aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/Kyuafile51
-rw-r--r--tests/Makefile28
-rw-r--r--tests/Makefile.depend10
-rw-r--r--tests/Makefile.inc06
-rw-r--r--tests/README61
-rw-r--r--tests/__init__.py0
-rw-r--r--tests/atf_python/Makefile14
-rw-r--r--tests/atf_python/__init__.py4
-rw-r--r--tests/atf_python/atf_pytest.py298
-rw-r--r--tests/atf_python/ktest.py173
-rw-r--r--tests/atf_python/sys/Makefile12
-rw-r--r--tests/atf_python/sys/__init__.py0
-rw-r--r--tests/atf_python/sys/net/Makefile11
-rw-r--r--tests/atf_python/sys/net/__init__.py0
-rwxr-xr-xtests/atf_python/sys/net/rtsock.py604
-rw-r--r--tests/atf_python/sys/net/tools.py100
-rw-r--r--tests/atf_python/sys/net/vnet.py554
-rw-r--r--tests/atf_python/sys/netlink/Makefile13
-rw-r--r--tests/atf_python/sys/netlink/__init__.py0
-rw-r--r--tests/atf_python/sys/netlink/attrs.py335
-rw-r--r--tests/atf_python/sys/netlink/base_headers.py72
-rw-r--r--tests/atf_python/sys/netlink/message.py286
-rw-r--r--tests/atf_python/sys/netlink/netlink.py417
-rw-r--r--tests/atf_python/sys/netlink/netlink_generic.py312
-rw-r--r--tests/atf_python/sys/netlink/netlink_route.py832
-rw-r--r--tests/atf_python/sys/netlink/utils.py80
-rw-r--r--tests/atf_python/sys/netpfil/Makefile12
-rw-r--r--tests/atf_python/sys/netpfil/__init__.py0
-rw-r--r--tests/atf_python/sys/netpfil/ipfw/Makefile13
-rw-r--r--tests/atf_python/sys/netpfil/ipfw/__init__.py0
-rw-r--r--tests/atf_python/sys/netpfil/ipfw/insn_headers.py198
-rw-r--r--tests/atf_python/sys/netpfil/ipfw/insns.py555
-rw-r--r--tests/atf_python/sys/netpfil/ipfw/ioctl.py511
-rw-r--r--tests/atf_python/sys/netpfil/ipfw/ioctl_headers.py90
-rw-r--r--tests/atf_python/sys/netpfil/ipfw/ipfw.py118
-rw-r--r--tests/atf_python/sys/netpfil/ipfw/utils.py61
-rw-r--r--tests/atf_python/utils.py100
-rw-r--r--tests/ci/Makefile265
-rw-r--r--tests/ci/Makefile.aarch6429
-rw-r--r--tests/ci/Makefile.amd6426
-rw-r--r--tests/ci/Makefile.armv729
-rw-r--r--tests/ci/Makefile.powerpc6427
-rw-r--r--tests/ci/Makefile.powerpc64le27
-rw-r--r--tests/ci/Makefile.riscv6431
-rw-r--r--tests/ci/tools/ci.conf100
-rwxr-xr-xtests/ci/tools/freebsdci110
-rw-r--r--tests/conftest.py136
-rw-r--r--tests/etc/Makefile10
-rw-r--r--tests/etc/Makefile.depend10
-rw-r--r--tests/etc/Makefile.inc1
-rw-r--r--tests/etc/rc.d/Makefile5
-rw-r--r--tests/etc/rc.d/Makefile.depend10
-rw-r--r--tests/etc/rc.d/routing_test.sh76
-rw-r--r--tests/examples/Makefile9
-rw-r--r--tests/examples/test_examples.py208
-rw-r--r--tests/examples/test_ktest_example.py35
-rw-r--r--tests/freebsd_test_suite/macros.h94
-rw-r--r--tests/include/Makefile13
-rw-r--r--tests/include/byteswap_endian_test.c9
-rw-r--r--tests/include/byteswap_test.c76
-rw-r--r--tests/include/endian_sys_endian_test.c13
-rw-r--r--tests/include/endian_test.c139
-rw-r--r--tests/include/stdckdint_test.c52
-rw-r--r--tests/include/sys_endian_endian_test.c12
-rw-r--r--tests/include/sys_endian_test.c136
-rw-r--r--tests/oclo/Makefile11
-rw-r--r--tests/sys/Makefile57
-rw-r--r--tests/sys/Makefile.depend10
-rw-r--r--tests/sys/Makefile.inc1
-rw-r--r--tests/sys/acl/00.sh90
-rw-r--r--tests/sys/acl/01.sh89
-rw-r--r--tests/sys/acl/02.sh95
-rw-r--r--tests/sys/acl/03.sh119
-rw-r--r--tests/sys/acl/04.sh76
-rw-r--r--tests/sys/acl/Makefile39
-rw-r--r--tests/sys/acl/Makefile.depend16
-rw-r--r--tests/sys/acl/acl-api-test.c194
-rw-r--r--tests/sys/acl/aclfuzzer.sh223
-rw-r--r--tests/sys/acl/mktrivial.sh51
-rw-r--r--tests/sys/acl/run328
-rw-r--r--tests/sys/acl/tools-crossfs.test321
-rw-r--r--tests/sys/acl/tools-nfs4-psarc.test583
-rw-r--r--tests/sys/acl/tools-nfs4-trivial.test80
-rw-r--r--tests/sys/acl/tools-nfs4.test861
-rw-r--r--tests/sys/acl/tools-posix.test451
-rw-r--r--tests/sys/aio/Makefile18
-rw-r--r--tests/sys/aio/Makefile.depend20
-rw-r--r--tests/sys/aio/aio_kqueue_test.c236
-rw-r--r--tests/sys/aio/aio_test.c2128
-rw-r--r--tests/sys/aio/lio_kqueue_test.c238
-rw-r--r--tests/sys/aio/lio_test.c336
-rw-r--r--tests/sys/aio/local.h87
-rw-r--r--tests/sys/audit/Makefile66
-rw-r--r--tests/sys/audit/administrative.c1692
-rw-r--r--tests/sys/audit/file-attribute-access.c1254
-rw-r--r--tests/sys/audit/file-attribute-modify.c1397
-rw-r--r--tests/sys/audit/file-close.c233
-rw-r--r--tests/sys/audit/file-create.c587
-rw-r--r--tests/sys/audit/file-delete.c272
-rw-r--r--tests/sys/audit/file-read.c134
-rw-r--r--tests/sys/audit/file-write.c139
-rw-r--r--tests/sys/audit/inter-process.c1658
-rw-r--r--tests/sys/audit/ioctl.c101
-rw-r--r--tests/sys/audit/miscellaneous.c225
-rw-r--r--tests/sys/audit/network.c1181
-rw-r--r--tests/sys/audit/open.c194
-rw-r--r--tests/sys/audit/process-control.c1662
-rw-r--r--tests/sys/audit/utils.c329
-rw-r--r--tests/sys/audit/utils.h60
-rw-r--r--tests/sys/auditpipe/Makefile8
-rw-r--r--tests/sys/auditpipe/auditpipe_test.c183
-rw-r--r--tests/sys/cam/Makefile7
-rw-r--r--tests/sys/cam/ctl/Makefile20
-rw-r--r--tests/sys/cam/ctl/ctl.subr106
-rw-r--r--tests/sys/cam/ctl/persist.sh349
-rw-r--r--tests/sys/cam/ctl/prevent.sh161
-rw-r--r--tests/sys/cam/ctl/prout_register_huge_cdb.c88
-rw-r--r--tests/sys/cam/ctl/read_buffer.sh172
-rw-r--r--tests/sys/cam/ctl/start_stop_unit.sh150
-rw-r--r--tests/sys/capsicum/Makefile65
-rw-r--r--tests/sys/capsicum/bindat_connectat.c230
-rwxr-xr-xtests/sys/capsicum/functional.sh60
-rw-r--r--tests/sys/capsicum/ioctls_test.c127
-rw-r--r--tests/sys/cddl/Makefile11
-rw-r--r--tests/sys/cddl/zfs/Makefile9
-rw-r--r--tests/sys/cddl/zfs/bin/Makefile61
-rw-r--r--tests/sys/cddl/zfs/bin/bsddisks.ksh4
-rw-r--r--tests/sys/cddl/zfs/bin/chg_usr_exec.c76
-rw-r--r--tests/sys/cddl/zfs/bin/devname2devid.c121
-rw-r--r--tests/sys/cddl/zfs/bin/dir_rd_update.c120
-rw-r--r--tests/sys/cddl/zfs/bin/dircmp.ksh3
-rw-r--r--tests/sys/cddl/zfs/bin/dumpadm.ksh8
-rw-r--r--tests/sys/cddl/zfs/bin/ff.ksh4
-rw-r--r--tests/sys/cddl/zfs/bin/file_check.c87
-rw-r--r--tests/sys/cddl/zfs/bin/file_common.h63
-rw-r--r--tests/sys/cddl/zfs/bin/file_trunc.c238
-rw-r--r--tests/sys/cddl/zfs/bin/file_write.c239
-rw-r--r--tests/sys/cddl/zfs/bin/fmadm.ksh4
-rw-r--r--tests/sys/cddl/zfs/bin/fmdump.ksh4
-rw-r--r--tests/sys/cddl/zfs/bin/format.ksh4
-rw-r--r--tests/sys/cddl/zfs/bin/groupadd.ksh11
-rw-r--r--tests/sys/cddl/zfs/bin/groupdel.ksh11
-rw-r--r--tests/sys/cddl/zfs/bin/groupmod.ksh11
-rw-r--r--tests/sys/cddl/zfs/bin/groupshow.ksh3
-rw-r--r--tests/sys/cddl/zfs/bin/largest_file.c130
-rw-r--r--tests/sys/cddl/zfs/bin/mkfile.c209
-rw-r--r--tests/sys/cddl/zfs/bin/mktree.c194
-rw-r--r--tests/sys/cddl/zfs/bin/mmapwrite.c97
-rw-r--r--tests/sys/cddl/zfs/bin/randfree_file.c102
-rw-r--r--tests/sys/cddl/zfs/bin/readmmap.c139
-rw-r--r--tests/sys/cddl/zfs/bin/rename_dir.c90
-rw-r--r--tests/sys/cddl/zfs/bin/rm_lnkcnt_zero_file.c146
-rw-r--r--tests/sys/cddl/zfs/bin/svcs.ksh4
-rw-r--r--tests/sys/cddl/zfs/bin/swap.ksh3
-rw-r--r--tests/sys/cddl/zfs/bin/testenv.ksh6
-rw-r--r--tests/sys/cddl/zfs/bin/useradd.ksh11
-rw-r--r--tests/sys/cddl/zfs/bin/userdel.ksh11
-rw-r--r--tests/sys/cddl/zfs/bin/usermod.ksh11
-rw-r--r--tests/sys/cddl/zfs/bin/zfs.ksh39
-rw-r--r--tests/sys/cddl/zfs/bin/zfs_crypto.ksh54
-rw-r--r--tests/sys/cddl/zfs/bin/zfs_version.ksh63
-rw-r--r--tests/sys/cddl/zfs/bin/zlogin.ksh4
-rw-r--r--tests/sys/cddl/zfs/bin/zoneadm.ksh4
-rw-r--r--tests/sys/cddl/zfs/bin/zonecfg.ksh4
-rw-r--r--tests/sys/cddl/zfs/bin/zpool.ksh41
-rw-r--r--tests/sys/cddl/zfs/bin/zpool_bsd.ksh40
-rw-r--r--tests/sys/cddl/zfs/bin/zpool_smi.ksh133
-rw-r--r--tests/sys/cddl/zfs/bin/zpool_version.ksh68
-rw-r--r--tests/sys/cddl/zfs/include/Makefile30
-rw-r--r--tests/sys/cddl/zfs/include/commands.txt189
-rw-r--r--tests/sys/cddl/zfs/include/constants.cfg112
-rw-r--r--tests/sys/cddl/zfs/include/default.cfg.in47
-rw-r--r--tests/sys/cddl/zfs/include/libgnop.kshlib103
-rw-r--r--tests/sys/cddl/zfs/include/libremote.kshlib42
-rw-r--r--tests/sys/cddl/zfs/include/libtest.kshlib3472
-rwxr-xr-xtests/sys/cddl/zfs/include/libtest_test.sh42
-rw-r--r--tests/sys/cddl/zfs/include/logapi.kshlib414
-rw-r--r--tests/sys/cddl/zfs/include/stf.shlib50
-rw-r--r--tests/sys/cddl/zfs/include/testenv.ksh19
-rw-r--r--tests/sys/cddl/zfs/include/testenv.kshlib21
-rw-r--r--tests/sys/cddl/zfs/include/translatecommands.awk39
-rw-r--r--tests/sys/cddl/zfs/tests/Makefile87
-rw-r--r--tests/sys/cddl/zfs/tests/Makefile.inc1
-rw-r--r--tests/sys/cddl/zfs/tests/acl/Makefile16
-rw-r--r--tests/sys/cddl/zfs/tests/acl/acl.cfg67
-rw-r--r--tests/sys/cddl/zfs/tests/acl/acl_common.kshlib633
-rw-r--r--tests/sys/cddl/zfs/tests/acl/cifs/Makefile16
-rw-r--r--tests/sys/cddl/zfs/tests/acl/cifs/cifs.kshlib86
-rw-r--r--tests/sys/cddl/zfs/tests/acl/cifs/cifs_attr_001_pos.ksh257
-rw-r--r--tests/sys/cddl/zfs/tests/acl/cifs/cifs_attr_002_pos.ksh275
-rw-r--r--tests/sys/cddl/zfs/tests/acl/cifs/cifs_attr_003_pos.ksh617
-rwxr-xr-xtests/sys/cddl/zfs/tests/acl/cifs/cifs_test.sh114
-rw-r--r--tests/sys/cddl/zfs/tests/acl/cleanup.ksh39
-rw-r--r--tests/sys/cddl/zfs/tests/acl/nontrivial/Makefile38
-rwxr-xr-xtests/sys/cddl/zfs/tests/acl/nontrivial/nontrivial_test.sh693
-rw-r--r--tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_001_neg.ksh146
-rw-r--r--tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_002_pos.ksh77
-rw-r--r--tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_aclmode_001_pos.ksh485
-rw-r--r--tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_compact_001_pos.ksh269
-rw-r--r--tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_delete_001_pos.ksh316
-rw-r--r--tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_inherit_001_pos.ksh188
-rw-r--r--tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_inherit_002_pos.ksh410
-rw-r--r--tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_inherit_003_pos.ksh444
-rw-r--r--tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_inherit_004_pos.ksh157
-rw-r--r--tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_owner_001_pos.ksh397
-rw-r--r--tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_rwacl_001_pos.ksh257
-rw-r--r--tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_rwx_001_pos.ksh136
-rw-r--r--tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_rwx_002_pos.ksh256
-rw-r--r--tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_rwx_003_pos.ksh147
-rw-r--r--tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_rwx_004_pos.ksh150
-rw-r--r--tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_xattr_001_pos.ksh252
-rw-r--r--tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_xattr_002_pos.ksh247
-rw-r--r--tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_cp_001_pos.ksh110
-rw-r--r--tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_cp_002_pos.ksh116
-rw-r--r--tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_cpio_001_pos.ksh135
-rw-r--r--tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_cpio_002_pos.ksh139
-rw-r--r--tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_find_001_pos.ksh139
-rw-r--r--tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_ls_001_pos.ksh119
-rw-r--r--tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_mv_001_pos.ksh185
-rw-r--r--tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_tar_001_pos.ksh119
-rw-r--r--tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_tar_002_pos.ksh126
-rw-r--r--tests/sys/cddl/zfs/tests/acl/setup.ksh61
-rw-r--r--tests/sys/cddl/zfs/tests/acl/trivial/Makefile31
-rwxr-xr-xtests/sys/cddl/zfs/tests/acl/trivial/trivial_test.sh514
-rw-r--r--tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_chmod_001_pos.ksh146
-rw-r--r--tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_compress_001_pos.ksh87
-rw-r--r--tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_cp_001_pos.ksh90
-rw-r--r--tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_cp_002_neg.ksh87
-rw-r--r--tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_cp_003_neg.ksh133
-rw-r--r--tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_find_001_pos.ksh96
-rw-r--r--tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_find_002_neg.ksh102
-rw-r--r--tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_ls_001_pos.ksh83
-rw-r--r--tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_ls_002_neg.ksh84
-rw-r--r--tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_mv_001_pos.ksh82
-rw-r--r--tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_pack_001_pos.ksh87
-rw-r--r--tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_pax_001_pos.ksh99
-rw-r--r--tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_pax_002_pos.ksh98
-rw-r--r--tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_pax_003_pos.ksh93
-rw-r--r--tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_pax_004_pos.ksh99
-rw-r--r--tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_pax_005_pos.ksh106
-rw-r--r--tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_pax_006_pos.ksh106
-rw-r--r--tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_tar_001_pos.ksh97
-rw-r--r--tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_tar_002_neg.ksh113
-rw-r--r--tests/sys/cddl/zfs/tests/atime/Makefile18
-rw-r--r--tests/sys/cddl/zfs/tests/atime/atime.cfg30
-rw-r--r--tests/sys/cddl/zfs/tests/atime/atime_001_pos.ksh80
-rw-r--r--tests/sys/cddl/zfs/tests/atime/atime_002_neg.ksh81
-rw-r--r--tests/sys/cddl/zfs/tests/atime/atime_common.kshlib67
-rwxr-xr-xtests/sys/cddl/zfs/tests/atime/atime_test.sh84
-rw-r--r--tests/sys/cddl/zfs/tests/atime/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/atime/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/bootfs/Makefile22
-rw-r--r--tests/sys/cddl/zfs/tests/bootfs/bootfs.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/bootfs/bootfs_001_pos.ksh93
-rw-r--r--tests/sys/cddl/zfs/tests/bootfs/bootfs_002_neg.ksh76
-rw-r--r--tests/sys/cddl/zfs/tests/bootfs/bootfs_003_pos.ksh104
-rw-r--r--tests/sys/cddl/zfs/tests/bootfs/bootfs_004_neg.ksh105
-rw-r--r--tests/sys/cddl/zfs/tests/bootfs/bootfs_005_neg.ksh98
-rw-r--r--tests/sys/cddl/zfs/tests/bootfs/bootfs_006_pos.ksh157
-rw-r--r--tests/sys/cddl/zfs/tests/bootfs/bootfs_007_pos.ksh76
-rw-r--r--tests/sys/cddl/zfs/tests/bootfs/bootfs_008_neg.ksh91
-rw-r--r--tests/sys/cddl/zfs/tests/bootfs/bootfs_009_neg.ksh92
-rwxr-xr-xtests/sys/cddl/zfs/tests/bootfs/bootfs_test.sh178
-rw-r--r--tests/sys/cddl/zfs/tests/cache/Makefile27
-rw-r--r--tests/sys/cddl/zfs/tests/cache/cache.cfg42
-rw-r--r--tests/sys/cddl/zfs/tests/cache/cache.kshlib175
-rw-r--r--tests/sys/cddl/zfs/tests/cache/cache_001_pos.ksh73
-rw-r--r--tests/sys/cddl/zfs/tests/cache/cache_002_pos.ksh73
-rw-r--r--tests/sys/cddl/zfs/tests/cache/cache_003_pos.ksh76
-rw-r--r--tests/sys/cddl/zfs/tests/cache/cache_004_neg.ksh71
-rw-r--r--tests/sys/cddl/zfs/tests/cache/cache_005_neg.ksh72
-rw-r--r--tests/sys/cddl/zfs/tests/cache/cache_006_pos.ksh92
-rw-r--r--tests/sys/cddl/zfs/tests/cache/cache_007_neg.ksh70
-rw-r--r--tests/sys/cddl/zfs/tests/cache/cache_008_neg.ksh74
-rw-r--r--tests/sys/cddl/zfs/tests/cache/cache_009_pos.ksh80
-rw-r--r--tests/sys/cddl/zfs/tests/cache/cache_010_neg.ksh97
-rw-r--r--tests/sys/cddl/zfs/tests/cache/cache_011_pos.ksh75
-rwxr-xr-xtests/sys/cddl/zfs/tests/cache/cache_test.sh339
-rw-r--r--tests/sys/cddl/zfs/tests/cache/cleanup.ksh39
-rw-r--r--tests/sys/cddl/zfs/tests/cache/setup.ksh40
-rw-r--r--tests/sys/cddl/zfs/tests/cachefile/Makefile18
-rw-r--r--tests/sys/cddl/zfs/tests/cachefile/cachefile.cfg33
-rw-r--r--tests/sys/cddl/zfs/tests/cachefile/cachefile.kshlib48
-rw-r--r--tests/sys/cddl/zfs/tests/cachefile/cachefile_001_pos.ksh102
-rw-r--r--tests/sys/cddl/zfs/tests/cachefile/cachefile_002_pos.ksh87
-rw-r--r--tests/sys/cddl/zfs/tests/cachefile/cachefile_003_pos.ksh108
-rw-r--r--tests/sys/cddl/zfs/tests/cachefile/cachefile_004_pos.ksh131
-rwxr-xr-xtests/sys/cddl/zfs/tests/cachefile/cachefile_test.sh102
-rw-r--r--tests/sys/cddl/zfs/tests/clean_mirror/Makefile20
-rw-r--r--tests/sys/cddl/zfs/tests/clean_mirror/clean_mirror_001_pos.ksh62
-rw-r--r--tests/sys/cddl/zfs/tests/clean_mirror/clean_mirror_002_pos.ksh62
-rw-r--r--tests/sys/cddl/zfs/tests/clean_mirror/clean_mirror_003_pos.ksh62
-rw-r--r--tests/sys/cddl/zfs/tests/clean_mirror/clean_mirror_004_pos.ksh62
-rw-r--r--tests/sys/cddl/zfs/tests/clean_mirror/clean_mirror_common.kshlib74
-rwxr-xr-xtests/sys/cddl/zfs/tests/clean_mirror/clean_mirror_test.sh131
-rw-r--r--tests/sys/cddl/zfs/tests/clean_mirror/cleanup.ksh42
-rw-r--r--tests/sys/cddl/zfs/tests/clean_mirror/default.cfg52
-rw-r--r--tests/sys/cddl/zfs/tests/clean_mirror/setup.ksh42
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/Makefile58
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/cli.cfg50
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/cli_common.kshlib86
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zdb/Makefile16
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zdb/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zdb/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zdb/zdb.cfg27
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zdb/zdb_001_neg.ksh81
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zdb/zdb_test.sh55
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs/Makefile18
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs/zfs_001_neg.ksh85
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs/zfs_002_pos.ksh122
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs/zfs_003_neg.ksh72
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs/zfs_malformed.cfg27
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zfs/zfs_test.sh106
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_clone/Makefile24
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_clone/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_clone/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone.cfg30
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_001_neg.ksh138
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_002_pos.ksh101
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_003_pos.ksh94
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_004_pos.ksh102
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_005_pos.ksh94
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_006_pos.ksh100
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_007_pos.ksh101
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_008_neg.ksh92
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_009_neg.ksh92
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_test.sh255
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_copies/Makefile22
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_copies/cleanup.ksh43
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_copies/setup.ksh34
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies.cfg35
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies.kshlib141
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies_001_pos.ksh121
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies_002_pos.ksh112
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies_003_pos.ksh77
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies_004_neg.ksh66
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies_005_neg.ksh84
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies_006_pos.ksh86
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies_test.sh193
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_create/Makefile30
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_create/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_create/properties.kshlib78
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_create/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create.cfg51
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_001_pos.ksh79
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_002_pos.ksh91
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_003_pos.ksh76
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_004_pos.ksh87
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_005_pos.ksh96
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_006_pos.ksh98
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_007_pos.ksh101
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_008_neg.ksh108
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_009_neg.ksh134
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_010_neg.ksh156
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_011_pos.ksh82
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_012_pos.ksh87
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_013_pos.ksh91
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_common.kshlib51
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_test.sh408
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/Makefile23
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/cleanup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy.cfg39
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_001_pos.ksh210
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_002_pos.ksh109
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_003_pos.ksh163
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_004_pos.ksh127
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_005_neg.ksh222
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_006_neg.ksh74
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_007_neg.ksh81
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_common.kshlib168
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_test.sh226
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_diff/Makefile18
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_diff/cleanup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_diff/setup.ksh32
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_diff/zfs_diff.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_diff/zfs_diff_001_pos.golden13
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_diff/zfs_diff_001_pos.ksh77
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zfs_diff/zfs_diff_test.sh54
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_get/Makefile27
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_get/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_get/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get.cfg29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_001_pos.ksh155
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_002_pos.ksh111
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_003_pos.ksh74
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_004_pos.ksh241
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_005_neg.ksh135
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_006_neg.ksh73
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_007_neg.ksh73
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_008_pos.ksh108
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_009_pos.ksh100
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_010_neg.ksh72
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_common.kshlib97
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_list_d.kshlib89
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_test.sh321
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/Makefile18
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/zfs_inherit.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/zfs_inherit_001_neg.ksh91
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/zfs_inherit_002_neg.ksh109
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/zfs_inherit_003_pos.ksh99
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zfs_inherit/zfs_inherit_test.sh105
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_mount/Makefile28
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_mount/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_mount/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount.cfg37
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount.kshlib130
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_001_pos.ksh76
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_002_pos.ksh87
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_003_pos.ksh99
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_004_pos.ksh92
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_005_pos.ksh95
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_006_pos.ksh133
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_007_pos.ksh147
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_008_pos.ksh104
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_009_neg.ksh122
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_010_neg.ksh80
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_011_neg.ksh90
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_all_001_pos.ksh192
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_test.sh370
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_promote/Makefile24
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_promote/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_promote/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote.cfg41
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_001_pos.ksh134
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_002_pos.ksh109
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_003_pos.ksh141
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_004_pos.ksh147
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_005_pos.ksh82
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_006_neg.ksh93
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_007_neg.ksh85
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_008_pos.ksh92
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_common.kshlib43
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_test.sh246
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_property/Makefile16
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_property/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_property/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_property/zfs_property.cfg27
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zfs_property/zfs_property_test.sh55
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_property/zfs_set_property_001_pos.ksh83
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_receive/Makefile24
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_receive/cleanup.ksh32
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_receive/setup.ksh34
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive.cfg31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_001_pos.ksh183
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_002_pos.ksh117
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_003_pos.ksh102
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_004_neg.ksh114
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_005_neg.ksh108
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_006_pos.ksh117
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_007_neg.ksh93
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_008_pos.ksh157
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_009_neg.ksh127
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_test.sh255
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_rename/Makefile29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_rename/cleanup.ksh38
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_rename/setup.ksh43
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename.cfg36
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename.kshlib112
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_001_pos.ksh116
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_002_pos.ksh99
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_003_pos.ksh77
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_004_neg.ksh120
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_005_neg.ksh100
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_006_pos.ksh87
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_007_pos.ksh165
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_008_pos.ksh103
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_009_neg.ksh91
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_010_neg.ksh88
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_011_pos.ksh90
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_012_neg.ksh76
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_013_pos.ksh100
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_test.sh381
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_reservation/Makefile17
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_reservation/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_reservation/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_reservation/zfs_reservation.cfg27
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_reservation/zfs_reservation_001_pos.ksh72
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_reservation/zfs_reservation_002_pos.ksh96
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zfs_reservation/zfs_reservation_test.sh80
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/Makefile20
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/cleanup.ksh34
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/setup.ksh30
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/zfs_rollback.cfg48
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/zfs_rollback_001_pos.ksh176
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/zfs_rollback_002_pos.ksh77
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/zfs_rollback_003_neg.ksh93
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/zfs_rollback_004_neg.ksh95
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/zfs_rollback_common.kshlib300
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zfs_rollback/zfs_rollback_test.sh142
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_send/Makefile19
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_send/cleanup.ksh32
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_send/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_send/zfs_send.cfg31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_send/zfs_send_001_pos.ksh135
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_send/zfs_send_002_pos.ksh147
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_send/zfs_send_003_pos.ksh78
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_send/zfs_send_004_neg.ksh118
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zfs_send/zfs_send_test.sh130
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/Makefile42
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/cache_001_pos.ksh76
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/cache_002_neg.ksh76
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/canmount_001_pos.ksh132
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/canmount_002_pos.ksh165
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/canmount_003_pos.ksh126
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/canmount_004_pos.ksh108
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/checksum_001_pos.ksh80
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/compression_001_pos.ksh76
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/mountpoint_001_pos.ksh107
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/mountpoint_002_pos.ksh105
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/mountpoint_003_pos.ksh111
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/onoffs_001_pos.ksh101
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/property_alias_001_pos.ksh153
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/readonly_001_pos.ksh167
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/reservation_001_neg.ksh111
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/ro_props_001_pos.ksh158
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/setup.ksh30
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/share_mount_001_neg.ksh73
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/snapdir_001_pos.ksh123
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/user_property_001_pos.ksh78
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/user_property_002_pos.ksh131
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/user_property_003_neg.ksh76
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/user_property_004_pos.ksh110
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/version_001_neg.ksh104
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/zfs_set.cfg30
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/zfs_set_001_neg.ksh88
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/zfs_set_002_neg.ksh71
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/zfs_set_003_neg.ksh86
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_set/zfs_set_common.kshlib248
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zfs_set/zfs_set_test.sh763
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_share/Makefile26
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_share/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_share/setup.ksh33
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share.cfg30
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_001_pos.ksh158
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_002_pos.ksh81
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_003_pos.ksh119
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_004_pos.ksh104
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_005_pos.ksh92
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_006_pos.ksh114
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_007_neg.ksh94
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_008_neg.ksh83
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_009_neg.ksh82
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_009_pos.ksh108
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_010_neg.ksh69
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_test.sh294
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/Makefile22
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot.cfg32
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_001_neg.ksh127
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_002_neg.ksh106
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_003_neg.ksh74
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_004_neg.ksh106
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_005_neg.ksh97
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_006_pos.ksh138
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_007_neg.ksh141
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_test.sh205
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/Makefile26
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount.cfg37
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount.kshlib71
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_001_pos.ksh125
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_002_pos.ksh107
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_003_pos.ksh118
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_004_pos.ksh108
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_005_pos.ksh122
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_006_pos.ksh80
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_007_neg.ksh119
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_008_neg.ksh152
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_009_pos.ksh136
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_all_001_pos.ksh204
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_test.sh308
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/Makefile20
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/setup.ksh33
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/zfs_unshare.cfg30
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/zfs_unshare_001_pos.ksh190
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/zfs_unshare_002_pos.ksh189
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/zfs_unshare_003_pos.ksh104
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/zfs_unshare_004_neg.ksh90
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/zfs_unshare_005_neg.ksh69
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zfs_unshare/zfs_unshare_test.sh150
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/Makefile23
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/cleanup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/setup.ksh40
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade.cfg30
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade.kshlib191
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_001_pos.ksh148
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_002_pos.ksh75
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_003_pos.ksh112
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_004_pos.ksh116
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_005_pos.ksh120
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_006_neg.ksh67
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_007_neg.ksh68
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_test.sh223
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool/Makefile18
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool/zpool.cfg27
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool/zpool_001_neg.ksh78
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool/zpool_002_pos.ksh105
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool/zpool_003_pos.ksh78
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zpool/zpool_test.sh105
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_add/Makefile26
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_add/cleanup.ksh34
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_add/setup.ksh32
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add.cfg64
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add.kshlib79
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_001_pos.ksh122
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_002_pos.ksh72
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_003_pos.ksh75
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_004_pos.ksh84
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_005_pos.ksh104
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_006_pos.ksh145
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_007_neg.ksh71
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_008_neg.ksh69
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_009_neg.ksh66
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_010_pos.ksh44
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_test.sh311
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_attach/Makefile16
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_attach/cleanup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_attach/setup.ksh33
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_attach/zpool_attach.cfg27
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_attach/zpool_attach_001_neg.ksh91
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zpool_attach/zpool_attach_test.sh55
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_clear/Makefile19
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_clear/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_clear/zpool_clear.cfg32
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_clear/zpool_clear_001_pos.ksh105
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_clear/zpool_clear_002_neg.ksh83
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_clear/zpool_clear_003_neg.ksh74
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_clear/zpool_clear_004_pos.ksh95
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_clear/zpool_clear_005_pos.ksh74
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zpool_clear/zpool_clear_test.sh150
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_create/Makefile36
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_create/cleanup.ksh40
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_create/setup.ksh48
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create.cfg72
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create.kshlib112
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_001_pos.ksh128
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_002_pos.ksh116
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_003_pos.ksh90
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_004_pos.ksh89
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_005_pos.ksh117
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_006_pos.ksh136
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_007_neg.ksh92
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_008_pos.ksh75
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_009_neg.ksh97
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_010_neg.ksh92
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_011_neg.ksh135
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_012_neg.ksh67
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_015_neg.ksh85
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_017_neg.ksh97
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_018_pos.ksh115
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_019_pos.ksh86
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_020_pos.ksh118
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_021_pos.ksh101
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_022_pos.ksh108
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_023_neg.ksh97
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_test.sh583
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/Makefile19
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/cleanup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/zpool_destroy.cfg30
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/zpool_destroy_001_pos.ksh83
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/zpool_destroy_002_pos.ksh117
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/zpool_destroy_003_neg.ksh65
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/zpool_destroy_004_pos.ksh144
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zpool_destroy/zpool_destroy_test.sh127
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_detach/Makefile16
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_detach/cleanup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_detach/setup.ksh33
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_detach/zpool_detach.cfg27
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_detach/zpool_detach_001_neg.ksh78
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zpool_detach/zpool_detach_test.sh55
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_expand/Makefile18
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_expand/cleanup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_expand/setup.ksh37
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_expand/zpool_expand.cfg42
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_expand/zpool_expand_001_pos.ksh133
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_expand/zpool_expand_002_pos.ksh137
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_expand/zpool_expand_003_neg.ksh123
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zpool_expand/zpool_expand_test.sh108
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_export/Makefile19
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_export/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_export/setup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_export/zpool_export.cfg29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_export/zpool_export_001_pos.ksh78
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_export/zpool_export_002_pos.ksh89
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_export/zpool_export_003_neg.ksh77
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_export/zpool_export_004_pos.ksh112
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zpool_export/zpool_export_test.sh130
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_get/Makefile19
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_get/cleanup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_get/setup.ksh30
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_get/zpool_get.cfg75
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_get/zpool_get_001_pos.ksh70
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_get/zpool_get_002_pos.ksh103
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_get/zpool_get_003_pos.ksh80
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_get/zpool_get_004_neg.ksh70
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zpool_get/zpool_get_test.sh128
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_history/Makefile17
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_history/cleanup.ksh32
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_history/setup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_history/zpool_history.cfg30
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_history/zpool_history_001_neg.ksh78
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_history/zpool_history_002_pos.ksh65
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zpool_history/zpool_history_test.sh80
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/Makefile41
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/blockfiles/Makefile10
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/blockfiles/README29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/blockfiles/unclean_export.dat.bz2bin0 -> 14161 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/cleanup.ksh42
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/setup.ksh50
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import.cfg104
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import.kshlib33
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_002_pos.ksh161
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_003_pos.ksh73
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_004_pos.ksh105
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_005_pos.ksh107
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_006_pos.ksh102
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_007_pos.ksh106
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_008_pos.ksh107
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_009_neg.ksh110
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_010_pos.ksh104
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_011_neg.ksh89
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_012_pos.ksh189
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_013_neg.ksh82
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_014_pos.ksh64
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_all_001_pos.ksh233
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_corrupt_001_pos.ksh128
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_destroyed_001_neg.ksh65
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_destroyed_002_neg.ksh58
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_missing_001_pos.ksh166
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_missing_002_pos.ksh145
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_missing_003_pos.ksh234
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_missing_004_pos.ksh99
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_missing_005_pos.ksh122
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_rename_001_pos.ksh176
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_test.sh587
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_offline/Makefile17
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_offline/cleanup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_offline/setup.ksh33
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_offline/zpool_offline.cfg27
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_offline/zpool_offline_001_pos.ksh133
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_offline/zpool_offline_002_neg.ksh104
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zpool_offline/zpool_offline_test.sh80
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_online/Makefile17
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_online/cleanup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_online/setup.ksh33
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_online/zpool_online.cfg27
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_online/zpool_online_001_pos.ksh121
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_online/zpool_online_002_neg.ksh83
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zpool_online/zpool_online_test.sh80
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_remove/Makefile18
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_remove/cleanup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_remove/setup.ksh32
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_remove/zpool_remove.cfg30
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_remove/zpool_remove_001_neg.ksh103
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_remove/zpool_remove_002_pos.ksh74
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_remove/zpool_remove_003_pos.ksh85
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zpool_remove/zpool_remove_test.sh105
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_replace/Makefile17
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_replace/cleanup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_replace/setup.ksh33
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_replace/zpool_replace.cfg27
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_replace/zpool_replace_001_neg.ksh91
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_replace/zpool_replace_002_neg.ksh38
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zpool_replace/zpool_replace_test.sh77
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/Makefile20
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/cleanup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/setup.ksh40
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/zpool_scrub.cfg29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/zpool_scrub_001_neg.ksh71
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/zpool_scrub_002_pos.ksh61
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/zpool_scrub_003_pos.ksh84
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/zpool_scrub_004_pos.ksh71
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/zpool_scrub_005_pos.ksh72
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zpool_scrub/zpool_scrub_test.sh155
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_set/Makefile15
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_set/zpool_set_001_pos.ksh75
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_set/zpool_set_002_neg.ksh139
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_set/zpool_set_003_neg.ksh90
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zpool_set/zpool_set_test.sh75
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_status/Makefile17
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_status/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_status/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_status/zpool_status.cfg27
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_status/zpool_status_001_pos.ksh70
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_status/zpool_status_002_pos.ksh75
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zpool_status/zpool_status_test.sh80
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/Makefile27
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/Makefile63
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/README29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-broken-mirror1.dat.Zbin0 -> 43027 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-broken-mirror2.dat.Zbin0 -> 77195 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1.dat.Zbin0 -> 82255 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v10.dat.Zbin0 -> 84381 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v11.dat.Zbin0 -> 78551 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v12.dat.Zbin0 -> 78717 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v13.dat.Zbin0 -> 81519 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v14.dat.Zbin0 -> 81669 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v15.dat.Zbin0 -> 94841 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1mirror1.dat.Zbin0 -> 86301 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1mirror2.dat.Zbin0 -> 86277 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1mirror3.dat.Zbin0 -> 86319 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1raidz1.dat.Zbin0 -> 72903 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1raidz2.dat.Zbin0 -> 67753 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1raidz3.dat.Zbin0 -> 72119 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1stripe1.dat.Zbin0 -> 50441 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1stripe2.dat.Zbin0 -> 50223 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1stripe3.dat.Zbin0 -> 85825 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2.dat.Zbin0 -> 83157 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v28.dat.Zbin0 -> 52860 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2mirror1.dat.Zbin0 -> 112015 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2mirror2.dat.Zbin0 -> 112019 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2mirror3.dat.Zbin0 -> 112023 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2raidz1.dat.Zbin0 -> 96249 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2raidz2.dat.Zbin0 -> 103399 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2raidz3.dat.Zbin0 -> 99425 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2stripe1.dat.Zbin0 -> 77433 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2stripe2.dat.Zbin0 -> 66485 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2stripe3.dat.Zbin0 -> 77869 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3.dat.Zbin0 -> 69699 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3hotspare1.dat.Zbin0 -> 113405 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3hotspare2.dat.Zbin0 -> 96439 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3hotspare3.dat.Zbin0 -> 54789 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3mirror1.dat.Zbin0 -> 71439 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3mirror2.dat.Zbin0 -> 71457 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3mirror3.dat.Zbin0 -> 71455 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3raidz1.dat.Zbin0 -> 67695 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3raidz2.dat.Zbin0 -> 67771 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3raidz21.dat.Zbin0 -> 70207 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3raidz22.dat.Zbin0 -> 70163 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3raidz23.dat.Zbin0 -> 70173 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3raidz3.dat.Zbin0 -> 67749 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3stripe1.dat.Zbin0 -> 60789 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3stripe2.dat.Zbin0 -> 58073 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3stripe3.dat.Zbin0 -> 60861 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v4.dat.Zbin0 -> 106121 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v5.dat.Zbin0 -> 115725 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v5000.dat.Zbin0 -> 56867 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v6.dat.Zbin0 -> 101989 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v7.dat.Zbin0 -> 94261 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v8.dat.Zbin0 -> 94625 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v9.dat.Zbin0 -> 88061 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v999.dat.Zbin0 -> 85575 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-vBROKEN.dat.Zbin0 -> 66695 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/cleanup.ksh49
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/create_upgrade_pool_dat.sh40
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/setup.ksh38
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade.cfg288
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade.kshlib157
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_001_pos.ksh84
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_002_pos.ksh71
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_003_pos.ksh69
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_004_pos.ksh98
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_005_neg.ksh68
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_006_neg.ksh68
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_007_pos.ksh83
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_008_pos.ksh94
-rw-r--r--tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_009_neg.ksh87
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_test.sh287
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/Makefile17
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/cli.cfg31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/cli_user.kshlib33
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/Makefile57
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/cleanup.ksh37
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/misc.cfg67
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_user/misc/misc_test.sh1172
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/setup.ksh168
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/setup_basic.ksh30
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zdb_001_neg.ksh91
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zfs_001_neg.ksh69
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zfs_allow_001_neg.ksh77
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zfs_clone_001_neg.ksh64
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zfs_create_001_neg.ksh69
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zfs_destroy_001_neg.ksh75
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zfs_get_001_neg.ksh74
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zfs_inherit_001_neg.ksh66
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zfs_mount_001_neg.ksh66
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zfs_promote_001_neg.ksh66
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zfs_receive_001_neg.ksh68
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zfs_rename_001_neg.ksh66
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zfs_rollback_001_neg.ksh70
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zfs_send_001_neg.ksh77
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zfs_set_001_neg.ksh79
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zfs_share_001_neg.ksh72
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zfs_snapshot_001_neg.ksh65
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zfs_unallow_001_neg.ksh74
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zfs_unmount_001_neg.ksh73
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zfs_unshare_001_neg.ksh73
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zfs_upgrade_001_neg.ksh77
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zpool_001_neg.ksh70
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zpool_add_001_neg.ksh67
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zpool_add_002_pos.ksh43
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zpool_attach_001_neg.ksh75
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zpool_clear_001_neg.ksh61
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zpool_create_001_neg.ksh70
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zpool_create_002_pos.ksh42
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zpool_destroy_001_neg.ksh67
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zpool_detach_001_neg.ksh67
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zpool_export_001_neg.ksh74
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zpool_get_001_neg.ksh76
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zpool_history_001_neg.ksh63
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zpool_import_001_neg.ksh76
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zpool_import_002_neg.ksh72
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zpool_offline_001_neg.ksh76
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zpool_online_001_neg.ksh76
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zpool_remove_001_neg.ksh68
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zpool_replace_001_neg.ksh75
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zpool_scrub_001_neg.ksh62
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zpool_set_001_neg.ksh80
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zpool_status_001_neg.ksh74
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/misc/zpool_upgrade_001_neg.ksh75
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/zfs_list/Makefile24
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/zfs_list/cleanup.ksh30
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/zfs_list/setup.ksh71
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list.cfg35
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list.kshlib114
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_001_pos.ksh129
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_002_pos.ksh185
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_003_pos.ksh86
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_004_neg.ksh72
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_005_pos.ksh192
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_006_pos.ksh121
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_007_pos.ksh106
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_008_neg.ksh72
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_test.sh263
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/Makefile17
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/zpool_iostat.cfg27
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/zpool_iostat_001_neg.ksh72
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/zpool_iostat_002_pos.ksh79
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/zpool_iostat_003_neg.ksh73
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_user/zpool_iostat/zpool_iostat_test.sh101
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/zpool_list/Makefile16
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/zpool_list/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/zpool_list/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/zpool_list/zpool_list.cfg27
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/zpool_list/zpool_list_001_pos.ksh73
-rw-r--r--tests/sys/cddl/zfs/tests/cli_user/zpool_list/zpool_list_002_neg.ksh65
-rwxr-xr-xtests/sys/cddl/zfs/tests/cli_user/zpool_list/zpool_list_test.sh73
-rw-r--r--tests/sys/cddl/zfs/tests/compression/Makefile18
-rw-r--r--tests/sys/cddl/zfs/tests/compression/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/compression/compress.cfg34
-rw-r--r--tests/sys/cddl/zfs/tests/compression/compress_001_pos.ksh81
-rw-r--r--tests/sys/cddl/zfs/tests/compression/compress_003_pos.ksh104
-rw-r--r--tests/sys/cddl/zfs/tests/compression/compress_004_pos.ksh141
-rwxr-xr-xtests/sys/cddl/zfs/tests/compression/compression_test.sh108
-rw-r--r--tests/sys/cddl/zfs/tests/compression/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/ctime/Makefile18
-rw-r--r--tests/sys/cddl/zfs/tests/ctime/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/ctime/ctime.cfg27
-rw-r--r--tests/sys/cddl/zfs/tests/ctime/ctime_001_pos.c379
-rwxr-xr-xtests/sys/cddl/zfs/tests/ctime/ctime_test.sh55
-rw-r--r--tests/sys/cddl/zfs/tests/ctime/setup.ksh30
-rw-r--r--tests/sys/cddl/zfs/tests/delegate/Makefile37
-rw-r--r--tests/sys/cddl/zfs/tests/delegate/cleanup.ksh32
-rw-r--r--tests/sys/cddl/zfs/tests/delegate/delegate.cfg57
-rw-r--r--tests/sys/cddl/zfs/tests/delegate/delegate_common.kshlib1701
-rw-r--r--tests/sys/cddl/zfs/tests/delegate/setup.ksh45
-rw-r--r--tests/sys/cddl/zfs/tests/delegate/zfs_allow_001_pos.ksh111
-rw-r--r--tests/sys/cddl/zfs/tests/delegate/zfs_allow_002_pos.ksh93
-rw-r--r--tests/sys/cddl/zfs/tests/delegate/zfs_allow_003_pos.ksh106
-rw-r--r--tests/sys/cddl/zfs/tests/delegate/zfs_allow_004_pos.ksh116
-rw-r--r--tests/sys/cddl/zfs/tests/delegate/zfs_allow_005_pos.ksh98
-rw-r--r--tests/sys/cddl/zfs/tests/delegate/zfs_allow_006_pos.ksh86
-rw-r--r--tests/sys/cddl/zfs/tests/delegate/zfs_allow_007_pos.ksh116
-rw-r--r--tests/sys/cddl/zfs/tests/delegate/zfs_allow_008_pos.ksh87
-rw-r--r--tests/sys/cddl/zfs/tests/delegate/zfs_allow_009_neg.ksh73
-rw-r--r--tests/sys/cddl/zfs/tests/delegate/zfs_allow_010_pos.ksh119
-rw-r--r--tests/sys/cddl/zfs/tests/delegate/zfs_allow_011_neg.ksh77
-rw-r--r--tests/sys/cddl/zfs/tests/delegate/zfs_allow_012_neg.ksh80
-rwxr-xr-xtests/sys/cddl/zfs/tests/delegate/zfs_allow_test.sh354
-rw-r--r--tests/sys/cddl/zfs/tests/delegate/zfs_unallow_001_pos.ksh74
-rw-r--r--tests/sys/cddl/zfs/tests/delegate/zfs_unallow_002_pos.ksh70
-rw-r--r--tests/sys/cddl/zfs/tests/delegate/zfs_unallow_003_pos.ksh80
-rw-r--r--tests/sys/cddl/zfs/tests/delegate/zfs_unallow_004_pos.ksh68
-rw-r--r--tests/sys/cddl/zfs/tests/delegate/zfs_unallow_005_pos.ksh82
-rw-r--r--tests/sys/cddl/zfs/tests/delegate/zfs_unallow_006_pos.ksh80
-rw-r--r--tests/sys/cddl/zfs/tests/delegate/zfs_unallow_007_neg.ksh73
-rw-r--r--tests/sys/cddl/zfs/tests/delegate/zfs_unallow_008_neg.ksh83
-rwxr-xr-xtests/sys/cddl/zfs/tests/delegate/zfs_unallow_test.sh246
-rw-r--r--tests/sys/cddl/zfs/tests/devices/Makefile20
-rw-r--r--tests/sys/cddl/zfs/tests/devices/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/devices/devices.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/devices/devices_001_pos.ksh74
-rw-r--r--tests/sys/cddl/zfs/tests/devices/devices_002_neg.ksh74
-rw-r--r--tests/sys/cddl/zfs/tests/devices/devices_003_pos.ksh58
-rw-r--r--tests/sys/cddl/zfs/tests/devices/devices_common.kshlib115
-rwxr-xr-xtests/sys/cddl/zfs/tests/devices/devices_test.sh110
-rw-r--r--tests/sys/cddl/zfs/tests/devices/setup.ksh30
-rw-r--r--tests/sys/cddl/zfs/tests/exec/Makefile19
-rw-r--r--tests/sys/cddl/zfs/tests/exec/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/exec/exec_001_pos.ksh72
-rw-r--r--tests/sys/cddl/zfs/tests/exec/exec_002_neg.ksh93
-rwxr-xr-xtests/sys/cddl/zfs/tests/exec/exec_test.sh78
-rw-r--r--tests/sys/cddl/zfs/tests/exec/mmap_exec.c68
-rw-r--r--tests/sys/cddl/zfs/tests/exec/setup.ksh30
-rw-r--r--tests/sys/cddl/zfs/tests/grow_pool/Makefile16
-rw-r--r--tests/sys/cddl/zfs/tests/grow_pool/cleanup.ksh41
-rw-r--r--tests/sys/cddl/zfs/tests/grow_pool/grow_pool.cfg41
-rw-r--r--tests/sys/cddl/zfs/tests/grow_pool/grow_pool_001_pos.ksh82
-rwxr-xr-xtests/sys/cddl/zfs/tests/grow_pool/grow_pool_test.sh55
-rw-r--r--tests/sys/cddl/zfs/tests/grow_pool/setup.ksh42
-rw-r--r--tests/sys/cddl/zfs/tests/grow_replicas/Makefile16
-rw-r--r--tests/sys/cddl/zfs/tests/grow_replicas/cleanup.ksh37
-rw-r--r--tests/sys/cddl/zfs/tests/grow_replicas/grow_replicas.cfg69
-rw-r--r--tests/sys/cddl/zfs/tests/grow_replicas/grow_replicas_001_pos.ksh91
-rwxr-xr-xtests/sys/cddl/zfs/tests/grow_replicas/grow_replicas_test.sh56
-rw-r--r--tests/sys/cddl/zfs/tests/grow_replicas/setup.ksh54
-rw-r--r--tests/sys/cddl/zfs/tests/history/Makefile31
-rw-r--r--tests/sys/cddl/zfs/tests/history/cleanup.ksh32
-rw-r--r--tests/sys/cddl/zfs/tests/history/history.cfg43
-rw-r--r--tests/sys/cddl/zfs/tests/history/history_001_pos.ksh116
-rw-r--r--tests/sys/cddl/zfs/tests/history/history_002_pos.ksh172
-rw-r--r--tests/sys/cddl/zfs/tests/history/history_003_pos.ksh84
-rw-r--r--tests/sys/cddl/zfs/tests/history/history_004_pos.ksh109
-rw-r--r--tests/sys/cddl/zfs/tests/history/history_005_neg.ksh77
-rw-r--r--tests/sys/cddl/zfs/tests/history/history_006_neg.ksh90
-rw-r--r--tests/sys/cddl/zfs/tests/history/history_007_pos.ksh124
-rw-r--r--tests/sys/cddl/zfs/tests/history/history_008_pos.ksh142
-rw-r--r--tests/sys/cddl/zfs/tests/history/history_009_pos.ksh175
-rw-r--r--tests/sys/cddl/zfs/tests/history/history_010_pos.ksh103
-rw-r--r--tests/sys/cddl/zfs/tests/history/history_common.kshlib263
-rwxr-xr-xtests/sys/cddl/zfs/tests/history/history_test.sh299
-rw-r--r--tests/sys/cddl/zfs/tests/history/i386.migratedpool.DAT.Zbin0 -> 173047 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/history/i386.orig_history.txt13
-rw-r--r--tests/sys/cddl/zfs/tests/history/setup.ksh33
-rw-r--r--tests/sys/cddl/zfs/tests/history/sparc.migratedpool.DAT.Zbin0 -> 163879 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/history/sparc.orig_history.txt13
-rw-r--r--tests/sys/cddl/zfs/tests/history/zfs-pool-v4.dat.Zbin0 -> 73415 bytes
-rw-r--r--tests/sys/cddl/zfs/tests/hotplug/Makefile19
-rw-r--r--tests/sys/cddl/zfs/tests/hotplug/cleanup.ksh30
-rw-r--r--tests/sys/cddl/zfs/tests/hotplug/hotplug.cfg31
-rw-r--r--tests/sys/cddl/zfs/tests/hotplug/hotplug.kshlib458
-rw-r--r--tests/sys/cddl/zfs/tests/hotplug/hotplug_001_pos.ksh72
-rw-r--r--tests/sys/cddl/zfs/tests/hotplug/hotplug_008_pos.ksh69
-rw-r--r--tests/sys/cddl/zfs/tests/hotplug/hotplug_011_pos.ksh79
-rwxr-xr-xtests/sys/cddl/zfs/tests/hotplug/hotplug_test.sh109
-rw-r--r--tests/sys/cddl/zfs/tests/hotplug/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/Makefile43
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/cleanup.ksh36
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare.cfg55
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare.kshlib121
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare_add_001_pos.ksh85
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare_add_002_pos.ksh101
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare_add_003_neg.ksh137
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare_add_004_neg.ksh90
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare_clone_001_pos.ksh123
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare_clone_002_pos.ksh131
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare_create_001_neg.ksh132
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare_detach_001_pos.ksh99
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare_detach_002_pos.ksh109
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare_detach_003_pos.ksh102
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare_detach_004_pos.ksh103
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare_detach_005_neg.ksh88
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare_export_001_neg.ksh122
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare_import_001_pos.ksh162
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare_onoffline_003_neg.ksh94
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare_onoffline_004_neg.ksh162
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare_remove_001_pos.ksh87
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare_remove_002_neg.ksh111
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare_remove_003_neg.ksh81
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare_remove_004_pos.ksh94
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare_replace_001_neg.ksh107
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare_replace_002_neg.ksh92
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare_scrub_001_pos.ksh115
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare_scrub_002_pos.ksh133
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare_shared_001_pos.ksh91
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare_snapshot_001_pos.ksh121
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/hotspare_snapshot_002_pos.ksh127
-rwxr-xr-xtests/sys/cddl/zfs/tests/hotspare/hotspare_test.sh761
-rw-r--r--tests/sys/cddl/zfs/tests/hotspare/setup.ksh38
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/Makefile64
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/README.config61
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/README.state102
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/config001.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/config002.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/config003.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/config004.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/config005.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/config006.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/config007.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/config008.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/config009.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/config010.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/config011.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/config012.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/config013.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/config014.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/config015.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/config016.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/config017.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/config018.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/config019.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/config020.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/config021.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/config022.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/config023.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/config024.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/inherit.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/inherit.kshlib110
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/inherit_001_pos.ksh435
-rwxr-xr-xtests/sys/cddl/zfs/tests/inheritance/inheritance_test.sh55
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/state001.cfg39
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/state002.cfg40
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/state003.cfg38
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/state004.cfg39
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/state005.cfg40
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/state006.cfg42
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/state007.cfg40
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/state008.cfg39
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/state009.cfg52
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/state010.cfg51
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/state011.cfg53
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/state012.cfg57
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/state013.cfg51
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/state014.cfg57
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/state015.cfg61
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/state016.cfg57
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/state017.cfg62
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/state018.cfg59
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/state019.cfg58
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/state020.cfg59
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/state021.cfg59
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/state022.cfg58
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/state023.cfg60
-rw-r--r--tests/sys/cddl/zfs/tests/inheritance/state024.cfg58
-rw-r--r--tests/sys/cddl/zfs/tests/interop/Makefile16
-rw-r--r--tests/sys/cddl/zfs/tests/interop/cleanup.ksh67
-rw-r--r--tests/sys/cddl/zfs/tests/interop/interop.cfg77
-rw-r--r--tests/sys/cddl/zfs/tests/interop/interop_001_pos.ksh96
-rwxr-xr-xtests/sys/cddl/zfs/tests/interop/interop_test.sh54
-rw-r--r--tests/sys/cddl/zfs/tests/interop/setup.ksh78
-rw-r--r--tests/sys/cddl/zfs/tests/inuse/Makefile16
-rw-r--r--tests/sys/cddl/zfs/tests/inuse/inuse.cfg121
-rw-r--r--tests/sys/cddl/zfs/tests/inuse/inuse_005_pos.ksh69
-rw-r--r--tests/sys/cddl/zfs/tests/inuse/inuse_010_neg.ksh51
-rwxr-xr-xtests/sys/cddl/zfs/tests/inuse/inuse_test.sh64
-rw-r--r--tests/sys/cddl/zfs/tests/inuse/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/iscsi/Makefile22
-rw-r--r--tests/sys/cddl/zfs/tests/iscsi/cleanup.ksh33
-rw-r--r--tests/sys/cddl/zfs/tests/iscsi/iscsi.cfg29
-rw-r--r--tests/sys/cddl/zfs/tests/iscsi/iscsi_001_pos.ksh94
-rw-r--r--tests/sys/cddl/zfs/tests/iscsi/iscsi_002_neg.ksh83
-rw-r--r--tests/sys/cddl/zfs/tests/iscsi/iscsi_003_neg.ksh74
-rw-r--r--tests/sys/cddl/zfs/tests/iscsi/iscsi_004_pos.ksh83
-rw-r--r--tests/sys/cddl/zfs/tests/iscsi/iscsi_005_pos.ksh89
-rw-r--r--tests/sys/cddl/zfs/tests/iscsi/iscsi_006_neg.ksh83
-rw-r--r--tests/sys/cddl/zfs/tests/iscsi/iscsi_common.kshlib82
-rwxr-xr-xtests/sys/cddl/zfs/tests/iscsi/iscsi_test.sh186
-rw-r--r--tests/sys/cddl/zfs/tests/iscsi/setup.ksh35
-rw-r--r--tests/sys/cddl/zfs/tests/large_files/Makefile16
-rw-r--r--tests/sys/cddl/zfs/tests/large_files/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/large_files/large_files.cfg27
-rw-r--r--tests/sys/cddl/zfs/tests/large_files/large_files_001_pos.ksh62
-rwxr-xr-xtests/sys/cddl/zfs/tests/large_files/large_files_test.sh54
-rw-r--r--tests/sys/cddl/zfs/tests/large_files/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/largest_pool/Makefile14
-rw-r--r--tests/sys/cddl/zfs/tests/largest_pool/largest_pool.cfg40
-rw-r--r--tests/sys/cddl/zfs/tests/largest_pool/largest_pool_001_pos.ksh163
-rwxr-xr-xtests/sys/cddl/zfs/tests/largest_pool/largest_pool_test.sh55
-rw-r--r--tests/sys/cddl/zfs/tests/link_count/Makefile16
-rw-r--r--tests/sys/cddl/zfs/tests/link_count/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/link_count/link_count.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/link_count/link_count_001.ksh97
-rwxr-xr-xtests/sys/cddl/zfs/tests/link_count/link_count_test.sh53
-rw-r--r--tests/sys/cddl/zfs/tests/link_count/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/migration/Makefile28
-rw-r--r--tests/sys/cddl/zfs/tests/migration/cleanup.ksh52
-rw-r--r--tests/sys/cddl/zfs/tests/migration/migration.cfg62
-rw-r--r--tests/sys/cddl/zfs/tests/migration/migration.kshlib147
-rw-r--r--tests/sys/cddl/zfs/tests/migration/migration_001_pos.ksh75
-rw-r--r--tests/sys/cddl/zfs/tests/migration/migration_002_pos.ksh75
-rw-r--r--tests/sys/cddl/zfs/tests/migration/migration_003_pos.ksh75
-rw-r--r--tests/sys/cddl/zfs/tests/migration/migration_004_pos.ksh82
-rw-r--r--tests/sys/cddl/zfs/tests/migration/migration_005_pos.ksh82
-rw-r--r--tests/sys/cddl/zfs/tests/migration/migration_006_pos.ksh82
-rw-r--r--tests/sys/cddl/zfs/tests/migration/migration_007_pos.ksh75
-rw-r--r--tests/sys/cddl/zfs/tests/migration/migration_008_pos.ksh75
-rw-r--r--tests/sys/cddl/zfs/tests/migration/migration_009_pos.ksh75
-rw-r--r--tests/sys/cddl/zfs/tests/migration/migration_010_pos.ksh75
-rw-r--r--tests/sys/cddl/zfs/tests/migration/migration_011_pos.ksh75
-rw-r--r--tests/sys/cddl/zfs/tests/migration/migration_012_pos.ksh75
-rwxr-xr-xtests/sys/cddl/zfs/tests/migration/migration_test.sh354
-rw-r--r--tests/sys/cddl/zfs/tests/migration/setup.ksh64
-rw-r--r--tests/sys/cddl/zfs/tests/mmap/Makefile17
-rw-r--r--tests/sys/cddl/zfs/tests/mmap/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/mmap/mmap.cfg27
-rw-r--r--tests/sys/cddl/zfs/tests/mmap/mmap_read_001_pos.ksh64
-rwxr-xr-xtests/sys/cddl/zfs/tests/mmap/mmap_test.sh78
-rw-r--r--tests/sys/cddl/zfs/tests/mmap/mmap_write_001_pos.ksh88
-rw-r--r--tests/sys/cddl/zfs/tests/mmap/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/mount/Makefile17
-rw-r--r--tests/sys/cddl/zfs/tests/mount/cleanup.ksh29
-rwxr-xr-xtests/sys/cddl/zfs/tests/mount/mount_test.sh84
-rw-r--r--tests/sys/cddl/zfs/tests/mount/mounttest.ksh96
-rw-r--r--tests/sys/cddl/zfs/tests/mount/setup.ksh64
-rw-r--r--tests/sys/cddl/zfs/tests/mount/vars.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/mv_files/Makefile18
-rw-r--r--tests/sys/cddl/zfs/tests/mv_files/cleanup.ksh34
-rw-r--r--tests/sys/cddl/zfs/tests/mv_files/mv_files.cfg41
-rw-r--r--tests/sys/cddl/zfs/tests/mv_files/mv_files_001_pos.ksh79
-rw-r--r--tests/sys/cddl/zfs/tests/mv_files/mv_files_002_pos.ksh80
-rw-r--r--tests/sys/cddl/zfs/tests/mv_files/mv_files_common.kshlib211
-rwxr-xr-xtests/sys/cddl/zfs/tests/mv_files/mv_files_test.sh86
-rw-r--r--tests/sys/cddl/zfs/tests/mv_files/setup.ksh42
-rw-r--r--tests/sys/cddl/zfs/tests/nestedfs/Makefile16
-rw-r--r--tests/sys/cddl/zfs/tests/nestedfs/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/nestedfs/nestedfs.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/nestedfs/nestedfs_001_pos.ksh74
-rwxr-xr-xtests/sys/cddl/zfs/tests/nestedfs/nestedfs_test.sh55
-rw-r--r--tests/sys/cddl/zfs/tests/nestedfs/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/no_space/Makefile16
-rw-r--r--tests/sys/cddl/zfs/tests/no_space/cleanup.ksh42
-rw-r--r--tests/sys/cddl/zfs/tests/no_space/enospc.cfg34
-rw-r--r--tests/sys/cddl/zfs/tests/no_space/enospc_001_pos.ksh76
-rwxr-xr-xtests/sys/cddl/zfs/tests/no_space/no_space_test.sh55
-rw-r--r--tests/sys/cddl/zfs/tests/no_space/setup.ksh35
-rw-r--r--tests/sys/cddl/zfs/tests/online_offline/Makefile16
-rw-r--r--tests/sys/cddl/zfs/tests/online_offline/cleanup.ksh34
-rw-r--r--tests/sys/cddl/zfs/tests/online_offline/online_offline.cfg29
-rw-r--r--tests/sys/cddl/zfs/tests/online_offline/online_offline_001_pos.ksh65
-rw-r--r--tests/sys/cddl/zfs/tests/online_offline/online_offline_002_neg.ksh79
-rwxr-xr-xtests/sys/cddl/zfs/tests/online_offline/online_offline_test.sh80
-rw-r--r--tests/sys/cddl/zfs/tests/pool_names/Makefile15
-rw-r--r--tests/sys/cddl/zfs/tests/pool_names/pool_names.cfg27
-rw-r--r--tests/sys/cddl/zfs/tests/pool_names/pool_names_001_pos.ksh121
-rw-r--r--tests/sys/cddl/zfs/tests/pool_names/pool_names_002_neg.ksh142
-rwxr-xr-xtests/sys/cddl/zfs/tests/pool_names/pool_names_test.sh66
-rw-r--r--tests/sys/cddl/zfs/tests/poolversion/Makefile16
-rw-r--r--tests/sys/cddl/zfs/tests/poolversion/cleanup.ksh40
-rw-r--r--tests/sys/cddl/zfs/tests/poolversion/poolversion_001_pos.ksh68
-rw-r--r--tests/sys/cddl/zfs/tests/poolversion/poolversion_002_pos.ksh81
-rwxr-xr-xtests/sys/cddl/zfs/tests/poolversion/poolversion_test.sh76
-rw-r--r--tests/sys/cddl/zfs/tests/poolversion/setup.ksh45
-rw-r--r--tests/sys/cddl/zfs/tests/quota/Makefile22
-rw-r--r--tests/sys/cddl/zfs/tests/quota/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/quota/quota.cfg31
-rw-r--r--tests/sys/cddl/zfs/tests/quota/quota.kshlib83
-rw-r--r--tests/sys/cddl/zfs/tests/quota/quota_001_pos.ksh86
-rw-r--r--tests/sys/cddl/zfs/tests/quota/quota_002_pos.ksh83
-rw-r--r--tests/sys/cddl/zfs/tests/quota/quota_003_pos.ksh89
-rw-r--r--tests/sys/cddl/zfs/tests/quota/quota_004_pos.ksh84
-rw-r--r--tests/sys/cddl/zfs/tests/quota/quota_005_pos.ksh83
-rw-r--r--tests/sys/cddl/zfs/tests/quota/quota_006_neg.ksh79
-rwxr-xr-xtests/sys/cddl/zfs/tests/quota/quota_test.sh192
-rw-r--r--tests/sys/cddl/zfs/tests/quota/setup.ksh32
-rw-r--r--tests/sys/cddl/zfs/tests/redundancy/Makefile20
-rw-r--r--tests/sys/cddl/zfs/tests/redundancy/cleanup.ksh33
-rw-r--r--tests/sys/cddl/zfs/tests/redundancy/redundancy.cfg38
-rw-r--r--tests/sys/cddl/zfs/tests/redundancy/redundancy.kshlib299
-rw-r--r--tests/sys/cddl/zfs/tests/redundancy/redundancy_001_pos.ksh85
-rw-r--r--tests/sys/cddl/zfs/tests/redundancy/redundancy_002_pos.ksh93
-rw-r--r--tests/sys/cddl/zfs/tests/redundancy/redundancy_003_pos.ksh103
-rw-r--r--tests/sys/cddl/zfs/tests/redundancy/redundancy_004_neg.ksh69
-rwxr-xr-xtests/sys/cddl/zfs/tests/redundancy/redundancy_test.sh135
-rw-r--r--tests/sys/cddl/zfs/tests/redundancy/setup.ksh32
-rw-r--r--tests/sys/cddl/zfs/tests/refquota/Makefile21
-rw-r--r--tests/sys/cddl/zfs/tests/refquota/cleanup.ksh34
-rw-r--r--tests/sys/cddl/zfs/tests/refquota/refquota.cfg27
-rw-r--r--tests/sys/cddl/zfs/tests/refquota/refquota_001_pos.ksh86
-rw-r--r--tests/sys/cddl/zfs/tests/refquota/refquota_002_pos.ksh98
-rw-r--r--tests/sys/cddl/zfs/tests/refquota/refquota_003_pos.ksh91
-rw-r--r--tests/sys/cddl/zfs/tests/refquota/refquota_004_pos.ksh84
-rw-r--r--tests/sys/cddl/zfs/tests/refquota/refquota_005_pos.ksh85
-rw-r--r--tests/sys/cddl/zfs/tests/refquota/refquota_006_neg.ksh87
-rwxr-xr-xtests/sys/cddl/zfs/tests/refquota/refquota_test.sh180
-rw-r--r--tests/sys/cddl/zfs/tests/refquota/setup.ksh35
-rw-r--r--tests/sys/cddl/zfs/tests/refreserv/Makefile20
-rw-r--r--tests/sys/cddl/zfs/tests/refreserv/cleanup.ksh34
-rw-r--r--tests/sys/cddl/zfs/tests/refreserv/refreserv.cfg27
-rw-r--r--tests/sys/cddl/zfs/tests/refreserv/refreserv_001_pos.ksh83
-rw-r--r--tests/sys/cddl/zfs/tests/refreserv/refreserv_002_pos.ksh122
-rw-r--r--tests/sys/cddl/zfs/tests/refreserv/refreserv_003_pos.ksh85
-rw-r--r--tests/sys/cddl/zfs/tests/refreserv/refreserv_004_pos.ksh97
-rw-r--r--tests/sys/cddl/zfs/tests/refreserv/refreserv_005_pos.ksh80
-rwxr-xr-xtests/sys/cddl/zfs/tests/refreserv/refreserv_test.sh155
-rw-r--r--tests/sys/cddl/zfs/tests/refreserv/setup.ksh35
-rw-r--r--tests/sys/cddl/zfs/tests/rename_dirs/Makefile16
-rw-r--r--tests/sys/cddl/zfs/tests/rename_dirs/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/rename_dirs/rename_dirs.cfg28
-rw-r--r--tests/sys/cddl/zfs/tests/rename_dirs/rename_dirs_001_pos.ksh80
-rwxr-xr-xtests/sys/cddl/zfs/tests/rename_dirs/rename_dirs_test.sh54
-rw-r--r--tests/sys/cddl/zfs/tests/rename_dirs/setup.ksh30
-rw-r--r--tests/sys/cddl/zfs/tests/replacement/Makefile17
-rw-r--r--tests/sys/cddl/zfs/tests/replacement/cleanup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/replacement/replacement.kshlib49
-rw-r--r--tests/sys/cddl/zfs/tests/replacement/replacement_001_pos.ksh42
-rw-r--r--tests/sys/cddl/zfs/tests/replacement/replacement_002_pos.ksh48
-rw-r--r--tests/sys/cddl/zfs/tests/replacement/replacement_003_pos.ksh46
-rwxr-xr-xtests/sys/cddl/zfs/tests/replacement/replacement_test.sh98
-rw-r--r--tests/sys/cddl/zfs/tests/reservation/Makefile34
-rw-r--r--tests/sys/cddl/zfs/tests/reservation/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/reservation/reservation.cfg42
-rw-r--r--tests/sys/cddl/zfs/tests/reservation/reservation.kshlib175
-rw-r--r--tests/sys/cddl/zfs/tests/reservation/reservation_001_pos.ksh127
-rw-r--r--tests/sys/cddl/zfs/tests/reservation/reservation_002_pos.ksh106
-rw-r--r--tests/sys/cddl/zfs/tests/reservation/reservation_003_pos.ksh132
-rw-r--r--tests/sys/cddl/zfs/tests/reservation/reservation_004_pos.ksh126
-rw-r--r--tests/sys/cddl/zfs/tests/reservation/reservation_005_pos.ksh118
-rw-r--r--tests/sys/cddl/zfs/tests/reservation/reservation_006_pos.ksh82
-rw-r--r--tests/sys/cddl/zfs/tests/reservation/reservation_007_pos.ksh126
-rw-r--r--tests/sys/cddl/zfs/tests/reservation/reservation_008_pos.ksh118
-rw-r--r--tests/sys/cddl/zfs/tests/reservation/reservation_009_pos.ksh100
-rw-r--r--tests/sys/cddl/zfs/tests/reservation/reservation_010_pos.ksh100
-rw-r--r--tests/sys/cddl/zfs/tests/reservation/reservation_011_pos.ksh76
-rw-r--r--tests/sys/cddl/zfs/tests/reservation/reservation_012_pos.ksh93
-rw-r--r--tests/sys/cddl/zfs/tests/reservation/reservation_013_pos.ksh116
-rw-r--r--tests/sys/cddl/zfs/tests/reservation/reservation_014_pos.ksh121
-rw-r--r--tests/sys/cddl/zfs/tests/reservation/reservation_015_pos.ksh109
-rw-r--r--tests/sys/cddl/zfs/tests/reservation/reservation_016_pos.ksh107
-rw-r--r--tests/sys/cddl/zfs/tests/reservation/reservation_017_pos.ksh100
-rw-r--r--tests/sys/cddl/zfs/tests/reservation/reservation_018_pos.ksh73
-rwxr-xr-xtests/sys/cddl/zfs/tests/reservation/reservation_test.sh522
-rw-r--r--tests/sys/cddl/zfs/tests/reservation/setup.ksh38
-rw-r--r--tests/sys/cddl/zfs/tests/rootpool/Makefile17
-rw-r--r--tests/sys/cddl/zfs/tests/rootpool/cleanup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/rootpool/rootpool_001_pos.ksh66
-rw-r--r--tests/sys/cddl/zfs/tests/rootpool/rootpool_002_neg.ksh66
-rw-r--r--tests/sys/cddl/zfs/tests/rootpool/rootpool_007_neg.ksh79
-rwxr-xr-xtests/sys/cddl/zfs/tests/rootpool/rootpool_test.sh105
-rw-r--r--tests/sys/cddl/zfs/tests/rootpool/setup.ksh34
-rw-r--r--tests/sys/cddl/zfs/tests/rsend/Makefile29
-rw-r--r--tests/sys/cddl/zfs/tests/rsend/cleanup.ksh48
-rw-r--r--tests/sys/cddl/zfs/tests/rsend/rsend.cfg38
-rw-r--r--tests/sys/cddl/zfs/tests/rsend/rsend.kshlib368
-rw-r--r--tests/sys/cddl/zfs/tests/rsend/rsend_001_pos.ksh82
-rw-r--r--tests/sys/cddl/zfs/tests/rsend/rsend_002_pos.ksh102
-rw-r--r--tests/sys/cddl/zfs/tests/rsend/rsend_003_pos.ksh104
-rw-r--r--tests/sys/cddl/zfs/tests/rsend/rsend_004_pos.ksh129
-rw-r--r--tests/sys/cddl/zfs/tests/rsend/rsend_005_pos.ksh113
-rw-r--r--tests/sys/cddl/zfs/tests/rsend/rsend_006_pos.ksh91
-rw-r--r--tests/sys/cddl/zfs/tests/rsend/rsend_007_pos.ksh108
-rw-r--r--tests/sys/cddl/zfs/tests/rsend/rsend_008_pos.ksh137
-rw-r--r--tests/sys/cddl/zfs/tests/rsend/rsend_009_pos.ksh103
-rw-r--r--tests/sys/cddl/zfs/tests/rsend/rsend_010_pos.ksh86
-rw-r--r--tests/sys/cddl/zfs/tests/rsend/rsend_011_pos.ksh132
-rw-r--r--tests/sys/cddl/zfs/tests/rsend/rsend_012_pos.ksh206
-rw-r--r--tests/sys/cddl/zfs/tests/rsend/rsend_013_pos.ksh95
-rwxr-xr-xtests/sys/cddl/zfs/tests/rsend/rsend_test.sh394
-rw-r--r--tests/sys/cddl/zfs/tests/rsend/setup.ksh47
-rw-r--r--tests/sys/cddl/zfs/tests/scrub_mirror/Makefile20
-rw-r--r--tests/sys/cddl/zfs/tests/scrub_mirror/cleanup.ksh43
-rw-r--r--tests/sys/cddl/zfs/tests/scrub_mirror/default.cfg52
-rw-r--r--tests/sys/cddl/zfs/tests/scrub_mirror/scrub_mirror_001_pos.ksh62
-rw-r--r--tests/sys/cddl/zfs/tests/scrub_mirror/scrub_mirror_002_pos.ksh62
-rw-r--r--tests/sys/cddl/zfs/tests/scrub_mirror/scrub_mirror_003_pos.ksh62
-rw-r--r--tests/sys/cddl/zfs/tests/scrub_mirror/scrub_mirror_004_pos.ksh62
-rw-r--r--tests/sys/cddl/zfs/tests/scrub_mirror/scrub_mirror_common.kshlib78
-rwxr-xr-xtests/sys/cddl/zfs/tests/scrub_mirror/scrub_mirror_test.sh138
-rw-r--r--tests/sys/cddl/zfs/tests/scrub_mirror/setup.ksh42
-rw-r--r--tests/sys/cddl/zfs/tests/slog/Makefile30
-rw-r--r--tests/sys/cddl/zfs/tests/slog/cleanup.ksh44
-rw-r--r--tests/sys/cddl/zfs/tests/slog/setup.ksh40
-rw-r--r--tests/sys/cddl/zfs/tests/slog/slog.cfg39
-rw-r--r--tests/sys/cddl/zfs/tests/slog/slog.kshlib176
-rw-r--r--tests/sys/cddl/zfs/tests/slog/slog_001_pos.ksh73
-rw-r--r--tests/sys/cddl/zfs/tests/slog/slog_002_pos.ksh71
-rw-r--r--tests/sys/cddl/zfs/tests/slog/slog_003_pos.ksh78
-rw-r--r--tests/sys/cddl/zfs/tests/slog/slog_004_pos.ksh77
-rw-r--r--tests/sys/cddl/zfs/tests/slog/slog_005_pos.ksh75
-rw-r--r--tests/sys/cddl/zfs/tests/slog/slog_006_pos.ksh75
-rw-r--r--tests/sys/cddl/zfs/tests/slog/slog_007_pos.ksh86
-rw-r--r--tests/sys/cddl/zfs/tests/slog/slog_008_neg.ksh72
-rw-r--r--tests/sys/cddl/zfs/tests/slog/slog_009_neg.ksh73
-rw-r--r--tests/sys/cddl/zfs/tests/slog/slog_010_neg.ksh73
-rw-r--r--tests/sys/cddl/zfs/tests/slog/slog_011_neg.ksh77
-rw-r--r--tests/sys/cddl/zfs/tests/slog/slog_012_neg.ksh84
-rw-r--r--tests/sys/cddl/zfs/tests/slog/slog_013_pos.ksh102
-rw-r--r--tests/sys/cddl/zfs/tests/slog/slog_014_pos.ksh88
-rwxr-xr-xtests/sys/cddl/zfs/tests/slog/slog_test.sh409
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/Makefile40
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/clone_001_pos.ksh138
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/ctldir_acl.txt3
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/rollback_001_pos.ksh109
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/rollback_002_pos.ksh121
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/rollback_003_pos.ksh109
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/snapshot.cfg57
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/snapshot_001_pos.ksh98
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/snapshot_002_pos.ksh134
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/snapshot_003_pos.ksh103
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/snapshot_004_pos.ksh91
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/snapshot_005_pos.ksh97
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/snapshot_006_pos.ksh132
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/snapshot_007_pos.ksh107
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/snapshot_008_pos.ksh100
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/snapshot_009_pos.ksh126
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/snapshot_010_pos.ksh108
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/snapshot_011_pos.ksh109
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/snapshot_012_pos.ksh111
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/snapshot_013_pos.ksh101
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/snapshot_014_pos.ksh85
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/snapshot_015_pos.ksh131
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/snapshot_016_pos.ksh111
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/snapshot_017_pos.ksh208
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/snapshot_018_pos.ksh93
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/snapshot_019_pos.ksh117
-rw-r--r--tests/sys/cddl/zfs/tests/snapshot/snapshot_020_pos.ksh87
-rwxr-xr-xtests/sys/cddl/zfs/tests/snapshot/snapshot_test.sh632
-rw-r--r--tests/sys/cddl/zfs/tests/snapused/Makefile21
-rw-r--r--tests/sys/cddl/zfs/tests/snapused/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/snapused/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/snapused/snapused.cfg29
-rw-r--r--tests/sys/cddl/zfs/tests/snapused/snapused.kshlib186
-rw-r--r--tests/sys/cddl/zfs/tests/snapused/snapused_001_pos.ksh105
-rw-r--r--tests/sys/cddl/zfs/tests/snapused/snapused_002_pos.ksh96
-rw-r--r--tests/sys/cddl/zfs/tests/snapused/snapused_003_pos.ksh96
-rw-r--r--tests/sys/cddl/zfs/tests/snapused/snapused_004_pos.ksh109
-rw-r--r--tests/sys/cddl/zfs/tests/snapused/snapused_005_pos.ksh87
-rwxr-xr-xtests/sys/cddl/zfs/tests/snapused/snapused_test.sh165
-rw-r--r--tests/sys/cddl/zfs/tests/sparse/Makefile16
-rw-r--r--tests/sys/cddl/zfs/tests/sparse/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/sparse/setup.ksh32
-rw-r--r--tests/sys/cddl/zfs/tests/sparse/sparse.cfg33
-rw-r--r--tests/sys/cddl/zfs/tests/sparse/sparse_001_pos.ksh88
-rwxr-xr-xtests/sys/cddl/zfs/tests/sparse/sparse_test.sh56
-rw-r--r--tests/sys/cddl/zfs/tests/threadsappend/Makefile20
-rw-r--r--tests/sys/cddl/zfs/tests/threadsappend/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/threadsappend/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/threadsappend/threadsappend.c133
-rw-r--r--tests/sys/cddl/zfs/tests/threadsappend/threadsappend.cfg27
-rw-r--r--tests/sys/cddl/zfs/tests/threadsappend/threadsappend_001_pos.ksh90
-rwxr-xr-xtests/sys/cddl/zfs/tests/threadsappend/threadsappend_test.sh55
-rw-r--r--tests/sys/cddl/zfs/tests/truncate/Makefile17
-rw-r--r--tests/sys/cddl/zfs/tests/truncate/cleanup.ksh29
-rw-r--r--tests/sys/cddl/zfs/tests/truncate/setup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/truncate/truncate.cfg33
-rw-r--r--tests/sys/cddl/zfs/tests/truncate/truncate_001_pos.ksh87
-rw-r--r--tests/sys/cddl/zfs/tests/truncate/truncate_002_pos.ksh75
-rwxr-xr-xtests/sys/cddl/zfs/tests/truncate/truncate_test.sh81
-rw-r--r--tests/sys/cddl/zfs/tests/txg_integrity/Makefile24
-rw-r--r--tests/sys/cddl/zfs/tests/txg_integrity/cleanup.ksh31
-rw-r--r--tests/sys/cddl/zfs/tests/txg_integrity/fsync_integrity.c516
-rw-r--r--tests/sys/cddl/zfs/tests/txg_integrity/fsync_integrity_001_pos.ksh81
-rw-r--r--tests/sys/cddl/zfs/tests/txg_integrity/make_patterns.py57
-rw-r--r--tests/sys/cddl/zfs/tests/txg_integrity/setup.ksh44
-rw-r--r--tests/sys/cddl/zfs/tests/txg_integrity/txg_integrity.c608
-rw-r--r--tests/sys/cddl/zfs/tests/txg_integrity/txg_integrity.cfg29
-rw-r--r--tests/sys/cddl/zfs/tests/txg_integrity/txg_integrity.d115
-rw-r--r--tests/sys/cddl/zfs/tests/txg_integrity/txg_integrity_001_pos.ksh72
-rwxr-xr-xtests/sys/cddl/zfs/tests/txg_integrity/txg_integrity_test.sh111
-rw-r--r--tests/sys/cddl/zfs/tests/userquota/Makefile32
-rw-r--r--tests/sys/cddl/zfs/tests/userquota/cleanup.ksh36
-rw-r--r--tests/sys/cddl/zfs/tests/userquota/groupspace_001_pos.ksh93
-rw-r--r--tests/sys/cddl/zfs/tests/userquota/groupspace_002_pos.ksh88
-rw-r--r--tests/sys/cddl/zfs/tests/userquota/setup.ksh43
-rw-r--r--tests/sys/cddl/zfs/tests/userquota/userquota.cfg43
-rw-r--r--tests/sys/cddl/zfs/tests/userquota/userquota_001_pos.ksh83
-rw-r--r--tests/sys/cddl/zfs/tests/userquota/userquota_002_pos.ksh93
-rw-r--r--tests/sys/cddl/zfs/tests/userquota/userquota_003_pos.ksh70
-rw-r--r--tests/sys/cddl/zfs/tests/userquota/userquota_004_pos.ksh90
-rw-r--r--tests/sys/cddl/zfs/tests/userquota/userquota_005_neg.ksh103
-rw-r--r--tests/sys/cddl/zfs/tests/userquota/userquota_006_pos.ksh84
-rw-r--r--tests/sys/cddl/zfs/tests/userquota/userquota_007_pos.ksh84
-rw-r--r--tests/sys/cddl/zfs/tests/userquota/userquota_008_pos.ksh69
-rw-r--r--tests/sys/cddl/zfs/tests/userquota/userquota_009_pos.ksh101
-rw-r--r--tests/sys/cddl/zfs/tests/userquota/userquota_010_pos.ksh83
-rw-r--r--tests/sys/cddl/zfs/tests/userquota/userquota_011_pos.ksh136
-rw-r--r--tests/sys/cddl/zfs/tests/userquota/userquota_012_neg.ksh76
-rw-r--r--tests/sys/cddl/zfs/tests/userquota/userquota_common.kshlib142
-rwxr-xr-xtests/sys/cddl/zfs/tests/userquota/userquota_test.sh462
-rw-r--r--tests/sys/cddl/zfs/tests/userquota/userspace_001_pos.ksh93
-rw-r--r--tests/sys/cddl/zfs/tests/userquota/userspace_002_pos.ksh90
-rw-r--r--tests/sys/cddl/zfs/tests/utils_test/Makefile25
-rw-r--r--tests/sys/cddl/zfs/tests/utils_test/cleanup.ksh28
-rw-r--r--tests/sys/cddl/zfs/tests/utils_test/setup.ksh28
-rw-r--r--tests/sys/cddl/zfs/tests/utils_test/utils_test.cfg36
-rw-r--r--tests/sys/cddl/zfs/tests/utils_test/utils_test.kshlib25
-rw-r--r--tests/sys/cddl/zfs/tests/utils_test/utils_test_001_pos.ksh70
-rw-r--r--tests/sys/cddl/zfs/tests/utils_test/utils_test_002_pos.ksh76
-rw-r--r--tests/sys/cddl/zfs/tests/utils_test/utils_test_003_pos.ksh71
-rw-r--r--tests/sys/cddl/zfs/tests/utils_test/utils_test_004_pos.ksh68
-rw-r--r--tests/sys/cddl/zfs/tests/utils_test/utils_test_005_pos.ksh70
-rw-r--r--tests/sys/cddl/zfs/tests/utils_test/utils_test_006_pos.ksh74
-rw-r--r--tests/sys/cddl/zfs/tests/utils_test/utils_test_007_pos.ksh80
-rw-r--r--tests/sys/cddl/zfs/tests/utils_test/utils_test_008_pos.ksh76
-rw-r--r--tests/sys/cddl/zfs/tests/utils_test/utils_test_009_pos.ksh68
-rwxr-xr-xtests/sys/cddl/zfs/tests/utils_test/utils_test_test.sh274
-rw-r--r--tests/sys/cddl/zfs/tests/write_dirs/Makefile17
-rw-r--r--tests/sys/cddl/zfs/tests/write_dirs/cleanup.ksh32
-rw-r--r--tests/sys/cddl/zfs/tests/write_dirs/setup.ksh36
-rw-r--r--tests/sys/cddl/zfs/tests/write_dirs/write_dirs.cfg31
-rw-r--r--tests/sys/cddl/zfs/tests/write_dirs/write_dirs_001_pos.ksh85
-rw-r--r--tests/sys/cddl/zfs/tests/write_dirs/write_dirs_002_pos.ksh86
-rwxr-xr-xtests/sys/cddl/zfs/tests/write_dirs/write_dirs_test.sh82
-rw-r--r--tests/sys/cddl/zfs/tests/xattr/Makefile29
-rw-r--r--tests/sys/cddl/zfs/tests/xattr/cleanup.ksh38
-rw-r--r--tests/sys/cddl/zfs/tests/xattr/setup.ksh57
-rw-r--r--tests/sys/cddl/zfs/tests/xattr/xattr.cfg49
-rw-r--r--tests/sys/cddl/zfs/tests/xattr/xattr_001_pos.ksh72
-rw-r--r--tests/sys/cddl/zfs/tests/xattr/xattr_002_neg.ksh65
-rw-r--r--tests/sys/cddl/zfs/tests/xattr/xattr_003_neg.ksh76
-rw-r--r--tests/sys/cddl/zfs/tests/xattr/xattr_004_pos.ksh97
-rw-r--r--tests/sys/cddl/zfs/tests/xattr/xattr_005_pos.ksh87
-rw-r--r--tests/sys/cddl/zfs/tests/xattr/xattr_006_pos.ksh72
-rw-r--r--tests/sys/cddl/zfs/tests/xattr/xattr_007_neg.ksh97
-rw-r--r--tests/sys/cddl/zfs/tests/xattr/xattr_008_pos.ksh107
-rw-r--r--tests/sys/cddl/zfs/tests/xattr/xattr_009_neg.ksh73
-rw-r--r--tests/sys/cddl/zfs/tests/xattr/xattr_010_neg.ksh77
-rw-r--r--tests/sys/cddl/zfs/tests/xattr/xattr_011_pos.ksh198
-rw-r--r--tests/sys/cddl/zfs/tests/xattr/xattr_012_pos.ksh114
-rw-r--r--tests/sys/cddl/zfs/tests/xattr/xattr_013_pos.ksh99
-rw-r--r--tests/sys/cddl/zfs/tests/xattr/xattr_common.kshlib103
-rwxr-xr-xtests/sys/cddl/zfs/tests/xattr/xattr_test.sh368
-rw-r--r--tests/sys/cddl/zfs/tests/zfsd/Makefile39
-rw-r--r--tests/sys/cddl/zfs/tests/zfsd/cleanup.ksh36
-rw-r--r--tests/sys/cddl/zfs/tests/zfsd/hotspare_cleanup.ksh45
-rw-r--r--tests/sys/cddl/zfs/tests/zfsd/hotspare_setup.ksh43
-rw-r--r--tests/sys/cddl/zfs/tests/zfsd/setup.ksh33
-rw-r--r--tests/sys/cddl/zfs/tests/zfsd/zfsd.cfg30
-rw-r--r--tests/sys/cddl/zfs/tests/zfsd/zfsd.kshlib101
-rw-r--r--tests/sys/cddl/zfs/tests/zfsd/zfsd_autoreplace_001_neg.ksh91
-rw-r--r--tests/sys/cddl/zfs/tests/zfsd/zfsd_autoreplace_002_pos.ksh88
-rw-r--r--tests/sys/cddl/zfs/tests/zfsd/zfsd_autoreplace_003_pos.ksh107
-rw-r--r--tests/sys/cddl/zfs/tests/zfsd/zfsd_degrade_001_pos.ksh89
-rw-r--r--tests/sys/cddl/zfs/tests/zfsd/zfsd_degrade_002_pos.ksh101
-rw-r--r--tests/sys/cddl/zfs/tests/zfsd/zfsd_fault_001_pos.ksh123
-rw-r--r--tests/sys/cddl/zfs/tests/zfsd/zfsd_fault_002_pos.ksh99
-rw-r--r--tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_001_pos.ksh149
-rw-r--r--tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_002_pos.ksh109
-rw-r--r--tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_003_pos.ksh104
-rw-r--r--tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_004_pos.ksh103
-rw-r--r--tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_005_pos.ksh107
-rw-r--r--tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_006_pos.ksh142
-rw-r--r--tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_007_pos.ksh104
-rw-r--r--tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_008_neg.ksh81
-rw-r--r--tests/sys/cddl/zfs/tests/zfsd/zfsd_import_001_pos.ksh147
-rw-r--r--tests/sys/cddl/zfs/tests/zfsd/zfsd_replace_001_pos.ksh79
-rw-r--r--tests/sys/cddl/zfs/tests/zfsd/zfsd_replace_002_pos.ksh73
-rw-r--r--tests/sys/cddl/zfs/tests/zfsd/zfsd_replace_003_pos.ksh122
-rw-r--r--tests/sys/cddl/zfs/tests/zfsd/zfsd_replace_004_pos.ksh63
-rw-r--r--tests/sys/cddl/zfs/tests/zfsd/zfsd_replace_005_pos.ksh69
-rwxr-xr-xtests/sys/cddl/zfs/tests/zfsd/zfsd_test.sh703
-rw-r--r--tests/sys/cddl/zfs/tests/zil/Makefile18
-rw-r--r--tests/sys/cddl/zfs/tests/zil/cleanup.ksh34
-rw-r--r--tests/sys/cddl/zfs/tests/zil/setup.ksh35
-rw-r--r--tests/sys/cddl/zfs/tests/zil/zil.cfg29
-rw-r--r--tests/sys/cddl/zfs/tests/zil/zil.kshlib53
-rw-r--r--tests/sys/cddl/zfs/tests/zil/zil_001_pos.ksh82
-rw-r--r--tests/sys/cddl/zfs/tests/zil/zil_002_pos.ksh104
-rwxr-xr-xtests/sys/cddl/zfs/tests/zil/zil_test.sh84
-rw-r--r--tests/sys/cddl/zfs/tests/zinject/Makefile20
-rw-r--r--tests/sys/cddl/zfs/tests/zinject/cleanup.ksh35
-rw-r--r--tests/sys/cddl/zfs/tests/zinject/setup.ksh47
-rw-r--r--tests/sys/cddl/zfs/tests/zinject/zinject.cfg58
-rw-r--r--tests/sys/cddl/zfs/tests/zinject/zinject.kshlib222
-rw-r--r--tests/sys/cddl/zfs/tests/zinject/zinject_001_pos.ksh142
-rw-r--r--tests/sys/cddl/zfs/tests/zinject/zinject_002_pos.ksh143
-rw-r--r--tests/sys/cddl/zfs/tests/zinject/zinject_003_pos.ksh139
-rw-r--r--tests/sys/cddl/zfs/tests/zinject/zinject_004_pos.ksh125
-rwxr-xr-xtests/sys/cddl/zfs/tests/zinject/zinject_test.sh138
-rw-r--r--tests/sys/cddl/zfs/tests/zones/Makefile21
-rw-r--r--tests/sys/cddl/zfs/tests/zones/cleanup.ksh61
-rw-r--r--tests/sys/cddl/zfs/tests/zones/setup.ksh60
-rw-r--r--tests/sys/cddl/zfs/tests/zones/zones.cfg78
-rw-r--r--tests/sys/cddl/zfs/tests/zones/zones_001_pos.ksh107
-rw-r--r--tests/sys/cddl/zfs/tests/zones/zones_002_pos.ksh76
-rw-r--r--tests/sys/cddl/zfs/tests/zones/zones_003_pos.ksh128
-rw-r--r--tests/sys/cddl/zfs/tests/zones/zones_004_pos.ksh86
-rw-r--r--tests/sys/cddl/zfs/tests/zones/zones_005_pos.ksh67
-rw-r--r--tests/sys/cddl/zfs/tests/zones/zones_common.kshlib65
-rwxr-xr-xtests/sys/cddl/zfs/tests/zones/zones_test.sh165
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/Makefile15
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol.cfg34
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_ENOSPC/Makefile16
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_ENOSPC/cleanup.ksh34
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_ENOSPC/setup.ksh38
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_ENOSPC/zvol_ENOSPC.cfg30
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_ENOSPC/zvol_ENOSPC_001_pos.ksh77
-rwxr-xr-xtests/sys/cddl/zfs/tests/zvol/zvol_ENOSPC/zvol_ENOSPC_test.sh54
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_cli/Makefile18
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_cli/cleanup.ksh34
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_cli/setup.ksh34
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_cli/zvol_cli.cfg30
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_cli/zvol_cli_001_pos.ksh70
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_cli/zvol_cli_002_pos.ksh69
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_cli/zvol_cli_003_neg.ksh68
-rwxr-xr-xtests/sys/cddl/zfs/tests/zvol/zvol_cli/zvol_cli_test.sh105
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_common.kshlib160
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_misc/Makefile24
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_misc/cleanup.ksh34
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_misc/setup.ksh34
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc.cfg31
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_001_neg.ksh75
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_002_pos.ksh84
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_003_neg.ksh88
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_004_pos.ksh124
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_005_neg.ksh89
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_006_pos.ksh87
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_007_pos.ksh128
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_008_pos.ksh154
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_009_pos.ksh121
-rwxr-xr-xtests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_test.sh263
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_swap/Makefile21
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_swap/cleanup.ksh47
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_swap/setup.ksh38
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap.cfg36
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap_001_pos.ksh88
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap_002_pos.ksh95
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap_003_pos.ksh158
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap_004_pos.ksh103
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap_005_pos.ksh104
-rw-r--r--tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap_006_pos.ksh115
-rwxr-xr-xtests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap_test.sh174
-rw-r--r--tests/sys/cddl/zfs/tests/zvol_thrash/Makefile16
-rw-r--r--tests/sys/cddl/zfs/tests/zvol_thrash/cleanup.ksh35
-rw-r--r--tests/sys/cddl/zfs/tests/zvol_thrash/setup.ksh35
-rw-r--r--tests/sys/cddl/zfs/tests/zvol_thrash/zvol_thrash.cfg32
-rw-r--r--tests/sys/cddl/zfs/tests/zvol_thrash/zvol_thrash_001_pos.ksh111
-rwxr-xr-xtests/sys/cddl/zfs/tests/zvol_thrash/zvol_thrash_test.sh55
-rw-r--r--tests/sys/common/Makefile12
-rwxr-xr-xtests/sys/common/divert.py82
-rwxr-xr-xtests/sys/common/net_receiver.py115
-rwxr-xr-xtests/sys/common/sender.py202
-rw-r--r--tests/sys/common/vnet.subr132
-rw-r--r--tests/sys/compat32/Makefile5
-rw-r--r--tests/sys/compat32/Makefile.inc3
-rw-r--r--tests/sys/compat32/aarch64/Makefile27
-rw-r--r--tests/sys/compat32/aarch64/common.sh9
-rw-r--r--tests/sys/compat32/aarch64/swp_cond_test.sh14
-rw-r--r--tests/sys/compat32/aarch64/swp_cond_test_impl.S413
-rw-r--r--tests/sys/compat32/aarch64/swp_test.sh14
-rw-r--r--tests/sys/compat32/aarch64/swp_test_impl.S216
-rw-r--r--tests/sys/devrandom/Makefile23
-rw-r--r--tests/sys/devrandom/uint128_test.c278
-rw-r--r--tests/sys/fifo/Makefile12
-rw-r--r--tests/sys/fifo/Makefile.depend16
-rw-r--r--tests/sys/fifo/fifo_create.c280
-rw-r--r--tests/sys/fifo/fifo_io.c1397
-rw-r--r--tests/sys/fifo/fifo_kqueue.c430
-rw-r--r--tests/sys/fifo/fifo_misc.c334
-rw-r--r--tests/sys/fifo/fifo_open.c474
-rw-r--r--tests/sys/file/Makefile22
-rw-r--r--tests/sys/file/Makefile.depend18
-rw-r--r--tests/sys/file/closefrom_test.c393
-rw-r--r--tests/sys/file/dup_test.c480
-rw-r--r--tests/sys/file/fcntlflags_test.c108
-rw-r--r--tests/sys/file/flock_helper.c1596
-rw-r--r--tests/sys/file/flock_test.sh52
-rw-r--r--tests/sys/file/fspacectl_test.c338
-rw-r--r--tests/sys/file/ftruncate_test.c174
-rw-r--r--tests/sys/file/newfileops_on_fork_test.c122
-rw-r--r--tests/sys/file/path_test.c1039
-rw-r--r--tests/sys/fs/Makefile29
-rw-r--r--tests/sys/fs/Makefile.depend10
-rw-r--r--tests/sys/fs/Makefile.inc1
-rw-r--r--tests/sys/fs/fusefs/Makefile100
-rw-r--r--tests/sys/fs/fusefs/access.cc301
-rw-r--r--tests/sys/fs/fusefs/allow_other.cc306
-rw-r--r--tests/sys/fs/fusefs/bad_server.cc103
-rw-r--r--tests/sys/fs/fusefs/bmap.cc266
-rw-r--r--tests/sys/fs/fusefs/cache.cc218
-rw-r--r--tests/sys/fs/fusefs/copy_file_range.cc802
-rw-r--r--tests/sys/fs/fusefs/create.cc493
-rw-r--r--tests/sys/fs/fusefs/ctl.sh69
-rw-r--r--tests/sys/fs/fusefs/default_permissions.cc1644
-rw-r--r--tests/sys/fs/fusefs/default_permissions_privileged.cc124
-rw-r--r--tests/sys/fs/fusefs/destroy.cc157
-rw-r--r--tests/sys/fs/fusefs/dev_fuse_poll.cc229
-rw-r--r--tests/sys/fs/fusefs/fallocate.cc779
-rw-r--r--tests/sys/fs/fusefs/fifo.cc211
-rw-r--r--tests/sys/fs/fusefs/flush.cc272
-rw-r--r--tests/sys/fs/fusefs/forget.cc177
-rw-r--r--tests/sys/fs/fusefs/fsync.cc285
-rw-r--r--tests/sys/fs/fusefs/fsyncdir.cc195
-rw-r--r--tests/sys/fs/fusefs/getattr.cc367
-rw-r--r--tests/sys/fs/fusefs/interrupt.cc796
-rw-r--r--tests/sys/fs/fusefs/io.cc609
-rw-r--r--tests/sys/fs/fusefs/last_local_modify.cc513
-rw-r--r--tests/sys/fs/fusefs/link.cc280
-rw-r--r--tests/sys/fs/fusefs/locks.cc730
-rw-r--r--tests/sys/fs/fusefs/lookup.cc662
-rw-r--r--tests/sys/fs/fusefs/lseek.cc518
-rw-r--r--tests/sys/fs/fusefs/mkdir.cc274
-rw-r--r--tests/sys/fs/fusefs/mknod.cc318
-rw-r--r--tests/sys/fs/fusefs/mockfs.cc1060
-rw-r--r--tests/sys/fs/fusefs/mockfs.hh441
-rw-r--r--tests/sys/fs/fusefs/mount.cc200
-rw-r--r--tests/sys/fs/fusefs/nfs.cc480
-rw-r--r--tests/sys/fs/fusefs/notify.cc546
-rw-r--r--tests/sys/fs/fusefs/open.cc307
-rw-r--r--tests/sys/fs/fusefs/opendir.cc197
-rw-r--r--tests/sys/fs/fusefs/pre-init.cc154
-rw-r--r--tests/sys/fs/fusefs/read.cc1497
-rw-r--r--tests/sys/fs/fusefs/readdir.cc516
-rw-r--r--tests/sys/fs/fusefs/readlink.cc162
-rw-r--r--tests/sys/fs/fusefs/release.cc235
-rw-r--r--tests/sys/fs/fusefs/releasedir.cc116
-rw-r--r--tests/sys/fs/fusefs/rename.cc330
-rw-r--r--tests/sys/fs/fusefs/rmdir.cc162
-rw-r--r--tests/sys/fs/fusefs/setattr.cc862
-rw-r--r--tests/sys/fs/fusefs/statfs.cc171
-rw-r--r--tests/sys/fs/fusefs/symlink.cc208
-rw-r--r--tests/sys/fs/fusefs/unlink.cc237
-rw-r--r--tests/sys/fs/fusefs/utils.cc673
-rw-r--r--tests/sys/fs/fusefs/utils.hh277
-rw-r--r--tests/sys/fs/fusefs/write.cc1589
-rw-r--r--tests/sys/fs/fusefs/xattr.cc891
-rw-r--r--tests/sys/fs/tarfs/Makefile10
-rw-r--r--tests/sys/fs/tarfs/mktar.c273
-rw-r--r--tests/sys/fs/tarfs/tarfs_test.sh417
-rw-r--r--tests/sys/fs/tarfs/tarsum.c128
-rw-r--r--tests/sys/fs/tmpfs/Makefile62
-rw-r--r--tests/sys/fs/tmpfs/Makefile.depend16
-rw-r--r--tests/sys/geom/Makefile5
-rw-r--r--tests/sys/geom/Makefile.depend10
-rw-r--r--tests/sys/geom/class/Makefile25
-rw-r--r--tests/sys/geom/class/Makefile.depend10
-rw-r--r--tests/sys/geom/class/Makefile.inc1
-rw-r--r--tests/sys/geom/class/concat/1_test.sh22
-rw-r--r--tests/sys/geom/class/concat/2_test.sh29
-rw-r--r--tests/sys/geom/class/concat/Makefile16
-rw-r--r--tests/sys/geom/class/concat/Makefile.depend10
-rw-r--r--tests/sys/geom/class/concat/append1.sh46
-rw-r--r--tests/sys/geom/class/concat/append2.sh77
-rw-r--r--tests/sys/geom/class/concat/conf.sh14
-rw-r--r--tests/sys/geom/class/eli/Makefile44
-rw-r--r--tests/sys/geom/class/eli/Makefile.depend18
-rw-r--r--tests/sys/geom/class/eli/attach_test.sh158
-rw-r--r--tests/sys/geom/class/eli/conf.sh94
-rw-r--r--tests/sys/geom/class/eli/configure_test.sh59
-rw-r--r--tests/sys/geom/class/eli/delkey_test.sh113
-rw-r--r--tests/sys/geom/class/eli/detach_test.sh46
-rw-r--r--tests/sys/geom/class/eli/gentestvect.py62
-rw-r--r--tests/sys/geom/class/eli/hmac_test.c40
-rw-r--r--tests/sys/geom/class/eli/init_test.sh379
-rw-r--r--tests/sys/geom/class/eli/integrity_test.sh163
-rw-r--r--tests/sys/geom/class/eli/kill_test.sh101
-rw-r--r--tests/sys/geom/class/eli/misc_test.sh206
-rw-r--r--tests/sys/geom/class/eli/onetime_test.sh182
-rwxr-xr-xtests/sys/geom/class/eli/online_resize_test.sh191
-rwxr-xr-xtests/sys/geom/class/eli/reentrancy_test.sh68
-rw-r--r--tests/sys/geom/class/eli/resize_test.sh88
-rw-r--r--tests/sys/geom/class/eli/setkey_test.sh221
-rw-r--r--tests/sys/geom/class/eli/testvect.h1136
-rw-r--r--tests/sys/geom/class/eli/unaligned_io.c131
-rw-r--r--tests/sys/geom/class/gate/Makefile10
-rw-r--r--tests/sys/geom/class/gate/Makefile.depend10
-rw-r--r--tests/sys/geom/class/gate/ggate_test.sh267
-rw-r--r--tests/sys/geom/class/geom_subr.sh83
-rw-r--r--tests/sys/geom/class/mirror/10_test.sh68
-rw-r--r--tests/sys/geom/class/mirror/11_test.sh83
-rw-r--r--tests/sys/geom/class/mirror/12_test.sh67
-rw-r--r--tests/sys/geom/class/mirror/13_test.sh80
-rw-r--r--tests/sys/geom/class/mirror/1_test.sh22
-rw-r--r--tests/sys/geom/class/mirror/2_test.sh51
-rw-r--r--tests/sys/geom/class/mirror/3_test.sh63
-rw-r--r--tests/sys/geom/class/mirror/4_test.sh65
-rw-r--r--tests/sys/geom/class/mirror/5_test.sh63
-rw-r--r--tests/sys/geom/class/mirror/6_test.sh44
-rw-r--r--tests/sys/geom/class/mirror/7_test.sh63
-rw-r--r--tests/sys/geom/class/mirror/8_test.sh55
-rw-r--r--tests/sys/geom/class/mirror/9_test.sh57
-rw-r--r--tests/sys/geom/class/mirror/Makefile31
-rw-r--r--tests/sys/geom/class/mirror/Makefile.depend10
-rwxr-xr-xtests/sys/geom/class/mirror/component_selection.sh139
-rw-r--r--tests/sys/geom/class/mirror/conf.sh57
-rw-r--r--tests/sys/geom/class/mirror/sync_error.sh108
-rw-r--r--tests/sys/geom/class/multipath/Makefile11
-rwxr-xr-xtests/sys/geom/class/multipath/conf.sh106
-rwxr-xr-xtests/sys/geom/class/multipath/failloop.sh79
-rwxr-xr-xtests/sys/geom/class/multipath/misc.sh362
-rw-r--r--tests/sys/geom/class/nop/Makefile11
-rw-r--r--tests/sys/geom/class/nop/Makefile.depend10
-rw-r--r--tests/sys/geom/class/nop/nop_test.sh280
-rw-r--r--tests/sys/geom/class/part/Makefile8
-rw-r--r--tests/sys/geom/class/part/misc.sh187
-rw-r--r--tests/sys/geom/class/raid3/10_test.sh31
-rw-r--r--tests/sys/geom/class/raid3/11_test.sh31
-rw-r--r--tests/sys/geom/class/raid3/12_test.sh37
-rw-r--r--tests/sys/geom/class/raid3/1_test.sh27
-rw-r--r--tests/sys/geom/class/raid3/2_test.sh31
-rw-r--r--tests/sys/geom/class/raid3/3_test.sh35
-rw-r--r--tests/sys/geom/class/raid3/4_test.sh35
-rw-r--r--tests/sys/geom/class/raid3/5_test.sh35
-rw-r--r--tests/sys/geom/class/raid3/6_test.sh39
-rw-r--r--tests/sys/geom/class/raid3/7_test.sh42
-rw-r--r--tests/sys/geom/class/raid3/8_test.sh38
-rw-r--r--tests/sys/geom/class/raid3/9_test.sh41
-rw-r--r--tests/sys/geom/class/raid3/Makefile24
-rw-r--r--tests/sys/geom/class/raid3/Makefile.depend10
-rw-r--r--tests/sys/geom/class/raid3/conf.sh14
-rw-r--r--tests/sys/geom/class/shsec/1_test.sh27
-rw-r--r--tests/sys/geom/class/shsec/2_test.sh51
-rw-r--r--tests/sys/geom/class/shsec/Makefile14
-rw-r--r--tests/sys/geom/class/shsec/Makefile.depend10
-rw-r--r--tests/sys/geom/class/shsec/conf.sh14
-rw-r--r--tests/sys/geom/class/stripe/1_test.sh22
-rw-r--r--tests/sys/geom/class/stripe/2_test.sh29
-rw-r--r--tests/sys/geom/class/stripe/Makefile14
-rw-r--r--tests/sys/geom/class/stripe/Makefile.depend10
-rw-r--r--tests/sys/geom/class/stripe/conf.sh14
-rw-r--r--tests/sys/geom/class/union/Makefile9
-rw-r--r--tests/sys/geom/class/union/conf.sh40
-rw-r--r--tests/sys/geom/class/union/union_test.sh338
-rw-r--r--tests/sys/geom/class/uzip/1_endian_big.img.uzip.uue86
-rw-r--r--tests/sys/geom/class/uzip/1_endian_little.img.uzip.uue109
-rw-r--r--tests/sys/geom/class/uzip/1_test.sh33
-rw-r--r--tests/sys/geom/class/uzip/Makefile41
-rw-r--r--tests/sys/geom/class/uzip/Makefile.depend10
-rw-r--r--tests/sys/geom/class/uzip/conf.sh19
-rw-r--r--tests/sys/geom/class/uzip/etalon/etalon.txt42
-rw-r--r--tests/sys/geom/class/virstor/Makefile9
-rw-r--r--tests/sys/geom/class/virstor/conf.sh31
-rw-r--r--tests/sys/geom/class/virstor/virstor_test.sh73
-rw-r--r--tests/sys/kern/Makefile147
-rw-r--r--tests/sys/kern/Makefile.depend20
-rw-r--r--tests/sys/kern/Makefile.inc1
-rw-r--r--tests/sys/kern/acct/Makefile18
-rw-r--r--tests/sys/kern/acct/Makefile.depend18
-rw-r--r--tests/sys/kern/acct/acct_test.c237
-rw-r--r--tests/sys/kern/basic_signal.c225
-rw-r--r--tests/sys/kern/coredump_phnum_helper.c64
-rw-r--r--tests/sys/kern/coredump_phnum_test.sh114
-rw-r--r--tests/sys/kern/execve/Makefile38
-rw-r--r--tests/sys/kern/execve/Makefile.depend15
-rw-r--r--tests/sys/kern/execve/bad_interp_len3
-rw-r--r--tests/sys/kern/execve/dev_null_script3
-rw-r--r--tests/sys/kern/execve/execve_argc_helper.c15
-rw-r--r--tests/sys/kern/execve/execve_helper.c59
-rw-r--r--tests/sys/kern/execve/execve_test.sh133
-rw-r--r--tests/sys/kern/execve/good_aout.c43
-rw-r--r--tests/sys/kern/execve/good_script3
-rw-r--r--tests/sys/kern/execve/non_exist_shell3
-rw-r--r--tests/sys/kern/execve/script_arg3
-rw-r--r--tests/sys/kern/execve/script_arg_nospace3
-rw-r--r--tests/sys/kern/exterr_test.c108
-rw-r--r--tests/sys/kern/fdgrowtable_test.c286
-rw-r--r--tests/sys/kern/getdirentries_test.c172
-rw-r--r--tests/sys/kern/inotify_test.c864
-rw-r--r--tests/sys/kern/jail_lookup_root.c133
-rw-r--r--tests/sys/kern/jailmeta.sh588
-rw-r--r--tests/sys/kern/kcov.c477
-rw-r--r--tests/sys/kern/kern_copyin.c155
-rw-r--r--tests/sys/kern/kern_descrip_test.c209
-rw-r--r--tests/sys/kern/kill_zombie.c63
-rw-r--r--tests/sys/kern/ktls_test.c2848
-rw-r--r--tests/sys/kern/ktrace_test.c620
-rw-r--r--tests/sys/kern/libkern_crc32.c155
-rw-r--r--tests/sys/kern/listener_wakeup.c293
-rw-r--r--tests/sys/kern/logsigexit_test.sh37
-rw-r--r--tests/sys/kern/module_test.c197
-rw-r--r--tests/sys/kern/pdeathsig.c345
-rw-r--r--tests/sys/kern/pdeathsig_helper.c48
-rw-r--r--tests/sys/kern/pipe/Makefile13
-rw-r--r--tests/sys/kern/pipe/Makefile.depend16
-rw-r--r--tests/sys/kern/pipe/big_pipe_test.c86
-rw-r--r--tests/sys/kern/pipe/pipe_fstat_bug_test.c155
-rw-r--r--tests/sys/kern/pipe/pipe_ino_test.c66
-rw-r--r--tests/sys/kern/pipe/pipe_kqueue_test.c364
-rw-r--r--tests/sys/kern/pipe/pipe_overcommit1_test.c50
-rw-r--r--tests/sys/kern/pipe/pipe_overcommit2_test.c82
-rw-r--r--tests/sys/kern/pipe/pipe_reverse2_test.c65
-rw-r--r--tests/sys/kern/pipe/pipe_reverse_test.c148
-rw-r--r--tests/sys/kern/pipe/pipe_wraparound_test.c139
-rw-r--r--tests/sys/kern/prace.c144
-rw-r--r--tests/sys/kern/ptrace_test.c4665
-rw-r--r--tests/sys/kern/reaper.c792
-rw-r--r--tests/sys/kern/sched_affinity.c275
-rw-r--r--tests/sys/kern/sendfile_helper.c177
-rwxr-xr-xtests/sys/kern/sendfile_test.sh192
-rw-r--r--tests/sys/kern/shutdown_dgram.c111
-rw-r--r--tests/sys/kern/sigaltstack.c115
-rw-r--r--tests/sys/kern/sigsys.c142
-rw-r--r--tests/sys/kern/sigwait.c583
-rw-r--r--tests/sys/kern/socket_accept.c127
-rw-r--r--tests/sys/kern/socket_accf.c251
-rw-r--r--tests/sys/kern/socket_msg_trunc.c218
-rw-r--r--tests/sys/kern/socket_msg_waitall.c182
-rw-r--r--tests/sys/kern/socket_splice.c979
-rw-r--r--tests/sys/kern/sonewconn_overflow.py157
-rwxr-xr-xtests/sys/kern/sonewconn_overflow.sh42
-rw-r--r--tests/sys/kern/subr_physmem_test.c137
-rw-r--r--tests/sys/kern/sys_getrandom.c127
-rw-r--r--tests/sys/kern/sysctl_kern_proc.c199
-rw-r--r--tests/sys/kern/sysctl_security_jail_children.sh77
-rw-r--r--tests/sys/kern/tty/Makefile15
-rw-r--r--tests/sys/kern/tty/fionread.c21
-rw-r--r--tests/sys/kern/tty/readsz.c130
-rw-r--r--tests/sys/kern/tty/test_canon.orch102
-rw-r--r--tests/sys/kern/tty/test_canon_fullbuf.orch23
-rw-r--r--tests/sys/kern/tty/test_ncanon.orch39
-rw-r--r--tests/sys/kern/tty/test_recanon.orch90
-rw-r--r--tests/sys/kern/tty/test_sti.c337
-rw-r--r--tests/sys/kern/tty_pts.c64
-rw-r--r--tests/sys/kern/unix_dgram.c441
-rw-r--r--tests/sys/kern/unix_passfd_dgram.c2
-rw-r--r--tests/sys/kern/unix_passfd_stream.c2
-rw-r--r--tests/sys/kern/unix_passfd_test.c1216
-rw-r--r--tests/sys/kern/unix_seqpacket_test.c1375
-rw-r--r--tests/sys/kern/unix_stream.c487
-rw-r--r--tests/sys/kern/waitpid_nohang.c68
-rw-r--r--tests/sys/kqueue/Makefile21
-rw-r--r--tests/sys/kqueue/Makefile.depend18
-rw-r--r--tests/sys/kqueue/Makefile.inc1
-rw-r--r--tests/sys/kqueue/kqueue_fork.c89
-rw-r--r--tests/sys/kqueue/kqueue_peek_signal.c106
-rw-r--r--tests/sys/kqueue/libkqueue/Makefile16
-rw-r--r--tests/sys/kqueue/libkqueue/Makefile.depend16
-rw-r--r--tests/sys/kqueue/libkqueue/common.h89
-rw-r--r--tests/sys/kqueue/libkqueue/config.h13
-rw-r--r--tests/sys/kqueue/libkqueue/main.c368
-rw-r--r--tests/sys/kqueue/libkqueue/proc.c423
-rw-r--r--tests/sys/kqueue/libkqueue/read.c323
-rw-r--r--tests/sys/kqueue/libkqueue/signal.c196
-rw-r--r--tests/sys/kqueue/libkqueue/timer.c764
-rw-r--r--tests/sys/kqueue/libkqueue/user.c186
-rw-r--r--tests/sys/kqueue/libkqueue/vnode.c322
-rw-r--r--tests/sys/mac/Makefile7
-rw-r--r--tests/sys/mac/Makefile.depend10
-rw-r--r--tests/sys/mac/Makefile.inc1
-rw-r--r--tests/sys/mac/bsdextended/Makefile14
-rw-r--r--tests/sys/mac/bsdextended/Makefile.depend17
-rw-r--r--tests/sys/mac/bsdextended/matches_test.sh396
-rw-r--r--tests/sys/mac/bsdextended/ugidfw_test.c251
-rw-r--r--tests/sys/mac/ipacl/Makefile9
-rw-r--r--tests/sys/mac/ipacl/ipacl_test.sh281
-rw-r--r--tests/sys/mac/ipacl/utils.subr17
-rw-r--r--tests/sys/mac/portacl/LICENSE26
-rw-r--r--tests/sys/mac/portacl/Makefile16
-rw-r--r--tests/sys/mac/portacl/Makefile.depend10
-rw-r--r--tests/sys/mac/portacl/misc.sh106
-rw-r--r--tests/sys/mac/portacl/nobody_test.sh66
-rw-r--r--tests/sys/mac/portacl/root_test.sh50
-rw-r--r--tests/sys/mqueue/Makefile17
-rw-r--r--tests/sys/mqueue/Makefile.depend19
-rw-r--r--tests/sys/mqueue/mqtest1.c56
-rw-r--r--tests/sys/mqueue/mqtest2.c100
-rw-r--r--tests/sys/mqueue/mqtest3.c117
-rw-r--r--tests/sys/mqueue/mqtest4.c119
-rw-r--r--tests/sys/mqueue/mqtest5.c127
-rw-r--r--tests/sys/mqueue/mqueue_test.sh80
-rw-r--r--tests/sys/net/Makefile47
-rw-r--r--tests/sys/net/bpf/Makefile15
-rw-r--r--tests/sys/net/bpf/bpf.sh67
-rw-r--r--tests/sys/net/bpf/bpf_multi_read.c76
-rw-r--r--tests/sys/net/dhclient_pcp.conf1
-rwxr-xr-xtests/sys/net/if_bridge_test.sh1274
-rwxr-xr-xtests/sys/net/if_clone_test.sh574
-rw-r--r--tests/sys/net/if_epair.c77
-rw-r--r--tests/sys/net/if_epair_test.sh65
-rw-r--r--tests/sys/net/if_gif.sh76
-rwxr-xr-xtests/sys/net/if_lagg_test.sh463
-rw-r--r--tests/sys/net/if_ovpn/Makefile30
-rw-r--r--tests/sys/net/if_ovpn/ca.crt33
-rw-r--r--tests/sys/net/if_ovpn/ca.key51
-rw-r--r--tests/sys/net/if_ovpn/ccd/Makefile8
-rw-r--r--tests/sys/net/if_ovpn/ccd/Test-Client22
-rw-r--r--tests/sys/net/if_ovpn/client.crt123
-rw-r--r--tests/sys/net/if_ovpn/client.key51
-rw-r--r--tests/sys/net/if_ovpn/client2.crt32
-rw-r--r--tests/sys/net/if_ovpn/client2.key51
-rw-r--r--tests/sys/net/if_ovpn/dh.pem8
-rw-r--r--tests/sys/net/if_ovpn/if_ovpn.sh1426
-rw-r--r--tests/sys/net/if_ovpn/if_ovpn_c.c135
-rw-r--r--tests/sys/net/if_ovpn/server.crt123
-rw-r--r--tests/sys/net/if_ovpn/server.key51
-rw-r--r--tests/sys/net/if_ovpn/user.pass2
-rw-r--r--tests/sys/net/if_ovpn/utils.subr73
-rw-r--r--tests/sys/net/if_stf.sh203
-rwxr-xr-xtests/sys/net/if_tun_test.sh63
-rwxr-xr-xtests/sys/net/if_vlan.sh373
-rw-r--r--tests/sys/net/if_wg.sh633
-rw-r--r--tests/sys/net/pcp.py74
-rw-r--r--tests/sys/net/randsleep.c61
-rw-r--r--tests/sys/net/routing/Makefile21
-rwxr-xr-xtests/sys/net/routing/generic_cleanup.sh35
-rw-r--r--tests/sys/net/routing/params.h36
-rw-r--r--tests/sys/net/routing/rtsock_common.h882
-rw-r--r--tests/sys/net/routing/rtsock_config.h175
-rw-r--r--tests/sys/net/routing/rtsock_print.h412
-rwxr-xr-xtests/sys/net/routing/test_routing_l3.py120
-rw-r--r--tests/sys/net/routing/test_rtsock_l3.c1421
-rw-r--r--tests/sys/net/routing/test_rtsock_lladdr.c416
-rwxr-xr-xtests/sys/net/routing/test_rtsock_multipath.py271
-rw-r--r--tests/sys/net/routing/test_rtsock_ops.c53
-rw-r--r--tests/sys/net/stp.py114
-rw-r--r--tests/sys/netgraph/Makefile28
-rw-r--r--tests/sys/netgraph/basic.c191
-rw-r--r--tests/sys/netgraph/bridge.c632
-rw-r--r--tests/sys/netgraph/hub.c231
-rw-r--r--tests/sys/netgraph/ksocket.c180
-rwxr-xr-xtests/sys/netgraph/ng_macfilter_test.sh443
-rw-r--r--tests/sys/netgraph/socket.c63
-rw-r--r--tests/sys/netgraph/util.c277
-rw-r--r--tests/sys/netgraph/util.h114
-rw-r--r--tests/sys/netgraph/vlan_rotate.c335
-rw-r--r--tests/sys/netinet/Makefile59
-rw-r--r--tests/sys/netinet/Makefile.depend17
-rwxr-xr-xtests/sys/netinet/arp.sh273
-rw-r--r--tests/sys/netinet/broadcast.c196
-rw-r--r--tests/sys/netinet/carp.py69
-rwxr-xr-xtests/sys/netinet/carp.sh590
-rwxr-xr-xtests/sys/netinet/divert.sh158
-rwxr-xr-xtests/sys/netinet/fibs.sh72
-rw-r--r--tests/sys/netinet/fibs_multibind_test.c755
-rw-r--r--tests/sys/netinet/fibs_test.sh830
-rwxr-xr-xtests/sys/netinet/forward.sh325
-rw-r--r--tests/sys/netinet/igmp.py157
-rw-r--r--tests/sys/netinet/ip6_v4mapped_test.c398
-rw-r--r--tests/sys/netinet/ip_reass_test.c384
-rw-r--r--tests/sys/netinet/libalias/1_instance.c119
-rw-r--r--tests/sys/netinet/libalias/2_natout.c547
-rw-r--r--tests/sys/netinet/libalias/3_natin.c381
-rw-r--r--tests/sys/netinet/libalias/Makefile31
-rw-r--r--tests/sys/netinet/libalias/perf.c308
-rw-r--r--tests/sys/netinet/libalias/util.c123
-rw-r--r--tests/sys/netinet/libalias/util.h137
-rwxr-xr-xtests/sys/netinet/lpm.sh178
-rw-r--r--tests/sys/netinet/multicast.sh61
-rwxr-xr-xtests/sys/netinet/output.sh593
-rwxr-xr-xtests/sys/netinet/redirect.py86
-rwxr-xr-xtests/sys/netinet/redirect.sh110
-rw-r--r--tests/sys/netinet/sendto-IP_MULTICAST_IF.c63
-rw-r--r--tests/sys/netinet/so_reuseport_lb_test.c566
-rw-r--r--tests/sys/netinet/socket_afinet.c599
-rw-r--r--tests/sys/netinet/tcp_connect_port_test.c331
-rw-r--r--tests/sys/netinet/tcp_implied_connect.c80
-rw-r--r--tests/sys/netinet/tcp_md5_getsockopt.c135
-rw-r--r--tests/sys/netinet/tcp_user_cookie.c110
-rw-r--r--tests/sys/netinet/udp_bindings.c249
-rw-r--r--tests/sys/netinet/udp_dontroute.c136
-rw-r--r--tests/sys/netinet/udp_io.c140
-rw-r--r--tests/sys/netinet6/Makefile54
-rwxr-xr-xtests/sys/netinet6/addr6.sh70
-rwxr-xr-xtests/sys/netinet6/divert.sh101
-rw-r--r--tests/sys/netinet6/exthdr.py274
-rwxr-xr-xtests/sys/netinet6/exthdr.sh125
-rwxr-xr-xtests/sys/netinet6/fibs6.sh91
-rwxr-xr-xtests/sys/netinet6/forward6.sh535
-rw-r--r--tests/sys/netinet6/frag6/Makefile79
-rw-r--r--tests/sys/netinet6/frag6/frag6.subr125
-rw-r--r--tests/sys/netinet6/frag6/frag6_01.py116
-rwxr-xr-xtests/sys/netinet6/frag6/frag6_01.sh232
-rw-r--r--tests/sys/netinet6/frag6/frag6_02.py110
-rwxr-xr-xtests/sys/netinet6/frag6/frag6_02.sh229
-rw-r--r--tests/sys/netinet6/frag6/frag6_03.py135
-rwxr-xr-xtests/sys/netinet6/frag6/frag6_03.sh230
-rw-r--r--tests/sys/netinet6/frag6/frag6_04.py107
-rwxr-xr-xtests/sys/netinet6/frag6/frag6_04.sh230
-rw-r--r--tests/sys/netinet6/frag6/frag6_05.py85
-rwxr-xr-xtests/sys/netinet6/frag6/frag6_05.sh474
-rw-r--r--tests/sys/netinet6/frag6/frag6_06.py82
-rwxr-xr-xtests/sys/netinet6/frag6/frag6_06.sh262
-rw-r--r--tests/sys/netinet6/frag6/frag6_07.py179
-rwxr-xr-xtests/sys/netinet6/frag6/frag6_07.sh234
-rw-r--r--tests/sys/netinet6/frag6/frag6_08.py153
-rwxr-xr-xtests/sys/netinet6/frag6/frag6_08.sh231
-rw-r--r--tests/sys/netinet6/frag6/frag6_09.py110
-rwxr-xr-xtests/sys/netinet6/frag6/frag6_09.sh228
-rw-r--r--tests/sys/netinet6/frag6/frag6_10.py82
-rwxr-xr-xtests/sys/netinet6/frag6/frag6_10.sh230
-rw-r--r--tests/sys/netinet6/frag6/frag6_11.py82
-rwxr-xr-xtests/sys/netinet6/frag6/frag6_11.sh229
-rw-r--r--tests/sys/netinet6/frag6/frag6_12.py112
-rwxr-xr-xtests/sys/netinet6/frag6/frag6_12.sh227
-rw-r--r--tests/sys/netinet6/frag6/frag6_13.py123
-rwxr-xr-xtests/sys/netinet6/frag6/frag6_13.sh227
-rw-r--r--tests/sys/netinet6/frag6/frag6_14.py138
-rwxr-xr-xtests/sys/netinet6/frag6/frag6_14.sh227
-rw-r--r--tests/sys/netinet6/frag6/frag6_15.py106
-rwxr-xr-xtests/sys/netinet6/frag6/frag6_15.sh256
-rw-r--r--tests/sys/netinet6/frag6/frag6_16.py133
-rwxr-xr-xtests/sys/netinet6/frag6/frag6_16.sh232
-rw-r--r--tests/sys/netinet6/frag6/frag6_17.py91
-rwxr-xr-xtests/sys/netinet6/frag6/frag6_17.sh46
-rw-r--r--tests/sys/netinet6/frag6/frag6_18.py91
-rwxr-xr-xtests/sys/netinet6/frag6/frag6_18.sh63
-rw-r--r--tests/sys/netinet6/frag6/frag6_19.py84
-rwxr-xr-xtests/sys/netinet6/frag6/frag6_19.sh223
-rwxr-xr-xtests/sys/netinet6/frag6/frag6_20.py138
-rwxr-xr-xtests/sys/netinet6/frag6/frag6_20.sh230
-rw-r--r--tests/sys/netinet6/frag6/sniffer.py41
-rwxr-xr-xtests/sys/netinet6/lpm6.sh198
-rw-r--r--tests/sys/netinet6/mld.py77
-rwxr-xr-xtests/sys/netinet6/mld.sh128
-rwxr-xr-xtests/sys/netinet6/ndp.sh196
-rwxr-xr-xtests/sys/netinet6/output6.sh663
-rwxr-xr-xtests/sys/netinet6/proxy_ndp.sh221
-rw-r--r--tests/sys/netinet6/ra.py38
-rw-r--r--tests/sys/netinet6/redirect.py85
-rw-r--r--tests/sys/netinet6/redirect.sh121
-rw-r--r--tests/sys/netinet6/scapyi386.py86
-rwxr-xr-xtests/sys/netinet6/scapyi386.sh88
-rw-r--r--tests/sys/netinet6/test_ip6_output.py540
-rw-r--r--tests/sys/netipsec/Makefile5
-rw-r--r--tests/sys/netipsec/tunnel/Makefile21
-rwxr-xr-xtests/sys/netipsec/tunnel/aes_cbc_128_hmac_sha1.sh48
-rwxr-xr-xtests/sys/netipsec/tunnel/aes_cbc_256_hmac_sha2_256.sh48
-rwxr-xr-xtests/sys/netipsec/tunnel/aes_gcm_128.sh48
-rwxr-xr-xtests/sys/netipsec/tunnel/aes_gcm_256.sh48
-rwxr-xr-xtests/sys/netipsec/tunnel/aesni_aes_cbc_128_hmac_sha1.sh48
-rwxr-xr-xtests/sys/netipsec/tunnel/aesni_aes_cbc_256_hmac_sha2_256.sh48
-rwxr-xr-xtests/sys/netipsec/tunnel/aesni_aes_gcm_128.sh48
-rwxr-xr-xtests/sys/netipsec/tunnel/aesni_aes_gcm_256.sh48
-rwxr-xr-xtests/sys/netipsec/tunnel/chacha20_poly1305.sh42
-rwxr-xr-xtests/sys/netipsec/tunnel/empty.sh43
-rw-r--r--tests/sys/netipsec/tunnel/utils.subr132
-rw-r--r--tests/sys/netlink/Makefile17
-rw-r--r--tests/sys/netlink/netlink_socket.c341
-rw-r--r--tests/sys/netlink/test_netlink_message_writer.py39
-rw-r--r--tests/sys/netlink/test_nl_core.py33
-rw-r--r--tests/sys/netlink/test_rtnl_iface.py355
-rw-r--r--tests/sys/netlink/test_rtnl_ifaddr.py762
-rw-r--r--tests/sys/netlink/test_rtnl_neigh.py53
-rw-r--r--tests/sys/netlink/test_rtnl_route.py142
-rw-r--r--tests/sys/netlink/test_snl.c251
-rw-r--r--tests/sys/netlink/test_snl_generic.c120
-rw-r--r--tests/sys/netmap/Makefile13
-rw-r--r--tests/sys/netmap/ctrl-api-test.c2335
-rw-r--r--tests/sys/netpfil/Makefile14
-rw-r--r--tests/sys/netpfil/Makefile.depend10
-rw-r--r--tests/sys/netpfil/common/Makefile36
-rw-r--r--tests/sys/netpfil/common/divapp.c146
-rw-r--r--tests/sys/netpfil/common/dummynet.sh641
-rw-r--r--tests/sys/netpfil/common/forward.sh100
-rw-r--r--tests/sys/netpfil/common/fragments.sh79
-rw-r--r--tests/sys/netpfil/common/nat.sh252
-rw-r--r--tests/sys/netpfil/common/pass_block.sh128
-rw-r--r--tests/sys/netpfil/common/pft_icmp_check.py112
-rw-r--r--tests/sys/netpfil/common/pft_ping.py758
-rw-r--r--tests/sys/netpfil/common/pft_rst.py39
-rw-r--r--tests/sys/netpfil/common/pft_synflood.py68
-rw-r--r--tests/sys/netpfil/common/rdr.sh137
-rw-r--r--tests/sys/netpfil/common/runner.subr66
-rw-r--r--tests/sys/netpfil/common/sniffer.py75
-rw-r--r--tests/sys/netpfil/common/tos.sh117
-rw-r--r--tests/sys/netpfil/common/utils.subr143
-rw-r--r--tests/sys/netpfil/ipfw/Makefile14
-rw-r--r--tests/sys/netpfil/ipfw/divert.sh281
-rwxr-xr-xtests/sys/netpfil/ipfw/fwd.sh70
-rw-r--r--tests/sys/netpfil/ipfw/fwd_inetd.conf6
-rw-r--r--tests/sys/netpfil/pf/CVE-2019-5597.py63
-rw-r--r--tests/sys/netpfil/pf/CVE-2019-5598.py92
-rw-r--r--tests/sys/netpfil/pf/Makefile105
-rw-r--r--tests/sys/netpfil/pf/Makefile.depend11
-rw-r--r--tests/sys/netpfil/pf/altq.sh346
-rw-r--r--tests/sys/netpfil/pf/anchor.sh510
-rw-r--r--tests/sys/netpfil/pf/bsnmpd.conf47
-rw-r--r--tests/sys/netpfil/pf/daytime_inetd.conf30
-rw-r--r--tests/sys/netpfil/pf/debug.sh106
-rw-r--r--tests/sys/netpfil/pf/divert-to.sh386
-rw-r--r--tests/sys/netpfil/pf/dup.sh80
-rw-r--r--tests/sys/netpfil/pf/echo_inetd.conf30
-rw-r--r--tests/sys/netpfil/pf/ether.sh746
-rw-r--r--tests/sys/netpfil/pf/forward.sh170
-rw-r--r--tests/sys/netpfil/pf/frag-adjhole.py58
-rw-r--r--tests/sys/netpfil/pf/frag-overhole.py83
-rw-r--r--tests/sys/netpfil/pf/frag-overindex.py82
-rw-r--r--tests/sys/netpfil/pf/frag-overlimit.py87
-rw-r--r--tests/sys/netpfil/pf/frag-overreplace.py84
-rw-r--r--tests/sys/netpfil/pf/frag6.py276
-rw-r--r--tests/sys/netpfil/pf/fragcommon.py54
-rw-r--r--tests/sys/netpfil/pf/fragmentation_compat.sh338
-rw-r--r--tests/sys/netpfil/pf/fragmentation_no_reassembly.sh130
-rw-r--r--tests/sys/netpfil/pf/fragmentation_pass.sh668
-rw-r--r--tests/sys/netpfil/pf/get_state.sh80
-rw-r--r--tests/sys/netpfil/pf/header.py216
-rw-r--r--tests/sys/netpfil/pf/icmp.py269
-rw-r--r--tests/sys/netpfil/pf/icmp.sh144
-rw-r--r--tests/sys/netpfil/pf/icmp6.sh204
-rw-r--r--tests/sys/netpfil/pf/if_enc.sh178
-rw-r--r--tests/sys/netpfil/pf/igmp.py95
-rw-r--r--tests/sys/netpfil/pf/ioctl/Makefile8
-rw-r--r--tests/sys/netpfil/pf/ioctl/validation.c958
-rw-r--r--tests/sys/netpfil/pf/killstate.sh710
-rw-r--r--tests/sys/netpfil/pf/limits.sh119
-rw-r--r--tests/sys/netpfil/pf/loginterface.sh87
-rw-r--r--tests/sys/netpfil/pf/macro.sh40
-rw-r--r--tests/sys/netpfil/pf/match.sh185
-rw-r--r--tests/sys/netpfil/pf/max_pkt_rate.sh121
-rw-r--r--tests/sys/netpfil/pf/max_pkt_size.sh122
-rwxr-xr-xtests/sys/netpfil/pf/max_states.sh62
-rw-r--r--tests/sys/netpfil/pf/mbuf.sh242
-rw-r--r--tests/sys/netpfil/pf/mld.py95
-rw-r--r--tests/sys/netpfil/pf/modulate.sh79
-rw-r--r--tests/sys/netpfil/pf/names.sh102
-rw-r--r--tests/sys/netpfil/pf/nat.sh830
-rw-r--r--tests/sys/netpfil/pf/nat64.py328
-rw-r--r--tests/sys/netpfil/pf/nat64.sh1056
-rw-r--r--tests/sys/netpfil/pf/nat66.py186
-rw-r--r--tests/sys/netpfil/pf/pass_block.sh465
-rw-r--r--tests/sys/netpfil/pf/pflog.sh406
-rw-r--r--tests/sys/netpfil/pf/pflow.sh349
-rw-r--r--tests/sys/netpfil/pf/pfsync.sh1227
-rw-r--r--tests/sys/netpfil/pf/pfsync_defer.py130
-rw-r--r--tests/sys/netpfil/pf/pft_ether.py72
-rw-r--r--tests/sys/netpfil/pf/pft_read_ipfix.py109
-rw-r--r--tests/sys/netpfil/pf/prio.sh72
-rw-r--r--tests/sys/netpfil/pf/proxy.sh94
-rw-r--r--tests/sys/netpfil/pf/rdr-srcport.py20
-rw-r--r--tests/sys/netpfil/pf/rdr.sh290
-rw-r--r--tests/sys/netpfil/pf/return.py153
-rw-r--r--tests/sys/netpfil/pf/ridentifier.sh105
-rw-r--r--tests/sys/netpfil/pf/route_to.sh997
-rw-r--r--tests/sys/netpfil/pf/rtable.sh130
-rw-r--r--tests/sys/netpfil/pf/rules_counter.sh209
-rw-r--r--tests/sys/netpfil/pf/scrub.sh220
-rw-r--r--tests/sys/netpfil/pf/scrub_compat.sh221
-rw-r--r--tests/sys/netpfil/pf/scrub_pass.sh173
-rw-r--r--tests/sys/netpfil/pf/sctp.py713
-rw-r--r--tests/sys/netpfil/pf/sctp.sh846
-rw-r--r--tests/sys/netpfil/pf/set_skip.sh231
-rw-r--r--tests/sys/netpfil/pf/set_tos.sh217
-rw-r--r--tests/sys/netpfil/pf/snmp.sh123
-rwxr-xr-xtests/sys/netpfil/pf/src_track.sh515
-rw-r--r--tests/sys/netpfil/pf/status.sh73
-rw-r--r--tests/sys/netpfil/pf/syncookie.sh577
-rw-r--r--tests/sys/netpfil/pf/synproxy.sh164
-rw-r--r--tests/sys/netpfil/pf/table.sh628
-rw-r--r--tests/sys/netpfil/pf/tcp.py158
-rw-r--r--tests/sys/netpfil/pf/tcp.sh110
-rw-r--r--tests/sys/netpfil/pf/tos.sh94
-rw-r--r--tests/sys/netpfil/pf/utils.py46
-rw-r--r--tests/sys/netpfil/pf/utils.subr414
-rw-r--r--tests/sys/opencrypto/Makefile22
-rw-r--r--tests/sys/opencrypto/Makefile.depend16
-rw-r--r--tests/sys/opencrypto/blake2-kat.h16467
-rw-r--r--tests/sys/opencrypto/blake2_test.c217
-rw-r--r--tests/sys/opencrypto/cryptodev.py736
-rw-r--r--tests/sys/opencrypto/cryptodevh.py237
-rw-r--r--tests/sys/opencrypto/cryptotest.py456
-rw-r--r--tests/sys/opencrypto/poly1305_test.c394
-rw-r--r--tests/sys/opencrypto/runtests.sh100
-rw-r--r--tests/sys/pjdfstest/Makefile8
-rw-r--r--tests/sys/pjdfstest/Makefile.inc1
-rw-r--r--tests/sys/pjdfstest/config.h205
-rw-r--r--tests/sys/pjdfstest/pjdfstest/Makefile11
-rw-r--r--tests/sys/pjdfstest/pjdfstest/Makefile.depend16
-rw-r--r--tests/sys/pjdfstest/tests/Makefile38
-rw-r--r--tests/sys/pjdfstest/tests/Makefile.depend10
-rw-r--r--tests/sys/pjdfstest/tests/Makefile.inc1
-rw-r--r--tests/sys/pjdfstest/tests/chflags/Makefile16
-rw-r--r--tests/sys/pjdfstest/tests/chflags/Makefile.depend10
-rw-r--r--tests/sys/pjdfstest/tests/chmod/Makefile15
-rw-r--r--tests/sys/pjdfstest/tests/chmod/Makefile.depend10
-rw-r--r--tests/sys/pjdfstest/tests/chown/Makefile13
-rw-r--r--tests/sys/pjdfstest/tests/chown/Makefile.depend10
-rw-r--r--tests/sys/pjdfstest/tests/conf7
-rw-r--r--tests/sys/pjdfstest/tests/ftruncate/Makefile17
-rw-r--r--tests/sys/pjdfstest/tests/ftruncate/Makefile.depend10
-rw-r--r--tests/sys/pjdfstest/tests/granular/Makefile8
-rw-r--r--tests/sys/pjdfstest/tests/granular/Makefile.depend10
-rw-r--r--tests/sys/pjdfstest/tests/link/Makefile20
-rw-r--r--tests/sys/pjdfstest/tests/link/Makefile.depend10
-rw-r--r--tests/sys/pjdfstest/tests/mkdir/Makefile15
-rw-r--r--tests/sys/pjdfstest/tests/mkdir/Makefile.depend10
-rw-r--r--tests/sys/pjdfstest/tests/mkfifo/Makefile15
-rw-r--r--tests/sys/pjdfstest/tests/mkfifo/Makefile.depend10
-rw-r--r--tests/sys/pjdfstest/tests/mknod/Makefile14
-rw-r--r--tests/sys/pjdfstest/tests/mknod/Makefile.depend10
-rw-r--r--tests/sys/pjdfstest/tests/open/Makefile27
-rw-r--r--tests/sys/pjdfstest/tests/open/Makefile.depend10
-rw-r--r--tests/sys/pjdfstest/tests/pjdfstest.test.mk13
-rw-r--r--tests/sys/pjdfstest/tests/rename/Makefile24
-rw-r--r--tests/sys/pjdfstest/tests/rename/Makefile.depend10
-rw-r--r--tests/sys/pjdfstest/tests/rmdir/Makefile18
-rw-r--r--tests/sys/pjdfstest/tests/rmdir/Makefile.depend10
-rw-r--r--tests/sys/pjdfstest/tests/symlink/Makefile15
-rw-r--r--tests/sys/pjdfstest/tests/symlink/Makefile.depend10
-rw-r--r--tests/sys/pjdfstest/tests/truncate/Makefile17
-rw-r--r--tests/sys/pjdfstest/tests/truncate/Makefile.depend10
-rw-r--r--tests/sys/pjdfstest/tests/unlink/Makefile16
-rw-r--r--tests/sys/pjdfstest/tests/unlink/Makefile.depend10
-rw-r--r--tests/sys/pjdfstest/tests/utimensat/Makefile12
-rw-r--r--tests/sys/pjdfstest/tests/utimensat/Makefile.depend10
-rw-r--r--tests/sys/posixshm/Makefile8
-rw-r--r--tests/sys/posixshm/Makefile.depend17
-rw-r--r--tests/sys/posixshm/memfd_test.c293
-rw-r--r--tests/sys/posixshm/posixshm_test.c1997
-rw-r--r--tests/sys/ses/Makefile11
-rw-r--r--tests/sys/ses/common.h67
-rw-r--r--tests/sys/ses/destructive.c305
-rw-r--r--tests/sys/ses/nondestructive.c670
-rw-r--r--tests/sys/sound/Makefile13
-rw-r--r--tests/sys/sound/pcm_read_write.c262
-rw-r--r--tests/sys/sound/sndstat.c395
-rw-r--r--tests/sys/sys/Makefile19
-rw-r--r--tests/sys/sys/Makefile.depend17
-rw-r--r--tests/sys/sys/arb_test.c113
-rw-r--r--tests/sys/sys/bitset_test.c90
-rw-r--r--tests/sys/sys/bitstring_test.c977
-rw-r--r--tests/sys/sys/buf_ring_test.c180
-rw-r--r--tests/sys/sys/qmath_test.c649
-rw-r--r--tests/sys/sys/queue_test.c293
-rw-r--r--tests/sys/sys/rb_test.c113
-rw-r--r--tests/sys/sys/splay_test.c110
-rw-r--r--tests/sys/sys/time_test.c222
-rw-r--r--tests/sys/vfs/Makefile12
-rw-r--r--tests/sys/vfs/Makefile.depend17
-rw-r--r--tests/sys/vfs/lookup_cap_dotdot.c272
-rw-r--r--tests/sys/vfs/lookup_test.sh16
-rw-r--r--tests/sys/vfs/trailing_slash.sh42
-rw-r--r--tests/sys/vm/Makefile19
-rw-r--r--tests/sys/vm/Makefile.depend17
-rw-r--r--tests/sys/vm/mlock_test.c286
-rw-r--r--tests/sys/vm/mmap_map_32bit_helper.c50
-rw-r--r--tests/sys/vm/mmap_map_32bit_test.sh37
-rw-r--r--tests/sys/vm/mmap_test.c376
-rw-r--r--tests/sys/vm/page_fault_signal.c179
-rw-r--r--tests/sys/vm/shared_shadow_inval_test.c435
-rw-r--r--tests/sys/vm/soxstack/Makefile16
-rw-r--r--tests/sys/vm/soxstack/soxstack.c51
-rw-r--r--tests/sys/vm/stack/Makefile13
-rw-r--r--tests/sys/vm/stack/stack_dlopen_exec_test.c64
-rw-r--r--tests/sys/vm/stack/stack_dt_need_exec_test.c50
-rw-r--r--tests/sys/vm/stack/stack_mprotect_exec_test.c57
-rw-r--r--tests/sys/vmm/Makefile11
-rw-r--r--tests/sys/vmm/utils.subr47
-rw-r--r--tests/sys/vmm/vmm_cred_jail.sh80
2327 files changed, 320298 insertions, 0 deletions
diff --git a/tests/Kyuafile b/tests/Kyuafile
new file mode 100644
index 000000000000..3e97c4ce5cde
--- /dev/null
+++ b/tests/Kyuafile
@@ -0,0 +1,51 @@
+--
+-- Copyright 2011 Google Inc.
+-- All rights reserved.
+--
+-- Redistribution and use in source and binary forms, with or without
+-- modification, are permitted provided that the following conditions are
+-- met:
+--
+-- * Redistributions of source code must retain the above copyright
+-- notice, this list of conditions and the following disclaimer.
+-- * Redistributions in binary form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in the
+-- documentation and/or other materials provided with the distribution.
+-- * Neither the name of Google Inc. nor the names of its contributors
+-- may be used to endorse or promote products derived from this software
+-- without specific prior written permission.
+--
+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+-- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-- Automatically recurses into any subdirectory that holds a Kyuafile.
+-- As such, this Kyuafile is suitable for installation into the root of
+-- the tests hierarchy as well as into any other subdirectory that needs
+-- "auto-discovery" of tests.
+--
+-- This file is based on the Kyuafile.top sample file distributed in the
+-- kyua-cli package.
+
+syntax(2)
+
+local directory = fs.dirname(current_kyuafile())
+for file in fs.files(directory) do
+ if file == "." or file == ".." then
+ -- Skip these special entries.
+ else
+ local kyuafile_relative = fs.join(file, "Kyuafile")
+ local kyuafile_absolute = fs.join(directory, kyuafile_relative)
+ if fs.exists(kyuafile_absolute) then
+ include(kyuafile_relative)
+ end
+ end
+end
diff --git a/tests/Makefile b/tests/Makefile
new file mode 100644
index 000000000000..451d55498a26
--- /dev/null
+++ b/tests/Makefile
@@ -0,0 +1,28 @@
+.include <src.opts.mk>
+
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}
+
+${PACKAGE}FILES+= README __init__.py conftest.py
+
+KYUAFILE= yes
+
+SUBDIR+= etc
+SUBDIR+= examples
+SUBDIR+= include
+SUBDIR+= sys
+SUBDIR+= atf_python
+.if ${MK_CDDL} != "no"
+SUBDIR+= oclo
+.endif
+
+SUBDIR_PARALLEL=
+
+afterinstall: install-tests-local
+install-tests-local: .PHONY
+ ${INSTALL_SYMLINK} -T 'package=tests' \
+ ../local/tests ${DESTDIR}${TESTSDIR}/local
+
+.include "Makefile.inc0"
+.include <bsd.test.mk>
diff --git a/tests/Makefile.depend b/tests/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/Makefile.inc0 b/tests/Makefile.inc0
new file mode 100644
index 000000000000..dc84ddc60992
--- /dev/null
+++ b/tests/Makefile.inc0
@@ -0,0 +1,6 @@
+# tests/... doesn't conform to bsd.lib.mk and bsd.prog.mk, so specify a sane
+# default for MK_CHECK_USE_SANDBOX. src.opts.mk will override the value if the
+# user sets it to no.
+MK_CHECK_USE_SANDBOX= yes
+
+.include <bsd.opts.mk>
diff --git a/tests/README b/tests/README
new file mode 100644
index 000000000000..099788f197f7
--- /dev/null
+++ b/tests/README
@@ -0,0 +1,61 @@
+src/tests: The FreeBSD test suite
+=================================
+
+Usage of the FreeBSD test suite:
+(1) Run the tests:
+ kyua test -k /usr/tests/Kyuafile
+(2) See the test results:
+ kyua report
+
+For further information on using the test suite, read tests(7):
+ man tests
+
+Description of FreeBSD test suite
+=================================
+The build of the test suite is organized in the following manner:
+
+* The build of all test artifacts is protected by the MK_TESTS knob.
+ The user can disable these with the WITHOUT_TESTS setting in
+ src.conf(5).
+
+* The goal for /usr/tests/ (the installed test programs) is to follow
+ the same hierarchy as /usr/src/ wherever possible, which in turn drives
+ several of the design decisions described below. This simplifies the
+ discoverability of tests. We want a mapping such as:
+
+ /usr/src/bin/cp/ -> /usr/tests/bin/cp/
+ /usr/src/lib/libc/ -> /usr/tests/lib/libc/
+ /usr/src/usr.bin/cut/ -> /usr/tests/usr.bin/cut/
+ ... and many more ...
+
+* Test programs for specific utilities and libraries are located next
+ to the source code of such programs. For example, the tests for the
+ src/lib/libcrypt/ library live in src/lib/libcrypt/tests/. The tests/
+ subdirectory is optional and should, in general, be avoided.
+
+* The src/tests/ hierarchy (this directory) provides generic test
+ infrastructure and glue code to join all test programs together into
+ a single test suite definition.
+
+* The src/tests/ hierarchy also includes cross-functional test programs:
+ i.e. test programs that cover more than a single utility or library
+ and thus don't fit anywhere else in the tree. Consider this to follow
+ the same rationale as src/share/man/: this directory contains generic
+ manual pages while the manual pages that are specific to individual
+ tools or libraries live next to the source code.
+
+In order to keep the src/tests/ hierarchy decoupled from the actual test
+programs being installed --which is a worthy goal because it simplifies
+the addition of new test programs and simplifies the maintenance of the
+tree-- the top-level Kyuafile does not know which subdirectories may
+exist upfront. Instead, such Kyuafile automatically detects, at
+run-time, which */Kyuafile files exist and uses those directly.
+
+Similarly, every directory in src/ that wants to install a Kyuafile to
+just recurse into other subdirectories reuses this Kyuafile with
+auto-discovery features. As an example, take a look at src/lib/tests/
+whose sole purpose is to install a Kyuafile into /usr/tests/lib/.
+The goal in this specific case is for /usr/tests/lib/ to be generated
+entirely from src/lib/.
+
+--
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/tests/__init__.py
diff --git a/tests/atf_python/Makefile b/tests/atf_python/Makefile
new file mode 100644
index 000000000000..6b7c39948b0a
--- /dev/null
+++ b/tests/atf_python/Makefile
@@ -0,0 +1,14 @@
+.include <src.opts.mk>
+
+.PATH: ${.CURDIR}
+
+PACKAGE= tests
+
+FILES= __init__.py atf_pytest.py ktest.py utils.py
+SUBDIR= sys
+
+.include <bsd.own.mk>
+FILESDIR= ${TESTSBASE}/atf_python
+
+
+.include <bsd.prog.mk>
diff --git a/tests/atf_python/__init__.py b/tests/atf_python/__init__.py
new file mode 100644
index 000000000000..6d5ec22ef054
--- /dev/null
+++ b/tests/atf_python/__init__.py
@@ -0,0 +1,4 @@
+import pytest
+
+pytest.register_assert_rewrite("atf_python.sys.net.rtsock")
+pytest.register_assert_rewrite("atf_python.sys.net.vnet")
diff --git a/tests/atf_python/atf_pytest.py b/tests/atf_python/atf_pytest.py
new file mode 100644
index 000000000000..19b5f88fa200
--- /dev/null
+++ b/tests/atf_python/atf_pytest.py
@@ -0,0 +1,298 @@
+import types
+from typing import Any
+from typing import Dict
+from typing import List
+from typing import NamedTuple
+from typing import Optional
+from typing import Tuple
+
+from atf_python.ktest import generate_ktests
+from atf_python.utils import nodeid_to_method_name
+
+import pytest
+import os
+
+
+class ATFCleanupItem(pytest.Item):
+ def runtest(self):
+ """Runs cleanup procedure for the test instead of the test itself"""
+ instance = self.parent.cls()
+ cleanup_name = "cleanup_{}".format(nodeid_to_method_name(self.nodeid))
+ if hasattr(instance, cleanup_name):
+ cleanup = getattr(instance, cleanup_name)
+ cleanup(self.nodeid)
+ elif hasattr(instance, "cleanup"):
+ instance.cleanup(self.nodeid)
+
+ def setup_method_noop(self, method):
+ """Overrides runtest setup method"""
+ pass
+
+ def teardown_method_noop(self, method):
+ """Overrides runtest teardown method"""
+ pass
+
+
+class ATFTestObj(object):
+ def __init__(self, obj, has_cleanup):
+ # Use nodeid without name to properly name class-derived tests
+ self.ident = obj.nodeid.split("::", 1)[1]
+ self.description = self._get_test_description(obj)
+ self.has_cleanup = has_cleanup
+ self.obj = obj
+
+ def _get_test_description(self, obj):
+ """Returns first non-empty line from func docstring or func name"""
+ if getattr(obj, "descr", None) is not None:
+ return getattr(obj, "descr")
+ docstr = obj.function.__doc__
+ if docstr:
+ for line in docstr.split("\n"):
+ if line:
+ return line
+ return obj.name
+
+ @staticmethod
+ def _convert_user_mark(mark, obj, ret: Dict):
+ username = mark.args[0]
+ if username == "unprivileged":
+ # Special unprivileged user requested.
+ # First, require the unprivileged-user config option presence
+ key = "require.config"
+ if key not in ret:
+ ret[key] = "unprivileged_user"
+ else:
+ ret[key] = "{} {}".format(ret[key], "unprivileged_user")
+ # Check if the framework requires root
+ test_cls = ATFHandler.get_test_class(obj)
+ if test_cls and getattr(test_cls, "NEED_ROOT", False):
+ # Yes, so we ask kyua to run us under root instead
+ # It is up to the implementation to switch back to the desired
+ # user
+ ret["require.user"] = "root"
+ else:
+ ret["require.user"] = username
+
+ def _convert_marks(self, obj) -> Dict[str, Any]:
+ wj_func = lambda x: " ".join(x) # noqa: E731
+ _map: Dict[str, Dict] = {
+ "require_user": {"handler": self._convert_user_mark},
+ "require_arch": {"name": "require.arch", "fmt": wj_func},
+ "require_diskspace": {"name": "require.diskspace"},
+ "require_files": {"name": "require.files", "fmt": wj_func},
+ "require_machine": {"name": "require.machine", "fmt": wj_func},
+ "require_memory": {"name": "require.memory"},
+ "require_progs": {"name": "require.progs", "fmt": wj_func},
+ "timeout": {},
+ }
+ ret = {}
+ for mark in obj.iter_markers():
+ if mark.name in _map:
+ if "handler" in _map[mark.name]:
+ _map[mark.name]["handler"](mark, obj, ret)
+ continue
+ name = _map[mark.name].get("name", mark.name)
+ if "fmt" in _map[mark.name]:
+ val = _map[mark.name]["fmt"](mark.args[0])
+ else:
+ val = mark.args[0]
+ ret[name] = val
+ return ret
+
+ def as_lines(self) -> List[str]:
+ """Output test definition in ATF-specific format"""
+ ret = []
+ ret.append("ident: {}".format(self.ident))
+ ret.append("descr: {}".format(self._get_test_description(self.obj)))
+ if self.has_cleanup:
+ ret.append("has.cleanup: true")
+ for key, value in self._convert_marks(self.obj).items():
+ ret.append("{}: {}".format(key, value))
+ return ret
+
+
+class ATFHandler(object):
+ class ReportState(NamedTuple):
+ state: str
+ reason: str
+
+ def __init__(self, report_file_name: Optional[str]):
+ self._tests_state_map: Dict[str, ReportStatus] = {}
+ self._report_file_name = report_file_name
+ self._report_file_handle = None
+
+ def setup_configure(self):
+ fname = self._report_file_name
+ if fname:
+ self._report_file_handle = open(fname, mode="w")
+
+ def setup_method_pre(self, item):
+ """Called before actually running the test setup_method"""
+ # Check if we need to manually drop the privileges
+ for mark in item.iter_markers():
+ if mark.name == "require_user":
+ cls = self.get_test_class(item)
+ cls.TARGET_USER = mark.args[0]
+ break
+
+ def override_runtest(self, obj):
+ # Override basic runtest command
+ obj.runtest = types.MethodType(ATFCleanupItem.runtest, obj)
+ # Override class setup/teardown
+ obj.parent.cls.setup_method = ATFCleanupItem.setup_method_noop
+ obj.parent.cls.teardown_method = ATFCleanupItem.teardown_method_noop
+
+ @staticmethod
+ def get_test_class(obj):
+ if hasattr(obj, "parent") and obj.parent is not None:
+ if hasattr(obj.parent, "cls"):
+ return obj.parent.cls
+
+ def has_object_cleanup(self, obj):
+ cls = self.get_test_class(obj)
+ if cls is not None:
+ method_name = nodeid_to_method_name(obj.nodeid)
+ cleanup_name = "cleanup_{}".format(method_name)
+ if hasattr(cls, "cleanup") or hasattr(cls, cleanup_name):
+ return True
+ return False
+
+ def _generate_test_cleanups(self, items):
+ new_items = []
+ for obj in items:
+ if self.has_object_cleanup(obj):
+ self.override_runtest(obj)
+ new_items.append(obj)
+ items.clear()
+ items.extend(new_items)
+
+ def expand_tests(self, collector, name, obj):
+ return generate_ktests(collector, name, obj)
+
+ def modify_tests(self, items, config):
+ if config.option.atf_cleanup:
+ self._generate_test_cleanups(items)
+
+ def list_tests(self, tests: List[str]):
+ print('Content-Type: application/X-atf-tp; version="1"')
+ print()
+ for test_obj in tests:
+ has_cleanup = self.has_object_cleanup(test_obj)
+ atf_test = ATFTestObj(test_obj, has_cleanup)
+ for line in atf_test.as_lines():
+ print(line)
+ print()
+
+ def set_report_state(self, test_name: str, state: str, reason: str):
+ self._tests_state_map[test_name] = self.ReportState(state, reason)
+
+ def _extract_report_reason(self, report):
+ data = report.longrepr
+ if data is None:
+ return None
+ if isinstance(data, Tuple):
+ # ('/path/to/test.py', 23, 'Skipped: unable to test')
+ reason = data[2]
+ for prefix in "Skipped: ":
+ if reason.startswith(prefix):
+ reason = reason[len(prefix):]
+ return reason
+ else:
+ # string/ traceback / exception report. Capture the last line
+ return str(data).split("\n")[-1]
+ return None
+
+ def add_report(self, report):
+ # MAP pytest report state to the atf-desired state
+ #
+ # ATF test states:
+ # (1) expected_death, (2) expected_exit, (3) expected_failure
+ # (4) expected_signal, (5) expected_timeout, (6) passed
+ # (7) skipped, (8) failed
+ #
+ # Note that ATF don't have the concept of "soft xfail" - xpass
+ # is a failure. It also calls teardown routine in a separate
+ # process, thus teardown states (pytest-only) are handled as
+ # body continuation.
+
+ # (stage, state, wasxfail)
+
+ # Just a passing test: WANT: passed
+ # GOT: (setup, passed, F), (call, passed, F), (teardown, passed, F)
+ #
+ # Failing body test: WHAT: failed
+ # GOT: (setup, passed, F), (call, failed, F), (teardown, passed, F)
+ #
+ # pytest.skip test decorator: WANT: skipped
+ # GOT: (setup,skipped, False), (teardown, passed, False)
+ #
+ # pytest.skip call inside test function: WANT: skipped
+ # GOT: (setup, passed, F), (call, skipped, F), (teardown,passed, F)
+ #
+ # mark.xfail decorator+pytest.xfail: WANT: expected_failure
+ # GOT: (setup, passed, F), (call, skipped, T), (teardown, passed, F)
+ #
+ # mark.xfail decorator+pass: WANT: failed
+ # GOT: (setup, passed, F), (call, passed, T), (teardown, passed, F)
+
+ test_name = report.location[2]
+ stage = report.when
+ state = report.outcome
+ reason = self._extract_report_reason(report)
+
+ # We don't care about strict xfail - it gets translated to False
+
+ if stage == "setup":
+ if state in ("skipped", "failed"):
+ # failed init -> failed test, skipped setup -> xskip
+ # for the whole test
+ self.set_report_state(test_name, state, reason)
+ elif stage == "call":
+ # "call" stage shouldn't matter if setup failed
+ if test_name in self._tests_state_map:
+ if self._tests_state_map[test_name].state == "failed":
+ return
+ if state == "failed":
+ # Record failure & override "skipped" state
+ self.set_report_state(test_name, state, reason)
+ elif state == "skipped":
+ if hasattr(reason, "wasxfail"):
+ # xfail() called in the test body
+ state = "expected_failure"
+ else:
+ # skip inside the body
+ pass
+ self.set_report_state(test_name, state, reason)
+ elif state == "passed":
+ if hasattr(reason, "wasxfail"):
+ # the test was expected to fail but didn't
+ # mark as hard failure
+ state = "failed"
+ self.set_report_state(test_name, state, reason)
+ elif stage == "teardown":
+ if state == "failed":
+ # teardown should be empty, as the cleanup
+ # procedures should be implemented as a separate
+ # function/method, so mark teardown failure as
+ # global failure
+ self.set_report_state(test_name, state, reason)
+
+ def write_report(self):
+ if self._report_file_handle is None:
+ return
+ if self._tests_state_map:
+ # If we're executing in ATF mode, there has to be just one test
+ # Anyway, deterministically pick the first one
+ first_test_name = next(iter(self._tests_state_map))
+ test = self._tests_state_map[first_test_name]
+ if test.state == "passed":
+ line = test.state
+ else:
+ line = "{}: {}".format(test.state, test.reason)
+ print(line, file=self._report_file_handle)
+ self._report_file_handle.close()
+
+ @staticmethod
+ def get_atf_vars() -> Dict[str, str]:
+ px = "_ATF_VAR_"
+ return {k[len(px):]: v for k, v in os.environ.items() if k.startswith(px)}
diff --git a/tests/atf_python/ktest.py b/tests/atf_python/ktest.py
new file mode 100644
index 000000000000..a18f47d1dd06
--- /dev/null
+++ b/tests/atf_python/ktest.py
@@ -0,0 +1,173 @@
+import logging
+import time
+from typing import NamedTuple
+
+import pytest
+from atf_python.sys.netlink.attrs import NlAttrNested
+from atf_python.sys.netlink.attrs import NlAttrStr
+from atf_python.sys.netlink.netlink import NetlinkMultipartIterator
+from atf_python.sys.netlink.netlink import NlHelper
+from atf_python.sys.netlink.netlink import Nlsock
+from atf_python.sys.netlink.netlink_generic import KtestAttrType
+from atf_python.sys.netlink.netlink_generic import KtestInfoMessage
+from atf_python.sys.netlink.netlink_generic import KtestLogMsgType
+from atf_python.sys.netlink.netlink_generic import KtestMsgAttrType
+from atf_python.sys.netlink.netlink_generic import KtestMsgType
+from atf_python.sys.netlink.netlink_generic import timespec
+from atf_python.sys.netlink.utils import NlConst
+from atf_python.utils import BaseTest
+from atf_python.utils import libc
+from atf_python.utils import nodeid_to_method_name
+
+
+datefmt = "%H:%M:%S"
+fmt = "%(asctime)s.%(msecs)03d %(filename)s:%(funcName)s:%(lineno)d %(message)s"
+logging.basicConfig(level=logging.DEBUG, format=fmt, datefmt=datefmt)
+logger = logging.getLogger("ktest")
+
+
+NETLINK_FAMILY = "ktest"
+
+
+class KtestItem(pytest.Item):
+ def __init__(self, *, descr, kcls, **kwargs):
+ super().__init__(**kwargs)
+ self.descr = descr
+ self._kcls = kcls
+
+ def runtest(self):
+ self._kcls().runtest()
+
+
+class KtestCollector(pytest.Class):
+ def collect(self):
+ obj = self.obj
+ exclude_names = set([n for n in dir(obj) if not n.startswith("_")])
+
+ autoload = obj.KTEST_MODULE_AUTOLOAD
+ module_name = obj.KTEST_MODULE_NAME
+ loader = KtestLoader(module_name, autoload)
+ ktests = loader.load_ktests()
+ if not ktests:
+ return
+
+ orig = pytest.Class.from_parent(self.parent, name=self.name, obj=obj)
+ for py_test in orig.collect():
+ yield py_test
+
+ for ktest in ktests:
+ name = ktest["name"]
+ descr = ktest["desc"]
+ if name in exclude_names:
+ continue
+ yield KtestItem.from_parent(self, name=name, descr=descr, kcls=obj)
+
+
+class KtestLoader(object):
+ def __init__(self, module_name: str, autoload: bool):
+ self.module_name = module_name
+ self.autoload = autoload
+ self.helper = NlHelper()
+ self.nlsock = Nlsock(NlConst.NETLINK_GENERIC, self.helper)
+ self.family_id = self._get_family_id()
+
+ def _get_family_id(self):
+ try:
+ family_id = self.nlsock.get_genl_family_id(NETLINK_FAMILY)
+ except ValueError:
+ if self.autoload:
+ libc.kldload(self.module_name)
+ family_id = self.nlsock.get_genl_family_id(NETLINK_FAMILY)
+ else:
+ raise
+ return family_id
+
+ def _load_ktests(self):
+ msg = KtestInfoMessage(self.helper, self.family_id, KtestMsgType.KTEST_CMD_LIST)
+ msg.set_request()
+ msg.add_nla(NlAttrStr(KtestAttrType.KTEST_ATTR_MOD_NAME, self.module_name))
+ self.nlsock.write_message(msg, verbose=False)
+ nlmsg_seq = msg.nl_hdr.nlmsg_seq
+
+ ret = []
+ for rx_msg in NetlinkMultipartIterator(self.nlsock, nlmsg_seq, self.family_id):
+ # rx_msg.print_message()
+ tst = {
+ "mod_name": rx_msg.get_nla(KtestAttrType.KTEST_ATTR_MOD_NAME).text,
+ "name": rx_msg.get_nla(KtestAttrType.KTEST_ATTR_TEST_NAME).text,
+ "desc": rx_msg.get_nla(KtestAttrType.KTEST_ATTR_TEST_DESCR).text,
+ }
+ ret.append(tst)
+ return ret
+
+ def load_ktests(self):
+ ret = self._load_ktests()
+ if not ret and self.autoload:
+ libc.kldload(self.module_name)
+ ret = self._load_ktests()
+ return ret
+
+
+def generate_ktests(collector, name, obj):
+ if getattr(obj, "KTEST_MODULE_NAME", None) is not None:
+ return KtestCollector.from_parent(collector, name=name, obj=obj)
+ return None
+
+
+class BaseKernelTest(BaseTest):
+ KTEST_MODULE_AUTOLOAD = True
+ KTEST_MODULE_NAME = None
+
+ def _get_record_time(self, msg) -> float:
+ timespec = msg.get_nla(KtestMsgAttrType.KTEST_MSG_ATTR_TS).ts
+ epoch_ktime = timespec.tv_sec * 1.0 + timespec.tv_nsec * 1.0 / 1000000000
+ if not hasattr(self, "_start_epoch"):
+ self._start_ktime = epoch_ktime
+ self._start_time = time.time()
+ epoch_time = self._start_time
+ else:
+ epoch_time = time.time() - self._start_time + epoch_ktime
+ return epoch_time
+
+ def _log_message(self, msg):
+ # Convert syslog-type l
+ syslog_level = msg.get_nla(KtestMsgAttrType.KTEST_MSG_ATTR_LEVEL).u8
+ if syslog_level <= 6:
+ loglevel = logging.INFO
+ else:
+ loglevel = logging.DEBUG
+ rec = logging.LogRecord(
+ self.KTEST_MODULE_NAME,
+ loglevel,
+ msg.get_nla(KtestMsgAttrType.KTEST_MSG_ATTR_FILE).text,
+ msg.get_nla(KtestMsgAttrType.KTEST_MSG_ATTR_LINE).u32,
+ "%s",
+ (msg.get_nla(KtestMsgAttrType.KTEST_MSG_ATTR_TEXT).text),
+ None,
+ msg.get_nla(KtestMsgAttrType.KTEST_MSG_ATTR_FUNC).text,
+ None,
+ )
+ rec.created = self._get_record_time(msg)
+ logger.handle(rec)
+
+ def _runtest_name(self, test_name: str, test_data):
+ module_name = self.KTEST_MODULE_NAME
+ # print("Running kernel test {} for module {}".format(test_name, module_name))
+ helper = NlHelper()
+ nlsock = Nlsock(NlConst.NETLINK_GENERIC, helper)
+ family_id = nlsock.get_genl_family_id(NETLINK_FAMILY)
+ msg = KtestInfoMessage(helper, family_id, KtestMsgType.KTEST_CMD_RUN)
+ msg.set_request()
+ msg.add_nla(NlAttrStr(KtestAttrType.KTEST_ATTR_MOD_NAME, module_name))
+ msg.add_nla(NlAttrStr(KtestAttrType.KTEST_ATTR_TEST_NAME, test_name))
+ if test_data is not None:
+ msg.add_nla(NlAttrNested(KtestAttrType.KTEST_ATTR_TEST_META, test_data))
+ nlsock.write_message(msg, verbose=False)
+
+ for log_msg in NetlinkMultipartIterator(
+ nlsock, msg.nl_hdr.nlmsg_seq, family_id
+ ):
+ self._log_message(log_msg)
+
+ def runtest(self, test_data=None):
+ self._runtest_name(nodeid_to_method_name(self.test_id), test_data)
diff --git a/tests/atf_python/sys/Makefile b/tests/atf_python/sys/Makefile
new file mode 100644
index 000000000000..a5a1a532104d
--- /dev/null
+++ b/tests/atf_python/sys/Makefile
@@ -0,0 +1,12 @@
+.include <src.opts.mk>
+
+.PATH: ${.CURDIR}
+
+PACKAGE=tests
+FILES= __init__.py
+SUBDIR= net netlink netpfil
+
+.include <bsd.own.mk>
+FILESDIR= ${TESTSBASE}/atf_python/sys
+
+.include <bsd.prog.mk>
diff --git a/tests/atf_python/sys/__init__.py b/tests/atf_python/sys/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/tests/atf_python/sys/__init__.py
diff --git a/tests/atf_python/sys/net/Makefile b/tests/atf_python/sys/net/Makefile
new file mode 100644
index 000000000000..70d5b1a3284b
--- /dev/null
+++ b/tests/atf_python/sys/net/Makefile
@@ -0,0 +1,11 @@
+.include <src.opts.mk>
+
+.PATH: ${.CURDIR}
+
+PACKAGE=tests
+FILES= __init__.py rtsock.py tools.py vnet.py
+
+.include <bsd.own.mk>
+FILESDIR= ${TESTSBASE}/atf_python/sys/net
+
+.include <bsd.prog.mk>
diff --git a/tests/atf_python/sys/net/__init__.py b/tests/atf_python/sys/net/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/tests/atf_python/sys/net/__init__.py
diff --git a/tests/atf_python/sys/net/rtsock.py b/tests/atf_python/sys/net/rtsock.py
new file mode 100755
index 000000000000..788e863f8b28
--- /dev/null
+++ b/tests/atf_python/sys/net/rtsock.py
@@ -0,0 +1,604 @@
+#!/usr/local/bin/python3
+import os
+import socket
+import struct
+import sys
+from ctypes import c_byte
+from ctypes import c_char
+from ctypes import c_int
+from ctypes import c_long
+from ctypes import c_uint32
+from ctypes import c_ulong
+from ctypes import c_ushort
+from ctypes import sizeof
+from ctypes import Structure
+from typing import Dict
+from typing import List
+from typing import Optional
+from typing import Union
+
+
+def roundup2(val: int, num: int) -> int:
+ if val % num:
+ return (val | (num - 1)) + 1
+ else:
+ return val
+
+
+class RtSockException(OSError):
+ pass
+
+
+class RtConst:
+ RTM_VERSION = 5
+ ALIGN = sizeof(c_long)
+
+ AF_INET = socket.AF_INET
+ AF_INET6 = socket.AF_INET6
+ AF_LINK = socket.AF_LINK
+
+ RTA_DST = 0x1
+ RTA_GATEWAY = 0x2
+ RTA_NETMASK = 0x4
+ RTA_GENMASK = 0x8
+ RTA_IFP = 0x10
+ RTA_IFA = 0x20
+ RTA_AUTHOR = 0x40
+ RTA_BRD = 0x80
+
+ RTM_ADD = 1
+ RTM_DELETE = 2
+ RTM_CHANGE = 3
+ RTM_GET = 4
+
+ RTF_UP = 0x1
+ RTF_GATEWAY = 0x2
+ RTF_HOST = 0x4
+ RTF_REJECT = 0x8
+ RTF_DYNAMIC = 0x10
+ RTF_MODIFIED = 0x20
+ RTF_DONE = 0x40
+ RTF_XRESOLVE = 0x200
+ RTF_LLINFO = 0x400
+ RTF_LLDATA = 0x400
+ RTF_STATIC = 0x800
+ RTF_BLACKHOLE = 0x1000
+ RTF_PROTO2 = 0x4000
+ RTF_PROTO1 = 0x8000
+ RTF_PROTO3 = 0x40000
+ RTF_FIXEDMTU = 0x80000
+ RTF_PINNED = 0x100000
+ RTF_LOCAL = 0x200000
+ RTF_BROADCAST = 0x400000
+ RTF_MULTICAST = 0x800000
+ RTF_STICKY = 0x10000000
+ RTF_RNH_LOCKED = 0x40000000
+ RTF_GWFLAG_COMPAT = 0x80000000
+
+ RTV_MTU = 0x1
+ RTV_HOPCOUNT = 0x2
+ RTV_EXPIRE = 0x4
+ RTV_RPIPE = 0x8
+ RTV_SPIPE = 0x10
+ RTV_SSTHRESH = 0x20
+ RTV_RTT = 0x40
+ RTV_RTTVAR = 0x80
+ RTV_WEIGHT = 0x100
+
+ @staticmethod
+ def get_props(prefix: str) -> List[str]:
+ return [n for n in dir(RtConst) if n.startswith(prefix)]
+
+ @staticmethod
+ def get_name(prefix: str, value: int) -> str:
+ props = RtConst.get_props(prefix)
+ for prop in props:
+ if getattr(RtConst, prop) == value:
+ return prop
+ return "U:{}:{}".format(prefix, value)
+
+ @staticmethod
+ def get_bitmask_map(prefix: str, value: int) -> Dict[int, str]:
+ props = RtConst.get_props(prefix)
+ propmap = {getattr(RtConst, prop): prop for prop in props}
+ v = 1
+ ret = {}
+ while value:
+ if v & value:
+ if v in propmap:
+ ret[v] = propmap[v]
+ else:
+ ret[v] = hex(v)
+ value -= v
+ v *= 2
+ return ret
+
+ @staticmethod
+ def get_bitmask_str(prefix: str, value: int) -> str:
+ bmap = RtConst.get_bitmask_map(prefix, value)
+ return ",".join([v for k, v in bmap.items()])
+
+
+class RtMetrics(Structure):
+ _fields_ = [
+ ("rmx_locks", c_ulong),
+ ("rmx_mtu", c_ulong),
+ ("rmx_hopcount", c_ulong),
+ ("rmx_expire", c_ulong),
+ ("rmx_recvpipe", c_ulong),
+ ("rmx_sendpipe", c_ulong),
+ ("rmx_ssthresh", c_ulong),
+ ("rmx_rtt", c_ulong),
+ ("rmx_rttvar", c_ulong),
+ ("rmx_pksent", c_ulong),
+ ("rmx_weight", c_ulong),
+ ("rmx_nhidx", c_ulong),
+ ("rmx_filler", c_ulong * 2),
+ ]
+
+
+class RtMsgHdr(Structure):
+ _fields_ = [
+ ("rtm_msglen", c_ushort),
+ ("rtm_version", c_byte),
+ ("rtm_type", c_byte),
+ ("rtm_index", c_ushort),
+ ("_rtm_spare1", c_ushort),
+ ("rtm_flags", c_int),
+ ("rtm_addrs", c_int),
+ ("rtm_pid", c_int),
+ ("rtm_seq", c_int),
+ ("rtm_errno", c_int),
+ ("rtm_fmask", c_int),
+ ("rtm_inits", c_ulong),
+ ("rtm_rmx", RtMetrics),
+ ]
+
+
+class SockaddrIn(Structure):
+ _fields_ = [
+ ("sin_len", c_byte),
+ ("sin_family", c_byte),
+ ("sin_port", c_ushort),
+ ("sin_addr", c_uint32),
+ ("sin_zero", c_char * 8),
+ ]
+
+
+class SockaddrIn6(Structure):
+ _fields_ = [
+ ("sin6_len", c_byte),
+ ("sin6_family", c_byte),
+ ("sin6_port", c_ushort),
+ ("sin6_flowinfo", c_uint32),
+ ("sin6_addr", c_byte * 16),
+ ("sin6_scope_id", c_uint32),
+ ]
+
+
+class SockaddrDl(Structure):
+ _fields_ = [
+ ("sdl_len", c_byte),
+ ("sdl_family", c_byte),
+ ("sdl_index", c_ushort),
+ ("sdl_type", c_byte),
+ ("sdl_nlen", c_byte),
+ ("sdl_alen", c_byte),
+ ("sdl_slen", c_byte),
+ ("sdl_data", c_byte * 8),
+ ]
+
+
+class SaHelper(object):
+ @staticmethod
+ def is_ipv6(ip: str) -> bool:
+ return ":" in ip
+
+ @staticmethod
+ def ip_sa(ip: str, scopeid: int = 0) -> bytes:
+ if SaHelper.is_ipv6(ip):
+ return SaHelper.ip6_sa(ip, scopeid)
+ else:
+ return SaHelper.ip4_sa(ip)
+
+ @staticmethod
+ def ip4_sa(ip: str) -> bytes:
+ addr_int = int.from_bytes(socket.inet_pton(2, ip), sys.byteorder)
+ sin = SockaddrIn(sizeof(SockaddrIn), socket.AF_INET, 0, addr_int)
+ return bytes(sin)
+
+ @staticmethod
+ def ip6_sa(ip6: str, scopeid: int) -> bytes:
+ addr_bytes = (c_byte * 16)()
+ for i, b in enumerate(socket.inet_pton(socket.AF_INET6, ip6)):
+ addr_bytes[i] = b
+ sin6 = SockaddrIn6(
+ sizeof(SockaddrIn6), socket.AF_INET6, 0, 0, addr_bytes, scopeid
+ )
+ return bytes(sin6)
+
+ @staticmethod
+ def link_sa(ifindex: int = 0, iftype: int = 0) -> bytes:
+ sa = SockaddrDl(sizeof(SockaddrDl), socket.AF_LINK, c_ushort(ifindex), iftype)
+ return bytes(sa)
+
+ @staticmethod
+ def pxlen4_sa(pxlen: int) -> bytes:
+ return SaHelper.ip_sa(SaHelper.pxlen_to_ip4(pxlen))
+
+ @staticmethod
+ def pxlen_to_ip4(pxlen: int) -> str:
+ if pxlen == 32:
+ return "255.255.255.255"
+ else:
+ addr = 0xFFFFFFFF - ((1 << (32 - pxlen)) - 1)
+ addr_bytes = struct.pack("!I", addr)
+ return socket.inet_ntop(socket.AF_INET, addr_bytes)
+
+ @staticmethod
+ def pxlen6_sa(pxlen: int) -> bytes:
+ return SaHelper.ip_sa(SaHelper.pxlen_to_ip6(pxlen))
+
+ @staticmethod
+ def pxlen_to_ip6(pxlen: int) -> str:
+ ip6_b = [0] * 16
+ start = 0
+ while pxlen > 8:
+ ip6_b[start] = 0xFF
+ pxlen -= 8
+ start += 1
+ ip6_b[start] = 0xFF - ((1 << (8 - pxlen)) - 1)
+ return socket.inet_ntop(socket.AF_INET6, bytes(ip6_b))
+
+ @staticmethod
+ def print_sa_inet(sa: bytes):
+ if len(sa) < 8:
+ raise RtSockException("IPv4 sa size too small: {}".format(len(sa)))
+ addr = socket.inet_ntop(socket.AF_INET, sa[4:8])
+ return "{}".format(addr)
+
+ @staticmethod
+ def print_sa_inet6(sa: bytes):
+ if len(sa) < sizeof(SockaddrIn6):
+ raise RtSockException("IPv6 sa size too small: {}".format(len(sa)))
+ addr = socket.inet_ntop(socket.AF_INET6, sa[8:24])
+ scopeid = struct.unpack(">I", sa[24:28])[0]
+ return "{} scopeid {}".format(addr, scopeid)
+
+ @staticmethod
+ def print_sa_link(sa: bytes, hd: Optional[bool] = True):
+ if len(sa) < sizeof(SockaddrDl):
+ raise RtSockException("LINK sa size too small: {}".format(len(sa)))
+ sdl = SockaddrDl.from_buffer_copy(sa)
+ if sdl.sdl_index:
+ ifindex = "link#{} ".format(sdl.sdl_index)
+ else:
+ ifindex = ""
+ if sdl.sdl_nlen:
+ iface_offset = 8
+ if sdl.sdl_nlen + iface_offset > len(sa):
+ raise RtSockException(
+ "LINK sa sdl_nlen {} > total len {}".format(sdl.sdl_nlen, len(sa))
+ )
+ ifname = "ifname:{} ".format(
+ bytes.decode(sa[iface_offset : iface_offset + sdl.sdl_nlen])
+ )
+ else:
+ ifname = ""
+ return "{}{}".format(ifindex, ifname)
+
+ @staticmethod
+ def print_sa_unknown(sa: bytes):
+ return "unknown_type:{}".format(sa[1])
+
+ @classmethod
+ def print_sa(cls, sa: bytes, hd: Optional[bool] = False):
+ if sa[0] != len(sa):
+ raise Exception("sa size {} != buffer size {}".format(sa[0], len(sa)))
+
+ if len(sa) < 2:
+ raise Exception(
+ "sa type {} too short: {}".format(
+ RtConst.get_name("AF_", sa[1]), len(sa)
+ )
+ )
+
+ if sa[1] == socket.AF_INET:
+ text = cls.print_sa_inet(sa)
+ elif sa[1] == socket.AF_INET6:
+ text = cls.print_sa_inet6(sa)
+ elif sa[1] == socket.AF_LINK:
+ text = cls.print_sa_link(sa)
+ else:
+ text = cls.print_sa_unknown(sa)
+ if hd:
+ dump = " [{!r}]".format(sa)
+ else:
+ dump = ""
+ return "{}{}".format(text, dump)
+
+
+class BaseRtsockMessage(object):
+ def __init__(self, rtm_type):
+ self.rtm_type = rtm_type
+ self.sa = SaHelper()
+
+ @staticmethod
+ def print_rtm_type(rtm_type):
+ return RtConst.get_name("RTM_", rtm_type)
+
+ @property
+ def rtm_type_str(self):
+ return self.print_rtm_type(self.rtm_type)
+
+
+class RtsockRtMessage(BaseRtsockMessage):
+ messages = [
+ RtConst.RTM_ADD,
+ RtConst.RTM_DELETE,
+ RtConst.RTM_CHANGE,
+ RtConst.RTM_GET,
+ ]
+
+ def __init__(self, rtm_type, rtm_seq=1, dst_sa=None, mask_sa=None):
+ super().__init__(rtm_type)
+ self.rtm_flags = 0
+ self.rtm_seq = rtm_seq
+ self._attrs = {}
+ self.rtm_errno = 0
+ self.rtm_pid = 0
+ self.rtm_inits = 0
+ self.rtm_rmx = RtMetrics()
+ self._orig_data = None
+ if dst_sa:
+ self.add_sa_attr(RtConst.RTA_DST, dst_sa)
+ if mask_sa:
+ self.add_sa_attr(RtConst.RTA_NETMASK, mask_sa)
+
+ def add_sa_attr(self, attr_type, attr_bytes: bytes):
+ self._attrs[attr_type] = attr_bytes
+
+ def add_ip_attr(self, attr_type, ip_addr: str, scopeid: int = 0):
+ if ":" in ip_addr:
+ self.add_ip6_attr(attr_type, ip_addr, scopeid)
+ else:
+ self.add_ip4_attr(attr_type, ip_addr)
+
+ def add_ip4_attr(self, attr_type, ip: str):
+ self.add_sa_attr(attr_type, self.sa.ip_sa(ip))
+
+ def add_ip6_attr(self, attr_type, ip6: str, scopeid: int):
+ self.add_sa_attr(attr_type, self.sa.ip6_sa(ip6, scopeid))
+
+ def add_link_attr(self, attr_type, ifindex: Optional[int] = 0):
+ self.add_sa_attr(attr_type, self.sa.link_sa(ifindex))
+
+ def get_sa(self, attr_type) -> bytes:
+ return self._attrs.get(attr_type)
+
+ def print_message(self):
+ # RTM_GET: Report Metrics: len 272, pid: 87839, seq 1, errno 0, flags:<UP,GATEWAY,DONE,STATIC>
+ if self._orig_data:
+ rtm_len = len(self._orig_data)
+ else:
+ rtm_len = len(bytes(self))
+ print(
+ "{}: len {}, pid: {}, seq {}, errno {}, flags: <{}>".format(
+ self.rtm_type_str,
+ rtm_len,
+ self.rtm_pid,
+ self.rtm_seq,
+ self.rtm_errno,
+ RtConst.get_bitmask_str("RTF_", self.rtm_flags),
+ )
+ )
+ rtm_addrs = sum(list(self._attrs.keys()))
+ print("Addrs: <{}>".format(RtConst.get_bitmask_str("RTA_", rtm_addrs)))
+ for attr in sorted(self._attrs.keys()):
+ sa_data = SaHelper.print_sa(self._attrs[attr])
+ print(" {}: {}".format(RtConst.get_name("RTA_", attr), sa_data))
+
+ def print_in_message(self):
+ print("vvvvvvvv IN vvvvvvvv")
+ self.print_message()
+ print()
+
+ def verify_sa_inet(self, sa_data):
+ if len(sa_data) < 8:
+ raise Exception("IPv4 sa size too small: {}".format(sa_data))
+ if sa_data[0] > len(sa_data):
+ raise Exception(
+ "IPv4 sin_len too big: {} vs sa size {}: {}".format(
+ sa_data[0], len(sa_data), sa_data
+ )
+ )
+ sin = SockaddrIn.from_buffer_copy(sa_data)
+ assert sin.sin_port == 0
+ assert sin.sin_zero == [0] * 8
+
+ def compare_sa(self, sa_type, sa_data):
+ if len(sa_data) < 4:
+ sa_type_name = RtConst.get_name("RTA_", sa_type)
+ raise Exception(
+ "sa_len for type {} too short: {}".format(sa_type_name, len(sa_data))
+ )
+ our_sa = self._attrs[sa_type]
+ assert SaHelper.print_sa(sa_data) == SaHelper.print_sa(our_sa)
+ assert len(sa_data) == len(our_sa)
+ assert sa_data == our_sa
+
+ def verify(self, rtm_type: int, rtm_sa):
+ assert self.rtm_type_str == self.print_rtm_type(rtm_type)
+ assert self.rtm_errno == 0
+ hdr = RtMsgHdr.from_buffer_copy(self._orig_data)
+ assert hdr._rtm_spare1 == 0
+ for sa_type, sa_data in rtm_sa.items():
+ if sa_type not in self._attrs:
+ sa_type_name = RtConst.get_name("RTA_", sa_type)
+ raise Exception("SA type {} not present".format(sa_type_name))
+ self.compare_sa(sa_type, sa_data)
+
+ @classmethod
+ def from_bytes(cls, data: bytes):
+ if len(data) < sizeof(RtMsgHdr):
+ raise Exception(
+ "messages size {} is less than expected {}".format(
+ len(data), sizeof(RtMsgHdr)
+ )
+ )
+ hdr = RtMsgHdr.from_buffer_copy(data)
+
+ self = cls(hdr.rtm_type)
+ self.rtm_flags = hdr.rtm_flags
+ self.rtm_seq = hdr.rtm_seq
+ self.rtm_errno = hdr.rtm_errno
+ self.rtm_pid = hdr.rtm_pid
+ self.rtm_inits = hdr.rtm_inits
+ self.rtm_rmx = hdr.rtm_rmx
+ self._orig_data = data
+
+ off = sizeof(RtMsgHdr)
+ v = 1
+ addrs_mask = hdr.rtm_addrs
+ while addrs_mask:
+ if addrs_mask & v:
+ addrs_mask -= v
+
+ if off + data[off] > len(data):
+ raise Exception(
+ "SA sizeof for {} > total message length: {}+{} > {}".format(
+ RtConst.get_name("RTA_", v), off, data[off], len(data)
+ )
+ )
+ self._attrs[v] = data[off : off + data[off]]
+ off += roundup2(data[off], RtConst.ALIGN)
+ v *= 2
+ return self
+
+ def __bytes__(self):
+ sz = sizeof(RtMsgHdr)
+ addrs_mask = 0
+ for k, v in self._attrs.items():
+ sz += roundup2(len(v), RtConst.ALIGN)
+ addrs_mask += k
+ hdr = RtMsgHdr(
+ rtm_msglen=sz,
+ rtm_version=RtConst.RTM_VERSION,
+ rtm_type=self.rtm_type,
+ rtm_flags=self.rtm_flags,
+ rtm_seq=self.rtm_seq,
+ rtm_addrs=addrs_mask,
+ rtm_inits=self.rtm_inits,
+ rtm_rmx=self.rtm_rmx,
+ )
+ buf = bytearray(sz)
+ buf[0 : sizeof(RtMsgHdr)] = hdr
+ off = sizeof(RtMsgHdr)
+ for attr in sorted(self._attrs.keys()):
+ v = self._attrs[attr]
+ sa_len = len(v)
+ buf[off : off + sa_len] = v
+ off += roundup2(len(v), RtConst.ALIGN)
+ return bytes(buf)
+
+
+class Rtsock:
+ def __init__(self):
+ self.socket = self._setup_rtsock()
+ self.rtm_seq = 1
+ self.msgmap = self.build_msgmap()
+
+ def build_msgmap(self):
+ classes = [RtsockRtMessage]
+ xmap = {}
+ for cls in classes:
+ for message in cls.messages:
+ xmap[message] = cls
+ return xmap
+
+ def get_seq(self):
+ ret = self.rtm_seq
+ self.rtm_seq += 1
+ return ret
+
+ def get_weight(self, weight) -> int:
+ if weight:
+ return weight
+ else:
+ return 1 # RT_DEFAULT_WEIGHT
+
+ def new_rtm_any(self, msg_type, prefix: str, gw: Union[str, bytes]):
+ px = prefix.split("/")
+ addr_sa = SaHelper.ip_sa(px[0])
+ if len(px) > 1:
+ pxlen = int(px[1])
+ if SaHelper.is_ipv6(px[0]):
+ mask_sa = SaHelper.pxlen6_sa(pxlen)
+ else:
+ mask_sa = SaHelper.pxlen4_sa(pxlen)
+ else:
+ mask_sa = None
+ msg = RtsockRtMessage(msg_type, self.get_seq(), addr_sa, mask_sa)
+ if isinstance(gw, bytes):
+ msg.add_sa_attr(RtConst.RTA_GATEWAY, gw)
+ else:
+ # String
+ msg.add_ip_attr(RtConst.RTA_GATEWAY, gw)
+ return msg
+
+ def new_rtm_add(self, prefix: str, gw: Union[str, bytes]):
+ return self.new_rtm_any(RtConst.RTM_ADD, prefix, gw)
+
+ def new_rtm_del(self, prefix: str, gw: Union[str, bytes]):
+ return self.new_rtm_any(RtConst.RTM_DELETE, prefix, gw)
+
+ def new_rtm_change(self, prefix: str, gw: Union[str, bytes]):
+ return self.new_rtm_any(RtConst.RTM_CHANGE, prefix, gw)
+
+ def _setup_rtsock(self) -> socket.socket:
+ s = socket.socket(socket.AF_ROUTE, socket.SOCK_RAW, socket.AF_UNSPEC)
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_USELOOPBACK, 1)
+ return s
+
+ def print_hd(self, data: bytes):
+ width = 16
+ print("==========================================")
+ for chunk in [data[i : i + width] for i in range(0, len(data), width)]:
+ for b in chunk:
+ print("0x{:02X} ".format(b), end="")
+ print()
+ print()
+
+ def write_message(self, msg):
+ print("vvvvvvvv OUT vvvvvvvv")
+ msg.print_message()
+ print()
+ msg_bytes = bytes(msg)
+ ret = os.write(self.socket.fileno(), msg_bytes)
+ if ret != -1:
+ assert ret == len(msg_bytes)
+
+ def parse_message(self, data: bytes):
+ if len(data) < 4:
+ raise OSError("Short read from rtsock: {} bytes".format(len(data)))
+ rtm_type = data[4]
+ if rtm_type not in self.msgmap:
+ return None
+
+ def write_data(self, data: bytes):
+ self.socket.send(data)
+
+ def read_data(self, seq: Optional[int] = None) -> bytes:
+ while True:
+ data = self.socket.recv(4096)
+ if seq is None:
+ break
+ if len(data) > sizeof(RtMsgHdr):
+ hdr = RtMsgHdr.from_buffer_copy(data)
+ if hdr.rtm_seq == seq:
+ break
+ return data
+
+ def read_message(self) -> bytes:
+ data = self.read_data()
+ return self.parse_message(data)
diff --git a/tests/atf_python/sys/net/tools.py b/tests/atf_python/sys/net/tools.py
new file mode 100644
index 000000000000..44bd74d8578f
--- /dev/null
+++ b/tests/atf_python/sys/net/tools.py
@@ -0,0 +1,100 @@
+#!/usr/local/bin/python3
+import json
+import os
+import subprocess
+
+
+class ToolsHelper(object):
+ NETSTAT_PATH = "/usr/bin/netstat"
+ IFCONFIG_PATH = "/sbin/ifconfig"
+
+ @classmethod
+ def get_output(cls, cmd: str, verbose=False) -> str:
+ if verbose:
+ print("run: '{}'".format(cmd))
+ return os.popen(cmd).read()
+
+ @classmethod
+ def pf_rules(cls, rules, verbose=True):
+ pf_conf = ""
+ for r in rules:
+ pf_conf = pf_conf + r + "\n"
+
+ if verbose:
+ print("Set rules:")
+ print(pf_conf)
+
+ ps = subprocess.Popen("/sbin/pfctl -g -f -", shell=True,
+ stdin=subprocess.PIPE)
+ ps.communicate(bytes(pf_conf, 'utf-8'))
+ ret = ps.wait()
+ if ret != 0:
+ raise Exception("Failed to set pf rules %d" % ret)
+
+ if verbose:
+ cls.print_output("/sbin/pfctl -sr")
+
+ @classmethod
+ def print_output(cls, cmd: str, verbose=True):
+ if verbose:
+ print("======= {} =====".format(cmd))
+ print(cls.get_output(cmd))
+ if verbose:
+ print()
+
+ @classmethod
+ def print_net_debug(cls):
+ cls.print_output("ifconfig")
+ cls.print_output("netstat -rnW")
+
+ @classmethod
+ def set_sysctl(cls, oid, val):
+ cls.get_output("sysctl {}={}".format(oid, val))
+
+ @classmethod
+ def get_routes(cls, family: str, fibnum: int = 0):
+ family_key = {"inet": "-4", "inet6": "-6"}.get(family)
+ out = cls.get_output(
+ "{} {} -rnW -F {} --libxo json".format(cls.NETSTAT_PATH, family_key, fibnum)
+ )
+ js = json.loads(out)
+ js = js["statistics"]["route-information"]["route-table"]["rt-family"]
+ if js:
+ return js[0]["rt-entry"]
+ else:
+ return []
+
+ @classmethod
+ def get_nhops(cls, family: str, fibnum: int = 0):
+ family_key = {"inet": "-4", "inet6": "-6"}.get(family)
+ out = cls.get_output(
+ "{} {} -onW -F {} --libxo json".format(cls.NETSTAT_PATH, family_key, fibnum)
+ )
+ js = json.loads(out)
+ js = js["statistics"]["route-nhop-information"]["nhop-table"]["rt-family"]
+ if js:
+ return js[0]["nh-entry"]
+ else:
+ return []
+
+ @classmethod
+ def get_linklocals(cls):
+ ret = {}
+ ifname = None
+ ips = []
+ for line in cls.get_output(cls.IFCONFIG_PATH).splitlines():
+ if line[0].isalnum():
+ if ifname:
+ ret[ifname] = ips
+ ips = []
+ ifname = line.split(":")[0]
+ else:
+ words = line.split()
+ if words[0] == "inet6" and words[1].startswith("fe80"):
+ # inet6 fe80::1%lo0 prefixlen 64 scopeid 0x2
+ ip = words[1].split("%")[0]
+ scopeid = int(words[words.index("scopeid") + 1], 16)
+ ips.append((ip, scopeid))
+ if ifname:
+ ret[ifname] = ips
+ return ret
diff --git a/tests/atf_python/sys/net/vnet.py b/tests/atf_python/sys/net/vnet.py
new file mode 100644
index 000000000000..7afb5c721bf3
--- /dev/null
+++ b/tests/atf_python/sys/net/vnet.py
@@ -0,0 +1,554 @@
+#!/usr/local/bin/python3
+import copy
+import ipaddress
+import os
+import re
+import socket
+import sys
+import time
+from multiprocessing import connection
+from multiprocessing import Pipe
+from multiprocessing import Process
+from typing import Dict
+from typing import List
+from typing import NamedTuple
+
+from atf_python.sys.net.tools import ToolsHelper
+from atf_python.utils import BaseTest
+from atf_python.utils import libc
+
+
+def run_cmd(cmd: str, verbose=True) -> str:
+ if verbose:
+ print("run: '{}'".format(cmd))
+ return os.popen(cmd).read()
+
+
+def get_topology_id(test_id: str) -> str:
+ """
+ Gets a unique topology id based on the pytest test_id.
+ "test_ip6_output.py::TestIP6Output::test_output6_pktinfo[ipandif]" ->
+ "TestIP6Output:test_output6_pktinfo[ipandif]"
+ """
+ return ":".join(test_id.split("::")[-2:])
+
+
+def convert_test_name(test_name: str) -> str:
+ """Convert test name to a string that can be used in the file/jail names"""
+ ret = ""
+ for char in test_name:
+ if char.isalnum() or char in ("_", "-", ":"):
+ ret += char
+ elif char in ("["):
+ ret += "_"
+ return ret
+
+
+class VnetInterface(object):
+ # defines from net/if_types.h
+ IFT_LOOP = 0x18
+ IFT_ETHER = 0x06
+
+ def __init__(self, iface_alias: str, iface_name: str):
+ self.name = iface_name
+ self.alias = iface_alias
+ self.vnet_name = ""
+ self.jailed = False
+ self.addr_map: Dict[str, Dict] = {"inet6": {}, "inet": {}}
+ self.prefixes4: List[List[str]] = []
+ self.prefixes6: List[List[str]] = []
+ if iface_name.startswith("lo"):
+ self.iftype = self.IFT_LOOP
+ else:
+ self.iftype = self.IFT_ETHER
+ self.ether = ToolsHelper.get_output("/sbin/ifconfig %s ether | awk '/ether/ { print $2; }'" % iface_name).rstrip()
+
+ @property
+ def ifindex(self):
+ return socket.if_nametoindex(self.name)
+
+ @property
+ def first_ipv6(self):
+ d = self.addr_map["inet6"]
+ return d[next(iter(d))]
+
+ @property
+ def first_ipv4(self):
+ d = self.addr_map["inet"]
+ return d[next(iter(d))]
+
+ def set_vnet(self, vnet_name: str):
+ self.vnet_name = vnet_name
+
+ def set_jailed(self, jailed: bool):
+ self.jailed = jailed
+
+ def run_cmd(self, cmd, verbose=False):
+ if self.vnet_name and not self.jailed:
+ cmd = "/usr/sbin/jexec {} {}".format(self.vnet_name, cmd)
+ return run_cmd(cmd, verbose)
+
+ @classmethod
+ def setup_loopback(cls, vnet_name: str):
+ lo = VnetInterface("", "lo0")
+ lo.set_vnet(vnet_name)
+ lo.setup_addr("127.0.0.1/8")
+ lo.turn_up()
+
+ @classmethod
+ def create_iface(cls, alias_name: str, iface_name: str) -> List["VnetInterface"]:
+ name = run_cmd("/sbin/ifconfig {} create".format(iface_name)).rstrip()
+ if not name:
+ raise Exception("Unable to create iface {}".format(iface_name))
+ if1 = cls(alias_name, name)
+ ret = [if1]
+ if name.startswith("epair"):
+ if2 = cls(alias_name, name[:-1] + "b")
+ if1.epairb = if2
+ ret.append(if2);
+ return ret
+
+ def setup_addr(self, _addr: str):
+ addr = ipaddress.ip_interface(_addr)
+ if addr.version == 6:
+ family = "inet6"
+ cmd = "/sbin/ifconfig {} {} {}".format(self.name, family, addr)
+ else:
+ family = "inet"
+ if self.addr_map[family]:
+ cmd = "/sbin/ifconfig {} alias {}".format(self.name, addr)
+ else:
+ cmd = "/sbin/ifconfig {} {} {}".format(self.name, family, addr)
+ self.run_cmd(cmd)
+ self.addr_map[family][str(addr.ip)] = addr
+
+ def delete_addr(self, _addr: str):
+ addr = ipaddress.ip_address(_addr)
+ if addr.version == 6:
+ family = "inet6"
+ cmd = "/sbin/ifconfig {} inet6 {} delete".format(self.name, addr)
+ else:
+ family = "inet"
+ cmd = "/sbin/ifconfig {} -alias {}".format(self.name, addr)
+ self.run_cmd(cmd)
+ del self.addr_map[family][str(addr)]
+
+ def turn_up(self):
+ cmd = "/sbin/ifconfig {} up".format(self.name)
+ self.run_cmd(cmd)
+
+ def enable_ipv6(self):
+ cmd = "/usr/sbin/ndp -i {} -- -disabled".format(self.name)
+ self.run_cmd(cmd)
+
+ def has_tentative(self) -> bool:
+ """True if an interface has some addresses in tenative state"""
+ cmd = "/sbin/ifconfig {} inet6".format(self.name)
+ out = self.run_cmd(cmd, verbose=False)
+ for line in out.splitlines():
+ if "tentative" in line:
+ return True
+ return False
+
+
+class IfaceFactory(object):
+ INTERFACES_FNAME = "created_ifaces.lst"
+ AUTODELETE_TYPES = ("epair", "gif", "gre", "lo", "tap", "tun")
+
+ def __init__(self):
+ self.file_name = self.INTERFACES_FNAME
+
+ def _register_iface(self, iface_name: str):
+ with open(self.file_name, "a") as f:
+ f.write(iface_name + "\n")
+
+ def _list_ifaces(self) -> List[str]:
+ ret: List[str] = []
+ try:
+ with open(self.file_name, "r") as f:
+ for line in f:
+ ret.append(line.strip())
+ except OSError:
+ pass
+ return ret
+
+ def create_iface(self, alias_name: str, iface_name: str) -> List[VnetInterface]:
+ ifaces = VnetInterface.create_iface(alias_name, iface_name)
+ for iface in ifaces:
+ if not self.is_autodeleted(iface.name):
+ self._register_iface(iface.name)
+ return ifaces
+
+ @staticmethod
+ def is_autodeleted(iface_name: str) -> bool:
+ if iface_name == "lo0":
+ return False
+ iface_type = re.split(r"\d+", iface_name)[0]
+ return iface_type in IfaceFactory.AUTODELETE_TYPES
+
+ def cleanup_vnet_interfaces(self, vnet_name: str) -> List[str]:
+ """Destroys"""
+ ifaces_lst = ToolsHelper.get_output(
+ "/usr/sbin/jexec {} /sbin/ifconfig -l".format(vnet_name)
+ )
+ for iface_name in ifaces_lst.split():
+ if not self.is_autodeleted(iface_name):
+ if iface_name not in self._list_ifaces():
+ print("Skipping interface {}:{}".format(vnet_name, iface_name))
+ continue
+ run_cmd(
+ "/usr/sbin/jexec {} /sbin/ifconfig {} destroy".format(vnet_name, iface_name)
+ )
+
+ def cleanup(self):
+ try:
+ os.unlink(self.INTERFACES_FNAME)
+ except OSError:
+ pass
+
+
+class VnetInstance(object):
+ def __init__(
+ self, vnet_alias: str, vnet_name: str, jid: int, ifaces: List[VnetInterface]
+ ):
+ self.name = vnet_name
+ self.alias = vnet_alias # reference in the test topology
+ self.jid = jid
+ self.ifaces = ifaces
+ self.iface_alias_map = {} # iface.alias: iface
+ self.iface_map = {} # iface.name: iface
+ for iface in ifaces:
+ iface.set_vnet(vnet_name)
+ iface.set_jailed(True)
+ self.iface_alias_map[iface.alias] = iface
+ self.iface_map[iface.name] = iface
+ # Allow reference to interfce aliases as attributes
+ setattr(self, iface.alias, iface)
+ self.need_dad = False # Disable duplicate address detection by default
+ self.attached = False
+ self.pipe = None
+ self.subprocess = None
+
+ def run_vnet_cmd(self, cmd, verbose=True):
+ if not self.attached:
+ cmd = "/usr/sbin/jexec {} {}".format(self.name, cmd)
+ return run_cmd(cmd, verbose)
+
+ def disable_dad(self):
+ self.run_vnet_cmd("/sbin/sysctl net.inet6.ip6.dad_count=0")
+
+ def set_pipe(self, pipe):
+ self.pipe = pipe
+
+ def set_subprocess(self, p):
+ self.subprocess = p
+
+ @staticmethod
+ def attach_jid(jid: int):
+ error_code = libc.jail_attach(jid)
+ if error_code != 0:
+ raise Exception("jail_attach() failed: errno {}".format(error_code))
+
+ def attach(self):
+ self.attach_jid(self.jid)
+ self.attached = True
+
+
+class VnetFactory(object):
+ JAILS_FNAME = "created_jails.lst"
+
+ def __init__(self, topology_id: str):
+ self.topology_id = topology_id
+ self.file_name = self.JAILS_FNAME
+ self._vnets: List[str] = []
+
+ def _register_vnet(self, vnet_name: str):
+ self._vnets.append(vnet_name)
+ with open(self.file_name, "a") as f:
+ f.write(vnet_name + "\n")
+
+ @staticmethod
+ def _wait_interfaces(vnet_name: str, ifaces: List[str]) -> List[str]:
+ cmd = "/usr/sbin/jexec {} /sbin/ifconfig -l".format(vnet_name)
+ not_matched: List[str] = []
+ for i in range(50):
+ vnet_ifaces = run_cmd(cmd).strip().split(" ")
+ not_matched = []
+ for iface_name in ifaces:
+ if iface_name not in vnet_ifaces:
+ not_matched.append(iface_name)
+ if len(not_matched) == 0:
+ return []
+ time.sleep(0.1)
+ return not_matched
+
+ def create_vnet(self, vnet_alias: str, ifaces: List[VnetInterface]):
+ vnet_name = "pytest:{}".format(convert_test_name(self.topology_id))
+ if self._vnets:
+ # add number to distinguish jails
+ vnet_name = "{}_{}".format(vnet_name, len(self._vnets) + 1)
+ iface_cmds = " ".join(["vnet.interface={}".format(i.name) for i in ifaces])
+ cmd = "/usr/sbin/jail -i -c name={} persist vnet {}".format(
+ vnet_name, iface_cmds
+ )
+ jid = 0
+ try:
+ jid_str = run_cmd(cmd)
+ jid = int(jid_str)
+ except ValueError:
+ print("Jail creation failed, output: {}".format(jid_str))
+ raise
+ self._register_vnet(vnet_name)
+
+ # Run expedited version of routing
+ VnetInterface.setup_loopback(vnet_name)
+
+ not_found = self._wait_interfaces(vnet_name, [i.name for i in ifaces])
+ if not_found:
+ raise Exception(
+ "Interfaces {} has not appeared in vnet {}".format(not_found, vnet_name)
+ )
+ return VnetInstance(vnet_alias, vnet_name, jid, ifaces)
+
+ def cleanup(self):
+ iface_factory = IfaceFactory()
+ try:
+ with open(self.file_name) as f:
+ for line in f:
+ vnet_name = line.strip()
+ iface_factory.cleanup_vnet_interfaces(vnet_name)
+ run_cmd("/usr/sbin/jail -r {}".format(vnet_name))
+ os.unlink(self.JAILS_FNAME)
+ except OSError:
+ pass
+
+
+class SingleInterfaceMap(NamedTuple):
+ ifaces: List[VnetInterface]
+ vnet_aliases: List[str]
+
+
+class ObjectsMap(NamedTuple):
+ iface_map: Dict[str, SingleInterfaceMap] # keyed by ifX
+ vnet_map: Dict[str, VnetInstance] # keyed by vnetX
+ topo_map: Dict # self.TOPOLOGY
+
+
+class VnetTestTemplate(BaseTest):
+ NEED_ROOT: bool = True
+ TOPOLOGY = {}
+
+ def _require_default_modules(self):
+ libc.kldload("if_epair.ko")
+ self.require_module("if_epair")
+
+ def _get_vnet_handler(self, vnet_alias: str):
+ handler_name = "{}_handler".format(vnet_alias)
+ return getattr(self, handler_name, None)
+
+ def _setup_vnet(self, vnet: VnetInstance, obj_map: Dict, pipe):
+ """Base Handler to setup given VNET.
+ Can be run in a subprocess. If so, passes control to the special
+ vnetX_handler() after setting up interface addresses
+ """
+ vnet.attach()
+ print("# setup_vnet({})".format(vnet.name))
+ if pipe is not None:
+ vnet.set_pipe(pipe)
+
+ topo = obj_map.topo_map
+ ipv6_ifaces = []
+ # Disable DAD
+ if not vnet.need_dad:
+ vnet.disable_dad()
+ for iface in vnet.ifaces:
+ # check index of vnet within an interface
+ # as we have prefixes for both ends of the interface
+ iface_map = obj_map.iface_map[iface.alias]
+ idx = iface_map.vnet_aliases.index(vnet.alias)
+ prefixes6 = topo[iface.alias].get("prefixes6", [])
+ prefixes4 = topo[iface.alias].get("prefixes4", [])
+ if prefixes6 or prefixes4:
+ ipv6_ifaces.append(iface)
+ iface.turn_up()
+ if prefixes6:
+ iface.enable_ipv6()
+ for prefix in prefixes6 + prefixes4:
+ if prefix[idx]:
+ iface.setup_addr(prefix[idx])
+ for iface in ipv6_ifaces:
+ while iface.has_tentative():
+ time.sleep(0.1)
+
+ # Run actual handler
+ handler = self._get_vnet_handler(vnet.alias)
+ if handler:
+ # Do unbuffered stdout for children
+ # so the logs are present if the child hangs
+ sys.stdout.reconfigure(line_buffering=True)
+ self.drop_privileges()
+ handler(vnet)
+
+ def _get_topo_ifmap(self, topo: Dict):
+ iface_factory = IfaceFactory()
+ iface_map: Dict[str, SingleInterfaceMap] = {}
+ iface_aliases = set()
+ for obj_name, obj_data in topo.items():
+ if obj_name.startswith("vnet"):
+ for iface_alias in obj_data["ifaces"]:
+ iface_aliases.add(iface_alias)
+ for iface_alias in iface_aliases:
+ print("Creating {}".format(iface_alias))
+ iface_data = topo[iface_alias]
+ iface_type = iface_data.get("type", "epair")
+ ifaces = iface_factory.create_iface(iface_alias, iface_type)
+ smap = SingleInterfaceMap(ifaces, [])
+ iface_map[iface_alias] = smap
+ return iface_map
+
+ def setup_topology(self, topo: Dict, topology_id: str):
+ """Creates jails & interfaces for the provided topology"""
+ vnet_map = {}
+ vnet_factory = VnetFactory(topology_id)
+ iface_map = self._get_topo_ifmap(topo)
+ for obj_name, obj_data in topo.items():
+ if obj_name.startswith("vnet"):
+ vnet_ifaces = []
+ for iface_alias in obj_data["ifaces"]:
+ # epair creates 2 interfaces, grab first _available_
+ # and map it to the VNET being created
+ idx = len(iface_map[iface_alias].vnet_aliases)
+ iface_map[iface_alias].vnet_aliases.append(obj_name)
+ vnet_ifaces.append(iface_map[iface_alias].ifaces[idx])
+ vnet = vnet_factory.create_vnet(obj_name, vnet_ifaces)
+ vnet_map[obj_name] = vnet
+ # Allow reference to VNETs as attributes
+ setattr(self, obj_name, vnet)
+ # Debug output
+ print("============= TEST TOPOLOGY =============")
+ for vnet_alias, vnet in vnet_map.items():
+ print("# vnet {} -> {}".format(vnet.alias, vnet.name), end="")
+ handler = self._get_vnet_handler(vnet.alias)
+ if handler:
+ print(" handler: {}".format(handler.__name__), end="")
+ print()
+ for iface_alias, iface_data in iface_map.items():
+ vnets = iface_data.vnet_aliases
+ ifaces: List[VnetInterface] = iface_data.ifaces
+ if len(vnets) == 1 and len(ifaces) == 2:
+ print(
+ "# iface {}: {}::{} -> main::{}".format(
+ iface_alias, vnets[0], ifaces[0].name, ifaces[1].name
+ )
+ )
+ elif len(vnets) == 2 and len(ifaces) == 2:
+ print(
+ "# iface {}: {}::{} -> {}::{}".format(
+ iface_alias, vnets[0], ifaces[0].name, vnets[1], ifaces[1].name
+ )
+ )
+ else:
+ print(
+ "# iface {}: ifaces: {} vnets: {}".format(
+ iface_alias, vnets, [i.name for i in ifaces]
+ )
+ )
+ print()
+ return ObjectsMap(iface_map, vnet_map, topo)
+
+ def setup_method(self, _method):
+ """Sets up all the required topology and handlers for the given test"""
+ super().setup_method(_method)
+ self._require_default_modules()
+
+ # TestIP6Output.test_output6_pktinfo[ipandif]
+ topology_id = get_topology_id(self.test_id)
+ topology = self.TOPOLOGY
+ # First, setup kernel objects - interfaces & vnets
+ obj_map = self.setup_topology(topology, topology_id)
+ main_vnet = None # one without subprocess handler
+ for vnet_alias, vnet in obj_map.vnet_map.items():
+ if self._get_vnet_handler(vnet_alias):
+ # Need subprocess to run
+ parent_pipe, child_pipe = Pipe()
+ p = Process(
+ target=self._setup_vnet,
+ args=(
+ vnet,
+ obj_map,
+ child_pipe,
+ ),
+ )
+ vnet.set_pipe(parent_pipe)
+ vnet.set_subprocess(p)
+ p.start()
+ else:
+ if main_vnet is not None:
+ raise Exception("there can be only 1 VNET w/o handler")
+ main_vnet = vnet
+ # Main vnet needs to be the last, so all the other subprocesses
+ # are started & their pipe handles collected
+ self.vnet = main_vnet
+ self._setup_vnet(main_vnet, obj_map, None)
+ # Save state for the main handler
+ self.iface_map = obj_map.iface_map
+ self.vnet_map = obj_map.vnet_map
+ self.drop_privileges()
+
+ def cleanup(self, test_id: str):
+ # pytest test id: file::class::test_name
+ topology_id = get_topology_id(self.test_id)
+
+ print("============= vnet cleanup =============")
+ print("# topology_id: '{}'".format(topology_id))
+ VnetFactory(topology_id).cleanup()
+ IfaceFactory().cleanup()
+
+ def wait_object(self, pipe, timeout=5):
+ if pipe.poll(timeout):
+ return pipe.recv()
+ raise TimeoutError
+
+ def wait_objects_any(self, pipe_list, timeout=5):
+ objects = connection.wait(pipe_list, timeout)
+ if objects:
+ return objects[0].recv()
+ raise TimeoutError
+
+ def send_object(self, pipe, obj):
+ pipe.send(obj)
+
+ def wait(self):
+ while True:
+ time.sleep(1)
+
+ @property
+ def curvnet(self):
+ pass
+
+
+class SingleVnetTestTemplate(VnetTestTemplate):
+ IPV6_PREFIXES: List[str] = []
+ IPV4_PREFIXES: List[str] = []
+ IFTYPE = "epair"
+
+ def _setup_default_topology(self):
+ topology = copy.deepcopy(
+ {
+ "vnet1": {"ifaces": ["if1"]},
+ "if1": {"type": self.IFTYPE, "prefixes4": [], "prefixes6": []},
+ }
+ )
+ for prefix in self.IPV6_PREFIXES:
+ topology["if1"]["prefixes6"].append((prefix,))
+ for prefix in self.IPV4_PREFIXES:
+ topology["if1"]["prefixes4"].append((prefix,))
+ return topology
+
+ def setup_method(self, method):
+ if not getattr(self, "TOPOLOGY", None):
+ self.TOPOLOGY = self._setup_default_topology()
+ else:
+ names = self.TOPOLOGY.keys()
+ assert len([n for n in names if n.startswith("vnet")]) == 1
+ super().setup_method(method)
diff --git a/tests/atf_python/sys/netlink/Makefile b/tests/atf_python/sys/netlink/Makefile
new file mode 100644
index 000000000000..6a40a93f3ae9
--- /dev/null
+++ b/tests/atf_python/sys/netlink/Makefile
@@ -0,0 +1,13 @@
+.include <src.opts.mk>
+
+.PATH: ${.CURDIR}
+
+PACKAGE=tests
+FILES= __init__.py attrs.py base_headers.py message.py netlink.py \
+ netlink_generic.py netlink_route.py utils.py
+
+.include <bsd.own.mk>
+FILESDIR= ${TESTSBASE}/atf_python/sys/netlink
+
+.include <bsd.prog.mk>
+
diff --git a/tests/atf_python/sys/netlink/__init__.py b/tests/atf_python/sys/netlink/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/tests/atf_python/sys/netlink/__init__.py
diff --git a/tests/atf_python/sys/netlink/attrs.py b/tests/atf_python/sys/netlink/attrs.py
new file mode 100644
index 000000000000..36dd8191df1c
--- /dev/null
+++ b/tests/atf_python/sys/netlink/attrs.py
@@ -0,0 +1,335 @@
+import socket
+import struct
+from enum import Enum
+
+from atf_python.sys.netlink.utils import align4
+from atf_python.sys.netlink.utils import enum_or_int
+
+
+class NlAttr(object):
+ HDR_LEN = 4 # sizeof(struct nlattr)
+
+ def __init__(self, nla_type, data):
+ if isinstance(nla_type, Enum):
+ self._nla_type = nla_type.value
+ self._enum = nla_type
+ else:
+ self._nla_type = nla_type
+ self._enum = None
+ self.nla_list = []
+ self._data = data
+
+ @property
+ def nla_type(self):
+ return self._nla_type & 0x3FFF
+
+ @property
+ def nla_len(self):
+ return len(self._data) + 4
+
+ def add_nla(self, nla):
+ self.nla_list.append(nla)
+
+ def print_attr(self, prepend=""):
+ if self._enum is not None:
+ type_str = self._enum.name
+ else:
+ type_str = "nla#{}".format(self.nla_type)
+ print(
+ "{}len={} type={}({}){}".format(
+ prepend, self.nla_len, type_str, self.nla_type, self._print_attr_value()
+ )
+ )
+
+ @staticmethod
+ def _validate(data):
+ if len(data) < 4:
+ raise ValueError("attribute too short")
+ nla_len, nla_type = struct.unpack("@HH", data[:4])
+ if nla_len > len(data):
+ raise ValueError("attribute length too big")
+ if nla_len < 4:
+ raise ValueError("attribute length too short")
+
+ @classmethod
+ def _parse(cls, data):
+ nla_len, nla_type = struct.unpack("@HH", data[:4])
+ return cls(nla_type, data[4:])
+
+ @classmethod
+ def from_bytes(cls, data, attr_type_enum=None):
+ cls._validate(data)
+ attr = cls._parse(data)
+ attr._enum = attr_type_enum
+ return attr
+
+ def _to_bytes(self, data: bytes):
+ ret = data
+ if align4(len(ret)) != len(ret):
+ ret = data + bytes(align4(len(ret)) - len(ret))
+ return struct.pack("@HH", len(data) + 4, self._nla_type) + ret
+
+ def __bytes__(self):
+ return self._to_bytes(self._data)
+
+ def _print_attr_value(self):
+ return " " + " ".join(["x{:02X}".format(b) for b in self._data])
+
+
+class NlAttrNested(NlAttr):
+ def __init__(self, nla_type, val):
+ super().__init__(nla_type, b"")
+ self.nla_list = val
+
+ def get_nla(self, nla_type):
+ nla_type_raw = enum_or_int(nla_type)
+ for nla in self.nla_list:
+ if nla.nla_type == nla_type_raw:
+ return nla
+ return None
+
+ @property
+ def nla_len(self):
+ return align4(len(b"".join([bytes(nla) for nla in self.nla_list]))) + 4
+
+ def print_attr(self, prepend=""):
+ if self._enum is not None:
+ type_str = self._enum.name
+ else:
+ type_str = "nla#{}".format(self.nla_type)
+ print(
+ "{}len={} type={}({}) {{".format(
+ prepend, self.nla_len, type_str, self.nla_type
+ )
+ )
+ for nla in self.nla_list:
+ nla.print_attr(prepend + " ")
+ print("{}}}".format(prepend))
+
+ def __bytes__(self):
+ return self._to_bytes(b"".join([bytes(nla) for nla in self.nla_list]))
+
+
+class NlAttrU32(NlAttr):
+ def __init__(self, nla_type, val):
+ self.u32 = enum_or_int(val)
+ super().__init__(nla_type, b"")
+
+ @property
+ def nla_len(self):
+ return 8
+
+ def _print_attr_value(self):
+ return " val={}".format(self.u32)
+
+ @staticmethod
+ def _validate(data):
+ assert len(data) == 8
+ nla_len, nla_type = struct.unpack("@HH", data[:4])
+ assert nla_len == 8
+
+ @classmethod
+ def _parse(cls, data):
+ nla_len, nla_type, val = struct.unpack("@HHI", data)
+ return cls(nla_type, val)
+
+ def __bytes__(self):
+ return self._to_bytes(struct.pack("@I", self.u32))
+
+
+class NlAttrS32(NlAttr):
+ def __init__(self, nla_type, val):
+ self.s32 = enum_or_int(val)
+ super().__init__(nla_type, b"")
+
+ @property
+ def nla_len(self):
+ return 8
+
+ def _print_attr_value(self):
+ return " val={}".format(self.s32)
+
+ @staticmethod
+ def _validate(data):
+ assert len(data) == 8
+ nla_len, nla_type = struct.unpack("@HH", data[:4])
+ assert nla_len == 8
+
+ @classmethod
+ def _parse(cls, data):
+ nla_len, nla_type, val = struct.unpack("@HHi", data)
+ return cls(nla_type, val)
+
+ def __bytes__(self):
+ return self._to_bytes(struct.pack("@i", self.s32))
+
+
+class NlAttrU16(NlAttr):
+ def __init__(self, nla_type, val):
+ self.u16 = enum_or_int(val)
+ super().__init__(nla_type, b"")
+
+ @property
+ def nla_len(self):
+ return 6
+
+ def _print_attr_value(self):
+ return " val={}".format(self.u16)
+
+ @staticmethod
+ def _validate(data):
+ assert len(data) == 6
+ nla_len, nla_type = struct.unpack("@HH", data[:4])
+ assert nla_len == 6
+
+ @classmethod
+ def _parse(cls, data):
+ nla_len, nla_type, val = struct.unpack("@HHH", data)
+ return cls(nla_type, val)
+
+ def __bytes__(self):
+ return self._to_bytes(struct.pack("@H", self.u16))
+
+
+class NlAttrU8(NlAttr):
+ def __init__(self, nla_type, val):
+ self.u8 = enum_or_int(val)
+ super().__init__(nla_type, b"")
+
+ @property
+ def nla_len(self):
+ return 5
+
+ def _print_attr_value(self):
+ return " val={}".format(self.u8)
+
+ @staticmethod
+ def _validate(data):
+ assert len(data) == 5
+ nla_len, nla_type = struct.unpack("@HH", data[:4])
+ assert nla_len == 5
+
+ @classmethod
+ def _parse(cls, data):
+ nla_len, nla_type, val = struct.unpack("@HHB", data)
+ return cls(nla_type, val)
+
+ def __bytes__(self):
+ return self._to_bytes(struct.pack("@B", self.u8))
+
+
+class NlAttrIp(NlAttr):
+ def __init__(self, nla_type, addr: str):
+ super().__init__(nla_type, b"")
+ self.addr = addr
+ if ":" in self.addr:
+ self.family = socket.AF_INET6
+ else:
+ self.family = socket.AF_INET
+
+ @staticmethod
+ def _validate(data):
+ nla_len, nla_type = struct.unpack("@HH", data[:4])
+ data_len = nla_len - 4
+ if data_len != 4 and data_len != 16:
+ raise ValueError(
+ "Error validating attr {}: nla_len is not valid".format( # noqa: E501
+ nla_type
+ )
+ )
+
+ @property
+ def nla_len(self):
+ if self.family == socket.AF_INET6:
+ return 20
+ else:
+ return 8
+ return align4(len(self._data)) + 4
+
+ @classmethod
+ def _parse(cls, data):
+ nla_len, nla_type = struct.unpack("@HH", data[:4])
+ data_len = len(data) - 4
+ if data_len == 4:
+ addr = socket.inet_ntop(socket.AF_INET, data[4:8])
+ else:
+ addr = socket.inet_ntop(socket.AF_INET6, data[4:20])
+ return cls(nla_type, addr)
+
+ def __bytes__(self):
+ return self._to_bytes(socket.inet_pton(self.family, self.addr))
+
+ def _print_attr_value(self):
+ return " addr={}".format(self.addr)
+
+
+class NlAttrIp4(NlAttrIp):
+ def __init__(self, nla_type, addr: str):
+ super().__init__(nla_type, addr)
+ assert self.family == socket.AF_INET
+
+
+class NlAttrIp6(NlAttrIp):
+ def __init__(self, nla_type, addr: str):
+ super().__init__(nla_type, addr)
+ assert self.family == socket.AF_INET6
+
+
+class NlAttrStr(NlAttr):
+ def __init__(self, nla_type, text):
+ super().__init__(nla_type, b"")
+ self.text = text
+
+ @staticmethod
+ def _validate(data):
+ NlAttr._validate(data)
+ try:
+ data[4:].decode("utf-8")
+ except Exception as e:
+ raise ValueError("wrong utf-8 string: {}".format(e))
+
+ @property
+ def nla_len(self):
+ return len(self.text) + 5
+
+ @classmethod
+ def _parse(cls, data):
+ text = data[4:-1].decode("utf-8")
+ nla_len, nla_type = struct.unpack("@HH", data[:4])
+ return cls(nla_type, text)
+
+ def __bytes__(self):
+ return self._to_bytes(bytes(self.text, encoding="utf-8") + bytes(1))
+
+ def _print_attr_value(self):
+ return ' val="{}"'.format(self.text)
+
+
+class NlAttrStrn(NlAttr):
+ def __init__(self, nla_type, text):
+ super().__init__(nla_type, b"")
+ self.text = text
+
+ @staticmethod
+ def _validate(data):
+ NlAttr._validate(data)
+ try:
+ data[4:].decode("utf-8")
+ except Exception as e:
+ raise ValueError("wrong utf-8 string: {}".format(e))
+
+ @property
+ def nla_len(self):
+ return len(self.text) + 4
+
+ @classmethod
+ def _parse(cls, data):
+ text = data[4:].decode("utf-8")
+ nla_len, nla_type = struct.unpack("@HH", data[:4])
+ return cls(nla_type, text)
+
+ def __bytes__(self):
+ return self._to_bytes(bytes(self.text, encoding="utf-8"))
+
+ def _print_attr_value(self):
+ return ' val="{}"'.format(self.text)
diff --git a/tests/atf_python/sys/netlink/base_headers.py b/tests/atf_python/sys/netlink/base_headers.py
new file mode 100644
index 000000000000..71771a249b3d
--- /dev/null
+++ b/tests/atf_python/sys/netlink/base_headers.py
@@ -0,0 +1,72 @@
+from ctypes import c_ubyte
+from ctypes import c_uint
+from ctypes import c_ushort
+from ctypes import Structure
+from enum import Enum
+
+
+class Nlmsghdr(Structure):
+ _fields_ = [
+ ("nlmsg_len", c_uint),
+ ("nlmsg_type", c_ushort),
+ ("nlmsg_flags", c_ushort),
+ ("nlmsg_seq", c_uint),
+ ("nlmsg_pid", c_uint),
+ ]
+
+
+class Nlattr(Structure):
+ _fields_ = [
+ ("nla_len", c_ushort),
+ ("nla_type", c_ushort),
+ ]
+
+
+class NlMsgType(Enum):
+ NLMSG_NOOP = 1
+ NLMSG_ERROR = 2
+ NLMSG_DONE = 3
+ NLMSG_OVERRUN = 4
+
+
+class NlmBaseFlags(Enum):
+ NLM_F_REQUEST = 0x01
+ NLM_F_MULTI = 0x02
+ NLM_F_ACK = 0x04
+ NLM_F_ECHO = 0x08
+ NLM_F_DUMP_INTR = 0x10
+ NLM_F_DUMP_FILTERED = 0x20
+
+
+# XXX: in python3.8 it is possible to
+# class NlmGetFlags(Enum, NlmBaseFlags):
+
+
+class NlmGetFlags(Enum):
+ NLM_F_ROOT = 0x100
+ NLM_F_MATCH = 0x200
+ NLM_F_ATOMIC = 0x400
+
+
+class NlmNewFlags(Enum):
+ NLM_F_REPLACE = 0x100
+ NLM_F_EXCL = 0x200
+ NLM_F_CREATE = 0x400
+ NLM_F_APPEND = 0x800
+
+
+class NlmDeleteFlags(Enum):
+ NLM_F_NONREC = 0x100
+
+
+class NlmAckFlags(Enum):
+ NLM_F_CAPPED = 0x100
+ NLM_F_ACK_TLVS = 0x200
+
+
+class GenlMsgHdr(Structure):
+ _fields_ = [
+ ("cmd", c_ubyte),
+ ("version", c_ubyte),
+ ("reserved", c_ushort),
+ ]
diff --git a/tests/atf_python/sys/netlink/message.py b/tests/atf_python/sys/netlink/message.py
new file mode 100644
index 000000000000..98a1e3bb21c5
--- /dev/null
+++ b/tests/atf_python/sys/netlink/message.py
@@ -0,0 +1,286 @@
+#!/usr/local/bin/python3
+import struct
+from ctypes import sizeof
+from enum import Enum
+from typing import List
+from typing import NamedTuple
+
+from atf_python.sys.netlink.attrs import NlAttr
+from atf_python.sys.netlink.attrs import NlAttrNested
+from atf_python.sys.netlink.base_headers import NlmAckFlags
+from atf_python.sys.netlink.base_headers import NlmNewFlags
+from atf_python.sys.netlink.base_headers import NlmGetFlags
+from atf_python.sys.netlink.base_headers import NlmDeleteFlags
+from atf_python.sys.netlink.base_headers import NlmBaseFlags
+from atf_python.sys.netlink.base_headers import Nlmsghdr
+from atf_python.sys.netlink.base_headers import NlMsgType
+from atf_python.sys.netlink.utils import align4
+from atf_python.sys.netlink.utils import enum_or_int
+from atf_python.sys.netlink.utils import get_bitmask_str
+
+
+class NlMsgCategory(Enum):
+ UNKNOWN = 0
+ GET = 1
+ NEW = 2
+ DELETE = 3
+ ACK = 4
+
+
+class NlMsgProps(NamedTuple):
+ msg: Enum
+ category: NlMsgCategory
+
+
+class BaseNetlinkMessage(object):
+ def __init__(self, helper, nlmsg_type):
+ self.nlmsg_type = enum_or_int(nlmsg_type)
+ self.nla_list = []
+ self._orig_data = None
+ self.helper = helper
+ self.nl_hdr = Nlmsghdr(
+ nlmsg_type=self.nlmsg_type, nlmsg_seq=helper.get_seq(), nlmsg_pid=helper.pid
+ )
+ self.base_hdr = None
+
+ def set_request(self, need_ack=True):
+ self.add_nlflags([NlmBaseFlags.NLM_F_REQUEST])
+ if need_ack:
+ self.add_nlflags([NlmBaseFlags.NLM_F_ACK])
+
+ def add_nlflags(self, flags: List):
+ int_flags = 0
+ for flag in flags:
+ int_flags |= enum_or_int(flag)
+ self.nl_hdr.nlmsg_flags |= int_flags
+
+ def add_nla(self, nla):
+ self.nla_list.append(nla)
+
+ def _get_nla(self, nla_list, nla_type):
+ nla_type_raw = enum_or_int(nla_type)
+ for nla in nla_list:
+ if nla.nla_type == nla_type_raw:
+ return nla
+ return None
+
+ def get_nla(self, nla_type):
+ return self._get_nla(self.nla_list, nla_type)
+
+ @staticmethod
+ def parse_nl_header(data: bytes):
+ if len(data) < sizeof(Nlmsghdr):
+ raise ValueError("length less than netlink message header")
+ return Nlmsghdr.from_buffer_copy(data), sizeof(Nlmsghdr)
+
+ def is_type(self, nlmsg_type):
+ nlmsg_type_raw = enum_or_int(nlmsg_type)
+ return nlmsg_type_raw == self.nl_hdr.nlmsg_type
+
+ def is_reply(self, hdr):
+ return hdr.nlmsg_type == NlMsgType.NLMSG_ERROR.value
+
+ @property
+ def msg_name(self):
+ return "msg#{}".format(self._get_msg_type())
+
+ def _get_nl_category(self):
+ if self.is_reply(self.nl_hdr):
+ return NlMsgCategory.ACK
+ return NlMsgCategory.UNKNOWN
+
+ def get_nlm_flags_str(self):
+ category = self._get_nl_category()
+ flags = self.nl_hdr.nlmsg_flags
+
+ if category == NlMsgCategory.UNKNOWN:
+ return self.helper.get_bitmask_str(NlmBaseFlags, flags)
+ elif category == NlMsgCategory.GET:
+ flags_enum = NlmGetFlags
+ elif category == NlMsgCategory.NEW:
+ flags_enum = NlmNewFlags
+ elif category == NlMsgCategory.DELETE:
+ flags_enum = NlmDeleteFlags
+ elif category == NlMsgCategory.ACK:
+ flags_enum = NlmAckFlags
+ return get_bitmask_str([NlmBaseFlags, flags_enum], flags)
+
+ def print_nl_header(self, prepend=""):
+ # len=44, type=RTM_DELROUTE, flags=NLM_F_REQUEST|NLM_F_ACK, seq=1641163704, pid=0 # noqa: E501
+ hdr = self.nl_hdr
+ print(
+ "{}len={}, type={}, flags={}(0x{:X}), seq={}, pid={}".format(
+ prepend,
+ hdr.nlmsg_len,
+ self.msg_name,
+ self.get_nlm_flags_str(),
+ hdr.nlmsg_flags,
+ hdr.nlmsg_seq,
+ hdr.nlmsg_pid,
+ )
+ )
+
+ @classmethod
+ def from_bytes(cls, helper, data):
+ try:
+ hdr, hdrlen = BaseNetlinkMessage.parse_nl_header(data)
+ self = cls(helper, hdr.nlmsg_type)
+ self._orig_data = data
+ self.nl_hdr = hdr
+ except ValueError as e:
+ print("Failed to parse nl header: {}".format(e))
+ cls.print_as_bytes(data)
+ raise
+ return self
+
+ def print_message(self):
+ self.print_nl_header()
+
+ @staticmethod
+ def print_as_bytes(data: bytes, descr: str):
+ print("===vv {} (len:{:3d}) vv===".format(descr, len(data)))
+ off = 0
+ step = 16
+ while off < len(data):
+ for i in range(step):
+ if off + i < len(data):
+ print(" {:02X}".format(data[off + i]), end="")
+ print("")
+ off += step
+ print("--------------------")
+
+
+class StdNetlinkMessage(BaseNetlinkMessage):
+ nl_attrs_map = {}
+
+ @classmethod
+ def from_bytes(cls, helper, data):
+ try:
+ hdr, hdrlen = BaseNetlinkMessage.parse_nl_header(data)
+ self = cls(helper, hdr.nlmsg_type)
+ self._orig_data = data
+ self.nl_hdr = hdr
+ except ValueError as e:
+ print("Failed to parse nl header: {}".format(e))
+ cls.print_as_bytes(data)
+ raise
+
+ offset = align4(hdrlen)
+ try:
+ base_hdr, hdrlen = self.parse_base_header(data[offset:])
+ self.base_hdr = base_hdr
+ offset += align4(hdrlen)
+ # XXX: CAP_ACK
+ except ValueError as e:
+ print("Failed to parse nl rt header: {}".format(e))
+ cls.print_as_bytes(data)
+ raise
+
+ orig_offset = offset
+ try:
+ nla_list, nla_len = self.parse_nla_list(data[offset:])
+ offset += nla_len
+ if offset != len(data):
+ raise ValueError(
+ "{} bytes left at the end of the packet".format(len(data) - offset)
+ ) # noqa: E501
+ self.nla_list = nla_list
+ except ValueError as e:
+ print(
+ "Failed to parse nla attributes at offset {}: {}".format(orig_offset, e)
+ ) # noqa: E501
+ cls.print_as_bytes(data, "msg dump")
+ cls.print_as_bytes(data[orig_offset:], "failed block")
+ raise
+ return self
+
+ def parse_child(self, data: bytes, attr_key, attr_map):
+ attrs, _ = self.parse_attrs(data, attr_map)
+ return NlAttrNested(attr_key, attrs)
+
+ def parse_child_array(self, data: bytes, attr_key, attr_map):
+ ret = []
+ off = 0
+ while len(data) - off >= 4:
+ nla_len, raw_nla_type = struct.unpack("@HH", data[off : off + 4])
+ if nla_len + off > len(data):
+ raise ValueError(
+ "attr length {} > than the remaining length {}".format(
+ nla_len, len(data) - off
+ )
+ )
+ nla_type = raw_nla_type & 0x3FFF
+ val = self.parse_child(data[off + 4 : off + nla_len], nla_type, attr_map)
+ ret.append(val)
+ off += align4(nla_len)
+ return NlAttrNested(attr_key, ret)
+
+ def parse_attrs(self, data: bytes, attr_map):
+ ret = []
+ off = 0
+ while len(data) - off >= 4:
+ nla_len, raw_nla_type = struct.unpack("@HH", data[off : off + 4])
+ if nla_len + off > len(data):
+ raise ValueError(
+ "attr length {} > than the remaining length {}".format(
+ nla_len, len(data) - off
+ )
+ )
+ nla_type = raw_nla_type & 0x3FFF
+ if nla_type in attr_map:
+ v = attr_map[nla_type]
+ val = v["ad"].cls.from_bytes(data[off : off + nla_len], v["ad"].val)
+ if "child" in v:
+ # nested
+ child_data = data[off + 4 : off + nla_len]
+ if v.get("is_array", False):
+ # Array of nested attributes
+ val = self.parse_child_array(
+ child_data, v["ad"].val, v["child"]
+ )
+ else:
+ val = self.parse_child(child_data, v["ad"].val, v["child"])
+ else:
+ # unknown attribute
+ val = NlAttr(raw_nla_type, data[off + 4 : off + nla_len])
+ ret.append(val)
+ off += align4(nla_len)
+ return ret, off
+
+ def parse_nla_list(self, data: bytes) -> List[NlAttr]:
+ return self.parse_attrs(data, self.nl_attrs_map)
+
+ def __bytes__(self):
+ ret = bytes()
+ for nla in self.nla_list:
+ ret += bytes(nla)
+ ret = bytes(self.base_hdr) + ret
+ self.nl_hdr.nlmsg_len = len(ret) + sizeof(Nlmsghdr)
+ return bytes(self.nl_hdr) + ret
+
+ def _get_msg_type(self):
+ return self.nl_hdr.nlmsg_type
+
+ @property
+ def msg_props(self):
+ msg_type = self._get_msg_type()
+ for msg_props in self.messages:
+ if msg_props.msg.value == msg_type:
+ return msg_props
+ return None
+
+ @property
+ def msg_name(self):
+ msg_props = self.msg_props
+ if msg_props is not None:
+ return msg_props.msg.name
+ return super().msg_name
+
+ def print_base_header(self, hdr, prepend=""):
+ pass
+
+ def print_message(self):
+ self.print_nl_header()
+ self.print_base_header(self.base_hdr, " ")
+ for nla in self.nla_list:
+ nla.print_attr(" ")
diff --git a/tests/atf_python/sys/netlink/netlink.py b/tests/atf_python/sys/netlink/netlink.py
new file mode 100644
index 000000000000..f8f886b09b24
--- /dev/null
+++ b/tests/atf_python/sys/netlink/netlink.py
@@ -0,0 +1,417 @@
+#!/usr/local/bin/python3
+import os
+import socket
+import sys
+from ctypes import c_int
+from ctypes import c_ubyte
+from ctypes import c_uint
+from ctypes import c_ushort
+from ctypes import sizeof
+from ctypes import Structure
+from enum import auto
+from enum import Enum
+
+from atf_python.sys.netlink.attrs import NlAttr
+from atf_python.sys.netlink.attrs import NlAttrStr
+from atf_python.sys.netlink.attrs import NlAttrU32
+from atf_python.sys.netlink.base_headers import GenlMsgHdr
+from atf_python.sys.netlink.base_headers import NlmBaseFlags
+from atf_python.sys.netlink.base_headers import Nlmsghdr
+from atf_python.sys.netlink.base_headers import NlMsgType
+from atf_python.sys.netlink.message import BaseNetlinkMessage
+from atf_python.sys.netlink.message import NlMsgCategory
+from atf_python.sys.netlink.message import NlMsgProps
+from atf_python.sys.netlink.message import StdNetlinkMessage
+from atf_python.sys.netlink.netlink_generic import GenlCtrlAttrType
+from atf_python.sys.netlink.netlink_generic import GenlCtrlMsgType
+from atf_python.sys.netlink.netlink_generic import handler_classes as genl_classes
+from atf_python.sys.netlink.netlink_route import handler_classes as rt_classes
+from atf_python.sys.netlink.utils import align4
+from atf_python.sys.netlink.utils import AttrDescr
+from atf_python.sys.netlink.utils import build_propmap
+from atf_python.sys.netlink.utils import enum_or_int
+from atf_python.sys.netlink.utils import get_bitmask_map
+from atf_python.sys.netlink.utils import NlConst
+from atf_python.sys.netlink.utils import prepare_attrs_map
+
+
+class SockaddrNl(Structure):
+ _fields_ = [
+ ("nl_len", c_ubyte),
+ ("nl_family", c_ubyte),
+ ("nl_pad", c_ushort),
+ ("nl_pid", c_uint),
+ ("nl_groups", c_uint),
+ ]
+
+
+class Nlmsgdone(Structure):
+ _fields_ = [
+ ("error", c_int),
+ ]
+
+
+class Nlmsgerr(Structure):
+ _fields_ = [
+ ("error", c_int),
+ ("msg", Nlmsghdr),
+ ]
+
+
+class NlErrattrType(Enum):
+ NLMSGERR_ATTR_UNUSED = 0
+ NLMSGERR_ATTR_MSG = auto()
+ NLMSGERR_ATTR_OFFS = auto()
+ NLMSGERR_ATTR_COOKIE = auto()
+ NLMSGERR_ATTR_POLICY = auto()
+
+
+class AddressFamilyLinux(Enum):
+ AF_INET = socket.AF_INET
+ AF_INET6 = socket.AF_INET6
+ AF_NETLINK = 16
+
+
+class AddressFamilyBsd(Enum):
+ AF_INET = socket.AF_INET
+ AF_INET6 = socket.AF_INET6
+ AF_NETLINK = 38
+
+
+class NlHelper:
+ def __init__(self):
+ self._pmap = {}
+ self._af_cls = self.get_af_cls()
+ self._seq_counter = 1
+ self.pid = os.getpid()
+
+ def get_seq(self):
+ ret = self._seq_counter
+ self._seq_counter += 1
+ return ret
+
+ def get_af_cls(self):
+ if sys.platform.startswith("freebsd"):
+ cls = AddressFamilyBsd
+ else:
+ cls = AddressFamilyLinux
+ return cls
+
+ def get_propmap(self, cls):
+ if cls not in self._pmap:
+ self._pmap[cls] = build_propmap(cls)
+ return self._pmap[cls]
+
+ def get_name_propmap(self, cls):
+ ret = {}
+ for prop in dir(cls):
+ if not prop.startswith("_"):
+ ret[prop] = getattr(cls, prop).value
+ return ret
+
+ def get_attr_byval(self, cls, attr_val):
+ propmap = self.get_propmap(cls)
+ return propmap.get(attr_val)
+
+ def get_af_name(self, family):
+ v = self.get_attr_byval(self._af_cls, family)
+ if v is not None:
+ return v
+ return "af#{}".format(family)
+
+ def get_af_value(self, family_str: str) -> int:
+ propmap = self.get_name_propmap(self._af_cls)
+ return propmap.get(family_str)
+
+ def get_bitmask_str(self, cls, val):
+ bmap = get_bitmask_map(self.get_propmap(cls), val)
+ return ",".join([v for k, v in bmap.items()])
+
+ @staticmethod
+ def get_bitmask_str_uncached(cls, val):
+ pmap = NlHelper.build_propmap(cls)
+ bmap = NlHelper.get_bitmask_map(pmap, val)
+ return ",".join([v for k, v in bmap.items()])
+
+
+nldone_attrs = prepare_attrs_map([])
+
+nlerr_attrs = prepare_attrs_map(
+ [
+ AttrDescr(NlErrattrType.NLMSGERR_ATTR_MSG, NlAttrStr),
+ AttrDescr(NlErrattrType.NLMSGERR_ATTR_OFFS, NlAttrU32),
+ AttrDescr(NlErrattrType.NLMSGERR_ATTR_COOKIE, NlAttr),
+ ]
+)
+
+
+class NetlinkDoneMessage(StdNetlinkMessage):
+ messages = [NlMsgProps(NlMsgType.NLMSG_DONE, NlMsgCategory.ACK)]
+ nl_attrs_map = nldone_attrs
+
+ @property
+ def error_code(self):
+ return self.base_hdr.error
+
+ def parse_base_header(self, data):
+ if len(data) < sizeof(Nlmsgdone):
+ raise ValueError("length less than nlmsgdone header")
+ done_hdr = Nlmsgdone.from_buffer_copy(data)
+ sz = sizeof(Nlmsgdone)
+ return (done_hdr, sz)
+
+ def print_base_header(self, hdr, prepend=""):
+ print("{}error={}".format(prepend, hdr.error))
+
+
+class NetlinkErrorMessage(StdNetlinkMessage):
+ messages = [NlMsgProps(NlMsgType.NLMSG_ERROR, NlMsgCategory.ACK)]
+ nl_attrs_map = nlerr_attrs
+
+ @property
+ def error_code(self):
+ return self.base_hdr.error
+
+ @property
+ def error_str(self):
+ nla = self.get_nla(NlErrattrType.NLMSGERR_ATTR_MSG)
+ if nla:
+ return nla.text
+ return None
+
+ @property
+ def error_offset(self):
+ nla = self.get_nla(NlErrattrType.NLMSGERR_ATTR_OFFS)
+ if nla:
+ return nla.u32
+ return None
+
+ @property
+ def cookie(self):
+ return self.get_nla(NlErrattrType.NLMSGERR_ATTR_COOKIE)
+
+ def parse_base_header(self, data):
+ if len(data) < sizeof(Nlmsgerr):
+ raise ValueError("length less than nlmsgerr header")
+ err_hdr = Nlmsgerr.from_buffer_copy(data)
+ sz = sizeof(Nlmsgerr)
+ if (self.nl_hdr.nlmsg_flags & 0x100) == 0:
+ sz += align4(err_hdr.msg.nlmsg_len - sizeof(Nlmsghdr))
+ return (err_hdr, sz)
+
+ def print_base_header(self, errhdr, prepend=""):
+ print("{}error={}, ".format(prepend, errhdr.error), end="")
+ hdr = errhdr.msg
+ print(
+ "{}len={}, type={}, flags={}(0x{:X}), seq={}, pid={}".format(
+ prepend,
+ hdr.nlmsg_len,
+ "msg#{}".format(hdr.nlmsg_type),
+ self.helper.get_bitmask_str(NlmBaseFlags, hdr.nlmsg_flags),
+ hdr.nlmsg_flags,
+ hdr.nlmsg_seq,
+ hdr.nlmsg_pid,
+ )
+ )
+
+
+core_classes = {
+ "netlink_core": [
+ NetlinkDoneMessage,
+ NetlinkErrorMessage,
+ ],
+}
+
+
+class Nlsock:
+ HANDLER_CLASSES = [core_classes, rt_classes, genl_classes]
+
+ def __init__(self, family, helper):
+ self.helper = helper
+ self.sock_fd = self._setup_netlink(family)
+ self._sock_family = family
+ self._data = bytes()
+ self.msgmap = self.build_msgmap()
+ self._family_map = {
+ NlConst.GENL_ID_CTRL: "nlctrl",
+ }
+
+ def build_msgmap(self):
+ handler_classes = {}
+ for d in self.HANDLER_CLASSES:
+ handler_classes.update(d)
+ xmap = {}
+ # 'family_name': [class.messages[MsgProps.msg], ]
+ for family_id, family_classes in handler_classes.items():
+ xmap[family_id] = {}
+ for cls in family_classes:
+ for msg_props in cls.messages:
+ xmap[family_id][enum_or_int(msg_props.msg)] = cls
+ return xmap
+
+ def _setup_netlink(self, netlink_family) -> int:
+ family = self.helper.get_af_value("AF_NETLINK")
+ s = socket.socket(family, socket.SOCK_RAW, netlink_family)
+ s.setsockopt(270, 10, 1) # NETLINK_CAP_ACK
+ s.setsockopt(270, 11, 1) # NETLINK_EXT_ACK
+ return s
+
+ def set_groups(self, mask: int):
+ self.sock_fd.setsockopt(socket.SOL_SOCKET, 1, mask)
+ # snl = SockaddrNl(nl_len = sizeof(SockaddrNl), nl_family=38,
+ # nl_pid=self.pid, nl_groups=mask)
+ # xbuffer = create_string_buffer(sizeof(SockaddrNl))
+ # memmove(xbuffer, addressof(snl), sizeof(SockaddrNl))
+ # k = struct.pack("@BBHII", 12, 38, 0, self.pid, mask)
+ # self.sock_fd.bind(k)
+
+ def join_group(self, group_id: int):
+ self.sock_fd.setsockopt(270, 1, group_id)
+
+ def write_message(self, msg, verbose=True):
+ if verbose:
+ print("vvvvvvvv OUT vvvvvvvv")
+ msg.print_message()
+ msg_bytes = bytes(msg)
+ try:
+ ret = os.write(self.sock_fd.fileno(), msg_bytes)
+ assert ret == len(msg_bytes)
+ except Exception as e:
+ print("write({}) -> {}".format(len(msg_bytes), e))
+
+ def parse_message(self, data: bytes):
+ if len(data) < sizeof(Nlmsghdr):
+ raise Exception("Short read from nl: {} bytes".format(len(data)))
+ hdr = Nlmsghdr.from_buffer_copy(data)
+ if hdr.nlmsg_type < 16:
+ family_name = "netlink_core"
+ nlmsg_type = hdr.nlmsg_type
+ elif self._sock_family == NlConst.NETLINK_ROUTE:
+ family_name = "netlink_route"
+ nlmsg_type = hdr.nlmsg_type
+ else:
+ # Genetlink
+ if len(data) < sizeof(Nlmsghdr) + sizeof(GenlMsgHdr):
+ raise Exception("Short read from genl: {} bytes".format(len(data)))
+ family_name = self._family_map.get(hdr.nlmsg_type, "")
+ ghdr = GenlMsgHdr.from_buffer_copy(data[sizeof(Nlmsghdr):])
+ nlmsg_type = ghdr.cmd
+ cls = self.msgmap.get(family_name, {}).get(nlmsg_type)
+ if not cls:
+ cls = BaseNetlinkMessage
+ return cls.from_bytes(self.helper, data)
+
+ def get_genl_family_id(self, family_name):
+ hdr = Nlmsghdr(
+ nlmsg_type=NlConst.GENL_ID_CTRL,
+ nlmsg_flags=NlmBaseFlags.NLM_F_REQUEST.value,
+ nlmsg_seq=self.helper.get_seq(),
+ )
+ ghdr = GenlMsgHdr(cmd=GenlCtrlMsgType.CTRL_CMD_GETFAMILY.value)
+ nla = NlAttrStr(GenlCtrlAttrType.CTRL_ATTR_FAMILY_NAME, family_name)
+ hdr.nlmsg_len = sizeof(Nlmsghdr) + sizeof(GenlMsgHdr) + len(bytes(nla))
+
+ msg_bytes = bytes(hdr) + bytes(ghdr) + bytes(nla)
+ self.write_data(msg_bytes)
+ while True:
+ rx_msg = self.read_message()
+ if hdr.nlmsg_seq == rx_msg.nl_hdr.nlmsg_seq:
+ if rx_msg.is_type(NlMsgType.NLMSG_ERROR):
+ if rx_msg.error_code != 0:
+ raise ValueError("unable to get family {}".format(family_name))
+ else:
+ family_id = rx_msg.get_nla(GenlCtrlAttrType.CTRL_ATTR_FAMILY_ID).u16
+ self._family_map[family_id] = family_name
+ return family_id
+ raise ValueError("unable to get family {}".format(family_name))
+
+ def write_data(self, data: bytes):
+ self.sock_fd.send(data)
+
+ def read_data(self):
+ while True:
+ data = self.sock_fd.recv(65535)
+ self._data += data
+ if len(self._data) >= sizeof(Nlmsghdr):
+ break
+
+ def read_message(self) -> bytes:
+ if len(self._data) < sizeof(Nlmsghdr):
+ self.read_data()
+ hdr = Nlmsghdr.from_buffer_copy(self._data)
+ while hdr.nlmsg_len > len(self._data):
+ self.read_data()
+ raw_msg = self._data[: hdr.nlmsg_len]
+ self._data = self._data[hdr.nlmsg_len:]
+ return self.parse_message(raw_msg)
+
+ def get_reply(self, tx_msg):
+ self.write_message(tx_msg)
+ while True:
+ rx_msg = self.read_message()
+ if tx_msg.nl_hdr.nlmsg_seq == rx_msg.nl_hdr.nlmsg_seq:
+ return rx_msg
+
+
+class NetlinkMultipartIterator(object):
+ def __init__(self, obj, seq_number: int, msg_type):
+ self._obj = obj
+ self._seq = seq_number
+ self._msg_type = msg_type
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ msg = self._obj.read_message()
+ if self._seq != msg.nl_hdr.nlmsg_seq:
+ raise ValueError("bad sequence number")
+ if msg.is_type(NlMsgType.NLMSG_ERROR):
+ raise ValueError(
+ "error while handling multipart msg: {}".format(msg.error_code)
+ )
+ elif msg.is_type(NlMsgType.NLMSG_DONE):
+ if msg.error_code == 0:
+ raise StopIteration
+ raise ValueError(
+ "error listing some parts of the multipart msg: {}".format(
+ msg.error_code
+ )
+ )
+ elif not msg.is_type(self._msg_type):
+ raise ValueError("bad message type: {}".format(msg))
+ return msg
+
+
+class NetlinkTestTemplate(object):
+ REQUIRED_MODULES = ["netlink"]
+
+ def setup_netlink(self, netlink_family: NlConst):
+ self.helper = NlHelper()
+ self.nlsock = Nlsock(netlink_family, self.helper)
+
+ def write_message(self, msg, silent=False):
+ if not silent:
+ print("")
+ print("============= >> TX MESSAGE =============")
+ msg.print_message()
+ msg.print_as_bytes(bytes(msg), "-- DATA --")
+ self.nlsock.write_data(bytes(msg))
+
+ def read_message(self, silent=False):
+ msg = self.nlsock.read_message()
+ if not silent:
+ print("")
+ print("============= << RX MESSAGE =============")
+ msg.print_message()
+ return msg
+
+ def get_reply(self, tx_msg):
+ self.write_message(tx_msg)
+ while True:
+ rx_msg = self.read_message()
+ if tx_msg.nl_hdr.nlmsg_seq == rx_msg.nl_hdr.nlmsg_seq:
+ return rx_msg
+
+ def read_msg_list(self, seq, msg_type):
+ return list(NetlinkMultipartIterator(self, seq, msg_type))
diff --git a/tests/atf_python/sys/netlink/netlink_generic.py b/tests/atf_python/sys/netlink/netlink_generic.py
new file mode 100644
index 000000000000..80c6eea72a93
--- /dev/null
+++ b/tests/atf_python/sys/netlink/netlink_generic.py
@@ -0,0 +1,312 @@
+#!/usr/local/bin/python3
+import struct
+from ctypes import c_int64
+from ctypes import c_long
+from ctypes import sizeof
+from ctypes import Structure
+from enum import Enum
+
+from atf_python.sys.netlink.attrs import NlAttr
+from atf_python.sys.netlink.attrs import NlAttrIp4
+from atf_python.sys.netlink.attrs import NlAttrIp6
+from atf_python.sys.netlink.attrs import NlAttrNested
+from atf_python.sys.netlink.attrs import NlAttrS32
+from atf_python.sys.netlink.attrs import NlAttrStr
+from atf_python.sys.netlink.attrs import NlAttrU16
+from atf_python.sys.netlink.attrs import NlAttrU32
+from atf_python.sys.netlink.attrs import NlAttrU8
+from atf_python.sys.netlink.base_headers import GenlMsgHdr
+from atf_python.sys.netlink.message import NlMsgCategory
+from atf_python.sys.netlink.message import NlMsgProps
+from atf_python.sys.netlink.message import StdNetlinkMessage
+from atf_python.sys.netlink.utils import AttrDescr
+from atf_python.sys.netlink.utils import enum_or_int
+from atf_python.sys.netlink.utils import prepare_attrs_map
+
+
+class NetlinkGenlMessage(StdNetlinkMessage):
+ messages = []
+ nl_attrs_map = {}
+ family_name = None
+
+ def __init__(self, helper, family_id, cmd=0):
+ super().__init__(helper, family_id)
+ self.base_hdr = GenlMsgHdr(cmd=enum_or_int(cmd))
+
+ def parse_base_header(self, data):
+ if len(data) < sizeof(GenlMsgHdr):
+ raise ValueError("length less than GenlMsgHdr header")
+ ghdr = GenlMsgHdr.from_buffer_copy(data)
+ return (ghdr, sizeof(GenlMsgHdr))
+
+ def _get_msg_type(self):
+ return self.base_hdr.cmd
+
+ def print_nl_header(self, prepend=""):
+ # len=44, type=RTM_DELROUTE, flags=NLM_F_REQUEST|NLM_F_ACK, seq=1641163704, pid=0 # noqa: E501
+ hdr = self.nl_hdr
+ print(
+ "{}len={}, family={}, flags={}(0x{:X}), seq={}, pid={}".format(
+ prepend,
+ hdr.nlmsg_len,
+ self.family_name,
+ self.get_nlm_flags_str(),
+ hdr.nlmsg_flags,
+ hdr.nlmsg_seq,
+ hdr.nlmsg_pid,
+ )
+ )
+
+ def print_base_header(self, hdr, prepend=""):
+ print(
+ "{}cmd={} version={} reserved={}".format(
+ prepend, self.msg_name, hdr.version, hdr.reserved
+ )
+ )
+
+
+GenlCtrlFamilyName = "nlctrl"
+
+
+class GenlCtrlMsgType(Enum):
+ CTRL_CMD_UNSPEC = 0
+ CTRL_CMD_NEWFAMILY = 1
+ CTRL_CMD_DELFAMILY = 2
+ CTRL_CMD_GETFAMILY = 3
+ CTRL_CMD_NEWOPS = 4
+ CTRL_CMD_DELOPS = 5
+ CTRL_CMD_GETOPS = 6
+ CTRL_CMD_NEWMCAST_GRP = 7
+ CTRL_CMD_DELMCAST_GRP = 8
+ CTRL_CMD_GETMCAST_GRP = 9
+ CTRL_CMD_GETPOLICY = 10
+
+
+class GenlCtrlAttrType(Enum):
+ CTRL_ATTR_FAMILY_ID = 1
+ CTRL_ATTR_FAMILY_NAME = 2
+ CTRL_ATTR_VERSION = 3
+ CTRL_ATTR_HDRSIZE = 4
+ CTRL_ATTR_MAXATTR = 5
+ CTRL_ATTR_OPS = 6
+ CTRL_ATTR_MCAST_GROUPS = 7
+ CTRL_ATTR_POLICY = 8
+ CTRL_ATTR_OP_POLICY = 9
+ CTRL_ATTR_OP = 10
+
+
+class GenlCtrlAttrOpType(Enum):
+ CTRL_ATTR_OP_ID = 1
+ CTRL_ATTR_OP_FLAGS = 2
+
+
+class GenlCtrlAttrMcastGroupsType(Enum):
+ CTRL_ATTR_MCAST_GRP_NAME = 1
+ CTRL_ATTR_MCAST_GRP_ID = 2
+
+
+genl_ctrl_attrs = prepare_attrs_map(
+ [
+ AttrDescr(GenlCtrlAttrType.CTRL_ATTR_FAMILY_ID, NlAttrU16),
+ AttrDescr(GenlCtrlAttrType.CTRL_ATTR_FAMILY_NAME, NlAttrStr),
+ AttrDescr(GenlCtrlAttrType.CTRL_ATTR_VERSION, NlAttrU32),
+ AttrDescr(GenlCtrlAttrType.CTRL_ATTR_HDRSIZE, NlAttrU32),
+ AttrDescr(GenlCtrlAttrType.CTRL_ATTR_MAXATTR, NlAttrU32),
+ AttrDescr(
+ GenlCtrlAttrType.CTRL_ATTR_OPS,
+ NlAttrNested,
+ [
+ AttrDescr(GenlCtrlAttrOpType.CTRL_ATTR_OP_ID, NlAttrU32),
+ AttrDescr(GenlCtrlAttrOpType.CTRL_ATTR_OP_FLAGS, NlAttrU32),
+ ],
+ True,
+ ),
+ AttrDescr(
+ GenlCtrlAttrType.CTRL_ATTR_MCAST_GROUPS,
+ NlAttrNested,
+ [
+ AttrDescr(
+ GenlCtrlAttrMcastGroupsType.CTRL_ATTR_MCAST_GRP_NAME, NlAttrStr
+ ),
+ AttrDescr(
+ GenlCtrlAttrMcastGroupsType.CTRL_ATTR_MCAST_GRP_ID, NlAttrU32
+ ),
+ ],
+ True,
+ ),
+ ]
+)
+
+
+class NetlinkGenlCtrlMessage(NetlinkGenlMessage):
+ messages = [
+ NlMsgProps(GenlCtrlMsgType.CTRL_CMD_NEWFAMILY, NlMsgCategory.NEW),
+ NlMsgProps(GenlCtrlMsgType.CTRL_CMD_GETFAMILY, NlMsgCategory.GET),
+ NlMsgProps(GenlCtrlMsgType.CTRL_CMD_DELFAMILY, NlMsgCategory.DELETE),
+ ]
+ nl_attrs_map = genl_ctrl_attrs
+ family_name = GenlCtrlFamilyName
+
+
+CarpFamilyName = "carp"
+
+
+class CarpMsgType(Enum):
+ CARP_NL_CMD_UNSPEC = 0
+ CARP_NL_CMD_GET = 1
+ CARP_NL_CMD_SET = 2
+
+
+class CarpAttrType(Enum):
+ CARP_NL_UNSPEC = 0
+ CARP_NL_VHID = 1
+ CARP_NL_STATE = 2
+ CARP_NL_ADVBASE = 3
+ CARP_NL_ADVSKEW = 4
+ CARP_NL_KEY = 5
+ CARP_NL_IFINDEX = 6
+ CARP_NL_ADDR = 7
+ CARP_NL_ADDR6 = 8
+ CARP_NL_IFNAME = 9
+
+
+carp_gen_attrs = prepare_attrs_map(
+ [
+ AttrDescr(CarpAttrType.CARP_NL_VHID, NlAttrU32),
+ AttrDescr(CarpAttrType.CARP_NL_STATE, NlAttrU32),
+ AttrDescr(CarpAttrType.CARP_NL_ADVBASE, NlAttrS32),
+ AttrDescr(CarpAttrType.CARP_NL_ADVSKEW, NlAttrS32),
+ AttrDescr(CarpAttrType.CARP_NL_KEY, NlAttr),
+ AttrDescr(CarpAttrType.CARP_NL_IFINDEX, NlAttrU32),
+ AttrDescr(CarpAttrType.CARP_NL_ADDR, NlAttrIp4),
+ AttrDescr(CarpAttrType.CARP_NL_ADDR6, NlAttrIp6),
+ AttrDescr(CarpAttrType.CARP_NL_IFNAME, NlAttrStr),
+ ]
+)
+
+
+class CarpGenMessage(NetlinkGenlMessage):
+ messages = [
+ NlMsgProps(CarpMsgType.CARP_NL_CMD_GET, NlMsgCategory.GET),
+ NlMsgProps(CarpMsgType.CARP_NL_CMD_SET, NlMsgCategory.NEW),
+ ]
+ nl_attrs_map = carp_gen_attrs
+ family_name = CarpFamilyName
+
+
+KtestFamilyName = "ktest"
+
+
+class KtestMsgType(Enum):
+ KTEST_CMD_UNSPEC = 0
+ KTEST_CMD_LIST = 1
+ KTEST_CMD_RUN = 2
+ KTEST_CMD_NEWTEST = 3
+ KTEST_CMD_NEWMESSAGE = 4
+
+
+class KtestAttrType(Enum):
+ KTEST_ATTR_MOD_NAME = 1
+ KTEST_ATTR_TEST_NAME = 2
+ KTEST_ATTR_TEST_DESCR = 3
+ KTEST_ATTR_TEST_META = 4
+
+
+class KtestLogMsgType(Enum):
+ KTEST_MSG_START = 1
+ KTEST_MSG_END = 2
+ KTEST_MSG_LOG = 3
+ KTEST_MSG_FAIL = 4
+
+
+class KtestMsgAttrType(Enum):
+ KTEST_MSG_ATTR_TS = 1
+ KTEST_MSG_ATTR_FUNC = 2
+ KTEST_MSG_ATTR_FILE = 3
+ KTEST_MSG_ATTR_LINE = 4
+ KTEST_MSG_ATTR_TEXT = 5
+ KTEST_MSG_ATTR_LEVEL = 6
+ KTEST_MSG_ATTR_META = 7
+
+
+class timespec(Structure):
+ _fields_ = [
+ ("tv_sec", c_int64),
+ ("tv_nsec", c_long),
+ ]
+
+
+class NlAttrTS(NlAttr):
+ DATA_LEN = sizeof(timespec)
+
+ def __init__(self, nla_type, val):
+ self.ts = val
+ super().__init__(nla_type, b"")
+
+ @property
+ def nla_len(self):
+ return NlAttr.HDR_LEN + self.DATA_LEN
+
+ def _print_attr_value(self):
+ return " tv_sec={} tv_nsec={}".format(self.ts.tv_sec, self.ts.tv_nsec)
+
+ @staticmethod
+ def _validate(data):
+ assert len(data) == NlAttr.HDR_LEN + NlAttrTS.DATA_LEN
+ nla_len, nla_type = struct.unpack("@HH", data[: NlAttr.HDR_LEN])
+ assert nla_len == NlAttr.HDR_LEN + NlAttrTS.DATA_LEN
+
+ @classmethod
+ def _parse(cls, data):
+ nla_len, nla_type = struct.unpack("@HH", data[: NlAttr.HDR_LEN])
+ val = timespec.from_buffer_copy(data[NlAttr.HDR_LEN :])
+ return cls(nla_type, val)
+
+ def __bytes__(self):
+ return self._to_bytes(bytes(self.ts))
+
+
+ktest_info_attrs = prepare_attrs_map(
+ [
+ AttrDescr(KtestAttrType.KTEST_ATTR_MOD_NAME, NlAttrStr),
+ AttrDescr(KtestAttrType.KTEST_ATTR_TEST_NAME, NlAttrStr),
+ AttrDescr(KtestAttrType.KTEST_ATTR_TEST_DESCR, NlAttrStr),
+ ]
+)
+
+
+ktest_msg_attrs = prepare_attrs_map(
+ [
+ AttrDescr(KtestMsgAttrType.KTEST_MSG_ATTR_FUNC, NlAttrStr),
+ AttrDescr(KtestMsgAttrType.KTEST_MSG_ATTR_FILE, NlAttrStr),
+ AttrDescr(KtestMsgAttrType.KTEST_MSG_ATTR_LINE, NlAttrU32),
+ AttrDescr(KtestMsgAttrType.KTEST_MSG_ATTR_TEXT, NlAttrStr),
+ AttrDescr(KtestMsgAttrType.KTEST_MSG_ATTR_LEVEL, NlAttrU8),
+ AttrDescr(KtestMsgAttrType.KTEST_MSG_ATTR_TS, NlAttrTS),
+ ]
+)
+
+
+class KtestInfoMessage(NetlinkGenlMessage):
+ messages = [
+ NlMsgProps(KtestMsgType.KTEST_CMD_LIST, NlMsgCategory.GET),
+ NlMsgProps(KtestMsgType.KTEST_CMD_RUN, NlMsgCategory.NEW),
+ NlMsgProps(KtestMsgType.KTEST_CMD_NEWTEST, NlMsgCategory.NEW),
+ ]
+ nl_attrs_map = ktest_info_attrs
+ family_name = KtestFamilyName
+
+
+class KtestMsgMessage(NetlinkGenlMessage):
+ messages = [
+ NlMsgProps(KtestMsgType.KTEST_CMD_NEWMESSAGE, NlMsgCategory.NEW),
+ ]
+ nl_attrs_map = ktest_msg_attrs
+ family_name = KtestFamilyName
+
+
+handler_classes = {
+ CarpFamilyName: [CarpGenMessage],
+ GenlCtrlFamilyName: [NetlinkGenlCtrlMessage],
+ KtestFamilyName: [KtestInfoMessage, KtestMsgMessage],
+}
diff --git a/tests/atf_python/sys/netlink/netlink_route.py b/tests/atf_python/sys/netlink/netlink_route.py
new file mode 100644
index 000000000000..2cfeb57da13f
--- /dev/null
+++ b/tests/atf_python/sys/netlink/netlink_route.py
@@ -0,0 +1,832 @@
+import socket
+import struct
+from ctypes import c_int
+from ctypes import c_ubyte
+from ctypes import c_uint
+from ctypes import c_ushort
+from ctypes import sizeof
+from ctypes import Structure
+from enum import auto
+from enum import Enum
+
+from atf_python.sys.netlink.attrs import NlAttr
+from atf_python.sys.netlink.attrs import NlAttrIp
+from atf_python.sys.netlink.attrs import NlAttrNested
+from atf_python.sys.netlink.attrs import NlAttrStr
+from atf_python.sys.netlink.attrs import NlAttrU32
+from atf_python.sys.netlink.attrs import NlAttrU8
+from atf_python.sys.netlink.message import StdNetlinkMessage
+from atf_python.sys.netlink.message import NlMsgProps
+from atf_python.sys.netlink.message import NlMsgCategory
+from atf_python.sys.netlink.utils import AttrDescr
+from atf_python.sys.netlink.utils import get_bitmask_str
+from atf_python.sys.netlink.utils import prepare_attrs_map
+
+
+class RtattrType(Enum):
+ RTA_UNSPEC = 0
+ RTA_DST = 1
+ RTA_SRC = 2
+ RTA_IIF = 3
+ RTA_OIF = 4
+ RTA_GATEWAY = 5
+ RTA_PRIORITY = 6
+ RTA_PREFSRC = 7
+ RTA_METRICS = 8
+ RTA_MULTIPATH = 9
+ # RTA_PROTOINFO = 10
+ RTA_KNH_ID = 10
+ RTA_FLOW = 11
+ RTA_CACHEINFO = 12
+ RTA_SESSION = 13
+ # RTA_MP_ALGO = 14
+ RTA_RTFLAGS = 14
+ RTA_TABLE = 15
+ RTA_MARK = 16
+ RTA_MFC_STATS = 17
+ RTA_VIA = 18
+ RTA_NEWDST = 19
+ RTA_PREF = 20
+ RTA_ENCAP_TYPE = 21
+ RTA_ENCAP = 22
+ RTA_EXPIRES = 23
+ RTA_PAD = 24
+ RTA_UID = 25
+ RTA_TTL_PROPAGATE = 26
+ RTA_IP_PROTO = 27
+ RTA_SPORT = 28
+ RTA_DPORT = 29
+ RTA_NH_ID = 30
+
+
+class NlRtMsgType(Enum):
+ RTM_NEWLINK = 16
+ RTM_DELLINK = 17
+ RTM_GETLINK = 18
+ RTM_SETLINK = 19
+ RTM_NEWADDR = 20
+ RTM_DELADDR = 21
+ RTM_GETADDR = 22
+ RTM_NEWROUTE = 24
+ RTM_DELROUTE = 25
+ RTM_GETROUTE = 26
+ RTM_NEWNEIGH = 28
+ RTM_DELNEIGH = 29
+ RTM_GETNEIGH = 30
+ RTM_NEWRULE = 32
+ RTM_DELRULE = 33
+ RTM_GETRULE = 34
+ RTM_NEWQDISC = 36
+ RTM_DELQDISC = 37
+ RTM_GETQDISC = 38
+ RTM_NEWTCLASS = 40
+ RTM_DELTCLASS = 41
+ RTM_GETTCLASS = 42
+ RTM_NEWTFILTER = 44
+ RTM_DELTFILTER = 45
+ RTM_GETTFILTER = 46
+ RTM_NEWACTION = 48
+ RTM_DELACTION = 49
+ RTM_GETACTION = 50
+ RTM_NEWPREFIX = 52
+ RTM_GETMULTICAST = 58
+ RTM_GETANYCAST = 62
+ RTM_NEWNEIGHTBL = 64
+ RTM_GETNEIGHTBL = 66
+ RTM_SETNEIGHTBL = 67
+ RTM_NEWNDUSEROPT = 68
+ RTM_NEWADDRLABEL = 72
+ RTM_DELADDRLABEL = 73
+ RTM_GETADDRLABEL = 74
+ RTM_GETDCB = 78
+ RTM_SETDCB = 79
+ RTM_NEWNETCONF = 80
+ RTM_GETNETCONF = 82
+ RTM_NEWMDB = 84
+ RTM_DELMDB = 85
+ RTM_GETMDB = 86
+ RTM_NEWNSID = 88
+ RTM_DELNSID = 89
+ RTM_GETNSID = 90
+ RTM_NEWSTATS = 92
+ RTM_GETSTATS = 94
+
+
+class RtAttr(Structure):
+ _fields_ = [
+ ("rta_len", c_ushort),
+ ("rta_type", c_ushort),
+ ]
+
+
+class RtMsgHdr(Structure):
+ _fields_ = [
+ ("rtm_family", c_ubyte),
+ ("rtm_dst_len", c_ubyte),
+ ("rtm_src_len", c_ubyte),
+ ("rtm_tos", c_ubyte),
+ ("rtm_table", c_ubyte),
+ ("rtm_protocol", c_ubyte),
+ ("rtm_scope", c_ubyte),
+ ("rtm_type", c_ubyte),
+ ("rtm_flags", c_uint),
+ ]
+
+
+class RtMsgFlags(Enum):
+ RTM_F_NOTIFY = 0x100
+ RTM_F_CLONED = 0x200
+ RTM_F_EQUALIZE = 0x400
+ RTM_F_PREFIX = 0x800
+ RTM_F_LOOKUP_TABLE = 0x1000
+ RTM_F_FIB_MATCH = 0x2000
+ RTM_F_OFFLOAD = 0x4000
+ RTM_F_TRAP = 0x8000
+ RTM_F_OFFLOAD_FAILED = 0x20000000
+
+
+class RtScope(Enum):
+ RT_SCOPE_UNIVERSE = 0
+ RT_SCOPE_SITE = 200
+ RT_SCOPE_LINK = 253
+ RT_SCOPE_HOST = 254
+ RT_SCOPE_NOWHERE = 255
+
+
+class RtType(Enum):
+ RTN_UNSPEC = 0
+ RTN_UNICAST = auto()
+ RTN_LOCAL = auto()
+ RTN_BROADCAST = auto()
+ RTN_ANYCAST = auto()
+ RTN_MULTICAST = auto()
+ RTN_BLACKHOLE = auto()
+ RTN_UNREACHABLE = auto()
+ RTN_PROHIBIT = auto()
+ RTN_THROW = auto()
+ RTN_NAT = auto()
+ RTN_XRESOLVE = auto()
+
+
+class RtProto(Enum):
+ RTPROT_UNSPEC = 0
+ RTPROT_REDIRECT = 1
+ RTPROT_KERNEL = 2
+ RTPROT_BOOT = 3
+ RTPROT_STATIC = 4
+ RTPROT_GATED = 8
+ RTPROT_RA = 9
+ RTPROT_MRT = 10
+ RTPROT_ZEBRA = 11
+ RTPROT_BIRD = 12
+ RTPROT_DNROUTED = 13
+ RTPROT_XORP = 14
+ RTPROT_NTK = 15
+ RTPROT_DHCP = 16
+ RTPROT_MROUTED = 17
+ RTPROT_KEEPALIVED = 18
+ RTPROT_BABEL = 42
+ RTPROT_OPENR = 99
+ RTPROT_BGP = 186
+ RTPROT_ISIS = 187
+ RTPROT_OSPF = 188
+ RTPROT_RIP = 189
+ RTPROT_EIGRP = 192
+
+
+class NlRtaxType(Enum):
+ RTAX_UNSPEC = 0
+ RTAX_LOCK = auto()
+ RTAX_MTU = auto()
+ RTAX_WINDOW = auto()
+ RTAX_RTT = auto()
+ RTAX_RTTVAR = auto()
+ RTAX_SSTHRESH = auto()
+ RTAX_CWND = auto()
+ RTAX_ADVMSS = auto()
+ RTAX_REORDERING = auto()
+ RTAX_HOPLIMIT = auto()
+ RTAX_INITCWND = auto()
+ RTAX_FEATURES = auto()
+ RTAX_RTO_MIN = auto()
+ RTAX_INITRWND = auto()
+ RTAX_QUICKACK = auto()
+ RTAX_CC_ALGO = auto()
+ RTAX_FASTOPEN_NO_COOKIE = auto()
+
+
+class RtFlagsBSD(Enum):
+ RTF_UP = 0x1
+ RTF_GATEWAY = 0x2
+ RTF_HOST = 0x4
+ RTF_REJECT = 0x8
+ RTF_DYNAMIC = 0x10
+ RTF_MODIFIED = 0x20
+ RTF_DONE = 0x40
+ RTF_XRESOLVE = 0x200
+ RTF_LLINFO = 0x400
+ RTF_LLDATA = 0x400
+ RTF_STATIC = 0x800
+ RTF_BLACKHOLE = 0x1000
+ RTF_PROTO2 = 0x4000
+ RTF_PROTO1 = 0x8000
+ RTF_PROTO3 = 0x40000
+ RTF_FIXEDMTU = 0x80000
+ RTF_PINNED = 0x100000
+ RTF_LOCAL = 0x200000
+ RTF_BROADCAST = 0x400000
+ RTF_MULTICAST = 0x800000
+ RTF_STICKY = 0x10000000
+ RTF_RNH_LOCKED = 0x40000000
+ RTF_GWFLAG_COMPAT = 0x80000000
+
+
+class NlRtGroup(Enum):
+ RTNLGRP_NONE = 0
+ RTNLGRP_LINK = auto()
+ RTNLGRP_NOTIFY = auto()
+ RTNLGRP_NEIGH = auto()
+ RTNLGRP_TC = auto()
+ RTNLGRP_IPV4_IFADDR = auto()
+ RTNLGRP_IPV4_MROUTE = auto()
+ RTNLGRP_IPV4_ROUTE = auto()
+ RTNLGRP_IPV4_RULE = auto()
+ RTNLGRP_IPV6_IFADDR = auto()
+ RTNLGRP_IPV6_MROUTE = auto()
+ RTNLGRP_IPV6_ROUTE = auto()
+ RTNLGRP_IPV6_IFINFO = auto()
+ RTNLGRP_DECnet_IFADDR = auto()
+ RTNLGRP_NOP2 = auto()
+ RTNLGRP_DECnet_ROUTE = auto()
+ RTNLGRP_DECnet_RULE = auto()
+ RTNLGRP_NOP4 = auto()
+ RTNLGRP_IPV6_PREFIX = auto()
+ RTNLGRP_IPV6_RULE = auto()
+ RTNLGRP_ND_USEROPT = auto()
+ RTNLGRP_PHONET_IFADDR = auto()
+ RTNLGRP_PHONET_ROUTE = auto()
+ RTNLGRP_DCB = auto()
+ RTNLGRP_IPV4_NETCONF = auto()
+ RTNLGRP_IPV6_NETCONF = auto()
+ RTNLGRP_MDB = auto()
+ RTNLGRP_MPLS_ROUTE = auto()
+ RTNLGRP_NSID = auto()
+ RTNLGRP_MPLS_NETCONF = auto()
+ RTNLGRP_IPV4_MROUTE_R = auto()
+ RTNLGRP_IPV6_MROUTE_R = auto()
+ RTNLGRP_NEXTHOP = auto()
+ RTNLGRP_BRVLAN = auto()
+
+
+class IfinfoMsg(Structure):
+ _fields_ = [
+ ("ifi_family", c_ubyte),
+ ("__ifi_pad", c_ubyte),
+ ("ifi_type", c_ushort),
+ ("ifi_index", c_int),
+ ("ifi_flags", c_uint),
+ ("ifi_change", c_uint),
+ ]
+
+
+class IflattrType(Enum):
+ IFLA_UNSPEC = 0
+ IFLA_ADDRESS = 1
+ IFLA_BROADCAST = 2
+ IFLA_IFNAME = 3
+ IFLA_MTU = 4
+ IFLA_LINK = 5
+ IFLA_QDISC = 6
+ IFLA_STATS = 7
+ IFLA_COST = 8
+ IFLA_PRIORITY = 9
+ IFLA_MASTER = 10
+ IFLA_WIRELESS = 11
+ IFLA_PROTINFO = 12
+ IFLA_TXQLEN = 13
+ IFLA_MAP = 14
+ IFLA_WEIGHT = 15
+ IFLA_OPERSTATE = 16
+ IFLA_LINKMODE = 17
+ IFLA_LINKINFO = 18
+ IFLA_NET_NS_PID = 19
+ IFLA_IFALIAS = 20
+ IFLA_NUM_VF = 21
+ IFLA_VFINFO_LIST = 22
+ IFLA_STATS64 = 23
+ IFLA_VF_PORTS = 24
+ IFLA_PORT_SELF = 25
+ IFLA_AF_SPEC = 26
+ IFLA_GROUP = 27
+ IFLA_NET_NS_FD = 28
+ IFLA_EXT_MASK = 29
+ IFLA_PROMISCUITY = 30
+ IFLA_NUM_TX_QUEUES = 31
+ IFLA_NUM_RX_QUEUES = 32
+ IFLA_CARRIER = 33
+ IFLA_PHYS_PORT_ID = 34
+ IFLA_CARRIER_CHANGES = 35
+ IFLA_PHYS_SWITCH_ID = 36
+ IFLA_LINK_NETNSID = 37
+ IFLA_PHYS_PORT_NAME = 38
+ IFLA_PROTO_DOWN = 39
+ IFLA_GSO_MAX_SEGS = 40
+ IFLA_GSO_MAX_SIZE = 41
+ IFLA_PAD = 42
+ IFLA_XDP = 43
+ IFLA_EVENT = 44
+ IFLA_NEW_NETNSID = 45
+ IFLA_IF_NETNSID = 46
+ IFLA_CARRIER_UP_COUNT = 47
+ IFLA_CARRIER_DOWN_COUNT = 48
+ IFLA_NEW_IFINDEX = 49
+ IFLA_MIN_MTU = 50
+ IFLA_MAX_MTU = 51
+ IFLA_PROP_LIST = 52
+ IFLA_ALT_IFNAME = 53
+ IFLA_PERM_ADDRESS = 54
+ IFLA_PROTO_DOWN_REASON = 55
+ IFLA_PARENT_DEV_NAME = 56
+ IFLA_PARENT_DEV_BUS_NAME = 57
+ IFLA_GRO_MAX_SIZE = 58
+ IFLA_TSO_MAX_SEGS = 59
+ IFLA_ALLMULTI = 60
+ IFLA_DEVLINK_PORT = 61
+ IFLA_GSO_IPV4_MAX_SIZE = 62
+ IFLA_GRO_IPV4_MAX_SIZE = 63
+ IFLA_FREEBSD = 64
+
+
+class IflafAttrType(Enum):
+ IFLAF_UNSPEC = 0
+ IFLAF_ORIG_IFNAME = 1
+ IFLAF_ORIG_HWADDR = 2
+
+
+class IflinkInfo(Enum):
+ IFLA_INFO_UNSPEC = 0
+ IFLA_INFO_KIND = auto()
+ IFLA_INFO_DATA = auto()
+ IFLA_INFO_XSTATS = auto()
+ IFLA_INFO_SLAVE_KIND = auto()
+ IFLA_INFO_SLAVE_DATA = auto()
+
+
+class IfLinkInfoDataVlan(Enum):
+ IFLA_VLAN_UNSPEC = 0
+ IFLA_VLAN_ID = auto()
+ IFLA_VLAN_FLAGS = auto()
+ IFLA_VLAN_EGRESS_QOS = auto()
+ IFLA_VLAN_INGRESS_QOS = auto()
+ IFLA_VLAN_PROTOCOL = auto()
+
+
+class IfaddrMsg(Structure):
+ _fields_ = [
+ ("ifa_family", c_ubyte),
+ ("ifa_prefixlen", c_ubyte),
+ ("ifa_flags", c_ubyte),
+ ("ifa_scope", c_ubyte),
+ ("ifa_index", c_uint),
+ ]
+
+
+class IfaAttrType(Enum):
+ IFA_UNSPEC = 0
+ IFA_ADDRESS = 1
+ IFA_LOCAL = 2
+ IFA_LABEL = 3
+ IFA_BROADCAST = 4
+ IFA_ANYCAST = 5
+ IFA_CACHEINFO = 6
+ IFA_MULTICAST = 7
+ IFA_FLAGS = 8
+ IFA_RT_PRIORITY = 9
+ IFA_TARGET_NETNSID = 10
+ IFA_FREEBSD = 11
+
+
+class IfafAttrType(Enum):
+ IFAF_UNSPEC = 0
+ IFAF_VHID = 1
+ IFAF_FLAGS = 2
+
+
+class IfaCacheInfo(Structure):
+ _fields_ = [
+ ("ifa_prefered", c_uint), # seconds till the end of the prefix considered preferred
+ ("ifa_valid", c_uint), # seconds till the end of the prefix considered valid
+ ("cstamp", c_uint), # creation time in 1ms intervals from the boot time
+ ("tstamp", c_uint), # update time in 1ms intervals from the boot time
+ ]
+
+
+class IfaFlags(Enum):
+ IFA_F_TEMPORARY = 0x01
+ IFA_F_NODAD = 0x02
+ IFA_F_OPTIMISTIC = 0x04
+ IFA_F_DADFAILED = 0x08
+ IFA_F_HOMEADDRESS = 0x10
+ IFA_F_DEPRECATED = 0x20
+ IFA_F_TENTATIVE = 0x40
+ IFA_F_PERMANENT = 0x80
+ IFA_F_MANAGETEMPADDR = 0x100
+ IFA_F_NOPREFIXROUTE = 0x200
+ IFA_F_MCAUTOJOIN = 0x400
+ IFA_F_STABLE_PRIVACY = 0x800
+
+
+class IfafFlags6(Enum):
+ IN6_IFF_ANYCAST = 0x01
+ IN6_IFF_TENTATIVE = 0x02
+ IN6_IFF_DUPLICATED = 0x04
+ IN6_IFF_DETACHED = 0x08
+ IN6_IFF_DEPRECATED = 0x10
+ IN6_IFF_NODAD = 0x20
+ IN6_IFF_AUTOCONF = 0x40
+ IN6_IFF_TEMPORARY = 0x80
+ IN6_IFF_PREFER_SOURCE = 0x100
+
+
+class NdMsg(Structure):
+ _fields_ = [
+ ("ndm_family", c_ubyte),
+ ("ndm_pad1", c_ubyte),
+ ("ndm_pad2", c_ubyte),
+ ("ndm_ifindex", c_uint),
+ ("ndm_state", c_ushort),
+ ("ndm_flags", c_ubyte),
+ ("ndm_type", c_ubyte),
+ ]
+
+
+class NdAttrType(Enum):
+ NDA_UNSPEC = 0
+ NDA_DST = 1
+ NDA_LLADDR = 2
+ NDA_CACHEINFO = 3
+ NDA_PROBES = 4
+ NDA_VLAN = 5
+ NDA_PORT = 6
+ NDA_VNI = 7
+ NDA_IFINDEX = 8
+ NDA_MASTER = 9
+ NDA_LINK_NETNSID = 10
+ NDA_SRC_VNI = 11
+ NDA_PROTOCOL = 12
+ NDA_NH_ID = 13
+ NDA_FDB_EXT_ATTRS = 14
+ NDA_FLAGS_EXT = 15
+ NDA_NDM_STATE_MASK = 16
+ NDA_NDM_FLAGS_MASK = 17
+
+
+class NlAttrRtFlags(NlAttrU32):
+ def _print_attr_value(self):
+ s = get_bitmask_str(RtFlagsBSD, self.u32)
+ return " rtflags={}".format(s)
+
+
+class NlAttrIfindex(NlAttrU32):
+ def _print_attr_value(self):
+ try:
+ ifname = socket.if_indextoname(self.u32)
+ return " iface={}(#{})".format(ifname, self.u32)
+ except OSError:
+ pass
+ return " iface=if#{}".format(self.u32)
+
+
+class NlAttrTable(NlAttrU32):
+ def _print_attr_value(self):
+ return " rtable={}".format(self.u32)
+
+
+class NlAttrNhId(NlAttrU32):
+ def _print_attr_value(self):
+ return " nh_id={}".format(self.u32)
+
+
+class NlAttrKNhId(NlAttrU32):
+ def _print_attr_value(self):
+ return " knh_id={}".format(self.u32)
+
+
+class NlAttrMac(NlAttr):
+ def _print_attr_value(self):
+ return ' mac="' + ":".join(["{:02X}".format(b) for b in self._data]) + '"'
+
+
+class NlAttrIfStats(NlAttr):
+ def _print_attr_value(self):
+ return " stats={...}"
+
+
+class NlAttrCacheInfo(NlAttr):
+ def __init__(self, nla_type, data):
+ super().__init__(nla_type, data)
+ self.ci = IfaCacheInfo.from_buffer_copy(data)
+
+ @staticmethod
+ def _validate(data):
+ nla_len, nla_type = struct.unpack("@HH", data[:4])
+ data_len = nla_len - 4
+ if data_len != sizeof(IfaCacheInfo):
+ raise ValueError(
+ "Error validating attr {}: wrong size".format(nla_type)
+ ) # noqa: E501
+
+ def _print_attr_value(self):
+ return " ifa_prefered={} ifa_valid={} cstamp={} tstamp={}".format(
+ self.ci.ifa_prefered, self.ci.ifa_valid, self.ci.cstamp, self.ci.tstamp)
+
+
+class NlAttrVia(NlAttr):
+ def __init__(self, nla_type, family, addr: str):
+ super().__init__(nla_type, b"")
+ self.addr = addr
+ self.family = family
+
+ @staticmethod
+ def _validate(data):
+ nla_len, nla_type = struct.unpack("@HH", data[:4])
+ data_len = nla_len - 4
+ if data_len == 0:
+ raise ValueError(
+ "Error validating attr {}: empty data".format(nla_type)
+ ) # noqa: E501
+ family = int(data_len[0])
+ if family not in (socket.AF_INET, socket.AF_INET6):
+ raise ValueError(
+ "Error validating attr {}: unsupported AF {}".format( # noqa: E501
+ nla_type, family
+ )
+ )
+ if family == socket.AF_INET:
+ expected_len = 1 + 4
+ else:
+ expected_len = 1 + 16
+ if data_len != expected_len:
+ raise ValueError(
+ "Error validating attr {}: expected len {} got {}".format( # noqa: E501
+ nla_type, expected_len, data_len
+ )
+ )
+
+ @property
+ def nla_len(self):
+ if self.family == socket.AF_INET6:
+ return 21
+ else:
+ return 9
+
+ @classmethod
+ def _parse(cls, data):
+ nla_len, nla_type, family = struct.unpack("@HHB", data[:5])
+ off = 5
+ if family == socket.AF_INET:
+ addr = socket.inet_ntop(family, data[off:off + 4])
+ else:
+ addr = socket.inet_ntop(family, data[off:off + 16])
+ return cls(nla_type, family, addr)
+
+ def __bytes__(self):
+ addr = socket.inet_pton(self.family, self.addr)
+ return self._to_bytes(struct.pack("@B", self.family) + addr)
+
+ def _print_attr_value(self):
+ return " via={}".format(self.addr)
+
+
+rtnl_route_attrs = prepare_attrs_map(
+ [
+ AttrDescr(RtattrType.RTA_DST, NlAttrIp),
+ AttrDescr(RtattrType.RTA_SRC, NlAttrIp),
+ AttrDescr(RtattrType.RTA_IIF, NlAttrIfindex),
+ AttrDescr(RtattrType.RTA_OIF, NlAttrIfindex),
+ AttrDescr(RtattrType.RTA_GATEWAY, NlAttrIp),
+ AttrDescr(RtattrType.RTA_TABLE, NlAttrTable),
+ AttrDescr(RtattrType.RTA_PRIORITY, NlAttrU32),
+ AttrDescr(RtattrType.RTA_VIA, NlAttrVia),
+ AttrDescr(RtattrType.RTA_NH_ID, NlAttrNhId),
+ AttrDescr(RtattrType.RTA_KNH_ID, NlAttrKNhId),
+ AttrDescr(RtattrType.RTA_RTFLAGS, NlAttrRtFlags),
+ AttrDescr(
+ RtattrType.RTA_METRICS,
+ NlAttrNested,
+ [
+ AttrDescr(NlRtaxType.RTAX_MTU, NlAttrU32),
+ ],
+ ),
+ ]
+)
+
+rtnl_ifla_attrs = prepare_attrs_map(
+ [
+ AttrDescr(IflattrType.IFLA_ADDRESS, NlAttrMac),
+ AttrDescr(IflattrType.IFLA_BROADCAST, NlAttrMac),
+ AttrDescr(IflattrType.IFLA_IFNAME, NlAttrStr),
+ AttrDescr(IflattrType.IFLA_MTU, NlAttrU32),
+ AttrDescr(IflattrType.IFLA_LINK, NlAttrU32),
+ AttrDescr(IflattrType.IFLA_PROMISCUITY, NlAttrU32),
+ AttrDescr(IflattrType.IFLA_OPERSTATE, NlAttrU8),
+ AttrDescr(IflattrType.IFLA_CARRIER, NlAttrU8),
+ AttrDescr(IflattrType.IFLA_IFALIAS, NlAttrStr),
+ AttrDescr(IflattrType.IFLA_STATS64, NlAttrIfStats),
+ AttrDescr(IflattrType.IFLA_NEW_IFINDEX, NlAttrU32),
+ AttrDescr(
+ IflattrType.IFLA_LINKINFO,
+ NlAttrNested,
+ [
+ AttrDescr(IflinkInfo.IFLA_INFO_KIND, NlAttrStr),
+ AttrDescr(IflinkInfo.IFLA_INFO_DATA, NlAttr),
+ ],
+ ),
+ AttrDescr(
+ IflattrType.IFLA_FREEBSD,
+ NlAttrNested,
+ [
+ AttrDescr(IflafAttrType.IFLAF_ORIG_HWADDR, NlAttrMac),
+ ],
+ ),
+ ]
+)
+
+rtnl_ifa_attrs = prepare_attrs_map(
+ [
+ AttrDescr(IfaAttrType.IFA_ADDRESS, NlAttrIp),
+ AttrDescr(IfaAttrType.IFA_LOCAL, NlAttrIp),
+ AttrDescr(IfaAttrType.IFA_LABEL, NlAttrStr),
+ AttrDescr(IfaAttrType.IFA_BROADCAST, NlAttrIp),
+ AttrDescr(IfaAttrType.IFA_ANYCAST, NlAttrIp),
+ AttrDescr(IfaAttrType.IFA_FLAGS, NlAttrU32),
+ AttrDescr(IfaAttrType.IFA_CACHEINFO, NlAttrCacheInfo),
+ AttrDescr(
+ IfaAttrType.IFA_FREEBSD,
+ NlAttrNested,
+ [
+ AttrDescr(IfafAttrType.IFAF_VHID, NlAttrU32),
+ AttrDescr(IfafAttrType.IFAF_FLAGS, NlAttrU32),
+ ],
+ ),
+ ]
+)
+
+
+rtnl_nd_attrs = prepare_attrs_map(
+ [
+ AttrDescr(NdAttrType.NDA_DST, NlAttrIp),
+ AttrDescr(NdAttrType.NDA_IFINDEX, NlAttrIfindex),
+ AttrDescr(NdAttrType.NDA_FLAGS_EXT, NlAttrU32),
+ AttrDescr(NdAttrType.NDA_LLADDR, NlAttrMac),
+ ]
+)
+
+
+class BaseNetlinkRtMessage(StdNetlinkMessage):
+ pass
+
+
+class NetlinkRtMessage(BaseNetlinkRtMessage):
+ messages = [
+ NlMsgProps(NlRtMsgType.RTM_NEWROUTE, NlMsgCategory.NEW),
+ NlMsgProps(NlRtMsgType.RTM_DELROUTE, NlMsgCategory.DELETE),
+ NlMsgProps(NlRtMsgType.RTM_GETROUTE, NlMsgCategory.GET),
+ ]
+ nl_attrs_map = rtnl_route_attrs
+
+ def __init__(self, helper, nlm_type):
+ super().__init__(helper, nlm_type)
+ self.base_hdr = RtMsgHdr()
+
+ def parse_base_header(self, data):
+ if len(data) < sizeof(RtMsgHdr):
+ raise ValueError("length less than rtmsg header")
+ rtm_hdr = RtMsgHdr.from_buffer_copy(data)
+ return (rtm_hdr, sizeof(RtMsgHdr))
+
+ def print_base_header(self, hdr, prepend=""):
+ family = self.helper.get_af_name(hdr.rtm_family)
+ print(
+ "{}family={}, dst_len={}, src_len={}, tos={}, table={}, protocol={}({}), scope={}({}), type={}({}), flags={}({})".format( # noqa: E501
+ prepend,
+ family,
+ hdr.rtm_dst_len,
+ hdr.rtm_src_len,
+ hdr.rtm_tos,
+ hdr.rtm_table,
+ self.helper.get_attr_byval(RtProto, hdr.rtm_protocol),
+ hdr.rtm_protocol,
+ self.helper.get_attr_byval(RtScope, hdr.rtm_scope),
+ hdr.rtm_scope,
+ self.helper.get_attr_byval(RtType, hdr.rtm_type),
+ hdr.rtm_type,
+ self.helper.get_bitmask_str(RtMsgFlags, hdr.rtm_flags),
+ hdr.rtm_flags,
+ )
+ )
+
+
+class NetlinkIflaMessage(BaseNetlinkRtMessage):
+ messages = [
+ NlMsgProps(NlRtMsgType.RTM_NEWLINK, NlMsgCategory.NEW),
+ NlMsgProps(NlRtMsgType.RTM_DELLINK, NlMsgCategory.DELETE),
+ NlMsgProps(NlRtMsgType.RTM_GETLINK, NlMsgCategory.GET),
+ ]
+ nl_attrs_map = rtnl_ifla_attrs
+
+ def __init__(self, helper, nlm_type):
+ super().__init__(helper, nlm_type)
+ self.base_hdr = IfinfoMsg()
+
+ def parse_base_header(self, data):
+ if len(data) < sizeof(IfinfoMsg):
+ raise ValueError("length less than IfinfoMsg header")
+ rtm_hdr = IfinfoMsg.from_buffer_copy(data)
+ return (rtm_hdr, sizeof(IfinfoMsg))
+
+ def print_base_header(self, hdr, prepend=""):
+ family = self.helper.get_af_name(hdr.ifi_family)
+ print(
+ "{}family={}, ifi_type={}, ifi_index={}, ifi_flags={}, ifi_change={}".format( # noqa: E501
+ prepend,
+ family,
+ hdr.ifi_type,
+ hdr.ifi_index,
+ hdr.ifi_flags,
+ hdr.ifi_change,
+ )
+ )
+
+
+class NetlinkIfaMessage(BaseNetlinkRtMessage):
+ messages = [
+ NlMsgProps(NlRtMsgType.RTM_NEWADDR, NlMsgCategory.NEW),
+ NlMsgProps(NlRtMsgType.RTM_DELADDR, NlMsgCategory.DELETE),
+ NlMsgProps(NlRtMsgType.RTM_GETADDR, NlMsgCategory.GET),
+ ]
+ nl_attrs_map = rtnl_ifa_attrs
+
+ def __init__(self, helper, nlm_type):
+ super().__init__(helper, nlm_type)
+ self.base_hdr = IfaddrMsg()
+
+ def parse_base_header(self, data):
+ if len(data) < sizeof(IfaddrMsg):
+ raise ValueError("length less than IfaddrMsg header")
+ rtm_hdr = IfaddrMsg.from_buffer_copy(data)
+ return (rtm_hdr, sizeof(IfaddrMsg))
+
+ def print_base_header(self, hdr, prepend=""):
+ family = self.helper.get_af_name(hdr.ifa_family)
+ print(
+ "{}family={}, ifa_prefixlen={}, ifa_flags={}, ifa_scope={}, ifa_index={}".format( # noqa: E501
+ prepend,
+ family,
+ hdr.ifa_prefixlen,
+ hdr.ifa_flags,
+ hdr.ifa_scope,
+ hdr.ifa_index,
+ )
+ )
+
+
+class NetlinkNdMessage(BaseNetlinkRtMessage):
+ messages = [
+ NlMsgProps(NlRtMsgType.RTM_NEWNEIGH, NlMsgCategory.NEW),
+ NlMsgProps(NlRtMsgType.RTM_DELNEIGH, NlMsgCategory.DELETE),
+ NlMsgProps(NlRtMsgType.RTM_GETNEIGH, NlMsgCategory.GET),
+ ]
+ nl_attrs_map = rtnl_nd_attrs
+
+ def __init__(self, helper, nlm_type):
+ super().__init__(helper, nlm_type)
+ self.base_hdr = NdMsg()
+
+ def parse_base_header(self, data):
+ if len(data) < sizeof(NdMsg):
+ raise ValueError("length less than NdMsg header")
+ nd_hdr = NdMsg.from_buffer_copy(data)
+ return (nd_hdr, sizeof(NdMsg))
+
+ def print_base_header(self, hdr, prepend=""):
+ family = self.helper.get_af_name(hdr.ndm_family)
+ print(
+ "{}family={}, ndm_ifindex={}, ndm_state={}, ndm_flags={}".format( # noqa: E501
+ prepend,
+ family,
+ hdr.ndm_ifindex,
+ hdr.ndm_state,
+ hdr.ndm_flags,
+ )
+ )
+
+
+handler_classes = {
+ "netlink_route": [
+ NetlinkRtMessage,
+ NetlinkIflaMessage,
+ NetlinkIfaMessage,
+ NetlinkNdMessage,
+ ],
+}
diff --git a/tests/atf_python/sys/netlink/utils.py b/tests/atf_python/sys/netlink/utils.py
new file mode 100644
index 000000000000..f1d0ba3321ed
--- /dev/null
+++ b/tests/atf_python/sys/netlink/utils.py
@@ -0,0 +1,80 @@
+#!/usr/local/bin/python3
+from enum import Enum
+from typing import Any
+from typing import Dict
+from typing import List
+from typing import NamedTuple
+
+
+class NlConst:
+ AF_NETLINK = 38
+ NETLINK_ROUTE = 0
+ NETLINK_GENERIC = 16
+ GENL_ID_CTRL = 16
+
+
+def roundup2(val: int, num: int) -> int:
+ if val % num:
+ return (val | (num - 1)) + 1
+ else:
+ return val
+
+
+def align4(val: int) -> int:
+ return roundup2(val, 4)
+
+
+def enum_or_int(val) -> int:
+ if isinstance(val, Enum):
+ return val.value
+ return val
+
+
+class AttrDescr(NamedTuple):
+ val: Enum
+ cls: "NlAttr"
+ child_map: Any = None
+ is_array: bool = False
+
+
+def prepare_attrs_map(attrs: List[AttrDescr]) -> Dict[str, Dict]:
+ ret = {}
+ for ad in attrs:
+ ret[ad.val.value] = {"ad": ad}
+ if ad.child_map:
+ ret[ad.val.value]["child"] = prepare_attrs_map(ad.child_map)
+ ret[ad.val.value]["is_array"] = ad.is_array
+ return ret
+
+
+def build_propmap(cls):
+ ret = {}
+ for prop in dir(cls):
+ if not prop.startswith("_"):
+ ret[getattr(cls, prop).value] = prop
+ return ret
+
+
+def get_bitmask_map(propmap, val):
+ v = 1
+ ret = {}
+ while val:
+ if v & val:
+ if v in propmap:
+ ret[v] = propmap[v]
+ else:
+ ret[v] = hex(v)
+ val -= v
+ v *= 2
+ return ret
+
+
+def get_bitmask_str(cls, val):
+ if isinstance(cls, type):
+ pmap = build_propmap(cls)
+ else:
+ pmap = {}
+ for _cls in cls:
+ pmap.update(build_propmap(_cls))
+ bmap = get_bitmask_map(pmap, val)
+ return ",".join([v for k, v in bmap.items()])
diff --git a/tests/atf_python/sys/netpfil/Makefile b/tests/atf_python/sys/netpfil/Makefile
new file mode 100644
index 000000000000..47e7a0d4d4f1
--- /dev/null
+++ b/tests/atf_python/sys/netpfil/Makefile
@@ -0,0 +1,12 @@
+.include <src.opts.mk>
+
+.PATH: ${.CURDIR}
+
+PACKAGE=tests
+FILES= __init__.py
+SUBDIR= ipfw
+
+.include <bsd.own.mk>
+FILESDIR= ${TESTSBASE}/atf_python/sys/netpfil
+
+.include <bsd.prog.mk>
diff --git a/tests/atf_python/sys/netpfil/__init__.py b/tests/atf_python/sys/netpfil/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/tests/atf_python/sys/netpfil/__init__.py
diff --git a/tests/atf_python/sys/netpfil/ipfw/Makefile b/tests/atf_python/sys/netpfil/ipfw/Makefile
new file mode 100644
index 000000000000..fde36de23c93
--- /dev/null
+++ b/tests/atf_python/sys/netpfil/ipfw/Makefile
@@ -0,0 +1,13 @@
+.include <src.opts.mk>
+
+.PATH: ${.CURDIR}
+
+PACKAGE=tests
+FILES= __init__.py insns.py insn_headers.py ioctl.py ioctl_headers.py \
+ ipfw.py utils.py
+
+.include <bsd.own.mk>
+FILESDIR= ${TESTSBASE}/atf_python/sys/netpfil/ipfw
+
+.include <bsd.prog.mk>
+
diff --git a/tests/atf_python/sys/netpfil/ipfw/__init__.py b/tests/atf_python/sys/netpfil/ipfw/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/tests/atf_python/sys/netpfil/ipfw/__init__.py
diff --git a/tests/atf_python/sys/netpfil/ipfw/insn_headers.py b/tests/atf_python/sys/netpfil/ipfw/insn_headers.py
new file mode 100644
index 000000000000..5c160d0758d6
--- /dev/null
+++ b/tests/atf_python/sys/netpfil/ipfw/insn_headers.py
@@ -0,0 +1,198 @@
+from enum import Enum
+
+
+class IpFwOpcode(Enum):
+ O_NOP = 0
+ O_IP_SRC = 1
+ O_IP_SRC_MASK = 2
+ O_IP_SRC_ME = 3
+ O_IP_SRC_SET = 4
+ O_IP_DST = 5
+ O_IP_DST_MASK = 6
+ O_IP_DST_ME = 7
+ O_IP_DST_SET = 8
+ O_IP_SRCPORT = 9
+ O_IP_DSTPORT = 10
+ O_PROTO = 11
+ O_MACADDR2 = 12
+ O_MAC_TYPE = 13
+ O_LAYER2 = 14
+ O_IN = 15
+ O_FRAG = 16
+ O_RECV = 17
+ O_XMIT = 18
+ O_VIA = 19
+ O_IPOPT = 20
+ O_IPLEN = 21
+ O_IPID = 22
+ O_IPTOS = 23
+ O_IPPRECEDENCE = 24
+ O_IPTTL = 25
+ O_IPVER = 26
+ O_UID = 27
+ O_GID = 28
+ O_ESTAB = 29
+ O_TCPFLAGS = 30
+ O_TCPWIN = 31
+ O_TCPSEQ = 32
+ O_TCPACK = 33
+ O_ICMPTYPE = 34
+ O_TCPOPTS = 35
+ O_VERREVPATH = 36
+ O_VERSRCREACH = 37
+ O_PROBE_STATE = 38
+ O_KEEP_STATE = 39
+ O_LIMIT = 40
+ O_LIMIT_PARENT = 41
+ O_LOG = 42
+ O_PROB = 43
+ O_CHECK_STATE = 44
+ O_ACCEPT = 45
+ O_DENY = 46
+ O_REJECT = 47
+ O_COUNT = 48
+ O_SKIPTO = 49
+ O_PIPE = 50
+ O_QUEUE = 51
+ O_DIVERT = 52
+ O_TEE = 53
+ O_FORWARD_IP = 54
+ O_FORWARD_MAC = 55
+ O_NAT = 56
+ O_REASS = 57
+ O_IPSEC = 58
+ O_IP_SRC_LOOKUP = 59
+ O_IP_DST_LOOKUP = 60
+ O_ANTISPOOF = 61
+ O_JAIL = 62
+ O_ALTQ = 63
+ O_DIVERTED = 64
+ O_TCPDATALEN = 65
+ O_IP6_SRC = 66
+ O_IP6_SRC_ME = 67
+ O_IP6_SRC_MASK = 68
+ O_IP6_DST = 69
+ O_IP6_DST_ME = 70
+ O_IP6_DST_MASK = 71
+ O_FLOW6ID = 72
+ O_ICMP6TYPE = 73
+ O_EXT_HDR = 74
+ O_IP6 = 75
+ O_NETGRAPH = 76
+ O_NGTEE = 77
+ O_IP4 = 78
+ O_UNREACH6 = 79
+ O_TAG = 80
+ O_TAGGED = 81
+ O_SETFIB = 82
+ O_FIB = 83
+ O_SOCKARG = 84
+ O_CALLRETURN = 85
+ O_FORWARD_IP6 = 86
+ O_DSCP = 87
+ O_SETDSCP = 88
+ O_IP_FLOW_LOOKUP = 89
+ O_EXTERNAL_ACTION = 90
+ O_EXTERNAL_INSTANCE = 91
+ O_EXTERNAL_DATA = 92
+ O_SKIP_ACTION = 93
+ O_TCPMSS = 94
+ O_MAC_SRC_LOOKUP = 95
+ O_MAC_DST_LOOKUP = 96
+ O_SETMARK = 97
+ O_MARK = 98
+ O_LAST_OPCODE = 99
+
+
+class Op3CmdType(Enum):
+ IP_FW_TABLE_XADD = 86
+ IP_FW_TABLE_XDEL = 87
+ IP_FW_TABLE_XGETSIZE = 88
+ IP_FW_TABLE_XLIST = 89
+ IP_FW_TABLE_XDESTROY = 90
+ IP_FW_TABLES_XLIST = 92
+ IP_FW_TABLE_XINFO = 93
+ IP_FW_TABLE_XFLUSH = 94
+ IP_FW_TABLE_XCREATE = 95
+ IP_FW_TABLE_XMODIFY = 96
+ IP_FW_XGET = 97
+ IP_FW_XADD = 98
+ IP_FW_XDEL = 99
+ IP_FW_XMOVE = 100
+ IP_FW_XZERO = 101
+ IP_FW_XRESETLOG = 102
+ IP_FW_SET_SWAP = 103
+ IP_FW_SET_MOVE = 104
+ IP_FW_SET_ENABLE = 105
+ IP_FW_TABLE_XFIND = 106
+ IP_FW_XIFLIST = 107
+ IP_FW_TABLES_ALIST = 108
+ IP_FW_TABLE_XSWAP = 109
+ IP_FW_TABLE_VLIST = 110
+ IP_FW_NAT44_XCONFIG = 111
+ IP_FW_NAT44_DESTROY = 112
+ IP_FW_NAT44_XGETCONFIG = 113
+ IP_FW_NAT44_LIST_NAT = 114
+ IP_FW_NAT44_XGETLOG = 115
+ IP_FW_DUMP_SOPTCODES = 116
+ IP_FW_DUMP_SRVOBJECTS = 117
+ IP_FW_NAT64STL_CREATE = 130
+ IP_FW_NAT64STL_DESTROY = 131
+ IP_FW_NAT64STL_CONFIG = 132
+ IP_FW_NAT64STL_LIST = 133
+ IP_FW_NAT64STL_STATS = 134
+ IP_FW_NAT64STL_RESET_STATS = 135
+ IP_FW_NAT64LSN_CREATE = 140
+ IP_FW_NAT64LSN_DESTROY = 141
+ IP_FW_NAT64LSN_CONFIG = 142
+ IP_FW_NAT64LSN_LIST = 143
+ IP_FW_NAT64LSN_STATS = 144
+ IP_FW_NAT64LSN_LIST_STATES = 145
+ IP_FW_NAT64LSN_RESET_STATS = 146
+ IP_FW_NPTV6_CREATE = 150
+ IP_FW_NPTV6_DESTROY = 151
+ IP_FW_NPTV6_CONFIG = 152
+ IP_FW_NPTV6_LIST = 153
+ IP_FW_NPTV6_STATS = 154
+ IP_FW_NPTV6_RESET_STATS = 155
+ IP_FW_NAT64CLAT_CREATE = 160
+ IP_FW_NAT64CLAT_DESTROY = 161
+ IP_FW_NAT64CLAT_CONFIG = 162
+ IP_FW_NAT64CLAT_LIST = 163
+ IP_FW_NAT64CLAT_STATS = 164
+ IP_FW_NAT64CLAT_RESET_STATS = 165
+
+
+class IcmpRejectCode(Enum):
+ ICMP_UNREACH_NET = 0
+ ICMP_UNREACH_HOST = 1
+ ICMP_UNREACH_PROTOCOL = 2
+ ICMP_UNREACH_PORT = 3
+ ICMP_UNREACH_NEEDFRAG = 4
+ ICMP_UNREACH_SRCFAIL = 5
+ ICMP_UNREACH_NET_UNKNOWN = 6
+ ICMP_UNREACH_HOST_UNKNOWN = 7
+ ICMP_UNREACH_ISOLATED = 8
+ ICMP_UNREACH_NET_PROHIB = 9
+ ICMP_UNREACH_HOST_PROHIB = 10
+ ICMP_UNREACH_TOSNET = 11
+ ICMP_UNREACH_TOSHOST = 12
+ ICMP_UNREACH_FILTER_PROHIB = 13
+ ICMP_UNREACH_HOST_PRECEDENCE = 14
+ ICMP_UNREACH_PRECEDENCE_CUTOFF = 15
+ ICMP_REJECT_RST = 256
+ ICMP_REJECT_ABORT = 257
+
+
+class Icmp6RejectCode(Enum):
+ ICMP6_DST_UNREACH_NOROUTE = 0
+ ICMP6_DST_UNREACH_ADMIN = 1
+ ICMP6_DST_UNREACH_BEYONDSCOPE = 2
+ ICMP6_DST_UNREACH_NOTNEIGHBOR = 2
+ ICMP6_DST_UNREACH_ADDR = 3
+ ICMP6_DST_UNREACH_NOPORT = 4
+ ICMP6_DST_UNREACH_POLICY = 5
+ ICMP6_DST_UNREACH_REJECT = 6
+ ICMP6_DST_UNREACH_SRCROUTE = 7
+ ICMP6_UNREACH_RST = 256
+ ICMP6_UNREACH_ABORT = 257
diff --git a/tests/atf_python/sys/netpfil/ipfw/insns.py b/tests/atf_python/sys/netpfil/ipfw/insns.py
new file mode 100644
index 000000000000..12f145f49393
--- /dev/null
+++ b/tests/atf_python/sys/netpfil/ipfw/insns.py
@@ -0,0 +1,555 @@
+#!/usr/bin/env python3
+import os
+import socket
+import struct
+import subprocess
+import sys
+from ctypes import c_byte
+from ctypes import c_char
+from ctypes import c_int
+from ctypes import c_long
+from ctypes import c_uint32
+from ctypes import c_uint8
+from ctypes import c_ulong
+from ctypes import c_ushort
+from ctypes import sizeof
+from ctypes import Structure
+from enum import Enum
+from typing import Any
+from typing import Dict
+from typing import List
+from typing import NamedTuple
+from typing import Optional
+from typing import Union
+
+from atf_python.sys.netpfil.ipfw.insn_headers import IpFwOpcode
+from atf_python.sys.netpfil.ipfw.insn_headers import IcmpRejectCode
+from atf_python.sys.netpfil.ipfw.insn_headers import Icmp6RejectCode
+from atf_python.sys.netpfil.ipfw.utils import AttrDescr
+from atf_python.sys.netpfil.ipfw.utils import enum_or_int
+from atf_python.sys.netpfil.ipfw.utils import enum_from_int
+from atf_python.sys.netpfil.ipfw.utils import prepare_attrs_map
+
+
+insn_actions = (
+ IpFwOpcode.O_CHECK_STATE.value,
+ IpFwOpcode.O_REJECT.value,
+ IpFwOpcode.O_UNREACH6.value,
+ IpFwOpcode.O_ACCEPT.value,
+ IpFwOpcode.O_DENY.value,
+ IpFwOpcode.O_COUNT.value,
+ IpFwOpcode.O_NAT.value,
+ IpFwOpcode.O_QUEUE.value,
+ IpFwOpcode.O_PIPE.value,
+ IpFwOpcode.O_SKIPTO.value,
+ IpFwOpcode.O_NETGRAPH.value,
+ IpFwOpcode.O_NGTEE.value,
+ IpFwOpcode.O_DIVERT.value,
+ IpFwOpcode.O_TEE.value,
+ IpFwOpcode.O_CALLRETURN.value,
+ IpFwOpcode.O_FORWARD_IP.value,
+ IpFwOpcode.O_FORWARD_IP6.value,
+ IpFwOpcode.O_SETFIB.value,
+ IpFwOpcode.O_SETDSCP.value,
+ IpFwOpcode.O_REASS.value,
+ IpFwOpcode.O_SETMARK.value,
+ IpFwOpcode.O_EXTERNAL_ACTION.value,
+)
+
+
+class IpFwInsn(Structure):
+ _fields_ = [
+ ("opcode", c_uint8),
+ ("length", c_uint8),
+ ("arg1", c_ushort),
+ ]
+
+
+class BaseInsn(object):
+ obj_enum_class = IpFwOpcode
+
+ def __init__(self, opcode, is_or, is_not, arg1):
+ if isinstance(opcode, Enum):
+ self.obj_type = opcode.value
+ self._enum = opcode
+ else:
+ self.obj_type = opcode
+ self._enum = enum_from_int(self.obj_enum_class, self.obj_type)
+ self.is_or = is_or
+ self.is_not = is_not
+ self.arg1 = arg1
+ self.is_action = self.obj_type in insn_actions
+ self.ilen = 1
+ self.obj_list = []
+
+ @property
+ def obj_name(self):
+ if self._enum is not None:
+ return self._enum.name
+ else:
+ return "opcode#{}".format(self.obj_type)
+
+ @staticmethod
+ def get_insn_len(data: bytes) -> int:
+ (opcode_len,) = struct.unpack("@B", data[1:2])
+ return opcode_len & 0x3F
+
+ @classmethod
+ def _validate_len(cls, data, valid_options=None):
+ if len(data) < 4:
+ raise ValueError("opcode too short")
+ opcode_type, opcode_len = struct.unpack("@BB", data[:2])
+ if len(data) != ((opcode_len & 0x3F) * 4):
+ raise ValueError("wrong length")
+ if valid_options and len(data) not in valid_options:
+ raise ValueError(
+ "len {} not in {} for {}".format(
+ len(data), valid_options,
+ enum_from_int(cls.obj_enum_class, data[0])
+ )
+ )
+
+ @classmethod
+ def _validate(cls, data):
+ cls._validate_len(data)
+
+ @classmethod
+ def _parse(cls, data):
+ insn = IpFwInsn.from_buffer_copy(data[:4])
+ is_or = (insn.length & 0x40) != 0
+ is_not = (insn.length & 0x80) != 0
+ return cls(opcode=insn.opcode, is_or=is_or, is_not=is_not, arg1=insn.arg1)
+
+ @classmethod
+ def from_bytes(cls, data, attr_type_enum):
+ cls._validate(data)
+ opcode = cls._parse(data)
+ opcode._enum = attr_type_enum
+ return opcode
+
+ def __bytes__(self):
+ raise NotImplementedError()
+
+ def print_obj(self, prepend=""):
+ is_or = ""
+ if self.is_or:
+ is_or = " [OR]\\"
+ is_not = ""
+ if self.is_not:
+ is_not = "[!] "
+ print(
+ "{}{}len={} type={}({}){}{}".format(
+ prepend,
+ is_not,
+ len(bytes(self)),
+ self.obj_name,
+ self.obj_type,
+ self._print_obj_value(),
+ is_or,
+ )
+ )
+
+ def _print_obj_value(self):
+ raise NotImplementedError()
+
+ def print_obj_hex(self, prepend=""):
+ print(prepend)
+ print()
+ print(" ".join(["x{:02X}".format(b) for b in bytes(self)]))
+
+ @staticmethod
+ def parse_insns(data, attr_map):
+ ret = []
+ off = 0
+ while off + sizeof(IpFwInsn) <= len(data):
+ hdr = IpFwInsn.from_buffer_copy(data[off : off + sizeof(IpFwInsn)])
+ insn_len = (hdr.length & 0x3F) * 4
+ if off + insn_len > len(data):
+ raise ValueError("wrng length")
+ # print("GET insn type {} len {}".format(hdr.opcode, insn_len))
+ attr = attr_map.get(hdr.opcode, None)
+ if attr is None:
+ cls = InsnUnknown
+ type_enum = enum_from_int(BaseInsn.obj_enum_class, hdr.opcode)
+ else:
+ cls = attr["ad"].cls
+ type_enum = attr["ad"].val
+ insn = cls.from_bytes(data[off : off + insn_len], type_enum)
+ ret.append(insn)
+ off += insn_len
+
+ if off != len(data):
+ raise ValueError("empty space")
+ return ret
+
+
+class Insn(BaseInsn):
+ def __init__(self, opcode, is_or=False, is_not=False, arg1=0):
+ super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)
+
+ @classmethod
+ def _validate(cls, data):
+ cls._validate_len(data, [4])
+
+ def __bytes__(self):
+ length = self.ilen
+ if self.is_or:
+ length |= 0x40
+ if self.is_not:
+ length | 0x80
+ insn = IpFwInsn(opcode=self.obj_type, length=length, arg1=enum_or_int(self.arg1))
+ return bytes(insn)
+
+ def _print_obj_value(self):
+ return " arg1={}".format(self.arg1)
+
+
+class InsnUnknown(Insn):
+ @classmethod
+ def _validate(cls, data):
+ cls._validate_len(data)
+
+ @classmethod
+ def _parse(cls, data):
+ self = super()._parse(data)
+ self._data = data
+ return self
+
+ def __bytes__(self):
+ return self._data
+
+ def _print_obj_value(self):
+ return " " + " ".join(["x{:02X}".format(b) for b in self._data])
+
+
+class InsnEmpty(Insn):
+ @classmethod
+ def _validate(cls, data):
+ cls._validate_len(data, [4])
+ insn = IpFwInsn.from_buffer_copy(data[:4])
+ if insn.arg1 != 0:
+ raise ValueError("arg1 should be empty")
+
+ def _print_obj_value(self):
+ return ""
+
+
+class InsnComment(Insn):
+ def __init__(self, opcode=IpFwOpcode.O_NOP, is_or=False, is_not=False, arg1=0, comment=""):
+ super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)
+ if comment:
+ self.comment = comment
+ else:
+ self.comment = ""
+
+ @classmethod
+ def _validate(cls, data):
+ cls._validate_len(data)
+ if len(data) > 88:
+ raise ValueError("comment too long")
+
+ @classmethod
+ def _parse(cls, data):
+ self = super()._parse(data)
+ # Comment encoding can be anything,
+ # use utf-8 to ease debugging
+ max_len = 0
+ for b in range(4, len(data)):
+ if data[b] == b"\0":
+ break
+ max_len += 1
+ self.comment = data[4:max_len].decode("utf-8")
+ return self
+
+ def __bytes__(self):
+ ret = super().__bytes__()
+ comment_bytes = self.comment.encode("utf-8") + b"\0"
+ if len(comment_bytes) % 4 > 0:
+ comment_bytes += b"\0" * (4 - (len(comment_bytes) % 4))
+ ret += comment_bytes
+ return ret
+
+ def _print_obj_value(self):
+ return " comment='{}'".format(self.comment)
+
+
+class InsnProto(Insn):
+ def __init__(self, opcode=IpFwOpcode.O_PROTO, is_or=False, is_not=False, arg1=0):
+ super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)
+
+ def _print_obj_value(self):
+ known_map = {6: "TCP", 17: "UDP", 41: "IPV6"}
+ proto = self.arg1
+ if proto in known_map:
+ return " proto={}".format(known_map[proto])
+ else:
+ return " proto=#{}".format(proto)
+
+
+class InsnU32(Insn):
+ def __init__(self, opcode, is_or=False, is_not=False, arg1=0, u32=0):
+ super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)
+ self.u32 = u32
+ self.ilen = 2
+
+ @classmethod
+ def _validate(cls, data):
+ cls._validate_len(data, [8])
+
+ @classmethod
+ def _parse(cls, data):
+ self = super()._parse(data[:4])
+ self.u32 = struct.unpack("@I", data[4:8])[0]
+ return self
+
+ def __bytes__(self):
+ return super().__bytes__() + struct.pack("@I", self.u32)
+
+ def _print_obj_value(self):
+ return " arg1={} u32={}".format(self.arg1, self.u32)
+
+
+class InsnProb(InsnU32):
+ def __init__(
+ self,
+ opcode=IpFwOpcode.O_PROB,
+ is_or=False,
+ is_not=False,
+ arg1=0,
+ u32=0,
+ prob=0.0,
+ ):
+ super().__init__(opcode, is_or=is_or, is_not=is_not)
+ self.prob = prob
+
+ @property
+ def prob(self):
+ return 1.0 * self.u32 / 0x7FFFFFFF
+
+ @prob.setter
+ def prob(self, prob: float):
+ self.u32 = int(prob * 0x7FFFFFFF)
+
+ def _print_obj_value(self):
+ return " prob={}".format(round(self.prob, 5))
+
+
+class InsnIp(InsnU32):
+ def __init__(self, opcode, is_or=False, is_not=False, arg1=0, u32=0, ip=None):
+ super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1, u32=u32)
+ if ip:
+ self.ip = ip
+
+ @property
+ def ip(self):
+ return socket.inet_ntop(socket.AF_INET, struct.pack("@I", self.u32))
+
+ @ip.setter
+ def ip(self, ip: str):
+ ip_bin = socket.inet_pton(socket.AF_INET, ip)
+ self.u32 = struct.unpack("@I", ip_bin)[0]
+
+ def _print_opcode_value(self):
+ return " ip={}".format(self.ip)
+
+
+class InsnTable(Insn):
+ @classmethod
+ def _validate(cls, data):
+ cls._validate_len(data, [4, 8])
+
+ @classmethod
+ def _parse(cls, data):
+ self = super()._parse(data)
+
+ if len(data) == 8:
+ (self.val,) = struct.unpack("@I", data[4:8])
+ self.ilen = 2
+ else:
+ self.val = None
+ return self
+
+ def __bytes__(self):
+ ret = super().__bytes__()
+ if getattr(self, "val", None) is not None:
+ ret += struct.pack("@I", self.val)
+ return ret
+
+ def _print_obj_value(self):
+ if getattr(self, "val", None) is not None:
+ return " table={} value={}".format(self.arg1, self.val)
+ else:
+ return " table={}".format(self.arg1)
+
+
+class InsnReject(Insn):
+ def __init__(self, opcode, is_or=False, is_not=False, arg1=0, mtu=None):
+ super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)
+ self.mtu = mtu
+ if self.mtu is not None:
+ self.ilen = 2
+
+ @classmethod
+ def _validate(cls, data):
+ cls._validate_len(data, [4, 8])
+
+ @classmethod
+ def _parse(cls, data):
+ self = super()._parse(data)
+
+ if len(data) == 8:
+ (self.mtu,) = struct.unpack("@I", data[4:8])
+ self.ilen = 2
+ else:
+ self.mtu = None
+ return self
+
+ def __bytes__(self):
+ ret = super().__bytes__()
+ if getattr(self, "mtu", None) is not None:
+ ret += struct.pack("@I", self.mtu)
+ return ret
+
+ def _print_obj_value(self):
+ code = enum_from_int(IcmpRejectCode, self.arg1)
+ if getattr(self, "mtu", None) is not None:
+ return " code={} mtu={}".format(code, self.mtu)
+ else:
+ return " code={}".format(code)
+
+
+class InsnPorts(Insn):
+ def __init__(self, opcode, is_or=False, is_not=False, arg1=0, port_pairs=[]):
+ super().__init__(opcode, is_or=is_or, is_not=is_not)
+ self.port_pairs = []
+ if port_pairs:
+ self.port_pairs = port_pairs
+
+ @classmethod
+ def _validate(cls, data):
+ if len(data) < 8:
+ raise ValueError("no ports specified")
+ cls._validate_len(data)
+
+ @classmethod
+ def _parse(cls, data):
+ self = super()._parse(data)
+
+ off = 4
+ port_pairs = []
+ while off + 4 <= len(data):
+ low, high = struct.unpack("@HH", data[off : off + 4])
+ port_pairs.append((low, high))
+ off += 4
+ self.port_pairs = port_pairs
+ return self
+
+ def __bytes__(self):
+ ret = super().__bytes__()
+ if getattr(self, "val", None) is not None:
+ ret += struct.pack("@I", self.val)
+ return ret
+
+ def _print_obj_value(self):
+ ret = []
+ for p in self.port_pairs:
+ if p[0] == p[1]:
+ ret.append(str(p[0]))
+ else:
+ ret.append("{}-{}".format(p[0], p[1]))
+ return " ports={}".format(",".join(ret))
+
+
+class IpFwInsnIp6(Structure):
+ _fields_ = [
+ ("o", IpFwInsn),
+ ("addr6", c_byte * 16),
+ ("mask6", c_byte * 16),
+ ]
+
+
+class InsnIp6(Insn):
+ def __init__(self, opcode, is_or=False, is_not=False, arg1=0, ip6=None, mask6=None):
+ super().__init__(opcode, is_or=is_or, is_not=is_not, arg1=arg1)
+ self.ip6 = ip6
+ self.mask6 = mask6
+ if mask6 is not None:
+ self.ilen = 9
+ else:
+ self.ilen = 5
+
+ @classmethod
+ def _validate(cls, data):
+ cls._validate_len(data, [4 + 16, 4 + 16 * 2])
+
+ @classmethod
+ def _parse(cls, data):
+ self = super()._parse(data)
+ self.ip6 = socket.inet_ntop(socket.AF_INET6, data[4:20])
+
+ if len(data) == 4 + 16 * 2:
+ self.mask6 = socket.inet_ntop(socket.AF_INET6, data[20:36])
+ self.ilen = 9
+ else:
+ self.mask6 = None
+ self.ilen = 5
+ return self
+
+ def __bytes__(self):
+ ret = super().__bytes__() + socket.inet_pton(socket.AF_INET6, self.ip6)
+ if self.mask6 is not None:
+ ret += socket.inet_pton(socket.AF_INET6, self.mask6)
+ return ret
+
+ def _print_obj_value(self):
+ if self.mask6:
+ return " ip6={}/{}".format(self.ip6, self.mask6)
+ else:
+ return " ip6={}".format(self.ip6)
+
+
+insn_attrs = prepare_attrs_map(
+ [
+ AttrDescr(IpFwOpcode.O_CHECK_STATE, Insn),
+ AttrDescr(IpFwOpcode.O_ACCEPT, InsnEmpty),
+ AttrDescr(IpFwOpcode.O_COUNT, InsnEmpty),
+
+ AttrDescr(IpFwOpcode.O_REJECT, InsnReject),
+ AttrDescr(IpFwOpcode.O_UNREACH6, Insn),
+ AttrDescr(IpFwOpcode.O_DENY, InsnEmpty),
+ AttrDescr(IpFwOpcode.O_DIVERT, Insn),
+ AttrDescr(IpFwOpcode.O_COUNT, InsnEmpty),
+ AttrDescr(IpFwOpcode.O_QUEUE, Insn),
+ AttrDescr(IpFwOpcode.O_PIPE, Insn),
+ AttrDescr(IpFwOpcode.O_SKIPTO, Insn),
+ AttrDescr(IpFwOpcode.O_NETGRAPH, Insn),
+ AttrDescr(IpFwOpcode.O_NGTEE, Insn),
+ AttrDescr(IpFwOpcode.O_DIVERT, Insn),
+ AttrDescr(IpFwOpcode.O_TEE, Insn),
+ AttrDescr(IpFwOpcode.O_CALLRETURN, Insn),
+ AttrDescr(IpFwOpcode.O_SETFIB, Insn),
+ AttrDescr(IpFwOpcode.O_SETDSCP, Insn),
+ AttrDescr(IpFwOpcode.O_REASS, InsnEmpty),
+ AttrDescr(IpFwOpcode.O_SETMARK, Insn),
+
+
+
+ AttrDescr(IpFwOpcode.O_NOP, InsnComment),
+ AttrDescr(IpFwOpcode.O_PROTO, InsnProto),
+ AttrDescr(IpFwOpcode.O_PROB, InsnProb),
+ AttrDescr(IpFwOpcode.O_IP_DST_ME, InsnEmpty),
+ AttrDescr(IpFwOpcode.O_IP_SRC_ME, InsnEmpty),
+ AttrDescr(IpFwOpcode.O_IP6_DST_ME, InsnEmpty),
+ AttrDescr(IpFwOpcode.O_IP6_SRC_ME, InsnEmpty),
+ AttrDescr(IpFwOpcode.O_IP_SRC, InsnIp),
+ AttrDescr(IpFwOpcode.O_IP_DST, InsnIp),
+ AttrDescr(IpFwOpcode.O_IP6_DST, InsnIp6),
+ AttrDescr(IpFwOpcode.O_IP6_SRC, InsnIp6),
+ AttrDescr(IpFwOpcode.O_IP_SRC_LOOKUP, InsnTable),
+ AttrDescr(IpFwOpcode.O_IP_DST_LOOKUP, InsnTable),
+ AttrDescr(IpFwOpcode.O_IP_SRCPORT, InsnPorts),
+ AttrDescr(IpFwOpcode.O_IP_DSTPORT, InsnPorts),
+ AttrDescr(IpFwOpcode.O_PROBE_STATE, Insn),
+ AttrDescr(IpFwOpcode.O_KEEP_STATE, Insn),
+ ]
+)
diff --git a/tests/atf_python/sys/netpfil/ipfw/ioctl.py b/tests/atf_python/sys/netpfil/ipfw/ioctl.py
new file mode 100644
index 000000000000..4c6d3f234c6c
--- /dev/null
+++ b/tests/atf_python/sys/netpfil/ipfw/ioctl.py
@@ -0,0 +1,511 @@
+#!/usr/bin/env python3
+import os
+import socket
+import struct
+import subprocess
+import sys
+from ctypes import c_byte
+from ctypes import c_char
+from ctypes import c_int
+from ctypes import c_long
+from ctypes import c_uint32
+from ctypes import c_uint8
+from ctypes import c_ulong
+from ctypes import c_ushort
+from ctypes import sizeof
+from ctypes import Structure
+from enum import Enum
+from typing import Any
+from typing import Dict
+from typing import List
+from typing import NamedTuple
+from typing import Optional
+from typing import Union
+
+import pytest
+from atf_python.sys.netpfil.ipfw.insns import BaseInsn
+from atf_python.sys.netpfil.ipfw.insns import insn_attrs
+from atf_python.sys.netpfil.ipfw.ioctl_headers import IpFwTableLookupType
+from atf_python.sys.netpfil.ipfw.ioctl_headers import IpFwTlvType
+from atf_python.sys.netpfil.ipfw.ioctl_headers import Op3CmdType
+from atf_python.sys.netpfil.ipfw.utils import align8
+from atf_python.sys.netpfil.ipfw.utils import AttrDescr
+from atf_python.sys.netpfil.ipfw.utils import enum_from_int
+from atf_python.sys.netpfil.ipfw.utils import prepare_attrs_map
+
+
+class IpFw3OpHeader(Structure):
+ _fields_ = [
+ ("opcode", c_ushort),
+ ("version", c_ushort),
+ ("reserved1", c_ushort),
+ ("reserved2", c_ushort),
+ ]
+
+
+class IpFwObjTlv(Structure):
+ _fields_ = [
+ ("n_type", c_ushort),
+ ("flags", c_ushort),
+ ("length", c_uint32),
+ ]
+
+
+class BaseTlv(object):
+ obj_enum_class = IpFwTlvType
+
+ def __init__(self, obj_type):
+ if isinstance(obj_type, Enum):
+ self.obj_type = obj_type.value
+ self._enum = obj_type
+ else:
+ self.obj_type = obj_type
+ self._enum = enum_from_int(self.obj_enum_class, obj_type)
+ self.obj_list = []
+
+ def add_obj(self, obj):
+ self.obj_list.append(obj)
+
+ @property
+ def len(self):
+ return len(bytes(self))
+
+ @property
+ def obj_name(self):
+ if self._enum is not None:
+ return self._enum.name
+ else:
+ return "tlv#{}".format(self.obj_type)
+
+ def print_hdr(self, prepend=""):
+ print(
+ "{}len={} type={}({}){}".format(
+ prepend, self.len, self.obj_name, self.obj_type, self._print_obj_value()
+ )
+ )
+
+ def print_obj(self, prepend=""):
+ self.print_hdr(prepend)
+ prepend = " " + prepend
+ for obj in self.obj_list:
+ obj.print_obj(prepend)
+
+ def print_obj_hex(self, prepend=""):
+ print(prepend)
+ print()
+ print(" ".join(["x{:02X}".format(b) for b in bytes(self)]))
+
+ @classmethod
+ def _validate(cls, data):
+ if len(data) < sizeof(IpFwObjTlv):
+ raise ValueError("TLV too short")
+ hdr = IpFwObjTlv.from_buffer_copy(data[: sizeof(IpFwObjTlv)])
+ if len(data) != hdr.length:
+ raise ValueError("wrong TLV size")
+
+ @classmethod
+ def _parse(cls, data, attr_map):
+ hdr = IpFwObjTlv.from_buffer_copy(data[: sizeof(IpFwObjTlv)])
+ return cls(hdr.n_type)
+
+ @classmethod
+ def from_bytes(cls, data, attr_map=None):
+ cls._validate(data)
+ obj = cls._parse(data, attr_map)
+ return obj
+
+ def __bytes__(self):
+ raise NotImplementedError()
+
+ def _print_obj_value(self):
+ return " " + " ".join(
+ ["x{:02X}".format(b) for b in self._data[sizeof(IpFwObjTlv) :]]
+ )
+
+ def as_hexdump(self):
+ return " ".join(["x{:02X}".format(b) for b in bytes(self)])
+
+
+class UnknownTlv(BaseTlv):
+ def __init__(self, obj_type, data):
+ super().__init__(obj_type)
+ self._data = data
+
+ @classmethod
+ def _validate(cls, data):
+ if len(data) < sizeof(IpFwObjNTlv):
+ raise ValueError("TLV size is too short")
+ hdr = IpFwObjTlv.from_buffer_copy(data[: sizeof(IpFwObjTlv)])
+ if len(data) != hdr.length:
+ raise ValueError("wrong TLV size")
+
+ @classmethod
+ def _parse(cls, data, attr_map):
+ hdr = IpFwObjTlv.from_buffer_copy(data[: sizeof(IpFwObjTlv)])
+ self = cls(hdr.n_type, data)
+ return self
+
+ def __bytes__(self):
+ return self._data
+
+
+class Tlv(BaseTlv):
+ @staticmethod
+ def parse_tlvs(data, attr_map):
+ # print("PARSING " + " ".join(["x{:02X}".format(b) for b in data]))
+ off = 0
+ ret = []
+ while off + sizeof(IpFwObjTlv) <= len(data):
+ hdr = IpFwObjTlv.from_buffer_copy(data[off : off + sizeof(IpFwObjTlv)])
+ if off + hdr.length > len(data):
+ raise ValueError("TLV size do not match")
+ obj_data = data[off : off + hdr.length]
+ obj_descr = attr_map.get(hdr.n_type, None)
+ if obj_descr is None:
+ # raise ValueError("unknown child TLV {}".format(hdr.n_type))
+ cls = UnknownTlv
+ child_map = {}
+ else:
+ cls = obj_descr["ad"].cls
+ child_map = obj_descr.get("child", {})
+ # print("FOUND OBJECT type {}".format(cls))
+ # print()
+ obj = cls.from_bytes(obj_data, child_map)
+ ret.append(obj)
+ off += hdr.length
+ return ret
+
+
+class IpFwObjNTlv(Structure):
+ _fields_ = [
+ ("head", IpFwObjTlv),
+ ("idx", c_ushort),
+ ("n_set", c_uint8),
+ ("n_type", c_uint8),
+ ("spare", c_uint32),
+ ("name", c_char * 64),
+ ]
+
+
+class NTlv(Tlv):
+ def __init__(self, obj_type, idx=0, n_set=0, n_type=0, name=None):
+ super().__init__(obj_type)
+ self.n_idx = idx
+ self.n_set = n_set
+ self.n_type = n_type
+ self.n_name = name
+
+ @classmethod
+ def _validate(cls, data):
+ if len(data) != sizeof(IpFwObjNTlv):
+ raise ValueError("TLV size is not correct")
+ hdr = IpFwObjTlv.from_buffer_copy(data[: sizeof(IpFwObjTlv)])
+ if len(data) != hdr.length:
+ raise ValueError("wrong TLV size")
+
+ @classmethod
+ def _parse(cls, data, attr_map):
+ hdr = IpFwObjNTlv.from_buffer_copy(data[: sizeof(IpFwObjNTlv)])
+ name = hdr.name.decode("utf-8")
+ self = cls(hdr.head.n_type, hdr.idx, hdr.n_set, hdr.n_type, name)
+ return self
+
+ def __bytes__(self):
+ name_bytes = self.n_name.encode("utf-8")
+ if len(name_bytes) < 64:
+ name_bytes += b"\0" * (64 - len(name_bytes))
+ hdr = IpFwObjNTlv(
+ head=IpFwObjTlv(n_type=self.obj_type, length=sizeof(IpFwObjNTlv)),
+ idx=self.n_idx,
+ n_set=self.n_set,
+ n_type=self.n_type,
+ name=name_bytes[:64],
+ )
+ return bytes(hdr)
+
+ def _print_obj_value(self):
+ return " " + "type={} set={} idx={} name={}".format(
+ self.n_type, self.n_set, self.n_idx, self.n_name
+ )
+
+
+class IpFwObjCTlv(Structure):
+ _fields_ = [
+ ("head", IpFwObjTlv),
+ ("count", c_uint32),
+ ("objsize", c_ushort),
+ ("version", c_uint8),
+ ("flags", c_uint8),
+ ]
+
+
+class CTlv(Tlv):
+ def __init__(self, obj_type, obj_list=[]):
+ super().__init__(obj_type)
+ if obj_list:
+ self.obj_list.extend(obj_list)
+
+ @classmethod
+ def _validate(cls, data):
+ if len(data) < sizeof(IpFwObjCTlv):
+ raise ValueError("TLV too short")
+ hdr = IpFwObjCTlv.from_buffer_copy(data[: sizeof(IpFwObjCTlv)])
+ if len(data) != hdr.head.length:
+ raise ValueError("wrong TLV size")
+
+ @classmethod
+ def _parse(cls, data, attr_map):
+ hdr = IpFwObjCTlv.from_buffer_copy(data[: sizeof(IpFwObjCTlv)])
+ tlv_list = cls.parse_tlvs(data[sizeof(IpFwObjCTlv) :], attr_map)
+ if len(tlv_list) != hdr.count:
+ raise ValueError("wrong number of objects")
+ self = cls(hdr.head.n_type, obj_list=tlv_list)
+ return self
+
+ def __bytes__(self):
+ ret = b""
+ for obj in self.obj_list:
+ ret += bytes(obj)
+ length = len(ret) + sizeof(IpFwObjCTlv)
+ if self.obj_list:
+ objsize = len(bytes(self.obj_list[0]))
+ else:
+ objsize = 0
+ hdr = IpFwObjCTlv(
+ head=IpFwObjTlv(n_type=self.obj_type, length=sizeof(IpFwObjNTlv)),
+ count=len(self.obj_list),
+ objsize=objsize,
+ )
+ return bytes(hdr) + ret
+
+ def _print_obj_value(self):
+ return ""
+
+
+class IpFwRule(Structure):
+ _fields_ = [
+ ("act_ofs", c_ushort),
+ ("cmd_len", c_ushort),
+ ("spare", c_ushort),
+ ("n_set", c_uint8),
+ ("flags", c_uint8),
+ ("rulenum", c_uint32),
+ ("n_id", c_uint32),
+ ]
+
+
+class RawRule(Tlv):
+ def __init__(self, obj_type=0, n_set=0, rulenum=0, obj_list=[]):
+ super().__init__(obj_type)
+ self.n_set = n_set
+ self.rulenum = rulenum
+ if obj_list:
+ self.obj_list.extend(obj_list)
+
+ @classmethod
+ def _validate(cls, data):
+ min_size = sizeof(IpFwRule)
+ if len(data) < min_size:
+ raise ValueError("rule TLV too short")
+ rule = IpFwRule.from_buffer_copy(data[:min_size])
+ if len(data) != min_size + rule.cmd_len * 4:
+ raise ValueError("rule TLV cmd_len incorrect")
+
+ @classmethod
+ def _parse(cls, data, attr_map):
+ hdr = IpFwRule.from_buffer_copy(data[: sizeof(IpFwRule)])
+ self = cls(
+ n_set=hdr.n_set,
+ rulenum=hdr.rulenum,
+ obj_list=BaseInsn.parse_insns(data[sizeof(IpFwRule) :], insn_attrs),
+ )
+ return self
+
+ def __bytes__(self):
+ act_ofs = 0
+ cmd_len = 0
+ ret = b""
+ for obj in self.obj_list:
+ if obj.is_action and act_ofs == 0:
+ act_ofs = cmd_len
+ obj_bytes = bytes(obj)
+ cmd_len += len(obj_bytes) // 4
+ ret += obj_bytes
+
+ hdr = IpFwRule(
+ act_ofs=act_ofs,
+ cmd_len=cmd_len,
+ n_set=self.n_set,
+ rulenum=self.rulenum,
+ )
+ return bytes(hdr) + ret
+
+ @property
+ def obj_name(self):
+ return "rule#{}".format(self.rulenum)
+
+ def _print_obj_value(self):
+ cmd_len = sum([len(bytes(obj)) for obj in self.obj_list]) // 4
+ return " set={} cmd_len={}".format(self.n_set, cmd_len)
+
+
+class CTlvRule(CTlv):
+ def __init__(self, obj_type=IpFwTlvType.IPFW_TLV_RULE_LIST, obj_list=[]):
+ super().__init__(obj_type, obj_list)
+
+ @classmethod
+ def _parse(cls, data, attr_map):
+ chdr = IpFwObjCTlv.from_buffer_copy(data[: sizeof(IpFwObjCTlv)])
+ rule_list = []
+ off = sizeof(IpFwObjCTlv)
+ while off + sizeof(IpFwRule) <= len(data):
+ hdr = IpFwRule.from_buffer_copy(data[off : off + sizeof(IpFwRule)])
+ rule_len = sizeof(IpFwRule) + hdr.cmd_len * 4
+ # print("FOUND RULE len={} cmd_len={}".format(rule_len, hdr.cmd_len))
+ if off + rule_len > len(data):
+ raise ValueError("wrong rule size")
+ rule = RawRule.from_bytes(data[off : off + rule_len])
+ rule_list.append(rule)
+ off += align8(rule_len)
+ if off != len(data):
+ raise ValueError("rule bytes left: off={} len={}".format(off, len(data)))
+ return cls(chdr.head.n_type, obj_list=rule_list)
+
+ # XXX: _validate
+
+ def __bytes__(self):
+ ret = b""
+ for rule in self.obj_list:
+ rule_bytes = bytes(rule)
+ remainder = len(rule_bytes) % 8
+ if remainder > 0:
+ rule_bytes += b"\0" * (8 - remainder)
+ ret += rule_bytes
+ hdr = IpFwObjCTlv(
+ head=IpFwObjTlv(
+ n_type=self.obj_type, length=len(ret) + sizeof(IpFwObjCTlv)
+ ),
+ count=len(self.obj_list),
+ )
+ return bytes(hdr) + ret
+
+
+class BaseIpFwMessage(object):
+ messages = []
+
+ def __init__(self, msg_type, obj_list=[]):
+ if isinstance(msg_type, Enum):
+ self.obj_type = msg_type.value
+ self._enum = msg_type
+ else:
+ self.obj_type = msg_type
+ self._enum = enum_from_int(self.messages, self.obj_type)
+ self.obj_list = []
+ if obj_list:
+ self.obj_list.extend(obj_list)
+
+ def add_obj(self, obj):
+ self.obj_list.append(obj)
+
+ def get_obj(self, obj_type):
+ obj_type_raw = enum_or_int(obj_type)
+ for obj in self.obj_list:
+ if obj.obj_type == obj_type_raw:
+ return obj
+ return None
+
+ @staticmethod
+ def parse_header(data: bytes):
+ if len(data) < sizeof(IpFw3OpHeader):
+ raise ValueError("length less than op3 message header")
+ return IpFw3OpHeader.from_buffer_copy(data), sizeof(IpFw3OpHeader)
+
+ def parse_obj_list(self, data: bytes):
+ off = 0
+ while off < len(data):
+ # print("PARSE off={} rem={}".format(off, len(data) - off))
+ hdr = IpFwObjTlv.from_buffer_copy(data[off : off + sizeof(IpFwObjTlv)])
+ # print(" tlv len {}".format(hdr.length))
+ if hdr.length + off > len(data):
+ raise ValueError("TLV too big")
+ tlv = Tlv(hdr.n_type, data[off : off + hdr.length])
+ self.add_obj(tlv)
+ off += hdr.length
+
+ def is_type(self, msg_type):
+ return enum_or_int(msg_type) == self.msg_type
+
+ @property
+ def obj_name(self):
+ if self._enum is not None:
+ return self._enum.name
+ else:
+ return "msg#{}".format(self.obj_type)
+
+ def print_hdr(self, prepend=""):
+ print("{}len={}, type={}".format(prepend, len(bytes(self)), self.obj_name))
+
+ @classmethod
+ def from_bytes(cls, data):
+ try:
+ hdr, hdrlen = cls.parse_header(data)
+ self = cls(hdr.opcode)
+ self._orig_data = data
+ except ValueError as e:
+ print("Failed to parse op3 header: {}".format(e))
+ cls.print_as_bytes(data)
+ raise
+ tlv_list = Tlv.parse_tlvs(data[hdrlen:], self.attr_map)
+ self.obj_list.extend(tlv_list)
+ return self
+
+ def __bytes__(self):
+ ret = bytes(IpFw3OpHeader(opcode=self.obj_type))
+ for obj in self.obj_list:
+ ret += bytes(obj)
+ return ret
+
+ def print_obj(self):
+ self.print_hdr()
+ for obj in self.obj_list:
+ obj.print_obj(" ")
+
+ @staticmethod
+ def print_as_bytes(data: bytes, descr: str):
+ print("===vv {} (len:{:3d}) vv===".format(descr, len(data)))
+ off = 0
+ step = 16
+ while off < len(data):
+ for i in range(step):
+ if off + i < len(data):
+ print(" {:02X}".format(data[off + i]), end="")
+ print("")
+ off += step
+ print("--------------------")
+
+
+rule_attrs = prepare_attrs_map(
+ [
+ AttrDescr(
+ IpFwTlvType.IPFW_TLV_TBLNAME_LIST,
+ CTlv,
+ [
+ AttrDescr(IpFwTlvType.IPFW_TLV_TBL_NAME, NTlv),
+ AttrDescr(IpFwTlvType.IPFW_TLV_STATE_NAME, NTlv),
+ AttrDescr(IpFwTlvType.IPFW_TLV_EACTION, NTlv),
+ ],
+ True,
+ ),
+ AttrDescr(IpFwTlvType.IPFW_TLV_RULE_LIST, CTlvRule),
+ ]
+)
+
+
+class IpFwXRule(BaseIpFwMessage):
+ messages = [Op3CmdType.IP_FW_XADD]
+ attr_map = rule_attrs
+
+
+legacy_classes = []
+set3_classes = []
+get3_classes = [IpFwXRule]
diff --git a/tests/atf_python/sys/netpfil/ipfw/ioctl_headers.py b/tests/atf_python/sys/netpfil/ipfw/ioctl_headers.py
new file mode 100644
index 000000000000..dc5c74bd1ad1
--- /dev/null
+++ b/tests/atf_python/sys/netpfil/ipfw/ioctl_headers.py
@@ -0,0 +1,90 @@
+from enum import Enum
+
+
+class Op3CmdType(Enum):
+ IP_FW_TABLE_XADD = 86
+ IP_FW_TABLE_XDEL = 87
+ IP_FW_TABLE_XGETSIZE = 88
+ IP_FW_TABLE_XLIST = 89
+ IP_FW_TABLE_XDESTROY = 90
+ IP_FW_TABLES_XLIST = 92
+ IP_FW_TABLE_XINFO = 93
+ IP_FW_TABLE_XFLUSH = 94
+ IP_FW_TABLE_XCREATE = 95
+ IP_FW_TABLE_XMODIFY = 96
+ IP_FW_XGET = 97
+ IP_FW_XADD = 98
+ IP_FW_XDEL = 99
+ IP_FW_XMOVE = 100
+ IP_FW_XZERO = 101
+ IP_FW_XRESETLOG = 102
+ IP_FW_SET_SWAP = 103
+ IP_FW_SET_MOVE = 104
+ IP_FW_SET_ENABLE = 105
+ IP_FW_TABLE_XFIND = 106
+ IP_FW_XIFLIST = 107
+ IP_FW_TABLES_ALIST = 108
+ IP_FW_TABLE_XSWAP = 109
+ IP_FW_TABLE_VLIST = 110
+ IP_FW_NAT44_XCONFIG = 111
+ IP_FW_NAT44_DESTROY = 112
+ IP_FW_NAT44_XGETCONFIG = 113
+ IP_FW_NAT44_LIST_NAT = 114
+ IP_FW_NAT44_XGETLOG = 115
+ IP_FW_DUMP_SOPTCODES = 116
+ IP_FW_DUMP_SRVOBJECTS = 117
+ IP_FW_NAT64STL_CREATE = 130
+ IP_FW_NAT64STL_DESTROY = 131
+ IP_FW_NAT64STL_CONFIG = 132
+ IP_FW_NAT64STL_LIST = 133
+ IP_FW_NAT64STL_STATS = 134
+ IP_FW_NAT64STL_RESET_STATS = 135
+ IP_FW_NAT64LSN_CREATE = 140
+ IP_FW_NAT64LSN_DESTROY = 141
+ IP_FW_NAT64LSN_CONFIG = 142
+ IP_FW_NAT64LSN_LIST = 143
+ IP_FW_NAT64LSN_STATS = 144
+ IP_FW_NAT64LSN_LIST_STATES = 145
+ IP_FW_NAT64LSN_RESET_STATS = 146
+ IP_FW_NPTV6_CREATE = 150
+ IP_FW_NPTV6_DESTROY = 151
+ IP_FW_NPTV6_CONFIG = 152
+ IP_FW_NPTV6_LIST = 153
+ IP_FW_NPTV6_STATS = 154
+ IP_FW_NPTV6_RESET_STATS = 155
+ IP_FW_NAT64CLAT_CREATE = 160
+ IP_FW_NAT64CLAT_DESTROY = 161
+ IP_FW_NAT64CLAT_CONFIG = 162
+ IP_FW_NAT64CLAT_LIST = 163
+ IP_FW_NAT64CLAT_STATS = 164
+ IP_FW_NAT64CLAT_RESET_STATS = 165
+
+
+class IpFwTableLookupType(Enum):
+ LOOKUP_DST_IP = 0
+ LOOKUP_SRC_IP = 1
+ LOOKUP_DST_PORT = 2
+ LOOKUP_SRC_PORT = 3
+ LOOKUP_UID = 4
+ LOOKUP_JAIL = 5
+ LOOKUP_DSCP = 6
+ LOOKUP_DST_MAC = 7
+ LOOKUP_SRC_MAC = 8
+ LOOKUP_MARK = 9
+
+
+class IpFwTlvType(Enum):
+ IPFW_TLV_TBL_NAME = 1
+ IPFW_TLV_TBLNAME_LIST = 2
+ IPFW_TLV_RULE_LIST = 3
+ IPFW_TLV_DYNSTATE_LIST = 4
+ IPFW_TLV_TBL_ENT = 5
+ IPFW_TLV_DYN_ENT = 6
+ IPFW_TLV_RULE_ENT = 7
+ IPFW_TLV_TBLENT_LIST = 8
+ IPFW_TLV_RANGE = 9
+ IPFW_TLV_EACTION = 10
+ IPFW_TLV_COUNTERS = 11
+ IPFW_TLV_OBJDATA = 12
+ IPFW_TLV_STATE_NAME = 14
+ IPFW_TLV_EACTION_BASE = 1000
diff --git a/tests/atf_python/sys/netpfil/ipfw/ipfw.py b/tests/atf_python/sys/netpfil/ipfw/ipfw.py
new file mode 100644
index 000000000000..0bcc907eeab8
--- /dev/null
+++ b/tests/atf_python/sys/netpfil/ipfw/ipfw.py
@@ -0,0 +1,118 @@
+#!/usr/bin/env python3
+import os
+import socket
+import struct
+import subprocess
+import sys
+from ctypes import c_byte
+from ctypes import c_char
+from ctypes import c_int
+from ctypes import c_long
+from ctypes import c_uint32
+from ctypes import c_uint8
+from ctypes import c_ulong
+from ctypes import c_ushort
+from ctypes import sizeof
+from ctypes import Structure
+from enum import Enum
+from typing import Any
+from typing import Dict
+from typing import List
+from typing import NamedTuple
+from typing import Optional
+from typing import Union
+
+from atf_python.sys.netpfil.ipfw.ioctl import get3_classes
+from atf_python.sys.netpfil.ipfw.ioctl import legacy_classes
+from atf_python.sys.netpfil.ipfw.ioctl import set3_classes
+from atf_python.sys.netpfil.ipfw.utils import AttrDescr
+from atf_python.sys.netpfil.ipfw.utils import enum_from_int
+from atf_python.sys.netpfil.ipfw.utils import enum_or_int
+from atf_python.sys.netpfil.ipfw.utils import prepare_attrs_map
+
+
+class DebugHeader(Structure):
+ _fields_ = [
+ ("cmd_type", c_ushort),
+ ("spare1", c_ushort),
+ ("opt_name", c_uint32),
+ ("total_len", c_uint32),
+ ("spare2", c_uint32),
+ ]
+
+
+class DebugType(Enum):
+ DO_CMD = 1
+ DO_SET3 = 2
+ DO_GET3 = 3
+
+
+class DebugIoReader(object):
+ HANDLER_CLASSES = {
+ DebugType.DO_CMD: legacy_classes,
+ DebugType.DO_SET3: set3_classes,
+ DebugType.DO_GET3: get3_classes,
+ }
+
+ def __init__(self, ipfw_path):
+ self._msgmap = self.build_msgmap()
+ self.ipfw_path = ipfw_path
+
+ def build_msgmap(self):
+ xmap = {}
+ for debug_type, handler_classes in self.HANDLER_CLASSES.items():
+ debug_type = enum_or_int(debug_type)
+ if debug_type not in xmap:
+ xmap[debug_type] = {}
+ for handler_class in handler_classes:
+ for msg in handler_class.messages:
+ xmap[debug_type][enum_or_int(msg)] = handler_class
+ return xmap
+
+ def print_obj_header(self, hdr):
+ debug_type = "#{}".format(hdr.cmd_type)
+ for _type in self.HANDLER_CLASSES.keys():
+ if _type.value == hdr.cmd_type:
+ debug_type = _type.name.lower()
+ break
+ print(
+ "@@ record for {} len={} optname={}".format(
+ debug_type, hdr.total_len, hdr.opt_name
+ )
+ )
+
+ def parse_record(self, data):
+ hdr = DebugHeader.from_buffer_copy(data[: sizeof(DebugHeader)])
+ data = data[sizeof(DebugHeader) :]
+ cls = self._msgmap[hdr.cmd_type].get(hdr.opt_name)
+ if cls is not None:
+ return cls.from_bytes(data)
+ raise ValueError(
+ "unsupported cmd_type={} opt_name={}".format(hdr.cmd_type, hdr.opt_name)
+ )
+
+ def get_record_from_stdin(self):
+ data = sys.stdin.buffer.peek(sizeof(DebugHeader))
+ if len(data) == 0:
+ return None
+
+ hdr = DebugHeader.from_buffer_copy(data)
+ data = sys.stdin.buffer.read(hdr.total_len)
+ return self.parse_record(data)
+
+ def get_records_from_buffer(self, data):
+ off = 0
+ ret = []
+ while off + sizeof(DebugHeader) <= len(data):
+ hdr = DebugHeader.from_buffer_copy(data[off : off + sizeof(DebugHeader)])
+ ret.append(self.parse_record(data[off : off + hdr.total_len]))
+ off += hdr.total_len
+ return ret
+
+ def run_ipfw(self, cmd: str) -> bytes:
+ args = [self.ipfw_path, "-xqn"] + cmd.split()
+ r = subprocess.run(args, capture_output=True)
+ return r.stdout
+
+ def get_records(self, cmd: str):
+ return self.get_records_from_buffer(self.run_ipfw(cmd))
diff --git a/tests/atf_python/sys/netpfil/ipfw/utils.py b/tests/atf_python/sys/netpfil/ipfw/utils.py
new file mode 100644
index 000000000000..0b3e9570d216
--- /dev/null
+++ b/tests/atf_python/sys/netpfil/ipfw/utils.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python3
+
+import os
+import socket
+import struct
+import subprocess
+import sys
+from enum import Enum
+from typing import Dict
+from typing import List
+from typing import Optional
+from typing import Union
+from typing import Any
+from typing import NamedTuple
+import pytest
+
+
+def roundup2(val: int, num: int) -> int:
+ if val % num:
+ return (val | (num - 1)) + 1
+ else:
+ return val
+
+
+def align8(val: int) -> int:
+ return roundup2(val, 8)
+
+
+def enum_or_int(val) -> int:
+ if isinstance(val, Enum):
+ return val.value
+ return val
+
+
+def enum_from_int(enum_class: Enum, val) -> Enum:
+ if isinstance(val, Enum):
+ return val
+ for item in enum_class:
+ if val == item.value:
+ return item
+ return None
+
+
+class AttrDescr(NamedTuple):
+ val: Enum
+ cls: Any
+ child_map: Any = None
+ is_array: bool = False
+
+
+def prepare_attrs_map(attrs: List[AttrDescr]) -> Dict[str, Dict]:
+ ret = {}
+ for ad in attrs:
+ ret[ad.val.value] = {"ad": ad}
+ if ad.child_map:
+ ret[ad.val.value]["child"] = prepare_attrs_map(ad.child_map)
+ ret[ad.val.value]["is_array"] = ad.is_array
+ return ret
+
+
+
diff --git a/tests/atf_python/utils.py b/tests/atf_python/utils.py
new file mode 100644
index 000000000000..26911c12aef3
--- /dev/null
+++ b/tests/atf_python/utils.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python3
+import os
+import pwd
+from ctypes import CDLL
+from ctypes import get_errno
+from ctypes.util import find_library
+from typing import Dict
+from typing import List
+from typing import Optional
+
+import pytest
+
+
+def nodeid_to_method_name(nodeid: str) -> str:
+ """file_name.py::ClassName::method_name[parametrize] -> method_name"""
+ return nodeid.split("::")[-1].split("[")[0]
+
+
+class LibCWrapper(object):
+ def __init__(self):
+ path: Optional[str] = find_library("c")
+ if path is None:
+ raise RuntimeError("libc not found")
+ self._libc = CDLL(path, use_errno=True)
+
+ def modfind(self, mod_name: str) -> int:
+ if self._libc.modfind(bytes(mod_name, encoding="ascii")) == -1:
+ return get_errno()
+ return 0
+
+ def kldload(self, kld_name: str) -> int:
+ if self._libc.kldload(bytes(kld_name, encoding="ascii")) == -1:
+ return get_errno()
+ return 0
+
+ def jail_attach(self, jid: int) -> int:
+ if self._libc.jail_attach(jid) != 0:
+ return get_errno()
+ return 0
+
+
+libc = LibCWrapper()
+
+
+class BaseTest(object):
+ NEED_ROOT: bool = False # True if the class needs root privileges for the setup
+ TARGET_USER = None # Set to the target user by the framework
+ REQUIRED_MODULES: List[str] = []
+ SKIP_MODULES: List[str] = []
+
+ def require_module(self, mod_name: str, skip=True):
+ error_code = libc.modfind(mod_name)
+ if error_code == 0:
+ return
+ err_str = os.strerror(error_code)
+ txt = "kernel module '{}' not available: {}".format(mod_name, err_str)
+ if skip:
+ pytest.skip(txt)
+ else:
+ raise ValueError(txt)
+
+ def skip_module(self, mod_name: str):
+ error_code = libc.modfind(mod_name)
+ if error_code == 0:
+ txt = "kernel module '{}' loaded, skip test".format(mod_name)
+ pytest.skip(txt)
+ return
+
+ def _check_modules(self):
+ for mod_name in self.REQUIRED_MODULES:
+ self.require_module(mod_name)
+ for mod_name in self.SKIP_MODULES:
+ self.skip_module(mod_name)
+
+ @property
+ def atf_vars(self) -> Dict[str, str]:
+ px = "_ATF_VAR_"
+ return {k[len(px):]: v for k, v in os.environ.items() if k.startswith(px)}
+
+ def drop_privileges_user(self, user: str):
+ uid = pwd.getpwnam(user)[2]
+ print("Dropping privs to {}/{}".format(user, uid))
+ os.setuid(uid)
+
+ def drop_privileges(self):
+ if self.TARGET_USER:
+ if self.TARGET_USER == "unprivileged":
+ user = self.atf_vars["unprivileged-user"]
+ else:
+ user = self.TARGET_USER
+ self.drop_privileges_user(user)
+
+ @property
+ def test_id(self) -> str:
+ # 'test_ip6_output.py::TestIP6Output::test_output6_pktinfo[ipandif] (setup)'
+ return os.environ.get("PYTEST_CURRENT_TEST").split(" ")[0]
+
+ def setup_method(self, method):
+ """Run all pre-requisits for the test execution"""
+ self._check_modules()
diff --git a/tests/ci/Makefile b/tests/ci/Makefile
new file mode 100644
index 000000000000..48e638fdb79c
--- /dev/null
+++ b/tests/ci/Makefile
@@ -0,0 +1,265 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 The FreeBSD Foundation
+#
+# This software was developed by Cybermancer Infosec <bofh@FreeBSD.org>
+# under sponsorship from the FreeBSD Foundation.
+#
+# Makefile for CI testing.
+#
+# User-driven targets:
+# ci: Run CI tests
+# ci-smoke: Run smoke tests which is simply booting the image
+# ci-full: Run full tests
+#
+# Variables affecting the build process:
+# TARGET/TARGET_ARCH: architecture of built release (default: same as build host)
+# KERNELCONF: kernel configuration to use
+# USE_QEMU: Use QEMU for testing rather than bhyve
+#
+
+WORLDDIR?= ${.CURDIR}/../..
+RELEASEDIR= ${WORLDDIR}/release
+MAKECONF?= /dev/null
+SRCCONF?= /dev/null
+_MEMORY!=sysctl -n hw.physmem 2>/dev/null
+PARALLEL_JOBS!=sysctl -n hw.ncpu 2>/dev/null || nproc 2>/dev/null
+TOTAL_MEMORY!=expr ${_MEMORY} / 1073741824
+KERNCONF?= GENERIC
+LOCALBASE?= /usr/local
+EXTRA_MAKE_FLAGS?=
+
+.if !defined(TARGET) || empty(TARGET)
+TARGET= ${MACHINE}
+.endif
+.if !defined(TARGET_ARCH) || empty(TARGET_ARCH)
+. if ${TARGET} == ${MACHINE}
+TARGET_ARCH= ${MACHINE_ARCH}
+. else
+TARGET_ARCH= ${TARGET}
+. endif
+.endif
+IMAKE= ${MAKE} TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH}
+
+.if defined(CROSS_TOOLCHAIN) || !empty(CROSS_TOOLCHAIN)
+CROSS_TOOLCHAIN_PARAM= "CROSS_TOOLCHAIN=${CROSS_TOOLCHAIN}"
+.endif
+
+# Define OSRELEASE by using newvers.sh
+.if !defined(OSRELEASE) || empty(OSRELEASE)
+. for _V in TYPE BRANCH REVISION
+. if !defined(${_V}) || empty(${_V})
+${_V}!= eval $$(awk '/^${_V}=/{print}' ${.CURDIR}/../../sys/conf/newvers.sh); echo $$${_V}
+. endif
+. endfor
+. for _V in ${TARGET_ARCH}
+. if !empty(TARGET:M${_V})
+OSRELEASE= ${TYPE}-${REVISION}-${BRANCH}-${TARGET}
+VOLUME_LABEL= ${REVISION:C/[.-]/_/g}_${BRANCH:C/[.-]/_/g}_${TARGET}
+. else
+OSRELEASE= ${TYPE}-${REVISION}-${BRANCH}-${TARGET}-${TARGET_ARCH}
+VOLUME_LABEL= ${REVISION:C/[.-]/_/g}_${BRANCH:C/[.-]/_/g}_${TARGET_ARCH}
+. endif
+. endfor
+.endif
+
+.if exists(${.CURDIR}/tools/ci.conf) && !defined(CICONF)
+CICONF?= ${.CURDIR}/tools/ci.conf
+.endif
+SWAPSIZE?= 1g
+VMFS?= ufs
+FORMAT= raw
+CIIMAGE= ci-${OSRELEASE}-${GITREV}-${KERNCONF}.${FORMAT}
+CIDISK?= ${.OBJDIR}/${CIIMAGE}
+VMSIZE?= 6g
+CITYPE?= full
+META_TAR!=mktemp /tmp/meta.XXXXXX
+META_DIR!=mktemp -d /tmp/meta.XXXXXX
+META_DIROUT!=mktemp -d /tmp/meta.XXXXXX
+DISC_CAM!=truncate -s 128m /tmp/disk-cam
+EXTRA_DISK_NUM?=5
+DISK_NUMBERS!=jot - 1 ${EXTRA_DISK_NUM}
+BHYVE_EXTRA_DISK_PARAM?=
+BHYVE_EXTRA_DISK_PARAM+=-s 4:0,ahci-hd,/tmp/disk-cam
+.for i in ${DISK_NUMBERS}
+NEW_DISK!=truncate -s 128m /tmp/disk${i}
+BHYVE_EXTRA_DISK_PARAM+=-s $$((${i} + 4)):0,virtio-blk,/tmp/disk${i}
+CLEANFILES+=/tmp/disk${i}
+.endfor
+TEST_VM_NAME= ci-${OSRELEASE}-${GITREV}-${KERNCONF}
+.if ${TOTAL_MEMORY} >= 16
+VM_MEM=8
+.elif ${TOTAL_MEMORY} >=4
+VM_MEM=${TOTAL_MEMORY}
+.else
+echo "Please increase the memory to at least 4GB"
+exit 0
+.endif
+VM_MEM_SIZE?=${VM_MEM}g
+TIMEOUT_MS?=5400000
+TIMEOUT=$$((${TIMEOUT_MS} / 1000))
+TIMEOUT_EXPECT=$$((${TIMEOUT} - 60))
+TIMEOUT_VM=$$((${TIMEOUT_EXPECT} - 120))
+.if exists(${.CURDIR}/Makefile.${TARGET_ARCH})
+. include "${.CURDIR}/Makefile.${TARGET_ARCH}"
+.endif
+.if ${TARGET_ARCH} != ${MACHINE_ARCH}
+. if ( ${TARGET_ARCH} != "i386" ) || ( ${MACHINE_ARCH} != "amd64" )
+QEMUSTATIC=/usr/local/bin/qemu-${QEMU_ARCH}-static
+QEMUTGT=portinstall-qemu
+. endif
+.endif
+QEMUTGT?=
+QEMU_DEVICES?=
+QEMU_EXTRA_PARAM?=
+QEMU_MACHINE?=virt
+QEMUBIN=/usr/local/bin/qemu-system-${QEMU_ARCH}
+.if ${PARALLEL_JOBS} >= ${QEMU_MAX_CPU_COUNT}
+QEMU_CPU_COUNT=${QEMU_MAX_CPU_COUNT}
+.else
+QEMU_CPU_COUNT=${PARALLEL_JOBS}
+.endif
+.if ${VM_MEM} >= ${QEMU_MAX_MEM_SIZE}
+VM_MEM_SIZE=${QEMU_MAX_MEM_SIZE}g
+.else
+VM_MEM_SIZE=${VM_MEM}g
+.endif
+VMGUEST!=sysctl -n kern.vm_guest 2>/dev/null || true
+.if ${VMGUEST} != "none"
+USE_QEMU?=1
+.endif
+KLDFILEMONISLOADED!=kldload -q -n filemon 2>/dev/null && echo "1" || echo "0"
+.if ${KLDFILEMONISLOADED} == "1"
+METAMODE?=-DWITH_META_MODE
+.endif
+
+CLEANFILES+= ${.OBJDIR}/${CIIMAGE} ${.OBJDIR}/ci.img ${META_TAR}
+IMAGEDIR= ${.OBJDIR}/ci-buildimage
+CLEANDIRS+= ${IMAGEDIR}
+
+portinstall: portinstall-pkg portinstall-qemu portinstall-expect portinstall-${TARGET_ARCH:tl} .PHONY
+
+portinstall-pkg: .PHONY
+.if !exists(/usr/local/sbin/pkg-static)
+ env ASSUME_ALWAYS_YES=yes pkg bootstrap
+.endif
+
+portinstall-qemu: portinstall-pkg .PHONY
+.if !exists(/usr/local/bin/qemu-${QEMU_ARCH}-static)
+ env ASSUME_ALWAYS_YES=yes pkg install emulators/qemu-user-static
+.endif
+.if !exists(${QEMUBIN})
+ env ASSUME_ALWAYS_YES=yes pkg install emulators/qemu@nox11
+.endif
+
+portinstall-expect: portinstall-pkg .PHONY
+.if !exists(/usr/local/bin/expect)
+ env ASSUME_ALWAYS_YES=yes pkg install lang/expect
+.endif
+
+beforeclean: .PHONY
+ chflags -R noschg ${IMAGEDIR}
+
+.include <bsd.obj.mk>
+clean: beforeclean .PHONY
+
+cleandir: beforeclean .PHONY
+
+ci-buildworld: .PHONY
+ @echo "Building world for ${TARGET_ARCH}"
+ ${IMAKE} -j${PARALLEL_JOBS} -C ${WORLDDIR} ${METAMODE} \
+ ${CROSS_TOOLCHAIN_PARAM} __MAKE_CONF=${MAKECONF} SRCCONF=${SRCCONF} \
+ ${EXTRA_MAKE_FLAGS} buildworld > ${.CURDIR}/_.${TARGET_ARCH}.${.TARGET} 2>&1 || \
+ (echo "${.TARGET} failed, check _.${TARGET_ARCH}.${.TARGET} for details" ; false)
+
+
+ci-buildkernel: ci-buildworld-${TARGET_ARCH:tl} .PHONY
+ @echo "Building kernel for ${TARGET_ARCH}"
+ ${IMAKE} -j${PARALLEL_JOBS} -C ${WORLDDIR} ${METAMODE} \
+ ${CROSS_TOOLCHAIN_PARAM} __MAKE_CONF=${MAKECONF} SRCCONF=${SRCCONF} \
+ ${EXTRA_MAKE_FLAGS} KERNCONF=${KERNCONF} \
+ buildkernel > ${.CURDIR}/_.${TARGET_ARCH}.${.TARGET} 2>&1 || \
+ (echo "${.TARGET} failed, check _.${TARGET_ARCH}.${.TARGET} for details" ; false)
+
+ci-buildimage: ${QEMUTGT} ci-buildkernel-${TARGET_ARCH:tl} .PHONY
+ @echo "Building ci image for ${TARGET_ARCH}"
+ mkdir -p ${.OBJDIR}/${.TARGET}
+ env TARGET=${TARGET} TARGET_ARCH=${TARGET_ARCH} SWAPSIZE=${SWAPSIZE} \
+ QEMUSTATIC=${QEMUSTATIC} CITYPE=${CITYPE} \
+ ${RELEASEDIR}/scripts/mk-vmimage.sh \
+ -C ${RELEASEDIR}/tools/vmimage.subr -d ${.OBJDIR}/${.TARGET} -F ${VMFS} \
+ -i ${.OBJDIR}/ci.img -s ${VMSIZE} -f ${FORMAT} \
+ -S ${WORLDDIR} -o ${.OBJDIR}/${CIIMAGE} -c ${CICONF} \
+ > ${.CURDIR}/_.${TARGET_ARCH}.${.TARGET} 2>&1 || \
+ (echo "${.TARGET} failed, check _.${TARGET_ARCH}.${.TARGET} for details" ; false)
+ touch ${.TARGET}
+
+ci-set-smoke-var: .PHONY
+CITYPE=smoke
+
+ci-set-full-var: .PHONY
+CITYPE=full
+
+ci-create-meta: .PHONY
+ truncate -s 512M ${META_TAR}
+ tar rvf ${META_TAR} -C ${META_DIR} .
+
+ci-extract-meta: .PHONY
+ tar xfv ${META_TAR} -C ${META_DIROUT}
+ rm -rf ${META_TAR} ${META_DIR}
+ @echo "Extracted kyua reports to ${META_DIROUT}"
+
+ci-runtest: ci-buildimage-${TARGET_ARCH:tl} portinstall .PHONY
+.if ${MACHINE} == "amd64" && ( ${TARGET_ARCH} == "amd64" || ${TARGET_ARCH} == "i386" ) && ( !defined(USE_QEMU) || empty(USE_QEMU) )
+ /usr/sbin/bhyvectl --vm=${TEST_VM_NAME} --destroy || true
+ expect -c "set timeout ${TIMEOUT_EXPECT}; \
+ spawn /usr/bin/timeout -k 5s 30s /usr/sbin/bhyveload \
+ -c stdio -m ${VM_MEM_SIZE} -d ${CIDISK} ${TEST_VM_NAME}; \
+ expect { eof }; \
+ exit [lindex [wait] 3]"
+ expect -c "set timeout ${TIMEOUT_EXPECT}; \
+ spawn /usr/bin/timeout -k 60 ${TIMEOUT_VM} /usr/sbin/bhyve \
+ -c ${PARALLEL_JOBS} -m ${VM_MEM_SIZE} -A -H -P \
+ -s 0:0,hostbridge \
+ -s 1:0,lpc \
+ -s 2:0,virtio-blk,${CIDISK} \
+ -s 3:0,virtio-blk,${META_TAR} \
+ ${BHYVE_EXTRA_DISK_PARAM} \
+ -l com1,stdio \
+ ${TEST_VM_NAME}; \
+ expect { eof }"
+ /usr/sbin/bhyvectl --vm=${TEST_VM_NAME} --destroy
+.else
+ timeout -k 60 ${TIMEOUT_VM} ${QEMUBIN} \
+ -machine ${QEMU_MACHINE} \
+ -smp ${QEMU_CPU_COUNT} \
+ -m ${VM_MEM_SIZE} \
+ -nographic \
+ -no-reboot \
+ ${QEMU_EXTRA_PARAM} \
+ -device virtio-blk,drive=hd0 \
+ -device virtio-blk,drive=hd1 \
+ -blockdev driver=raw,node-name=hd0,file.driver=file,file.filename=${CIDISK} \
+ -blockdev driver=raw,node-name=hd1,file.driver=file,file.filename=${META_TAR} \
+ ${QEMU_DEVICES}
+.endif
+
+ci-checktarget: .PHONY
+.if ${TARGET_ARCH} != "aarch64" && \
+ ${TARGET_ARCH} != "amd64" && \
+ ${TARGET_ARCH} != "armv7" && \
+ ${TARGET_ARCH} != "powerpc64" && \
+ ${TARGET_ARCH} != "powerpc64le" && \
+ ${TARGET_ARCH} != "riscv64"
+ @false
+.ERROR:
+ @echo "Error: ${TARGET_ARCH} is not supported on ${TYPE} ${REVISION} ${BRANCH}"
+.endif
+
+ci-smoke: ci-set-smoke-var ci-create-meta ci-checktarget .WAIT ci-runtest-${TARGET_ARCH:tl} .PHONY
+
+ci-full: ci-set-full-var ci-create-meta ci-checktarget .WAIT ci-runtest-${TARGET_ARCH:tl} .WAIT ci-extract-meta .PHONY
+
+ci: ci-${CITYPE:tl} .PHONY
+
+.include "${RELEASEDIR}/Makefile.inc1"
diff --git a/tests/ci/Makefile.aarch64 b/tests/ci/Makefile.aarch64
new file mode 100644
index 000000000000..5a62e73d8eaa
--- /dev/null
+++ b/tests/ci/Makefile.aarch64
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 The FreeBSD Foundation
+#
+# This software was developed by Cybermancer Infosec <bofh@FreeBSD.org>
+# under sponsorship from the FreeBSD Foundation.
+#
+# CI Makefile for aarch64.
+#
+QEMU_ARCH=aarch64
+QEMU_DEVICES=-device ahci,id=ahci
+QEMU_EXTRA_PARAM=-bios /usr/local/share/u-boot/u-boot-qemu-arm64/u-boot.bin -cpu cortex-a57
+QEMU_MAX_CPU_COUNT=64
+QEMU_MAX_MEM_SIZE=64
+
+portinstall-aarch64: portinstall-pkg .PHONY
+.if !exists(/usr/local/share/u-boot/u-boot-qemu-arm64/u-boot.bin)
+ env ASSUME_ALWAYS_YES=yes pkg install sysutils/u-boot-qemu-arm64
+.endif
+
+# NOTE: Nothing should be changed below this line unless explicitly required.
+
+ci-buildworld-aarch64: ci-buildworld .PHONY
+
+ci-buildkernel-aarch64: ci-buildkernel .PHONY
+
+ci-buildimage-aarch64: ci-buildimage .PHONY
+
+ci-runtest-aarch64: ci-runtest .PHONY
diff --git a/tests/ci/Makefile.amd64 b/tests/ci/Makefile.amd64
new file mode 100644
index 000000000000..2f71f3f8c371
--- /dev/null
+++ b/tests/ci/Makefile.amd64
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 The FreeBSD Foundation
+#
+# This software was developed by Cybermancer Infosec <bofh@FreeBSD.org>
+# under sponsorship from the FreeBSD Foundation.
+#
+# CI Makefile for amd64.
+#
+QEMU_ARCH=x86_64
+QEMU_MACHINE=q35
+QEMU_MAX_CPU_COUNT=256
+QEMU_MAX_MEM_SIZE=128
+
+portinstall-amd64: portinstall-pkg .PHONY
+ @true
+
+# NOTE: Nothing should be changed below this line unless explicitly required.
+
+ci-buildworld-amd64: ci-buildworld .PHONY
+
+ci-buildkernel-amd64: ci-buildkernel .PHONY
+
+ci-buildimage-amd64: ci-buildimage .PHONY
+
+ci-runtest-amd64: ci-runtest .PHONY
diff --git a/tests/ci/Makefile.armv7 b/tests/ci/Makefile.armv7
new file mode 100644
index 000000000000..3b0d180fa352
--- /dev/null
+++ b/tests/ci/Makefile.armv7
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 The FreeBSD Foundation
+#
+# This software was developed by Cybermancer Infosec <bofh@FreeBSD.org>
+# under sponsorship from the FreeBSD Foundation.
+#
+# CI Makefile for armv7.
+#
+QEMU_ARCH=arm
+QEMU_DEVICES=-device ahci,id=ahci
+QEMU_EXTRA_PARAM=-bios /usr/local/share/u-boot/u-boot-qemu-arm/u-boot.bin
+QEMU_MAX_CPU_COUNT=1
+QEMU_MAX_MEM_SIZE=3
+
+portinstall-armv7: portinstall-pkg .PHONY
+.if !exists(/usr/local/share/u-boot/u-boot-qemu-arm/u-boot.bin)
+ env ASSUME_ALWAYS_YES=yes pkg install sysutils/u-boot-qemu-arm
+.endif
+
+# NOTE: Nothing should be changed below this line unless explicitly required.
+
+ci-buildworld-armv7: ci-buildworld .PHONY
+
+ci-buildkernel-armv7: ci-buildkernel .PHONY
+
+ci-buildimage-armv7: ci-buildimage .PHONY
+
+ci-runtest-armv7: ci-runtest .PHONY
diff --git a/tests/ci/Makefile.powerpc64 b/tests/ci/Makefile.powerpc64
new file mode 100644
index 000000000000..d4e8e2cdc778
--- /dev/null
+++ b/tests/ci/Makefile.powerpc64
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 The FreeBSD Foundation
+#
+# This software was developed by Cybermancer Infosec <bofh@FreeBSD.org>
+# under sponsorship from the FreeBSD Foundation.
+#
+# CI Makefile for powerpc64.
+#
+QEMU_ARCH=ppc64
+QEMU_EXTRA_PARAM=-vga none -accel tcg,thread=multi
+QEMU_MACHINE=pseries,cap-hpt-max-page-size=16M
+QEMU_MAX_CPU_COUNT=1
+QEMU_MAX_MEM_SIZE=64
+
+portinstall-powerpc64: portinstall-pkg .PHONY
+ @true
+
+# NOTE: Nothing should be changed below this line unless explicitly required.
+
+ci-buildworld-powerpc64: ci-buildworld .PHONY
+
+ci-buildkernel-powerpc64: ci-buildkernel .PHONY
+
+ci-buildimage-powerpc64: ci-buildimage .PHONY
+
+ci-runtest-powerpc64: ci-runtest .PHONY
diff --git a/tests/ci/Makefile.powerpc64le b/tests/ci/Makefile.powerpc64le
new file mode 100644
index 000000000000..60c255f569fa
--- /dev/null
+++ b/tests/ci/Makefile.powerpc64le
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 The FreeBSD Foundation
+#
+# This software was developed by Cybermancer Infosec <bofh@FreeBSD.org>
+# under sponsorship from the FreeBSD Foundation.
+#
+# CI Makefile for powerpc64le.
+#
+QEMU_ARCH=ppc64
+QEMU_EXTRA_PARAM=-vga none -accel tcg,thread=multi
+QEMU_MACHINE=pseries,cap-hpt-max-page-size=16M
+QEMU_MAX_CPU_COUNT=1
+QEMU_MAX_MEM_SIZE=64
+
+portinstall-powerpc64le: portinstall-pkg .PHONY
+ @true
+
+# NOTE: Nothing should be changed below this line unless explicitly required.
+
+ci-buildworld-powerpc64le: ci-buildworld .PHONY
+
+ci-buildkernel-powerpc64le: ci-buildkernel .PHONY
+
+ci-buildimage-powerpc64le: ci-buildimage .PHONY
+
+ci-runtest-powerpc64le: ci-runtest .PHONY
diff --git a/tests/ci/Makefile.riscv64 b/tests/ci/Makefile.riscv64
new file mode 100644
index 000000000000..d494fc4f43f5
--- /dev/null
+++ b/tests/ci/Makefile.riscv64
@@ -0,0 +1,31 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 The FreeBSD Foundation
+#
+# This software was developed by Cybermancer Infosec <bofh@FreeBSD.org>
+# under sponsorship from the FreeBSD Foundation.
+#
+# CI Makefile for riscv64.
+#
+QEMU_ARCH=riscv64
+QEMU_EXTRA_PARAM=-bios /usr/local/share/opensbi/lp64/generic/firmware/fw_jump.elf -kernel /usr/local/share/u-boot/u-boot-qemu-riscv64/u-boot.bin
+QEMU_MAX_CPU_COUNT=16
+QEMU_MAX_MEM_SIZE=64
+
+portinstall-riscv64: portinstall-pkg .PHONY
+.if !exists(/usr/local/share/opensbi/lp64/generic/firmware/fw_jump.elf)
+ env ASSUME_ALWAYS_YES=yes pkg install sysutils/opensbi
+.endif
+.if !exists(/usr/local/share/u-boot/u-boot-qemu-riscv64/u-boot.bin)
+ env ASSUME_ALWAYS_YES=yes pkg install sysutils/u-boot-qemu-riscv64
+.endif
+
+# NOTE: Nothing should be changed below this line unless explicitly required.
+
+ci-buildworld-riscv64: ci-buildworld .PHONY
+
+ci-buildkernel-riscv64: ci-buildkernel .PHONY
+
+ci-buildimage-riscv64: ci-buildimage .PHONY
+
+ci-runtest-riscv64: ci-runtest .PHONY
diff --git a/tests/ci/tools/ci.conf b/tests/ci/tools/ci.conf
new file mode 100644
index 000000000000..a9998a3e5373
--- /dev/null
+++ b/tests/ci/tools/ci.conf
@@ -0,0 +1,100 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 The FreeBSD Foundation
+#
+# This software was developed by Cybermancer Infosec <bofh@FreeBSD.org>
+# under sponsorship from the FreeBSD Foundation.
+#
+# Set to a list of third-party software to enable in rc.conf(5).
+export VM_RC_LIST="auditd freebsdci"
+
+if [ "${CITYPE}" != "smoke" ]; then
+export VM_EXTRA_PACKAGES="coreutils devel/py-pytest gdb jq ksh93 net/py-dpkt net/scapy nist-kat nmap perl5 python python3 sudo sysutils/porch tcptestsuite"
+
+ if [ "${TARGET}" = "amd64" ]; then
+ export VM_EXTRA_PACKAGES="${VM_EXTRA_PACKAGES} linux-c7-ltp"
+ fi
+fi
+
+vm_extra_pre_umount() {
+cat << EOF >> ${DESTDIR}/boot/loader.conf
+autoboot_delay=1
+beastie_disable="YES"
+loader_logo="none"
+console="comconsole,vidconsole"
+net.fibs=3
+net.inet.ip.fw.default_to_accept=1
+mac_bsdextended_load="YES"
+vfs.zfs.arc_max=4294967296
+kern.vty=sc
+EOF
+cat << EOF >> ${DESTDIR}/etc/kyua/kyua.conf
+test_suites.FreeBSD.ci = 'true'
+test_suites.FreeBSD.fibs = '1 2'
+test_suites.FreeBSD.allow_sysctl_side_effects = '1'
+test_suites.FreeBSD.cam_test_device = '/dev/ada0'
+test_suites.FreeBSD.disks = '/dev/vtbd2 /dev/vtbd3 /dev/vtbd4 /dev/vtbd5 /dev/vtbd6'
+EOF
+cat << EOF >> ${DESTDIR}/etc/rc.conf
+kld_list="" # Load modules needed by tests
+kld_list="${kld_list} blake2" # sys/opencrypto
+kld_list="${kld_list} cryptodev" # sys/opencrypto
+kld_list="${kld_list} dummymbuf" # sys/netpfil
+kld_list="${kld_list} fusefs" # sys/fs/fusefs
+kld_list="${kld_list} ipsec" # sys/netipsec
+kld_list="${kld_list} mac_portacl" # sys/mac/portacl
+kld_list="${kld_list} mqueuefs" # sys/kern/mqueue_test
+kld_list="${kld_list} pfsync" # sys/netpfil/pf (loads pf)
+kld_list="${kld_list} pflog" # sys/netpfil/pf
+kld_list="${kld_list} ipl" # sys/sbin/ipf (loads ipfilter)
+kld_list="${kld_list} ipfw" # sys/netpfil/ipfw (loads ipfw)
+kld_list="${kld_list} ipfw_nat" # sys/netpfil/ipfw (loads ipfw_nat)
+kld_list="${kld_list} ipdivert" # sys/netinet (loads ipdivert)
+kld_list="${kld_list} dummynet" # sys/netpfil/common
+kld_list="${kld_list} carp" # sys/netinet/carp
+kld_list="${kld_list} if_stf" # sys/net/if_stf
+background_fsck="NO"
+sendmail_enable="NONE"
+cron_enable="NO"
+syslogd_enable="NO"
+newsyslog_enable="NO"
+EOF
+if [ "${CITYPE}" = "smoke" ]; then
+cat << EOF >> ${DESTDIR}/etc/rc.conf
+freebsdci_type="smoke"
+EOF
+elif [ "${CITYPE}" = "full" ]; then
+cat << EOF >> ${DESTDIR}/etc/rc.conf
+freebsdci_type="full"
+EOF
+fi
+cat << EOF >> ${DESTDIR}/etc/sysctl.conf
+kern.cryptodevallowsoft=1
+kern.ipc.tls.enable=1
+net.add_addr_allfibs=0
+security.mac.bsdextended.enabled=0
+vfs.aio.enable_unsafe=1
+vfs.usermount=1
+EOF
+cat << EOF >> ${DESTDIR}/etc/fstab
+fdesc /dev/fd fdescfs rw 0 0
+EOF
+ mkdir -p ${DESTDIR}/usr/local/etc/rc.d
+ cp -p ${scriptdir}/../../tests/ci/tools/freebsdci ${DESTDIR}/usr/local/etc/rc.d/
+ touch ${DESTDIR}/firstboot
+
+ return 0
+}
+
+vm_extra_pkg_rmcache() {
+ if [ -e ${DESTDIR}/usr/local/sbin/pkg ]; then
+ mount -t devfs devfs ${DESTDIR}/dev
+ chroot ${DESTDIR} ${EMULATOR} env ASSUME_ALWAYS_YES=yes \
+ /usr/local/sbin/pkg clean -a
+ umount_loop ${DESTDIR}/dev
+ fi
+
+ return 0
+}
diff --git a/tests/ci/tools/freebsdci b/tests/ci/tools/freebsdci
new file mode 100755
index 000000000000..51bd19e2967d
--- /dev/null
+++ b/tests/ci/tools/freebsdci
@@ -0,0 +1,110 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 The FreeBSD Foundation
+#
+# This software was developed by Cybermancer Infosec <bofh@FreeBSD.org>
+# under sponsorship from the FreeBSD Foundation.
+#
+# PROVIDE: freebsdci
+# REQUIRE: LOGIN FILESYSTEMS
+# KEYWORD: firstboot
+
+# This script is used to run the firstboot CI tests on the first boot of a
+# FreeBSD image. It is automatically disabled after the first boot.
+#
+# The script will run the firstboot CI tests and then shut down the system.
+# The tests are run in the foreground so that the system can shut down
+# immediately after the tests are finished.
+#
+# Default test types are full and smoke. To run only the smoke tests, set
+# freebsdci_type="smoke" in /etc/rc.conf.local or /etc/rc.conf.
+# To run only the full tests, set freebsdci_type="full" in
+# /etc/rc.conf.local or /etc/rc.conf.
+
+. /etc/rc.subr
+
+name="freebsdci"
+desc="Run FreeBSD CI"
+rcvar=freebsdci_enable
+start_cmd="firstboot_ci_run"
+stop_cmd=":"
+os_arch=$(uname -p)
+parallelism=$(nproc)
+tardev=/dev/vtbd1
+metadir=/meta
+istar=$(file -s ${tardev} | grep "POSIX tar archive" | wc -l)
+
+load_rc_config $name
+: ${freebsdci_enable:="NO"}
+: ${freebsdci_type:="full"}
+PATH="${PATH}:/usr/local/sbin:/usr/local/bin"
+
+auto_shutdown()
+{
+ # NOTE: Currently RISC-V kernels lack the ability to
+ # make qemu exit on shutdown. Reboot instead;
+ # it makes qemu exit too.
+ case "$os_arch" in
+ riscv64)
+ shutdown -r now
+ ;;
+ *)
+ shutdown -p now
+ ;;
+ esac
+}
+
+smoke_tests()
+{
+ echo
+ echo "--------------------------------------------------------------"
+ echo "BOOT sequence COMPLETED"
+ echo "INITIATING system SHUTDOWN"
+ echo "--------------------------------------------------------------"
+}
+
+full_tests()
+{
+ echo
+ echo "--------------------------------------------------------------"
+ echo "BOOT sequence COMPLETED"
+ echo "TEST sequence STARTED"
+ if [ "${istar}" -eq 1 ]; then
+ rm -fr ${metadir}
+ mkdir -p ${metadir}
+ tar xvf ${tardev} -C ${metadir}
+ cd /usr/tests
+ set +e
+ kyua -v parallelism=${parallelism} test
+ rc=$?
+ set -e
+ if [ ${rc} -ne 0 ] && [ ${rc} -ne 1 ]; then
+ exit ${rc}
+ fi
+ kyua report --verbose --results-filter passed,skipped,xfail,broken,failed --output test-report.txt
+ kyua report-junit --output=test-report.xml
+ mv test-report.* /${metadir}
+ tar cvf ${tardev} -C ${metadir} .
+ else
+ echo "ERROR: no device with POSIX tar archive format found."
+ # Don't shutdown because this is not run in unattended mode
+ exit 1
+ fi
+ echo "TEST sequence COMPLETED"
+ echo "INITIATING system SHUTDOWN"
+ echo "--------------------------------------------------------------"
+}
+
+firstboot_ci_run()
+{
+ if [ "$freebsdci_type" = "smoke" ]; then
+ smoke_tests
+ elif [ "$freebsdci_type" = "full" ]; then
+ full_tests
+ fi
+ auto_shutdown
+}
+
+run_rc_command "$1"
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 000000000000..8e3c004b74d6
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,136 @@
+import pytest
+from atf_python.atf_pytest import ATFHandler
+from typing import Dict
+
+
+PLUGIN_ENABLED = False
+DEFAULT_HANDLER = None
+
+
+def set_handler(config):
+ global DEFAULT_HANDLER, PLUGIN_ENABLED
+ DEFAULT_HANDLER = ATFHandler(report_file_name=config.option.atf_file)
+ PLUGIN_ENABLED = True
+ return DEFAULT_HANDLER
+
+
+def get_handler():
+ return DEFAULT_HANDLER
+
+
+def pytest_addoption(parser):
+ """Add file output"""
+ # Add meta-values
+ group = parser.getgroup("general", "Running and selection options")
+ group.addoption(
+ "--atf-source-dir",
+ type=str,
+ dest="atf_source_dir",
+ help="Path to the test source directory",
+ )
+ group.addoption(
+ "--atf-cleanup",
+ default=False,
+ action="store_true",
+ dest="atf_cleanup",
+ help="Call cleanup procedure for a given test",
+ )
+ group = parser.getgroup("terminal reporting", "reporting", after="general")
+ group.addoption(
+ "--atf",
+ default=False,
+ action="store_true",
+ help="Enable test listing/results output in atf format",
+ )
+ group.addoption(
+ "--atf-file",
+ type=str,
+ dest="atf_file",
+ help="Path to the status file provided by atf runtime",
+ )
+
+
+@pytest.fixture(autouse=True, scope="session")
+def atf_vars() -> Dict[str, str]:
+ return ATFHandler.get_atf_vars()
+
+
+@pytest.hookimpl(trylast=True)
+def pytest_configure(config):
+ if config.option.help:
+ return
+
+ # Register markings anyway to avoid warnings
+ config.addinivalue_line("markers", "require_user(name): user to run the test with")
+ config.addinivalue_line(
+ "markers", "require_arch(names): List[str] of support archs"
+ )
+ # config.addinivalue_line("markers", "require_config(config): List[Tuple[str,Any]] of k=v pairs")
+ config.addinivalue_line(
+ "markers", "require_diskspace(amount): str with required diskspace"
+ )
+ config.addinivalue_line(
+ "markers", "require_files(space): List[str] with file paths"
+ )
+ config.addinivalue_line(
+ "markers", "require_machine(names): List[str] of support machine types"
+ )
+ config.addinivalue_line(
+ "markers", "require_memory(amount): str with required memory"
+ )
+ config.addinivalue_line(
+ "markers", "require_progs(space): List[str] with file paths"
+ )
+ config.addinivalue_line(
+ "markers", "timeout(dur): int/float with max duration in sec"
+ )
+
+ if not config.option.atf:
+ return
+ handler = set_handler(config)
+
+ if config.option.collectonly:
+ # Need to output list of tests to stdout, hence override
+ # standard reporter plugin
+ reporter = config.pluginmanager.getplugin("terminalreporter")
+ if reporter:
+ config.pluginmanager.unregister(reporter)
+ else:
+ handler.setup_configure()
+
+
+def pytest_pycollect_makeitem(collector, name, obj):
+ if PLUGIN_ENABLED:
+ handler = get_handler()
+ return handler.expand_tests(collector, name, obj)
+
+
+def pytest_collection_modifyitems(session, config, items):
+ """If cleanup is requested, replace collected tests with their cleanups (if any)"""
+ if PLUGIN_ENABLED:
+ handler = get_handler()
+ handler.modify_tests(items, config)
+
+
+def pytest_collection_finish(session):
+ if PLUGIN_ENABLED and session.config.option.collectonly:
+ handler = get_handler()
+ handler.list_tests(session.items)
+
+
+def pytest_runtest_setup(item):
+ if PLUGIN_ENABLED:
+ handler = get_handler()
+ handler.setup_method_pre(item)
+
+
+def pytest_runtest_logreport(report):
+ if PLUGIN_ENABLED:
+ handler = get_handler()
+ handler.add_report(report)
+
+
+def pytest_unconfigure(config):
+ if PLUGIN_ENABLED and config.option.atf_file:
+ handler = get_handler()
+ handler.write_report()
diff --git a/tests/etc/Makefile b/tests/etc/Makefile
new file mode 100644
index 000000000000..d78ac932e0dd
--- /dev/null
+++ b/tests/etc/Makefile
@@ -0,0 +1,10 @@
+.include <bsd.own.mk>
+
+TESTSDIR= ${TESTSBASE}/etc
+
+.PATH: ${SRCTOP}/tests
+KYUAFILE= yes
+
+SUBDIR+= rc.d
+
+.include <bsd.test.mk>
diff --git a/tests/etc/Makefile.depend b/tests/etc/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/etc/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/etc/Makefile.inc b/tests/etc/Makefile.inc
new file mode 100644
index 000000000000..cec69b26e149
--- /dev/null
+++ b/tests/etc/Makefile.inc
@@ -0,0 +1 @@
+.include "${SRCTOP}/tests/Makefile.inc0"
diff --git a/tests/etc/rc.d/Makefile b/tests/etc/rc.d/Makefile
new file mode 100644
index 000000000000..185bc9e7c112
--- /dev/null
+++ b/tests/etc/rc.d/Makefile
@@ -0,0 +1,5 @@
+TESTSDIR= ${TESTSBASE}/etc/rc.d
+
+ATF_TESTS_SH+= routing_test
+
+.include <bsd.test.mk>
diff --git a/tests/etc/rc.d/Makefile.depend b/tests/etc/rc.d/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/etc/rc.d/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/etc/rc.d/routing_test.sh b/tests/etc/rc.d/routing_test.sh
new file mode 100644
index 000000000000..dc54ac76e307
--- /dev/null
+++ b/tests/etc/rc.d/routing_test.sh
@@ -0,0 +1,76 @@
+#
+# Copyright (c) 2014 Spectra Logic Corporation
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions, and the following disclaimer,
+# without modification.
+# 2. Redistributions in binary form must reproduce at minimum a disclaimer
+# substantially similar to the "NO WARRANTY" disclaimer below
+# ("Disclaimer") and any redistribution must be conditioned upon
+# including a substantially similar Disclaimer requirement for further
+# binary redistribution.
+#
+# NO WARRANTY
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGES.
+#
+# Authors: Alan Somers (Spectra Logic Corporation)
+#
+
+atf_test_case static_ipv4_loopback_route_for_each_fib cleanup
+static_ipv4_loopback_route_for_each_fib_head()
+{
+ atf_set "descr" "Every FIB should have a static IPv4 loopback route"
+}
+static_ipv4_loopback_route_for_each_fib_body()
+{
+ local nfibs fib
+ nfibs=`sysctl -n net.fibs`
+
+ # Check for an IPv4 loopback route
+ for fib in `seq 0 $((${nfibs} - 1))`; do
+ atf_check -o match:"interface: lo0" -s exit:0 \
+ setfib -F ${fib} route -4 get 127.0.0.1
+ done
+}
+
+atf_test_case static_ipv6_loopback_route_for_each_fib cleanup
+static_ipv6_loopback_route_for_each_fib_head()
+{
+ atf_set "descr" "Every FIB should have a static IPv6 loopback route"
+}
+static_ipv6_loopback_route_for_each_fib_body()
+{
+ local nfibs fib
+ nfibs=`sysctl -n net.fibs`
+
+ if [ "`sysctl -in kern.features.inet6`" != "1" ]; then
+ atf_skip "This test requires IPv6 support"
+ fi
+
+ # Check for an IPv6 loopback route
+ for fib in `seq 0 $((${nfibs} - 1))`; do
+ atf_check -o match:"interface: lo0" -s exit:0 \
+ setfib -F ${fib} route -6 get ::1
+ done
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case static_ipv4_loopback_route_for_each_fib
+ atf_add_test_case static_ipv6_loopback_route_for_each_fib
+}
+
diff --git a/tests/examples/Makefile b/tests/examples/Makefile
new file mode 100644
index 000000000000..869fae2e963b
--- /dev/null
+++ b/tests/examples/Makefile
@@ -0,0 +1,9 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/examples
+
+ATF_TESTS_PYTEST += test_examples.py
+ATF_TESTS_PYTEST += test_ktest_example.py
+
+.include <bsd.test.mk>
+
diff --git a/tests/examples/test_examples.py b/tests/examples/test_examples.py
new file mode 100644
index 000000000000..fe9ae0a72cf2
--- /dev/null
+++ b/tests/examples/test_examples.py
@@ -0,0 +1,208 @@
+import pytest
+from atf_python.utils import BaseTest
+from atf_python.sys.net.tools import ToolsHelper
+from atf_python.sys.net.vnet import SingleVnetTestTemplate
+from atf_python.sys.net.vnet import VnetTestTemplate
+from atf_python.sys.net.vnet import VnetInstance
+
+import errno
+import socket
+import subprocess
+import json
+
+from typing import List
+
+
+# Test classes should be inherited
+# from the BaseTest
+
+
+class TestExampleSimplest(BaseTest):
+ @pytest.mark.skip(reason="comment me to run the test")
+ def test_one(self):
+ assert ToolsHelper.get_output("uname -s").strip() == "FreeBSD"
+
+
+class TestExampleSimple(BaseTest):
+ # List of required kernel modules (kldstat -v)
+ # that needs to be present for the tests to run
+ REQUIRED_MODULES = ["null"]
+
+ @pytest.mark.skip(reason="comment me to run the test")
+ def test_one(self):
+ """Optional test description
+ This and the following lines are not propagated
+ to the ATF test description.
+ """
+ pass
+
+ @pytest.mark.skip(reason="comment me to run the test")
+ # List of all requirements supported by an atf runner
+ # See atf-test-case(4) for the detailed description
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_arch(["amd64", "i386"])
+ @pytest.mark.require_files(["/path/file1", "/path/file2"])
+ @pytest.mark.require_machine(["amd64", "i386"])
+ @pytest.mark.require_memory("200M")
+ @pytest.mark.require_progs(["prog1", "prog2"])
+ @pytest.mark.timeout(300)
+ def test_two(self):
+ pass
+
+ @pytest.mark.skip(reason="comment me to run the test")
+ def test_get_properties(self, request):
+ """Shows fetching of test src dir and ATF-set variables"""
+ print()
+ print("SRC_DIR={}".format(request.fspath.dirname))
+ print("ATF VARS:")
+ for k, v in self.atf_vars.items():
+ print(" {}: {}".format(k, v))
+ print()
+
+ @pytest.mark.skip(reason="comment me to run the test")
+ @pytest.mark.require_user("unprivileged")
+ def test_syscall_failure(self):
+ s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
+ with pytest.raises(OSError) as exc_info:
+ s.bind(("::1", 42))
+ assert exc_info.value.errno == errno.EACCES
+
+ @pytest.mark.skip(reason="comment me to run the test")
+ @pytest.mark.parametrize(
+ "family_tuple",
+ [
+ pytest.param([socket.AF_INET, None], id="AF_INET"),
+ pytest.param([socket.AF_INET6, None], id="AF_INET6"),
+ pytest.param([39, errno.EAFNOSUPPORT], id="FAMILY_39"),
+ ],
+ )
+ def test_parametrize(self, family_tuple):
+ family, error = family_tuple
+ try:
+ s = socket.socket(family, socket.SOCK_STREAM)
+ s.close()
+ except OSError as e:
+ if error is None or error != e.errno:
+ raise
+
+ # @pytest.mark.skip(reason="comment me to run the test")
+ def test_with_cleanup(self):
+ print("TEST BODY")
+
+ def cleanup_test_with_cleanup(self, test_id):
+ print("CLEANUP HANDLER")
+
+
+class TestVnetSimple(SingleVnetTestTemplate):
+ """
+ SingleVnetTestTemplate creates a topology with a single
+ vnet and a single epair between this vnet and the host system.
+ Additionally, lo0 interface is created inside the vnet.
+
+ Both vnets and interfaces are aliased as vnetX and ifY.
+ They can be accessed via maps:
+ vnet: VnetInstance = self.vnet_map["vnet1"]
+ iface: VnetInterface = vnet.iface_alias_map["if1"]
+
+ All prefixes from IPV4_PREFIXES and IPV6_PREFIXES are
+ assigned to the single epair interface inside the jail.
+
+ One can rely on the fact that there are no IPv6 prefixes
+ in the tentative state when the test method is called.
+ """
+
+ IPV6_PREFIXES: List[str] = ["2001:db8::1/64"]
+ IPV4_PREFIXES: List[str] = ["192.0.2.1/24"]
+
+ def setup_method(self, method):
+ """
+ Optional pre-setup for all of the tests inside the class
+ """
+ # Code to run before vnet setup
+ #
+ super().setup_method(method)
+ #
+ # Code to run after vnet setup
+ # Executed inside the vnet
+
+ @pytest.mark.skip(reason="comment me to run the test")
+ @pytest.mark.require_user("root")
+ def test_ping(self):
+ assert subprocess.run("ping -c1 192.0.2.1".split()).returncode == 0
+ assert subprocess.run("ping -c1 2001:db8::1".split()).returncode == 0
+
+ @pytest.mark.skip(reason="comment me to run the test")
+ def test_topology(self):
+ vnet = self.vnet_map["vnet1"]
+ iface = vnet.iface_alias_map["if1"]
+ print("Iface {} inside vnet {}".format(iface.name, vnet.name))
+
+
+class TestVnetDual1(VnetTestTemplate):
+ """
+ VnetTestTemplate creates topology described in the self.TOPOLOGY
+
+ Each vnet (except vnet1) can have a handler function, named
+ vnetX_handler. This function will be run in a separate process
+ inside vnetX jail. The framework automatically creates a pipe
+ to allow communication between the main test and the vnet handler.
+
+ This topology contains 2 VNETs connected with 2 epairs:
+
+ [ VNET1 ] [ VNET2 ]
+ if1(epair) 2001:db8:a::1/64 <-> 2001:db8:a::2/64 if1(epair)
+ if2(epair) 2001:db8:b::1/64 <-> 2001:db8:b::2/64 if2(epair)
+ lo0 lo0
+
+ """
+
+ TOPOLOGY = {
+ "vnet1": {"ifaces": ["if1", "if2"]},
+ "vnet2": {"ifaces": ["if1", "if2"]},
+ "if1": {"prefixes6": [("2001:db8:a::1/64", "2001:db8:a::2/64")]},
+ "if2": {"prefixes6": [("2001:db8:b::1/64", "2001:db8:b::2/64")]},
+ }
+
+ def _get_iface_stat(self, os_ifname: str):
+ out = ToolsHelper.get_output(
+ "{} -I {} --libxo json".format(ToolsHelper.NETSTAT_PATH, os_ifname)
+ )
+ js = json.loads(out)
+ return js["statistics"]["interface"][0]
+
+ def vnet2_handler(self, vnet: VnetInstance):
+ """
+ Test handler that runs in the vnet2 as a separate process.
+
+ This handler receives an interface name, fetches received/sent packets
+ and returns this data back to the parent process.
+ """
+ while True:
+ # receives 'ifX' with an infinite timeout
+ iface_alias = self.wait_object(vnet.pipe, None)
+ # Translates topology interface name to the actual OS-assigned name
+ os_ifname = vnet.iface_alias_map[iface_alias].name
+ self.send_object(vnet.pipe, self._get_iface_stat(os_ifname))
+
+ @pytest.mark.skip(reason="comment me to run the test")
+ @pytest.mark.require_user("root")
+ def test_ifstat(self):
+ """Checks that RX interface packets are properly accounted for"""
+ second_vnet = self.vnet_map["vnet2"]
+ pipe = second_vnet.pipe
+
+ # Ping neighbor IP on if1 and verify that the counter was incremented
+ self.send_object(pipe, "if1")
+ old_stat = self.wait_object(pipe)
+ assert subprocess.run("ping -c5 2001:db8:a::2".split()).returncode == 0
+ self.send_object(pipe, "if1")
+ new_stat = self.wait_object(pipe)
+ assert new_stat["received-packets"] - old_stat["received-packets"] >= 5
+
+ # Ping neighbor IP on if2 and verify that the counter was incremented
+ self.send_object(pipe, "if2")
+ old_stat = self.wait_object(pipe)
+ assert subprocess.run("ping -c5 2001:db8:b::2".split()).returncode == 0
+ self.send_object(pipe, "if2")
+ new_stat = self.wait_object(pipe)
+ assert new_stat["received-packets"] - old_stat["received-packets"] >= 5
diff --git a/tests/examples/test_ktest_example.py b/tests/examples/test_ktest_example.py
new file mode 100644
index 000000000000..c11f178cb054
--- /dev/null
+++ b/tests/examples/test_ktest_example.py
@@ -0,0 +1,35 @@
+import pytest
+
+from atf_python.ktest import BaseKernelTest
+
+from atf_python.sys.netlink.attrs import NlAttrStr
+from atf_python.sys.netlink.attrs import NlAttrU32
+
+
+class TestExample(BaseKernelTest):
+ KTEST_MODULE_NAME = "ktest_example"
+
+ @pytest.mark.parametrize(
+ "numbers",
+ [
+ pytest.param([1, 2], id="1_2_Sum"),
+ pytest.param([3, 4], id="3_4_Sum"),
+ ],
+ )
+ def test_with_params(self, numbers):
+ """override to parametrize"""
+
+ test_meta = [
+ NlAttrU32(1, numbers[0]),
+ NlAttrU32(2, numbers[1]),
+ NlAttrStr(3, "test string"),
+ ]
+ self.runtest(test_meta)
+
+ @pytest.mark.skip(reason="comment me ( or delete the func) to run the test")
+ def test_failed(self):
+ pass
+
+ @pytest.mark.skip(reason="comment me ( or delete the func) to run the test")
+ def test_failed2(self):
+ pass
diff --git a/tests/freebsd_test_suite/macros.h b/tests/freebsd_test_suite/macros.h
new file mode 100644
index 000000000000..d09d7fe1bf05
--- /dev/null
+++ b/tests/freebsd_test_suite/macros.h
@@ -0,0 +1,94 @@
+/*-
+ * Copyright (c) 2015 EMC / Isilon Storage Division
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _FREEBSD_TEST_MACROS_H_
+#define _FREEBSD_TEST_MACROS_H_
+
+#include <sys/param.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#define ATF_REQUIRE_FEATURE(_feature_name) do { \
+ if (feature_present(_feature_name) == 0) { \
+ atf_tc_skip("kernel feature (%s) not present", \
+ _feature_name); \
+ } \
+} while(0)
+
+#define ATF_REQUIRE_KERNEL_MODULE(_mod_name) do { \
+ if (modfind(_mod_name) == -1) { \
+ atf_tc_skip("module %s could not be resolved: %s", \
+ _mod_name, strerror(errno)); \
+ } \
+} while(0)
+
+#define ATF_REQUIRE_SYSCTL_BOOL(_mib_name, _required_value) do { \
+ bool value; \
+ size_t size = sizeof(value); \
+ if (sysctlbyname(_mib_name, &value, &size, NULL, 0) == -1) { \
+ atf_tc_skip("sysctl for %s failed: %s", _mib_name, \
+ strerror(errno)); \
+ } \
+ if (value != (_required_value)) \
+ atf_tc_skip("requires %s=%d, =%d", (_mib_name), \
+ (_required_value), value); \
+} while (0)
+
+#define ATF_REQUIRE_SYSCTL_INT(_mib_name, _required_value) do { \
+ int value; \
+ size_t size = sizeof(value); \
+ if (sysctlbyname(_mib_name, &value, &size, NULL, 0) == -1) { \
+ atf_tc_skip("sysctl for %s failed: %s", _mib_name, \
+ strerror(errno)); \
+ } \
+ if (value != (_required_value)) \
+ atf_tc_skip("requires %s=%d, =%d", (_mib_name), \
+ (_required_value), value); \
+} while(0)
+
+#define PLAIN_REQUIRE_FEATURE(_feature_name, _exit_code) do { \
+ if (feature_present(_feature_name) == 0) { \
+ printf("kernel feature (%s) not present\n", \
+ _feature_name); \
+ _exit(_exit_code); \
+ } \
+} while(0)
+
+#define PLAIN_REQUIRE_KERNEL_MODULE(_mod_name, _exit_code) do { \
+ if (modfind(_mod_name) == -1) { \
+ printf("module %s could not be resolved: %s\n", \
+ _mod_name, strerror(errno)); \
+ _exit(_exit_code); \
+ } \
+} while(0)
+
+#endif
diff --git a/tests/include/Makefile b/tests/include/Makefile
new file mode 100644
index 000000000000..0f6ef88f9e35
--- /dev/null
+++ b/tests/include/Makefile
@@ -0,0 +1,13 @@
+.include <bsd.own.mk>
+
+TESTSDIR= ${TESTSBASE}/include
+
+ATF_TESTS_C+= byteswap_test
+ATF_TESTS_C+= byteswap_endian_test
+ATF_TESTS_C+= endian_test
+ATF_TESTS_C+= endian_sys_endian_test
+ATF_TESTS_C+= stdckdint_test
+ATF_TESTS_C+= sys_endian_test
+ATF_TESTS_C+= sys_endian_endian_test
+
+.include <bsd.test.mk>
diff --git a/tests/include/byteswap_endian_test.c b/tests/include/byteswap_endian_test.c
new file mode 100644
index 000000000000..012c1b5c447e
--- /dev/null
+++ b/tests/include/byteswap_endian_test.c
@@ -0,0 +1,9 @@
+/*-
+ * Copyright (c) 2021 M. Warner Losh <imp@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+/* Make sure this still passes if both endian.h and byteswap.h included */
+#include <endian.h>
+#include "byteswap_test.c"
diff --git a/tests/include/byteswap_test.c b/tests/include/byteswap_test.c
new file mode 100644
index 000000000000..ed23351e689b
--- /dev/null
+++ b/tests/include/byteswap_test.c
@@ -0,0 +1,76 @@
+/*-
+ * Copyright (c) 2021 M. Warner Losh <imp@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <byteswap.h>
+
+#include <atf-c.h>
+
+ATF_TC(byteswap);
+ATF_TC_HEAD(byteswap, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test swapping macros in <byteswap.h>");
+}
+
+ATF_TC_BODY(byteswap, tc)
+{
+ uint16_t ui16;
+ uint32_t ui32;
+ uint64_t ui64;
+
+ /* glibc defines the {__,}bswap_{16,32,64} */
+#ifndef __bswap_16
+ atf_tc_fail_nonfatal("__bswap_16 not defined");
+#endif
+#ifndef bswap_16
+ atf_tc_fail_nonfatal("bswap_16 not defined");
+#endif
+#ifndef __bswap_32
+ atf_tc_fail_nonfatal("__bswap_32 not defined");
+#endif
+#ifndef bswap_32
+ atf_tc_fail_nonfatal("bswap_32 not defined");
+#endif
+#ifndef __bswap_64
+ atf_tc_fail_nonfatal("__bswap_64 not defined");
+#endif
+#ifndef bswap_64
+ atf_tc_fail_nonfatal("bswap_64 not defined");
+#endif
+
+ /* glibc does not define bswap{16,32,64} */
+#ifdef bswap16
+ atf_tc_fail_nonfatal("bswap16 improperly defined");
+#endif
+#ifdef bswap32
+ atf_tc_fail_nonfatal("bswap32 improperly defined");
+#endif
+#ifdef bswap64
+ atf_tc_fail_nonfatal("bswap64 improperly defined");
+#endif
+
+ ui16 = 0x1234;
+ ATF_REQUIRE_MSG(0x3412 == bswap_16(ui16),
+ "bswap16(%#x) != 0x3412 instead %#x\n", ui16, bswap_16(ui16));
+
+ ui32 = 0x12345678ul;
+ ATF_REQUIRE_MSG(0x78563412ul == bswap_32(ui32),
+ "bswap32(%#lx) != 0x78563412 instead %#lx\n",
+ (unsigned long)ui32, (unsigned long)bswap_32(ui32));
+
+ ui64 = 0x123456789abcdef0ull;
+ ATF_REQUIRE_MSG(0xf0debc9a78563412ull == bswap_64(ui64),
+ "bswap64(%#llx) != 0x3412 instead %#llx\n",
+ (unsigned long long)ui64, (unsigned long long)bswap_64(ui64));
+
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, byteswap);
+
+ return atf_no_error();
+}
diff --git a/tests/include/endian_sys_endian_test.c b/tests/include/endian_sys_endian_test.c
new file mode 100644
index 000000000000..9c7829c94b1f
--- /dev/null
+++ b/tests/include/endian_sys_endian_test.c
@@ -0,0 +1,13 @@
+/*-
+ * Copyright (c) 2021 M. Warner Losh <imp@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+/*
+ * Make sure this still passes if both endian.h and sys/endian.h are included
+ * with sys/endian.h first
+ */
+#include <sys/endian.h>
+#include <endian.h>
+#include "sys_endian_test.c"
diff --git a/tests/include/endian_test.c b/tests/include/endian_test.c
new file mode 100644
index 000000000000..2431b54535a2
--- /dev/null
+++ b/tests/include/endian_test.c
@@ -0,0 +1,139 @@
+/*-
+ * Copyright (c) 2021 M. Warner Losh <imp@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <endian.h>
+
+#include <atf-c.h>
+
+ATF_TC(endian);
+ATF_TC_HEAD(endian, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test swapping macros in <byteswap.h>");
+}
+
+ATF_TC_BODY(endian, tc)
+{
+ /* glibc doesn't define the {__,}bswap_{16,32,64} */
+#ifdef __bswap_16
+ atf_tc_fail_nonfatal("__bswap_16 improperly defined");
+#endif
+#ifdef bswap_16
+ atf_tc_fail_nonfatal("bswap_16 improperly defined");
+#endif
+#ifdef __bswap_32
+ atf_tc_fail_nonfatal("__bswap_32 improperly defined");
+#endif
+#ifdef bswap_32
+ atf_tc_fail_nonfatal("bswap_32 improperly defined");
+#endif
+#ifdef __bswap_64
+ atf_tc_fail_nonfatal("__bswap_64 improperly defined");
+#endif
+#ifdef bswap_64
+ atf_tc_fail_nonfatal("bswap_64 improperly defined");
+#endif
+
+ /* glibc doesn't define bswap{16,32,64} */
+#ifdef bswap16
+ atf_tc_fail_nonfatal("bswap16 improperly defined");
+#endif
+#ifdef bswap32
+ atf_tc_fail_nonfatal("bswap32 improperly defined");
+#endif
+#ifdef bswap64
+ atf_tc_fail_nonfatal("bswap64 improperly defined");
+#endif
+
+ /*
+ * glibc defines with two underscores. We don't test for only one since
+ * that doesn't interfere.
+ */
+#ifndef __BIG_ENDIAN
+ atf_tc_fail_nonfatal("__BIG_ENDIAN not defined");
+#endif
+#ifndef __LITTLE_ENDIAN
+ atf_tc_fail_nonfatal("__LITTLE_ENDIAN not defined");
+#endif
+#ifndef __PDP_ENDIAN
+ atf_tc_fail_nonfatal("__PDP_ENDIAN not defined");
+#endif
+#ifndef __FLOAT_WORD_ORDER
+ atf_tc_fail_nonfatal("__FLOAT_WORD_ORDER not defined");
+#endif
+#ifndef __BYTE_ORDER
+ atf_tc_fail_nonfatal("__BYTE_ORDER not defined");
+#endif
+
+ /* order to host */
+#ifdef __BYTE_ORDER
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define H16(x) be16toh(x)
+#define H32(x) be32toh(x)
+#define H64(x) be64toh(x)
+#define O16(x) le16toh(x)
+#define O32(x) le32toh(x)
+#define O64(x) le64toh(x)
+#else
+#define H16(x) le16toh(x)
+#define H32(x) le32toh(x)
+#define H64(x) le64toh(x)
+#define O16(x) be16toh(x)
+#define O32(x) be32toh(x)
+#define O64(x) be64toh(x)
+#endif
+#endif
+ ATF_REQUIRE(H16(0x1234) == 0x1234);
+ ATF_REQUIRE(H32(0x12345678ul) == 0x12345678ul);
+ ATF_REQUIRE(H64(0x123456789abcdef0ull) == 0x123456789abcdef0ull);
+ ATF_REQUIRE(O16(0x1234) == __bswap16(0x1234));
+ ATF_REQUIRE(O32(0x12345678ul) == __bswap32(0x12345678ul));
+ ATF_REQUIRE(O64(0x123456789abcdef0ull) == __bswap64(0x123456789abcdef0ull));
+#undef H16
+#undef H32
+#undef H64
+#undef O16
+#undef O32
+#undef O64
+
+ /* host to order */
+#ifdef __BYTE_ORDER
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define H16(x) htobe16(x)
+#define H32(x) htobe32(x)
+#define H64(x) htobe64(x)
+#define O16(x) htole16(x)
+#define O32(x) htole32(x)
+#define O64(x) htole64(x)
+#else
+#define H16(x) htole16(x)
+#define H32(x) htole32(x)
+#define H64(x) htole64(x)
+#define O16(x) htobe16(x)
+#define O32(x) htobe32(x)
+#define O64(x) htobe64(x)
+#endif
+#endif
+ ATF_REQUIRE(H16(0x1234) == 0x1234);
+ ATF_REQUIRE(H32(0x12345678ul) == 0x12345678ul);
+ ATF_REQUIRE(H64(0x123456789abcdef0ull) == 0x123456789abcdef0ull);
+ ATF_REQUIRE(O16(0x1234) == __bswap16(0x1234));
+ ATF_REQUIRE(O32(0x12345678ul) == __bswap32(0x12345678ul));
+ ATF_REQUIRE(O64(0x123456789abcdef0ull) == __bswap64(0x123456789abcdef0ull));
+#undef H16
+#undef H32
+#undef H64
+#undef O16
+#undef O32
+#undef O64
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, endian);
+
+ return atf_no_error();
+}
diff --git a/tests/include/stdckdint_test.c b/tests/include/stdckdint_test.c
new file mode 100644
index 000000000000..89262bbd5500
--- /dev/null
+++ b/tests/include/stdckdint_test.c
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 2023 Dag-Erling Smørgrav
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <limits.h>
+#include <stdckdint.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(ckd_add);
+ATF_TC_BODY(ckd_add, tc)
+{
+ int result;
+
+ ATF_CHECK(!ckd_add(&result, INT_MAX, 0));
+ ATF_CHECK_EQ(INT_MAX, result);
+ ATF_CHECK(ckd_add(&result, INT_MAX, 1));
+ ATF_CHECK_EQ(INT_MIN, result);
+}
+
+ATF_TC_WITHOUT_HEAD(ckd_sub);
+ATF_TC_BODY(ckd_sub, tc)
+{
+ int result;
+
+ ATF_CHECK(!ckd_sub(&result, INT_MIN, 0));
+ ATF_CHECK_EQ(INT_MIN, result);
+ ATF_CHECK(ckd_sub(&result, INT_MIN, 1));
+ ATF_CHECK_EQ(INT_MAX, result);
+}
+
+ATF_TC_WITHOUT_HEAD(ckd_mul);
+ATF_TC_BODY(ckd_mul, tc)
+{
+ int result;
+
+ ATF_CHECK(!ckd_mul(&result, INT_MAX / 2, 2));
+ ATF_CHECK_EQ(INT_MAX - 1, result);
+ ATF_CHECK(ckd_mul(&result, INT_MAX / 2 + 1, 2));
+ ATF_CHECK_EQ(INT_MIN, result);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, ckd_add);
+ ATF_TP_ADD_TC(tp, ckd_sub);
+ ATF_TP_ADD_TC(tp, ckd_mul);
+ return (atf_no_error());
+
+}
diff --git a/tests/include/sys_endian_endian_test.c b/tests/include/sys_endian_endian_test.c
new file mode 100644
index 000000000000..4d668f8d5625
--- /dev/null
+++ b/tests/include/sys_endian_endian_test.c
@@ -0,0 +1,12 @@
+/*-
+ * Copyright (c) 2021 M. Warner Losh <imp@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+/*
+ * Make sure this still passes if both endian.h and sys/endian.h are included
+ * with endian.h first.
+ */
+#include <endian.h>
+#include "sys_endian_test.c"
diff --git a/tests/include/sys_endian_test.c b/tests/include/sys_endian_test.c
new file mode 100644
index 000000000000..5a5f77526832
--- /dev/null
+++ b/tests/include/sys_endian_test.c
@@ -0,0 +1,136 @@
+/*-
+ * Copyright (c) 2021 M. Warner Losh <imp@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/endian.h>
+
+#include <atf-c.h>
+
+ATF_TC(sys_endian);
+ATF_TC_HEAD(sys_endian, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test swapping macros in <byteswap.h>");
+}
+
+ATF_TC_BODY(sys_endian, tc)
+{
+ /* FreeBSD sys/endian.h doesn't define the {__,}bswap_{16,32,64} */
+#ifdef __bswap_16
+ atf_tc_fail_nonfatal("__bswap_16 defined");
+#endif
+#ifdef bswap_16
+ atf_tc_fail_nonfatal("bswap_16 defined");
+#endif
+#ifdef __bswap_32
+ atf_tc_fail_nonfatal("__bswap_32 defined");
+#endif
+#ifdef bswap_32
+ atf_tc_fail_nonfatal("bswap_32 defined");
+#endif
+#ifdef __bswap_64
+ atf_tc_fail_nonfatal("__bswap_64 defined");
+#endif
+#ifdef bswap_64
+ atf_tc_fail_nonfatal("bswap_64 defined");
+#endif
+
+ /* FreeBSD sys/endian.h does define bswap{16,32,64} */
+#ifndef bswap16
+ atf_tc_fail_nonfatal("bswap16 not defined");
+#endif
+#ifndef bswap32
+ atf_tc_fail_nonfatal("bswap32 not defined");
+#endif
+#ifndef bswap64
+ atf_tc_fail_nonfatal("bswap64 not defined");
+#endif
+
+ /*
+ * FreeBSD defines with one underscore
+ * We don't test for two since that doesn't interfere
+ */
+#ifndef _BIG_ENDIAN
+ atf_tc_fail_nonfatal("_BIG_ENDIAN not defined");
+#endif
+#ifndef _LITTLE_ENDIAN
+ atf_tc_fail_nonfatal("_LITTLE_ENDIAN not defined");
+#endif
+#ifndef _PDP_ENDIAN
+ atf_tc_fail_nonfatal("_PDP_ENDIAN not defined");
+#endif
+#ifndef _BYTE_ORDER
+ atf_tc_fail_nonfatal("_BYTE_ORDER not defined");
+#endif
+
+ /* order to host */
+#ifdef _BYTE_ORDER
+#if _BYTE_ORDER == _BIG_ENDIAN
+#define H16(x) be16toh(x)
+#define H32(x) be32toh(x)
+#define H64(x) be64toh(x)
+#define O16(x) le16toh(x)
+#define O32(x) le32toh(x)
+#define O64(x) le64toh(x)
+#else
+#define H16(x) le16toh(x)
+#define H32(x) le32toh(x)
+#define H64(x) le64toh(x)
+#define O16(x) be16toh(x)
+#define O32(x) be32toh(x)
+#define O64(x) be64toh(x)
+#endif
+#endif
+ ATF_REQUIRE(H16(0x1234) == 0x1234);
+ ATF_REQUIRE(H32(0x12345678ul) == 0x12345678ul);
+ ATF_REQUIRE(H64(0x123456789abcdef0ull) == 0x123456789abcdef0ull);
+ ATF_REQUIRE(O16(0x1234) == __bswap16(0x1234));
+ ATF_REQUIRE(O32(0x12345678ul) == __bswap32(0x12345678ul));
+ ATF_REQUIRE(O64(0x123456789abcdef0ull) == __bswap64(0x123456789abcdef0ull));
+#undef H16
+#undef H32
+#undef H64
+#undef O16
+#undef O32
+#undef O64
+
+ /* host to order */
+#ifdef _BYTE_ORDER
+#if _BYTE_ORDER == _BIG_ENDIAN
+#define H16(x) htobe16(x)
+#define H32(x) htobe32(x)
+#define H64(x) htobe64(x)
+#define O16(x) htole16(x)
+#define O32(x) htole32(x)
+#define O64(x) htole64(x)
+#else
+#define H16(x) htole16(x)
+#define H32(x) htole32(x)
+#define H64(x) htole64(x)
+#define O16(x) htobe16(x)
+#define O32(x) htobe32(x)
+#define O64(x) htobe64(x)
+#endif
+#endif
+ ATF_REQUIRE(H16(0x1234) == 0x1234);
+ ATF_REQUIRE(H32(0x12345678ul) == 0x12345678ul);
+ ATF_REQUIRE(H64(0x123456789abcdef0ull) == 0x123456789abcdef0ull);
+ ATF_REQUIRE(O16(0x1234) == __bswap16(0x1234));
+ ATF_REQUIRE(O32(0x12345678ul) == __bswap32(0x12345678ul));
+ ATF_REQUIRE(O64(0x123456789abcdef0ull) == __bswap64(0x123456789abcdef0ull));
+#undef H16
+#undef H32
+#undef H64
+#undef O16
+#undef O32
+#undef O64
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, sys_endian);
+
+ return atf_no_error();
+}
diff --git a/tests/oclo/Makefile b/tests/oclo/Makefile
new file mode 100644
index 000000000000..350c9f857c85
--- /dev/null
+++ b/tests/oclo/Makefile
@@ -0,0 +1,11 @@
+.PATH: ${SRCTOP}/cddl/contrib/opensolaris/tests/os-tests/tests/oclo
+
+TESTSDIR= ${TESTSBASE}/cddl/oclo
+
+PLAIN_TESTS_C= oclo oclo_errors ocloexec_verify
+
+SRCS.oclo= oclo.c
+LIBADD.oclo+= openbsd
+LIBADD.ocloexec_verify+= util
+
+.include <bsd.test.mk>
diff --git a/tests/sys/Makefile b/tests/sys/Makefile
new file mode 100644
index 000000000000..535e627d22cb
--- /dev/null
+++ b/tests/sys/Makefile
@@ -0,0 +1,57 @@
+.include <src.opts.mk>
+
+TESTSDIR= ${TESTSBASE}/sys
+
+TESTS_SUBDIRS+= acl
+TESTS_SUBDIRS+= aio
+TESTS_SUBDIRS+= ${_audit}
+TESTS_SUBDIRS+= auditpipe
+TESTS_SUBDIRS+= cam
+TESTS_SUBDIRS+= capsicum
+TESTS_SUBDIRS+= ${_cddl}
+# XXX: Currently broken in CI
+#TESTS_SUBDIRS+= compat32
+TESTS_SUBDIRS+= devrandom
+TESTS_SUBDIRS+= fifo
+TESTS_SUBDIRS+= file
+TESTS_SUBDIRS+= fs
+TESTS_SUBDIRS+= geom
+TESTS_SUBDIRS+= kern
+TESTS_SUBDIRS+= kqueue
+TESTS_SUBDIRS+= mac
+TESTS_SUBDIRS+= mqueue
+TESTS_SUBDIRS+= net
+TESTS_SUBDIRS+= ${_netgraph}
+TESTS_SUBDIRS+= netinet
+TESTS_SUBDIRS+= netinet6
+TESTS_SUBDIRS+= netipsec
+TESTS_SUBDIRS+= netlink
+TESTS_SUBDIRS+= netmap
+TESTS_SUBDIRS+= netpfil
+TESTS_SUBDIRS+= opencrypto
+TESTS_SUBDIRS+= posixshm
+TESTS_SUBDIRS+= ses
+TESTS_SUBDIRS+= sound
+TESTS_SUBDIRS+= sys
+TESTS_SUBDIRS+= vfs
+TESTS_SUBDIRS+= vm
+TESTS_SUBDIRS+= vmm
+
+.if ${MK_AUDIT} != "no"
+_audit= audit
+.endif
+
+.if ${MK_CDDL} != "no"
+_cddl= cddl
+.endif
+
+.if ${MK_NETGRAPH} != "no"
+_netgraph= netgraph
+.endif
+
+# Items not integrated into kyua runs by default
+SUBDIR+= pjdfstest
+
+SUBDIR+= common
+
+.include <bsd.test.mk>
diff --git a/tests/sys/Makefile.depend b/tests/sys/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/Makefile.inc b/tests/sys/Makefile.inc
new file mode 100644
index 000000000000..cec69b26e149
--- /dev/null
+++ b/tests/sys/Makefile.inc
@@ -0,0 +1 @@
+.include "${SRCTOP}/tests/Makefile.inc0"
diff --git a/tests/sys/acl/00.sh b/tests/sys/acl/00.sh
new file mode 100644
index 000000000000..5853aac2cac8
--- /dev/null
+++ b/tests/sys/acl/00.sh
@@ -0,0 +1,90 @@
+#!/bin/sh
+#
+# Copyright (c) 2008, 2009 Edward Tomasz Napierała <trasz@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# This is a wrapper script to run tools-posix.test on UFS filesystem.
+#
+# If any of the tests fails, here is how to debug it: go to
+# the directory with problematic filesystem mounted on it,
+# and do /path/to/test run /path/to/test tools-posix.test, e.g.
+#
+# /usr/src/tools/regression/acltools/run /usr/src/tools/regression/acltools/tools-posix.test
+#
+# Output should be obvious.
+
+if [ $(sysctl -n kern.features.ufs_acl 2>/dev/null || echo 0) -eq 0 ]; then
+ echo "1..0 # SKIP system does not have UFS ACL support"
+ exit 0
+fi
+if [ $(id -u) -ne 0 ]; then
+ echo "1..0 # SKIP you must be root"
+ exit 0
+fi
+if [ ! -c /dev/mdctl ]; then
+ echo "1..0 # SKIP no /dev/mdctl to create md devices"
+ exit 0
+fi
+
+echo "1..4"
+
+TESTDIR=$(dirname $(realpath $0))
+
+# Set up the test filesystem.
+MD=`mdconfig -at swap -s 10m`
+MNT=`mktemp -dt acltools`
+newfs /dev/$MD > /dev/null
+trap "cd /; umount -f $MNT; rmdir $MNT; mdconfig -d -u $MD" EXIT
+mount -o acls /dev/$MD $MNT
+if [ $? -ne 0 ]; then
+ echo "not ok 1 - mount failed."
+ echo 'Bail out!'
+ exit 1
+fi
+
+echo "ok 1"
+
+cd $MNT
+
+# First, check whether we can crash the kernel by creating too many
+# entries. For some reason this won't work in the test file.
+touch xxx
+i=0;
+while :; do i=$(($i+1)); setfacl -m u:$i:rwx xxx 2> /dev/null; if [ $? -ne 0 ]; then break; fi; done
+chmod 600 xxx
+rm xxx
+echo "ok 2"
+
+perl $TESTDIR/run $TESTDIR/tools-posix.test >&2
+
+if [ $? -eq 0 ]; then
+ echo "ok 3"
+else
+ echo "not ok 3"
+fi
+
+cd /
+
+echo "ok 4"
diff --git a/tests/sys/acl/01.sh b/tests/sys/acl/01.sh
new file mode 100644
index 000000000000..044d1b81ab68
--- /dev/null
+++ b/tests/sys/acl/01.sh
@@ -0,0 +1,89 @@
+#!/bin/sh
+#
+# Copyright (c) 2008, 2009 Edward Tomasz Napierała <trasz@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# This is a wrapper script to run tools-nfs4.test on ZFS filesystem.
+#
+# WARNING: It uses hardcoded ZFS pool name "acltools"
+#
+# If any of the tests fails, here is how to debug it: go to
+# the directory with problematic filesystem mounted on it,
+# and do /path/to/test run /path/to/test tools-nfs4.test, e.g.
+#
+# /usr/src/tools/regression/acltools/run /usr/src/tools/regression/acltools/tools-nfs4.test
+#
+# Output should be obvious.
+
+if ! sysctl vfs.zfs.version.spa >/dev/null 2>&1; then
+ echo "1..0 # SKIP system doesn't have ZFS loaded"
+ exit 0
+fi
+if [ $(id -u) -ne 0 ]; then
+ echo "1..0 # SKIP you must be root"
+ exit 0
+fi
+if [ ! -c /dev/mdctl ]; then
+ echo "1..0 # SKIP no /dev/mdctl to create md devices"
+ exit 0
+fi
+
+echo "1..4"
+
+TESTDIR=$(dirname $(realpath $0))
+
+# Set up the test filesystem.
+MD=`mdconfig -at swap -s 64m`
+MNT=`mktemp -dt acltools`
+trap "cd /; zpool destroy -f acltools; rmdir $MNT; mdconfig -d -u $MD" EXIT
+zpool create -m $MNT acltools /dev/$MD
+if [ $? -ne 0 ]; then
+ echo "not ok 1 - 'zpool create' failed."
+ echo 'Bail out!'
+ exit 1
+fi
+
+echo "ok 1"
+
+cd $MNT
+
+# First, check whether we can crash the kernel by creating too many
+# entries. For some reason this won't work in the test file.
+touch xxx
+setfacl -x2 xxx
+while :; do setfacl -a0 u:42:rwx:allow xxx 2> /dev/null; if [ $? -ne 0 ]; then break; fi; done
+chmod 600 xxx
+rm xxx
+echo "ok 2"
+
+perl $TESTDIR/run $TESTDIR/tools-nfs4-psarc.test >&2
+
+if [ $? -eq 0 ]; then
+ echo "ok 3"
+else
+ echo "not ok 3 # TODO: fails due to ACL changes in ZFS; bug 212323"
+fi
+
+echo "ok 4"
diff --git a/tests/sys/acl/02.sh b/tests/sys/acl/02.sh
new file mode 100644
index 000000000000..98fe1345b069
--- /dev/null
+++ b/tests/sys/acl/02.sh
@@ -0,0 +1,95 @@
+#!/bin/sh
+#
+# Copyright (c) 2008, 2009 Edward Tomasz Napierała <trasz@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# This is a wrapper script to run tools-nfs4.test on UFS filesystem.
+#
+# If any of the tests fails, here is how to debug it: go to
+# the directory with problematic filesystem mounted on it,
+# and do /path/to/test run /path/to/test tools-nfs4.test, e.g.
+#
+# /usr/src/tools/regression/acltools/run /usr/src/tools/regression/acltools/tools-nfs4.test
+#
+# Output should be obvious.
+
+if [ $(sysctl -n kern.features.ufs_acl 2>/dev/null || echo 0) -eq 0 ]; then
+ echo "1..0 # SKIP system does not have UFS ACL support"
+ exit 0
+fi
+if [ $(id -u) -ne 0 ]; then
+ echo "1..0 # SKIP you must be root"
+ exit 0
+fi
+if [ ! -c /dev/mdctl ]; then
+ echo "1..0 # SKIP no /dev/mdctl to create md devices"
+ exit 0
+fi
+
+echo "1..4"
+
+TESTDIR=$(dirname $(realpath $0))
+
+# Set up the test filesystem.
+MD=`mdconfig -at swap -s 10m`
+MNT=`mktemp -dt acltools`
+newfs /dev/$MD > /dev/null
+trap "cd /; umount -f $MNT; rmdir $MNT; mdconfig -d -u $MD" EXIT
+mount -o nfsv4acls /dev/$MD $MNT
+if [ $? -ne 0 ]; then
+ echo "not ok 1 - mount failed."
+ echo 'Bail out!'
+ exit 1
+fi
+
+echo "ok 1"
+
+cd $MNT
+
+# First, check whether we can crash the kernel by creating too many
+# entries. For some reason this won't work in the test file.
+touch xxx
+setfacl -x2 xxx
+while :; do setfacl -a0 u:42:rwx:allow xxx 2> /dev/null; if [ $? -ne 0 ]; then break; fi; done
+chmod 600 xxx
+rm xxx
+echo "ok 2"
+
+if [ `sysctl -n vfs.acl_nfs4_old_semantics` = 0 ]; then
+ perl $TESTDIR/run $TESTDIR/tools-nfs4-psarc.test >&2
+else
+ perl $TESTDIR/run $TESTDIR/tools-nfs4.test >&2
+fi
+
+if [ $? -eq 0 ]; then
+ echo "ok 3"
+else
+ echo "not ok 3"
+fi
+
+cd /
+
+echo "ok 4"
+
diff --git a/tests/sys/acl/03.sh b/tests/sys/acl/03.sh
new file mode 100644
index 000000000000..4c85638ca8db
--- /dev/null
+++ b/tests/sys/acl/03.sh
@@ -0,0 +1,119 @@
+#!/bin/sh
+#
+# Copyright (c) 2008, 2009 Edward Tomasz Napierała <trasz@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# This is a wrapper script to run tools-crossfs.test between UFS without
+# ACLs, UFS with POSIX.1e ACLs, and ZFS with NFSv4 ACLs.
+#
+# WARNING: It uses hardcoded ZFS pool name "acltools"
+#
+# Output should be obvious.
+
+if ! sysctl vfs.zfs.version.spa >/dev/null 2>&1; then
+ echo "1..0 # SKIP system doesn't have ZFS loaded"
+ exit 0
+fi
+if [ $(id -u) -ne 0 ]; then
+ echo "1..0 # SKIP you must be root"
+ exit 0
+fi
+if [ ! -c /dev/mdctl ]; then
+ echo "1..0 # SKIP no /dev/mdctl to create md devices"
+ exit 0
+fi
+
+echo "1..5"
+
+TESTDIR=$(dirname $(realpath $0))
+MNTROOT=`mktemp -dt acltools`
+
+# Set up the test filesystems.
+MD1=`mdconfig -at swap -s 64m`
+MNT1=$MNTROOT/nfs4
+mkdir $MNT1
+zpool create -m $MNT1 acltools /dev/$MD1
+if [ $? -ne 0 ]; then
+ echo "not ok 1 - 'zpool create' failed."
+ echo 'Bail out!'
+ exit 1
+fi
+
+echo "ok 1"
+
+MD2=`mdconfig -at swap -s 10m`
+MNT2=$MNTROOT/posix
+mkdir $MNT2
+newfs /dev/$MD2 > /dev/null
+mount -o acls /dev/$MD2 $MNT2
+if [ $? -ne 0 ]; then
+ echo "not ok 2 - mount failed."
+ echo 'Bail out!'
+ exit 1
+fi
+
+echo "ok 2"
+
+MD3=`mdconfig -at swap -s 10m`
+MNT3=$MNTROOT/none
+mkdir $MNT3
+newfs /dev/$MD3 > /dev/null
+mount /dev/$MD3 $MNT3
+if [ $? -ne 0 ]; then
+ echo "not ok 3 - mount failed."
+ echo 'Bail out!'
+ exit 1
+fi
+
+echo "ok 3"
+
+cd $MNTROOT
+
+perl $TESTDIR/run $TESTDIR/tools-crossfs.test >&2
+
+if [ $? -eq 0 ]; then
+ echo "ok 4"
+else
+ echo "not ok 4"
+fi
+
+cd /
+
+umount -f $MNT3
+rmdir $MNT3
+mdconfig -du $MD3
+
+umount -f $MNT2
+rmdir $MNT2
+mdconfig -du $MD2
+
+zpool destroy -f acltools
+rmdir $MNT1
+mdconfig -du $MD1
+
+rmdir $MNTROOT
+
+echo "ok 5"
+
diff --git a/tests/sys/acl/04.sh b/tests/sys/acl/04.sh
new file mode 100644
index 000000000000..cfe4b1cb08a8
--- /dev/null
+++ b/tests/sys/acl/04.sh
@@ -0,0 +1,76 @@
+#!/bin/sh
+#
+# Copyright (c) 2011 Edward Tomasz Napierała <trasz@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# This is a wrapper script to run tools-nfs4-trivial.test on ZFS filesystem.
+#
+# WARNING: It uses hardcoded ZFS pool name "acltools"
+
+if ! sysctl vfs.zfs.version.spa >/dev/null 2>&1; then
+ echo "1..0 # SKIP system doesn't have ZFS loaded"
+ exit 0
+fi
+if [ $(id -u) -ne 0 ]; then
+ echo "1..0 # SKIP you must be root"
+ exit 0
+fi
+if [ ! -c /dev/mdctl ]; then
+ echo "1..0 # SKIP no /dev/mdctl to create md devices"
+ exit 0
+fi
+
+echo "1..3"
+
+TESTDIR=$(dirname $(realpath $0))
+
+# Set up the test filesystem.
+MD=`mdconfig -at swap -s 64m`
+MNT=`mktemp -dt acltools`
+zpool create -m $MNT acltools /dev/$MD
+if [ $? -ne 0 ]; then
+ echo "not ok 1 - 'zpool create' failed."
+ echo 'Bail out!'
+ exit 1
+fi
+
+echo "ok 1"
+
+cd $MNT
+
+perl $TESTDIR/run $TESTDIR/tools-nfs4-trivial.test >&2
+
+if [ $? -eq 0 ]; then
+ echo "ok 2"
+else
+ echo "not ok 2"
+fi
+
+cd /
+zpool destroy -f acltools
+rmdir $MNT
+mdconfig -du $MD
+
+echo "ok 3"
diff --git a/tests/sys/acl/Makefile b/tests/sys/acl/Makefile
new file mode 100644
index 000000000000..0080e8dd5b5a
--- /dev/null
+++ b/tests/sys/acl/Makefile
@@ -0,0 +1,39 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/acl
+
+BINDIR= ${TESTSDIR}
+
+${PACKAGE}FILES+= tools-crossfs.test
+${PACKAGE}FILES+= tools-nfs4.test
+${PACKAGE}FILES+= tools-nfs4-psarc.test
+${PACKAGE}FILES+= tools-nfs4-trivial.test
+${PACKAGE}FILES+= tools-posix.test
+
+SCRIPTS+= run
+
+ATF_TESTS_C+= acl-api-test
+
+TAP_TESTS_SH+= 00
+TAP_TESTS_SH+= 01
+TAP_TESTS_SH+= 02
+TAP_TESTS_SH+= 03
+TAP_TESTS_SH+= 04
+
+.for t in ${TAP_TESTS_SH}
+# Tests hard-code a ZFS pool name of "acltools" and so cannot run in parallel.
+TEST_METADATA.$t+= required_user="root" \
+ is_exclusive=true
+.endfor
+
+_ACL_PROGS= getfacl setfacl
+
+.for t in 01 03 04
+TEST_METADATA.$t+= required_programs="perl zpool ${_ACL_PROGS}"
+.endfor
+
+.for t in 00 02
+TEST_METADATA.$t+= required_programs="perl ${_ACL_PROGS}"
+.endfor
+
+.include <bsd.test.mk>
diff --git a/tests/sys/acl/Makefile.depend b/tests/sys/acl/Makefile.depend
new file mode 100644
index 000000000000..462a1562f495
--- /dev/null
+++ b/tests/sys/acl/Makefile.depend
@@ -0,0 +1,16 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/acl/acl-api-test.c b/tests/sys/acl/acl-api-test.c
new file mode 100644
index 000000000000..3bd288313fed
--- /dev/null
+++ b/tests/sys/acl/acl-api-test.c
@@ -0,0 +1,194 @@
+/*-
+ * Copyright (c) 2021 Gleb Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/acl.h>
+#include <sys/stat.h>
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include <atf-c.h>
+
+/* Compatibility shim to make it possible to run this test on Linux
+ * gcc -I/path/to/atf/include -L/path/to/atf/lib -latf-c -lacl acl-api-test.c
+ */
+#ifdef __linux__
+#include <acl/libacl.h>
+#define acl_from_mode_np acl_from_mode
+#define acl_equiv_mode_np acl_equiv_mode
+#define acl_cmp_np acl_cmp
+#endif
+
+static const mode_t all_modes[] = {
+ S_IRUSR,
+ S_IWUSR,
+ S_IXUSR,
+ S_IRGRP,
+ S_IWGRP,
+ S_IXGRP,
+ S_IROTH,
+ S_IWOTH,
+ S_IXOTH
+};
+
+static mode_t gen_random_mode(void)
+{
+ mode_t mode = 0;
+
+ for (unsigned i = 0; i < sizeof(all_modes) / sizeof(mode_t); i++) {
+ if (rand() % 2)
+ mode |= all_modes[i];
+ }
+
+ return (mode);
+}
+
+/* Generate a random mode_t, produce an acl_t from it,
+ * then use acl_equiv_mode_np to produce a mode_t again.
+ * The call should succeed and mode_t's should be equal
+ */
+ATF_TC_WITHOUT_HEAD(acl_mode_roundup);
+ATF_TC_BODY(acl_mode_roundup, tc)
+{
+ int num_tests = 100;
+
+ while (num_tests--) {
+ mode_t src_mode, equiv_mode;
+ acl_t acl;
+
+ src_mode = gen_random_mode();
+
+ acl = acl_from_mode_np(src_mode);
+ ATF_REQUIRE(acl != NULL);
+
+ ATF_CHECK_EQ(0, acl_equiv_mode_np(acl, &equiv_mode));
+ ATF_CHECK_EQ(src_mode, equiv_mode);
+
+ acl_free(acl);
+ }
+}
+
+/* Successfull acl_equiv_mode_np calls are tested in acl_mode_roundup.
+ * Here some specific cases are tested.
+ */
+ATF_TC_WITHOUT_HEAD(acl_equiv_mode_test);
+ATF_TC_BODY(acl_equiv_mode_test, tc)
+{
+ acl_t acl;
+ acl_entry_t entry;
+ mode_t mode;
+ int uid = 0;
+
+ acl = acl_init(1);
+ ATF_REQUIRE(acl != NULL);
+
+ /* empty acl maps to 0000 UNIX mode */
+ ATF_CHECK_EQ(0, acl_equiv_mode_np(acl, &mode));
+ ATF_CHECK_EQ(0, mode);
+
+#ifndef __linux__
+ /* NFS-branded acl's can't be converted to UNIX mode */
+ ATF_REQUIRE_EQ(0, acl_create_entry(&acl, &entry));
+ ATF_REQUIRE_EQ(0, acl_set_tag_type(entry, ACL_EVERYONE));
+ ATF_CHECK_EQ(1, acl_equiv_mode_np(acl, &mode));
+#endif
+
+ /* acl's with qualified user entries can't be converted to UNIX mode */
+ acl_free(acl);
+ acl = acl_init(1);
+ ATF_REQUIRE(acl != NULL);
+ ATF_REQUIRE_EQ(0, acl_create_entry(&acl, &entry));
+ ATF_REQUIRE_EQ(0, acl_set_tag_type(entry, ACL_USER));
+ ATF_REQUIRE_EQ(0, acl_set_qualifier(entry, &uid));
+ ATF_CHECK_EQ(1, acl_equiv_mode_np(acl, &mode));
+
+ /* passing NULL causes EINVAL */
+ ATF_CHECK_ERRNO(EINVAL, acl_equiv_mode_np(NULL, &mode));
+}
+
+ATF_TC_WITHOUT_HEAD(acl_cmp_test);
+ATF_TC_BODY(acl_cmp_test, tc)
+{
+ acl_t empty_acl, acl1, acl2;
+ acl_entry_t entry;
+ acl_permset_t perms;
+
+ empty_acl = acl_init(1);
+ ATF_REQUIRE(empty_acl != NULL);
+
+ acl1 = acl_init(3);
+ ATF_REQUIRE(acl1 != NULL);
+
+ /* first, check that two empty acls are equal */
+ ATF_CHECK_EQ(0, acl_cmp_np(acl1, empty_acl));
+
+ /* now create an entry and compare against empty acl */
+ ATF_REQUIRE_EQ(0, acl_create_entry(&acl1, &entry));
+ ATF_REQUIRE_EQ(0, acl_set_tag_type(entry, ACL_USER_OBJ));
+ ATF_REQUIRE_EQ(0, acl_get_permset(entry, &perms));
+ ATF_REQUIRE_EQ(0, acl_clear_perms(perms));
+ ATF_REQUIRE_EQ(0, acl_add_perm(perms, ACL_READ));
+ ATF_CHECK_EQ(1, acl_cmp_np(empty_acl, acl1));
+
+ /* make a dup of non-empty acl and check that they are equal */
+ acl2 = acl_dup(acl1);
+ ATF_REQUIRE(acl2 != NULL);
+ ATF_CHECK_EQ(0, acl_cmp_np(acl1, acl2));
+
+ /* change the tag type and compare */
+ ATF_REQUIRE_EQ(1, acl_get_entry(acl1, ACL_FIRST_ENTRY, &entry));
+ ATF_REQUIRE_EQ(0, acl_set_tag_type(entry, ACL_GROUP_OBJ));
+ ATF_CHECK_EQ(1, acl_cmp_np(acl1, acl2));
+
+ /* change the permset and compare */
+ acl_free(acl2);
+ acl2 = acl_dup(acl1);
+ ATF_REQUIRE(acl2 != NULL);
+ ATF_REQUIRE_EQ(1, acl_get_entry(acl1, ACL_FIRST_ENTRY, &entry));
+ ATF_REQUIRE_EQ(0, acl_get_permset(entry, &perms));
+ ATF_REQUIRE_EQ(0, acl_clear_perms(perms));
+ ATF_CHECK_EQ(1, acl_cmp_np(acl1, acl2));
+
+ /* check that passing NULL yields EINVAL */
+ ATF_CHECK_ERRNO(EINVAL, acl_cmp_np(NULL, NULL));
+ ATF_CHECK_ERRNO(EINVAL, acl_cmp_np(acl1, NULL));
+ ATF_CHECK_ERRNO(EINVAL, acl_cmp_np(NULL, acl1));
+
+ acl_free(empty_acl);
+ acl_free(acl1);
+ acl_free(acl2);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, acl_mode_roundup);
+ ATF_TP_ADD_TC(tp, acl_equiv_mode_test);
+ ATF_TP_ADD_TC(tp, acl_cmp_test);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/acl/aclfuzzer.sh b/tests/sys/acl/aclfuzzer.sh
new file mode 100644
index 000000000000..de1a8f52909f
--- /dev/null
+++ b/tests/sys/acl/aclfuzzer.sh
@@ -0,0 +1,223 @@
+#!/bin/sh
+#
+# Copyright (c) 2008, 2009 Edward Tomasz Napierała <trasz@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# This is an NFSv4 ACL fuzzer. It expects to be run by non-root in a scratch
+# directory on a filesystem with NFSv4 ACLs support. Output it generates
+# is expected to be fed to /usr/src/tools/regression/acltools/run script.
+
+NUMBER_OF_COMMANDS=300
+
+run_command()
+{
+ echo "\$ $1"
+ eval $1 2>&1 | sed 's/^/> /'
+}
+
+rnd_from_0_to()
+{
+ max=`expr $1 + 1`
+ rnd=`jot -r 1`
+ rnd=`expr $rnd % $max`
+
+ echo $rnd
+}
+
+rnd_path()
+{
+ rnd=`rnd_from_0_to 3`
+ case $rnd in
+ 0) echo "$TMP/aaa" ;;
+ 1) echo "$TMP/bbb" ;;
+ 2) echo "$TMP/aaa/ccc" ;;
+ 3) echo "$TMP/bbb/ddd" ;;
+ esac
+}
+
+f_prepend_random_acl_on()
+{
+ rnd=`rnd_from_0_to 4`
+ case $rnd in
+ 0) u="owner@" ;;
+ 1) u="group@" ;;
+ 2) u="everyone@" ;;
+ 3) u="u:1138" ;;
+ 4) u="g:1138" ;;
+ esac
+
+ p=""
+ while :; do
+ rnd=`rnd_from_0_to 30`
+ if [ -n "$p" -a $rnd -ge 14 ]; then
+ break;
+ fi
+
+ case $rnd in
+ 0) p="${p}r" ;;
+ 1) p="${p}w" ;;
+ 2) p="${p}x" ;;
+ 3) p="${p}p" ;;
+ 4) p="${p}d" ;;
+ 5) p="${p}D" ;;
+ 6) p="${p}a" ;;
+ 7) p="${p}A" ;;
+ 8) p="${p}R" ;;
+ 9) p="${p}W" ;;
+ 10) p="${p}R" ;;
+ 11) p="${p}c" ;;
+ 12) p="${p}C" ;;
+ 13) p="${p}o" ;;
+ 14) p="${p}s" ;;
+ esac
+ done
+
+ f=""
+ while :; do
+ rnd=`rnd_from_0_to 10`
+ if [ $rnd -ge 6 ]; then
+ break;
+ fi
+
+ case $rnd in
+ 0) f="${f}f" ;;
+ 1) f="${f}d" ;;
+ 2) f="${f}n" ;;
+ 3) f="${f}i" ;;
+ esac
+ done
+
+ rnd=`rnd_from_0_to 1`
+ case $rnd in
+ 0) x="allow" ;;
+ 1) x="deny" ;;
+ esac
+
+ acl="$u:$p:$f:$x"
+
+ file=`rnd_path`
+ run_command "setfacl -a0 $acl $file"
+}
+
+f_getfacl()
+{
+ file=`rnd_path`
+ run_command "getfacl -qn $file"
+}
+
+f_ls_mode()
+{
+ file=`rnd_path`
+ run_command "ls -al $file | sed -n '2p' | cut -d' ' -f1"
+}
+
+f_chmod()
+{
+ b1=`rnd_from_0_to 7`
+ b2=`rnd_from_0_to 7`
+ b3=`rnd_from_0_to 7`
+ b4=`rnd_from_0_to 7`
+ file=`rnd_path`
+
+ run_command "chmod $b1$b2$b3$b4 $file $2"
+}
+
+f_touch()
+{
+ file=`rnd_path`
+ run_command "touch $file"
+}
+
+f_rm()
+{
+ file=`rnd_path`
+ run_command "rm -f $file"
+}
+
+f_mkdir()
+{
+ file=`rnd_path`
+ run_command "mkdir $file"
+}
+
+f_rmdir()
+{
+ file=`rnd_path`
+ run_command "rmdir $file"
+}
+
+f_mv()
+{
+ from=`rnd_path`
+ to=`rnd_path`
+ run_command "mv -f $from $to"
+}
+
+# XXX: To be implemented: chown(8), setting times with touch(1).
+
+switch_to_random_user()
+{
+ # XXX: To be implemented.
+}
+
+execute_random_command()
+{
+ rnd=`rnd_from_0_to 20`
+
+ case $rnd in
+ 0|10|11|12|13|15) cmd=f_prepend_random_acl_on ;;
+ 1) cmd=f_getfacl ;;
+ 2) cmd=f_ls_mode ;;
+ 3) cmd=f_chmod ;;
+ 4|18|19) cmd=f_touch ;;
+ 5) cmd=f_rm ;;
+ 6|16|17) cmd=f_mkdir ;;
+ 7) cmd=f_rmdir ;;
+ 8) cmd=f_mv ;;
+ esac
+
+ $cmd "XXX"
+}
+
+echo "# Fuzzing; will stop after $NUMBER_OF_COMMANDS commands."
+TMP="aclfuzzer_`dd if=/dev/random bs=1k count=1 2>/dev/null | openssl md5`"
+
+run_command "whoami"
+umask 022
+run_command "umask 022"
+run_command "mkdir $TMP"
+
+i=0;
+while [ "$i" -lt "$NUMBER_OF_COMMANDS" ]; do
+ switch_to_random_user
+ execute_random_command
+ i=`expr $i + 1`
+done
+
+run_command "find $TMP -exec setfacl -a0 everyone@:rxd:allow {} \;"
+run_command "rm -rfv $TMP"
+
+echo "# Fuzzed, thank you."
+
diff --git a/tests/sys/acl/mktrivial.sh b/tests/sys/acl/mktrivial.sh
new file mode 100644
index 000000000000..2b1a8d5b3533
--- /dev/null
+++ b/tests/sys/acl/mktrivial.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Edward Tomasz Napierała <trasz@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# This shell script generates an input file for the "run" script, used
+# to verify generation of trivial ACLs.
+
+echo "$ touch f"
+touch f
+
+for s in `jot 7 0 7`; do
+ for u in `jot 7 0 7`; do
+ for g in `jot 7 0 7`; do
+ for o in `jot 7 0 7`; do
+ echo "$ chmod 0$s$u$g$o f"
+ chmod "0$s$u$g$o" f
+ echo "$ ls -l f | cut -d' ' -f1"
+ ls -l f | cut -d' ' -f1 | sed 's/^/> /'
+ echo "$ getfacl -q f"
+ getfacl -q f | sed 's/^/> /'
+ done
+ done
+ done
+done
+
+echo "$ rm f"
+rm f
+
diff --git a/tests/sys/acl/run b/tests/sys/acl/run
new file mode 100644
index 000000000000..f8e9c8d87f71
--- /dev/null
+++ b/tests/sys/acl/run
@@ -0,0 +1,328 @@
+#!/usr/bin/perl -w -U
+
+# Copyright (c) 2007, 2008 Andreas Gruenbacher.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions, and the following disclaimer,
+# without modification, immediately at the beginning of the file.
+# 2. The name of the author may not be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# Alternatively, this software may be distributed under the terms of the
+# GNU Public License ("GPL").
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+#
+# Possible improvements:
+#
+# - distinguish stdout and stderr output
+# - add environment variable like assignments
+# - run up to a specific line
+# - resume at a specific line
+#
+
+use strict;
+use FileHandle;
+use Getopt::Std;
+use POSIX qw(isatty setuid getcwd);
+use vars qw($opt_l $opt_v);
+
+no warnings qw(taint);
+
+$opt_l = ~0; # a really huge number
+getopts('l:v');
+
+my ($OK, $FAILED) = ("ok", "failed");
+if (isatty(fileno(STDOUT))) {
+ $OK = "\033[32m" . $OK . "\033[m";
+ $FAILED = "\033[31m\033[1m" . $FAILED . "\033[m";
+}
+
+sub exec_test($$);
+sub process_test($$$$);
+
+my ($prog, $in, $out) = ([], [], []);
+my $prog_line = 0;
+my ($tests, $failed) = (0,0);
+my $lineno;
+my $width = ($ENV{COLUMNS} || 80) >> 1;
+
+for (;;) {
+ my $line = <>; $lineno++;
+ if (defined $line) {
+ # Substitute %VAR and %{VAR} with environment variables.
+ $line =~ s[%(\w+)][$ENV{$1}]eg;
+ $line =~ s[%\{(\w+)\}][$ENV{$1}]eg;
+ }
+ if (defined $line) {
+ if ($line =~ s/^\s*< ?//) {
+ push @$in, $line;
+ } elsif ($line =~ s/^\s*> ?//) {
+ push @$out, $line;
+ } else {
+ process_test($prog, $prog_line, $in, $out);
+ last if $prog_line >= $opt_l;
+
+ $prog = [];
+ $prog_line = 0;
+ }
+ if ($line =~ s/^\s*\$ ?//) {
+ $prog = [ map { s/\\(.)/$1/g; $_ } split /(?<!\\)\s+/, $line ];
+ $prog_line = $lineno;
+ $in = [];
+ $out = [];
+ }
+ } else {
+ process_test($prog, $prog_line, $in, $out);
+ last;
+ }
+}
+
+my $status = sprintf("%d commands (%d passed, %d failed)",
+ $tests, $tests-$failed, $failed);
+if (isatty(fileno(STDOUT))) {
+ if ($failed) {
+ $status = "\033[31m\033[1m" . $status . "\033[m";
+ } else {
+ $status = "\033[32m" . $status . "\033[m";
+ }
+}
+print $status, "\n";
+exit $failed ? 1 : 0;
+
+
+sub process_test($$$$) {
+ my ($prog, $prog_line, $in, $out) = @_;
+
+ return unless @$prog;
+
+ my $p = [ @$prog ];
+ print "[$prog_line] \$ ", join(' ',
+ map { s/\s/\\$&/g; $_ } @$p), " -- ";
+ my $result = exec_test($prog, $in);
+ my @good = ();
+ my $nmax = (@$out > @$result) ? @$out : @$result;
+ for (my $n=0; $n < $nmax; $n++) {
+ my $use_re;
+ if (defined $out->[$n] && $out->[$n] =~ /^~ /) {
+ $use_re = 1;
+ $out->[$n] =~ s/^~ //g;
+ }
+
+ if (!defined($out->[$n]) || !defined($result->[$n]) ||
+ (!$use_re && $result->[$n] ne $out->[$n]) ||
+ ( $use_re && $result->[$n] !~ /^$out->[$n]/)) {
+ push @good, ($use_re ? '!~' : '!=');
+ }
+ else {
+ push @good, ($use_re ? '=~' : '==');
+ }
+ }
+ my $good = !(grep /!/, @good);
+ $tests++;
+ $failed++ unless $good;
+ print $good ? $OK : $FAILED, "\n";
+ if (!$good || $opt_v) {
+ for (my $n=0; $n < $nmax; $n++) {
+ my $l = defined($out->[$n]) ? $out->[$n] : "~";
+ chomp $l;
+ my $r = defined($result->[$n]) ? $result->[$n] : "~";
+ chomp $r;
+ print sprintf("%-" . ($width-3) . "s %s %s\n",
+ $r, $good[$n], $l);
+ }
+ }
+}
+
+
+sub su($) {
+ my ($user) = @_;
+
+ $user ||= "root";
+
+ my ($login, $pass, $uid, $gid) = getpwnam($user)
+ or return [ "su: user $user does not exist\n" ];
+ my @groups = ();
+ my $fh = new FileHandle("/etc/group")
+ or return [ "opening /etc/group: $!\n" ];
+ while (<$fh>) {
+ chomp;
+ my ($group, $passwd, $gid, $users) = split /:/;
+ foreach my $u (split /,/, $users) {
+ push @groups, $gid
+ if ($user eq $u);
+ }
+ }
+ $fh->close;
+
+ my $groups = join(" ", ($gid, $gid, @groups));
+ #print STDERR "[[$groups]]\n";
+ $! = 0; # reset errno
+ $> = 0;
+ $( = $gid;
+ $) = $groups;
+ if ($!) {
+ return [ "su: $!\n" ];
+ }
+ if ($uid != 0) {
+ $> = $uid;
+ #$< = $uid;
+ if ($!) {
+ return [ "su: $prog->[1]: $!\n" ];
+ }
+ }
+ #print STDERR "[($>,$<)($(,$))]";
+ return [];
+}
+
+
+sub sg($) {
+ my ($group) = @_;
+
+ my $gid = getgrnam($group)
+ or return [ "sg: group $group does not exist\n" ];
+ my %groups = map { $_ eq $gid ? () : ($_ => 1) } (split /\s/, $));
+
+ #print STDERR "<<", join("/", keys %groups), ">>\n";
+ my $groups = join(" ", ($gid, $gid, keys %groups));
+ #print STDERR "[[$groups]]\n";
+ $! = 0; # reset errno
+ if ($> != 0) {
+ my $uid = $>;
+ $> = 0;
+ $( = $gid;
+ $) = $groups;
+ $> = $uid;
+ } else {
+ $( = $gid;
+ $) = $groups;
+ }
+ if ($!) {
+ return [ "sg: $!\n" ];
+ }
+ print STDERR "[($>,$<)($(,$))]";
+ return [];
+}
+
+
+sub exec_test($$) {
+ my ($prog, $in) = @_;
+ local (*IN, *IN_DUP, *IN2, *OUT_DUP, *OUT, *OUT2);
+ my $needs_shell = (join('', @$prog) =~ /[][|<>"'`\$\*\?]/);
+
+ if ($prog->[0] eq "umask") {
+ umask oct $prog->[1];
+ return [];
+ } elsif ($prog->[0] eq "cd") {
+ if (!chdir $prog->[1]) {
+ return [ "chdir: $prog->[1]: $!\n" ];
+ }
+ $ENV{PWD} = getcwd;
+ return [];
+ } elsif ($prog->[0] eq "su") {
+ return su($prog->[1]);
+ } elsif ($prog->[0] eq "sg") {
+ return sg($prog->[1]);
+ } elsif ($prog->[0] eq "export") {
+ my ($name, $value) = split /=/, $prog->[1];
+ # FIXME: need to evaluate $value, so that things like this will work:
+ # export dir=$PWD/dir
+ $ENV{$name} = $value;
+ return [];
+ } elsif ($prog->[0] eq "unset") {
+ delete $ENV{$prog->[1]};
+ return [];
+ }
+
+ pipe *IN2, *OUT
+ or die "Can't create pipe for reading: $!";
+ open *IN_DUP, "<&STDIN"
+ or *IN_DUP = undef;
+ open *STDIN, "<&IN2"
+ or die "Can't duplicate pipe for reading: $!";
+ close *IN2;
+
+ open *OUT_DUP, ">&STDOUT"
+ or die "Can't duplicate STDOUT: $!";
+ pipe *IN, *OUT2
+ or die "Can't create pipe for writing: $!";
+ open *STDOUT, ">&OUT2"
+ or die "Can't duplicate pipe for writing: $!";
+ close *OUT2;
+
+ *STDOUT->autoflush();
+ *OUT->autoflush();
+
+ $SIG{CHLD} = 'IGNORE';
+
+ if (fork()) {
+ # Server
+ if (*IN_DUP) {
+ open *STDIN, "<&IN_DUP"
+ or die "Can't duplicate STDIN: $!";
+ close *IN_DUP
+ or die "Can't close STDIN duplicate: $!";
+ }
+ open *STDOUT, ">&OUT_DUP"
+ or die "Can't duplicate STDOUT: $!";
+ close *OUT_DUP
+ or die "Can't close STDOUT duplicate: $!";
+
+ foreach my $line (@$in) {
+ #print "> $line";
+ print OUT $line;
+ }
+ close *OUT
+ or die "Can't close pipe for writing: $!";
+
+ my $result = [];
+ while (<IN>) {
+ #print "< $_";
+ if ($needs_shell) {
+ s#^/bin/sh: line \d+: ##;
+ }
+ push @$result, $_;
+ }
+ return $result;
+ } else {
+ # Client
+ $< = $>;
+ close IN
+ or die "Can't close read end for input pipe: $!";
+ close OUT
+ or die "Can't close write end for output pipe: $!";
+ close OUT_DUP
+ or die "Can't close STDOUT duplicate: $!";
+ local *ERR_DUP;
+ open ERR_DUP, ">&STDERR"
+ or die "Can't duplicate STDERR: $!";
+ open STDERR, ">&STDOUT"
+ or die "Can't join STDOUT and STDERR: $!";
+
+ if ($needs_shell) {
+ exec ('/bin/sh', '-c', join(" ", @$prog));
+ } else {
+ exec @$prog;
+ }
+ print STDERR $prog->[0], ": $!\n";
+ exit;
+ }
+}
+
diff --git a/tests/sys/acl/tools-crossfs.test b/tests/sys/acl/tools-crossfs.test
new file mode 100644
index 000000000000..25fd49613cd8
--- /dev/null
+++ b/tests/sys/acl/tools-crossfs.test
@@ -0,0 +1,321 @@
+# Copyright (c) 2008, 2009 Edward Tomasz Napierała <trasz@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# This is a tools-level test intended to verify that cp(1) and mv(1)
+# do the right thing with respect to ACLs. Run it as root using
+# ACL-enabled kernel:
+#
+# /usr/src/tools/regression/acltools/run /usr/src/tools/regression/acltools/tools-nfs4.test
+#
+# You need to have three subdirectories, named nfs4, posix and none,
+# with filesystems with NFSv4 ACLs, POSIX.1e ACLs and no ACLs enabled,
+# respectively, mounted on them, in your current directory.
+#
+# WARNING: Creates files in unsafe way.
+
+$ whoami
+> root
+$ umask 022
+
+$ touch nfs4/xxx
+$ getfacl -nq nfs4/xxx
+> owner@:rw-p--aARWcCos:-------:allow
+> group@:r-----a-R-c--s:-------:allow
+> everyone@:r-----a-R-c--s:-------:allow
+
+$ touch posix/xxx
+$ getfacl -nq posix/xxx
+> user::rw-
+> group::r--
+> other::r--
+
+# mv with POSIX.1e ACLs.
+$ rm -f posix/xxx
+$ rm -f posix/yyy
+$ touch posix/xxx
+$ chmod 456 posix/xxx
+$ ls -l posix/xxx | cut -d' ' -f1
+> -r--r-xrw-
+$ setfacl -m u:42:x,g:43:w posix/xxx
+$ mv posix/xxx posix/yyy
+$ getfacl -nq posix/yyy
+> user::r--
+> user:42:--x
+> group::r-x
+> group:43:-w-
+> mask::rwx
+> other::rw-
+$ ls -l posix/yyy | cut -d' ' -f1
+> -r--rwxrw-+
+
+# mv from POSIX.1e to none.
+$ rm -f posix/xxx
+$ rm -f none/xxx
+$ touch posix/xxx
+$ chmod 345 posix/xxx
+$ setfacl -m u:42:x,g:43:w posix/xxx
+$ ls -l posix/xxx | cut -d' ' -f1
+> --wxrwxr-x+
+$ mv posix/xxx none/xxx
+> mv: failed to set acl entries for none/xxx: Operation not supported
+$ ls -l none/xxx | cut -d' ' -f1
+> --wxrwxr-x
+
+# mv from POSIX.1e to NFSv4.
+$ rm -f posix/xxx
+$ rm -f nfs4/xxx
+$ touch posix/xxx
+$ chmod 456 posix/xxx
+$ setfacl -m u:42:x,g:43:w posix/xxx
+$ ls -l posix/xxx | cut -d' ' -f1
+> -r--rwxrw-+
+$ mv posix/yyy nfs4/xxx
+> mv: failed to set acl entries for nfs4/xxx: Invalid argument
+$ getfacl -nq nfs4/xxx
+> owner@:-wxp----------:-------:deny
+> owner@:r-----aARWcCos:-------:allow
+> group@:rwxp--a-R-c--s:-------:allow
+> everyone@:rw-p--a-R-c--s:-------:allow
+$ ls -l nfs4/xxx | cut -d' ' -f1
+> -r--rwxrw-
+
+# mv with NFSv4 ACLs.
+$ rm -f nfs4/xxx
+$ rm -f nfs4/yyy
+$ touch nfs4/xxx
+$ setfacl -a0 u:42:x:allow,g:43:w:allow nfs4/xxx
+$ mv nfs4/xxx nfs4/yyy
+$ getfacl -nq nfs4/yyy
+> user:42:--x-----------:-------:allow
+> group:43:-w------------:-------:allow
+> owner@:rw-p--aARWcCos:-------:allow
+> group@:r-----a-R-c--s:-------:allow
+> everyone@:r-----a-R-c--s:-------:allow
+$ ls -l nfs4/yyy | cut -d' ' -f1
+> -rw-r--r--+
+
+# mv from NFSv4 to POSIX.1e without any ACLs.
+$ rm -f nfs4/xxx
+$ rm -f posix/xxx
+$ touch nfs4/xxx
+$ chmod 456 nfs4/xxx
+$ ls -l nfs4/xxx | cut -d' ' -f1
+> -r--r-xrw-
+$ mv nfs4/xxx posix/xxx
+$ ls -l posix/xxx | cut -d' ' -f1
+> -r--r-xrw-
+
+# mv from NFSv4 to none.
+$ rm -f nfs4/xxx
+$ rm -f none/xxx
+$ touch nfs4/xxx
+$ chmod 345 nfs4/xxx
+$ ls -l nfs4/xxx | cut -d' ' -f1
+> --wxr--r-x
+$ setfacl -a0 u:42:x:allow,g:43:w:allow nfs4/xxx
+$ ls -l nfs4/xxx | cut -d' ' -f1
+> --wxr--r-x+
+$ mv nfs4/xxx none/xxx
+> mv: failed to set acl entries for none/xxx: Operation not supported
+$ ls -l none/xxx | cut -d' ' -f1
+> --wxr--r-x
+
+# mv from NFSv4 to POSIX.1e.
+$ rm -f nfs4/xxx
+$ rm -f posix/xxx
+$ touch nfs4/xxx
+$ chmod 345 nfs4/xxx
+$ ls -l nfs4/xxx | cut -d' ' -f1
+> --wxr--r-x
+$ setfacl -a0 u:42:x:allow,g:43:w:allow nfs4/xxx
+$ ls -l nfs4/xxx | cut -d' ' -f1
+> --wxr--r-x+
+$ mv nfs4/xxx posix/xxx
+> mv: failed to set acl entries for posix/xxx: Invalid argument
+$ ls -l posix/xxx | cut -d' ' -f1
+> --wxr--r-x
+
+# cp with POSIX.1e ACLs.
+$ rm -f posix/xxx
+$ rm -f posix/yyy
+$ touch posix/xxx
+$ setfacl -m u:42:x,g:43:w posix/xxx
+$ ls -l posix/xxx | cut -d' ' -f1
+> -rw-rwxr--+
+$ cp posix/xxx posix/yyy
+$ ls -l posix/yyy | cut -d' ' -f1
+> -rw-r-xr--
+
+# cp -p with POSIX.1e ACLs.
+$ rm -f posix/xxx
+$ rm -f posix/yyy
+$ touch posix/xxx
+$ setfacl -m u:42:x,g:43:w posix/xxx
+$ getfacl -nq posix/xxx
+> user::rw-
+> user:42:--x
+> group::r--
+> group:43:-w-
+> mask::rwx
+> other::r--
+$ ls -l posix/xxx | cut -d' ' -f1
+> -rw-rwxr--+
+$ cp -p posix/xxx posix/yyy
+$ getfacl -nq posix/yyy
+> user::rw-
+> user:42:--x
+> group::r--
+> group:43:-w-
+> mask::rwx
+> other::r--
+$ ls -l posix/yyy | cut -d' ' -f1
+> -rw-rwxr--+
+
+# cp from POSIX.1e to none.
+$ rm -f posix/xxx
+$ rm -f none/xxx
+$ touch posix/xxx
+$ setfacl -m u:42:x,g:43:w posix/xxx
+$ ls -l posix/xxx | cut -d' ' -f1
+> -rw-rwxr--+
+$ cp posix/xxx none/xxx
+$ ls -l none/xxx | cut -d' ' -f1
+> -rw-r-xr--
+
+# cp -p from POSIX.1e to none.
+$ rm -f posix/xxx
+$ rm -f none/xxx
+$ touch posix/xxx
+$ setfacl -m u:42:x,g:43:w posix/xxx
+$ ls -l posix/xxx | cut -d' ' -f1
+> -rw-rwxr--+
+$ cp -p posix/xxx none/xxx
+> cp: failed to set acl entries for none/xxx: Operation not supported
+$ ls -l none/xxx | cut -d' ' -f1
+> -rw-rwxr--
+
+# cp from POSIX.1e to NFSv4.
+$ rm -f posix/xxx
+$ rm -f nfs4/xxx
+$ touch posix/xxx
+$ setfacl -m u:42:x,g:43:w posix/xxx
+$ ls -l posix/xxx | cut -d' ' -f1
+> -rw-rwxr--+
+$ cp posix/xxx nfs4/xxx
+$ ls -l nfs4/xxx | cut -d' ' -f1
+> -rw-r-xr--
+
+# cp -p from POSIX.1e to NFSv4.
+$ rm -f posix/xxx
+$ rm -f nfs4/xxx
+$ touch posix/xxx
+$ setfacl -m u:42:x,g:43:w posix/xxx
+$ ls -l posix/xxx | cut -d' ' -f1
+> -rw-rwxr--+
+$ cp -p posix/xxx nfs4/xxx
+> cp: failed to set acl entries for nfs4/xxx: Invalid argument
+$ ls -l nfs4/xxx | cut -d' ' -f1
+> -rw-rwxr--
+
+# cp with NFSv4 ACLs.
+$ rm -f nfs4/xxx
+$ rm -f nfs4/yyy
+$ touch nfs4/xxx
+$ chmod 543 nfs4/xxx
+$ setfacl -a0 u:42:x:allow,g:43:w:allow nfs4/xxx
+$ ls -l nfs4/xxx | cut -d' ' -f1
+> -r-xr---wx+
+$ cp nfs4/xxx nfs4/yyy
+$ ls -l nfs4/yyy | cut -d' ' -f1
+> -r-xr----x
+
+# cp -p with NFSv4 ACLs.
+$ rm -f nfs4/xxx
+$ rm -f nfs4/yyy
+$ touch nfs4/xxx
+$ chmod 543 nfs4/xxx
+$ setfacl -a0 u:42:x:allow,g:43:w:allow nfs4/xxx
+$ cp -p nfs4/xxx nfs4/yyy
+$ getfacl -nq nfs4/yyy
+> user:42:--x-----------:-------:allow
+> group:43:-w------------:-------:allow
+> owner@:--x-----------:-------:allow
+> owner@:-w-p----------:-------:deny
+> group@:-wxp----------:-------:deny
+> owner@:r-x---aARWcCos:-------:allow
+> group@:r-----a-R-c--s:-------:allow
+> everyone@:-wxp--a-R-c--s:-------:allow
+$ ls -l nfs4/yyy | cut -d' ' -f1
+> -r-xr---wx+
+
+# cp from NFSv4 to none.
+$ rm -f nfs4/xxx
+$ rm -f none/xxx
+$ touch nfs4/xxx
+$ chmod 543 nfs4/xxx
+$ setfacl -a0 u:42:x:allow,g:43:w:allow nfs4/xxx
+$ ls -l nfs4/xxx | cut -d' ' -f1
+> -r-xr---wx+
+$ cp nfs4/xxx none/xxx
+$ ls -l none/xxx | cut -d' ' -f1
+> -r-xr----x
+
+# cp -p from NFSv4 to none.
+$ rm -f nfs4/xxx
+$ rm -f none/xxx
+$ touch nfs4/xxx
+$ chmod 543 nfs4/xxx
+$ setfacl -a0 u:42:x:allow,g:43:w:allow nfs4/xxx
+$ ls -l nfs4/xxx | cut -d' ' -f1
+> -r-xr---wx+
+$ cp -p nfs4/xxx none/xxx
+> cp: failed to set acl entries for none/xxx: Operation not supported
+$ ls -l none/xxx | cut -d' ' -f1
+> -r-xr---wx
+
+# cp from NFSv4 to POSIX.1e.
+$ rm -f nfs4/xxx
+$ rm -f posix/xxx
+$ touch nfs4/xxx
+$ chmod 543 nfs4/xxx
+$ setfacl -a0 u:42:x:allow,g:43:w:allow nfs4/xxx
+$ ls -l nfs4/xxx | cut -d' ' -f1
+> -r-xr---wx+
+$ cp nfs4/xxx posix/xxx
+$ ls -l posix/xxx | cut -d' ' -f1
+> -r-xr----x
+
+# cp -p from NFSv4 to POSIX.1e.
+$ rm -f nfs4/xxx
+$ rm -f posix/xxx
+$ touch nfs4/xxx
+$ chmod 543 nfs4/xxx
+$ setfacl -a0 u:42:x:allow,g:43:w:allow nfs4/xxx
+$ ls -l nfs4/xxx | cut -d' ' -f1
+> -r-xr---wx+
+$ cp -p nfs4/xxx posix/xxx
+> cp: failed to set acl entries for posix/xxx: Invalid argument
+$ ls -l posix/xxx | cut -d' ' -f1
+> -r-xr---wx
diff --git a/tests/sys/acl/tools-nfs4-psarc.test b/tests/sys/acl/tools-nfs4-psarc.test
new file mode 100644
index 000000000000..8f1ba30c428a
--- /dev/null
+++ b/tests/sys/acl/tools-nfs4-psarc.test
@@ -0,0 +1,583 @@
+# Copyright (c) 2008, 2009 Edward Tomasz Napierała <trasz@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# This is a tools-level test for NFSv4 ACL functionality with PSARC/2010/029
+# semantics. Run it as root using ACL-enabled kernel:
+#
+# /usr/src/tools/regression/acltools/run /usr/src/tools/regression/acltools/tools-nfs4-psarc.test
+#
+# WARNING: Creates files in unsafe way.
+
+$ whoami
+> root
+$ umask 022
+
+# Smoke test for getfacl(1).
+$ touch xxx
+$ getfacl xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> owner@:rw-p--aARWcCos:-------:allow
+> group@:r-----a-R-c--s:-------:allow
+> everyone@:r-----a-R-c--s:-------:allow
+
+$ getfacl -q xxx
+> owner@:rw-p--aARWcCos:-------:allow
+> group@:r-----a-R-c--s:-------:allow
+> everyone@:r-----a-R-c--s:-------:allow
+
+# Check verbose mode formatting.
+$ getfacl -v xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> owner@:read_data/write_data/append_data/read_attributes/write_attributes/read_xattr/write_xattr/read_acl/write_acl/write_owner/synchronize::allow
+> group@:read_data/read_attributes/read_xattr/read_acl/synchronize::allow
+> everyone@:read_data/read_attributes/read_xattr/read_acl/synchronize::allow
+
+# Test setfacl -a.
+$ setfacl -a2 u:0:write_acl:allow,g:1:read_acl:deny xxx
+$ getfacl -n xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> owner@:rw-p--aARWcCos:-------:allow
+> group@:r-----a-R-c--s:-------:allow
+> user:0:-----------C--:-------:allow
+> group:1:----------c---:-------:deny
+> everyone@:r-----a-R-c--s:-------:allow
+
+# Test user and group name resolving.
+$ rm xxx
+$ touch xxx
+$ setfacl -a2 u:root:write_acl:allow,g:daemon:read_acl:deny xxx
+$ getfacl xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> owner@:rw-p--aARWcCos:-------:allow
+> group@:r-----a-R-c--s:-------:allow
+> user:root:-----------C--:-------:allow
+> group:daemon:----------c---:-------:deny
+> everyone@:r-----a-R-c--s:-------:allow
+
+# Check whether ls correctly marks files with "+".
+$ ls -l xxx | cut -d' ' -f1
+> -rw-r--r--+
+
+# Test removing entries by number.
+$ setfacl -x 1 xxx
+$ getfacl -n xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> owner@:rw-p--aARWcCos:-------:allow
+> user:0:-----------C--:-------:allow
+> group:1:----------c---:-------:deny
+> everyone@:r-----a-R-c--s:-------:allow
+
+# Test setfacl -m.
+$ setfacl -a0 everyone@:rwx:deny xxx
+$ setfacl -a0 everyone@:rwx:deny xxx
+$ setfacl -a0 everyone@:rwx:deny xxx
+$ setfacl -m everyone@::deny xxx
+$ getfacl -n xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> everyone@:--------------:-------:deny
+> everyone@:--------------:-------:deny
+> everyone@:--------------:-------:deny
+> owner@:rw-p--aARWcCos:-------:allow
+> user:0:-----------C--:-------:allow
+> group:1:----------c---:-------:deny
+> everyone@:r-----a-R-c--s:-------:allow
+
+# Test getfacl -i.
+$ getfacl -i xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> everyone@:--------------:-------:deny
+> everyone@:--------------:-------:deny
+> everyone@:--------------:-------:deny
+> owner@:rw-p--aARWcCos:-------:allow
+> user:root:-----------C--:-------:allow:0
+> group:daemon:----------c---:-------:deny:1
+> everyone@:r-----a-R-c--s:-------:allow
+
+# Make sure cp without any flags does not copy copy the ACL.
+$ cp xxx yyy
+$ ls -l yyy | cut -d' ' -f1
+> -rw-r--r--
+
+# Make sure it does with the "-p" flag.
+$ rm yyy
+$ cp -p xxx yyy
+$ getfacl -n yyy
+> # file: yyy
+> # owner: root
+> # group: wheel
+> everyone@:--------------:-------:deny
+> everyone@:--------------:-------:deny
+> everyone@:--------------:-------:deny
+> owner@:rw-p--aARWcCos:-------:allow
+> user:0:-----------C--:-------:allow
+> group:1:----------c---:-------:deny
+> everyone@:r-----a-R-c--s:-------:allow
+
+$ rm yyy
+
+# Test removing entries by... by example?
+$ setfacl -x everyone@::deny xxx
+$ getfacl -n xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> owner@:rw-p--aARWcCos:-------:allow
+> user:0:-----------C--:-------:allow
+> group:1:----------c---:-------:deny
+> everyone@:r-----a-R-c--s:-------:allow
+
+# Test setfacl -b.
+$ setfacl -b xxx
+$ getfacl -n xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> owner@:rw-p--aARWcCos:-------:allow
+> group@:r-----a-R-c--s:-------:allow
+> everyone@:r-----a-R-c--s:-------:allow
+
+$ ls -l xxx | cut -d' ' -f1
+> -rw-r--r--
+
+# Check setfacl(1) and getfacl(1) with multiple files.
+$ touch xxx yyy zzz
+
+$ ls -l xxx yyy zzz | cut -d' ' -f1
+> -rw-r--r--
+> -rw-r--r--
+> -rw-r--r--
+
+$ setfacl -m u:42:x:allow,g:43:w:allow nnn xxx yyy zzz
+> setfacl: nnn: acl_get_file() failed: No such file or directory
+
+$ ls -l nnn xxx yyy zzz | cut -d' ' -f1
+> ls: nnn: No such file or directory
+> -rw-r--r--+
+> -rw-r--r--+
+> -rw-r--r--+
+
+$ getfacl -nq nnn xxx yyy zzz
+> getfacl: nnn: stat() failed: No such file or directory
+> user:42:--x-----------:-------:allow
+> group:43:-w------------:-------:allow
+> owner@:rw-p--aARWcCos:-------:allow
+> group@:r-----a-R-c--s:-------:allow
+> everyone@:r-----a-R-c--s:-------:allow
+>
+> user:42:--x-----------:-------:allow
+> group:43:-w------------:-------:allow
+> owner@:rw-p--aARWcCos:-------:allow
+> group@:r-----a-R-c--s:-------:allow
+> everyone@:r-----a-R-c--s:-------:allow
+>
+> user:42:--x-----------:-------:allow
+> group:43:-w------------:-------:allow
+> owner@:rw-p--aARWcCos:-------:allow
+> group@:r-----a-R-c--s:-------:allow
+> everyone@:r-----a-R-c--s:-------:allow
+
+$ setfacl -b nnn xxx yyy zzz
+> setfacl: nnn: acl_get_file() failed: No such file or directory
+
+$ ls -l nnn xxx yyy zzz | cut -d' ' -f1
+> ls: nnn: No such file or directory
+> -rw-r--r--
+> -rw-r--r--
+> -rw-r--r--
+
+$ rm xxx yyy zzz
+
+# Test applying mode to an ACL.
+$ touch xxx
+$ setfacl -a0 user:42:r:allow,user:43:w:deny,user:43:w:allow,user:44:x:allow -x everyone@::allow xxx
+$ chmod 600 xxx
+$ getfacl -n xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> owner@:rw-p--aARWcCos:-------:allow
+> group@:------a-R-c--s:-------:allow
+> everyone@:------a-R-c--s:-------:allow
+
+$ ls -l xxx | cut -d' ' -f1
+> -rw-------
+
+$ rm xxx
+$ touch xxx
+$ chown 42 xxx
+$ setfacl -a0 user:42:r:allow,user:43:w:deny,user:43:w:allow,user:44:x:allow xxx
+$ chmod 600 xxx
+$ getfacl -n xxx
+> # file: xxx
+> # owner: 42
+> # group: wheel
+> owner@:rw-p--aARWcCos:-------:allow
+> group@:------a-R-c--s:-------:allow
+> everyone@:------a-R-c--s:-------:allow
+$ ls -l xxx | cut -d' ' -f1
+> -rw-------
+
+$ rm xxx
+$ touch xxx
+$ chown 43 xxx
+$ setfacl -a0 user:42:r:allow,user:43:w:deny,user:43:w:allow,user:44:x:allow xxx
+$ chmod 124 xxx
+$ getfacl -n xxx
+> # file: xxx
+> # owner: 43
+> # group: wheel
+> owner@:rw-p----------:-------:deny
+> group@:r-------------:-------:deny
+> owner@:--x---aARWcCos:-------:allow
+> group@:-w-p--a-R-c--s:-------:allow
+> everyone@:r-----a-R-c--s:-------:allow
+$ ls -l xxx | cut -d' ' -f1
+> ---x-w-r--
+
+$ rm xxx
+$ touch xxx
+$ chown 43 xxx
+$ setfacl -a0 user:42:r:allow,user:43:w:deny,user:43:w:allow,user:44:x:allow xxx
+$ chmod 412 xxx
+$ getfacl -n xxx
+> # file: xxx
+> # owner: 43
+> # group: wheel
+> owner@:-wxp----------:-------:deny
+> group@:-w-p----------:-------:deny
+> owner@:r-----aARWcCos:-------:allow
+> group@:--x---a-R-c--s:-------:allow
+> everyone@:-w-p--a-R-c--s:-------:allow
+$ ls -l xxx | cut -d' ' -f1
+> -r----x-w-
+
+$ mkdir ddd
+$ setfacl -a0 group:44:rwapd:allow ddd
+$ setfacl -a0 group:43:write_data/delete_child:d:deny,group@:ad:allow ddd
+$ setfacl -a0 user:42:rx:fi:allow,group:42:write_data/delete_child:d:allow ddd
+$ setfacl -m everyone@:-w-p--a-R-c--s:fi:allow ddd
+$ getfacl -n ddd
+> # file: ddd
+> # owner: root
+> # group: wheel
+> user:42:r-x-----------:f-i----:allow
+> group:42:-w--D---------:-d-----:allow
+> group:43:-w--D---------:-d-----:deny
+> group@:-----da-------:-------:allow
+> group:44:rw-p-da-------:-------:allow
+> owner@:rwxp--aARWcCos:-------:allow
+> group@:r-x---a-R-c--s:-------:allow
+> everyone@:-w-p--a-R-c--s:f-i----:allow
+
+$ chmod 777 ddd
+$ getfacl -n ddd
+> # file: ddd
+> # owner: root
+> # group: wheel
+> owner@:rwxp--aARWcCos:-------:allow
+> group@:rwxp--a-R-c--s:-------:allow
+> everyone@:rwxp--a-R-c--s:-------:allow
+
+# Test applying ACL to mode.
+$ rmdir ddd
+$ mkdir ddd
+$ setfacl -a0 u:42:rwx:fi:allow ddd
+$ ls -ld ddd | cut -d' ' -f1
+> drwxr-xr-x+
+
+$ rmdir ddd
+$ mkdir ddd
+$ chmod 0 ddd
+$ setfacl -a0 owner@:r:allow,group@:w:deny,group@:wx:allow ddd
+$ ls -ld ddd | cut -d' ' -f1
+> dr----x---+
+
+$ rmdir ddd
+$ mkdir ddd
+$ chmod 0 ddd
+$ setfacl -a0 owner@:r:allow,group@:w:fi:deny,group@:wx:allow ddd
+$ ls -ld ddd | cut -d' ' -f1
+> dr---wx---+
+
+$ rmdir ddd
+$ mkdir ddd
+$ chmod 0 ddd
+$ setfacl -a0 owner@:r:allow,group:43:w:deny,group:43:wx:allow ddd
+$ ls -ld ddd | cut -d' ' -f1
+> dr--------+
+
+$ rmdir ddd
+$ mkdir ddd
+$ chmod 0 ddd
+$ setfacl -a0 owner@:r:allow,user:43:w:deny,user:43:wx:allow ddd
+$ ls -ld ddd | cut -d' ' -f1
+> dr--------+
+
+# Test inheritance.
+$ rmdir ddd
+$ mkdir ddd
+$ setfacl -a0 group:43:write_data/write_acl:fin:deny,u:43:rwxp:allow ddd
+$ setfacl -a0 user:42:rx:fi:allow,group:42:write_data/delete_child:dn:deny ddd
+$ setfacl -a0 user:42:write_acl/write_owner:fi:allow ddd
+$ setfacl -a0 group:41:read_data/read_attributes:dni:allow ddd
+$ setfacl -a0 user:41:write_data/write_attributes:fn:allow ddd
+$ getfacl -qn ddd
+> user:41:-w-----A------:f--n---:allow
+> group:41:r-----a-------:-din---:allow
+> user:42:-----------Co-:f-i----:allow
+> user:42:r-x-----------:f-i----:allow
+> group:42:-w--D---------:-d-n---:deny
+> group:43:-w---------C--:f-in---:deny
+> user:43:rwxp----------:-------:allow
+> owner@:rwxp--aARWcCos:-------:allow
+> group@:r-x---a-R-c--s:-------:allow
+> everyone@:r-x---a-R-c--s:-------:allow
+
+$ cd ddd
+$ touch xxx
+$ getfacl -qn xxx
+> user:41:--------------:------I:allow
+> user:42:--------------:------I:allow
+> user:42:r-------------:------I:allow
+> group:43:-w---------C--:------I:deny
+> owner@:rw-p--aARWcCos:-------:allow
+> group@:r-----a-R-c--s:-------:allow
+> everyone@:r-----a-R-c--s:-------:allow
+
+$ rm xxx
+$ umask 077
+$ touch xxx
+$ getfacl -qn xxx
+> user:41:--------------:------I:allow
+> user:42:--------------:------I:allow
+> user:42:--------------:------I:allow
+> group:43:-w---------C--:------I:deny
+> owner@:rw-p--aARWcCos:-------:allow
+> group@:------a-R-c--s:-------:allow
+> everyone@:------a-R-c--s:-------:allow
+
+$ rm xxx
+$ umask 770
+$ touch xxx
+$ getfacl -qn xxx
+> owner@:rw-p----------:-------:deny
+> group@:rw-p----------:-------:deny
+> user:41:--------------:------I:allow
+> user:42:--------------:------I:allow
+> user:42:--------------:------I:allow
+> group:43:-w---------C--:------I:deny
+> owner@:------aARWcCos:-------:allow
+> group@:------a-R-c--s:-------:allow
+> everyone@:rw-p--a-R-c--s:-------:allow
+
+$ rm xxx
+$ umask 707
+$ touch xxx
+$ getfacl -qn xxx
+> owner@:rw-p----------:-------:deny
+> user:41:-w------------:------I:allow
+> user:42:--------------:------I:allow
+> user:42:r-------------:------I:allow
+> group:43:-w---------C--:------I:deny
+> owner@:------aARWcCos:-------:allow
+> group@:rw-p--a-R-c--s:-------:allow
+> everyone@:------a-R-c--s:-------:allow
+
+$ umask 077
+$ mkdir yyy
+$ getfacl -qn yyy
+> group:41:------a-------:------I:allow
+> user:42:-----------Co-:f-i---I:allow
+> user:42:r-x-----------:f-i---I:allow
+> group:42:-w--D---------:------I:deny
+> owner@:rwxp--aARWcCos:-------:allow
+> group@:------a-R-c--s:-------:allow
+> everyone@:------a-R-c--s:-------:allow
+
+$ rmdir yyy
+$ umask 770
+$ mkdir yyy
+$ getfacl -qn yyy
+> owner@:rwxp----------:-------:deny
+> group@:rwxp----------:-------:deny
+> group:41:------a-------:------I:allow
+> user:42:-----------Co-:f-i---I:allow
+> user:42:r-x-----------:f-i---I:allow
+> group:42:-w--D---------:------I:deny
+> owner@:------aARWcCos:-------:allow
+> group@:------a-R-c--s:-------:allow
+> everyone@:rwxp--a-R-c--s:-------:allow
+
+$ rmdir yyy
+$ umask 707
+$ mkdir yyy
+$ getfacl -qn yyy
+> owner@:rwxp----------:-------:deny
+> group:41:r-----a-------:------I:allow
+> user:42:-----------Co-:f-i---I:allow
+> user:42:r-x-----------:f-i---I:allow
+> group:42:-w--D---------:------I:deny
+> owner@:------aARWcCos:-------:allow
+> group@:rwxp--a-R-c--s:-------:allow
+> everyone@:------a-R-c--s:-------:allow
+
+# There is some complication regarding how write_acl and write_owner flags
+# get inherited. Make sure we got it right.
+$ setfacl -b .
+$ setfacl -a0 u:42:Co:f:allow .
+$ setfacl -a0 u:43:Co:d:allow .
+$ setfacl -a0 u:44:Co:fd:allow .
+$ setfacl -a0 u:45:Co:fi:allow .
+$ setfacl -a0 u:46:Co:di:allow .
+$ setfacl -a0 u:47:Co:fdi:allow .
+$ setfacl -a0 u:48:Co:fn:allow .
+$ setfacl -a0 u:49:Co:dn:allow .
+$ setfacl -a0 u:50:Co:fdn:allow .
+$ setfacl -a0 u:51:Co:fni:allow .
+$ setfacl -a0 u:52:Co:dni:allow .
+$ setfacl -a0 u:53:Co:fdni:allow .
+$ umask 022
+$ rm xxx
+$ touch xxx
+$ getfacl -nq xxx
+> user:53:--------------:------I:allow
+> user:51:--------------:------I:allow
+> user:50:--------------:------I:allow
+> user:48:--------------:------I:allow
+> user:47:--------------:------I:allow
+> user:45:--------------:------I:allow
+> user:44:--------------:------I:allow
+> user:42:--------------:------I:allow
+> owner@:rw-p--aARWcCos:-------:allow
+> group@:r-----a-R-c--s:-------:allow
+> everyone@:r-----a-R-c--s:-------:allow
+
+$ rmdir yyy
+$ mkdir yyy
+$ getfacl -nq yyy
+> user:53:--------------:------I:allow
+> user:52:--------------:------I:allow
+> user:50:--------------:------I:allow
+> user:49:--------------:------I:allow
+> user:47:--------------:fd----I:allow
+> user:46:--------------:-d----I:allow
+> user:45:-----------Co-:f-i---I:allow
+> user:44:--------------:fd----I:allow
+> user:43:--------------:-d----I:allow
+> user:42:-----------Co-:f-i---I:allow
+> owner@:rwxp--aARWcCos:-------:allow
+> group@:r-x---a-R-c--s:-------:allow
+> everyone@:r-x---a-R-c--s:-------:allow
+
+$ setfacl -b .
+$ setfacl -a0 u:42:Co:f:deny .
+$ setfacl -a0 u:43:Co:d:deny .
+$ setfacl -a0 u:44:Co:fd:deny .
+$ setfacl -a0 u:45:Co:fi:deny .
+$ setfacl -a0 u:46:Co:di:deny .
+$ setfacl -a0 u:47:Co:fdi:deny .
+$ setfacl -a0 u:48:Co:fn:deny .
+$ setfacl -a0 u:49:Co:dn:deny .
+$ setfacl -a0 u:50:Co:fdn:deny .
+$ setfacl -a0 u:51:Co:fni:deny .
+$ setfacl -a0 u:52:Co:dni:deny .
+$ setfacl -a0 u:53:Co:fdni:deny .
+$ umask 022
+$ rm xxx
+$ touch xxx
+$ getfacl -nq xxx
+> user:53:-----------Co-:------I:deny
+> user:51:-----------Co-:------I:deny
+> user:50:-----------Co-:------I:deny
+> user:48:-----------Co-:------I:deny
+> user:47:-----------Co-:------I:deny
+> user:45:-----------Co-:------I:deny
+> user:44:-----------Co-:------I:deny
+> user:42:-----------Co-:------I:deny
+> owner@:rw-p--aARWcCos:-------:allow
+> group@:r-----a-R-c--s:-------:allow
+> everyone@:r-----a-R-c--s:-------:allow
+
+$ rmdir yyy
+$ mkdir yyy
+$ getfacl -nq yyy
+> user:53:-----------Co-:------I:deny
+> user:52:-----------Co-:------I:deny
+> user:50:-----------Co-:------I:deny
+> user:49:-----------Co-:------I:deny
+> user:47:-----------Co-:fd----I:deny
+> user:46:-----------Co-:-d----I:deny
+> user:45:-----------Co-:f-i---I:deny
+> user:44:-----------Co-:fd----I:deny
+> user:43:-----------Co-:-d----I:deny
+> user:42:-----------Co-:f-i---I:deny
+> owner@:rwxp--aARWcCos:-------:allow
+> group@:r-x---a-R-c--s:-------:allow
+> everyone@:r-x---a-R-c--s:-------:allow
+
+$ rmdir yyy
+$ rm xxx
+$ cd ..
+$ rmdir ddd
+$ rm xxx
+
+# Test basic recursive setting of ACLs.
+$ mkdir ddd
+$ touch ddd/xxx
+$ mkdir ddd/eee
+$ touch ddd/eee/yyy
+$ setfacl -R -m owner@:full_set:f:allow,group@:full_set::allow,everyone@:full_set::allow ddd
+$ getfacl -q ddd
+> owner@:rwxpDdaARWcCos:f------:allow
+> group@:rwxpDdaARWcCos:-------:allow
+> everyone@:rwxpDdaARWcCos:-------:allow
+$ getfacl -q ddd/xxx
+> owner@:rwxpDdaARWcCos:-------:allow
+> group@:rwxpDdaARWcCos:-------:allow
+> everyone@:rwxpDdaARWcCos:-------:allow
+$ getfacl -q ddd/eee
+> owner@:rwxpDdaARWcCos:f------:allow
+> group@:rwxpDdaARWcCos:-------:allow
+> everyone@:rwxpDdaARWcCos:-------:allow
+$ getfacl -q ddd/eee/yyy
+> owner@:rwxpDdaARWcCos:-------:allow
+> group@:rwxpDdaARWcCos:-------:allow
+> everyone@:rwxpDdaARWcCos:-------:allow
+
+$ rm -r ddd
diff --git a/tests/sys/acl/tools-nfs4-trivial.test b/tests/sys/acl/tools-nfs4-trivial.test
new file mode 100644
index 000000000000..0ff6c18e4bb9
--- /dev/null
+++ b/tests/sys/acl/tools-nfs4-trivial.test
@@ -0,0 +1,80 @@
+# Copyright (c) 2011 Edward Tomasz Napierała <trasz@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# This is a tools-level test for acl_is_trivial_np(3). Run it as root on ZFS.
+# Note that this does not work on UFS with NFSv4 ACLs enabled - UFS recognizes
+# both kind of trivial ACLs and replaces it by the default one.
+#
+# WARNING: Creates files in unsafe way.
+
+$ whoami
+> root
+$ umask 022
+
+# Check whether ls(1) correctly recognizes PSARC/2010/029-style trivial ACLs.
+$ touch xxx
+
+$ ls -l xxx | cut -d' ' -f1
+> -rw-r--r--
+
+$ getfacl -q xxx
+> owner@:rw-p--aARWcCos:-------:allow
+> group@:r-----a-R-c--s:-------:allow
+> everyone@:r-----a-R-c--s:-------:allow
+
+# Check whether ls(1) correctly recognizes draft-style trivial ACLs.
+$ rm xxx
+$ touch xxx
+$ setfacl -a0 owner@:x:deny,owner@:rwpAWCo:allow,group@:wxp:deny,group@:r:allow,everyone@:wxpAWCo:deny,everyone@:raRcs:allow xxx
+$ setfacl -x5 xxx
+$ setfacl -x5 xxx
+$ setfacl -x5 xxx
+
+$ ls -l xxx | cut -d' ' -f1
+> -rw-r--r--
+
+$ getfacl -q xxx
+> owner@:--x-----------:-------:deny
+> owner@:rw-p---A-W-Co-:-------:allow
+> group@:-wxp----------:-------:deny
+> group@:r-------------:-------:allow
+> everyone@:-wxp---A-W-Co-:-------:deny
+> everyone@:r-----a-R-c--s:-------:allow
+
+# Make sure ls(1) actually can recognize something as non-trivial.
+$ setfacl -x0 xxx
+
+$ ls -l xxx | cut -d' ' -f1
+> -rw-r--r--+
+
+$ getfacl -q xxx
+> owner@:rw-p---A-W-Co-:-------:allow
+> group@:-wxp----------:-------:deny
+> group@:r-------------:-------:allow
+> everyone@:-wxp---A-W-Co-:-------:deny
+> everyone@:r-----a-R-c--s:-------:allow
+
+$ rm xxx
+
diff --git a/tests/sys/acl/tools-nfs4.test b/tests/sys/acl/tools-nfs4.test
new file mode 100644
index 000000000000..01467b82b570
--- /dev/null
+++ b/tests/sys/acl/tools-nfs4.test
@@ -0,0 +1,861 @@
+# Copyright (c) 2008, 2009 Edward Tomasz Napierała <trasz@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# This is a tools-level test for NFSv4 ACL functionality. Run it as root
+# using ACL-enabled kernel:
+#
+# /usr/src/tools/regression/acltools/run /usr/src/tools/regression/acltools/tools-nfs4.test
+#
+# WARNING: Creates files in unsafe way.
+
+$ whoami
+> root
+$ umask 022
+
+# Smoke test for getfacl(1).
+$ touch xxx
+$ getfacl xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> owner@:--x-----------:-------:deny
+> owner@:rw-p---A-W-Co-:-------:allow
+> group@:-wxp----------:-------:deny
+> group@:r-------------:-------:allow
+> everyone@:-wxp---A-W-Co-:-------:deny
+> everyone@:r-----a-R-c--s:-------:allow
+
+$ getfacl -q xxx
+> owner@:--x-----------:-------:deny
+> owner@:rw-p---A-W-Co-:-------:allow
+> group@:-wxp----------:-------:deny
+> group@:r-------------:-------:allow
+> everyone@:-wxp---A-W-Co-:-------:deny
+> everyone@:r-----a-R-c--s:-------:allow
+
+# Check verbose mode formatting.
+$ getfacl -v xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> owner@:execute::deny
+> owner@:read_data/write_data/append_data/write_attributes/write_xattr/write_acl/write_owner::allow
+> group@:write_data/execute/append_data::deny
+> group@:read_data::allow
+> everyone@:write_data/execute/append_data/write_attributes/write_xattr/write_acl/write_owner::deny
+> everyone@:read_data/read_attributes/read_xattr/read_acl/synchronize::allow
+
+# Test setfacl -a.
+$ setfacl -a2 u:0:write_acl:allow,g:1:read_acl:deny xxx
+$ getfacl -n xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> owner@:--x-----------:-------:deny
+> owner@:rw-p---A-W-Co-:-------:allow
+> user:0:-----------C--:-------:allow
+> group:1:----------c---:-------:deny
+> group@:-wxp----------:-------:deny
+> group@:r-------------:-------:allow
+> everyone@:-wxp---A-W-Co-:-------:deny
+> everyone@:r-----a-R-c--s:-------:allow
+
+# Test user and group name resolving.
+$ rm xxx
+$ touch xxx
+$ setfacl -a2 u:root:write_acl:allow,g:daemon:read_acl:deny xxx
+$ getfacl xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> owner@:--x-----------:-------:deny
+> owner@:rw-p---A-W-Co-:-------:allow
+> user:root:-----------C--:-------:allow
+> group:daemon:----------c---:-------:deny
+> group@:-wxp----------:-------:deny
+> group@:r-------------:-------:allow
+> everyone@:-wxp---A-W-Co-:-------:deny
+> everyone@:r-----a-R-c--s:-------:allow
+
+# Check whether ls correctly marks files with "+".
+$ ls -l xxx | cut -d' ' -f1
+> -rw-r--r--+
+
+# Test removing entries by number.
+$ setfacl -x 4 xxx
+$ setfacl -x 4 xxx
+$ getfacl -n xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> owner@:--x-----------:-------:deny
+> owner@:rw-p---A-W-Co-:-------:allow
+> user:0:-----------C--:-------:allow
+> group:1:----------c---:-------:deny
+> everyone@:-wxp---A-W-Co-:-------:deny
+> everyone@:r-----a-R-c--s:-------:allow
+
+# Test setfacl -m.
+$ setfacl -a0 everyone@:rwx:deny xxx
+$ setfacl -a0 everyone@:rwx:deny xxx
+$ setfacl -a0 everyone@:rwx:deny xxx
+$ setfacl -m everyone@::deny xxx
+$ getfacl -n xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> everyone@:--------------:-------:deny
+> everyone@:--------------:-------:deny
+> everyone@:--------------:-------:deny
+> owner@:--x-----------:-------:deny
+> owner@:rw-p---A-W-Co-:-------:allow
+> user:0:-----------C--:-------:allow
+> group:1:----------c---:-------:deny
+> everyone@:--------------:-------:deny
+> everyone@:r-----a-R-c--s:-------:allow
+
+# Test getfacl -i.
+$ getfacl -i xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> everyone@:--------------:-------:deny
+> everyone@:--------------:-------:deny
+> everyone@:--------------:-------:deny
+> owner@:--x-----------:-------:deny
+> owner@:rw-p---A-W-Co-:-------:allow
+> user:root:-----------C--:-------:allow:0
+> group:daemon:----------c---:-------:deny:1
+> everyone@:--------------:-------:deny
+> everyone@:r-----a-R-c--s:-------:allow
+
+# Make sure cp without any flags does not copy copy the ACL.
+$ cp xxx yyy
+$ ls -l yyy | cut -d' ' -f1
+> -rw-r--r--
+
+# Make sure it does with the "-p" flag.
+$ rm yyy
+$ cp -p xxx yyy
+$ getfacl -n yyy
+> # file: yyy
+> # owner: root
+> # group: wheel
+> everyone@:--------------:-------:deny
+> everyone@:--------------:-------:deny
+> everyone@:--------------:-------:deny
+> owner@:--x-----------:-------:deny
+> owner@:rw-p---A-W-Co-:-------:allow
+> user:0:-----------C--:-------:allow
+> group:1:----------c---:-------:deny
+> everyone@:--------------:-------:deny
+> everyone@:r-----a-R-c--s:-------:allow
+
+$ rm yyy
+
+# Test removing entries by... by example?
+$ setfacl -x everyone@::deny xxx
+$ getfacl -n xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> owner@:--x-----------:-------:deny
+> owner@:rw-p---A-W-Co-:-------:allow
+> user:0:-----------C--:-------:allow
+> group:1:----------c---:-------:deny
+> everyone@:r-----a-R-c--s:-------:allow
+
+# Test setfacl -b.
+$ setfacl -b xxx
+$ getfacl -n xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> owner@:--x-----------:-------:deny
+> owner@:rw-p---A-W-Co-:-------:allow
+> group@:-wxp----------:-------:deny
+> group@:r-------------:-------:allow
+> everyone@:-wxp---A-W-Co-:-------:deny
+> everyone@:r-----a-R-c--s:-------:allow
+
+$ ls -l xxx | cut -d' ' -f1
+> -rw-r--r--
+
+# Check setfacl(1) and getfacl(1) with multiple files.
+$ touch xxx yyy zzz
+
+$ ls -l xxx yyy zzz | cut -d' ' -f1
+> -rw-r--r--
+> -rw-r--r--
+> -rw-r--r--
+
+$ setfacl -m u:42:x:allow,g:43:w:allow nnn xxx yyy zzz
+> setfacl: nnn: acl_get_file() failed: No such file or directory
+
+$ ls -l nnn xxx yyy zzz | cut -d' ' -f1
+> ls: nnn: No such file or directory
+> -rw-r--r--+
+> -rw-r--r--+
+> -rw-r--r--+
+
+$ getfacl -nq nnn xxx yyy zzz
+> getfacl: nnn: stat() failed: No such file or directory
+> user:42:--x-----------:-------:allow
+> group:43:-w------------:-------:allow
+> owner@:--x-----------:-------:deny
+> owner@:rw-p---A-W-Co-:-------:allow
+> group@:-wxp----------:-------:deny
+> group@:r-------------:-------:allow
+> everyone@:-wxp---A-W-Co-:-------:deny
+> everyone@:r-----a-R-c--s:-------:allow
+>
+> user:42:--x-----------:-------:allow
+> group:43:-w------------:-------:allow
+> owner@:--x-----------:-------:deny
+> owner@:rw-p---A-W-Co-:-------:allow
+> group@:-wxp----------:-------:deny
+> group@:r-------------:-------:allow
+> everyone@:-wxp---A-W-Co-:-------:deny
+> everyone@:r-----a-R-c--s:-------:allow
+>
+> user:42:--x-----------:-------:allow
+> group:43:-w------------:-------:allow
+> owner@:--x-----------:-------:deny
+> owner@:rw-p---A-W-Co-:-------:allow
+> group@:-wxp----------:-------:deny
+> group@:r-------------:-------:allow
+> everyone@:-wxp---A-W-Co-:-------:deny
+> everyone@:r-----a-R-c--s:-------:allow
+
+$ setfacl -b nnn xxx yyy zzz
+> setfacl: nnn: acl_get_file() failed: No such file or directory
+
+$ ls -l nnn xxx yyy zzz | cut -d' ' -f1
+> ls: nnn: No such file or directory
+> -rw-r--r--
+> -rw-r--r--
+> -rw-r--r--
+
+$ rm xxx yyy zzz
+
+# Test applying mode to an ACL.
+$ touch xxx
+$ setfacl -a0 user:42:r:allow,user:43:w:deny,user:43:w:allow,user:44:x:allow -x everyone@::allow xxx
+$ chmod 600 xxx
+$ getfacl -n xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> user:42:r-------------:-------:deny
+> user:42:r-------------:-------:allow
+> user:43:-w------------:-------:deny
+> user:43:-w------------:-------:allow
+> user:44:--x-----------:-------:deny
+> user:44:--x-----------:-------:allow
+> owner@:--------------:-------:deny
+> owner@:-------A-W-Co-:-------:allow
+> group@:--------------:-------:deny
+> group@:--------------:-------:allow
+> everyone@:-------A-W-Co-:-------:deny
+> owner@:--x-----------:-------:deny
+> owner@:rw-p---A-W-Co-:-------:allow
+> group@:rwxp----------:-------:deny
+> group@:--------------:-------:allow
+> everyone@:rwxp---A-W-Co-:-------:deny
+> everyone@:------a-R-c--s:-------:allow
+$ ls -l xxx | cut -d' ' -f1
+> -rw-------+
+
+$ rm xxx
+$ touch xxx
+$ chown 42 xxx
+$ setfacl -a0 user:42:r:allow,user:43:w:deny,user:43:w:allow,user:44:x:allow xxx
+$ chmod 600 xxx
+$ getfacl -n xxx
+> # file: xxx
+> # owner: 42
+> # group: wheel
+> user:42:--------------:-------:deny
+> user:42:r-------------:-------:allow
+> user:43:-w------------:-------:deny
+> user:43:-w------------:-------:allow
+> user:44:--x-----------:-------:deny
+> user:44:--x-----------:-------:allow
+> owner@:--x-----------:-------:deny
+> owner@:rw-p---A-W-Co-:-------:allow
+> group@:rwxp----------:-------:deny
+> group@:--------------:-------:allow
+> everyone@:rwxp---A-W-Co-:-------:deny
+> everyone@:------a-R-c--s:-------:allow
+$ ls -l xxx | cut -d' ' -f1
+> -rw-------+
+
+$ rm xxx
+$ touch xxx
+$ chown 43 xxx
+$ setfacl -a0 user:42:r:allow,user:43:w:deny,user:43:w:allow,user:44:x:allow xxx
+$ chmod 124 xxx
+$ getfacl -n xxx
+> # file: xxx
+> # owner: 43
+> # group: wheel
+> user:42:r-------------:-------:deny
+> user:42:r-------------:-------:allow
+> user:43:-w------------:-------:deny
+> user:43:-w------------:-------:allow
+> user:44:--x-----------:-------:deny
+> user:44:--x-----------:-------:allow
+> owner@:rw-p----------:-------:deny
+> owner@:--x----A-W-Co-:-------:allow
+> group@:r-x-----------:-------:deny
+> group@:-w-p----------:-------:allow
+> everyone@:-wxp---A-W-Co-:-------:deny
+> everyone@:r-----a-R-c--s:-------:allow
+$ ls -l xxx | cut -d' ' -f1
+> ---x-w-r--+
+
+$ rm xxx
+$ touch xxx
+$ chown 43 xxx
+$ setfacl -a0 user:42:r:allow,user:43:w:deny,user:43:w:allow,user:44:x:allow xxx
+$ chmod 412 xxx
+$ getfacl -n xxx
+> # file: xxx
+> # owner: 43
+> # group: wheel
+> user:42:r-------------:-------:deny
+> user:42:r-------------:-------:allow
+> user:43:-w------------:-------:deny
+> user:43:-w------------:-------:allow
+> user:44:--------------:-------:deny
+> user:44:--x-----------:-------:allow
+> owner@:-wxp----------:-------:deny
+> owner@:r------A-W-Co-:-------:allow
+> group@:rw-p----------:-------:deny
+> group@:--x-----------:-------:allow
+> everyone@:r-x----A-W-Co-:-------:deny
+> everyone@:-w-p--a-R-c--s:-------:allow
+$ ls -l xxx | cut -d' ' -f1
+> -r----x-w-+
+
+$ mkdir ddd
+$ setfacl -a0 group:44:rwapd:allow ddd
+$ setfacl -a0 group:43:write_data/delete_child:d:deny,group@:ad:allow ddd
+$ setfacl -a0 user:42:rx:fi:allow,group:42:write_data/delete_child:d:allow ddd
+$ setfacl -m everyone@:-w-p--a-R-c--s:fi:allow ddd
+$ getfacl -n ddd
+> # file: ddd
+> # owner: root
+> # group: wheel
+> user:42:r-x-----------:f-i----:allow
+> group:42:-w--D---------:-d-----:allow
+> group:43:-w--D---------:-d-----:deny
+> group@:-----da-------:-------:allow
+> group:44:rw-p-da-------:-------:allow
+> owner@:--------------:-------:deny
+> owner@:rwxp---A-W-Co-:-------:allow
+> group@:-w-p----------:-------:deny
+> group@:r-x-----------:-------:allow
+> everyone@:-w-p---A-W-Co-:-------:deny
+> everyone@:-w-p--a-R-c--s:f-i----:allow
+$ chmod 777 ddd
+$ getfacl -n ddd
+> # file: ddd
+> # owner: root
+> # group: wheel
+> user:42:r-x-----------:f-i----:allow
+> group:42:-w--D---------:-di----:allow
+> group:42:--------------:-------:deny
+> group:42:-w--D---------:-------:allow
+> group:43:-w--D---------:-di----:deny
+> group:43:-w--D---------:-------:deny
+> group@:-----da-------:-------:allow
+> group:44:--------------:-------:deny
+> group:44:rw-p-da-------:-------:allow
+> owner@:--------------:-------:deny
+> owner@:-------A-W-Co-:-------:allow
+> group@:--------------:-------:deny
+> group@:--------------:-------:allow
+> everyone@:-------A-W-Co-:-------:deny
+> everyone@:-w-p--a-R-c--s:f-i----:allow
+> owner@:--------------:-------:deny
+> owner@:rwxp---A-W-Co-:-------:allow
+> group@:--------------:-------:deny
+> group@:rwxp----------:-------:allow
+> everyone@:-------A-W-Co-:-------:deny
+> everyone@:rwxp--a-R-c--s:-------:allow
+
+$ rmdir ddd
+$ mkdir ddd
+$ setfacl -a0 group:44:rwapd:allow ddd
+$ setfacl -a0 group:43:write_data/delete_child:d:deny,group@:ad:allow ddd
+$ setfacl -a0 user:42:rx:fi:allow,group:42:write_data/delete_child:d:allow ddd
+$ setfacl -m everyone@:-w-p--a-R-c--s:fi:allow ddd
+$ chmod 124 ddd
+$ getfacl -n ddd
+> # file: ddd
+> # owner: root
+> # group: wheel
+> user:42:r-x-----------:f-i----:allow
+> group:42:-w--D---------:-di----:allow
+> group:42:--------------:-------:deny
+> group:42:----D---------:-------:allow
+> group:43:-w--D---------:-di----:deny
+> group:43:-w--D---------:-------:deny
+> group@:-----da-------:-------:allow
+> group:44:r-------------:-------:deny
+> group:44:r----da-------:-------:allow
+> owner@:--------------:-------:deny
+> owner@:-------A-W-Co-:-------:allow
+> group@:--------------:-------:deny
+> group@:--------------:-------:allow
+> everyone@:-------A-W-Co-:-------:deny
+> everyone@:-w-p--a-R-c--s:f-i----:allow
+> owner@:rw-p----------:-------:deny
+> owner@:--x----A-W-Co-:-------:allow
+> group@:r-x-----------:-------:deny
+> group@:-w-p----------:-------:allow
+> everyone@:-wxp---A-W-Co-:-------:deny
+> everyone@:r-----a-R-c--s:-------:allow
+
+$ rmdir ddd
+$ mkdir ddd
+$ setfacl -a0 group:44:rwapd:allow ddd
+$ setfacl -a0 group:43:write_data/delete_child:d:deny,group@:ad:allow ddd
+$ setfacl -a0 user:42:rx:allow,user:42:rx:fi:allow,group:42:write_data/delete_child:d:allow ddd
+$ setfacl -m everyone@:-w-p--a-R-c--s:fi:allow ddd
+$ chmod 412 ddd
+$ getfacl -n ddd
+> # file: ddd
+> # owner: root
+> # group: wheel
+> user:42:r-------------:-------:deny
+> user:42:r-x-----------:-------:allow
+> user:42:r-x-----------:f-i----:allow
+> group:42:-w--D---------:-di----:allow
+> group:42:-w------------:-------:deny
+> group:42:-w--D---------:-------:allow
+> group:43:-w--D---------:-di----:deny
+> group:43:-w--D---------:-------:deny
+> group@:-----da-------:-------:allow
+> group:44:rw-p----------:-------:deny
+> group:44:rw-p-da-------:-------:allow
+> owner@:--------------:-------:deny
+> owner@:-------A-W-Co-:-------:allow
+> group@:--------------:-------:deny
+> group@:--------------:-------:allow
+> everyone@:-------A-W-Co-:-------:deny
+> everyone@:-w-p--a-R-c--s:f-i----:allow
+> owner@:-wxp----------:-------:deny
+> owner@:r------A-W-Co-:-------:allow
+> group@:rw-p----------:-------:deny
+> group@:--x-----------:-------:allow
+> everyone@:r-x----A-W-Co-:-------:deny
+> everyone@:-w-p--a-R-c--s:-------:allow
+
+$ rmdir ddd
+$ mkdir ddd
+$ setfacl -a0 group:44:rwapd:allow ddd
+$ setfacl -a0 group:43:write_data/delete_child:d:deny,group@:ad:allow ddd
+$ setfacl -a0 user:42:rx:allow,user:42:rx:fi:allow,group:42:write_data/delete_child:d:allow ddd
+$ setfacl -m everyone@:-w-p--a-R-c--s:fi:allow ddd
+$ chown 42 ddd
+$ chmod 412 ddd
+$ getfacl -n ddd
+> # file: ddd
+> # owner: 42
+> # group: wheel
+> user:42:--x-----------:-------:deny
+> user:42:r-x-----------:-------:allow
+> user:42:r-x-----------:f-i----:allow
+> group:42:-w--D---------:-di----:allow
+> group:42:-w------------:-------:deny
+> group:42:-w--D---------:-------:allow
+> group:43:-w--D---------:-di----:deny
+> group:43:-w--D---------:-------:deny
+> group@:-----da-------:-------:allow
+> group:44:rw-p----------:-------:deny
+> group:44:rw-p-da-------:-------:allow
+> owner@:--------------:-------:deny
+> owner@:-------A-W-Co-:-------:allow
+> group@:--------------:-------:deny
+> group@:--------------:-------:allow
+> everyone@:-------A-W-Co-:-------:deny
+> everyone@:-w-p--a-R-c--s:f-i----:allow
+> owner@:-wxp----------:-------:deny
+> owner@:r------A-W-Co-:-------:allow
+> group@:rw-p----------:-------:deny
+> group@:--x-----------:-------:allow
+> everyone@:r-x----A-W-Co-:-------:deny
+> everyone@:-w-p--a-R-c--s:-------:allow
+
+# Test applying ACL to mode.
+$ rmdir ddd
+$ mkdir ddd
+$ setfacl -a0 u:42:rwx:fi:allow ddd
+$ ls -ld ddd | cut -d' ' -f1
+> drwxr-xr-x+
+
+$ rmdir ddd
+$ mkdir ddd
+$ chmod 0 ddd
+$ setfacl -a0 owner@:r:allow,group@:w:deny,group@:wx:allow ddd
+$ ls -ld ddd | cut -d' ' -f1
+> dr----x---+
+
+$ rmdir ddd
+$ mkdir ddd
+$ chmod 0 ddd
+$ setfacl -a0 owner@:r:allow,group@:w:fi:deny,group@:wx:allow ddd
+$ ls -ld ddd | cut -d' ' -f1
+> dr---wx---+
+
+$ rmdir ddd
+$ mkdir ddd
+$ chmod 0 ddd
+$ setfacl -a0 owner@:r:allow,group:43:w:deny,group:43:wx:allow ddd
+$ ls -ld ddd | cut -d' ' -f1
+> dr--------+
+
+$ rmdir ddd
+$ mkdir ddd
+$ chmod 0 ddd
+$ setfacl -a0 owner@:r:allow,user:43:w:deny,user:43:wx:allow ddd
+$ ls -ld ddd | cut -d' ' -f1
+> dr--------+
+
+# Test inheritance.
+$ rmdir ddd
+$ mkdir ddd
+$ setfacl -a0 group:43:write_data/write_acl:fin:deny,u:43:rwxp:allow ddd
+$ setfacl -a0 user:42:rx:fi:allow,group:42:write_data/delete_child:dn:deny ddd
+$ setfacl -a0 user:42:write_acl/write_owner:fi:allow ddd
+$ setfacl -a0 group:41:read_data/read_attributes:dni:allow ddd
+$ setfacl -a0 user:41:write_data/write_attributes:fn:allow ddd
+$ getfacl -qn ddd
+> user:41:-w-----A------:f--n---:allow
+> group:41:r-----a-------:-din---:allow
+> user:42:-----------Co-:f-i----:allow
+> user:42:r-x-----------:f-i----:allow
+> group:42:-w--D---------:-d-n---:deny
+> group:43:-w---------C--:f-in---:deny
+> user:43:rwxp----------:-------:allow
+> owner@:--------------:-------:deny
+> owner@:rwxp---A-W-Co-:-------:allow
+> group@:-w-p----------:-------:deny
+> group@:r-x-----------:-------:allow
+> everyone@:-w-p---A-W-Co-:-------:deny
+> everyone@:r-x---a-R-c--s:-------:allow
+
+$ cd ddd
+$ touch xxx
+$ getfacl -qn xxx
+> user:41:-w------------:-------:deny
+> user:41:-w-----A------:-------:allow
+> user:42:--------------:-------:deny
+> user:42:--------------:-------:allow
+> user:42:--x-----------:-------:deny
+> user:42:r-x-----------:-------:allow
+> group:43:-w---------C--:-------:deny
+> owner@:--x-----------:-------:deny
+> owner@:rw-p---A-W-Co-:-------:allow
+> group@:-wxp----------:-------:deny
+> group@:r-------------:-------:allow
+> everyone@:-wxp---A-W-Co-:-------:deny
+> everyone@:r-----a-R-c--s:-------:allow
+
+$ rm xxx
+$ umask 077
+$ touch xxx
+$ getfacl -qn xxx
+> user:41:-w------------:-------:deny
+> user:41:-w-----A------:-------:allow
+> user:42:--------------:-------:deny
+> user:42:--------------:-------:allow
+> user:42:r-x-----------:-------:deny
+> user:42:r-x-----------:-------:allow
+> group:43:-w---------C--:-------:deny
+> owner@:--x-----------:-------:deny
+> owner@:rw-p---A-W-Co-:-------:allow
+> group@:rwxp----------:-------:deny
+> group@:--------------:-------:allow
+> everyone@:rwxp---A-W-Co-:-------:deny
+> everyone@:------a-R-c--s:-------:allow
+
+$ rm xxx
+$ umask 770
+$ touch xxx
+$ getfacl -qn xxx
+> user:41:-w------------:-------:deny
+> user:41:-w-----A------:-------:allow
+> user:42:--------------:-------:deny
+> user:42:--------------:-------:allow
+> user:42:r-x-----------:-------:deny
+> user:42:r-x-----------:-------:allow
+> group:43:-w---------C--:-------:deny
+> owner@:rwxp----------:-------:deny
+> owner@:-------A-W-Co-:-------:allow
+> group@:rwxp----------:-------:deny
+> group@:--------------:-------:allow
+> everyone@:--x----A-W-Co-:-------:deny
+> everyone@:rw-p--a-R-c--s:-------:allow
+
+$ rm xxx
+$ umask 707
+$ touch xxx
+$ getfacl -qn xxx
+> user:41:--------------:-------:deny
+> user:41:-w-----A------:-------:allow
+> user:42:--------------:-------:deny
+> user:42:--------------:-------:allow
+> user:42:--x-----------:-------:deny
+> user:42:r-x-----------:-------:allow
+> group:43:-w---------C--:-------:deny
+> owner@:rwxp----------:-------:deny
+> owner@:-------A-W-Co-:-------:allow
+> group@:--x-----------:-------:deny
+> group@:rw-p----------:-------:allow
+> everyone@:rwxp---A-W-Co-:-------:deny
+> everyone@:------a-R-c--s:-------:allow
+
+$ umask 077
+$ mkdir yyy
+$ getfacl -qn yyy
+> group:41:r-------------:-------:deny
+> group:41:r-----a-------:-------:allow
+> user:42:-----------Co-:f-i----:allow
+> user:42:r-x-----------:f-i----:allow
+> group:42:-w--D---------:-------:deny
+> owner@:--------------:-------:deny
+> owner@:rwxp---A-W-Co-:-------:allow
+> group@:rwxp----------:-------:deny
+> group@:--------------:-------:allow
+> everyone@:rwxp---A-W-Co-:-------:deny
+> everyone@:------a-R-c--s:-------:allow
+
+$ rmdir yyy
+$ umask 770
+$ mkdir yyy
+$ getfacl -qn yyy
+> group:41:r-------------:-------:deny
+> group:41:r-----a-------:-------:allow
+> user:42:-----------Co-:f-i----:allow
+> user:42:r-x-----------:f-i----:allow
+> group:42:-w--D---------:-------:deny
+> owner@:rwxp----------:-------:deny
+> owner@:-------A-W-Co-:-------:allow
+> group@:rwxp----------:-------:deny
+> group@:--------------:-------:allow
+> everyone@:-------A-W-Co-:-------:deny
+> everyone@:rwxp--a-R-c--s:-------:allow
+
+$ rmdir yyy
+$ umask 707
+$ mkdir yyy
+$ getfacl -qn yyy
+> group:41:--------------:-------:deny
+> group:41:------a-------:-------:allow
+> user:42:-----------Co-:f-i----:allow
+> user:42:r-x-----------:f-i----:allow
+> group:42:-w--D---------:-------:deny
+> owner@:rwxp----------:-------:deny
+> owner@:-------A-W-Co-:-------:allow
+> group@:--------------:-------:deny
+> group@:rwxp----------:-------:allow
+> everyone@:rwxp---A-W-Co-:-------:deny
+> everyone@:------a-R-c--s:-------:allow
+
+# There is some complication regarding how write_acl and write_owner flags
+# get inherited. Make sure we got it right.
+$ setfacl -b .
+$ setfacl -a0 u:42:Co:f:allow .
+$ setfacl -a0 u:43:Co:d:allow .
+$ setfacl -a0 u:44:Co:fd:allow .
+$ setfacl -a0 u:45:Co:fi:allow .
+$ setfacl -a0 u:46:Co:di:allow .
+$ setfacl -a0 u:47:Co:fdi:allow .
+$ setfacl -a0 u:48:Co:fn:allow .
+$ setfacl -a0 u:49:Co:dn:allow .
+$ setfacl -a0 u:50:Co:fdn:allow .
+$ setfacl -a0 u:51:Co:fni:allow .
+$ setfacl -a0 u:52:Co:dni:allow .
+$ setfacl -a0 u:53:Co:fdni:allow .
+$ umask 022
+$ rm xxx
+$ touch xxx
+$ getfacl -nq xxx
+> user:53:--------------:-------:deny
+> user:53:--------------:-------:allow
+> user:51:--------------:-------:deny
+> user:51:--------------:-------:allow
+> user:50:--------------:-------:deny
+> user:50:--------------:-------:allow
+> user:48:--------------:-------:deny
+> user:48:--------------:-------:allow
+> user:47:--------------:-------:deny
+> user:47:--------------:-------:allow
+> user:45:--------------:-------:deny
+> user:45:--------------:-------:allow
+> user:44:--------------:-------:deny
+> user:44:--------------:-------:allow
+> user:42:--------------:-------:deny
+> user:42:--------------:-------:allow
+> owner@:--x-----------:-------:deny
+> owner@:rw-p---A-W-Co-:-------:allow
+> group@:-wxp----------:-------:deny
+> group@:r-------------:-------:allow
+> everyone@:-wxp---A-W-Co-:-------:deny
+> everyone@:r-----a-R-c--s:-------:allow
+
+$ rmdir yyy
+$ mkdir yyy
+$ getfacl -nq yyy
+> user:53:--------------:-------:deny
+> user:53:--------------:-------:allow
+> user:52:--------------:-------:deny
+> user:52:--------------:-------:allow
+> user:50:--------------:-------:deny
+> user:50:--------------:-------:allow
+> user:49:--------------:-------:deny
+> user:49:--------------:-------:allow
+> user:47:-----------Co-:fdi----:allow
+> user:47:--------------:-------:deny
+> user:47:--------------:-------:allow
+> user:46:-----------Co-:-di----:allow
+> user:46:--------------:-------:deny
+> user:46:--------------:-------:allow
+> user:45:-----------Co-:f-i----:allow
+> user:44:-----------Co-:fdi----:allow
+> user:44:--------------:-------:deny
+> user:44:--------------:-------:allow
+> user:43:-----------Co-:-di----:allow
+> user:43:--------------:-------:deny
+> user:43:--------------:-------:allow
+> user:42:-----------Co-:f-i----:allow
+> owner@:--------------:-------:deny
+> owner@:rwxp---A-W-Co-:-------:allow
+> group@:-w-p----------:-------:deny
+> group@:r-x-----------:-------:allow
+> everyone@:-w-p---A-W-Co-:-------:deny
+> everyone@:r-x---a-R-c--s:-------:allow
+
+$ setfacl -b .
+$ setfacl -a0 u:42:Co:f:deny .
+$ setfacl -a0 u:43:Co:d:deny .
+$ setfacl -a0 u:44:Co:fd:deny .
+$ setfacl -a0 u:45:Co:fi:deny .
+$ setfacl -a0 u:46:Co:di:deny .
+$ setfacl -a0 u:47:Co:fdi:deny .
+$ setfacl -a0 u:48:Co:fn:deny .
+$ setfacl -a0 u:49:Co:dn:deny .
+$ setfacl -a0 u:50:Co:fdn:deny .
+$ setfacl -a0 u:51:Co:fni:deny .
+$ setfacl -a0 u:52:Co:dni:deny .
+$ setfacl -a0 u:53:Co:fdni:deny .
+$ umask 022
+$ rm xxx
+$ touch xxx
+$ getfacl -nq xxx
+> user:53:-----------Co-:-------:deny
+> user:51:-----------Co-:-------:deny
+> user:50:-----------Co-:-------:deny
+> user:48:-----------Co-:-------:deny
+> user:47:-----------Co-:-------:deny
+> user:45:-----------Co-:-------:deny
+> user:44:-----------Co-:-------:deny
+> user:42:-----------Co-:-------:deny
+> owner@:--x-----------:-------:deny
+> owner@:rw-p---A-W-Co-:-------:allow
+> group@:-wxp----------:-------:deny
+> group@:r-------------:-------:allow
+> everyone@:-wxp---A-W-Co-:-------:deny
+> everyone@:r-----a-R-c--s:-------:allow
+
+$ rmdir yyy
+$ mkdir yyy
+$ getfacl -nq yyy
+> user:53:-----------Co-:-------:deny
+> user:52:-----------Co-:-------:deny
+> user:50:-----------Co-:-------:deny
+> user:49:-----------Co-:-------:deny
+> user:47:-----------Co-:fdi----:deny
+> user:47:-----------Co-:-------:deny
+> user:46:-----------Co-:-di----:deny
+> user:46:-----------Co-:-------:deny
+> user:45:-----------Co-:f-i----:deny
+> user:44:-----------Co-:fdi----:deny
+> user:44:-----------Co-:-------:deny
+> user:43:-----------Co-:-di----:deny
+> user:43:-----------Co-:-------:deny
+> user:42:-----------Co-:f-i----:deny
+> owner@:--------------:-------:deny
+> owner@:rwxp---A-W-Co-:-------:allow
+> group@:-w-p----------:-------:deny
+> group@:r-x-----------:-------:allow
+> everyone@:-w-p---A-W-Co-:-------:deny
+> everyone@:r-x---a-R-c--s:-------:allow
+
+$ rmdir yyy
+$ rm xxx
+$ cd ..
+$ rmdir ddd
+$ rm xxx
+
+# Test basic recursive setting of ACLs.
+$ mkdir ddd
+$ touch ddd/xxx
+$ mkdir ddd/eee
+$ touch ddd/eee/yyy
+$ setfacl -R -m owner@:full_set:f:allow,group@:full_set::allow,everyone@:full_set::allow ddd
+$ getfacl -q ddd
+> owner@:--------------:-------:deny
+> owner@:rwxpDdaARWcCos:f------:allow
+> group@:-w-p----------:-------:deny
+> group@:rwxpDdaARWcCos:-------:allow
+> everyone@:-w-p---A-W-Co-:-------:deny
+> everyone@:rwxpDdaARWcCos:-------:allow
+$ getfacl -q ddd/xxx
+> owner@:--x-----------:-------:deny
+> owner@:rwxpDdaARWcCos:-------:allow
+> group@:-wxp----------:-------:deny
+> group@:rwxpDdaARWcCos:-------:allow
+> everyone@:-wxp---A-W-Co-:-------:deny
+> everyone@:rwxpDdaARWcCos:-------:allow
+$ getfacl -q ddd/eee
+> owner@:--------------:-------:deny
+> owner@:rwxpDdaARWcCos:f------:allow
+> group@:-w-p----------:-------:deny
+> group@:rwxpDdaARWcCos:-------:allow
+> everyone@:-w-p---A-W-Co-:-------:deny
+> everyone@:rwxpDdaARWcCos:-------:allow
+$ getfacl -q ddd/eee/yyy
+> owner@:--x-----------:-------:deny
+> owner@:rwxpDdaARWcCos:-------:allow
+> group@:-wxp----------:-------:deny
+> group@:rwxpDdaARWcCos:-------:allow
+> everyone@:-wxp---A-W-Co-:-------:deny
+> everyone@:rwxpDdaARWcCos:-------:allow
+
+$ rm -r ddd
diff --git a/tests/sys/acl/tools-posix.test b/tests/sys/acl/tools-posix.test
new file mode 100644
index 000000000000..2b2a27d24a0d
--- /dev/null
+++ b/tests/sys/acl/tools-posix.test
@@ -0,0 +1,451 @@
+# Copyright (c) 2008, 2009 Edward Tomasz Napierała <trasz@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+# This is a tools-level test for POSIX.1e ACL functionality. Run it as root
+# using ACL-enabled kernel:
+#
+# /usr/src/tools/regression/acltools/run /usr/src/tools/regression/acltools/tools-posix.test
+#
+# WARNING: Creates files in unsafe way.
+
+$ whoami
+> root
+$ umask 022
+
+# Smoke test for getfacl(1).
+$ touch xxx
+$ getfacl -n xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> user::rw-
+> group::r--
+> other::r--
+
+$ getfacl -q xxx
+> user::rw-
+> group::r--
+> other::r--
+
+$ setfacl -m u:42:r,g:43:w xxx
+$ getfacl -n xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> user::rw-
+> user:42:r--
+> group::r--
+> group:43:-w-
+> mask::rw-
+> other::r--
+
+# Check whether ls correctly marks files with "+".
+$ ls -l xxx | cut -d' ' -f1
+> -rw-rw-r--+
+
+# Same as above, but for symlinks.
+$ ln -s xxx lll
+$ getfacl -h lll
+> # file: lll
+> # owner: root
+> # group: wheel
+> user::rwx
+> group::r-x
+> other::r-x
+
+$ getfacl -qh lll
+> user::rwx
+> group::r-x
+> other::r-x
+
+$ getfacl -q lll
+> user::rw-
+> user:42:r--
+> group::r--
+> group:43:-w-
+> mask::rw-
+> other::r--
+
+$ setfacl -hm u:44:x,g:45:w lll
+$ getfacl -h lll
+> # file: lll
+> # owner: root
+> # group: wheel
+> user::rwx
+> user:44:--x
+> group::r-x
+> group:45:-w-
+> mask::rwx
+> other::r-x
+
+$ ls -l lll | cut -d' ' -f1
+> lrwxrwxr-x+
+
+# Check whether the original file is left untouched.
+$ ls -l xxx | cut -d' ' -f1
+> -rw-rw-r--+
+
+$ rm lll
+
+# Test removing entries.
+$ setfacl -x user:42: xxx
+$ getfacl xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> user::rw-
+> group::r--
+> group:43:-w-
+> mask::rw-
+> other::r--
+
+$ setfacl -m u:42:r xxx
+$ getfacl -n xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> user::rw-
+> user:42:r--
+> group::r--
+> group:43:-w-
+> mask::rw-
+> other::r--
+
+# Test removing entries by number.
+$ setfacl -x 1 xxx
+$ getfacl -n xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> user::rw-
+> group::r--
+> group:43:-w-
+> mask::rw-
+> other::r--
+
+$ setfacl -m g:43:r xxx
+$ getfacl -n xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> user::rw-
+> group::r--
+> group:43:r--
+> mask::r--
+> other::r--
+
+# Make sure cp without any flags does not copy the ACL.
+$ cp xxx yyy
+$ ls -l yyy | cut -d' ' -f1
+> -rw-r--r--
+
+# Make sure it does with the "-p" flag.
+$ rm yyy
+$ cp -p xxx yyy
+$ getfacl -n yyy
+> # file: yyy
+> # owner: root
+> # group: wheel
+> user::rw-
+> group::r--
+> group:43:r--
+> mask::r--
+> other::r--
+
+$ rm yyy
+
+# Test removing entries by... by example?
+$ setfacl -m u:42:r,g:43:w xxx
+$ setfacl -x u:42: xxx
+$ getfacl -n xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> user::rw-
+> group::r--
+> group:43:-w-
+> mask::rw-
+> other::r--
+
+# Test setfacl -b.
+$ setfacl -b xxx
+$ getfacl -n xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> user::rw-
+> group::r--
+> mask::r--
+> other::r--
+
+$ ls -l xxx | cut -d' ' -f1
+> -rw-r--r--+
+
+$ setfacl -nb xxx
+$ getfacl -n xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> user::rw-
+> group::r--
+> other::r--
+
+$ ls -l xxx | cut -d' ' -f1
+> -rw-r--r--
+
+# Check setfacl(1) and getfacl(1) with multiple files.
+$ touch xxx yyy zzz
+
+$ ls -l xxx yyy zzz | cut -d' ' -f1
+> -rw-r--r--
+> -rw-r--r--
+> -rw-r--r--
+
+$ setfacl -m u:42:x,g:43:w nnn xxx yyy zzz
+> setfacl: nnn: acl_get_file() failed: No such file or directory
+
+$ ls -l nnn xxx yyy zzz | cut -d' ' -f1
+> ls: nnn: No such file or directory
+> -rw-rwxr--+
+> -rw-rwxr--+
+> -rw-rwxr--+
+
+$ getfacl -nq nnn xxx yyy zzz
+> getfacl: nnn: stat() failed: No such file or directory
+> user::rw-
+> user:42:--x
+> group::r--
+> group:43:-w-
+> mask::rwx
+> other::r--
+>
+> user::rw-
+> user:42:--x
+> group::r--
+> group:43:-w-
+> mask::rwx
+> other::r--
+>
+> user::rw-
+> user:42:--x
+> group::r--
+> group:43:-w-
+> mask::rwx
+> other::r--
+
+$ setfacl -b nnn xxx yyy zzz
+> setfacl: nnn: acl_get_file() failed: No such file or directory
+
+$ ls -l nnn xxx yyy zzz | cut -d' ' -f1
+> ls: nnn: No such file or directory
+> -rw-r--r--+
+> -rw-r--r--+
+> -rw-r--r--+
+
+$ setfacl -bn nnn xxx yyy zzz
+> setfacl: nnn: acl_get_file() failed: No such file or directory
+
+$ ls -l nnn xxx yyy zzz | cut -d' ' -f1
+> ls: nnn: No such file or directory
+> -rw-r--r--
+> -rw-r--r--
+> -rw-r--r--
+
+$ rm xxx yyy zzz
+
+# Check whether chmod actually does what it should do.
+$ touch xxx
+$ setfacl -m u:42:rwx,g:43:rwx xxx
+$ chmod 600 xxx
+$ getfacl -n xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> user::rw-
+> user:42:rwx # effective: ---
+> group::r-- # effective: ---
+> group:43:rwx # effective: ---
+> mask::---
+> other::---
+
+$ chmod 060 xxx
+$ getfacl -n xxx
+> # file: xxx
+> # owner: root
+> # group: wheel
+> user::---
+> user:42:rwx # effective: rw-
+> group::r--
+> group:43:rwx # effective: rw-
+> mask::rw-
+> other::---
+
+# Test default ACLs.
+$ umask 022
+$ mkdir ddd
+$ getfacl -qn ddd
+> user::rwx
+> group::r-x
+> other::r-x
+
+$ ls -l | grep ddd | cut -d' ' -f1
+> drwxr-xr-x
+
+$ getfacl -dq ddd
+$ setfacl -dm u::rwx,g::rx,o::rx,mask::rwx ddd
+$ getfacl -dqn ddd
+> user::rwx
+> group::r-x
+> mask::rwx
+> other::r-x
+
+# No change - ls(1) output doesn't take into account default ACLs.
+$ ls -l | grep ddd | cut -d' ' -f1
+> drwxr-xr-x
+
+$ setfacl -dm g:42:rwx,u:42:r ddd
+$ setfacl -dm g::w ddd
+$ getfacl -dqn ddd
+> user::rwx
+> user:42:r--
+> group::-w-
+> group:42:rwx
+> mask::rwx
+> other::r-x
+
+$ setfacl -dx group:42: ddd
+$ getfacl -dqn ddd
+> user::rwx
+> user:42:r--
+> group::-w-
+> mask::rw-
+> other::r-x
+
+$ ls -l | grep ddd | cut -d' ' -f1
+> drwxr-xr-x
+
+$ rmdir ddd
+$ rm xxx
+
+# Test inheritance.
+$ mkdir ddd
+
+$ touch ddd/xxx
+$ getfacl -q ddd/xxx
+> user::rw-
+> group::r--
+> other::r--
+
+$ mkdir ddd/ddd
+$ getfacl -q ddd/ddd
+> user::rwx
+> group::r-x
+> other::r-x
+
+$ rmdir ddd/ddd
+$ rm ddd/xxx
+
+$ setfacl -dm u::rwx,g::rx,o::rx,mask::rwx ddd
+$ setfacl -dm g:42:rwx,u:43:r ddd
+$ getfacl -dq ddd
+> user::rwx
+> user:43:r--
+> group::r-x
+> group:42:rwx
+> mask::rwx
+> other::r-x
+
+$ touch ddd/xxx
+$ getfacl -q ddd/xxx
+> user::rw-
+> user:43:r--
+> group::r-x # effective: r--
+> group:42:rwx # effective: r--
+> mask::r--
+> other::r--
+
+$ mkdir ddd/ddd
+$ getfacl -q ddd/ddd
+> user::rwx
+> user:43:r--
+> group::r-x
+> group:42:rwx # effective: r-x
+> mask::r-x
+> other::r-x
+
+$ rmdir ddd/ddd
+$ rm ddd/xxx
+$ rmdir ddd
+
+# Test if we deal properly with fifos.
+$ mkfifo fff
+$ ls -l fff | cut -d' ' -f1
+> prw-r--r--
+
+$ setfacl -m u:42:r,g:43:w fff
+$ getfacl fff
+> # file: fff
+> # owner: root
+> # group: wheel
+> user::rw-
+> user:42:r--
+> group::r--
+> group:43:-w-
+> mask::rw-
+> other::r--
+
+$ ls -l fff | cut -d' ' -f1
+> prw-rw-r--+
+
+$ setfacl -bn fff
+$ getfacl fff
+> # file: fff
+> # owner: root
+> # group: wheel
+> user::rw-
+> group::r--
+> other::r--
+
+$ ls -l fff | cut -d' ' -f1
+> prw-r--r--
+
+$ rm fff
+
+# Test if we deal properly with device files.
+$ mknod bbb b 1 1
+$ setfacl -m u:42:r,g:43:w bbb
+> setfacl: bbb: acl_get_file() failed: Operation not supported
+$ ls -l bbb | cut -d' ' -f1
+> brw-r--r--
+
+$ rm bbb
+
+$ mknod ccc c 1 1
+$ setfacl -m u:42:r,g:43:w ccc
+> setfacl: ccc: acl_get_file() failed: Operation not supported
+$ ls -l ccc | cut -d' ' -f1
+> crw-r--r--
+
+$ rm ccc
diff --git a/tests/sys/aio/Makefile b/tests/sys/aio/Makefile
new file mode 100644
index 000000000000..5cddb28c27a6
--- /dev/null
+++ b/tests/sys/aio/Makefile
@@ -0,0 +1,18 @@
+TESTSDIR= ${TESTSBASE}/sys/aio
+
+ATF_TESTS_C+= aio_test
+ATF_TESTS_C+= lio_test
+TEST_METADATA.aio_test+= timeout="30"
+TEST_METADATA.lio_test+= timeout="10"
+# Some lio_test testcases involve system resource limitations, so cannot run concurrently
+TEST_METADATA.lio_test+= is_exclusive=true
+
+PLAIN_TESTS_C+= aio_kqueue_test
+PLAIN_TESTS_C+= lio_kqueue_test
+
+LIBADD.aio_test+= util rt
+LIBADD.lio_test+= rt
+
+CFLAGS+= -I${.CURDIR:H:H}
+
+.include <bsd.test.mk>
diff --git a/tests/sys/aio/Makefile.depend b/tests/sys/aio/Makefile.depend
new file mode 100644
index 000000000000..65cfb7fd2dd0
--- /dev/null
+++ b/tests/sys/aio/Makefile.depend
@@ -0,0 +1,20 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/librt \
+ lib/libthr \
+ lib/libutil \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/aio/aio_kqueue_test.c b/tests/sys/aio/aio_kqueue_test.c
new file mode 100644
index 000000000000..5e5cb40d0752
--- /dev/null
+++ b/tests/sys/aio/aio_kqueue_test.c
@@ -0,0 +1,236 @@
+/*-
+ * Copyright (C) 2005 IronPort Systems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Prerequisities:
+ * - AIO support must be compiled into the kernel (see sys/<arch>/NOTES for
+ * more details).
+ *
+ * Note: it is a good idea to run this against a physical drive to
+ * exercise the physio fast path (ie. aio_kqueue /dev/<something safe>)
+ */
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/time.h>
+#include <assert.h>
+#include <aio.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "freebsd_test_suite/macros.h"
+#include "local.h"
+
+#define PATH_TEMPLATE "aio.XXXXXXXXXX"
+
+#define MAX_RUNS 300
+/* #define DEBUG */
+
+int
+main (int argc, char *argv[])
+{
+ struct aiocb **iocb, *kq_iocb;
+ char *file, pathname[sizeof(PATH_TEMPLATE)+1];
+ struct kevent kq_returned;
+ struct timespec ts;
+ char buffer[32768];
+ int max_queue_per_proc;
+ size_t max_queue_per_proc_size;
+#ifdef DEBUG
+ int cancel, error;
+#endif
+ int failed = 0, fd, kq, pending, result, run;
+ int tmp_file = 0;
+ int i, j;
+
+ PLAIN_REQUIRE_KERNEL_MODULE("aio", 0);
+ PLAIN_REQUIRE_UNSAFE_AIO(0);
+
+ max_queue_per_proc_size = sizeof(max_queue_per_proc);
+ if (sysctlbyname("vfs.aio.max_aio_queue_per_proc",
+ &max_queue_per_proc, &max_queue_per_proc_size, NULL, 0) != 0)
+ err(1, "sysctlbyname");
+ iocb = calloc(max_queue_per_proc, sizeof(struct aiocb*));
+ if (iocb == NULL)
+ err(1, "calloc");
+
+ kq = kqueue();
+ if (kq < 0) {
+ perror("No kqeueue\n");
+ exit(1);
+ }
+
+ if (argc == 1) {
+ strcpy(pathname, PATH_TEMPLATE);
+ fd = mkstemp(pathname);
+ file = pathname;
+ tmp_file = 1;
+ } else {
+ file = argv[1];
+ fd = open(file, O_RDWR|O_CREAT, 0666);
+ }
+ if (fd == -1)
+ err(1, "Can't open %s\n", file);
+
+ for (run = 0; run < MAX_RUNS; run++){
+#ifdef DEBUG
+ printf("Run %d\n", run);
+#endif
+ for (i = 0; i < max_queue_per_proc; i++) {
+ iocb[i] = (struct aiocb *)calloc(1,
+ sizeof(struct aiocb));
+ if (iocb[i] == NULL)
+ err(1, "calloc");
+ }
+
+ pending = 0;
+ for (i = 0; i < max_queue_per_proc; i++) {
+ pending++;
+ iocb[i]->aio_nbytes = sizeof(buffer);
+ iocb[i]->aio_buf = buffer;
+ iocb[i]->aio_fildes = fd;
+ iocb[i]->aio_offset = iocb[i]->aio_nbytes * i * run;
+
+ iocb[i]->aio_sigevent.sigev_notify_kqueue = kq;
+ iocb[i]->aio_sigevent.sigev_value.sival_ptr = iocb[i];
+ iocb[i]->aio_sigevent.sigev_notify = SIGEV_KEVENT;
+
+ result = aio_write(iocb[i]);
+ if (result != 0) {
+ perror("aio_write");
+ printf("Result %d iteration %d\n", result, i);
+ exit(1);
+ }
+#ifdef DEBUG
+ printf("WRITE %d is at %p\n", i, iocb[i]);
+#endif
+ result = rand();
+ if (result < RAND_MAX/32) {
+ if (result > RAND_MAX/64) {
+ result = aio_cancel(fd, iocb[i]);
+#ifdef DEBUG
+ printf("Cancel %d %p result %d\n", i, iocb[i], result);
+#endif
+ if (result == AIO_CANCELED) {
+ aio_return(iocb[i]);
+ iocb[i] = NULL;
+ pending--;
+ }
+ }
+ }
+ }
+#ifdef DEBUG
+ cancel = max_queue_per_proc - pending;
+#endif
+
+ i = 0;
+ while (pending) {
+
+ for (;;) {
+
+ bzero(&kq_returned, sizeof(kq_returned));
+ ts.tv_sec = 0;
+ ts.tv_nsec = 1;
+ result = kevent(kq, NULL, 0,
+ &kq_returned, 1, &ts);
+#ifdef DEBUG
+ error = errno;
+#endif
+ if (result < 0)
+ perror("kevent error: ");
+ kq_iocb = kq_returned.udata;
+#ifdef DEBUG
+ printf("kevent %d %d errno %d return.ident %p "
+ "return.data %p return.udata %p %p"
+ " filter %d flags %#x fflags %#x\n",
+ i, result, error,
+ (void*)kq_returned.ident,
+ (void*)kq_returned.data,
+ kq_returned.udata,
+ kq_iocb,
+ kq_returned.filter,
+ kq_returned.flags,
+ kq_returned.fflags);
+ if (result > 0)
+ printf("\tsigev_notify_kevent_flags %#x\n",
+ ((struct aiocb*)(kq_returned.ident))->aio_sigevent.sigev_notify_kevent_flags);
+#endif
+
+ if (kq_iocb)
+ break;
+#ifdef DEBUG
+ printf("Try again left %d out of %d %d\n",
+ pending, max_queue_per_proc, cancel);
+#endif
+ }
+
+ for (j = 0; j < max_queue_per_proc && iocb[j] != kq_iocb;
+ j++) ;
+ assert(j < max_queue_per_proc);
+#ifdef DEBUG
+ printf("kq_iocb %p\n", kq_iocb);
+
+ printf("Error Result for %d is %d pending %d\n",
+ j, result, pending);
+#endif
+ result = aio_return(kq_iocb);
+#ifdef DEBUG
+ printf("Return Result for %d is %d\n\n", j, result);
+#endif
+ if (result != sizeof(buffer)) {
+ printf("FAIL: run %d, operation %d, result %d "
+ " (errno=%d) should be %zu\n", run, pending,
+ result, errno, sizeof(buffer));
+ failed++;
+ } else
+ printf("PASS: run %d, left %d\n", run,
+ pending - 1);
+
+ free(kq_iocb);
+ iocb[j] = NULL;
+ pending--;
+ i++;
+ }
+
+ for (i = 0; i < max_queue_per_proc; i++)
+ free(iocb[i]);
+
+ }
+
+ if (tmp_file)
+ unlink(pathname);
+
+ if (failed != 0)
+ printf("FAIL: %d tests failed\n", failed);
+ else
+ printf("PASS: All tests passed\n");
+
+ exit (failed == 0 ? 0 : 1);
+}
diff --git a/tests/sys/aio/aio_test.c b/tests/sys/aio/aio_test.c
new file mode 100644
index 000000000000..64939825ec66
--- /dev/null
+++ b/tests/sys/aio/aio_test.c
@@ -0,0 +1,2128 @@
+/*-
+ * Copyright (c) 2004 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Regression test to do some very basic AIO exercising on several types of
+ * file descriptors. Currently, the tests consist of initializing a fixed
+ * size buffer with pseudo-random data, writing it to one fd using AIO, then
+ * reading it from a second descriptor using AIO. For some targets, the same
+ * fd is used for write and read (i.e., file, md device), but for others the
+ * operation is performed on a peer (pty, socket, fifo, etc). For each file
+ * descriptor type, several completion methods are tested. This test program
+ * does not attempt to exercise error cases or more subtle asynchronous
+ * behavior, just make sure that the basic operations work on some basic object
+ * types.
+ */
+
+#include <sys/param.h>
+#include <sys/event.h>
+#include <sys/mdioctl.h>
+#include <sys/module.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+
+#include <aio.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <limits.h>
+#include <semaphore.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "freebsd_test_suite/macros.h"
+#include "local.h"
+
+/*
+ * GLOBAL_MAX sets the largest usable buffer size to be read and written, as
+ * it sizes ac_buffer in the aio_context structure. It is also the default
+ * size for file I/O. For other types, we use smaller blocks or we risk
+ * blocking (and we run in a single process/thread so that would be bad).
+ */
+#define GLOBAL_MAX 16384
+
+#define BUFFER_MAX GLOBAL_MAX
+
+/*
+ * A completion function will block until the aio has completed, then return
+ * the result of the aio. errno will be set appropriately.
+ */
+typedef ssize_t (*completion)(struct aiocb*);
+
+struct aio_context {
+ int ac_read_fd, ac_write_fd;
+ long ac_seed;
+ char ac_buffer[GLOBAL_MAX];
+ int ac_buflen;
+ int ac_seconds;
+};
+
+static sem_t completions;
+
+/*
+ * Fill a buffer given a seed that can be fed into srandom() to initialize
+ * the PRNG in a repeatable manner.
+ */
+static void
+aio_fill_buffer(char *buffer, int len, long seed)
+{
+ char ch;
+ int i;
+
+ srandom(seed);
+ for (i = 0; i < len; i++) {
+ ch = random() & 0xff;
+ buffer[i] = ch;
+ }
+}
+
+/*
+ * Test that a buffer matches a given seed. See aio_fill_buffer(). Return
+ * (1) on a match, (0) on a mismatch.
+ */
+static int
+aio_test_buffer(char *buffer, int len, long seed)
+{
+ char ch;
+ int i;
+
+ srandom(seed);
+ for (i = 0; i < len; i++) {
+ ch = random() & 0xff;
+ if (buffer[i] != ch)
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * Initialize a testing context given the file descriptors provided by the
+ * test setup.
+ */
+static void
+aio_context_init(struct aio_context *ac, int read_fd,
+ int write_fd, int buflen)
+{
+
+ ATF_REQUIRE_MSG(buflen <= BUFFER_MAX,
+ "aio_context_init: buffer too large (%d > %d)",
+ buflen, BUFFER_MAX);
+ bzero(ac, sizeof(*ac));
+ ac->ac_read_fd = read_fd;
+ ac->ac_write_fd = write_fd;
+ ac->ac_buflen = buflen;
+ srandomdev();
+ ac->ac_seed = random();
+ aio_fill_buffer(ac->ac_buffer, buflen, ac->ac_seed);
+ ATF_REQUIRE_MSG(aio_test_buffer(ac->ac_buffer, buflen,
+ ac->ac_seed) != 0, "aio_test_buffer: internal error");
+}
+
+static ssize_t
+poll(struct aiocb *aio)
+{
+ int error;
+
+ while ((error = aio_error(aio)) == EINPROGRESS)
+ usleep(25000);
+ if (error)
+ return (error);
+ else
+ return (aio_return(aio));
+}
+
+static void
+sigusr1_handler(int sig __unused)
+{
+ ATF_REQUIRE_EQ(0, sem_post(&completions));
+}
+
+static void
+thr_handler(union sigval sv __unused)
+{
+ ATF_REQUIRE_EQ(0, sem_post(&completions));
+}
+
+static ssize_t
+poll_signaled(struct aiocb *aio)
+{
+ int error;
+
+ ATF_REQUIRE_EQ(0, sem_wait(&completions));
+ error = aio_error(aio);
+ switch (error) {
+ case EINPROGRESS:
+ errno = EINTR;
+ return (-1);
+ case 0:
+ return (aio_return(aio));
+ default:
+ return (error);
+ }
+}
+
+/*
+ * Setup a signal handler for signal delivery tests
+ * This isn't thread safe, but it's ok since ATF runs each testcase in a
+ * separate process
+ */
+static struct sigevent*
+setup_signal(void)
+{
+ static struct sigevent sev;
+
+ ATF_REQUIRE_EQ(0, sem_init(&completions, false, 0));
+ sev.sigev_notify = SIGEV_SIGNAL;
+ sev.sigev_signo = SIGUSR1;
+ ATF_REQUIRE(SIG_ERR != signal(SIGUSR1, sigusr1_handler));
+ return (&sev);
+}
+
+/*
+ * Setup a thread for thread delivery tests
+ * This isn't thread safe, but it's ok since ATF runs each testcase in a
+ * separate process
+ */
+static struct sigevent*
+setup_thread(void)
+{
+ static struct sigevent sev;
+
+ ATF_REQUIRE_EQ(0, sem_init(&completions, false, 0));
+ sev.sigev_notify = SIGEV_THREAD;
+ sev.sigev_notify_function = thr_handler;
+ sev.sigev_notify_attributes = NULL;
+ return (&sev);
+}
+
+static ssize_t
+suspend(struct aiocb *aio)
+{
+ const struct aiocb *const iocbs[] = {aio};
+ int error;
+
+ error = aio_suspend(iocbs, 1, NULL);
+ if (error == 0)
+ return (aio_return(aio));
+ else
+ return (error);
+}
+
+static ssize_t
+waitcomplete(struct aiocb *aio)
+{
+ struct aiocb *aiop;
+ ssize_t ret;
+
+ ret = aio_waitcomplete(&aiop, NULL);
+ ATF_REQUIRE_EQ(aio, aiop);
+ return (ret);
+}
+
+/*
+ * Setup an iocb for kqueue notification. This isn't thread
+ * safe, but it's ok because ATF runs every test case in a separate process.
+ */
+static struct sigevent*
+setup_kqueue(void)
+{
+ static struct sigevent sev;
+ static int kq;
+
+ kq = kqueue();
+ ATF_REQUIRE(kq >= 0);
+
+ memset(&sev, 0, sizeof(sev));
+ sev.sigev_notify_kqueue = kq;
+ sev.sigev_value.sival_ptr = (void*)0xdeadbeef;
+ sev.sigev_notify = SIGEV_KEVENT;
+
+ return (&sev);
+}
+
+static ssize_t
+poll_kqueue(struct aiocb *aio)
+{
+ int kq, nevents;
+ struct kevent events[1];
+
+ kq = aio->aio_sigevent.sigev_notify_kqueue;
+
+ nevents = kevent(kq, NULL, 0, events, 1, NULL);
+ ATF_CHECK_EQ(1, nevents);
+ ATF_CHECK_EQ(events[0].ident, (uintptr_t) aio);
+ ATF_CHECK_EQ(events[0].filter, EVFILT_AIO);
+ ATF_CHECK_EQ(events[0].flags, EV_EOF);
+ ATF_CHECK_EQ(events[0].fflags, 0);
+ ATF_CHECK_EQ(events[0].data, 0);
+ ATF_CHECK_EQ((uintptr_t)events[0].udata, 0xdeadbeef);
+
+ return (aio_return(aio));
+}
+
+/*
+ * Perform a simple write test of our initialized data buffer to the provided
+ * file descriptor.
+ */
+static void
+aio_write_test(struct aio_context *ac, completion comp, struct sigevent *sev)
+{
+ struct aiocb aio;
+ ssize_t len;
+
+ bzero(&aio, sizeof(aio));
+ aio.aio_buf = ac->ac_buffer;
+ aio.aio_nbytes = ac->ac_buflen;
+ aio.aio_fildes = ac->ac_write_fd;
+ aio.aio_offset = 0;
+ if (sev)
+ aio.aio_sigevent = *sev;
+
+ if (aio_write(&aio) < 0)
+ atf_tc_fail("aio_write failed: %s", strerror(errno));
+
+ len = comp(&aio);
+ if (len < 0)
+ atf_tc_fail("aio failed: %s", strerror(errno));
+
+ if (len != ac->ac_buflen)
+ atf_tc_fail("aio short write (%jd)", (intmax_t)len);
+}
+
+/*
+ * Perform a vectored I/O test of our initialized data buffer to the provided
+ * file descriptor.
+ *
+ * To vectorize the linear buffer, chop it up into two pieces of dissimilar
+ * size, and swap their offsets.
+ */
+static void
+aio_writev_test(struct aio_context *ac, completion comp, struct sigevent *sev)
+{
+ struct aiocb aio;
+ struct iovec iov[2];
+ size_t len0, len1;
+ ssize_t len;
+
+ bzero(&aio, sizeof(aio));
+
+ aio.aio_fildes = ac->ac_write_fd;
+ aio.aio_offset = 0;
+ len0 = ac->ac_buflen * 3 / 4;
+ len1 = ac->ac_buflen / 4;
+ iov[0].iov_base = ac->ac_buffer + len1;
+ iov[0].iov_len = len0;
+ iov[1].iov_base = ac->ac_buffer;
+ iov[1].iov_len = len1;
+ aio.aio_iov = iov;
+ aio.aio_iovcnt = 2;
+ if (sev)
+ aio.aio_sigevent = *sev;
+
+ if (aio_writev(&aio) < 0)
+ atf_tc_fail("aio_writev failed: %s", strerror(errno));
+
+ len = comp(&aio);
+ if (len < 0)
+ atf_tc_fail("aio failed: %s", strerror(errno));
+
+ if (len != ac->ac_buflen)
+ atf_tc_fail("aio short write (%jd)", (intmax_t)len);
+}
+
+/*
+ * Perform a simple read test of our initialized data buffer from the
+ * provided file descriptor.
+ */
+static void
+aio_read_test(struct aio_context *ac, completion comp, struct sigevent *sev)
+{
+ struct aiocb aio;
+ ssize_t len;
+
+ bzero(ac->ac_buffer, ac->ac_buflen);
+ bzero(&aio, sizeof(aio));
+ aio.aio_buf = ac->ac_buffer;
+ aio.aio_nbytes = ac->ac_buflen;
+ aio.aio_fildes = ac->ac_read_fd;
+ aio.aio_offset = 0;
+ if (sev)
+ aio.aio_sigevent = *sev;
+
+ if (aio_read(&aio) < 0)
+ atf_tc_fail("aio_read failed: %s", strerror(errno));
+
+ len = comp(&aio);
+ if (len < 0)
+ atf_tc_fail("aio failed: %s", strerror(errno));
+
+ ATF_REQUIRE_EQ_MSG(len, ac->ac_buflen,
+ "aio short read (%jd)", (intmax_t)len);
+
+ if (aio_test_buffer(ac->ac_buffer, ac->ac_buflen, ac->ac_seed) == 0)
+ atf_tc_fail("buffer mismatched");
+}
+
+static void
+aio_readv_test(struct aio_context *ac, completion comp, struct sigevent *sev)
+{
+ struct aiocb aio;
+ struct iovec iov[2];
+ size_t len0, len1;
+ ssize_t len;
+
+ bzero(ac->ac_buffer, ac->ac_buflen);
+ bzero(&aio, sizeof(aio));
+ aio.aio_fildes = ac->ac_read_fd;
+ aio.aio_offset = 0;
+ len0 = ac->ac_buflen * 3 / 4;
+ len1 = ac->ac_buflen / 4;
+ iov[0].iov_base = ac->ac_buffer + len1;
+ iov[0].iov_len = len0;
+ iov[1].iov_base = ac->ac_buffer;
+ iov[1].iov_len = len1;
+ aio.aio_iov = iov;
+ aio.aio_iovcnt = 2;
+ if (sev)
+ aio.aio_sigevent = *sev;
+
+ if (aio_readv(&aio) < 0)
+ atf_tc_fail("aio_read failed: %s", strerror(errno));
+
+ len = comp(&aio);
+ if (len < 0)
+ atf_tc_fail("aio failed: %s", strerror(errno));
+
+ ATF_REQUIRE_EQ_MSG(len, ac->ac_buflen,
+ "aio short read (%jd)", (intmax_t)len);
+
+ if (aio_test_buffer(ac->ac_buffer, ac->ac_buflen, ac->ac_seed) == 0)
+ atf_tc_fail("buffer mismatched");
+}
+
+/*
+ * Series of type-specific tests for AIO. For now, we just make sure we can
+ * issue a write and then a read to each type. We assume that once a write
+ * is issued, a read can follow.
+ */
+
+/*
+ * Test with a classic file. Assumes we can create a moderate size temporary
+ * file.
+ */
+#define FILE_LEN GLOBAL_MAX
+#define FILE_PATHNAME "testfile"
+
+static void
+aio_file_test(completion comp, struct sigevent *sev, bool vectored)
+{
+ struct aio_context ac;
+ int fd;
+
+ ATF_REQUIRE_KERNEL_MODULE("aio");
+ ATF_REQUIRE_UNSAFE_AIO();
+
+ fd = open(FILE_PATHNAME, O_RDWR | O_CREAT, 0600);
+ ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
+
+ aio_context_init(&ac, fd, fd, FILE_LEN);
+ if (vectored) {
+ aio_writev_test(&ac, comp, sev);
+ aio_readv_test(&ac, comp, sev);
+ } else {
+ aio_write_test(&ac, comp, sev);
+ aio_read_test(&ac, comp, sev);
+ }
+ close(fd);
+}
+
+ATF_TC_WITHOUT_HEAD(file_kq);
+ATF_TC_BODY(file_kq, tc)
+{
+ aio_file_test(poll_kqueue, setup_kqueue(), false);
+}
+
+ATF_TC_WITHOUT_HEAD(file_poll);
+ATF_TC_BODY(file_poll, tc)
+{
+ aio_file_test(poll, NULL, false);
+}
+
+ATF_TC_WITHOUT_HEAD(file_signal);
+ATF_TC_BODY(file_signal, tc)
+{
+ aio_file_test(poll_signaled, setup_signal(), false);
+}
+
+ATF_TC_WITHOUT_HEAD(file_suspend);
+ATF_TC_BODY(file_suspend, tc)
+{
+ aio_file_test(suspend, NULL, false);
+}
+
+ATF_TC_WITHOUT_HEAD(file_thread);
+ATF_TC_BODY(file_thread, tc)
+{
+ aio_file_test(poll_signaled, setup_thread(), false);
+}
+
+ATF_TC_WITHOUT_HEAD(file_waitcomplete);
+ATF_TC_BODY(file_waitcomplete, tc)
+{
+ aio_file_test(waitcomplete, NULL, false);
+}
+
+#define FIFO_LEN 256
+#define FIFO_PATHNAME "testfifo"
+
+static void
+aio_fifo_test(completion comp, struct sigevent *sev)
+{
+ int error, read_fd = -1, write_fd = -1;
+ struct aio_context ac;
+
+ ATF_REQUIRE_KERNEL_MODULE("aio");
+ ATF_REQUIRE_UNSAFE_AIO();
+
+ ATF_REQUIRE_MSG(mkfifo(FIFO_PATHNAME, 0600) != -1,
+ "mkfifo failed: %s", strerror(errno));
+
+ read_fd = open(FIFO_PATHNAME, O_RDONLY | O_NONBLOCK);
+ if (read_fd == -1) {
+ error = errno;
+ errno = error;
+ atf_tc_fail("read_fd open failed: %s",
+ strerror(errno));
+ }
+
+ write_fd = open(FIFO_PATHNAME, O_WRONLY);
+ if (write_fd == -1) {
+ error = errno;
+ errno = error;
+ atf_tc_fail("write_fd open failed: %s",
+ strerror(errno));
+ }
+
+ aio_context_init(&ac, read_fd, write_fd, FIFO_LEN);
+ aio_write_test(&ac, comp, sev);
+ aio_read_test(&ac, comp, sev);
+
+ close(read_fd);
+ close(write_fd);
+}
+
+ATF_TC_WITHOUT_HEAD(fifo_kq);
+ATF_TC_BODY(fifo_kq, tc)
+{
+ aio_fifo_test(poll_kqueue, setup_kqueue());
+}
+
+ATF_TC_WITHOUT_HEAD(fifo_poll);
+ATF_TC_BODY(fifo_poll, tc)
+{
+ aio_fifo_test(poll, NULL);
+}
+
+ATF_TC_WITHOUT_HEAD(fifo_signal);
+ATF_TC_BODY(fifo_signal, tc)
+{
+ aio_fifo_test(poll_signaled, setup_signal());
+}
+
+ATF_TC_WITHOUT_HEAD(fifo_suspend);
+ATF_TC_BODY(fifo_suspend, tc)
+{
+ aio_fifo_test(suspend, NULL);
+}
+
+ATF_TC_WITHOUT_HEAD(fifo_thread);
+ATF_TC_BODY(fifo_thread, tc)
+{
+ aio_fifo_test(poll_signaled, setup_thread());
+}
+
+ATF_TC_WITHOUT_HEAD(fifo_waitcomplete);
+ATF_TC_BODY(fifo_waitcomplete, tc)
+{
+ aio_fifo_test(waitcomplete, NULL);
+}
+
+#define UNIX_SOCKETPAIR_LEN 256
+static void
+aio_unix_socketpair_test(completion comp, struct sigevent *sev, bool vectored)
+{
+ struct aio_context ac;
+ struct rusage ru_before, ru_after;
+ int sockets[2];
+
+ ATF_REQUIRE_KERNEL_MODULE("aio");
+
+ ATF_REQUIRE_MSG(socketpair(PF_UNIX, SOCK_STREAM, 0, sockets) != -1,
+ "socketpair failed: %s", strerror(errno));
+
+ aio_context_init(&ac, sockets[0], sockets[1], UNIX_SOCKETPAIR_LEN);
+ ATF_REQUIRE_MSG(getrusage(RUSAGE_SELF, &ru_before) != -1,
+ "getrusage failed: %s", strerror(errno));
+ if (vectored) {
+ aio_writev_test(&ac, comp, sev);
+ aio_readv_test(&ac, comp, sev);
+ } else {
+ aio_write_test(&ac, comp, sev);
+ aio_read_test(&ac, comp, sev);
+ }
+ ATF_REQUIRE_MSG(getrusage(RUSAGE_SELF, &ru_after) != -1,
+ "getrusage failed: %s", strerror(errno));
+ ATF_REQUIRE(ru_after.ru_msgsnd == ru_before.ru_msgsnd + 1);
+ ATF_REQUIRE(ru_after.ru_msgrcv == ru_before.ru_msgrcv + 1);
+
+ close(sockets[0]);
+ close(sockets[1]);
+}
+
+ATF_TC_WITHOUT_HEAD(socket_kq);
+ATF_TC_BODY(socket_kq, tc)
+{
+ aio_unix_socketpair_test(poll_kqueue, setup_kqueue(), false);
+}
+
+ATF_TC_WITHOUT_HEAD(socket_poll);
+ATF_TC_BODY(socket_poll, tc)
+{
+ aio_unix_socketpair_test(poll, NULL, false);
+}
+
+ATF_TC_WITHOUT_HEAD(socket_signal);
+ATF_TC_BODY(socket_signal, tc)
+{
+ aio_unix_socketpair_test(poll_signaled, setup_signal(), false);
+}
+
+ATF_TC_WITHOUT_HEAD(socket_suspend);
+ATF_TC_BODY(socket_suspend, tc)
+{
+ aio_unix_socketpair_test(suspend, NULL, false);
+}
+
+ATF_TC_WITHOUT_HEAD(socket_thread);
+ATF_TC_BODY(socket_thread, tc)
+{
+ aio_unix_socketpair_test(poll_signaled, setup_thread(), false);
+}
+
+ATF_TC_WITHOUT_HEAD(socket_waitcomplete);
+ATF_TC_BODY(socket_waitcomplete, tc)
+{
+ aio_unix_socketpair_test(waitcomplete, NULL, false);
+}
+
+struct aio_pty_arg {
+ int apa_read_fd;
+ int apa_write_fd;
+};
+
+#define PTY_LEN 256
+static void
+aio_pty_test(completion comp, struct sigevent *sev)
+{
+ struct aio_context ac;
+ int read_fd, write_fd;
+ struct termios ts;
+ int error;
+
+ ATF_REQUIRE_KERNEL_MODULE("aio");
+ ATF_REQUIRE_UNSAFE_AIO();
+
+ ATF_REQUIRE_MSG(openpty(&read_fd, &write_fd, NULL, NULL, NULL) == 0,
+ "openpty failed: %s", strerror(errno));
+
+
+ if (tcgetattr(write_fd, &ts) < 0) {
+ error = errno;
+ errno = error;
+ atf_tc_fail("tcgetattr failed: %s", strerror(errno));
+ }
+ cfmakeraw(&ts);
+ if (tcsetattr(write_fd, TCSANOW, &ts) < 0) {
+ error = errno;
+ errno = error;
+ atf_tc_fail("tcsetattr failed: %s", strerror(errno));
+ }
+ aio_context_init(&ac, read_fd, write_fd, PTY_LEN);
+
+ aio_write_test(&ac, comp, sev);
+ aio_read_test(&ac, comp, sev);
+
+ close(read_fd);
+ close(write_fd);
+}
+
+ATF_TC_WITHOUT_HEAD(pty_kq);
+ATF_TC_BODY(pty_kq, tc)
+{
+ aio_pty_test(poll_kqueue, setup_kqueue());
+}
+
+ATF_TC_WITHOUT_HEAD(pty_poll);
+ATF_TC_BODY(pty_poll, tc)
+{
+ aio_pty_test(poll, NULL);
+}
+
+ATF_TC_WITHOUT_HEAD(pty_signal);
+ATF_TC_BODY(pty_signal, tc)
+{
+ aio_pty_test(poll_signaled, setup_signal());
+}
+
+ATF_TC_WITHOUT_HEAD(pty_suspend);
+ATF_TC_BODY(pty_suspend, tc)
+{
+ aio_pty_test(suspend, NULL);
+}
+
+ATF_TC_WITHOUT_HEAD(pty_thread);
+ATF_TC_BODY(pty_thread, tc)
+{
+ aio_pty_test(poll_signaled, setup_thread());
+}
+
+ATF_TC_WITHOUT_HEAD(pty_waitcomplete);
+ATF_TC_BODY(pty_waitcomplete, tc)
+{
+ aio_pty_test(waitcomplete, NULL);
+}
+
+#define PIPE_LEN 256
+static void
+aio_pipe_test(completion comp, struct sigevent *sev)
+{
+ struct aio_context ac;
+ int pipes[2];
+
+ ATF_REQUIRE_KERNEL_MODULE("aio");
+ ATF_REQUIRE_UNSAFE_AIO();
+
+ ATF_REQUIRE_MSG(pipe(pipes) != -1,
+ "pipe failed: %s", strerror(errno));
+
+ aio_context_init(&ac, pipes[0], pipes[1], PIPE_LEN);
+ aio_write_test(&ac, comp, sev);
+ aio_read_test(&ac, comp, sev);
+
+ close(pipes[0]);
+ close(pipes[1]);
+}
+
+ATF_TC_WITHOUT_HEAD(pipe_kq);
+ATF_TC_BODY(pipe_kq, tc)
+{
+ aio_pipe_test(poll_kqueue, setup_kqueue());
+}
+
+ATF_TC_WITHOUT_HEAD(pipe_poll);
+ATF_TC_BODY(pipe_poll, tc)
+{
+ aio_pipe_test(poll, NULL);
+}
+
+ATF_TC_WITHOUT_HEAD(pipe_signal);
+ATF_TC_BODY(pipe_signal, tc)
+{
+ aio_pipe_test(poll_signaled, setup_signal());
+}
+
+ATF_TC_WITHOUT_HEAD(pipe_suspend);
+ATF_TC_BODY(pipe_suspend, tc)
+{
+ aio_pipe_test(suspend, NULL);
+}
+
+ATF_TC_WITHOUT_HEAD(pipe_thread);
+ATF_TC_BODY(pipe_thread, tc)
+{
+ aio_pipe_test(poll_signaled, setup_thread());
+}
+
+ATF_TC_WITHOUT_HEAD(pipe_waitcomplete);
+ATF_TC_BODY(pipe_waitcomplete, tc)
+{
+ aio_pipe_test(waitcomplete, NULL);
+}
+
+#define MD_LEN GLOBAL_MAX
+#define MDUNIT_LINK "mdunit_link"
+
+static int
+aio_md_setup(void)
+{
+ int error, fd, mdctl_fd, unit;
+ char pathname[PATH_MAX];
+ struct md_ioctl mdio;
+ char buf[80];
+
+ ATF_REQUIRE_KERNEL_MODULE("aio");
+
+ mdctl_fd = open("/dev/" MDCTL_NAME, O_RDWR, 0);
+ ATF_REQUIRE_MSG(mdctl_fd != -1,
+ "opening /dev/%s failed: %s", MDCTL_NAME, strerror(errno));
+
+ bzero(&mdio, sizeof(mdio));
+ mdio.md_version = MDIOVERSION;
+ mdio.md_type = MD_MALLOC;
+ mdio.md_options = MD_AUTOUNIT | MD_COMPRESS;
+ mdio.md_mediasize = GLOBAL_MAX;
+ mdio.md_sectorsize = 512;
+ strlcpy(buf, __func__, sizeof(buf));
+ mdio.md_label = buf;
+
+ if (ioctl(mdctl_fd, MDIOCATTACH, &mdio) < 0) {
+ error = errno;
+ errno = error;
+ atf_tc_fail("ioctl MDIOCATTACH failed: %s", strerror(errno));
+ }
+ close(mdctl_fd);
+
+ /* Store the md unit number in a symlink for future cleanup */
+ unit = mdio.md_unit;
+ snprintf(buf, sizeof(buf), "%d", unit);
+ ATF_REQUIRE_EQ(0, symlink(buf, MDUNIT_LINK));
+ snprintf(pathname, PATH_MAX, "/dev/md%d", unit);
+ fd = open(pathname, O_RDWR);
+ ATF_REQUIRE_MSG(fd != -1,
+ "opening %s failed: %s", pathname, strerror(errno));
+
+ return (fd);
+}
+
+static void
+aio_md_cleanup(void)
+{
+ struct md_ioctl mdio;
+ int mdctl_fd, n, unit;
+ char buf[80];
+
+ mdctl_fd = open("/dev/" MDCTL_NAME, O_RDWR, 0);
+ if (mdctl_fd < 0) {
+ fprintf(stderr, "opening /dev/%s failed: %s\n", MDCTL_NAME,
+ strerror(errno));
+ return;
+ }
+ n = readlink(MDUNIT_LINK, buf, sizeof(buf) - 1);
+ if (n > 0) {
+ buf[n] = '\0';
+ if (sscanf(buf, "%d", &unit) == 1 && unit >= 0) {
+ bzero(&mdio, sizeof(mdio));
+ mdio.md_version = MDIOVERSION;
+ mdio.md_unit = unit;
+ if (ioctl(mdctl_fd, MDIOCDETACH, &mdio) == -1) {
+ fprintf(stderr,
+ "ioctl MDIOCDETACH unit %d failed: %s\n",
+ unit, strerror(errno));
+ }
+ }
+ }
+
+ close(mdctl_fd);
+}
+
+static void
+aio_md_test(completion comp, struct sigevent *sev, bool vectored)
+{
+ struct aio_context ac;
+ int fd;
+
+ fd = aio_md_setup();
+ aio_context_init(&ac, fd, fd, MD_LEN);
+ if (vectored) {
+ aio_writev_test(&ac, comp, sev);
+ aio_readv_test(&ac, comp, sev);
+ } else {
+ aio_write_test(&ac, comp, sev);
+ aio_read_test(&ac, comp, sev);
+ }
+
+ close(fd);
+}
+
+ATF_TC_WITH_CLEANUP(md_kq);
+ATF_TC_HEAD(md_kq, tc)
+{
+
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(md_kq, tc)
+{
+ aio_md_test(poll_kqueue, setup_kqueue(), false);
+}
+ATF_TC_CLEANUP(md_kq, tc)
+{
+ aio_md_cleanup();
+}
+
+ATF_TC_WITH_CLEANUP(md_poll);
+ATF_TC_HEAD(md_poll, tc)
+{
+
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(md_poll, tc)
+{
+ aio_md_test(poll, NULL, false);
+}
+ATF_TC_CLEANUP(md_poll, tc)
+{
+ aio_md_cleanup();
+}
+
+ATF_TC_WITH_CLEANUP(md_signal);
+ATF_TC_HEAD(md_signal, tc)
+{
+
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(md_signal, tc)
+{
+ aio_md_test(poll_signaled, setup_signal(), false);
+}
+ATF_TC_CLEANUP(md_signal, tc)
+{
+ aio_md_cleanup();
+}
+
+ATF_TC_WITH_CLEANUP(md_suspend);
+ATF_TC_HEAD(md_suspend, tc)
+{
+
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(md_suspend, tc)
+{
+ aio_md_test(suspend, NULL, false);
+}
+ATF_TC_CLEANUP(md_suspend, tc)
+{
+ aio_md_cleanup();
+}
+
+ATF_TC_WITH_CLEANUP(md_thread);
+ATF_TC_HEAD(md_thread, tc)
+{
+
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(md_thread, tc)
+{
+ aio_md_test(poll_signaled, setup_thread(), false);
+}
+ATF_TC_CLEANUP(md_thread, tc)
+{
+ aio_md_cleanup();
+}
+
+ATF_TC_WITH_CLEANUP(md_waitcomplete);
+ATF_TC_HEAD(md_waitcomplete, tc)
+{
+
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(md_waitcomplete, tc)
+{
+ aio_md_test(waitcomplete, NULL, false);
+}
+ATF_TC_CLEANUP(md_waitcomplete, tc)
+{
+ aio_md_cleanup();
+}
+
+#define ZVOL_VDEV_PATHNAME "test_vdev"
+#define POOL_SIZE (1 << 28) /* 256 MB */
+#define ZVOL_SIZE "64m"
+#define POOL_NAME "aio_testpool"
+#define ZVOL_NAME "aio_testvol"
+
+static int
+aio_zvol_setup(const char *unique)
+{
+ FILE *pidfile;
+ int fd;
+ pid_t pid;
+ char vdev_name[160];
+ char pool_name[80];
+ char cmd[160];
+ char zvol_name[160];
+ char devname[160];
+
+ ATF_REQUIRE_KERNEL_MODULE("aio");
+ ATF_REQUIRE_KERNEL_MODULE("zfs");
+
+ pid = getpid();
+ snprintf(vdev_name, sizeof(vdev_name), "%s", ZVOL_VDEV_PATHNAME);
+ snprintf(pool_name, sizeof(pool_name), "%s_%s.%d", POOL_NAME, unique,
+ pid);
+ snprintf(zvol_name, sizeof(zvol_name), "%s/%s_%s", pool_name, ZVOL_NAME,
+ unique);
+
+ fd = open(vdev_name, O_RDWR | O_CREAT, 0600);
+ ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
+ ATF_REQUIRE_EQ_MSG(0,
+ ftruncate(fd, POOL_SIZE), "ftruncate failed: %s", strerror(errno));
+ close(fd);
+
+ pidfile = fopen("pidfile", "w");
+ ATF_REQUIRE_MSG(NULL != pidfile, "fopen: %s", strerror(errno));
+ fprintf(pidfile, "%d", pid);
+ fclose(pidfile);
+
+ snprintf(cmd, sizeof(cmd), "zpool create %s $PWD/%s", pool_name,
+ vdev_name);
+ ATF_REQUIRE_EQ_MSG(0, system(cmd),
+ "zpool create failed: %s", strerror(errno));
+ snprintf(cmd, sizeof(cmd),
+ "zfs create -o volblocksize=8192 -o volmode=dev -V %s %s",
+ ZVOL_SIZE, zvol_name);
+ ATF_REQUIRE_EQ_MSG(0, system(cmd),
+ "zfs create failed: %s", strerror(errno));
+
+ snprintf(devname, sizeof(devname), "/dev/zvol/%s", zvol_name);
+ do {
+ fd = open(devname, O_RDWR);
+ } while (fd == -1 && errno == EINTR);
+ ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
+ return (fd);
+}
+
+static void
+aio_zvol_cleanup(const char *unique)
+{
+ FILE *pidfile;
+ pid_t testpid;
+ char cmd[160];
+
+ pidfile = fopen("pidfile", "r");
+ if (pidfile == NULL && errno == ENOENT) {
+ /* Setup probably failed */
+ return;
+ }
+ ATF_REQUIRE_MSG(NULL != pidfile, "fopen: %s", strerror(errno));
+ ATF_REQUIRE_EQ(1, fscanf(pidfile, "%d", &testpid));
+ fclose(pidfile);
+
+ snprintf(cmd, sizeof(cmd), "zpool destroy %s_%s.%d", POOL_NAME, unique,
+ testpid);
+ system(cmd);
+}
+
+
+ATF_TC_WITHOUT_HEAD(aio_large_read_test);
+ATF_TC_BODY(aio_large_read_test, tc)
+{
+ struct aiocb cb, *cbp;
+ ssize_t nread;
+ size_t len;
+ int fd;
+#ifdef __LP64__
+ int clamped;
+#endif
+
+ ATF_REQUIRE_KERNEL_MODULE("aio");
+ ATF_REQUIRE_UNSAFE_AIO();
+
+#ifdef __LP64__
+ len = sizeof(clamped);
+ if (sysctlbyname("debug.iosize_max_clamp", &clamped, &len, NULL, 0) ==
+ -1)
+ atf_libc_error(errno, "Failed to read debug.iosize_max_clamp");
+#endif
+
+ /* Determine the maximum supported read(2) size. */
+ len = SSIZE_MAX;
+#ifdef __LP64__
+ if (clamped)
+ len = INT_MAX;
+#endif
+
+ fd = open(FILE_PATHNAME, O_RDWR | O_CREAT, 0600);
+ ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
+
+ unlink(FILE_PATHNAME);
+
+ memset(&cb, 0, sizeof(cb));
+ cb.aio_nbytes = len;
+ cb.aio_fildes = fd;
+ cb.aio_buf = NULL;
+ if (aio_read(&cb) == -1)
+ atf_tc_fail("aio_read() of maximum read size failed: %s",
+ strerror(errno));
+
+ nread = aio_waitcomplete(&cbp, NULL);
+ if (nread == -1)
+ atf_tc_fail("aio_waitcomplete() failed: %s", strerror(errno));
+ if (nread != 0)
+ atf_tc_fail("aio_read() from empty file returned data: %zd",
+ nread);
+
+ memset(&cb, 0, sizeof(cb));
+ cb.aio_nbytes = len + 1;
+ cb.aio_fildes = fd;
+ cb.aio_buf = NULL;
+ if (aio_read(&cb) == -1) {
+ if (errno == EINVAL)
+ goto finished;
+ atf_tc_fail("aio_read() of too large read size failed: %s",
+ strerror(errno));
+ }
+
+ nread = aio_waitcomplete(&cbp, NULL);
+ if (nread == -1) {
+ if (errno == EINVAL)
+ goto finished;
+ atf_tc_fail("aio_waitcomplete() failed: %s", strerror(errno));
+ }
+ atf_tc_fail("aio_read() of too large read size returned: %zd", nread);
+
+finished:
+ close(fd);
+}
+
+/*
+ * This tests for a bug where arriving socket data can wakeup multiple
+ * AIO read requests resulting in an uncancellable request.
+ */
+ATF_TC_WITHOUT_HEAD(aio_socket_two_reads);
+ATF_TC_BODY(aio_socket_two_reads, tc)
+{
+ struct ioreq {
+ struct aiocb iocb;
+ char buffer[1024];
+ } ioreq[2];
+ struct aiocb *iocb;
+ unsigned i;
+ int s[2];
+ char c;
+
+ ATF_REQUIRE_KERNEL_MODULE("aio");
+#if __FreeBSD_version < 1100101
+ aft_tc_skip("kernel version %d is too old (%d required)",
+ __FreeBSD_version, 1100101);
+#endif
+
+ ATF_REQUIRE(socketpair(PF_UNIX, SOCK_STREAM, 0, s) != -1);
+
+ /* Queue two read requests. */
+ memset(&ioreq, 0, sizeof(ioreq));
+ for (i = 0; i < nitems(ioreq); i++) {
+ ioreq[i].iocb.aio_nbytes = sizeof(ioreq[i].buffer);
+ ioreq[i].iocb.aio_fildes = s[0];
+ ioreq[i].iocb.aio_buf = ioreq[i].buffer;
+ ATF_REQUIRE(aio_read(&ioreq[i].iocb) == 0);
+ }
+
+ /* Send a single byte. This should complete one request. */
+ c = 0xc3;
+ ATF_REQUIRE(write(s[1], &c, sizeof(c)) == 1);
+
+ ATF_REQUIRE(aio_waitcomplete(&iocb, NULL) == 1);
+
+ /* Determine which request completed and verify the data was read. */
+ if (iocb == &ioreq[0].iocb)
+ i = 0;
+ else
+ i = 1;
+ ATF_REQUIRE(ioreq[i].buffer[0] == c);
+
+ i ^= 1;
+
+ /*
+ * Try to cancel the other request. On broken systems this
+ * will fail and the process will hang on exit.
+ */
+ ATF_REQUIRE(aio_error(&ioreq[i].iocb) == EINPROGRESS);
+ ATF_REQUIRE(aio_cancel(s[0], &ioreq[i].iocb) == AIO_CANCELED);
+
+ close(s[1]);
+ close(s[0]);
+}
+
+static void
+aio_socket_blocking_short_write_test(bool vectored)
+{
+ struct aiocb iocb, *iocbp;
+ struct iovec iov[2];
+ char *buffer[2];
+ ssize_t done, r;
+ int buffer_size, sb_size;
+ socklen_t len;
+ int s[2];
+
+ ATF_REQUIRE_KERNEL_MODULE("aio");
+
+ ATF_REQUIRE(socketpair(PF_UNIX, SOCK_STREAM, 0, s) != -1);
+
+ len = sizeof(sb_size);
+ ATF_REQUIRE(getsockopt(s[0], SOL_SOCKET, SO_RCVBUF, &sb_size, &len) !=
+ -1);
+ ATF_REQUIRE(len == sizeof(sb_size));
+ buffer_size = sb_size;
+
+ ATF_REQUIRE(getsockopt(s[1], SOL_SOCKET, SO_SNDBUF, &sb_size, &len) !=
+ -1);
+ ATF_REQUIRE(len == sizeof(sb_size));
+ if (sb_size > buffer_size)
+ buffer_size = sb_size;
+
+ /*
+ * Use twice the size of the MAX(receive buffer, send buffer)
+ * to ensure that the write is split up into multiple writes
+ * internally.
+ */
+ buffer_size *= 2;
+
+ buffer[0] = malloc(buffer_size);
+ ATF_REQUIRE(buffer[0] != NULL);
+ buffer[1] = malloc(buffer_size);
+ ATF_REQUIRE(buffer[1] != NULL);
+
+ srandomdev();
+ aio_fill_buffer(buffer[1], buffer_size, random());
+
+ memset(&iocb, 0, sizeof(iocb));
+ iocb.aio_fildes = s[1];
+ if (vectored) {
+ iov[0].iov_base = buffer[1];
+ iov[0].iov_len = buffer_size / 2 + 1;
+ iov[1].iov_base = buffer[1] + buffer_size / 2 + 1;
+ iov[1].iov_len = buffer_size / 2 - 1;
+ iocb.aio_iov = iov;
+ iocb.aio_iovcnt = 2;
+ r = aio_writev(&iocb);
+ ATF_CHECK_EQ_MSG(0, r, "aio_writev returned %zd", r);
+ } else {
+ iocb.aio_buf = buffer[1];
+ iocb.aio_nbytes = buffer_size;
+ r = aio_write(&iocb);
+ ATF_CHECK_EQ_MSG(0, r, "aio_writev returned %zd", r);
+ }
+
+ done = recv(s[0], buffer[0], buffer_size, MSG_WAITALL);
+ ATF_REQUIRE(done == buffer_size);
+
+ done = aio_waitcomplete(&iocbp, NULL);
+ ATF_REQUIRE(iocbp == &iocb);
+ ATF_REQUIRE(done == buffer_size);
+
+ ATF_REQUIRE(memcmp(buffer[0], buffer[1], buffer_size) == 0);
+
+ close(s[1]);
+ close(s[0]);
+}
+
+/*
+ * This test ensures that aio_write() on a blocking socket of a "large"
+ * buffer does not return a short completion.
+ */
+ATF_TC_WITHOUT_HEAD(aio_socket_blocking_short_write);
+ATF_TC_BODY(aio_socket_blocking_short_write, tc)
+{
+ aio_socket_blocking_short_write_test(false);
+}
+
+/*
+ * Like aio_socket_blocking_short_write, but also tests that partially
+ * completed vectored sends can be retried correctly.
+ */
+ATF_TC_WITHOUT_HEAD(aio_socket_blocking_short_write_vectored);
+ATF_TC_BODY(aio_socket_blocking_short_write_vectored, tc)
+{
+ aio_socket_blocking_short_write_test(true);
+}
+
+/*
+ * Verify that AIO requests fail when applied to a listening socket.
+ */
+ATF_TC_WITHOUT_HEAD(aio_socket_listen_fail);
+ATF_TC_BODY(aio_socket_listen_fail, tc)
+{
+ struct aiocb iocb;
+ struct sockaddr_un sun;
+ char buf[16];
+ int s;
+
+ s = socket(AF_LOCAL, SOCK_STREAM, 0);
+ ATF_REQUIRE(s != -1);
+
+ memset(&sun, 0, sizeof(sun));
+ snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", "listen.XXXXXX");
+ mktemp(sun.sun_path);
+ sun.sun_family = AF_LOCAL;
+ sun.sun_len = SUN_LEN(&sun);
+
+ ATF_REQUIRE(bind(s, (struct sockaddr *)&sun, SUN_LEN(&sun)) == 0);
+ ATF_REQUIRE(listen(s, 5) == 0);
+
+ memset(buf, 0, sizeof(buf));
+ memset(&iocb, 0, sizeof(iocb));
+ iocb.aio_fildes = s;
+ iocb.aio_buf = buf;
+ iocb.aio_nbytes = sizeof(buf);
+
+ ATF_REQUIRE_ERRNO(EINVAL, aio_read(&iocb) == -1);
+ ATF_REQUIRE_ERRNO(EINVAL, aio_write(&iocb) == -1);
+
+ ATF_REQUIRE(unlink(sun.sun_path) == 0);
+ close(s);
+}
+
+/*
+ * Verify that listen(2) fails if a socket has pending AIO requests.
+ */
+ATF_TC_WITHOUT_HEAD(aio_socket_listen_pending);
+ATF_TC_BODY(aio_socket_listen_pending, tc)
+{
+ struct aiocb iocb;
+ struct sockaddr_un sun;
+ char buf[16];
+ int s;
+
+ s = socket(AF_LOCAL, SOCK_STREAM, 0);
+ ATF_REQUIRE(s != -1);
+
+ memset(&sun, 0, sizeof(sun));
+ snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", "listen.XXXXXX");
+ mktemp(sun.sun_path);
+ sun.sun_family = AF_LOCAL;
+ sun.sun_len = SUN_LEN(&sun);
+
+ ATF_REQUIRE(bind(s, (struct sockaddr *)&sun, SUN_LEN(&sun)) == 0);
+
+ memset(buf, 0, sizeof(buf));
+ memset(&iocb, 0, sizeof(iocb));
+ iocb.aio_fildes = s;
+ iocb.aio_buf = buf;
+ iocb.aio_nbytes = sizeof(buf);
+ ATF_REQUIRE(aio_read(&iocb) == 0);
+
+ ATF_REQUIRE_ERRNO(EINVAL, listen(s, 5) == -1);
+
+ ATF_REQUIRE(aio_cancel(s, &iocb) != -1);
+
+ ATF_REQUIRE(unlink(sun.sun_path) == 0);
+ close(s);
+}
+
+/*
+ * This test verifies that cancelling a partially completed socket write
+ * returns a short write rather than ECANCELED.
+ */
+ATF_TC_WITHOUT_HEAD(aio_socket_short_write_cancel);
+ATF_TC_BODY(aio_socket_short_write_cancel, tc)
+{
+ struct aiocb iocb, *iocbp;
+ char *buffer[2];
+ ssize_t done;
+ int buffer_size, sb_size;
+ socklen_t len;
+ int s[2];
+
+ ATF_REQUIRE_KERNEL_MODULE("aio");
+
+ ATF_REQUIRE(socketpair(PF_UNIX, SOCK_STREAM, 0, s) != -1);
+
+ len = sizeof(sb_size);
+ ATF_REQUIRE(getsockopt(s[0], SOL_SOCKET, SO_RCVBUF, &sb_size, &len) !=
+ -1);
+ ATF_REQUIRE(len == sizeof(sb_size));
+ buffer_size = sb_size;
+
+ ATF_REQUIRE(getsockopt(s[1], SOL_SOCKET, SO_SNDBUF, &sb_size, &len) !=
+ -1);
+ ATF_REQUIRE(len == sizeof(sb_size));
+ if (sb_size > buffer_size)
+ buffer_size = sb_size;
+
+ /*
+ * Use three times the size of the MAX(receive buffer, send
+ * buffer) for the write to ensure that the write is split up
+ * into multiple writes internally. The recv() ensures that
+ * the write has partially completed, but a remaining size of
+ * two buffers should ensure that the write has not completed
+ * fully when it is cancelled.
+ */
+ buffer[0] = malloc(buffer_size);
+ ATF_REQUIRE(buffer[0] != NULL);
+ buffer[1] = malloc(buffer_size * 3);
+ ATF_REQUIRE(buffer[1] != NULL);
+
+ srandomdev();
+ aio_fill_buffer(buffer[1], buffer_size * 3, random());
+
+ memset(&iocb, 0, sizeof(iocb));
+ iocb.aio_fildes = s[1];
+ iocb.aio_buf = buffer[1];
+ iocb.aio_nbytes = buffer_size * 3;
+ ATF_REQUIRE(aio_write(&iocb) == 0);
+
+ done = recv(s[0], buffer[0], buffer_size, MSG_WAITALL);
+ ATF_REQUIRE(done == buffer_size);
+
+ ATF_REQUIRE(aio_error(&iocb) == EINPROGRESS);
+ ATF_REQUIRE(aio_cancel(s[1], &iocb) == AIO_NOTCANCELED);
+
+ done = aio_waitcomplete(&iocbp, NULL);
+ ATF_REQUIRE(iocbp == &iocb);
+ ATF_REQUIRE(done >= buffer_size && done <= buffer_size * 2);
+
+ ATF_REQUIRE(memcmp(buffer[0], buffer[1], buffer_size) == 0);
+
+ close(s[1]);
+ close(s[0]);
+}
+
+/*
+ * Test handling of aio_read() and aio_write() on shut-down sockets.
+ */
+ATF_TC_WITHOUT_HEAD(aio_socket_shutdown);
+ATF_TC_BODY(aio_socket_shutdown, tc)
+{
+ struct aiocb iocb;
+ sigset_t set;
+ char *buffer;
+ ssize_t len;
+ size_t bsz;
+ int error, s[2];
+
+ ATF_REQUIRE_KERNEL_MODULE("aio");
+
+ ATF_REQUIRE(socketpair(PF_UNIX, SOCK_STREAM, 0, s) != -1);
+
+ bsz = 1024;
+ buffer = malloc(bsz);
+ memset(buffer, 0, bsz);
+
+ /* Put some data in s[0]'s recv buffer. */
+ ATF_REQUIRE(send(s[1], buffer, bsz, 0) == (ssize_t)bsz);
+
+ /* No more reading from s[0]. */
+ ATF_REQUIRE(shutdown(s[0], SHUT_RD) != -1);
+
+ ATF_REQUIRE(buffer != NULL);
+
+ memset(&iocb, 0, sizeof(iocb));
+ iocb.aio_fildes = s[0];
+ iocb.aio_buf = buffer;
+ iocb.aio_nbytes = bsz;
+ ATF_REQUIRE(aio_read(&iocb) == 0);
+
+ /* Expect to see zero bytes, analogous to recv(2). */
+ while ((error = aio_error(&iocb)) == EINPROGRESS)
+ usleep(25000);
+ ATF_REQUIRE_MSG(error == 0, "aio_error() returned %d", error);
+ len = aio_return(&iocb);
+ ATF_REQUIRE_MSG(len == 0, "read job returned %zd bytes", len);
+
+ /* No more writing to s[1]. */
+ ATF_REQUIRE(shutdown(s[1], SHUT_WR) != -1);
+
+ /* Block SIGPIPE so that we can detect the error in-band. */
+ sigemptyset(&set);
+ sigaddset(&set, SIGPIPE);
+ ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) == 0);
+
+ memset(&iocb, 0, sizeof(iocb));
+ iocb.aio_fildes = s[1];
+ iocb.aio_buf = buffer;
+ iocb.aio_nbytes = bsz;
+ ATF_REQUIRE(aio_write(&iocb) == 0);
+
+ /* Expect an error, analogous to send(2). */
+ while ((error = aio_error(&iocb)) == EINPROGRESS)
+ usleep(25000);
+ ATF_REQUIRE_MSG(error == EPIPE, "aio_error() returned %d", error);
+
+ ATF_REQUIRE(close(s[0]) != -1);
+ ATF_REQUIRE(close(s[1]) != -1);
+ free(buffer);
+}
+
+/*
+ * test aio_fsync's behavior with bad inputs
+ */
+ATF_TC_WITHOUT_HEAD(aio_fsync_errors);
+ATF_TC_BODY(aio_fsync_errors, tc)
+{
+ int fd;
+ struct aiocb iocb;
+
+ ATF_REQUIRE_KERNEL_MODULE("aio");
+ ATF_REQUIRE_UNSAFE_AIO();
+
+ fd = open(FILE_PATHNAME, O_RDWR | O_CREAT, 0600);
+ ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
+ unlink(FILE_PATHNAME);
+
+ /* aio_fsync should return EINVAL unless op is O_SYNC or O_DSYNC */
+ memset(&iocb, 0, sizeof(iocb));
+ iocb.aio_fildes = fd;
+ ATF_CHECK_EQ(-1, aio_fsync(666, &iocb));
+ ATF_CHECK_EQ(EINVAL, errno);
+
+ /* aio_fsync should return EBADF if fd is not a valid descriptor */
+ memset(&iocb, 0, sizeof(iocb));
+ iocb.aio_fildes = 666;
+ ATF_CHECK_EQ(-1, aio_fsync(O_SYNC, &iocb));
+ ATF_CHECK_EQ(EBADF, errno);
+
+ /* aio_fsync should return EINVAL if sigev_notify is invalid */
+ memset(&iocb, 0, sizeof(iocb));
+ iocb.aio_fildes = fd;
+ iocb.aio_sigevent.sigev_notify = 666;
+ ATF_CHECK_EQ(-1, aio_fsync(666, &iocb));
+ ATF_CHECK_EQ(EINVAL, errno);
+}
+
+/*
+ * This test just performs a basic test of aio_fsync().
+ */
+static void
+aio_fsync_test(int op)
+{
+ struct aiocb synccb, *iocbp;
+ struct {
+ struct aiocb iocb;
+ bool done;
+ char *buffer;
+ } buffers[16];
+ struct stat sb;
+ ssize_t rval;
+ unsigned i;
+ int fd;
+
+ ATF_REQUIRE_KERNEL_MODULE("aio");
+ ATF_REQUIRE_UNSAFE_AIO();
+
+ fd = open(FILE_PATHNAME, O_RDWR | O_CREAT, 0600);
+ ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
+ unlink(FILE_PATHNAME);
+
+ ATF_REQUIRE(fstat(fd, &sb) == 0);
+ ATF_REQUIRE(sb.st_blksize != 0);
+ ATF_REQUIRE(ftruncate(fd, sb.st_blksize * nitems(buffers)) == 0);
+
+ /*
+ * Queue several asynchronous write requests. Hopefully this
+ * forces the aio_fsync() request to be deferred. There is no
+ * reliable way to guarantee that however.
+ */
+ srandomdev();
+ for (i = 0; i < nitems(buffers); i++) {
+ buffers[i].done = false;
+ memset(&buffers[i].iocb, 0, sizeof(buffers[i].iocb));
+ buffers[i].buffer = malloc(sb.st_blksize);
+ aio_fill_buffer(buffers[i].buffer, sb.st_blksize, random());
+ buffers[i].iocb.aio_fildes = fd;
+ buffers[i].iocb.aio_buf = buffers[i].buffer;
+ buffers[i].iocb.aio_nbytes = sb.st_blksize;
+ buffers[i].iocb.aio_offset = sb.st_blksize * i;
+ ATF_REQUIRE(aio_write(&buffers[i].iocb) == 0);
+ }
+
+ /* Queue the aio_fsync request. */
+ memset(&synccb, 0, sizeof(synccb));
+ synccb.aio_fildes = fd;
+ ATF_REQUIRE(aio_fsync(op, &synccb) == 0);
+
+ /* Wait for requests to complete. */
+ for (;;) {
+ next:
+ rval = aio_waitcomplete(&iocbp, NULL);
+ ATF_REQUIRE(iocbp != NULL);
+ if (iocbp == &synccb) {
+ ATF_REQUIRE(rval == 0);
+ break;
+ }
+
+ for (i = 0; i < nitems(buffers); i++) {
+ if (iocbp == &buffers[i].iocb) {
+ ATF_REQUIRE(buffers[i].done == false);
+ ATF_REQUIRE(rval == sb.st_blksize);
+ buffers[i].done = true;
+ goto next;
+ }
+ }
+
+ ATF_REQUIRE_MSG(false, "unmatched AIO request");
+ }
+
+ for (i = 0; i < nitems(buffers); i++)
+ ATF_REQUIRE_MSG(buffers[i].done,
+ "AIO request %u did not complete", i);
+
+ close(fd);
+}
+
+ATF_TC_WITHOUT_HEAD(aio_fsync_sync_test);
+ATF_TC_BODY(aio_fsync_sync_test, tc)
+{
+ aio_fsync_test(O_SYNC);
+}
+
+ATF_TC_WITHOUT_HEAD(aio_fsync_dsync_test);
+ATF_TC_BODY(aio_fsync_dsync_test, tc)
+{
+ aio_fsync_test(O_DSYNC);
+}
+
+/*
+ * We shouldn't be able to DoS the system by setting iov_len to an insane
+ * value
+ */
+ATF_TC_WITHOUT_HEAD(aio_writev_dos_iov_len);
+ATF_TC_BODY(aio_writev_dos_iov_len, tc)
+{
+ struct aiocb aio;
+ const struct aiocb *const iocbs[] = {&aio};
+ const char *wbuf = "Hello, world!";
+ struct iovec iov[1];
+ ssize_t r;
+ int fd;
+
+ ATF_REQUIRE_KERNEL_MODULE("aio");
+ ATF_REQUIRE_UNSAFE_AIO();
+
+ fd = open("testfile", O_RDWR | O_CREAT, 0600);
+ ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
+
+ iov[0].iov_base = __DECONST(void*, wbuf);
+ iov[0].iov_len = 1 << 30;
+ bzero(&aio, sizeof(aio));
+ aio.aio_fildes = fd;
+ aio.aio_offset = 0;
+ aio.aio_iov = iov;
+ aio.aio_iovcnt = 1;
+
+ r = aio_writev(&aio);
+ ATF_CHECK_EQ_MSG(0, r, "aio_writev returned %zd", r);
+ ATF_REQUIRE_EQ(0, aio_suspend(iocbs, 1, NULL));
+ r = aio_return(&aio);
+ ATF_CHECK_EQ_MSG(-1, r, "aio_return returned %zd", r);
+ ATF_CHECK_MSG(errno == EFAULT || errno == EINVAL,
+ "aio_writev: %s", strerror(errno));
+
+ close(fd);
+}
+
+/*
+ * We shouldn't be able to DoS the system by setting aio_iovcnt to an insane
+ * value
+ */
+ATF_TC_WITHOUT_HEAD(aio_writev_dos_iovcnt);
+ATF_TC_BODY(aio_writev_dos_iovcnt, tc)
+{
+ struct aiocb aio;
+ const char *wbuf = "Hello, world!";
+ struct iovec iov[1];
+ ssize_t len;
+ int fd;
+
+ ATF_REQUIRE_KERNEL_MODULE("aio");
+ ATF_REQUIRE_UNSAFE_AIO();
+
+ fd = open("testfile", O_RDWR | O_CREAT, 0600);
+ ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
+
+ len = strlen(wbuf);
+ iov[0].iov_base = __DECONST(void*, wbuf);
+ iov[0].iov_len = len;
+ bzero(&aio, sizeof(aio));
+ aio.aio_fildes = fd;
+ aio.aio_offset = 0;
+ aio.aio_iov = iov;
+ aio.aio_iovcnt = 1 << 30;
+
+ ATF_REQUIRE_EQ(-1, aio_writev(&aio));
+ ATF_CHECK_EQ(EINVAL, errno);
+
+ close(fd);
+}
+
+ATF_TC_WITH_CLEANUP(aio_writev_efault);
+ATF_TC_HEAD(aio_writev_efault, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Vectored AIO should gracefully handle invalid addresses");
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(aio_writev_efault, tc)
+{
+ struct aiocb aio;
+ ssize_t buflen;
+ char *buffer;
+ struct iovec iov[2];
+ long seed;
+ int fd;
+
+ ATF_REQUIRE_KERNEL_MODULE("aio");
+ ATF_REQUIRE_UNSAFE_AIO();
+
+ fd = aio_md_setup();
+
+ seed = random();
+ buflen = 4096;
+ buffer = malloc(buflen);
+ aio_fill_buffer(buffer, buflen, seed);
+ iov[0].iov_base = buffer;
+ iov[0].iov_len = buflen;
+ iov[1].iov_base = (void*)-1; /* Invalid! */
+ iov[1].iov_len = buflen;
+ bzero(&aio, sizeof(aio));
+ aio.aio_fildes = fd;
+ aio.aio_offset = 0;
+ aio.aio_iov = iov;
+ aio.aio_iovcnt = nitems(iov);
+
+ ATF_REQUIRE_EQ(-1, aio_writev(&aio));
+ ATF_CHECK_EQ(EFAULT, errno);
+
+ close(fd);
+}
+ATF_TC_CLEANUP(aio_writev_efault, tc)
+{
+ aio_md_cleanup();
+}
+
+ATF_TC_WITHOUT_HEAD(aio_writev_empty_file_poll);
+ATF_TC_BODY(aio_writev_empty_file_poll, tc)
+{
+ struct aiocb aio;
+ int fd;
+
+ ATF_REQUIRE_KERNEL_MODULE("aio");
+ ATF_REQUIRE_UNSAFE_AIO();
+
+ fd = open("testfile", O_RDWR | O_CREAT, 0600);
+ ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
+
+ bzero(&aio, sizeof(aio));
+ aio.aio_fildes = fd;
+ aio.aio_offset = 0;
+ aio.aio_iovcnt = 0;
+
+ ATF_REQUIRE_EQ(0, aio_writev(&aio));
+ ATF_REQUIRE_EQ(0, suspend(&aio));
+
+ close(fd);
+}
+
+ATF_TC_WITHOUT_HEAD(aio_writev_empty_file_signal);
+ATF_TC_BODY(aio_writev_empty_file_signal, tc)
+{
+ struct aiocb aio;
+ int fd;
+
+ ATF_REQUIRE_KERNEL_MODULE("aio");
+ ATF_REQUIRE_UNSAFE_AIO();
+
+ fd = open("testfile", O_RDWR | O_CREAT, 0600);
+ ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
+
+ bzero(&aio, sizeof(aio));
+ aio.aio_fildes = fd;
+ aio.aio_offset = 0;
+ aio.aio_iovcnt = 0;
+ aio.aio_sigevent = *setup_signal();
+
+ ATF_REQUIRE_EQ(0, aio_writev(&aio));
+ ATF_REQUIRE_EQ(0, poll_signaled(&aio));
+
+ close(fd);
+}
+
+/*
+ * Use an aiocb with kqueue and EV_ONESHOT. kqueue should deliver the event
+ * only once, even if the user doesn't promptly call aio_return.
+ */
+ATF_TC_WITHOUT_HEAD(ev_oneshot);
+ATF_TC_BODY(ev_oneshot, tc)
+{
+ int fd, kq, nevents;
+ struct aiocb iocb;
+ struct kevent events[1];
+ struct timespec timeout;
+
+ ATF_REQUIRE_KERNEL_MODULE("aio");
+
+ kq = kqueue();
+ ATF_REQUIRE(kq >= 0);
+
+ fd = open(FILE_PATHNAME, O_RDWR | O_CREAT, 0600);
+ ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
+
+ memset(&iocb, 0, sizeof(iocb));
+ iocb.aio_fildes = fd;
+ iocb.aio_sigevent.sigev_notify_kqueue = kq;
+ iocb.aio_sigevent.sigev_value.sival_ptr = (void*)0xdeadbeef;
+ iocb.aio_sigevent.sigev_notify_kevent_flags = EV_ONESHOT;
+ iocb.aio_sigevent.sigev_notify = SIGEV_KEVENT;
+
+ ATF_CHECK_EQ(0, aio_fsync(O_SYNC, &iocb));
+
+ nevents = kevent(kq, NULL, 0, events, 1, NULL);
+ ATF_CHECK_EQ(1, nevents);
+ ATF_CHECK_EQ(events[0].ident, (uintptr_t) &iocb);
+ ATF_CHECK_EQ(events[0].filter, EVFILT_AIO);
+ ATF_CHECK_EQ(events[0].flags, EV_EOF | EV_ONESHOT);
+ ATF_CHECK_EQ(events[0].fflags, 0);
+ ATF_CHECK_EQ(events[0].data, 0);
+ ATF_CHECK_EQ((uintptr_t)events[0].udata, 0xdeadbeef);
+
+ /*
+ * Even though we haven't called aio_return, kevent will not return the
+ * event again due to EV_ONESHOT.
+ */
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 100000000;
+ nevents = kevent(kq, NULL, 0, events, 1, &timeout);
+ ATF_CHECK_EQ(0, nevents);
+
+ ATF_CHECK_EQ(0, aio_return(&iocb));
+ close(fd);
+ close(kq);
+}
+
+
+// aio_writev and aio_readv should still work even if the iovcnt is greater
+// than the number of buffered AIO operations permitted per process.
+ATF_TC_WITH_CLEANUP(vectored_big_iovcnt);
+ATF_TC_HEAD(vectored_big_iovcnt, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Vectored AIO should still work even if the iovcnt is greater than "
+ "the number of buffered AIO operations permitted by the process");
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(vectored_big_iovcnt, tc)
+{
+ struct aiocb aio;
+ struct iovec *iov;
+ ssize_t len, buflen;
+ char *buffer;
+ const char *oid = "vfs.aio.max_buf_aio";
+ long seed;
+ int max_buf_aio;
+ int fd, i;
+ ssize_t sysctl_len = sizeof(max_buf_aio);
+
+ ATF_REQUIRE_KERNEL_MODULE("aio");
+ ATF_REQUIRE_UNSAFE_AIO();
+
+ if (sysctlbyname(oid, &max_buf_aio, &sysctl_len, NULL, 0) == -1)
+ atf_libc_error(errno, "Failed to read %s", oid);
+
+ seed = random();
+ buflen = 512 * (max_buf_aio + 1);
+ buffer = malloc(buflen);
+ aio_fill_buffer(buffer, buflen, seed);
+ iov = calloc(max_buf_aio + 1, sizeof(struct iovec));
+
+ fd = aio_md_setup();
+
+ bzero(&aio, sizeof(aio));
+ aio.aio_fildes = fd;
+ aio.aio_offset = 0;
+ for (i = 0; i < max_buf_aio + 1; i++) {
+ iov[i].iov_base = &buffer[i * 512];
+ iov[i].iov_len = 512;
+ }
+ aio.aio_iov = iov;
+ aio.aio_iovcnt = max_buf_aio + 1;
+
+ if (aio_writev(&aio) < 0)
+ atf_tc_fail("aio_writev failed: %s", strerror(errno));
+
+ len = poll(&aio);
+ if (len < 0)
+ atf_tc_fail("aio failed: %s", strerror(errno));
+
+ if (len != buflen)
+ atf_tc_fail("aio short write (%jd)", (intmax_t)len);
+
+ bzero(&aio, sizeof(aio));
+ aio.aio_fildes = fd;
+ aio.aio_offset = 0;
+ aio.aio_iov = iov;
+ aio.aio_iovcnt = max_buf_aio + 1;
+
+ if (aio_readv(&aio) < 0)
+ atf_tc_fail("aio_readv failed: %s", strerror(errno));
+
+ len = poll(&aio);
+ if (len < 0)
+ atf_tc_fail("aio failed: %s", strerror(errno));
+
+ if (len != buflen)
+ atf_tc_fail("aio short read (%jd)", (intmax_t)len);
+
+ if (aio_test_buffer(buffer, buflen, seed) == 0)
+ atf_tc_fail("buffer mismatched");
+
+ close(fd);
+}
+ATF_TC_CLEANUP(vectored_big_iovcnt, tc)
+{
+ aio_md_cleanup();
+}
+
+ATF_TC_WITHOUT_HEAD(vectored_file_poll);
+ATF_TC_BODY(vectored_file_poll, tc)
+{
+ aio_file_test(poll, NULL, true);
+}
+
+ATF_TC_WITHOUT_HEAD(vectored_thread);
+ATF_TC_BODY(vectored_thread, tc)
+{
+ aio_file_test(poll_signaled, setup_thread(), true);
+}
+
+ATF_TC_WITH_CLEANUP(vectored_md_poll);
+ATF_TC_HEAD(vectored_md_poll, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(vectored_md_poll, tc)
+{
+ aio_md_test(poll, NULL, true);
+}
+ATF_TC_CLEANUP(vectored_md_poll, tc)
+{
+ aio_md_cleanup();
+}
+
+ATF_TC_WITHOUT_HEAD(vectored_socket_poll);
+ATF_TC_BODY(vectored_socket_poll, tc)
+{
+ aio_unix_socketpair_test(poll, NULL, true);
+}
+
+// aio_writev and aio_readv should still work even if the iov contains elements
+// that aren't a multiple of the device's sector size, and even if the total
+// amount if I/O _is_ a multiple of the device's sector size.
+ATF_TC_WITH_CLEANUP(vectored_unaligned);
+ATF_TC_HEAD(vectored_unaligned, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Vectored AIO should still work even if the iov contains elements "
+ "that aren't a multiple of the sector size.");
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(vectored_unaligned, tc)
+{
+ struct aio_context ac;
+ struct aiocb aio;
+ struct iovec iov[3];
+ ssize_t len, total_len;
+ int fd;
+
+ if (atf_tc_get_config_var_as_bool_wd(tc, "ci", false))
+ atf_tc_skip("https://bugs.freebsd.org/258766");
+
+ ATF_REQUIRE_KERNEL_MODULE("aio");
+ ATF_REQUIRE_UNSAFE_AIO();
+
+ /*
+ * Use a zvol with volmode=dev, so it will allow .d_write with
+ * unaligned uio. geom devices use physio, which doesn't allow that.
+ */
+ fd = aio_zvol_setup(atf_tc_get_ident(tc));
+ aio_context_init(&ac, fd, fd, FILE_LEN);
+
+ /* Break the buffer into 3 parts:
+ * * A 4kB part, aligned to 4kB
+ * * Two other parts that add up to 4kB:
+ * - 256B
+ * - 4kB - 256B
+ */
+ iov[0].iov_base = ac.ac_buffer;
+ iov[0].iov_len = 4096;
+ iov[1].iov_base = (void*)((uintptr_t)iov[0].iov_base + iov[0].iov_len);
+ iov[1].iov_len = 256;
+ iov[2].iov_base = (void*)((uintptr_t)iov[1].iov_base + iov[1].iov_len);
+ iov[2].iov_len = 4096 - iov[1].iov_len;
+ total_len = iov[0].iov_len + iov[1].iov_len + iov[2].iov_len;
+ bzero(&aio, sizeof(aio));
+ aio.aio_fildes = ac.ac_write_fd;
+ aio.aio_offset = 0;
+ aio.aio_iov = iov;
+ aio.aio_iovcnt = 3;
+
+ if (aio_writev(&aio) < 0)
+ atf_tc_fail("aio_writev failed: %s", strerror(errno));
+
+ len = poll(&aio);
+ if (len < 0)
+ atf_tc_fail("aio failed: %s", strerror(errno));
+
+ if (len != total_len)
+ atf_tc_fail("aio short write (%jd)", (intmax_t)len);
+
+ bzero(&aio, sizeof(aio));
+ aio.aio_fildes = ac.ac_read_fd;
+ aio.aio_offset = 0;
+ aio.aio_iov = iov;
+ aio.aio_iovcnt = 3;
+
+ if (aio_readv(&aio) < 0)
+ atf_tc_fail("aio_readv failed: %s", strerror(errno));
+ len = poll(&aio);
+
+ ATF_REQUIRE_MSG(aio_test_buffer(ac.ac_buffer, total_len,
+ ac.ac_seed) != 0, "aio_test_buffer: internal error");
+
+ close(fd);
+}
+ATF_TC_CLEANUP(vectored_unaligned, tc)
+{
+ aio_zvol_cleanup(atf_tc_get_ident(tc));
+}
+
+static void
+aio_zvol_test(completion comp, struct sigevent *sev, bool vectored,
+ const char *unique)
+{
+ struct aio_context ac;
+ int fd;
+
+ fd = aio_zvol_setup(unique);
+ aio_context_init(&ac, fd, fd, MD_LEN);
+ if (vectored) {
+ aio_writev_test(&ac, comp, sev);
+ aio_readv_test(&ac, comp, sev);
+ } else {
+ aio_write_test(&ac, comp, sev);
+ aio_read_test(&ac, comp, sev);
+ }
+
+ close(fd);
+}
+
+/*
+ * Note that unlike md, the zvol is not a geom device, does not allow unmapped
+ * buffers, and does not use physio.
+ */
+ATF_TC_WITH_CLEANUP(vectored_zvol_poll);
+ATF_TC_HEAD(vectored_zvol_poll, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(vectored_zvol_poll, tc)
+{
+ if (atf_tc_get_config_var_as_bool_wd(tc, "ci", false))
+ atf_tc_skip("https://bugs.freebsd.org/258766");
+ aio_zvol_test(poll, NULL, true, atf_tc_get_ident(tc));
+}
+ATF_TC_CLEANUP(vectored_zvol_poll, tc)
+{
+ aio_zvol_cleanup(atf_tc_get_ident(tc));
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ /* Test every file type with every completion method */
+ ATF_TP_ADD_TC(tp, file_kq);
+ ATF_TP_ADD_TC(tp, file_poll);
+ ATF_TP_ADD_TC(tp, file_signal);
+ ATF_TP_ADD_TC(tp, file_suspend);
+ ATF_TP_ADD_TC(tp, file_thread);
+ ATF_TP_ADD_TC(tp, file_waitcomplete);
+ ATF_TP_ADD_TC(tp, fifo_kq);
+ ATF_TP_ADD_TC(tp, fifo_poll);
+ ATF_TP_ADD_TC(tp, fifo_signal);
+ ATF_TP_ADD_TC(tp, fifo_suspend);
+ ATF_TP_ADD_TC(tp, fifo_thread);
+ ATF_TP_ADD_TC(tp, fifo_waitcomplete);
+ ATF_TP_ADD_TC(tp, socket_kq);
+ ATF_TP_ADD_TC(tp, socket_poll);
+ ATF_TP_ADD_TC(tp, socket_signal);
+ ATF_TP_ADD_TC(tp, socket_suspend);
+ ATF_TP_ADD_TC(tp, socket_thread);
+ ATF_TP_ADD_TC(tp, socket_waitcomplete);
+ ATF_TP_ADD_TC(tp, pty_kq);
+ ATF_TP_ADD_TC(tp, pty_poll);
+ ATF_TP_ADD_TC(tp, pty_signal);
+ ATF_TP_ADD_TC(tp, pty_suspend);
+ ATF_TP_ADD_TC(tp, pty_thread);
+ ATF_TP_ADD_TC(tp, pty_waitcomplete);
+ ATF_TP_ADD_TC(tp, pipe_kq);
+ ATF_TP_ADD_TC(tp, pipe_poll);
+ ATF_TP_ADD_TC(tp, pipe_signal);
+ ATF_TP_ADD_TC(tp, pipe_suspend);
+ ATF_TP_ADD_TC(tp, pipe_thread);
+ ATF_TP_ADD_TC(tp, pipe_waitcomplete);
+ ATF_TP_ADD_TC(tp, md_kq);
+ ATF_TP_ADD_TC(tp, md_poll);
+ ATF_TP_ADD_TC(tp, md_signal);
+ ATF_TP_ADD_TC(tp, md_suspend);
+ ATF_TP_ADD_TC(tp, md_thread);
+ ATF_TP_ADD_TC(tp, md_waitcomplete);
+
+ /* Various special cases */
+ ATF_TP_ADD_TC(tp, aio_fsync_errors);
+ ATF_TP_ADD_TC(tp, aio_fsync_sync_test);
+ ATF_TP_ADD_TC(tp, aio_fsync_dsync_test);
+ ATF_TP_ADD_TC(tp, aio_large_read_test);
+ ATF_TP_ADD_TC(tp, aio_socket_two_reads);
+ ATF_TP_ADD_TC(tp, aio_socket_blocking_short_write);
+ ATF_TP_ADD_TC(tp, aio_socket_blocking_short_write_vectored);
+ ATF_TP_ADD_TC(tp, aio_socket_listen_fail);
+ ATF_TP_ADD_TC(tp, aio_socket_listen_pending);
+ ATF_TP_ADD_TC(tp, aio_socket_short_write_cancel);
+ ATF_TP_ADD_TC(tp, aio_socket_shutdown);
+ ATF_TP_ADD_TC(tp, aio_writev_dos_iov_len);
+ ATF_TP_ADD_TC(tp, aio_writev_dos_iovcnt);
+ ATF_TP_ADD_TC(tp, aio_writev_efault);
+ ATF_TP_ADD_TC(tp, aio_writev_empty_file_poll);
+ ATF_TP_ADD_TC(tp, aio_writev_empty_file_signal);
+ ATF_TP_ADD_TC(tp, ev_oneshot);
+ ATF_TP_ADD_TC(tp, vectored_big_iovcnt);
+ ATF_TP_ADD_TC(tp, vectored_file_poll);
+ ATF_TP_ADD_TC(tp, vectored_md_poll);
+ ATF_TP_ADD_TC(tp, vectored_zvol_poll);
+ ATF_TP_ADD_TC(tp, vectored_unaligned);
+ ATF_TP_ADD_TC(tp, vectored_socket_poll);
+ ATF_TP_ADD_TC(tp, vectored_thread);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/aio/lio_kqueue_test.c b/tests/sys/aio/lio_kqueue_test.c
new file mode 100644
index 000000000000..f891ab95f3ca
--- /dev/null
+++ b/tests/sys/aio/lio_kqueue_test.c
@@ -0,0 +1,238 @@
+/*-
+ * Copyright (C) 2005 IronPort Systems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Note: it is a good idea to run this against a physical drive to
+ * exercise the physio fast path (ie. lio_kqueue_test /dev/<something safe>)
+ */
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/time.h>
+#include <aio.h>
+#include <fcntl.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "freebsd_test_suite/macros.h"
+#include "local.h"
+
+#define PATH_TEMPLATE "aio.XXXXXXXXXX"
+
+#define LIO_MAX 5
+#define MAX_IOCBS_PER_LIO 64
+#define MAX_IOCBS (LIO_MAX * MAX_IOCBS_PER_LIO)
+#define MAX_RUNS 300
+
+int
+main(int argc, char *argv[])
+{
+ int fd;
+ struct aiocb *iocb[MAX_IOCBS];
+ struct aiocb **lio[LIO_MAX], **kq_lio;
+ int i, result, run, error, j, k, max_queue_per_proc;
+ int max_iocbs, iocbs_per_lio;
+ size_t max_queue_per_proc_size;
+ char buffer[32768];
+ int kq;
+ struct kevent kq_returned;
+ struct timespec ts;
+ struct sigevent sig;
+ time_t time1, time2;
+ char *file, pathname[sizeof(PATH_TEMPLATE)];
+ int tmp_file = 0, failed = 0;
+
+ PLAIN_REQUIRE_KERNEL_MODULE("aio", 0);
+ PLAIN_REQUIRE_UNSAFE_AIO(0);
+
+ max_queue_per_proc_size = sizeof(max_queue_per_proc);
+ if (sysctlbyname("vfs.aio.max_aio_queue_per_proc",
+ &max_queue_per_proc, &max_queue_per_proc_size, NULL, 0) != 0)
+ err(1, "sysctlbyname");
+ iocbs_per_lio = max_queue_per_proc / LIO_MAX;
+ max_iocbs = LIO_MAX * iocbs_per_lio;
+
+ kq = kqueue();
+ if (kq < 0)
+ err(1, "kqeueue(2) failed");
+
+ if (argc == 1) {
+ strcpy(pathname, PATH_TEMPLATE);
+ fd = mkstemp(pathname);
+ file = pathname;
+ tmp_file = 1;
+ } else {
+ file = argv[1];
+ fd = open(file, O_RDWR|O_CREAT, 0666);
+ }
+ if (fd < 0)
+ err(1, "can't open %s", argv[1]);
+
+#ifdef DEBUG
+ printf("Hello kq %d fd %d\n", kq, fd);
+#endif
+
+ for (run = 0; run < MAX_RUNS; run++) {
+#ifdef DEBUG
+ printf("Run %d\n", run);
+#endif
+ for (j = 0; j < LIO_MAX; j++) {
+ lio[j] =
+ malloc(sizeof(struct aiocb *) * iocbs_per_lio);
+ for (i = 0; i < iocbs_per_lio; i++) {
+ k = (iocbs_per_lio * j) + i;
+ lio[j][i] = iocb[k] =
+ calloc(1, sizeof(struct aiocb));
+ iocb[k]->aio_nbytes = sizeof(buffer);
+ iocb[k]->aio_buf = buffer;
+ iocb[k]->aio_fildes = fd;
+ iocb[k]->aio_offset
+ = iocb[k]->aio_nbytes * k * (run + 1);
+
+#ifdef DEBUG
+ printf("hello iocb[k] %jd\n",
+ (intmax_t)iocb[k]->aio_offset);
+#endif
+ iocb[k]->aio_lio_opcode = LIO_WRITE;
+ }
+ sig.sigev_notify_kqueue = kq;
+ sig.sigev_value.sival_ptr = lio[j];
+ sig.sigev_notify = SIGEV_KEVENT;
+ time(&time1);
+ result = lio_listio(LIO_NOWAIT, lio[j],
+ iocbs_per_lio, &sig);
+ error = errno;
+ time(&time2);
+#ifdef DEBUG
+ printf("Time %jd %jd %jd result -> %d\n",
+ (intmax_t)time1, (intmax_t)time2,
+ (intmax_t)time2-time1, result);
+#endif
+ if (result != 0) {
+ errno = error;
+ err(1, "FAIL: Result %d iteration %d\n",
+ result, j);
+ }
+#ifdef DEBUG
+ printf("write %d is at %p\n", j, lio[j]);
+#endif
+ }
+
+ for (i = 0; i < LIO_MAX; i++) {
+ for (j = LIO_MAX - 1; j >=0; j--) {
+ if (lio[j])
+ break;
+ }
+
+ for (;;) {
+ bzero(&kq_returned, sizeof(kq_returned));
+ ts.tv_sec = 0;
+ ts.tv_nsec = 1;
+#ifdef DEBUG
+ printf("FOO lio %d -> %p\n", j, lio[j]);
+#endif
+ result = kevent(kq, NULL, 0,
+ &kq_returned, 1, &ts);
+ error = errno;
+ if (result < 0) {
+ perror("kevent error: ");
+ }
+ kq_lio = kq_returned.udata;
+#ifdef DEBUG
+ printf("kevent %d %d errno %d return.ident %p "
+ "return.data %p return.udata %p %p\n",
+ i, result, error,
+ (void*)kq_returned.ident,
+ (void*)kq_returned.data,
+ kq_returned.udata,
+ lio[j]);
+#endif
+
+ if (kq_lio)
+ break;
+#ifdef DEBUG
+ printf("Try again\n");
+#endif
+ }
+
+#ifdef DEBUG
+ printf("lio %p\n", lio);
+#endif
+
+ for (j = 0; j < LIO_MAX; j++) {
+ if (lio[j] == kq_lio)
+ break;
+ }
+ if (j == LIO_MAX)
+ errx(1, "FAIL: ");
+
+#ifdef DEBUG
+ printf("Error Result for %d is %d\n", j, result);
+#endif
+ if (result < 0) {
+ printf("FAIL: run %d, operation %d result %d \n", run, LIO_MAX - i -1, result);
+ failed++;
+ } else
+ printf("PASS: run %d, operation %d result %d \n", run, LIO_MAX - i -1, result);
+ for (k = 0; k < max_iocbs / LIO_MAX; k++) {
+ result = aio_return(kq_lio[k]);
+#ifdef DEBUG
+ printf("Return Result for %d %d is %d\n", j, k, result);
+#endif
+ if (result != sizeof(buffer)) {
+ printf("FAIL: run %d, operation %d sub-opt %d result %d (errno=%d) should be %zu\n",
+ run, LIO_MAX - i -1, k, result, errno, sizeof(buffer));
+ } else {
+ printf("PASS: run %d, operation %d sub-opt %d result %d\n",
+ run, LIO_MAX - i -1, k, result);
+ }
+ }
+#ifdef DEBUG
+ printf("\n");
+#endif
+
+ for (k = 0; k < max_iocbs / LIO_MAX; k++)
+ free(lio[j][k]);
+ free(lio[j]);
+ lio[j] = NULL;
+ }
+ }
+#ifdef DEBUG
+ printf("Done\n");
+#endif
+
+ if (tmp_file)
+ unlink(pathname);
+
+ if (failed)
+ errx(1, "FAIL: %d testcases failed", failed);
+ else
+ errx(0, "PASS: All\n");
+
+}
diff --git a/tests/sys/aio/lio_test.c b/tests/sys/aio/lio_test.c
new file mode 100644
index 000000000000..a59f9bd518bc
--- /dev/null
+++ b/tests/sys/aio/lio_test.c
@@ -0,0 +1,336 @@
+/*-
+ * Copyright (c) 2017 Spectra Logic Corp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define _WANT_ALL_LIO_OPCODES
+
+#include <sys/param.h>
+#include <sys/event.h>
+#include <sys/uio.h>
+
+#include <aio.h>
+#include <fcntl.h>
+#include <semaphore.h>
+#include <stdlib.h>
+
+#include <atf-c.h>
+
+#include "local.h"
+#include "freebsd_test_suite/macros.h"
+
+static sem_t completions;
+
+
+static void
+handler(int sig __unused)
+{
+ ATF_REQUIRE_EQ(0, sem_post(&completions));
+}
+
+static void
+thr_handler(union sigval sv __unused)
+{
+ ATF_REQUIRE_EQ(0, sem_post(&completions));
+}
+
+/*
+ * If lio_listio is unable to enqueue any requests at all, it should return
+ * EAGAIN.
+ */
+ATF_TC_WITHOUT_HEAD(lio_listio_eagain_kevent);
+ATF_TC_BODY(lio_listio_eagain_kevent, tc)
+{
+ int fd, i, j, kq, max_queue_per_proc, ios_per_call;
+ size_t max_queue_per_proc_size;
+ struct aiocb *aiocbs[2];
+ struct aiocb **list[2];
+ struct sigevent sev[2];
+ char *buffer;
+ const char *path="tempfile";
+ void *udata[2];
+
+ ATF_REQUIRE_KERNEL_MODULE("aio");
+ ATF_REQUIRE_UNSAFE_AIO();
+
+ max_queue_per_proc_size = sizeof(max_queue_per_proc);
+ ATF_REQUIRE_EQ(sysctlbyname("vfs.aio.max_aio_queue_per_proc",
+ &max_queue_per_proc, &max_queue_per_proc_size, NULL, 0), 0);
+ ios_per_call = max_queue_per_proc;
+
+ fd = open(path, O_RDWR|O_CREAT, 0666);
+ ATF_REQUIRE(fd >= 0);
+
+ kq = kqueue();
+ ATF_REQUIRE(kq > 0);
+
+ buffer = calloc(1, 4096);
+ ATF_REQUIRE(buffer != NULL);
+
+ /*
+ * Call lio_listio twice, each with the maximum number of operations.
+ * The first call should succeed and the second should fail.
+ */
+ for (i = 0; i < 2; i++) {
+ aiocbs[i] = calloc(ios_per_call, sizeof(struct aiocb));
+ ATF_REQUIRE(aiocbs[i] != NULL);
+ list[i] = calloc(ios_per_call, sizeof(struct aiocb*));
+ ATF_REQUIRE(list[i] != NULL);
+ udata[i] = (void*)((caddr_t)0xdead0000 + i);
+ sev[i].sigev_notify = SIGEV_KEVENT;
+ sev[i].sigev_notify_kqueue = kq;
+ sev[i].sigev_value.sival_ptr = udata[i];
+ for (j = 0; j < ios_per_call; j++) {
+ aiocbs[i][j].aio_fildes = fd;
+ aiocbs[i][j].aio_offset = (i * ios_per_call + j) * 4096;
+ aiocbs[i][j].aio_buf = buffer;
+ aiocbs[i][j].aio_nbytes = 4096;
+ aiocbs[i][j].aio_lio_opcode = LIO_WRITE;
+ list[i][j] = &aiocbs[i][j];
+ }
+ }
+
+ ATF_REQUIRE_EQ(0, lio_listio(LIO_NOWAIT, list[0], ios_per_call, &sev[0]));
+ ATF_REQUIRE_EQ(-1, lio_listio(LIO_NOWAIT, list[1], ios_per_call, &sev[1]));
+ /*
+ * The second lio_listio call should fail with EAGAIN. Bad timing may
+ * mean that some requests did get enqueued, but the result should
+ * still be EAGAIN.
+ */
+ ATF_REQUIRE_EQ(errno, EAGAIN);
+}
+
+
+/* With LIO_WAIT, an empty lio_listio should return immediately */
+ATF_TC_WITHOUT_HEAD(lio_listio_empty_wait);
+ATF_TC_BODY(lio_listio_empty_wait, tc)
+{
+ struct aiocb *list = NULL;
+
+ ATF_REQUIRE_EQ(0, lio_listio(LIO_WAIT, &list, 0, NULL));
+}
+
+/* With LIO_NOWAIT, an empty lio_listio should return immediately */
+ATF_TC_WITHOUT_HEAD(lio_listio_empty_nowait);
+ATF_TC_BODY(lio_listio_empty_nowait, tc)
+{
+ struct aiocb *list = NULL;
+
+ ATF_REQUIRE_EQ(0, lio_listio(LIO_NOWAIT, &list, 0, NULL));
+}
+
+/*
+ * With LIO_NOWAIT, an empty lio_listio should send completion notification
+ * immediately
+ */
+ATF_TC_WITHOUT_HEAD(lio_listio_empty_nowait_kevent);
+ATF_TC_BODY(lio_listio_empty_nowait_kevent, tc)
+{
+ struct aiocb *list = NULL;
+ struct sigevent sev;
+ struct kevent kq_returned;
+ int kq, result;
+ void *udata = (void*)0xdeadbeefdeadbeef;
+
+ atf_tc_expect_timeout("Bug 251515 - lio_listio(2) never sends"
+ " kevent if nent==0");
+ kq = kqueue();
+ ATF_REQUIRE(kq > 0);
+ sev.sigev_notify = SIGEV_KEVENT;
+ sev.sigev_notify_kqueue = kq;
+ sev.sigev_value.sival_ptr = udata;
+ ATF_REQUIRE_EQ(0, lio_listio(LIO_NOWAIT, &list, 0, &sev));
+ result = kevent(kq, NULL, 0, &kq_returned, 1, NULL);
+ ATF_REQUIRE_MSG(result == 1, "Never got completion notification");
+ ATF_REQUIRE_EQ((uintptr_t)list, kq_returned.ident);
+ ATF_REQUIRE_EQ(EVFILT_LIO, kq_returned.filter);
+ ATF_REQUIRE_EQ(udata, kq_returned.udata);
+}
+
+/*
+ * With LIO_NOWAIT, an empty lio_listio should send completion notification
+ * immediately
+ */
+ATF_TC_WITHOUT_HEAD(lio_listio_empty_nowait_signal);
+ATF_TC_BODY(lio_listio_empty_nowait_signal, tc)
+{
+ struct aiocb *list = NULL;
+ struct sigevent sev;
+
+ ATF_REQUIRE_EQ(0, sem_init(&completions, false, 0));
+ sev.sigev_notify = SIGEV_SIGNAL;
+ sev.sigev_signo = SIGUSR1;
+ ATF_REQUIRE(SIG_ERR != signal(SIGUSR1, handler));
+ ATF_REQUIRE_EQ(0, lio_listio(LIO_NOWAIT, &list, 0, &sev));
+ ATF_REQUIRE_EQ(0, sem_wait(&completions));
+ ATF_REQUIRE_EQ(0, sem_destroy(&completions));
+}
+
+/*
+ * With LIO_NOWAIT, an empty lio_listio should send completion notification
+ * immediately
+ */
+ATF_TC_WITHOUT_HEAD(lio_listio_empty_nowait_thread);
+ATF_TC_BODY(lio_listio_empty_nowait_thread, tc)
+{
+ struct aiocb *list = NULL;
+ struct sigevent sev;
+
+ ATF_REQUIRE_EQ(0, sem_init(&completions, false, 0));
+ bzero(&sev, sizeof(sev));
+ sev.sigev_notify = SIGEV_THREAD;
+ sev.sigev_notify_function = thr_handler;
+ sev.sigev_notify_attributes = NULL;
+ ATF_REQUIRE_MSG(0 == lio_listio(LIO_NOWAIT, &list, 0, &sev),
+ "lio_listio: %s", strerror(errno));
+ ATF_REQUIRE_EQ(0, sem_wait(&completions));
+ ATF_REQUIRE_EQ(0, sem_destroy(&completions));
+}
+
+/*
+ * A simple check that the allowed operations work.
+ */
+ATF_TC_WITHOUT_HEAD(lio_listio_opcodes);
+ATF_TC_BODY(lio_listio_opcodes, tc)
+{
+ struct aiocb write_cb, read_cb, writev_cb, readv_cb;
+ struct aiocb *list[] = {&write_cb, &read_cb, &writev_cb, &readv_cb};
+ struct iovec writev_iov[2];
+ struct iovec readv_iov[2];
+ char buffer[6];
+ int fd;
+
+ fd = open("testfile", O_CREAT | O_RDWR, 0666);
+ ATF_REQUIRE_MSG(fd >= 0, "open: %s", strerror(errno));
+
+ /* We start with numbers in a file and letters in memory... */
+ ATF_CHECK_EQ(6, write(fd, "123456", 6));
+ memcpy(buffer, "abcdef", 6);
+
+ /* a -> 1 */
+ bzero(&write_cb, sizeof(write_cb));
+ write_cb.aio_sigevent.sigev_notify = SIGEV_NONE;
+ write_cb.aio_fildes = fd;
+ write_cb.aio_lio_opcode = LIO_WRITE;
+ write_cb.aio_buf = &buffer[0];
+ write_cb.aio_nbytes = 1;
+ write_cb.aio_offset = 0;
+
+ /* b <- 2 */
+ bzero(&read_cb, sizeof(read_cb));
+ read_cb.aio_sigevent.sigev_notify = SIGEV_NONE;
+ read_cb.aio_fildes = fd;
+ read_cb.aio_lio_opcode = LIO_READ;
+ read_cb.aio_buf = &buffer[1];
+ read_cb.aio_nbytes = 1;
+ read_cb.aio_offset = 1;
+
+ /* d -> 3, c -> 4 */
+ writev_iov[0].iov_base = &buffer[3];
+ writev_iov[0].iov_len = 1;
+ writev_iov[1].iov_base = &buffer[2];
+ writev_iov[1].iov_len = 1;
+ bzero(&writev_cb, sizeof(writev_cb));
+ writev_cb.aio_sigevent.sigev_notify = SIGEV_NONE;
+ writev_cb.aio_fildes = fd;
+ writev_cb.aio_lio_opcode = LIO_WRITEV;
+ writev_cb.aio_iov = &writev_iov;
+ writev_cb.aio_iovcnt = 2;
+ writev_cb.aio_offset = 2;
+
+ /* f <- 5, e <- 6 */
+ readv_iov[0].iov_base = &buffer[5];
+ readv_iov[0].iov_len = 1;
+ readv_iov[1].iov_base = &buffer[4];
+ readv_iov[1].iov_len = 1;
+ bzero(&readv_cb, sizeof(readv_cb));
+ readv_cb.aio_sigevent.sigev_notify = SIGEV_NONE;
+ readv_cb.aio_fildes = fd;
+ readv_cb.aio_lio_opcode = LIO_READV;
+ readv_cb.aio_iov = &readv_iov;
+ readv_cb.aio_iovcnt = 2;
+ readv_cb.aio_offset = 4;
+
+ ATF_CHECK_EQ(0, lio_listio(LIO_WAIT, list, nitems(list), NULL));
+ ATF_CHECK_EQ(0, aio_error(&write_cb));
+ ATF_CHECK_EQ(1, aio_return(&write_cb));
+ ATF_CHECK_EQ(0, aio_error(&read_cb));
+ ATF_CHECK_EQ(1, aio_return(&read_cb));
+ ATF_CHECK_EQ(0, aio_error(&writev_cb));
+ ATF_CHECK_EQ(2, aio_return(&writev_cb));
+ ATF_CHECK_EQ(0, aio_error(&readv_cb));
+ ATF_CHECK_EQ(2, aio_return(&readv_cb));
+
+ ATF_CHECK_EQ(0, memcmp(buffer, "a2cd65", 6));
+ ATF_CHECK_EQ(6, pread(fd, buffer, 6, 0));
+ ATF_CHECK_EQ(0, memcmp(buffer, "a2dc56", 6));
+
+ close(fd);
+}
+
+
+/*
+ * Only select opcodes are allowed with lio_listio
+ */
+ATF_TC_WITHOUT_HEAD(lio_listio_invalid_opcode);
+ATF_TC_BODY(lio_listio_invalid_opcode, tc)
+{
+ struct aiocb sync_cb, mlock_cb;
+ struct aiocb *list[] = {&sync_cb, &mlock_cb};
+ int fd;
+
+ fd = open("testfile", O_CREAT | O_RDWR, 0666);
+ ATF_REQUIRE_MSG(fd >= 0, "open: %s", strerror(errno));
+
+ bzero(&sync_cb, sizeof(sync_cb));
+ sync_cb.aio_fildes = fd;
+ sync_cb.aio_lio_opcode = LIO_SYNC;
+
+ bzero(&mlock_cb, sizeof(mlock_cb));
+ mlock_cb.aio_lio_opcode = LIO_MLOCK;
+
+ ATF_CHECK_ERRNO(EIO, lio_listio(LIO_WAIT, list, nitems(list), NULL));
+ ATF_CHECK_EQ(EINVAL, aio_error(&sync_cb));
+ ATF_CHECK_ERRNO(EINVAL, aio_return(&sync_cb) < 0);
+ ATF_CHECK_EQ(EINVAL, aio_error(&mlock_cb));
+ ATF_CHECK_ERRNO(EINVAL, aio_return(&mlock_cb) < 0);
+
+ close(fd);
+}
+
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, lio_listio_eagain_kevent);
+ ATF_TP_ADD_TC(tp, lio_listio_empty_nowait);
+ ATF_TP_ADD_TC(tp, lio_listio_empty_nowait_kevent);
+ ATF_TP_ADD_TC(tp, lio_listio_empty_nowait_signal);
+ ATF_TP_ADD_TC(tp, lio_listio_empty_nowait_thread);
+ ATF_TP_ADD_TC(tp, lio_listio_empty_wait);
+ ATF_TP_ADD_TC(tp, lio_listio_opcodes);
+ ATF_TP_ADD_TC(tp, lio_listio_invalid_opcode);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/aio/local.h b/tests/sys/aio/local.h
new file mode 100644
index 000000000000..c1574e16994d
--- /dev/null
+++ b/tests/sys/aio/local.h
@@ -0,0 +1,87 @@
+/*-
+ * Copyright (c) 2016 Chelsio Communications, Inc.
+ * All rights reserved.
+ * Written by: John Baldwin <jhb@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _AIO_TEST_LOCAL_H_
+#define _AIO_TEST_LOCAL_H_
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+static const char *sysctl_oid_name = "vfs.aio.enable_unsafe";
+
+static int
+is_unsafe_aio_enabled(void)
+{
+ size_t len;
+ int unsafe;
+
+ len = sizeof(unsafe);
+ if (sysctlbyname(sysctl_oid_name, &unsafe, &len, NULL, 0) < 0) {
+ if (errno == ENOENT)
+ return (-1);
+ return (0);
+ }
+ return (unsafe == 0 ? 0 : 1);
+}
+
+#define ATF_REQUIRE_UNSAFE_AIO() do { \
+ switch (is_unsafe_aio_enabled()) { \
+ case -1: \
+ atf_libc_error(errno, "Failed to read %s", sysctl_oid_name); \
+ break; \
+ case 0: \
+ atf_tc_skip("Unsafe AIO is disabled"); \
+ break; \
+ default: \
+ printf("Unsafe AIO is enabled\n"); \
+ break; \
+ } \
+} while (0)
+
+#define PLAIN_REQUIRE_UNSAFE_AIO(_exit_code) do { \
+ switch (is_unsafe_aio_enabled()) { \
+ case -1: \
+ printf("Failed to read %s", sysctl_oid_name); \
+ _exit(_exit_code); \
+ break; \
+ case 0: \
+ printf("Unsafe AIO is disabled\n"); \
+ _exit(_exit_code); \
+ break; \
+ default: \
+ printf("Unsafe AIO is enabled\n"); \
+ break; \
+ } \
+} while (0)
+
+#endif /* !_AIO_TEST_LOCAL_H_ */
diff --git a/tests/sys/audit/Makefile b/tests/sys/audit/Makefile
new file mode 100644
index 000000000000..d6d9c2874d09
--- /dev/null
+++ b/tests/sys/audit/Makefile
@@ -0,0 +1,66 @@
+TESTSDIR= ${TESTSBASE}/sys/audit
+
+ATF_TESTS_C= file-attribute-access
+ATF_TESTS_C+= file-attribute-modify
+ATF_TESTS_C+= file-create
+ATF_TESTS_C+= file-delete
+ATF_TESTS_C+= file-close
+ATF_TESTS_C+= file-write
+ATF_TESTS_C+= file-read
+ATF_TESTS_C+= open
+ATF_TESTS_C+= ioctl
+ATF_TESTS_C+= network
+ATF_TESTS_C+= inter-process
+ATF_TESTS_C+= administrative
+ATF_TESTS_C+= process-control
+ATF_TESTS_C+= miscellaneous
+
+SRCS.file-attribute-access+= file-attribute-access.c
+SRCS.file-attribute-access+= utils.c
+SRCS.file-attribute-modify+= file-attribute-modify.c
+SRCS.file-attribute-modify+= utils.c
+SRCS.file-create+= file-create.c
+SRCS.file-create+= utils.c
+SRCS.file-delete+= file-delete.c
+SRCS.file-delete+= utils.c
+SRCS.file-close+= file-close.c
+SRCS.file-close+= utils.c
+SRCS.file-write+= file-write.c
+SRCS.file-write+= utils.c
+SRCS.file-read+= file-read.c
+SRCS.file-read+= utils.c
+SRCS.open+= open.c
+SRCS.open+= utils.c
+SRCS.ioctl+= ioctl.c
+SRCS.ioctl+= utils.c
+SRCS.network+= network.c
+SRCS.network+= utils.c
+SRCS.inter-process+= inter-process.c
+SRCS.inter-process+= utils.c
+SRCS.administrative+= administrative.c
+SRCS.administrative+= utils.c
+SRCS.process-control+= process-control.c
+SRCS.process-control+= utils.c
+SRCS.miscellaneous+= miscellaneous.c
+SRCS.miscellaneous+= utils.c
+
+TEST_METADATA+= timeout="30"
+TEST_METADATA+= required_user="root"
+# Only one process can be auditing, if we attempt to run these tests in parallel
+# some of them will fail to start auditing.
+# TODO: it would be nice to be able to run them in parallel with other non-audit
+# tests using some internal form of synchronization.
+# TODO: In addititon to test failures, running in parallel can trigger a kernel
+# panic: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=253616
+TEST_METADATA+= is_exclusive="true"
+TEST_METADATA+= required_files="/etc/rc.d/auditd /dev/auditpipe"
+
+MK_PIE:= no # XXX libprivateauditd.a is not PIE
+LDFLAGS+= -lbsm -lutil
+OPENBSMDIR=${SRCTOP}/contrib/openbsm
+CFLAGS+= -I${OPENBSMDIR}
+LDADD+= ${LIBAUDITD}
+
+CFLAGS.process-control.c+= -I${SRCTOP}/tests
+
+.include <bsd.test.mk>
diff --git a/tests/sys/audit/administrative.c b/tests/sys/audit/administrative.c
new file mode 100644
index 000000000000..48e24222f881
--- /dev/null
+++ b/tests/sys/audit/administrative.c
@@ -0,0 +1,1692 @@
+/*-
+ * Copyright (c) 2018 Aniket Pandey
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/timespec.h>
+#include <sys/timex.h>
+
+#include <bsm/audit.h>
+#include <bsm/audit_kevents.h>
+#include <ufs/ufs/quota.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "utils.h"
+
+static pid_t pid;
+static int filedesc;
+/* Default argument for handling ENOSYS in auditon(2) functions */
+static int auditon_def = 0;
+static mode_t mode = 0777;
+static struct pollfd fds[1];
+static char adregex[80];
+static const char *auclass = "ad";
+static const char *path = "fileforaudit";
+static const char *successreg = "fileforaudit.*return,success";
+
+
+ATF_TC_WITH_CLEANUP(settimeofday_success);
+ATF_TC_HEAD(settimeofday_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "settimeofday(2) call");
+}
+
+ATF_TC_BODY(settimeofday_success, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "settimeofday.*%d.*success", pid);
+
+ struct timeval tp;
+ struct timezone tzp;
+ ATF_REQUIRE_EQ(0, gettimeofday(&tp, &tzp));
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Setting the same time as obtained by gettimeofday(2) */
+ ATF_REQUIRE_EQ(0, settimeofday(&tp, &tzp));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(settimeofday_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(settimeofday_failure);
+ATF_TC_HEAD(settimeofday_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "settimeofday(2) call");
+}
+
+ATF_TC_BODY(settimeofday_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "settimeofday.*%d.*failure", pid);
+
+ struct timeval tp;
+ struct timezone tzp;
+ ATF_REQUIRE_EQ(0, gettimeofday(&tp, &tzp));
+
+ FILE *pipefd = setup(fds, auclass);
+ tp.tv_sec = -1;
+ /* Failure reason: Invalid value for tp.tv_sec; */
+ ATF_REQUIRE_EQ(-1, settimeofday(&tp, &tzp));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(settimeofday_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(clock_settime_success);
+ATF_TC_HEAD(clock_settime_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "clock_settime(2) call");
+}
+
+ATF_TC_BODY(clock_settime_success, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "clock_settime.*%d.*success", pid);
+
+ struct timespec tp;
+ ATF_REQUIRE_EQ(0, clock_gettime(CLOCK_REALTIME, &tp));
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Setting the same time as obtained by clock_gettime(2) */
+ ATF_REQUIRE_EQ(0, clock_settime(CLOCK_REALTIME, &tp));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(clock_settime_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(clock_settime_failure);
+ATF_TC_HEAD(clock_settime_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "clock_settime(2) call");
+}
+
+ATF_TC_BODY(clock_settime_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "clock_settime.*%d.*failure", pid);
+
+ struct timespec tp;
+ ATF_REQUIRE_EQ(0, clock_gettime(CLOCK_MONOTONIC, &tp));
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: cannot use CLOCK_MONOTONIC to set the system time */
+ ATF_REQUIRE_EQ(-1, clock_settime(CLOCK_MONOTONIC, &tp));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(clock_settime_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(adjtime_success);
+ATF_TC_HEAD(adjtime_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "adjtime(2) call");
+}
+
+ATF_TC_BODY(adjtime_success, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "adjtime.*%d.*return,success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* We don't want to change the system time, hence NULL */
+ ATF_REQUIRE_EQ(0, adjtime(NULL, NULL));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(adjtime_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(adjtime_failure);
+ATF_TC_HEAD(adjtime_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "adjtime(2) call");
+}
+
+ATF_TC_BODY(adjtime_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "adjtime.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, adjtime((struct timeval *)(-1), NULL));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(adjtime_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(ntp_adjtime_success);
+ATF_TC_HEAD(ntp_adjtime_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "ntp_adjtime(2) call");
+}
+
+ATF_TC_BODY(ntp_adjtime_success, tc)
+{
+ struct timex timebuff;
+ bzero(&timebuff, sizeof(timebuff));
+
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "ntp_adjtime.*%d.*success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE(ntp_adjtime(&timebuff) != -1);
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(ntp_adjtime_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(ntp_adjtime_failure);
+ATF_TC_HEAD(ntp_adjtime_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "ntp_adjtime(2) call");
+}
+
+ATF_TC_BODY(ntp_adjtime_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "ntp_adjtime.*%d.*failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, ntp_adjtime(NULL));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(ntp_adjtime_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(nfs_getfh_success);
+ATF_TC_HEAD(nfs_getfh_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "getfh(2) call");
+}
+
+ATF_TC_BODY(nfs_getfh_success, tc)
+{
+ fhandle_t fhp;
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "nfs_getfh.*%d.*ret.*success", pid);
+
+ /* File needs to exist to call getfh(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, getfh(path, &fhp));
+ check_audit(fds, adregex, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(nfs_getfh_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(nfs_getfh_failure);
+ATF_TC_HEAD(nfs_getfh_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "getfh(2) call");
+}
+
+ATF_TC_BODY(nfs_getfh_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "nfs_getfh.*%d.*ret.*failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_EQ(-1, getfh(path, NULL));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(nfs_getfh_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(auditctl_success);
+ATF_TC_HEAD(auditctl_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "auditctl(2) call");
+}
+
+ATF_TC_BODY(auditctl_success, tc)
+{
+ /* File needs to exist in order to call auditctl(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT | O_WRONLY, mode)) != -1);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, auditctl(path));
+ check_audit(fds, successreg, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(auditctl_success, tc)
+{
+ /*
+ * auditctl(2) disables audit log at /var/audit and initiates auditing
+ * at the configured path. To reset this, we need to stop and start the
+ * auditd(8) again. Here, we check if auditd(8) was running already
+ * before the test started. If so, we stop and start it again.
+ *
+ * TODO: should we skip this test if auditd(8) is already running to
+ * avoid restarting it?
+ */
+ if (!atf_utils_file_exists("started_fake_auditd")) {
+ system("service auditd onestop > /dev/null 2>&1");
+ system("service auditd onestart > /dev/null 2>&1");
+ } else {
+ cleanup();
+ }
+}
+
+
+ATF_TC_WITH_CLEANUP(auditctl_failure);
+ATF_TC_HEAD(auditctl_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "auditctl(2) call");
+}
+
+ATF_TC_BODY(auditctl_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "auditctl.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_EQ(-1, auditctl(NULL));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditctl_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(acct_success);
+ATF_TC_HEAD(acct_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "acct(2) call");
+ atf_tc_set_md_var(tc, "require.files",
+ "/etc/rc.d/accounting /etc/rc.d/auditd");
+}
+
+ATF_TC_BODY(acct_success, tc)
+{
+ int acctinfo, filedesc2;
+ size_t len = sizeof(acctinfo);
+ const char *acctname = "kern.acct_configured";
+ ATF_REQUIRE_EQ(0, sysctlbyname(acctname, &acctinfo, &len, NULL, 0));
+
+ /* File needs to exist to start system accounting */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT | O_RDWR, mode)) != -1);
+
+ /*
+ * acctinfo = 0: System accounting was disabled
+ * acctinfo = 1: System accounting was enabled
+ */
+ if (acctinfo) {
+ ATF_REQUIRE((filedesc2 = open("acct_ok", O_CREAT, mode)) != -1);
+ close(filedesc2);
+ }
+
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex),
+ "acct.*%s.*%d.*return,success", path, pid);
+
+ /*
+ * We temporarily switch the accounting record to a file at
+ * our own configured path in order to confirm acct(2)'s successful
+ * auditing. Then we set everything back to its original state.
+ */
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, acct(path));
+ check_audit(fds, adregex, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(acct_success, tc)
+{
+ /* Reset accounting configured path */
+ ATF_REQUIRE_EQ(0, system("service accounting onestop"));
+ if (atf_utils_file_exists("acct_ok")) {
+ ATF_REQUIRE_EQ(0, system("service accounting onestart"));
+ }
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(acct_failure);
+ATF_TC_HEAD(acct_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "acct(2) call");
+}
+
+ATF_TC_BODY(acct_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "acct.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: File does not exist */
+ ATF_REQUIRE_EQ(-1, acct(path));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(acct_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(getauid_success);
+ATF_TC_HEAD(getauid_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "getauid(2) call");
+}
+
+ATF_TC_BODY(getauid_success, tc)
+{
+ au_id_t auid;
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "getauid.*%d.*return,success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, getauid(&auid));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(getauid_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(getauid_failure);
+ATF_TC_HEAD(getauid_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "getauid(2) call");
+}
+
+ATF_TC_BODY(getauid_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "getauid.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Bad address */
+ ATF_REQUIRE_EQ(-1, getauid(NULL));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(getauid_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(setauid_success);
+ATF_TC_HEAD(setauid_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "setauid(2) call");
+}
+
+ATF_TC_BODY(setauid_success, tc)
+{
+ au_id_t auid;
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "setauid.*%d.*return,success", pid);
+ ATF_REQUIRE_EQ(0, getauid(&auid));
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, setauid(&auid));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(setauid_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(setauid_failure);
+ATF_TC_HEAD(setauid_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "setauid(2) call");
+}
+
+ATF_TC_BODY(setauid_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "setauid.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Bad address */
+ ATF_REQUIRE_EQ(-1, setauid(NULL));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(setauid_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(getaudit_success);
+ATF_TC_HEAD(getaudit_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "getaudit(2) call");
+}
+
+ATF_TC_BODY(getaudit_success, tc)
+{
+ pid = getpid();
+ auditinfo_t auditinfo;
+ snprintf(adregex, sizeof(adregex), "getaudit.*%d.*return,success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, getaudit(&auditinfo));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(getaudit_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(getaudit_failure);
+ATF_TC_HEAD(getaudit_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "getaudit(2) call");
+}
+
+ATF_TC_BODY(getaudit_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "getaudit.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Bad address */
+ ATF_REQUIRE_EQ(-1, getaudit(NULL));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(getaudit_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(setaudit_success);
+ATF_TC_HEAD(setaudit_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "setaudit(2) call");
+}
+
+ATF_TC_BODY(setaudit_success, tc)
+{
+ pid = getpid();
+ auditinfo_t auditinfo;
+ snprintf(adregex, sizeof(adregex), "setaudit.*%d.*return,success", pid);
+ ATF_REQUIRE_EQ(0, getaudit(&auditinfo));
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, setaudit(&auditinfo));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(setaudit_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(setaudit_failure);
+ATF_TC_HEAD(setaudit_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "setaudit(2) call");
+}
+
+ATF_TC_BODY(setaudit_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "setaudit.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Bad address */
+ ATF_REQUIRE_EQ(-1, setaudit(NULL));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(setaudit_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(getaudit_addr_success);
+ATF_TC_HEAD(getaudit_addr_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "getaudit_addr(2) call");
+}
+
+ATF_TC_BODY(getaudit_addr_success, tc)
+{
+ pid = getpid();
+ auditinfo_addr_t auditinfo;
+ snprintf(adregex, sizeof(adregex),
+ "getaudit_addr.*%d.*return,success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, getaudit_addr(&auditinfo, sizeof(auditinfo)));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(getaudit_addr_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(getaudit_addr_failure);
+ATF_TC_HEAD(getaudit_addr_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "getaudit_addr(2) call");
+}
+
+ATF_TC_BODY(getaudit_addr_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex),
+ "getaudit_addr.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Bad address */
+ ATF_REQUIRE_EQ(-1, getaudit_addr(NULL, 0));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(getaudit_addr_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(setaudit_addr_success);
+ATF_TC_HEAD(setaudit_addr_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "setaudit_addr(2) call");
+}
+
+ATF_TC_BODY(setaudit_addr_success, tc)
+{
+ pid = getpid();
+ auditinfo_addr_t auditinfo;
+ snprintf(adregex, sizeof(adregex),
+ "setaudit_addr.*%d.*return,success", pid);
+
+ ATF_REQUIRE_EQ(0, getaudit_addr(&auditinfo, sizeof(auditinfo)));
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, setaudit_addr(&auditinfo, sizeof(auditinfo)));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(setaudit_addr_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(setaudit_addr_failure);
+ATF_TC_HEAD(setaudit_addr_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "setaudit_addr(2) call");
+}
+
+ATF_TC_BODY(setaudit_addr_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex),
+ "setaudit_addr.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Bad address */
+ ATF_REQUIRE_EQ(-1, setaudit_addr(NULL, 0));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(setaudit_addr_failure, tc)
+{
+ cleanup();
+}
+
+/*
+ * Note: The test-case uses A_GETFSIZE as the command argument but since it is
+ * not an independent audit event, it will be used to check the default mode
+ * auditing of auditon(2) system call.
+ *
+ * Please See: sys/security/audit/audit_bsm_klib.c
+ * function(): au_event_t auditon_command_event() :: case A_GETFSIZE:
+ */
+ATF_TC_WITH_CLEANUP(auditon_default_success);
+ATF_TC_HEAD(auditon_default_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "auditon(2) call");
+}
+
+ATF_TC_BODY(auditon_default_success, tc)
+{
+ au_fstat_t fsize_arg;
+ bzero(&fsize_arg, sizeof(au_fstat_t));
+
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "auditon.*%d.*return,success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, auditon(A_GETFSIZE, &fsize_arg, sizeof(fsize_arg)));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditon_default_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(auditon_default_failure);
+ATF_TC_HEAD(auditon_default_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "auditon(2) call");
+}
+
+ATF_TC_BODY(auditon_default_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "auditon.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid argument */
+ ATF_REQUIRE_EQ(-1, auditon(A_GETFSIZE, NULL, 0));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditon_default_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(auditon_getpolicy_success);
+ATF_TC_HEAD(auditon_getpolicy_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "auditon(2) call for cmd: A_GETPOLICY");
+}
+
+ATF_TC_BODY(auditon_getpolicy_success, tc)
+{
+ int aupolicy;
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "GPOLICY command.*%d.*success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, auditon(A_GETPOLICY, &aupolicy, sizeof(aupolicy)));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditon_getpolicy_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(auditon_getpolicy_failure);
+ATF_TC_HEAD(auditon_getpolicy_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "auditon(2) call for cmd: A_GETPOLICY");
+}
+
+ATF_TC_BODY(auditon_getpolicy_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "GPOLICY command.*%d.*failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid argument */
+ ATF_REQUIRE_EQ(-1, auditon(A_GETPOLICY, NULL, 0));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditon_getpolicy_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(auditon_setpolicy_success);
+ATF_TC_HEAD(auditon_setpolicy_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "auditon(2) call for cmd: A_SETPOLICY");
+}
+
+ATF_TC_BODY(auditon_setpolicy_success, tc)
+{
+ int aupolicy;
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "SPOLICY command.*%d.*success", pid);
+
+ /* Retrieve the current auditing policy, to be used with A_SETPOLICY */
+ ATF_REQUIRE_EQ(0, auditon(A_GETPOLICY, &aupolicy, sizeof(aupolicy)));
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, auditon(A_SETPOLICY, &aupolicy, sizeof(aupolicy)));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditon_setpolicy_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(auditon_setpolicy_failure);
+ATF_TC_HEAD(auditon_setpolicy_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "auditon(2) call for cmd: A_SETPOLICY");
+}
+
+ATF_TC_BODY(auditon_setpolicy_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "SPOLICY command.*%d.*failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid argument */
+ ATF_REQUIRE_EQ(-1, auditon(A_SETPOLICY, NULL, 0));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditon_setpolicy_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(auditon_getkmask_success);
+ATF_TC_HEAD(auditon_getkmask_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "auditon(2) call for cmd: A_GETKMASK");
+}
+
+ATF_TC_BODY(auditon_getkmask_success, tc)
+{
+ pid = getpid();
+ au_mask_t evmask;
+ snprintf(adregex, sizeof(adregex), "get kernel mask.*%d.*success", pid);
+
+ bzero(&evmask, sizeof(evmask));
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, auditon(A_GETKMASK, &evmask, sizeof(evmask)));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditon_getkmask_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(auditon_getkmask_failure);
+ATF_TC_HEAD(auditon_getkmask_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "auditon(2) call for cmd: A_GETKMASK");
+}
+
+ATF_TC_BODY(auditon_getkmask_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "get kernel mask.*%d.*failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid au_mask_t structure */
+ ATF_REQUIRE_EQ(-1, auditon(A_GETKMASK, NULL, 0));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditon_getkmask_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(auditon_setkmask_success);
+ATF_TC_HEAD(auditon_setkmask_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "auditon(2) call for cmd: A_SETKMASK");
+}
+
+ATF_TC_BODY(auditon_setkmask_success, tc)
+{
+ pid = getpid();
+ au_mask_t evmask;
+ snprintf(adregex, sizeof(adregex), "set kernel mask.*%d.*success", pid);
+
+ /* Retrieve the current audit mask to be used with A_SETKMASK */
+ bzero(&evmask, sizeof(evmask));
+ ATF_REQUIRE_EQ(0, auditon(A_GETKMASK, &evmask, sizeof(evmask)));
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, auditon(A_SETKMASK, &evmask, sizeof(evmask)));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditon_setkmask_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(auditon_setkmask_failure);
+ATF_TC_HEAD(auditon_setkmask_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "auditon(2) call for cmd: A_SETKMASK");
+}
+
+ATF_TC_BODY(auditon_setkmask_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "set kernel mask.*%d.*failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid au_mask_t structure */
+ ATF_REQUIRE_EQ(-1, auditon(A_SETKMASK, NULL, 0));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditon_setkmask_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(auditon_getqctrl_success);
+ATF_TC_HEAD(auditon_getqctrl_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "auditon(2) call for cmd: A_GETQCTRL");
+}
+
+ATF_TC_BODY(auditon_getqctrl_success, tc)
+{
+ pid = getpid();
+ au_qctrl_t evqctrl;
+ snprintf(adregex, sizeof(adregex), "GQCTRL command.*%d.*success", pid);
+
+ bzero(&evqctrl, sizeof(evqctrl));
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, auditon(A_GETQCTRL, &evqctrl, sizeof(evqctrl)));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditon_getqctrl_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(auditon_getqctrl_failure);
+ATF_TC_HEAD(auditon_getqctrl_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "auditon(2) call for cmd: A_GETQCTRL");
+}
+
+ATF_TC_BODY(auditon_getqctrl_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "GQCTRL command.*%d.*failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid au_qctrl_t structure */
+ ATF_REQUIRE_EQ(-1, auditon(A_GETQCTRL, NULL, 0));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditon_getqctrl_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(auditon_setqctrl_success);
+ATF_TC_HEAD(auditon_setqctrl_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "auditon(2) call for cmd: A_SETKMASK");
+}
+
+ATF_TC_BODY(auditon_setqctrl_success, tc)
+{
+ pid = getpid();
+ au_qctrl_t evqctrl;
+ snprintf(adregex, sizeof(adregex), "SQCTRL command.*%d.*success", pid);
+
+ /* Retrieve the current audit mask to be used with A_SETQCTRL */
+ bzero(&evqctrl, sizeof(evqctrl));
+ ATF_REQUIRE_EQ(0, auditon(A_GETQCTRL, &evqctrl, sizeof(evqctrl)));
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, auditon(A_SETQCTRL, &evqctrl, sizeof(evqctrl)));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditon_setqctrl_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(auditon_setqctrl_failure);
+ATF_TC_HEAD(auditon_setqctrl_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "auditon(2) call for cmd: A_SETKMASK");
+}
+
+ATF_TC_BODY(auditon_setqctrl_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "SQCTRL command.*%d.*failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid au_qctrl_t structure */
+ ATF_REQUIRE_EQ(-1, auditon(A_SETQCTRL, NULL, 0));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditon_setqctrl_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(auditon_getclass_success);
+ATF_TC_HEAD(auditon_getclass_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "auditon(2) call for cmd: A_GETCLASS");
+}
+
+ATF_TC_BODY(auditon_getclass_success, tc)
+{
+ pid = getpid();
+ au_evclass_map_t evclass;
+ snprintf(adregex, sizeof(adregex), "get event class.*%d.*success", pid);
+
+ /* Initialize evclass to get the event-class mapping for auditon(2) */
+ evclass.ec_number = AUE_AUDITON;
+ evclass.ec_class = 0;
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, auditon(A_GETCLASS, &evclass, sizeof(evclass)));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditon_getclass_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(auditon_getclass_failure);
+ATF_TC_HEAD(auditon_getclass_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "auditon(2) call for cmd: A_GETCLASS");
+}
+
+ATF_TC_BODY(auditon_getclass_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "get event class.*%d.*failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid au_evclass_map_t structure */
+ ATF_REQUIRE_EQ(-1, auditon(A_GETCLASS, NULL, 0));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditon_getclass_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(auditon_setclass_success);
+ATF_TC_HEAD(auditon_setclass_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "auditon(2) call for cmd: A_SETCLASS");
+}
+
+ATF_TC_BODY(auditon_setclass_success, tc)
+{
+ pid = getpid();
+ au_evclass_map_t evclass;
+ snprintf(adregex, sizeof(adregex), "set event class.*%d.*success", pid);
+
+ /* Initialize evclass and get the event-class mapping for auditon(2) */
+ evclass.ec_number = AUE_AUDITON;
+ evclass.ec_class = 0;
+ ATF_REQUIRE_EQ(0, auditon(A_GETCLASS, &evclass, sizeof(evclass)));
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, auditon(A_SETCLASS, &evclass, sizeof(evclass)));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditon_setclass_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(auditon_setclass_failure);
+ATF_TC_HEAD(auditon_setclass_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "auditon(2) call for cmd: A_SETCLASS");
+}
+
+ATF_TC_BODY(auditon_setclass_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "set event class.*%d.*failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid au_evclass_map_t structure */
+ ATF_REQUIRE_EQ(-1, auditon(A_SETCLASS, NULL, 0));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditon_setclass_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(auditon_getcond_success);
+ATF_TC_HEAD(auditon_getcond_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "auditon(2) call for cmd: A_GETCOND");
+}
+
+ATF_TC_BODY(auditon_getcond_success, tc)
+{
+ int auditcond;
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "get audit state.*%d.*success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, auditon(A_GETCOND, &auditcond, sizeof(auditcond)));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditon_getcond_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(auditon_getcond_failure);
+ATF_TC_HEAD(auditon_getcond_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "auditon(2) call for cmd: A_GETCOND");
+}
+
+ATF_TC_BODY(auditon_getcond_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "get audit state.*%d.*failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid argument */
+ ATF_REQUIRE_EQ(-1, auditon(A_GETCOND, NULL, 0));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditon_getcond_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(auditon_setcond_success);
+ATF_TC_HEAD(auditon_setcond_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "auditon(2) call for cmd: A_SETCOND");
+}
+
+ATF_TC_BODY(auditon_setcond_success, tc)
+{
+ int auditcond = AUC_AUDITING;
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "set audit state.*%d.*success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* At this point auditd is running, so the audit state is AUC_AUDITING */
+ ATF_REQUIRE_EQ(0, auditon(A_SETCOND, &auditcond, sizeof(auditcond)));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditon_setcond_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(auditon_setcond_failure);
+ATF_TC_HEAD(auditon_setcond_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "auditon(2) call for cmd: A_SETCOND");
+}
+
+ATF_TC_BODY(auditon_setcond_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "set audit state.*%d.*failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid argument */
+ ATF_REQUIRE_EQ(-1, auditon(A_SETCOND, NULL, 0));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditon_setcond_failure, tc)
+{
+ cleanup();
+}
+
+/*
+ * Following test-cases for auditon(2) are all in failure mode only as although
+ * auditable, they have not been implemented and return ENOSYS whenever called.
+ *
+ * Commands: A_GETCWD A_GETCAR A_GETSTAT A_SETSTAT A_SETUMASK A_SETSMASK
+ */
+
+ATF_TC_WITH_CLEANUP(auditon_getcwd_failure);
+ATF_TC_HEAD(auditon_getcwd_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "auditon(2) call for cmd: A_GETCWD");
+}
+
+ATF_TC_BODY(auditon_getcwd_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "get cwd.*%d.*failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_ERRNO(ENOSYS, auditon(A_GETCWD, &auditon_def,
+ sizeof(auditon_def)) == -1);
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditon_getcwd_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(auditon_getcar_failure);
+ATF_TC_HEAD(auditon_getcar_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "auditon(2) call for cmd: A_GETCAR");
+}
+
+ATF_TC_BODY(auditon_getcar_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "get car.*%d.*failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_ERRNO(ENOSYS, auditon(A_GETCAR, &auditon_def,
+ sizeof(auditon_def)) == -1);
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditon_getcar_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(auditon_getstat_failure);
+ATF_TC_HEAD(auditon_getstat_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "auditon(2) call for cmd: A_GETSTAT");
+}
+
+ATF_TC_BODY(auditon_getstat_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex),
+ "get audit statistics.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_ERRNO(ENOSYS, auditon(A_GETSTAT, &auditon_def,
+ sizeof(auditon_def)) == -1);
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditon_getstat_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(auditon_setstat_failure);
+ATF_TC_HEAD(auditon_setstat_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "auditon(2) call for cmd: A_SETSTAT");
+}
+
+ATF_TC_BODY(auditon_setstat_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex),
+ "set audit statistics.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_ERRNO(ENOSYS, auditon(A_SETSTAT, &auditon_def,
+ sizeof(auditon_def)) == -1);
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditon_setstat_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(auditon_setumask_failure);
+ATF_TC_HEAD(auditon_setumask_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "auditon(2) call for cmd: A_SETUMASK");
+}
+
+ATF_TC_BODY(auditon_setumask_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex),
+ "set mask per uid.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_ERRNO(ENOSYS, auditon(A_SETUMASK, &auditon_def,
+ sizeof(auditon_def)) == -1);
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditon_setumask_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(auditon_setsmask_failure);
+ATF_TC_HEAD(auditon_setsmask_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "auditon(2) call for cmd: A_SETSMASK");
+}
+
+ATF_TC_BODY(auditon_setsmask_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex),
+ "set mask per session.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_ERRNO(ENOSYS, auditon(A_SETSMASK, &auditon_def,
+ sizeof(auditon_def)) == -1);
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(auditon_setsmask_failure, tc)
+{
+ cleanup();
+}
+
+
+/*
+ * Audit of reboot(2) cannot be tested in normal conditions as we don't want
+ * to reboot the system while running the tests
+ */
+
+
+ATF_TC_WITH_CLEANUP(reboot_failure);
+ATF_TC_HEAD(reboot_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "reboot(2) call");
+}
+
+ATF_TC_BODY(reboot_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "reboot.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, reboot(-1));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(reboot_failure, tc)
+{
+ cleanup();
+}
+
+
+/*
+ * Audit of quotactl(2) cannot be tested in normal conditions as we don't want
+ * to tamper with filesystem quotas
+ */
+
+
+ATF_TC_WITH_CLEANUP(quotactl_failure);
+ATF_TC_HEAD(quotactl_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "quotactl(2) call");
+}
+
+ATF_TC_BODY(quotactl_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "quotactl.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, quotactl(NULL, 0, 0, NULL));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(quotactl_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(mount_failure);
+ATF_TC_HEAD(mount_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "mount(2) call");
+}
+
+ATF_TC_BODY(mount_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "mount.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, mount(NULL, NULL, 0, NULL));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(mount_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(nmount_failure);
+ATF_TC_HEAD(nmount_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "nmount(2) call");
+}
+
+ATF_TC_BODY(nmount_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "nmount.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, nmount(NULL, 0, 0));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(nmount_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(swapon_failure);
+ATF_TC_HEAD(swapon_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "swapon(2) call");
+}
+
+ATF_TC_BODY(swapon_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "swapon.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Block device required */
+ ATF_REQUIRE_EQ(-1, swapon(path));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(swapon_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(swapoff_failure);
+ATF_TC_HEAD(swapoff_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "swapoff(2) call");
+}
+
+ATF_TC_BODY(swapoff_failure, tc)
+{
+ pid = getpid();
+ snprintf(adregex, sizeof(adregex), "swapoff.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Block device required */
+ ATF_REQUIRE_EQ(-1, swapoff(path, 0));
+ check_audit(fds, adregex, pipefd);
+}
+
+ATF_TC_CLEANUP(swapoff_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, settimeofday_success);
+ ATF_TP_ADD_TC(tp, settimeofday_failure);
+ ATF_TP_ADD_TC(tp, clock_settime_success);
+ ATF_TP_ADD_TC(tp, clock_settime_failure);
+ ATF_TP_ADD_TC(tp, adjtime_success);
+ ATF_TP_ADD_TC(tp, adjtime_failure);
+ ATF_TP_ADD_TC(tp, ntp_adjtime_success);
+ ATF_TP_ADD_TC(tp, ntp_adjtime_failure);
+
+ ATF_TP_ADD_TC(tp, nfs_getfh_success);
+ ATF_TP_ADD_TC(tp, nfs_getfh_failure);
+ ATF_TP_ADD_TC(tp, acct_success);
+ ATF_TP_ADD_TC(tp, acct_failure);
+ ATF_TP_ADD_TC(tp, auditctl_success);
+ ATF_TP_ADD_TC(tp, auditctl_failure);
+
+ ATF_TP_ADD_TC(tp, getauid_success);
+ ATF_TP_ADD_TC(tp, getauid_failure);
+ ATF_TP_ADD_TC(tp, setauid_success);
+ ATF_TP_ADD_TC(tp, setauid_failure);
+
+ ATF_TP_ADD_TC(tp, getaudit_success);
+ ATF_TP_ADD_TC(tp, getaudit_failure);
+ ATF_TP_ADD_TC(tp, setaudit_success);
+ ATF_TP_ADD_TC(tp, setaudit_failure);
+
+ ATF_TP_ADD_TC(tp, getaudit_addr_success);
+ ATF_TP_ADD_TC(tp, getaudit_addr_failure);
+ ATF_TP_ADD_TC(tp, setaudit_addr_success);
+ ATF_TP_ADD_TC(tp, setaudit_addr_failure);
+
+ ATF_TP_ADD_TC(tp, auditon_default_success);
+ ATF_TP_ADD_TC(tp, auditon_default_failure);
+
+ ATF_TP_ADD_TC(tp, auditon_getpolicy_success);
+ ATF_TP_ADD_TC(tp, auditon_getpolicy_failure);
+ ATF_TP_ADD_TC(tp, auditon_setpolicy_success);
+ ATF_TP_ADD_TC(tp, auditon_setpolicy_failure);
+
+ ATF_TP_ADD_TC(tp, auditon_getkmask_success);
+ ATF_TP_ADD_TC(tp, auditon_getkmask_failure);
+ ATF_TP_ADD_TC(tp, auditon_setkmask_success);
+ ATF_TP_ADD_TC(tp, auditon_setkmask_failure);
+
+ ATF_TP_ADD_TC(tp, auditon_getqctrl_success);
+ ATF_TP_ADD_TC(tp, auditon_getqctrl_failure);
+ ATF_TP_ADD_TC(tp, auditon_setqctrl_success);
+ ATF_TP_ADD_TC(tp, auditon_setqctrl_failure);
+
+ ATF_TP_ADD_TC(tp, auditon_getclass_success);
+ ATF_TP_ADD_TC(tp, auditon_getclass_failure);
+ ATF_TP_ADD_TC(tp, auditon_setclass_success);
+ ATF_TP_ADD_TC(tp, auditon_setclass_failure);
+
+ ATF_TP_ADD_TC(tp, auditon_getcond_success);
+ ATF_TP_ADD_TC(tp, auditon_getcond_failure);
+ ATF_TP_ADD_TC(tp, auditon_setcond_success);
+ ATF_TP_ADD_TC(tp, auditon_setcond_failure);
+
+ ATF_TP_ADD_TC(tp, auditon_getcwd_failure);
+ ATF_TP_ADD_TC(tp, auditon_getcar_failure);
+ ATF_TP_ADD_TC(tp, auditon_getstat_failure);
+ ATF_TP_ADD_TC(tp, auditon_setstat_failure);
+ ATF_TP_ADD_TC(tp, auditon_setumask_failure);
+ ATF_TP_ADD_TC(tp, auditon_setsmask_failure);
+
+ ATF_TP_ADD_TC(tp, reboot_failure);
+ ATF_TP_ADD_TC(tp, quotactl_failure);
+ ATF_TP_ADD_TC(tp, mount_failure);
+ ATF_TP_ADD_TC(tp, nmount_failure);
+ ATF_TP_ADD_TC(tp, swapon_failure);
+ ATF_TP_ADD_TC(tp, swapoff_failure);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/audit/file-attribute-access.c b/tests/sys/audit/file-attribute-access.c
new file mode 100644
index 000000000000..066689a91db1
--- /dev/null
+++ b/tests/sys/audit/file-attribute-access.c
@@ -0,0 +1,1254 @@
+/*-
+ * Copyright (c) 2018 Aniket Pandey
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/extattr.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/ucred.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "utils.h"
+
+static struct pollfd fds[1];
+static mode_t mode = 0777;
+static pid_t pid;
+static fhandle_t fht;
+static int filedesc, fhdesc;
+static char extregex[80];
+static char buff[] = "ezio";
+static struct stat statbuff;
+static struct statfs statfsbuff;
+static const char *auclass = "fa";
+static const char *name = "authorname";
+static const char *path = "fileforaudit";
+static const char *errpath = "dirdoesnotexist/fileforaudit";
+static const char *successreg = "fileforaudit.*return,success";
+static const char *failurereg = "fileforaudit.*return,failure";
+
+
+ATF_TC_WITH_CLEANUP(stat_success);
+ATF_TC_HEAD(stat_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "stat(2) call");
+}
+
+ATF_TC_BODY(stat_success, tc)
+{
+ /* File needs to exist to call stat(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, stat(path, &statbuff));
+ check_audit(fds, successreg, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(stat_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(stat_failure);
+ATF_TC_HEAD(stat_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "stat(2) call");
+}
+
+ATF_TC_BODY(stat_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT, stat(errpath, &statbuff) == -1);
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(stat_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(lstat_success);
+ATF_TC_HEAD(lstat_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "lstat(2) call");
+}
+
+ATF_TC_BODY(lstat_success, tc)
+{
+ /* Symbolic link needs to exist to call lstat(2) */
+ ATF_REQUIRE_EQ(0, symlink("symlink", path));
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, lstat(path, &statbuff));
+ check_audit(fds, successreg, pipefd);
+}
+
+ATF_TC_CLEANUP(lstat_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(lstat_failure);
+ATF_TC_HEAD(lstat_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "lstat(2) call");
+}
+
+ATF_TC_BODY(lstat_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: symbolic link does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT, lstat(errpath, &statbuff) == -1);
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(lstat_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fstat_success);
+ATF_TC_HEAD(fstat_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "fstat(2) call");
+}
+
+ATF_TC_BODY(fstat_success, tc)
+{
+ /* File needs to exist to call fstat(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT | O_RDWR, mode)) != -1);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, fstat(filedesc, &statbuff));
+
+ snprintf(extregex, sizeof(extregex),
+ "fstat.*%jd.*return,success", (intmax_t)statbuff.st_ino);
+ check_audit(fds, extregex, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(fstat_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fstat_failure);
+ATF_TC_HEAD(fstat_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "fstat(2) call");
+}
+
+ATF_TC_BODY(fstat_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ const char *regex = "fstat.*return,failure : Bad file descriptor";
+ /* Failure reason: bad file descriptor */
+ ATF_REQUIRE_ERRNO(EBADF, fstat(-1, &statbuff) == -1);
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(fstat_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fstatat_success);
+ATF_TC_HEAD(fstatat_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "fstatat(2) call");
+}
+
+ATF_TC_BODY(fstatat_success, tc)
+{
+ /* File or Symbolic link needs to exist to call lstat(2) */
+ ATF_REQUIRE_EQ(0, symlink("symlink", path));
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, fstatat(AT_FDCWD, path, &statbuff,
+ AT_SYMLINK_NOFOLLOW));
+ check_audit(fds, successreg, pipefd);
+}
+
+ATF_TC_CLEANUP(fstatat_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fstatat_failure);
+ATF_TC_HEAD(fstatat_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "fstatat(2) call");
+}
+
+ATF_TC_BODY(fstatat_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: symbolic link does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT,
+ fstatat(AT_FDCWD, path, &statbuff, AT_SYMLINK_NOFOLLOW) == -1);
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(fstatat_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(statfs_success);
+ATF_TC_HEAD(statfs_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "statfs(2) call");
+}
+
+ATF_TC_BODY(statfs_success, tc)
+{
+ /* File needs to exist to call statfs(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, statfs(path, &statfsbuff));
+ check_audit(fds, successreg, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(statfs_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(statfs_failure);
+ATF_TC_HEAD(statfs_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "statfs(2) call");
+}
+
+ATF_TC_BODY(statfs_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT, statfs(errpath, &statfsbuff) == -1);
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(statfs_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fstatfs_success);
+ATF_TC_HEAD(fstatfs_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "fstatfs(2) call");
+}
+
+ATF_TC_BODY(fstatfs_success, tc)
+{
+ /* File needs to exist to call fstat(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT | O_RDWR, mode)) != -1);
+ /* Call stat(2) to store the Inode number of 'path' */
+ ATF_REQUIRE_EQ(0, stat(path, &statbuff));
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, fstatfs(filedesc, &statfsbuff));
+
+ snprintf(extregex, sizeof(extregex), "fstatfs.*%jd.*return,success",
+ (intmax_t)statbuff.st_ino);
+ check_audit(fds, extregex, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(fstatfs_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fstatfs_failure);
+ATF_TC_HEAD(fstatfs_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "fstatfs(2) call");
+}
+
+ATF_TC_BODY(fstatfs_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ const char *regex = "fstatfs.*return,failure : Bad file descriptor";
+ /* Failure reason: bad file descriptor */
+ ATF_REQUIRE_ERRNO(EBADF, fstatfs(-1, &statfsbuff) == -1);
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(fstatfs_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(getfsstat_success);
+ATF_TC_HEAD(getfsstat_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "getfsstat(2) call");
+}
+
+ATF_TC_BODY(getfsstat_success, tc)
+{
+ pid = getpid();
+ snprintf(extregex, sizeof(extregex), "getfsstat.*%d.*success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE(getfsstat(NULL, 0, MNT_NOWAIT) != -1);
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(getfsstat_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(getfsstat_failure);
+ATF_TC_HEAD(getfsstat_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "getfsstat(2) call");
+}
+
+ATF_TC_BODY(getfsstat_failure, tc)
+{
+ const char *regex = "getfsstat.*return,failure : Invalid argument";
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid value for mode */
+ ATF_REQUIRE_ERRNO(EINVAL, getfsstat(NULL, 0, -1) == -1);
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(getfsstat_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(lgetfh_success);
+ATF_TC_HEAD(lgetfh_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "lgetfh(2) call");
+}
+
+ATF_TC_BODY(lgetfh_success, tc)
+{
+ /* Symbolic link needs to exist to get a file-handle */
+ ATF_REQUIRE_EQ(0, symlink("symlink", path));
+ const char *regex = "lgetfh.*return,success";
+ FILE *pipefd = setup(fds, "fa");
+ ATF_REQUIRE_EQ(0, lgetfh(path, &fht));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(lgetfh_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(lgetfh_failure);
+ATF_TC_HEAD(lgetfh_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "lgetfh(2) call");
+}
+
+ATF_TC_BODY(lgetfh_failure, tc)
+{
+ const char *regex = "lgetfh.*return,failure";
+ FILE *pipefd = setup(fds, "fa");
+ /* Failure reason: symbolic link does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT, lgetfh(errpath, &fht) == -1);
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(lgetfh_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fhopen_success);
+ATF_TC_HEAD(fhopen_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "fhopen(2) call");
+}
+
+ATF_TC_BODY(fhopen_success, tc)
+{
+ pid = getpid();
+ snprintf(extregex, sizeof(extregex), "fhopen.*%d.*return,success", pid);
+
+ /* File needs to exist to get a file-handle */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ /* Get the file handle to be passed to fhopen(2) */
+ ATF_REQUIRE_EQ(0, getfh(path, &fht));
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE((fhdesc = fhopen(&fht, O_RDWR)) != -1);
+ check_audit(fds, extregex, pipefd);
+
+ close(fhdesc);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(fhopen_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fhopen_failure);
+ATF_TC_HEAD(fhopen_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "fhopen(2) call");
+}
+
+ATF_TC_BODY(fhopen_failure, tc)
+{
+ const char *regex = "fhopen.*return,failure : Invalid argument";
+ FILE *pipefd = setup(fds, auclass);
+ /*
+ * Failure reason: NULL does not represent any file handle
+ * and O_CREAT is not allowed as the flag for fhopen(2)
+ */
+ ATF_REQUIRE_ERRNO(EINVAL, fhopen(NULL, O_CREAT) == -1);
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(fhopen_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fhstat_success);
+ATF_TC_HEAD(fhstat_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "fstat(2) call");
+}
+
+ATF_TC_BODY(fhstat_success, tc)
+{
+ pid = getpid();
+ snprintf(extregex, sizeof(extregex), "fhstat.*%d.*return,success", pid);
+
+ /* File needs to exist to get a file-handle */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ /* Get the file handle to be passed to fhstat(2) */
+ ATF_REQUIRE_EQ(0, getfh(path, &fht));
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, fhstat(&fht, &statbuff));
+ check_audit(fds, extregex, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(fhstat_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fhstat_failure);
+ATF_TC_HEAD(fhstat_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "fhstat(2) call");
+}
+
+ATF_TC_BODY(fhstat_failure, tc)
+{
+ const char *regex = "fhstat.*return,failure : Bad address";
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: NULL does not represent any file handle */
+ ATF_REQUIRE_ERRNO(EFAULT, fhstat(NULL, NULL) == -1);
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(fhstat_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fhstatfs_success);
+ATF_TC_HEAD(fhstatfs_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "fstatfs(2) call");
+}
+
+ATF_TC_BODY(fhstatfs_success, tc)
+{
+ pid = getpid();
+ snprintf(extregex, sizeof(extregex), "fhstatfs.*%d.*success", pid);
+
+ /* File needs to exist to get a file-handle */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ /* Get the file handle to be passed to fhstatfs(2) */
+ ATF_REQUIRE_EQ(0, getfh(path, &fht));
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, fhstatfs(&fht, &statfsbuff));
+ check_audit(fds, extregex, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(fhstatfs_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fhstatfs_failure);
+ATF_TC_HEAD(fhstatfs_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "fhstatfs(2) call");
+}
+
+ATF_TC_BODY(fhstatfs_failure, tc)
+{
+ const char *regex = "fhstatfs.*return,failure : Bad address";
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: NULL does not represent any file handle */
+ ATF_REQUIRE_ERRNO(EFAULT, fhstatfs(NULL, NULL) == -1);
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(fhstatfs_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(access_success);
+ATF_TC_HEAD(access_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "access(2) call");
+}
+
+ATF_TC_BODY(access_success, tc)
+{
+ /* File needs to exist to call access(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, access(path, F_OK));
+ check_audit(fds, successreg, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(access_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(access_failure);
+ATF_TC_HEAD(access_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "access(2) call");
+}
+
+ATF_TC_BODY(access_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT, access(errpath, F_OK) == -1);
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(access_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(eaccess_success);
+ATF_TC_HEAD(eaccess_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "eaccess(2) call");
+}
+
+ATF_TC_BODY(eaccess_success, tc)
+{
+ /* File needs to exist to call eaccess(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, eaccess(path, F_OK));
+ check_audit(fds, successreg, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(eaccess_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(eaccess_failure);
+ATF_TC_HEAD(eaccess_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "eaccess(2) call");
+}
+
+ATF_TC_BODY(eaccess_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT, eaccess(errpath, F_OK) == -1);
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(eaccess_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(faccessat_success);
+ATF_TC_HEAD(faccessat_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "faccessat(2) call");
+}
+
+ATF_TC_BODY(faccessat_success, tc)
+{
+ /* File needs to exist to call faccessat(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, faccessat(AT_FDCWD, path, F_OK, AT_EACCESS));
+ check_audit(fds, successreg, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(faccessat_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(faccessat_failure);
+ATF_TC_HEAD(faccessat_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "faccessat(2) call");
+}
+
+ATF_TC_BODY(faccessat_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT,
+ faccessat(AT_FDCWD, errpath, F_OK, AT_EACCESS) == -1);
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(faccessat_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(pathconf_success);
+ATF_TC_HEAD(pathconf_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "pathconf(2) call");
+}
+
+ATF_TC_BODY(pathconf_success, tc)
+{
+ /* File needs to exist to call pathconf(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, auclass);
+ /* Get the maximum number of bytes of filename */
+ ATF_REQUIRE(pathconf(path, _PC_NAME_MAX) != -1);
+ check_audit(fds, successreg, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(pathconf_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(pathconf_failure);
+ATF_TC_HEAD(pathconf_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "pathconf(2) call");
+}
+
+ATF_TC_BODY(pathconf_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT, pathconf(errpath, _PC_NAME_MAX) == -1);
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(pathconf_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(lpathconf_success);
+ATF_TC_HEAD(lpathconf_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "lpathconf(2) call");
+}
+
+ATF_TC_BODY(lpathconf_success, tc)
+{
+ /* Symbolic link needs to exist to call lpathconf(2) */
+ ATF_REQUIRE_EQ(0, symlink("symlink", path));
+ FILE *pipefd = setup(fds, auclass);
+ /* Get the maximum number of bytes of symlink's name */
+ ATF_REQUIRE(lpathconf(path, _PC_SYMLINK_MAX) != -1);
+ check_audit(fds, successreg, pipefd);
+}
+
+ATF_TC_CLEANUP(lpathconf_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(lpathconf_failure);
+ATF_TC_HEAD(lpathconf_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "lpathconf(2) call");
+}
+
+ATF_TC_BODY(lpathconf_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: symbolic link does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT, lpathconf(errpath, _PC_SYMLINK_MAX) == -1);
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(lpathconf_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fpathconf_success);
+ATF_TC_HEAD(fpathconf_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "fpathconf(2) call");
+}
+
+ATF_TC_BODY(fpathconf_success, tc)
+{
+ pid = getpid();
+ snprintf(extregex, sizeof(extregex), "fpathconf.*%d.*success", pid);
+
+ /* File needs to exist to call fpathconf(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, auclass);
+ /* Get the maximum number of bytes of filename */
+ ATF_REQUIRE(fpathconf(filedesc, _PC_NAME_MAX) != -1);
+ check_audit(fds, extregex, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(fpathconf_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fpathconf_failure);
+ATF_TC_HEAD(fpathconf_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "fpathconf(2) call");
+}
+
+ATF_TC_BODY(fpathconf_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ const char *regex = "fpathconf.*return,failure : Bad file descriptor";
+ /* Failure reason: Bad file descriptor */
+ ATF_REQUIRE_ERRNO(EBADF, fpathconf(-1, _PC_NAME_MAX) == -1);
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(fpathconf_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(extattr_get_file_success);
+ATF_TC_HEAD(extattr_get_file_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "extattr_get_file(2) call");
+}
+
+ATF_TC_BODY(extattr_get_file_success, tc)
+{
+ /* File needs to exist to call extattr_get_file(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ skip_if_extattr_not_supported(path);
+
+ /* Set an extended attribute to be retrieved later on */
+ REQUIRE_EXTATTR_RESULT(sizeof(buff),
+ extattr_set_file(path, EXTATTR_NAMESPACE_USER, name, buff,
+ sizeof(buff)));
+
+ /* Prepare the regex to be checked in the audit record */
+ snprintf(extregex, sizeof(extregex),
+ "extattr_get_file.*%s.*%s.*return,success", path, name);
+
+ FILE *pipefd = setup(fds, auclass);
+ REQUIRE_EXTATTR_RESULT(sizeof(buff),
+ extattr_get_file(path, EXTATTR_NAMESPACE_USER, name, NULL, 0));
+ check_audit(fds, extregex, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(extattr_get_file_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(extattr_get_file_failure);
+ATF_TC_HEAD(extattr_get_file_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "extattr_get_file(2) call");
+}
+
+ATF_TC_BODY(extattr_get_file_failure, tc)
+{
+ /* Prepare the regex to be checked in the audit record */
+ snprintf(extregex, sizeof(extregex),
+ "extattr_get_file.*%s.*%s.*failure", path, name);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT,
+ extattr_get_file(path, EXTATTR_NAMESPACE_USER, name, NULL, 0) ==
+ -1);
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(extattr_get_file_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(extattr_get_fd_success);
+ATF_TC_HEAD(extattr_get_fd_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "extattr_get_fd(2) call");
+}
+
+ATF_TC_BODY(extattr_get_fd_success, tc)
+{
+ /* File needs to exist to call extattr_get_fd(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ skip_if_extattr_not_supported(path);
+
+ /* Set an extended attribute to be retrieved later on */
+ REQUIRE_EXTATTR_RESULT(sizeof(buff),
+ extattr_set_file(path, EXTATTR_NAMESPACE_USER, name, buff,
+ sizeof(buff)));
+
+ /* Prepare the regex to be checked in the audit record */
+ snprintf(extregex, sizeof(extregex),
+ "extattr_get_fd.*%s.*return,success", name);
+
+ FILE *pipefd = setup(fds, auclass);
+ REQUIRE_EXTATTR_RESULT(sizeof(buff),
+ extattr_get_fd(filedesc, EXTATTR_NAMESPACE_USER, name, NULL, 0));
+ check_audit(fds, extregex, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(extattr_get_fd_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(extattr_get_fd_failure);
+ATF_TC_HEAD(extattr_get_fd_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "extattr_get_fd(2) call");
+}
+
+ATF_TC_BODY(extattr_get_fd_failure, tc)
+{
+ /* Prepare the regex to be checked in the audit record */
+ snprintf(extregex, sizeof(extregex),
+ "extattr_get_fd.*%s.*return,failure : Bad file descriptor", name);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid file descriptor */
+ ATF_REQUIRE_ERRNO(EBADF,
+ extattr_get_fd(-1, EXTATTR_NAMESPACE_USER, name, NULL, 0) == -1);
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(extattr_get_fd_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(extattr_get_link_success);
+ATF_TC_HEAD(extattr_get_link_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "extattr_get_link(2) call");
+}
+
+ATF_TC_BODY(extattr_get_link_success, tc)
+{
+ /* Symbolic link needs to exist to call extattr_get_link(2) */
+ ATF_REQUIRE_EQ(0, symlink("symlink", path));
+ skip_if_extattr_not_supported(".");
+
+ /* Set an extended attribute to be retrieved later on */
+ REQUIRE_EXTATTR_RESULT(sizeof(buff),
+ extattr_set_link(path, EXTATTR_NAMESPACE_USER, name, buff,
+ sizeof(buff)));
+
+ /* Prepare the regex to be checked in the audit record */
+ snprintf(extregex, sizeof(extregex),
+ "extattr_get_link.*%s.*%s.*return,success", path, name);
+
+ FILE *pipefd = setup(fds, auclass);
+ REQUIRE_EXTATTR_RESULT(sizeof(buff),
+ extattr_get_link(path, EXTATTR_NAMESPACE_USER, name, NULL, 0));
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(extattr_get_link_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(extattr_get_link_failure);
+ATF_TC_HEAD(extattr_get_link_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "extattr_get_link(2) call");
+}
+
+ATF_TC_BODY(extattr_get_link_failure, tc)
+{
+ /* Prepare the regex to be checked in the audit record */
+ snprintf(extregex, sizeof(extregex),
+ "extattr_get_link.*%s.*%s.*failure", path, name);
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: symbolic link does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT,
+ extattr_get_link(path, EXTATTR_NAMESPACE_USER, name, NULL, 0));
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(extattr_get_link_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(extattr_list_file_success);
+ATF_TC_HEAD(extattr_list_file_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "extattr_list_file(2) call");
+}
+
+ATF_TC_BODY(extattr_list_file_success, tc)
+{
+ ssize_t readbuff;
+ /* File needs to exist to call extattr_list_file(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ skip_if_extattr_not_supported(path);
+
+ FILE *pipefd = setup(fds, auclass);
+ readbuff = REQUIRE_EXTATTR_SUCCESS(
+ extattr_list_file(path, EXTATTR_NAMESPACE_USER, NULL, 0));
+ /* Prepare the regex to be checked in the audit record */
+ snprintf(extregex, sizeof(extregex),
+ "extattr_list_file.*%s.*return,success,%zd", path, readbuff);
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(extattr_list_file_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(extattr_list_file_failure);
+ATF_TC_HEAD(extattr_list_file_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "extattr_list_file(2) call");
+}
+
+ATF_TC_BODY(extattr_list_file_failure, tc)
+{
+ /* Prepare the regex to be checked in the audit record */
+ snprintf(extregex, sizeof(extregex),
+ "extattr_list_file.*%s.*return,failure", path);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT,
+ extattr_list_file(path, EXTATTR_NAMESPACE_USER, NULL, 0));
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(extattr_list_file_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(extattr_list_fd_success);
+ATF_TC_HEAD(extattr_list_fd_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "extattr_list_fd(2) call");
+}
+
+ATF_TC_BODY(extattr_list_fd_success, tc)
+{
+ ssize_t readbuff;
+ /* File needs to exist to call extattr_list_fd(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ skip_if_extattr_not_supported(path);
+
+ FILE *pipefd = setup(fds, auclass);
+ readbuff = REQUIRE_EXTATTR_SUCCESS(
+ extattr_list_fd(filedesc, EXTATTR_NAMESPACE_USER, NULL, 0));
+ /* Prepare the regex to be checked in the audit record */
+ snprintf(extregex, sizeof(extregex),
+ "extattr_list_fd.*return,success,%zd", readbuff);
+ check_audit(fds, extregex, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(extattr_list_fd_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(extattr_list_fd_failure);
+ATF_TC_HEAD(extattr_list_fd_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "extattr_list_fd(2) call");
+}
+
+ATF_TC_BODY(extattr_list_fd_failure, tc)
+{
+ /* Prepare the regex to be checked in the audit record */
+ snprintf(extregex, sizeof(extregex),
+ "extattr_list_fd.*return,failure : Bad file descriptor");
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid file descriptor */
+ ATF_REQUIRE_ERRNO(EBADF,
+ extattr_list_fd(-1, EXTATTR_NAMESPACE_USER, NULL, 0) == -1);
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(extattr_list_fd_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(extattr_list_link_success);
+ATF_TC_HEAD(extattr_list_link_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "extattr_list_link(2) call");
+}
+
+ATF_TC_BODY(extattr_list_link_success, tc)
+{
+ ssize_t readbuff;
+ /* Symbolic link needs to exist to call extattr_list_link(2) */
+ ATF_REQUIRE_EQ(0, symlink("symlink", path));
+ skip_if_extattr_not_supported(".");
+
+ FILE *pipefd = setup(fds, auclass);
+ readbuff = REQUIRE_EXTATTR_SUCCESS(
+ extattr_list_link(path, EXTATTR_NAMESPACE_USER, NULL, 0));
+ /* Prepare the regex to be checked in the audit record */
+ snprintf(extregex, sizeof(extregex),
+ "extattr_list_link.*%s.*return,success,%zd", path, readbuff);
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(extattr_list_link_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(extattr_list_link_failure);
+ATF_TC_HEAD(extattr_list_link_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "extattr_list_link(2) call");
+}
+
+ATF_TC_BODY(extattr_list_link_failure, tc)
+{
+ /* Prepare the regex to be checked in the audit record */
+ snprintf(extregex, sizeof(extregex),
+ "extattr_list_link.*%s.*failure", path);
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: symbolic link does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT,
+ extattr_list_link(path, EXTATTR_NAMESPACE_USER, NULL, 0) == -1);
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(extattr_list_link_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, stat_success);
+ ATF_TP_ADD_TC(tp, stat_failure);
+ ATF_TP_ADD_TC(tp, lstat_success);
+ ATF_TP_ADD_TC(tp, lstat_failure);
+ ATF_TP_ADD_TC(tp, fstat_success);
+ ATF_TP_ADD_TC(tp, fstat_failure);
+ ATF_TP_ADD_TC(tp, fstatat_success);
+ ATF_TP_ADD_TC(tp, fstatat_failure);
+
+ ATF_TP_ADD_TC(tp, statfs_success);
+ ATF_TP_ADD_TC(tp, statfs_failure);
+ ATF_TP_ADD_TC(tp, fstatfs_success);
+ ATF_TP_ADD_TC(tp, fstatfs_failure);
+
+ ATF_TP_ADD_TC(tp, getfsstat_success);
+ ATF_TP_ADD_TC(tp, getfsstat_failure);
+
+ ATF_TP_ADD_TC(tp, lgetfh_success);
+ ATF_TP_ADD_TC(tp, lgetfh_failure);
+ ATF_TP_ADD_TC(tp, fhopen_success);
+ ATF_TP_ADD_TC(tp, fhopen_failure);
+ ATF_TP_ADD_TC(tp, fhstat_success);
+ ATF_TP_ADD_TC(tp, fhstat_failure);
+ ATF_TP_ADD_TC(tp, fhstatfs_success);
+ ATF_TP_ADD_TC(tp, fhstatfs_failure);
+
+ ATF_TP_ADD_TC(tp, access_success);
+ ATF_TP_ADD_TC(tp, access_failure);
+ ATF_TP_ADD_TC(tp, eaccess_success);
+ ATF_TP_ADD_TC(tp, eaccess_failure);
+ ATF_TP_ADD_TC(tp, faccessat_success);
+ ATF_TP_ADD_TC(tp, faccessat_failure);
+
+ ATF_TP_ADD_TC(tp, pathconf_success);
+ ATF_TP_ADD_TC(tp, pathconf_failure);
+ ATF_TP_ADD_TC(tp, lpathconf_success);
+ ATF_TP_ADD_TC(tp, lpathconf_failure);
+ ATF_TP_ADD_TC(tp, fpathconf_success);
+ ATF_TP_ADD_TC(tp, fpathconf_failure);
+
+ ATF_TP_ADD_TC(tp, extattr_get_file_success);
+ ATF_TP_ADD_TC(tp, extattr_get_file_failure);
+ ATF_TP_ADD_TC(tp, extattr_get_fd_success);
+ ATF_TP_ADD_TC(tp, extattr_get_fd_failure);
+ ATF_TP_ADD_TC(tp, extattr_get_link_success);
+ ATF_TP_ADD_TC(tp, extattr_get_link_failure);
+
+ ATF_TP_ADD_TC(tp, extattr_list_file_success);
+ ATF_TP_ADD_TC(tp, extattr_list_file_failure);
+ ATF_TP_ADD_TC(tp, extattr_list_fd_success);
+ ATF_TP_ADD_TC(tp, extattr_list_fd_failure);
+ ATF_TP_ADD_TC(tp, extattr_list_link_success);
+ ATF_TP_ADD_TC(tp, extattr_list_link_failure);
+ return (atf_no_error());
+}
diff --git a/tests/sys/audit/file-attribute-modify.c b/tests/sys/audit/file-attribute-modify.c
new file mode 100644
index 000000000000..23e06fe99f07
--- /dev/null
+++ b/tests/sys/audit/file-attribute-modify.c
@@ -0,0 +1,1397 @@
+/*-
+ * Copyright (c) 2018 Aniket Pandey
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/extattr.h>
+#include <sys/file.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include "utils.h"
+
+static pid_t pid;
+static uid_t uid = -1;
+static gid_t gid = -1;
+static int filedesc, retval;
+static struct pollfd fds[1];
+static mode_t mode = 0777;
+static char extregex[80];
+static char buff[] = "ezio";
+static const char *auclass = "fm";
+static const char *name = "authorname";
+static const char *path = "fileforaudit";
+static const char *errpath = "adirhasnoname/fileforaudit";
+static const char *successreg = "fileforaudit.*return,success";
+static const char *failurereg = "fileforaudit.*return,failure";
+
+
+ATF_TC_WITH_CLEANUP(flock_success);
+ATF_TC_HEAD(flock_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "flock(2) call");
+}
+
+ATF_TC_BODY(flock_success, tc)
+{
+ pid = getpid();
+ snprintf(extregex, sizeof(extregex), "flock.*%d.*return,success", pid);
+
+ /* File needs to exist to call flock(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, flock(filedesc, LOCK_SH));
+ check_audit(fds, extregex, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(flock_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(flock_failure);
+ATF_TC_HEAD(flock_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "flock(2) call");
+}
+
+ATF_TC_BODY(flock_failure, tc)
+{
+ const char *regex = "flock.*return,failure : Bad file descriptor";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_ERRNO(EBADF, flock(-1, LOCK_SH) == -1);
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(flock_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fcntl_success);
+ATF_TC_HEAD(fcntl_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "fcntl(2) call");
+}
+
+ATF_TC_BODY(fcntl_success, tc)
+{
+ int flagstatus;
+ /* File needs to exist to call fcntl(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, auclass);
+
+ /* Retrieve the status flags of 'filedesc' and store it in flagstatus */
+ ATF_REQUIRE((flagstatus = fcntl(filedesc, F_GETFL, 0)) != -1);
+ snprintf(extregex, sizeof(extregex),
+ "fcntl.*return,success,%d", flagstatus);
+ check_audit(fds, extregex, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(fcntl_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fcntl_failure);
+ATF_TC_HEAD(fcntl_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "fcntl(2) call");
+}
+
+ATF_TC_BODY(fcntl_failure, tc)
+{
+ const char *regex = "fcntl.*return,failure : Bad file descriptor";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_ERRNO(EBADF, fcntl(-1, F_GETFL, 0) == -1);
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(fcntl_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fsync_success);
+ATF_TC_HEAD(fsync_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "fsync(2) call");
+}
+
+ATF_TC_BODY(fsync_success, tc)
+{
+ pid = getpid();
+ snprintf(extregex, sizeof(extregex), "fsync.*%d.*return,success", pid);
+
+ /* File needs to exist to call fsync(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, fsync(filedesc));
+ check_audit(fds, extregex, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(fsync_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fsync_failure);
+ATF_TC_HEAD(fsync_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "fsync(2) call");
+}
+
+ATF_TC_BODY(fsync_failure, tc)
+{
+ const char *regex = "fsync.*return,failure : Bad file descriptor";
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid file descriptor */
+ ATF_REQUIRE_ERRNO(EBADF, fsync(-1) == -1);
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(fsync_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(chmod_success);
+ATF_TC_HEAD(chmod_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "chmod(2) call");
+}
+
+ATF_TC_BODY(chmod_success, tc)
+{
+ /* File needs to exist to call chmod(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, chmod(path, mode));
+ check_audit(fds, successreg, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(chmod_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(chmod_failure);
+ATF_TC_HEAD(chmod_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "chmod(2) call");
+}
+
+ATF_TC_BODY(chmod_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT, chmod(errpath, mode) == -1);
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(chmod_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fchmod_success);
+ATF_TC_HEAD(fchmod_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "fchmod(2) call");
+}
+
+ATF_TC_BODY(fchmod_success, tc)
+{
+ pid = getpid();
+ snprintf(extregex, sizeof(extregex), "fchmod.*%d.*return,success", pid);
+
+ /* File needs to exist to call fchmod(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, fchmod(filedesc, mode));
+ check_audit(fds, extregex, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(fchmod_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fchmod_failure);
+ATF_TC_HEAD(fchmod_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "fchmod(2) call");
+}
+
+ATF_TC_BODY(fchmod_failure, tc)
+{
+ const char *regex = "fchmod.*return,failure : Bad file descriptor";
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid file descriptor */
+ ATF_REQUIRE_ERRNO(EBADF, fchmod(-1, mode) == -1);
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(fchmod_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(lchmod_success);
+ATF_TC_HEAD(lchmod_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "lchmod(2) call");
+}
+
+ATF_TC_BODY(lchmod_success, tc)
+{
+ /* Symbolic link needs to exist to call lchmod(2) */
+ ATF_REQUIRE_EQ(0, symlink("symlink", path));
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, lchmod(path, mode));
+ check_audit(fds, successreg, pipefd);
+}
+
+ATF_TC_CLEANUP(lchmod_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(lchmod_failure);
+ATF_TC_HEAD(lchmod_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "lchmod(2) call");
+}
+
+ATF_TC_BODY(lchmod_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT, lchmod(errpath, mode) == -1);
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(lchmod_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fchmodat_success);
+ATF_TC_HEAD(fchmodat_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "fchmodat(2) call");
+}
+
+ATF_TC_BODY(fchmodat_success, tc)
+{
+ /* File needs to exist to call fchmodat(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, fchmodat(AT_FDCWD, path, mode, 0));
+ check_audit(fds, successreg, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(fchmodat_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fchmodat_failure);
+ATF_TC_HEAD(fchmodat_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "fchmodat(2) call");
+}
+
+ATF_TC_BODY(fchmodat_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT, fchmodat(AT_FDCWD, errpath, mode, 0) == -1);
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(fchmodat_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(chown_success);
+ATF_TC_HEAD(chown_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "chown(2) call");
+}
+
+ATF_TC_BODY(chown_success, tc)
+{
+ /* File needs to exist to call chown(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, chown(path, uid, gid));
+ check_audit(fds, successreg, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(chown_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(chown_failure);
+ATF_TC_HEAD(chown_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "chown(2) call");
+}
+
+ATF_TC_BODY(chown_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT, chown(errpath, uid, gid) == -1);
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(chown_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fchown_success);
+ATF_TC_HEAD(fchown_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "fchown(2) call");
+}
+
+ATF_TC_BODY(fchown_success, tc)
+{
+ pid = getpid();
+ snprintf(extregex, sizeof(extregex), "fchown.*%d.*return,success", pid);
+
+ /* File needs to exist to call fchown(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, fchown(filedesc, uid, gid));
+ check_audit(fds, extregex, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(fchown_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fchown_failure);
+ATF_TC_HEAD(fchown_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "fchown(2) call");
+}
+
+ATF_TC_BODY(fchown_failure, tc)
+{
+ const char *regex = "fchown.*return,failure : Bad file descriptor";
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid file descriptor */
+ ATF_REQUIRE_ERRNO(EBADF, fchown(-1, uid, gid) == -1);
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(fchown_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(lchown_success);
+ATF_TC_HEAD(lchown_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "lchown(2) call");
+}
+
+ATF_TC_BODY(lchown_success, tc)
+{
+ /* Symbolic link needs to exist to call lchown(2) */
+ ATF_REQUIRE_EQ(0, symlink("symlink", path));
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, lchown(path, uid, gid));
+ check_audit(fds, successreg, pipefd);
+}
+
+ATF_TC_CLEANUP(lchown_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(lchown_failure);
+ATF_TC_HEAD(lchown_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "lchown(2) call");
+}
+
+ATF_TC_BODY(lchown_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Symbolic link does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT, lchown(errpath, uid, gid) == -1);
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(lchown_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fchownat_success);
+ATF_TC_HEAD(fchownat_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "fchownat(2) call");
+}
+
+ATF_TC_BODY(fchownat_success, tc)
+{
+ /* File needs to exist to call fchownat(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, fchownat(AT_FDCWD, path, uid, gid, 0));
+ check_audit(fds, successreg, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(fchownat_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fchownat_failure);
+ATF_TC_HEAD(fchownat_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "fchownat(2) call");
+}
+
+ATF_TC_BODY(fchownat_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT,
+ fchownat(AT_FDCWD, errpath, uid, gid, 0) == -1);
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(fchownat_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(chflags_success);
+ATF_TC_HEAD(chflags_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "chflags(2) call");
+}
+
+ATF_TC_BODY(chflags_success, tc)
+{
+ /* File needs to exist to call chflags(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, chflags(path, UF_OFFLINE));
+ check_audit(fds, successreg, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(chflags_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(chflags_failure);
+ATF_TC_HEAD(chflags_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "chflags(2) call");
+}
+
+ATF_TC_BODY(chflags_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT, chflags(errpath, UF_OFFLINE) == -1);
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(chflags_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fchflags_success);
+ATF_TC_HEAD(fchflags_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "fchflags(2) call");
+}
+
+ATF_TC_BODY(fchflags_success, tc)
+{
+ pid = getpid();
+ snprintf(extregex, sizeof(extregex), "fchflags.*%d.*ret.*success", pid);
+ /* File needs to exist to call fchflags(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, fchflags(filedesc, UF_OFFLINE));
+ check_audit(fds, extregex, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(fchflags_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fchflags_failure);
+ATF_TC_HEAD(fchflags_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "fchflags(2) call");
+}
+
+ATF_TC_BODY(fchflags_failure, tc)
+{
+ const char *regex = "fchflags.*return,failure : Bad file descriptor";
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid file descriptor */
+ ATF_REQUIRE_ERRNO(EBADF, fchflags(-1, UF_OFFLINE) == -1);
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(fchflags_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(lchflags_success);
+ATF_TC_HEAD(lchflags_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "lchflags(2) call");
+}
+
+ATF_TC_BODY(lchflags_success, tc)
+{
+ /* Symbolic link needs to exist to call lchflags(2) */
+ ATF_REQUIRE_EQ(0, symlink("symlink", path));
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, lchflags(path, UF_OFFLINE));
+ check_audit(fds, successreg, pipefd);
+}
+
+ATF_TC_CLEANUP(lchflags_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(lchflags_failure);
+ATF_TC_HEAD(lchflags_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "lchflags(2) call");
+}
+
+ATF_TC_BODY(lchflags_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Symbolic link does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT, lchflags(errpath, UF_OFFLINE) == -1);
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(lchflags_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(chflagsat_success);
+ATF_TC_HEAD(chflagsat_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "chflagsat(2) call");
+}
+
+ATF_TC_BODY(chflagsat_success, tc)
+{
+ /* File needs to exist to call chflagsat(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, chflagsat(AT_FDCWD, path, UF_OFFLINE, 0));
+ check_audit(fds, successreg, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(chflagsat_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(chflagsat_failure);
+ATF_TC_HEAD(chflagsat_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "chflagsat(2) call");
+}
+
+ATF_TC_BODY(chflagsat_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT,
+ chflagsat(AT_FDCWD, errpath, UF_OFFLINE, 0) == -1);
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(chflagsat_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(utimes_success);
+ATF_TC_HEAD(utimes_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "utimes(2) call");
+}
+
+ATF_TC_BODY(utimes_success, tc)
+{
+ /* File needs to exist to call utimes(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, utimes(path, NULL));
+ check_audit(fds, successreg, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(utimes_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(utimes_failure);
+ATF_TC_HEAD(utimes_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "utimes(2) call");
+}
+
+ATF_TC_BODY(utimes_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT, utimes(errpath, NULL) == -1);
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(utimes_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(futimes_success);
+ATF_TC_HEAD(futimes_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "futimes(2) call");
+}
+
+ATF_TC_BODY(futimes_success, tc)
+{
+ pid = getpid();
+ snprintf(extregex, sizeof(extregex), "futimes.*%d.*ret.*success", pid);
+
+ /* File needs to exist to call futimes(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, futimes(filedesc, NULL));
+ check_audit(fds, extregex, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(futimes_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(futimes_failure);
+ATF_TC_HEAD(futimes_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "futimes(2) call");
+}
+
+ATF_TC_BODY(futimes_failure, tc)
+{
+ const char *regex = "futimes.*return,failure : Bad file descriptor";
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid file descriptor */
+ ATF_REQUIRE_ERRNO(EBADF, futimes(-1, NULL) == -1);
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(futimes_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(lutimes_success);
+ATF_TC_HEAD(lutimes_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "lutimes(2) call");
+}
+
+ATF_TC_BODY(lutimes_success, tc)
+{
+ /* Symbolic link needs to exist to call lutimes(2) */
+ ATF_REQUIRE_EQ(0, symlink("symlink", path));
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, lutimes(path, NULL));
+ check_audit(fds, successreg, pipefd);
+}
+
+ATF_TC_CLEANUP(lutimes_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(lutimes_failure);
+ATF_TC_HEAD(lutimes_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "lutimes(2) call");
+}
+
+ATF_TC_BODY(lutimes_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: symbolic link does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT, lutimes(errpath, NULL) == -1);
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(lutimes_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(futimesat_success);
+ATF_TC_HEAD(futimesat_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "futimesat(2) call");
+}
+
+ATF_TC_BODY(futimesat_success, tc)
+{
+ /* File needs to exist to call futimesat(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, futimesat(AT_FDCWD, path, NULL));
+ check_audit(fds, successreg, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(futimesat_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(futimesat_failure);
+ATF_TC_HEAD(futimesat_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "futimesat(2) call");
+}
+
+ATF_TC_BODY(futimesat_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT, futimesat(AT_FDCWD, errpath, NULL) == -1);
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(futimesat_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(mprotect_success);
+ATF_TC_HEAD(mprotect_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "mprotect(2) call");
+}
+
+ATF_TC_BODY(mprotect_success, tc)
+{
+ pid = getpid();
+ snprintf(extregex, sizeof(extregex), "mprotect.*%d.*ret.*success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, mprotect(NULL, 0, PROT_NONE));
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(mprotect_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(mprotect_failure);
+ATF_TC_HEAD(mprotect_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "mprotect(2) call");
+}
+
+ATF_TC_BODY(mprotect_failure, tc)
+{
+ const char *regex = "mprotect.*return,failure : Invalid argument";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_ERRNO(EINVAL,
+ mprotect((void *)SIZE_MAX, -1, PROT_NONE) == -1);
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(mprotect_failure, tc)
+{
+ cleanup();
+}
+
+/*
+ * undelete(2) only works on whiteout files in union file system. Hence, no
+ * test case for successful invocation.
+ */
+
+ATF_TC_WITH_CLEANUP(undelete_failure);
+ATF_TC_HEAD(undelete_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "undelete(2) call");
+}
+
+ATF_TC_BODY(undelete_failure, tc)
+{
+ pid = getpid();
+ snprintf(extregex, sizeof(extregex), "undelete.*%d.*ret.*failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: File does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT, undelete(errpath) == -1);
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(undelete_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(extattr_set_file_success);
+ATF_TC_HEAD(extattr_set_file_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "extattr_set_file(2) call");
+}
+
+ATF_TC_BODY(extattr_set_file_success, tc)
+{
+ /* File needs to exist to call extattr_set_file(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ skip_if_extattr_not_supported(path);
+
+ /* Prepare the regex to be checked in the audit record */
+ snprintf(extregex, sizeof(extregex),
+ "extattr_set_file.*%s.*%s.*return,success", path, name);
+
+ FILE *pipefd = setup(fds, auclass);
+ REQUIRE_EXTATTR_RESULT(sizeof(buff), extattr_set_file(path,
+ EXTATTR_NAMESPACE_USER, name, buff, sizeof(buff)));
+ check_audit(fds, extregex, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(extattr_set_file_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(extattr_set_file_failure);
+ATF_TC_HEAD(extattr_set_file_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "extattr_set_file(2) call");
+}
+
+ATF_TC_BODY(extattr_set_file_failure, tc)
+{
+ /* Prepare the regex to be checked in the audit record */
+ snprintf(extregex, sizeof(extregex),
+ "extattr_set_file.*%s.*%s.*failure", path, name);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT,
+ extattr_set_file(path, EXTATTR_NAMESPACE_USER, name, NULL, 0) ==
+ -1);
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(extattr_set_file_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(extattr_set_fd_success);
+ATF_TC_HEAD(extattr_set_fd_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "extattr_set_fd(2) call");
+}
+
+ATF_TC_BODY(extattr_set_fd_success, tc)
+{
+ /* File needs to exist to call extattr_set_fd(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ skip_if_extattr_not_supported(path);
+
+ /* Prepare the regex to be checked in the audit record */
+ snprintf(extregex, sizeof(extregex),
+ "extattr_set_fd.*%s.*return,success", name);
+
+ FILE *pipefd = setup(fds, auclass);
+ REQUIRE_EXTATTR_RESULT(sizeof(buff), extattr_set_fd(filedesc,
+ EXTATTR_NAMESPACE_USER, name, buff, sizeof(buff)));
+ check_audit(fds, extregex, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(extattr_set_fd_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(extattr_set_fd_failure);
+ATF_TC_HEAD(extattr_set_fd_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "extattr_set_fd(2) call");
+}
+
+ATF_TC_BODY(extattr_set_fd_failure, tc)
+{
+ /* Prepare the regex to be checked in the audit record */
+ snprintf(extregex, sizeof(extregex),
+ "extattr_set_fd.*%s.*return,failure : Bad file descriptor", name);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid file descriptor */
+ ATF_REQUIRE_ERRNO(EBADF,
+ extattr_set_fd(-1, EXTATTR_NAMESPACE_USER, name, NULL, 0) == -1);
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(extattr_set_fd_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(extattr_set_link_success);
+ATF_TC_HEAD(extattr_set_link_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "extattr_set_link(2) call");
+}
+
+ATF_TC_BODY(extattr_set_link_success, tc)
+{
+ /* Symbolic link needs to exist to call extattr_set_link(2) */
+ ATF_REQUIRE_EQ(0, symlink("symlink", path));
+ skip_if_extattr_not_supported(".");
+
+ /* Prepare the regex to be checked in the audit record */
+ snprintf(extregex, sizeof(extregex),
+ "extattr_set_link.*%s.*%s.*return,success", path, name);
+
+ FILE *pipefd = setup(fds, auclass);
+ REQUIRE_EXTATTR_RESULT(sizeof(buff), extattr_set_link(path,
+ EXTATTR_NAMESPACE_USER, name, buff, sizeof(buff)));
+
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(extattr_set_link_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(extattr_set_link_failure);
+ATF_TC_HEAD(extattr_set_link_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "extattr_set_link(2) call");
+}
+
+ATF_TC_BODY(extattr_set_link_failure, tc)
+{
+ /* Prepare the regex to be checked in the audit record */
+ snprintf(extregex, sizeof(extregex),
+ "extattr_set_link.*%s.*%s.*failure", path, name);
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: symbolic link does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT,
+ extattr_set_link(path, EXTATTR_NAMESPACE_USER, name, NULL, 0) ==
+ -1);
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(extattr_set_link_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(extattr_delete_file_success);
+ATF_TC_HEAD(extattr_delete_file_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "extattr_delete_file(2) call");
+}
+
+ATF_TC_BODY(extattr_delete_file_success, tc)
+{
+ /* File needs to exist to call extattr_delete_file(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ skip_if_extattr_not_supported(path);
+
+ REQUIRE_EXTATTR_RESULT(sizeof(buff), extattr_set_file(path,
+ EXTATTR_NAMESPACE_USER, name, buff, sizeof(buff)));
+
+ FILE *pipefd = setup(fds, auclass);
+ retval = REQUIRE_EXTATTR_SUCCESS(
+ extattr_delete_file(path, EXTATTR_NAMESPACE_USER, name));
+ /* Prepare the regex to be checked in the audit record */
+ snprintf(extregex, sizeof(extregex),
+ "extattr_delete_file.*%s.*return,success,%d", path, retval);
+ check_audit(fds, extregex, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(extattr_delete_file_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(extattr_delete_file_failure);
+ATF_TC_HEAD(extattr_delete_file_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "extattr_delete_file(2) call");
+}
+
+ATF_TC_BODY(extattr_delete_file_failure, tc)
+{
+ /* Prepare the regex to be checked in the audit record */
+ snprintf(extregex, sizeof(extregex),
+ "extattr_delete_file.*%s.*return,failure", path);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT,
+ extattr_delete_file(path, EXTATTR_NAMESPACE_USER, name) == -1);
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(extattr_delete_file_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(extattr_delete_fd_success);
+ATF_TC_HEAD(extattr_delete_fd_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "extattr_delete_fd(2) call");
+}
+
+ATF_TC_BODY(extattr_delete_fd_success, tc)
+{
+ /* File needs to exist to call extattr_delete_fd(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ skip_if_extattr_not_supported(path);
+
+ REQUIRE_EXTATTR_RESULT(sizeof(buff), extattr_set_file(path,
+ EXTATTR_NAMESPACE_USER, name, buff, sizeof(buff)));
+
+ FILE *pipefd = setup(fds, auclass);
+ retval = REQUIRE_EXTATTR_SUCCESS(extattr_delete_fd(filedesc,
+ EXTATTR_NAMESPACE_USER, name));
+ /* Prepare the regex to be checked in the audit record */
+ snprintf(extregex, sizeof(extregex),
+ "extattr_delete_fd.*return,success,%d", retval);
+ check_audit(fds, extregex, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(extattr_delete_fd_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(extattr_delete_fd_failure);
+ATF_TC_HEAD(extattr_delete_fd_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "extattr_delete_fd(2) call");
+}
+
+ATF_TC_BODY(extattr_delete_fd_failure, tc)
+{
+ /* Prepare the regex to be checked in the audit record */
+ snprintf(extregex, sizeof(extregex),
+ "extattr_delete_fd.*return,failure : Bad file descriptor");
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid file descriptor */
+ ATF_REQUIRE_ERRNO(EBADF,
+ extattr_delete_fd(-1, EXTATTR_NAMESPACE_USER, name) == -1);
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(extattr_delete_fd_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(extattr_delete_link_success);
+ATF_TC_HEAD(extattr_delete_link_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "extattr_delete_link(2) call");
+}
+
+ATF_TC_BODY(extattr_delete_link_success, tc)
+{
+ /* Symbolic link needs to exist to call extattr_delete_link(2) */
+ ATF_REQUIRE_EQ(0, symlink("symlink", path));
+ skip_if_extattr_not_supported(".");
+
+ REQUIRE_EXTATTR_RESULT(sizeof(buff), extattr_set_link(path,
+ EXTATTR_NAMESPACE_USER, name, buff, sizeof(buff)));
+
+ FILE *pipefd = setup(fds, auclass);
+ retval = REQUIRE_EXTATTR_SUCCESS(extattr_delete_link(path,
+ EXTATTR_NAMESPACE_USER, name));
+ /* Prepare the regex to be checked in the audit record */
+ snprintf(extregex, sizeof(extregex),
+ "extattr_delete_link.*%s.*return,success,%d", path, retval);
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(extattr_delete_link_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(extattr_delete_link_failure);
+ATF_TC_HEAD(extattr_delete_link_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "extattr_delete_link(2) call");
+}
+
+ATF_TC_BODY(extattr_delete_link_failure, tc)
+{
+ /* Prepare the regex to be checked in the audit record */
+ snprintf(extregex, sizeof(extregex),
+ "extattr_delete_link.*%s.*failure", path);
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: symbolic link does not exist */
+ ATF_REQUIRE_ERRNO(ENOENT,
+ extattr_delete_link(path, EXTATTR_NAMESPACE_USER, name) == -1);
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(extattr_delete_link_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, flock_success);
+ ATF_TP_ADD_TC(tp, flock_failure);
+ ATF_TP_ADD_TC(tp, fcntl_success);
+ ATF_TP_ADD_TC(tp, fcntl_failure);
+ ATF_TP_ADD_TC(tp, fsync_success);
+ ATF_TP_ADD_TC(tp, fsync_failure);
+
+ ATF_TP_ADD_TC(tp, chmod_success);
+ ATF_TP_ADD_TC(tp, chmod_failure);
+ ATF_TP_ADD_TC(tp, fchmod_success);
+ ATF_TP_ADD_TC(tp, fchmod_failure);
+ ATF_TP_ADD_TC(tp, lchmod_success);
+ ATF_TP_ADD_TC(tp, lchmod_failure);
+ ATF_TP_ADD_TC(tp, fchmodat_success);
+ ATF_TP_ADD_TC(tp, fchmodat_failure);
+
+ ATF_TP_ADD_TC(tp, chown_success);
+ ATF_TP_ADD_TC(tp, chown_failure);
+ ATF_TP_ADD_TC(tp, fchown_success);
+ ATF_TP_ADD_TC(tp, fchown_failure);
+ ATF_TP_ADD_TC(tp, lchown_success);
+ ATF_TP_ADD_TC(tp, lchown_failure);
+ ATF_TP_ADD_TC(tp, fchownat_success);
+ ATF_TP_ADD_TC(tp, fchownat_failure);
+
+ ATF_TP_ADD_TC(tp, chflags_success);
+ ATF_TP_ADD_TC(tp, chflags_failure);
+ ATF_TP_ADD_TC(tp, fchflags_success);
+ ATF_TP_ADD_TC(tp, fchflags_failure);
+ ATF_TP_ADD_TC(tp, lchflags_success);
+ ATF_TP_ADD_TC(tp, lchflags_failure);
+ ATF_TP_ADD_TC(tp, chflagsat_success);
+ ATF_TP_ADD_TC(tp, chflagsat_failure);
+
+ ATF_TP_ADD_TC(tp, utimes_success);
+ ATF_TP_ADD_TC(tp, utimes_failure);
+ ATF_TP_ADD_TC(tp, futimes_success);
+ ATF_TP_ADD_TC(tp, futimes_failure);
+ ATF_TP_ADD_TC(tp, lutimes_success);
+ ATF_TP_ADD_TC(tp, lutimes_failure);
+ ATF_TP_ADD_TC(tp, futimesat_success);
+ ATF_TP_ADD_TC(tp, futimesat_failure);
+
+ ATF_TP_ADD_TC(tp, mprotect_success);
+ ATF_TP_ADD_TC(tp, mprotect_failure);
+ ATF_TP_ADD_TC(tp, undelete_failure);
+
+ ATF_TP_ADD_TC(tp, extattr_set_file_success);
+ ATF_TP_ADD_TC(tp, extattr_set_file_failure);
+ ATF_TP_ADD_TC(tp, extattr_set_fd_success);
+ ATF_TP_ADD_TC(tp, extattr_set_fd_failure);
+ ATF_TP_ADD_TC(tp, extattr_set_link_success);
+ ATF_TP_ADD_TC(tp, extattr_set_link_failure);
+
+ ATF_TP_ADD_TC(tp, extattr_delete_file_success);
+ ATF_TP_ADD_TC(tp, extattr_delete_file_failure);
+ ATF_TP_ADD_TC(tp, extattr_delete_fd_success);
+ ATF_TP_ADD_TC(tp, extattr_delete_fd_failure);
+ ATF_TP_ADD_TC(tp, extattr_delete_link_success);
+ ATF_TP_ADD_TC(tp, extattr_delete_link_failure);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/audit/file-close.c b/tests/sys/audit/file-close.c
new file mode 100644
index 000000000000..38564b8504f1
--- /dev/null
+++ b/tests/sys/audit/file-close.c
@@ -0,0 +1,233 @@
+/*-
+ * Copyright 2018 Aniket Pandey
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * SUCH DAMAGE.
+ */
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "utils.h"
+
+static pid_t pid;
+static struct pollfd fds[1];
+static mode_t mode = 0777;
+static int filedesc;
+static char extregex[80];
+static struct stat statbuff;
+static const char *auclass = "cl";
+static const char *path = "fileforaudit";
+static const char *errpath = "dirdoesnotexist/fileforaudit";
+static const char *failurereg = "fileforaudit.*return,failure";
+
+
+ATF_TC_WITH_CLEANUP(munmap_success);
+ATF_TC_HEAD(munmap_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "munmap(2) call");
+}
+
+ATF_TC_BODY(munmap_success, tc)
+{
+ pid = getpid();
+ snprintf(extregex, sizeof(extregex), "munmap.*%d.*return,success", pid);
+
+ /* Allocate sample memory, to be removed by munmap(2) */
+ char *addr = mmap(NULL, sizeof(char), PROT_READ , MAP_ANONYMOUS, -1, 0);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, munmap(addr, sizeof(char)));
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(munmap_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(munmap_failure);
+ATF_TC_HEAD(munmap_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "munmap(2) call");
+}
+
+ATF_TC_BODY(munmap_failure, tc)
+{
+ const char *regex = "munmap.*return,failure : Invalid argument";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, munmap((void *)SIZE_MAX, -1));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(munmap_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(close_success);
+ATF_TC_HEAD(close_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "close(2) call");
+}
+
+ATF_TC_BODY(close_success, tc)
+{
+ /* File needs to exist to call close(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT | O_RDWR, mode)) != -1);
+ /* Call stat(2) to store the Inode number of 'path' */
+ ATF_REQUIRE_EQ(0, stat(path, &statbuff));
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, close(filedesc));
+
+ /* intmax_t to support all architectures */
+ snprintf(extregex, sizeof(extregex), "close.*%jd.*return,succes",
+ (intmax_t)statbuff.st_ino);
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(close_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(close_failure);
+ATF_TC_HEAD(close_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "close(2) call");
+}
+
+ATF_TC_BODY(close_failure, tc)
+{
+ const char *regex = "close.*return,failure";
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_EQ(-1, close(-1));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(close_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(closefrom_success);
+ATF_TC_HEAD(closefrom_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "closefrom(2) call");
+}
+
+ATF_TC_BODY(closefrom_success, tc)
+{
+ const char *regex = "close_range\\(2\\),.*,0x7fffffff,lowfd,.*"
+ "0xffffffff,highfd,.*return,success";
+ FILE *pipefd = setup(fds, auclass);
+
+ /* closefrom(2) returns 'void' */
+ closefrom(INT_MAX);
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(closefrom_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(revoke_success);
+ATF_TC_HEAD(revoke_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "revoke(2) call");
+}
+
+ATF_TC_BODY(revoke_success, tc)
+{
+ char *ptyname;
+ pid = getpid();
+ snprintf(extregex, sizeof(extregex), "revoke.*%d.*return,success", pid);
+
+ /* Obtain a pseudo terminal and get the path to slave device */
+ ATF_REQUIRE((filedesc = posix_openpt(O_RDWR | O_NOCTTY)) != -1);
+ ATF_REQUIRE((ptyname = ptsname(filedesc)) != NULL);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, revoke(ptyname));
+ check_audit(fds, extregex, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(revoke_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(revoke_failure);
+ATF_TC_HEAD(revoke_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "revoke(2) call");
+}
+
+ATF_TC_BODY(revoke_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_EQ(-1, revoke(errpath));
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(revoke_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, munmap_success);
+ ATF_TP_ADD_TC(tp, munmap_failure);
+
+ ATF_TP_ADD_TC(tp, close_success);
+ ATF_TP_ADD_TC(tp, close_failure);
+ ATF_TP_ADD_TC(tp, closefrom_success);
+
+ ATF_TP_ADD_TC(tp, revoke_success);
+ ATF_TP_ADD_TC(tp, revoke_failure);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/audit/file-create.c b/tests/sys/audit/file-create.c
new file mode 100644
index 000000000000..56ecf258fa3d
--- /dev/null
+++ b/tests/sys/audit/file-create.c
@@ -0,0 +1,587 @@
+/*-
+ * Copyright 2018 Aniket Pandey
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "utils.h"
+
+static struct pollfd fds[1];
+static mode_t mode = 0777;
+static int filedesc;
+static dev_t dev = 0;
+static const char *auclass = "fc";
+static const char *path = "fileforaudit";
+static const char *successreg = "fileforaudit.*return,success";
+static const char *failurereg = "fileforaudit.*return,failure";
+
+
+ATF_TC_WITH_CLEANUP(mkdir_success);
+ATF_TC_HEAD(mkdir_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "mkdir(2) call");
+}
+
+ATF_TC_BODY(mkdir_success, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, mkdir(path, mode));
+ check_audit(fds, successreg, pipefd);
+}
+
+ATF_TC_CLEANUP(mkdir_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(mkdir_failure);
+ATF_TC_HEAD(mkdir_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "mkdir(2) call");
+}
+
+ATF_TC_BODY(mkdir_failure, tc)
+{
+ ATF_REQUIRE_EQ(0, mkdir(path, mode));
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: directory already exists */
+ ATF_REQUIRE_EQ(-1, mkdir(path, mode));
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(mkdir_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(mkdirat_success);
+ATF_TC_HEAD(mkdirat_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "mkdirat(2) call");
+}
+
+ATF_TC_BODY(mkdirat_success, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, mkdirat(AT_FDCWD, path, mode));
+ check_audit(fds, successreg, pipefd);
+}
+
+ATF_TC_CLEANUP(mkdirat_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(mkdirat_failure);
+ATF_TC_HEAD(mkdirat_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "mkdirat(2) call");
+}
+
+ATF_TC_BODY(mkdirat_failure, tc)
+{
+ ATF_REQUIRE_EQ(0, mkdirat(AT_FDCWD, path, mode));
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: directory already exists */
+ ATF_REQUIRE_EQ(-1, mkdirat(AT_FDCWD, path, mode));
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(mkdirat_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(mkfifo_success);
+ATF_TC_HEAD(mkfifo_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "mkfifo(2) call");
+}
+
+ATF_TC_BODY(mkfifo_success, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, mkfifo(path, mode));
+ check_audit(fds, successreg, pipefd);
+}
+
+ATF_TC_CLEANUP(mkfifo_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(mkfifo_failure);
+ATF_TC_HEAD(mkfifo_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "mkfifo(2) call");
+}
+
+ATF_TC_BODY(mkfifo_failure, tc)
+{
+ ATF_REQUIRE_EQ(0, mkfifo(path, mode));
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: FIFO already exists */
+ ATF_REQUIRE_EQ(-1, mkfifo(path, mode));
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(mkfifo_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(mkfifoat_success);
+ATF_TC_HEAD(mkfifoat_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "mkfifoat(2) call");
+}
+
+ATF_TC_BODY(mkfifoat_success, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, mkfifoat(AT_FDCWD, path, mode));
+ check_audit(fds, successreg, pipefd);
+}
+
+ATF_TC_CLEANUP(mkfifoat_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(mkfifoat_failure);
+ATF_TC_HEAD(mkfifoat_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "mkfifoat(2) call");
+}
+
+ATF_TC_BODY(mkfifoat_failure, tc)
+{
+ ATF_REQUIRE_EQ(0, mkfifoat(AT_FDCWD, path, mode));
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: FIFO already exists */
+ ATF_REQUIRE_EQ(-1, mkfifoat(AT_FDCWD, path, mode));
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(mkfifoat_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(mknod_success);
+ATF_TC_HEAD(mknod_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "mknod(2) call");
+}
+
+ATF_TC_BODY(mknod_success, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, mknod(path, S_IFIFO | S_IRWXO, dev));
+ check_audit(fds, successreg, pipefd);
+}
+
+ATF_TC_CLEANUP(mknod_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(mknod_failure);
+ATF_TC_HEAD(mknod_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "mknod(2) call");
+}
+
+ATF_TC_BODY(mknod_failure, tc)
+{
+ ATF_REQUIRE_EQ(0, mknod(path, S_IFIFO | S_IRWXO, dev));
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: FIFO node already exists */
+ ATF_REQUIRE_EQ(-1, mknod(path, S_IFIFO | S_IRWXO, dev));
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(mknod_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(mknodat_success);
+ATF_TC_HEAD(mknodat_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "mknodat(2) call");
+}
+
+ATF_TC_BODY(mknodat_success, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, mknodat(AT_FDCWD, path, S_IFIFO | S_IRWXO, dev));
+ check_audit(fds, successreg, pipefd);
+}
+
+ATF_TC_CLEANUP(mknodat_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(mknodat_failure);
+ATF_TC_HEAD(mknodat_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "mknodat(2) call");
+}
+
+ATF_TC_BODY(mknodat_failure, tc)
+{
+ ATF_REQUIRE_EQ(0, mknodat(AT_FDCWD, path, S_IFIFO | S_IRWXO, dev));
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: FIFO node already exists */
+ ATF_REQUIRE_EQ(-1, mknodat(AT_FDCWD, path, S_IFIFO | S_IRWXO, dev));
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(mknodat_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(rename_success);
+ATF_TC_HEAD(rename_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "rename(2) call");
+}
+
+ATF_TC_BODY(rename_success, tc)
+{
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, rename(path, "renamed"));
+ check_audit(fds, successreg, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(rename_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(rename_failure);
+ATF_TC_HEAD(rename_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "rename(2) call");
+}
+
+ATF_TC_BODY(rename_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_EQ(-1, rename(path, "renamed"));
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(rename_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(renameat_success);
+ATF_TC_HEAD(renameat_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "renameat(2) call");
+}
+
+ATF_TC_BODY(renameat_success, tc)
+{
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, renameat(AT_FDCWD, path, AT_FDCWD, "renamed"));
+ check_audit(fds, successreg, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(renameat_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(renameat_failure);
+ATF_TC_HEAD(renameat_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "renameat(2) call");
+}
+
+ATF_TC_BODY(renameat_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_EQ(-1, renameat(AT_FDCWD, path, AT_FDCWD, "renamed"));
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(renameat_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(link_success);
+ATF_TC_HEAD(link_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "link(2) call");
+}
+
+ATF_TC_BODY(link_success, tc)
+{
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, link(path, "hardlink"));
+ check_audit(fds, successreg, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(link_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(link_failure);
+ATF_TC_HEAD(link_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "link(2) call");
+}
+
+ATF_TC_BODY(link_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_EQ(-1, link(path, "hardlink"));
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(link_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(linkat_success);
+ATF_TC_HEAD(linkat_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "linkat(2) call");
+}
+
+ATF_TC_BODY(linkat_success, tc)
+{
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, linkat(AT_FDCWD, path, AT_FDCWD, "hardlink", 0));
+ check_audit(fds, successreg, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(linkat_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(linkat_failure);
+ATF_TC_HEAD(linkat_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "linkat(2) call");
+}
+
+ATF_TC_BODY(linkat_failure, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_EQ(-1, linkat(AT_FDCWD, path, AT_FDCWD, "hardlink", 0));
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(linkat_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(symlink_success);
+ATF_TC_HEAD(symlink_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "symlink(2) call");
+}
+
+ATF_TC_BODY(symlink_success, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, symlink(path, "symlink"));
+ check_audit(fds, successreg, pipefd);
+}
+
+ATF_TC_CLEANUP(symlink_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(symlink_failure);
+ATF_TC_HEAD(symlink_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "symlink(2) call");
+}
+
+ATF_TC_BODY(symlink_failure, tc)
+{
+ ATF_REQUIRE_EQ(0, symlink(path, "symlink"));
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: symbolic link already exists */
+ ATF_REQUIRE_EQ(-1, symlink(path, "symlink"));
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(symlink_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(symlinkat_success);
+ATF_TC_HEAD(symlinkat_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "symlinkat(2) call");
+}
+
+ATF_TC_BODY(symlinkat_success, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, symlinkat(path, AT_FDCWD, "symlink"));
+ check_audit(fds, successreg, pipefd);
+}
+
+ATF_TC_CLEANUP(symlinkat_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(symlinkat_failure);
+ATF_TC_HEAD(symlinkat_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "symlinkat(2) call");
+}
+
+ATF_TC_BODY(symlinkat_failure, tc)
+{
+ ATF_REQUIRE_EQ(0, symlinkat(path, AT_FDCWD, "symlink"));
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: symbolic link already exists */
+ ATF_REQUIRE_EQ(-1, symlinkat(path, AT_FDCWD, "symlink"));
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(symlinkat_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, mkdir_success);
+ ATF_TP_ADD_TC(tp, mkdir_failure);
+ ATF_TP_ADD_TC(tp, mkdirat_success);
+ ATF_TP_ADD_TC(tp, mkdirat_failure);
+
+ ATF_TP_ADD_TC(tp, mkfifo_success);
+ ATF_TP_ADD_TC(tp, mkfifo_failure);
+ ATF_TP_ADD_TC(tp, mkfifoat_success);
+ ATF_TP_ADD_TC(tp, mkfifoat_failure);
+
+ ATF_TP_ADD_TC(tp, mknod_success);
+ ATF_TP_ADD_TC(tp, mknod_failure);
+ ATF_TP_ADD_TC(tp, mknodat_success);
+ ATF_TP_ADD_TC(tp, mknodat_failure);
+
+ ATF_TP_ADD_TC(tp, rename_success);
+ ATF_TP_ADD_TC(tp, rename_failure);
+ ATF_TP_ADD_TC(tp, renameat_success);
+ ATF_TP_ADD_TC(tp, renameat_failure);
+
+ ATF_TP_ADD_TC(tp, link_success);
+ ATF_TP_ADD_TC(tp, link_failure);
+ ATF_TP_ADD_TC(tp, linkat_success);
+ ATF_TP_ADD_TC(tp, linkat_failure);
+
+ ATF_TP_ADD_TC(tp, symlink_success);
+ ATF_TP_ADD_TC(tp, symlink_failure);
+ ATF_TP_ADD_TC(tp, symlinkat_success);
+ ATF_TP_ADD_TC(tp, symlinkat_failure);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/audit/file-delete.c b/tests/sys/audit/file-delete.c
new file mode 100644
index 000000000000..788ea564ca02
--- /dev/null
+++ b/tests/sys/audit/file-delete.c
@@ -0,0 +1,272 @@
+/*-
+ * Copyright 2018 Aniket Pandey
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * SUCH DAMAGE.
+ */
+
+#include <sys/stat.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "utils.h"
+
+static struct pollfd fds[1];
+static mode_t mode = 0777;
+static int filedesc;
+static const char *path = "fileforaudit";
+static const char *errpath = "dirdoesnotexist/fileforaudit";
+static const char *successreg = "fileforaudit.*return,success";
+static const char *failurereg = "fileforaudit.*return,failure";
+
+
+ATF_TC_WITH_CLEANUP(rmdir_success);
+ATF_TC_HEAD(rmdir_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "rmdir(2) call");
+}
+
+ATF_TC_BODY(rmdir_success, tc)
+{
+ ATF_REQUIRE_EQ(0, mkdir(path, mode));
+ FILE *pipefd = setup(fds, "fd");
+ ATF_REQUIRE_EQ(0, rmdir(path));
+ check_audit(fds, successreg, pipefd);
+}
+
+ATF_TC_CLEANUP(rmdir_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(rmdir_failure);
+ATF_TC_HEAD(rmdir_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "rmdir(2) call");
+}
+
+ATF_TC_BODY(rmdir_failure, tc)
+{
+ FILE *pipefd = setup(fds, "fd");
+ /* Failure reason: directory does not exist */
+ ATF_REQUIRE_EQ(-1, rmdir(errpath));
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(rmdir_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(rename_success);
+ATF_TC_HEAD(rename_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "rename(2) call");
+}
+
+ATF_TC_BODY(rename_success, tc)
+{
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, "fd");
+ ATF_REQUIRE_EQ(0, rename(path, "renamed"));
+ check_audit(fds, successreg, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(rename_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(rename_failure);
+ATF_TC_HEAD(rename_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "rename(2) call");
+}
+
+ATF_TC_BODY(rename_failure, tc)
+{
+ FILE *pipefd = setup(fds, "fd");
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_EQ(-1, rename(path, "renamed"));
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(rename_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(renameat_success);
+ATF_TC_HEAD(renameat_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "renameat(2) call");
+}
+
+ATF_TC_BODY(renameat_success, tc)
+{
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, "fd");
+ ATF_REQUIRE_EQ(0, renameat(AT_FDCWD, path, AT_FDCWD, "renamed"));
+ check_audit(fds, successreg, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(renameat_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(renameat_failure);
+ATF_TC_HEAD(renameat_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "renameat(2) call");
+}
+
+ATF_TC_BODY(renameat_failure, tc)
+{
+ FILE *pipefd = setup(fds, "fd");
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_EQ(-1, renameat(AT_FDCWD, path, AT_FDCWD, "renamed"));
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(renameat_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(unlink_success);
+ATF_TC_HEAD(unlink_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "unlink(2) call");
+}
+
+ATF_TC_BODY(unlink_success, tc)
+{
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, "fd");
+ ATF_REQUIRE_EQ(0, unlink(path));
+ check_audit(fds, successreg, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(unlink_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(unlink_failure);
+ATF_TC_HEAD(unlink_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "unlink(2) call");
+}
+
+ATF_TC_BODY(unlink_failure, tc)
+{
+ FILE *pipefd = setup(fds, "fd");
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_EQ(-1, unlink(errpath));
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(unlink_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(unlinkat_success);
+ATF_TC_HEAD(unlinkat_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "unlinkat(2) call");
+}
+
+ATF_TC_BODY(unlinkat_success, tc)
+{
+ ATF_REQUIRE_EQ(0, mkdir(path, mode));
+ FILE *pipefd = setup(fds, "fd");
+ ATF_REQUIRE_EQ(0, unlinkat(AT_FDCWD, path, AT_REMOVEDIR));
+ check_audit(fds, successreg, pipefd);
+}
+
+ATF_TC_CLEANUP(unlinkat_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(unlinkat_failure);
+ATF_TC_HEAD(unlinkat_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "unlinkat(2) call");
+}
+
+ATF_TC_BODY(unlinkat_failure, tc)
+{
+ FILE *pipefd = setup(fds, "fd");
+ /* Failure reason: directory does not exist */
+ ATF_REQUIRE_EQ(-1, unlinkat(AT_FDCWD, errpath, AT_REMOVEDIR));
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(unlinkat_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, rmdir_success);
+ ATF_TP_ADD_TC(tp, rmdir_failure);
+
+ ATF_TP_ADD_TC(tp, rename_success);
+ ATF_TP_ADD_TC(tp, rename_failure);
+ ATF_TP_ADD_TC(tp, renameat_success);
+ ATF_TP_ADD_TC(tp, renameat_failure);
+
+ ATF_TP_ADD_TC(tp, unlink_success);
+ ATF_TP_ADD_TC(tp, unlink_failure);
+ ATF_TP_ADD_TC(tp, unlinkat_success);
+ ATF_TP_ADD_TC(tp, unlinkat_failure);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/audit/file-read.c b/tests/sys/audit/file-read.c
new file mode 100644
index 000000000000..66d173b2f757
--- /dev/null
+++ b/tests/sys/audit/file-read.c
@@ -0,0 +1,134 @@
+/*-
+ * Copyright 2018 Aniket Pandey
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * SUCH DAMAGE.
+ */
+
+#include <atf-c.h>
+#include <fcntl.h>
+
+#include "utils.h"
+
+static struct pollfd fds[1];
+static char buff[1024];
+static const char *path = "fileforaudit";
+static const char *successreg = "fileforaudit.*return,success";
+static const char *failurereg = "fileforaudit.*return,failure";
+
+
+ATF_TC_WITH_CLEANUP(readlink_success);
+ATF_TC_HEAD(readlink_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "readlink(2) call");
+}
+
+ATF_TC_BODY(readlink_success, tc)
+{
+ memset(buff, 0, sizeof(buff));
+ ATF_REQUIRE_EQ(0, symlink("symlink", path));
+ FILE *pipefd = setup(fds, "fr");
+ ATF_REQUIRE(readlink(path, buff, sizeof(buff)-1) != -1);
+ check_audit(fds, successreg, pipefd);
+}
+
+ATF_TC_CLEANUP(readlink_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(readlink_failure);
+ATF_TC_HEAD(readlink_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "readlink(2) call");
+}
+
+ATF_TC_BODY(readlink_failure, tc)
+{
+ memset(buff, 0, sizeof(buff));
+ FILE *pipefd = setup(fds, "fr");
+ /* Failure reason: symbolic link does not exist */
+ ATF_REQUIRE_EQ(-1, readlink(path, buff, sizeof(buff)-1));
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(readlink_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(readlinkat_success);
+ATF_TC_HEAD(readlinkat_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "readlinkat(2) call");
+}
+
+ATF_TC_BODY(readlinkat_success, tc)
+{
+ memset(buff, 0, sizeof(buff));
+ ATF_REQUIRE_EQ(0, symlink("symlink", path));
+ FILE *pipefd = setup(fds, "fr");
+ ATF_REQUIRE(readlinkat(AT_FDCWD, path, buff, sizeof(buff)-1) != -1);
+ check_audit(fds, successreg, pipefd);
+}
+
+ATF_TC_CLEANUP(readlinkat_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(readlinkat_failure);
+ATF_TC_HEAD(readlinkat_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "readlinkat(2) call");
+}
+
+ATF_TC_BODY(readlinkat_failure, tc)
+{
+ memset(buff, 0, sizeof(buff));
+ FILE *pipefd = setup(fds, "fr");
+ /* Failure reason: symbolic link does not exist */
+ ATF_REQUIRE_EQ(-1, readlinkat(AT_FDCWD, path, buff, sizeof(buff)-1));
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(readlinkat_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, readlink_success);
+ ATF_TP_ADD_TC(tp, readlink_failure);
+ ATF_TP_ADD_TC(tp, readlinkat_success);
+ ATF_TP_ADD_TC(tp, readlinkat_failure);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/audit/file-write.c b/tests/sys/audit/file-write.c
new file mode 100644
index 000000000000..98e670ccd8c3
--- /dev/null
+++ b/tests/sys/audit/file-write.c
@@ -0,0 +1,139 @@
+/*-
+ * Copyright 2018 Aniket Pandey
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * SUCH DAMAGE.
+ */
+
+#include <atf-c.h>
+#include <fcntl.h>
+
+#include "utils.h"
+
+static struct pollfd fds[1];
+static mode_t mode = 0777;
+static int filedesc;
+static off_t offlen = 0;
+static const char *path = "fileforaudit";
+static const char *errpath = "dirdoesnotexist/fileforaudit";
+static const char *successreg = "fileforaudit.*return,success";
+static const char *failurereg = "fileforaudit.*return,failure";
+
+
+ATF_TC_WITH_CLEANUP(truncate_success);
+ATF_TC_HEAD(truncate_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "truncate(2) call");
+}
+
+ATF_TC_BODY(truncate_success, tc)
+{
+ /* File needs to exist to call truncate(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, mode)) != -1);
+ FILE *pipefd = setup(fds, "fw");
+ ATF_REQUIRE_EQ(0, truncate(path, offlen));
+ check_audit(fds, successreg, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(truncate_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(truncate_failure);
+ATF_TC_HEAD(truncate_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "truncate(2) call");
+}
+
+ATF_TC_BODY(truncate_failure, tc)
+{
+ FILE *pipefd = setup(fds, "fw");
+ /* Failure reason: file does not exist */
+ ATF_REQUIRE_EQ(-1, truncate(errpath, offlen));
+ check_audit(fds, failurereg, pipefd);
+}
+
+ATF_TC_CLEANUP(truncate_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(ftruncate_success);
+ATF_TC_HEAD(ftruncate_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "ftruncate(2) call");
+}
+
+ATF_TC_BODY(ftruncate_success, tc)
+{
+ const char *regex = "ftruncate.*return,success";
+ /* Valid file descriptor needs to exist to call ftruncate(2) */
+ ATF_REQUIRE((filedesc = open(path, O_CREAT | O_RDWR)) != -1);
+ FILE *pipefd = setup(fds, "fw");
+ ATF_REQUIRE_EQ(0, ftruncate(filedesc, offlen));
+ check_audit(fds, regex, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(ftruncate_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(ftruncate_failure);
+ATF_TC_HEAD(ftruncate_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "ftruncate(2) call");
+}
+
+ATF_TC_BODY(ftruncate_failure, tc)
+{
+ const char *regex = "ftruncate.*return,failure";
+ FILE *pipefd = setup(fds, "fw");
+ /* Failure reason: bad file descriptor */
+ ATF_REQUIRE_EQ(-1, ftruncate(-1, offlen));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(ftruncate_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, truncate_success);
+ ATF_TP_ADD_TC(tp, truncate_failure);
+ ATF_TP_ADD_TC(tp, ftruncate_success);
+ ATF_TP_ADD_TC(tp, ftruncate_failure);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/audit/inter-process.c b/tests/sys/audit/inter-process.c
new file mode 100644
index 000000000000..4912da43fe95
--- /dev/null
+++ b/tests/sys/audit/inter-process.c
@@ -0,0 +1,1658 @@
+/*-
+ * Copyright (c) 2018 Aniket Pandey
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/mman.h>
+#include <sys/msg.h>
+#include <sys/shm.h>
+#define _WANT_SEMUN
+#include <sys/sem.h>
+#include <sys/stat.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "utils.h"
+#define BUFFSIZE 80
+
+struct msgstr {
+ long int mtype;
+ char mtext[BUFFSIZE];
+};
+typedef struct msgstr msgstr_t;
+
+static pid_t pid;
+static int msqid, shmid, semid;
+static union semun semarg;
+static struct pollfd fds[1];
+static struct msqid_ds msgbuff;
+static struct shmid_ds shmbuff;
+static struct semid_ds sembuff;
+static char ipcregex[BUFFSIZE];
+static const char *auclass = "ip";
+static char path[BUFFSIZE] = "/fileforaudit";
+static unsigned short semvals[BUFFSIZE];
+
+
+ATF_TC_WITH_CLEANUP(msgget_success);
+ATF_TC_HEAD(msgget_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "msgget(2) call");
+}
+
+ATF_TC_BODY(msgget_success, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ /* Create a message queue and obtain the corresponding identifier */
+ ATF_REQUIRE((msqid = msgget(IPC_PRIVATE, IPC_CREAT | S_IRUSR)) != -1);
+ /* Check the presence of message queue ID in audit record */
+ snprintf(ipcregex, sizeof(ipcregex),
+ "msgget.*return,success,%d", msqid);
+ check_audit(fds, ipcregex, pipefd);
+
+ /* Destroy the message queue with ID = msqid */
+ ATF_REQUIRE_EQ(0, msgctl(msqid, IPC_RMID, NULL));
+}
+
+ATF_TC_CLEANUP(msgget_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(msgget_failure);
+ATF_TC_HEAD(msgget_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "msgget(2) call");
+}
+
+ATF_TC_BODY(msgget_failure, tc)
+{
+ const char *regex = "msgget.*return,failure.*No such file or directory";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, msgget((key_t)(-1), 0));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(msgget_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(msgsnd_success);
+ATF_TC_HEAD(msgsnd_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "msgsnd(2) call");
+}
+
+ATF_TC_BODY(msgsnd_success, tc)
+{
+ /* Create a message queue and obtain the corresponding identifier */
+ ATF_REQUIRE((msqid = msgget(IPC_PRIVATE, IPC_CREAT | S_IRUSR)) != -1);
+
+ /* Initialize a msgstr_t structure to store message */
+ msgstr_t msg;
+ msg.mtype = 1;
+ memset(msg.mtext, 0, BUFFSIZE);
+
+ /* Check the presence of message queue ID in audit record */
+ snprintf(ipcregex, sizeof(ipcregex),
+ "msgsnd.*Message IPC.*%d.*return,success", msqid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, msgsnd(msqid, &msg, BUFFSIZE, IPC_NOWAIT));
+ check_audit(fds, ipcregex, pipefd);
+
+ /* Destroy the message queue with ID = msqid */
+ ATF_REQUIRE_EQ(0, msgctl(msqid, IPC_RMID, NULL));
+}
+
+ATF_TC_CLEANUP(msgsnd_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(msgsnd_failure);
+ATF_TC_HEAD(msgsnd_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "msgsnd(2) call");
+}
+
+ATF_TC_BODY(msgsnd_failure, tc)
+{
+ const char *regex = "msgsnd.*Message IPC.*return,failure : Bad address";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, msgsnd(-1, NULL, 0, IPC_NOWAIT));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(msgsnd_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(msgrcv_success);
+ATF_TC_HEAD(msgrcv_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "msgrcv(2) call");
+}
+
+ATF_TC_BODY(msgrcv_success, tc)
+{
+ ssize_t recv_bytes;
+ /* Create a message queue and obtain the corresponding identifier */
+ ATF_REQUIRE((msqid = msgget(IPC_PRIVATE, IPC_CREAT | S_IRUSR)) != -1);
+
+ /* Initialize two msgstr_t structures to store respective messages */
+ msgstr_t msg1, msg2;
+ msg1.mtype = 1;
+ memset(msg1.mtext, 0, BUFFSIZE);
+
+ /* Send a message to the queue with ID = msqid */
+ ATF_REQUIRE_EQ(0, msgsnd(msqid, &msg1, BUFFSIZE, IPC_NOWAIT));
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE((recv_bytes = msgrcv(msqid, &msg2,
+ BUFFSIZE, 0, MSG_NOERROR | IPC_NOWAIT)) != -1);
+ /* Check the presence of queue ID and returned bytes in audit record */
+ snprintf(ipcregex, sizeof(ipcregex),
+ "msgrcv.*Message IPC,*%d.*return,success,%zd", msqid, recv_bytes);
+ check_audit(fds, ipcregex, pipefd);
+
+ /* Destroy the message queue with ID = msqid */
+ ATF_REQUIRE_EQ(0, msgctl(msqid, IPC_RMID, NULL));
+}
+
+ATF_TC_CLEANUP(msgrcv_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(msgrcv_failure);
+ATF_TC_HEAD(msgrcv_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "msgrcv(2) call");
+}
+
+ATF_TC_BODY(msgrcv_failure, tc)
+{
+ const char *regex = "msgrcv.*return,failure : Invalid argument";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, msgrcv(-1, NULL, 0, 0, MSG_NOERROR | IPC_NOWAIT));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(msgrcv_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(msgctl_rmid_success);
+ATF_TC_HEAD(msgctl_rmid_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "msgctl(2) call for IPC_RMID command");
+}
+
+ATF_TC_BODY(msgctl_rmid_success, tc)
+{
+ /* Create a message queue and obtain the corresponding identifier */
+ ATF_REQUIRE((msqid = msgget(IPC_PRIVATE, IPC_CREAT | S_IRUSR)) != -1);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, msgctl(msqid, IPC_RMID, NULL));
+ /* Check the presence of queue ID and IPC_RMID in audit record */
+ snprintf(ipcregex, sizeof(ipcregex),
+ "msgctl.*IPC_RMID.*%d.*return,success", msqid);
+ check_audit(fds, ipcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(msgctl_rmid_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(msgctl_rmid_failure);
+ATF_TC_HEAD(msgctl_rmid_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "msgctl(2) call for IPC_RMID command");
+}
+
+ATF_TC_BODY(msgctl_rmid_failure, tc)
+{
+ const char *regex = "msgctl.*IPC_RMID.*return,failur.*Invalid argument";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, msgctl(-1, IPC_RMID, NULL));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(msgctl_rmid_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(msgctl_stat_success);
+ATF_TC_HEAD(msgctl_stat_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "msgctl(2) call for IPC_STAT command");
+}
+
+ATF_TC_BODY(msgctl_stat_success, tc)
+{
+ /* Create a message queue and obtain the corresponding identifier */
+ ATF_REQUIRE((msqid = msgget(IPC_PRIVATE, IPC_CREAT | S_IRUSR)) != -1);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, msgctl(msqid, IPC_STAT, &msgbuff));
+ /* Check the presence of queue ID and IPC_STAT in audit record */
+ snprintf(ipcregex, sizeof(ipcregex),
+ "msgctl.*IPC_STAT.*%d.*return,success", msqid);
+ check_audit(fds, ipcregex, pipefd);
+
+ /* Destroy the message queue with ID = msqid */
+ ATF_REQUIRE_EQ(0, msgctl(msqid, IPC_RMID, NULL));
+}
+
+ATF_TC_CLEANUP(msgctl_stat_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(msgctl_stat_failure);
+ATF_TC_HEAD(msgctl_stat_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "msgctl(2) call for IPC_STAT command");
+}
+
+ATF_TC_BODY(msgctl_stat_failure, tc)
+{
+ const char *regex = "msgctl.*IPC_STAT.*return,failur.*Invalid argument";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, msgctl(-1, IPC_STAT, &msgbuff));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(msgctl_stat_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(msgctl_set_success);
+ATF_TC_HEAD(msgctl_set_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "msgctl(2) call for IPC_SET command");
+}
+
+ATF_TC_BODY(msgctl_set_success, tc)
+{
+ /* Create a message queue and obtain the corresponding identifier */
+ ATF_REQUIRE((msqid = msgget(IPC_PRIVATE, IPC_CREAT | S_IRUSR)) != -1);
+ /* Fill up the msgbuff structure to be used with IPC_SET */
+ ATF_REQUIRE_EQ(0, msgctl(msqid, IPC_STAT, &msgbuff));
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, msgctl(msqid, IPC_SET, &msgbuff));
+ /* Check the presence of message queue ID in audit record */
+ snprintf(ipcregex, sizeof(ipcregex),
+ "msgctl.*IPC_SET.*%d.*return,success", msqid);
+ check_audit(fds, ipcregex, pipefd);
+
+ /* Destroy the message queue with ID = msqid */
+ ATF_REQUIRE_EQ(0, msgctl(msqid, IPC_RMID, NULL));
+}
+
+ATF_TC_CLEANUP(msgctl_set_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(msgctl_set_failure);
+ATF_TC_HEAD(msgctl_set_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "msgctl(2) call for IPC_SET command");
+}
+
+ATF_TC_BODY(msgctl_set_failure, tc)
+{
+ const char *regex = "msgctl.*IPC_SET.*return,failure.*Invalid argument";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, msgctl(-1, IPC_SET, &msgbuff));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(msgctl_set_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(msgctl_illegal_command);
+ATF_TC_HEAD(msgctl_illegal_command, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "msgctl(2) call for illegal cmd value");
+}
+
+ATF_TC_BODY(msgctl_illegal_command, tc)
+{
+ /* Create a message queue and obtain the corresponding identifier */
+ ATF_REQUIRE((msqid = msgget(IPC_PRIVATE, IPC_CREAT | S_IRUSR)) != -1);
+
+ const char *regex = "msgctl.*illegal command.*failur.*Invalid argument";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, msgctl(msqid, -1, &msgbuff));
+ check_audit(fds, regex, pipefd);
+
+ /* Destroy the message queue with ID = msqid */
+ ATF_REQUIRE_EQ(0, msgctl(msqid, IPC_RMID, NULL));
+}
+
+ATF_TC_CLEANUP(msgctl_illegal_command, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(shmget_success);
+ATF_TC_HEAD(shmget_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "shmget(2) call");
+}
+
+ATF_TC_BODY(shmget_success, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE((shmid =
+ shmget(IPC_PRIVATE, 1, IPC_CREAT | S_IRUSR)) != -1);
+ /* Check the presence of shared memory ID in audit record */
+ snprintf(ipcregex, sizeof(ipcregex), "shmget.*ret.*success,%d", shmid);
+ check_audit(fds, ipcregex, pipefd);
+
+ /* Destroy the shared memory with ID = shmid */
+ ATF_REQUIRE_EQ(0, shmctl(shmid, IPC_RMID, NULL));
+}
+
+ATF_TC_CLEANUP(shmget_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(shmget_failure);
+ATF_TC_HEAD(shmget_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "shmget(2) call");
+}
+
+ATF_TC_BODY(shmget_failure, tc)
+{
+ const char *regex = "shmget.*return,failure.*No such file or directory";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, shmget((key_t)(-1), 0, 0));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(shmget_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(shmat_success);
+ATF_TC_HEAD(shmat_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "shmat(2) call");
+}
+
+ATF_TC_BODY(shmat_success, tc)
+{
+ void *addr;
+ /* Create a shared memory segment and obtain the identifier */
+ ATF_REQUIRE((shmid =
+ shmget(IPC_PRIVATE, 1, IPC_CREAT | S_IRUSR)) != -1);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE((intptr_t)(addr = shmat(shmid, NULL, 0)) != -1);
+
+ /* Check for shared memory ID and process address in record */
+ snprintf(ipcregex, sizeof(ipcregex), "shmat.*Shared Memory "
+ "IPC.*%d.*return,success", shmid);
+ check_audit(fds, ipcregex, pipefd);
+
+ /* Destroy the shared memory with ID = shmid */
+ ATF_REQUIRE_EQ(0, shmctl(shmid, IPC_RMID, NULL));
+}
+
+ATF_TC_CLEANUP(shmat_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(shmat_failure);
+ATF_TC_HEAD(shmat_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "shmat(2) call");
+}
+
+ATF_TC_BODY(shmat_failure, tc)
+{
+ const char *regex = "shmat.*Shared Memory IPC.*return,failure";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, (intptr_t)shmat(-1, NULL, 0));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(shmat_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(shmdt_success);
+ATF_TC_HEAD(shmdt_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "shmdt(2) call");
+}
+
+ATF_TC_BODY(shmdt_success, tc)
+{
+ void *addr;
+ pid = getpid();
+ snprintf(ipcregex, sizeof(ipcregex), "shmdt.*%d.*return,success", pid);
+
+ /* Create a shared memory segment and obtain the identifier */
+ ATF_REQUIRE((shmid =
+ shmget(IPC_PRIVATE, 1, IPC_CREAT | S_IRUSR)) != -1);
+
+ /* Attach the shared memory to calling process's address space */
+ ATF_REQUIRE((intptr_t)(addr = shmat(shmid, NULL, 0)) != -1);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, shmdt(addr));
+ check_audit(fds, ipcregex, pipefd);
+
+ /* Destroy the shared memory with ID = shmid */
+ ATF_REQUIRE_EQ(0, shmctl(shmid, IPC_RMID, NULL));
+}
+
+ATF_TC_CLEANUP(shmdt_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(shmdt_failure);
+ATF_TC_HEAD(shmdt_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "shmdt(2) call");
+}
+
+ATF_TC_BODY(shmdt_failure, tc)
+{
+ const char *regex = "shmdt.*return,failure : Invalid argument";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, shmdt(NULL));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(shmdt_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(shmctl_rmid_success);
+ATF_TC_HEAD(shmctl_rmid_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "shmctl(2) call for IPC_RMID command");
+}
+
+ATF_TC_BODY(shmctl_rmid_success, tc)
+{
+ /* Create a shared memory segment and obtain the identifier */
+ ATF_REQUIRE((shmid =
+ shmget(IPC_PRIVATE, 1, IPC_CREAT | S_IRUSR)) != -1);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, shmctl(shmid, IPC_RMID, NULL));
+ /* Check the presence of shmid and IPC_RMID in audit record */
+ snprintf(ipcregex, sizeof(ipcregex),
+ "shmctl.*IPC_RMID.*%d.*return,success", shmid);
+ check_audit(fds, ipcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(shmctl_rmid_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(shmctl_rmid_failure);
+ATF_TC_HEAD(shmctl_rmid_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "shmctl(2) call for IPC_RMID command");
+}
+
+ATF_TC_BODY(shmctl_rmid_failure, tc)
+{
+ const char *regex = "shmctl.*IPC_RMID.*return,fail.*Invalid argument";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, shmctl(-1, IPC_RMID, NULL));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(shmctl_rmid_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(shmctl_stat_success);
+ATF_TC_HEAD(shmctl_stat_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "shmctl(2) call for IPC_STAT command");
+}
+
+ATF_TC_BODY(shmctl_stat_success, tc)
+{
+ /* Create a shared memory segment and obtain the identifier */
+ ATF_REQUIRE((shmid =
+ shmget(IPC_PRIVATE, 1, IPC_CREAT | S_IRUSR)) != -1);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, shmctl(shmid, IPC_STAT, &shmbuff));
+ /* Check if shared memory ID and IPC_STAT are present in audit record */
+ snprintf(ipcregex, sizeof(ipcregex),
+ "shmctl.*IPC_STAT.*%d.*return,success", shmid);
+ check_audit(fds, ipcregex, pipefd);
+
+ /* Destroy the shared memory with ID = shmid */
+ ATF_REQUIRE_EQ(0, shmctl(shmid, IPC_RMID, NULL));
+}
+
+ATF_TC_CLEANUP(shmctl_stat_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(shmctl_stat_failure);
+ATF_TC_HEAD(shmctl_stat_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "shmctl(2) call for IPC_STAT command");
+}
+
+ATF_TC_BODY(shmctl_stat_failure, tc)
+{
+ const char *regex = "shmctl.*IPC_STAT.*return,fail.*Invalid argument";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, shmctl(-1, IPC_STAT, &shmbuff));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(shmctl_stat_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(shmctl_set_success);
+ATF_TC_HEAD(shmctl_set_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "shmctl(2) call for IPC_SET command");
+}
+
+ATF_TC_BODY(shmctl_set_success, tc)
+{
+ /* Create a shared memory segment and obtain the identifier */
+ ATF_REQUIRE((shmid =
+ shmget(IPC_PRIVATE, 1, IPC_CREAT | S_IRUSR)) != -1);
+ /* Fill up the shmbuff structure to be used with IPC_SET */
+ ATF_REQUIRE_EQ(0, shmctl(shmid, IPC_STAT, &shmbuff));
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, shmctl(shmid, IPC_SET, &shmbuff));
+ /* Check the presence of shared memory ID in audit record */
+ snprintf(ipcregex, sizeof(ipcregex),
+ "shmctl.*IPC_SET.*%d.*return,success", msqid);
+ check_audit(fds, ipcregex, pipefd);
+
+ /* Destroy the shared memory with ID = shmid */
+ ATF_REQUIRE_EQ(0, shmctl(shmid, IPC_RMID, NULL));
+}
+
+ATF_TC_CLEANUP(shmctl_set_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(shmctl_set_failure);
+ATF_TC_HEAD(shmctl_set_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "shmctl(2) call for IPC_SET command");
+}
+
+ATF_TC_BODY(shmctl_set_failure, tc)
+{
+ const char *regex = "shmctl.*IPC_SET.*return,failure.*Invalid argument";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, shmctl(-1, IPC_SET, &shmbuff));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(shmctl_set_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(shmctl_illegal_command);
+ATF_TC_HEAD(shmctl_illegal_command, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "shmctl(2) call for illegal cmd value");
+}
+
+ATF_TC_BODY(shmctl_illegal_command, tc)
+{
+ /* Create a shared memory segment and obtain the identifier */
+ ATF_REQUIRE((shmid =
+ shmget(IPC_PRIVATE, 1, IPC_CREAT | S_IRUSR)) != -1);
+
+ const char *regex = "shmctl.*illegal command.*fail.*Invalid argument";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, shmctl(shmid, -1, &shmbuff));
+ check_audit(fds, regex, pipefd);
+
+ /* Destroy the shared memory with ID = shmid */
+ ATF_REQUIRE_EQ(0, shmctl(shmid, IPC_RMID, NULL));
+}
+
+ATF_TC_CLEANUP(shmctl_illegal_command, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(semget_success);
+ATF_TC_HEAD(semget_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "semget(2) call");
+}
+
+ATF_TC_BODY(semget_success, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE((semid =
+ semget(IPC_PRIVATE, 1, IPC_CREAT | S_IRUSR)) != -1);
+
+ /* Check the presence of semaphore set ID in audit record */
+ snprintf(ipcregex, sizeof(ipcregex),
+ "semget.*return,success,%d", semid);
+ check_audit(fds, ipcregex, pipefd);
+
+ /* Destroy the semaphore set with ID = semid */
+ ATF_REQUIRE_EQ(0, semctl(semid, 0, IPC_RMID));
+}
+
+ATF_TC_CLEANUP(semget_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(semget_failure);
+ATF_TC_HEAD(semget_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "semget(2) call");
+}
+
+ATF_TC_BODY(semget_failure, tc)
+{
+ pid = getpid();
+ snprintf(ipcregex, sizeof(ipcregex), "semget.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: nsems is a negative number */
+ ATF_REQUIRE_EQ(-1, semget(IPC_PRIVATE, -1, 0));
+ check_audit(fds, ipcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(semget_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(semop_success);
+ATF_TC_HEAD(semop_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "semop(2) call");
+}
+
+ATF_TC_BODY(semop_success, tc)
+{
+ /* Create a semaphore set and obtain the set identifier */
+ ATF_REQUIRE((semid =
+ semget(IPC_PRIVATE, 1, IPC_CREAT | S_IRUSR)) != -1);
+
+ /* Initialize a sembuf structure to operate on semaphore set */
+ struct sembuf sop[1] = {{0, 1, 0}};
+ /* Check the presence of semaphore set ID in audit record */
+ snprintf(ipcregex, sizeof(ipcregex),
+ "semop.*Semaphore IPC.*%d.*return,success", semid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, semop(semid, sop, sizeof(sop)/sizeof(struct sembuf)));
+ check_audit(fds, ipcregex, pipefd);
+
+ /* Destroy the semaphore set with ID = semid */
+ ATF_REQUIRE_EQ(0, semctl(semid, 0, IPC_RMID));
+}
+
+ATF_TC_CLEANUP(semop_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(semop_failure);
+ATF_TC_HEAD(semop_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "semop(2) call");
+}
+
+ATF_TC_BODY(semop_failure, tc)
+{
+ const char *regex = "semop.*0xffff.*return,failure : Invalid argument";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, semop(-1, NULL, 0));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(semop_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(semctl_getval_success);
+ATF_TC_HEAD(semctl_getval_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "semctl(2) call for GETVAL command");
+}
+
+ATF_TC_BODY(semctl_getval_success, tc)
+{
+ /* Create a semaphore set and obtain the set identifier */
+ ATF_REQUIRE((semid =
+ semget(IPC_PRIVATE, 1, IPC_CREAT | S_IRUSR)) != -1);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, semctl(semid, 0, GETVAL));
+ /* Check the presence of semaphore ID and GETVAL in audit record */
+ snprintf(ipcregex, sizeof(ipcregex),
+ "semctl.*GETVAL.*%d.*return,success", semid);
+ check_audit(fds, ipcregex, pipefd);
+
+ /* Destroy the semaphore set with ID = semid */
+ ATF_REQUIRE_EQ(0, semctl(semid, 0, IPC_RMID));
+}
+
+ATF_TC_CLEANUP(semctl_getval_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(semctl_getval_failure);
+ATF_TC_HEAD(semctl_getval_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "semctl(2) call for GETVAL command");
+}
+
+ATF_TC_BODY(semctl_getval_failure, tc)
+{
+ const char *regex = "semctl.*GETVAL.*return,failure : Invalid argument";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, semctl(-1, 0, GETVAL));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(semctl_getval_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(semctl_setval_success);
+ATF_TC_HEAD(semctl_setval_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "semctl(2) call for SETVAL command");
+}
+
+ATF_TC_BODY(semctl_setval_success, tc)
+{
+ /* Create a semaphore set and obtain the set identifier */
+ ATF_REQUIRE((semid =
+ semget(IPC_PRIVATE, 1, IPC_CREAT | S_IRUSR)) != -1);
+
+ semarg.val = 1;
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, semctl(semid, 0, SETVAL, semarg));
+ /* Check the presence of semaphore ID and SETVAL in audit record */
+ snprintf(ipcregex, sizeof(ipcregex),
+ "semctl.*SETVAL.*%d.*return,success", semid);
+ check_audit(fds, ipcregex, pipefd);
+
+ /* Destroy the semaphore set with ID = semid */
+ ATF_REQUIRE_EQ(0, semctl(semid, 0, IPC_RMID));
+}
+
+ATF_TC_CLEANUP(semctl_setval_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(semctl_setval_failure);
+ATF_TC_HEAD(semctl_setval_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "semctl(2) call for SETVAL command");
+}
+
+ATF_TC_BODY(semctl_setval_failure, tc)
+{
+ const char *regex = "semctl.*SETVAL.*return,failure : Invalid argument";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, semctl(-1, 0, SETVAL, semarg));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(semctl_setval_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(semctl_getpid_success);
+ATF_TC_HEAD(semctl_getpid_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "semctl(2) call for GETPID command");
+}
+
+ATF_TC_BODY(semctl_getpid_success, tc)
+{
+ /* Create a semaphore set and obtain the set identifier */
+ ATF_REQUIRE((semid =
+ semget(IPC_PRIVATE, 1, IPC_CREAT | S_IRUSR)) != -1);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, semctl(semid, 0, GETPID));
+ /* Check the presence of semaphore ID and GETVAL in audit record */
+ snprintf(ipcregex, sizeof(ipcregex),
+ "semctl.*GETPID.*%d.*return,success", semid);
+ check_audit(fds, ipcregex, pipefd);
+
+ /* Destroy the semaphore set with ID = semid */
+ ATF_REQUIRE_EQ(0, semctl(semid, 0, IPC_RMID));
+}
+
+ATF_TC_CLEANUP(semctl_getpid_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(semctl_getpid_failure);
+ATF_TC_HEAD(semctl_getpid_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "semctl(2) call for GETPID command");
+}
+
+ATF_TC_BODY(semctl_getpid_failure, tc)
+{
+ const char *regex = "semctl.*GETPID.*return,failure : Invalid argument";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, semctl(-1, 0, GETPID));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(semctl_getpid_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(semctl_getncnt_success);
+ATF_TC_HEAD(semctl_getncnt_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "semctl(2) call for GETNCNT command");
+}
+
+ATF_TC_BODY(semctl_getncnt_success, tc)
+{
+ /* Create a semaphore set and obtain the set identifier */
+ ATF_REQUIRE((semid =
+ semget(IPC_PRIVATE, 1, IPC_CREAT | S_IRUSR)) != -1);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, semctl(semid, 0, GETNCNT));
+ /* Check the presence of semaphore ID and GETNCNT in audit record */
+ snprintf(ipcregex, sizeof(ipcregex),
+ "semctl.*GETNCNT.*%d.*return,success", semid);
+ check_audit(fds, ipcregex, pipefd);
+
+ /* Destroy the semaphore set with ID = semid */
+ ATF_REQUIRE_EQ(0, semctl(semid, 0, IPC_RMID));
+}
+
+ATF_TC_CLEANUP(semctl_getncnt_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(semctl_getncnt_failure);
+ATF_TC_HEAD(semctl_getncnt_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "semctl(2) call for GETNCNT command");
+}
+
+ATF_TC_BODY(semctl_getncnt_failure, tc)
+{
+ const char *regex = "semctl.*GETNCNT.*return,failure.*Invalid argument";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, semctl(-1, 0, GETNCNT));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(semctl_getncnt_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(semctl_getzcnt_success);
+ATF_TC_HEAD(semctl_getzcnt_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "semctl(2) call for GETZCNT command");
+}
+
+ATF_TC_BODY(semctl_getzcnt_success, tc)
+{
+ /* Create a semaphore set and obtain the set identifier */
+ ATF_REQUIRE((semid =
+ semget(IPC_PRIVATE, 1, IPC_CREAT | S_IRUSR)) != -1);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, semctl(semid, 0, GETZCNT));
+ /* Check the presence of semaphore ID and GETZCNT in audit record */
+ snprintf(ipcregex, sizeof(ipcregex),
+ "semctl.*GETZCNT.*%d.*return,success", semid);
+ check_audit(fds, ipcregex, pipefd);
+
+ /* Destroy the semaphore set with ID = semid */
+ ATF_REQUIRE_EQ(0, semctl(semid, 0, IPC_RMID));
+}
+
+ATF_TC_CLEANUP(semctl_getzcnt_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(semctl_getzcnt_failure);
+ATF_TC_HEAD(semctl_getzcnt_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "semctl(2) call for GETZCNT command");
+}
+
+ATF_TC_BODY(semctl_getzcnt_failure, tc)
+{
+ const char *regex = "semctl.*GETZCNT.*return,failure.*Invalid argument";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, semctl(-1, 0, GETZCNT));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(semctl_getzcnt_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(semctl_getall_success);
+ATF_TC_HEAD(semctl_getall_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "semctl(2) call for GETALL command");
+}
+
+ATF_TC_BODY(semctl_getall_success, tc)
+{
+ /* Create a semaphore set and obtain the set identifier */
+ ATF_REQUIRE((semid =
+ semget(IPC_PRIVATE, 1, IPC_CREAT | S_IRUSR)) != -1);
+
+ semarg.array = semvals;
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE(semctl(semid, 0, GETALL, semarg) != -1);
+ /* Check the presence of semaphore ID and GETALL in audit record */
+ snprintf(ipcregex, sizeof(ipcregex),
+ "semctl.*GETALL.*%d.*return,success", semid);
+ check_audit(fds, ipcregex, pipefd);
+
+ /* Destroy the semaphore set with ID = semid */
+ ATF_REQUIRE_EQ(0, semctl(semid, 0, IPC_RMID));
+}
+
+ATF_TC_CLEANUP(semctl_getall_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(semctl_getall_failure);
+ATF_TC_HEAD(semctl_getall_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "semctl(2) call for GETALL command");
+}
+
+ATF_TC_BODY(semctl_getall_failure, tc)
+{
+ const char *regex = "semctl.*GETALL.*return,failure : Invalid argument";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, semctl(-1, 0, GETALL, semarg));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(semctl_getall_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(semctl_setall_success);
+ATF_TC_HEAD(semctl_setall_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "semctl(2) call for SETALL command");
+}
+
+ATF_TC_BODY(semctl_setall_success, tc)
+{
+ /* Create a semaphore set and obtain the set identifier */
+ ATF_REQUIRE((semid =
+ semget(IPC_PRIVATE, 1, IPC_CREAT | S_IRUSR)) != -1);
+
+ semarg.array = semvals;
+ /* Initialize semvals to be used with SETALL */
+ ATF_REQUIRE(semctl(semid, 0, GETALL, semarg) != -1);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, semctl(semid, 0, SETALL, semarg));
+ /* Check the presence of semaphore ID and SETALL in audit record */
+ snprintf(ipcregex, sizeof(ipcregex),
+ "semctl.*SETALL.*%d.*return,success", semid);
+ check_audit(fds, ipcregex, pipefd);
+
+ /* Destroy the semaphore set with ID = semid */
+ ATF_REQUIRE_EQ(0, semctl(semid, 0, IPC_RMID));
+}
+
+ATF_TC_CLEANUP(semctl_setall_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(semctl_setall_failure);
+ATF_TC_HEAD(semctl_setall_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "semctl(2) call for SETALL command");
+}
+
+ATF_TC_BODY(semctl_setall_failure, tc)
+{
+ const char *regex = "semctl.*SETALL.*return,failure : Invalid argument";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, semctl(-1, 0, SETALL, semarg));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(semctl_setall_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(semctl_stat_success);
+ATF_TC_HEAD(semctl_stat_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "semctl(2) call for IPC_STAT command");
+}
+
+ATF_TC_BODY(semctl_stat_success, tc)
+{
+ /* Create a semaphore set and obtain the set identifier */
+ ATF_REQUIRE((semid =
+ semget(IPC_PRIVATE, 1, IPC_CREAT | S_IRUSR)) != -1);
+
+ semarg.buf = &sembuff;
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, semctl(semid, 0, IPC_STAT, semarg));
+ /* Check the presence of semaphore ID and IPC_STAT in audit record */
+ snprintf(ipcregex, sizeof(ipcregex),
+ "semctl.*IPC_STAT.*%d.*return,success", semid);
+ check_audit(fds, ipcregex, pipefd);
+
+ /* Destroy the semaphore set with ID = semid */
+ ATF_REQUIRE_EQ(0, semctl(semid, 0, IPC_RMID));
+}
+
+ATF_TC_CLEANUP(semctl_stat_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(semctl_stat_failure);
+ATF_TC_HEAD(semctl_stat_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "semctl(2) call for IPC_STAT command");
+}
+
+ATF_TC_BODY(semctl_stat_failure, tc)
+{
+ const char *regex = "semctl.*IPC_STAT.*return,fail.*Invalid argument";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, semctl(-1, 0, IPC_STAT, semarg));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(semctl_stat_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(semctl_set_success);
+ATF_TC_HEAD(semctl_set_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "semctl(2) call for IPC_SET command");
+}
+
+ATF_TC_BODY(semctl_set_success, tc)
+{
+ /* Create a semaphore set and obtain the set identifier */
+ ATF_REQUIRE((semid =
+ semget(IPC_PRIVATE, 1, IPC_CREAT | S_IRUSR)) != -1);
+
+ semarg.buf = &sembuff;
+ /* Fill up the sembuff structure to be used with IPC_SET */
+ ATF_REQUIRE_EQ(0, semctl(semid, 0, IPC_STAT, semarg));
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, semctl(semid, 0, IPC_SET, semarg));
+ /* Check the presence of semaphore ID and IPC_SET in audit record */
+ snprintf(ipcregex, sizeof(ipcregex),
+ "semctl.*IPC_SET.*%d.*return,success", semid);
+ check_audit(fds, ipcregex, pipefd);
+
+ /* Destroy the semaphore set with ID = semid */
+ ATF_REQUIRE_EQ(0, semctl(semid, 0, IPC_RMID));
+}
+
+ATF_TC_CLEANUP(semctl_set_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(semctl_set_failure);
+ATF_TC_HEAD(semctl_set_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "semctl(2) call for IPC_SET command");
+}
+
+ATF_TC_BODY(semctl_set_failure, tc)
+{
+ /* Create a semaphore set and obtain the set identifier */
+ ATF_REQUIRE((semid =
+ semget(IPC_PRIVATE, 1, IPC_CREAT | S_IRUSR)) != -1);
+
+ semarg.buf = &sembuff;
+ /* Fill up the sembuff structure to be used with IPC_SET */
+ ATF_REQUIRE_EQ(0, semctl(semid, 0, IPC_STAT, semarg));
+
+ const char *regex = "semctl.*IPC_SET.*return,failure.*Invalid argument";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, semctl(-1, 0, IPC_SET, semarg));
+ check_audit(fds, regex, pipefd);
+
+ /* Destroy the semaphore set with ID = semid */
+ ATF_REQUIRE_EQ(0, semctl(semid, 0, IPC_RMID));
+}
+
+ATF_TC_CLEANUP(semctl_set_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(semctl_rmid_success);
+ATF_TC_HEAD(semctl_rmid_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "semctl(2) call for IPC_RMID command");
+}
+
+ATF_TC_BODY(semctl_rmid_success, tc)
+{
+ /* Create a semaphore set and obtain the set identifier */
+ ATF_REQUIRE((semid =
+ semget(IPC_PRIVATE, 1, IPC_CREAT | S_IRUSR)) != -1);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, semctl(semid, 0, IPC_RMID, semarg));
+ /* Check the presence of semaphore ID and IPC_RMID in audit record */
+ snprintf(ipcregex, sizeof(ipcregex),
+ "semctl.*IPC_RMID.*%d.*return,success", semid);
+ check_audit(fds, ipcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(semctl_rmid_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(semctl_rmid_failure);
+ATF_TC_HEAD(semctl_rmid_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "semctl(2) call for IPC_RMID command");
+}
+
+ATF_TC_BODY(semctl_rmid_failure, tc)
+{
+ const char *regex = "semctl.*IPC_RMID.*return,fail.*Invalid argument";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, semctl(-1, 0, IPC_RMID, semarg));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(semctl_rmid_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(semctl_illegal_command);
+ATF_TC_HEAD(semctl_illegal_command, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "semctl(2) call for illegal cmd value");
+}
+
+ATF_TC_BODY(semctl_illegal_command, tc)
+{
+ /* Create a semaphore set and obtain the set identifier */
+ ATF_REQUIRE((semid =
+ semget(IPC_PRIVATE, 1, IPC_CREAT | S_IRUSR)) != -1);
+
+ const char *regex = "semctl.*illegal command.*fail.*Invalid argument";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, semctl(semid, 0, -1));
+ check_audit(fds, regex, pipefd);
+
+ /* Destroy the semaphore set with ID = semid */
+ ATF_REQUIRE_EQ(0, semctl(semid, 0, IPC_RMID));
+}
+
+ATF_TC_CLEANUP(semctl_illegal_command, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(shm_open_success);
+ATF_TC_HEAD(shm_open_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "shm_open(2) call");
+}
+
+ATF_TC_BODY(shm_open_success, tc)
+{
+ pid = getpid();
+ snprintf(ipcregex, sizeof(ipcregex), "shm_open.*%d.*ret.*success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE(shm_open(SHM_ANON, O_CREAT | O_TRUNC | O_RDWR, 0600) != -1);
+ check_audit(fds, ipcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(shm_open_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(shm_open_failure);
+ATF_TC_HEAD(shm_open_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "shm_open(2) call");
+}
+
+ATF_TC_BODY(shm_open_failure, tc)
+{
+ const char *regex = "shm_open.*fileforaudit.*return,failure";
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: File does not exist */
+ ATF_REQUIRE_EQ(-1, shm_open(path, O_TRUNC | O_RDWR, 0600));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(shm_open_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(shm_unlink_success);
+ATF_TC_HEAD(shm_unlink_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "shm_unlink(2) call");
+}
+
+ATF_TC_BODY(shm_unlink_success, tc)
+{
+ /* Build an absolute path to a file in the test-case directory */
+ char dirpath[PATH_MAX];
+ ATF_REQUIRE(getcwd(dirpath, sizeof(dirpath)) != NULL);
+ strlcat(dirpath, path, sizeof(dirpath));
+ ATF_REQUIRE(shm_open(dirpath, O_CREAT | O_TRUNC | O_RDWR, 0600) != -1);
+
+ const char *regex = "shm_unlink.*fileforaudit.*return,success";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, shm_unlink(dirpath));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(shm_unlink_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(shm_unlink_failure);
+ATF_TC_HEAD(shm_unlink_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "shm_unlink(2) call");
+}
+
+ATF_TC_BODY(shm_unlink_failure, tc)
+{
+ const char *regex = "shm_unlink.*fileforaudit.*return,failure";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, shm_unlink(path));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(shm_unlink_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(pipe_success);
+ATF_TC_HEAD(pipe_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "pipe(2) call");
+}
+
+ATF_TC_BODY(pipe_success, tc)
+{
+ int filedesc[2];
+ pid = getpid();
+ snprintf(ipcregex, sizeof(ipcregex), "pipe.*%d.*return,success", pid);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, pipe(filedesc));
+ check_audit(fds, ipcregex, pipefd);
+
+ close(filedesc[0]);
+ close(filedesc[1]);
+}
+
+ATF_TC_CLEANUP(pipe_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(pipe_failure);
+ATF_TC_HEAD(pipe_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "pipe(2) call");
+}
+
+ATF_TC_BODY(pipe_failure, tc)
+{
+ pid = getpid();
+ snprintf(ipcregex, sizeof(ipcregex), "pipe.*%d.*return.failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, pipe(NULL));
+ check_audit(fds, ipcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(pipe_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(posix_openpt_success);
+ATF_TC_HEAD(posix_openpt_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "posix_openpt(2) call");
+}
+
+ATF_TC_BODY(posix_openpt_success, tc)
+{
+ int filedesc;
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE((filedesc = posix_openpt(O_RDWR | O_NOCTTY)) != -1);
+ /* Check for the presence of filedesc in the audit record */
+ snprintf(ipcregex, sizeof(ipcregex),
+ "posix_openpt.*return,success,%d", filedesc);
+ check_audit(fds, ipcregex, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(posix_openpt_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(posix_openpt_failure);
+ATF_TC_HEAD(posix_openpt_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "posix_openpt(2) call");
+}
+
+ATF_TC_BODY(posix_openpt_failure, tc)
+{
+ const char *regex = "posix_openpt.*return,failure : Invalid argument";
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, posix_openpt(-1));
+ check_audit(fds, regex, pipefd);
+}
+
+ATF_TC_CLEANUP(posix_openpt_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, msgget_success);
+ ATF_TP_ADD_TC(tp, msgget_failure);
+ ATF_TP_ADD_TC(tp, msgsnd_success);
+ ATF_TP_ADD_TC(tp, msgsnd_failure);
+ ATF_TP_ADD_TC(tp, msgrcv_success);
+ ATF_TP_ADD_TC(tp, msgrcv_failure);
+
+ ATF_TP_ADD_TC(tp, msgctl_rmid_success);
+ ATF_TP_ADD_TC(tp, msgctl_rmid_failure);
+ ATF_TP_ADD_TC(tp, msgctl_stat_success);
+ ATF_TP_ADD_TC(tp, msgctl_stat_failure);
+ ATF_TP_ADD_TC(tp, msgctl_set_success);
+ ATF_TP_ADD_TC(tp, msgctl_set_failure);
+ ATF_TP_ADD_TC(tp, msgctl_illegal_command);
+
+ ATF_TP_ADD_TC(tp, shmget_success);
+ ATF_TP_ADD_TC(tp, shmget_failure);
+ ATF_TP_ADD_TC(tp, shmat_success);
+ ATF_TP_ADD_TC(tp, shmat_failure);
+ ATF_TP_ADD_TC(tp, shmdt_success);
+ ATF_TP_ADD_TC(tp, shmdt_failure);
+
+ ATF_TP_ADD_TC(tp, shmctl_rmid_success);
+ ATF_TP_ADD_TC(tp, shmctl_rmid_failure);
+ ATF_TP_ADD_TC(tp, shmctl_stat_success);
+ ATF_TP_ADD_TC(tp, shmctl_stat_failure);
+ ATF_TP_ADD_TC(tp, shmctl_set_success);
+ ATF_TP_ADD_TC(tp, shmctl_set_failure);
+ ATF_TP_ADD_TC(tp, shmctl_illegal_command);
+
+ ATF_TP_ADD_TC(tp, semget_success);
+ ATF_TP_ADD_TC(tp, semget_failure);
+ ATF_TP_ADD_TC(tp, semop_success);
+ ATF_TP_ADD_TC(tp, semop_failure);
+
+ ATF_TP_ADD_TC(tp, semctl_getval_success);
+ ATF_TP_ADD_TC(tp, semctl_getval_failure);
+ ATF_TP_ADD_TC(tp, semctl_setval_success);
+ ATF_TP_ADD_TC(tp, semctl_setval_failure);
+ ATF_TP_ADD_TC(tp, semctl_getpid_success);
+ ATF_TP_ADD_TC(tp, semctl_getpid_failure);
+ ATF_TP_ADD_TC(tp, semctl_getncnt_success);
+ ATF_TP_ADD_TC(tp, semctl_getncnt_failure);
+ ATF_TP_ADD_TC(tp, semctl_getzcnt_success);
+ ATF_TP_ADD_TC(tp, semctl_getzcnt_failure);
+ ATF_TP_ADD_TC(tp, semctl_getall_success);
+ ATF_TP_ADD_TC(tp, semctl_getall_failure);
+ ATF_TP_ADD_TC(tp, semctl_setall_success);
+ ATF_TP_ADD_TC(tp, semctl_setall_failure);
+ ATF_TP_ADD_TC(tp, semctl_stat_success);
+ ATF_TP_ADD_TC(tp, semctl_stat_failure);
+ ATF_TP_ADD_TC(tp, semctl_set_success);
+ ATF_TP_ADD_TC(tp, semctl_set_failure);
+ ATF_TP_ADD_TC(tp, semctl_rmid_success);
+ ATF_TP_ADD_TC(tp, semctl_rmid_failure);
+ ATF_TP_ADD_TC(tp, semctl_illegal_command);
+
+ ATF_TP_ADD_TC(tp, shm_open_success);
+ ATF_TP_ADD_TC(tp, shm_open_failure);
+ ATF_TP_ADD_TC(tp, shm_unlink_success);
+ ATF_TP_ADD_TC(tp, shm_unlink_failure);
+
+ ATF_TP_ADD_TC(tp, pipe_success);
+ ATF_TP_ADD_TC(tp, pipe_failure);
+ ATF_TP_ADD_TC(tp, posix_openpt_success);
+ ATF_TP_ADD_TC(tp, posix_openpt_failure);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/audit/ioctl.c b/tests/sys/audit/ioctl.c
new file mode 100644
index 000000000000..a17ec7611bac
--- /dev/null
+++ b/tests/sys/audit/ioctl.c
@@ -0,0 +1,101 @@
+/*-
+ * Copyright (c) 2018 Aniket Pandey
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * SUCH DAMAGE.
+ */
+
+#include <sys/ioctl.h>
+
+#include <bsm/libbsm.h>
+#include <security/audit/audit_ioctl.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "utils.h"
+
+static int filedesc;
+static char ioregex[80];
+static const char *auclass = "io";
+static struct pollfd fds[1];
+static unsigned long request = AUDITPIPE_FLUSH;
+
+
+ATF_TC_WITH_CLEANUP(ioctl_success);
+ATF_TC_HEAD(ioctl_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "ioctl(2) call");
+}
+
+ATF_TC_BODY(ioctl_success, tc)
+{
+ /* auditpipe(4) supports quite a few ioctls */
+ ATF_REQUIRE((filedesc = open("/dev/auditpipe", O_RDONLY)) != -1);
+ /* Prepare the regex to be checked in the audit record */
+ snprintf(ioregex, sizeof(ioregex),
+ "ioctl.*%#lx.*%#x.*return,success", request, filedesc);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE(ioctl(filedesc, request) != -1);
+ check_audit(fds, ioregex, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(ioctl_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(ioctl_failure);
+ATF_TC_HEAD(ioctl_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "ioctl(2) call");
+}
+
+ATF_TC_BODY(ioctl_failure, tc)
+{
+ snprintf(ioregex, sizeof(ioregex),
+ "ioctl.*%#lx.*return,failure : Bad file descriptor", request);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid file descriptor */
+ ATF_REQUIRE_EQ(-1, ioctl(-1, request));
+ check_audit(fds, ioregex, pipefd);
+}
+
+ATF_TC_CLEANUP(ioctl_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, ioctl_success);
+ ATF_TP_ADD_TC(tp, ioctl_failure);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/audit/miscellaneous.c b/tests/sys/audit/miscellaneous.c
new file mode 100644
index 000000000000..f2abd8877b10
--- /dev/null
+++ b/tests/sys/audit/miscellaneous.c
@@ -0,0 +1,225 @@
+/*-
+ * Copyright (c) 2018 Aniket Pandey
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+#include <bsm/audit.h>
+#include <machine/sysarch.h>
+
+#include <atf-c.h>
+#include <unistd.h>
+
+#include "utils.h"
+
+static pid_t pid;
+static char miscreg[80];
+static struct pollfd fds[1];
+static const char *auclass = "ot";
+
+
+/*
+ * Success case of audit(2) is skipped for now as the behaviour is quite
+ * undeterministic. It will be added when the intermittency is resolved.
+ */
+
+
+ATF_TC_WITH_CLEANUP(audit_failure);
+ATF_TC_HEAD(audit_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "audit(2) call");
+}
+
+ATF_TC_BODY(audit_failure, tc)
+{
+ pid = getpid();
+ snprintf(miscreg, sizeof(miscreg), "audit.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid argument */
+ ATF_REQUIRE_EQ(-1, audit(NULL, -1));
+ check_audit(fds, miscreg, pipefd);
+}
+
+ATF_TC_CLEANUP(audit_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(sysarch_success);
+ATF_TC_HEAD(sysarch_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "sysarch(2) call");
+}
+
+ATF_TC_BODY(sysarch_success, tc)
+{
+ pid = getpid();
+ snprintf(miscreg, sizeof(miscreg), "sysarch.*%d.*return,success", pid);
+
+ /* Set sysnum to the syscall corresponding to the system architecture */
+#if defined(I386_GET_IOPERM) /* i386 */
+ struct i386_ioperm_args i3sysarg;
+ bzero(&i3sysarg, sizeof(i3sysarg));
+
+#elif defined(AMD64_GET_FSBASE) /* amd64 */
+ register_t amd64arg;
+
+#elif defined(ARM_SYNC_ICACHE) /* ARM */
+ struct arm_sync_icache_args armsysarg;
+ bzero(&armsysarg, sizeof(armsysarg));
+
+#elif defined(SPARC_UTRAP_INSTALL) /* Sparc64 */
+ struct sparc_utrap_args handler = {
+ .type = UT_DIVISION_BY_ZERO,
+ /* We don't want to change the previous handlers */
+ .new_precise = (void *)UTH_NOCHANGE,
+ .new_deferred = (void *)UTH_NOCHANGE,
+ .old_precise = NULL,
+ .old_deferred = NULL
+ };
+
+ struct sparc_utrap_install_args sparc64arg = {
+ .num = ST_DIVISION_BY_ZERO,
+ .handlers = &handler
+ };
+#else
+ /* For PowerPC, ARM64, RISCV archs, sysarch(2) is not supported */
+ atf_tc_skip("sysarch(2) is not supported for the system architecture");
+#endif
+
+ FILE *pipefd = setup(fds, auclass);
+#if defined(I386_GET_IOPERM)
+ ATF_REQUIRE_EQ(0, sysarch(I386_GET_IOPERM, &i3sysarg));
+#elif defined(AMD64_GET_FSBASE)
+ ATF_REQUIRE_EQ(0, sysarch(AMD64_GET_FSBASE, &amd64arg));
+#elif defined(ARM_SYNC_ICACHE)
+ ATF_REQUIRE_EQ(0, sysarch(ARM_SYNC_ICACHE, &armsysarg));
+#elif defined(SPARC_UTRAP_INSTALL)
+ ATF_REQUIRE_EQ(0, sysarch(SPARC_UTRAP_INSTALL, &sparc64arg));
+#endif
+ check_audit(fds, miscreg, pipefd);
+}
+
+ATF_TC_CLEANUP(sysarch_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(sysarch_failure);
+ATF_TC_HEAD(sysarch_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "sysarch(2) call for any architecture");
+}
+
+ATF_TC_BODY(sysarch_failure, tc)
+{
+ pid = getpid();
+ snprintf(miscreg, sizeof(miscreg), "sysarch.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid argument and Bad address */
+ ATF_REQUIRE_EQ(-1, sysarch(-1, NULL));
+ check_audit(fds, miscreg, pipefd);
+}
+
+ATF_TC_CLEANUP(sysarch_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(sysctl_success);
+ATF_TC_HEAD(sysctl_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "sysctl(3) call");
+}
+
+ATF_TC_BODY(sysctl_success, tc)
+{
+ int mib[2], maxproc;
+ size_t proclen;
+
+ /* Set mib to retrieve the maximum number of allowed processes */
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_MAXPROC;
+ proclen = sizeof(maxproc);
+
+ pid = getpid();
+ snprintf(miscreg, sizeof(miscreg), "sysctl.*%d.*return,success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, sysctl(mib, 2, &maxproc, &proclen, NULL, 0));
+ check_audit(fds, miscreg, pipefd);
+}
+
+ATF_TC_CLEANUP(sysctl_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(sysctl_failure);
+ATF_TC_HEAD(sysctl_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "sysctl(3) call");
+}
+
+ATF_TC_BODY(sysctl_failure, tc)
+{
+ pid = getpid();
+ snprintf(miscreg, sizeof(miscreg), "sysctl.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid arguments */
+ ATF_REQUIRE_EQ(-1, sysctl(NULL, 0, NULL, NULL, NULL, 0));
+ check_audit(fds, miscreg, pipefd);
+}
+
+ATF_TC_CLEANUP(sysctl_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, audit_failure);
+
+ ATF_TP_ADD_TC(tp, sysarch_success);
+ ATF_TP_ADD_TC(tp, sysarch_failure);
+
+ ATF_TP_ADD_TC(tp, sysctl_success);
+ ATF_TP_ADD_TC(tp, sysctl_failure);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/audit/network.c b/tests/sys/audit/network.c
new file mode 100644
index 000000000000..2d9aed34111a
--- /dev/null
+++ b/tests/sys/audit/network.c
@@ -0,0 +1,1181 @@
+/*-
+ * Copyright (c) 2018 Aniket Pandey
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <unistd.h>
+
+#include "utils.h"
+
+#define MAX_DATA 128
+#define SERVER_PATH "server"
+
+static pid_t pid;
+static mode_t mode = 0777;
+static int sockfd, sockfd2, connectfd;
+static ssize_t data_bytes;
+static socklen_t len = sizeof(struct sockaddr_un);
+static struct iovec io1, io2;
+static struct pollfd fds[1];
+static struct sockaddr_un server;
+static struct msghdr sendbuf, recvbuf;
+static char extregex[MAX_DATA];
+static char data[MAX_DATA];
+static char msgbuff[MAX_DATA] = "This message does not exist";
+static const char *auclass = "nt";
+static const char *path = "fileforaudit";
+static const char *nosupregex = "return,failure : Address family "
+ "not supported by protocol family";
+static const char *invalregex = "return,failure : Bad file descriptor";
+
+/*
+ * Initialize iovec structure to be used as a field of struct msghdr
+ */
+static void
+init_iov(struct iovec *io, char msgbuf[], int datalen)
+{
+ io->iov_base = msgbuf;
+ io->iov_len = datalen;
+}
+
+/*
+ * Initialize msghdr structure for communication via datagram sockets
+ */
+static void
+init_msghdr(struct msghdr *hdrbuf, struct iovec *io, struct sockaddr_un *addr)
+{
+ socklen_t length;
+
+ bzero(hdrbuf, sizeof(*hdrbuf));
+ length = (socklen_t)sizeof(struct sockaddr_un);
+ hdrbuf->msg_name = addr;
+ hdrbuf->msg_namelen = length;
+ hdrbuf->msg_iov = io;
+ hdrbuf->msg_iovlen = 1;
+}
+
+/*
+ * Variadic function to close socket descriptors
+ */
+static void
+close_sockets(int count, ...)
+{
+ int sockd;
+ va_list socklist;
+ va_start(socklist, count);
+ for (sockd = 0; sockd < count; sockd++) {
+ close(va_arg(socklist, int));
+ }
+ va_end(socklist);
+}
+
+/*
+ * Assign local filesystem address to a Unix domain socket
+ */
+static void
+assign_address(struct sockaddr_un *serveraddr)
+{
+ memset(serveraddr, 0, sizeof(*serveraddr));
+ serveraddr->sun_family = AF_UNIX;
+ strcpy(serveraddr->sun_path, SERVER_PATH);
+}
+
+
+ATF_TC_WITH_CLEANUP(socket_success);
+ATF_TC_HEAD(socket_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "socket(2) call");
+}
+
+ATF_TC_BODY(socket_success, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE((sockfd = socket(PF_UNIX, SOCK_STREAM, 0)) != -1);
+ /* Check the presence of sockfd in audit record */
+ snprintf(extregex, sizeof(extregex), "socket.*ret.*success,%d", sockfd);
+ check_audit(fds, extregex, pipefd);
+ close(sockfd);
+}
+
+ATF_TC_CLEANUP(socket_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(socket_failure);
+ATF_TC_HEAD(socket_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "socket(2) call");
+}
+
+ATF_TC_BODY(socket_failure, tc)
+{
+ snprintf(extregex, sizeof(extregex), "socket.*%s", nosupregex);
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Unsupported value of 'domain' argument: 0 */
+ ATF_REQUIRE_EQ(-1, socket(0, SOCK_STREAM, 0));
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(socket_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(socketpair_success);
+ATF_TC_HEAD(socketpair_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "socketpair(2) call");
+}
+
+ATF_TC_BODY(socketpair_success, tc)
+{
+ int sv[2];
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, socketpair(PF_UNIX, SOCK_STREAM, 0, sv));
+
+ /* Check for 0x0 (argument 3: default protocol) in the audit record */
+ snprintf(extregex, sizeof(extregex), "socketpair.*0x0.*return,success");
+ check_audit(fds, extregex, pipefd);
+ close_sockets(2, sv[0], sv[1]);
+}
+
+ATF_TC_CLEANUP(socketpair_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(socketpair_failure);
+ATF_TC_HEAD(socketpair_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "socketpair(2) call");
+}
+
+ATF_TC_BODY(socketpair_failure, tc)
+{
+ snprintf(extregex, sizeof(extregex), "socketpair.*%s", nosupregex);
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Unsupported value of 'domain' argument: 0 */
+ ATF_REQUIRE_EQ(-1, socketpair(0, SOCK_STREAM, 0, NULL));
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(socketpair_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(setsockopt_success);
+ATF_TC_HEAD(setsockopt_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "setsockopt(2) call");
+}
+
+ATF_TC_BODY(setsockopt_success, tc)
+{
+ int tr = 1;
+ ATF_REQUIRE((sockfd = socket(PF_UNIX, SOCK_STREAM, 0)) != -1);
+ /* Check the presence of sockfd in audit record */
+ snprintf(extregex, sizeof(extregex),
+ "setsockopt.*0x%x.*return,success", sockfd);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, setsockopt(sockfd, SOL_SOCKET,
+ SO_REUSEADDR, &tr, sizeof(int)));
+ check_audit(fds, extregex, pipefd);
+ close(sockfd);
+}
+
+ATF_TC_CLEANUP(setsockopt_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(setsockopt_failure);
+ATF_TC_HEAD(setsockopt_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "setsockopt(2) call");
+}
+
+ATF_TC_BODY(setsockopt_failure, tc)
+{
+ snprintf(extregex, sizeof(extregex), "setsockopt.*%s", invalregex);
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid socket descriptor */
+ ATF_REQUIRE_EQ(-1, setsockopt(-1, SOL_SOCKET, 0, NULL, 0));
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(setsockopt_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(bind_success);
+ATF_TC_HEAD(bind_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "bind(2) call");
+}
+
+ATF_TC_BODY(bind_success, tc)
+{
+ assign_address(&server);
+ /* Preliminary socket setup */
+ ATF_REQUIRE((sockfd = socket(PF_UNIX, SOCK_STREAM, 0)) != -1);
+ /* Check the presence of AF_UNIX address path in audit record */
+ snprintf(extregex, sizeof(extregex),
+ "bind.*unix.*%s.*return,success", SERVER_PATH);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, bind(sockfd, (struct sockaddr *)&server, len));
+ check_audit(fds, extregex, pipefd);
+ close(sockfd);
+}
+
+ATF_TC_CLEANUP(bind_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(bind_failure);
+ATF_TC_HEAD(bind_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "bind(2) call");
+}
+
+ATF_TC_BODY(bind_failure, tc)
+{
+ assign_address(&server);
+ /* Check the presence of AF_UNIX path in audit record */
+ snprintf(extregex, sizeof(extregex),
+ "bind.*%s.*return,failure", SERVER_PATH);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid socket descriptor */
+ ATF_REQUIRE_EQ(-1, bind(0, (struct sockaddr *)&server, len));
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(bind_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(bindat_success);
+ATF_TC_HEAD(bindat_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "bindat(2) call");
+}
+
+ATF_TC_BODY(bindat_success, tc)
+{
+ assign_address(&server);
+ /* Preliminary socket setup */
+ ATF_REQUIRE((sockfd = socket(PF_UNIX, SOCK_STREAM, 0)) != -1);
+ /* Check the presence of socket descriptor in audit record */
+ snprintf(extregex, sizeof(extregex),
+ "bindat.*0x%x.*return,success", sockfd);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, bindat(AT_FDCWD, sockfd,
+ (struct sockaddr *)&server, len));
+ check_audit(fds, extregex, pipefd);
+ close(sockfd);
+}
+
+ATF_TC_CLEANUP(bindat_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(bindat_failure);
+ATF_TC_HEAD(bindat_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "bindat(2) call");
+}
+
+ATF_TC_BODY(bindat_failure, tc)
+{
+ assign_address(&server);
+ snprintf(extregex, sizeof(extregex), "bindat.*%s", invalregex);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid socket descriptor */
+ ATF_REQUIRE_EQ(-1, bindat(AT_FDCWD, -1,
+ (struct sockaddr *)&server, len));
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(bindat_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(listen_success);
+ATF_TC_HEAD(listen_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "listen(2) call");
+}
+
+ATF_TC_BODY(listen_success, tc)
+{
+ assign_address(&server);
+ /* Preliminary socket setup */
+ ATF_REQUIRE((sockfd = socket(PF_UNIX, SOCK_STREAM, 0)) != -1);
+ ATF_REQUIRE_EQ(0, bind(sockfd, (struct sockaddr *)&server, len));
+ /* Check the presence of socket descriptor in the audit record */
+ snprintf(extregex, sizeof(extregex),
+ "listen.*0x%x.*return,success", sockfd);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, listen(sockfd, 1));
+ check_audit(fds, extregex, pipefd);
+ close(sockfd);
+}
+
+ATF_TC_CLEANUP(listen_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(listen_failure);
+ATF_TC_HEAD(listen_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "listen(2) call");
+}
+
+ATF_TC_BODY(listen_failure, tc)
+{
+ snprintf(extregex, sizeof(extregex), "listen.*%s", invalregex);
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid socket descriptor */
+ ATF_REQUIRE_EQ(-1, listen(-1, 1));
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(listen_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(connect_success);
+ATF_TC_HEAD(connect_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "connect(2) call");
+}
+
+ATF_TC_BODY(connect_success, tc)
+{
+ assign_address(&server);
+ /* Setup a server socket and bind to the specified address */
+ ATF_REQUIRE((sockfd = socket(PF_UNIX, SOCK_STREAM, 0)) != -1);
+ ATF_REQUIRE_EQ(0, bind(sockfd, (struct sockaddr *)&server, len));
+ ATF_REQUIRE_EQ(0, listen(sockfd, 1));
+
+ /* Set up "blocking" client socket */
+ ATF_REQUIRE((sockfd2 = socket(PF_UNIX, SOCK_STREAM, 0)) != -1);
+
+ /* Audit record must contain AF_UNIX address path & sockfd2 */
+ snprintf(extregex, sizeof(extregex),
+ "connect.*0x%x.*%s.*success", sockfd2, SERVER_PATH);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, connect(sockfd2, (struct sockaddr *)&server, len));
+ check_audit(fds, extregex, pipefd);
+
+ /* Close all socket descriptors */
+ close_sockets(2, sockfd, sockfd2);
+}
+
+ATF_TC_CLEANUP(connect_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(connect_failure);
+ATF_TC_HEAD(connect_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "connect(2) call");
+}
+
+ATF_TC_BODY(connect_failure, tc)
+{
+ assign_address(&server);
+ /* Audit record must contain AF_UNIX address path */
+ snprintf(extregex, sizeof(extregex),
+ "connect.*%s.*return,failure", SERVER_PATH);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid socket descriptor */
+ ATF_REQUIRE_EQ(-1, connect(-1, (struct sockaddr *)&server, len));
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(connect_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(connectat_success);
+ATF_TC_HEAD(connectat_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "connectat(2) call");
+}
+
+ATF_TC_BODY(connectat_success, tc)
+{
+ assign_address(&server);
+ /* Setup a server socket and bind to the specified address */
+ ATF_REQUIRE((sockfd = socket(PF_UNIX, SOCK_STREAM, 0)) != -1);
+ ATF_REQUIRE_EQ(0, bind(sockfd, (struct sockaddr *)&server, len));
+ ATF_REQUIRE_EQ(0, listen(sockfd, 1));
+
+ /* Set up "blocking" client socket */
+ ATF_REQUIRE((sockfd2 = socket(PF_UNIX, SOCK_STREAM, 0)) != -1);
+
+ /* Audit record must contain sockfd2 */
+ snprintf(extregex, sizeof(extregex),
+ "connectat.*0x%x.*return,success", sockfd2);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, connectat(AT_FDCWD, sockfd2,
+ (struct sockaddr *)&server, len));
+ check_audit(fds, extregex, pipefd);
+
+ /* Close all socket descriptors */
+ close_sockets(2, sockfd, sockfd2);
+}
+
+ATF_TC_CLEANUP(connectat_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(connectat_failure);
+ATF_TC_HEAD(connectat_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "connectat(2) call");
+}
+
+ATF_TC_BODY(connectat_failure, tc)
+{
+ assign_address(&server);
+ snprintf(extregex, sizeof(extregex), "connectat.*%s", invalregex);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid socket descriptor */
+ ATF_REQUIRE_EQ(-1, connectat(AT_FDCWD, -1,
+ (struct sockaddr *)&server, len));
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(connectat_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(accept_success);
+ATF_TC_HEAD(accept_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "accept(2) call");
+}
+
+ATF_TC_BODY(accept_success, tc)
+{
+ assign_address(&server);
+ /* Setup a server socket and bind to the specified address */
+ ATF_REQUIRE((sockfd = socket(PF_UNIX, SOCK_STREAM, 0)) != -1);
+ ATF_REQUIRE_EQ(0, bind(sockfd, (struct sockaddr *)&server, len));
+ ATF_REQUIRE_EQ(0, listen(sockfd, 1));
+
+ /* Set up "blocking" client socket */
+ ATF_REQUIRE((sockfd2 = socket(PF_UNIX, SOCK_STREAM, 0)) != -1);
+ ATF_REQUIRE_EQ(0, connect(sockfd2, (struct sockaddr *)&server, len));
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE((connectfd = accept(sockfd, NULL, &len)) != -1);
+
+ /* Audit record must contain connectfd & sockfd */
+ snprintf(extregex, sizeof(extregex),
+ "accept.*0x%x.*return,success,%d", sockfd, connectfd);
+ check_audit(fds, extregex, pipefd);
+
+ /* Close all socket descriptors */
+ close_sockets(3, sockfd, sockfd2, connectfd);
+}
+
+ATF_TC_CLEANUP(accept_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(accept_failure);
+ATF_TC_HEAD(accept_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "accept(2) call");
+}
+
+ATF_TC_BODY(accept_failure, tc)
+{
+ snprintf(extregex, sizeof(extregex), "accept.*%s", invalregex);
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid socket descriptor */
+ ATF_REQUIRE_EQ(-1, accept(-1, NULL, NULL));
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(accept_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(send_success);
+ATF_TC_HEAD(send_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "send(2) call");
+}
+
+ATF_TC_BODY(send_success, tc)
+{
+ assign_address(&server);
+ /* Setup a server socket and bind to the specified address */
+ ATF_REQUIRE((sockfd = socket(PF_UNIX, SOCK_STREAM, 0)) != -1);
+ ATF_REQUIRE_EQ(0, bind(sockfd, (struct sockaddr *)&server, len));
+ ATF_REQUIRE_EQ(0, listen(sockfd, 1));
+
+ /* Set up "blocking" client and connect with non-blocking server */
+ ATF_REQUIRE((sockfd2 = socket(PF_UNIX, SOCK_STREAM, 0)) != -1);
+ ATF_REQUIRE_EQ(0, connect(sockfd2, (struct sockaddr *)&server, len));
+ ATF_REQUIRE((connectfd = accept(sockfd, NULL, &len)) != -1);
+
+ /* Send a sample message to the connected socket */
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE((data_bytes =
+ send(sockfd2, msgbuff, strlen(msgbuff), 0)) != -1);
+
+ /* Audit record must contain sockfd2 and data_bytes */
+ snprintf(extregex, sizeof(extregex),
+ "send.*0x%x.*return,success,%zd", sockfd2, data_bytes);
+ check_audit(fds, extregex, pipefd);
+
+ /* Close all socket descriptors */
+ close_sockets(3, sockfd, sockfd2, connectfd);
+}
+
+ATF_TC_CLEANUP(send_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(send_failure);
+ATF_TC_HEAD(send_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "send(2) call");
+}
+
+ATF_TC_BODY(send_failure, tc)
+{
+ snprintf(extregex, sizeof(extregex), "send.*%s", invalregex);
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid socket descriptor */
+ ATF_REQUIRE_EQ(-1, send(-1, NULL, 0, 0));
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(send_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(recv_success);
+ATF_TC_HEAD(recv_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "recv(2) call");
+}
+
+ATF_TC_BODY(recv_success, tc)
+{
+ assign_address(&server);
+ /* Setup a server socket and bind to the specified address */
+ ATF_REQUIRE((sockfd = socket(PF_UNIX, SOCK_STREAM, 0)) != -1);
+ ATF_REQUIRE_EQ(0, bind(sockfd, (struct sockaddr *)&server, len));
+ ATF_REQUIRE_EQ(0, listen(sockfd, 1));
+
+ /* Set up "blocking" client and connect with non-blocking server */
+ ATF_REQUIRE((sockfd2 = socket(PF_UNIX, SOCK_STREAM, 0)) != -1);
+ ATF_REQUIRE_EQ(0, connect(sockfd2, (struct sockaddr *)&server, len));
+ ATF_REQUIRE((connectfd = accept(sockfd, NULL, &len)) != -1);
+ /* Send a sample message to the connected socket */
+ ATF_REQUIRE(send(sockfd2, msgbuff, strlen(msgbuff), 0) != -1);
+
+ /* Receive data once connectfd is ready for reading */
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE((data_bytes = recv(connectfd, data, MAX_DATA, 0)) != 0);
+
+ /* Audit record must contain connectfd and data_bytes */
+ snprintf(extregex, sizeof(extregex),
+ "recv.*0x%x.*return,success,%zd", connectfd, data_bytes);
+ check_audit(fds, extregex, pipefd);
+
+ /* Close all socket descriptors */
+ close_sockets(3, sockfd, sockfd2, connectfd);
+}
+
+ATF_TC_CLEANUP(recv_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(recv_failure);
+ATF_TC_HEAD(recv_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "recv(2) call");
+}
+
+ATF_TC_BODY(recv_failure, tc)
+{
+ snprintf(extregex, sizeof(extregex), "recv.*%s", invalregex);
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid socket descriptor */
+ ATF_REQUIRE_EQ(-1, recv(-1, NULL, 0, 0));
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(recv_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(sendto_success);
+ATF_TC_HEAD(sendto_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "sendto(2) call");
+}
+
+ATF_TC_BODY(sendto_success, tc)
+{
+ assign_address(&server);
+ /* Setup a server socket and bind to the specified address */
+ ATF_REQUIRE((sockfd = socket(PF_UNIX, SOCK_DGRAM, 0)) != -1);
+ ATF_REQUIRE_EQ(0, bind(sockfd, (struct sockaddr *)&server, len));
+
+ /* Set up client socket to be used for sending the data */
+ ATF_REQUIRE((sockfd2 = socket(PF_UNIX, SOCK_DGRAM, 0)) != -1);
+
+ /* Send a sample message to server's address */
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE((data_bytes = sendto(sockfd2, msgbuff,
+ strlen(msgbuff), 0, (struct sockaddr *)&server, len)) != -1);
+
+ /* Audit record must contain sockfd2 and data_bytes */
+ snprintf(extregex, sizeof(extregex),
+ "sendto.*0x%x.*return,success,%zd", sockfd2, data_bytes);
+ check_audit(fds, extregex, pipefd);
+
+ /* Close all socket descriptors */
+ close_sockets(2, sockfd, sockfd2);
+}
+
+ATF_TC_CLEANUP(sendto_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(sendto_failure);
+ATF_TC_HEAD(sendto_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "sendto(2) call");
+}
+
+ATF_TC_BODY(sendto_failure, tc)
+{
+ snprintf(extregex, sizeof(extregex), "sendto.*%s", invalregex);
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid socket descriptor */
+ ATF_REQUIRE_EQ(-1, sendto(-1, NULL, 0, 0, NULL, 0));
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(sendto_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(recvfrom_success);
+ATF_TC_HEAD(recvfrom_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "recvfrom(2) call");
+}
+
+ATF_TC_BODY(recvfrom_success, tc)
+{
+ assign_address(&server);
+ /* Setup a server socket and bind to the specified address */
+ ATF_REQUIRE((sockfd = socket(PF_UNIX, SOCK_DGRAM, 0)) != -1);
+ ATF_REQUIRE_EQ(0, bind(sockfd, (struct sockaddr *)&server, len));
+
+ /* Set up client socket to be used for sending the data */
+ ATF_REQUIRE((sockfd2 = socket(PF_UNIX, SOCK_DGRAM, 0)) != -1);
+ ATF_REQUIRE(sendto(sockfd2, msgbuff, strlen(msgbuff), 0,
+ (struct sockaddr *)&server, len) != -1);
+
+ /* Receive data once sockfd is ready for reading */
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE((data_bytes = recvfrom(sockfd, data,
+ MAX_DATA, 0, NULL, &len)) != 0);
+
+ /* Audit record must contain sockfd and data_bytes */
+ snprintf(extregex, sizeof(extregex),
+ "recvfrom.*0x%x.*return,success,%zd", sockfd, data_bytes);
+ check_audit(fds, extregex, pipefd);
+
+ /* Close all socket descriptors */
+ close_sockets(2, sockfd, sockfd2);
+}
+
+ATF_TC_CLEANUP(recvfrom_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(recvfrom_failure);
+ATF_TC_HEAD(recvfrom_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "recvfrom(2) call");
+}
+
+ATF_TC_BODY(recvfrom_failure, tc)
+{
+ snprintf(extregex, sizeof(extregex), "recvfrom.*%s", invalregex);
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid socket descriptor */
+ ATF_REQUIRE_EQ(-1, recvfrom(-1, NULL, 0, 0, NULL, NULL));
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(recvfrom_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(sendmsg_success);
+ATF_TC_HEAD(sendmsg_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "recvmsg(2) call");
+}
+
+ATF_TC_BODY(sendmsg_success, tc)
+{
+ assign_address(&server);
+ /* Create a datagram server socket & bind to UNIX address family */
+ ATF_REQUIRE((sockfd = socket(PF_UNIX, SOCK_DGRAM, 0)) != -1);
+ ATF_REQUIRE_EQ(0, bind(sockfd, (struct sockaddr *)&server, len));
+
+ /* Message buffer to be sent to the server */
+ init_iov(&io1, msgbuff, sizeof(msgbuff));
+ init_msghdr(&sendbuf, &io1, &server);
+
+ /* Set up UDP client to communicate with the server */
+ ATF_REQUIRE((sockfd2 = socket(PF_UNIX, SOCK_DGRAM, 0)) != -1);
+
+ /* Send a sample message to the specified client address */
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE((data_bytes = sendmsg(sockfd2, &sendbuf, 0)) != -1);
+
+ /* Audit record must contain sockfd2 and data_bytes */
+ snprintf(extregex, sizeof(extregex),
+ "sendmsg.*0x%x.*return,success,%zd", sockfd2, data_bytes);
+ check_audit(fds, extregex, pipefd);
+
+ /* Close all socket descriptors */
+ close_sockets(2, sockfd, sockfd2);
+}
+
+ATF_TC_CLEANUP(sendmsg_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(sendmsg_failure);
+ATF_TC_HEAD(sendmsg_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "sendmsg(2) call");
+}
+
+ATF_TC_BODY(sendmsg_failure, tc)
+{
+ snprintf(extregex, sizeof(extregex),
+ "sendmsg.*return,failure : Bad address");
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, sendmsg(-1, NULL, 0));
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(sendmsg_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(recvmsg_success);
+ATF_TC_HEAD(recvmsg_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "recvmsg(2) call");
+}
+
+ATF_TC_BODY(recvmsg_success, tc)
+{
+ assign_address(&server);
+ /* Create a datagram server socket & bind to UNIX address family */
+ ATF_REQUIRE((sockfd = socket(PF_UNIX, SOCK_DGRAM, 0)) != -1);
+ ATF_REQUIRE_EQ(0, bind(sockfd, (struct sockaddr *)&server, len));
+
+ /* Message buffer to be sent to the server */
+ init_iov(&io1, msgbuff, sizeof(msgbuff));
+ init_msghdr(&sendbuf, &io1, &server);
+
+ /* Prepare buffer to store the received data in */
+ init_iov(&io2, data, sizeof(data));
+ init_msghdr(&recvbuf, &io2, NULL);
+
+ /* Set up UDP client to communicate with the server */
+ ATF_REQUIRE((sockfd2 = socket(PF_UNIX, SOCK_DGRAM, 0)) != -1);
+ /* Send a sample message to the connected socket */
+ ATF_REQUIRE(sendmsg(sockfd2, &sendbuf, 0) != -1);
+
+ /* Receive data once clientfd is ready for reading */
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE((data_bytes = recvmsg(sockfd, &recvbuf, 0)) != -1);
+
+ /* Audit record must contain sockfd and data_bytes */
+ snprintf(extregex, sizeof(extregex),
+ "recvmsg.*%#x.*return,success,%zd", sockfd, data_bytes);
+ check_audit(fds, extregex, pipefd);
+
+ /* Close all socket descriptors */
+ close_sockets(2, sockfd, sockfd2);
+}
+
+ATF_TC_CLEANUP(recvmsg_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(recvmsg_failure);
+ATF_TC_HEAD(recvmsg_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "recvmsg(2) call");
+}
+
+ATF_TC_BODY(recvmsg_failure, tc)
+{
+ snprintf(extregex, sizeof(extregex),
+ "recvmsg.*return,failure : Bad address");
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, recvmsg(-1, NULL, 0));
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(recvmsg_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(shutdown_success);
+ATF_TC_HEAD(shutdown_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "shutdown(2) call");
+}
+
+ATF_TC_BODY(shutdown_success, tc)
+{
+ assign_address(&server);
+ /* Setup server socket and bind to the specified address */
+ ATF_REQUIRE((sockfd = socket(PF_UNIX, SOCK_STREAM, 0)) != -1);
+ ATF_REQUIRE_EQ(0, bind(sockfd, (struct sockaddr *)&server, len));
+ ATF_REQUIRE_EQ(0, listen(sockfd, 1));
+
+ /* Setup client and connect with the blocking server */
+ ATF_REQUIRE((sockfd2 = socket(PF_UNIX, SOCK_STREAM, 0)) != -1);
+ ATF_REQUIRE_EQ(0, connect(sockfd2, (struct sockaddr *)&server, len));
+ ATF_REQUIRE((connectfd = accept(sockfd, NULL, &len)) != -1);
+
+ /* Audit record must contain clientfd */
+ snprintf(extregex, sizeof(extregex),
+ "shutdown.*%#x.*return,success", connectfd);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, shutdown(connectfd, SHUT_RDWR));
+ check_audit(fds, extregex, pipefd);
+
+ /* Close all socket descriptors */
+ close_sockets(3, sockfd, sockfd2, connectfd);
+}
+
+ATF_TC_CLEANUP(shutdown_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(shutdown_failure);
+ATF_TC_HEAD(shutdown_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "shutdown(2) call");
+}
+
+ATF_TC_BODY(shutdown_failure, tc)
+{
+ pid = getpid();
+ snprintf(extregex, sizeof(extregex),
+ "shutdown.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid socket descriptor */
+ ATF_REQUIRE_EQ(-1, shutdown(-1, SHUT_RDWR));
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(shutdown_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(sendfile_success);
+ATF_TC_HEAD(sendfile_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "sendfile(2) call");
+}
+
+ATF_TC_BODY(sendfile_success, tc)
+{
+ int filedesc;
+ ATF_REQUIRE((filedesc = open(path, O_CREAT | O_RDONLY, mode)) != -1);
+ /* Create a simple UNIX socket to send out random data */
+ ATF_REQUIRE((sockfd = socket(PF_UNIX, SOCK_STREAM, 0)) != -1);
+ /* Check the presence of sockfd, non-file in the audit record */
+ snprintf(extregex, sizeof(extregex),
+ "sendfile.*%#x,non-file.*return,success", filedesc);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, sendfile(filedesc, sockfd, 0, 0, NULL, NULL, 0));
+ check_audit(fds, extregex, pipefd);
+
+ /* Teardown socket and file descriptors */
+ close_sockets(2, sockfd, filedesc);
+}
+
+ATF_TC_CLEANUP(sendfile_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(sendfile_failure);
+ATF_TC_HEAD(sendfile_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "sendfile(2) call");
+}
+
+ATF_TC_BODY(sendfile_failure, tc)
+{
+ pid = getpid();
+ snprintf(extregex, sizeof(extregex),
+ "sendfile.*%d.*return,failure", pid);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, sendfile(-1, -1, 0, 0, NULL, NULL, 0));
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(sendfile_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(setfib_success);
+ATF_TC_HEAD(setfib_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "setfib(2) call");
+}
+
+ATF_TC_BODY(setfib_success, tc)
+{
+ pid = getpid();
+ snprintf(extregex, sizeof(extregex), "setfib.*%d.*return,success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, setfib(0));
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(setfib_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(setfib_failure);
+ATF_TC_HEAD(setfib_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "setfib(2) call");
+}
+
+ATF_TC_BODY(setfib_failure, tc)
+{
+ pid = getpid();
+ snprintf(extregex, sizeof(extregex), "setfib.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, setfib(-1));
+ check_audit(fds, extregex, pipefd);
+}
+
+ATF_TC_CLEANUP(setfib_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, socket_success);
+ ATF_TP_ADD_TC(tp, socket_failure);
+ ATF_TP_ADD_TC(tp, socketpair_success);
+ ATF_TP_ADD_TC(tp, socketpair_failure);
+ ATF_TP_ADD_TC(tp, setsockopt_success);
+ ATF_TP_ADD_TC(tp, setsockopt_failure);
+
+ ATF_TP_ADD_TC(tp, bind_success);
+ ATF_TP_ADD_TC(tp, bind_failure);
+ ATF_TP_ADD_TC(tp, bindat_success);
+ ATF_TP_ADD_TC(tp, bindat_failure);
+ ATF_TP_ADD_TC(tp, listen_success);
+ ATF_TP_ADD_TC(tp, listen_failure);
+
+ ATF_TP_ADD_TC(tp, connect_success);
+ ATF_TP_ADD_TC(tp, connect_failure);
+ ATF_TP_ADD_TC(tp, connectat_success);
+ ATF_TP_ADD_TC(tp, connectat_failure);
+ ATF_TP_ADD_TC(tp, accept_success);
+ ATF_TP_ADD_TC(tp, accept_failure);
+
+ ATF_TP_ADD_TC(tp, send_success);
+ ATF_TP_ADD_TC(tp, send_failure);
+ ATF_TP_ADD_TC(tp, recv_success);
+ ATF_TP_ADD_TC(tp, recv_failure);
+
+ ATF_TP_ADD_TC(tp, sendto_success);
+ ATF_TP_ADD_TC(tp, sendto_failure);
+ ATF_TP_ADD_TC(tp, recvfrom_success);
+ ATF_TP_ADD_TC(tp, recvfrom_failure);
+
+ ATF_TP_ADD_TC(tp, sendmsg_success);
+ ATF_TP_ADD_TC(tp, sendmsg_failure);
+ ATF_TP_ADD_TC(tp, recvmsg_success);
+ ATF_TP_ADD_TC(tp, recvmsg_failure);
+
+ ATF_TP_ADD_TC(tp, shutdown_success);
+ ATF_TP_ADD_TC(tp, shutdown_failure);
+ ATF_TP_ADD_TC(tp, sendfile_success);
+ ATF_TP_ADD_TC(tp, sendfile_failure);
+ ATF_TP_ADD_TC(tp, setfib_success);
+ ATF_TP_ADD_TC(tp, setfib_failure);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/audit/open.c b/tests/sys/audit/open.c
new file mode 100644
index 000000000000..3ba7f65208d8
--- /dev/null
+++ b/tests/sys/audit/open.c
@@ -0,0 +1,194 @@
+/*-
+ * Copyright (c) 2018 Aniket Pandey
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Note: open(2) and openat(2) have 12 events each for various values of 'flag'
+ * Please see: contrib/openbsm/etc/audit_event#L261
+ *
+ * 270:AUE_OPENAT_R:openat(2) - read:fr
+ * 271:AUE_OPENAT_RC:openat(2) - read,creat:fc,fr,fa,fm
+ * 272:AUE_OPENAT_RT:openat(2) - read,trunc:fd,fr,fa,fm
+ * 273:AUE_OPENAT_RTC:openat(2) - read,creat,trunc:fc,fd,fr,fa,fm
+ * 274:AUE_OPENAT_W:openat(2) - write:fw
+ * 275:AUE_OPENAT_WC:openat(2) - write,creat:fc,fw,fa,fm
+ * 276:AUE_OPENAT_WT:openat(2) - write,trunc:fd,fw,fa,fm
+ * 277:AUE_OPENAT_WTC:openat(2) - write,creat,trunc:fc,fd,fw,fa,fm
+ * 278:AUE_OPENAT_RW:openat(2) - read,write:fr,fw
+ * 279:AUE_OPENAT_RWC:openat(2) - read,write,create:fc,fw,fr,fa,fm
+ * 280:AUE_OPENAT_RWT:openat(2) - read,write,trunc:fd,fw,fr,fa,fm
+ * 281:AUE_OPENAT_RWTC:openat(2) - read,write,creat,trunc:fc,fd,fw,fr,fa,fm
+ */
+
+#include <sys/syscall.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+
+#include "utils.h"
+
+static struct pollfd fds[1];
+static mode_t o_mode = 0777;
+static int filedesc;
+static char extregex[80];
+static const char *path = "fileforaudit";
+static const char *errpath = "adirhasnoname/fileforaudit";
+
+/*
+ * Define test-cases for success and failure modes of both open(2) and openat(2)
+ */
+#define OPEN_AT_TC_DEFINE(mode, regex, flag, class) \
+ATF_TC_WITH_CLEANUP(open_ ## mode ## _success); \
+ATF_TC_HEAD(open_ ## mode ## _success, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " \
+ "open(2) call with flags = %s", #flag); \
+} \
+ATF_TC_BODY(open_ ## mode ## _success, tc) \
+{ \
+ snprintf(extregex, sizeof(extregex), \
+ "open.*%s.*fileforaudit.*return,success", regex); \
+ /* File needs to exist for successful open(2) invocation */ \
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, o_mode)) != -1); \
+ FILE *pipefd = setup(fds, class); \
+ ATF_REQUIRE(syscall(SYS_open, path, flag) != -1); \
+ check_audit(fds, extregex, pipefd); \
+ close(filedesc); \
+} \
+ATF_TC_CLEANUP(open_ ## mode ## _success, tc) \
+{ \
+ cleanup(); \
+} \
+ATF_TC_WITH_CLEANUP(open_ ## mode ## _failure); \
+ATF_TC_HEAD(open_ ## mode ## _failure, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " \
+ "open(2) call with flags = %s", #flag); \
+} \
+ATF_TC_BODY(open_ ## mode ## _failure, tc) \
+{ \
+ snprintf(extregex, sizeof(extregex), \
+ "open.*%s.*fileforaudit.*return,failure", regex); \
+ FILE *pipefd = setup(fds, class); \
+ ATF_REQUIRE_EQ(-1, syscall(SYS_open, errpath, flag)); \
+ check_audit(fds, extregex, pipefd); \
+} \
+ATF_TC_CLEANUP(open_ ## mode ## _failure, tc) \
+{ \
+ cleanup(); \
+} \
+ATF_TC_WITH_CLEANUP(openat_ ## mode ## _success); \
+ATF_TC_HEAD(openat_ ## mode ## _success, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful " \
+ "openat(2) call with flags = %s", #flag); \
+} \
+ATF_TC_BODY(openat_ ## mode ## _success, tc) \
+{ \
+ int filedesc2; \
+ snprintf(extregex, sizeof(extregex), \
+ "openat.*%s.*fileforaudit.*return,success", regex); \
+ /* File needs to exist for successful openat(2) invocation */ \
+ ATF_REQUIRE((filedesc = open(path, O_CREAT, o_mode)) != -1); \
+ FILE *pipefd = setup(fds, class); \
+ ATF_REQUIRE((filedesc2 = openat(AT_FDCWD, path, flag)) != -1); \
+ check_audit(fds, extregex, pipefd); \
+ close(filedesc2); \
+ close(filedesc); \
+} \
+ATF_TC_CLEANUP(openat_ ## mode ## _success, tc) \
+{ \
+ cleanup(); \
+} \
+ATF_TC_WITH_CLEANUP(openat_ ## mode ## _failure); \
+ATF_TC_HEAD(openat_ ## mode ## _failure, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful " \
+ "openat(2) call with flags = %s", #flag); \
+} \
+ATF_TC_BODY(openat_ ## mode ## _failure, tc) \
+{ \
+ snprintf(extregex, sizeof(extregex), \
+ "openat.*%s.*fileforaudit.*return,failure", regex); \
+ FILE *pipefd = setup(fds, class); \
+ ATF_REQUIRE_EQ(-1, openat(AT_FDCWD, errpath, flag)); \
+ check_audit(fds, extregex, pipefd); \
+} \
+ATF_TC_CLEANUP(openat_ ## mode ## _failure, tc) \
+{ \
+ cleanup(); \
+}
+
+/*
+ * Add both success and failure modes of open(2) and openat(2)
+ */
+#define OPEN_AT_TC_ADD(tp, mode) \
+do { \
+ ATF_TP_ADD_TC(tp, open_ ## mode ## _success); \
+ ATF_TP_ADD_TC(tp, open_ ## mode ## _failure); \
+ ATF_TP_ADD_TC(tp, openat_ ## mode ## _success); \
+ ATF_TP_ADD_TC(tp, openat_ ## mode ## _failure); \
+} while (0)
+
+
+/*
+ * Each of the 12 OPEN_AT_TC_DEFINE statement is a group of 4 test-cases
+ * corresponding to separate audit events for open(2) and openat(2)
+ */
+OPEN_AT_TC_DEFINE(read, "read", O_RDONLY, "fr")
+OPEN_AT_TC_DEFINE(read_creat, "read,creat", O_RDONLY | O_CREAT, "fr")
+OPEN_AT_TC_DEFINE(read_trunc, "read,trunc", O_RDONLY | O_TRUNC, "fr")
+OPEN_AT_TC_DEFINE(read_creat_trunc, "read,creat,trunc", O_RDONLY | O_CREAT
+ | O_TRUNC, "fr")
+OPEN_AT_TC_DEFINE(write, "write", O_WRONLY, "fw")
+OPEN_AT_TC_DEFINE(write_creat, "write,creat", O_WRONLY | O_CREAT, "fw")
+OPEN_AT_TC_DEFINE(write_trunc, "write,trunc", O_WRONLY | O_TRUNC, "fw")
+OPEN_AT_TC_DEFINE(write_creat_trunc, "write,creat,trunc", O_WRONLY | O_CREAT
+ | O_TRUNC, "fw")
+OPEN_AT_TC_DEFINE(read_write, "read,write", O_RDWR, "fr")
+OPEN_AT_TC_DEFINE(read_write_creat, "read,write,creat", O_RDWR | O_CREAT, "fw")
+OPEN_AT_TC_DEFINE(read_write_trunc, "read,write,trunc", O_RDWR | O_TRUNC, "fr")
+OPEN_AT_TC_DEFINE(read_write_creat_trunc, "read,write,creat,trunc", O_RDWR |
+ O_CREAT | O_TRUNC, "fw")
+
+
+ATF_TP_ADD_TCS(tp)
+{
+ OPEN_AT_TC_ADD(tp, read);
+ OPEN_AT_TC_ADD(tp, read_creat);
+ OPEN_AT_TC_ADD(tp, read_trunc);
+ OPEN_AT_TC_ADD(tp, read_creat_trunc);
+
+ OPEN_AT_TC_ADD(tp, write);
+ OPEN_AT_TC_ADD(tp, write_creat);
+ OPEN_AT_TC_ADD(tp, write_trunc);
+ OPEN_AT_TC_ADD(tp, write_creat_trunc);
+
+ OPEN_AT_TC_ADD(tp, read_write);
+ OPEN_AT_TC_ADD(tp, read_write_creat);
+ OPEN_AT_TC_ADD(tp, read_write_trunc);
+ OPEN_AT_TC_ADD(tp, read_write_creat_trunc);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/audit/process-control.c b/tests/sys/audit/process-control.c
new file mode 100644
index 000000000000..ed622487ac89
--- /dev/null
+++ b/tests/sys/audit/process-control.c
@@ -0,0 +1,1662 @@
+/*-
+ * Copyright (c) 2018 Aniket Pandey
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/capsicum.h>
+#include <sys/uio.h>
+#include <sys/ktrace.h>
+#include <sys/mman.h>
+#include <sys/procctl.h>
+#include <sys/ptrace.h>
+#include <sys/resource.h>
+#include <sys/rtprio.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "utils.h"
+
+#include "freebsd_test_suite/macros.h"
+
+static pid_t pid;
+static int filedesc, status;
+static struct pollfd fds[1];
+static char pcregex[80];
+static const char *auclass = "pc";
+
+
+ATF_TC_WITH_CLEANUP(fork_success);
+ATF_TC_HEAD(fork_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "fork(2) call");
+}
+
+ATF_TC_BODY(fork_success, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "fork.*%d.*return,success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Check if fork(2) succeded. If so, exit from the child process */
+ ATF_REQUIRE((pid = fork()) != -1);
+ if (pid)
+ check_audit(fds, pcregex, pipefd);
+ else
+ _exit(0);
+}
+
+ATF_TC_CLEANUP(fork_success, tc)
+{
+ cleanup();
+}
+
+/*
+ * No fork(2) in failure mode since possibilities for failure are only when
+ * user is not privileged or when the number of processes exceed KERN_MAXPROC.
+ */
+
+
+ATF_TC_WITH_CLEANUP(_exit_success);
+ATF_TC_HEAD(_exit_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "_exit(2) call");
+}
+
+ATF_TC_BODY(_exit_success, tc)
+{
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE((pid = fork()) != -1);
+ if (pid) {
+ snprintf(pcregex, sizeof(pcregex), "exit.*%d.*success", pid);
+ check_audit(fds, pcregex, pipefd);
+ }
+ else
+ _exit(0);
+}
+
+ATF_TC_CLEANUP(_exit_success, tc)
+{
+ cleanup();
+}
+
+/*
+ * _exit(2) never returns, hence the auditing by default is always successful
+ */
+
+
+ATF_TC_WITH_CLEANUP(rfork_success);
+ATF_TC_HEAD(rfork_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "rfork(2) call");
+}
+
+ATF_TC_BODY(rfork_success, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "rfork.*%d.*return,success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE((pid = rfork(RFPROC)) != -1);
+ if (pid)
+ check_audit(fds, pcregex, pipefd);
+ else
+ _exit(0);
+}
+
+ATF_TC_CLEANUP(rfork_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(rfork_failure);
+ATF_TC_HEAD(rfork_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "rfork(2) call");
+}
+
+ATF_TC_BODY(rfork_failure, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "rfork.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid argument */
+ ATF_REQUIRE_EQ(-1, rfork(-1));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(rfork_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(wait4_success);
+ATF_TC_HEAD(wait4_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "wait4(2) call");
+}
+
+ATF_TC_BODY(wait4_success, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "wait4.*%d.*return,success", pid);
+
+ ATF_REQUIRE((pid = fork()) != -1);
+ if (pid) {
+ FILE *pipefd = setup(fds, auclass);
+ /* wpid = -1 : Wait for any child process */
+ ATF_REQUIRE(wait4(-1, &status, 0, NULL) != -1);
+ check_audit(fds, pcregex, pipefd);
+ }
+ else
+ _exit(0);
+}
+
+ATF_TC_CLEANUP(wait4_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(wait4_failure);
+ATF_TC_HEAD(wait4_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "wait4(2) call");
+}
+
+ATF_TC_BODY(wait4_failure, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "wait4.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: No child process to wait for */
+ ATF_REQUIRE_EQ(-1, wait4(-1, NULL, 0, NULL));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(wait4_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(wait6_success);
+ATF_TC_HEAD(wait6_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "wait6(2) call");
+}
+
+ATF_TC_BODY(wait6_success, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "wait6.*%d.*return,success", pid);
+
+ ATF_REQUIRE((pid = fork()) != -1);
+ if (pid) {
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE(wait6(P_ALL, 0, &status, WEXITED, NULL,NULL) != -1);
+ check_audit(fds, pcregex, pipefd);
+ }
+ else
+ _exit(0);
+}
+
+ATF_TC_CLEANUP(wait6_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(wait6_failure);
+ATF_TC_HEAD(wait6_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "wait6(2) call");
+}
+
+ATF_TC_BODY(wait6_failure, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "wait6.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid argument */
+ ATF_REQUIRE_EQ(-1, wait6(0, 0, NULL, 0, NULL, NULL));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(wait6_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(kill_success);
+ATF_TC_HEAD(kill_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "kill(2) call");
+}
+
+ATF_TC_BODY(kill_success, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "kill.*%d.*return,success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Don't send any signal to anyone, live in peace! */
+ ATF_REQUIRE_EQ(0, kill(0, 0));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(kill_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(kill_failure);
+ATF_TC_HEAD(kill_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "kill(2) call");
+}
+
+ATF_TC_BODY(kill_failure, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "kill.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /*
+ * Failure reason: Non existent process with PID '-2'
+ * Note: '-1' is not used as it means sending no signal to
+ * all non-system processes: A successful invocation
+ */
+ ATF_REQUIRE_EQ(-1, kill(0, -2));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(kill_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(chdir_success);
+ATF_TC_HEAD(chdir_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "chdir(2) call");
+}
+
+ATF_TC_BODY(chdir_success, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "chdir.*/.*%d.*return,success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, chdir("/"));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(chdir_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(chdir_failure);
+ATF_TC_HEAD(chdir_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "chdir(2) call");
+}
+
+ATF_TC_BODY(chdir_failure, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "chdir.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Bad address */
+ ATF_REQUIRE_EQ(-1, chdir(NULL));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(chdir_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fchdir_success);
+ATF_TC_HEAD(fchdir_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "fchdir(2) call");
+}
+
+ATF_TC_BODY(fchdir_success, tc)
+{
+ /* Build an absolute path to the test-case directory */
+ char dirpath[50];
+ ATF_REQUIRE(getcwd(dirpath, sizeof(dirpath)) != NULL);
+ ATF_REQUIRE((filedesc = open(dirpath, O_RDONLY)) != -1);
+
+ /* Audit record generated by fchdir(2) does not contain filedesc */
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "fchdir.*%d.*return,success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, fchdir(filedesc));
+ check_audit(fds, pcregex, pipefd);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(fchdir_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(fchdir_failure);
+ATF_TC_HEAD(fchdir_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "fchdir(2) call");
+}
+
+ATF_TC_BODY(fchdir_failure, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "fchdir.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Bad directory address */
+ ATF_REQUIRE_EQ(-1, fchdir(-1));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(fchdir_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(chroot_success);
+ATF_TC_HEAD(chroot_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "chroot(2) call");
+}
+
+ATF_TC_BODY(chroot_success, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "chroot.*%d.*return,success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* We don't want to change the root directory, hence '/' */
+ ATF_REQUIRE_EQ(0, chroot("/"));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(chroot_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(chroot_failure);
+ATF_TC_HEAD(chroot_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "chroot(2) call");
+}
+
+ATF_TC_BODY(chroot_failure, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "chroot.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, chroot(NULL));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(chroot_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(umask_success);
+ATF_TC_HEAD(umask_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "umask(2) call");
+}
+
+ATF_TC_BODY(umask_success, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "umask.*%d.*return,success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ umask(0);
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(umask_success, tc)
+{
+ cleanup();
+}
+
+/*
+ * umask(2) system call never fails. Hence, no test case for failure mode
+ */
+
+
+ATF_TC_WITH_CLEANUP(setuid_success);
+ATF_TC_HEAD(setuid_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "setuid(2) call");
+}
+
+ATF_TC_BODY(setuid_success, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "setuid.*%d.*return,success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Since we're privileged, we'll let ourselves be privileged! */
+ ATF_REQUIRE_EQ(0, setuid(0));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(setuid_success, tc)
+{
+ cleanup();
+}
+
+/*
+ * setuid(2) fails only when the current user is not root. So no test case for
+ * failure mode since the required_user="root"
+ */
+
+
+ATF_TC_WITH_CLEANUP(seteuid_success);
+ATF_TC_HEAD(seteuid_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "seteuid(2) call");
+}
+
+ATF_TC_BODY(seteuid_success, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "seteuid.*%d.*return,success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* This time, we'll let ourselves be 'effectively' privileged! */
+ ATF_REQUIRE_EQ(0, seteuid(0));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(seteuid_success, tc)
+{
+ cleanup();
+}
+
+/*
+ * seteuid(2) fails only when the current user is not root. So no test case for
+ * failure mode since the required_user="root"
+ */
+
+
+ATF_TC_WITH_CLEANUP(setgid_success);
+ATF_TC_HEAD(setgid_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "setgid(2) call");
+}
+
+ATF_TC_BODY(setgid_success, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "setgid.*%d.*return,success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, setgid(0));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(setgid_success, tc)
+{
+ cleanup();
+}
+
+/*
+ * setgid(2) fails only when the current user is not root. So no test case for
+ * failure mode since the required_user="root"
+ */
+
+
+ATF_TC_WITH_CLEANUP(setegid_success);
+ATF_TC_HEAD(setegid_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "setegid(2) call");
+}
+
+ATF_TC_BODY(setegid_success, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "setegid.*%d.*return,success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, setegid(0));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(setegid_success, tc)
+{
+ cleanup();
+}
+
+/*
+ * setegid(2) fails only when the current user is not root. So no test case for
+ * failure mode since the required_user="root"
+ */
+
+
+ATF_TC_WITH_CLEANUP(setregid_success);
+ATF_TC_HEAD(setregid_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "setregid(2) call");
+}
+
+ATF_TC_BODY(setregid_success, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "setregid.*%d.*return,success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* setregid(-1, -1) does not change any real or effective GIDs */
+ ATF_REQUIRE_EQ(0, setregid(-1, -1));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(setregid_success, tc)
+{
+ cleanup();
+}
+
+/*
+ * setregid(2) fails only when the current user is not root. So no test case for
+ * failure mode since the required_user="root"
+ */
+
+
+ATF_TC_WITH_CLEANUP(setreuid_success);
+ATF_TC_HEAD(setreuid_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "setreuid(2) call");
+}
+
+ATF_TC_BODY(setreuid_success, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "setreuid.*%d.*return,success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* setreuid(-1, -1) does not change any real or effective UIDs */
+ ATF_REQUIRE_EQ(0, setreuid(-1, -1));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(setreuid_success, tc)
+{
+ cleanup();
+}
+
+/*
+ * setregid(2) fails only when the current user is not root. So no test case for
+ * failure mode since the required_user="root"
+ */
+
+
+ATF_TC_WITH_CLEANUP(setresuid_success);
+ATF_TC_HEAD(setresuid_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "setresuid(2) call");
+}
+
+ATF_TC_BODY(setresuid_success, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "setresuid.*%d.*return,success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* setresuid(-1, -1, -1) does not change real, effective & saved UIDs */
+ ATF_REQUIRE_EQ(0, setresuid(-1, -1, -1));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(setresuid_success, tc)
+{
+ cleanup();
+}
+
+/*
+ * setresuid(2) fails only when the current user is not root. So no test case
+ * for failure mode since the required_user="root"
+ */
+
+
+ATF_TC_WITH_CLEANUP(setresgid_success);
+ATF_TC_HEAD(setresgid_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "setresgid(2) call");
+}
+
+ATF_TC_BODY(setresgid_success, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "setresgid.*%d.*ret.*success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* setresgid(-1, -1, -1) does not change real, effective & saved GIDs */
+ ATF_REQUIRE_EQ(0, setresgid(-1, -1, -1));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(setresgid_success, tc)
+{
+ cleanup();
+}
+
+/*
+ * setresgid(2) fails only when the current user is not root. So no test case
+ * for failure mode since the required_user="root"
+ */
+
+
+ATF_TC_WITH_CLEANUP(getresuid_success);
+ATF_TC_HEAD(getresuid_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "getresuid(2) call");
+}
+
+ATF_TC_BODY(getresuid_success, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "getresuid.*%d.*ret.*success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, getresuid(NULL, NULL, NULL));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(getresuid_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(getresuid_failure);
+ATF_TC_HEAD(getresuid_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "getresuid(2) call");
+}
+
+ATF_TC_BODY(getresuid_failure, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "getresuid.*%d.*ret.*failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid address "-1" */
+ ATF_REQUIRE_EQ(-1, getresuid((uid_t *)-1, NULL, NULL));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(getresuid_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(getresgid_success);
+ATF_TC_HEAD(getresgid_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "getresgid(2) call");
+}
+
+ATF_TC_BODY(getresgid_success, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "getresgid.*%d.*ret.*success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, getresgid(NULL, NULL, NULL));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(getresgid_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(getresgid_failure);
+ATF_TC_HEAD(getresgid_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "getresgid(2) call");
+}
+
+ATF_TC_BODY(getresgid_failure, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "getresgid.*%d.*ret.*failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* Failure reason: Invalid address "-1" */
+ ATF_REQUIRE_EQ(-1, getresgid((gid_t *)-1, NULL, NULL));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(getresgid_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(setpriority_success);
+ATF_TC_HEAD(setpriority_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "setpriority(2) call");
+}
+
+ATF_TC_BODY(setpriority_success, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "setpriority.*%d.*success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, setpriority(PRIO_PROCESS, 0, 0));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(setpriority_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(setpriority_failure);
+ATF_TC_HEAD(setpriority_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "setpriority(2) call");
+}
+
+ATF_TC_BODY(setpriority_failure, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "setpriority.*%d.*failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, setpriority(-1, -1, -1));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(setpriority_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(setgroups_success);
+ATF_TC_HEAD(setgroups_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "setgroups(2) call");
+}
+
+ATF_TC_BODY(setgroups_success, tc)
+{
+ gid_t gids[5];
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "setgroups.*%d.*ret.*success", pid);
+ /* Retrieve the current group access list to be used with setgroups */
+ ATF_REQUIRE(getgroups(sizeof(gids)/sizeof(gids[0]), gids) != -1);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, setgroups(sizeof(gids)/sizeof(gids[0]), gids));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(setgroups_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(setgroups_failure);
+ATF_TC_HEAD(setgroups_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "setgroups(2) call");
+}
+
+ATF_TC_BODY(setgroups_failure, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "setgroups.*%d.*ret.*failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, setgroups(-1, NULL));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(setgroups_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(setpgrp_success);
+ATF_TC_HEAD(setpgrp_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "setpgrp(2) call");
+}
+
+ATF_TC_BODY(setpgrp_success, tc)
+{
+ /* Main procedure is carried out from within the child process */
+ ATF_REQUIRE((pid = fork()) != -1);
+ if (pid) {
+ ATF_REQUIRE(wait(&status) != -1);
+ } else {
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "setpgrp.*%d.*success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, setpgrp(0, 0));
+ check_audit(fds, pcregex, pipefd);
+ }
+}
+
+ATF_TC_CLEANUP(setpgrp_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(setpgrp_failure);
+ATF_TC_HEAD(setpgrp_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "setpgrp(2) call");
+}
+
+ATF_TC_BODY(setpgrp_failure, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "setpgrp.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, setpgrp(-1, -1));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(setpgrp_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(setsid_success);
+ATF_TC_HEAD(setsid_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "setsid(2) call");
+}
+
+ATF_TC_BODY(setsid_success, tc)
+{
+ /* Main procedure is carried out from within the child process */
+ ATF_REQUIRE((pid = fork()) != -1);
+ if (pid) {
+ ATF_REQUIRE(wait(&status) != -1);
+ } else {
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "setsid.*%d.*success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE(setsid() != -1);
+ check_audit(fds, pcregex, pipefd);
+ }
+}
+
+ATF_TC_CLEANUP(setsid_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(setsid_failure);
+ATF_TC_HEAD(setsid_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "setsid(2) call");
+}
+
+ATF_TC_BODY(setsid_failure, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "setsid.*%d.*return,failure", pid);
+
+ /*
+ * Here, we are intentionally ignoring the output of the setsid()
+ * call because it may or may not be a process leader already. But it
+ * ensures that the next invocation of setsid() will definitely fail.
+ */
+ setsid();
+ FILE *pipefd = setup(fds, auclass);
+ /*
+ * Failure reason: [EPERM] Creating a new session is not permitted
+ * as the PID of calling process matches the PGID of a process group
+ * created by premature setsid() call.
+ */
+ ATF_REQUIRE_EQ(-1, setsid());
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(setsid_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(setrlimit_success);
+ATF_TC_HEAD(setrlimit_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "setrlimit(2) call");
+}
+
+ATF_TC_BODY(setrlimit_success, tc)
+{
+ struct rlimit rlp;
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "setrlimit.*%d.*ret.*success", pid);
+ /* Retrieve the system resource consumption limit to be used later on */
+ ATF_REQUIRE_EQ(0, getrlimit(RLIMIT_FSIZE, &rlp));
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, setrlimit(RLIMIT_FSIZE, &rlp));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(setrlimit_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(setrlimit_failure);
+ATF_TC_HEAD(setrlimit_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "setrlimit(2) call");
+}
+
+ATF_TC_BODY(setrlimit_failure, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "setrlimit.*%d.*ret.*failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, setrlimit(RLIMIT_FSIZE, NULL));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(setrlimit_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(mlock_success);
+ATF_TC_HEAD(mlock_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "mlock(2) call");
+}
+
+ATF_TC_BODY(mlock_success, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "mlock.*%d.*return,success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, mlock(NULL, 0));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(mlock_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(mlock_failure);
+ATF_TC_HEAD(mlock_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "mlock(2) call");
+}
+
+ATF_TC_BODY(mlock_failure, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "mlock.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, mlock((void *)(-1), -1));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(mlock_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(munlock_success);
+ATF_TC_HEAD(munlock_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "munlock(2) call");
+}
+
+ATF_TC_BODY(munlock_success, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "munlock.*%d.*return,success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, munlock(NULL, 0));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(munlock_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(munlock_failure);
+ATF_TC_HEAD(munlock_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "munlock(2) call");
+}
+
+ATF_TC_BODY(munlock_failure, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "munlock.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, munlock((void *)(-1), -1));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(munlock_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(minherit_success);
+ATF_TC_HEAD(minherit_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "minherit(2) call");
+}
+
+ATF_TC_BODY(minherit_success, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "minherit.*%d.*return,success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, minherit(NULL, 0, INHERIT_ZERO));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(minherit_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(minherit_failure);
+ATF_TC_HEAD(minherit_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "minherit(2) call");
+}
+
+ATF_TC_BODY(minherit_failure, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "minherit.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, minherit((void *)(-1), -1, 0));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(minherit_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(setlogin_success);
+ATF_TC_HEAD(setlogin_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "setlogin(2) call");
+}
+
+ATF_TC_BODY(setlogin_success, tc)
+{
+ char *name;
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "setlogin.*%d.*return,success", pid);
+
+ /* Retrieve the current user's login name to be used with setlogin(2) */
+ ATF_REQUIRE((name = getlogin()) != NULL);
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, setlogin(name));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(setlogin_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(setlogin_failure);
+ATF_TC_HEAD(setlogin_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "setlogin(2) call");
+}
+
+ATF_TC_BODY(setlogin_failure, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "setlogin.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, setlogin(NULL));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(setlogin_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(rtprio_success);
+ATF_TC_HEAD(rtprio_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "rtprio(2) call");
+}
+
+ATF_TC_BODY(rtprio_success, tc)
+{
+ struct rtprio rtp;
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "rtprio.*%d.*return,success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, rtprio(RTP_LOOKUP, 0, &rtp));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(rtprio_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(rtprio_failure);
+ATF_TC_HEAD(rtprio_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "rtprio(2) call");
+}
+
+ATF_TC_BODY(rtprio_failure, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "rtprio.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, rtprio(-1, -1, NULL));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(rtprio_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(profil_success);
+ATF_TC_HEAD(profil_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "profil(2) call");
+}
+
+ATF_TC_BODY(profil_success, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "profil.*%d.*return,success", pid);
+
+ char samples[20];
+ FILE *pipefd = setup(fds, auclass);
+ /* Set scale argument as 0 to disable profiling of current process */
+ ATF_REQUIRE_EQ(0, profil(samples, sizeof(samples), 0, 0));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(profil_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(profil_failure);
+ATF_TC_HEAD(profil_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "profil(2) call");
+}
+
+ATF_TC_BODY(profil_failure, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "profil.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, profil((char *)(SIZE_MAX), -1, -1, -1));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(profil_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(ptrace_success);
+ATF_TC_HEAD(ptrace_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "ptrace(2) call");
+}
+
+ATF_TC_BODY(ptrace_success, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "ptrace.*%d.*return,success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, ptrace(PT_TRACE_ME, 0, NULL, 0));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(ptrace_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(ptrace_failure);
+ATF_TC_HEAD(ptrace_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "ptrace(2) call");
+}
+
+ATF_TC_BODY(ptrace_failure, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "ptrace.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, ptrace(-1, 0, NULL, 0));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(ptrace_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(ktrace_success);
+ATF_TC_HEAD(ktrace_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "ktrace(2) call");
+}
+
+ATF_TC_BODY(ktrace_success, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "ktrace.*%d.*return,success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, ktrace(NULL, KTROP_CLEAR, KTRFAC_SYSCALL, pid));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(ktrace_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(ktrace_failure);
+ATF_TC_HEAD(ktrace_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "ktrace(2) call");
+}
+
+ATF_TC_BODY(ktrace_failure, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "ktrace.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, ktrace(NULL, -1, -1, 0));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(ktrace_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(procctl_success);
+ATF_TC_HEAD(procctl_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "procctl(2) call");
+}
+
+ATF_TC_BODY(procctl_success, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "procctl.*%d.*return,success", pid);
+
+ struct procctl_reaper_status reapstat;
+ FILE *pipefd = setup(fds, auclass);
+ /* Retrieve information about the reaper of current process (pid) */
+ ATF_REQUIRE_EQ(0, procctl(P_PID, pid, PROC_REAP_STATUS, &reapstat));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(procctl_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(procctl_failure);
+ATF_TC_HEAD(procctl_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "procctl(2) call");
+}
+
+ATF_TC_BODY(procctl_failure, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "procctl.*%d.*return,failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(-1, procctl(-1, -1, -1, NULL));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(procctl_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(cap_enter_success);
+ATF_TC_HEAD(cap_enter_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "cap_enter(2) call");
+}
+
+ATF_TC_BODY(cap_enter_success, tc)
+{
+ ATF_REQUIRE_FEATURE("security_capability_mode");
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE((pid = fork()) != -1);
+ if (pid) {
+ snprintf(pcregex, sizeof(pcregex),
+ "cap_enter.*%d.*return,success", pid);
+ ATF_REQUIRE(wait(&status) != -1);
+ check_audit(fds, pcregex, pipefd);
+ }
+ else {
+ ATF_REQUIRE_EQ(0, cap_enter());
+ _exit(0);
+ }
+}
+
+ATF_TC_CLEANUP(cap_enter_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(cap_getmode_success);
+ATF_TC_HEAD(cap_getmode_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of a successful "
+ "cap_getmode(2) call");
+}
+
+ATF_TC_BODY(cap_getmode_success, tc)
+{
+ int modep;
+
+ ATF_REQUIRE_FEATURE("security_capability_mode");
+
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "cap_getmode.*%d.*success", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ ATF_REQUIRE_EQ(0, cap_getmode(&modep));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(cap_getmode_success, tc)
+{
+ cleanup();
+}
+
+
+ATF_TC_WITH_CLEANUP(cap_getmode_failure);
+ATF_TC_HEAD(cap_getmode_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the audit of an unsuccessful "
+ "cap_getmode(2) call");
+}
+
+ATF_TC_BODY(cap_getmode_failure, tc)
+{
+ pid = getpid();
+ snprintf(pcregex, sizeof(pcregex), "cap_getmode.*%d.*failure", pid);
+
+ FILE *pipefd = setup(fds, auclass);
+ /* cap_getmode(2) can either fail with EFAULT or ENOSYS */
+ ATF_REQUIRE_EQ(-1, cap_getmode(NULL));
+ check_audit(fds, pcregex, pipefd);
+}
+
+ATF_TC_CLEANUP(cap_getmode_failure, tc)
+{
+ cleanup();
+}
+
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, fork_success);
+ ATF_TP_ADD_TC(tp, _exit_success);
+ ATF_TP_ADD_TC(tp, rfork_success);
+ ATF_TP_ADD_TC(tp, rfork_failure);
+
+ ATF_TP_ADD_TC(tp, wait4_success);
+ ATF_TP_ADD_TC(tp, wait4_failure);
+ ATF_TP_ADD_TC(tp, wait6_success);
+ ATF_TP_ADD_TC(tp, wait6_failure);
+ ATF_TP_ADD_TC(tp, kill_success);
+ ATF_TP_ADD_TC(tp, kill_failure);
+
+ ATF_TP_ADD_TC(tp, chdir_success);
+ ATF_TP_ADD_TC(tp, chdir_failure);
+ ATF_TP_ADD_TC(tp, fchdir_success);
+ ATF_TP_ADD_TC(tp, fchdir_failure);
+ ATF_TP_ADD_TC(tp, chroot_success);
+ ATF_TP_ADD_TC(tp, chroot_failure);
+
+ ATF_TP_ADD_TC(tp, umask_success);
+ ATF_TP_ADD_TC(tp, setuid_success);
+ ATF_TP_ADD_TC(tp, seteuid_success);
+ ATF_TP_ADD_TC(tp, setgid_success);
+ ATF_TP_ADD_TC(tp, setegid_success);
+
+ ATF_TP_ADD_TC(tp, setreuid_success);
+ ATF_TP_ADD_TC(tp, setregid_success);
+ ATF_TP_ADD_TC(tp, setresuid_success);
+ ATF_TP_ADD_TC(tp, setresgid_success);
+
+ ATF_TP_ADD_TC(tp, getresuid_success);
+ ATF_TP_ADD_TC(tp, getresuid_failure);
+ ATF_TP_ADD_TC(tp, getresgid_success);
+ ATF_TP_ADD_TC(tp, getresgid_failure);
+
+ ATF_TP_ADD_TC(tp, setpriority_success);
+ ATF_TP_ADD_TC(tp, setpriority_failure);
+ ATF_TP_ADD_TC(tp, setgroups_success);
+ ATF_TP_ADD_TC(tp, setgroups_failure);
+ ATF_TP_ADD_TC(tp, setpgrp_success);
+ ATF_TP_ADD_TC(tp, setpgrp_failure);
+ ATF_TP_ADD_TC(tp, setsid_success);
+ ATF_TP_ADD_TC(tp, setsid_failure);
+ ATF_TP_ADD_TC(tp, setrlimit_success);
+ ATF_TP_ADD_TC(tp, setrlimit_failure);
+
+ ATF_TP_ADD_TC(tp, mlock_success);
+ ATF_TP_ADD_TC(tp, mlock_failure);
+ ATF_TP_ADD_TC(tp, munlock_success);
+ ATF_TP_ADD_TC(tp, munlock_failure);
+ ATF_TP_ADD_TC(tp, minherit_success);
+ ATF_TP_ADD_TC(tp, minherit_failure);
+
+ ATF_TP_ADD_TC(tp, setlogin_success);
+ ATF_TP_ADD_TC(tp, setlogin_failure);
+ ATF_TP_ADD_TC(tp, rtprio_success);
+ ATF_TP_ADD_TC(tp, rtprio_failure);
+
+ ATF_TP_ADD_TC(tp, profil_success);
+ ATF_TP_ADD_TC(tp, profil_failure);
+ ATF_TP_ADD_TC(tp, ptrace_success);
+ ATF_TP_ADD_TC(tp, ptrace_failure);
+ ATF_TP_ADD_TC(tp, ktrace_success);
+ ATF_TP_ADD_TC(tp, ktrace_failure);
+ ATF_TP_ADD_TC(tp, procctl_success);
+ ATF_TP_ADD_TC(tp, procctl_failure);
+
+ ATF_TP_ADD_TC(tp, cap_enter_success);
+ ATF_TP_ADD_TC(tp, cap_getmode_success);
+ ATF_TP_ADD_TC(tp, cap_getmode_failure);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/audit/utils.c b/tests/sys/audit/utils.c
new file mode 100644
index 000000000000..7c9c12e955ec
--- /dev/null
+++ b/tests/sys/audit/utils.c
@@ -0,0 +1,329 @@
+/*-
+ * Copyright 2018 Aniket Pandey
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/extattr.h>
+#include <sys/ioctl.h>
+
+#include <bsm/libbsm.h>
+#include <bsm/auditd_lib.h>
+#include <security/audit/audit_ioctl.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "utils.h"
+
+/*
+ * Checks the presence of "auditregex" in auditpipe(4) after the
+ * corresponding system call has been triggered.
+ */
+static bool
+get_records(const char *auditregex, FILE *pipestream)
+{
+ uint8_t *buff;
+ tokenstr_t token;
+ ssize_t size = 1024;
+ char membuff[size];
+ char del[] = ",";
+ int reclen, bytes = 0;
+ FILE *memstream;
+
+ /*
+ * Open a stream on 'membuff' (address to memory buffer) for storing
+ * the audit records in the default mode.'reclen' is the length of the
+ * available records from auditpipe which is passed to the functions
+ * au_fetch_tok(3) and au_print_flags_tok(3) for further use.
+ */
+ ATF_REQUIRE((memstream = fmemopen(membuff, size, "w")) != NULL);
+ ATF_REQUIRE((reclen = au_read_rec(pipestream, &buff)) != -1);
+
+ /*
+ * Iterate through each BSM token, extracting the bits that are
+ * required to start processing the token sequences.
+ */
+ while (bytes < reclen) {
+ if (au_fetch_tok(&token, buff + bytes, reclen - bytes) == -1) {
+ perror("au_read_rec");
+ atf_tc_fail("Incomplete Audit Record");
+ }
+
+ /* Print the tokens as they are obtained, in the default form */
+ au_print_flags_tok(memstream, &token, del, AU_OFLAG_NONE);
+ fputc(',', memstream);
+ bytes += token.len;
+ }
+
+ free(buff);
+ ATF_REQUIRE_EQ(0, fclose(memstream));
+ return (atf_utils_grep_string("%s", membuff, auditregex));
+}
+
+/*
+ * Override the system-wide audit mask settings in /etc/security/audit_control
+ * and set the auditpipe's maximum allowed queue length limit
+ */
+static void
+set_preselect_mode(int filedesc, au_mask_t *fmask)
+{
+ int qlimit_max;
+ int fmode = AUDITPIPE_PRESELECT_MODE_LOCAL;
+
+ /* Set local preselection mode for auditing */
+ if (ioctl(filedesc, AUDITPIPE_SET_PRESELECT_MODE, &fmode) < 0)
+ atf_tc_fail("Preselection mode: %s", strerror(errno));
+
+ /* Set local preselection flag corresponding to the audit_event */
+ if (ioctl(filedesc, AUDITPIPE_SET_PRESELECT_FLAGS, fmask) < 0)
+ atf_tc_fail("Preselection flag: %s", strerror(errno));
+
+ /* Set local preselection flag for non-attributable audit_events */
+ if (ioctl(filedesc, AUDITPIPE_SET_PRESELECT_NAFLAGS, fmask) < 0)
+ atf_tc_fail("Preselection naflag: %s", strerror(errno));
+
+ /* Query the maximum possible queue length limit for auditpipe */
+ if (ioctl(filedesc, AUDITPIPE_GET_QLIMIT_MAX, &qlimit_max) < 0)
+ atf_tc_fail("Query max-limit: %s", strerror(errno));
+
+ /* Set the queue length limit as obtained from previous step */
+ if (ioctl(filedesc, AUDITPIPE_SET_QLIMIT, &qlimit_max) < 0)
+ atf_tc_fail("Set max-qlimit: %s", strerror(errno));
+
+ /* This removes any outstanding record on the auditpipe */
+ if (ioctl(filedesc, AUDITPIPE_FLUSH) < 0)
+ atf_tc_fail("Auditpipe flush: %s", strerror(errno));
+}
+
+/*
+ * Get the corresponding audit_mask for class-name "name" then set the
+ * success and failure bits for fmask to be used as the ioctl argument
+ */
+static au_mask_t
+get_audit_mask(const char *name)
+{
+ au_mask_t fmask;
+ au_class_ent_t *class;
+
+ ATF_REQUIRE((class = getauclassnam(name)) != NULL);
+ fmask.am_success = class->ac_class;
+ fmask.am_failure = class->ac_class;
+ return (fmask);
+}
+
+/*
+ * Loop until the auditpipe returns something, check if it is what
+ * we want, else repeat the procedure until ppoll(2) times out.
+ */
+static void
+check_auditpipe(struct pollfd fd[], const char *auditregex, FILE *pipestream)
+{
+ struct timespec currtime, endtime, timeout;
+
+ /* Set the expire time for poll(2) while waiting for syscall audit */
+ ATF_REQUIRE_EQ(0, clock_gettime(CLOCK_MONOTONIC, &endtime));
+ /* Set limit to 30 seconds total and ~10s without an event. */
+ endtime.tv_sec += 30;
+
+ for (;;) {
+ /* Update the time left for auditpipe to return any event */
+ ATF_REQUIRE_EQ(0, clock_gettime(CLOCK_MONOTONIC, &currtime));
+ timespecsub(&endtime, &currtime, &timeout);
+ timeout.tv_sec = MIN(timeout.tv_sec, 9);
+ if (timeout.tv_sec < 0) {
+ atf_tc_fail("%s not found in auditpipe within the "
+ "time limit", auditregex);
+ }
+
+ switch (ppoll(fd, 1, &timeout, NULL)) {
+ /* ppoll(2) returns, check if it's what we want */
+ case 1:
+ if (fd[0].revents & POLLIN) {
+ if (get_records(auditregex, pipestream))
+ return;
+ } else {
+ atf_tc_fail("Auditpipe returned an "
+ "unknown event %#x", fd[0].revents);
+ }
+ break;
+
+ /* poll(2) timed out */
+ case 0:
+ atf_tc_fail("%s not found in auditpipe within the "
+ "time limit", auditregex);
+ break;
+
+ /* poll(2) standard error */
+ case -1:
+ atf_tc_fail("Poll: %s", strerror(errno));
+ break;
+
+ default:
+ atf_tc_fail("Poll returned too many file descriptors");
+ }
+ }
+}
+
+/*
+ * Wrapper functions around static "check_auditpipe"
+ */
+static void
+check_audit_startup(struct pollfd fd[], const char *auditrgx, FILE *pipestream){
+ check_auditpipe(fd, auditrgx, pipestream);
+}
+
+void
+check_audit(struct pollfd fd[], const char *auditrgx, FILE *pipestream) {
+ check_auditpipe(fd, auditrgx, pipestream);
+
+ /* Teardown: /dev/auditpipe's instance opened for this test-suite */
+ ATF_REQUIRE_EQ(0, fclose(pipestream));
+}
+
+void
+skip_if_extattr_not_supported(const char *path)
+{
+ ssize_t result;
+
+ /*
+ * Some file systems (e.g. tmpfs) do not support extattr, so we need
+ * skip tests that use extattrs. To detect this we can check whether
+ * the extattr_list_file returns EOPNOTSUPP.
+ */
+ result = extattr_list_file(path, EXTATTR_NAMESPACE_USER, NULL, 0);
+ if (result == -1 && errno == EOPNOTSUPP) {
+ atf_tc_skip("File system does not support extattrs.");
+ }
+}
+
+static bool
+is_auditd_running(void)
+{
+ int trigger;
+ int err;
+
+ /*
+ * AUDIT_TRIGGER_INITIALIZE is a no-op message on FreeBSD and can
+ * therefore be used to check whether auditd has already been started.
+ * This is significantly cheaper than running `service auditd onestatus`
+ * for each test case. It is also slightly less racy since it will only
+ * return true once auditd() has opened the trigger file rather than
+ * just when the pidfile has been created.
+ */
+ trigger = AUDIT_TRIGGER_INITIALIZE;
+ err = auditon(A_SENDTRIGGER, &trigger, sizeof(trigger));
+ if (err == 0) {
+ fprintf(stderr, "auditd(8) is running.\n");
+ return (true);
+ } else {
+ /*
+ * A_SENDTRIGGER returns ENODEV if auditd isn't listening,
+ * all other error codes indicate a fatal error.
+ */
+ ATF_REQUIRE_MSG(errno == ENODEV,
+ "Unexpected error from auditon(2): %s", strerror(errno));
+ return (false);
+ }
+
+}
+
+FILE *
+setup(struct pollfd fd[], const char *name)
+{
+ au_mask_t fmask, nomask;
+ FILE *pipestream;
+ fmask = get_audit_mask(name);
+ nomask = get_audit_mask("no");
+
+ ATF_REQUIRE((fd[0].fd = open("/dev/auditpipe", O_RDONLY)) != -1);
+ ATF_REQUIRE((pipestream = fdopen(fd[0].fd, "r")) != NULL);
+ fd[0].events = POLLIN;
+
+ /*
+ * Disable stream buffering for read operations from /dev/auditpipe.
+ * Otherwise it is possible that fread(3), called via au_read_rec(3),
+ * can store buffered data in user-space unbeknown to ppoll(2), which
+ * as a result, reports that /dev/auditpipe is empty.
+ */
+ ATF_REQUIRE_EQ(0, setvbuf(pipestream, NULL, _IONBF, 0));
+
+ /* Set local preselection audit_class as "no" for audit startup */
+ set_preselect_mode(fd[0].fd, &nomask);
+ if (!is_auditd_running()) {
+ fprintf(stderr, "Running audit_quick_start() for testing... ");
+ /*
+ * Previously, this test started auditd using
+ * `service auditd onestart`. However, there is a race condition
+ * there since service can return before auditd(8) has
+ * fully started (once the daemon parent process has forked)
+ * and this can cause check_audit_startup() to fail sometimes.
+ *
+ * In the CheriBSD CI this caused the first test executed by
+ * kyua (administrative:acct_failure) to fail every time, but
+ * subsequent ones would almost always succeed.
+ *
+ * To avoid this problem (and as a nice side-effect this speeds
+ * up the test quite a bit), we register this process as a
+ * "fake" auditd(8) using the audit_quick_start() function from
+ * libauditd.
+ */
+ atf_utils_create_file("started_fake_auditd", "yes\n");
+ ATF_REQUIRE(atf_utils_file_exists("started_fake_auditd"));
+ ATF_REQUIRE_EQ_MSG(0, audit_quick_start(),
+ "Failed to start fake auditd: %m");
+ fprintf(stderr, "done.\n");
+ /* audit_quick_start() should log an audit start event. */
+ check_audit_startup(fd, "audit startup", pipestream);
+ /*
+ * If we exit cleanly shutdown audit_quick_start(), if not
+ * cleanup() will take care of it.
+ * This is not required, but makes it easier to run individual
+ * tests outside of kyua.
+ */
+ atexit(cleanup);
+ }
+
+ /* Set local preselection parameters specific to "name" audit_class */
+ set_preselect_mode(fd[0].fd, &fmask);
+ return (pipestream);
+}
+
+void
+cleanup(void)
+{
+ if (atf_utils_file_exists("started_fake_auditd")) {
+ fprintf(stderr, "Running audit_quick_stop()... ");
+ if (audit_quick_stop() != 0) {
+ fprintf(stderr, "Failed to stop fake auditd: %m\n");
+ abort();
+ }
+ fprintf(stderr, "done.\n");
+ unlink("started_fake_auditd");
+ }
+}
diff --git a/tests/sys/audit/utils.h b/tests/sys/audit/utils.h
new file mode 100644
index 000000000000..774734206ded
--- /dev/null
+++ b/tests/sys/audit/utils.h
@@ -0,0 +1,60 @@
+/*-
+ * Copyright 2018 Aniket Pandey
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef _UTILS_H_
+#define _UTILS_H_
+
+#include <poll.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <bsm/audit.h>
+
+void check_audit(struct pollfd [], const char *, FILE *);
+FILE *setup(struct pollfd [], const char *);
+void cleanup(void);
+void skip_if_extattr_not_supported(const char *);
+
+#define REQUIRE_EXTATTR_SUCCESS(call) \
+ ({ \
+ errno = 0; /* Reset errno before call */ \
+ ssize_t result = (call); \
+ if (result == -1) { \
+ atf_tc_fail_requirement(__FILE__, __LINE__, \
+ "%s failed with errno %d (%s)", #call, errno, \
+ strerror(errno)); \
+ } \
+ result; \
+ })
+
+#define REQUIRE_EXTATTR_RESULT(_expected, expr) \
+ do { \
+ ssize_t expected = (_expected); \
+ ssize_t _result = REQUIRE_EXTATTR_SUCCESS(expr); \
+ ATF_REQUIRE_EQ_MSG(expected, _result, "%s: %zd != %zd", #expr, \
+ expected, _result); \
+ } while (0)
+
+#endif /* _SETUP_H_ */
diff --git a/tests/sys/auditpipe/Makefile b/tests/sys/auditpipe/Makefile
new file mode 100644
index 000000000000..189535ee74ca
--- /dev/null
+++ b/tests/sys/auditpipe/Makefile
@@ -0,0 +1,8 @@
+TESTSDIR= ${TESTSBASE}/sys/auditpipe
+
+ATF_TESTS_C= auditpipe_test
+
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= required_files="/dev/auditpipe"
+
+.include <bsd.test.mk>
diff --git a/tests/sys/auditpipe/auditpipe_test.c b/tests/sys/auditpipe/auditpipe_test.c
new file mode 100644
index 000000000000..ce6275ef07b2
--- /dev/null
+++ b/tests/sys/auditpipe/auditpipe_test.c
@@ -0,0 +1,183 @@
+/*-
+ * Copyright (c) 2018 Aniket Pandey
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * SUCH DAMAGE.
+ */
+
+#include <sys/ioctl.h>
+
+#include <bsm/audit.h>
+#include <security/audit/audit_ioctl.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+static int filedesc;
+static FILE *fileptr;
+
+ATF_TC(auditpipe_get_qlen);
+ATF_TC_HEAD(auditpipe_get_qlen, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Verifies whether the auditpipe ioctl, "
+ "AUDITPIPE_GET_QLEN works properly");
+}
+
+ATF_TC_BODY(auditpipe_get_qlen, tc)
+{
+ int qlen = -1;
+ ATF_REQUIRE((filedesc = open("/dev/auditpipe", O_RDONLY)) != -1);
+ ATF_REQUIRE_EQ(0, ioctl(filedesc, AUDITPIPE_GET_QLEN, &qlen));
+ ATF_REQUIRE(qlen != -1);
+ close(filedesc);
+}
+
+
+ATF_TC(auditpipe_get_qlimit);
+ATF_TC_HEAD(auditpipe_get_qlimit, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Verifies whether the auditpipe ioctl, "
+ "AUDITPIPE_GET_QLIMIT works properly");
+}
+
+ATF_TC_BODY(auditpipe_get_qlimit, tc)
+{
+ int qlimit = -1;
+ ATF_REQUIRE((filedesc = open("/dev/auditpipe", O_RDONLY)) != -1);
+ ATF_REQUIRE_EQ(0, ioctl(filedesc, AUDITPIPE_GET_QLIMIT, &qlimit));
+ ATF_REQUIRE(qlimit != -1);
+ close(filedesc);
+}
+
+
+ATF_TC_WITH_CLEANUP(auditpipe_set_qlimit);
+ATF_TC_HEAD(auditpipe_set_qlimit, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Verifies whether the auditpipe ioctl, "
+ "AUDITPIPE_SET_QLIMIT works properly");
+}
+
+ATF_TC_BODY(auditpipe_set_qlimit, tc)
+{
+ int test_qlimit, curr_qlimit, recv_qlimit;
+
+ ATF_REQUIRE((filedesc = open("/dev/auditpipe", O_RDONLY)) != -1);
+ /* Retreive the current QLIMIT value and store it in a file */
+ ATF_REQUIRE_EQ(0, ioctl(filedesc, AUDITPIPE_GET_QLIMIT, &curr_qlimit));
+ ATF_REQUIRE((fileptr = fopen("qlimit_store", "a")) != NULL);
+ ATF_REQUIRE_EQ(sizeof(curr_qlimit),
+ fprintf(fileptr, "%d\n", curr_qlimit));
+
+ /*
+ * Set QLIMIT different from the current system value to confirm
+ * proper functioning of AUDITPIPE_SET_QLIMIT ioctl.
+ */
+ test_qlimit = curr_qlimit - 1;
+ ATF_REQUIRE_EQ(0, ioctl(filedesc, AUDITPIPE_SET_QLIMIT, &test_qlimit));
+ /* Receive modified value and check whether QLIMIT was set correctly */
+ ATF_REQUIRE_EQ(0, ioctl(filedesc, AUDITPIPE_GET_QLIMIT, &recv_qlimit));
+ ATF_REQUIRE_EQ(test_qlimit, recv_qlimit);
+
+ fclose(fileptr);
+ close(filedesc);
+}
+
+ATF_TC_CLEANUP(auditpipe_set_qlimit, tc)
+{
+ if (atf_utils_file_exists("qlimit_store")) {
+ int fd, curr_qlim;
+ ATF_REQUIRE((fileptr = fopen("qlimit_store", "r")) != NULL);
+ ATF_REQUIRE(fscanf(fileptr, "%d", &curr_qlim));
+
+ ATF_REQUIRE((fd = open("/dev/auditpipe", O_RDONLY)) != -1);
+ /* Set QLIMIT's value as it was prior to test-case invocation */
+ ATF_REQUIRE_EQ(0, ioctl(fd, AUDITPIPE_SET_QLIMIT, &curr_qlim));
+
+ close(fd);
+ fclose(fileptr);
+ }
+}
+
+
+ATF_TC(auditpipe_get_qlimit_min);
+ATF_TC_HEAD(auditpipe_get_qlimit_min, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Verifies whether the auditpipe ioctl, "
+ "AUDITPIPE_GET_QLIMIT_MIN works properly");
+}
+
+ATF_TC_BODY(auditpipe_get_qlimit_min, tc)
+{
+ int qlim_min = -1;
+ ATF_REQUIRE((filedesc = open("/dev/auditpipe", O_RDONLY)) != -1);
+ ATF_REQUIRE_EQ(0, ioctl(filedesc, AUDITPIPE_GET_QLIMIT_MIN, &qlim_min));
+ ATF_REQUIRE(qlim_min != -1);
+ close(filedesc);
+}
+
+
+ATF_TC(auditpipe_get_qlimit_max);
+ATF_TC_HEAD(auditpipe_get_qlimit_max, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Verifies whether the auditpipe ioctl, "
+ "AUDITPIPE_GET_QLIMIT_MAX works properly");
+}
+
+ATF_TC_BODY(auditpipe_get_qlimit_max, tc)
+{
+ int qlim_max = -1;
+ ATF_REQUIRE((filedesc = open("/dev/auditpipe", O_RDONLY)) != -1);
+ ATF_REQUIRE_EQ(0, ioctl(filedesc, AUDITPIPE_GET_QLIMIT_MAX, &qlim_max));
+ ATF_REQUIRE(qlim_max != -1);
+ close(filedesc);
+}
+
+
+ATF_TC(auditpipe_get_maxauditdata);
+ATF_TC_HEAD(auditpipe_get_maxauditdata, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Verifies whether the auditpipe ioctl, "
+ "AUDITPIPE_GET_MAXAUDITDATA works properly");
+}
+
+ATF_TC_BODY(auditpipe_get_maxauditdata, tc)
+{
+ int audata = -1;
+ ATF_REQUIRE((filedesc = open("/dev/auditpipe", O_RDONLY)) != -1);
+ ATF_REQUIRE_EQ(0, ioctl(filedesc, AUDITPIPE_GET_MAXAUDITDATA, &audata));
+ ATF_REQUIRE(audata != -1);
+ close(filedesc);
+}
+
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, auditpipe_get_qlen);
+ ATF_TP_ADD_TC(tp, auditpipe_get_qlimit);
+ ATF_TP_ADD_TC(tp, auditpipe_set_qlimit);
+ ATF_TP_ADD_TC(tp, auditpipe_get_qlimit_min);
+ ATF_TP_ADD_TC(tp, auditpipe_get_qlimit_max);
+ ATF_TP_ADD_TC(tp, auditpipe_get_maxauditdata);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/cam/Makefile b/tests/sys/cam/Makefile
new file mode 100644
index 000000000000..4cc36604280a
--- /dev/null
+++ b/tests/sys/cam/Makefile
@@ -0,0 +1,7 @@
+.include <src.opts.mk>
+
+TESTSDIR= ${TESTSBASE}/sys/cam
+
+TESTS_SUBDIRS+= ctl
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cam/ctl/Makefile b/tests/sys/cam/ctl/Makefile
new file mode 100644
index 000000000000..05f0831fc8b0
--- /dev/null
+++ b/tests/sys/cam/ctl/Makefile
@@ -0,0 +1,20 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/cam/ctl
+BINDIR=${TESTSDIR}
+
+${PACKAGE}FILES+= ctl.subr
+
+ATF_TESTS_SH+= persist
+ATF_TESTS_SH+= prevent
+ATF_TESTS_SH+= read_buffer
+ATF_TESTS_SH+= start_stop_unit
+
+PROGS+= prout_register_huge_cdb
+LIBADD+= cam
+CFLAGS+= -I${SRCTOP}/sys
+
+# Must be exclusive because it disables/enables camsim
+TEST_METADATA+= is_exclusive="true"
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cam/ctl/ctl.subr b/tests/sys/cam/ctl/ctl.subr
new file mode 100644
index 000000000000..6cc02d774bdb
--- /dev/null
+++ b/tests/sys/cam/ctl/ctl.subr
@@ -0,0 +1,106 @@
+# vim: filetype=sh
+
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 Axcient
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS DOCUMENTATION IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+find_device() {
+ LUN=$1
+
+ # Rescan camsim
+ # XXX camsim doesn't update when creating a new device. Worse, a
+ # rescan won't look for new devices. So we must disable/re-enable it.
+ # Worse still, enabling it isn't synchronous, so we need a retry loop
+ # https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=281000
+ retries=5
+ ctladm port -o off -p 0 >/dev/null
+ ctladm port -o on -p 0 >/dev/null
+ HEXLUN=`printf %x $LUN`
+ while true; do
+ dev=`camcontrol devlist | awk -v lun=$HEXLUN '
+ /FREEBSD CTL.*,pass/ && $9==lun {
+ split($10, fields, /[,]/); print fields[1];
+ }
+ /FREEBSD CTL.*\(pass/ && $9==lun {
+ split($10, fields, /[,]/); print fields[2];
+ }
+ ' | sed 's:[()]::'`
+ if [ -z "$dev" -o ! -c /dev/$dev ]; then
+ retries=$(( $retries - 1 ))
+ if [ $retries -eq 0 ]; then
+ cat lun-create.txt
+ camcontrol devlist
+ atf_fail "Could not find GEOM device"
+ fi
+ sleep 0.1
+ continue
+ fi
+ break
+ done
+ # Ensure that it's actually ready. camcontrol may report the disk's
+ # ident before it's actually ready to receive commands. Maybe that's
+ # because all of the GEOM providers must probe it?
+ while true; do
+ dd if=/dev/$dev bs=4096 count=1 of=/dev/null >/dev/null 2>/dev/null && break
+ retries=$(( $retries - 1 ))
+ if [ $retries -eq 0 ]; then
+ atf_fail "Device never became ready"
+ fi
+ sleep 0.1
+ done
+}
+
+# Create a CTL LUN backed by a file
+create_block() {
+ EXTRA_ARGS=$*
+
+ atf_check -o save:lun-create.txt ctladm create -b block $EXTRA_ARGS
+ atf_check egrep -q "LUN created successfully" lun-create.txt
+ LUN=`awk '/LUN ID:/ {print $NF}' lun-create.txt`
+ if [ -z "$LUN" ]; then
+ atf_fail "Could not find LUN id"
+ fi
+ find_device $LUN
+}
+
+# Create a CTL LUN backed by RAM
+create_ramdisk() {
+ EXTRA_ARGS=$*
+
+ atf_check -o save:lun-create.txt ctladm create -b ramdisk -s 1048576 $EXTRA_ARGS
+ atf_check egrep -q "LUN created successfully" lun-create.txt
+ LUN=`awk '/LUN ID:/ {print $NF}' lun-create.txt`
+ if [ -z "$LUN" ]; then
+ atf_fail "Could not find LUN id"
+ fi
+ find_device $LUN
+}
+
+cleanup() {
+ if [ -e "lun-create.txt" ]; then
+ backend=`awk '/backend:/ {print $NF}' lun-create.txt`
+ lun_id=`awk '/LUN ID:/ {print $NF}' lun-create.txt`
+ ctladm remove -b $backend -l $lun_id > /dev/null
+ fi
+}
diff --git a/tests/sys/cam/ctl/persist.sh b/tests/sys/cam/ctl/persist.sh
new file mode 100644
index 000000000000..2a350ee4775a
--- /dev/null
+++ b/tests/sys/cam/ctl/persist.sh
@@ -0,0 +1,349 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 ConnectWise
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS DOCUMENTATION IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+. $(atf_get_srcdir)/ctl.subr
+
+# TODO
+# * PRIN READ RESERVATION, with one reservation
+# * PROUT with illegal type
+# * PROUT REGISTER AND IGNORE EXISTING KEY
+# * PROUT REGISTER AND IGNORE EXISTING KEY with a RESERVATION KEY that isn't registered
+# * PROUT REGISTER AND IGNORE EXISTING KEY to unregister
+# * PROUT CLEAR allows previously prevented medium removal
+# * PROUT PREEMPT
+# * PROUT PREEMPT with a RESERVATION KEY that isn't registered
+# * PROUT PREEMPT_AND_ABORT
+# * PROUT PREEMPT_AND_ABORT with a RESERVATION KEY that isn't registered
+# * PROUT REGISTER AND MOVE
+# * PROUT REGISTER AND MOVE with a RESERVATION KEY that isn't registered
+# * multiple initiators
+
+# Not Tested
+# * PROUT REPLACE LOST RESERVATION (not supported by ctl)
+# * Specify Initiator Ports bit (not supported by ctl)
+# * Activate Persist Through Power Loss bit (not supported by ctl)
+# * All Target Ports bit (not supported by ctl)
+
+RESERVATION_KEY=0xdeadbeef1a7ebabe
+
+atf_test_case prin_read_full_status_empty cleanup
+prin_read_full_status_empty_head()
+{
+ atf_set "descr" "PERSISTENT RESERVATION IN with the READ FULL STATUS service action, with no status descriptors"
+ atf_set "require.user" "root"
+ atf_set "require.progs" sg_persist ctladm
+}
+prin_read_full_status_empty_body()
+{
+ create_ramdisk
+
+ atf_check -o match:"No full status descriptors" sg_persist -ns /dev/$dev
+}
+prin_read_full_status_empty_cleanup()
+{
+ cleanup
+}
+
+atf_test_case prin_read_keys_empty cleanup
+prin_read_keys_empty_head()
+{
+ atf_set "descr" "PERSISTENT RESERVATION IN with the READ KEYS service action, with no registered keys"
+ atf_set "require.user" "root"
+ atf_set "require.progs" sg_persist ctladm
+}
+prin_read_keys_empty_body()
+{
+ create_ramdisk
+
+ atf_check -o match:"there are NO registered reservation keys" sg_persist -nk /dev/$dev
+}
+prin_read_keys_empty_cleanup()
+{
+ cleanup
+}
+
+atf_test_case prin_read_reservation_empty cleanup
+prin_read_reservation_empty_head()
+{
+ atf_set "descr" "PERSISTENT RESERVATION IN with the READ RESERVATION service action, with no reservations"
+ atf_set "require.user" "root"
+ atf_set "require.progs" sg_persist ctladm
+}
+prin_read_reservation_empty_body()
+{
+ create_ramdisk
+
+ atf_check -o match:"there is NO reservation held" sg_persist -nr /dev/$dev
+}
+prin_read_reservation_empty_cleanup()
+{
+ cleanup
+}
+
+atf_test_case prin_report_capabilities cleanup
+prin_report_capabilities_head()
+{
+ atf_set "descr" "PERSISTENT RESERVATION IN with the REPORT CAPABILITIES service action"
+ atf_set "require.user" "root"
+ atf_set "require.progs" sg_persist ctladm
+}
+prin_report_capabilities_body()
+{
+ create_ramdisk
+
+ cat > expected <<HERE
+Report capabilities response:
+ Replace Lost Reservation Capable(RLR_C): 0
+ Compatible Reservation Handling(CRH): 1
+ Specify Initiator Ports Capable(SIP_C): 0
+ All Target Ports Capable(ATP_C): 0
+ Persist Through Power Loss Capable(PTPL_C): 0
+ Type Mask Valid(TMV): 1
+ Allow Commands: 5
+ Persist Through Power Loss Active(PTPL_A): 0
+ Support indicated in Type mask:
+ Write Exclusive, all registrants: 1
+ Exclusive Access, registrants only: 1
+ Write Exclusive, registrants only: 1
+ Exclusive Access: 1
+ Write Exclusive: 1
+ Exclusive Access, all registrants: 1
+HERE
+ atf_check -o file:expected sg_persist -nc /dev/$dev
+}
+prin_report_capabilities_cleanup()
+{
+ cleanup
+}
+
+atf_test_case prout_clear cleanup
+prout_clear_head()
+{
+ atf_set "descr" "PERSISTENT RESERVATION OUT with the CLEAR service action"
+ atf_set "require.user" "root"
+ atf_set "require.progs" sg_persist ctladm
+}
+prout_clear_body()
+{
+ create_ramdisk
+
+ # First register a key
+ atf_check sg_persist -n --out --param-rk=0 --param-sark=$RESERVATION_KEY -G /dev/$dev
+
+ # Then make a reservation using that key
+ atf_check sg_persist -n --out --param-rk=$RESERVATION_KEY --reserve --prout-type=8 /dev/$dev
+
+ # Now, clear all reservations and registrations
+ atf_check sg_persist -n --out --param-rk=$RESERVATION_KEY --clear /dev/$dev
+
+ # Finally, check that all reservations and keys are gone
+ atf_check -o match:"there is NO reservation held" sg_persist -nr /dev/$dev
+ atf_check -o match:"there are NO registered reservation keys" sg_persist -nk /dev/$dev
+}
+prout_clear_cleanup()
+{
+ cleanup
+}
+
+
+atf_test_case prout_register cleanup
+prout_register_head()
+{
+ atf_set "descr" "PERSISTENT RESERVATION OUT with the REGISTER service action"
+ atf_set "require.user" "root"
+ atf_set "require.progs" sg_persist ctladm
+}
+prout_register_body()
+{
+ create_ramdisk
+ atf_check sg_persist -n --out --param-rk=0 --param-sark=$RESERVATION_KEY -G /dev/$dev
+ atf_check -o match:$RESERVATION_KEY sg_persist -nk /dev/$dev
+}
+prout_register_cleanup()
+{
+ cleanup
+}
+
+atf_test_case prout_register_duplicate cleanup
+prout_register_duplicate_head()
+{
+ atf_set "descr" "attempting to register a key twice should fail"
+ atf_set "require.user" "root"
+ atf_set "require.progs" sg_persist ctladm
+}
+prout_register_duplicate_body()
+{
+ create_ramdisk
+ atf_check sg_persist -n --out --param-rk=0 --param-sark=$RESERVATION_KEY -G /dev/$dev
+ atf_check -s exit:24 -e match:"Reservation conflict" sg_persist -n --out --param-rk=0 --param-sark=$RESERVATION_KEY -G /dev/$dev
+ atf_check -o match:$RESERVATION_KEY sg_persist -nk /dev/$dev
+}
+prout_register_duplicate_cleanup()
+{
+ cleanup
+}
+
+atf_test_case prout_register_huge_cdb cleanup
+prout_register_huge_cdb_head()
+{
+ atf_set "descr" "PERSISTENT RESERVATION OUT with an enormous CDB size should not cause trouble"
+ atf_set "require.user" "root"
+ atf_set "require.progs" sg_persist ctladm
+}
+prout_register_huge_cdb_body()
+{
+ create_ramdisk
+
+ atf_check -s exit:1 $(atf_get_srcdir)/prout_register_huge_cdb $LUN
+}
+prout_register_huge_cdb_cleanup()
+{
+ cleanup
+}
+
+atf_test_case prout_register_unregister cleanup
+prout_register_unregister_head()
+{
+ atf_set "descr" "use PERSISTENT RESERVATION OUT with the REGISTER service action to remove a prior registration"
+ atf_set "require.user" "root"
+ atf_set "require.progs" sg_persist ctladm
+}
+prout_register_unregister_body()
+{
+ create_ramdisk
+ # First register a key
+ atf_check sg_persist -n --out --param-rk=0 --param-sark=$RESERVATION_KEY -G /dev/$dev
+ # Then unregister it
+ atf_check sg_persist -n --out --param-sark=0 --param-rk=$RESERVATION_KEY -G /dev/$dev
+ # Finally, check that no keys are registered
+ atf_check -o match:"there are NO registered reservation keys" sg_persist -nk /dev/$dev
+}
+prout_register_unregister_cleanup()
+{
+ cleanup
+}
+
+atf_test_case prout_release cleanup
+prout_release_head()
+{
+ atf_set "descr" "PERSISTENT RESERVATION OUT with the RESERVE service action"
+ atf_set "require.user" "root"
+ atf_set "require.progs" sg_persist ctladm
+}
+prout_release_body()
+{
+ create_ramdisk
+
+ # First register a key
+ atf_check sg_persist -n --out --param-rk=0 --param-sark=$RESERVATION_KEY -G /dev/$dev
+
+ # Then make a reservation using that key
+ atf_check sg_persist -n --out --param-rk=$RESERVATION_KEY --reserve --prout-type=8 /dev/$dev
+ atf_check sg_persist -n --out --param-rk=$RESERVATION_KEY --prout-type=8 --release /dev/$dev
+
+ # Now check that the reservation is released
+ atf_check -o match:"there is NO reservation held" sg_persist -nr /dev/$dev
+ # But the registration shouldn't be.
+ atf_check -o match:$RESERVATION_KEY sg_persist -nk /dev/$dev
+}
+prout_release_cleanup()
+{
+ cleanup
+}
+
+
+atf_test_case prout_reserve cleanup
+prout_reserve_head()
+{
+ atf_set "descr" "PERSISTENT RESERVATION OUT with the RESERVE service action"
+ atf_set "require.user" "root"
+ atf_set "require.progs" sg_persist ctladm
+}
+prout_reserve_body()
+{
+ create_ramdisk
+ # First register a key
+ atf_check sg_persist -n --out --param-rk=0 --param-sark=$RESERVATION_KEY -G /dev/$dev
+ # Then make a reservation using that key
+ atf_check sg_persist -n --out --param-rk=$RESERVATION_KEY --reserve --prout-type=8 /dev/$dev
+ # Finally, check that the reservation is correct
+ cat > expected <<HERE
+ PR generation=0x1
+ Key=0xdeadbeef1a7ebabe
+ All target ports bit clear
+ Relative port address: 0x0
+ << Reservation holder >>
+ scope: LU_SCOPE, type: Exclusive Access, all registrants
+ Transport Id of initiator:
+ Parallel SCSI initiator SCSI address: 0x1
+ relative port number (of corresponding target): 0x0
+HERE
+ atf_check -o file:expected sg_persist -ns /dev/$dev
+}
+prout_reserve_cleanup()
+{
+ cleanup
+}
+
+atf_test_case prout_reserve_bad_scope cleanup
+prout_reserve_bad_scope_head()
+{
+ atf_set "descr" "PERSISTENT RESERVATION OUT will be rejected with an unknown scope field"
+ atf_set "require.user" "root"
+ atf_set "require.progs" sg_persist camcontrol ctladm
+}
+prout_reserve_bad_scope_body()
+{
+ create_ramdisk
+ # First register a key
+ atf_check sg_persist -n --out --param-rk=0 --param-sark=$RESERVATION_KEY -G /dev/$dev
+
+ # Then make a reservation using that key
+ atf_check -s exit:1 -e match:"ILLEGAL REQUEST asc:24,0 .Invalid field in CDB." camcontrol persist $dev -o reserve -k $RESERVATION_KEY -T read_shared -s 15 -v
+
+ # Finally, check that nothing has been reserved
+ atf_check -o match:"there is NO reservation held" sg_persist -nr /dev/$dev
+}
+prout_reserve_bad_scope_cleanup()
+{
+ cleanup
+}
+
+
+atf_init_test_cases()
+{
+ atf_add_test_case prin_read_full_status_empty
+ atf_add_test_case prin_read_keys_empty
+ atf_add_test_case prin_read_reservation_empty
+ atf_add_test_case prin_report_capabilities
+ atf_add_test_case prout_clear
+ atf_add_test_case prout_register
+ atf_add_test_case prout_register_duplicate
+ atf_add_test_case prout_register_huge_cdb
+ atf_add_test_case prout_register_unregister
+ atf_add_test_case prout_release
+ atf_add_test_case prout_reserve
+ atf_add_test_case prout_reserve_bad_scope
+}
diff --git a/tests/sys/cam/ctl/prevent.sh b/tests/sys/cam/ctl/prevent.sh
new file mode 100644
index 000000000000..315bedc39581
--- /dev/null
+++ b/tests/sys/cam/ctl/prevent.sh
@@ -0,0 +1,161 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 Axcient
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS DOCUMENTATION IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+. $(atf_get_srcdir)/ctl.subr
+
+# TODO
+# * multiple initiators may block removal
+
+# Not Tested
+# * persistent removal (not implemented in CTL)
+
+atf_test_case allow cleanup
+allow_head()
+{
+ atf_set "descr" "SCSI PREVENT ALLOW MEDIUM REMOVAL will prevent a CD from being ejected"
+ atf_set "require.user" "root"
+ atf_set "require.progs" "sg_prevent sg_start ctladm"
+}
+allow_body()
+{
+ # -t 5 for CD/DVD device type
+ create_ramdisk -t 5
+
+ atf_check sg_prevent --prevent 1 /dev/$dev
+
+ # Now sg_start --eject should fail
+ atf_check -s exit:5 -e match:"Illegal request" sg_start --eject /dev/$dev
+
+ atf_check sg_prevent --allow /dev/$dev
+
+ # Now sg_start --eject should work again
+ atf_check -s exit:0 sg_start --eject /dev/$dev
+}
+allow_cleanup()
+{
+ cleanup
+}
+
+atf_test_case allow_idempotent cleanup
+allow_idempotent_head()
+{
+ atf_set "descr" "SCSI PREVENT ALLOW MEDIUM REMOVAL is idempotent when run from the same initiator"
+ atf_set "require.user" "root"
+ atf_set "require.progs" "sg_prevent sg_start ctladm"
+}
+allow_idempotent_body()
+{
+ # -t 5 for CD/DVD device type
+ create_ramdisk -t 5
+
+ atf_check sg_prevent --allow /dev/$dev
+ atf_check sg_prevent --allow /dev/$dev
+ atf_check sg_prevent --prevent 1 /dev/$dev
+
+ # Even though we ran --allow twice, a single --prevent command should
+ # suffice to prevent ejecting. Multiple ALLOW/PREVENT commands from
+ # the same initiator don't have any additional effect.
+ atf_check -s exit:5 -e match:"Illegal request" sg_start --eject /dev/$dev
+}
+allow_idempotent_cleanup()
+{
+ cleanup
+}
+
+atf_test_case nonremovable cleanup
+nonremovable_head()
+{
+ atf_set "descr" "SCSI PREVENT ALLOW MEDIUM REMOVAL may not be used on non-removable media"
+ atf_set "require.user" "root"
+ atf_set "require.progs" "sg_prevent ctladm"
+}
+nonremovable_body()
+{
+ # Create a HDD, not a CD, device
+ create_ramdisk -t 0
+
+ atf_check -s exit:9 -e match:"Invalid opcode" sg_prevent /dev/$dev
+}
+nonremovable_cleanup()
+{
+ cleanup
+}
+
+atf_test_case prevent cleanup
+prevent_head()
+{
+ atf_set "descr" "SCSI PREVENT ALLOW MEDIUM REMOVAL will prevent a CD from being ejected"
+ atf_set "require.user" "root"
+ atf_set "require.progs" "sg_prevent sg_start ctladm"
+}
+prevent_body()
+{
+ # -t 5 for CD/DVD device type
+ create_ramdisk -t 5
+
+ atf_check sg_prevent --prevent 1 /dev/$dev
+
+ # Now sg_start --eject should fail
+ atf_check -s exit:5 -e match:"Illegal request" sg_start --eject /dev/$dev
+}
+prevent_cleanup()
+{
+ cleanup
+}
+
+atf_test_case prevent_idempotent cleanup
+prevent_idempotent_head()
+{
+ atf_set "descr" "SCSI PREVENT ALLOW MEDIUM REMOVAL is idempotent when run from the same initiator"
+ atf_set "require.user" "root"
+ atf_set "require.progs" "sg_prevent sg_start ctladm"
+}
+prevent_idempotent_body()
+{
+ # -t 5 for CD/DVD device type
+ create_ramdisk -t 5
+
+ atf_check sg_prevent --prevent 1 /dev/$dev
+ atf_check sg_prevent --prevent 1 /dev/$dev
+ atf_check sg_prevent --allow /dev/$dev
+
+ # Even though we ran prevent idempotent and allow only once, eject
+ # should be allowed. Multiple PREVENT commands from the same initiator
+ # don't have any additional effect.
+ atf_check sg_start --eject /dev/$dev
+}
+prevent_idempotent_cleanup()
+{
+ cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case allow
+ atf_add_test_case allow_idempotent
+ atf_add_test_case nonremovable
+ atf_add_test_case prevent
+ atf_add_test_case prevent_idempotent
+}
diff --git a/tests/sys/cam/ctl/prout_register_huge_cdb.c b/tests/sys/cam/ctl/prout_register_huge_cdb.c
new file mode 100644
index 000000000000..f57a6abfadd6
--- /dev/null
+++ b/tests/sys/cam/ctl/prout_register_huge_cdb.c
@@ -0,0 +1,88 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 ConnectWise
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS DOCUMENTATION IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Helper that sends a PERSISTENT RESERVATION OUT command to CTL with a
+ * ridiculously huge size for the length of the CDB. This is not possible with
+ * ctladm, for good reason.
+ */
+#include <camlib.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include <cam/scsi/scsi_message.h>
+#include <cam/ctl/ctl_io.h>
+#include <cam/ctl/ctl.h>
+#include <cam/ctl/ctl_ioctl.h>
+#include <cam/ctl/ctl_util.h>
+
+int
+main(int argc, char **argv)
+{
+ union ctl_io *io;
+ int fd = open("/dev/cam/ctl", O_RDWR);
+ int r;
+ uint32_t targ_port;
+
+ if (argc < 2)
+ errx(2, "usage: prout_register_huge_cdb <target_port>\n");
+
+ targ_port = strtoul(argv[1], NULL, 10);
+
+ io = calloc(1, sizeof(*io));
+ io->io_hdr.nexus.initid = 7; /* 7 is ctladm's default initiator id */
+ io->io_hdr.nexus.targ_port = targ_port;
+ io->io_hdr.nexus.targ_mapped_lun = 0;
+ io->io_hdr.nexus.targ_lun = 0;
+ io->io_hdr.io_type = CTL_IO_SCSI;
+ io->taskio.tag_type = CTL_TAG_UNTAGGED;
+ uint8_t cdb[32] = {};
+ // ctl_persistent_reserve_out// 5f 00
+ cdb[0] = 0x5f;
+ cdb[1] = 0x00;
+ struct scsi_per_res_out *cdb_ = ( struct scsi_per_res_out *)cdb;
+ // Claim an enormous size of the CDB, but don't actually alloc it all.
+ cdb_->length[0] = 0xff;
+ cdb_->length[1] = 0xff;
+ cdb_->length[2] = 0xff;
+ cdb_->length[3] = 0xff;
+ io->scsiio.cdb_len = sizeof(cdb);
+ memcpy(io->scsiio.cdb, cdb, sizeof(cdb));
+ io->io_hdr.flags |= CTL_FLAG_DATA_IN;
+ r = ioctl(fd, CTL_IO, io);
+ if (r == -1)
+ err(1, "ioctl");
+ if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) {
+ return (0);
+ } else {
+ return (1);
+ }
+}
diff --git a/tests/sys/cam/ctl/read_buffer.sh b/tests/sys/cam/ctl/read_buffer.sh
new file mode 100644
index 000000000000..98515943dd40
--- /dev/null
+++ b/tests/sys/cam/ctl/read_buffer.sh
@@ -0,0 +1,172 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 Axcient
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS DOCUMENTATION IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Not tested
+# * modes other than "Data" and "Desc". We don't support those.
+# * Buffer ID other than 0. We don't support those.
+# * The Mode Specific field. We don't support it.
+
+. $(atf_get_srcdir)/ctl.subr
+
+atf_test_case basic cleanup
+basic_head()
+{
+ atf_set "descr" "READ BUFFER can retrieve data previously written by WRITE BUFFER"
+ atf_set "require.user" "root"
+ atf_set "require.progs" "sg_read_buffer sg_write_buffer ctladm"
+}
+basic_body()
+{
+ create_ramdisk
+
+ # Write to its buffer
+ cp /etc/passwd input
+ len=`wc -c input | cut -wf 2`
+ atf_check -o ignore sg_write_buffer --mode data --in=input /dev/$dev
+
+ # Read it back
+ atf_check -o save:output sg_read_buffer --mode data -l $len --raw /dev/$dev
+
+ # And verify
+ if ! diff -q input output; then
+ atf_fail "Miscompare!"
+ fi
+}
+basic_cleanup()
+{
+ cleanup
+}
+
+# Read from the Descriptor mode. Along with Data, these are the only two modes
+# we support.
+atf_test_case desc cleanup
+desc_head()
+{
+ atf_set "descr" "READ BUFFER can retrieve the buffer size via the DESCRIPTOR mode"
+ atf_set "require.user" "root"
+ atf_set "require.progs" "sg_read_buffer ctladm"
+}
+desc_body()
+{
+ create_ramdisk
+
+ atf_check -o inline:" 00 00 04 00 00\n" sg_read_buffer --hex --mode desc /dev/$dev
+}
+desc_cleanup()
+{
+ cleanup
+}
+
+atf_test_case length cleanup
+length_head()
+{
+ atf_set "descr" "READ BUFFER can limit its length with the LENGTH field"
+ atf_set "require.user" "root"
+ atf_set "require.progs" "sg_read_buffer sg_write_buffer ctladm"
+}
+length_body()
+{
+ create_ramdisk
+
+ # Write to its buffer
+ atf_check -o ignore -e ignore dd if=/dev/random of=input bs=4096 count=1
+ atf_check -o ignore -e ignore dd if=input bs=2048 count=1 of=expected
+ atf_check -o ignore sg_write_buffer --mode data --in=input /dev/$dev
+
+ # Read it back
+ atf_check -o save:output sg_read_buffer --mode data -l 2048 --raw /dev/$dev
+
+ # And verify
+ if ! diff -q expected output; then
+ atf_fail "Miscompare!"
+ fi
+}
+length_cleanup()
+{
+ cleanup
+}
+
+atf_test_case offset cleanup
+offset_head()
+{
+ atf_set "descr" "READ BUFFER accepts the BUFFER OFFSET field"
+ atf_set "require.user" "root"
+ atf_set "require.progs" "sg_read_buffer sg_write_buffer ctladm"
+}
+offset_body()
+{
+ create_ramdisk
+
+ # Write to its buffer
+ atf_check -o ignore -e ignore dd if=/dev/random of=input bs=4096 count=1
+ atf_check -o ignore -e ignore dd if=input iseek=2 bs=512 count=1 of=expected
+ atf_check -o ignore sg_write_buffer --mode data --in=input /dev/$dev
+
+ # Read it back
+ atf_check -o save:output sg_read_buffer --mode data -l 512 -o 1024 --raw /dev/$dev
+
+ # And verify
+ if ! diff -q expected output; then
+ atf_fail "Miscompare!"
+ fi
+}
+offset_cleanup()
+{
+ cleanup
+}
+
+atf_test_case uninitialized cleanup
+uninitialized_head()
+{
+ atf_set "descr" "READ BUFFER buffers are zero-initialized"
+ atf_set "require.user" "root"
+ atf_set "require.progs" "sg_read_buffer ctladm"
+}
+uninitialized_body()
+{
+ create_ramdisk
+
+ # Read an uninitialized buffer
+ atf_check -o save:output sg_read_buffer --mode data -l 262144 --raw /dev/$dev
+
+ # And verify
+ atf_check -o ignore -e ignore dd if=/dev/zero bs=262144 count=1 of=expected
+ if ! diff -q expected output; then
+ atf_fail "Miscompare!"
+ fi
+}
+uninitialized_cleanup()
+{
+ cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case basic
+ atf_add_test_case desc
+ atf_add_test_case length
+ atf_add_test_case offset
+ atf_add_test_case uninitialized
+}
diff --git a/tests/sys/cam/ctl/start_stop_unit.sh b/tests/sys/cam/ctl/start_stop_unit.sh
new file mode 100644
index 000000000000..a1160b35e4a7
--- /dev/null
+++ b/tests/sys/cam/ctl/start_stop_unit.sh
@@ -0,0 +1,150 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 Axcient
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS DOCUMENTATION IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+. $(atf_get_srcdir)/ctl.subr
+
+# TODO:
+# * format layer
+# * IMM bit
+# * LOEJ
+# * noflush
+# * power conditions
+
+# Not Tested
+# * Power Condition Modifier (not implemented in CTL)
+
+atf_test_case eject cleanup
+eject_head()
+{
+ atf_set "descr" "START STOP UNIT can eject a CDROM device"
+ atf_set "require.user" "root"
+ atf_set "require.progs" "sg_start sg_readcap ctladm"
+}
+eject_body()
+{
+ # -t 5 for CD/DVD device type
+ create_ramdisk -t 5
+
+ # Verify that the device is online
+ # Too bad I don't know of any other way to check that it's stopped but
+ # by using sg_readcap.
+ atf_check -o ignore -e not-match:"Device not ready" sg_readcap /dev/$dev
+
+ # eject the device
+ atf_check sg_start --eject /dev/$dev
+
+ # Ejected, it should now return ENXIO
+ atf_check -s exit:1 -o ignore -e match:"Device not configured" dd if=/dev/$dev bs=4096 count=1 of=/dev/null
+}
+eject_cleanup()
+{
+ cleanup
+}
+
+atf_test_case load cleanup
+load_head()
+{
+ atf_set "descr" "START STOP UNIT can load a CDROM device"
+ atf_set "require.user" "root"
+ atf_set "require.progs" "sg_start sg_readcap ctladm"
+}
+load_body()
+{
+ # -t 5 for CD/DVD device type
+ create_ramdisk -t 5
+
+ # eject the device
+ atf_check sg_start --eject /dev/$dev
+
+ # Verify that it's offline it should now return ENXIO
+ atf_check -s exit:1 -o ignore -e match:"Device not configured" dd if=/dev/$dev bs=4096 count=1 of=/dev/null
+
+ # Load it again
+ atf_check sg_start --load /dev/$dev
+
+ atf_check -o ignore -e ignore dd if=/dev/$dev bs=4096 count=1 of=/dev/null
+ atf_check -o ignore -e not-match:"Device not ready" sg_readcap /dev/$dev
+}
+load_cleanup()
+{
+ cleanup
+}
+
+atf_test_case start cleanup
+start_head()
+{
+ atf_set "descr" "START STOP UNIT can start a device"
+ atf_set "require.user" "root"
+ atf_set "require.progs" "sg_start sg_readcap ctladm"
+}
+start_body()
+{
+ create_ramdisk
+
+ # stop the device
+ atf_check sg_start --stop /dev/$dev
+
+ # And start it again
+ atf_check sg_start /dev/$dev
+
+ # Now sg_readcap should succeed. Too bad I don't know of any other way
+ # to check that it's stopped.
+ atf_check -o ignore -e not-match:"Device not ready" sg_readcap /dev/$dev
+}
+start_cleanup()
+{
+ cleanup
+}
+
+atf_test_case stop cleanup
+stop_head()
+{
+ atf_set "descr" "START STOP UNIT can stop a device"
+ atf_set "require.user" "root"
+ atf_set "require.progs" "sg_start sg_readcap ctladm"
+}
+stop_body()
+{
+ create_ramdisk
+
+ # Stop the device
+ atf_check sg_start --stop /dev/$dev
+
+ # Now sg_readcap should fail. Too bad I don't know of any other way to
+ # check that it's stopped.
+ atf_check -s exit:2 -e match:"Device not ready" sg_readcap /dev/$dev
+}
+stop_cleanup()
+{
+ cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case eject
+ atf_add_test_case load
+ atf_add_test_case start
+ atf_add_test_case stop
+}
diff --git a/tests/sys/capsicum/Makefile b/tests/sys/capsicum/Makefile
new file mode 100644
index 000000000000..fd8dcb29d65c
--- /dev/null
+++ b/tests/sys/capsicum/Makefile
@@ -0,0 +1,65 @@
+.include <src.opts.mk>
+
+TESTSDIR= ${TESTSBASE}/sys/capsicum
+
+ATF_TESTS_C+= bindat_connectat
+ATF_TESTS_C+= ioctls_test
+
+CFLAGS+= -I${SRCTOP}/tests
+
+.if ${MK_GOOGLETEST} != no
+
+.PATH: ${SRCTOP}/contrib/capsicum-test
+
+GTESTS+= capsicum-test
+GTESTS_WRAPPER_SH.capsicum-test= functional
+# This test script runs the same test suite twice, once as root and once as an
+# unprivileged user. Serialize them since some tests access global namespaces,
+# e.g., mqueuefs, and can trample on each other.
+TEST_METADATA.functional+= is_exclusive="true"
+
+SRCS.capsicum-test+= \
+ capsicum-test-main.cc \
+ capsicum-test.cc \
+ capability-fd.cc \
+ copy_file_range.cc \
+ fexecve.cc \
+ procdesc.cc \
+ capmode.cc \
+ fcntl.cc \
+ ioctl.cc \
+ openat.cc \
+ sysctl.cc \
+ select.cc \
+ mqueue.cc \
+ socket.cc \
+ sctp.cc \
+ capability-fd-pair.cc \
+ overhead.cc \
+ rename.cc
+
+LIBADD.capsicum-test+= gtest pthread procstat
+TEST_METADATA.capsicum-test= required_user="unprivileged"
+
+.for p in mini-me mini-me.noexec mini-me.setuid
+PROGS+= $p
+NO_SHARED.$p=
+SRCS.$p= mini-me.c
+.endfor
+.if ${MK_ASAN} != "no" || ${MK_UBSAN} != "no"
+# mini-me.o is linked into a static binary so we can't use sanitizers.
+# Note: We have to set CFLAGS here since it will be built as part of
+# _PROGS_COMMON_OBJS and therefore NO_SHARED.$p does not disable ASAN/UBSAN.
+CFLAGS.mini-me.c+= -fno-sanitize=address -fno-sanitize=undefined
+.endif
+
+BINDIR= ${TESTSDIR}
+
+BINMODE.mini-me.noexec= ${NOBINMODE}
+BINMODE.mini-me.setuid= 4555
+
+WARNS.capsicum-test= 3
+
+.endif # MK_GOOGLETEST
+
+.include <bsd.test.mk>
diff --git a/tests/sys/capsicum/bindat_connectat.c b/tests/sys/capsicum/bindat_connectat.c
new file mode 100644
index 000000000000..2d7ec706c1cc
--- /dev/null
+++ b/tests/sys/capsicum/bindat_connectat.c
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2017 Jan Kokemüller
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/capsicum.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/stat.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <atf-c.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "freebsd_test_suite/macros.h"
+
+static int rootfd = -1;
+
+/* circumvent bug 215690 */
+int
+open(const char *path, int flags, ...)
+{
+ mode_t mode = 0;
+
+ if (flags & O_CREAT) {
+ va_list ap;
+ va_start(ap, flags);
+ mode = (mode_t) va_arg(ap, int);
+ va_end(ap);
+ }
+
+ if (path && path[0] == '/' && rootfd >= 0) {
+ return (openat(rootfd, path + 1, flags, mode));
+ } else {
+ return (openat(AT_FDCWD, path, flags, mode));
+ }
+}
+
+static void
+check_capsicum(void)
+{
+ ATF_REQUIRE_FEATURE("security_capabilities");
+ ATF_REQUIRE_FEATURE("security_capability_mode");
+
+ ATF_REQUIRE((rootfd = open("/", O_EXEC | O_CLOEXEC)) >= 0);
+}
+
+typedef int (*socket_fun)(int, const struct sockaddr *, socklen_t);
+
+static int
+connectat_fdcwd(int s, const struct sockaddr *name, socklen_t namelen)
+{
+
+ return (connectat(AT_FDCWD, s, name, namelen));
+}
+
+static int
+bindat_fdcwd(int s, const struct sockaddr *name, socklen_t namelen)
+{
+
+ return (bindat(AT_FDCWD, s, name, namelen));
+}
+
+
+ATF_TC(bindat_connectat_1);
+ATF_TC_HEAD(bindat_connectat_1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that connect/bind work in normal case");
+}
+
+static void
+check_1(socket_fun f, int s, const struct sockaddr_in *name)
+{
+
+ ATF_REQUIRE((s = socket(AF_INET, SOCK_STREAM, 0)) >= 0);
+ ATF_REQUIRE_ERRNO(EAFNOSUPPORT,
+ f(s, (const struct sockaddr *)(name),
+ sizeof(struct sockaddr_in)) < 0);
+}
+
+ATF_TC_BODY(bindat_connectat_1, tc)
+{
+ struct sockaddr_in sin;
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(0);
+ sin.sin_addr.s_addr = htonl(0xE0000000);
+
+ check_1(bindat_fdcwd, 0, &sin);
+ check_1(bind, 0, &sin);
+ check_1(connectat_fdcwd, 0, &sin);
+ check_1(connect, 0, &sin);
+}
+
+
+ATF_TC(bindat_connectat_2);
+ATF_TC_HEAD(bindat_connectat_2, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that connect/bind are disabled in cap-mode");
+}
+
+static void
+check_2(socket_fun f, int s, const struct sockaddr_in *name)
+{
+
+ ATF_REQUIRE_ERRNO(ECAPMODE,
+ f(s, (const struct sockaddr *)name,
+ sizeof(struct sockaddr_in)) < 0);
+}
+
+ATF_TC_BODY(bindat_connectat_2, tc)
+{
+ int sock;
+ struct sockaddr_in sin;
+
+ check_capsicum();
+
+ ATF_REQUIRE(cap_enter() >= 0);
+
+ /* note: sock is created _after_ cap_enter() and contains all rights */
+ ATF_REQUIRE((sock = socket(AF_INET, SOCK_STREAM, 0)) >= 0);
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ /* dummy port and multicast address (224.0.0.0) to distinguish two
+ * cases:
+ * - ECAPMODE/ENOTCAPABLE --> call blocked by capsicum
+ * - EAFNOSUPPORT --> call went through to protocol layer
+ */
+ sin.sin_port = htons(0);
+ sin.sin_addr.s_addr = htonl(0xE0000000);
+
+ check_2(bindat_fdcwd, sock, &sin);
+ check_2(bind, sock, &sin);
+ check_2(connectat_fdcwd, sock, &sin);
+ check_2(connect, sock, &sin);
+}
+
+
+ATF_TC(bindat_connectat_3);
+ATF_TC_HEAD(bindat_connectat_3, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Check that taking away CAP_BIND/CAP_CONNECT "
+ "sabotages bind/connect");
+}
+
+static void
+check_3(socket_fun f, int s, const struct sockaddr_in *name,
+ cap_rights_t *rights, cap_rights_t *sub_rights)
+{
+
+ ATF_REQUIRE((s = socket(AF_INET, SOCK_STREAM, 0)) >= 0);
+ ATF_REQUIRE(cap_rights_limit(s, rights) >= 0);
+ ATF_REQUIRE_ERRNO(EAFNOSUPPORT,
+ f(s, (const struct sockaddr *)name,
+ sizeof(struct sockaddr_in)) < 0);
+ ATF_REQUIRE(cap_rights_limit(s,
+ cap_rights_remove(rights, sub_rights)) >= 0);
+ ATF_REQUIRE_ERRNO(ENOTCAPABLE,
+ f(s, (const struct sockaddr *)name,
+ sizeof(struct sockaddr_in)) < 0);
+}
+
+ATF_TC_BODY(bindat_connectat_3, tc)
+{
+ struct sockaddr_in sin;
+ cap_rights_t rights, sub_rights;
+
+ check_capsicum();
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(0);
+ sin.sin_addr.s_addr = htonl(0xE0000000);
+
+ check_3(bindat_fdcwd, 0, &sin,
+ cap_rights_init(&rights, CAP_SOCK_SERVER),
+ cap_rights_init(&sub_rights, CAP_BIND));
+ check_3(bind, 0, &sin,
+ cap_rights_init(&rights, CAP_SOCK_SERVER),
+ cap_rights_init(&sub_rights, CAP_BIND));
+ check_3(connectat_fdcwd, 0, &sin,
+ cap_rights_init(&rights, CAP_SOCK_CLIENT),
+ cap_rights_init(&sub_rights, CAP_CONNECT));
+ check_3(connect, 0, &sin,
+ cap_rights_init(&rights, CAP_SOCK_CLIENT),
+ cap_rights_init(&sub_rights, CAP_CONNECT));
+}
+
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, bindat_connectat_1);
+ ATF_TP_ADD_TC(tp, bindat_connectat_2);
+ ATF_TP_ADD_TC(tp, bindat_connectat_3);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/capsicum/functional.sh b/tests/sys/capsicum/functional.sh
new file mode 100755
index 000000000000..8d4a7eef2dec
--- /dev/null
+++ b/tests/sys/capsicum/functional.sh
@@ -0,0 +1,60 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 The FreeBSD Foundation
+#
+# This software was developed by Li-Wen Hsu <lwhsu@FreeBSD.org>
+# under sponsorship from the FreeBSD Foundation.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+CAPSICUM_TEST_BIN=capsicum-test
+
+atf_test_case "test_root"
+test_root_head() {
+
+ atf_set descr 'Run capsicum-test as root'
+ atf_set require.user root
+}
+
+test_root_body() {
+ atf_check -s exit:0 -o match:PASSED -e ignore \
+ "$(atf_get_srcdir)/${CAPSICUM_TEST_BIN}" -u "$(id -u tests)"
+}
+
+atf_test_case "test_unprivileged"
+test_unprivileged_head() {
+
+ atf_set descr 'Run capsicum-test as an unprivileged user'
+ atf_set require.user unprivileged
+}
+
+test_unprivileged_body() {
+ atf_check -s exit:0 -o match:PASSED -e ignore \
+ "$(atf_get_srcdir)/${CAPSICUM_TEST_BIN}" -u "$(id -u)"
+}
+
+atf_init_test_cases() {
+ atf_add_test_case test_root
+ atf_add_test_case test_unprivileged
+}
diff --git a/tests/sys/capsicum/ioctls_test.c b/tests/sys/capsicum/ioctls_test.c
new file mode 100644
index 000000000000..5db58279071c
--- /dev/null
+++ b/tests/sys/capsicum/ioctls_test.c
@@ -0,0 +1,127 @@
+/*-
+ * Copyright (c) 2018 John Baldwin <jhb@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/capsicum.h>
+#include <sys/filio.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "freebsd_test_suite/macros.h"
+
+/*
+ * A variant of ATF_REQUIRE that is suitable for use in child
+ * processes. This only works if the parent process is tripped up by
+ * the early exit and fails some requirement itself.
+ */
+#define CHILD_REQUIRE(exp) do { \
+ if (!(exp)) \
+ child_fail_require(__FILE__, __LINE__, \
+ #exp " not met"); \
+ } while (0)
+
+static __dead2 void
+child_fail_require(const char *file, int line, const char *str)
+{
+ char buf[128];
+
+ snprintf(buf, sizeof(buf), "%s:%d: %s\n", file, line, str);
+ write(2, buf, strlen(buf));
+ _exit(32);
+}
+
+/*
+ * Exercise the edge case of a custom ioctl list being copied from a
+ * listen socket to an accepted socket.
+ */
+ATF_TC_WITHOUT_HEAD(cap_ioctls__listen_copy);
+ATF_TC_BODY(cap_ioctls__listen_copy, tc)
+{
+ struct sockaddr_in sin;
+ cap_rights_t rights;
+ u_long cmds[] = { FIONREAD };
+ socklen_t len;
+ pid_t pid;
+ char dummy;
+ int s[2], status;
+
+ ATF_REQUIRE_FEATURE("security_capabilities");
+
+ s[0] = socket(AF_INET, SOCK_STREAM, 0);
+ ATF_REQUIRE(s[0] > 0);
+
+ /* Bind to an arbitrary unused port. */
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_port = 0;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ ATF_REQUIRE(bind(s[0], (struct sockaddr *)&sin, sizeof(sin)) == 0);
+
+ CHILD_REQUIRE(listen(s[0], 1) == 0);
+
+ len = sizeof(sin);
+ ATF_REQUIRE(getsockname(s[0], (struct sockaddr *)&sin, &len) == 0);
+ ATF_REQUIRE(len == sizeof(sin));
+
+ cap_rights_init(&rights, CAP_ACCEPT, CAP_IOCTL);
+ ATF_REQUIRE(cap_rights_limit(s[0], &rights) == 0);
+ ATF_REQUIRE(cap_ioctls_limit(s[0], cmds, nitems(cmds)) == 0);
+
+ pid = fork();
+ if (pid == 0) {
+ s[1] = accept(s[0], NULL, NULL);
+ CHILD_REQUIRE(s[1] > 0);
+
+ /* Close both sockets during exit(). */
+ exit(0);
+ }
+
+ ATF_REQUIRE(pid > 0);
+
+ ATF_REQUIRE(close(s[0]) == 0);
+ s[1] = socket(AF_INET, SOCK_STREAM, 0);
+ ATF_REQUIRE(s[1] > 0);
+ ATF_REQUIRE(connect(s[1], (struct sockaddr *)&sin, sizeof(sin)) == 0);
+ ATF_REQUIRE(read(s[1], &dummy, sizeof(dummy)) == 0);
+ ATF_REQUIRE(close(s[1]) == 0);
+
+ ATF_REQUIRE(wait(&status) == pid);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE(WEXITSTATUS(status) == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, cap_ioctls__listen_copy);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/cddl/Makefile b/tests/sys/cddl/Makefile
new file mode 100644
index 000000000000..66377e1e3bfd
--- /dev/null
+++ b/tests/sys/cddl/Makefile
@@ -0,0 +1,11 @@
+.include <src.opts.mk>
+
+TESTSDIR= ${TESTSBASE}/sys/cddl
+
+TESTS_SUBDIRS+= ${_zfs}
+
+.if ${MK_ZFS_TESTS} != "no"
+_zfs= zfs
+.endif
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/Makefile b/tests/sys/cddl/zfs/Makefile
new file mode 100644
index 000000000000..f215f7438e78
--- /dev/null
+++ b/tests/sys/cddl/zfs/Makefile
@@ -0,0 +1,9 @@
+.include <src.opts.mk>
+
+TESTSDIR= ${TESTSBASE}/sys/cddl/zfs
+
+TESTS_SUBDIRS+= tests include
+
+SUBDIR+= bin
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/bin/Makefile b/tests/sys/cddl/zfs/bin/Makefile
new file mode 100644
index 000000000000..98db25e396d3
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/Makefile
@@ -0,0 +1,61 @@
+.include <src.opts.mk>
+
+PACKAGE= tests
+MAN=
+BINDIR= ${TESTSBASE}/sys/cddl/zfs/bin
+SCRIPTSDIR= ${TESTSBASE}/sys/cddl/zfs/bin
+
+WARNS?= 0
+
+SCRIPTS+= bsddisks.ksh
+SCRIPTS+= dircmp.ksh
+SCRIPTS+= dumpadm.ksh
+SCRIPTS+= ff.ksh
+SCRIPTS+= fmadm.ksh
+SCRIPTS+= fmdump.ksh
+SCRIPTS+= format.ksh
+SCRIPTS+= groupadd.ksh
+SCRIPTS+= groupdel.ksh
+SCRIPTS+= groupmod.ksh
+SCRIPTS+= groupshow.ksh
+SCRIPTS+= svcs.ksh
+SCRIPTS+= swap.ksh
+SCRIPTS+= testenv.ksh
+SCRIPTS+= useradd.ksh
+SCRIPTS+= userdel.ksh
+SCRIPTS+= usermod.ksh
+SCRIPTS+= zfs.ksh
+SCRIPTS+= zfs_crypto.ksh
+SCRIPTS+= zfs_version.ksh
+SCRIPTS+= zlogin.ksh
+SCRIPTS+= zoneadm.ksh
+SCRIPTS+= zonecfg.ksh
+SCRIPTS+= zpool.ksh
+SCRIPTS+= zpool_bsd.ksh
+SCRIPTS+= zpool_smi.ksh
+SCRIPTS+= zpool_version.ksh
+
+PROGS+= chg_usr_exec
+# Not ported to FreeBSD
+# PROGRS+= devname2devid
+PROGS+= dir_rd_update
+PROGS+= file_check
+PROGS+= file_trunc
+PROGS+= file_write
+PROGS+= largest_file
+PROGS+= mkfile
+PROGS+= mktree
+PROGS+= mmapwrite
+PROGS+= randfree_file
+PROGS+= readmmap
+PROGS+= rename_dir
+PROGS+= rm_lnkcnt_zero_file
+
+.for p in ${PROGS}
+SRCS.$p= $p.c
+.endfor
+
+LIBADD.mmapwrite+= pthread
+LIBADD.rm_lnkcnt_zero_file+= pthread
+
+.include <bsd.progs.mk>
diff --git a/tests/sys/cddl/zfs/bin/bsddisks.ksh b/tests/sys/cddl/zfs/bin/bsddisks.ksh
new file mode 100644
index 000000000000..102b65597c11
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/bsddisks.ksh
@@ -0,0 +1,4 @@
+#!/usr/local/bin/ksh93
+
+BSDDEVS="ad|da|mlxd|myld|aacd|ided|twed"
+ls /dev|egrep "^($BSDDEVS)[0-9]+\$" |sed 's/^/\/dev\//'
diff --git a/tests/sys/cddl/zfs/bin/chg_usr_exec.c b/tests/sys/cddl/zfs/bin/chg_usr_exec.c
new file mode 100644
index 000000000000..cf491e6f23cf
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/chg_usr_exec.c
@@ -0,0 +1,76 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <pwd.h>
+
+int
+main(int argc, char *argv[])
+{
+ char *plogin = NULL;
+ char cmds[BUFSIZ] = { 0 };
+ char sep[] = " ";
+ struct passwd *ppw = NULL;
+ int i, len;
+
+ if (argc < 3 || strlen(argv[1]) == 0) {
+ (void) printf("\tUsage: %s <login> <commands> ...\n", argv[0]);
+ return (1);
+ }
+
+ plogin = argv[1];
+ len = 0;
+ for (i = 2; i < argc; i++) {
+ (void) snprintf(cmds+len, sizeof (cmds)-len,
+ "%s%s", argv[i], sep);
+ len += strlen(argv[i]) + strlen(sep);
+ }
+
+ if ((ppw = getpwnam(plogin)) == NULL) {
+ perror("getpwnam");
+ return (errno);
+ }
+ if (setgid(ppw->pw_gid) != 0) {
+ perror("setgid");
+ return (errno);
+ }
+ if (setuid(ppw->pw_uid) != 0) {
+ perror("setuid");
+ return (errno);
+ }
+
+ if (execl("/bin/sh", "sh", "-c", cmds, (char *)0) != 0) {
+ perror("execl");
+ return (errno);
+ }
+
+ return (0);
+}
diff --git a/tests/sys/cddl/zfs/bin/devname2devid.c b/tests/sys/cddl/zfs/bin/devname2devid.c
new file mode 100644
index 000000000000..dd67032e97cd
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/devname2devid.c
@@ -0,0 +1,121 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <devid.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+/*
+ * Usage: devname2devid <devicepath>
+ *
+ * Examples:
+ * # ./devname2devid /dev/c1t4d0s0
+ * devid id1,sd@SSEAGATE_ST318404LSUN18G_3BT2G0Z300002146G4CR/a
+ * # ./devname2devid /dev/c1t4d0
+ * devid id1,sd@SSEAGATE_ST318404LSUN18G_3BT2G0Z300002146G4CR/wd
+ * # ./devname2devid /dev/c1t4d0s1
+ * devid id1,sd@SSEAGATE_ST318404LSUN18G_3BT2G0Z300002146G4CR/b
+ * #
+ *
+ * This program accepts a disk or disk slice path and prints a
+ * device id.
+ *
+ * Exit values:
+ * 0 - means success
+ * 1 - means failure
+ *
+ */
+int
+main(int argc, char *argv[])
+{
+ int fd;
+ ddi_devid_t devid;
+ char *minor_name, *devidstr, *device;
+#ifdef DEBUG
+ devid_nmlist_t *list = NULL;
+ char *search_path;
+ int i;
+#endif
+
+ if (argc == 1) {
+ (void) printf("%s <devicepath> [search path]\n",
+ argv[0]);
+ exit(1);
+ }
+ device = argv[1];
+
+ if ((fd = open(device, O_RDONLY|O_NDELAY)) < 0) {
+ perror(device);
+ exit(1);
+ }
+ if (devid_get(fd, &devid) != 0) {
+ perror("devid_get");
+ exit(1);
+ }
+ if (devid_get_minor_name(fd, &minor_name) != 0) {
+ perror("devid_get_minor_name");
+ exit(1);
+ }
+ if ((devidstr = devid_str_encode(devid, minor_name)) == 0) {
+ perror("devid_str_encode");
+ exit(1);
+ }
+
+ (void) printf("devid %s\n", devidstr);
+
+ devid_str_free(devidstr);
+
+#ifdef DEBUG
+ if (argc == 3) {
+ search_path = argv[2];
+ } else {
+ search_path = "/dev/";
+ }
+
+ if (devid_deviceid_to_nmlist(search_path, devid, DEVID_MINOR_NAME_ALL,
+ &list)) {
+ perror("devid_deviceid_to_nmlist");
+ exit(1);
+ }
+
+ /* loop through list and process device names and numbers */
+ for (i = 0; list[i].devname != NULL; i++) {
+ (void) printf("devname: %s %p\n", list[i].devname, list[i].dev);
+ }
+ devid_free_nmlist(list);
+
+#endif /* DEBUG */
+
+ devid_str_free(minor_name);
+ devid_free(devid);
+
+ return (0);
+}
diff --git a/tests/sys/cddl/zfs/bin/dir_rd_update.c b/tests/sys/cddl/zfs/bin/dir_rd_update.c
new file mode 100644
index 000000000000..f48d74f710a5
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/dir_rd_update.c
@@ -0,0 +1,120 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Assertion:
+ *
+ * A read operation and directory update operation performed
+ * concurrently on the same directory can lead to deadlock
+ * on a UFS logging file system, but not on a ZFS file system.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#define TMP_DIR /tmp
+
+static char dirpath[256];
+
+int
+main(int argc, char **argv)
+{
+ char *cp1 = "";
+ int i = 0;
+ int ret = 0;
+ int testdd = 0;
+ pid_t pid;
+ static const int op_num = 5;
+
+ if (argc == 1) {
+ (void) printf("Usage: %s <mount point>\n", argv[0]);
+ exit(-1);
+ }
+ for (i = 0; i < 256; i++) {
+ dirpath[i] = 0;
+ }
+
+ cp1 = argv[1];
+ (void) strcpy(&dirpath[0], (const char *)cp1);
+ (void) strcat(&dirpath[strlen(dirpath)], "TMP_DIR");
+
+ ret = mkdir(dirpath, 0777);
+ if (ret != 0) {
+ if (errno != EEXIST) {
+ (void) printf(
+ "%s: mkdir(<%s>, 0777) failed: errno (decimal)=%d\n",
+ argv[0], dirpath, errno);
+ exit(-1);
+ }
+ }
+ testdd = open(dirpath, O_RDONLY|O_SYNC);
+ if (testdd < 0) {
+ (void) printf(
+"%s: open(<%s>, O_RDONLY|O_SYNC) failed: errno (decimal)=%d\n",
+ argv[0], dirpath, errno);
+ exit(-1);
+ } else {
+ (void) close(testdd);
+ }
+ pid = fork();
+ if (pid > 0) {
+ int fd = open(dirpath, O_RDONLY|O_SYNC);
+ char buf[16];
+ int rdret;
+ int j = 0;
+
+ while (j < op_num) {
+ (void) sleep(1);
+ rdret = read(fd, buf, 16);
+ if (rdret == -1) {
+ (void) printf("readdir failed");
+ }
+ j++;
+ }
+ } else if (pid == 0) {
+ int fd = open(dirpath, O_RDONLY);
+ int chownret;
+ int k = 0;
+
+ while (k < op_num) {
+ (void) sleep(1);
+ chownret = fchown(fd, 0, 0);
+ if (chownret == -1) {
+ (void) printf("chown failed");
+ }
+
+ k++;
+ }
+ }
+
+ return (0);
+}
diff --git a/tests/sys/cddl/zfs/bin/dircmp.ksh b/tests/sys/cddl/zfs/bin/dircmp.ksh
new file mode 100644
index 000000000000..8780a5122d42
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/dircmp.ksh
@@ -0,0 +1,3 @@
+#!/usr/local/bin/ksh93
+
+diff -qr $*
diff --git a/tests/sys/cddl/zfs/bin/dumpadm.ksh b/tests/sys/cddl/zfs/bin/dumpadm.ksh
new file mode 100644
index 000000000000..0c0c0d509009
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/dumpadm.ksh
@@ -0,0 +1,8 @@
+#! /usr/local/bin/ksh93 -p
+
+if [ $# != 0 ]
+then
+ echo "ERROR option not supported"
+ return 1
+fi
+grep dumpdev /etc/rc.conf
diff --git a/tests/sys/cddl/zfs/bin/ff.ksh b/tests/sys/cddl/zfs/bin/ff.ksh
new file mode 100644
index 000000000000..9ac3e43306ca
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/ff.ksh
@@ -0,0 +1,4 @@
+#!/usr/local/bin/ksh93
+
+echo dummy
+exit 0
diff --git a/tests/sys/cddl/zfs/bin/file_check.c b/tests/sys/cddl/zfs/bin/file_check.c
new file mode 100644
index 000000000000..ee741412119d
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/file_check.c
@@ -0,0 +1,87 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include "file_common.h"
+
+static unsigned char bigbuffer[BIGBUFFERSIZE];
+
+/*
+ * Given a filename, check that the file consists entirely
+ * of a particular pattern. If the pattern is not specified a
+ * default will be used. For default values see file_common.h
+ */
+int
+main(int argc, char **argv)
+{
+ int bigfd;
+ long i, n;
+ uint8_t fillchar = DATA;
+ int bigbuffersize = BIGBUFFERSIZE;
+ int64_t read_count = 0;
+
+ /*
+ * Validate arguments
+ */
+ if (argc < 2) {
+ (void) printf("Usage: %s filename [pattern]\n",
+ argv[0]);
+ exit(1);
+ }
+
+ if (argv[2]) {
+ fillchar = atoi(argv[2]);
+ }
+
+ /*
+ * Read the file contents and check every character
+ * against the supplied pattern. Abort if the
+ * pattern check fails.
+ */
+ if ((bigfd = open(argv[1], O_RDONLY)) == -1) {
+ (void) printf("open %s failed %d\n", argv[1], errno);
+ exit(1);
+ }
+
+ do {
+ if ((n = read(bigfd, &bigbuffer, bigbuffersize)) == -1) {
+ (void) printf("read failed (%ld), %d\n", n, errno);
+ exit(errno);
+ }
+
+ for (i = 0; i < n; i++) {
+ if (bigbuffer[i] != fillchar) {
+ (void) printf("error %s: 0x%x != 0x%x)\n",
+ argv[1], bigbuffer[i], fillchar);
+ exit(1);
+ }
+ }
+
+ read_count += n;
+ } while (n == bigbuffersize);
+
+ return (0);
+}
diff --git a/tests/sys/cddl/zfs/bin/file_common.h b/tests/sys/cddl/zfs/bin/file_common.h
new file mode 100644
index 000000000000..f2fd40d31969
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/file_common.h
@@ -0,0 +1,63 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef FILE_COMMON_H
+#define FILE_COMMON_H
+
+
+/*
+ * header file for file_* utilities. These utilities
+ * are used by the test cases to perform various file
+ * operations (append writes, for example).
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define BLOCKSZ 8192
+#define DATA 0xa5
+#define DATA_RANGE 120
+#define BIGBUFFERSIZE 0x800000
+#define BIGFILESIZE 20
+
+extern char *optarg;
+extern int optind, opterr, optopt;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FILE_COMMON_H */
diff --git a/tests/sys/cddl/zfs/bin/file_trunc.c b/tests/sys/cddl/zfs/bin/file_trunc.c
new file mode 100644
index 000000000000..d4d23b6f8073
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/file_trunc.c
@@ -0,0 +1,238 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <string.h>
+
+#define FSIZE 256*1024*1024
+#define BSIZE 512
+
+/* Initialize Globals */
+static long fsize = FSIZE;
+static size_t bsize = BSIZE;
+static int count = 0;
+static int rflag = 0;
+static int seed = 0;
+static int vflag = 0;
+static int errflag = 0;
+static off_t offset = 0;
+static char *filename = NULL;
+
+static void usage(char *execname);
+static void parse_options(int argc, char *argv[]);
+static void do_write(int fd);
+static void do_trunc(int fd);
+
+static void
+usage(char *execname)
+{
+ (void) fprintf(stderr,
+ "usage: %s [-b blocksize] [-c count] [-f filesize]"
+ " [-o offset] [-s seed] [-r] [-v] filename\n", execname);
+ (void) exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int i = 0;
+ int fd = -1;
+
+ parse_options(argc, argv);
+
+ fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0666);
+ if (fd < 0) {
+ perror("open");
+ exit(3);
+ }
+
+ while (i < count) {
+ (void) do_write(fd);
+ (void) do_trunc(fd);
+
+ i++;
+ }
+
+ (void) close(fd);
+ return (0);
+}
+
+static void
+parse_options(int argc, char *argv[])
+{
+ int c;
+
+ extern char *optarg;
+ extern int optind, optopt;
+
+ count = fsize / bsize;
+ seed = time(NULL);
+ while ((c = getopt(argc, argv, "b:c:f:o:rs:v")) != -1) {
+ switch (c) {
+ case 'b':
+ bsize = atoi(optarg);
+ break;
+
+ case 'c':
+ count = atoi(optarg);
+ break;
+
+ case 'f':
+ fsize = atoi(optarg);
+ break;
+
+ case 'o':
+ offset = atoi(optarg);
+ break;
+
+ case 'r':
+ rflag++;
+ break;
+
+ case 's':
+ seed = atoi(optarg);
+ break;
+
+ case 'v':
+ vflag++;
+ break;
+
+ case ':':
+ (void) fprintf(stderr,
+ "Option -%c requires an operand\n", optopt);
+ errflag++;
+ break;
+
+ case '?':
+ (void) fprintf(stderr,
+ "Unrecognized option: -%c\n", optopt);
+ errflag++;
+ break;
+ }
+
+ if (errflag) {
+ (void) usage(argv[0]);
+ }
+ }
+ if (argc <= optind) {
+ (void) fprintf(stderr,
+ "No filename specified\n");
+ usage(argv[0]);
+ }
+ filename = argv[optind];
+
+ if (vflag) {
+ (void) fprintf(stderr, "Seed = %d\n", seed);
+ }
+ srandom(seed);
+}
+
+static void
+do_write(int fd)
+{
+ off_t roffset = 0;
+ char *buf = NULL;
+ char *rbuf = NULL;
+
+ buf = (char *)calloc(1, bsize);
+ rbuf = (char *)calloc(1, bsize);
+ if (buf == NULL || rbuf == NULL) {
+ perror("malloc");
+ exit(4);
+ }
+
+ roffset = random() % fsize;
+ if (lseek(fd, (offset + roffset), SEEK_SET) < 0) {
+ perror("lseek");
+ exit(5);
+ }
+
+ strcpy(buf, "ZFS Test Suite Truncation Test");
+ if (write(fd, buf, bsize) < bsize) {
+ perror("write");
+ exit(6);
+ }
+
+ if (rflag) {
+ if (lseek(fd, (offset + roffset), SEEK_SET) < 0) {
+ perror("lseek");
+ exit(7);
+ }
+
+ if (read(fd, rbuf, bsize) < bsize) {
+ perror("read");
+ exit(8);
+ }
+
+ if (memcmp(buf, rbuf, bsize) != 0) {
+ perror("memcmp");
+ exit(9);
+ }
+ }
+ if (vflag) {
+ (void) fprintf(stderr,
+ "Wrote to offset %ld\n", (offset + roffset));
+ if (rflag) {
+ (void) fprintf(stderr,
+ "Read back from offset %ld\n", (offset + roffset));
+ }
+ }
+
+ (void) free(buf);
+ (void) free(rbuf);
+}
+
+static void
+do_trunc(int fd)
+{
+ off_t roffset = 0;
+
+ roffset = random() % fsize;
+ if (ftruncate(fd, (offset + roffset)) < 0) {
+ perror("truncate");
+ exit(7);
+ }
+
+ if (vflag) {
+ (void) fprintf(stderr,
+ "Truncated at offset %ld\n",
+ (offset + roffset));
+ }
+}
diff --git a/tests/sys/cddl/zfs/bin/file_write.c b/tests/sys/cddl/zfs/bin/file_write.c
new file mode 100644
index 000000000000..597bdd03b65d
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/file_write.c
@@ -0,0 +1,239 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include "file_common.h"
+#include <inttypes.h>
+#include <libgen.h>
+
+static unsigned char bigbuffer[BIGBUFFERSIZE];
+
+/*
+ * Writes (or appends) a given value to a file repeatedly.
+ * See header file for defaults.
+ */
+
+static void usage(void) __dead2;
+static char *execname;
+
+int
+main(int argc, char **argv)
+{
+ int bigfd;
+ int c;
+ int oflag = 0;
+ int err = 0;
+ int k;
+ long i;
+ int64_t good_writes = 0;
+ uint8_t nxtfillchar;
+ /*
+ * Default Parameters
+ */
+ int write_count = BIGFILESIZE;
+ uint8_t fillchar = DATA;
+ int block_size = BLOCKSZ;
+ char *filename = NULL;
+ char *operation = NULL;
+ off_t noffset, offset = 0;
+ int verbose = 0;
+ int rsync = 0;
+ int wsync = 0;
+
+ execname = argv[0];
+
+ /*
+ * Process Arguments
+ */
+ while ((c = getopt(argc, argv, "b:c:d:s:f:o:vwr")) != -1) {
+ switch (c) {
+ case 'b':
+ block_size = atoi(optarg);
+ break;
+ case 'c':
+ write_count = atoi(optarg);
+ break;
+ case 'd':
+ fillchar = atoi(optarg);
+ break;
+ case 's':
+ offset = atoll(optarg);
+ break;
+ case 'f':
+ filename = optarg;
+ break;
+ case 'o':
+ operation = optarg;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'w':
+ wsync = 1;
+ break;
+ case 'r':
+ rsync = 1;
+ break;
+ case '?':
+ (void) printf("unknown arg %c\n", optopt);
+ usage();
+ break;
+ }
+ }
+
+ /*
+ * Validate Parameters
+ */
+ if (!filename) {
+ (void) printf("Filename not specified (-f <file>)\n");
+ err++;
+ }
+
+ if (!operation) {
+ (void) printf("Operation not specified (-o <operation>).\n");
+ err++;
+ }
+
+ if (block_size > BIGBUFFERSIZE) {
+ (void) printf("block_size is too large max==%d.\n",
+ BIGBUFFERSIZE);
+ err++;
+ }
+
+ if (err) usage();
+
+ /*
+ * Prepare the buffer and determine the requested operation
+ */
+ nxtfillchar = fillchar;
+ k = 0;
+
+ for (i = 0; i < block_size; i++) {
+ bigbuffer[i] = nxtfillchar;
+
+ if (fillchar == 0) {
+ if ((k % DATA_RANGE) == 0) {
+ k = 0;
+ }
+ nxtfillchar = k++;
+ }
+ }
+
+ /*
+ * using the strncmp of operation will make the operation match the
+ * first shortest match - as the operations are unique from the first
+ * character this means that we match single character operations
+ */
+ if ((strncmp(operation, "create", strlen(operation) + 1)) == 0 ||
+ (strncmp(operation, "overwrite", strlen(operation) + 1)) == 0) {
+ oflag = (O_RDWR|O_CREAT);
+ } else if ((strncmp(operation, "append", strlen(operation) + 1)) == 0) {
+ oflag = (O_RDWR|O_APPEND);
+ } else {
+ (void) printf("valid operations are <create|append> not '%s'\n",
+ operation);
+ usage();
+ }
+
+#ifdef UNSUPPORTED
+ if (rsync) {
+ oflag = oflag | O_RSYNC;
+ }
+#endif
+
+ if (wsync) {
+ oflag = oflag | O_SYNC;
+ }
+
+ /*
+ * Given an operation (create/overwrite/append), open the file
+ * accordingly and perform a write of the appropriate type.
+ */
+ if ((bigfd = open(filename, oflag, 0666)) == -1) {
+ (void) printf("open %s: failed [%s]%d. Aborting!\n", filename,
+ strerror(errno), errno);
+ exit(errno);
+ }
+ noffset = lseek(bigfd, offset, SEEK_SET);
+ if (noffset != offset) {
+ (void) printf("lseek %s (%"PRId64"/%"PRId64") "
+ "failed [%s]%d. Aborting!\n",
+ filename, offset, noffset, strerror(errno), errno);
+ exit(errno);
+ }
+
+ if (verbose) {
+ (void) printf("%s: block_size = %d, write_count = %d, "
+ "offset = %"PRId64", data = %s%d\n", filename, block_size,
+ write_count, offset,
+ (fillchar == 0) ? "0->" : "",
+ (fillchar == 0) ? DATA_RANGE : fillchar);
+ }
+
+ for (i = 0; i < write_count; i++) {
+ ssize_t n;
+
+ if ((n = write(bigfd, &bigbuffer, block_size)) == -1) {
+ (void) printf("write failed (%ld), "
+ "good_writes = %"PRId64", "
+ "error: %s[%d]\n", (long)n, good_writes,
+ strerror(errno), errno);
+ exit(errno);
+ }
+ good_writes++;
+ }
+
+ if (verbose) {
+ (void) printf("Success: good_writes = %"PRId64" (%"PRId64")\n",
+ good_writes, (good_writes * block_size));
+ }
+
+ return (0);
+}
+
+static void
+usage(void)
+{
+ char *base = (char *)"file_write";
+ char *exec = (char *)execname;
+
+ if (exec != NULL)
+ exec = strdup(exec);
+ if (exec != NULL)
+ base = basename(exec);
+
+ (void) printf("Usage: %s [-v] -o {create,overwrite,append} -f file_name"
+ " [-b block_size]\n"
+ "\t[-s offset] [-c write_count] [-d data]\n"
+ "\twhere [data] equal to zero causes chars "
+ "0->%d to be repeated throughout\n", base, DATA_RANGE);
+
+ if (exec) {
+ free(exec);
+ }
+
+ exit(1);
+}
diff --git a/tests/sys/cddl/zfs/bin/fmadm.ksh b/tests/sys/cddl/zfs/bin/fmadm.ksh
new file mode 100644
index 000000000000..9ac3e43306ca
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/fmadm.ksh
@@ -0,0 +1,4 @@
+#!/usr/local/bin/ksh93
+
+echo dummy
+exit 0
diff --git a/tests/sys/cddl/zfs/bin/fmdump.ksh b/tests/sys/cddl/zfs/bin/fmdump.ksh
new file mode 100644
index 000000000000..9ac3e43306ca
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/fmdump.ksh
@@ -0,0 +1,4 @@
+#!/usr/local/bin/ksh93
+
+echo dummy
+exit 0
diff --git a/tests/sys/cddl/zfs/bin/format.ksh b/tests/sys/cddl/zfs/bin/format.ksh
new file mode 100644
index 000000000000..9ac3e43306ca
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/format.ksh
@@ -0,0 +1,4 @@
+#!/usr/local/bin/ksh93
+
+echo dummy
+exit 0
diff --git a/tests/sys/cddl/zfs/bin/groupadd.ksh b/tests/sys/cddl/zfs/bin/groupadd.ksh
new file mode 100644
index 000000000000..fae75b6b5f9c
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/groupadd.ksh
@@ -0,0 +1,11 @@
+#! /usr/local/bin/ksh93 -p
+
+a=
+g=
+for i in $*
+do
+ a="$a $g"
+ g=$i
+done
+
+/usr/sbin/pw groupadd $g $a
diff --git a/tests/sys/cddl/zfs/bin/groupdel.ksh b/tests/sys/cddl/zfs/bin/groupdel.ksh
new file mode 100644
index 000000000000..923913aaf07f
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/groupdel.ksh
@@ -0,0 +1,11 @@
+#! /usr/local/bin/ksh93 -p
+
+a=
+g=
+for i in $*
+do
+ a="$a $g"
+ g=$i
+done
+
+/usr/sbin/pw groupdel $a $g
diff --git a/tests/sys/cddl/zfs/bin/groupmod.ksh b/tests/sys/cddl/zfs/bin/groupmod.ksh
new file mode 100644
index 000000000000..60b5401f9a34
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/groupmod.ksh
@@ -0,0 +1,11 @@
+#! /usr/local/bin/ksh93 -p
+
+a=
+g=
+for i in $*
+do
+ a="$a $g"
+ g=$i
+done
+
+/usr/sbin/pw groupmod $g $a
diff --git a/tests/sys/cddl/zfs/bin/groupshow.ksh b/tests/sys/cddl/zfs/bin/groupshow.ksh
new file mode 100644
index 000000000000..dced36dc56e3
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/groupshow.ksh
@@ -0,0 +1,3 @@
+#! /usr/local/bin/ksh93 -p
+
+/usr/sbin/pw groupshow $*
diff --git a/tests/sys/cddl/zfs/bin/largest_file.c b/tests/sys/cddl/zfs/bin/largest_file.c
new file mode 100644
index 000000000000..b732a2207c8b
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/largest_file.c
@@ -0,0 +1,130 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include "file_common.h"
+#include <sys/param.h>
+#include <signal.h>
+#include <stdio.h>
+
+/*
+ * --------------------------------------------------------------
+ *
+ * Assertion:
+ * The last byte of the largest file size can be
+ * accessed without any errors. Also, the writing
+ * beyond the last byte of the largest file size
+ * will produce an errno of EFBIG.
+ *
+ * --------------------------------------------------------------
+ * If the write() system call below returns a "1",
+ * then the last byte can be accessed.
+ * --------------------------------------------------------------
+ */
+static void sigxfsz(int);
+static void usage(char *);
+
+int
+main(int argc, char **argv)
+{
+ int fd = 0;
+ off_t offset = (OFF_MAX - 1);
+ off_t lseek_ret = 0;
+ int write_ret = 0;
+ int err = 0;
+ char mybuf[5];
+ char *testfile;
+
+ if (argc != 2) {
+ usage(argv[0]);
+ }
+
+ (void) sigset(SIGXFSZ, sigxfsz);
+
+ testfile = strdup(argv[1]);
+
+ fd = open(testfile, O_CREAT | O_RDWR);
+ if (fd < 0) {
+ perror("Failed to create testfile");
+ err = errno;
+ goto out;
+ }
+
+ lseek_ret = lseek(fd, offset, SEEK_SET);
+ if (lseek_ret < 0) {
+ perror("Failed to seek to end of testfile");
+ err = errno;
+ goto out;
+ }
+
+ write_ret = write(fd, mybuf, 1);
+ if (write_ret < 0) {
+ perror("Failed to write to end of file");
+ err = errno;
+ goto out;
+ }
+
+ offset = 0;
+ lseek_ret = lseek(fd, offset, SEEK_CUR);
+ if (lseek_ret < 0) {
+ perror("Failed to seek to end of file");
+ err = errno;
+ goto out;
+ }
+
+ write_ret = write(fd, mybuf, 1);
+ if (write_ret < 0) {
+ if (errno == EFBIG) {
+ (void) printf("write errno=EFBIG: success\n");
+ err = 0;
+ } else {
+ perror("Did not receive EFBIG");
+ err = errno;
+ }
+ } else {
+ (void) printf("write completed successfully, test failed\n");
+ err = 1;
+ }
+
+out:
+ (void) unlink(testfile);
+ free(testfile);
+ return (err);
+}
+
+static void
+usage(char *name)
+{
+ (void) printf("%s <testfile>\n", name);
+ exit(1);
+}
+
+/* ARGSUSED */
+static void
+sigxfsz(int signo)
+{
+ (void) printf("\nlargest_file: sigxfsz() caught SIGXFSZ\n");
+}
diff --git a/tests/sys/cddl/zfs/bin/mkfile.c b/tests/sys/cddl/zfs/bin/mkfile.c
new file mode 100644
index 000000000000..0b7f43c8f42f
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/mkfile.c
@@ -0,0 +1,209 @@
+/*-
+ * Copyright (c) 2001-2013
+ * HATANO Tomomi. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+
+#define MKFILE_WBUF ((size_t)(1048576)) /* Is 1M a reasonable value? */
+
+/* SunOS's mkfile(8) sets "sticky bit." */
+#define MKFILE_FLAG (O_WRONLY | O_CREAT | O_TRUNC)
+#define MKFILE_MODE (S_IRUSR | S_IWUSR | S_ISVTX)
+
+static char buf[MKFILE_WBUF];
+static int nofill = 0;
+static int verbose = 0;
+
+static void
+usage()
+{
+ fprintf(stderr,
+ "Usage: mkfile [-nv] <size>[e|p|t|g|m|k|b] <filename> ...\n");
+}
+
+static unsigned long long
+getsize(char *s)
+{
+ int sh;
+ unsigned long long length;
+ char *suffix;
+
+ /*
+ * NOTE: We don't handle 'Z' (zetta) or 'Y' (yotta) suffixes yet.
+ * These are too large to store in unsigned long long (64bits).
+ * In the future, we'll have to use larger type,
+ * something like uint128_t.
+ */
+ length = strtoull(s, &suffix, 10);
+ sh = 0;
+ switch (tolower(*suffix)) {
+ case 'e': /* Exabytes. */
+ sh = 60;
+ break;
+ case 'p': /* Petabytes. */
+ sh = 50;
+ break;
+ case 't': /* Terabytes. */
+ sh = 40;
+ break;
+ case 'g': /* Gigabytes. */
+ sh = 30;
+ break;
+ case 'm': /* Megabytes. */
+ sh = 20;
+ break;
+ case 'k': /* Kilobytes. */
+ sh = 10;
+ break;
+ case 'b': /* Blocks. */
+ sh = 9;
+ break;
+ case '\0': /* Bytes. */
+ break;
+ default: /* Unknown... */
+ errno = EINVAL;
+ return 0;
+ }
+ if (sh) {
+ unsigned long long l;
+
+ l = length;
+ length <<= sh;
+ /* Check overflow. */
+ if ((length >> sh) != l) {
+ errno = ERANGE;
+ return 0;
+ }
+ }
+
+ return length;
+}
+
+static int
+create_file(char *f, unsigned long long s)
+{
+ int fd;
+ size_t w;
+ ssize_t ws;
+
+ if (verbose) {
+ fprintf(stdout, "%s %llu bytes\n", f, s);
+ fflush(stdout);
+ }
+
+ /* Open file to create. */
+ if ((fd = open(f, MKFILE_FLAG, MKFILE_MODE)) < 0) {
+ return -1;
+ }
+
+ /* Seek to the end and write 1 byte. */
+ if ((lseek(fd, (off_t)(s - 1LL), SEEK_SET) == (off_t)-1) ||
+ (write(fd, buf, (size_t)1) == (ssize_t)-1)) {
+ /*
+ * We don't close(fd) here to avoid overwriting errno.
+ * This is fd-leak, but is not harmful
+ * because returning error causes mkfile(8) to exit.
+ */
+ return -1;
+ }
+
+ /* Fill. */
+ if (!nofill) {
+ if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
+ /* Same as above. */
+ return -1;
+ }
+ while (s) {
+ w = (s > MKFILE_WBUF) ? MKFILE_WBUF : s;
+ if ((ws = write(fd, buf, w)) == (ssize_t)-1) {
+ /* Same as above. */
+ return -1;
+ }
+ s -= ws;
+ }
+ }
+ close(fd);
+
+ return 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+ unsigned long long fsize;
+ int ch;
+
+ /* We have at least 2 arguments. */
+ if (argc < 3) {
+ usage();
+ return EXIT_FAILURE;
+ }
+
+ /* Options. */
+ while ((ch = getopt(argc, argv, "nv")) != -1) {
+ switch (ch) {
+ case 'n':
+ nofill = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ usage();
+ return EXIT_FAILURE;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* File size to create. */
+ if ((fsize = getsize(*argv)) == 0) {
+ perror(*argv);
+ return EXIT_FAILURE;
+ }
+
+ /* Filenames to create. */
+ bzero(buf, MKFILE_WBUF);
+ while (++argv, --argc) {
+ if (create_file(*argv, fsize) == -1) {
+ perror(*argv);
+ return EXIT_FAILURE;
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/sys/cddl/zfs/bin/mktree.c b/tests/sys/cddl/zfs/bin/mktree.c
new file mode 100644
index 000000000000..5cfad210de91
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/mktree.c
@@ -0,0 +1,194 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+
+#define TYPE_D 'D'
+#define TYPE_F 'F'
+
+extern int errno;
+
+static char fdname[MAXPATHLEN] = {0};
+static char *pbasedir = NULL;
+static int nlevel = 2;
+static int ndir = 2;
+static int nfile = 2;
+
+static void usage(char *this);
+static void crtfile(char *pname);
+static char *getfdname(char *pdir, char type, int level, int dir, int file);
+static int mktree(char *pbasedir, int level);
+
+int
+main(int argc, char *argv[])
+{
+ int c, ret;
+
+ while ((c = getopt(argc, argv, "b:l:d:f:")) != -1) {
+ switch (c) {
+ case 'b':
+ pbasedir = optarg;
+ break;
+ case 'l':
+ nlevel = atoi(optarg);
+ break;
+ case 'd':
+ ndir = atoi(optarg);
+ break;
+ case 'f':
+ nfile = atoi(optarg);
+ break;
+ case '?':
+ usage(argv[0]);
+ }
+ }
+ if (nlevel < 0 || ndir < 0 || nfile < 0 || pbasedir == NULL) {
+ usage(argv[0]);
+ }
+
+ ret = mktree(pbasedir, 1);
+
+ return (ret);
+}
+
+static void
+usage(char *this)
+{
+ (void) fprintf(stderr,
+ "\tUsage: %s -b <base_dir> -l [nlevel] -d [ndir] -f [nfile]\n",
+ this);
+ exit(1);
+}
+
+static int
+mktree(char *pdir, int level)
+{
+ int d, f;
+ char dname[MAXPATHLEN] = {0};
+ char fname[MAXPATHLEN] = {0};
+
+ if (level > nlevel) {
+ return (1);
+ }
+
+ for (d = 0; d < ndir; d++) {
+ (void) memset(dname, '\0', sizeof (dname));
+ (void) strcpy(dname, getfdname(pdir, TYPE_D, level, d, 0));
+
+ if (mkdir(dname, 0777) != 0) {
+ (void) fprintf(stderr, "mkdir(%s) failed."
+ "\n[%d]: %s.\n",
+ dname, errno, strerror(errno));
+ exit(errno);
+ }
+
+ /*
+ * No sub-directory need be created, only create files in it.
+ */
+ if (mktree(dname, level+1) != 0) {
+ for (f = 0; f < nfile; f++) {
+ (void) memset(fname, '\0', sizeof (fname));
+ (void) strcpy(fname,
+ getfdname(dname, TYPE_F, level+1, d, f));
+ crtfile(fname);
+ }
+ }
+ }
+
+ for (f = 0; f < nfile; f++) {
+ (void) memset(fname, '\0', sizeof (fname));
+ (void) strcpy(fname, getfdname(pdir, TYPE_F, level, d, f));
+ crtfile(fname);
+ }
+
+ return (0);
+}
+
+static char *
+getfdname(char *pdir, char type, int level, int dir, int file)
+{
+ (void) snprintf(fdname, sizeof (fdname),
+ "%s/%c-l%dd%df%d", pdir, type, level, dir, file);
+ return (fdname);
+}
+
+static void
+crtfile(char *pname)
+{
+ int fd = -1;
+ int afd = -1;
+ int i, size;
+ char *context = "0123456789ABCDF";
+ char *pbuf;
+
+ if (pname == NULL) {
+ exit(1);
+ }
+
+ size = sizeof (char) * 1024;
+ pbuf = (char *)valloc(size);
+ for (i = 0; i < size / strlen(context); i++) {
+ int offset = i * strlen(context);
+ (void) snprintf(pbuf+offset, size-offset, "%s", context);
+ }
+
+ if ((fd = open(pname, O_CREAT|O_RDWR, 0777)) < 0) {
+ (void) fprintf(stderr, "open(%s, O_CREAT|O_RDWR, 0777) failed."
+ "\n[%d]: %s.\n", pname, errno, strerror(errno));
+ exit(errno);
+ }
+ if (write(fd, pbuf, 1024) < 1024) {
+ (void) fprintf(stderr, "write(fd, pbuf, 1024) failed."
+ "\n[%d]: %s.\n", errno, strerror(errno));
+ exit(errno);
+ }
+
+#if UNSUPPORTED
+ if ((afd = openat(fd, "xattr", O_CREAT | O_RDWR | O_XATTR, 0777)) < 0) {
+ (void) fprintf(stderr, "openat failed.\n[%d]: %s.\n",
+ errno, strerror(errno));
+ exit(errno);
+ }
+ if (write(afd, pbuf, 1024) < 1024) {
+ (void) fprintf(stderr, "write(afd, pbuf, 1024) failed."
+ "\n[%d]: %s.\n", errno, strerror(errno));
+ exit(errno);
+ }
+
+ (void) close(afd);
+#endif
+ (void) close(fd);
+ free(pbuf);
+}
diff --git a/tests/sys/cddl/zfs/bin/mmapwrite.c b/tests/sys/cddl/zfs/bin/mmapwrite.c
new file mode 100644
index 000000000000..75b36a75a33a
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/mmapwrite.c
@@ -0,0 +1,97 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <pthread.h>
+
+/*
+ * --------------------------------------------------------------------
+ * Bug Id: 5032643
+ *
+ * Simply writing to a file and mmaping that file at the same time can
+ * result in deadlock. Nothing perverse like writing from the file's
+ * own mapping is required.
+ * --------------------------------------------------------------------
+ */
+
+static void *
+mapper(void *fdp)
+{
+ void *addr;
+ int fd = *(int *)fdp;
+
+ if ((addr =
+ mmap(0, 8192, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+ perror("mmap");
+ exit(1);
+ }
+ for (;;) {
+ if (mmap(addr, 8192, PROT_READ,
+ MAP_SHARED|MAP_FIXED, fd, 0) == MAP_FAILED) {
+ perror("mmap");
+ exit(1);
+ }
+ }
+ /* NOTREACHED */
+ return ((void *)1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int fd;
+ char buf[BUFSIZ];
+ pthread_t pt;
+
+ if (argc != 2) {
+ (void) printf("usage: %s <file name>\n", argv[0]);
+ exit(1);
+ }
+
+ if ((fd = open(argv[1], O_RDWR|O_CREAT|O_TRUNC, 0666)) == -1) {
+ perror("open");
+ exit(1);
+ }
+
+ if (pthread_create(&pt, NULL, mapper, &fd) != 0) {
+ perror("pthread_create");
+ exit(1);
+ }
+ for (;;) {
+ if (write(fd, buf, sizeof (buf)) == -1) {
+ perror("write");
+ exit(1);
+ }
+ }
+
+ /* NOTREACHED */
+ return (0);
+}
diff --git a/tests/sys/cddl/zfs/bin/randfree_file.c b/tests/sys/cddl/zfs/bin/randfree_file.c
new file mode 100644
index 000000000000..a32e3cef2af8
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/randfree_file.c
@@ -0,0 +1,102 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include "file_common.h"
+
+/*
+ * Create a file with assigned size and then free the specified
+ * section of the file
+ */
+
+static void usage(char *progname);
+
+static void
+usage(char *progname)
+{
+ (void) fprintf(stderr,
+ "usage: %s [-l filesize] [-s start-offset]"
+ "[-n section-len] filename\n", progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *filename, *buf;
+ size_t filesize;
+ off_t start_off, off_len;
+ int fd, ch;
+ struct flock fl;
+
+ while ((ch = getopt(argc, argv, "l:s:n:")) != EOF) {
+ switch (ch) {
+ case 'l':
+ filesize = atoll(optarg);
+ break;
+ case 's':
+ start_off = atoll(optarg);
+ break;
+ case 'n':
+ off_len = atoll(optarg);
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ if (optind == argc - 1)
+ filename = argv[optind];
+ else
+ usage(argv[0]);
+
+ buf = (char *)malloc(filesize);
+
+ if ((fd = open(filename, O_RDWR|O_CREAT|O_TRUNC)) < 0) {
+ perror("open");
+ return (1);
+ }
+ if (write(fd, buf, filesize) < filesize) {
+ perror("write");
+ return (1);
+ }
+#if UNSUPPORTED
+ fl.l_whence = SEEK_SET;
+ fl.l_start = start_off;
+ fl.l_len = off_len;
+ if (fcntl(fd, F_FREESP, &fl) != 0) {
+ perror("fcntl");
+ return (1);
+ }
+#else
+ fprintf(stderr, "fcntl: F_FREESP not supported\n");
+ return (1);
+#endif
+
+ free(buf);
+ return (0);
+}
diff --git a/tests/sys/cddl/zfs/bin/readmmap.c b/tests/sys/cddl/zfs/bin/readmmap.c
new file mode 100644
index 000000000000..1d8eb403cb33
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/readmmap.c
@@ -0,0 +1,139 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * --------------------------------------------------------------
+ * BugId 5047993 : Getting bad read data.
+ *
+ * Usage: readmmap <filename>
+ *
+ * where:
+ * filename is an absolute path to the file name.
+ *
+ * Return values:
+ * 1 : error
+ * 0 : no errors
+ * --------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/mman.h>
+
+int
+main(int argc, char **argv)
+{
+ char *filename = "badfile";
+ size_t size = 4395;
+ size_t idx = 0;
+ char *buf = NULL;
+ char *map = NULL;
+ int fd = -1, bytes, retval = 0;
+ unsigned seed;
+
+ if (argc < 2 || optind == argc) {
+ (void) fprintf(stderr,
+ "usage: %s <file name>\n", argv[0]);
+ exit(1);
+ }
+
+ if ((buf = calloc(1, size)) == NULL) {
+ perror("calloc");
+ exit(1);
+ }
+
+ filename = argv[optind];
+
+ (void) remove(filename);
+
+ fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0666);
+ if (fd == -1) {
+ perror("open to create");
+ retval = 1;
+ goto end;
+ }
+
+ bytes = write(fd, buf, size);
+ if (bytes != size) {
+ (void) printf("short write: %d != %zu\n", bytes, size);
+ retval = 1;
+ goto end;
+ }
+
+ map = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ if (map == MAP_FAILED) {
+ perror("mmap");
+ retval = 1;
+ goto end;
+ }
+ seed = time(NULL);
+ srandom(seed);
+
+ idx = random() % size;
+ map[idx] = 1;
+
+ if (msync(map, size, MS_SYNC) != 0) {
+ perror("msync");
+ retval = 1;
+ goto end;
+ }
+
+ if (munmap(map, size) != 0) {
+ perror("munmap");
+ retval = 1;
+ goto end;
+ }
+
+ bytes = pread(fd, buf, size, 0);
+ if (bytes != size) {
+ (void) printf("short read: %d != %zu\n", bytes, size);
+ retval = 1;
+ goto end;
+ }
+
+ if (buf[idx] != 1) {
+ (void) printf(
+ "bad data from read! got buf[%zu]=%d, expected 1\n",
+ idx, buf[idx]);
+ retval = 1;
+ goto end;
+ }
+
+ (void) printf("good data from read: buf[%zu]=1\n", idx);
+end:
+ if (fd != -1) {
+ (void) close(fd);
+ }
+ if (buf != NULL) {
+ free(buf);
+ }
+
+ return (retval);
+}
diff --git a/tests/sys/cddl/zfs/bin/rename_dir.c b/tests/sys/cddl/zfs/bin/rename_dir.c
new file mode 100644
index 000000000000..58a7cf51a4fd
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/rename_dir.c
@@ -0,0 +1,90 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Assertion:
+ * Create two directory trees in zfs filesystem, and rename
+ * directory across the directory structure. ZFS can handle
+ * the race situation.
+ */
+
+/*
+ * Need to create the following directory structures before
+ * running this program:
+ *
+ * mkdir -p 1/2/3/4/5 a/b/c/d/e
+ */
+
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+
+int
+main()
+{
+ int i = 1;
+ char buf[256];
+ char *msg = "rename() fails to handle race situation\n";
+
+ switch (fork()) {
+ case -1:
+ perror("fork");
+ exit(1);
+ break;
+ case 0:
+ while (i > 0) {
+ int c_count = 0;
+ if (rename("a/b/c", "1/2/3/c") == 0)
+ c_count++;
+ if (rename("1/2/3/c", "a/b/c") == 0)
+ c_count++;
+ if (c_count) {
+ (void) strlcat(buf, "c_count: %d,", 256);
+ (void) strlcat(buf, msg, 256);
+ (void) fprintf(stderr, buf, c_count);
+ }
+ }
+ break;
+ default:
+ while (i > 0) {
+ int p_count = 0;
+ if (rename("1", "a/b/c/d/e/1") == 0)
+ p_count++;
+ if (rename("a/b/c/d/e/1", "1") == 0)
+ p_count++;
+ if (p_count) {
+ (void) strlcat(buf, "p_count: %d,", 256);
+ (void) strlcat(buf, msg, 256);
+ (void) fprintf(stderr, buf, p_count);
+ }
+ }
+ break;
+ }
+ return (0);
+}
diff --git a/tests/sys/cddl/zfs/bin/rm_lnkcnt_zero_file.c b/tests/sys/cddl/zfs/bin/rm_lnkcnt_zero_file.c
new file mode 100644
index 000000000000..0c00e62fdc9a
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/rm_lnkcnt_zero_file.c
@@ -0,0 +1,146 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * --------------------------------------------------------------------
+ * The purpose of this test is to see if the bug reported (#4723351) for
+ * UFS exists when using a ZFS file system.
+ * --------------------------------------------------------------------
+ *
+ */
+#define _REENTRANT 1
+#include <stdio.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static const int TRUE = 1;
+static char *filebase;
+
+static int
+pickidx()
+{
+ return (random() % 1000);
+}
+
+/* ARGSUSED */
+static void *
+mover(void *a)
+{
+ char buf[256];
+ int idx, ret;
+
+ while (TRUE) {
+ idx = pickidx();
+ (void) sprintf(buf, "%s.%03d", filebase, idx);
+ ret = rename(filebase, buf);
+ if (ret < 0 && errno != ENOENT)
+ (void) perror("renaming file");
+ }
+
+ return (NULL);
+}
+
+/* ARGSUSED */
+static void *
+cleaner(void *a)
+{
+ char buf[256];
+ int idx, ret;
+
+ while (TRUE) {
+ idx = pickidx();
+ (void) sprintf(buf, "%s.%03d", filebase, idx);
+ ret = remove(buf);
+ if (ret < 0 && errno != ENOENT)
+ (void) perror("removing file");
+ }
+
+ return (NULL);
+}
+
+static void *
+writer(void *a)
+{
+ int *fd = (int *)a;
+
+ while (TRUE) {
+ (void) close (*fd);
+ *fd = open(filebase, O_APPEND | O_RDWR | O_CREAT, 0644);
+ if (*fd < 0)
+ perror("refreshing file");
+ (void) write(*fd, "test\n", 5);
+ }
+
+ return (NULL);
+}
+
+int
+main(int argc, char **argv)
+{
+ int fd;
+ pthread_t tid;
+
+ if (argc == 1) {
+ (void) printf("Usage: %s <filebase>\n", argv[0]);
+ exit(-1);
+ }
+
+ filebase = argv[1];
+ fd = open(filebase, O_APPEND | O_RDWR | O_CREAT, 0644);
+ if (fd < 0) {
+ perror("creating test file");
+ exit(-1);
+ }
+
+ if (pthread_setconcurrency(4)) { /* 3 threads + main */
+ fprintf(stderr, "failed to set concurrency\n");
+ exit(-1);
+ }
+ (void) pthread_create(&tid, NULL, mover, NULL);
+ (void) pthread_create(&tid, NULL, cleaner, NULL);
+ (void) pthread_create(&tid, NULL, writer, (void *) &fd);
+
+ while (TRUE) {
+ int ret;
+ struct stat st;
+
+ ret = stat(filebase, &st);
+ if (ret == 0 && (st.st_nlink > 2 || st.st_nlink < 1)) {
+ (void) printf("st.st_nlink = %d, exiting\n", \
+ (int)st.st_nlink);
+ exit(0);
+ }
+ (void) sleep(1);
+ }
+
+ return (0);
+}
diff --git a/tests/sys/cddl/zfs/bin/svcs.ksh b/tests/sys/cddl/zfs/bin/svcs.ksh
new file mode 100644
index 000000000000..9ac3e43306ca
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/svcs.ksh
@@ -0,0 +1,4 @@
+#!/usr/local/bin/ksh93
+
+echo dummy
+exit 0
diff --git a/tests/sys/cddl/zfs/bin/swap.ksh b/tests/sys/cddl/zfs/bin/swap.ksh
new file mode 100644
index 000000000000..fa25bcc16239
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/swap.ksh
@@ -0,0 +1,3 @@
+#! /usr/local/bin/ksh93 -p
+
+swapinfo
diff --git a/tests/sys/cddl/zfs/bin/testenv.ksh b/tests/sys/cddl/zfs/bin/testenv.ksh
new file mode 100644
index 000000000000..667ac756fc78
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/testenv.ksh
@@ -0,0 +1,6 @@
+#!/usr/bin/env ksh93
+script=$(realpath $0)
+export STF_BIN=$(dirname ${script})
+export STF_SUITE=$(dirname ${STF_BIN})
+
+env ENV=${STF_SUITE}/include/testenv.kshlib ksh93 -E -l
diff --git a/tests/sys/cddl/zfs/bin/useradd.ksh b/tests/sys/cddl/zfs/bin/useradd.ksh
new file mode 100644
index 000000000000..82f3b66d0218
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/useradd.ksh
@@ -0,0 +1,11 @@
+#! /usr/local/bin/ksh93 -p
+
+a=
+g=
+for i in $*
+do
+ a="$a $g"
+ g=$i
+done
+
+/usr/sbin/pw useradd $g $a
diff --git a/tests/sys/cddl/zfs/bin/userdel.ksh b/tests/sys/cddl/zfs/bin/userdel.ksh
new file mode 100644
index 000000000000..5aa031932288
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/userdel.ksh
@@ -0,0 +1,11 @@
+#! /usr/local/bin/ksh93 -p
+
+a=
+g=
+for i in $*
+do
+ a="$a $g"
+ g=$i
+done
+
+/usr/sbin/pw userdel $g $a
diff --git a/tests/sys/cddl/zfs/bin/usermod.ksh b/tests/sys/cddl/zfs/bin/usermod.ksh
new file mode 100644
index 000000000000..a3140304e1c4
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/usermod.ksh
@@ -0,0 +1,11 @@
+#! /usr/local/bin/ksh93 -p
+
+a=
+g=
+for i in $*
+do
+ a="$a $g"
+ g=$i
+done
+
+/usr/sbin/pw usermod $g $a
diff --git a/tests/sys/cddl/zfs/bin/zfs.ksh b/tests/sys/cddl/zfs/bin/zfs.ksh
new file mode 100644
index 000000000000..df33a22dcbed
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/zfs.ksh
@@ -0,0 +1,39 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+ZFS=/sbin/zfs
+
+set -A saved_options -- "$@"
+
+for wrapper in ${ZFS_WRAPPER} ; do
+ if [[ -x ${STF_SUITE}/bin/zfs_$wrapper ]]; then
+ options=$(${STF_SUITE}/bin/zfs_$wrapper "${saved_options[@]}")
+ set -A saved_options -- $options
+ fi
+done
+
+$ZFS "${saved_options[@]}"
+return $?
diff --git a/tests/sys/cddl/zfs/bin/zfs_crypto.ksh b/tests/sys/cddl/zfs/bin/zfs_crypto.ksh
new file mode 100644
index 000000000000..73117bcdcdf8
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/zfs_crypto.ksh
@@ -0,0 +1,54 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+cmd=$1
+shift
+options="$@"
+
+case $cmd in
+ create)
+ # Get zfs name
+ # eval zfsname=\${$#}
+
+ if [[ $KEYSOURCE_DATASET == "passphrase" ]]; then
+ options="-o encryption=$ENCRYPTION \
+-o keysource=passphrase,file://$PASSPHRASE_FILE $options"
+ elif [[ $KEYSOURCE_DATASET == "raw" ]]; then
+ options="-o encryption=$ENCRYPTION \
+-o keysource=raw,file://$RAW_KEY_FILE $options"
+ elif [[ $KEYSOURCE_DATASET == "hex" ]]; then
+ options="-o encryption=$ENCRYPTION \
+-o keysource=hex,file://$HEX_KEY_FILE $options"
+ elif [[ -n $KEYSOURCE_DATASET ]]; then
+ log_note "Warning: invalid KEYSOURCE_DATASET \c"
+ log_note "value: $KEYSOURCE_DATASET, ignore it"
+ fi
+ ;;
+ *)
+ ;;
+esac
+
+print $cmd $options
diff --git a/tests/sys/cddl/zfs/bin/zfs_version.ksh b/tests/sys/cddl/zfs/bin/zfs_version.ksh
new file mode 100644
index 000000000000..bbd1b013c808
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/zfs_version.ksh
@@ -0,0 +1,63 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+cmd=$1
+shift
+options="$@"
+
+o_version_option="no"
+V_option="no"
+
+while getopts :o:V: c
+do
+ case $c in
+ o)
+ if [[ "$OPTARG" == "version="* ]]; then
+ o_version_option="yes"
+ fi
+ ;;
+ V)
+ V_option="yes"
+ ;;
+ *)
+ ;;
+ esac
+done
+shift $(($OPTIND - 1))
+
+case $cmd in
+ create)
+ if [[ "$ZFS_TEST_VERSION" != "0" ]] &&
+ [[ "$o_version_option" == "no" ]] &&
+ [[ "$V_option" == "no" ]]; then
+ options="-o version=$ZFS_TEST_VERSION $options"
+ fi
+ ;;
+ *)
+ ;;
+esac
+
+print "$cmd $options"
diff --git a/tests/sys/cddl/zfs/bin/zlogin.ksh b/tests/sys/cddl/zfs/bin/zlogin.ksh
new file mode 100644
index 000000000000..9ac3e43306ca
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/zlogin.ksh
@@ -0,0 +1,4 @@
+#!/usr/local/bin/ksh93
+
+echo dummy
+exit 0
diff --git a/tests/sys/cddl/zfs/bin/zoneadm.ksh b/tests/sys/cddl/zfs/bin/zoneadm.ksh
new file mode 100644
index 000000000000..82c9c7677250
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/zoneadm.ksh
@@ -0,0 +1,4 @@
+#!/usr/local/bin/ksh93
+
+echo global
+exit 0
diff --git a/tests/sys/cddl/zfs/bin/zonecfg.ksh b/tests/sys/cddl/zfs/bin/zonecfg.ksh
new file mode 100644
index 000000000000..9ac3e43306ca
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/zonecfg.ksh
@@ -0,0 +1,4 @@
+#!/usr/local/bin/ksh93
+
+echo dummy
+exit 0
diff --git a/tests/sys/cddl/zfs/bin/zpool.ksh b/tests/sys/cddl/zfs/bin/zpool.ksh
new file mode 100644
index 000000000000..63731aeada54
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/zpool.ksh
@@ -0,0 +1,41 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+ZPOOL=/sbin/zpool
+
+set -A saved_options -- "$@"
+
+for wrapper in ${ZPOOL_WRAPPER} ; do
+ if [[ -x ${STF_SUITE}/bin/zpool_$wrapper ]]; then
+ options=$(${STF_SUITE}/bin/zpool_$wrapper "${saved_options[@]}")
+ set -A saved_options -- $options
+ fi
+done
+
+$ZPOOL "${saved_options[@]}"
+return $?
diff --git a/tests/sys/cddl/zfs/bin/zpool_bsd.ksh b/tests/sys/cddl/zfs/bin/zpool_bsd.ksh
new file mode 100644
index 000000000000..24811d7274a1
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/zpool_bsd.ksh
@@ -0,0 +1,40 @@
+#!/usr/local/bin/ksh93 -p
+#
+
+cmd=$1
+
+if [[ -z $cmd ]]; then
+ return 0
+fi
+
+shift
+
+
+typeset option
+case $cmd in
+ create|add|attach|detach|replace|remove|online|offline|clear)
+ for arg in $@; do
+ if [[ $arg == "/dev/"* ]]; then
+ arg=${arg#/dev/}
+ arg="/dev/"$arg
+ fi
+ if [[ $arg == "/dev/"* ]]; then
+ echo $arg | egrep "*s[0-9]$" > /dev/null 2>&1
+ if [ $? -eq 0 ]
+ then
+ n=`echo $arg| wc -c`
+ set -A map a b c d e f g h i j
+ s=`echo $arg | cut -c $((n-1))`
+ arg=${arg%s[0-9]}${map[$s]}
+ fi
+ fi
+
+ option="${option} $arg"
+ done
+ ;;
+ *)
+ option="$@"
+ ;;
+esac
+
+echo $cmd $option
diff --git a/tests/sys/cddl/zfs/bin/zpool_smi.ksh b/tests/sys/cddl/zfs/bin/zpool_smi.ksh
new file mode 100644
index 000000000000..1b9d73eeb143
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/zpool_smi.ksh
@@ -0,0 +1,133 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+function labelvtoc
+{
+ typeset disk=$1
+ if [[ -z $disk ]]; then
+ print "no disk is given."
+ return 1
+ fi
+
+ /usr/sbin/format $disk << _EOF >/dev/null 2>&1
+label
+yes
+_EOF
+
+ labeltype=$(/usr/sbin/prtvtoc -fh /dev/${disk}s2 | \
+ awk '{print $1}' | awk -F= '{print $2}' )
+ if [[ -z $labeltype ]]; then
+ print "${disk} not exist."
+ return 1
+ fi
+
+ if [[ $labeltype == "34" ]]; then
+
+ typeset label_file=$TMPDIR/labelvtoc.${TESTCASE_ID:-$$}
+ typeset arch=$(uname -p)
+
+ if [[ $arch == "i386" ]]; then
+ print "label" > $label_file
+ print "0" >> $label_file
+ print "" >> $label_file
+ print "q" >> $label_file
+ print "q" >> $label_file
+
+ fdisk -B /dev/${disk}p0 >/dev/null 2>&1
+ # wait a while for fdisk finishes
+ /usr/sbin/devfsadm > /dev/null 2>&1
+ elif [[ $arch == "sparc" ]]; then
+ print "label" > $label_file
+ print "0" >> $label_file
+ print "" >> $label_file
+ print "" >> $label_file
+ print "" >> $label_file
+ print "q" >> $label_file
+ else
+ print "unknow arch type : $arch"
+ return 1
+ fi
+
+ format -e -s -d $disk -f $label_file
+ typeset -i ret_val=$?
+ rm -f $label_file
+ #
+ # wait the format to finish
+ #
+ /usr/sbin/devfsadm > /dev/null 2>&1
+ if (( ret_val != 0 )); then
+ print "unable to label $disk as VTOC."
+ return 1
+ fi
+ fi
+
+ return 0
+}
+
+cmd=$1
+
+if [[ -z $cmd ]]; then
+ return 0
+fi
+
+shift
+
+
+typeset option
+case $cmd in
+ create|add|attach|detach|replace|remove|online|offline|clear)
+ for arg in $@; do
+ if [[ $arg == "/dev/"* ]]; then
+ arg=${arg#/dev/}
+ fi
+
+ print $arg | egrep "^c[0-F]+([td][0-F]+)+$" > /dev/null 2>&1
+
+ if [[ $? -eq 0 ]] ; then
+ labelvtoc $arg
+ if [[ $? -eq 0 ]] ; then
+ arg=${arg}s2
+ fi
+ fi
+ option="${option} $arg"
+ done
+ ;;
+ *)
+ option="$@"
+ ;;
+esac
+
+case $cmd in
+ create|add|attach|replace)
+ if [[ $option != *"-f"* ]]; then
+ cmd="${cmd} -f"
+ fi
+ ;;
+ *)
+ ;;
+esac
+
+print $cmd $option
diff --git a/tests/sys/cddl/zfs/bin/zpool_version.ksh b/tests/sys/cddl/zfs/bin/zpool_version.ksh
new file mode 100644
index 000000000000..f8a0283b8a4c
--- /dev/null
+++ b/tests/sys/cddl/zfs/bin/zpool_version.ksh
@@ -0,0 +1,68 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+cmd=$1
+shift
+options="$@"
+
+o_version_option="no"
+O_version_option="no"
+
+while getopts :o:O: c
+do
+ case $c in
+ o)
+ if [[ "$OPTARG" == "version="* ]]; then
+ o_version_option="yes"
+ fi
+ ;;
+ O)
+ if [[ "$OPTARG" == "version="* ]]; then
+ O_version_option="yes"
+ fi
+ ;;
+ *)
+ ;;
+ esac
+done
+shift $(($OPTIND - 1))
+
+case $cmd in
+ create)
+ if [[ "$ZFS_TEST_VERSION" != "0" ]] &&
+ [[ "$O_version_option" == "no" ]]; then
+ options="-O version=$ZFS_TEST_VERSION $options"
+ fi
+ if [[ "$ZPOOL_TEST_VERSION" != "0" ]] &&
+ [[ "$o_version_option" == "no" ]]; then
+ options="-o version=$ZPOOL_TEST_VERSION $options"
+ fi
+ ;;
+ *)
+ ;;
+esac
+
+print "$cmd $options"
diff --git a/tests/sys/cddl/zfs/include/Makefile b/tests/sys/cddl/zfs/include/Makefile
new file mode 100644
index 000000000000..c7712a7d0a88
--- /dev/null
+++ b/tests/sys/cddl/zfs/include/Makefile
@@ -0,0 +1,30 @@
+.include <src.opts.mk>
+
+PACKAGE= tests
+
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/include
+
+STFSUITEDIR=${TESTSBASE}/sys/cddl/zfs
+
+MAN=
+FILESDIR= ${TESTSBASE}/sys/cddl/zfs/include
+${PACKAGE}FILES+= constants.cfg
+${PACKAGE}FILES+= libremote.kshlib
+${PACKAGE}FILES+= libgnop.kshlib
+${PACKAGE}FILES+= logapi.kshlib
+${PACKAGE}FILES+= libtest.kshlib
+${PACKAGE}FILES+= stf.shlib
+${PACKAGE}FILES+= testenv.kshlib
+
+${PACKAGE}FILES+= commands.cfg
+CLEANFILES+= commands.cfg
+commands.cfg: translatecommands.awk commands.txt
+ awk -v stfsuitedir=${STFSUITEDIR} -f ${.ALLSRC} > ${.TARGET}
+
+${PACKAGE}FILES+= default.cfg
+CLEANFILES+= default.cfg
+default.cfg: default.cfg.in
+ sed "s:%%STFSUITEDIR%%:${STFSUITEDIR}:" ${.ALLSRC} > ${.TARGET}
+
+ATF_TESTS_KSH93+= libtest_test
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/include/commands.txt b/tests/sys/cddl/zfs/include/commands.txt
new file mode 100644
index 000000000000..796358fe5227
--- /dev/null
+++ b/tests/sys/cddl/zfs/include/commands.txt
@@ -0,0 +1,189 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#!/usr/local/bin/ksh93 -p
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# All the commands in this file are converted into environment variables
+# with the same name as the command e.g.
+#
+# /bin/df becomes export DF="/bin/df"
+# finally an export CMDS="aggregation of all the environment variables"
+# is created for checking in the execution environment.
+#
+# comments are ignored in this file, as are whitespace lines
+
+# General Solaris Commands
+/usr/bin/awk
+/usr/sbin/arp
+/usr/bin/basename
+/usr/bin/bzcat
+/bin/cat
+/usr/bin/cd
+/usr/bin/chgrp
+/bin/chmod
+/usr/sbin/chown
+/usr/bin/cksum
+/sbin/clri
+/usr/bin/cmp
+/usr/bin/compress
+/usr/bin/uncompress
+#/usr/bin/coreadm
+/bin/cp
+/usr/bin/cpio
+/usr/bin/cut
+/bin/date
+/bin/dd
+#/usr/sbin/devfsadm
+%%STFSUITEDIR%%/bin/df
+/usr/bin/diff
+%%STFSUITEDIR%%/bin/dircmp
+/usr/bin/dirname
+/usr/bin/du
+#%%STFSUITEDIR%%/bin/dumpadm
+/sbin/dumpon
+/bin/echo
+/usr/bin/egrep
+/usr/bin/env
+#%%STFSUITEDIR%%/bin/ff
+/usr/bin/fgrep
+/usr/bin/file
+/usr/bin/find
+#%%STFSUITEDIR%%/bin/fmadm
+#%%STFSUITEDIR%%/bin/fmdump
+#%%STFSUITEDIR%%/bin/format
+/sbin/fsck
+/sbin/fsdb
+/sbin/fsirand
+/usr/bin/fsync
+/usr/sbin/fstyp
+/usr/bin/id
+#/usr/bin/isainfo
+#/usr/sbin/iscsiadm
+#/usr/sbin/iscsitadm
+/usr/bin/getent
+/bin/getfacl
+/usr/bin/getconf
+/sbin/sha1
+/sbin/gpart
+/usr/bin/grep
+/usr/bin/groups
+%%STFSUITEDIR%%/bin/groupadd
+%%STFSUITEDIR%%/bin/groupdel
+%%STFSUITEDIR%%/bin/groupmod
+%%STFSUITEDIR%%/bin/groupshow
+/usr/bin/head
+/bin/hostname
+/bin/kill
+/usr/local/bin/ksh93
+#/usr/sbin/labelit
+#/usr/sbin/lockfs
+#/usr/sbin/lofiadm
+/bin/ls
+/usr/bin/logname
+#/usr/bin/mdb
+/sbin/mdconfig
+#/usr/sbin/metaclear
+#/usr/sbin/metadb
+#/usr/sbin/metainit
+#/usr/sbin/metastat
+/bin/mkdir
+/sbin/mknod
+#/usr/sbin/modinfo
+#/usr/sbin/modunload
+/sbin/mount
+/bin/mv
+#/usr/sbin/ncheck
+/sbin/newfs
+/usr/bin/nawk
+#/usr/bin/pack
+/usr/bin/pagesize
+/bin/pax
+/sbin/ping
+/usr/bin/printf
+#/usr/sbin/prtvtoc
+#/usr/bin/pfexec
+/bin/pgrep
+#/usr/bin/pkginfo
+/bin/pkill
+/bin/ps
+#/usr/sbin/psrinfo
+/usr/sbin/quotaon
+/bin/rcp
+/sbin/reboot
+/bin/rm
+/bin/rmdir
+/usr/bin/rsh
+#/usr/bin/runat
+/usr/bin/sed
+#/usr/sbin/share
+/bin/sleep
+/usr/bin/su
+/usr/bin/sum
+#%%STFSUITEDIR%%/bin/svcs
+#/usr/sbin/svcadm
+#%%STFSUITEDIR%%/bin/swap
+#/sbin/swapadd
+/usr/bin/sort
+/usr/bin/stat
+/usr/bin/strings
+/bin/sync
+/usr/bin/tar
+/usr/bin/tail
+/usr/bin/timeout
+/usr/bin/touch
+/usr/bin/tr
+/usr/bin/true
+/usr/bin/truncate
+/sbin/tunefs
+#/usr/sbin/ufsdump
+#/usr/sbin/ufsrestore
+/usr/bin/umask
+/sbin/umount
+/usr/bin/uname
+/usr/bin/uniq
+#/usr/sbin/unshare
+#/usr/bin/unpack
+%%STFSUITEDIR%%/bin/useradd
+%%STFSUITEDIR%%/bin/userdel
+%%STFSUITEDIR%%/bin/usermod
+/usr/bin/wait
+/usr/bin/wc
+#%%STFSUITEDIR%%/bin/zoneadm
+#%%STFSUITEDIR%%/bin/zonecfg
+#%%STFSUITEDIR%%/bin/zlogin
+#/usr/bin/zonename
+/sbin/swapon
+/sbin/swapoff
+/sbin/swapctl
+/usr/bin/xargs
+/usr/sbin/zfsd
+
+# ZFS Commands
+/usr/sbin/zdb
+/sbin/zfs
+/sbin/zpool
+/usr/bin/zinject
+
+# Test framework commands
+#/opt/SUNWstc-runwattr/bin/runwattr
+%%STFSUITEDIR%%/bin/bsddisks
diff --git a/tests/sys/cddl/zfs/include/constants.cfg b/tests/sys/cddl/zfs/include/constants.cfg
new file mode 100644
index 000000000000..06335cc0945d
--- /dev/null
+++ b/tests/sys/cddl/zfs/include/constants.cfg
@@ -0,0 +1,112 @@
+#!/usr/bin/env ksh93
+# vim: filetype=sh
+
+# Add test-specific binaries to PATH
+export PATH=${STF_SUITE}/bin:${PATH}
+export TMPDIR=${TMPDIR-/tmp}
+
+# Set default value of TMPDIR
+export TMPDIR=${TMPDIR-/tmp}
+
+# Define run length constants
+export RT_LONG="3"
+export RT_MEDIUM="2"
+export RT_SHORT="1"
+
+# Define macro for zone test
+export ZONE_POOL="zonepool"
+export ZONE_CTR="zonectr"
+
+# Test Suite Specific Commands
+export DEVNAME2DEVID="devname2devid"
+export FILE_WRITE="file_write"
+export FILE_CHECK="file_check"
+export LARGEST_FILE="largest_file"
+export MMAPWRITE="mmapwrite"
+export MKFILE="mkfile"
+export READMMAP="readmmap"
+export FILE_TRUNC="file_trunc"
+export CHG_USR_EXEC="chg_usr_exec"
+export MKTREE="mktree"
+export RANDFREE_FILE="randfree_file"
+export DIR_RD_UPDATE="dir_rd_update"
+export RM_LNKCNT_ZERO_FILE="rm_lnkcnt_zero_file"
+export RENAME_DIR="rename_dir"
+
+# ensure we're running in the C locale, since
+# localised messages may result in test failures
+export LC_ALL="C"
+export LANG="C"
+
+#
+# pattern to ignore from 'zpool list'.
+#
+export NO_POOLS="no pools available"
+
+# pattern to ignore from 'zfs list'.
+export NO_DATASETS="no datasets available"
+
+export TEST_BASE_DIR="/"
+
+# Default to compression ON
+export COMPRESSION_PROP=on
+
+# Default to using the checksum
+export CHECKSUM_PROP=on
+
+# some common variables used by test scripts :
+
+export TESTCASE_ID=${TESTCASE_ID:-$$}
+# some test pool names
+export TESTPOOL=testpool.${TESTCASE_ID}
+export TESTPOOL1=testpool1.${TESTCASE_ID}
+export TESTPOOL2=testpool2.${TESTCASE_ID}
+export TESTPOOL3=testpool3.${TESTCASE_ID}
+
+# some test file system names
+export TESTCTR=testctr${TESTCASE_ID}
+export TESTFS=testfs.${TESTCASE_ID}
+export TESTFS1=testfs1.${TESTCASE_ID}
+export TESTFS2=testfs2.${TESTCASE_ID}
+export TESTFS3=testfs3.${TESTCASE_ID}
+
+# some test directory names
+export TESTDIR=${TEST_BASE_DIR%%/}/testdir${TESTCASE_ID}
+export TESTDIR0=${TEST_BASE_DIR%%/}/testdir0${TESTCASE_ID}
+export TESTDIR1=${TEST_BASE_DIR%%/}/testdir1${TESTCASE_ID}
+export TESTDIR2=${TEST_BASE_DIR%%/}/testdir2${TESTCASE_ID}
+
+# Default to limit disks to be checked
+export MAX_FINDDISKSNUM=100
+
+# For iscsi target support
+export ISCSITGTFILE=$TMPDIR/iscsitgt_file
+export ISCSITGT_FMRI=svc:/system/iscsitgt:default
+
+if [ -n "$SVCS" ]; then
+ export AUTO_SNAP=$($SVCS -a | $GREP auto-snapshot | $GREP online | $AWK '{print $3}')
+fi
+
+# zfs upgrade should output the first line as:
+# This system is currently running ZFS filesystem version 2.
+# .
+
+ZFS_VERSION=
+$ZFS upgrade -v > /dev/null 2>&1
+if [ $? -eq 0 ]; then
+ export ZFS_VERSION=$($ZFS upgrade | $HEAD -1 | $AWK '{print $NF}' \
+ | $SED -e 's/\.//g')
+fi
+
+if [ -n "$ZFS_VERSION" ]; then
+ i=1
+ ZFS_ALL_VERSIONS=""
+ while [ "$i" -le "$ZFS_VERSION" ]; do
+ eval 'export ZFS_VERSION_$i="v${i}-fs"'
+ ZFS_ALL_VERSIONS="$ZFS_ALL_VERSIONS $i"
+ i=$(( i + 1 ))
+ done
+ export ZFS_ALL_VERSIONS
+fi
+
+$TRUE
diff --git a/tests/sys/cddl/zfs/include/default.cfg.in b/tests/sys/cddl/zfs/include/default.cfg.in
new file mode 100644
index 000000000000..049d4316bb93
--- /dev/null
+++ b/tests/sys/cddl/zfs/include/default.cfg.in
@@ -0,0 +1,47 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# Read the user config file, if it exists
+#
+
+export STF_SUITE=%%STFSUITEDIR%%
+
+. $STF_SUITE/include/libtest.kshlib
+
+# source the autogenerated commands.cfg file (built from the commands.txt file
+. $STF_SUITE/include/commands.cfg
+
+# Turn ATF variables into environment variables
+export DISKS=`atf_config_get disks ""`
+export KEEP=`atf_config_get keep_pools "" | ${SED} -E 's/ +/|/g'`
+export TESTCASE_ID=$(echo $(atf_get ident) | cksum -o 2 | cut -f 1 -d " ")
+
+
+. $STF_SUITE/include/constants.cfg
+
+# finally, if we're running in a local zone
+# we take some additional actions
+if ! is_global_zone; then
+ reexport_pool
+fi
diff --git a/tests/sys/cddl/zfs/include/libgnop.kshlib b/tests/sys/cddl/zfs/include/libgnop.kshlib
new file mode 100644
index 000000000000..f4f742fe6929
--- /dev/null
+++ b/tests/sys/cddl/zfs/include/libgnop.kshlib
@@ -0,0 +1,103 @@
+# vim: filetype=sh
+#
+# Copyright (c) 2017 Spectra Logic Corporation
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions, and the following disclaimer,
+# without modification.
+# 2. Redistributions in binary form must reproduce at minimum a disclaimer
+# substantially similar to the "NO WARRANTY" disclaimer below
+# ("Disclaimer") and any redistribution must be conditioned upon
+# including a substantially similar Disclaimer requirement for further
+# binary redistribution.
+#
+# NO WARRANTY
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGES.
+#
+#
+
+#
+# Routines that use gnop(8) to simulate devices that can disappear at any time
+#
+
+# Create a gnop device on top of a real device. Don't use the full extent; use
+# a portion in the middle so that any ZFS label present on the real device
+# won't be present on the gnop device and vice versa.
+function create_gnop
+{
+ # Name of disk to use, with or without /dev/
+ typeset disk=$1
+ # Optional physical path to use
+ typeset physpath=$2
+ # size of disk in bytes
+ typeset -li disk_size
+ # disk size, rounded down to multiple of 16384
+ typeset -li disk_size_rounded
+ # size of the nop device in bytes
+ typeset -li nop_size
+ # offset of the beginning of the nop device in bytes
+ typeset -li nop_offset
+ typeset args
+
+ disk_size=`diskinfo $disk | cut -f 3`
+ # Round it down so the nop device will be 4k-aligned
+ disk_size_rounded=$(( ${disk_size} / 16384 * 16384 ))
+ nop_size=$(( ${disk_size_rounded} / 4 ))
+ nop_offset=${nop_size}
+ args="-s ${nop_size} -o ${nop_offset}"
+ if [ -n "$physpath" ]; then
+ args="$args -z $physpath"
+ fi
+
+ gnop create ${args} ${disk}
+}
+
+# Create multiple gnop devices
+function create_gnops
+{
+ typeset disk
+ for disk in $@; do
+ create_gnop "$disk" || return 1
+ done
+ return 0
+}
+
+# Destroy a gnop device.
+function destroy_gnop
+{
+ # Name of the underlying (non-gnop) device
+ typeset disk=$1
+
+ # Use "-f" so we can destroy a gnop with a consumer (like ZFS)
+ gnop destroy -f ${disk}.nop
+
+ # Wait for it to disappear
+ for i in `seq 5`; do
+ gnop status ${disk}.nop >/dev/null 2>/dev/null || break
+ sleep $i
+ done
+}
+
+# Destroy multiple gnop devices. Attempt to destroy them all, ignoring errors
+function destroy_gnops
+{
+ typeset disk
+ for disk in $@; do
+ destroy_gnop "$disk"
+ done
+ return 0
+}
diff --git a/tests/sys/cddl/zfs/include/libremote.kshlib b/tests/sys/cddl/zfs/include/libremote.kshlib
new file mode 100644
index 000000000000..d6c893bd7166
--- /dev/null
+++ b/tests/sys/cddl/zfs/include/libremote.kshlib
@@ -0,0 +1,42 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+# Check if a package is installed
+# $1 package name
+# Return 0 if it is installed; 1 if not installed
+function pkg_isinstalled
+{
+ /usr/bin/pkginfo -q $1
+ return $?
+}
+
+# Get the installation base for a package
+# $1 package name
+# Return
+function pkg_getinstbase
+{
+ /bin/echo $(/usr/bin/pkginfo -r $1 2>/dev/null)
+}
diff --git a/tests/sys/cddl/zfs/include/libtest.kshlib b/tests/sys/cddl/zfs/include/libtest.kshlib
new file mode 100644
index 000000000000..a439f1767410
--- /dev/null
+++ b/tests/sys/cddl/zfs/include/libtest.kshlib
@@ -0,0 +1,3472 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/logapi.kshlib
+
+ZFS=${ZFS:-/sbin/zfs}
+ZPOOL=${ZPOOL:-/sbin/zpool}
+os_name=`uname -s`
+
+# Determine if a test has the necessary requirements to run
+
+function test_requires
+{
+ integer unsupported=0
+ unsupported_list=""
+ until [[ $# -eq 0 ]];do
+ var_name=$1
+ cmd=$(eval echo \$${1})
+ if [[ ! "$cmd" != "" ]] ; then
+ print $var_name is not set
+ unsupported_list="$var_name $unsupported_list"
+ ((unsupported=unsupported+1))
+ fi
+ shift
+ done
+ if [[ unsupported -gt 0 ]] ; then
+ log_unsupported "$unsupported_list commands are unsupported"
+ else
+ log_note "All commands are supported"
+ fi
+}
+
+# Determine whether a dataset is mounted
+#
+# $1 dataset name
+# $2 filesystem type; optional - defaulted to zfs
+#
+# Return 0 if dataset is mounted; 1 if unmounted; 2 on error
+
+function ismounted
+{
+ typeset fstype=$2
+ [[ -z $fstype ]] && fstype=zfs
+ typeset out dir name ret
+
+ case $fstype in
+ zfs)
+ if [[ "$1" == "/"* ]] ; then
+ for out in $($ZFS mount | $AWK '{print $2}') ; do
+ [[ $1 == $out ]] && return 0
+ done
+ else
+ for out in $($ZFS mount | $AWK '{print $1}') ; do
+ [[ $1 == $out ]] && return 0
+ done
+ fi
+ ;;
+ ufs|nfs)
+ # a = device, b = "on", c = mount point", d = flags
+ $MOUNT | $GREP $fstype | while read a b c d
+ do
+ [[ "$1" == "$a" || "$1" == "$c" ]] && return 0
+ done
+ ;;
+ esac
+
+ return 1
+}
+
+# Return 0 if a dataset is mounted; 1 otherwise
+#
+# $1 dataset name
+# $2 filesystem type; optional - defaulted to zfs
+
+function mounted
+{
+ ismounted $1 $2
+ (( $? == 0 )) && return 0
+ return 1
+}
+
+# Return 0 if a dataset is unmounted; 1 otherwise
+#
+# $1 dataset name
+# $2 filesystem type; optional - defaulted to zfs
+
+function unmounted
+{
+ ismounted $1 $2
+ (( $? == 1 )) && return 0
+ return 1
+}
+
+# split line on ","
+#
+# $1 - line to split
+
+function splitline
+{
+ $ECHO $1 | $SED "s/,/ /g"
+}
+
+function default_setup
+{
+ default_setup_noexit "$@"
+
+ log_pass
+}
+
+#
+# Given a list of disks, setup storage pools and datasets.
+#
+function default_setup_noexit
+{
+ typeset disklist=$1
+ typeset container=$2
+ typeset volume=$3
+
+ if is_global_zone; then
+ if poolexists $TESTPOOL ; then
+ destroy_pool $TESTPOOL
+ fi
+ [[ -d /$TESTPOOL ]] && $RM -rf /$TESTPOOL
+ log_must $ZPOOL create -f $TESTPOOL $disklist
+ else
+ reexport_pool
+ fi
+
+ $RM -rf $TESTDIR || log_unresolved Could not remove $TESTDIR
+ $MKDIR -p $TESTDIR || log_unresolved Could not create $TESTDIR
+
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+
+ if [[ -n $container ]]; then
+ $RM -rf $TESTDIR1 || \
+ log_unresolved Could not remove $TESTDIR1
+ $MKDIR -p $TESTDIR1 || \
+ log_unresolved Could not create $TESTDIR1
+
+ log_must $ZFS create $TESTPOOL/$TESTCTR
+ log_must $ZFS set canmount=off $TESTPOOL/$TESTCTR
+ log_must $ZFS create $TESTPOOL/$TESTCTR/$TESTFS1
+ log_must $ZFS set mountpoint=$TESTDIR1 \
+ $TESTPOOL/$TESTCTR/$TESTFS1
+ fi
+
+ if [[ -n $volume ]]; then
+ if is_global_zone ; then
+ log_must $ZFS create -V $VOLSIZE $TESTPOOL/$TESTVOL
+ else
+ log_must $ZFS create $TESTPOOL/$TESTVOL
+ fi
+
+ fi
+}
+
+#
+# Given a list of disks, setup a storage pool, file system and
+# a container.
+#
+function default_container_setup
+{
+ typeset disklist=$1
+
+ default_setup "$disklist" "true"
+}
+
+#
+# Given a list of disks, setup a storage pool,file system
+# and a volume.
+#
+function default_volume_setup
+{
+ typeset disklist=$1
+
+ default_setup "$disklist" "" "true"
+}
+
+#
+# Given a list of disks, setup a storage pool,file system,
+# a container and a volume.
+#
+function default_container_volume_setup
+{
+ typeset disklist=$1
+
+ default_setup "$disklist" "true" "true"
+}
+
+#
+# Create a snapshot on a filesystem or volume. Defaultly create a snapshot on
+# filesystem
+#
+# $1 Existing filesystem or volume name. Default, $TESTFS
+# $2 snapshot name. Default, $TESTSNAP
+#
+function create_snapshot
+{
+ typeset fs_vol=${1:-$TESTFS}
+ typeset snap=${2:-$TESTSNAP}
+
+ [[ -z $fs_vol ]] && log_fail "Filesystem or volume's name is undefined."
+ [[ -z $snap ]] && log_fail "Snapshot's name is undefined."
+
+ if snapexists $fs_vol@$snap; then
+ log_fail "$fs_vol@$snap already exists."
+ fi
+ datasetexists $fs_vol || \
+ log_fail "$fs_vol must exist."
+
+ log_must $ZFS snapshot $fs_vol@$snap
+}
+
+#
+# Create a clone from a snapshot, default clone name is $TESTCLONE.
+#
+# $1 Existing snapshot, $TESTPOOL/$TESTFS@$TESTSNAP is default.
+# $2 Clone name, $TESTPOOL/$TESTCLONE is default.
+#
+function create_clone # snapshot clone
+{
+ typeset snap=${1:-$TESTPOOL/$TESTFS@$TESTSNAP}
+ typeset clone=${2:-$TESTPOOL/$TESTCLONE}
+
+ [[ -z $snap ]] && \
+ log_fail "Snapshot name is undefined."
+ [[ -z $clone ]] && \
+ log_fail "Clone name is undefined."
+
+ log_must $ZFS clone $snap $clone
+}
+
+function default_mirror_setup
+{
+ default_mirror_setup_noexit $1 $2 $3
+
+ log_pass
+}
+
+#
+# Given a pair of disks, set up a storage pool and dataset for the mirror
+# @parameters: $1 the primary side of the mirror
+# $2 the secondary side of the mirror
+# @uses: ZPOOL ZFS TESTPOOL TESTFS
+function default_mirror_setup_noexit
+{
+ readonly func="default_mirror_setup_noexit"
+ typeset primary=$1
+ typeset secondary=$2
+
+ [[ -z $primary ]] && \
+ log_fail "$func: No parameters passed"
+ [[ -z $secondary ]] && \
+ log_fail "$func: No secondary partition passed"
+ [[ -d /$TESTPOOL ]] && $RM -rf /$TESTPOOL
+ log_must $ZPOOL create -f $TESTPOOL mirror $@
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+}
+
+#
+# create a number of mirrors.
+# We create a number($1) of 2 way mirrors using the pairs of disks named
+# on the command line. These mirrors are *not* mounted
+# @parameters: $1 the number of mirrors to create
+# $... the devices to use to create the mirrors on
+# @uses: ZPOOL ZFS TESTPOOL
+function setup_mirrors
+{
+ typeset -i nmirrors=$1
+
+ shift
+ while (( nmirrors > 0 )); do
+ log_must test -n "$1" -a -n "$2"
+ [[ -d /$TESTPOOL$nmirrors ]] && $RM -rf /$TESTPOOL$nmirrors
+ log_must $ZPOOL create -f $TESTPOOL$nmirrors mirror $1 $2
+ shift 2
+ (( nmirrors = nmirrors - 1 ))
+ done
+}
+
+#
+# create a number of raidz pools.
+# We create a number($1) of 2 raidz pools using the pairs of disks named
+# on the command line. These pools are *not* mounted
+# @parameters: $1 the number of pools to create
+# $... the devices to use to create the pools on
+# @uses: ZPOOL ZFS TESTPOOL
+function setup_raidzs
+{
+ typeset -i nraidzs=$1
+
+ shift
+ while (( nraidzs > 0 )); do
+ log_must test -n "$1" -a -n "$2"
+ [[ -d /$TESTPOOL$nraidzs ]] && $RM -rf /$TESTPOOL$nraidzs
+ log_must $ZPOOL create -f $TESTPOOL$nraidzs raidz $1 $2
+ shift 2
+ (( nraidzs = nraidzs - 1 ))
+ done
+}
+
+#
+# Destroy the configured testpool mirrors.
+# the mirrors are of the form ${TESTPOOL}{number}
+# @uses: ZPOOL ZFS TESTPOOL
+function destroy_mirrors
+{
+ default_cleanup_noexit
+
+ log_pass
+}
+
+#
+# Given a minimum of two disks, set up a storage pool and dataset for the raid-z
+# $1 the list of disks
+#
+function default_raidz_setup
+{
+ typeset disklist="$*"
+ set -A disks $disklist
+
+ if [[ ${#disks[*]} -lt 2 ]]; then
+ log_fail "A raid-z requires a minimum of two disks."
+ fi
+
+ [[ -d /$TESTPOOL ]] && $RM -rf /$TESTPOOL
+ log_must $ZPOOL create -f $TESTPOOL raidz $1 $2 $3
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+
+ log_pass
+}
+
+#
+# Common function used to cleanup storage pools and datasets.
+#
+# Invoked at the start of the test suite to ensure the system
+# is in a known state, and also at the end of each set of
+# sub-tests to ensure errors from one set of tests doesn't
+# impact the execution of the next set.
+
+function default_cleanup
+{
+ default_cleanup_noexit
+
+ log_pass
+}
+
+function all_pools
+{
+ cmd="$ZPOOL list -H -o name | $GREP 'testpool'"
+ eval $cmd
+}
+
+#
+# Returns 0 if the system contains any pools that must not be modified by the
+# ZFS tests.
+#
+function other_pools_exist
+{
+ typeset pool_count=`$ZPOOL list -H | $GREP -v '^testpool' | $WC -l`
+ [ "$pool_count" -ne 0 ]
+}
+
+function default_cleanup_noexit
+{
+ typeset exclude=""
+ typeset pool=""
+ #
+ # Destroying the pool will also destroy any
+ # filesystems it contains.
+ #
+ if is_global_zone; then
+ # Here, we loop through the pools we're allowed to
+ # destroy, only destroying them if it's safe to do
+ # so.
+ for pool in $(all_pools); do
+ if safe_to_destroy_pool $pool; then
+ destroy_pool $pool
+ fi
+ done
+ else
+ typeset fs=""
+ for fs in $($ZFS list -H -o name \
+ | $GREP "^$ZONE_POOL/$ZONE_CTR[01234]/"); do
+ datasetexists $fs && \
+ log_must $ZFS destroy -Rf $fs
+ done
+
+ # Need cleanup here to avoid garbage dir left.
+ for fs in $($ZFS list -H -o name \
+ ); do
+ [[ $fs == /$ZONE_POOL ]] && continue
+ [[ -d $fs ]] && log_must $RM -rf $fs/*
+ done
+
+ #
+ # Reset the $ZONE_POOL/$ZONE_CTR[01234] file systems property to
+ # the default value
+ #
+ for fs in $($ZFS list -H -o name \
+ ); do
+ if [[ $fs == $ZONE_POOL/$ZONE_CTR[01234] ]]; then
+ log_must $ZFS set reservation=none $fs
+ log_must $ZFS set recordsize=128K $fs
+ log_must $ZFS set mountpoint=/$fs $fs
+ typeset enc=""
+ enc=$(get_prop encryption $fs)
+ if [[ $? -ne 0 ]] || [[ -z "$enc" ]] || \
+ [[ "$enc" == "off" ]]; then
+ log_must $ZFS set checksum=on $fs
+ fi
+ log_must $ZFS set compression=off $fs
+ log_must $ZFS set atime=on $fs
+ log_must $ZFS set devices=off $fs
+ log_must $ZFS set exec=on $fs
+ log_must $ZFS set setuid=on $fs
+ log_must $ZFS set readonly=off $fs
+ log_must $ZFS set snapdir=hidden $fs
+ log_must $ZFS set aclmode=groupmask $fs
+ log_must $ZFS set aclinherit=secure $fs
+ fi
+ done
+ fi
+
+ [[ -d $TESTDIR ]] && \
+ log_must $RM -rf $TESTDIR
+}
+
+
+#
+# Common function used to cleanup storage pools, file systems
+# and containers.
+#
+function default_container_cleanup
+{
+ if ! is_global_zone; then
+ reexport_pool
+ fi
+
+ ismounted $TESTPOOL/$TESTCTR/$TESTFS1
+ [[ $? -eq 0 ]] && \
+ log_must $ZFS unmount $TESTPOOL/$TESTCTR/$TESTFS1
+
+ datasetexists $TESTPOOL/$TESTCTR/$TESTFS1 && \
+ log_must $ZFS destroy -R $TESTPOOL/$TESTCTR/$TESTFS1
+
+ datasetexists $TESTPOOL/$TESTCTR && \
+ log_must $ZFS destroy -Rf $TESTPOOL/$TESTCTR
+
+ [[ -e $TESTDIR1 ]] && \
+ log_must $RM -rf $TESTDIR1 > /dev/null 2>&1
+
+ default_cleanup
+}
+
+#
+# Common function used to cleanup snapshot of file system or volume. Default to
+# delete the file system's snapshot
+#
+# $1 snapshot name
+#
+function destroy_snapshot
+{
+ typeset snap=${1:-$TESTPOOL/$TESTFS@$TESTSNAP}
+
+ if ! snapexists $snap; then
+ log_fail "'$snap' does not existed."
+ fi
+
+ #
+ # For the sake of the value which come from 'get_prop' is not equal
+ # to the really mountpoint when the snapshot is unmounted. So, firstly
+ # check and make sure this snapshot's been mounted in current system.
+ #
+ typeset mtpt=""
+ if ismounted $snap; then
+ mtpt=$(get_prop mountpoint $snap)
+ (( $? != 0 )) && \
+ log_fail "get_prop mountpoint $snap failed."
+ fi
+
+ log_must $ZFS destroy $snap
+ [[ $mtpt != "" && -d $mtpt ]] && \
+ log_must $RM -rf $mtpt
+}
+
+#
+# Common function used to cleanup clone.
+#
+# $1 clone name
+#
+function destroy_clone
+{
+ typeset clone=${1:-$TESTPOOL/$TESTCLONE}
+
+ if ! datasetexists $clone; then
+ log_fail "'$clone' does not existed."
+ fi
+
+ # With the same reason in destroy_snapshot
+ typeset mtpt=""
+ if ismounted $clone; then
+ mtpt=$(get_prop mountpoint $clone)
+ (( $? != 0 )) && \
+ log_fail "get_prop mountpoint $clone failed."
+ fi
+
+ log_must $ZFS destroy $clone
+ [[ $mtpt != "" && -d $mtpt ]] && \
+ log_must $RM -rf $mtpt
+}
+
+# Return 0 if a snapshot exists; $? otherwise
+#
+# $1 - snapshot name
+
+function snapexists
+{
+ $ZFS list -H -t snapshot "$1" > /dev/null 2>&1
+ return $?
+}
+
+#
+# Set a property to a certain value on a dataset.
+# Sets a property of the dataset to the value as passed in.
+# @param:
+# $1 dataset who's property is being set
+# $2 property to set
+# $3 value to set property to
+# @return:
+# 0 if the property could be set.
+# non-zero otherwise.
+# @use: ZFS
+#
+function dataset_setprop
+{
+ typeset fn=dataset_setprop
+
+ if (( $# < 3 )); then
+ log_note "$fn: Insufficient parameters (need 3, had $#)"
+ return 1
+ fi
+ typeset output=
+ output=$($ZFS set $2=$3 $1 2>&1)
+ typeset rv=$?
+ if (( rv != 0 )); then
+ log_note "Setting property on $1 failed."
+ log_note "property $2=$3"
+ log_note "Return Code: $rv"
+ log_note "Output: $output"
+ return $rv
+ fi
+ return 0
+}
+
+#
+# Assign suite defined dataset properties.
+# This function is used to apply the suite's defined default set of
+# properties to a dataset.
+# @parameters: $1 dataset to use
+# @uses: ZFS COMPRESSION_PROP CHECKSUM_PROP
+# @returns:
+# 0 if the dataset has been altered.
+# 1 if no pool name was passed in.
+# 2 if the dataset could not be found.
+# 3 if the dataset could not have it's properties set.
+#
+function dataset_set_defaultproperties
+{
+ typeset dataset="$1"
+
+ [[ -z $dataset ]] && return 1
+
+ typeset confset=
+ typeset -i found=0
+ for confset in $($ZFS list); do
+ if [[ $dataset = $confset ]]; then
+ found=1
+ break
+ fi
+ done
+ [[ $found -eq 0 ]] && return 2
+ if [[ -n $COMPRESSION_PROP ]]; then
+ dataset_setprop $dataset compression $COMPRESSION_PROP || \
+ return 3
+ log_note "Compression set to '$COMPRESSION_PROP' on $dataset"
+ fi
+ if [[ -n $CHECKSUM_PROP && $WRAPPER != *"crypto"* ]]; then
+ dataset_setprop $dataset checksum $CHECKSUM_PROP || \
+ return 3
+ log_note "Checksum set to '$CHECKSUM_PROP' on $dataset"
+ fi
+ return 0
+}
+
+#
+# Check a numeric assertion
+# @parameter: $@ the assertion to check
+# @output: big loud notice if assertion failed
+# @use: log_fail
+#
+function assert
+{
+ (( $@ )) || log_fail $@
+}
+
+function wipe_partition_table #<whole_disk_name> [<whole_disk_name> ...]
+{
+ while [[ -n $* ]]; do
+ typeset diskname=$1
+ [ ! -e $diskname ] && log_fail "ERROR: $diskname doesn't exist"
+ if gpart list ${diskname#/dev/} >/dev/null 2>&1; then
+ wait_for 5 1 $GPART destroy -F $diskname
+ else
+ log_note "No GPT partitions detected on $diskname"
+ fi
+ log_must $GPART create -s gpt $diskname
+ shift
+ done
+}
+
+#
+# Given a slice, size and disk, this function
+# formats the slice to the specified size.
+# Size should be specified with units as per
+# the `format` command requirements eg. 100mb 3gb
+#
+function set_partition #<slice_num> <slice_start> <size_plus_units> <whole_disk_name>
+{
+ typeset -i slicenum=$1
+ typeset start=$2
+ typeset size=$3
+ typeset disk=$4
+ set -A devmap a b c d e f g h
+ [[ -z $slicenum || -z $size || -z $disk ]] && \
+ log_fail "The slice, size or disk name is unspecified."
+
+ size=`$ECHO $size| sed s/mb/M/`
+ size=`$ECHO $size| sed s/m/M/`
+ size=`$ECHO $size| sed s/gb/G/`
+ size=`$ECHO $size| sed s/g/G/`
+ [[ -n $start ]] && start="-b $start"
+ log_must $GPART add -t efi $start -s $size -i $slicenum $disk
+ return 0
+}
+
+function get_disk_size #<disk>
+{
+ typeset disk=$1
+ diskinfo $disk | awk '{print $3}'
+}
+
+function get_available_disk_size #<disk>
+{
+ typeset disk=$1
+ raw_size=`get_disk_size $disk`
+ (( available_size = raw_size * 95 / 100 ))
+ echo $available_size
+}
+
+#
+# Get the end cyl of the given slice
+# #TODO: fix this to be GPT-compatible if we want to use the SMI WRAPPER. This
+# function is not necessary on FreeBSD
+#
+function get_endslice #<disk> <slice>
+{
+ log_fail "get_endslice has not been updated for GPT partitions"
+}
+
+#
+# Get the first LBA that is beyond the end of the given partition
+function get_partition_end #<disk> <partition_index>
+{
+ typeset disk=$1
+ typeset partition_index=$2
+ export partition_index
+ $GPART show $disk | $AWK \
+ '/^[ \t]/ && $3 ~ ENVIRON["partition_index"] {print $1 + $2}'
+}
+
+
+#
+# Given a size,disk and total number of partitions, this function formats the
+# disk partitions from 0 to the total partition number with the same specified
+# size.
+#
+function partition_disk #<part_size> <whole_disk_name> <total_parts>
+{
+ typeset -i i=1
+ typeset part_size=$1
+ typeset disk_name=$2
+ typeset total_parts=$3
+ typeset cyl
+
+ wipe_partition_table $disk_name
+ while (( i <= $total_parts )); do
+ set_partition $i "" $part_size $disk_name
+ (( i = i+1 ))
+ done
+}
+
+function size_of_file # fname
+{
+ typeset fname=$1
+ sz=`stat -f '%z' $fname`
+ [[ -z "$sz" ]] && log_fail "stat($fname) failed"
+ $ECHO $sz
+ return 0
+}
+
+#
+# This function continues to write to a filenum number of files into dirnum
+# number of directories until either $FILE_WRITE returns an error or the
+# maximum number of files per directory have been written.
+#
+# Usage:
+# fill_fs [destdir] [dirnum] [filenum] [bytes] [num_writes] [data]
+#
+# Return value: 0 on success
+# non 0 on error
+#
+# Where :
+# destdir: is the directory where everything is to be created under
+# dirnum: the maximum number of subdirectories to use, -1 no limit
+# filenum: the maximum number of files per subdirectory
+# blocksz: number of bytes per block
+# num_writes: number of blocks to write
+# data: the data that will be written
+#
+# E.g.
+# file_fs /testdir 20 25 1024 256 0
+#
+# Note: blocksz * num_writes equals the size of the testfile
+#
+function fill_fs # destdir dirnum filenum blocksz num_writes data
+{
+ typeset destdir=${1:-$TESTDIR}
+ typeset -i dirnum=${2:-50}
+ typeset -i filenum=${3:-50}
+ typeset -i blocksz=${4:-8192}
+ typeset -i num_writes=${5:-10240}
+ typeset -i data=${6:-0}
+
+ typeset -i retval=0
+ typeset -i dn=0 # current dir number
+ typeset -i fn=0 # current file number
+ while (( retval == 0 )); do
+ (( dirnum >= 0 && dn >= dirnum )) && break
+ typeset curdir=$destdir/$dn
+ log_must $MKDIR -p $curdir
+ for (( fn = 0; $fn < $filenum && $retval == 0; fn++ )); do
+ log_cmd $FILE_WRITE -o create -f $curdir/$TESTFILE.$fn \
+ -b $blocksz -c $num_writes -d $data
+ retval=$?
+ done
+ (( dn = dn + 1 ))
+ done
+ return $retval
+}
+
+#
+# Simple function to get the specified property. If unable to
+# get the property then exits.
+#
+# Note property is in 'parsable' format (-p)
+#
+function get_prop # property dataset
+{
+ typeset prop_val
+ typeset prop=$1
+ typeset dataset=$2
+
+ prop_val=$($ZFS get -pH -o value $prop $dataset 2>/dev/null)
+ if [[ $? -ne 0 ]]; then
+ log_note "Unable to get $prop property for dataset $dataset"
+ return 1
+ fi
+
+ $ECHO $prop_val
+ return 0
+}
+
+#
+# Simple function to return the lesser of two values.
+#
+function min
+{
+ typeset first_arg=$1
+ typeset second_arg=$2
+
+ if (( first_arg < second_arg )); then
+ $ECHO $first_arg
+ else
+ $ECHO $second_arg
+ fi
+ return 0
+}
+
+#
+# Simple function to get the specified property of pool. If unable to
+# get the property then exits.
+#
+function get_pool_prop # property pool
+{
+ typeset prop_val
+ typeset prop=$1
+ typeset pool=$2
+
+ if poolexists $pool ; then
+ prop_val=$($ZPOOL get $prop $pool 2>/dev/null | $TAIL -1 | \
+ $AWK '{print $3}')
+ if [[ $? -ne 0 ]]; then
+ log_note "Unable to get $prop property for pool " \
+ "$pool"
+ return 1
+ fi
+ else
+ log_note "Pool $pool not exists."
+ return 1
+ fi
+
+ $ECHO $prop_val
+ return 0
+}
+
+# Return 0 if a pool exists; $? otherwise
+#
+# $1 - pool name
+
+function poolexists
+{
+ typeset pool=$1
+
+ if [[ -z $pool ]]; then
+ log_note "No pool name given."
+ return 1
+ fi
+
+ $ZPOOL list -H "$pool" > /dev/null 2>&1
+ return $?
+}
+
+# Return 0 if all the specified datasets exist; $? otherwise
+#
+# $1-n dataset name
+function datasetexists
+{
+ if (( $# == 0 )); then
+ log_note "No dataset name given."
+ return 1
+ fi
+
+ while (( $# > 0 )); do
+ $ZFS list -H -t filesystem,snapshot,volume $1 > /dev/null 2>&1 || \
+ return $?
+ shift
+ done
+
+ return 0
+}
+
+# return 0 if none of the specified datasets exists, otherwise return 1.
+#
+# $1-n dataset name
+function datasetnonexists
+{
+ if (( $# == 0 )); then
+ log_note "No dataset name given."
+ return 1
+ fi
+
+ while (( $# > 0 )); do
+ $ZFS list -H -t filesystem,snapshot,volume $1 > /dev/null 2>&1 && \
+ return 1
+ shift
+ done
+
+ return 0
+}
+
+#
+# Given a mountpoint, or a dataset name, determine if it is shared.
+#
+# Returns 0 if shared, 1 otherwise.
+#
+function is_shared
+{
+ typeset fs=$1
+ typeset mtpt
+
+ if [[ $fs != "/"* ]] ; then
+ if datasetnonexists "$fs" ; then
+ return 1
+ else
+ mtpt=$(get_prop mountpoint "$fs")
+ case $mtpt in
+ none|legacy|-) return 1
+ ;;
+ *) fs=$mtpt
+ ;;
+ esac
+ fi
+ fi
+
+ for mtpt in `$SHARE | $AWK '{print $2}'` ; do
+ if [[ $mtpt == $fs ]] ; then
+ return 0
+ fi
+ done
+
+ typeset stat=$($SVCS -H -o STA nfs/server:default)
+ if [[ $stat != "ON" ]]; then
+ log_note "Current nfs/server status: $stat"
+ fi
+
+ return 1
+}
+
+#
+# Given a mountpoint, determine if it is not shared.
+#
+# Returns 0 if not shared, 1 otherwise.
+#
+function not_shared
+{
+ typeset fs=$1
+
+ is_shared $fs
+ if (( $? == 0)); then
+ return 1
+ fi
+
+ return 0
+}
+
+#
+# Helper function to unshare a mountpoint.
+#
+function unshare_fs #fs
+{
+ typeset fs=$1
+
+ is_shared $fs
+ if (( $? == 0 )); then
+ log_must $ZFS unshare $fs
+ fi
+
+ return 0
+}
+
+#
+# Check NFS server status and trigger it online.
+#
+function setup_nfs_server
+{
+ # Cannot share directory in non-global zone.
+ #
+ if ! is_global_zone; then
+ log_note "Cannot trigger NFS server by sharing in LZ."
+ return
+ fi
+
+ typeset nfs_fmri="svc:/network/nfs/server:default"
+ if [[ $($SVCS -Ho STA $nfs_fmri) != "ON" ]]; then
+ #
+ # Only really sharing operation can enable NFS server
+ # to online permanently.
+ #
+ typeset dummy=$TMPDIR/dummy
+
+ if [[ -d $dummy ]]; then
+ log_must $RM -rf $dummy
+ fi
+
+ log_must $MKDIR $dummy
+ log_must $SHARE $dummy
+
+ #
+ # Waiting for fmri's status to be the final status.
+ # Otherwise, in transition, an asterisk (*) is appended for
+ # instances, unshare will reverse status to 'DIS' again.
+ #
+ # Waiting for 1's at least.
+ #
+ log_must $SLEEP 1
+ timeout=10
+ while [[ timeout -ne 0 && $($SVCS -Ho STA $nfs_fmri) == *'*' ]]
+ do
+ log_must $SLEEP 1
+
+ (( timeout -= 1 ))
+ done
+
+ log_must $UNSHARE $dummy
+ log_must $RM -rf $dummy
+ fi
+
+ log_note "Current NFS status: '$($SVCS -Ho STA,FMRI $nfs_fmri)'"
+}
+
+#
+# To verify whether calling process is in global zone
+#
+# Return 0 if in global zone, 1 in non-global zone
+#
+function is_global_zone
+{
+ typeset cur_zone=$($ZONENAME 2>/dev/null)
+
+ # Zones are not supported on FreeBSD.
+ if [[ $os_name == "FreeBSD" ]]; then
+ return 0
+ fi
+
+ if [[ $cur_zone != "global" ]]; then
+ return 1
+ fi
+ return 0
+}
+
+#
+# Verify whether test is permit to run from
+# global zone, local zone, or both
+#
+# $1 zone limit, could be "global", "local", or "both"(no limit)
+#
+# Return 0 if permit, otherwise exit with log_unsupported
+#
+function verify_runnable # zone limit
+{
+ typeset limit=$1
+
+ [[ -z $limit ]] && return 0
+
+ if is_global_zone ; then
+ case $limit in
+ global|both)
+ break
+ ;;
+ local) log_unsupported "Test is unable to run from \
+ global zone."
+ break
+ ;;
+ *) log_note "Warning: unknown limit $limit - use both."
+ ;;
+ esac
+ else
+ case $limit in
+ local|both)
+ break
+ ;;
+ global) log_unsupported "Test is unable to run from \
+ local zone."
+ break
+ ;;
+ *) log_note "Warning: unknown limit $limit - use both."
+ ;;
+ esac
+
+ reexport_pool
+ fi
+
+ return 0
+}
+
+# Return 0 if create successfully or the pool exists; $? otherwise
+# Note: In local zones, this function should return 0 silently.
+#
+# $1 - pool name
+# $2-n - [keyword] devs_list
+
+function create_pool #pool devs_list
+{
+ typeset pool=${1%%/*}
+
+ shift
+
+ if [[ -z $pool ]]; then
+ log_note "Missing pool name."
+ return 1
+ fi
+
+ if poolexists $pool ; then
+ destroy_pool $pool
+ fi
+
+ if is_global_zone ; then
+ [[ -d /$pool ]] && $RM -rf /$pool
+ log_must $ZPOOL create -f $pool $@
+ fi
+
+ return 0
+}
+
+# Return 0 if destroy successfully or the pool exists; $? otherwise
+# Note: In local zones, this function should return 0 silently.
+#
+# $1 - pool name
+# Destroy pool with the given parameters.
+
+function destroy_pool #pool
+{
+ typeset pool=${1%%/*}
+ typeset mtpt
+
+ if [[ -z $pool ]]; then
+ log_note "No pool name given."
+ return 1
+ fi
+
+ if is_global_zone ; then
+ if poolexists "$pool" ; then
+ mtpt=$(get_prop mountpoint "$pool")
+ log_must $ZPOOL destroy -f $pool
+
+ [[ -d $mtpt ]] && \
+ log_must $RM -rf $mtpt
+ else
+ log_note "Pool $pool does not exist, skipping destroy."
+ return 1
+ fi
+ fi
+
+ return 0
+}
+
+#
+# Create file vdevs.
+# By default this generates sparse vdevs 10GB in size, for performance.
+#
+function create_vdevs # vdevs
+{
+ typeset vdsize=10G
+
+ [ -n "$VDEV_SIZE" ] && vdsize=$VDEV_SIZE
+ rm -f $@ || return 1
+ truncate -s $vdsize $@
+}
+
+#
+# Firstly, create a pool with 5 datasets. Then, create a single zone and
+# export the 5 datasets to it. In addition, we also add a ZFS filesystem
+# and a zvol device to the zone.
+#
+# $1 zone name
+# $2 zone root directory prefix
+# $3 zone ip
+#
+function zfs_zones_setup #zone_name zone_root zone_ip
+{
+ typeset zone_name=${1:-$(hostname)-z}
+ typeset zone_root=${2:-"/zone_root"}
+ typeset zone_ip=${3:-"10.1.1.10"}
+ typeset prefix_ctr=$ZONE_CTR
+ typeset pool_name=$ZONE_POOL
+ typeset -i cntctr=5
+ typeset -i i=0
+
+ # Create pool and 5 container within it
+ #
+ [[ -d /$pool_name ]] && $RM -rf /$pool_name
+ log_must $ZPOOL create -f $pool_name $DISKS
+ while (( i < cntctr )); do
+ log_must $ZFS create $pool_name/$prefix_ctr$i
+ (( i += 1 ))
+ done
+
+ # create a zvol
+ log_must $ZFS create -V 1g $pool_name/zone_zvol
+
+ #
+ # If current system support slog, add slog device for pool
+ #
+ typeset sdevs="$TMPDIR/sdev1 $TMPDIR/sdev2"
+ log_must create_vdevs $sdevs
+ log_must $ZPOOL add $pool_name log mirror $sdevs
+
+ # this isn't supported just yet.
+ # Create a filesystem. In order to add this to
+ # the zone, it must have it's mountpoint set to 'legacy'
+ # log_must $ZFS create $pool_name/zfs_filesystem
+ # log_must $ZFS set mountpoint=legacy $pool_name/zfs_filesystem
+
+ [[ -d $zone_root ]] && \
+ log_must $RM -rf $zone_root/$zone_name
+ [[ ! -d $zone_root ]] && \
+ log_must $MKDIR -p -m 0700 $zone_root/$zone_name
+
+ # Create zone configure file and configure the zone
+ #
+ typeset zone_conf=$TMPDIR/zone_conf.${TESTCASE_ID}
+ $ECHO "create" > $zone_conf
+ $ECHO "set zonepath=$zone_root/$zone_name" >> $zone_conf
+ $ECHO "set autoboot=true" >> $zone_conf
+ i=0
+ while (( i < cntctr )); do
+ $ECHO "add dataset" >> $zone_conf
+ $ECHO "set name=$pool_name/$prefix_ctr$i" >> \
+ $zone_conf
+ $ECHO "end" >> $zone_conf
+ (( i += 1 ))
+ done
+
+ # add our zvol to the zone
+ $ECHO "add device" >> $zone_conf
+ $ECHO "set match=/dev/zvol/$pool_name/zone_zvol" >> $zone_conf
+ $ECHO "end" >> $zone_conf
+
+ # add a corresponding zvol to the zone
+ $ECHO "add device" >> $zone_conf
+ $ECHO "set match=/dev/zvol/$pool_name/zone_zvol" >> $zone_conf
+ $ECHO "end" >> $zone_conf
+
+ # once it's supported, we'll add our filesystem to the zone
+ # $ECHO "add fs" >> $zone_conf
+ # $ECHO "set type=zfs" >> $zone_conf
+ # $ECHO "set special=$pool_name/zfs_filesystem" >> $zone_conf
+ # $ECHO "set dir=/export/zfs_filesystem" >> $zone_conf
+ # $ECHO "end" >> $zone_conf
+
+ $ECHO "verify" >> $zone_conf
+ $ECHO "commit" >> $zone_conf
+ log_must $ZONECFG -z $zone_name -f $zone_conf
+ log_must $RM -f $zone_conf
+
+ # Install the zone
+ $ZONEADM -z $zone_name install
+ if (( $? == 0 )); then
+ log_note "SUCCESS: $ZONEADM -z $zone_name install"
+ else
+ log_fail "FAIL: $ZONEADM -z $zone_name install"
+ fi
+
+ # Install sysidcfg file
+ #
+ typeset sysidcfg=$zone_root/$zone_name/root/etc/sysidcfg
+ $ECHO "system_locale=C" > $sysidcfg
+ $ECHO "terminal=dtterm" >> $sysidcfg
+ $ECHO "network_interface=primary {" >> $sysidcfg
+ $ECHO "hostname=$zone_name" >> $sysidcfg
+ $ECHO "}" >> $sysidcfg
+ $ECHO "name_service=NONE" >> $sysidcfg
+ $ECHO "root_password=mo791xfZ/SFiw" >> $sysidcfg
+ $ECHO "security_policy=NONE" >> $sysidcfg
+ $ECHO "timezone=US/Eastern" >> $sysidcfg
+
+ # Boot this zone
+ log_must $ZONEADM -z $zone_name boot
+}
+
+#
+# Reexport TESTPOOL & TESTPOOL(1-4)
+#
+function reexport_pool
+{
+ typeset -i cntctr=5
+ typeset -i i=0
+
+ while (( i < cntctr )); do
+ if (( i == 0 )); then
+ TESTPOOL=$ZONE_POOL/$ZONE_CTR$i
+ if ! ismounted $TESTPOOL; then
+ log_must $ZFS mount $TESTPOOL
+ fi
+ else
+ eval TESTPOOL$i=$ZONE_POOL/$ZONE_CTR$i
+ if eval ! ismounted \$TESTPOOL$i; then
+ log_must eval $ZFS mount \$TESTPOOL$i
+ fi
+ fi
+ (( i += 1 ))
+ done
+}
+
+#
+# Wait for something to return true, checked by the caller.
+#
+function wait_for_checked # timeout dt <method> [args...]
+{
+ typeset timeout=$1
+ typeset dt=$2
+ shift; shift
+ typeset -i start=$(date '+%s')
+ typeset -i endtime
+
+ log_note "Waiting $timeout seconds (checked every $dt seconds) for: $*"
+ ((endtime = start + timeout))
+ while :; do
+ $*
+ [ $? -eq 0 ] && return
+ curtime=$(date '+%s')
+ [ $curtime -gt $endtime ] && return 1
+ sleep $dt
+ done
+ return 0
+}
+
+#
+# Wait for something to return true.
+#
+function wait_for # timeout dt <method> [args...]
+{
+ typeset timeout=$1
+ typeset dt=$2
+ shift; shift
+
+ wait_for_checked $timeout $dt $* || \
+ log_fail "ERROR: Timed out waiting for: $*"
+}
+
+#
+# Verify a given disk is online or offline
+#
+# Return 0 is pool/disk matches expected state, 1 otherwise
+# stateexpr is a regex like ONLINE or REMOVED|UNAVAIL
+#
+function check_state # pool disk stateexpr
+{
+ typeset pool=$1
+ typeset disk=${2#/dev/}
+ disk=${disk#/dev/}
+ disk=${disk#/dev/}
+ typeset stateexpr=$3
+
+ $ZPOOL status -v $pool | grep "$disk" \
+ | egrep -i "$stateexpr" > /dev/null 2>&1
+
+ return $?
+}
+
+#
+# Wait for a given disk to leave a state
+#
+function wait_for_state_exit
+{
+ typeset pool=$1
+ typeset disk=$2
+ typeset state=$3
+
+ while check_state "$pool" "$disk" "$state"; do
+ $SLEEP 1
+ done
+}
+
+#
+# Wait for a given disk to enter a state
+#
+function wait_for_state_enter
+{
+ typeset -i timeout=$1
+ typeset pool=$2
+ typeset disk=$3
+ typeset state=$4
+
+ log_note "Waiting up to $timeout seconds for $disk to become $state ..."
+ for ((; $timeout > 0; timeout=$timeout-1)); do
+ check_state $pool "$disk" "$state"
+ [ $? -eq 0 ] && return
+ $SLEEP 1
+ done
+ log_must $ZPOOL status $pool
+ log_fail "ERROR: Disk $disk not marked as $state in $pool"
+}
+
+#
+# Get the mountpoint of snapshot
+# as its mountpoint
+#
+function snapshot_mountpoint
+{
+ typeset dataset=${1:-$TESTPOOL/$TESTFS@$TESTSNAP}
+
+ if [[ $dataset != *@* ]]; then
+ log_fail "Error name of snapshot '$dataset'."
+ fi
+
+ typeset fs=${dataset%@*}
+ typeset snap=${dataset#*@}
+
+ if [[ -z $fs || -z $snap ]]; then
+ log_fail "Error name of snapshot '$dataset'."
+ fi
+
+ $ECHO $(get_prop mountpoint $fs)/$(get_snapdir_name)/$snap
+}
+
+function pool_maps_intact # pool
+{
+ typeset pool="$1"
+
+ if ! $ZDB -bcv $pool; then
+ return 1
+ fi
+ return 0
+}
+
+function filesys_has_zil # filesystem
+{
+ typeset filesys="$1"
+
+ if ! $ZDB -ivv $filesys | $GREP "ZIL header"; then
+ return 1
+ fi
+ return 0
+}
+
+#
+# Given a pool and file system, this function will verify the file system
+# using the zdb internal tool. Note that the pool is exported and imported
+# to ensure it has consistent state.
+#
+function verify_filesys # pool filesystem dir
+{
+ typeset pool="$1"
+ typeset filesys="$2"
+ typeset zdbout="$TMPDIR/zdbout.${TESTCASE_ID}"
+
+ shift
+ shift
+ typeset dirs=$@
+ typeset search_path=""
+
+ log_note "Calling $ZDB to verify filesystem '$filesys'"
+ log_must $ZPOOL export $pool
+
+ if [[ -n $dirs ]] ; then
+ for dir in $dirs ; do
+ search_path="$search_path -d $dir"
+ done
+ fi
+
+ log_must $ZPOOL import $search_path $pool
+
+ $ZDB -cudi $filesys > $zdbout 2>&1
+ if [[ $? != 0 ]]; then
+ log_note "Output: $ZDB -cudi $filesys"
+ $CAT $zdbout
+ log_fail "$ZDB detected errors with: '$filesys'"
+ fi
+
+ log_must $RM -rf $zdbout
+}
+
+#
+# Given a pool, and this function list all disks in the pool
+#
+function get_disklist # pool
+{
+ typeset disklist=""
+
+ disklist=$($ZPOOL iostat -v $1 | $NAWK '(NR >4 ) {print $1}' | \
+ $GREP -v "\-\-\-\-\-" | \
+ $EGREP -v -e "^(mirror|raidz1|raidz2|spare|log|cache)$" )
+
+ $ECHO $disklist
+}
+
+#
+# Destroy all existing metadevices and state database
+#
+function destroy_metas
+{
+ typeset metad
+
+ for metad in $($METASTAT -p | $AWK '{print $1}'); do
+ log_must $METACLEAR -rf $metad
+ done
+
+ for metad in $($METADB | $CUT -f6 | $GREP dev | $UNIQ); do
+ log_must $METADB -fd $metad
+ done
+}
+
+# /**
+# This function kills a given list of processes after a time period. We use
+# this in the stress tests instead of STF_TIMEOUT so that we can have processes
+# run for a fixed amount of time, yet still pass. Tests that hit STF_TIMEOUT
+# would be listed as FAIL, which we don't want : we're happy with stress tests
+# running for a certain amount of time, then finishing.
+#
+# @param $1 the time in seconds after which we should terminate these processes
+# @param $2..$n the processes we wish to terminate.
+# */
+function stress_timeout
+{
+ typeset -i TIMEOUT=$1
+ shift
+ typeset cpids="$@"
+
+ log_note "Waiting for child processes($cpids). " \
+ "It could last dozens of minutes, please be patient ..."
+ log_must $SLEEP $TIMEOUT
+
+ log_note "Killing child processes after ${TIMEOUT} stress timeout."
+ typeset pid
+ for pid in $cpids; do
+ $PS -p $pid > /dev/null 2>&1
+ if (( $? == 0 )); then
+ log_must $KILL -USR1 $pid
+ fi
+ done
+}
+
+#
+# Check whether current OS support a specified feature or not
+#
+# return 0 if current OS version is in unsupported list, 1 otherwise
+#
+# $1 unsupported target OS versions
+#
+function check_version # <OS version>
+{
+ typeset unsupported_vers="$@"
+ typeset ver
+ typeset cur_ver=`$UNAME -r`
+
+ for ver in $unsupported_vers; do
+ [[ "$cur_ver" == "$ver" ]] && return 0
+ done
+
+ return 1
+}
+
+#
+# Verify a given hotspare disk is inuse or avail
+#
+# Return 0 is pool/disk matches expected state, 1 otherwise
+#
+function check_hotspare_state # pool disk state{inuse,avail}
+{
+ typeset pool=$1
+ typeset disk=${2#/dev/}
+ disk=${disk#/dev/}
+ disk=${disk#/dev/}
+ typeset state=$3
+
+ cur_state=$(get_device_state $pool $disk "spares")
+
+ if [[ $state != ${cur_state} ]]; then
+ return 1
+ fi
+ return 0
+}
+
+#
+# Verify a given slog disk is inuse or avail
+#
+# Return 0 is pool/disk matches expected state, 1 otherwise
+#
+function check_slog_state # pool disk state{online,offline,unavail}
+{
+ typeset pool=$1
+ typeset disk=${2#/dev/}
+ disk=${disk#/dev/}
+ disk=${disk#/dev/}
+ typeset state=$3
+
+ cur_state=$(get_device_state $pool $disk "logs")
+
+ if [[ $state != ${cur_state} ]]; then
+ return 1
+ fi
+ return 0
+}
+
+#
+# Verify a given vdev disk is inuse or avail
+#
+# Return 0 is pool/disk matches expected state, 1 otherwise
+#
+function check_vdev_state # pool disk state{online,offline,unavail}
+{
+ typeset pool=$1
+ typeset disk=${2#/dev/}
+ disk=${disk#/dev/}
+ disk=${disk#/dev/}
+ typeset state=$3
+
+ if [[ $WRAPPER == *"smi"* ]]; then
+ $ECHO $disk | $EGREP "^c[0-F]+([td][0-F]+)+$" > /dev/null 2>&1
+ if (( $? == 0 )); then
+ disk=${disk}s2
+ fi
+ fi
+
+ cur_state=$(get_device_state $pool $disk)
+
+ if [[ $state != ${cur_state} ]]; then
+ return 1
+ fi
+ return 0
+}
+
+#
+# Check the output of 'zpool status -v <pool>',
+# and to see if the content of <token> contain the <keyword> specified.
+#
+# Return 0 is contain, 1 otherwise
+#
+function check_pool_status # pool token keyword
+{
+ typeset pool=$1
+ typeset token=$2
+ typeset keyword=$3
+
+ $ZPOOL status -v "$pool" 2>/dev/null | \
+ $NAWK -v token="$token:" '($1==token) {print $0}' | \
+ $GREP -i "$keyword" >/dev/null 2>&1
+
+ return $?
+}
+
+function vdev_pool_error_count
+{
+ typeset errs=$1
+ if [ -z "$2" ]; then
+ test $errs -gt 0; ret=$?
+ else
+ test $errs -eq $2; ret=$?
+ fi
+ log_debug "vdev_pool_error_count: errs='$errs' \$2='$2' ret='$ret'"
+ return $ret
+}
+
+#
+# Generate a pool status error file suitable for pool_errors_from_file.
+# If the pool is healthy, returns 0. Otherwise, the caller must handle the
+# returned temporarily file appropriately.
+#
+function pool_error_file # <pool>
+{
+ typeset pool="$1"
+
+ typeset tmpfile=$TMPDIR/pool_status.${TESTCASE_ID}
+ $ZPOOL status -x $pool > ${tmpfile}
+ echo $tmpfile
+}
+
+#
+# Evaluates <file> counting the number of errors. If vdev specified, only
+# that vdev's errors are counted. Returns the total number. <file> will be
+# deleted on exit.
+#
+function pool_errors_from_file # <file> [vdev]
+{
+ typeset file=$1
+ shift
+ typeset checkvdev="$2"
+
+ typeset line
+ typeset -i fetchbegin=1
+ typeset -i errnum=0
+ typeset -i c_read=0
+ typeset -i c_write=0
+ typeset -i c_cksum=0
+
+ cat ${file} | $EGREP -v "pool:" | while read line; do
+ if (( $fetchbegin != 0 )); then
+ $ECHO $line | $GREP "NAME" >/dev/null 2>&1
+ (( $? == 0 )) && (( fetchbegin = 0 ))
+ continue
+ fi
+
+ if [[ -n $checkvdev ]]; then
+ $ECHO $line | $GREP $checkvdev >/dev/null 2>&1
+ (( $? != 0 )) && continue
+ c_read=`$ECHO $line | $AWK '{print $3}'`
+ c_write=`$ECHO $line | $AWK '{print $4}'`
+ c_cksum=`$ECHO $line | $AWK '{print $5}'`
+ if [ $c_read != 0 ] || [ $c_write != 0 ] || \
+ [ $c_cksum != 0 ]
+ then
+ (( errnum = errnum + 1 ))
+ fi
+ break
+ fi
+
+ c_read=`$ECHO $line | $AWK '{print $3}'`
+ c_write=`$ECHO $line | $AWK '{print $4}'`
+ c_cksum=`$ECHO $line | $AWK '{print $5}'`
+ if [ $c_read != 0 ] || [ $c_write != 0 ] || \
+ [ $c_cksum != 0 ]
+ then
+ (( errnum = errnum + 1 ))
+ fi
+ done
+
+ rm -f $file
+ echo $errnum
+}
+
+#
+# Returns whether the vdev has the given number of errors.
+# If the number is unspecified, any non-zero number returns true.
+#
+function vdev_has_errors # pool vdev [errors]
+{
+ typeset pool=$1
+ typeset vdev=$2
+ typeset tmpfile=$(pool_error_file $pool)
+ log_note "Original pool status:"
+ cat $tmpfile
+
+ typeset -i errs=$(pool_errors_from_file $tmpfile $vdev)
+ vdev_pool_error_count $errs $3
+}
+
+#
+# Returns whether the pool has the given number of errors.
+# If the number is unspecified, any non-zero number returns true.
+#
+function pool_has_errors # pool [errors]
+{
+ typeset pool=$1
+ typeset tmpfile=$(pool_error_file $pool)
+ log_note "Original pool status:"
+ cat $tmpfile
+
+ typeset -i errs=$(pool_errors_from_file $tmpfile)
+ vdev_pool_error_count $errs $2
+}
+
+#
+# Returns whether clearing $pool at $vdev (if given) succeeds.
+#
+function pool_clear_succeeds
+{
+ typeset pool="$1"
+ typeset vdev=$2
+
+ $ZPOOL clear $pool $vdev
+ ! pool_has_errors $pool
+}
+
+#
+# Return whether the pool is healthy
+#
+function is_pool_healthy # pool
+{
+ typeset pool=$1
+
+ typeset healthy_output="pool '$pool' is healthy"
+ typeset real_output=$($ZPOOL status -x $pool)
+
+ if [[ "$real_output" == "$healthy_output" ]]; then
+ return 0
+ else
+ typeset -i ret
+ $ZPOOL status -x $pool | $GREP "state:" | \
+ $GREP "FAULTED" >/dev/null 2>&1
+ ret=$?
+ (( $ret == 0 )) && return 1
+ typeset l_scan
+ typeset errnum
+ l_scan=$($ZPOOL status -x $pool | $GREP "scan:")
+ l_scan=${l_scan##*"with"}
+ errnum=$($ECHO $l_scan | $AWK '{print $1}')
+ if [ "$errnum" != "0" ]; then
+ return 1
+ else
+ return 0
+ fi
+ fi
+}
+
+#
+# These 5 following functions are instance of check_pool_status()
+# is_pool_resilvering - to check if the pool is resilver in progress
+# is_pool_resilvered - to check if the pool is resilver completed
+# is_pool_scrubbing - to check if the pool is scrub in progress
+# is_pool_scrubbed - to check if the pool is scrub completed
+# is_pool_scrub_stopped - to check if the pool is scrub stopped
+#
+function is_pool_resilvering #pool
+{
+ check_pool_status "$1" "scan" "resilver in progress"
+ return $?
+}
+
+function is_pool_resilvered #pool
+{
+ check_pool_status "$1" "scan" "resilvered"
+ return $?
+}
+
+function resilver_happened # pool
+{
+ typeset pool=$1
+ is_pool_resilvering "$pool" || is_pool_resilvered "$pool"
+}
+
+function is_pool_scrubbing #pool
+{
+ check_pool_status "$1" "scan" "scrub in progress"
+ return $?
+}
+
+function is_pool_scrubbed #pool
+{
+ check_pool_status "$1" "scan" "scrub repaired"
+ return $?
+}
+
+function is_pool_scrub_stopped #pool
+{
+ check_pool_status "$1" "scan" "scrub canceled"
+ return $?
+}
+
+function is_pool_state # pool state
+{
+ check_pool_status "$1" "state" "$2"
+ return $?
+}
+
+#
+# Erase the partition tables and destroy any zfs labels
+#
+function cleanup_devices #vdevs
+{
+ for device in $@; do
+ # Labelclear must happen first, otherwise it may interfere
+ # with the teardown/setup of GPT labels.
+ $ZPOOL labelclear -f $device
+ # Only wipe partition tables for arguments that are disks,
+ # as opposed to slices (which are valid arguments here).
+ if geom disk list | grep -qx "Geom name: ${device#/dev/}"; then
+ wipe_partition_table $device
+ fi
+ done
+ return 0
+}
+
+#
+# Verify the rsh connectivity to each remote host in RHOSTS.
+#
+# Return 0 if remote host is accessible; otherwise 1.
+# $1 remote host name
+# $2 username
+#
+function verify_rsh_connect #rhost, username
+{
+ typeset rhost=$1
+ typeset username=$2
+ typeset rsh_cmd="$RSH -n"
+ typeset cur_user=
+
+ $GETENT hosts $rhost >/dev/null 2>&1
+ if (( $? != 0 )); then
+ log_note "$rhost cannot be found from" \
+ "administrative database."
+ return 1
+ fi
+
+ $PING $rhost 3 >/dev/null 2>&1
+ if (( $? != 0 )); then
+ log_note "$rhost is not reachable."
+ return 1
+ fi
+
+ if (( ${#username} != 0 )); then
+ rsh_cmd="$rsh_cmd -l $username"
+ cur_user="given user \"$username\""
+ else
+ cur_user="current user \"`$LOGNAME`\""
+ fi
+
+ if ! $rsh_cmd $rhost $TRUE; then
+ log_note "$RSH to $rhost is not accessible" \
+ "with $cur_user."
+ return 1
+ fi
+
+ return 0
+}
+
+#
+# Verify the remote host connection via rsh after rebooting
+# $1 remote host
+#
+function verify_remote
+{
+ rhost=$1
+
+ #
+ # The following loop waits for the remote system rebooting.
+ # Each iteration will wait for 150 seconds. there are
+ # total 5 iterations, so the total timeout value will
+ # be 12.5 minutes for the system rebooting. This number
+ # is an approxiate number.
+ #
+ typeset -i count=0
+ while ! verify_rsh_connect $rhost; do
+ sleep 150
+ (( count = count + 1 ))
+ if (( count > 5 )); then
+ return 1
+ fi
+ done
+ return 0
+}
+
+#
+# Replacement function for /usr/bin/rsh. This function will include
+# the /usr/bin/rsh and meanwhile return the execution status of the
+# last command.
+#
+# $1 usrname passing down to -l option of /usr/bin/rsh
+# $2 remote machine hostname
+# $3... command string
+#
+
+function rsh_status
+{
+ typeset ruser=$1
+ typeset rhost=$2
+ typeset -i ret=0
+ typeset cmd_str=""
+ typeset rsh_str=""
+
+ shift; shift
+ cmd_str="$@"
+
+ err_file=$TMPDIR/${rhost}.${TESTCASE_ID}.err
+ if (( ${#ruser} == 0 )); then
+ rsh_str="$RSH -n"
+ else
+ rsh_str="$RSH -n -l $ruser"
+ fi
+
+ $rsh_str $rhost /usr/local/bin/ksh93 -c "'$cmd_str; \
+ print -u 2 \"status=\$?\"'" \
+ >/dev/null 2>$err_file
+ ret=$?
+ if (( $ret != 0 )); then
+ $CAT $err_file
+ $RM -f $std_file $err_file
+ log_fail "$RSH itself failed with exit code $ret..."
+ fi
+
+ ret=$($GREP -v 'print -u 2' $err_file | $GREP 'status=' | \
+ $CUT -d= -f2)
+ (( $ret != 0 )) && $CAT $err_file >&2
+
+ $RM -f $err_file >/dev/null 2>&1
+ return $ret
+}
+
+#
+# Get the SUNWstc-fs-zfs package installation path in a remote host
+# $1 remote host name
+#
+function get_remote_pkgpath
+{
+ typeset rhost=$1
+ typeset pkgpath=""
+
+ pkgpath=$($RSH -n $rhost "$PKGINFO -l SUNWstc-fs-zfs | $GREP BASEDIR: |\
+ $CUT -d: -f2")
+
+ $ECHO $pkgpath
+}
+
+#/**
+# A function to find and locate free disks on a system or from given
+# disks as the parameter. Since the conversion to ATF, this function is
+# superfluous; it is assumed that the user will supply an accurate list of
+# disks to use. So we just return the arguments.
+#
+# $@ given disks to find which are free
+#
+# @return a string containing the list of available disks
+#*/
+function find_disks
+{
+ (( first=0 ))
+ for disk in $@; do
+ [[ $first == 1 ]] && echo -n " "
+ (( first=1 ))
+ case $disk in
+ /dev/*) echo -n "$disk" ;;
+ *) echo -n "/dev/$disk" ;;
+ esac
+ done
+}
+
+# A function to set convenience variables for disks.
+function set_disks
+{
+ set -A disk_array $(find_disks $DISKS)
+ [[ -z "$DISK_ARRAY_LIMIT" ]] && typeset -i DISK_ARRAY_LIMIT=5
+
+ export DISK=""
+ typeset -i i=0
+ while (( i < ${#disk_array[*]} && i <= $DISK_ARRAY_LIMIT )); do
+ export DISK${i}="${disk_array[$i]}"
+ DISKSARRAY="$DISKSARRAY ${disk_array[$i]}"
+ (( i = i + 1 ))
+ done
+ export DISK_ARRAY_NUM=$i
+ export DISKSARRAY
+ export disk=$DISK0
+}
+
+#
+# Add specified user to specified group
+#
+# $1 group name
+# $2 user name
+#
+function add_user #<group_name> <user_name>
+{
+ typeset gname=$1
+ typeset uname=$2
+
+ if (( ${#gname} == 0 || ${#uname} == 0 )); then
+ log_fail "group name or user name are not defined."
+ fi
+
+ # Check to see if the user exists.
+ $ID $uname > /dev/null 2>&1 && return 0
+
+ # Assign 1000 as the base uid
+ typeset -i uid=1000
+ while true; do
+ typeset -i ret
+ $USERADD -u $uid -g $gname -d /var/tmp/$uname -m $uname
+ ret=$?
+ case $ret in
+ 0) return 0 ;;
+ # The uid is not unique
+ 65) ((uid += 1)) ;;
+ *) return 1 ;;
+ esac
+ if [[ $uid == 65000 ]]; then
+ log_fail "No user id available under 65000 for $uname"
+ fi
+ done
+
+ return 0
+}
+
+#
+# Delete the specified user.
+#
+# $1 login name
+#
+function del_user #<logname>
+{
+ typeset user=$1
+
+ if (( ${#user} == 0 )); then
+ log_fail "login name is necessary."
+ fi
+
+ if $ID $user > /dev/null 2>&1; then
+ log_must $USERDEL $user
+ fi
+
+ return 0
+}
+
+#
+# Select valid gid and create specified group.
+#
+# $1 group name
+#
+function add_group #<group_name>
+{
+ typeset group=$1
+
+ if (( ${#group} == 0 )); then
+ log_fail "group name is necessary."
+ fi
+
+ # See if the group already exists.
+ $GROUPSHOW $group >/dev/null 2>&1
+ [[ $? == 0 ]] && return 0
+
+ # Assign 100 as the base gid
+ typeset -i gid=100
+ while true; do
+ $GROUPADD -g $gid $group > /dev/null 2>&1
+ typeset -i ret=$?
+ case $ret in
+ 0) return 0 ;;
+ # The gid is not unique
+ 65) ((gid += 1)) ;;
+ *) return 1 ;;
+ esac
+ if [[ $gid == 65000 ]]; then
+ log_fail "No user id available under 65000 for $group"
+ fi
+ done
+}
+
+#
+# Delete the specified group.
+#
+# $1 group name
+#
+function del_group #<group_name>
+{
+ typeset grp=$1
+ if (( ${#grp} == 0 )); then
+ log_fail "group name is necessary."
+ fi
+
+ $GROUPDEL -n $grp > /dev/null 2>&1
+ typeset -i ret=$?
+ case $ret in
+ # Group does not exist, or was deleted successfully.
+ 0|6|65) return 0 ;;
+ # Name already exists as a group name
+ 9) log_must $GROUPDEL $grp ;;
+ *) return 1 ;;
+ esac
+
+ return 0
+}
+
+#
+# This function will return true if it's safe to destroy the pool passed
+# as argument 1. It checks for pools based on zvols and files, and also
+# files contained in a pool that may have a different mountpoint.
+#
+function safe_to_destroy_pool { # $1 the pool name
+
+ typeset pool=""
+ typeset DONT_DESTROY=""
+
+ # We check that by deleting the $1 pool, we're not
+ # going to pull the rug out from other pools. Do this
+ # by looking at all other pools, ensuring that they
+ # aren't built from files or zvols contained in this pool.
+
+ for pool in $($ZPOOL list -H -o name)
+ do
+ ALTMOUNTPOOL=""
+
+ # this is a list of the top-level directories in each of the files
+ # that make up the path to the files the pool is based on
+ FILEPOOL=$($ZPOOL status -v $pool | $GREP /$1/ | \
+ $AWK '{print $1}')
+
+ # this is a list of the zvols that make up the pool
+ ZVOLPOOL=$($ZPOOL status -v $pool | $GREP "/dev/zvol/$1$" | \
+ $AWK '{print $1}')
+
+ # also want to determine if it's a file-based pool using an
+ # alternate mountpoint...
+ POOL_FILE_DIRS=$($ZPOOL status -v $pool | \
+ $GREP / | $AWK '{print $1}' | \
+ $AWK -F/ '{print $2}' | $GREP -v "dev")
+
+ for pooldir in $POOL_FILE_DIRS
+ do
+ OUTPUT=$($ZFS list -H -r -o mountpoint $1 | \
+ $GREP "${pooldir}$" | $AWK '{print $1}')
+
+ ALTMOUNTPOOL="${ALTMOUNTPOOL}${OUTPUT}"
+ done
+
+
+ if [ ! -z "$ZVOLPOOL" ]
+ then
+ DONT_DESTROY="true"
+ log_note "Pool $pool is built from $ZVOLPOOL on $1"
+ fi
+
+ if [ ! -z "$FILEPOOL" ]
+ then
+ DONT_DESTROY="true"
+ log_note "Pool $pool is built from $FILEPOOL on $1"
+ fi
+
+ if [ ! -z "$ALTMOUNTPOOL" ]
+ then
+ DONT_DESTROY="true"
+ log_note "Pool $pool is built from $ALTMOUNTPOOL on $1"
+ fi
+ done
+
+ if [ -z "${DONT_DESTROY}" ]
+ then
+ return 0
+ else
+ log_note "Warning: it is not safe to destroy $1!"
+ return 1
+ fi
+}
+
+#
+# Get IP address of hostname
+# $1 hostname
+#
+function getipbyhost
+{
+ typeset ip
+ ip=`$ARP $1 2>/dev/null | $AWK -F\) '{print $1}' \
+ | $AWK -F\( '{print $2}'`
+ $ECHO $ip
+}
+
+#
+# Setup iSCSI initiator to target
+# $1 target hostname
+#
+function iscsi_isetup
+{
+ # check svc:/network/iscsi_initiator:default state, try to enable it
+ # if the state is not ON
+ typeset ISCSII_FMRI="svc:/network/iscsi_initiator:default"
+ if [[ "ON" != $($SVCS -H -o sta $ISCSII_FMRI) ]]; then
+ log_must $SVCADM enable $ISCSII_FMRI
+
+ typeset -i retry=20
+ while [[ "ON" != $($SVCS -H -o sta $ISCSII_FMRI) && \
+ ( $retry -ne 0 ) ]]
+ do
+ (( retry = retry - 1 ))
+ $SLEEP 1
+ done
+
+ if [[ "ON" != $($SVCS -H -o sta $ISCSII_FMRI) ]]; then
+ log_fail "$ISCSII_FMRI service can not be enabled!"
+ fi
+ fi
+
+ log_must $ISCSIADM add discovery-address $(getipbyhost $1)
+ log_must $ISCSIADM modify discovery --sendtargets enable
+ log_must $DEVFSADM -i iscsi
+}
+
+#
+# Check whether iscsi parameter is set as remote
+#
+# return 0 if iscsi is set as remote, otherwise 1
+#
+function check_iscsi_remote
+{
+ if [[ $iscsi == "remote" ]] ; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+#
+# Check if a volume is a valide iscsi target
+# $1 volume name
+# return 0 if suceeds, otherwise, return 1
+#
+function is_iscsi_target
+{
+ typeset dataset=$1
+ typeset target targets
+
+ [[ -z $dataset ]] && return 1
+
+ targets=$($ISCSITADM list target | $GREP "Target:" | $AWK '{print $2}')
+ [[ -z $targets ]] && return 1
+
+ for target in $targets; do
+ [[ $dataset == $target ]] && return 0
+ done
+
+ return 1
+}
+
+#
+# Get the iSCSI name of a target
+# $1 target name
+#
+function iscsi_name
+{
+ typeset target=$1
+ typeset name
+
+ [[ -z $target ]] && log_fail "No parameter."
+
+ if ! is_iscsi_target $target ; then
+ log_fail "Not a target."
+ fi
+
+ name=$($ISCSITADM list target $target | $GREP "iSCSI Name:" \
+ | $AWK '{print $2}')
+
+ return $name
+}
+
+#
+# check svc:/system/iscsitgt:default state, try to enable it if the state
+# is not ON
+#
+function iscsitgt_setup
+{
+ log_must $RM -f $ISCSITGTFILE
+ if [[ "ON" == $($SVCS -H -o sta $ISCSITGT_FMRI) ]]; then
+ log_note "iscsitgt is already enabled"
+ return
+ fi
+
+ log_must $SVCADM enable -t $ISCSITGT_FMRI
+
+ typeset -i retry=20
+ while [[ "ON" != $($SVCS -H -o sta $ISCSITGT_FMRI) && \
+ ( $retry -ne 0 ) ]]
+ do
+ $SLEEP 1
+ (( retry = retry - 1 ))
+ done
+
+ if [[ "ON" != $($SVCS -H -o sta $ISCSITGT_FMRI) ]]; then
+ log_fail "$ISCSITGT_FMRI service can not be enabled!"
+ fi
+
+ log_must $TOUCH $ISCSITGTFILE
+}
+
+#
+# set DISABLED state of svc:/system/iscsitgt:default
+# which is the most suiteable state if $ISCSITGTFILE exists
+#
+function iscsitgt_cleanup
+{
+ if [[ -e $ISCSITGTFILE ]]; then
+ log_must $SVCADM disable $ISCSITGT_FMRI
+ log_must $RM -f $ISCSITGTFILE
+ fi
+}
+
+#
+# Close iSCSI initiator to target
+# $1 target hostname
+#
+function iscsi_iclose
+{
+ log_must $ISCSIADM modify discovery --sendtargets disable
+ log_must $ISCSIADM remove discovery-address $(getipbyhost $1)
+ $DEVFSADM -Cv
+}
+
+#
+# Get the available ZFS compression options
+# $1 option type zfs_set|zfs_compress
+#
+function get_compress_opts
+{
+ typeset COMPRESS_OPTS
+ typeset GZIP_OPTS="gzip gzip-1 gzip-2 gzip-3 gzip-4 gzip-5 \
+ gzip-6 gzip-7 gzip-8 gzip-9"
+
+ if [[ $1 == "zfs_compress" ]] ; then
+ COMPRESS_OPTS="on lzjb"
+ elif [[ $1 == "zfs_set" ]] ; then
+ COMPRESS_OPTS="on off lzjb"
+ fi
+ typeset valid_opts="$COMPRESS_OPTS"
+ $ZFS get 2>&1 | $GREP gzip >/dev/null 2>&1
+ if [[ $? -eq 0 ]]; then
+ valid_opts="$valid_opts $GZIP_OPTS"
+ fi
+ $ECHO "$valid_opts"
+}
+
+#
+# Check the subcommand/option is supported
+#
+function check_opt_support #command, option
+{
+ typeset command=$1
+ typeset option=$2
+
+ if [[ -z $command ]]; then
+ return 0
+ elif [[ -z $option ]]; then
+ eval "$ZFS 2>&1 | $GREP '$command' > /dev/null 2>&1"
+ else
+ eval "$ZFS $command 2>&1 | $GREP -- '$option' | \
+ $GREP -v -- 'User-defined' > /dev/null 2>&1"
+ fi
+ return $?
+}
+
+#
+# Check the zpool subcommand/option is supported
+#
+function check_zpool_opt_support #command, option
+{
+ typeset command=$1
+ typeset option=$2
+
+ if [[ -z $command ]]; then
+ return 0
+ elif [[ -z $option ]]; then
+ eval "$ZPOOL 2>&1 | $GREP '$command' > /dev/null 2>&1"
+ else
+ eval "$ZPOOL $command 2>&1 | $GREP -- '$option' > /dev/null 2>&1"
+ fi
+ return $?
+}
+
+#
+# Verify zfs operation with -p option work as expected
+# $1 operation, value could be create, clone or rename
+# $2 dataset type, value could be fs or vol
+# $3 dataset name
+# $4 new dataset name
+#
+function verify_opt_p_ops
+{
+ typeset ops=$1
+ typeset datatype=$2
+ typeset dataset=$3
+ typeset newdataset=$4
+
+ if [[ $datatype != "fs" && $datatype != "vol" ]]; then
+ log_fail "$datatype is not supported."
+ fi
+
+ # check parameters accordingly
+ case $ops in
+ create)
+ newdataset=$dataset
+ dataset=""
+ if [[ $datatype == "vol" ]]; then
+ ops="create -V $VOLSIZE"
+ fi
+ ;;
+ clone)
+ if [[ -z $newdataset ]]; then
+ log_fail "newdataset should not be empty" \
+ "when ops is $ops."
+ fi
+ log_must datasetexists $dataset
+ log_must snapexists $dataset
+ ;;
+ rename)
+ if [[ -z $newdataset ]]; then
+ log_fail "newdataset should not be empty" \
+ "when ops is $ops."
+ fi
+ log_must datasetexists $dataset
+ log_mustnot snapexists $dataset
+ ;;
+ *)
+ log_fail "$ops is not supported."
+ ;;
+ esac
+
+ # make sure the upper level filesystem does not exist
+ if datasetexists ${newdataset%/*} ; then
+ log_must $ZFS destroy -rRf ${newdataset%/*}
+ fi
+
+ # without -p option, operation will fail
+ log_mustnot $ZFS $ops $dataset $newdataset
+ log_mustnot datasetexists $newdataset ${newdataset%/*}
+
+ # with -p option, operation should succeed
+ log_must $ZFS $ops -p $dataset $newdataset
+ if ! datasetexists $newdataset ; then
+ log_fail "-p option does not work for $ops"
+ fi
+
+ # when $ops is create or clone, redo the operation still return zero
+ if [[ $ops != "rename" ]]; then
+ log_must $ZFS $ops -p $dataset $newdataset
+ fi
+
+ return 0
+}
+
+function get_disk_guid
+{
+ typeset diskname=$1
+ lastcwd=$(pwd)
+ cd /dev
+ guid=$($ZDB -l ${diskname} | ${AWK} '/^ guid:/ {print $2}' | head -1)
+ cd $lastcwd
+ echo $guid
+}
+
+#
+# Get cachefile for a pool.
+# Prints the cache file, if there is one.
+# Returns 0 for a default zpool.cache, 1 for an explicit one, and 2 for none.
+#
+function cachefile_for_pool
+{
+ typeset pool=$1
+
+ cachefile=$(get_pool_prop cachefile $pool)
+ [[ $? != 0 ]] && return 1
+
+ case "$cachefile" in
+ none) ret=2 ;;
+ "-")
+ ret=2
+ for dir in /boot/zfs /etc/zfs; do
+ if [[ -f "${dir}/zpool.cache" ]]; then
+ cachefile="${dir}/zpool.cache"
+ ret=0
+ break
+ fi
+ done
+ ;;
+ *) ret=1;
+ esac
+ [[ $ret -eq 0 || $ret -eq 1 ]] && print "$cachefile"
+ return $ret
+}
+
+#
+# Assert that the pool is in the appropriate cachefile.
+#
+function assert_pool_in_cachefile
+{
+ typeset pool=$1
+
+ cachefile=$(cachefile_for_pool $pool)
+ [ $? -ne 0 ] && log_fail "ERROR: Cachefile not created for '$pool'?"
+ log_must test -e "${cachefile}"
+ log_must zdb -U ${cachefile} -C ${pool}
+}
+
+#
+# Get the zdb options given the cachefile state of the pool.
+#
+function zdb_cachefile_opts
+{
+ typeset pool=$1
+ typeset vdevdir=$2
+ typeset opts
+
+ if poolexists "$pool"; then
+ cachefile=$(cachefile_for_pool $pool)
+ typeset -i ret=$?
+ case $ret in
+ 0) opts="-C" ;;
+ 1) opts="-U $cachefile -C" ;;
+ 2) opts="-eC" ;;
+ *) log_fail "Unknown return '$ret'" ;;
+ esac
+ else
+ opts="-eC"
+ [[ -n "$vdevdir" ]] && opts="$opts -p $vdevdir"
+ fi
+ echo "$opts"
+}
+
+#
+# Get configuration of pool
+# $1 pool name
+# $2 config name
+#
+function get_config
+{
+ typeset pool=$1
+ typeset config=$2
+ typeset vdevdir=$3
+ typeset alt_root
+ typeset zdb_opts
+
+ zdb_opts=$(zdb_cachefile_opts $pool $vdevdir)
+ value=$($ZDB $zdb_opts $pool | $GREP "$config:" | $AWK -F: '{print $2}')
+ if [[ -n $value ]] ; then
+ value=${value#'}
+ value=${value%'}
+ else
+ return 1
+ fi
+ echo $value
+
+ return 0
+}
+
+#
+# Privated function. Random select one of items from arguments.
+#
+# $1 count
+# $2-n string
+#
+function _random_get
+{
+ typeset cnt=$1
+ shift
+
+ typeset str="$@"
+ typeset -i ind
+ ((ind = RANDOM % cnt + 1))
+
+ typeset ret=$($ECHO "$str" | $CUT -f $ind -d ' ')
+ $ECHO $ret
+}
+
+#
+# Random select one of item from arguments which include NONE string
+#
+function random_get_with_non
+{
+ typeset -i cnt=$#
+ ((cnt =+ 1))
+
+ _random_get "$cnt" "$@"
+}
+
+#
+# Random select one of item from arguments which doesn't include NONE string
+#
+function random_get
+{
+ _random_get "$#" "$@"
+}
+
+#
+# The function will generate a dataset name with specific length
+# $1, the length of the name
+# $2, the base string to construct the name
+#
+function gen_dataset_name
+{
+ typeset -i len=$1
+ typeset basestr="$2"
+ typeset -i baselen=${#basestr}
+ typeset -i iter=0
+ typeset l_name=""
+
+ if (( len % baselen == 0 )); then
+ (( iter = len / baselen ))
+ else
+ (( iter = len / baselen + 1 ))
+ fi
+ while (( iter > 0 )); do
+ l_name="${l_name}$basestr"
+
+ (( iter -= 1 ))
+ done
+
+ $ECHO $l_name
+}
+
+#
+# Ensure that a given path has been synced, not just ZIL committed.
+#
+# XXX On FreeBSD, the sync(8) command (via $SYNC) calls zfs_sync() which just
+# does a zil_commit(), as opposed to a txg_wait_synced(). For things that
+# require writing to their final destination (e.g. for intentional
+# corruption purposes), zil_commit() is not good enough.
+#
+function force_sync_path # path
+{
+ typeset path="$1"
+
+ log_must $ZPOOL export $TESTPOOL
+ log_must $ZPOOL import -d $path $TESTPOOL
+}
+
+#
+# Get cksum tuple of dataset
+# $1 dataset name
+#
+# zdb output is like below
+# " Dataset pool/fs [ZPL], ID 978, cr_txg 2277, 19.0K, 5 objects,
+# rootbp [L0 DMU objset] 400L/200P DVA[0]=<0:1880c00:200>
+# DVA[1]=<0:341880c00:200> fletcher4 lzjb LE contiguous birth=2292 fill=5
+# cksum=989930ccf:4014fe00c83:da5e388e58b4:1f7332052252ac "
+#
+function datasetcksum
+{
+ typeset cksum
+ $SYNC
+ cksum=$($ZDB -vvv $1 | $GREP "^Dataset $1 \[" | $GREP "cksum" \
+ | $AWK -F= '{print $6}')
+ $ECHO $cksum
+}
+
+#
+# Get cksum of file
+# #1 file path
+#
+function checksum
+{
+ typeset cksum
+ cksum=$($CKSUM $1 | $AWK '{print $1}')
+ $ECHO $cksum
+}
+
+#
+# Get the given disk/slice state from the specific field of the pool
+#
+function get_device_state #pool disk field("", "spares","logs")
+{
+ typeset pool=$1
+ typeset disk=${2#/dev/}
+ disk=${disk#/dev/}
+ disk=${disk#/dev/}
+ typeset field=${3:-$pool}
+
+ state=$($ZPOOL status -v "$pool" 2>/dev/null | \
+ $NAWK -v device=$disk -v pool=$pool -v field=$field \
+ 'BEGIN {startconfig=0; startfield=0; }
+ /config:/ {startconfig=1}
+ (startconfig==1)&&($1==field) {startfield=1; next;}
+ (startfield==1)&&($1==device) {print $2; exit;}
+ (startfield==1)&&(NF>=3)&&($(NF-1)=="was")&&($NF==device) {print $2; exit;}
+ (startfield==1)&&($1==field || $1 ~ "^spares$" || $1 ~ "^logs$") {startfield=0}')
+ print $state
+}
+
+
+#
+# print the given directory filesystem type
+#
+# $1 directory name
+#
+function get_fstype
+{
+ typeset dir=$1
+
+ if [[ -z $dir ]]; then
+ log_fail "Usage: get_fstype <directory>"
+ fi
+
+ $DF -T $dir | $AWK '{print $2}'
+}
+
+#
+# Given a disk, label it to VTOC regardless what label was on the disk
+# $1 disk
+#
+function labelvtoc
+{
+ typeset disk=$1
+ if [[ -z $disk ]]; then
+ log_fail "The disk name is unspecified."
+ fi
+ typeset label_file=$TMPDIR/labelvtoc.${TESTCASE_ID}
+ typeset arch=$($UNAME -p)
+
+ if [[ $arch == "i386" ]]; then
+ $ECHO "label" > $label_file
+ $ECHO "0" >> $label_file
+ $ECHO "" >> $label_file
+ $ECHO "q" >> $label_file
+ $ECHO "q" >> $label_file
+
+ $FDISK -B $disk >/dev/null 2>&1
+ # wait a while for fdisk finishes
+ $SLEEP 60
+ elif [[ $arch == "sparc" ]]; then
+ $ECHO "label" > $label_file
+ $ECHO "0" >> $label_file
+ $ECHO "" >> $label_file
+ $ECHO "" >> $label_file
+ $ECHO "" >> $label_file
+ $ECHO "q" >> $label_file
+ else
+ log_fail "unknown arch type"
+ fi
+
+ $FORMAT -e -s -d $disk -f $label_file
+ typeset -i ret_val=$?
+ $RM -f $label_file
+ #
+ # wait the format to finish
+ #
+ $SLEEP 60
+ if (( ret_val != 0 )); then
+ log_fail "unable to label $disk as VTOC."
+ fi
+
+ return 0
+}
+
+#
+# Detect if the given filesystem property is supported in this release
+#
+# 0 Yes, it is supported
+# !0 No, it is not supported
+#
+function fs_prop_exist
+{
+ typeset prop=$1
+
+ if [[ -z $prop ]]; then
+ log_fail "Usage: fs_prop_exist <property>"
+
+ return 1
+ fi
+
+ #
+ # If the property is shortened column name,
+ # convert it to the standard name
+ #
+ case $prop in
+ avail) prop=available ;;
+ refer) prop=referenced ;;
+ volblock) prop=volblocksize ;;
+ compress) prop=compression ;;
+ rdonly) prop=readonly ;;
+ recsize) prop=recordsize ;;
+ reserv) prop=reservation ;;
+ refreserv) prop=refreservation ;;
+ esac
+
+ #
+ # The zfs get output looks like the following
+ #
+
+ #
+ # The following properties are supported:
+ #
+ # PROPERTY EDIT INHERIT VALUES
+ #
+ # available NO NO <size>
+ # compressratio NO NO <1.00x or higher if compressed>
+ # creation NO NO <date>
+ # ... ...
+ # zoned YES YES on | off
+ #
+ # Sizes are specified in bytes with standard units such as K, M, G, etc.
+ #
+
+ #
+ # Start to extract property from the first blank line after 'PROPERTY'
+ # and stop at the next blank line
+ #
+ $ZFS get 2>&1 | \
+ $AWK '/PROPERTY/ {start=1; next}
+ /Sizes/ {start=0}
+ start==1 {print $1}' | \
+ $GREP -w "$prop" > /dev/null 2>&1
+
+ return $?
+}
+
+#
+# Detect if the given pool property is supported in this release
+#
+# 0 Yes, it is supported
+# !0 No, it is not supported
+#
+function pool_prop_exist
+{
+ typeset prop=$1
+ if [[ -z $prop ]]; then
+ log_fail "Usage: pool_prop_exist <property>"
+
+ return 1
+ fi
+ #
+ # If the property is shortened column name,
+ # convert it to the standard name
+ #
+ case $prop in
+ avail) prop=available ;;
+ cap) prop=capacity ;;
+ replace) prop=autoreplace ;;
+ esac
+
+ #
+ # The zpool get output looks like the following
+ #
+
+ # usage:
+ # get <"all" | property[,...]> <pool> ...
+ #
+ # the following properties are supported:
+ #
+ # PROPERTY EDIT VALUES
+ #
+ # available NO <size>
+ # capacity NO <size>
+ # guid NO <guid>
+ # health NO <state>
+ # size NO <size>
+ # used NO <size>
+ # altroot YES <path>
+ # autoreplace YES on | off
+ # bootfs YES <filesystem>
+ # cachefile YES <file> | none
+ # delegation YES on | off
+ # failmode YES wait | continue | panic
+ # version YES <version>
+
+ $ZPOOL get 2>&1 | \
+ $AWK '/PROPERTY/ {start=1; next}
+ start==1 {print $1}' | \
+ $GREP -w "$prop" > /dev/null 2>&1
+
+ return $?
+}
+
+#
+# check if the system was installed as zfsroot or not
+# return: 0 ture, otherwise false
+#
+function is_zfsroot
+{
+ $DF -T / | $GREP -q zfs
+}
+
+#
+# get the root filesystem name if it's zfsroot system.
+#
+# return: root filesystem name
+function get_rootfs
+{
+ typeset rootfs=""
+ rootfs=$($MOUNT | $AWK '$3 == "\/" && $4~/zfs/ {print $1}')
+ if [[ -z "$rootfs" ]]; then
+ log_fail "Can not get rootfs"
+ fi
+ $ZFS list $rootfs > /dev/null 2>&1
+ if (( $? == 0 )); then
+ $ECHO $rootfs
+ else
+ log_fail "This is not a zfsroot system."
+ fi
+}
+
+#
+# get the rootfs's pool name
+# return:
+# rootpool name
+#
+function get_rootpool
+{
+ typeset rootfs=""
+ typeset rootpool=""
+ rootfs=$(get_rootfs)
+ rootpool=`$ECHO $rootfs | awk -F\/ '{print $1}'`
+ echo $rootpool
+}
+
+#
+# Get the sub string from specified source string
+#
+# $1 source string
+# $2 start position. Count from 1
+# $3 offset
+#
+function get_substr #src_str pos offset
+{
+ typeset pos offset
+
+ $ECHO $1 | \
+ $NAWK -v pos=$2 -v offset=$3 '{print substr($0, pos, offset)}'
+}
+
+#
+# Get the directory path of given device
+#
+function get_device_dir #device
+{
+ typeset device=$1
+
+ $ECHO "/dev"
+}
+
+#
+# Get the package name
+#
+function get_package_name
+{
+ typeset dirpath=${1:-$STC_NAME}
+
+ print "SUNWstc-${dirpath}" | /usr/bin/sed -e "s/\//-/g"
+}
+
+#
+# Get the word numbers from a string separated by white space
+#
+function get_word_count
+{
+ $ECHO $1 | $WC -w
+}
+
+#
+# To verify if the require numbers of disks is given
+#
+function verify_disk_count
+{
+ typeset -i min=${2:-1}
+
+ typeset -i count=$(get_word_count "$1")
+
+ if (( count < min )); then
+ atf_skip "A minimum of $min disks is required to run." \
+ " You specified $count disk(s)"
+ fi
+}
+
+#
+# Verify that vfs.zfs.vol.recursive is set, so pools can be created using zvols
+# as backing stores.
+#
+function verify_zvol_recursive
+{
+ if [ "`sysctl -n vfs.zfs.vol.recursive`" -ne 1 ]; then
+ atf_skip "Recursive ZVOLs not enabled"
+ fi
+}
+
+#
+# bsdmap disk/slice number to a device path
+#
+function bsddevmap
+{
+ typeset arg=$1
+ echo $arg | egrep "*s[0-9]$" > /dev/null 2>&1
+ if [ $? -eq 0 ]
+ then
+ n=`echo $arg| wc -c`
+ set -A map a b c d e f g h i j
+ s=`echo $arg | cut -c $((n-1))`
+ arg=${arg%s[0-9]}${map[$s]}
+ fi
+ echo $arg
+}
+
+#
+# Get the name of the snapshots directory. Traditionally .zfs/snapshots
+#
+function get_snapdir_name
+{
+ echo ".zfs/snapshot"
+}
+
+#
+# Unmount all ZFS filesystems except for those that are in the KEEP variable
+#
+function unmount_all_safe
+{
+ echo $(all_pools) | \
+ $XARGS -n 1 $ZFS list -H -o name -t all -r | \
+ $XARGS -n 1 $ZFS unmount
+}
+
+#
+# Return the highest pool version that this OS can create
+#
+function get_zpool_version
+{
+ # We assume output from zpool upgrade -v of the form:
+ #
+ # This system is currently running ZFS version 2.
+ # .
+ # .
+ typeset ZPOOL_VERSION=$($ZPOOL upgrade -v | $HEAD -1 | \
+ $AWK '{print $NF}' | $SED -e 's/\.//g')
+ # Starting with version 5000, the output format changes to:
+ # This system supports ZFS pool feature flags.
+ # .
+ # .
+ if [[ $ZPOOL_VERSION = "flags" ]]; then
+ ZPOOL_VERSION=5000
+ fi
+ echo $ZPOOL_VERSION
+}
+
+# Ensures that zfsd is running, starting it if necessary. Every test that
+# interacts with zfsd must call this at startup. This is intended primarily
+# to eliminate interference from outside the test suite.
+function ensure_zfsd_running
+{
+ if ! service zfsd status > /dev/null 2>&1; then
+ service zfsd start || service zfsd onestart
+ service zfsd status > /dev/null 2>&1 ||
+ log_unsupported "Test requires zfsd"
+ fi
+}
+
+# Temporarily stops ZFSD, because it can interfere with some tests. If this
+# function is used, then restart_zfsd _must_ be called in the cleanup routine.
+function stop_zfsd
+{
+ $RM -f $TMPDIR/.zfsd_enabled_during_stf_zfs_tests
+ if [[ -n "$ZFSD" && -x "$ZFSD" ]]; then
+ if /etc/rc.d/zfsd status > /dev/null; then
+ log_note "Stopping zfsd"
+ $TOUCH $TMPDIR/.zfsd_enabled_during_stf_zfs_tests
+ /etc/rc.d/zfsd stop || /etc/rc.d/zfsd onestop
+ fi
+ fi
+}
+
+# Restarts zfsd after it has been stopped by stop_zfsd. Intelligently restarts
+# only iff zfsd was running at the time stop_zfsd was called.
+function restart_zfsd
+{
+ if [[ -f $TMPDIR/.zfsd_enabled_during_stf_zfs_tests ]]; then
+ log_note "Restarting zfsd"
+ /etc/rc.d/zfsd start || /etc/rc.d/zfsd onestart
+ fi
+ $RM -f $TMPDIR/.zfsd_enabled_during_stf_zfs_tests
+}
+
+#
+# Using the given <vdev>, obtain the value of the property <propname> for
+# the given <tvd> identified by numeric id.
+#
+function get_tvd_prop # vdev tvd propname
+{
+ typeset vdev=$1
+ typeset -i tvd=$2
+ typeset propname=$3
+
+ $ZDB -l $vdev | $AWK -v tvd=$tvd -v prop="${propname}:" '
+ BEGIN { start = 0; }
+ /^ id:/ && ($2==tvd) { start = 1; next; }
+ (start==0) { next; }
+ /^ [a-z]+/ && ($1==prop) { print $2; exit; }
+ /^ children/ { exit; }
+ '
+}
+
+#
+# Convert a DVA into a physical block address. Prints number of blocks.
+# This takes the usual printed form, in which offsets are left shifted so
+# they represent bytes rather than the native sector count.
+#
+function dva_to_block_addr # dva
+{
+ typeset dva=$1
+
+ typeset offcol=$(echo $dva | cut -f2 -d:)
+ typeset -i offset="0x${offcol}"
+ # First add 4MB to skip the boot blocks and first two vdev labels,
+ # then convert to 512 byte blocks (for use with dd). Note that this
+ # differs from simply adding 8192 blocks, since the input offset is
+ # given in bytes and has the actual ashift baked in.
+ (( offset += 4*1024*1024 ))
+ (( offset >>= 9 ))
+ echo "$offset"
+}
+
+#
+# Convert a RAIDZ DVA into a physical block address. This has the same
+# output as dva_to_block_addr (number of blocks from beginning of device), but
+# is more complicated due to RAIDZ. ashift is normally always 9, but RAIDZ
+# uses the actual tvd ashift instead. Furthermore, the number of vdevs changes
+# the actual block for each device.
+#
+function raidz_dva_to_block_addr # dva ncols ashift
+{
+ typeset dva=$1
+ typeset -i ncols=$2
+ typeset -i ashift=$3
+
+ typeset -i offset=0x$(echo $dva | cut -f2 -d:)
+ (( offset >>= ashift ))
+
+ typeset -i ioff=$(( (offset + ncols - 1) / ncols ))
+
+ # Now add the front 4MB and return.
+ (( ioff += ( 4194304 >> $ashift ) ))
+ echo "$ioff"
+}
+
+#
+# Return the vdevs for the given toplevel vdev number.
+# Child vdevs will only be included if they are ONLINE. Output format:
+#
+# <toplevel vdev type> <nchildren> <child1>[:<child2> ...]
+#
+# Valid toplevel vdev types are mirror, raidz[1-3], leaf (which can be a
+# disk or a file). Note that 'nchildren' can be larger than the number of
+# returned children; it represents the number of children regardless of how
+# many are actually online.
+#
+function vdevs_for_tvd # pool tvd
+{
+ typeset pool=$1
+ typeset -i tvd=$2
+
+ $ZPOOL status $pool | $AWK -v want_tvd=$tvd '
+ BEGIN {
+ start = 0; tvd = -1; lvd = -1;
+ type = "UNKNOWN"; disks = ""; disk = "";
+ nchildren = 0;
+ }
+ /NAME.*STATE/ { start = 1; next; }
+ (start==0) { next; }
+
+ (tvd > want_tvd) { exit; }
+ END { print type " " nchildren " " disks; }
+
+ length(disk) > 0 {
+ if (length(disks) > 0) { disks = disks " "; }
+ if (substr(disk, 0, 1) == "/") {
+ disks = disks disk;
+ } else {
+ disks = disks "/dev/" disk;
+ }
+ disk = "";
+ }
+
+ /^\t(spares|logs)/ { tvd = want_tvd + 1; next; }
+ /^\t (mirror|raidz[1-3])-[0-9]+/ {
+ tvd += 1;
+ (tvd == want_tvd) && type = substr($1, 0, 6);
+ next;
+ }
+ /^\t [\/A-Za-z]+/ {
+ tvd += 1;
+ if (tvd == want_tvd) {
+ (( nchildren += 1 ))
+ type = "leaf";
+ ($2 == "ONLINE") && disk = $1;
+ }
+ next;
+ }
+
+ (tvd < want_tvd) { next; }
+
+ /^\t spare-[0-9]+/ { next; }
+ /^\t [\/A-Za-z]+/ {
+ (( nchildren += 1 ))
+ ($2 == "ONLINE") && disk = $1;
+ next;
+ }
+
+ /^\t [\/A-Za-z]+/ {
+ (( nchildren += 1 ))
+ ($2 == "ONLINE") && disk = $1;
+ next;
+ }
+ '
+}
+
+#
+# Get a vdev path, ashift & offset for a given pool/dataset and DVA.
+# If desired, can also select the toplevel vdev child number.
+#
+function dva_to_vdev_ashift_off # pool/dataset dva [leaf_vdev_num]
+{
+ typeset poollike=$1
+ typeset dva=$2
+ typeset -i leaf_vdev_num=$3
+
+ # vdevs are normally 0-indexed while arguments are 1-indexed.
+ (( leaf_vdev_num += 1 ))
+
+ # Strip any child datasets or snapshots.
+ pool=$(echo $poollike | sed -e 's,[/@].*,,g')
+ tvd=$(echo $dva | cut -d: -f1)
+
+ set -- $(vdevs_for_tvd $pool $tvd)
+ log_debug "vdevs_for_tvd: $* <EOM>"
+ tvd_type=$1; shift
+ nchildren=$1; shift
+
+ lvd=$(eval echo \$$leaf_vdev_num)
+ log_debug "type='$tvd_type' children='$nchildren' lvd='$lvd' dva='$dva'"
+ case $tvd_type in
+ raidz*)
+ ashift=$(get_tvd_prop $lvd $tvd ashift)
+ log_debug "raidz: ashift='${ashift}'"
+ off=$(raidz_dva_to_block_addr $dva $nchildren $ashift)
+ ;;
+ *)
+ ashift=9
+ off=$(dva_to_block_addr $dva)
+ ;;
+ esac
+ echo "${lvd}:${ashift}:${off}"
+}
+
+#
+# Get the DVA for the specified dataset's given filepath.
+#
+function file_dva # dataset filepath [level] [offset] [dva_num]
+{
+ typeset dataset=$1
+ typeset filepath=$2
+ typeset -i level=$3
+ typeset -i offset=$4
+ typeset -i dva_num=$5
+
+ typeset -li blksz=0
+ typeset -li blknum=0
+ typeset -li startoff
+ typeset -li inode
+
+ eval `$STAT -s "$filepath"`
+ inode="$st_ino"
+
+ # The inner match is for 'DVA[0]=<0:1b412600:200>', in which the
+ # text surrounding the actual DVA is a fixed size with 8 characters
+ # before it and 1 after.
+ $ZDB -P -vvvvv "$dataset/" $inode | \
+ $AWK -v level=${level} -v dva_num=${dva_num} '
+ BEGIN { stage = 0; }
+ (stage == 0) && ($1=="Object") { stage = 1; next; }
+
+ (stage == 1) {
+ print $3 " " $4;
+ stage = 2; next;
+ }
+
+ (stage == 2) && /^Indirect blocks/ { stage=3; next; }
+ (stage < 3) { next; }
+
+ match($2, /L[0-9]/) {
+ if (substr($2, RSTART+1, RLENGTH-1) != level) { next; }
+ }
+ match($3, /DVA\[.*>/) {
+ dva = substr($3, RSTART+8, RLENGTH-9);
+ if (substr($3, RSTART+4, 1) == dva_num) {
+ print $1 " " dva;
+ }
+ }
+ ' | \
+ while read line; do
+ log_debug "params='$blksz/$blknum/$startoff' line='$line'"
+ if (( blksz == 0 )); then
+ typeset -i iblksz=$(echo $line | cut -d " " -f1)
+ typeset -i dblksz=$(echo $line | cut -d " " -f2)
+
+ # Calculate the actual desired block starting offset.
+ if (( level > 0 )); then
+ typeset -i nbps_per_level
+ typeset -i indsz
+ typeset -i i=0
+
+ (( nbps_per_level = iblksz / 128 ))
+ (( blksz = dblksz ))
+ for (( i = 0; $i < $level; i++ )); do
+ (( blksz *= nbps_per_level ))
+ done
+ else
+ blksz=$dblksz
+ fi
+
+ (( blknum = offset / blksz ))
+ (( startoff = blknum * blksz ))
+ continue
+ fi
+
+ typeset lineoffstr=$(echo $line | cut -d " " -f1)
+ typeset -i lineoff=$(printf "%d" "0x${lineoffstr}")
+ typeset dva="$(echo $line | cut -d " " -f2)"
+ log_debug "str='$lineoffstr' lineoff='$lineoff' dva='$dva'"
+ if [[ -n "$dva" ]] && (( lineoff == startoff )); then
+ echo $line | cut -d " " -f2
+ return 0
+ fi
+ done
+ return 1
+}
+
+#
+# Corrupt the given dataset's filepath file. This will obtain the first
+# level 0 block's DVA and scribble random bits on it.
+#
+function corrupt_file # dataset filepath [leaf_vdev_num]
+{
+ typeset dataset=$1
+ typeset filepath=$2
+ typeset -i leaf_vdev_num="$3"
+
+ dva=$(file_dva $dataset $filepath)
+ [ $? -ne 0 ] && log_fail "ERROR: Can't find file $filepath on $dataset"
+
+ vdoff=$(dva_to_vdev_ashift_off $dataset $dva $leaf_vdev_num)
+ vdev=$(echo $vdoff | cut -d: -f1)
+ ashift=$(echo $vdoff | cut -d: -f2)
+ off=$(echo $vdoff | cut -d: -f3)
+ blocksize=$(( 1 << $ashift ))
+
+ log_note "Corrupting ${dataset}'s $filepath on $vdev at DVA $dva with ashift $ashift"
+ log_must $DD if=/dev/urandom bs=$blocksize of=$vdev seek=$off count=1 conv=notrunc
+}
+
+#
+# Given a number of files, this function will iterate through
+# the loop creating the specified number of files, whose names
+# will start with <basename>.
+#
+# The <data> argument is special: it can be "ITER", in which case
+# the -d argument will be the value of the current iteration. It
+# can be 0, in which case it will always be 0. Otherwise, it will
+# always be the given value.
+#
+# If <snapbase> is specified, a snapshot will be taken using the
+# argument as the snapshot basename.
+#
+function populate_dir # basename num_files write_count blocksz data snapbase
+{
+ typeset basename=$1
+ typeset -i num_files=$2
+ typeset -i write_count=$3
+ typeset -i blocksz=$4
+ typeset -i i
+ typeset data=$5
+ typeset snapbase="$6"
+
+ log_note "populate_dir: data='$data'"
+ for (( i = 0; i < num_files; i++ )); do
+ case "$data" in
+ 0) d=0 ;;
+ ITER) d=$i ;;
+ *) d=$data ;;
+ esac
+
+ log_must $FILE_WRITE -o create -c $write_count \
+ -f ${basename}.$i -b $blocksz -d $d
+
+ [ -n "$snapbase" ] && log_must $ZFS snapshot ${snapbase}.${i}
+ done
+}
+
+# Reap all children registered in $child_pids.
+function reap_children
+{
+ [ -z "$child_pids" ] && return
+ for wait_pid in $child_pids; do
+ log_must $KILL $wait_pid
+ done
+ child_pids=""
+}
+
+# Busy a path. Expects to be reaped via reap_children. Tries to run as
+# long and slowly as possible. [num] is taken as a hint; if such a file
+# already exists a different one will be chosen.
+function busy_path # <path> [num]
+{
+ typeset busypath=$1
+ typeset -i num=$2
+
+ while :; do
+ busyfile="$busypath/busyfile.${num}"
+ [ ! -f "$busyfile" ] && break
+ done
+
+ cmd="$DD if=/dev/urandom of=$busyfile bs=512"
+ ( cd $busypath && $cmd ) &
+ typeset pid=$!
+ $SLEEP 1
+ log_must $PS -p $pid
+ child_pids="$child_pids $pid"
+}
diff --git a/tests/sys/cddl/zfs/include/libtest_test.sh b/tests/sys/cddl/zfs/include/libtest_test.sh
new file mode 100755
index 000000000000..58ecdf95518c
--- /dev/null
+++ b/tests/sys/cddl/zfs/include/libtest_test.sh
@@ -0,0 +1,42 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2016 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+atf_test_case raidz_dva_to_block_addr
+raidz_dva_to_block_addr_head()
+{
+ atf_set "descr" "Unit tests for raidz_dva_to_block_addr"
+}
+raidz_dva_to_block_addr_body()
+{
+ . $(atf_get_srcdir)/default.cfg
+
+ # These test cases were determined by hand on an actual filesystem
+ atf_check_equal 3211 `raidz_dva_to_block_addr 0:3f40000:4000 3 13`
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case raidz_dva_to_block_addr
+}
diff --git a/tests/sys/cddl/zfs/include/logapi.kshlib b/tests/sys/cddl/zfs/include/logapi.kshlib
new file mode 100644
index 000000000000..8c238ea34f0a
--- /dev/null
+++ b/tests/sys/cddl/zfs/include/logapi.kshlib
@@ -0,0 +1,414 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+#
+# This is a ksh function library. It is intended to be sourced into
+# other ksh scripts and not executed directly.
+#
+
+. ${STF_SUITE}/include/stf.shlib
+
+#
+# Send a debug message to stderr, if $STF_DEBUG set.
+#
+function log_debug
+{
+ [ -z "$STF_DEBUG" ] && return
+ echo "$*" >&2
+}
+
+# Output an assertion
+#
+# $@ - assertion text
+
+function log_assert
+{
+ _printline ASSERTION: "$@"
+}
+
+# Output a comment
+#
+# $@ - comment text
+
+function log_note
+{
+ _printline NOTE: "$@"
+}
+
+# Execute a positive test and exit $STF_FAIL is test fails
+#
+# $@ - command to execute
+
+function log_must
+{
+ log_pos "$@"
+ (( $? != 0 )) && log_fail
+}
+
+# Execute a command that must exit $1
+#
+# $@ - command to execute
+function log_mustbe
+{
+ typeset exitcode_wanted=$1
+ shift
+
+ log_cmd "$@"
+ (( $? != $exitcode_wanted )) && log_fail
+}
+
+# Execute a negative test and exit $STF_FAIL if test passes
+#
+# $@ - command to execute
+
+function log_mustnot
+{
+ log_neg "$@"
+ (( $? != 0 )) && log_fail
+}
+
+# Execute a command that should only be logged if it fails.
+#
+# $@ - command to execute
+function log_onfail
+{
+ eval $@
+ typeset status=$?
+ [ $status -eq 0 ] && return
+ _printerror "$@" "unexpectedly exited $status"
+}
+
+# Execute and print command with status where success equals non-zero result
+# or output includes expected keyword
+#
+# $2-$@ - command to execute
+#
+# Summary: execute $@. Return 1 if any of the following hold:
+# 1) The command exited 0, 127, 138, or 139
+# 2) The command's stderr included "internal error" or
+# "assertion failed"
+#
+# return 0 if command fails, or the output contains the keyword expected,
+# return 1 otherwise
+
+function log_neg
+{
+ typeset out=""
+ typeset logfile="$TMPDIR/log.$$"
+ typeset ret=1
+
+ while [[ -e $logfile ]]; do
+ logfile="$logfile.$$"
+ done
+
+ "$@" 2>$logfile
+ typeset status=$?
+ out="/bin/cat $logfile"
+
+ # unexpected status
+ if (( $status == 0 )); then
+ print -u2 $($out)
+ _printerror "$@" "unexpectedly exited $status"
+ # missing binary
+ elif (( $status == 127 )); then
+ print -u2 $($out)
+ _printerror "$@" "unexpectedly exited $status (File not found)"
+ # bus error - core dump
+ elif (( $status == 138 )); then
+ print -u2 $($out)
+ _printerror "$@" "unexpectedly exited $status (Bus Error)"
+ # segmentation violation - core dump
+ elif (( $status == 139 )); then
+ print -u2 $($out)
+ _printerror "$@" "unexpectedly exited $status (SEGV)"
+ else
+ $out | /usr/bin/egrep -i "internal error|assertion failed" \
+ > /dev/null 2>&1
+ # internal error or assertion failed
+ if (( $? == 0 )); then
+ print -u2 $($out)
+ _printerror "$@" "internal error or assertion failure" \
+ " exited $status"
+ else
+ ret=0
+ fi
+
+ if (( $ret == 0 )); then
+ [[ -n $LOGAPI_DEBUG ]] && print $($out)
+ _printsuccess "$@" "exited $status"
+ fi
+ fi
+ _recursive_output $logfile "false"
+ return $ret
+}
+
+# Execute and print command; unconditionally return its exit code.
+# Useful for code that needs to do more specialized exit status filtering.
+function log_cmd
+{
+ typeset logfile="$TMPDIR/log.$$"
+
+ while [[ -e $logfile ]]; do
+ logfile="$logfile.$$"
+ done
+
+ "$@" 2>$logfile
+ typeset status=$?
+ _printline "EXECUTED (exited $status): $@"
+ _recursive_output $logfile "false"
+ return $status
+}
+
+# Execute and print command with status where success equals zero result
+#
+# $@ command to execute
+#
+# Summary: run $@. return 1 if its exit status was nonzero or if it printed
+# "internal error" or "assertion failed" to stderr.
+# print stderr on failure or if LOGAPI_DEBUG is set.
+#
+# return command exit status
+
+function log_pos
+{
+ typeset out=""
+ typeset logfile="$TMPDIR/log.$$"
+
+ while [[ -e $logfile ]]; do
+ logfile="$logfile.$$"
+ done
+
+ "$@" 2>$logfile
+ typeset status=$?
+ out="/bin/cat $logfile"
+
+ if (( $status != 0 )) ; then
+ print -u2 $($out)
+ _printerror "$@" "exited $status"
+ else
+ $out | /usr/bin/egrep -i "internal error|assertion failed" \
+ > /dev/null 2>&1
+ # internal error or assertion failed
+ if [[ $? -eq 0 ]]; then
+ print -u2 $($out)
+ _printerror "$@" "internal error or assertion failure" \
+ " exited $status"
+ status=1
+ else
+ [[ -n $LOGAPI_DEBUG ]] && print $($out)
+ _printsuccess "$@"
+ fi
+ fi
+ _recursive_output $logfile "false"
+ return $status
+}
+
+# Set an exit handler
+#
+# $@ - function(s) to perform on exit
+
+function log_onexit
+{
+ _CLEANUP="$@"
+}
+
+#
+# Exit functions
+#
+
+# Perform cleanup and exit $STF_PASS
+#
+# $@ - message text
+
+function log_pass
+{
+ _endlog $STF_PASS "$@"
+}
+
+# Perform cleanup and exit $STF_FAIL
+#
+# $@ - message text
+
+function log_fail
+{
+ _endlog $STF_FAIL "$@"
+}
+
+# Perform cleanup and exit $STF_UNRESOLVED
+#
+# $@ - message text
+
+function log_unresolved
+{
+ _endlog $STF_UNRESOLVED "$@"
+}
+
+# Perform cleanup and exit $STF_NOTINUSE
+#
+# $@ - message text
+
+function log_notinuse
+{
+ _endlog $STF_NOTINUSE "$@"
+}
+
+# Perform cleanup and exit $STF_UNSUPPORTED
+#
+# $@ - message text
+
+function log_unsupported
+{
+ _endlog $STF_UNSUPPORTED "$@"
+}
+
+# Perform cleanup and exit $STF_UNTESTED
+#
+# $@ - message text
+
+function log_untested
+{
+ _endlog $STF_UNTESTED "$@"
+}
+
+# Perform cleanup and exit $STF_UNINITIATED
+#
+# $@ - message text
+
+function log_uninitiated
+{
+ _endlog $STF_UNINITIATED "$@"
+}
+
+# Perform cleanup and exit $STF_NORESULT
+#
+# $@ - message text
+
+function log_noresult
+{
+ _endlog $STF_NORESULT "$@"
+}
+
+# Perform cleanup and exit $STF_WARNING
+#
+# $@ - message text
+
+function log_warning
+{
+ _endlog $STF_WARNING "$@"
+}
+
+# Perform cleanup and exit $STF_TIMED_OUT
+#
+# $@ - message text
+
+function log_timed_out
+{
+ _endlog $STF_TIMED_OUT "$@"
+}
+
+# Perform cleanup and exit $STF_OTHER
+#
+# $@ - message text
+
+function log_other
+{
+ _endlog $STF_OTHER "$@"
+}
+
+#
+# Internal functions
+#
+
+# Perform cleanup and exit
+#
+# Summary: Runs any cleanup routine registered with log_onexit. Prints a
+# message and exits $1. Note: the _recursive_output does
+# nothing, because the rest of this api guarantees that the
+# logfile will not exist.
+# $1 - stf exit code
+# $2-$n - message text
+
+function _endlog
+{
+ typeset logfile="$TMPDIR/log.$$"
+ _recursive_output $logfile
+
+ export STF_EXITCODE=$1
+ shift
+ (( ${#@} > 0 )) && _printline "$@"
+ if [[ -n $_CLEANUP ]] ; then
+ typeset cleanup=$_CLEANUP
+ log_onexit ""
+ log_note "Performing local cleanup via log_onexit ($cleanup)"
+ $cleanup
+ fi
+ exit $STF_EXITCODE
+}
+
+# Output a formatted line
+#
+# $@ - message text
+
+function _printline
+{
+ print `/bin/date +%H:%M:%S` "$@"
+}
+
+# Output an error message
+#
+# $@ - message text
+
+function _printerror
+{
+ _printline ERROR: "$@"
+}
+
+# Output a success message
+#
+# $@ - message text
+
+function _printsuccess
+{
+ _printline SUCCESS: "$@"
+}
+
+# Output logfiles recursively
+#
+# $1 - start file
+# $2 - indicate whether output the start file itself, default as yes.
+
+function _recursive_output #logfile
+{
+ typeset logfile=$1
+
+ while [[ -e $logfile ]]; do
+ if [[ -z $2 || $logfile != $1 ]]; then
+ /bin/cat $logfile
+ fi
+ /bin/rm -f $logfile
+ logfile="$logfile.$$"
+ done
+}
diff --git a/tests/sys/cddl/zfs/include/stf.shlib b/tests/sys/cddl/zfs/include/stf.shlib
new file mode 100644
index 000000000000..4bf6213c4178
--- /dev/null
+++ b/tests/sys/cddl/zfs/include/stf.shlib
@@ -0,0 +1,50 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+STF_PASS=0
+STF_FAIL=1
+STF_UNRESOLVED=2
+STF_NOTINUSE=3
+STF_UNSUPPORTED=4
+STF_UNTESTED=5
+STF_UNINITIATED=6
+STF_NORESULT=7
+STF_WARNING=8
+STF_TIMED_OUT=9
+STF_OTHER=10
+
+# do this to use the names: eval echo \$STF_RESULT_NAME_${result}
+STF_RESULT_NAME_0="PASS"
+STF_RESULT_NAME_1="FAIL"
+STF_RESULT_NAME_2="UNRESOLVED"
+STF_RESULT_NAME_3="NOTINUSE"
+STF_RESULT_NAME_4="UNSUPPORTED"
+STF_RESULT_NAME_5="UNTESTED"
+STF_RESULT_NAME_6="UNINITIATED"
+STF_RESULT_NAME_7="NORESULT"
+STF_RESULT_NAME_8="WARNING"
+STF_RESULT_NAME_9="TIMED_OUT"
+STF_RESULT_NAME_10="OTHER"
diff --git a/tests/sys/cddl/zfs/include/testenv.ksh b/tests/sys/cddl/zfs/include/testenv.ksh
new file mode 100644
index 000000000000..fb464f2feaec
--- /dev/null
+++ b/tests/sys/cddl/zfs/include/testenv.ksh
@@ -0,0 +1,19 @@
+. ${STF_SUITE}/include/libtest.kshlib
+. ${STF_SUITE}/include/commands.cfg
+
+# Environment-dependent constants.
+for d in `geom disk list | awk '/Name:/ {print $3}'`; do
+ # Clear the GPT label first to avoid spurious create failures.
+ gpart destroy -F $d >/dev/null 2>&1
+ if gpart create -s gpt $d >/dev/null 2>&1 ; then
+ gpart destroy $d >/dev/null 2>&1 || continue
+ DISKS=("${DISKS[@]}" "/dev/$d") #"$DISKS $d"
+ fi
+ # Don't bother testing any more if we have enough already.
+ # Currently we use at most 5 disks plus 1 for temporary disks.
+ [ ${#DISKS[@]} -eq 6 ] && break
+done
+export KEEP="$(zpool list -H -o name)"
+
+# Pull in constants.
+. ${STF_SUITE}/include/constants.cfg
diff --git a/tests/sys/cddl/zfs/include/testenv.kshlib b/tests/sys/cddl/zfs/include/testenv.kshlib
new file mode 100644
index 000000000000..5d35954fc73c
--- /dev/null
+++ b/tests/sys/cddl/zfs/include/testenv.kshlib
@@ -0,0 +1,21 @@
+# vim: filetype=sh
+
+. ${STF_SUITE}/include/libtest.kshlib
+. ${STF_SUITE}/include/commands.cfg
+
+# Environment-dependent constants.
+for d in `geom disk list | awk '/Name:/ {print $3}'`; do
+ # Clear the GPT label first to avoid spurious create failures.
+ gpart destroy -F $d >/dev/null 2>&1
+ if gpart create -s gpt $d >/dev/null 2>&1 ; then
+ gpart destroy $d >/dev/null 2>&1 || continue
+ DISKS=("${DISKS[@]}" "/dev/$d") #"$DISKS $d"
+ fi
+ # Don't bother testing any more if we have enough already.
+ # Currently we use at most 5 disks plus 1 for temporary disks.
+ [ ${#DISKS[@]} -eq 6 ] && break
+done
+export KEEP="$(zpool list -H -o name)"
+
+# Pull in constants.
+. ${STF_SUITE}/include/constants.cfg
diff --git a/tests/sys/cddl/zfs/include/translatecommands.awk b/tests/sys/cddl/zfs/include/translatecommands.awk
new file mode 100644
index 000000000000..e3f1931e9fc2
--- /dev/null
+++ b/tests/sys/cddl/zfs/include/translatecommands.awk
@@ -0,0 +1,39 @@
+#! /usr/bin/awk -f
+
+BEGIN {
+ print "# This file is autogenerated from commands.txt. Do not edit"
+ cmdstring = "export CMDS=\""
+ cmdch = 1
+ cmd_idx=0
+}
+
+# Strip comments
+{
+ gsub(/#.*/, "", $0)
+}
+
+# Strip blank lines
+/^[ ]*$/ {
+ next
+}
+
+# Process remaining lines
+{
+ gsub(/%%STFSUITEDIR%%/, stfsuitedir, $1)
+ fullcmd = $1
+ cmdname = $1
+ gsub(/.*\//, "", cmdname)
+ CMDNAME = toupper(cmdname)
+ allcmds[cmd_idx] = CMDNAME
+ cmd_idx += 1
+ printf "export %s=\"%s\"\n", CMDNAME, fullcmd
+}
+
+# Print CMDS
+END {
+ print ""
+ printf "export CMDS=\""
+ for (idx in allcmds)
+ printf "$%s ", allcmds[idx]
+ print "\""
+}
diff --git a/tests/sys/cddl/zfs/tests/Makefile b/tests/sys/cddl/zfs/tests/Makefile
new file mode 100644
index 000000000000..f007e78992fc
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/Makefile
@@ -0,0 +1,87 @@
+.include <src.opts.mk>
+
+PACKAGE= tests
+TESTSDIR= ${TESTSBASE}/sys/cddl/zfs/tests
+
+TESTS_SUBDIRS+= acl
+TESTS_SUBDIRS+= atime
+TESTS_SUBDIRS+= bootfs
+TESTS_SUBDIRS+= cache
+TESTS_SUBDIRS+= cachefile
+TESTS_SUBDIRS+= clean_mirror
+TESTS_SUBDIRS+= cli_root
+TESTS_SUBDIRS+= cli_user
+TESTS_SUBDIRS+= compression
+TESTS_SUBDIRS+= ctime
+TESTS_SUBDIRS+= delegate
+TESTS_SUBDIRS+= devices
+TESTS_SUBDIRS+= exec
+TESTS_SUBDIRS+= grow_pool
+TESTS_SUBDIRS+= grow_replicas
+TESTS_SUBDIRS+= history
+TESTS_SUBDIRS+= hotplug
+TESTS_SUBDIRS+= hotspare
+TESTS_SUBDIRS+= inheritance
+# Not yet ported to FreeBSD
+# TESTS_SUBDIRS+= interop
+TESTS_SUBDIRS+= inuse
+# Not yet ported to FreeBSD
+# TESTS_SUBDIRS+= iscsi
+TESTS_SUBDIRS+= large_files
+# Not yet ported to FreeBSD
+# TESTS_SUBDIRS+= largest_pool
+# link_count is not yet ported to FreeBSD. I'm not sure what its purpose is.
+# The assertion message contradicts with the log_fail message.
+# TESTS_SUBDIRS+= link_count
+TESTS_SUBDIRS+= migration
+TESTS_SUBDIRS+= mmap
+TESTS_SUBDIRS+= mount
+TESTS_SUBDIRS+= mv_files
+TESTS_SUBDIRS+= nestedfs
+TESTS_SUBDIRS+= no_space
+TESTS_SUBDIRS+= online_offline
+TESTS_SUBDIRS+= pool_names
+TESTS_SUBDIRS+= poolversion
+TESTS_SUBDIRS+= quota
+TESTS_SUBDIRS+= redundancy
+TESTS_SUBDIRS+= refquota
+TESTS_SUBDIRS+= refreserv
+# Broken on every OS
+# TESTS_SUBDIRS+= rename_dirs
+TESTS_SUBDIRS+= replacement
+TESTS_SUBDIRS+= reservation
+TESTS_SUBDIRS+= rootpool
+# Not yet ported to FreeBSD
+# TESTS_SUBDIRS+= rsend
+TESTS_SUBDIRS+= scrub_mirror
+TESTS_SUBDIRS+= slog
+TESTS_SUBDIRS+= snapshot
+TESTS_SUBDIRS+= snapused
+TESTS_SUBDIRS+= sparse
+TESTS_SUBDIRS+= threadsappend
+TESTS_SUBDIRS+= truncate
+TESTS_SUBDIRS+= txg_integrity
+TESTS_SUBDIRS+= userquota
+TESTS_SUBDIRS+= utils_test
+TESTS_SUBDIRS+= write_dirs
+# Not yet ported to FreeBSD
+# TESTS_SUBDIRS+= xattr
+TESTS_SUBDIRS+= zfsd
+TESTS_SUBDIRS+= zil
+# Not yet ported to FreeBSD
+# TESTS_SUBDIRS+= zinject
+# Not yet ported to FreeBSD
+# TESTS_SUBDIRS+= zones
+TESTS_SUBDIRS+= zvol
+TESTS_SUBDIRS+= zvol_thrash
+
+# This is primarily useful for identifying which test a testid corresponds to.
+# Sometimes all you might have is a pool name like 'testpool.1316'.
+testids:
+ for i in `find ${.CURDIR} -name '*.sh' | xargs grep '^atf_test_case '|awk '{print $$2}'`; do \
+ echo "$${i}: $$(echo $$i | cksum -o 2 | cut -d" " -f1)"; \
+ done
+
+.PHONY: testids
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/Makefile.inc b/tests/sys/cddl/zfs/tests/Makefile.inc
new file mode 100644
index 000000000000..1b911c451c01
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/Makefile.inc
@@ -0,0 +1 @@
+WARNS?= 0
diff --git a/tests/sys/cddl/zfs/tests/acl/Makefile b/tests/sys/cddl/zfs/tests/acl/Makefile
new file mode 100644
index 000000000000..95cbc40972e5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/Makefile
@@ -0,0 +1,16 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/acl
+FILESDIR=${TESTSDIR}
+
+${PACKAGE}FILES+= acl.cfg
+${PACKAGE}FILES+= acl_common.kshlib
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= setup.ksh
+
+TESTS_SUBDIRS+= cifs
+TESTS_SUBDIRS+= trivial
+TESTS_SUBDIRS+= nontrivial
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/acl/acl.cfg b/tests/sys/cddl/zfs/tests/acl/acl.cfg
new file mode 100644
index 000000000000..5f2f2eed1ddf
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/acl.cfg
@@ -0,0 +1,67 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+export NISSTAFILE=$TMPDIR/nis_state
+export TESTFILE=testfile${TESTCASE_ID}
+export TESTFILE0=testfile0.${TESTCASE_ID}
+export TESTFILE2=testfile2.${TESTCASE_ID}
+
+# Define super user 'admin'
+export ZFS_ACL_ADMIN=admin
+
+export ZFS_ACL_STAFF_GROUP=zfsgrp
+export ZFS_ACL_STAFF1=staff1
+export ZFS_ACL_STAFF2=staff2
+
+export ZFS_ACL_OTHER_GROUP=othergrp
+export ZFS_ACL_OTHER1=other1
+export ZFS_ACL_OTHER2=other2
+
+# Define the current user who run 'usr_exec'
+export ZFS_ACL_CUR_USER=""
+
+# Define global error string
+export ZFS_ACL_ERR_STR=""
+
+# Define test file and test directory which will be operated by chmod
+export testfile=$TESTDIR/testfile
+export testdir=$TESTDIR/testdir
+
+# Define several directories for trivial ACLs function test.
+export RES_DIR=$TESTDIR/RES
+export INI_DIR=$TESTDIR/INIT
+export TST_DIR=$TESTDIR/TEST
+export TMP_DIR=$TESTDIR/TMP
+
+# Define test files and their attributes files number for trivial
+# ACLs function test
+export NUM_FILE=5
+export NUM_ATTR=10
+
+# Enlarge STF_TIMEOUT
+export STF_TIMEOUT=1800
diff --git a/tests/sys/cddl/zfs/tests/acl/acl_common.kshlib b/tests/sys/cddl/zfs/tests/acl/acl_common.kshlib
new file mode 100644
index 000000000000..504acbd5c14a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/acl_common.kshlib
@@ -0,0 +1,633 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+# FreeBSD doesn't support ZFS extended attributes. It also doesn't support the
+# same ACL mechanisms Solaris does for testing.
+if [[ $os_name != "FreeBSD" ]]; then
+ export ZFS_XATTR="true"
+ export ZFS_ACL="true"
+else
+ log_note "On FreeBSD most xattr and ACL tests are disabled"
+fi
+
+#
+# Get the given file/directory access mode
+#
+# $1 object -- file or directroy
+#
+function get_mode #<obj>
+{
+ typeset obj=$1
+ if (( ${#obj} == 0 )); then
+ return 1
+ fi
+
+ $LS -ld $obj | $AWK '{print $1}'
+}
+
+#
+# Get the given file/directory ACL
+#
+# $1 object -- file or directroy
+#
+function get_acl #<obj>
+{
+ typeset obj=$1
+ if (( ${#obj} == 0 )); then
+ return 1
+ fi
+
+ $LS -vd $obj | $NAWK '(NR != 1) {print $0}'
+}
+
+#
+# Get the given file/directory ACL
+#
+# $1 object -- file or directroy
+#
+function get_compact_acl #<obj>
+{
+ typeset obj=$1
+ if (( ${#obj} == 0 )); then
+ return 1
+ fi
+
+ $LS -Vd $obj | $NAWK '(NR != 1) {print $0}'
+}
+
+#
+# Check the given two files/directories have the same ACLs
+#
+# Return 0, if source object acl is equal to target object acl.
+#
+# $1 source object
+# $2 target object
+#
+function compare_acls #<src> <tgt>
+{
+ typeset src=$1
+ typeset tgt=$2
+
+ (( ${#src} == 0 || ${#tgt} == 0 )) && return 1
+ [[ $src == $tgt ]] && return 0
+
+ typeset tmpsrc=$TMPDIR/compare_acls.src.${TESTCASE_ID}
+ typeset tmptgt=$TMPDIR/compare_acls.tgt.${TESTCASE_ID}
+
+ get_acl $src > $tmpsrc
+ get_acl $tgt > $tmptgt
+ typeset -i ret=0
+ $DIFF $tmpsrc $tmptgt > /dev/null 2>&1
+ ret=$?
+ $RM -f $tmpsrc $tmptgt
+
+ if (( ret != 0 )); then
+ return $ret
+ fi
+
+ get_compact_acl $src > $tmpsrc
+ get_compact_acl $tgt > $tmptgt
+ $DIFF $tmpsrc $tmptgt > /dev/null 2>&1
+ ret=$?
+ $RM -f $tmpsrc $tmptgt
+
+ return $ret
+}
+
+#
+# Check that the given two objects have the same modes.
+# Return 0, if their modes are equal with each other. Otherwise, return 1.
+#
+# $1 source object
+# $2 target object
+#
+function compare_modes #<src> <tgt>
+{
+ typeset src=$1
+ typeset tgt=$2
+ typeset -i i=0
+ set -A mode
+
+ (( ${#src} == 0 || ${#tgt} == 0 )) && return 1
+ [[ $src == $tgt ]] && return 0
+
+ typeset obj
+ for obj in $src $tgt
+ do
+ mode[i]=$(get_mode $obj)
+
+ (( i = i + 1 ))
+ done
+
+ [[ ${mode[0]} != ${mode[1]} ]] && return 1
+
+ return 0
+}
+
+#
+# Check that the given two objects have the same xattrs.
+# Return 0, if their xattrs are equal with each other. Otherwise, return 1.
+#
+# $1 source object
+# $2 target object
+#
+function compare_xattrs #<src> <tgt>
+{
+ typeset src=$1
+ typeset tgt=$2
+
+ (( ${#src} == 0 || ${#tgt} == 0 )) && return 1
+ [[ $src == $tgt ]] && return 0
+
+ typeset tmpsrc=$TMPDIR/compare_xattrs.src.${TESTCASE_ID}
+ typeset tmptgt=$TMPDIR/compare_xattrs.tgt.${TESTCASE_ID}
+
+ get_xattr $src > $tmpsrc
+ get_xattr $tgt > $tmptgt
+ typeset -i ret=0
+ $DIFF $tmpsrc $tmptgt > /dev/null 2>&1
+ ret=$?
+ $RM -f $tmpsrc $tmptgt
+
+ return $ret
+}
+
+#
+# Check '+' is set for a given file/directory with 'ls [-l]' command
+#
+# $1 object -- file or directory.
+#
+function plus_sign_check_l #<obj>
+{
+ typeset obj=$1
+ if (( ${#obj} == 0 )); then
+ return 1
+ fi
+
+ $LS -ld $obj | $AWK '{print $1}' | $GREP "+\>" > /dev/null
+
+ return $?
+}
+
+#
+# Check '+' is set for a given file/directory with 'ls [-v]' command
+#
+# $1 object -- file or directory.
+#
+function plus_sign_check_v #<obj>
+{
+ typeset obj=$1
+ if (( ${#obj} == 0 )); then
+ return 1
+ fi
+
+ $LS -vd $obj | $NAWK '(NR == 1) {print $1}' | $GREP "+\>" > /dev/null
+
+ return $?
+}
+
+#
+# A wrapper function of c program
+#
+# $1 legal login name
+# $2-n commands and options
+#
+function chgusr_exec #<login_name> <commands> [...]
+{
+ $CHG_USR_EXEC $@
+ return $?
+}
+
+#
+# Export the current user for the following usr_exec operating.
+#
+# $1 legal login name
+#
+function set_cur_usr #<login_name>
+{
+ export ZFS_ACL_CUR_USER=$1
+}
+
+#
+# Run commands by $ZFS_ACL_CUR_USER
+#
+# $1-n commands and options
+#
+function usr_exec #<commands> [...]
+{
+ $CHG_USR_EXEC "$ZFS_ACL_CUR_USER" $@
+ return $?
+}
+
+#
+# Count how many ACEs for the specified file or directory.
+#
+# $1 file or directroy name
+#
+function count_ACE #<file or dir name>
+{
+ if [[ ! -e $1 ]]; then
+ log_note "Need input file or directroy name."
+ return 1
+ fi
+
+ $LS -vd $1 | $NAWK 'BEGIN {count=0}
+ (NR != 1)&&(/[0-9]:/) {count++}
+ END {print count}'
+
+ return 0
+}
+
+#
+# Get specified number ACE content of specified file or directory.
+#
+# $1 file or directory name
+# $2 specified number
+#
+function get_ACE #<file or dir name> <specified number> <verbose|compact>
+{
+ if [[ ! -e $1 || $2 -ge $(count_ACE $1) ]]; then
+ return 1
+ fi
+
+ typeset file=$1
+ typeset -i num=$2
+ typeset format=${3:-verbose}
+ typeset -i next_num=-1
+
+ typeset tmpfile=$TMPDIR/tmp_get_ACE.${TESTCASE_ID}
+ typeset line=""
+ typeset args
+
+ case $format in
+ verbose) args="-vd"
+ ;;
+ compact) args="-Vd"
+ ;;
+ *) log_fail "Invalid parameter as ($format), " \
+ "only verbose|compact is supported."
+ ;;
+ esac
+
+ $LS $args $file > $tmpfile
+ (( $? != 0 )) && log_fail "FAIL: $LS $args $file > $tmpfile"
+ while read line; do
+ [[ -z $line ]] && continue
+ if [[ $args == -vd ]]; then
+ if [[ $line == "$num":* ]]; then
+ (( next_num = num + 1 ))
+ fi
+ if [[ $line == "$next_num":* ]]; then
+ break
+ fi
+ if (( next_num != -1 )); then
+ print -n $line
+ fi
+ else
+ if (( next_num == num )); then
+ print -n $line
+ fi
+ (( next_num += 1 ))
+ fi
+ done < $tmpfile
+
+ $RM -f $tmpfile
+ (( $? != 0 )) && log_fail "FAIL: $RM -f $tmpfile"
+}
+
+#
+# Cleanup exist user/group.
+#
+function cleanup_user_group
+{
+ del_user $ZFS_ACL_ADMIN
+
+ del_user $ZFS_ACL_STAFF1
+ del_user $ZFS_ACL_STAFF2
+ del_group $ZFS_ACL_STAFF_GROUP
+
+ del_user $ZFS_ACL_OTHER1
+ del_user $ZFS_ACL_OTHER2
+ del_group $ZFS_ACL_OTHER_GROUP
+
+ return 0
+}
+
+#
+# Clean up testfile and test directory
+#
+function cleanup
+{
+ if [[ -d $TESTDIR ]]; then
+ cd $TESTDIR
+ $RM -rf $TESTDIR/*
+ fi
+}
+
+#
+# According to specified access or acl_spec, do relevant operating by using the
+# specified user.
+#
+# $1 specified user
+# $2 node
+# $3 acl_spec or access
+#
+function rwx_node #user node acl_spec|access
+{
+ typeset user=$1
+ typeset node=$2
+ typeset acl_spec=$3
+
+ if [[ $user == "" || $node == "" || $acl_spec == "" ]]; then
+ log_note "node or acl_spec are not defined."
+ return 1
+ fi
+
+ if [[ -d $node ]]; then
+ case $acl_spec in
+ *:read_data:*|read_data)
+ chgusr_exec $user $LS -l $node > /dev/null 2>&1
+ return $? ;;
+ *:write_data:*|write_data)
+ if [[ -f ${node}/tmpfile ]]; then
+ log_must $RM -f ${node}/tmpfile
+ fi
+ chgusr_exec $user $TOUCH ${node}/tmpfile > \
+ /dev/null 2>&1
+ return $? ;;
+ *"execute:"*|execute)
+ chgusr_exec $user $FIND $node > /dev/null 2>&1
+ return $? ;;
+ esac
+ else
+ case $acl_spec in
+ *:read_data:*|read_data)
+ chgusr_exec $user $CAT $node > /dev/null 2>&1
+ return $? ;;
+ *:write_data:*|write_data)
+ chgusr_exec $user $DD if=/bin/ls of=$node > \
+ /dev/null 2>&1
+ return $? ;;
+ *"execute:"*|execute)
+ ZFS_ACL_ERR_STR=$(chgusr_exec $user $node 2>&1)
+ return $? ;;
+ esac
+ fi
+}
+
+#
+# Get the given file/directory xattr
+#
+# $1 object -- file or directroy
+#
+function get_xattr #<obj>
+{
+ typeset obj=$1
+ typeset xattr
+ if (( ${#obj} == 0 )); then
+ return 1
+ fi
+
+ for xattr in `$RUNAT $obj $LS | \
+ /usr/bin/egrep -v -e SUNWattr_ro -e SUNWattr_rw` ; do
+ $RUNAT $obj $SUM $xattr
+ done
+}
+
+#
+# Get the owner of a file/directory
+#
+function get_owner #node
+{
+ typeset node=$1
+ typeset value
+
+ if [[ -z $node ]]; then
+ log_fail "node are not defined."
+ fi
+
+ if [[ -d $node ]]; then
+ value=$($LS -dl $node | $AWK '{print $3}')
+ elif [[ -e $node ]]; then
+ value=$($LS -l $node | $AWK '{print $3}')
+ fi
+
+ $ECHO $value
+}
+
+#
+# Get the group of a file/directory
+#
+function get_group #node
+{
+ typeset node=$1
+ typeset value
+
+ if [[ -z $node ]]; then
+ log_fail "node are not defined."
+ fi
+
+ if [[ -d $node ]]; then
+ value=$($LS -dl $node | $AWK '{print $4}')
+ elif [[ -e $node ]]; then
+ value=$($LS -l $node | $AWK '{print $4}')
+ fi
+
+ $ECHO $value
+}
+
+
+#
+# Get the group name that a UID belongs to
+#
+function get_user_group #uid
+{
+ typeset uid=$1
+ typeset value
+
+ if [[ -z $uid ]]; then
+ log_fail "UID not defined."
+ fi
+
+ value=$(id $uid)
+
+ if [[ $? -eq 0 ]]; then
+ value=${value##*\(}
+ value=${value%%\)*}
+ $ECHO $value
+ else
+ log_fail "Invalid UID (uid)."
+ fi
+}
+
+#
+# Get the specified item of the specified string
+#
+# $1: Item number, count from 0.
+# $2-n: strings
+#
+function getitem
+{
+ typeset -i n=$1
+ shift
+
+ (( n += 1 ))
+ eval print \${$n}
+}
+
+#
+# This function calculate the specified directory files checksum and write
+# to the specified array.
+#
+# $1 directory in which the files will be cksum.
+# $2 file array name which was used to store file cksum information.
+# $3 attribute array name which was used to store attribute information.
+#
+function cksum_files #<dir> <file_array_name> <attribute_array_name>
+{
+ typeset dir=$1
+ typeset farr_name=$2
+ typeset aarr_name=$3
+
+ [[ ! -d $dir ]] && return
+ typeset oldpwd=$PWD
+ cd $dir
+ typeset files=$($LS file*)
+
+ typeset -i i=0
+ typeset -i n=0
+ while (( i < NUM_FILE )); do
+ typeset f=$(getitem $i $files)
+ eval $farr_name[$i]=\$\(\$CKSUM $f\)
+
+ typeset -i j=0
+ while (( j < NUM_ATTR )); do
+ eval $aarr_name[$n]=\$\(\$RUNAT \$f \$CKSUM \
+ attribute.$j\)
+
+ (( j += 1 ))
+ (( n += 1 ))
+ done
+
+ (( i += 1 ))
+ done
+
+ cd $oldpwd
+}
+
+#
+# This function compare two cksum results array.
+#
+# $1 The array name which stored the cksum before operation.
+# $2 The array name which stored the cksum after operation.
+#
+function compare_cksum #<array1> <array2>
+{
+ typeset before=$1
+ typeset after=$2
+ eval typeset -i count=\${#$before[@]}
+
+ typeset -i i=0
+ while (( i < count )); do
+ eval typeset var1=\${$before[$i]}
+ eval typeset var2=\${$after[$i]}
+
+ if [[ $var1 != $var2 ]]; then
+ return 1
+ fi
+
+ (( i += 1 ))
+ done
+
+ return 0
+}
+
+#
+# This function calculate all the files cksum information in current directory
+# and output them to the specified file.
+#
+# $1 directory from which the files will be cksum.
+# $2 cksum output file
+#
+function record_cksum #<outfile>
+{
+ typeset dir=$1
+ typeset outfile=$2
+
+ [[ ! -d ${outfile%/*} ]] && usr_exec $MKDIR -p ${outfile%/*}
+
+ usr_exec cd $dir ; $FIND . -depth -type f -exec cksum {} \\\; | $SORT > $outfile
+ usr_exec cd $dir ; $FIND . -depth -type f -xattr -exec runat {} \
+ cksum attribute* \\\; | $SORT >> $outfile
+}
+
+#
+# The function create_files creates the directories and files that the script
+# will operate on to test extended attribute functionality.
+#
+# $1 The base directory in which to create directories and files.
+#
+function create_files #<directory>
+{
+ typeset basedir=$1
+
+ [[ ! -d $basedir ]] && usr_exec $MKDIR -m 777 $basedir
+ [[ ! -d $RES_DIR ]] && usr_exec $MKDIR -m 777 $RES_DIR
+ [[ ! -d $INI_DIR ]] && usr_exec $MKDIR -m 777 $INI_DIR
+ [[ ! -d $TST_DIR ]] && usr_exec $MKDIR -m 777 $TST_DIR
+ [[ ! -d $TMP_DIR ]] && usr_exec $MKDIR -m 777 $TMP_DIR
+
+ #
+ # Create the original file and its attribute files.
+ #
+ [[ ! -a $RES_DIR/file ]] && \
+ usr_exec $FILE_WRITE -o create -f $RES_DIR/file \
+ -b 1024 -d 0 -c 1
+ [[ ! -a $RES_DIR/attribute ]] && \
+ usr_exec $CP $RES_DIR/file $RES_DIR/attribute
+
+ typeset oldpwd=$PWD
+ cd $INI_DIR
+
+ typeset -i i=0
+ while (( i < NUM_FILE )); do
+ typeset dstfile=$INI_DIR/file.${TESTCASE_ID}.$i
+ usr_exec $CP $RES_DIR/file $dstfile
+
+ typeset -i j=0
+ while (( j < NUM_ATTR )); do
+ usr_exec $RUNAT $dstfile \
+ $CP $RES_DIR/attribute ./attribute.$j
+ (( j += 1 ))
+ done
+
+ (( i += 1 ))
+ done
+
+ cd $oldpwd
+}
diff --git a/tests/sys/cddl/zfs/tests/acl/cifs/Makefile b/tests/sys/cddl/zfs/tests/acl/cifs/Makefile
new file mode 100644
index 000000000000..bbb54429ec2c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/cifs/Makefile
@@ -0,0 +1,16 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/acl/cifs
+FILESDIR=${TESTSDIR}
+
+${PACKAGE}FILES+= cifs.kshlib
+${PACKAGE}FILES+= cifs_attr_001_pos.ksh
+${PACKAGE}FILES+= cifs_attr_002_pos.ksh
+${PACKAGE}FILES+= cifs_attr_003_pos.ksh
+
+ATF_TESTS_KSH93+= cifs_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/acl/cifs/cifs.kshlib b/tests/sys/cddl/zfs/tests/acl/cifs/cifs.kshlib
new file mode 100644
index 000000000000..462d160e26f7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/cifs/cifs.kshlib
@@ -0,0 +1,86 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+function cifs_supported
+{
+ if check_version "5.11" ; then
+ fs_prop_exist "sharesmb"
+ return $?
+ fi
+ return 1
+}
+
+#
+# Create a file or direcotry
+#
+# $1: The type specified, "file" or "dir"
+# $2: The given node name
+# $3: Owner of the node
+#
+function create_object
+{
+ typeset type=$1
+ typeset object=$2
+ typeset owner=$3
+
+ destroy_object $object
+
+ case $type in
+ dir)
+ $MKDIR -p $object
+ ;;
+ file)
+ $ECHO "ZFS test suites" > $object
+ ;;
+ esac
+
+ if [[ -n $owner ]]; then
+ $CHOWN $owner $object
+ fi
+ return 0
+}
+
+#
+# Destroy the given node(s)
+#
+# $@: The node(s) need to be destroyed
+#
+function destroy_object
+{
+ for object in $@ ; do
+ if [[ -e $object ]]; then
+
+ # clear_attribute is a common function name,
+ # but each case should have their own implement.
+ log_must clear_attribute $object
+ log_must $RM -rf $object
+ fi
+ done
+ return 0
+}
+
diff --git a/tests/sys/cddl/zfs/tests/acl/cifs/cifs_attr_001_pos.ksh b/tests/sys/cddl/zfs/tests/acl/cifs/cifs_attr_001_pos.ksh
new file mode 100644
index 000000000000..71d04d5c7a17
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/cifs/cifs_attr_001_pos.ksh
@@ -0,0 +1,257 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+. $STF_SUITE/tests/acl/cifs/cifs.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: cifs_attr_001_pos
+#
+# DESCRIPTION:
+# Verify the user with write_attributes permission or
+# PRIV_FILE_OWNER privilege could set/clear DOS attributes.
+# (Readonly, Hidden, Archive, System)
+#
+# STRATEGY:
+# 1. Loop super user and non-super user to run the test case.
+# 2. Create basedir and a set of subdirectores and files within it.
+# 3. Grant user has write_attributes permission or
+# PRIV_FILE_OWNER privilege
+# 4. Verify set/clear DOS attributes should succeed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-11-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+if ! cifs_supported ; then
+ log_unsupported "CIFS not supported on current system."
+fi
+
+test_requires ZFS_ACL ZFS_XATTR
+
+function cleanup
+{
+ for fs in $TESTPOOL/$TESTFS $TESTPOOL ; do
+ mtpt=$(get_prop mountpoint $fs)
+ log_must $RM -rf $mtpt/file.* $mtpt/dir.*
+ done
+}
+
+#
+# Set the special attribute to the given node
+#
+# $1: The given node (file/dir)
+# $2: The special attribute to be set
+# $3: Execute username
+#
+function set_attribute
+{
+ typeset object=$1
+ typeset attr=${2:-AHRS}
+ typeset user=$3
+ typeset ret=0
+
+ if [[ -z $object ]]; then
+ log_fail "Object not defined."
+ fi
+
+ if [[ -n $user ]]; then
+ $RUNWATTR -u $user "$CHMOD S+c${attr} $object"
+ ret=$?
+ else
+ $CHMOD S+c${attr} $object
+ ret=$?
+ fi
+
+ return $ret
+}
+
+#
+# Clear the special attribute to the given node
+#
+# $1: The given node (file/dir)
+# $2: The special attribute to be cleared
+# $3: Execute username
+#
+function clear_attribute
+{
+ typeset object=$1
+ typeset attr=${2:-AHRS}
+ typeset user=$3
+ typeset ret=0
+
+ if [[ -z $object ]]; then
+ log_fail "Object not defined."
+ fi
+
+ if [[ -n $user ]]; then
+ $RUNWATTR -u $user "$CHMOD S-c${attr} $object"
+ ret=$?
+ else
+ $CHMOD S-c${attr} $object
+ ret=$?
+ fi
+
+ return $ret
+}
+
+#
+# Grant the ace of write_attributes to the given user
+#
+# $1: The given user
+# $2: The given node (file/dir)
+#
+function grant_attr
+{
+ typeset user=$1
+ typeset object=$2
+
+ if [[ -z $user || -z $object ]]; then
+ log_fail "User($user), Object($object) not defined."
+ fi
+
+ # To increase the coverage, here we set 'deny' against
+ # superuser and owner.
+ # Only grant the user explicitly while it's not root neither owner.
+
+ if [[ $user == "root" ]]; then
+ log_must chmod A+user:root:write_attributes:deny $object
+ elif [[ $user == $(get_owner $object) ]]; then
+ if (( ( RANDOM % 2 ) == 0 )); then
+ log_must chmod A+owner@:write_attributes:deny $object
+ else
+ log_must chmod A+user:$user:write_attributes:deny \
+ $object
+ fi
+ else
+ log_must chmod A+user:$user:write_attributes:allow $object
+ fi
+ attr_mod="write_attributes"
+}
+
+#
+# Revoke the ace of write_attributes from the given user
+#
+# $1: The given user
+# $2: The given node (file/dir)
+#
+function revoke_attr
+{
+ typeset user=$1
+ typeset object=$2
+
+ if [[ -z $user || -z $object ]]; then
+ log_fail "User($user), Object($object) not defined."
+ fi
+
+ log_must chmod A0- $object
+ attr_mod=
+}
+
+#
+# Invoke the function and verify whether its return code as expected
+#
+# $1: Function be invoked
+# $2: The given node (file/dir)
+# $3: Execute user
+# $4: Option
+#
+function verify_attr
+{
+ typeset func=$1
+ typeset object=$2
+ typeset opt=$3
+ typeset user=$4
+ typeset expect="log_mustnot"
+
+ if [[ -z $func || -z $object ]]; then
+ log_fail "Func($func), Object($object), User($user), \
+ Opt($opt) not defined."
+ fi
+
+ # If user is superuser or has write_attributes permission or
+ # PRIV_FILE_OWNER privilege, it should log_must,
+ # otherwise log_mustnot.
+
+ if [[ -z $user || $user == "root" || \
+ $user == $(get_owner $object) || \
+ $attr_mod == *"write_attributes"* ]] ; then
+ expect="log_must"
+ fi
+
+ $expect $func $object $opt $user
+}
+
+log_assert "Verify set/clear DOS attributes will succeed while user has " \
+ "write_attributes permission or PRIV_FILE_OWNER privilege"
+log_onexit cleanup
+
+file="file.0"
+dir="dir.0"
+XATTROPTIONS="H S R A"
+
+for fs in $TESTPOOL $TESTPOOL/$TESTFS ; do
+ mtpt=$(get_prop mountpoint $fs)
+ for owner in root $ZFS_ACL_STAFF1 ; do
+
+ create_object "file" $mtpt/$file $owner
+ create_object "dir" $mtpt/$dir $owner
+
+ for object in $mtpt/$file $mtpt/$dir ; do
+ for user in root $ZFS_ACL_STAFF2 ; do
+ for opt in $XATTROPTIONS ; do
+ verify_attr set_attribute \
+ $object $opt $user
+ verify_attr clear_attribute \
+ $object $opt $user
+ done
+ log_must grant_attr $user $object
+ for opt in $XATTROPTIONS ; do
+ verify_attr set_attribute \
+ $object $opt $user
+ verify_attr clear_attribute \
+ $object $opt $user
+ done
+ log_must revoke_attr $user $object
+ done
+ done
+ destroy_object $mtpt/$file $mtpt/$dir
+ done
+done
+
+log_pass "Set/Clear DOS attributes succeed while user has " \
+ "write_attributes permission or PRIV_FILE_OWNER privilege"
diff --git a/tests/sys/cddl/zfs/tests/acl/cifs/cifs_attr_002_pos.ksh b/tests/sys/cddl/zfs/tests/acl/cifs/cifs_attr_002_pos.ksh
new file mode 100644
index 000000000000..01dfa7d989b4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/cifs/cifs_attr_002_pos.ksh
@@ -0,0 +1,275 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+. $STF_SUITE/tests/acl/cifs/cifs.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: cifs_attr_002_pos
+#
+# DESCRIPTION:
+# Verify the user with PRIV_FILE_FLAG_SET/PRIV_FILE_FLAG_CLEAR
+# could set/clear BSD'ish attributes.
+# (Immutable, nounlink, and appendonly)
+#
+# STRATEGY:
+# 1. Loop super user and non-super user to run the test case.
+# 2. Create basedir and a set of subdirectores and files within it.
+# 3. Grant user has PRIV_FILE_FLAG_SET/PRIV_FILE_FLAG_CLEAR separately.
+# 4. Verify set/clear BSD'ish attributes should succeed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-11-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+if ! cifs_supported ; then
+ log_unsupported "CIFS not supported on current system."
+fi
+
+test_requires ZFS_ACL ZFS_XATTR
+
+function cleanup
+{
+ if [[ -n $gobject ]]; then
+ destroy_object $gobject
+ fi
+
+ for fs in $TESTPOOL/$TESTFS $TESTPOOL ; do
+ mtpt=$(get_prop mountpoint $fs)
+ log_must $RM -rf $mtpt/file.* $mtpt/dir.*
+ done
+}
+
+#
+# Set the special attribute to the given node
+#
+# $1: The given node (file/dir)
+# $2: The special attribute to be set
+# $3: Execute username
+#
+function set_attribute
+{
+ typeset object=$1
+ typeset attr=$2
+ typeset user=$3
+ typeset ret=0
+
+ if [[ -z $object ]]; then
+ log_fail "Object not defined."
+ fi
+
+ if [[ -z $attr ]]; then
+ attr="uiadm"
+ if [[ -f $object ]]; then
+ attr="${attr}q"
+ fi
+ fi
+
+ if [[ -n $user ]]; then
+ $RUNWATTR -u $user -p =basic${priv_mod} \
+ "$CHMOD S+c${attr} $object"
+ ret=$?
+ else
+ $CHMOD S+c${attr} $object
+ ret=$?
+ fi
+
+ return $ret
+}
+
+#
+# Clear the special attribute to the given node
+#
+# $1: The given node (file/dir)
+# $2: The special attribute to be cleared
+# $3: Execute username
+#
+function clear_attribute
+{
+ typeset object=$1
+ typeset attr=$2
+ typeset user=$3
+ typeset ret=0
+
+ if [[ -z $object ]]; then
+ log_fail "Object($object) not defined."
+ fi
+
+ if [[ -z $attr ]]; then
+ attr="uiadm"
+ if [[ -f $object ]]; then
+ attr="${attr}q"
+ fi
+ fi
+
+ if [[ -n $user ]]; then
+ $RUNWATTR -u $user -p =basic${priv_mod} \
+ "$CHMOD S-c${attr} $object"
+ ret=$?
+ else
+ $CHMOD S-c${attr} $object
+ ret=$?
+ fi
+
+ return $ret
+}
+
+#
+# Grant the privset to the given user
+#
+# $1: The given user
+# $2: The given privset
+#
+function grant_priv
+{
+ typeset user=$1
+ typeset priv=$2
+
+ if [[ -z $user || -z $priv ]]; then
+ log_fail "User($user), Priv($priv) not defined."
+ fi
+ priv_mod=",$priv"
+ return $?
+}
+
+#
+# Revoke the all additional privset from the given user
+#
+# $1: The given user
+#
+function revoke_priv
+{
+ typeset user=$1
+
+ if [[ -z $user ]]; then
+ log_fail "User not defined."
+ fi
+ priv_mod=
+ return $?
+}
+
+#
+# Invoke the function and verify whether its return code as expected
+#
+# $1: Function be invoked
+# $2: The given node (file/dir)
+# $3: Execute user
+# $4: Option
+#
+function verify_op
+{
+ typeset func=$1
+ typeset object=$2
+ typeset opt=$3
+ typeset user=$4
+ typeset expect="log_mustnot"
+
+ if [[ -z $func || -z $object ]]; then
+ log_fail "Func($func), Object($object) not defined."
+ fi
+
+ # If user has PRIV_FILE_FLAG_SET, it could permit to set_attribute,
+ # And If has PRIV_FILE_FLAG_CLEAR, it could permit to clear_attribute,
+ # otherwise log_mustnot.
+ if [[ -z $user || $user == "root" ]] || \
+ [[ $priv_mod == *"file_flag_set"* ]] || \
+ [[ $priv_mod == *"all"* ]] ; then
+ expect="log_must"
+ fi
+ if [[ -d $object ]] && \
+ [[ $opt == *"q"* ]] ; then
+ expect="log_mustnot"
+ fi
+
+ if [[ $func == clear_attribute ]]; then
+ if [[ $expect == "log_mustnot" ]]; then
+ expect="log_must"
+ elif [[ -z $user || $user == "root" ]] || \
+ [[ $priv_mod == *"all"* ]] ; then
+ expect="log_must"
+ else
+ expect="log_mustnot"
+ fi
+ fi
+
+ $expect $func $object $opt $user
+}
+
+log_assert "Verify set/clear BSD'ish attributes will succeed while user has " \
+ "PRIV_FILE_FLAG_SET/PRIV_FILE_FLAG_CLEAR privilege"
+log_onexit cleanup
+
+file="file.0"
+dir="dir.0"
+FLAGOPTIONS="u i a d q m"
+
+typeset gobject
+for fs in $TESTPOOL $TESTPOOL/$TESTFS ; do
+ mtpt=$(get_prop mountpoint $fs)
+ for owner in root $ZFS_ACL_STAFF1 ; do
+
+ create_object "file" $mtpt/$file $owner
+ create_object "dir" $mtpt/$dir $owner
+
+ for object in $mtpt/$file $mtpt/$dir ; do
+ gobject=$object
+ for user in root $ZFS_ACL_STAFF2 ; do
+ log_must grant_priv $user file_flag_set
+ for opt in $FLAGOPTIONS ; do
+ verify_op set_attribute \
+ $object $opt $user
+ verify_op clear_attribute \
+ $object $opt $user
+ done
+ log_must revoke_priv $user
+
+ log_must grant_priv $user all
+ for opt in $FLAGOPTIONS ; do
+ verify_op set_attribute \
+ $object $opt $user
+ verify_op clear_attribute \
+ $object $opt $user
+ done
+ log_must revoke_priv $user
+ done
+ done
+ destroy_object $mtpt/$file $mtpt/$dir
+ done
+done
+
+log_pass "Set/Clear BSD'ish attributes succeed while user has " \
+ "PRIV_FILE_FLAG_SET/PRIV_FILE_FLAG_CLEAR privilege"
diff --git a/tests/sys/cddl/zfs/tests/acl/cifs/cifs_attr_003_pos.ksh b/tests/sys/cddl/zfs/tests/acl/cifs/cifs_attr_003_pos.ksh
new file mode 100644
index 000000000000..92813171b3be
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/cifs/cifs_attr_003_pos.ksh
@@ -0,0 +1,617 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+. $STF_SUITE/tests/acl/cifs/cifs.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: cifs_attr_003_pos
+#
+# DESCRIPTION:
+# Verify the DOS attributes (Readonly, Hidden, Archive, System)
+# and BSD'ish attributes (Immutable, nounlink, and appendonly)
+# will provide the proper access limitation as expected.
+#
+# Readonly means that the content of a file can't be modified, but
+# timestamps, mode and so on can.
+#
+# Archive - Indicates if a file should be included in the next backup
+# of the file system. ZFS will set this bit whenever a file is
+# modified.
+#
+# Hidden and System (ZFS does nothing special with these, other than
+# letting a user/application set them.
+#
+# Immutable (The data can't, change nor can mode, ACL, size and so on)
+# The only attribute that can be updated is the access time.
+#
+# Nonunlink - Sort of like immutable except that a file/dir can't be
+# removed.
+# This will also effect a rename operation, since that involes a
+# remove.
+#
+# Appendonly - File can only be appended to.
+#
+# nodump, settable, opaque (These are for the MacOS port) we will
+# allow them to be set, but have no semantics tied to them.
+#
+# STRATEGY:
+# 1. Loop super user and non-super user to run the test case.
+# 2. Create basedir and a set of subdirectores and files within it.
+# 3. Set the file/dir with each kind of special attribute.
+# 4. Verify the access limitation works as expected.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-11-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+if ! cifs_supported ; then
+ log_unsupported "CIFS not supported on current system."
+fi
+
+test_requires ZFS_ACL ZFS_XATTR
+
+function cleanup
+{
+ if [[ -n $gobject ]]; then
+ destroy_object $gobject
+ fi
+
+ for fs in $TESTPOOL/$TESTFS $TESTPOOL ; do
+ mtpt=$(get_prop mountpoint $fs)
+ log_must $RM -rf $mtpt/file.* $mtpt/dir.*
+ done
+}
+
+#
+# Set the special attribute to the given node
+#
+# $1: The given node (file/dir)
+# $2: The special attribute to be set
+#
+function set_attribute
+{
+ typeset object=$1
+ typeset attr=$2
+
+ if [[ -z $attr ]]; then
+ attr="AHRSadimu"
+ if [[ -f $object ]]; then
+ attr="${attr}q"
+ fi
+ fi
+
+ $CHMOD S+c${attr} $object
+ return $?
+}
+
+#
+# Clear the special attribute to the given node
+#
+# $1: The given node (file/dir)
+# $2: The special attribute to be cleared
+#
+function clear_attribute
+{
+ typeset object=$1
+ typeset attr=$2
+
+ if [[ -z $attr ]]; then
+ if is_global_zone ; then
+ attr="AHRSadimu"
+ if [[ -f $object ]]; then
+ attr="${attr}q"
+ fi
+ else
+ attr="AHRS"
+ fi
+ fi
+
+ $CHMOD S-c${attr} $object
+ return $?
+}
+
+#
+# A wrapper function to call test function according to the given attr
+#
+# $1: The given node (file/dir)
+# $2: The special attribute to be test
+#
+function test_wrapper
+{
+ typeset object=$1
+ typeset attr=$2
+
+ if [[ -z $object || -z $attr ]]; then
+ log_fail "Object($object), Attr($attr) not defined."
+ fi
+
+ case $attr in
+ R) func=test_readonly
+ ;;
+ i) func=test_immutable
+ ;;
+ u) func=test_nounlink
+ ;;
+ a) func=test_appendonly
+ ;;
+ esac
+
+ if [[ -n $func ]]; then
+ $func $object
+ fi
+}
+
+#
+# Invoke the function and verify whether its return code as expected
+#
+# $1: Expect value
+# $2-$n: Function and args need to be invoked
+#
+function verify_expect
+{
+ typeset -i expect=$1
+ typeset status
+
+ shift
+
+ "$@" > /dev/null 2>&1
+ status=$?
+ if [[ $status -eq 0 ]]; then
+ if (( expect != 0 )); then
+ log_fail "$@ unexpect return 0"
+ fi
+ else
+ if (( expect == 0 )); then
+ log_fail "$@ unexpect return $status"
+ fi
+ fi
+}
+
+#
+# Unit testing function against overwrite file
+#
+# $1: The given file node
+# $2: Execute user
+# $3: Expect value, default to be zero
+#
+function unit_writefile
+{
+ typeset object=$1
+ typeset user=$2
+ typeset expect=${3:-0}
+
+ if [[ -f $object ]]; then
+ verify_expect $expect $CHG_USR_EXEC $user \
+ $CP $TESTFILE $object
+ verify_expect $expect $CHG_USR_EXEC $user \
+ $EVAL "$ECHO '$TESTSTR' > $object"
+ fi
+}
+
+#
+# Unit testing function against write new stuffs into a directory
+#
+# $1: The given directory node
+# $2: Execute user
+# $3: Expect value, default to be zero
+#
+function unit_writedir
+{
+ typeset object=$1
+ typeset user=$2
+ typeset expect=${3:-0}
+
+ if [[ -d $object ]]; then
+ verify_expect $expect $CHG_USR_EXEC $user \
+ $CP $TESTFILE $object
+ verify_expect $expect $CHG_USR_EXEC $user \
+ $MKDIR -p $object/$TESTDIR
+ fi
+}
+
+function unit_appenddata
+{
+ typeset object=$1
+ typeset user=$2
+ typeset expect=${3:-0}
+
+ if [[ ! -d $object ]]; then
+ verify_expect $expect $CHG_USR_EXEC $user \
+ $EVAL "$ECHO '$TESTSTR' >> $object"
+ fi
+}
+
+#
+# Unit testing function against delete content from a directory
+#
+# $1: The given node, dir
+# $2: Execute user
+# $3: Expect value, default to be zero
+#
+function unit_deletecontent
+{
+ typeset object=$1
+ typeset user=$2
+ typeset expect=${3:-0}
+
+ if [[ -d $object ]]; then
+ for target in $object/${TESTFILE##*/} $object/$TESTDIR ; do
+ if [[ -e $target ]]; then
+ verify_expect $expect $CHG_USR_EXEC $user \
+ $EVAL "$MV $target $target.new"
+ verify_expect $expect $CHG_USR_EXEC $user \
+ $EVAL "$ECHO y | $RM -r $target.new"
+ fi
+ done
+ fi
+}
+
+#
+# Unit testing function against delete a node
+#
+# $1: The given node, file/dir
+# $2: Execute user
+# $3: Expect value, default to be zero
+#
+function unit_deletedata
+{
+ typeset object=$1
+ typeset user=$2
+ typeset expect=${3:-0}
+
+ verify_expect $expect $CHG_USR_EXEC $user \
+ $EVAL "$ECHO y | $RM -r $object"
+
+}
+
+#
+# Unit testing function against write xattr to a node
+#
+# $1: The given node, file/dir
+# $2: Execute user
+# $3: Expect value, default to be zero
+#
+function unit_writexattr
+{
+ typeset object=$1
+ typeset user=$2
+ typeset expect=${3:-0}
+
+ verify_expect $expect $CHG_USR_EXEC $user \
+ $RUNAT $object "$CP $TESTFILE $TESTATTR"
+ verify_expect $expect $CHG_USR_EXEC $user \
+ $EVAL "$RUNAT $object \"$ECHO '$TESTSTR' > $TESTATTR\""
+ verify_expect $expect $CHG_USR_EXEC $user \
+ $EVAL "$RUNAT $object \"$ECHO '$TESTSTR' >> $TESTATTR\""
+ if [[ $expect -eq 0 ]]; then
+ verify_expect $expect $CHG_USR_EXEC $user \
+ $RUNAT $object "$RM -f $TESTATTR"
+ fi
+}
+
+#
+# Unit testing function against modify accesstime of a node
+#
+# $1: The given node, file/dir
+# $2: Execute user
+# $3: Expect value, default to be zero
+#
+function unit_accesstime
+{
+ typeset object=$1
+ typeset user=$2
+ typeset expect=${3:-0}
+
+ if [[ -d $object ]]; then
+ verify_expect $expect $CHG_USR_EXEC $user $LS $object
+ else
+ verify_expect $expect $CHG_USR_EXEC $user $CAT $object
+ fi
+}
+
+#
+# Unit testing function against modify updatetime of a node
+#
+# $1: The given node, file/dir
+# $2: Execute user
+# $3: Expect value, default to be zero
+#
+function unit_updatetime
+{
+ typeset object=$1
+ typeset user=$2
+ typeset expect=${3:-0}
+
+ verify_expect $expect $CHG_USR_EXEC $user $TOUCH $object
+ verify_expect $expect $CHG_USR_EXEC $user $TOUCH -a $object
+ verify_expect $expect $CHG_USR_EXEC $user $TOUCH -m $object
+}
+
+#
+# Unit testing function against write acl of a node
+#
+# $1: The given node, file/dir
+# $2: Execute user
+# $3: Expect value, default to be zero
+#
+function unit_writeacl
+{
+ typeset object=$1
+ typeset user=$2
+ typeset expect=${3:-0}
+
+ verify_expect $expect $CHG_USR_EXEC $user chmod A+$TESTACL $object
+ verify_expect $expect $CHG_USR_EXEC $user chmod A+$TESTACL $object
+ verify_expect $expect $CHG_USR_EXEC $user chmod A0- $object
+ verify_expect $expect $CHG_USR_EXEC $user chmod A0- $object
+ oldmode=$(get_mode $object)
+ verify_expect $expect $CHG_USR_EXEC $user chmod $TESTMODE $object
+}
+
+#
+# Testing function to verify the given node is readonly
+#
+# $1: The given node, file/dir
+#
+function test_readonly
+{
+ typeset object=$1
+
+ if [[ -z $object ]]; then
+ log_fail "Object($object) not defined."
+ fi
+
+ log_note "Testing readonly of $object"
+
+ for user in $ZFS_ACL_CUR_USER root $ZFS_ACL_STAFF2; do
+ if [[ -d $object ]]; then
+ log_must usr_exec chmod \
+ A+user:$user:${ace_dir}:allow $object
+ else
+ log_must usr_exec chmod \
+ A+user:$user:${ace_file}:allow $object
+ fi
+
+ log_must set_attribute $object "R"
+
+ unit_writefile $object $user 1
+ unit_writedir $object $user
+ unit_appenddata $object $user 1
+
+ if [[ -d $object ]]; then
+ unit_writexattr $object $user
+ else
+ unit_writexattr $object $user 1
+ fi
+
+ unit_accesstime $object $user
+ unit_updatetime $object $user
+ unit_writeacl $object $user
+ unit_deletecontent $object $user
+ unit_deletedata $object $user
+
+ if [[ -d $object ]] ;then
+ create_object "dir" $object $ZFS_ACL_CUR_USER
+ else
+ create_object "file" $object $ZFS_ACL_CUR_USER
+ fi
+ done
+}
+
+#
+# Testing function to verify the given node is immutable
+#
+# $1: The given node, file/dir
+#
+function test_immutable
+{
+ typeset object=$1
+
+ if [[ -z $object ]]; then
+ log_fail "Object($object) not defined."
+ fi
+
+ log_note "Testing immutable of $object"
+
+ for user in $ZFS_ACL_CUR_USER root $ZFS_ACL_STAFF2; do
+ if [[ -d $object ]]; then
+ log_must usr_exec chmod \
+ A+user:$user:${ace_dir}:allow $object
+ else
+ log_must usr_exec chmod \
+ A+user:$user:${ace_file}:allow $object
+ fi
+ log_must set_attribute $object "i"
+
+ unit_writefile $object $user 1
+ unit_writedir $object $user 1
+ unit_appenddata $object $user 1
+ unit_writexattr $object $user 1
+ unit_accesstime $object $user
+ unit_updatetime $object $user 1
+ unit_writeacl $object $user 1
+ unit_deletecontent $object $user 1
+ unit_deletedata $object $user 1
+
+ if [[ -d $object ]] ;then
+ create_object "dir" $object $ZFS_ACL_CUR_USER
+ else
+ create_object "file" $object $ZFS_ACL_CUR_USER
+ fi
+ done
+}
+
+#
+# Testing function to verify the given node is nounlink
+#
+# $1: The given node, file/dir
+#
+function test_nounlink
+{
+ typeset object=$1
+
+ if [[ -z $object ]]; then
+ log_fail "Object($object) not defined."
+ fi
+
+ $ECHO "Testing nounlink of $object"
+
+ for user in $ZFS_ACL_CUR_USER root $ZFS_ACL_STAFF2; do
+ if [[ -d $object ]]; then
+ log_must usr_exec chmod \
+ A+user:$user:${ace_dir}:allow $object
+ else
+ log_must usr_exec chmod \
+ A+user:$user:${ace_file}:allow $object
+ fi
+ log_must set_attribute $object "u"
+
+ unit_writefile $object $user
+ unit_writedir $object $user
+ unit_appenddata $object $user
+ unit_writexattr $object $user
+ unit_accesstime $object $user
+ unit_updatetime $object $user
+ unit_writeacl $object $user
+ unit_deletecontent $object $user 1
+ unit_deletedata $object $user 1
+
+ if [[ -d $object ]] ;then
+ create_object "dir" $object $ZFS_ACL_CUR_USER
+ else
+ create_object "file" $object $ZFS_ACL_CUR_USER
+ fi
+ done
+}
+
+#
+# Testing function to verify the given node is appendonly
+#
+# $1: The given node, file/dir
+#
+function test_appendonly
+{
+ typeset object=$1
+
+ if [[ -z $object ]]; then
+ log_fail "Object($object) not defined."
+ fi
+
+ log_note "Testing appendonly of $object"
+
+ for user in $ZFS_ACL_CUR_USER root $ZFS_ACL_STAFF2; do
+ if [[ -d $object ]]; then
+ log_must usr_exec chmod \
+ A+user:$user:${ace_dir}:allow $object
+ else
+ log_must usr_exec chmod \
+ A+user:$user:${ace_file}:allow $object
+ fi
+ log_must set_attribute $object "a"
+
+ unit_writefile $object $user 1
+ unit_writedir $object $user
+ unit_appenddata $object $user
+ unit_writexattr $object $user
+ unit_accesstime $object $user
+ unit_updatetime $object $user
+ unit_writeacl $object $user
+ unit_deletecontent $object $user
+ unit_deletedata $object $user
+
+ if [[ -d $object ]] ;then
+ create_object "dir" $object $ZFS_ACL_CUR_USER
+ else
+ create_object "file" $object $ZFS_ACL_CUR_USER
+ fi
+ done
+}
+
+FILES="file.0 file.1"
+DIRS="dir.0 dir.1"
+XATTRS="attr.0 attr.1"
+FS="$TESTPOOL $TESTPOOL/$TESTFS"
+
+if is_global_zone ; then
+ ATTRS="R i u a"
+else
+ ATTRS="R"
+fi
+
+TESTFILE=$TMPDIR/tfile
+TESTDIR=tdir
+TESTATTR=tattr
+TESTACL=user:$ZFS_ACL_OTHER1:write_data:allow
+TESTMODE=777
+TESTSTR="ZFS test suites"
+
+ace_file="write_data/append_data/write_xattr/write_acl/write_attributes"
+ace_dir="add_file/add_subdirectory/${ace_file}"
+
+log_assert "Verify DOS & BSD'ish attributes will provide the " \
+ "access limitation as expected."
+log_onexit cleanup
+
+$ECHO "$TESTSTR" > $TESTFILE
+
+typeset gobject
+typeset gattr
+for gattr in $ATTRS ; do
+ for fs in $FS ; do
+ mtpt=$(get_prop mountpoint $fs)
+ $CHMOD 777 $mtpt
+ for user in root $ZFS_ACL_STAFF1; do
+ log_must set_cur_usr $user
+ for file in $FILES ; do
+ gobject=$mtpt/$file
+ create_object "file" $gobject $ZFS_ACL_CUR_USER
+ test_wrapper $gobject $gattr
+ destroy_object $gobject
+ done
+
+ for dir in $DIRS ; do
+ gobject=$mtpt/$dir
+ create_object "dir" $gobject $ZFS_ACL_CUR_USER
+ test_wrapper $gobject $gattr
+ destroy_object $gobject
+ done
+ done
+ done
+done
+
+log_pass "DOS & BSD'ish attributes provide the access limitation as expected."
diff --git a/tests/sys/cddl/zfs/tests/acl/cifs/cifs_test.sh b/tests/sys/cddl/zfs/tests/acl/cifs/cifs_test.sh
new file mode 100755
index 000000000000..bbf48dc12437
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/cifs/cifs_test.sh
@@ -0,0 +1,114 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case cifs_attr_001_pos cleanup
+cifs_attr_001_pos_head()
+{
+ atf_set "descr" "Verify set/clear DOS attributes will succeed while user haswrite_attributes permission or PRIV_FILE_OWNER privilege"
+ atf_set "require.config" "zfs_acl zfs_xattr"
+ atf_set "require.progs" "ksh93 runwattr"
+}
+cifs_attr_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/cifs.kshlib
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/cifs_attr_001_pos.ksh || atf_fail "Testcase failed"
+}
+cifs_attr_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/cifs.kshlib
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case cifs_attr_002_pos cleanup
+cifs_attr_002_pos_head()
+{
+ atf_set "descr" "Verify set/clear BSD'ish attributes will succeed while user hasPRIV_FILE_FLAG_SET/PRIV_FILE_FLAG_CLEAR privilege"
+ atf_set "require.config" "zfs_acl zfs_xattr"
+ atf_set "require.progs" "ksh93 runwattr"
+}
+cifs_attr_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/cifs.kshlib
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/cifs_attr_002_pos.ksh || atf_fail "Testcase failed"
+}
+cifs_attr_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/cifs.kshlib
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case cifs_attr_003_pos cleanup
+cifs_attr_003_pos_head()
+{
+ atf_set "descr" "Verify DOS & BSD'ish attributes will provide theaccess limitation as expected."
+ atf_set "require.config" "zfs_acl zfs_xattr"
+ atf_set "require.progs" "ksh93 runat"
+}
+cifs_attr_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/cifs.kshlib
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/cifs_attr_003_pos.ksh || atf_fail "Testcase failed"
+}
+cifs_attr_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/cifs.kshlib
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case cifs_attr_001_pos
+ atf_add_test_case cifs_attr_002_pos
+ atf_add_test_case cifs_attr_003_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/acl/cleanup.ksh b/tests/sys/cddl/zfs/tests/acl/cleanup.ksh
new file mode 100644
index 000000000000..36e56c4905ce
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/cleanup.ksh
@@ -0,0 +1,39 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+
+cleanup_user_group
+
+# restore the state of svc:/network/nis/client:default
+if [[ -e $NISSTAFILE ]]; then
+ log_must $SVCADM enable svc:/network/nis/client:default
+ log_must $RM -f $NISSTAFILE
+fi
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/acl/nontrivial/Makefile b/tests/sys/cddl/zfs/tests/acl/nontrivial/Makefile
new file mode 100644
index 000000000000..1332371f7570
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/nontrivial/Makefile
@@ -0,0 +1,38 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/acl/nontrivial
+FILESDIR=${TESTSDIR}
+
+${PACKAGE}FILES+= zfs_acl_chmod_001_neg.ksh
+${PACKAGE}FILES+= zfs_acl_chmod_002_pos.ksh
+${PACKAGE}FILES+= zfs_acl_chmod_aclmode_001_pos.ksh
+${PACKAGE}FILES+= zfs_acl_chmod_compact_001_pos.ksh
+${PACKAGE}FILES+= zfs_acl_chmod_delete_001_pos.ksh
+${PACKAGE}FILES+= zfs_acl_chmod_inherit_001_pos.ksh
+${PACKAGE}FILES+= zfs_acl_chmod_inherit_002_pos.ksh
+${PACKAGE}FILES+= zfs_acl_chmod_inherit_003_pos.ksh
+${PACKAGE}FILES+= zfs_acl_chmod_inherit_004_pos.ksh
+${PACKAGE}FILES+= zfs_acl_chmod_owner_001_pos.ksh
+${PACKAGE}FILES+= zfs_acl_chmod_rwacl_001_pos.ksh
+${PACKAGE}FILES+= zfs_acl_chmod_rwx_001_pos.ksh
+${PACKAGE}FILES+= zfs_acl_chmod_rwx_002_pos.ksh
+${PACKAGE}FILES+= zfs_acl_chmod_rwx_003_pos.ksh
+${PACKAGE}FILES+= zfs_acl_chmod_rwx_004_pos.ksh
+${PACKAGE}FILES+= zfs_acl_chmod_xattr_001_pos.ksh
+${PACKAGE}FILES+= zfs_acl_chmod_xattr_002_pos.ksh
+${PACKAGE}FILES+= zfs_acl_cp_001_pos.ksh
+${PACKAGE}FILES+= zfs_acl_cp_002_pos.ksh
+${PACKAGE}FILES+= zfs_acl_cpio_001_pos.ksh
+${PACKAGE}FILES+= zfs_acl_cpio_002_pos.ksh
+${PACKAGE}FILES+= zfs_acl_find_001_pos.ksh
+${PACKAGE}FILES+= zfs_acl_ls_001_pos.ksh
+${PACKAGE}FILES+= zfs_acl_mv_001_pos.ksh
+${PACKAGE}FILES+= zfs_acl_tar_001_pos.ksh
+${PACKAGE}FILES+= zfs_acl_tar_002_pos.ksh
+
+ATF_TESTS_KSH93+= nontrivial_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/acl/nontrivial/nontrivial_test.sh b/tests/sys/cddl/zfs/tests/acl/nontrivial/nontrivial_test.sh
new file mode 100755
index 000000000000..62f4da2412c5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/nontrivial/nontrivial_test.sh
@@ -0,0 +1,693 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zfs_acl_chmod_001_neg cleanup
+zfs_acl_chmod_001_neg_head()
+{
+ atf_set "descr" "Verify illegal operating to ACL, it will fail."
+ atf_set "require.config" zfs_acl
+}
+zfs_acl_chmod_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_chmod_001_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_chmod_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_chmod_002_pos cleanup
+zfs_acl_chmod_002_pos_head()
+{
+ atf_set "descr" "Verify acl after upgrading."
+ atf_set "require.config" zfs_acl
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_acl_chmod_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_chmod_002_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_chmod_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_chmod_aclmode_001_pos cleanup
+zfs_acl_chmod_aclmode_001_pos_head()
+{
+ atf_set "descr" "Verify chmod have correct behaviour to directory and file whenfilesystem has the different aclmode setting."
+ atf_set "require.config" zfs_acl
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_acl_chmod_aclmode_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_chmod_aclmode_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_chmod_aclmode_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_chmod_compact_001_pos cleanup
+zfs_acl_chmod_compact_001_pos_head()
+{
+ atf_set "descr" "chmod A{+|=} should set compact ACL correctly."
+ atf_set "require.config" zfs_acl
+}
+zfs_acl_chmod_compact_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_chmod_compact_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_chmod_compact_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_chmod_delete_001_pos cleanup
+zfs_acl_chmod_delete_001_pos_head()
+{
+ atf_set "descr" "Verify that the combined delete_child/delete permission forowner/group/everyone are correct."
+ atf_set "require.config" zfs_acl
+}
+zfs_acl_chmod_delete_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_chmod_delete_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_chmod_delete_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_chmod_inherit_001_pos cleanup
+zfs_acl_chmod_inherit_001_pos_head()
+{
+ atf_set "descr" "Verify chmod have correct behaviour to directory and file whensetting different inherit strategies to them."
+ atf_set "require.config" zfs_acl
+}
+zfs_acl_chmod_inherit_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_chmod_inherit_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_chmod_inherit_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_chmod_inherit_002_pos cleanup
+zfs_acl_chmod_inherit_002_pos_head()
+{
+ atf_set "descr" "Verify chmod have correct behaviour to directory and file whenfilesystem has the different aclinherit setting."
+ atf_set "require.config" zfs_acl
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_acl_chmod_inherit_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_chmod_inherit_002_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_chmod_inherit_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_chmod_inherit_003_pos cleanup
+zfs_acl_chmod_inherit_003_pos_head()
+{
+ atf_set "descr" "Verify chmod have correct behaviour to directory and file whenfilesystem has the different aclinherit setting."
+ atf_set "require.config" zfs_acl
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_acl_chmod_inherit_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_chmod_inherit_003_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_chmod_inherit_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_chmod_inherit_004_pos cleanup
+zfs_acl_chmod_inherit_004_pos_head()
+{
+ atf_set "descr" "Verify aclinherit=passthrough-x will inherit the 'x' bits while mode request."
+ atf_set "require.config" zfs_acl
+ atf_set "require.progs" "ksh93 zfs zpool"
+}
+zfs_acl_chmod_inherit_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_chmod_inherit_004_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_chmod_inherit_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_chmod_owner_001_pos cleanup
+zfs_acl_chmod_owner_001_pos_head()
+{
+ atf_set "descr" "Verify that the chown/chgrp could take owner/groupwhile permission is granted."
+ atf_set "require.config" zfs_acl
+}
+zfs_acl_chmod_owner_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_chmod_owner_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_chmod_owner_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_chmod_rwacl_001_pos cleanup
+zfs_acl_chmod_rwacl_001_pos_head()
+{
+ atf_set "descr" "Verify chmod A[number]{+|-|=} read_acl/write_acl have correctbehaviour to access permission."
+ atf_set "require.config" zfs_acl
+}
+zfs_acl_chmod_rwacl_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_chmod_rwacl_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_chmod_rwacl_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_chmod_rwx_001_pos cleanup
+zfs_acl_chmod_rwx_001_pos_head()
+{
+ atf_set "descr" "chmod A{+|-|=} have the correct behaviour to the ACL list."
+ atf_set "require.config" zfs_acl
+}
+zfs_acl_chmod_rwx_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_chmod_rwx_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_chmod_rwx_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_chmod_rwx_002_pos cleanup
+zfs_acl_chmod_rwx_002_pos_head()
+{
+ atf_set "descr" "chmod A{+|-|=} read_data|write_data|execute for owner@, group@or everyone@ correctly alters mode bits."
+ atf_set "require.config" zfs_acl
+}
+zfs_acl_chmod_rwx_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_chmod_rwx_002_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_chmod_rwx_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_chmod_rwx_003_pos cleanup
+zfs_acl_chmod_rwx_003_pos_head()
+{
+ atf_set "descr" "Verify that the read_data/write_data/execute permission forowner/group/everyone are correct."
+ atf_set "require.config" zfs_acl
+}
+zfs_acl_chmod_rwx_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_chmod_rwx_003_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_chmod_rwx_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_chmod_rwx_004_pos cleanup
+zfs_acl_chmod_rwx_004_pos_head()
+{
+ atf_set "descr" "Verify that explicit ACL setting to specified user or group willoverride existed access rule."
+ atf_set "require.config" zfs_acl
+}
+zfs_acl_chmod_rwx_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_chmod_rwx_004_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_chmod_rwx_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_chmod_xattr_001_pos cleanup
+zfs_acl_chmod_xattr_001_pos_head()
+{
+ atf_set "descr" "Verify that the permission of read_xattr/write_xattr forowner/group/everyone are correct."
+ atf_set "require.config" "zfs_acl zfs_xattr"
+ atf_set "require.progs" "ksh93 runat"
+}
+zfs_acl_chmod_xattr_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_chmod_xattr_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_chmod_xattr_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_chmod_xattr_002_pos cleanup
+zfs_acl_chmod_xattr_002_pos_head()
+{
+ atf_set "descr" "Verify that the permission of write_xattr forowner/group/everyone while remove extended attributes are correct."
+ atf_set "require.config" zfs_xattr
+ atf_set "require.progs" "ksh93 runat"
+}
+zfs_acl_chmod_xattr_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_chmod_xattr_002_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_chmod_xattr_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_cp_001_pos cleanup
+zfs_acl_cp_001_pos_head()
+{
+ atf_set "descr" "Verify that '$CP [-p]' supports ZFS ACLs."
+ atf_set "require.config" zfs_acl
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_acl_cp_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_cp_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_cp_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_cp_002_pos cleanup
+zfs_acl_cp_002_pos_head()
+{
+ atf_set "descr" "Verify that '$CP [-p]' supports ZFS ACLs."
+ atf_set "require.config" "zfs_acl zfs_xattr"
+ atf_set "require.progs" "ksh93 zfs runat"
+}
+zfs_acl_cp_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_cp_002_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_cp_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_cpio_001_pos cleanup
+zfs_acl_cpio_001_pos_head()
+{
+ atf_set "descr" "Verify that '$CPIO' command supports to archive ZFS ACLs."
+ atf_set "require.config" zfs_acl
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_acl_cpio_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_cpio_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_cpio_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_cpio_002_pos cleanup
+zfs_acl_cpio_002_pos_head()
+{
+ atf_set "descr" "Verify that '$CPIO' command supports to archive ZFS ACLs & xattrs."
+ atf_set "require.config" "zfs_acl zfs_xattr"
+ atf_set "require.progs" "ksh93 zfs runat"
+}
+zfs_acl_cpio_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_cpio_002_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_cpio_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_find_001_pos cleanup
+zfs_acl_find_001_pos_head()
+{
+ atf_set "descr" "Verify that '$FIND' command supports ZFS ACLs."
+ atf_set "require.config" zfs_acl
+}
+zfs_acl_find_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_find_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_find_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_ls_001_pos cleanup
+zfs_acl_ls_001_pos_head()
+{
+ atf_set "descr" "Verify that '$LS' command supports ZFS ACLs."
+ atf_set "require.config" zfs_acl
+}
+zfs_acl_ls_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_ls_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_ls_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_mv_001_pos cleanup
+zfs_acl_mv_001_pos_head()
+{
+ atf_set "descr" "Verify that '$MV' supports ZFS ACLs."
+ atf_set "require.config" zfs_acl
+}
+zfs_acl_mv_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_mv_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_mv_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_tar_001_pos cleanup
+zfs_acl_tar_001_pos_head()
+{
+ atf_set "descr" "Verify that '$TAR' command supports to archive ZFS ACLs."
+ atf_set "require.config" zfs_acl
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_acl_tar_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_tar_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_tar_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_tar_002_pos cleanup
+zfs_acl_tar_002_pos_head()
+{
+ atf_set "descr" "Verify that '$TAR' command supports to archive ZFS ACLs & xattrs."
+ atf_set "require.config" "zfs_acl zfs_xattr"
+ atf_set "require.progs" "ksh93 zfs runat"
+}
+zfs_acl_tar_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_tar_002_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_tar_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zfs_acl_chmod_001_neg
+ atf_add_test_case zfs_acl_chmod_002_pos
+ atf_add_test_case zfs_acl_chmod_aclmode_001_pos
+ atf_add_test_case zfs_acl_chmod_compact_001_pos
+ atf_add_test_case zfs_acl_chmod_delete_001_pos
+ atf_add_test_case zfs_acl_chmod_inherit_001_pos
+ atf_add_test_case zfs_acl_chmod_inherit_002_pos
+ atf_add_test_case zfs_acl_chmod_inherit_003_pos
+ atf_add_test_case zfs_acl_chmod_inherit_004_pos
+ atf_add_test_case zfs_acl_chmod_owner_001_pos
+ atf_add_test_case zfs_acl_chmod_rwacl_001_pos
+ atf_add_test_case zfs_acl_chmod_rwx_001_pos
+ atf_add_test_case zfs_acl_chmod_rwx_002_pos
+ atf_add_test_case zfs_acl_chmod_rwx_003_pos
+ atf_add_test_case zfs_acl_chmod_rwx_004_pos
+ atf_add_test_case zfs_acl_chmod_xattr_001_pos
+ atf_add_test_case zfs_acl_chmod_xattr_002_pos
+ atf_add_test_case zfs_acl_cp_001_pos
+ atf_add_test_case zfs_acl_cp_002_pos
+ atf_add_test_case zfs_acl_cpio_001_pos
+ atf_add_test_case zfs_acl_cpio_002_pos
+ atf_add_test_case zfs_acl_find_001_pos
+ atf_add_test_case zfs_acl_ls_001_pos
+ atf_add_test_case zfs_acl_mv_001_pos
+ atf_add_test_case zfs_acl_tar_001_pos
+ atf_add_test_case zfs_acl_tar_002_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_001_neg.ksh b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_001_neg.ksh
new file mode 100644
index 000000000000..4073cecae1c0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_001_neg.ksh
@@ -0,0 +1,146 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_chmod_001_neg
+#
+# DESCRIPTION:
+# Verify 1) Illegal options to chmod should fail.
+# 2) Delete all the ACE will lead to fail.
+# 3) Add ACE exceed 1024 will cause to fail.
+#
+# STRATEGY:
+# 1. Loop root and non-root users
+# 2. Verify all kinds of illegal option will lead to chmod failed.
+# 3. Verify 'chmod A0-' will fail when try to delete all the ACE.
+# 4. Verify 'chmod A+' will succeed when the ACE number exceed 1024.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-10-14)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify illegal operating to ACL, it will fail."
+log_onexit cleanup
+
+test_requires ZFS_ACL
+
+function err_opts #node
+{
+ typeset A_opts="+A@ -A#- +A% =A^ =A# =A@ +A#\ asd \
+ A+@ A-#- A+% A=^ A=# A=@ A+#"
+
+ log_note "Illegal option to chmod should fail."
+ for A in ${A_opts[@]}; do
+ log_mustnot usr_exec $CHMOD ${A}owner@:read_data:allow $node
+ log_mustnot usr_exec $CHMOD A+ asd owner@:execute:deny $node
+ done
+
+ typeset type_opts="everyone groups owner user@ users"
+ for tp in ${type_opts[@]}; do
+ log_mustnot usr_exec $CHMOD A+$tp:read_data:deny $node
+ done
+
+ return 0
+}
+
+function del_all_ACE #node
+{
+ typeset node=$1
+ typeset -i cnt
+
+ cnt=$(count_ACE $node)
+ while (( cnt > 0 )); do
+ if (( cnt == 1 )); then
+ log_mustnot $CHMOD A0- $node
+ else
+ log_must $CHMOD A0- $node
+ fi
+
+ (( cnt -= 1 ))
+ done
+
+ return 0
+}
+
+function exceed_max_ACE #node
+{
+ typeset node=$1
+ typeset -i max=1024
+ typeset -i cnt
+
+ cnt=$(count_ACE $node)
+
+ # One more ACE exceed the max limitation.
+ (( max = max - cnt + 1 ))
+ while (( max > 0 )); do
+ if (( max == 1 )); then
+ log_mustnot $CHMOD A+owner@:read_data:allow $node
+ else
+ $CHMOD A+owner@:read_data:allow $node
+ if (($? != 0)); then
+ ((cnt = 1024 - max))
+ log_fail "Add No.$cnt ACL item failed."
+ fi
+ fi
+
+ (( max -= 1 ))
+ done
+
+ return 0
+}
+
+typeset node
+typeset func_name="err_opts del_all_ACE exceed_max_ACE"
+
+for usr in "root" "$ZFS_ACL_STAFF1"; do
+ log_must set_cur_usr $usr
+
+ for node in $testfile $testdir; do
+ log_must usr_exec $TOUCH $testfile
+ log_must usr_exec $MKDIR $testdir
+
+ for func in $func_name; do
+ log_must eval "$func $node"
+ done
+
+ log_must usr_exec $RM -rf $testfile $testdir
+ done
+done
+
+log_pass "Verify illegal operating to ACL passed."
diff --git a/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_002_pos.ksh b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_002_pos.ksh
new file mode 100644
index 000000000000..7cdc135ef5a1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_002_pos.ksh
@@ -0,0 +1,77 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_chmod_002_pos
+#
+# DESCRIPTION:
+# Verify acl after upgrading.
+# STRATEGY:
+# 1. Create a low version fs.
+# 2. Create a directory and chmod it.
+# 3. Upgrade the fs.
+# 4. Create a file under the directory and list it.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-06-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function acl_upgrade_cleannup
+{
+ datasetexists $ACL_UPGRADE_FS && \
+ log_must $ZFS destroy -rR $ACL_UPGRADE_FS
+}
+
+log_assert "Verify acl after upgrading."
+log_onexit acl_upgrade_cleannup
+
+ACL_UPGRADE_FS=$TESTPOOL/acl_upgrade_fs.${TESTCASE_ID}
+
+test_requires ZFS_ACL
+
+log_must $ZFS create -o version=2 $ACL_UPGRADE_FS
+mntpnt=$(get_prop mountpoint $ACL_UPGRADE_FS)
+log_must $MKDIR $mntpnt/dir.${TESTCASE_ID}
+log_must $CHMOD A+owner@:rwxp:f:allow,group@:rwxp:f:allow $mntpnt/dir.${TESTCASE_ID}
+log_must $ZFS upgrade $ACL_UPGRADE_FS
+log_must $TOUCH $mntpnt/dir.${TESTCASE_ID}/file.${TESTCASE_ID}
+log_must eval "$LS -V $mntpnt/dir.${TESTCASE_ID}/file.${TESTCASE_ID} > /dev/null 2>&1"
+
+log_pass "Verify acl after upgrading."
+
diff --git a/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_aclmode_001_pos.ksh b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_aclmode_001_pos.ksh
new file mode 100644
index 000000000000..09e75bab7938
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_aclmode_001_pos.ksh
@@ -0,0 +1,485 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_chmod_aclmode_001_pos
+#
+# DESCRIPTION:
+# Verify chmod have correct behaviour to directory and file when
+# filesystem has the different aclmode setting
+#
+# STRATEGY:
+# 1. Loop super user and non-super user to run the test case.
+# 2. Create basedir and a set of subdirectores and files within it.
+# 3. Separately chmod basedir with different aclmode options,
+# combine with the variable setting of aclmode:
+# "discard", "groupmask", or "passthrough".
+# 4. Verify each directories and files have the correct access control
+# capability.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-03-02)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+test_requires ZFS_ACL
+
+function cleanup
+{
+ # Cleanup tarfile & basedir.
+
+ (( ${#cwd} != 0 )) && cd $cwd
+
+ if [[ -f $TARFILE ]]; then
+ log_must $RM -f $TARFILE
+ fi
+
+ if [[ -d $basedir ]]; then
+ log_must $RM -rf $basedir
+ fi
+}
+
+log_assert "Verify chmod have correct behaviour to directory and file when " \
+ "filesystem has the different aclmode setting."
+log_onexit cleanup
+
+# Define aclmode flag
+set -A aclmode_flag discard groupmask passthrough
+
+set -A ace_prefix "user:$ZFS_ACL_OTHER1" \
+ "user:$ZFS_ACL_OTHER2" \
+ "group:$ZFS_ACL_STAFF_GROUP" \
+ "group:$ZFS_ACL_OTHER_GROUP"
+
+set -A argv "000" "444" "644" "777" "755" "231" "562" "413"
+
+set -A ace_file_preset "read_data" \
+ "write_data" \
+ "append_data" \
+ "execute" \
+ "read_data/write_data" \
+ "read_data/write_data/append_data" \
+ "write_data/append_data" \
+ "read_data/execute" \
+ "write_data/append_data/execute" \
+ "read_data/write_data/append_data/execute"
+
+# Defile the based directory and file
+basedir=$TESTDIR/basedir; ofile=$basedir/ofile; odir=$basedir/odir
+nfile=$basedir/nfile; ndir=$basedir/ndir
+
+TARFILE=$TESTDIR/tarfile
+
+# Verify all the node have expected correct access control
+allnodes="$nfile $ndir"
+
+#
+# According to the original bits, the input ACE access and ACE type, return the
+# expect bits after 'chmod A0{+|=}'.
+#
+# $1 isdir indicate if the target is a directory
+# $1 bits which was make up of three bit 'rwx'
+# $2 bits_limit which was make up of three bit 'rwx'
+# $3 ACE access which is read_data, write_data or execute
+# $4 ACE type which is allow or deny
+#
+function cal_bits #isdir bits bits_limit acl_access ctrl
+{
+ typeset -i isdir=$1
+ typeset -i bits=$2
+ typeset -i bits_limit=$3
+ typeset acl_access=$4
+ typeset -i ctrl=${5:-0}
+ typeset flagr=0; flagw=0; flagx=0
+ typeset tmpstr
+
+ if (( ctrl == 0 )); then
+ if (( (( bits & 4 )) == 0 )); then
+ flagr=1
+ fi
+ if (( (( bits & 2 )) == 0 )); then
+ flagw=1
+ fi
+ if (( (( bits & 1 )) == 0 )); then
+ flagx=1
+ fi
+ else
+ #
+ # Tricky here:
+ # (1) flagr is always set to be 1,
+ # (2) flagw & flagx is set to be 0 only while
+ # bits_limit has lower permissions than bits
+ #
+
+ flagr=1
+ flagw=1
+ flagx=1
+
+ if (( (( bits & 2 )) != 0 )) && \
+ (( (( bits_limit & 2 )) == 0 )) ; then
+ flagw=0
+ fi
+ if (( (( bits & 1 )) != 0 )) && \
+ (( (( bits_limit & 1 )) == 0 )) ; then
+ flagx=0
+ fi
+ fi
+
+ if (( flagr != 0 )); then
+ if [[ $acl_access == *"read_data"* ]]; then
+ if (( isdir == 0 )) ; then
+ tmpstr=${tmpstr}/read_data
+ else
+ tmpstr=${tmpstr}/list_directory/read_data
+ fi
+ fi
+ fi
+
+ if (( flagw != 0 )); then
+ if [[ $acl_access == *"write_data"* ]]; then
+ if (( isdir == 0 )); then
+ tmpstr=${tmpstr}/write_data
+ else
+ tmpstr=${tmpstr}/add_file/write_data
+ fi
+ fi
+
+ if [[ $acl_access == *"append_data"* ]]; then
+ if (( isdir == 0 )); then
+ tmpstr=${tmpstr}/append_data
+ else
+ tmpstr=${tmpstr}/add_subdirectory/append_data
+ fi
+ fi
+ fi
+ if (( flagx != 0 )); then
+ [[ $acl_access == *"execute"* ]] && \
+ tmpstr=${tmpstr}/execute
+ fi
+
+ tmpstr=${tmpstr#/}
+
+ $ECHO "$tmpstr"
+}
+
+#
+# To translate an ace if the node is dir
+#
+# $1 isdir indicate if the target is a directory
+# $2 acl to be translated
+#
+function translate_acl #isdir acl
+{
+ typeset -i isdir=$1
+ typeset acl=$2
+ typeset who prefix acltemp action
+
+ if (( isdir != 0 )); then
+ who=${acl%%:*}
+ prefix=$who
+ acltemp=${acl#*:}
+ acltemp=${acltemp%%:*}
+ prefix=$prefix:$acltemp
+ action=${acl##*:}
+
+ acl=$prefix:$(cal_bits $isdir 7 7 $acl 1):$action
+ fi
+ $ECHO "$acl"
+}
+
+#
+# According to inherited flag, verify subdirectories and files within it has
+# correct inherited access control.
+#
+function verify_aclmode #<aclmode> <node> <newmode>
+{
+ # Define the nodes which will be affected by inherit.
+ typeset aclmode=$1
+ typeset node=$2
+ typeset newmode=$3
+
+ # count: the ACE item to fetch
+ # pass: to mark if the current ACE should apply to the target
+ # passcnt: counter, if it achieves to maxnumber,
+ # then no additional ACE should apply.
+ # step: indicate if the ACE be split during aclmode.
+
+ typeset -i count=0 pass=0 passcnt=0 step=0
+ typeset -i bits=0 obits=0 bits_owner=0 isdir=0
+
+ if [[ -d $node ]]; then
+ (( isdir = 1 ))
+ fi
+
+ (( i = maxnumber - 1 ))
+ count=0
+ passcnt=0
+ while (( i >= 0 )); do
+ pass=0
+ step=0
+ expect1=${acls[$i]}
+ expect2=""
+
+ #
+ # aclmode=passthrough,
+ # no changes will be made to the ACL other than
+ # generating the necessary ACL entries to represent
+ # the new mode of the file or directory.
+ #
+ # aclmode=discard,
+ # delete all ACL entries that don't represent
+ # the mode of the file.
+ #
+ # aclmode=groupmask,
+ # reduce user or group permissions. The permissions are
+ # reduced, such that they are no greater than the group
+ # permission bits, unless it is a user entry that has the
+ # same UID as the owner of the file or directory.
+ # Then, the ACL permissions are reduced so that they are
+ # no greater than owner permission bits.
+ #
+
+ case $aclmode in
+ passthrough)
+ expect1=$(translate_acl $isdir $expect1)
+ ;;
+ groupmask)
+ if [[ $expect1 == *":allow" ]]; then
+ expect2=$expect1
+ who=${expect1%%:*}
+ prefix=$who
+ acltemp=""
+ reduce=0
+
+ # To determine the mask bits
+ # according to the entry type.
+
+ case $who in
+ owner@)
+ pos=1
+ ;;
+ group@)
+ pos=2
+ ;;
+ everyone@)
+ pos=3
+ ;;
+ user)
+ acltemp=${expect1#*:}
+ acltemp=${acltemp%%:*}
+ owner=$(get_owner $node)
+ group=$(get_group $node)
+ if [[ $acltemp == $owner ]]; then
+ pos=1
+ else
+ pos=2
+ fi
+ prefix=$prefix:$acltemp
+ ;;
+ group)
+ acltemp=${expect1#*:}
+ acltemp=${acltemp%%:*}
+ pos=2
+ prefix=$prefix:$acltemp
+ reduce=1
+ ;;
+ esac
+
+ obits=$(get_substr $newmode $pos 1)
+ (( bits = obits ))
+ #
+ # permission should no greater than the group permission bits
+ #
+ if (( reduce != 0 )); then
+ (( bits &= $(get_substr $newmode 2 1) ))
+
+ # The ACL permissions are reduced so that they are
+ # no greater than owner permission bits.
+
+ (( bits_owner = $(get_substr $newmode 1 1) ))
+ (( bits &= bits_owner ))
+ fi
+
+ if (( bits < obits )) && [[ -n $acltemp ]]; then
+ expect2=$prefix:$(cal_bits $isdir $obits $bits_owner $expect2 1):allow
+ else
+ expect2=$prefix:$(cal_bits $isdir $obits $obits $expect2 1):allow
+
+ fi
+
+ priv=$(cal_bits $isdir $obits $bits_owner $expect2 0)
+ expect1=$prefix:$priv:deny
+ step=1
+ else
+ expect1=$(translate_acl $isdir $expect1)
+ fi
+ ;;
+ discard)
+ passcnt=maxnumber
+ break
+ ;;
+ esac
+
+ if (( pass == 0 )) ; then
+ # Get the first ACE to do comparison
+
+ aclcur=$(get_ACE $node $count)
+ aclcur=${aclcur#$count:}
+ if [[ -n $expect1 && $expect1 != $aclcur ]]; then
+ $LS -vd $node
+ log_fail "$i #$count " \
+ "ACE: $aclcur, expect to be " \
+ "$expect1"
+ fi
+
+ # Get the second ACE (if should have) to do comparison
+
+ if (( step > 0 )); then
+ (( count = count + step ))
+
+ aclcur=$(get_ACE $node $count)
+ aclcur=${aclcur#$count:}
+ if [[ -n $expect2 && \
+ $expect2 != $aclcur ]]; then
+
+ $LS -vd $node
+ log_fail "$i #$count " \
+ "ACE: $aclcur, expect to be " \
+ "$expect2"
+ fi
+ fi
+ (( count = count + 1 ))
+ fi
+ (( i = i - 1 ))
+ done
+
+ #
+ # If there's no any ACE be checked, it should be identify as
+ # an normal file/dir, verify it.
+ #
+
+ if (( passcnt == maxnumber )); then
+ if [[ -d $node ]]; then
+ compare_acls $node $odir
+ elif [[ -f $node ]]; then
+ compare_acls $node $ofile
+ fi
+
+ if [[ $? -ne 0 ]]; then
+ $LS -vd $node
+ log_fail "Unexpect acl: $node, $aclmode ($newmode)"
+ fi
+ fi
+}
+
+
+
+typeset -i maxnumber=0
+typeset acl
+typeset target
+
+cwd=$PWD
+cd $TESTDIR
+
+for mode in "${aclmode_flag[@]}"; do
+
+ #
+ # Set different value of aclmode
+ #
+
+ log_must $ZFS set aclmode=$mode $TESTPOOL/$TESTFS
+
+ for user in root $ZFS_ACL_STAFF1; do
+ log_must set_cur_usr $user
+
+ log_must usr_exec $MKDIR $basedir
+
+ log_must usr_exec $MKDIR $odir
+ log_must usr_exec $TOUCH $ofile
+ log_must usr_exec $MKDIR $ndir
+ log_must usr_exec $TOUCH $nfile
+
+ for obj in $allnodes ; do
+ maxnumber=0
+ for preset in "${ace_file_preset[@]}"; do
+ for prefix in "${ace_prefix[@]}"; do
+ acl=$prefix:$preset
+
+ case $(( maxnumber % 2 )) in
+ 0)
+ acl=$acl:deny
+ ;;
+ 1)
+ acl=$acl:allow
+ ;;
+ esac
+
+ #
+ # Place on the target should succeed.
+ #
+ log_must usr_exec $CHMOD A+$acl $obj
+ acls[$maxnumber]=$acl
+
+ (( maxnumber = maxnumber + 1 ))
+ done
+ done
+
+ # Archive the file and directory
+ log_must $TAR cpf@ $TARFILE basedir
+
+ if [[ -d $obj ]]; then
+ target=$odir
+ elif [[ -f $obj ]]; then
+ target=$ofile
+ fi
+
+ for newmode in "${argv[@]}" ; do
+ log_must usr_exec $CHMOD $newmode $obj
+ log_must usr_exec $CHMOD $newmode $target
+ verify_aclmode $mode $obj $newmode
+
+ # Restore the tar archive
+ log_must $TAR xpf@ $TARFILE
+ done
+ done
+
+ log_must usr_exec $RM -rf $basedir $TARFILE
+ done
+done
+
+log_pass "Verify chmod behaviour co-op with aclmode setting passed."
diff --git a/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_compact_001_pos.ksh b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_compact_001_pos.ksh
new file mode 100644
index 000000000000..3f247d869a1c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_compact_001_pos.ksh
@@ -0,0 +1,269 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+. $STF_SUITE/tests/acl/cifs/cifs.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_chmod_compact_001_pos
+#
+# DESCRIPTION:
+# chmod A{+|-|=} could set compact ACL correctly.
+#
+# STRATEGY:
+# 1. Loop root and non-root user.
+# 2. Get the random compact ACL string.
+# 4. Separately chmod +|-|=
+# 5. Check compact ACL display as expected
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-08-11)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+test_requires ZFS_ACL
+
+log_assert "chmod A{+|=} should set compact ACL correctly."
+log_onexit cleanup
+
+set -A a_flag owner group everyone
+set -A a_access r w x p d D a A R W c C o s
+set -A a_inherit_object f d
+set -A a_inherit_strategy i n
+set -A a_type allow deny
+
+typeset cifs=""
+
+if cifs_supported ; then
+ cifs="true"
+fi
+
+#
+# Get a random item from an array.
+#
+# $1 the base set
+#
+function random_select #array_name
+{
+ typeset arr_name=$1
+ typeset -i ind
+
+ eval typeset -i cnt=\${#${arr_name}[@]}
+ (( ind = $RANDOM % cnt ))
+
+ eval print \${${arr_name}[$ind]}
+}
+
+#
+# Create a random string according to array name, the item number and
+# separated tag.
+#
+# $1 array name where the function get the elements
+# $2 the items number which you want to form the random string
+# $3 the separated tag
+#
+function form_random_str #<array_name> <count> <sep>
+{
+ typeset arr_name=$1
+ typeset -i count=${2:-1}
+ typeset sep=${3:-""}
+
+ typeset str=""
+ while (( count > 0 )); do
+ str="${str}$(random_select $arr_name)${sep}"
+
+ (( count -= 1 ))
+ done
+
+ print $str
+}
+
+#
+# According to the input ACE access,ACE type, and inherit flags, return the
+# expect compact ACE that could be used by chmod A0{+|=}'.
+#
+# $1 ACE flag which is owner, group, or everyone
+# $2 ACE access generated by the element of a_access
+# $3 ACE inherit_object generated by the element of a_inherit_object
+# $4 ACE inherit_strategy generated by the element of a_inherit_strategy
+# $5 ACE type which is allow or deny
+#
+function cal_ace # acl_flag acl_access \
+ # acl_inherit_object acl_inherit_strategy acl_type
+{
+ typeset acl_flag=$1
+ typeset acl_access=$2
+ typeset acl_inherit_object=$3
+ typeset acl_inherit_strategy=$4
+ typeset acl_type=$5
+
+ tmp_ace=${acl_flag}@:
+
+ for element in ${a_access[@]} ; do
+ if [[ $acl_access == *"$element"* ]]; then
+ tmp_ace="${tmp_ace}${element}"
+ else
+ tmp_ace="${tmp_ace}-"
+ fi
+ done
+ tmp_ace=${tmp_ace}:
+
+ for element in ${a_inherit_object[@]} ; do
+ if [[ $acl_inherit_object == *"$element"* ]]; then
+ tmp_ace="${tmp_ace}${element}"
+ else
+ tmp_ace="${tmp_ace}-"
+ fi
+ done
+ for element in ${a_inherit_strategy[@]} ; do
+ if [[ $acl_inherit_strategy == *"$element"* ]]; then
+ tmp_ace="${tmp_ace}${element}"
+ else
+ tmp_ace="${tmp_ace}-"
+ fi
+ done
+
+ if [[ -n $cifs ]]; then
+ tmp_ace=${tmp_ace}---:${acl_type}
+ else
+ tmp_ace=${tmp_ace}--:${acl_type}
+ fi
+
+ print "${tmp_ace}"
+}
+
+#
+# Check if chmod set the compact ACE correctly.
+#
+function check_test_result # node acl_flag acl_access \
+ # acl_inherit_object acl_inherit_strategy acl_type
+{
+ typeset node=$1
+ typeset acl_flag=$2
+ typeset acl_access=$3
+ typeset acl_inherit_object=$4
+ typeset acl_inherit_strategy=$5
+ typeset acl_type=$6
+
+ typeset expect_ace=$(cal_ace "$acl_flag" "$acl_access" \
+ "$acl_inherit_object" "$acl_inherit_strategy" "$acl_type")
+
+ typeset cur_ace=$(get_ACE $node 0 "compact")
+
+ if [[ $cur_ace != $expect_ace ]]; then
+ log_fail "FAIL: Current map($cur_ace) != \
+ expected ace($expect_ace)"
+ fi
+}
+
+function test_chmod_map #<node>
+{
+ typeset node=$1
+ typeset acl_flag acl_access acl_inherit_object acl_inherit_strategy acl_type
+ typeset -i cnt
+
+ if (( ${#node} == 0 )); then
+ log_fail "FAIL: file name or directroy name is not defined."
+ fi
+
+ # Get ACL flag, access & type
+ acl_flag=$(form_random_str a_flag)
+ (( cnt = ($RANDOM % ${#a_access[@]}) + 1 ))
+ acl_access=$(form_random_str a_access $cnt)
+ acl_access=${acl_access%/}
+ acl_type=$(form_random_str a_type 1)
+
+ acl_spec=${acl_flag}@:${acl_access}
+ if [[ -d $node ]]; then
+ # Get ACL inherit_object & inherit_strategy
+ (( cnt = ($RANDOM % ${#a_inherit_object[@]}) + 1 ))
+ acl_inherit_object=$(form_random_str a_inherit_object $cnt)
+ (( cnt = ($RANDOM % ${#a_inherit_strategy[@]}) + 1 ))
+ acl_inherit_strategy=$(form_random_str a_inherit_strategy $cnt)
+ acl_spec=${acl_spec}:${acl_inherit_object}${acl_inherit_strategy}
+ fi
+ acl_spec=${acl_spec}:${acl_type}
+
+ # Set the initial map and back the initial ACEs
+ typeset orig_ace=$TMPDIR/orig_ace.${TESTCASE_ID}
+ typeset cur_ace=$TMPDIR/cur_ace.${TESTCASE_ID}
+
+ for operator in "A0+" "A0="; do
+ log_must usr_exec eval "$LS -Vd $node > $orig_ace"
+
+ # To "A=", firstly add one ACE which can't modify map
+ if [[ $operator == "A0=" ]]; then
+ log_must $CHMOD A0+user:$ZFS_ACL_OTHER1:execute:deny \
+ $node
+ fi
+ log_must usr_exec $CHMOD ${operator}${acl_spec} $node
+
+ check_test_result \
+ "$node" "$acl_flag" "$acl_access" \
+ "$acl_inherit_object" "$acl_inherit_strategy" \
+ "$acl_type"
+
+ # Check "chmod A-"
+ log_must usr_exec $CHMOD A0- $node
+ log_must usr_exec eval "$LS -Vd $node > $cur_ace"
+
+ $DIFF $orig_ace $cur_ace
+ [[ $? -ne 0 ]] && \
+ log_fail "FAIL: 'chmod A-' failed."
+ done
+
+ [[ -f $orig_ace ]] && log_must usr_exec $RM -f $orig_ace
+ [[ -f $cur_ace ]] && log_must usr_exec $RM -f $cur_ace
+}
+
+for user in root $ZFS_ACL_STAFF1; do
+ set_cur_usr $user
+
+ typeset -i loop_cnt=2
+ while (( loop_cnt > 0 )); do
+ log_must usr_exec $TOUCH $testfile
+ test_chmod_map $testfile
+ log_must $RM -f $testfile
+
+ log_must usr_exec $MKDIR $testdir
+ test_chmod_map $testdir
+ log_must $RM -rf $testdir
+
+ (( loop_cnt -= 1 ))
+ done
+done
+
+log_pass "chmod A{+|=} set compact ACL correctly."
diff --git a/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_delete_001_pos.ksh b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_delete_001_pos.ksh
new file mode 100644
index 000000000000..29f3fb3e11f3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_delete_001_pos.ksh
@@ -0,0 +1,316 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_chmod_delete_001_pos
+#
+# DESCRIPTION:
+# Verify that the combined delete_child/delete permission for
+# owner/group/everyone are correct.
+#
+# -------------------------------------------------------
+# | Parent Dir | Target Object Permissions |
+# | permissions | |
+# -------------------------------------------------------
+# | | ACL Allows | ACL Denies| Delete |
+# | | Delete | Delete | unspecified|
+# -------------------------------------------------------
+# | ACL Allows | Permit | Permit | Permit |
+# | DELETE_CHILD | |
+# -------------------------------------------------------
+# | ACL Denies | Permit | Deny | Deny |
+# | DELETE_CHILD | | | |
+# -------------------------------------------------------
+# | ACL specifies | | | |
+# | only allows | Permit | Permit | Permit |
+# | write and | | | |
+# | execute | | | |
+# -------------------------------------------------------
+# | ACL denies | | | |
+# | write and | Permit | Deny | Deny |
+# | execute | | | |
+# -------------------------------------------------------
+#
+# STRATEGY:
+# 1. Create file and directory in zfs filesystem
+# 2. Set special ACE combination to the file and directory
+# 3. Try to remove the file
+# 4. Verify that combined permissions for owner/group/everyone are correct.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-10-24)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ [[ ! -e $TESTDIR/$ARCHIVEFILE ]] && return 0
+
+ if [[ ! -e $target ]]; then
+ log_must $TAR xpf $TESTDIR/$ARCHIVEFILE
+ fi
+
+ (( ${#cwd} != 0 )) && cd $cwd
+ cleanup_test_files $TESTDIR/basedir
+ log_must $RM -f $TESTDIR/$ARCHIVEFILE
+ return 0
+}
+
+#owner@ group group_users other_users
+set -A users \
+"root" "root" "$ZFS_ACL_ADMIN" "$ZFS_ACL_OTHER1" \
+"$ZFS_ACL_STAFF1" "$ZFS_ACL_STAFF_GROUP" "$ZFS_ACL_STAFF2" "$ZFS_ACL_OTHER1"
+
+set -A access_parent \
+ "delete_child:allow" \
+ "delete_child:deny" \
+ "write_data:allow" \
+ "write_data:deny" \
+ "delete_child:deny write_data:allow" \
+ "delete_child:allow write_data:deny"
+
+set -A access_target \
+ "delete:allow" \
+ "delete:deny" \
+ ""
+
+set -A a_flag "owner@" "group@" "everyone@" "user:$ZFS_ACL_STAFF1"
+
+log_assert "Verify that the combined delete_child/delete permission for" \
+ "owner/group/everyone are correct."
+log_onexit cleanup
+
+function operate_node #user node
+{
+ typeset user=$1
+ typeset node=$2
+ typeset ret
+
+ if [[ $user == "" || $node == "" ]]; then
+ log_fail "user, node are not defined."
+ fi
+ if [[ -d $node ]]; then
+ chgusr_exec $user $RM -rf $node ; ret=$?
+ else
+ chgusr_exec $user $RM -f $node ; ret=$?
+ fi
+
+ if [[ -e $node ]]; then
+ if [[ $ret -eq 0 ]]; then
+ log_note "$node not removed, but return code is 0."
+ return 1
+ fi
+ else
+ log_must $TAR xpf $TESTDIR/$ARCHIVEFILE
+ if [[ $ret -ne 0 ]]; then
+ log_note "$node removed, but return code is $ret."
+ return 1
+ fi
+ fi
+ return $ret
+}
+
+function logname #acl_parent acl_target user
+{
+ typeset acl_parent=$1
+ typeset acl_target=$2
+ typeset user=$3
+
+ # To super user, read and write deny permission was override.
+ if [[ $user == root || $acl_target == *:allow ]]; then
+ print "log_must"
+ elif [[ $acl_parent == *"delete_child"* ]]; then
+ if [[ $acl_parent == *"delete_child:allow"* ]]; then
+ print "log_must"
+ else
+ print "log_mustnot"
+ fi
+ elif [[ $acl_parent == *"write_data"* ]]; then
+ if [[ $acl_parent == *"write_data:allow"* ]]; then
+ print "log_must"
+ else
+ print "log_mustnot"
+ fi
+ else
+ print "log_mustnot"
+ fi
+}
+
+function check_chmod_results #node flag acl_parent acl_target g_usr o_usr
+{
+ typeset node=$1
+ typeset flag=$2
+ typeset acl_parent=$3
+ typeset acl_target=$2:$4
+ typeset g_usr=$5
+ typeset o_usr=$6
+ typeset log acl_tmp
+
+ for acl in $acl_parent ; do
+ acl_tmp="$2:$acl $acl_tmp"
+ done
+ acl_parent=$acl_tmp
+
+ if [[ $flag == "owner@" || $flag == "everyone@" ]]; then
+ log=$(logname "$acl_parent" $acl_target $ZFS_ACL_CUR_USER)
+ $log operate_node $ZFS_ACL_CUR_USER $node
+ fi
+ if [[ $flag == "group@" || $flag == "everyone@" ]]; then
+ log=$(logname "$acl_parent" $acl_target $g_usr)
+ $log operate_node $g_usr $node
+ fi
+ if [[ $flag == "everyone@" ]]; then
+ log=$(logname "$acl_parent" $acl_target $o_usr)
+ $log operate_node $o_usr $node
+ fi
+ if [[ $flag == "user:"* ]]; then
+ typeset user=${flag#user:}
+ log=$(logname "$acl_parent" $acl_target $user)
+ $log operate_node $user $node
+ fi
+}
+
+function test_chmod_basic_access #node g_usr o_usr
+{
+ typeset node=${1%/}
+ typeset g_usr=$2
+ typeset o_usr=$3
+ typeset flag acl_p acl_t parent
+ typeset -i i=0
+
+ parent=${node%/*}
+
+ for flag in ${a_flag[@]}; do
+ for acl_p in "${access_parent[@]}"; do
+ i=0
+ for acl in $acl_p ; do
+ log_must usr_exec $CHMOD A+$flag:$acl $parent
+ (( i = i + 1))
+ done
+
+ for acl_t in "${access_target[@]}"; do
+ [[ -n $acl_t ]] && \
+ log_must usr_exec $CHMOD A+$flag:$acl_t $node
+
+ log_must $TAR cpf $TESTDIR/$ARCHIVEFILE basedir
+
+ check_chmod_results "$node" "$flag" \
+ "$acl_p" "$acl_t" "$g_usr" "$o_usr"
+
+ [[ -n $acl_t ]] && \
+ log_must usr_exec $CHMOD A0- $node
+ done
+
+ while (( i > 0 )); do
+ log_must usr_exec $CHMOD A0- $parent
+ (( i = i - 1 ))
+ done
+ done
+ done
+}
+
+function setup_test_files #base_node user group
+{
+ typeset base_node=$1
+ typeset user=$2
+ typeset group=$3
+
+ cleanup_test_files $base_node
+
+ log_must $MKDIR -p $base_node
+ log_must $CHOWN $user:$group $base_node
+
+ log_must set_cur_usr $user
+
+ # Prepare all files/sub-dirs for testing.
+ file0=$base_node/testfile_rm
+ dir0=$base_node/testdir_rm
+
+ log_must usr_exec $TOUCH $file0
+ log_must usr_exec $CHMOD 444 $file0
+
+ log_must usr_exec $MKDIR -p $dir0
+ log_must usr_exec $CHMOD 444 $dir0
+
+ log_must usr_exec $CHMOD 555 $base_node
+ return 0
+}
+
+function cleanup_test_files #base_node
+{
+ typeset base_node=$1
+
+ if [[ -d $base_node ]]; then
+ log_must $RM -rf $base_node
+ elif [[ -e $base_node ]]; then
+ log_must $RM -f $base_node
+ fi
+
+ return 0
+}
+
+typeset cwd=$PWD
+typeset ARCHIVEFILE=archive.tar
+
+test_requires ZFS_ACL
+
+typeset -i i=0
+typeset -i j=0
+typeset target
+cd $TESTDIR
+while (( i < ${#users[@]} )); do
+ setup_test_files $TESTDIR/basedir ${users[i]} ${users[((i+1))]}
+
+ j=0
+ while (( j < 1 )); do
+ eval target=\$file$j
+ test_chmod_basic_access $target \
+ "${users[((i+2))]}" "${users[((i+3))]}"
+
+ eval target=\$dir$j
+ test_chmod_basic_access $target \
+ "${users[((i+2))]}" "${users[((i+3))]}"
+
+ (( j = j + 1 ))
+ done
+
+ (( i += 4 ))
+done
+
+log_pass "Verify that the combined delete_child/delete permission for" \
+ "owner/group/everyone are correct."
diff --git a/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_inherit_001_pos.ksh b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_inherit_001_pos.ksh
new file mode 100644
index 000000000000..54542339970d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_inherit_001_pos.ksh
@@ -0,0 +1,188 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_chmod_inherit_001_pos
+#
+# DESCRIPTION:
+# Verify chmod have correct behaviour to directory and file when setting
+# different inherit strategy to them.
+#
+# STRATEGY:
+# 1. Loop super user and non-super user to run the test case.
+# 2. Create basedir and a set of subdirectores and files within it.
+# 3. Separately chmod basedir with different inherite options.
+# 4. Then create nested directories and files like the following.
+#
+# _ odir4
+# |_ ofile4
+# _ odir3 _|
+# |_ ofile3
+# _ odir1 _|
+# |_ ofile2
+# basefile |
+# chmod --> basedir -|
+# |_ nfile1
+# |_ ndir1 _
+# |_ nfile2
+# |_ ndir2 _
+# |_ nfile3
+# |_ ndir3
+#
+# 5. Verify each directories and files have the correct access control
+# capability.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-11-15)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if [[ -f $basefile ]]; then
+ log_must $RM -f $basefile
+ fi
+ if [[ -d $basedir ]]; then
+ log_must $RM -rf $basedir
+ fi
+}
+
+log_assert "Verify chmod have correct behaviour to directory and file when " \
+ "setting different inherit strategies to them."
+log_onexit cleanup
+
+# Define inherit flag
+set -A object_flag file_inherit dir_inherit file_inherit/dir_inherit
+set -A strategy_flag "" inherit_only no_propagate inherit_only/no_propagate
+
+# Defile the based directory and file
+basedir=$TESTDIR/basedir; basefile=$TESTDIR/basefile
+
+test_requires ZFS_ACL
+
+# Define the existed files and directories before chmod
+odir1=$basedir/odir1; odir2=$odir1/odir2; odir3=$odir2/odir3
+ofile1=$basedir/ofile1; ofile2=$odir1/ofile2; ofile3=$odir2/ofile3
+
+# Define the files and directories will be created after chmod
+ndir1=$basedir/ndir1; ndir2=$ndir1/ndir2; ndir3=$ndir2/ndir3
+nfile1=$basedir/nfile1; nfile2=$ndir1/nfile2; nfile3=$ndir2/nfile3
+
+# Verify all the node have expected correct access control
+allnodes="$basedir $ndir1 $ndir2 $ndir3 $nfile1 $nfile2 $nfile3"
+allnodes="$allnodes $odir1 $odir2 $odir3 $ofile1 $ofile2 $ofile3"
+
+#
+# According to inherited flag, verify subdirectories and files within it has
+# correct inherited access control.
+#
+function verify_inherit #<object> [strategy]
+{
+ # Define the nodes which will be affected by inherit.
+ typeset inherit_nodes
+ typeset obj=$1
+ typeset str=$2
+
+ log_must usr_exec $MKDIR -p $ndir3
+ log_must usr_exec $TOUCH $nfile1 $nfile2 $nfile3
+
+ # Except for inherit_only, the basedir was affected always.
+ if [[ $str != *"inherit_only"* ]]; then
+ inherit_nodes="$inherit_nodes $basedir"
+ fi
+ # Get the files which inherited ACE.
+ if [[ $obj == *"file_inherit"* ]]; then
+ inherit_nodes="$inherit_nodes $nfile1"
+
+ if [[ $str != *"no_propagate"* ]]; then
+ inherit_nodes="$inherit_nodes $nfile2 $nfile3"
+ fi
+ fi
+ # Get the directores which inherited ACE.
+ if [[ $obj == *"dir_inherit"* ]]; then
+ inherit_nodes="$inherit_nodes $ndir1"
+
+ if [[ $str != *"no_propagate"* ]]; then
+ inherit_nodes="$inherit_nodes $ndir2 $ndir3"
+ fi
+ fi
+
+ for node in $allnodes; do
+ if [[ " $inherit_nodes " == *" $node "* ]]; then
+ log_mustnot chgusr_exec $ZFS_ACL_OTHER1 $LS -vd $node \
+ > /dev/null 2>&1
+ else
+ log_must chgusr_exec $ZFS_ACL_OTHER1 $LS -vd $node \
+ > /dev/null 2>&1
+ fi
+ done
+}
+
+for user in root $ZFS_ACL_STAFF1; do
+ log_must set_cur_usr $user
+
+ for obj in "${object_flag[@]}"; do
+ for str in "${strategy_flag[@]}"; do
+ typeset inh_opt=$obj
+ (( ${#str} != 0 )) && inh_opt=$inh_opt/$str
+ aclspec="A+user:$ZFS_ACL_OTHER1:read_acl:$inh_opt:deny"
+
+ log_must usr_exec $MKDIR $basedir
+ log_must usr_exec $TOUCH $basefile
+ log_must usr_exec $MKDIR -p $odir3
+ log_must usr_exec $TOUCH $ofile1 $ofile2 $ofile3
+
+ #
+ # Inherit flag can only be placed on a directory,
+ # otherwise it will fail.
+ #
+ log_must usr_exec $CHMOD $aclspec $basefile
+
+ #
+ # Place on a directory should succeed.
+ #
+ log_must usr_exec $CHMOD $aclspec $basedir
+
+ verify_inherit $obj $str
+
+ log_must usr_exec $RM -rf $basefile $basedir
+ done
+ done
+done
+
+log_pass "Verify chmod inherit behaviour passed."
diff --git a/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_inherit_002_pos.ksh b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_inherit_002_pos.ksh
new file mode 100644
index 000000000000..862fb0058c5a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_inherit_002_pos.ksh
@@ -0,0 +1,410 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+. $STF_SUITE/tests/acl/cifs/cifs.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_chmod_inherit_002_pos
+#
+# DESCRIPTION:
+# Verify chmod have correct behaviour to directory and file when
+# filesystem has the different aclinherit setting
+#
+# STRATEGY:
+# 1. Loop super user and non-super user to run the test case.
+# 2. Create basedir and a set of subdirectores and files within it.
+# 3. Separately chmod basedir with different inherite options,
+# combine with the variable setting of aclinherit:
+# "discard", "noallow", "secure" or "passthrough".
+# 4. Then create nested directories and files like the following.
+#
+# ofile
+# odir
+# chmod --> basedir -|
+# |_ nfile1
+# |_ ndir1 _
+# |_ nfile2
+# |_ ndir2 _
+# |_ nfile3
+# |_ ndir3
+#
+# 5. Verify each directories and files have the correct access control
+# capability.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-03-02)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ typeset dir
+
+ # Cleanup basedir, compared file and dir.
+
+ if [[ -f $ofile ]]; then
+ log_must $RM -f $ofile
+ fi
+
+ for dir in $odir $basedir ; do
+ if [[ -d $dir ]]; then
+ log_must $RM -rf $dir
+ fi
+ done
+}
+
+log_assert "Verify chmod have correct behaviour to directory and file when " \
+ "filesystem has the different aclinherit setting."
+log_onexit cleanup
+
+# Define inherit flag
+set -A aclinherit_flag discard noallow secure passthrough
+set -A object_flag file_inherit dir_inherit file_inherit/dir_inherit
+set -A strategy_flag "" inherit_only no_propagate inherit_only/no_propagate
+
+typeset ace_prefix1="user:$ZFS_ACL_OTHER1"
+typeset ace_prefix2="user:$ZFS_ACL_OTHER2"
+typeset ace_discard ace_noallow ace_secure ace_passthrough
+typeset ace_secure_new
+
+# Defile the based directory and file
+basedir=$TESTDIR/basedir; ofile=$TESTDIR/ofile; odir=$TESTDIR/odir
+
+test_requires ZFS_ACL
+
+# Define the files and directories will be created after chmod
+ndir1=$basedir/ndir1; ndir2=$ndir1/ndir2; ndir3=$ndir2/ndir3
+nfile1=$basedir/nfile1; nfile2=$ndir1/nfile2; nfile3=$ndir2/nfile3
+
+# Verify all the node have expected correct access control
+allnodes="$ndir1 $ndir2 $ndir3 $nfile1 $nfile2 $nfile3"
+
+typeset cifs=""
+if cifs_supported ; then
+ cifs="true"
+fi
+
+#
+# According to inherited flag, verify subdirectories and files within it has
+# correct inherited access control.
+#
+function verify_inherit #<aclinherit> <object> [strategy]
+{
+ # Define the nodes which will be affected by inherit.
+ typeset inherit_nodes
+ typeset inherit=$1
+ typeset obj=$2
+ typeset str=$3
+
+ # count: the ACE item to fetch
+ # pass: to mark if the current ACE should apply to the target
+ # maxnumber: predefine as 4
+ # passcnt: counter, if it achieves to maxnumber,
+ # then no additional ACE should apply.
+ # isinherit: indicate if the current target is in the inherit list.
+ # step: indicate if the ACE be split during inherit.
+
+ typeset -i count=0 pass=0 passcnt=0 isinherit=0 maxnumber=4 step=0
+
+ log_must usr_exec $MKDIR -p $ndir3
+ log_must usr_exec $TOUCH $nfile1 $nfile2 $nfile3
+
+ # Get the files which inherited ACE.
+ if [[ $obj == *"file_inherit"* ]]; then
+ inherit_nodes="$inherit_nodes $nfile1"
+
+ if [[ $str != *"no_propagate"* ]]; then
+ inherit_nodes="$inherit_nodes $nfile2 $nfile3"
+ fi
+ fi
+ # Get the directores which inherited ACE.
+ if [[ $obj == *"dir_inherit"* ]]; then
+ inherit_nodes="$inherit_nodes $ndir1"
+
+ if [[ $str != *"no_propagate"* ]]; then
+ inherit_nodes="$inherit_nodes $ndir2 $ndir3"
+ fi
+ fi
+
+ for node in $allnodes; do
+ step=0
+ if [[ " $inherit_nodes " == *" $node "* ]]; then
+ isinherit=1
+ if [[ -d $node ]] ; then
+ step=1
+ fi
+ else
+ isinherit=0
+ fi
+
+ i=0
+ count=0
+ passcnt=0
+ while (( i < maxnumber )); do
+ pass=0
+ eval expect1=\$acl$i
+ expect2=$expect1
+
+ #
+ # aclinherit=passthrough,
+ # inherit all inheritable ACL entries without any
+ # modifications made to the ACL entries when they
+ # are inherited.
+ #
+ # aclinherit=secure,
+ # any inheritable ACL entries will remove
+ # write_acl and write_owner permissions when the ACL entry is
+ # inherited.
+ #
+ # aclinherit=noallow,
+ # only inherit inheritable ACE that specify "deny" permissions
+ #
+ # aclinherit=discard
+ # will not inherit any ACL entries
+ #
+
+ case $inherit in
+ passthrough)
+ ;;
+ secure)
+ if [[ $expect1 == *":allow" ]] ; then
+ eval expect2=\$acls$i
+ fi
+ ;;
+ noallow)
+ if [[ $expect1 == *":allow" ]] ; then
+ pass=1
+ (( passcnt = passcnt + 1 ))
+ fi
+ ;;
+ discard)
+ passcnt=maxnumber
+ break
+ ;;
+ esac
+
+ if (( pass == 0 )) ; then
+ acltemp=${expect2%:*}
+ acltemp=${acltemp%:*}
+ aclaction=${expect2##*:}
+
+ if [[ -n $cifs ]]; then
+ expect2=${acltemp}:inherited:${aclaction}
+ else
+ expect2=${acltemp}:${aclaction}
+ fi
+
+ acltemp=${expect1%:*}
+
+ if [[ -d $node ]]; then
+ if [[ $expect1 == *"no_propagate"* ]] ; then
+
+ #
+ # if no_propagate is set,
+ # then clear all inherit flags,
+ # only one ACE should left.
+ #
+
+ step=0
+ expect1=""
+
+ elif [[ $expect1 != *"inherit_only"* ]]; then
+
+ #
+ # directory should append
+ # "inherit_only" if not have
+ #
+
+ if [[ -n $cifs ]]; then
+
+ expect1=${acltemp}/inherit_only/inherited:${aclaction}
+ else
+ expect1=${acltemp}/inherit_only:${aclaction}
+ fi
+ elif [[ -n $cifs ]]; then
+ expect1=${acltemp}/inherited:${aclaction}
+ fi
+
+ #
+ # cleanup the first ACE if the directory
+ # not in inherit list
+ #
+
+ if (( isinherit == 0 )); then
+ expect1=""
+ fi
+ elif [[ -f $node ]] ; then
+ expect1=""
+ fi
+
+ # Get the first ACE to do comparison
+
+ aclcur=$(get_ACE $node $count)
+ aclcur=${aclcur#$count:}
+ if [[ -n $expect1 && $expect1 != $aclcur ]]; then
+ $LS -vd $basedir
+ $LS -vd $node
+ log_fail "$inherit $i #$count " \
+ "ACE: $aclcur, expect to be " \
+ "$expect1"
+ fi
+
+ # Get the second ACE (if should have) to do comparison
+
+ if (( step > 0 )); then
+ (( count = count + step ))
+
+ aclcur=$(get_ACE $node $count)
+ aclcur=${aclcur#$count:}
+ if [[ -n $expect2 && \
+ $expect2 != $aclcur ]]; then
+
+ $LS -vd $basedir
+ $LS -vd $node
+ log_fail "$inherit $i #$count " \
+ "ACE: $aclcur, expect to be " \
+ "$expect2"
+ fi
+ fi
+ (( count = count + 1 ))
+ fi
+ (( i = i + 1 ))
+ done
+
+ #
+ # If there's no any ACE be checked, it should be identify as
+ # an normal file/dir, verify it.
+ #
+
+ if (( passcnt == maxnumber )); then
+ if [[ -d $node ]]; then
+ compare_acls $node $odir
+ elif [[ -f $node ]]; then
+ compare_acls $node $ofile
+ fi
+
+ if [[ $? -ne 0 ]]; then
+ $LS -vd $basedir
+ $LS -vd $node
+ log_fail "Unexpect acl: $node, $inherit ($str)"
+ fi
+ fi
+ done
+}
+
+typeset -i i=0
+typeset acl0 acl1 acl2 acl3
+typeset acls0 acls1 acls2 acls3
+
+#
+# Set aclmode=passthrough to make sure
+# the acl will not change during chmod.
+# A general testing should verify the combination of
+# aclmode/aclinherit works well,
+# here we just simple test them separately.
+#
+
+log_must $ZFS set aclmode=passthrough $TESTPOOL/$TESTFS
+
+for inherit in "${aclinherit_flag[@]}"; do
+
+ #
+ # Set different value of aclinherit
+ #
+
+ log_must $ZFS set aclinherit=$inherit $TESTPOOL/$TESTFS
+
+ for user in root $ZFS_ACL_STAFF1; do
+ log_must set_cur_usr $user
+
+ for obj in "${object_flag[@]}"; do
+ for str in "${strategy_flag[@]}"; do
+ typeset inh_opt=$obj
+ (( ${#str} != 0 )) && inh_opt=$inh_opt/$str
+
+ #
+ # Prepare 4 ACES, which should include :
+ # deny -> to verify "noallow"
+ # write_acl/write_owner -> to verify "secure"
+ #
+
+ acl0="$ace_prefix1:read_xattr/write_acl/write_owner:$inh_opt:deny"
+ acl1="$ace_prefix2:read_xattr/write_acl/write_owner:$inh_opt:allow"
+ acl2="$ace_prefix1:read_xattr:$inh_opt:deny"
+ acl3="$ace_prefix2:read_xattr:$inh_opt:allow"
+
+ #
+ # The ACE filtered by write_acl/write_owner
+ #
+
+ acls0="$ace_prefix1:read_xattr:$inh_opt:deny"
+ acls1="$ace_prefix2:read_xattr:$inh_opt:allow"
+ acls2=$acl2
+ acls3=$acl3
+
+
+ #
+ # Create basedir and tmp dir/file
+ # for comparison.
+ #
+
+ log_note "$user: $CHMOD $acl $basedir"
+ log_must usr_exec $MKDIR $basedir
+ log_must usr_exec $MKDIR $odir
+ log_must usr_exec $TOUCH $ofile
+
+ i=3
+ while (( i >= 0 )); do
+ eval acl=\$acl$i
+
+ #
+ # Place on a directory should succeed.
+ #
+ log_must usr_exec $CHMOD A+$acl $basedir
+
+ (( i = i - 1 ))
+ done
+
+ verify_inherit $inherit $obj $str
+
+ log_must usr_exec $RM -rf $ofile $odir $basedir
+ done
+ done
+ done
+done
+
+log_pass "Verify chmod inherit behaviour co-op with aclinherit setting passed."
diff --git a/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_inherit_003_pos.ksh b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_inherit_003_pos.ksh
new file mode 100644
index 000000000000..15941c5f44d9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_inherit_003_pos.ksh
@@ -0,0 +1,444 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+. $STF_SUITE/tests/acl/cifs/cifs.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_chmod_inherit_003_pos
+#
+# DESCRIPTION:
+# Verify chmod have correct behaviour to directory and file when
+# filesystem has the different aclinherit setting
+#
+# STRATEGY:
+# 1. Loop super user and non-super user to run the test case.
+# 2. Create basedir and a set of subdirectores and files within it.
+# 3. Separately chmod basedir with different inherite options,
+# combine with the variable setting of aclinherit:
+# "discard", "noallow", "secure" or "passthrough".
+# 4. Then create nested directories and files like the following.
+#
+# ofile
+# odir
+# chmod --> basedir -|
+# |_ nfile1
+# |_ ndir1 _
+# |_ nfile2
+# |_ ndir2 _
+# |_ nfile3
+# |_ ndir3
+#
+# 5. Verify each directories and files have the correct access control
+# capability.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ typeset dir
+
+ # Cleanup basedir, compared file and dir.
+
+ if [[ -f $ofile ]]; then
+ log_must $RM -f $ofile
+ fi
+
+ for dir in $odir $basedir ; do
+ if [[ -d $dir ]]; then
+ log_must $RM -rf $dir
+ fi
+ done
+}
+
+log_assert "Verify chmod have correct behaviour to directory and file when " \
+ "filesystem has the different aclinherit setting."
+log_onexit cleanup
+
+# Define inherit flag
+set -A aclinherit_flag discard noallow secure passthrough
+set -A object_flag "f-" "-d" "fd"
+set -A strategy_flag "--" "i-" "-n" "in"
+
+typeset ace_prefix1="owner@"
+typeset ace_prefix2="group@"
+typeset ace_prefix3="everyone@"
+typeset ace_discard ace_noallow ace_secure ace_passthrough
+typeset ace_secure_new
+
+# Defile the based directory and file
+basedir=$TESTDIR/basedir; ofile=$TESTDIR/ofile; odir=$TESTDIR/odir
+
+test_requires ZFS_ACL
+
+# Define the files and directories will be created after chmod
+ndir1=$basedir/ndir1; ndir2=$ndir1/ndir2; ndir3=$ndir2/ndir3
+nfile1=$basedir/nfile1; nfile2=$ndir1/nfile2; nfile3=$ndir2/nfile3
+
+# Verify all the node have expected correct access control
+allnodes="$ndir1 $ndir2 $ndir3 $nfile1 $nfile2 $nfile3"
+
+typeset cifs=""
+if cifs_supported ; then
+ cifs="true"
+fi
+
+#
+# According to inherited flag, verify subdirectories and files within it has
+# correct inherited access control.
+#
+function verify_inherit #<aclinherit> <object> [strategy]
+{
+ # Define the nodes which will be affected by inherit.
+ typeset inherit_nodes
+ typeset inherit=$1
+ typeset obj=$2
+ typeset str=$3
+
+ # count: the ACE item to fetch
+ # pass: to mark if the current ACE should apply to the target
+ # maxnumber: predefine as 4
+ # passcnt: counter, if it achieves to maxnumber,
+ # then no additional ACE should apply.
+ # isinherit: indicate if the current target is in the inherit list.
+ # step: indicate if the ACE be split during inherit.
+
+ typeset -i count=0 pass=0 passcnt=0 isinherit=0 maxnumber=4 step=0
+
+ log_must usr_exec $MKDIR -p $ndir3
+ log_must usr_exec $TOUCH $nfile1 $nfile2 $nfile3
+
+ # Get the files which inherited ACE.
+ if [[ $(get_substr $obj 1 1) == f ]]; then
+ inherit_nodes="$inherit_nodes $nfile1"
+
+ if [[ $(get_substr $str 2 1) != n ]]; then
+ inherit_nodes="$inherit_nodes $nfile2 $nfile3"
+ fi
+ fi
+ # Get the directores which inherited ACE.
+ if [[ $(get_substr $obj 2 1) == d ]]; then
+ inherit_nodes="$inherit_nodes $ndir1"
+
+ if [[ $(get_substr $str 2 1) != n ]]; then
+ inherit_nodes="$inherit_nodes $ndir2 $ndir3"
+ fi
+ fi
+
+ for node in $allnodes; do
+ step=0
+ if [[ " $inherit_nodes " == *" $node "* ]]; then
+ isinherit=1
+ if [[ -d $node ]] ; then
+ step=1
+ fi
+ else
+ isinherit=0
+ fi
+
+ i=0
+ count=0
+ passcnt=0
+ while (( i < maxnumber )); do
+ pass=0
+ eval expect1=\$acl$i
+ expect2=$expect1
+
+ #
+ # aclinherit=passthrough,
+ # inherit all inheritable ACL entries without any
+ # modifications made to the ACL entries when they
+ # are inherited.
+ #
+ # aclinherit=secure,
+ # any inheritable ACL entries will remove
+ # write_acl and write_owner permissions when the ACL entry is
+ # inherited.
+ #
+ # aclinherit=noallow,
+ # only inherit inheritable ACE that specify "deny" permissions
+ #
+ # aclinherit=discard
+ # will not inherit any ACL entries
+ #
+
+ case $inherit in
+ passthrough)
+ if [[ -z $cifs ]]; then
+ break
+ fi
+
+ action=${expect1##*:}
+ expect1=${expect1%:$action}
+ expect1=${expect1%-}
+ expect1=${expect1%I}
+ expect1=${expect1}I:$action
+ ;;
+ secure)
+ eval expect2=\$acls$i
+ ;;
+ noallow)
+ if [[ $expect1 == *":allow" ]] ; then
+ pass=1
+ (( passcnt = passcnt + 1 ))
+ else
+ eval expect2=\$acls$i
+ fi
+ ;;
+ discard)
+ passcnt=maxnumber
+ break
+ ;;
+ esac
+
+ if (( pass == 0 )) ; then
+ acltemp=${expect2%:*}
+ acltemp=${acltemp%:*}
+ aclaction=${expect2##*:}
+
+ if [[ -n $cifs ]]; then
+ expect2=${acltemp}:------I:${aclaction}
+ else
+ expect2=${acltemp}:------:${aclaction}
+ fi
+
+ acltemp=${expect1%:*}
+ inh=${acltemp##*:}
+
+ if [[ -d $node ]]; then
+ if [[ $(get_substr $inh 4 1) == n ]]; then
+
+ #
+ # if no_propagate is set,
+ # then clear all inherit flags,
+ # only one ACE should left.
+ #
+
+ step=0
+ expect1=""
+
+ elif [[ $(get_substr $inh 3 1) != i ]]; then
+
+ #
+ # directory should append
+ # "inherit_only" if not have
+ #
+ acltemp=${acltemp%i*}
+ if [[ -n $cifs ]]; then
+
+ expect1=${acltemp}i---I:${aclaction}
+ else
+ expect1=${acltemp}i---:${aclaction}
+ fi
+ elif [[ -n $cifs ]]; then
+ acltemp=${acltemp%-}
+ acltemp=${acltemp%I}
+ expect1=${acltemp}I:${aclaction}
+ fi
+
+ #
+ # cleanup the first ACE if the directory
+ # not in inherit list
+ #
+
+ if (( isinherit == 0 )); then
+ expect1=""
+ fi
+ elif [[ -f $node ]] ; then
+ expect1=""
+ fi
+
+ # Get the first ACE to do comparison
+
+ aclcur=$(get_ACE $node $count compact)
+ aclcur=${aclcur#$count:}
+ if [[ -n $expect1 && $expect1 != $aclcur ]]; then
+ $LS -Vd $basedir
+ $LS -Vd $node
+ log_fail "$inherit $i #$count " \
+ "ACE: $aclcur, expect to be " \
+ "$expect1"
+ fi
+
+ # Get the second ACE (if should have) to do comparison
+
+ if (( step > 0 )); then
+ (( count = count + step ))
+
+ aclcur=$(get_ACE $node $count compact)
+ aclcur=${aclcur#$count:}
+ if [[ -n $expect2 && \
+ $expect2 != $aclcur ]]; then
+
+ $LS -Vd $basedir
+ $LS -Vd $node
+ log_fail "$inherit $i #$count " \
+ "ACE: $aclcur, expect to be " \
+ "$expect2"
+ fi
+ fi
+ (( count = count + 1 ))
+ fi
+ (( i = i + 1 ))
+ done
+
+ #
+ # If there's no any ACE be checked, it should be identify as
+ # an normal file/dir, verify it.
+ #
+
+ if (( passcnt == maxnumber )); then
+ if [[ -d $node ]]; then
+ compare_acls $node $odir
+ elif [[ -f $node ]]; then
+ compare_acls $node $ofile
+ fi
+
+ if [[ $? -ne 0 ]]; then
+ $LS -Vd $basedir
+ $LS -Vd $node
+ log_fail "Unexpect acl: $node, $inherit ($str)"
+ fi
+ fi
+ done
+}
+
+typeset -i i=0
+typeset acl0 acl1 acl2 acl3
+typeset acls0 acls1 acls2 acls3
+
+#
+# Set aclmode=passthrough to make sure
+# the acl will not change during chmod.
+# A general testing should verify the combination of
+# aclmode/aclinherit works well,
+# here we just simple test them separately.
+#
+
+log_must $ZFS set aclmode=passthrough $TESTPOOL/$TESTFS
+
+for inherit in "${aclinherit_flag[@]}"; do
+
+ #
+ # Set different value of aclinherit
+ #
+
+ log_must $ZFS set aclinherit=$inherit $TESTPOOL/$TESTFS
+
+ for user in root $ZFS_ACL_STAFF1; do
+ log_must set_cur_usr $user
+
+ for obj in "${object_flag[@]}"; do
+ for str in "${strategy_flag[@]}"; do
+ typeset inh_opt=$obj
+ (( ${#str} != 0 )) && inh_opt=${inh_opt}${str}--
+
+ if [[ -n $cifs ]]; then
+ inh_a=${inh_opt}-
+ inh_b=${inh_opt}I
+ else
+ inh_a=${inh_opt}
+ inh_b=${inh_opt}
+ fi
+
+ #
+ # Prepare 4 ACES, which should include :
+ # deny -> to verify "noallow"
+ # write_acl/write_owner -> to verify "secure"
+ #
+
+ acl0="$ace_prefix1:rwxp---A-W-Co-:${inh_a}:allow"
+ acl1="$ace_prefix2:rwxp---A-W-Co-:${inh_a}:deny"
+ acl2="$ace_prefix3:rwxp---A-W-Co-:${inh_a}:allow"
+ acl3="$ace_prefix1:-------A-W----:${inh_a}:deny"
+ acl4="$ace_prefix2:-------A-W----:${inh_a}:allow"
+ acl5="$ace_prefix3:-------A-W----:${inh_a}:deny"
+
+
+ #
+ # The ACE filtered by write_acl/write_owner
+ #
+
+ if [[ $inheri == "passthrough" ]]; then
+ acls0="$ace_prefix1:rwxp---A-W----:${inh_b}:allow"
+ acls1="$ace_prefix2:rwxp---A-W----:${inh_b}:deny"
+ acls2="$ace_prefix3:rwxp---A-W----:${inh_b}:allow"
+ acls3="$ace_prefix1:rwxp---A-W----:${inh_b}:deny"
+ acls4="$ace_prefix2:rwxp---A-W----:${inh_b}:allow"
+ acls5="$ace_prefix3:rwxp---A-W----:${inh_b}:deny"
+ else
+ acls0="$ace_prefix1:-------A-W----:${inh_b}:allow"
+ acls1="$ace_prefix2:-------A-W-Co-:${inh_b}:deny"
+ acls2="$ace_prefix3:-------A-W----:${inh_b}:allow"
+ acls3="$ace_prefix1:-------A-W----:${inh_b}:deny"
+ acls4="$ace_prefix2:-------A-W----:${inh_b}:allow"
+ acls5="$ace_prefix3:-------A-W----:${inh_b}:deny"
+ fi
+
+ #
+ # Create basedir and tmp dir/file
+ # for comparison.
+ #
+
+ log_note "$user: $CHMOD $acl $basedir"
+ log_must usr_exec $MKDIR $basedir
+ log_must usr_exec $MKDIR $odir
+ log_must usr_exec $TOUCH $ofile
+
+ i=5
+ while (( i >= 0 )); do
+ eval acl=\$acl$i
+
+ #
+ # Place on a directory should succeed.
+ #
+ log_must usr_exec $CHMOD A+$acl $basedir
+
+ (( i = i - 1 ))
+ done
+
+ verify_inherit $inherit $obj $str
+
+ log_must usr_exec $RM -rf $ofile $odir $basedir
+ done
+ done
+ done
+done
+
+log_pass "Verify chmod inherit behaviour co-op with aclinherit setting passed."
diff --git a/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_inherit_004_pos.ksh b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_inherit_004_pos.ksh
new file mode 100644
index 000000000000..42e67c5102d3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_inherit_004_pos.ksh
@@ -0,0 +1,157 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_chmod_inherit_004_pos
+#
+# DESCRIPTION:
+# Verify aclinherit=passthrough-x will inherit the 'x' bits while mode request.
+#
+# STRATEGY:
+# 1. Loop super user and non-super user to run the test case.
+# 2. Create basedir and a set of subdirectores and files within it.
+# 3. Set aclinherit=passthrough-x
+# 4. Verify only passthrough-x will inherit the 'x' bits while mode request.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-04-29)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if [[ -d $basedir ]]; then
+ log_must $RM -rf $basedir
+ fi
+}
+
+$ZPOOL upgrade -v | $GREP "passthrough\-x aclinherit support" > /dev/null 2>&1
+if (( $? != 0 )) ; then
+ log_unsupported "passthrough-x aclinherit not supported."
+fi
+
+log_assert "Verify aclinherit=passthrough-x will inherit the 'x' bits while mode request."
+log_onexit cleanup
+
+set -A aces "owner@:read_data/write_data/add_subdirectory/append_data/execute:dir_inherit/inherit_only:allow" \
+ "owner@:read_data/write_data/add_subdirectory/append_data/execute::allow" \
+ "group@:add_subdirectory/append_data/execute:dir_inherit/inherit_only:allow" \
+ "group@:add_subdirectory/append_data/execute::allow" \
+ "everyone@:add_subdirectory/append_data/execute:dir_inherit/inherit_only:allow" \
+ "everyone@:add_subdirectory/append_data/execute::allow" \
+ "owner@:read_data/write_data/add_subdirectory/append_data/execute:file_inherit/inherit_only:allow" \
+ "group@:read_data/add_subdirectory/append_data/execute:file_inherit/inherit_only:allow" \
+ "everyone@:read_data/add_subdirectory/append_data/execute:file_inherit/inherit_only:allow"
+
+# Defile the based directory and file
+basedir=$TESTDIR/basedir
+
+test_requires ZFS_ACL
+
+#
+# According to inherited flag, verify subdirectories and files within it has
+# correct inherited access control.
+#
+function verify_inherit # <object>
+{
+ typeset obj=$1
+
+ # Define the files and directories will be created after chmod
+ ndir1=$obj/ndir1; ndir2=$ndir1/ndir2
+ nfile1=$ndir1/nfile1.c; nfile2=$ndir1/nfile2
+
+ log_must usr_exec $MKDIR -p $ndir1
+
+ typeset -i i=0
+ while (( i < ${#aces[*]} )) ; do
+ if (( i < 6 )) ; then
+ log_must usr_exec $CHMOD A$i=${aces[i]} $ndir1
+ else
+ log_must usr_exec $CHMOD A$i+${aces[i]} $ndir1
+ fi
+ (( i = i + 1 ))
+ done
+ log_must usr_exec $MKDIR -p $ndir2
+ log_must usr_exec $TOUCH $nfile1
+
+ $CAT > $nfile1 <<EOF
+#include <stdlib.h>
+#include <stdio.h>
+int main()
+{ return 0; }
+EOF
+
+ mode=$(get_mode $ndir2)
+ if [[ $mode != "drwx--x--x"* ]] ; then
+ log_fail "Unexpect mode of $ndir2, expect: drwx--x--x, current: $mode"
+ fi
+
+ mode=$(get_mode $nfile1)
+ if [[ $mode != "-rw-r--r--"* ]] ; then
+ log_fail "Unexpect mode of $nfile1, expect: -rw-r--r--, current: $mode"
+ fi
+
+ if [[ -x /usr/sfw/bin/gcc ]] ; then
+ log_must /usr/sfw/bin/gcc -o $nfile2 $nfile1
+ mode=$(get_mode $nfile2)
+ if [[ $mode != "-rwxr-xr-x"* ]] ; then
+ log_fail "Unexpect mode of $nfile2, expect: -rwxr-xr-x, current: $mode"
+ fi
+ fi
+}
+
+#
+# Set aclmode=passthrough to make sure
+# the acl will not change during chmod.
+# A general testing should verify the combination of
+# aclmode/aclinherit works well,
+# here we just simple test them separately.
+#
+
+log_must $ZFS set aclmode=passthrough $TESTPOOL/$TESTFS
+log_must $ZFS set aclinherit=passthrough-x $TESTPOOL/$TESTFS
+
+for user in root $ZFS_ACL_STAFF1; do
+ log_must set_cur_usr $user
+
+ verify_inherit $basedir
+
+ cleanup
+done
+
+log_pass "Verify aclinherit=passthrough-x will inherit the 'x' bits while mode request."
diff --git a/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_owner_001_pos.ksh b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_owner_001_pos.ksh
new file mode 100644
index 000000000000..259658784640
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_owner_001_pos.ksh
@@ -0,0 +1,397 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_chmod_owner_001_pos
+#
+# DESCRIPTION:
+# Verify that the write_owner for
+# owner/group/everyone are correct.
+#
+# STRATEGY:
+# 1. Create file and directory in zfs filesystem
+# 2. Set special write_owner ACE to the file and directory
+# 3. Try to chown/chgrp of the file and directory to take owner/group
+# 4. Verify that the owner/group are correct. Follow these rules:
+# (1) If uid is granted the write_owner permission,
+# then it can only do chown to its own uid,
+# or a group that they are a member of.
+# (2) Owner will ignore permission of (1) even write_owner not granted.
+# (3) Superuser will always permit whatever they do.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-10-26)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ [[ ! -e $TESTDIR/$ARCHIVEFILE ]] && return 0
+
+ if [[ ! -e $target ]]; then
+ log_must $TAR xpf $TESTDIR/$ARCHIVEFILE
+ fi
+
+ (( ${#cwd} != 0 )) && cd $cwd
+ cleanup_test_files $TESTDIR/basedir
+ log_must $RM -f $TESTDIR/$ARCHIVEFILE
+ return 0
+}
+
+#owner@ group group_users other_users
+set -A users \
+"root" "root" "$ZFS_ACL_ADMIN" "$ZFS_ACL_OTHER1" \
+"$ZFS_ACL_STAFF1" "$ZFS_ACL_STAFF_GROUP" "$ZFS_ACL_STAFF2" "$ZFS_ACL_OTHER1"
+
+set -A a_access \
+ "write_owner:allow" \
+ "write_owner:deny"
+
+set -A a_flag "owner@" "group@" "everyone@"
+
+log_assert "Verify that the chown/chgrp could take owner/group " \
+ "while permission is granted."
+log_onexit cleanup
+
+#
+# Get the owner of a file/directory
+#
+function get_owner #node
+{
+ typeset node=$1
+ typeset value
+
+ if [[ -z $node ]]; then
+ log_fail "node are not defined."
+ fi
+
+ if [[ -d $node ]]; then
+ value=$($LS -dl $node | $AWK '{print $3}')
+ elif [[ -e $node ]]; then
+ value=$($LS -l $node | $AWK '{print $3}')
+ fi
+
+ $ECHO $value
+}
+
+#
+# Get the group of a file/directory
+#
+function get_group #node
+{
+ typeset node=$1
+ typeset value
+
+ if [[ -z $node ]]; then
+ log_fail "node are not defined."
+ fi
+
+ if [[ -d $node ]]; then
+ value=$($LS -dl $node | $AWK '{print $4}')
+ elif [[ -e $node ]]; then
+ value=$($LS -l $node | $AWK '{print $4}')
+ fi
+
+ $ECHO $value
+}
+
+
+#
+# Get the group name that a UID belongs to
+#
+function get_user_group #uid
+{
+ typeset uid=$1
+ typeset value
+
+ if [[ -z $uid ]]; then
+ log_fail "UID not defined."
+ fi
+
+ value=$(id $uid)
+
+ if [[ $? -eq 0 ]]; then
+ value=${value##*\(}
+ value=${value%%\)*}
+ $ECHO $value
+ else
+ log_fail "Invalid UID (uid)."
+ fi
+}
+
+function operate_node_owner #user node old_owner expect_owner
+{
+ typeset user=$1
+ typeset node=$2
+ typeset old_owner=$3
+ typeset expect_owner=$4
+ typeset ret new_owner
+
+ if [[ $user == "" || $node == "" ]]; then
+ log_fail "user, node are not defined."
+ fi
+
+ chgusr_exec $user $CHOWN $expect_owner $node ; ret=$?
+ new_owner=$(get_owner $node)
+
+ if [[ $new_owner != $old_owner ]]; then
+ $TAR xpf $TESTDIR/$ARCHIVEFILE
+ fi
+
+ if [[ $ret -eq 0 ]]; then
+ if [[ $new_owner != $expect_owner ]]; then
+ log_note "Owner not changed as expected " \
+ "($old_owner|$new_owner|$expect_owner), " \
+ "but return code is $ret."
+ return 1
+ fi
+ elif [[ $ret -ne 0 && $new_owner != $old_owner ]]; then
+ log_note "Owner changed ($old_owner|$new_owner), " \
+ "but return code is $ret."
+ return 2
+ fi
+
+ return $ret
+}
+
+function operate_node_group #user node old_group expect_group
+{
+ typeset user=$1
+ typeset node=$2
+ typeset old_group=$3
+ typeset expect_group=$4
+ typeset ret new_group
+
+ if [[ $user == "" || $node == "" ]]; then
+ log_fail "user, node are not defined."
+ fi
+
+ chgusr_exec $user $CHGRP $expect_group $node ; ret=$?
+ new_group=$(get_group $node)
+
+ if [[ $new_group != $old_group ]]; then
+ $TAR xpf $TESTDIR/$ARCHIVEFILE
+ fi
+
+ if [[ $ret -eq 0 ]]; then
+ if [[ $new_group != $expect_group ]]; then
+ log_note "Group not changed as expected " \
+ "($old_group|$new_group|$expect_group), " \
+ "but return code is $ret."
+ return 1
+ fi
+ elif [[ $ret -ne 0 && $new_group != $old_group ]]; then
+ log_note "Group changed ($old_group|$new_group), " \
+ "but return code is $ret."
+ return 2
+ fi
+
+ return $ret
+}
+
+function logname #acl_target user old new
+{
+ typeset acl_target=$1
+ typeset user=$2
+ typeset old=$3
+ typeset new=$4
+ typeset ret="log_mustnot"
+
+ # To super user, read and write deny permission was override.
+ if [[ $user == root ]]; then
+ ret="log_must"
+ elif [[ $user == $new ]] ; then
+ if [[ $user == $old || $acl_target == *:allow ]]; then
+ ret="log_must"
+ fi
+ fi
+
+ print $ret
+}
+
+function check_chmod_results #node flag acl_target g_usr o_usr
+{
+ typeset node=$1
+ typeset flag=$2
+ typeset acl_target=$2:$3
+ typeset g_usr=$4
+ typeset o_usr=$5
+ typeset log old_owner old_group new_owner new_group
+
+ old_owner=$(get_owner $node)
+ old_group=$(get_group $node)
+
+ if [[ $flag == "owner@" || $flag == "everyone@" ]]; then
+ for new_owner in $ZFS_ACL_CUR_USER "nobody"; do
+ new_group=$(get_user_group $new_owner)
+
+ log=$(logname $acl_target $ZFS_ACL_CUR_USER \
+ $old_owner $new_owner)
+
+ $log operate_node_owner $ZFS_ACL_CUR_USER $node \
+ $old_owner $new_owner
+
+ $log operate_node_group $ZFS_ACL_CUR_USER $node \
+ $old_group $new_group
+ done
+ fi
+ if [[ $flag == "group@" || $flag == "everyone@" ]]; then
+ for new_owner in $g_usr "nobody"; do
+ new_group=$(get_user_group $new_owner)
+
+ log=$(logname $acl_target $g_usr $old_owner \
+ $new_owner)
+
+ $log operate_node_owner $g_usr $node \
+ $old_owner $new_owner
+
+ $log operate_node_group $g_usr \
+ $node $old_group $new_group
+ done
+ fi
+ if [[ $flag == "everyone@" ]]; then
+ for new_owner in $g_usr "nobody"; do
+ new_group=$(get_user_group $new_owner)
+
+ log=$(logname $acl_target $o_usr $old_owner \
+ $new_owner)
+
+ $log operate_node_owner $o_usr $node \
+ $old_owner $new_owner
+
+ $log operate_node_group $o_usr $node \
+ $old_group $new_group
+ done
+ fi
+}
+
+function test_chmod_basic_access #node g_usr o_usr
+{
+ typeset node=${1%/}
+ typeset g_usr=$2
+ typeset o_usr=$3
+ typeset flag acl_p acl_t parent
+
+ parent=${node%/*}
+
+ for flag in ${a_flag[@]}; do
+ for acl_t in "${a_access[@]}"; do
+ log_must usr_exec $CHMOD A+$flag:$acl_t $node
+
+ $TAR cpf $TESTDIR/$ARCHIVEFILE basedir
+
+ check_chmod_results "$node" "$flag" \
+ "$acl_t" "$g_usr" "$o_usr"
+
+ log_must usr_exec $CHMOD A0- $node
+ done
+ done
+}
+
+function setup_test_files #base_node user group
+{
+ typeset base_node=$1
+ typeset user=$2
+ typeset group=$3
+
+ cleanup_test_files $base_node
+
+ log_must $MKDIR -p $base_node
+ log_must $CHOWN $user:$group $base_node
+
+ log_must set_cur_usr $user
+
+ # Prepare all files/sub-dirs for testing.
+
+ file0=$base_node/testfile_rm
+
+ dir0=$base_node/testdir_rm
+
+ log_must usr_exec $TOUCH $file0
+ log_must usr_exec $CHMOD 444 $file0
+
+ log_must usr_exec $MKDIR -p $dir0
+ log_must usr_exec $CHMOD 444 $dir0
+
+ log_must usr_exec $CHMOD 555 $base_node
+ return 0
+}
+
+function cleanup_test_files #base_node
+{
+ typeset base_node=$1
+
+ if [[ -d $base_node ]]; then
+ log_must $RM -rf $base_node
+ elif [[ -e $base_node ]]; then
+ log_must $RM -f $base_node
+ fi
+
+ return 0
+}
+
+typeset cwd=$PWD
+typeset ARCHIVEFILE=archive.tar
+
+test_requires ZFS_ACL
+
+typeset -i i=0
+typeset -i j=0
+typeset target
+cd $TESTDIR
+while (( i < ${#users[@]} )); do
+ setup_test_files $TESTDIR/basedir ${users[i]} ${users[((i+1))]}
+
+ j=0
+ while (( j < 1 )); do
+ eval target=\$file$j
+ test_chmod_basic_access $target \
+ "${users[((i+2))]}" "${users[((i+3))]}"
+
+ eval target=\$dir$j
+ test_chmod_basic_access $target \
+ "${users[((i+2))]}" "${users[((i+3))]}"
+
+ (( j = j + 1 ))
+ done
+
+ (( i += 4 ))
+done
+
+log_pass "Verify that the chown/chgrp could take owner/group " \
+ "while permission is granted."
diff --git a/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_rwacl_001_pos.ksh b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_rwacl_001_pos.ksh
new file mode 100644
index 000000000000..16f55ac0e919
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_rwacl_001_pos.ksh
@@ -0,0 +1,257 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_chmod_rwacl_001_pos
+#
+# DESCRIPTION:
+# Verify assigned read_acl/write_acl to owner@/group@/everyone@,
+# specificied user and group. File have the correct access permission.
+#
+# STRATEGY:
+# 1. Separatedly verify file and directory was assigned read_acl/write_acl
+# by root and non-root user.
+# 2. Verify owner always can read and write acl, even deny.
+# 3. Verify group access permission, when group was assigned
+# read_acl/write_acl.
+# 4. Verify access permission, after everyone was assigned read_acl/write.
+# 5. Verify everyone@ was deny except specificied user, this user can read
+# and write acl.
+# 6. Verify the group was deny except specified user, this user can read
+# and write acl
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-10-19)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify chmod A[number]{+|-|=} read_acl/write_acl have correct " \
+ "behaviour to access permission."
+log_onexit cleanup
+
+function read_ACL #<node> <user1> <user2> ...
+{
+ typeset node=$1
+ typeset user
+ typeset -i ret
+
+ shift
+ for user in $@; do
+ chgusr_exec $user $LS -vd $node > /dev/null 2>&1
+ ret=$?
+ (( ret != 0 )) && return $ret
+
+ shift
+ done
+
+ return 0
+}
+
+function write_ACL #<node> <user1> <user2> ...
+{
+ typeset node=$1
+ typeset user
+ typeset -i ret before_cnt after_cnt
+
+ shift
+ for user in "$@"; do
+ before_cnt=$(count_ACE $node)
+ ret=$?;
+ (( ret != 0 )) && return $ret
+
+ chgusr_exec $user $CHMOD A0+owner@:read_data:allow $node
+ ret=$?
+ (( ret != 0 )) && return $ret
+
+ after_cnt=$(count_ACE $node)
+ ret=$?
+ (( ret != 0 )) && return $ret
+
+ chgusr_exec $user $CHMOD A0- $node
+ ret=$?
+ (( ret != 0 )) && return $ret
+
+ if (( after_cnt - before_cnt != 1 )); then
+ return 1
+ fi
+
+ shift
+ done
+
+ return 0
+}
+
+function check_owner #<node>
+{
+ typeset node=$1
+
+ for acc in allow deny; do
+ log_must usr_exec \
+ $CHMOD A0+owner@:read_acl/write_acl:$acc $node
+ log_must read_ACL $node $ZFS_ACL_CUR_USER
+ log_must write_ACL $node $ZFS_ACL_CUR_USER
+ log_must usr_exec $CHMOD A0- $node
+ done
+}
+
+function check_group #<node>
+{
+ typeset node=$1
+
+ typeset grp_usr=""
+ if [[ $ZFS_ACL_CUR_USER == root ]]; then
+ grp_usr=$ZFS_ACL_ADMIN
+ elif [[ $ZFS_ACL_CUR_USER == $ZFS_ACL_STAFF1 ]]; then
+ grp_usr=$ZFS_ACL_STAFF2
+ fi
+
+ log_must usr_exec $CHMOD A0+group@:read_acl/write_acl:allow $node
+ log_must read_ACL $node $grp_usr
+ log_must write_ACL $node $grp_usr
+ log_must usr_exec $CHMOD A0- $node
+
+ log_must usr_exec $CHMOD A0+group@:read_acl/write_acl:deny $node
+ log_mustnot read_ACL $node $grp_usr
+ log_mustnot write_ACL $node $grp_usr
+ log_must usr_exec $CHMOD A0- $node
+}
+
+function check_everyone #<node>
+{
+ typeset node=$1
+
+ typeset flag
+ for flag in allow deny; do
+ if [[ $flag == allow ]]; then
+ log=log_must
+ else
+ log=log_mustnot
+ fi
+
+ log_must usr_exec \
+ $CHMOD A0+everyone@:read_acl/write_acl:$flag $node
+
+ $log read_ACL $node $ZFS_ACL_OTHER1 $ZFS_ACL_OTHER2
+ $log write_ACL $node $ZFS_ACL_OTHER1 $ZFS_ACL_OTHER2
+
+ log_must usr_exec $CHMOD A0- $node
+ done
+}
+
+function check_spec_user #<node>
+{
+ typeset node=$1
+
+ log_must usr_exec $CHMOD A0+everyone@:read_acl/write_acl:deny $node
+ log_must usr_exec \
+ $CHMOD A0+user:$ZFS_ACL_OTHER1:read_acl/write_acl:allow $node
+
+ # The specified user can read and write acl
+ log_must read_ACL $node $ZFS_ACL_OTHER1
+ log_must write_ACL $node $ZFS_ACL_OTHER1
+
+ # All the other user can't read and write acl
+ log_mustnot \
+ read_ACL $node $ZFS_ACL_ADMIN $ZFS_ACL_STAFF2 $ZFS_ACL_OTHER2
+ log_mustnot \
+ write_ACL $node $ZFS_ACL_ADMIN $ZFS_ACL_STAFF2 $ZFS_ACL_OTHER2
+
+ log_must usr_exec $CHMOD A0- $node
+ log_must usr_exec $CHMOD A0- $node
+}
+
+function check_spec_group #<node>
+{
+ typeset node=$1
+
+ log_must usr_exec $CHMOD A0+everyone@:read_acl/write_acl:deny $node
+ log_must usr_exec $CHMOD \
+ A0+group:$ZFS_ACL_OTHER_GROUP:read_acl/write_acl:allow $node
+
+ # The specified group can read and write acl
+ log_must read_ACL $node $ZFS_ACL_OTHER1 $ZFS_ACL_OTHER2
+ log_must write_ACL $node $ZFS_ACL_OTHER1 $ZFS_ACL_OTHER2
+
+ # All the other user can't read and write acl
+ log_mustnot read_ACL $node $ZFS_ACL_ADMIN $ZFS_ACL_STAFF2
+ log_mustnot write_ACL $node $ZFS_ACL_ADMIN $ZFS_ACL_STAFF2
+}
+
+function check_user_in_group #<node>
+{
+ typeset node=$1
+
+ log_must usr_exec $CHMOD \
+ A0+group:$ZFS_ACL_OTHER_GROUP:read_acl/write_acl:deny $node
+ log_must usr_exec $CHMOD \
+ A0+user:$ZFS_ACL_OTHER1:read_acl/write_acl:allow $node
+ log_must read_ACL $node $ZFS_ACL_OTHER1
+ log_must write_ACL $node $ZFS_ACL_OTHER1
+ log_mustnot read_ACL $node $ZFS_ACL_OTHER2
+ log_mustnot write_ACL $node $ZFS_ACL_OTHER2
+
+ log_must usr_exec $CHMOD A0- $node
+ log_must usr_exec $CHMOD A0- $node
+}
+
+set -A func_name check_owner \
+ check_group \
+ check_everyone \
+ check_spec_user \
+ check_spec_group \
+ check_user_in_group
+
+test_requires ZFS_ACL
+
+for user in root $ZFS_ACL_STAFF1; do
+ log_must set_cur_usr $user
+
+ log_must usr_exec $TOUCH $testfile
+ log_must usr_exec $MKDIR $testdir
+
+ typeset func node
+ for func in ${func_name[@]}; do
+ for node in $testfile $testdir; do
+ eval $func \$node
+ done
+ done
+
+ log_must usr_exec $RM -rf $testfile $testdir
+done
+
+log_pass "Verify chmod A[number]{+|-|=} read_acl/write_acl passed."
diff --git a/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_rwx_001_pos.ksh b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_rwx_001_pos.ksh
new file mode 100644
index 000000000000..0ce0096a666e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_rwx_001_pos.ksh
@@ -0,0 +1,136 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_chmod_rwx_001_pos
+#
+# DESCRIPTION:
+# chmod A{+|-|=} have the correct behaviour to the ACL list.
+#
+# STRATEGY:
+# 1. loop check root and non-root users
+# 2. chmod file or dir with specified options
+# 3. get ACE after behaviours of chmod
+# 4. compare specified ACE and excpect ACE
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-09-30)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "chmod A{+|-|=} have the correct behaviour to the ACL list."
+log_onexit cleanup
+
+typeset -i trival_count=6 head=0 mid end
+((mid = RANDOM % $trival_count))
+((end = trival_count - 1))
+
+opts="+ - ="
+nums="$head $mid $end"
+set -A file_ACEs \
+ "user:$ZFS_ACL_STAFF1:read_data:allow" \
+ "user:$ZFS_ACL_STAFF2:write_data:allow" \
+ "user:$ZFS_ACL_OTHER1:execute:allow"
+set -A dir_ACEs \
+ "user:$ZFS_ACL_STAFF1:list_directory/read_data:allow" \
+ "user:$ZFS_ACL_STAFF2:add_file/write_data:allow" \
+ "user:$ZFS_ACL_OTHER1:execute:allow"
+
+function test_chmod_ACE_list #$opt $num $ace-spec $node
+{
+ typeset opt=A$2$1
+ typeset -i num=$2
+ typeset ace=$3
+ typeset node=$4
+ typeset -i expect_count=0
+
+ # Get expect ACE count
+ case $opt in
+ A[0-9]*+) (( expect_count = trival_count + 1 )) ;;
+ A[0-9]*-) (( expect_count = trival_count - 1 )) ;;
+ A[0-9]*=) (( expect_count = trival_count )) ;;
+ *) log_fail "Error option: '$opt'" ;;
+ esac
+
+ # Invoke chmod A[number]{+|-|=}<acl-specification> file|dir
+ if [[ $opt == A[0-9]*+ || $opt == A[0-9]*= ]]; then
+ log_must usr_exec $CHMOD "$opt$ace" "$node"
+ else
+ log_must usr_exec $CHMOD "$opt" "$node"
+ fi
+
+ # Get the current ACE count and specified ACE
+ typeset cur_ace cur_count
+ cur_ace=$(get_ACE $node $num)
+ cur_count=$(count_ACE $node)
+
+ # Compare with expected results
+ if [[ $opt == A[0-9]*+ || $opt == A[0-9]*= ]]; then
+ if [[ "$num:$ace" != "$cur_ace" ]]; then
+ log_fail "FAIL: $CHMOD $opt$ace $node"
+ fi
+ fi
+ if [[ "$expect_count" != "$cur_count" ]]; then
+ log_fail "FAIL: '$expect_count' != '$cur_count'"
+ fi
+}
+
+test_requires ZFS_ACL
+
+for user in root $ZFS_ACL_STAFF1 $ZFS_ACL_OTHER1; do
+ log_must set_cur_usr $user
+
+ for opt in $opts; do
+ for num in $nums; do
+ for ace in $file_ACEs; do
+ ls -l $TESTDIR
+ log_must usr_exec $TOUCH $testfile
+ test_chmod_ACE_list $opt $num $ace $testfile
+ log_must $RM -f $testfile
+ done
+ for ace in $dir_ACEs; do
+ ls -l $TESTDIR
+ log_must usr_exec $MKDIR -p $testdir
+ test_chmod_ACE_list $opt $num $ace $testdir
+ log_must $RM -rf $testdir
+ done
+ done
+ done
+done
+
+log_pass "chmod A{+|-|=} behave to the ACL list passed."
diff --git a/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_rwx_002_pos.ksh b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_rwx_002_pos.ksh
new file mode 100644
index 000000000000..8f2ada624780
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_rwx_002_pos.ksh
@@ -0,0 +1,256 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_chmod_rwx_002_pos
+#
+# DESCRIPTION:
+# chmod A{+|-|=} read_data|write_data|execute for owner@ group@ or everyone@
+# correctly alters mode bits .
+#
+# STRATEGY:
+# 1. Loop root and non-root user.
+# 2. Get the random initial map.
+# 3. Get the random ACL string.
+# 4. Separately chmod +|-|= read_data|write_data|execute
+# 5. Check map bits
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-10-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "chmod A{+|-|=} read_data|write_data|execute for owner@, group@ " \
+ "or everyone@ correctly alters mode bits."
+log_onexit cleanup
+
+set -A bits 0 1 2 3 4 5 6 7
+set -A a_flag owner group everyone
+set -A a_access read_data write_data execute
+set -A a_type allow deny
+
+#
+# Get a random item from an array.
+#
+# $1 the base set
+#
+function random_select #array_name
+{
+ typeset arr_name=$1
+ typeset -i ind
+
+ eval typeset -i cnt=\${#${arr_name}[@]}
+ (( ind = $RANDOM % cnt ))
+
+ eval print \${${arr_name}[$ind]}
+}
+
+#
+# Create a random string according to array name, the item number and
+# separated tag.
+#
+# $1 array name where the function get the elements
+# $2 the items number which you want to form the random string
+# $3 the separated tag
+#
+function form_random_str #<array_name> <count> <sep>
+{
+ typeset arr_name=$1
+ typeset -i count=${2:-1}
+ typeset sep=${3:-""}
+
+ typeset str=""
+ while (( count > 0 )); do
+ str="${str}$(random_select $arr_name)${sep}"
+
+ (( count -= 1 ))
+ done
+
+ print $str
+}
+
+#
+# According to the original bits, the input ACE access and ACE type, return the
+# expect bits after 'chmod A0{+|=}'.
+#
+# $1 bits which was make up of three bit 'rwx'
+# $2 ACE access which is read_data, write_data or execute
+# $3 ACE type which is allow or deny
+#
+function cal_bits #bits acl_access acl_type
+{
+ typeset bits=$1
+ typeset acl_access=$2
+ typeset acl_type=$3
+ set -A bit r w x
+
+ typeset tmpbits=""
+ typeset -i i=0 j
+ while (( i < 3 )); do
+ if [[ $acl_access == *"${a_access[i]}"* ]]; then
+ if [[ $acl_type == "allow" ]]; then
+ tmpbits="$tmpbits${bit[i]}"
+ elif [[ $acl_type == "deny" ]]; then
+ tmpbits="${tmpbits}-"
+ fi
+ else
+ (( j = i + 1 ))
+ tmpbits="$tmpbits$(get_substr $bits $j 1)"
+ fi
+
+ (( i += 1 ))
+ done
+
+ print "$tmpbits"
+}
+
+#
+# Based on the initial node map before chmod and the ace-spec, check if chmod
+# has the correct behaven to map bits.
+#
+function check_test_result #init_mode node acl_flag acl_access a_type
+{
+ typeset init_mode=$1
+ typeset node=$2
+ typeset acl_flag=$3
+ typeset acl_access=$4
+ typeset acl_type=$5
+
+ typeset -3L u_bits=$init_mode
+ typeset g_bits=$(get_substr $init_mode 4 3)
+ typeset -3R o_bits=$init_mode
+
+ if [[ $acl_flag == "owner" || $acl_flag == "everyone" ]]; then
+ u_bits=$(cal_bits $u_bits $acl_access $acl_type)
+ fi
+ if [[ $acl_flag == "group" || $acl_flag == "everyone" ]]; then
+ g_bits=$(cal_bits $g_bits $acl_access $acl_type)
+ fi
+ if [[ $acl_flag == "everyone" ]]; then
+ o_bits=$(cal_bits $o_bits $acl_access $acl_type)
+ fi
+
+ typeset cur_mode=$(get_mode $node)
+ cur_mode=$(get_substr $cur_mode 2 9)
+
+ if [[ $cur_mode == $u_bits$g_bits$o_bits ]]; then
+ log_note "SUCCESS: Current map($cur_mode) ==" \
+ "expected map($u_bits$g_bits$o_bits)"
+ else
+ log_fail "FAIL: Current map($cur_mode) != " \
+ "expected map($u_bits$g_bits$o_bits)"
+ fi
+}
+
+function test_chmod_map #<node>
+{
+ typeset node=$1
+ typeset init_mask acl_flag acl_access acl_type
+ typeset -i cnt
+
+ if (( ${#node} == 0 )); then
+ log_fail "FAIL: file name or directroy name is not defined."
+ fi
+
+ # Get the initial map
+ init_mask=$(form_random_str bits 3)
+ # Get ACL flag, access & type
+ acl_flag=$(form_random_str a_flag)
+ (( cnt = ($RANDOM % ${#a_access[@]}) + 1 ))
+ acl_access=$(form_random_str a_access $cnt '/')
+ acl_access=${acl_access%/}
+ acl_type=$(form_random_str a_type)
+
+ typeset acl_spec=${acl_flag}@:${acl_access}:${acl_type}
+
+ # Set the initial map and back the initial ACEs
+ typeset orig_ace=$TMPDIR/orig_ace.${TESTCASE_ID}
+ typeset cur_ace=$TMPDIR/cur_ace.${TESTCASE_ID}
+
+ for operator in "A0+" "A0="; do
+ log_must usr_exec $CHMOD $init_mask $node
+ init_mode=$(get_mode $node)
+ init_mode=$(get_substr $init_mode 2 9)
+ log_must usr_exec eval "$LS -vd $node > $orig_ace"
+
+ # To "A=", firstly add one ACE which can't modify map
+ if [[ $operator == "A0=" ]]; then
+ log_must $CHMOD A0+user:$ZFS_ACL_OTHER1:execute:deny \
+ $node
+ fi
+ log_must usr_exec $CHMOD $operator$acl_spec $node
+ check_test_result \
+ $init_mode $node $acl_flag $acl_access $acl_type
+
+ # Check "chmod A-"
+ log_must usr_exec $CHMOD A0- $node
+ log_must usr_exec eval "$LS -vd $node > $cur_ace"
+
+ if $DIFF $orig_ace $cur_ace; then
+ log_note "SUCCESS: original ACEs equivalence the " \
+ "current ACEs. 'chmod A-' succeeded."
+ else
+ log_fail "FAIL: 'chmod A-' failed."
+ fi
+ done
+
+ [[ -f $orig_ace ]] && log_must usr_exec $RM -f $orig_ace
+ [[ -f $cur_ace ]] && log_must usr_exec $RM -f $cur_ace
+}
+
+test_requires ZFS_ACL
+
+for user in root $ZFS_ACL_STAFF1; do
+ set_cur_usr $user
+
+ typeset -i loop_cnt=20
+ while (( loop_cnt > 0 )); do
+ log_must usr_exec $TOUCH $testfile
+ test_chmod_map $testfile
+ log_must $RM -f $testfile
+
+ log_must usr_exec $MKDIR $testdir
+ test_chmod_map $testdir
+ log_must $RM -rf $testdir
+
+ (( loop_cnt -= 1 ))
+ done
+done
+
+log_pass "chmod A{+|-|=} read_data|write_data|execute for owner@, group@ " \
+ "oreveryone@ correctly alters mode bits passed."
diff --git a/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_rwx_003_pos.ksh b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_rwx_003_pos.ksh
new file mode 100644
index 000000000000..b062b290664f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_rwx_003_pos.ksh
@@ -0,0 +1,147 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_chmod_rwx_003_pos
+#
+# DESCRIPTION:
+# Verify that the read_data/write_data/execute permission for
+# owner/group/everyone are correct.
+#
+# STRATEGY:
+# 1. Loop root and non-root user.
+# 2. Separated verify type@:access:allow|deny to file and directory
+# 3. To super user, read and write deny was override.
+# 4. According to ACE list and override rule, expect that
+# read/write/execute file or directory succeed or fail.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-10-09)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+# owner@ group_users other_users
+set -A users \
+ "root" "$ZFS_ACL_ADMIN" "$ZFS_ACL_OTHER1" \
+ "$ZFS_ACL_STAFF1" "$ZFS_ACL_STAFF2" "$ZFS_ACL_OTHER1"
+
+# In order to test execute permission, read_data was need firstly.
+set -A a_access "read_data" "write_data" "read_data/execute"
+set -A a_flag "owner@" "group@" "everyone@"
+
+log_assert "Verify that the read_data/write_data/execute permission for" \
+ "owner/group/everyone are correct."
+log_onexit cleanup
+
+function logname #node acl_spec user
+{
+ typeset node=$1
+ typeset acl_spec=$2
+ typeset user=$3
+
+ # To super user, read and write deny permission was override.
+ if [[ $acl_spec == *:allow ]] || \
+ [[ $user == root && -d $node ]] || \
+ [[ $user == root && $acl_spec != *"execute"* ]]
+ then
+ print "log_must"
+ elif [[ $acl_spec == *:deny ]]; then
+ print "log_mustnot"
+ fi
+}
+
+function check_chmod_results #node acl_spec g_usr o_usr
+{
+ typeset node=$1
+ typeset acl_spec=$2
+ typeset g_usr=$3
+ typeset o_usr=$4
+ typeset log
+
+ if [[ $acl_spec == "owner@:"* || $acl_spec == "everyone@:"* ]]; then
+ log=$(logname $node $acl_spec $ZFS_ACL_CUR_USER)
+ $log rwx_node $ZFS_ACL_CUR_USER $node $acl_spec
+ fi
+ if [[ $acl_spec == "group@:"* || $acl_spec == "everyone@:"* ]]; then
+ log=$(logname $node $acl_spec $g_usr)
+ $log rwx_node $g_usr $node $acl_spec
+ fi
+ if [[ $acl_spec == "everyone@"* ]]; then
+ log=$(logname $node $acl_spec $o_usr)
+ $log rwx_node $o_usr $node $acl_spec
+ fi
+}
+
+function test_chmod_basic_access #node group_user other_user
+{
+ typeset node=$1
+ typeset g_usr=$2
+ typeset o_usr=$3
+ typeset flag access acl_spec
+
+ for flag in ${a_flag[@]}; do
+ for access in ${a_access[@]}; do
+ for tp in allow deny; do
+ acl_spec="$flag:$access:$tp"
+ log_must usr_exec $CHMOD A+$acl_spec $node
+ check_chmod_results \
+ $node $acl_spec $g_usr $o_usr
+ log_must usr_exec $CHMOD A0- $node
+ done
+ done
+ done
+}
+
+test_requires ZFS_ACL
+
+typeset -i i=0
+while (( i < ${#users[@]} )); do
+ log_must set_cur_usr ${users[i]}
+
+ log_must usr_exec $TOUCH $testfile
+ test_chmod_basic_access $testfile ${users[((i+1))]} ${users[((i+2))]}
+ log_must usr_exec $MKDIR $testdir
+ test_chmod_basic_access $testdir ${users[((i+1))]} ${users[((i+2))]}
+
+ log_must usr_exec $RM -rf $testfile $testdir
+
+ (( i += 3 ))
+done
+
+log_pass "Verify that the read_data/write_data/execute permission for" \
+ "owner/group/everyone passed."
diff --git a/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_rwx_004_pos.ksh b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_rwx_004_pos.ksh
new file mode 100644
index 000000000000..37708bf035aa
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_rwx_004_pos.ksh
@@ -0,0 +1,150 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_chmod_rwx_004_pos
+#
+# DESCRIPTION:
+# Verify that explicit ACL setting to specified user or group will
+# override existed access rule.
+#
+# STRATEGY:
+# 1. Loop root and non-root user.
+# 2. Loop the specified access one by one.
+# 3. Loop verify explicit ACL set to specified user and group.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-10-14)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function check_access #log user node access rflag
+{
+ typeset log=$1
+ typeset user=$2
+ typeset node=$3
+ typeset access=$4
+ typeset rflag=$5
+
+ if [[ $rflag == "allow" && $access == execute ]]; then
+ rwx_node $user $node $access
+ #
+ # When everyone@ were deny, this file can't execute.
+ # So,'cannot execute' means user has the permission to
+ # execute, just the file can't be execute.
+ #
+ if [[ $ZFS_ACL_ERR_STR == *"cannot execute" ]]; then
+ log_note "SUCCESS: rwx_node $user $node $access"
+ else
+ log_fail "FAIL: rwx_node $user $node $access"
+ fi
+ else
+ $log rwx_node $user $node $access
+ fi
+}
+
+function verify_explicit_ACL_rule #node access flag
+{
+ set -A a_access "read_data" "write_data" "execute"
+ typeset node=$1
+ typeset access=$2
+ typeset flag=$3
+ typeset log rlog rflag
+
+ # Get the expect log check
+ if [[ $flag == allow ]]; then
+ log=log_mustnot
+ rlog=log_must
+ rflag=deny
+ else
+ log=log_must
+ rlog=log_mustnot
+ rflag=allow
+ fi
+
+ log_must usr_exec $CHMOD A+everyone@:$access:$flag $node
+ log_must usr_exec $CHMOD A+user:$ZFS_ACL_OTHER1:$access:$rflag $node
+ check_access $log $ZFS_ACL_OTHER1 $node $access $rflag
+ log_must usr_exec $CHMOD A0- $node
+
+ log_must usr_exec \
+ $CHMOD A+group:$ZFS_ACL_OTHER_GROUP:$access:$rflag $node
+ check_access $log $ZFS_ACL_OTHER1 $node $access $rflag
+ check_access $log $ZFS_ACL_OTHER2 $node $access $rflag
+ log_must usr_exec $CHMOD A0- $node
+ log_must usr_exec $CHMOD A0- $node
+
+ log_must usr_exec \
+ $CHMOD A+group:$ZFS_ACL_OTHER_GROUP:$access:$flag $node
+ log_must usr_exec $CHMOD A+user:$ZFS_ACL_OTHER1:$access:$rflag $node
+ $log rwx_node $ZFS_ACL_OTHER1 $node $access
+ $rlog rwx_node $ZFS_ACL_OTHER2 $node $access
+ log_must usr_exec $CHMOD A0- $node
+ log_must usr_exec $CHMOD A0- $node
+}
+
+log_assert "Verify that explicit ACL setting to specified user or group will" \
+ "override existed access rule."
+log_onexit cleanup
+
+set -A a_access "read_data" "write_data" "execute"
+set -A a_flag "allow" "deny"
+typeset node
+
+test_requires ZFS_ACL
+
+for user in root $ZFS_ACL_STAFF1; do
+ log_must set_cur_usr $user
+
+ log_must usr_exec $TOUCH $testfile
+ log_must usr_exec $MKDIR $testdir
+ log_must usr_exec $CHMOD 755 $testfile $testdir
+
+ for node in $testfile $testdir; do
+ for access in ${a_access[@]}; do
+ for flag in ${a_flag[@]}; do
+ verify_explicit_ACL_rule $node $access $flag
+ done
+ done
+ done
+
+ log_must usr_exec $RM -rf $testfile $testdir
+done
+
+log_pass "Explicit ACL setting to specified user or group will override " \
+ "existed access rule passed."
diff --git a/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_xattr_001_pos.ksh b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_xattr_001_pos.ksh
new file mode 100644
index 000000000000..a64f0f5c9504
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_xattr_001_pos.ksh
@@ -0,0 +1,252 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_chmod_xattr_001_pos
+#
+# DESCRIPTION:
+# Verify that the read_xattr/write_xattr for
+# owner/group/everyone are correct.
+#
+# STRATEGY:
+# 1. Create file and directory in zfs filesystem
+# 2. Set special read_xattr ACE to the file and directory
+# 3. Try to list the extended attributes of the file and directory
+# 4. Set special write_xattr ACE to the file and directory
+# 5. Try to add new extended attributes to the file and directory
+# 6. Verify above operation is successful.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-11-29)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ cd $cwd
+
+ cleanup_test_files $TESTDIR/basedir
+
+ if [[ -e $TESTDIR/$ARCHIVEFILE ]]; then
+ log_must $RM -f $TESTDIR/$ARCHIVEFILE
+ fi
+
+ return 0
+}
+
+# owner@ group group_users other_users
+set -A users \
+ "root" "root" "$ZFS_ACL_ADMIN" "$ZFS_ACL_OTHER1" \
+ "$ZFS_ACL_STAFF1" "$ZFS_ACL_STAFF_GROUP" "$ZFS_ACL_STAFF2" "$ZFS_ACL_OTHER1"
+
+set -A a_access \
+ "read_xattr:allow" \
+ "read_xattr:deny" \
+ "write_xattr:allow" \
+ "write_xattr:deny"
+
+set -A a_flag "owner@" "group@" "everyone@"
+
+MYTESTFILE=$STF_SUITE/include/default.cfg
+
+log_assert "Verify that the permission of read_xattr/write_xattr for " \
+ "owner/group/everyone are correct."
+log_onexit cleanup
+
+function operate_node #user node acl
+{
+ typeset user=$1
+ typeset node=$2
+ typeset acl_t=$3
+ typeset ret
+
+ if [[ $user == "" || $node == "" ]]; then
+ log_fail "user, node are not defined."
+ fi
+
+ if [[ $acl_t == *read_xattr* ]]; then
+ chgusr_exec $user $RUNAT $node $LS > /dev/null 2>&1; ret=$?
+ elif [[ $acl_t == *write_xattr* ]]; then
+ chgusr_exec $user $RUNAT $node $CP $MYTESTFILE attr.1 ; ret=$?
+
+ if [[ $ret -eq 0 ]]; then
+ log_must cleanup_test_files $TESTDIR/basedir
+ log_must $TAR xpf@ $TESTDIR/$ARCHIVEFILE
+ fi
+ fi
+
+ return $ret
+}
+
+function logname #acl_target user
+{
+ typeset acl_target=$1
+ typeset user=$2
+ typeset ret="log_mustnot"
+
+ # To super user, read and write deny permission was override.
+ if [[ $user == root || $acl_target == *:allow ]] then
+ ret="log_must"
+ fi
+
+ print $ret
+}
+
+function check_chmod_results #node flag acl_target g_usr o_usr
+{
+ typeset node=$1
+ typeset flag=$2
+ typeset acl_target=$2:$3
+ typeset g_usr=$4
+ typeset o_usr=$5
+ typeset log
+
+ if [[ $flag == "owner@" || $flag == "everyone@" ]]; then
+ log=$(logname $acl_target $ZFS_ACL_CUR_USER)
+ $log operate_node $ZFS_ACL_CUR_USER $node $acl_target
+ fi
+ if [[ $flag == "group@" || $flag == "everyone@" ]]; then
+ log=$(logname $acl_target $g_usr)
+ $log operate_node $g_usr $node $acl_target
+ fi
+ if [[ $flag == "everyone@" ]]; then
+ log=$(logname $acl_target $o_usr)
+ $log operate_node $o_usr $node $acl_target
+ fi
+}
+
+function test_chmod_basic_access #node g_usr o_usr
+{
+ typeset node=${1%/}
+ typeset g_usr=$2
+ typeset o_usr=$3
+ typeset flag acl_p acl_t parent
+
+ parent=${node%/*}
+
+ for flag in ${a_flag[@]}; do
+ for acl_t in "${a_access[@]}"; do
+ log_must usr_exec $CHMOD A+$flag:$acl_t $node
+
+ log_must $TAR cpf@ $TESTDIR/$ARCHIVEFILE basedir
+
+ check_chmod_results "$node" "$flag" \
+ "$acl_t" "$g_usr" "$o_usr"
+
+ log_must usr_exec $CHMOD A0- $node
+ done
+ done
+}
+
+function setup_test_files #base_node user group
+{
+ typeset base_node=$1
+ typeset user=$2
+ typeset group=$3
+
+ cleanup_test_files $base_node
+
+ log_must $MKDIR -p $base_node
+ log_must $CHOWN $user:$group $base_node
+
+ log_must set_cur_usr $user
+
+ # Prepare all files/sub-dirs for testing.
+
+ file0=$base_node/testfile_rm
+
+ dir0=$base_node/testdir_rm
+
+ log_must usr_exec $TOUCH $file0
+ log_must usr_exec $CHMOD 444 $file0
+
+ log_must usr_exec $RUNAT $file0 $CP $MYTESTFILE attr.0
+
+ log_must usr_exec $MKDIR -p $dir0
+ log_must usr_exec $CHMOD 555 $dir0
+
+ log_must usr_exec $RUNAT $dir0 $CP $MYTESTFILE attr.0
+
+ log_must usr_exec $CHMOD 777 $base_node
+ return 0
+}
+
+function cleanup_test_files #base_node
+{
+ typeset base_node=$1
+
+ if [[ -d $base_node ]]; then
+ log_must $RM -rf $base_node
+ elif [[ -e $base_node ]]; then
+ log_must $RM -f $base_node
+ fi
+
+ return 0
+}
+
+typeset cwd=$PWD
+typeset ARCHIVEFILE=archive.tar
+
+test_requires RUNAT ZFS_ACL ZFS_XATTR
+
+typeset -i i=0
+typeset -i j=0
+typeset target
+
+while (( i < ${#users[@]} )); do
+ setup_test_files $TESTDIR/basedir ${users[i]} ${users[((i+1))]}
+ cd $TESTDIR
+
+ j=0
+ while (( j < 1 )); do
+ eval target=\$file$j
+ test_chmod_basic_access $target \
+ "${users[((i+2))]}" "${users[((i+3))]}"
+
+ eval target=\$dir$j
+ test_chmod_basic_access $target \
+ "${users[((i+2))]}" "${users[((i+3))]}"
+
+ (( j = j + 1 ))
+ done
+
+ (( i += 4 ))
+done
+
+log_pass "Verify that the permission of read_xattr/write_xattr for " \
+ "owner/group/everyone are correct."
diff --git a/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_xattr_002_pos.ksh b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_xattr_002_pos.ksh
new file mode 100644
index 000000000000..69d04c3692d4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_chmod_xattr_002_pos.ksh
@@ -0,0 +1,247 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_chmod_xattr_002_pos
+#
+# DESCRIPTION:
+# Verify that the write_xattr for remove the extended attributes of
+# owner/group/everyone are correct.
+#
+# STRATEGY:
+# 1. Create file and directory in zfs filesystem
+# 2. Set special write_xattr ACE to the file and directory
+# 3. Try to remove the extended attributes of the file and directory
+# 4. Verify above operation is successful.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-11-29)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ cd $cwd
+
+ cleanup_test_files $TESTDIR/basedir
+
+ if [[ -e $TESTDIR/$ARCHIVEFILE ]]; then
+ log_must $RM -f $TESTDIR/$ARCHIVEFILE
+ fi
+
+ return 0
+}
+
+# owner@ group group_users other_users
+set -A users \
+ "root" "root" "$ZFS_ACL_ADMIN" "$ZFS_ACL_OTHER1" \
+ "$ZFS_ACL_STAFF1" "$ZFS_ACL_STAFF_GROUP" "$ZFS_ACL_STAFF2" "$ZFS_ACL_OTHER1"
+
+set -A a_access \
+ "write_xattr:allow" \
+ "write_xattr:deny"
+
+set -A a_flag "owner@" "group@" "everyone@"
+
+MYTESTFILE=$STF_SUITE/include/default.cfg
+
+log_assert "Verify that the permission of write_xattr for " \
+ "owner/group/everyone while remove extended attributes are correct."
+log_onexit cleanup
+
+function operate_node #user node acl
+{
+ typeset user=$1
+ typeset node=$2
+ typeset acl_t=$3
+ typeset ret
+
+ if [[ $user == "" || $node == "" ]]; then
+ log_fail "user, node are not defined."
+ fi
+
+ chgusr_exec $user $RUNAT $node $RM -f attr.0 ; ret=$?
+
+ if [[ $ret -eq 0 ]]; then
+ log_must cleanup_test_files $TESTDIR/basedir
+ log_must $TAR xpf@ $TESTDIR/$ARCHIVEFILE
+ fi
+
+ return $ret
+}
+
+function logname #acl_target owner user
+{
+ typeset acl_target=$1
+ typeset owner=$2
+ typeset user=$3
+ typeset ret="log_mustnot"
+
+ # To super user, read and write deny permission was override.
+ if [[ $user == root || $owner == $user ]] then
+ ret="log_must"
+ fi
+
+ print $ret
+}
+
+function check_chmod_results #node flag acl_target owner g_usr o_usr
+{
+ typeset node=$1
+ typeset flag=$2
+ typeset acl_target=$2:$3
+ typeset owner=$4
+ typeset g_usr=$5
+ typeset o_usr=$6
+ typeset log
+
+ if [[ $flag == "owner@" || $flag == "everyone@" ]]; then
+ log=$(logname $acl_target $owner $ZFS_ACL_CUR_USER)
+ $log operate_node $ZFS_ACL_CUR_USER $node $acl_target
+ fi
+ if [[ $flag == "group@" || $flag == "everyone@" ]]; then
+ log=$(logname $acl_target $owner $g_usr)
+ $log operate_node $g_usr $node $acl_target
+ fi
+ if [[ $flag == "everyone@" ]]; then
+ log=$(logname $acl_target $owner $o_usr)
+ $log operate_node $o_usr $node $acl_target
+ fi
+}
+
+function test_chmod_basic_access #node owner g_usr o_usr
+{
+ typeset node=${1%/}
+ typeset owner=$2
+ typeset g_usr=$3
+ typeset o_usr=$4
+ typeset flag acl_p acl_t parent
+
+ parent=${node%/*}
+
+ for flag in ${a_flag[@]}; do
+ for acl_t in "${a_access[@]}"; do
+ log_must usr_exec $CHMOD A+$flag:$acl_t $node
+
+ log_must $TAR cpf@ $TESTDIR/$ARCHIVEFILE basedir
+
+ check_chmod_results "$node" "$flag" \
+ "$acl_t" "$owner" "$g_usr" "$o_usr"
+
+ log_must usr_exec $CHMOD A0- $node
+ done
+ done
+}
+
+function setup_test_files #base_node user group
+{
+ typeset base_node=$1
+ typeset user=$2
+ typeset group=$3
+
+ cleanup_test_files $base_node
+
+ log_must $MKDIR -p $base_node
+ log_must $CHOWN $user:$group $base_node
+
+ log_must set_cur_usr $user
+
+ # Prepare all files/sub-dirs for testing.
+
+ file0=$base_node/testfile_rm
+
+ dir0=$base_node/testdir_rm
+
+ log_must usr_exec $TOUCH $file0
+ log_must usr_exec $CHMOD 444 $file0
+
+ log_must usr_exec $RUNAT $file0 $CP $MYTESTFILE attr.0
+
+ log_must usr_exec $MKDIR -p $dir0
+ log_must usr_exec $CHMOD 555 $dir0
+
+ log_must usr_exec $RUNAT $dir0 $CP $MYTESTFILE attr.0
+
+ log_must usr_exec $CHMOD 555 $base_node
+ return 0
+}
+
+function cleanup_test_files #base_node
+{
+ typeset base_node=$1
+
+ if [[ -d $base_node ]]; then
+ log_must $RM -rf $base_node
+ elif [[ -e $base_node ]]; then
+ log_must $RM -f $base_node
+ fi
+
+ return 0
+}
+
+typeset cwd=$PWD
+typeset ARCHIVEFILE=archive.tar
+
+test_requires RUNAT ZFS_XATTR
+
+typeset -i i=0
+typeset -i j=0
+typeset target
+
+while (( i < ${#users[@]} )); do
+ setup_test_files $TESTDIR/basedir ${users[i]} ${users[((i+1))]}
+ cd $TESTDIR
+
+ j=0
+ while (( j < 1 )); do
+ eval target=\$file$j
+ test_chmod_basic_access $target ${users[i]} \
+ "${users[((i+2))]}" "${users[((i+3))]}"
+
+ eval target=\$dir$j
+ test_chmod_basic_access $target ${users[i]} \
+ "${users[((i+2))]}" "${users[((i+3))]}"
+
+ (( j = j + 1 ))
+ done
+
+ (( i += 4 ))
+done
+
+log_pass "Verify that the permission of write_xattr for " \
+ "owner/group/everyone while remove extended attributes are correct."
diff --git a/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_cp_001_pos.ksh b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_cp_001_pos.ksh
new file mode 100644
index 000000000000..621d718bb57a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_cp_001_pos.ksh
@@ -0,0 +1,110 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_cp_001_pos
+#
+# DESCRIPTION:
+# Verify that '/bin/cp [-p]' supports ZFS ACL
+#
+# STRATEGY:
+# 1. Create file and directory in zfs filesystem
+# 2. Set special ACE to the file and directory
+# 3. Copy the file/directory within and across zfs file system
+# 4. Verify that the ACL of file/directroy is not changed, when you are
+# inserting an ACL with a user: or group: entry on the top.
+# (abstractions entry are treated special, since they represent the
+# traditional permission bit mapping.)
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-10-11)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if datasetexists $TESTPOOL/$TESTFS1; then
+ log_must $ZFS destroy -f $TESTPOOL/$TESTFS1
+ fi
+ [[ -d $TESTDIR1 ]] && log_must $RM -rf $TESTDIR1
+ [[ -d $TESTDIR ]] && log_must $RM -rf $TESTDIR/*
+}
+
+log_assert "Verify that '$CP [-p]' supports ZFS ACLs."
+log_onexit cleanup
+
+test_requires ZFS_ACL
+
+log_note "Create the second zfs file system: $TESTPOOL/$TESTFS1."
+log_must $ZFS create $TESTPOOL/$TESTFS1
+log_must $ZFS set mountpoint=$TESTDIR1 $TESTPOOL/$TESTFS1
+log_must $ZFS set aclmode=passthrough $TESTPOOL/$TESTFS1
+log_must $CHMOD 777 $TESTDIR1
+
+# Define target directory.
+dstdir=$TESTDIR1/dstdir.${TESTCASE_ID}
+
+for user in root $ZFS_ACL_STAFF1; do
+ # Set the current user
+ log_must set_cur_usr $user
+
+ for obj in $testfile $testdir; do
+ # Create source object and target directroy
+ log_must usr_exec $TOUCH $testfile
+ log_must usr_exec $MKDIR $testdir $dstdir
+
+ # Add the new ACE on the head.
+ log_must usr_exec $CHMOD \
+ A0+user:$ZFS_ACL_OTHER1:read_acl:deny $obj
+
+ cmd_str="$CP -p"
+ [[ -d $obj ]] && cmd_str="$CP -rp"
+ log_must usr_exec $cmd_str $obj $dstdir
+ log_must usr_exec $cmd_str $obj $TESTDIR1
+
+ for dir in $dstdir $TESTDIR1; do
+ log_must compare_modes $obj $dir/${obj##*/}
+ log_must compare_acls $obj $dir/${obj##*/}
+ done
+
+ # Delete all the test file and directory
+ log_must usr_exec $RM -rf $TESTDIR/* $TESTDIR1/*
+ done
+done
+
+log_pass "'$CP [-p]' succeeds to support ZFS ACLs."
diff --git a/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_cp_002_pos.ksh b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_cp_002_pos.ksh
new file mode 100644
index 000000000000..43975aa4117a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_cp_002_pos.ksh
@@ -0,0 +1,116 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_cp_002_pos
+#
+# DESCRIPTION:
+# Verify that '/bin/cp [-p@]' supports ZFS ACL & xattrs
+#
+# STRATEGY:
+# 1. Create file and directory in zfs filesystem
+# 2. Set special ACE to the file and directory
+# 3. Create xattr of the file and directory
+# 4. Copy the file/directory within and across zfs file system
+# 5. Verify that the ACL & xattrs of the file/directroy is not changed,
+# when you are inserting an ACL with user: or group: entry on the top.
+# (abstractions entry are treated special, since they represent the
+# traditional permission bit mapping.)
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-10-11)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if datasetexists $TESTPOOL/$TESTFS1; then
+ log_must $ZFS destroy -f $TESTPOOL/$TESTFS1
+ fi
+ [[ -d $TESTDIR1 ]] && log_must $RM -rf $TESTDIR1
+ [[ -d $TESTDIR ]] && log_must $RM -rf $TESTDIR/*
+}
+
+log_assert "Verify that '$CP [-p]' supports ZFS ACLs."
+log_onexit cleanup
+
+test_requires RUNAT ZFS_ACL ZFS_XATTR
+
+log_note "Create the second zfs file system: $TESTPOOL/$TESTFS1."
+log_must $ZFS create $TESTPOOL/$TESTFS1
+log_must $ZFS set mountpoint=$TESTDIR1 $TESTPOOL/$TESTFS1
+log_must $ZFS set aclmode=passthrough $TESTPOOL/$TESTFS1
+log_must $CHMOD 777 $TESTDIR1
+
+# Define target directory.
+dstdir=$TESTDIR1/dstdir.${TESTCASE_ID}
+MYTESTFILE=$STF_SUITE/include/default.cfg
+
+for user in root $ZFS_ACL_STAFF1; do
+ # Set the current user
+ log_must set_cur_usr $user
+
+ for obj in $testfile $testdir; do
+ # Create source object and target directroy
+ log_must usr_exec $TOUCH $testfile
+ log_must usr_exec $MKDIR $testdir $dstdir
+
+ log_must usr_exec $RUNAT $testfile $CP $MYTESTFILE attr.0
+ log_must usr_exec $RUNAT $testdir $CP $MYTESTFILE attr.0
+
+ # Add the new ACE on the head.
+ log_must usr_exec $CHMOD \
+ A0+user:$ZFS_ACL_OTHER1:read_acl:deny $obj
+
+ cmd_str="$CP -p@"
+ [[ -d $obj ]] && cmd_str="$CP -rp@"
+ log_must usr_exec $cmd_str $obj $dstdir
+ log_must usr_exec $cmd_str $obj $TESTDIR1
+
+ for dir in $dstdir $TESTDIR1; do
+ log_must compare_modes $obj $dir/${obj##*/}
+ log_must compare_acls $obj $dir/${obj##*/}
+ log_must compare_xattrs $obj $dir/${obj##*/}
+ done
+
+ # Delete all the test file and directory
+ log_must usr_exec $RM -rf $TESTDIR/* $TESTDIR1/*
+ done
+done
+
+log_pass "'$CP [-p@]' succeeds to support ZFS ACLs."
diff --git a/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_cpio_001_pos.ksh b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_cpio_001_pos.ksh
new file mode 100644
index 000000000000..9aa5a92a9e87
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_cpio_001_pos.ksh
@@ -0,0 +1,135 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_cpio_001_pos
+#
+# DESCRIPTION:
+# Verify that '$CPIO' command with -P option supports to archive ZFS ACLs
+#
+# STRATEGY:
+# 1. Create file and directory in zfs filesystem
+# 2. Add new ACE in ACL or change mode of file and directory
+# 3. Use $CPIO to archive file and directory
+# 4. Extract the archive file
+# 5. Verify that the restored ACLs of file and directory identify
+# with the origional ones.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-09-26)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if datasetexists $TESTPOOL/$TESTFS1; then
+ log_must $ZFS destroy -f $TESTPOOL/$TESTFS1
+ fi
+ if (( ${#orig_dir} != 0 )); then
+ cd $orig_dir
+ fi
+ [[ -d $TESTDIR1 ]] && log_must $RM -rf $TESTDIR1
+ [[ -d $TESTDIR ]] && log_must $RM -rf $TESTDIR/*
+}
+
+log_assert "Verify that '$CPIO' command supports to archive ZFS ACLs."
+log_onexit cleanup
+
+test_requires ZFS_ACL
+
+set -A ops "A+everyone@:execute:allow" \
+ "A3+user:$ZFS_ACL_OTHER1:write_data:deny" \
+ "A5+group:$ZFS_ACL_OTHER_GROUP:read_data:deny" \
+ "A0+user:$ZFS_ACL_OTHER1:write_data:deny" \
+ "A1=user:$ZFS_ACL_STAFF1:write_data:deny" \
+ "A5=group:$ZFS_ACL_STAFF_GROUP:write_data:deny"
+
+log_note "Create second zfs file system to restore the cpio archive."
+log_must $ZFS create $TESTPOOL/$TESTFS1
+log_must $ZFS set mountpoint=$TESTDIR1 $TESTPOOL/$TESTFS1
+log_must $CHMOD 777 $TESTDIR1
+
+# Define test fine and record the original directory.
+CPIOFILE=cpiofile.${TESTCASE_ID}
+file=$TESTFILE0
+dir=dir.${TESTCASE_ID}
+orig_dir=$PWD
+
+typeset user
+for user in root $ZFS_ACL_STAFF1; do
+ # Set the current user
+ log_must set_cur_usr $user
+
+ typeset -i i=0
+ while (( i < ${#ops[*]} )); do
+ log_note "Create file $file and directory $dir " \
+ "in zfs filesystem. "
+ cd $TESTDIR
+ log_must usr_exec $TOUCH $file
+ log_must usr_exec $MKDIR $dir
+
+ log_note "Change the ACLs of file and directory with " \
+ "'$CHMOD ${ops[i]}'."
+ for obj in $file $dir; do
+ log_must usr_exec $CHMOD ${ops[i]} $obj
+ done
+
+ log_note "Archive the file and directory."
+ cd $TESTDIR
+ log_must eval "usr_exec $LS | " \
+ "usr_exec $CPIO -ocP -O $CPIOFILE > /dev/null 2>&1"
+
+ log_note "Restore the cpio archive."
+ log_must usr_exec $MV $CPIOFILE $TESTDIR1
+ cd $TESTDIR1
+ log_must eval "usr_exec $CAT $CPIOFILE | " \
+ "usr_exec $CPIO -icP > /dev/null 2>&1"
+
+ log_note "Verify that the ACLs of restored file/directory " \
+ "have no changes."
+ for obj in $file $dir; do
+ log_must compare_modes $TESTDIR/$obj $TESTDIR1/$obj
+ log_must compare_acls $TESTDIR/$obj $TESTDIR1/$obj
+ done
+
+ log_must usr_exec $RM -rf $TESTDIR/* $TESTDIR1/*
+
+ (( i = i + 1 ))
+ done
+done
+
+log_pass "'$CPIO' command succeeds to support ZFS ACLs."
diff --git a/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_cpio_002_pos.ksh b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_cpio_002_pos.ksh
new file mode 100644
index 000000000000..fbf62fda6a06
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_cpio_002_pos.ksh
@@ -0,0 +1,139 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_cpio_002_pos
+#
+# DESCRIPTION:
+# Verify that '$CPIO' command with -P@ option supports to archive ZFS ACLs
+#
+# STRATEGY:
+# 1. Create file and directory in zfs filesystem
+# 2. Add new ACE in ACL or change mode of file and directory
+# 3. Create xattr of the file and directory
+# 4. Use $CPIO to archive file and directory
+# 5. Extract the archive file
+# 6. Verify that the restored ACLs of file and directory identify
+# with the origional ones.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-09-26)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if datasetexists $TESTPOOL/$TESTFS1; then
+ log_must $ZFS destroy -f $TESTPOOL/$TESTFS1
+ fi
+ if (( ${#orig_dir} != 0 )); then
+ cd $orig_dir
+ fi
+ [[ -d $TESTDIR1 ]] && log_must $RM -rf $TESTDIR1
+ [[ -d $TESTDIR ]] && log_must $RM -rf $TESTDIR/*
+}
+
+log_assert "Verify that '$CPIO' command supports to archive ZFS ACLs & xattrs."
+log_onexit cleanup
+
+test_requires RUNAT ZFS_ACL ZFS_XATTR
+
+set -A ops "A+everyone@:execute:allow" \
+ "A3+user:$ZFS_ACL_OTHER1:write_data:deny" \
+ "A5+group:$ZFS_ACL_OTHER_GROUP:read_data:deny" \
+ "A0+user:$ZFS_ACL_OTHER1:write_data:deny"
+
+log_note "Create second zfs file system to restore the cpio archive."
+log_must $ZFS create $TESTPOOL/$TESTFS1
+log_must $ZFS set mountpoint=$TESTDIR1 $TESTPOOL/$TESTFS1
+log_must $CHMOD 777 $TESTDIR1
+
+# Define test fine and record the original directory.
+CPIOFILE=cpiofile.${TESTCASE_ID}
+file=$TESTFILE0
+dir=dir.${TESTCASE_ID}
+orig_dir=$PWD
+MYTESTFILE=$STF_SUITE/include/default.cfg
+
+typeset user
+for user in root $ZFS_ACL_STAFF1; do
+ # Set the current user
+ log_must set_cur_usr $user
+
+ typeset -i i=0
+ while (( i < ${#ops[*]} )); do
+ log_note "Create file $file and directory $dir " \
+ "in zfs filesystem. "
+ cd $TESTDIR
+ log_must usr_exec $TOUCH $file
+ log_must usr_exec $MKDIR $dir
+ log_must usr_exec $RUNAT $file $CP $MYTESTFILE attr.0
+ log_must usr_exec $RUNAT $dir $CP $MYTESTFILE attr.0
+
+ log_note "Change the ACLs of file and directory with " \
+ "'$CHMOD ${ops[i]}'."
+ for obj in $file $dir; do
+ log_must usr_exec $CHMOD ${ops[i]} $obj
+ done
+
+ log_note "Archive the file and directory."
+ cd $TESTDIR
+ log_must eval "usr_exec $LS | " \
+ "usr_exec $CPIO -ocP@ -O $CPIOFILE > /dev/null 2>&1"
+
+ log_note "Restore the cpio archive."
+ log_must usr_exec $MV $CPIOFILE $TESTDIR1
+ cd $TESTDIR1
+ log_must eval "usr_exec $CAT $CPIOFILE | " \
+ "usr_exec $CPIO -icP@ > /dev/null 2>&1"
+
+ log_note "Verify that the ACLs of restored file/directory " \
+ "have no changes."
+ for obj in $file $dir; do
+ log_must compare_modes $TESTDIR/$obj $TESTDIR1/$obj
+ log_must compare_acls $TESTDIR/$obj $TESTDIR1/$obj
+ log_must compare_xattrs $TESTDIR/$obj $TESTDIR1/$obj
+ done
+
+ log_must usr_exec $RM -rf $TESTDIR/* $TESTDIR1/*
+
+ (( i = i + 1 ))
+ done
+done
+
+log_pass "'$CPIO' command succeeds to support ZFS ACLs & xattrs."
diff --git a/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_find_001_pos.ksh b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_find_001_pos.ksh
new file mode 100644
index 000000000000..f100b5976f84
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_find_001_pos.ksh
@@ -0,0 +1,139 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_find_001_pos
+#
+# DESCRIPTION:
+# Verify that '$FIND' command with '-ls' and '-acl' options supports ZFS ACL
+#
+# STRATEGY:
+# 1. Create 5 files and 5 directories in zfs filesystem
+# 2. Select a file or directory and add a few ACEs to it
+# 3. Use $FIND -ls to check the "+" existen only with the selected file or
+# directory
+# 4. Use $FIND -acl to check only the selected file/directory in the list
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-09-26)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ [[ -d $TESTDIR ]] && $RM -rf $TESTDIR/*
+ (( ${#cwd} != 0 )) && cd $cwd
+ (( ${#mask} != 0 )) && $UMASK $mask
+}
+
+function find_ls_acl #<opt> <obj>
+{
+ typeset opt=$1 # -ls or -acl
+ typeset obj=$2
+ typeset rst_str=""
+
+ if [[ $opt == "ls" ]]; then
+ rst_str=`$FIND . -ls | $GREP "+" | $AWK '{print $11}'`
+ else
+ rst_str=`$FIND . -acl`
+ fi
+
+ if [[ $rst_str == "./$obj" ]]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+log_assert "Verify that '$FIND' command supports ZFS ACLs."
+log_onexit cleanup
+
+test_requires ZFS_ACL
+
+set -A ops " A+everyone@:read_data:allow" \
+ " A+owner@:write_data:allow"
+
+f_base=testfile.${TESTCASE_ID} # Base file name for tested files
+d_base=testdir.${TESTCASE_ID} # Base directory name for tested directory
+cwd=$PWD
+mask=`$UMASK`
+
+log_note "Create five files and directories in the zfs filesystem. "
+cd $TESTDIR
+$UMASK 0777
+typeset -i i=0
+while (( i < 5 ))
+do
+ log_must $TOUCH ${f_base}.$i
+ log_must $MKDIR ${d_base}.$i
+
+ (( i = i + 1 ))
+done
+
+for obj in ${f_base}.3 ${d_base}.3
+do
+ i=0
+ while (( i < ${#ops[*]} ))
+ do
+ log_must $CHMOD ${ops[i]} $obj
+
+ (( i = i + 1 ))
+ done
+
+ for opt in "ls" "acl"
+ do
+ log_must find_ls_acl $opt $obj
+ done
+
+ log_note "Check the file access permission according to the added ACEs"
+ if [[ ! -r $obj || ! -w $obj ]]; then
+ log_fail "The added ACEs for $obj cannot be represented in " \
+ "mode."
+ fi
+
+ log_note "Remove the added ACEs from ACL."
+ i=0
+ while (( i < ${#ops[*]} ))
+ do
+ log_must $CHMOD A0- $obj
+
+ (( i = i + 1 ))
+ done
+done
+
+log_pass "'$FIND' command succeeds to support ZFS ACLs."
diff --git a/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_ls_001_pos.ksh b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_ls_001_pos.ksh
new file mode 100644
index 000000000000..9e8b9fd49dcf
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_ls_001_pos.ksh
@@ -0,0 +1,119 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_ls_001_pos
+#
+# DESCRIPTION:
+# Verify that '/bin/ls' command option supports ZFS ACL
+#
+# STRATEGY:
+# 1. Create file and directory in zfs filesystem
+# 2. Verify that 'ls [-dv]' can list the ACEs of ACL of
+# file/directroy
+# 3. Change the file/directory's acl
+# 4. Verify that 'ls -l' can use the '+' to indicate the non-trivial
+# acl.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-10-11)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ (( ${#cwd} != 0 )) && cd $cwd
+ [[ -d $TESTDIR ]] && log_must $RM -rf $TESTDIR/*
+ (( ${#mask} != 0 )) && log_must $UMASK $mask
+}
+
+log_assert "Verify that '$LS' command supports ZFS ACLs."
+log_onexit cleanup
+
+test_requires ZFS_ACL
+
+file=$TESTFILE0
+dir=dir.${TESTCASE_ID}
+cwd=$PWD
+mask=`$UMASK`
+spec_ace="everyone@:write_acl:allow"
+
+$UMASK 0022
+
+log_note "Create file and directory in the zfs filesystem. "
+cd $TESTDIR
+log_must $TOUCH $file
+log_must $MKDIR $dir
+
+log_note "Verify that '$LS [-dv]' can list file/directory ACEs of its acl."
+
+typeset -i ace_num=0
+for obj in $file $dir
+do
+ typeset ls_str=""
+ if [[ -f $obj ]]; then
+ ls_str="$LS -v"
+ else
+ ls_str="$LS -dv"
+ fi
+
+ for ace_type in "owner@" "group@" "everyone@"
+ do
+ $ls_str $obj | $GREP $ace_type > /dev/null 2>&1
+ (( $? == 0 )) && (( ace_num += 1 ))
+ done
+
+ (( ace_num < 1 )) && \
+ log_fail "'$LS [-dv] fails to list file/directroy acls."
+done
+
+log_note "Verify that '$LS [-dl] [-dv]' can output '+' to indicate " \
+ "the acl existent."
+
+for obj in $file $dir
+do
+ $CHMOD A0+$spec_ace $obj
+
+ log_must eval "$LS -ld -vd $obj | $GREP "+" > /dev/null"
+ log_must plus_sign_check_v $obj
+
+ log_must eval "$LS -ld -vd $obj | $GREP $spec_ace > /dev/null"
+ log_must plus_sign_check_l $obj
+done
+
+log_pass "'$LS' command succeeds to support ZFS ACLs."
diff --git a/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_mv_001_pos.ksh b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_mv_001_pos.ksh
new file mode 100644
index 000000000000..636dffacbb4a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_mv_001_pos.ksh
@@ -0,0 +1,185 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_mv_001_pos
+#
+# DESCRIPTION:
+# Verify that '/bin/mv' supports ZFS ACL
+#
+# STRATEGY:
+# 1. Create file and directory in zfs filesystem
+# 2. Set special ACE to the file and directory
+# 3. Copy the file/directory within and across zfs file system
+# 4. Verify that the ACL of file/directroy is not changed
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-10-11)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ (( ${#cwd} != 0 )) && cd $cwd
+ [[ -d $TESTDIR ]] && log_must $RM -rf $TESTDIR/*
+ [[ -d $TESTDIR1 ]] && log_must $RM -rf $TESTDIR1
+ (( ${#mask} != 0 )) && log_must $UMASK $mask
+}
+
+function testing_mv #<flag for file|dir> <file1|dir1> <file2|dir2>
+{
+ typeset flag=$1
+ set -A obj $2 $3
+ typeset -i i=0
+ typeset orig_acl=""
+ typeset orig_mode=""
+ typeset dst_acl=""
+ typeset dst_mode=""
+
+ if [[ $flag == "f" ]]; then
+ while (( i < ${#obj[*]} ))
+ do
+ orig_acl="$(get_acl ${obj[i]})"
+ orig_mode="$(get_mode ${obj[i]})"
+ if (( i < 1 )); then
+ log_must $MV ${obj[i]} $dst_file
+ dst_acl=$(get_acl $dst_file)
+ dst_mode=$(get_mode $dst_file)
+ else
+ log_must $MV ${obj[i]} $TESTDIR1
+ dst_acl=$(get_acl $TESTDIR1/${obj[i]})
+ dst_mode=$(get_mode $TESTDIR1/${obj[i]})
+ fi
+
+ if [[ "$dst_mode" != "$orig_mode" ]] || \
+ [[ "$dst_acl" != "$orig_acl" ]]; then
+ log_fail "$MV fails to keep the acl for file."
+ fi
+
+ (( i = i + 1 ))
+ done
+ else
+ while (( i < ${#obj[*]} ))
+ do
+ typeset orig_nested_acl=""
+ typeset orig_nested_mode=""
+ typeset dst_nested_acl=""
+ typeset dst_nested_mode=""
+
+ orig_acl=$(get_acl ${obj[i]})
+ orig_mode=$(get_mode ${obj[i]})
+ orig_nested_acl=$(get_acl ${obj[i]}/$nestedfile)
+ orig_nested_mode=$(get_mode ${obj[i]}/$nestedfile)
+ if (( i < 1 )); then
+ log_must $MV ${obj[i]} $dst_dir
+ dst_acl=$(get_acl $dst_dir)
+ dst_mode=$(get_mode $dst_dir)
+ dst_nested_acl=$(get_acl $dst_dir/$nestedfile)
+ dst_nested_mode=$(get_mode $dst_dir/$nestedfile)
+ else
+ log_must $MV ${obj[i]} $TESTDIR1
+ dst_acl=$(get_acl $TESTDIR1/${obj[i]})
+ dst_mode=$(get_mode $TESTDIR1/${obj[i]})
+ dst_nested_acl=$(get_acl \
+ $TESTDIR1/${obj[i]}/$nestedfile)
+ dst_nested_mode=$(get_mode \
+ $TESTDIR1/${obj[i]}/$nestedfile)
+ fi
+
+ if [[ "$orig_mode" != "$dst_mode" ]] || \
+ [[ "$orig_acl" != "$dst_acl" ]] || \
+ [[ "$dst_nested_mode" != "$orig_nested_mode" ]] || \
+ [[ "$dst_nested_acl" != "$orig_nested_acl" ]]; then
+ log_fail "$MV fails to recursively keep the acl for " \
+ "directory."
+ fi
+
+ (( i = i + 1 ))
+ done
+ fi
+}
+
+log_assert "Verify that '$MV' supports ZFS ACLs."
+log_onexit cleanup
+
+test_requires ZFS_ACL
+
+spec_ace="everyone@:execute:allow"
+set -A orig_file "origfile1.${TESTCASE_ID}" "origfile2.${TESTCASE_ID}"
+set -A orig_dir "origdir1.${TESTCASE_ID}" "origdir2.${TESTCASE_ID}"
+nestedfile="nestedfile.${TESTCASE_ID}"
+dst_file=dstfile.${TESTCASE_ID}
+dst_dir=dstdir.${TESTCASE_ID}
+cwd=$PWD
+mask=`$UMASK`
+$UMASK 0022
+
+#
+# This assertion should only test 'mv' within the same filesystem
+#
+TESTDIR1=$TESTDIR/testdir1${TESTCASE_ID}
+
+[[ ! -d $TESTDIR1 ]] && \
+ log_must $MKDIR -p $TESTDIR1
+
+log_note "Create files and directories and set special ace on them for testing. "
+cd $TESTDIR
+typeset -i i=0
+while (( i < ${#orig_file[*]} ))
+do
+ log_must $TOUCH ${orig_file[i]}
+ log_must $CHMOD A0+$spec_ace ${orig_file[i]}
+
+ (( i = i + 1 ))
+done
+i=0
+while (( i < ${#orig_dir[*]} ))
+do
+ log_must $MKDIR ${orig_dir[i]}
+ log_must $TOUCH ${orig_dir[i]}/$nestedfile
+
+ for obj in ${orig_dir[i]} ${orig_dir[i]}/$nestedfile; do
+ log_must $CHMOD A0+$spec_ace $obj
+ done
+
+ (( i = i + 1 ))
+done
+
+testing_mv "f" ${orig_file[0]} ${orig_file[1]}
+testing_mv "d" ${orig_dir[0]} ${orig_dir[1]}
+
+log_pass "'$MV' succeeds to support ZFS ACLs."
diff --git a/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_tar_001_pos.ksh b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_tar_001_pos.ksh
new file mode 100644
index 000000000000..723cd241ae39
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_tar_001_pos.ksh
@@ -0,0 +1,119 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_tar_001_pos
+#
+# DESCRIPTION:
+# Verify that '$TAR' command with -p option supports to archive ZFS ACLs
+#
+# STRATEGY:
+# 1. Create file and directory in zfs filesystem
+# 2. Add new ACE in ACL of file and directory
+# 3. Use $TAR to archive file and directory
+# 4. Extract the archive file
+# 5. Verify that the restored ACLs of file and directory identify
+# with the origional ones.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-09-26)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if datasetexists $TESTPOOL/$TESTFS1; then
+ log_must $ZFS destroy -f $TESTPOOL/$TESTFS1
+ fi
+
+ (( ${#cwd} != 0 )) && cd $cwd
+ [[ -d $TESTDIR1 ]] && log_must $RM -rf $TESTDIR1
+ [[ -d $TESTDIR/ ]] && log_must $RM -rf $TESTDIR/*
+}
+
+log_assert "Verify that '$TAR' command supports to archive ZFS ACLs."
+log_onexit cleanup
+
+test_requires ZFS_ACL
+
+set -A ops " A+everyone@:execute:allow" "a-x" "777"
+
+TARFILE=tarfile.${TESTCASE_ID}.tar
+file=$TESTFILE0
+dir=dir.${TESTCASE_ID}
+cwd=$PWD
+
+log_note "Create second zfs file system to restore the tar archive."
+log_must $ZFS create $TESTPOOL/$TESTFS1
+[[ ! -d $TESTDIR1 ]] && \
+ log_must $MKDIR -p $TESTDIR1
+log_must $ZFS set mountpoint=$TESTDIR1 $TESTPOOL/$TESTFS1
+
+log_note "Create a file: $file, and directory: $dir, in zfs filesystem. "
+cd $TESTDIR
+log_must $TOUCH $file
+log_must $MKDIR $dir
+
+typeset -i i=0
+while (( i < ${#ops[*]} ))
+do
+ log_note "Change the ACLs of file and directory with " \
+ "'$CHMOD ${ops[i]}'."
+ cd $TESTDIR
+ for obj in $file $dir; do
+ log_must $CHMOD ${ops[i]} $obj
+ done
+ log_note "Archive the file and directory."
+ log_must $TAR cpf $TARFILE $file $dir
+
+ log_note "Restore the tar archive."
+ log_must $MV $TARFILE $TESTDIR1
+ cd $TESTDIR1
+ log_must $TAR xpf $TARFILE
+
+ log_note "Verify the ACLs of restored file/directory have no changes."
+ for obj in $file $dir; do
+ log_must compare_modes $TESTDIR/$obj $TESTDIR1/$obj
+ log_must compare_acls $TESTDIR/$obj $TESTDIR1/$obj
+ done
+
+ log_must $RM -rf $TESTDIR1/*
+
+ (( i = i + 1 ))
+done
+
+log_pass "'$TAR' command succeeds to support ZFS ACLs."
diff --git a/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_tar_002_pos.ksh b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_tar_002_pos.ksh
new file mode 100644
index 000000000000..ef7864dc1825
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/nontrivial/zfs_acl_tar_002_pos.ksh
@@ -0,0 +1,126 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_tar_002_pos
+#
+# DESCRIPTION:
+# Verify that '$TAR' command with -p@ option supports to archive ZFS ACLs
+# & xattrs
+#
+# STRATEGY:
+# 1. Create file and directory in zfs filesystem
+# 2. Add new ACE in ACL of file and directory
+# 3. Create xattr of the file and directory
+# 4. Use $TAR cf@ to archive file and directory
+# 5. Use $TAR xf@ to extract the archive file
+# 6. Verify that the restored ACLs & xttrs of file and directory identify
+# with the origional ones.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-12-16)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if datasetexists $TESTPOOL/$TESTFS1; then
+ log_must $ZFS destroy -f $TESTPOOL/$TESTFS1
+ fi
+
+ (( ${#cwd} != 0 )) && cd $cwd
+ [[ -d $TESTDIR1 ]] && log_must $RM -rf $TESTDIR1
+ [[ -d $TESTDIR/ ]] && log_must $RM -rf $TESTDIR/*
+}
+
+log_assert "Verify that '$TAR' command supports to archive ZFS ACLs & xattrs."
+log_onexit cleanup
+
+test_requires RUNAT ZFS_ACL ZFS_XATTR
+
+set -A ops " A+everyone@:execute:allow" "a-x" "777"
+MYTESTFILE=$STF_SUITE/include/default.cfg
+
+TARFILE=tarfile.${TESTCASE_ID}.tar
+cwd=$PWD
+
+log_note "Create second zfs file system to restore the tar archive."
+log_must $ZFS create $TESTPOOL/$TESTFS1
+[[ ! -d $TESTDIR1 ]] && \
+ log_must $MKDIR -p $TESTDIR1
+log_must $ZFS set mountpoint=$TESTDIR1 $TESTPOOL/$TESTFS1
+
+log_note "Create a file: $testfile, and directory: $testdir, in zfs filesystem. " \
+ "And prepare for there xattr files."
+
+for user in root $ZFS_ACL_STAFF1; do
+ # Set the current user
+ log_must set_cur_usr $user
+
+ # Create source object and target directroy
+ cd $TESTDIR
+ log_must usr_exec $TOUCH $testfile
+ log_must usr_exec $MKDIR $testdir
+
+ log_must usr_exec $RUNAT $testfile $CP $MYTESTFILE attr.0
+ log_must usr_exec $RUNAT $testdir $CP $MYTESTFILE attr.0
+
+ # Add the new ACE on the head.
+ log_note "Change the ACLs of file and directory with " \
+ "'$CHMOD ${ops[0]}'."
+ log_must usr_exec $CHMOD ${ops[0]} $testfile
+ log_must usr_exec $CHMOD ${ops[0]} $testdir
+
+ log_note "Archive the file and directory."
+ log_must $TAR cpf@ $TARFILE ${testfile#$TESTDIR/} ${testdir#$TESTDIR/}
+
+ log_note "Restore the tar archive."
+ cd $TESTDIR1
+ log_must $TAR xpf@ $TESTDIR/$TARFILE
+
+ log_note "Verify the ACLs of restored file/directory have no changes."
+ for obj in $testfile $testdir; do
+ log_must compare_modes $obj $TESTDIR1/${obj##*/}
+ log_must compare_acls $obj $TESTDIR1/${obj##*/}
+ log_must compare_xattrs $obj $TESTDIR1/${obj##*/}
+ done
+
+ log_must $RM -rf $TESTDIR/* $TESTDIR1/*
+done
+
+log_pass "'$TAR' command succeeds to support ZFS ACLs."
diff --git a/tests/sys/cddl/zfs/tests/acl/setup.ksh b/tests/sys/cddl/zfs/tests/acl/setup.ksh
new file mode 100644
index 000000000000..4029a68e5893
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/setup.ksh
@@ -0,0 +1,61 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+
+# check svc:/network/nis/client:default state
+# disable it if the state is ON
+# and the state will be restored during cleanup.ksh
+if [[ `$UNAME -s` != "FreeBSD" ]]; then
+ log_must $RM -f $NISSTAFILE
+ if [[ "ON" == $($SVCS -H -o sta svc:/network/nis/client:default) ]]; then
+ log_must $SVCADM disable -t svc:/network/nis/client:default
+ log_must $TOUCH $NISSTAFILE
+ fi
+fi
+
+cleanup_user_group
+
+# Add wheel group user
+log_must add_user wheel $ZFS_ACL_ADMIN
+
+# Create staff group and add two user to it
+log_must add_group $ZFS_ACL_STAFF_GROUP
+log_must add_user $ZFS_ACL_STAFF_GROUP $ZFS_ACL_STAFF1
+log_must add_user $ZFS_ACL_STAFF_GROUP $ZFS_ACL_STAFF2
+
+# Create other group and add two user to it
+log_must add_group $ZFS_ACL_OTHER_GROUP
+log_must add_user $ZFS_ACL_OTHER_GROUP $ZFS_ACL_OTHER1
+log_must add_user $ZFS_ACL_OTHER_GROUP $ZFS_ACL_OTHER2
+
+DISK=${DISKS%% *}
+default_setup_noexit $DISK
+log_must $CHMOD 777 $TESTDIR
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/acl/trivial/Makefile b/tests/sys/cddl/zfs/tests/acl/trivial/Makefile
new file mode 100644
index 000000000000..5a00bc05746b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/trivial/Makefile
@@ -0,0 +1,31 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/acl/trivial
+FILESDIR=${TESTSDIR}
+
+${PACKAGE}FILES+= zfs_acl_chmod_001_pos.ksh
+${PACKAGE}FILES+= zfs_acl_compress_001_pos.ksh
+${PACKAGE}FILES+= zfs_acl_cp_001_pos.ksh
+${PACKAGE}FILES+= zfs_acl_cp_002_neg.ksh
+${PACKAGE}FILES+= zfs_acl_cp_003_neg.ksh
+${PACKAGE}FILES+= zfs_acl_find_001_pos.ksh
+${PACKAGE}FILES+= zfs_acl_find_002_neg.ksh
+${PACKAGE}FILES+= zfs_acl_ls_001_pos.ksh
+${PACKAGE}FILES+= zfs_acl_ls_002_neg.ksh
+${PACKAGE}FILES+= zfs_acl_mv_001_pos.ksh
+${PACKAGE}FILES+= zfs_acl_pack_001_pos.ksh
+${PACKAGE}FILES+= zfs_acl_pax_001_pos.ksh
+${PACKAGE}FILES+= zfs_acl_pax_002_pos.ksh
+${PACKAGE}FILES+= zfs_acl_pax_003_pos.ksh
+${PACKAGE}FILES+= zfs_acl_pax_004_pos.ksh
+${PACKAGE}FILES+= zfs_acl_pax_005_pos.ksh
+${PACKAGE}FILES+= zfs_acl_pax_006_pos.ksh
+${PACKAGE}FILES+= zfs_acl_tar_001_pos.ksh
+${PACKAGE}FILES+= zfs_acl_tar_002_neg.ksh
+
+ATF_TESTS_KSH93+= trivial_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/acl/trivial/trivial_test.sh b/tests/sys/cddl/zfs/tests/acl/trivial/trivial_test.sh
new file mode 100755
index 000000000000..1acb5e2c2083
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/trivial/trivial_test.sh
@@ -0,0 +1,514 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zfs_acl_chmod_001_pos cleanup
+zfs_acl_chmod_001_pos_head()
+{
+ atf_set "descr" "Verify chmod permission settings on files and directories"
+}
+zfs_acl_chmod_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_chmod_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_chmod_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_compress_001_pos cleanup
+zfs_acl_compress_001_pos_head()
+{
+ atf_set "descr" "Compress will keep file attribute intact after the file iscompressed and uncompressed"
+ atf_set "require.config" "zfs_acl zfs_xattr"
+}
+zfs_acl_compress_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_compress_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_compress_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_cp_001_pos cleanup
+zfs_acl_cp_001_pos_head()
+{
+ atf_set "descr" "Verifies that cp will include file attribute when using the -@ flag"
+ atf_set "require.config" "zfs_acl zfs_xattr"
+}
+zfs_acl_cp_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_cp_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_cp_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_cp_002_neg cleanup
+zfs_acl_cp_002_neg_head()
+{
+ atf_set "descr" "Verifies that cp will not include file attribute when the -@ flagis not present."
+ atf_set "require.config" "zfs_acl zfs_xattr"
+}
+zfs_acl_cp_002_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_cp_002_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_cp_002_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_cp_003_neg cleanup
+zfs_acl_cp_003_neg_head()
+{
+ atf_set "descr" "Verifies that cp won't be able to include file attribute whenattribute is unreadable (except root)"
+ atf_set "require.config" "zfs_acl zfs_xattr"
+ atf_set "require.progs" "ksh93 runat"
+}
+zfs_acl_cp_003_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_cp_003_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_cp_003_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_find_001_pos cleanup
+zfs_acl_find_001_pos_head()
+{
+ atf_set "descr" "Verifies ability to find files with attribute with-xattr flag and using '-exec runat ls'"
+ atf_set "require.config" "zfs_acl zfs_xattr"
+}
+zfs_acl_find_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_find_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_find_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_find_002_neg cleanup
+zfs_acl_find_002_neg_head()
+{
+ atf_set "descr" "verifies -xattr doesn't include files withoutattribute and using '-exec runat ls'"
+ atf_set "require.config" "zfs_acl zfs_xattr"
+ atf_set "require.progs" "ksh93 runat"
+}
+zfs_acl_find_002_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_find_002_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_find_002_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_ls_001_pos cleanup
+zfs_acl_ls_001_pos_head()
+{
+ atf_set "descr" "Verifies that ls displays @ in the file permissions using ls -@for files with attribute."
+ atf_set "require.config" "zfs_acl zfs_xattr"
+}
+zfs_acl_ls_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_ls_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_ls_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_ls_002_neg cleanup
+zfs_acl_ls_002_neg_head()
+{
+ atf_set "descr" "Verifies that ls doesn't display @ in the filepermissions using ls -@ for files without attribute."
+ atf_set "require.config" "zfs_acl zfs_xattr"
+ atf_set "require.progs" "ksh93 runat"
+}
+zfs_acl_ls_002_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_ls_002_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_ls_002_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_mv_001_pos cleanup
+zfs_acl_mv_001_pos_head()
+{
+ atf_set "descr" "Verifies that mv will include file attribute."
+ atf_set "require.config" "zfs_acl zfs_xattr"
+}
+zfs_acl_mv_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_mv_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_mv_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_pack_001_pos cleanup
+zfs_acl_pack_001_pos_head()
+{
+ atf_set "descr" "Verifies that pack will keep file attribute intact after the fileis packed and unpacked"
+ atf_set "require.config" "zfs_acl zfs_xattr"
+ atf_set "require.progs" "ksh93 unpack pack"
+}
+zfs_acl_pack_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_pack_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_pack_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_pax_001_pos cleanup
+zfs_acl_pax_001_pos_head()
+{
+ atf_set "descr" "Verify include attribute in pax archive and restore with paxshould succeed."
+ atf_set "require.config" "zfs_acl zfs_xattr"
+ atf_set "require.progs" "ksh93 pax"
+}
+zfs_acl_pax_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_pax_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_pax_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_pax_002_pos cleanup
+zfs_acl_pax_002_pos_head()
+{
+ atf_set "descr" "Verify include attribute in pax archive and restore with tarshould succeed."
+ atf_set "require.config" "zfs_acl zfs_xattr"
+ atf_set "require.progs" "ksh93 pax"
+}
+zfs_acl_pax_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_pax_002_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_pax_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_pax_003_pos cleanup
+zfs_acl_pax_003_pos_head()
+{
+ atf_set "descr" "Verify include attribute in pax archive and restore with cpioshould succeed."
+ atf_set "require.config" "zfs_acl zfs_xattr"
+ atf_set "require.progs" "ksh93 pax"
+}
+zfs_acl_pax_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_pax_003_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_pax_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_pax_004_pos cleanup
+zfs_acl_pax_004_pos_head()
+{
+ atf_set "descr" "Verify files include attribute in pax archive and restore with paxshould succeed."
+ atf_set "require.config" "zfs_acl zfs_xattr"
+ atf_set "require.progs" "ksh93 pax"
+}
+zfs_acl_pax_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_pax_004_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_pax_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_pax_005_pos cleanup
+zfs_acl_pax_005_pos_head()
+{
+ atf_set "descr" "Verify files include attribute in cpio archive and restore withcpio should succeed."
+ atf_set "require.config" "zfs_acl zfs_xattr"
+ atf_set "require.progs" "ksh93 pax"
+}
+zfs_acl_pax_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_pax_005_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_pax_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_pax_006_pos cleanup
+zfs_acl_pax_006_pos_head()
+{
+ atf_set "descr" "Verify files include attribute in tar archive and restore withtar should succeed."
+ atf_set "require.config" "zfs_acl zfs_xattr"
+ atf_set "require.progs" "ksh93 pax"
+}
+zfs_acl_pax_006_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_pax_006_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_pax_006_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_tar_001_pos cleanup
+zfs_acl_tar_001_pos_head()
+{
+ atf_set "descr" "Verifies that tar will include file attribute when @ flag ispresent."
+ atf_set "require.config" "zfs_acl zfs_xattr"
+}
+zfs_acl_tar_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_tar_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_tar_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_acl_tar_002_neg cleanup
+zfs_acl_tar_002_neg_head()
+{
+ atf_set "descr" "Verifies that tar will not include files attribute when @ flag isnot present"
+ atf_set "require.config" "zfs_acl zfs_xattr"
+}
+zfs_acl_tar_002_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/../setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_acl_tar_002_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_acl_tar_002_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/../acl.cfg
+
+ ksh93 $(atf_get_srcdir)/../cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zfs_acl_chmod_001_pos
+ atf_add_test_case zfs_acl_compress_001_pos
+ atf_add_test_case zfs_acl_cp_001_pos
+ atf_add_test_case zfs_acl_cp_002_neg
+ atf_add_test_case zfs_acl_cp_003_neg
+ atf_add_test_case zfs_acl_find_001_pos
+ atf_add_test_case zfs_acl_find_002_neg
+ atf_add_test_case zfs_acl_ls_001_pos
+ atf_add_test_case zfs_acl_ls_002_neg
+ atf_add_test_case zfs_acl_mv_001_pos
+ atf_add_test_case zfs_acl_pack_001_pos
+ atf_add_test_case zfs_acl_pax_001_pos
+ atf_add_test_case zfs_acl_pax_002_pos
+ atf_add_test_case zfs_acl_pax_003_pos
+ atf_add_test_case zfs_acl_pax_004_pos
+ atf_add_test_case zfs_acl_pax_005_pos
+ atf_add_test_case zfs_acl_pax_006_pos
+ atf_add_test_case zfs_acl_tar_001_pos
+ atf_add_test_case zfs_acl_tar_002_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_chmod_001_pos.ksh b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_chmod_001_pos.ksh
new file mode 100644
index 000000000000..033f5abb6d15
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_chmod_001_pos.ksh
@@ -0,0 +1,146 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_chmod_001_pos
+#
+# DESCRIPTION:
+# Verify chmod permission settings on files and directories, as both root
+# and non-root users.
+#
+# STRATEGY:
+# 1. Loop root and $ZFS_ACL_STAFF1 as root and non-root users.
+# 2. Create test file and directory in zfs filesystem.
+# 3. Execute 'chmod' with specified options.
+# 4. Check 'ls -l' output and compare with expect results.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-09-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+# "init_map" "options" "expect_map"
+set -A argv \
+ "000" "a+rw" "rw-rw-rw-" "000" "a+rwx" "rwxrwxrwx" \
+ "000" "u+xr" "r-x------" "000" "gu-xw" "---------" \
+ "644" "a-r" "-w-------" "644" "augo-x" "rw-r--r--" \
+ "644" "=x" "--x--x--x" "644" "u-rw" "---r--r--" \
+ "644" "uo+x" "rwxr--r-x" "644" "ga-wr" "---------" \
+ "777" "augo+x" "rwxrwxrwx" "777" "go-xr" "rwx-w--w-" \
+ "777" "o-wx" "rwxrwxr--" "777" "ou-rx" "-w-rwx-w-" \
+ "777" "a+rwx" "rwxrwxrwx" "777" "u=rw" "rw-rwxrwx" \
+ "000" "123" "--x-w--wx" "000" "412" "r----x-w-" \
+ "231" "562" "r-xrw--w-" "712" "000" "---------" \
+ "777" "121" "--x-w---x" "123" "775" "rwxrwxr-x"
+
+log_assert " Verify chmod permission settings on files and directories"
+log_onexit cleanup
+
+#
+# Verify file or directory have correct map after chmod
+#
+# $1 file or directory
+#
+function test_chmod_mapping #<file-dir>
+{
+ typeset node=$1
+ typeset -i i=0
+
+ while (( i < ${#argv[@]} )); do
+ usr_exec $CHMOD ${argv[i]} $node
+ if (($? != 0)); then
+ log_note "usr_exec $CHMOD ${argv[i]} $node"
+ return 1
+ fi
+
+ usr_exec $CHMOD ${argv[((i + 1))]} $node
+ if (($? != 0)); then
+ log_note "usr_exec $CHMOD ${argv[((i + 1))]} $node"
+ return 1
+ fi
+
+ typeset mode
+ mode=$(get_mode ${node})
+
+ if [[ $mode != "-${argv[((i + 2))]}"* && \
+ $mode != "d${argv[((i + 2))]}"* ]]
+ then
+ log_fail "FAIL: '${argv[i]}' '${argv[((i + 1))]}' \
+ '${argv[((i + 2))]}'"
+ fi
+
+ (( i += 3 ))
+ done
+
+ return 0
+}
+
+for user in root $ZFS_ACL_STAFF1; do
+ log_must set_cur_usr $user
+
+ # Test file
+ log_must usr_exec $TOUCH $testfile
+ log_must test_chmod_mapping $testfile
+
+ if [ "$ZFS_ACL" != "" ] ; then
+ log_must $CHMOD A+user:$ZFS_ACL_STAFF2:write_acl:allow $testfile
+ fi
+ log_must set_cur_usr $ZFS_ACL_STAFF2
+
+ # Test directory
+ log_must usr_exec $MKDIR $testdir
+ log_must test_chmod_mapping $testdir
+
+ if [ "$ZFS_ACL" != "" ] ; then
+ # Grant privileges of write_acl and retest the chmod commands.
+ acl="user:$ZFS_ACL_STAFF2:write_acl:allow"
+ log_must usr_exec $CHMOD A+${acl} $testfile
+ log_must usr_exec $CHMOD A+${acl} $testdir
+
+ log_must set_cur_usr $ZFS_ACL_STAFF2
+ log_must test_chmod_mapping $testfile
+ log_must test_chmod_mapping $testdir
+ fi
+
+ log_must set_cur_usr $user
+
+ log_must usr_exec $RM $testfile
+ log_must usr_exec $RM -rf $testdir
+done
+
+log_pass "Setting permissions using 'chmod' completed successfully."
diff --git a/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_compress_001_pos.ksh b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_compress_001_pos.ksh
new file mode 100644
index 000000000000..16403a37de09
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_compress_001_pos.ksh
@@ -0,0 +1,87 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_compress_001_pos
+#
+# DESCRIPTION:
+# The function verifies that compress will keep file attribute intact
+# after the file is compressed and uncompressed.
+#
+# STRATEGY:
+# 1. In directory A, create several files and add attribute files for them
+# 2. Save all files and their attribute files cksum value, then compress
+# all the files.
+# 3. Move them to another directory B.
+# 4. Uncompress them and calculate all the files and attribute files cksum
+# 5. Verify all the cksum are identical
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Compress will keep file attribute intact after the file is " \
+ "compressed and uncompressed"
+log_onexit cleanup
+
+test_requires ZFS_ACL ZFS_XATTR
+
+set -A BEFORE_FCKSUM
+set -A BEFORE_ACKSUM
+set -A AFTER_FCKSUM
+set -A AFTER_ACKSUM
+
+for user in root $ZFS_ACL_STAFF1; do
+ log_must set_cur_usr $user
+
+ log_must create_files $TESTDIR
+
+ log_must cksum_files $INI_DIR BEFORE_FCKSUM BEFORE_ACKSUM
+ log_must usr_exec $COMPRESS $INI_DIR/*
+ log_must usr_exec $MV $INI_DIR/* $TST_DIR
+ log_must usr_exec $UNCOMPRESS $TST_DIR/*
+ log_must cksum_files $TST_DIR AFTER_FCKSUM AFTER_ACKSUM
+
+ log_must compare_cksum BEFORE_FCKSUM AFTER_FCKSUM
+ log_must compare_cksum BEFORE_ACKSUM AFTER_ACKSUM
+
+ log_must cleanup
+done
+
+log_pass "compress/uncompress test passed."
diff --git a/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_cp_001_pos.ksh b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_cp_001_pos.ksh
new file mode 100644
index 000000000000..a8511ddfd8e9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_cp_001_pos.ksh
@@ -0,0 +1,90 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_cp_001_pos
+#
+# DESCRIPTION:
+# Verifies that cp will include file attribute when using the -@ flag
+#
+# STRATEGY:
+# 1. In directory A, create several files and add attribute files for them
+# 2. Save all files and their attribute files cksum value, then 'cp -@p'
+# all the files to to another directory B.
+# 3. Calculate all the cksum in directory B.
+# 4. Verify all the cksum are identical
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verifies that cp will include file attribute when using the -@ flag"
+log_onexit cleanup
+
+test_requires ZFS_ACL ZFS_XATTR
+
+set -A BEFORE_FCKSUM
+set -A BEFORE_ACKSUM
+set -A AFTER_FCKSUM
+set -A AFTER_ACKSUM
+
+for user in root $ZFS_ACL_STAFF1; do
+ log_must set_cur_usr $user
+
+ log_must create_files $TESTDIR
+ log_must cksum_files $INI_DIR BEFORE_FCKSUM BEFORE_ACKSUM
+
+ initfiles=$($LS -R $INI_DIR/*)
+ typeset -i i=0
+ while ((i < NUM_FILE)); do
+ f=$(getitem $i $initfiles)
+
+ usr_exec $CP -@p $f $TST_DIR
+
+ ((i += 1))
+ done
+
+ log_must cksum_files $TST_DIR AFTER_FCKSUM AFTER_ACKSUM
+ log_must compare_cksum BEFORE_FCKSUM AFTER_FCKSUM
+ log_must compare_cksum BEFORE_ACKSUM AFTER_ACKSUM
+
+ log_must cleanup
+done
+
+log_pass "'cp -@' include file attribute passed."
diff --git a/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_cp_002_neg.ksh b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_cp_002_neg.ksh
new file mode 100644
index 000000000000..5bd751b98d37
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_cp_002_neg.ksh
@@ -0,0 +1,87 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_cp_002_neg
+#
+# DESCRIPTION:
+# Verifies that cp will not include file attribute when the -@ flag is not
+# present.
+#
+# STRATEGY:
+# 1. In directory A, create several files and add attribute files for them
+# 2. Implement cp to files without '-@'
+# 3. Verify attribute files will not include file attribute
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verifies that cp will not include file attribute when the -@ flag "\
+ "is not present."
+log_onexit cleanup
+
+test_requires ZFS_ACL ZFS_XATTR
+
+for user in root $ZFS_ACL_STAFF1; do
+ log_must set_cur_usr $user
+
+ log_must create_files $TESTDIR
+
+ initfiles=$($LS -R $INI_DIR/*)
+ typeset -i i=0
+ while (( i < NUM_FILE )); do
+ typeset f=$(getitem $i $initfiles)
+ usr_exec $CP $f $TST_DIR
+
+ testfiles=$($LS -R $TST_DIR/*)
+ tf=$(getitem $i $testfiles)
+ ls_attr=$($LS -@ $tf | $AWK '{print substr($1, 11, 1)}')
+ if [[ $ls_attr == "@" ]]; then
+ log_fail "cp of attribute should fail without " \
+ "-@ or -p option"
+ fi
+
+ (( i += 1 ))
+ done
+
+ log_must cleanup
+done
+
+log_pass "'cp' won't include file attribute passed."
diff --git a/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_cp_003_neg.ksh b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_cp_003_neg.ksh
new file mode 100644
index 000000000000..df03db75b06d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_cp_003_neg.ksh
@@ -0,0 +1,133 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_cp_003_neg
+#
+# DESCRIPTION:
+# Verifies that cp will not be able to include file attribute when
+# attribute is unreadable (unless the user is root)
+#
+# STRATEGY:
+# 1. In directory A, create several files and add attribute files for them
+# 2. chmod all files'the attribute files to '000'.
+# 3. Implement 'cp -@p' to files.
+# 4. Verify attribute files are not existing for non-root user.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verifies that cp won't be able to include file attribute when " \
+ "attribute is unreadable (except root)"
+log_onexit cleanup
+
+test_requires RUNAT ZFS_ACL ZFS_XATTR
+
+function test_unreadable_attr
+{
+ typeset initfiles=$($LS -R $INI_DIR/*)
+
+ typeset -i i=0
+ while (( i < NUM_FILE )); do
+ typeset f=$(getitem $i $initfiles)
+ typeset -i j=0
+ while (( j < NUM_ATTR )); do
+ # chmod all the attribute files to '000'.
+ usr_exec $RUNAT $f $CHMOD 000 attribute.$j
+
+ (( j += 1 ))
+ done
+
+ #
+ # Implement 'cp -@p' to the file whose attribute files
+ # models are '000'.
+ #
+ usr_exec $CP -@p $f $TST_DIR > /dev/null 2>&1
+
+ typeset testfiles=$($LS -R $TST_DIR/*)
+ typeset tf=$(getitem $i $testfiles)
+ typeset ls_attr=$(usr_exec $LS -@ $tf | \
+ $AWK '{print substr($1, 11, 1)}')
+
+ case $ZFS_ACL_CUR_USER in
+ root)
+ case $ls_attr in
+ @)
+ log_note "SUCCESS: root enable to cp attribute"\
+ "when attribute files is unreadable"
+ break ;;
+ *)
+ log_fail "root should enable to cp attribute " \
+ "when attribute files is unreadable"
+ break ;;
+ esac
+ ;;
+ $ZFS_ACL_STAFF1)
+ case $ls_attr in
+ @)
+ log_fail "non-root shouldn't enable to cp " \
+ "attribute when attribute files is " \
+ "unreadable."
+ break ;;
+ *)
+ log_note "SUCCESS: non-root doesn't enable to "\
+ "cp attribute when attribute files is "\
+ "unreadable."
+ break ;;
+ esac
+ ;;
+ *)
+ esac
+
+
+ (( i += 1 ))
+ done
+}
+
+for user in root $ZFS_ACL_STAFF1; do
+ log_must set_cur_usr $user
+
+ log_must create_files $TESTDIR
+ test_unreadable_attr
+
+ log_must cleanup
+done
+
+log_pass "'cp -@p' won't include file attribute passed."
diff --git a/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_find_001_pos.ksh b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_find_001_pos.ksh
new file mode 100644
index 000000000000..b41fc6f1926c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_find_001_pos.ksh
@@ -0,0 +1,96 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_find_001_pos
+#
+# DESCRIPTION:
+# Verifies ability to find files with attribute with -xattr flag and using
+# "-exec runat ls".
+#
+# STRATEGY:
+# 1. In directory A, create several files and add attribute files for them
+# 2. Verify all the specified files can be found with '-xattr',
+# 3. Verify all the attribute files can be found with '-exec runat ls'
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verifies ability to find files with attribute with" \
+ "-xattr flag and using '-exec runat ls'"
+log_onexit cleanup
+
+test_requires ZFS_ACL ZFS_XATTR
+
+for user in root $ZFS_ACL_STAFF1; do
+ log_must set_cur_usr $user
+
+ log_must create_files $TESTDIR
+ initfiles=$($LS -R $INI_DIR/*)
+
+ typeset -i i=0
+ while (( i < NUM_FILE )); do
+ f=$(getitem $i $initfiles)
+ ff=$(usr_exec $FIND $INI_DIR -type f -name ${f##*/} \
+ -xattr -print)
+ if [[ $ff != $f ]]; then
+ log_fail "find file containing attribute fail."
+ else
+ log_note "find $f by '-xattr'."
+ fi
+
+ typeset -i j=0
+ while (( j < NUM_ATTR )); do
+ typeset af=attribute.$j
+ fa=$(usr_exec $FIND $INI_DIR -type f -name ${f##*/} \
+ -xattr -exec runat {} ls $af \\\;)
+ if [[ $fa != $af ]]; then
+ log_fail "find file attribute fail"
+ fi
+ (( j += 1 ))
+ done
+ (( i += 1 ))
+ log_note "find all attribute files of $f"
+ done
+
+ log_must cleanup
+done
+
+log_pass "find files with -xattr passed."
diff --git a/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_find_002_neg.ksh b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_find_002_neg.ksh
new file mode 100644
index 000000000000..c46776e8611e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_find_002_neg.ksh
@@ -0,0 +1,102 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_find_002_neg
+#
+# DESCRIPTION:
+# Verifies ability to find files with attribute with -xattr flag and using
+# "-exec runat ls".
+#
+# STRATEGY:
+# 1. In directory A, create several files and add attribute files for them
+# 2. Delete all the attribute files.
+# 2. Verify all the specified files can not be found with '-xattr',
+# 3. Verify all the attribute files can not be found with '-exec runat ls'
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "verifies -xattr doesn't include files without " \
+ "attribute and using '-exec runat ls'"
+log_onexit cleanup
+
+test_requires RUNAT ZFS_ACL ZFS_XATTR
+
+for user in root $ZFS_ACL_STAFF1; do
+ log_must set_cur_usr $user
+
+ log_must create_files $TESTDIR
+
+ initfiles=$($LS -R $INI_DIR/*)
+ typeset -i i=0
+ while (( i < NUM_FILE )); do
+ f=$(getitem $i $initfiles)
+ usr_exec $RUNAT $f $RM attribute*
+ (( i += 1 ))
+ done
+
+ i=0
+ while (( i < NUM_FILE )); do
+ f=$(getitem $i $initfiles)
+ ff=$(usr_exec $FIND $INI_DIR -type f -name ${f##*/} \
+ -xattr -print)
+ if [[ $ff == $f ]]; then
+ log_fail "find not containing attribute should fail."
+ fi
+
+ typeset -i j=0
+ while (( j < NUM_ATTR )); do
+ fa=$(usr_exec $FIND $INI_DIR -type f -name ${f##*/} \
+ -xattr -exec $RUNAT {} $LS attribute.$j \\\;)
+ if [[ $fa == attribute.$j ]]; then
+ log_fail "find file attribute should fail."
+ fi
+ (( j += 1 ))
+ done
+ log_note "Failed to find $f and its attribute file as expected."
+
+ (( i += 1 ))
+ done
+
+ log_must cleanup
+done
+
+log_pass "find files which have no attrabute files with -xattr passed."
diff --git a/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_ls_001_pos.ksh b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_ls_001_pos.ksh
new file mode 100644
index 000000000000..dc0d83a399d9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_ls_001_pos.ksh
@@ -0,0 +1,83 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_ls_001_pos
+#
+# DESCRIPTION:
+# Verifies that ls displays @ in the file permissions using ls -@
+# for files with attribute.
+#
+# STRATEGY:
+# 1. Create files with attribute files in directory A.
+# 2. Verify 'ls -l' can display @ in file permissions.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verifies that ls displays @ in the file permissions using ls -@ " \
+ "for files with attribute."
+log_onexit cleanup
+
+test_requires ZFS_ACL ZFS_XATTR
+
+for user in root $ZFS_ACL_STAFF1; do
+ log_must set_cur_usr $user
+
+ log_must create_files $TESTDIR
+
+ initfiles=$($LS -R $INI_DIR/*)
+ typeset -i i=0
+ while (( i < NUM_FILE )); do
+ f=$(getitem $i $initfiles)
+ ls_attr=$(usr_exec $LS -@ $f | $AWK '{print substr($1, 11, 1)}')
+ if [[ $ls_attr != "@" ]]; then
+ log_fail "ls -@ $f with attribute should success."
+ else
+ log_note "ls -@ $f with attribute success."
+ fi
+
+ (( i += 1 ))
+ done
+
+ log_must cleanup
+done
+
+log_pass "ls display @ in file permission passed."
diff --git a/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_ls_002_neg.ksh b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_ls_002_neg.ksh
new file mode 100644
index 000000000000..a96779b7108f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_ls_002_neg.ksh
@@ -0,0 +1,84 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_ls_002_neg
+#
+# DESCRIPTION:
+# Verifies that ls doesn't display @ in the file permissions using ls -@
+# for files without attribute.
+#
+# STRATEGY:
+# 1. Create files with attribute files in directory A.
+# 2. Removed all attribute files.
+# 3. Verify 'ls -l' can't display @ in file permission.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verifies that ls doesn't display @ in the file " \
+ "permissions using ls -@ for files without attribute."
+log_onexit cleanup
+
+test_requires RUNAT ZFS_ACL ZFS_XATTR
+
+for user in root $ZFS_ACL_STAFF1; do
+ log_must set_cur_usr $user
+
+ log_must create_files $TESTDIR
+
+ initfiles=$($LS -R $INI_DIR/*)
+ typeset -i i=0
+ while (( i < NUM_FILE )); do
+ f=$(getitem $i $initfiles)
+ log_must usr_exec $RUNAT $f $RM attribute*
+
+ ls_attr=$(usr_exec $LS -l $f | $AWK '{print substr($1, 11, 1)}')
+ if [[ $ls_attr == "@" ]]; then
+ log_fail "ls with attribute shouldn't success."
+ fi
+
+ (( i += 1 ))
+ done
+
+ log_must cleanup
+done
+
+log_pass "ls doesn't display @ in file permissions passed."
diff --git a/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_mv_001_pos.ksh b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_mv_001_pos.ksh
new file mode 100644
index 000000000000..8c332d5b327f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_mv_001_pos.ksh
@@ -0,0 +1,82 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_mv_001_pos
+#
+# DESCRIPTION:
+# Verifies that mv will include file attribute.
+#
+# STRATEGY:
+# 1. In directory A, create several files and add attribute files for them
+# 2. Save all files and their attribute files cksum value
+# 3. Move them to another directory B.
+# 4. Calculate all the files and attribute files cksum
+# 5. Verify all the cksum are identical
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verifies that mv will include file attribute."
+log_onexit cleanup
+
+test_requires ZFS_ACL ZFS_XATTR
+
+set -A BEFORE_FCKSUM
+set -A BEFORE_ACKSUM
+set -A AFTER_FCKSUM
+set -A AFTER_ACKSUM
+
+for user in root $ZFS_ACL_STAFF1; do
+ log_must set_cur_usr $user
+
+ log_must create_files $TESTDIR
+
+ log_must cksum_files $INI_DIR BEFORE_FCKSUM BEFORE_ACKSUM
+ log_must usr_exec $MV $INI_DIR/* $TST_DIR
+ log_must cksum_files $TST_DIR AFTER_FCKSUM AFTER_ACKSUM
+
+ log_must compare_cksum BEFORE_FCKSUM AFTER_FCKSUM
+ log_must compare_cksum BEFORE_ACKSUM AFTER_ACKSUM
+
+ log_must cleanup
+done
+
+log_pass "mv file include attribute passed."
diff --git a/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_pack_001_pos.ksh b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_pack_001_pos.ksh
new file mode 100644
index 000000000000..54960ce7ce4e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_pack_001_pos.ksh
@@ -0,0 +1,87 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_pack_001_pos
+#
+# DESCRIPTION:
+# Verifies that pack will keep file attribute intact afterthe file is
+# packed and unpacked.
+#
+# STRATEGY:
+# 1. In directory A, create several files and add attribute files for them
+# 2. Save all files and their attribute files cksum value, then pack
+# all the files.
+# 3. Move them to another directory B.
+# 4. Unpack them and calculate all the files and attribute files cksum
+# 5. Verify all the cksum are identical
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verifies that pack will keep file attribute intact after the file "\
+ "is packed and unpacked"
+log_onexit cleanup
+
+test_requires ZFS_ACL ZFS_XATTR
+
+set -A BEFORE_FCKSUM
+set -A BEFORE_ACKSUM
+set -A AFTER_FCKSUM
+set -A AFTER_ACKSUM
+
+for user in root $ZFS_ACL_STAFF1; do
+ log_must set_cur_usr $user
+
+ log_must create_files $TESTDIR
+
+ log_must cksum_files $INI_DIR BEFORE_FCKSUM BEFORE_ACKSUM
+ log_must eval "usr_exec $PACK -f $INI_DIR/* > /dev/null 2>&1"
+ log_must usr_exec $MV $INI_DIR/* $TST_DIR
+ log_must eval "usr_exec $UNPACK $TST_DIR/* > /dev/null 2>&1"
+ log_must cksum_files $TST_DIR AFTER_FCKSUM AFTER_ACKSUM
+
+ log_must compare_cksum BEFORE_FCKSUM AFTER_FCKSUM
+ log_must compare_cksum BEFORE_ACKSUM AFTER_ACKSUM
+
+ log_must cleanup
+done
+
+log_pass "pack/unpack test passed."
diff --git a/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_pax_001_pos.ksh b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_pax_001_pos.ksh
new file mode 100644
index 000000000000..56cdb51ab230
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_pax_001_pos.ksh
@@ -0,0 +1,99 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_pax_001_pos
+#
+# DESCRIPTION:
+# Verify directories include attribute in pax archive and restore with pax
+# should succeed.
+#
+# STRATEGY:
+# 1. Use mktree create a set of directories in directory A.
+# 2. Enter into directory A and record all directory information.
+# 3. pax all the files to directory B.
+# 4. Then pax the pax file to directory C.
+# 5. Record all the directories informat in derectory C.
+# 6. Verify the two records should be identical.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify include attribute in pax archive and restore with pax " \
+ "should succeed."
+log_onexit cleanup
+
+test_requires ZFS_ACL ZFS_XATTR
+
+for user in root $ZFS_ACL_STAFF1; do
+ log_must set_cur_usr $user
+
+ [[ ! -d $INI_DIR ]] && log_must usr_exec $MKDIR -m 777 -p $INI_DIR
+ log_must usr_exec $MKTREE -b $INI_DIR -l 6 -d 2 -f 2
+
+ #
+ # Enter into initial directory and record all directory information,
+ # then pax all the files to $TMP_DIR/files.pax.
+ #
+ [[ ! -d $TMP_DIR ]] && log_must usr_exec $MKDIR $TMP_DIR
+ initout=$TMP_DIR/initout.${TESTCASE_ID}
+ paxout=$TMP_DIR/files.pax
+
+ cd $INI_DIR
+ log_must eval "record_cksum $INI_DIR $initout > /dev/null 2>&1"
+ log_must eval "usr_exec $PAX -w -@ -f $paxout * > /dev/null 2>&1"
+
+ #
+ # Enter into test directory and pax $TMP_DIR/files.pax to current
+ # directory. Record all directory information and compare with initial
+ # directory record.
+ #
+ [[ ! -d $TST_DIR ]] && log_must usr_exec $MKDIR -m 777 $TST_DIR
+ testout=$TMP_DIR/testout.${TESTCASE_ID}
+ cd $TST_DIR
+ log_must eval "usr_exec $PAX -r -@ -f $paxout > /dev/null 2>&1"
+ log_must eval "record_cksum $TST_DIR $testout > /dev/null 2>&1"
+
+ log_must usr_exec $DIFF $initout $testout
+
+ log_must cleanup
+done
+
+log_pass "Directories pax archive and restre with pax passed."
diff --git a/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_pax_002_pos.ksh b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_pax_002_pos.ksh
new file mode 100644
index 000000000000..a4dca8a5f9ee
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_pax_002_pos.ksh
@@ -0,0 +1,98 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_pax_002_pos
+#
+# DESCRIPTION:
+# Verify directories which include attribute in pax archive and restore
+# with tar should succeed.
+#
+# STRATEGY:
+# 1. Use mktree create a set of directories in directory A.
+# 2. Enter into directory A and record all directory information.
+# 3. pax all the files to directory B.
+# 4. Then tar the pax file to directory C.
+# 5. Record all the directories informat in derectory C.
+# 6. Verify the two records should be identical.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify include attribute in pax archive and restore with tar " \
+ "should succeed."
+log_onexit cleanup
+
+test_requires ZFS_ACL ZFS_XATTR
+
+for user in root $ZFS_ACL_STAFF1; do
+ log_must set_cur_usr $user
+
+ [[ ! -d $INI_DIR ]] && log_must usr_exec $MKDIR -m 777 $INI_DIR
+ log_must usr_exec $MKTREE -b $INI_DIR -l 6 -d 2 -f 2
+
+ #
+ # Enter into initial directory and record all directory information,
+ # then pax all the files to $TMP_DIR/files.pax.
+ #
+ [[ ! -d $TMP_DIR ]] && log_must usr_exec $MKDIR -m 777 $TMP_DIR
+ initout=$TMP_DIR/initout.${TESTCASE_ID}
+ paxout=$TMP_DIR/files.tar
+ cd $INI_DIR
+ log_must eval "record_cksum $INI_DIR $initout > /dev/null 2>&1"
+ log_must eval "usr_exec $PAX -w -x ustar -@ -f $paxout *>/dev/null 2>&1"
+
+ #
+ # Enter into test directory and tar $TMP_DIR/files.pax to current
+ # directory. Record all directory information and compare with initial
+ # directory record.
+ #
+ [[ ! -d $TST_DIR ]] && log_must usr_exec $MKDIR -m 777 $TST_DIR
+ testout=$TMP_DIR/testout.${TESTCASE_ID}
+ cd $TST_DIR
+ log_must eval "usr_exec $TAR xpf@ $paxout > /dev/null 2>&1"
+ log_must eval "record_cksum $TST_DIR $testout > /dev/null 2>&1"
+
+ log_must usr_exec $DIFF $initout $testout
+
+ log_must cleanup
+done
+
+log_pass "Directories pax archive and restore with pax passed."
diff --git a/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_pax_003_pos.ksh b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_pax_003_pos.ksh
new file mode 100644
index 000000000000..0490f4ef1173
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_pax_003_pos.ksh
@@ -0,0 +1,93 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_pax_003_pos
+#
+# DESCRIPTION:
+# Verify directories which include attribute in pax archive and restore
+# with cpio should succeed.
+#
+# STRATEGY:
+# 1. Create several files in directory A.
+# 2. Enter into directory A and record all directory cksum.
+# 3. pax all the files to directory B.
+# 4. Then cpio the pax file to directory C.
+# 5. Record all the files cksum in derectory C.
+# 6. Verify the two records should be identical.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify include attribute in pax archive and restore with cpio " \
+ "should succeed."
+log_onexit cleanup
+
+test_requires ZFS_ACL ZFS_XATTR
+
+for user in root $ZFS_ACL_STAFF1; do
+ log_must set_cur_usr $user
+
+ [[ ! -d $INI_DIR ]] && log_must usr_exec $MKDIR -m 777 -p $INI_DIR
+ log_must usr_exec $MKTREE -b $INI_DIR -l 6 -d 2 -f 2
+
+ initout=$TMP_DIR/initout.${TESTCASE_ID}
+ paxout=$TMP_DIR/files.cpio
+ cd $INI_DIR
+ log_must eval "record_cksum $INI_DIR $initout > /dev/null 2>&1"
+ log_must eval "usr_exec $PAX -w -x cpio -@ -f $paxout * >/dev/null 2>&1"
+
+ #
+ # Enter into test directory and cpio $TMP_DIR/files.pax to current
+ # directory. Record all directory information and compare with initial
+ # directory record.
+ #
+ [[ ! -d $TST_DIR ]] && log_must usr_exec $MKDIR -m 777 $TST_DIR
+ testout=$TMP_DIR/testout.${TESTCASE_ID}
+ cd $TST_DIR
+ log_must eval "usr_exec $CPIO -ivd@ < $paxout" > /dev/null 2>&1
+ log_must eval "record_cksum $TST_DIR $testout > /dev/null 2>&1"
+
+ log_must usr_exec $DIFF $initout $testout
+
+ log_must cleanup
+done
+
+log_pass "Directories pax archive and restore with cpio passed."
diff --git a/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_pax_004_pos.ksh b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_pax_004_pos.ksh
new file mode 100644
index 000000000000..b4ecc78b3214
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_pax_004_pos.ksh
@@ -0,0 +1,99 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_pax_004_pos
+#
+# DESCRIPTION:
+# Verify files include attribute in pax archive and restore with pax
+# should succeed.
+#
+# STRATEGY:
+# 1. Create several files which contains contribute files in directory A.
+# 2. Enter into directory A and record all files cksum.
+# 3. pax all the files to directory B.
+# 4. Then pax the pax file to directory C.
+# 5. Record all the files cksum in derectory C.
+# 6. Verify the two records should be identical.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify files include attribute in pax archive and restore with pax " \
+ "should succeed."
+log_onexit cleanup
+
+test_requires ZFS_ACL ZFS_XATTR
+
+set -A BEFORE_FCKSUM
+set -A BEFORE_ACKSUM
+set -A AFTER_FCKSUM
+set -A AFTER_ACKSUM
+
+for user in root $ZFS_ACL_STAFF1; do
+ log_must set_cur_usr $user
+
+ log_must create_files $TESTDIR
+
+ #
+ # Enter into initial directory and record all files cksum,
+ # then pax all the files to $TMP_DIR/files.pax.
+ #
+ paxout=$TMP_DIR/files.pax
+ cd $INI_DIR
+ log_must cksum_files $INI_DIR BEFORE_FCKSUM BEFORE_ACKSUM
+ log_must eval "usr_exec $PAX -w -@ -f $paxout * > /dev/null 2>&1"
+
+ #
+ # Enter into test directory and pax $TMP_DIR/files.pax to current
+ # directory. Record all directory information and compare with initial
+ # directory record.
+ #
+ cd $TST_DIR
+ log_must eval "usr_exec $PAX -r -@ -f $paxout > /dev/null 2>&1"
+ log_must cksum_files $TST_DIR AFTER_FCKSUM AFTER_ACKSUM
+
+ log_must compare_cksum BEFORE_FCKSUM AFTER_FCKSUM
+ log_must compare_cksum BEFORE_ACKSUM AFTER_ACKSUM
+
+ log_must cleanup
+done
+
+log_pass "Files pax archive and restre with pax passed."
diff --git a/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_pax_005_pos.ksh b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_pax_005_pos.ksh
new file mode 100644
index 000000000000..657bbe92fad1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_pax_005_pos.ksh
@@ -0,0 +1,106 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_pax_005_pos
+#
+# DESCRIPTION:
+# Verify files include attribute in cpio archive and restore with cpio
+# should succeed.
+#
+# STRATEGY:
+# 1. Create several files which contains contribute files in directory A.
+# 2. Enter into directory A and record all files cksum.
+# 3. pax all the files to directory B.
+# 4. Then pax the pax file to directory C.
+# 5. Record all the files cksum in derectory C.
+# 6. Verify the two records should be identical.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify files include attribute in cpio archive and restore with " \
+ "cpio should succeed."
+log_onexit cleanup
+
+test_requires ZFS_ACL ZFS_XATTR
+
+set -A BEFORE_FCKSUM
+set -A BEFORE_ACKSUM
+set -A AFTER_FCKSUM
+set -A AFTER_ACKSUM
+
+for user in root $ZFS_ACL_STAFF1; do
+ log_must set_cur_usr $user
+
+ log_must create_files $TESTDIR
+
+ #
+ # Enter into initial directory and record all files cksum,
+ # then pax all the files to $TMP_DIR/files.pax.
+ #
+ paxout=$TMP_DIR/files.cpio
+ cd $INI_DIR
+ log_must cksum_files $INI_DIR BEFORE_FCKSUM BEFORE_ACKSUM
+ log_must eval "usr_exec $PAX -w -x cpio -@ -f $paxout * >/dev/null 2>&1"
+
+ #
+ # Enter into test directory and pax $TMP_DIR/files.cpio to current
+ # directory. Record all directory information and compare with initial
+ # directory record.
+ #
+ cd $TST_DIR
+ log_must eval "usr_exec $PAX -r -x cpio -@ -f $paxout > /dev/null 2>&1"
+ log_must cksum_files $TST_DIR AFTER_FCKSUM AFTER_ACKSUM
+
+ log_must compare_cksum BEFORE_FCKSUM AFTER_FCKSUM
+ log_must compare_cksum BEFORE_ACKSUM AFTER_ACKSUM
+
+ log_must usr_exec $RM -rf *
+ log_must eval "usr_exec $CPIO -iv@ < $paxout > /dev/null 2>&1"
+ log_must cksum_files $TST_DIR AFTER_FCKSUM AFTER_ACKSUM
+
+ log_must compare_cksum BEFORE_FCKSUM AFTER_FCKSUM
+ log_must compare_cksum BEFORE_ACKSUM AFTER_ACKSUM
+
+ log_must cleanup
+done
+
+log_pass "Files 'pax cpio' archive and restre with 'pax cpio' passed."
diff --git a/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_pax_006_pos.ksh b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_pax_006_pos.ksh
new file mode 100644
index 000000000000..f273fb503701
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_pax_006_pos.ksh
@@ -0,0 +1,106 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_pax_006_pos
+#
+# DESCRIPTION:
+# Verify files include attribute in tar archive and restore with tar
+# should succeed.
+#
+# STRATEGY:
+# 1. Create several files which contains contribute files in directory A.
+# 2. Enter into directory A and record all files cksum.
+# 3. 'pax ustar' all the files to directory B.
+# 4. Then 'pax ustar' the pax file to directory C.
+# 5. Record all the files cksum in derectory C.
+# 6. Verify the two records should be identical.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify files include attribute in tar archive and restore with " \
+ "tar should succeed."
+log_onexit cleanup
+
+test_requires ZFS_ACL ZFS_XATTR
+
+set -A BEFORE_FCKSUM
+set -A BEFORE_ACKSUM
+set -A AFTER_FCKSUM
+set -A AFTER_ACKSUM
+
+for user in root $ZFS_ACL_STAFF1; do
+ log_must set_cur_usr $user
+
+ log_must create_files $TESTDIR
+
+ #
+ # Enter into initial directory and record all files cksum,
+ # then pax all the files to $TMP_DIR/files.pax.
+ #
+ paxout=$TMP_DIR/files.tar
+ cd $INI_DIR
+ log_must cksum_files $INI_DIR BEFORE_FCKSUM BEFORE_ACKSUM
+ log_must eval "usr_exec $PAX -w -x ustar -@ -f $paxout *>/dev/null 2>&1"
+
+ #
+ # Enter into test directory and pax $TMP_DIR/files.tar to current
+ # directory. Record all directory information and compare with initial
+ # directory record.
+ #
+ cd $TST_DIR
+ log_must eval "usr_exec $PAX -r -x ustar -@ -f $paxout > /dev/null 2>&1"
+ log_must cksum_files $TST_DIR AFTER_FCKSUM AFTER_ACKSUM
+
+ log_must compare_cksum BEFORE_FCKSUM AFTER_FCKSUM
+ log_must compare_cksum BEFORE_ACKSUM AFTER_ACKSUM
+
+ log_must usr_exec $RM -rf *
+ log_must usr_exec $TAR xf@ $paxout
+ log_must cksum_files $TST_DIR AFTER_FCKSUM AFTER_ACKSUM
+
+ log_must compare_cksum BEFORE_FCKSUM AFTER_FCKSUM
+ log_must compare_cksum BEFORE_ACKSUM AFTER_ACKSUM
+
+ log_must cleanup
+done
+
+log_pass "Files 'pax tar' archive and restre with 'pax tar' passed."
diff --git a/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_tar_001_pos.ksh b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_tar_001_pos.ksh
new file mode 100644
index 000000000000..4813bc029176
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_tar_001_pos.ksh
@@ -0,0 +1,97 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_tar_001_pos
+#
+# DESCRIPTION:
+# Verifies that tar will include file attribute when @ flag is present.
+#
+# STRATEGY:
+# 1. Use mktree create a set of directories in directory A.
+# 2. Enter into directory A and record all directory information.
+# 3. tar all the files to directory B.
+# 4. Then tar the tar file to directory C.
+# 5. Record all the directories informat in derectory C.
+# 6. Verify the two records should be identical.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verifies that tar will include file attribute when @ flag is " \
+ "present."
+log_onexit cleanup
+
+test_requires ZFS_ACL ZFS_XATTR
+
+for user in root $ZFS_ACL_STAFF1; do
+ log_must set_cur_usr $user
+
+ [[ ! -d $INI_DIR ]] && $MKDIR -m 777 -p $INI_DIR
+ log_must usr_exec $MKTREE -b $INI_DIR -l 5 -d 2 -f 2
+
+ #
+ # Enter into initial directory and record all directory information,
+ # then tar all the files to $TMP_DIR/files.tar.
+ #
+ [[ ! -d $TMP_DIR ]] && usr_exec $MKDIR $TMP_DIR
+ initout=$TMP_DIR/initout.${TESTCASE_ID}
+ tarout=$TMP_DIR/files.tar
+ cd $INI_DIR
+ log_must record_cksum $INI_DIR $initout
+ log_must usr_exec $TAR cpf@ $tarout *
+
+ #
+ # Enter into test directory and tar $TMP_DIR/files.tar to current
+ # directory. Record all directory information and compare with initial
+ # directory record.
+ #
+ [[ ! -d $TST_DIR ]] && $MKDIR -m 777 $TST_DIR
+ testout=$TMP_DIR/testout.${TESTCASE_ID}
+ cd $TST_DIR
+ log_must usr_exec $TAR xpf@ $tarout
+ log_must record_cksum $TST_DIR $testout
+
+ log_must usr_exec $DIFF $initout $testout
+
+ log_must cleanup
+done
+
+log_pass "Verify tar with @ passed."
diff --git a/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_tar_002_neg.ksh b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_tar_002_neg.ksh
new file mode 100644
index 000000000000..ec7291e3205b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/acl/trivial/zfs_acl_tar_002_neg.ksh
@@ -0,0 +1,113 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/acl/acl_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_acl_tar_002_neg
+#
+# DESCRIPTION:
+# Verifies that tar will not include files attribute when @ flag is not
+# present.
+#
+# STRATEGY:
+# 1. Create several files with attribute files.
+# 2. Enter into directory A and record all files cksum
+# 3. tar all the files to directory B.
+# 4. Then tar the tar file to directory C.
+# 5. Record all the files cksum in derectory C.
+# 6. Verify the two records should be not identical.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verifies that tar will not include files attribute when @ flag is "\
+ "not present"
+log_onexit cleanup
+
+test_requires ZFS_ACL ZFS_XATTR
+
+set -A BEFORE_FCKSUM
+set -A BEFORE_ACKSUM
+set -A AFTER_FCKSUM
+set -A AFTER_ACKSUM
+
+for user in root $ZFS_ACL_STAFF1; do
+ log_must set_cur_usr $user
+
+ log_must create_files $TESTDIR
+
+ #
+ # Enter into initial directory and record all directory information,
+ # then tar all the files to $TMP_DIR/files.tar.
+ #
+ tarout=$TMP_DIR/files.tar
+ cd $INI_DIR
+ log_must cksum_files $INI_DIR BEFORE_FCKSUM BEFORE_ACKSUM
+ log_must usr_exec $TAR cpf $tarout *
+
+ #
+ # Enter into test directory and tar $TMP_DIR/files.tar to current
+ # directory. Record all directory information and compare with initial
+ # directory record.
+ #
+ cd $TST_DIR
+ log_must usr_exec $CP $tarout $TST_DIR
+ log_must usr_exec $TAR xpf $tarout
+
+ testfiles=$($LS -R $TST_DIR/*)
+ typeset -i i=0
+ while (( i < NUM_FILE )); do
+ f=$(getitem $i $testfiles)
+ ls_attr=$($LS -@ $f | $AWK '{print substr($1, 11, 1)}')
+ if [[ $ls_attr == "@" ]]; then
+ log_fail "extraction of attribute successful w/ -@ flag"
+ fi
+
+ (( i += 1 ))
+ done
+
+ log_must cksum_files $TST_DIR AFTER_FCKSUM AFTER_ACKSUM
+
+ log_must compare_cksum BEFORE_FCKSUM AFTER_FCKSUM
+ log_mustnot compare_cksum BEFORE_ACKSUM AFTER_ACKSUM
+
+ log_must cleanup
+done
+
+log_pass "Verify tar without @ passed."
diff --git a/tests/sys/cddl/zfs/tests/atime/Makefile b/tests/sys/cddl/zfs/tests/atime/Makefile
new file mode 100644
index 000000000000..f89f32c149f3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/atime/Makefile
@@ -0,0 +1,18 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/atime
+FILESDIR=${TESTSDIR}
+
+${PACKAGE}FILES+= atime.cfg
+${PACKAGE}FILES+= atime_001_pos.ksh
+${PACKAGE}FILES+= atime_002_neg.ksh
+${PACKAGE}FILES+= atime_common.kshlib
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= setup.ksh
+
+ATF_TESTS_KSH93+= atime_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/atime/atime.cfg b/tests/sys/cddl/zfs/tests/atime/atime.cfg
new file mode 100644
index 000000000000..46e74193587a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/atime/atime.cfg
@@ -0,0 +1,30 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export TESTCLONE=testclone${TESTCASE_ID}
+export TESTSNAP=testsnap${TESTCASE_ID}
+
+export TESTFILE=testfile
diff --git a/tests/sys/cddl/zfs/tests/atime/atime_001_pos.ksh b/tests/sys/cddl/zfs/tests/atime/atime_001_pos.ksh
new file mode 100644
index 000000000000..743c2a9357e4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/atime/atime_001_pos.ksh
@@ -0,0 +1,80 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/atime/atime_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: atime_001_pos
+#
+# DESCRIPTION:
+# When atime=on, verify the access time for files is updated when read. It
+# is available to fs and clone. To snapshot, it is unavailable.
+#
+# STRATEGY:
+# 1. Create pool and fs.
+# 2. Create '$TESTFILE' for fs.
+# 3. Create snapshot and clone.
+# 4. Setting atime=on on datasets except snapshot, and read '$TESTFILE'.
+# 5. Expect the access time is updated on datasets except snapshot.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-11)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Setting atime=on, the access time for files is updated when read."
+log_onexit cleanup
+
+#
+# Create $TESTFILE, snapshot and clone.
+#
+setup_snap_clone
+
+for dst in $TESTPOOL/$TESTFS $TESTPOOL/$TESTCLONE $TESTPOOL/$TESTFS@$TESTSNAP
+do
+ typeset mtpt=$(get_prop mountpoint $dst)
+
+ if [[ $dst == $TESTPOOL/$TESTFS@$TESTSNAP ]]; then
+ mtpt=$(snapshot_mountpoint $dst)
+ log_mustnot check_atime_updated $mtpt/$TESTFILE
+ else
+ log_must $ZFS set atime=on $dst
+ log_must check_atime_updated $mtpt/$TESTFILE
+ fi
+done
+
+log_pass "Verify the property atime=on passed."
diff --git a/tests/sys/cddl/zfs/tests/atime/atime_002_neg.ksh b/tests/sys/cddl/zfs/tests/atime/atime_002_neg.ksh
new file mode 100644
index 000000000000..235bb2069508
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/atime/atime_002_neg.ksh
@@ -0,0 +1,81 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/atime/atime_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: atime_002_neg
+#
+# DESCRIPTION:
+# When atime=off, verify the access time for files is not updated when read.
+# It is available to pool, fs snapshot and clone.
+#
+# STRATEGY:
+# 1. Create pool, fs.
+# 2. Create '$TESTFILE' for fs.
+# 3. Create snapshot and clone.
+# 4. Setting atime=off on dataset and read '$TESTFILE'.
+# 5. Verify the access time is not updated.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-11)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Setting atime=off, the access time for files will not be updated \
+ when read."
+log_onexit cleanup
+
+#
+# Create $TESTFILE, snapshot and clone.
+#
+setup_snap_clone
+
+for dst in $TESTPOOL/$TESTFS $TESTPOOL/$TESTCLONE $TESTPOOL/$TESTFS@$TESTSNAP
+do
+ typeset mtpt=$(get_prop mountpoint $dst)
+
+ if [[ $dst == $TESTPOOL/$TESTFS@$TESTSNAP ]]; then
+ mtpt=$(snapshot_mountpoint $dst)
+ else
+ log_must $ZFS set atime=off $dst
+ fi
+
+ log_mustnot check_atime_updated $mtpt/$TESTFILE
+done
+
+log_pass "Verify the property atime=off passed."
diff --git a/tests/sys/cddl/zfs/tests/atime/atime_common.kshlib b/tests/sys/cddl/zfs/tests/atime/atime_common.kshlib
new file mode 100644
index 000000000000..f77fee8f13a7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/atime/atime_common.kshlib
@@ -0,0 +1,67 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#
+# Check if the access time for specified file is updated.
+#
+# $1 Given an absolute path to a file name
+#
+# Return value:
+# 0 -> The access time is updated.
+# 1 -> The access time is not updated.
+#
+function check_atime_updated
+{
+ typeset filename=$1
+
+ typeset before=$($LS -luD "%Y-%m-%d %R.%s" $filename | $AWK '{print $7}')
+ sleep 2
+ log_must $CAT $filename
+ typeset after=$($LS -luD "%Y-%m-%d %R.%s" $filename | $AWK '{print $7}')
+
+ if [[ $before != $after ]]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+function setup_snap_clone
+{
+ # Create two file to verify snapshot.
+ log_must $TOUCH $TESTDIR/$TESTFILE
+
+ create_snapshot $TESTPOOL/$TESTFS $TESTSNAP
+ create_clone $TESTPOOL/$TESTFS@$TESTSNAP $TESTPOOL/$TESTCLONE
+}
+
+function cleanup
+{
+ destroy_clone $TESTPOOL/$TESTCLONE
+ destroy_snapshot $TESTPOOL/$TESTFS@$TESTSNAP
+}
diff --git a/tests/sys/cddl/zfs/tests/atime/atime_test.sh b/tests/sys/cddl/zfs/tests/atime/atime_test.sh
new file mode 100755
index 000000000000..ec49861a4cfa
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/atime/atime_test.sh
@@ -0,0 +1,84 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case atime_001_pos cleanup
+atime_001_pos_head()
+{
+ atf_set "descr" "Setting atime=on, the access time for files is updated when read."
+ atf_set "require.progs" "ksh93 zfs"
+}
+atime_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/atime_common.kshlib
+ . $(atf_get_srcdir)/atime.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/atime_001_pos.ksh || atf_fail "Testcase failed"
+}
+atime_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/atime_common.kshlib
+ . $(atf_get_srcdir)/atime.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case atime_002_neg cleanup
+atime_002_neg_head()
+{
+ atf_set "descr" "Setting atime=off, the access time for files will not be updatedwhen read."
+ atf_set "require.progs" "ksh93 zfs"
+}
+atime_002_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/atime_common.kshlib
+ . $(atf_get_srcdir)/atime.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/atime_002_neg.ksh || atf_fail "Testcase failed"
+}
+atime_002_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/atime_common.kshlib
+ . $(atf_get_srcdir)/atime.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case atime_001_pos
+ atf_add_test_case atime_002_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/atime/cleanup.ksh b/tests/sys/cddl/zfs/tests/atime/cleanup.ksh
new file mode 100644
index 000000000000..713cb16bb31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/atime/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/atime/setup.ksh b/tests/sys/cddl/zfs/tests/atime/setup.ksh
new file mode 100644
index 000000000000..5abc4429b4b3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/atime/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+
+DISK=${DISKS%% *}
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/bootfs/Makefile b/tests/sys/cddl/zfs/tests/bootfs/Makefile
new file mode 100644
index 000000000000..53e2224e1d8c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/bootfs/Makefile
@@ -0,0 +1,22 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/bootfs
+FILESDIR=${TESTSDIR}
+
+${PACKAGE}FILES+= bootfs.cfg
+${PACKAGE}FILES+= bootfs_001_pos.ksh
+${PACKAGE}FILES+= bootfs_002_neg.ksh
+${PACKAGE}FILES+= bootfs_003_pos.ksh
+${PACKAGE}FILES+= bootfs_004_neg.ksh
+${PACKAGE}FILES+= bootfs_005_neg.ksh
+${PACKAGE}FILES+= bootfs_006_pos.ksh
+${PACKAGE}FILES+= bootfs_007_pos.ksh
+${PACKAGE}FILES+= bootfs_008_neg.ksh
+${PACKAGE}FILES+= bootfs_009_neg.ksh
+
+ATF_TESTS_KSH93+= bootfs_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/bootfs/bootfs.cfg b/tests/sys/cddl/zfs/tests/bootfs/bootfs.cfg
new file mode 100644
index 000000000000..19bc6033b7b8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/bootfs/bootfs.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+FS=fs
+export FS
diff --git a/tests/sys/cddl/zfs/tests/bootfs/bootfs_001_pos.ksh b/tests/sys/cddl/zfs/tests/bootfs/bootfs_001_pos.ksh
new file mode 100644
index 000000000000..ac416ffa3e7d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/bootfs/bootfs_001_pos.ksh
@@ -0,0 +1,93 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: bootfs_001_pos
+#
+# DESCRIPTION:
+#
+# Valid datasets are accepted as bootfs property values
+#
+# STRATEGY:
+# 1. Create a set of datasets in a test pool
+# 2. Try setting them as boot filesystems
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-03-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup {
+ destroy_pool $TESTPOOL
+
+ if [[ -f $VDEV ]]; then
+ log_must $RM -f $VDEV
+ fi
+}
+
+$ZPOOL set 2>&1 | $GREP bootfs > /dev/null
+if [ $? -ne 0 ]
+then
+ log_unsupported "bootfs pool property not supported on this release."
+fi
+
+log_assert "Valid datasets are accepted as bootfs property values"
+log_onexit cleanup
+
+typeset VDEV=$TMPDIR/bootfs_001_pos_a.${TESTCASE_ID}.dat
+
+log_must create_vdevs $VDEV
+create_pool "$TESTPOOL" "$VDEV"
+log_must $ZFS create $TESTPOOL/$FS
+
+enc=$(get_prop encryption $TESTPOOL/$FS)
+if [[ $? -eq 0 ]] && [[ -n "$enc" ]] && [[ "$enc" != "off" ]]; then
+ log_unsupported "bootfs pool property not supported when \
+encryption is set to on."
+fi
+
+log_must $ZFS snapshot $TESTPOOL/$FS@snap
+log_must $ZFS clone $TESTPOOL/$FS@snap $TESTPOOL/clone
+
+log_must $ZPOOL set bootfs=$TESTPOOL/$FS $TESTPOOL
+log_must $ZPOOL set bootfs=$TESTPOOL/$FS@snap $TESTPOOL
+log_must $ZPOOL set bootfs=$TESTPOOL/clone $TESTPOOL
+
+log_must $ZFS promote $TESTPOOL/clone
+log_must $ZPOOL set bootfs=$TESTPOOL/clone $TESTPOOL
+log_pass "Valid datasets are accepted as bootfs property values"
diff --git a/tests/sys/cddl/zfs/tests/bootfs/bootfs_002_neg.ksh b/tests/sys/cddl/zfs/tests/bootfs/bootfs_002_neg.ksh
new file mode 100644
index 000000000000..b2c356df4f6c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/bootfs/bootfs_002_neg.ksh
@@ -0,0 +1,76 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: bootfs_002_neg
+#
+# DESCRIPTION:
+#
+# Invalid datasets are rejected as boot property values
+#
+# STRATEGY:
+#
+# 1. Create a zvol
+# 2. Verify that we can't set the bootfs to those datasets
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-03-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup {
+ destroy_pool $TESTPOOL
+}
+
+
+$ZPOOL set 2>&1 | $GREP bootfs > /dev/null
+if [ $? -ne 0 ]
+then
+ log_unsupported "bootfs pool property not supported on this release."
+fi
+
+log_assert "Invalid datasets are rejected as boot property values"
+log_onexit cleanup
+
+DISK=${DISKS%% *}
+
+log_must $ZPOOL create $TESTPOOL $DISK
+log_must $ZFS create -V 10m $TESTPOOL/vol
+log_mustnot $ZPOOL set bootfs=$TESTPOOL/vol $TESTPOOL
+
+log_pass "Invalid datasets are rejected as boot property values"
diff --git a/tests/sys/cddl/zfs/tests/bootfs/bootfs_003_pos.ksh b/tests/sys/cddl/zfs/tests/bootfs/bootfs_003_pos.ksh
new file mode 100644
index 000000000000..27edf4b21acb
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/bootfs/bootfs_003_pos.ksh
@@ -0,0 +1,104 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: bootfs_003_pos
+#
+# DESCRIPTION:
+#
+# Valid pool names are accepted
+#
+# STRATEGY:
+# 1. Using a list of valid pool names
+# 2. Create a filesystem in that pool
+# 2. Verify we can set the bootfs to that filesystem
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-03-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+set -A pools "pool.${TESTCASE_ID}" "pool123" "mypool"
+typeset VDEV=$TMPDIR/bootfs_003.${TESTCASE_ID}.dat
+
+function cleanup {
+ typeset -i i=0
+ while [ $i -lt "${#pools[@]}" ]; do
+ destroy_pool ${pools[$i]}
+ i=$(( $i + 1 ))
+ done
+ $RM $VDEV
+}
+
+
+$ZPOOL set 2>&1 | $GREP bootfs > /dev/null
+if [ $? -ne 0 ]
+then
+ log_unsupported "bootfs pool property not supported on this release."
+fi
+
+log_onexit cleanup
+
+log_assert "Valid pool names are accepted by zpool set bootfs"
+create_vdevs $VDEV
+
+typeset -i i=0;
+
+while [ $i -lt "${#pools[@]}" ]
+do
+ POOL=${pools[$i]}
+ log_must $ZPOOL create $POOL $VDEV
+ log_must $ZFS create $POOL/$FS
+
+ enc=$(get_prop encryption $POOL/$FS)
+ if [[ $? -eq 0 ]] && [[ -n "$enc" ]] && [[ "$enc" != "off" ]]; then
+ log_unsupported "bootfs pool property not supported \
+when encryption is set to on."
+ fi
+
+ log_must $ZPOOL set bootfs=$POOL/$FS $POOL
+ RES=$($ZPOOL get bootfs $POOL | $TAIL -1 | $AWK '{print $3}' )
+ if [ $RES != "$POOL/$FS" ]
+ then
+ log_fail "Expected $RES == $POOL/$FS"
+ fi
+ log_must $ZPOOL destroy -f $POOL
+ i=$(( $i + 1 ))
+done
+
+log_pass "Valid pool names are accepted by zpool set bootfs"
diff --git a/tests/sys/cddl/zfs/tests/bootfs/bootfs_004_neg.ksh b/tests/sys/cddl/zfs/tests/bootfs/bootfs_004_neg.ksh
new file mode 100644
index 000000000000..b356c2b62b32
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/bootfs/bootfs_004_neg.ksh
@@ -0,0 +1,105 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: bootfs_004_neg
+#
+# DESCRIPTION:
+#
+# Invalid pool names are rejected by zpool set bootfs
+#
+# STRATEGY:
+# 1. Try to set bootfs on some non-existent pools
+#
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-03-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+set -A pools "pool//${TESTCASE_ID}" "pool%d123" "mirror" "c0t0d0s0" "pool*23*" "*po!l" \
+ "%s££%^"
+typeset VDEV=$TMPDIR/bootfs_004.${TESTCASE_ID}.dat
+
+function cleanup {
+ typeset -i=0
+ while [ $i -lt "${#pools[@]}" ]; do
+ destroy_pool ${pools[$i]}
+ i=$(( $i + 1 ))
+ done
+ $RM $VDEV
+}
+
+
+$ZPOOL set 2>&1 | $GREP bootfs > /dev/null
+if [ $? -ne 0 ]
+then
+ log_unsupported "bootfs pool property not supported on this release."
+fi
+
+log_assert "Invalid pool names are rejected by zpool set bootfs"
+log_onexit cleanup
+
+# here, we build up a large string and add it to the list of pool names
+# a word to the ksh-wary, ${#array[@]} gives you the
+# total number of entries in an array, so array[${#array[@]}]
+# will index the last entry+1, ksh arrays start at index 0.
+COUNT=0
+while [ $COUNT -le 1025 ]
+do
+ bigname="${bigname}o"
+ COUNT=$(( $COUNT + 1 ))
+done
+pools[${#pools[@]}]="$bigname"
+
+
+create_vdevs $VDEV
+typeset -i i=0;
+
+while [ $i -lt "${#pools[@]}" ]
+do
+ POOL=${pools[$i]}/$FS
+ log_mustnot $ZPOOL create $POOL $VDEV
+ log_mustnot $ZFS create $POOL/$FS
+ log_mustnot $ZPOOL set bootfs=$POOL/$FS $POOL
+
+ i=$(( $i + 1 ))
+done
+
+log_pass "Invalid pool names are rejected by zpool set bootfs"
diff --git a/tests/sys/cddl/zfs/tests/bootfs/bootfs_005_neg.ksh b/tests/sys/cddl/zfs/tests/bootfs/bootfs_005_neg.ksh
new file mode 100644
index 000000000000..db74b2e01121
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/bootfs/bootfs_005_neg.ksh
@@ -0,0 +1,98 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_upgrade/zpool_upgrade.cfg
+. $STF_SUITE/tests/cli_root/zpool_upgrade/zpool_upgrade.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: bootfs_005_neg
+#
+# DESCRIPTION:
+#
+# Boot properties cannot be set on pools with older versions
+#
+# STRATEGY:
+# 1. Copy and import some pools of older versions
+# 2. Create a filesystem on each
+# 3. Verify that zpool set bootfs fails on each
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-03-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup {
+
+ #
+ # we need destroy pools that created on top of $TESTPOOL first
+ #
+ typeset pool_name
+ for config in $CONFIGS; do
+ pool_name=$($ENV| $GREP "ZPOOL_VERSION_${config}_NAME"\
+ | $AWK -F= '{print $2}')
+ if poolexists $pool_name; then
+ log_must $ZPOOL destroy -f $pool_name
+ fi
+ done
+ destroy_pool $TESTPOOL
+}
+
+$ZPOOL set 2>&1 | $GREP bootfs > /dev/null
+if [ $? -ne 0 ]
+then
+ log_unsupported "bootfs pool property not supported on this release."
+fi
+
+log_assert "Boot properties cannot be set on pools with older versions"
+
+# These are configs from zpool_upgrade.cfg - see that file for more info.
+CONFIGS="1 2 3"
+
+log_onexit cleanup
+log_must $ZPOOL create -f $TESTPOOL $DISKS
+
+for config in $CONFIGS
+do
+ create_old_pool $config
+ POOL_NAME=$($ENV| $GREP "ZPOOL_VERSION_${config}_NAME"\
+ | $AWK -F= '{print $2}')
+ log_must $ZFS create $POOL_NAME/$FS
+ log_mustnot $ZPOOL set bootfs=$POOL_NAME/$FS $POOL_NAME
+ log_must destroy_upgraded_pool $config
+done
+
+log_pass "Boot properties cannot be set on pools with older versions"
diff --git a/tests/sys/cddl/zfs/tests/bootfs/bootfs_006_pos.ksh b/tests/sys/cddl/zfs/tests/bootfs/bootfs_006_pos.ksh
new file mode 100644
index 000000000000..7312d861a553
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/bootfs/bootfs_006_pos.ksh
@@ -0,0 +1,157 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: bootfs_006_pos
+#
+# DESCRIPTION:
+#
+# Pools of correct vdev types accept boot property
+#
+# STRATEGY:
+# 1. create pools of each vdev type (raid, raidz, raidz2, mirror + hotspares)
+# 2. verify we can set bootfs on each pool type according to design
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-03-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+$ZPOOL set 2>&1 | $GREP bootfs > /dev/null
+if [ $? -ne 0 ]
+then
+ log_unsupported "bootfs pool property not supported on this release."
+fi
+
+VDEV1=$TMPDIR/bootfs_006_pos_a.${TESTCASE_ID}.dat
+VDEV2=$TMPDIR/bootfs_006_pos_b.${TESTCASE_ID}.dat
+VDEV3=$TMPDIR/bootfs_006_pos_c.${TESTCASE_ID}.dat
+VDEV4=$TMPDIR/bootfs_006_pos_d.${TESTCASE_ID}.dat
+
+function verify_bootfs { # $POOL
+ POOL=$1
+ log_must $ZFS create $POOL/$FS
+
+ enc=$(get_prop encryption $POOL/$FS)
+ if [[ $? -eq 0 ]] && [[ -n "$enc" ]] && [[ "$enc" != "off" ]]; then
+ log_unsupported "bootfs pool property not supported \
+when encryption is set to on."
+ fi
+
+ log_must $ZPOOL set bootfs=$POOL/$FS $POOL
+ VAL=$($ZPOOL get bootfs $POOL | $TAIL -1 | $AWK '{print $3}' )
+ if [ $VAL != "$POOL/$FS" ]
+ then
+ log_must $ZPOOL status -v $POOL
+ log_fail "set/get failed on $POOL - expected $VAL == $POOL/$FS"
+ fi
+ log_must $ZPOOL destroy $POOL
+}
+
+function verify_no_bootfs { # $POOL
+ POOL=$1
+ log_must $ZFS create $POOL/$FS
+ log_mustnot $ZPOOL set bootfs=$POOL/$FS $POOL
+ VAL=$($ZPOOL get bootfs $POOL | $TAIL -1 | $AWK '{print $3}' )
+ if [ $VAL == "$POOL/$FS" ]
+ then
+ log_must $ZPOOL status -v $POOL
+ log_fail "set/get unexpectedly failed $VAL != $POOL/$FS"
+ fi
+ log_must $ZPOOL destroy $POOL
+}
+
+function cleanup {
+ destroy_pool $TESTPOOL
+ log_must $RM $VDEV1 $VDEV2 $VDEV3 $VDEV4
+}
+
+log_assert "Pools of correct vdev types accept boot property"
+
+
+
+log_onexit cleanup
+log_must create_vdevs $VDEV1 $VDEV2 $VDEV3 $VDEV4
+
+
+## the following configurations are supported bootable pools
+
+# normal
+log_must $ZPOOL create $TESTPOOL $VDEV1
+verify_bootfs $TESTPOOL
+
+# normal + hotspare
+log_must $ZPOOL create $TESTPOOL $VDEV1 spare $VDEV2
+verify_bootfs $TESTPOOL
+
+# mirror
+log_must $ZPOOL create $TESTPOOL mirror $VDEV1 $VDEV2
+verify_bootfs $TESTPOOL
+
+# mirror + hotspare
+log_must $ZPOOL create $TESTPOOL mirror $VDEV1 $VDEV2 spare $VDEV3
+verify_bootfs $TESTPOOL
+
+## the following configurations are not supported as bootable pools in Solaris,
+## but they are in FreeBSD
+
+# stripe
+log_must $ZPOOL create $TESTPOOL $VDEV1 $VDEV2
+verify_bootfs $TESTPOOL
+
+# stripe + hotspare
+log_must $ZPOOL create $TESTPOOL $VDEV1 $VDEV2 spare $VDEV3
+verify_bootfs $TESTPOOL
+
+# raidz
+log_must $ZPOOL create $TESTPOOL raidz $VDEV1 $VDEV2
+verify_bootfs $TESTPOOL
+
+# raidz + hotspare
+log_must $ZPOOL create $TESTPOOL raidz $VDEV1 $VDEV2 spare $VDEV3
+verify_bootfs $TESTPOOL
+
+# raidz2
+log_must $ZPOOL create $TESTPOOL raidz2 $VDEV1 $VDEV2 $VDEV3
+verify_bootfs $TESTPOOL
+
+# raidz2 + hotspare
+log_must $ZPOOL create $TESTPOOL raidz2 $VDEV1 $VDEV2 $VDEV3 spare $VDEV4
+verify_bootfs $TESTPOOL
+
+log_pass "Pools of correct vdev types accept boot property"
diff --git a/tests/sys/cddl/zfs/tests/bootfs/bootfs_007_pos.ksh b/tests/sys/cddl/zfs/tests/bootfs/bootfs_007_pos.ksh
new file mode 100644
index 000000000000..a9903fcffb50
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/bootfs/bootfs_007_pos.ksh
@@ -0,0 +1,76 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: bootfs_007_neg
+#
+# DESCRIPTION:
+#
+# Setting bootfs on a pool which was configured with the whole disk
+# (i.e. EFI) will succeed. This is only supported on FreeBSD, not Solaris.
+#
+# STRATEGY:
+# 1. create a pool with a whole disk
+# 2. create a filesystem on this pool
+# 3. verify we can set bootfs on the filesystem we just created.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-07-08)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup {
+ if poolexists $TESTPOOL ; then
+ destroy_pool "$TESTPOOL"
+ fi
+}
+
+log_onexit cleanup
+
+DISK=${DISKS%% *}
+typeset EFI_BOOTFS=$TESTPOOL/efs
+typeset assert_mesg="setting bootfs on a pool which was configured with the \
+ whole disk will succeed"
+
+log_assert $assert_mesg
+create_pool "$TESTPOOL" "$DISK"
+log_must $ZFS create $EFI_BOOTFS
+
+log_must $ZPOOL set bootfs=$EFI_BOOTFS $TESTPOOL
+
+log_pass $assert_mesg
diff --git a/tests/sys/cddl/zfs/tests/bootfs/bootfs_008_neg.ksh b/tests/sys/cddl/zfs/tests/bootfs/bootfs_008_neg.ksh
new file mode 100644
index 000000000000..c91c7cae1e70
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/bootfs/bootfs_008_neg.ksh
@@ -0,0 +1,91 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: bootfs_008_neg
+#
+# DESCRIPTION:
+#
+# setting bootfs on a dataset which has gzip compression enabled will fail
+#
+# STRATEGY:
+# 1. create pools based on a valid vdev
+# 2. create a filesytem on this pool and set the compression property to gzip1-9
+# 3. set the pool's bootfs property to filesystem we just configured which should
+# fail
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-07-08)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup {
+ if poolexists $TESTPOOL ; then
+ destroy_pool "$TESTPOOL"
+ fi
+
+ if [[ -f $VDEV ]]; then
+ log_must $RM -f $VDEV
+ fi
+}
+
+typeset assert_msg="setting bootfs on a dataset which has gzip \
+ compression enabled will fail"
+
+typeset VDEV=$TMPDIR/bootfs_008_neg_a.${TESTCASE_ID}.dat
+typeset COMP_FS=$TESTPOOL/COMP_FS
+
+log_onexit cleanup
+log_assert $assert_msg
+
+log_must create_vdevs $VDEV
+log_must $ZPOOL create $TESTPOOL $VDEV
+log_must $ZFS create $COMP_FS
+
+typeset -i i=0
+set -A gtype "gzip" "gzip-1" "gzip-2" "gzip-3" "gzip-4" "gzip-5" \
+ "gzip-6" "gzip-7" "gzip-8" "gzip-9" "zle"
+
+while (( i < ${#gtype[@]} )); do
+ log_must $ZFS set compression=${gtype[i]} $COMP_FS
+ log_mustnot $ZPOOL set bootfs=$COMP_FS $TESTPOOL
+ log_must $ZFS set compression=off $COMP_FS
+ (( i += 1 ))
+done
+
+log_pass $assert_msg
diff --git a/tests/sys/cddl/zfs/tests/bootfs/bootfs_009_neg.ksh b/tests/sys/cddl/zfs/tests/bootfs/bootfs_009_neg.ksh
new file mode 100644
index 000000000000..be95f4e8fe64
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/bootfs/bootfs_009_neg.ksh
@@ -0,0 +1,92 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: bootfs_009_neg
+#
+# DESCRIPTION:
+#
+# Valid encrypted datasets can't be set bootfs property values
+#
+# STRATEGY:
+# 1. Create encrypted datasets in a test pool
+# 2. Try setting encrypted datasets as boot filesystems
+# 3. Verify failures.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-07-29)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup {
+ destroy_pool $TESTPOOL
+}
+
+$ZPOOL set 2>&1 | $GREP bootfs > /dev/null
+if [ $? -ne 0 ]
+then
+ log_unsupported "bootfs pool property not supported on this release."
+fi
+
+log_assert "Valid encrypted datasets can't be set bootfs property values"
+log_onexit cleanup
+
+DISK=${DISKS%% *}
+
+log_must $ZPOOL create $TESTPOOL $DISK
+log_must $ZFS create $TESTPOOL/$FS
+
+enc=$(get_prop encryption $TESTPOOL/$FS)
+if [ $? -ne 0 ]; then
+ log_unsupported "get_prop encryption $TESTPOOL/$FS failed."
+else
+ if [ -z "$enc" ] || [ "$enc" = "off" ]; then
+ log_unsupported "encryption isn't set to on, this test case \
+is not supported."
+ else
+ log_mustnot $ZPOOL set bootfs=$TESTPOOL/$FS $TESTPOOL
+ fi
+fi
+
+
+log_must $ZFS snapshot $TESTPOOL/$FS@snap
+log_must $ZFS clone $TESTPOOL/$FS@snap $TESTPOOL/clone
+log_must $ZFS promote $TESTPOOL/clone
+log_mustnot $ZPOOL set bootfs=$TESTPOOL/clone $TESTPOOL
+
+log_pass "Encrypted datasets can't be set bootfs property"
diff --git a/tests/sys/cddl/zfs/tests/bootfs/bootfs_test.sh b/tests/sys/cddl/zfs/tests/bootfs/bootfs_test.sh
new file mode 100755
index 000000000000..d041e52f399a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/bootfs/bootfs_test.sh
@@ -0,0 +1,178 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case bootfs_001_pos
+bootfs_001_pos_head()
+{
+ atf_set "descr" "Valid datasets are accepted as bootfs property values"
+ atf_set "require.progs" "ksh93 zpool zfs"
+}
+bootfs_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/bootfs.cfg
+
+ ksh93 $(atf_get_srcdir)/bootfs_001_pos.ksh || atf_fail "Testcase failed"
+}
+
+
+atf_test_case bootfs_002_neg
+bootfs_002_neg_head()
+{
+ atf_set "descr" "Invalid datasets are rejected as boot property values"
+ atf_set "require.progs" "ksh93 zfs zpool"
+}
+bootfs_002_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/bootfs.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/bootfs_002_neg.ksh || atf_fail "Testcase failed"
+}
+
+
+atf_test_case bootfs_003_pos
+bootfs_003_pos_head()
+{
+ atf_set "descr" "Valid pool names are accepted by zpool set bootfs"
+ atf_set "require.progs" "ksh93 zpool zfs"
+}
+bootfs_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/bootfs.cfg
+
+ ksh93 $(atf_get_srcdir)/bootfs_003_pos.ksh || atf_fail "Testcase failed"
+}
+
+
+atf_test_case bootfs_004_neg
+bootfs_004_neg_head()
+{
+ atf_set "descr" "Invalid pool names are rejected by zpool set bootfs"
+ atf_set "require.progs" "ksh93 zpool zfs"
+}
+bootfs_004_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/bootfs.cfg
+
+ ksh93 $(atf_get_srcdir)/bootfs_004_neg.ksh || atf_fail "Testcase failed"
+}
+
+
+atf_test_case bootfs_005_neg
+bootfs_005_neg_head()
+{
+ atf_set "descr" "Boot properties cannot be set on pools with older versions"
+ atf_set "require.progs" "ksh93 zfs zpool"
+}
+bootfs_005_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/bootfs.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/bootfs_005_neg.ksh || atf_fail "Testcase failed"
+}
+
+
+atf_test_case bootfs_006_pos
+bootfs_006_pos_head()
+{
+ atf_set "descr" "Pools of correct vdev types accept boot property"
+ atf_set "require.progs" "ksh93 zfs zpool"
+}
+bootfs_006_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/bootfs.cfg
+
+ ksh93 $(atf_get_srcdir)/bootfs_006_pos.ksh || atf_fail "Testcase failed"
+}
+
+
+atf_test_case bootfs_007_pos
+bootfs_007_pos_head()
+{
+ atf_set "descr" "setting bootfs on a pool which was configured with the whole disk will succeed"
+ atf_set "require.progs" "ksh93 zfs zpool"
+}
+bootfs_007_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/bootfs.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/bootfs_007_pos.ksh || atf_fail "Testcase failed"
+}
+
+
+atf_test_case bootfs_008_neg
+bootfs_008_neg_head()
+{
+ atf_set "descr" "setting bootfs on a dataset which has gzip compression enabled will fail"
+ atf_set "require.progs" "ksh93 zpool zfs"
+}
+bootfs_008_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/bootfs.cfg
+
+ ksh93 $(atf_get_srcdir)/bootfs_008_neg.ksh || atf_fail "Testcase failed"
+}
+
+
+atf_test_case bootfs_009_neg
+bootfs_009_neg_head()
+{
+ atf_set "descr" "Valid encrypted datasets can't be set bootfs property values"
+ atf_set "require.config" zfs_encryption
+ atf_set "require.progs" "ksh93 zfs zpool"
+}
+bootfs_009_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/bootfs.cfg
+
+ ksh93 $(atf_get_srcdir)/bootfs_009_neg.ksh || atf_fail "Testcase failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case bootfs_001_pos
+ atf_add_test_case bootfs_002_neg
+ atf_add_test_case bootfs_003_pos
+ atf_add_test_case bootfs_004_neg
+ atf_add_test_case bootfs_005_neg
+ atf_add_test_case bootfs_006_pos
+ atf_add_test_case bootfs_007_pos
+ atf_add_test_case bootfs_008_neg
+ atf_add_test_case bootfs_009_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/cache/Makefile b/tests/sys/cddl/zfs/tests/cache/Makefile
new file mode 100644
index 000000000000..df46dedcc8ec
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cache/Makefile
@@ -0,0 +1,27 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cache
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= cache_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= cache_005_neg.ksh
+${PACKAGE}FILES+= cache.kshlib
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= cache_011_pos.ksh
+${PACKAGE}FILES+= cache_010_neg.ksh
+${PACKAGE}FILES+= cache_008_neg.ksh
+${PACKAGE}FILES+= cache_004_neg.ksh
+${PACKAGE}FILES+= cache_009_pos.ksh
+${PACKAGE}FILES+= cache_001_pos.ksh
+${PACKAGE}FILES+= cache_003_pos.ksh
+${PACKAGE}FILES+= cache_007_neg.ksh
+${PACKAGE}FILES+= cache_002_pos.ksh
+${PACKAGE}FILES+= cache_006_pos.ksh
+${PACKAGE}FILES+= cache.cfg
+${PACKAGE}FILES+= setup.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cache/cache.cfg b/tests/sys/cddl/zfs/tests/cache/cache.cfg
new file mode 100644
index 000000000000..0214162947d2
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cache/cache.cfg
@@ -0,0 +1,42 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
+. $STF_SUITE/include/libtest.kshlib
+
+set_disks
+
+export SIZE=64M
+
+export VDIR=$TMPDIR/disk.${TESTCASE_ID}
+export VDIR2=$TMPDIR/disk2.${TESTCASE_ID}
+
+export VDEV="$VDIR/a $VDIR/b $VDIR/c"
+export LDEV="$DISK0"
+export VDEV2="$VDIR2/a $VDIR2/b $VDIR2/c"
+export LDEV2="$DISK1"
+
+export STF_TIMEOUT=1200
diff --git a/tests/sys/cddl/zfs/tests/cache/cache.kshlib b/tests/sys/cddl/zfs/tests/cache/cache.kshlib
new file mode 100644
index 000000000000..3953f9868aba
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cache/cache.kshlib
@@ -0,0 +1,175 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+function cleanup
+{
+ log_note "Final pool configurations:"
+ poolexists $TESTPOOL && log_must $ZPOOL status -v $TESTPOOL
+ poolexists $TESTPOOL2 && log_must $ZPOOL status -v $TESTPOOL2
+ destroy_pool $TESTPOOL
+ destroy_pool $TESTPOOL2
+}
+
+#
+# Try zpool status/iostat for given pool
+#
+# $1 pool
+#
+function display_status
+{
+ typeset pool=$1
+
+ typeset -i ret=0
+ $ZPOOL status -xv $pool > /dev/null 2>&1
+ ret=$?
+
+ $ZPOOL iostat > /dev/null 2>&1
+ ((ret |= $?))
+
+ typeset mntpnt=$(get_prop mountpoint $pool)
+ $DD if=/dev/random of=$mntpnt/testfile.${TESTCASE_ID} &
+ typeset pid=$!
+
+ $ZPOOL iostat -v 1 3 > /dev/null
+ ((ret |= $?))
+
+ kill -9 $pid
+
+ return $ret
+}
+
+#
+# Verify the give cache device have correct type and status
+#
+# $1 pool name
+# $2 device name
+# $3 device status
+# $4 device type
+#
+function verify_cache_device
+{
+ typeset pool=$1
+ typeset device=$2
+ typeset status=$3
+ typeset type=$4
+
+ if [[ -z $pool || -z $device || -z $status ]]; then
+ log_fail "Usage: verify_cache_device <pool> <device> " \
+ "<status> [type]"
+ fi
+
+ # Zpool status returns on the device name sans the /dev, so
+ # if the device contains /dev/ remove it.
+ device=${device#"/dev/"}
+
+ if [[ $WRAPPER == *"smi"* ]]; then
+ $ECHO $device | $EGREP "^c[0-F]+([td][0-F]+)+$" > /dev/null 2>&1
+ if (( $? == 0 )); then
+ device=${device}s2
+ fi
+ fi
+
+ #
+ # Get all the cache devices and status table like below
+ #
+ # mirror:/disks/d ONLINE mirror:/disks/e ONLINE stripe:/disks/f ONLINE
+ #
+ set -A dev_stat_tab $($ZPOOL status -v $pool | $NAWK '
+ function parse_name(status)
+ {
+ if (status == "OFFLINE")
+ return substr($7,6)
+ else if (status == "UNAVAIL")
+ return substr($7,6)
+ else
+ return $1
+ }
+
+ BEGIN {in_cache=0}
+ /\tcache/ {in_cache=1}
+ /\tlog/ || /\tspares/ || /^$/ {in_cache=0}
+
+ # Skip if not in a cache section
+ (in_cache==0) { next; }
+
+ /\t (\/|[0-9a-zA-Z])/ {
+ print "stripe:" parse_name($2) " " $2;
+ }
+
+ /\t (\/|[a-zA-Z])/ {
+ print "mirror:" parse_name($2) " " $2;
+ }
+
+ # When hotspare is replacing
+ /\t (\/|[a-zA-Z])/ {
+ print "mirror:" parse_name($2) " " $2;
+ }
+ ')
+
+ typeset -i i=0
+ typeset find=0
+ while (( i < ${#dev_stat_tab[@]} )); do
+ typeset dev=${dev_stat_tab[$i]}
+ typeset stat=${dev_stat_tab[((i+1))]}
+
+ case $dev in
+ stripe:$device)
+ if [[ "$type" == 'mirror' ]]; then
+ log_note "Unexpected type: mirror"
+ return 1
+ else
+ if [[ $stat != $status ]]; then
+ log_note "Status($stat) " \
+ "!= Expected stat($status)"
+ return 1
+ fi
+ return 0
+ fi
+ ;;
+ mirror:$device)
+ if [[ -z "$type" || $type == 'stripe' ]]; then
+ log_note "Unexpected type: stripe"
+ return 1
+ else
+ if [[ $stat != $status ]]; then
+ log_note "Status($stat) " \
+ "!= Expected stat($status)"
+ return 1
+ fi
+ return 0
+ fi
+ ;;
+ esac
+
+ ((i += 2))
+ done
+
+ log_note "Can not find device: $device"
+
+ return 1
+}
diff --git a/tests/sys/cddl/zfs/tests/cache/cache_001_pos.ksh b/tests/sys/cddl/zfs/tests/cache/cache_001_pos.ksh
new file mode 100644
index 000000000000..610c1eb86b8f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cache/cache_001_pos.ksh
@@ -0,0 +1,73 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cache/cache.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: cache_001_pos
+#
+# DESCRIPTION:
+# Creating a pool with a cache device succeeds.
+#
+# STRATEGY:
+# 1. Create pool with separated cache devices.
+# 2. Display pool status
+# 3. Destroy and loop to create pool with different configuration.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-04-24)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Creating a pool with a cache device succeeds."
+log_onexit cleanup
+
+for type in "" "mirror" "raidz" "raidz2"
+do
+ log_must $ZPOOL create $TESTPOOL $type $VDEV \
+ cache $LDEV
+ log_must display_status $TESTPOOL
+
+ ldev=$(random_get $LDEV)
+ log_must verify_cache_device $TESTPOOL $ldev 'ONLINE'
+
+ log_must $ZPOOL remove $TESTPOOL $ldev
+ log_must check_vdev_state $TESTPOOL $ldev ""
+
+ log_must $ZPOOL destroy -f $TESTPOOL
+done
+
+log_pass "Creating a pool with a cache device succeeds."
diff --git a/tests/sys/cddl/zfs/tests/cache/cache_002_pos.ksh b/tests/sys/cddl/zfs/tests/cache/cache_002_pos.ksh
new file mode 100644
index 000000000000..ec05e972f670
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cache/cache_002_pos.ksh
@@ -0,0 +1,73 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cache/cache.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: cache_002_pos
+#
+# DESCRIPTION:
+# Adding a cache device to normal pool works.
+#
+# STRATEGY:
+# 1. Create pool
+# 2. Add cache devices with different configuration
+# 3. Display pool status
+# 4. Destroy and loop to create pool with different configuration.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-04-24)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Adding a cache device to normal pool works."
+log_onexit cleanup
+
+for type in "" "mirror" "raidz" "raidz2"
+do
+ log_must $ZPOOL create $TESTPOOL $type $VDEV
+ log_must $ZPOOL add $TESTPOOL cache $LDEV
+ log_must display_status $TESTPOOL
+ typeset ldev=$(random_get $LDEV)
+ log_must verify_cache_device $TESTPOOL $ldev 'ONLINE'
+
+ log_must $ZPOOL remove $TESTPOOL $ldev
+ log_must check_vdev_state $TESTPOOL $ldev ""
+
+ log_must $ZPOOL destroy -f $TESTPOOL
+done
+
+log_pass "Adding a cache device to normal pool works."
diff --git a/tests/sys/cddl/zfs/tests/cache/cache_003_pos.ksh b/tests/sys/cddl/zfs/tests/cache/cache_003_pos.ksh
new file mode 100644
index 000000000000..c2a18a8f136f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cache/cache_003_pos.ksh
@@ -0,0 +1,76 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cache/cache.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: cache_003_pos
+#
+# DESCRIPTION:
+# Adding an extra cache device works
+#
+# STRATEGY:
+# 1. Create pool with separated cache devices.
+# 2. Add an extra cache devices
+# 3. Display pool status
+# 4. Destroy and loop to create pool with different configuration.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-04-24)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Adding an extra cache device works."
+log_onexit cleanup
+
+for type in "" "mirror" "raidz" "raidz2"
+do
+ log_must $ZPOOL create $TESTPOOL $type $VDEV \
+ cache $LDEV
+ log_must $ZPOOL add $TESTPOOL \
+ cache $LDEV2
+
+ log_must display_status $TESTPOOL
+ ldev=$(random_get $LDEV2)
+ log_must verify_cache_device $TESTPOOL $ldev 'ONLINE'
+
+ log_must $ZPOOL remove $TESTPOOL $ldev
+ log_must check_vdev_state $TESTPOOL $ldev ""
+
+ log_must $ZPOOL destroy -f $TESTPOOL
+done
+
+log_pass "Adding an extra cache device works."
diff --git a/tests/sys/cddl/zfs/tests/cache/cache_004_neg.ksh b/tests/sys/cddl/zfs/tests/cache/cache_004_neg.ksh
new file mode 100644
index 000000000000..47731150f0a4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cache/cache_004_neg.ksh
@@ -0,0 +1,71 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cache/cache.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: cache_004_neg
+#
+# DESCRIPTION:
+# Attaching a cache device fails.
+#
+# STRATEGY:
+# 1. Create pool with separated cache devices.
+# 2. Attaching a cache device for existing cache device
+# 3. Verify the operation fails
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-03-26)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Attaching a cache device fails."
+log_onexit cleanup
+
+for type in "" "mirror" "raidz" "raidz2"
+do
+ log_must $ZPOOL create $TESTPOOL $type $VDEV \
+ cache $LDEV
+
+ ldev=$(random_get $LDEV)
+ typeset ldev2=$(random_get $LDEV2)
+ log_mustnot $ZPOOL attach $TESTPOOL $ldev $ldev2
+ log_must check_vdev_state $TESTPOOL $ldev2 ""
+
+ log_must $ZPOOL destroy -f $TESTPOOL
+done
+
+log_pass "Attaching a cache device fails."
diff --git a/tests/sys/cddl/zfs/tests/cache/cache_005_neg.ksh b/tests/sys/cddl/zfs/tests/cache/cache_005_neg.ksh
new file mode 100644
index 000000000000..a78d31f2bf63
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cache/cache_005_neg.ksh
@@ -0,0 +1,72 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cache/cache.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: cache_005_neg
+#
+# DESCRIPTION:
+# Replacing a cache device fails.
+#
+# STRATEGY:
+# 1. Create pool with cache devices.
+# 2. Replacing one cache device
+# 3. Verify replace fails
+# 4. Destroy and loop to create pool with different configuration.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-03-26)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Replacing a cache device fails."
+log_onexit cleanup
+
+for type in "" "mirror" "raidz" "raidz2"
+do
+ log_must $ZPOOL create $TESTPOOL $type $VDEV \
+ cache $LDEV
+ sdev=$(random_get $LDEV)
+ tdev=$(random_get $LDEV2)
+ log_mustnot $ZPOOL replace $TESTPOOL $sdev $tdev
+ log_must verify_cache_device $TESTPOOL $sdev 'ONLINE'
+ log_must check_vdev_state $TESTPOOL $tdev ""
+
+ log_must $ZPOOL destroy -f $TESTPOOL
+done
+
+log_pass "Replacing a cache device fails."
diff --git a/tests/sys/cddl/zfs/tests/cache/cache_006_pos.ksh b/tests/sys/cddl/zfs/tests/cache/cache_006_pos.ksh
new file mode 100644
index 000000000000..ec0d9f6b1012
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cache/cache_006_pos.ksh
@@ -0,0 +1,92 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cache/cache.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: cache_006_pos
+#
+# DESCRIPTION:
+# Exporting and importing pool with cache devices passes.
+#
+# STRATEGY:
+# 1. Create pool with cache devices.
+# 2. Export and import the pool
+# 3. Display pool status
+# 4. Destroy and import the pool again
+# 5. Display pool status
+# 6. Destroy and loop to create pool with different configuration.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-04-24)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Exporting and importing pool with cache devices passes."
+log_onexit cleanup
+
+for type in "" "mirror" "raidz" "raidz2"
+do
+ log_must $ZPOOL create $TESTPOOL $type $VDEV \
+ cache $LDEV $LDEV2
+ ldev=$(random_get $LDEV $LDEV2)
+ log_must verify_cache_device \
+ $TESTPOOL $ldev 'ONLINE'
+
+ #
+ # Nomal export/import operating
+ #
+ log_must $ZPOOL export $TESTPOOL
+ log_must $ZPOOL import -d $VDIR $TESTPOOL
+ log_must display_status $TESTPOOL
+ ldev=$(random_get $LDEV $LDEV2)
+ log_must verify_cache_device \
+ $TESTPOOL $ldev 'ONLINE'
+
+ #
+ # Destroy the pool and import again
+ #
+ log_must $ZPOOL destroy $TESTPOOL
+ log_must $ZPOOL import -Df -d $VDIR $TESTPOOL
+ log_must display_status $TESTPOOL
+ ldev=$(random_get $LDEV $LDEV2)
+ log_must verify_cache_device \
+ $TESTPOOL $ldev 'ONLINE'
+
+ log_must $ZPOOL destroy -f $TESTPOOL
+done
+
+log_pass "Exporting and importing pool with cache devices passes."
diff --git a/tests/sys/cddl/zfs/tests/cache/cache_007_neg.ksh b/tests/sys/cddl/zfs/tests/cache/cache_007_neg.ksh
new file mode 100644
index 000000000000..7208386d53bb
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cache/cache_007_neg.ksh
@@ -0,0 +1,70 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cache/cache.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: cache_007_neg
+#
+# DESCRIPTION:
+# A mirror/raidz/raidz2 cache is not supported.
+#
+# STRATEGY:
+# 1. Try to create pool with unsupported type
+# 2. Verify failed to create pool.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-04-24)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "A mirror/raidz/raidz2 cache is not supported."
+log_onexit cleanup
+
+for type in "" "mirror" "raidz" "raidz2"
+do
+ for cachetype in "mirror" "raidz" "raidz1" "raidz2"
+ do
+ log_mustnot $ZPOOL create $TESTPOOL $type $VDEV \
+ cache $cachetype $LDEV $LDEV2
+ ldev=$(random_get $LDEV $LDEV2)
+ log_mustnot verify_cache_device \
+ $TESTPOOL $ldev 'ONLINE' $cachetype
+ log_must datasetnonexists $TESTPOOL
+ done
+done
+
+log_pass "A mirror/raidz/raidz2 cache is not supported."
diff --git a/tests/sys/cddl/zfs/tests/cache/cache_008_neg.ksh b/tests/sys/cddl/zfs/tests/cache/cache_008_neg.ksh
new file mode 100644
index 000000000000..0bbddf211b9b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cache/cache_008_neg.ksh
@@ -0,0 +1,74 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cache/cache.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: cache_008_neg
+#
+# DESCRIPTION:
+# A mirror/raidz/raidz2 cache can not be added to existed pool.
+#
+# STRATEGY:
+# 1. Create pool with or without cache.
+# 2. Add a mirror/raidz/raidz2 cache to this pool.
+# 3. Verify failed to add.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-04-24)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "A raidz/raidz2 cache can not be added to existed pool."
+log_onexit cleanup
+
+for type in "" "mirror" "raidz" "raidz2"
+do
+ for cachetype in "mirror" "raidz" "raidz1" "raidz2"
+ do
+ log_must $ZPOOL create $TESTPOOL $type $VDEV \
+ cache $LDEV
+
+ log_mustnot $ZPOOL add $TESTPOOL cache $cachetype $LDEV2
+ ldev=$(random_get $LDEV2)
+ log_mustnot verify_cache_device \
+ $TESTPOOL $ldev 'ONLINE' $cachetype
+
+ log_must $ZPOOL destroy -f $TESTPOOL
+ done
+done
+
+log_pass "A mirror/raidz/raidz2 cache can not be added to existed pool."
diff --git a/tests/sys/cddl/zfs/tests/cache/cache_009_pos.ksh b/tests/sys/cddl/zfs/tests/cache/cache_009_pos.ksh
new file mode 100644
index 000000000000..ad67ebb52b7c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cache/cache_009_pos.ksh
@@ -0,0 +1,80 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cache/cache.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: cache_009_pos
+#
+# DESCRIPTION:
+# Offline and online a cache device succeed.
+#
+# STRATEGY:
+# 1. Create pool with mirror cache devices.
+# 2. Offine and online a cache device
+# 3. Display pool status
+# 4. Destroy and loop to create pool with different configuration.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-04-24)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Offline and online a cache device succeed."
+log_onexit cleanup
+
+for type in "" "mirror" "raidz" "raidz2"
+do
+ log_must $ZPOOL create $TESTPOOL $type $VDEV \
+ cache $LDEV $LDEV2
+
+ ldev=$(random_get $LDEV $LDEV2)
+ log_must $ZPOOL offline $TESTPOOL $ldev
+ log_must display_status $TESTPOOL
+ log_must verify_cache_device $TESTPOOL $ldev 'OFFLINE' ''
+ log_note "Offline pool configuration, for reference:"
+ log_must $ZPOOL status -v $TESTPOOL
+
+ log_must $ZPOOL online $TESTPOOL $ldev
+ log_must display_status $TESTPOOL
+ log_must verify_cache_device $TESTPOOL $ldev 'ONLINE' ''
+
+ log_note "Final pool configuration, for reference:"
+ log_must $ZPOOL status -v $TESTPOOL
+ log_must $ZPOOL destroy -f $TESTPOOL
+done
+
+log_pass "Offline and online a cache device succeed."
diff --git a/tests/sys/cddl/zfs/tests/cache/cache_010_neg.ksh b/tests/sys/cddl/zfs/tests/cache/cache_010_neg.ksh
new file mode 100644
index 000000000000..a062ab94cba2
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cache/cache_010_neg.ksh
@@ -0,0 +1,97 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cache/cache.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: cache_010_neg
+#
+# DESCRIPTION:
+# Verify cache device can only be disk or slice.
+#
+# STRATEGY:
+# 1. Create a pool
+# 2. Loop to add different object as cache
+# 3. Verify it fails
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-04-24)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup_testenv
+{
+ cleanup
+ if [[ -n $mdconfig_unit ]]; then
+ $MDCONFIG -d -u $mdconfig_unit
+ fi
+}
+
+log_assert "Cache device can only be disk or slice."
+log_onexit cleanup_testenv
+
+log_must $ZPOOL create $TESTPOOL $VDEV
+
+# Add nomal disk
+log_must $ZPOOL add $TESTPOOL cache ${LDEV}
+log_must verify_cache_device $TESTPOOL ${LDEV} 'ONLINE'
+# Add nomal file
+log_mustnot $ZPOOL add $TESTPOOL cache $VDEV2
+
+# Add md
+mdconfig_dev=${VDEV2%% *}
+mdconfig_unit=$($MDCONFIG $mdconfig_dev)
+log_note "$MDCONFIG $mdconfig_dev"
+if [[ $? -eq 0 ]]; then
+ log_note "$mdconfig_unit is created."
+else
+ log_fail "Failed to execute mdconfig."
+fi
+
+log_must $ZPOOL add $TESTPOOL cache $mdconfig_unit
+log_must verify_cache_device $TESTPOOL $mdconfig_unit 'ONLINE'
+log_must $ZPOOL destroy $TESTPOOL
+if [[ -n $mdconfig_unit ]]; then
+ log_must $MDCONFIG -d -u $mdconfig_unit
+ mdconfig_unit=""
+fi
+
+#Add zvol
+log_must $ZPOOL create $TESTPOOL2 $VDEV2
+log_must $ZFS create -V $SIZE $TESTPOOL2/$TESTVOL
+log_mustnot $ZPOOL add $TESTPOOL cache /dev/zvol/$TESTPOOL2/$TESTVOL
+
+log_pass "Cache device can only be disk or slice."
diff --git a/tests/sys/cddl/zfs/tests/cache/cache_011_pos.ksh b/tests/sys/cddl/zfs/tests/cache/cache_011_pos.ksh
new file mode 100644
index 000000000000..f1c5f43446c1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cache/cache_011_pos.ksh
@@ -0,0 +1,75 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cache/cache.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: cache_011_pos
+#
+# DESCRIPTION:
+# Remove cache device from pool with spare device should succeed.
+#
+# STRATEGY:
+# 1. Create pool with cache devices and spare devices
+# 2. Remove cache device from the pool
+# 3. The upper action should succeed
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-12-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup {
+ if datasetexists $TESTPOOL ; then
+ log_must $ZPOOL destroy -f $TESTPOOL
+ fi
+}
+
+log_assert "Remove cache device from pool with spare device should succeed"
+log_onexit cleanup
+
+for type in "" "mirror" "raidz" "raidz2"
+do
+ log_must $ZPOOL create $TESTPOOL $type $VDEV \
+ cache $LDEV spare $LDEV2
+
+ log_must $ZPOOL remove $TESTPOOL $LDEV
+ log_must display_status $TESTPOOL
+
+ log_must $ZPOOL destroy -f $TESTPOOL
+done
+
+log_pass "Remove cache device from pool with spare device should succeed"
diff --git a/tests/sys/cddl/zfs/tests/cache/cache_test.sh b/tests/sys/cddl/zfs/tests/cache/cache_test.sh
new file mode 100755
index 000000000000..79b09dd93183
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cache/cache_test.sh
@@ -0,0 +1,339 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case cache_001_pos cleanup
+cache_001_pos_head()
+{
+ atf_set "descr" "Creating a pool with a cache device succeeds."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1200
+}
+cache_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/cache.kshlib
+ . $(atf_get_srcdir)/cache.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/cache_001_pos.ksh || atf_fail "Testcase failed"
+}
+cache_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/cache.kshlib
+ . $(atf_get_srcdir)/cache.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case cache_002_pos cleanup
+cache_002_pos_head()
+{
+ atf_set "descr" "Adding a cache device to normal pool works."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1200
+}
+cache_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/cache.kshlib
+ . $(atf_get_srcdir)/cache.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/cache_002_pos.ksh || atf_fail "Testcase failed"
+}
+cache_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/cache.kshlib
+ . $(atf_get_srcdir)/cache.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case cache_003_pos cleanup
+cache_003_pos_head()
+{
+ atf_set "descr" "Adding an extra cache device works."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1200
+}
+cache_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/cache.kshlib
+ . $(atf_get_srcdir)/cache.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/cache_003_pos.ksh || atf_fail "Testcase failed"
+}
+cache_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/cache.kshlib
+ . $(atf_get_srcdir)/cache.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case cache_004_neg cleanup
+cache_004_neg_head()
+{
+ atf_set "descr" "Attaching a cache device fails."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1200
+}
+cache_004_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/cache.kshlib
+ . $(atf_get_srcdir)/cache.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/cache_004_neg.ksh || atf_fail "Testcase failed"
+}
+cache_004_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/cache.kshlib
+ . $(atf_get_srcdir)/cache.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case cache_005_neg cleanup
+cache_005_neg_head()
+{
+ atf_set "descr" "Replacing a cache device fails."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1200
+}
+cache_005_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/cache.kshlib
+ . $(atf_get_srcdir)/cache.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/cache_005_neg.ksh || atf_fail "Testcase failed"
+}
+cache_005_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/cache.kshlib
+ . $(atf_get_srcdir)/cache.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case cache_006_pos cleanup
+cache_006_pos_head()
+{
+ atf_set "descr" "Exporting and importing pool with cache devices passes."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1200
+}
+cache_006_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/cache.kshlib
+ . $(atf_get_srcdir)/cache.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/cache_006_pos.ksh || atf_fail "Testcase failed"
+}
+cache_006_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/cache.kshlib
+ . $(atf_get_srcdir)/cache.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case cache_007_neg cleanup
+cache_007_neg_head()
+{
+ atf_set "descr" "A mirror/raidz/raidz2 cache is not supported."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1200
+}
+cache_007_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/cache.kshlib
+ . $(atf_get_srcdir)/cache.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/cache_007_neg.ksh || atf_fail "Testcase failed"
+}
+cache_007_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/cache.kshlib
+ . $(atf_get_srcdir)/cache.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case cache_008_neg cleanup
+cache_008_neg_head()
+{
+ atf_set "descr" "A raidz/raidz2 cache can not be added to existed pool."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1200
+}
+cache_008_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/cache.kshlib
+ . $(atf_get_srcdir)/cache.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/cache_008_neg.ksh || atf_fail "Testcase failed"
+}
+cache_008_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/cache.kshlib
+ . $(atf_get_srcdir)/cache.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case cache_009_pos cleanup
+cache_009_pos_head()
+{
+ atf_set "descr" "Offline and online a cache device succeed."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1200
+}
+cache_009_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/cache.kshlib
+ . $(atf_get_srcdir)/cache.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/cache_009_pos.ksh || atf_fail "Testcase failed"
+}
+cache_009_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/cache.kshlib
+ . $(atf_get_srcdir)/cache.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case cache_010_neg cleanup
+cache_010_neg_head()
+{
+ atf_set "descr" "Cache device can only be disk or slice."
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 1200
+}
+cache_010_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/cache.kshlib
+ . $(atf_get_srcdir)/cache.cfg
+
+ [ -c /dev/mdctl ] || atf_skip "no /dev/mdctl to create md devices"
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/cache_010_neg.ksh || atf_fail "Testcase failed"
+}
+cache_010_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/cache.kshlib
+ . $(atf_get_srcdir)/cache.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case cache_011_pos cleanup
+cache_011_pos_head()
+{
+ atf_set "descr" "Remove cache device from pool with spare device should succeed"
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1200
+}
+cache_011_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/cache.kshlib
+ . $(atf_get_srcdir)/cache.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/cache_011_pos.ksh || atf_fail "Testcase failed"
+}
+cache_011_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/cache.kshlib
+ . $(atf_get_srcdir)/cache.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case cache_001_pos
+ atf_add_test_case cache_002_pos
+ atf_add_test_case cache_003_pos
+ atf_add_test_case cache_004_neg
+ atf_add_test_case cache_005_neg
+ atf_add_test_case cache_006_pos
+ atf_add_test_case cache_007_neg
+ atf_add_test_case cache_008_neg
+ atf_add_test_case cache_009_pos
+ atf_add_test_case cache_010_neg
+ atf_add_test_case cache_011_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/cache/cleanup.ksh b/tests/sys/cddl/zfs/tests/cache/cleanup.ksh
new file mode 100644
index 000000000000..6f8d02c89dd9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cache/cleanup.ksh
@@ -0,0 +1,39 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cache/cache.kshlib
+
+verify_runnable "global"
+
+cleanup
+if [[ -d $VDIR ]]; then
+ log_must $RM -rf $VDIR
+fi
+if [[ -d $VDIR2 ]]; then
+ log_must $RM -rf $VDIR2
+fi
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/cache/setup.ksh b/tests/sys/cddl/zfs/tests/cache/setup.ksh
new file mode 100644
index 000000000000..bfccaeeedd60
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cache/setup.ksh
@@ -0,0 +1,40 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cache/cache.kshlib
+
+verify_runnable "global"
+
+if [[ -d $VDEV ]]; then
+ log_must $RM -rf $VDIR
+fi
+if [[ -d $VDEV2 ]]; then
+ log_must $RM -rf $VDIR2
+fi
+log_must $MKDIR -p $VDIR $VDIR2
+log_must create_vdevs $VDEV $VDEV2
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/cachefile/Makefile b/tests/sys/cddl/zfs/tests/cachefile/Makefile
new file mode 100644
index 000000000000..09b55f1681aa
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cachefile/Makefile
@@ -0,0 +1,18 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cachefile
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= cachefile_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= cachefile_002_pos.ksh
+${PACKAGE}FILES+= cachefile_003_pos.ksh
+${PACKAGE}FILES+= cachefile_001_pos.ksh
+${PACKAGE}FILES+= cachefile.cfg
+${PACKAGE}FILES+= cachefile.kshlib
+${PACKAGE}FILES+= cachefile_004_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cachefile/cachefile.cfg b/tests/sys/cddl/zfs/tests/cachefile/cachefile.cfg
new file mode 100644
index 000000000000..a07e86061a00
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cachefile/cachefile.cfg
@@ -0,0 +1,33 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+if [[ $os_name == "FreeBSD" ]]; then
+ export CPATH="/boot/zfs/zpool.cache"
+else
+ export CPATH="/etc/zfs/zpool.cache"
+fi
+export CPATH1=$TMPDIR/cachefile.1.${TESTCASE_ID}
+export CPATH2=$TMPDIR/cachefile.2.${TESTCASE_ID}
diff --git a/tests/sys/cddl/zfs/tests/cachefile/cachefile.kshlib b/tests/sys/cddl/zfs/tests/cachefile/cachefile.kshlib
new file mode 100644
index 000000000000..4d8167793e4e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cachefile/cachefile.kshlib
@@ -0,0 +1,48 @@
+#!/usr/local/bin/ksh93 -p
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+
+#
+# A function to determine if a given pool name has an entry in cachefile
+# returns 1 if the pool is not in the cache, 0 otherwise.
+function pool_in_cache {
+
+ # checking for the pool name in the strings output of
+ # the given cachefile, default is /etc/zfs/zpool.cache
+ typeset cachefile=${2:-$CPATH}
+
+ if [[ -f $cachefile ]]; then
+ RESULT=$($STRINGS $cachefile | $GREP -w $1)
+ if [ -z "$RESULT" ]
+ then
+ return 1
+ fi
+ return 0
+ else
+ return 1
+ fi
+}
diff --git a/tests/sys/cddl/zfs/tests/cachefile/cachefile_001_pos.ksh b/tests/sys/cddl/zfs/tests/cachefile/cachefile_001_pos.ksh
new file mode 100644
index 000000000000..e8da32f697e9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cachefile/cachefile_001_pos.ksh
@@ -0,0 +1,102 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cachefile/cachefile.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: cachefile_001_pos
+#
+# DESCRIPTION:
+#
+# Creating a pool with "cachefile" set doesn't update zpool.cache
+#
+# STRATEGY:
+# 1. Create a pool with the cachefile property set
+# 2. Verify that the pool doesn't have an entry in zpool.cache
+# 3. Verify the cachefile property is set
+# 4. Create a pool without the cachefile property
+# 5. Verify the cachefile property isn't set
+# 6. Verify that zpool.cache contains an entry for the pool
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-09-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function cleanup
+{
+ typeset file
+
+ destroy_pool $TESTPOOL
+ for file in $CPATH1 $CPATH2 ; do
+ if [[ -f $file ]] ; then
+ log_must $RM $file
+ fi
+ done
+}
+
+verify_runnable "global"
+
+log_assert "Creating a pool with \"cachefile\" set doesn't update zpool.cache"
+log_onexit cleanup
+
+CPATHARG="-"
+set -A opts "none" "false" "none" \
+ "$CPATH" "true" "$CPATHARG" \
+ "$CPATH1" "true" "$CPATH1" \
+ "$CPATH2" "true" "$CPATH2"
+
+typeset -i i=0
+
+while (( i < ${#opts[*]} )); do
+ log_must $ZPOOL create -o cachefile=${opts[i]} $TESTPOOL $DISKS
+ case ${opts[((i+1))]} in
+ false) log_mustnot pool_in_cache $TESTPOOL
+ ;;
+ true) log_must pool_in_cache $TESTPOOL ${opts[i]}
+ ;;
+ esac
+
+ PROP=$(get_pool_prop cachefile $TESTPOOL)
+ if [[ $PROP != ${opts[((i+2))]} ]]; then
+ log_fail "cachefile property not set as expected. " \
+ "Expect: ${opts[((i+2))]}, Current: $PROP"
+ fi
+ log_must $ZPOOL destroy $TESTPOOL
+ (( i = i + 3 ))
+done
+
+log_pass "Creating a pool with \"cachefile\" set doesn't update zpool.cache"
+
diff --git a/tests/sys/cddl/zfs/tests/cachefile/cachefile_002_pos.ksh b/tests/sys/cddl/zfs/tests/cachefile/cachefile_002_pos.ksh
new file mode 100644
index 000000000000..43ef7f0ab0a0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cachefile/cachefile_002_pos.ksh
@@ -0,0 +1,87 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cachefile/cachefile.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: cachefile_002_pos
+#
+# DESCRIPTION:
+#
+# Importing a pool with "cachefile" set doesn't update zpool.cache
+#
+# STRATEGY:
+# 1. Create a pool with the cachefile property set
+# 2. Verify the pool doesn't have an entry in zpool.cache
+# 3. Export the pool
+# 4. Import the pool
+# 5. Verify the pool does have an entry in zpool.cache
+# 6. Export the pool
+# 7. Import the pool -o cachefile=<cachefile>
+# 8. Verify the pool doesn't have an entry in zpool.cache
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-09-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function cleanup
+{
+ destroy_pool $TESTPOOL
+}
+
+verify_runnable "global"
+
+log_assert "Importing a pool with \"cachefile\" set doesn't update zpool.cache"
+log_onexit cleanup
+
+log_must $ZPOOL create -o cachefile=none $TESTPOOL $DISKS
+typeset DEVICEDIR=$(get_device_dir $DISKS)
+log_mustnot pool_in_cache $TESTPOOL
+
+log_must $ZPOOL export $TESTPOOL
+log_must $ZPOOL import -d $DEVICEDIR $TESTPOOL
+log_must pool_in_cache $TESTPOOL
+
+log_must $ZPOOL export $TESTPOOL
+log_must $ZPOOL import -o cachefile=none -d $DEVICEDIR $TESTPOOL
+log_mustnot pool_in_cache $TESTPOOL
+
+log_must $ZPOOL export $TESTPOOL
+log_must $ZPOOL import -o cachefile=$CPATH -d $DEVICEDIR $TESTPOOL
+log_must pool_in_cache $TESTPOOL
+
+log_pass "Importing a pool with \"cachefile\" set doesn't update zpool.cache"
+
diff --git a/tests/sys/cddl/zfs/tests/cachefile/cachefile_003_pos.ksh b/tests/sys/cddl/zfs/tests/cachefile/cachefile_003_pos.ksh
new file mode 100644
index 000000000000..b4f25e186ad4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cachefile/cachefile_003_pos.ksh
@@ -0,0 +1,108 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cachefile/cachefile.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: cachefile_003_pos
+#
+# DESCRIPTION:
+#
+# Setting altroot=<path> and cachefile=$CPATH for zpool create is succeed
+#
+# STRATEGY:
+# 1. Attempt to create a pool with -o altroot=<path> -o cachefile=<value>
+# 2. Verify the command succeed
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-09-10)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+TESTDIR=/altdir.${TESTCASE_ID}
+
+function cleanup
+{
+ typeset file
+
+ destroy_pool $TESTPOOL
+
+ for file in $CPATH1 $CPATH2 ; do
+ if [[ -f $file ]] ; then
+ log_must $RM $file
+ fi
+ done
+
+ if [ -d $TESTDIR ]
+ then
+ $RMDIR $TESTDIR
+ fi
+}
+
+verify_runnable "global"
+
+log_assert "Setting altroot=path and cachefile=$CPATH for zpool create succeed."
+log_onexit cleanup
+
+typeset -i i=0
+
+CPATHARG="-"
+set -A opts "none" "none" \
+ "$CPATH" "$CPATHARG" \
+ "$CPATH1" "$CPATH1" \
+ "$CPATH2" "$CPATH2"
+
+
+while (( i < ${#opts[*]} )); do
+ log_must $ZPOOL create -o altroot=$TESTDIR -o cachefile=${opts[i]} \
+ $TESTPOOL $DISKS
+ if [[ ${opts[i]} != none ]]; then
+ log_must pool_in_cache $TESTPOOL ${opts[i]}
+ else
+ log_mustnot pool_in_cache $TESTPOOL
+ fi
+
+ PROP=$(get_pool_prop cachefile $TESTPOOL)
+ if [[ $PROP != ${opts[((i+1))]} ]]; then
+ log_fail "cachefile property not set as expected. " \
+ "Expect: ${opts[((i+1))]}, Current: $PROP"
+ fi
+ log_must $ZPOOL destroy -f $TESTPOOL
+ (( i = i + 2 ))
+done
+
+log_pass "Setting altroot=path and cachefile=$CPATH for zpool create succeed."
+
diff --git a/tests/sys/cddl/zfs/tests/cachefile/cachefile_004_pos.ksh b/tests/sys/cddl/zfs/tests/cachefile/cachefile_004_pos.ksh
new file mode 100644
index 000000000000..46b91ce7f5d8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cachefile/cachefile_004_pos.ksh
@@ -0,0 +1,131 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cachefile/cachefile.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: cachefile_004_pos
+#
+# DESCRIPTION:
+# Verify set, export and destroy when cachefile is set on pool.
+#
+# STRATEGY:
+# 1. Create two pools with one same cahcefile1.
+# 2. Set cachefile of the two pools to another same cachefile2.
+# 3. Verify cachefile1 not exist.
+# 4. Export the two pools.
+# 5. Verify cachefile2 not exist.
+# 6. Import the two pools and set cachefile to cachefile2.
+# 7. Destroy the two pools.
+# 8. Verify cachefile2 not exist.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-04-24)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ destroy_pool $TESTPOOL1
+ destroy_pool $TESTPOOL2
+
+ mntpnt=$(get_prop mountpoint $TESTPOOL)
+ typeset -i i=0
+ while ((i < 2)); do
+ if [[ -e $mntpnt/vdev$i ]]; then
+ log_must $RM -f $mntpnt/vdev$i
+ fi
+ ((i += 1))
+ done
+
+ destroy_pool $TESTPOOL
+
+ for file in $CPATH1 $CPATH2 ; do
+ if [[ -f $file ]] ; then
+ log_must $RM $file
+ fi
+ done
+}
+
+
+log_assert "Verify set, export and destroy when cachefile is set on pool."
+log_onexit cleanup
+
+log_must $ZPOOL create $TESTPOOL $DISKS
+
+mntpnt=$(get_prop mountpoint $TESTPOOL)
+typeset -i i=0
+while ((i < 2)); do
+ log_must create_vdevs $mntpnt/vdev$i
+ eval vdev$i=$mntpnt/vdev$i
+ ((i += 1))
+done
+
+log_must $ZPOOL create -o cachefile=$CPATH1 $TESTPOOL1 $vdev0
+log_must pool_in_cache $TESTPOOL1 $CPATH1
+log_must $ZPOOL create -o cachefile=$CPATH1 $TESTPOOL2 $vdev1
+log_must pool_in_cache $TESTPOOL2 $CPATH1
+
+log_must $ZPOOL set cachefile=$CPATH2 $TESTPOOL1
+log_must pool_in_cache $TESTPOOL1 $CPATH2
+log_must $ZPOOL set cachefile=$CPATH2 $TESTPOOL2
+log_must pool_in_cache $TESTPOOL2 $CPATH2
+if [[ -f $CPATH1 ]]; then
+ log_fail "Verify set when cachefile is set on pool."
+fi
+
+log_must $ZPOOL export $TESTPOOL1
+log_must $ZPOOL export $TESTPOOL2
+if [[ -f $CPATH2 ]]; then
+ log_fail "Verify export when cachefile is set on pool."
+fi
+
+log_must $ZPOOL import -d $mntpnt $TESTPOOL1
+log_must $ZPOOL set cachefile=$CPATH2 $TESTPOOL1
+log_must pool_in_cache $TESTPOOL1 $CPATH2
+log_must $ZPOOL import -d $mntpnt $TESTPOOL2
+log_must $ZPOOL set cachefile=$CPATH2 $TESTPOOL2
+log_must pool_in_cache $TESTPOOL2 $CPATH2
+
+log_must $ZPOOL destroy $TESTPOOL1
+log_must $ZPOOL destroy $TESTPOOL2
+if [[ -f $CPATH2 ]]; then
+ log_fail "Verify destroy when cachefile is set on pool."
+fi
+
+log_pass "Verify set, export and destroy when cachefile is set on pool."
+
diff --git a/tests/sys/cddl/zfs/tests/cachefile/cachefile_test.sh b/tests/sys/cddl/zfs/tests/cachefile/cachefile_test.sh
new file mode 100755
index 000000000000..af36d0f7c722
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cachefile/cachefile_test.sh
@@ -0,0 +1,102 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case cachefile_001_pos
+cachefile_001_pos_head()
+{
+ atf_set "descr" "Creating a pool with \cachefile\ set doesn't update zpool.cache"
+ atf_set "require.progs" "ksh93 zpool"
+}
+cachefile_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/cachefile.kshlib
+ . $(atf_get_srcdir)/cachefile.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/cachefile_001_pos.ksh || atf_fail "Testcase failed"
+}
+
+
+atf_test_case cachefile_002_pos
+cachefile_002_pos_head()
+{
+ atf_set "descr" "Importing a pool with \cachefile\ set doesn't update zpool.cache"
+ atf_set "require.progs" "ksh93 zpool"
+}
+cachefile_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/cachefile.kshlib
+ . $(atf_get_srcdir)/cachefile.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/cachefile_002_pos.ksh || atf_fail "Testcase failed"
+}
+
+
+atf_test_case cachefile_003_pos
+cachefile_003_pos_head()
+{
+ atf_set "descr" "Setting altroot=path and cachefile=$CPATH for zpool create succeed."
+ atf_set "require.progs" "ksh93 zpool"
+}
+cachefile_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/cachefile.kshlib
+ . $(atf_get_srcdir)/cachefile.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/cachefile_003_pos.ksh || atf_fail "Testcase failed"
+}
+
+
+atf_test_case cachefile_004_pos
+cachefile_004_pos_head()
+{
+ atf_set "descr" "Verify set, export and destroy when cachefile is set on pool."
+ atf_set "require.progs" "ksh93 zpool"
+}
+cachefile_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/cachefile.kshlib
+ . $(atf_get_srcdir)/cachefile.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/cachefile_004_pos.ksh || atf_fail "Testcase failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case cachefile_001_pos
+ atf_add_test_case cachefile_002_pos
+ atf_add_test_case cachefile_003_pos
+ atf_add_test_case cachefile_004_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/clean_mirror/Makefile b/tests/sys/cddl/zfs/tests/clean_mirror/Makefile
new file mode 100644
index 000000000000..881d6cdedb7a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/clean_mirror/Makefile
@@ -0,0 +1,20 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/clean_mirror
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= clean_mirror_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= clean_mirror_001_pos.ksh
+${PACKAGE}FILES+= clean_mirror_002_pos.ksh
+${PACKAGE}FILES+= clean_mirror_003_pos.ksh
+${PACKAGE}FILES+= clean_mirror_004_pos.ksh
+${PACKAGE}FILES+= clean_mirror_common.kshlib
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= default.cfg
+${PACKAGE}FILES+= setup.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/clean_mirror/clean_mirror_001_pos.ksh b/tests/sys/cddl/zfs/tests/clean_mirror/clean_mirror_001_pos.ksh
new file mode 100644
index 000000000000..afc55b192603
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/clean_mirror/clean_mirror_001_pos.ksh
@@ -0,0 +1,62 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/clean_mirror/clean_mirror_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: clean_mirror_001_pos
+#
+# DESCRIPTION:
+# The primary side of a zpool mirror can be zeroed without causing damage
+# to the data in the pool
+#
+# STRATEGY:
+# 1) Write several files to the ZFS filesystem mirror
+# 2) dd from /dev/zero over the primary side of the mirror
+# 3) verify that all the file contents are unchanged on the file system
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "The primary side of a zpool mirror may be completely wiped" \
+ "without affecting the content of the pool"
+
+overwrite_verify_mirror $SIDE_PRIMARY /dev/zero
+
+log_pass "The overwrite had no effect on the data"
diff --git a/tests/sys/cddl/zfs/tests/clean_mirror/clean_mirror_002_pos.ksh b/tests/sys/cddl/zfs/tests/clean_mirror/clean_mirror_002_pos.ksh
new file mode 100644
index 000000000000..7fad4377cffb
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/clean_mirror/clean_mirror_002_pos.ksh
@@ -0,0 +1,62 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/clean_mirror/clean_mirror_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: clean_mirror_002_pos
+#
+# DESCRIPTION:
+# The secondary side of a zpool mirror can be zeroed without causing damage
+# to the data in the pool
+#
+# STRATEGY:
+# 1) Write several files to the ZFS filesystem in the mirrored pool
+# 2) dd from /dev/zero over the secondary side of the mirror
+# 3) verify that all the file contents are unchanged on the file system
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "The primary side of a zpool mirror may be completely wiped" \
+ "without affecting the content of the pool"
+
+overwrite_verify_mirror $SIDE_SECONDARY /dev/zero
+
+log_pass "The overwrite had no effect on the data"
diff --git a/tests/sys/cddl/zfs/tests/clean_mirror/clean_mirror_003_pos.ksh b/tests/sys/cddl/zfs/tests/clean_mirror/clean_mirror_003_pos.ksh
new file mode 100644
index 000000000000..cf74a978b3a7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/clean_mirror/clean_mirror_003_pos.ksh
@@ -0,0 +1,62 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/clean_mirror/clean_mirror_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: clean_mirror_003_pos
+#
+# DESCRIPTION:
+# The primary side of a zpool mirror can be mangled without causing damage
+# to the data in the pool
+#
+# STRATEGY:
+# 1) Write several files to the ZFS filesystem mirror
+# 2) dd from /dev/urandom over the primary side of the mirror
+# 3) verify that all the file contents are unchanged on the file system
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "The primary side of a zpool mirror may be completely mangled" \
+ "without affecting the content of the pool"
+
+overwrite_verify_mirror $SIDE_PRIMARY /dev/urandom
+
+log_pass "The overwrite did not have any effect on the data"
diff --git a/tests/sys/cddl/zfs/tests/clean_mirror/clean_mirror_004_pos.ksh b/tests/sys/cddl/zfs/tests/clean_mirror/clean_mirror_004_pos.ksh
new file mode 100644
index 000000000000..a3469b0fd401
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/clean_mirror/clean_mirror_004_pos.ksh
@@ -0,0 +1,62 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/clean_mirror/clean_mirror_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: clean_mirror_004_pos
+#
+# DESCRIPTION:
+# The secondary side of a zpool mirror can be mangled without causing damage
+# to the data in the pool
+#
+# STRATEGY:
+# 1) Write several files to the ZFS filesystem in the mirrored pool
+# 2) dd from /dev/urandom over the secondary side of the mirror
+# 3) verify that all the file contents are unchanged on the file system
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "The primary side of a zpool mirror may be completely mangled" \
+ "without affecting the content of the pool"
+
+overwrite_verify_mirror $SIDE_SECONDARY /dev/urandom
+
+log_pass "The overwrite had no effect on the data"
diff --git a/tests/sys/cddl/zfs/tests/clean_mirror/clean_mirror_common.kshlib b/tests/sys/cddl/zfs/tests/clean_mirror/clean_mirror_common.kshlib
new file mode 100644
index 000000000000..4f6676f91648
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/clean_mirror/clean_mirror_common.kshlib
@@ -0,0 +1,74 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+# Most of the code related to the clearing of mirrors is duplicated in all
+# the test cases below this directory, barring a few minor changes
+# involving the device to be affected and the 'object' to use to mangle
+# the contents of the mirror.
+# This code is sourced into each of these test cases.
+
+function overwrite_verify_mirror
+{
+ typeset AFFECTED_DEVICE=$1
+ typeset OVERWRITING_DEVICE=$2
+
+ typeset atfile=0
+ set -A cksums
+ set -A newcksums
+
+ populate_dir $TESTDIR/file $FILE_COUNT $WRITE_COUNT $BLOCKSZ 0
+ while (( atfile < FILE_COUNT )); do
+ cksums[$atfile]=$($CKSUM ${TESTDIR}/file.${atfile})
+ (( atfile = atfile + 1 ))
+ done
+
+ # unmount and export before dd
+ log_must $UMOUNT $TESTDIR
+ log_must $ZPOOL export $TESTPOOL
+
+ # dd the primary side of the mirror
+ log_must $DD if=$OVERWRITING_DEVICE of=$(bsddevmap $AFFECTED_DEVICE) \
+ seek=8 bs=$DD_BLOCK count=$(( DD_COUNT - 8 )) conv=notrunc
+
+ # now remount
+ log_must $ZPOOL import $TESTPOOL
+
+ atfile=0
+ typeset -i failedcount=0
+ while (( atfile < FILE_COUNT )); do
+ newcksum=$($CKSUM $TESTDIR/file.${atfile})
+ if [[ $newcksum != ${cksums[$atfile]} ]]; then
+ (( failedcount = failedcount + 1 ))
+ fi
+ $RM -f ${files[$atfile]}
+ (( atfile = atfile + 1 ))
+ done
+
+ if (( $failedcount > 0 )); then
+ log_fail "of the $FILE_COUNT files $failedcount did not " \
+ "have the same checksum before and after."
+ fi
+}
diff --git a/tests/sys/cddl/zfs/tests/clean_mirror/clean_mirror_test.sh b/tests/sys/cddl/zfs/tests/clean_mirror/clean_mirror_test.sh
new file mode 100755
index 000000000000..e6df08ab27e7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/clean_mirror/clean_mirror_test.sh
@@ -0,0 +1,131 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2018 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case clean_mirror_001_pos cleanup
+clean_mirror_001_pos_head()
+{
+ atf_set "descr" "The primary side of a zpool mirror may be completely wipedwithout affecting the content of the pool"
+}
+clean_mirror_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/clean_mirror_common.kshlib
+ . $(atf_get_srcdir)/default.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/clean_mirror_001_pos.ksh || atf_fail "Testcase failed"
+}
+clean_mirror_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/clean_mirror_common.kshlib
+ . $(atf_get_srcdir)/default.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case clean_mirror_002_pos cleanup
+clean_mirror_002_pos_head()
+{
+ atf_set "descr" "The primary side of a zpool mirror may be completely wipedwithout affecting the content of the pool"
+}
+clean_mirror_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/clean_mirror_common.kshlib
+ . $(atf_get_srcdir)/default.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/clean_mirror_002_pos.ksh || atf_fail "Testcase failed"
+}
+clean_mirror_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/clean_mirror_common.kshlib
+ . $(atf_get_srcdir)/default.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case clean_mirror_003_pos cleanup
+clean_mirror_003_pos_head()
+{
+ atf_set "descr" "The primary side of a zpool mirror may be completely mangledwithout affecting the content of the pool"
+}
+clean_mirror_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/clean_mirror_common.kshlib
+ . $(atf_get_srcdir)/default.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/clean_mirror_003_pos.ksh || atf_fail "Testcase failed"
+}
+clean_mirror_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/clean_mirror_common.kshlib
+ . $(atf_get_srcdir)/default.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case clean_mirror_004_pos cleanup
+clean_mirror_004_pos_head()
+{
+ atf_set "descr" "The primary side of a zpool mirror may be completely mangledwithout affecting the content of the pool"
+}
+clean_mirror_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/clean_mirror_common.kshlib
+ . $(atf_get_srcdir)/default.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/clean_mirror_004_pos.ksh || atf_fail "Testcase failed"
+}
+clean_mirror_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/clean_mirror_common.kshlib
+ . $(atf_get_srcdir)/default.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case clean_mirror_001_pos
+ atf_add_test_case clean_mirror_002_pos
+ atf_add_test_case clean_mirror_003_pos
+ atf_add_test_case clean_mirror_004_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/clean_mirror/cleanup.ksh b/tests/sys/cddl/zfs/tests/clean_mirror/cleanup.ksh
new file mode 100644
index 000000000000..252e3b9bfa66
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/clean_mirror/cleanup.ksh
@@ -0,0 +1,42 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+$DF -t zfs -h | $GREP "$TESTFS " >/dev/null
+[[ $? == 0 ]] && log_must $ZFS umount -f $TESTDIR
+destroy_pool $TESTPOOL
+
+# Erase the partition table that we made
+if [[ -n $SINGLE_DISK ]]; then
+ log_must cleanup_devices $MIRROR_PRIMARY
+else
+ log_must cleanup_devices $MIRROR_PRIMARY $MIRROR_SECONDARY
+fi
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/clean_mirror/default.cfg b/tests/sys/cddl/zfs/tests/clean_mirror/default.cfg
new file mode 100644
index 000000000000..8bc56a556bc0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/clean_mirror/default.cfg
@@ -0,0 +1,52 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+typeset -i NUMBER_OF_DISKS=0
+for i in $DISKS; do
+ [[ -n $MIRROR_PRIMARY ]] && MIRROR_SECONDARY=$i
+ [[ -z $MIRROR_PRIMARY ]] && MIRROR_PRIMARY=$i
+done
+
+if [[ -z $MIRROR_SECONDARY ]]; then
+ # We need to repartition the single disk to two slices
+ SINGLE_DISK=$MIRROR_PRIMARY
+ MIRROR_SECONDARY=$MIRROR_PRIMARY
+ SIDE_PRIMARY=${SINGLE_DISK}p1
+ SIDE_SECONDARY=${SINGLE_DISK}p2
+else
+ SIDE_PRIMARY=${MIRROR_PRIMARY}p1
+ SIDE_SECONDARY=${MIRROR_SECONDARY}p1
+fi
+
+export MIRROR_PRIMARY MIRROR_SECONDARY SINGLE_DISK SIDE_PRIMARY SIDE_SECONDARY
+
+export FILE_COUNT=10
+export BLOCKSZ=131072
+export WRITE_COUNT=8
+export FILE_SIZE=$(( BLOCKSZ * WRITE_COUNT ))
+export MIRROR_SIZE=70
+export DD_BLOCK=$(( 64 * 1024 ))
+export DD_COUNT=$(( MIRROR_SIZE * 1024 * 1024 / DD_BLOCK ))
diff --git a/tests/sys/cddl/zfs/tests/clean_mirror/setup.ksh b/tests/sys/cddl/zfs/tests/clean_mirror/setup.ksh
new file mode 100644
index 000000000000..f3df3289a151
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/clean_mirror/setup.ksh
@@ -0,0 +1,42 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+if [[ -n $SINGLE_DISK ]]; then
+ log_note "Partitioning a single disk ($SINGLE_DISK)"
+else
+ log_note "Partitioning disks ($MIRROR_PRIMARY $MIRROR_SECONDARY)"
+fi
+wipe_partition_table ${SINGLE_DISK} ${MIRROR_PRIMARY} ${MIRROR_SECONDARY}
+log_must set_partition ${SIDE_PRIMARY##*p} "" ${MIRROR_SIZE}m $MIRROR_PRIMARY
+log_must set_partition ${SIDE_SECONDARY##*p} "" ${MIRROR_SIZE}m $MIRROR_SECONDARY
+
+default_mirror_setup $SIDE_PRIMARY $SIDE_SECONDARY
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/cli_root/Makefile b/tests/sys/cddl/zfs/tests/cli_root/Makefile
new file mode 100644
index 000000000000..05b2f480e006
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/Makefile
@@ -0,0 +1,58 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root
+FILESDIR=${TESTSDIR}
+
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= cli.cfg
+${PACKAGE}FILES+= cli_common.kshlib
+${PACKAGE}FILES+= setup.ksh
+
+TESTS_SUBDIRS+= zdb
+
+TESTS_SUBDIRS+= zfs
+TESTS_SUBDIRS+= zfs_clone
+TESTS_SUBDIRS+= zfs_copies
+TESTS_SUBDIRS+= zfs_create
+TESTS_SUBDIRS+= zfs_destroy
+TESTS_SUBDIRS+= zfs_diff
+TESTS_SUBDIRS+= zfs_get
+TESTS_SUBDIRS+= zfs_inherit
+TESTS_SUBDIRS+= zfs_mount
+TESTS_SUBDIRS+= zfs_promote
+TESTS_SUBDIRS+= zfs_property
+TESTS_SUBDIRS+= zfs_receive
+TESTS_SUBDIRS+= zfs_rename
+TESTS_SUBDIRS+= zfs_reservation
+TESTS_SUBDIRS+= zfs_rollback
+TESTS_SUBDIRS+= zfs_send
+TESTS_SUBDIRS+= zfs_set
+TESTS_SUBDIRS+= zfs_share
+TESTS_SUBDIRS+= zfs_snapshot
+TESTS_SUBDIRS+= zfs_unshare
+TESTS_SUBDIRS+= zfs_unmount
+TESTS_SUBDIRS+= zfs_upgrade
+
+TESTS_SUBDIRS+= zpool
+TESTS_SUBDIRS+= zpool_add
+TESTS_SUBDIRS+= zpool_attach
+TESTS_SUBDIRS+= zpool_clear
+TESTS_SUBDIRS+= zpool_create
+TESTS_SUBDIRS+= zpool_destroy
+TESTS_SUBDIRS+= zpool_detach
+TESTS_SUBDIRS+= zpool_expand
+TESTS_SUBDIRS+= zpool_export
+TESTS_SUBDIRS+= zpool_get
+TESTS_SUBDIRS+= zpool_history
+TESTS_SUBDIRS+= zpool_import
+TESTS_SUBDIRS+= zpool_offline
+TESTS_SUBDIRS+= zpool_online
+TESTS_SUBDIRS+= zpool_remove
+TESTS_SUBDIRS+= zpool_replace
+TESTS_SUBDIRS+= zpool_set
+TESTS_SUBDIRS+= zpool_scrub
+TESTS_SUBDIRS+= zpool_status
+TESTS_SUBDIRS+= zpool_upgrade
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/cleanup.ksh
new file mode 100644
index 000000000000..713cb16bb31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/cli.cfg b/tests/sys/cddl/zfs/tests/cli_root/cli.cfg
new file mode 100644
index 000000000000..9f3767913ab6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/cli.cfg
@@ -0,0 +1,50 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+export ZFSROOT=
+
+export TESTSNAP=testsnap${TESTCASE_ID}
+export TESTSNAP1=testsnap1${TESTCASE_ID}
+export TESTSNAP2=testsnap2${TESTCASE_ID}
+export TESTCLONE=testclone${TESTCASE_ID}
+export TESTCLONE1=testclone1${TESTCASE_ID}
+export TESTCLONE2=testclone2${TESTCASE_ID}
+export TESTCLCT=testclct${TESTCASE_ID}
+export TESTCTR1=testctr1${TESTCASE_ID}
+export TESTCTR2=testctr2${TESTCASE_ID}
+export TESTVOL=testvol${TESTCASE_ID}
+export TESTVOL1=testvol1${TESTCASE_ID}
+export TESTVOL2=testvol2${TESTCASE_ID}
+export TESTFILE0=testfile0.${TESTCASE_ID}
+export TESTFILE1=testfile1.${TESTCASE_ID}
+export TESTFILE2=testfile2.${TESTCASE_ID}
+
+export LONGPNAME="poolname50charslong_012345678901234567890123456789"
+export LONGFSNAME="fsysname50charslong_012345678901234567890123456789"
+
+export VOLSIZE=150m
+export BIGVOLSIZE=1eb
diff --git a/tests/sys/cddl/zfs/tests/cli_root/cli_common.kshlib b/tests/sys/cddl/zfs/tests/cli_root/cli_common.kshlib
new file mode 100644
index 000000000000..7615e094e4f8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/cli_common.kshlib
@@ -0,0 +1,86 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#
+# Get the checksum and size of the file.
+#
+function get_cksum # <file path>
+{
+ return $($CKSUM $1 | $AWK '{print $1 $2}')
+}
+
+#
+# Compare the check sum of target files with the original file
+#
+
+function compare_cksum #<orig_data> <target_data1>...<target_datan>
+{
+ typeset orig_data=$1
+ typeset orig_sum=$(get_cksum $orig_data)
+ typeset target_sum=""
+ typeset bad_data_list=""
+ typeset -i bad_count=0
+
+ shift
+ for data in $@; do
+ if [[ ! -e $data ]]; then
+ bad_data_list="$bad_data_list $data"
+ (( bad_count +=1 ))
+ continue
+ fi
+
+ target_sum=$(get_cksum $data)
+ if [[ $target_sum != $orig_sum ]]; then
+ bad_data_list="$bad_data_list $data"
+ (( bad_count +=1 ))
+ fi
+ done
+
+ [[ $bad_data_list != "" ]] && \
+ log_fail "Data corruptions appear during send->receive." \
+ "There are total $bad_count corruptions. They are:\n"\
+ "$bad_data_list"
+}
+
+#
+# Check the received dataset exists or not
+#
+function receive_check #<dataset1>...<datasetn>
+{
+ typeset bad_rst_tgts=""
+
+ for dataset in $@; do
+ ! datasetexists $dataset && \
+ bad_rst_tgts="$bad_rst_tgts $dataset"
+ done
+
+ if [[ $bad_rst_tgts != "" ]]; then
+ log_fail "Restoring fails. The specified datasets"\
+ "$bad_rst_tgts are not being received."
+ fi
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/setup.ksh
new file mode 100644
index 000000000000..326bc555f71b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zdb/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zdb/Makefile
new file mode 100644
index 000000000000..7bf850245a84
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zdb/Makefile
@@ -0,0 +1,16 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zdb
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zdb_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zdb_001_neg.ksh
+${PACKAGE}FILES+= zdb.cfg
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= setup.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zdb/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zdb/cleanup.ksh
new file mode 100644
index 000000000000..713cb16bb31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zdb/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zdb/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zdb/setup.ksh
new file mode 100644
index 000000000000..326bc555f71b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zdb/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zdb/zdb.cfg b/tests/sys/cddl/zfs/tests/cli_root/zdb/zdb.cfg
new file mode 100644
index 000000000000..99d93c9d9c72
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zdb/zdb.cfg
@@ -0,0 +1,27 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zdb/zdb_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zdb/zdb_001_neg.ksh
new file mode 100644
index 000000000000..f34b595de27c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zdb/zdb_001_neg.ksh
@@ -0,0 +1,81 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zdb_001_neg
+#
+# DESCRIPTION:
+# A badly formed parameter passed to zdb(1) should
+# return an error.
+#
+# STRATEGY:
+# 1. Create an array containg bad zdb parameters.
+# 2. For each element, execute the sub-command.
+# 3. Verify it returns an error.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-09-28)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+set -A args "create" "add" "destroy" "import fakepool" \
+ "export fakepool" "create fakepool" "add fakepool" \
+ "create mirror" "create raidz" \
+ "create mirror fakepool" "create raidz fakepool" \
+ "create raidz1 fakepool" "create raidz2 fakepool" \
+ "create fakepool mirror" "create fakepool raidz" \
+ "create fakepool raidz1" "create fakepool raidz2" \
+ "add fakepool mirror" "add fakepool raidz" \
+ "add fakepool raidz1" "add fakepool raidz2" \
+ "add mirror fakepool" "add raidz fakepool" \
+ "add raidz1 fakepool" "add raidz2 fakepool" \
+ "setvprop" "blah blah" "-%" "--?" "-*" "-=" \
+ "-a" "-f" "-g" "-h" "-j" "-m" "-n" "-p" "-p /tmp" \
+ "-r" "-t" "-w" "-x" "-y" "-z" \
+ "-D" "-E" "-G" "-H" "-I" "-J" "-K" "-M" \
+ "-N" "-Q" "-T" "-W"
+
+log_assert "Execute zdb using invalid parameters."
+
+typeset -i i=0
+while [[ $i -lt ${#args[*]} ]]; do
+ log_mustnot $ZDB ${args[i]}
+
+ ((i = i + 1))
+done
+
+log_pass "Badly formed zdb parameters fail as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zdb/zdb_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zdb/zdb_test.sh
new file mode 100755
index 000000000000..a47ab3400be0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zdb/zdb_test.sh
@@ -0,0 +1,55 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zdb_001_neg cleanup
+zdb_001_neg_head()
+{
+ atf_set "descr" "Execute zdb using invalid parameters."
+ atf_set "require.progs" "ksh93 zdb"
+}
+zdb_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zdb.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zdb_001_neg.ksh || atf_fail "Testcase failed"
+}
+zdb_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zdb.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zdb_001_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zfs/Makefile
new file mode 100644
index 000000000000..e123a67e482b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs/Makefile
@@ -0,0 +1,18 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zfs
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zfs_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zfs_malformed.cfg
+${PACKAGE}FILES+= zfs_001_neg.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zfs_003_neg.ksh
+${PACKAGE}FILES+= zfs_002_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs/cleanup.ksh
new file mode 100644
index 000000000000..713cb16bb31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs/setup.ksh
new file mode 100644
index 000000000000..326bc555f71b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs/zfs_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs/zfs_001_neg.ksh
new file mode 100644
index 000000000000..a17e162af8d5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs/zfs_001_neg.ksh
@@ -0,0 +1,85 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_001_neg
+#
+# DESCRIPTION:
+# Try each zfs(1) sub-command without parameters to make sure
+# it returns an error.
+#
+# STRATEGY:
+# 1. Create an array of parameters
+# 2. For each parameter in the array, execute the sub-command
+# 3. Verify an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+
+set -A args "" "create" "create -s" "create -V" "create -s -V" \
+ "destroy" "destroy -f" "destroy -r" "destroy -R" "destroy -rRf" \
+ "snapshot" "snapshot -r" \
+ "rollback" "rollback -r" "rollback -R" "rollback -f" "rollback -rRf" \
+ "clone" "clone -p" "promote" "rename" "rename -p" "rename -r" "list blah" \
+ "set" "get" "get -rHp" "get -o" "get -s" \
+ "inherit" "inherit -r" "quota=" \
+ "set reservation=" "set atime=" "set checksum=" "set compression=" \
+ "set type=" "set creation=" "set used=" "set available=" "set referenced=" \
+ "set compressratio=" "set mounted=" "set origin=" "set quota=" \
+ "set reservation=" "set volsize=" " set volblocksize=" "set recordsize=" \
+ "set mountpoint=" "set devices=" "set exec=" "set setuid=" "set readonly=" \
+ "set zoned=" "set snapdir=" "set aclmode=" "set aclinherit=" \
+ "set quota=blah" "set reservation=blah" "set atime=blah" "set checksum=blah" \
+ "set compression=blah" \
+ "upgrade blah" "mount blah" "mount -o" \
+ "umount blah" "unmount" "unmount blah" "unmount -f" \
+ "share" "unshare" "send" "send -i" "receive" "receive -d" "receive -vnF" \
+ "recv" "recv -d" "recv -vnF" "allow" "unallow" \
+ "blah blah" "-%" "--" "--?" "-*" "-="
+
+log_assert "Badly-formed zfs sub-command should return an error."
+
+typeset -i i=0
+while (( $i < ${#args[*]} )); do
+ log_mustnot $ZFS ${args[i]}
+ ((i = i + 1))
+done
+
+log_pass "Badly formed zfs sub-commands fail as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs/zfs_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs/zfs_002_pos.ksh
new file mode 100644
index 000000000000..0a003d2e331c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs/zfs_002_pos.ksh
@@ -0,0 +1,122 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/userquota/userquota_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_002_pos
+#
+# DESCRIPTION:
+# With ZFS_ABORT set, all zfs commands should be able to abort and generate a core file.
+#
+# STRATEGY:
+# 1. Create an array of zfs command
+# 2. Execute each command in the array
+# 3. Verify the command aborts and generate a core file
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-29)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ unset ZFS_ABORT
+
+ if [[ -d $corepath ]]; then
+ $RM -rf $corepath
+ fi
+ for ds in $fs1 $fs $ctr; do
+ if datasetexists $ds; then
+ log_must $ZFS destroy -rRf $ds
+ fi
+ done
+}
+
+log_assert "With ZFS_ABORT set, all zfs commands can abort and generate a core file."
+log_onexit cleanup
+
+#preparation work for testing
+corepath=$TESTDIR/core
+if [[ -d $corepath ]]; then
+ $RM -rf $corepath
+fi
+log_must $MKDIR $corepath
+
+ctr=$TESTPOOL/$TESTCTR
+log_must $ZFS create $ctr
+
+fs=$ctr/$TESTFS
+fs1=$ctr/$TESTFS1
+snap=$fs@$TESTSNAP
+clone=$ctr/$TESTCLONE
+streamf=$corepath/s.${TESTCASE_ID}
+
+set -A cmds "create $fs" "list $fs" "snapshot $snap" "set snapdir=hidden $fs" \
+ "get snapdir $fs" "rollback $snap" "inherit snapdir $fs" \
+ "rename $fs $fs-new" "rename $fs-new $fs" "unmount $fs" \
+ "mount $fs" "share $fs" "unshare $fs" "send $snap \>$streamf" \
+ "receive $fs1 \<$streamf" "clone $snap $clone" "promote $clone" \
+ "promote $fs" "destroy -rRf $fs"
+
+set -A badparams "" "create" "destroy" "snapshot" "rollback" "clone" "promote" "rename" \
+ "list -*" "set" "get -*" "inherit" "mount -A" "unmount" "share" \
+ "unshare" "send" "receive"
+
+if ! is_userquota_supported; then
+ typeset -i i=${cmds[#]}
+ cmds[i]="allow everyone snapshot $fs"
+ cmds[((i+1))]="unallow everyone snapshot $fs"
+
+ i=${badparams[#]}
+ badparams[i]="allow"
+ badparams[((i+1))]="unallow"
+fi
+
+
+log_must $COREADM -p ${corepath}/core.%f
+log_must export ZFS_ABORT=yes
+
+for subcmd in "${cmds[@]}" "${badparams[@]}"; do
+ log_mustnot $ZFS $subcmd
+ corefile=${corepath}/core.zfs
+ if [[ ! -e $corefile ]]; then
+ log_fail "$ZFS $subcmd cannot generate core file with ZFS_ABORT set."
+ fi
+ log_must $RM -f $corefile
+done
+
+log_pass "With ZFS_ABORT set, zfs command can abort and generate core file as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs/zfs_003_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs/zfs_003_neg.ksh
new file mode 100644
index 000000000000..ea097d7ee3d1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs/zfs_003_neg.ksh
@@ -0,0 +1,72 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_003_neg
+#
+# DESCRIPTION:
+# zfs command will failed with unexpected scenarios:
+# (1) ZFS_DEV cannot be opened
+# (2) MNTTAB cannot be opened
+#
+# STRATEGY:
+# 1. Create an array of zfs command
+# 2. Execute each command in the array
+# 3. Verify the command aborts and generate a core file
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-29)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "zfs fails with unexpected scenarios."
+
+#verify zfs failed if ZFS_DEV cannot be opened
+ZFS_DEV=/dev/zfs
+MNTTAB=/etc/mnttab
+
+for file in $ZFS_DEV $MNTTAB; do
+ if [[ -e $file ]]; then
+ $MV $file ${file}.bak
+ fi
+ for cmd in "" "list" "get all" "mount"; do
+ log_mustnot eval "$ZFS $cmd >/dev/null 2>&1"
+ done
+ $MV ${file}.bak $file
+done
+
+log_pass "zfs fails with unexpected scenarios as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs/zfs_malformed.cfg b/tests/sys/cddl/zfs/tests/cli_root/zfs/zfs_malformed.cfg
new file mode 100644
index 000000000000..99d93c9d9c72
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs/zfs_malformed.cfg
@@ -0,0 +1,27 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs/zfs_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zfs/zfs_test.sh
new file mode 100755
index 000000000000..2e2bf28ea675
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs/zfs_test.sh
@@ -0,0 +1,106 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zfs_001_neg cleanup
+zfs_001_neg_head()
+{
+ atf_set "descr" "Badly-formed zfs sub-command should return an error."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_malformed.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_001_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_malformed.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_002_pos cleanup
+zfs_002_pos_head()
+{
+ atf_set "descr" "With ZFS_ABORT set, all zfs commands can abort and generate a core file."
+ atf_set "require.progs" "ksh93 zfs coreadm"
+}
+zfs_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_malformed.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_002_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_malformed.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_003_neg cleanup
+zfs_003_neg_head()
+{
+ atf_set "descr" "zfs fails with unexpected scenarios."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_003_neg_body()
+{
+ atf_skip "FreeBSD does not allow /dev/zfs to be renamed"
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_malformed.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_003_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_003_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_malformed.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zfs_001_neg
+ atf_add_test_case zfs_002_pos
+ atf_add_test_case zfs_003_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/Makefile
new file mode 100644
index 000000000000..017e2c312ac6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/Makefile
@@ -0,0 +1,24 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zfs_clone
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zfs_clone_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zfs_clone_005_pos.ksh
+${PACKAGE}FILES+= zfs_clone_008_neg.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zfs_clone_004_pos.ksh
+${PACKAGE}FILES+= zfs_clone_001_neg.ksh
+${PACKAGE}FILES+= zfs_clone_009_neg.ksh
+${PACKAGE}FILES+= zfs_clone_006_pos.ksh
+${PACKAGE}FILES+= zfs_clone_002_pos.ksh
+${PACKAGE}FILES+= zfs_clone.cfg
+${PACKAGE}FILES+= zfs_clone_007_pos.ksh
+${PACKAGE}FILES+= zfs_clone_003_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/cleanup.ksh
new file mode 100644
index 000000000000..9af80e992e94
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_container_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/setup.ksh
new file mode 100644
index 000000000000..db5cb7d70c9b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_container_volume_setup ${DISK}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone.cfg b/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone.cfg
new file mode 100644
index 000000000000..0a38cb9f6ea1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone.cfg
@@ -0,0 +1,30 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
+
+export SNAPFS="$TESTPOOL/$TESTFS@$TESTSNAP"
+export SNAPFS1="$TESTPOOL/$TESTVOL@$TESTSNAP"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_001_neg.ksh
new file mode 100644
index 000000000000..cf81f667a2d0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_001_neg.ksh
@@ -0,0 +1,138 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_clone_001_neg
+#
+# DESCRIPTION:
+# 'zfs clone' should fail with inapplicable scenarios, including:
+# * Null arguments
+# * non-existant snapshots.
+# * invalid characters in ZFS namesapec
+# * Leading slash in the target clone name
+# * The argument contains an empty component.
+# * The pool specified in the target doesn't exist.
+# * The parent dataset of the target doesn't exist.
+# * The argument refer to a pool, not dataset.
+# * The target clone already exists.
+# * Null target clone argument.
+# * Too many arguments.
+#
+# STRATEGY:
+# 1. Create an array of parameters
+# 2. For each parameter in the array, execute the sub-command
+# 3. Verify an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-25)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+typeset target1=$TESTPOOL/$TESTFS1
+typeset target2=$TESTPOOL/$TESTCTR1/$TESTFS1
+typeset targets="$target1 $target2 $NONEXISTPOOLNAME/$TESTFS"
+
+set -A args "" \
+ "$TESTPOOL/$TESTFS@blah $target1" "$TESTPOOL/$TESTVOL@blah $target1" \
+ "$TESTPOOL/$TESTFS@blah* $target1" "$TESTPOOL/$TESTVOL@blah* $target1" \
+ "$SNAPFS $target1*" "$SNAPFS1 $target1*" \
+ "$SNAPFS /$target1" "$SNAPFS1 /$target1" \
+ "$SNAPFS $TESTPOOL//$TESTFS1" "$SNAPFS1 $TESTPOOL//$TESTFS1" \
+ "$SNAPFS $NONEXISTPOOLNAME/$TESTFS" "$SNAPFS1 $NONEXISTPOOLNAME/$TESTFS" \
+ "$SNAPFS" "$SNAPFS1" \
+ "$SNAPFS $target1 $target2" "$SNAPFS1 $target1 $target2"
+typeset -i argsnum=${#args[*]}
+typeset -i j=0
+while (( j < argsnum )); do
+ args[((argsnum+j))]="-p ${args[j]}"
+ ((j = j + 1))
+done
+
+set -A moreargs "$SNAPFS $target2" "$SNAPFS1 $target2" \
+ "$SNAPFS $TESTPOOL" "$SNAPFS1 $TESTPOOL" \
+ "$SNAPFS $TESTPOOL/$TESTCTR" "$SNAPFS $TESTPOOL/$TESTFS" \
+ "$SNAPFS1 $TESTPOOL/$TESTCTR" "$SNAPFS1 $TESTPOOL/$TESTFS"
+
+set -A args ${args[*]} ${moreargs[*]}
+
+function setup_all
+{
+ log_note "Create snapshots and mount them..."
+
+ for snap in $SNAPFS $SNAPFS1 ; do
+ if ! snapexists $snap ; then
+ log_must $ZFS snapshot $snap
+ fi
+ done
+
+ return 0
+}
+
+function cleanup_all
+{
+ typeset -i i=0
+
+ for fs in $targets; do
+
+ datasetexists $fs && \
+ log_must $ZFS destroy -f $fs
+
+ (( i = i + 1 ))
+ done
+
+ for snap in $SNAPFS $SNAPFS1 ; do
+ snapexists $snap && \
+ log_must $ZFS destroy -Rf $snap
+ done
+
+ return 0
+}
+
+log_assert "Badly-formed 'zfs clone' with inapplicable scenarios" \
+ "should return an error."
+log_onexit cleanup_all
+
+setup_all
+
+typeset -i i=0
+while (( i < ${#args[*]} )); do
+ log_mustnot $ZFS clone ${args[i]}
+ ((i = i + 1))
+done
+
+log_pass "Badly formed 'zfs clone' with inapplicable scenarios" \
+ "fail as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_002_pos.ksh
new file mode 100644
index 000000000000..f15de7824030
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_002_pos.ksh
@@ -0,0 +1,101 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_clone_002_pos
+#
+# DESCRIPTION:
+# 'zfs clone -p' should work as expected
+#
+# STRATEGY:
+# 1. prepare snapshots
+# 2. make sure without -p option, 'zfs clone' will fail
+# 3. with -p option, the clone can be created
+# 4. run 'zfs clone -p' again, the exit code should be zero
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+if ! $(check_opt_support "clone" "-p") ; then
+ log_unsupported "'zfs clone -p' is not supported yet."
+fi
+
+verify_runnable "both"
+
+function setup_all
+{
+ log_note "Create snapshots and mount them..."
+
+ for snap in $SNAPFS $SNAPFS1 ; do
+ if ! snapexists $snap ; then
+ log_must $ZFS snapshot $snap
+ fi
+ done
+
+ return 0
+}
+
+function cleanup_all
+{
+
+ if datasetexists $TESTPOOL/notexist ; then
+ log_must $ZFS destroy -rRf $TESTPOOL/notexist
+ fi
+
+ for snap in $SNAPFS $SNAPFS1 ; do
+ if snapexists $snap ; then
+ log_must $ZFS destroy -Rf $snap
+ fi
+ done
+
+ return 0
+}
+
+log_assert "clone -p should work as expected."
+log_onexit cleanup_all
+
+setup_all
+
+log_must verify_opt_p_ops "clone" "fs" $SNAPFS \
+ $TESTPOOL/notexist/new/clonefs${TESTCASE_ID}
+
+if is_global_zone ; then
+ log_must verify_opt_p_ops "clone" "vol" $SNAPFS1 \
+ $TESTPOOL/notexist/new/clonevol${TESTCASE_ID}
+fi
+
+log_pass "clone -p should work as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_003_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_003_pos.ksh
new file mode 100644
index 000000000000..1bc7babe0976
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_003_pos.ksh
@@ -0,0 +1,94 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_create/zfs_create_common.kshlib
+. $STF_SUITE/tests/cli_root/zfs_create/properties.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_clone_003_pos
+#
+# DESCRIPTION:
+# 'zfs clone -o property=value filesystem' can successfully create a ZFS
+# clone filesystem with correct property set.
+#
+# STRATEGY:
+# 1. Create a ZFS clone filesystem in the storage pool with -o option
+# 2. Verify the filesystem created successfully
+# 3. Verify the property is correctly set
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-12-16)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if snapexists $SNAPFS ; then
+ log_must $ZFS destroy -Rf $SNAPFS
+ fi
+}
+
+if ! $(check_opt_support "clone" "-o") ; then
+ log_unsupported "'zfs clone -o' unsupported."
+fi
+
+log_onexit cleanup
+
+
+log_assert "'zfs clone -o property=value filesystem' can successfully create" \
+ "a ZFS clone filesystem with correct property set."
+
+log_must $ZFS snapshot $SNAPFS
+
+typeset -i i=0
+while (( $i < ${#RW_FS_PROP[*]} )); do
+ if [[ $WRAPPER == *"crypto"* ]] && \
+ [[ ${RW_FS_PROP[$i]} == *"checksum"* ]]; then
+ (( i = i + 1 ))
+ continue
+ fi
+ log_must $ZFS clone -o ${RW_FS_PROP[$i]} $SNAPFS $TESTPOOL/$TESTCLONE
+ datasetexists $TESTPOOL/$TESTCLONE || \
+ log_fail "zfs clone $TESTPOOL/$TESTCLONE fail."
+ propertycheck $TESTPOOL/$TESTCLONE ${RW_FS_PROP[i]} || \
+ log_fail "${RW_FS_PROP[i]} is failed to set."
+ log_must $ZFS destroy -f $TESTPOOL/$TESTCLONE
+ (( i = i + 1 ))
+done
+
+log_pass "'zfs clone -o property=value filesystem' can successfully create" \
+ "a ZFS clone filesystem with correct property set."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_004_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_004_pos.ksh
new file mode 100644
index 000000000000..a191cbef90fc
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_004_pos.ksh
@@ -0,0 +1,102 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_create/zfs_create_common.kshlib
+. $STF_SUITE/tests/cli_root/zfs_create/properties.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_clone_004_pos
+#
+# DESCRIPTION:
+# 'zfs clone -o property=value filesystem' can successfully create a ZFS
+# clone filesystem with multiple properties set.
+#
+# STRATEGY:
+# 1. Create a ZFS clone filesystem in the storage pool with multiple -o options
+# 2. Verify the filesystem created successfully
+# 3. Verify the properties are correctly set
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-12-16)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if snapexists $SNAPFS ; then
+ log_must $ZFS destroy -Rf $SNAPFS
+ fi
+}
+
+if ! $(check_opt_support "clone" "-o") ; then
+ log_unsupported "'zfs clone -o' unsupported."
+fi
+
+log_onexit cleanup
+
+log_assert "'zfs clone -o property=value filesystem' can successfully create" \
+ "a ZFS clone filesystem with multiple properties set."
+
+typeset -i i=0
+typeset opts=""
+
+log_must $ZFS snapshot $SNAPFS
+
+while (( $i < ${#RW_FS_PROP[*]} )); do
+ if [[ $WRAPPER != *"crypto"* ]] || \
+ [[ ${RW_FS_PROP[$i]} != *"checksum"* ]]; then
+ opts="$opts -o ${RW_FS_PROP[$i]}"
+ fi
+ (( i = i + 1 ))
+done
+
+log_must $ZFS clone $opts $SNAPFS $TESTPOOL/$TESTCLONE
+datasetexists $TESTPOOL/$TESTCLONE || \
+ log_fail "zfs create $TESTPOOL/$TESTCLONE fail."
+
+i=0
+while (( $i < ${#RW_FS_PROP[*]} )); do
+ if [[ $WRAPPER != *"crypto"* ]] || \
+ [[ ${RW_FS_PROP[$i]} != *"checksum"* ]]; then
+ propertycheck $TESTPOOL/$TESTCLONE ${RW_FS_PROP[i]} || \
+ log_fail "${RW_FS_PROP[i]} is failed to set."
+ fi
+ (( i = i + 1 ))
+done
+
+log_pass "'zfs clone -o property=value filesystem' can successfully create" \
+ "a ZFS clone filesystem with multiple properties set."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_005_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_005_pos.ksh
new file mode 100644
index 000000000000..b97591e43ad2
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_005_pos.ksh
@@ -0,0 +1,94 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_create/zfs_create_common.kshlib
+. $STF_SUITE/tests/cli_root/zfs_create/properties.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_clone_005_pos
+#
+# DESCRIPTION:
+# 'zfs clone -o property=value -V size volume' can successfully create a ZFS
+# clone volume with correct property set.
+#
+# STRATEGY:
+# 1. Create a ZFS clone volume in the storage pool with -o option
+# 2. Verify the volume created successfully
+# 3. Verify the property is correctly set
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-12-16)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ if snapexists $SNAPFS1 ; then
+ log_must $ZFS destroy -Rf $SNAPFS1
+ fi
+}
+
+if ! $(check_opt_support "clone" "-o") ; then
+ log_unsupported "'zfs clone -o' unsupported."
+fi
+
+log_onexit cleanup
+
+log_assert "'zfs clone -o property=value -V size volume' can successfully" \
+ "create a ZFS clone volume with correct property set."
+
+log_must $ZFS snapshot $SNAPFS1
+typeset -i i=0
+while (( $i < ${#RW_VOL_CLONE_PROP[*]} )); do
+ if [[ $WRAPPER == *"crypto"* ]] && \
+ [[ ${RW_VOL_CLONE_PROP[$i]} == *"checksum"* ]]; then
+ (( i = i + 1 ))
+ continue
+ fi
+
+ log_must $ZFS clone -o ${RW_VOL_CLONE_PROP[$i]} $SNAPFS1 $TESTPOOL/$TESTCLONE
+ datasetexists $TESTPOOL/$TESTCLONE || \
+ log_fail "zfs clone $TESTPOOL/$TESTCLONE fail."
+ propertycheck $TESTPOOL/$TESTCLONE ${RW_VOL_CLONE_PROP[i]} || \
+ log_fail "${RW_VOL_CLONE_PROP[i]} is failed to set."
+ log_must $ZFS destroy -f $TESTPOOL/$TESTCLONE
+
+ (( i = i + 1 ))
+done
+
+log_pass "'zfs clone -o property=value volume' can successfully" \
+ "create a ZFS clone volume with correct property set."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_006_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_006_pos.ksh
new file mode 100644
index 000000000000..5aa739fef6fe
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_006_pos.ksh
@@ -0,0 +1,100 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_create/zfs_create_common.kshlib
+. $STF_SUITE/tests/cli_root/zfs_create/properties.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_clone_006_pos
+#
+# DESCRIPTION:
+# 'zfs clone -o property=value volume' can successfully create a ZFS
+# clone volume with multiple properties set.
+#
+# STRATEGY:
+# 1. Create a ZFS clone volume in the storage pool with -o option
+# 2. Verify the volume created successfully
+# 3. Verify the properties are correctly set
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-12-16)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ if snapexists $SNAPFS1 ; then
+ log_must $ZFS destroy -Rf $SNAPFS1
+ fi
+}
+
+if ! $(check_opt_support "clone" "-o") ; then
+ log_unsupported "'zfs clone -o' unsupported."
+fi
+
+log_onexit cleanup
+
+log_assert "'zfs clone -o property=value volume' can successfully" \
+ "create a ZFS clone volume with multiple correct properties set."
+
+typeset -i i=0
+typeset opts=""
+
+log_must $ZFS snapshot $SNAPFS1
+
+while (( $i < ${#RW_VOL_CLONE_PROP[*]} )); do
+ if [[ $WRAPPER != *"crypto"* ]] || \
+ [[ ${RW_VOL_CLONE_PROP[$i]} != *"checksum"* ]]; then
+ opts="$opts -o ${RW_VOL_CLONE_PROP[$i]}"
+ fi
+ (( i = i + 1 ))
+done
+
+log_must $ZFS clone $opts $SNAPFS1 $TESTPOOL/$TESTCLONE
+
+i=0
+while (( $i < ${#RW_VOL_CLONE_PROP[*]} )); do
+ if [[ $WRAPPER != *"crypto"* ]] || \
+ [[ ${RW_VOL_CLONE_PROP[$i]} != *"checksum"* ]]; then
+ propertycheck $TESTPOOL/$TESTCLONE ${RW_VOL_CLONE_PROP[i]} || \
+ log_fail "${RW_VOL_CLONE_PROP[i]} is failed to set."
+ fi
+ (( i = i + 1 ))
+done
+
+log_pass "'zfs clone -o property=value volume' can successfully" \
+ "create a ZFS clone volume with multiple correct properties set."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_007_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_007_pos.ksh
new file mode 100644
index 000000000000..ee9bf1705eff
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_007_pos.ksh
@@ -0,0 +1,101 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_clone_007_pos
+#
+# DESCRIPTION:
+# 'zfs clone -o version=' could upgrade version, but downgrade is denied.
+#
+# STRATEGY:
+# 1. Create clone with "-o version=" specified
+# 2. Verify it succeed while upgrade, but fails while the version downgraded.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-12-16)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+if ! $(check_opt_support "upgrade") ; then
+ log_unsupported "'zfs upgrade' unsupported."
+fi
+
+if ! $(check_opt_support "clone" "-o") ; then
+ log_unsupported "'zfs clone -o' unsupported."
+fi
+
+ZFS_VERSION=$($ZFS upgrade | $HEAD -1 | $AWK '{print $NF}' \
+ | $SED -e 's/\.//g')
+
+verify_runnable "both"
+
+function cleanup
+{
+ if snapexists $SNAPFS ; then
+ log_must $ZFS destroy -Rf $SNAPFS
+ fi
+}
+
+log_onexit cleanup
+
+log_assert "'zfs clone -o version=' could upgrade version," \
+ "but downgrade is denied."
+
+log_must $ZFS snapshot $SNAPFS
+
+typeset -i ver
+
+if (( ZFS_TEST_VERSION == 0 )) ; then
+ (( ZFS_TEST_VERSION = ZFS_VERSION ))
+fi
+
+(( ver = ZFS_TEST_VERSION ))
+while (( ver <= ZFS_VERSION )); do
+ log_must $ZFS clone -o version=$ver $SNAPFS $TESTPOOL/$TESTCLONE
+ cleanup
+ (( ver = ver + 1 ))
+done
+
+(( ver = 0 ))
+while (( ver < ZFS_TEST_VERSION )); do
+ log_mustnot $ZFS clone -o version=$ver \
+ $SNAPFS $TESTPOOL/$TESTCLONE
+ log_mustnot datasetexists $TESTPOOL/$TESTCLONE
+ cleanup
+ (( ver = ver + 1 ))
+done
+
+log_pass "'zfs clone -o version=' could upgrade version," \
+ "but downgrade is denied."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_008_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_008_neg.ksh
new file mode 100644
index 000000000000..8f330dbb8ca6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_008_neg.ksh
@@ -0,0 +1,92 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_create/properties.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_clone_008_neg
+#
+# DESCRIPTION:
+# 'zfs clone -o <filesystem>' fails with bad <filesystem> arguments, including:
+# *Same property set multiple times via '-o property=value'
+# *Volume's property set on filesystem
+#
+# STRATEGY:
+# 1. Create an array of <filesystem> arguments
+# 2. Execute 'zfs clone -o <filesystem>' with each argument
+# 3. Verify an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-12-16)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if snapexists $SNAPFS ; then
+ log_must $ZFS destroy -Rf $SNAPFS
+ fi
+}
+
+if ! $(check_opt_support "clone" "-o") ; then
+ log_unsupported "'zfs clone -o' unsupported."
+fi
+
+log_onexit cleanup
+
+log_assert "Verify 'zfs clone -o <filesystem>' fails with bad <filesystem> argument."
+
+log_must $ZFS snapshot $SNAPFS
+
+typeset -i i=0
+while (( $i < ${#RW_FS_PROP[*]} )); do
+ log_mustnot $ZFS clone -o ${RW_FS_PROP[i]} -o ${RW_FS_PROP[i]} \
+ $SNAPFS $TESTPOOL/$TESTCLONE
+ log_mustnot $ZFS clone -p -o ${RW_FS_PROP[i]} -o ${RW_FS_PROP[i]} \
+ $SNAPFS $TESTPOOL/$TESTCLONE
+ ((i = i + 1))
+done
+
+i=0
+while (( $i < ${#VOL_ONLY_PROP[*]} )); do
+ log_mustnot $ZFS clone -o ${VOL_ONLY_PROP[i]} \
+ $SNAPFS $TESTPOOL/$TESTCLONE
+ log_mustnot $ZFS clone -p -o ${VOL_ONLY_PROP[i]} \
+ $SNAPFS $TESTPOOL/$TESTCLONE
+ ((i = i + 1))
+done
+
+log_pass "'zfs clone -o <filesystem>' fails with bad <filesystem> argument."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_009_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_009_neg.ksh
new file mode 100644
index 000000000000..7a321bee25e9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_009_neg.ksh
@@ -0,0 +1,92 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_create/properties.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_clone_009_neg
+#
+# DESCRIPTION:
+# 'zfs clone -o <volume>' fails with badly formed arguments,including:
+# *Same property set multiple times via '-o property=value'
+# *Filesystems's property set on volume
+#
+# STRATEGY:
+# 1. Create an array of badly formed arguments
+# 2. For each argument, execute 'zfs clone -o <volume>'
+# 3. Verify an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-12-16)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ if snapexists $SNAPFS1 ; then
+ log_must $ZFS destroy -Rf $SNAPFS1
+ fi
+}
+
+if ! $(check_opt_support "clone" "-o") ; then
+ log_unsupported "'zfs clone -o' unsupported."
+fi
+
+log_onexit cleanup
+
+log_assert "Verify 'zfs clone -o <volume>' fails with bad <volume> argument."
+
+log_must $ZFS snapshot $SNAPFS1
+
+typeset -i i=0
+while (( $i < ${#RW_VOL_PROP[*]} )); do
+ log_mustnot $ZFS clone -o ${RW_VOL_PROP[i]} -o ${RW_VOL_PROP[i]} \
+ $SNAPFS1 $TESTPOOL/$TESTCLONE
+ log_mustnot $ZFS clone -p -o ${RW_VOL_PROP[i]} -o ${RW_VOL_PROP[i]} \
+ $SNAPFS1 $TESTPOOL/$TESTCLONE
+ ((i = i + 1))
+done
+
+i=0
+while (( $i < ${#FS_ONLY_PROP[*]} )); do
+ log_mustnot $ZFS clone -o ${FS_ONLY_PROP[i]} \
+ $SNAPFS1 $TESTPOOL/$TESTCLONE
+ log_mustnot $ZFS clone -p -o ${FS_ONLY_PROP[i]} \
+ $SNAPFS1 $TESTPOOL/$TESTCLONE
+ ((i = i + 1))
+done
+
+log_pass "Verify 'zfs clone -o <volume>' fails with bad <volume> argument."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_test.sh
new file mode 100755
index 000000000000..e1193c1f4415
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_clone/zfs_clone_test.sh
@@ -0,0 +1,255 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zfs_clone_001_neg cleanup
+zfs_clone_001_neg_head()
+{
+ atf_set "descr" "Badly-formed 'zfs clone' with inapplicable scenariosshould return an error."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_clone_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_clone.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_clone_001_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_clone_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_clone.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_clone_002_pos cleanup
+zfs_clone_002_pos_head()
+{
+ atf_set "descr" "clone -p should work as expected."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_clone_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_clone.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_clone_002_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_clone_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_clone.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_clone_003_pos cleanup
+zfs_clone_003_pos_head()
+{
+ atf_set "descr" "'zfs clone -o property=value filesystem' can successfully createa ZFS clone filesystem with correct property set."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_clone_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_clone.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_clone_003_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_clone_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_clone.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_clone_004_pos cleanup
+zfs_clone_004_pos_head()
+{
+ atf_set "descr" "'zfs clone -o property=value filesystem' can successfully createa ZFS clone filesystem with multiple properties set."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_clone_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_clone.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_clone_004_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_clone_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_clone.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_clone_005_pos cleanup
+zfs_clone_005_pos_head()
+{
+ atf_set "descr" "'zfs clone -o property=value -V size volume' can successfullycreate a ZFS clone volume with correct property set."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_clone_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_clone.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_clone_005_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_clone_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_clone.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_clone_006_pos cleanup
+zfs_clone_006_pos_head()
+{
+ atf_set "descr" "'zfs clone -o property=value volume' can successfullycreate a ZFS clone volume with multiple correct properties set."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_clone_006_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_clone.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_clone_006_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_clone_006_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_clone.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_clone_007_pos cleanup
+zfs_clone_007_pos_head()
+{
+ atf_set "descr" "'zfs clone -o version=' could upgrade version,but downgrade is denied."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_clone_007_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_clone.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_clone_007_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_clone_007_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_clone.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_clone_008_neg cleanup
+zfs_clone_008_neg_head()
+{
+ atf_set "descr" "Verify 'zfs clone -o <filesystem>' fails with bad <filesystem> argument."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_clone_008_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_clone.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_clone_008_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_clone_008_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_clone.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_clone_009_neg cleanup
+zfs_clone_009_neg_head()
+{
+ atf_set "descr" "Verify 'zfs clone -o <volume>' fails with bad <volume> argument."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_clone_009_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_clone.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_clone_009_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_clone_009_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_clone.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zfs_clone_001_neg
+ atf_add_test_case zfs_clone_002_pos
+ atf_add_test_case zfs_clone_003_pos
+ atf_add_test_case zfs_clone_004_pos
+ atf_add_test_case zfs_clone_005_pos
+ atf_add_test_case zfs_clone_006_pos
+ atf_add_test_case zfs_clone_007_pos
+ atf_add_test_case zfs_clone_008_neg
+ atf_add_test_case zfs_clone_009_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/Makefile
new file mode 100644
index 000000000000..f608eca9abf2
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/Makefile
@@ -0,0 +1,22 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zfs_copies
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zfs_copies_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zfs_copies.cfg
+${PACKAGE}FILES+= zfs_copies_001_pos.ksh
+${PACKAGE}FILES+= zfs_copies_004_neg.ksh
+${PACKAGE}FILES+= zfs_copies_005_neg.ksh
+${PACKAGE}FILES+= zfs_copies.kshlib
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zfs_copies_006_pos.ksh
+${PACKAGE}FILES+= zfs_copies_002_pos.ksh
+${PACKAGE}FILES+= zfs_copies_003_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/cleanup.ksh
new file mode 100644
index 000000000000..d91ec19f921a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/cleanup.ksh
@@ -0,0 +1,43 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_copies/zfs_copies.kshlib
+. $STF_SUITE/tests/cli_root/zfs_copies/zfs_copies.cfg
+
+if ! fs_prop_exist "copies" ; then
+ log_unsupported "copies is not supported by this release."
+fi
+
+#
+# umount the ufs fs if there is timedout in the ufs test
+#
+
+if ismounted $UFS_MNTPOINT ufs ; then
+ log_must $UMOUNT -f $UFS_MNTPOINT
+ $RM -fr $UFS_MNTPOINT
+fi
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/setup.ksh
new file mode 100644
index 000000000000..3829f1a3e8e7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/setup.ksh
@@ -0,0 +1,34 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+if ! fs_prop_exist "copies" ; then
+ log_unsupported "copies is not supported by this release."
+fi
+
+DISK=${DISKS%% *}
+default_volume_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies.cfg b/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies.cfg
new file mode 100644
index 000000000000..685a51d00f2f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies.cfg
@@ -0,0 +1,35 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
+
+export ZPOOL_VERSION_1_FILES="zfs-pool-v1.dat.Z"
+export ZPOOL_VERSION_1_NAME="v1-pool"
+
+export FILESIZE=10m
+export FILE=file.${TESTCASE_ID}
+export SLEEPTIME=30
+export UFS_MNTPOINT=/testdir_nfs_mntpoint
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies.kshlib b/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies.kshlib
new file mode 100644
index 000000000000..1e996bcd960d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies.kshlib
@@ -0,0 +1,141 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#
+# Compare the value of copies property with specified value
+# $1, the dataset name
+# $2, the expected copies value
+#
+function cmp_prop
+{
+ typeset ds=$1
+ typeset val_expect=$2
+ typeset val_actual
+
+ val_actual=$(get_prop copies $ds)
+ if [[ $val_actual != $val_expect ]]; then
+ log_fail "Expected value ($val_expect) != " \
+ "actual value ($val_actual)"
+ fi
+}
+
+#
+# Get the value of property used via zfs list
+# $1, the dataset name
+#
+function get_used_prop
+{
+ typeset ds=$1
+ typeset used
+
+ used=`$ZFS list -H -o used $ds`
+ used=${used%[m|M]}
+ if [[ $used == *K ]]; then
+ used=0
+ fi
+ $ECHO $used
+}
+
+#
+# Check the used space is charged correctly
+# $1, the number of used space
+# $2, the expected common factor between the used space and the file space
+#
+function check_used
+{
+ typeset charged_spc=$1
+ typeset -i used
+ typeset -i expected_cfactor=$2
+ typeset -i cfactor
+ typeset -i fsize=${FILESIZE%[m|M]}
+
+ (( used = ${charged_spc%[m|M]} ))
+ (( cfactor = used / fsize ))
+ if (( cfactor != expected_cfactor )); then
+ log_fail "The space is not charged correctly while setting"\
+ "copies as $expected_cfactor."
+ fi
+}
+
+#
+# test ncopies on volume
+# $1 test type zfs|ufs, default zfs
+# $2 copies
+# $3 mntp for ufs test
+function do_vol_test
+{
+ typeset type=$1
+ typeset copy=$2
+ typeset mntp=$3
+
+ vol=$TESTPOOL/$TESTVOL1
+ vol_b_path=/dev/zvol/$TESTPOOL/$TESTVOL1
+ vol_r_path=/dev/zvol/$TESTPOOL/$TESTVOL1
+
+ log_must $ZFS create -V $VOLSIZE -o copies=$copy $vol
+ if fs_prop_exist "refreserv" ; then
+ log_must $ZFS set refreservation=none $vol
+ fi
+ if [[ $type == "ufs" ]]; then
+ log_must $ECHO y | $NEWFS $vol_r_path >/dev/null 2>&1
+ log_must $MOUNT -F ufs -o rw $vol_b_path $mntp
+ else
+ log_must $ZPOOL create $TESTPOOL1 $vol_b_path
+ log_must $ZFS create $TESTPOOL1/$TESTFS1
+ fi
+
+ (( nfilesize = copy * ${FILESIZE%m} ))
+ pre_used=$(get_used_prop $vol)
+ (( target_size = pre_used + nfilesize ))
+
+ if [[ $type == "ufs" ]]; then
+ log_must $MKFILE $FILESIZE $mntp/$FILE
+ else
+ log_must $MKFILE $FILESIZE /$TESTPOOL1/$TESTFS1/$FILE
+ fi
+
+ post_used=$(get_used_prop $vol)
+ while (( post_used < target_size )) ; do
+ sleep 1
+ post_used=$(get_used_prop $vol)
+ done
+
+ (( used = post_used - pre_used ))
+ if (( used < nfilesize )); then
+ log_fail "The space is not charged correctly while setting"\
+ "copies as $copy"
+ fi
+
+ if [[ $type == "ufs" ]]; then
+ $UMOUNT $mntp
+ else
+ log_must $ZPOOL destroy $TESTPOOL1
+ fi
+
+ log_must $ZFS destroy $vol
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies_001_pos.ksh
new file mode 100644
index 000000000000..6627578bdbdf
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies_001_pos.ksh
@@ -0,0 +1,121 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_copies/zfs_copies.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_copies_001_pos
+#
+# DESCRIPTION:
+# Verify "copies" property can be correctly set as 1,2 and 3 and different
+# filesystem can have different value of "copies" property within the same pool.
+#
+# STRATEGY:
+# 1. Create different filesystems with copies set as 1,2,3;
+# 2. Verify that the "copies" property has been set correctly
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-05-31)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ typeset ds
+
+ for ds in $fs1 $fs2 $vol1 $vol2; do
+ if datasetexists $ds; then
+ log_must $ZFS destroy $ds
+ fi
+ done
+}
+
+log_assert "Verify 'copies' property with correct arguments works or not."
+log_onexit cleanup
+
+fs=$TESTPOOL/$TESTFS
+fs1=$TESTPOOL/$TESTFS1
+fs2=$TESTPOOL/$TESTFS2
+vol=$TESTPOOL/$TESTVOL
+vol1=$TESTPOOL/$TESTVOL1
+vol2=$TESTPOOL/$TESTVOL2
+
+#
+# Check the default value for copies property
+#
+for ds in $fs $vol; do
+ cmp_prop $ds 1
+done
+
+for val in 1 2 3; do
+ log_must $ZFS create -o copies=$val $fs1
+ if is_global_zone; then
+ log_must $ZFS create -V $VOLSIZE -o copies=$val $vol1
+ else
+ log_must $ZFS create -o copies=$val $vol1
+ fi
+ for ds in $fs1 $vol1; do
+ cmp_prop $ds $val
+ done
+
+ for val2 in 3 2 1; do
+ log_must $ZFS create -o copies=$val2 $fs2
+ if is_global_zone; then
+ log_must $ZFS create -V $VOLSIZE -o copies=$val2 $vol2
+ else
+ log_must $ZFS create -o copies=$val2 $vol2
+ fi
+ for ds in $fs2 $vol2; do
+ cmp_prop $ds $val2
+ log_must $ZFS destroy $ds
+ done
+ done
+
+ for ds in $fs1 $vol1; do
+ log_must $ZFS destroy $ds
+ done
+
+done
+
+for val in 3 2 1; do
+ for ds in $fs $vol; do
+ log_must $ZFS set copies=$val $ds
+ cmp_prop $ds $val
+ done
+done
+
+log_pass "'copies' property with correct arguments works as expected. "
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies_002_pos.ksh
new file mode 100644
index 000000000000..ad1a28ff8082
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies_002_pos.ksh
@@ -0,0 +1,112 @@
+#!/usr/local/bin/ksh93
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_copies/zfs_copies.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_copies_002_pos
+#
+# DESCRIPTION:
+# Verify that the space used by multiple copies is charged correctly
+#
+# STRATEGY:
+# 1. Create filesystems with copies set as 2,3 respectively;
+# 2. Copy specified size data into each filesystem;
+# 3. Verify that the space is charged as expected with zfs list, ls -s, df(1m),
+# du(1) commands;
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-05-31)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ typeset val
+
+ for val in 1 2 3; do
+ if datasetexists $TESTPOOL/fs_$val; then
+ log_must $ZFS destroy $TESTPOOL/fs_$val
+ fi
+ done
+}
+
+log_assert "Verify that the space used by multiple copies is charged correctly."
+log_onexit cleanup
+
+for val in 1 2 3; do
+ log_must $ZFS create -o copies=$val $TESTPOOL/fs_$val
+
+ log_must $MKFILE $FILESIZE /$TESTPOOL/fs_$val/$FILE
+done
+
+#
+# Sync up the filesystem
+#
+$SYNC
+
+#
+# Verify 'zfs list' can correctly list the space charged
+#
+log_note "Verify 'zfs list' can correctly list the space charged."
+fsize=${FILESIZE%[m|M]}
+for val in 1 2 3; do
+ used=$(get_used_prop $TESTPOOL/fs_$val)
+ check_used $used $val
+done
+
+log_note "Verify 'ls -s' can correctly list the space charged."
+for val in 1 2 3; do
+ blks=`$LS -lsk /$TESTPOOL/fs_$val/$FILE | $AWK '{print $1}'`
+ (( used = blks / 1024 ))
+ check_used $used $val
+done
+
+log_note "Verify df(1M) can corectly display the space charged."
+for val in 1 2 3; do
+ used=`$DF -m /$TESTPOOL/fs_$val | $GREP $TESTPOOL/fs_$val \
+ | $AWK -v fs=fs_$val '$4 ~ fs {print $3}'`
+ check_used $used $val
+done
+
+log_note "Verify du(1) can correctly display the space charged."
+for val in 1 2 3; do
+ used=`$DU -h /$TESTPOOL/fs_$val/$FILE | $AWK '{print $1}'`
+ check_used $used $val
+done
+
+log_pass "The space used by multiple copies is charged correctly as expected. "
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies_003_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies_003_pos.ksh
new file mode 100644
index 000000000000..929e31b2dc6b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies_003_pos.ksh
@@ -0,0 +1,77 @@
+#!/usr/local/bin/ksh93
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_copies/zfs_copies.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_copies_003_pos
+#
+# DESCRIPTION:
+# Verify that the volume space used by multiple copies is charged correctly
+#
+# STRATEGY:
+# 1. Create volume;
+# 2. Create ZFS filesystem based on the volume;
+# 3. Set the copies property of volume to 1,2 or 3;
+# 4. Copy specified size data into each filesystem;
+# 5. Verify that the volume space is charged as expected.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-05-31)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ if poolexists $TESTPOOL1; then
+ destroy_pool $TESTPOOL1
+ fi
+
+ if datasetexists $vol; then
+ log_must $ZFS destroy $vol
+ fi
+}
+
+log_assert "Verify that ZFS volume space used by multiple copies is charged correctly."
+log_onexit cleanup
+vol=$TESTPOOL/$TESTVOL1
+
+
+for val in 1 2 3; do
+ do_vol_test zfs $val
+done
+
+log_pass "The volume space used by multiple copies is charged correctly as expected. "
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies_004_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies_004_neg.ksh
new file mode 100644
index 000000000000..6087d76c9569
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies_004_neg.ksh
@@ -0,0 +1,66 @@
+#!/usr/local/bin/ksh93
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_copies/zfs_copies.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_copies_004_neg
+#
+# DESCRIPTION:
+# Verify that copies cannot be set to other value except for 1, 2 or 3
+#
+# STRATEGY:
+# 1. Create filesystems with copies set as any value other than 1, 2 or 3
+# 2. Verify that the create operations fail
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-05-31)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify that copies property cannot be set to any value other than 1,2 or 3"
+
+set -A badval 0 01 02 03 0 -1 -2 -3 10 20 30 4 5 6 ? * blah
+
+for val in ${badval[@]}; do
+ log_mustnot $ZFS create -o copies=$val $TESTPOOL/$TESTFS1
+ log_mustnot $ZFS create -V $VOLSIZE -o copies=$val $TESTPOOL/$TESTVOL1
+ log_mustnot $ZFS set copies=$val $TESTPOOL/$TESTFS
+ log_mustnot $ZFS set copies=$val $TESTPOOL/$TESTVOL
+done
+
+log_pass "The copies property cannot be set to any value other than 1,2 or 3 as expected"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies_005_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies_005_neg.ksh
new file mode 100644
index 000000000000..fe87c160bead
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies_005_neg.ksh
@@ -0,0 +1,84 @@
+#!/usr/local/bin/ksh93
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_copies/zfs_copies.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_copies_005_neg
+#
+# DESCRIPTION:
+# Verify that copies cannot be set with pool version 1
+#
+# STRATEGY:
+# 1. Create filesystems with copies set in a pool with version 1
+# 2. Verify that the create operations fail
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-05-31)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ if poolexists $ZPOOL_VERSION_1_NAME; then
+ destroy_pool $ZPOOL_VERSION_1_NAME
+ fi
+
+ if [[ -f $TESTDIR/$ZPOOL_VERSION_1_FILES ]]; then
+ rm -f $TESTDIR/$ZPOOL_VERSION_1_FILES
+ fi
+}
+
+log_assert "Verify that copies cannot be set with pool version 1"
+log_onexit cleanup
+
+$CP $STF_SUITE/tests/cli_root/zpool_upgrade/blockfiles/$ZPOOL_VERSION_1_FILES $TESTDIR
+$UNCOMPRESS $TESTDIR/$ZPOOL_VERSION_1_FILES
+log_must $ZPOOL import -d $TESTDIR $ZPOOL_VERSION_1_NAME
+log_must $ZFS create $ZPOOL_VERSION_1_NAME/$TESTFS
+log_must $ZFS create -V 1m $ZPOOL_VERSION_1_NAME/$TESTVOL
+
+for val in 3 2 1; do
+ for ds in $ZPOOL_VERSION_1_NAME/$TESTFS $ZPOOL_VERSION_1_NAME/$TESTVOL; do
+ log_mustnot $ZFS set copies=$val $ds
+ done
+ for ds in $ZPOOL_VERSION_1_NAME/$TESTFS1 $ZPOOL_VERSION_1_NAME/$TESTVOL1; do
+ log_mustnot $ZFS create -o copies=$val $ds
+ done
+done
+
+log_pass "Verification pass: copies cannot be set with pool version 1. "
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies_006_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies_006_pos.ksh
new file mode 100644
index 000000000000..d3675c91b2a5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies_006_pos.ksh
@@ -0,0 +1,86 @@
+#!/usr/local/bin/ksh93
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_copies/zfs_copies.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_copies_006_pos
+#
+# DESCRIPTION:
+# Verify that the volume space used by multiple copies is charged correctly
+#
+# STRATEGY:
+# 1. Create volume
+# 2. Create UFS filesystem based on the volume
+# 3. Set the copies property of volume to 1,2 or 3
+# 4. Copy specified size data into each filesystem
+# 5. Verify that the volume space is charged as expected
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-08-06)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ if ismounted $mntp ufs ; then
+ log_must $UMOUNT $mntp
+ fi
+
+ if datasetexists $vol; then
+ log_must $ZFS destroy $vol
+ fi
+
+ if [[ -d $mntp ]]; then
+ $RM -rf $mntp
+ fi
+}
+
+
+log_assert "Verify that ZFS volume space used by multiple copies is charged correctly."
+log_onexit cleanup
+mntp=$UFS_MNTPOINT
+vol=$TESTPOOL/$TESTVOL1
+
+if [[ ! -d $mntp ]]; then
+ $MKDIR -p $mntp
+fi
+
+for val in 1 2 3; do
+ do_vol_test ufs $val $mntp
+done
+
+log_pass "The volume space used by multiple copies is charged correctly as expected. "
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies_test.sh
new file mode 100755
index 000000000000..14a2b180a1a1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_copies/zfs_copies_test.sh
@@ -0,0 +1,193 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zfs_copies_001_pos cleanup
+zfs_copies_001_pos_head()
+{
+ atf_set "descr" "Verify 'copies' property with correct arguments works or not."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_copies_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_copies.kshlib
+ . $(atf_get_srcdir)/zfs_copies.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_copies_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_copies_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_copies.kshlib
+ . $(atf_get_srcdir)/zfs_copies.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_copies_002_pos cleanup
+zfs_copies_002_pos_head()
+{
+ atf_set "descr" "Verify that the space used by multiple copies is charged correctly."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_copies_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_copies.kshlib
+ . $(atf_get_srcdir)/zfs_copies.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_copies_002_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_copies_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_copies.kshlib
+ . $(atf_get_srcdir)/zfs_copies.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_copies_003_pos cleanup
+zfs_copies_003_pos_head()
+{
+ atf_set "descr" "Verify that ZFS volume space used by multiple copies is charged correctly."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_copies_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_copies.kshlib
+ . $(atf_get_srcdir)/zfs_copies.cfg
+
+ verify_zvol_recursive
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_copies_003_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_copies_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_copies.kshlib
+ . $(atf_get_srcdir)/zfs_copies.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_copies_004_neg cleanup
+zfs_copies_004_neg_head()
+{
+ atf_set "descr" "Verify that copies property cannot be set to any value other than 1,2 or 3"
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_copies_004_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_copies.kshlib
+ . $(atf_get_srcdir)/zfs_copies.cfg
+
+ verify_disk_count "$DISKS" 1
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_copies_004_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_copies_004_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_copies.kshlib
+ . $(atf_get_srcdir)/zfs_copies.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_copies_005_neg cleanup
+zfs_copies_005_neg_head()
+{
+ atf_set "descr" "Verify that copies cannot be set with pool version 1"
+ atf_set "require.progs" "ksh93 zfs zpool"
+}
+zfs_copies_005_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_copies.kshlib
+ . $(atf_get_srcdir)/zfs_copies.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_copies_005_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_copies_005_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_copies.kshlib
+ . $(atf_get_srcdir)/zfs_copies.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_copies_006_pos cleanup
+zfs_copies_006_pos_head()
+{
+ atf_set "descr" "Verify that ZFS volume space used by multiple copies is charged correctly."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_copies_006_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_copies.kshlib
+ . $(atf_get_srcdir)/zfs_copies.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_copies_006_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_copies_006_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_copies.kshlib
+ . $(atf_get_srcdir)/zfs_copies.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zfs_copies_001_pos
+ atf_add_test_case zfs_copies_002_pos
+ atf_add_test_case zfs_copies_003_pos
+ atf_add_test_case zfs_copies_004_neg
+ atf_add_test_case zfs_copies_005_neg
+ atf_add_test_case zfs_copies_006_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_create/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/Makefile
new file mode 100644
index 000000000000..6f048fd37e7a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/Makefile
@@ -0,0 +1,30 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zfs_create
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zfs_create_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= properties.kshlib
+${PACKAGE}FILES+= zfs_create_011_pos.ksh
+${PACKAGE}FILES+= zfs_create_010_neg.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zfs_create_009_neg.ksh
+${PACKAGE}FILES+= zfs_create_004_pos.ksh
+${PACKAGE}FILES+= zfs_create_common.kshlib
+${PACKAGE}FILES+= zfs_create_008_neg.ksh
+${PACKAGE}FILES+= zfs_create_005_pos.ksh
+${PACKAGE}FILES+= zfs_create_001_pos.ksh
+${PACKAGE}FILES+= zfs_create_007_pos.ksh
+${PACKAGE}FILES+= zfs_create_003_pos.ksh
+${PACKAGE}FILES+= zfs_create_012_pos.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zfs_create_013_pos.ksh
+${PACKAGE}FILES+= zfs_create.cfg
+${PACKAGE}FILES+= zfs_create_006_pos.ksh
+${PACKAGE}FILES+= zfs_create_002_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_create/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/cleanup.ksh
new file mode 100644
index 000000000000..713cb16bb31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_create/properties.kshlib b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/properties.kshlib
new file mode 100644
index 000000000000..057d9085c935
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/properties.kshlib
@@ -0,0 +1,78 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+set -A RW_FS_PROP "quota=512M" \
+ "reservation=512M" \
+ "recordsize=64K" \
+ "mountpoint=/tmp/mnt${TESTCASE_ID}" \
+ "checksum=fletcher2" \
+ "compression=lzjb" \
+ "atime=off" \
+ "devices=off" \
+ "exec=off" \
+ "setuid=off" \
+ "readonly=on" \
+ "snapdir=visible" \
+ "aclmode=discard" \
+ "aclinherit=discard" \
+ "canmount=off" \
+ "local:department=123"
+
+is_global_zone && \
+ set -A RW_FS_PROP ${RW_FS_PROP[*]} "sharenfs=on"
+
+set -A RW_VOL_PROP "volblocksize=16K" \
+ "checksum=fletcher2" \
+ "compression=lzjb" \
+ "readonly=on" \
+ "local:department=123"
+
+set -A RW_VOL_CLONE_PROP "checksum=fletcher2" \
+ "compression=lzjb" \
+ "readonly=on" \
+ "local:department=123"
+
+set -A FS_ONLY_PROP "quota=512M" \
+ "recordsize=64K" \
+ "mountpoint=/tmp/mnt${TESTCASE_ID}" \
+ "sharenfs=on" \
+ "atime=off" \
+ "devices=off" \
+ "exec=off" \
+ "setuid=off" \
+ "snapdir=visible" \
+ "aclmode=discard" \
+ "aclinherit=discard" \
+ "canmount=off"
+
+$ZFS upgrade -v > /dev/null 2>&1
+if [[ $? -eq 0 ]]; then
+ set -A FS_ONLY_PROP ${FS_ONLY_PROP[*]} "version=1"
+fi
+
+set -A VOL_ONLY_PROP "volblocksize=16K" "volsize=512M"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_create/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/setup.ksh
new file mode 100644
index 000000000000..326bc555f71b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create.cfg b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create.cfg
new file mode 100644
index 000000000000..d37899c10cde
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create.cfg
@@ -0,0 +1,51 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
+
+export BYND_MAX_NAME="byondmaxnamelength\
+012345678901234567890123456789\
+012345678901234567890123456789\
+012345678901234567890123456789\
+012345678901234567890123456789\
+012345678901234567890123456789\
+012345678901234567890123456789\
+012345678901234567890123456789\
+012345678901234567890123456789"
+
+# There're 3 different prompt messages while create
+# a volume that great than 1TB on 32-bit
+# - volume size exceeds limit for this system. (happy gate)
+# - max volume size is 1TB on 32-bit systems (s10u2)
+# - value is too large (old)
+
+export VOL_LIMIT_KEYWORD1="1TB on 32-bit"
+export VOL_LIMIT_KEYWORD2="value is too large"
+export VOL_LIMIT_KEYWORD3="volume size exceeds limit"
+
+set -A size "8k" "8K" "1m" "1M" "1mb" "1mB" "1Mb" "1MB" "1g" "1G" \
+ "1p" "1P" "1z" "1Z" "1gb" "1gB" "1Gb" "1GB" "1pb" "1pB" "1Pb" \
+ "1PB" "1zb" "1zB" "1Zb" "1ZB"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_001_pos.ksh
new file mode 100644
index 000000000000..ae90884d71e5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_001_pos.ksh
@@ -0,0 +1,79 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_create_001_pos
+#
+# DESCRIPTION:
+# 'zfs create <filesystem>' can create a ZFS filesystem in the namespace.
+#
+# STRATEGY:
+# 1. Create a ZFS filesystem in the storage pool
+# 2. Verify the filesystem created successfully
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+
+function cleanup
+{
+ typeset -i i=0
+ while (( $i < ${#datasets[*]} )); do
+ datasetexists ${datasets[$i]} && \
+ log_must $ZFS destroy -f ${datasets[$i]}
+ ((i = i + 1))
+ done
+}
+
+log_onexit cleanup
+
+set -A datasets "$TESTPOOL/$TESTFS1" "$TESTPOOL/$LONGFSNAME" "$TESTPOOL/..." \
+ "$TESTPOOL/_1234_"
+
+log_assert "'zfs create <filesystem>' can create a ZFS filesystem in the namespace."
+
+typeset -i i=0
+while (( $i < ${#datasets[*]} )); do
+ log_must $ZFS create ${datasets[$i]}
+ datasetexists ${datasets[$i]} || \
+ log_fail "zfs create ${datasets[$i]} fail."
+ ((i = i + 1))
+done
+
+log_pass "'zfs create <filesystem>' works as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_002_pos.ksh
new file mode 100644
index 000000000000..dd84e9f06aa9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_002_pos.ksh
@@ -0,0 +1,91 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_create/zfs_create.cfg
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_create_002_pos
+#
+# DESCRIPTION:
+# 'zfs create -s -V <size> <volume>' can create various-size sparse volume.
+#
+# STRATEGY:
+# 1. Create a volume in the storage pool.
+# 2. Verify the volume is created correctly.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ typeset -i j=0
+ while [[ $j -lt ${#size[*]} ]]; do
+ if datasetexists $TESTPOOL/${TESTVOL}${size[j]}; then
+ log_must $ZFS destroy $TESTPOOL/${TESTVOL}${size[j]}
+ fi
+ ((j = j + 1))
+ done
+}
+
+log_onexit cleanup
+
+
+log_assert "'zfs create -s -V <size> <volume>' succeeds"
+
+typeset -i j=0
+while (( $j < ${#size[*]} )); do
+ typeset cmdline="$ZFS create -s -V ${size[j]} \
+ $TESTPOOL/${TESTVOL}${size[j]}"
+
+ str=$(eval $cmdline 2>&1)
+ if (( $? == 0 )); then
+ log_note "SUCCESS: $cmdline"
+ log_must datasetexists $TESTPOOL/${TESTVOL}${size[j]}
+ elif [[ $str == *${VOL_LIMIT_KEYWORD1}* || \
+ $str == *${VOL_LIMIT_KEYWORD2}* || \
+ $str == *${VOL_LIMIT_KEYWORD3}* ]]
+ then
+ log_note "UNSUPPORTED: $cmdline"
+ else
+ log_fail "$cmdline"
+ fi
+
+ ((j = j + 1))
+
+done
+log_pass "'zfs create -s -V <size> <volume>' works as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_003_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_003_pos.ksh
new file mode 100644
index 000000000000..bff1f86bf2fe
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_003_pos.ksh
@@ -0,0 +1,76 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_create_003_pos
+#
+# DESCRIPTION:
+# 'zfs create [-b <blocksize>] -V <size> <volume>' can create a volume
+# with specified blocksize, which is power of 2 between 512 - 128k.
+#
+# STRATEGY:
+# 1. Create a volume with blocksize in the storage pool
+# 2. Verify the volume created successfully
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-11-23)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ datasetexists $vol && \
+ log_must $ZFS destroy -f $vol
+}
+
+log_assert "Verify creating volume with specified blocksize works."
+log_onexit cleanup
+
+set -A options "" "-b 1k" "-b 1K" "-b 1024" "-b 1024b"
+vol=$TESTPOOL/$TESTVOL
+
+typeset -i i=0
+while (( i < ${#options[*]} )); do
+ log_must $ZFS create ${options[i]} -V $VOLSIZE $vol
+ datasetexists $vol || \
+ log_fail "zfs create ${options[i]} -V $VOLSIZE $vol fail."
+
+ log_must $ZFS destroy -f $vol
+ ((i = i + 1))
+done
+
+log_pass "'zfs create [-b <blocksize>] -V <size> <volume>' works as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_004_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_004_pos.ksh
new file mode 100644
index 000000000000..ac2ce16032cb
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_004_pos.ksh
@@ -0,0 +1,87 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_create/zfs_create_common.kshlib
+. $STF_SUITE/tests/cli_root/zfs_create/properties.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_create_004_pos
+#
+# DESCRIPTION:
+# 'zfs create -o property=value filesystem' can successfully create a ZFS
+# filesystem with correct property set.
+#
+# STRATEGY:
+# 1. Create a ZFS filesystem in the storage pool with -o option
+# 2. Verify the filesystem created successfully
+# 3. Verify the property is correctly set
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-09-07)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ datasetexists $TESTPOOL/$TESTFS1 && \
+ log_must $ZFS destroy -f $TESTPOOL/$TESTFS1
+}
+
+log_onexit cleanup
+
+
+log_assert "'zfs create -o property=value filesystem' can successfully create \
+ a ZFS filesystem with correct property set."
+
+typeset -i i=0
+while (( $i < ${#RW_FS_PROP[*]} )); do
+ if [[ $WRAPPER == *"crypto"* ]] && \
+ [[ ${RW_FS_PROP[$i]} == *"checksum"* ]]; then
+ (( i = i + 1 ))
+ continue
+ fi
+ log_must $ZFS create -o ${RW_FS_PROP[$i]} $TESTPOOL/$TESTFS1
+ datasetexists $TESTPOOL/$TESTFS1 || \
+ log_fail "zfs create $TESTPOOL/$TESTFS1 fail."
+ propertycheck $TESTPOOL/$TESTFS1 ${RW_FS_PROP[i]} || \
+ log_fail "${RW_FS_PROP[i]} is failed to set."
+ log_must $ZFS destroy -f $TESTPOOL/$TESTFS1
+ (( i = i + 1 ))
+done
+
+log_pass "'zfs create -o property=value filesystem' can successfully create \
+ a ZFS filesystem with correct property set."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_005_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_005_pos.ksh
new file mode 100644
index 000000000000..feb005960c76
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_005_pos.ksh
@@ -0,0 +1,96 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_create/zfs_create_common.kshlib
+. $STF_SUITE/tests/cli_root/zfs_create/properties.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_create_005_pos
+#
+# DESCRIPTION:
+# 'zfs create -o property=value filesystem' can successfully create a ZFS
+# filesystem with multiple properties set.
+#
+# STRATEGY:
+# 1. Create a ZFS filesystem in the storage pool with multiple -o options
+# 2. Verify the filesystem created successfully
+# 3. Verify the properties are correctly set
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-09-07)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ datasetexists $TESTPOOL/$TESTFS1 && \
+ log_must $ZFS destroy -f $TESTPOOL/$TESTFS1
+}
+
+log_onexit cleanup
+
+
+log_assert "'zfs create -o property=value filesystem' can successfully create \
+ a ZFS filesystem with multiple properties set."
+
+typeset -i i=0
+typeset opts=""
+
+while (( $i < ${#RW_FS_PROP[*]} )); do
+ if [[ $WRAPPER != *"crypto"* ]] || \
+ [[ ${RW_FS_PROP[$i]} != *"checksum"* ]]; then
+ opts="$opts -o ${RW_FS_PROP[$i]}"
+ fi
+ (( i = i + 1 ))
+done
+
+log_must $ZFS create $opts $TESTPOOL/$TESTFS1
+datasetexists $TESTPOOL/$TESTFS1 || \
+ log_fail "zfs create $TESTPOOL/$TESTFS1 fail."
+
+i=0
+while (( $i < ${#RW_FS_PROP[*]} )); do
+ if [[ $WRAPPER != *"crypto"* ]] || \
+ [[ ${RW_FS_PROP[$i]} != *"checksum"* ]]; then
+ propertycheck $TESTPOOL/$TESTFS1 ${RW_FS_PROP[i]} || \
+ log_fail "${RW_FS_PROP[i]} is failed to set."
+ fi
+ (( i = i + 1 ))
+done
+
+log_pass "'zfs create -o property=value filesystem' can successfully create \
+ a ZFS filesystem with multiple properties set."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_006_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_006_pos.ksh
new file mode 100644
index 000000000000..b610decce1cf
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_006_pos.ksh
@@ -0,0 +1,98 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_create/zfs_create_common.kshlib
+. $STF_SUITE/tests/cli_root/zfs_create/properties.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_create_006_pos
+#
+# DESCRIPTION:
+# 'zfs create -o property=value -V size volume' can successfully create a ZFS
+# volume with correct property set.
+#
+# STRATEGY:
+# 1. Create a ZFS volume in the storage pool with -o option
+# 2. Verify the volume created successfully
+# 3. Verify the property is correctly set
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-09-07)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ datasetexists $TESTPOOL/$TESTVOL1 && \
+ log_must $ZFS destroy -f $TESTPOOL/$TESTVOL1
+}
+
+log_onexit cleanup
+
+
+log_assert "'zfs create -o property=value -V size volume' can successfully \
+ create a ZFS volume with correct property set."
+
+typeset -i i=0
+while (( $i < ${#RW_VOL_PROP[*]} )); do
+ if [[ $WRAPPER == *"crypto"* ]] && \
+ [[ ${RW_VOL_PROP[$i]} == *"checksum"* ]]; then
+ (( i = i + 1 ))
+ continue
+ fi
+
+ log_must $ZFS create -o ${RW_VOL_PROP[$i]} -V $VOLSIZE \
+ $TESTPOOL/$TESTVOL1
+ datasetexists $TESTPOOL/$TESTVOL1 || \
+ log_fail "zfs create -V size $TESTPOOL/$TESTVOL1 fail."
+ propertycheck $TESTPOOL/$TESTVOL1 ${RW_VOL_PROP[i]} || \
+ log_fail "${RW_VOL_PROP[i]} is failed to set."
+ log_must $ZFS destroy -f $TESTPOOL/$TESTVOL1
+
+ log_must $ZFS create -s -o ${RW_VOL_PROP[$i]} -V $VOLSIZE \
+ $TESTPOOL/$TESTVOL1
+ datasetexists $TESTPOOL/$TESTVOL1 || \
+ log_fail "zfs create -s -V $TESTPOOL/$TESTVOL1 fail."
+ propertycheck $TESTPOOL/$TESTVOL1 ${RW_VOL_PROP[i]} || \
+ log_fail "${RW_VOL_PROP[i]} is failed to set."
+ log_must $ZFS destroy -f $TESTPOOL/$TESTVOL1
+
+ (( i = i + 1 ))
+done
+
+log_pass "'zfs create -o property=value -V size volume' can successfully \
+ create a ZFS volume with correct property set."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_007_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_007_pos.ksh
new file mode 100644
index 000000000000..693e03ed39a4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_007_pos.ksh
@@ -0,0 +1,101 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_create/zfs_create_common.kshlib
+. $STF_SUITE/tests/cli_root/zfs_create/properties.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_create_007_pos
+#
+# DESCRIPTION:
+# 'zfs create -o property=value -V size volume' can successfully create a ZFS
+# volume with multiple properties set.
+#
+# STRATEGY:
+# 1. Create a ZFS volume in the storage pool with -o option
+# 2. Verify the volume created successfully
+# 3. Verify the properties are correctly set
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-09-07)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ datasetexists $TESTPOOL/$TESTVOL1 && \
+ log_must $ZFS destroy -f $TESTPOOL/$TESTVOL1
+}
+
+log_onexit cleanup
+
+
+log_assert "'zfs create -o property=value -V size volume' can successfully \
+ create a ZFS volume with correct property set."
+
+typeset -i i=0
+typeset opts=""
+
+while (( $i < ${#RW_VOL_PROP[*]} )); do
+ if [[ $WRAPPER != *"crypto"* ]] || \
+ [[ ${RW_VOL_PROP[$i]} != *"checksum"* ]]; then
+ opts="$opts -o ${RW_VOL_PROP[$i]}"
+ fi
+ (( i = i + 1 ))
+done
+
+log_must $ZFS create $opts -V $VOLSIZE $TESTPOOL/$TESTVOL
+datasetexists $TESTPOOL/$TESTVOL || \
+ log_fail "zfs create $TESTPOOL/$TESTVOL fail."
+log_must $ZFS create -s $opts -V $VOLSIZE $TESTPOOL/$TESTVOL1
+datasetexists $TESTPOOL/$TESTVOL1 || \
+ log_fail "zfs create $TESTPOOL/$TESTVOL1 fail."
+
+i=0
+while (( $i < ${#RW_VOL_PROP[*]} )); do
+ if [[ $WRAPPER != *"crypto"* ]] || \
+ [[ ${RW_VOL_PROP[$i]} != *"checksum"* ]]; then
+ propertycheck $TESTPOOL/$TESTVOL ${RW_VOL_PROP[i]} || \
+ log_fail "${RW_VOL_PROP[i]} is failed to set."
+ propertycheck $TESTPOOL/$TESTVOL1 ${RW_VOL_PROP[i]} || \
+ log_fail "${RW_VOL_PROP[i]} is failed to set."
+ fi
+ (( i = i + 1 ))
+done
+
+log_pass "'zfs create -o property=value -V size volume' can successfully \
+ create a ZFS volume with correct property set."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_008_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_008_neg.ksh
new file mode 100644
index 000000000000..cf645925e845
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_008_neg.ksh
@@ -0,0 +1,108 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_create_008_neg
+#
+# DESCRIPTION:
+# 'zfs create' should return an error with badly formed parameters.
+#
+# STRATEGY:
+# 1. Create an array of parameters
+# 2. For each parameter in the array, execute 'zfs create'
+# 3. Verify an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if datasetexists $TESTPOOL/$TESTFS1 ; then
+ log_must $ZFS destroy -f $TESTPOOL/$TESTFS1
+ fi
+}
+
+log_onexit cleanup
+
+set -A args "ab" "-?" "-cV" "-Vc" "-c -V" "c" "V" "--c" "-e" "-s" \
+ "-blah" "-cV 12k" "-s -cV 1P" "-sc" "-Vs 5g" "-o" "--o" "-O" "--O" \
+ "-o QuOta=none" "-o quota=non" "-o quota=abcd" "-o quota=0" "-o quota=" \
+ "-o ResErVaTi0n=none" "-o reserV=none" "-o reservation=abcd" "-o reserv=" \
+ "-o recorDSize=64k" "-o recordsize=256K" "-o recordsize=256" \
+ "-o recsize=" "-o recsize=zero" "-o recordsize=0" \
+ "-o mountPoint=/tmp/tmpfile${TESTCASE_ID}" "-o mountpoint=non0" "-o mountpoint=" \
+ "-o mountpoint=LEGACY" "-o mounpoint=none" \
+ "-o sharenfs=ON" "-o ShareNFS=off" "-o sharenfs=sss" \
+ "-o checkSUM=on" "-o checksum=SHA256" "-o chsum=off" "-o checksum=aaa" \
+ "-o checkSUM=on -V $VOLSIZE" "-o checksum=SHA256 -V $VOLSIZE" \
+ "-o chsum=off -V $VOLSIZE" "-o checksum=aaa -V $VOLSIZE" \
+ "-o compression=of" "-o ComPression=lzjb" "-o compress=ON" "-o compress=a" \
+ "-o compression=of -V $VOLSIZE" "-o ComPression=lzjb -V $VOLSIZE" \
+ "-o compress=ON -V $VOLSIZE" "-o compress=a -V $VOLSIZE" \
+ "-o atime=ON" "-o ATime=off" "-o atime=bbb" \
+ "-o deviCes=on" "-o devices=OFF" "-o devices=aaa" \
+ "-o exec=ON" "-o EXec=off" "-o exec=aaa" \
+ "-o readonly=ON" "-o reADOnly=off" "-o rdonly=OFF" "-o rdonly=aaa" \
+ "-o readonly=ON -V $VOLSIZE" "-o reADOnly=off -V $VOLSIZE" \
+ "-o rdonly=OFF -V $VOLSIZE" "-o rdonly=aaa -V $VOLSIZE" \
+ "-o zoned=ON" "-o ZoNed=off" "-o zoned=aaa" \
+ "-o snapdIR=hidden" "-o snapdir=VISible" "-o snapdir=aaa" \
+ "-o aclmode=DIScard" "-o aclmODE=groupmask" "-o aclmode=aaa" \
+ "-o aclinherit=deny" "-o aclinHerit=secure" "-o aclinherit=aaa" \
+ "-o type=volume" "-o type=snapshot" "-o type=filesystem" \
+ "-o type=volume -V $VOLSIZE" "-o type=snapshot -V $VOLSIZE" \
+ "-o type=filesystem -V $VOLSIZE" \
+ "-o creation=aaa" "-o creation=aaa -V $VOLSIZE" \
+ "-o used=10K" "-o used=10K -V $VOLSIZE" \
+ "-o available=10K" "-o available=10K -V $VOLSIZE" \
+ "-o referenced=10K" "-o referenced=10K -V $VOLSIZE" \
+ "-o compressratio=1.00x" "-o compressratio=1.00x -V $VOLSIZE" \
+ "-o version=0" "-o version=1.234" "-o version=10K" "-o version=-1" \
+ "-o version=aaa" "-o version=999"
+
+log_assert "'zfs create' should return an error with badly-formed parameters."
+
+typeset -i i=0
+while [[ $i -lt ${#args[*]} ]]; do
+ log_mustnot $ZFS create ${args[i]} $TESTPOOL/$TESTFS1
+ log_mustnot $ZFS create -p ${args[i]} $TESTPOOL/$TESTFS1
+ ((i = i + 1))
+done
+
+log_pass "'zfs create' with badly formed parameters failed as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_009_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_009_neg.ksh
new file mode 100644
index 000000000000..03899ab6f741
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_009_neg.ksh
@@ -0,0 +1,134 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_create/properties.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_create_009_neg
+#
+# DESCRIPTION:
+# 'zfs create <filesystem>' fails with bad <filesystem> arguments, including:
+# *Invalid character against the ZFS namespace
+# *Incomplete component
+# *Too many arguments
+# *Filesystem already exists
+# *Beyond maximal name length.
+# *Same property set multiple times via '-o property=value'
+# *Volume's property set on filesystem
+#
+# STRATEGY:
+# 1. Create an array of <filesystem> arguments
+# 2. Execute 'zfs create <filesystem>' with each argument
+# 3. Verify an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ typeset -i i
+ typeset found
+
+ #
+ # check to see if there is any new fs created during the test
+ # if so destroy it.
+ #
+ for dset in $($ZFS list -H | \
+ $AWK '{print $1}' | $GREP / ); do
+ found=false
+ i=0
+ while (( $i < ${#existed_fs[*]} )); do
+ if [[ $dset == ${existed_fs[i]} ]]; then
+ found=true
+ break
+ fi
+ (( i = i + 1 ))
+ done
+
+ #
+ # new fs created during the test, cleanup it
+ #
+ if [[ $found == "false" ]]; then
+ log_must $ZFS destroy -f $dset
+ fi
+ done
+}
+
+log_onexit cleanup
+
+set -A args "$TESTPOOL/" "$TESTPOOL//blah" "$TESTPOOL/@blah" \
+ "$TESTPOOL/blah@blah" "$TESTPOOL/blah^blah" "$TESTPOOL/blah%blah" \
+ "$TESTPOOL/blah*blah" "$TESTPOOL/blah blah" \
+ "-s $TESTPOOL/$TESTFS1" "-b 1092 $TESTPOOL/$TESTFS1" \
+ "-b 64k $TESTPOOL/$TESTFS1" "-s -b 32k $TESTPOOL/$TESTFS1" \
+ "$TESTPOOL/$BYND_MAX_NAME"
+
+log_assert "Verify 'zfs create <filesystem>' fails with bad <filesystem> argument."
+
+datasetexists $TESTPOOL/$TESTFS || \
+ log_must $ZFS create $TESTPOOL/$TESTFS
+
+set -A existed_fs $($ZFS list -H | $AWK '{print $1}' | $GREP / )
+
+log_mustnot $ZFS create $TESTPOOL
+log_mustnot $ZFS create $TESTPOOL/$TESTFS
+
+typeset -i i=0
+while (( $i < ${#args[*]} )); do
+ log_mustnot $ZFS create ${args[$i]}
+ log_mustnot $ZFS create -p ${args[$i]}
+ ((i = i + 1))
+done
+
+i=0
+while (( $i < ${#RW_FS_PROP[*]} )); do
+ log_mustnot $ZFS create -o ${RW_FS_PROP[i]} -o ${RW_FS_PROP[i]} \
+ $TESTPOOL/$TESTFS1
+ log_mustnot $ZFS create -p -o ${RW_FS_PROP[i]} -o ${RW_FS_PROP[i]} \
+ $TESTPOOL/$TESTFS1
+ ((i = i + 1))
+done
+
+i=0
+while (( $i < ${#VOL_ONLY_PROP[*]} )); do
+ log_mustnot $ZFS create -o ${VOL_ONLY_PROP[i]} $TESTPOOL/$TESTFS1
+ log_mustnot $ZFS create -p -o ${VOL_ONLY_PROP[i]} $TESTPOOL/$TESTFS1
+ ((i = i + 1))
+done
+
+log_pass "'zfs create <filesystem>' fails as expected with bad <filesystem> argument."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_010_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_010_neg.ksh
new file mode 100644
index 000000000000..76ef597d9667
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_010_neg.ksh
@@ -0,0 +1,156 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_create/properties.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_create_010_neg
+#
+# DESCRIPTION:
+# 'zfs create [-b <blocksize> ] -V <size> <volume>' fails with badly formed
+# <size> or <volume> arguments,including:
+# *Invalid volume size and volume name
+# *Invalid blocksize
+# *Incomplete component in the dataset tree
+# *The volume already exists
+# *The volume name beyond the maximal name length - 256.
+# *Same property set multiple times via '-o property=value'
+# *Filesystems's property set on volume
+#
+# STRATEGY:
+# 1. Create an array of badly formed arguments
+# 2. For each argument, execute 'zfs create -V <size> <volume>'
+# 3. Verify an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ typeset -i i
+ typeset found
+
+ #
+ # check to see if there is any new fs created during the test
+ # if so destroy it.
+ #
+ for dset in $($ZFS list -H | \
+ $AWK '{print $1}' | $GREP / ); do
+ found=false
+ i=0
+ while (( $i < ${#existed_fs[*]} )); do
+ if [[ $dset == ${existed_fs[i]} ]]; then
+ found=true
+ break
+ fi
+ (( i = i + 1 ))
+ done
+
+ #
+ # new fs created during the test, cleanup it
+ #
+ if [[ $found == "false" ]]; then
+ log_must $ZFS destroy -f $dset
+ fi
+ done
+}
+
+log_onexit cleanup
+
+log_assert "Verify 'zfs create [-s] [-b <blocksize> ] -V <size> <volume>' fails with" \
+ "badly-formed <size> or <volume> arguments."
+
+set -A args "$VOLSIZE" "$TESTVOL1" \
+ "$VOLSIZE $TESTVOL1" "0 $TESTPOOL/$TESTVOL1" \
+ "-1gb $TESTPOOL/$TESTVOL1" "1g? $TESTPOOL/$TESTVOL1" \
+ "1.01BB $TESTPOOL/$TESTVOL1" "1%g $TESTPOOL/$TESTVOL1" \
+ "1g% $TESTPOOL/$TESTVOL1" "1g$ $TESTPOOL/$TESTVOL1" \
+ "$m $TESTPOOL/$TESTVOL1" "1m$ $TESTPOOL/$TESTVOL1" \
+ "1m! $TESTPOOL/$TESTVOL1" \
+ "1gbb $TESTPOOL/blah" "1blah $TESTPOOL/blah" "blah $TESTPOOL/blah" \
+ "$VOLSIZE $TESTPOOL" "$VOLSIZE $TESTPOOL/" "$VOLSIZE $TESTPOOL//blah"\
+ "$VOLSIZE $TESTPOOL/blah@blah" "$VOLSIZE $TESTPOOL/blah^blah" \
+ "$VOLSIZE $TESTPOOL/blah*blah" "$VOLSIZE $TESTPOOL/blah%blah" \
+ "$VOLSIZE blah" "$VOLSIZE $TESTPOOL/$BYND_MAX_NAME" \
+ "1m -b $TESTPOOL/$TESTVOL1" "1m -b 11k $TESTPOOL/$TESTVOL1" \
+ "1m -b 511 $TESTPOOL/$TESTVOL1" "1m -b 256k $TESTPOOL/$TESTVOL1"
+
+set -A options "" "-s"
+
+datasetexists $TESTPOOL/$TESTVOL || \
+ log_must $ZFS create -V $VOLSIZE $TESTPOOL/$TESTVOL
+
+set -A existed_fs $($ZFS list -H | $AWK '{print $1}' | $GREP / )
+
+log_mustnot $ZFS create -V $VOLSIZE $TESTPOOL/$TESTVOL
+log_mustnot $ZFS create -s -V $VOLSIZE $TESTPOOL/$TESTVOL
+
+typeset -i i=0
+typeset -i j=0
+while (( i < ${#options[*]} )); do
+
+ j=0
+ while (( j < ${#args[*]} )); do
+ log_mustnot $ZFS create ${options[$i]} -V ${args[$j]}
+ log_mustnot $ZFS create -p ${options[$i]} -V ${args[$j]}
+
+ ((j = j + 1))
+ done
+
+ j=0
+ while (( $j < ${#RW_VOL_PROP[*]} )); do
+ log_mustnot $ZFS create ${options[$i]} -o ${RW_VOL_PROP[j]} \
+ -o ${RW_VOL_PROP[j]} -V $VOLSIZE $TESTPOOL/$TESTVOL1
+ log_mustnot $ZFS create -p ${options[$i]} -o ${RW_VOL_PROP[j]} \
+ -o ${RW_VOL_PROP[j]} -V $VOLSIZE $TESTPOOL/$TESTVOL1
+ ((j = j + 1))
+ done
+
+ j=0
+ while (( $j < ${#FS_ONLY_PROP[*]} )); do
+ log_mustnot $ZFS create ${options[$i]} -o ${FS_ONLY_PROP[j]} \
+ -V $VOLSIZE $TESTPOOL/$TESTVOL1
+ log_mustnot $ZFS create -p ${options[$i]} -o ${FS_ONLY_PROP[j]} \
+ -V $VOLSIZE $TESTPOOL/$TESTVOL1
+ ((j = j + 1))
+ done
+
+ ((i = i + 1))
+done
+
+log_pass "'zfs create [-s][-b <blocksize>] -V <size> <volume>' fails as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_011_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_011_pos.ksh
new file mode 100644
index 000000000000..84aa71c70f57
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_011_pos.ksh
@@ -0,0 +1,82 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_create_011_pos
+#
+# DESCRIPTION:
+# 'zfs create -p' should work as expecteed
+#
+# STRATEGY:
+# 1. To create $newdataset with -p option, first make sure the upper level
+# of $newdataset does not exist
+# 2. Make sure without -p option, 'zfs create' will fail
+# 3. Create $newdataset with -p option, verify it is created
+# 4. Run 'zfs create -p $newdataset' again, the exit code should be zero
+# even $newdataset exists
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+if ! $(check_opt_support "create" "-p") ; then
+ log_unsupported "'zfs create -p' option is not supported yet."
+fi
+
+verify_runnable "both"
+
+function cleanup
+{
+ if datasetexists $TESTPOOL/$TESTFS1 ; then
+ log_must $ZFS destroy -rf $TESTPOOL/$TESTFS1
+ fi
+}
+
+log_onexit cleanup
+
+typeset newdataset1="$TESTPOOL/$TESTFS1/$TESTFS/$TESTFS1"
+typeset newdataset2="$TESTPOOL/$TESTFS1/$TESTFS/$TESTVOL1"
+
+log_assert "'zfs create -p' works as expected."
+
+log_must verify_opt_p_ops "create" "fs" $newdataset1
+
+# verify volume creation
+if is_global_zone; then
+ log_must verify_opt_p_ops "create" "vol" $newdataset2
+fi
+
+log_pass "'zfs create -p' works as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_012_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_012_pos.ksh
new file mode 100644
index 000000000000..400191d057c5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_012_pos.ksh
@@ -0,0 +1,87 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_upgrade/zfs_upgrade.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_create_012_pos
+#
+# DESCRIPTION:
+# 'zfs create -p -o version=1' should only cause the leaf filesystem to be version=1
+#
+# STRATEGY:
+# 1. Create $newdataset with -p option, verify it is created
+# 2. Verify only the leaf filesystem to be version=1, others use the current version
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-08-08)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+if ! $(check_opt_support "create" "-p") ; then
+ log_unsupported "-p option is not supported yet."
+fi
+
+if ! $(check_opt_support "upgrade"); then
+ log_unsupported "zfs upgrade not supported yet."
+fi
+
+ZFS_VERSION=$($ZFS upgrade | $HEAD -1 | $AWK '{print $NF}' \
+ | $SED -e 's/\.//g')
+
+verify_runnable "both"
+
+function cleanup
+{
+ if datasetexists $TESTPOOL/$TESTFS1 ; then
+ log_must $ZFS destroy -rf $TESTPOOL/$TESTFS1
+ fi
+}
+
+log_onexit cleanup
+
+
+typeset newdataset1="$TESTPOOL/$TESTFS1/$TESTFS/$TESTFS1"
+
+log_assert "'zfs create -p -o version=1' only cause the leaf filesystem to be version=1."
+
+log_must $ZFS create -p -o version=1 $newdataset1
+log_must datasetexists $newdataset1
+
+log_must check_fs_version $TESTPOOL/$TESTFS1/$TESTFS/$TESTFS1 1
+for fs in $TESTPOOL/$TESTFS1 $TESTPOOL/$TESTFS1/$TESTFS ; do
+ log_must check_fs_version $fs $ZFS_VERSION
+done
+
+log_pass "'zfs create -p -o version=1' only cause the leaf filesystem to be version=1."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_013_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_013_pos.ksh
new file mode 100644
index 000000000000..a0dc30bf614a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_013_pos.ksh
@@ -0,0 +1,91 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_create/zfs_create.cfg
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_create_013_pos
+#
+# DESCRIPTION:
+# 'zfs create -s -V <size> <volume>' can create various-size sparse volume
+# with long fs name
+#
+# STRATEGY:
+# 1. Create a volume in the storage pool.
+# 2. Verify the volume is created correctly.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-08-07)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ typeset -i j=0
+ while [[ $j -lt ${#size[*]} ]]; do
+ datasetexists $TESTPOOL/${LONGFSNAME}${size[j]} && \
+ log_must $ZFS destroy $TESTPOOL/${LONGFSNAME}${size[j]}
+ ((j = j + 1))
+ done
+}
+
+log_onexit cleanup
+
+
+log_assert "'zfs create -s -V <size> <volume>' succeeds"
+
+typeset -i j=0
+while (( $j < ${#size[*]} )); do
+ typeset cmdline="$ZFS create -s -V ${size[j]} \
+ $TESTPOOL/${LONGFSNAME}${size[j]}"
+
+ str=$(eval $cmdline 2>&1)
+ if (( $? == 0 )); then
+ log_note "SUCCESS: $cmdline"
+ log_must datasetexists $TESTPOOL/${LONGFSNAME}${size[j]}
+ elif [[ $str == *${VOL_LIMIT_KEYWORD1}* || \
+ $str == *${VOL_LIMIT_KEYWORD2}* || \
+ $str == *${VOL_LIMIT_KEYWORD3}* ]]
+ then
+ log_note "UNSUPPORTED: $cmdline"
+ else
+ log_fail "$cmdline"
+ fi
+
+ ((j = j + 1))
+done
+
+log_pass "'zfs create -s -V <size> <volume>' works as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_common.kshlib b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_common.kshlib
new file mode 100644
index 000000000000..8b798f7d8c67
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_common.kshlib
@@ -0,0 +1,51 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#!/bin/ksh -p
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#
+# Check if the user property is identical to the expected value.
+#
+# $1 dataset
+# $2 property string
+#
+function propertycheck
+{
+ typeset dtst=$1
+ typeset propstr=$2
+
+ typeset prop=$($ECHO $propstr | $AWK -F= '{print $1}')
+ typeset expect_value=$($ECHO $propstr | $AWK -F= '{print $2}')
+ typeset value=$($ZFS get -H -o value $prop $dtst)
+
+
+ if [[ "$expect_value" == "$value" ]]; then
+ return 0
+ else
+ return 1
+ fi
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_test.sh
new file mode 100755
index 000000000000..e5b8b17efffc
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_create/zfs_create_test.sh
@@ -0,0 +1,408 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zfs_create_001_pos cleanup
+zfs_create_001_pos_head()
+{
+ atf_set "descr" "'zfs create <filesystem>' can create a ZFS filesystem in the namespace."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_create_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_create_common.kshlib
+ . $(atf_get_srcdir)/properties.kshlib
+ . $(atf_get_srcdir)/zfs_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_create_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_create_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_create_common.kshlib
+ . $(atf_get_srcdir)/properties.kshlib
+ . $(atf_get_srcdir)/zfs_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_create_002_pos cleanup
+zfs_create_002_pos_head()
+{
+ atf_set "descr" "'zfs create -s -V <size> <volume>' succeeds"
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_create_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_create_common.kshlib
+ . $(atf_get_srcdir)/properties.kshlib
+ . $(atf_get_srcdir)/zfs_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_create_002_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_create_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_create_common.kshlib
+ . $(atf_get_srcdir)/properties.kshlib
+ . $(atf_get_srcdir)/zfs_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_create_003_pos cleanup
+zfs_create_003_pos_head()
+{
+ atf_set "descr" "Verify creating volume with specified blocksize works."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_create_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_create_common.kshlib
+ . $(atf_get_srcdir)/properties.kshlib
+ . $(atf_get_srcdir)/zfs_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_create_003_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_create_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_create_common.kshlib
+ . $(atf_get_srcdir)/properties.kshlib
+ . $(atf_get_srcdir)/zfs_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_create_004_pos cleanup
+zfs_create_004_pos_head()
+{
+ atf_set "descr" "'zfs create -o property=value filesystem' can successfully createa ZFS filesystem with correct property set."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_create_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_create_common.kshlib
+ . $(atf_get_srcdir)/properties.kshlib
+ . $(atf_get_srcdir)/zfs_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_create_004_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_create_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_create_common.kshlib
+ . $(atf_get_srcdir)/properties.kshlib
+ . $(atf_get_srcdir)/zfs_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_create_005_pos cleanup
+zfs_create_005_pos_head()
+{
+ atf_set "descr" "'zfs create -o property=value filesystem' can successfully createa ZFS filesystem with multiple properties set."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_create_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_create_common.kshlib
+ . $(atf_get_srcdir)/properties.kshlib
+ . $(atf_get_srcdir)/zfs_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_create_005_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_create_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_create_common.kshlib
+ . $(atf_get_srcdir)/properties.kshlib
+ . $(atf_get_srcdir)/zfs_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_create_006_pos cleanup
+zfs_create_006_pos_head()
+{
+ atf_set "descr" "'zfs create -o property=value -V size volume' can successfullycreate a ZFS volume with correct property set."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_create_006_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_create_common.kshlib
+ . $(atf_get_srcdir)/properties.kshlib
+ . $(atf_get_srcdir)/zfs_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_create_006_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_create_006_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_create_common.kshlib
+ . $(atf_get_srcdir)/properties.kshlib
+ . $(atf_get_srcdir)/zfs_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_create_007_pos cleanup
+zfs_create_007_pos_head()
+{
+ atf_set "descr" "'zfs create -o property=value -V size volume' can successfullycreate a ZFS volume with correct property set."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_create_007_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_create_common.kshlib
+ . $(atf_get_srcdir)/properties.kshlib
+ . $(atf_get_srcdir)/zfs_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_create_007_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_create_007_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_create_common.kshlib
+ . $(atf_get_srcdir)/properties.kshlib
+ . $(atf_get_srcdir)/zfs_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_create_008_neg cleanup
+zfs_create_008_neg_head()
+{
+ atf_set "descr" "'zfs create' should return an error with badly-formed parameters."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_create_008_neg_body()
+{
+ atf_expect_fail 'kern/221987 - ZFS does not validate the sharenfs parameter'
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_create_common.kshlib
+ . $(atf_get_srcdir)/properties.kshlib
+ . $(atf_get_srcdir)/zfs_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_create_008_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_create_008_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_create_common.kshlib
+ . $(atf_get_srcdir)/properties.kshlib
+ . $(atf_get_srcdir)/zfs_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_create_009_neg cleanup
+zfs_create_009_neg_head()
+{
+ atf_set "descr" "Verify 'zfs create <filesystem>' fails with bad <filesystem> argument."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_create_009_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_create_common.kshlib
+ . $(atf_get_srcdir)/properties.kshlib
+ . $(atf_get_srcdir)/zfs_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_create_009_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_create_009_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_create_common.kshlib
+ . $(atf_get_srcdir)/properties.kshlib
+ . $(atf_get_srcdir)/zfs_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_create_010_neg cleanup
+zfs_create_010_neg_head()
+{
+ atf_set "descr" "Verify 'zfs create [-s] [-b <blocksize> ] -V <size> <volume>' fails withbadly-formed <size> or <volume> arguments."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_create_010_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_create_common.kshlib
+ . $(atf_get_srcdir)/properties.kshlib
+ . $(atf_get_srcdir)/zfs_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_create_010_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_create_010_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_create_common.kshlib
+ . $(atf_get_srcdir)/properties.kshlib
+ . $(atf_get_srcdir)/zfs_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_create_011_pos cleanup
+zfs_create_011_pos_head()
+{
+ atf_set "descr" "'zfs create -p' works as expected."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_create_011_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_create_common.kshlib
+ . $(atf_get_srcdir)/properties.kshlib
+ . $(atf_get_srcdir)/zfs_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_create_011_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_create_011_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_create_common.kshlib
+ . $(atf_get_srcdir)/properties.kshlib
+ . $(atf_get_srcdir)/zfs_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_create_012_pos cleanup
+zfs_create_012_pos_head()
+{
+ atf_set "descr" "'zfs create -p -o version=1' only cause the leaf filesystem to be version=1."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_create_012_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_create_common.kshlib
+ . $(atf_get_srcdir)/properties.kshlib
+ . $(atf_get_srcdir)/zfs_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_create_012_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_create_012_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_create_common.kshlib
+ . $(atf_get_srcdir)/properties.kshlib
+ . $(atf_get_srcdir)/zfs_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_create_013_pos cleanup
+zfs_create_013_pos_head()
+{
+ atf_set "descr" "'zfs create -s -V <size> <volume>' succeeds"
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_create_013_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_create_common.kshlib
+ . $(atf_get_srcdir)/properties.kshlib
+ . $(atf_get_srcdir)/zfs_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_create_013_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_create_013_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_create_common.kshlib
+ . $(atf_get_srcdir)/properties.kshlib
+ . $(atf_get_srcdir)/zfs_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zfs_create_001_pos
+ atf_add_test_case zfs_create_002_pos
+ atf_add_test_case zfs_create_003_pos
+ atf_add_test_case zfs_create_004_pos
+ atf_add_test_case zfs_create_005_pos
+ atf_add_test_case zfs_create_006_pos
+ atf_add_test_case zfs_create_007_pos
+ atf_add_test_case zfs_create_008_neg
+ atf_add_test_case zfs_create_009_neg
+ atf_add_test_case zfs_create_010_neg
+ atf_add_test_case zfs_create_011_pos
+ atf_add_test_case zfs_create_012_pos
+ atf_add_test_case zfs_create_013_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/Makefile
new file mode 100644
index 000000000000..8e8777ff42af
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/Makefile
@@ -0,0 +1,23 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zfs_destroy
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zfs_destroy_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zfs_destroy_001_pos.ksh
+${PACKAGE}FILES+= zfs_destroy.cfg
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zfs_destroy_004_pos.ksh
+${PACKAGE}FILES+= zfs_destroy_005_neg.ksh
+${PACKAGE}FILES+= zfs_destroy_common.kshlib
+${PACKAGE}FILES+= zfs_destroy_002_pos.ksh
+${PACKAGE}FILES+= zfs_destroy_007_neg.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zfs_destroy_003_pos.ksh
+${PACKAGE}FILES+= zfs_destroy_006_neg.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/cleanup.ksh
new file mode 100644
index 000000000000..8e4deae9c70c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/cleanup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_destroy/zfs_destroy_common.kshlib
+
+cleanup_testenv
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/setup.ksh
new file mode 100644
index 000000000000..326bc555f71b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy.cfg b/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy.cfg
new file mode 100644
index 000000000000..cf27b143d802
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy.cfg
@@ -0,0 +1,39 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
+
+export TESTFSCLONE=${TESTFS}clone
+export TESTVOLCLONE=${TESTVOL}clone
+
+export CTR=$TESTPOOL/$TESTCTR
+export FS=$CTR/$TESTFS
+export VOL=$CTR/$TESTVOL
+export FSSNAP=$FS@$TESTSNAP
+export VOLSNAP=$VOL@$TESTSNAP
+export FSCLONE=$TESTPOOL/$TESTFSCLONE
+export VOLCLONE=$TESTPOOL/$TESTVOLCLONE
+export STF_TIMEOUT=3600
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_001_pos.ksh
new file mode 100644
index 000000000000..4afa2ea55cf9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_001_pos.ksh
@@ -0,0 +1,210 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_destroy/zfs_destroy_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_destroy_001_pos
+#
+# DESCRIPTION:
+# 'zfs destroy -r|-rf|-R|-Rf <fs|ctr|vol|snap>' should recursively destroy
+# all children and clones based on options.
+#
+# STRATEGY:
+# 1. Create test environment according to options. There are three test
+# models can be created. Only ctr, fs & vol; with snap; with clone.
+# 2. According to option, make the dataset busy or not.
+# 3. Run 'zfs destroy [-rRf] <dataset>'
+# 4. According to dataset and option, check if get the expected results.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-22)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+#
+# According to parameters, 1st, create suitable testing environment. 2nd,
+# run 'zfs destroy $opt <dataset>'. 3rd, check the system status.
+#
+# $1 option of 'zfs destroy'
+# $2 dataset will be destroied.
+#
+function test_n_check
+{
+ typeset opt=$1
+ typeset dtst=$2
+
+ if ! is_global_zone ; then
+ if [[ $dtst == $VOL || $dtst == $VOLSNAP ]]; then
+ log_note "UNSUPPORTED: Volume are unavailable in LZ."
+ return
+ fi
+ fi
+
+ # '-f' has no effect on non-filesystems
+ if [[ $opt == -f ]]; then
+ if [[ $dtst != $FS || $dtst != $CTR ]]; then
+ log_note "UNSUPPORTED: '-f ' is only available for FS."
+ return
+ fi
+ fi
+
+ # Clean the test environment and make it clear.
+ if datasetexists $CTR; then
+ log_must $ZFS destroy -Rf $CTR
+ fi
+
+ # According to option create test compatible environment.
+ case $opt in
+ -r|-rf) setup_testenv snap ;;
+ -R|-Rf) setup_testenv clone ;;
+ -f) setup_testenv ;;
+ *) log_fail "Incorrect option: '$opt'." ;;
+ esac
+
+ #
+ # According to different dataset type, create busy condition when try to
+ # destroy this dataset.
+ #
+ typeset mpt_dir
+ case $dtst in
+ $CTR|$FS)
+ if [[ $opt == *f* ]]; then
+ mpt_dir=$(get_prop mountpoint $FS)
+ make_dir_busy $mpt_dir
+ log_mustnot $ZFS destroy -rR $dtst
+ make_dir_unbusy $mpt_dir
+ fi
+ ;;
+ $VOL)
+ if [[ $opt == *f* ]]; then
+ make_dir_busy $TESTDIR1
+ log_mustnot $ZFS destroy -rR $dtst
+ make_dir_unbusy $TESTDIR1
+ fi
+ ;;
+ $FSSNAP)
+ if [[ $opt == *f* ]]; then
+ mpt_dir=$(snapshot_mountpoint $dtst)
+ init_dir=$PWD
+ make_dir_busy $mpt_dir
+ log_must $ZFS destroy -rR $dtst
+ log_must $ZFS snapshot $dtst
+ make_dir_unbusy $mpt_dir
+ fi
+ ;;
+ $VOLSNAP)
+ if [[ $opt == *f* ]]; then
+ mpt_dir=$TESTDIR1
+ init_dir=$PWD
+ make_dir_busy $mpt_dir
+ log_must $ZFS destroy -rR $dtst
+ log_must $ZFS snapshot $dtst
+ make_dir_unbusy $mpt_dir
+ fi
+ ;;
+ *) log_fail "Unsupported dataset: '$dtst'."
+ esac
+
+ # Firstly, umount ufs filesystem which was created by zfs volume.
+ if is_global_zone; then
+ log_must $UMOUNT -f $TESTDIR1
+ fi
+ # Invoke 'zfs destroy [-rRf] <dataset>'
+ log_must $ZFS destroy $opt $dtst
+
+ case $dtst in
+ $CTR) check_dataset datasetnonexists \
+ $CTR $FS $VOL $FSSNAP $VOLSNAP
+ if [[ $opt == *R* ]]; then
+ check_dataset datasetnonexists \
+ $FSCLONE $VOLCLONE
+ fi
+ ;;
+ $FS) check_dataset datasetexists $CTR $VOL
+ check_dataset datasetnonexists $FS
+ if [[ $opt != -f ]]; then
+ check_dataset datasetexists $VOLSNAP
+ check_dataset datasetnonexists $FSSNAP
+ fi
+ if [[ $opt == *R* ]]; then
+ check_dataset datasetexists $VOLCLONE
+ check_dataset datasetnonexists $FSCLONE
+ fi
+ ;;
+ $VOL) check_dataset datasetexists $CTR $FS $FSSNAP
+ check_dataset datasetnonexists $VOL $VOLSNAP
+ if [[ $opt == *R* ]]; then
+ check_dataset datasetexists $FSCLONE
+ check_dataset datasetnonexists $VOLCLONE
+ fi
+ ;;
+ $FSSNAP)
+ check_dataset datasetexists $CTR $FS $VOL $VOLSNAP
+ check_dataset datasetnonexists $FSSNAP
+ if [[ $opt == *R* ]]; then
+ check_dataset datasetexists $VOLCLONE
+ check_dataset datasetnonexists $FSCLONE
+ fi
+ ;;
+ $VOLSNAP)
+ check_dataset datasetexists $CTR $FS $VOL $FSSNAP
+ check_dataset datasetnonexists $VOLSNAP
+ if [[ $opt == *R* ]]; then
+ check_dataset datasetexists $FSCLONE
+ check_dataset datasetnonexists $VOLCLONE
+ fi
+ ;;
+ esac
+
+ log_note "'$ZFS destroy $opt $dtst' passed."
+}
+
+log_assert "'zfs destroy -r|-R|-f|-rf|-Rf <fs|ctr|vol|snap>' should " \
+ "recursively destroy all children."
+log_onexit cleanup_testenv
+
+typeset dtst=""
+typeset opt=""
+for dtst in $CTR $FS $VOL $FSSNAP $VOLSNAP; do
+ for opt in "-r" "-R" "-f" "-rf" "-Rf"; do
+ log_note "Starting test: $ZFS destroy $opt $dtst"
+ test_n_check $opt $dtst
+ done
+done
+
+log_pass "'zfs destroy -r|-R|-f|-rf|-Rf <fs|ctr|vol|snap>' passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_002_pos.ksh
new file mode 100644
index 000000000000..a2e371b4d900
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_002_pos.ksh
@@ -0,0 +1,109 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_destroy_002_pos
+#
+# DESCRIPTION:
+# 'zfs destroy <filesystem|volume|snapshot>' can successfully destroy
+# the specified dataset which has no active dependents.
+#
+# STRATEGY:
+# 1. Create a filesystem,volume and snapshot in the storage pool
+# 2. Destroy the filesystem,volume and snapshot
+# 3. Verify the datasets are destroyed successfully
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-07)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ typeset -i i=0
+ while (( $i < ${#data_objs[*]} )); do
+ datasetexists "${data_objs[i]}" && \
+ $ZFS destroy -rf ${data_objs[i]}
+ ((i = i + 1))
+ done
+}
+
+log_assert "Verify 'zfs destroy' can destroy the specified datasets without active" \
+ "dependents."
+log_onexit cleanup
+
+if is_global_zone ; then
+ set -A data_objs "$TESTPOOL/$TESTFS@$TESTSNAP" "$TESTPOOL/$TESTFS1" \
+ "$TESTPOOL/$TESTVOL" "$TESTPOOL/$TESTVOL1"
+else
+ set -A data_objs "$TESTPOOL/$TESTFS@$TESTSNAP" "$TESTPOOL/$TESTFS1"
+fi
+
+log_must $ZFS create $TESTPOOL/$TESTFS1
+log_must $ZFS snapshot $TESTPOOL/$TESTFS@$TESTSNAP
+
+if is_global_zone ; then
+ log_must $ZFS create -V $VOLSIZE $TESTPOOL/$TESTVOL
+
+ # Max volume size is 1TB on 32-bit systems
+ [[ `$UNAME -p` == "i386" ]] && \
+ BIGVOLSIZE=1Tb
+ [[ `$UNAME -p` == "arm" ]] && \
+ BIGVOLSIZE=1Tb
+ [[ `$UNAME -p` == "mips" ]] && \
+ BIGVOLSIZE=1Tb
+ [[ `$UNAME -p` == "powerpc" ]] && \
+ BIGVOLSIZE=1Tb
+ log_must $ZFS create -sV $BIGVOLSIZE $TESTPOOL/$TESTVOL1
+fi
+
+typeset -i i=0
+while (( $i < ${#data_objs[*]} )); do
+ datasetexists ${data_objs[i]} || \
+ log_fail "Create <filesystem>|<volume>|<snapshot> fail."
+ ((i = i + 1))
+done
+
+i=0
+while (( $i < ${#data_objs[*]} )); do
+ log_must $ZFS destroy ${data_objs[i]}
+ datasetexists ${data_objs[i]} && \
+ log_fail "'zfs destroy <filesystem>|<volume>|<snapshot>' fail."
+ ((i = i + 1))
+done
+
+log_pass "'zfs destroy <filesystem>|<volume>|<snapshot>' works as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_003_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_003_pos.ksh
new file mode 100644
index 000000000000..1baa16614ec6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_003_pos.ksh
@@ -0,0 +1,163 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion__start
+#
+# ID: zfs_destroy_003_pos
+#
+# DESCRIPTION:
+# Verify 'zfs destroy [-rR]' succeeds as root.
+#
+# STRATEGY:
+# 1. Create two datasets in the storage pool
+# 2. Create fs,vol,ctr,snapshot and clones of snapshot in the two datasets
+# 3. Create clone in the second dataset for the snapshot in the first dataset
+# 4. Verify 'zfs destroy -r' fails to destroy dataset with clone outside it
+# 5. Verify 'zfs destroy -R' succeeds to destroy dataset with clone outside it
+# 6. Verify 'zfs destroy -r' succeeds to destroy dataset without clone outside it.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-08-02)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ for obj in $ctr2 $ctr1 $ctr; do
+ datasetexists $obj && \
+ log_must $ZFS destroy -Rf $obj
+ done
+
+ for mntp in $TESTDIR1 $TESTDIR2; do
+ [[ -d $mntp ]] && \
+ log_must $RM -rf $mntp
+ done
+}
+
+log_assert "Verify that 'zfs destroy [-rR]' succeeds as root. "
+
+log_onexit cleanup
+
+#
+# Preparations for testing
+#
+for dir in $TESTDIR1 $TESTDIR2; do
+ [[ ! -d $dir ]] && \
+ log_must $MKDIR -p $dir
+done
+
+ctr=$TESTPOOL/$TESTCTR
+ctr1=$TESTPOOL/$TESTCTR1
+ctr2=$ctr/$TESTCTR2
+ctr3=$ctr1/$TESTCTR2
+child_fs=$ctr/$TESTFS1
+child_fs1=$ctr1/$TESTFS2
+child_fs_mntp=$TESTDIR1
+child_fs1_mntp=$TESTDIR2
+child_vol=$ctr/$TESTVOL
+child_vol1=$ctr1/$TESTVOL
+child_fs_snap=$child_fs@snap
+child_fs1_snap=$child_fs1@snap
+child_fs_snap_clone=$ctr/$TESTCLONE
+child_fs_snap_clone1=$ctr1/${TESTCLONE}_across_ctr
+child_fs_snap_clone2=$ctr2/$TESTCLONE2
+child_fs1_snap_clone=$ctr1/$TESTCLONE1
+child_fs1_snap_clone1=$ctr/${TESTCLONE1}_across_ctr
+
+#
+# Create two datasets in the storage pool
+#
+log_must $ZFS create $ctr
+log_must $ZFS create $ctr1
+
+#
+# Create children datasets fs,vol,snapshot in the datasets, and
+# clones across two datasets
+#
+log_must $ZFS create $ctr2
+
+for fs in $child_fs $child_fs1; do
+ log_must $ZFS create $fs
+done
+
+log_must $ZFS set mountpoint=$child_fs_mntp $child_fs
+log_must $ZFS set mountpoint=$child_fs1_mntp $child_fs1
+
+for snap in $child_fs_snap $child_fs1_snap; do
+ log_must $ZFS snapshot $snap
+done
+
+if is_global_zone ; then
+ for vol in $child_vol $child_vol1; do
+ log_must $ZFS create -V $VOLSIZE $vol
+ done
+fi
+
+for clone in $child_fs_snap_clone $child_fs_snap_clone1; do
+ log_must $ZFS clone $child_fs_snap $clone
+done
+
+
+for clone in $child_fs1_snap_clone $child_fs1_snap_clone1; do
+ log_must $ZFS clone $child_fs1_snap $clone
+done
+
+log_note "Verify that 'zfs destroy -r' fails to destroy dataset " \
+ "with clone dependent outside it."
+
+for obj in $child_fs $child_fs1 $ctr $ctr1; do
+ log_mustnot $ZFS destroy -r $obj
+ datasetexists $obj || \
+ log_fail "'zfs destroy -r' fails to keep clone " \
+ "dependent outside the hirearchy."
+done
+
+
+log_note "Verify that 'zfs destroy -R' succeeds to destroy dataset " \
+ "with clone dependent outside it."
+
+log_must $ZFS destroy -R $ctr1
+datasetexists $ctr1 && \
+ log_fail "'zfs destroy -R' fails to destroy dataset with clone outside it."
+
+log_note "Verify that 'zfs destroy -r' succeeds to destroy dataset " \
+ "without clone dependent outside it."
+
+log_must $ZFS destroy -r $ctr
+datasetexists $ctr && \
+ log_fail "'zfs destroy -r' fails to destroy dataset with clone outside it."
+
+log_pass "'zfs destroy [-rR] succeeds as root."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_004_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_004_pos.ksh
new file mode 100644
index 000000000000..e2a100819d6d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_004_pos.ksh
@@ -0,0 +1,127 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_destroy_004_pos
+#
+# DESCRIPTION:
+# Verify 'zfs destroy -f' succeeds as root.
+#
+# STRATEGY:
+# 1. Create filesystem in the storage pool
+# 2. Set mountpoint for the filesystem and make it busy
+# 3. Verify that 'zfs destroy' fails to destroy the filesystem
+# 4. Verify 'zfs destroy -f' succeeds to destroy the filesystem.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-08-02)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ cd $olddir
+
+ datasetexists $clone && \
+ log_must $ZFS destroy -f $clone
+
+ snapexists $snap && \
+ log_must $ZFS destroy -f $snap
+
+ for fs in $fs1 $fs2; do
+ datasetexists $fs && \
+ log_must $ZFS destroy -f $fs
+ done
+
+ for dir in $TESTDIR1 $TESTDIR2; do
+ [[ -d $dir ]] && \
+ log_must $RM -rf $dir
+ done
+}
+
+log_assert "Verify that 'zfs destroy -f' succeeds as root. "
+
+log_onexit cleanup
+
+#
+# Preparations for testing
+#
+olddir=$PWD
+
+for dir in $TESTDIR1 $TESTDIR2; do
+ [[ ! -d $dir ]] && \
+ log_must $MKDIR -p $dir
+done
+
+fs1=$TESTPOOL/$TESTFS1
+mntp1=$TESTDIR1
+fs2=$TESTPOOL/$TESTFS2
+snap=$TESTPOOL/$TESTFS2@snap
+clone=$TESTPOOL/$TESTCLONE
+mntp2=$TESTDIR2
+
+#
+# Create filesystem and clone in the storage pool, mount them and
+# make the mountpoint busy
+#
+for fs in $fs1 $fs2; do
+ log_must $ZFS create $fs
+done
+
+log_must $ZFS snapshot $snap
+log_must $ZFS clone $snap $clone
+
+log_must $ZFS set mountpoint=$mntp1 $fs1
+log_must $ZFS set mountpoint=$mntp2 $clone
+
+for arg in "$fs1 $mntp1" "$clone $mntp2"; do
+ fs=`$ECHO $arg | $AWK '{print $1}'`
+ mntp=`$ECHO $arg | $AWK '{print $2}'`
+
+ log_note "Verify that 'zfs destroy' fails to" \
+ "destroy filesystem when it is busy."
+ cd $mntp
+ log_mustnot $ZFS destroy $fs
+
+ log_must $ZFS destroy -f $fs
+ datasetexists $fs && \
+ log_fail "'zfs destroy -f' fails to destroy busy filesystem."
+
+ cd $olddir
+done
+
+log_pass "'zfs destroy -f' succeeds as root."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_005_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_005_neg.ksh
new file mode 100644
index 000000000000..5c19fc18cebd
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_005_neg.ksh
@@ -0,0 +1,222 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_destroy/zfs_destroy_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_destroy_005_neg
+#
+# DESCRIPTION:
+# Separately verify 'zfs destroy -f|-r|-rf|-R|-rR <dataset>' will fail in
+# different conditions.
+#
+# STRATEGY:
+# 1. Create pool, fs & vol.
+# 2. Create snapshot for fs & vol.
+# 3. Invoke 'zfs destroy ''|-f <dataset>', it should fail.
+# 4. Create clone for fs & vol.
+# 5. Invoke 'zfs destroy -r|-rf <dataset>', it should fail.
+# 6. Write file to filesystem or enter snapshot mountpoint.
+# 7. Invoke 'zfs destroy -R|-rR <dataset>', it should fail.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-08-03)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Separately verify 'zfs destroy -f|-r|-rf|-R|-rR <dataset>' will " \
+ "fail in different conditions."
+log_onexit cleanup_testenv
+
+#
+# Run 'zfs destroy [-rRf] <dataset>', make sure it fail.
+#
+# $1 the collection of options
+# $2 the collection of datasets
+#
+function negative_test
+{
+ typeset options=$1
+ typeset datasets=$2
+
+ for dtst in $datasets; do
+ if ! is_global_zone; then
+ if [[ $dtst == $VOL || $dtst == $VOLSNAP || \
+ $dtst == $VOLCLONE ]]
+ then
+ log_note "UNSUPPORTED: " \
+ "Volume is unavailable in LZ."
+ continue
+ fi
+ fi
+ for opt in $options; do
+ log_mustnot $ZFS destroy $opt $dtst
+ done
+ done
+}
+
+# This filesystem is created by setup.ksh, and conflicts with the filesystems
+# created from within this file
+$ZFS destroy -f $TESTPOOL/$TESTFS
+
+#
+# Create snapshots for filesystem and volume,
+# and verify 'zfs destroy' failed without '-r' or '-R'.
+#
+setup_testenv snap
+negative_test "-f" "$CTR $FS $VOL"
+
+#
+# Create clones for filesystem and volume,
+# and verify 'zfs destroy' failed without '-R'.
+#
+setup_testenv clone
+negative_test "-r -rf" "$CTR $FS $VOL"
+
+#
+# Get $FS mountpoint and make it busy, then verify 'zfs destroy $CTR'
+# failed without '-f'.
+#
+# Then verify the datasets are expected existed or non-existed.
+#
+typeset mtpt_dir=$(get_prop mountpoint $FS)
+make_dir_busy $mtpt_dir
+negative_test "-R -rR" $CTR
+
+#
+# Checking the outcome of the test above is tricky, because the order in
+# which datasets are destroyed is not deterministic. Both $FS and $VOL are
+# busy, and the remaining datasets will be different depending on whether we
+# tried (and failed) to delete $FS or $VOL first.
+
+# The following datasets will exist independent of the order
+check_dataset datasetexists $CTR $FS $VOL
+
+if datasetexists $VOLSNAP && datasetnonexists $FSSNAP; then
+ # The recursive destroy failed on $FS
+ check_dataset datasetnonexists $FSSNAP $FSCLONE
+ check_dataset datasetexists $VOLSNAP $VOLCLONE
+elif datasetexists $FSSNAP && datasetnonexists $VOLSNAP; then
+ # The recursive destroy failed on $VOL
+ check_dataset datasetnonexists $VOLSNAP $VOLCLONE
+ check_dataset datasetexists $FSSNAP $FSCLONE
+else
+ log_must zfs list -rtall
+ log_fail "Unexpected datasets remaining"
+fi
+
+#
+# Create the clones for test environment, then verify 'zfs destroy $FS'
+# failed without '-f'.
+#
+# Then verify the datasets are expected existed or non-existed.
+#
+setup_testenv clone
+negative_test "-R -rR" $FS
+check_dataset datasetexists $CTR $FS $VOL $VOLSNAP $VOLCLONE
+log_must datasetnonexists $FSSNAP $FSCLONE
+
+make_dir_unbusy $mtpt_dir
+
+if is_global_zone; then
+ #
+ # Create the clones for test environment and make the volume busy.
+ # Then verify 'zfs destroy $CTR' failed without '-f'.
+ #
+ # Then verify the datasets are expected existed or non-existed.
+ #
+ setup_testenv clone
+ make_dir_busy $TESTDIR1
+ negative_test "-R -rR" $CTR
+ log_must datasetexists $CTR $VOL
+ log_must datasetnonexists $VOLSNAP $VOLCLONE
+
+ # Here again, the non-determinism of destroy order is a factor. $FS,
+ # $FSSNAP and $FSCLONE will still exist here iff we attempted to destroy
+ # $VOL (and failed) first. So check that either all of the datasets are
+ # present, or they're all gone.
+ if datasetexists $FS; then
+ check_dataset datasetexists $FS $FSSNAP $FSCLONE
+ else
+ check_dataset datasetnonexists $FS $FSSNAP $FSCLONE
+ fi
+
+ #
+ # Create the clones for test environment and make the volume busy.
+ # Then verify 'zfs destroy $VOL' failed without '-f'.
+ #
+ # Then verify the datasets are expected existed or non-existed.
+ #
+ setup_testenv clone
+ negative_test "-R -rR" $VOL
+ log_must datasetexists $CTR $VOL $FS $FSSNAP $FSCLONE
+ log_must datasetnonexists $VOLSNAP $VOLCLONE
+
+ make_dir_unbusy $TESTDIR1
+fi
+
+#
+# Create the clones for test environment and make the snapshot busy.
+# Then verify 'zfs destroy $snap' failed without '-f'.
+#
+# Then verify the datasets are expected existed or non-existed.
+#
+snaplist="$FSSNAP"
+
+setup_testenv clone
+for snap in $snaplist; do
+ for option in -R -rR ; do
+ mtpt_dir=$(snapshot_mountpoint $snap)
+ (( $? != 0 )) && \
+ log_fail "get mountpoint $snap failed."
+
+ init_dir=$PWD
+ log_must cd $mtpt_dir
+
+ log_must $ZFS destroy $option $snap
+ check_dataset datasetexists $CTR $FS $VOL
+ if [[ $snap == $FSSNAP ]]; then
+ log_must datasetnonexists $snap $FSCLONE
+ else
+ log_must datasetnonexists $snap $VOLCLONE
+ fi
+ setup_testenv clone
+ done
+done
+
+cmds="zfs destroy -f|-r|-rf|-R|-rR <dataset>"
+log_pass "'$cmds' must fail in certain conditions."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_006_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_006_neg.ksh
new file mode 100644
index 000000000000..517f538a69d9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_006_neg.ksh
@@ -0,0 +1,74 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_destroy_006_neg
+#
+# DESCRIPTION:
+# 'zfs destroy' should return an error with badly formed parameters,
+# including null destroyed object parameter, invalid options excluding
+# '-r' and '-f', non-existent datasets.
+#
+# STRATEGY:
+# 1. Create an array of parameters
+# 2. For each parameter in the array, execute 'zfs destroy'
+# 3. Verify an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+
+set -A args "" "-r" "-f" "-rf" "-fr" "$TESTPOOL" "-f $TESTPOOL" \
+ "-? $TESTPOOL/$TESTFS" "$TESTPOOL/blah"\
+ "-r $TESTPOOL/blah" "-f $TESTPOOL/blah" "-rf $TESTPOOL/blah" \
+ "-fr $TESTPOOL/blah" "-$ $TESTPOOL/$TESTFS" "-5 $TESTPOOL/$TESTFS" \
+ "-rfgh $TESTPOOL/$TESTFS" "-rghf $TESTPOOL/$TESTFS" \
+ "$TESTPOOL/$TESTFS@blah" "/$TESTPOOL/$TESTFS" "-f /$TESTPOOL/$TESTFS" \
+ "-rf /$TESTPOOL/$TESTFS" "$TESTPOOL/$TESTFS $TESTPOOL/$TESTFS" \
+ "-rRf $TESTPOOL/$TESTFS $TESTPOOL/$TESTFS"
+
+log_assert "'zfs destroy' should return an error with badly-formed parameters."
+
+typeset -i i=0
+while (( $i < ${#args[*]} )); do
+ log_mustnot $ZFS destroy ${args[i]}
+ ((i = i + 1))
+done
+
+log_pass "'zfs destroy' badly formed parameters fail as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_007_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_007_neg.ksh
new file mode 100644
index 000000000000..ce0fc1822a4c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_007_neg.ksh
@@ -0,0 +1,81 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_destroy_007_neg
+#
+# DESCRIPTION:
+# 'zpool destroy' failed if this filesystem is namespace-parent
+# of origin.
+#
+# STRATEGY:
+# 1. Create pool, fs and snapshot.
+# 2. Create a namespace-parent of origin clone.
+# 3. Promote this clone
+# 4. Verify the original fs can not be destroyed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-07-18)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if datasetexists $clonesnap; then
+ log_must $ZFS promote $fs
+ fi
+ datasetexists $clone && log_must $ZFS destroy $clone
+ datasetexists $fssnap && log_must $ZFS destroy $fssnap
+}
+
+log_assert "Destroy dataset which is namespace-parent of origin should failed."
+log_onexit cleanup
+
+# Define variable $fssnap & and namespace-parent of origin clone.
+fs=$TESTPOOL/$TESTFS
+fssnap=$fs@snap
+clone=$fs/clone
+clonesnap=$fs/clone@snap
+
+log_must $ZFS snapshot $fssnap
+log_must $ZFS clone $fssnap $clone
+log_must $ZFS promote $clone
+log_mustnot $ZFS destroy $fs
+log_mustnot $ZFS destroy $clone
+
+log_pass "Destroy dataset which is namespace-parent of origin passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_common.kshlib b/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_common.kshlib
new file mode 100644
index 000000000000..e3e035de248c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_common.kshlib
@@ -0,0 +1,168 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#
+# Create or recover a set of test environment which include ctr, vol, fs,
+# snap & clone. It looks like the following.
+#
+# pool
+# |ctr
+# | |fs
+# | | |fssnap
+# | |vol
+# | |volsnap
+# |fsclone
+# |volclone
+#
+# $1 indicate which dependent dataset need be created. Such as 'snap', 'clone'.
+#
+function setup_testenv #[dtst]
+{
+ typeset dtst=$1
+
+ if ! datasetexists $CTR; then
+ log_must $ZFS create $CTR
+ fi
+ if ! datasetexists $FS; then
+ log_must $ZFS create $FS
+ fi
+ # Volume test is only availible on globle zone
+ if ! datasetexists $VOL && is_global_zone; then
+ log_must $ZFS create -V $VOLSIZE $VOL
+ log_must $NEWFS /dev/zvol/$VOL
+
+ if [[ ! -d $TESTDIR1 ]]; then
+ log_must $MKDIR $TESTDIR1
+ fi
+ log_must $MOUNT /dev/zvol/$VOL $TESTDIR1
+ fi
+
+ if [[ $dtst == snap || $dtst == clone ]]; then
+ if ! datasetexists $FSSNAP; then
+ log_must $ZFS snapshot $FSSNAP
+ fi
+ if ! datasetexists $VOLSNAP && is_global_zone; then
+ log_must $ZFS snapshot $VOLSNAP
+ fi
+ fi
+
+ if [[ $dtst == clone ]]; then
+ if ! datasetexists $FSCLONE; then
+ log_must $ZFS clone $FSSNAP $FSCLONE
+ fi
+ if ! datasetexists $VOLCLONE && is_global_zone; then
+ log_must $ZFS clone $VOLSNAP $VOLCLONE
+ fi
+ fi
+}
+
+function make_dir_busy
+{
+ typeset dir=$1
+ typeset dirfiltered=$(echo $dir | sed -Ee 's,[/\.],_,g')
+
+ OLDPWD=$(pwd)
+ cd $dir
+ # Sleep for long enough for the test to have run through. Note that
+ # even if the test itself changes directory, sleep will still be on it.
+ $SLEEP $STF_TIMEOUT &
+ eval SLEEP_PID_${dirfiltered}=$!
+ pid=$(eval echo \$SLEEP_PID_${dirfiltered})
+ cd ${OLDPWD}
+ log_note "Sleeping while on ${dir} in pid $pid"
+}
+
+function make_dir_unbusy
+{
+ typeset dir=$1
+ typeset dirfiltered=$(echo $dir | sed -Ee 's,[/\.],_,g')
+ typeset pid=$(eval echo \$SLEEP_PID_${dirfiltered})
+
+ # Safeguard in case this is used incorrectly.
+ [[ -z "$pid" ]] && log_fail "make_dir_unbusy called without busy?"
+ $KILL -15 $pid
+ eval SLEEP_PID_${dirfiltered}=""
+ log_note "Unbusied ${dir}"
+}
+
+# Clean up the testing environment
+#
+function cleanup_testenv
+{
+ if [[ $STF_EXITCODE -eq $STF_FAIL ]]; then
+ $ECHO "Testcase failed; dataset listing follows:"
+ $ZFS list -t all -r $TESTPOOL
+ fi
+ if (( ${#init_dir} != 0 )); then
+ cd $init_dir
+ init_dir=""
+ fi
+ if is_global_zone && ismounted "$TESTDIR1" "ufs" ; then
+ log_must $UMOUNT -f $TESTDIR1
+ fi
+ if [[ -d $TESTDIR1 ]]; then
+ log_must $RM -rf $TESTDIR1
+ fi
+
+ [[ -n "$SLEEP_PID" ]] && $KILL -15 $SLEEP_PID
+
+ if datasetexists $CTR; then
+ log_must $ZFS destroy -Rf $CTR
+ fi
+}
+
+#
+# Delete volume and related datasets from list, if the test cases was
+# runing in local zone. Then check them are existed or non-exists.
+#
+# $1 function name
+# $2-n datasets name
+#
+function check_dataset
+{
+ typeset funname=$1
+ typeset newlist=""
+ typeset dtst
+ shift
+
+ for dtst in "$@"; do
+ # Volume and related stuff are unvailable in local zone
+ if ! is_global_zone; then
+ if [[ $dtst == $VOL || $dtst == $VOLSNAP || \
+ $dtst == $VOLCLONE ]]
+ then
+ continue
+ fi
+ fi
+ newlist="$newlist $dtst"
+ done
+
+ if (( ${#newlist} != 0 )); then
+ log_must eval "$funname $newlist"
+ fi
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_test.sh
new file mode 100755
index 000000000000..22d31e7211ac
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_destroy/zfs_destroy_test.sh
@@ -0,0 +1,226 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zfs_destroy_001_pos cleanup
+zfs_destroy_001_pos_head()
+{
+ atf_set "descr" "'zfs destroy -r|-R|-f|-rf|-Rf <fs|ctr|vol|snap>' shouldrecursively destroy all children."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 3600
+}
+zfs_destroy_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_destroy_common.kshlib
+ . $(atf_get_srcdir)/zfs_destroy.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_destroy_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_destroy_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_destroy_common.kshlib
+ . $(atf_get_srcdir)/zfs_destroy.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_destroy_002_pos cleanup
+zfs_destroy_002_pos_head()
+{
+ atf_set "descr" "Verify 'zfs destroy' can destroy the specified datasets without activedependents."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 3600
+}
+zfs_destroy_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_destroy_common.kshlib
+ . $(atf_get_srcdir)/zfs_destroy.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_destroy_002_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_destroy_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_destroy_common.kshlib
+ . $(atf_get_srcdir)/zfs_destroy.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_destroy_003_pos cleanup
+zfs_destroy_003_pos_head()
+{
+ atf_set "descr" "Verify that 'zfs destroy [-rR]' succeeds as root."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 3600
+}
+zfs_destroy_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_destroy_common.kshlib
+ . $(atf_get_srcdir)/zfs_destroy.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_destroy_003_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_destroy_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_destroy_common.kshlib
+ . $(atf_get_srcdir)/zfs_destroy.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_destroy_004_pos cleanup
+zfs_destroy_004_pos_head()
+{
+ atf_set "descr" "Verify that 'zfs destroy -f' succeeds as root."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 3600
+}
+zfs_destroy_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_destroy_common.kshlib
+ . $(atf_get_srcdir)/zfs_destroy.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_destroy_004_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_destroy_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_destroy_common.kshlib
+ . $(atf_get_srcdir)/zfs_destroy.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_destroy_005_neg cleanup
+zfs_destroy_005_neg_head()
+{
+ atf_set "descr" "Separately verify 'zfs destroy -f|-r|-rf|-R|-rR <dataset>' willfail in different conditions."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 3600
+}
+zfs_destroy_005_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_destroy_common.kshlib
+ . $(atf_get_srcdir)/zfs_destroy.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_destroy_005_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_destroy_005_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_destroy_common.kshlib
+ . $(atf_get_srcdir)/zfs_destroy.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_destroy_006_neg cleanup
+zfs_destroy_006_neg_head()
+{
+ atf_set "descr" "'zfs destroy' should return an error with badly-formed parameters."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 3600
+}
+zfs_destroy_006_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_destroy_common.kshlib
+ . $(atf_get_srcdir)/zfs_destroy.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_destroy_006_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_destroy_006_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_destroy_common.kshlib
+ . $(atf_get_srcdir)/zfs_destroy.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_destroy_007_neg cleanup
+zfs_destroy_007_neg_head()
+{
+ atf_set "descr" "Destroy dataset which is namespace-parent of origin should failed."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 3600
+}
+zfs_destroy_007_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_destroy_common.kshlib
+ . $(atf_get_srcdir)/zfs_destroy.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_destroy_007_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_destroy_007_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_destroy_common.kshlib
+ . $(atf_get_srcdir)/zfs_destroy.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zfs_destroy_001_pos
+ atf_add_test_case zfs_destroy_002_pos
+ atf_add_test_case zfs_destroy_003_pos
+ atf_add_test_case zfs_destroy_004_pos
+ atf_add_test_case zfs_destroy_005_neg
+ atf_add_test_case zfs_destroy_006_neg
+ atf_add_test_case zfs_destroy_007_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_diff/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zfs_diff/Makefile
new file mode 100644
index 000000000000..2b6288c76ba8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_diff/Makefile
@@ -0,0 +1,18 @@
+.include <src.opts.mk>
+
+PACKAGE= tests
+
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zfs_diff
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zfs_diff_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zfs_diff_001_pos.ksh
+${PACKAGE}FILES+= zfs_diff.cfg
+${PACKAGE}FILES+= zfs_diff_001_pos.golden
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_diff/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_diff/cleanup.ksh
new file mode 100644
index 000000000000..031560d89c63
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_diff/cleanup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2017 Spectra Logic Corp. All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_diff/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_diff/setup.ksh
new file mode 100644
index 000000000000..ba3bd8304ef9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_diff/setup.ksh
@@ -0,0 +1,32 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2017 Spectra Logic Corp. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_diff/zfs_diff.cfg b/tests/sys/cddl/zfs/tests/cli_root/zfs_diff/zfs_diff.cfg
new file mode 100644
index 000000000000..2577c648e102
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_diff/zfs_diff.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2017 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/tests/cli_root/cli.cfg
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_diff/zfs_diff_001_pos.golden b/tests/sys/cddl/zfs/tests/cli_root/zfs_diff/zfs_diff_001_pos.golden
new file mode 100644
index 000000000000..dffc9b67d68b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_diff/zfs_diff_001_pos.golden
@@ -0,0 +1,13 @@
++ /testdir1526/dirs/create
++ /testdir1526/files/create
+- /testdir1526/dirs/delete
+- /testdir1526/files/delete
+M /testdir1526/dirs
+M /testdir1526/dirs/modify
+M /testdir1526/files
+M /testdir1526/files/dstdir
+M /testdir1526/files/modify
+M /testdir1526/files/srcdir
+R /testdir1526/dirs/rename -> /testdir1526/dirs/rename.new
+R /testdir1526/files/rename -> /testdir1526/files/rename.new
+R /testdir1526/files/srcdir/move -> /testdir1526/files/dstdir/move
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_diff/zfs_diff_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_diff/zfs_diff_001_pos.ksh
new file mode 100644
index 000000000000..269965665390
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_diff/zfs_diff_001_pos.ksh
@@ -0,0 +1,77 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2017 Spectra Logic Corp. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_set/zfs_set_common.kshlib
+
+verify_runnable "both"
+
+log_assert "'zfs diff' output for typical operations"
+
+# First create a bunch of files and directories
+
+#log_must ${CD} $TESTDIR
+log_must ${MKDIR} ${TESTDIR}/dirs
+log_must ${MKDIR} ${TESTDIR}/dirs/leavealone
+log_must ${MKDIR} ${TESTDIR}/dirs/modify
+log_must ${MKDIR} ${TESTDIR}/dirs/rename
+log_must ${MKDIR} ${TESTDIR}/dirs/delete
+log_must ${MKDIR} ${TESTDIR}/files
+log_must ${TOUCH} ${TESTDIR}/files/leavealone
+log_must ${TOUCH} ${TESTDIR}/files/modify
+log_must ${TOUCH} ${TESTDIR}/files/rename
+log_must ${TOUCH} ${TESTDIR}/files/delete
+log_must ${MKDIR} ${TESTDIR}/files/srcdir
+log_must ${MKDIR} ${TESTDIR}/files/dstdir
+log_must ${TOUCH} ${TESTDIR}/files/srcdir/move
+
+log_must $ZFS snapshot $TESTPOOL/$TESTFS@1
+
+# Now modify them in different ways
+log_must ${TOUCH} ${TESTDIR}/dirs/modify
+log_must ${MV} ${TESTDIR}/dirs/rename ${TESTDIR}/dirs/rename.new
+log_must ${RMDIR} ${TESTDIR}/dirs/delete
+log_must ${MKDIR} ${TESTDIR}/dirs/create
+log_must ${DATE} >> ${TESTDIR}/files/modify
+log_must ${MV} ${TESTDIR}/files/rename ${TESTDIR}/files/rename.new
+log_must ${RM} ${TESTDIR}/files/delete
+log_must ${MV} ${TESTDIR}/files/srcdir/move ${TESTDIR}/files/dstdir/move
+log_must ${TOUCH} ${TESTDIR}/files/create
+
+log_must $ZFS snapshot $TESTPOOL/$TESTFS@2
+
+# "zfs diff"'s output order is unspecified, so we must sort it. The golden
+# file is already sorted.
+LC_ALL=C $ZFS diff $TESTPOOL/$TESTFS@1 $TESTPOOL/$TESTFS@2 | ${SORT} > $TESTDIR/zfs_diff_output.txt
+if [ $? -ne 0 ]; then
+ log_fail "zfs diff failed"
+fi
+
+# Finally, compare output to the golden output
+log_must diff $STF_SUITE/tests/cli_root/zfs_diff/zfs_diff_001_pos.golden $TESTDIR/zfs_diff_output.txt
+
+log_pass "'zfs diff' gave the expected output"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_diff/zfs_diff_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zfs_diff/zfs_diff_test.sh
new file mode 100755
index 000000000000..626946377d85
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_diff/zfs_diff_test.sh
@@ -0,0 +1,54 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2017 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zfs_diff_001_pos cleanup
+zfs_diff_001_pos_head()
+{
+ atf_set "descr" "zfs diff output for typical operations"
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_diff_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_diff.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_diff_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_diff_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_diff.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zfs_diff_001_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_get/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/Makefile
new file mode 100644
index 000000000000..140e5634d23f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/Makefile
@@ -0,0 +1,27 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zfs_get
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zfs_get_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zfs_get_002_pos.ksh
+${PACKAGE}FILES+= zfs_get_007_neg.ksh
+${PACKAGE}FILES+= zfs_get_003_pos.ksh
+${PACKAGE}FILES+= zfs_get_list_d.kshlib
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zfs_get_006_neg.ksh
+${PACKAGE}FILES+= zfs_get_009_pos.ksh
+${PACKAGE}FILES+= zfs_get_001_pos.ksh
+${PACKAGE}FILES+= zfs_get_010_neg.ksh
+${PACKAGE}FILES+= zfs_get.cfg
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zfs_get_004_pos.ksh
+${PACKAGE}FILES+= zfs_get_008_pos.ksh
+${PACKAGE}FILES+= zfs_get_common.kshlib
+${PACKAGE}FILES+= zfs_get_005_neg.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_get/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/cleanup.ksh
new file mode 100644
index 000000000000..713cb16bb31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_get/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/setup.ksh
new file mode 100644
index 000000000000..4ef170bda6d3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_container_volume_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get.cfg b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get.cfg
new file mode 100644
index 000000000000..03e21b4ba540
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get.cfg
@@ -0,0 +1,29 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
+
+export TESTSNAP=testsnap${TESTCASE_ID}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_001_pos.ksh
new file mode 100644
index 000000000000..b85153573df3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_001_pos.ksh
@@ -0,0 +1,155 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_get/zfs_get_common.kshlib
+. $STF_SUITE/tests/cli_root/zfs_get/zfs_get_list_d.kshlib
+. $STF_SUITE/tests/userquota/userquota_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_get_001_pos
+#
+# DESCRIPTION:
+# Setting the valid option and properties, 'zfs get' should return the
+# correct property value.
+#
+# STRATEGY:
+# 1. Create pool, filesystem, volume and snapshot.
+# 2. Setting valid parameter, 'zfs get' should succeed.
+# 3. Compare the output property name with the original input property.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+set -A options "" "-p" "-r" "-H"
+if zfs_get_list_d_supported ; then
+ typeset -i i=${#options[*]}
+ typeset -i j=0
+ while (( j<${#depth_options[*]} ));
+ do
+ options[$i]=-"${depth_options[$j]}"
+ (( j+=1 ))
+ (( i+=1 ))
+ done
+fi
+
+set -A zfs_props type used available creation volsize referenced \
+ compressratio mounted origin recordsize quota reservation mountpoint \
+ sharenfs checksum compression atime devices exec readonly setuid \
+ snapdir aclmode aclinherit canmount primarycache secondarycache \
+ usedbychildren usedbydataset usedbyrefreservation usedbysnapshots
+
+
+$ZFS upgrade -v > /dev/null 2>&1
+if [[ $? -eq 0 ]]; then
+ set -A zfs_props ${zfs_props[*]} version
+fi
+
+if is_userquota_supported; then
+ set -A userquota_props userquota@root groupquota@root \
+ userused@root groupused@root
+fi
+
+set -A all_props -- "${zfs_props[@]}" "${userquota_props[@]}"
+
+set -A dataset $TESTPOOL/$TESTCTR $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL \
+ $TESTPOOL/$TESTFS@$TESTSNAP $TESTPOOL/$TESTVOL@$TESTSNAP
+
+#
+# According to dataset and option, checking if 'zfs get' return correct
+# property information.
+#
+# $1 dataset
+# $2 properties which are expected to output into $TESTDIR/$TESTFILE0
+# $3 option
+#
+function check_return_value
+{
+ typeset dst=$1
+ typeset props=$2
+ typeset opt=$3
+ typeset -i found=0
+ typeset p
+
+ for p in $props; do
+ found=0
+
+ while read line; do
+ typeset item
+ item=$($ECHO $line | $AWK '{print $2}' 2>&1)
+
+ if [[ $item == $p ]]; then
+ (( found += 1 ))
+ break
+ fi
+ done < $TESTDIR/$TESTFILE0
+
+ if (( found == 0 )); then
+ log_fail "'zfs get $opt $props $dst' return " \
+ "error message.'$p' haven't been found."
+ fi
+ done
+
+ log_note "SUCCESS: '$ZFS get $opt $prop $dst'."
+}
+
+log_assert "Setting the valid options and properties 'zfs get' should return " \
+ "the correct property value."
+log_onexit cleanup
+
+# Create filesystem and volume's snapshot
+create_snapshot $TESTPOOL/$TESTFS $TESTSNAP
+create_snapshot $TESTPOOL/$TESTVOL $TESTSNAP
+
+typeset -i i=0
+while (( i < ${#dataset[@]} )); do
+ for opt in "${options[@]}"; do
+ for prop in ${all_props[@]}; do
+ eval "$ZFS get $opt $prop ${dataset[i]} > \
+ $TESTDIR/$TESTFILE0"
+ ret=$?
+ if [[ $ret != 0 ]]; then
+ log_fail "$ZFS get returned: $ret"
+ fi
+ check_return_value ${dataset[i]} "$prop" "$opt"
+ done
+ done
+ (( i += 1 ))
+done
+
+log_pass "Setting the valid options to dataset, it should succeed and return " \
+ "valid value. 'zfs get' pass."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_002_pos.ksh
new file mode 100644
index 000000000000..bc4c5120de02
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_002_pos.ksh
@@ -0,0 +1,111 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_get/zfs_get_common.kshlib
+. $STF_SUITE/tests/userquota/userquota_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_get_002_pos
+#
+# DESCRIPTION:
+# Setting the valid option and properties 'zfs get' return correct value.
+# It should be successful.
+#
+# STRATEGY:
+# 1. Create pool, filesystem, dataset, volume and snapshot.
+# 2. Getting the options and properties random combination.
+# 3. Using the combination as the parameters of 'zfs get' to check the
+# command line return value.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+set -A options " " p r H
+
+set -A zfs_props type used available creation volsize referenced compressratio \
+ mounted origin recordsize quota reservation mountpoint sharenfs \
+ checksum compression atime devices exec readonly setuid snapdir \
+ aclmode aclinherit canmount primarycache secondarycache \
+ usedbychildren usedbydataset usedbyrefreservation usedbysnapshots
+
+
+$ZFS upgrade -v > /dev/null 2>&1
+if [[ $? -eq 0 ]]; then
+ set -A zfs_props ${zfs_props[*]} version
+fi
+
+if is_userquota_supported; then
+ set -A userquota_props userquota@root groupquota@root \
+ userused@root groupused@root
+fi
+
+set -A props -- "${zfs_props[@]}" "${userquota_props[@]}"
+
+set -A dataset $TESTPOOL/$TESTCTR $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL \
+ $TESTPOOL/$TESTFS@$TESTSNAP $TESTPOOL/$TESTVOL@$TESTSNAP
+
+log_assert "Setting the valid options and properties 'zfs get' return correct "\
+ "value. It should be successful."
+log_onexit cleanup
+
+# Create volume and filesystem's snapshot
+create_snapshot $TESTPOOL/$TESTFS $TESTSNAP
+create_snapshot $TESTPOOL/$TESTVOL $TESTSNAP
+
+#
+# Begin to test 'get [-prH] <property[,property]...>
+# <filesystem|dataset|volume|snapshot>'
+# 'get [-prH] <-a|-d> <filesystem|dataset|volume|snapshot>"
+#
+typeset -i opt_numb=8
+typeset -i prop_numb=20
+for dst in ${dataset[@]}; do
+ # option can be empty, so "" is necessary.
+ for opt in "" $(gen_option_str "${options[*]}" "-" "" $opt_numb); do
+ for prop in $(gen_option_str "${props[*]}" "" "," $prop_numb)
+ do
+ $ZFS get $opt $prop $dst > /dev/null 2>&1
+ ret=$?
+ if [[ $ret != 0 ]]; then
+ log_fail "$ZFS get $opt $prop $dst (Code: $ret)"
+ fi
+ done
+ done
+done
+
+log_pass "Setting the valid options to dataset, 'zfs get' pass."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_003_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_003_pos.ksh
new file mode 100644
index 000000000000..3a67ce17ec18
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_003_pos.ksh
@@ -0,0 +1,74 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_get_003_pos
+#
+# DESCRIPTION:
+# 'zfs get' should get consistent report with different options.
+#
+# STRATEGY:
+# 1. Create pool and filesystem.
+# 2. 'zfs mount -o update,noatime <fs>.'
+# 3. Verify the value of 'zfs get atime' and 'zfs get all | grep atime'
+# are identical.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-07-18)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ log_must $ZFS mount -o update,atime $TESTPOOL/$TESTFS
+}
+
+log_assert "'zfs get' should get consistent report with different option."
+log_onexit cleanup
+
+log_must $ZFS set atime=on $TESTPOOL/$TESTFS
+log_must $ZFS mount -o update,noatime $TESTPOOL/$TESTFS
+
+value1=$($ZFS get -H atime $TESTPOOL/$TESTFS | $AWK '{print $3}')
+value2=$($ZFS get -H all $TESTPOOL/$TESTFS | $AWK '{print $2 " " $3}' | \
+ $GREP ^atime | $AWK '{print $2}')
+if [[ $value1 != $value2 ]]; then
+ log_fail "value1($value1) != value2($value2)"
+fi
+
+log_pass "'zfs get' get consistent report with different option passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_004_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_004_pos.ksh
new file mode 100644
index 000000000000..338dc9e346e6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_004_pos.ksh
@@ -0,0 +1,241 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_get_004_pos
+#
+# DESCRIPTION:
+# Verify 'zfs get all' can get all properties for all datasets in the system
+#
+# STRATEGY:
+# 1. Create datasets for testing
+# 2. Issue 'zfs get all' command
+# 3. Verify the command gets all available properties of all datasets
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-08-31)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+#check 'zfs get all' supportability with the installed OS version
+$ZFS get all >/dev/null 2>&1
+(( $? != 0 )) && log_unsupported "ZFS get all option is unsupported."
+
+
+function cleanup
+{
+ [[ -e $propfile ]] && $RM -f $propfile
+
+ datasetexists $clone && \
+ log_must $ZFS destroy $clone
+ for snap in $fssnap $volsnap ; do
+ snapexists $snap && \
+ log_must $ZFS destroy $snap
+ done
+
+ if [[ -n $globalzone ]] ; then
+ for pool in $TESTPOOL1 $TESTPOOL2 $TESTPOOL3; do
+ poolexists $pool && \
+ log_must $ZPOOL destroy -f $pool
+ done
+ for file in `$LS $TESTDIR1/poolfile*`; do
+ $RM -f $file
+ done
+ else
+ for fs in $TESTPOOL/$TESTFS1 $TESTPOOL/$TESTFS2 $TESTPOOL/$TESTFS3; do
+ datasetexists $fs && \
+ log_must $ZFS destroy -rf $fs
+ done
+ fi
+}
+
+log_assert "Verify the functions of 'zfs get all' work."
+log_onexit cleanup
+
+typeset globalzone=""
+
+if is_global_zone ; then
+ globalzone="true"
+fi
+
+set -A opts "" "-r" "-H" "-p" "-rHp" "-o name" \
+ "-s local,default,temporary,inherited,none" \
+ "-o name -s local,default,temporary,inherited,none" \
+ "-rHp -o name -s local,default,temporary,inherited,none"
+set -A usrprops "a:b=c" "d_1:1_e=0f" "123:456=789"
+
+fs=$TESTPOOL/$TESTFS
+fssnap=$fs@$TESTSNAP
+clone=$TESTPOOL/$TESTCLONE
+volsnap=$TESTPOOL/$TESTVOL@$TESTSNAP
+
+#set user defined properties for $TESTPOOL
+for usrprop in ${usrprops[@]}; do
+ log_must $ZFS set $usrprop $TESTPOOL
+done
+# create snapshot and clone in $TESTPOOL
+log_must $ZFS snapshot $fssnap
+log_must $ZFS clone $fssnap $clone
+log_must $ZFS snapshot $volsnap
+
+# collect datasets which can be set user defined properties
+usrpropds="$clone $fs"
+
+# collect all datasets which we are creating
+allds="$fs $clone $fssnap $volsnap"
+
+#create pool and datasets to guarantee testing under multiple pools and datasets.
+file=$TESTDIR1/poolfile
+typeset -i FILESIZE=104857600 #100M
+(( DFILESIZE = FILESIZE * 2 )) # double of FILESIZE
+typeset -i VOLSIZE=10485760 #10M
+availspace=$(get_prop available $TESTPOOL)
+typeset -i i=0
+
+# make sure 'availspace' is larger then twice of FILESIZE to create a new pool.
+# If any, we only totally create 3 pools for multple datasets testing to limit
+# testing time
+while (( availspace > DFILESIZE )) && (( i < 3 )) ; do
+ (( i += 1 ))
+
+ if [[ -n $globalzone ]] ; then
+ log_must create_vdevs ${file}$i
+ eval pool=\$TESTPOOL$i
+ log_must $ZPOOL create $pool ${file}$i
+ else
+ eval pool=$TESTPOOL/\$TESTFS$i
+ log_must $ZFS create $pool
+ fi
+
+ #set user defined properties for testing
+ for usrprop in ${usrprops[@]}; do
+ log_must $ZFS set $usrprop $pool
+ done
+
+ #create datasets in pool
+ log_must $ZFS create $pool/$TESTFS
+ log_must $ZFS snapshot $pool/$TESTFS@$TESTSNAP
+ log_must $ZFS clone $pool/$TESTFS@$TESTSNAP $pool/$TESTCLONE
+
+ if [[ -n $globalzone ]] ; then
+ log_must $ZFS create -V $VOLSIZE $pool/$TESTVOL
+ else
+ log_must $ZFS create $pool/$TESTVOL
+ fi
+
+ ds=`$ZFS list -H -r -o name -t filesystem,volume $pool`
+ usrpropds="$usrpropds $pool/$TESTFS $pool/$TESTCLONE $pool/$TESTVOL"
+ allds="$allds $pool/$TESTFS $pool/$TESTCLONE $pool/$TESTVOL \
+ $pool/$TESTFS@$TESTSNAP"
+
+ availspace=$(get_prop available $TESTPOOL)
+done
+
+#the expected number of property for each type of dataset in this testing
+typeset -i fspropnum=27
+typeset -i snappropnum=8
+typeset -i volpropnum=15
+propfile=$TMPDIR/allpropfile.${TESTCASE_ID}
+
+typeset -i i=0
+typeset -i propnum=0
+typeset -i failflag=0
+while (( i < ${#opts[*]} )); do
+ [[ -e $propfile ]] && $RM -f $propfile
+ log_must eval "$ZFS get ${opts[i]} all >$propfile"
+
+ for ds in $allds; do
+ $GREP $ds $propfile >/dev/null 2>&1
+ (( $? != 0 )) && \
+ log_fail "There is no property for" \
+ "dataset $ds in 'get all' output."
+
+ propnum=`$CAT $propfile | $AWK '{print $1}' | \
+ $GREP "${ds}$" | $WC -l`
+ ds_type=`$ZFS get -H -o value type $ds`
+ case $ds_type in
+ filesystem )
+ (( propnum < fspropnum )) && \
+ (( failflag += 1 ))
+ ;;
+ snapshot )
+ (( propnum < snappropnum )) && \
+ (( failflag += 1 ))
+ ;;
+ volume )
+ (( propnum < volpropnum )) && \
+ (( failflag += 1 ))
+ ;;
+ esac
+
+ (( failflag != 0 )) && \
+ log_fail " 'zfs get all' fails to get out " \
+ "all properties for dataset $ds."
+
+ (( propnum = 0 ))
+ (( failflag = 0 ))
+ done
+
+ (( i += 1 ))
+done
+
+log_note "'zfs get' can get particular property for all datasets with that property."
+
+function do_particular_prop_test #<property> <suitable datasets>
+{
+ typeset props="$1"
+ typeset ds="$2"
+
+ for prop in ${commprops[*]}; do
+ ds=`$ZFS get -H -o name $prop`
+
+ [[ "$ds" != "$allds" ]] && \
+ log_fail "The result datasets are $ds, but all suitable" \
+ "datasets are $allds for the property $prop"
+ done
+}
+
+# Here, we do a testing for user defined properties and the most common properties
+# for all datasets.
+commprop="type creation used referenced compressratio"
+usrprop="a:b d_1:1_e 123:456"
+
+do_particular_prop_test "$commprop" "$allds"
+do_particular_prop_test "$usrprop" "$usrpropds"
+
+log_pass "'zfs get all' works as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_005_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_005_neg.ksh
new file mode 100644
index 000000000000..632d40c1b1a8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_005_neg.ksh
@@ -0,0 +1,135 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_get/zfs_get_common.kshlib
+. $STF_SUITE/tests/userquota/userquota_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_get_005_neg
+#
+# DESCRIPTION:
+# Setting the invalid option and properties, 'zfs get' should failed.
+#
+# STRATEGY:
+# 1. Create pool, filesystem, volume and snapshot.
+# 2. Getting incorrect combination by invalid parameters
+# 3. Using the combination as the parameters of 'zfs get' to check the
+# command line return value.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+set -A val_opts p r H
+set -A v_props type used available creation volsize referenced compressratio mounted \
+ origin recordsize quota reservation mountpoint sharenfs checksum \
+ compression atime devices exec readonly setuid zoned snapdir aclmode \
+ aclinherit canmount primarycache secondarycache \
+ usedbychildren usedbydataset usedbyrefreservation usedbysnapshots
+
+$ZFS upgrade -v > /dev/null 2>&1
+if [[ $? -eq 0 ]]; then
+ set -A v_props ${v_props[*]} version
+fi
+
+if is_userquota_supported; then
+ set -A userquota_props userquota@root groupquota@root \
+ userused@root groupused@root
+fi
+
+set -A val_pros -- "${v_props[@]}" "${userquota_props[@]}"
+
+set -f # Force shell does not parse '?' and '*' as the wildcard
+set -A inval_opts P R h ? *
+set -A inval_props Type 0 ? * -on --on readonl time USED RATIO MOUNTED
+
+set -A dataset $TESTPOOL/$TESTFS $TESTPOOL/$TESTCTR $TESTPOOL/$TESTVOL \
+ $TESTPOOL/$TESTFS@$TESTSNAP $TESTPOOL/$TESTVOL@$TESTSNAP
+
+typeset -i opt_numb=6
+typeset -i prop_numb=12
+
+val_opts_str=$(gen_option_str "${val_opts[*]}" "-" "" $opt_numb)
+val_props_str=$(gen_option_str "${val_props[*]}" "" "," $prop_numb)
+val_props_str="$val_props_str -a -d"
+
+inval_opts_str=$(gen_option_str "${inval_opts[*]}" "-" "" $opt_numb)
+inval_props_str=$(gen_option_str "${inval_props[*]}" "" "," $prop_numb)
+
+#
+# Test different options and properties combination.
+#
+# $1 options
+# $2 properties
+#
+function test_options
+{
+ typeset opts=$1
+ typeset props=$2
+
+ for dst in ${dataset[@]}; do
+ for opt in $opts; do
+ for prop in $props; do
+ $ZFS get $opt $prop $dst > /dev/null 2>&1
+ ret=$?
+ if [[ $ret == 0 ]]; then
+ log_fail "$ZFS get \
+ $opt $prop $dst unexpectedly succeeded."
+ fi
+ done
+ done
+ done
+}
+
+log_assert "Setting the invalid option and properties, 'zfs get' should be \
+ failed."
+log_onexit cleanup
+
+# Create filesystem and volume's snapshot
+create_snapshot $TESTPOOL/$TESTFS $TESTSNAP
+create_snapshot $TESTPOOL/$TESTVOL $TESTSNAP
+
+log_note "Valid options + invalid properties, 'zfs get' should fail."
+test_options "$val_opts_str" "$inval_props_str"
+
+log_note "Invalid options + valid properties, 'zfs get' should fail."
+test_options "$inval_opts_str" "$val_props_str"
+
+log_note "Invalid options + invalid properties, 'zfs get' should fail."
+test_options "$inval_opts_str" "$inval_props_str"
+
+log_pass "Setting the invalid options to dataset, 'zfs get' pass."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_006_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_006_neg.ksh
new file mode 100644
index 000000000000..5cb8e0506870
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_006_neg.ksh
@@ -0,0 +1,73 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_get/zfs_get_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_get_006_neg
+#
+# DESCRIPTION:
+# Verify 'zfs get all' can deal with invalid scenarios
+#
+# STRATEGY:
+# 1. Define invalid scenarios for 'zfs get all'
+# 2. Run zfs get with those invalid scenarios
+# 3. Verify that zfs get fails with invalid scenarios
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-08-31)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify 'zfs get all' fails with invalid combination scenarios."
+
+set -f # Force ksh ignore '?' and '*'
+set -A bad_combine "ALL" "\-R all" "-P all" "-h all" "-rph all" "-RpH all" "-PrH all" \
+ "-o all" "-s all" "-? all" "-* all" "-?* all" "all -r" "all -p" \
+ "all -H" "all -rp" "all -rH" "all -ph" "all -rpH" "all -r $TESTPOOL" \
+ "all -H $TESTPOOL" "all -p $TESTPOOL" "all -r -p -H $TESTPOOL" \
+ "all -rph $TESTPOOL" "all,available,reservation $TESTPOOL" \
+ "all $TESTPOOL?" "all $TESTPOOL*" "all nonexistpool"
+
+typeset -i i=0
+while (( i < ${#bad_combine[*]} ))
+do
+ log_mustnot eval "$ZFS get ${bad_combine[i]} >/dev/null"
+
+ (( i = i + 1 ))
+done
+
+log_pass "'zfs get all' fails with invalid combinations scenarios as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_007_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_007_neg.ksh
new file mode 100644
index 000000000000..d533eff60580
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_007_neg.ksh
@@ -0,0 +1,73 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_get/zfs_get_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_get_007_neg
+#
+# DESCRIPTION:
+# 'zfs get -o' should fail with invalid column names
+#
+# STRATEGY:
+# 1. Run zfs get -o with invalid column name combinations
+# 2. Verify that zfs get returns error
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-21)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "'zfs get -o' fails with invalid options or column names"
+
+set -A badargs "o name,property,value,resource" "o name" \
+ "-O name,property,value,source" "-oo name" "-o blah" \
+ "-o name,property,blah,source" "-o name,name,name,name,name" \
+ "-o name,property,value,," "-o *,*,*,*" "-o ?,?,?,?" \
+ "-o" "-o ,,,,," "-o -o -o -o" "-o NAME,PROPERTY,VALUE,SOURCE" \
+ "-o name,properTy,value,source" "-o name, property, value,source" \
+ "-o name:property:value:source" "-o name,property:value,source" \
+ "-o name;property;value;source"
+
+typeset -i i=0
+while (( i < ${#badargs[*]} ))
+do
+ log_mustnot eval "$ZFS get \"${badargs[i]}\" >/dev/null 2>&1"
+
+ (( i = i + 1 ))
+done
+
+log_pass "'zfs get -o' fails with invalid options or column name as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_008_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_008_pos.ksh
new file mode 100644
index 000000000000..41c412ea7ab5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_008_pos.ksh
@@ -0,0 +1,108 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_get/zfs_get_common.kshlib
+. $STF_SUITE/tests/cli_root/zfs_get/zfs_get_list_d.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_get_008_pos
+#
+# DESCRIPTION:
+# Verify "-d <n>" can work with other options
+#
+# STRATEGY:
+# 1. Create pool, filesystem, dataset, volume and snapshot.
+# 2. Getting an -d option, other options and properties random combination.
+# 3. Using the combination as the parameters of 'zfs get' to check the
+# command line return value.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-05-22)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+if ! zfs_get_list_d_supported ; then
+ log_unsupported "'zfs get -d' is not supported."
+fi
+
+set -A options " " "-r" "-H" "-p" "-rHp" "-o name" \
+ "-s local,default,temporary,inherited,none" \
+ "-o name -s local,default,temporary,inherited,none" \
+ "-rHp -o name -s local,default,temporary,inherited,none"
+
+set -A props type used available creation volsize referenced compressratio \
+ mounted origin recordsize quota reservation mountpoint sharenfs \
+ checksum compression atime devices exec readonly setuid snapdir \
+ aclmode aclinherit canmount primarycache secondarycache \
+ usedbychildren usedbydataset usedbyrefreservation usedbysnapshots \
+ userquota@root groupquota@root userused@root groupused@root
+
+$ZFS upgrade -v > /dev/null 2>&1
+if [[ $? -eq 0 ]]; then
+ set -A all_props ${all_props[*]} version
+fi
+
+set -A dataset $TESTPOOL/$TESTCTR $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL \
+ $TESTPOOL/$TESTFS@$TESTSNAP $TESTPOOL/$TESTVOL@$TESTSNAP
+
+log_assert "Verify '-d <n>' can work with other options"
+log_onexit cleanup
+
+# Create volume and filesystem's snapshot
+create_snapshot $TESTPOOL/$TESTFS $TESTSNAP
+create_snapshot $TESTPOOL/$TESTVOL $TESTSNAP
+
+typeset -i opt_numb=16
+typeset -i prop_numb=16
+typeset -i i=0
+typeset -i item=0
+typeset -i depth_item=0
+
+for dst in ${dataset[@]}; do
+ (( i=0 ))
+ while (( i < opt_numb )); do
+ (( item = $RANDOM % ${#options[@]} ))
+ (( depth_item = $RANDOM % ${#depth_options[@]} ))
+ for prop in $(gen_option_str "${props[*]}" "" "," $prop_numb)
+ do
+ log_must eval "$ZFS get -${depth_options[depth_item]} ${options[item]} $prop $dst > /dev/null 2>&1"
+ done
+ (( i += 1 ))
+ done
+done
+
+log_pass "Verify '-d <n>' can work with other options"
+
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_009_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_009_pos.ksh
new file mode 100644
index 000000000000..5a13bdc8318f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_009_pos.ksh
@@ -0,0 +1,100 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_get/zfs_get_common.kshlib
+. $STF_SUITE/tests/cli_root/zfs_get/zfs_get_list_d.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_get_009_pos
+#
+# DESCRIPTION:
+# 'zfs get -d <n>' should get expected output.
+#
+# STRATEGY:
+# 1. Create a multiple depth filesystem.
+# 2. 'zfs get -d <n>' to get the output.
+# 3. 'zfs get -r|egrep' to get the expected output.
+# 4. Compare the two outputs, they shoud be same.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-05-20)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+if ! zfs_get_list_d_supported ; then
+ log_unsupported "'zfs get -d' is not supported."
+fi
+
+log_assert "'zfs get -d <n>' should get expected output."
+log_onexit depth_fs_cleanup
+
+set -A all_props type used available creation volsize referenced \
+ compressratio mounted origin recordsize quota reservation mountpoint \
+ sharenfs checksum compression atime devices exec readonly setuid \
+ snapdir aclmode aclinherit canmount primarycache secondarycache \
+ usedbychildren usedbydataset usedbyrefreservation usedbysnapshots \
+ userquota@root groupquota@root userused@root groupused@root
+
+$ZFS upgrade -v > /dev/null 2>&1
+if [[ $? -eq 0 ]]; then
+ set -A all_props ${all_props[*]} version
+fi
+
+depth_fs_setup
+
+mntpnt=$(get_prop mountpoint $DEPTH_FS)
+DEPTH_OUTPUT="$mntpnt/depth_output"
+EXPECT_OUTPUT="$mntpnt/expect_output"
+typeset -i prop_numb=16
+typeset -i old_val=0
+typeset -i j=0
+typeset eg_opt="$DEPTH_FS"$
+for dp in ${depth_array[@]}; do
+ (( j=old_val+1 ))
+ while (( j<=dp && j<=MAX_DEPTH )); do
+ eg_opt="$eg_opt""|d""$j"$
+ (( j+=1 ))
+ done
+ for prop in $(gen_option_str "${all_props[*]}" "" "," $prop_numb); do
+ log_must eval "$ZFS get -H -d $dp -o name $prop $DEPTH_FS > $DEPTH_OUTPUT"
+ log_must eval "$ZFS get -rH -o name $prop $DEPTH_FS | $EGREP -e '$eg_opt' > $EXPECT_OUTPUT"
+ log_must $DIFF $DEPTH_OUTPUT $EXPECT_OUTPUT
+ done
+ (( old_val=dp ))
+done
+
+log_pass "'zfs get -d <n>' should get expected output."
+
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_010_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_010_neg.ksh
new file mode 100644
index 000000000000..2ac48a9af81d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_010_neg.ksh
@@ -0,0 +1,72 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_get/zfs_get_common.kshlib
+. $STF_SUITE/tests/cli_root/zfs_get/zfs_get_list_d.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_get_010_neg
+#
+# DESCRIPTION:
+# A negative depth or a non numeric depth should fail in 'zfs get -d <n>'
+#
+# STRATEGY:
+# 1. Run zfs get -d with negative depth or non numeric depth
+# 2. Verify that zfs get returns error
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-05-22)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+if ! zfs_get_list_d_supported ; then
+ log_unsupported "'zfs get -d' is not supported."
+fi
+
+log_assert "A negative depth or a non numeric depth should fail in 'zfs get -d <n>'"
+
+set -A badargs "a" "AB" "aBc" "2A" "a2b" "aB2" "-1" "-32" "-999"
+
+typeset -i i=0
+while (( i < ${#badargs[*]} ))
+do
+ log_mustnot eval "$ZFS get -d ${badargs[i]} $TESTPOOL/$TESTFS >/dev/null 2>&1"
+ (( i = i + 1 ))
+done
+
+log_pass "A negative depth or a non numeric depth should fail in 'zfs get -d <n>'"
+
+
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_common.kshlib b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_common.kshlib
new file mode 100644
index 000000000000..6b677e6f5bd1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_common.kshlib
@@ -0,0 +1,97 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#
+# According to $elements, $prefix and $separator, the function random produce
+# the number of $counter combination.
+#
+# $1 elements which is used to get the combination.
+# $2 prefix is appended to the combination
+# $3 separator between the combination, such as ' ' or ','
+# $4 counter is the number of combination which you want to get.
+#
+function gen_option_str # $elements $prefix $separator $counter
+{
+ typeset elements=""
+ typeset prefix=${2}
+ typeset separator=${3}
+ typeset -i counter=${4:-0}
+ typeset -i i=0
+ typeset comb_str=""
+
+ for e in $1; do
+ elements[i]="$e"
+ (( i += 1 ))
+ done
+ (( ${#elements[@]} == 0 )) && log_fail "The elements can't be empty."
+
+ typeset -i item=0
+ typeset -i j=0
+ typeset -i numb_item=0
+
+ # Loop and get the specified number combination strings.
+ i=0
+ while (( i < counter )); do
+ j=0
+ numb_item=0
+ comb_str=""
+
+ # Get random number items for each combinations.
+ (( numb_item = ($RANDOM % ${#elements[@]}) + 1 ))
+
+ while (( j < numb_item )); do
+ # Random select elements from the array
+ (( item = $RANDOM % ${#elements[@]} ))
+
+ if (( ${#comb_str} == 0 )); then
+ comb_str=${elements[item]}
+ else
+ comb_str=$comb_str$separator${elements[item]}
+ fi
+ (( j += 1 ))
+ done
+
+ print "$prefix$comb_str"
+
+ (( i += 1 ))
+ done
+}
+
+#
+# Cleanup the volume snapshot and filesystem snapshot were created for
+# this test case.
+#
+function cleanup
+{
+ datasetexists $TESTPOOL/$TESTVOL@$TESTSNAP && \
+ destroy_snapshot $TESTPOOL/$TESTVOL@$TESTSNAP
+ datasetexists $TESTPOOL/$TESTFS@$TESTSNAP && \
+ destroy_snapshot $TESTPOOL/$TESTFS@$TESTSNAP
+
+ [[ -e $TESTFILE0 ]] && log_must $RM $TESTFILE0
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_list_d.kshlib b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_list_d.kshlib
new file mode 100644
index 000000000000..c43c1e80a43f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_list_d.kshlib
@@ -0,0 +1,89 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DEPTH_FS=$TESTPOOL/depth_fs
+MAX_DEPTH=3
+DS_COUNT=3
+set -A depth_options "d 0" "d 1" "d 2" "d 4" "d 32"
+set -A depth_array 0 1 2 4 32
+
+#
+# 'zfs get -d or zfs list -d' is supported
+#
+function zfs_get_list_d_supported
+{
+ check_opt_support "get" "-d"
+ return $?
+}
+
+#
+# Setup multiple depths datasets, including fs, volume and snapshot.
+#
+function depth_fs_setup
+{
+ typeset -i i j k
+ typeset fslist
+
+ log_must $ZFS create $DEPTH_FS
+
+ (( i=1 ))
+ while (( i<=MAX_DEPTH )); do
+ if (( i==1 )); then
+ fslist=$DEPTH_FS
+ else
+ (( k=i-1 ))
+ fslist=$($ZFS list -rH -t filesystem -o name $DEPTH_FS|$GREP d"$k"$)
+ if (( $? != 0 )); then
+ zfs list -rH -t filesystem -o name $DEPTH_FS
+ log_fail "No d$k filesystem"
+ fi
+ fi
+ for fs in $fslist; do
+ (( j=1 ))
+ while (( j<=DS_COUNT )); do
+ log_must $ZFS create $fs/fs_"$j"_d"$i"
+ if is_global_zone ; then
+ log_must $ZFS create -V 8M $fs/v_"$j"_d"$i"
+ fi
+ log_must $ZFS snapshot $fs@s_"$j"_d"$i"
+ (( j=j+1 ))
+ done
+ done
+ (( i=i+1 ))
+ done
+}
+
+#
+# Cleanup multiple depths filesystem.
+#
+function depth_fs_cleanup
+{
+ log_must $ZFS destroy -rR $DEPTH_FS
+}
+
+
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_test.sh
new file mode 100755
index 000000000000..e403f9077b26
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_get/zfs_get_test.sh
@@ -0,0 +1,321 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zfs_get_001_pos cleanup
+zfs_get_001_pos_head()
+{
+ atf_set "descr" "Setting the valid options and properties 'zfs get' should returnthe correct property value."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_get_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_get_list_d.kshlib
+ . $(atf_get_srcdir)/zfs_get_common.kshlib
+ . $(atf_get_srcdir)/zfs_get.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_get_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_get_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_get_list_d.kshlib
+ . $(atf_get_srcdir)/zfs_get_common.kshlib
+ . $(atf_get_srcdir)/zfs_get.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_get_002_pos cleanup
+zfs_get_002_pos_head()
+{
+ atf_set "descr" "Setting the valid options and properties 'zfs get' return correctvalue. It should be successful."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_get_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_get_list_d.kshlib
+ . $(atf_get_srcdir)/zfs_get_common.kshlib
+ . $(atf_get_srcdir)/zfs_get.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_get_002_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_get_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_get_list_d.kshlib
+ . $(atf_get_srcdir)/zfs_get_common.kshlib
+ . $(atf_get_srcdir)/zfs_get.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_get_003_pos cleanup
+zfs_get_003_pos_head()
+{
+ atf_set "descr" "'zfs get' should get consistent report with different option."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_get_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_get_list_d.kshlib
+ . $(atf_get_srcdir)/zfs_get_common.kshlib
+ . $(atf_get_srcdir)/zfs_get.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_get_003_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_get_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_get_list_d.kshlib
+ . $(atf_get_srcdir)/zfs_get_common.kshlib
+ . $(atf_get_srcdir)/zfs_get.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_get_004_pos cleanup
+zfs_get_004_pos_head()
+{
+ atf_set "descr" "Verify the functions of 'zfs get all' work."
+ atf_set "require.progs" "ksh93 zfs zpool"
+}
+zfs_get_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_get_list_d.kshlib
+ . $(atf_get_srcdir)/zfs_get_common.kshlib
+ . $(atf_get_srcdir)/zfs_get.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_get_004_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_get_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_get_list_d.kshlib
+ . $(atf_get_srcdir)/zfs_get_common.kshlib
+ . $(atf_get_srcdir)/zfs_get.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_get_005_neg cleanup
+zfs_get_005_neg_head()
+{
+ atf_set "descr" "Setting the invalid option and properties, 'zfs get' should befailed."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_get_005_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_get_list_d.kshlib
+ . $(atf_get_srcdir)/zfs_get_common.kshlib
+ . $(atf_get_srcdir)/zfs_get.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_get_005_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_get_005_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_get_list_d.kshlib
+ . $(atf_get_srcdir)/zfs_get_common.kshlib
+ . $(atf_get_srcdir)/zfs_get.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_get_006_neg cleanup
+zfs_get_006_neg_head()
+{
+ atf_set "descr" "Verify 'zfs get all' fails with invalid combination scenarios."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_get_006_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_get_list_d.kshlib
+ . $(atf_get_srcdir)/zfs_get_common.kshlib
+ . $(atf_get_srcdir)/zfs_get.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_get_006_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_get_006_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_get_list_d.kshlib
+ . $(atf_get_srcdir)/zfs_get_common.kshlib
+ . $(atf_get_srcdir)/zfs_get.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_get_007_neg cleanup
+zfs_get_007_neg_head()
+{
+ atf_set "descr" "'zfs get -o' fails with invalid options or column names"
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_get_007_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_get_list_d.kshlib
+ . $(atf_get_srcdir)/zfs_get_common.kshlib
+ . $(atf_get_srcdir)/zfs_get.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_get_007_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_get_007_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_get_list_d.kshlib
+ . $(atf_get_srcdir)/zfs_get_common.kshlib
+ . $(atf_get_srcdir)/zfs_get.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_get_008_pos cleanup
+zfs_get_008_pos_head()
+{
+ atf_set "descr" "Verify '-d <n>' can work with other options"
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_get_008_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_get_list_d.kshlib
+ . $(atf_get_srcdir)/zfs_get_common.kshlib
+ . $(atf_get_srcdir)/zfs_get.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_get_008_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_get_008_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_get_list_d.kshlib
+ . $(atf_get_srcdir)/zfs_get_common.kshlib
+ . $(atf_get_srcdir)/zfs_get.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_get_009_pos cleanup
+zfs_get_009_pos_head()
+{
+ atf_set "descr" "'zfs get -d <n>' should get expected output."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1200
+}
+zfs_get_009_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_get_list_d.kshlib
+ . $(atf_get_srcdir)/zfs_get_common.kshlib
+ . $(atf_get_srcdir)/zfs_get.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_get_009_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_get_009_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_get_list_d.kshlib
+ . $(atf_get_srcdir)/zfs_get_common.kshlib
+ . $(atf_get_srcdir)/zfs_get.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_get_010_neg cleanup
+zfs_get_010_neg_head()
+{
+ atf_set "descr" "A negative depth or a non numeric depth should fail in 'zfs get -d <n>'"
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_get_010_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_get_list_d.kshlib
+ . $(atf_get_srcdir)/zfs_get_common.kshlib
+ . $(atf_get_srcdir)/zfs_get.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_get_010_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_get_010_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_get_list_d.kshlib
+ . $(atf_get_srcdir)/zfs_get_common.kshlib
+ . $(atf_get_srcdir)/zfs_get.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zfs_get_001_pos
+ atf_add_test_case zfs_get_002_pos
+ atf_add_test_case zfs_get_003_pos
+ atf_add_test_case zfs_get_004_pos
+ atf_add_test_case zfs_get_005_neg
+ atf_add_test_case zfs_get_006_neg
+ atf_add_test_case zfs_get_007_neg
+ atf_add_test_case zfs_get_008_pos
+ atf_add_test_case zfs_get_009_pos
+ atf_add_test_case zfs_get_010_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/Makefile
new file mode 100644
index 000000000000..3b532596cee1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/Makefile
@@ -0,0 +1,18 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zfs_inherit
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zfs_inherit_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zfs_inherit_002_neg.ksh
+${PACKAGE}FILES+= zfs_inherit_003_pos.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zfs_inherit_001_neg.ksh
+${PACKAGE}FILES+= zfs_inherit.cfg
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/cleanup.ksh
new file mode 100644
index 000000000000..713cb16bb31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/setup.ksh
new file mode 100644
index 000000000000..4ef170bda6d3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_container_volume_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/zfs_inherit.cfg b/tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/zfs_inherit.cfg
new file mode 100644
index 000000000000..ada9190abcdf
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/zfs_inherit.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2013 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/tests/cli_root/cli.cfg
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/zfs_inherit_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/zfs_inherit_001_neg.ksh
new file mode 100644
index 000000000000..79e3ed8fcded
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/zfs_inherit_001_neg.ksh
@@ -0,0 +1,91 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_inherit_001_neg
+#
+# DESCRIPTION:
+# 'zfs inherit' should return an error when attempting to inherit
+# properties which are not inheritable.
+#
+# STRATEGY:
+# 1. Create an array of properties which cannot be inherited
+# 2. For each property in the array, execute 'zfs inherit'
+# 3. Verify an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+# Define uninherited properties and their short name.
+typeset props_str="type creation \
+ compressratio ratio mounted origin quota reservation \
+ reserv volsize volblocksize volblock"
+
+$ZFS upgrade -v > /dev/null 2>&1
+if [[ $? -eq 0 ]]; then
+ props_str="$props_str version"
+fi
+
+set -A prop $props_str canmount
+
+
+log_assert "'zfs inherit' should return an error when attempting to inherit" \
+ " un-inheritable properties."
+
+typeset -i i=0
+for obj in $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL; do
+ i=0
+ while [[ $i -lt ${#prop[*]} ]]; do
+ orig_val=$(get_prop ${prop[i]} $obj)
+
+ log_mustnot $ZFS inherit ${prop[i]} $obj
+
+ new_val=$(get_prop ${prop[i]} $obj)
+
+ if [[ $new_val != $orig_val ]]; then
+ log_fail "${prop[i]} property changed from $orig_val "
+ " to $new_val"
+ fi
+ ((i = i + 1))
+ done
+done
+
+log_pass "'zfs inherit' failed as expected when attempting to inherit" \
+ " un-inheritable properties."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/zfs_inherit_002_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/zfs_inherit_002_neg.ksh
new file mode 100644
index 000000000000..c63802b558ad
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/zfs_inherit_002_neg.ksh
@@ -0,0 +1,109 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_inherit_002_neg
+#
+# DESCRIPTION:
+# 'zfs inherit' should return an error with bad parameters in one command.
+#
+# STRATEGY:
+# 1. Set an array of bad options and invlid properties to 'zfs inherit'
+# 2. Execute 'zfs inherit' with bad options and passing invlid properties
+# 3. Verify an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-19)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if snapexists $TESTPOOL/$TESTFS@$TESTSNAP; then
+ log_must $ZFS destroy $TESTPOOL/$TESTFS@$TESTSNAP
+ fi
+}
+
+log_assert "'zfs inherit' should return an error with bad parameters in one command."
+log_onexit cleanup
+
+set -A badopts "r" "R" "-R" "-rR" "-a" "-" "-?" "-1" "-2" "-v" "-n"
+set -A props "recordsize" "mountpoint" "sharenfs" "checksum" "compression" \
+ "atime" "devices" "exec" "setuid" "readonly" "zoned" "snapdir" "aclmode" \
+ "aclinherit" "shareiscsi" "xattr" "copies"
+set -A illprops "recordsiz" "mountpont" "sharen" "compres" "atme" "???" "***" "blah"
+
+log_must $ZFS snapshot $TESTPOOL/$TESTFS@$TESTSNAP
+
+typeset -i i=0
+for ds in $TESTPOOL $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL \
+ $TESTPOOL/$TESTFS@$TESTSNAP; do
+
+ #zfs inherit should fail with bad options
+ for opt in ${badopts[@]}; do
+ for prop in ${props[@]}; do
+ log_mustnot eval "$ZFS inherit $opt $prop $ds >/dev/null 2>&1"
+ done
+ done
+
+ #zfs inherit should fail with invalid properties
+ for prop in ${illprops[@]}; do
+ log_mustnot eval "$ZFS inherit $prop $ds >/dev/null 2>&1"
+ log_mustnot eval "$ZFS inherit -r $prop $ds >/dev/null 2>&1"
+ done
+
+ #zfs inherit should fail with too many arguments
+ (( i = 0 ))
+ while (( i < ${#props[*]} -1 )); do
+ log_mustnot eval "$ZFS inherit ${props[(( i ))]} \
+ ${props[(( i + 1 ))]} $ds >/dev/null 2>&1"
+ log_mustnot eval "$ZFS inherit -r ${props[(( i ))]} \
+ ${props[(( i + 1 ))]} $ds >/dev/null 2>&1"
+
+ (( i = i + 2 ))
+ done
+
+done
+
+#zfs inherit should fail with missing datasets
+for prop in ${props[@]}; do
+ log_mustnot eval "$ZFS inherit $prop >/dev/null 2>&1"
+ log_mustnot eval "$ZFS inherit -r $prop >/dev/null 2>&1"
+done
+
+log_pass "'zfs inherit' failed as expected when passing illegal arguments."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/zfs_inherit_003_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/zfs_inherit_003_pos.ksh
new file mode 100644
index 000000000000..548d3caa5009
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/zfs_inherit_003_pos.ksh
@@ -0,0 +1,99 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_set/zfs_set_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_inherit_003_pos
+#
+# DESCRIPTION:
+# 'zfs inherit' should return an error with bad parameters in one command.
+#
+# STRATEGY:
+# 1. Set an array of bad options and invlid properties to 'zfs inherit'
+# 2. Execute 'zfs inherit' with bad options and passing invlid properties
+# 3. Verify an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-19)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ for ds in $TESTPOOL $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL ; do
+ if snapexists $ds@$TESTSNAP; then
+ log_must $ZFS destroy $ds@$TESTSNAP
+ fi
+ done
+ cleanup_user_prop $TESTPOOL
+}
+
+log_assert "'zfs inherit' should inherit user property."
+log_onexit cleanup
+
+for ds in $TESTPOOL $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL ; do
+ typeset prop_name=$(valid_user_property 10)
+ typeset value=$(user_property_value 16)
+
+ log_must eval "$ZFS set $prop_name='$value' $ds"
+
+ log_must $ZFS snapshot $ds@$TESTSNAP
+
+ typeset snapvalue=$(get_prop $prop_name $ds@$TESTSNAP)
+
+ if [[ "$snapvalue" != "$value" ]] ; then
+ log_fail "The '$ds@$TESTSNAP '$prop_name' value '$snapvalue' " \
+ "not equal to the expected value '$value'."
+ fi
+
+ snapvalue=$(user_property_value 16)
+ log_must eval "$ZFS set $prop_name='$snapvalue' $ds@$TESTSNAP"
+
+ log_must $ZFS inherit $prop_name $ds@$TESTSNAP
+
+ snapvalue=$(get_prop $prop_name $ds@$TESTSNAP)
+
+ if [[ "$snapvalue" != "$value" ]] ; then
+ log_fail "The '$ds@$TESTSNAP '$prop_name' value '$snapvalue' " \
+ "not equal to the expected value '$value'."
+ fi
+
+
+done
+
+log_pass "'zfs inherit' inherit user property."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/zfs_inherit_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/zfs_inherit_test.sh
new file mode 100755
index 000000000000..f560b35afb75
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_inherit/zfs_inherit_test.sh
@@ -0,0 +1,105 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zfs_inherit_001_neg cleanup
+zfs_inherit_001_neg_head()
+{
+ atf_set "descr" "'zfs inherit' should return an error when attempting to inherit un-inheritable properties."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_inherit_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_inherit.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_inherit_001_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_inherit_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_inherit.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_inherit_002_neg cleanup
+zfs_inherit_002_neg_head()
+{
+ atf_set "descr" "'zfs inherit' should return an error with bad parameters in one command."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_inherit_002_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_inherit.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_inherit_002_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_inherit_002_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_inherit.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_inherit_003_pos cleanup
+zfs_inherit_003_pos_head()
+{
+ atf_set "descr" "'zfs inherit' should inherit user property."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_inherit_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_inherit.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_inherit_003_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_inherit_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_inherit.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zfs_inherit_001_neg
+ atf_add_test_case zfs_inherit_002_neg
+ atf_add_test_case zfs_inherit_003_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/Makefile
new file mode 100644
index 000000000000..b5b68263d8a5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/Makefile
@@ -0,0 +1,28 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zfs_mount
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zfs_mount_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zfs_mount_004_pos.ksh
+${PACKAGE}FILES+= zfs_mount_008_pos.ksh
+${PACKAGE}FILES+= zfs_mount_009_neg.ksh
+${PACKAGE}FILES+= zfs_mount_010_neg.ksh
+${PACKAGE}FILES+= zfs_mount_011_neg.ksh
+${PACKAGE}FILES+= zfs_mount_005_pos.ksh
+${PACKAGE}FILES+= zfs_mount_001_pos.ksh
+${PACKAGE}FILES+= zfs_mount.cfg
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zfs_mount.kshlib
+${PACKAGE}FILES+= zfs_mount_007_pos.ksh
+${PACKAGE}FILES+= zfs_mount_003_pos.ksh
+${PACKAGE}FILES+= zfs_mount_all_001_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zfs_mount_006_pos.ksh
+${PACKAGE}FILES+= zfs_mount_002_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/cleanup.ksh
new file mode 100644
index 000000000000..b2b441e574d0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/setup.ksh
new file mode 100644
index 000000000000..957b1bddadb8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount.cfg b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount.cfg
new file mode 100644
index 000000000000..cd2c2e416ac3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount.cfg
@@ -0,0 +1,37 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
+
+export mountcmd=mount
+export mountforce="$mountcmd -f"
+export mountall="$mountcmd -a"
+
+export unmountcmd=unmount
+export unmountforce="$unmountcmd -f"
+export unmountall="$unmountcmd -a"
+
+export NONEXISTFSNAME="nonexistfs50charslong_0123456789012345678901234567"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount.kshlib b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount.kshlib
new file mode 100644
index 000000000000..11f9bb3da2df
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount.kshlib
@@ -0,0 +1,130 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+function force_unmount #dev
+{
+ typeset dev=$1
+
+ ismounted $dev
+ if (( $? == 0 )); then
+ log_must $ZFS $unmountforce $dev
+ fi
+ return 0
+}
+
+# Create pool and ( fs | container | vol ) with the given parameters,
+# it'll destroy prior exist one that has the same name.
+
+function setup_filesystem #disklist #pool #fs #mntpoint #type #vdev
+{
+ typeset disklist=$1
+ typeset pool=$2
+ typeset fs=${3##/}
+ typeset mntpoint=$4
+ typeset type=$5
+ typeset vdev=$6
+
+ if [[ -z $pool || -z $fs || -z $mntpoint ]]; then
+ log_note "Missing parameter: (\"$pool\", \"$fs\", \"$mntpoint\")"
+ return 1
+ fi
+
+ if is_global_zone && [[ -z $disklist ]] ; then
+ log_note "Missing disklist."
+ return 1
+ fi
+
+ if [[ $vdev != "" && \
+ $vdev != "mirror" && \
+ $vdev != "raidz" ]] ; then
+
+ log_note "Wrong vdev: (\"$vdev\")"
+ return 1
+ fi
+
+ poolexists $pool || \
+ create_pool $pool $vdev $disklist
+
+ datasetexists $pool/$fs && \
+ log_must cleanup_filesystem $pool $fs
+
+ $RMDIR $mntpoint > /dev/null 2>&1
+ if [[ ! -d $mntpoint ]]; then
+ log_must $MKDIR -p $mntpoint
+ fi
+
+ case "$type" in
+ 'ctr') log_must $ZFS create $pool/$fs
+ log_must $ZFS set mountpoint=$mntpoint $pool/$fs
+ ;;
+ 'vol') log_must $ZFS create -V $VOLSIZE $pool/$fs
+ ;;
+ *) log_must $ZFS create $pool/$fs
+ log_must $ZFS set mountpoint=$mntpoint $pool/$fs
+ ;;
+ esac
+
+ return 0
+}
+
+# Destroy ( fs | container | vol ) with the given parameters.
+function cleanup_filesystem #pool #fs
+{
+ typeset pool=$1
+ typeset fs=${2##/}
+ typeset mtpt=""
+
+ if [[ -z $pool || -z $fs ]]; then
+ log_note "Missing parameter: (\"$pool\", \"$fs\")"
+ return 1
+ fi
+
+ if datasetexists "$pool/$fs" ; then
+ mtpt=$(get_prop mountpoint "$pool/$fs")
+ log_must $ZFS destroy -r $pool/$fs
+
+ [[ -d $mtpt ]] && \
+ log_must $RM -rf $mtpt
+ else
+ return 1
+ fi
+
+ return 0
+}
+
+# Make sure 'zfs mount' should display all ZFS filesystems currently mounted.
+# The results of 'zfs mount' and 'df -F zfs' should be identical.
+function verify_mount_display
+{
+ typeset fs
+
+ for fs in $($ZFS $mountcmd | $AWK '{print $1}') ; do
+ log_must mounted $fs
+ done
+ return 0
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_001_pos.ksh
new file mode 100644
index 000000000000..cf6d6033b7ac
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_001_pos.ksh
@@ -0,0 +1,76 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_mount/zfs_mount.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_mount_001_pos
+#
+# DESCRIPTION:
+# Invoke "zfs mount <filesystem>" with a regular name of filesystem,
+# will mount that filesystem successfully.
+#
+# STRATEGY:
+# 1. Make sure that the ZFS filesystem is unmounted.
+# 2. Invoke 'zfs mount <filesystem>'.
+# 3. Verify that the filesystem is mounted.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ log_must force_unmount $TESTPOOL/$TESTFS
+ return 0
+}
+
+log_assert "Verify that '$ZFS $mountcmd <filesystem>' succeeds as root."
+
+log_onexit cleanup
+
+unmounted $TESTPOOL/$TESTFS || \
+ log_must cleanup
+
+log_must $ZFS $mountcmd $TESTPOOL/$TESTFS
+
+log_note "Make sure the filesystem $TESTPOOL/$TESTFS is mounted"
+mounted $TESTPOOL/$TESTFS || \
+ log_fail Filesystem $TESTPOOL/$TESTFS is unmounted
+
+log_pass "'$ZFS $mountcmd <filesystem>' succeeds as root."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_002_pos.ksh
new file mode 100644
index 000000000000..2ba7445b2c48
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_002_pos.ksh
@@ -0,0 +1,87 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_mount/zfs_mount.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_mount_002_pos
+#
+# DESCRIPTION:
+# Invoking "zfs mount <filesystem>" with a filesystem whose name is not in
+# "zfs list", will fail with a return code of 1.
+#
+# STRATEGY:
+# 1. Make sure the NONEXISTFSNAME ZFS filesystem is not in 'zfs list'.
+# 2. Invoke 'zfs mount <filesystem>'.
+# 3. Verify that mount failed with return code of 1.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ typeset fs
+ for fs in $NONEXISTFSNAME $TESTFS ; do
+ log_must force_unmount $TESTPOOL/$fs
+ done
+}
+
+
+log_assert "Verify that '$ZFS $mountcmd' with a filesystem " \
+ "whose name is not in 'zfs list' will fail with return code 1."
+
+log_onexit cleanup
+
+log_note "Make sure the filesystem $TESTPOOL/$NONEXISTFSNAME " \
+ "is not in 'zfs list'"
+log_mustnot $ZFS list $TESTPOOL/$NONEXISTFSNAME
+
+typeset -i ret=0
+$ZFS $mountcmd $TESTPOOL/$NONEXISTFSNAME
+ret=$?
+(( ret == 1 )) || \
+ log_fail "'$ZFS $mountcmd $TESTPOOL/$NONEXISTFSNAME' " \
+ "unexpected return code of $ret."
+
+log_note "Make sure the filesystem $TESTPOOL/$NONEXISTFSNAME is unmounted"
+unmounted $TESTPOOL/$NONEXISTFSNAME || \
+ log_fail Filesystem $TESTPOOL/$NONEXISTFSNAME is mounted
+
+log_pass "'$ZFS $mountcmd' with a filesystem " \
+ "whose name is not in 'zfs list' failed with return code 1."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_003_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_003_pos.ksh
new file mode 100644
index 000000000000..6103fcd8efcb
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_003_pos.ksh
@@ -0,0 +1,99 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_mount/zfs_mount.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_mount_003_pos
+#
+# DESCRIPTION:
+# Invoke "zfs mount <filesystem>" with a filesystem whose mountpoint property
+# is 'legacy' or 'none',
+# it will fail with a return code of 1 and issue an error message.
+#
+# STRATEGY:
+# 1. Make sure that the ZFS filesystem is unmounted.
+# 2. Mount the filesystem using the various combinations
+# - zfs set mountpoint=legacy <filesystem>
+# - zfs set mountpoint=none <filesystem>
+# 3. Verify that mount failed with return code of 1.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+ log_must force_unmount $TESTPOOL/$TESTFS
+ return 0
+}
+
+log_assert "Verify that '$ZFS $mountcmd' with a filesystem " \
+ "whose mountpoint property is 'legacy' or 'none' " \
+ "will fail with return code 1."
+
+log_onexit cleanup
+
+set -A mopt "legacy" "none"
+
+typeset -i ret=0
+typeset -i i=0
+
+while (( i < ${#mopt[*]} )); do
+ unmounted $TESTPOOL/$TESTFS || \
+ log_must cleanup
+
+ log_must $ZFS set mountpoint=${mopt[i]} $TESTPOOL/$TESTFS
+
+ $ZFS $mountcmd $TESTPOOL/$TESTFS
+ ret=$?
+ (( ret == 1)) || \
+ log_fail "'$ZFS $mountcmd $TESTPOOL/$TESTFS' " \
+ "unexpected return code of $ret."
+
+ log_note "Make sure the filesystem $TESTPOOL/$TESTFS is unmounted"
+ unmounted $TESTPOOL/$TESTFS || \
+ log_fail Filesystem $TESTPOOL/$TESTFS is mounted
+
+ ((i = i + 1))
+done
+
+log_pass "Verify that '$ZFS $mountcmd' with a filesystem " \
+ "whose mountpoint property is 'legacy' or 'none' " \
+ "will fail with return code 1."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_004_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_004_pos.ksh
new file mode 100644
index 000000000000..9207c0af6961
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_004_pos.ksh
@@ -0,0 +1,92 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_mount/zfs_mount.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_mount_004_pos
+#
+# DESCRIPTION:
+# Invoke "zfs mount <filesystem>" with a filesystem
+# which has been already mounted,
+# it will fail with a return code of 1
+#
+# STRATEGY:
+# 1. Make sure that the ZFS filesystem is unmounted.
+# 2. Invoke 'zfs mount <filesystem>'.
+# 3. Verify that the filesystem is mounted.
+# 4. Invoke 'zfs mount <filesystem>' the second times.
+# 5. Verify the last mount operation failed with return code of 1.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ log_must force_unmount $TESTPOOL/$TESTFS
+ return 0
+}
+
+typeset -i ret=0
+
+log_assert "Verify that '$ZFS $mountcmd <filesystem>' " \
+ "with a mounted filesystem will fail with return code 1."
+
+log_onexit cleanup
+
+unmounted $TESTPOOL/$TESTFS || \
+ log_must cleanup
+
+log_must $ZFS $mountcmd $TESTPOOL/$TESTFS
+
+mounted $TESTPOOL/$TESTFS || \
+ log_unresolved "Filesystem $TESTPOOL/$TESTFS is unmounted"
+
+$ZFS $mountcmd $TESTPOOL/$TESTFS
+ret=$?
+(( ret == 1 )) || \
+ log_fail "'$ZFS $mountcmd $TESTPOOL/$TESTFS' " \
+ "unexpected return code of $ret."
+
+log_note "Make sure the filesystem $TESTPOOL/$TESTFS is mounted"
+mounted $TESTPOOL/$TESTFS || \
+ log_fail Filesystem $TESTPOOL/$TESTFS is unmounted
+
+log_pass "'$ZFS $mountcmd <filesystem>' with a mounted filesystem " \
+ "will fail with return code 1."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_005_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_005_pos.ksh
new file mode 100644
index 000000000000..369e5618fd96
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_005_pos.ksh
@@ -0,0 +1,95 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_mount/zfs_mount.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_mount_005_pos
+#
+# DESCRIPTION:
+# Invoke "zfs mount <filesystem>" with a filesystem
+# but its mountpoint is currently in use,
+# it will fail with a return code of 1
+# and issue an error message.
+#
+# STRATEGY:
+# 1. Make sure that the ZFS filesystem is unmounted.
+# 2. Apply 'zfs set mountpoint=path <filesystem>'.
+# 3. Change directory to that given mountpoint.
+# 3. Invoke 'zfs mount <filesystem>'.
+# 4. Verify that mount failed with return code of 1.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+ log_must force_unmount $TESTPOOL/$TESTFS
+ return 0
+}
+
+typeset -i ret=0
+
+log_assert "Verify that '$ZFS $mountcmd' with a filesystem " \
+ "whose mountpoint is currently in use will fail with return code 1."
+
+log_onexit cleanup
+
+unmounted $TESTPOOL/$TESTFS || \
+ log_must cleanup
+
+[[ -d $TESTDIR ]] || \
+ log_must $MKDIR -p $TESTDIR
+
+cd $TESTDIR || \
+ log_unresolved "Unable change directory to $TESTDIR"
+
+$ZFS $mountcmd $TESTPOOL/$TESTFS
+ret=$?
+(( ret == 1 )) || \
+ log_fail "'$ZFS $mountcmd $TESTPOOL/$TESTFS' " \
+ "unexpected return code of $ret."
+
+log_note "Make sure the filesystem $TESTPOOL/$TESTFS is unmounted"
+unmounted $TESTPOOL/$TESTFS || \
+ log_fail Filesystem $TESTPOOL/$TESTFS is mounted
+
+log_pass "'$ZFS $mountcmd' with a filesystem " \
+ "whose mountpoint is currently in use failed with return code 1."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_006_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_006_pos.ksh
new file mode 100644
index 000000000000..5755bb66b553
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_006_pos.ksh
@@ -0,0 +1,133 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_mount/zfs_mount.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_mount_006_pos
+#
+# DESCRIPTION:
+# Invoke "zfs mount <filesystem>" with a filesystem
+# which mountpoint be the identical or the top of an existing one,
+# it will fail with a return code of 1
+#
+# STRATEGY:
+# 1. Prepare an existing mounted filesystem.
+# 2. Setup a new filesystem and make sure that it is unmounted.
+# 3. Mount the new filesystem using the various combinations
+# - zfs set mountpoint=<identical path> <filesystem>
+# - zfs set mountpoint=<top path> <filesystem>
+# 4. Verify that mount failed with return code of 1.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-11)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ log_must force_unmount $TESTPOOL/$TESTFS
+
+ datasetexists $TESTPOOL/$TESTFS1 && \
+ cleanup_filesystem $TESTPOOL $TESTFS1
+
+ [[ -d $TESTDIR ]] && \
+ log_must $RM -rf $TESTDIR
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+ log_must force_unmount $TESTPOOL/$TESTFS
+
+ return 0
+}
+
+typeset -i ret=0
+
+log_assert "Verify that '$ZFS $mountcmd <filesystem>' " \
+ "which mountpoint be the identical or the top of an existing one " \
+ "will fail with return code 1."
+
+log_onexit cleanup
+
+unmounted $TESTPOOL/$TESTFS || \
+ log_must force_unmount $TESTPOOL/$TESTFS
+
+[[ -d $TESTDIR ]] && \
+ log_must $RM -rf $TESTDIR
+
+typeset -i MAXDEPTH=3
+typeset -i depth=0
+typeset mtpt=$TESTDIR
+
+while (( depth < MAXDEPTH )); do
+ mtpt=$mtpt/$depth
+ (( depth = depth + 1))
+done
+
+log_must $ZFS set mountpoint=$mtpt $TESTPOOL/$TESTFS
+log_must $ZFS $mountcmd $TESTPOOL/$TESTFS
+
+mounted $TESTPOOL/$TESTFS || \
+ log_unresolved "Filesystem $TESTPOOL/$TESTFS is unmounted"
+
+log_must $ZFS create $TESTPOOL/$TESTFS1
+
+unmounted $TESTPOOL/$TESTFS1 || \
+ log_must force_unmount $TESTPOOL/$TESTFS1
+
+while [[ -n $mtpt ]] ; do
+ (( depth == MAXDEPTH )) && \
+ log_note "Verify that '$ZFS $mountcmd <filesystem>' " \
+ "which mountpoint be the identical of an existing one " \
+ "will fail with return code 1."
+
+ log_must $ZFS set mountpoint=$mtpt $TESTPOOL/$TESTFS1
+ log_mustnot $ZFS $mountcmd $TESTPOOL/$TESTFS1
+
+ unmounted $TESTPOOL/$TESTFS1 || \
+ log_fail "Filesystem $TESTPOOL/$TESTFS1 is mounted."
+
+ mtpt=${mtpt%/*}
+
+ (( depth == MAXDEPTH )) && \
+ log_note "Verify that '$ZFS $mountcmd <filesystem>' " \
+ "which mountpoint be the top of an existing one " \
+ "will fail with return code 1."
+ (( depth = depth - 1 ))
+done
+
+log_pass "'$ZFS $mountcmd <filesystem>' " \
+ "which mountpoint be the identical or the top of an existing one " \
+ "will fail with return code 1."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_007_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_007_pos.ksh
new file mode 100644
index 000000000000..83f18fe692e4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_007_pos.ksh
@@ -0,0 +1,147 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_mount/zfs_mount.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_mount_007_pos
+#
+# DESCRIPTION:
+# The following options can be set on a temporary basis using the -o option
+# without affecting the on-disk property. The original on-disk value will be
+# restored when the file system is unmounted and mounted.
+#
+# PROPERTY MOUNT OPTION
+# atime atime/noatime
+# exec exec/noexec
+# readonly ro/rw
+# setuid setuid/nosetuid
+#
+# STRATEGY:
+# 1. Create filesystem and get origianl property value.
+# 2. Using 'zfs mount -o' to set filesystem property.
+# 3. Verify the property was set temporarily.
+# 4. Verify it will not affect the property that is stored on disk.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-08-02)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function cleanup
+{
+ if ! ismounted $TESTPOOL/$TESTFS; then
+ log_must $ZFS mount $TESTPOOL/$TESTFS
+ fi
+}
+
+log_assert "Verify '-o' will set filesystem property temporarily, " \
+ "without affecting the property that is stored on disk."
+log_onexit cleanup
+
+set -A properties "atime" "exec" "readonly" "setuid"
+
+#
+# Get the specified filesystem property reverse mount option.
+#
+# $1 filesystem
+# $2 property
+#
+function get_reverse_option
+{
+ typeset fs=$1
+ typeset prop=$2
+
+ # Define property value: "reverse if value=on" "reverse if value=off"
+ set -A values "noatime" "atime" \
+ "noexec" "exec" \
+ "rw" "ro" \
+ "nosetuid" "setuid"
+
+ typeset -i i=0
+ while (( i < ${#properties[@]} )); do
+ if [[ $prop == ${properties[$i]} ]]; then
+ break
+ fi
+
+ (( i += 1 ))
+ done
+ if (( i >= ${#properties[@]} )); then
+ log_fail "Incorrect option: $prop"
+ fi
+
+ typeset val
+ typeset -i ind=0
+ val=$(get_prop $prop $fs) || log_fail "get_prop $prop $fs"
+ if [[ $val == "on" ]]; then
+ (( ind = i * 2 ))
+ else
+ (( ind = i * 2 + 1 ))
+ fi
+
+ $ECHO ${values[$ind]}
+}
+
+fs=$TESTPOOL/$TESTFS
+cleanup
+
+for property in ${properties[@]}; do
+ orig_val=$(get_prop $property $fs)
+ (($? != 0)) && log_fail "get_prop $property $fs"
+
+ # Set filesystem property temporarily
+ reverse_opt=$(get_reverse_option $fs $property)
+ log_must $ZFS mount -o update,$reverse_opt $fs
+
+ cur_val=$(get_prop $property $fs)
+ (($? != 0)) && log_fail "get_prop $property $fs"
+
+ if [[ $orig_val == $cur_val ]]; then
+ log_fail "zfs mount -o update,$reverse_opt " \
+ "doesn't change property."
+ fi
+
+ # unmount & mount will revert property to the original value
+ log_must $ZFS unmount $fs
+ log_must $ZFS mount $fs
+
+ cur_val=$(get_prop $property $fs)
+ (($? != 0)) && log_fail "get_prop $property $fs"
+ if [[ $orig_val != $cur_val ]]; then
+ log_fail "zfs mount -o update,$reverse_opt " \
+ "change the property that is stored on disks"
+ fi
+done
+
+log_pass "Verify '-o' set filesystem property temporarily passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_008_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_008_pos.ksh
new file mode 100644
index 000000000000..b2c18f7690e7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_008_pos.ksh
@@ -0,0 +1,104 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_mount/zfs_mount.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_mount_008_pos
+#
+# DESCRIPTION:
+# 'zfs mount -O' allow the file system to be mounted over an existing
+# mount point, making the underlying file system inaccessible.
+#
+# STRATEGY:
+# 1. Create two filesystem fs & fs1, and create two test files for them.
+# 2. Unmount fs1 and set mountpoint property is identical to fs.
+# 3. Verify 'zfs mount -O' will make the underlying filesystem fs
+# inaccessible.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-08-02)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function cleanup
+{
+ ! ismounted $fs && log_must $ZFS mount $fs
+
+ if datasetexists $fs1; then
+ log_must $ZFS destroy $fs1
+ fi
+
+ if [[ -f $testfile ]]; then
+ log_must $RM -f $testfile
+ fi
+}
+
+log_assert "Verify 'zfs mount -O' will override existing mount point."
+log_onexit cleanup
+
+fs=$TESTPOOL/$TESTFS; fs1=$TESTPOOL/$TESTFS1
+
+cleanup
+
+# Get the original mountpoint of $fs and $fs1
+mntpnt=$(get_prop mountpoint $fs)
+log_must $ZFS create $fs1
+mntpnt1=$(get_prop mountpoint $fs1)
+
+testfile=$mntpnt/$TESTFILE0; testfile1=$mntpnt1/$TESTFILE1
+log_must $MKFILE 1M $testfile $testfile1
+
+log_must $ZFS unmount $fs1
+log_must $ZFS set mountpoint=$mntpnt $fs1
+log_must $ZFS mount $fs1
+
+# Create new file in override mountpoint
+log_must $MKFILE 1M $mntpnt/$TESTFILE2
+
+# Verify the underlying file system inaccessible
+log_mustnot $LS $testfile
+log_must $LS $mntpnt/$TESTFILE1 $mntpnt/$TESTFILE2
+
+# Verify $TESTFILE2 was created in $fs1, rather then $fs
+log_must $ZFS unmount $fs1
+log_must $ZFS set mountpoint=$mntpnt1 $fs1
+log_must $ZFS mount $fs1
+log_must $LS $testfile1 $mntpnt1/$TESTFILE2
+
+# Verify $TESTFILE2 was not created in $fs, and $fs is accessible again.
+log_mustnot $LS $mntpnt/$TESTFILE2
+log_must $LS $testfile
+
+log_pass "Verify 'zfs mount -O' override mount point passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_009_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_009_neg.ksh
new file mode 100644
index 000000000000..11e35739b683
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_009_neg.ksh
@@ -0,0 +1,122 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_mount/zfs_mount.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_mount_009_neg
+#
+# DESCRIPTION:
+# Try each 'zfs mount' with inapplicable scenarios to make sure
+# it returns an error. include:
+# * Multiple filesystems specified
+# * '-a', but also with a specific filesystem.
+#
+# STRATEGY:
+# 1. Create an array of parameters
+# 2. For each parameter in the array, execute the sub-command
+# 3. Verify an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-07)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+multifs="$TESTFS $TESTFS1"
+datasets=""
+
+for fs in $multifs ; do
+ datasets="$datasets $TESTPOOL/$fs"
+done
+
+set -A args "$mountall $TESTPOOL/$TESTFS" \
+ "$mountcmd $datasets"
+
+function setup_all
+{
+ typeset fs
+
+ for fs in $multifs ; do
+ setup_filesystem "$DISKS" "$TESTPOOL" \
+ "$fs" \
+ "${TEST_BASE_DIR%%/}/testroot${TESTCASE_ID}/$TESTPOOL/$fs"
+ done
+ return 0
+}
+
+function cleanup_all
+{
+ typeset fs
+
+ cleanup_filesystem "$TESTPOOL" "$TESTFS1"
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+
+ [[ -d ${TEST_BASE_DIR%%/}/testroot${TESTCASE_ID} ]] && \
+ $RM -rf ${TEST_BASE_DIR%%/}/testroot${TESTCASE_ID}
+
+
+ return 0
+}
+
+function verify_all
+{
+ typeset fs
+
+ for fs in $multifs ; do
+ log_must unmounted $TESTPOOL/$fs
+ done
+ return 0
+}
+
+log_assert "Badly-formed 'zfs $mountcmd' with inapplicable scenarios " \
+ "should return an error."
+
+log_onexit cleanup_all
+
+log_must setup_all
+
+log_must $ZFS $unmountall
+
+typeset -i i=0
+while (( i < ${#args[*]} )); do
+ log_mustnot $ZFS ${args[i]}
+ ((i = i + 1))
+done
+
+log_must verify_all
+
+log_pass "Badly formed 'zfs $mountcmd' with inapplicable scenarios " \
+ "fail as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_010_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_010_neg.ksh
new file mode 100644
index 000000000000..1e117941fef6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_010_neg.ksh
@@ -0,0 +1,80 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_mount_010_neg
+#
+# DESCRIPTION:
+# Verify that zfs mount should fail when mounting a mounted zfs filesystem or
+# the mountpoint is busy
+#
+# STRATEGY:
+# 1. Make a zfs filesystem mounted or mountpoint busy
+# 2. Use zfs mount to mount the filesystem
+# 3. Verify that zfs mount returns error
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-9)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if ! ismounted $fs; then
+ log_must $ZFS mount $fs
+ fi
+}
+
+log_assert "zfs mount fails with mounted filesystem or busy mountpoint"
+log_onexit cleanup
+
+fs=$TESTPOOL/$TESTFS
+if ! ismounted $fs; then
+ log_must $ZFS mount $fs
+fi
+
+log_mustnot $ZFS mount $fs
+
+mpt=$(get_prop mountpoint $fs)
+log_must $ZFS umount $fs
+curpath=`$DIRNAME $0`
+cd $mpt
+log_mustnot $ZFS mount $fs
+cd $curpath
+
+log_pass "zfs mount fails with mounted filesystem or busy moutpoint as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_011_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_011_neg.ksh
new file mode 100644
index 000000000000..e71dbebaa413
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_011_neg.ksh
@@ -0,0 +1,90 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_mount_011_neg
+#
+# DESCRIPTION:
+# Verify that zfs mount should fail with bad parameters
+#
+# STRATEGY:
+# 1. Make an array of bad parameters
+# 2. Use zfs mount to mount the filesystem
+# 3. Verify that zfs mount returns error
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-9)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if snapexists $TESTPOOL/$TESTFS@$TESTSNAP; then
+ log_must $ZFS destroy $TESTPOOL/$TESTFS@$TESTSNAP
+ fi
+
+ if is_global_zone && datasetexists $TESTPOOL/$TESTVOL; then
+ log_must $ZFS destroy $TESTPOOL/$TESTVOL
+ fi
+}
+
+log_assert "zfs mount fails with bad parameters"
+log_onexit cleanup
+
+fs=$TESTPOOL/$TESTFS
+set -A badargs "A" "-A" "-" "-x" "-?" "=" "-o *" "-a"
+
+for arg in "${badargs[@]}"; do
+ log_mustnot eval "$ZFS mount $arg $fs >/dev/null 2>&1"
+done
+
+#verify that zfs mount fails with invalid dataset
+for opt in "-o abc" "-O"; do
+ log_mustnot eval "$ZFS mount $opt /$fs >/dev/null 2>&1"
+done
+
+#verify that zfs mount fails with volume and snapshot
+log_must $ZFS snapshot $TESTPOOL/$TESTFS@$TESTSNAP
+log_mustnot eval "$ZFS mount $TESTPOOL/$TESTFS@$TESTSNAP >/dev/null 2>&1"
+
+if is_global_zone; then
+ log_must $ZFS create -V 10m $TESTPOOL/$TESTVOL
+ log_mustnot eval "$ZFS mount $TESTPOOL/$TESTVOL >/dev/null 2>&1"
+fi
+
+log_pass "zfs mount fails with bad parameters as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_all_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_all_001_pos.ksh
new file mode 100644
index 000000000000..76e335b4cd05
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_all_001_pos.ksh
@@ -0,0 +1,192 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_mount/zfs_mount.kshlib
+
+#################################################################################
+# __stc_assertion_start
+#
+# ID: zfs_mount_all_001_pos
+#
+# DESCRIPTION:
+# Verify that 'zfs mount -a' succeeds as root.
+#
+# STRATEGY:
+# 1. Create a group of pools with specified vdev.
+# 2. Create zfs filesystems within the given pools.
+# 3. Unmount all the filesystems.
+# 4. Verify that 'zfs mount -a' command succeed,
+# and all available ZFS filesystems are mounted.
+# 5. Verify that 'zfs mount' is identical with 'df -F zfs'
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-07)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+set -A fs "$TESTFS" "$TESTFS1"
+set -A ctr "" "$TESTCTR" "$TESTCTR/$TESTCTR1" "$TESTCTR1"
+set -A vol "$TESTVOL" "$TESTVOL1"
+
+function setup_all
+{
+ typeset -i i=0
+ typeset -i j=0
+ typeset path
+
+ while (( i < ${#ctr[*]} )); do
+
+ path=${TEST_BASE_DIR%%/}/testroot${TESTCASE_ID}/$TESTPOOL
+ if [[ -n ${ctr[i]} ]]; then
+ path=$path/${ctr[i]}
+
+ setup_filesystem "$DISKS" "$TESTPOOL" \
+ "${ctr[i]}" "$path" \
+ "ctr"
+ fi
+
+ if is_global_zone ; then
+ j=0
+ while (( j < ${#vol[*]} )); do
+ setup_filesystem "$DISKS" "$TESTPOOL" \
+ "${ctr[i]}/${vol[j]}" \
+ "$path/${vol[j]}" \
+ "vol"
+ ((j = j + 1))
+ done
+ fi
+
+ j=0
+ while (( j < ${#fs[*]} )); do
+ setup_filesystem "$DISKS" "$TESTPOOL" \
+ "${ctr[i]}/${fs[j]}" \
+ "$path/${fs[j]}"
+ ((j = j + 1))
+ done
+
+ ((i = i + 1))
+ done
+
+ return 0
+}
+
+function cleanup_all
+{
+ typeset -i i=0
+ typeset -i j=0
+ typeset path
+
+ ((i = ${#ctr[*]} - 1))
+
+ while (( i >= 0 )); do
+ if is_global_zone ; then
+ j=0
+ while (( j < ${#vol[*]} )); do
+ cleanup_filesystem "$TESTPOOL" \
+ "${ctr[i]}/${vol[j]}"
+ ((j = j + 1))
+ done
+ fi
+
+ j=0
+ while (( j < ${#fs[*]} )); do
+ cleanup_filesystem "$TESTPOOL" \
+ "${ctr[i]}/${fs[j]}"
+ ((j = j + 1))
+ done
+
+ [[ -n ${ctr[i]} ]] && \
+ cleanup_filesystem "$TESTPOOL" "${ctr[i]}"
+
+ ((i = i - 1))
+ done
+
+ [[ -d ${TEST_BASE_DIR%%/}/testroot${TESTCASE_ID} ]] && \
+ $RM -rf ${TEST_BASE_DIR%%/}/testroot${TESTCASE_ID}
+}
+
+function verify_all
+{
+ typeset -i i=0
+ typeset -i j=0
+ typeset path
+
+ while (( i < ${#ctr[*]} )); do
+
+ path=$TESTPOOL
+ [[ -n ${ctr[i]} ]] && \
+ path=$path/${ctr[i]}
+
+ if is_global_zone ; then
+ j=0
+ while (( j < ${#vol[*]} )); do
+ log_mustnot mounted "$path/${vol[j]}"
+ ((j = j + 1))
+ done
+ fi
+
+ j=0
+ while (( j < ${#fs[*]} )); do
+ log_must mounted "$path/${fs[j]}"
+ ((j = j + 1))
+ done
+
+ log_must mounted "$path"
+
+ ((i = i + 1))
+ done
+
+ return 0
+}
+
+
+log_assert "Verify that 'zfs $mountall' succeeds as root, " \
+ "and all available ZFS filesystems are mounted."
+
+log_onexit cleanup_all
+
+log_must setup_all
+
+log_must $ZFS $unmountall
+
+log_must $ZFS $mountall
+
+verify_all
+
+log_note "Verify that 'zfs $mountcmd' will display " \
+ "all ZFS filesystems currently mounted."
+
+verify_mount_display
+
+log_pass "'zfs $mountall' succeeds as root, " \
+ "and all available ZFS filesystems are mounted."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_test.sh
new file mode 100755
index 000000000000..0acea26a5e96
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_mount/zfs_mount_test.sh
@@ -0,0 +1,370 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zfs_mount_001_pos cleanup
+zfs_mount_001_pos_head()
+{
+ atf_set "descr" "Verify that '$ZFS $mountcmd <filesystem>' succeeds as root."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_mount_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_mount.kshlib
+ . $(atf_get_srcdir)/zfs_mount.cfg
+
+ verify_disk_count "$DISKS" 1
+ if other_pools_exist; then
+ atf_skip "Can't test unmount -a with existing pools"
+ fi
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_mount_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_mount_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_mount.kshlib
+ . $(atf_get_srcdir)/zfs_mount.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_mount_002_pos cleanup
+zfs_mount_002_pos_head()
+{
+ atf_set "descr" "Verify that '$ZFS $mountcmd' with a filesystemwhose name is not in 'zfs list' will fail with return code 1."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_mount_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_mount.kshlib
+ . $(atf_get_srcdir)/zfs_mount.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_mount_002_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_mount_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_mount.kshlib
+ . $(atf_get_srcdir)/zfs_mount.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_mount_003_pos cleanup
+zfs_mount_003_pos_head()
+{
+ atf_set "descr" "Verify that '$ZFS $mountcmd' with a filesystemwhose mountpoint property is 'legacy' or 'none' \will fail with return code 1."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_mount_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_mount.kshlib
+ . $(atf_get_srcdir)/zfs_mount.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_mount_003_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_mount_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_mount.kshlib
+ . $(atf_get_srcdir)/zfs_mount.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_mount_004_pos cleanup
+zfs_mount_004_pos_head()
+{
+ atf_set "descr" "Verify that '$ZFS $mountcmd <filesystem>'with a mounted filesystem will fail with return code 1."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_mount_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_mount.kshlib
+ . $(atf_get_srcdir)/zfs_mount.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_mount_004_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_mount_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_mount.kshlib
+ . $(atf_get_srcdir)/zfs_mount.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_mount_005_pos cleanup
+zfs_mount_005_pos_head()
+{
+ atf_set "descr" "Verify that '$ZFS $mountcmd' with a filesystemwhose mountpoint is currently in use will fail with return code 1."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_mount_005_pos_body()
+{
+ [[ `uname -s` = "FreeBSD" ]] && atf_skip "Unlike Illumos, FreeBSD allows the behavior the prohibition of which is tested by this testcase"
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_mount.kshlib
+ . $(atf_get_srcdir)/zfs_mount.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_mount_005_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_mount_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_mount.kshlib
+ . $(atf_get_srcdir)/zfs_mount.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_mount_006_pos cleanup
+zfs_mount_006_pos_head()
+{
+ atf_set "descr" "Verify that '$ZFS $mountcmd <filesystem>'which mountpoint be the identical or the top of an existing one \will fail with return code 1."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_mount_006_pos_body()
+{
+ [[ `uname -s` = "FreeBSD" ]] && atf_skip "Unlike Illumos, FreeBSD allows the behavior the prohibition of which is tested by this testcase"
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_mount.kshlib
+ . $(atf_get_srcdir)/zfs_mount.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_mount_006_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_mount_006_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_mount.kshlib
+ . $(atf_get_srcdir)/zfs_mount.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_mount_007_pos cleanup
+zfs_mount_007_pos_head()
+{
+ atf_set "descr" "Verify '-o' will set filesystem property temporarily,without affecting the property that is stored on disk."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_mount_007_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_mount.kshlib
+ . $(atf_get_srcdir)/zfs_mount.cfg
+
+ verify_disk_count "$DISKS" 1
+ atf_expect_fail "PR 115361 zfs get setuid doesn't reflect setuid state as set by zfs mount"
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_mount_007_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_mount_007_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_mount.kshlib
+ . $(atf_get_srcdir)/zfs_mount.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_mount_008_pos cleanup
+zfs_mount_008_pos_head()
+{
+ atf_set "descr" "Verify 'zfs mount -O' will override existing mount point."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_mount_008_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_mount.kshlib
+ . $(atf_get_srcdir)/zfs_mount.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_mount_008_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_mount_008_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_mount.kshlib
+ . $(atf_get_srcdir)/zfs_mount.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_mount_009_neg cleanup
+zfs_mount_009_neg_head()
+{
+ atf_set "descr" "Badly-formed 'zfs $mountcmd' with inapplicable scenariosshould return an error."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_mount_009_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_mount.kshlib
+ . $(atf_get_srcdir)/zfs_mount.cfg
+
+ if other_pools_exist; then
+ atf_skip "Can't test unmount -a with existing pools"
+ fi
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_mount_009_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_mount_009_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_mount.kshlib
+ . $(atf_get_srcdir)/zfs_mount.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_mount_010_neg cleanup
+zfs_mount_010_neg_head()
+{
+ atf_set "descr" "zfs mount fails with mounted filesystem or busy mountpoint"
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_mount_010_neg_body()
+{
+ [[ `uname -s` = "FreeBSD" ]] && atf_skip "Unlike Illumos, FreeBSD allows the behavior the prohibition of which is tested by this testcase"
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_mount.kshlib
+ . $(atf_get_srcdir)/zfs_mount.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_mount_010_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_mount_010_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_mount.kshlib
+ . $(atf_get_srcdir)/zfs_mount.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_mount_011_neg cleanup
+zfs_mount_011_neg_head()
+{
+ atf_set "descr" "zfs mount fails with bad parameters"
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_mount_011_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_mount.kshlib
+ . $(atf_get_srcdir)/zfs_mount.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_mount_011_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_mount_011_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_mount.kshlib
+ . $(atf_get_srcdir)/zfs_mount.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_mount_all_001_pos cleanup
+zfs_mount_all_001_pos_head()
+{
+ atf_set "descr" "Verify that 'zfs $mountall' succeeds as root,and all available ZFS filesystems are mounted."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_mount_all_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_mount.kshlib
+ . $(atf_get_srcdir)/zfs_mount.cfg
+
+ if other_pools_exist; then
+ atf_skip "Can't test unmount -a with existing pools"
+ fi
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_mount_all_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_mount_all_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_mount.kshlib
+ . $(atf_get_srcdir)/zfs_mount.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zfs_mount_001_pos
+ atf_add_test_case zfs_mount_002_pos
+ atf_add_test_case zfs_mount_003_pos
+ atf_add_test_case zfs_mount_004_pos
+ atf_add_test_case zfs_mount_005_pos
+ atf_add_test_case zfs_mount_006_pos
+ atf_add_test_case zfs_mount_007_pos
+ atf_add_test_case zfs_mount_008_pos
+ atf_add_test_case zfs_mount_009_neg
+ atf_add_test_case zfs_mount_010_neg
+ atf_add_test_case zfs_mount_011_neg
+ atf_add_test_case zfs_mount_all_001_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/Makefile
new file mode 100644
index 000000000000..53c59cf0bd99
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/Makefile
@@ -0,0 +1,24 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zfs_promote
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zfs_promote_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zfs_promote_006_neg.ksh
+${PACKAGE}FILES+= zfs_promote_003_pos.ksh
+${PACKAGE}FILES+= zfs_promote_common.kshlib
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zfs_promote_007_neg.ksh
+${PACKAGE}FILES+= zfs_promote.cfg
+${PACKAGE}FILES+= zfs_promote_002_pos.ksh
+${PACKAGE}FILES+= zfs_promote_004_pos.ksh
+${PACKAGE}FILES+= zfs_promote_008_pos.ksh
+${PACKAGE}FILES+= zfs_promote_005_pos.ksh
+${PACKAGE}FILES+= zfs_promote_001_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/cleanup.ksh
new file mode 100644
index 000000000000..713cb16bb31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/setup.ksh
new file mode 100644
index 000000000000..6ad8158d3afe
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_volume_setup ${DISK}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote.cfg b/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote.cfg
new file mode 100644
index 000000000000..6b5116566ea2
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote.cfg
@@ -0,0 +1,41 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
+
+export FILESIZE=1m
+
+export TESTSNAP3=testsnap3.${TESTCASE_ID}
+export TESTSNAP4=testsnap4.${TESTCASE_ID}
+export TESTSNAP5=testsnap5.${TESTCASE_ID}
+export TESTFILE3=testfile3.${TESTCASE_ID}
+
+export CLONEFILE=clonefile.${TESTCASE_ID}
+export CLONEFILE1=clonefile1.${TESTCASE_ID}
+export CLONEFILE2=clonefile2.${TESTCASE_ID}
+export CLONEFILE3=clonefile3.${TESTCASE_ID}
+
+
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_001_pos.ksh
new file mode 100644
index 000000000000..24132caded96
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_001_pos.ksh
@@ -0,0 +1,134 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_promote_001_pos
+#
+# DESCRIPTION:
+# 'zfs promote' can promote a clone filesystem to no longer be dependent
+# on its "origin" snapshot.
+#
+# STRATEGY:
+# 1. Create a snapshot and a clone of the snapshot
+# 2. Promote the clone filesystem
+# 3. Verify the promoted filesystem become independent
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-05-16)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if snapexists $csnap; then
+ log_must $ZFS promote $fs
+ fi
+ snapexists $snap && \
+ log_must $ZFS destroy -rR $snap
+
+ typeset data
+ for data in $file0 $file1; do
+ [[ -e $data ]] && $RM -f $data
+ done
+}
+
+function testing_verify
+{
+ typeset ds=$1
+ typeset ds_file=$2
+ typeset snap_file=$3
+ typeset c_ds=$4
+ typeset c_file=$5
+ typeset csnap_file=$6
+ typeset origin_prop=""
+
+
+ snapexists $ds@$TESTSNAP && \
+ log_fail "zfs promote cannot promote $ds@$TESTSNAP."
+ ! snapexists $c_ds@$TESTSNAP && \
+ log_fail "The $c_ds@$TESTSNAP after zfs promote doesn't exist."
+
+ origin_prop=$(get_prop origin $ds)
+ [[ "$origin_prop" != "$c_ds@$TESTSNAP" ]] && \
+ log_fail "The dependency of $ds is not correct."
+ origin_prop=$(get_prop origin $c_ds)
+ [[ "$origin_prop" != "-" ]] && \
+ log_fail "The dependency of $c_ds is not correct."
+
+ if [[ -e $snap_file ]] || [[ ! -e $csnap_file ]]; then
+ log_fail "Data file $snap_file cannot be correctly promoted."
+ fi
+ if [[ ! -e $ds_file ]] || [[ ! -e $c_file ]]; then
+ log_fail "There exists data file losing after zfs promote."
+ fi
+
+ log_mustnot $ZFS destroy -r $c_ds
+}
+
+log_assert "'zfs promote' can promote a clone filesystem."
+log_onexit cleanup
+
+fs=$TESTPOOL/$TESTFS
+file0=$TESTDIR/$TESTFILE0
+file1=$TESTDIR/$TESTFILE1
+snap=$fs@$TESTSNAP
+snapfile=$TESTDIR/$(get_snapdir_name)/$TESTSNAP/$TESTFILE0
+clone=$TESTPOOL/$TESTCLONE
+cfile=/$clone/$CLONEFILE
+csnap=$clone@$TESTSNAP
+csnapfile=/$clone/$(get_snapdir_name)/$TESTSNAP/$TESTFILE0
+
+# setup for promte testing
+log_must $MKFILE $FILESIZE $file0
+log_must $ZFS snapshot $snap
+log_must $MKFILE $FILESIZE $file1
+log_must $RM -f $file0
+log_must $ZFS clone $snap $clone
+log_must $MKFILE $FILESIZE $cfile
+
+log_must $ZFS promote $clone
+# verify the 'promote' operation
+testing_verify $fs $file1 $snapfile $clone $cfile $csnapfile
+
+log_note "Verify 'zfs promote' can change back the dependency relationship."
+log_must $ZFS promote $fs
+#verify the result
+testing_verify $clone $cfile $csnapfile $fs $file1 $snapfile
+
+log_pass "'zfs promote' reverses the clone parent-child dependency relationship"\
+ "as expected."
+
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_002_pos.ksh
new file mode 100644
index 000000000000..05776995b9a6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_002_pos.ksh
@@ -0,0 +1,109 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_promote_002_pos
+#
+# DESCRIPTION:
+# 'zfs promote' can deal with multiple snapshots in the origin filesystem.
+#
+# STRATEGY:
+# 1. Create multiple snapshots and a clone of the last snapshot
+# 2. Promote the clone filesystem
+# 3. Verify the promoted filesystem included all snapshots
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-05-16)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if snapexists $csnap1; then
+ log_must $ZFS promote $fs
+ fi
+
+ typeset ds
+ typeset data
+ for ds in $snap $snap1; do
+ log_must $ZFS destroy -rR $ds
+ done
+ for file in $TESTDIR/$TESTFILE0 $TESTDIR/$TESTFILE1; do
+ [[ -e $file ]] && $RM -f $file
+ done
+}
+
+log_assert "'zfs promote' can deal with multiple snapshots in a filesystem."
+log_onexit cleanup
+
+fs=$TESTPOOL/$TESTFS
+snap=$fs@$TESTSNAP
+snap1=$fs@$TESTSNAP1
+clone=$TESTPOOL/$TESTCLONE
+csnap=$clone@$TESTSNAP
+csnap1=$clone@$TESTSNAP1
+
+# setup for promote testing
+log_must $MKFILE $FILESIZE $TESTDIR/$TESTFILE0
+log_must $ZFS snapshot $snap
+log_must $MKFILE $FILESIZE $TESTDIR/$TESTFILE1
+log_must $RM -f $testdir/$TESTFILE0
+log_must $ZFS snapshot $snap1
+log_must $ZFS clone $snap1 $clone
+log_must $MKFILE $FILESIZE /$clone/$CLONEFILE
+
+log_must $ZFS promote $clone
+
+# verify the 'promote' operation
+for ds in $csnap $csnap1; do
+ ! snapexists $ds && \
+ log_fail "Snapshot $ds doesn't exist after zfs promote."
+done
+for ds in $snap $snap1; do
+ snapexists $ds && \
+ log_fail "Snapshot $ds is still there after zfs promote."
+done
+
+origin_prop=$(get_prop origin $fs)
+[[ "$origin_prop" != "$csnap1" ]] && \
+ log_fail "The dependency of $fs is not correct."
+origin_prop=$(get_prop origin $clone)
+[[ "$origin_prop" != "-" ]] && \
+ log_fail "The dependency of $clone is not correct."
+
+log_pass "'zfs promote' deal with multiple snapshots as expected."
+
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_003_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_003_pos.ksh
new file mode 100644
index 000000000000..b2ecd2595a23
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_003_pos.ksh
@@ -0,0 +1,141 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_promote_003_pos
+#
+# DESCRIPTION:
+# 'zfs promote' can deal with multi-point snapshots.
+#
+# STRATEGY:
+# 1. Create multiple snapshots and a clone to a middle point snapshot
+# 2. Promote the clone filesystem
+# 3. Verify the origin filesystem and promoted filesystem include
+# correct datasets separated by the clone point.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-05-16)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if snapexists ${csnap[2]}; then
+ log_must $ZFS promote $fs
+ fi
+
+ typeset ds
+ typeset data
+ for ds in ${snap[*]}; do
+ snapexists $ds && \
+ log_must $ZFS destroy -rR $ds
+ done
+ for data in ${file[*]}; do
+ [[ -e $data ]] && $RM -f $data
+ done
+
+}
+
+log_assert "'zfs promote' can deal with multi-point snapshots."
+log_onexit cleanup
+
+fs=$TESTPOOL/$TESTFS
+clone=$TESTPOOL/$TESTCLONE
+
+# Define some arrays here to use loop to reduce code amount
+
+# Array which stores the origin snapshots created in the origin filesystem
+set -A snap "${fs}@$TESTSNAP" "${fs}@$TESTSNAP1" "${fs}@$TESTSNAP2" "${fs}@$TESTSNAP3"
+# Array which stores the snapshots existing in the clone after promote operation
+set -A csnap "${clone}@$TESTSNAP" "${clone}@$TESTSNAP1" "${clone}@$TESTSNAP2" \
+ "${clone}@$TESTSNAP3"
+# The data will inject into the origin filesystem
+set -A file "$TESTDIR/$TESTFILE0" "$TESTDIR/$TESTFILE1" "$TESTDIR/$TESTFILE2" \
+ "$TESTDIR/$TESTFILE3"
+snapdir=$TESTDIR/$(get_snapdir_name)
+# The data which will exist in the snapshot after creation of snapshot
+set -A snapfile "$snapdir/$TESTSNAP/$TESTFILE0" "$snapdir/$TESTSNAP1/$TESTFILE1" \
+ "$snapdir/$TESTSNAP2/$TESTFILE2" "$snapdir/$TESTSNAP3/$TESTFILE3"
+csnapdir=/$clone/$(get_snapdir_name)
+# The data which will exist in the snapshot of clone filesystem after promote
+set -A csnapfile "${csnapdir}/$TESTSNAP/$TESTFILE0" "${csnapdir}/$TESTSNAP1/$TESTFILE1" \
+ "${csnapdir}/$TESTSNAP2/$TESTFILE2"
+
+# setup for promote testing
+typeset -i i=0
+while (( i < 4 )); do
+ log_must $MKFILE $FILESIZE ${file[i]}
+ (( i>0 )) && log_must $RM -f ${file[((i-1))]}
+ log_must $ZFS snapshot ${snap[i]}
+
+ (( i = i + 1 ))
+done
+log_must $ZFS clone ${snap[2]} $clone
+log_must $MKFILE $FILESIZE /$clone/$CLONEFILE
+log_must $RM -f /$clone/$TESTFILE2
+log_must $ZFS snapshot ${csnap[3]}
+
+log_must $ZFS promote $clone
+
+# verify the 'promote' operation
+for ds in ${snap[3]} ${csnap[*]}; do
+ ! snapexists $ds && \
+ log_fail "The snapshot $ds disappear after zfs promote."
+done
+for data in ${csnapfile[*]} $TESTDIR/$TESTFILE3 /$clone/$CLONEFILE; do
+ [[ ! -e $data ]] && \
+ log_fail "The data file $data loses after zfs promote."
+done
+
+for ds in ${snap[0]} ${snap[1]} ${snap[2]}; do
+ snapexists $ds && \
+ log_fail "zfs promote cannot promote the snapshot $ds."
+done
+for data in ${snapfile[0]} ${snapfile[1]} ${snapfile[2]}; do
+ [[ -e $data ]] && \
+ log_fail "zfs promote cannot promote the data $data."
+done
+
+origin_prop=$(get_prop origin $fs)
+[[ "$origin_prop" != "${csnap[2]}" ]] && \
+ log_fail "The dependency is not correct for $fs after zfs promote."
+origin_prop=$(get_prop origin $clone)
+[[ "$origin_prop" != "-" ]] && \
+ log_fail "The dependency is not correct for $clone after zfs promote."
+
+log_pass "'zfs promote' deal with multi-point snapshots as expected."
+
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_004_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_004_pos.ksh
new file mode 100644
index 000000000000..e2648d80a612
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_004_pos.ksh
@@ -0,0 +1,147 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_promote_004_pos
+#
+# DESCRIPTION:
+# 'zfs promote' can deal with multi-level clones.
+#
+# STRATEGY:
+# 1. Create multiple snapshots and multi-level clones
+# 2. Promote a clone filesystem
+# 3. Verify the dataset dependency relationships are correct after promotion.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-05-16)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if snapexists ${c1snap[1]}; then
+ log_must $ZFS promote $clone
+ fi
+
+ typeset ds
+ typeset data
+ for ds in ${snap[*]}; do
+ snapexists $ds && \
+ log_must $ZFS destroy -rR $ds
+ done
+ for data in ${file[*]}; do
+ [[ -e $data ]] && $RM -f $data
+ done
+}
+
+log_assert "'zfs promote' can deal with multi-level clone."
+log_onexit cleanup
+
+fs=$TESTPOOL/$TESTFS
+clone=$TESTPOOL/$TESTCLONE
+clone1=$TESTPOOL/$TESTCLONE1
+
+# Define some arrays here to use loop to reduce code amount
+
+# Array which stores the origin snapshots created in the origin filesystem
+set -A snap "${fs}@$TESTSNAP" "${fs}@$TESTSNAP1" "${fs}@$TESTSNAP2" "${fs}@$TESTSNAP3"
+# Array which stores the snapshots existing in the first clone
+set -A csnap "${clone}@$TESTSNAP3" "${clone}@$TESTSNAP4" "${clone}@$TESTSNAP5"
+# Array which stores the snapshots existing in the second clone after promote operation
+set -A c1snap "${clone1}@$TESTSNAP3" "${clone1}@$TESTSNAP4" "${clone1}@$TESTSNAP5"
+# The data will inject into the origin filesystem
+set -A file "$TESTDIR/$TESTFILE0" "$TESTDIR/$TESTFILE1" "$TESTDIR/$TESTFILE2" \
+ "$TESTDIR/$TESTFILE3"
+cdir=/$TESTPOOL/$TESTCLONE
+# The data will inject into the first clone
+set -A cfile "${cdir}/$CLONEFILE" "${cdir}/$CLONEFILE1" "${cdir}/$CLONEFILE2"
+c1snapdir=/$TESTPOOL/$TESTCLONE1/$(get_snapdir_name)
+# The data which will exist in the snapshot of the second clone filesystem after promote
+set -A c1snapfile "${c1snapdir}/$TESTSNAP3/$CLONEFILE" \
+ "${c1snapdir}/$TESTSNAP4/$CLONEFILE1" \
+ "${c1snapdir}/$TESTSNAP5/$CLONEFILE2"
+
+# setup for promote testing
+typeset -i i=0
+while (( i < 4 )); do
+ log_must $MKFILE $FILESIZE ${file[i]}
+ (( i>0 )) && log_must $RM -f ${file[((i-1))]}
+ log_must $ZFS snapshot ${snap[i]}
+
+ (( i = i + 1 ))
+done
+log_must $ZFS clone ${snap[2]} $clone
+
+log_must $RM -f /$clone/$TESTFILE2
+i=0
+while (( i < 3 )); do
+ log_must $MKFILE $FILESIZE ${cfile[i]}
+ (( i>0 )) && log_must $RM -f ${cfile[(( i-1 ))]}
+ log_must $ZFS snapshot ${csnap[i]}
+
+ (( i = i + 1 ))
+done
+
+log_must $ZFS clone ${csnap[1]} $clone1
+log_must $MKFILE $FILESIZE /$clone1/$CLONEFILE2
+log_must $RM -f /$clone1/$CLONEFILE1
+log_must $ZFS snapshot ${c1snap[2]}
+
+log_must $ZFS promote $clone1
+
+# verify the 'promote' operation
+for ds in ${snap[*]} ${csnap[2]} ${c1snap[*]}; do
+ ! snapexists $ds && \
+ log_fail "The snapshot $ds disappear after zfs promote."
+done
+for data in ${c1snapfile[*]}; do
+ [[ ! -e $data ]] && \
+ log_fail "The data file $data loses after zfs promote."
+done
+
+origin_prop=$(get_prop origin $fs)
+[[ "$origin_prop" != "-" ]] && \
+ log_fail "The dependency is not correct for $fs after zfs promote."
+origin_prop=$(get_prop origin $clone)
+[[ "$origin_prop" != "${c1snap[1]}" ]] && \
+ log_fail "The dependency is not correct for $clone after zfs promote."
+origin_prop=$(get_prop origin $clone1)
+[[ "$origin_prop" != "${snap[2]}" ]] && \
+ log_fail "The dependency is not correct for $clone1 after zfs promote."
+
+log_pass "'zfs promote' deal with multi-level clones as expected."
+
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_005_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_005_pos.ksh
new file mode 100644
index 000000000000..03580fdc167a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_005_pos.ksh
@@ -0,0 +1,82 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_promote_005_pos
+#
+# DESCRIPTION:
+# The original fs was unmounted, 'zfs promote' still should succeed.
+#
+# STRATEGY:
+# 1. Create pool, fs and snapshot.
+# 2. Create clone of fs.
+# 3. Unmount fs, then verify 'zfs promote' clone still succeed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-07-19)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if datasetexists $fssnap ; then
+ datasetexists $clone && log_must $ZFS destroy $clone
+ log_must $ZFS destroy $fssnap
+ fi
+ if datasetexists $clone ; then
+ log_must $ZFS promote $fs
+ log_must $ZFS destroy $clone
+ log_must $ZFS destroy $fssnap
+ fi
+}
+
+log_assert "The original fs was unmounted, 'zfs promote' still should succeed."
+log_onexit cleanup
+
+fs=$TESTPOOL/$TESTFS
+clone=$TESTPOOL/$TESTCLONE
+fssnap=$fs@fssnap
+
+log_must $ZFS snapshot $fssnap
+log_must $ZFS clone $fssnap $clone
+log_must $ZFS unmount $fs
+log_must $ZFS promote $clone
+log_must $ZFS unmount $clone
+log_must $ZFS promote $fs
+
+log_pass "Unmount original fs, 'zfs promote' passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_006_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_006_neg.ksh
new file mode 100644
index 000000000000..ac3169d3aecb
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_006_neg.ksh
@@ -0,0 +1,93 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_promote_006_neg
+#
+# DESCRIPTION:
+# 'zfs promote' will fail with invalid arguments:
+# (1) NULL arguments
+# (2) non-existent clone
+# (3) non-clone datasets:
+# pool, fs, snapshot,volume
+# (4) too many arguments.
+# (5) invalid options
+#
+# STRATEGY:
+# 1. Create an array of invalid arguments
+# 2. For each invalid argument in the array, 'zfs promote' should fail
+# 3. Verify the return code from zfs promote
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-05-16)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+snap=$TESTPOOL/$TESTFS@$TESTSNAP
+set -A args "" \
+ "$TESTPOOL/blah" \
+ "$TESTPOOL" "$TESTPOOL/$TESTFS" "$snap" \
+ "$TESTPOOL/$TESTVOL" "$TESTPOL $TESTPOOL/$TESTFS" \
+ "$clone $TESTPOOL/$TESTFS" "- $clone" "-? $clone"
+
+function cleanup
+{
+ if datasetexists $clone; then
+ log_must $ZFS destroy $clone
+ fi
+
+ if snapexists $snap; then
+ destroy_snapshot $snap
+ fi
+}
+
+log_assert "'zfs promote' will fail with invalid arguments. "
+log_onexit cleanup
+
+snap=$TESTPOOL/$TESTFS@$TESTSNAP
+clone=$TESTPOOL/$TESTCLONE
+log_must $ZFS snapshot $snap
+log_must $ZFS clone $snap $clone
+
+typeset -i i=0
+while (( i < ${#args[*]} )); do
+ log_mustnot $ZFS promote ${args[i]}
+
+ (( i = i + 1 ))
+done
+
+log_pass "'zfs promote' fails with invalid argument as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_007_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_007_neg.ksh
new file mode 100644
index 000000000000..d19435cb7db2
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_007_neg.ksh
@@ -0,0 +1,85 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_promote_007_neg
+#
+# DESCRIPTION:
+# 'zfs promote' can deal with conflicts in the namespaces.
+#
+# STRATEGY:
+# 1. Create a snapshot and a clone of the snapshot
+# 2. Create the same name snapshot for the clone
+# 3. Promote the clone filesystem
+# 4. Verify the promote operation fail due to the name conflicts.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-05-16)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ snapexists $snap && \
+ log_must $ZFS destroy -rR $snap
+
+ typeset data
+ for data in $TESTDIR/$TESTFILE0 $TESTDIR/$TESTFILE1; do
+ [[ -e $data ]] && $RM -f $data
+ done
+}
+
+log_assert "'zfs promote' can deal with name conflicts."
+log_onexit cleanup
+
+snap=$TESTPOOL/$TESTFS@$TESTSNAP
+clone=$TESTPOOL/$TESTCLONE
+clonesnap=$TESTPOOL/$TESTCLONE@$TESTSNAP
+
+# setup for promte testing
+log_must $MKFILE $FILESIZE $TESTDIR/$TESTFILE0
+log_must $ZFS snapshot $snap
+log_must $MKFILE $FILESIZE $TESTDIR/$TESTFILE1
+log_must $RM -f $TESTDIR/$TESTFILE0
+log_must $ZFS clone $snap $clone
+log_must $MKFILE $FILESIZE /$clone/$CLONEFILE
+log_must $ZFS snapshot $clonesnap
+
+log_mustnot $ZFS promote $clone
+
+log_pass "'zfs promote' deals with name conflicts as expected."
+
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_008_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_008_pos.ksh
new file mode 100644
index 000000000000..914c59656e40
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_008_pos.ksh
@@ -0,0 +1,92 @@
+#!/usr/local/bin/ksh93
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_promote_008_pos
+#
+# DESCRIPTION:
+# 'zfs promote' can successfully promote a volume clone.
+#
+# STRATEGY:
+# 1. Create a volume clone
+# 2. Promote the volume clone
+# 3. Verify the dependency changed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-02-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ if snapexists $csnap; then
+ log_must $ZFS promote $vol
+ fi
+
+ log_must $ZFS destroy -rR $snap
+}
+
+log_assert "'zfs promote' can promote a volume clone."
+log_onexit cleanup
+
+vol=$TESTPOOL/$TESTVOL
+snap=$vol@$TESTSNAP
+clone=$TESTPOOL/volclone
+csnap=$clone@$TESTSNAP
+
+if ! snapexists $snap ; then
+ log_must $ZFS snapshot $snap
+ log_must $ZFS clone $snap $clone
+fi
+
+log_must $ZFS promote $clone
+
+# verify the 'promote' operation
+! snapexists $csnap && \
+ log_fail "Snapshot $csnap doesn't exist after zfs promote."
+snapexists $snap && \
+ log_fail "Snapshot $snap is still there after zfs promote."
+
+origin_prop=$(get_prop origin $vol)
+[[ "$origin_prop" != "$csnap" ]] && \
+ log_fail "The dependency of $vol is not correct."
+origin_prop=$(get_prop origin $clone)
+[[ "$origin_prop" != "-" ]] && \
+ log_fail "The dependency of $clone is not correct."
+
+log_pass "'zfs promote' can promote volume clone as expected."
+
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_common.kshlib b/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_common.kshlib
new file mode 100644
index 000000000000..7077fa296649
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_common.kshlib
@@ -0,0 +1,43 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+#
+# Check whether the operating filesystem support 'zfs promote' or not
+#
+function unsupport_check
+{
+ typeset srch_str="unrecognized command"
+ typeset tmpout=$TMPDIR/tmpout.${TESTCASE_ID}
+ typeset -i ret
+
+ $ZFS promote >$tmpout 2>&1
+ $GREP "$srch_str" $tmpout >/dev/null 2>&1
+ ret=$?
+
+ $RM -f $tmpout
+
+ return $ret
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_test.sh
new file mode 100755
index 000000000000..5178608bf98d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_promote/zfs_promote_test.sh
@@ -0,0 +1,246 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zfs_promote_001_pos cleanup
+zfs_promote_001_pos_head()
+{
+ atf_set "descr" "'zfs promote' can promote a clone filesystem."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_promote_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_promote_common.kshlib
+ . $(atf_get_srcdir)/zfs_promote.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_promote_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_promote_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_promote_common.kshlib
+ . $(atf_get_srcdir)/zfs_promote.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_promote_002_pos cleanup
+zfs_promote_002_pos_head()
+{
+ atf_set "descr" "'zfs promote' can deal with multiple snapshots in a filesystem."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_promote_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_promote_common.kshlib
+ . $(atf_get_srcdir)/zfs_promote.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_promote_002_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_promote_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_promote_common.kshlib
+ . $(atf_get_srcdir)/zfs_promote.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_promote_003_pos cleanup
+zfs_promote_003_pos_head()
+{
+ atf_set "descr" "'zfs promote' can deal with multi-point snapshots."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_promote_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_promote_common.kshlib
+ . $(atf_get_srcdir)/zfs_promote.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_promote_003_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_promote_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_promote_common.kshlib
+ . $(atf_get_srcdir)/zfs_promote.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_promote_004_pos cleanup
+zfs_promote_004_pos_head()
+{
+ atf_set "descr" "'zfs promote' can deal with multi-level clone."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_promote_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_promote_common.kshlib
+ . $(atf_get_srcdir)/zfs_promote.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_promote_004_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_promote_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_promote_common.kshlib
+ . $(atf_get_srcdir)/zfs_promote.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_promote_005_pos cleanup
+zfs_promote_005_pos_head()
+{
+ atf_set "descr" "The original fs was unmounted, 'zfs promote' still should succeed."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_promote_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_promote_common.kshlib
+ . $(atf_get_srcdir)/zfs_promote.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_promote_005_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_promote_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_promote_common.kshlib
+ . $(atf_get_srcdir)/zfs_promote.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_promote_006_neg cleanup
+zfs_promote_006_neg_head()
+{
+ atf_set "descr" "'zfs promote' will fail with invalid arguments."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_promote_006_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_promote_common.kshlib
+ . $(atf_get_srcdir)/zfs_promote.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_promote_006_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_promote_006_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_promote_common.kshlib
+ . $(atf_get_srcdir)/zfs_promote.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_promote_007_neg cleanup
+zfs_promote_007_neg_head()
+{
+ atf_set "descr" "'zfs promote' can deal with name conflicts."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_promote_007_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_promote_common.kshlib
+ . $(atf_get_srcdir)/zfs_promote.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_promote_007_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_promote_007_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_promote_common.kshlib
+ . $(atf_get_srcdir)/zfs_promote.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_promote_008_pos cleanup
+zfs_promote_008_pos_head()
+{
+ atf_set "descr" "'zfs promote' can promote a volume clone."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_promote_008_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_promote_common.kshlib
+ . $(atf_get_srcdir)/zfs_promote.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_promote_008_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_promote_008_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_promote_common.kshlib
+ . $(atf_get_srcdir)/zfs_promote.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zfs_promote_001_pos
+ atf_add_test_case zfs_promote_002_pos
+ atf_add_test_case zfs_promote_003_pos
+ atf_add_test_case zfs_promote_004_pos
+ atf_add_test_case zfs_promote_005_pos
+ atf_add_test_case zfs_promote_006_neg
+ atf_add_test_case zfs_promote_007_neg
+ atf_add_test_case zfs_promote_008_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_property/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zfs_property/Makefile
new file mode 100644
index 000000000000..91b14e1aeadd
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_property/Makefile
@@ -0,0 +1,16 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zfs_property
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zfs_property_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zfs_set_property_001_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zfs_property.cfg
+${PACKAGE}FILES+= cleanup.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_property/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_property/cleanup.ksh
new file mode 100644
index 000000000000..713cb16bb31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_property/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_property/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_property/setup.ksh
new file mode 100644
index 000000000000..326bc555f71b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_property/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_property/zfs_property.cfg b/tests/sys/cddl/zfs/tests/cli_root/zfs_property/zfs_property.cfg
new file mode 100644
index 000000000000..99d93c9d9c72
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_property/zfs_property.cfg
@@ -0,0 +1,27 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_property/zfs_property_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zfs_property/zfs_property_test.sh
new file mode 100755
index 000000000000..52f1df9db765
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_property/zfs_property_test.sh
@@ -0,0 +1,55 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zfs_set_property_001_pos cleanup
+zfs_set_property_001_pos_head()
+{
+ atf_set "descr" "Verify each of the file system properties."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_set_property_001_pos_body()
+{
+ atf_skip "Due to changing zfs ls output, test needs a re-write."
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_property.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_set_property_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_set_property_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_property.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zfs_set_property_001_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_property/zfs_set_property_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_property/zfs_set_property_001_pos.ksh
new file mode 100644
index 000000000000..7377179e8fd6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_property/zfs_set_property_001_pos.ksh
@@ -0,0 +1,83 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_set_property_001_pos
+#
+# DESCRIPTION:
+# For each property verify that it accepts on/off/inherit.
+#
+# STRATEGY:
+# 1. Create an array of properties.
+# 2. Create an array of possible values.
+# 3. For each property set to every possible value.
+# 4. Verify success is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+
+set -A options "on" "off" "inherit"
+set -A args "compression" "checksum" "mutable" "atime"
+
+log_assert "Verify each of the file system properties."
+
+log_untested "Due to changing zfs ls output, test needs a re-write."
+
+typeset -i i=0
+typeset -i j=0
+
+while [[ $i -lt ${#args[*]} ]]; do
+ j=0
+ while [[ $j -lt ${#options[*]} ]]; do
+ log_must $ZFS ${args[i]}=${options[j]} $TESTPOOL/$TESTFS
+
+ $ZFS ls -L | $GREP "${args[i]}" | $GREP "${options[j]}"
+ [[ $? -ne 0 ]] && \
+ log_fail "Unable to verify ${args[i]}=${options[j]}"
+
+ log_note "Verified ${args[i]}=${options[j]}"
+
+ ((j = j + 1))
+ done
+
+ ((i = i + 1))
+done
+
+log_pass "zfs properties were set correctly."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/Makefile
new file mode 100644
index 000000000000..066039a3fa04
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/Makefile
@@ -0,0 +1,24 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zfs_receive
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zfs_receive_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zfs_receive_004_neg.ksh
+${PACKAGE}FILES+= zfs_receive_001_pos.ksh
+${PACKAGE}FILES+= zfs_receive_009_neg.ksh
+${PACKAGE}FILES+= zfs_receive_005_neg.ksh
+${PACKAGE}FILES+= zfs_receive_008_pos.ksh
+${PACKAGE}FILES+= zfs_receive.cfg
+${PACKAGE}FILES+= zfs_receive_007_neg.ksh
+${PACKAGE}FILES+= zfs_receive_002_pos.ksh
+${PACKAGE}FILES+= zfs_receive_006_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zfs_receive_003_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/cleanup.ksh
new file mode 100644
index 000000000000..9bbd16b5acbf
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/cleanup.ksh
@@ -0,0 +1,32 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+poolexists $TESTPOOL1 && \
+ destroy_pool $TESTPOOL1
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/setup.ksh
new file mode 100644
index 000000000000..f5aaf64197e7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/setup.ksh
@@ -0,0 +1,34 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+if is_global_zone; then
+ default_volume_setup $DISK
+else
+ default_setup $DISK
+fi
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive.cfg b/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive.cfg
new file mode 100644
index 000000000000..c1a2a26ca210
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive.cfg
@@ -0,0 +1,31 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
+
+export PARTSIZE=1g
+export BLOCK_SIZE=512
+export WRITE_COUNT=8
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_001_pos.ksh
new file mode 100644
index 000000000000..ec8f45d6b9d7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_001_pos.ksh
@@ -0,0 +1,183 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_receive_001_pos
+#
+# DESCRIPTION:
+# Verifying 'zfs receive [<filesystem|snapshot>] -d <filesystem>' works.
+#
+# STRATEGY:
+# 1. Fill in fs with some data
+# 2. Create full and incremental send stream
+# 3. Receive the send stream
+# 4. Verify the restoring results.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-09-06)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ typeset -i i=0
+
+ datasetexists $rst_root && \
+ log_must $ZFS destroy -Rf $rst_root
+ while (( i < 2 )); do
+ snapexists ${orig_snap[$i]} && \
+ log_must $ZFS destroy -f ${orig_snap[$i]}
+ log_must $RM -f ${bkup[$i]}
+
+ (( i = i + 1 ))
+ done
+
+ log_must $RM -rf $TESTDIR1
+}
+
+function recreate_root
+{
+ datasetexists $rst_root && \
+ log_must $ZFS destroy -Rf $rst_root
+ if [[ -d $TESTDIR1 ]] ; then
+ log_must $RM -rf $TESTDIR1
+ fi
+ log_must $ZFS create $rst_root
+ log_must $ZFS set mountpoint=$TESTDIR1 $rst_root
+}
+
+log_assert "Verifying 'zfs receive [<filesystem|snapshot>] -d <filesystem>' works."
+log_onexit cleanup
+
+typeset datasets="$TESTPOOL/$TESTFS $TESTPOOL"
+set -A bkup "$TMPDIR/fullbkup" "$TMPDIR/incbkup"
+orig_sum=""
+rst_sum=""
+rst_root=$TESTPOOL/rst_ctr
+rst_fs=${rst_root}/$TESTFS
+
+for orig_fs in $datasets ; do
+ #
+ # Preparations for testing
+ #
+ recreate_root
+
+ set -A orig_snap "${orig_fs}@init_snap" "${orig_fs}@inc_snap"
+ typeset mntpnt=$(get_prop mountpoint ${orig_fs})
+ set -A orig_data "${mntpnt}/$TESTFILE1" "${mntpnt}/$TESTFILE2"
+
+ typeset relative_path=""
+ if [[ ${orig_fs} == *"/"* ]]; then
+ relative_path=${orig_fs#*/}
+ fi
+
+ typeset leaf_fs=${rst_root}/${relative_path}
+ leaf_fs=${leaf_fs%/}
+ rst_snap=${leaf_fs}@snap
+
+ set -A rst_snap "$rst_root/$TESTFS@init_snap" "$rst_root/$TESTFS@inc_snap"
+ set -A rst_snap2 "${leaf_fs}@init_snap" "${leaf_fs}@inc_snap"
+ set -A rst_data "$TESTDIR1/$TESTFS/$TESTFILE1" "$TESTDIR1/$TESTFS/$TESTFILE2"
+ set -A rst_data2 "$TESTDIR1/${relative_path}/$TESTFILE1" "$TESTDIR1/${relative_path}/$TESTFILE2"
+
+ typeset -i i=0
+ while (( i < ${#orig_snap[*]} )); do
+ log_must $FILE_WRITE -o create -f ${orig_data[$i]} \
+ -b $BLOCK_SIZE -c $WRITE_COUNT
+ log_must $ZFS snapshot ${orig_snap[$i]}
+ if (( i < 1 )); then
+ log_must eval "$ZFS send ${orig_snap[$i]} > ${bkup[$i]}"
+ else
+ log_must eval "$ZFS send -i ${orig_snap[(( i - 1 ))]} \
+ ${orig_snap[$i]} > ${bkup[$i]}"
+ fi
+
+ (( i = i + 1 ))
+ done
+
+ log_note "Verifying 'zfs receive <filesystem>' works."
+ i=0
+ while (( i < ${#bkup[*]} )); do
+ if (( i > 0 )); then
+ log_must $ZFS rollback ${rst_snap[0]}
+ fi
+ log_must eval "$ZFS receive $rst_fs < ${bkup[$i]}"
+ snapexists ${rst_snap[$i]} || \
+ log_fail "Restoring filesystem fails. ${rst_snap[$i]} not exist"
+ compare_cksum ${orig_data[$i]} ${rst_data[$i]}
+
+ (( i = i + 1 ))
+ done
+
+ log_must $ZFS destroy -Rf $rst_fs
+
+ log_note "Verifying 'zfs receive <snapshot>' works."
+ i=0
+ while (( i < ${#bkup[*]} )); do
+ if (( i > 0 )); then
+ log_must $ZFS rollback ${rst_snap[0]}
+ fi
+ log_must eval "$ZFS receive ${rst_snap[$i]} <${bkup[$i]}"
+ snapexists ${rst_snap[$i]} || \
+ log_fail "Restoring filesystem fails. ${rst_snap[$i]} not exist"
+ compare_cksum ${orig_data[$i]} ${rst_data[$i]}
+
+ (( i = i + 1 ))
+ done
+
+ log_must $ZFS destroy -Rf $rst_fs
+
+ log_note "Verfiying 'zfs receive -d <filesystem>' works."
+
+ i=0
+ while (( i < ${#bkup[*]} )); do
+ if (( i > 0 )); then
+ log_must $ZFS rollback ${rst_snap2[0]}
+ fi
+ log_must eval "$ZFS receive -d -F $rst_root <${bkup[$i]}"
+ snapexists ${rst_snap2[$i]} || \
+ log_fail "Restoring filesystem fails. ${rst_snap2[$i]} not exist"
+ compare_cksum ${orig_data[$i]} ${rst_data2[$i]}
+
+ (( i = i + 1 ))
+ done
+
+ cleanup
+done
+
+log_pass "Verifying 'zfs receive [<filesystem|snapshot>] -d <filesystem>' succeeds."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_002_pos.ksh
new file mode 100644
index 000000000000..07a9638359cf
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_002_pos.ksh
@@ -0,0 +1,117 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_receive_002_pos
+#
+# DESCRIPTION:
+# Verifying 'zfs receive <volume>' works.
+#
+# STRATEGY:
+# 1. Fill in volume with some data
+# 2. Create full and incremental send stream
+# 3. Restore the send stream
+# 4. Verify the restoring results.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-09-06)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ typeset -i i=0
+ typeset ds
+
+ while (( i < ${#orig_snap[*]} )); do
+ snapexists ${rst_snap[$i]} && \
+ log_must $ZFS destroy -f ${rst_snap[$i]}
+ snapexists ${orig_snap[$i]} && \
+ log_must $ZFS destroy -f ${orig_snap[$i]}
+ [[ -e ${bkup[$i]} ]] && \
+ log_must $RM -rf ${bkup[$i]}
+
+ (( i = i + 1 ))
+ done
+
+ for ds in $rst_vol $rst_root; do
+ datasetexists $ds && \
+ log_must $ZFS destroy -Rf $ds
+ done
+}
+
+log_assert "Verifying 'zfs receive <volume>' works."
+log_onexit cleanup
+
+set -A orig_snap "$TESTPOOL/$TESTVOL@init_snap" "$TESTPOOL/$TESTVOL@inc_snap"
+set -A bkup "$TMPDIR/fullbkup" "$TMPDIR/incbkup"
+rst_root=$TESTPOOL/rst_ctr
+rst_vol=$rst_root/$TESTVOL
+set -A rst_snap "${rst_vol}@init_snap" "${rst_vol}@inc_snap"
+
+#
+# Preparations for testing
+#
+log_must $ZFS create $rst_root
+[[ ! -d $TESTDIR1 ]] && \
+ log_must $MKDIR -p $TESTDIR1
+log_must $ZFS set mountpoint=$TESTDIR1 $rst_root
+
+typeset -i i=0
+while (( i < ${#orig_snap[*]} )); do
+ log_must $ZFS snapshot ${orig_snap[$i]}
+ if (( i < 1 )); then
+ log_must eval "$ZFS send ${orig_snap[$i]} > ${bkup[$i]}"
+ else
+ log_must eval "$ZFS send -i ${orig_snap[(( i - 1 ))]} \
+ ${orig_snap[$i]} > ${bkup[$i]}"
+ fi
+
+ (( i = i + 1 ))
+done
+
+i=0
+while (( i < ${#bkup[*]} )); do
+ log_must eval "$ZFS receive $rst_vol < ${bkup[$i]}"
+ ! datasetexists $rst_vol || ! snapexists ${rst_snap[$i]} && \
+ log_fail "Restoring volume fails."
+
+ (( i = i + 1 ))
+done
+
+log_pass "Verifying 'zfs receive <volume>' succeeds."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_003_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_003_pos.ksh
new file mode 100644
index 000000000000..3d852e933750
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_003_pos.ksh
@@ -0,0 +1,102 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_receive_003_pos
+#
+# DESCRIPTION:
+# 'zfs recv -F' to force rollback.
+#
+# STRATEGY:
+# 1. Create pool and fs.
+# 2. Create some files in fs and take a snapshot1.
+# 3. Create another files in fs and take snapshot2.
+# 4. Create incremental stream from snapshot1 to snapshot2.
+# 5. fs rollback to snapshot1 and modify fs.
+# 6. Verify 'zfs recv -F' can force rollback.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-07-18)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ for snap in $snap2 $snap1; do
+ datasetexists $snap && log_must $ZFS destroy -rf $snap
+ done
+ for file in $ibackup $mntpnt/file1 $mntpnt/file2; do
+ [[ -f $file ]] && log_must $RM -f $file
+ done
+}
+
+log_assert "'zfs recv -F' to force rollback."
+log_onexit cleanup
+
+ibackup=$TMPDIR/ibackup.${TESTCASE_ID}
+fs=$TESTPOOL/$TESTFS; snap1=$fs@snap1; snap2=$fs@snap2
+
+mntpnt=$(get_prop mountpoint $fs) || log_fail "get_prop mountpoint $fs"
+log_must $MKFILE 10m $mntpnt/file1
+log_must $ZFS snapshot $snap1
+log_must $MKFILE 10m $mntpnt/file2
+log_must $ZFS snapshot $snap2
+
+log_must eval "$ZFS send -i $snap1 $snap2 > $ibackup"
+
+log_note "Verify 'zfs receive' succeed, if filesystem was not modified."
+log_must $ZFS rollback -r $snap1
+log_must eval "$ZFS receive $fs < $ibackup"
+if [[ ! -f $mntpnt/file1 || ! -f $mntpnt/file2 ]]; then
+ log_fail "'$ZFS receive' failed."
+fi
+
+log_note "Verify 'zfs receive' failed if filesystem was modified."
+log_must $ZFS rollback -r $snap1
+log_must $RM -rf $mntpnt/file1
+log_mustnot eval "$ZFS receive $fs < $ibackup"
+
+# Verify 'zfs receive -F' to force rollback whatever filesystem was modified.
+log_must $ZFS rollback -r $snap1
+log_must $RM -rf $mntpnt/file1
+log_must eval "$ZFS receive -F $fs < $ibackup"
+if [[ ! -f $mntpnt/file1 || ! -f $mntpnt/file2 ]]; then
+ log_fail "'$ZFS receive -F' failed."
+fi
+
+log_pass "'zfs recv -F' to force rollback passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_004_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_004_neg.ksh
new file mode 100644
index 000000000000..c78d2ca9252a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_004_neg.ksh
@@ -0,0 +1,114 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_receive_004_neg
+#
+# DESCRIPTION:
+# Verify 'zfs receive' fails with malformed parameters.
+#
+# STRATEGY:
+# 1. Denfine malformed parameters array
+# 2. Feed the malformed parameters to 'zfs receive'
+# 3. Verify the command should be failed
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-09-06)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ typeset snap
+ typeset bkup
+
+ for snap in $init_snap $inc_snap $init_topsnap $inc_topsnap ; do
+ snapexists $snap && \
+ log_must $ZFS destroy -Rf $snap
+ done
+
+ for bkup in $full_bkup $inc_bkup $full_topbkup $inc_topbkup; do
+ [[ -e $bkup ]] && \
+ log_must $RM -f $bkup
+ done
+}
+
+log_assert "Verify that invalid parameters to 'zfs receive' are caught."
+log_onexit cleanup
+
+init_snap=$TESTPOOL/$TESTFS@initsnap
+inc_snap=$TESTPOOL/$TESTFS@incsnap
+full_bkup=$TMPDIR/full_bkup.${TESTCASE_ID}
+inc_bkup=$TMPDIR/inc_bkup.${TESTCASE_ID}
+
+init_topsnap=$TESTPOOL@initsnap
+inc_topsnap=$TESTPOOL@incsnap
+full_topbkup=$TMPDIR/full_topbkup.${TESTCASE_ID}
+inc_topbkup=$TMPDIR/inc_topbkup.${TESTCASE_ID}
+
+log_must $ZFS snapshot $init_topsnap
+log_must eval "$ZFS send $init_topsnap > $full_topbkup"
+
+log_must $ZFS snapshot $inc_topsnap
+log_must eval "$ZFS send -i $init_topsnap $inc_topsnap > $inc_topbkup"
+
+log_must $ZFS snapshot $init_snap
+log_must eval "$ZFS send $init_snap > $full_bkup"
+
+log_must $ZFS snapshot $inc_snap
+log_must eval "$ZFS send -i $init_snap $inc_snap > $inc_bkup"
+
+set -A badargs \
+ "" "nonexistent-snap" "blah@blah" "$snap1" "$snap1 $snap2" \
+ "-d" "-d nonexistent-dataset" \
+ "$TESTPOOL/fs@" "$TESTPOOL/fs@@mysnap" "$TESTPOOL/fs@@" \
+ "$TESTPOOL/fs/@mysnap" "$TESTPOOL/fs@/mysnap" \
+ "$TESTPOOL/nonexistent-fs/nonexistent-fs" \
+ "-d $TESTPOOL/nonexistent-fs" "-d $TESTPOOL/$TESTFS/nonexistent-fs"
+
+typeset -i i=0
+while (( i < ${#badargs[*]} ))
+do
+ for bkup in $full_bkup $inc_bkup $full_topbkup $inc_topbkup ; do
+ log_mustnot eval "$ZFS receive ${badargs[i]} < $bkup"
+ done
+
+ (( i = i + 1 ))
+done
+
+log_pass "Invalid parameters to 'zfs receive' are caught as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_005_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_005_neg.ksh
new file mode 100644
index 000000000000..8bff65fd1d42
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_005_neg.ksh
@@ -0,0 +1,108 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_receive_005_neg
+#
+# DESCRIPTION:
+# Verify 'zfs receive' fails with unsupported scenarios.
+# including:
+# (1) Invalid send streams;
+# (2) The received incremental send doesn't match the filesystem
+# latest status.
+#
+# STRATEGY:
+# 1. Preparation for unsupported scenarios
+# 2. Execute 'zfs receive'
+# 3. Verify the results are failed
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-09-06)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ typeset snap
+ typeset bkup
+
+ for snap in $init_snap $inc_snap; do
+ snapexists $snap && \
+ log_must $ZFS destroy -f $snap
+ done
+
+ datasetexists $rst_root && \
+ log_must $ZFS destroy -Rf $rst_root
+
+ for bkup in $full_bkup $inc_bkup; do
+ [[ -e $bkup ]] && \
+ log_must $RM -f $bkup
+ done
+}
+
+log_assert "Verify 'zfs receive' fails with unsupported scenarios."
+log_onexit cleanup
+
+init_snap=$TESTPOOL/$TESTFS@initsnap
+inc_snap=$TESTPOOL/$TESTFS@incsnap
+rst_root=$TESTPOOL/rst_ctr
+rst_init_snap=$rst_root/$TESTFS@init_snap
+rst_inc_snap=$rst_root/$TESTFS@inc_snap
+full_bkup=$TMPDIR/full_bkup.${TESTCASE_ID}
+inc_bkup=$TMPDIR/inc_bkup.${TESTCASE_ID}
+
+log_must $ZFS create $rst_root
+log_must $ZFS snapshot $init_snap
+log_must eval "$ZFS send $init_snap > $full_bkup"
+
+log_note "'zfs receive' fails with invalid send streams."
+log_mustnot eval "$ZFS receive $rst_init_snap < /dev/zero"
+log_mustnot eval "$ZFS receive -d $rst_root </dev/zero"
+
+log_must eval "$ZFS receive $rst_init_snap < $full_bkup"
+
+log_note "Unmatched send stream with restoring filesystem" \
+ " cannot be received."
+log_must $ZFS snapshot $inc_snap
+log_must eval "$ZFS send -i $init_snap $inc_snap > $inc_bkup"
+#make changes on the restoring filesystem
+log_must $TOUCH $ZFSROOT/$rst_root/$TESTFS/tmpfile
+log_mustnot eval "$ZFS receive $rst_inc_snap < $inc_bkup"
+log_mustnot eval "$ZFS receive -d $rst_root < $inc_bkup"
+
+log_pass "Unsupported scenarios to 'zfs receive' fail as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_006_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_006_pos.ksh
new file mode 100644
index 000000000000..7370647ccd50
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_006_pos.ksh
@@ -0,0 +1,117 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_receive_006_pos
+#
+# DESCRIPTION:
+# 'zfs recv -d <fs>' should create ancestor filesystem if it does not
+# exist and it should not fail if it exists
+#
+# STRATEGY:
+# 1. Create pool and fs.
+# 2. Create some files in fs and take snapshots.
+# 3. Keep the stream and restore the stream to the pool
+# 4. Verify receiving the stream succeeds, and the ancestor filesystem
+# is created if it did not exist
+# 5. Verify receiving the stream still succeeds when ancestor filesystem
+# exists
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-10-13)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ for snap in $snap2 $snap1; do
+ datasetexists $snap && log_must $ZFS destroy -rf $snap
+ done
+ for file in $fbackup1 $fbackup2 $mntpnt/file1 $mntpnt/file2; do
+ [[ -f $file ]] && log_must $RM -f $file
+ done
+
+ if is_global_zone; then
+ datasetexists $TESTPOOL/$TESTFS/$TESTFS1 && \
+ log_must $ZFS destroy -rf $TESTPOOL/$TESTFS/$TESTFS1
+ else
+ datasetexists $TESTPOOL/${ZONE_CTR}0 && \
+ log_must $ZFS destroy -rf $TESTPOOL/${ZONE_CTR}0
+ fi
+
+}
+
+log_assert "'zfs recv -d <fs>' should succeed no matter ancestor filesystem \
+ exists."
+log_onexit cleanup
+
+ancestor_fs=$TESTPOOL/$TESTFS
+fs=$TESTPOOL/$TESTFS/$TESTFS1
+snap1=$fs@snap1
+snap2=$fs@snap2
+fbackup1=$TMPDIR/fbackup1.${TESTCASE_ID}
+fbackup2=$TMPDIR/fbackup2.${TESTCASE_ID}
+
+datasetexists $ancestor_fs || \
+ log_must $ZFS create $ancestor_fs
+log_must $ZFS create $fs
+
+mntpnt=$(get_prop mountpoint $fs) || log_fail "get_prop mountpoint $fs"
+log_must $MKFILE 10m $mntpnt/file1
+log_must $ZFS snapshot $snap1
+log_must $MKFILE 10m $mntpnt/file2
+log_must $ZFS snapshot $snap2
+
+log_must eval "$ZFS send $snap1 > $fbackup1"
+log_must eval "$ZFS send $snap2 > $fbackup2"
+
+log_note "Verify 'zfs receive -d' succeed and create ancestor filesystem \
+ if it did not exist. "
+log_must $ZFS destroy -rf $ancestor_fs
+log_must eval "$ZFS receive -d $TESTPOOL < $fbackup1"
+is_global_zone || ancestor_fs=$TESTPOOL/${ZONE_CTR}0/$TESTFS
+datasetexists $ancestor_fs || \
+ log_fail "ancestor filesystem is not created"
+
+log_note "Verify 'zfs receive -d' still succeed if ancestor filesystem exists"
+is_global_zone || fs=$TESTPOOL/${ZONE_CTR}0/$TESTFS/$TESTFS1
+log_must $ZFS destroy -rf $fs
+log_must eval "$ZFS receive -d $TESTPOOL < $fbackup2"
+
+log_pass "'zfs recv -d <fs>' should succeed no matter ancestor filesystem \
+ exists."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_007_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_007_neg.ksh
new file mode 100644
index 000000000000..69c133297a1a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_007_neg.ksh
@@ -0,0 +1,93 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_receive_007_neg
+#
+# DESCRIPTION:
+# 'zfs recv -F' should fail if the incremental stream does not match
+#
+# STRATEGY:
+# 1. Create pool and fs.
+# 2. Create some files in fs and take snapshots.
+# 3. Keep the incremental stream and restore the stream to the pool
+# 4. Verify receiving the stream fails
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-10-13)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ for snap in $snap2 $snap1; do
+ datasetexists $snap && log_must $ZFS destroy -rf $snap
+ done
+ for file in $ibackup $mntpnt/file1 $mntpnt/file2; do
+ [[ -f $file ]] && log_must $RM -f $file
+ done
+}
+
+log_assert "'zfs recv -F' should fail if the incremental stream does not match"
+log_onexit cleanup
+
+fs=$TESTPOOL/$TESTFS
+snap1=$fs@snap1
+snap2=$fs@snap2
+ibackup=$TMPDIR/ibackup.${TESTCASE_ID}
+
+datasetexists $fs || log_must $ZFS create $fs
+
+mntpnt=$(get_prop mountpoint $fs) || log_fail "get_prop mountpoint $fs"
+log_must $MKFILE 10m $mntpnt/file1
+log_must $ZFS snapshot $snap1
+log_must $MKFILE 10m $mntpnt/file2
+log_must $ZFS snapshot $snap2
+
+log_must eval "$ZFS send -i $snap1 $snap2 > $ibackup"
+
+log_must $ZFS destroy $snap1
+log_must $ZFS destroy $snap2
+log_mustnot eval "$ZFS receive -F $fs < $ibackup"
+
+log_must $MKFILE 20m $mntpnt/file1
+log_must $RM -rf $mntpnt/file2
+log_must $ZFS snapshot $snap1
+log_mustnot eval "$ZFS receive -F $snap2 < $ibackup"
+
+log_pass "'zfs recv -F' should fail if the incremental stream does not match"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_008_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_008_pos.ksh
new file mode 100644
index 000000000000..73ad85b44698
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_008_pos.ksh
@@ -0,0 +1,157 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_receive_008_pos
+#
+# DESCRIPTION:
+# Verifying 'zfs receive -vn [<filesystem|snapshot>]
+# and zfs receive -vn -d <filesystem>'
+#
+# STRATEGY:
+# 1. Fill in fs with some data
+# 2. Create full and incremental send stream
+# 3. run zfs receive with -v option
+# 3. Dryrun zfs receive with -vn option
+# 3. Dryrun zfs receive with -vn -d option
+# 4. Verify receive output and result
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-14)
+#
+# __stc_assertion_end
+#
+################################################################################
+function cleanup
+{
+ for dset in $rst_snap $rst_fs $orig_snap; do
+ if datasetexists $dset; then
+ log_must $ZFS destroy -fr $dset
+ fi
+ done
+
+ for file in $fbackup $mnt_file $tmp_out; do
+ if [[ -f $file ]]; then
+ log_must $RM -f $file
+ fi
+ done
+
+ if datasetexists $TESTPOOL/$TESTFS; then
+ log_must $ZFS destroy -Rf $TESTPOOL/$TESTFS
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+ fi
+}
+
+verify_runnable "both"
+log_assert "Verifying 'zfs receive -vn [<filesystem|snapshot>] " \
+ "and zfs receive -vn -d <filesystem>'"
+
+log_onexit cleanup
+
+typeset datasets="$TESTPOOL/$TESTFS $TESTPOOL"
+typeset rst_fs=$TESTPOOL/$TESTFS/$TESTFS
+typeset fbackup=$TMPDIR/fbackup.${TESTCASE_ID}
+typeset tmp_out=$TMPDIR/tmpout.${TESTCASE_ID}
+
+for orig_fs in $datasets ; do
+ typeset rst_snap=$rst_fs@snap
+ typeset orig_snap=$orig_fs@snap
+ typeset verb_msg="receiving full stream of ${orig_snap} into ${rst_snap}"
+ typeset dryrun_msg="would receive full stream of ${orig_snap} into ${rst_snap}"
+
+ if ! datasetexists $orig_fs; then
+ log_must $ZFS create $orig_fs
+ fi
+
+ typeset mntpnt
+ mntpnt=$(get_prop mountpoint $orig_fs)
+ if [[ $? -ne 0 ]] ; then
+ log_fail "get_prop mountpoint $orig_fs failed"
+ fi
+
+ typeset mnt_file=$mntpnt/file1
+
+ log_must $MKFILE 100m $mnt_file
+ log_must $ZFS snapshot $orig_snap
+ log_must eval "$ZFS send $orig_snap > $fbackup"
+
+ for opt in "-v" "-vn"; do
+ if datasetexists $rst_fs; then
+ log_must $ZFS destroy -fr $rst_fs
+ fi
+ log_note "Check ZFS receive $opt [<filesystem|snapshot>]"
+ log_must eval "$ZFS receive $opt $rst_fs < $fbackup > $tmp_out 2>&1"
+ if [[ $opt == "-v" ]]; then
+ log_must eval "$GREP \"$verb_msg\" $tmp_out >/dev/null 2>&1"
+ if ! datasetexists $rst_snap; then
+ log_fail "dataset was not received, even though the"\
+ " -v flag was used."
+ fi
+ else
+ log_must eval "$GREP \"$dryrun_msg\" $tmp_out >/dev/null 2>&1"
+ if datasetexists $rst_snap; then
+ log_fail "dataset was received, even though the -nv"\
+ " flag was used."
+ fi
+ fi
+ done
+
+ log_note "Check ZFS receive -vn -d <filesystem>"
+ if ! datasetexists $rst_fs; then
+ log_must $ZFS create $rst_fs
+ fi
+ log_must eval "$ZFS receive -vn -d -F $rst_fs <$fbackup >$tmp_out 2>&1"
+ typeset relative_path=""
+ if [[ ${orig_fs} == *"/"* ]]; then
+ relative_path=${orig_fs#*/}
+ fi
+
+ typeset leaf_fs=${rst_fs}/${relative_path}
+ leaf_fs=${leaf_fs%/}
+ rst_snap=${leaf_fs}@snap
+ dryrun_msg="would receive full stream of ${orig_snap} into ${rst_snap}"
+
+ log_must eval "$GREP \"$dryrun_msg\" $tmp_out > /dev/null 2>&1"
+
+ if datasetexists $rst_snap; then
+ log_fail "dataset $rst_snap should not existed."
+ fi
+ log_must $ZFS destroy -Rf $rst_fs
+
+ cleanup
+done
+
+log_pass "zfs receive -vn [<filesystem|snapshot>] and " \
+ "zfs receive -vn -d <filesystem>' succeed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_009_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_009_neg.ksh
new file mode 100644
index 000000000000..1453c0c51c7a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_009_neg.ksh
@@ -0,0 +1,127 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_receive_009_neg
+#
+# DESCRIPTION:
+# Verify 'zfs receive' fails with bad options, missing argument or too many
+# arguments.
+#
+# STRATEGY:
+# 1. Set a array of illegal arguments
+# 2. Execute 'zfs receive' with illegal arguments
+# 3. Verify the command should be failed
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-20)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ typeset ds
+
+ if snapexists $snap; then
+ log_must $ZFS destroy $snap
+ fi
+ for ds in $ctr1 $ctr2 $fs1; do
+ if datasetexists $ds; then
+ log_must $ZFS destroy -rf $ds
+ fi
+ done
+ if [[ -d $TESTDIR2 ]]; then
+ $RM -rf $TESTDIR2
+ fi
+}
+
+log_assert "Verify 'zfs receive' fails with bad option, missing or too many arguments"
+log_onexit cleanup
+
+set -A badopts "v" "n" "F" "d" "-V" "-N" "-f" "-D" "-VNfD" "-vNFd" "-vnFD" "-dVnF" \
+ "-vvvNfd" "-blah" "-12345" "-?" "-*" "-%"
+set -A validopts "" "-v" "-n" "-F" "-vn" "-nF" "-vnF" "-vd" "-nd" "-Fd" "-vnFd"
+
+ctr1=$TESTPOOL/$TESTCTR1
+ctr2=$TESTPOOL/$TESTCTR2
+fs1=$TESTPOOL/$TESTFS1
+fs2=$TESTPOOL/$TESTFS2
+fs3=$TESTPOOL/$TESTFS3
+snap=$TESTPOOL/$TESTFS@$TESTSNAP
+bkup=$TESTDIR2/bkup.${TESTCASE_ID}
+
+# Preparations for negative testing
+for ctr in $ctr1 $ctr2; do
+ log_must $ZFS create $ctr
+done
+if [[ -d $TESTDIR2 ]]; then
+ $RM -rf $TESTDIR2
+fi
+log_must $ZFS create -o mountpoint=$TESTDIR2 $fs1
+log_must $ZFS snapshot $snap
+log_must eval "$ZFS send $snap > $bkup"
+
+#Testing zfs receive fails with input from terminal
+log_mustnot eval "$ZFS recv $fs3 </dev/console"
+
+# Testing with missing argument and too many arguments
+typeset -i i=0
+while (( i < ${#validopts[*]} )); do
+ log_mustnot eval "$ZFS recv < $bkup"
+
+ $ECHO ${validopts[i]} | $GREP "d" >/dev/null 2>&1
+ if (( $? != 0 )); then
+ log_mustnot eval "$ZFS recv ${validopts[i]} $fs2 $fs3 < $bkup"
+ else
+ log_mustnot eval "$ZFS recv ${validopts[i]} $ctr1 $ctr2 < $bkup"
+ fi
+
+ (( i += 1 ))
+done
+
+# Testing with bad options
+i=0
+while (( i < ${#badopts[*]} ))
+do
+ log_mustnot eval "$ZFS recv ${badopts[i]} $ctr1 < $bkup"
+ log_mustnot eval "$ZFS recv ${badopts[i]} $fs2 < $bkup"
+
+ (( i = i + 1 ))
+done
+
+log_pass "'zfs receive' as expected with bad options, missing or too many arguments."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_test.sh
new file mode 100755
index 000000000000..0aac0fa0eb10
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_receive/zfs_receive_test.sh
@@ -0,0 +1,255 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zfs_receive_001_pos cleanup
+zfs_receive_001_pos_head()
+{
+ atf_set "descr" "Verifying 'zfs receive [<filesystem|snapshot>] -d <filesystem>' works."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_receive_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_receive.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_receive_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_receive_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_receive.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_receive_002_pos cleanup
+zfs_receive_002_pos_head()
+{
+ atf_set "descr" "Verifying 'zfs receive <volume>' works."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_receive_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_receive.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_receive_002_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_receive_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_receive.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_receive_003_pos cleanup
+zfs_receive_003_pos_head()
+{
+ atf_set "descr" "'zfs recv -F' to force rollback."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_receive_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_receive.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_receive_003_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_receive_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_receive.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_receive_004_neg cleanup
+zfs_receive_004_neg_head()
+{
+ atf_set "descr" "Verify that invalid parameters to 'zfs receive' are caught."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_receive_004_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_receive.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_receive_004_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_receive_004_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_receive.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_receive_005_neg cleanup
+zfs_receive_005_neg_head()
+{
+ atf_set "descr" "Verify 'zfs receive' fails with unsupported scenarios."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_receive_005_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_receive.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_receive_005_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_receive_005_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_receive.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_receive_006_pos cleanup
+zfs_receive_006_pos_head()
+{
+ atf_set "descr" "'zfs recv -d <fs>' should succeed no matter ancestor filesystemexists."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_receive_006_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_receive.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_receive_006_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_receive_006_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_receive.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_receive_007_neg cleanup
+zfs_receive_007_neg_head()
+{
+ atf_set "descr" "'zfs recv -F' should fail if the incremental stream does not match"
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_receive_007_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_receive.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_receive_007_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_receive_007_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_receive.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_receive_008_pos cleanup
+zfs_receive_008_pos_head()
+{
+ atf_set "descr" "Verifying 'zfs receive -vn [<filesystem|snapshot>]and zfs receive -vn -d <filesystem>'"
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_receive_008_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_receive.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_receive_008_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_receive_008_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_receive.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_receive_009_neg cleanup
+zfs_receive_009_neg_head()
+{
+ atf_set "descr" "Verify 'zfs receive' fails with bad option, missing or too many arguments"
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_receive_009_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_receive.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_receive_009_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_receive_009_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_receive.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zfs_receive_001_pos
+ atf_add_test_case zfs_receive_002_pos
+ atf_add_test_case zfs_receive_003_pos
+ atf_add_test_case zfs_receive_004_neg
+ atf_add_test_case zfs_receive_005_neg
+ atf_add_test_case zfs_receive_006_pos
+ atf_add_test_case zfs_receive_007_neg
+ atf_add_test_case zfs_receive_008_pos
+ atf_add_test_case zfs_receive_009_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/Makefile
new file mode 100644
index 000000000000..431ab61354b4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/Makefile
@@ -0,0 +1,29 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zfs_rename
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zfs_rename_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zfs_rename_002_pos.ksh
+${PACKAGE}FILES+= zfs_rename_006_pos.ksh
+${PACKAGE}FILES+= zfs_rename_012_neg.ksh
+${PACKAGE}FILES+= zfs_rename.cfg
+${PACKAGE}FILES+= zfs_rename_013_pos.ksh
+${PACKAGE}FILES+= zfs_rename_003_pos.ksh
+${PACKAGE}FILES+= zfs_rename_007_pos.ksh
+${PACKAGE}FILES+= zfs_rename_001_pos.ksh
+${PACKAGE}FILES+= zfs_rename_004_neg.ksh
+${PACKAGE}FILES+= zfs_rename_008_pos.ksh
+${PACKAGE}FILES+= zfs_rename_009_neg.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zfs_rename_005_neg.ksh
+${PACKAGE}FILES+= zfs_rename.kshlib
+${PACKAGE}FILES+= zfs_rename_010_neg.ksh
+${PACKAGE}FILES+= zfs_rename_011_pos.ksh
+${PACKAGE}FILES+= cleanup.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/cleanup.ksh
new file mode 100644
index 000000000000..076d0e97087d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/cleanup.ksh
@@ -0,0 +1,38 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup_noexit
+
+if [[ -d $TESTDIR2 ]]; then
+ $RM -rf $TESTDIR2
+ if (( $? != 0 )); then
+ log_unresolved Could not remove $TESTDIR2
+ fi
+fi
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/setup.ksh
new file mode 100644
index 000000000000..e8bbdd79f2e0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/setup.ksh
@@ -0,0 +1,43 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_setup_noexit "$DISK" "true" "true"
+
+if [[ -d $TESTDIR2 ]]; then
+ $RM -rf $TESTDIR2
+ if (( $? != 0 )); then
+ log_unresolved Could not remove $TESTDIR2
+ fi
+fi
+log_must $ZFS create $TESTPOOL/$DATAFS
+log_must $ZFS set mountpoint=$TESTDIR2 $TESTPOOL/$DATAFS
+log_must eval "$DD if=$IF of=$OF bs=$BS count=$CNT >/dev/null 2>&1"
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename.cfg b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename.cfg
new file mode 100644
index 000000000000..c5cb08bd4352
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename.cfg
@@ -0,0 +1,36 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
+
+export DATAFS=datafs${TESTCASE_ID}
+export DATA=$TESTDIR2/data.${TESTCASE_ID}
+export IF=/dev/urandom
+export OF=$DATA
+export BS=512
+export CNT=2048
+export VOL_R_PATH=/dev/zvol/$TESTPOOL/$TESTVOL
+export VOLDATA=$TESTDIR2/voldata.${TESTCASE_ID}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename.kshlib b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename.kshlib
new file mode 100644
index 000000000000..039db9d9f004
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename.kshlib
@@ -0,0 +1,112 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+function additional_setup
+{
+ # Create testfile
+ log_must $CP $DATA $TESTDIR/$TESTFILE0
+ log_must $CP $DATA $TESTDIR1/$TESTFILE0
+
+ # Create snapshot
+ if ! snapexists $TESTPOOL/$TESTFS@snapshot; then
+ log_must $ZFS snapshot $TESTPOOL/$TESTFS@snapshot
+ log_must $ZFS clone $TESTPOOL/$TESTFS@snapshot \
+ $TESTPOOL/$TESTFS-clone
+ fi
+
+ # Create file system
+ datasetexists $TESTPOOL/$TESTFS1 || \
+ log_must $ZFS create $TESTPOOL/$TESTFS1
+
+ # Create testfile
+ log_must $CP $DATA $(get_prop mountpoint $TESTPOOL/$TESTFS1)/$TESTFILE0
+
+ # Create container
+ datasetexists $TESTPOOL/$TESTCTR1 || \
+ log_must $ZFS create $TESTPOOL/$TESTCTR1
+ log_must $CP $DATA $(get_prop mountpoint $TESTPOOL/$TESTCTR1)/$TESTFILE0
+
+ # Create data in zvol
+ if is_global_zone; then
+ log_must eval "$DD if=$DATA of=$VOL_R_PATH bs=$BS count=$CNT \
+ >/dev/null 2>&1"
+ else
+ log_must $CP $DATA $(get_prop mountpoint $TESTPOOL/$TESTVOL)/$TESTFILE0
+ fi
+
+}
+
+function rename_dataset # src dest
+{
+ typeset src=$1
+ typeset dest=$2
+
+ log_must $ZFS rename $src $dest
+
+ #
+ # Verify src name no longer in use
+ #
+ log_mustnot datasetexists $src
+ log_must datasetexists $dest
+}
+
+function cleanup
+{
+ typeset -i i=0
+ while ((i < ${#dataset[*]} )); do
+ if ! datasetexists ${dataset[i]}-new ; then
+ ((i = i + 1))
+ continue
+ fi
+
+ if [[ ${dataset[i]}-new != *@* ]] ; then
+ $ZFS rename ${dataset[i]}-new ${dataset[i]}
+ if [[ $? -ne 0 ]]; then
+ typeset newfs=${dataset[i]}-new
+ typeset oldfs=${dataset[i]}
+ typeset mntp=$(get_prop mountpoint $newfs)
+ log_must $ZFS destroy -f $newfs
+ log_must $ZFS create -p $oldfs
+ log_must $ZFS set mountpoint=$mntp $oldfs
+ fi
+ else
+ log_must $ZFS destroy -fR ${dataset[i]}-new
+ fi
+
+ ((i = i + 1))
+ done
+}
+
+function cmp_data #<$1 src data, $2 tgt data>
+{
+ typeset src=$1
+ typeset tgt=$2
+
+ $CMP $src $tgt >/dev/null 2>&1
+
+ return $?
+}
+
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_001_pos.ksh
new file mode 100644
index 000000000000..94be6eeaad4e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_001_pos.ksh
@@ -0,0 +1,116 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_rename/zfs_rename.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_rename_001_pos
+#
+# DESCRIPTION:
+# 'zfs rename' should successfully rename valid datasets.
+# As a sub-assertion we check to ensure the datasets that can
+# be mounted are mounted.
+#
+# STRATEGY:
+# 1. Given a file system, snapshot and volume.
+# 2. Rename each dataset object to a new name.
+# 3. Verify that only the new name is displayed by zfs list.
+# 4. Verify mountable datasets are mounted.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-06-29)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "both"
+
+set -A dataset "$TESTPOOL/$TESTFS@snapshot" "$TESTPOOL/$TESTFS1" \
+ "$TESTPOOL/$TESTCTR/$TESTFS1" "$TESTPOOL/$TESTCTR1" \
+ "$TESTPOOL/$TESTVOL" "$TESTPOOL/$TESTFS-clone"
+set -A mountable "$TESTPOOL/$TESTFS1-new" "$TESTPOOL/$TESTFS@snapshot-new" \
+ "$TESTPOOL/$TESTCTR/$TESTFS1-new" "$TESTPOOL/$TESTFS-clone-new"
+
+#
+# cleanup defined in zfs_rename.kshlib
+#
+log_onexit cleanup
+
+log_assert "'zfs rename' should successfully rename valid datasets"
+
+additional_setup
+
+typeset -i i=0
+while (( i < ${#dataset[*]} )); do
+ rename_dataset ${dataset[i]} ${dataset[i]}-new
+
+ ((i = i + 1))
+done
+
+log_note "Verify mountable datasets are mounted in their new namespace."
+typeset mtpt
+i=0
+while (( i < ${#mountable[*]} )); do
+ # Snapshot have no mountpoint
+ if [[ ${mountable[i]} != *@* ]]; then
+ log_must mounted ${mountable[i]}
+ mtpt=$(get_prop mountpoint ${mountable[i]})
+ else
+ mtpt=$(snapshot_mountpoint ${mountable[i]})
+ fi
+
+ if ! cmp_data $DATA $mtpt/$TESTFILE0 ; then
+ log_fail "$mtpt/$TESTFILE0 gets corrupted after rename operation."
+ fi
+
+ ((i = i + 1))
+done
+
+#verify the data integrity in zvol
+if is_global_zone; then
+ log_must eval "$DD if=${VOL_R_PATH}-new of=$VOLDATA bs=$BS count=$CNT >/dev/null 2>&1"
+ if ! cmp_data $VOLDATA $DATA ; then
+ log_fail "$VOLDATA gets corrupted after rename operation."
+ fi
+fi
+
+# rename back fs
+typeset -i i=0
+while ((i < ${#dataset[*]} )); do
+ if datasetexists ${dataset[i]}-new ; then
+ log_must $ZFS rename ${dataset[i]}-new ${dataset[i]}
+ fi
+ ((i = i + 1))
+done
+
+log_pass "'zfs rename' successfully renamed each dataset type."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_002_pos.ksh
new file mode 100644
index 000000000000..7273656f1092
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_002_pos.ksh
@@ -0,0 +1,99 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_rename/zfs_rename.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_rename_002_pos
+#
+# DESCRIPTION:
+# 'zfs rename' should successfully be capable of renaming
+# valid datasets back and forth multiple times.
+#
+# STRATEGY:
+# 1. Given a file system, snapshot and volume.
+# 2. Rename each dataset object to a new name.
+# 3. Rename each dataset back to its original name.
+# 4. Repeat steps 2 and 3 multiple times.
+# 5. Verify that the correct name is displayed by zfs list.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-06-29)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "both"
+
+set -A dataset "$TESTPOOL/$TESTFS@snapshot" "$TESTPOOL/$TESTFS1" \
+ "$TESTPOOL/$TESTCTR/$TESTFS1" "$TESTPOOL/$TESTCTR1" \
+ "$TESTPOOL/$TESTVOL" "$TESTPOOL/$TESTFS-clone"
+
+#
+# cleanup defined in zfs_rename.kshlib
+#
+log_onexit cleanup
+
+log_assert "'zfs rename' should successfully rename valid datasets"
+
+additional_setup
+
+typeset -i i=0
+typeset -i iters=10
+
+while ((i < ${#dataset[*]} )); do
+ j=0
+ while ((j < iters )); do
+ rename_dataset ${dataset[i]} ${dataset[i]}-new
+ rename_dataset ${dataset[i]}-new ${dataset[i]}
+
+ ((j = j + 1))
+ done
+
+ if [[ ${dataset[i]} == *@* ]]; then
+ data=$(snapshot_mountpoint ${dataset[i]})/$TESTFILE0
+ elif [[ ${dataset[i]} == "$TESTPOOL/$TESTVOL" ]] && is_global_zone; then
+ log_must eval "$DD if=$VOL_R_PATH of=$VOLDATA bs=$BS count=$CNT >/dev/null 2>&1"
+ data=$VOLDATA
+ else
+ data=$(get_prop mountpoint ${dataset[i]})/$TESTFILE0
+ fi
+
+ if ! cmp_data $DATA $data; then
+ log_fail "$data gets corrupted after $iters times rename operations."
+ fi
+
+ ((i = i + 1))
+done
+
+log_pass "'zfs rename' renamed each dataset type multiple times as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_003_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_003_pos.ksh
new file mode 100644
index 000000000000..564b583bede9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_003_pos.ksh
@@ -0,0 +1,77 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_rename_003_pos
+#
+# DESCRIPTION:
+# 'zfs rename' can address the abbreviated snapshot name.
+#
+# STRATEGY:
+# 1. Create pool, fs and snap.
+# 2. Verify 'zfs rename' support the abbreviated snapshot name.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-07-18)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ datasetexists $snap && log_must $ZFS destroy $snap
+}
+
+log_assert "'zfs rename' can address the abbreviated snapshot name."
+log_onexit cleanup
+
+fs=$TESTPOOL/$TESTFS; snap=$fs@snap
+set -A newname "$fs@new-snap" "@new-snap" "new-snap"
+
+log_must $ZFS snapshot $snap
+log_must datasetexists $snap
+
+typeset -i i=0
+while ((i < ${#newname[*]} )); do
+ log_must $ZFS rename $snap ${newname[$i]}
+ log_must datasetexists ${snap%%@*}@${newname[$i]##*@}
+ log_must $ZFS rename ${snap%%@*}@${newname[$i]##*@} $snap
+
+ ((i += 1))
+done
+
+log_pass "'zfs rename' address the abbreviated snapshot name passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_004_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_004_neg.ksh
new file mode 100644
index 000000000000..ee7b0a47f71f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_004_neg.ksh
@@ -0,0 +1,120 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_rename/zfs_rename.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_rename_004_neg
+#
+# DESCRIPTION:
+# 'zfs rename' should fail when this dataset was changed to an existed
+# dataset name or datasets are of different types.
+# For example, a filesystem cannot be renamed as a volume.
+#
+# STRATEGY:
+# 1. Given a file system, snapshot and volume.
+# 2. Rename each dataset object to a different type.
+# 3. Verify that only the original name is displayed by zfs list.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-06-29)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "both"
+
+#
+# This array is a list of pairs:
+# item i: original type
+# item i + 1: new type
+#
+set -A bad_dataset $TESTPOOL/$TESTFS1 $TESTPOOL/$TESTCTR1 \
+ $TESTPOOL/$TESTFS1 $TESTPOOL/$TESTCTR/$TESTFS1 \
+ $TESTPOOL/$TESTFS1 $TESTPOOL/$TESTVOL \
+ $TESTPOOL/$TESTFS1 $TESTPOOL/$TESTFS1/$TESTFS1 \
+ $TESTPOOL/$TESTFS1 $TESTPOOL/$TESTFS@snapshot \
+ $TESTPOOL/$TESTCTR1 $TESTPOOL/$TESTVOL \
+ $TESTPOOL/$TESTCTR1 $TESTPOOL/$TESTFS@snapshot \
+ $TESTPOOL/$TESTCTR1 $TESTPOOL/$TESTFS1 \
+ $TESTPOOL/$TESTCTR1 $TESTPOOL/$TESTCTR/$TESTFS1 \
+ $TESTPOOL/$TESTCTR/$TESTFS1 $TESTPOOL/$TESTVOL \
+ $TESTPOOL/$TESTCTR/$TESTFS1 $TESTPOOL/$TESTFS@snapshot \
+ $TESTPOOL/$TESTCTR/$TESTFS1 $TESTPOOL/$TESTFS1 \
+ $TESTPOOL/$TESTCTR/$TESTFS1 $TESTPOOL/$TESTCTR1 \
+ $TESTPOOL/$TESTVOL $TESTPOOL/$TESTCTR1 \
+ $TESTPOOL/$TESTVOL $TESTPOOL/$TESTFS@snapshot \
+ $TESTPOOL/$TESTVOL $TESTPOOL/$TESTFS1 \
+ $TESTPOOL/$TESTVOL $TESTPOOL/$TESTCTR/$TESTFS1 \
+ $TESTPOOL/$TESTFS@snapshot $TESTPOOL/$TESTCTR1 \
+ $TESTPOOL/$TESTFS@snapshot $TESTPOOL/$TESTVOL \
+ $TESTPOOL/$TESTFS@snapshot $TESTPOOL/$TESTFS1 \
+ $TESTPOOL/$TESTFS@snapshot $TESTPOOL/$TESTCTR/$TESTFS1 \
+ $TESTPOOL/$TESTFS1 $TESTPOOL/${TESTFS1}%c \
+ $TESTPOOL/$TESTFS1 $TESTPOOL/${TESTFS1}%d \
+ $TESTPOOL/$TESTFS1 $TESTPOOL/${TESTFS1}%x \
+ $TESTPOOL/$TESTFS1 $TESTPOOL/${TESTFS1}%p \
+ $TESTPOOL/$TESTFS1 $TESTPOOL/${TESTFS1}%s \
+ $TESTPOOL/$TESTFS@snapshot \
+ $TESTPOOL/$TESTFS@snapshot/fs
+
+#
+# cleanup defined in zfs_rename.kshlib
+#
+log_onexit cleanup
+
+log_assert "'zfs rename' should fail when datasets are of a different type."
+
+additional_setup
+
+typeset -i i=0
+while ((i < ${#bad_dataset[*]} )); do
+ log_mustnot $ZFS rename ${bad_dataset[i]} ${bad_dataset[((i + 1))]}
+ log_must datasetexists ${bad_dataset[i]}
+
+ log_mustnot $ZFS rename -p ${bad_dataset[i]} ${bad_dataset[((i + 1))]}
+ log_must datasetexists ${bad_dataset[i]}
+
+ ((i = i + 2))
+done
+
+#verify 'rename -p' can not work with snapshots
+
+log_mustnot $ZFS rename -p $TESTPOOL/$TESTFS@snapshot \
+ $TESTPOOL/$TESTFS@snapshot2
+log_must datasetexists $TESTPOOL/$TESTFS@snapshot
+log_mustnot $ZFS rename -p $TESTPOOL/$TESTFS@snapshot \
+ $TESTPOOL/$TESTFS/$TESTFS@snapshot2
+log_must datasetexists $TESTPOOL/$TESTFS@snapshot
+
+log_pass "'zfs rename' fails as expected when given different dataset types."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_005_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_005_neg.ksh
new file mode 100644
index 000000000000..11951f49e5d0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_005_neg.ksh
@@ -0,0 +1,100 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_rename/zfs_rename.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_rename_005_neg
+#
+# DESCRIPTION:
+# 'zfs rename' should fail when the dataset are not within the same pool
+#
+# STRATEGY:
+# 1. Given a file system, snapshot and volume.
+# 2. Rename each dataset object to a different pool.
+# 3. Verify the operation fails, and only the original name
+# is displayed by zfs list.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-09-13)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function my_cleanup
+{
+ poolexists $TESTPOOL1 && \
+ destroy_pool $TESTPOOL1
+ [[ -e $TESTDIR/$TESTFILE1 ]] && \
+ log_must $RM -f $TESTDIR/$TESTFILE1
+ cleanup
+}
+
+set -A src_dataset \
+ "$TESTPOOL/$TESTFS1" "$TESTPOOL/$TESTCTR1" \
+ "$TESTPOOL/$TESTCTR/$TESTFS1" "$TESTPOOL/$TESTVOL" \
+ "$TESTPOOL/$TESTFS@snapshot" "$TESTPOOL/$TESTFS-clone"
+
+#
+# cleanup defined in zfs_rename.kshlib
+#
+log_onexit my_cleanup
+
+log_assert "'zfs rename' should fail while datasets are within different pool."
+
+additional_setup
+
+typeset FILESIZE=64m
+log_must $TRUNCATE -s $FILESIZE $TESTDIR/$TESTFILE1
+create_pool $TESTPOOL1 $TESTDIR/$TESTFILE1
+
+for src in ${src_dataset[@]} ; do
+ dest=${src#$TESTPOOL/}
+ if [[ $dest == *"@"* ]]; then
+ dest=${dest#*@}
+ dest=${TESTPOOL1}@$dest
+ else
+ dest=${TESTPOOL1}/$dest
+ fi
+ log_mustnot $ZFS rename $src $dest
+ log_mustnot $ZFS rename -p $src $dest
+
+ #
+ # Verify original dataset name still in use
+ #
+ log_must datasetexists $src
+done
+
+log_pass "'zfs rename' fail while datasets are within different pool."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_006_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_006_pos.ksh
new file mode 100644
index 000000000000..3d8181114297
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_006_pos.ksh
@@ -0,0 +1,87 @@
+#!/usr/local/bin/ksh93
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_rename/zfs_rename.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_rename_006_pos
+#
+# DESCRIPTION:
+# 'zfs rename' can successfully rename a volume snapshot.
+#
+# STRATEGY:
+# 1. Create a snapshot of volume.
+# 2. Rename volume snapshot to a new one.
+# 3. Rename volume to a new one.
+# 5. Verify that the rename operations are successful and zfs list can
+# list them.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-02-05)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+#
+# cleanup defined in zfs_rename.kshlib
+#
+log_onexit cleanup
+
+log_assert "'zfs rename' can successfully rename a volume snapshot."
+
+vol=$TESTPOOL/$TESTVOL
+snap=$TESTSNAP
+
+log_must eval "$DD if=$DATA of=$VOL_R_PATH bs=$BS count=$CNT >/dev/null 2>&1"
+if ! snapexists $vol@$snap; then
+ log_must $ZFS snapshot $vol@$snap
+fi
+
+rename_dataset $vol@$snap $vol@${snap}-new
+rename_dataset $vol ${vol}-new
+rename_dataset ${vol}-new@${snap}-new ${vol}-new@$snap
+rename_dataset ${vol}-new $vol
+
+#verify data integrity
+for input in $VOL_R_PATH ${VOL_R_PATH}@$snap; do
+ log_must eval "$DD if=$input of=$VOLDATA bs=$BS count=$CNT >/dev/null 2>&1"
+ if ! cmp_data $VOLDATA $DATA ; then
+ log_fail "$input gets corrupted after rename operation."
+ fi
+done
+
+log_must $ZFS destroy $vol@$snap
+
+log_pass "'zfs rename' can rename volume snapshot as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_007_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_007_pos.ksh
new file mode 100644
index 000000000000..55c7bee5398c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_007_pos.ksh
@@ -0,0 +1,165 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_rename_007_pos
+#
+# DESCRIPTION:
+# Rename dataset, verify that the data haven't changed.
+#
+# STRATEGY:
+# 1. Create random data and copy to dataset.
+# 2. Perform renaming commands.
+# 3. Verify that the data haven't changed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-03-15)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+# Check if current system support recursive rename
+$ZFS rename 2>&1 | grep "rename -r" >/dev/null 2>&1
+if (($? != 0)); then
+ log_unsupported
+fi
+
+function cleanup
+{
+ if datasetexists $TESTPOOL/$TESTFS ; then
+ log_must $ZFS destroy -Rf $TESTPOOL/$TESTFS
+ fi
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+
+ $RM -f $SRC_FILE $DST_FILE
+}
+
+function target_obj
+{
+ typeset dtst=$1
+
+ typeset obj
+ typeset type=$(get_prop type $dtst)
+ if [[ $type == "filesystem" ]]; then
+ obj=$(get_prop mountpoint $dtst)/${SRC_FILE##*/}
+ elif [[ $type == "volume" ]]; then
+ obj=/dev/zvol/$dtst
+ fi
+
+ print $obj
+}
+
+log_assert "Rename dataset, verify that the data haven't changed."
+log_onexit cleanup
+
+# Generate random data
+#
+BS=512 ; CNT=2048
+SRC_FILE=$TMPDIR/srcfile.${TESTCASE_ID}
+DST_FILE=$TMPDIR/dstfile.${TESTCASE_ID}
+log_must $DD if=/dev/random of=$SRC_FILE bs=$BS count=$CNT
+
+fs=$TESTPOOL/$TESTFS/fs.${TESTCASE_ID}
+fsclone=$TESTPOOL/$TESTFS/fsclone.${TESTCASE_ID}
+log_must $ZFS create $fs
+
+obj=$(target_obj $fs)
+log_must $CP $SRC_FILE $obj
+
+snap=${fs}@snap.${TESTCASE_ID}
+log_must $ZFS snapshot $snap
+log_must $ZFS clone $snap $fsclone
+
+# Rename dataset & clone
+#
+log_must $ZFS rename $fs ${fs}-new
+log_must $ZFS rename $fsclone ${fsclone}-new
+
+# Compare source file and target file
+#
+obj=$(target_obj ${fs}-new)
+log_must $DIFF $SRC_FILE $obj
+obj=$(target_obj ${fsclone}-new)
+log_must $DIFF $SRC_FILE $obj
+
+# Rename snapshot and re-clone dataset
+#
+log_must $ZFS rename ${fs}-new $fs
+log_must $ZFS rename $snap ${snap}-new
+log_must $ZFS clone ${snap}-new $fsclone
+
+# Compare source file and target file
+#
+obj=$(target_obj $fsclone)
+log_must $DIFF $SRC_FILE $obj
+
+if is_global_zone; then
+ vol=$TESTPOOL/$TESTFS/vol.${TESTCASE_ID} ; volclone=$TESTPOOL/$TESTFS/volclone.${TESTCASE_ID}
+ log_must $ZFS create -V 100M $vol
+
+ obj=$(target_obj $vol)
+ log_must $DD if=$SRC_FILE of=$obj bs=$BS count=$CNT
+
+ snap=${vol}@snap.${TESTCASE_ID}
+ log_must $ZFS snapshot $snap
+ log_must $ZFS clone $snap $volclone
+
+ # Rename dataset & clone
+ log_must $ZFS rename $vol ${vol}-new
+ log_must $ZFS rename $volclone ${volclone}-new
+
+ # Compare source file and target file
+ obj=$(target_obj ${vol}-new)
+ log_must $DD if=$obj of=$DST_FILE bs=$BS count=$CNT
+ log_must $DIFF $SRC_FILE $DST_FILE
+ obj=$(target_obj ${volclone}-new)
+ log_must $DD if=$obj of=$DST_FILE bs=$BS count=$CNT
+ log_must $DIFF $SRC_FILE $DST_FILE
+
+ # Rename snapshot and re-clone dataset
+ log_must $ZFS rename ${vol}-new $vol
+ log_must $ZFS rename $snap ${snap}-new
+ log_must $ZFS clone ${snap}-new $volclone
+
+ # Compare source file and target file
+ obj=$(target_obj $volclone)
+ log_must $DD if=$obj of=$DST_FILE bs=$BS count=$CNT
+ log_must $DIFF $SRC_FILE $DST_FILE
+fi
+
+log_pass "Rename dataset, the data haven't changed passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_008_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_008_pos.ksh
new file mode 100644
index 000000000000..f964abe0a072
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_008_pos.ksh
@@ -0,0 +1,103 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_rename_008_pos
+#
+# DESCRIPTION:
+# zfs rename -r can rename snapshot recursively.
+#
+# STRATEGY:
+# 1. Create snapshot recursively.
+# 2. Rename snapshot recursively.
+# 3. Verify rename -r snapshot correctly.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-03-15)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+# Check if current system support recursive rename
+$ZFS rename 2>&1 | grep "rename -r" > /dev/null 2>&1
+if (($? != 0)); then
+ log_unsupported
+fi
+
+function cleanup
+{
+ typeset -i i=0
+ while ((i < ${#datasets[@]})); do
+ if datasetexists ${datasets[$i]}@snap ; then
+ log_must $ZFS destroy ${datasets[$i]}@snap
+ fi
+ if datasetexists ${datasets[$i]}@snap-new ; then
+ log_must $ZFS destroy ${datasets[$i]}@snap-new
+ fi
+
+ ((i += 1))
+ done
+}
+
+log_assert "zfs rename -r can rename snapshot recursively."
+log_onexit cleanup
+
+set -A datasets $TESTPOOL $TESTPOOL/$TESTCTR \
+ $TESTPOOL/$TESTCTR/$TESTFS1 $TESTPOOL/$TESTFS
+if is_global_zone; then
+ datasets[${#datasets[@]}]=$TESTPOOL/$TESTVOL
+fi
+
+log_must $ZFS snapshot -r ${TESTPOOL}@snap
+typeset -i i=0
+while ((i < ${#datasets[@]})); do
+ log_must datasetexists ${datasets[$i]}@snap
+
+ ((i += 1))
+done
+
+log_must $ZFS rename -r ${TESTPOOL}@snap ${TESTPOOL}@snap-new
+i=0
+while ((i < ${#datasets[@]})); do
+ log_must datasetexists ${datasets[$i]}@snap-new
+
+ ((i += 1))
+done
+
+log_must $ZFS destroy -rf ${TESTPOOL}@snap-new
+
+log_pass "Verify zfs rename -r passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_009_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_009_neg.ksh
new file mode 100644
index 000000000000..a08087c7d4f2
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_009_neg.ksh
@@ -0,0 +1,91 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_rename_009_neg
+#
+# DESCRIPTION:
+# A snapshot already exists with the new name, then none of the
+# snapshots is renamed.
+#
+# STRATEGY:
+# 1. Create snapshot for a set of datasets.
+# 2. Create a new snapshot for one of datasets.
+# 3. Using rename -r command with exists snapshot name.
+# 4. Verify none of the snapshots is renamed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-03-15)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+# Check if current system support recursive rename
+$ZFS rename 2>&1 | grep "rename -r" >/dev/null 2>&1
+if (($? != 0)); then
+ log_unsupported
+fi
+
+log_assert "zfs rename -r failed, when snapshot name is already existing."
+
+set -A datasets $TESTPOOL $TESTPOOL/$TESTCTR \
+ $TESTPOOL/$TESTCTR/$TESTFS1 $TESTPOOL/$TESTFS
+if is_global_zone; then
+ datasets[${#datasets[@]}]=$TESTPOOL/$TESTVOL
+fi
+
+log_must $ZFS snapshot -r ${TESTPOOL}@snap
+typeset -i i=0
+while ((i < ${#datasets[@]})); do
+ # Create one more snapshot
+ log_must $ZFS snapshot ${datasets[$i]}@snap2
+ log_mustnot $ZFS rename -r ${TESTPOOL}@snap ${TESTPOOL}@snap2
+ log_must $ZFS destroy ${datasets[$i]}@snap2
+
+ # Check datasets, make sure none of them was renamed.
+ typeset -i j=0
+ while ((j < ${#datasets[@]})); do
+ if datasetexists ${datasets[$j]}@snap2 ; then
+ log_fail "${datasets[$j]}@snap2 should not exist."
+ fi
+ ((j += 1))
+ done
+
+ ((i += 1))
+done
+
+log_pass "zfs rename -r failed, when snapshot name is already existing passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_010_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_010_neg.ksh
new file mode 100644
index 000000000000..347f4e89b5a6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_010_neg.ksh
@@ -0,0 +1,88 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_rename_010_neg
+#
+# DESCRIPTION:
+# The recursive flag -r can only be used for snapshots and not for
+# volumes/filesystems.
+#
+# STRATEGY:
+# 1. Loop pool, fs, container and volume.
+# 2. Verify none of them can be rename by rename -r.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-03-15)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+# Check if current system support recursive rename
+$ZFS rename 2>&1 | grep "rename -r" >/dev/null 2>&1
+if (($? != 0)); then
+ log_unsupported
+fi
+
+log_assert "The recursive flag -r can only be used for snapshots."
+
+set -A datasets $TESTPOOL $TESTPOOL/$TESTCTR \
+ $TESTPOOL/$TESTCTR/$TESTFS1 $TESTPOOL/$TESTFS
+if is_global_zone; then
+ datasets[${#datasets[@]}]=$TESTPOOL/$TESTVOL
+fi
+
+for opts in "-r" "-r -p"; do
+ typeset -i i=0
+ while ((i < ${#datasets[@]})); do
+ log_mustnot $ZFS rename $opts ${datasets[$i]} \
+ ${datasets[$i]}-new
+
+ # Check datasets, make sure none of them was renamed.
+ typeset -i j=0
+ while ((j < ${#datasets[@]})); do
+ if datasetexists ${datasets[$j]}-new ; then
+ log_fail "${datasets[$j]}-new should not exists."
+ fi
+ ((j += 1))
+ done
+
+ ((i += 1))
+ done
+done
+
+log_pass "The recursive flag -r can only be used for snapshots passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_011_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_011_pos.ksh
new file mode 100644
index 000000000000..00a2ab309196
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_011_pos.ksh
@@ -0,0 +1,90 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_rename/zfs_rename.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_rename_011_pos
+#
+# DESCRIPTION
+# 'zfs rename -p' should work as expected
+#
+# STRATEGY:
+# 1. Make sure the upper level of $newdataset does not exist
+# 2. Make sure without -p option, 'zfs rename' will fail
+# 3. With -p option, rename works
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-05)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+if ! $(check_opt_support "create" "-p") ; then
+ log_unsupported "-p option is not supported yet."
+fi
+
+verify_runnable "both"
+
+function additional_cleanup
+{
+ if datasetexists $TESTPOOL/notexist ; then
+ log_must $ZFS destroy -Rf $TESTPOOL/notexist
+ fi
+
+ if datasetexists $TESTPOOL/$TESTFS ; then
+ log_must $ZFS destroy -Rf $TESTPOOL/$TESTFS
+ fi
+ log_must $ZFS create $TESTPOOL/$TESTFS
+
+ if is_global_zone ; then
+ if datasetexists $TESTPOOL/$TESTVOL ; then
+ log_must $ZFS destroy -Rf $TESTPOOL/$TESTVOL
+ fi
+ log_must $ZFS create -V $VOLSIZE $TESTPOOL/$TESTVOL
+ fi
+}
+
+log_onexit additional_cleanup
+
+log_assert "'zfs rename -p' should work as expected"
+
+log_must verify_opt_p_ops "rename" "fs" "$TESTPOOL/$TESTFS" \
+ "$TESTPOOL/notexist/new/$TESTFS1"
+
+if is_global_zone; then
+ log_must verify_opt_p_ops "rename" "vol" "$TESTPOOL/$TESTVOL" \
+ "$TESTPOOL/notexist/new/$TESTVOL1"
+fi
+
+log_pass "'zfs rename -p' should work as expected"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_012_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_012_neg.ksh
new file mode 100644
index 000000000000..9a7faf5db9d1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_012_neg.ksh
@@ -0,0 +1,76 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_rename_012_neg
+#
+# DESCRIPTION:
+# 'zfs rename' should be failed with bad option, null target dataset,
+# too many datasets and long target dataset name.
+#
+# STRATEGY:
+# 1. Create a set of ZFS datasets;
+# 2. Try 'zfs rename' with various illegal scenarios;
+# 3. Verify 'zfs rename' command should be failed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-15)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "'zfs rename' should fail with bad option, null target dataset and" \
+ "too long target dataset name."
+
+set -A badopts "r" "R" "-R" "-rR" "-Rr" "-P" "-pP" "-Pp" "-r*" "-p*" "-?" "-*" \
+ "-" "-o"
+set -A datasets "$TESTPOOL" "$TESTPOOL/$TESTFS" "$TESTPOOL/$TESTFS@$TESTSNAP" \
+ "$TESTPOOL/$TESTCTR" "$TESTPOOL/$TESTCTR/$TESTFS1" "$TESTPOOL/$TESTVOL"
+
+longname="$(gen_dataset_name 260 abcdefg)"
+
+log_must $ZFS snapshot $TESTPOOL/$TESTFS@$TESTSNAP
+for ds in ${datasets[@]}; do
+ for opt in ${badopts[@]}; do
+ log_mustnot $ZFS rename $opt $ds ${ds}-new
+ done
+ log_mustnot $ZFS rename $ds
+ log_mustnot $ZFS rename $ds ${ds}-new ${ds}-new1
+ log_mustnot $ZFS rename $ds ${ds}.$longname
+done
+
+log_pass "'zfs rename' fails with illegal scenarios as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_013_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_013_pos.ksh
new file mode 100644
index 000000000000..6f678b734ba3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_013_pos.ksh
@@ -0,0 +1,100 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_rename_013_pos
+#
+# DESCRIPTION:
+# zfs rename -r can rename snapshot when child datasets
+# don't have a snapshot of the given name.
+#
+# STRATEGY:
+# 1. Create snapshot.
+# 2. Rename snapshot recursively.
+# 3. Verify rename -r snapshot correctly.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-04-24)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+# Check if current system support recursive rename
+$ZFS rename 2>&1 | grep "rename -r" > /dev/null 2>&1
+if (($? != 0)); then
+ log_unsupported
+fi
+
+function cleanup
+{
+ if datasetexists $TESTPOOL/$TESTCTR@snap-new ; then
+ log_must $ZFS destroy -f $TESTPOOL/$TESTCTR@snap-new
+ fi
+
+ if datasetexists $TESTPOOL/$TESTCTR@snap ; then
+ log_must $ZFS destroy -f $TESTPOOL/$TESTCTR@snap
+ fi
+
+ if datasetexists $TESTPOOL@snap-new ; then
+ log_must $ZFS destroy -f $TESTPOOL@snap-new
+ fi
+
+ if datasetexists $TESTPOOL@snap ; then
+ log_must $ZFS destroy -f $TESTPOOL@snap
+ fi
+}
+
+log_assert "zfs rename -r can rename snapshot when child datasets" \
+ "don't have a snapshot of the given name."
+
+log_onexit cleanup
+
+log_must $ZFS snapshot $TESTPOOL/$TESTCTR@snap
+log_must $ZFS rename -r $TESTPOOL/$TESTCTR@snap $TESTPOOL/$TESTCTR@snap-new
+log_must datasetexists $TESTPOOL/$TESTCTR@snap-new
+
+log_must $ZFS snapshot $TESTPOOL@snap
+log_must $ZFS rename -r $TESTPOOL@snap $TESTPOOL@snap-new
+log_must datasetexists $TESTPOOL/$TESTCTR@snap-new
+log_must datasetexists $TESTPOOL@snap-new
+
+log_must $ZFS destroy -f $TESTPOOL/$TESTCTR@snap-new
+log_must $ZFS destroy -f $TESTPOOL@snap-new
+
+log_pass "Verify zfs rename -r passed when child datasets" \
+ "don't have a snapshot of the given name."
+
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_test.sh
new file mode 100755
index 000000000000..85c924182970
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rename/zfs_rename_test.sh
@@ -0,0 +1,381 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zfs_rename_001_pos cleanup
+zfs_rename_001_pos_head()
+{
+ atf_set "descr" "'zfs rename' should successfully rename valid datasets"
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_rename_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rename.kshlib
+ . $(atf_get_srcdir)/zfs_rename.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_rename_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_rename_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rename.kshlib
+ . $(atf_get_srcdir)/zfs_rename.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_rename_002_pos cleanup
+zfs_rename_002_pos_head()
+{
+ atf_set "descr" "'zfs rename' should successfully rename valid datasets"
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_rename_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rename.kshlib
+ . $(atf_get_srcdir)/zfs_rename.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_rename_002_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_rename_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rename.kshlib
+ . $(atf_get_srcdir)/zfs_rename.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_rename_003_pos cleanup
+zfs_rename_003_pos_head()
+{
+ atf_set "descr" "'zfs rename' can address the abbreviated snapshot name."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_rename_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rename.kshlib
+ . $(atf_get_srcdir)/zfs_rename.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_rename_003_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_rename_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rename.kshlib
+ . $(atf_get_srcdir)/zfs_rename.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_rename_004_neg cleanup
+zfs_rename_004_neg_head()
+{
+ atf_set "descr" "'zfs rename' should fail when datasets are of a different type."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_rename_004_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rename.kshlib
+ . $(atf_get_srcdir)/zfs_rename.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_rename_004_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_rename_004_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rename.kshlib
+ . $(atf_get_srcdir)/zfs_rename.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_rename_005_neg cleanup
+zfs_rename_005_neg_head()
+{
+ atf_set "descr" "'zfs rename' should fail while datasets are within different pool."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_rename_005_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rename.kshlib
+ . $(atf_get_srcdir)/zfs_rename.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_rename_005_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_rename_005_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rename.kshlib
+ . $(atf_get_srcdir)/zfs_rename.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_rename_006_pos cleanup
+zfs_rename_006_pos_head()
+{
+ atf_set "descr" "'zfs rename' can successfully rename a volume snapshot."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_rename_006_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rename.kshlib
+ . $(atf_get_srcdir)/zfs_rename.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_rename_006_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_rename_006_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rename.kshlib
+ . $(atf_get_srcdir)/zfs_rename.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_rename_007_pos cleanup
+zfs_rename_007_pos_head()
+{
+ atf_set "descr" "Rename dataset, verify that the data haven't changed."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_rename_007_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rename.kshlib
+ . $(atf_get_srcdir)/zfs_rename.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_rename_007_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_rename_007_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rename.kshlib
+ . $(atf_get_srcdir)/zfs_rename.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_rename_008_pos cleanup
+zfs_rename_008_pos_head()
+{
+ atf_set "descr" "zfs rename -r can rename snapshot recursively."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_rename_008_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rename.kshlib
+ . $(atf_get_srcdir)/zfs_rename.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_rename_008_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_rename_008_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rename.kshlib
+ . $(atf_get_srcdir)/zfs_rename.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_rename_009_neg cleanup
+zfs_rename_009_neg_head()
+{
+ atf_set "descr" "zfs rename -r failed, when snapshot name is already existing."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_rename_009_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rename.kshlib
+ . $(atf_get_srcdir)/zfs_rename.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_rename_009_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_rename_009_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rename.kshlib
+ . $(atf_get_srcdir)/zfs_rename.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_rename_010_neg cleanup
+zfs_rename_010_neg_head()
+{
+ atf_set "descr" "The recursive flag -r can only be used for snapshots."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_rename_010_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rename.kshlib
+ . $(atf_get_srcdir)/zfs_rename.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_rename_010_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_rename_010_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rename.kshlib
+ . $(atf_get_srcdir)/zfs_rename.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_rename_011_pos cleanup
+zfs_rename_011_pos_head()
+{
+ atf_set "descr" "'zfs rename -p' should work as expected"
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_rename_011_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rename.kshlib
+ . $(atf_get_srcdir)/zfs_rename.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_rename_011_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_rename_011_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rename.kshlib
+ . $(atf_get_srcdir)/zfs_rename.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_rename_012_neg cleanup
+zfs_rename_012_neg_head()
+{
+ atf_set "descr" "'zfs rename' should fail with bad option, null target dataset andtoo long target dataset name."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_rename_012_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rename.kshlib
+ . $(atf_get_srcdir)/zfs_rename.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_rename_012_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_rename_012_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rename.kshlib
+ . $(atf_get_srcdir)/zfs_rename.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_rename_013_pos cleanup
+zfs_rename_013_pos_head()
+{
+ atf_set "descr" "zfs rename -r can rename snapshot when child datasetsdon't have a snapshot of the given name."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_rename_013_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rename.kshlib
+ . $(atf_get_srcdir)/zfs_rename.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_rename_013_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_rename_013_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rename.kshlib
+ . $(atf_get_srcdir)/zfs_rename.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zfs_rename_001_pos
+ atf_add_test_case zfs_rename_002_pos
+ atf_add_test_case zfs_rename_003_pos
+ atf_add_test_case zfs_rename_004_neg
+ atf_add_test_case zfs_rename_005_neg
+ atf_add_test_case zfs_rename_006_pos
+ atf_add_test_case zfs_rename_007_pos
+ atf_add_test_case zfs_rename_008_pos
+ atf_add_test_case zfs_rename_009_neg
+ atf_add_test_case zfs_rename_010_neg
+ atf_add_test_case zfs_rename_011_pos
+ atf_add_test_case zfs_rename_012_neg
+ atf_add_test_case zfs_rename_013_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_reservation/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zfs_reservation/Makefile
new file mode 100644
index 000000000000..07f9e88fe963
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_reservation/Makefile
@@ -0,0 +1,17 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zfs_reservation
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zfs_reservation_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zfs_reservation.cfg
+${PACKAGE}FILES+= zfs_reservation_001_pos.ksh
+${PACKAGE}FILES+= zfs_reservation_002_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= cleanup.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_reservation/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_reservation/cleanup.ksh
new file mode 100644
index 000000000000..713cb16bb31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_reservation/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_reservation/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_reservation/setup.ksh
new file mode 100644
index 000000000000..326bc555f71b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_reservation/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_reservation/zfs_reservation.cfg b/tests/sys/cddl/zfs/tests/cli_root/zfs_reservation/zfs_reservation.cfg
new file mode 100644
index 000000000000..99d93c9d9c72
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_reservation/zfs_reservation.cfg
@@ -0,0 +1,27 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_reservation/zfs_reservation_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_reservation/zfs_reservation_001_pos.ksh
new file mode 100644
index 000000000000..324fbc30df2c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_reservation/zfs_reservation_001_pos.ksh
@@ -0,0 +1,72 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_reservation_001_pos
+#
+# DESCRIPTION:
+# Exceed the maximum limit for a reservation and ensure it fails.
+#
+# STRATEGY:
+# 1. Create a reservation file system.
+# 2. Set the reservation to an absurd value.
+# 3. Verify the return code is an error.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+RESERVATION="reserve"
+
+function cleanup
+{
+ if datasetexists $TESTPOOL/$RESERVATION ; then
+ log_must $ZFS unmount $TESTPOOL/$RESERVATION
+ log_must $ZFS destroy $TESTPOOL/$RESERVATION
+ fi
+}
+
+log_onexit cleanup
+
+log_assert "Verify that a reservation > 2^64 -1 fails."
+
+log_must $ZFS create $TESTPOOL/$RESERVATION
+
+log_mustnot $ZFS set reservation=18446744073709551615 $TESTPOOL/$RESERVATION
+
+log_pass "Unable to set a reservation > 2^64 - 1"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_reservation/zfs_reservation_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_reservation/zfs_reservation_002_pos.ksh
new file mode 100644
index 000000000000..65c6a70e8b4f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_reservation/zfs_reservation_002_pos.ksh
@@ -0,0 +1,96 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_reservation_002_pos
+#
+# DESCRIPTION:
+# A reservation of 'none' (which is an alias for 0) should be allowed. This
+# test verifies that is true.
+#
+# STRATEGY:
+# 1. Create a new file system in the test pool.
+# 2. Set the reservation to 'none'.
+# 3. Verify the associated reservation is indeed 0.
+# 4. Repeat with reservation set to 0.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+# Use a unique value so earlier test failures will not impact this test.
+RESERVATION="reserve"-${TESTCASE_ID}
+RESERVATION2="reserve2"-${TESTCASE_ID}
+
+function cleanup
+{
+ typeset FS
+ for FS in $TESTPOOL/$RESERVATION $TESTPOOL/$RESERVATION2
+ do
+ if datasetexists $FS ; then
+ log_must $ZFS unmount $FS
+ log_must $ZFS destroy $FS
+ fi
+ done
+}
+
+log_onexit cleanup
+
+log_assert "Ensure a reservation of 0 or 'none' is allowed."
+
+log_must $ZFS create $TESTPOOL/$RESERVATION
+log_must $ZFS create $TESTPOOL/$RESERVATION2
+
+log_must $ZFS set reservation=0 $TESTPOOL/$RESERVATION
+log_must $ZFS set reservation=none $TESTPOOL/$RESERVATION2
+
+for FS in $TESTPOOL/$RESERVATION $TESTPOOL/$RESERVATION2
+do
+
+ reserve=`$ZFS get -pH reservation $FS | $AWK '{print $3}'`
+ if [[ $reserve -ne 0 ]]; then
+ log_fail "ZFS get -p reservation did not return 0"
+ fi
+
+ reserve=`$ZFS get -H reservation $FS | $AWK '{print $3}'`
+ if [[ $reserve != "none" ]]; then
+ log_fail "ZFS get reservation did not return 'none'"
+ fi
+done
+
+log_pass "Successfully set reservation to 0 and 'none'"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_reservation/zfs_reservation_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zfs_reservation/zfs_reservation_test.sh
new file mode 100755
index 000000000000..1a538583e580
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_reservation/zfs_reservation_test.sh
@@ -0,0 +1,80 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zfs_reservation_001_pos cleanup
+zfs_reservation_001_pos_head()
+{
+ atf_set "descr" "Verify that a reservation > 2^64 -1 fails."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_reservation_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_reservation.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_reservation_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_reservation_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_reservation.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_reservation_002_pos cleanup
+zfs_reservation_002_pos_head()
+{
+ atf_set "descr" "Ensure a reservation of 0 or 'none' is allowed."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_reservation_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_reservation.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_reservation_002_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_reservation_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_reservation.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zfs_reservation_001_pos
+ atf_add_test_case zfs_reservation_002_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/Makefile
new file mode 100644
index 000000000000..d288ea35914b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/Makefile
@@ -0,0 +1,20 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zfs_rollback
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zfs_rollback_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zfs_rollback_003_neg.ksh
+${PACKAGE}FILES+= zfs_rollback_002_pos.ksh
+${PACKAGE}FILES+= zfs_rollback_common.kshlib
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zfs_rollback_004_neg.ksh
+${PACKAGE}FILES+= zfs_rollback_001_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zfs_rollback.cfg
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/cleanup.ksh
new file mode 100644
index 000000000000..606716be3b7a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/cleanup.ksh
@@ -0,0 +1,34 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_rollback/zfs_rollback_common.kshlib
+
+$MOUNT | grep -q "/dev/zvol/$VOL" > /dev/null 2>&1
+if (( $? == 0 )); then
+ log_must $UMOUNT -f $TESTDIR1
+fi
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/setup.ksh
new file mode 100644
index 000000000000..b397ddd3e5c0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/setup.ksh
@@ -0,0 +1,30 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+default_volume_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/zfs_rollback.cfg b/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/zfs_rollback.cfg
new file mode 100644
index 000000000000..dcfd7406d30a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/zfs_rollback.cfg
@@ -0,0 +1,48 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/cli.cfg
+
+export FS=$TESTPOOL/$TESTFS
+export VOL=$TESTPOOL/$TESTVOL
+
+export FSSNAP0=$FS@$TESTSNAP
+export FSSNAP1=$FS@$TESTSNAP1
+export FSSNAP2=$FS@$TESTSNAP2
+
+export VOLSNAP0=$VOL@$TESTSNAP
+export VOLSNAP1=$VOL@$TESTSNAP1
+export VOLSNAP2=$VOL@$TESTSNAP2
+
+export FSCLONE0=$FS$TESTCLONE
+export FSCLONE1=$FS$TESTCLONE1
+export FSCLONE2=$FS$TESTCLONE2
+
+export VOLCLONE0=$VOL$TESTCLONE
+export VOLCLONE1=$VOL$TESTCLONE1
+export VOLCLONE2=$VOL$TESTCLONE2
+
+export STF_TIMEOUT=1800
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/zfs_rollback_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/zfs_rollback_001_pos.ksh
new file mode 100644
index 000000000000..e72f186a66c3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/zfs_rollback_001_pos.ksh
@@ -0,0 +1,176 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_rollback/zfs_rollback_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_rollback_001_pos
+#
+# DESCRIPTION:
+# 'zfs rollback -r|-rf|-R|-Rf' will recursively destroy any snapshots
+# more recent than the one specified.
+#
+# STRATEGY:
+# 1. Create pool, fs & volume.
+# 2. Separately create three snapshots or clones for fs & volume
+# 3. Roll back to the second snapshot and check the results.
+# 4. Create the third snapshot or clones for fs & volume again.
+# 5. Roll back to the first snapshot and check the results.
+# 6. Separately create two snapshots for fs & volume.
+# 7. Roll back to the first snapshot and check the results.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-29)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "'zfs rollback -r|-rf|-R|-Rf' will recursively destroy any " \
+ "snapshots more recent than the one specified."
+log_onexit cleanup_env
+
+#
+# Create suitable test environment and run 'zfs rollback', then compare with
+# expected value to check the system status.
+#
+# $1 option.
+# $2 the number of snapshots or clones.
+# $3 the number of snapshot point which we want to rollback.
+#
+function test_n_check #opt num_snap_clone num_rollback
+{
+ typeset opt=$1
+ typeset -i cnt=$2
+ typeset -i pointcnt=$3
+ typeset dtst
+
+ (( cnt > 3 || pointcnt > cnt )) && \
+ log_fail "Unsupported testing condition."
+
+ # Clean up the test environment
+ datasetexists $FS && log_must $ZFS destroy -Rf $FS
+ if datasetexists $VOL; then
+ $MOUNT | grep -q "/dev/zvol/$VOL" > /dev/null 2>&1
+ (( $? == 0 )) && log_must $UMOUNT -f $TESTDIR1
+
+ log_must $ZFS destroy -Rf $VOL
+ fi
+
+ # Create specified test environment
+ case $opt in
+ *r*) setup_snap_env $cnt ;;
+ *R*) setup_clone_env $cnt ;;
+ esac
+
+ all_snap="$TESTSNAP $TESTSNAP1 $TESTSNAP2"
+ all_clone="$TESTCLONE $TESTCLONE1 $TESTCLONE2"
+ typeset snap_point
+ typeset exist_snap
+ typeset exist_clone
+ case $pointcnt in
+ 1) snap_point=$TESTSNAP
+ exist_snap=$TESTSNAP
+ [[ $opt == *R* ]] && exist_clone=$TESTCLONE
+ ;;
+ 2) snap_point=$TESTSNAP1
+ exist_snap="$TESTSNAP $TESTSNAP1"
+ [[ $opt == *R* ]] && exist_clone="$TESTCLONE $TESTCLONE1"
+ ;;
+ esac
+
+ typeset snap
+ for dtst in $FS $VOL; do
+ # Volume is not available in Local Zone.
+ if [[ $dtst == $VOL ]]; then
+ if ! is_global_zone; then
+ break
+ fi
+ fi
+ if [[ $opt == *f* ]]; then
+ # To write data to the mountpoint directory,
+ write_mountpoint_dir $dtst
+ opt=${opt%f}
+ fi
+
+ if [[ $dtst == $VOL ]]; then
+ log_must $UMOUNT -f $TESTDIR1
+ log_must $ZFS rollback $opt $dtst@$snap_point
+ log_must $MOUNT \
+ /dev/zvol/$TESTPOOL/$TESTVOL $TESTDIR1
+ else
+ log_must $ZFS rollback $opt $dtst@$snap_point
+ fi
+
+ for snap in $all_snap; do
+ if [[ " $exist_snap " == *" $snap "* ]]; then
+ log_must datasetexists $dtst@$snap
+ else
+ log_must datasetnonexists $dtst@$snap
+ fi
+ done
+ for clone in $all_clone; do
+ if [[ " $exist_clone " == *" $clone "* ]]; then
+ log_must datasetexists $dtst$clone
+ else
+ log_must datasetnonexists $dtst$clone
+ fi
+ done
+
+ check_files $dtst@$snap_point
+ done
+}
+
+typeset opt
+for opt in "-r" "-rf" "-R" "-Rf"; do
+ #
+ # Currently, the test case was limited to create and rollback
+ # in three snapshots
+ #
+ log_note "Create 3 snapshots, rollback to the 2nd snapshot " \
+ "using $opt."
+ test_n_check "$opt" 3 2
+
+ log_note "Create 3 snapshots and rollback to the 1st snapshot " \
+ "using $opt."
+ test_n_check "$opt" 3 1
+
+ log_note "Create 2 snapshots and rollback to the 1st snapshot " \
+ "using $opt."
+ test_n_check "$opt" 2 1
+done
+
+log_pass "'zfs rollback -r|-rf|-R|-Rf' recursively destroy any snapshots more "\
+ "recent than the one specified passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/zfs_rollback_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/zfs_rollback_002_pos.ksh
new file mode 100644
index 000000000000..1fd8cb44b253
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/zfs_rollback_002_pos.ksh
@@ -0,0 +1,77 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_rollback/zfs_rollback_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_rollback_002_pos
+#
+# DESCRIPTION:
+# 'zfs rollback -f' will force unmount any filesystems.
+#
+# STRATEGY:
+# 1. Create pool & fs.
+# 2. Create the snapshot of this file system.
+# 3. Write the mountpoint directory of this file system.
+# 4. Make sure 'zfs rollback -f' succeeds.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-29)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "'zfs rollback -f' will force unmount any filesystems."
+log_onexit cleanup_env
+
+# Create a snapshot of this file system: FSSNAP0
+setup_snap_env 1
+
+#
+# Write file and make the mountpoint directory busy when try to unmount
+# the file system that was mounted on it.
+#
+write_mountpoint_dir ${FSSNAP0%%@*}
+
+log_must $ZFS rollback $FSSNAP0
+log_must $ZFS rollback -f $FSSNAP0
+log_must datasetexists $FSSNAP0
+
+$PKILL ${DD##*/}
+
+check_files $FSSNAP0
+
+log_pass "'zfs rollback -f' force unmount any filesystem passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/zfs_rollback_003_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/zfs_rollback_003_neg.ksh
new file mode 100644
index 000000000000..5b09c5aa4861
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/zfs_rollback_003_neg.ksh
@@ -0,0 +1,93 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_rollback/zfs_rollback_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_rollback_003_neg
+#
+# DESCRIPTION:
+# Separately verify 'zfs rollback ''|-f|-r|-rf|-R|-rR will fail in
+# different conditions.
+#
+# STRATEGY:
+# 1. Create pool and file system
+# 2. Create 'snap' and 'snap1' of this file system.
+# 3. Run 'zfs rollback ""|-f <snap>' and it should fail.
+# 4. Create 'clone1' based on 'snap1'.
+# 5. Run 'zfs rollback -r|-rf <snap>' and it should fail.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-20)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ $PKILL ${DD##*/}
+ for snap in $FSSNAP0 $FSSNAP1 $FSSNAP2; do
+ if snapexists $snap; then
+ log_must $ZFS destroy -Rf $snap
+ fi
+ done
+}
+
+log_assert "Separately verify 'zfs rollback ''|-f|-r|-rf will fail in " \
+ "different conditions."
+log_onexit cleanup
+
+# Create snapshot1 and snapshot2 for this file system.
+#
+create_snapshot $TESTPOOL/$TESTFS $TESTSNAP
+create_snapshot $TESTPOOL/$TESTFS $TESTSNAP1
+
+# Run 'zfs rollback ""|-f <snap>' and it should fail.
+#
+log_mustnot $ZFS rollback $TESTPOOL/$TESTFS@$TESTSNAP
+log_mustnot $ZFS rollback -f $TESTPOOL/$TESTFS@$TESTSNAP
+
+# Create 'clone1' based on 'snap1'.
+#
+create_clone $TESTPOOL/$TESTFS@$TESTSNAP1 $TESTPOOL/$TESTCLONE1
+
+# Run 'zfs rollback -r|-rf <snap>' and it should fail.
+#
+log_mustnot $ZFS rollback -r $TESTPOOL/$TESTFS@$TESTSNAP
+log_mustnot $ZFS rollback -rf $TESTPOOL/$TESTFS@$TESTSNAP
+
+log_pass "zfs rollback ''|-f|-r|-rf will fail in different conditions " \
+ "passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/zfs_rollback_004_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/zfs_rollback_004_neg.ksh
new file mode 100644
index 000000000000..4790788f2c2a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/zfs_rollback_004_neg.ksh
@@ -0,0 +1,95 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_rollback/zfs_rollback_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_rollback_004_neg
+#
+# DESCRIPTION:
+# 'zfs rollback' should fail when passing invalid options, too many
+# arguments,non-snapshot datasets or missing datasets
+#
+# STRATEGY:
+# 1. Create an array of invalid options
+# 2. Execute 'zfs rollback' with invalid options, too many arguments
+# or missing datasets
+# 3. Verify 'zfs rollback' return with errors
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-20)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ typeset ds
+
+ for ds in $TESTPOOL $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL; do
+ if snapexists ${ds}@$TESTSNAP; then
+ log_must $ZFS destroy ${ds}@$TESTSNAP
+ fi
+ done
+}
+
+log_assert "'zfs rollback' should fail with bad options,too many arguments," \
+ "non-snapshot datasets or missing datasets."
+log_onexit cleanup
+
+set -A badopts "r" "R" "f" "-F" "-rF" "-RF" "-fF" "-?" "-*" "-blah" "-1" "-2"
+
+for ds in $TESTPOOL $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL; do
+ log_must $ZFS snapshot ${ds}@$TESTSNAP
+done
+
+for ds in $TESTPOOL $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL; do
+ for opt in "" "-r" "-R" "-f" "-rR" "-rf" "-rRf"; do
+ log_mustnot eval "$ZFS rollback $opt $ds >/dev/null 2>&1"
+ log_mustnot eval "$ZFS rollback $opt ${ds}@$TESTSNAP \
+ ${ds}@$TESTSNAP >/dev/null 2>&1"
+ log_mustnot eval "$ZFS rollback $opt >/dev/null 2>&1"
+ # zfs rollback should fail with non-existen snapshot
+ log_mustnot eval "$ZFS rollback $opt ${ds}@nosnap >/dev/null 2>&1"
+ done
+
+ for badopt in ${badopts[@]}; do
+ log_mustnot eval "$ZFS rollback $badopt ${ds}@$TESTSNAP \
+ >/dev/null 2>&1"
+ done
+done
+
+log_pass "'zfs rollback' fails as expected with illegal arguments."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/zfs_rollback_common.kshlib b/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/zfs_rollback_common.kshlib
new file mode 100644
index 000000000000..3487e0a114f8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/zfs_rollback_common.kshlib
@@ -0,0 +1,300 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+# Get file sum
+#
+# $1 full file name
+function getsum #fname
+{
+ (( ${#1} == 0 )) && \
+ log_fail "Need give file name."
+ return $($SUM $1 | $AWK '{print $1}')
+}
+
+# Define global variable checksum, get the original file sum.
+#
+origsum=$(getsum /etc/passwd)
+
+#
+# Setup or recover the test environment. Firstly, copy /etc/passwd to ZFS file
+# system or volume, then make a snapshot or clone. Repeat up to three times.
+#
+# $1 number of snapshot. Note: Currently only support three snapshots.
+# $2 indicate if it is necessary to create clone
+#
+function setup_snap_env
+{
+ typeset -i cnt=${1:-3}
+ typeset createclone=${2:-"false"}
+
+ if datasetnonexists $FS; then
+ log_must $ZFS create $FS
+ log_must $ZFS set mountpoint=$TESTDIR $FS
+ fi
+ # Volume can't be created in Local Zone.
+ if datasetnonexists $VOL && is_global_zone; then
+ log_must $ZFS create -V $VOLSIZE $VOL
+ fi
+
+ # Make sure $VOL is volume
+ typeset type=$(get_prop type $VOL)
+ if datasetexists $VOL && \
+ [[ $type == 'volume' ]]; then
+ #
+ # At the first time, Make a UFS file system in volume and
+ # mount it. Otherwise, only check if this ufs file system
+ # was mounted.
+ #
+ log_must eval "$ECHO "y" | \
+ $NEWFS /dev/zvol/$VOL > /dev/null 2>&1"
+
+ [[ ! -d $TESTDIR1 ]] && log_must $MKDIR $TESTDIR1
+
+ # Make sure the ufs filesystem hasn't been mounted,
+ # then mount the new ufs filesystem.
+ $MOUNT | grep -q "/dev/zvol/$VOL" > /dev/null 2>&1
+ if (( $? != 0 )); then
+ log_must $MOUNT \
+ /dev/zvol/$TESTPOOL/$TESTVOL $TESTDIR1
+ fi
+ fi
+
+ # Separately Create three snapshots for file system & volume
+ typeset -i ind=0
+ typeset dtst
+ for dtst in $FS $VOL; do
+ # Volume can be created in Local Zone.
+ if [[ $dtst == $VOL ]]; then
+ if ! is_global_zone; then
+ break
+ fi
+ fi
+
+ ind=0
+ while (( ind < cnt )); do
+ case $dtst in
+ $FS)
+ eval typeset snap=\$FSSNAP$ind
+ eval typeset clone=\$FSCLONE$ind
+ eval typeset fname=\$TESTDIR/\$TESTFILE$ind
+ ;;
+ $VOL)
+ eval typeset snap=\$VOLSNAP$ind
+ eval typeset clone=\$VOLCLONE$ind
+ eval typeset fname=\$TESTDIR1/\$TESTFILE$ind
+ ;;
+ esac
+
+ if datasetnonexists $snap; then
+ log_must $CP /etc/passwd $fname
+ #
+ # Take the snapshot with the zvol unmounted so
+ # that its filesystem's state will be
+ # consistent.
+ #
+ mount -u -o ro /dev/zvol/$TESTPOOL/$TESTVOL
+ log_must $ZFS snapshot $snap
+ mount -u -o rw /dev/zvol/$TESTPOOL/$TESTVOL
+ fi
+ if [[ $createclone == "true" ]]; then
+ if datasetnonexists $clone; then
+ log_must $ZFS clone $snap $clone
+ fi
+ fi
+ (( ind += 1 ))
+ done
+ done
+}
+
+function setup_clone_env
+{
+ setup_snap_env $1 "true"
+}
+
+#
+# Clean up the test environmnet
+#
+# $1 number of snapshot Note: Currently only support three snapshots.
+#
+function cleanup_env
+{
+ typeset -i cnt=${1:-3}
+ typeset -i ind=0
+ typeset dtst
+ typeset snap
+
+ $PKILL ${DD##*/}
+
+ $MOUNT | grep -q "/dev/zvol/$VOL" > /dev/null 2>&1
+ if (( $? == 0 )); then
+ log_must $UMOUNT -f $TESTDIR1
+ fi
+
+ [[ -d $TESTDIR ]] && log_must $RM -rf $TESTDIR/*
+ [[ -d $TESTDIR1 ]] && log_must $RM -rf $TESTDIR1/*
+
+ for dtst in $FS $VOL; do
+ for snap in $TESTSNAP $TESTSNAP1 $TESTSNAP2; do
+ if snapexists $dtst@$snap; then
+ log_must $ZFS destroy -Rf $dtst@$snap
+ fi
+ done
+ done
+
+ # Restore original test environment
+ if datasetnonexists $FS ; then
+ log_must $ZFS create $FS
+ fi
+ if datasetnonexists $VOL ; then
+ if is_global_zone ; then
+ log_must $ZFS create -V $VOLSIZE $VOL
+ else
+ log_must $ZFS create $VOL
+ fi
+ fi
+}
+
+#
+# check if the specified files have specified status.
+#
+# $1 expected status
+# $2-n full file name
+# If it is true return 0, else return 1
+#
+function file_status
+{
+ (( $# == 0 )) && \
+ log_fail "The file name is not defined."
+
+ typeset opt
+ case $1 in
+ exist) opt="-e" ;;
+ nonexist) opt="! -e" ;;
+ *) log_fail "Unsupported file status." ;;
+ esac
+
+ shift
+ while (( $# > 0 )); do
+ eval [[ $opt $1 ]] || return 1
+ shift
+ done
+
+ return 0
+}
+
+function files_exist
+{
+ file_status "exist" $@
+}
+
+function files_nonexist
+{
+ file_status "nonexist" $@
+}
+
+#
+# According to snapshot check if the file system was recovered to the right
+# point.
+#
+# $1 snapshot. fs@snap or vol@snap
+#
+function check_files
+{
+ typeset dtst=$1
+
+ if [[ $(get_prop type $dtst) != snapshot ]]; then
+ log_fail "Parameter must be a snapshot."
+ fi
+
+ typeset fsvol=${dtst%%@*}
+ typeset snap=${dtst##*@}
+ if [[ $(get_prop type $fsvol) == "filesystem" ]]; then
+ ind=""
+ else
+ ind="1"
+ fi
+
+ eval typeset file0=\$TESTDIR$ind/\$TESTFILE0
+ eval typeset file1=\$TESTDIR$ind/\$TESTFILE1
+ eval typeset file2=\$TESTDIR$ind/\$TESTFILE2
+
+ case $snap in
+ $TESTSNAP2)
+ log_must files_exist $file0 $file1 $file2
+
+ typeset sum0=$(getsum $file0)
+ typeset sum1=$(getsum $file1)
+ typeset sum2=$(getsum $file2)
+ if [[ $sum0 != $origsum || \
+ $sum1 != $origsum || sum2 != $origsum ]]
+ then
+ log_fail "After rollback, file sum is changed."
+ fi
+ ;;
+ $TESTSNAP1)
+ log_must files_exist $file0 $file1
+ log_must files_nonexist $file2
+
+ typeset sum0=$(getsum $file0)
+ typeset sum1=$(getsum $file1)
+ if [[ $sum0 != $origsum || $sum1 != $origsum ]]
+ then
+ log_fail "After rollback, file sum is changed."
+ fi
+ ;;
+ $TESTSNAP)
+ log_must files_exist $file0
+ log_must files_nonexist $file1 $file2
+
+ typeset sum0=$(getsum $file0)
+ if [[ $sum0 != $origsum ]]; then
+ log_fail "After rollback, file sum is changed."
+ fi
+ ;;
+ esac
+}
+
+# According to dataset type, write file to different directories.
+#
+# $1 dataset
+#
+function write_mountpoint_dir
+{
+ typeset dtst=$1
+ typeset dir
+
+ if [[ $dtst == $FS ]]; then
+ dir=$TESTDIR
+ log_must ismounted $dir
+ else
+ dir=$TESTDIR1
+ log_must ismounted $dir "ufs"
+ fi
+ $DD if=/dev/urandom of=$dir/$TESTFILE1 &
+ log_must $SLEEP 3
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/zfs_rollback_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/zfs_rollback_test.sh
new file mode 100755
index 000000000000..04002df4e400
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_rollback/zfs_rollback_test.sh
@@ -0,0 +1,142 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zfs_rollback_001_pos cleanup
+zfs_rollback_001_pos_head()
+{
+ atf_set "descr" "'zfs rollback -r|-rf|-R|-Rf' will recursively destroy anysnapshots more recent than the one specified."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1800
+}
+zfs_rollback_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rollback_common.kshlib
+ . $(atf_get_srcdir)/zfs_rollback.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_rollback_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_rollback_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rollback_common.kshlib
+ . $(atf_get_srcdir)/zfs_rollback.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_rollback_002_pos cleanup
+zfs_rollback_002_pos_head()
+{
+ atf_set "descr" "'zfs rollback -f' will force unmount any filesystems."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1800
+}
+zfs_rollback_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rollback_common.kshlib
+ . $(atf_get_srcdir)/zfs_rollback.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_rollback_002_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_rollback_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rollback_common.kshlib
+ . $(atf_get_srcdir)/zfs_rollback.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_rollback_003_neg cleanup
+zfs_rollback_003_neg_head()
+{
+ atf_set "descr" "Separately verify 'zfs rollback ''|-f|-r|-rf will fail indifferent conditions."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1800
+}
+zfs_rollback_003_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rollback_common.kshlib
+ . $(atf_get_srcdir)/zfs_rollback.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_rollback_003_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_rollback_003_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rollback_common.kshlib
+ . $(atf_get_srcdir)/zfs_rollback.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_rollback_004_neg cleanup
+zfs_rollback_004_neg_head()
+{
+ atf_set "descr" "'zfs rollback' should fail with bad options,too many arguments,non-snapshot datasets or missing datasets."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1800
+}
+zfs_rollback_004_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rollback_common.kshlib
+ . $(atf_get_srcdir)/zfs_rollback.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_rollback_004_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_rollback_004_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_rollback_common.kshlib
+ . $(atf_get_srcdir)/zfs_rollback.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zfs_rollback_001_pos
+ atf_add_test_case zfs_rollback_002_pos
+ atf_add_test_case zfs_rollback_003_neg
+ atf_add_test_case zfs_rollback_004_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_send/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zfs_send/Makefile
new file mode 100644
index 000000000000..ff100e37f330
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_send/Makefile
@@ -0,0 +1,19 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zfs_send
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zfs_send_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zfs_send_003_pos.ksh
+${PACKAGE}FILES+= zfs_send_002_pos.ksh
+${PACKAGE}FILES+= zfs_send.cfg
+${PACKAGE}FILES+= zfs_send_001_pos.ksh
+${PACKAGE}FILES+= zfs_send_004_neg.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_send/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_send/cleanup.ksh
new file mode 100644
index 000000000000..9bbd16b5acbf
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_send/cleanup.ksh
@@ -0,0 +1,32 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+poolexists $TESTPOOL1 && \
+ destroy_pool $TESTPOOL1
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_send/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_send/setup.ksh
new file mode 100644
index 000000000000..326bc555f71b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_send/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_send/zfs_send.cfg b/tests/sys/cddl/zfs/tests/cli_root/zfs_send/zfs_send.cfg
new file mode 100644
index 000000000000..c1a2a26ca210
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_send/zfs_send.cfg
@@ -0,0 +1,31 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
+
+export PARTSIZE=1g
+export BLOCK_SIZE=512
+export WRITE_COUNT=8
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_send/zfs_send_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_send/zfs_send_001_pos.ksh
new file mode 100644
index 000000000000..4b926b924fd2
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_send/zfs_send_001_pos.ksh
@@ -0,0 +1,135 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_send_001_pos
+#
+# DESCRIPTION:
+# Verify 'zfs send' can create valid send streams as expected.
+#
+# STRATEGY:
+# 1. Fill in fs with some data
+# 2. Create a full send streams with the fs
+# 3. Receive the send stream and verify the data integrity
+# 4. Fill in fs with some new data
+# 5. Create an incremental send stream with the fs
+# 6. Receive the incremental send stream and verify the data integrity.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-09-06)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ for snap in $init_snap $inc_snap $rst_snap $rst_inc_snap; do
+ snapexists $snap && \
+ log_must $ZFS destroy -f $snap
+ done
+
+ datasetexists $rst_root && \
+ log_must $ZFS destroy -Rf $rst_root
+
+ for file in $full_bkup $inc_bkup \
+ $init_data $inc_data
+ do
+ [[ -e $file ]] && \
+ log_must $RM -f $file
+ done
+
+ [[ -d $TESTDIR1 ]] && \
+ log_must $RM -rf $TESTDIR1
+
+}
+
+log_assert "Verify 'zfs send' can create valid send streams as expected."
+log_onexit cleanup
+
+init_snap=$TESTPOOL/$TESTFS@init_snap
+inc_snap=$TESTPOOL/$TESTFS@inc_snap
+full_bkup=$TMPDIR/fullbkup.${TESTCASE_ID}
+inc_bkup=$TMPDIR/incbkup.${TESTCASE_ID}
+init_data=$TESTDIR/$TESTFILE1
+inc_data=$TESTDIR/$TESTFILE2
+orig_sum=""
+rst_sum=""
+rst_root=$TESTPOOL/rst_ctr
+rst_snap=$rst_root/$TESTFS@init_snap
+rst_inc_snap=$rst_root/$TESTFS@inc_snap
+rst_data=$TESTDIR1/$TESTFS/$TESTFILE1
+rst_inc_data=$TESTDIR1/$TESTFS/$TESTFILE2
+
+
+log_note "Verify 'zfs send' can create full send stream."
+
+#Pre-paration
+log_must $ZFS create $rst_root
+[[ ! -d $TESTDIR1 ]] && \
+ log_must $MKDIR -p $TESTDIR1
+log_must $ZFS set mountpoint=$TESTDIR1 $rst_root
+
+log_must $FILE_WRITE -o create -f $init_data -b $BLOCK_SIZE -c $WRITE_COUNT
+
+log_must $ZFS snapshot $init_snap
+$ZFS send $init_snap > $full_bkup
+(( $? != 0 )) && \
+ log_fail "'$ZFS send' fails to create full send"
+
+log_note "Verify the send stream is valid to receive."
+
+log_must $ZFS receive $rst_snap <$full_bkup
+receive_check $rst_snap ${rst_snap%%@*}
+compare_cksum $init_data $rst_data
+
+log_note "Verify 'zfs send -i' can create incremental send stream."
+
+log_must $FILE_WRITE -o create -f $inc_data -b $BLOCK_SIZE -c $WRITE_COUNT -d 0
+
+log_must $ZFS snapshot $inc_snap
+$ZFS send -i $init_snap $inc_snap > $inc_bkup
+(( $? != 0 )) && \
+ log_fail "'$ZFS send -i' fails to create incremental send"
+
+log_note "Verify the incremental send stream is valid to receive."
+
+log_must $ZFS rollback $rst_snap
+log_must $ZFS receive $rst_inc_snap <$inc_bkup
+receive_check $rst_inc_snap
+compare_cksum $inc_data $rst_inc_data
+
+log_pass "Verifying 'zfs receive' succeed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_send/zfs_send_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_send/zfs_send_002_pos.ksh
new file mode 100644
index 000000000000..08d30b6b3f12
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_send/zfs_send_002_pos.ksh
@@ -0,0 +1,147 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_send_002_pos
+#
+# DESCRIPTION:
+# Verify 'zfs send' can generate valid streams with a property setup.
+#
+# STRATEGY:
+# 1. Setup property for filesystem
+# 2. Fill in some data into filesystem
+# 3. Create a full send streams
+# 4. Receive the send stream
+# 5. Verify the receive result
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-07-11)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ snapexists $snap && \
+ log_must $ZFS destroy $snap
+
+ datasetexists $ctr && \
+ log_must $ZFS destroy -r $ctr
+
+ [[ -e $origfile ]] && \
+ log_must $RM -f $origfile
+
+ [[ -e $stream ]] && \
+ log_must $RM -f $stream
+}
+
+function do_testing # <prop> <prop_value>
+{
+ typeset property=$1
+ typeset prop_val=$2
+
+ log_must $ZFS set $property=$prop_val $fs
+ log_must $FILE_WRITE -o create -f $origfile -b $BLOCK_SIZE -c $WRITE_COUNT
+ log_must $ZFS snapshot $snap
+ $ZFS send $snap > $stream
+ (( $? != 0 )) && \
+ log_fail "'$ZFS send' fails to create send streams."
+ $ZFS receive -d $ctr <$stream
+ (( $? != 0 )) && \
+ log_fail "'$ZFS receive' fails to receive send streams."
+
+ #verify receive result
+ ! datasetexists $rstfs && \
+ log_fail "'$ZFS receive' fails to restore $rstfs"
+ ! snapexists $rstfssnap && \
+ log_fail "'$ZFS receive' fails to restore $rstfssnap"
+ if [[ ! -e $rstfile ]] || [[ ! -e $rstsnapfile ]]; then
+ log_fail " Data lost after receiving stream"
+ fi
+
+ compare_cksum $origfile $rstfile
+ compare_cksum $origsnapfile $rstsnapfile
+
+ #Destroy datasets and stream for next testing
+ log_must $ZFS destroy $snap
+ if is_global_zone ; then
+ log_must $ZFS destroy -r $rstfs
+ else
+ log_must $ZFS destroy -r $ds_path
+ fi
+ log_must $RM -f $stream
+}
+
+log_assert "Verify 'zfs send' generates valid streams with a property setup"
+log_onexit cleanup
+
+fs=$TESTPOOL/$TESTFS
+snap=$fs@$TESTSNAP
+ctr=$TESTPOOL/$TESTCTR
+if is_global_zone; then
+ rstfs=$ctr/$TESTFS
+else
+ ds_path=$ctr/${ZONE_CTR}0
+ rstfs=$ds_path/$TESTFS
+fi
+rstfssnap=$rstfs@$TESTSNAP
+snapdir="$(get_snapdir_name)/$TESTSNAP"
+origfile=$TESTDIR/$TESTFILE1
+rstfile=/$rstfs/$TESTFILE1
+origsnapfile=$TESTDIR/$snapdir/$TESTFILE1
+rstsnapfile=/$rstfs/$snapdir/$TESTFILE1
+stream=$TMPDIR/streamfile.${TESTCASE_ID}
+
+set -A props "compression" "checksum" "recordsize"
+set -A propval "on lzjb" "on fletcher2 fletcher4 sha256" \
+ "512 1k 4k 8k 16k 32k 64k 128k"
+
+#Create a dataset to receive the send stream
+log_must $ZFS create $ctr
+
+typeset -i i=0
+while (( i < ${#props[*]} ))
+do
+ for value in ${propval[i]}
+ do
+ do_testing ${props[i]} $value
+ done
+
+ (( i = i + 1 ))
+done
+
+log_pass "'zfs send' generates streams with a property setup as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_send/zfs_send_003_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_send/zfs_send_003_pos.ksh
new file mode 100644
index 000000000000..d5db8e33971b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_send/zfs_send_003_pos.ksh
@@ -0,0 +1,78 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_send_003_pos
+#
+# DESCRIPTION:
+# 'zfs send -i' can deal with abbreviated snapshot name.
+#
+# STRATEGY:
+# 1. Create pool, fs and two snapshots.
+# 2. Make sure 'zfs send -i' support abbreviated snapshot name.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-07-18)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ datasetexists $snap1 && log_must $ZFS destroy $snap1
+ datasetexists $snap2 && log_must $ZFS destroy $snap2
+}
+
+log_assert "'zfs send -i' can deal with abbreviated snapshot name."
+log_onexit cleanup
+
+snap1=$TESTPOOL/$TESTFS@snap1; snap2=$TESTPOOL/$TESTFS@snap2
+
+set -A args "$snap1 $snap2" \
+ "${snap1##*@} $snap2" "@${snap1##*@} $snap2"
+
+log_must $ZFS snapshot $snap1
+log_must $ZFS snapshot $snap2
+
+typeset -i i=0
+while (( i < ${#args[*]} )); do
+ log_must eval "$ZFS send -i ${args[i]} > /dev/null"
+
+ (( i += 1 ))
+done
+
+log_pass "'zfs send -i' deal with abbreviated snapshot name passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_send/zfs_send_004_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_send/zfs_send_004_neg.ksh
new file mode 100644
index 000000000000..f52fd30c31ab
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_send/zfs_send_004_neg.ksh
@@ -0,0 +1,118 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_send_004_neg
+#
+# DESCRIPTION:
+# Verify 'zfs send' fails with malformed parameters.
+#
+# STRATEGY:
+# 1. Define malformed parameters in array
+# 2. Feed the parameters to 'zfs send'
+# 3. Verify the result
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-09-06)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ typeset snap f
+
+ for snap in $snap1 $snap2 $snap3; do
+ snapexists $snap && \
+ log_must $ZFS destroy -f $snap
+ done
+
+ for f in $tmpfile1 $tmpfile2; do
+ if [[ -e $f ]]; then
+ $RM -f $f
+ fi
+ done
+}
+
+fs=$TESTPOOL/$TESTFS
+snap1=$fs@snap1
+snap2=$fs@snap2
+snap3=$fs@snap3
+
+set -A badargs \
+ "" "$TESTPOOL" "$TESTFS" "$fs" "$fs@nonexisten_snap" "?" \
+ "$snap1/blah" "$snap1@blah" "-i" "-x" "-i $fs" \
+ "-x $snap1 $snap2" "-i $snap1" \
+ "-i $snap2 $snap1" "$snap1 $snap2" "-i $snap1 $snap2 $snap3" \
+ "-ii $snap1 $snap2" "-iii $snap1 $snap2" " -i $snap2 $snap1/blah" \
+ "-i $snap2/blah $snap1" \
+ "-i $snap2/blah $snap1/blah" \
+ "-i $snap1 blah@blah" \
+ "-i blah@blah $snap1" \
+ "-i $snap1 ${snap2##*@}" "-i $snap1 @${snap2##*@}" \
+ "-i ${snap1##*@} ${snap2##*@}" "-i @${snap1##*@} @${snap2##*@}" \
+ "-i ${snap1##*@} $snap2/blah" "-i @${snap1##*@} $snap2/blah" \
+ "-i @@${snap1##*@} $snap2" "-i $snap1 -i $snap1 $snap2" \
+ "-i snap1 snap2" "-i $snap1 snap2" \
+ "-i $snap1 $snap2 -i $snap1 $snap2" \
+ "-i snap1 $snap2 -i snap1 $snap2"
+
+log_assert "Verify that invalid parameters to 'zfs send' are caught."
+log_onexit cleanup
+
+log_must $ZFS snapshot $snap1
+tmpfile1=$TESTDIR/testfile1.${TESTCASE_ID}
+log_must $TOUCH $tmpfile1
+log_must $ZFS snapshot $snap2
+tmpfile2=$TESTDIR/testfile2.${TESTCASE_ID}
+log_must $TOUCH $tmpfile2
+log_must $ZFS snapshot $snap3
+
+typeset -i i=0
+while (( i < ${#badargs[*]} ))
+do
+ log_mustnot eval "$ZFS send ${badargs[i]} >/dev/null"
+
+ (( i = i + 1 ))
+done
+
+#Testing zfs send fails by send backup stream to terminal
+for arg in "$snap1" "-i $snap1 $snap2"; do
+ log_mustnot eval "$ZFS send $arg >/dev/console"
+done
+
+log_pass "Invalid parameters to 'zfs send' are caught as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_send/zfs_send_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zfs_send/zfs_send_test.sh
new file mode 100755
index 000000000000..23111b4499c0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_send/zfs_send_test.sh
@@ -0,0 +1,130 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zfs_send_001_pos cleanup
+zfs_send_001_pos_head()
+{
+ atf_set "descr" "Verify 'zfs send' can create valid send streams as expected."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_send_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_send.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_send_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_send_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_send.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_send_002_pos cleanup
+zfs_send_002_pos_head()
+{
+ atf_set "descr" "Verify 'zfs send' generates valid streams with a property setup"
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_send_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_send.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_send_002_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_send_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_send.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_send_003_pos cleanup
+zfs_send_003_pos_head()
+{
+ atf_set "descr" "'zfs send -i' can deal with abbreviated snapshot name."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_send_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_send.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_send_003_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_send_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_send.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_send_004_neg cleanup
+zfs_send_004_neg_head()
+{
+ atf_set "descr" "Verify that invalid parameters to 'zfs send' are caught."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_send_004_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_send.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_send_004_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_send_004_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_send.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zfs_send_001_pos
+ atf_add_test_case zfs_send_002_pos
+ atf_add_test_case zfs_send_003_pos
+ atf_add_test_case zfs_send_004_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/Makefile
new file mode 100644
index 000000000000..69300727fd9e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/Makefile
@@ -0,0 +1,42 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zfs_set
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zfs_set_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zfs_set_common.kshlib
+${PACKAGE}FILES+= onoffs_001_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= mountpoint_001_pos.ksh
+${PACKAGE}FILES+= share_mount_001_neg.ksh
+${PACKAGE}FILES+= user_property_004_pos.ksh
+${PACKAGE}FILES+= canmount_002_pos.ksh
+${PACKAGE}FILES+= readonly_001_pos.ksh
+${PACKAGE}FILES+= zfs_set_001_neg.ksh
+${PACKAGE}FILES+= user_property_001_pos.ksh
+${PACKAGE}FILES+= snapdir_001_pos.ksh
+${PACKAGE}FILES+= canmount_003_pos.ksh
+${PACKAGE}FILES+= reservation_001_neg.ksh
+${PACKAGE}FILES+= compression_001_pos.ksh
+${PACKAGE}FILES+= cache_001_pos.ksh
+${PACKAGE}FILES+= zfs_set_003_neg.ksh
+${PACKAGE}FILES+= canmount_001_pos.ksh
+${PACKAGE}FILES+= property_alias_001_pos.ksh
+${PACKAGE}FILES+= version_001_neg.ksh
+${PACKAGE}FILES+= zfs_set.cfg
+${PACKAGE}FILES+= cache_002_neg.ksh
+${PACKAGE}FILES+= mountpoint_002_pos.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= checksum_001_pos.ksh
+${PACKAGE}FILES+= mountpoint_003_pos.ksh
+${PACKAGE}FILES+= user_property_002_pos.ksh
+${PACKAGE}FILES+= zfs_set_002_neg.ksh
+${PACKAGE}FILES+= user_property_003_neg.ksh
+${PACKAGE}FILES+= ro_props_001_pos.ksh
+${PACKAGE}FILES+= canmount_004_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/cache_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/cache_001_pos.ksh
new file mode 100644
index 000000000000..2b11e76e1e50
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/cache_001_pos.ksh
@@ -0,0 +1,76 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_set/zfs_set_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: cache_001_pos
+#
+# DESCRIPTION:
+# Setting a valid primarycache and secondarycache on file system or volume.
+# It should be successful.
+#
+# STRATEGY:
+# 1. Create pool, then create filesystem & volume within it.
+# 2. Setting valid cache value, it should be successful.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-04-16)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+set -A dataset "$TESTPOOL" "$TESTPOOL/$TESTFS" "$TESTPOOL/$TESTVOL"
+set -A values "none" "all" "metadata"
+
+log_assert "Setting a valid {primary|secondary}cache on file system and volume, " \
+ "It should be successful."
+
+typeset -i i=0
+typeset -i j=0
+for propname in "primarycache" "secondarycache"
+do
+ while (( i < ${#dataset[@]} )); do
+ j=0
+ while (( j < ${#values[@]} )); do
+ set_n_check_prop "${values[j]}" "$propname" "${dataset[i]}"
+ (( j += 1 ))
+ done
+ (( i += 1 ))
+ done
+done
+
+log_pass "Setting a valid {primary|secondary}cache on file system or volume pass."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/cache_002_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/cache_002_neg.ksh
new file mode 100644
index 000000000000..0bf2c4809beb
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/cache_002_neg.ksh
@@ -0,0 +1,76 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_set/zfs_set_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: cache_001_pos
+#
+# DESCRIPTION:
+# Setting invalid primarycache and secondarycache on file system or volume.
+# It should fail.
+#
+# STRATEGY:
+# 1. Create pool, then create filesystem & volume within it.
+# 2. Setting invalid {primary|secondary}cache value, it should fail.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-04-16)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+set -A dataset "$TESTPOOL" "$TESTPOOL/$TESTFS" "$TESTPOOL/$TESTVOL"
+set -A values "12345" "null" "not_existed" "abcd1234"
+
+log_assert "Setting invalid {primary|secondary}cache on fs and volume, " \
+ "It should fail."
+
+typeset -i i=0
+typeset -i j=0
+for propname in "primarycache" "secondarycache"
+do
+ while (( i < ${#dataset[@]} )); do
+ j=0
+ while (( j < ${#values[@]} )); do
+ log_mustnot $ZFS set $propname=${values[j]} ${dataset[i]}
+ (( j += 1 ))
+ done
+ (( i += 1 ))
+ done
+done
+
+log_pass "Setting invalid {primary|secondary}cache on fs or volume fail as expeced."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/canmount_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/canmount_001_pos.ksh
new file mode 100644
index 000000000000..00bf0c4c67c7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/canmount_001_pos.ksh
@@ -0,0 +1,132 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_set/zfs_set_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: canmount_001_pos
+#
+# DESCRIPTION:
+# Setting valid canmount to filesystem, it is successful.
+# Whatever is set to volume or snapshot, it is failed.
+# 'zfs set canmount=on|off <fs>'
+#
+# STRATEGY:
+# 1. Setup a pool and create fs, volume, snapshot clone within it.
+# 2. Loop all the valid mountpoint value.
+# 3. Check the return value.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-08-30)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+set -A dataset_pos \
+ "$TESTPOOL/$TESTFS" "$TESTPOOL/$TESTCTR" "$TESTPOOL/$TESTCLONE"
+
+if is_global_zone ; then
+ set -A dataset_neg \
+ "$TESTPOOL/$TESTVOL" "$TESTPOOL/$TESTFS@$TESTSNAP" \
+ "$TESTPOOL/$TESTVOL@$TESTSNAP" "$TESTPOOL/$TESTCLONE1"
+else
+ set -A dataset_neg \
+ "$TESTPOOL/$TESTFS@$TESTSNAP" "$TESTPOOL/$TESTVOL@$TESTSNAP"
+fi
+
+
+set -A values "on" "off"
+
+function cleanup
+{
+ if snapexists $TESTPOOL/$TESTFS@$TESTSNAP ; then
+ log_must $ZFS destroy -R $TESTPOOL/$TESTFS@$TESTSNAP
+ fi
+ if snapexists $TESTPOOL/$TESTVOL@$TESTSNAP ; then
+ log_must $ZFS destroy -R $TESTPOOL/$TESTVOL@$TESTSNAP
+ fi
+
+ [[ -n $old_ctr_canmount ]] && \
+ log_must $ZFS set canmount=$old_ctr_canmount $TESTPOOL/$TESTCTR
+ [[ -n $old_fs_canmount ]] && \
+ log_must $ZFS set canmount=$old_fs_canmount $TESTPOOL/$TESTFS
+
+ unmount_all_safe > /dev/null 2>&1
+ log_must $ZFS mount -a
+}
+
+log_assert "Setting a valid property of canmount to file system, it must be successful."
+log_onexit cleanup
+
+typeset old_fs_canmount="" old_ctr_canmount=""
+
+old_fs_canmount=$(get_prop canmount $TESTPOOL/$TESTFS)
+[[ $? != 0 ]] && \
+ log_fail "Get the $TESTPOOL/$TESTFS canmount error."
+old_ctr_canmount=$(get_prop canmount $TESTPOOL/$TESTCTR)
+[[ $? != 0 ]] && \
+ log_fail "Get the $TESTPOOL/$TESTCTR canmount error."
+
+log_must $ZFS snapshot $TESTPOOL/$TESTFS@$TESTSNAP
+log_must $ZFS snapshot $TESTPOOL/$TESTVOL@$TESTSNAP
+log_must $ZFS clone $TESTPOOL/$TESTFS@$TESTSNAP $TESTPOOL/$TESTCLONE
+log_must $ZFS clone $TESTPOOL/$TESTVOL@$TESTSNAP $TESTPOOL/$TESTCLONE1
+
+for dataset in "${dataset_pos[@]}" ; do
+ for value in "${values[@]}" ; do
+ set_n_check_prop "$value" "canmount" "$dataset"
+ if [[ $value == "off" ]]; then
+ log_mustnot ismounted $dataset
+ log_mustnot $ZFS mount $dataset
+ log_mustnot ismounted $dataset
+ else
+ if ! ismounted $dataset ; then
+ log_must $ZFS mount $dataset
+ fi
+ log_must ismounted $dataset
+ fi
+ done
+done
+
+for dataset in "${dataset_neg[@]}" ; do
+ for value in "${values[@]}" ; do
+ set_n_check_prop "$value" "canmount" \
+ "$dataset" "false"
+ log_mustnot ismounted $dataset
+ done
+done
+
+log_pass "Setting canmount to filesystem pass."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/canmount_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/canmount_002_pos.ksh
new file mode 100644
index 000000000000..f37925e79ed4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/canmount_002_pos.ksh
@@ -0,0 +1,165 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_set/zfs_set_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: canmount_002_pos
+#
+# DESCRIPTION:
+# Setting valid canmount to filesystem, it is successful.
+# Whatever is set to volume or snapshot, it is failed.
+# 'zfs set canmount=noauto <fs>'
+#
+# STRATEGY:
+# 1. Setup a pool and create fs, volume, snapshot clone within it.
+# 2. Set canmount=noauto for each dataset and check the retuen value
+# and check if it still can be mounted by mount -a.
+# 3. mount each dataset(except volume) to see if it can be mounted.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-03-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+# check if the testing box support noauto option or not.
+$ZFS get 2>&1 | $GREP -w canmount | $GREP -w noauto >/dev/null 2>&1
+if (( $? != 0 )); then
+ log_unsupported "canmount=noauto is not supported."
+fi
+
+set -A dataset_pos \
+ "$TESTPOOL/$TESTFS" "$TESTPOOL/$TESTCTR" "$TESTPOOL/$TESTCLONE"
+
+if is_global_zone ; then
+ set -A dataset_neg \
+ "$TESTPOOL/$TESTVOL" "$TESTPOOL/$TESTFS@$TESTSNAP" \
+ "$TESTPOOL/$TESTVOL@$TESTSNAP" "$TESTPOOL/$TESTCLONE1"
+else
+ set -A dataset_neg \
+ "$TESTPOOL/$TESTFS@$TESTSNAP" "$TESTPOOL/$TESTVOL@$TESTSNAP"
+fi
+
+function cleanup
+{
+ i=0
+ while (( i < ${#dataset_pos[*]} )); do
+ ds=${dataset_pos[i]}
+ if datasetexists $ds; then
+ log_must $ZFS set mountpoint=${old_mnt[i]} $ds
+ log_must $ZFS set canmount=${old_canmount[i]} $ds
+ fi
+ (( i = i + 1 ))
+ done
+
+ ds=$TESTPOOL/$TESTCLONE
+ if datasetexists $ds; then
+ mntp=$(get_prop mountpoint $ds)
+ log_must $ZFS destroy $ds
+ if [[ -d $mntp ]]; then
+ $RM -fr $mntp
+ fi
+ fi
+
+ if snapexists $TESTPOOL/$TESTFS@$TESTSNAP ; then
+ log_must $ZFS destroy -R $TESTPOOL/$TESTFS@$TESTSNAP
+ fi
+ if snapexists $TESTPOOL/$TESTVOL@$TESTSNAP ; then
+ log_must $ZFS destroy -R $TESTPOOL/$TESTVOL@$TESTSNAP
+ fi
+
+ $ZFS unmount -a > /dev/null 2>&1
+ log_must $ZFS mount -a
+
+ if [[ -d $tmpmnt ]]; then
+ $RM -fr $tmpmnt
+ fi
+}
+
+log_assert "Setting canmount=noauto to file system, it must be successful."
+log_onexit cleanup
+
+set -A old_mnt
+set -A old_canmount
+typeset tmpmnt=/tmpmount${TESTCASE_ID}
+typeset ds
+
+log_must $ZFS snapshot $TESTPOOL/$TESTFS@$TESTSNAP
+log_must $ZFS snapshot $TESTPOOL/$TESTVOL@$TESTSNAP
+log_must $ZFS clone $TESTPOOL/$TESTFS@$TESTSNAP $TESTPOOL/$TESTCLONE
+log_must $ZFS clone $TESTPOOL/$TESTVOL@$TESTSNAP $TESTPOOL/$TESTCLONE1
+
+typeset -i i=0
+while (( i < ${#dataset_pos[*]} )); do
+ ds=${dataset_pos[i]}
+ old_mnt[i]=$(get_prop mountpoint $ds)
+ old_canmount[i]=$(get_prop canmount $ds)
+ (( i = i + 1 ))
+done
+
+i=0
+while (( i < ${#dataset_pos[*]} )) ; do
+ dataset=${dataset_pos[i]}
+ set_n_check_prop "noauto" "canmount" "$dataset"
+ log_must $ZFS set mountpoint=$tmpmnt $dataset
+ if ismounted $dataset; then
+ $ZFS unmount -a > /dev/null 2>&1
+ log_must mounted $dataset
+ log_must $ZFS unmount $dataset
+ log_must unmounted $dataset
+ log_must $ZFS mount -a
+ log_must unmounted $dataset
+ else
+ log_must $ZFS mount -a
+ log_must unmounted $dataset
+ $ZFS unmount -a > /dev/null 2>&1
+ log_must unmounted $dataset
+ fi
+
+ log_must $ZFS mount $dataset
+ log_must mounted $dataset
+ log_must $ZFS set canmount="${old_canmount[i]}" $dataset
+ log_must $ZFS set mountpoint="${old_mnt[i]}" $dataset
+ (( i = i + 1 ))
+done
+
+for dataset in "${dataset_neg[@]}" ; do
+ set_n_check_prop "noauto" "canmount" "$dataset" "false"
+ log_mustnot ismounted $dataset
+done
+
+log_pass "Setting canmount=noauto to filesystem pass."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/canmount_003_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/canmount_003_pos.ksh
new file mode 100644
index 000000000000..0e8b8f0fda0d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/canmount_003_pos.ksh
@@ -0,0 +1,126 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_set/zfs_set_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: canmount_003_pos
+#
+# DESCRIPTION:
+# While canmount=noauto and the dataset is mounted,
+# zfs must not attempt to unmount it.
+#
+# STRATEGY:
+# 1. Setup a pool and create fs, volume, snapshot clone within it.
+# 2. Set canmount=noauto for each dataset and check the return value
+# and check if it still can not be unmounted when the dataset is mounted
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-09-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+# check if the testing box support noauto option or not.
+$ZFS get 2>&1 | $GREP -w canmount | $GREP -w noauto >/dev/null 2>&1
+if (( $? != 0 )); then
+ log_unsupported "canmount=noauto is not supported."
+fi
+
+set -A dataset_pos "$TESTPOOL/$TESTFS" "$TESTPOOL/$TESTCLONE"
+
+function cleanup
+{
+ i=0
+ cd $pwd
+ while (( i < ${#dataset_pos[*]} )); do
+ ds=${dataset_pos[i]}
+ if datasetexists $ds; then
+ log_must $ZFS set mountpoint=${old_mnt[i]} $ds
+ log_must $ZFS set canmount=${old_canmount[i]} $ds
+ fi
+ (( i = i + 1 ))
+ done
+
+ ds=$TESTPOOL/$TESTCLONE
+ if datasetexists $ds; then
+ mntp=$(get_prop mountpoint $ds)
+ log_must $ZFS destroy $ds
+ if [[ -d $mntp ]]; then
+ log_must $RM -fr $mntp
+ fi
+ fi
+
+ if snapexists $TESTPOOL/$TESTFS@$TESTSNAP ; then
+ log_must $ZFS destroy -R $TESTPOOL/$TESTFS@$TESTSNAP
+ fi
+
+ unmount_all_safe > /dev/null 2>&1
+ log_must $ZFS mount -a
+}
+
+log_assert "While canmount=noauto and the dataset is mounted,"\
+ " zfs must not attempt to unmount it"
+log_onexit cleanup
+
+set -A old_mnt
+set -A old_canmount
+typeset ds
+typeset pwd=$PWD
+
+log_must $ZFS snapshot $TESTPOOL/$TESTFS@$TESTSNAP
+log_must $ZFS clone $TESTPOOL/$TESTFS@$TESTSNAP $TESTPOOL/$TESTCLONE
+
+typeset -i i=0
+while (( i < ${#dataset_pos[*]} )); do
+ ds=${dataset_pos[i]}
+ old_mnt[i]=$(get_prop mountpoint $ds)
+ old_canmount[i]=$(get_prop canmount $ds)
+ (( i = i + 1 ))
+done
+
+i=0
+while (( i < ${#dataset_pos[*]} )) ; do
+ dataset=${dataset_pos[i]}
+ if ismounted $dataset; then
+ log_must cd ${old_mnt[i]}
+ set_n_check_prop "noauto" "canmount" "$dataset"
+ log_must mounted $dataset
+ fi
+ (( i = i + 1 ))
+done
+
+log_pass "Setting canmount=noauto to filesystem while dataset busy pass."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/canmount_004_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/canmount_004_pos.ksh
new file mode 100644
index 000000000000..ca5e7f161ce4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/canmount_004_pos.ksh
@@ -0,0 +1,108 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: canmount_sharenfs_001_pos
+#
+# DESCRIPTION:
+# Verify canmount=noauto work fine when setting sharenfs or sharesmb.
+#
+# STRATEGY:
+# 1. Create a fs canmount=noauto.
+# 2. Set sharenfs or sharesmb.
+# 3. Verify the fs is umounted.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-05-25)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+# properties
+set -A sharenfs_prop "off" "on" "rw"
+set -A sharesmb_prop "off" "on"
+if check_version "5.11" ; then
+ set -A sharesmb_prop ${sharesmb_prop[*]} "name=anybody"
+fi
+
+function cleanup
+{
+ log_must $ZFS destroy -rR $CS_FS
+}
+
+function assert_unmounted
+{
+ mnted=$(get_prop mounted $CS_FS)
+ if [[ "$mnted" == "yes" ]]; then
+ canmnt=$(get_prop canmount $CS_FS)
+ shnfs=$(get_prop sharenfs $CS_FS)
+ shsmb=$(get_prop sharesmb $CS_FS)
+ mntpt=$(get_prop mountpoint $CS_FS)
+ log_fail "$CS_FS should be unmounted" \
+ "[canmount=$canmnt,sharenfs=$shnfs,sharesmb=$shsmb,mountpoint=$mntpt]."
+ fi
+}
+
+log_assert "Verify canmount=noauto work fine when setting sharenfs or sharesmb."
+log_onexit cleanup
+
+CS_FS=$TESTPOOL/$TESTFS/cs_fs.${TESTCASE_ID}
+oldmpt=$TESTDIR/old_cs_fs.${TESTCASE_ID}
+newmpt=$TESTDIR/new_cs_fs.${TESTCASE_ID}
+
+log_must $ZFS create -o canmount=noauto -o mountpoint=$oldmpt $CS_FS
+assert_unmounted
+
+for n in ${sharenfs_prop[@]}; do
+ log_must $ZFS set sharenfs="$n" $CS_FS
+ assert_unmounted
+ for s in ${sharesmb_prop[@]}; do
+ log_must $ZFS set sharesmb="$s" $CS_FS
+ assert_unmounted
+
+ mntpt=$(get_prop mountpoint $CS_FS)
+ if [[ "$mntpt" == "$oldmpt" ]]; then
+ log_must $ZFS set mountpoint="$newmpt" $CS_FS
+ else
+ log_must $ZFS set mountpoint="$oldmpt" $CS_FS
+ fi
+ assert_unmounted
+ done
+done
+
+log_pass "Verify canmount=noauto work fine when setting sharenfs or sharesmb."
+
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/checksum_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/checksum_001_pos.ksh
new file mode 100644
index 000000000000..c374de481a45
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/checksum_001_pos.ksh
@@ -0,0 +1,80 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_set/zfs_set_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: checksum_001_pos
+#
+# DESCRIPTION:
+# Setting a valid checksum on a pool, file system, volume, it should be
+# successful.
+#
+# STRATEGY:
+# 1. Create pool, then create filesystem and volume within it.
+# 2. Setting different valid checksum to each dataset.
+# 3. Check the return value and make sure it is 0.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+enc=$(get_prop encryption $TESTPOOL/$TESTFS)
+if [[ $? -eq 0 ]] && [[ -n "$enc" ]] && [[ "$enc" != "off" ]]; then
+ log_unsupported "checksum property can not be changed when \
+encryption is set to on."
+fi
+
+set -A dataset "$TESTPOOL" "$TESTPOOL/$TESTFS" "$TESTPOOL/$TESTVOL"
+set -A values "on" "off" "fletcher2" "fletcher4" "sha256"
+
+log_assert "Setting a valid checksum on a file system, volume," \
+ "it should be successful."
+
+typeset -i i=0
+typeset -i j=0
+while (( i < ${#dataset[@]} )); do
+ j=0
+ while (( j < ${#values[@]} )); do
+ set_n_check_prop "${values[j]}" "checksum" "${dataset[i]}"
+ (( j += 1 ))
+ done
+ (( i += 1 ))
+done
+
+log_pass "Setting a valid checksum on a file system, volume pass."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/cleanup.ksh
new file mode 100644
index 000000000000..0c0a44b06965
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_container_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/compression_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/compression_001_pos.ksh
new file mode 100644
index 000000000000..605f4cc6e768
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/compression_001_pos.ksh
@@ -0,0 +1,76 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_set/zfs_set_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: compression_001_pos
+#
+# DESCRIPTION:
+# Setting a valid compression on file system or volume.
+# It should be successful.
+#
+# STRATEGY:
+# 1. Create pool, then create filesystem & volume within it.
+# 2. Setting valid value, it should be successful.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+set -A dataset "$TESTPOOL" "$TESTPOOL/$TESTFS" "$TESTPOOL/$TESTVOL"
+set -A values $(get_compress_opts zfs_set)
+
+log_assert "Setting a valid compression on file system and volume, " \
+ "It should be successful."
+
+typeset -i i=0
+typeset -i j=0
+for propname in "compression" "compress"
+do
+ while (( i < ${#dataset[@]} )); do
+ j=0
+ while (( j < ${#values[@]} )); do
+ set_n_check_prop "${values[j]}" "$propname" "${dataset[i]}"
+ (( j += 1 ))
+ done
+ (( i += 1 ))
+ done
+done
+
+log_pass "Setting a valid compression on file system or volume pass."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/mountpoint_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/mountpoint_001_pos.ksh
new file mode 100644
index 000000000000..277a479263c4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/mountpoint_001_pos.ksh
@@ -0,0 +1,107 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_set/zfs_set_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: mountpoint_001_pos
+#
+# DESCRIPTION:
+# Setting valid mountpoint to filesystem, it is successful.
+# Whatever is set to volume, it is failed.
+# 'zfs set mountpoint=<path>|legacy|none <fs|ctr|vol>'
+#
+# STRATEGY:
+# 1. Setup a pool and create fs, ctr within it.
+# 2. Loop all the valid mountpoint value.
+# 3. Check the return value.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+if is_global_zone ; then
+ set -A dataset \
+ "$TESTPOOL/$TESTFS" "$TESTPOOL/$TESTCTR" "$TESTPOOL/$TESTVOL"
+else
+ set -A dataset "$TESTPOOL/$TESTFS" "$TESTPOOL/$TESTCTR"
+fi
+
+set -A values "$TESTDIR2" "legacy" "none" "$TESTDIR_NOTEXISTING"
+
+function cleanup
+{
+ log_must $ZFS set mountpoint=$old_ctr_mpt $TESTPOOL/$TESTCTR
+ log_must $ZFS set mountpoint=$old_fs_mpt $TESTPOOL/$TESTFS
+ [[ -d $TESTDIR2 ]] && log_must $RM -r $TESTDIR2
+ [[ -d $TESTDIR_NOTEXISTING ]] && log_must $RM -r $TESTDIR_NOTEXISTING
+}
+
+log_assert "Setting a valid mountpoint to file system, it must be successful."
+log_onexit cleanup
+
+old_fs_mpt=$(get_prop mountpoint $TESTPOOL/$TESTFS)
+[[ $? != 0 ]] && \
+ log_fail "Get the $TESTPOOL/$TESTFS mountpoint error."
+old_ctr_mpt=$(get_prop mountpoint $TESTPOOL/$TESTCTR)
+[[ $? != 0 ]] && \
+ log_fail "Get the $TESTPOOL/$TESTCTR mountpoint error."
+
+if [[ ! -d $TESTDIR2 ]]; then
+ log_must $MKDIR $TESTDIR2
+fi
+
+typeset -i i=0
+typeset -i j=0
+while (( i < ${#dataset[@]} )); do
+ j=0
+ while (( j < ${#values[@]} )); do
+ if [[ ${dataset[i]} == "$TESTPOOL/$TESTVOL" ]]; then
+ set_n_check_prop "${values[j]}" "mountpoint" \
+ "${dataset[i]}" "false"
+ else
+ set_n_check_prop "${values[j]}" "mountpoint" \
+ "${dataset[i]}"
+ fi
+ (( j += 1 ))
+ done
+ cleanup
+ (( i += 1 ))
+done
+
+log_pass "Setting mountpoint to filesystem pass."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/mountpoint_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/mountpoint_002_pos.ksh
new file mode 100644
index 000000000000..c6df72be2bae
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/mountpoint_002_pos.ksh
@@ -0,0 +1,105 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_set/zfs_set_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: mountpoint_002_pos
+#
+# DESCRIPTION:
+# If ZFS is currently managing the file system but it is currently unmoutned,
+# and the mountpoint property is changed, the file system remains unmounted.
+#
+# STRATEGY:
+# 1. Setup a pool and create fs, ctr within it.
+# 2. Unmount that dataset
+# 2. Change the mountpoint to the valid mountpoint value.
+# 3. Check the file system remains unmounted.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+set -A dataset "$TESTPOOL/$TESTFS" "$TESTPOOL/$TESTCTR"
+
+set -A values "$TESTDIR2" "$TESTDIR_NOTEXISTING"
+
+function cleanup
+{
+ log_must $ZFS set mountpoint=$old_ctr_mpt $TESTPOOL/$TESTCTR
+ log_must $ZFS set mountpoint=$old_fs_mpt $TESTPOOL/$TESTFS
+ log_must $ZFS mount -a
+ [[ -d $TESTDIR2 ]] && log_must $RM -r $TESTDIR2
+ [[ -d $TESTDIR_NOTEXISTING ]] && log_must $RM -r $TESTDIR_NOTEXISTING
+}
+
+log_assert "Setting a valid mountpoint for an unmounted file system, \
+ it remains unmounted."
+log_onexit cleanup
+
+old_fs_mpt=$(get_prop mountpoint $TESTPOOL/$TESTFS)
+[[ $? != 0 ]] && \
+ log_fail "Unable to get the mountpoint property for $TESTPOOL/$TESTFS"
+old_ctr_mpt=$(get_prop mountpoint $TESTPOOL/$TESTCTR)
+[[ $? != 0 ]] && \
+ log_fail "Unable to get the mountpoint property for $TESTPOOL/$TESTCTR"
+
+if [[ ! -d $TESTDIR2 ]]; then
+ log_must $MKDIR $TESTDIR2
+fi
+
+typeset -i i=0
+typeset -i j=0
+while (( i < ${#dataset[@]} )); do
+ j=0
+ if ismounted ${dataset[i]} ; then
+ log_must $ZFS unmount ${dataset[i]}
+ fi
+ log_mustnot ismounted ${dataset[i]}
+ while (( j < ${#values[@]} )); do
+ set_n_check_prop "${values[j]}" "mountpoint" \
+ "${dataset[i]}"
+ log_mustnot ismounted ${dataset[i]}
+ (( j += 1 ))
+ done
+ cleanup
+ (( i += 1 ))
+done
+
+log_pass "Setting a valid mountpoint for an unmounted file system, \
+ it remains unmounted."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/mountpoint_003_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/mountpoint_003_pos.ksh
new file mode 100644
index 000000000000..aaabd441744e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/mountpoint_003_pos.ksh
@@ -0,0 +1,111 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: mountpoint_003_pos
+#
+# DESCRIPTION:
+# Verify FSType-specific option works well with legacy mount.
+#
+# STRATEGY:
+# 1. Set up FSType-specific options and expected keywords array.
+# 2. Create a test ZFS file system and set mountpoint=legacy.
+# 3. Mount ZFS test filesystem with specific options.
+# 4. Verify the filesystem was mounted with specific option.
+# 5. Loop check all the options.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-01-26)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ ismounted $tmpmnt && log_must $UMOUNT $tmpmnt
+ [[ -d $tmpmnt ]] && log_must $RM -rf $tmpmnt
+ [[ -n $oldmpt ]] && log_must $ZFS set mountpoint=$oldmpt $testfs
+ ! ismounted $oldmpt && log_must $ZFS mount $testfs
+}
+
+log_assert "With legacy mount, FSType-specific option works well."
+log_onexit cleanup
+
+#
+# /mnt on pool/fs read/write/setuid/devices/noexec/xattr/atime/dev=2d9009e
+#
+# FSType- FSType-
+# specific Keyword specific Keyword
+# option option
+#
+set -A args \
+ "devices" "/devices/" "nodevices" "/nodevices/" \
+ "exec" "/exec/" "noexec" "/noexec/" \
+ "nbmand" "/nbmand/" "nonbmand" "/nonbmand/" \
+ "ro" "read only" "rw" "read/write" \
+ "setuid" "/setuid/" "nosetuid" "/nosetuid/" \
+ "xattr" "/xattr/" "noxattr" "/noxattr/" \
+ "atime" "/atime/" "noatime" "/noatime/"
+
+tmpmnt=/tmpmnt.${TESTCASE_ID}
+[[ -d $tmpmnt ]] && $RM -rf $tmpmnt
+testfs=$TESTPOOL/$TESTFS
+log_must $MKDIR $tmpmnt
+oldmpt=$(get_prop mountpoint $testfs)
+log_must $ZFS set mountpoint=legacy $testfs
+
+typeset i=0
+while ((i < ${#args[@]})); do
+ log_must $MOUNT -t zfs -o ${args[$i]} $testfs $tmpmnt
+ msg=$($MOUNT | $GREP "^$tmpmnt ")
+
+ # In LZ, a user with all zone privileges can never with "devices"
+ if ! is_global_zone && [[ ${args[$i]} == devices ]] ; then
+ args[((i+1))]="/nodevices/"
+ fi
+
+ $ECHO $msg | $GREP "${args[((i+1))]}" > /dev/null 2>&1
+ if (($? != 0)) ; then
+ log_fail "Expected option: ${args[((i+1))]} \n" \
+ "Real option: $msg"
+ fi
+
+ log_must $UMOUNT $tmpmnt
+ ((i += 2))
+done
+
+log_pass "With legacy mount, FSType-specific option works well passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/onoffs_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/onoffs_001_pos.ksh
new file mode 100644
index 000000000000..60f7182eafab
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/onoffs_001_pos.ksh
@@ -0,0 +1,101 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_set/zfs_set_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: onoffs_001_pos
+#
+# DESCRIPTION:
+# Setting a valid value to atime, readonly, or setuid on file
+# system or volume. It should be successful.
+#
+# STRATEGY:
+# 1. Create pool and filesystem & volume within it.
+# 2. Setting valid value, it should be successful.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ log_must $ZFS mount -a
+}
+
+log_onexit cleanup
+
+set -A props "atime" "readonly" "setuid"
+set -A values "on" "off"
+
+if is_global_zone ; then
+ set -A dataset "$TESTPOOL/$TESTFS" "$TESTPOOL/$TESTCTR" "$TESTPOOL/$TESTVOL"
+else
+ set -A dataset "$TESTPOOL/$TESTFS" "$TESTPOOL/$TESTCTR"
+fi
+
+log_assert "Setting a valid value to atime, readonly, or setuid on file" \
+ "system or volume. It should be successful."
+
+typeset -i i=0
+typeset -i j=0
+typeset -i k=0
+while (( i < ${#dataset[@]} )); do
+ j=0
+ while (( j < ${#props[@]} )); do
+ k=0
+ while (( k < ${#values[@]} )); do
+ if [[ ${dataset[i]} == "$TESTPOOL/$TESTVOL" && \
+ ${props[j]} != "readonly" ]]
+ then
+ set_n_check_prop "${values[k]}" "${props[j]}" \
+ "${dataset[i]}" "false"
+ else
+ set_n_check_prop "${values[k]}" "${props[j]}" \
+ "${dataset[i]}"
+ fi
+
+ (( k += 1 ))
+ done
+ (( j += 1 ))
+ done
+ (( i += 1 ))
+done
+
+log_pass "Setting a valid value to atime, readonly, or setuid on file" \
+ "system or volume pass."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/property_alias_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/property_alias_001_pos.ksh
new file mode 100644
index 000000000000..9760c6edcf7d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/property_alias_001_pos.ksh
@@ -0,0 +1,153 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_set/zfs_set_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: property_alias_001_pos
+#
+# DESCRIPTION:
+# Verify the properties with aliases also work with those aliases
+#
+# STRATEGY:
+# 1. Create pool, then create filesystem & volume within it.
+# 2. Set or retrieve property via alias with datasets.
+# 3. Verify the result should be successful.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-08-16)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function set_and_check #<dataset><set_prop_name><set_value><check_prop_name>
+{
+ typeset ds=$1
+ typeset setprop=$2
+ typeset setval=$3
+ typeset chkprop=$4
+ typeset getval
+
+ log_must $ZFS set $setprop=$setval $ds
+ if [[ $setval == "gzip-6" ]]; then
+ setval="gzip"
+ fi
+ getval=$(get_prop $chkprop $ds)
+
+ case $setprop in
+ reservation|reserv )
+ if [[ $setval == "none" ]]; then
+ [[ $getval != "0" ]] && \
+ log_fail "Setting the property $setprop" \
+ "with value $setval fails."
+ elif [[ $getval != $setval ]]; then
+ log_fail "Setting the property $setprop with" \
+ "with $setval fails."
+ fi
+ ;;
+ * )
+ [[ $getval != $setval ]] && \
+ log_fail "Setting the property $setprop with value \
+ $setval fails."
+ ;;
+ esac
+}
+
+log_assert "Properties with aliases also work with those aliases."
+
+set -A ro_prop "available" "avail" "referenced" "refer"
+set -A rw_prop "readonly" "rdonly" "compression" "compress" "reservation" "reserv"
+set -A chk_prop "rdonly" "readonly" "compress" "compression" "reserv" "reservation"
+set -A size "512" "1024" "2048" "4096" "8192" "16384" "32768" "65536" "131072"
+
+pool=$TESTPOOL
+fs=$TESTPOOL/$TESTFS
+vol=$TESTPOOL/$TESTVOL
+typeset -l avail_space=$(get_prop avail $pool)
+typeset -l reservsize
+typeset -i i=0
+
+for ds in $pool $fs $vol; do
+ for propname in ${ro_prop[*]}; do
+ $ZFS get -pH -o value $propname $ds >/dev/null 2>&1
+ (( $? != 0 )) && \
+ log_fail "Get the property $proname of $ds failed."
+ done
+ i=0
+ while (( i < ${#rw_prop[*]} )); do
+ case ${rw_prop[i]} in
+ readonly|rdonly )
+ for val in "on" "off"; do
+ set_and_check $ds ${rw_prop[i]} $val ${chk_prop[i]}
+ done
+ ;;
+ compression|compress )
+ for val in $(get_compress_opts zfs_set); do
+ set_and_check $ds ${rw_prop[i]} $val ${chk_prop[i]}
+ done
+ ;;
+ reservation|reserv )
+ (( reservsize = $avail_space % $RANDOM ))
+ for val in "0" "$reservsize" "none"; do
+ set_and_check $ds ${rw_prop[i]} $val ${chk_prop[i]}
+ done
+ ;;
+ esac
+
+ (( i = i + 1 ))
+ done
+ if [[ $ds == $vol ]]; then
+ for propname in "volblocksize" "volblock" ; do
+ $ZFS get -pH -o value $propname $ds >/dev/null 2>&1
+ (( $? != 0 )) && \
+ log_fail "Get the property $propname of $ds failed."
+ done
+ fi
+done
+
+for ds in $pool $fs; do
+ for propname in "recordsize" "recsize"; do
+ for val in ${size[*]}; do
+ if [[ $propname == "recordsize" ]]; then
+ set_and_check $ds $propname $val "recsize"
+ else
+ set_and_check $ds $propname $val "recordsize"
+ fi
+ done
+ done
+done
+
+log_pass "The alias of a property works as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/readonly_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/readonly_001_pos.ksh
new file mode 100644
index 000000000000..7d138b25d3c7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/readonly_001_pos.ksh
@@ -0,0 +1,167 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_set/zfs_set_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: readonly_001_pos
+#
+# DESCRIPTION:
+# Setting readonly on a dataset, it should keep the dataset as readonly.
+#
+# STRATEGY:
+# 1. Create pool, then create filesystem and volume within it.
+# 2. Setting readonly to each dataset.
+# 3. Check the return value and make sure it is 0.
+# 4. Verify the stuff under mountpoint is readonly.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-04-21)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ for dataset in $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL ; do
+ snapexists ${dataset}@$TESTSNAP && \
+ log_must $ZFS destroy -R ${dataset}@$TESTSNAP
+ done
+}
+
+function initial_dataset # $1 dataset
+{
+ typeset dataset=$1
+
+ typeset fstype=$(get_prop type $dataset)
+
+ if [[ $fstype == "filesystem" ]] ; then
+ typeset mtpt=$(get_prop mountpoint $dataset)
+ log_must $TOUCH $mtpt/$TESTFILE0
+ log_must $MKDIR -p $mtpt/$TESTDIR0
+ fi
+}
+
+
+function cleanup_dataset # $1 dataset
+{
+ typeset dataset=$1
+
+ typeset fstype=$(get_prop type $dataset)
+
+ if [[ $fstype == "filesystem" ]] ; then
+ typeset mtpt=$(get_prop mountpoint $dataset)
+ log_must $RM -f $mtpt/$TESTFILE0
+ log_must $RM -rf $mtpt/$TESTDIR0
+ fi
+}
+
+function verify_readonly # $1 dataset, $2 on|off
+{
+ typeset dataset=$1
+ typeset value=$2
+
+ if datasetnonexists $dataset ; then
+ log_note "$dataset not exist!"
+ return 1
+ fi
+
+ typeset fstype=$(get_prop type $dataset)
+
+ expect="log_must"
+
+ if [[ $2 == "on" ]] ; then
+ expect="log_mustnot"
+ fi
+
+ case $fstype in
+ filesystem)
+ typeset mtpt=$(get_prop mountpoint $dataset)
+ $expect $TOUCH $mtpt/$TESTFILE1
+ $expect $MKDIR -p $mtpt/$TESTDIR1
+ $expect $ECHO 'y' | $RM $mtpt/$TESTFILE0
+ $expect $RMDIR $mtpt/$TESTDIR0
+
+ if [[ $expect == "log_must" ]] ; then
+ log_must $ECHO 'y' | $RM $mtpt/$TESTFILE1
+ log_must $RMDIR $mtpt/$TESTDIR1
+ log_must $TOUCH $mtpt/$TESTFILE0
+ log_must $MKDIR -p $mtpt/$TESTDIR0
+ fi
+ ;;
+ volume)
+ $expect eval "$ECHO 'y' | $NEWFS /dev/zvol/$dataset > /dev/null 2>&1"
+ ;;
+ *)
+ ;;
+ esac
+
+ return 0
+}
+
+log_onexit cleanup
+
+log_assert "Setting a valid readonly property on a dataset succeeds."
+
+typeset all_datasets
+
+log_must $ZFS mount -a
+
+log_must $ZFS snapshot $TESTPOOL/$TESTFS@$TESTSNAP
+log_must $ZFS clone $TESTPOOL/$TESTFS@$TESTSNAP $TESTPOOL/$TESTCLONE
+
+if is_global_zone ; then
+ log_must $ZFS snapshot $TESTPOOL/$TESTVOL@$TESTSNAP
+ log_must $ZFS clone $TESTPOOL/$TESTVOL@$TESTSNAP $TESTPOOL/$TESTCLONE1
+ all_datasets="$TESTPOOL $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL $TESTPOOL/$TESTCLONE $TESTPOOL/$TESTCLONE1"
+else
+ all_datasets="$TESTPOOL $TESTPOOL/$TESTFS $TESTPOOL/$TESTCLONE"
+fi
+
+
+for dataset in $all_datasets; do
+ for value in on off; do
+ set_n_check_prop "off" "readonly" "$dataset"
+ initial_dataset $dataset
+
+ set_n_check_prop "$value" "readonly" "$dataset"
+ verify_readonly $dataset $value
+
+ set_n_check_prop "off" "readonly" "$dataset"
+ cleanup_dataset $dataset
+ done
+done
+
+log_pass "Setting a valid readonly property on a dataset succeeds."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/reservation_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/reservation_001_neg.ksh
new file mode 100644
index 000000000000..33816c8145ba
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/reservation_001_neg.ksh
@@ -0,0 +1,111 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/reservation/reservation.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: reservation_001_neg
+#
+# DESCRIPTION:
+# Valid reservation values should be positive integers only.
+#
+# STRATEGY:
+# 1) Form an array of invalid reservation values (negative and
+# incorrectly formed)
+# 2) Attempt to set each invalid reservation value in turn on a
+# filesystem and volume.
+# 3) Verify that attempt fails and the reservation value remains
+# unchanged
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify invalid reservation values are rejected"
+
+set -A suffix "b" "k" "m" "t" "p" "e" "K" "M" "G" "T" "P" "E" "kb" "Mb" "Gb" \
+ "Tb" "Pb" "Eb" "KB" "MB" "GB" "TB" "PB" "EB"
+
+set -A values '' '-1' '-1.0' '-1.8' '-9999999999999999' '0x1' '0b' '1b' '1.1b'
+
+#
+# Function to loop through a series of bad reservation
+# values, checking they are when we attempt to set them
+# on a dataset.
+#
+function set_n_check # data-set
+{
+ typeset obj=$1
+ typeset -i i=0
+ typeset -i j=0
+
+ orig_resv_val=$(get_prop reservation $obj)
+
+ while (( $i < ${#values[*]} )); do
+ j=0
+ while (( $j < ${#suffix[*]} )); do
+
+ $ZFS set \
+ reservation=${values[$i]}${suffix[$j]} $obj \
+ > /dev/null 2>&1
+ if [ $? -eq 0 ]
+ then
+ log_note "$ZFS set \
+ reservation=${values[$i]}${suffix[$j]} $obj"
+ log_fail "The above reservation set returned 0!"
+ fi
+
+ new_resv_val=$(get_prop reservation $obj)
+
+ if [[ $new_resv_val != $orig_resv_val ]]; then
+ log_fail "$obj : reservation values changed " \
+ "($orig_resv_val : $new_resv_val)"
+ fi
+ (( j = j + 1 ))
+ done
+
+ (( i = i + 1 ))
+ done
+}
+
+for dataset in $TESTPOOL/$TESTFS $TESTPOOL/$TESTCTR $TESTPOOL/$TESTVOL
+do
+ set_n_check $dataset
+done
+
+log_pass "Invalid reservation values correctly rejected"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/ro_props_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/ro_props_001_pos.ksh
new file mode 100644
index 000000000000..8bb29e798d23
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/ro_props_001_pos.ksh
@@ -0,0 +1,158 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_set/zfs_set_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: ro_props_001_pos
+#
+# DESCRIPTION:
+# Verify that read-only properties are immutable.
+#
+# STRATEGY:
+# 1. Create pool, fs, vol, fs@snap & vol@snap.
+# 2. Get the original property value and set value to those properties.
+# 3. Check return value.
+# 4. Compare the current property value with the original one.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+set -A values filesystem volume snapshot -3 0 1 50K 10G 80G \
+ 2005/06/17 30K 20x yes no \
+ on off default pool/fs@snap $TESTDIR
+set -A dataset $TESTPOOL $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL \
+ $TESTPOOL/$TESTCTR/$TESTFS1 $TESTPOOL/$TESTFS@$TESTSNAP \
+ $TESTPOOL/$TESTVOL@$TESTSNAP
+
+typeset ro_props="type"
+ro_props="$ro_props creation"
+ro_props="$ro_props compressratio"
+ro_props="$ro_props mounted"
+ro_props="$ro_props origin"
+# Uncomment these once the test ensures they can't be changed.
+#ro_props="$ro_props used"
+#ro_props="$ro_props available"
+#ro_props="$ro_props avail"
+#ro_props="$ro_props referenced"
+#ro_props="$ro_props refer"
+
+typeset snap_ro_props="volsize"
+snap_ro_props="$snap_ro_props recordsize"
+snap_ro_props="$snap_ro_props recsize"
+snap_ro_props="$snap_ro_props quota"
+snap_ro_props="$snap_ro_props reservation"
+snap_ro_props="$snap_ro_props reserv"
+snap_ro_props="$snap_ro_props mountpoint"
+snap_ro_props="$snap_ro_props sharenfs"
+snap_ro_props="$snap_ro_props checksum"
+snap_ro_props="$snap_ro_props compression"
+snap_ro_props="$snap_ro_props compress"
+snap_ro_props="$snap_ro_props atime"
+snap_ro_props="$snap_ro_props devices"
+snap_ro_props="$snap_ro_props exec"
+snap_ro_props="$snap_ro_props readonly"
+snap_ro_props="$snap_ro_props rdonly"
+snap_ro_props="$snap_ro_props setuid"
+
+$ZFS upgrade -v > /dev/null 2>&1
+if [[ $? -eq 0 ]]; then
+ snap_ro_props="$snap_ro_props version"
+fi
+
+function cleanup
+{
+ poolexists $TESTPOOL && log_must $ZPOOL history $TESTPOOL
+ datasetexists $TESTPOOL/$TESTVOL@$TESTSNAP && \
+ destroy_snapshot $TESTPOOL/$TESTVOL@$TESTSNAP
+ datasetexists $TESTPOOL/$TESTFS@$TESTSNAP && \
+ destroy_snapshot $TESTPOOL/$TESTFS@$TESTSNAP
+}
+
+log_assert "Verify that read-only properties are immutable."
+log_onexit cleanup
+
+# Create filesystem and volume's snapshot
+create_snapshot $TESTPOOL/$TESTFS $TESTSNAP
+create_snapshot $TESTPOOL/$TESTVOL $TESTSNAP
+
+# Make sure any history logs have been synced. They're asynchronously
+# pushed to the syncing context, and could influence the value of some
+# properties on $TESTPOOL, like 'used'. Fetching it here forces the sync,
+# per spa_history.c:spa_history_get().
+log_must $ZPOOL history $TESTPOOL
+
+typeset -i i=0
+typeset -i j=0
+typeset cur_value=""
+typeset props=""
+
+while (( i < ${#dataset[@]} )); do
+ props=$ro_props
+
+ dst_type=$(get_prop type ${dataset[i]})
+ if [[ $dst_type == 'snapshot' ]]; then
+ props="$ro_props $snap_ro_props"
+ fi
+
+ for prop in $props; do
+ cur_value=$(get_prop $prop ${dataset[i]})
+
+ j=0
+ while (( j < ${#values[@]} )); do
+ #
+ # If the current property value is equal to values[j],
+ # just expect it failed. Otherwise, set it to dataset,
+ # expecting it failed and the property value is not
+ # equal to values[j].
+ #
+ if [[ $cur_value == ${values[j]} ]]; then
+ log_mustnot $ZFS set $prop=${values[j]} \
+ ${dataset[i]}
+ else
+ set_n_check_prop ${values[j]} $prop \
+ ${dataset[i]} false
+ fi
+ (( j += 1 ))
+ done
+ done
+ (( i += 1 ))
+done
+
+log_pass "Setting uneditable properties fail, as required."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/setup.ksh
new file mode 100644
index 000000000000..f899a97c7947
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/setup.ksh
@@ -0,0 +1,30 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+default_container_volume_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/share_mount_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/share_mount_001_neg.ksh
new file mode 100644
index 000000000000..152ccb2d5d0d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/share_mount_001_neg.ksh
@@ -0,0 +1,73 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_set/zfs_set_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: share_mount_001_neg
+#
+# DESCRIPTION:
+# Verify that we cannot share or mount legacy filesystems.
+#
+# STRATEGY:
+# 1. Set mountpoint as legacy or none
+# 2. Use zfs share or zfs mount to share or mount the filesystem
+# 3. Verify that the command returns error
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-9)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ log_must $ZFS set mountpoint=$oldmpt $fs
+}
+
+log_assert "Verify that we cannot share or mount legacy filesystems."
+log_onexit cleanup
+
+fs=$TESTPOOL/$TESTFS
+oldmpt=$(get_prop mountpoint $fs)
+
+for propval in "legacy" "none"; do
+ log_must $ZFS set mountpoint=$propval $fs
+
+ log_mustnot $ZFS mount $fs
+ log_mustnot $ZFS share $fs
+done
+
+log_pass "We cannot share or mount legacy filesystems as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/snapdir_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/snapdir_001_pos.ksh
new file mode 100644
index 000000000000..c183a0c222ee
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/snapdir_001_pos.ksh
@@ -0,0 +1,123 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_set/zfs_set_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: snapdir_001_pos
+#
+# DESCRIPTION:
+# Setting a valid snapdir on a dataset, it should be successful.
+#
+# STRATEGY:
+# 1. Create pool, then create filesystem and volume within it.
+# 2. Create a snapshot for each dataset.
+# 3. Setting different valid snapdir to each dataset.
+# 4. Check the return value and make sure it is 0.
+# 5. Verify .zfs directory is hidden|visible according to the snapdir setting.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-02-17)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ for dataset in $all_datasets; do
+ snapexists ${dataset}@snap && \
+ log_must $ZFS destroy ${dataset}@snap
+ done
+}
+
+function verify_snapdir_visible # $1 dataset, $2 hidden|visible
+{
+ typeset dataset=$1
+ typeset value=$2
+ typeset mtpt=$(get_prop mountpoint $dataset)
+ typeset name
+
+ CTLDIR=".zfs"
+
+ for name in `$LS -a $mtpt`; do
+ if [[ $name == $CTLDIR ]]; then
+ if [[ $value == "visible" ]]; then
+ return 0
+ else
+ return 1
+ fi
+ fi
+ done
+
+ if [[ $value == "visible" ]]; then
+ return 1
+ else
+ return 0
+ fi
+}
+
+
+typeset all_datasets
+
+if is_global_zone ; then
+ all_datasets="$TESTPOOL $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL"
+else
+ all_datasets="$TESTPOOL $TESTPOOL/$TESTFS"
+fi
+
+log_onexit cleanup
+
+for dataset in $all_datasets; do
+ log_must $ZFS snapshot ${dataset}@snap
+done
+
+log_assert "Setting a valid snapdir property on a dataset succeeds."
+
+for dataset in $all_datasets; do
+ for value in hidden visible; do
+ if [[ $dataset == "$TESTPOOL/$TESTVOL" ]] ; then
+ set_n_check_prop "$value" "snapdir" \
+ "$dataset" "false"
+ else
+ set_n_check_prop "$value" "snapdir" \
+ "$dataset"
+ verify_snapdir_visible $dataset $value
+ [[ $? -eq 0 ]] || \
+ log_fail "$dataset/.zfs is not $value as expect."
+ fi
+ done
+done
+
+log_pass "Setting a valid snapdir property on a dataset succeeds."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/user_property_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/user_property_001_pos.ksh
new file mode 100644
index 000000000000..bdaa9dced6f2
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/user_property_001_pos.ksh
@@ -0,0 +1,78 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_set/zfs_set_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: user_property_001_pos
+#
+# DESCRIPTION:
+# ZFS can set any valid user defined property to the non-readonly dataset.
+#
+# STRATEGY:
+# 1. Loop pool, fs and volume.
+# 2. Combine all kind of valid characters into a valid user defined
+# property name.
+# 3. Random get a string as the value.
+# 4. Verify all the valid user defined properties can be set to the
+# dataset in #1.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-08-31)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "ZFS can set any valid user defined property to the non-readonly " \
+ "dataset."
+log_onexit cleanup_user_prop $TESTPOOL
+
+typeset -i i=0
+while ((i < 10)); do
+ typeset -i len
+ ((len = RANDOM % 32))
+ typeset user_prop=$(valid_user_property $len)
+ ((len = RANDOM % 512))
+ typeset value=$(user_property_value $len)
+
+ for dtst in $TESTPOOL $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL; do
+ log_must eval "$ZFS set $user_prop='$value' $dtst"
+ log_must eval "check_user_prop $dtst $user_prop '$value'"
+ done
+
+ ((i += 1))
+done
+
+log_pass "ZFS can set any valid user defined property passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/user_property_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/user_property_002_pos.ksh
new file mode 100644
index 000000000000..120ee27c3f06
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/user_property_002_pos.ksh
@@ -0,0 +1,131 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_set/zfs_set_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: user_property_002_pos
+#
+# DESCRIPTION:
+# User defined property are always inherited from its parent dataset
+# directly.
+#
+# STRATEGY:
+# 1. Create pool, fs, volume, fsclone & volclone.
+# 2. Get random user property name and set to the pool
+# 3. Verify all dataset user property inherit from pool.
+# 4. Set intermediate dataset and verify its children will inherit user
+# property from it directly.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-09-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ datasetexists $new_vol && log_must $ZFS rename $new_vol $vol
+
+ typeset dtst
+ for dtst in $new_fsclone $new_volclone $fsclone $volclone \
+ $fssnap $volsnap; do
+ if datasetexists $dtst; then
+ log_must $ZFS destroy -f $dtst
+ fi
+ done
+
+ cleanup_user_prop $pool
+}
+
+#
+# Verify options datasets (3-n) inherit from the inherited dataset $2.
+#
+# $1 user property
+# $2 inherited dataset
+# $3-n datasets
+#
+function inherit_check
+{
+ typeset prop=$1
+ typeset inherited_dtst=$2
+ shift 2
+ [[ -z $@ ]] && return 1
+
+ typeset inherited_value=$(get_prop $prop $inherited_dtst)
+ for dtst in $@; do
+ typeset value=$(get_prop $prop $dtst)
+ typeset source=$(get_source $prop $dtst)
+ if [[ "$value" != "$inherited_value" || \
+ "$source" != "inherited from $inherited_dtst" ]]
+ then
+ return 1
+ fi
+
+ shift
+ done
+
+ return 0
+}
+
+log_assert "User defined property inherited from its parent."
+log_onexit cleanup
+
+pool=$TESTPOOL; fs=$pool/$TESTFS; vol=$pool/$TESTVOL
+fssnap=$fs@snap; volsnap=$vol@snap;
+log_must $ZFS snapshot $fssnap
+log_must $ZFS snapshot $volsnap
+fsclone=$pool/fsclone; volclone=$pool/volclone
+log_must $ZFS clone $fssnap $fsclone
+log_must $ZFS clone $volsnap $volclone
+
+prop_name=$(valid_user_property 10)
+value=$(user_property_value 16)
+log_must eval "$ZFS set $prop_name='$value' $pool"
+log_must eval "check_user_prop $pool $prop_name '$value'"
+log_must inherit_check $prop_name $pool $fs $vol $fsclone $volclone
+
+new_fsclone=$fs/fsclone; new_volclone=$fs/volclone
+log_must $ZFS rename $fsclone $new_fsclone
+log_must $ZFS rename $volclone $new_volclone
+log_must inherit_check $prop_name $pool $fs $new_fsclone $new_volclone
+
+log_note "Set intermediate dataset will change the inherited relationship."
+new_value=$(user_property_value 16)
+log_must eval "$ZFS set $prop_name='$new_value' $fs"
+log_must eval "check_user_prop $fs $prop_name '$new_value'"
+log_must inherit_check $prop_name $fs $new_fsclone $new_volclone
+
+log_pass "User defined property inherited from its parent passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/user_property_003_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/user_property_003_neg.ksh
new file mode 100644
index 000000000000..9668c4e2268b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/user_property_003_neg.ksh
@@ -0,0 +1,76 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_set/zfs_set_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: user_property_003_neg
+#
+# DESCRIPTION:
+# ZFS can handle any invalid user defined property.
+#
+# STRATEGY:
+# 1. Loop pool, fs and volume.
+# 2. Combine all kind of invalid user property names.
+# 3. Random get a string as the value.
+# 4. Verify all the invalid user defined properties can not be set to the
+# dataset in #1.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-09-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "ZFS can handle invalid user property."
+log_onexit cleanup_user_prop $TESTPOOL
+
+typeset -i i=0
+while ((i < 10)); do
+ typeset -i len
+ ((len = RANDOM % 32))
+ typeset user_prop=$(invalid_user_property $len)
+ ((len = RANDOM % 512))
+ typeset value=$(user_property_value $len)
+
+ for dtst in $TESTPOOL $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL ; do
+ log_mustnot $ZFS set $user_prop=$value $dtst
+ log_mustnot check_user_prop $dtst \"$user_prop\" \"$value\"
+ done
+
+ ((i += 1))
+done
+
+log_pass "ZFS can handle invalid user property passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/user_property_004_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/user_property_004_pos.ksh
new file mode 100644
index 000000000000..da5142544aa5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/user_property_004_pos.ksh
@@ -0,0 +1,110 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_set/zfs_set_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: user_property_004_neg
+#
+# DESCRIPTION:
+# User property has no effect to snapshot until 'Snapshot properties' supported.
+#
+# STRATEGY:
+# 1. Verify user properties could be transformed by 'zfs snapshot'
+# 2. Verify user properties could be set upon snapshot.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-09-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ for fs in $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL $TESTPOOL ; do
+ typeset fssnap=$fs@snap
+ if datasetexists $fssnap ; then
+ log_must $ZFS destroy -f $fssnap
+ fi
+ done
+ cleanup_user_prop $TESTPOOL
+}
+
+function nonexist_user_prop
+{
+ typeset user_prop=$1
+ typeset dtst=$2
+
+ typeset source=$(get_source $user_prop $dtst)
+ typeset value=$(get_prop $user_prop $dtst)
+ if [[ $source == '-' && $value == '-' ]]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+log_assert "User property has no effect to snapshot until 'Snapshot properties' supported."
+log_onexit cleanup
+
+typeset snap_property=
+
+$ZPOOL upgrade -v | $GREP "Snapshot properties" > /dev/null 2>&1
+if (( $? == 0 )) ; then
+ snap_property="true"
+fi
+
+for fs in $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL $TESTPOOL ; do
+ typeset fssnap=$fs@snap
+ prop_name=$(valid_user_property 10)
+ value=$(user_property_value 16)
+ log_must eval "$ZFS set $prop_name='$value' $fs"
+ log_must eval "check_user_prop $fs $prop_name '$value'"
+
+ log_must $ZFS snapshot $fssnap
+
+ if [[ -n $snap_property ]] ; then
+ log_mustnot nonexist_user_prop $prop_name $fssnap
+
+ log_must eval "$ZFS set $prop_name='$value' $fssnap"
+ log_mustnot nonexist_user_prop $prop_name $fssnap
+ else
+ log_must nonexist_user_prop $prop_name $fssnap
+ log_mustnot eval "$ZFS set $prop_name='$value' $fssnap"
+ log_must nonexist_user_prop $prop_name $fssnap
+ fi
+done
+
+log_pass "User properties has effect upon snapshot."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/version_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/version_001_neg.ksh
new file mode 100644
index 000000000000..812cd653a212
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/version_001_neg.ksh
@@ -0,0 +1,104 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_upgrade/zfs_upgrade.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: version_001_neg
+#
+# DESCRIPTION:
+# Valid version values should be positive integers only.
+#
+# STRATEGY:
+# 1) Form an array of invalid reservation values (negative and
+# incorrectly formed)
+# 2) Attempt to set each invalid version value in turn on a
+# filesystem and volume.
+# 3) Verify that attempt fails and the version value remains
+# unchanged
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+if ! fs_prop_exist "version" ; then
+ log_unsupported "version is not supported by this release."
+fi
+
+log_assert "Verify invalid version values are rejected"
+
+set -A values '' '-1' '-1.0' '-1.8' '-9999999999999999' \
+ '0x1' '0b' '1b' '1.1b' '0' '0.000' '1.234'
+
+#
+# Function to loop through a series of bad reservation
+# values, checking they are when we attempt to set them
+# on a dataset.
+#
+function set_n_check # data-set
+{
+ typeset obj=$1
+ typeset -i i=0
+ typeset -i j=0
+
+ orig_val=$(get_prop version $obj)
+
+ while (( $i < ${#values[*]} )); do
+ $ZFS set version=${values[$i]} $obj > /dev/null 2>&1
+ if [[ $? -eq 0 ]]; then
+ log_note "$ZFS set version=${values[$i]} $obj"
+ log_fail "The above version set returned 0!"
+ fi
+
+ new_val=$(get_prop version $obj)
+
+ if [[ $new_val != $orig_val ]]; then
+ log_fail "$obj : version values changed " \
+ "($orig_val : $new_val)"
+ fi
+
+ (( i = i + 1 ))
+ done
+}
+
+for dataset in $TESTPOOL/$TESTFS $TESTPOOL/$TESTCTR $TESTPOOL/$TESTVOL
+do
+ set_n_check $dataset
+done
+
+log_pass "Invalid version values correctly rejected"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/zfs_set.cfg b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/zfs_set.cfg
new file mode 100644
index 000000000000..ea99dda7bf1b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/zfs_set.cfg
@@ -0,0 +1,30 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/cli_root/cli.cfg
+
+export TESTDIR_NOTEXISTING=${TEST_BASE_DIR%%/}/testdir_notexisting${TESTCASE_ID}
+
+export STF_TIMEOUT=1200 #ro_prop_001_pos need more time
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/zfs_set_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/zfs_set_001_neg.ksh
new file mode 100644
index 000000000000..3ee332a49d85
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/zfs_set_001_neg.ksh
@@ -0,0 +1,88 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_set/zfs_set_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_set_001_neg
+#
+# DESCRIPTION:
+# Setting invalid value to mountpoint, checksum, atime, readonly, setuid,
+# or canmount on a file system, volume. It should be failed.
+#
+# STRATEGY:
+# 1. Create pool, then create file system & volume within it.
+# 2. Setting invalid value, it should be failed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+set -A props "" "mountpoint" "checksum" "compression" "atime" "readonly" \
+ "setuid" "canmount"
+
+set -A values "" "mountpoint" "checksum" "compression" "atime" "readonly" \
+ "setuid" "0" "-?" "-on" "--on" "*" "?" "Legacy" "NONE" "oN" \
+ "On" "ON" "ofF" "OFf" "oFF" "Off" "OfF" "OFF" "LzJb" "lZJb" "LZjB" \
+ "blad" "default" "TESTPOOL" "$TESTPOOL/$TESTCTR" \
+ "$TESTPOOL/$TESTCTR/$TESTFS" "$TESTPOOL/$TESTFS"
+set -A dataset "$TESTPOOL" "$TESTPOOL/$TESTFS" "$TESTPOOL/$TESTVOL"
+
+log_assert "Setting invalid value to mountpoint, checksum, compression, atime,"\
+ "readonly, setuid, or canmount on a file system file system or volume." \
+ "It should be failed."
+
+typeset -i i=0
+typeset -i j=0
+typeset -i k=0
+while (( i < ${#dataset[@]} )); do
+ j=0
+ while (( j < ${#props[@]} )); do
+ k=0
+ while (( k < ${#values[@]} )); do
+ set_n_check_prop "${values[k]}" "${props[j]}" \
+ "${dataset[i]}" false
+ (( k += 1 ))
+ done
+ (( j += 1 ))
+ done
+ (( i += 1 ))
+done
+
+log_pass "Setting invalid value to mountpoint, checksum, compression, atime, " \
+ "readonly, setuid, or canmount on file system or volume pass."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/zfs_set_002_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/zfs_set_002_neg.ksh
new file mode 100644
index 000000000000..2260381646af
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/zfs_set_002_neg.ksh
@@ -0,0 +1,71 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_set/zfs_set_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_set_002_neg
+#
+# DESCRIPTION:
+# 'zfs set' should fail with invalid arguments
+#
+# STRATEGY:
+# 1. Create an array of invalid arguments
+# 1. Run zfs set with each invalid argument
+# 2. Verify that zfs set returns error
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-9)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "'zfs set' fails with invalid arguments"
+
+set -A editable_props "quota" "reservation" "reserv" "volsize" "recordsize" "recsize" \
+ "mountpoint" "checksum" "compression" "compress" "atime" \
+ "devices" "exec" "setuid" "readonly" "zoned" "snapdir" "aclmode" \
+ "aclinherit" "canmount" "shareiscsi" "xattr" "copies" "version"
+
+for ds in $TESTPOOL $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL \
+ $TESTPOOL/$TESTFS@$TESTSNAP; do
+ for badarg in "" "-" "-?"; do
+ for prop in ${editable_props[@]}; do
+ log_mustnot eval "$ZFS set $badarg $prop= $ds >/dev/null 2>&1"
+ done
+ done
+done
+
+log_pass "'zfs set' fails with invalid arguments as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/zfs_set_003_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/zfs_set_003_neg.ksh
new file mode 100644
index 000000000000..6df3d4c925cc
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/zfs_set_003_neg.ksh
@@ -0,0 +1,86 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_set/zfs_set_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_set_003_neg
+#
+# DESCRIPTION:
+# 'zfs set mountpoint/sharenfs' should fail when the mountpoint is invlid
+#
+# STRATEGY:
+# 1. Create invalid scenarios
+# 2. Run zfs set mountpoint/sharenfs with invalid value
+# 3. Verify that zfs set returns expected errors
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-9)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if [ -e $badpath ]; then
+ $RM -f $badpath
+ fi
+ if datasetexists $TESTPOOL/foo; then
+ log_must $ZFS destroy $TESTPOOL/foo
+ fi
+}
+
+log_assert "'zfs set mountpoint/sharenfs' fails with invalid scenarios"
+log_onexit cleanup
+
+badpath=$TMPDIR/foo1.${TESTCASE_ID}
+$TOUCH $badpath
+longpath=$(gen_dataset_name 1030 "abcdefg")
+
+log_must $ZFS create -o mountpoint=legacy $TESTPOOL/foo
+
+# Do the negative testing about "property may be set but unable to remount filesystem"
+log_mustnot eval "$ZFS set mountpoint=$badpath $TESTPOOL/foo >/dev/null 2>&1"
+
+# Do the negative testing about "property may be set but unable to reshare filesystem"
+log_mustnot eval "$ZFS set sharenfs=on $TESTPOOL/foo >/dev/null 2>&1"
+
+# Do the negative testing about "sharenfs property can not be set to null"
+log_mustnot eval "$ZFS set sharenfs= $TESTPOOL/foo >/dev/null 2>&1"
+
+# Do the too long pathname testing (>1024)
+log_mustnot eval "$ZFS set mountpoint=/$longpath $TESTPOOL/foo >/dev/null 2>&1"
+
+log_pass "'zfs set mountpoint/sharenfs' fails with invalid scenarios as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/zfs_set_common.kshlib b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/zfs_set_common.kshlib
new file mode 100644
index 000000000000..369eee3bb2c1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/zfs_set_common.kshlib
@@ -0,0 +1,248 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+set -A VALID_NAME_CHAR a b c d e f g h i j k l m n o p q r s t u v w x y z \
+ 0 1 2 3 4 5 6 7 8 9 ':' '-' '.' '_'
+set -A INVALID_NAME_CHAR A B C D E F G H I J K L M N O P Q R S T U V W X Y Z \
+ '`' '~' '!' '@' '#' '$' '%' '^' '&' '(' ')' '+' '=' \
+ '|' '{' '[' ']' '}' ';' '"' '<' ',' '>' '?' '/' \
+ ' '
+set -A ALL_CHAR ${VALID_NAME_CHAR[*]} ${INVALID_NAME_CHAR[*]}
+
+#
+# Firstly, set the property value to dataset. Then checking if the property
+# value is equal with the expected value, according to the expected result.
+#
+# $1 property value
+# $2 property name
+# $3 dataset
+# $4 expected result
+#
+function set_n_check_prop
+{
+ typeset expect_value=$1
+ typeset prop=$2
+ typeset dataset=$3
+ typeset expect_result=${4:-true}
+ typeset old_value=""
+ typeset cur_value=""
+
+ [ -n "$prop" ] && old_value=$(get_prop $prop $dataset)
+ if [ "$expect_result" = "true" ]; then
+ log_must $ZFS set $prop=$expect_value $dataset
+ else
+ log_mustnot $ZFS set $prop=$expect_value $dataset
+ fi
+ [ -n "$prop" ] && cur_value=$(get_prop $prop $dataset)
+
+ err="ERROR: Dataset '$dataset': '$prop' value '$cur_value'"
+ if [ "$expect_result" = "true" ]; then
+ [ "$expect_value" = "gzip-6" ] && expect_value="gzip"
+ case "$prop" in
+ reservation|reserv|quota)
+ if [ "$expect_value" = "none" -a "$cur_value" != "0" ]; then
+ err="$err should not be set!"
+ log_fail "$err"
+ return
+ fi
+ ;;
+ esac
+ if [ "$cur_value" != "$expect_value" ]; then
+ err="$err should have changed to '$expect_value'!"
+ log_fail "$err"
+ fi
+ else
+ if [ "$expect_value" != "" -a "$cur_value" != "$old_value" ]; then
+ err="$err should be unchanged at '$old_value'!"
+ log_fail "$err"
+ fi
+ fi
+}
+
+#
+# Cleanup all the user properties of the pool and the dataset reside it.
+#
+# $1 pool name
+#
+function cleanup_user_prop
+{
+ typeset pool=$1
+ typeset dtst=$($ZFS list -H -r -o name -t filesystem,volume $pool)
+
+ typeset user_prop
+ for dt in $dtst; do
+ user_prop=$($ZFS get -H -o property all $dtst | grep ":")
+
+ typeset prop
+ for prop in $user_prop; do
+ $ZFS inherit $prop $dt
+ (($? != 0)) && log_must $ZFS inherit $prop $dt
+ done
+ done
+}
+
+#
+# Random select charactor from the specified charactor set and combine into a
+# random string
+#
+# $1 character set name
+# $2 String length
+#
+function random_string
+{
+ typeset char_set=${1:-VALID_NAME_CHAR}
+ typeset -i len=${2:-5}
+
+ eval typeset -i count=\${#$char_set[@]}
+
+ typeset str
+ typeset -i i=0
+ while ((i < len)); do
+ typeset -i ind
+ ((ind = RANDOM % count))
+ eval str=\${str}\${$char_set[\$ind]}
+
+ ((i += 1))
+ done
+
+ $ECHO "$str"
+}
+
+#
+# Get vaild user defined property name
+#
+# $1 user defined property name length
+#
+function valid_user_property
+{
+ typeset -i sumlen=${1:-10}
+ ((sumlen < 2 )) && sumlen=2
+ typeset -i len
+ ((len = RANDOM % sumlen))
+ typeset part1 part2
+
+ while true; do
+ part1="$(random_string VALID_NAME_CHAR $len)"
+ if [[ "$part1" == "-"* ]]; then
+ continue
+ fi
+ break
+ done
+ ((len = sumlen - (len + 1)))
+
+ while true; do
+ part2="$(random_string VALID_NAME_CHAR $len)"
+ if [[ -z $part1 && -z $part2 ]]; then
+ continue
+ fi
+ break
+ done
+
+ $ECHO "${part1}:${part2}"
+}
+
+#
+# Get invaild user defined property name
+#
+# $1 user defined property name length
+#
+function invalid_user_property
+{
+ typeset -i sumlen=${1:-10}
+ ((sumlen == 0)) && sumlen=1
+ typeset -i len
+ ((len = RANDOM % sumlen))
+
+ typeset part1 part2
+ while true; do
+ part1="$(random_string VALID_NAME_CHAR $len)"
+ ((len = sumlen - len))
+ part2="$(random_string INVALID_NAME_CHAR $len)"
+
+ # Avoid $part1 is *:* and $part2 is "=*"
+ if [[ "$part1" == *":"* && "$part2" == "="* ]]; then
+ continue
+ fi
+ break
+ done
+
+ $ECHO "${part1}${part2}"
+}
+
+#
+# Get user property value
+#
+# $1 user defined property name length
+#
+function user_property_value
+{
+ typeset -i len=${1:-100}
+ ((len < 1 )) && len=1
+
+ typeset value=$(random_string ALL_CHAR $len)
+
+ $ECHO "$value"
+}
+
+#
+# Check if the user property is identical to the expected value.
+#
+# $1 dataset
+# $2 user property
+# $3 expected value
+#
+function check_user_prop
+{
+ typeset dtst=$1
+ typeset user_prop="$2"
+ typeset expect_value="$3"
+ typeset value=$($ZFS get -p -H -o value "$user_prop" $dtst 2>&1)
+
+ if [[ "$expect_value" == "$value" ]]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+#
+# Get source of the dataset
+#
+function get_source
+{
+ typeset prop=$1
+ typeset dataset=$2
+ typeset source
+
+ source=$($ZFS get -H -o source $prop $dataset)
+ if (($? != 0)); then
+ log_fail "Unable to get $prop source for dataset $dataset"
+ fi
+
+ $ECHO "$source"
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_set/zfs_set_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/zfs_set_test.sh
new file mode 100755
index 000000000000..89d715fbe7ea
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_set/zfs_set_test.sh
@@ -0,0 +1,763 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case cache_001_pos cleanup
+cache_001_pos_head()
+{
+ atf_set "descr" "Setting a valid {primary|secondary}cache on file system and volume,It should be successful."
+ atf_set "timeout" 1200
+}
+cache_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/cache_001_pos.ksh || atf_fail "Testcase failed"
+}
+cache_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case cache_002_neg cleanup
+cache_002_neg_head()
+{
+ atf_set "descr" "Setting invalid {primary|secondary}cache on fs and volume,It should fail."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1200
+}
+cache_002_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/cache_002_neg.ksh || atf_fail "Testcase failed"
+}
+cache_002_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case canmount_001_pos cleanup
+canmount_001_pos_head()
+{
+ atf_set "descr" "Setting a valid property of canmount to file system, it must be successful."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1200
+}
+canmount_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/canmount_001_pos.ksh || atf_fail "Testcase failed"
+}
+canmount_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case canmount_002_pos cleanup
+canmount_002_pos_head()
+{
+ atf_set "descr" "Setting canmount=noauto to file system, it must be successful."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1200
+}
+canmount_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ if other_pools_exist; then
+ atf_skip "Can't test unmount -a with existing pools"
+ fi
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/canmount_002_pos.ksh || atf_fail "Testcase failed"
+}
+canmount_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case canmount_003_pos cleanup
+canmount_003_pos_head()
+{
+ atf_set "descr" "While canmount=noauto and the dataset is mounted, zfs must not attempt to unmount it"
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1200
+}
+canmount_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ if other_pools_exist; then
+ atf_skip "Can't test unmount -a with existing pools"
+ fi
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/canmount_003_pos.ksh || atf_fail "Testcase failed"
+}
+canmount_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case canmount_004_pos cleanup
+canmount_004_pos_head()
+{
+ atf_set "descr" "Verify canmount=noauto work fine when setting sharenfs or sharesmb."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1200
+}
+canmount_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/canmount_004_pos.ksh || atf_fail "Testcase failed"
+}
+canmount_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case checksum_001_pos cleanup
+checksum_001_pos_head()
+{
+ atf_set "descr" "Setting a valid checksum on a file system, volume,it should be successful."
+ atf_set "timeout" 1200
+}
+checksum_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/checksum_001_pos.ksh || atf_fail "Testcase failed"
+}
+checksum_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case compression_001_pos cleanup
+compression_001_pos_head()
+{
+ atf_set "descr" "Setting a valid compression on file system and volume,It should be successful."
+ atf_set "timeout" 1200
+}
+compression_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/compression_001_pos.ksh || atf_fail "Testcase failed"
+}
+compression_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case mountpoint_001_pos cleanup
+mountpoint_001_pos_head()
+{
+ atf_set "descr" "Setting a valid mountpoint to file system, it must be successful."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1200
+}
+mountpoint_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/mountpoint_001_pos.ksh || atf_fail "Testcase failed"
+}
+mountpoint_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case mountpoint_002_pos cleanup
+mountpoint_002_pos_head()
+{
+ atf_set "descr" "Setting a valid mountpoint for an unmounted file system,it remains unmounted."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1200
+}
+mountpoint_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/mountpoint_002_pos.ksh || atf_fail "Testcase failed"
+}
+mountpoint_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case mountpoint_003_pos cleanup
+mountpoint_003_pos_head()
+{
+ atf_set "descr" "With legacy mount, FSType-specific option works well."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1200
+}
+mountpoint_003_pos_body()
+{
+ atf_expect_fail "The devices property is not yet supported on FreeBSD"
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/mountpoint_003_pos.ksh || atf_fail "Testcase failed"
+}
+mountpoint_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case onoffs_001_pos cleanup
+onoffs_001_pos_head()
+{
+ atf_set "descr" "Setting a valid value to atime, readonly, setuid or zoned on filesystem or volume. It should be successful."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1200
+}
+onoffs_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/onoffs_001_pos.ksh || atf_fail "Testcase failed"
+}
+onoffs_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case property_alias_001_pos cleanup
+property_alias_001_pos_head()
+{
+ atf_set "descr" "Properties with aliases also work with those aliases."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1200
+}
+property_alias_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/property_alias_001_pos.ksh || atf_fail "Testcase failed"
+}
+property_alias_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case readonly_001_pos cleanup
+readonly_001_pos_head()
+{
+ atf_set "descr" "Setting a valid readonly property on a dataset succeeds."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1200
+}
+readonly_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/readonly_001_pos.ksh || atf_fail "Testcase failed"
+}
+readonly_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case reservation_001_neg cleanup
+reservation_001_neg_head()
+{
+ atf_set "descr" "Verify invalid reservation values are rejected"
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1200
+}
+reservation_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/reservation_001_neg.ksh || atf_fail "Testcase failed"
+}
+reservation_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case ro_props_001_pos cleanup
+ro_props_001_pos_head()
+{
+ atf_set "descr" "Verify that read-only properties are immutable."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1200
+}
+ro_props_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/ro_props_001_pos.ksh || atf_fail "Testcase failed"
+}
+ro_props_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case share_mount_001_neg cleanup
+share_mount_001_neg_head()
+{
+ atf_set "descr" "Verify that we cannot share or mount legacy filesystems."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1200
+}
+share_mount_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/share_mount_001_neg.ksh || atf_fail "Testcase failed"
+}
+share_mount_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case snapdir_001_pos cleanup
+snapdir_001_pos_head()
+{
+ atf_set "descr" "Setting a valid snapdir property on a dataset succeeds."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1200
+}
+snapdir_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/snapdir_001_pos.ksh || atf_fail "Testcase failed"
+}
+snapdir_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case user_property_001_pos cleanup
+user_property_001_pos_head()
+{
+ atf_set "descr" "ZFS can set any valid user defined property to the non-readonlydataset."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1200
+}
+user_property_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/user_property_001_pos.ksh || atf_fail "Testcase failed"
+}
+user_property_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case user_property_002_pos cleanup
+user_property_002_pos_head()
+{
+ atf_set "descr" "User defined property inherited from its parent."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1200
+}
+user_property_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/user_property_002_pos.ksh || atf_fail "Testcase failed"
+}
+user_property_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case user_property_003_neg cleanup
+user_property_003_neg_head()
+{
+ atf_set "descr" "ZFS can handle invalid user property."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1200
+}
+user_property_003_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/user_property_003_neg.ksh || atf_fail "Testcase failed"
+}
+user_property_003_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case user_property_004_pos cleanup
+user_property_004_pos_head()
+{
+ atf_set "descr" "User property has no effect to snapshot until 'Snapshot properties' supported."
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 1200
+}
+user_property_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/user_property_004_pos.ksh || atf_fail "Testcase failed"
+}
+user_property_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case version_001_neg cleanup
+version_001_neg_head()
+{
+ atf_set "descr" "Verify invalid version values are rejected"
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1200
+}
+version_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/version_001_neg.ksh || atf_fail "Testcase failed"
+}
+version_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_set_001_neg cleanup
+zfs_set_001_neg_head()
+{
+ atf_set "descr" "Setting invalid value to mountpoint, checksum, compression, atime,readonly, setuid, zoned or canmount on a file system file system or volume. \It should be failed."
+ atf_set "timeout" 1200
+}
+zfs_set_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_set_001_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_set_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_set_002_neg cleanup
+zfs_set_002_neg_head()
+{
+ atf_set "descr" "'zfs set' fails with invalid arguments"
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1200
+}
+zfs_set_002_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_set_002_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_set_002_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_set_003_neg cleanup
+zfs_set_003_neg_head()
+{
+ atf_set "descr" "'zfs set mountpoint/sharenfs' fails with invalid scenarios"
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1200
+}
+zfs_set_003_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_set_003_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_set_003_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_set_common.kshlib
+ . $(atf_get_srcdir)/zfs_set.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case cache_001_pos
+ atf_add_test_case cache_002_neg
+ atf_add_test_case canmount_001_pos
+ atf_add_test_case canmount_002_pos
+ atf_add_test_case canmount_003_pos
+ atf_add_test_case canmount_004_pos
+ atf_add_test_case checksum_001_pos
+ atf_add_test_case compression_001_pos
+ atf_add_test_case mountpoint_001_pos
+ atf_add_test_case mountpoint_002_pos
+ atf_add_test_case mountpoint_003_pos
+ atf_add_test_case onoffs_001_pos
+ atf_add_test_case property_alias_001_pos
+ atf_add_test_case readonly_001_pos
+ atf_add_test_case reservation_001_neg
+ atf_add_test_case ro_props_001_pos
+ atf_add_test_case share_mount_001_neg
+ atf_add_test_case snapdir_001_pos
+ atf_add_test_case user_property_001_pos
+ atf_add_test_case user_property_002_pos
+ atf_add_test_case user_property_003_neg
+ atf_add_test_case user_property_004_pos
+ atf_add_test_case version_001_neg
+ atf_add_test_case zfs_set_001_neg
+ atf_add_test_case zfs_set_002_neg
+ atf_add_test_case zfs_set_003_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_share/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/Makefile
new file mode 100644
index 000000000000..1027523ddcd6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/Makefile
@@ -0,0 +1,26 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zfs_share
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zfs_share_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zfs_share_003_pos.ksh
+${PACKAGE}FILES+= zfs_share_006_pos.ksh
+${PACKAGE}FILES+= zfs_share_002_pos.ksh
+${PACKAGE}FILES+= zfs_share_007_neg.ksh
+${PACKAGE}FILES+= zfs_share.cfg
+${PACKAGE}FILES+= zfs_share_004_pos.ksh
+${PACKAGE}FILES+= zfs_share_009_neg.ksh
+${PACKAGE}FILES+= zfs_share_010_neg.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zfs_share_009_pos.ksh
+${PACKAGE}FILES+= zfs_share_005_pos.ksh
+${PACKAGE}FILES+= zfs_share_001_pos.ksh
+${PACKAGE}FILES+= zfs_share_008_neg.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_share/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/cleanup.ksh
new file mode 100644
index 000000000000..713cb16bb31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_share/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/setup.ksh
new file mode 100644
index 000000000000..51f94b82f6af
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/setup.ksh
@@ -0,0 +1,33 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+# Make sure NFS server is running before testing.
+setup_nfs_server
+
+DISK=${DISKS%% *}
+default_container_volume_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share.cfg b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share.cfg
new file mode 100644
index 000000000000..57a8f52192bd
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share.cfg
@@ -0,0 +1,30 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
+
+export SNAPROOT="$(get_snapdir_name)"
+export NONEXISTFSNAME="nonexistfs50charslong_0123456789012345678901234567"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_001_pos.ksh
new file mode 100644
index 000000000000..78e5b265792a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_001_pos.ksh
@@ -0,0 +1,158 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_share_001_pos
+#
+# DESCRIPTION:
+# Verify that 'zfs set sharenfs' and 'zfs share' shares a given dataset.
+#
+# STRATEGY:
+# 1. Invoke 'zfs set sharenfs'.
+# 2. Verify that the file system is shared.
+# 3. Invoke 'zfs share'.
+# 4. Verify that the file system is shared.
+# 5. Verify that a shared filesystem cannot be shared again.
+# 6. Verify that share -a succeeds.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+set -A fs \
+ "$TESTDIR" "$TESTPOOL/$TESTFS" \
+ "$TESTDIR1" "$TESTPOOL/$TESTCTR/$TESTFS1" \
+ "$TESTDIR2" "$TESTPOOL/$TESTFS-clone"
+
+function cleanup
+{
+ typeset -i i=0
+ while (( i < ${#fs[*]} )); do
+ log_must $ZFS set sharenfs=off ${fs[((i+1))]}
+ unshare_fs ${fs[i]}
+
+ ((i = i + 2))
+ done
+
+ if mounted $TESTPOOL/$TESTFS-clone; then
+ log_must $ZFS unmount $TESTDIR2
+ fi
+
+ datasetexists $TESTPOOL/$TESTFS-clone && \
+ log_must $ZFS destroy -f $TESTPOOL/$TESTFS-clone
+
+ if snapexists "$TESTPOOL/$TESTFS@snapshot"; then
+ log_must $ZFS destroy -f $TESTPOOL/$TESTFS@snapshot
+ fi
+}
+
+
+#
+# Main test routine.
+#
+# Given a mountpoint and file system this routine will attempt
+# share the mountpoint and then verify it has been shared.
+#
+function test_share # mntp filesystem
+{
+ typeset mntp=$1
+ typeset filesystem=$2
+
+ not_shared $mntp || \
+ log_fail "File system $filesystem is already shared."
+
+ log_must $ZFS set sharenfs=on $filesystem
+ is_shared $mntp || \
+ log_fail "File system $filesystem is not shared (set sharenfs)."
+
+ #
+ # Verify 'zfs share' works as well.
+ #
+ log_must $ZFS unshare $filesystem
+ is_shared $mntp && \
+ log_fail "File system $filesystem is still shared."
+
+ log_must $ZFS share $filesystem
+ is_shared $mntp || \
+ log_fail "file system $filesystem is not shared (zfs share)."
+
+ log_note "Sharing a shared file system fails."
+ log_mustnot $ZFS share $filesystem
+}
+
+log_assert "Verify that 'zfs share' succeeds as root."
+log_onexit cleanup
+
+log_must $ZFS snapshot $TESTPOOL/$TESTFS@snapshot
+log_must $ZFS clone $TESTPOOL/$TESTFS@snapshot $TESTPOOL/$TESTFS-clone
+log_must $ZFS set mountpoint=$TESTDIR2 $TESTPOOL/$TESTFS-clone
+
+typeset -i i=0
+while (( i < ${#fs[*]} )); do
+ test_share ${fs[i]} ${fs[((i + 1))]}
+
+ ((i = i + 2))
+done
+
+log_note "Verify 'zfs share -a' succeeds."
+
+#
+# Unshare each of the file systems.
+#
+i=0
+while (( i < ${#fs[*]} )); do
+ unshare_fs ${fs[i]}
+
+ ((i = i + 2))
+done
+
+#
+# Try a zfs share -a and verify all file systems are shared.
+#
+log_must $ZFS share -a
+
+i=0
+while (( i < ${#fs[*]} )); do
+ is_shared ${fs[i]} || \
+ log_fail "File system ${fs[i]} is not shared (share -a)"
+
+ ((i = i + 2))
+done
+
+log_pass "'$ZFS share [ -a ] <filesystem>' succeeds as root."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_002_pos.ksh
new file mode 100644
index 000000000000..d9256cf9e18f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_002_pos.ksh
@@ -0,0 +1,81 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_share_002_pos
+#
+# DESCRIPTION:
+# Verify that "zfs share" with a non-existent file system fails.
+#
+# STRATEGY:
+# 1. Make sure the NONEXISTFSNAME ZFS file system is not in 'zfs list'.
+# 2. Invoke 'zfs share <file system>'.
+# 3. Verify that share fails
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ typeset fs
+ for fs in $NONEXISTFSNAME $TESTFS ; do
+ log_must unshare_fs $TESTPOOL/$fs
+ done
+}
+
+typeset -i ret=0
+
+log_assert "Verify that "zfs share" with a non-existent file system fails."
+
+log_onexit cleanup
+
+log_mustnot $ZFS list $TESTPOOL/$NONEXISTFSNAME
+
+$ZFS share $TESTPOOL/$NONEXISTFSNAME
+ret=$?
+(( ret == 1)) || \
+ log_fail "'$ZFS share $TESTPOOL/$NONEXISTFSNAME' " \
+ "failed with an unexpected return code of $ret."
+
+log_note "Make sure the file system $TESTPOOL/$NONEXISTFSNAME is unshared"
+not_shared $TESTPOOL/$NONEXISTFSNAME || \
+ log_fail "File system $TESTPOOL/$NONEXISTFSNAME is unexpectedly shared."
+
+log_pass "'$ZFS share' with a non-existent file system fails."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_003_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_003_pos.ksh
new file mode 100644
index 000000000000..94da79cff9c1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_003_pos.ksh
@@ -0,0 +1,119 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_share_003_pos
+#
+# DESCRIPTION:
+# Invoking "zfs share <file system>" with a file system
+# whose sharenfs property is 'off' , will fail with a
+# return code of 1 and issue an error message.
+#
+# STRATEGY:
+# 1. Make sure that the ZFS file system is unshared.
+# 2. Mount the file system using the various combinations
+# - zfs set sharenfs=off <file system>
+# - zfs set sharenfs=none <file system>
+# 3. Verify that share failed with return code of 1.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+set -A fs \
+ "$TESTDIR" "$TESTPOOL/$TESTFS" \
+ "$TESTDIR1" "$TESTPOOL/$TESTCTR/$TESTFS1"
+
+function cleanup
+{
+ typeset -i i=0
+ while (( i < ${#fs[*]} )); do
+ log_must $ZFS inherit -r sharenfs ${fs[((i + 1))]}
+ log_must unshare_fs ${fs[i]}
+
+ ((i = i + 2))
+ done
+}
+
+
+#
+# Main test routine.
+#
+# Given a mountpoint and file system this routine will attempt
+# to share a legacy mountpoint and then verify the share fails as
+# expected.
+#
+function test_legacy_share # mntp filesystem
+{
+ typeset mntp=$1
+ typeset filesystem=$2
+
+ not_shared $mntp || \
+ log_fail "File system $filesystem is already shared."
+
+ if is_global_zone ; then
+ log_must $ZFS set sharenfs=off $filesystem
+ not_shared $mntp || \
+ log_fail "File system $filesystem is still shared (set sharenfs)."
+ fi
+
+ $ZFS share $filesystem
+ ret=$?
+ (( ret == 1)) || \
+ log_fail "'$ZFS share $filesystem' " \
+ "unexpected return code of $ret."
+
+ not_shared $mntp || \
+ log_fail "file system $filesystem is shared (zfs share)."
+}
+
+log_assert "Verify that '$ZFS share' with a file system " \
+ "whose sharenfs property is 'off' " \
+ "will fail with return code 1."
+log_onexit cleanup
+
+typeset -i i=0
+while (( i < ${#fs[*]} )); do
+ test_legacy_share ${fs[i]} ${fs[((i + 1))]}
+
+ ((i = i + 2))
+done
+
+log_pass "Verify that '$ZFS share' with a file system " \
+ "whose sharenfs property is 'off' fails."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_004_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_004_pos.ksh
new file mode 100644
index 000000000000..3ff499ac2911
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_004_pos.ksh
@@ -0,0 +1,104 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_share_004_pos
+#
+# DESCRIPTION:
+# Verify that a file system and its snapshot are shared.
+#
+# STRATEGY:
+# 1. Create a file system
+# 2. Set the sharenfs property on the file system
+# 3. Create a snapshot
+# 4. Verify that both are shared.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ if snapexists $TESTPOOL/$TESTFS@snapshot; then
+ log_must $ZFS destroy $TESTPOOL/$TESTFS@snapshot
+ fi
+
+ log_must $ZFS set sharenfs=off $TESTPOOL/$TESTFS
+ log_must unshare_fs $TESTPOOL/$TESTFS
+}
+
+#
+# Main test routine.
+#
+# Given a mountpoint and file system this routine will attempt
+# share the mountpoint and then verify a snapshot of the mounpoint
+# is also shared.
+#
+function test_snap_share # mntp filesystem
+{
+ typeset mntp=$1
+ typeset filesystem=$2
+
+ not_shared $mntp || \
+ log_fail "File system $filesystem is already shared."
+
+ log_must $ZFS set sharenfs=on $filesystem
+ is_shared $mntp || \
+ log_fail "File system $filesystem is not shared (set sharenfs)."
+
+ log_must $LS -l $mntp/$SNAPROOT/snapshot
+ #
+ # Verify 'zfs share' works as well.
+ #
+ log_must $ZFS unshare $filesystem
+ log_must $ZFS share $filesystem
+
+ is_shared $mntp || \
+ log_fail "file system $filesystem is not shared (zfs share)."
+
+ log_must $LS -l $mntp/$SNAPROOT/snapshot
+}
+
+log_assert "Verify that a file system and its snapshot are shared."
+log_onexit cleanup
+
+log_must $ZFS snapshot $TESTPOOL/$TESTFS@snapshot
+test_snap_share $TESTDIR $TESTPOOL/$TESTFS
+
+log_pass "A file system and its snapshot are both shared as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_005_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_005_pos.ksh
new file mode 100644
index 000000000000..a1e345520c05
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_005_pos.ksh
@@ -0,0 +1,92 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_share_005_pos
+#
+# DESCRIPTION:
+# Verify that NFS share options are propagated correctly.
+#
+# STRATEGY:
+# 1. Create a ZFS file system.
+# 2. For each option in the list, set the sharenfs property.
+# 3. Verify through the share command that the options are propagated.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ log_must $ZFS set sharenfs=off $TESTPOOL/$TESTFS
+ is_shared $TESTPOOL/$TESTFS && \
+ log_must unshare_fs $TESTPOOL/$TESTFS
+}
+
+set -A shareopts \
+ "ro" "ro=machine1" "ro=machine1:machine2" \
+ "rw" "rw=machine1" "rw=machine1:machine2" \
+ "ro=machine1:machine2,rw" "anon=0" "anon=0,sec=sys,rw" \
+ "nosuid" "root=machine1:machine2" "rw=.mydomain.mycompany.com" \
+ "rw=-terra:engineering" "log" "public"
+
+log_assert "Verify that NFS share options are propagated correctly."
+log_onexit cleanup
+
+cleanup
+
+typeset -i i=0
+while (( i < ${#shareopts[*]} ))
+do
+ log_must $ZFS set sharenfs="${shareopts[i]}" $TESTPOOL/$TESTFS
+
+ option=`get_prop sharenfs $TESTPOOL/$TESTFS`
+ if [[ $option != ${shareopts[i]} ]]; then
+ log_fail "get sharenfs failed. ($option != ${shareopts[i]})"
+ fi
+
+ $SHARE | $GREP $option > /dev/null 2>&1
+ if (( $? != 0 )); then
+ log_fail "The '$option' option was not found in share output."
+ fi
+
+ ((i = i + 1))
+done
+
+log_pass "NFS options were propagated correctly."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_006_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_006_pos.ksh
new file mode 100644
index 000000000000..543533d66d84
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_006_pos.ksh
@@ -0,0 +1,114 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_share_006_pos
+#
+# DESCRIPTION:
+# Verify that a dataset could not be shared but filesystems are shared.
+#
+# STRATEGY:
+# 1. Create a dataset and file system
+# 2. Set the sharenfs property on the dataset
+# 3. Verify that the dataset is unable be shared.
+# 4. Add a new file system to the dataset.
+# 5. Verify that the newly added file system be shared.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ log_must $ZFS set sharenfs=off $TESTPOOL/$TESTCTR
+ if mounted $TESTDIR2; then
+ log_must $ZFS unmount $TESTDIR2
+ fi
+
+ datasetexists $TESTPOOL/$TESTCTR/$TESTFS2 && \
+ log_must $ZFS destroy $TESTPOOL/$TESTCTR/$TESTFS2
+
+ typeset fs=""
+ for fs in $mntp $TESTDIR1 $TESTDIR2
+ do
+ log_must unshare_fs $fs
+ done
+}
+
+#
+# Main test routine.
+#
+# Given a mountpoint and a dataset, this routine will set the
+# sharenfs property on the dataset and verify that dataset
+# is unable to be shared but the existing contained file systems
+# could be shared.
+#
+function test_ctr_share # mntp ctr
+{
+ typeset mntp=$1
+ typeset ctr=$2
+
+ not_shared $mntp || \
+ log_fail "Mountpoint: $mntp is already shared."
+
+ log_must $ZFS set sharenfs=on $ctr
+
+ not_shared $mntp || \
+ log_fail "File system $mntp is shared (set sharenfs)."
+
+ #
+ # Add a new file system to the dataset and verify it is shared.
+ #
+ typeset mntp2=$TESTDIR2
+ log_must $ZFS create $ctr/$TESTFS2
+ log_must $ZFS set mountpoint=$mntp2 $ctr/$TESTFS2
+
+ is_shared $mntp2 || \
+ log_fail "File system $mntp2 was not shared (set sharenfs)."
+}
+
+log_assert "Verify that a dataset could not be shared, " \
+ "but its sub-filesystems could be shared."
+log_onexit cleanup
+
+typeset mntp=$(get_prop mountpoint $TESTPOOL/$TESTCTR)
+test_ctr_share $mntp $TESTPOOL/$TESTCTR
+
+log_pass "A dataset could not be shared, " \
+ "but its sub-filesystems could be shared as expect."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_007_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_007_neg.ksh
new file mode 100644
index 000000000000..db88fd846c53
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_007_neg.ksh
@@ -0,0 +1,94 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_share_007_neg
+#
+# DESCRIPTION:
+# Verify that invalid share parameters and options are caught.
+#
+# STRATEGY:
+# 1. Create a ZFS file system.
+# 2. For each option in the list, set the sharenfs property.
+# 3. Verify that the error code and sharenfs property.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup {
+ if is_global_zone; then
+ log_must $ZFS set sharenfs=off $TESTPOOL/$TESTFS
+ fi
+}
+
+set -A badopts \
+ "r0" "r0=machine1" "r0=machine1:machine2" \
+ "-g" "-b" "-c" "-d" "--invalid" \
+ "$TESTPOOL" "$TESTPOOL/$TESTFS" "$TESTPOOL\$TESTCTR\$TESTFS1"
+
+log_assert "Verify that invalid share parameters and options are caught."
+log_onexit cleanup
+
+typeset -i i=0
+while (( i < ${#badopts[*]} ))
+do
+ log_note "Setting sharenfs=${badopts[i]} $i "
+ log_mustnot $ZFS set sharenfs="${badopts[i]}" $TESTPOOL/$TESTFS
+
+ $SHARE | $GREP $option > /dev/null 2>&1
+ if (( $? == 0 )); then
+ log_fail "An invalid setting '$option' was propagated."
+ fi
+
+ #
+ # To global zone, sharenfs must be set 'off' before malformed testing.
+ # Otherwise, the malformed test return '0'.
+ #
+ # To non-global zone, sharenfs can be set even 'off' or 'on'.
+ #
+ if is_global_zone; then
+ log_note "Resetting sharenfs option"
+ log_must $ZFS set sharenfs=off $TESTPOOL/$TESTFS
+ fi
+
+ ((i = i + 1))
+done
+
+log_pass "Invalid share parameters and options we caught as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_008_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_008_neg.ksh
new file mode 100644
index 000000000000..6559de5576e8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_008_neg.ksh
@@ -0,0 +1,83 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_share_008_neg
+#
+# DESCRIPTION:
+# Verify that sharing a dataset other than filesystem fails.
+#
+# STRATEGY:
+# 1. Create a ZFS file system.
+# 2. For each dataset in the list, set the sharenfs property.
+# 3. Verify that the invalid datasets are not shared.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+if is_global_zone ; then
+ set -A datasets \
+ "$TESTPOOL/$TESTVOL" "$TESTDIR"
+fi
+
+log_assert "Verify that sharing a dataset other than filesystem fails."
+
+typeset -i i=0
+while (( i < ${#datasets[*]} ))
+do
+ log_mustnot $ZFS set sharenfs=on ${datasets[i]}
+
+ option=`get_prop sharenfs ${datasets[i]}`
+ if [[ $option == ${datasets[i]} ]]; then
+ log_fail "set sharenfs failed. ($option == ${datasets[i]})"
+ fi
+
+ not_shared ${datasets[i]} || \
+ log_fail "An invalid setting '$option' was propagated."
+
+ log_mustnot $ZFS share ${datasets[i]}
+
+ not_shared ${datasets[i]} || \
+ log_fail "An invalid dataset '${datasets[i]}' was shared."
+
+ ((i = i + 1))
+done
+
+log_pass "Sharing datasets other than filesystems failed as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_009_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_009_neg.ksh
new file mode 100644
index 000000000000..468d4c769e04
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_009_neg.ksh
@@ -0,0 +1,82 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_share_009_neg
+#
+# DESCRIPTION:
+# Verify that zfs share should fail when sharing a shared zfs filesystem
+#
+# STRATEGY:
+# 1. Make a zfs filesystem shared
+# 2. Use zfs share to share the filesystem
+# 3. Verify that zfs share returns error
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-9)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ typeset val
+
+ val=$(get_prop sharenfs $fs)
+ if [[ $val == on ]]; then
+ log_must $ZFS set sharenfs=off $fs
+ fi
+}
+
+log_assert "zfs share fails with shared filesystem"
+log_onexit cleanup
+
+fs=$TESTPOOL/$TESTFS
+sharenfs_val=$(get_prop sharenfs $fs)
+mpt=$(get_prop mountpoint $fs)
+if [[ $sharenfs_val == off ]]; then
+ log_must $ZFS set sharenfs=on $fs
+fi
+
+$SHARE | $GREP $mpt >/dev/null 2>&1
+if (( $? != 0 )); then
+ log_must $ZFS share $fs
+fi
+
+log_mustnot $ZFS share $fs
+
+log_pass "zfs share fails with shared filesystem as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_009_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_009_pos.ksh
new file mode 100644
index 000000000000..1e228564498a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_009_pos.ksh
@@ -0,0 +1,108 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_share_009_pos
+#
+# DESCRIPTION:
+# Verify that umount/rollback/destroy fails does not unshare the shared
+# file system
+#
+# STRATEGY:
+# 1. Share the filesystem via 'zfs set sharenfs'.
+# 2. Try umount failure, and verify that the file system is still shared.
+# 3. Try rollback failure, and verify that the file system is still shared.
+# 4. Try destroy failure, and verify that the file system is still shared.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-04-28)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ log_must cd $origdir
+
+ log_must $ZFS set sharenfs=off $TESTPOOL/$TESTFS
+ unshare_fs $TESTPOOL/$TESTFS
+
+ if snapexists "$TESTPOOL/$TESTFS@snapshot"; then
+ log_must $ZFS destroy -f $TESTPOOL/$TESTFS@snapshot
+ fi
+
+ if datasetexists $TESTPOOL/$TESTFS/fs2 ; then
+ log_must $ZFS destroy -f $TESTPOOL/$TESTFS/fs2
+ fi
+}
+
+log_assert "Verify umount/rollback/destroy fails does not unshare the shared" \
+ "file system"
+log_onexit cleanup
+
+typeset origdir=$PWD
+
+# unmount fails will not unshare the shared filesystem
+log_must $ZFS set sharenfs=on $TESTPOOL/$TESTFS
+log_must is_shared $TESTDIR
+if cd $TESTDIR ; then
+ log_mustnot $ZFS umount $TESTPOOL/$TESTFS
+else
+ log_fail "cd $TESTDIR fails"
+fi
+log_must is_shared $TESTDIR
+
+# rollback fails will not unshare the shared filesystem
+log_must $ZFS snapshot $TESTPOOL/$TESTFS@snapshot
+if cd $TESTDIR ; then
+ log_mustnot $ZFS rollback $TESTPOOL/$TESTFS@snapshot
+else
+ log_fail "cd $TESTDIR fails"
+fi
+log_must is_shared $TESTDIR
+
+# destroy fails will not unshare the shared filesystem
+log_must $ZFS create $TESTPOOL/$TESTFS/fs2
+if cd $TESTDIR/fs2 ; then
+ log_mustnot $ZFS destroy $TESTPOOL/$TESTFS/fs2
+else
+ log_fail "cd $TESTDIR/fs2 fails"
+fi
+log_must is_shared $TESTDIR/fs2
+
+log_pass "Verify umount/rollback/destroy fails does not unshare the shared" \
+ "file system"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_010_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_010_neg.ksh
new file mode 100644
index 000000000000..e310d90ac6a1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_010_neg.ksh
@@ -0,0 +1,69 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_share_010_neg
+#
+# DESCRIPTION:
+# Verify that zfs share should fail with bad parameters
+#
+# STRATEGY:
+# 1. Make an array of bad parameters
+# 2. Use zfs share to share the filesystem
+# 3. Verify that zfs share returns error
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-9)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "zfs share fails with bad parameters"
+
+fs=$TESTPOOL/$TESTFS
+set -A badargs "A" "-A" "-" "-x" "-?" "=" "-a *" "-a"
+
+for arg in "${badargs[@]}"; do
+ log_mustnot eval "$ZFS share $arg $fs >/dev/null 2>&1"
+done
+
+#zfs share failed when missing arguments or invalid datasetname
+for obj in "" "/$fs"; do
+ log_mustnot eval "$ZFS share $obj >/dev/null 2>&1"
+done
+
+log_pass "zfs share fails with bad parameters as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_test.sh
new file mode 100755
index 000000000000..516bc3a1d5b2
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_share/zfs_share_test.sh
@@ -0,0 +1,294 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zfs_share_001_pos cleanup
+zfs_share_001_pos_head()
+{
+ atf_set "descr" "Verify that 'zfs share' succeeds as root."
+ atf_set "require.progs" "ksh93 zfs svcs"
+}
+zfs_share_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_share.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_share_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_share_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_share.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_share_002_pos cleanup
+zfs_share_002_pos_head()
+{
+ atf_set "descr" "Verify that zfs share with a non-existent file system fails."
+ atf_set "require.progs" "ksh93 zfs svcs"
+}
+zfs_share_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_share.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_share_002_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_share_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_share.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_share_003_pos cleanup
+zfs_share_003_pos_head()
+{
+ atf_set "descr" "Verify that '$ZFS share' with a file systemwhose sharenfs property is 'off' \will fail with return code 1."
+ atf_set "require.progs" "ksh93 zfs svcs"
+}
+zfs_share_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_share.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_share_003_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_share_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_share.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_share_004_pos cleanup
+zfs_share_004_pos_head()
+{
+ atf_set "descr" "Verify that a file system and its snapshot are shared."
+ atf_set "require.progs" "ksh93 zfs svcs"
+}
+zfs_share_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_share.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_share_004_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_share_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_share.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_share_005_pos cleanup
+zfs_share_005_pos_head()
+{
+ atf_set "descr" "Verify that NFS share options are propagated correctly."
+ atf_set "require.progs" "ksh93 zfs share svcs"
+}
+zfs_share_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_share.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_share_005_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_share_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_share.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_share_006_pos cleanup
+zfs_share_006_pos_head()
+{
+ atf_set "descr" "Verify that a dataset could not be shared,but its sub-filesystems could be shared."
+ atf_set "require.progs" "ksh93 zfs svcs"
+}
+zfs_share_006_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_share.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_share_006_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_share_006_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_share.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_share_007_neg cleanup
+zfs_share_007_neg_head()
+{
+ atf_set "descr" "Verify that invalid share parameters and options are caught."
+ atf_set "require.progs" "ksh93 zfs share svcs"
+}
+zfs_share_007_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_share.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_share_007_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_share_007_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_share.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_share_008_neg cleanup
+zfs_share_008_neg_head()
+{
+ atf_set "descr" "Verify that sharing a dataset other than filesystem fails."
+ atf_set "require.progs" "ksh93 zfs svcs"
+}
+zfs_share_008_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_share.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_share_008_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_share_008_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_share.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_share_009_neg cleanup
+zfs_share_009_neg_head()
+{
+ atf_set "descr" "zfs share fails with shared filesystem"
+ atf_set "require.progs" "ksh93 zfs share svcs"
+}
+zfs_share_009_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_share.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_share_009_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_share_009_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_share.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_share_009_pos cleanup
+zfs_share_009_pos_head()
+{
+ atf_set "descr" "Verify umount/rollback/destroy fails does not unshare the sharedfile system"
+ atf_set "require.progs" "ksh93 zfs svcs"
+}
+zfs_share_009_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_share.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_share_009_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_share_009_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_share.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_share_010_neg cleanup
+zfs_share_010_neg_head()
+{
+ atf_set "descr" "zfs share fails with bad parameters"
+ atf_set "require.progs" "ksh93 zfs svcs"
+}
+zfs_share_010_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_share.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_share_010_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_share_010_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_share.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zfs_share_001_pos
+ atf_add_test_case zfs_share_002_pos
+ atf_add_test_case zfs_share_003_pos
+ atf_add_test_case zfs_share_004_pos
+ atf_add_test_case zfs_share_005_pos
+ atf_add_test_case zfs_share_006_pos
+ atf_add_test_case zfs_share_007_neg
+ atf_add_test_case zfs_share_008_neg
+ atf_add_test_case zfs_share_009_neg
+ atf_add_test_case zfs_share_009_pos
+ atf_add_test_case zfs_share_010_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/Makefile
new file mode 100644
index 000000000000..7cad43c9d796
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/Makefile
@@ -0,0 +1,22 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zfs_snapshot
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zfs_snapshot_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zfs_snapshot_005_neg.ksh
+${PACKAGE}FILES+= zfs_snapshot_001_neg.ksh
+${PACKAGE}FILES+= zfs_snapshot_004_neg.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zfs_snapshot_002_neg.ksh
+${PACKAGE}FILES+= zfs_snapshot_006_pos.ksh
+${PACKAGE}FILES+= zfs_snapshot_007_neg.ksh
+${PACKAGE}FILES+= zfs_snapshot_003_neg.ksh
+${PACKAGE}FILES+= zfs_snapshot.cfg
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/cleanup.ksh
new file mode 100644
index 000000000000..9af80e992e94
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_container_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/setup.ksh
new file mode 100644
index 000000000000..db5cb7d70c9b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_container_volume_setup ${DISK}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot.cfg b/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot.cfg
new file mode 100644
index 000000000000..f4f16097c4bd
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot.cfg
@@ -0,0 +1,32 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/cli.cfg
+
+export SNAPFS="$TESTPOOL/$TESTFS@$TESTSNAP"
+export SNAPFS1="$TESTPOOL/$TESTVOL@$TESTSNAP"
+export SNAPDIR="$TESTDIR@$TESTSNAP"
+export SNAPDIR1="$ZFSROOT/$SNAPFS1"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_001_neg.ksh
new file mode 100644
index 000000000000..0c34798fcd2f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_001_neg.ksh
@@ -0,0 +1,127 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_snapshot_001_neg
+#
+# DESCRIPTION:
+# Try each 'zfs snapshot' with inapplicable scenarios to make sure
+# it returns an error. include:
+# * No arguments given.
+# * The argument contains invalid characters for the ZFS namesapec
+# * Leading slash in snapshot name
+# * The argument contains an empty component.
+# * Missing '@' delimiter.
+# * Multiple '@' delimiters in snapshot name.
+# * The snapshot already exist.
+# * Create snapshot upon the pool.
+# (Be removed since pool is treated as filesystem as well)
+# * Create snapshot upon a non-existent filesystem.
+# * Too many arguments.
+#
+# STRATEGY:
+# 1. Create an array of parameters
+# 2. For each parameter in the array, execute the sub-command
+# 3. Verify an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-26)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+set -A args "" \
+ "$TESTPOOL/$TESTFS@blah*" "$TESTPOOL/$TESTFS@blah?" \
+ "$TESTPOOL/$TESTVOL@blah*" "$TESTPOOL/$TESTVOL@blah?" \
+ "/$TESTPOOL/$TESTFS@$TESTSNAP" "/$TESTPOOL/$TESTVOL@$TESTSNAP" \
+ "@$TESTSNAP" "$TESTPOOL/$TESTFS@" "$TESTPOOL/$TESTVOL@" \
+ "$TESTPOOL//$TESTFS@$TESTSNAP" "$TESTPOOL//$TESTVOL@$TESTSNAP" \
+ "$TESTPOOL/$TESTFS/$TESTSNAP" "$TESTPOOL/$TESTVOL/$TESTSNAP" \
+ "$TESTPOOL/$TESTFS@$TESTSNAP@$TESTSNAP1" \
+ "$TESTPOOL/$TESTVOL@$TESTSNAP@$TESTSNAP1" \
+ "$SNAPFS" "$SNAPFS1" \
+ "blah/blah@$TESTSNAP"
+
+function setup_all
+{
+ log_note "Create snapshots and mount them..."
+
+ for snap in $SNAPFS $SNAPFS1 ; do
+ if ! snapexists $snap ; then
+ log_must $ZFS snapshot $snap
+ fi
+ done
+
+ return 0
+}
+
+function cleanup_all
+{
+ typeset -i i=0
+
+ while (( i < ${#args[*]} )); do
+
+ for snap in ${args[i]}; do
+ snapexists $snap && \
+ log_must $ZFS destroy -f $snap
+
+ done
+
+ (( i = i + 1 ))
+ done
+
+ for mtpt in $SNAPDIR $SNAPDIR1 ; do
+ [[ -d $mtpt ]] && \
+ log_must $RM -rf $mtpt
+ done
+
+ return 0
+}
+
+log_assert "Badly-formed 'zfs snapshot' with inapplicable scenarios " \
+ "should return an error."
+log_onexit cleanup_all
+
+setup_all
+
+typeset -i i=0
+while (( i < ${#args[*]} )); do
+ log_mustnot $ZFS snapshot ${args[i]}
+ ((i = i + 1))
+done
+
+log_pass "Badly formed 'zfs snapshot' with inapplicable scenarios " \
+ "fail as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_002_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_002_neg.ksh
new file mode 100644
index 000000000000..4cef9225f5ad
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_002_neg.ksh
@@ -0,0 +1,106 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_snapshot_002_neg
+#
+# DESCRIPTION:
+# "zfs snapshot -r" fails with invalid arguments or scenarios.
+# The invalid scenarios may include:
+# (1) The child filesystem already has snapshot with the same name
+# (2) The child volume already has snapshot with the same name
+#
+# STRATEGY:
+# 1. Create an array of invalid arguments
+# 2. Execute 'zfs snapshot -r' with each argument in the array,
+# 3. Verify an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-19)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ typeset snap
+
+ for snap in $TESTPOOL/$TESTCTR/$TESTFS1@$TESTSNAP \
+ $TESTPOOL/$TESTCTR/$TESTVOL@$TESTSNAP;
+ do
+ snapexists $snap && \
+ log_must $ZFS destroy $snap
+ done
+
+ datasetexists $TESTPOOL/$TESTCTR/$TESTVOL && \
+ log_must $ZFS destroy -rf $TESTPOOL/$TESTCTR/$TESTVOL
+
+}
+
+log_assert "'zfs snapshot -r' fails with invalid arguments or scenarios. "
+log_onexit cleanup
+
+set -A args "" \
+ "$TESTPOOL/$TESTCTR@$TESTSNAP" "$TESTPOOL/$TESTCTR@blah?" \
+ "$TESTPOOL/$TESTCTR@blah*" "@$TESTSNAP" "$TESTPOOL/$TESTCTR@" \
+ "$TESTPOOL/$TESTFS/$TESTSNAP" "blah/blah@$TESTSNAP" \
+ "$TESTPOOL/$TESTCTR@$TESTSNAP@$TESTSNAP"
+
+# setup preparations
+log_must $ZFS snapshot $TESTPOOL/$TESTCTR/$TESTFS1@$TESTSNAP
+
+# testing
+typeset -i i=0
+while (( i < ${#args[*]} )); do
+ log_mustnot $ZFS snapshot -r ${args[i]}
+
+ ((i = i + 1))
+done
+
+# Testing the invalid senario: the child volume already has an
+# identical name snapshot, zfs snapshot -r should fail when
+# creating snapshot with -r for the parent
+log_must $ZFS destroy $TESTPOOL/$TESTCTR/$TESTFS1@$TESTSNAP
+if is_global_zone; then
+ log_must $ZFS create -V $VOLSIZE $TESTPOOL/$TESTCTR/$TESTVOL
+else
+ log_must $ZFS create $TESTPOOL/$TESTCTR/$TESTVOL
+fi
+log_must $ZFS snapshot $TESTPOOL/$TESTCTR/$TESTVOL@$TESTSNAP
+
+log_mustnot $ZFS snapshot -r $TESTPOOL/$TESTCTR@$TESTSNAP
+
+log_pass "'zfs snapshot -r' fails with invalid arguments or scenarios as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_003_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_003_neg.ksh
new file mode 100644
index 000000000000..bdca9c6c7b54
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_003_neg.ksh
@@ -0,0 +1,74 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_snapshot_003_neg
+#
+# DESCRIPTION:
+# "zfs snapshot" fails with bad options,too many arguments or too long
+# snapshot name
+#
+# STRATEGY:
+# 1. Create an array of invalid arguments
+# 2. Execute 'zfs snapshot' with each argument in the array,
+# 3. Verify an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-19)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "'zfs snapshot' fails with bad options, or too many arguments. "
+
+set -A badopts "r" "R" "-R" "-x" "-rR" "-?" "-*" "-123"
+
+# set too long snapshot name (>256)
+l_name="$(gen_dataset_name 260 abcdefg)"
+
+for ds in $TESTPOOL/$TESTFS $TESTPOOL/$TESTCTR $TESTPOOL/$TESTVOL; do
+ for opt in ${badopts[@]}; do
+ log_mustnot $ZFS snapshot $opt $ds@$TESTSNAP
+ done
+
+ log_mustnot $ZFS snapshot $ds@snap $ds@snap1
+ log_mustnot $ZFS snapshot -r $ds@snap $ds@snap1
+
+ log_mustnot $ZFS snapshot $ds@$l_name
+ log_mustnot $ZFS snapshot -r $ds@$l_name
+done
+
+log_pass "'zfs snapshot' fails with bad options or too many arguments as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_004_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_004_neg.ksh
new file mode 100644
index 000000000000..5ad754de8483
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_004_neg.ksh
@@ -0,0 +1,106 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_snapshot_004_neg
+#
+# DESCRIPTION:
+# Verify recursive snapshotting could not break ZFS.
+#
+# STRATEGY:
+# 1. Create deeply-nested filesystems until it is too long to create snap
+# 2. Verify zfs snapshot -r pool@snap will not break ZFS
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-08-08)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if datasetexists $initfs ; then
+ log_must $ZFS destroy -rf $initfs
+ fi
+}
+
+log_assert "Verify recursive snapshotting could not break ZFS."
+log_onexit cleanup
+
+initfs=$TESTPOOL/$TESTFS/$TESTFS
+basefs=$initfs
+typeset -i ret=0 len snaplen
+while ((ret == 0)); do
+ $ZFS create $basefs
+ $ZFS snapshot $basefs@snap1
+ ret=$?
+
+ len=$($ECHO $basefs| $WC -c)
+ if ((ret != 0)); then
+ log_note "The deeply-nested filesystem len: $len"
+ #
+ # Make sure there are at lease 2 characters left
+ # for snapshot name space, otherwise snapshot name
+ # is incorrect
+ #
+ if ((len >= 255)); then
+ if datasetexists $basefs; then
+ log_must $ZFS destroy -r $basefs
+ fi
+ basefs=${basefs%/*}
+ len=$($ECHO $basefs| $WC -c)
+ fi
+ break
+ else
+ log_note "ZFS snapshot suceeded. len: $len"
+ fi
+
+ basefs=$basefs/$TESTFS
+done
+
+# Make snapshot name is longer than the max length
+((snaplen = 256 - len + 10))
+snap=$(gen_dataset_name $snaplen "s")
+log_mustnot $ZFS snapshot -r $TESTPOOL@$snap
+
+log_must datasetnonexists $TESTPOOL@$snap
+while [[ $basefs != $TESTPOOL ]]; do
+ log_must datasetnonexists $basefs@$snap
+ basefs=${basefs%/*}
+done
+
+log_pass "Verify recursive snapshotting could not break ZFS."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_005_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_005_neg.ksh
new file mode 100644
index 000000000000..55d65029ae1b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_005_neg.ksh
@@ -0,0 +1,97 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_snapshot_005_neg
+#
+# DESCRIPTION:
+# Long name filesystem with snapshot should not break ZFS.
+#
+# STRATEGY:
+# 1. Create filesystem and snapshot.
+# 2. When the snapshot length is 256, rename the filesystem.
+# 3. Verify it does not break ZFS
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-08-09)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify long name filesystem with snapshot should not break ZFS."
+
+initfs=$TESTPOOL/$TESTFS/$TESTFS
+basefs=$initfs
+typeset -i ret=0 len snaplen
+while ((ret == 0)); do
+ $ZFS create $basefs
+ $ZFS snapshot $basefs@snap1
+ ret=$?
+
+ if ((ret != 0)); then
+ len=$($ECHO $basefs | $WC -c)
+ log_note "The deeply-nested filesystem len: $len"
+
+ #
+ # Make sure there are at lease 2 characters left
+ # for snapshot name space, otherwise snapshot name
+ # is incorrect
+ #
+ if ((len >= 255)); then
+ if datasetexists $basefs; then
+ log_must $ZFS destroy -r $basefs
+ fi
+ basefs=${basefs%/*}
+ len=$($ECHO $basefs| $WC -c)
+ fi
+ break
+ fi
+
+ basefs=$basefs/$TESTFS
+done
+
+# Make snapshot name length match the longest one
+((snaplen = 256 - len - 1)) # 1: @
+snap=$(gen_dataset_name $snaplen "s")
+log_must $ZFS snapshot $basefs@$snap
+
+log_mustnot $ZFS rename $basefs ${basefs}a
+log_mustnot $ZFS rename $basefs ${basefs}-new
+log_mustnot $ZFS rename $initfs ${initfs}-new
+log_mustnot $ZFS rename $TESTPOOL/$TESTFS $TESTPOOL/$TESTFS-new
+
+log_pass "Verify long name filesystem with snapshot should not break ZFS."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_006_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_006_pos.ksh
new file mode 100644
index 000000000000..5d60b9eb6e67
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_006_pos.ksh
@@ -0,0 +1,138 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_set/zfs_set_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_snapshot_006_pos
+#
+# DESCRIPTION:
+# User property could be set via creation time by 'zfs snapshot -o'
+#
+# STRATEGY:
+# 1. Create snapshot and give '-o property=value'
+# 2. Verify the snapshot be created and user property have been set.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-04-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ for fs in $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL $TESTPOOL ; do
+ typeset fssnap=$fs@snap
+ if datasetexists $fssnap ; then
+ log_must $ZFS destroy -rf $fssnap
+ fi
+ done
+ cleanup_user_prop $TESTPOOL
+}
+
+function nonexist_user_prop
+{
+ typeset user_prop=$1
+ typeset dtst=$2
+
+ typeset source=$(get_source $user_prop $dtst)
+ typeset value=$(get_prop $user_prop $dtst)
+ if [[ $source == '-' && $value == '-' ]]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+log_assert "User property could be set upon snapshot via 'zfs snapshot -o'."
+log_onexit cleanup
+
+typeset snap_property=
+
+$ZPOOL upgrade -v | $GREP "Snapshot properties" > /dev/null 2>&1
+if (( $? != 0 )) ; then
+ log_unsupported "Snapshot properties not supported on current system."
+fi
+
+for fs in $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL $TESTPOOL ; do
+ typeset fssnap=$fs@snap
+ prop_name=$(valid_user_property 10)
+ value=$(user_property_value 16)
+
+ log_must eval "$ZFS snapshot -o $prop_name='$value' $fssnap"
+ log_must snapexists $fssnap
+ log_mustnot nonexist_user_prop $prop_name $fssnap
+
+ log_must $ZFS destroy -f $fssnap
+
+ prop_name2=$(valid_user_property 10)
+ value2=$(user_property_value 16)
+
+ log_must eval "$ZFS snapshot -o $prop_name='$value' -o $prop_name2='$value2' $fssnap"
+ log_must snapexists $fssnap
+ log_mustnot nonexist_user_prop $prop_name $fssnap
+ log_mustnot nonexist_user_prop $prop_name2 $fssnap
+done
+
+cleanup
+
+prop_name=$(valid_user_property 10)
+value=$(user_property_value 16)
+
+log_must eval "$ZFS snapshot -r -o $prop_name='$value' $TESTPOOL@snap"
+for fs in $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL $TESTPOOL ; do
+ typeset fssnap=$fs@snap
+ log_must snapexists $fssnap
+ log_mustnot nonexist_user_prop $prop_name $fssnap
+
+ log_must $ZFS destroy -rf $fssnap
+done
+
+cleanup
+
+prop_name2=$(valid_user_property 10)
+value2=$(user_property_value 16)
+
+log_must eval "$ZFS snapshot -r -o $prop_name='$value' -o $prop_name2='$value2' $TESTPOOL@snap"
+for fs in $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL $TESTPOOL ; do
+ typeset fssnap=$fs@snap
+ log_must snapexists $fssnap
+ log_mustnot nonexist_user_prop $prop_name $fssnap
+ log_mustnot nonexist_user_prop $prop_name2 $fssnap
+
+ log_must $ZFS destroy -rf $fssnap
+done
+
+log_pass "User property could be set upon snapshot via 'zfs snapshot -o'."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_007_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_007_neg.ksh
new file mode 100644
index 000000000000..551db16ac34e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_007_neg.ksh
@@ -0,0 +1,141 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_set/zfs_set_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_snapshot_007_pos
+#
+# DESCRIPTION:
+# 'zfs snapshot -o' cannot set properties other than user property
+#
+# STRATEGY:
+# 1. Create snapshot and give '-o property=value' with regular property.
+# 2. Verify the snapshot creation failed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-04-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ for fs in $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL $TESTPOOL/$TESTCTR $TESTPOOL ; do
+ typeset fssnap=$fs@snap
+ if datasetexists $fssnap ; then
+ log_must $ZFS destroy -rf $fssnap
+ fi
+ done
+ cleanup_user_prop $TESTPOOL
+}
+
+function nonexist_user_prop
+{
+ typeset user_prop=$1
+ typeset dtst=$2
+
+ typeset source=$(get_source $user_prop $dtst)
+ typeset value=$(get_prop $user_prop $dtst)
+ if [[ $source == '-' && $value == '-' ]]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+log_assert "'zfs snapshot -o' cannot set properties other than user property."
+log_onexit cleanup
+
+typeset ro_props="type used available avail creation referenced refer compressratio \
+ mounted origin"
+typeset snap_ro_props="volsize recordsize recsize quota reservation reserv mountpoint \
+ sharenfs checksum compression compress atime devices exec readonly rdonly \
+ setuid zoned"
+
+$ZFS upgrade -v > /dev/null 2>&1
+if [[ $? -eq 0 ]]; then
+ snap_ro_props="$snap_ro_props version"
+fi
+
+
+for fs in $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL $TESTPOOL/$TESTCTR $TESTPOOL ; do
+ typeset fssnap=$fs@snap
+ prop_name=$(valid_user_property 10)
+ value=$(user_property_value 16)
+
+ log_must eval "$ZFS snapshot -o $prop_name='$value' $fssnap"
+ log_must snapexists $fssnap
+ log_mustnot nonexist_user_prop $prop_name $fssnap
+
+ log_must $ZFS destroy -f $fssnap
+
+ prop_name2=$(valid_user_property 10)
+ value2=$(user_property_value 16)
+
+ log_must eval "$ZFS snapshot -o $prop_name='$value' -o $prop_name2='$value2' $fssnap"
+ log_must snapexists $fssnap
+ log_mustnot nonexist_user_prop $prop_name $fssnap
+ log_mustnot nonexist_user_prop $prop_name2 $fssnap
+
+ log_must $ZFS destroy -f $fssnap
+done
+
+cleanup
+
+prop_name=$(valid_user_property 10)
+value=$(user_property_value 16)
+
+log_must eval "$ZFS snapshot -r -o $prop_name='$value' $TESTPOOL@snap"
+for fs in $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL $TESTPOOL/$TESTCTR $TESTPOOL ; do
+ typeset fssnap=$fs@snap
+ log_must snapexists $fssnap
+ log_mustnot nonexist_user_prop $prop_name $fssnap
+done
+
+cleanup
+
+prop_name2=$(valid_user_property 10)
+value2=$(user_property_value 16)
+
+log_must eval "$ZFS snapshot -r -o $prop_name='$value' -o $prop_name2='$value2' $TESTPOOL@snap"
+for fs in $TESTPOOL/$TESTFS $TESTPOOL/$TESTVOL $TESTPOOL/$TESTCTR $TESTPOOL ; do
+ typeset fssnap=$fs@snap
+ log_must snapexists $fssnap
+ log_mustnot nonexist_user_prop $prop_name $fssnap
+ log_mustnot nonexist_user_prop $prop_name2 $fssnap
+done
+
+log_pass "'zfs snapshot -o' cannot set properties other than user property."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_test.sh
new file mode 100755
index 000000000000..f68779afcb00
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_snapshot/zfs_snapshot_test.sh
@@ -0,0 +1,205 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zfs_snapshot_001_neg cleanup
+zfs_snapshot_001_neg_head()
+{
+ atf_set "descr" "Badly-formed 'zfs snapshot' with inapplicable scenariosshould return an error."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_snapshot_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_snapshot_001_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_snapshot_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_snapshot_002_neg cleanup
+zfs_snapshot_002_neg_head()
+{
+ atf_set "descr" "'zfs snapshot -r' fails with invalid arguments or scenarios."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_snapshot_002_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_snapshot_002_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_snapshot_002_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_snapshot_003_neg cleanup
+zfs_snapshot_003_neg_head()
+{
+ atf_set "descr" "'zfs snapshot' fails with bad options, or too many arguments."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_snapshot_003_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_snapshot_003_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_snapshot_003_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_snapshot_004_neg cleanup
+zfs_snapshot_004_neg_head()
+{
+ atf_set "descr" "Verify recursive snapshotting could not break ZFS."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_snapshot_004_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_snapshot_004_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_snapshot_004_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_snapshot_005_neg cleanup
+zfs_snapshot_005_neg_head()
+{
+ atf_set "descr" "Verify long name filesystem with snapshot should not break ZFS."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_snapshot_005_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_snapshot_005_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_snapshot_005_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_snapshot_006_pos cleanup
+zfs_snapshot_006_pos_head()
+{
+ atf_set "descr" "User property could be set upon snapshot via 'zfs snapshot -o'."
+ atf_set "require.progs" "ksh93 zfs zpool"
+}
+zfs_snapshot_006_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_snapshot_006_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_snapshot_006_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_snapshot_007_neg cleanup
+zfs_snapshot_007_neg_head()
+{
+ atf_set "descr" "'zfs snapshot -o' cannot set properties other than user property."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_snapshot_007_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_snapshot_007_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_snapshot_007_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zfs_snapshot_001_neg
+ atf_add_test_case zfs_snapshot_002_neg
+ atf_add_test_case zfs_snapshot_003_neg
+ atf_add_test_case zfs_snapshot_004_neg
+ atf_add_test_case zfs_snapshot_005_neg
+ atf_add_test_case zfs_snapshot_006_pos
+ atf_add_test_case zfs_snapshot_007_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/Makefile
new file mode 100644
index 000000000000..ba37c8fc0f62
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/Makefile
@@ -0,0 +1,26 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zfs_unmount
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zfs_unmount_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zfs_unmount_003_pos.ksh
+${PACKAGE}FILES+= zfs_unmount_all_001_pos.ksh
+${PACKAGE}FILES+= zfs_unmount_006_pos.ksh
+${PACKAGE}FILES+= zfs_unmount_002_pos.ksh
+${PACKAGE}FILES+= zfs_unmount_007_neg.ksh
+${PACKAGE}FILES+= zfs_unmount.cfg
+${PACKAGE}FILES+= zfs_unmount_004_pos.ksh
+${PACKAGE}FILES+= zfs_unmount_009_pos.ksh
+${PACKAGE}FILES+= zfs_unmount_005_pos.ksh
+${PACKAGE}FILES+= zfs_unmount_001_pos.ksh
+${PACKAGE}FILES+= zfs_unmount_008_neg.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zfs_unmount.kshlib
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/cleanup.ksh
new file mode 100644
index 000000000000..713cb16bb31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/setup.ksh
new file mode 100644
index 000000000000..326bc555f71b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount.cfg b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount.cfg
new file mode 100644
index 000000000000..cd2c2e416ac3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount.cfg
@@ -0,0 +1,37 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
+
+export mountcmd=mount
+export mountforce="$mountcmd -f"
+export mountall="$mountcmd -a"
+
+export unmountcmd=unmount
+export unmountforce="$unmountcmd -f"
+export unmountall="$unmountcmd -a"
+
+export NONEXISTFSNAME="nonexistfs50charslong_0123456789012345678901234567"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount.kshlib b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount.kshlib
new file mode 100644
index 000000000000..c3f955ee6ee2
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount.kshlib
@@ -0,0 +1,71 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+function do_unmount #cmd #opt #mnt #expect
+{
+ typeset cmd=$1
+ typeset opt=$2
+ typeset mnt=$3
+ typeset -i expect=${4-0}
+ typeset -i ret
+ typeset -i wasmounted=1
+
+ mounted $mnt || wasmounted=0
+
+ if (( expect == 0 )) ; then
+ log_must $ZFS $cmd $opt $mnt
+
+ log_must unmounted $mnt
+
+ log_note "Successfully $ZFS $cmd $opt $mnt"
+
+ else
+ log_note "$ZFS $cmd $opt $mnt"
+
+ $ZFS $cmd $opt $mnt
+ ret=$?
+ if (( ret != expect)); then
+ log_fail "'$ZFS $cmd $opt $mnt' " \
+ "unexpected return code of $ret."
+ fi
+
+ if (( wasmounted == 1 )) ; then
+ log_must mounted $mnt
+ else
+ log_must unmounted $mnt
+ fi
+ log_note "Mount status of $mnt not changed."
+ fi
+}
+
+function cleanup
+{
+ [[ -n $cwd ]] && cd $cwd
+
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+ mounted $TESTPOOL/$TESTFS || \
+ log_must $ZFS $mountcmd $TESTPOOL/$TESTFS
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_001_pos.ksh
new file mode 100644
index 000000000000..4472c70e5315
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_001_pos.ksh
@@ -0,0 +1,125 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_unmount_001_pos
+#
+# DESCRIPTION:
+# Creates a file system and verifies that it can be unmounted
+# using each of the various unmount options and sub-command
+# variants.
+#
+# STRATEGY:
+# 1. Create and mount a file system as necessary.
+# 2. Umount the file system using the various combinations.
+# - With force option.
+# - Without force option.
+# - Using the unmount sub-command.
+# - Using the umount sub-command.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+
+function cleanup
+{
+ mounted $TESTDIR2 && \
+ log_must $ZFS umount -f $TESTDIR2
+
+ datasetexists $TESTPOOL/$TESTFS2 && \
+ log_must $ZFS destroy $TESTPOOL/$TESTFS2
+
+ [[ -d $TESTDIR2 ]] && \
+ log_must $RM -rf $TESTDIR2
+}
+function do_unmount
+{
+ typeset cmd=$1
+ typeset opt=$2
+ typeset mnt=$3
+
+ [[ ! -d $TESTDIR2 ]] && \
+ log_must $MKDIR $TESTDIR2
+
+ if ! datasetexists $TESTPOOL/$TESTFS2 ; then
+ log_must $ZFS create $TESTPOOL/$TESTFS2
+ log_must $ZFS set mountpoint=$TESTDIR2 \
+ $TESTPOOL/$TESTFS2
+ fi
+
+ unmounted $TESTPOOL/$TESTFS2 && \
+ log_must $ZFS mount $TESTPOOL/$TESTFS2
+
+ log_must $ZFS $cmd $options $mnt
+
+ unmounted "$mnt" || \
+ log_fail "Unable to unmount $options $mnt"
+
+ log_note "Successfully unmounted $options $mnt"
+}
+
+log_onexit cleanup
+
+set -A cmd "umount" "unmount"
+set -A options "" "-f"
+set -A dev "$TESTPOOL/$TESTFS2" "$TESTDIR2"
+
+log_assert "Verify the u[n]mount [-f] sub-command."
+
+typeset -i i=0
+typeset -i j=0
+typeset -i k=0
+while [[ $i -lt ${#cmd[*]} ]]; do
+ j=0
+ while [[ $j -lt ${#options[*]} ]]; do
+ k=0
+ while [[ $k -lt ${#dev[*]} ]]; do
+ do_unmount "${cmd[i]}" "${options[j]}" \
+ "${dev[k]}"
+
+ ((k = k + 1))
+ done
+
+ ((j = j + 1))
+ done
+
+ ((i = i + 1))
+done
+
+log_pass "zfs u[n]mount [-f] completed successfully."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_002_pos.ksh
new file mode 100644
index 000000000000..411d75828f63
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_002_pos.ksh
@@ -0,0 +1,107 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_unmount/zfs_unmount.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_unmount_002_pos
+#
+# DESCRIPTION:
+# If invoke "zfs unmount [-f]" with a filesystem|mountpoint
+# whose name is not in "zfs list",
+# it will fail with a return code of 1
+# and issue an error message.
+#
+# STRATEGY:
+# 1. Make sure that the non-existent ZFS filesystem|mountpoint
+# not in 'zfs list'.
+# 2. Unmount the file system using the various combinations.
+# - Without force option. (FAILED)
+# - With force option. (FAILED)
+# 3. Unmount the mountpoint using the various combinations.
+# - Without force option. (FAILED)
+# - With force option. (FAILED)
+# 4. Verify the above expected results of the filesystem|mountpoint.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+
+set -A cmd "umount" "unmount"
+set -A options "" "-f"
+set -A dev "$TESTPOOL/$NONEXISTFSNAME" "${TEST_BASE_DIR%%/}/$NONEXISTFSNAME"
+
+function do_unmount_multiple #options #expect
+{
+ typeset opt=$1
+ typeset -i expect=${2-0}
+
+ typeset -i i=0
+ typeset -i j=0
+
+ while (( i < ${#cmd[*]} )); do
+ j=0
+ while (( j < ${#dev[*]} )); do
+ log_note "Make sure ${dev[j]} is not in 'zfs list'"
+ log_mustnot $ZFS list ${dev[j]}
+
+ do_unmount "${cmd[i]}" "$opt" \
+ "${dev[j]}" $expect
+
+ ((j = j + 1))
+ done
+
+ ((i = i + 1))
+ done
+}
+
+log_assert "Verify that '$ZFS $unmountcmd [-f] <filesystem|mountpoint>' " \
+ "whose name is not in 'zfs list' will fail with return code 1."
+
+log_onexit cleanup
+
+typeset -i i=0
+
+while (( i < ${#options[*]} )); do
+ do_unmount_multiple "${options[i]}" 1
+ ((i = i + 1))
+done
+
+log_pass "'$ZFS $unmountcmd [-f] <filesystem|mountpoint>' " \
+ "whose name is not in 'zfs list' failed with return code 1."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_003_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_003_pos.ksh
new file mode 100644
index 000000000000..82530c8b9edb
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_003_pos.ksh
@@ -0,0 +1,118 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_unmount/zfs_unmount.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_unmount_003_pos
+#
+# DESCRIPTION:
+# If invoke "zfs unmount [-f]" with a filesystem|mountpoint
+# whose mountpoint property is 'legacy' or 'none',
+# it will fail with a return code of 1
+# and issue an error message.
+#
+# STRATEGY:
+# 1. Make sure that the ZFS filesystem is mounted.
+# 2. Apply 'zfs set mountpoint=legacy|none <filesystem>'.
+# 3. Unmount the file system using the various combinations.
+# - Without force option. (FAILED)
+# - With force option. (FAILED)
+# 4. Unmount the mountpoint using the various combinations.
+# - Without force option. (FAILED)
+# - With force option. (FAILED)
+# 5. Verify the above expected results of the filesystem|mountpoint.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+
+set -A cmd "umount" "unmount"
+set -A options "" "-f"
+set -A dev "$TESTPOOL/$TESTFS" "$TESTDIR"
+set -A mopts "legacy" "none"
+
+function do_unmount_multiple #options #expect #mountpoint
+{
+ typeset opt=$1
+ typeset -i expect=${2-0}
+ typeset mopt=$3
+
+ typeset -i i=0
+ typeset -i j=0
+
+ while (( i < ${#cmd[*]} )); do
+ j=0
+ while (( j < ${#dev[*]} )); do
+ [[ -n $mopt ]] && \
+ log_must $ZFS set mountpoint=$mopt ${dev[0]}
+
+ do_unmount "${cmd[i]}" "$opt" \
+ "${dev[j]}" $expect
+
+ cleanup
+
+ ((j = j + 1))
+ done
+
+ ((i = i + 1))
+ done
+}
+
+log_assert "Verify that '$ZFS $unmountcmd [-f] <filesystem|mountpoint>' " \
+ "whose mountpoint property is 'legacy' or 'none' " \
+ "will fail with return code 1."
+
+log_onexit cleanup
+
+typeset -i i=0
+typeset -i j=0
+
+while (( i < ${#mopts[*]} )); do
+ j=0
+ while (( j < ${#options[*]} )); do
+ do_unmount_multiple "${options[j]}" 1 "${mopts[i]}"
+ ((j = j + 1))
+ done
+ ((i = i + 1))
+done
+
+log_pass "'$ZFS $unmountcmd [-f] <filesystem|mountpoint>' " \
+ "whose mountpoint property is 'legacy' or 'none' " \
+ "will fail with return code 1."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_004_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_004_pos.ksh
new file mode 100644
index 000000000000..aaeb4914e574
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_004_pos.ksh
@@ -0,0 +1,108 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_unmount/zfs_unmount.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_unmount_004_pos
+#
+# DESCRIPTION:
+# If invoke "zfs unmount [-f]" with a specific filesystem|mountpoint,
+# which is not currently mounted,
+# it will fail with a return code of 1
+# and issue an error message.
+#
+# STRATEGY:
+# 1. Make sure that the ZFS filesystem is mounted.
+# 2. Invoke 'zfs unmount <filesystem>'.
+# 3. Verify that the filesystem is unmounted.
+# 4. Unmount the file system using the various combinations.
+# - Without force option. (FAILED)
+# - With force option. (FAILED)
+# 5. Unmount the mountpoint using the various combinations.
+# - Without force option. (FAILED)
+# - With force option. (FAILED)
+# 6. Verify the above expected results of the filesystem|mountpoint.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+
+set -A cmd "umount" "unmount"
+set -A options "" "-f"
+set -A dev "$TESTPOOL/$TESTFS" "$TESTDIR"
+
+function do_unmount_multiple #options #expect
+{
+ typeset opt=$1
+ typeset -i expect=${2-0}
+
+ typeset -i i=0
+ typeset -i j=0
+
+ while (( i < ${#cmd[*]} )); do
+ j=0
+ while (( j < ${#dev[*]} )); do
+ unmounted ${dev[j]} || \
+ log_must $ZFS $unmountforce ${dev[j]}
+
+ do_unmount "${cmd[i]}" "$opt" \
+ "${dev[j]}" $expect
+
+ ((j = j + 1))
+ done
+
+ ((i = i + 1))
+ done
+}
+
+log_assert "Verify that '$ZFS $unmountcmd [-f] <filesystem|mountpoint>' " \
+ "with an unmounted filesystem will fail with return code 1."
+
+log_onexit cleanup
+
+typeset -i i=0
+
+while (( i < ${#options[*]} )); do
+ do_unmount_multiple "${options[i]}" 1
+ ((i = i + 1))
+done
+
+log_pass "'$ZFS $unmountcmd [-f] <filesystem|mountpoint>' " \
+ "with an unmounted filesystem failed with return code 1."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_005_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_005_pos.ksh
new file mode 100644
index 000000000000..33db4686c115
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_005_pos.ksh
@@ -0,0 +1,122 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_unmount/zfs_unmount.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_unmount_005_pos
+#
+# DESCRIPTION:
+# If invoke "zfs unmount" with a specific filesystem|mountpoint
+# that have been mounted, but it's currently in use,
+# it will fail with a return code of 1
+# and issue an error message.
+# But unmount forcefully will bypass this restriction and
+# unmount that given filesystem successfully.
+#
+# STRATEGY:
+# 1. Make sure that the ZFS filesystem is mounted.
+# 2. Change directory to that given mountpoint.
+# 3. Unmount the file system using the various combinations.
+# - Without force option. (FAILED)
+# - With force option. (PASS)
+# 4. Unmount the mountpoint using the various combinations.
+# - Without force option. (FAILED)
+# - With force option. (PASS)
+# 5. Verify the above expected results of the filesystem|mountpoint.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-07)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+
+set -A cmd "umount" "unmount"
+set -A options "" "-f"
+set -A dev "$TESTPOOL/$TESTFS" "$TESTDIR"
+
+function do_unmount_multiple #options #expect
+{
+ typeset opt=$1
+ typeset -i expect=${2-0}
+
+ typeset -i i=0
+ typeset -i j=0
+
+ while (( i < ${#cmd[*]} )); do
+ j=0
+ while (( j < ${#dev[*]} )); do
+ mounted ${dev[j]} || \
+ log_must $ZFS $mountcmd ${dev[0]}
+
+ cd $TESTDIR || \
+ log_unresolved "Unable change dir to $TESTDIR"
+
+ do_unmount "${cmd[i]}" "$opt" \
+ "${dev[j]}" $expect
+
+ cleanup
+
+ ((j = j + 1))
+ done
+
+ ((i = i + 1))
+ done
+}
+
+log_assert "Verify that '$ZFS $unmountcmd <filesystem|mountpoint>' " \
+ "with a filesystem which mountpoint is currently in use " \
+ "will fail with return code 1, and forcefully will succeeds as root."
+
+log_onexit cleanup
+
+cwd=$PWD
+
+typeset -i i=0
+
+while (( i < ${#options[*]} )); do
+ if [[ ${options[i]} == "-f" ]]; then
+ do_unmount_multiple "${options[i]}"
+ else
+ do_unmount_multiple "${options[i]}" 1
+ fi
+ ((i = i + 1))
+done
+
+log_pass "'$ZFS $unmountcmd <filesystem|mountpoint>' " \
+ "with a filesystem which mountpoint is currently in use " \
+ "will fail with return code 1, and forcefully will succeeds as root."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_006_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_006_pos.ksh
new file mode 100644
index 000000000000..a1efcef7e6a5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_006_pos.ksh
@@ -0,0 +1,80 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_unmount_006_pos
+#
+# DESCRIPTION:
+# Re-creating zfs files, 'zfs unmount' still succeed.
+#
+# STRATEGY:
+# 1. Create pool and filesystem.
+# 2. Recreating the same file in this fs for a while, then breaking out.
+# 3. Verify the filesystem can be unmount successfully.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-07-18)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if ! ismounted $TESTPOOL/$TESTFS ; then
+ log_must $ZFS mount $TESTPOOL/$TESTFS
+ fi
+}
+
+log_assert "Re-creating zfs files, 'zfs unmount' still succeed."
+log_onexit cleanup
+
+# Call cleanup to make sure the file system are mounted.
+cleanup
+mntpnt=$(get_prop mountpoint $TESTPOOL/$TESTFS)
+(($? != 0)) && log_fail "get_prop mountpoint $TESTPOOL/$TESTFS"
+
+typeset -i i=0
+while (( i < 10000 )); do
+ $CP $STF_SUITE/include/libtest.kshlib $mntpnt
+
+ (( i += 1 ))
+done
+log_note "Recreating zfs files for 10000 times."
+
+log_must $ZFS unmount $TESTPOOL/$TESTFS
+
+log_pass "Re-creating zfs files, 'zfs unmount' passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_007_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_007_neg.ksh
new file mode 100644
index 000000000000..421cfd1abf7d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_007_neg.ksh
@@ -0,0 +1,119 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_mount/zfs_mount.kshlib
+. $STF_SUITE/tests/cli_root/zfs_unmount/zfs_unmount.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_unmount_007_neg
+#
+# DESCRIPTION:
+# Try each 'zfs unmount' with inapplicable scenarios to make sure
+# it returns an error. include:
+# * Multiple filesystem|mountpoint specified
+# * '-a', but also with a specific filesystem|mountpoint.
+#
+# STRATEGY:
+# 1. Create an array of parameters
+# 2. For each parameter in the array, execute the sub-command
+# 3. Verify an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-12)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+multifs="$TESTFS $TESTFS1"
+datasets=""
+
+for fs in $multifs ; do
+ datasets="$datasets $TESTPOOL/$fs"
+done
+
+set -A args "$unmountall $TESTPOOL/$TESTFS" \
+ "$unmountcmd $datasets"
+
+function setup_all
+{
+ typeset fs
+
+ for fs in $multifs ; do
+ setup_filesystem "$DISKS" "$TESTPOOL" \
+ "$fs" \
+ "${TEST_BASE_DIR%%/}/testroot${TESTCASE_ID}/$TESTPOOL/$fs"
+ done
+ return 0
+}
+
+function cleanup_all
+{
+ typeset fs
+
+ cleanup_filesystem "$TESTPOOL" "$TESTFS1"
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+
+ [[ -d ${TEST_BASE_DIR%%/}/testroot${TESTCASE_ID} ]] && \
+ $RM -rf ${TEST_BASE_DIR%%/}/testroot${TESTCASE_ID}
+
+ return 0
+}
+
+function verify_all
+{
+ typeset fs
+
+ for fs in $multifs ; do
+ log_must mounted $TESTPOOL/$fs
+ done
+ return 0
+}
+
+log_assert "Badly-formed 'zfs $unmountcmd' with inapplicable scenarios " \
+ "should return an error."
+log_onexit cleanup_all
+
+log_must setup_all
+
+typeset -i i=0
+while (( i < ${#args[*]} )); do
+ log_mustnot $ZFS ${args[i]}
+ ((i = i + 1))
+done
+
+log_must verify_all
+
+log_pass "Badly formed 'zfs $unmountcmd' with inapplicable scenarios " \
+ "fail as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_008_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_008_neg.ksh
new file mode 100644
index 000000000000..ba019bf87efa
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_008_neg.ksh
@@ -0,0 +1,152 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_unmount_008_neg
+#
+# DESCRIPTION:
+# Verify that zfs unmount should fail with bad parameters or scenarios:
+# 1. bad option;
+# 2. too many arguments;
+# 3. null arguments;
+# 4. invalid datasets;
+# 5. invalid mountpoint;
+# 6. already unmounted zfs filesystem;
+# 7. legacy mounted zfs filesystem
+#
+# STRATEGY:
+# 1. Make an array of bad parameters
+# 2. Use zfs unmount to unmount the filesystem
+# 3. Verify that zfs unmount returns error
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-9)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ for ds in $vol $fs1; do
+ if datasetexists $ds; then
+ log_must $ZFS destroy -f $ds
+ fi
+ done
+
+ if snapexists $snap; then
+ log_must $ZFS destroy $snap
+ fi
+
+ if [[ -e $TMPDIR/$file ]]; then
+ $RM -f $TMPDIR/$file
+ fi
+ if [[ -d $TMPDIR/$dir ]]; then
+ $RM -rf $TMPDIR/$dir
+ fi
+
+}
+
+log_assert "zfs unmount fails with bad parameters or scenarios"
+log_onexit cleanup
+
+fs=$TESTPOOL/$TESTFS
+vol=$TESTPOOL/vol.${TESTCASE_ID}
+snap=$TESTPOOL/$TESTFS@snap.${TESTCASE_ID}
+set -A badargs "A" "-A" "F" "-F" "-" "-x" "-?"
+
+if ! ismounted $fs; then
+ log_must $ZFS mount $fs
+fi
+
+log_must $ZFS snapshot $snap
+if is_global_zone; then
+ log_must $ZFS create -V 10m $vol
+else
+ vol=""
+fi
+
+# Testing bad options
+for arg in ${badargs[@]}; do
+ log_mustnot eval "$ZFS unmount $arg $fs >/dev/null 2>&1"
+done
+
+
+#Testing invalid datasets
+for ds in $snap $vol "blah"; do
+ for opt in "" "-f"; do
+ log_mustnot eval "$ZFS unmount $opt $ds >/dev/null 2>&1"
+ done
+done
+
+#Testing invalid mountpoint
+dir=foodir.${TESTCASE_ID}
+file=foo.${TESTCASE_ID}
+fs1=$TESTPOOL/fs.${TESTCASE_ID}
+$MKDIR $TMPDIR/$dir
+$TOUCH $TMPDIR/$file
+log_must $ZFS create -o mountpoint=$TMPDIR/$dir $fs1
+curpath=`$DIRNAME $0`
+cd $TMPDIR
+for mpt in "./$dir" "./$file"; do
+ for opt in "" "-f"; do
+ log_mustnot eval "$ZFS unmount $opt $mpt >/dev/null 2>&1"
+ done
+done
+cd $curpath
+
+#Testing null argument and too many arguments
+for opt in "" "-f"; do
+ log_mustnot eval "$ZFS unmount $opt >/dev/null 2>&1"
+ log_mustnot eval "$ZFS unmount $opt $fs $fs1 >/dev/null 2>&1"
+done
+
+#Testing already unmounted filesystem
+log_must $ZFS unmount $fs1
+for opt in "" "-f"; do
+ log_mustnot eval "$ZFS unmount $opt $fs1 >/dev/null 2>&1"
+ log_mustnot eval "$ZFS unmount $TMPDIR/$dir >/dev/null 2>&1"
+done
+
+#Testing legacy mounted filesystem
+log_must $ZFS set mountpoint=legacy $fs1
+log_must $MOUNT -t zfs $fs1 $TMPDIR/$dir
+for opt in "" "-f"; do
+ log_mustnot eval "$ZFS unmount $opt $fs1 >/dev/null 2>&1"
+done
+$UMOUNT $TMPDIR/$dir
+
+log_pass "zfs unmount fails with bad parameters or scenarios as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_009_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_009_pos.ksh
new file mode 100644
index 000000000000..d968c5da0a0a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_009_pos.ksh
@@ -0,0 +1,136 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_unmount_009_pos
+#
+# DESCRIPTION:
+# Verify that zfs unmount and destroy in a snapshot directory will not cause error.
+#
+# STRATEGY:
+# 1. Create a file in a zfs filesystem, snapshot it and change directory to snapshot directory
+# 2. Verify that 'zfs unmount -a' will fail and 'zfs unmount -fa' will succeed
+# 3. Verify 'ls' and 'cd /' will succeed
+# 4. 'zfs mount -a' and change directory to snapshot directory again
+# 5. Verify that zfs destroy snapshot will succeed
+# 6. Verify 'ls' and 'cd /' will succeed
+# 7. Create zfs filesystem, create a file, snapshot it and change to snapshot directory
+# 8. Verify that zpool destroy the pool will succeed
+# 9. Verify 'ls' 'cd /' 'zpool list' and etc will succeed
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-07-29)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ DISK=${DISKS%% *}
+
+ for fs in $TESTPOOL/$TESTFS $TESTPOOL ; do
+ typeset snap=$fs@$TESTSNAP
+ if snapexists $snap; then
+ log_must $ZFS destroy $snap
+ fi
+ done
+
+ if ! poolexists $TESTPOOL && is_global_zone; then
+ log_must $ZPOOL create $TESTPOOL $DISK
+ fi
+
+ if ! datasetexists $TESTPOOL/$TESTFS; then
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+ fi
+}
+
+function restore_dataset
+{
+ if ! datasetexists $TESTPOOL/$TESTFS ; then
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+ log_must cd $TESTDIR
+ $ECHO hello > world
+ log_must $ZFS snapshot $TESTPOOL/$TESTFS@$TESTSNAP
+ log_must cd $(get_snapdir_name)/$TESTSNAP
+ fi
+}
+
+
+log_assert "zfs fource unmount and destroy in snapshot directory will not cause error."
+log_onexit cleanup
+
+for fs in $TESTPOOL/$TESTFS $TESTPOOL ; do
+ typeset snap=$fs@$TESTSNAP
+ typeset mtpt=$(get_prop mountpoint $fs)
+
+ log_must cd $mtpt
+ $ECHO hello > world
+ log_must $ZFS snapshot $snap
+ log_must cd $(get_snapdir_name)/$TESTSNAP
+
+ log_mustnot $ZFS unmount -a
+ log_must $ZFS unmount -fa
+ log_mustnot $LS
+ log_must cd /
+
+ log_must $ZFS mount -a
+ log_must cd $mtpt
+ log_must cd $(get_snapdir_name)/$TESTSNAP
+
+ if is_global_zone || [[ $fs != $TESTPOOL ]] ; then
+ log_must $ZFS destroy -rf $fs
+ log_mustnot $LS
+ log_must cd /
+ fi
+
+ restore_dataset
+done
+
+if is_global_zone ; then
+ log_must $ZPOOL destroy -f $TESTPOOL
+ log_mustnot $LS
+ log_must cd /
+fi
+
+log_must eval $ZFS list > /dev/null 2>&1
+log_must eval $ZPOOL list > /dev/null 2>&1
+log_must eval $ZPOOL status > /dev/null 2>&1
+$ZPOOL iostat > /dev/null 2>&1
+
+log_pass "zfs fource unmount and destroy in snapshot directory will not cause error."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_all_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_all_001_pos.ksh
new file mode 100644
index 000000000000..af8c8251d125
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_all_001_pos.ksh
@@ -0,0 +1,204 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_mount/zfs_mount.kshlib
+. $STF_SUITE/tests/cli_root/zfs_unmount/zfs_unmount.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_unmount_all_001_pos
+#
+# DESCRIPTION:
+# Verify that 'zfs unmount -a[f]' succeeds as root.
+#
+# STRATEGY:
+# 1. Create a group of pools with specified vdev.
+# 2. Create zfs filesystems within the given pools.
+# 3. Mount all the filesystems.
+# 4. Verify that 'zfs unmount -a[f]' command succeed,
+# and all available ZFS filesystems are unmounted.
+# 5. Verify that 'zfs mount' is identical with 'df -F zfs'
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-12)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+set -A fs "$TESTFS" "$TESTFS1"
+set -A ctr "" "$TESTCTR" "$TESTCTR1" "$TESTCTR/$TESTCTR1"
+set -A vol "$TESTVOL" "$TESTVOL1"
+
+function setup_all
+{
+ typeset -i i=0
+ typeset -i j=0
+ typeset path
+
+ while (( i < ${#ctr[*]} )); do
+
+ path=${TEST_BASE_DIR%%/}/testroot${TESTCASE_ID}/$TESTPOOL
+ if [[ -n ${ctr[i]} ]]; then
+ path=$path/${ctr[i]}
+
+ setup_filesystem "$DISKS" "$TESTPOOL" \
+ "${ctr[i]}" "$path" \
+ "ctr"
+ fi
+
+ if is_global_zone ; then
+ j=0
+ while (( j < ${#vol[*]} )); do
+ setup_filesystem "$DISKS" "$TESTPOOL" \
+ "${ctr[i]}/${vol[j]}" \
+ "$path/${vol[j]}" \
+ "vol"
+ ((j = j + 1))
+ done
+ fi
+ j=0
+ while (( j < ${#fs[*]} )); do
+ setup_filesystem "$DISKS" "$TESTPOOL" \
+ "${ctr[i]}/${fs[j]}" \
+ "$path/${fs[j]}"
+ ((j = j + 1))
+ done
+
+ ((i = i + 1))
+ done
+
+ return 0
+}
+
+function cleanup_all
+{
+ typeset -i i=0
+ typeset -i j=0
+
+ ((i = ${#ctr[*]} - 1))
+
+ while (( i >= 0 )); do
+ if is_global_zone ; then
+ j=0
+ while (( j < ${#vol[*]} )); do
+ cleanup_filesystem "$TESTPOOL" \
+ "${ctr[i]}/${vol[j]}"
+ ((j = j + 1))
+ done
+ fi
+
+ j=0
+ while (( j < ${#fs[*]} )); do
+ cleanup_filesystem "$TESTPOOL" \
+ "${ctr[i]}/${fs[j]}"
+ ((j = j + 1))
+ done
+
+ [[ -n ${ctr[i]} ]] && \
+ cleanup_filesystem "$TESTPOOL" "${ctr[i]}"
+
+ ((i = i - 1))
+ done
+
+ [[ -d ${TEST_BASE_DIR%%/}/testroot${TESTCASE_ID} ]] && \
+ $RM -rf ${TEST_BASE_DIR%%/}/testroot${TESTCASE_ID}
+}
+
+function verify_all
+{
+ typeset -i i=0
+ typeset -i j=0
+ typeset path
+
+ while (( i < ${#ctr[*]} )); do
+
+ path=$TESTPOOL
+ [[ -n ${ctr[i]} ]] && \
+ path=$path/${ctr[i]}
+
+ if is_global_zone ; then
+ j=0
+ while (( j < ${#vol[*]} )); do
+ log_must unmounted "$path/${vol[j]}"
+ ((j = j + 1))
+ done
+ fi
+
+ j=0
+ while (( j < ${#fs[*]} )); do
+ log_must unmounted "$path/${fs[j]}"
+ ((j = j + 1))
+ done
+
+ log_must unmounted "$path"
+
+ ((i = i + 1))
+ done
+
+ return 0
+}
+
+
+log_assert "Verify that 'zfs $unmountall' succeeds as root, " \
+ "and all available ZFS filesystems are unmounted."
+
+log_onexit cleanup_all
+
+log_must setup_all
+
+typeset opt
+for opt in "-a" "-fa"; do
+ log_must $ZFS $mountall
+
+ if [[ $opt == "-fa" ]]; then
+ mntpnt=$(get_prop mountpoint ${TESTPOOL}/${TESTCTR}/${TESTFS})
+ cd $mntpnt
+ log_mustnot $ZFS unmount -a
+ fi
+
+ log_must $ZFS unmount $opt
+
+ if [[ $opt == "-fa" ]]; then
+ cd /tmp
+ fi
+
+ log_must verify_all
+ log_note "Verify that 'zfs $mountcmd' will display " \
+ "all ZFS filesystems currently mounted."
+ log_must verify_mount_display
+
+done
+
+log_pass "'zfs mount -[f]a' succeeds as root, " \
+ "and all available ZFS filesystems are unmounted."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_test.sh
new file mode 100755
index 000000000000..f0d5d772618b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_unmount/zfs_unmount_test.sh
@@ -0,0 +1,308 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zfs_unmount_001_pos cleanup
+zfs_unmount_001_pos_head()
+{
+ atf_set "descr" "Verify the u[n]mount [-f] sub-command."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_unmount_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unmount.kshlib
+ . $(atf_get_srcdir)/zfs_unmount.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_unmount_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_unmount_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unmount.kshlib
+ . $(atf_get_srcdir)/zfs_unmount.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_unmount_002_pos cleanup
+zfs_unmount_002_pos_head()
+{
+ atf_set "descr" "Verify that '$ZFS $unmountcmd [-f] <filesystem|mountpoint>'whose name is not in 'zfs list' will fail with return code 1."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_unmount_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unmount.kshlib
+ . $(atf_get_srcdir)/zfs_unmount.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_unmount_002_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_unmount_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unmount.kshlib
+ . $(atf_get_srcdir)/zfs_unmount.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_unmount_003_pos cleanup
+zfs_unmount_003_pos_head()
+{
+ atf_set "descr" "Verify that '$ZFS $unmountcmd [-f] <filesystem|mountpoint>'whose mountpoint property is 'legacy' or 'none' \will fail with return code 1."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_unmount_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unmount.kshlib
+ . $(atf_get_srcdir)/zfs_unmount.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_unmount_003_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_unmount_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unmount.kshlib
+ . $(atf_get_srcdir)/zfs_unmount.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_unmount_004_pos cleanup
+zfs_unmount_004_pos_head()
+{
+ atf_set "descr" "Verify that '$ZFS $unmountcmd [-f] <filesystem|mountpoint>'with an unmounted filesystem will fail with return code 1."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_unmount_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unmount.kshlib
+ . $(atf_get_srcdir)/zfs_unmount.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_unmount_004_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_unmount_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unmount.kshlib
+ . $(atf_get_srcdir)/zfs_unmount.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_unmount_005_pos cleanup
+zfs_unmount_005_pos_head()
+{
+ atf_set "descr" "Verify that '$ZFS $unmountcmd <filesystem|mountpoint>'with a filesystem which mountpoint is currently in use \will fail with return code 1, and forcefully will succeeds as root."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_unmount_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unmount.kshlib
+ . $(atf_get_srcdir)/zfs_unmount.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_unmount_005_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_unmount_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unmount.kshlib
+ . $(atf_get_srcdir)/zfs_unmount.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_unmount_006_pos cleanup
+zfs_unmount_006_pos_head()
+{
+ atf_set "descr" "Re-creating zfs files, 'zfs unmount' still succeed."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_unmount_006_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unmount.kshlib
+ . $(atf_get_srcdir)/zfs_unmount.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_unmount_006_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_unmount_006_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unmount.kshlib
+ . $(atf_get_srcdir)/zfs_unmount.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_unmount_007_neg cleanup
+zfs_unmount_007_neg_head()
+{
+ atf_set "descr" "Badly-formed 'zfs $unmountcmd' with inapplicable scenariosshould return an error."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_unmount_007_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unmount.kshlib
+ . $(atf_get_srcdir)/zfs_unmount.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_unmount_007_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_unmount_007_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unmount.kshlib
+ . $(atf_get_srcdir)/zfs_unmount.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_unmount_008_neg cleanup
+zfs_unmount_008_neg_head()
+{
+ atf_set "descr" "zfs unmount fails with bad parameters or scenarios"
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_unmount_008_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unmount.kshlib
+ . $(atf_get_srcdir)/zfs_unmount.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_unmount_008_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_unmount_008_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unmount.kshlib
+ . $(atf_get_srcdir)/zfs_unmount.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_unmount_009_pos cleanup
+zfs_unmount_009_pos_head()
+{
+ atf_set "descr" "zfs fource unmount and destroy in snapshot directory will not cause error."
+ atf_set "require.progs" "ksh93 zfs zpool"
+}
+zfs_unmount_009_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unmount.kshlib
+ . $(atf_get_srcdir)/zfs_unmount.cfg
+
+ if other_pools_exist; then
+ atf_skip "Can't test unmount -a with existing pools"
+ fi
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_unmount_009_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_unmount_009_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unmount.kshlib
+ . $(atf_get_srcdir)/zfs_unmount.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_unmount_all_001_pos cleanup
+zfs_unmount_all_001_pos_head()
+{
+ atf_set "descr" "Verify that 'zfs $unmountall' succeeds as root,and all available ZFS filesystems are unmounted."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_unmount_all_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unmount.kshlib
+ . $(atf_get_srcdir)/zfs_unmount.cfg
+
+ if other_pools_exist; then
+ atf_skip "Can't test unmount -a with existing pools"
+ fi
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_unmount_all_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_unmount_all_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unmount.kshlib
+ . $(atf_get_srcdir)/zfs_unmount.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zfs_unmount_001_pos
+ atf_add_test_case zfs_unmount_002_pos
+ atf_add_test_case zfs_unmount_003_pos
+ atf_add_test_case zfs_unmount_004_pos
+ atf_add_test_case zfs_unmount_005_pos
+ atf_add_test_case zfs_unmount_006_pos
+ atf_add_test_case zfs_unmount_007_neg
+ atf_add_test_case zfs_unmount_008_neg
+ atf_add_test_case zfs_unmount_009_pos
+ atf_add_test_case zfs_unmount_all_001_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/Makefile
new file mode 100644
index 000000000000..eeaceb593ef7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/Makefile
@@ -0,0 +1,20 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zfs_unshare
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zfs_unshare_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zfs_unshare_003_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zfs_unshare_002_pos.ksh
+${PACKAGE}FILES+= zfs_unshare.cfg
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zfs_unshare_005_neg.ksh
+${PACKAGE}FILES+= zfs_unshare_001_pos.ksh
+${PACKAGE}FILES+= zfs_unshare_004_neg.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/cleanup.ksh
new file mode 100644
index 000000000000..713cb16bb31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/setup.ksh
new file mode 100644
index 000000000000..51f94b82f6af
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/setup.ksh
@@ -0,0 +1,33 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+# Make sure NFS server is running before testing.
+setup_nfs_server
+
+DISK=${DISKS%% *}
+default_container_volume_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/zfs_unshare.cfg b/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/zfs_unshare.cfg
new file mode 100644
index 000000000000..9811fac0c0ac
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/zfs_unshare.cfg
@@ -0,0 +1,30 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
+
+export NONEXISTFSNAME="nonexistfs50charslong_0123456789012345678901234567"
+export NONEXISTMOUNTPOINT="/nonexistmountpoint_0123456789"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/zfs_unshare_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/zfs_unshare_001_pos.ksh
new file mode 100644
index 000000000000..6d56ee1f29bc
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/zfs_unshare_001_pos.ksh
@@ -0,0 +1,190 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_unshare_001_pos
+#
+# DESCRIPTION:
+# Verify that 'zfs unshare <filesystem|mountpoint>' unshares a given shared
+# filesystem.
+#
+# STRATEGY:
+# 1. Share filesystems
+# 2. Invoke 'zfs unshare <filesystem|mountpoint>' to unshare zfs file system
+# 3. Verify that the file system is unshared
+# 4. Verify that unsharing an unshared file system fails
+# 5. Verify that "zfs unshare -a" succeeds to unshare all zfs file systems.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-18)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ typeset -i i=0
+ while (( i < ${#mntp_fs[*]} )); do
+ log_must $ZFS set sharenfs=off ${mntp_fs[((i+1))]}
+
+ ((i = i + 2))
+ done
+
+ if mounted $TESTPOOL/$TESTCLONE; then
+ log_must $ZFS unmount $TESTDIR2
+ fi
+
+ [[ -d $TESTDIR2 ]] && \
+ log_must $RM -rf $TESTDIR2
+
+ if datasetexists "$TESTPOOL/$TESTCLONE"; then
+ log_must $ZFS destroy -f $TESTPOOL/$TESTCLONE
+ fi
+
+ if snapexists "$TESTPOOL/$TESTFS2@snapshot"; then
+ log_must $ZFS destroy -f $TESTPOOL/$TESTFS2@snapshot
+ fi
+
+ if datasetexists "$TESTPOOL/$TESTFS2"; then
+ log_must $ZFS destroy -f $TESTPOOL/$TESTFS2
+ fi
+}
+
+#
+# Main test routine.
+#
+# Given a mountpoint and file system this routine will attempt
+# unshare the filesystem via <filesystem|mountpoint> argument
+# and then verify it has been unshared.
+#
+function test_unshare # <mntp> <filesystem>
+{
+ typeset mntp=$1
+ typeset filesystem=$2
+ typeset prop_value
+
+ prop_value=$(get_prop "sharenfs" $filesystem)
+
+ if [[ $prop_value == "off" ]]; then
+ not_shared $mntp ||
+ log_must $UNSHARE -F nfs $mntp
+ log_must $ZFS set sharenfs=on $filesystem
+ is_shared $mntp || \
+ log_fail "'$ZFS set sharenfs=on' fails to make" \
+ "file system $filesystem shared."
+ fi
+
+ is_shared $mntp || \
+ log_must $ZFS share $filesystem
+
+ #
+ # Verify 'zfs unshare <filesystem>' works as well.
+ #
+ log_must $ZFS unshare $filesystem
+ not_shared $mntp || \
+ log_fail "'zfs unshare <filesystem>' fails"
+
+ log_must $ZFS share $filesystem
+
+ log_must $ZFS unshare $mntp
+ not_shared $mntp || \
+ log_fail "'zfs unshare <mountpoint>' fails"
+
+ log_note "Unsharing an unshared file system fails."
+ log_mustnot $ZFS unshare $filesystem
+ log_mustnot $ZFS unshare $mntp
+}
+
+
+set -A mntp_fs \
+ "$TESTDIR" "$TESTPOOL/$TESTFS" \
+ "$TESTDIR1" "$TESTPOOL/$TESTCTR/$TESTFS1" \
+ "$TESTDIR2" "$TESTPOOL/$TESTCLONE"
+
+log_assert "Verify that 'zfs unshare [-a] <filesystem|mountpoint>' succeeds as root."
+log_onexit cleanup
+
+log_must $ZFS create $TESTPOOL/$TESTFS2
+log_must $ZFS snapshot $TESTPOOL/$TESTFS2@snapshot
+log_must $ZFS clone $TESTPOOL/$TESTFS2@snapshot $TESTPOOL/$TESTCLONE
+log_must $ZFS set mountpoint=$TESTDIR2 $TESTPOOL/$TESTCLONE
+
+#
+# Invoke 'test_unshare' routine to test 'zfs unshare <filesystem|mountpoint>'.
+#
+typeset -i i=0
+while (( i < ${#mntp_fs[*]} )); do
+ test_unshare ${mntp_fs[i]} ${mntp_fs[((i + 1 ))]}
+
+ ((i = i + 2))
+done
+
+log_note "Verify '$ZFS unshare -a' succeds as root."
+
+i=0
+typeset sharenfs_val
+while (( i < ${#mntp_fs[*]} )); do
+ sharenfs_val=$(get_prop "sharenfs" ${mntp_fs[((i+1))]})
+ if [[ $sharenfs_val == "on" ]]; then
+ not_shared ${mntp_fs[i]} && \
+ log_must $ZFS share ${mntp_fs[((i+1))]}
+ else
+ log_must $ZFS set sharenfs=on ${mntp_fs[((i+1))]}
+ is_shared ${mntp_fs[i]} || \
+ log_fail "'$ZFS set sharenfs=on' fails to share filesystem."
+ fi
+
+ ((i = i + 2))
+done
+
+#
+# test 'zfs unshare -a '
+#
+log_must $ZFS unshare -a
+
+#
+# verify all shared filesystems become unshared
+#
+i=0
+while (( i < ${#mntp_fs[*]} )); do
+ not_shared ${mntp_fs[i]} || \
+ log_fail "'$ZFS unshare -a' fails to unshare all shared zfs filesystems."
+
+ ((i = i + 2))
+done
+
+log_pass "'$ZFS unshare [-a] <filesystem|mountpoint>' succeeds as root."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/zfs_unshare_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/zfs_unshare_002_pos.ksh
new file mode 100644
index 000000000000..2ed3dc2b1c4b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/zfs_unshare_002_pos.ksh
@@ -0,0 +1,189 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_unshare_002_pos
+#
+# DESCRIPTION:
+# Verify that 'zfs unshare [-a] <filesystem|mountpoint>' is aware of legacy share.
+#
+# STRATEGY:
+# 1. Set 'zfs set sharenfs=off'
+# 2. Use 'share' to share given filesystem
+# 3. Verify that 'zfs unshare <filesystem|mountpoint>' is aware of legacy share
+# 4. Verify that 'zfs unshare -a' is aware of legacy share.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-28)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ typeset -i i=0
+ while (( i < ${#mntp_fs[*]} )); do
+ is_shared ${mntp_fs[i]} && \
+ log_must $UNSHARE -F nfs ${mntp_fs[i]}
+
+ ((i = i + 2))
+ done
+
+ if mounted $TESTPOOL/$TESTCLONE; then
+ log_must $ZFS unmount $TESTDIR2
+ fi
+
+ [[ -d $TESTDIR2 ]] && \
+ log_must $RM -rf $TESTDIR2
+
+ if datasetexists "$TESTPOOL/$TESTCLONE"; then
+ log_must $ZFS destroy -f $TESTPOOL/$TESTCLONE
+ fi
+
+ if snapexists "$TESTPOOL/$TESTFS2@snapshot"; then
+ log_must $ZFS destroy -f $TESTPOOL/$TESTFS2@snapshot
+ fi
+
+ if datasetexists "$TESTPOOL/$TESTFS2"; then
+ log_must $ZFS destroy -f $TESTPOOL/$TESTFS2
+ fi
+}
+
+#
+# Main test routine.
+#
+# Given a mountpoint and file system this routine will attempt
+# to verify 'zfs unshare' is aware of legacy share.
+#
+function test_legacy_unshare # <mntp> <filesystem>
+{
+ typeset mntp=$1
+ typeset filesystem=$2
+
+ log_must $ZFS set sharenfs=off $filesystem
+ not_shared $mntp || \
+ log_fail "'zfs set sharenfs=off' fails to make ZFS filesystem $filesystem unshared."
+
+ log_must $SHARE -F nfs $mntp
+ is_shared $mntp || \
+ log_fail "'share' command fails to share ZFS file system."
+ #
+ # Verify 'zfs unshare <filesystem>' is aware of legacy share.
+ #
+ log_mustnot $ZFS unshare $filesystem
+ is_shared $mntp || \
+ log_fail "'zfs unshare <filesystem>' fails to be aware" \
+ "of legacy share."
+
+ #
+ # Verify 'zfs unshare <filesystem>' is aware of legacy share.
+ #
+ log_mustnot $ZFS unshare $mntp
+ is_shared $mntp || \
+ log_fail "'zfs unshare <mountpoint>' fails to be aware" \
+ "of legacy share."
+}
+
+
+set -A mntp_fs \
+ "$TESTDIR" "$TESTPOOL/$TESTFS" \
+ "$TESTDIR1" "$TESTPOOL/$TESTCTR/$TESTFS1" \
+ "$TESTDIR2" "$TESTPOOL/$TESTCLONE"
+
+log_assert "Verify that 'zfs unshare [-a]' is aware of legacy share."
+log_onexit cleanup
+
+log_must $ZFS create $TESTPOOL/$TESTFS2
+log_must $ZFS snapshot $TESTPOOL/$TESTFS2@snapshot
+log_must $ZFS clone $TESTPOOL/$TESTFS2@snapshot $TESTPOOL/$TESTCLONE
+log_must $ZFS set mountpoint=$TESTDIR2 $TESTPOOL/$TESTCLONE
+
+#
+# Invoke 'test_legacy_unshare' routine to verify.
+#
+typeset -i i=0
+while (( i < ${#mntp_fs[*]} )); do
+ test_legacy_unshare ${mntp_fs[i]} ${mntp_fs[((i + 1 ))]}
+
+ ((i = i + 2))
+done
+
+
+log_note "Verify '$ZFS unshare -a' is aware of legacy share."
+
+#
+# set the 'sharenfs' property to 'off' for each filesystem
+#
+i=0
+while (( i < ${#mntp_fs[*]} )); do
+ log_must $ZFS set sharenfs=off ${mntp_fs[((i + 1))]}
+ not_shared ${mntp_fs[i]} || \
+ log_fail "'$ZFS set sharenfs=off' unshares file system failed."
+
+ ((i = i + 2))
+done
+
+#
+# Share each of the file systems via legacy share.
+#
+i=0
+while (( i < ${#mntp_fs[*]} )); do
+ $SHARE -F nfs ${mntp_fs[i]}
+ is_shared ${mntp_fs[i]} || \
+ log_fail "'$SHARE' shares ZFS filesystem failed."
+
+ ((i = i + 2))
+done
+
+#
+# Verify that 'zfs unshare -a' is aware of legacy share
+#
+log_must $ZFS unshare -a
+
+#
+# verify ZFS filesystems are still shared
+#
+i=0
+while (( i < ${#mntp_fs[*]} )); do
+ is_shared ${mntp_fs[i]} || \
+ log_fail "'$ZFS unshare -a' fails to be aware of legacy share."
+
+ ((i = i + 2))
+done
+
+log_pass "'$ZFS unshare [-a]' succeeds to be aware of legacy share."
+
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/zfs_unshare_003_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/zfs_unshare_003_pos.ksh
new file mode 100644
index 000000000000..be2553ec7c3a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/zfs_unshare_003_pos.ksh
@@ -0,0 +1,104 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_unshare_003_pos
+#
+# DESCRIPTION:
+# Verify that a file system and its dependent are unshared when turn off sharenfs
+# property.
+#
+# STRATEGY:
+# 1. Create a file system
+# 2. Set the sharenfs property on the file system
+# 3. Create a snapshot
+# 4. Verify that both are shared
+# 5. Turn off the sharenfs property
+# 6. Verify that both are unshared.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-18)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ if snapexists $TESTPOOL/$TESTFS@snapshot; then
+ log_must $ZFS destroy $TESTPOOL/$TESTFS@snapshot
+ fi
+
+ log_must $ZFS set sharenfs=off $TESTPOOL/$TESTFS
+}
+
+#
+# Main test routine.
+#
+# Given a mountpoint and file system this routine will attempt
+# unshare the mountpoint and then verify a snapshot of the mounpoint
+# is also unshared.
+#
+function test_snap_unshare # <mntp> <filesystem>
+{
+ typeset mntp=$1
+ typeset filesystem=$2
+ typeset prop_value
+
+ prop_value=$(get_prop "sharenfs" $filesystem)
+
+ if [[ $prop_value == "off" ]]; then
+ is_shared $mntp || \
+ $UNSHARE -F nfs $mntp
+ log_must $ZFS set sharenfs=on $filesystem
+ fi
+
+ log_must $ZFS set sharenfs=off $filesystem
+
+ not_shared $mntp || \
+ log_fail "File system $filesystem is shared (set sharenfs)."
+
+ not_shared $mntp@snapshot || \
+ log_fail "Snapshot $mntpt@snapshot is shared (set sharenfs)."
+}
+
+log_assert "Verify that a file system and its dependent are unshared."
+log_onexit cleanup
+
+log_must $ZFS snapshot $TESTPOOL/$TESTFS@snapshot
+test_snap_unshare $TESTDIR $TESTPOOL/$TESTFS
+
+log_pass "A file system and its dependent are both unshared as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/zfs_unshare_004_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/zfs_unshare_004_neg.ksh
new file mode 100644
index 000000000000..9532c4fc757a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/zfs_unshare_004_neg.ksh
@@ -0,0 +1,90 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_unshare_004_neg
+#
+# DESCRIPTION:
+# Verify that "zfs unshare" issue error message with badly formed parameter.
+#
+# STRATEGY:
+# 1. Define badly formed parameters
+# 2. Invoke 'zfs unshare'
+# 3. Verify that unshare fails and issue error message.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-18)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+set -A opts "" "$TESTPOOL/$NONEXISTFSNAME" "$NONEEXISTMOUNTPOINT" "-?" "-1" \
+ "-a blah" "$TESTPOOL/$TESTFS $TESTPOOL/$TESTFS1" \
+ "-f $TESTPOOL/$TESTFS $TESTPOOL/$TESTFS1" \
+ "$TESTPOOL/$TESTFS $TESTDIR" "-f $TESTPOOL/$TESTFS $TESTDIR" \
+ "${TESTDIR#/}" "-f ${TESTDIR#/}"
+
+log_assert "Verify that '$ZFS unshare' issue error message with badly formed parameter."
+
+shareval=$(get_prop sharenfs $TESTPOOL/$TESTFS)
+if [[ $shareval == off ]]; then
+ log_must $ZFS set sharenfs=on $TESTPOOL/$TESTFS
+fi
+
+typeset -i i=0
+while [[ $i -lt ${#args[*]} ]]; do
+ log_mustnot $ZFS unshare ${args[i]}
+
+ ((i = i + 1))
+done
+
+#Testing that unsharing unshared filesystem fails.
+mpt=$(get_prop mountpoint $TESTPOOL/$TESTFS)
+log_must $ZFS unshare $TESTPOOL/$TESTFS
+for opt in "" "-f"; do
+ log_mustnot eval "$ZFS unshare $opt $TESTPOOL/$TESTFS >/dev/null 2>&1"
+ log_mustnot eval "$ZFS unshare $opt $mpt >/dev/null 2>&1"
+done
+
+#Testing zfs unshare fails with legacy share set
+log_must $ZFS set sharenfs=off $TESTPOOL/$TESTFS
+for opt in "" "-f"; do
+ log_mustnot eval "$ZFS unshare $opt $TESTPOOL/$TESTFS >/dev/null 2>&1"
+ log_mustnot eval "$ZFS unshare $opt $mpt >/dev/null 2>&1"
+done
+
+log_pass "'$ZFS unshare' fails as expected with badly-formed parameters."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/zfs_unshare_005_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/zfs_unshare_005_neg.ksh
new file mode 100644
index 000000000000..5a86324bffaf
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/zfs_unshare_005_neg.ksh
@@ -0,0 +1,69 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_unshare_005_neg
+#
+# DESCRIPTION:
+# Verify that unsharing a dataset and mountpoint other than filesystem fails.
+#
+# STRATEGY:
+# 1. Create a volume, dataset other than a ZFS file system
+# 2. Verify that the datasets other than file system are not support by 'zfs unshare'.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-18)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+set -A datasets \
+ "$TESTPOOL" "$ZFSROOT/$TESTPOOL" \
+ "$TESTPOOL/$TESTCTR" "$ZFSROOT/$TESTPOOL/$TESTCTR" \
+ "$TESTPOOL/$TESTVOL" "/dev/zvol/$TESTPOOL/$TESTVOL"
+
+log_assert "Verify that unsharing a dataset other than filesystem fails."
+
+typeset -i i=0
+while (( i < ${#datasets[*]} ))
+do
+ log_mustnot $ZFS unshare ${datasets[i]}
+
+ ((i = i + 1))
+done
+
+log_pass "Unsharing datasets other than filesystem failed as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/zfs_unshare_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/zfs_unshare_test.sh
new file mode 100755
index 000000000000..41de3ecad772
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_unshare/zfs_unshare_test.sh
@@ -0,0 +1,150 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zfs_unshare_001_pos cleanup
+zfs_unshare_001_pos_head()
+{
+ atf_set "descr" "Verify that 'zfs unshare [-a] <filesystem|mountpoint>' succeeds as root."
+ atf_set "require.progs" "ksh93 zfs unshare svcs"
+}
+zfs_unshare_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unshare.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_unshare_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_unshare_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unshare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_unshare_002_pos cleanup
+zfs_unshare_002_pos_head()
+{
+ atf_set "descr" "Verify that 'zfs unshare [-a]' is aware of legacy share."
+ atf_set "require.progs" "ksh93 zfs unshare share svcs"
+}
+zfs_unshare_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unshare.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_unshare_002_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_unshare_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unshare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_unshare_003_pos cleanup
+zfs_unshare_003_pos_head()
+{
+ atf_set "descr" "Verify that a file system and its dependent are unshared."
+ atf_set "require.progs" "ksh93 zfs unshare svcs"
+}
+zfs_unshare_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unshare.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_unshare_003_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_unshare_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unshare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_unshare_004_neg cleanup
+zfs_unshare_004_neg_head()
+{
+ atf_set "descr" "Verify that '$ZFS unshare' issue error message with badly formed parameter."
+ atf_set "require.progs" "ksh93 zfs svcs"
+}
+zfs_unshare_004_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unshare.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_unshare_004_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_unshare_004_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unshare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_unshare_005_neg cleanup
+zfs_unshare_005_neg_head()
+{
+ atf_set "descr" "Verify that unsharing a dataset other than filesystem fails."
+ atf_set "require.progs" "ksh93 zfs svcs"
+}
+zfs_unshare_005_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unshare.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_unshare_005_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_unshare_005_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_unshare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zfs_unshare_001_pos
+ atf_add_test_case zfs_unshare_002_pos
+ atf_add_test_case zfs_unshare_003_pos
+ atf_add_test_case zfs_unshare_004_neg
+ atf_add_test_case zfs_unshare_005_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/Makefile
new file mode 100644
index 000000000000..0b01b8f05aba
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/Makefile
@@ -0,0 +1,23 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zfs_upgrade
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zfs_upgrade_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zfs_upgrade.cfg
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zfs_upgrade_004_pos.ksh
+${PACKAGE}FILES+= zfs_upgrade_001_pos.ksh
+${PACKAGE}FILES+= zfs_upgrade_005_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zfs_upgrade.kshlib
+${PACKAGE}FILES+= zfs_upgrade_006_neg.ksh
+${PACKAGE}FILES+= zfs_upgrade_003_pos.ksh
+${PACKAGE}FILES+= zfs_upgrade_007_neg.ksh
+${PACKAGE}FILES+= zfs_upgrade_002_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/cleanup.ksh
new file mode 100644
index 000000000000..52265dd4ef36
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/cleanup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "both"
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/setup.ksh
new file mode 100644
index 000000000000..d656a08d141f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/setup.ksh
@@ -0,0 +1,40 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+# This should have been set by the .cfg script - verify it's set to something
+# (we check that something later on)
+if [ -z "$ZFS_VERSION" ]
+then
+ log_unresolved "Unable to determine ZFS Filesystem version of this machine"
+else
+ log_note "This machine is running ZFS Filesystem version $ZFS_VERSION"
+fi
+
+default_setup "$DISK"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade.cfg b/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade.cfg
new file mode 100644
index 000000000000..3cecbd83a81c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade.cfg
@@ -0,0 +1,30 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# Copyright 2013 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade.kshlib b/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade.kshlib
new file mode 100644
index 000000000000..8ad021544cda
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade.kshlib
@@ -0,0 +1,191 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#
+# For zfs create.
+# When pool version is 15, fs whose version is 4 can be created.
+#
+set -A zpl_create_versions 3 4 5
+set -A spa_create_versions 9 15 24
+
+#
+# For zfs upgrade
+#
+set -A zpl_upgrade_versions 3 4 5
+set -A spa_upgrade_versions 9 15 24
+
+function get_pool_version #pool
+{
+ typeset pool=$1
+ typeset vs=$(get_pool_prop version $pool)
+ if [ "$vs" = "-" ]; then
+ echo 5000
+ else
+ echo "$vs"
+ fi
+}
+
+function default_setup_datasets #rootfs
+{
+ typeset rootfs=$1
+ typeset pool=${rootfs%%/*}
+ typeset -i vp=$(get_pool_version $pool)
+ typeset -i version
+ typeset -i m
+ typeset -i spa_version
+ typeset -i zpl_version
+
+ for version in $ZFS_ALL_VERSIONS ; do
+ typeset verfs
+ eval verfs=\$ZFS_VERSION_$version
+ typeset current_fs=$rootfs/$verfs
+ typeset current_snap=${current_fs}@snap
+ typeset current_clone=$rootfs/clone$verfs
+
+ (( m=0 ))
+ (( spa_version=0 ))
+ while (( m < ${#zpl_create_versions[@]} )); do
+ (( zpl_version=${zpl_create_versions[m]} ))
+ if (( version == zpl_version )); then
+ (( spa_version=${spa_create_versions[m]} ))
+ break
+ fi
+ (( m+=1 ))
+ done
+ if (( spa_version != 0 )) && (( vp < spa_version )); then
+ log_mustnot $ZFS create -o version=${version} ${current_fs}
+ continue
+ fi
+ log_must $ZFS create -o version=${version} ${current_fs}
+ log_must $ZFS snapshot ${current_snap}
+ log_must $ZFS clone ${current_snap} ${current_clone}
+
+ for subversion in $ZFS_ALL_VERSIONS ; do
+ typeset subverfs
+ eval subverfs=\$ZFS_VERSION_$subversion
+
+ (( m=0 ))
+ (( spa_version=0 ))
+ while (( m < ${#zpl_create_versions[@]} )); do
+ (( zpl_version=${zpl_create_versions[m]} ))
+ if (( subversion == zpl_version )); then
+ (( spa_version=${spa_create_versions[m]} ))
+ break
+ fi
+ (( m+=1 ))
+ done
+ if (( spa_version != 0 )) && (( vp < spa_version )); then
+ log_mustnot $ZFS create -o \
+ version=${subversion} ${current_fs}/$subverfs
+ else
+ log_must $ZFS create -o \
+ version=${subversion} ${current_fs}/$subverfs
+ fi
+ done
+ done
+}
+
+function default_cleanup_datasets #rootfs
+{
+ typeset rootfs=$1
+
+ if datasetexists $rootfs ; then
+ log_must $ZFS destroy -Rf $rootfs
+ fi
+
+ if datasetnonexists $rootfs ; then
+ log_must $ZFS create $rootfs
+ fi
+}
+
+function default_check_zfs_upgrade #rootfs
+{
+ typeset rootfs=$1
+ typeset pool=${rootfs%%/*}
+ typeset -i vp="$(get_pool_version $pool)"
+ typeset -i m
+ typeset -i spa_version
+ typeset -i zpl_version
+ typeset newv
+ typeset -i df_ret
+
+ $DF -t zfs / > /dev/null 2>&1
+ df_ret=$?
+
+ for newv in "" $ZFS_VERSION; do
+ default_setup_datasets $rootfs
+ if [[ -n $newv ]]; then
+ opt="-V $newv"
+ else
+ newv=$ZFS_VERSION
+ fi
+
+ (( m=0 ))
+ (( spa_version=0 ))
+ while (( m < ${#zpl_upgrade_versions[@]} )); do
+ (( zpl_version=${zpl_upgrade_versions[m]} ))
+ if (( newv == zpl_version )); then
+ (( spa_version=${spa_upgrade_versions[m]} ))
+ break
+ fi
+ (( m+=1 ))
+ done
+
+ if (( df_ret != 0 )); then
+ if (( spa_version != 0 )) && (( vp < spa_version )); then
+ log_mustnot eval '$ZFS upgrade $opt -a > /dev/null 2>&1'
+ log_must eval '$ZPOOL upgrade $pool > /dev/null 2>&1'
+ vp="$(get_pool_version $pool)"
+ fi
+
+ log_must eval '$ZFS upgrade $opt -a > /dev/null 2>&1'
+
+ for fs in $($ZFS list -rH -t filesystem -o name $rootfs) ; do
+ log_must check_fs_version $fs $newv
+ done
+ fi
+
+ default_cleanup_datasets $rootfs
+ done
+}
+
+function check_fs_version #filesystem version
+{
+ typeset fs=$1
+ typeset -i version=${2:-$ZFS_VERSION}
+
+ if [[ -z $fs ]]; then
+ log_fail "No filesystem specified."
+ fi
+
+ typeset -i curv=$(get_prop version $fs)
+ if (( curv != version )); then
+ return 1
+ fi
+ return 0
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_001_pos.ksh
new file mode 100644
index 000000000000..73e878320e6d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_001_pos.ksh
@@ -0,0 +1,148 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_upgrade/zfs_upgrade.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_upgrade_001_pos
+#
+# DESCRIPTION:
+# Executing 'zfs upgrade' command succeeds, it should report
+# the current system version and list all old-version filesystems.
+# If no old-version filesystems be founded, it prints out
+# "All filesystems are formatted with the current version."
+#
+# STRATEGY:
+# 1. Prepare a set of datasets which contain old-version and current version.
+# 2. Execute 'zfs upgrade', verify return 0, and it prints out
+# the current system version and list all old-version filesystems.
+# 3. Remove all old-version filesystems, then execute 'zfs upgrade' again,
+# verify return 0, and get the expected message.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-25)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if datasetexists $rootfs ; then
+ log_must $ZFS destroy -Rf $rootfs
+ fi
+ log_must $ZFS create $rootfs
+
+ for file in $output $oldoutput ; do
+ if [[ -f $file ]]; then
+ log_must $RM -f $file
+ fi
+ done
+}
+
+log_assert "Executing 'zfs upgrade' command succeeds."
+log_onexit cleanup
+
+rootfs=$TESTPOOL/$TESTFS
+typeset output=$TMPDIR/zfs-versions.${TESTCASE_ID}
+typeset oldoutput=$TMPDIR/zfs-versions-old.${TESTCASE_ID}
+typeset expect_str1="This system is currently running ZFS filesystem version"
+typeset expect_str2="All filesystems are formatted with the current version"
+typeset expect_str3="The following filesystems are out of date, and can be upgraded"
+typeset -i COUNT OLDCOUNT
+
+$ZFS upgrade | $NAWK '$1 ~ "^[0-9]+$" {print $2}'> $oldoutput
+OLDCOUNT=$( $WC -l $oldoutput | $AWK '{print $1}' )
+
+old_datasets=""
+for version in $ZFS_ALL_VERSIONS ; do
+ typeset verfs
+ eval verfs=\$ZFS_VERSION_$version
+ typeset current_fs=$rootfs/$verfs
+ typeset current_snap=${current_fs}@snap
+ typeset current_clone=$rootfs/clone$verfs
+ log_must $ZFS create -o version=${version} ${current_fs}
+ log_must $ZFS snapshot ${current_snap}
+ log_must $ZFS clone ${current_snap} ${current_clone}
+
+ if (( version != $ZFS_VERSION )); then
+ old_datasets="$old_datasets ${current_fs} ${current_clone}"
+ fi
+done
+
+if is_global_zone; then
+ log_must $ZFS create -V 100m $rootfs/$TESTVOL
+fi
+
+log_must eval '$ZFS upgrade > $output 2>&1'
+
+# we also check that the usage message contains at least a description
+# of the current ZFS version.
+log_must eval '$GREP "${expect_str1} $ZFS_VERSION" $output > /dev/null 2>&1'
+$ZFS upgrade | $NAWK '$1 ~ "^[0-9]+$" {print $2}'> $output
+COUNT=$( $WC -l $output | $AWK '{print $1}' )
+
+typeset -i i=0
+for fs in ${old_datasets}; do
+ log_must $GREP "^$fs$" $output
+ (( i = i + 1 ))
+done
+
+if (( i != COUNT - OLDCOUNT )); then
+ $CAT $output
+ log_fail "More old-version filesystems print out than expect."
+fi
+
+for fs in $old_datasets ; do
+ if datasetexists $fs ; then
+ log_must $ZFS destroy -Rf $fs
+ fi
+done
+
+log_must eval '$ZFS upgrade > $output 2>&1'
+log_must eval '$GREP "${expect_str1} $ZFS_VERSION" $output > /dev/null 2>&1'
+if (( OLDCOUNT == 0 )); then
+ log_must eval '$GREP "${expect_str2}" $output > /dev/null 2>&1'
+else
+ log_must eval '$GREP "${expect_str3}" $output > /dev/null 2>&1'
+fi
+$ZFS upgrade | $NAWK '$1 ~ "^[0-9]+$" {print $2}'> $output
+COUNT=$( $WC -l $output | $AWK '{print $1}' )
+
+if (( COUNT != OLDCOUNT )); then
+ $CAT $output
+ log_fail "Unexpect old-version filesystems print out."
+fi
+
+log_pass "Executing 'zfs upgrade' command succeeds."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_002_pos.ksh
new file mode 100644
index 000000000000..bdad1785237d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_002_pos.ksh
@@ -0,0 +1,75 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_upgrade/zfs_upgrade.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_upgrade_002_pos
+#
+# DESCRIPTION:
+# Executing 'zfs upgrade -v ' command succeeds, it should
+# show the info of available versions.
+#
+# STRATEGY:
+# 1. Execute 'zfs upgrade -v', verify return value is 0.
+# 2, Verify all the available versions info are printed out.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-25)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if [[ -f $output ]]; then
+ log_must $RM -f $output
+ fi
+}
+
+log_assert "Executing 'zfs upgrade -v' command succeeds."
+log_onexit cleanup
+
+typeset output=$TMPDIR/zfs-versions.${TESTCASE_ID}
+typeset expect_str1="Initial ZFS filesystem version"
+typeset expect_str2="Enhanced directory entries"
+
+log_must eval '$ZFS upgrade -v > /dev/null 2>&1'
+
+$ZFS upgrade -v | $NAWK '$1 ~ "^[0-9]+$" {print $0}'> $output
+log_must eval '$GREP "${expect_str1}" $output > /dev/null 2>&1'
+log_must eval '$GREP "${expect_str2}" $output > /dev/null 2>&1'
+
+log_pass "Executing 'zfs upgrade -v' command succeeds."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_003_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_003_pos.ksh
new file mode 100644
index 000000000000..a800055b15d6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_003_pos.ksh
@@ -0,0 +1,112 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_upgrade/zfs_upgrade.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_upgrade_003_pos
+#
+# DESCRIPTION:
+# Executing 'zfs upgrade [-V version] filesystem' command succeeds,
+# it could upgrade a filesystem to specific version or current version.
+#
+# STRATEGY:
+# 1. Prepare a set of datasets which contain old-version and current version.
+# 2. Execute 'zfs upgrade [-V version] filesystem', verify return 0,
+# 3. Verify the filesystem be updated as expected.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-25)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if datasetexists $rootfs ; then
+ log_must $ZFS destroy -Rf $rootfs
+ fi
+ log_must $ZFS create $rootfs
+}
+
+function setup_datasets
+{
+ datasets=""
+ for version in $ZFS_ALL_VERSIONS ; do
+ typeset verfs
+ eval verfs=\$ZFS_VERSION_$version
+ typeset current_fs=$rootfs/$verfs
+ typeset current_snap=${current_fs}@snap
+ typeset current_clone=$rootfs/clone$verfs
+ log_must $ZFS create -o version=${version} ${current_fs}
+ log_must $ZFS snapshot ${current_snap}
+ log_must $ZFS clone ${current_snap} ${current_clone}
+ datasets="$datasets ${current_fs} ${current_clone}"
+ done
+}
+
+log_assert "Executing 'zfs upgrade [-V version] filesystem' command succeeds."
+log_onexit cleanup
+
+rootfs=$TESTPOOL/$TESTFS
+typeset datasets
+
+typeset newv
+for newv in "" "current" $ZFS_ALL_VERSIONS; do
+ setup_datasets
+ for fs in $datasets ; do
+ typeset -i oldv=$(get_prop version $fs)
+
+ if [[ -n $newv ]]; then
+ opt="-V $newv"
+ if [[ $newv == current ]]; then
+ newv=$ZFS_VERSION
+ fi
+ else
+ newv=$ZFS_VERSION
+ fi
+
+ if (( newv >= oldv )); then
+ log_must eval '$ZFS upgrade $opt $fs > /dev/null 2>&1'
+ log_must check_fs_version $fs $newv
+ else
+ log_mustnot eval '$ZFS upgrade $opt $fs > /dev/null 2>&1'
+ log_must check_fs_version $fs $oldv
+ fi
+ done
+ cleanup
+done
+
+log_pass "Executing 'zfs upgrade [-V version] filesystem' command succeeds."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_004_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_004_pos.ksh
new file mode 100644
index 000000000000..8ba5c59fd0db
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_004_pos.ksh
@@ -0,0 +1,116 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_upgrade/zfs_upgrade.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_upgrade_004_pos
+#
+# DESCRIPTION:
+# Executing 'zfs upgrade -r [-V version] filesystem' command succeeds,
+# it upgrade filesystem recursively to specific or current version.
+#
+# STRATEGY:
+# 1. Prepare a set of datasets which contain old-version and current version.
+# 2. Execute 'zfs upgrade -r [-V version] filesystem', verify return 0,
+# 3. Verify the filesystem be updated recursively as expected.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-25)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if datasetexists $rootfs ; then
+ log_must $ZFS destroy -Rf $rootfs
+ fi
+ log_must $ZFS create $rootfs
+}
+
+function setup_datasets
+{
+ datasets=""
+ for version in $ZFS_ALL_VERSIONS ; do
+ typeset verfs
+ eval verfs=\$ZFS_VERSION_$version
+ typeset current_fs=$rootfs/$verfs
+ typeset current_snap=${current_fs}@snap
+ typeset current_clone=$rootfs/clone$verfs
+ log_must $ZFS create -o version=${version} ${current_fs}
+ log_must $ZFS snapshot ${current_snap}
+ log_must $ZFS clone ${current_snap} ${current_clone}
+
+ for subversion in $ZFS_ALL_VERSIONS ; do
+ typeset subverfs
+ eval subverfs=\$ZFS_VERSION_$subversion
+ log_must $ZFS create -o version=${subversion} \
+ ${current_fs}/$subverfs
+ done
+ datasets="$datasets ${current_fs}"
+ done
+}
+
+log_assert "Executing 'zfs upgrade -r [-V version] filesystem' command succeeds."
+log_onexit cleanup
+
+rootfs=$TESTPOOL/$TESTFS
+
+typeset datasets
+
+typeset newv
+for newv in "" "current" $ZFS_VERSION; do
+ setup_datasets
+ for topfs in $datasets ; do
+ if [[ -n $newv ]]; then
+ opt="-V $newv"
+ if [[ $newv == current ]]; then
+ newv=$ZFS_VERSION
+ fi
+ else
+ newv=$ZFS_VERSION
+ fi
+
+ log_must eval '$ZFS upgrade -r $opt $topfs > /dev/null 2>&1'
+
+ for fs in $($ZFS list -rH -t filesystem -o name $topfs) ; do
+ log_must check_fs_version $fs $newv
+ done
+ done
+ cleanup
+done
+
+log_pass "Executing 'zfs upgrade -r [-V version] filesystem' command succeeds."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_005_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_005_pos.ksh
new file mode 100644
index 000000000000..09c911848646
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_005_pos.ksh
@@ -0,0 +1,120 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_upgrade/zfs_upgrade.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_upgrade_005_pos
+#
+# DESCRIPTION:
+# Executing 'zfs upgrade [-V version] -a' command succeeds,
+# it upgrade all filesystems to specific or current version.
+#
+# STRATEGY:
+# 1. Prepare a set of datasets which contain old-version and current version.
+# 2. Execute 'zfs upgrade [-V version] -a', verify return 0,
+# 3. Verify all the filesystems be updated as expected.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-25)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if datasetexists $rootfs ; then
+ log_must $ZFS destroy -Rf $rootfs
+ fi
+ log_must $ZFS create $rootfs
+}
+
+function setup_datasets
+{
+ datasets=""
+ for version in $ZFS_ALL_VERSIONS ; do
+ typeset verfs
+ eval verfs=\$ZFS_VERSION_$version
+ typeset current_fs=$rootfs/$verfs
+ typeset current_snap=${current_fs}@snap
+ typeset current_clone=$rootfs/clone$verfs
+ log_must $ZFS create -o version=${version} ${current_fs}
+ log_must $ZFS snapshot ${current_snap}
+ log_must $ZFS clone ${current_snap} ${current_clone}
+
+ for subversion in $ZFS_ALL_VERSIONS ; do
+ typeset subverfs
+ eval subverfs=\$ZFS_VERSION_$subversion
+ log_must $ZFS create -o version=${subversion} \
+ ${current_fs}/$subverfs
+ done
+ datasets="$datasets ${current_fs}"
+ done
+}
+
+log_assert "Executing 'zfs upgrade [-V version] -a' command succeeds."
+
+$DF -t zfs / > /dev/null 2>&1
+if (( $? == 0 )) ; then
+ log_unsupported "This case should not run on ZFS root system"
+fi
+
+log_onexit cleanup
+
+rootfs=$TESTPOOL/$TESTFS
+
+typeset datasets
+
+typeset newv
+for newv in "" "current" $ZFS_VERSION; do
+ setup_datasets
+ if [[ -n $newv ]]; then
+ opt="-V $newv"
+ if [[ $newv == current ]]; then
+ newv=$ZFS_VERSION
+ fi
+ else
+ newv=$ZFS_VERSION
+ fi
+
+ log_must eval '$ZFS upgrade $opt -a > /dev/null 2>&1'
+
+ for fs in $($ZFS list -rH -t filesystem -o name $rootfs) ; do
+ log_must check_fs_version $fs $newv
+ done
+ cleanup
+done
+
+log_pass "Executing 'zfs upgrade [-V version] -a' command succeeds."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_006_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_006_neg.ksh
new file mode 100644
index 000000000000..a06ebf469242
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_006_neg.ksh
@@ -0,0 +1,67 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_upgrade/zfs_upgrade.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_upgrade_006_neg
+#
+# DESCRIPTION:
+# Verify that invalid upgrade parameters and options are caught.
+#
+# STRATEGY:
+# 1. Create a ZFS file system.
+# 2. For each option in the list, try 'zfs upgrade'.
+# 3. Verify that the operation fails as expected.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-26)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+set -A args "" "-?" "-A" "-R" "-b" "-c" "-d" "--invalid" \
+ "-V" "-V $TESTPOOL/$TESTFS" "-V $TESTPOOL $TESTPOOL/$TESTFS"
+
+log_assert "Badly-formed 'zfs upgrade' should return an error."
+
+typeset -i i=1
+while (( i < ${#args[*]} )); do
+ log_mustnot $ZFS upgrade ${args[i]}
+ ((i = i + 1))
+done
+
+log_pass "Badly-formed 'zfs upgrade' fail as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_007_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_007_neg.ksh
new file mode 100644
index 000000000000..15751c48a68a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_007_neg.ksh
@@ -0,0 +1,68 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_upgrade/zfs_upgrade.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_upgrade_007_neg
+#
+# DESCRIPTION:
+# Verify that version should only by '1' '2' or current version,
+# non-digit input are invalid.
+#
+# STRATEGY:
+# 1. For each invalid value of version in the list, try 'zfs upgrade -V version'.
+# 2. Verify that the operation fails as expected.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-26)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+set -A args \
+ "0" "0.000" "0.5" "-1.234" "-1" "1234b" "5678x"
+
+log_assert "Set invalid value or non-digit version should fail as expected."
+
+typeset -i i=0
+while (( i < ${#args[*]} ))
+do
+ log_mustnot $ZFS upgrade -V ${args[i]} $TESTPOOL/$TESTFS
+ ((i = i + 1))
+done
+
+log_pass "Set invalid value or non-digit version fail as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_test.sh
new file mode 100755
index 000000000000..380dfa6e64f5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zfs_upgrade/zfs_upgrade_test.sh
@@ -0,0 +1,223 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zfs_upgrade_001_pos cleanup
+zfs_upgrade_001_pos_head()
+{
+ atf_set "descr" "Executing 'zfs upgrade' command succeeds."
+ atf_set "require.progs" "ksh93 zfs nawk"
+}
+zfs_upgrade_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_upgrade.cfg
+ . $(atf_get_srcdir)/zfs_upgrade.kshlib
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_upgrade_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_upgrade_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_upgrade.cfg
+ . $(atf_get_srcdir)/zfs_upgrade.kshlib
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_upgrade_002_pos cleanup
+zfs_upgrade_002_pos_head()
+{
+ atf_set "descr" "Executing 'zfs upgrade -v' command succeeds."
+ atf_set "require.progs" "ksh93 zfs nawk"
+}
+zfs_upgrade_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_upgrade.cfg
+ . $(atf_get_srcdir)/zfs_upgrade.kshlib
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_upgrade_002_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_upgrade_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_upgrade.cfg
+ . $(atf_get_srcdir)/zfs_upgrade.kshlib
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_upgrade_003_pos cleanup
+zfs_upgrade_003_pos_head()
+{
+ atf_set "descr" "Executing 'zfs upgrade [-V version] filesystem' command succeeds."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_upgrade_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_upgrade.cfg
+ . $(atf_get_srcdir)/zfs_upgrade.kshlib
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_upgrade_003_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_upgrade_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_upgrade.cfg
+ . $(atf_get_srcdir)/zfs_upgrade.kshlib
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_upgrade_004_pos cleanup
+zfs_upgrade_004_pos_head()
+{
+ atf_set "descr" "Executing 'zfs upgrade -r [-V version] filesystem' command succeeds."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_upgrade_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_upgrade.cfg
+ . $(atf_get_srcdir)/zfs_upgrade.kshlib
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_upgrade_004_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_upgrade_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_upgrade.cfg
+ . $(atf_get_srcdir)/zfs_upgrade.kshlib
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_upgrade_005_pos cleanup
+zfs_upgrade_005_pos_head()
+{
+ atf_set "descr" "Executing 'zfs upgrade [-V version] -a' command succeeds."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_upgrade_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_upgrade.cfg
+ . $(atf_get_srcdir)/zfs_upgrade.kshlib
+
+ if other_pools_exist; then
+ atf_skip "Can't test unmount -a with existing pools"
+ fi
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_upgrade_005_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_upgrade_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_upgrade.cfg
+ . $(atf_get_srcdir)/zfs_upgrade.kshlib
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_upgrade_006_neg cleanup
+zfs_upgrade_006_neg_head()
+{
+ atf_set "descr" "Badly-formed 'zfs upgrade' should return an error."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_upgrade_006_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_upgrade.cfg
+ . $(atf_get_srcdir)/zfs_upgrade.kshlib
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_upgrade_006_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_upgrade_006_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_upgrade.cfg
+ . $(atf_get_srcdir)/zfs_upgrade.kshlib
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_upgrade_007_neg cleanup
+zfs_upgrade_007_neg_head()
+{
+ atf_set "descr" "Set invalid value or non-digit version should fail as expected."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zfs_upgrade_007_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_upgrade.cfg
+ . $(atf_get_srcdir)/zfs_upgrade.kshlib
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_upgrade_007_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_upgrade_007_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_upgrade.cfg
+ . $(atf_get_srcdir)/zfs_upgrade.kshlib
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zfs_upgrade_001_pos
+ atf_add_test_case zfs_upgrade_002_pos
+ atf_add_test_case zfs_upgrade_003_pos
+ atf_add_test_case zfs_upgrade_004_pos
+ atf_add_test_case zfs_upgrade_005_pos
+ atf_add_test_case zfs_upgrade_006_neg
+ atf_add_test_case zfs_upgrade_007_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zpool/Makefile
new file mode 100644
index 000000000000..1d09a43ee977
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool/Makefile
@@ -0,0 +1,18 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zpool
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zpool_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zpool_001_neg.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zpool.cfg
+${PACKAGE}FILES+= zpool_003_pos.ksh
+${PACKAGE}FILES+= zpool_002_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool/cleanup.ksh
new file mode 100644
index 000000000000..713cb16bb31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool/setup.ksh
new file mode 100644
index 000000000000..326bc555f71b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool/zpool.cfg b/tests/sys/cddl/zfs/tests/cli_root/zpool/zpool.cfg
new file mode 100644
index 000000000000..99d93c9d9c72
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool/zpool.cfg
@@ -0,0 +1,27 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool/zpool_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool/zpool_001_neg.ksh
new file mode 100644
index 000000000000..865ab651cf23
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool/zpool_001_neg.ksh
@@ -0,0 +1,78 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_001_neg
+#
+# DESCRIPTION:
+# A badly formed sub-command passed to zpool(1) should
+# return an error.
+#
+# STRATEGY:
+# 1. Create an array containg each zpool sub-command name.
+# 2. For each element, execute the sub-command.
+# 3. Verify it returns an error.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+
+set -A args "" "create" "add" "destroy" "import fakepool" \
+ "export fakepool" "create fakepool" "add fakepool" \
+ "create mirror" "create raidz" "create raidz1" \
+ "create mirror fakepool" "create raidz fakepool" \
+ "create raidz1 fakepool" "create raidz2 fakepool" \
+ "create fakepool mirror" "create fakepool raidz" \
+ "create fakepool raidz1" "create fakepool raidz2" \
+ "add fakepool mirror" "add fakepool raidz" \
+ "add fakepool raidz1" "add fakepool raidz2" \
+ "add mirror fakepool" "add raidz fakepool" \
+ "add raidz1 fakepool" "add raidz2 fakepool" \
+ "setvprop" "blah blah" "-%" "--" "--?" "-*" "-="
+
+log_assert "Execute zpool sub-command without proper parameters."
+
+typeset -i i=0
+while [[ $i -lt ${#args[*]} ]]; do
+ log_mustnot $ZPOOL ${args[i]}
+
+ ((i = i + 1))
+done
+
+log_pass "Badly formed zpool sub-commands fail as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool/zpool_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool/zpool_002_pos.ksh
new file mode 100644
index 000000000000..9ea2f40931c3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool/zpool_002_pos.ksh
@@ -0,0 +1,105 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_002_pos
+#
+# DESCRIPTION:
+# With ZFS_ABORT set, all zpool commands should be able to abort and generate a core file.
+#
+# STRATEGY:
+# 1. Create an array of zpool command
+# 2. Execute each command in the array
+# 3. Verify the command aborts and generate a core file
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-29)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ unset ZFS_ABORT
+
+ if [[ -d $corepath ]]; then
+ $RM -rf $corepath
+ fi
+ if poolexists $pool; then
+ log_must $ZPOOL destroy -f $pool
+ fi
+}
+
+log_assert "With ZFS_ABORT set, all zpool commands can abort and generate a core file."
+log_onexit cleanup
+
+#preparation work for testing
+corepath=$TESTDIR/core
+if [[ -d $corepath ]]; then
+ $RM -rf $corepath
+fi
+$MKDIR $corepath
+
+pool=pool.${TESTCASE_ID}
+vdev1=$TESTDIR/file1
+vdev2=$TESTDIR/file2
+vdev3=$TESTDIR/file3
+log_must create_vdevs $vdev1 $vdev2 $vdev3
+
+set -A cmds "create $pool mirror $vdev1 $vdev2" "list $pool" "iostat $pool" \
+ "status $pool" "upgrade $pool" "get delegation $pool" "set delegation=off $pool" \
+ "export $pool" "import -d $TESTDIR $pool" "offline $pool $vdev1" \
+ "online $pool $vdev1" "clear $pool" "detach $pool $vdev2" \
+ "attach $pool $vdev1 $vdev2" "replace $pool $vdev2 $vdev3" \
+ "scrub $pool" "destroy -f $pool"
+
+set -A badparams "" "create" "destroy" "add" "remove" "list *" "iostat" "status" \
+ "online" "offline" "clear" "attach" "detach" "replace" "scrub" \
+ "import" "export" "upgrade" "history -?" "get" "set"
+
+$COREADM -p ${corepath}/core.%f
+export ZFS_ABORT=yes
+
+for subcmd in "${cmds[@]}" "${badparams[@]}"; do
+ $ZPOOL $subcmd >/dev/null 2>&1
+ corefile=${corepath}/core.zpool
+ if [[ ! -e $corefile ]]; then
+ log_fail "$ZPOOL $subcmd cannot generate core file with ZFS_ABORT set."
+ fi
+ $RM -f $corefile
+done
+
+log_pass "With ZFS_ABORT set, zpool command can abort and generate core file as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool/zpool_003_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool/zpool_003_pos.ksh
new file mode 100644
index 000000000000..f76cdc52b064
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool/zpool_003_pos.ksh
@@ -0,0 +1,78 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_003_pos
+#
+# DESCRIPTION:
+# Verify debugging features of zpool such as ABORT and freeze/unfreeze
+# should run successfully.
+#
+# STRATEGY:
+# 1. Create an array containg each zpool options.
+# 2. For each element, execute the zpool command.
+# 3. Verify it run successfully.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-18)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Debugging features of zpool should succeed."
+
+log_must $ZPOOL -? > /dev/null 2>&1
+
+if is_global_zone ; then
+ log_must $ZPOOL freeze $TESTPOOL
+else
+ log_mustnot $ZPOOL freeze $TESTPOOL
+ log_mustnot $ZPOOL freeze ${TESTPOOL%%/*}
+fi
+
+log_mustnot $ZPOOL freeze fakepool
+
+ZFS_ABORT=1; export ZFS_ABORT
+$ZPOOL > /dev/null 2>&1
+typeset ret=$?
+unset ZFS_ABORT
+# Note: "/bin/kill -l $ret" will not recognize the signal number. We must use
+# ksh93's builtin kill command
+if [ `kill -l $ret` != "ABRT" ]; then
+ log_fail "$ZPOOL not dump core by request."
+fi
+
+log_pass "Debugging features of zpool succeed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool/zpool_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zpool/zpool_test.sh
new file mode 100755
index 000000000000..ef510c484e68
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool/zpool_test.sh
@@ -0,0 +1,105 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zpool_001_neg cleanup
+zpool_001_neg_head()
+{
+ atf_set "descr" "Execute zpool sub-command without proper parameters."
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_001_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_002_pos cleanup
+zpool_002_pos_head()
+{
+ atf_set "descr" "With ZFS_ABORT set, all zpool commands can abort and generate a core file."
+ atf_set "require.progs" "ksh93 zpool coreadm"
+}
+zpool_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_002_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_003_pos cleanup
+zpool_003_pos_head()
+{
+ atf_set "descr" "Debugging features of zpool should succeed."
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_003_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zpool_001_neg
+ atf_add_test_case zpool_002_pos
+ atf_add_test_case zpool_003_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_add/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/Makefile
new file mode 100644
index 000000000000..27986f8207fd
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/Makefile
@@ -0,0 +1,26 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zpool_add
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zpool_add_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zpool_add.cfg
+${PACKAGE}FILES+= zpool_add.kshlib
+${PACKAGE}FILES+= zpool_add_001_pos.ksh
+${PACKAGE}FILES+= zpool_add_002_pos.ksh
+${PACKAGE}FILES+= zpool_add_003_pos.ksh
+${PACKAGE}FILES+= zpool_add_004_pos.ksh
+${PACKAGE}FILES+= zpool_add_005_pos.ksh
+${PACKAGE}FILES+= zpool_add_006_pos.ksh
+${PACKAGE}FILES+= zpool_add_007_neg.ksh
+${PACKAGE}FILES+= zpool_add_008_neg.ksh
+${PACKAGE}FILES+= zpool_add_009_neg.ksh
+${PACKAGE}FILES+= zpool_add_010_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_add/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/cleanup.ksh
new file mode 100644
index 000000000000..0e9383880ffd
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/cleanup.ksh
@@ -0,0 +1,34 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_add/zpool_add.kshlib
+
+poolexists $TESTPOOL && \
+ destroy_pool $TESTPOOL
+cleanup_devices $DISKS
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_add/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/setup.ksh
new file mode 100644
index 000000000000..20d100a3e42a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/setup.ksh
@@ -0,0 +1,32 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_add/zpool_add.kshlib
+
+verify_runnable "global"
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add.cfg b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add.cfg
new file mode 100644
index 000000000000..429a76f65413
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add.cfg
@@ -0,0 +1,64 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
+. $STF_SUITE/include/libtest.kshlib
+
+export DISK_ARRAY_NUM=0
+export DISK_ARRAY_LIMIT=4
+export DISKSARRAY=""
+
+#
+# Variables for zpool_add_006
+#
+export STF_TIMEOUT=2400
+export VDEVS_NUM=300
+export FILE_SIZE=100 #100mb
+
+set_disks
+
+export FILESIZE="100m"
+export FILESIZE1="150m"
+export SIZE="150m"
+export SIZE1="250m"
+
+export FILEDISK=filedisk${TESTCASE_ID}
+export FILEDISK0=filedisk0${TESTCASE_ID}
+export FILEDISK1=filedisk1${TESTCASE_ID}
+export FILEDISK2=filedisk2${TESTCASE_ID}
+export FILEDISK3=filedisk3${TESTCASE_ID}
+
+export VOLSIZE=64mb
+
+export BYND_MAX_NAME="byondmaxnamelength\
+012345678901234567890123456789\
+012345678901234567890123456789\
+012345678901234567890123456789\
+012345678901234567890123456789\
+012345678901234567890123456789\
+012345678901234567890123456789\
+012345678901234567890123456789\
+012345678901234567890123456789"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add.kshlib b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add.kshlib
new file mode 100644
index 000000000000..abfd6e6e6575
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add.kshlib
@@ -0,0 +1,79 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#
+# check if the <pool> contains <vdev> ...
+#
+# $1 pool
+# $2..n <vdev> ...
+#
+# Return 0 if <vdev> are contained in the <pool>; 1 if not used; 2 if pool
+# name is missing
+#
+function iscontained
+{
+ typeset pool=$1
+ typeset vdev
+
+ if [[ -z $pool ]]; then
+ log_note "Missing pool name."
+ return 2
+ fi
+
+ shift
+
+ for vdev in $@; do
+
+# remove /dev/ in vdev if there is
+ vdev=${vdev#/dev/}
+
+ $ZPOOL status "$pool" | $AWK '$1 == vdevname {exit 1}' \
+ vdevname=$vdev >/dev/null 2>&1
+ (( $? != 1 )) && \
+ return 1
+ done
+
+ return 0;
+
+}
+
+#
+# Common cleanup routine for partitions used in testing
+#
+function partition_cleanup
+{
+ log_note "Cleaning up partitions..."
+ if [[ -n $DISK ]]; then
+ partition_disk $SIZE $DISK 7
+ else
+ typeset disk=""
+ for disk in $DISK0 $DISK1; do
+ partition_disk $SIZE $disk 7
+ done
+ fi
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_001_pos.ksh
new file mode 100644
index 000000000000..beeb2ca7ca4a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_001_pos.ksh
@@ -0,0 +1,122 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_add/zpool_add.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_add_001_pos
+#
+# DESCRIPTION:
+# 'zpool add <pool> <vdev> ...' can successfully add the specified
+# devices to the given pool
+#
+# STRATEGY:
+# 1. Create a storage pool
+# 2. Add spare devices to the pool
+# 3. Verify the devices are added to the pool successfully
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2005-09-27)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+log_assert "'zpool add <pool> <vdev> ...' can add devices to the pool."
+
+set -A keywords "" "mirror" "raidz" "raidz1" "spare"
+
+set_disks
+
+typeset diskname0=${DISK0#/dev/}
+typeset diskname1=${DISK1#/dev/}
+typeset diskname2=${DISK2#/dev/}
+typeset diskname3=${DISK3#/dev/}
+typeset diskname4=${DISK4#/dev/}
+
+pooldevs="${diskname0}\
+ \"/dev/${diskname0} ${diskname1}\" \
+ \"${diskname0} ${diskname1} ${diskname2}\""
+mirrordevs="\"/dev/${diskname0} ${diskname1}\""
+raidzdevs="\"/dev/${diskname0} ${diskname1}\""
+
+typeset -i i=0
+typeset vdev
+eval set -A poolarray $pooldevs
+eval set -A mirrorarray $mirrordevs
+eval set -A raidzarray $raidzdevs
+
+while (( $i < ${#keywords[*]} )); do
+ case ${keywords[i]} in
+ ""|spare)
+ for vdev in "${poolarray[@]}"; do
+ create_pool "$TESTPOOL" "${diskname3}"
+ log_must poolexists "$TESTPOOL"
+ log_must $ZPOOL add -f "$TESTPOOL" ${keywords[i]} \
+ $vdev
+ log_must iscontained "$TESTPOOL" "$vdev"
+ destroy_pool "$TESTPOOL"
+ done
+
+ ;;
+ mirror)
+ for vdev in "${mirrorarray[@]}"; do
+ create_pool "$TESTPOOL" "${keywords[i]}" \
+ "${diskname3}" "${diskname4}"
+ log_must poolexists "$TESTPOOL"
+ log_must $ZPOOL add "$TESTPOOL" ${keywords[i]} \
+ $vdev
+ log_must iscontained "$TESTPOOL" "$vdev"
+ destroy_pool "$TESTPOOL"
+ done
+
+ ;;
+ raidz|raidz1)
+ for vdev in "${raidzarray[@]}"; do
+ create_pool "$TESTPOOL" "${keywords[i]}" \
+ "${diskname3}" "${diskname4}"
+ log_must poolexists "$TESTPOOL"
+ log_must $ZPOOL add "$TESTPOOL" ${keywords[i]} \
+ $vdev
+ log_must iscontained "$TESTPOOL" "$vdev"
+ destroy_pool "$TESTPOOL"
+ done
+
+ ;;
+ esac
+
+ (( i = i+1 ))
+done
+
+log_pass "'zpool add <pool> <vdev> ...' executes successfully"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_002_pos.ksh
new file mode 100644
index 000000000000..a242389320d3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_002_pos.ksh
@@ -0,0 +1,72 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_add/zpool_add.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_add_002_pos
+#
+# DESCRIPTION:
+# 'zpool add -f <pool> <vdev> ...' can successfully add the specified
+# devices to given pool in some cases.
+#
+# STRATEGY:
+# 1. Create a mirrored pool
+# 2. Without -f option to add 1-way device the mirrored pool will fail
+# 3. Use -f to override the errors to add 1-way device to the mirrored
+# pool
+# 4. Verify the device is added successfully
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-09-29)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+set_disks
+
+log_assert "'zpool add -f <pool> <vdev> ...' can successfully add" \
+ "devices to the pool in some cases."
+
+create_pool "$TESTPOOL" mirror "${DISK0}" "${DISK1}"
+log_must poolexists "$TESTPOOL"
+
+log_mustnot $ZPOOL add "$TESTPOOL" ${DISK2}
+log_mustnot iscontained "$TESTPOOL" "${DISK2}"
+
+log_must $ZPOOL add -f "$TESTPOOL" ${DISK2}
+log_must iscontained "$TESTPOOL" "${DISK2}"
+
+log_pass "'zpool add -f <pool> <vdev> ...' executes successfully."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_003_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_003_pos.ksh
new file mode 100644
index 000000000000..6145117ab539
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_003_pos.ksh
@@ -0,0 +1,75 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_add/zpool_add.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_add_003_pos
+#
+# DESCRIPTION:
+# 'zpool add -n <pool> <vdev> ...' can display the configuration without
+# adding the specified devices to given pool
+#
+# STRATEGY:
+# 1. Create a storage pool
+# 2. Use -n to add a device to the pool
+# 3. Verify the device is not added actually
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-09-29)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+set_disks
+
+log_assert "'zpool add -n <pool> <vdev> ...' can display the configuration" \
+ "without actually adding devices to the pool."
+
+tmpfile="zpool_add_003.tmp${TESTCASE_ID}"
+
+create_pool "$TESTPOOL" "${DISK0}"
+log_must poolexists "$TESTPOOL"
+
+$ZPOOL add -n "$TESTPOOL" ${DISK1} > $tmpfile
+
+log_mustnot iscontained "$TESTPOOL" "${DISK1}"
+
+str="would update '$TESTPOOL' to the following configuration:"
+$CAT $tmpfile | $GREP "$str" >/dev/null 2>&1
+(( $? != 0 )) && \
+ log_fail "'zpool add -n <pool> <vdev> ...' is executed as unexpected"
+
+log_pass "'zpool add -n <pool> <vdev> ...'executes successfully."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_004_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_004_pos.ksh
new file mode 100644
index 000000000000..83afe17763c8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_004_pos.ksh
@@ -0,0 +1,84 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_add/zpool_add.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_add_004_pos
+#
+# DESCRIPTION:
+# 'zpool add <pool> <vdev> ...' can successfully add a zfs volume
+# to the given pool
+#
+# STRATEGY:
+# 1. Create a storage pool and a zfs volume
+# 2. Add the volume to the pool
+# 3. Verify the devices are added to the pool successfully
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2005-09-29)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+set_disks
+
+function cleanup
+{
+ poolexists $TESTPOOL && \
+ destroy_pool "$TESTPOOL"
+
+ datasetexists $TESTPOOL1/$TESTVOL && \
+ log_must $ZFS destroy -f $TESTPOOL1/$TESTVOL
+ poolexists $TESTPOOL1 && \
+ destroy_pool "$TESTPOOL1"
+}
+
+log_assert "'zpool add <pool> <vdev> ...' can add zfs volume to the pool."
+
+log_onexit cleanup
+
+create_pool "$TESTPOOL" "${DISK0}"
+log_must poolexists "$TESTPOOL"
+
+create_pool "$TESTPOOL1" "${DISK1}"
+log_must poolexists "$TESTPOOL1"
+log_must $ZFS create -V $VOLSIZE $TESTPOOL1/$TESTVOL
+
+log_must $ZPOOL add "$TESTPOOL" /dev/zvol/$TESTPOOL1/$TESTVOL
+
+log_must iscontained "$TESTPOOL" "/dev/zvol/$TESTPOOL1/$TESTVOL"
+
+log_pass "'zpool add <pool> <vdev> ...' adds zfs volume to the pool successfully"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_005_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_005_pos.ksh
new file mode 100644
index 000000000000..b8797adea4a3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_005_pos.ksh
@@ -0,0 +1,104 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_add/zpool_add.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_add_005_pos
+#
+# DESCRIPTION:
+# 'zpool add' should return fail if
+# 1. vdev is part of an active pool
+# 2. vdev is currently mounted
+# 3. vdev is in /etc/vfstab
+# 3. vdev is specified as the dedicated dump device
+#
+# STRATEGY:
+# 1. Create case scenarios
+# 2. For each scenario, try to add the device to the pool
+# 3. Verify the add operation get failed
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-09-29)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+set_disks
+
+function cleanup
+{
+ poolexists "$TESTPOOL" && \
+ destroy_pool "$TESTPOOL"
+ poolexists "$TESTPOOL1" && \
+ destroy_pool "$TESTPOOL1"
+
+ log_onfail $UMOUNT $TMPDIR/mounted_dir
+ log_onfail $SWAPOFF $swap_dev
+ log_onfail $DUMPON -r $dump_dev
+}
+
+log_assert "'zpool add' should fail with inapplicable scenarios."
+
+log_onexit cleanup
+
+create_pool "$TESTPOOL" "${DISK0}"
+log_must poolexists "$TESTPOOL"
+
+create_pool "$TESTPOOL1" "${DISK1}"
+log_must poolexists "$TESTPOOL1"
+
+mounted_dev=${DISK2}
+log_must $MKDIR $TMPDIR/mounted_dir
+log_must $NEWFS $mounted_dev
+log_must $MOUNT $mounted_dev $TMPDIR/mounted_dir
+
+swap_dev=${DISK3}
+log_must $SWAPON $swap_dev
+
+dump_dev=${DISK4}
+log_must $DUMPON $dump_dev
+
+log_mustnot $ZPOOL add -f "$TESTPOOL" ${DISK1}
+
+log_mustnot $ZPOOL add -f "$TESTPOOL" $mounted_dev
+
+log_mustnot $ZPOOL add -f "$TESTPOOL" $swap_dev
+
+# https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=241070
+# When that bug is fixed, change this to log_mustnot.
+log_must $ZPOOL add -f "$TESTPOOL" $dump_dev
+
+log_pass "'zpool add' should fail with inapplicable scenarios."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_006_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_006_pos.ksh
new file mode 100644
index 000000000000..6f67a3d0370f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_006_pos.ksh
@@ -0,0 +1,145 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_add/zpool_add.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_add_006_pos
+#
+# DESCRIPTION:
+# 'zpool add [-f]' can add large numbers of file-in-zfs-filesystem-based vdevs
+# to the specified pool without any errors.
+#
+# STRATEGY:
+# 1. Create assigned number of files in ZFS filesystem as vdevs and use the first
+# file to create a pool
+# 2. Add other vdevs to the pool should get success
+# 3 Fill in the filesystem and create a partially written file
+# as vdev
+# 4. Add the new file into the pool should be failed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-10-09)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TESTPOOL1 && \
+ destroy_pool $TESTPOOL1
+
+ poolexists $TESTPOOL && \
+ destroy_pool $TESTPOOL
+
+ if [[ -d $TESTDIR ]]; then
+ log_must $RM -rf $TESTDIR
+ fi
+}
+
+
+#
+# Create a pool and fs on the assigned disk, and dynamically create large
+# numbers of files as vdevs.(the default value is <VDEVS_NUM>)
+# the first file will be used to create a pool for other vdevs to be added into
+#
+
+function setup_vdevs #<disk>
+{
+ typeset disk=$1
+ typeset -i count=0
+ typeset -i largest_num=0
+ typeset -i slicesize=0
+ typeset vdev=""
+
+ fs_size=$(get_available_disk_size $disk)
+
+ # 64M is the minimum size for the pool
+ (( largest_num = fs_size / (1024 * 1024 * 64) ))
+ if (( largest_num < $VDEVS_NUM )); then
+ # Minus $largest_num/20 to leave 5% space for metadata.
+ (( vdevs_num=largest_num - largest_num/20 ))
+ file_size=64
+ else
+ vdevs_num=$VDEVS_NUM
+ (( file_size = fs_size / (1024 * 1024 * (vdevs_num + vdevs_num/20)) ))
+ if (( file_size > FILE_SIZE )); then
+ file_size=$FILE_SIZE
+ fi
+ # Plus $vdevs_num/20 to provide enough space for metadata.
+ (( slice_size = file_size * (vdevs_num + vdevs_num/20) ))
+ wipe_partition_table $disk
+ set_partition 0 "" ${slice_size}m $disk
+ fi
+ vdev=${disk}
+
+ create_pool $TESTPOOL $vdev
+ [[ -d $TESTDIR ]] && \
+ log_must $RM -rf $TESTDIR
+ log_must $MKDIR -p $TESTDIR
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+
+# Create a pool first using the first file, and make subsequent files ready
+# as vdevs to add to the pool
+
+ vdev=${TESTDIR}/file.$count
+ VDEV_SIZE=${file_size}m
+ log_must create_vdevs ${TESTDIR}/file.$count
+ create_pool "$TESTPOOL1" "${TESTDIR}/file.$count"
+ log_must poolexists "$TESTPOOL1"
+
+ while (( count < vdevs_num )); do # minus 1 to avoid space non-enough
+ (( count = count + 1 ))
+ log_must create_vdevs ${TESTDIR}/file.$count
+ vdevs_list="$vdevs_list ${TESTDIR}/file.$count"
+ done
+ unset VDEV_SIZE
+}
+
+log_assert " 'zpool add [-f]' can add large numbers of vdevs to the specified" \
+ " pool without any errors."
+log_onexit cleanup
+
+vdevs_list=""
+vdevs_num=$VDEVS_NUM
+file_size=$FILE_SIZE
+
+setup_vdevs $DISK0
+log_must $ZPOOL add -f "$TESTPOOL1" $vdevs_list
+log_must iscontained "$TESTPOOL1" "$vdevs_list"
+
+log_pass "'zpool successfully add [-f]' can add large numbers of vdevs to the" \
+ "specified pool without any errors."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_007_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_007_neg.ksh
new file mode 100644
index 000000000000..0433422b89ba
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_007_neg.ksh
@@ -0,0 +1,71 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_add/zpool_add.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_add_007_neg
+#
+# DESCRIPTION:
+# 'zpool add' should return an error with badly-formed parameters,
+#
+# STRATEGY:
+# 1. Create an array of parameters
+# 2. For each parameter in the array, execute 'zpool add'
+# 3. Verify an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-09-29)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+set_disks
+
+log_assert "'zpool add' should return an error with badly-formed parameters."
+
+set -A args "" "-f" "-n" "-?" "-nf" "-fn" "-f -n" "--f" "-blah" \
+ "-? $TESTPOOL ${DISK1}"
+
+create_pool "$TESTPOOL" "${DISK0}"
+log_must poolexists "$TESTPOOL"
+
+typeset -i i=0
+while (( $i < ${#args[*]} )); do
+ log_mustnot $ZPOOL add ${args[i]}
+ ((i = i + 1))
+done
+
+log_pass "'zpool add' badly formed parameters fail as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_008_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_008_neg.ksh
new file mode 100644
index 000000000000..269ba99265b3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_008_neg.ksh
@@ -0,0 +1,69 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_add/zpool_add.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_add_008_neg
+#
+# DESCRIPTION:
+# 'zpool add' should return an error with nonexistent pools or vdevs
+#
+# STRATEGY:
+# 1. Create an array of parameters which contains nonexistent pools/vdevs
+# 2. For each parameter in the array, execute 'zpool add'
+# 3. Verify an error is returned
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-09-29)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+log_assert "'zpool add' should return an error with nonexistent pools and vdevs"
+
+set -A args "" "-f nonexistent_pool ${DISK1}" \
+ "-f $TESTPOOL nonexistent_vdev"
+
+create_pool "$TESTPOOL" "${DISK0}"
+log_must poolexists "$TESTPOOL"
+
+typeset -i i=0
+while (( $i < ${#args[*]} )); do
+ log_mustnot $ZPOOL add ${args[i]}
+ ((i = i + 1))
+done
+
+log_pass "'zpool add' with nonexistent pools and vdevs fail as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_009_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_009_neg.ksh
new file mode 100644
index 000000000000..7a3a57686052
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_009_neg.ksh
@@ -0,0 +1,66 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_add/zpool_add.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_add_009_neg
+#
+# DESCRIPTION:
+# 'zpool add' should return fail if vdevs are the same or vdev is
+# contained in the given pool
+#
+# STRATEGY:
+# 1. Create a storage pool
+# 2. Add the two same devices to pool A
+# 3. Add the device in pool A to pool A again
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-09-29)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+log_assert "'zpool add' should fail if vdevs are the same or vdev is " \
+ "contained in the given pool."
+
+create_pool "$TESTPOOL" "${DISK1}"
+log_must poolexists "$TESTPOOL"
+
+log_mustnot $ZPOOL add -f "$TESTPOOL" ${DISK0} ${DISK0}
+log_mustnot $ZPOOL add -f "$TESTPOOL" ${DISK1}
+
+log_pass "'zpool add' get fail as expected if vdevs are the same or vdev is " \
+ "contained in the given pool."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_010_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_010_pos.ksh
new file mode 100644
index 000000000000..c0073abea451
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_010_pos.ksh
@@ -0,0 +1,44 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2017 Spectra Logic Corp. All rights reserved.
+# Use is subject to license terms.
+#
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_add/zpool_add.kshlib
+
+verify_runnable "global"
+
+log_assert "'zpool add' can add devices, even if a replacing vdev with a spare child is present"
+
+create_pool $TESTPOOL mirror ${DISK0} ${DISK1}
+# A replacing vdev will automatically detach the older member when resilvering
+# is complete. We don't want that to happen during this test, so write some
+# data just to slow down resilvering.
+$TIMEOUT 60s $DD if=/dev/zero of=/$TESTPOOL/zerofile bs=128k
+log_must $ZPOOL add $TESTPOOL spare ${DISK3}
+log_must $ZPOOL replace $TESTPOOL ${DISK0} ${DISK2}
+log_must $ZPOOL replace $TESTPOOL ${DISK0} ${DISK3}
+log_must $ZPOOL add $TESTPOOL spare ${DISK4}
+
+log_pass "'zpool add <pool> <vdev> ...' executes successfully, even when a replacing vdev with a spare child is present"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_test.sh
new file mode 100755
index 000000000000..6fc0abb35d05
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_test.sh
@@ -0,0 +1,311 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zpool_add_001_pos cleanup
+zpool_add_001_pos_head()
+{
+ atf_set "descr" "'zpool add <pool> <vdev> ...' can add devices to the pool."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 2400
+}
+zpool_add_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_add.kshlib
+ . $(atf_get_srcdir)/zpool_add.cfg
+
+ verify_disk_count "$DISKS" 5
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_add_001_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_add_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_add.kshlib
+ . $(atf_get_srcdir)/zpool_add.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_add_002_pos cleanup
+zpool_add_002_pos_head()
+{
+ atf_set "descr" "'zpool add -f <pool> <vdev> ...' can successfully add devices to the pool in some cases."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 2400
+}
+zpool_add_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_add.kshlib
+ . $(atf_get_srcdir)/zpool_add.cfg
+
+ verify_disk_count "$DISKS" 3
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_add_002_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_add_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_add.kshlib
+ . $(atf_get_srcdir)/zpool_add.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_add_003_pos cleanup
+zpool_add_003_pos_head()
+{
+ atf_set "descr" "'zpool add -n <pool> <vdev> ...' can display the configuration without actually adding devices to the pool."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 2400
+}
+zpool_add_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_add.kshlib
+ . $(atf_get_srcdir)/zpool_add.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_add_003_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_add_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_add.kshlib
+ . $(atf_get_srcdir)/zpool_add.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_add_004_pos cleanup
+zpool_add_004_pos_head()
+{
+ atf_set "descr" "'zpool add <pool> <vdev> ...' can add zfs volume to the pool."
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 2400
+}
+zpool_add_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_add.kshlib
+ . $(atf_get_srcdir)/zpool_add.cfg
+
+ verify_disk_count "$DISKS" 2
+ verify_zvol_recursive
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_add_004_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_add_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_add.kshlib
+ . $(atf_get_srcdir)/zpool_add.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_add_005_pos cleanup
+zpool_add_005_pos_head()
+{
+ atf_set "descr" "'zpool add' should fail with inapplicable scenarios."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 2400
+}
+zpool_add_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_add.kshlib
+ . $(atf_get_srcdir)/zpool_add.cfg
+
+ verify_disk_count "$DISKS" 5
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_add_005_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_add_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_add.kshlib
+ . $(atf_get_srcdir)/zpool_add.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_add_006_pos cleanup
+zpool_add_006_pos_head()
+{
+ atf_set "descr" "'zpool add [-f]' can add large numbers of vdevs to the specified pool without any errors."
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 2400
+}
+zpool_add_006_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_add.kshlib
+ . $(atf_get_srcdir)/zpool_add.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_add_006_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_add_006_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_add.kshlib
+ . $(atf_get_srcdir)/zpool_add.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_add_007_neg cleanup
+zpool_add_007_neg_head()
+{
+ atf_set "descr" "'zpool add' should return an error with badly-formed parameters."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 2400
+}
+zpool_add_007_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_add.kshlib
+ . $(atf_get_srcdir)/zpool_add.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_add_007_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_add_007_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_add.kshlib
+ . $(atf_get_srcdir)/zpool_add.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_add_008_neg cleanup
+zpool_add_008_neg_head()
+{
+ atf_set "descr" "'zpool add' should return an error with nonexistent pools and vdevs"
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 2400
+}
+zpool_add_008_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_add.kshlib
+ . $(atf_get_srcdir)/zpool_add.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_add_008_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_add_008_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_add.kshlib
+ . $(atf_get_srcdir)/zpool_add.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_add_009_neg cleanup
+zpool_add_009_neg_head()
+{
+ atf_set "descr" "'zpool add' should fail if vdevs are the same or vdev iscontained in the given pool."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 2400
+}
+zpool_add_009_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_add.kshlib
+ . $(atf_get_srcdir)/zpool_add.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_add_009_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_add_009_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_add.kshlib
+ . $(atf_get_srcdir)/zpool_add.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+# Regression test for PR 225546. "zpool add" asserts if the pool contains a
+# replacing vdev with a spare child.
+# Assertion failed: (nvlist_lookup_string(cnv, "path", &path) == 0), file /usr/home/alans/freebsd/head/cddl/contrib/opensolaris/cmd/zpool/zpool_vdev.c, line 694. /usr/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_010_pos.ksh[54]: log_must[69]: log_pos: line 206: 27710: Abort(coredump)
+atf_test_case zpool_add_010_pos cleanup
+zpool_add_010_pos_head()
+{
+ atf_set "descr" "'zpool add' can add devices, even if a replacing vdev with a spare child is present"
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_add_010_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_add.kshlib
+ . $(atf_get_srcdir)/zpool_add.cfg
+
+ verify_disk_count "$DISKS" 5
+ ksh93 $(atf_get_srcdir)/zpool_add_010_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_add_010_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_add.kshlib
+ . $(atf_get_srcdir)/zpool_add.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zpool_add_001_pos
+ atf_add_test_case zpool_add_002_pos
+ atf_add_test_case zpool_add_003_pos
+ atf_add_test_case zpool_add_004_pos
+ atf_add_test_case zpool_add_005_pos
+ atf_add_test_case zpool_add_006_pos
+ atf_add_test_case zpool_add_007_neg
+ atf_add_test_case zpool_add_008_neg
+ atf_add_test_case zpool_add_009_neg
+ atf_add_test_case zpool_add_010_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_attach/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zpool_attach/Makefile
new file mode 100644
index 000000000000..d094cf65cbfd
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_attach/Makefile
@@ -0,0 +1,16 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zpool_attach
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zpool_attach_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zpool_attach_001_neg.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zpool_attach.cfg
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_attach/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_attach/cleanup.ksh
new file mode 100644
index 000000000000..9038281882b5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_attach/cleanup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_attach/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_attach/setup.ksh
new file mode 100644
index 000000000000..f0e805f5f8b6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_attach/setup.ksh
@@ -0,0 +1,33 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+DISK=${DISKS%% *}
+
+default_mirror_setup $DISKS
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_attach/zpool_attach.cfg b/tests/sys/cddl/zfs/tests/cli_root/zpool_attach/zpool_attach.cfg
new file mode 100644
index 000000000000..99d93c9d9c72
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_attach/zpool_attach.cfg
@@ -0,0 +1,27 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_attach/zpool_attach_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_attach/zpool_attach_001_neg.ksh
new file mode 100644
index 000000000000..944f28385928
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_attach/zpool_attach_001_neg.ksh
@@ -0,0 +1,91 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_attach_001_neg
+#
+# DESCRIPTION:
+# Executing 'zpool attach' command with bad options fails.
+#
+# STRATEGY:
+# 1. Create an array of badly formed 'zpool attach' options.
+# 2. Execute each element of the array.
+# 3. Verify an error code is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-10-18)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+DISKLIST=$(get_disklist $TESTPOOL)
+
+set -A args "" "-f" "-?" "-z fakepool" "-f fakepool" "-ev fakepool" "fakepool" \
+ "$TESTPOOL" "-t $TESTPOOL/$TESTFS" "-t $TESTPOOL/$TESTFS $DISKLIST" \
+ "$TESTPOOL/$TESTCTR" "-t $TESTPOOL/$TESTCTR/$TESTFS1" \
+ "$TESTPOOL/$TESTCTR $DISKLIST" "-t $TESTPOOL/$TESTVOL" \
+ "$TESTPOOL/$TESTCTR/$TESTFS1 $DISKLIST" \
+ "$TESTPOOL/$TESTVOL $DISKLIST" \
+ "$DISKLIST" \
+ "fakepool fakedevice" "fakepool fakedevice fakenewdevice" \
+ "$TESTPOOL fakedevice" "$TESTPOOL $DISKLIST" \
+ "$TESTPOOL fakedevice fakenewdevice fakenewdevice" \
+ "-f $TESTPOOL" "-f $TESTPOOL/$TESTFS" "-f $TESTPOOL/$TESTFS $DISKLIST" \
+ "-f $TESTPOOL/$TESTCTR" "-f $TESTPOOL/$TESTCTR/$TESTFS1" \
+ "-f $TESTPOOL/$TESTCTR $DISKLIST" "-f $TESTPOOL/$TESTVOL" \
+ "-f $TESTPOOL/$TESTCTR/$TESTFS1 $DISKLIST" \
+ "-f $TESTPOOL/$TESTVOL $DISKLIST" \
+ "-f $DISKLIST" \
+ "-f fakepool fakedevice" "-f fakepool fakedevice fakenewdevice" \
+ "-f $TESTPOOL fakedevice fakenewdevice fakenewdevice" \
+ "-f $TESTPOOL fakedevice" "-f $TESTPOOL $DISKLIST"
+
+log_assert "Executing 'zpool attach' with bad options fails"
+
+if [[ -z $DISKLIST ]]; then
+ log_fail "DISKLIST is empty."
+fi
+
+typeset -i i=0
+
+while [[ $i -lt ${#args[*]} ]]; do
+
+ log_mustnot $ZPOOL attach ${args[$i]}
+
+ (( i = i + 1 ))
+done
+
+log_pass "'zpool attach' command with bad options failed as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_attach/zpool_attach_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zpool_attach/zpool_attach_test.sh
new file mode 100755
index 000000000000..29f62c576938
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_attach/zpool_attach_test.sh
@@ -0,0 +1,55 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zpool_attach_001_neg cleanup
+zpool_attach_001_neg_head()
+{
+ atf_set "descr" "Executing 'zpool attach' with bad options fails"
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_attach_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_attach.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_attach_001_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_attach_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_attach.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zpool_attach_001_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_clear/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zpool_clear/Makefile
new file mode 100644
index 000000000000..de2992fe86b3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_clear/Makefile
@@ -0,0 +1,19 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zpool_clear
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zpool_clear_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zpool_clear_003_neg.ksh
+${PACKAGE}FILES+= zpool_clear_002_neg.ksh
+${PACKAGE}FILES+= zpool_clear.cfg
+${PACKAGE}FILES+= zpool_clear_001_pos.ksh
+${PACKAGE}FILES+= zpool_clear_004_pos.ksh
+${PACKAGE}FILES+= zpool_clear_005_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_clear/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_clear/cleanup.ksh
new file mode 100644
index 000000000000..713cb16bb31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_clear/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_clear/zpool_clear.cfg b/tests/sys/cddl/zfs/tests/cli_root/zpool_clear/zpool_clear.cfg
new file mode 100644
index 000000000000..d8ff47843a33
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_clear/zpool_clear.cfg
@@ -0,0 +1,32 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
+
+export FILESIZE=100m
+export STF_TIMEOUT=2100
+export BLOCKSZ=$(( 1024 * 1024 ))
+export NUM_WRITES=40
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_clear/zpool_clear_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_clear/zpool_clear_001_pos.ksh
new file mode 100644
index 000000000000..059e2beb7bd4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_clear/zpool_clear_001_pos.ksh
@@ -0,0 +1,105 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_clear_001_pos
+#
+# DESCRIPTION:
+# Verify 'zpool clear' can clear pool errors.
+#
+# STRATEGY:
+# 1. Create various configuration pools
+# 2. Make errors to pool
+# 3. Use zpool clear to clear errors
+# 4. Verify the errors has been cleared.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-08-10)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ destroy_pool $TESTPOOL1
+
+ for file in `$LS $TMPDIR/file.*`; do
+ log_must $RM -f $file
+ done
+}
+
+
+log_assert "Verify 'zpool clear' can clear errors of a storage pool."
+log_onexit cleanup
+
+#make raw files to create various configuration pools
+fbase=$TMPDIR/file
+log_must create_vdevs $fbase.0 $fbase.1 $fbase.2
+set -A poolconf "mirror $fbase.0 $fbase.1 $fbase.2" \
+ "raidz1 $fbase.0 $fbase.1 $fbase.2" \
+ "raidz2 $fbase.0 $fbase.1 $fbase.2"
+
+function test_clear
+{
+ typeset type="$1"
+ typeset vdev_arg=""
+
+ log_note "Testing ${type} clear type ..."
+ [ "$type" = "device" ] && vdev_arg="${fbase}.0"
+
+ corrupt_file $TESTPOOL1 /$TESTPOOL1/f
+ log_must $ZPOOL scrub $TESTPOOL1
+ wait_for 20 1 is_pool_scrubbed $TESTPOOL1
+ log_must pool_has_errors $TESTPOOL1
+
+ # zpool clear races with things that set error counts; try several
+ # times in case that race is hit.
+ wait_for 10 1 pool_clear_succeeds $TESTPOOL1 $vdev_arg
+}
+
+for devconf in "${poolconf[@]}"; do
+ # Create the pool and sync out a file to it.
+ log_must $ZPOOL create -f $TESTPOOL1 $devconf
+ log_must $FILE_WRITE -o create -f /$TESTPOOL1/f -b 131072 -c 32
+ log_must $SYNC /$TESTPOOL1
+
+ test_clear "device"
+ test_clear "pool"
+
+ log_must $ZPOOL destroy -f $TESTPOOL1
+done
+
+log_pass "'zpool clear' clears pool errors as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_clear/zpool_clear_002_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_clear/zpool_clear_002_neg.ksh
new file mode 100644
index 000000000000..0b3e9f6e8df7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_clear/zpool_clear_002_neg.ksh
@@ -0,0 +1,83 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_clear_002_neg
+#
+# DESCRIPTION:
+# A badly formed parameter passed to 'zpool clear' should
+# return an error.
+#
+# STRATEGY:
+# 1. Create an array containing bad 'zpool clear' parameters.
+# 2. For each element, execute the sub-command.
+# 3. Verify it returns an error.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-08-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TESTPOOL1 && \
+ log_must $ZPOOL destroy -f $TESTPOOL1
+ [[ -e $file ]] && \
+ log_must $RM -f $file
+}
+
+log_assert "Execute 'zpool clear' using invalid parameters."
+log_onexit cleanup
+
+# Create another pool for negative testing, which clears pool error
+# with vdev device not in the pool vdev devices.
+file=$TMPDIR/file.${TESTCASE_ID}
+log_must create_vdevs $file
+log_must $ZPOOL create $TESTPOOL1 $file
+
+set -A args "" "-?" "--%" "-1234567" "0.0001" "0.7644" "-0.7644" \
+ "blah" "blah $DISK" "$TESTPOOL c0txdx" "$TESTPOOL $file" \
+ "$TESTPOOL c0txdx blah" "$TESTPOOL $file blah"
+
+typeset -i i=0
+while (( i < ${#args[*]} )); do
+ log_mustnot $ZPOOL clear ${args[i]}
+
+ ((i = i + 1))
+done
+
+log_pass "Invalid parameters to 'zpool clear' fail as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_clear/zpool_clear_003_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_clear/zpool_clear_003_neg.ksh
new file mode 100644
index 000000000000..5a16ae3cc764
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_clear/zpool_clear_003_neg.ksh
@@ -0,0 +1,74 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_clear_003_neg
+#
+# DESCRIPTION:
+# Verify 'zpool clear' cannot used on an available spare device.
+#
+# STRATEGY:
+# 1. Create a spare pool.
+# 2. Try to clear the spare device
+# 3. Verify it returns an error.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-08-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TESTPOOL1 && \
+ log_must $ZPOOL destroy -f $TESTPOOL1
+
+ for file in `$LS $TMPDIR/file.*`; do
+ log_must $RM -f $file
+ done
+}
+
+
+log_assert "Verify 'zpool clear' cannot clear error for spare device."
+log_onexit cleanup
+
+# make raw files to create a spare pool
+fbase=$TMPDIR/file
+log_must create_vdevs $fbase.1 $fbase.2 $fbase.3 $fbase.4
+log_must create_pool $TESTPOOL1 raidz $fbase.1 $fbase.2 $fbase.3 spare $fbase.4
+log_mustnot $ZPOOL clear $TESTPOOL1 $fbase.4
+
+log_pass "'zpool clear' works on spare device failed as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_clear/zpool_clear_004_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_clear/zpool_clear_004_pos.ksh
new file mode 100644
index 000000000000..2f2a787c5420
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_clear/zpool_clear_004_pos.ksh
@@ -0,0 +1,95 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2013 Spectra Logic Corp. All rights reserved.
+# Use is subject to license terms.
+#
+#
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_clear_004_pos
+#
+# DESCRIPTION:
+# Verify 'zpool clear' can clear errors on spare devices.
+#
+# We don't need to check whether 'zpool clear' actually clears error counters.
+# zpool_clear_001_pos will do that. We just need to check that it doesn't
+# return an error when used on a spare vdev. This is really a test for whether
+# zpool_find_vdev() from libzfs can work on a spare vdev. Note that we're
+# talking about he mirror-like "spare-0" vdev, not the leaf hotspare vdev.
+#
+# STRATEGY:
+# 1. Create a pool
+# 2. Activate a spare
+# 3. Verify that "zpool clear" on the spare returns no errors
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2013-06-26)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ destroy_pool $TESTPOOL1
+
+ for file in `$LS $TMPDIR/file.*`; do
+ log_must $RM -f $file
+ done
+
+ restart_zfsd
+}
+
+
+log_assert "Verify 'zpool clear' works on spare vdevs"
+log_onexit cleanup
+
+# Stop ZFSD so it won't interfere with our spare device.
+stop_zfsd
+
+#make raw files to create various configuration pools
+fbase=$TMPDIR/file
+log_must create_vdevs $fbase.0 $fbase.1 $fbase.2
+VDEV1=$fbase.0
+VDEV2=$fbase.1
+SDEV=$fbase.2
+typeset devlist="$VDEV1 $VDEV2 spare $SDEV"
+
+log_note "'zpool clear' clears leaf-device error."
+
+
+log_must $ZPOOL create -f $TESTPOOL1 $devlist
+log_must $ZPOOL replace $TESTPOOL1 $VDEV1 $SDEV
+log_must $ZPOOL clear $TESTPOOL1 "spare-0"
+
+log_pass "'zpool clear' works on spare vdevs"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_clear/zpool_clear_005_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_clear/zpool_clear_005_pos.ksh
new file mode 100644
index 000000000000..f5670ea3286b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_clear/zpool_clear_005_pos.ksh
@@ -0,0 +1,74 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# Copyright 2012-2018 Spectra Logic Corporation. All rights reserved.
+# Use is subject to license terms.
+#
+# Portions taken from:
+# $Id$
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/include/libgnop.kshlib
+
+function is_pool_unavail # pool
+{
+ is_pool_state "$1" "UNAVAIL"
+}
+
+log_assert "A pool can come back online after all disks are failed and reactivated"
+
+set_disks
+typeset ALLDISKS="${DISK0} ${DISK1} ${DISK2}"
+typeset ALLNOPS=${ALLDISKS//~(E)([[:space:]]+|$)/.nop\1}
+
+log_must create_gnops $ALLDISKS
+for type in "raidz" "mirror"; do
+ # Create a pool on the supplied disks
+ create_pool $TESTPOOL $type $ALLNOPS
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+
+ # Disable all vdevs. The pool should become UNAVAIL
+ log_must destroy_gnop $DISK0
+ log_must destroy_gnop $DISK1
+ log_must destroy_gnop $DISK2
+ wait_for 5 1 is_pool_unavail $TESTPOOL
+
+ # Renable all vdevs. The pool should become healthy again
+ log_must create_gnop $DISK0
+ log_must create_gnop $DISK1
+ log_must create_gnop $DISK2
+
+ # Manually online the pool
+ log_must $ZPOOL clear $TESTPOOL
+
+ wait_for 5 1 is_pool_healthy $TESTPOOL
+
+ destroy_pool $TESTPOOL
+ log_must $RM -rf /$TESTPOOL
+done
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_clear/zpool_clear_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zpool_clear/zpool_clear_test.sh
new file mode 100755
index 000000000000..06d027d91c1c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_clear/zpool_clear_test.sh
@@ -0,0 +1,150 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zpool_clear_001_pos cleanup
+zpool_clear_001_pos_head()
+{
+ atf_set "descr" "Verify 'zpool clear' can clear errors of a storage pool."
+ atf_set "require.progs" "ksh93 zpool zfs"
+ atf_set "timeout" 2100
+}
+zpool_clear_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_clear.cfg
+
+ ksh93 $(atf_get_srcdir)/zpool_clear_001_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_clear_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_clear.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_clear_002_neg cleanup
+zpool_clear_002_neg_head()
+{
+ atf_set "descr" "Execute 'zpool clear' using invalid parameters."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 2100
+}
+zpool_clear_002_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_clear.cfg
+
+ ksh93 $(atf_get_srcdir)/zpool_clear_002_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_clear_002_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_clear.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_clear_003_neg cleanup
+zpool_clear_003_neg_head()
+{
+ atf_set "descr" "Verify 'zpool clear' cannot clear error for available spare devices."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 2100
+}
+zpool_clear_003_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_clear.cfg
+
+ ksh93 $(atf_get_srcdir)/zpool_clear_003_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_clear_003_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_clear.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case zpool_clear_004_pos cleanup
+zpool_clear_004_pos_head()
+{
+ atf_set "descr" "Verify 'zpool clear' can work on spare vdevs"
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 2100
+}
+zpool_clear_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_clear.cfg
+
+ ksh93 $(atf_get_srcdir)/zpool_clear_004_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_clear_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_clear.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case zpool_clear_005_pos cleanup
+zpool_clear_005_pos_head()
+{
+ atf_set "descr" "'zpool clear' can online an UNAVAIL pool after all vdevs have reappeared"
+ atf_set "require.progs" "ksh93 gnop zpool"
+}
+zpool_clear_005_pos_body()
+{
+ atf_skip "Fails on OpenZFS, causing eventual deadlock. PR tests/248910"
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_clear.cfg
+
+ verify_disk_count "$DISKS" 3
+ ksh93 $(atf_get_srcdir)/zpool_clear_005_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_clear_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_clear.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zpool_clear_001_pos
+ atf_add_test_case zpool_clear_002_neg
+ atf_add_test_case zpool_clear_003_neg
+ atf_add_test_case zpool_clear_004_pos
+ atf_add_test_case zpool_clear_005_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_create/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/Makefile
new file mode 100644
index 000000000000..73026cf21ada
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/Makefile
@@ -0,0 +1,36 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zpool_create
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zpool_create_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zpool_create_003_pos.ksh
+${PACKAGE}FILES+= zpool_create_020_pos.ksh
+${PACKAGE}FILES+= zpool_create_017_neg.ksh
+${PACKAGE}FILES+= zpool_create_012_neg.ksh
+${PACKAGE}FILES+= zpool_create_006_pos.ksh
+${PACKAGE}FILES+= zpool_create_002_pos.ksh
+${PACKAGE}FILES+= zpool_create_021_pos.ksh
+${PACKAGE}FILES+= zpool_create_007_neg.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zpool_create_010_neg.ksh
+${PACKAGE}FILES+= zpool_create_019_pos.ksh
+${PACKAGE}FILES+= zpool_create_008_pos.ksh
+${PACKAGE}FILES+= zpool_create_004_pos.ksh
+${PACKAGE}FILES+= zpool_create_009_neg.ksh
+${PACKAGE}FILES+= zpool_create_022_pos.ksh
+${PACKAGE}FILES+= zpool_create.cfg
+${PACKAGE}FILES+= zpool_create_005_pos.ksh
+${PACKAGE}FILES+= zpool_create_001_pos.ksh
+${PACKAGE}FILES+= zpool_create_023_neg.ksh
+${PACKAGE}FILES+= zpool_create.kshlib
+${PACKAGE}FILES+= zpool_create_015_neg.ksh
+${PACKAGE}FILES+= zpool_create_011_neg.ksh
+${PACKAGE}FILES+= zpool_create_018_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_create/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/cleanup.ksh
new file mode 100644
index 000000000000..ec5faa4f1799
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/cleanup.ksh
@@ -0,0 +1,40 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_create/zpool_create.kshlib
+
+clean_blockfile "$TESTDIR $TESTDIR0 $TESTDIR1"
+
+for pool in $TESTPOOL2 $TESTPOOL1 $TESTPOOL; do
+ if poolexists $pool; then
+ destroy_pool $pool
+ fi
+done
+
+cleanup_devices $DISKS
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_create/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/setup.ksh
new file mode 100644
index 000000000000..3e63741f8221
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/setup.ksh
@@ -0,0 +1,48 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_create/zpool_create.kshlib
+
+verify_runnable "global"
+
+if [[ -n $DISK ]]; then
+ #
+ # Use 'zpool create' to clean up the infomation in
+ # in the given disk to avoid slice overlapping.
+ #
+ cleanup_devices $DISK
+
+ partition_disk $SIZE $DISK 7
+else
+ for disk in `$ECHO $DISKSARRAY`; do
+ cleanup_devices $disk
+
+ partition_disk $SIZE $disk 7
+ done
+fi
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create.cfg b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create.cfg
new file mode 100644
index 000000000000..87fc1e52b7c7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create.cfg
@@ -0,0 +1,72 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
+. $STF_SUITE/include/libtest.kshlib
+
+export DISK_ARRAY_NUM=0
+export DISK_ARRAY_LIMIT=4
+export DISKSARRAY=""
+
+#
+# Variables for zpool_create_005
+#
+export STF_TIMEOUT=2400
+export VDEVS_NUM=300
+export FILE_SIZE=100 #100mb
+export MD_OVERHEAD=10 # 10%
+export POOL_MINSIZE=64 # the minimum size(64m) to create a storage pool
+
+set_disks
+
+export FILESIZE="100m"
+export FILESIZE1="150m"
+export SIZE="200m"
+export SIZE1="250m"
+
+export FILEDISK=filedisk${TESTCASE_ID}
+export FILEDISK0=filedisk0${TESTCASE_ID}
+export FILEDISK1=filedisk1${TESTCASE_ID}
+export FILEDISK2=filedisk2${TESTCASE_ID}
+export FILEDISK3=filedisk3${TESTCASE_ID}
+
+export BYND_MAX_NAME="byondmaxnamelength\
+012345678901234567890123456789\
+012345678901234567890123456789\
+012345678901234567890123456789\
+012345678901234567890123456789\
+012345678901234567890123456789\
+012345678901234567890123456789\
+012345678901234567890123456789\
+012345678901234567890123456789"
+
+export TOOSMALL="toosmall"
+
+export TESTPOOL4=testpool4.${TESTCASE_ID}
+export TESTPOOL5=testpool5.${TESTCASE_ID}
+export TESTPOOL6=testpool6.${TESTCASE_ID}
+
+export CPATH="$TMPDIR/cachefile.${TESTCASE_ID}"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create.kshlib b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create.kshlib
new file mode 100644
index 000000000000..bd01d6056607
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create.kshlib
@@ -0,0 +1,112 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#
+# Given a pool vdevs list, create the pool,verify the created pool,
+# and destroy the pool
+# $1, pool name
+# $2, pool type, mirror, raidz, or none
+# $3, vdevs list
+#
+function create_pool_test
+{
+ typeset pool=$1
+ typeset keywd=$2
+ typeset vdevs
+ eval set -A diskarray $3
+
+ for vdevs in "${diskarray[@]}";do
+ create_pool $pool $keywd $vdevs
+ log_must poolexists $pool
+ destroy_pool $pool
+ done
+}
+
+#
+# Create a ufs file system and make a vdev file on it
+#
+# $1, disk name to create ufs file system
+# $2, file name
+#
+function create_blockfile
+{
+ typeset disk=$1
+ typeset file=$2
+ typeset dir=`$DIRNAME $file`
+
+ if [[ -d $dir ]]; then
+ ismounted $dir ufs && log_must $UMOUNT -f $dir
+ else
+ log_must $MKDIR -p $dir
+ fi
+
+ log_must $NEWFS $disk
+ log_must $MOUNT $disk $dir
+ log_must create_vdevs $file
+}
+
+#
+# Umount the ufs filesystem and remove the mountpoint
+# $1, the mount point
+#
+function clean_blockfile
+{
+ typeset dirs=$1
+
+ for dir in $dirs; do
+ if [[ -d $dir ]]; then
+ if ismounted $dir ufs; then
+ log_must $UMOUNT -f $dir
+ fi
+ log_must $RM -rf $dir
+ fi
+ done
+}
+
+#
+# Find the storage device in /etc/vfstab
+#
+function find_fstab_dev
+{
+ typeset fstab="/etc/fstab"
+ typeset tmpfile="$TMPDIR/fstab.tmp"
+ typeset fstabdev
+ typeset fstabdevs=""
+ typeset line
+
+ $CAT $fstab | $GREP "^/dev" >$tmpfile
+ while read -r line
+ do
+ fstabdev=`$ECHO "$line" | $AWK '{print $1}'`
+ fstabdev=${fstabdev%%:}
+ fstabdevs="$fstabdev $fstabdevs"
+ done <$tmpfile
+
+ $RM -f $tmpfile
+ $ECHO $fstabdevs
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_001_pos.ksh
new file mode 100644
index 000000000000..81bd2f36a2ac
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_001_pos.ksh
@@ -0,0 +1,128 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_create/zpool_create.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_create_001_pos
+#
+# DESCRIPTION:
+# 'zpool create <pool> <vspec> ...' can successfully create a
+# new pool with a name in ZFS namespace.
+#
+# STRATEGY:
+# 1. Create storage pools with a name in ZFS namespace with different
+# vdev specs.
+# 2. Verify the pool created successfully
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TESTPOOL && destroy_pool $TESTPOOL
+
+ clean_blockfile "$TESTDIR0 $TESTDIR1"
+}
+
+log_assert "'zpool create <pool> <vspec> ...' can successfully create" \
+ "a new pool with a name in ZFS namespace."
+
+log_onexit cleanup
+
+set -A keywords "" "mirror" "raidz" "raidz1"
+
+typeset diskname0=${DISK0#/dev/}
+typeset diskname1=${DISK1#/dev/}
+
+case $DISK_ARRAY_NUM in
+0|1)
+ typeset disk=""
+ if (( $DISK_ARRAY_NUM == 0 )); then
+ disk=$DISK
+ else
+ disk=$DISK0
+ fi
+ typeset diskname=${disk#/dev/}
+ create_blockfile ${disk}p5 $TESTDIR0/$FILEDISK0
+ create_blockfile ${disk}p6 $TESTDIR1/$FILEDISK1
+
+ pooldevs="${diskname}p1 \
+ /dev/${diskname}p1 \
+ \"${diskname}p1 ${diskname}p2\" \
+ $TESTDIR0/$FILEDISK0"
+ raidzdevs="\"/dev/${diskname}p1 ${diskname}p2\" \
+ \"${diskname}p1 ${diskname}p2 ${diskname}p3\" \
+ \"${diskname}p1 ${diskname}p2 ${diskname}p3 \
+ ${diskname}p4\"\
+ \"$TESTDIR0/$FILEDISK0 $TESTDIR1/$FILEDISK1\""
+ mirrordevs=$raidzdevs
+ ;;
+2|*)
+ create_blockfile ${DISK0}p5 $TESTDIR0/$FILEDISK0
+ create_blockfile ${DISK1}p5 $TESTDIR1/$FILEDISK1
+
+ pooldevs="${diskname0}p1\
+ \"/dev/${diskname0}p1 ${diskname1}p1\" \
+ \"${diskname0}p1 ${diskname0}p2 ${diskname1}p2\"\
+ \"${diskname0}p1 ${diskname1}p1 ${diskname0}p2\
+ ${diskname1}p2\" \
+ \"$TESTDIR0/$FILEDISK0 $TESTDIR1/$FILEDISK1\""
+ raidzdevs="\"/dev/${diskname0}p1 ${diskname1}p1\" \
+ \"${diskname0}p1 ${diskname0}p2 ${diskname1}p2\"\
+ \"${diskname0}p1 ${diskname1}p1 ${diskname0}p2\
+ ${diskname1}p2\" \
+ \"$TESTDIR0/$FILEDISK0 $TESTDIR1/$FILEDISK1\""
+ mirrordevs=$raidzdevs
+ ;;
+esac
+
+typeset -i i=0
+while (( $i < ${#keywords[*]} )); do
+ case ${keywords[i]} in
+ "")
+ create_pool_test "$TESTPOOL" "${keywords[i]}" "$pooldevs";;
+ mirror)
+ create_pool_test "$TESTPOOL" "${keywords[i]}" "$mirrordevs";;
+ raidz|raidz1)
+ create_pool_test "$TESTPOOL" "${keywords[i]}" "$raidzdevs" ;;
+ esac
+ (( i = i+1 ))
+done
+
+log_pass "'zpool create <pool> <vspec> ...' success."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_002_pos.ksh
new file mode 100644
index 000000000000..7b45c52b6ad2
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_002_pos.ksh
@@ -0,0 +1,116 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_create/zpool_create.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_create_002_pos
+#
+# DESCRIPTION:
+# 'zpool create -f <pool> <vspec> ...' can successfully create a
+# new pool in some cases.
+#
+# STRATEGY:
+# 1. Prepare the scenarios for '-f' option
+# 2. Use -f to override the devices to create new pools
+# 3. Verify the pool created successfully
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ for pool in $TESTPOOL $TESTPOOL1 $TESTPOOL2 $TESTPOOL3 $TESTPOOL4 \
+ $TESTPOOL5 $TESTPOOL6
+ do
+ poolexists $pool && destroy_pool $pool
+ done
+
+ clean_blockfile "$TESTDIR0 $TESTDIR1"
+
+ for file in $TMPDIR/$FILEDISK0 $TMPDIR/$FILEDISK1 $TMPDIR/$FILEDISK2
+ do
+ if [[ -e $file ]]; then
+ $RM -rf $file
+ fi
+ done
+}
+
+log_onexit cleanup
+
+log_assert "'zpool create -f <pool> <vspec> ...' can successfully create" \
+ "a new pool in some cases."
+
+function create_fails_without_force
+{
+ log_mustnot $ZPOOL create $TESTPOOL $*
+ create_pool $TESTPOOL $*
+ destroy_pool $TESTPOOL
+}
+
+[ -n "$DISK" ] && disk=$DISK || disk=$DISK0
+
+create_pool "$TESTPOOL" "${disk}p1"
+log_must $ZPOOL export $TESTPOOL
+log_note "'zpool create' without '-f' will fail " \
+ "while device is belong to an exported pool."
+create_fails_without_force "${disk}p1"
+
+log_assert "'zpool create' mirror without '-f' will fail " \
+ "when vdevs are different sizes."
+VDEV_SIZE=84m
+create_vdevs $TMPDIR/$FILEDISK0
+unset VDEV_SIZE
+log_must create_vdevs $TMPDIR/$FILEDISK1
+create_fails_without_force mirror $TMPDIR/$FILEDISK0 $TMPDIR/$FILEDISK1
+
+log_assert "'zpool create' mirror without '-f' will fail " \
+ "when devices are different types."
+create_vdevs $TMPDIR/$FILEDISK0
+log_mustnot $ZPOOL create "$TESTPOOL4" "mirror" $TMPDIR/$FILEDISK0 ${disk}p3
+create_fails_without_force mirror $TMPDIR/$FILEDISK0 ${disk}p3
+
+log_assert "'zpool create' without '-f' will fail " \
+ "while device is part of potentially active pool."
+create_vdevs $TMPDIR/$FILEDISK1 $TMPDIR/$FILEDISK2
+create_pool "$TESTPOOL5" "mirror" $TMPDIR/$FILEDISK1 $TMPDIR/$FILEDISK2
+log_must $ZPOOL offline $TESTPOOL5 $TMPDIR/$FILEDISK2
+log_must $ZPOOL export $TESTPOOL5
+create_fails_without_force $TMPDIR/$FILEDISK2
+
+log_pass "'zpool create -f <pool> <vspec> ...' success."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_003_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_003_pos.ksh
new file mode 100644
index 000000000000..4789b81f2838
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_003_pos.ksh
@@ -0,0 +1,90 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_create/zpool_create.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_create_003_pos
+#
+# DESCRIPTION:
+# 'zpool create -n <pool> <vspec> ...' can display the configuration without
+# actually creating the pool.
+#
+# STRATEGY:
+# 1. Create storage pool with -n option
+# 2. Verify the pool has not been actually created
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ [[ -e $tmpfile ]] && \
+ log_must $RM -f $tmpfile
+}
+
+tmpfile="$TMPDIR/zpool_create_003.tmp${TESTCASE_ID}"
+
+log_assert "'zpool create -n <pool> <vspec> ...' can display the configureation" \
+ "without actually creating the pool."
+
+log_onexit cleanup
+
+if [[ -n $DISK ]]; then
+ disk=$DISK
+else
+ disk=$DISK0
+fi
+
+#
+# Make sure disk is clean before we use it
+#
+create_pool $TESTPOOL ${disk}p1 > $tmpfile
+destroy_pool $TESTPOOL
+
+$ZPOOL create -n $TESTPOOL ${disk}p1 > $tmpfile
+
+poolexists $TESTPOOL && \
+ log_fail "'zpool create -n <pool> <vspec> ...' fail."
+
+str="would create '$TESTPOOL' with the following layout:"
+$CAT $tmpfile | $GREP "$str" >/dev/null 2>&1
+(( $? != 0 )) && \
+ log_fail "'zpool create -n <pool> <vspec>...' is executed as unexpected."
+
+log_pass "'zpool create -n <pool> <vspec>...' success."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_004_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_004_pos.ksh
new file mode 100644
index 000000000000..56cce0106e47
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_004_pos.ksh
@@ -0,0 +1,89 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_create/zpool_create.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_create_004_pos
+#
+# DESCRIPTION:
+# 'zpool create [-f]' can create a storage pool with large number of
+# file-in-zfs-filesystem-based vdevs without any errors.
+#
+# STRATEGY:
+# 1. Create assigned number of files in ZFS filesystem as vdevs
+# 2. Creating a new pool based on the vdevs should get success
+# 3. Fill in the filesystem and create a partially writen file as vdev
+# 4. Add the new file into vdevs list and create a pool
+# 5. Creating a storage pool with the new vdevs list should be failed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-08-25)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+VDEVS_POOL=create_004_pool
+
+function cleanup
+{
+ destroy_pool $VDEVS_POOL
+ destroy_pool $TESTPOOL2
+ destroy_pool $TESTPOOL1
+ destroy_pool $TESTPOOL
+ [ -d "$TESTDIR" ] && log_must $RM -rf $TESTDIR
+}
+
+log_assert "'zpool create [-f]' can create a pool with $VDEVS_NUM vdevs " \
+ "without any errors."
+log_onexit cleanup
+
+log_note "Creating storage pool with $VDEVS_NUM file vdevs should succeed."
+vdevs_list=""
+file_size=$FILE_SIZE
+
+[ -n "$DISK" ] && disk=$DISK || disk=$DISK0
+create_pool $TESTPOOL $disk
+$ZFS create -o mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+
+for (( i = 0; $i < $VDEVS_NUM; i++ )); do
+ log_must create_vdevs $TESTDIR/vdev.${i}
+ vdevs_list="$vdevs_list $TESTDIR/vdev.${i}"
+done
+
+create_pool $TESTPOOL1 $vdevs_list
+destroy_pool $TESTPOOL1
+
+log_pass "'zpool create [-f]' with $VDEVS_NUM vdevs success."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_005_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_005_pos.ksh
new file mode 100644
index 000000000000..271d0a80e8db
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_005_pos.ksh
@@ -0,0 +1,117 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_create/zpool_create.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_create_005_pos
+#
+# DESCRIPTION:
+# 'zpool create [-R root][-m mountpoint] <pool> <vdev> ...' can create an
+# alternate root pool or a new pool mounted at the specified mountpoint.
+#
+# STRATEGY:
+# 1. Create a pool with '-m' option
+# 2. Verify the pool is mounted at the specified mountpoint
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-08-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TESTPOOL && \
+ log_must $ZPOOL destroy -f $TESTPOOL
+
+ for dir in $TMPDIR/zpool_create_005_pos $TESTDIR1; do
+ [[ -d $dir ]] && $RM -rf $dir
+ done
+}
+
+log_assert "'zpool create [-R root][-m mountpoint] <pool> <vdev> ...' can create" \
+ "an alternate pool or a new pool mounted at the specified mountpoint."
+log_onexit cleanup
+
+set -A pooltype "" "mirror" "raidz" "raidz1" "raidz2"
+
+#prepare raw file for file disk
+TDIR=$TMPDIR/zpool_create_005_pos
+FBASE=$TDIR/file
+log_must $MKDIR $TDIR
+log_must create_vdevs $FBASE.0 $FBASE.1 $FBASE.2 $FBASE.3
+#Remove the directory with name as pool name if it exists
+[[ -d /$TESTPOOL ]] && $RM -rf /$TESTPOOL
+
+for opt in "-R $TESTDIR1" "-m $TESTDIR1" \
+ "-R $TESTDIR1 -m $TESTDIR1" "-m $TESTDIR1 -R $TESTDIR1"
+do
+ i=0
+ while (( i < ${#pooltype[*]} )); do
+ #Remove the testing pool and its mount directory
+ poolexists $TESTPOOL && \
+ log_must $ZPOOL destroy -f $TESTPOOL
+ [[ -d $TESTDIR1 ]] && $RM -rf $TESTDIR1
+ log_must $ZPOOL create $opt $TESTPOOL ${pooltype[i]} \
+ $FBASE.1 $FBASE.2 $FBASE.3
+ mpt=`$ZFS mount | $EGREP "^$TESTPOOL[^/]" | $AWK '{print $2}'`
+ (( ${#mpt} == 0 )) && \
+ log_fail "$TESTPOOL created with $opt is not mounted."
+ mpt_val=$(get_prop "mountpoint" $TESTPOOL)
+ [[ "$mpt" != "$mpt_val" ]] && \
+ log_fail "The value of mountpoint property is different\
+ from the output of zfs mount"
+ if [[ "$opt" == "-R $TESTDIR1" ]]; then
+ expected_mpt=${TESTDIR1}/${TESTPOOL}
+ elif [[ "$opt" == "-m $TESTDIR1" ]]; then
+ expected_mpt=${TESTDIR1}
+ else
+ expected_mpt=${TESTDIR1}${TESTDIR1}
+ fi
+ [[ ! -d $expected_mpt ]] && \
+ log_fail "$expected_mpt is not created auotmatically."
+ [[ "$mpt" != "$expected_mpt" ]] && \
+ log_fail "$expected_mpt is mounted on ${mpt} instead of $expected_mpt."
+
+ [[ -d /$TESTPOOL ]] && \
+ log_fail "The default mountpoint /$TESTPOOL is created" \
+ "while with $opt option."
+
+ (( i = i + 1 ))
+ done
+done
+
+log_pass "'zpool create [-R root][-m mountpoint] <pool> <vdev> ...' works as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_006_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_006_pos.ksh
new file mode 100644
index 000000000000..7fc5ccf32f9a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_006_pos.ksh
@@ -0,0 +1,136 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_create_006_pos
+#
+# DESCRIPTION:
+# Verify zpool create succeed with multiple keywords combination.
+#
+# STRATEGY:
+# 1. Create base filesystem to hold virtual disk files.
+# 2. Create several files >= 64M.
+# 3. Verify 'zpool create' succeed with valid keywords combination.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-08-16)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ datasetexists $TESTPOOL1 && destroy_pool $TESTPOOL1
+ datasetexists $TESTPOOL && destroy_pool $TESTPOOL
+}
+
+
+log_assert "Verify 'zpool create' succeed with keywords combination."
+log_onexit cleanup
+
+create_pool $TESTPOOL $DISKS
+mntpnt=$(get_prop mountpoint $TESTPOOL)
+
+typeset -i i=0
+while ((i < 10)); do
+ log_must create_vdevs $mntpnt/vdev$i
+
+ eval vdev$i=$mntpnt/vdev$i
+ ((i += 1))
+done
+
+set -A valid_args \
+ "mirror $vdev0 $vdev1 $vdev2 mirror $vdev3 $vdev4 $vdev5" \
+ "mirror $vdev0 $vdev1 mirror $vdev2 $vdev3 mirror $vdev4 $vdev5" \
+ "mirror $vdev0 $vdev1 $vdev2 mirror $vdev3 $vdev4 $vdev5 \
+ spare $vdev6" \
+ "mirror $vdev0 $vdev1 mirror $vdev2 $vdev3 mirror $vdev4 $vdev5 \
+ spare $vdev6 $vdev7" \
+ "mirror $vdev0 $vdev1 spare $vdev2 mirror $vdev3 $vdev4" \
+ "raidz $vdev0 $vdev1 $vdev2 raidz1 $vdev3 $vdev4 $vdev5" \
+ "raidz $vdev0 $vdev1 raidz1 $vdev2 $vdev3 raidz $vdev4 $vdev5" \
+ "raidz $vdev0 $vdev1 $vdev2 raidz1 $vdev3 $vdev4 $vdev5 \
+ spare $vdev6" \
+ "raidz $vdev0 $vdev1 raidz1 $vdev2 $vdev3 raidz $vdev4 $vdev5 \
+ spare $vdev6 $vdev7" \
+ "raidz $vdev0 $vdev1 spare $vdev2 raidz $vdev3 $vdev4" \
+ "raidz2 $vdev0 $vdev1 $vdev2 raidz2 $vdev3 $vdev4 $vdev5" \
+ "raidz2 $vdev0 $vdev1 $vdev2 raidz2 $vdev3 $vdev4 $vdev5 \
+ raidz2 $vdev6 $vdev7 $vdev8" \
+ "raidz2 $vdev0 $vdev1 $vdev2 raidz2 $vdev3 $vdev4 $vdev5 \
+ spare $vdev6" \
+ "raidz2 $vdev0 $vdev1 $vdev2 raidz2 $vdev3 $vdev4 $vdev5 \
+ raidz2 $vdev6 $vdev7 $vdev8 spare $vdev9" \
+ "raidz2 $vdev0 $vdev1 $vdev2 spare $vdev3 raidz2 $vdev4 $vdev5 $vdev6"
+
+set -A forced_args \
+ "$vdev0 raidz $vdev1 $vdev2 raidz1 $vdev3 $vdev4 $vdev5" \
+ "$vdev0 raidz2 $vdev1 $vdev2 $vdev3 raidz2 $vdev4 $vdev5 $vdev6" \
+ "$vdev0 mirror $vdev1 $vdev2 mirror $vdev3 $vdev4" \
+ "$vdev0 mirror $vdev1 $vdev2 raidz $vdev3 $vdev4 \
+ raidz2 $vdev5 $vdev6 $vdev7 spare $vdev8" \
+ "$vdev0 mirror $vdev1 $vdev2 spare $vdev3 raidz $vdev4 $vdev5" \
+ "raidz $vdev0 $vdev1 raidz2 $vdev2 $vdev3 $vdev4" \
+ "raidz $vdev0 $vdev1 raidz2 $vdev2 $vdev3 $vdev4 spare $vdev5" \
+ "raidz $vdev0 $vdev1 spare $vdev2 raidz2 $vdev3 $vdev4 $vdev5" \
+ "mirror $vdev0 $vdev1 raidz $vdev2 $vdev3 raidz2 $vdev4 $vdev5 $vdev6" \
+ "mirror $vdev0 $vdev1 raidz $vdev2 $vdev3 \
+ raidz2 $vdev4 $vdev5 $vdev6 spare $vdev7" \
+ "mirror $vdev0 $vdev1 raidz $vdev2 $vdev3 \
+ spare $vdev4 raidz2 $vdev5 $vdev6 $vdev7" \
+ "spare $vdev0 $vdev1 $vdev2 mirror $vdev3 $vdev4 raidz $vdev5 $vdev6"
+
+i=0
+while ((i < ${#valid_args[@]})); do
+ log_must $ZPOOL create $TESTPOOL1 ${valid_args[$i]}
+ $SYNC; $SYNC
+ log_must $ZPOOL destroy -f $TESTPOOL1
+
+ ((i += 1))
+done
+
+i=0
+while ((i < ${#forced_args[@]})); do
+ log_mustnot $ZPOOL create $TESTPOOL1 ${forced_args[$i]}
+ log_must $ZPOOL create -f $TESTPOOL1 ${forced_args[$i]}
+ $SYNC; $SYNC
+ log_must $ZPOOL destroy -f $TESTPOOL1
+
+ ((i += 1))
+done
+
+log_pass "'zpool create' succeed with keywords combination."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_007_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_007_neg.ksh
new file mode 100644
index 000000000000..a01fad37fc68
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_007_neg.ksh
@@ -0,0 +1,92 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_create_007_neg
+#
+# DESCRIPTION:
+# 'zpool create' should return an error with badly formed parameters.
+#
+# STRATEGY:
+# 1. Create an array of parameters
+# 2. For each parameter in the array, execute 'zpool create'
+# 3. Verify an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+if [[ -n $DISK ]]; then
+ disk=$DISK
+else
+ disk=$DISK0
+fi
+
+set -A args "" "-?" "-n" "-f" "-nf" "-fn" "-f -n" "--f" "-e" "-s" \
+ "-m" "-R" "-m -R" "-Rm" "-mR" "-m $TESTDIR $TESTPOOL" \
+ "-R $TESTDIR $TESTPOOL" "-m nodir $TESTPOOL $disk" \
+ "-R nodir $TESTPOOL $disk" "-m nodir -R nodir $TESTPOOL $disk" \
+ "-R nodir -m nodir $TESTPOOL $disk" "-R $TESTDIR -m nodir $TESTPOOL $disk" \
+ "-R nodir -m $TESTDIR $TESTPOOL $disk" \
+ "-blah" "$TESTPOOL" "$TESTPOOL blah" "$TESTPOOL c?t0d0" \
+ "$TESTPOOL c0txd0" "$TESTPOOL c0t0dx" "$TESTPOOL cxtxdx" \
+ "$TESTPOOL mirror" "$TESTPOOL raidz" "$TESTPOOL mirror raidz" \
+ "$TESTPOOL raidz1" "$TESTPOOL mirror raidz1" \
+ "$TESTPOOL mirror c?t?d?" "$TESTPOOL mirror $disk c0t1d?" \
+ "$TESTPOOL RAIDZ ${disk}p1 ${disk}p2" \
+ "$TESTPOOL ${disk}p1 log ${disk}p2 \
+ log ${disk}p3" \
+ "$TESTPOOL ${disk}p1 spare ${disk}p2 \
+ spare ${disk}p3" \
+ "$TESTPOOL RAIDZ1 ${disk}p1 ${disk}p2" \
+ "$TESTPOOL MIRROR $disk" "$TESTPOOL raidz $disk" \
+ "$TESTPOOL raidz1 $disk" \
+ "1tank $disk" "1234 $disk" "?tank $disk" \
+ "tan%k $disk" "ta@# $disk" "tan+k $disk" \
+ "$BYND_MAX_NAME $disk"
+
+log_assert "'zpool create' should return an error with badly-formed parameters."
+log_onexit default_cleanup_noexit
+
+typeset -i i=0
+while [[ $i -lt ${#args[*]} ]]; do
+ log_mustnot $ZPOOL create ${args[i]}
+ ((i = i + 1))
+done
+
+log_pass "'zpool create' with badly formed parameters failed as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_008_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_008_pos.ksh
new file mode 100644
index 000000000000..5774c4fc6f0b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_008_pos.ksh
@@ -0,0 +1,75 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_create/zpool_create.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_create_008_pos
+#
+# DESCRIPTION:
+# 'zpool create' have to use '-f' scenarios
+#
+# STRATEGY:
+# 1. Prepare the scenarios
+# 2. Create pool without '-f' and verify it fails
+# 3. Create pool with '-f' and verify it succeeds
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-08-17)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "'zpool create' have to use '-f' scenarios"
+
+if [[ -n $DISK ]]; then
+ disk=$DISK
+else
+ disk=$DISK0
+fi
+
+# Make the disk is EFI labeled first via pool creation
+create_pool $TESTPOOL $disk
+destroy_pool $TESTPOOL
+
+# exported device to be as spare vdev need -f to create pool
+log_must partition_disk $SIZE $disk 6
+create_pool $TESTPOOL ${disk}p1 ${disk}p2
+log_must $ZPOOL export $TESTPOOL
+log_mustnot $ZPOOL create $TESTPOOL1 ${disk}p3 spare ${disk}p2
+create_pool $TESTPOOL1 ${disk}p3 spare ${disk}p2
+destroy_pool $TESTPOOL1
+
+log_pass "'zpool create' have to use '-f' scenarios"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_009_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_009_neg.ksh
new file mode 100644
index 000000000000..b0c66fe92b27
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_009_neg.ksh
@@ -0,0 +1,97 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_create_009_neg
+#
+# DESCRIPTION:
+# Create a pool with same devices twice or create two pools with same
+# devices, 'zpool create' should failed.
+#
+# STRATEGY:
+# 1. Loop to create the following three kinds of pools.
+# - Regular pool
+# - Mirror
+# - Raidz
+# 2. Create two pools but using the same disks, expect failed.
+# 3. Create one pool but using the same disks twice, expect failed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-08-15)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ typeset dtst
+ typeset disk
+
+ for dtst in $TESTPOOL $TESTPOOL1; do
+ poolexists $dtst && destroy_pool $dtst
+ done
+}
+
+log_assert "Create a pool with same devices twice or create two pools with " \
+ "same devices, 'zpool create' should fail."
+log_onexit cleanup
+
+typeset opt
+for opt in "" "mirror" "raidz" "raidz1"; do
+ typeset disk="$DISKS"
+ (( ${#opt} == 0 )) && disk=${DISKS%% *}
+
+ typeset -i count=$(get_word_count "$disk")
+ if (( count < 2 && ${#opt} != 0 )) ; then
+ continue
+ fi
+
+ # Create two pools but using the same disks.
+ create_pool $TESTPOOL $opt $disk
+ log_mustnot $ZPOOL create -f $TESTPOOL1 $opt $disk
+ destroy_pool $TESTPOOL
+
+ # Create two pools and part of the devices were overlapped
+ create_pool $TESTPOOL $opt $disk
+ log_mustnot $ZPOOL create -f $TESTPOOL1 $opt ${DISKS% *}
+ destroy_pool $TESTPOOL
+
+ # Create one pool but using the same disks twice.
+ log_mustnot $ZPOOL create -f $TESTPOOL $opt $disk $disk
+done
+
+log_pass "Using overlapping or in-use disks to create a new pool fails as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_010_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_010_neg.ksh
new file mode 100644
index 000000000000..c0b8231fd936
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_010_neg.ksh
@@ -0,0 +1,92 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_create_010_neg
+#
+# DESCRIPTION:
+# 'zpool create' should return an error with VDEVsof size <64mb
+#
+# STRATEGY:
+# 1. Create an array of parameters
+# 2. For each parameter in the array, execute 'zpool create'
+# 3. Verify an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-09-30)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+log_assert "'zpool create' should return an error with VDEVs <64mb"
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TOOSMALL && destroy_pool $TOOSMALL
+ poolexists $TESTPOOL1 && destroy_pool $TESTPOOL1
+
+ poolexists $TESTPOOL && destroy_pool $TESTPOOL
+
+ [[ -d $TESTDIR ]] && $RM -rf $TESTDIR
+}
+log_onexit cleanup
+
+if [[ -n $DISK ]]; then
+ disk=$DISK
+else
+ disk=$DISK0
+fi
+
+create_pool $TESTPOOL $disk
+log_must $ZFS create $TESTPOOL/$TESTFS
+log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+
+VDEV_SIZE=63m
+log_must create_vdevs $TESTDIR/file1 $TESTDIR/file2
+unset VDEV_SIZE
+
+set -A args \
+ "$TOOSMALL $TESTDIR/file1" "$TESTPOOL1 $TESTDIR/file1 $TESTDIR/file2" \
+ "$TOOSMALL mirror $TESTDIR/file1 $TESTDIR/file2" \
+ "$TOOSMALL raidz $TESTDIR/file1 $TESTDIR/file2"
+
+typeset -i i=0
+while [[ $i -lt ${#args[*]} ]]; do
+ log_mustnot $ZPOOL create ${args[i]}
+ ((i = i + 1))
+done
+
+log_pass "'zpool create' with badly formed parameters failed as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_011_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_011_neg.ksh
new file mode 100644
index 000000000000..22a5a0aa4e11
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_011_neg.ksh
@@ -0,0 +1,135 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_create/zpool_create.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_create_011_neg
+#
+# DESCRIPTION:
+# 'zpool create' will fail in the following cases:
+# existent pool; device is part of an active pool; nested virtual devices;
+# differently sized devices without -f option; device being currently
+# mounted; devices in /etc/fstab; specified as the dedicated dump device.
+#
+# STRATEGY:
+# 1. Create case scenarios
+# 2. For each scenario, try to create a new pool with the virtual devices
+# 3. Verify the creation is failed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ for pool in $TESTPOOL $TESTPOOL1
+ do
+ destroy_pool $pool
+ done
+
+ if [[ -n $specified_dump_dev ]]; then
+ $DUMPON -r $specified_dump_dev
+ fi
+}
+
+log_assert "'zpool create' should be failed with inapplicable scenarios."
+log_onexit cleanup
+
+if [[ -n $DISK ]]; then
+ disk=$DISK
+else
+ disk=$DISK0
+fi
+pooldev1=${disk}p1
+pooldev2=${disk}p2
+mirror1="${disk}p2 ${disk}p3"
+mirror2="${disk}p4 ${disk}p5"
+raidz1=$mirror1
+raidz2=$mirror2
+diff_size_dev="${disk}p6 ${disk}p7"
+fstab_dev=$(find_fstab_dev)
+specified_dump_dev=${disk}
+
+lba=$(get_partition_end $disk 6)
+$GPART delete -i 7 $disk
+set_partition 7 "$lba" $SIZE1 $disk
+create_pool "$TESTPOOL" "$pooldev1"
+
+#
+# Set up the testing scenarios parameters
+#
+set -A arg "$TESTPOOL $pooldev2" \
+ "$TESTPOOL1 $pooldev1" \
+ "$TESTPOOL1 $TESTDIR0/$FILEDISK0" \
+ "$TESTPOOL1 mirror mirror $mirror1 mirror $mirror2" \
+ "$TESTPOOL1 raidz raidz $raidz1 raidz $raidz2" \
+ "$TESTPOOL1 raidz1 raidz1 $raidz1 raidz1 $raidz2" \
+ "$TESTPOOL1 mirror raidz $raidz1 raidz $raidz2" \
+ "$TESTPOOL1 mirror raidz1 $raidz1 raidz1 $raidz2" \
+ "$TESTPOOL1 raidz mirror $mirror1 mirror $mirror2" \
+ "$TESTPOOL1 raidz1 mirror $mirror1 mirror $mirror2" \
+ "$TESTPOOL1 mirror $diff_size_dev" \
+ "$TESTPOOL1 raidz $diff_size_dev" \
+ "$TESTPOOL1 raidz1 $diff_size_dev" \
+ "$TESTPOOL1 mirror $mirror1 spare $mirror2 spare $diff_size_dev" \
+ "$TESTPOOL1 $fstab_dev" \
+ "$TESTPOOL1 ${disk}s10" \
+ "$TESTPOOL1 spare $pooldev2"
+
+typeset -i i=0
+while (( i < ${#arg[*]} )); do
+ log_mustnot $ZPOOL create ${arg[i]}
+ (( i = i+1 ))
+done
+
+# now destroy the pool to be polite
+log_must $ZPOOL destroy -f $TESTPOOL
+
+# create/destroy a pool as a simple way to set the partitioning
+# back to something normal so we can use this $disk as a dump device
+log_must $ZPOOL create -f $TESTPOOL3 $disk
+log_must $ZPOOL destroy -f $TESTPOOL3
+
+log_must dumpon $specified_dump_dev
+log_mustnot $ZPOOL create -f $TESTPOOL1 "$specified_dump_dev"
+
+# Also check to see that in-use checking prevents us from creating
+# a zpool from just the first slice on the disk.
+log_mustnot $ZPOOL create -f $TESTPOOL1 ${specified_dump_dev}s0
+
+log_pass "'zpool create' is failed as expected with inapplicable scenarios."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_012_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_012_neg.ksh
new file mode 100644
index 000000000000..df28c690b93d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_012_neg.ksh
@@ -0,0 +1,67 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_create_012_neg
+#
+#
+# DESCRIPTION:
+# 'zpool create' will fail with disk in swap
+#
+#
+# STRATEGY:
+# 1. Add a disk to swap
+# 2. Try to create a pool on that disk. It should fail.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-04-17)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ $SWAPOFF $DISK0
+
+}
+
+log_assert "'zpool create' should fail with disk in swap."
+log_onexit cleanup
+
+log_must $SWAPON $DISK0
+log_mustnot $ZPOOL create $TESTPOOL $DISK0
+
+log_pass "'zpool create' cannot use a swap disk"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_015_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_015_neg.ksh
new file mode 100644
index 000000000000..da57ac7c438b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_015_neg.ksh
@@ -0,0 +1,85 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_create_015_neg
+#
+#
+# DESCRIPTION:
+# 'zpool create' will fail with zfs vol device in swap
+#
+#
+# STRATEGY:
+# 1. Create a zpool
+# 2. Create a zfs vol on zpool
+# 3. Add this zfs vol device to swap
+# 4. Try to create a new pool with devices in swap
+# 5. Verify the creation is failed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-04-17)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ $SWAPOFF /dev/zvol/${vol_name}
+}
+
+if [[ -n $DISK ]]; then
+ disk=$DISK
+else
+ disk=$DISK0
+fi
+
+typeset pool_dev=${disk}
+typeset vol_name=$TESTPOOL/$TESTVOL
+
+log_assert "'zpool create' should fail with zfs vol device in swap."
+log_onexit cleanup
+
+#
+# use zfs vol device in swap to create pool which should fail.
+#
+create_pool $TESTPOOL $pool_dev
+log_must $ZFS create -V 100m $vol_name
+log_must $SWAPON /dev/zvol/$vol_name
+for opt in "" "-f"; do
+ log_mustnot $ZPOOL create $opt $TESTPOOL1 /dev/zvol/${vol_name}
+done
+
+log_pass "'zpool create' passed as expected with inapplicable scenario."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_017_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_017_neg.ksh
new file mode 100644
index 000000000000..04680ee90382
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_017_neg.ksh
@@ -0,0 +1,97 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_create_017_neg
+#
+#
+# DESCRIPTION:
+# 'zpool create' will fail with mountpoint exists and is not empty.
+#
+#
+# STRATEGY:
+# 1. Prepare the mountpoint put some stuff into it.
+# 2. Verify 'zpool create' over that mountpoint fails.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-02)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ if poolexists $TESTPOOL; then
+ destroy_pool $TESTPOOL
+ fi
+
+ if [[ -d $TESTDIR ]]; then
+ log_must $RM -rf $TESTDIR
+ fi
+}
+
+if [[ -n $DISK ]]; then
+ disk=$DISK
+else
+ disk=$DISK0
+fi
+
+typeset pool_dev=${disk}p1
+
+log_assert "'zpool create' should fail with mountpoint exists and not empty."
+log_onexit cleanup
+
+if [[ ! -d $TESTDIR ]]; then
+ log_must $MKDIR -p $TESTDIR
+fi
+
+typeset -i i=0
+
+while (( i < 2 )); do
+ log_must $RM -rf $TESTDIR/*
+ if (( i == 0 )); then
+ log_must $MKDIR $TESTDIR/testdir
+ else
+ log_must $TOUCH $TESTDIR/testfile
+ fi
+
+ log_mustnot $ZPOOL create -m $TESTDIR -f $TESTPOOL $pool_dev
+ log_mustnot poolexists $TESTPOOL
+
+ (( i = i + 1 ))
+done
+
+log_pass "'zpool create' fail as expected with mountpoint exists and not empty."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_018_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_018_pos.ksh
new file mode 100644
index 000000000000..d58f411682e2
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_018_pos.ksh
@@ -0,0 +1,115 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_create/zpool_create.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_create_018_pos
+#
+# DESCRIPTION:
+#
+# zpool create can create pools with specified properties
+#
+# STRATEGY:
+# 1. Create a pool with all editable properties
+# 2. Verify those properties are set
+# 3. Create a pool with two properties set
+# 4. Verify both properties are set correctly
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function cleanup
+{
+ destroy_pool $TESTPOOL
+
+ if [[ -f $CPATH ]] ; then
+ log_must $RM $CPATH
+ fi
+}
+
+log_onexit cleanup
+log_assert "zpool create can create pools with specified properties"
+
+if [[ -n $DISK ]]; then
+ disk=$DISK
+else
+ disk=$DISK0
+fi
+
+# we don't include "root" property in this list, as it requires both "cachefile"
+# and "root" to be set at the same time. A test for this is included in
+# ../../root.
+set -A props "autoreplace" "delegation" "cachefile" "version"
+set -A vals "off" "off" "$CPATH" "3"
+
+if pool_prop_exist autoexpand ; then
+ set -A props ${props[*]} "autoexpand"
+ set -A vals ${vals[*]} "on"
+fi
+
+typeset -i i=0;
+while [ $i -lt "${#props[@]}" ]
+do
+ log_must $ZPOOL create -o ${props[$i]}=${vals[$i]} $TESTPOOL $disk
+ RESULT=$(get_pool_prop ${props[$i]} $TESTPOOL)
+ if [[ $RESULT != ${vals[$i]} ]]
+ then
+ $ZPOOL get all $TESTPOOL
+ log_fail "Pool was created without setting the ${props[$i]} property"
+ fi
+ destroy_pool $TESTPOOL
+ (( i = i + 1 ))
+done
+
+# pick two properties, and verify we can create with those as well
+log_must $ZPOOL create -o delegation=off -o cachefile=$CPATH $TESTPOOL $disk
+RESULT=$(get_pool_prop delegation $TESTPOOL)
+if [[ $RESULT != off ]]
+then
+ $ZPOOL get all $TESTPOOL
+ log_fail "Pool created without the delegation prop."
+fi
+
+RESULT=$(get_pool_prop cachefile $TESTPOOL)
+if [[ $RESULT != $CPATH ]]
+then
+ $ZPOOL get all $TESTPOOL
+ log_fail "Pool created without the cachefile prop."
+fi
+
+log_pass "zpool create can create pools with specified properties"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_019_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_019_pos.ksh
new file mode 100644
index 000000000000..430b1816fd51
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_019_pos.ksh
@@ -0,0 +1,86 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_create/zpool_create.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_create_019_pos
+#
+# DESCRIPTION:
+#
+# zpool create cannot create pools specifying readonly properties
+#
+# STRATEGY:
+# 1. Attempt to create a pool, specifying each readonly property in turn
+# 2. Verify the pool was not created
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-08-24)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function cleanup
+{
+ if poolexists $TESTPOOL ; then
+ destroy_pool $TESTPOOL
+ fi
+}
+
+log_onexit cleanup
+
+log_assert "zpool create cannot create pools specifying readonly properties"
+
+if [[ -n $DISK ]]; then
+ disk=$DISK
+else
+ disk=$DISK0
+fi
+
+set -A props "available" "capacity" "guid" "health" "size" "used"
+set -A vals "100" "10" "12345" "HEALTHY" "10" "10"
+
+typeset -i i=0;
+while [ $i -lt "${#props[@]}" ]
+do
+ # try to set each property in the prop list with it's corresponding val
+ log_mustnot $ZPOOL create -o ${props[$i]}=${vals[$i]} $TESTPOOL $disk
+ if poolexists $TESTPOOL
+ then
+ log_fail "$TESTPOOL was created when setting ${props[$i]}!"
+ fi
+ i=$(( $i + 1))
+done
+
+log_pass "zpool create cannot create pools specifying readonly properties"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_020_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_020_pos.ksh
new file mode 100644
index 000000000000..727b16574d55
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_020_pos.ksh
@@ -0,0 +1,118 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_create/zpool_create.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_create_020_pos
+#
+# DESCRIPTION:
+#
+# zpool create -R works as expected
+#
+# STRATEGY:
+# 1. Create a -R altroot pool
+# 2. Verify the pool is mounted at the correct location
+# 3. Verify that cachefile=none for the pool
+# 4. Verify that root=<mountpoint> for the pool
+# 5. Verify that no reference to the pool is found in /etc/zfs/zpool.cache
+
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function cleanup
+{
+ poolexists $TESTPOOL && destroy_pool $TESTPOOL
+ [[ -d ${TESTPOOL}.root ]] && log_must $RM -rf /${TESTPOOL}.root
+}
+
+log_onexit cleanup
+
+log_assert "zpool create -R works as expected"
+
+if [[ -n $DISK ]]; then
+ disk=$DISK
+else
+ disk=$DISK0
+fi
+
+$RM -rf /${TESTPOOL}.root
+log_must $MKDIR /${TESTPOOL}.root
+log_must $ZPOOL create -R /${TESTPOOL}.root $TESTPOOL $disk
+if [ ! -d /${TESTPOOL}.root ]
+then
+ log_fail "Mountpoint was not create when using zpool with -R flag!"
+fi
+
+FS=$($ZFS list $TESTPOOL)
+if [ -z "$FS" ]
+then
+ log_fail "Mounted filesystem at /${TESTPOOL}.root isn't ZFS!"
+fi
+
+log_must $ZPOOL get all $TESTPOOL
+$ZPOOL get all $TESTPOOL > $TMPDIR/values.${TESTCASE_ID}
+
+# check for the cachefile property, verifying that it's set to 'none'
+$GREP "$TESTPOOL[ ]*cachefile[ ]*none" $TMPDIR/values.${TESTCASE_ID} > /dev/null 2>&1
+if [ $? -ne 0 ]
+then
+ log_fail "zpool property \'cachefile\' was not set to \'none\'."
+fi
+
+# check that the root = /mountpoint property is set correctly
+$GREP "$TESTPOOL[ ]*altroot[ ]*/${TESTPOOL}.root" $TMPDIR/values.${TESTCASE_ID} > /dev/null 2>&1
+if [ $? -ne 0 ]
+then
+ log_fail "zpool property root was not found in pool output."
+fi
+
+$RM $TMPDIR/values.${TESTCASE_ID}
+
+# finally, check that the pool has no reference in /etc/zfs/zpool.cache
+if [[ -f /etc/zfs/zpool.cache ]] ; then
+ REF=$($STRINGS /etc/zfs/zpool.cache | $GREP ${TESTPOOL})
+ if [ ! -z "$REF" ]
+ then
+ $STRINGS /etc/zfs/zpool.cache
+ log_fail "/etc/zfs/zpool.cache appears to have a reference to $TESTPOOL"
+ fi
+fi
+
+
+log_pass "zpool create -R works as expected"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_021_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_021_pos.ksh
new file mode 100644
index 000000000000..689fda3aae72
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_021_pos.ksh
@@ -0,0 +1,101 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_create/zfs_create_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_create_021_pos
+#
+# DESCRIPTION:
+# 'zpool create -O property=value pool' can successfully create a pool
+# with correct filesystem property set.
+#
+# STRATEGY:
+# 1. Create a storage pool with -O option
+# 2. Verify the pool created successfully
+# 3. Verify the filesystem property is correctly set
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-04-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+if ! $(check_zpool_opt_support "create" "-O") ; then
+ log_unsupported "-O option is not supported yet."
+fi
+
+verify_runnable "global"
+
+function cleanup
+{
+ destroy_pool $TESTPOOL
+}
+
+log_onexit cleanup
+
+log_assert "'zpool create -O property=value pool' can successfully create a pool \
+ with correct filesystem property set."
+
+set -A RW_FS_PROP "quota=512M" \
+ "reservation=512M" \
+ "recordsize=64K" \
+ "mountpoint=/tmp/mnt${TESTCASE_ID}" \
+ "checksum=fletcher2" \
+ "compression=lzjb" \
+ "atime=off" \
+ "devices=off" \
+ "exec=off" \
+ "setuid=off" \
+ "readonly=on" \
+ "snapdir=visible" \
+ "aclmode=discard" \
+ "aclinherit=discard" \
+ "canmount=off" \
+ "sharenfs=on"
+
+typeset -i i=0
+while (( $i < ${#RW_FS_PROP[*]} )); do
+ log_must $ZPOOL create -O ${RW_FS_PROP[$i]} -f $TESTPOOL $DISKS
+ datasetexists $TESTPOOL || \
+ log_fail "zpool create $TESTPOOL fail."
+ propertycheck $TESTPOOL ${RW_FS_PROP[i]} || \
+ log_fail "${RW_FS_PROP[i]} is failed to set."
+ destroy_pool $TESTPOOL
+ (( i = i + 1 ))
+done
+
+log_pass "'zpool create -O property=value pool' can successfully create a pool \
+ with correct filesystem property set."
+
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_022_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_022_pos.ksh
new file mode 100644
index 000000000000..5e391f939149
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_022_pos.ksh
@@ -0,0 +1,108 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_create/zfs_create_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_create_022_pos
+#
+# DESCRIPTION:
+# 'zpool create -O property=value pool' can successfully create a pool
+# with multiple filesystem properties set.
+#
+# STRATEGY:
+# 1. Create a storage pool with multiple -O options
+# 2. Verify the pool created successfully
+# 3. Verify the properties are correctly set
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-04-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+if ! $(check_zpool_opt_support "create" "-O") ; then
+ log_unsupported "-O option is not supported yet."
+fi
+
+verify_runnable "global"
+
+function cleanup
+{
+ destroy_pool $TESTPOOL
+}
+
+log_onexit cleanup
+
+log_assert "'zpool create -O property=value pool' can successfully create a pool \
+ with multiple filesystem properties set."
+
+set -A RW_FS_PROP "quota=512M" \
+ "reservation=512M" \
+ "recordsize=64K" \
+ "mountpoint=/tmp/mnt${TESTCASE_ID}" \
+ "checksum=fletcher2" \
+ "compression=lzjb" \
+ "atime=off" \
+ "devices=off" \
+ "exec=off" \
+ "setuid=off" \
+ "readonly=on" \
+ "snapdir=visible" \
+ "aclmode=discard" \
+ "aclinherit=discard" \
+ "canmount=off" \
+ "sharenfs=on"
+
+typeset -i i=0
+typeset opts=""
+
+while (( $i < ${#RW_FS_PROP[*]} )); do
+ opts="$opts -O ${RW_FS_PROP[$i]}"
+ (( i = i + 1 ))
+done
+
+log_must $ZPOOL create $opts -f $TESTPOOL $DISKS
+datasetexists $TESTPOOL || log_fail "zpool create $TESTPOOL fail."
+
+i=0
+while (( $i < ${#RW_FS_PROP[*]} )); do
+ propertycheck $TESTPOOL ${RW_FS_PROP[i]} || \
+ log_fail "${RW_FS_PROP[i]} is failed to set."
+ (( i = i + 1 ))
+done
+
+log_pass "'zpool create -O property=value pool' can successfully create a pool \
+ with multiple filesystem properties set."
+
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_023_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_023_neg.ksh
new file mode 100644
index 000000000000..77facf15a8b3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_023_neg.ksh
@@ -0,0 +1,97 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_create_023_neg
+#
+# DESCRIPTION:
+# 'zpool create -O' should return an error with badly formed parameters.
+#
+# STRATEGY:
+# 1. Create an array of parameters with '-O'
+# 2. For each parameter in the array, execute 'zpool create -O'
+# 3. Verify an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-04-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+if ! $(check_zpool_opt_support "create" "-O") ; then
+ log_unsupported "-O option is not supported yet."
+fi
+
+verify_runnable "global"
+
+function cleanup
+{
+ destroy_pool $TESTPOOL
+}
+
+log_onexit cleanup
+
+set -A args "QuOta=none" "quota=non" "quota=abcd" "quota=0" "quota=" \
+ "ResErVaTi0n=none" "reserV=none" "reservation=abcd" "reserv=" \
+ "recorDSize=64k" "recordsize=32M" "recordsize=256" \
+ "recsize=" "recsize=zero" "recordsize=0" \
+ "mountPoint=/tmp/tmpfile${TESTCASE_ID}" "mountpoint=non0" "mountpoint=" \
+ "mountpoint=LEGACY" "mounpoint=none" \
+ "sharenfs=ON" "ShareNFS=off" "sharenfs=sss" \
+ "checkSUM=on" "checksum=SHA256" "chsum=off" "checksum=aaa" \
+ "compression=of" "ComPression=lzjb" "compress=ON" "compress=a" \
+ "atime=ON" "ATime=off" "atime=bbb" \
+ "deviCes=on" "devices=OFF" "devices=aaa" \
+ "exec=ON" "EXec=off" "exec=aaa" \
+ "readonly=ON" "reADOnly=off" "rdonly=OFF" "rdonly=aaa" \
+ "zoned=ON" "ZoNed=off" "zoned=aaa" \
+ "snapdIR=hidden" "snapdir=VISible" "snapdir=aaa" \
+ "aclmode=DIScard" "aclmODE=groupmask" "aclmode=aaa" \
+ "aclinherit=deny" "aclinHerit=secure" "aclinherit=aaa" \
+ "type=volume" "type=snapshot" "type=filesystem" \
+ "creation=aaa" "used=10K" "available=10K" \
+ "referenced=10K" "compressratio=1.00x" \
+ "version=0" "version=1.234" "version=10K" "version=-1" \
+ "version=aaa" "version=999"
+
+log_assert "'zpool create -O' should return an error with badly formed parameters."
+
+typeset -i i=0
+while (( $i < ${#args[*]} )); do
+ log_mustnot $ZPOOL create -O ${args[i]} -f $TESTPOOL $DISKS
+ ((i = i + 1))
+done
+
+log_pass "'zpool create -O' should return an error with badly formed parameters."
+
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_test.sh
new file mode 100755
index 000000000000..59407c9517f9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_create/zpool_create_test.sh
@@ -0,0 +1,583 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zpool_create_001_pos cleanup
+zpool_create_001_pos_head()
+{
+ atf_set "descr" "'zpool create <pool> <vspec> ...' can successfully createa new pool with a name in ZFS namespace."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 2400
+}
+zpool_create_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_create_001_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_create_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_create_002_pos cleanup
+zpool_create_002_pos_head()
+{
+ atf_set "descr" "'zpool create -f <pool> <vspec> ...' can successfully createa new pool in some cases."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 2400
+}
+zpool_create_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_create_002_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_create_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_create_003_pos cleanup
+zpool_create_003_pos_head()
+{
+ atf_set "descr" "'zpool create -n <pool> <vspec> ...' can display the configureationwithout actually creating the pool."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 2400
+}
+zpool_create_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_create_003_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_create_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_create_004_pos cleanup
+zpool_create_004_pos_head()
+{
+ atf_set "descr" "'zpool create [-f]' can create a storage pool with large numbers of vdevswithout any errors."
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 2400
+}
+zpool_create_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_create_004_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_create_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_create_005_pos cleanup
+zpool_create_005_pos_head()
+{
+ atf_set "descr" "'zpool create [-R root][-m mountpoint] <pool> <vdev> ...' can createan alternate pool or a new pool mounted at the specified mountpoint."
+ atf_set "require.progs" "ksh93 zpool zfs"
+ atf_set "timeout" 2400
+}
+zpool_create_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_create_005_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_create_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_create_006_pos cleanup
+zpool_create_006_pos_head()
+{
+ atf_set "descr" "Verify 'zpool create' succeed with keywords combination."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 2400
+}
+zpool_create_006_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_create_006_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_create_006_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_create_007_neg cleanup
+zpool_create_007_neg_head()
+{
+ atf_set "descr" "'zpool create' should return an error with badly-formed parameters."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 2400
+}
+zpool_create_007_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_create_007_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_create_007_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_create_008_pos cleanup
+zpool_create_008_pos_head()
+{
+ atf_set "descr" "'zpool create' have to use '-f' scenarios"
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 2400
+}
+zpool_create_008_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_create_008_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_create_008_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_create_009_neg cleanup
+zpool_create_009_neg_head()
+{
+ atf_set "descr" "Create a pool with same devices twice or create two pools withsame devices, 'zpool create' should fail."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 2400
+}
+zpool_create_009_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_create_009_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_create_009_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_create_010_neg cleanup
+zpool_create_010_neg_head()
+{
+ atf_set "descr" "'zpool create' should return an error with VDEVs <64mb"
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 2400
+}
+zpool_create_010_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_create_010_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_create_010_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_create_011_neg cleanup
+zpool_create_011_neg_head()
+{
+ atf_set "descr" "'zpool create' should be failed with inapplicable scenarios."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 2400
+}
+zpool_create_011_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ atf_expect_fail "PR 241070 dumpon opens geom devices non-exclusively"
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_create_011_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_create_011_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_create_012_neg
+zpool_create_012_neg_head()
+{
+ atf_set "descr" "'zpool create' should fail with disk slice in swap."
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_create_012_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/zpool_create_012_neg.ksh || atf_fail "Testcase failed"
+}
+
+
+atf_test_case zpool_create_015_neg cleanup
+zpool_create_015_neg_head()
+{
+ atf_set "descr" "'zpool create' should fail with zfs vol device in swap."
+ atf_set "require.progs" "ksh93 zfs zpool"
+}
+zpool_create_015_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_create_015_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_create_015_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_create_017_neg cleanup
+zpool_create_017_neg_head()
+{
+ atf_set "descr" "'zpool create' should fail with mountpoint exists and not empty."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 2400
+}
+zpool_create_017_neg_body()
+{
+ [ `uname -s` = "FreeBSD" ] && atf_skip "FreeBSD does not consider creating pools on non-empty mountpoints a bug"
+
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_create_017_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_create_017_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_create_018_pos cleanup
+zpool_create_018_pos_head()
+{
+ atf_set "descr" "zpool create can create pools with specified properties"
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 2400
+}
+zpool_create_018_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_create_018_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_create_018_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_create_019_pos cleanup
+zpool_create_019_pos_head()
+{
+ atf_set "descr" "zpool create cannot create pools specifying readonly properties"
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 2400
+}
+zpool_create_019_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_create_019_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_create_019_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_create_020_pos cleanup
+zpool_create_020_pos_head()
+{
+ atf_set "descr" "zpool create -R works as expected"
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 2400
+}
+zpool_create_020_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_create_020_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_create_020_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_create_021_pos cleanup
+zpool_create_021_pos_head()
+{
+ atf_set "descr" "'zpool create -O property=value pool' can successfully create a poolwith correct filesystem property set."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 2400
+}
+zpool_create_021_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_create_021_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_create_021_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_create_022_pos cleanup
+zpool_create_022_pos_head()
+{
+ atf_set "descr" "'zpool create -O property=value pool' can successfully create a poolwith multiple filesystem properties set."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 2400
+}
+zpool_create_022_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_create_022_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_create_022_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_create_023_neg cleanup
+zpool_create_023_neg_head()
+{
+ atf_set "descr" "'zpool create -O' should return an error with badly formed parameters."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 2400
+}
+zpool_create_023_neg_body()
+{
+ atf_expect_fail 'kern/221987 - ZFS does not validate the sharenfs parameter'
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_create_023_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_create_023_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_create.kshlib
+ . $(atf_get_srcdir)/zpool_create.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zpool_create_001_pos
+ atf_add_test_case zpool_create_002_pos
+ atf_add_test_case zpool_create_003_pos
+ atf_add_test_case zpool_create_004_pos
+ atf_add_test_case zpool_create_005_pos
+ atf_add_test_case zpool_create_006_pos
+ atf_add_test_case zpool_create_007_neg
+ atf_add_test_case zpool_create_008_pos
+ atf_add_test_case zpool_create_009_neg
+ atf_add_test_case zpool_create_010_neg
+ atf_add_test_case zpool_create_011_neg
+ atf_add_test_case zpool_create_012_neg
+ atf_add_test_case zpool_create_015_neg
+ atf_add_test_case zpool_create_017_neg
+ atf_add_test_case zpool_create_018_pos
+ atf_add_test_case zpool_create_019_pos
+ atf_add_test_case zpool_create_020_pos
+ atf_add_test_case zpool_create_021_pos
+ atf_add_test_case zpool_create_022_pos
+ atf_add_test_case zpool_create_023_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/Makefile
new file mode 100644
index 000000000000..90ceee214b00
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/Makefile
@@ -0,0 +1,19 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zpool_destroy
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zpool_destroy_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= cleanup.ksh
+
+${PACKAGE}FILES+= zpool_destroy.cfg
+${PACKAGE}FILES+= zpool_destroy_001_pos.ksh
+${PACKAGE}FILES+= zpool_destroy_002_pos.ksh
+${PACKAGE}FILES+= zpool_destroy_003_neg.ksh
+${PACKAGE}FILES+= zpool_destroy_004_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/cleanup.ksh
new file mode 100644
index 000000000000..252b6798d279
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/cleanup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2013 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/zpool_destroy.cfg b/tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/zpool_destroy.cfg
new file mode 100644
index 000000000000..096b35e93d91
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/zpool_destroy.cfg
@@ -0,0 +1,30 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
+
+export DISK=${DISKS%% *}
+export PART_SIZE=500m
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/zpool_destroy_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/zpool_destroy_001_pos.ksh
new file mode 100644
index 000000000000..b657f6d3796f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/zpool_destroy_001_pos.ksh
@@ -0,0 +1,83 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_destroy_001_pos
+#
+# DESCRIPTION:
+# 'zpool destroy <pool>' can successfully destroy the specified pool.
+#
+# STRATEGY:
+# 1. Create a storage pool
+# 2. Destroy the pool
+# 3. Verify the is destroyed successfully
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ destroy_pool $TESTPOOL2
+ destroy_pool $TESTPOOL1
+ destroy_pool $TESTPOOL
+ wipe_partition_table $DISK
+}
+
+set -A datasets "$TESTPOOL" "$TESTPOOL2"
+
+log_assert "'zpool destroy <pool>' can destroy a specified pool."
+
+log_onexit cleanup
+
+partition_disk $PART_SIZE $DISK 2
+
+create_pool "$TESTPOOL" "${DISK}p1"
+create_pool "$TESTPOOL1" "${DISK}p2"
+log_must $ZFS create -s -V $VOLSIZE $TESTPOOL1/$TESTVOL
+create_pool "$TESTPOOL2" "/dev/zvol/$TESTPOOL1/$TESTVOL"
+
+typeset -i i=0
+while (( i < ${#datasets[*]} )); do
+ log_must poolexists "${datasets[i]}"
+ log_must $ZPOOL destroy "${datasets[i]}"
+ log_mustnot poolexists "${datasets[i]}"
+ ((i = i + 1))
+done
+
+log_pass "'zpool destroy <pool>' executes successfully"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/zpool_destroy_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/zpool_destroy_002_pos.ksh
new file mode 100644
index 000000000000..f475b91ca40e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/zpool_destroy_002_pos.ksh
@@ -0,0 +1,117 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_destroy_002_pos
+#
+# DESCRIPTION:
+# 'zpool destroy -f <pool>' can forcely destroy the specified pool.
+#
+# STRATEGY:
+# 1. Create a storage pool
+# 2. Create some datasets within the pool
+# 3. Change directory to any mountpoint of these datasets,
+# Verify 'zpool destroy' without '-f' will fail.
+# 4. 'zpool destroy -f' the pool
+# 5. Verify the pool is destroyed successfully
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ [[ -n $cwd ]] && log_must cd $cwd
+
+ if [[ -d $TESTDIR ]]; then
+ ismounted $TESTDIR
+ (( $? == 0 )) && \
+ log_must $UNMOUNT $TESTDIR
+ log_must $RM -rf $TESTDIR
+ fi
+
+ destroy_pool $TESTPOOL
+}
+
+set -A datasets "$TESTPOOL/$TESTFS" "$TESTPOOL/$TESTCTR/$TESTFS1" \
+ "$TESTPOOL/$TESTCTR" "$TESTPOOL/$TESTVOL" \
+
+log_assert "'zpool destroy -f <pool>' can forcely destroy the specified pool"
+
+log_onexit cleanup
+
+typeset cwd=""
+
+create_pool "$TESTPOOL" "$DISK"
+log_must $ZFS create $TESTPOOL/$TESTFS
+log_must $MKDIR -p $TESTDIR
+log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+log_must $ZFS create $TESTPOOL/$TESTCTR
+log_must $ZFS create $TESTPOOL/$TESTCTR/$TESTFS1
+log_must $ZFS create -V $VOLSIZE $TESTPOOL/$TESTVOL
+
+typeset -i i=0
+while (( $i < ${#datasets[*]} )); do
+ datasetexists "${datasets[i]}" || \
+ log_fail "Create datasets fail."
+ ((i = i + 1))
+done
+
+cwd=$PWD
+log_note "'zpool destroy' without '-f' will fail " \
+ "while pool is busy."
+
+for dir in $TESTDIR /$TESTPOOL/$TESTCTR /$TESTPOOL/$TESTCTR/$TESTFS1 ; do
+ log_must cd $dir
+ log_mustnot $ZPOOL destroy $TESTPOOL
+
+ # Need mount here, otherwise some dataset may be unmounted.
+ log_must $ZFS mount -a
+
+ i=0
+ while (( i < ${#datasets[*]} )); do
+ datasetexists "${datasets[i]}" || \
+ log_fail "Dataset ${datasets[i]} removed unexpected."
+ ((i = i + 1))
+ done
+done
+
+destroy_pool $TESTPOOL
+log_mustnot poolexists "$TESTPOOL"
+
+log_pass "'zpool destroy -f <pool>' success."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/zpool_destroy_003_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/zpool_destroy_003_neg.ksh
new file mode 100644
index 000000000000..4d10b7276e83
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/zpool_destroy_003_neg.ksh
@@ -0,0 +1,65 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_destroy_003_neg
+#
+# DESCRIPTION:
+# 'zpool destroy' should return an error with badly formed parameters,
+#
+# STRATEGY:
+# 1. Create an array of parameters
+# 2. For each parameter in the array, execute 'zpool destroy'
+# 3. Verify an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+set -A args "" "-f" "-? $TESTPOOL" "nonexistent_pool" \
+ "$TESTPOOL abcd" "abcd $TESTPOOL"
+
+log_assert "'zpool destroy' should return an error with badly-formed parameters."
+
+typeset -i i=0
+while (( $i < ${#args[*]} )); do
+ log_mustnot $ZPOOL destroy ${args[i]}
+ ((i = i + 1))
+done
+
+log_pass "'zpool destroy' badly formed parameters fail as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/zpool_destroy_004_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/zpool_destroy_004_pos.ksh
new file mode 100644
index 000000000000..77a8d5331ef1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/zpool_destroy_004_pos.ksh
@@ -0,0 +1,144 @@
+#!/usr/local/bin/ksh93 -p
+#
+# Copyright 2015 Spectra Logic Corporation.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_destroy_004_pos
+#
+# DESCRIPTION:
+# 'zpool destroy -f <pool>' can forcibly destroy the specified pool,
+# even if that pool has running zfs send or receive activity.
+#
+# STRATEGY:
+# 1. Create a storage pool
+# 2. For each sleep time in a set:
+# 2a. For each destroy type (same pool, sender only, receiver only):
+# - Create a dataset with some amount of data
+# - Run zfs send | zfs receive in the background.
+# - Sleep the amount of time specified for this run.
+# - 'zpool destroy -f' the pool.
+# - Wait for the send|receive to exit. It must not be killed in
+# order to ensure that the destroy takes care of doing so.
+# - Verify the pool destroyed successfully
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TESTPOOL && destroy_pool $TESTPOOL
+ poolexists $TESTPOOL1 && destroy_pool $TESTPOOL1
+}
+
+function create_sender
+{
+ cleanup
+ create_pool "$TESTPOOL" "$DISK0"
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $MKDIR -p $TESTDIR
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+ log_must dd if=/dev/zero of=$TESTDIR/f0 bs=1024k count=$datasz
+ log_must $ZFS snapshot $TESTPOOL/$TESTFS@snap1
+}
+
+function create_sender_and_receiver
+{
+ create_sender
+ create_pool "$TESTPOOL1" "$DISK1"
+}
+
+function send_recv_destroy
+{
+ sleeptime=$1
+ recv=$2
+ to_destroy=$3
+ who_to_destroy="$4"
+
+ # The pid of this pipe line is that of zfs receive
+ #
+ ( $ZFS send -RP $TESTPOOL/$TESTFS@snap1 | $ZFS receive -Fu $recv/d1 ) &
+ sndrcv_start=$(date '+%s')
+ rcvpid=$!
+ sndpid=$(pgrep -P $rcvpid)
+
+ log_must sleep $sleeptime
+ log_note "post sleep: $(ps -p ${sndpid},${rcvpid} -o command)"
+
+ destroy_start=$(date '+%s')
+ log_must $ZPOOL destroy -f $to_destroy
+ destroy_end=$(date '+%s')
+ dtime=$((destroy_end - destroy_start))
+ log_note "Destroy of $who_to_destroy took ${dtime} seconds."
+
+ log_note "post destroy: $(ps -p ${sndpid},${rcvpid} -o command)"
+
+ # Wait for send and recv to exit.
+ #
+ wait $sndpid
+ snderr=$?
+ wait $rcvpid
+ rcverr=$?
+ wait_end=$(date '+%s')
+ wtime=$((wait_end - sndrcv_start))
+ log_note "send|receive took ${wtime} seconds to finish."
+
+ # KSH: "wait pid" exit status of 127 means that process never existed
+ # or already completed; ksh's wait only returns the status of the
+ # child process if the child was running when the wait was issued.
+ # Therefore, we can not imply much about the interruption of the
+ # send | recv by zpool destroy -f
+ #
+ # The real test of success is simply that the pool was destroyed.
+ #
+ log_note \
+ "Destruction of ${who_to_destroy}: send ${snderr}, recv ${rcverr}"
+
+ log_mustnot $ZPOOL list $to_destroy
+}
+
+function run_tests
+{
+ log_note "TEST: send|receive to the same pool"
+ create_sender
+ send_recv_destroy $sleeptime $TESTPOOL $TESTPOOL SAME_POOL
+
+ log_note "TEST: send|receive to different pools, destroy sender"
+ create_sender_and_receiver
+ send_recv_destroy $sleeptime $TESTPOOL1 $TESTPOOL SENDER
+
+ log_note "TEST: send|receive to different pools, destroy receiver"
+ create_sender_and_receiver
+ send_recv_destroy $sleeptime $TESTPOOL1 $TESTPOOL1 RECEIVER
+}
+
+log_assert "'zpool destroy -f <pool>' can force destroy active pool"
+log_onexit cleanup
+set_disks
+
+# Faster tests using 1GB data size
+datasz=1000
+log_note "Running fast tests with 1000MB of data"
+for sleeptime in 0.1 0.3 0.5 0.75 1 2 3; do
+ run_tests
+done
+
+# A longer test that simulates a more realistic send|receive that exceeds
+# the size of arc memory by 1/3 and gets interrupted a decent amount of
+# time after the start of the run.
+arcmem=$(sysctl -n vfs.zfs.arc_max)
+# ARC will use 2xdatasz memory since it caches both the src and dst copies
+datasz=$((arcmem / 1048576 * 2 / 3))
+log_note "Running longer test with ${datasz}MB of data"
+sleeptime=15
+run_tests
+
+log_pass "'zpool destroy -f <pool>' successful with active pools."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/zpool_destroy_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/zpool_destroy_test.sh
new file mode 100755
index 000000000000..38e3957dec18
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_destroy/zpool_destroy_test.sh
@@ -0,0 +1,127 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zpool_destroy_001_pos cleanup
+zpool_destroy_001_pos_head()
+{
+ atf_set "descr" "'zpool destroy <pool>' can destroy a specified pool."
+ atf_set "require.progs" "ksh93 zfs zpool"
+}
+zpool_destroy_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_destroy.cfg
+
+ verify_zvol_recursive
+ verify_disk_count "$DISK" 1
+ ksh93 $(atf_get_srcdir)/zpool_destroy_001_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_destroy_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_destroy.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_destroy_002_pos cleanup
+zpool_destroy_002_pos_head()
+{
+ atf_set "descr" "'zpool destroy -f <pool>' can forcely destroy the specified pool"
+ atf_set "require.progs" "ksh93 zfs zpool"
+}
+zpool_destroy_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_destroy.cfg
+
+ verify_disk_count "$DISK" 1
+ ksh93 $(atf_get_srcdir)/zpool_destroy_002_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_destroy_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_destroy.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_destroy_003_neg cleanup
+zpool_destroy_003_neg_head()
+{
+ atf_set "descr" "'zpool destroy' should return an error with badly-formed parameters."
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_destroy_003_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_destroy.cfg
+
+ ksh93 $(atf_get_srcdir)/zpool_destroy_003_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_destroy_003_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_destroy.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_destroy_004_pos cleanup
+zpool_destroy_004_pos_head()
+{
+ atf_set "descr" "'zpool destroy -f' should work on active pools."
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 2000
+}
+zpool_destroy_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_destroy.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/zpool_destroy_004_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_destroy_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_destroy.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zpool_destroy_001_pos
+ atf_add_test_case zpool_destroy_002_pos
+ atf_add_test_case zpool_destroy_003_neg
+ atf_add_test_case zpool_destroy_004_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_detach/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zpool_detach/Makefile
new file mode 100644
index 000000000000..353b8d22be79
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_detach/Makefile
@@ -0,0 +1,16 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zpool_detach
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zpool_detach_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zpool_detach.cfg
+${PACKAGE}FILES+= zpool_detach_001_neg.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= setup.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_detach/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_detach/cleanup.ksh
new file mode 100644
index 000000000000..9038281882b5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_detach/cleanup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_detach/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_detach/setup.ksh
new file mode 100644
index 000000000000..f0e805f5f8b6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_detach/setup.ksh
@@ -0,0 +1,33 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+DISK=${DISKS%% *}
+
+default_mirror_setup $DISKS
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_detach/zpool_detach.cfg b/tests/sys/cddl/zfs/tests/cli_root/zpool_detach/zpool_detach.cfg
new file mode 100644
index 000000000000..99d93c9d9c72
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_detach/zpool_detach.cfg
@@ -0,0 +1,27 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_detach/zpool_detach_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_detach/zpool_detach_001_neg.ksh
new file mode 100644
index 000000000000..c2874dc5451f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_detach/zpool_detach_001_neg.ksh
@@ -0,0 +1,78 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_detach_001_neg
+#
+# DESCRIPTION:
+# Executing 'zpool detach' command with bad options fails.
+#
+# STRATEGY:
+# 1. Create an array of badly formed 'zpool detach' options.
+# 2. Execute each element of the array.
+# 3. Verify an error code is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-10-18)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+DISKLIST=$(get_disklist $TESTPOOL)
+
+set -A args "" "-?" "-t fakepool" "-f fakepool" "-ev fakepool" "fakepool" \
+ "$TESTPOOL" "$TESTPOOL/$TESTFS" "$TESTPOOL/$TESTFS $DISKLIST" \
+ "$TESTPOOL/$TESTCTR" "$TESTPOOL/$TESTCTR/$TESTFS1" \
+ "$TESTPOOL/$TESTCTR $DISKLIST" "$TESTPOOL/$TESTVOL" \
+ "$TESTPOOL/$TESTCTR/$TESTFS1 $DISKLIST" "$TESTPOOL/$TESTVOL $DISKLIST" \
+ "$DISKLIST"
+
+log_assert "Executing 'zpool detach' with bad options fails"
+
+if [[ -z $DISKLIST ]]; then
+ log_fail "DISKLIST is empty."
+fi
+
+typeset -i i=0
+
+while [[ $i -lt ${#args[*]} ]]; do
+
+ log_mustnot $ZPOOL detach ${args[$i]}
+
+ (( i = i + 1 ))
+done
+
+log_pass "'zpool detach' command with bad options failed as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_detach/zpool_detach_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zpool_detach/zpool_detach_test.sh
new file mode 100755
index 000000000000..b2760b20f7f0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_detach/zpool_detach_test.sh
@@ -0,0 +1,55 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zpool_detach_001_neg cleanup
+zpool_detach_001_neg_head()
+{
+ atf_set "descr" "Executing 'zpool detach' with bad options fails"
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_detach_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_detach.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_detach_001_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_detach_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_detach.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zpool_detach_001_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_expand/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zpool_expand/Makefile
new file mode 100644
index 000000000000..68265a18bbd0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_expand/Makefile
@@ -0,0 +1,18 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zpool_expand
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zpool_expand_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zpool_expand_003_neg.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zpool_expand_002_pos.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zpool_expand.cfg
+${PACKAGE}FILES+= zpool_expand_001_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_expand/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_expand/cleanup.ksh
new file mode 100644
index 000000000000..cf6055009399
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_expand/cleanup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_expand/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_expand/setup.ksh
new file mode 100644
index 000000000000..b778cebd360c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_expand/setup.ksh
@@ -0,0 +1,37 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+if ! pool_prop_exist "autoexpand"; then
+ log_unsupported "zpool autoexpand is not support on testing machine"
+fi
+
+DISK=${DISKS%% *}
+
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_expand/zpool_expand.cfg b/tests/sys/cddl/zfs/tests/cli_root/zpool_expand/zpool_expand.cfg
new file mode 100644
index 000000000000..cfa24b7c158d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_expand/zpool_expand.cfg
@@ -0,0 +1,42 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
+org_size=1g
+exp_size=2g
+
+export org_size
+export exp_size
+
+VFS=$TESTPOOL/$TESTFS
+export VFS
+
+EX_1GB=1073741824
+EX_3GB=3221225472
+
+export EX_1GB
+export EX_3GB
+
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_expand/zpool_expand_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_expand/zpool_expand_001_pos.ksh
new file mode 100644
index 000000000000..3d9198cea27e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_expand/zpool_expand_001_pos.ksh
@@ -0,0 +1,133 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_expand_001_pos
+#
+# DESCRIPTION:
+# Once zpool set autoexpand=on poolname, zpool can autoexpand by
+# Dynamic LUN Expansion
+#
+#
+# STRATEGY:
+# 1) Create a pool
+# 2) Create volume on top of the pool
+# 3) Create pool by using the zvols and set autoexpand=on
+# 4) Expand the vol size by 'zfs set volsize'
+# 5) Check that the pool size was expanded
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-06-12)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ destroy_pool $TESTPOOL1
+
+ for i in 1 2 3; do
+ if datasetexists $VFS/vol$i; then
+ log_must $ZFS destroy $VFS/vol$i
+ fi
+ done
+}
+
+log_onexit cleanup
+
+log_assert "zpool can be autoexpanded after set autoexpand=on on LUN expansion"
+
+for i in 1 2 3; do
+ log_must $ZFS create -V $org_size $VFS/vol$i
+done
+
+for type in " " mirror raidz raidz2; do
+
+ log_must $ZPOOL create -o autoexpand=on $TESTPOOL1 $type \
+ /dev/zvol/$VFS/vol1 \
+ /dev/zvol/$VFS/vol2 \
+ /dev/zvol/$VFS/vol3
+
+ typeset autoexp=$(get_pool_prop autoexpand $TESTPOOL1)
+ if [[ $autoexp != "on" ]]; then
+ log_fail "zpool $TESTPOOL1 autoexpand should on but is $autoexp"
+ fi
+
+ typeset prev_size=$(get_pool_prop size $TESTPOOL1)
+
+ for i in 1 2 3; do
+ log_must $ZFS set volsize=$exp_size $VFS/vol$i
+ done
+
+ $SYNC
+ $SLEEP 10
+ $SYNC
+
+ typeset expand_size=$(get_pool_prop size $TESTPOOL1)
+
+ # check for zpool history for the pool size expansion
+ if [[ $type == "mirror" ]]; then
+ $ZPOOL history -il $TESTPOOL1 | \
+ $GREP "pool '$TESTPOOL1' size:" | \
+ $GREP "internal vdev online" | \
+ $GREP "(+${EX_1GB})" >/dev/null 2>&1
+
+ if [[ $? -ne 0 ]] ; then
+ log_fail "pool $TESTPOOL1" \
+ " is not autoexpand after LUN expansion"
+ fi
+ else
+ $ZPOOL history -il $TESTPOOL1 | \
+ $GREP "pool '$TESTPOOL1' size:" | \
+ $GREP "internal vdev online" | \
+ $GREP "(+${EX_3GB})" >/dev/null 2>&1
+
+ if [[ $? -ne 0 ]] ; then
+ log_fail "pool $TESTPOOL1" \
+ " is not autoexpand after LUN expansion"
+ fi
+ fi
+
+ log_must $ZPOOL destroy $TESTPOOL1
+
+ for i in 1 2 3; do
+ log_must $ZFS set volsize=$org_size $VFS/vol$i
+ done
+
+done
+
+log_pass "zpool can be autoexpanded after set autoexpand=on on LUN expansion"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_expand/zpool_expand_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_expand/zpool_expand_002_pos.ksh
new file mode 100644
index 000000000000..51461361e961
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_expand/zpool_expand_002_pos.ksh
@@ -0,0 +1,137 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_expand_002_pos
+#
+# DESCRIPTION:
+# After zpool online -e poolname zvol vdevs, zpool can autoexpand by
+# Dynamic LUN Expansion
+#
+#
+# STRATEGY:
+# 1) Create a pool
+# 2) Create volume on top of the pool
+# 3) Create pool by using the zvols
+# 4) Expand the vol size by zfs set volsize
+# 5 Use zpool online -e to online the zvol vdevs
+# 6) Check that the pool size was expaned
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-06-12)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ destroy_pool $TESTPOOL1
+
+ for i in 1 2 3; do
+ if datasetexists $VFS/vol$i; then
+ log_must $ZFS destroy $VFS/vol$i
+ fi
+ done
+}
+
+log_onexit cleanup
+
+log_assert "zpool can expand after zpool online -e zvol vdevs on LUN expansion"
+
+for i in 1 2 3; do
+ log_must $ZFS create -V $org_size $VFS/vol$i
+done
+
+for type in " " mirror raidz raidz2; do
+ log_must $ZPOOL create $TESTPOOL1 $type \
+ /dev/zvol/$VFS/vol1 \
+ /dev/zvol/$VFS/vol2 \
+ /dev/zvol/$VFS/vol3
+
+ typeset autoexp=$(get_pool_prop autoexpand $TESTPOOL1)
+ if [[ $autoexp != "off" ]]; then
+ log_fail "zpool $TESTPOOL1 autoexpand should off but is $autoexp"
+ fi
+
+ typeset prev_size=$(get_pool_prop size $TESTPOOL1)
+
+ for i in 1 2 3; do
+ log_must $ZFS set volsize=$exp_size $VFS/vol$i
+ done
+
+ for i in 1 2 3; do
+ log_must $ZPOOL online -e $TESTPOOL1 /dev/zvol/$VFS/vol$i
+ done
+
+ $SYNC
+ $SLEEP 10
+ $SYNC
+
+ # check for zpool history for the pool size expansion
+ if [[ $type == " " || $type == "mirror" ]]; then
+ $ZPOOL history -il $TESTPOOL1 | \
+ $GREP "pool '$TESTPOOL1' size:" | \
+ $GREP "internal vdev online" | \
+ $GREP "(+${EX_1GB})" >/dev/null 2>&1
+
+ if [[ $? -ne 0 ]]; then
+ log_fail "pool $TESTPOOL1" \
+ " is not autoexpand after LUN expansion"
+ fi
+ else
+ $ZPOOL history -il $TESTPOOL1 | \
+ $GREP "pool '$TESTPOOL1' size:" | \
+ $GREP "internal vdev online" | \
+ $GREP "(+${EX_3GB})" >/dev/null 2>&1
+
+ if [[ $? -ne 0 ]] ; then
+ log_fail "pool $TESTPOOL1" \
+ " is not autoexpand after LUN expansion"
+ fi
+ fi
+
+ typeset expand_size=$(get_pool_prop size $TESTPOOL1)
+
+ log_must $ZPOOL destroy $TESTPOOL1
+
+ for i in 1 2 3; do
+ log_must $ZFS set volsize=$org_size $VFS/vol$i
+ done
+
+done
+
+log_pass "zpool can expand after zpool online -e zvol vdevs on LUN expansion"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_expand/zpool_expand_003_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_expand/zpool_expand_003_neg.ksh
new file mode 100644
index 000000000000..ee7bdf2eaf92
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_expand/zpool_expand_003_neg.ksh
@@ -0,0 +1,123 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_expand_003_neg
+#
+# Description:
+# Once set zpool autoexpand=off, zpool can *NOT* autoexpand by
+# Dynamic LUN Expansion
+#
+#
+# STRATEGY:
+# 1) Create a pool
+# 2) Create volumes on top of the pool
+# 3) Create pool by using the zvols and set autoexpand=off
+# 4) Expand the vol size by zfs set volsize
+# 5) Check that the pool size is not changed
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-06-12)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ destroy_pool $TESTPOOL1
+
+ for i in 1 2 3; do
+ if datasetexists $VFS/vol$i; then
+ log_must $ZFS destroy $VFS/vol$i
+ fi
+ done
+}
+
+log_onexit cleanup
+
+log_assert "zpool can not expand if set autoexpand=off after LUN expansion"
+
+for i in 1 2 3; do
+ log_must $ZFS create -V $org_size $VFS/vol$i
+done
+
+for type in "" mirror raidz raidz2; do
+ log_must $ZPOOL create $TESTPOOL1 $type \
+ /dev/zvol/$VFS/vol1 \
+ /dev/zvol/$VFS/vol2 \
+ /dev/zvol/$VFS/vol3
+
+ typeset autoexp=$(get_pool_prop autoexpand $TESTPOOL1)
+ if [[ $autoexp != "off" ]]; then
+ log_fail "zpool $TESTPOOL1 autoexpand should off but is $autoexp"
+ fi
+
+ typeset prev_size=$(get_pool_prop size $TESTPOOL1)
+
+ for i in 1 2 3; do
+ log_must $ZFS set volsize=$exp_size $VFS/vol$i
+ done
+
+ $SYNC
+ $SLEEP 10
+ $SYNC
+
+ # check for zpool history for the pool size expansion
+ $ZPOOL history -il $TESTPOOL1 | \
+ $GREP "pool '$TESTPOOL1' size:" | \
+ $GREP "internal vdev online" >/dev/null 2>&1
+
+ if [[ $? -eq 0 ]]; then
+ log_fail "pool $TESTPOOL1" \
+ " is not autoexpand after LUN expansion"
+ fi
+
+ typeset expand_size=$(get_pool_prop size $TESTPOOL1)
+
+ if [[ "$prev_size" != "$expand_size" ]]; then
+ log_fail "pool $TESTPOOL1 size changed after LUN expansion"
+ fi
+
+ log_must $ZPOOL destroy $TESTPOOL1
+
+ for i in 1 2 3; do
+ log_must $ZFS set volsize=$org_size $VFS/vol$i
+ done
+
+done
+
+log_pass "zpool can not expand if set autoexpand=off after LUN expansion"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_expand/zpool_expand_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zpool_expand/zpool_expand_test.sh
new file mode 100755
index 000000000000..b49476ea5fa2
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_expand/zpool_expand_test.sh
@@ -0,0 +1,108 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zpool_expand_001_pos cleanup
+zpool_expand_001_pos_head()
+{
+ atf_set "descr" "zpool can be autoexpanded after set autoexpand=on on LUN expansion"
+ atf_set "require.progs" "ksh93 zfs zpool"
+}
+zpool_expand_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_expand.cfg
+
+ verify_zvol_recursive
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_expand_001_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_expand_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_expand.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_expand_002_pos cleanup
+zpool_expand_002_pos_head()
+{
+ atf_set "descr" "zpool can expand after zpool online -e zvol vdevs on LUN expansion"
+ atf_set "require.progs" "ksh93 zfs zpool"
+}
+zpool_expand_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_expand.cfg
+
+ verify_zvol_recursive
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_expand_002_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_expand_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_expand.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_expand_003_neg cleanup
+zpool_expand_003_neg_head()
+{
+ atf_set "descr" "zpool can not expand if set autoexpand=off after LUN expansion"
+ atf_set "require.progs" "ksh93 zfs zpool"
+}
+zpool_expand_003_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_expand.cfg
+
+ verify_zvol_recursive
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_expand_003_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_expand_003_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_expand.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zpool_expand_001_pos
+ atf_add_test_case zpool_expand_002_pos
+ atf_add_test_case zpool_expand_003_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_export/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zpool_export/Makefile
new file mode 100644
index 000000000000..fb15c0421a31
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_export/Makefile
@@ -0,0 +1,19 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zpool_export
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zpool_export_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zpool_export.cfg
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zpool_export_001_pos.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zpool_export_004_pos.ksh
+${PACKAGE}FILES+= zpool_export_003_neg.ksh
+${PACKAGE}FILES+= zpool_export_002_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_export/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_export/cleanup.ksh
new file mode 100644
index 000000000000..713cb16bb31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_export/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_export/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_export/setup.ksh
new file mode 100644
index 000000000000..f87249554afe
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_export/setup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_export/zpool_export.cfg b/tests/sys/cddl/zfs/tests/cli_root/zpool_export/zpool_export.cfg
new file mode 100644
index 000000000000..4a34112f0dc5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_export/zpool_export.cfg
@@ -0,0 +1,29 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
+
+export DISK=${DISKS%% *}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_export/zpool_export_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_export/zpool_export_001_pos.ksh
new file mode 100644
index 000000000000..07cd9a9c6874
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_export/zpool_export_001_pos.ksh
@@ -0,0 +1,78 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_export_001_pos
+#
+# DESCRIPTION:
+# Exported pools should no longer be visible from 'zpool list'.
+# Therefore, we export an existing pool and verify it cannot
+# be accessed.
+#
+# STRATEGY:
+# 1. Unmount the test directory.
+# 2. Export the pool.
+# 3. Verify the pool is no longer present in the list output.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ typeset dir=$(get_device_dir $DISKS)
+
+ datasetexists "$TESTPOOL/$TESTFS" || \
+ log_must $ZPOOL import -d $dir $TESTPOOL
+
+ ismounted "$TESTPOOL/$TESTFS"
+ (( $? != 0 )) && \
+ log_must $ZFS mount $TESTPOOL/$TESTFS
+}
+
+log_onexit cleanup
+
+log_assert "Verify a pool can be exported."
+
+log_must $ZFS umount $TESTDIR
+log_must $ZPOOL export $TESTPOOL
+
+poolexists $TESTPOOL && \
+ log_fail "$TESTPOOL unexpectedly found in 'zpool list' output."
+
+log_pass "Successfully exported a ZPOOL."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_export/zpool_export_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_export/zpool_export_002_pos.ksh
new file mode 100644
index 000000000000..cf0650a491be
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_export/zpool_export_002_pos.ksh
@@ -0,0 +1,89 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_export_002_pos
+#
+# DESCRIPTION:
+# The 'zpool export' command must fail when a pool is
+# busy i.e. mounted.
+#
+# STRATEGY:
+# 1. Try and export the default pool when mounted and busy.
+# 2. Verify an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ typeset dir=$(get_device_dir $DISKS)
+ cd $olddir || \
+ log_fail "Couldn't cd back to $olddir"
+
+ datasetexists "$TESTPOOL/$TESTFS" || \
+ log_must $ZPOOL import -d $dir $TESTPOOL
+
+ ismounted "$TESTPOOL/$TESTFS"
+ (( $? != 0 )) && \
+ log_must $ZFS mount $TESTPOOL/$TESTFS
+
+ [[ -e $TESTDIR/$TESTFILE0 ]] && \
+ log_must $RM -rf $TESTDIR/$TESTFILE0
+}
+
+olddir=$PWD
+
+log_onexit cleanup
+
+log_assert "Verify a busy ZPOOL cannot be exported."
+
+ismounted "$TESTPOOL/$TESTFS"
+(( $? != 0 )) && \
+ log_fail "$TESTDIR not mounted. Unable to continue."
+
+cd $TESTDIR || \
+ log_fail "Couldn't cd to $TESTDIR"
+
+log_mustnot $ZPOOL export $TESTPOOL
+
+poolexists $TESTPOOL || \
+ log_fail "$TESTPOOL not found in 'zpool list' output."
+
+log_pass "Unable to export a busy ZPOOL as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_export/zpool_export_003_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_export/zpool_export_003_neg.ksh
new file mode 100644
index 000000000000..75093e1a1182
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_export/zpool_export_003_neg.ksh
@@ -0,0 +1,77 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_export_003_neg
+#
+# DESCRIPTION:
+# 'zpool export' should return an error with badly formed parameters,
+#
+# STRATEGY:
+# 1. Create an array of parameters
+# 2. For each parameter in the array, execute 'zpool export'
+# 3. Verify an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ typeset dir=$(get_device_dir $DISKS)
+ datasetexists "$TESTPOOL/$TESTFS" || \
+ log_must $ZPOOL import -d $dir $TESTPOOL
+
+ ismounted "$TESTPOOL/$TESTFS"
+ (( $? != 0 )) && \
+ log_must $ZFS mount $TESTPOOL/$TESTFS
+}
+
+log_onexit cleanup
+
+set -A args "" "-f" "-? $TESTPOOL" "-QWERTYUIO $TESTPOOL"
+
+log_assert "'zpool export' should return an error with badly-formed parameters."
+
+typeset -i i=0
+while (( $i < ${#args[*]} )); do
+ log_mustnot $ZPOOL export ${args[i]}
+ ((i = i + 1))
+done
+
+log_pass "'zpool export' badly formed parameters fail as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_export/zpool_export_004_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_export/zpool_export_004_pos.ksh
new file mode 100644
index 000000000000..49f0e29eac5d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_export/zpool_export_004_pos.ksh
@@ -0,0 +1,112 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_export_004_pos
+#
+# DESCRIPTION:
+# Verify zpool export succeed or fail with spare.
+#
+# STRATEGY:
+# 1. Create two mirror pools with same spare.
+# 2. Verify zpool export one pool succeed.
+# 3. Import the pool.
+# 4. Replace one device with the spare and detach it in one pool.
+# 5. Verify zpool export the pool succeed.
+# 6. Import the pool.
+# 7. Replace one device with the spare in one pool.
+# 8. Verify zpool export the pool fail.
+# 9. Verify zpool export the pool with "-f" succeed.
+# 10. Import the pool.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-03-10)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ restart_zfsd
+
+ mntpnt=$(get_prop mountpoint $TESTPOOL)
+ datasetexists $TESTPOOL1 || log_must $ZPOOL import -d $mntpnt $TESTPOOL1
+ datasetexists $TESTPOOL1 && destroy_pool $TESTPOOL1
+ datasetexists $TESTPOOL2 && destroy_pool $TESTPOOL2
+ typeset -i i=0
+ while ((i < 5)); do
+ if [[ -e $mntpnt/vdev$i ]]; then
+ log_must $RM -f $mntpnt/vdev$i
+ fi
+ ((i += 1))
+ done
+}
+
+
+log_assert "Verify zpool export succeed or fail with spare."
+log_onexit cleanup
+# Stop ZFSD because it interferes with our manually activated spares
+stop_zfsd
+
+mntpnt=$(get_prop mountpoint $TESTPOOL)
+
+typeset -i i=0
+while ((i < 5)); do
+ log_must create_vdevs $mntpnt/vdev$i
+ eval vdev$i=$mntpnt/vdev$i
+ ((i += 1))
+done
+
+log_must $ZPOOL create $TESTPOOL1 mirror $vdev0 $vdev1 spare $vdev4
+log_must $ZPOOL create $TESTPOOL2 mirror $vdev2 $vdev3 spare $vdev4
+
+log_must $ZPOOL export $TESTPOOL1
+log_must $ZPOOL import -d $mntpnt $TESTPOOL1
+
+log_must $ZPOOL replace $TESTPOOL1 $vdev0 $vdev4
+log_must $ZPOOL detach $TESTPOOL1 $vdev4
+log_must $ZPOOL export $TESTPOOL1
+log_must $ZPOOL import -d $mntpnt $TESTPOOL1
+
+log_must $ZPOOL replace $TESTPOOL1 $vdev0 $vdev4
+log_mustnot $ZPOOL export $TESTPOOL1
+
+log_must $ZPOOL export -f $TESTPOOL1
+log_must $ZPOOL import -d $mntpnt $TESTPOOL1
+
+log_pass "Verify zpool export succeed or fail with spare."
+
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_export/zpool_export_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zpool_export/zpool_export_test.sh
new file mode 100755
index 000000000000..4acde12449d4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_export/zpool_export_test.sh
@@ -0,0 +1,130 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zpool_export_001_pos cleanup
+zpool_export_001_pos_head()
+{
+ atf_set "descr" "Verify a pool can be exported."
+ atf_set "require.progs" "ksh93 zfs zpool"
+}
+zpool_export_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_export.cfg
+
+ verify_disk_count "$DISK" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_export_001_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_export_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_export.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_export_002_pos cleanup
+zpool_export_002_pos_head()
+{
+ atf_set "descr" "Verify a busy ZPOOL cannot be exported."
+ atf_set "require.progs" "ksh93 zfs zpool"
+}
+zpool_export_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_export.cfg
+
+ verify_disk_count "$DISK" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_export_002_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_export_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_export.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_export_003_neg cleanup
+zpool_export_003_neg_head()
+{
+ atf_set "descr" "'zpool export' should return an error with badly-formed parameters."
+ atf_set "require.progs" "ksh93 zfs zpool"
+}
+zpool_export_003_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_export.cfg
+
+ verify_disk_count "$DISK" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_export_003_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_export_003_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_export.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_export_004_pos cleanup
+zpool_export_004_pos_head()
+{
+ atf_set "descr" "Verify zpool export succeed or fail with spare."
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_export_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_export.cfg
+
+ verify_disk_count "$DISK" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_export_004_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_export_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_export.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zpool_export_001_pos
+ atf_add_test_case zpool_export_002_pos
+ atf_add_test_case zpool_export_003_neg
+ atf_add_test_case zpool_export_004_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_get/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zpool_get/Makefile
new file mode 100644
index 000000000000..7066d97f02bc
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_get/Makefile
@@ -0,0 +1,19 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zpool_get
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zpool_get_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zpool_get_002_pos.ksh
+${PACKAGE}FILES+= zpool_get_003_pos.ksh
+${PACKAGE}FILES+= zpool_get.cfg
+${PACKAGE}FILES+= zpool_get_001_pos.ksh
+${PACKAGE}FILES+= zpool_get_004_neg.ksh
+${PACKAGE}FILES+= cleanup.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_get/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_get/cleanup.ksh
new file mode 100644
index 000000000000..3e9b1020086d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_get/cleanup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+verify_runnable "both"
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_get/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_get/setup.ksh
new file mode 100644
index 000000000000..68897c5b106b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_get/setup.ksh
@@ -0,0 +1,30 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+verify_runnable "both"
+
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_get/zpool_get.cfg b/tests/sys/cddl/zfs/tests/cli_root/zpool_get/zpool_get.cfg
new file mode 100644
index 000000000000..28da0fa2cbcf
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_get/zpool_get.cfg
@@ -0,0 +1,75 @@
+#!/bin/ksh -p
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+# Set the expected properties of zpool
+typeset -a properties=(
+ "size"
+ "capacity"
+ "altroot"
+ "health"
+ "guid"
+ "version"
+ "bootfs"
+ "delegation"
+ "autoreplace"
+ "cachefile"
+ "failmode"
+ "listsnapshots"
+ "autoexpand"
+ "dedupditto"
+ "dedupratio"
+ "free"
+ "allocated"
+ "readonly"
+ "comment"
+ "expandsize"
+ "freeing"
+ "fragmentation"
+ "leaked"
+ "bootsize"
+ "checkpoint"
+ "feature@async_destroy"
+ "feature@empty_bpobj"
+ "feature@lz4_compress"
+ "feature@multi_vdev_crash_dump"
+ "feature@spacemap_histogram"
+ "feature@enabled_txg"
+ "feature@hole_birth"
+ "feature@extensible_dataset"
+ "feature@embedded_data"
+ "feature@bookmarks"
+ "feature@filesystem_limits"
+ "feature@large_blocks"
+ "feature@large_dnode"
+ "feature@sha512"
+ "feature@skein"
+ # "feature@edonr" Edonr is not yet implemented on FreeBSD
+ "feature@device_removal"
+ "feature@obsolete_counts"
+ "feature@zpool_checkpoint"
+ "feature@spacemap_v2"
+)
+
+export DISK=${DISKS%% *}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_get/zpool_get_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_get/zpool_get_001_pos.ksh
new file mode 100644
index 000000000000..1887e19908ea
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_get/zpool_get_001_pos.ksh
@@ -0,0 +1,70 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_get_001_pos
+#
+# DESCRIPTION:
+#
+# Zpool get usage message is displayed when called with no arguments
+#
+# STRATEGY:
+# 1. Run zpool get
+# 2. Check that exit status is set to 2
+# 3. Check usage message contains text "usage"
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-03-06)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+log_assert "Zpool get usage message is displayed when called with no arguments."
+
+$ZPOOL get > /dev/null 2>&1
+RET=$?
+if [ $RET != 2 ]
+then
+ log_fail "\"zpool get\" exit status $RET should be equal to 2."
+fi
+
+OUTPUT=$($ZPOOL get 2>&1 | $GREP -i usage)
+RET=$?
+if [ $RET != 0 ]
+then
+ log_fail "Usage message for zpool get did not contain the word 'usage'."
+fi
+
+log_pass "Zpool get usage message is displayed when called with no arguments."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_get/zpool_get_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_get/zpool_get_002_pos.ksh
new file mode 100644
index 000000000000..46ed56ad689f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_get/zpool_get_002_pos.ksh
@@ -0,0 +1,103 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_get/zpool_get.cfg
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_get_002_pos
+#
+# DESCRIPTION:
+#
+# zpool get all works as expected
+#
+# STRATEGY:
+#
+# 1. Using zpool get, retrieve all default values
+# 2. Verify that the header is printed
+# 3. Verify that we can see all the properties we expect to see
+# 4. Verify that the total output contains just those properties + header.
+#
+# Test for those properties are expected to check whether their
+# default values are sane, or whether they can be changed with zpool set.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-03-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+log_assert "Zpool get all works as expected"
+
+typeset -i i=0;
+
+if ! is_global_zone ; then
+ TESTPOOL=${TESTPOOL%%/*}
+fi
+
+log_must $ZPOOL get all $TESTPOOL
+$ZPOOL get all $TESTPOOL > $TMPDIR/values.${TESTCASE_ID}
+
+log_note "Checking zpool get all output for a header."
+$GREP ^"NAME " $TMPDIR/values.${TESTCASE_ID} > /dev/null 2>&1
+if [ $? -ne 0 ]
+then
+ log_fail "The header was not printed from zpool get all"
+fi
+
+
+while [ $i -lt "${#properties[@]}" ]
+do
+ log_note "Checking for ${properties[$i]} property"
+ $GREP "$TESTPOOL *${properties[$i]}" $TMPDIR/values.${TESTCASE_ID} > /dev/null 2>&1
+ if [ $? -ne 0 ]
+ then
+ log_fail "zpool property ${properties[$i]} was not found\
+ in pool output."
+ fi
+ i=$(( $i + 1 ))
+done
+
+# increment the counter to include the header line
+i=$(( $i + 1 ))
+
+COUNT=$($WC $TMPDIR/values.${TESTCASE_ID})
+if [ $i -ne $COUNT ]
+then
+ log_fail "Length of output $COUNT was not equal to number of props + 1 ($i)."
+fi
+
+
+
+$RM $TMPDIR/values.${TESTCASE_ID}
+log_pass "Zpool get all works as expected"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_get/zpool_get_003_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_get/zpool_get_003_pos.ksh
new file mode 100644
index 000000000000..67903ca3def6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_get/zpool_get_003_pos.ksh
@@ -0,0 +1,80 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_get/zpool_get.cfg
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_get_003_pos
+#
+# DESCRIPTION:
+#
+# Zpool get returns values for all known properties
+#
+# STRATEGY:
+# 1. For all properties, verify zpool get retrieves a value
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-03-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+log_assert "Zpool get returns values for all known properties"
+
+if ! is_global_zone ; then
+ TESTPOOL=${TESTPOOL%%/*}
+fi
+
+typeset -i i=0;
+
+while [ $i -lt "${#properties[@]}" ]
+do
+ log_note "Checking for ${properties[$i]} property"
+ log_must eval "$ZPOOL get ${properties[$i]} $TESTPOOL > $TMPDIR/value.${TESTCASE_ID}"
+ $GREP "${properties[$i]}" $TMPDIR/value.${TESTCASE_ID} > /dev/null 2>&1
+ if [ $? -ne 0 ]
+ then
+ log_fail "${properties[$i]} not seen in output"
+ fi
+ $GREP "^NAME " $TMPDIR/value.${TESTCASE_ID} > /dev/null 2>&1
+ # only need to check this once.
+ if [ $i -eq 0 ] && [ $? -ne 0 ]
+ then
+ log_fail "Header not seen in zpool get output"
+ fi
+ i=$(( $i + 1 ))
+done
+
+$RM $TMPDIR/value.${TESTCASE_ID}
+log_pass "Zpool get returns values for all known properties"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_get/zpool_get_004_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_get/zpool_get_004_neg.ksh
new file mode 100644
index 000000000000..cb6ec440bf6a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_get/zpool_get_004_neg.ksh
@@ -0,0 +1,70 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_get_004_neg
+#
+# DESCRIPTION:
+#
+# Malformed zpool get commands are rejected
+#
+# STRATEGY:
+#
+# 1. Run several different "zpool get" commands that should fail.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-03-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+log_assert "Malformed zpool get commands are rejected"
+
+if ! is_global_zone ; then
+ TESTPOOL=${TESTPOOL%%/*}
+fi
+
+set -A arguments "$TESTPOOL $TESTPOOL" "$TESTPOOL rubbish" "-v $TESTPOOL" \
+ "nosuchproperty $TESTPOOL" "--$TESTPOOL" "all all" \
+ "type $TESTPOOL" "usage: $TESTPOOL" "bootfs $TESTPOOL@" \
+ "bootfs,bootfs $TESTPOOL" "name $TESTPOOL" "t%d%s" \
+ "bootfs,delegation $TESTPOOL" "delegation $TESTPOOL@"
+
+for arg in $arguments
+do
+ log_mustnot $ZPOOL get $arg
+done
+
+log_pass "Malformed zpool get commands are rejected"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_get/zpool_get_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zpool_get/zpool_get_test.sh
new file mode 100755
index 000000000000..c7081309349d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_get/zpool_get_test.sh
@@ -0,0 +1,128 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zpool_get_001_pos cleanup
+zpool_get_001_pos_head()
+{
+ atf_set "descr" "Zpool get usage message is displayed when called with no arguments."
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_get_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_get.cfg
+
+ ksh93 $(atf_get_srcdir)/zpool_get_001_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_get_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_get.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_get_002_pos cleanup
+zpool_get_002_pos_head()
+{
+ atf_set "descr" "Zpool get all works as expected"
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_get_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_get.cfg
+
+ verify_disk_count "$DISK" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_get_002_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_get_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_get.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_get_003_pos cleanup
+zpool_get_003_pos_head()
+{
+ atf_set "descr" "Zpool get returns values for all known properties"
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_get_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_get.cfg
+
+ verify_disk_count "$DISK" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_get_003_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_get_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_get.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_get_004_neg cleanup
+zpool_get_004_neg_head()
+{
+ atf_set "descr" "Malformed zpool get commands are rejected"
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_get_004_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_get.cfg
+
+ verify_disk_count "$DISK" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_get_004_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_get_004_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_get.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zpool_get_001_pos
+ atf_add_test_case zpool_get_002_pos
+ atf_add_test_case zpool_get_003_pos
+ atf_add_test_case zpool_get_004_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_history/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zpool_history/Makefile
new file mode 100644
index 000000000000..c1341eaeec67
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_history/Makefile
@@ -0,0 +1,17 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zpool_history
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zpool_history_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zpool_history_001_neg.ksh
+${PACKAGE}FILES+= zpool_history_002_pos.ksh
+${PACKAGE}FILES+= zpool_history.cfg
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_history/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_history/cleanup.ksh
new file mode 100644
index 000000000000..53e41e3d5e5b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_history/cleanup.ksh
@@ -0,0 +1,32 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+$ZPOOL history > /dev/null
+(($? != 0)) && log_unsupported
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_history/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_history/setup.ksh
new file mode 100644
index 000000000000..ef46198185bc
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_history/setup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_container_volume_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_history/zpool_history.cfg b/tests/sys/cddl/zfs/tests/cli_root/zpool_history/zpool_history.cfg
new file mode 100644
index 000000000000..e98337bc3d65
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_history/zpool_history.cfg
@@ -0,0 +1,30 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2013 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/tests/cli_root/cli.cfg
+
+export DISK=${DISKS%% *}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_history/zpool_history_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_history/zpool_history_001_neg.ksh
new file mode 100644
index 000000000000..a8608ac33a80
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_history/zpool_history_001_neg.ksh
@@ -0,0 +1,78 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_history_001_neg
+#
+# DESCRIPTION:
+# Verify 'zpool history' can deal with non-existent pools and garbage
+# to the command.
+#
+# STRATEGY:
+# 1. Create pool, volume & snap
+# 2. Verify 'zpool history' can cope with incorret arguments.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-07-07)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+snap=$TESTPOOL/$TESTFS@snap
+clone=$TESTPOOL/clone
+
+set -A neg_opt "$TESTPOOL/$TESTCTR" "$TESTPOOL/$TESTVOL" "-t $TESTPOOL" \
+ "-v $TESTPOOL" "$snap" "$clone" "nonexist" "TESTPOOL"
+
+function cleanup
+{
+ datasetexists $clone && log_must $ZFS destroy $clone
+ datasetexists $snap && log_must $ZFS destroy $snap
+}
+
+log_assert "Verify 'zpool history' can deal with non-existent pools and " \
+ "garbage to the command."
+log_onexit cleanup
+
+log_must $ZFS snapshot $snap
+log_must $ZFS clone $snap $clone
+
+for opt in "${neg_opt[@]}"; do
+ log_mustnot eval "$ZPOOL history $opt > /dev/null"
+done
+
+log_pass "'zpool history' command line negation test passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_history/zpool_history_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_history/zpool_history_002_pos.ksh
new file mode 100644
index 000000000000..530d7e23832e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_history/zpool_history_002_pos.ksh
@@ -0,0 +1,65 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_history_002_pos
+#
+# DESCRIPTION:
+# Verify zpool history can handle options [-il] correctly.
+#
+# STRATEGY:
+# 1. Create varied combinations of option -i & -l.
+# 2. Verify 'zpool history' can cope with these combination correctly.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-11-24)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+$ZFS 2>&1 | $GREP "allow" > /dev/null
+(($? != 0)) && log_unsupported
+
+log_assert "Verify zpool history can handle options [-il] correctly."
+
+options="-i -l -il -li -lil -ili -lli -iill -liil"
+
+for opt in $options; do
+ log_must eval "$ZPOOL history $opt $TESTPOOL > /dev/null 2>&1"
+done
+
+log_pass "Verify zpool history can handle options [-il] passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_history/zpool_history_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zpool_history/zpool_history_test.sh
new file mode 100755
index 000000000000..65a2dca1abd4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_history/zpool_history_test.sh
@@ -0,0 +1,80 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zpool_history_001_neg cleanup
+zpool_history_001_neg_head()
+{
+ atf_set "descr" "Verify 'zpool history' can deal with non-existent pools andgarbage to the command."
+ atf_set "require.progs" "ksh93 zfs zpool"
+}
+zpool_history_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_history.cfg
+
+ verify_disk_count "$DISK" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_history_001_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_history_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_history.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_history_002_pos cleanup
+zpool_history_002_pos_head()
+{
+ atf_set "descr" "Verify zpool history can handle options [-il] correctly."
+ atf_set "require.progs" "ksh93 zfs zpool"
+}
+zpool_history_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_history.cfg
+
+ verify_disk_count "$DISK" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_history_002_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_history_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_history.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zpool_history_001_neg
+ atf_add_test_case zpool_history_002_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/Makefile
new file mode 100644
index 000000000000..508bfef28e4c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/Makefile
@@ -0,0 +1,41 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zpool_import
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zpool_import_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zpool_import_006_pos.ksh
+${PACKAGE}FILES+= zpool_import_002_pos.ksh
+${PACKAGE}FILES+= zpool_import_missing_003_pos.ksh
+${PACKAGE}FILES+= zpool_import_all_001_pos.ksh
+${PACKAGE}FILES+= zpool_import.cfg
+${PACKAGE}FILES+= zpool_import_corrupt_001_pos.ksh
+${PACKAGE}FILES+= zpool_import_destroyed_001_neg.ksh
+${PACKAGE}FILES+= zpool_import_destroyed_002_neg.ksh
+${PACKAGE}FILES+= zpool_import_012_pos.ksh
+${PACKAGE}FILES+= zpool_import_013_neg.ksh
+${PACKAGE}FILES+= zpool_import_007_pos.ksh
+${PACKAGE}FILES+= zpool_import_003_pos.ksh
+${PACKAGE}FILES+= zpool_import_rename_001_pos.ksh
+${PACKAGE}FILES+= zpool_import_missing_002_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zpool_import_010_pos.ksh
+${PACKAGE}FILES+= zpool_import_014_pos.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zpool_import_011_neg.ksh
+${PACKAGE}FILES+= zpool_import_missing_004_pos.ksh
+${PACKAGE}FILES+= zpool_import_missing_005_pos.ksh
+${PACKAGE}FILES+= zpool_import_005_pos.ksh
+${PACKAGE}FILES+= zpool_import_009_neg.ksh
+${PACKAGE}FILES+= zpool_import_missing_001_pos.ksh
+${PACKAGE}FILES+= zpool_import_008_pos.ksh
+${PACKAGE}FILES+= zpool_import_004_pos.ksh
+${PACKAGE}FILES+= zpool_import.kshlib
+
+SUBDIR+= blockfiles
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/blockfiles/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/blockfiles/Makefile
new file mode 100644
index 000000000000..13235edd1f47
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/blockfiles/Makefile
@@ -0,0 +1,10 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zpool_import/blockfiles
+FILESDIR=${TESTSDIR}
+
+${PACKAGE}FILES+= README
+${PACKAGE}FILES+= unclean_export.dat.bz2
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/blockfiles/README b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/blockfiles/README
new file mode 100644
index 000000000000..e7f7c2e445b9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/blockfiles/README
@@ -0,0 +1,29 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2012 by Delphix. All rights reserved.
+#
+
+Unless otherwise noted, all files in this distribution are released
+under the Common Development and Distribution License (CDDL).
+
+This directory contains compressed blockfiles for zpool import testing.
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/blockfiles/unclean_export.dat.bz2 b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/blockfiles/unclean_export.dat.bz2
new file mode 100644
index 000000000000..a7e042b523df
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/blockfiles/unclean_export.dat.bz2
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/cleanup.ksh
new file mode 100644
index 000000000000..ea6e18e5993b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/cleanup.ksh
@@ -0,0 +1,42 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+for pool in "$TESTPOOL" "$TESTPOOL1"; do
+ datasetexists $pool/$TESTFS && \
+ log_must $ZFS destroy -Rf $pool/$TESTFS
+ destroy_pool "$pool"
+done
+
+for dir in "$TESTDIR" "$TESTDIR1" "$DEVICE_DIR" ; do
+ [[ -d $dir ]] && \
+ log_must $RM -rf $dir
+done
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/setup.ksh
new file mode 100644
index 000000000000..7acb5760d490
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/setup.ksh
@@ -0,0 +1,50 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+create_pool "$TESTPOOL" "$DISK0"
+
+if [[ -d $TESTDIR ]]; then
+ $RM -rf $TESTDIR || log_unresolved Could not remove $TESTDIR
+ $MKDIR -p $TESTDIR || log_unresolved Could not create $TESTDIR
+fi
+
+log_must $ZFS create $TESTPOOL/$TESTFS
+log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+
+[[ ! -d $DEVICE_DIR ]] && \
+ log_must $MKDIR -p $DEVICE_DIR
+
+i=0
+while (( i < $MAX_NUM )); do
+ log_must create_vdevs ${DEVICE_DIR}/${DEVICE_FILE}$i
+ (( i = i + 1 ))
+done
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import.cfg b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import.cfg
new file mode 100644
index 000000000000..9639b0240541
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import.cfg
@@ -0,0 +1,104 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
+. $STF_SUITE/include/libtest.kshlib
+
+set_disks
+
+export FS_SIZE=2gb
+export FILE_SIZE=64m
+export PART_SIZE=128m
+export MAX_NUM=5
+export GROUP_NUM=3
+export DEVICE_DIR=$(pwd)/dev
+export BACKUP_DEVICE_DIR=$(pwd)/bakdev
+export DEVICE_FILE=disk
+export DEVICE_ARCHIVE=$(pwd)/archive${TESTCASE_ID}.tar
+# MYTESTFILE can be any file that exists and we have r access to
+export MYTESTFILE=$STF_SUITE/include/default.cfg
+
+# NB: It's easier just to repeat the expansion patterns than to reuse.
+for (( num=0 ; $num < $GROUP_NUM ; num += 1 )); do
+ eval export VDEV\${num}F="${DEVICE_FILE}${num}"
+ eval export VDEV\${num}="${DEVICE_DIR}/${DEVICE_FILE}${num}"
+ DEVICE_FILES="$DEVICE_FILES ${DEVICE_DIR}/${DEVICE_FILE}${num}"
+done
+export DEVICE_FILES
+for (( num = GROUP_NUM ; $num < $MAX_NUM ; num += 1 )); do
+ eval export VDEV\${num}F="${DEVICE_FILE}${num}"
+ eval export VDEV\${num}="${DEVICE_DIR}/${DEVICE_FILE}${num}"
+done
+
+export ALTER_ROOT=/alter${TESTCASE_ID}
+export STF_TIMEOUT=2400
+
+export ZPOOL_VERSION=$(get_zpool_version)
+
+# Version 1 pools
+export ZPOOL_VERSION_1_FILES="zfs-pool-v1.dat"
+export ZPOOL_VERSION_1_NAME="v1-pool"
+
+# Version 2 pools
+export ZPOOL_VERSION_2_FILES="zfs-pool-v2.dat"
+export ZPOOL_VERSION_2_NAME="v2-pool"
+
+# This is a v3 pool
+export ZPOOL_VERSION_3_FILES="zfs-pool-v3.dat"
+export ZPOOL_VERSION_3_NAME="v3-pool"
+
+# This is a v6 pool
+export ZPOOL_VERSION_6_FILES="zfs-pool-v6.dat"
+export ZPOOL_VERSION_6_NAME="v6-pool"
+
+# This is a v7 pool
+export ZPOOL_VERSION_7_FILES="zfs-pool-v7.dat"
+export ZPOOL_VERSION_7_NAME="v7-pool"
+
+# This is a v8 pool
+export ZPOOL_VERSION_8_FILES="zfs-pool-v8.dat"
+export ZPOOL_VERSION_8_NAME="v8-pool"
+
+# This statement builds up a list of configurations we should be able to
+# upgrade, for each pool version. Once we've built this variable, we'll
+# call the functions above for each value.
+case $ZPOOL_VERSION in
+6)
+ CONFIGS="6"
+ ;;
+7)
+ CONFIGS="6 7"
+ ;;
+8)
+ CONFIGS="6 7 8"
+ ;;
+*)
+ # we should be able to upgrade pools of version 1, 2 & 3
+ # but we should also log a note about the unknown pool version
+ CONFIGS="6 7 8"
+ ;;
+esac
+export CONFIGS
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import.kshlib b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import.kshlib
new file mode 100644
index 000000000000..7092c472b210
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import.kshlib
@@ -0,0 +1,33 @@
+# vim: filetype=sh
+# Common routines used by zpool_import*.
+
+function setup_missing_test_pool # <tvd_type>
+{
+ typeset tvd_type=$1
+
+ setup_filesystem "$DEVICE_FILES" $TESTPOOL1 $TESTFS $TESTDIR1 \
+ "" $tvd_type
+ log_must $CP $MYTESTFILE $TESTDIR1/$TESTFILE0
+ log_must $ZPOOL export $TESTPOOL1
+}
+
+function recreate_missing_files
+{
+ destroy_pool $TESTPOOL1
+ log_must $RM -rf $DEVICE_DIR/*
+ for (( devnum=0 ; $devnum < $MAX_NUM ; devnum += 1 )); do
+ log_must create_vdevs ${DEVICE_DIR}/${DEVICE_FILE}$devnum
+ done
+}
+
+function cleanup_missing
+{
+ log_note "State of pools at the end of the test:"
+ poolexists $TESTPOOL1 && log_must $ZPOOL status $TESTPOOL1
+ log_note "State of $DEVICE_DIR at the end of the test:"
+ log_cmd $ZPOOL import -d $DEVICE_DIR
+ destroy_pool $TESTPOOL1
+ log_must $RM -rf $DEVICE_DIR/*
+ [[ -d $ALTER_ROOT ]] && log_must $RM -rf $ALTER_ROOT
+ [[ -d $BACKUP_DEVICE_DIR ]] && log_must $RM -rf $BACKUP_DEVICE_DIR
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_002_pos.ksh
new file mode 100644
index 000000000000..8cf8fca84f21
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_002_pos.ksh
@@ -0,0 +1,161 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_mount/zfs_mount.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_import_002_pos
+#
+# DESCRIPTION:
+# Verify that an exported pool cannot be imported
+# more than once.
+#
+# STRATEGY:
+# 1. Populate the default test directory and unmount it.
+# 2. Export the default test pool.
+# 3. Import it using the various combinations.
+# - Regular import
+# - Alternate Root Specified
+# 4. Verify it shows up under 'zpool list'.
+# 5. Verify it contains a file.
+# 6. Attempt to import it for a second time. Verify this fails.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+set -A pools "$TESTPOOL" "$TESTPOOL1"
+set -A devs "" "-d $DEVICE_DIR"
+set -A options "" "-R $ALTER_ROOT"
+set -A mtpts "$TESTDIR" "$TESTDIR1"
+
+
+function cleanup
+{
+ typeset -i i=0
+ while (( i < ${#pools[*]} )); do
+ poolexists ${pools[i]} && \
+ log_must $ZPOOL export ${pools[i]}
+
+ datasetexists "${pools[i]}/$TESTFS" || \
+ log_must $ZPOOL import ${devs[i]} ${pools[i]}
+
+ ismounted "${pools[i]}/$TESTFS" || \
+ log_must $ZFS mount ${pools[i]}/$TESTFS
+
+ [[ -e ${mtpts[i]}/$TESTFILE0 ]] && \
+ log_must $RM -rf ${mtpts[i]}/$TESTFILE0
+
+ ((i = i + 1))
+ done
+
+ cleanup_filesystem $TESTPOOL1 $TESTFS
+
+ destroy_pool $TESTPOOL1
+
+ [[ -d $ALTER_ROOT ]] && \
+ log_must $RM -rf $ALTER_ROOT
+}
+
+log_onexit cleanup
+
+log_assert "Verify that an exported pool cannot be imported more than once."
+
+setup_filesystem "$DEVICE_FILES" $TESTPOOL1 $TESTFS $TESTDIR1
+
+checksum1=$($SUM $MYTESTFILE | $AWK '{print $1}')
+
+typeset -i i=0
+typeset -i j=0
+typeset basedir
+
+function inner_test
+{
+ typeset pool=$1
+ typeset target=$2
+ typeset devs=$3
+ typeset opts=$4
+ typeset mtpt=$5
+
+ log_must $ZPOOL import ${devs} ${opts} $target
+ log_must poolexists $pool
+ log_must ismounted $pool/$TESTFS
+
+ basedir=$mtpt
+ [ -n "$opts" ] && basedir="$ALTER_ROOT/$mtpt"
+
+ [ ! -e "$basedir/$TESTFILE0" ] && \
+ log_fail "ERROR: $basedir/$TESTFILE0 missing after import."
+
+ checksum2=$($SUM $basedir/$TESTFILE0 | $AWK '{print $1}')
+ [[ "$checksum1" != "$checksum2" ]] && \
+ log_fail "ERROR: Checksums differ ($checksum1 != $checksum2)"
+
+ log_mustnot $ZPOOL import $devs $target
+}
+
+while (( i < ${#pools[*]} )); do
+ log_must $CP $MYTESTFILE ${mtpts[i]}/$TESTFILE0
+
+ log_must $ZFS umount ${mtpts[i]}
+
+ j=0
+ while (( j < ${#options[*]} )); do
+ typeset pool=${pools[i]}
+ typeset vdevdir=""
+
+ log_must $ZPOOL export $pool
+
+ [ "$pool" = "$TESTPOOL1" ] && vdevdir="$DEVICE_DIR"
+ guid=$(get_config $pool pool_guid $vdevdir)
+ log_must test -n "$guid"
+ log_note "Importing '$pool' by guid '$guid'"
+ inner_test $pool $guid "${devs[i]}" "${options[j]}" ${mtpts[i]}
+
+ log_must $ZPOOL export $pool
+
+ log_note "Importing '$pool' by name."
+ inner_test $pool $pool "${devs[i]}" "${options[j]}" ${mtpts[i]}
+
+ ((j = j + 1))
+ done
+
+ ((i = i + 1))
+
+done
+
+log_pass "Able to import exported pools and import only once."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_003_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_003_pos.ksh
new file mode 100644
index 000000000000..bed06a12faa4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_003_pos.ksh
@@ -0,0 +1,73 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_import_003_pos
+#
+# DESCRIPTION:
+# Destroyed pools are not listed unless with -D option is specified.
+#
+# STRATEGY:
+# 1. Create test pool A.
+# 2. Destroy pool A.
+# 3. Verify only 'import -D' can list pool A.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-12)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ destroy_pool $TESTPOOL1
+ log_must $RM $VDEV0 $VDEV1
+}
+
+log_assert "Destroyed pools are not listed unless with -D option is specified."
+log_onexit cleanup
+
+log_must $ZPOOL create $TESTPOOL1 $VDEV0 $VDEV1
+log_must $ZPOOL destroy $TESTPOOL1
+
+#
+# 'pool:' is the keywords of 'zpool import -D' output.
+#
+log_mustnot eval "$ZPOOL import -d $DEVICE_DIR | $GREP pool:"
+log_must eval "$ZPOOL import -d $DEVICE_DIR -D | $GREP pool:"
+
+log_pass "Destroyed pool only can be listed with -D option."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_004_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_004_pos.ksh
new file mode 100644
index 000000000000..cde3c05380dc
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_004_pos.ksh
@@ -0,0 +1,105 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_import_004_pos
+#
+# DESCRIPTION:
+# Destroyed pools devices was moved to another directory, it still can be
+# imported correctly.
+#
+# STRATEGY:
+# 1. Create test pool A with several devices.
+# 2. Destroy pool A.
+# 3. Move devices to another directory.
+# 4. Verify 'zpool import -D' succeed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-12)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ destroy_pool $TESTPOOL1
+ log_must $RM -rf $DEVICE_DIR/*
+}
+
+function perform_test
+{
+ target=$1
+
+ assert_pool_in_cachefile $TESTPOOL1
+ log_must $ZPOOL destroy $TESTPOOL1
+
+ log_note "Devices was moved to different directories."
+ log_must $MKDIR -p $DEVICE_DIR/newdir1 $DEVICE_DIR/newdir2
+ log_must $MV $VDEV1 $DEVICE_DIR/newdir1
+ log_must $MV $VDEV2 $DEVICE_DIR/newdir2
+ log_must $ZPOOL import -d $DEVICE_DIR/newdir1 -d $DEVICE_DIR/newdir2 \
+ -d $DEVICE_DIR -D -f $target
+ log_must $ZPOOL destroy -f $TESTPOOL1
+
+ log_note "Devices was moved to same directory."
+ log_must $MV $VDEV0 $DEVICE_DIR/newdir2
+ log_must $MV $DEVICE_DIR/newdir1/* $DEVICE_DIR/newdir2
+ log_must $ZPOOL import -d $DEVICE_DIR/newdir2 -D -f $target
+ log_must $ZPOOL destroy -f $TESTPOOL1
+
+ # Revert at the end so this test can be rerun.
+ log_must $MV $DEVICE_DIR/newdir2/$VDEV0F $VDEV0
+ log_must $MV $DEVICE_DIR/newdir2/$VDEV1F $VDEV1
+ log_must $MV $DEVICE_DIR/newdir2/$VDEV2F $VDEV2
+}
+
+log_assert "Destroyed pools devices was moved to another directory," \
+ "it still can be imported correctly."
+log_onexit cleanup
+
+log_must $ZPOOL create $TESTPOOL1 $VDEV0 $VDEV1 $VDEV2
+log_note "Testing import by name '$TESTPOOL1'."
+perform_test $TESTPOOL1
+
+log_must $ZPOOL create $TESTPOOL1 $VDEV0 $VDEV1 $VDEV2
+log_must $ZPOOL status $TESTPOOL1
+log_must $ZDB -C $TESTPOOL1
+typeset guid=$(get_config $TESTPOOL1 pool_guid)
+log_note "Testing import by GUID '${guid}'."
+perform_test $guid
+
+log_pass "Destroyed pools devices was moved, 'zpool import -D' passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_005_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_005_pos.ksh
new file mode 100644
index 000000000000..2b0b0da677bb
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_005_pos.ksh
@@ -0,0 +1,107 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_import_005_pos
+#
+# DESCRIPTION:
+# Destroyed pools devices was renamed, it still can be imported correctly.
+#
+# STRATEGY:
+# 1. Create test pool A with several devices.
+# 2. Destroy pool A and rename devices name.
+# 3. Verify 'zpool import -D' succeed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-12)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ destroy_pool $TESTPOOL1
+
+ log_must $RM -rf $DEVICE_DIR/*
+ typeset i=0
+ while (( i < $MAX_NUM )); do
+ log_must create_vdevs ${DEVICE_DIR}/${DEVICE_FILE}$i
+ ((i += 1))
+ done
+}
+
+log_assert "Destroyed pools devices was renamed, it still can be imported " \
+ "correctly."
+log_onexit cleanup
+
+function perform_test
+{
+ typeset target=$1
+
+ assert_pool_in_cachefile $TESTPOOL1
+ log_must $ZPOOL destroy $TESTPOOL1
+
+ log_note "Testing some devices renamed in the same directory."
+ log_must $MV $VDEV0 $DEVICE_DIR/vdev0-new
+ log_must $ZPOOL import -d $DEVICE_DIR -D -f $target
+ log_must $ZPOOL destroy -f $TESTPOOL1
+
+ log_note "Testing all devices moved to different directories."
+ log_must $MKDIR -p $DEVICE_DIR/newdir1 $DEVICE_DIR/newdir2
+ log_must $MV $VDEV1 $DEVICE_DIR/newdir1/vdev1-new
+ log_must $MV $VDEV2 $DEVICE_DIR/newdir2/vdev2-new
+ log_must $ZPOOL import -d $DEVICE_DIR/newdir1 -d $DEVICE_DIR/newdir2 \
+ -d $DEVICE_DIR -D -f $target
+ log_must $ZPOOL destroy -f $TESTPOOL1
+
+ # Restore the vdevs to their old location so this can be re-run
+ log_note "Restoring vdev files for any further runs."
+ log_must $MV $DEVICE_DIR/vdev0-new $VDEV0
+ log_must $MV $DEVICE_DIR/newdir1/vdev1-new $VDEV1
+ log_must $MV $DEVICE_DIR/newdir2/vdev2-new $VDEV2
+}
+
+log_note "Testing import by name."
+log_must $ZPOOL create $TESTPOOL1 $VDEV0 $VDEV1 $VDEV2
+perform_test $TESTPOOL1
+
+log_note "Testing import by GUID."
+log_must $ZPOOL create $TESTPOOL1 $VDEV0 $VDEV1 $VDEV2
+typeset guid=$(get_config $TESTPOOL1 pool_guid)
+perform_test $guid
+
+log_pass "Destroyed pools devices was renamed, 'zpool import -D' passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_006_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_006_pos.ksh
new file mode 100644
index 000000000000..cc3497fcf990
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_006_pos.ksh
@@ -0,0 +1,102 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_import_006_pos
+#
+# DESCRIPTION:
+# For mirror, N-1 destroyed pools devices was removed or used by other
+# pool, it still can be imported correctly.
+#
+# STRATEGY:
+# 1. Create mirror with N disks.
+# 2. Destroy this mirror.
+# 3. Create another pool with N-1 disks which was used by this mirror.
+# 4. Verify import mirror can succeed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-12)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ destroy_pool $TESTPOOL2
+ destroy_pool $TESTPOOL1
+
+ log_must $RM -rf $DEVICE_DIR/*
+ typeset i=0
+ while (( i < $MAX_NUM )); do
+ log_must create_vdevs ${DEVICE_DIR}/${DEVICE_FILE}$i
+ ((i += 1))
+ done
+}
+
+log_assert "For mirror, N-1 destroyed pools devices was removed or used " \
+ "by other pool, it still can be imported correctly."
+log_onexit cleanup
+
+function perform_test
+{
+ typeset target=$1
+
+ assert_pool_in_cachefile $TESTPOOL1
+ log_must $ZPOOL destroy $TESTPOOL1
+
+ create_pool $TESTPOOL2 $VDEV0 $VDEV2
+ log_must $ZPOOL import -d $DEVICE_DIR -D -f $target
+ log_must $ZPOOL destroy $TESTPOOL1
+
+ log_must $ZPOOL destroy $TESTPOOL2
+ log_must $RM -rf $VDEV2
+ log_must $ZPOOL import -d $DEVICE_DIR -D -f $target
+
+ # Restore the vdev.
+ log_must create_vdevs $VDEV2
+}
+
+log_note "Testing import by name."
+create_pool $TESTPOOL1 mirror $VDEV0 $VDEV1 $VDEV2
+perform_test $TESTPOOL1
+
+log_note "Testing import by GUID."
+create_pool $TESTPOOL1 mirror $VDEV0 $VDEV1 $VDEV2
+typeset guid=$(get_config $TESTPOOL1 pool_guid)
+perform_test $guid
+
+log_pass "zpool import -D mirror passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_007_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_007_pos.ksh
new file mode 100644
index 000000000000..9ee8e19645c9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_007_pos.ksh
@@ -0,0 +1,106 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_import_007_pos
+#
+# DESCRIPTION:
+# For raidz, one destroyed pools devices was removed or used by other
+# pool, it still can be imported correctly.
+#
+# STRATEGY:
+# 1. Create a raidz pool A with N disks.
+# 2. Destroy this pool A.
+# 3. Create another pool B with 1 disk which was used by pool A.
+# 4. Verify import this raidz pool can succeed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-12)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ destroy_pool $TESTPOOL2
+ destroy_pool $TESTPOOL1
+
+ log_must $RM -rf $DEVICE_DIR/*
+ typeset i=0
+ while (( i < $MAX_NUM )); do
+ log_must create_vdevs ${DEVICE_DIR}/${DEVICE_FILE}$i
+ ((i += 1))
+ done
+}
+
+log_assert "For raidz, one destroyed pools devices was removed or used by " \
+ "other pool, it still can be imported correctly."
+log_onexit cleanup
+
+function perform_test
+{
+ typeset target=$1
+
+ assert_pool_in_cachefile $TESTPOOL1
+ log_must $ZPOOL destroy $TESTPOOL1
+
+ log_must $ZPOOL create $TESTPOOL2 $VDEV0
+ log_must $ZPOOL import -d $DEVICE_DIR -D -f $target
+ log_must $ZPOOL destroy $TESTPOOL1
+
+ log_must $ZPOOL destroy $TESTPOOL2
+ log_must $RM -rf $VDEV0
+ log_must $ZPOOL import -d $DEVICE_DIR -D -f $target
+ log_must $ZPOOL destroy $TESTPOOL1
+
+ log_note "For raidz, two destroyed pool's devices were used, import failed."
+ log_must create_vdevs $VDEV0
+ log_must $ZPOOL create $TESTPOOL2 $VDEV0 $VDEV1
+ log_mustnot $ZPOOL import -d $DEVICE_DIR -D -f $target
+ log_must $ZPOOL destroy $TESTPOOL2
+}
+
+log_note "Testing import by name."
+log_must $ZPOOL create $TESTPOOL1 raidz $VDEV0 $VDEV1 $VDEV2 $VDEV3
+perform_test $TESTPOOL1
+
+log_note "Testing import by GUID."
+log_must $ZPOOL create $TESTPOOL1 raidz $VDEV0 $VDEV1 $VDEV2 $VDEV3
+typeset guid=$(get_config $TESTPOOL1 pool_guid)
+perform_test $guid
+
+log_pass "zpool import -D raidz passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_008_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_008_pos.ksh
new file mode 100644
index 000000000000..1789efcae390
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_008_pos.ksh
@@ -0,0 +1,107 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_import_008_pos
+#
+# DESCRIPTION:
+# For raidz2, two destroyed pool's devices were removed or used by other
+# pool, it still can be imported correctly.
+#
+# STRATEGY:
+# 1. Create a raidz2 pool A with N disks.
+# 2. Destroy this pool A.
+# 3. Create another pool B with two disks which were used by pool A.
+# 4. Verify import this raidz2 pool can succeed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-12)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ destroy_pool $TESTPOOL2
+ destroy_pool $TESTPOOL1
+
+ log_must $RM -rf $DEVICE_DIR/*
+ typeset i=0
+ while (( i < $MAX_NUM )); do
+ log_must create_vdevs ${DEVICE_DIR}/${DEVICE_FILE}$i
+ ((i += 1))
+ done
+}
+
+function perform_test
+{
+ typeset target=$1
+
+ assert_pool_in_cachefile $TESTPOOL1
+ log_must $ZPOOL destroy $TESTPOOL1
+
+ log_must $ZPOOL create $TESTPOOL2 $VDEV0 $VDEV1
+ log_must $ZPOOL import -d $DEVICE_DIR -D -f $target
+ log_must $ZPOOL destroy $TESTPOOL1
+
+ log_must $ZPOOL destroy $TESTPOOL2
+ log_must $RM -rf $VDEV0 $VDEV1
+ log_must $ZPOOL import -d $DEVICE_DIR -D -f $target
+ log_must $ZPOOL destroy $TESTPOOL1
+
+ log_note "For raidz2, more than two destroyed pool's devices were used, " \
+ "import failed."
+ log_must create_vdevs $VDEV0 $VDEV1
+ log_must $ZPOOL create $TESTPOOL2 $VDEV0 $VDEV1 $VDEV2
+ log_mustnot $ZPOOL import -d $DEVICE_DIR -D -f $target
+ log_must $ZPOOL destroy $TESTPOOL2
+}
+
+log_assert "For raidz2, two destroyed pools devices was removed or used by " \
+ "other pool, it still can be imported correctly."
+log_onexit cleanup
+
+log_note "Testing import by name."
+log_must $ZPOOL create $TESTPOOL1 raidz2 $VDEV0 $VDEV1 $VDEV2 $VDEV3
+perform_test $TESTPOOL1
+
+log_note "Testing import by GUID."
+log_must $ZPOOL create $TESTPOOL1 raidz2 $VDEV0 $VDEV1 $VDEV2 $VDEV3
+typeset guid=$(get_config $TESTPOOL1 pool_guid)
+perform_test $guid
+
+log_pass "zpool import -D raidz2 passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_009_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_009_neg.ksh
new file mode 100644
index 000000000000..5b6bfc5150e3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_009_neg.ksh
@@ -0,0 +1,110 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_mount/zfs_mount.kshlib
+
+#################################################################################
+# __stc_assertion_start
+#
+# ID: zpool_import_009_neg
+#
+# DESCRIPTION:
+# Try each 'zpool import' with inapplicable scenarios to make sure
+# it returns an error. include:
+# * A non-existent pool name is given
+# * '-d', but no device directory specified
+# * '-R', but no alter root directory specified
+# * '-a', but a pool name specified either
+# * more than 2 pool names is given
+# * The new pool name specified already exists
+# * Contain invalid characters not allowed in the ZFS namespace
+#
+# STRATEGY:
+# 1. Create an array of parameters
+# 2. For each parameter in the array, execute the sub-command
+# 3. Verify an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-07)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+set -A args "blah" "-d" "-R" "-a $TESTPOOL" \
+ "$TESTPOOL ${TESTPOOL}-new ${TESTPOOL}-new" \
+ "$TESTPOOL $TESTPOOL1" \
+ "$TESTPOOL ${TESTPOOL1}*" "$TESTPOOL ${TESTPOOL1}?"
+
+set -A pools "$TESTPOOL" "$TESTPOOL1"
+set -A devs "" "-d $DEVICE_DIR"
+
+function cleanup
+{
+ typeset -i i=0
+ typeset -i j=0
+
+ while (( i < ${#pools[*]} )); do
+
+ poolexists ${pools[i]} && \
+ log_must $ZPOOL export ${pools[i]}
+
+ datasetexists "${pools[i]}/$TESTFS" || \
+ log_must $ZPOOL import ${devs[i]} ${pools[i]}
+
+ ismounted "${pools[i]}/$TESTFS" || \
+ log_must $ZFS mount ${pools[i]}/$TESTFS
+
+ ((i = i + 1))
+ done
+
+ cleanup_filesystem $TESTPOOL1 $TESTFS
+
+ destroy_pool $TESTPOOL1
+}
+
+log_onexit cleanup
+
+log_assert "Badly-formed 'zpool import' with inapplicable scenarios " \
+ "should return an error."
+
+setup_filesystem "$DEVICE_FILES" $TESTPOOL1 $TESTFS $TESTDIR1
+
+log_must $ZPOOL export $TESTPOOL
+
+typeset -i i=0
+while (( i < ${#args[*]} )); do
+ log_mustnot $ZPOOL import ${args[i]}
+ ((i = i + 1))
+done
+
+log_pass "Badly formed 'zpool import' with inapplicable scenarios " \
+ "fail as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_010_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_010_pos.ksh
new file mode 100644
index 000000000000..89562b7ed17b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_010_pos.ksh
@@ -0,0 +1,104 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_import_010_pos
+#
+# DESCRIPTION:
+# 'zpool -D -a' can import all the specified directories destroyed pools.
+#
+# STRATEGY:
+# 1. Create a 5 ways mirror pool A with dev0/1/2/3/4, then destroy it.
+# 2. Create a stripe pool B with dev1. Then destroy it.
+# 3. Create a raidz2 pool C with dev2/3/4. Then destroy it.
+# 4. Create a raidz pool D with dev3/4. Then destroy it.
+# 5. Create a stripe pool E with dev4. Then destroy it.
+# 6. Verify 'zpool import -D -a' recover all the pools.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-12)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ typeset dt
+ for dt in $poolE $poolD $poolC $poolB $poolA; do
+ destroy_pool $dt
+ done
+
+ log_must $RM -rf $DEVICE_DIR/*
+ typeset i=0
+ while (( i < $MAX_NUM )); do
+ log_must create_vdevs ${DEVICE_DIR}/${DEVICE_FILE}$i
+ ((i += 1))
+ done
+}
+
+log_assert "'zpool -D -a' can import all the specified directories " \
+ "destroyed pools."
+log_onexit cleanup
+
+poolA=poolA.${TESTCASE_ID}
+poolB=poolB.${TESTCASE_ID}
+poolC=poolC.${TESTCASE_ID}
+poolD=poolD.${TESTCASE_ID}
+poolE=poolE.${TESTCASE_ID}
+
+log_must $ZPOOL create $poolA mirror $VDEV0 $VDEV1 $VDEV2 $VDEV3 $VDEV4
+log_must $ZPOOL destroy $poolA
+
+log_must $ZPOOL create $poolB $VDEV1
+log_must $ZPOOL destroy $poolB
+
+log_must $ZPOOL create $poolC raidz2 $VDEV2 $VDEV3 $VDEV4
+log_must $ZPOOL destroy $poolC
+
+log_must $ZPOOL create $poolD raidz $VDEV3 $VDEV4
+log_must $ZPOOL destroy $poolD
+
+log_must $ZPOOL create $poolE $VDEV4
+log_must $ZPOOL destroy $poolE
+
+log_must $ZPOOL import -d $DEVICE_DIR -D -f -a
+
+for dt in $poolA $poolB $poolC $poolD $poolE; do
+ log_must datasetexists $dt
+done
+
+log_pass "'zpool -D -a' test passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_011_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_011_neg.ksh
new file mode 100644
index 000000000000..9c9333bcaee5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_011_neg.ksh
@@ -0,0 +1,89 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_import_011_neg
+#
+# DESCRIPTION:
+# For strip pool, any destroyed pool devices was demaged, zpool import -D
+# will failed.
+#
+# STRATEGY:
+# 1. Create strip pool A with three devices.
+# 2. Destroy this pool B.
+# 3. Create pool B with one of devices in step 1.
+# 4. Verify 'import -D' pool A will failed whenever pool B was destroyed
+# or not.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-12)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ destroy_pool $TESTPOOL1
+ destroy_pool $TESTPOOL2
+
+ #
+ # Recreate virtual devices to avoid destroyed pool information on files.
+ #
+ log_must $RM -rf $VDEV0 $VDEV1 $VDEV2
+ log_must create_vdevs $VDEV0 $VDEV1 $VDEV2
+}
+
+log_assert "For strip pool, any destroyed pool devices was demaged," \
+ "zpool import -D will failed."
+log_onexit cleanup
+
+log_must $ZPOOL create $TESTPOOL1 $VDEV0 $VDEV1 $VDEV2
+typeset guid=$(get_config $TESTPOOL1 pool_guid)
+typeset target=$TESTPOOL1
+if (( RANDOM % 2 == 0 )) ; then
+ target=$guid
+ log_note "Import by guid."
+fi
+log_must $ZPOOL destroy $TESTPOOL1
+log_must $ZPOOL create $TESTPOOL2 $VDEV2
+
+log_mustnot $ZPOOL import -d $DEVICE_DIR -D -f $target
+
+log_must $ZPOOL destroy $TESTPOOL2
+log_mustnot $ZPOOL import -d $DEVICE_DIR -D -f $target
+
+log_pass "Any strip pool devices damaged, pool can't be import passed."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_012_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_012_pos.ksh
new file mode 100644
index 000000000000..cc2f4a87674d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_012_pos.ksh
@@ -0,0 +1,189 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_mount/zfs_mount.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_import_012_pos
+#
+# DESCRIPTION:
+# Once a pool has been exported, it should be recreated after a
+# successful import, all the sub-filesystems within it should all be restored,
+# include mount & share status. Verify that is true.
+#
+# STRATEGY:
+# 1. Create the test pool and hierarchical filesystems.
+# 2. Export the test pool, or destroy the test pool,
+# depend on testing import [-Df].
+# 3. Import it using the various combinations.
+# - Regular import
+# - Alternate Root Specified
+# 4. Verify the mount & share status is restored.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-11-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+set -A pools "$TESTPOOL" "$TESTPOOL1"
+set -A devs "" "-d $DEVICE_DIR"
+set -A options "" "-R $ALTER_ROOT"
+set -A mtpts "$TESTDIR" "$TESTDIR1"
+
+
+function cleanup
+{
+ typeset -i i=0
+
+ while (( i < ${#pools[*]} )); do
+ if poolexists ${pools[i]} ; then
+ log_must $ZPOOL export ${pools[i]}
+ log_note "Try to import ${devs[i]} ${pools[i]}"
+ $ZPOOL import ${devs[i]} ${pools[i]}
+ else
+ log_note "Try to import $option ${devs[i]} ${pools[i]}"
+ $ZPOOL import $option ${devs[i]} ${pools[i]}
+ fi
+
+ if poolexists ${pools[i]} ; then
+ is_shared ${pools[i]} && \
+ log_must $ZFS set sharenfs=off ${pools[i]}
+
+ ismounted "${pools[i]}/$TESTFS" || \
+ log_must $ZFS mount ${pools[i]}/$TESTFS
+ fi
+
+ ((i = i + 1))
+ done
+
+ destroy_pool $TESTPOOL1
+
+ if datasetexists $TESTPOOL/$TESTFS ; then
+ log_must $ZFS destroy -Rf $TESTPOOL/$TESTFS
+ fi
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+
+ [[ -d $ALTER_ROOT ]] && \
+ log_must $RM -rf $ALTER_ROOT
+}
+
+log_onexit cleanup
+
+log_assert "Verify all mount & share status of sub-filesystems within a pool \
+ can be restored after import [-Df]."
+
+setup_filesystem "$DEVICE_FILES" $TESTPOOL1 $TESTFS $TESTDIR1
+for pool in ${pools[@]} ; do
+ log_must $ZFS create $pool/$TESTFS/$TESTCTR
+ log_must $ZFS create $pool/$TESTFS/$TESTCTR/$TESTCTR1
+ log_must $ZFS set canmount=off $pool/$TESTFS/$TESTCTR
+ log_must $ZFS set canmount=off $pool/$TESTFS/$TESTCTR/$TESTCTR1
+ log_must $ZFS create $pool/$TESTFS/$TESTCTR/$TESTFS1
+ log_must $ZFS create $pool/$TESTFS/$TESTCTR/$TESTCTR1/$TESTFS1
+ log_must $ZFS create $pool/$TESTFS/$TESTFS1
+ log_must $ZFS snapshot $pool/$TESTFS/$TESTFS1@snap
+ log_must $ZFS clone $pool/$TESTFS/$TESTFS1@snap $pool/$TESTCLONE1
+done
+
+typeset mount_fs="$TESTFS $TESTFS/$TESTFS1 $TESTCLONE1 \
+ $TESTFS/$TESTCTR/$TESTFS1 $TESTFS/$TESTCTR/$TESTCTR1/$TESTFS1"
+typeset nomount_fs="$TESTFS/$TESTCTR $TESTFS/$TESTCTR/$TESTCTR1"
+
+typeset -i i=0
+typeset -i j=0
+typeset basedir
+
+for option in "" "-Df" ; do
+ i=0
+ while (( i < ${#pools[*]} )); do
+ pool=${pools[i]}
+ guid=$(get_config $pool pool_guid)
+ j=0
+ while (( j < ${#options[*]} )); do
+ typeset f_share=""
+ if ((RANDOM % 2 == 0)); then
+ log_note "Set sharenfs=on $pool"
+ log_must $ZFS set sharenfs=on $pool
+ log_must is_shared $pool
+ f_share="true"
+ fi
+
+ if [[ -z $option ]]; then
+ log_must $ZPOOL export $pool
+ else
+ log_must $ZPOOL destroy $pool
+ fi
+
+ typeset target=$pool
+ if (( RANDOM % 2 == 0 )) ; then
+ log_note "Import by guid."
+ if [[ -z $guid ]]; then
+ log_fail "guid should not be empty!"
+ else
+ target=$guid
+ fi
+ fi
+ log_must $ZPOOL import $option \
+ ${devs[i]} ${options[j]} $target
+
+ log_must poolexists $pool
+
+ for fs in $mount_fs ; do
+ log_must ismounted $pool/$fs
+ [[ -n $f_share ]] && \
+ log_must is_shared $pool/$fs
+ done
+
+ for fs in $nomount_fs ; do
+ log_mustnot ismounted $pool/$fs
+ log_mustnot is_shared $pool/$fs
+ done
+
+ if [[ -n $f_share ]] ; then
+ log_must $ZFS set sharenfs=off $pool
+ log_mustnot is_shared $pool
+ fi
+
+ ((j = j + 1))
+ done
+
+ ((i = i + 1))
+ done
+done
+
+log_pass "All mount & share status of sub-filesystems within a pool \
+ can be restored after import [-Df]."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_013_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_013_neg.ksh
new file mode 100644
index 000000000000..19047e369c12
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_013_neg.ksh
@@ -0,0 +1,82 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_import_013_neg
+#
+# DESCRIPTION:
+# For pool may be in use from other system,
+# 'zpool import' will prompt the warning and fails.
+#
+# STRATEGY:
+# 1. Prepare rawfile that are created from other system.
+# 2. Verify 'zpool import' will fail.
+# 3. Verify 'zpool import -f' succeed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+POOL_NAME=unclean_export
+POOL_FILE=unclean_export.dat
+
+function uncompress_pool
+{
+ log_note "Creating pool from $POOL_FILE"
+ log_must $BZCAT \
+ $STF_SUITE/tests/cli_root/zpool_import/blockfiles/$POOL_FILE.bz2 \
+ > $TMPDIR/$POOL_FILE
+ return 0
+}
+
+function cleanup
+{
+ poolexists $POOL_NAME && log_must zpool destroy $POOL_NAME
+ [[ -e $TMPDIR/$POOL_FILE ]] && rm $TMPDIR/$POOL_FILE
+ return 0
+}
+
+log_assert "'zpool import' fails for pool that was not cleanly exported"
+log_onexit cleanup
+
+uncompress_pool
+log_mustnot zpool import -d $TMPDIR $POOL_NAME
+log_must zpool import -d $TMPDIR -f $POOL_NAME
+
+log_pass "'zpool import' fails for pool that was not cleanly exported"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_014_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_014_pos.ksh
new file mode 100644
index 000000000000..0d1388c6c906
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_014_pos.ksh
@@ -0,0 +1,64 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2013 Spectra Logic All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_import_014_pos
+#
+# DESCRIPTION:
+# "'zpool import' can import destroyed disk-backed pools"
+#
+# STRATEGY:
+# 1. Create test pool A.
+# 2. Destroy pool A.
+# 3. Verify 'import -D' can import pool A.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2013-03-13)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Destroyed pools are not listed unless with -D option is specified."
+
+log_must $ZPOOL create $TESTPOOL ${DISKS[0]}
+log_must $ZPOOL destroy $TESTPOOL
+log_mustnot $ZPOOL import $TESTPOOL
+log_must $ZPOOL import -D $TESTPOOL
+log_must poolexists $TESTPOOL
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_all_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_all_001_pos.ksh
new file mode 100644
index 000000000000..a87ddf420e5b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_all_001_pos.ksh
@@ -0,0 +1,233 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_mount/zfs_mount.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_import_all_001_pos
+#
+# DESCRIPTION:
+# Verify that 'zpool import -a' succeeds as root.
+#
+# STRATEGY:
+# 1. Create a group of pools with specified vdev.
+# 2. Create zfs filesystems within the given pools.
+# 3. Export the pools.
+# 4. Verify that import command succeed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+set -A options "" "-R $ALTER_ROOT"
+
+typeset -i number=0
+typeset -i id=0
+typeset -i i=0
+typeset checksum1
+typeset unwantedpool
+
+function setup_single_disk #disk #pool #fs #mtpt
+{
+ typeset disk=$1
+ typeset pool=$2
+ typeset fs=${3##/}
+ typeset mtpt=$4
+
+ setup_filesystem "$disk" "$pool" "$fs" "$mtpt"
+
+ log_must $CP $MYTESTFILE $mtpt/$TESTFILE0
+
+ log_must $ZPOOL export $pool
+
+ [[ -d $mtpt ]] && \
+ $RM -rf $mtpt
+}
+
+function cleanup_all
+{
+ typeset -i id=0
+
+ #
+ # Try import individually if 'import -a' failed.
+ #
+ for pool in `$ZPOOL import | $GREP "pool:" | $AWK '{print $2}'`; do
+ $ZPOOL import -f $pool
+ done
+
+ for pool in `$ZPOOL import -d $DEVICE_DIR | $GREP "pool:" | $AWK '{print $2}'`; do
+ log_must $ZPOOL import -d $DEVICE_DIR -f $pool
+ done
+
+ while (( id < number )); do
+ if ! poolexists ${TESTPOOL}-$id ; then
+ (( id = id + 1 ))
+ continue
+ fi
+
+ if (( id == 0 )); then
+ log_must $ZPOOL export ${TESTPOOL}-$id
+
+ [[ -d /${TESTPOOL}-$id ]] && \
+ log_must $RM -rf /${TESTPOOL}-$id
+
+ log_must $ZPOOL import -f ${TESTPOOL}-$id $TESTPOOL
+
+ [[ -e $TESTDIR/$TESTFILE0 ]] && \
+ log_must $RM -rf $TESTDIR/$TESTFILE0
+ else
+ cleanup_filesystem "${TESTPOOL}-$id" $TESTFS
+
+ destroy_pool ${TESTPOOL}-$id
+ fi
+
+ (( id = id + 1 ))
+ done
+
+ [[ -d $ALTER_ROOT ]] && \
+ $RM -rf $ALTER_ROOT
+}
+
+function checksum_all #alter_root
+{
+ typeset alter_root=$1
+ typeset -i id=0
+ typeset file
+ typeset checksum2
+
+ while (( id < number )); do
+ if (( id == 2 )); then
+ (( id = id + 1 ))
+ continue
+ fi
+
+ if (( id == 0 )); then
+ file=${alter_root}/$TESTDIR/$TESTFILE0
+ else
+ file=${alter_root}/$TESTDIR.$id/$TESTFILE0
+ fi
+ [[ ! -e $file ]] && \
+ log_fail "$file missing after import."
+
+ checksum2=$($SUM $file | $AWK '{print $1}')
+ [[ "$checksum1" != "$checksum2" ]] && \
+ log_fail "Checksums differ ($checksum1 != $checksum2)"
+
+ (( id = id + 1 ))
+ done
+
+ return 0
+}
+
+
+log_assert "Verify that 'zpool import -a' succeeds as root."
+
+log_onexit cleanup_all
+
+checksum1=$($SUM $MYTESTFILE | $AWK '{print $1}')
+
+log_must $ZPOOL export $TESTPOOL
+log_must $ZPOOL import $TESTPOOL ${TESTPOOL}-0
+log_must $CP $MYTESTFILE $TESTDIR/$TESTFILE0
+log_must $ZPOOL export ${TESTPOOL}-0
+[[ -d /${TESTPOOL}-0 ]] && \
+ log_must $RM -rf /${TESTPOOL}-0
+
+#
+# setup exported pools on normal devices
+#
+number=1
+while (( number <= $GROUP_NUM )); do
+ if [[ `$UNAME -s` != "FreeBSD" ]]; then
+ if (( number == 2)); then
+ (( number = number + 1 ))
+ continue
+ fi
+ fi
+ set_partition $number "" $PART_SIZE ${DISK1}
+
+ setup_single_disk "${DISK1}p${number}" \
+ "${TESTPOOL}-$number" \
+ "$TESTFS" \
+ "$TESTDIR.$number"
+
+ (( number = number + 1 ))
+done
+
+#
+# setup exported pools on raw files
+#
+for disk in $DEVICE_FILES
+do
+
+ setup_single_disk "$disk" \
+ "${TESTPOOL}-$number" \
+ "$TESTFS" \
+ "$TESTDIR.$number"
+
+ (( number = number + 1 ))
+done
+
+while (( i < ${#options[*]} )); do
+
+ log_must $ZPOOL import -d /dev -d $DEVICE_DIR ${options[i]} -a -f
+
+ # destroy unintentional imported pools
+ typeset exclude=`eval $ECHO \"'(${KEEP})'\"`
+ for unwantedpool in $($ZPOOL list -H -o name \
+ | $EGREP -v "$exclude" | $GREP -v $TESTPOOL); do
+ log_must $ZPOOL export $unwantedpool
+ done
+
+ if [[ -n ${options[i]} ]]; then
+ checksum_all $ALTER_ROOT
+ else
+ checksum_all
+ fi
+
+ id=0
+ while (( id < number )); do
+ if poolexists ${TESTPOOL}-$id ; then
+ log_must $ZPOOL export ${TESTPOOL}-$id
+ fi
+ (( id = id + 1 ))
+ done
+
+ (( i = i + 1 ))
+done
+
+log_pass "'zpool import -a' succeeds as root."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_corrupt_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_corrupt_001_pos.ksh
new file mode 100644
index 000000000000..acdf523f8cfd
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_corrupt_001_pos.ksh
@@ -0,0 +1,128 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2013 Spectra Logic All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_import_014_pos
+#
+# DESCRIPTION:
+# Verify that a disk-backed exported pool with some of its vdev labels
+# corrupted can still be imported
+# STRATEGY:
+# 1. Create a disk-backed pool
+# 2. Export it
+# 3. Overwrite one or more of its vdev labels
+# 4. Use zdb to verify that the labels are damaged
+# 5. Verify 'zpool import' can import it
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2013-03-15)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+# ZFS has four vdev labels per vdev
+typeset -i N_VDEV_LABELS=4
+# Size of a single label, in bytes
+typeset -i VDEV_LABEL_SIZE=$(( 256 * 1024))
+
+
+#
+# The authoritative version of this calculation can be found in the function of
+# the same name in vdev_label.c. The rounding of psize is based on the
+# calculation in vdev_disk_read_rootlabel in vdev_disk.c
+#
+# arg1: vdev size in bytes
+# arg2: label index, 0 through 3
+#
+function vdev_label_offset
+{
+ typeset -il psize=$1
+ typeset -i l=$2
+ typeset -il offset
+ typeset -il roundsize
+
+ roundsize=$(( $psize & -$VDEV_LABEL_SIZE ))
+ if [[ $l -lt $(( N_VDEV_LABELS / 2 )) ]]; then
+ offset=$(( l * $VDEV_LABEL_SIZE))
+ else
+ offset=$(( l * $VDEV_LABEL_SIZE + $roundsize - $N_VDEV_LABELS * $VDEV_LABEL_SIZE ))
+ fi
+ echo $offset
+}
+
+log_assert "Verify that a disk-backed exported pool with some of its vdev labels corrupted can still be imported"
+
+typeset -i i
+typeset -i j
+set -A DISKS_ARRAY $DISKS
+typeset DISK=${DISKS_ARRAY[0]}
+typeset PROV=${DISK#/dev/}
+typeset -il psize=$(geom disk list $PROV | awk '/Mediasize/ {print $2}')
+if [[ -z $psize ]]; then
+ log_fail "Could not determine the capacity of $DISK"
+fi
+
+for ((i=0; $i<$N_VDEV_LABELS; i=$i+1 )); do
+ log_must $ZPOOL create -f $TESTPOOL $DISK
+ log_must $ZPOOL export $TESTPOOL
+
+ # Corrupt all labels except the ith
+ for ((j=0; $j<$N_VDEV_LABELS; j=$j+1 )); do
+ typeset -il offset
+
+ [[ $i -eq $j ]] && continue
+
+ log_note offset=vdev_label_offset $psize $j
+ offset=$(vdev_label_offset $psize $j)
+ log_must $DD if=/dev/zero of=$DISK bs=1024 \
+ count=$(( $VDEV_LABEL_SIZE / 1024 )) \
+ oseek=$(( $offset / 1024 )) \
+ conv=notrunc
+ done
+
+ typeset -i num_labels=$( $ZDB -l $DISK | $GREP pool_guid | wc -l )
+ if [[ $num_labels -ne 1 ]]; then
+ $ZDB -l $DISK
+ log_fail "Expected 1 vdev label but found $num_labels"
+ fi
+
+ log_must $ZPOOL import $TESTPOOL
+ destroy_pool $TESTPOOL
+done
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_destroyed_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_destroyed_001_neg.ksh
new file mode 100644
index 000000000000..864d23a7f4bf
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_destroyed_001_neg.ksh
@@ -0,0 +1,65 @@
+#!/usr/local/bin/ksh93
+#
+# Copyright (c) 2017 Spectra Logic Corporation
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions, and the following disclaimer,
+# without modification.
+# 2. Redistributions in binary form must reproduce at minimum a disclaimer
+# substantially similar to the "NO WARRANTY" disclaimer below
+# ("Disclaimer") and any redistribution must be conditioned upon
+# including a substantially similar Disclaimer requirement for further
+# binary redistribution.
+#
+# NO WARRANTY
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGES.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+set_disks
+
+# A destroyed pool cannot be imported, even if an out-of-date non-destroyed
+# label is present
+#
+# This situation arose when a user activated a spare, removed the spare disk,
+# destroyed the pool, reinserted the spare disk, and then tried to import the
+# pool. Since the pool was destroyed, nothing should've happened. But the
+# spare disk had a non-destroyed label, so zpool tried to import it. A panic
+# ensued.
+#
+# More generally, this situation can happen any time the following things happen:
+# 1) A disk gets removed with its label intact
+# 2) The pool's configuration changes
+# 3) The pool gets destroyed
+# 4) Somebody tries to import the pool
+
+log_must $ZPOOL create -f $TESTPOOL mirror ${DISK0} ${DISK1}
+
+# Offline a disk so it's label won't get updated by the upcoming destroy
+log_must $ZPOOL offline $TESTPOOL ${DISK0}
+
+# Now change the pool's configuration, so DISK0's label will be out-of-date
+log_must $ZPOOL attach $TESTPOOL ${DISK1} ${DISK2}
+
+# Destroy the pool, so DISK1's and DISK2's labels will be in the destroyed
+# state, leaving DISK0's label as the most recent non-destroyed label
+log_must $ZPOOL destroy $TESTPOOL
+
+# Now try to import the pool. It should fail.
+log_mustnot $ZPOOL import $TESTPOOL
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_destroyed_002_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_destroyed_002_neg.ksh
new file mode 100644
index 000000000000..2a1192770890
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_destroyed_002_neg.ksh
@@ -0,0 +1,58 @@
+#!/usr/local/bin/ksh93
+#
+# Copyright (c) 2017 Spectra Logic Corporation
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions, and the following disclaimer,
+# without modification.
+# 2. Redistributions in binary form must reproduce at minimum a disclaimer
+# substantially similar to the "NO WARRANTY" disclaimer below
+# ("Disclaimer") and any redistribution must be conditioned upon
+# including a substantially similar Disclaimer requirement for further
+# binary redistribution.
+#
+# NO WARRANTY
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGES.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+set_disks
+
+# "zpool import" will not show destroyed pools, even if out-of-date
+# non-destroyed label is present
+#
+# This situation can happen any time the following things happen:
+# 1) A disk gets removed with its label intact
+# 2) The pool gets destroyed
+# 3) Somebody run "zpool import" to see importable pools
+
+log_must $ZPOOL create -f $TESTPOOL mirror ${DISK0} ${DISK1}
+
+# Offline a disk so it's label won't get updated by the upcoming destroy
+log_must $ZPOOL offline $TESTPOOL ${DISK0}
+
+# Destroy the pool, so DISK1's and DISK2's labels will be in the destroyed
+# state, leaving DISK0's label as the most recent non-destroyed label
+log_must $ZPOOL destroy $TESTPOOL
+
+# Now try to import the pool. It should fail.
+if $ZPOOL import | $GREP -q $TESTPOOL; then
+ $ZPOOL import
+ log_fail "ERROR: Destroyed pool visible"
+else
+ log_pass
+fi
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_missing_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_missing_001_pos.ksh
new file mode 100644
index 000000000000..34fea0802846
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_missing_001_pos.ksh
@@ -0,0 +1,166 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_mount/zfs_mount.kshlib
+. $STF_SUITE/tests/cli_root/zpool_import/zpool_import.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_import_missing_001_pos
+#
+# DESCRIPTION:
+# Once a pool has been exported, and one or more devices are
+# damaged or missing (d/m), import should handle this kind of situation
+# as described:
+# - Regular, report error while any number of devices failing.
+# - Mirror could withstand (N-1) devices failing
+# before data integrity is compromised
+# - Raidz could withstand one devices failing
+# before data integrity is compromised
+# Verify those are true.
+#
+# STRATEGY:
+# 1. Create test pool upon device files using the various combinations.
+# - Regular pool
+# - Mirror
+# - Raidz
+# 2. Create necessary filesystem and test files.
+# 3. Export the test pool.
+# 4. Remove one or more devices
+# 5. Verify 'zpool import' will handle d/m device successfully.
+# Using the various combinations.
+# - Regular import
+# - Alternate Root Specified
+# It should be succeed with single d/m device upon 'raidz' & 'mirror',
+# but failed against 'regular' or more d/m devices.
+# 6. If import succeed, verify following is true:
+# - The pool shows up under 'zpool list'.
+# - The pool's health should be DEGRADED.
+# - It contains the correct test file
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-08-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+set -A vdevs "" "mirror" "raidz"
+set -A options "" "-R $ALTER_ROOT"
+
+function perform_inner_test
+{
+ typeset action=$1
+ typeset import_opts=$2
+ typeset target=$3
+ typeset basedir
+
+ $action $ZPOOL import -d $DEVICE_DIR ${import_opts} $target
+ [[ $action == "log_mustnot" ]] && return
+
+ log_must poolexists $TESTPOOL1
+
+ health=$($ZPOOL list -H -o health $TESTPOOL1)
+ [[ "$health" == "DEGRADED" ]] || \
+ log_fail "ERROR: $TESTPOOL1: Incorrect health '$health'"
+ log_must ismounted $TESTPOOL1/$TESTFS
+
+ basedir=$TESTDIR1
+ [[ -n "${import_opts}" ]] && basedir=$ALTER_ROOT/$TESTDIR1
+ [[ ! -e "$basedir/$TESTFILE0" ]] && \
+ log_fail "ERROR: $basedir/$TESTFILE0 missing after import."
+
+ checksum2=$($SUM $basedir/$TESTFILE0 | $AWK '{print $1}')
+ [[ "$checksum1" != "$checksum2" ]] && \
+ log_fail "ERROR: Checksums differ ($checksum1 != $checksum2)"
+
+ log_must $ZPOOL export $TESTPOOL1
+}
+
+log_onexit cleanup_missing
+
+log_assert "Verify that import could handle damaged or missing device."
+
+CWD=$PWD
+cd $DEVICE_DIR || log_fail "ERROR: Unable change directory to $DEVICE_DIR"
+
+checksum1=$($SUM $MYTESTFILE | $AWK '{print $1}')
+
+typeset -i i=0
+while :; do
+ typeset vdtype="${vdevs[i]}"
+
+ typeset -i j=0
+ while (( j < ${#options[*]} )); do
+ typeset opts="${options[j]}"
+ [ -n "$vdtype" ] && typestr="$vdtype" || typestr="stripe"
+
+ # Prepare the pool.
+ setup_missing_test_pool $vdtype
+ guid=$(get_config $TESTPOOL1 pool_guid $DEVICE_DIR)
+ log_note "*** Testing $typestr tvd guid $guid opts '${opts}'"
+
+ typeset -i count=0
+ for device in $DEVICE_FILES ; do
+ log_mustnot poolexists $TESTPOOL1
+ log_must $RM -f $device
+
+ (( count = count + 1 ))
+
+ action=log_must
+ case "$vdtype" in
+ 'mirror') (( count == $GROUP_NUM )) && \
+ action=log_mustnot
+ ;;
+ 'raidz') (( count > 1 )) && \
+ action=log_mustnot
+ ;;
+ '') action=log_mustnot
+ ;;
+ esac
+
+ log_note "Testing import by name; ${count} removed."
+ perform_inner_test $action "${opts}" $TESTPOOL1
+
+ log_note "Testing import by GUID; ${count} removed."
+ perform_inner_test $action "${opts}" $guid
+ done
+
+ recreate_missing_files
+ (( j = j + 1 ))
+ done
+ (( i = i + 1 ))
+ (( i == ${#vdevs[*]} )) && break
+done
+
+log_pass "Import could handle damaged or missing device."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_missing_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_missing_002_pos.ksh
new file mode 100644
index 000000000000..2d6a324bcd8c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_missing_002_pos.ksh
@@ -0,0 +1,145 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_mount/zfs_mount.kshlib
+. $STF_SUITE/tests/cli_root/zpool_import/zpool_import.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_import_missing_002_pos
+#
+# DESCRIPTION:
+# Once a pool has been exported, and one or more devices are
+# move to other place, import should handle this kind of situation
+# as described:
+# - Regular, report error while any number of devices failing.
+# - Mirror could withstand (N-1) devices failing
+# before data integrity is compromised
+# - Raidz could withstand one devices failing
+# before data integrity is compromised
+# Verify that is true.
+#
+# STRATEGY:
+# 1. Create test pool upon device files using the various combinations.
+# - Regular pool
+# - Mirror
+# - Raidz
+# 2. Create necessary filesystem and test files.
+# 3. Export the test pool.
+# 4. Move one or more device files to other directory
+# 5. Verify 'zpool import -d' with the new directory
+# will handle moved files successfullly.
+# Using the various combinations.
+# - Regular import
+# - Alternate Root Specified
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-08-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+set -A vdevs "" "mirror" "raidz"
+set -A options "" "-R $ALTER_ROOT"
+
+log_onexit cleanup_missing
+
+log_assert "Verify that import could handle moving device."
+
+log_must $MKDIR -p $BACKUP_DEVICE_DIR
+cd $DEVICE_DIR || log_fail "Unable change directory to $DEVICE_DIR"
+
+typeset -i i=0
+typeset -i count=0
+typeset action
+
+function try_import # <action> <poolish> [opts]
+{
+ typeset action=$1; shift
+ typeset poolish="$1"; shift
+ log_note "try_import action=$action poolish=$poolish opts='$1'"
+ if [ -z "$1" ]; then
+ $action $ZPOOL import -d $DEVICE_DIR $poolish
+ else
+ $action $ZPOOL import -d $DEVICE_DIR $1 $poolish
+ fi
+ [ "$action" = "log_mustnot" ] && return
+ log_must $ZPOOL export $TESTPOOL1
+}
+
+while :; do
+ typeset vdtype="${vdevs[i]}"
+
+ typeset -i j=0
+ while (( j < ${#options[*]} )); do
+ typeset opts="${options[j]}"
+
+ [ -n "$vdtype" ] && typestr="$vdtype" || typestr="stripe"
+ setup_missing_test_pool $vdtype
+ guid=$(get_config $TESTPOOL1 pool_guid $DEVICE_DIR)
+ log_note "*** Testing $typestr tvd guid $guid opts '${opts}'"
+
+ typeset -i count=0
+ for device in $DEVICE_FILES ; do
+ log_mustnot poolexists $TESTPOOL1
+ log_must $MV $device $BACKUP_DEVICE_DIR
+
+ (( count = count + 1 ))
+
+ action=log_mustnot
+ case "${vdevs[i]}" in
+ 'mirror') (( count < $GROUP_NUM )) && \
+ action=log_must
+ ;;
+ 'raidz') (( count == 1 )) && \
+ action=log_must
+ ;;
+ esac
+
+ log_note "Testing import by name; ${count} moved."
+ try_import $action $TESTPOOL1 "$opts"
+
+ log_note "Testing import by GUID; ${count} moved."
+ try_import $action $guid "$opts"
+ done
+
+ log_must $RM -f $BACKUP_DEVICE_DIR/*
+ recreate_missing_files
+ ((j = j + 1))
+ done
+ ((i = i + 1))
+ (( i == ${#vdevs[*]} )) && break
+done
+
+log_pass "Import could handle moving device."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_missing_003_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_missing_003_pos.ksh
new file mode 100644
index 000000000000..7911f84ac1b2
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_missing_003_pos.ksh
@@ -0,0 +1,234 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_mount/zfs_mount.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_import_missing_003_pos
+#
+# DESCRIPTION:
+# Once a pool has been exported, but one or more devices are
+# overlapped with other exported pool, import should handle
+# this kind of situation properly.
+#
+# STRATEGY:
+# 1. Repeat 1-3, create two test pools upon device files separately.
+# These two pools should have one or more devices are overlapped.
+# using the various combinations.
+# - Regular pool
+# - Mirror
+# - Raidz
+# 2. Create necessary filesystem and test files.
+# 3. Export the test pool.
+# 4. Verify 'zpool import -d' with these two pools will have results
+# as described:
+# - Regular, report error while any number of devices failing.
+# - Mirror could withstand (N-1) devices failing
+# before data integrity is compromised
+# - Raidz could withstand one devices failing
+# before data integrity is compromised
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-08-10)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+set -A vdevs "" "mirror" "raidz"
+
+function verify
+{
+ typeset pool=$1
+ typeset fs=$2
+ typeset mtpt=$3
+ typeset health=$4
+ typeset file=$5
+ typeset checksum1=$6
+
+ typeset myhealth
+ typeset mymtpt
+ typeset checksum2
+
+ log_must poolexists $pool
+
+ myhealth=$($ZPOOL list -H -o health $pool)
+
+ [[ $myhealth == $health ]] || \
+ log_fail "$pool: Incorrect health ($myhealth), " \
+ "expected ($health)."
+
+ log_must ismounted $pool/$fs
+
+ mymtpt=$(get_prop mountpoint $pool/$fs)
+ [[ $mymtpt == $mtpt ]] || \
+ log_fail "$pool/$fs: Incorrect mountpoint ($mymtpt), " \
+ "expected ($mtpt)."
+
+ [[ ! -e $mtpt/$file ]] && \
+ log_fail "$mtpt/$file missing after import."
+
+ checksum2=$($SUM $mymtpt/$file | $AWK '{print $1}')
+ [[ "$checksum1" != "$checksum2" ]] && \
+ log_fail "Checksums differ ($checksum1 != $checksum2)"
+
+ return 0
+
+}
+
+function cleanup
+{
+ cd $DEVICE_DIR || log_fail "Unable change directory to $DEVICE_DIR"
+
+ for pool in $TESTPOOL1 $TESTPOOL2; do
+ if poolexists "$pool" ; then
+ cleanup_filesystem $pool $TESTFS
+ destroy_pool $pool
+ fi
+ done
+
+ [[ -e $DEVICE_ARCHIVE ]] && log_must $TAR xf $DEVICE_ARCHIVE
+}
+
+function cleanup_all
+{
+ cleanup
+
+ # recover dev files
+ typeset i=0
+ while (( i < $MAX_NUM )); do
+ log_must create_vdevs ${DEVICE_DIR}/${DEVICE_FILE}$i
+ ((i += 1))
+ done
+
+ log_must $RM -f $DEVICE_ARCHIVE
+ cd $CWD || log_fail "Unable change directory to $CWD"
+
+}
+
+log_onexit cleanup_all
+
+log_assert "Verify that import could handle device overlapped."
+
+CWD=$PWD
+
+cd $DEVICE_DIR || log_fail "Unable change directory to $DEVICE_DIR"
+log_must $TAR cf $DEVICE_ARCHIVE ${DEVICE_FILE}*
+
+checksum1=$($SUM $MYTESTFILE | $AWK '{print $1}')
+
+typeset -i i=0
+typeset -i j=0
+typeset -i count=0
+typeset -i num=0
+typeset vdev1=""
+typeset vdev2=""
+typeset action
+
+while (( num < $GROUP_NUM )); do
+ vdev1="$vdev1 ${DEVICE_DIR}/${DEVICE_FILE}$num"
+ (( num = num + 1 ))
+done
+
+while (( i < ${#vdevs[*]} )); do
+ j=0
+ while (( j < ${#vdevs[*]} )); do
+
+ (( j != 0 )) && \
+ log_must $TAR xf $DEVICE_ARCHIVE
+
+ typeset -i overlap=1
+ typeset -i begin
+ typeset -i end
+
+ while (( overlap <= $GROUP_NUM )); do
+ vdev2=""
+ (( begin = $GROUP_NUM - overlap ))
+ (( end = 2 * $GROUP_NUM - overlap - 1 ))
+ (( num = begin ))
+ while (( num <= end )); do
+ vdev2="$vdev2 ${DEVICE_DIR}/${DEVICE_FILE}$num"
+ (( num = num + 1 ))
+ done
+
+ setup_filesystem "$vdev1" $TESTPOOL1 $TESTFS $TESTDIR1 \
+ "" ${vdevs[i]}
+ log_must $CP $MYTESTFILE $TESTDIR1/$TESTFILE0
+ log_must $ZFS umount $TESTDIR1
+ poolexists $TESTPOOL1 && \
+ log_must $ZPOOL export $TESTPOOL1
+
+ setup_filesystem "$vdev2" $TESTPOOL2 $TESTFS $TESTDIR2 \
+ "" ${vdevs[j]}
+ log_must $CP $MYTESTFILE $TESTDIR2/$TESTFILE0
+ log_must $ZFS umount $TESTDIR2
+ poolexists $TESTPOOL2 && \
+ log_must $ZPOOL export $TESTPOOL2
+
+ action=log_must
+ case "${vdevs[i]}" in
+ 'mirror') (( overlap == $GROUP_NUM )) && \
+ action=log_mustnot
+ ;;
+ 'raidz') (( overlap > 1 )) && \
+ action=log_mustnot
+ ;;
+ '') action=log_mustnot
+ ;;
+ esac
+
+ $action $ZPOOL import -d $DEVICE_DIR $TESTPOOL1
+ log_must $ZPOOL import -d $DEVICE_DIR $TESTPOOL2
+
+ if [[ $action == log_must ]]; then
+ verify "$TESTPOOL1" "$TESTFS" "$TESTDIR1" \
+ "DEGRADED" "$TESTFILE0" "$checksum1"
+ fi
+
+ verify "$TESTPOOL2" "$TESTFS" "$TESTDIR2" \
+ "ONLINE" "$TESTFILE0" "$checksum1"
+
+ cleanup
+
+ (( overlap = overlap + 1 ))
+
+ done
+
+ ((j = j + 1))
+ done
+
+ ((i = i + 1))
+done
+
+log_pass "Import could handle device overlapped."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_missing_004_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_missing_004_pos.ksh
new file mode 100644
index 000000000000..e9c6bc1555d1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_missing_004_pos.ksh
@@ -0,0 +1,99 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2013 Spectra Logic Corp. All rights reserved.
+# Use is subject to license terms.
+#
+#
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_import_missing_004_pos
+#
+# DESCRIPTION:
+# Once a pool has been exported and one or more devices are missing
+# "zpool import" with no pool argument should exit with error code 0.
+#
+# STRATEGY:
+# 1. Create test pool upon device files using the various combinations.
+# - Striped pool
+# - Mirror
+# - Raidz
+# 2. Export the test pool.
+# 3. Remove one or more devices
+# 4. Verify 'zpool import' will handle missing devices successfully.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2013-07-02)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+set -A vdevs "mirror" "raidz" ""
+
+function cleanup
+{
+ destroy_pool $TESTPOOL1
+ log_must $RM -rf $DEVICE_DIR/*
+}
+
+function recreate_files
+{
+ cleanup
+ typeset -i i=0
+ for (( ; $i < $GROUP_NUM; i += 1 )); do
+ log_must create_vdevs ${DEVICE_DIR}/${DEVICE_FILE}$i
+ done
+ log_must $SYNC
+}
+
+log_onexit cleanup
+
+log_assert "Verify that zpool import succeeds when devices are missing"
+
+typeset rootvdev
+typeset option
+log_must $MKDIR -p $DEVICE_DIR
+for rootvdev in "${vdevs[@]}"; do
+ recreate_files
+ poolexists $TESTPOOL1 || \
+ create_pool $TESTPOOL1 "${rootvdev}" $DEVICE_FILES
+
+ # Remove all devices but the last, one at a time
+ for device in ${DEVICE_FILES% *} ; do
+ poolexists $TESTPOOL1 && log_must $ZPOOL export $TESTPOOL1
+ log_must $RM -f $device
+ log_must $ZPOOL import -d $DEVICE_DIR
+ done
+done
+
+log_pass "zpool import succeeded when devices were missing"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_missing_005_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_missing_005_pos.ksh
new file mode 100644
index 000000000000..8b7dee79c4fe
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_missing_005_pos.ksh
@@ -0,0 +1,122 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2016 Spectra Logic All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_import_missing_005_pos
+#
+# DESCRIPTION:
+# Verify that a pool can still be imported even if its devices' names
+# have changed, for all types of devices. This is a test of vdev_geom's
+# import_by_guid functionality.
+# STRATEGY:
+# 1. Create a supply of file-backed md devices
+# 2. Create a disk-backed pool with regular, cache, log, and spare vdevs
+# 3. Export it
+# 4. Cause all the md devices names to change
+# 5. Verify 'zpool import' can import it
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2015-01-4)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Verify that all types of vdevs of a disk-backed exported pool can be imported even if they have been renamed"
+
+# Create md devices so we can control their devnames
+# Use high devnames so we'll be unlikely to have collisions
+typeset -i REGULAR_U=4000
+typeset -i LOG_U=4001
+typeset -i CACHE_U=4002
+typeset -i SPARE_U=4003
+typeset -i REGULAR_ALTU=5000
+typeset -i LOG_ALTU=5001
+typeset -i CACHE_ALTU=5002
+typeset -i SPARE_ALTU=5003
+typeset REGULAR=${TMPDIR}/regular
+typeset LOG=${TMPDIR}/log
+typeset CACHE=${TMPDIR}/cache
+typeset SPARE=${TMPDIR}/spare
+
+function cleanup
+{
+ destroy_pool $TESTPOOL
+ $MDCONFIG -d -u $REGULAR_U 2>/dev/null
+ $MDCONFIG -d -u $LOG_U 2>/dev/null
+ $MDCONFIG -d -u $CACHE_U 2>/dev/null
+ $MDCONFIG -d -u $SPARE_U 2>/dev/null
+ $MDCONFIG -d -u $REGULAR_ALTU 2>/dev/null
+ $MDCONFIG -d -u $LOG_ALTU 2>/dev/null
+ $MDCONFIG -d -u $CACHE_ALTU 2>/dev/null
+ $MDCONFIG -d -u $SPARE_ALTU 2>/dev/null
+ $RM -f $REGULAR
+ $RM -f $CACHE
+ $RM -f $LOG
+ $RM -f $SPARE
+}
+log_onexit cleanup
+
+log_must $TRUNCATE -s 64m $REGULAR
+log_must $TRUNCATE -s 64m $LOG
+log_must $TRUNCATE -s 64m $CACHE
+log_must $TRUNCATE -s 64m $SPARE
+log_must $MDCONFIG -t vnode -a -f $REGULAR -u $REGULAR_U
+log_must $MDCONFIG -t vnode -a -f $LOG -u $LOG_U
+log_must $MDCONFIG -t vnode -a -f $CACHE -u $CACHE_U
+log_must $MDCONFIG -t vnode -a -f $SPARE -u $SPARE_U
+
+log_must $ZPOOL create $TESTPOOL md$REGULAR_U log md$LOG_U cache md$CACHE_U spare md$SPARE_U
+log_must $ZPOOL export $TESTPOOL
+# Now destroy the md devices, then recreate them with different names
+log_must $MDCONFIG -d -u $REGULAR_U
+log_must $MDCONFIG -d -u $LOG_U
+log_must $MDCONFIG -d -u $CACHE_U
+log_must $MDCONFIG -d -u $SPARE_U
+log_must $MDCONFIG -t vnode -a -f $REGULAR -u $REGULAR_ALTU
+log_must $MDCONFIG -t vnode -a -f $LOG -u $LOG_ALTU
+log_must $MDCONFIG -t vnode -a -f $CACHE -u $CACHE_ALTU
+log_must $MDCONFIG -t vnode -a -f $SPARE -u $SPARE_ALTU
+
+log_must $ZPOOL import $TESTPOOL
+zpool status $TESTPOOL
+log_must check_state $TESTPOOL md${REGULAR_ALTU} ONLINE
+log_must check_state $TESTPOOL md${LOG_ALTU} ONLINE
+log_must check_state $TESTPOOL md${CACHE_ALTU} ONLINE
+log_must check_state $TESTPOOL md${SPARE_ALTU} AVAIL
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_rename_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_rename_001_pos.ksh
new file mode 100644
index 000000000000..383a00ec72b6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_rename_001_pos.ksh
@@ -0,0 +1,176 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_mount/zfs_mount.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_import_rename_001_pos
+#
+# DESCRIPTION:
+# An exported pool can be imported under a different name. Hence
+# we test that a previously exported pool can be renamed.
+#
+# STRATEGY:
+# 1. Copy a file into the default test directory.
+# 2. Umount the default directory.
+# 3. Export the pool.
+# 4. Import the pool using the name ${TESTPOOL}-new,
+# and using the various combinations.
+# - Regular import
+# - Alternate Root Specified
+# 5. Verify it exists in the 'zpool list' output.
+# 6. Verify the default file system is mounted and that the file
+# from step (1) is present.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+set -A pools "$TESTPOOL" "$TESTPOOL1"
+set -A devs "" "-d $DEVICE_DIR"
+set -A options "" "-R $ALTER_ROOT"
+set -A mtpts "$TESTDIR" "$TESTDIR1"
+
+
+function cleanup
+{
+ typeset -i i=0
+ while (( i < ${#pools[*]} )); do
+ if poolexists "${pools[i]}-new" ; then
+ log_must $ZPOOL export "${pools[i]}-new"
+
+ [[ -d /${pools[i]}-new ]] && \
+ log_must $RM -rf /${pools[i]}-new
+
+ log_must $ZPOOL import ${devs[i]} \
+ "${pools[i]}-new" ${pools[i]}
+ fi
+
+ datasetexists "${pools[i]}" || \
+ log_must $ZPOOL import ${devs[i]} ${pools[i]}
+
+ ismounted "${pools[i]}/$TESTFS" || \
+ log_must $ZFS mount ${pools[i]}/$TESTFS
+
+ [[ -e ${mtpts[i]}/$TESTFILE0 ]] && \
+ log_must $RM -rf ${mtpts[i]}/$TESTFILE0
+
+ ((i = i + 1))
+
+ done
+
+ cleanup_filesystem $TESTPOOL1 $TESTFS $TESTDIR1
+
+ destroy_pool $TESTPOOL1
+
+ [[ -d $ALTER_ROOT ]] && \
+ log_must $RM -rf $ALTER_ROOT
+}
+
+function perform_inner_test
+{
+ target=$1
+
+ log_must $ZPOOL import ${devs[i]} ${options[j]} \
+ $target ${pools[i]}-new
+
+ log_must poolexists "${pools[i]}-new"
+
+ log_must ismounted ${pools[i]}-new/$TESTFS
+
+ basedir=${mtpts[i]}
+ [[ -n ${options[j]} ]] && \
+ basedir=$ALTER_ROOT/${mtpts[i]}
+
+ [[ ! -e $basedir/$TESTFILE0 ]] && \
+ log_fail "$basedir/$TESTFILE0 missing after import."
+
+ checksum2=$($SUM $basedir/$TESTFILE0 | $AWK '{print $1}')
+ [[ "$checksum1" != "$checksum2" ]] && \
+ log_fail "Checksums differ ($checksum1 != $checksum2)"
+
+ log_must $ZPOOL export "${pools[i]}-new"
+
+ [[ -d /${pools[i]}-new ]] && \
+ log_must $RM -rf /${pools[i]}-new
+
+ target=${pools[i]}-new
+ if (( RANDOM % 2 == 0 )) ; then
+ target=$guid
+ fi
+ log_must $ZPOOL import ${devs[i]} $target ${pools[i]}
+}
+
+log_onexit cleanup
+
+log_assert "Verify that an imported pool can be renamed."
+
+setup_filesystem "$DEVICE_FILES" $TESTPOOL1 $TESTFS $TESTDIR1
+checksum1=$($SUM $MYTESTFILE | $AWK '{print $1}')
+
+typeset -i i=0
+typeset -i j=0
+typeset basedir
+
+while (( i < ${#pools[*]} )); do
+ guid=$(get_config ${pools[i]} pool_guid)
+ log_must $CP $MYTESTFILE ${mtpts[i]}/$TESTFILE0
+
+ log_must $ZFS umount ${mtpts[i]}
+
+ j=0
+ while (( j < ${#options[*]} )); do
+ log_must $ZPOOL export ${pools[i]}
+
+ [[ -d /${pools[i]} ]] && \
+ log_must $RM -rf /${pools[i]}
+
+ log_note "Testing import by name."
+ perform_inner_test ${pools[i]}
+
+ log_must $ZPOOL export ${pools[i]}
+
+ log_note "Testing import by GUID."
+ perform_inner_test $guid
+
+ ((j = j + 1))
+ done
+
+ ((i = i + 1))
+done
+
+log_pass "Successfully imported and renamed a ZPOOL"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_test.sh
new file mode 100755
index 000000000000..8dd147bcb0c0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_import/zpool_import_test.sh
@@ -0,0 +1,587 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zpool_import_002_pos cleanup
+zpool_import_002_pos_head()
+{
+ atf_set "descr" "Verify that an exported pool can be imported and cannot be imported more than once."
+ atf_set "require.progs" "ksh93 zfs zpool sum zdb"
+ atf_set "timeout" 2400
+}
+zpool_import_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_import_002_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_import_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_import_003_pos cleanup
+zpool_import_003_pos_head()
+{
+ atf_set "descr" "Destroyed pools are not listed unless with -D option is specified."
+ atf_set "require.progs" "ksh93 zpool zfs"
+ atf_set "timeout" 2400
+}
+zpool_import_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_import_003_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_import_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_import_004_pos cleanup
+zpool_import_004_pos_head()
+{
+ atf_set "descr" "Destroyed pools devices was moved to another directory,it still can be imported correctly."
+ atf_set "require.progs" "ksh93 zpool zfs zdb"
+ atf_set "timeout" 2400
+}
+zpool_import_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_import_004_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_import_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_import_005_pos cleanup
+zpool_import_005_pos_head()
+{
+ atf_set "descr" "Destroyed pools devices was renamed, it still can be importedcorrectly."
+ atf_set "require.progs" "ksh93 zpool zfs zdb"
+ atf_set "timeout" 2400
+}
+zpool_import_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_import_005_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_import_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_import_006_pos cleanup
+zpool_import_006_pos_head()
+{
+ atf_set "descr" "For mirror, N-1 destroyed pools devices was removed or usedby other pool, it still can be imported correctly."
+ atf_set "require.progs" "ksh93 zpool zfs zdb"
+ atf_set "timeout" 2400
+}
+zpool_import_006_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_import_006_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_import_006_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_import_007_pos cleanup
+zpool_import_007_pos_head()
+{
+ atf_set "descr" "For raidz, one destroyed pools devices was removed or used byother pool, it still can be imported correctly."
+ atf_set "require.progs" "ksh93 zpool zfs zdb"
+ atf_set "timeout" 2400
+}
+zpool_import_007_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_import_007_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_import_007_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_import_008_pos cleanup
+zpool_import_008_pos_head()
+{
+ atf_set "descr" "For raidz2, two destroyed pools devices was removed or used byother pool, it still can be imported correctly."
+ atf_set "require.progs" "ksh93 zpool zfs zdb"
+ atf_set "timeout" 2400
+}
+zpool_import_008_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_import_008_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_import_008_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_import_009_neg cleanup
+zpool_import_009_neg_head()
+{
+ atf_set "descr" "Badly-formed 'zpool import' with inapplicable scenariosshould return an error."
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 2400
+}
+zpool_import_009_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_import_009_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_import_009_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_import_010_pos cleanup
+zpool_import_010_pos_head()
+{
+ atf_set "descr" "'zpool -D -a' can import all the specified directoriesdestroyed pools."
+ atf_set "require.progs" "ksh93 zpool zfs"
+ atf_set "timeout" 2400
+}
+zpool_import_010_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_import_010_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_import_010_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_import_011_neg cleanup
+zpool_import_011_neg_head()
+{
+ atf_set "descr" "For strip pool, any destroyed pool devices was demaged,zpool import -D will failed."
+ atf_set "require.progs" "ksh93 zpool zfs zdb"
+ atf_set "timeout" 2400
+}
+zpool_import_011_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_import_011_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_import_011_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_import_012_pos cleanup
+zpool_import_012_pos_head()
+{
+ atf_set "descr" "Verify all mount & share status of sub-filesystems within a poolcan be restored after import [-Df]."
+ atf_set "require.progs" "ksh93 zfs zpool zdb share"
+ atf_set "timeout" 2400
+}
+zpool_import_012_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_import_012_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_import_012_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_import_013_neg
+zpool_import_013_neg_head()
+{
+ atf_set "descr" "'zpool import' fails for pool that was not cleanly exported"
+ atf_set "require.progs" "ksh93 zfs zpool"
+}
+zpool_import_013_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ ksh93 $(atf_get_srcdir)/zpool_import_013_neg.ksh || atf_fail "Testcase failed"
+}
+
+
+atf_test_case zpool_import_014_pos cleanup
+zpool_import_014_pos_head()
+{
+ atf_set "descr" "'zpool import' can import destroyed disk-backed pools"
+ atf_set "require.progs" "ksh93 zfs zpool"
+}
+zpool_import_014_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/zpool_import_014_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_import_014_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_import_all_001_pos cleanup
+zpool_import_all_001_pos_head()
+{
+ atf_set "descr" "Verify that 'zpool import -a' succeeds as root."
+ atf_set "require.progs" "ksh93 zfs zpool sum"
+ atf_set "timeout" 2400
+}
+zpool_import_all_001_pos_body()
+{
+ atf_skip "This test relies heavily on Solaris slices. It could be ported, but that is difficult due to the high degree of obfuscation in the code"
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_import_all_001_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_import_all_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_import_missing_001_pos cleanup
+zpool_import_missing_001_pos_head()
+{
+ atf_set "descr" "Verify that import could handle damaged or missing device."
+ atf_set "require.progs" "ksh93 zfs sum zpool zdb"
+ atf_set "timeout" 2400
+}
+zpool_import_missing_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_import_missing_001_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_import_missing_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_import_missing_002_pos cleanup
+zpool_import_missing_002_pos_head()
+{
+ atf_set "descr" "Verify that import could handle moving device."
+ atf_set "require.progs" "ksh93 zpool zfs zdb"
+ atf_set "timeout" 2400
+}
+zpool_import_missing_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_import_missing_002_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_import_missing_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_import_missing_003_pos cleanup
+zpool_import_missing_003_pos_head()
+{
+ atf_set "descr" "Verify that import could handle device overlapped."
+ atf_set "require.progs" "ksh93 zpool sum zfs"
+ atf_set "timeout" 2400
+}
+zpool_import_missing_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_import_missing_003_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_import_missing_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case zpool_import_missing_004_pos
+zpool_import_missing_004_pos_head()
+{
+ atf_set "descr" "Verify that zpool import succeeds when devices are missing"
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 300
+}
+zpool_import_missing_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ ksh93 $(atf_get_srcdir)/zpool_import_missing_004_pos.ksh || atf_fail "Testcase failed"
+}
+
+atf_test_case zpool_import_missing_005_pos
+zpool_import_missing_005_pos_head()
+{
+ atf_set "descr" "Verify that zpool import succeeds when devices of all types have been renamed"
+ atf_set "require.progs" "ksh93 mdconfig zfs zpool"
+ atf_set "timeout" 300
+}
+zpool_import_missing_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ ksh93 $(atf_get_srcdir)/zpool_import_missing_005_pos.ksh || atf_fail "Testcase failed"
+}
+
+
+atf_test_case zpool_import_rename_001_pos cleanup
+zpool_import_rename_001_pos_head()
+{
+ atf_set "descr" "Verify that an imported pool can be renamed."
+ atf_set "require.progs" "ksh93 zfs zpool sum zdb"
+ atf_set "timeout" 2400
+}
+zpool_import_rename_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_import_rename_001_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_import_rename_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case zpool_import_corrupt_001_pos cleanup
+zpool_import_corrupt_001_pos_head()
+{
+ atf_set "descr" "Verify that a disk-backed exported pool with some of its vdev labels corrupted can still be imported"
+ atf_set "require.progs" "ksh93 zfs zpool zdb"
+ atf_set "timeout" 2400
+}
+zpool_import_corrupt_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/zpool_import_corrupt_001_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_import_corrupt_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_import.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case zpool_import_destroyed_001_neg cleanup
+zpool_import_destroyed_001_neg_head()
+{
+ atf_set "descr" "'zpool import' will not show destroyed pools, even if an out-of-date non-destroyed label remains"
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_import_destroyed_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+
+ verify_disk_count "$DISKS" 3
+ ksh93 $(atf_get_srcdir)/zpool_import_destroyed_001_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_import_destroyed_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+
+ destroy_pool "$TESTPOOL"
+ cleanup_devices "$DISKS"
+}
+
+atf_test_case zpool_import_destroyed_002_neg cleanup
+zpool_import_destroyed_002_neg_head()
+{
+ atf_set "descr" "'zpool import' will not show destroyed pools, even if an out-of-date non-destroyed label remains"
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_import_destroyed_002_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/zpool_import_destroyed_002_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_import_destroyed_002_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+
+ destroy_pool "$TESTPOOL"
+ cleanup_devices "$DISKS"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zpool_import_002_pos
+ atf_add_test_case zpool_import_003_pos
+ atf_add_test_case zpool_import_004_pos
+ atf_add_test_case zpool_import_005_pos
+ atf_add_test_case zpool_import_006_pos
+ atf_add_test_case zpool_import_007_pos
+ atf_add_test_case zpool_import_008_pos
+ atf_add_test_case zpool_import_009_neg
+ atf_add_test_case zpool_import_010_pos
+ atf_add_test_case zpool_import_011_neg
+ atf_add_test_case zpool_import_012_pos
+ atf_add_test_case zpool_import_013_neg
+ atf_add_test_case zpool_import_014_pos
+ atf_add_test_case zpool_import_all_001_pos
+ atf_add_test_case zpool_import_missing_001_pos
+ atf_add_test_case zpool_import_missing_002_pos
+ atf_add_test_case zpool_import_missing_003_pos
+ atf_add_test_case zpool_import_missing_004_pos
+ atf_add_test_case zpool_import_missing_005_pos
+ atf_add_test_case zpool_import_rename_001_pos
+ atf_add_test_case zpool_import_corrupt_001_pos
+ atf_add_test_case zpool_import_destroyed_001_neg
+ atf_add_test_case zpool_import_destroyed_002_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_offline/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zpool_offline/Makefile
new file mode 100644
index 000000000000..22728ca40310
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_offline/Makefile
@@ -0,0 +1,17 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zpool_offline
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zpool_offline_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zpool_offline_002_neg.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zpool_offline_001_pos.ksh
+${PACKAGE}FILES+= zpool_offline.cfg
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_offline/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_offline/cleanup.ksh
new file mode 100644
index 000000000000..9038281882b5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_offline/cleanup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_offline/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_offline/setup.ksh
new file mode 100644
index 000000000000..f0e805f5f8b6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_offline/setup.ksh
@@ -0,0 +1,33 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+DISK=${DISKS%% *}
+
+default_mirror_setup $DISKS
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_offline/zpool_offline.cfg b/tests/sys/cddl/zfs/tests/cli_root/zpool_offline/zpool_offline.cfg
new file mode 100644
index 000000000000..99d93c9d9c72
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_offline/zpool_offline.cfg
@@ -0,0 +1,27 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_offline/zpool_offline_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_offline/zpool_offline_001_pos.ksh
new file mode 100644
index 000000000000..920af7b36a9c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_offline/zpool_offline_001_pos.ksh
@@ -0,0 +1,133 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_offline_001_pos
+#
+# DESCRIPTION:
+# Executing 'zpool offline' with valid parameters succeeds.
+#
+# STRATEGY:
+# 1. Create an array of correctly formed 'zpool offline' options
+# 2. Execute each element of the array.
+# 3. Verify use of each option is successful.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-09-30)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+DISKLIST=$(get_disklist $TESTPOOL)
+set -A disks $DISKLIST
+typeset -i num=${#disks[*]}
+
+set -A args "" "-t"
+
+function cleanup
+{
+ #
+ # Ensure we don't leave disks in the offline state
+ #
+ for disk in $DISKLIST; do
+ log_must $ZPOOL online $TESTPOOL $disk
+ check_state $TESTPOOL $disk "online"
+ if [[ $? != 0 ]]; then
+ log_fail "Unable to online $disk"
+ fi
+
+ done
+}
+
+log_assert "Executing 'zpool offline' with correct options succeeds"
+
+log_onexit cleanup
+
+if [[ -z $DISKLIST ]]; then
+ log_fail "DISKLIST is empty."
+fi
+
+typeset -i i=0
+typeset -i j=1
+
+for disk in $DISKLIST; do
+ i=0
+ while [[ $i -lt ${#args[*]} ]]; do
+ if (( j < num )) ; then
+ log_must $ZPOOL offline ${args[$i]} $TESTPOOL $disk
+ check_state $TESTPOOL $disk "offline"
+ if [[ $? != 0 ]]; then
+ log_fail "$disk of $TESTPOOL did not match offline state"
+ fi
+ else
+ log_mustnot $ZPOOL offline ${args[$i]} $TESTPOOL $disk
+ check_state $TESTPOOL $disk "online"
+ if [[ $? != 0 ]]; then
+ log_fail "$disk of $TESTPOOL did not match online state"
+ fi
+ fi
+
+ (( i = i + 1 ))
+ done
+ (( j = j + 1 ))
+done
+
+log_note "Issuing repeated 'zpool offline' commands succeeds."
+
+typeset -i iters=20
+typeset -i index=0
+
+for disk in $DISKLIST; do
+ i=0
+ while [[ $i -lt $iters ]]; do
+ index=`expr $RANDOM % ${#args[*]}`
+ log_must $ZPOOL offline ${args[$index]} $TESTPOOL $disk
+ check_state $TESTPOOL $disk "offline"
+ if [[ $? != 0 ]]; then
+ log_fail "$disk of $TESTPOOL is not offline."
+ fi
+
+ (( i = i + 1 ))
+ done
+
+ log_must $ZPOOL online $TESTPOOL $disk
+ check_state $TESTPOOL $disk "online"
+ if [[ $? != 0 ]]; then
+ log_fail "$disk of $TESTPOOL did not match online state"
+ fi
+done
+
+log_pass "'zpool offline' with correct options succeeded"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_offline/zpool_offline_002_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_offline/zpool_offline_002_neg.ksh
new file mode 100644
index 000000000000..a36a6c4bd055
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_offline/zpool_offline_002_neg.ksh
@@ -0,0 +1,104 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_offline_002_neg
+#
+# DESCRIPTION:
+# Executing 'zpool offline' command with bad options fails.
+#
+# STRATEGY:
+# 1. Create an array of badly formed 'zpool offline' options.
+# 2. Execute each element of the array.
+# 3. Verify an error code is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-09-30)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+DISKLIST=$(get_disklist $TESTPOOL)
+
+set -A args "" "-?" "-t fakepool" "-f fakepool" "-ev fakepool" "fakepool" \
+ "-t $TESTPOOL" "-t $TESTPOOL/$TESTFS" "-t $TESTPOOL/$TESTFS $DISKLIST" \
+ "-t $TESTPOOL/$TESTCTR" "-t $TESTPOOL/$TESTCTR/$TESTFS1" \
+ "-t $TESTPOOL/$TESTCTR $DISKLIST" "-t $TESTPOOL/$TESTVOL" \
+ "-t $TESTPOOL/$TESTCTR/$TESTFS1 $DISKLIST" \
+ "-t $TESTPOOL/$TESTVOL $DISKLIST" \
+ "-t $DISKLIST" \
+ "-f $TESTPOOL" "-f $TESTPOOL/$TESTFS" "-f $TESTPOOL/$TESTFS $DISKLIST" \
+ "-f $TESTPOOL/$TESTCTR" "-f $TESTPOOL/$TESTCTR/$TESTFS1" \
+ "-f $TESTPOOL/$TESTCTR $DISKLIST" "-f $TESTPOOL/$TESTVOL" \
+ "-f $TESTPOOL/$TESTCTR/$TESTFS1 $DISKLIST" \
+ "-f $TESTPOOL/$TESTVOL $DISKLIST" \
+ "-f $DISKLIST" \
+ "-ft $TESTPOOL" "-ft $TESTPOOL/$TESTFS" \
+ "-ft $TESTPOOL/$TESTFS $DISKLIST" \
+ "-ft $TESTPOOL/$TESTCTR" "-ft $TESTPOOL/$TESTCTR/$TESTFS1" \
+ "-ft $TESTPOOL/$TESTCTR $DISKLIST" "-ft $TESTPOOL/$TESTVOL" \
+ "-ft $TESTPOOL/$TESTCTR/$TESTFS1 $DISKLIST" \
+ "-ft $TESTPOOL/$TESTVOL $DISKLIST" \
+ "-ft $DISKLIST" \
+ "-tf $TESTPOOL" "-tf $TESTPOOL/$TESTFS" \
+ "-tf $TESTPOOL/$TESTFS $DISKLIST" \
+ "-tf $TESTPOOL/$TESTCTR" "-tf $TESTPOOL/$TESTCTR/$TESTFS1" \
+ "-tf $TESTPOOL/$TESTCTR $DISKLIST" "-tf $TESTPOOL/$TESTVOL" \
+ "-tf $TESTPOOL/$TESTCTR/$TESTFS1 $DISKLIST" \
+ "-tf $TESTPOOL/$TESTVOL $DISKLIST" \
+ "-tf $DISKLIST" \
+ "$TESTPOOL" "$TESTPOOL/$TESTFS" "$TESTPOOL/$TESTFS $DISKLIST" \
+ "$TESTPOOL/$TESTCTR" "$TESTPOOL/$TESTCTR/$TESTFS1" \
+ "$TESTPOOL/$TESTCTR $DISKLIST" "$TESTPOOL/$TESTVOL" \
+ "$TESTPOOL/$TESTCTR/$TESTFS1 $DISKLIST" "$TESTPOOL/$TESTVOL $DISKLIST" \
+ "$DISKLIST"
+
+log_assert "Executing 'zpool offline' with bad options fails"
+
+if [[ -z $DISKLIST ]]; then
+ log_fail "DISKLIST is empty."
+fi
+
+typeset -i i=0
+
+while [[ $i -lt ${#args[*]} ]]; do
+
+ log_mustnot $ZPOOL offline ${args[$i]}
+
+ (( i = i + 1 ))
+done
+
+log_pass "'zpool offline' command with bad options failed as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_offline/zpool_offline_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zpool_offline/zpool_offline_test.sh
new file mode 100755
index 000000000000..d3526ae8fa5e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_offline/zpool_offline_test.sh
@@ -0,0 +1,80 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zpool_offline_001_pos cleanup
+zpool_offline_001_pos_head()
+{
+ atf_set "descr" "Executing 'zpool offline' with correct options succeeds"
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_offline_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_offline.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_offline_001_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_offline_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_offline.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_offline_002_neg cleanup
+zpool_offline_002_neg_head()
+{
+ atf_set "descr" "Executing 'zpool offline' with bad options fails"
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_offline_002_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_offline.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_offline_002_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_offline_002_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_offline.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zpool_offline_001_pos
+ atf_add_test_case zpool_offline_002_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_online/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zpool_online/Makefile
new file mode 100644
index 000000000000..73d3cb438b87
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_online/Makefile
@@ -0,0 +1,17 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zpool_online
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zpool_online_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zpool_online.cfg
+${PACKAGE}FILES+= zpool_online_002_neg.ksh
+${PACKAGE}FILES+= zpool_online_001_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_online/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_online/cleanup.ksh
new file mode 100644
index 000000000000..9038281882b5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_online/cleanup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_online/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_online/setup.ksh
new file mode 100644
index 000000000000..f0e805f5f8b6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_online/setup.ksh
@@ -0,0 +1,33 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+DISK=${DISKS%% *}
+
+default_mirror_setup $DISKS
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_online/zpool_online.cfg b/tests/sys/cddl/zfs/tests/cli_root/zpool_online/zpool_online.cfg
new file mode 100644
index 000000000000..99d93c9d9c72
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_online/zpool_online.cfg
@@ -0,0 +1,27 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_online/zpool_online_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_online/zpool_online_001_pos.ksh
new file mode 100644
index 000000000000..2d3fe2d55afa
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_online/zpool_online_001_pos.ksh
@@ -0,0 +1,121 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_online_001
+#
+# DESCRIPTION:
+# Executing 'zpool online' with valid parameters succeeds.
+#
+# STRATEGY:
+# 1. Create an array of correctly formed 'zpool online' options
+# 2. Execute each element of the array.
+# 3. Verify use of each option is successful.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-09-30)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+DISKLIST=$(get_disklist $TESTPOOL)
+
+set -A args ""
+
+function cleanup
+{
+ #
+ # Ensure we don't leave disks in temporary online state (-t)
+ #
+ for disk in $DISKLIST; do
+ log_must $ZPOOL online $TESTPOOL $disk
+ check_state $TESTPOOL $disk "online"
+ if [[ $? != 0 ]]; then
+ log_fail "Unable to online $disk"
+ fi
+
+ done
+}
+
+log_assert "Executing 'zpool online' with correct options succeeds"
+
+log_onexit cleanup
+
+if [[ -z $DISKLIST ]]; then
+ log_fail "DISKLIST is empty."
+fi
+
+typeset -i i=0
+
+for disk in $DISKLIST; do
+ i=0
+ while [[ $i -lt ${#args[*]} ]]; do
+ log_must $ZPOOL offline $TESTPOOL $disk
+ check_state $TESTPOOL $disk "offline"
+ if [[ $? != 0 ]]; then
+ log_fail "$disk of $TESTPOOL did not match offline state"
+ fi
+
+ log_must $ZPOOL online ${args[$i]} $TESTPOOL $disk
+ check_state $TESTPOOL $disk "online"
+ if [[ $? != 0 ]]; then
+ log_fail "$disk of $TESTPOOL did not match online state"
+ fi
+
+ (( i = i + 1 ))
+ done
+done
+
+log_note "Issuing repeated 'zpool online' commands succeeds."
+
+typeset -i iters=20
+typeset -i index=0
+
+for disk in $DISKLIST; do
+ i=0
+ while [[ $i -lt $iters ]]; do
+ index=`expr $RANDOM % ${#args[*]}`
+ log_must $ZPOOL online ${args[$index]} $TESTPOOL $disk
+ check_state $TESTPOOL $disk "online"
+ if [[ $? != 0 ]]; then
+ log_fail "$disk of $TESTPOOL did not match online state"
+ fi
+
+ (( i = i + 1 ))
+ done
+done
+
+log_pass "'zpool online' with correct options succeeded"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_online/zpool_online_002_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_online/zpool_online_002_neg.ksh
new file mode 100644
index 000000000000..59264a8729f4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_online/zpool_online_002_neg.ksh
@@ -0,0 +1,83 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_online_002_neg
+#
+# DESCRIPTION:
+# Executing 'zpool online' command with bad options fails.
+#
+# STRATEGY:
+# 1. Create an array of badly formed 'zpool online' options.
+# 2. Execute each element of the array.
+# 3. Verify an error code is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-09-30)
+#
+# __stc_assertion_end
+#
+################################################################################
+verify_runnable "global"
+
+DISKLIST=$(get_disklist $TESTPOOL)
+
+set -A args "" "-?" "-e fakepool" "-v fakepool" "-ev fakepool" "-ve fakepool" \
+ "-t $TESTPOOL" "-t $TESTPOOL/$TESTFS" "-t $TESTPOOL/$TESTFS $DISKLIST" \
+ "-t $TESTPOOL/$TESTCTR" "-t $TESTPOOL/$TESTCTR/$TESTFS1" \
+ "-t $TESTPOOL/$TESTCTR $DISKLIST" "-t $TESTPOOL/$TESTVOL" \
+ "-t $TESTPOOL/$TESTCTR/$TESTFS1 $DISKLIST" \
+ "-t $TESTPOOL/$TESTVOL $DISKLIST" \
+ "-t $DISKLIST" \
+ "$TESTPOOL" "$TESTPOOL/$TESTFS" "$TESTPOOL/$TESTFS $DISKLIST" \
+ "$TESTPOOL/$TESTCTR" "$TESTPOOL/$TESTCTR/$TESTFS1" \
+ "$TESTPOOL/$TESTCTR $DISKLIST" "$TESTPOOL/$TESTVOL" \
+ "$TESTPOOL/$TESTCTR/$TESTFS1 $DISKLIST" "$TESTPOOL/$TESTVOL $DISKLIST" \
+ "$DISKLIST"
+
+log_assert "Executing 'zpool online' with bad options fails"
+
+if [[ -z $DISKLIST ]]; then
+ log_fail "DISKLIST is empty."
+fi
+
+typeset -i i=0
+
+while [[ $i -lt ${#args[*]} ]]; do
+
+ log_mustnot $ZPOOL online ${args[$i]}
+
+ (( i = i + 1 ))
+done
+
+log_pass "'zpool online' command with bad options failed as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_online/zpool_online_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zpool_online/zpool_online_test.sh
new file mode 100755
index 000000000000..1cbbc85618ab
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_online/zpool_online_test.sh
@@ -0,0 +1,80 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zpool_online_001_pos cleanup
+zpool_online_001_pos_head()
+{
+ atf_set "descr" "Executing 'zpool online' with correct options succeeds"
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_online_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_online.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_online_001_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_online_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_online.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_online_002_neg cleanup
+zpool_online_002_neg_head()
+{
+ atf_set "descr" "Executing 'zpool online' with bad options fails"
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_online_002_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_online.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_online_002_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_online_002_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_online.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zpool_online_001_pos
+ atf_add_test_case zpool_online_002_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_remove/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zpool_remove/Makefile
new file mode 100644
index 000000000000..39fdf272482f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_remove/Makefile
@@ -0,0 +1,18 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zpool_remove
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zpool_remove_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zpool_remove_001_neg.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zpool_remove.cfg
+${PACKAGE}FILES+= zpool_remove_002_pos.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zpool_remove_003_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_remove/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_remove/cleanup.ksh
new file mode 100644
index 000000000000..92fa150469be
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_remove/cleanup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+cleanup_devices $DISKS
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_remove/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_remove/setup.ksh
new file mode 100644
index 000000000000..b6e91cf56452
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_remove/setup.ksh
@@ -0,0 +1,32 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+verify_runnable "global"
+
+partition_disk $SIZE $DISK 6
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_remove/zpool_remove.cfg b/tests/sys/cddl/zfs/tests/cli_root/zpool_remove/zpool_remove.cfg
new file mode 100644
index 000000000000..daabbebcd86c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_remove/zpool_remove.cfg
@@ -0,0 +1,30 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
+
+export DISK=${DISKS%% *}
+export SIZE="200m"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_remove/zpool_remove_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_remove/zpool_remove_001_neg.ksh
new file mode 100644
index 000000000000..adb476cf2203
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_remove/zpool_remove_001_neg.ksh
@@ -0,0 +1,103 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_remove_001_neg
+#
+# DESCRIPTION:
+# Verify that 'zpool can not remove device except inactive hot spares from pool'
+#
+# STRATEGY:
+# 1. Create all kinds of pool (strip, mirror, raidz, hotspare)
+# 2. Try to remove device from the pool
+# 3. Verify that the remove failed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-18)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+typeset disk=${DISK}
+typeset vdev_devs="${disk}p1"
+typeset mirror_devs="${disk}p1 ${disk}p2"
+typeset raidz_devs=${mirror_devs}
+typeset raidz1_devs=${mirror_devs}
+typeset raidz2_devs="${mirror_devs} ${disk}p3"
+typeset spare_devs1="${disk}p1"
+typeset spare_devs2="${disk}p2"
+
+function check_remove
+{
+ typeset pool=$1
+ typeset devs="$2"
+ typeset dev
+
+ for dev in $devs; do
+ log_mustnot $ZPOOL remove $dev
+ done
+
+ destroy_pool $pool
+
+}
+
+function cleanup
+{
+ if poolexists $TESTPOOL; then
+ destroy_pool $TESTPOOL
+ fi
+}
+
+set -A create_args "$vdev_devs" "mirror $mirror_devs" \
+ "raidz $raidz_devs" "raidz $raidz1_devs" \
+ "raidz2 $raidz2_devs" \
+ "$spare_devs1 spare $spare_devs2"
+
+set -A verify_disks "$vdev_devs" "$mirror_devs" "$raidz_devs" \
+ "$raidz1_devs" "$raidz2_devs" "$spare_devs1"
+
+
+log_assert "Check zpool remove <pool> <device> can not remove " \
+ "active device from pool"
+
+log_onexit cleanup
+
+typeset -i i=0
+while [[ $i -lt ${#create_args[*]} ]]; do
+ log_must $ZPOOL create $TESTPOOL ${create_args[i]}
+ check_remove $TESTPOOL "${verify_disks[i]}"
+ (( i = i + 1))
+done
+
+log_pass "'zpool remove <pool> <device> fail as expected .'"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_remove/zpool_remove_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_remove/zpool_remove_002_pos.ksh
new file mode 100644
index 000000000000..93989b6fa212
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_remove/zpool_remove_002_pos.ksh
@@ -0,0 +1,74 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_remove_002_pos
+#
+# DESCRIPTION:
+# Verify that 'zpool can only remove inactive hot spare devices from pool'
+#
+# STRATEGY:
+# 1. Create a hotspare pool
+# 2. Try to remove the inactive hotspare device from the pool
+# 3. Verify that the remove succeed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-18)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function cleanup
+{
+ destroy_pool $TESTPOOL
+}
+
+log_onexit cleanup
+typeset disk=${DISK}
+
+typeset spare_devs1="${disk}p1"
+typeset spare_devs2="${disk}p2"
+
+log_assert "zpool remove can only remove inactive hotspare device from pool"
+
+log_note "check hotspare device which is created by zpool create"
+log_must $ZPOOL create $TESTPOOL $spare_devs1 spare $spare_devs2
+log_must $ZPOOL remove $TESTPOOL $spare_devs2
+
+log_note "check hotspare device which is created by zpool add"
+log_must $ZPOOL add $TESTPOOL spare $spare_devs2
+log_must $ZPOOL remove $TESTPOOL $spare_devs2
+log_must $ZPOOL destroy $TESTPOOL
+
+log_pass "zpool remove can only remove inactive hotspare device from pool"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_remove/zpool_remove_003_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_remove/zpool_remove_003_pos.ksh
new file mode 100644
index 000000000000..363b737d16a1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_remove/zpool_remove_003_pos.ksh
@@ -0,0 +1,85 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_remove_003_pos
+#
+# DESCRIPTION:
+# Verify that 'zpool can remove hotspare devices from pool when it state
+# switch from active to inactive'
+#
+# STRATEGY:
+# 1. Create a hotspare pool
+# 2. Try to replace the inactive hotspare device to active device in the pool
+# 3. Try to detach active (spare) device to make it inactive
+# 3. Verify that the zpool remove succeed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-18)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function cleanup
+{
+ restart_zfsd
+
+ if poolexists $TESTPOOL; then
+ destroy_pool $TESTPOOL
+ fi
+}
+
+# Stop ZFSD because it interferes with our manually activated spares
+stop_zfsd
+
+log_onexit cleanup
+typeset disk=${DISK}
+
+typeset spare_devs1="${disk}p1"
+typeset spare_devs2="${disk}p2"
+typeset spare_devs3="${disk}p3"
+typeset spare_devs4="${disk}p4"
+
+log_assert "zpool remove can remove hotspare device which state go though" \
+ " active to inactive in pool"
+
+log_note "Check spare device which state go through active to inactive"
+log_must $ZPOOL create $TESTPOOL $spare_devs1 $spare_devs2 spare \
+ $spare_devs3 $spare_devs4
+log_must $ZPOOL replace $TESTPOOL $spare_devs2 $spare_devs3
+log_mustnot $ZPOOL remove $TESTPOOL $spare_devs3
+log_must $ZPOOL detach $TESTPOOL $spare_devs3
+log_must $ZPOOL remove $TESTPOOL $spare_devs3
+
+log_pass "'zpool remove device passed as expected.'"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_remove/zpool_remove_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zpool_remove/zpool_remove_test.sh
new file mode 100755
index 000000000000..5cb30c096547
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_remove/zpool_remove_test.sh
@@ -0,0 +1,105 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zpool_remove_001_neg cleanup
+zpool_remove_001_neg_head()
+{
+ atf_set "descr" "Check zpool remove <pool> <device> can not removeactive device from pool"
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_remove_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_remove.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_remove_001_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_remove_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_remove.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_remove_002_pos cleanup
+zpool_remove_002_pos_head()
+{
+ atf_set "descr" "zpool remove can only remove inactive hotspare device from pool"
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_remove_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_remove.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_remove_002_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_remove_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_remove.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_remove_003_pos cleanup
+zpool_remove_003_pos_head()
+{
+ atf_set "descr" "zpool remove can remove hotspare device which state go though active to inactive in pool"
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_remove_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_remove.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_remove_003_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_remove_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_remove.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zpool_remove_001_neg
+ atf_add_test_case zpool_remove_002_pos
+ atf_add_test_case zpool_remove_003_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_replace/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zpool_replace/Makefile
new file mode 100644
index 000000000000..dfe0a7c265d5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_replace/Makefile
@@ -0,0 +1,17 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zpool_replace
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zpool_replace_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zpool_replace_001_neg.ksh
+${PACKAGE}FILES+= zpool_replace_002_neg.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zpool_replace.cfg
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_replace/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_replace/cleanup.ksh
new file mode 100644
index 000000000000..9038281882b5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_replace/cleanup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_replace/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_replace/setup.ksh
new file mode 100644
index 000000000000..f0e805f5f8b6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_replace/setup.ksh
@@ -0,0 +1,33 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+DISK=${DISKS%% *}
+
+default_mirror_setup $DISKS
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_replace/zpool_replace.cfg b/tests/sys/cddl/zfs/tests/cli_root/zpool_replace/zpool_replace.cfg
new file mode 100644
index 000000000000..99d93c9d9c72
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_replace/zpool_replace.cfg
@@ -0,0 +1,27 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_replace/zpool_replace_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_replace/zpool_replace_001_neg.ksh
new file mode 100644
index 000000000000..9c2d79fec143
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_replace/zpool_replace_001_neg.ksh
@@ -0,0 +1,91 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_replace_001_neg
+#
+# DESCRIPTION:
+# Executing 'zpool replace' command with bad options fails.
+#
+# STRATEGY:
+# 1. Create an array of badly formed 'zpool replace' options.
+# 2. Execute each element of the array.
+# 3. Verify an error code is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-10-18)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+DISKLIST=$(get_disklist $TESTPOOL)
+
+set -A args "" "-f" "-?" "-z fakepool" "-f fakepool" "-ev fakepool" "fakepool" \
+ "$TESTPOOL" "-t $TESTPOOL/$TESTFS" "-t $TESTPOOL/$TESTFS $DISKLIST" \
+ "$TESTPOOL/$TESTCTR" "-t $TESTPOOL/$TESTCTR/$TESTFS1" \
+ "$TESTPOOL/$TESTCTR $DISKLIST" "-t $TESTPOOL/$TESTVOL" \
+ "$TESTPOOL/$TESTCTR/$TESTFS1 $DISKLIST" \
+ "$TESTPOOL/$TESTVOL $DISKLIST" \
+ "$DISKLIST" \
+ "fakepool fakedevice" "fakepool fakedevice fakenewdevice" \
+ "$TESTPOOL fakedevice" "$TESTPOOL $DISKLIST" \
+ "$TESTPOOL fakedevice fakenewdevice fakenewdevice" \
+ "-f $TESTPOOL" "-f $TESTPOOL/$TESTFS" "-f $TESTPOOL/$TESTFS $DISKLIST" \
+ "-f $TESTPOOL/$TESTCTR" "-f $TESTPOOL/$TESTCTR/$TESTFS1" \
+ "-f $TESTPOOL/$TESTCTR $DISKLIST" "-f $TESTPOOL/$TESTVOL" \
+ "-f $TESTPOOL/$TESTCTR/$TESTFS1 $DISKLIST" \
+ "-f $TESTPOOL/$TESTVOL $DISKLIST" \
+ "-f $DISKLIST" \
+ "-f fakepool fakedevice" "-f fakepool fakedevice fakenewdevice" \
+ "-f $TESTPOOL fakedevice fakenewdevice fakenewdevice" \
+ "-f $TESTPOOL fakedevice" "-f $TESTPOOL $DISKLIST"
+
+log_assert "Executing 'zpool replace' with bad options fails"
+
+if [[ -z $DISKLIST ]]; then
+ log_fail "DISKLIST is empty."
+fi
+
+typeset -i i=0
+
+while [[ $i -lt ${#args[*]} ]]; do
+
+ log_mustnot $ZPOOL replace ${args[$i]}
+
+ (( i = i + 1 ))
+done
+
+log_pass "'zpool replace' command with bad options failed as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_replace/zpool_replace_002_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_replace/zpool_replace_002_neg.ksh
new file mode 100644
index 000000000000..9c79b6bec94f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_replace/zpool_replace_002_neg.ksh
@@ -0,0 +1,38 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2017 Spectra Logic Corp. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+
+log_assert "'zpool replace' should fail if the new device is too small"
+
+log_must $TRUNCATE -s 1024m bigfile
+log_must $TRUNCATE -s 512m smallfile
+
+log_must $ZPOOL create $TESTPOOL $PWD/bigfile
+log_mustnot $ZPOOL replace $TESTPOOL $PWD/bigfile $PWD/smallfile
+
+log_pass "'zpool replace' should fail if the new device is too small"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_replace/zpool_replace_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zpool_replace/zpool_replace_test.sh
new file mode 100755
index 000000000000..e5b2c45709c8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_replace/zpool_replace_test.sh
@@ -0,0 +1,77 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zpool_replace_001_neg cleanup
+zpool_replace_001_neg_head()
+{
+ atf_set "descr" "Executing 'zpool replace' with bad options fails"
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_replace_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_replace.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_replace_001_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_replace_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_replace.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case zpool_replace_002_neg cleanup
+zpool_replace_002_neg_head()
+{
+ atf_set "descr" "'zpool replace' should fail if the new device is too small"
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_replace_002_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_replace.cfg
+
+ ksh93 $(atf_get_srcdir)/zpool_replace_002_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_replace_002_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_replace.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zpool_replace_001_neg
+ atf_add_test_case zpool_replace_002_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/Makefile
new file mode 100644
index 000000000000..5c855230c0d7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/Makefile
@@ -0,0 +1,20 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zpool_scrub
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zpool_scrub_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zpool_scrub_001_neg.ksh
+${PACKAGE}FILES+= zpool_scrub_004_pos.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zpool_scrub_005_pos.ksh
+${PACKAGE}FILES+= zpool_scrub.cfg
+${PACKAGE}FILES+= zpool_scrub_003_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zpool_scrub_002_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/cleanup.ksh
new file mode 100644
index 000000000000..6395bb8cb9f8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/cleanup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+destroy_mirrors
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/setup.ksh
new file mode 100644
index 000000000000..3c79c5127a37
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/setup.ksh
@@ -0,0 +1,40 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+default_mirror_setup_noexit $DISK1 $DISK2
+
+mntpnt=$(get_prop mountpoint $TESTPOOL)
+typeset -i i=0
+while ((i < 10)); do
+ log_must $MKFILE 500M $mntpnt/bigfile.$i
+ ((i += 1))
+done
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/zpool_scrub.cfg b/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/zpool_scrub.cfg
new file mode 100644
index 000000000000..59d3615669bb
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/zpool_scrub.cfg
@@ -0,0 +1,29 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/cli_root/cli.cfg
+
+export DISK1=$($ECHO $DISKS | $AWK '{print $1}')
+export DISK2=$($ECHO $DISKS | $AWK '{print $2}')
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/zpool_scrub_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/zpool_scrub_001_neg.ksh
new file mode 100644
index 000000000000..39e45004ab47
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/zpool_scrub_001_neg.ksh
@@ -0,0 +1,71 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_scrub_001_neg
+#
+# DESCRIPTION:
+# A badly formed parameter passed to 'zpool scrub' should
+# return an error.
+#
+# STRATEGY:
+# 1. Create an array containing bad 'zpool scrub' parameters.
+# 2. For each element, execute the sub-command.
+# 3. Verify it returns an error.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-10-19)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+set -A args "" "-?" "blah blah" "-%" "--?" "-*" "-=" \
+ "-a" "-b" "-c" "-d" "-e" "-f" "-g" "-h" "-i" "-j" "-k" "-l" \
+ "-m" "-n" "-o" "-p" "-q" "-r" "-s" "-t" "-u" "-v" "-w" "-x" "-y" "-z" \
+ "-A" "-B" "-C" "-D" "-E" "-F" "-G" "-H" "-I" "-J" "-K" "-L" \
+ "-M" "-N" "-O" "-P" "-Q" "-R" "-S" "-T" "-U" "-V" "-W" "-X" "-W" "-Z"
+
+
+log_assert "Execute 'zpool scrub' using invalid parameters."
+
+typeset -i i=0
+while [[ $i -lt ${#args[*]} ]]; do
+ log_mustnot $ZPOOL scrub ${args[i]}
+
+ ((i = i + 1))
+done
+
+log_pass "Badly formed 'zpool scrub' parameters fail as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/zpool_scrub_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/zpool_scrub_002_pos.ksh
new file mode 100644
index 000000000000..4dc527658b7c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/zpool_scrub_002_pos.ksh
@@ -0,0 +1,61 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_scrub_002_pos
+#
+# DESCRIPTION:
+# Verify scrub -s works correctly.
+#
+# STRATEGY:
+# 1. Create pool and fill with hundreds data.
+# 2. zpool scrub the pool
+# 3. Verify zpool scrub -s succeed when the system is scrubbing.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-08-08)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Verify scrub -s works correctly."
+
+log_must $ZPOOL scrub $TESTPOOL
+log_must $ZPOOL scrub -s $TESTPOOL
+log_must is_pool_scrub_stopped $TESTPOOL
+
+log_pass "Verify scrub -s works correctly."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/zpool_scrub_003_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/zpool_scrub_003_pos.ksh
new file mode 100644
index 000000000000..56b514d87447
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/zpool_scrub_003_pos.ksh
@@ -0,0 +1,84 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_scrub_003_pos
+#
+# DESCRIPTION:
+# scrub command terminates the existing scrub process and starts
+# a new scrub.
+#
+# STRATEGY:
+# 1. Setup a pool and fill with data
+# 2. Kick off a scrub
+# 3. Check the completed percent and invoke another scrub
+# 4. Check the percent again, verify a new scrub started.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-08-16)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function get_scrub_percent
+{
+ typeset -i percent
+ percent=$($ZPOOL status $TESTPOOL | $GREP "^ scrub" | \
+ $AWK '{print $7}' | $AWK -F. '{print $1}')
+ if is_pool_scrubbed $TESTPOOL ; then
+ percent=100
+ fi
+ $ECHO $percent
+}
+
+log_assert "scrub command terminates the existing scrub process and starts" \
+ "a new scrub."
+
+log_must $ZPOOL scrub $TESTPOOL
+typeset -i PERCENT=30 percent=0
+while ((percent < PERCENT)) ; do
+ percent=$(get_scrub_percent)
+done
+
+log_must $ZPOOL scrub $TESTPOOL
+percent=$(get_scrub_percent)
+if ((percent > PERCENT)); then
+ log_fail "zpool scrub don't stop existing scrubbing process."
+fi
+
+log_pass "scrub command terminates the existing scrub process and starts" \
+ "a new scrub."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/zpool_scrub_004_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/zpool_scrub_004_pos.ksh
new file mode 100644
index 000000000000..87d07aa67309
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/zpool_scrub_004_pos.ksh
@@ -0,0 +1,71 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_scrub_004_pos
+#
+# DESCRIPTION:
+# Resilver prevent scrub from starting until the resilver completes
+#
+# STRATEGY:
+# 1. Setup a mirror pool and filled with data.
+# 2. Detach one of devices
+# 3. Verify scrub failed until the resilver completed
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-08-16)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Resilver prevent scrub from starting until the resilver completes"
+
+log_must $ZPOOL detach $TESTPOOL $DISK2
+log_must $ZPOOL attach $TESTPOOL $DISK1 $DISK2
+log_must is_pool_resilvering $TESTPOOL
+log_mustnot $ZPOOL scrub $TESTPOOL
+
+while true; do
+ if is_pool_resilvered $TESTPOOL ; then
+ $SLEEP 3
+ break
+ fi
+done
+
+log_must $ZPOOL scrub $TESTPOOL
+
+log_pass "Resilver prevent scrub from starting until the resilver completes"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/zpool_scrub_005_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/zpool_scrub_005_pos.ksh
new file mode 100644
index 000000000000..d484299dc871
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/zpool_scrub_005_pos.ksh
@@ -0,0 +1,72 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_scrub_005_pos
+#
+# DESCRIPTION:
+# When scrubbing, detach device should not break system.
+#
+# STRATEGY:
+# 1. Setup filesys with data.
+# 2. Detaching and attaching the device when scrubbing.
+# 3. Try it twice, verify both of them work fine.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-08-16)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "When scrubbing, detach device should not break system."
+
+log_must $ZPOOL scrub $TESTPOOL
+log_must $ZPOOL detach $TESTPOOL $DISK2
+log_must $ZPOOL attach $TESTPOOL $DISK1 $DISK2
+
+while true ; do
+ if is_pool_resilvered $TESTPOOL ; then
+ $SLEEP 3
+ break
+ fi
+done
+
+log_must $ZPOOL scrub $TESTPOOL
+log_must $ZPOOL detach $TESTPOOL $DISK1
+log_must $ZPOOL attach $TESTPOOL $DISK2 $DISK1
+
+log_pass "When scrubbing, detach device should not break system."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/zpool_scrub_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/zpool_scrub_test.sh
new file mode 100755
index 000000000000..fcd5e62ab0a9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_scrub/zpool_scrub_test.sh
@@ -0,0 +1,155 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zpool_scrub_001_neg cleanup
+zpool_scrub_001_neg_head()
+{
+ atf_set "descr" "Execute 'zpool scrub' using invalid parameters."
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_scrub_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_scrub.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_scrub_001_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_scrub_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_scrub.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_scrub_002_pos cleanup
+zpool_scrub_002_pos_head()
+{
+ atf_set "descr" "Verify scrub -s works correctly."
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_scrub_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_scrub.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_scrub_002_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_scrub_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_scrub.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_scrub_003_pos cleanup
+zpool_scrub_003_pos_head()
+{
+ atf_set "descr" "scrub command terminates the existing scrub process and starts a new scrub."
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_scrub_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_scrub.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_scrub_003_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_scrub_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_scrub.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_scrub_004_pos cleanup
+zpool_scrub_004_pos_head()
+{
+ atf_set "descr" "Resilver prevent scrub from starting until the resilver completes"
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_scrub_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_scrub.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_scrub_004_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_scrub_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_scrub.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_scrub_005_pos cleanup
+zpool_scrub_005_pos_head()
+{
+ atf_set "descr" "When scrubbing, detach device should not break system."
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_scrub_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_scrub.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_scrub_005_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_scrub_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_scrub.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zpool_scrub_001_neg
+ atf_add_test_case zpool_scrub_002_pos
+ atf_add_test_case zpool_scrub_003_pos
+ atf_add_test_case zpool_scrub_004_pos
+ atf_add_test_case zpool_scrub_005_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_set/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zpool_set/Makefile
new file mode 100644
index 000000000000..4e8936cdb3b1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_set/Makefile
@@ -0,0 +1,15 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zpool_set
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zpool_set_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zpool_set_002_neg.ksh
+${PACKAGE}FILES+= zpool_set_003_neg.ksh
+${PACKAGE}FILES+= zpool_set_001_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_set/zpool_set_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_set/zpool_set_001_pos.ksh
new file mode 100644
index 000000000000..74be1182ffd8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_set/zpool_set_001_pos.ksh
@@ -0,0 +1,75 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_set_001_pos
+#
+# DESCRIPTION:
+#
+# Zpool set usage message is displayed when called with no arguments
+#
+# STRATEGY:
+# 1. Run zpool set
+# 2. Check that exit status is set to 2
+# 3. Check usage message contains text "usage"
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-03-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+log_assert "zpool set usage message is displayed when called with no arguments"
+
+$ZPOOL upgrade -v 2>&1 | $GREP "bootfs pool property" > /dev/null
+if [ $? -ne 0 ]
+then
+ log_unsupported "Pool properties not supported on this release."
+fi
+
+$ZPOOL set > /dev/null 2>&1
+RET=$?
+if [ $RET != 2 ]
+then
+ log_fail "\"zpool set\" exit status $RET should be equal to 2."
+fi
+
+OUTPUT=$($ZPOOL set 2>&1 | $GREP -i usage)
+if [ $? != 0 ]
+then
+ log_fail "Usage message for zpool set did not contain the word 'usage'."
+fi
+
+log_pass "zpool set usage message is displayed when called with no arguments"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_set/zpool_set_002_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_set/zpool_set_002_neg.ksh
new file mode 100644
index 000000000000..54d645f0b20f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_set/zpool_set_002_neg.ksh
@@ -0,0 +1,139 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_set_002_neg
+#
+# DESCRIPTION:
+#
+# Malformed zpool set commands are rejected
+#
+# STRATEGY:
+# 1. Create an array of many different malformed zfs set arguments
+# 2. Run zpool set for each arg checking each will exit with status code 1
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-03-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+# note to self - need to make sure there isn't a pool called bootfs
+# before running this test...
+function cleanup {
+ destroy_pool bootfs
+ $RM $TMPDIR/zpool_set_002.${TESTCASE_ID}.dat
+}
+
+$ZPOOL upgrade -v 2>&1 | $GREP "bootfs pool property" > /dev/null
+if [ $? -ne 0 ]
+then
+ log_unsupported "Pool properties not supported on this release."
+fi
+
+
+log_assert "Malformed zpool set commands are rejected"
+
+if poolexists bootfs
+then
+ log_unsupported "Unable to run test on a machine with a pool called \
+ bootfs"
+fi
+
+log_onexit cleanup
+
+# build up an array of bad arguments.
+set -A arguments "rubbish " \
+ "foo@bar= " \
+ "@@@= +pool " \
+ "zpool bootfs " \
+ "bootfs " \
+ "bootfs +" \
+ "bootfs=bootfs/123 " \
+ "bootfs=bootfs@val " \
+ "Bootfs=bootfs " \
+ "- " \
+ "== " \
+ "set " \
+ "@@ " \
+ "12345 " \
+ "€にほんご " \
+ "/ " \
+ "bootfs=bootfs /" \
+ "bootfs=a%d%s "
+
+
+# here, we build up a large string.
+# a word to the ksh-wary, ${#array[@]} gives you the
+# total number of entries in an array, so array[${#array[@]}]
+# will index the last entry+1, ksh arrays start at index 0.
+COUNT=0
+while [ $COUNT -le 1025 ]
+do
+ bigname="${bigname}o"
+ COUNT=$(( $COUNT + 1 ))
+done
+
+# add an argument of maximum length property name
+arguments[${#arguments[@]}]="$bigname=value"
+
+# add an argument of maximum length property value
+arguments[${#arguments[@]}]="bootfs=$bigname"
+
+# Create a pool called bootfs (so-called, so as to trip any clashes between
+# property name, and pool name)
+# Also create a filesystem in this pool
+VDEV=$TMPDIR/zpool_set_002.${TESTCASE_ID}.vdev
+log_must create_vdevs $VDEV
+log_must $ZPOOL create bootfs $VDEV
+log_must $ZFS create bootfs/root
+
+typeset -i i=0;
+while [ $i -lt "${#arguments[@]}" ]
+do
+ log_mustnot eval "$ZPOOL set ${arguments[$i]} > /dev/null 2>&1"
+
+ # now also try with a valid pool in the argument list
+ log_mustnot eval "$ZPOOL set ${arguments[$i]}bootfs > /dev/null 2>&1"
+
+ # now also try with two valid pools in the argument list
+ log_mustnot eval "$ZPOOL set ${arguments[$i]}bootfs bootfs > /dev/null"
+ i=$(( $i + 1))
+done
+
+log_pass "Malformed zpool set commands are rejected"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_set/zpool_set_003_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_set/zpool_set_003_neg.ksh
new file mode 100644
index 000000000000..afd3caeedf8d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_set/zpool_set_003_neg.ksh
@@ -0,0 +1,90 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_set_003_neg
+#
+# DESCRIPTION:
+#
+# zpool set cannot set a readonly property
+#
+# STRATEGY:
+# 1. Create a pool
+# 2. Verify that we can't set readonly properties on that pool
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-08-24)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ destroy_pool $TESTPOOL
+ $RM $TMPDIR/zpool_set_003.${TESTCASE_ID}.dat
+}
+
+set -A props "available" "capacity" "guid" "health" "size" "used"
+set -A vals "100" "10" "12345" "HEALTHY" "10" "10"
+
+$ZPOOL upgrade -v 2>&1 | $GREP "bootfs pool property" > /dev/null
+if [ $? -ne 0 ]
+then
+ log_unsupported "Pool properties not supported on this release."
+fi
+
+
+
+log_onexit cleanup
+
+log_assert "zpool set cannot set a readonly property"
+
+VDEV=$TMPDIR/zpool_set_003.${TESTCASE_ID}.vdev
+log_must create_vdevs $VDEV
+log_must $ZPOOL create $TESTPOOL $VDEV
+
+typeset -i i=0;
+while [ $i -lt "${#props[@]}" ]
+do
+ # try to set each property in the prop list with it's corresponding val
+ log_mustnot eval "$ZPOOL set ${props[$i]}=${vals[$i]} $TESTPOOL \
+ > /dev/null 2>&1"
+ i=$(( $i + 1))
+done
+
+log_pass "zpool set cannot set a readonly property"
+
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_set/zpool_set_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zpool_set/zpool_set_test.sh
new file mode 100755
index 000000000000..9306d0badc71
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_set/zpool_set_test.sh
@@ -0,0 +1,75 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zpool_set_001_pos
+zpool_set_001_pos_head()
+{
+ atf_set "descr" "zpool set usage message is displayed when called with no arguments"
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_set_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+
+ ksh93 $(atf_get_srcdir)/zpool_set_001_pos.ksh || atf_fail "Testcase failed"
+}
+
+
+atf_test_case zpool_set_002_neg
+zpool_set_002_neg_head()
+{
+ atf_set "descr" "Malformed zpool set commands are rejected"
+ atf_set "require.progs" "ksh93 zpool zfs"
+}
+zpool_set_002_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+
+ ksh93 $(atf_get_srcdir)/zpool_set_002_neg.ksh || atf_fail "Testcase failed"
+}
+
+
+atf_test_case zpool_set_003_neg
+zpool_set_003_neg_head()
+{
+ atf_set "descr" "zpool set cannot set a readonly property"
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_set_003_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+
+ ksh93 $(atf_get_srcdir)/zpool_set_003_neg.ksh || atf_fail "Testcase failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zpool_set_001_pos
+ atf_add_test_case zpool_set_002_neg
+ atf_add_test_case zpool_set_003_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_status/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zpool_status/Makefile
new file mode 100644
index 000000000000..4340af728892
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_status/Makefile
@@ -0,0 +1,17 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zpool_status
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zpool_status_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zpool_status.cfg
+${PACKAGE}FILES+= zpool_status_001_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zpool_status_002_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_status/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_status/cleanup.ksh
new file mode 100644
index 000000000000..713cb16bb31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_status/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_status/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_status/setup.ksh
new file mode 100644
index 000000000000..326bc555f71b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_status/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_status/zpool_status.cfg b/tests/sys/cddl/zfs/tests/cli_root/zpool_status/zpool_status.cfg
new file mode 100644
index 000000000000..99d93c9d9c72
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_status/zpool_status.cfg
@@ -0,0 +1,27 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_status/zpool_status_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_status/zpool_status_001_pos.ksh
new file mode 100644
index 000000000000..7cba6b27dd59
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_status/zpool_status_001_pos.ksh
@@ -0,0 +1,70 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_status_001_pos
+#
+# DESCRIPTION:
+# Executing 'zpool status' command with bad options fails.
+#
+# STRATEGY:
+# 1. Create an array of badly formed 'zpool status' options
+# 2. Execute each element of the array.
+# 3. Verify an error code is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+
+set -A args "" "-?" "-x fakepool" "-v fakepool" "-xv fakepool" "-vx fakepool" \
+ "-x $TESTPOOL/$TESTFS" "-v $TESTPOOL/$TESTFS" "-xv $TESTPOOL/$TESTFS" \
+ "-vx $TESTPOOL/$TESTFS"
+
+log_assert "Executing 'zpool status' with bad options fails"
+
+typeset -i i=1
+
+while [[ $i -lt ${#args[*]} ]]; do
+
+ log_mustnot $ZPOOL status ${args[$i]}
+
+ (( i = i + 1 ))
+done
+
+log_pass "'zpool status' command with bad options failed as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_status/zpool_status_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_status/zpool_status_002_pos.ksh
new file mode 100644
index 000000000000..4314b439bfc9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_status/zpool_status_002_pos.ksh
@@ -0,0 +1,75 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_status_002_pos
+#
+# DESCRIPTION:
+# Executing 'zpool status' with correct options succeeds
+#
+# STRATEGY:
+# 1. Create an array of correctly formed 'zpool status' options
+# 2. Execute each element of the array.
+# 3. Verify use of each option is successful.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+typeset testpool
+if is_global_zone; then
+ testpool=$TESTPOOL
+else
+ testpool=${TESTPOOL%%/*}
+fi
+
+set -A args "" "-x" "-v" "-x $testpool" "-v $testpool" "-xv $testpool" \
+ "-vx $testpool"
+
+log_assert "Executing 'zpool status' with correct options succeeds"
+
+typeset -i i=0
+
+while [[ $i -lt ${#args[*]} ]]; do
+
+ log_must $ZPOOL status ${args[$i]}
+
+ (( i = i + 1 ))
+done
+
+log_pass "'zpool status' with correct options succeeded"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_status/zpool_status_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zpool_status/zpool_status_test.sh
new file mode 100755
index 000000000000..07e2999b174c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_status/zpool_status_test.sh
@@ -0,0 +1,80 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zpool_status_001_pos cleanup
+zpool_status_001_pos_head()
+{
+ atf_set "descr" "Executing 'zpool status' with bad options fails"
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_status_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_status.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_status_001_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_status_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_status.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_status_002_pos cleanup
+zpool_status_002_pos_head()
+{
+ atf_set "descr" "Executing 'zpool status' with correct options succeeds"
+ atf_set "require.progs" "ksh93 zpool"
+}
+zpool_status_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_status.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_status_002_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_status_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_status.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zpool_status_001_pos
+ atf_add_test_case zpool_status_002_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/Makefile
new file mode 100644
index 000000000000..f6365bbb46d5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/Makefile
@@ -0,0 +1,27 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zpool_upgrade
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zpool_upgrade_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zpool_upgrade_003_pos.ksh
+${PACKAGE}FILES+= zpool_upgrade_007_pos.ksh
+${PACKAGE}FILES+= zpool_upgrade_006_neg.ksh
+${PACKAGE}FILES+= zpool_upgrade_002_pos.ksh
+${PACKAGE}FILES+= zpool_upgrade.cfg
+${PACKAGE}FILES+= zpool_upgrade_004_pos.ksh
+${PACKAGE}FILES+= zpool_upgrade_008_pos.ksh
+${PACKAGE}FILES+= zpool_upgrade_005_neg.ksh
+${PACKAGE}FILES+= zpool_upgrade_009_neg.ksh
+${PACKAGE}FILES+= zpool_upgrade.kshlib
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zpool_upgrade_001_pos.ksh
+
+SUBDIR+= blockfiles
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/Makefile b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/Makefile
new file mode 100644
index 000000000000..2380e3be9e43
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/Makefile
@@ -0,0 +1,63 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles
+FILESDIR=${TESTSDIR}
+
+${PACKAGE}FILES+= README
+${PACKAGE}FILES+= zfs-broken-mirror1.dat.Z
+${PACKAGE}FILES+= zfs-broken-mirror2.dat.Z
+${PACKAGE}FILES+= zfs-pool-v1.dat.Z
+${PACKAGE}FILES+= zfs-pool-v10.dat.Z
+${PACKAGE}FILES+= zfs-pool-v11.dat.Z
+${PACKAGE}FILES+= zfs-pool-v12.dat.Z
+${PACKAGE}FILES+= zfs-pool-v13.dat.Z
+${PACKAGE}FILES+= zfs-pool-v14.dat.Z
+${PACKAGE}FILES+= zfs-pool-v15.dat.Z
+${PACKAGE}FILES+= zfs-pool-v1mirror1.dat.Z
+${PACKAGE}FILES+= zfs-pool-v1mirror2.dat.Z
+${PACKAGE}FILES+= zfs-pool-v1mirror3.dat.Z
+${PACKAGE}FILES+= zfs-pool-v1raidz1.dat.Z
+${PACKAGE}FILES+= zfs-pool-v1raidz2.dat.Z
+${PACKAGE}FILES+= zfs-pool-v1raidz3.dat.Z
+${PACKAGE}FILES+= zfs-pool-v1stripe1.dat.Z
+${PACKAGE}FILES+= zfs-pool-v1stripe2.dat.Z
+${PACKAGE}FILES+= zfs-pool-v1stripe3.dat.Z
+${PACKAGE}FILES+= zfs-pool-v2.dat.Z
+${PACKAGE}FILES+= zfs-pool-v28.dat.Z
+${PACKAGE}FILES+= zfs-pool-v2mirror1.dat.Z
+${PACKAGE}FILES+= zfs-pool-v2mirror2.dat.Z
+${PACKAGE}FILES+= zfs-pool-v2mirror3.dat.Z
+${PACKAGE}FILES+= zfs-pool-v2raidz1.dat.Z
+${PACKAGE}FILES+= zfs-pool-v2raidz2.dat.Z
+${PACKAGE}FILES+= zfs-pool-v2raidz3.dat.Z
+${PACKAGE}FILES+= zfs-pool-v2stripe1.dat.Z
+${PACKAGE}FILES+= zfs-pool-v2stripe2.dat.Z
+${PACKAGE}FILES+= zfs-pool-v2stripe3.dat.Z
+${PACKAGE}FILES+= zfs-pool-v3.dat.Z
+${PACKAGE}FILES+= zfs-pool-v3hotspare1.dat.Z
+${PACKAGE}FILES+= zfs-pool-v3hotspare2.dat.Z
+${PACKAGE}FILES+= zfs-pool-v3hotspare3.dat.Z
+${PACKAGE}FILES+= zfs-pool-v3mirror1.dat.Z
+${PACKAGE}FILES+= zfs-pool-v3mirror2.dat.Z
+${PACKAGE}FILES+= zfs-pool-v3mirror3.dat.Z
+${PACKAGE}FILES+= zfs-pool-v3raidz1.dat.Z
+${PACKAGE}FILES+= zfs-pool-v3raidz2.dat.Z
+${PACKAGE}FILES+= zfs-pool-v3raidz21.dat.Z
+${PACKAGE}FILES+= zfs-pool-v3raidz22.dat.Z
+${PACKAGE}FILES+= zfs-pool-v3raidz23.dat.Z
+${PACKAGE}FILES+= zfs-pool-v3raidz3.dat.Z
+${PACKAGE}FILES+= zfs-pool-v3stripe1.dat.Z
+${PACKAGE}FILES+= zfs-pool-v3stripe2.dat.Z
+${PACKAGE}FILES+= zfs-pool-v3stripe3.dat.Z
+${PACKAGE}FILES+= zfs-pool-v4.dat.Z
+${PACKAGE}FILES+= zfs-pool-v5.dat.Z
+${PACKAGE}FILES+= zfs-pool-v5000.dat.Z
+${PACKAGE}FILES+= zfs-pool-v6.dat.Z
+${PACKAGE}FILES+= zfs-pool-v7.dat.Z
+${PACKAGE}FILES+= zfs-pool-v8.dat.Z
+${PACKAGE}FILES+= zfs-pool-v9.dat.Z
+${PACKAGE}FILES+= zfs-pool-v999.dat.Z
+${PACKAGE}FILES+= zfs-pool-vBROKEN.dat.Z
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/README b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/README
new file mode 100644
index 000000000000..d5d7cf144b7e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/README
@@ -0,0 +1,29 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+Unless otherwise noted, all files in this distribution are released
+under the Common Development and Distribution License (CDDL).
+
+This directory contains compressed blockfiles for zpool upgrade testing.
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-broken-mirror1.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-broken-mirror1.dat.Z
new file mode 100644
index 000000000000..8e0b7ea48aa0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-broken-mirror1.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-broken-mirror2.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-broken-mirror2.dat.Z
new file mode 100644
index 000000000000..82eadecadc37
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-broken-mirror2.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1.dat.Z
new file mode 100644
index 000000000000..0b68f7edb529
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v10.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v10.dat.Z
new file mode 100644
index 000000000000..b2723de64c25
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v10.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v11.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v11.dat.Z
new file mode 100644
index 000000000000..67e928ad84e1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v11.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v12.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v12.dat.Z
new file mode 100644
index 000000000000..6877889bdffe
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v12.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v13.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v13.dat.Z
new file mode 100644
index 000000000000..3f99a092f4be
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v13.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v14.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v14.dat.Z
new file mode 100644
index 000000000000..964ebe70bd9e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v14.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v15.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v15.dat.Z
new file mode 100644
index 000000000000..f8d01534da4b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v15.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1mirror1.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1mirror1.dat.Z
new file mode 100644
index 000000000000..4ae51c47bbbf
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1mirror1.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1mirror2.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1mirror2.dat.Z
new file mode 100644
index 000000000000..4f36f67c106f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1mirror2.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1mirror3.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1mirror3.dat.Z
new file mode 100644
index 000000000000..363628e86f89
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1mirror3.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1raidz1.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1raidz1.dat.Z
new file mode 100644
index 000000000000..6290398f49da
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1raidz1.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1raidz2.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1raidz2.dat.Z
new file mode 100644
index 000000000000..0b4ce2cc23dc
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1raidz2.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1raidz3.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1raidz3.dat.Z
new file mode 100644
index 000000000000..9dbd653d0a45
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1raidz3.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1stripe1.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1stripe1.dat.Z
new file mode 100644
index 000000000000..60a161cab576
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1stripe1.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1stripe2.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1stripe2.dat.Z
new file mode 100644
index 000000000000..cc532872678a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1stripe2.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1stripe3.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1stripe3.dat.Z
new file mode 100644
index 000000000000..d34b27bb4dd4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1stripe3.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2.dat.Z
new file mode 100644
index 000000000000..bb34970a3d7a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v28.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v28.dat.Z
new file mode 100644
index 000000000000..ebdcbab1f146
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v28.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2mirror1.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2mirror1.dat.Z
new file mode 100644
index 000000000000..a2b9263cbd35
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2mirror1.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2mirror2.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2mirror2.dat.Z
new file mode 100644
index 000000000000..42696960bb2b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2mirror2.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2mirror3.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2mirror3.dat.Z
new file mode 100644
index 000000000000..b8a8ba6e6eed
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2mirror3.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2raidz1.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2raidz1.dat.Z
new file mode 100644
index 000000000000..bff2ecd10897
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2raidz1.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2raidz2.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2raidz2.dat.Z
new file mode 100644
index 000000000000..a703a7ca749c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2raidz2.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2raidz3.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2raidz3.dat.Z
new file mode 100644
index 000000000000..0e0fa0be7969
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2raidz3.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2stripe1.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2stripe1.dat.Z
new file mode 100644
index 000000000000..a06e279827f0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2stripe1.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2stripe2.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2stripe2.dat.Z
new file mode 100644
index 000000000000..ab9f4b4ca95c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2stripe2.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2stripe3.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2stripe3.dat.Z
new file mode 100644
index 000000000000..629bff8da0a0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v2stripe3.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3.dat.Z
new file mode 100644
index 000000000000..63a1882252f6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3hotspare1.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3hotspare1.dat.Z
new file mode 100644
index 000000000000..cd1c13828f00
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3hotspare1.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3hotspare2.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3hotspare2.dat.Z
new file mode 100644
index 000000000000..c4f7543e0af3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3hotspare2.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3hotspare3.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3hotspare3.dat.Z
new file mode 100644
index 000000000000..bf2cc65f8f52
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3hotspare3.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3mirror1.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3mirror1.dat.Z
new file mode 100644
index 000000000000..0616fcc145cb
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3mirror1.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3mirror2.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3mirror2.dat.Z
new file mode 100644
index 000000000000..ca21f8511d2b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3mirror2.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3mirror3.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3mirror3.dat.Z
new file mode 100644
index 000000000000..ba31451a5388
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3mirror3.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3raidz1.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3raidz1.dat.Z
new file mode 100644
index 000000000000..2ac5260bab39
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3raidz1.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3raidz2.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3raidz2.dat.Z
new file mode 100644
index 000000000000..631658e2f16b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3raidz2.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3raidz21.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3raidz21.dat.Z
new file mode 100644
index 000000000000..5957973a9409
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3raidz21.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3raidz22.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3raidz22.dat.Z
new file mode 100644
index 000000000000..77de3d686d69
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3raidz22.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3raidz23.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3raidz23.dat.Z
new file mode 100644
index 000000000000..9b956c0547f2
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3raidz23.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3raidz3.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3raidz3.dat.Z
new file mode 100644
index 000000000000..9f7716e41c6d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3raidz3.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3stripe1.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3stripe1.dat.Z
new file mode 100644
index 000000000000..53e5a7ab73da
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3stripe1.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3stripe2.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3stripe2.dat.Z
new file mode 100644
index 000000000000..7c57b69587e5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3stripe2.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3stripe3.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3stripe3.dat.Z
new file mode 100644
index 000000000000..fb09ae7b2add
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v3stripe3.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v4.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v4.dat.Z
new file mode 100644
index 000000000000..8a90b5f006f8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v4.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v5.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v5.dat.Z
new file mode 100644
index 000000000000..8715d2cb53b9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v5.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v5000.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v5000.dat.Z
new file mode 100644
index 000000000000..da4982db985b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v5000.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v6.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v6.dat.Z
new file mode 100644
index 000000000000..a476c091a367
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v6.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v7.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v7.dat.Z
new file mode 100644
index 000000000000..6d9f828e2611
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v7.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v8.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v8.dat.Z
new file mode 100644
index 000000000000..d983516e34e1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v8.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v9.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v9.dat.Z
new file mode 100644
index 000000000000..943b11e87a8e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v9.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v999.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v999.dat.Z
new file mode 100644
index 000000000000..c77c47b0fc60
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v999.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-vBROKEN.dat.Z b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-vBROKEN.dat.Z
new file mode 100644
index 000000000000..bc8d0c908a1a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-vBROKEN.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/cleanup.ksh
new file mode 100644
index 000000000000..6da9b7389884
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/cleanup.ksh
@@ -0,0 +1,49 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_upgrade/zpool_upgrade.kshlib
+
+function destroy_upgraded_pool {
+ for VERSION in $@
+ do
+ POOL_FILES=$($ENV | $GREP "ZPOOL_VERSION_${VERSION}_FILES"\
+ | $AWK -F= '{print $2}')
+ POOL_NAME=$($ENV | $GREP "ZPOOL_VERSION_${VERSION}_NAME"\
+ | $AWK -F= '{print $2}')
+ poolexists $POOL_NAME
+ if [ $? == 0 ]
+ then
+ log_must $ZPOOL destroy -f $POOL_NAME
+ fi
+
+ done
+}
+
+for config in $CONFIGS
+do
+ destroy_upgraded_pool $config
+done
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/create_upgrade_pool_dat.sh b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/create_upgrade_pool_dat.sh
new file mode 100755
index 000000000000..6248ddcbe919
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/create_upgrade_pool_dat.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+scriptpath=$(realpath $0)
+parent=$(dirname $scriptpath)
+blockfiles=${parent}/blockfiles
+
+version=$1
+if [ -z "$version" ]; then
+ echo "Must specify ZFS pool version"
+ exit 1
+fi
+
+# In case we need to test feature enabling?
+#avail_features=$(zpool upgrade -v | awk '/^[a-z]/ && !/^see the/ { print $1 }')
+
+zpool_opts=""
+# For v5000, the rest of the arguments are <feature>=<enabled|disabled>.
+if [ "$version" = "5000" ]; then
+ shift
+ for feature in $*; do
+ zpool_opts="$zpool_opts -o feature@${feature}"
+ done
+else
+ zpool_opts="-o version=${version}"
+fi
+
+dir=$(pwd)
+datfile=zfs-pool-v${version}.dat
+dat=${dir}/${datfile}
+poolname=v${version}-pool
+
+rm -f ${dat} ${dat}.Z
+set -e
+set -x
+dd if=/dev/zero of=${dat} bs=1M count=64
+zpool create ${zpool_opts} ${poolname} ${dat}
+zpool export ${poolname}
+compress ${dat}
+cp ${dat}.Z ${blockfiles}
+ls -l ${blockfiles}/${datfile}.Z
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/setup.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/setup.ksh
new file mode 100644
index 000000000000..e88b58c51157
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/setup.ksh
@@ -0,0 +1,38 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+# This should have been set by the .cfg script - verify it's set to something
+# (we check that something later on)
+if [ -z "$ZPOOL_VERSION" ]
+then
+ log_unresolved "Unable to determine ZFS Pool version of this machine"
+else
+ log_note "This machine is running ZFS version $ZPOOL_VERSION"
+fi
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade.cfg b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade.cfg
new file mode 100644
index 000000000000..c8bb10b7cc33
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade.cfg
@@ -0,0 +1,288 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
+. $STF_SUITE/include/libtest.kshlib
+
+export STF_TIMEOUT=1800
+
+# We need to determine which version of ZFS we're running on, so as to
+# work out which types of pool we should be able to upgrade on this
+# system.
+export ZPOOL_VERSION=$(get_zpool_version)
+
+
+# The following variable names describe files, stored as gzip compressed files
+# in the test directory which can be used to construct a pool of a given
+# version. The variable names are important, in that the construction
+# ZPOOL_VERSION_$var_FILES describes the files the pool is made from, and
+# ZPOOL_VERSION_$var_NAME describes the pool name.
+
+# Version 1 pools
+export ZPOOL_VERSION_1_FILES="zfs-pool-v1.dat"
+export ZPOOL_VERSION_1_NAME="v1-pool"
+# v1 stripe
+export ZPOOL_VERSION_1stripe_FILES="zfs-pool-v1stripe1.dat \
+zfs-pool-v1stripe2.dat zfs-pool-v1stripe3.dat"
+export ZPOOL_VERSION_1stripe_NAME="pool-v1stripe"
+# v1 raidz
+export ZPOOL_VERSION_1raidz_FILES="zfs-pool-v1raidz1.dat zfs-pool-v1raidz2.dat \
+zfs-pool-v1raidz3.dat"
+export ZPOOL_VERSION_1raidz_NAME="pool-v1raidz"
+# v1 mirror
+export ZPOOL_VERSION_1mirror_FILES="zfs-pool-v1mirror1.dat \
+zfs-pool-v1mirror2.dat zfs-pool-v1mirror3.dat"
+export ZPOOL_VERSION_1mirror_NAME="pool-v1mirror"
+
+
+# Version 2 pools
+export ZPOOL_VERSION_2_FILES="zfs-pool-v2.dat"
+export ZPOOL_VERSION_2_NAME="v2-pool"
+# v2 stripe
+export ZPOOL_VERSION_2stripe_FILES="zfs-pool-v2stripe1.dat \
+zfs-pool-v2stripe2.dat zfs-pool-v2stripe3.dat"
+export ZPOOL_VERSION_2stripe_NAME="pool-v2stripe"
+# v2 raidz
+export ZPOOL_VERSION_2raidz_FILES="zfs-pool-v2raidz1.dat zfs-pool-v2raidz2.dat \
+zfs-pool-v2raidz3.dat"
+export ZPOOL_VERSION_2raidz_NAME="pool-v2raidz"
+# v2 mirror
+export ZPOOL_VERSION_2mirror_FILES="zfs-pool-v2mirror1.dat \
+zfs-pool-v2mirror2.dat zfs-pool-v2mirror3.dat"
+export ZPOOL_VERSION_2mirror_NAME="pool-v2mirror"
+
+
+# This is a v3 pool
+export ZPOOL_VERSION_3_FILES="zfs-pool-v3.dat"
+export ZPOOL_VERSION_3_NAME="v3-pool"
+# v3 stripe
+export ZPOOL_VERSION_3stripe_FILES="zfs-pool-v3stripe1.dat \
+zfs-pool-v3stripe2.dat zfs-pool-v3stripe3.dat"
+export ZPOOL_VERSION_3stripe_NAME="pool-v3stripe"
+# v3 raidz
+export ZPOOL_VERSION_3raidz_FILES="zfs-pool-v3raidz1.dat zfs-pool-v3raidz2.dat \
+zfs-pool-v3raidz3.dat"
+export ZPOOL_VERSION_3raidz_NAME="pool-v3raidz"
+# v3 mirror
+export ZPOOL_VERSION_3mirror_FILES="zfs-pool-v3mirror1.dat \
+zfs-pool-v3mirror2.dat zfs-pool-v3mirror3.dat"
+export ZPOOL_VERSION_3mirror_NAME="pool-v3mirror"
+# v3 raidz2
+export ZPOOL_VERSION_3dblraidz_FILES="zfs-pool-v3raidz21.dat \
+zfs-pool-v3raidz22.dat zfs-pool-v3raidz23.dat"
+export ZPOOL_VERSION_3dblraidz_NAME="pool-v3raidz2"
+# v3 hotspares
+export ZPOOL_VERSION_3hotspare_FILES="zfs-pool-v3hotspare1.dat \
+zfs-pool-v3hotspare2.dat zfs-pool-v3hotspare3.dat"
+export ZPOOL_VERSION_3hotspare_NAME="pool-v3hotspare"
+
+# v4 pool
+export ZPOOL_VERSION_4_FILES="zfs-pool-v4.dat"
+export ZPOOL_VERSION_4_NAME="v4-pool"
+
+# v5 pool
+export ZPOOL_VERSION_5_FILES="zfs-pool-v5.dat"
+export ZPOOL_VERSION_5_NAME="v5-pool"
+
+# v6 pool
+export ZPOOL_VERSION_6_FILES="zfs-pool-v6.dat"
+export ZPOOL_VERSION_6_NAME="v6-pool"
+
+# v7 pool
+export ZPOOL_VERSION_7_FILES="zfs-pool-v7.dat"
+export ZPOOL_VERSION_7_NAME="v7-pool"
+
+# v8 pool
+export ZPOOL_VERSION_8_FILES="zfs-pool-v8.dat"
+export ZPOOL_VERSION_8_NAME="v8-pool"
+
+# v9 pool
+export ZPOOL_VERSION_9_FILES="zfs-pool-v9.dat"
+export ZPOOL_VERSION_9_NAME="v9-pool"
+
+# v10 pool
+export ZPOOL_VERSION_10_FILES="zfs-pool-v10.dat"
+export ZPOOL_VERSION_10_NAME="v10-pool"
+
+# v11 pool
+export ZPOOL_VERSION_11_FILES="zfs-pool-v11.dat"
+export ZPOOL_VERSION_11_NAME="v11-pool"
+
+# v12 pool
+export ZPOOL_VERSION_12_FILES="zfs-pool-v12.dat"
+export ZPOOL_VERSION_12_NAME="v12-pool"
+
+# v13 pool
+export ZPOOL_VERSION_13_FILES="zfs-pool-v13.dat"
+export ZPOOL_VERSION_13_NAME="v13-pool"
+
+# v14 pool
+export ZPOOL_VERSION_14_FILES="zfs-pool-v14.dat"
+export ZPOOL_VERSION_14_NAME="v14-pool"
+
+# v15 pool
+export ZPOOL_VERSION_15_FILES="zfs-pool-v15.dat"
+export ZPOOL_VERSION_15_NAME="v15-pool"
+
+# v28 pool
+export ZPOOL_VERSION_28_FILES="zfs-pool-v28.dat"
+export ZPOOL_VERSION_28_NAME="v28-pool"
+
+# v5000 pool
+export ZPOOL_VERSION_5000_FILES="zfs-pool-v5000.dat"
+export ZPOOL_VERSION_5000_NAME="v5000-pool"
+
+# This pool is a v2 pool, with device problems on one side of the mirror
+# so that the pool appears as DEGRADED
+export ZPOOL_VERSION_2brokenmirror_FILES="zfs-broken-mirror1.dat \
+zfs-broken-mirror2.dat"
+export ZPOOL_VERSION_2brokenmirror_NAME="zfs-broken-mirror"
+
+
+# This pool is a v999 pool (an unknown version) which can be used to check
+# whether upgrade, import or other tests that should fail against unknown
+# pool versions should fail. It should not be listed in the CONFIGS
+# variable below, as these are pool versions that can be imported and upgraded
+export ZPOOL_VERSION_9999_FILES="zfs-pool-v999.dat"
+export ZPOOL_VERSION_9999_NAME="v999-pool"
+
+
+# This statement builds up a list of configurations we should be able to
+# upgrade, for each pool version. Once we've built this variable, we'll
+# call the functions above for each value.
+case $ZPOOL_VERSION in
+1)
+ # we should be able to upgrade pools of version 1
+ CONFIGS="1 1stripe 1raidz 1mirror"
+ ;;
+
+2)
+ # we should be able to upgrade pools of version 1 & 2
+ CONFIGS="1 1stripe 1raidz 1mirror \
+ 2 2stripe 2raidz 2mirror 2brokenmirror"
+ ;;
+3)
+ # we should be able to upgrade pools of version 1, 2 & 3
+ CONFIGS="1 1stripe 1raidz 1mirror \
+ 2 2stripe 2raidz 2mirror 2brokenmirror \
+ 3 3stripe 3raidz 3mirror 3dblraidz 3hotspare"
+ ;;
+4)
+ # we should be able to upgrade pools of version 1, 2, 3 & 4
+ CONFIGS="1 1stripe 1raidz 1mirror \
+ 2 2stripe 2raidz 2mirror 2brokenmirror \
+ 3 3stripe 3raidz 3mirror 3dblraidz 3hotspare 4"
+ ;;
+5)
+ # we should be able to upgrade pools up to version 5
+ CONFIGS="1 1stripe 1raidz 1mirror \
+ 2 2stripe 2raidz 2mirror 2brokenmirror \
+ 3 3stripe 3raidz 3mirror 3dblraidz 3hotspare 4 5"
+ ;;
+6)
+ # we should be able to upgrade pools up to version 6
+ CONFIGS="1 1stripe 1raidz 1mirror \
+ 2 2stripe 2raidz 2mirror 2brokenmirror \
+ 3 3stripe 3raidz 3mirror 3dblraidz 3hotspare 4 5 6"
+ ;;
+7)
+ # we should be able to upgrade pools up to version 7
+ CONFIGS="1 1stripe 1raidz 1mirror \
+ 2 2stripe 2raidz 2mirror 2brokenmirror \
+ 3 3stripe 3raidz 3mirror 3dblraidz 3hotspare 4 5 6 7"
+ ;;
+8)
+ # we should be able to upgrade pools up to version 8
+ CONFIGS="1 1stripe 1raidz 1mirror \
+ 2 2stripe 2raidz 2mirror 2brokenmirror \
+ 3 3stripe 3raidz 3mirror 3dblraidz 3hotspare 4 5 6 7 8"
+ ;;
+9)
+ # we should be able to upgrade pools up to version 9
+ CONFIGS="1 1stripe 1raidz 1mirror \
+ 2 2stripe 2raidz 2mirror 2brokenmirror \
+ 3 3stripe 3raidz 3mirror 3dblraidz 3hotspare 4 5 6 7 8 9"
+ ;;
+10)
+ # we should be able to upgrade pools up to version 10
+ CONFIGS="1 1stripe 1raidz 1mirror \
+ 2 2stripe 2raidz 2mirror 2brokenmirror \
+ 3 3stripe 3raidz 3mirror 3dblraidz 3hotspare 4 5 6 7 8 9 10"
+ ;;
+11)
+ # we should be able to upgrade pools up to version 11
+ CONFIGS="1 1stripe 1raidz 1mirror \
+ 2 2stripe 2raidz 2mirror 2brokenmirror \
+ 3 3stripe 3raidz 3mirror 3dblraidz 3hotspare 4 5 6 7 8 9 10 11"
+ ;;
+12)
+ # we should be able to upgrade pools up to version 12
+ CONFIGS="1 1stripe 1raidz 1mirror \
+ 2 2stripe 2raidz 2mirror 2brokenmirror \
+ 3 3stripe 3raidz 3mirror 3dblraidz 3hotspare 4 5 6 7 8 9 10 11 12"
+ ;;
+13)
+ # we should be able to upgrade pools up to version 13
+ CONFIGS="1 1stripe 1raidz 1mirror \
+ 2 2stripe 2raidz 2mirror 2brokenmirror \
+ 3 3stripe 3raidz 3mirror 3dblraidz 3hotspare 4 5 6 7 8 9 10 11 12 13"
+ ;;
+14)
+ # we should be able to upgrade pools up to version 14
+ CONFIGS="1 1stripe 1raidz 1mirror \
+ 2 2stripe 2raidz 2mirror 2brokenmirror \
+ 3 3stripe 3raidz 3mirror 3dblraidz 3hotspare 4 5 6 7 8 9 10 11 12 13 14"
+ ;;
+15)
+ # we should be able to upgrade pools up to version 15
+ CONFIGS="1 1stripe 1raidz 1mirror \
+ 2 2stripe 2raidz 2mirror 2brokenmirror \
+ 3 3stripe 3raidz 3mirror 3dblraidz 3hotspare 4 5 6 7 8 9 10 11 12 13 14 15"
+ ;;
+28)
+ # we should be able to upgrade pools up to version 15
+ CONFIGS="1 1stripe 1raidz 1mirror \
+ 2 2stripe 2raidz 2mirror 2brokenmirror \
+ 3 3stripe 3raidz 3mirror 3dblraidz 3hotspare 4 5 6 7 8 9 10 11 12 13 14 15 28"
+ ;;
+5000)
+ # we should be able to upgrade pools up to version 15
+ CONFIGS="1 1stripe 1raidz 1mirror \
+ 2 2stripe 2raidz 2mirror 2brokenmirror \
+ 3 3stripe 3raidz 3mirror 3dblraidz 3hotspare 4 5 6 7 8 9 10 11 12 13 14 15 28 5000"
+ ;;
+*)
+ # we should be able to upgrade pools up to version 15
+ # but we should also log a note about the unknown pool version
+ CONFIGS="1 1stripe 1raidz 1mirror \
+ 2 2stripe 2raidz 2mirror 2brokenmirror \
+ 3 3stripe 3raidz 3mirror 3dblraidz 3hotspare 4 5 6 7 8 9 10 11 12 13 14 15"
+
+ log_note "Unknown ZFS version $ZPOOL_VERSION encountered:\
+ Test suite may need updating."
+ ;;
+esac
+export CONFIGS
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade.kshlib b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade.kshlib
new file mode 100644
index 000000000000..3fb9c3d4b092
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade.kshlib
@@ -0,0 +1,157 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+# This part of the test suite relies on variables being setup in the
+# zpool_upgrade.cfg script. Those variables give us details about which
+# files make up the pool, and what the pool name is.
+
+
+# A function to import a pool from files we have stored in the test suite
+# We import the pool, and create some random data in the pool.
+# $1 a version number we can use to get information about the pool
+function create_old_pool
+{
+ VERSION=$1
+ POOL_FILES=$($ENV | grep "ZPOOL_VERSION_${VERSION}_FILES"\
+ | $AWK -F= '{print $2}')
+ POOL_NAME=$($ENV|grep "ZPOOL_VERSION_${VERSION}_NAME"\
+ | $AWK -F= '{print $2}')
+
+ log_note "Creating $POOL_NAME from $POOL_FILES"
+ for pool_file in $POOL_FILES; do
+ $CP -f $STF_SUITE/tests/cli_root/zpool_upgrade/blockfiles/$pool_file.Z \
+ $TMPDIR
+ $UNCOMPRESS $TMPDIR/$pool_file.Z
+ done
+ log_must $ZPOOL import -d $TMPDIR $POOL_NAME
+
+ # Now put some random contents into the pool.
+ COUNT=0
+ while [ "$COUNT" -lt 1024 ]; do
+ $DD if=/dev/urandom of=/$POOL_NAME/random.$COUNT \
+ count=1 bs=1024 > /dev/null 2>&1
+ COUNT=$(( $COUNT + 1 ))
+ done
+}
+
+
+# A function to check the contents of a pool, upgrade it to the current version
+# and then verify that the data is consistent after upgrading. Note that we're
+# not using "zpool status -x" to see if the pool is healthy, as it's possible
+# to also upgrade faulted, or degraded pools.
+# $1 a version number we can use to get information about the pool
+function check_upgrade {
+ VERSION=$1
+ POOL_NAME=$($ENV| $GREP "ZPOOL_VERSION_${VERSION}_NAME"\
+ | $AWK -F= '{print $2}')
+ POOL_FILES=$($ENV | $GREP "ZPOOL_VERSION_${VERSION}_FILES"\
+ | $AWK -F= '{print $2}')
+
+ log_note "Checking if we can upgrade from ZFS version ${VERSION}."
+ PRE_UPGRADE_CHECKSUM=$(check_pool $POOL_NAME pre )
+ log_must $ZPOOL upgrade $POOL_NAME > /dev/null
+ POST_UPGRADE_CHECKSUM=$(check_pool $POOL_NAME post )
+
+ log_note "Checking that there are no differences between checksum output"
+ log_must $DIFF $PRE_UPGRADE_CHECKSUM $POST_UPGRADE_CHECKSUM
+ $RM $PRE_UPGRADE_CHECKSUM $POST_UPGRADE_CHECKSUM
+}
+
+# A function to destroy an upgraded pool, plus the files it was based on.
+# $1 a version number we can use to get information about the pool
+function destroy_upgraded_pool {
+ VERSION=$1
+ POOL_NAME=$($ENV|grep "ZPOOL_VERSION_${VERSION}_NAME"\
+ | $AWK -F= '{print $2}')
+ POOL_FILES=$($ENV | grep "ZPOOL_VERSION_${VERSION}_FILES"\
+ | $AWK -F= '{print $2}')
+ if poolexists "$POOL_NAME"; then
+ log_must $ZPOOL destroy $POOL_NAME
+ fi
+ for file in $POOL_FILES; do
+ if [ -e "$TMPDIR/$file" ]; then
+ $RM $TMPDIR/$file
+ fi
+ done
+}
+
+# This function does a basic sanity check on the pool by computing the
+# checksums of all files in the pool, printing the name of the file containing
+# the checksum results.
+# $1 the name of the pool
+# $2 a flag we can use to determine when this check is being performed
+# (ie. pre or post pool-upgrade)
+function check_pool { # pool state
+ POOL=$1
+ STATE=$2
+ $FIND /$POOL -type f -exec $CKSUM {} + > \
+ $TMPDIR/pool-checksums.$POOL.$STATE
+ print $TMPDIR/pool-checksums.$POOL.$STATE
+}
+
+# This function simply checks that a pool has a particular version number
+# as reported by zdb and zpool upgrade -v
+# $1 the name of the pool
+# $2 the version of the pool we expect to see
+function check_poolversion { # pool version
+
+ POOL=$1
+ VERSION=$2
+
+ # check version using zdb
+ ACTUAL=$(get_config $POOL version)
+ [ "$ACTUAL" != "$VERSION" ] && log_fail \
+ "ERROR: $POOL not upgraded: wanted '$VERSION', got '$ACTUAL'"
+
+ # check version using zpool upgrade
+ ACTUAL=$($ZPOOL upgrade | $GREP $POOL$ | \
+ $AWK '{print $1}' | $SED -e 's/ //g')
+ [ "$ACTUAL" != "$VERSION" ] &&
+ log_fail "$POOL reported version '$ACTUAL', expected '$VERSION'"
+}
+
+# A simple function to get a random number between two bounds
+# probably not the most efficient for large ranges, but it's okay.
+# Note since we're using $RANDOM, 32767 is the largest number we
+# can accept as the upper bound.
+# $1 lower bound
+# $2 upper bound
+function random { # min max
+
+ typeset MIN=$1
+ typeset MAX=$2
+ typeset RAND=0
+
+ while [ "$RAND" -lt "$MIN" ]
+ do
+ RAND=$(( $RANDOM % $MAX + 1))
+ done
+
+ print $RAND
+}
+
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_001_pos.ksh
new file mode 100644
index 000000000000..0713cd7b8ca7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_001_pos.ksh
@@ -0,0 +1,84 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_upgrade_001_pos
+#
+# DESCRIPTION:
+# Executing 'zpool upgrade -v' command succeeds, and also prints a description
+# of at least the current ZFS version.
+#
+# STRATEGY:
+# 1. Execute the command
+# 2. Verify a 0 exit status
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-07)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Executing 'zpool upgrade -v' command succeeds."
+
+log_must $ZPOOL upgrade -v
+
+# we also check that the usage message contains at least a description
+# of the current ZFS version.
+
+$ZPOOL upgrade -v > $TMPDIR/zpool-versions.${TESTCASE_ID}
+COUNT=$( $WC -l $TMPDIR/zpool-versions.${TESTCASE_ID} | $AWK '{print $1}' )
+COUNT=$(( $COUNT - 1 ))
+$TAIL -${COUNT} $TMPDIR/zpool-versions.${TESTCASE_ID} > $TMPDIR/zpool-versions-desc.${TESTCASE_ID}
+
+#
+# Current output for 'zpool upgrade -v' has different indent space
+# for single and double digit version number. For example,
+# 9 refquota and refreservation properties
+# 10 Cache devices
+#
+log_note "Checking to see we have a description for the current ZFS version."
+if (( ZPOOL_VERSION < 10 )); then
+ log_must $GREP "$ZPOOL_VERSION " $TMPDIR/zpool-versions-desc.${TESTCASE_ID}
+elif (( ZPOOL_VERSION >= 5000 )); then
+ log_must $GREP "The following features are supported" \
+ $TMPDIR/zpool-versions-desc.${TESTCASE_ID}
+else
+ log_must $GREP "$ZPOOL_VERSION " $TMPDIR/zpool-versions-desc.${TESTCASE_ID}
+fi
+$RM $TMPDIR/zpool-versions.${TESTCASE_ID}
+$RM $TMPDIR/zpool-versions-desc.${TESTCASE_ID}
+
+log_pass "Executing 'zpool upgrade -v' command succeeds."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_002_pos.ksh
new file mode 100644
index 000000000000..9996cba273b2
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_002_pos.ksh
@@ -0,0 +1,71 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_upgrade/zpool_upgrade.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_upgrade_002_pos
+#
+# DESCRIPTION:
+# import pools of all versions - zpool upgrade on each pools works
+#
+# STRATEGY:
+# 1. Execute the command with several invalid options
+# 2. Verify a 0 exit status for each
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-07)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ destroy_upgraded_pool $config
+}
+
+log_assert "Import pools of all versions - zpool upgrade on each pools works"
+log_onexit cleanup
+
+# $CONFIGS gets set in the .cfg script
+for config in $CONFIGS
+do
+ create_old_pool $config
+ check_upgrade $config
+ destroy_upgraded_pool $config
+done
+
+log_pass "Import pools of all versions - zpool upgrade on each pools works"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_003_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_003_pos.ksh
new file mode 100644
index 000000000000..35ab5fb17488
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_003_pos.ksh
@@ -0,0 +1,69 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_upgrade/zpool_upgrade.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_upgrade_003_pos
+#
+# DESCRIPTION:
+# Upgrading a pool that has already been upgraded succeeds.
+#
+# STRATEGY:
+# 1. Upgrade a pool, then try to upgrade it again
+# 2. Verify a 0 exit status
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-07)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ destroy_upgraded_pool 1
+}
+
+log_assert "Upgrading a pool that has already been upgraded succeeds."
+log_onexit cleanup
+
+# we just create a version 1 pool here
+create_old_pool 1
+check_upgrade 1
+check_upgrade 1
+destroy_upgraded_pool 1
+
+log_pass "Upgrading a pool that has already been upgraded succeeds."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_004_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_004_pos.ksh
new file mode 100644
index 000000000000..5c8480f2e883
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_004_pos.ksh
@@ -0,0 +1,98 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_upgrade/zpool_upgrade.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_upgrade_004_pos
+#
+# DESCRIPTION:
+# zpool upgrade -a works
+#
+# STRATEGY:
+# 1. Create all upgradable pools for this system, then upgrade -a
+# 2. Verify a 0 exit status
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-07)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ for config in $CONFIGS ; do
+ destroy_upgraded_pool $config
+ done
+}
+
+log_assert "zpool upgrade -a works"
+
+log_onexit cleanup
+
+# Now build all of our pools
+for config in $CONFIGS
+do
+ POOL_NAME=$($ENV | $GREP "ZPOOL_VERSION_${config}_NAME"\
+ | $AWK -F= '{print $2}')
+
+ create_old_pool $config
+ # a side effect of the check_pool here, is that we get a checksum written
+ # called $TMPDIR/pool-checksums.$POOL.pre
+ check_pool $POOL_NAME pre > /dev/null
+done
+
+# upgrade them all at once
+log_must $ZPOOL upgrade -a > /dev/null
+
+# verify their contents then destroy them
+for config in $CONFIGS
+do
+ POOL_NAME=$($ENV | $GREP "ZPOOL_VERSION_${config}_NAME"\
+ | $AWK -F= '{print $2}')
+
+ check_pool $POOL_NAME post > /dev/null
+
+ # a side effect of the check_pool here, is that we get a checksum written
+ # called $TMPDIR/pool-checksums.$POOL_NAME.post
+ log_must $DIFF $TMPDIR/pool-checksums.$POOL_NAME.pre \
+ $TMPDIR/pool-checksums.$POOL_NAME.post
+
+ $RM $TMPDIR/pool-checksums.$POOL_NAME.pre $TMPDIR/pool-checksums.$POOL_NAME.post
+ destroy_upgraded_pool $config
+done
+
+log_pass "zpool upgrade -a works"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_005_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_005_neg.ksh
new file mode 100644
index 000000000000..73c37ce20022
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_005_neg.ksh
@@ -0,0 +1,68 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_upgrade_005_neg
+#
+# DESCRIPTION:
+# Variations of upgrade -v print usage message, return with non-zero status
+#
+# STRATEGY:
+# 1. Execute the command with several invalid options
+# 2. Verify a 0 exit status for each
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-07)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+set -A args "/tmp" "-?" "-va" "-v fakepool" "-a fakepool"
+
+log_assert "Variations of upgrade -v print usage message, \
+ return with non-zero status"
+
+typeset -i i=0
+
+while [[ $i -lt ${#args[*]} ]]; do
+
+ log_mustnot $ZPOOL upgrade ${args[$i]} > /dev/null
+
+ (( i = i + 1 ))
+done
+
+log_pass "Variations of upgrade -v print usage message, \
+ return with non-zero status"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_006_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_006_neg.ksh
new file mode 100644
index 000000000000..0e4b24cad392
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_006_neg.ksh
@@ -0,0 +1,68 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_upgrade_006_neg
+#
+# DESCRIPTION:
+# Attempting to upgrade a non-existent pool will return an error
+#
+# STRATEGY:
+# 1. Verify a pool doesn't exist, then try to upgrade it
+# 2. Verify a 0 exit status
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-07)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+log_assert "Attempting to upgrade a non-existent pool will return an error"
+NO_POOL=notapool
+FOUND=""
+
+while [ -z "$FOUND" ]
+do
+ $ZPOOL list $NO_POOL 2>&1 > /dev/null
+ if [ $? -ne 0 ]
+ then
+ FOUND="true"
+ log_mustnot $ZPOOL upgrade $NO_POOL
+ else
+ NO_POOL="${NO_POOL}x"
+ fi
+done
+
+log_pass "Attempting to upgrade a non-existent pool will return an error"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_007_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_007_pos.ksh
new file mode 100644
index 000000000000..2caf3a726c61
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_007_pos.ksh
@@ -0,0 +1,83 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_upgrade/zpool_upgrade.kshlib
+. $STF_SUITE/tests/cli_root/zfs_upgrade/zfs_upgrade.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_upgrade_007_pos
+#
+# DESCRIPTION:
+# import pools of all versions - verify the following operation not break.
+# * zfs create -o version=<vers> <filesystem>
+# * zfs upgrade [-V vers] <filesystem>
+# * zfs set version=<vers> <filesystem>
+#
+# STRATEGY:
+# 1. Import pools of all versions
+# 2. Setup a test enviorment over the old pools.
+# 3. Verify the commands related to 'zfs upgrade' succeed as expected.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-28)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+if ! fs_prop_exist "version" ; then
+ log_unsupported "version is not supported by this release."
+fi
+
+function cleanup
+{
+ destroy_upgraded_pool $config
+}
+
+log_assert "Import pools of all versions - 'zfs upgrade' on each pools works"
+log_onexit cleanup
+
+# $CONFIGS gets set in the .cfg script
+for config in $CONFIGS
+do
+ create_old_pool $config
+ pool=$($ENV| $GREP "ZPOOL_VERSION_${config}_NAME" \
+ | $AWK -F= '{print $2}')
+
+ default_check_zfs_upgrade $pool
+ destroy_upgraded_pool $config
+done
+
+log_pass "Import pools of all versions - 'zfs upgrade' on each pools works"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_008_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_008_pos.ksh
new file mode 100644
index 000000000000..8332681d6e1d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_008_pos.ksh
@@ -0,0 +1,94 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_upgrade/zpool_upgrade.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_upgrade_008_pos
+#
+# DESCRIPTION:
+#
+# Zpool upgrade should be able to upgrade pools to a given version using -V
+#
+# STRATEGY:
+# 1. For all versions pools that can be upgraded on a given OS version
+# (latest pool version - 1)
+# 2. Pick a version that's a random number, greater than the version
+# we're running.
+# 3. Attempt to upgrade that pool to the given version
+# 4. Check the pool was upgraded correctly.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-09-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ destroy_upgraded_pool $config
+}
+
+log_assert \
+ "Zpool upgrade should be able to upgrade pools to a given version using -V"
+
+$ZPOOL upgrade --help 2>&1 | $GREP "V version" > /dev/null
+if [ $? -ne 0 ]
+then
+ log_unsupported "Zpool upgrade -V not supported on this release."
+fi
+log_onexit cleanup
+
+# We're just using the single disk version of the pool, which should be
+# enough to determine if upgrade works correctly. Also set a MAX_VER
+# variable, which specifies the highest version that we should expect
+# a zpool upgrade operation to succeed from. (latest version - 1)
+CONFIGS="1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 28"
+MAX_VER=28
+
+for config in $CONFIGS
+do
+ create_old_pool $config
+ pool=$($ENV| $GREP "ZPOOL_VERSION_${config}_NAME" \
+ | $AWK -F= '{print $2}')
+ NEXT=$(random $config $MAX_VER)
+ log_must $ZPOOL upgrade -V $NEXT $pool
+ check_poolversion $pool $NEXT
+ destroy_upgraded_pool $config
+done
+
+log_pass \
+ "Zpool upgrade should be able to upgrade pools to a given version using -V"
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_009_neg.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_009_neg.ksh
new file mode 100644
index 000000000000..954dde300cc4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_009_neg.ksh
@@ -0,0 +1,87 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_upgrade/zpool_upgrade.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_upgrade_009_neg
+#
+# DESCRIPTION:
+#
+# Zpool upgrade -V shouldn't be able to upgrade a pool to an unknown version
+#
+# STRATEGY:
+# 1. Take an existing pool
+# 2. Attempt to upgrade it to an unknown version
+# 3. Verify that the upgrade failed, and the pool version was still the original
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-09-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ destroy_upgraded_pool $config
+}
+
+log_assert \
+"Zpool upgrade -V shouldn't be able to upgrade a pool to an unknown version"
+
+$ZPOOL upgrade --help 2>&1 | $GREP "V version" > /dev/null
+if [ $? -ne 0 ]
+then
+ log_unsupported "Zpool upgrade -V not supported on this release."
+fi
+log_onexit cleanup
+
+# Create a version 2 pool
+typeset -i config=2
+create_old_pool $config
+pool=$($ENV| $GREP "ZPOOL_VERSION_${config}_NAME" | $AWK -F= '{print $2}')
+
+# Attempt to upgrade it
+log_mustnot $ZPOOL upgrade -V 999 $pool
+log_mustnot $ZPOOL upgrade -V 999
+
+# Verify we're still on the old version
+check_poolversion $pool $config
+destroy_upgraded_pool $config
+
+log_pass \
+ "Zpool upgrade -V shouldn't be able to upgrade a pool to an unknown version"
+
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_test.sh
new file mode 100755
index 000000000000..f20297a0cfb8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_upgrade/zpool_upgrade_test.sh
@@ -0,0 +1,287 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zpool_upgrade_001_pos cleanup
+zpool_upgrade_001_pos_head()
+{
+ atf_set "descr" "Executing 'zpool upgrade -v' command succeeds."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1800
+}
+zpool_upgrade_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_upgrade.kshlib
+ . $(atf_get_srcdir)/zpool_upgrade.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_upgrade_001_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_upgrade_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_upgrade.kshlib
+ . $(atf_get_srcdir)/zpool_upgrade.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_upgrade_002_pos cleanup
+zpool_upgrade_002_pos_head()
+{
+ atf_set "descr" "Import pools of all versions - zpool upgrade on each pools works"
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1800
+}
+zpool_upgrade_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_upgrade.kshlib
+ . $(atf_get_srcdir)/zpool_upgrade.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_upgrade_002_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_upgrade_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_upgrade.kshlib
+ . $(atf_get_srcdir)/zpool_upgrade.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_upgrade_003_pos cleanup
+zpool_upgrade_003_pos_head()
+{
+ atf_set "descr" "Upgrading a pool that has already been upgraded succeeds."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1800
+}
+zpool_upgrade_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_upgrade.kshlib
+ . $(atf_get_srcdir)/zpool_upgrade.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_upgrade_003_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_upgrade_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_upgrade.kshlib
+ . $(atf_get_srcdir)/zpool_upgrade.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_upgrade_004_pos cleanup
+zpool_upgrade_004_pos_head()
+{
+ atf_set "descr" "zpool upgrade -a works"
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1800
+}
+zpool_upgrade_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_upgrade.kshlib
+ . $(atf_get_srcdir)/zpool_upgrade.cfg
+
+ verify_disk_count "$DISKS" 2
+ if other_pools_exist; then
+ atf_skip "Can't test unmount -a with existing pools"
+ fi
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_upgrade_004_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_upgrade_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_upgrade.kshlib
+ . $(atf_get_srcdir)/zpool_upgrade.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_upgrade_005_neg cleanup
+zpool_upgrade_005_neg_head()
+{
+ atf_set "descr" "Variations of upgrade -v print usage message,return with non-zero status"
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1800
+}
+zpool_upgrade_005_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_upgrade.kshlib
+ . $(atf_get_srcdir)/zpool_upgrade.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_upgrade_005_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_upgrade_005_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_upgrade.kshlib
+ . $(atf_get_srcdir)/zpool_upgrade.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_upgrade_006_neg cleanup
+zpool_upgrade_006_neg_head()
+{
+ atf_set "descr" "Attempting to upgrade a non-existent pool will return an error"
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1800
+}
+zpool_upgrade_006_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_upgrade.kshlib
+ . $(atf_get_srcdir)/zpool_upgrade.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_upgrade_006_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_upgrade_006_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_upgrade.kshlib
+ . $(atf_get_srcdir)/zpool_upgrade.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_upgrade_007_pos cleanup
+zpool_upgrade_007_pos_head()
+{
+ atf_set "descr" "Import pools of all versions - 'zfs upgrade' on each pools works"
+ atf_set "require.progs" "ksh93 zpool"
+ # This test can take quite a while, especially on debug builds
+ atf_set "timeout" 7200
+}
+zpool_upgrade_007_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_upgrade.kshlib
+ . $(atf_get_srcdir)/zpool_upgrade.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_upgrade_007_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_upgrade_007_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_upgrade.kshlib
+ . $(atf_get_srcdir)/zpool_upgrade.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_upgrade_008_pos cleanup
+zpool_upgrade_008_pos_head()
+{
+ atf_set "descr" "Zpool upgrade should be able to upgrade pools to a given version using -V"
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1800
+}
+zpool_upgrade_008_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_upgrade.kshlib
+ . $(atf_get_srcdir)/zpool_upgrade.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_upgrade_008_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_upgrade_008_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_upgrade.kshlib
+ . $(atf_get_srcdir)/zpool_upgrade.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_upgrade_009_neg cleanup
+zpool_upgrade_009_neg_head()
+{
+ atf_set "descr" "Zpool upgrade -V shouldn't be able to upgrade a pool to an unknown version"
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1800
+}
+zpool_upgrade_009_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_upgrade.kshlib
+ . $(atf_get_srcdir)/zpool_upgrade.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_upgrade_009_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_upgrade_009_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_upgrade.kshlib
+ . $(atf_get_srcdir)/zpool_upgrade.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zpool_upgrade_001_pos
+ atf_add_test_case zpool_upgrade_002_pos
+ atf_add_test_case zpool_upgrade_003_pos
+ atf_add_test_case zpool_upgrade_004_pos
+ atf_add_test_case zpool_upgrade_005_neg
+ atf_add_test_case zpool_upgrade_006_neg
+ atf_add_test_case zpool_upgrade_007_pos
+ atf_add_test_case zpool_upgrade_008_pos
+ atf_add_test_case zpool_upgrade_009_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_user/Makefile b/tests/sys/cddl/zfs/tests/cli_user/Makefile
new file mode 100644
index 000000000000..947337d58367
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/Makefile
@@ -0,0 +1,17 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_user
+FILESDIR=${TESTSDIR}
+
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= cli.cfg
+${PACKAGE}FILES+= cli_user.kshlib
+
+TESTS_SUBDIRS+= zpool_iostat
+TESTS_SUBDIRS+= zpool_list
+TESTS_SUBDIRS+= zfs_list
+TESTS_SUBDIRS+= misc
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_user/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_user/cleanup.ksh
new file mode 100644
index 000000000000..713cb16bb31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_user/cli.cfg b/tests/sys/cddl/zfs/tests/cli_user/cli.cfg
new file mode 100644
index 000000000000..179242f92266
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/cli.cfg
@@ -0,0 +1,31 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export TESTSNAP=testsnap${TESTCASE_ID}
+export TESTCLCT=testclct${TESTCASE_ID}
+export TESTFILE0=testfile0.${TESTCASE_ID}
+export TESTFILE1=testfile1.${TESTCASE_ID}
+export UNPRIVILEGED_USER=`atf_config_get unprivileged_user`
diff --git a/tests/sys/cddl/zfs/tests/cli_user/cli_user.kshlib b/tests/sys/cddl/zfs/tests/cli_user/cli_user.kshlib
new file mode 100644
index 000000000000..df84cf0f4e7c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/cli_user.kshlib
@@ -0,0 +1,33 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2017 Spectra Logic Corp. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+# Executes $@ without root privileges
+function run_unprivileged
+{
+ echo "$@" | su -m "$UNPRIVILEGED_USER"
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/Makefile b/tests/sys/cddl/zfs/tests/cli_user/misc/Makefile
new file mode 100644
index 000000000000..d22caa7382c5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/Makefile
@@ -0,0 +1,57 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_user/misc
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= misc_test
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zfs_rename_001_neg.ksh
+${PACKAGE}FILES+= zpool_remove_001_neg.ksh
+${PACKAGE}FILES+= zfs_destroy_001_neg.ksh
+${PACKAGE}FILES+= zpool_import_001_neg.ksh
+${PACKAGE}FILES+= zfs_unshare_001_neg.ksh
+${PACKAGE}FILES+= zpool_export_001_neg.ksh
+${PACKAGE}FILES+= zfs_set_001_neg.ksh
+${PACKAGE}FILES+= zpool_upgrade_001_neg.ksh
+${PACKAGE}FILES+= zpool_replace_001_neg.ksh
+${PACKAGE}FILES+= misc.cfg
+${PACKAGE}FILES+= zfs_receive_001_neg.ksh
+${PACKAGE}FILES+= zpool_online_001_neg.ksh
+${PACKAGE}FILES+= zpool_history_001_neg.ksh
+${PACKAGE}FILES+= zpool_create_001_neg.ksh
+${PACKAGE}FILES+= zpool_create_002_pos.ksh
+${PACKAGE}FILES+= zfs_promote_001_neg.ksh
+${PACKAGE}FILES+= zfs_get_001_neg.ksh
+${PACKAGE}FILES+= zfs_share_001_neg.ksh
+${PACKAGE}FILES+= zfs_rollback_001_neg.ksh
+${PACKAGE}FILES+= zfs_unallow_001_neg.ksh
+${PACKAGE}FILES+= zfs_send_001_neg.ksh
+${PACKAGE}FILES+= zpool_clear_001_neg.ksh
+${PACKAGE}FILES+= zpool_add_001_neg.ksh
+${PACKAGE}FILES+= zpool_add_002_pos.ksh
+${PACKAGE}FILES+= zpool_import_002_neg.ksh
+${PACKAGE}FILES+= zpool_set_001_neg.ksh
+${PACKAGE}FILES+= zfs_inherit_001_neg.ksh
+${PACKAGE}FILES+= zfs_unmount_001_neg.ksh
+${PACKAGE}FILES+= zpool_destroy_001_neg.ksh
+${PACKAGE}FILES+= zpool_detach_001_neg.ksh
+${PACKAGE}FILES+= zfs_create_001_neg.ksh
+${PACKAGE}FILES+= zpool_attach_001_neg.ksh
+${PACKAGE}FILES+= zpool_001_neg.ksh
+${PACKAGE}FILES+= zdb_001_neg.ksh
+${PACKAGE}FILES+= zfs_001_neg.ksh
+${PACKAGE}FILES+= zfs_upgrade_001_neg.ksh
+${PACKAGE}FILES+= zpool_scrub_001_neg.ksh
+${PACKAGE}FILES+= zfs_mount_001_neg.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zpool_get_001_neg.ksh
+${PACKAGE}FILES+= zfs_allow_001_neg.ksh
+${PACKAGE}FILES+= zpool_offline_001_neg.ksh
+${PACKAGE}FILES+= zfs_clone_001_neg.ksh
+${PACKAGE}FILES+= zpool_status_001_neg.ksh
+${PACKAGE}FILES+= zfs_snapshot_001_neg.ksh
+${PACKAGE}FILES+= setup.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/cleanup.ksh
new file mode 100644
index 000000000000..43d1451c71af
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/cleanup.ksh
@@ -0,0 +1,37 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+destroy_pool $TESTPOOL.virt
+destroy_pool v1-pool
+
+if [[ -f $TMPDIR/zfstest_datastream.dat ]]
+then
+ log_must $RM -f $TMPDIR/zfstest_datastream.dat
+fi
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/misc.cfg b/tests/sys/cddl/zfs/tests/cli_user/misc/misc.cfg
new file mode 100644
index 000000000000..8586c606f0e4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/misc.cfg
@@ -0,0 +1,67 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_user/cli.cfg
+
+
+# these are the set of setable ZFS properties
+PROP_NAMES="\
+ aclinherit aclmode atime \
+ checksum compression devices \
+ exec mountpoint quota readonly \
+ recordsize reservation setuid shareiscsi \
+ sharenfs snapdir "
+
+# these are a set of values we apply, for use when testing the
+# zfs get/set subcommands - ordered as per the list above so we
+# can iterate over both sets in an array
+PROP_VALS="\
+ secure discard on \
+ fletcher2 on on \
+ on legacy none on \
+ 128k none on on \
+ on visible "
+
+# these are an alternate set of property values
+PROP_ALTVALS="\
+ noallow groupmask off \
+ fletcher4 lzjb off \
+ off $TMPDIR/zfstest 100m off \
+ 512 10m off off \
+ off hidden "
+
+
+
+# additional properties to worry about: canmount copies xattr zoned version
+
+POOL_PROPS="\
+ bootfs autoreplace"
+
+POOL_VALS="\
+ $TESTPOOL/$TESTFS on"
+
+POOL_ALTVALS="\
+ $TESTPOOL/$TESTFS/$TESTFS2 off"
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/misc_test.sh b/tests/sys/cddl/zfs/tests/cli_user/misc/misc_test.sh
new file mode 100755
index 000000000000..76f42cb45856
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/misc_test.sh
@@ -0,0 +1,1172 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zdb_001_neg cleanup
+zdb_001_neg_head()
+{
+ atf_set "descr" "zdb can't run as a user on datasets, but can run without arguments"
+ atf_set "require.progs" "ksh93 zfs fgrep zpool zdb"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zdb_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zdb_001_neg.ksh || atf_fail "Testcase failed"
+}
+zdb_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_001_neg cleanup
+zfs_001_neg_head()
+{
+ atf_set "descr" "zfs shows a usage message when run as a user"
+ atf_set "require.progs" "ksh93 zfs fgrep zpool"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zfs_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_001_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_allow_001_neg cleanup
+zfs_allow_001_neg_head()
+{
+ atf_set "descr" "zfs allow returns an error when run as a user"
+ atf_set "require.progs" "ksh93 zfs fgrep logname zpool"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zfs_allow_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_allow_001_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_allow_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_clone_001_neg cleanup
+zfs_clone_001_neg_head()
+{
+ atf_set "descr" "zfs clone returns an error when run as a user"
+ atf_set "require.progs" "ksh93 zfs fgrep zpool"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zfs_clone_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_clone_001_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_clone_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_create_001_neg cleanup
+zfs_create_001_neg_head()
+{
+ atf_set "descr" "Verify zfs create without parameters fails."
+ atf_set "require.progs" "ksh93 zfs fgrep zpool"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zfs_create_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_create_001_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_create_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_destroy_001_neg cleanup
+zfs_destroy_001_neg_head()
+{
+ atf_set "descr" "zfs destroy [-f|-r] [fs|snap]"
+ atf_set "require.progs" "ksh93 zfs fgrep zpool"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zfs_destroy_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_destroy_001_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_destroy_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_get_001_neg cleanup
+zfs_get_001_neg_head()
+{
+ atf_set "descr" "zfs get works when run as a user"
+ atf_set "require.progs" "ksh93 zfs fgrep zpool"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zfs_get_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_get_001_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_get_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_inherit_001_neg cleanup
+zfs_inherit_001_neg_head()
+{
+ atf_set "descr" "zfs inherit returns an error when run as a user"
+ atf_set "require.progs" "ksh93 zfs fgrep zpool"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zfs_inherit_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_inherit_001_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_inherit_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_mount_001_neg cleanup
+zfs_mount_001_neg_head()
+{
+ atf_set "descr" "zfs mount returns an error when run as a user"
+ atf_set "require.progs" "ksh93 zfs fgrep zpool"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zfs_mount_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_mount_001_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_mount_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_promote_001_neg cleanup
+zfs_promote_001_neg_head()
+{
+ atf_set "descr" "zfs promote returns an error when run as a user"
+ atf_set "require.progs" "ksh93 zfs fgrep zpool"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zfs_promote_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_promote_001_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_promote_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_receive_001_neg cleanup
+zfs_receive_001_neg_head()
+{
+ atf_set "descr" "zfs receive returns an error when run as a user"
+ atf_set "require.progs" "ksh93 zfs fgrep zpool"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zfs_receive_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_receive_001_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_receive_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_rename_001_neg cleanup
+zfs_rename_001_neg_head()
+{
+ atf_set "descr" "zfs rename returns an error when run as a user"
+ atf_set "require.progs" "ksh93 zfs fgrep zpool"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zfs_rename_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_rename_001_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_rename_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_rollback_001_neg cleanup
+zfs_rollback_001_neg_head()
+{
+ atf_set "descr" "zfs rollback returns an error when run as a user"
+ atf_set "require.progs" "ksh93 zfs fgrep zpool"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zfs_rollback_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_rollback_001_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_rollback_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_send_001_neg cleanup
+zfs_send_001_neg_head()
+{
+ atf_set "descr" "zfs send returns an error when run as a user"
+ atf_set "require.progs" "ksh93 zfs fgrep zpool"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zfs_send_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_send_001_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_send_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_set_001_neg cleanup
+zfs_set_001_neg_head()
+{
+ atf_set "descr" "zfs set returns an error when run as a user"
+ atf_set "require.progs" "ksh93 zfs fgrep zpool"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zfs_set_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_set_001_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_set_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_share_001_neg cleanup
+zfs_share_001_neg_head()
+{
+ atf_set "descr" "zfs share returns an error when run as a user"
+ atf_set "require.progs" "ksh93 zfs fgrep zpool"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zfs_share_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_share_001_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_share_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_snapshot_001_neg cleanup
+zfs_snapshot_001_neg_head()
+{
+ atf_set "descr" "zfs snapshot returns an error when run as a user"
+ atf_set "require.progs" "ksh93 zfs fgrep zpool"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zfs_snapshot_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_snapshot_001_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_snapshot_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_unallow_001_neg cleanup
+zfs_unallow_001_neg_head()
+{
+ atf_set "descr" "zfs unallow returns an error when run as a user"
+ atf_set "require.progs" "ksh93 zfs fgrep zpool"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zfs_unallow_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_unallow_001_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_unallow_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_unmount_001_neg cleanup
+zfs_unmount_001_neg_head()
+{
+ atf_set "descr" "zfs u[n]mount [-f] [mountpoint|fs|snap]"
+ atf_set "require.progs" "ksh93 zfs fgrep zpool"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zfs_unmount_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_unmount_001_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_unmount_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_unshare_001_neg cleanup
+zfs_unshare_001_neg_head()
+{
+ atf_set "descr" "zfs unshare returns an error when run as a user"
+ atf_set "require.progs" "ksh93 zfs fgrep share zpool"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zfs_unshare_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_unshare_001_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_unshare_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_upgrade_001_neg cleanup
+zfs_upgrade_001_neg_head()
+{
+ atf_set "descr" "zfs upgrade returns an error when run as a user"
+ atf_set "require.progs" "ksh93 zfs fgrep zpool"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zfs_upgrade_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_upgrade_001_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_upgrade_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_001_neg cleanup
+zpool_001_neg_head()
+{
+ atf_set "descr" "zpool shows a usage message when run as a user"
+ atf_set "require.progs" "ksh93 zfs zpool fgrep"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zpool_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_001_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_add_001_neg cleanup
+zpool_add_001_neg_head()
+{
+ atf_set "descr" "zpool add [-fn] pool_name vdev"
+ atf_set "require.progs" "ksh93 zfs zpool fgrep"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zpool_add_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_add_001_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_add_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_add_002_pos cleanup
+zpool_add_002_pos_head()
+{
+ atf_set "descr" "zpool add [-f] -n succeeds for unpriveleged users"
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zpool_add_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_add_002_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_add_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_attach_001_neg cleanup
+zpool_attach_001_neg_head()
+{
+ atf_set "descr" "zpool attach returns an error when run as a user"
+ atf_set "require.progs" "ksh93 zfs zpool fgrep"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zpool_attach_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_attach_001_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_attach_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_clear_001_neg cleanup
+zpool_clear_001_neg_head()
+{
+ atf_set "descr" "zpool clear returns an error when run as a user"
+ atf_set "require.progs" "ksh93 zfs zpool fgrep"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zpool_clear_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_clear_001_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_clear_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_create_001_neg
+zpool_create_001_neg_head()
+{
+ atf_set "descr" "zpool create [-f] fails for unpriveleged users"
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "require.user" unprivileged
+}
+zpool_create_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/zpool_create_001_neg.ksh || atf_fail "Testcase failed"
+}
+
+
+atf_test_case zpool_create_002_pos
+zpool_create_002_pos_head()
+{
+ atf_set "descr" "zpool create [-f] -n succeeds for unpriveleged users"
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "require.user" unprivileged
+}
+zpool_create_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/zpool_create_002_pos.ksh || atf_fail "Testcase failed"
+}
+
+
+atf_test_case zpool_destroy_001_neg cleanup
+zpool_destroy_001_neg_head()
+{
+ atf_set "descr" "zpool destroy [-f] [pool_name ...]"
+ atf_set "require.progs" "ksh93 zfs zpool fgrep"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zpool_destroy_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_destroy_001_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_destroy_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_detach_001_neg cleanup
+zpool_detach_001_neg_head()
+{
+ atf_set "descr" "zpool detach returns an error when run as a user"
+ atf_set "require.progs" "ksh93 zfs zpool fgrep"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zpool_detach_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_detach_001_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_detach_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_export_001_neg cleanup
+zpool_export_001_neg_head()
+{
+ atf_set "descr" "zpool export returns an error when run as a user"
+ atf_set "require.progs" "ksh93 zfs zpool fgrep"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zpool_export_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_export_001_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_export_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_get_001_neg cleanup
+zpool_get_001_neg_head()
+{
+ atf_set "descr" "zpool get works when run as a user"
+ atf_set "require.progs" "ksh93 zfs zpool fgrep"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zpool_get_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_get_001_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_get_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_history_001_neg cleanup
+zpool_history_001_neg_head()
+{
+ atf_set "descr" "zpool history returns an error when run as a user"
+ atf_set "require.progs" "ksh93 zfs zpool fgrep"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zpool_history_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_history_001_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_history_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_import_001_neg cleanup
+zpool_import_001_neg_head()
+{
+ atf_set "descr" "zpool import returns an error when run as a user"
+ atf_set "require.progs" "ksh93 zfs zpool fgrep"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zpool_import_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_import_001_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_import_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_import_002_neg cleanup
+zpool_import_002_neg_head()
+{
+ atf_set "descr" "Executing 'zpool import' by regular user fails"
+ atf_set "require.progs" "ksh93 zfs zpool fgrep"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zpool_import_002_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_import_002_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_import_002_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_offline_001_neg cleanup
+zpool_offline_001_neg_head()
+{
+ atf_set "descr" "zpool offline returns an error when run as a user"
+ atf_set "require.progs" "ksh93 zfs zpool fgrep"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zpool_offline_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_offline_001_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_offline_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_online_001_neg cleanup
+zpool_online_001_neg_head()
+{
+ atf_set "descr" "zpool online returns an error when run as a user"
+ atf_set "require.progs" "ksh93 zfs zpool fgrep"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zpool_online_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_online_001_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_online_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_remove_001_neg cleanup
+zpool_remove_001_neg_head()
+{
+ atf_set "descr" "zpool remove returns an error when run as a user"
+ atf_set "require.progs" "ksh93 zfs zpool fgrep"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zpool_remove_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_remove_001_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_remove_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_replace_001_neg cleanup
+zpool_replace_001_neg_head()
+{
+ atf_set "descr" "zpool replace returns an error when run as a user"
+ atf_set "require.progs" "ksh93 zfs zpool fgrep"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zpool_replace_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_replace_001_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_replace_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_scrub_001_neg cleanup
+zpool_scrub_001_neg_head()
+{
+ atf_set "descr" "zpool scrub returns an error when run as a user"
+ atf_set "require.progs" "ksh93 zfs zpool fgrep"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zpool_scrub_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_scrub_001_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_scrub_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_set_001_neg cleanup
+zpool_set_001_neg_head()
+{
+ atf_set "descr" "zpool set returns an error when run as a user"
+ atf_set "require.progs" "ksh93 zfs zpool fgrep"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zpool_set_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_set_001_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_set_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_status_001_neg cleanup
+zpool_status_001_neg_head()
+{
+ atf_set "descr" "zpool status works when run as a user"
+ atf_set "require.progs" "ksh93 zfs zpool fgrep"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zpool_status_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_status_001_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_status_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_upgrade_001_neg cleanup
+zpool_upgrade_001_neg_head()
+{
+ atf_set "descr" "zpool upgrade returns an error when run as a user"
+ atf_set "require.progs" "ksh93 zfs zpool fgrep"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zpool_upgrade_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_upgrade_001_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_upgrade_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zdb_001_neg
+ atf_add_test_case zfs_001_neg
+ atf_add_test_case zfs_allow_001_neg
+ atf_add_test_case zfs_clone_001_neg
+ atf_add_test_case zfs_create_001_neg
+ atf_add_test_case zfs_destroy_001_neg
+ atf_add_test_case zfs_get_001_neg
+ atf_add_test_case zfs_inherit_001_neg
+ atf_add_test_case zfs_mount_001_neg
+ atf_add_test_case zfs_promote_001_neg
+ atf_add_test_case zfs_receive_001_neg
+ atf_add_test_case zfs_rename_001_neg
+ atf_add_test_case zfs_rollback_001_neg
+ atf_add_test_case zfs_send_001_neg
+ atf_add_test_case zfs_set_001_neg
+ atf_add_test_case zfs_share_001_neg
+ atf_add_test_case zfs_snapshot_001_neg
+ atf_add_test_case zfs_unallow_001_neg
+ atf_add_test_case zfs_unmount_001_neg
+ atf_add_test_case zfs_unshare_001_neg
+ atf_add_test_case zfs_upgrade_001_neg
+ atf_add_test_case zpool_001_neg
+ atf_add_test_case zpool_add_001_neg
+ atf_add_test_case zpool_add_002_pos
+ atf_add_test_case zpool_attach_001_neg
+ atf_add_test_case zpool_clear_001_neg
+ atf_add_test_case zpool_create_001_neg
+ atf_add_test_case zpool_create_002_pos
+ atf_add_test_case zpool_destroy_001_neg
+ atf_add_test_case zpool_detach_001_neg
+ atf_add_test_case zpool_export_001_neg
+ atf_add_test_case zpool_get_001_neg
+ atf_add_test_case zpool_history_001_neg
+ atf_add_test_case zpool_import_001_neg
+ atf_add_test_case zpool_import_002_neg
+ atf_add_test_case zpool_offline_001_neg
+ atf_add_test_case zpool_online_001_neg
+ atf_add_test_case zpool_remove_001_neg
+ atf_add_test_case zpool_replace_001_neg
+ atf_add_test_case zpool_scrub_001_neg
+ atf_add_test_case zpool_set_001_neg
+ atf_add_test_case zpool_status_001_neg
+ atf_add_test_case zpool_upgrade_001_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/setup.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/setup.ksh
new file mode 100644
index 000000000000..726aaab4cbf1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/setup.ksh
@@ -0,0 +1,168 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+# This setup script is moderately complex, as it creates scenarios for all
+# of the tests included in this directory. Usually we'd want each test case
+# to setup/teardown it's own configuration, but this would be time consuming
+# given the nature of these tests. However, as a side-effect, one test
+# leaving the system in an unknown state could impact other test cases.
+
+
+DISK=${DISKS%% *}
+VOLSIZE=150m
+TESTVOL=testvol
+
+# Create a default setup that includes a volume
+default_setup_noexit "$DISK" "" "volume"
+
+#
+# The rest of this setup script creates a ZFS filesystem configuration
+# that is used to test the rest of the zfs subcommands in this directory.
+#
+
+# create a snapshot and a clone to test clone promote
+log_must $ZFS snapshot $TESTPOOL/$TESTFS@snap
+log_must $ZFS clone $TESTPOOL/$TESTFS@snap $TESTPOOL/$TESTFS/clone
+# create a file in the filesystem that isn't in the above snapshot
+$TOUCH /$TESTDIR/file.txt
+
+
+# create a non-default property and a child we can use to test inherit
+log_must $ZFS create $TESTPOOL/$TESTFS/$TESTFS2
+log_must $ZFS set snapdir=hidden $TESTPOOL/$TESTFS
+
+
+# create an unmounted filesystem to test unmount
+log_must $ZFS create $TESTPOOL/$TESTFS/$TESTFS2.unmounted
+log_must $ZFS unmount $TESTPOOL/$TESTFS/$TESTFS2.unmounted
+
+
+# send our snapshot to a known file in $TMPDIR
+$ZFS send $TESTPOOL/$TESTFS@snap > $TMPDIR/zfstest_datastream.dat
+if [ ! -s $TMPDIR/zfstest_datastream.dat ]
+then
+ log_fail "Zfs send datafile was not created!"
+fi
+log_must $CHMOD 644 $TMPDIR/zfstest_datastream.dat
+
+
+# create a filesystem that has particular properties to test set/get
+log_must $ZFS create -o version=1 $TESTPOOL/$TESTFS/prop
+set -A props $PROP_NAMES
+set -A prop_vals $PROP_VALS
+typeset -i i=0
+
+while [[ $i -lt ${#props[*]} ]]
+do
+ prop_name=${props[$i]}
+ prop_val=${prop_vals[$i]}
+ log_must $ZFS set $prop_name=$prop_val $TESTPOOL/$TESTFS/prop
+ i=$(( $i + 1 ))
+done
+
+
+# create a filesystem we don't mind renaming
+log_must $ZFS create $TESTPOOL/$TESTFS/renameme
+
+
+if is_global_zone
+then
+ # create a filesystem we can share
+ log_must $ZFS create $TESTPOOL/$TESTFS/unshared
+ log_must $ZFS set sharenfs=off $TESTPOOL/$TESTFS/unshared
+
+ # create a filesystem that we can unshare
+ log_must $ZFS create $TESTPOOL/$TESTFS/shared
+ log_must $ZFS set sharenfs=on $TESTPOOL/$TESTFS/shared
+fi
+
+
+# check for upgrade support
+$ZFS upgrade > /dev/null 2>&1
+HAS_UPGRADE=$?
+
+if [ $HAS_UPGRADE -eq 0 ]
+then
+ log_must $ZFS create -o version=1 $TESTPOOL/$TESTFS/version1
+fi
+
+$ZFS 2>&1 | $GREP "allow" > /dev/null
+if (( $? == 0 )); then
+ log_must $ZFS create -o version=1 $TESTPOOL/$TESTFS/allowed
+ log_must $ZFS allow everyone create $TESTPOOL/$TESTFS/allowed
+fi
+
+if is_global_zone; then
+ # Now create several virtual disks to test zpool with
+ log_must create_vdevs \
+ /$TESTDIR/disk1.dat \
+ /$TESTDIR/disk2.dat \
+ /$TESTDIR/disk3.dat \
+ /$TESTDIR/disk-additional.dat \
+ /$TESTDIR/disk-export.dat \
+ /$TESTDIR/disk-offline.dat \
+ /$TESTDIR/disk-spare1.dat \
+ /$TESTDIR/disk-spare2.dat
+
+ # and create a pool we can perform attach remove replace,
+ # etc. operations with
+ log_must $ZPOOL create $TESTPOOL.virt mirror /$TESTDIR/disk1.dat \
+ /$TESTDIR/disk2.dat /$TESTDIR/disk3.dat /$TESTDIR/disk-offline.dat \
+ spare /$TESTDIR/disk-spare1.dat
+
+ # Offline one of the disks to test online
+ log_must $ZPOOL offline $TESTPOOL.virt /$TESTDIR/disk-offline.dat
+
+ # create an exported pool to test import
+ log_must $ZPOOL create $TESTPOOL.exported /$TESTDIR/disk-export.dat
+ log_must $ZPOOL export $TESTPOOL.exported
+
+ # Now setup pool properties if they're supported
+ GET=$($ZPOOL 2>&1 | $FGREP "get <all")
+ if [ -n "$GET" ]
+ then
+ set -A props $POOL_PROPS
+ set -A prop_vals $POOL_VALS
+ typeset -i i=0
+
+ while [[ $i -lt ${#props[*]} ]]
+ do
+ prop_name=${props[$i]}
+ prop_val=${prop_vals[$i]}
+ log_must $ZPOOL set $prop_name=$prop_val $TESTPOOL
+ i=$(( $i + 1 ))
+ done
+ fi
+
+ # copy a v1 pool from cli_root
+ $CP $STF_SUITE/tests/cli_root/zpool_upgrade/blockfiles/zfs-pool-v1.dat.Z \
+ /$TESTDIR
+ log_must $UNCOMPRESS /$TESTDIR/zfs-pool-v1.dat.Z
+ log_must $ZPOOL import -d /$TESTDIR v1-pool
+fi
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/setup_basic.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/setup_basic.ksh
new file mode 100644
index 000000000000..627130f205d6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/setup_basic.ksh
@@ -0,0 +1,30 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Copyright 2015 Spectra Logic Corporation. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+# Allow unprivileged user to touch log files in our temp dir
+chmod 1777 ${TMPDIR}
+chmod 666 ${TMPDIR}/* || true
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zdb_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zdb_001_neg.ksh
new file mode 100644
index 000000000000..da00c6f3e755
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zdb_001_neg.ksh
@@ -0,0 +1,91 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zdb_001_neg
+#
+# DESCRIPTION:
+#
+# zdb can't run as a user on datasets, but can run without arguments
+#
+# STRATEGY:
+# 1. Run zdb as a user, it should print information
+# 2. Run zdb as a user on different datasets, it should fail
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function check_zdb
+{
+ run_unprivileged $@ > $TMPDIR/zdb.${TESTCASE_ID}
+ $GREP "Dataset mos" $TMPDIR/zdb.${TESTCASE_ID}
+ if [ $? -eq 0 ]
+ then
+ log_fail "$@ exited 0 when run as a non root user!"
+ fi
+ $RM $TMPDIR/zdb.${TESTCASE_ID}
+}
+
+
+function cleanup
+{
+ if [ -e $TMPDIR/zdb_001_neg.${TESTCASE_ID}.txt ]
+ then
+ $RM $TMPDIR/zdb_001_neg.${TESTCASE_ID}.txt
+ fi
+
+}
+
+verify_runnable "global"
+
+log_assert "zdb can't run as a user on datasets, but can run without arguments"
+log_onexit cleanup
+
+run_unprivileged $ZDB > $TMPDIR/zdb_001_neg.${TESTCASE_ID}.txt || log_fail "$ZDB failed"
+# verify the output looks okay
+log_must $GREP pool_guid $TMPDIR/zdb_001_neg.${TESTCASE_ID}.txt
+
+# we shouldn't able to run it on any dataset
+check_zdb $ZDB $TESTPOOL
+check_zdb $ZDB $TESTPOOL/$TESTFS
+check_zdb $ZDB $TESTPOOL/$TESTFS@snap
+check_zdb $ZDB $TESTPOOL/$TESTFS.clone
+
+log_pass "zdb can't run as a user on datasets, but can run without arguments"
+
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_001_neg.ksh
new file mode 100644
index 000000000000..664ad2b9d27c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_001_neg.ksh
@@ -0,0 +1,69 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_001_neg
+#
+# DESCRIPTION:
+#
+# zfs shows a usage message when run as a user
+#
+# STRATEGY:
+# 1. Run zfs as a user
+# 2. Verify it produces a usage message
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function cleanup
+{
+ if [ -e $TMPDIR/zfs_001_neg.${TESTCASE_ID}.txt ]
+ then
+ $RM $TMPDIR/zfs_001_neg.${TESTCASE_ID}.txt
+ fi
+}
+
+log_onexit cleanup
+log_assert "zfs shows a usage message when run as a user"
+
+run_unprivileged "$ZFS" > $TMPDIR/zfs_001_neg.${TESTCASE_ID}.txt 2>&1
+log_must $GREP "usage: zfs command args" $TMPDIR/zfs_001_neg.${TESTCASE_ID}.txt
+
+log_pass "zfs shows a usage message when run as a user"
+
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_allow_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_allow_001_neg.ksh
new file mode 100644
index 000000000000..42ee0010a19b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_allow_001_neg.ksh
@@ -0,0 +1,77 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_allow_001_neg
+#
+# DESCRIPTION:
+#
+# zfs allow returns an error when run as a user
+#
+# STRATEGY:
+#
+# 1. Verify that trying to show allows works as a user
+# 2. Verify that trying to set allows fails as a user
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+# check to see if we have zfs allow
+$ZFS 2>&1 | $GREP "allow" > /dev/null
+if (($? != 0)) then
+ log_unsupported "ZFS allow not supported on this machine."
+fi
+
+log_assert "zfs allow returns an error when run as a user"
+
+log_must run_unprivileged "$ZFS allow $TESTPOOL/$TESTFS"
+log_mustnot run_unprivileged "$ZFS allow `$LOGNAME` create $TESTPOOL/$TESTFS"
+
+# now verify that the above command actually did nothing by
+# checking for any allow output. ( if no allows are granted,
+# nothing should be output )
+OUTPUT=$(run_unprivileged "$ZFS allow $TESTPOOL/$TESTFS" | $GREP "Local+Descendent" )
+if [ -n "$OUTPUT" ]
+then
+ log_fail "zfs allow permissions were granted on $TESTPOOL/$TESTFS"
+fi
+
+log_pass "zfs allow returns an error when run as a user"
+
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_clone_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_clone_001_neg.ksh
new file mode 100644
index 000000000000..428f642e38c5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_clone_001_neg.ksh
@@ -0,0 +1,64 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_clone_001_neg
+#
+# DESCRIPTION:
+#
+# zfs clone returns an error when run as a user
+#
+# STRATEGY:
+#
+# 1. Verify that we're unable to clone snapshots as a user
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+log_assert "zfs clone returns an error when run as a user"
+log_mustnot run_unprivileged "$ZFS clone $TESTPOOL/$TESTFS@snap $TESTPOOL/$TESTFS.myclone"
+
+# check to see that the above command really did nothing
+if datasetexists $TESTPOOL/$TESTFS.myclone
+then
+ log_fail "Dataset $TESTPOOL/$TESTFS.myclone should not exist!"
+fi
+log_pass "zfs clone returns an error when run as a user"
+
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_create_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_create_001_neg.ksh
new file mode 100644
index 000000000000..c54dbc8f70be
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_create_001_neg.ksh
@@ -0,0 +1,69 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_create_001_neg
+#
+# DESCRIPTION:
+# Executing various badly formed 'zfs create' should fail.
+#
+# STRATEGY:
+# 1. Create an array of badly formed sub-commands.
+# 2. Execute each element of the array.
+# 3. Verify an error code is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+
+set -A args "" "create $TESTPOOL/$TESTFS" \
+ "create $TESTPOOL/$TESTFS@$TESTSNAP" \
+ "create $TESTPOOL/$TESTCLCT/$TESTFS" \
+ "create $TESTFS/$TESTPOOL/$TESTCLCT"
+
+log_assert "Verify zfs create without parameters fails."
+
+typeset -i i=0
+while [[ $i -lt ${#args[*]} ]]; do
+ log_mustnot run_unprivileged "$ZFS ${args[i]}"
+ ((i = i + 1))
+done
+
+log_pass "The sub-command 'create' fails as non-root."
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_destroy_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_destroy_001_neg.ksh
new file mode 100644
index 000000000000..748f07a337f8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_destroy_001_neg.ksh
@@ -0,0 +1,75 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_cli_006_neg
+#
+# DESCRIPTION:
+# Verify that 'zfs destroy' fails as non-root.
+#
+# STRATEGY:
+# 1. Create an array of options.
+# 2. Execute each element of the array.
+# 3. Verify an error code is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+
+set -A args "destroy" "destroy $TESTPOOL/$TESTFS" \
+ "destroy -f" "destroy -f $TESTPOOL/$TESTFS" \
+ "destroy -r" "destroy -r $TESTPOOL/$TESTFS" \
+ "destroy -rf $TESTPOOL/$TESTFS" \
+ "destroy -fr $TESTPOOL/$TESTFS" \
+ "destroy $TESTPOOL/$TESTFS@$TESTSNAP" \
+ "destroy -f $TESTPOOL/$TESTFS@$TESTSNAP" \
+ "destroy -r $TESTPOOL/$TESTFS@$TESTSNAP" \
+ "destroy -rf $TESTPOOL/$TESTFS@$TESTSNAP" \
+ "destroy -fr $TESTPOOL/$TESTFS@$TESTSNAP"
+
+log_assert "zfs destroy [-f|-r] [fs|snap]"
+
+typeset -i i=0
+while [[ $i -lt ${#args[*]} ]]; do
+ log_mustnot run_unprivileged "$ZFS ${args[i]}"
+ ((i = i + 1))
+done
+
+log_pass "The sub-command 'destroy' fails as non-root."
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_get_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_get_001_neg.ksh
new file mode 100644
index 000000000000..8da85b4e89d1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_get_001_neg.ksh
@@ -0,0 +1,74 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_get_001_neg
+#
+# DESCRIPTION:
+#
+# zfs get works when run as a user
+#
+# STRATEGY:
+# 1. Run zfs get with an array of different arguments
+# 2. Verify for each property, we get the value that's expected
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+log_assert "zfs get works when run as a user"
+
+typeset -i i=0
+
+set -A props $PROP_NAMES
+set -A prop_vals $PROP_VALS
+
+while [[ $i -lt ${#args[*]} ]]
+do
+ PROP=${props[$i]}
+ EXPECTED=${prop_vals[$i]}
+ ACTUAL=$( run_unprivileged "$ZFS get $PROP -o value -H snapdir $TESTPOOl/$TESTFS/prop" )
+ if [ "$ACTUAL" != "$EXPECTED" ]
+ then
+ log_fail "Property $PROP value was $ACTUAL, expected $EXPECTED"
+ fi
+ i=$(( $i + 1 ))
+done
+
+log_pass "zfs get works when run as a user"
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_inherit_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_inherit_001_neg.ksh
new file mode 100644
index 000000000000..ccd9313befbb
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_inherit_001_neg.ksh
@@ -0,0 +1,66 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_inherit_001_neg
+#
+# DESCRIPTION:
+#
+# zfs inherit returns an error when run as a user
+#
+# STRATEGY:
+#
+# 1. Verify that we can't inherit a property when running as a user
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+log_assert "zfs inherit returns an error when run as a user"
+log_mustnot run_unprivileged "$ZFS inherit snapdir $TESTPOOL/$TESTFS/$TESTFS2"
+
+# check to see that the above command really did nothing
+PROP=$($ZFS get snapdir $TESTPOOL/$TESTFS)
+if [ "$PROP" = "visible" ]
+then
+ log_fail "snapdir property inherited from the $TESTPOOL/$TESTFS!"
+fi
+
+log_pass "zfs inherit returns an error when run as a user"
+
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_mount_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_mount_001_neg.ksh
new file mode 100644
index 000000000000..2ee8fcef94a0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_mount_001_neg.ksh
@@ -0,0 +1,66 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_mount_001_neg
+#
+# DESCRIPTION:
+#
+# zfs mount returns an error when run as a user
+#
+# STRATEGY:
+#
+# 1. Verify that we can't mount the unmounted filesystem created in setup
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+log_assert "zfs mount returns an error when run as a user"
+
+log_mustnot run_unprivileged "$ZFS mount $TESTPOOL/$TESTFS/$TESTFS2.unmounted"
+
+# now verify that the above command didn't do anything
+MOUNTED=$($MOUNT | $GREP $TESTPOOL/$TESTFS/$TESTFS2.unmounted)
+if [ -n "$MOUNTED" ]
+then
+ log_fail "Filesystem $TESTPOOL/$TESTFS/$TESTFS2.unmounted was mounted!"
+fi
+
+log_pass "zfs mount returns an error when run as a user"
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_promote_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_promote_001_neg.ksh
new file mode 100644
index 000000000000..098fd54640ed
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_promote_001_neg.ksh
@@ -0,0 +1,66 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_promote_001_neg
+#
+# DESCRIPTION:
+#
+# zfs promote returns an error when run as a user
+#
+# STRATEGY:
+#
+# 1. Verify we don't have permissions to promote a clone
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+log_assert "zfs promote returns an error when run as a user"
+
+log_mustnot run_unprivileged "$ZFS promote $TESTPOOL/$TESTFS/clone"
+
+# Now verify that the above command didn't do anything
+if datasetexists $TESTPOOL/$TESTFS/clone@snap
+then
+ log_fail "Clone $TESTPOOl/$TESTFS/clone was actually promoted!"
+fi
+
+log_pass "zfs promote returns an error when run as a user"
+
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_receive_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_receive_001_neg.ksh
new file mode 100644
index 000000000000..0d85eafea285
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_receive_001_neg.ksh
@@ -0,0 +1,68 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_receive_001_neg
+#
+# DESCRIPTION:
+#
+# zfs receive returns an error when run as a user
+#
+# STRATEGY:
+#
+# 1. Attempt to receive a datastream as a user
+# 2. Verify that the dataset wasn't created
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+log_assert "zfs receive returns an error when run as a user"
+
+log_mustnot run_unprivileged "$ZFS receive -d $TESTPOOL/$TESTFS/$TESTFS2" \
+ < $TMPDIR/zfstest_datastream.dat
+
+# verify that command actually did nothing
+
+if datasetexists $TESTPOOL/$TESTFS/$TESTFS2/$TESTFS
+then
+ log_fail "$TESTPOOL/$TESTFS/$TESTFS2/$TESTFS was received!"
+fi
+
+log_pass "zfs receive returns an error when run as a user"
+
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_rename_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_rename_001_neg.ksh
new file mode 100644
index 000000000000..18e9d4904aa7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_rename_001_neg.ksh
@@ -0,0 +1,66 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_rename_001_neg
+#
+# DESCRIPTION:
+#
+# zfs rename returns an error when run as a user
+#
+# STRATEGY:
+# 1. Attempt to rename a dataset
+# 2. Verify that the renamed dataset does not exist.
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+log_assert "zfs rename returns an error when run as a user"
+
+log_mustnot run_unprivileged "$ZFS rename $TESTPOOL/$TESTFS/renameme $TESTPOOL/$TESTFS/renameme1"
+
+# now verify the above command didn't actually do anything
+if datasetexists $TESTPOOL/$TESTFS/renameme1
+then
+ log_fail "The dataset $TESTPOOL/$TESTFS/renameme was renamed!"
+fi
+
+log_pass "zfs rename returns an error when run as a user"
+
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_rollback_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_rollback_001_neg.ksh
new file mode 100644
index 000000000000..81d104625287
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_rollback_001_neg.ksh
@@ -0,0 +1,70 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_rollback_001_neg
+#
+# DESCRIPTION:
+#
+# zfs rollback returns an error when run as a user
+#
+# STRATEGY:
+# 1. Attempt to rollback a snapshot
+# 2. Verify that a file which doesn't exist in the snapshot still exists
+# (showing the snapshot rollback failed)
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+log_assert "zfs rollback returns an error when run as a user"
+
+log_mustnot run_unprivileged "$ZFS rollback $TESTPOOL/$TESTFS@snap"
+
+# now verify the above command didn't actually do anything
+
+# in the above filesystem there's a file that should not exist once
+# the snapshot is rolled back - we check for it
+if [ ! -e /$TESTDIR/file.txt ]
+then
+ log_fail "Rollback of snapshot $TESTPOOL/$TESTFS@snap succeeded!"
+fi
+
+log_pass "zfs rollback returns an error when run as a user"
+
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_send_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_send_001_neg.ksh
new file mode 100644
index 000000000000..3488fcc4f624
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_send_001_neg.ksh
@@ -0,0 +1,77 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_send_001_neg
+#
+# DESCRIPTION:
+#
+# zfs send returns an error when run as a user
+#
+# STRATEGY:
+# 1. Attempt to send a dataset to a file
+# 2. Verify the file created has zero-size
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function cleanup
+{
+ if [ -e $TMPDIR/zfstest_datastream.${TESTCASE_ID} ]
+ then
+ log_must $RM $TMPDIR/zfstest_datastream.${TESTCASE_ID}
+ fi
+}
+
+log_assert "zfs send returns an error when run as a user"
+log_onexit cleanup
+
+run_unprivileged "$ZFS send $TESTPOOL/$TESTFS@snap" > $TMPDIR/zfstest_datastream.${TESTCASE_ID} && log_fail "zfs send unexpectedly succeeded!"
+
+# Now check that the above command actually did nothing
+
+# We should have a non-zero-length file in $TMPDIR
+if [ -s $TMPDIR/zfstest_datastream.${TESTCASE_ID} ]
+then
+ log_fail "A zfs send file was created in $TMPDIR/zfstest_datastream.${TESTCASE_ID} !"
+fi
+
+log_pass "zfs send returns an error when run as a user"
+
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_set_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_set_001_neg.ksh
new file mode 100644
index 000000000000..43a7738e6a80
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_set_001_neg.ksh
@@ -0,0 +1,79 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_set_001_neg
+#
+# DESCRIPTION:
+#
+# zfs set returns an error when run as a user
+#
+# STRATEGY:
+# 1. Attempt to set an array of properties on a dataset
+# 2. Verify that those properties were not set and retain their original values.
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+log_assert "zfs set returns an error when run as a user"
+
+typeset -i i=0
+
+set -A props $PROP_NAMES
+set -A prop_vals $PROP_VALS
+set -A prop_new $PROP_ALTVALS
+
+while [[ $i -lt ${#args[*]} ]]
+do
+ PROP=${props[$i]}
+ EXPECTED=${prop_vals[$i]}
+ NEW=${prop_new[$i]}
+ log_mustnot run_unprivileged "$ZFS set $PROP=$NEW $TESTPOOL/$TESTFS/prop"
+
+ # Now verify that the above command did nothing
+ ACTUAL=$($ZFS get $PROP -o value -H snapdir $TESTPOOl/$TESTFS/prop )
+ if [ "$ACTUAL" != "$EXPECTED" ]
+ then
+ log_fail "Property $PROP was set to $ACTUAL, expected $EXPECTED"
+ fi
+ i=$(( $i + 1 ))
+done
+
+log_pass "zfs set returns an error when run as a user"
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_share_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_share_001_neg.ksh
new file mode 100644
index 000000000000..13778763ad96
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_share_001_neg.ksh
@@ -0,0 +1,72 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_share_001_neg
+#
+# DESCRIPTION:
+#
+# zfs share returns an error when run as a user
+#
+# STRATEGY:
+# 1. Attempt to share a dataset
+# 2. Verify the dataset was not shared.
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "zfs share returns an error when run as a user"
+
+if is_shared $TESTDIR/unshared
+then
+ log_fail "$TESTPOOL/$TESTFS/unshared was incorrectly shared initially!"
+fi
+
+log_mustnot run_unprivileged "$ZFS share $TESTPOOL/$TESTFS/unshared"
+
+# Now verify that the above command didn't actually do anything
+if is_shared $TESTDIR/unshared
+then
+ log_fail "$TESTPOOL/$TESTFS/unshared was actually shared!"
+fi
+
+log_pass "zfs share returns an error when run as a user"
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_snapshot_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_snapshot_001_neg.ksh
new file mode 100644
index 000000000000..036bc30db3b9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_snapshot_001_neg.ksh
@@ -0,0 +1,65 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_snapshot_001_neg
+#
+# DESCRIPTION:
+#
+# zfs snapshot returns an error when run as a user
+#
+# STRATEGY:
+# 1. Attempt to snapshot a dataset
+# 2. Verify the snapshot wasn't taken
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+log_assert "zfs snapshot returns an error when run as a user"
+
+log_mustnot run_unprivileged "$ZFS snapshot $TESTPOOL/$TESTFS@usersnap1"
+
+# Now verify that the above command didn't actually do anything
+if datasetexists $TESTPOOL/$TESTFS@usersnap1
+then
+ log_fail "Snapshot $TESTPOOL/$TESTFS@usersnap1 was taken !"
+fi
+
+log_pass "zfs snapshot returns an error when run as a user"
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_unallow_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_unallow_001_neg.ksh
new file mode 100644
index 000000000000..50305e4825d9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_unallow_001_neg.ksh
@@ -0,0 +1,74 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_unallow_001_neg
+#
+# DESCRIPTION:
+#
+# zfs unallow returns an error when run as a user
+#
+# STRATEGY:
+# 1. Attempt to unallow a set of permissions
+# 2. Verify the unallow wasn't performed
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+# check to see if we have zfs unallow
+$ZFS 2>&1 | $GREP "unallow" > /dev/null
+if (($? != 0)) then
+ log_unsupported "ZFS unallow not supported on this machine."
+fi
+
+log_assert "zfs unallow returns an error when run as a user"
+
+log_mustnot run_unprivileged "$ZFS unallow everyone $TESTPOOL/$TESTFS/allowed"
+
+# now check with zfs allow to see if the permissions are still there
+OUTPUT=$($ZFS allow $TESTPOOL/$TESTFS/allowed | $GREP "Local+Descendent" )
+if [ -z "$OUTPUT" ]
+then
+ log_fail "Error - create permissions were unallowed on \
+ $TESTPOOL/$TESTFS/allowed"
+fi
+
+log_pass "zfs unallow returns an error when run as a user"
+
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_unmount_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_unmount_001_neg.ksh
new file mode 100644
index 000000000000..615eea291493
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_unmount_001_neg.ksh
@@ -0,0 +1,73 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_unmount_001_neg
+#
+# DESCRIPTION:
+# Verify that 'zfs umount' and its variants fail as non-root.
+#
+# STRATEGY:
+# 1. Create an array of options.
+# 2. Execute each element of the array.
+# 3. Verify that the commands fail with an error code.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+set -A args "umount" "umount -f" "unmount" "unmount -f" \
+ "umount $TESTPOOL/$TESTFS" "umount -f $TESTPOOL/$TESTFS" \
+ "unmount $TESTPOOL/$TESTFS" "unmount -f $TESTPOOL/$TESTFS" \
+ "umount $TESTPOOL/$TESTFS@$TESTSNAP" \
+ "umount -f $TESTPOOL/$TESTFS@$TESTSNAP" \
+ "unmount $TESTPOOL/$TESTFS@$TESTSNAP" \
+ "unmount -f $TESTPOOL/$TESTFS@$TESTSNAP" \
+ "umount $TESTDIR" "umount -f $TESTDIR" \
+ "unmount $TESTDIR" "unmount -f $TESTDIR"
+
+log_assert "zfs u[n]mount [-f] [mountpoint|fs|snap]"
+
+typeset -i i=0
+while [[ $i -lt ${#args[*]} ]]; do
+ log_mustnot run_unprivileged "$ZFS ${args[i]}"
+ ((i = i + 1))
+done
+
+log_pass "The sub-command 'u[n]mount' fails as non-root."
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_unshare_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_unshare_001_neg.ksh
new file mode 100644
index 000000000000..cf8f373cf2f3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_unshare_001_neg.ksh
@@ -0,0 +1,73 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_unshare_001_neg
+#
+# DESCRIPTION:
+#
+# zfs unshare returns an error when run as a user
+#
+# STRATEGY:
+# 1. Attempt to unshare a shared dataset
+# 2. Verify the dataset is still shared
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "zfs unshare returns an error when run as a user"
+
+# verify that the filesystem was shared initially
+if not_shared $TESTDIR/shared
+then
+ log_fail "$TESTPOOL/$TESTFS/shared was not shared initially at all!"
+fi
+
+log_mustnot run_unprivileged "$ZFS unshare $TESTPOOL/$TESTFS/shared"
+
+# now verify that the above command didn't do anything
+if not_shared $TESTDIR/shared
+then
+ log_fail "$TESTPOOL/$TESTFS/shared was actually unshared!"
+fi
+
+log_pass "zfs unshare returns an error when run as a user"
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_upgrade_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_upgrade_001_neg.ksh
new file mode 100644
index 000000000000..745898547127
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zfs_upgrade_001_neg.ksh
@@ -0,0 +1,77 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_upgrade_001_neg
+#
+# DESCRIPTION:
+#
+# zfs upgrade returns an error when run as a user
+#
+# STRATEGY:
+# 1. Attempt to upgrade a version1 dataset
+# 2. Verify the dataset wasn't upgraded
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+# check to see if we have upgrade capability
+$ZFS upgrade > /dev/null 2>&1
+HAS_UPGRADE=$?
+if [ $HAS_UPGRADE -ne 0 ]
+then
+ log_unsupported "Zfs upgrade not supported"
+fi
+
+log_assert "zfs upgrade returns an error when run as a user"
+
+
+log_mustnot run_unprivileged "$ZFS upgrade $TESTPOOL/$TESTFS/version1"
+
+# now check to see the above command didn't do anything
+VERSION=$($ZFS upgrade $TESTPOOL/$TESTFS/version1 2>&1 \
+ | $GREP "already at this version")
+if [ -n "$VERSION" ]
+then
+ log_fail "A filesystem was upgraded!"
+fi
+
+log_pass "zfs upgrade returns an error when run as a user"
+
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_001_neg.ksh
new file mode 100644
index 000000000000..c35da9a2b0e2
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_001_neg.ksh
@@ -0,0 +1,70 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_001_neg
+#
+# DESCRIPTION:
+#
+# zpool shows a usage message when run as a user
+#
+# STRATEGY:
+# 1. Run the zpool command
+# 2. Verify that a usage message is produced
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function cleanup
+{
+ if [ -e $TMPDIR/zpool_001_neg.${TESTCASE_ID}.txt ]
+ then
+ $RM $TMPDIR/zpool_001_neg.${TESTCASE_ID}.txt
+ fi
+}
+
+log_onexit cleanup
+log_assert "zpool shows a usage message when run as a user"
+
+run_unprivileged "$ZPOOL" > $TMPDIR/zpool_001_neg.${TESTCASE_ID}.txt 2>&1
+log_must $GREP "usage: zpool command args" $TMPDIR/zpool_001_neg.${TESTCASE_ID}.txt
+
+log_pass "zpool shows a usage message when run as a user"
+
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_add_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_add_001_neg.ksh
new file mode 100644
index 000000000000..733278418ef4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_add_001_neg.ksh
@@ -0,0 +1,67 @@
+#!/usr/local/bin/ksh93 -p
+
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_add_001_neg
+#
+# DESCRIPTION:
+# Verify that 'zpool add' fails as non-root.
+#
+# STRATEGY:
+# 1. Create an array of options.
+# 2. Execute each element of the array.
+# 3. Verify that an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+set_disks
+
+set -A args "add $TESTPOOL $DISK1" "add -f $TESTPOOL $DISK1"
+
+log_assert "zpool add [-fn] pool_name vdev"
+
+typeset -i i=0
+while [[ $i -lt ${#args[*]} ]]; do
+ log_mustnot run_unprivileged "$ZPOOL ${args[i]}"
+ ((i = i + 1))
+done
+
+log_pass "The sub-command 'add' and its options fail as non-root."
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_add_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_add_002_pos.ksh
new file mode 100644
index 000000000000..03cb571b18eb
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_add_002_pos.ksh
@@ -0,0 +1,43 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2014 Spectra Logic Corp. All rights reserved.
+# Use is subject to license terms.
+#
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+
+set_disks
+
+set -A args "add -n $TESTPOOL $DISK1" "add -fn $TESTPOOL $DISK1"
+
+log_assert "zpool add [-f] -n succeeds for unpriveleged users"
+
+typeset -i i=0
+while [[ $i -lt ${#args[*]} ]]; do
+ log_must run_unprivileged "$ZPOOL ${args[i]}"
+ ((i = i + 1))
+done
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_attach_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_attach_001_neg.ksh
new file mode 100644
index 000000000000..ffc7f2e70628
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_attach_001_neg.ksh
@@ -0,0 +1,75 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_attach_001_neg
+#
+# DESCRIPTION:
+#
+# zpool attach returns an error when run as a user
+#
+# STRATEGY:
+# 1. Attempt to attach a disk to a pool
+# 2.Verify that the attach failed
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function check_for_attach
+{
+ RESULT=$($ZPOOL status -v $TESTPOOL.virt | $GREP disk-additional.dat)
+ if [ -n "$RESULT" ]
+ then
+ log_fail "A disk was attached to the pool!"
+ fi
+}
+
+verify_runnable "global"
+
+log_assert "zpool attach returns an error when run as a user"
+
+log_mustnot run_unprivileged "$ZPOOL attach $TESTPOOL.virt /$TESTDIR/disk1.dat /$TESTDIR/disk-additional.dat"
+check_for_attach
+
+log_mustnot run_unprivileged "$ZPOOL attach -f $TESTPOOL.virt /$TESTDIR/disk1.dat /$TESTDIR/disk-additional.dat"
+check_for_attach
+
+log_pass "zpool attach returns an error when run as a user"
+
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_clear_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_clear_001_neg.ksh
new file mode 100644
index 000000000000..5736652adc71
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_clear_001_neg.ksh
@@ -0,0 +1,61 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_clear_001_neg
+#
+# DESCRIPTION:
+#
+# zpool clear returns an error when run as a user
+#
+# STRATEGY:
+#
+# 1. Attempt to clear errors on a zpool
+# 2. Verify that the command fails
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "zpool clear returns an error when run as a user"
+
+log_mustnot run_unprivileged "$ZPOOL clear $TESTPOOL"
+
+log_pass "zpool clear returns an error when run as a user"
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_create_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_create_001_neg.ksh
new file mode 100644
index 000000000000..e6ef23ced113
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_create_001_neg.ksh
@@ -0,0 +1,70 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_create_001_neg
+#
+# DESCRIPTION:
+# Verify that 'zpool create' fails as a non-root user.
+#
+# STRATEGY:
+# 1. Create an array of options.
+# 2. Execute each element of the array.
+# 3. Verify that an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+ADD_DISK="${DISKS%% }"
+ADD_DISK="${ADD_DISK##* }"
+
+[[ -z $ADD_DISK ]] && \
+ log_fail "No spare disks available."
+
+set -A args "create $TESTPOOL $ADD_DISK" "create -f $TESTPOOL $ADD_DISK"
+
+log_assert "zpool create [-fn] pool_name vdev"
+
+typeset -i i=0
+while [[ $i -lt ${#args[*]} ]]; do
+ log_mustnot $ZPOOL ${args[i]}
+ ((i = i + 1))
+done
+
+log_pass "The sub-command 'create' and its options fail as non-root."
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_create_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_create_002_pos.ksh
new file mode 100644
index 000000000000..30d7565b8f79
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_create_002_pos.ksh
@@ -0,0 +1,42 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2014 Spectra Logic Corp. All rights reserved.
+# Use is subject to license terms.
+#
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% }
+DISK=${DISKS##* }
+
+set -A args "create -n $TESTPOOL $DISK" "create -fn $TESTPOOL $DISK"
+
+log_assert "zpool create [-f] -n as an unpriveleged user succeeds"
+
+typeset -i i=0
+while [[ $i -lt ${#args[*]} ]]; do
+ log_must $ZPOOL ${args[i]}
+ ((i = i + 1))
+done
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_destroy_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_destroy_001_neg.ksh
new file mode 100644
index 000000000000..57474ab299da
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_destroy_001_neg.ksh
@@ -0,0 +1,67 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_destroy_001_neg
+#
+# DESCRIPTION:
+# Verify that 'zpool destroy' fails as non-root.
+#
+# STRATEGY:
+# 1. Create an array of options.
+# 2. Execute each element of the array.
+# 3. Verify an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+set -A args "destroy" "destroy -f" \
+ "destroy $TESTPOOL" "destroy -f $TESTPOOL" \
+ "destroy $TESTPOOL $TESTPOOL"
+
+log_assert "zpool destroy [-f] [pool_name ...]"
+
+typeset -i i=0
+while [[ $i -lt ${#args[*]} ]]; do
+ log_mustnot run_unprivileged "$ZPOOL ${args[i]}"
+ ((i = i + 1))
+done
+
+log_pass "The sub-command 'destroy' and its options fail as non-root."
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_detach_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_detach_001_neg.ksh
new file mode 100644
index 000000000000..dc26cff1dd1f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_detach_001_neg.ksh
@@ -0,0 +1,67 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_detach_001_neg
+#
+# DESCRIPTION:
+#
+# zpool detach returns an error when run as a user
+#
+# STRATEGY:
+#
+# 1. Attempt to detach a device from a pool
+# 2. Verify the command fails
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "zpool detach returns an error when run as a user"
+
+log_mustnot run_unprivileged "$ZPOOL detach $TESTPOOL.virt /$TESTDIR/disk1.dat"
+
+RESULT=$($ZPOOL status -v $TESTPOOL.virt | $GREP disk1.dat)
+if [ -z "$RESULT" ]
+then
+ log_fail "A disk was detached from the pool!"
+fi
+
+log_pass "zpool detach returns an error when run as a user"
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_export_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_export_001_neg.ksh
new file mode 100644
index 000000000000..06493587e335
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_export_001_neg.ksh
@@ -0,0 +1,74 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_export_001_neg
+#
+# DESCRIPTION:
+#
+# zpool export returns an error when run as a user
+#
+# STRATEGY:
+# 1. Attempt to export a pool
+# 2. Verify the command fails
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function check_for_export
+{
+ RESULT=$($ZPOOL list | $GREP $TESTPOOL.virt )
+ if [ -z "$RESULT" ]
+ then
+ log_fail "A pool was exported!"
+ fi
+
+}
+
+verify_runnable "global"
+
+log_assert "zpool export returns an error when run as a user"
+
+log_mustnot run_unprivileged "$ZPOOL export $TESTPOOL.virt"
+check_for_export
+
+log_mustnot run_unprivileged "$ZPOOL export -f $TESTPOOL.virt"
+check_for_export
+
+log_pass "zpool export returns an error when run as a user"
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_get_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_get_001_neg.ksh
new file mode 100644
index 000000000000..626b5f740134
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_get_001_neg.ksh
@@ -0,0 +1,76 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_get_001_neg
+#
+# DESCRIPTION:
+#
+# zpool get works when run as a user
+#
+# STRATEGY:
+#
+# 1. For each property, get that property
+# 2. Verify the property was the same as that set in setup
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "zpool get works when run as a user"
+
+set -A props $POOL_PROPS
+set -A prop_vals $POOL_VALS
+
+while [[ $i -lt ${#args[*]} ]]
+do
+ PROP=${props[$i]}
+ EXPECTED=${prop_vals[$i]}
+ ACTUAL=$( $ZPOOL get $PROP $TESTPOOL | $GREP $PROP | $AWK '{print $1}' )
+ if [ "$ACTUAL" != "$EXPECTED" ]
+ then
+ log_fail "Property $PROP value was $ACTUAL, expected $EXPECTED"
+ fi
+ i=$(( $i + 1 ))
+done
+
+log_must run_unprivileged "$ZPOOL get all $TESTPOOL"
+
+log_pass "zpool get works when run as a user"
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_history_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_history_001_neg.ksh
new file mode 100644
index 000000000000..5a774038959f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_history_001_neg.ksh
@@ -0,0 +1,63 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_history_001_neg
+#
+# DESCRIPTION:
+#
+# zpool history works when run as a user
+#
+# STRATEGY:
+# 1. Attempt to get history on a test pool
+# 2. Verify the command fails
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "zpool history returns an error when run as a user"
+
+log_mustnot run_unprivileged "$ZPOOL history"
+log_mustnot run_unprivileged "$ZPOOL history $TESTPOOL"
+log_mustnot run_unprivileged "$ZPOOL history -i $TESTPOOL"
+log_mustnot run_unprivileged "$ZPOOL history -l $TESTPOOL"
+log_mustnot run_unprivileged "$ZPOOL history -il $TESTPOOL"
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_import_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_import_001_neg.ksh
new file mode 100644
index 000000000000..f9620d83730a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_import_001_neg.ksh
@@ -0,0 +1,76 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_import_001_neg
+#
+# DESCRIPTION:
+#
+# zpool import returns an error when run as a user
+#
+# STRATEGY:
+# 1. Attempt to import an exported pool
+# 2. Verify the command fails
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function check_for_import
+{
+ RESULT=$($ZPOOL list -H -o name | $GREP $TESTPOOL.exported)
+ if [ -n "$RESULT" ]
+ then
+ log_fail "Pool $TESTPOOL.export was successfully imported!"
+ fi
+}
+
+verify_runnable "global"
+
+log_assert "zpool import returns an error when run as a user"
+log_mustnot run_unprivileged "$ZPOOL import"
+
+log_mustnot run_unprivileged "$ZPOOL import -a"
+check_for_import
+
+log_mustnot run_unprivileged "$ZPOOL import -d /$TESTDIR $TESTPOOL.exported"
+check_for_import
+
+log_pass "zpool import returns an error when run as a user"
+
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_import_002_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_import_002_neg.ksh
new file mode 100644
index 000000000000..44e475e9bcec
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_import_002_neg.ksh
@@ -0,0 +1,72 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_import_002_neg
+#
+# DESCRIPTION:
+# Executing 'zpool import' as regular user should denied.
+#
+# STRATEGY:
+# 1. Create an array of options try to detect exported/destroyed pools.
+# 2. Execute 'zpool import' with each element of the array by regular user.
+# 3. Verify an error code is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-02)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+typeset testpool
+if is_global_zone ; then
+ testpool=$TESTPOOL.exported
+else
+ testpool=${TESTPOOL%%/*}
+fi
+
+set -A args "" "-D" "-Df" "-f" "-f $TESTPOOL" "-Df $TESTPOOL" "-a"
+
+log_assert "Executing 'zpool import' by regular user fails"
+
+typeset -i i=0
+while [[ $i -lt ${#args[*]} ]]; do
+ log_mustnot run_unprivileged "$ZPOOL import ${args[i]}"
+ ((i = i + 1))
+done
+
+log_pass "Executing 'zpool import' by regular user fails as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_offline_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_offline_001_neg.ksh
new file mode 100644
index 000000000000..777571d7e46b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_offline_001_neg.ksh
@@ -0,0 +1,76 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_offline_001_neg
+#
+# DESCRIPTION:
+#
+# zpool offline returns an error when run as a user
+#
+# STRATEGY:
+# 1. Attempt to offline a device in a pool
+# 2. Verify that the command fails
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function check_for_offline
+{
+ RESULT=$($ZPOOL status -v $TESTPOOL.virt | $GREP disk-1.dat \
+ | $GREP OFFLINE )
+ if [ -n "$RESULT" ]
+ then
+ log_fail "A disk was taken offline!"
+ fi
+}
+
+verify_runnable "global"
+
+log_assert "zpool offline returns an error when run as a user"
+
+log_mustnot run_unprivileged "$ZPOOL offline $TESTPOOL.virt /$TESTDIR/disk-1.dat"
+check_for_offline
+
+log_mustnot run_unprivileged "$ZPOOL offline -t $TESTPOOL.virt /$TESTDIR/disk-1.dat"
+check_for_offline
+
+log_pass "zpool offline returns an error when run as a user"
+
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_online_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_online_001_neg.ksh
new file mode 100644
index 000000000000..10a1e717ff84
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_online_001_neg.ksh
@@ -0,0 +1,76 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_online_001_neg
+#
+# DESCRIPTION:
+#
+# zpool online returns an error when run as a user
+#
+# STRATEGY:
+# 1. Attempt to online a device in a pool
+# 2. Verify the command fails
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function check_for_online
+{
+ RESULT=$($ZPOOL status -v $TESTPOOL.virt | $GREP disk-offline.dat \
+ | $GREP ONLINE )
+ if [ -n "$RESULT" ]
+ then
+ log_fail "A disk was brough online!"
+ fi
+}
+
+verify_runnable "global"
+
+log_assert "zpool online returns an error when run as a user"
+
+log_mustnot run_unprivileged "$ZPOOL online $TESTPOOL.virt /$TESTDIR/disk-offline.dat"
+check_for_online
+
+log_mustnot run_unprivileged "$ZPOOL online -t $TESTPOOL.virt /$TESTDIR/disk-offline.dat"
+check_for_online
+
+log_pass "zpool online returns an error when run as a user"
+
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_remove_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_remove_001_neg.ksh
new file mode 100644
index 000000000000..f8a1d5c65744
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_remove_001_neg.ksh
@@ -0,0 +1,68 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_remove_001_neg
+#
+# DESCRIPTION:
+#
+# zpool remove returns an error when run as a user
+#
+# STRATEGY:
+# 1. Attempt to remove a device from a pool
+# 2. Verify the command fails
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "zpool remove returns an error when run as a user"
+
+log_mustnot run_unprivileged "$ZPOOL remove $TESTPOOL.virt /$TESTDIR/disk-spare1.dat"
+
+RESULT=$($ZPOOL status -v $TESTPOOL.virt | $GREP disk-spare1.dat)
+if [ -z "$RESULT" ]
+then
+ log_fail "A disk was removed from the pool!"
+fi
+
+
+log_pass "zpool remove returns an error when run as a user"
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_replace_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_replace_001_neg.ksh
new file mode 100644
index 000000000000..91046c1f66db
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_replace_001_neg.ksh
@@ -0,0 +1,75 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_replace_001_neg
+#
+# DESCRIPTION:
+#
+# zpool replace returns an error when run as a user
+#
+# STRATEGY:
+# 1. Attempt to replace a device in a pool
+# 2. Verify the command fails
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function check_for_replace
+{
+ $SLEEP 10
+ RESULT=$($ZPOOL status -v $TESTPOOL.virt | $GREP disk-additional.dat)
+ if [ -n "$RESULT" ]
+ then
+ log_fail "A disk was replaced in the pool!"
+ fi
+}
+
+verify_runnable "global"
+
+log_assert "zpool replace returns an error when run as a user"
+
+log_mustnot run_unprivileged "$ZPOOL replace $TESTPOOL.virt /$TESTDIR/disk-1.dat /$TESTDIR/disk-additional.dat"
+check_for_replace
+
+log_mustnot run_unprivileged "$ZPOOL replace -f $TESTPOOL.virt /$TESTDIR/disk-1.dat /$TESTDIR/disk-additional.dat"
+check_for_replace
+
+log_pass "zpool replace returns an error when run as a user"
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_scrub_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_scrub_001_neg.ksh
new file mode 100644
index 000000000000..fdfa6e6e3dfc
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_scrub_001_neg.ksh
@@ -0,0 +1,62 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_scrub_001_neg
+#
+# DESCRIPTION:
+#
+# zpool scrub returns an error when run as a user
+#
+# STRATEGY:
+# 1. Attempt to start a scrub on a pool
+# 2. Verify the command fails
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "zpool scrub returns an error when run as a user"
+
+log_mustnot run_unprivileged "$ZPOOL scrub $TESTPOOL"
+log_mustnot run_unprivileged "$ZPOOL scrub -s $TESTPOOL"
+
+log_pass "zpool scrub returns an error when run as a user"
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_set_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_set_001_neg.ksh
new file mode 100644
index 000000000000..8fe98b61f289
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_set_001_neg.ksh
@@ -0,0 +1,80 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_set_001_neg
+#
+# DESCRIPTION:
+#
+# zpool set returns an error when run as a user
+#
+# STRATEGY:
+# 1. Attempt to set some properties on a pool
+# 2. Verify the command fails
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "zpool set returns an error when run as a user"
+
+set -A props $POOL_NAMES
+set -A prop_vals $POOL_VALS
+set -A prop_new $POOL_ALTVALS
+
+while [[ $i -lt ${#args[*]} ]]
+do
+ PROP=${props[$i]}
+ EXPECTED=${prop_vals[$i]}
+ NEW=${prop_new[$i]}
+ log_mustnot run_unprivileged "$ZPOOL set $PROP=$NEW $TESTPOOL"
+
+ # Now verify that the above command did nothing
+ ACTUAL=$( $ZPOOL get $PROP $TESTPOOL | $GREP $PROP | $AWK '{print $1}' )
+ if [ "$ACTUAL" != "$EXPECTED" ]
+ then
+ log_fail "Property $PROP was set to $ACTUAL, expected $EXPECTED"
+ fi
+ i=$(( $i + 1 ))
+done
+
+
+log_pass "zpool set returns an error when run as a user"
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_status_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_status_001_neg.ksh
new file mode 100644
index 000000000000..e189dd2fc492
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_status_001_neg.ksh
@@ -0,0 +1,74 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_status_001_neg
+#
+# DESCRIPTION:
+#
+# zpool status works when run as a user
+#
+# STRATEGY:
+#
+# 1. Run zpool status as a user
+# 2. Verify we get output
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "zpool status works when run as a user"
+
+log_must run_unprivileged "$ZPOOL status" | $GREP -q "pool:" || \
+ log_fail "No Pool: string found in zpool status output"
+log_must run_unprivileged "$ZPOOL status -v" | $GREP -q "pool:" || \
+ log_fail "No Pool: string found in zpool status output"
+log_must run_unprivileged "$ZPOOL status $TESTPOOL" | $GREP -q "pool:" || \
+ log_fail "No Pool: string found in zpool status output"
+log_must run_unprivileged "$ZPOOL status -v $TESTPOOL" | $GREP -q "pool:" || \
+ log_fail "No Pool: string found in zpool status output"
+
+# $TESTPOOL.virt has an offline device, so -x will show it
+log_must run_unprivileged "$ZPOOL status -x $TESTPOOL.virt" | \
+ $GREP -q "pool:" || \
+ log_fail "No Pool: string found in zpool status output"
+
+log_pass "zpool status works when run as a user"
+
diff --git a/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_upgrade_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_upgrade_001_neg.ksh
new file mode 100644
index 000000000000..b3db61504b34
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/misc/zpool_upgrade_001_neg.ksh
@@ -0,0 +1,75 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_upgrade_001_neg
+#
+# DESCRIPTION:
+#
+# zpool upgrade returns an error when run as a user
+#
+# STRATEGY:
+#
+# 1. Attempt to upgrade a pool
+# 2. Verify the command fails
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "zpool upgrade returns an error when run as a user"
+
+log_onexit cleanup
+# zpool upgrade returns 0 when it can't do anything
+log_must run_unprivileged "$ZPOOL upgrade $TESTPOOL.virt"
+
+# Now try to upgrade our version 1 pool
+log_mustnot run_unprivileged "$ZPOOL upgrade v1-pool"
+
+# if the pool has been upgraded, then v1-pool won't be listed in the output
+# of zpool upgrade anymore
+RESULT=$($ZPOOL upgrade | $GREP v1-pool)
+if [ -z "$RESULT" ]
+then
+ log_fail "A pool was upgraded successfully!"
+fi
+
+log_pass "zpool upgrade returns an error when run as a user"
+
diff --git a/tests/sys/cddl/zfs/tests/cli_user/setup.ksh b/tests/sys/cddl/zfs/tests/cli_user/setup.ksh
new file mode 100644
index 000000000000..326bc555f71b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zfs_list/Makefile b/tests/sys/cddl/zfs/tests/cli_user/zfs_list/Makefile
new file mode 100644
index 000000000000..14185efb74da
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zfs_list/Makefile
@@ -0,0 +1,24 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_user/zfs_list
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zfs_list_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zfs_list_006_pos.ksh
+${PACKAGE}FILES+= zfs_list_002_pos.ksh
+${PACKAGE}FILES+= zfs_list_007_pos.ksh
+${PACKAGE}FILES+= zfs_list_003_pos.ksh
+${PACKAGE}FILES+= zfs_list.cfg
+${PACKAGE}FILES+= zfs_list_005_pos.ksh
+${PACKAGE}FILES+= zfs_list_001_pos.ksh
+${PACKAGE}FILES+= zfs_list.kshlib
+${PACKAGE}FILES+= zfs_list_008_neg.ksh
+${PACKAGE}FILES+= zfs_list_004_neg.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= setup.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zfs_list/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_user/zfs_list/cleanup.ksh
new file mode 100644
index 000000000000..e6eaf0771523
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zfs_list/cleanup.ksh
@@ -0,0 +1,30 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_get/zfs_get_list_d.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zfs_list/setup.ksh b/tests/sys/cddl/zfs/tests/cli_user/zfs_list/setup.ksh
new file mode 100644
index 000000000000..7aed614abf71
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zfs_list/setup.ksh
@@ -0,0 +1,71 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_get/zfs_get_list_d.kshlib
+
+DISK=${DISKS%% *}
+
+default_setup_noexit $DISK
+
+# create datasets and set checksum options
+set -A cksumarray $CKSUMOPTS
+typeset -i index=0
+for dataset in $DATASETS
+do
+ log_must $ZFS create $TESTPOOL/$TESTFS/$dataset
+ enc=$(get_prop encryption $TESTPOOL/$TESTFS/$dataset)
+ if [[ $? -eq 0 ]] && [[ -n "$enc" ]] && [[ "$enc" != "off" ]]; then
+ log_unsupported "checksum property can't be changed \
+when encryption is set to on."
+ fi
+ $SLEEP 1
+ log_must $ZFS snapshot $TESTPOOL/$TESTFS/${dataset}@snap
+
+ $SLEEP 1
+ if is_global_zone ; then
+ log_must $ZFS create -V 64M $TESTPOOL/$TESTFS/${dataset}-vol
+ $SLEEP 1
+ log_must $ZFS snapshot $TESTPOOL/$TESTFS/${dataset}-vol@snap
+ fi
+
+ # sleep to ensure that the datasets have different creation dates
+ $SLEEP 1
+ log_must $ZFS set checksum=${cksumarray[$index]} \
+ $TESTPOOL/$TESTFS/$dataset
+ if datasetexists $TESTPOOL/$TESTFS/${dataset}-vol; then
+ log_must $ZFS set checksum=${cksumarray[$index]} \
+ $TESTPOOL/$TESTFS/${dataset}-vol
+ fi
+
+ index=$((index + 1))
+done
+
+if zfs_get_list_d_supported ; then
+ depth_fs_setup
+fi
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list.cfg b/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list.cfg
new file mode 100644
index 000000000000..1487c9e00f4b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list.cfg
@@ -0,0 +1,35 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_user/cli.cfg
+
+# Datasets (in order of creation date) and which checksum options
+# we want to set for each dataset.
+# These are used by various zfs list tests
+export DATASETS="Apple Banana Carrot Orange apple banana carrot"
+export CKSUMOPTS="on sha256 sha256 sha256 fletcher4 off fletcher2"
+
+
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list.kshlib b/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list.kshlib
new file mode 100644
index 000000000000..8a65c38e1e3c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list.kshlib
@@ -0,0 +1,114 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#
+# A function that verifies sort order. It takes as input
+# a command, which gets executed. We then iterate over the results
+# comparing that the sort order passed in via the list
+#
+function verify_sort { # command list name
+
+ # now verify we've sorted by creation date:
+ typeset CMD=$1
+ typeset list=$2
+ typeset name=$3
+
+ typeset -i RET=0
+ typeset -i index=1
+
+ # run the command to verify that it works
+ log_must eval "$CMD > /dev/null"
+
+ # Now check the sort order
+ for dataset in $( $CMD )
+ do
+ ACTUAL=$(basename $dataset)
+ if [ "$dataset" != "$TESTPOOL/$TESTFS" ]
+ then
+ EXPECTED=$($ECHO $list | $AWK "{print \$$index}")
+ if [ "$ACTUAL" != "$EXPECTED" ]
+ then
+ log_note "WARNING:" \
+ "'$ACTUAL' does not equal '$EXPECTED'"
+ log_fail "ERROR: Sort by $name fails."
+ fi
+
+ ((index = index + 1))
+ fi
+ done
+
+ # finally check to see if we have the expected number of elements
+ if [ $index -ne $($ECHO $list | $AWK '{print split($0,arr)+1}') ]
+ then
+ log_fail "Warning: " \
+ "unexpected number of filesystems found in list output!"
+ fi
+}
+
+# A function that verifies reverse sort order. It takes as input
+# a command, which gets executed. We then iterate over the results
+# comparing that the sort order passed in via the list
+#
+function verify_reverse_sort { # command list name
+
+ typeset CMD=$1
+ typeset list=$2
+ typeset name=$3
+
+ # set our index to the be number of elements in the list
+ typeset -i index=$($ECHO $list | $AWK '{print split($0,arr)}')
+
+ log_note "Checking reverse sort by '$name'," \
+ "expecting the reverse of '$list'"
+ log_must eval "$CMD > /dev/null"
+
+ for dataset in $( $CMD )
+ do
+ ACTUAL=$(basename $dataset)
+ if [ "$dataset" != "$TESTPOOL/$TESTFS" ]
+ then
+ EXPECTED=$($ECHO $list | $AWK "{print \$$index}")
+ if [ "$ACTUAL" != "$EXPECTED" ]
+ then
+ log_note "Warning:" \
+ "'$ACTUAL' does not equal to" \
+ "the reverse of '$EXPECTED'"
+ log_fail "ERROR: Reverse sort by '$name' fails."
+ fi
+
+ ((index = index - 1))
+ fi
+ done
+
+ # finally check to see if we have the expected number of elements
+ if [ $index -ne 0 ]
+ then
+ log_fail "Warning: " \
+ "unexpected number of filesystems found in list output!"
+ fi
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_001_pos.ksh
new file mode 100644
index 000000000000..41a6c70043b7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_001_pos.ksh
@@ -0,0 +1,129 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_get/zfs_get_list_d.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_list_001_pos
+#
+# DESCRIPTION:
+# Executing well-formed 'zfs list' commands should return success.
+#
+# STRATEGY:
+# 1. Create an array of valid options.
+# 2. Execute each element in the array.
+# 3. Verify success is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+set -A args "list" "list -r" "list -H" \
+ "list $TESTPOOL/$TESTFS" \
+ "list -r $TESTPOOL/$TESTFS" "list -H $TESTPOOL/$TESTFS" \
+ "list -rH $TESTPOOL/$TESTFS" "list -Hr $TESTPOOL/$TESTFS" \
+ "list -o name $TESTPOOL/$TESTFS" "list -r -o name $TESTPOOL/$TESTFS" \
+ "list -H -o name $TESTPOOL/$TESTFS" "list -rH -o name $TESTPOOL/$TESTFS" \
+ "list -Hr -o name $TESTPOOL/$TESTFS"
+
+if zfs_get_list_d_supported ; then
+ set -A d_args " " "-r" "-H" \
+ "$TESTPOOL/$TESTFS" \
+ "-r $TESTPOOL/$TESTFS" "-H $TESTPOOL/$TESTFS" \
+ "-rH $TESTPOOL/$TESTFS" "-Hr $TESTPOOL/$TESTFS" \
+ "-o name $TESTPOOL/$TESTFS" "-r -o name $TESTPOOL/$TESTFS" \
+ "-H -o name $TESTPOOL/$TESTFS" "-rH -o name $TESTPOOL/$TESTFS" \
+ "-Hr -o name $TESTPOOL/$TESTFS"
+
+ typeset -i m=${#args[*]}
+ typeset -i n=0
+ typeset -i k=0
+ while (( n<${#depth_options[*]} ));
+ do
+ (( k=0 ))
+ while (( k<${#d_args[*]} ));
+ do
+ args[$m]="list"" -${depth_options[$n]}"" ${d_args[$k]}"
+ (( k+=1 ))
+ (( m+=1 ))
+ done
+ (( n+=1 ))
+ done
+fi
+
+set -A pathargs "list -r $TESTDIR" "list -H $TESTDIR" \
+ "list -r ./../$TESTDIR" "list -H ./../$TESTDIR"
+
+if zfs_get_list_d_supported ; then
+set -A d_pathargs " $TESTDIR" "-r $TESTDIR" "-H $TESTDIR" \
+ "-r ./../$TESTDIR" "-H ./../$TESTDIR"
+
+ (( m=${#pathargs[*]} ))
+ (( n=0 ))
+ (( k=0 ))
+ while (( n<${#depth_options[*]} ));
+ do
+ (( k=0 ))
+ while (( k<${#d_pathargs[*]} ));
+ do
+ pathargs[$m]="list"" -${depth_options[$n]}"" ${d_pathargs[$k]}"
+ (( k+=1 ))
+ (( m+=1 ))
+ done
+ (( n+=1 ))
+ done
+fi
+
+log_assert "Verify 'zfs list [-rH] [-o property[,prop]*] [fs|clct|vol]'."
+
+typeset -i i=0
+while [[ $i -lt ${#args[*]} ]]; do
+ log_must run_unprivileged "eval $ZFS ${args[i]} > /dev/null"
+ ((i = i + 1))
+done
+
+# Verify 'zfs list <path>' will succeed on absolute or relative path.
+
+cd /tmp
+typeset -i i=0
+while [[ $i -lt ${#pathargs[*]} ]]; do
+ log_must run_unprivileged "eval $ZFS ${pathargs[i]} > /dev/null"
+ ((i = i + 1))
+done
+
+log_pass "The sub-command 'list' succeeds as non-root."
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_002_pos.ksh
new file mode 100644
index 000000000000..7a87f8efc7b6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_002_pos.ksh
@@ -0,0 +1,185 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/cli_user/zfs_list/zfs_list.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_list_002_pos
+#
+# DESCRIPTION:
+# The sort functionality in 'zfs list' works as expected.
+#
+# STRATEGY:
+# 1. Using several zfs datasets with names, creation dates, checksum options
+# 2. Sort the datasets by name, checksum options, creation date.
+# 3. Verify that the datasets are sorted correctly.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-09-08)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+# datasets ordered by name
+fs_name="Apple Banana Carrot Orange apple banana carrot"
+vol_name="Apple-vol Banana-vol Carrot-vol Orange-vol apple-vol"
+vol_name="$vol_name banana-vol carrot-vol"
+if is_global_zone ; then
+ snap_name="Apple-vol@snap Apple@snap Banana-vol@snap Banana@snap"
+ snap_name="$snap_name Carrot-vol@snap Carrot@snap Orange-vol@snap Orange@snap"
+ snap_name="$snap_name apple-vol@snap apple@snap banana-vol@snap banana@snap"
+ snap_name="$snap_name carrot-vol@snap carrot@snap"
+else
+ snap_name="Apple@snap Banana@snap"
+ snap_name="$snap_name Carrot@snap Orange@snap"
+ snap_name="$snap_name apple@snap banana@snap"
+ snap_name="$snap_name carrot@snap"
+fi
+
+fs_creation=$fs_name
+vol_creation=$vol_name
+if is_global_zone ; then
+ snap_creation="Apple@snap Apple-vol@snap Banana@snap Banana-vol@snap"
+ snap_creation="$snap_creation Carrot@snap Carrot-vol@snap Orange@snap Orange-vol@snap"
+ snap_creation="$snap_creation apple@snap apple-vol@snap banana@snap banana-vol@snap"
+ snap_creation="$snap_creation carrot@snap carrot-vol@snap"
+else
+ snap_creation="Apple@snap Banana@snap"
+ snap_creation="$snap_creation Carrot@snap Orange@snap"
+ snap_creation="$snap_creation apple@snap banana@snap"
+ snap_creation="$snap_creation carrot@snap"
+fi
+
+#
+# datsets ordered by checksum options (note, Orange, Carrot & Banana have the
+# same checksum options, so ZFS should revert to sorting them alphabetically by
+# name)
+#
+fs_cksum="carrot apple banana Apple Banana Carrot Orange"
+vol_cksum="carrot-vol apple-vol banana-vol Apple-vol Banana-vol"
+vol_cksum="$vol_cksum Carrot-vol Orange-vol"
+snap_cksum=$snap_creation
+
+fs_rev_cksum="carrot apple banana Apple Orange Carrot Banana"
+vol_rev_cksum="carrot-vol apple-vol banana-vol Apple-vol Orange-vol"
+vol_rev_cksum="$vol_rev_cksum Carrot-vol Banana-vol"
+
+log_assert "The sort functionality in 'zfs list' works as expected."
+
+#
+# we must be in the C locale here, as running in other locales
+# will make zfs use that locale's sort order.
+#
+LC_ALL=C; export LC_ALL
+
+# sort by creation
+verify_sort \
+ "run_unprivileged $ZFS list -H -r -o name -s creation -t filesystem $TESTPOOL/$TESTFS" \
+ "$fs_creation" "creation date"
+if is_global_zone ; then
+ verify_sort \
+ "run_unprivileged $ZFS list -H -r -o name -s creation -t volume $TESTPOOL/$TESTFS" \
+ "$vol_creation" "creation date"
+fi
+verify_sort \
+ "run_unprivileged $ZFS list -H -r -o name -s creation -t snapshot $TESTPOOL/$TESTFS" \
+ "$snap_creation" "creation date"
+
+# sort by checksum
+verify_sort \
+ "run_unprivileged $ZFS list -H -r -o name -s checksum -t filesystem $TESTPOOL/$TESTFS" \
+ "$fs_cksum" "checksum"
+if is_global_zone ; then
+ verify_sort \
+ "run_unprivileged $ZFS list -H -r -o name -s checksum -t volume $TESTPOOL/$TESTFS" \
+ "$vol_cksum" "checksum"
+fi
+verify_sort \
+ "run_unprivileged $ZFS list -H -r -o name -s checksum -t snapshot $TESTPOOL/$TESTFS" \
+ "$snap_cksum" "checksum"
+verify_sort \
+ "run_unprivileged $ZFS list -H -r -o name -S checksum -t snapshot $TESTPOOL/$TESTFS" \
+ "$snap_cksum" "checksum"
+
+# sort by name
+verify_sort \
+ "run_unprivileged $ZFS list -H -r -o name -s name -t filesystem $TESTPOOL/$TESTFS" \
+ "$fs_name" "name"
+if is_global_zone ; then
+ verify_sort \
+ "run_unprivileged $ZFS list -H -r -o name -s name -t volume $TESTPOOL/$TESTFS" \
+ "$vol_name" "name"
+fi
+verify_sort \
+ "run_unprivileged $ZFS list -H -r -o name -s name -t snapshot $TESTPOOL/$TESTFS" \
+ "$snap_name" "name"
+
+# reverse sort by creation
+verify_reverse_sort \
+ "run_unprivileged $ZFS list -H -r -o name -S creation -t filesystem $TESTPOOL/$TESTFS" \
+ "$fs_creation" "creation date"
+if is_global_zone ; then
+ verify_reverse_sort \
+ "run_unprivileged $ZFS list -H -r -o name -S creation -t volume $TESTPOOL/$TESTFS" \
+ "$vol_creation" "creation date"
+fi
+verify_reverse_sort \
+ "run_unprivileged $ZFS list -H -r -o name -S creation -t snapshot $TESTPOOL/$TESTFS" \
+ "$snap_creation" "creation date"
+
+# reverse sort by checksum
+verify_reverse_sort \
+ "run_unprivileged $ZFS list -H -r -o name -S checksum -t filesystem $TESTPOOL/$TESTFS" \
+ "$fs_rev_cksum" "checksum"
+if is_global_zone ; then
+ verify_reverse_sort \
+ "run_unprivileged $ZFS list -H -r -o name -S checksum -t volume $TESTPOOL/$TESTFS" \
+ "$vol_rev_cksum" "checksum"
+fi
+
+# reverse sort by name
+verify_reverse_sort \
+ "run_unprivileged $ZFS list -H -r -o name -S name -t filesystem $TESTPOOL/$TESTFS"\
+ "$fs_name" "name"
+if is_global_zone ; then
+ verify_reverse_sort \
+ "run_unprivileged $ZFS list -H -r -o name -S name -t volume $TESTPOOL/$TESTFS"\
+ "$vol_name" "name"
+fi
+verify_reverse_sort \
+ "run_unprivileged $ZFS list -H -r -o name -S name -t snapshot $TESTPOOL/$TESTFS"\
+ "$snap_name" "name"
+
+log_pass "The sort functionality in 'zfs list' works as expected."
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_003_pos.ksh b/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_003_pos.ksh
new file mode 100644
index 000000000000..3c98567cadd8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_003_pos.ksh
@@ -0,0 +1,86 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_list_003_pos
+#
+# DESCRIPTION:
+# Verify 'zfs list -r' could recursively display any children
+# of the dataset.
+#
+# STRATEGY:
+# 1. Prepare a set of datasets by hierarchy.
+# 2. Execute 'zfs list -r' at the top of these datasets.
+# 3. Verify all child datasets are all be shown.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-05-24)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function cleanup
+{
+ if [[ -f $tmpfile ]]; then
+ $RM -f $tmpfile
+ fi
+}
+
+verify_runnable "both"
+log_onexit cleanup
+
+log_assert "Verify 'zfs list -r' could display any children recursively."
+
+tmpfile=$TMPDIR/zfslist.out.${TESTCASE_ID}
+children="$TESTPOOL/$TESTFS"
+
+for fs in $DATASETS ; do
+ children="$children $TESTPOOL/$TESTFS/$fs"
+done
+
+cd /tmp
+
+for path in $TESTPOOL/$TESTFS $TESTDIR ./../$TESTDIR ; do
+ run_unprivileged $ZFS list -rH -o name $path > $tmpfile
+ for fs in $children ; do
+ $GREP -q "^${fs}$" $tmpfile
+ if (( $? != 0 )); then
+ cat $tmpfile
+ log_fail "$fs not shown in the output list."
+ fi
+ done
+done
+
+log_pass "'zfs list -r' could display any children recursively."
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_004_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_004_neg.ksh
new file mode 100644
index 000000000000..d2c86e266850
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_004_neg.ksh
@@ -0,0 +1,72 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_list_004_neg
+#
+# DESCRIPTION:
+# Verify 'zfs list [-r]' should fail while
+# * the given dataset does not exist
+# * the given path does not exist.
+# * the given path does not belong to zfs.
+#
+# STRATEGY:
+# 1. Create an array of invalid options.
+# 2. Execute each element in the array.
+# 3. Verify failure is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-05-24)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify 'zfs list [-r]' should fail while the given " \
+ "dataset/path does not exist or not belong to zfs."
+
+paths="$TESTPOOL/NONEXISTFS $TESTPOOL/$TESTFS/NONEXISTFS \
+ /$TESTDIR/NONEXISTFS /dev"
+
+cd /tmp
+
+for fs in $paths ; do
+ log_mustnot run_unprivileged $ZFS list $fs
+ log_mustnot run_unprivileged $ZFS list -r $fs
+done
+
+log_pass "'zfs list [-r]' fails while the given dataset/path does not exist " \
+ "or not belong to zfs."
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_005_pos.ksh b/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_005_pos.ksh
new file mode 100644
index 000000000000..fa023d991a72
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_005_pos.ksh
@@ -0,0 +1,192 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/cli_user/zfs_list/zfs_list.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_list_005_pos
+#
+# DESCRIPTION:
+# Verify 'zfs list' evaluate multiple '-s' options from left to right
+# in decreasing order of importance.
+#
+# STRATEGY:
+# 1. Setting user property f:color for filesystem and volume.
+# 2. Setting user property f:amount for filesystem and volume.
+# 3. Setting reservation for filesystem and volume
+# 3. Verify 'zfs list' evaluated multiple -s options from left to right.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-07-23)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify 'zfs list' evaluate multiple '-s' options " \
+ "from left to right in decreasing order of importance."
+
+COLOR="red yellow green blue red yellow white"
+AMOUNT="0217 812 0217 0781 7 1364 687"
+RESERVATION="2048K 1024 2048K 512K 16M 3072 128K"
+
+basefs=$TESTPOOL/$TESTFS
+typeset -i n=0
+for ds in $DATASETS ; do
+ color=$($ECHO $COLOR | $AWK '{print $1}')
+ log_must $ZFS set f:color=$color $basefs/$ds
+ if is_global_zone ; then
+ log_must $ZFS set f:color=$color $basefs/${ds}-vol
+ fi
+ eval COLOR=\${COLOR#$color }
+
+ amount=$($ECHO $AMOUNT | $AWK '{print $1}')
+ log_must $ZFS set f:amount=$amount $basefs/$ds
+ if is_global_zone ; then
+ log_must $ZFS set f:amount=$amount $basefs/${ds}-vol
+ fi
+ eval AMOUNT=\${AMOUNT#$amount }
+
+ reserv=$($ECHO $RESERVATION | $AWK '{print $1}')
+ log_must $ZFS set reservation=$reserv $basefs/$ds
+ if is_global_zone ; then
+ log_must $ZFS set reservation=$reserv $basefs/${ds}-vol
+ fi
+ eval RESERVATION=\${RESERVATION#$reserv }
+done
+
+#
+# we must be in the C locale here, as running in other locales
+# will make zfs use that locale's sort order.
+#
+LC_ALL=C; export LC_ALL
+
+fs_color_amount="Orange Carrot Apple apple carrot banana Banana"
+fs_amount_color="Carrot Apple Orange banana carrot apple Banana"
+fs_color_reserv="Orange Carrot Apple apple carrot Banana banana"
+fs_reserv_color="Banana banana carrot Orange Carrot Apple apple"
+fs_reserv_amount_color="Banana banana carrot Orange Carrot Apple apple"
+
+vol_color_amount="Orange-vol Carrot-vol Apple-vol apple-vol carrot-vol"
+vol_color_amount="$vol_color_amount banana-vol Banana-vol"
+
+vol_amount_color="Carrot-vol Apple-vol Orange-vol banana-vol carrot-vol"
+vol_amount_color="$vol_amount_color apple-vol Banana-vol"
+
+vol_color_reserv="Orange-vol Carrot-vol Apple-vol apple-vol carrot-vol"
+vol_color_reserv="$vol_color_reserv Banana-vol banana-vol"
+
+vol_reserv_color="Banana-vol banana-vol carrot-vol Orange-vol Carrot-vol"
+vol_reserv_color="$vol_reserv_color Apple-vol apple-vol"
+
+vol_reserv_amount_color="Banana-vol banana-vol carrot-vol Orange-vol Carrot-vol"
+vol_reserv_amount_color="$vol_reserv_amount_color Apple-vol apple-vol"
+
+if is_global_zone ; then
+ snap_list="Apple@snap Apple-vol@snap Banana@snap Banana-vol@snap"
+ snap_list="$snap_list Carrot@snap Carrot-vol@snap Orange@snap Orange-vol@snap"
+ snap_list="$snap_list apple@snap apple-vol@snap banana@snap banana-vol@snap"
+ snap_list="$snap_list carrot@snap carrot-vol@snap"
+else
+ snap_list="Apple@snap Banana@snap"
+ snap_list="$snap_list Carrot@snap Orange@snap"
+ snap_list="$snap_list apple@snap banana@snap"
+ snap_list="$snap_list carrot@snap"
+fi
+# Sort by color,amount
+verify_sort \
+ "run_unprivileged $ZFS list -H -r -o name -s f:color -s f:amount -t filesystem $basefs" \
+ "$fs_color_amount" "f:color,f:amount"
+if is_global_zone ; then
+ verify_sort \
+ "run_unprivileged $ZFS list -H -r -o name -s f:color -s f:amount -t volume $basefs" \
+ "$vol_color_amount" "f:color,f:amount"
+fi
+# Sort by amount,color
+verify_sort \
+ "run_unprivileged $ZFS list -H -r -o name -s f:amount -s f:color -t filesystem $basefs" \
+ "$fs_amount_color" "f:amount,f:color"
+if is_global_zone ; then
+ verify_sort \
+ "run_unprivileged $ZFS list -H -r -o name -s f:amount -s f:color -t volume $basefs" \
+ "$vol_amount_color" "f:amount,f:color"
+fi
+
+# Sort by color reservation
+verify_sort \
+ "run_unprivileged $ZFS list -H -r -o name -s f:color -s reserv -t filesystem $basefs" \
+ "$fs_color_reserv" "f:color,reserv"
+if is_global_zone ; then
+ verify_sort \
+ "run_unprivileged $ZFS list -H -r -o name -s f:color -s reserv -t volume $basefs" \
+ "$vol_color_reserv" "f:color,reserv"
+fi
+# Sort by reserv, color
+verify_sort \
+ "run_unprivileged $ZFS list -H -r -o name -s reserv -s f:color -t filesystem $basefs" \
+ "$fs_reserv_color" "reserv,f:color"
+if is_global_zone ; then
+ verify_sort \
+ "run_unprivileged $ZFS list -H -r -o name -s reserv -s f:color -t volume $basefs" \
+ "$vol_reserv_color" "reserv,f:color"
+fi
+
+# Sort by reservation, amount, color
+verify_sort \
+ "run_unprivileged $ZFS list -H -r -o name -s reserv -s reserv -s f:amount -s f:color -t filesystem $basefs" \
+ "$fs_reserv_amount_color" "reserv,:amount,f:color"
+if is_global_zone ; then
+ verify_sort \
+ "run_unprivileged $ZFS list -H -r -o name -s reserv -s f:amount -s f:color -t volume $basefs" \
+ "$vol_reserv_amount_color" "reserv,:amount,f:color"
+fi
+# User property and reservation was not stored in snapshot
+verify_sort \
+ "run_unprivileged $ZFS list -H -r -o name -s f:amount -s f:color -t snapshot $basefs" \
+ "$snap_list" "f:amount,f:color"
+verify_sort \
+ "run_unprivileged $ZFS list -H -r -o name -s f:color -s f:amount -t snapshot $basefs" \
+ "$snap_list" "f:color,f:amount"
+verify_sort \
+ "run_unprivileged $ZFS list -H -r -o name -s f:color -s reserv -t snapshot $basefs" \
+ "$snap_list" "f:color,reservation"
+verify_sort \
+ "run_unprivileged $ZFS list -H -r -o name -s reserv -s f:color -t snapshot $basefs" \
+ "$snap_list" "reserv,f:color"
+verify_sort \
+ "run_unprivileged $ZFS list -H -r -o name -s reserv -s f:amount -s f:color -t snapshot $basefs" \
+ "$snap_list" "reservation,f:color,f:amount"
+
+log_pass "Verify 'zfs list' evaluate multiple '-s' options " \
+ "from left to right in decreasing order of importance."
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_006_pos.ksh b/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_006_pos.ksh
new file mode 100644
index 000000000000..6f0d68c8fd5f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_006_pos.ksh
@@ -0,0 +1,121 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/cli_user/zfs_list/zfs_list.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_list_006_pos
+#
+# DESCRIPTION:
+# Verify 'zfs list' exclude list of snapshot.
+#
+# STRATEGY:
+# 1. Verify snapshot not shown in the list:
+# zfs list [-r]
+# 2. Verify snapshot will be shown by following case:
+# zfs list [-r] -t snapshot
+# zfs list [-r] -t all
+# zfs list <snapshot>
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-04-24)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+if ! pool_prop_exist "listsnapshots" ; then
+ log_unsupported "Pool property of 'listsnapshots' not supported."
+fi
+
+log_assert "Verify 'zfs list' exclude list of snapshot."
+
+set -A hide_options "--" "-t filesystem" "-t volume"
+set -A show_options "--" "-t snapshot" "-t all"
+
+typeset pool=${TESTPOOL%%/*}
+typeset dataset=${DATASETS%% *}
+typeset BASEFS=$TESTPOOL/$TESTFS
+
+for newvalue in "" "on" "off" ; do
+
+ if [[ -n $newvalue ]] && ! is_global_zone ; then
+ break
+ fi
+
+ if [[ -n $newvalue ]] ; then
+ log_must $ZPOOL set listsnapshots=$newvalue $pool
+ fi
+
+ if [ -z "$newvalue" -o "off" = "$newvalue" ] ; then
+ run_unprivileged $ZFS list -r -H -o name $pool | $GREP -q '@' && \
+ log_fail "zfs list included snapshots but shouldn't have"
+ else
+ run_unprivileged $ZFS list -r -H -o name $pool | $GREP -q '@' || \
+ log_fail "zfs list failed to include snapshots"
+ fi
+
+
+ typeset -i i=0
+ while (( i < ${#hide_options[*]} )) ; do
+ run_unprivileged $ZFS list -r -H -o name ${hide_options[i]} $pool | \
+ $GREP -q '@' && \
+ log_fail "zfs list included snapshots but shouldn't have"
+
+ (( i = i + 1 ))
+ done
+
+ (( i = 0 ))
+
+ while (( i < ${#show_options[*]} )) ; do
+ run_unprivileged $ZFS list -r -H -o name ${show_options[i]} $pool | \
+ $GREP -q '@' || \
+ log_fail "zfs list failed to include snapshots"
+ (( i = i + 1 ))
+ done
+
+ output=$(run_unprivileged $ZFS list -H -o name $BASEFS/${dataset}@snap)
+ if [[ $output != $BASEFS/${dataset}@snap ]] ; then
+ log_fail "zfs list not show $BASEFS/${dataset}@snap"
+ fi
+
+ if is_global_zone ; then
+ output=$(run_unprivileged $ZFS list -H -o name $BASEFS/${dataset}-vol@snap)
+ if [[ $output != $BASEFS/${dataset}-vol@snap ]] ; then
+ log_fail "zfs list not show $BASEFS/${dataset}-vol@snap"
+ fi
+ fi
+done
+
+log_pass "'zfs list' exclude list of snapshot."
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_007_pos.ksh b/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_007_pos.ksh
new file mode 100644
index 000000000000..cd18c943714b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_007_pos.ksh
@@ -0,0 +1,106 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_get/zfs_get_list_d.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_list_007_pos
+#
+# DESCRIPTION:
+# 'zfs list -d <n>' should get expected output.
+#
+# STRATEGY:
+# 1. 'zfs list -d <n>' to get the output.
+# 2. 'zfs list -r|egrep' to get the expected output.
+# 3. Compare the two outputs, they shoud be same.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-05-25)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+if ! zfs_get_list_d_supported ; then
+ log_unsupported "'zfs list -d' is not supported."
+fi
+
+set -A fs_type "all" "filesystem" "snapshot"
+if is_global_zone ; then
+ set -A fs_type ${fs_type[*]} "volume"
+fi
+
+function cleanup
+{
+ log_must $RM -f $DEPTH_OUTPUT
+ log_must $RM -f $EXPECT_OUTPUT
+}
+
+log_onexit cleanup
+log_assert "'zfs list -d <n>' should get expected output."
+
+mntpnt=$TMPDIR
+DEPTH_OUTPUT="$mntpnt/depth_output"
+EXPECT_OUTPUT="$mntpnt/expect_output"
+typeset -i old_val=0
+typeset -i j=0
+typeset -i fs=0
+typeset eg_opt="$DEPTH_FS"$
+for dp in ${depth_array[@]}; do
+ (( j=old_val+1 ))
+ while (( j<=dp && j<=MAX_DEPTH )); do
+ eg_opt="$eg_opt""|d""$j"$
+ (( j+=1 ))
+ done
+ (( fs=0 ))
+ while (( fs<${#fs_type[*]} )); do
+ if [[ "$dp" == "0" ]] && \
+ [[ "${fs_type[$fs]}" == "volume" || "${fs_type[$fs]}" == "snapshot" ]]; then
+ log_must eval "run_unprivileged $ZFS list -H -d $dp -o name -t ${fs_type[$fs]} $DEPTH_FS > $DEPTH_OUTPUT"
+ [[ -s "$DEPTH_OUTPUT" ]] && \
+ log_fail "$DEPTH_OUTPUT should be null."
+ log_mustnot run_unprivileged $ZFS list -rH -o name -t ${fs_type[$fs]} $DEPTH_FS | $EGREP -e '$eg_opt'
+ else
+ log_must eval "run_unprivileged $ZFS list -H -d $dp -o name -t ${fs_type[$fs]} $DEPTH_FS > $DEPTH_OUTPUT"
+ log_must eval "run_unprivileged $ZFS list -rH -o name -t ${fs_type[$fs]} $DEPTH_FS | $EGREP -e '$eg_opt' > $EXPECT_OUTPUT"
+ log_must $DIFF $DEPTH_OUTPUT $EXPECT_OUTPUT
+ fi
+ (( fs+=1 ))
+ done
+ (( old_val=dp ))
+done
+
+log_pass "'zfs list -d <n>' should get expected output."
+
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_008_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_008_neg.ksh
new file mode 100644
index 000000000000..0ef4e6f13496
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_008_neg.ksh
@@ -0,0 +1,72 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/zfs_get/zfs_get_list_d.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_list_008_neg
+#
+# DESCRIPTION:
+# A negative depth or a non numeric depth should fail in 'zfs list -d <n>'
+#
+# STRATEGY:
+# 1. Run zfs list -d with negative depth or non numeric depth
+# 2. Verify that zfs list returns error
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-05-26)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+if ! zfs_get_list_d_supported ; then
+ log_unsupported "'zfs list -d' is not supported."
+fi
+
+log_assert "A negative depth or a non numeric depth should fail in 'zfs list -d <n>'"
+
+set -A badargs "a" "AB" "aBc" "2A" "a2b" "aB2" "-1" "-32" "-999"
+
+typeset -i i=0
+while (( i < ${#badargs[*]} ))
+do
+ log_mustnot eval "run_unprivileged $ZFS list -d ${badargs[i]} $DEPTH_FS >/dev/null 2>&1"
+ (( i = i + 1 ))
+done
+
+log_pass "A negative depth or a non numeric depth should fail in 'zfs list -d <n>'"
+
+
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_test.sh b/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_test.sh
new file mode 100755
index 000000000000..0ef2ff4ce01d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zfs_list/zfs_list_test.sh
@@ -0,0 +1,263 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zfs_list_001_pos cleanup
+zfs_list_001_pos_head()
+{
+ atf_set "descr" "Verify 'zfs list [-rH] [-o property[,prop]*] [fs|clct|vol]'."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "require.user" root
+ atf_set "require.config" "unprivileged_user"
+}
+zfs_list_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_list.kshlib
+ . $(atf_get_srcdir)/zfs_list.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_list_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_list_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_list.kshlib
+ . $(atf_get_srcdir)/zfs_list.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_list_002_pos cleanup
+zfs_list_002_pos_head()
+{
+ atf_set "descr" "The sort functionality in 'zfs list' works as expected."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "require.user" root
+ atf_set "require.config" "unprivileged_user"
+}
+zfs_list_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_list.kshlib
+ . $(atf_get_srcdir)/zfs_list.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_list_002_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_list_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_list.kshlib
+ . $(atf_get_srcdir)/zfs_list.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_list_003_pos cleanup
+zfs_list_003_pos_head()
+{
+ atf_set "descr" "Verify 'zfs list -r' could display any children recursively."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "require.user" root
+ atf_set "require.config" "unprivileged_user"
+}
+zfs_list_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_list.kshlib
+ . $(atf_get_srcdir)/zfs_list.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_list_003_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_list_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_list.kshlib
+ . $(atf_get_srcdir)/zfs_list.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_list_004_neg cleanup
+zfs_list_004_neg_head()
+{
+ atf_set "descr" "Verify 'zfs list [-r]' should fail while the givendataset/path does not exist or not belong to zfs."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "require.user" root
+ atf_set "require.config" "unprivileged_user"
+}
+zfs_list_004_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_list.kshlib
+ . $(atf_get_srcdir)/zfs_list.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_list_004_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_list_004_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_list.kshlib
+ . $(atf_get_srcdir)/zfs_list.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_list_005_pos cleanup
+zfs_list_005_pos_head()
+{
+ atf_set "descr" "Verify 'zfs list' evaluate multiple '-s' optionsfrom left to right in decreasing order of importance."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "require.user" root
+ atf_set "require.config" "unprivileged_user"
+}
+zfs_list_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_list.kshlib
+ . $(atf_get_srcdir)/zfs_list.cfg
+
+ verify_disk_count "$DISKS" 1
+ atf_expect_fail "https://www.illumos.org/issues/8599 Snapshots don't preserve user properties"
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_list_005_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_list_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_list.kshlib
+ . $(atf_get_srcdir)/zfs_list.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_list_006_pos cleanup
+zfs_list_006_pos_head()
+{
+ atf_set "descr" "Verify 'zfs list' exclude list of snapshot."
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "require.user" root
+ atf_set "require.config" "unprivileged_user"
+}
+zfs_list_006_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_list.kshlib
+ . $(atf_get_srcdir)/zfs_list.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_list_006_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_list_006_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_list.kshlib
+ . $(atf_get_srcdir)/zfs_list.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_list_007_pos cleanup
+zfs_list_007_pos_head()
+{
+ atf_set "descr" "'zfs list -d <n>' should get expected output."
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "require.user" root
+ atf_set "require.config" "unprivileged_user"
+}
+zfs_list_007_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_list.kshlib
+ . $(atf_get_srcdir)/zfs_list.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_list_007_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_list_007_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_list.kshlib
+ . $(atf_get_srcdir)/zfs_list.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_list_008_neg cleanup
+zfs_list_008_neg_head()
+{
+ atf_set "descr" "A negative depth or a non numeric depth should fail in 'zfs list -d <n>'"
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "require.user" root
+ atf_set "require.config" "unprivileged_user"
+}
+zfs_list_008_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_list.kshlib
+ . $(atf_get_srcdir)/zfs_list.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_list_008_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_list_008_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zfs_list.kshlib
+ . $(atf_get_srcdir)/zfs_list.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zfs_list_001_pos
+ atf_add_test_case zfs_list_002_pos
+ atf_add_test_case zfs_list_003_pos
+ atf_add_test_case zfs_list_004_neg
+ atf_add_test_case zfs_list_005_pos
+ atf_add_test_case zfs_list_006_pos
+ atf_add_test_case zfs_list_007_pos
+ atf_add_test_case zfs_list_008_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/Makefile b/tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/Makefile
new file mode 100644
index 000000000000..4c66f6d8343a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/Makefile
@@ -0,0 +1,17 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_user/zpool_iostat
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zpool_iostat_test
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zpool_iostat.cfg
+${PACKAGE}FILES+= zpool_iostat_003_neg.ksh
+${PACKAGE}FILES+= zpool_iostat_002_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zpool_iostat_001_neg.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/cleanup.ksh
new file mode 100644
index 000000000000..713cb16bb31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/setup.ksh b/tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/setup.ksh
new file mode 100644
index 000000000000..326bc555f71b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/zpool_iostat.cfg b/tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/zpool_iostat.cfg
new file mode 100644
index 000000000000..01ffd4ea1124
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/zpool_iostat.cfg
@@ -0,0 +1,27 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_user/cli.cfg
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/zpool_iostat_001_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/zpool_iostat_001_neg.ksh
new file mode 100644
index 000000000000..4c5b219569ea
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/zpool_iostat_001_neg.ksh
@@ -0,0 +1,72 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_iostat_001_neg
+#
+# DESCRIPTION:
+# Verify that 'zpool iostat' can be executed as non-root.
+#
+# STRATEGY:
+# 1. Create an array of options.
+# 2. Execute each element of the array.
+# 3. Verify that a success is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+typeset testpool
+if is_global_zone ; then
+ testpool=$TESTPOOL
+else
+ testpool=${TESTPOOL%%/*}
+fi
+
+set -A args "iostat" "iostat $testpool"
+
+log_assert "zpool iostat [pool_name ...] [interval]"
+
+typeset -i i=0
+while [[ $i -lt ${#args[*]} ]]; do
+ log_must run_unprivileged $ZPOOL ${args[i]}
+ ((i = i + 1))
+done
+
+log_pass "The sub-command 'iostat' succeeds as non-root."
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/zpool_iostat_002_pos.ksh b/tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/zpool_iostat_002_pos.ksh
new file mode 100644
index 000000000000..4b21be715fd5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/zpool_iostat_002_pos.ksh
@@ -0,0 +1,79 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_iostat_002_pos
+#
+# DESCRIPTION:
+# Verify that 'zpool iostat [interval [count]' can be executed as non-root.
+#
+# STRATEGY:
+# 1. set the interval=2 and count=3
+# 2. sleep 30 seconds
+# 3. Verify that the output have 3 record.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-18)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+typeset tmpfile=$TMPDIR/zfsiostat.out.${TESTCASE_ID}
+typeset -i stat_count=0
+
+function cleanup
+{
+ if [[ -f $tmpfile ]]; then
+ $RM -f $tmpfile
+ fi
+}
+
+log_onexit cleanup
+log_assert "zpool iostat [pool_name ...] [interval] [count]"
+
+if ! is_global_zone ; then
+ TESTPOOL=${TESTPOOL%%/*}
+fi
+
+run_unprivileged $ZPOOL iostat $TESTPOOL 2 3 > $tmpfile 2>&1
+stat_count=$($GREP $TESTPOOL $tmpfile | $WC -l)
+
+if [[ $stat_count -ne 3 ]]; then
+ log_fail "zpool iostat [pool_name] [interval] [count] failed"
+fi
+
+log_pass "zpool iostat [pool_name ...] [interval] [count] passed"
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/zpool_iostat_003_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/zpool_iostat_003_neg.ksh
new file mode 100644
index 000000000000..4159cfd27a7d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/zpool_iostat_003_neg.ksh
@@ -0,0 +1,73 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_iostat_003_neg
+#
+# DESCRIPTION:
+# Executing 'zpool iostat' command with bad options fails.
+#
+# STRATEGY:
+# 1. Create an array of badly formed 'zpool iostat' options.
+# 2. Execute each element of the array.
+# 3. Verify an error code is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-18)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+typeset testpool
+if is_global_zone ; then
+ testpool=$TESTPOOL
+else
+ testpool=${TESTPOOL%%/*}
+fi
+
+set -A args "" "-?" "-f" "nonexistpool" "$TESTPOOL/$TESTFS" \
+ "$testpool 1.23" "$testpool 0" "$testpool -1" "$testpool 1 0" \
+ "$testpool 0 0"
+
+log_assert "Executing 'zpool iostat' with bad options fails"
+
+typeset -i i=1
+while [[ $i -lt ${#args[*]} ]]; do
+ log_mustnot $ZPOOL iostat ${args[i]}
+ ((i = i + 1))
+done
+
+log_pass "Executing 'zpool iostat' with bad options fails"
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/zpool_iostat_test.sh b/tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/zpool_iostat_test.sh
new file mode 100755
index 000000000000..f11a93da7336
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zpool_iostat/zpool_iostat_test.sh
@@ -0,0 +1,101 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zpool_iostat_001_neg cleanup
+zpool_iostat_001_neg_head()
+{
+ atf_set "descr" "zpool iostat [pool_name ...] [interval]"
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zpool_iostat_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_iostat.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_iostat_001_neg.ksh || atf_fail "Testcase failed"
+}
+zpool_iostat_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_iostat.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_iostat_002_pos cleanup
+zpool_iostat_002_pos_head()
+{
+ atf_set "descr" "zpool iostat [pool_name ...] [interval] [count]"
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zpool_iostat_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_iostat.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_iostat_002_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_iostat_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_iostat.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_iostat_003_neg
+zpool_iostat_003_neg_head()
+{
+ atf_set "descr" "Executing 'zpool iostat' with bad options fails"
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "require.user" unprivileged
+}
+zpool_iostat_003_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_iostat.cfg
+
+ ksh93 $(atf_get_srcdir)/zpool_iostat_003_neg.ksh || atf_fail "Testcase failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zpool_iostat_001_neg
+ atf_add_test_case zpool_iostat_002_pos
+ atf_add_test_case zpool_iostat_003_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zpool_list/Makefile b/tests/sys/cddl/zfs/tests/cli_user/zpool_list/Makefile
new file mode 100644
index 000000000000..b50c82ccc1b4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zpool_list/Makefile
@@ -0,0 +1,16 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/cli_user/zpool_list
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zpool_list_test
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zpool_list_001_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zpool_list.cfg
+${PACKAGE}FILES+= zpool_list_002_neg.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zpool_list/cleanup.ksh b/tests/sys/cddl/zfs/tests/cli_user/zpool_list/cleanup.ksh
new file mode 100644
index 000000000000..713cb16bb31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zpool_list/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zpool_list/setup.ksh b/tests/sys/cddl/zfs/tests/cli_user/zpool_list/setup.ksh
new file mode 100644
index 000000000000..326bc555f71b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zpool_list/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zpool_list/zpool_list.cfg b/tests/sys/cddl/zfs/tests/cli_user/zpool_list/zpool_list.cfg
new file mode 100644
index 000000000000..01ffd4ea1124
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zpool_list/zpool_list.cfg
@@ -0,0 +1,27 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_user/cli.cfg
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zpool_list/zpool_list_001_pos.ksh b/tests/sys/cddl/zfs/tests/cli_user/zpool_list/zpool_list_001_pos.ksh
new file mode 100644
index 000000000000..d0b1f6f50883
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zpool_list/zpool_list_001_pos.ksh
@@ -0,0 +1,73 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_user/cli_user.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_list_001_pos
+#
+# DESCRIPTION:
+# Verify that 'zpool list' succeeds as non-root.
+#
+# STRATEGY:
+# 1. Create an array of options.
+# 2. Execute each element of the array.
+# 3. Verify the command succeeds.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+if ! is_global_zone ; then
+ TESTPOOL=${TESTPOOL%%/*}
+fi
+
+set -A args "list $TESTPOOL" "list -H $TESTPOOL" "list" "list -H" \
+ "list -H -o name $TESTPOOL" "list -o name $TESTPOOL" \
+ "list -o name,size,capacity,health,altroot $TESTPOOL" \
+ "list -H -o name,size,capacity,health,altroot $TESTPOOL"
+
+log_assert "zpool list [-H] [-o filed[,filed]*] [<pool_name> ...]"
+
+typeset -i i=0
+while [[ $i -lt ${#args[*]} ]]; do
+ log_must run_unprivileged $ZPOOL ${args[i]}
+
+ ((i = i + 1))
+done
+
+log_pass "The sub-command 'list' succeeds as non-root."
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zpool_list/zpool_list_002_neg.ksh b/tests/sys/cddl/zfs/tests/cli_user/zpool_list/zpool_list_002_neg.ksh
new file mode 100644
index 000000000000..eefb463abb52
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zpool_list/zpool_list_002_neg.ksh
@@ -0,0 +1,65 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zpool_list_002_neg
+#
+# DESCRIPTION:
+# Executing 'zpool list' command with bad options fails.
+#
+# STRATEGY:
+# 1. Create an array of badly formed 'zpool list' options.
+# 2. Execute each element of the array.
+# 3. Verify an error code is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-18)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+set -A args "" "-?" "-f" "-o" \
+ "-o fakeproperty" "-o name,size,fakeproperty"
+
+log_assert "Executing 'zpool list' with bad options fails"
+
+typeset -i i=1
+while [[ $i -lt ${#args[*]} ]]; do
+ log_mustnot $ZPOOL list ${args[i]}
+ ((i = i + 1))
+done
+
+log_pass "Executing 'zpool list' with bad options fails"
diff --git a/tests/sys/cddl/zfs/tests/cli_user/zpool_list/zpool_list_test.sh b/tests/sys/cddl/zfs/tests/cli_user/zpool_list/zpool_list_test.sh
new file mode 100755
index 000000000000..7c5c57dc2f45
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/cli_user/zpool_list/zpool_list_test.sh
@@ -0,0 +1,73 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zpool_list_001_pos cleanup
+zpool_list_001_pos_head()
+{
+ atf_set "descr" "zpool list [-H] [-o filed[,filed]*] [<pool_name> ...]"
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "require.user" root
+ atf_set "require.config" unprivileged_user
+}
+zpool_list_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_list.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zpool_list_001_pos.ksh || atf_fail "Testcase failed"
+}
+zpool_list_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_list.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zpool_list_002_neg
+zpool_list_002_neg_head()
+{
+ atf_set "descr" "Executing 'zpool list' with bad options fails"
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "require.user" unprivileged
+}
+zpool_list_002_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zpool_list.cfg
+
+ ksh93 $(atf_get_srcdir)/zpool_list_002_neg.ksh || atf_fail "Testcase failed"
+}
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zpool_list_001_pos
+ atf_add_test_case zpool_list_002_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/compression/Makefile b/tests/sys/cddl/zfs/tests/compression/Makefile
new file mode 100644
index 000000000000..3ed64fc3460e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/compression/Makefile
@@ -0,0 +1,18 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/compression
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= compression_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= compress_004_pos.ksh
+${PACKAGE}FILES+= compress_001_pos.ksh
+${PACKAGE}FILES+= compress_003_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= compress.cfg
+${PACKAGE}FILES+= cleanup.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/compression/cleanup.ksh b/tests/sys/cddl/zfs/tests/compression/cleanup.ksh
new file mode 100644
index 000000000000..f85fc90ab195
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/compression/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+default_container_cleanup
diff --git a/tests/sys/cddl/zfs/tests/compression/compress.cfg b/tests/sys/cddl/zfs/tests/compression/compress.cfg
new file mode 100644
index 000000000000..e659d11ead02
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/compression/compress.cfg
@@ -0,0 +1,34 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export TESTFILE0=testfile0.${TESTCASE_ID}
+export TESTFILE1=testfile1.${TESTCASE_ID}
+
+export BLOCKSZ=8192
+export NUM_WRITES=65536
+export DATA=13
+export STF_TIMEOUT=1200
+
diff --git a/tests/sys/cddl/zfs/tests/compression/compress_001_pos.ksh b/tests/sys/cddl/zfs/tests/compression/compress_001_pos.ksh
new file mode 100644
index 000000000000..ffc2a8658436
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/compression/compress_001_pos.ksh
@@ -0,0 +1,81 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: compress_001_pos
+#
+# DESCRIPTION:
+# Create two files of exactly the same size. One with compression
+# and one without. Ensure the compressed file is smaller.
+#
+# STRATEGY:
+# Use "zfs set" to turn on compression and create files before
+# and after the set call. The compressed file should be smaller.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+typeset OP=create
+
+log_assert "Ensure that compressed files are smaller."
+
+log_note "Ensure compression is off"
+log_must $ZFS set compression=off $TESTPOOL/$TESTFS
+
+log_note "Writing file without compression..."
+log_must $FILE_WRITE -o $OP -f $TESTDIR/$TESTFILE0 -b $BLOCKSZ \
+ -c $NUM_WRITES -d $DATA
+
+log_note "Add compression property to the dataset and write another file"
+log_must $ZFS set compression=on $TESTPOOL/$TESTFS
+
+log_must $FILE_WRITE -o $OP -f $TESTDIR/$TESTFILE1 -b $BLOCKSZ \
+ -c $NUM_WRITES -d $DATA
+
+log_must $SYNC $TESTDIR
+
+FILE0_BLKS=`$DU -k $TESTDIR/$TESTFILE0 | $AWK '{ print $1}'`
+FILE1_BLKS=`$DU -k $TESTDIR/$TESTFILE1 | $AWK '{ print $1}'`
+
+if [[ $FILE0_BLKS -le $FILE1_BLKS ]]; then
+ log_fail "$TESTFILE0 is smaller than $TESTFILE1" \
+ "($FILE0_BLKS <= $FILE1_BLKS)"
+fi
+
+log_pass "$TESTFILE0 is bigger than $TESTFILE1 ($FILE0_BLKS > $FILE1_BLKS)"
diff --git a/tests/sys/cddl/zfs/tests/compression/compress_003_pos.ksh b/tests/sys/cddl/zfs/tests/compression/compress_003_pos.ksh
new file mode 100644
index 000000000000..2f619b284d76
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/compression/compress_003_pos.ksh
@@ -0,0 +1,104 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: compress_003_pos
+#
+# DESCRIPTION:
+# With 'compression' or 'compress' set, changing filesystem blocksize cannot
+# cause system panic
+#
+# STRATEGY:
+# 1. Set 'compression' or "compress" to on
+# 2. Set different blocksize with ZFS filesystem
+# 3. Use 'mkfile' create single block and multi-block files
+# 4. Verify the system continued work
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-07-26)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ $RM -f $TESTDIR/*
+}
+
+log_assert "Changing blocksize doesn't casue system panic with compression settings"
+log_onexit cleanup
+
+fs=$TESTPOOL/$TESTFS
+single_blk_file=$TESTDIR/singleblkfile.${TESTCASE_ID}
+multi_blk_file=$TESTDIR/multiblkfile.${TESTCASE_ID}
+typeset -i blksize=512
+typeset -i fsize=0
+typeset -i offset=0
+
+for propname in "compression" "compress"
+do
+ for value in $(get_compress_opts zfs_compress)
+ do
+ log_must $ZFS set $propname=$value $fs
+ if [[ $value == "gzip-6" ]]; then
+ value="gzip"
+ fi
+ real_val=$(get_prop $propname $fs)
+ [[ $real_val != $value ]] && \
+ log_fail "Set property $propname=$value failed."
+
+ (( blksize = 512 ))
+ while (( blksize <= 131072 )); do
+ log_must $ZFS set recordsize=$blksize $fs
+ (( offset = $RANDOM ))
+ if (( offset > blksize )); then
+ (( offset = offset % blksize ))
+ fi
+ if (( (offset % 2) == 0 )); then
+ #keep offset as non-power-of-2
+ (( offset = offset + 1 ))
+ fi
+ (( fsize = offset ))
+ log_must $MKFILE $fsize $single_blk_file
+ (( fsize = blksize + offset ))
+ log_must $MKFILE $fsize $multi_blk_file
+
+ (( blksize = blksize * 2 ))
+ done
+ done
+done
+
+log_pass "The system works as expected while changing blocksize with compression settings"
diff --git a/tests/sys/cddl/zfs/tests/compression/compress_004_pos.ksh b/tests/sys/cddl/zfs/tests/compression/compress_004_pos.ksh
new file mode 100644
index 000000000000..8256df3f86f0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/compression/compress_004_pos.ksh
@@ -0,0 +1,141 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: compress_004_pos
+#
+# DESCRIPTION:
+# With 'compression' set, a file with non-power-of-2 blocksize storage space
+# can be freed as will normally.
+#
+# STRATEGY:
+# 1. Set 'compression' or 'compress' to on or lzjb
+# 2. Set different recordsize with ZFS filesystem
+# 3. Repeatedly using 'randfree_file' to create a file and then free its
+# storage space with different range, the system should work normally.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-07-26)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ $RM -f $TESTDIR/*
+}
+
+function create_free_testing #<file size> <file>
+{
+ typeset -i fsz=$1
+ typeset file=$2
+ typeset -i start=0
+ typeset -i len=0
+ typeset -i dist=0
+
+ for start in 0 `expr $RANDOM % $fsz`
+ do
+ (( dist = fsz - start ))
+ for len in `expr $RANDOM % $dist` $dist \
+ `expr $start + $dist`; do
+ log_must $RANDFREE_FILE -l fsz -s $start \
+ -n $len $file
+ [[ -e $file ]] && \
+ log_must $RM -f $file
+ done
+ done
+}
+
+
+log_assert "Creating non-power-of-2 blocksize file and freeing the file \
+ storage space at will should work normally with compression setting"
+log_onexit cleanup
+
+fs=$TESTPOOL/$TESTFS
+single_blk_file=$TESTDIR/singleblkfile.${TESTCASE_ID}
+multi_blk_file=$TESTDIR/multiblkfile.${TESTCASE_ID}
+typeset -i blksize=512
+typeset -i fsize=0
+typeset -i avail=0
+typeset -i blknum=0
+
+for propname in "compression" "compress"
+do
+ for value in $(get_compress_opts zfs_compress)
+ do
+ log_must $ZFS set compression=$value $fs
+ real_val=$(get_prop $propname $fs)
+ if [[ $value == "gzip-6" ]]; then
+ value="gzip"
+ fi
+ [[ $real_val != $value ]] && \
+ log_fail "Set property $propname=$value failed."
+
+ (( blksize = 512 ))
+ while (( blksize <= 131072 )); do
+ log_must $ZFS set recordsize=$blksize $fs
+
+ # doing single block testing
+ (( fsize = $RANDOM ))
+ if (( fsize > blksize )); then
+ (( fsize = fsize % blksize ))
+ fi
+ if (( (fsize % 2) == 0 )); then
+ #make sure fsize is non-power-of-2
+ (( fsize = fsize + 1 ))
+ fi
+ create_free_testing $fsize $single_blk_file
+
+ # doing multiple blocks testing
+ avail=$(get_prop available $fs)
+ (( blknum = avail / blksize ))
+ # we just test <10 multi-blocks to limit testing time
+ (( blknum = blknum % 9 ))
+ while (( blknum < 2 )); do
+ (( blknum = blknum + $RANDOM % 9 ))
+ done
+ if (( (blknum % 2) == 0 )); then
+ (( blknum = blknum + 1 )) # keep blknum as odd
+ fi
+ (( fsize = blknum * blksize ))
+ create_free_testing $fsize $multi_blk_file
+
+ (( blksize = blksize * 2 ))
+ done
+ done
+done
+
+log_pass "Creating and freeing non-power-of-2 blocksize file work as expected."
diff --git a/tests/sys/cddl/zfs/tests/compression/compression_test.sh b/tests/sys/cddl/zfs/tests/compression/compression_test.sh
new file mode 100755
index 000000000000..6d1921c5bf6e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/compression/compression_test.sh
@@ -0,0 +1,108 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case compress_001_pos cleanup
+compress_001_pos_head()
+{
+ atf_set "descr" "Ensure that compressed files are smaller."
+ atf_set "require.progs" "ksh93 zfs"
+}
+compress_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/compress.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/compress_001_pos.ksh || atf_fail "Testcase failed"
+}
+compress_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/compress.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case compress_003_pos cleanup
+compress_003_pos_head()
+{
+ atf_set "descr" "Changing blocksize doesn't casue system panic with compression settings"
+ atf_set "require.progs" "ksh93 zfs"
+}
+compress_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/compress.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/compress_003_pos.ksh || atf_fail "Testcase failed"
+}
+compress_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/compress.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case compress_004_pos cleanup
+compress_004_pos_head()
+{
+ atf_set "descr" "Creating non-power-of-2 blocksize file and freeing the filestorage space at will should work normally with compression setting"
+ atf_set "require.progs" "ksh93 zfs"
+}
+compress_004_pos_body()
+{
+ if [[ $(uname) = "FreeBSD" ]]; then
+ atf_skip "FreeBSD does not implement F_FREESP in fcntl()"
+ fi
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/compress.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/compress_004_pos.ksh || atf_fail "Testcase failed"
+}
+compress_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/compress.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case compress_001_pos
+ atf_add_test_case compress_003_pos
+ atf_add_test_case compress_004_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/compression/setup.ksh b/tests/sys/cddl/zfs/tests/compression/setup.ksh
new file mode 100644
index 000000000000..67e9a299edb8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/compression/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_container_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/ctime/Makefile b/tests/sys/cddl/zfs/tests/ctime/Makefile
new file mode 100644
index 000000000000..d7cf76d4517c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/ctime/Makefile
@@ -0,0 +1,18 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/ctime
+FILESDIR=${TESTSDIR}
+BINDIR=${TESTSDIR}
+
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= ctime.cfg
+
+PROG= ctime_001_pos
+MAN=
+ATF_TESTS_KSH93= ctime_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/ctime/cleanup.ksh b/tests/sys/cddl/zfs/tests/ctime/cleanup.ksh
new file mode 100644
index 000000000000..713cb16bb31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/ctime/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/ctime/ctime.cfg b/tests/sys/cddl/zfs/tests/ctime/ctime.cfg
new file mode 100644
index 000000000000..a2d61f141178
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/ctime/ctime.cfg
@@ -0,0 +1,27 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export TESTFILE=testfile${TESTCASE_ID}
diff --git a/tests/sys/cddl/zfs/tests/ctime/ctime_001_pos.c b/tests/sys/cddl/zfs/tests/ctime/ctime_001_pos.c
new file mode 100644
index 000000000000..f3acb393cc56
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/ctime/ctime_001_pos.c
@@ -0,0 +1,379 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <utime.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#define ST_ATIME 0
+#define ST_CTIME 1
+#define ST_MTIME 2
+
+#define ALL_MODE (mode_t)(S_IRWXU|S_IRWXG|S_IRWXO)
+
+typedef struct timetest {
+ int type;
+ char *name;
+ int (*func)(const char *pfile);
+} timetest_t;
+
+#ifdef __stc_assertion__
+
+/*
+ * ID: ctime_001_pos
+ *
+ * DESCRIPTION:
+ * Verify time will be changed correctly according to relevant operating.
+ *
+ * STRATEGY:
+ * 1. Define time test array.
+ * 2. loop each item in this array.
+ * 3. Verify the time will be changed after relevant operating.
+ *
+ * TESTABILITY: explicit
+ *
+ * TEST_AUTOMATION_LEVEL: automated
+ *
+ * CODING_STATUS: COMPLETED (2007-01-30)
+ *
+ */
+
+#endif /* __stc_assertion__ */
+
+/*
+ * Get file specific time information.
+ */
+int get_file_time(char *pfile, int what, time_t *ptr);
+
+int do_read(const char *pfile);
+int do_write(const char *pfile);
+int do_link(const char *pfile);
+int do_creat(const char *pfile);
+int do_utime(const char *pfile);
+int do_chmod(const char *pfile);
+int do_chown(const char *pfile);
+
+static char tfile[BUFSIZ] = { 0 };
+static char msg[BUFSIZ] = { 0 };
+
+static timetest_t timetest_table[] = {
+ { ST_ATIME, "st_atime", do_read },
+ { ST_ATIME, "st_atime", do_utime },
+ { ST_MTIME, "st_mtime", do_creat },
+ { ST_MTIME, "st_mtime", do_write },
+ { ST_MTIME, "st_mtime", do_utime },
+ { ST_CTIME, "st_ctime", do_creat },
+ { ST_CTIME, "st_ctime", do_write },
+ { ST_CTIME, "st_ctime", do_chmod },
+ { ST_CTIME, "st_ctime", do_chown },
+ { ST_CTIME, "st_ctime", do_link },
+ { ST_CTIME, "st_ctime", do_utime },
+};
+
+#define NCOMMAND (sizeof (timetest_table) / sizeof (timetest_table[0]))
+
+int
+main(int argc, char *argv[])
+{
+ int i, ret, fd;
+ const char *env_names[2] = {"TESTDIR", "TESTFILE"};
+ char *env_vals[2];
+
+ /*
+ * Get envirnment variable value
+ */
+ for (i = 0; i < sizeof (env_names) / sizeof (char *); i++) {
+ if ((env_vals[i] = getenv(env_names[i])) == NULL) {
+ fprintf(stderr, "getenv(%s) returned NULL\n",
+ env_names[i]);
+ exit(1);
+ }
+ }
+ (void) snprintf(tfile, sizeof (tfile), "%s/%s", env_vals[0],
+ env_vals[1]);
+
+ /*
+ * If the test file is existing, remove it firstly
+ */
+ if (access(tfile, F_OK) == 0) {
+ unlink(tfile);
+ }
+ fd = open(tfile, O_WRONLY | O_CREAT | O_TRUNC, ALL_MODE);
+ if (fd < 0) {
+ perror("open");
+ exit(1);
+ }
+ (void) close(fd);
+
+ for (i = 0; i < NCOMMAND; i++) {
+ time_t t1, t2;
+
+ /*
+ * Get original time before operating.
+ */
+ ret = get_file_time(tfile, timetest_table[i].type, &t1);
+ if (ret != 0) {
+ fprintf(stderr,
+ "ERROR: get_file_time(%s, %d, &t1) returned %d\n",
+ tfile, timetest_table[i].type, ret);
+ exit(1);
+ }
+
+ /*
+ * Sleep 2 seconds to be sure that the timeofday has changed,
+ * then invoke command on given file
+ */
+ sleep(2);
+ timetest_table[i].func(tfile);
+
+ /*
+ * Get time after operating.
+ */
+ ret = get_file_time(tfile, timetest_table[i].type, &t2);
+ if (ret != 0) {
+ fprintf(stderr, "get_file_time(%s, %d, &t2)\n",
+ tfile, timetest_table[i].type);
+ exit(1);
+ }
+
+ if (t1 == t2) {
+ fprintf(stderr, "%s: t1(%ld) == t2(%ld)\n",
+ timetest_table[i].name, (long)t1, (long)t2);
+ exit(1);
+ }
+ }
+
+ (void) unlink(tfile);
+
+ return (0);
+}
+
+int
+get_file_time(char *pfile, int what, time_t *ptr)
+{
+ struct stat stat_buf;
+
+ if (pfile == NULL || ptr == NULL) {
+ return (-1);
+ }
+
+ if (stat(pfile, &stat_buf) == -1) {
+ return (-1);
+ }
+
+ switch (what) {
+ case ST_ATIME:
+ *ptr = stat_buf.st_atime;
+ return (0);
+ case ST_CTIME:
+ *ptr = stat_buf.st_ctime;
+ return (0);
+ case ST_MTIME:
+ *ptr = stat_buf.st_mtime;
+ return (0);
+ default:
+ return (-1);
+ }
+}
+
+int
+do_read(const char *pfile)
+{
+ int fd, ret = 0;
+ char buf[BUFSIZ] = { 0 };
+
+ if (pfile == NULL) {
+ return (-1);
+ }
+
+ if ((fd = open(pfile, O_RDONLY, ALL_MODE)) == -1) {
+ return (-1);
+ }
+ if (read(fd, buf, sizeof (buf)) == -1) {
+ ret = errno;
+ }
+ (void) close(fd);
+
+ if (ret != 0) {
+ fprintf(stderr, "read(%d, buf, %zu)\n", fd, sizeof (buf));
+ exit(1);
+ }
+
+ return (ret);
+}
+
+int
+do_write(const char *pfile)
+{
+ int fd, ret = 0;
+ char buf[BUFSIZ] = "call function do_write()";
+
+ if (pfile == NULL) {
+ return (-1);
+ }
+
+ if ((fd = open(pfile, O_WRONLY, ALL_MODE)) == -1) {
+ return (-1);
+ }
+ if (write(fd, buf, strlen(buf)) == -1) {
+ ret = errno;
+ }
+ (void) close(fd);
+
+ if (ret != 0) {
+ fprintf(stderr, "write(%d, buf, %zu)\n", fd, strlen(buf));
+ exit(1);
+ }
+
+ return (ret);
+}
+
+int
+do_link(const char *pfile)
+{
+ int ret = 0;
+ char link_file[BUFSIZ] = { 0 };
+ char *ptr = link_file;
+
+ if (pfile == NULL) {
+ return (-1);
+ }
+
+ /*
+ * Figure out source file directory name, and create
+ * the link file in the same directory.
+ */
+ snprintf(link_file, sizeof (link_file), "%s", pfile);
+ ptr = strrchr(link_file, '/');
+ snprintf(ptr + 1,
+ sizeof (link_file) - (ptr + 1 - link_file), "link_file");
+
+ if (link(pfile, link_file) == -1) {
+ ret = errno;
+ }
+ if (ret != 0) {
+ fprintf(stderr, "link(%s, %s)\n", pfile, link_file);
+ exit(1);
+ }
+
+ unlink(link_file);
+ return (ret);
+}
+
+int
+do_creat(const char *pfile)
+{
+ int fd, ret = 0;
+
+ if (pfile == NULL) {
+ return (-1);
+ }
+
+ if ((fd = creat(pfile, ALL_MODE)) == -1) {
+ ret = errno;
+ }
+ if (fd != -1) {
+ (void) close(fd);
+ }
+
+ if (ret != 0) {
+ fprintf(stderr, "creat(%s, ALL_MODE)\n", pfile);
+ exit(1);
+ }
+
+ return (ret);
+}
+
+int
+do_utime(const char *pfile)
+{
+ int ret = 0;
+
+ if (pfile == NULL) {
+ return (-1);
+ }
+
+ /*
+ * Times of the file are set to the current time
+ */
+ if (utime(pfile, NULL) == -1) {
+ ret = errno;
+ }
+ if (ret != 0) {
+ fprintf(stderr, "utime(%s, NULL)\n", pfile);
+ exit(1);
+ }
+
+ return (ret);
+}
+
+int
+do_chmod(const char *pfile)
+{
+ int ret = 0;
+
+ if (pfile == NULL) {
+ return (-1);
+ }
+
+ if (chmod(pfile, ALL_MODE) == -1) {
+ ret = errno;
+ }
+ if (ret != 0) {
+ fprintf(stderr, "chmod(%s, ALL_MODE)\n", pfile);
+ exit(1);
+ }
+
+ return (ret);
+}
+
+int
+do_chown(const char *pfile)
+{
+ int ret = 0;
+
+ if (pfile == NULL) {
+ return (-1);
+ }
+
+ if (chown(pfile, getuid(), getgid()) == -1) {
+ ret = errno;
+ }
+ if (ret != 0) {
+ fprintf(stderr, "chown(%s, %d, %d)\n", pfile, (int)getuid(),
+ (int)getgid());
+ exit(1);
+ }
+
+ return (ret);
+}
diff --git a/tests/sys/cddl/zfs/tests/ctime/ctime_test.sh b/tests/sys/cddl/zfs/tests/ctime/ctime_test.sh
new file mode 100755
index 000000000000..e75d647647f4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/ctime/ctime_test.sh
@@ -0,0 +1,55 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2014 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case ctime_001_pos cleanup
+ctime_001_pos_head()
+{
+ atf_set "descr" "A file's ctime should change when the file is modified"
+ atf_set "require.progs" "ksh93 zfs"
+}
+ctime_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/ctime.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ $(atf_get_srcdir)/ctime_001_pos || atf_fail "Testcase failed"
+}
+ctime_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/ctime.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case ctime_001_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/ctime/setup.ksh b/tests/sys/cddl/zfs/tests/ctime/setup.ksh
new file mode 100644
index 000000000000..2ab3c8a94139
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/ctime/setup.ksh
@@ -0,0 +1,30 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/delegate/Makefile b/tests/sys/cddl/zfs/tests/delegate/Makefile
new file mode 100644
index 000000000000..c25841283c20
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/delegate/Makefile
@@ -0,0 +1,37 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/delegate
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zfs_allow_test
+ATF_TESTS_KSH93+= zfs_unallow_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= delegate.cfg
+${PACKAGE}FILES+= delegate_common.kshlib
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zfs_allow_001_pos.ksh
+${PACKAGE}FILES+= zfs_allow_002_pos.ksh
+${PACKAGE}FILES+= zfs_allow_003_pos.ksh
+${PACKAGE}FILES+= zfs_allow_004_pos.ksh
+${PACKAGE}FILES+= zfs_allow_005_pos.ksh
+${PACKAGE}FILES+= zfs_allow_006_pos.ksh
+${PACKAGE}FILES+= zfs_allow_007_pos.ksh
+${PACKAGE}FILES+= zfs_allow_008_pos.ksh
+${PACKAGE}FILES+= zfs_allow_009_neg.ksh
+${PACKAGE}FILES+= zfs_allow_010_pos.ksh
+${PACKAGE}FILES+= zfs_allow_011_neg.ksh
+${PACKAGE}FILES+= zfs_allow_012_neg.ksh
+${PACKAGE}FILES+= zfs_unallow_001_pos.ksh
+${PACKAGE}FILES+= zfs_unallow_002_pos.ksh
+${PACKAGE}FILES+= zfs_unallow_003_pos.ksh
+${PACKAGE}FILES+= zfs_unallow_004_pos.ksh
+${PACKAGE}FILES+= zfs_unallow_005_pos.ksh
+${PACKAGE}FILES+= zfs_unallow_006_pos.ksh
+${PACKAGE}FILES+= zfs_unallow_007_neg.ksh
+${PACKAGE}FILES+= zfs_unallow_008_neg.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/delegate/cleanup.ksh b/tests/sys/cddl/zfs/tests/delegate/cleanup.ksh
new file mode 100644
index 000000000000..2d304dc27203
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/delegate/cleanup.ksh
@@ -0,0 +1,32 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/delegate/delegate_common.kshlib
+
+cleanup_user_group
+
+default_cleanup
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/delegate/delegate.cfg b/tests/sys/cddl/zfs/tests/delegate/delegate.cfg
new file mode 100644
index 000000000000..fb2c4f3ae0f3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/delegate/delegate.cfg
@@ -0,0 +1,57 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export STF_TIMEOUT=1800
+
+export NISSTAFILE=$TMPDIR/nis_state
+
+export STAFF_GROUP=zfsgrp
+export STAFF1=staff1
+export STAFF2=staff2
+
+export OTHER_GROUP=othergrp
+export OTHER1=other1
+export OTHER2=other2
+
+export EVERYONE="$STAFF1 $STAFF2 $OTHER1 $OTHER2"
+
+export LOCAL_SET="snapshot"
+export LOCAL_DESC_SET="readonly,checksum"
+export DESC_SET="compression"
+
+export TESTVOL=testvol.${TESTCASE_ID}
+export VOLSIZE=150m
+
+export ROOT_TESTVOL=$TESTPOOL/$TESTVOL
+export ROOT_TESTFS=$TESTPOOL/$TESTFS
+export SUBFS=$ROOT_TESTFS/SUBFS
+export SUBFS2=$ROOT_TESTFS/SUBFS2
+
+DATASETS="$ROOT_TESTFS"
+if is_global_zone ; then
+ DATASETS="$DATASETS $ROOT_TESTVOL"
+fi
+export DATASETS
diff --git a/tests/sys/cddl/zfs/tests/delegate/delegate_common.kshlib b/tests/sys/cddl/zfs/tests/delegate/delegate_common.kshlib
new file mode 100644
index 000000000000..13889ac8c20f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/delegate/delegate_common.kshlib
@@ -0,0 +1,1701 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#
+# Cleanup exist user/group.
+#
+function cleanup_user_group
+{
+ typeset i
+ for i in $STAFF1 $STAFF2 $OTHER1 $OTHER2 ; do
+ del_user $i
+ done
+ for i in $STAFF_GROUP $OTHER_GROUP ; do
+ del_group $i
+ done
+
+ return 0
+}
+
+#
+# Restore test file system to the original status.
+#
+function restore_root_datasets
+{
+ if datasetexists $ROOT_TESTFS ; then
+ log_must $ZFS destroy -Rf $ROOT_TESTFS
+ fi
+ log_must $ZFS create $ROOT_TESTFS
+
+ if is_global_zone ; then
+ if datasetexists $ROOT_TESTVOL ; then
+ log_must $ZFS destroy -Rf $ROOT_TESTVOL
+ fi
+ log_must $ZFS create -V $VOLSIZE $ROOT_TESTVOL
+ fi
+
+ return 0
+}
+
+#
+# Verify the specified user have permission on the dataset
+#
+# $1 dataset
+# $2 permissions which are separated by comma(,)
+# $3-n users
+#
+function verify_perm
+{
+ typeset dtst=$1
+ typeset permissions=$2
+ shift 2
+
+ if [[ -z $@ || -z $permissions || -z $dtst ]]; then
+ return 1
+ fi
+
+ typeset type=$(get_prop type $dtst)
+ permissions=$($ECHO $permissions | $TR -s "," " ")
+
+ typeset user
+ for user in $@; do
+ typeset perm
+ for perm in $permissions; do
+ typeset -i ret=1
+ if [[ $type == "filesystem" ]]; then
+ check_fs_perm $user $perm $dtst
+ ret=$?
+ elif [[ $type == "volume" ]]; then
+ check_vol_perm $user $perm $dtst
+ ret=$?
+ fi
+
+ if ((ret != 0)) ; then
+ log_note "Fail: $user should have $perm " \
+ "on $dtst"
+ return 1
+ fi
+ done
+ done
+
+ return 0
+}
+
+#
+# Verify the specified user have no permission on the dataset
+#
+# $1 dataset
+# $2 permissions which are separated by comma(,)
+# $3-n users
+#
+function verify_noperm
+{
+ typeset dtst=$1
+ typeset permissions=$2
+ shift 2
+
+ if [[ -z $@ || -z $permissions || -z $dtst ]]; then
+ return 1
+ fi
+
+ typeset type=$(get_prop type $dtst)
+ permissions=$($ECHO $permissions | $TR -s "," " ")
+
+ typeset user
+ for user in $@; do
+ typeset perm
+ for perm in $permissions; do
+ typeset -i ret=1
+ if [[ $type == "filesystem" ]]; then
+ check_fs_perm $user $perm $dtst
+ ret=$?
+ elif [[ $type == "volume" ]]; then
+ check_vol_perm $user $perm $dtst
+ ret=$?
+ fi
+
+ if ((ret == 0)) ; then
+ log_note "Fail: $user should not have $perm " \
+ "on $dtst"
+ return 1
+ fi
+ done
+ done
+
+ return 0
+}
+
+function user_run
+{
+ typeset user=$1
+ typeset group=$($GROUPS $user)
+ shift
+
+ sudo -u $user -g $group $@
+}
+
+function common_perm
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset dtst=$3
+
+ typeset -i ret=1
+ case $perm in
+ send)
+ verify_send $user $perm $dtst
+ ret=$?
+ ;;
+ allow)
+ verify_allow $user $perm $dtst
+ ret=$?
+ ;;
+ userprop)
+ verify_userprop $user $perm $dtst
+ ret=$?
+ ;;
+ compression|checksum|readonly)
+ verify_ccr $user $perm $dtst
+ ret=$?
+ ;;
+ copies)
+ verify_copies $user $perm $dtst
+ ret=$?
+ ;;
+ reservation)
+ verify_reservation $user $perm $dtst
+ ret=$?
+ ;;
+ *)
+ ret=1
+ ;;
+ esac
+
+ return $ret
+}
+
+function check_fs_perm
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset fs=$3
+
+ typeset -i ret=1
+ case $perm in
+ create)
+ verify_fs_create $user $perm $fs
+ ret=$?
+ ;;
+ destroy)
+ verify_fs_destroy $user $perm $fs
+ ret=$?
+ ;;
+ snapshot)
+ verify_fs_snapshot $user $perm $fs
+ ret=$?
+ ;;
+ rollback)
+ verify_fs_rollback $user $perm $fs
+ ret=$?
+ ;;
+ clone)
+ verify_fs_clone $user $perm $fs
+ ret=$?
+ ;;
+ rename)
+ verify_fs_rename $user $perm $fs
+ ret=$?
+ ;;
+ mount)
+ verify_fs_mount $user $perm $fs
+ ret=$?
+ ;;
+ share)
+ verify_fs_share $user $perm $fs
+ ret=$?
+ ;;
+ mountpoint)
+ verify_fs_mountpoint $user $perm $fs
+ ret=$?
+ ;;
+ promote)
+ verify_promote $user $perm $fs
+ ret=$?
+ ;;
+ canmount)
+ verify_fs_canmount $user $perm $fs
+ ret=$?
+ ;;
+ recordsize)
+ verify_fs_recordsize $user $perm $fs
+ ret=$?
+ ;;
+ quota)
+ verify_fs_quota $user $perm $fs
+ ret=$?
+ ;;
+ aclmode)
+ verify_fs_aclmode $user $perm $fs
+ ret=$?
+ ;;
+ aclinherit)
+ verify_fs_aclinherit $user $perm $fs
+ ret=$?
+ ;;
+ snapdir)
+ verify_fs_snapdir $user $perm $fs
+ ret=$?
+ ;;
+ atime|exec|devices|setuid|xattr)
+ verify_fs_aedsx $user $perm $fs
+ ret=$?
+ ;;
+ zoned)
+ verify_fs_zoned $user $perm $fs
+ ret=$?
+ ;;
+ sharenfs)
+ verify_fs_sharenfs $user $perm $fs
+ ret=$?
+ ;;
+ shareiscsi)
+ verify_fs_shareiscsi $user $perm $fs
+ ret=$?
+ ;;
+ receive)
+ verify_fs_receive $user $perm $fs
+ ret=$?
+ ;;
+ *)
+ common_perm $user $perm $fs
+ ret=$?
+ ;;
+ esac
+
+ return $ret
+}
+
+function check_vol_perm
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset vol=$3
+
+ typeset -i ret=1
+ case $perm in
+ destroy)
+ verify_vol_destroy $user $perm $vol
+ ret=$?
+ ;;
+ snapshot)
+ verify_vol_snapshot $user $perm $vol
+ ret=$?
+ ;;
+ rollback)
+ verify_vol_rollback $user $perm $vol
+ ret=$?
+ ;;
+ clone)
+ verify_vol_clone $user $perm $vol
+ ret=$?
+ ;;
+ rename)
+ verify_vol_rename $user $perm $vol
+ ret=$?
+ ;;
+ promote)
+ verify_promote $user $perm $vol
+ ret=$?
+ ;;
+ volsize)
+ verify_vol_volsize $user $perm $vol
+ ret=$?
+ ;;
+ shareiscsi)
+ verify_vol_shareiscsi $user $perm $vol
+ ret=$?
+ ;;
+ *)
+ common_perm $user $perm $vol
+ ret=$?
+ ;;
+ esac
+
+ return $ret
+}
+
+function setup_unallow_testenv
+{
+ typeset dtst
+
+ log_must restore_root_datasets
+
+ log_must $ZFS create $SUBFS
+
+ for dtst in $DATASETS ; do
+ log_must $ZFS allow -l $STAFF1 $LOCAL_SET $dtst
+ log_must $ZFS allow -d $STAFF2 $DESC_SET $dtst
+ log_must $ZFS allow $OTHER1 $LOCAL_DESC_SET $dtst
+ log_must $ZFS allow $OTHER2 $LOCAL_DESC_SET $dtst
+
+ log_must verify_perm $dtst $LOCAL_SET $STAFF1
+ log_must verify_perm $dtst $LOCAL_DESC_SET $OTHER1
+ log_must verify_perm $dtst $LOCAL_DESC_SET $OTHER2
+ if [[ $dtst == $ROOT_TESTFS ]]; then
+ log_must verify_perm $SUBFS $DESC_SET $STAFF2
+ log_must verify_perm $SUBFS $LOCAL_DESC_SET $OTHER1
+ log_must verify_perm $SUBFS $LOCAL_DESC_SET $OTHER2
+ fi
+ done
+
+ return 0
+}
+
+#
+# Verify permission send for specified user on the dataset
+# $1 user
+# $2 permission
+# $3 dataset
+#
+function verify_send
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset dtst=$3
+
+ typeset oldval
+ typeset stamp=${perm}.${user}.$($DATE +'%F-%R:%S')
+ typeset snap=$dtst@snap.$stamp
+
+ typeset -i ret=1
+
+ log_must $ZFS snapshot $snap
+ typeset bak_user=$TMPDIR/bak.$user.$stamp
+ typeset bak_root=$TMPDIR/bak.root.$stamp
+
+ user_run $user $ZFS send $snap > $bak_user
+ log_must eval "$ZFS send $snap > $bak_root"
+ log_must $ZFS destroy $snap
+
+ if [[ $(checksum $bak_user) == $(checksum $bak_root) ]]; then
+ ret=0
+ fi
+
+ $RM -rf $bak_user > /dev/null
+ $RM -rf $bak_root > /dev/null
+
+ return $ret
+}
+
+function verify_fs_receive
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset fs=$3
+
+ typeset dtst
+ typeset oldval
+ typeset stamp=${perm}.${user}.$($DATE +'%F-%R:%S')
+ typeset newfs=$fs/newfs.$stamp
+ typeset newvol=$fs/newvol.$stamp
+ typeset bak_user=$TMPDIR/bak.$user.$stamp
+ typeset bak_root=$TMPDIR/bak.root.$stamp
+
+ log_must $ZFS create $newfs
+ typeset datasets="$newfs"
+ if is_global_zone ; then
+ log_must $ZFS create -V $VOLSIZE $newvol
+ datasets="$newfs $newvol"
+ fi
+
+ for dtst in $datasets ; do
+
+ typeset dtstsnap=$dtst@snap.$stamp
+ log_must $ZFS snapshot $dtstsnap
+
+ log_must eval "$ZFS send $dtstsnap > $bak_root"
+ log_must $ZFS destroy -rf $dtst
+
+ user_run $user $ZFS receive $dtst < $bak_root
+ if datasetexists $dtstsnap ; then
+ return 1
+ fi
+
+ log_must $ZFS allow $user create $fs
+ user_run $user $ZFS receive $dtst < $bak_root
+ log_must $ZFS unallow $user create $fs
+ if datasetexists $dtstsnap ; then
+ return 1
+ fi
+
+ log_must $ZFS allow $user mount $fs
+ user_run $user $ZFS receive $dtst < $bak_root
+ log_must $ZFS unallow $user mount $fs
+ if datasetexists $dtstsnap ; then
+ return 1
+ fi
+
+ log_must $ZFS allow $user mount,create $fs
+ user_run $user $ZFS receive $dtst < $bak_root
+ log_must $ZFS unallow $user mount,create $fs
+ if ! datasetexists $dtstsnap ; then
+ return 1
+ fi
+
+ # check the data integrity
+ log_must eval "$ZFS send $dtstsnap > $bak_user"
+ log_must $ZFS destroy -rf $dtst
+ log_must eval "$ZFS receive $dtst < $bak_root"
+ log_must eval "$ZFS send $dtstsnap > $bak_root"
+ log_must $ZFS destroy -rf $dtst
+ if [[ $(checksum $bak_user) != $(checksum $bak_root) ]]; then
+ return 1
+ fi
+
+ $RM -rf $bak_user > /dev/null
+ $RM -rf $bak_root > /dev/null
+
+ done
+
+ return 0
+}
+
+function verify_userprop
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset dtst=$3
+
+ typeset stamp=${perm}.${user}.$($DATE +'%F-%R:%S')
+
+ user_run $user $ZFS set "$user:ts=$stamp" $dtst
+ if [[ $stamp != $(get_prop "$user:ts" $dtst) ]]; then
+ return 1
+ fi
+ log_must $ZFS inherit "$user:ts" $dtst
+
+ return 0
+}
+
+function verify_ccr
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset dtst=$3
+
+ typeset oldval
+
+ set -A modes "on" "off"
+ oldval=$(get_prop $perm $dtst)
+ if [[ $oldval == "on" ]]; then
+ n=1
+ elif [[ $oldval == "off" ]]; then
+ n=0
+ fi
+ log_note "$user $ZFS set $perm=${modes[$n]} $dtst"
+ user_run $user $ZFS set $perm=${modes[$n]} $dtst
+ if [[ ${modes[$n]} != $(get_prop $perm $dtst) ]]; then
+ return 1
+ fi
+
+ return 0
+}
+
+function verify_copies
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset dtst=$3
+
+ typeset oldval
+
+ set -A modes 1 2 3
+ oldval=$(get_prop $perm $dtst)
+ if [[ $oldval -eq 1 ]]; then
+ n=1
+ elif [[ $oldval -eq 2 ]]; then
+ n=2
+ elif [[ $oldval -eq 3 ]]; then
+ n=0
+ fi
+ log_note "$user $ZFS set $perm=${modes[$n]} $dtst"
+ user_run $user $ZFS set $perm=${modes[$n]} $dtst
+ if [[ ${modes[$n]} != $(get_prop $perm $dtst) ]]; then
+ return 1
+ fi
+
+ return 0
+}
+
+function verify_reservation
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset dtst=$3
+
+ typeset value32m=$(( 1024 * 1024 * 32 ))
+ typeset oldval=$(get_prop reservation $dtst)
+ user_run $user $ZFS set reservation=$value32m $dtst
+ if [[ $value32m != $(get_prop reservation $dtst) ]]; then
+ log_must $ZFS set reservation=$oldval $dtst
+ return 1
+ fi
+
+ log_must $ZFS set reservation=$oldval $dtst
+ return 0
+}
+
+function verify_fs_create
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset fs=$3
+
+ typeset stamp=${perm}.${user}.$($DATE +'%F-%R:%S')
+ typeset newfs=$fs/nfs.$stamp
+ typeset newvol=$fs/nvol.$stamp
+
+ user_run $user $ZFS create $newfs
+ if datasetexists $newfs ; then
+ return 1
+ fi
+
+ log_must $ZFS allow $user mount $fs
+ user_run $user $ZFS create $newfs
+ log_must $ZFS unallow $user mount $fs
+ if ! datasetexists $newfs ; then
+ return 1
+ fi
+ log_must $ZFS destroy $newfs
+
+ if is_global_zone ; then
+ # mount permission is required for sparse volume
+ user_run $user $ZFS create -V 150m -s $newvol
+ if datasetexists $newvol ; then
+ return 1
+ fi
+
+ log_must $ZFS allow $user mount $fs
+ user_run $user $ZFS create -V 150m -s $newvol
+ log_must $ZFS unallow $user mount $fs
+ if ! datasetexists $newvol ; then
+ return 1
+ fi
+ log_must $ZFS destroy $newvol
+
+ # mount and reserveration permission are
+ # required for normal volume
+ user_run $user $ZFS create -V 150m $newvol
+ if datasetexists $newvol ; then
+ return 1
+ fi
+
+ log_must $ZFS allow $user mount $fs
+ user_run $user $ZFS create -V 150m $newvol
+ log_must $ZFS unallow $user mount $fs
+ if datasetexists $newvol ; then
+ return 1
+ fi
+
+ log_must $ZFS allow $user reservation $fs
+ user_run $user $ZFS create -V 150m $newvol
+ log_must $ZFS unallow $user reservation $fs
+ if datasetexists $newvol ; then
+ return 1
+ fi
+
+ log_must $ZFS allow $user refreservation $fs
+ user_run $user $ZFS create -V 150m $newvol
+ log_must $ZFS unallow $user refreservation $fs
+ if datasetexists $newvol ; then
+ return 1
+ fi
+
+ log_must $ZFS allow $user mount $fs
+ log_must $ZFS allow $user reservation $fs
+ log_must $ZFS allow $user refreservation $fs
+ user_run $user $ZFS create -V 150m $newvol
+ log_must $ZFS unallow $user mount $fs
+ log_must $ZFS unallow $user reservation $fs
+ log_must $ZFS unallow $user refreservation $fs
+ if ! datasetexists $newvol ; then
+ return 1
+ fi
+ log_must $ZFS destroy $newvol
+ fi
+
+ return 0
+}
+
+function verify_fs_destroy
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset fs=$3
+
+ if ! ismounted $fs ; then
+ user_run $user $ZFS destroy $fs
+ if datasetexists $fs ; then
+ return 1
+ fi
+ fi
+
+ if ismounted $fs ; then
+ user_run $user $ZFS destroy $fs
+ if ! datasetexists $fs ; then
+ return 1
+ fi
+
+ # mount permission is required
+ log_must $ZFS allow $user mount $fs
+ user_run $user $ZFS destroy $fs
+ if datasetexists $fs ; then
+ return 1
+ fi
+ fi
+
+ return 0
+}
+
+function verify_fs_snapshot
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset fs=$3
+
+ typeset stamp=${perm}.${user}.$($DATE +'%F-%R:%S')
+ typeset snap=$fs@snap.$stamp
+ typeset mntpt=$(get_prop mountpoint $fs)
+
+ if [[ "yes" == $(get_prop mounted $fs) ]]; then
+ log_must $ZFS umount $fs
+ fi
+ user_run $user $ZFS snapshot $snap
+ if ! datasetexists $snap ; then
+ return 1
+ fi
+ log_must $ZFS destroy $snap
+
+ if [[ "no" == $(get_prop mounted $fs) ]]; then
+ log_must $ZFS mount $fs
+ fi
+ user_run $user $ZFS snapshot $snap
+ if ! datasetexists $snap ; then
+ return 1
+ fi
+ log_must $ZFS destroy $snap
+
+ # TODO
+ # FreeBSD does not yet support creating snapshots with mkdir.
+ # See tests/sys/cddl/zfs/tests/snapshot/snapshot_015_pos.ksh
+ # typeset snapdir=${mntpt}/$(get_snapdir_name)/snap.$stamp
+ # user_run $user $MKDIR $snapdir
+ # if ! datasetexists $snap ; then
+ # return 1
+ # fi
+ # log_must $ZFS destroy $snap
+
+ return 0
+}
+
+function verify_fs_rollback
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset fs=$3
+
+ typeset oldval
+ typeset stamp=${perm}.${user}.$($DATE +'%F-%R:%S')
+ typeset snap=$fs@snap.$stamp
+ typeset mntpt=$(get_prop mountpoint $fs)
+
+ oldval=$(datasetcksum $fs)
+ log_must $ZFS snapshot $snap
+
+ if ! ismounted $fs; then
+ log_must $ZFS mount $fs
+ fi
+ log_must $TOUCH $mntpt/testfile.$stamp
+
+ user_run $user $ZFS rollback -R $snap
+ if [[ -e $mntpt/testfile.$stamp ]]; then
+ return 1
+ fi
+
+ # rollback on mounted fs has to be with mount permission
+ log_must $ZFS allow $user mount $fs
+ user_run $user $ZFS rollback -R $snap
+ log_must $ZFS unallow $user mount $fs
+ if is_global_zone ; then
+ if [[ $oldval != $(datasetcksum $fs) ]]; then
+ return 1
+ fi
+ else
+ # datasetcksum can not be used in local zone
+ if [[ -e $mntpt/testfile.$stamp ]]; then
+ return 1
+ fi
+ fi
+
+ return 0
+}
+
+function verify_fs_clone
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset fs=$3
+
+ typeset stamp=${perm}.${user}.$($DATE +'%F-%R:%S')
+ typeset basefs=${fs%/*}
+ typeset snap=$fs@snap.$stamp
+ typeset clone=$basefs/cfs.$stamp
+
+ log_must $ZFS snapshot $snap
+ user_run $user $ZFS clone $snap $clone
+ if datasetexists $clone ; then
+ return 1
+ fi
+
+ log_must $ZFS allow $user create $basefs
+ user_run $user $ZFS clone $snap $clone
+ log_must $ZFS unallow $user create $basefs
+ if datasetexists $clone ; then
+ return 1
+ fi
+
+ log_must $ZFS allow $user mount $basefs
+ user_run $user $ZFS clone $snap $clone
+ log_must $ZFS unallow $user mount $basefs
+ if datasetexists $clone ; then
+ return 1
+ fi
+
+ log_must $ZFS allow $user mount $basefs
+ log_must $ZFS allow $user create $basefs
+ user_run $user $ZFS clone $snap $clone
+ log_must $ZFS unallow $user create $basefs
+ log_must $ZFS unallow $user mount $basefs
+ if ! datasetexists $clone ; then
+ return 1
+ fi
+
+ log_must $ZFS destroy -R $snap
+
+ return 0
+}
+
+function verify_fs_rename
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset fs=$3
+
+ typeset stamp=${perm}.${user}.$($DATE +'%F-%R:%S')
+ typeset basefs=${fs%/*}
+ typeset snap=$fs@snap.$stamp
+ typeset renamefs=$basefs/nfs.$stamp
+
+ if ! ismounted $fs; then
+ log_must $ZFS mount $fs
+ fi
+
+ # case 1
+ user_run $user $ZFS rename $fs $renamefs
+ if datasetexists $renamefs ; then
+ return 1
+ fi
+
+ # case 2
+ log_must $ZFS allow $user create $basefs
+ user_run $user $ZFS rename $fs $renamefs
+ log_must $ZFS unallow $user create $basefs
+ if datasetexists $renamefs ; then
+ return 1
+ fi
+
+ # case 3
+ log_must $ZFS allow $user mount $basefs
+ user_run $user $ZFS rename $fs $renamefs
+ log_must $ZFS unallow $user mount $basefs
+ if datasetexists $renamefs ; then
+ return 1
+ fi
+
+ # case 4
+ log_must $ZFS allow $user mount $fs
+ user_run $user $ZFS rename $fs $renamefs
+ if datasetexists $renamefs ; then
+ log_must $ZFS unallow $user mount $renamefs
+ return 1
+ fi
+ log_must $ZFS unallow $user mount $fs
+
+ # case 5
+ log_must $ZFS allow $user create $basefs
+ log_must $ZFS allow $user mount $fs
+ user_run $user $ZFS rename $fs $renamefs
+ log_must $ZFS unallow $user create $basefs
+ if datasetexists $renamefs ; then
+ log_must $ZFS unallow $user mount $renamefs
+ return 1
+ fi
+ log_must $ZFS unallow $user mount $fs
+
+ # case 6
+ log_must $ZFS allow $user mount $basefs
+ log_must $ZFS allow $user mount $fs
+ user_run $user $ZFS rename $fs $renamefs
+ log_must $ZFS unallow $user mount $basefs
+ if datasetexists $renamefs ; then
+ log_must $ZFS unallow $user mount $renamefs
+ return 1
+ fi
+ log_must $ZFS unallow $user mount $fs
+
+ # case 7
+ log_must $ZFS allow $user create $basefs
+ log_must $ZFS allow $user mount $basefs
+ user_run $user $ZFS rename $fs $renamefs
+ log_must $ZFS unallow $user mount $basefs
+ log_must $ZFS unallow $user create $basefs
+ if ! datasetexists $renamefs ; then
+ return 1
+ fi
+
+ log_must $ZFS rename $renamefs $fs
+
+ return 0
+}
+
+function verify_fs_mount
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset fs=$3
+
+ typeset stamp=${perm}.${user}.$($DATE +'%F-%R:%S')
+ typeset mntpt=$(get_prop mountpoint $fs)
+ typeset newmntpt=$TMPDIR/mnt.$stamp
+
+ if ismounted $fs ; then
+ user_run $user $ZFS unmount $fs
+ if ismounted $fs ; then
+ return 1
+ fi
+ fi
+
+ if ! ismounted $fs ; then
+ log_must $ZFS set mountpoint=$newmntpt $fs
+ log_must $RM -rf $newmntpt
+ log_must $MKDIR $newmntpt
+
+ user_run $user $ZFS mount $fs
+ if ismounted $fs ; then
+ return 1
+ fi
+
+ # mountpoint's owner must be the user
+ log_must $CHOWN $user $newmntpt
+ user_run $user $ZFS mount $fs
+ if ! ismounted $fs ; then
+ return 1
+ fi
+ log_must $ZFS umount $fs
+ log_must $RM -rf $newmntpt
+ log_must $ZFS set mountpoint=$mntpt $fs
+ fi
+
+ return 0
+}
+
+function verify_fs_share
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset fs=$3
+
+ typeset stamp=${perm}.${user}.$($DATE +'%F-%R:%S')
+ typeset mntpt=$(get_prop mountpoint $fs)
+
+ typeset stat=$($SVCS -H -o STA nfs/server:default)
+ if [[ $stat != "ON" ]]; then
+ log_note "Current nfs/server status: $stat"
+ # legacy share
+ user_run $user $SHARE $mntpt
+ if is_shared $fs; then
+ return 1
+ fi
+
+ # sharenfs=on
+ log_must $ZFS set sharenfs=on $fs
+ user_run $user $ZFS share $fs
+ if is_shared $fs; then
+ log_must $ZFS set sharenfs=off $fs
+ return 1
+ fi
+ log_must $ZFS set sharenfs=off $fs
+ fi
+
+ # turn on nfs/server service if it is not enabled
+ typeset tmpshare=$TMPDIR/a.${TESTCASE_ID}
+ $RM -rf $tmpshare
+ log_must $MKDIR -p $tmpshare
+ log_must $SHARE $tmpshare
+
+ # legacy share
+ user_run $user $SHARE $mntpt
+ if ! is_shared $fs ; then
+ log_must $UNSHARE $tmpshare
+ log_must $RM -rf $tmpshare
+ return 1
+ fi
+
+ user_run $user $UNSHARE $mntpt
+ if is_shared $fs ; then
+ log_must $UNSHARE $tmpshare
+ log_must $RM -rf $tmpshare
+ return 1
+ fi
+
+ # sharenfs=on
+ log_must $ZFS set sharenfs=on $fs
+ user_run $user $ZFS share $fs
+ if ! is_shared $fs; then
+ log_must $ZFS set sharenfs=off $fs
+ return 1
+ fi
+
+ user_run $user $ZFS unshare $fs
+ if is_shared $fs; then
+ log_must $ZFS set sharenfs=off $fs
+ return 1
+ fi
+ log_must $ZFS set sharenfs=off $fs
+
+ log_must $UNSHARE $tmpshare
+ log_must $RM -rf $tmpshare
+
+ return 0
+}
+
+function verify_fs_mountpoint
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset fs=$3
+
+ typeset stamp=${perm}.${user}.$($DATE +'%F-%R:%S')
+ typeset mntpt=$(get_prop mountpoint $fs)
+ typeset newmntpt=$TMPDIR/mnt.$stamp
+
+ if ! ismounted $fs ; then
+ user_run $user $ZFS set mountpoint=$newmntpt $fs
+ if [[ $newmntpt != \
+ $(get_prop mountpoint $fs) ]] ; then
+ return 1
+ fi
+ log_must $ZFS set mountpoint=$mntpt $fs
+ fi
+
+ if ismounted $fs ; then
+ user_run $user $ZFS set mountpoint=$newmntpt $fs
+ if [[ $mntpt != $(get_prop mountpoint $fs) ]] ;
+ then
+ return 1
+ fi
+
+ # require mount permission when fs is mounted
+ log_must $ZFS allow $user mount $fs
+ user_run $user $ZFS set mountpoint=$newmntpt $fs
+ log_must $ZFS unallow $user mount $fs
+ if [[ $newmntpt != \
+ $(get_prop mountpoint $fs) ]] ; then
+ return 1
+ fi
+ log_must $ZFS set mountpoint=$mntpt $fs
+ fi
+
+ return 0
+}
+
+function verify_promote
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset fs=$3
+
+ typeset stamp=${perm}.${user}.$($DATE +'%F-%R:%S')
+ typeset basefs=${fs%/*}
+ typeset snap=$fs@snap.$stamp
+ typeset clone=$basefs/cfs.$stamp
+
+ log_must $ZFS snapshot $snap
+ log_must $ZFS clone $snap $clone
+ log_must $ZFS promote $clone
+
+ typeset fs_orig=$(get_prop origin $fs)
+ typeset clone_orig=$(get_prop origin $clone)
+
+ user_run $user $ZFS promote $fs
+ # promote should fail if original fs does not have mount and promote
+ # permissions
+ if [[ $fs_orig != $(get_prop origin $fs) || \
+ $clone_orig != $(get_prop origin $clone) ]]; then
+ return 1
+ fi
+
+ # promote should fail if original fs does not have mount permission
+ log_must $ZFS allow $user promote $clone
+ user_run $user $ZFS promote $fs
+ log_must $ZFS unallow $user promote $clone
+ if [[ $fs_orig != $(get_prop origin $fs) || \
+ $clone_orig != $(get_prop origin $clone) ]]; then
+ return 1
+ fi
+
+ # promote should fail if original fs does not have promote permission
+ log_must $ZFS allow $user mount $fs
+ user_run $user $ZFS promote $fs
+ log_must $ZFS unallow $user mount $fs
+ if [[ $fs_orig != $(get_prop origin $fs) || \
+ $clone_orig != $(get_prop origin $clone) ]]; then
+ return 1
+ fi
+
+ log_must $ZFS allow $user mount $fs
+ log_must $ZFS allow $user promote $clone
+ user_run $user $ZFS promote $fs
+ log_must $ZFS unallow $user promote $clone
+ log_must $ZFS unallow $user mount $fs
+ if [[ $snap != $(get_prop origin $clone) || \
+ $clone_orig != $(get_prop origin $fs) ]]; then
+ return 1
+ fi
+
+ return 0
+}
+
+function verify_fs_canmount
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset fs=$3
+
+ typeset oldval
+ typeset stamp=${perm}.${user}.$($DATE +'%F-%R:%S')
+
+ if ! ismounted $fs ; then
+ set -A modes "on" "off"
+ oldval=$(get_prop $perm $fs)
+ if [[ $oldval == "on" ]]; then
+ n=1
+ elif [[ $oldval == "off" ]]; then
+ n=0
+ fi
+ log_note "$user $ZFS set $perm=${modes[$n]} $fs"
+ user_run $user $ZFS set $perm=${modes[$n]} $fs
+ if [[ ${modes[$n]} != $(get_prop $perm $fs) ]];
+ then
+ return 1
+ fi
+ fi
+
+
+ # fs is mounted
+ if ismounted $fs ; then
+ # property value does not change if
+ # no mount permission
+ set -A modes "on" "off"
+ oldval=$(get_prop $perm $fs)
+ if [[ $oldval == "on" ]]; then
+ n=1
+ elif [[ $oldval == "off" ]]; then
+ n=0
+ fi
+ log_note "$user $ZFS set $perm=${modes[$n]} $fs"
+ log_must $ZFS allow $user mount $fs
+ user_run $user $ZFS set $perm=${modes[$n]} $fs
+ log_must $ZFS unallow $user mount $fs
+ if [[ ${modes[$n]} != $(get_prop $perm $fs) ]];
+ then
+ return 1
+ fi
+ fi
+
+ return 0
+}
+
+function verify_fs_recordsize
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset fs=$3
+
+ typeset value8k=$(( 1024 * 8 ))
+ user_run $user $ZFS set recordsize=$value8k $fs
+ if [[ $value8k != $(get_prop recordsize $fs) ]]; then
+ return 1
+ fi
+
+ return 0
+}
+
+function verify_fs_quota
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset fs=$3
+
+ typeset value32m=$(( 1024 * 1024 * 32 ))
+ user_run $user $ZFS set quota=$value32m $fs
+ if [[ $value32m != $(get_prop quota $fs) ]]; then
+ return 1
+ fi
+
+ return 0
+}
+
+function verify_fs_aclmode
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset fs=$3
+
+ typeset oldval
+ set -A modes "discard" "groupmask" "passthrough"
+ oldval=$(get_prop $perm $fs)
+ if [[ $oldval == "discard" ]]; then
+ n=1
+ elif [[ $oldval == "groupmask" ]]; then
+ n=2
+ elif [[ $oldval == "passthrough" ]]; then
+ n=0
+ fi
+ log_note "$user $ZFS set aclmode=${modes[$n]} $fs"
+ user_run $user $ZFS set aclmode=${modes[$n]} $fs
+ if [[ ${modes[$n]} != $(get_prop aclmode $fs) ]]; then
+ return 1
+ fi
+
+ return 0
+}
+
+function verify_fs_aclinherit
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset fs=$3
+
+ #
+ # PSARC/2008/231 change the default value of aclinherit to "restricted"
+ # but still keep the old interface of "secure"
+ #
+
+ typeset oldval
+ set -A modes "discard" "noallow" "secure" "passthrough"
+ oldval=$(get_prop $perm $fs)
+ if [[ $oldval == "discard" ]]; then
+ n=1
+ elif [[ $oldval == "noallow" ]]; then
+ n=2
+ elif [[ $oldval == "secure" || $oldval == "restricted" ]]; then
+ n=3
+ elif [[ $oldval == "passthrough" ]]; then
+ n=0
+ fi
+ log_note "$user $ZFS set aclinherit=${modes[$n]} $fs"
+ user_run $user $ZFS set aclinherit=${modes[$n]} $fs
+
+ typeset newval=$(get_prop aclinherit $fs)
+ if [[ ${modes[$n]} == "secure" && $newval == "restricted" ]]; then
+ return 0
+ elif [[ ${modes[$n]} != $(get_prop aclinherit $fs) ]]; then
+ return 1
+ fi
+
+ return 0
+}
+
+function verify_fs_snapdir
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset fs=$3
+
+ typeset oldval
+ set -A modes "visible" "hidden"
+ oldval=$(get_prop $perm $fs)
+ if [[ $oldval == "visible" ]]; then
+ n=1
+ elif [[ $oldval == "hidden" ]]; then
+ n=0
+ fi
+ log_note "$user $ZFS set snapdir=${modes[$n]} $fs"
+ user_run $user $ZFS set snapdir=${modes[$n]} $fs
+ if [[ ${modes[$n]} != $(get_prop snapdir $fs) ]]; then
+ return 1
+ fi
+
+ return 0
+}
+
+function verify_fs_aedsx
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset fs=$3
+
+ typeset oldval
+ set -A modes "on" "off"
+ oldval=$(get_prop $perm $fs)
+ if [[ $oldval == "on" ]]; then
+ n=1
+ elif [[ $oldval == "off" ]]; then
+ n=0
+ fi
+ log_note "$user $ZFS set $perm=${modes[$n]} $fs"
+ user_run $user $ZFS set $perm=${modes[$n]} $fs
+ if [[ ${modes[$n]} != $(get_prop $perm $fs) ]]; then
+ return 1
+ fi
+
+ return 0
+}
+
+function verify_fs_zoned
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset fs=$3
+
+ typeset oldval
+ set -A modes "on" "off"
+ oldval=$(get_prop $perm $fs)
+ if [[ $oldval == "on" ]]; then
+ n=1
+ elif [[ $oldval == "off" ]]; then
+ n=0
+ fi
+ log_note "$user $ZFS set $perm=${modes[$n]} $fs"
+ if is_global_zone ; then
+ if ! ismounted $fs ; then
+ user_run $user $ZFS set \
+ $perm=${modes[$n]} $fs
+ if [[ ${modes[$n]} != \
+ $(get_prop $perm $fs) ]]; then
+ return 1
+ fi
+ if [[ $n -eq 0 ]]; then
+ log_mustnot $ZFS mount $fs
+ else
+ log_must $ZFS mount $fs
+ fi
+ fi
+
+ if ismounted $fs; then
+ # n always is 1 in this case
+ user_run $user $ZFS set \
+ $perm=${modes[$n]} $fs
+ if [[ $oldval != \
+ $(get_prop $perm $fs) ]]; then
+ return 1
+ fi
+
+ # mount permission is needed
+ # to make zoned=on
+ log_must $ZFS allow $user mount $fs
+ user_run $user $ZFS set \
+ $perm=${modes[$n]} $fs
+ log_must $ZFS unallow $user mount $fs
+ if [[ ${modes[$n]} != \
+ $(get_prop $perm $fs) ]]; then
+ return 1
+ fi
+ fi
+ fi
+
+ if ! is_global_zone; then
+ user_run $user $ZFS set $perm=${modes[$n]} $fs
+ if [[ $oldval != $(get_prop $perm $fs) ]]; then
+ return 1
+ fi
+ fi
+
+ return 0
+}
+
+function verify_fs_sharenfs
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset fs=$3
+
+ typeset oldval
+ set -A modes "on" "off"
+ oldval=$(get_prop $perm $fs)
+ if [[ $oldval == "on" ]]; then
+ n=1
+ elif [[ $oldval == "off" ]]; then
+ n=0
+ fi
+ log_note "$user $ZFS set $perm=${modes[$n]} $fs"
+ user_run $user $ZFS set $perm=${modes[$n]} $fs
+ if [[ ${modes[$n]} != $(get_prop $perm $fs) ]]; then
+ return 1
+ fi
+ log_must $ZFS set $perm=$oldval $fs
+
+ # turn on nfs/server service if it is not enabled
+ typeset tmpshare=$TMPDIR/a.${TESTCASE_ID}
+ $RM -rf $tmpshare
+ log_must $MKDIR -p $tmpshare
+ log_must $SHARE $tmpshare
+
+ log_note "$user $ZFS set $perm=${modes[$n]} $fs"
+ user_run $user $ZFS set $perm=${modes[$n]} $fs
+ if [[ ${modes[$n]} != $(get_prop $perm $fs) ]]; then
+ return 1
+ fi
+
+ user_run $user $ZFS share $fs
+ if is_shared $fs; then
+ return 1
+ fi
+
+ # share permission is needed
+ log_must $ZFS allow $user share $fs
+ user_run $user $ZFS share $fs
+ log_must $ZFS unallow $user share $fs
+
+ if [[ $n -eq 0 ]] && ! is_shared $fs ; then
+ log_must $UNSHARE $tmpshare
+ log_must $RM -rf $tmpshare
+ return 1
+ fi
+
+ if [[ $n -eq 1 ]] && is_shared $fs ; then
+ log_must $UNSHARE $tmpshare
+ log_must $RM -rf $tmpshare
+ return 1
+ fi
+
+ log_must $UNSHARE $tmpshare
+ log_must $RM -rf $tmpshare
+
+ return 0
+}
+
+function verify_fs_shareiscsi
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset fs=$3
+
+ typeset oldval
+ set -A modes "on" "off"
+ oldval=$(get_prop $perm $fs)
+ if [[ $oldval == "on" ]]; then
+ n=1
+ elif [[ $oldval == "off" ]]; then
+ n=0
+ fi
+ log_note "$user $ZFS set $perm=${modes[$n]} $fs"
+ user_run $user $ZFS set $perm=${modes[$n]} $fs
+ if [[ ${modes[$n]} != $(get_prop $perm $fs) ]]; then
+ return 1
+ fi
+
+ return 0
+}
+
+function verify_vol_destroy
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset vol=$3
+
+ user_run $user $ZFS destroy $vol
+ if ! datasetexists $vol ; then
+ return 1
+ fi
+
+ # mount permission is required
+ log_must $ZFS allow $user mount $vol
+ user_run $user $ZFS destroy $vol
+ if datasetexists $vol ; then
+ return 1
+ fi
+
+ return 0
+}
+
+function verify_vol_snapshot
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset vol=$3
+
+ typeset stamp=${perm}.${user}.$($DATE +'%F-%R:%S')
+ typeset basevol=${vol%/*}
+ typeset snap=$vol@snap.$stamp
+
+ user_run $user $ZFS snapshot $snap
+ if ! datasetexists $snap ; then
+ return 1
+ fi
+ log_must $ZFS destroy $snap
+
+ return 0
+}
+
+function verify_vol_rollback
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset vol=$3
+
+ typeset stamp=${perm}.${user}.$($DATE +'%F-%R:%S')
+ typeset basevol=${vol%/*}
+ typeset snap=$vol@snap.$stamp
+
+ typeset oldval
+ log_must $ZFS snapshot $snap
+ oldval=$(datasetcksum $vol)
+
+ log_must $DD if=/dev/random of=/dev/zvol/$vol \
+ bs=512 count=1
+
+ user_run $user $ZFS rollback -R $snap
+ if [[ $oldval != $(datasetcksum $vol) ]]; then
+ return 1
+ fi
+
+ return 0
+}
+
+function verify_vol_clone
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset vol=$3
+
+ typeset stamp=${perm}.${user}.$($DATE +'%F-%R:%S')
+ typeset basevol=${vol%/*}
+ typeset snap=$vol@snap.$stamp
+ typeset clone=$basevol/cvol.$stamp
+
+ log_must $ZFS snapshot $snap
+
+ user_run $user $ZFS clone $snap $clone
+ if datasetexists $clone ; then
+ return 1
+ fi
+
+ log_must $ZFS allow $user create $basevol
+ user_run $user $ZFS clone $snap $clone
+ log_must $ZFS unallow $user create $basevol
+ if datasetexists $clone ; then
+ return 1
+ fi
+
+ log_must $ZFS allow $user mount $basevol
+ user_run $user $ZFS clone $snap $clone
+ log_must $ZFS unallow $user mount $basevol
+ if datasetexists $clone ; then
+ return 1
+ fi
+
+ # require create permission on parent and
+ # mount permission on itself as well
+ log_must $ZFS allow $user mount $basevol
+ log_must $ZFS allow $user create $basevol
+ user_run $user $ZFS clone $snap $clone
+ log_must $ZFS unallow $user create $basevol
+ log_must $ZFS unallow $user mount $basevol
+ if ! datasetexists $clone ; then
+ return 1
+ fi
+
+ return 0
+}
+
+function verify_vol_rename
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset vol=$3
+
+ typeset stamp=${perm}.${user}.$($DATE +'%F-%R:%S')
+ typeset basevol=${vol%/*}
+ typeset snap=$vol@snap.$stamp
+ typeset clone=$basevol/cvol.$stamp
+ typeset renamevol=$basevol/nvol.$stamp
+
+ user_run $user $ZFS rename $vol $renamevol
+ if datasetexists $renamevol ; then
+ return 1
+ fi
+
+ log_must $ZFS allow $user create $basevol
+ user_run $user $ZFS rename $vol $renamevol
+ log_must $ZFS unallow $user create $basevol
+ if datasetexists $renamevol ; then
+ return 1
+ fi
+
+ log_must $ZFS allow $user mount $basevol
+ user_run $user $ZFS rename $vol $renamevol
+ log_must $ZFS unallow $user mount $basevol
+ if datasetexists $renamevol ; then
+ return 1
+ fi
+
+ # require both create permission on parent and
+ # mount permission on parent as well
+ log_must $ZFS allow $user mount $basevol
+ log_must $ZFS allow $user create $basevol
+ user_run $user $ZFS rename $vol $renamevol
+ log_must $ZFS unallow $user mount $basevol
+ log_must $ZFS unallow $user create $basevol
+ if ! datasetexists $renamevol ; then
+ return 1
+ fi
+
+ log_must $ZFS rename $renamevol $vol
+
+ return 0
+}
+
+function verify_vol_volsize
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset vol=$3
+
+ typeset oldval
+ oldval=$(get_prop volsize $vol)
+ (( newval = oldval * 2 ))
+
+ typeset reserv_size
+
+ reserv_size=$(get_prop refreservation $vol)
+
+ if [[ "0" == $reserv_size ]]; then
+ # sparse volume
+ user_run $user $ZFS set volsize=$newval $vol
+ if [[ $oldval == $(get_prop volsize $vol) ]];
+ then
+ return 1
+ fi
+
+ else
+ # normal volume, reservation permission
+ # is required
+ user_run $user $ZFS set volsize=$newval $vol
+ zfs get -p volsize $vol
+ if [[ $newval != $(get_prop volsize $vol) ]];
+ then
+ return 1
+ fi
+
+ log_must $ZFS allow $user refreservation $vol
+ user_run $user $ZFS set volsize=$newval $vol
+ log_must $ZFS unallow $user reservation $vol
+ log_must $ZFS unallow $user refreservation $vol
+ if [[ $oldval == $(get_prop volsize $vol) ]];
+ then
+ return 1
+ fi
+ fi
+
+ return 0
+}
+
+function verify_vol_shareiscsi
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset vol=$3
+
+ typeset oldval
+ set -A modes "on" "off"
+ oldval=$(get_prop $perm $vol)
+ if [[ $oldval == "on" ]]; then
+ n=1
+ elif [[ $oldval == "off" ]]; then
+ n=0
+ fi
+ log_note "$user $ZFS set $perm=${modes[$n]} $vol"
+ user_run $user $ZFS set $perm=${modes[$n]} $vol
+ if [[ ${modes[$n]} != $(get_prop $perm $vol) ]]; then
+ return 1
+ fi
+
+ iscsitgt_setup
+
+ if [[ $n -eq 1 ]] && is_iscsi_target $vol ; then
+ iscsitgt_cleanup
+ return 1
+ fi
+
+ if [[ $n -eq 0 ]] && ! is_iscsi_target $vol ; then
+ iscsitgt_cleanup
+ return 1
+ fi
+
+ iscsitgt_cleanup
+
+ return 0
+}
+
+function verify_allow
+{
+ typeset user=$1
+ typeset perm=$2
+ typeset dtst=$3
+
+ typeset -i ret
+
+ user_run $user $ZFS allow $user allow $dtst
+ ret=$?
+ if [[ $ret -eq 0 ]]; then
+ return 1
+ fi
+
+ log_must $ZFS allow $user copies $dtst
+ user_run $user $ZFS allow $user copies $dtst
+ ret=$?
+ log_must $ZFS unallow $user copies $dtst
+ if [[ $ret -eq 1 ]]; then
+ return 1
+ fi
+
+ return 0
+
+}
diff --git a/tests/sys/cddl/zfs/tests/delegate/setup.ksh b/tests/sys/cddl/zfs/tests/delegate/setup.ksh
new file mode 100644
index 000000000000..09aad2587696
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/delegate/setup.ksh
@@ -0,0 +1,45 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/delegate/delegate_common.kshlib
+
+cleanup_user_group
+
+# Create staff group and add two user to it
+log_must add_group $STAFF_GROUP
+log_must add_user $STAFF_GROUP $STAFF1
+log_must add_user $STAFF_GROUP $STAFF2
+
+# Create other group and add two user to it
+log_must add_group $OTHER_GROUP
+log_must add_user $OTHER_GROUP $OTHER1
+log_must add_user $OTHER_GROUP $OTHER2
+
+DISK=${DISKS%% *}
+default_volume_setup $DISK
+log_must $CHMOD 777 $TESTDIR
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/delegate/zfs_allow_001_pos.ksh b/tests/sys/cddl/zfs/tests/delegate/zfs_allow_001_pos.ksh
new file mode 100644
index 000000000000..cb25123862df
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/delegate/zfs_allow_001_pos.ksh
@@ -0,0 +1,111 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/delegate/delegate_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_allow_001_pos
+#
+# DESCRIPTION:
+# "everyone" is interpreted as the keyword "everyone" whatever the same
+# name user or group is existing.
+#
+# STRATEGY:
+# 1. Create user 'everyone'.
+# 2. Verify 'everyone' is interpreted as keywords.
+# 3. Create group 'everyone'.
+# 4. Verify 'everyone' is interpreted as keywords.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-09-14)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if [[ $user_added == "TRUE" ]] ; then
+ del_user everyone
+ fi
+ if [[ $group_added == "TRUE" ]] ; then
+ del_group everyone
+ fi
+}
+
+log_assert "everyone' is interpreted as a keyword even if a user " \
+ "or group named 'everyone' exists."
+log_onexit cleanup
+
+eval set -A dataset $DATASETS
+enc=$(get_prop encryption $dataset)
+if [[ $? -eq 0 ]] && [[ -n "$enc" ]] && [[ "$enc" != "off" ]]; then
+ typeset perms="snapshot,reservation,compression,send,allow,\
+userprop"
+else
+ typeset perms="snapshot,reservation,compression,checksum,\
+send,allow,userprop"
+fi
+
+log_note "Create a user called 'everyone'."
+if ! $ID everyone > /dev/null 2>&1; then
+ user_added="TRUE"
+ log_must $USERADD everyone
+fi
+for dtst in $DATASETS ; do
+ log_must $ZFS allow everyone $perms $dtst
+ log_must verify_perm $dtst $perms $EVERYONE "everyone"
+done
+log_must restore_root_datasets
+if [[ $user_added == "TRUE" ]]; then
+ log_must $USERDEL everyone
+fi
+
+log_note "Created a group called 'everyone'."
+if ! $CAT /etc/group | $AWK -F: '{print $1}' | \
+ $GREP -w 'everyone' > /dev/null 2>&1
+then
+ group_added="TRUE"
+ log_must $GROUPADD everyone
+fi
+
+for dtst in $DATASETS ; do
+ log_must $ZFS allow everyone $perms $dtst
+ log_must verify_perm $dtst $perms $EVERYONE
+done
+if [[ $group_added == "TRUE" ]]; then
+ log_must $GROUPDEL everyone
+fi
+
+log_pass "everyone is always interpreted as keyword passed."
diff --git a/tests/sys/cddl/zfs/tests/delegate/zfs_allow_002_pos.ksh b/tests/sys/cddl/zfs/tests/delegate/zfs_allow_002_pos.ksh
new file mode 100644
index 000000000000..d39e670167f2
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/delegate/zfs_allow_002_pos.ksh
@@ -0,0 +1,93 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/delegate/delegate_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_allow_002_pos
+#
+# DESCRIPTION:
+# <user|group> argument is interpreted as a user if possible, then as a group as
+# possible.
+#
+# STRATEGY:
+# 1. Create user $STAFF_GROUP
+# 2. Delegate permissions to $STAFF_GROUP
+# 3. Verify user $STAFF_GROUP has the permissions.
+# 4. Delete user $STAFF_GROUP and allow the permission to $STAFF_GROUP
+# 5. Verify $STAFF_GROUP is interpreted as group.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-09-14)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if $ID $STAFF_GROUP > /dev/null 2>&1; then
+ log_must del_user $STAFF_GROUP
+ fi
+}
+
+log_assert "<user|group> is interpreted as user if possible, then as group."
+log_onexit cleanup
+
+eval set -A dataset $DATASETS
+enc=$(get_prop encryption $dataset)
+if [[ $? -eq 0 ]] && [[ -n "$enc" ]] && [[ "$enc" != "off" ]]; then
+ typeset perms="snapshot,reservation,compression,send,allow,\
+userprop"
+else
+ typeset perms="snapshot,reservation,compression,checksum,\
+send,allow,userprop"
+fi
+
+log_must $USERADD $STAFF_GROUP
+for dtst in $DATASETS ; do
+ log_must $ZFS allow $STAFF_GROUP $perms $dtst
+ log_must verify_perm $dtst $perms $STAFF_GROUP
+ log_must verify_noperm $dtst $perms $STAFF1 $STAFF2
+done
+
+log_must restore_root_datasets
+
+log_must del_user $STAFF_GROUP
+for dtst in $datasets ; do
+ log_must $ZFS allow $STAFF_GROUP $perms $dtst
+ log_must verify_perm $dtst $perms $STAFF1 $STAFF2
+done
+
+log_pass "<user|group> is interpreted as user if possible, then as group passed."
diff --git a/tests/sys/cddl/zfs/tests/delegate/zfs_allow_003_pos.ksh b/tests/sys/cddl/zfs/tests/delegate/zfs_allow_003_pos.ksh
new file mode 100644
index 000000000000..61026823df8f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/delegate/zfs_allow_003_pos.ksh
@@ -0,0 +1,106 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/delegate/delegate_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_allow_003_pos
+#
+# DESCRIPTION:
+# Verify option '-l' only allow permission to the dataset itself.
+#
+# STRATEGY:
+# 1. Create descendent datasets of $ROOT_TESTFS
+# 2. Select user, group and everyone and set local permission separately.
+# 3. Set locally permissions to $ROOT_TESTFS or $ROOT_TESTVOL.
+# 4. Verify the permissions are only allow on $ROOT_TESTFS or
+# $ROOT_TESTVOL.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-09-19)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify option '-l' only allow permission to the dataset itself."
+
+childfs=$ROOT_TESTFS/childfs
+
+eval set -A dataset $DATASETS
+enc=$(get_prop encryption $dataset)
+if [[ $? -eq 0 ]] && [[ -n "$enc" ]] && [[ "$enc" != "off" ]]; then
+ typeset perms="snapshot,reservation,compression,allow,\
+userprop"
+else
+ typeset perms="snapshot,reservation,compression,checksum,\
+allow,userprop"
+fi
+
+log_must $ZFS create $childfs
+
+for dtst in $DATASETS ; do
+ log_must $ZFS allow -l $STAFF1 $perms $dtst
+ log_must verify_perm $dtst $perms $STAFF1
+ if [[ $dtst == $ROOT_TESTFS ]] ; then
+ log_must verify_noperm $childfs $perms \
+ $STAFF1 $STAFF2 $OTHER1 $OTHER2
+ fi
+done
+
+log_must restore_root_datasets
+
+log_must $ZFS create $childfs
+for dtst in $DATASETS ; do
+ log_must $ZFS allow -l -g $STAFF_GROUP $perms $dtst
+ log_must verify_perm $dtst $perms $STAFF1 $STAFF2
+ if [[ $dtst == $ROOT_TESTFS ]] ; then
+ log_must verify_noperm $childfs $perms \
+ $STAFF1 $STAFF2 $OTHER1 $OTHER2
+ fi
+done
+
+log_must restore_root_datasets
+
+log_must $ZFS create $childfs
+for dtst in $DATASETS ; do
+ log_must $ZFS allow -l -e $perms $dtst
+ log_must verify_perm $dtst $perms $STAFF1 $STAFF2 $OTHER1 $OTHER2
+ if [[ $dtst == $ROOT_TESTFS ]] ; then
+ log_must verify_noperm $childfs $perms \
+ $STAFF1 $STAFF2 $OTHER1 $OTHER2
+ fi
+done
+
+log_pass "Verify option '-l' only allow permission to the dataset itself pass."
diff --git a/tests/sys/cddl/zfs/tests/delegate/zfs_allow_004_pos.ksh b/tests/sys/cddl/zfs/tests/delegate/zfs_allow_004_pos.ksh
new file mode 100644
index 000000000000..766c01193c44
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/delegate/zfs_allow_004_pos.ksh
@@ -0,0 +1,116 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/delegate/delegate_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_allow_004_pos
+#
+# DESCRIPTION:
+# Verify option '-d' allow permission to the descendent datasets, and not
+# for this dataset itself.
+#
+# STRATEGY:
+# 1. Create descendent datasets of $ROOT_TESTFS
+# 2. Select user, group and everyone and set descendent permission
+# separately.
+# 3. Set descendent permissions to $ROOT_TESTFS or $ROOT_TESTVOL.
+# 4. Verify those permissions are allowed to $ROOT_TESTFS's
+# descendent dataset.
+# 5. Verify the permissions are not allowed to $ROOT_TESTFS or
+# $ROOT_TESTVOL.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-09-18)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify option '-d' allow permission to the descendent datasets."
+log_onexit restore_root_datasets
+
+childfs=$ROOT_TESTFS/childfs
+
+eval set -A dataset $DATASETS
+enc=$(get_prop encryption $dataset)
+if [[ $? -eq 0 ]] && [[ -n "$enc" ]] && [[ "$enc" != "off" ]]; then
+ typeset perms="snapshot,reservation,compression,allow,\
+userprop"
+else
+ typeset perms="snapshot,reservation,compression,checksum,\
+allow,userprop"
+fi
+
+if check_version "5.10" ; then
+ perms="${perms},send"
+fi
+
+# Verify option '-d' only affect sub-datasets
+log_must $ZFS create $childfs
+for dtst in $DATASETS ; do
+ log_must $ZFS allow -d $STAFF1 $perms $dtst
+ log_must verify_noperm $dtst $perms $STAFF1
+ if [[ $dtst == $ROOT_TESTFS ]]; then
+ log_must verify_perm $childfs $perms $STAFF1
+ fi
+done
+
+log_must restore_root_datasets
+
+# Verify option '-d + -g' affect group in sub-datasets.
+log_must $ZFS create $childfs
+for dtst in $DATASETS ; do
+ log_must $ZFS allow -d -g $STAFF_GROUP $perms $dtst
+ log_must verify_noperm $dtst $perms $STAFF2
+ if [[ $dtst == $ROOT_TESTFS ]]; then
+ log_must verify_perm $childfs $perms $STAFF2
+ fi
+done
+
+log_must restore_root_datasets
+
+# Verify option '-d + -e' affect everyone in sub-datasets.
+log_must $ZFS create $childfs
+for dtst in $DATASETS ; do
+ log_must $ZFS allow -d -e $perms $dtst
+ log_must verify_noperm $dtst $perms $OTHER1 $OTHER2
+ if [[ $dtst == $ROOT_TESTFS ]]; then
+ log_must verify_perm $childfs $perms $OTHER1 $OTHER2
+ fi
+done
+
+log_must restore_root_datasets
+
+log_pass "Verify option '-d' allow permission to the descendent datasets pass."
diff --git a/tests/sys/cddl/zfs/tests/delegate/zfs_allow_005_pos.ksh b/tests/sys/cddl/zfs/tests/delegate/zfs_allow_005_pos.ksh
new file mode 100644
index 000000000000..287bed5d363d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/delegate/zfs_allow_005_pos.ksh
@@ -0,0 +1,98 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/delegate/delegate_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_allow_005_pos
+#
+# DESCRIPTION:
+# Verify option '-c' will be granted locally to the creator on any
+# newly-created descendent file systems.
+#
+# STRATEGY:
+# 1. Allow create permissions to everyone on $ROOT_TESTFS locally.
+# 2. Allow '-c' create to $ROOT_TESTFS.
+# 3. chmod 777 the mountpoint of $ROOT_TESTFS
+# 4. Verify only creator can create descendent dataset on
+# $ROOT_TESTFS/$user.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-09-19)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify option '-c' will be granted locally to the creator."
+log_onexit restore_root_datasets
+
+eval set -A dataset $DATASETS
+enc=$(get_prop encryption $dataset)
+if [[ $? -eq 0 ]] && [[ -n "$enc" ]] && [[ "$enc" != "off" ]]; then
+ typeset perms="snapshot,reservation,compression,allow,\
+userprop"
+else
+ typeset perms="snapshot,reservation,compression,checksum,\
+allow,userprop"
+fi
+
+if check_version "5.10" ; then
+ perms="${perms},send"
+fi
+
+log_must $ZFS allow -l everyone create,mount $ROOT_TESTFS
+log_must $ZFS allow -c $perms $ROOT_TESTFS
+
+mntpnt=$(get_prop mountpoint $ROOT_TESTFS)
+log_must $CHMOD 777 $mntpnt
+
+for user in $EVERYONE; do
+ childfs=$ROOT_TESTFS/$user
+
+ user_run $user $ZFS create $childfs
+
+ for other in $EVERYONE; do
+ #
+ # Verify only the creator has the $perm time permissions.
+ #
+ if [[ $other == $user ]]; then
+ log_must verify_perm $childfs $perms $user
+ else
+ log_must verify_noperm $childfs $perms $other
+ fi
+ done
+done
+
+log_pass "Verify option '-c' will be granted locally to the creator passed."
diff --git a/tests/sys/cddl/zfs/tests/delegate/zfs_allow_006_pos.ksh b/tests/sys/cddl/zfs/tests/delegate/zfs_allow_006_pos.ksh
new file mode 100644
index 000000000000..3d0f5cdd8898
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/delegate/zfs_allow_006_pos.ksh
@@ -0,0 +1,86 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/delegate/delegate_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_allow_006_pos
+#
+# DESCRIPTION:
+# Changing permissions in a set will change what is allowed wherever the
+# set is used.
+#
+# STRATEGY:
+# 1. Set create as set @basic.
+# 2. Allow set @basic to $STAFF1 on $ROOT_TESTFS or $ROOT_TESTVOL
+# 3. Verify $STAFF1 has create permissions.
+# 4. Reset snapshot,allow to $basic
+# 5. Verify now $STAFF1 have create,allow,destroy permissions.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-09-19)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Changing permissions in a set will change what is allowed " \
+ "wherever the set is used."
+log_onexit restore_root_datasets
+
+fs1=$ROOT_TESTFS/fs1; fs2=$ROOT_TESTFS/fs2
+log_must $ZFS create $fs1
+log_must $ZFS create $fs2
+
+eval set -A dataset $DATASETS
+enc=$(get_prop encryption $dataset)
+if [[ $? -eq 0 ]] && [[ -n "$enc" ]] && [[ "$enc" != "off" ]]; then
+ perms1="snapshot,checksum,reservation"
+else
+ perms1="snapshot,reservation"
+fi
+
+for dtst in $DATASETS $fs1 $fs2; do
+ log_must $ZFS allow -s @basic $perms1 $dtst
+ log_must $ZFS allow $STAFF1 @basic $dtst
+ log_must verify_perm $dtst $perms1 $STAFF1
+done
+
+perms2="allow,send,compression,userprop"
+for dtst in $DATASETS $fs1 $fs2; do
+ log_must $ZFS allow -s @basic $perms2 $dtst
+ log_must verify_perm $dtst ${perms1},${perms2} $STAFF1
+done
+
+log_pass "Changing permissions in a set will change what is allowed passed."
diff --git a/tests/sys/cddl/zfs/tests/delegate/zfs_allow_007_pos.ksh b/tests/sys/cddl/zfs/tests/delegate/zfs_allow_007_pos.ksh
new file mode 100644
index 000000000000..57b60f4bc12c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/delegate/zfs_allow_007_pos.ksh
@@ -0,0 +1,116 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/delegate/delegate_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_allow_007_pos
+#
+# DESCRIPTION:
+# Verify the permissions set will be masked on its descendent
+# datasets by same name set.
+#
+# STRATEGY:
+# 1. Create $ROOT_TESTFS/childfs
+# 2. Set permission $perms1 to @set on $ROOT_TESTFS
+# 3. Reset permission $perms2 to @set on $ROOT_TESTFS/childfs
+# 4. Allow @set to $STAFF1 on $ROOT_TESTFS/childfs
+# 5. Verify $perms2 is delegated on $ROOT_TESTFS/childfs and its
+# descendent.
+# 6. Allow @set to $STAFF1 on $ROOT_TESTFS
+# 7. Verify $perms1 is not appended to $STAFF1 on $ROOT_TESTFS/childfs and
+# its descendent since it is masked
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-09-19)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify permission set can be masked on descendent dataset."
+
+typeset perms1="snapshot,reservation,compression"
+eval set -A dataset $DATASETS
+enc=$(get_prop encryption $dataset)
+if [[ $? -eq 0 ]] && [[ -n "$enc" ]] && [[ "$enc" != "off" ]]; then
+ typeset perms2="send,allow,userprop"
+else
+ typeset perms2="checksum,send,allow,userprop"
+fi
+
+#
+# Define three level filesystems
+#
+childfs=$ROOT_TESTFS/childfs
+grandchild=$childfs/grandchild
+log_must $ZFS create $childfs
+log_must $ZFS create $grandchild
+
+#
+# Setting different permissions to the same set on two level.
+# But only assign the user at one level.
+#
+log_must $ZFS allow -s @set $perms1 $ROOT_TESTFS
+log_must $ZFS allow -s @set $perms2 $childfs
+log_must $ZFS allow $STAFF1 @set $childfs
+
+#
+# Verify only perms2 is valid to user on the level which he was assigned.
+#
+log_must verify_noperm $ROOT_TESTFS $perms1 $STAFF1
+for fs in $childfs $grandchild ; do
+ log_must verify_noperm $childfs $perms1 $STAFF1
+ log_must verify_perm $childfs $perms2 $STAFF1
+done
+
+#
+# Delegate @set to STAFF1 on ROOT_TESTFS, verify $perms1 will not be appended
+# to its descendent datasets since it is masked
+#
+log_must $ZFS allow $STAFF1 @set $ROOT_TESTFS
+log_must verify_perm $ROOT_TESTFS $perms1 $STAFF1
+for fs in $childfs $grandchild ; do
+ log_must verify_noperm $childfs $perms1 $STAFF1
+ log_must verify_perm $childfs $perms2 $STAFF1
+done
+
+# Remove the mask, $perms1 will be allowed to its descendent datasets
+log_must $ZFS unallow -s @set $childfs
+for fs in $childfs $grandchild ; do
+ log_must verify_noperm $childfs $perms2 $STAFF1
+ log_must verify_perm $childfs $perms1 $STAFF1
+done
+
+log_pass "Verify permission set can be masked on descendetn dataset pass."
diff --git a/tests/sys/cddl/zfs/tests/delegate/zfs_allow_008_pos.ksh b/tests/sys/cddl/zfs/tests/delegate/zfs_allow_008_pos.ksh
new file mode 100644
index 000000000000..16102619831a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/delegate/zfs_allow_008_pos.ksh
@@ -0,0 +1,87 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/delegate/delegate_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_allow_008_pos
+#
+# DESCRIPTION:
+# non-root user can allow any permissions which he is holding to
+# other else user when it get 'allow' permission.
+#
+# STRATEGY:
+# 1. Set two set permissions to two datasets locally.
+# 2. Verify the non-root user can allow permission if he has allow
+# permission.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-09-20)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify non-root user can allow permissions."
+log_onexit restore_root_datasets
+
+perms1="snapshot,reservation"
+perms2="send,compression,checksum,userprop"
+childfs=$ROOT_TESTFS/childfs
+
+log_must $ZFS create $childfs
+
+for dtst in $DATASETS ; do
+ # Delegate local permission to $STAFF1
+ log_must $ZFS allow -l $STAFF1 $perms1 $dtst
+ log_must $ZFS allow -l $STAFF1 allow $dtst
+
+ if [[ $dtst == $ROOT_TESTFS ]]; then
+ log_must $ZFS allow -l $STAFF1 $perms2 $childfs
+ # $perms1 is local permission in $ROOT_TESTFS
+ log_mustnot user_run $STAFF1 $ZFS allow $OTHER1 $perms1 $childfs
+ log_must verify_noperm $childfs $perms1 $OTHER1
+ fi
+
+ # Verify 'allow' give non-privilege user delegated permission.
+ log_must user_run $STAFF1 $ZFS allow -l $OTHER1 $perms1 $dtst
+ log_must verify_perm $dtst $perms1 $OTHER1
+
+ # $perms2 was not allow to $STAFF1, so he have no permission to
+ # delegate permission to other else.
+ log_mustnot user_run $STAFF1 $ZFS allow $OTHER1 $perms2 $dtst
+ log_must verify_noperm $dtst $perms2 $OTHER1
+done
+
+log_pass "Verify non-root user can allow permissions passed."
diff --git a/tests/sys/cddl/zfs/tests/delegate/zfs_allow_009_neg.ksh b/tests/sys/cddl/zfs/tests/delegate/zfs_allow_009_neg.ksh
new file mode 100644
index 000000000000..3791d45ec2dd
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/delegate/zfs_allow_009_neg.ksh
@@ -0,0 +1,73 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/delegate/delegate_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_allow_009_neg
+#
+# DESCRIPTION:
+# zfs allow can deal with invalid arguments.(Invalid options or combination)
+#
+# STRATEGY:
+# 1. Verify invalid argumets will cause error.
+# 2. Verify non-optional argument was missing will cause error.
+# 3. Verify invalid options cause error.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-09-20)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify invalid arguments are handled correctly."
+log_onexit restore_root_datasets
+
+# Permission sets are limited to 64 characters in length.
+longset="set123456789012345678901234567890123456789012345678901234567890123"
+for dtst in $DATASETS ; do
+ log_mustnot eval "$ZFS allow -s @$longset $dtst"
+ # Create non-existent permission set
+ typeset timestamp=$($DATE +'%F-%R:%S')
+ log_mustnot $ZFS allow -s @non-existent $dtst
+ log_mustnot $ZFS allow $STAFF "atime,created,mounted" $dtst
+ log_mustnot $ZFS allow $dtst $TESTPOOL
+ log_mustnot $ZFS allow -c $dtst
+ log_mustnot $ZFS allow -u $STAFF1 $dtst
+ log_mustnot $ZFS allow -u $STAFF1 -g $STAFF_GROUP "create,destroy" $dtst
+ log_mustnot $ZFS allow -u $STAFF1 -e "mountpoint" $dtst
+done
+
+log_pass "Invalid arguments are handled correctly."
diff --git a/tests/sys/cddl/zfs/tests/delegate/zfs_allow_010_pos.ksh b/tests/sys/cddl/zfs/tests/delegate/zfs_allow_010_pos.ksh
new file mode 100644
index 000000000000..e45214e0681c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/delegate/zfs_allow_010_pos.ksh
@@ -0,0 +1,119 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/delegate/delegate_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_allow_010_pos
+#
+# DESCRIPTION:
+# Scan the following permissions one by one to verify privileged user
+# has correct permission delegation in datasets.
+#
+# STRATEGY:
+# 1. Delegate all the permission one by one to user on dataset.
+# 2. Verify privileged user has correct permission without any other
+# permissions allowed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-11-02)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify privileged user has correct permissions once which was "\
+ "delegated to him in datasets"
+
+#
+# Results in Results in
+# Permission Filesystem Volume
+#
+set -A perms create true false \
+ snapshot true true \
+ mount true false \
+ send true true \
+ allow true true \
+ quota true false \
+ reservation true true \
+ recordsize true false \
+ mountpoint true false \
+ checksum true true \
+ compression true true \
+ canmount true false \
+ atime true false \
+ exec true false \
+ volsize false true \
+ setuid true false \
+ readonly true true \
+ snapdir true false \
+ userprop true true \
+ aclmode true false \
+ aclinherit true false \
+ rollback true true \
+ clone true true \
+ rename true true \
+ promote true true \
+ receive true false \
+ destroy true true
+ # TODO: shareiscsi is not yet supported on FreeBSD
+ # shareiscsi true true
+# the sharenfs test is Solaris-specific. TODO: port it to FreeBSD.
+#typeset -i n=${#perms[@]}
+#perms[((n))]="sharenfs"; perms[((n+1))]="true"; perms[((n+2))]="false"
+#perms[((n+3))]="share"; perms[((n+4))]="true"; perms[((n+5))]="false"
+
+for dtst in $DATASETS; do
+ typeset -i k=1
+ typeset type=$(get_prop type $dtst)
+ [[ $type == "volume" ]] && k=2
+
+ typeset -i i=0
+ while (( i < ${#perms[@]} )); do
+ log_must $ZFS allow $STAFF1 ${perms[$i]} $dtst
+
+ if [[ ${perms[((i+k))]} == "true" ]]; then
+ log_must verify_perm $dtst ${perms[$i]} $STAFF1
+ else
+ log_must verify_noperm $dtst ${perms[$i]} $STAFF1
+ fi
+
+ log_must restore_root_datasets
+
+ ((i += 3))
+ done
+done
+
+log_pass "Verify privileged user has correct permissions " \
+ "in datasets passed."
diff --git a/tests/sys/cddl/zfs/tests/delegate/zfs_allow_011_neg.ksh b/tests/sys/cddl/zfs/tests/delegate/zfs_allow_011_neg.ksh
new file mode 100644
index 000000000000..0b0f90a75cac
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/delegate/zfs_allow_011_neg.ksh
@@ -0,0 +1,77 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/delegate/delegate_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_allow_011_neg
+#
+# DESCRIPTION:
+# Verify zpool subcmds and system readonly properties can't be delegated.
+#
+# STRATEGY:
+# 1. Loop all the zpool subcmds and readonly properties, except permission
+# 'create' & 'destroy'.
+# 2. Verify those subcmd or properties can't be delegated.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-12-13)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify zpool subcmds and system readonly properties can't be " \
+ "delegated."
+log_onexit restore_root_datasets
+
+set -A invalid_perms \
+ add remove list iostat \
+ status offline online clear \
+ attach detach replace scrub \
+ export import upgrade \
+ type creation used available \
+ referenced compressratio mounted
+
+for dtst in $DATASETS ; do
+ typeset -i i=0
+
+ while ((i < ${#invalid_perms[@]})); do
+ log_mustnot $ZFS allow $STAFF1 ${invalid_perms[$i]} $dtst
+
+ ((i += 1))
+ done
+done
+
+log_pass "Verify zpool subcmds and system readonly properties passed."
diff --git a/tests/sys/cddl/zfs/tests/delegate/zfs_allow_012_neg.ksh b/tests/sys/cddl/zfs/tests/delegate/zfs_allow_012_neg.ksh
new file mode 100644
index 000000000000..aed52e8b12c0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/delegate/zfs_allow_012_neg.ksh
@@ -0,0 +1,80 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/delegate/delegate_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_allow_012_neg
+#
+# DESCRIPTION:
+# Scan all permissions one by one to verify privileged user
+# can not use permissions properly when delegation property is set off
+#
+# STRATEGY:
+# 1. Delegate all the permission one by one to user on dataset.
+# 2. Verify privileged user can not use permissions properly when
+# delegation property is off
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-19)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+log_assert "Verify privileged user can not use permissions properly when " \
+ "delegation property is set off"
+
+set -A perms create snapshot mount send allow quota reservation \
+ recordsize mountpoint checksum compression canmount atime \
+ devices exec volsize setuid readonly snapdir userprop \
+ aclmode aclinherit rollback clone rename promote \
+ xattr receive destroy
+# TODO: add sharenfs and share after the Solarisisms have been removed from
+# those tests
+
+log_must $ZPOOL set delegation=off $TESTPOOL
+
+for dtst in $DATASETS; do
+ typeset -i i=0
+ while (( i < ${#perms[@]} )); do
+
+ log_must $ZFS allow $STAFF1 ${perms[$i]} $dtst
+ log_must verify_noperm $dtst ${perms[$i]} $STAFF1
+
+ log_must restore_root_datasets
+ ((i += 1))
+ done
+done
+
+log_pass "Verify privileged user can not use permissions properly when " \
+ "delegation property is set off"
diff --git a/tests/sys/cddl/zfs/tests/delegate/zfs_allow_test.sh b/tests/sys/cddl/zfs/tests/delegate/zfs_allow_test.sh
new file mode 100755
index 000000000000..3a3f2d05ba17
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/delegate/zfs_allow_test.sh
@@ -0,0 +1,354 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zfs_allow_001_pos cleanup
+zfs_allow_001_pos_head()
+{
+ atf_set "descr" "everyone' is interpreted as a keyword even if a useror group named 'everyone' exists."
+ atf_set "require.progs" "ksh93 zfs sudo"
+}
+zfs_allow_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_allow_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_allow_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_allow_002_pos cleanup
+zfs_allow_002_pos_head()
+{
+ atf_set "descr" "<user|group> is interpreted as user if possible, then as group."
+ atf_set "require.progs" "ksh93 zfs sudo"
+}
+zfs_allow_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_allow_002_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_allow_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_allow_003_pos cleanup
+zfs_allow_003_pos_head()
+{
+ atf_set "descr" "Verify option '-l' only allow permission to the dataset itself."
+ atf_set "require.progs" "ksh93 zfs sudo"
+}
+zfs_allow_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_allow_003_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_allow_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_allow_004_pos cleanup
+zfs_allow_004_pos_head()
+{
+ atf_set "descr" "Verify option '-d' allow permission to the descendent datasets."
+ atf_set "require.progs" "ksh93 zfs sudo"
+}
+zfs_allow_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_allow_004_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_allow_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_allow_005_pos cleanup
+zfs_allow_005_pos_head()
+{
+ atf_set "descr" "Verify option '-c' will be granted locally to the creator."
+ atf_set "require.progs" "ksh93 zfs sudo"
+}
+zfs_allow_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_allow_005_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_allow_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_allow_006_pos cleanup
+zfs_allow_006_pos_head()
+{
+ atf_set "descr" "Changing permissions in a set will change what is allowedwherever the set is used."
+ atf_set "require.progs" "ksh93 zfs sudo"
+}
+zfs_allow_006_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_allow_006_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_allow_006_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_allow_007_pos cleanup
+zfs_allow_007_pos_head()
+{
+ atf_set "descr" "Verify permission set can be masked on descendent dataset."
+ atf_set "require.progs" "ksh93 zfs sudo"
+}
+zfs_allow_007_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_allow_007_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_allow_007_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_allow_008_pos cleanup
+zfs_allow_008_pos_head()
+{
+ atf_set "descr" "Verify non-root user can allow permissions."
+ atf_set "require.progs" "ksh93 zfs sudo"
+}
+zfs_allow_008_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_allow_008_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_allow_008_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_allow_009_neg cleanup
+zfs_allow_009_neg_head()
+{
+ atf_set "descr" "Verify invalid arguments are handled correctly."
+ atf_set "require.progs" "ksh93 zfs sudo"
+}
+zfs_allow_009_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_allow_009_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_allow_009_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_allow_010_pos cleanup
+zfs_allow_010_pos_head()
+{
+ atf_set "descr" "Verify privileged user has correct permissions once which was delegated to him in datasets"
+ atf_set "require.progs" "ksh93 zfs sudo"
+}
+zfs_allow_010_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_allow_010_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_allow_010_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_allow_011_neg cleanup
+zfs_allow_011_neg_head()
+{
+ atf_set "descr" "Verify zpool subcmds and system readonly properties can't be delegated."
+ atf_set "require.progs" "ksh93 zfs sudo"
+}
+zfs_allow_011_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_allow_011_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_allow_011_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_allow_012_neg cleanup
+zfs_allow_012_neg_head()
+{
+ atf_set "descr" "Verify privileged user can not use permissions properly when delegation property is set off"
+ atf_set "require.progs" "ksh93 zfs sudo"
+}
+zfs_allow_012_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_allow_012_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_allow_012_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zfs_allow_001_pos
+ atf_add_test_case zfs_allow_002_pos
+ atf_add_test_case zfs_allow_003_pos
+ atf_add_test_case zfs_allow_004_pos
+ atf_add_test_case zfs_allow_005_pos
+ atf_add_test_case zfs_allow_006_pos
+ atf_add_test_case zfs_allow_007_pos
+ atf_add_test_case zfs_allow_008_pos
+ atf_add_test_case zfs_allow_009_neg
+ atf_add_test_case zfs_allow_010_pos
+ atf_add_test_case zfs_allow_011_neg
+ atf_add_test_case zfs_allow_012_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/delegate/zfs_unallow_001_pos.ksh b/tests/sys/cddl/zfs/tests/delegate/zfs_unallow_001_pos.ksh
new file mode 100644
index 000000000000..2b1c9ea67d83
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/delegate/zfs_unallow_001_pos.ksh
@@ -0,0 +1,74 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/delegate/delegate_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_unallow_001_pos
+#
+# DESCRIPTION:
+# Verify '-l' only removed the local permissions.
+#
+# STRATEGY:
+# 1. Set up unallow test model.
+# 2. Implement unallow -l to $ROOT_TESTFS or $TESTVOL
+# 3. Verify '-l' only remove the local permissions.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-09-29)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify '-l' only removed the local permissions."
+log_onexit restore_root_datasets
+
+log_must setup_unallow_testenv
+
+for dtst in $DATASETS ; do
+ log_must $ZFS unallow -l $STAFF1 $dtst
+ log_must verify_noperm $dtst $LOCAL_SET $STAFF1
+
+ log_must $ZFS unallow -l $OTHER1 $dtst
+ log_must verify_noperm $dtst $LOCAL_DESC_SET $OTHER1
+
+ log_must verify_perm $dtst $LOCAL_DESC_SET $OTHER2
+ if [[ $dtst == $ROOT_TESTFS ]]; then
+ log_must verify_perm $SUBFS $LOCAL_DESC_SET $OTHER1 $OTHER2
+ log_must verify_perm $SUBFS $DESC_SET $STAFF2
+ fi
+done
+
+log_pass "Verify '-l' only removed the local permissions passed."
diff --git a/tests/sys/cddl/zfs/tests/delegate/zfs_unallow_002_pos.ksh b/tests/sys/cddl/zfs/tests/delegate/zfs_unallow_002_pos.ksh
new file mode 100644
index 000000000000..17507fe8d8b9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/delegate/zfs_unallow_002_pos.ksh
@@ -0,0 +1,70 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/delegate/delegate_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_unallow_002_pos
+#
+# DESCRIPTION:
+# Verify '-d' only remove the permissions on descendent filesystem.
+
+# STRATEGY:
+# 1. Set up unallow test model.
+# 2. Implement unallow -d to $ROOT_TESTFS
+# 3. Verify '-d' only remove the permissions on descendent filesystem.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-09-29)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify '-d' only removed the descendent permissions."
+log_onexit restore_root_datasets
+
+log_must setup_unallow_testenv
+
+log_must $ZFS unallow -d $STAFF2 $ROOT_TESTFS
+log_must verify_noperm $SUBFS $DESC_SET $STAFF2
+
+log_must $ZFS unallow -d $OTHER1 $ROOT_TESTFS
+log_must verify_noperm $SUBFS $LOCAL_DESC_SET $OTHER1
+log_must verify_perm $ROOT_TESTFS $LOCAL_DESC_SET $OTHER1
+
+log_must verify_perm $ROOT_TESTFS $LOCAL_DESC_SET $OTHER2
+log_must verify_perm $SUBFS $LOCAL_DESC_SET $OTHER2
+
+log_pass "Verify '-d' only removed the descendent permissions passed"
diff --git a/tests/sys/cddl/zfs/tests/delegate/zfs_unallow_003_pos.ksh b/tests/sys/cddl/zfs/tests/delegate/zfs_unallow_003_pos.ksh
new file mode 100644
index 000000000000..4ff8625f39a8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/delegate/zfs_unallow_003_pos.ksh
@@ -0,0 +1,80 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/delegate/delegate_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_unallow_003_pos
+#
+# DESCRIPTION:
+# Verify options '-r' or '-l' + '-d' will unallow permission to this
+# dataset and the descendent datasets.
+#
+# STRATEGY:
+# 1. Set up unallow test model.
+# 2. Implement unallow -l -d to $ROOT_TESTFS or $ROOT_TESTVOL without
+# options.
+# 3. Verify '-l' + '-d' will unallow local + descendent permission.
+# 4. Verify '-r' will unallow local + descendent permission.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-09-29)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify options '-r' and '-l'+'-d' will unallow permission to " \
+ "this dataset and the descendent datasets."
+log_onexit restore_root_datasets
+
+log_must setup_unallow_testenv
+
+for dtst in $DATASETS ; do
+ log_must $ZFS unallow $STAFF1 $dtst
+ log_must $ZFS unallow -l -d $STAFF2 $dtst
+ log_must verify_noperm $dtst $LOCAL_SET $STAFF1
+ if [[ $dtst == $ROOT_TESTFS ]]; then
+ log_must verify_noperm $SUBFS $DESC_SET $STAFF2
+ fi
+
+ log_must $ZFS unallow -l -d $OTHER1 $dtst
+ log_must $ZFS unallow -r $OTHER2 $dtst
+ log_must verify_noperm $dtst $LOCAL_DESC_SET $OTHER1 $OTHER2
+ if [[ $dtst == $ROOT_TESTFS ]]; then
+ log_must verify_noperm $SUBFS $LOCAL_DESC_SET $OTHER1 $OTHER2
+ fi
+done
+
+log_pass "Verify options '-r' and '-l'+'-d' function passed."
diff --git a/tests/sys/cddl/zfs/tests/delegate/zfs_unallow_004_pos.ksh b/tests/sys/cddl/zfs/tests/delegate/zfs_unallow_004_pos.ksh
new file mode 100644
index 000000000000..42071eb3af40
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/delegate/zfs_unallow_004_pos.ksh
@@ -0,0 +1,68 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/delegate/delegate_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_unallow_004_pos
+#
+# DESCRIPTION:
+# Verify '-s' will remove permissions from the named set.
+#
+# STRATEGY:
+# 1. Set @basic set to $ROOT_TESTFS or $ROOT_TESTVOL and allow @basic
+# to $STAFF1
+# 2. Verify $STAFF1 have @basic permissions.
+# 3. Verify '-s' will remove permission from the named set.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-09-29)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify '-s' will remove permissions from the named set."
+log_onexit restore_root_datasets
+
+for dtst in $DATASETS ; do
+ log_must $ZFS allow -s @basic $LOCAL_DESC_SET $dtst
+ log_must $ZFS allow -u $STAFF1 @basic $dtst
+
+ log_must verify_perm $dtst $LOCAL_DESC_SET $STAFF1
+ log_must $ZFS unallow -s @basic $LOCAL_DESC_SET $dtst
+ log_must verify_noperm $dtst $LOCAL_DESC_SET $STAFF1
+done
+
+log_pass "Verify '-s' will remove permissions from the named set passed."
diff --git a/tests/sys/cddl/zfs/tests/delegate/zfs_unallow_005_pos.ksh b/tests/sys/cddl/zfs/tests/delegate/zfs_unallow_005_pos.ksh
new file mode 100644
index 000000000000..6ffb46e420d0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/delegate/zfs_unallow_005_pos.ksh
@@ -0,0 +1,82 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/delegate/delegate_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_unallow_005_pos
+#
+# DESCRIPTION:
+# Verify option '-c' will remove the created permission set.
+#
+# STRATEGY:
+# 1. Set created time set to $ROOT_TESTFS.
+# 2. Allow permission create to $STAFF1 on $ROOT_TESTFS.
+# 3. Create $SUBFS and verify $STAFF1 have created time permissions.
+# 4. Verify $STAFF1 has created time permission.
+# 5. Unallow created time permission with option '-c'.
+# 6. Created $SUBFS and verify $STAFF1 have not created time permissions.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-09-30)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify option '-c' will remove the created permission set."
+log_onexit restore_root_datasets
+
+log_must $ZFS allow -c $LOCAL_SET $ROOT_TESTFS
+log_must $ZFS allow -l $STAFF1 create,mount $ROOT_TESTFS
+
+# Create $SUBFS and verify $SUBFS has created time permissions.
+user_run $STAFF1 $ZFS create $SUBFS
+if ! datasetexists $SUBFS ; then
+ log_fail "ERROR: ($STAFF1): $ZFS create $SUBFS"
+fi
+log_must verify_perm $SUBFS $LOCAL_SET $STAFF1
+
+#
+# After unallow -c, create $SUBFS2 and verify $SUBFS2 has not created time
+# permissions any more.
+#
+log_must $ZFS unallow -c $LOCAL_SET $ROOT_TESTFS
+user_run $STAFF1 $ZFS create $SUBFS2
+if ! datasetexists $SUBFS2 ; then
+ log_fail "ERROR: ($STAFF1): $ZFS create $SUBFS2"
+fi
+log_must verify_noperm $SUBFS2 $LOCAL_SET $STAFF1
+
+log_pass "Verify option '-c' will remove the created permission set passed."
diff --git a/tests/sys/cddl/zfs/tests/delegate/zfs_unallow_006_pos.ksh b/tests/sys/cddl/zfs/tests/delegate/zfs_unallow_006_pos.ksh
new file mode 100644
index 000000000000..b6fd11025202
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/delegate/zfs_unallow_006_pos.ksh
@@ -0,0 +1,80 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/delegate/delegate_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_unallow_006_pos
+#
+# DESCRIPTION:
+# Verify option '-u', '-g' and '-e' only removed the specified type
+# permissions set.
+#
+# STRATEGY:
+# 1. Allow '-u' '-g' & '-e' to $STAFF1 on ROOT_TESTFS or $ROOT_TESTVOL.
+# 2. Unallow '-u' '-g' & '-e' on $ROOT_TESTFS or $ROOT_TESTVOL separately.
+# 3. Verify permissions on $ROOT_TESTFS or $ROOT_TESTVOL separately.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-09-30)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify option '-u', '-g' and '-e' only removed the specified type "\
+ "permissions set."
+log_onexit restore_root_datasets
+
+for dtst in $DATASETS ; do
+ log_must $ZFS allow -u $STAFF1 $LOCAL_DESC_SET $dtst
+ log_must $ZFS allow -g $STAFF_GROUP $LOCAL_DESC_SET $dtst
+ log_must $ZFS allow -e $LOCAL_DESC_SET $dtst
+
+ log_must verify_perm $dtst $LOCAL_DESC_SET \
+ $STAFF1 $STAFF2 $OTHER1 $OTHER2
+
+ log_must $ZFS unallow -e $dtst
+ log_must verify_perm $dtst $LOCAL_DESC_SET $STAFF1 $STAFF2
+ log_must verify_noperm $dtst $LOCAL_DESC_SET $OTHER1 $OTHER2
+
+ log_must $ZFS unallow -g $STAFF_GROUP $dtst
+ log_must verify_perm $dtst $LOCAL_DESC_SET $STAFF1
+ log_must verify_noperm $dtst $LOCAL_DESC_SET $STAFF2
+
+ log_must $ZFS unallow -u $STAFF1 $dtst
+ log_must verify_noperm $dtst $LOCAL_DESC_SET $STAFF1
+done
+
+log_pass "Verify option '-u', '-g' and '-e' passed."
diff --git a/tests/sys/cddl/zfs/tests/delegate/zfs_unallow_007_neg.ksh b/tests/sys/cddl/zfs/tests/delegate/zfs_unallow_007_neg.ksh
new file mode 100644
index 000000000000..cbad400a0808
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/delegate/zfs_unallow_007_neg.ksh
@@ -0,0 +1,73 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/delegate/delegate_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_unallow_007_neg
+#
+# DESCRIPTION:
+# zfs unallow will not remove those permissions which inherited from
+# its parent filesystem.
+#
+# STRATEGY:
+# 1. Assign perm1 to $ROOT_TESTFS
+# 2. Create $SUBFS and assign perm2 to it.
+# 3. Verify unallow can not affect perm1 on $SUBFS
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-09-30)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "zfs unallow won't remove those permissions which inherited from " \
+ "its parent dataset."
+log_onexit restore_root_datasets
+
+perm1="atime"; perm2="compression,checksum"
+log_must $ZFS create $SUBFS
+log_must $ZFS allow $STAFF1 $perm1 $ROOT_TESTFS
+log_must $ZFS allow $STAFF1 $perm2 $SUBFS
+
+log_must verify_perm $SUBFS ${perm1},${perm2} $STAFF1
+#
+# Athrough unallow the permissions which don't exists on the specific dataset
+# return 0, the inherited permissions can't be removed in fact.
+#
+log_must $ZFS unallow -u $STAFF1 $perm1 $SUBFS
+log_must verify_perm $SUBFS ${perm1},${perm2} $STAFF1
+
+log_pass "Verify zfs unallow won't remove inherited permissions passed."
diff --git a/tests/sys/cddl/zfs/tests/delegate/zfs_unallow_008_neg.ksh b/tests/sys/cddl/zfs/tests/delegate/zfs_unallow_008_neg.ksh
new file mode 100644
index 000000000000..3cf5914c09c5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/delegate/zfs_unallow_008_neg.ksh
@@ -0,0 +1,83 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/delegate/delegate_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_unallow_008_neg
+#
+# DESCRIPTION:
+# zfs unallow can handle invalid arguments.
+#
+# STRATEGY:
+# 1. Set up basic test environment.
+# 2. Verify zfs unallow handle invalid arguments correctly.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-09-30)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "zfs unallow can handle invalid arguments."
+log_onexit restore_root_datasets
+
+function neg_test
+{
+ log_mustnot eval "$@ > /dev/null 2>&1"
+}
+
+set -A badopts "everyone -e" "everyone -u $STAFF1" "everyone everyone" \
+ "-c -l" "-c -d" "-c -e" "-c -s" "-r" \
+ "-u -e" "-s -e" "-s -l -d" "-s @non-exist-set -l" \
+ "-s @non-existen-set -d" "-s @non-existen-set -e" \
+ "-r -u $STAFF1 $STAFF1" "-u $STAFF1 -g $STAFF_GROUP" \
+ "-u $STAFF1 -e"
+
+log_must setup_unallow_testenv
+
+for dtst in $DATASETS ; do
+ log_must $ZFS allow -c create $dtst
+
+ typeset -i i=0
+ while ((i < ${#badopts[@]})); do
+ neg_test $ZFS unallow ${badopts[$i]} $dtst
+ ((i += 1))
+ done
+
+ neg_test user_run $STAFF1 $ZFS unallow $dtst
+done
+
+log_pass "zfs unallow can handle invalid arguments passed."
diff --git a/tests/sys/cddl/zfs/tests/delegate/zfs_unallow_test.sh b/tests/sys/cddl/zfs/tests/delegate/zfs_unallow_test.sh
new file mode 100755
index 000000000000..89db7a1205e6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/delegate/zfs_unallow_test.sh
@@ -0,0 +1,246 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zfs_unallow_001_pos cleanup
+zfs_unallow_001_pos_head()
+{
+ atf_set "descr" "Verify '-l' only removed the local permissions."
+ atf_set "require.progs" "ksh93 zfs sudo"
+}
+zfs_unallow_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_unallow_001_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_unallow_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_unallow_002_pos cleanup
+zfs_unallow_002_pos_head()
+{
+ atf_set "descr" "Verify '-d' only removed the descendent permissions."
+ atf_set "require.progs" "ksh93 zfs sudo"
+}
+zfs_unallow_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_unallow_002_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_unallow_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_unallow_003_pos cleanup
+zfs_unallow_003_pos_head()
+{
+ atf_set "descr" "Verify options '-r' and '-l'+'-d' will unallow permission tothis dataset and the descendent datasets."
+ atf_set "require.progs" "ksh93 zfs sudo"
+}
+zfs_unallow_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_unallow_003_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_unallow_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_unallow_004_pos cleanup
+zfs_unallow_004_pos_head()
+{
+ atf_set "descr" "Verify '-s' will remove permissions from the named set."
+ atf_set "require.progs" "ksh93 zfs sudo"
+}
+zfs_unallow_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_unallow_004_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_unallow_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_unallow_005_pos cleanup
+zfs_unallow_005_pos_head()
+{
+ atf_set "descr" "Verify option '-c' will remove the created permission set."
+ atf_set "require.progs" "ksh93 zfs sudo"
+}
+zfs_unallow_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_unallow_005_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_unallow_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_unallow_006_pos cleanup
+zfs_unallow_006_pos_head()
+{
+ atf_set "descr" "Verify option '-u', '-g' and '-e' only removed the specified typepermissions set."
+ atf_set "require.progs" "ksh93 zfs sudo"
+}
+zfs_unallow_006_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_unallow_006_pos.ksh || atf_fail "Testcase failed"
+}
+zfs_unallow_006_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_unallow_007_neg cleanup
+zfs_unallow_007_neg_head()
+{
+ atf_set "descr" "zfs unallow won't remove those permissions which inherited fromits parent dataset."
+ atf_set "require.progs" "ksh93 zfs sudo"
+}
+zfs_unallow_007_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_unallow_007_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_unallow_007_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfs_unallow_008_neg cleanup
+zfs_unallow_008_neg_head()
+{
+ atf_set "descr" "zfs unallow can handle invalid arguments."
+ atf_set "require.progs" "ksh93 zfs sudo"
+}
+zfs_unallow_008_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfs_unallow_008_neg.ksh || atf_fail "Testcase failed"
+}
+zfs_unallow_008_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/delegate_common.kshlib
+ . $(atf_get_srcdir)/delegate.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zfs_unallow_001_pos
+ atf_add_test_case zfs_unallow_002_pos
+ atf_add_test_case zfs_unallow_003_pos
+ atf_add_test_case zfs_unallow_004_pos
+ atf_add_test_case zfs_unallow_005_pos
+ atf_add_test_case zfs_unallow_006_pos
+ atf_add_test_case zfs_unallow_007_neg
+ atf_add_test_case zfs_unallow_008_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/devices/Makefile b/tests/sys/cddl/zfs/tests/devices/Makefile
new file mode 100644
index 000000000000..6902f8c270be
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/devices/Makefile
@@ -0,0 +1,20 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/devices
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= devices_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= devices_common.kshlib
+${PACKAGE}FILES+= devices_002_neg.ksh
+${PACKAGE}FILES+= devices_test.sh
+${PACKAGE}FILES+= devices_003_pos.ksh
+${PACKAGE}FILES+= devices.cfg
+${PACKAGE}FILES+= devices_001_pos.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= setup.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/devices/cleanup.ksh b/tests/sys/cddl/zfs/tests/devices/cleanup.ksh
new file mode 100644
index 000000000000..713cb16bb31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/devices/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/devices/devices.cfg b/tests/sys/cddl/zfs/tests/devices/devices.cfg
new file mode 100644
index 000000000000..05c43a238de9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/devices/devices.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export TESTFILE1=testfile1${TESTCASE_ID}
+export TESTFILE2=testfile2${TESTCASE_ID}
diff --git a/tests/sys/cddl/zfs/tests/devices/devices_001_pos.ksh b/tests/sys/cddl/zfs/tests/devices/devices_001_pos.ksh
new file mode 100644
index 000000000000..58c7d96f6db6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/devices/devices_001_pos.ksh
@@ -0,0 +1,74 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/devices/devices_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: devices_001_pos
+#
+# DESCRIPTION:
+# When set property devices=on on file system, devices files can be used in
+# this file system.
+#
+# STRATEGY:
+# 1. Create pool and file system.
+# 2. Set devices=on on this file system.
+# 3. Separately create block device file and character file.
+# 4. Separately read from those two device files.
+# 5. Check the return value, and make sure it succeeds.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-11)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Setting devices=on on file system, the devices files in this file" \
+ "system can be used."
+log_onexit cleanup
+
+log_must $ZFS set devices=on $TESTPOOL/$TESTFS
+
+#
+# Separately create block device file and character device file, then try to
+# open them and make sure it succeed.
+#
+create_dev_file b $TESTDIR/$TESTFILE1
+log_must $DD if=$TESTDIR/$TESTFILE1 of=$TESTDIR/$TESTFILE1.out count=1
+create_dev_file c $TESTDIR/$TESTFILE2
+log_must $DD if=$TESTDIR/$TESTFILE2 of=$TESTDIR/$TESTFILE2.out count=1
+
+log_pass "Setting devices=on on file system and testing it pass."
diff --git a/tests/sys/cddl/zfs/tests/devices/devices_002_neg.ksh b/tests/sys/cddl/zfs/tests/devices/devices_002_neg.ksh
new file mode 100644
index 000000000000..f1969c438be8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/devices/devices_002_neg.ksh
@@ -0,0 +1,74 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/devices/devices_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: devices_002_neg
+#
+# DESCRIPTION:
+# When set property devices=off on file system, device files cannot be used
+# in this file system.
+#
+# STRATEGY:
+# 1. Create pool and file system.
+# 2. Set devices=off on this file system.
+# 3. Separately create block device file and character file.
+# 4. Separately read from those two device files.
+# 5. Check the return value, and make sure it failed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-11)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Setting devices=off on file system, the devices files in this file"\
+ "system can not be used."
+log_onexit cleanup
+
+log_must $ZFS set devices=off $TESTPOOL/$TESTFS
+
+#
+# Separately create block device file and character device file, then try to
+# open them and make sure it failed.
+#
+create_dev_file b $TESTDIR/$TESTFILE1
+log_mustnot $DD if=$TESTDIR/$TESTFILE1 of=$TESTDIR/$TESTFILE1.out count=1
+create_dev_file c $TESTDIR/$TESTFILE2
+log_mustnot $DD if=$TESTDIR/$TESTFILE2 of=$TESTDIR/$TESTFILE2.out count=1
+
+log_pass "Setting devices=off on file system and testing it pass."
diff --git a/tests/sys/cddl/zfs/tests/devices/devices_003_pos.ksh b/tests/sys/cddl/zfs/tests/devices/devices_003_pos.ksh
new file mode 100644
index 000000000000..a4447fc06ff6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/devices/devices_003_pos.ksh
@@ -0,0 +1,58 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: devices_003_pos
+#
+# DESCRIPTION:
+#
+# Writing random data into /dev/zfs should do no harm.
+#
+# STRATEGY:
+# 1. Write some random data into /dev/zfs
+# 2. Verify that this should fail.
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-04-24)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+log_assert "Writing random data into /dev/zfs should do no harm."
+
+log_mustnot $DD if=/dev/urandom of=/dev/zfs count=1024
+
+log_pass "Writing random data into /dev/zfs should do no harm."
diff --git a/tests/sys/cddl/zfs/tests/devices/devices_common.kshlib b/tests/sys/cddl/zfs/tests/devices/devices_common.kshlib
new file mode 100644
index 000000000000..49660f75a98d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/devices/devices_common.kshlib
@@ -0,0 +1,115 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#
+# Create block file or charactor file according to parameter.
+#
+# $1 device file type
+# $2 file name
+#
+function create_dev_file
+{
+ typeset filetype=$1
+ typeset filename=$2
+
+ case $filetype in
+ b)
+ devtype=$($DF -T / | $AWK '{print $2}')
+ case $devtype in
+ zfs)
+ rootpool=$($DF / | \
+ $AWK '{print $2}')
+ rootpool=${rootpool#\(}
+ rootpool=${rootpool%%/*}
+
+ devstr=$(get_disklist $rootpool)
+ devstr=$($ECHO "$devstr" | \
+ $AWK '{print $1}')
+ [[ -z $devstr ]] && \
+ log_fail "Can not get block device file."
+ devstr=/dev/${devstr}
+ ;;
+ ufs)
+ #
+ # Get the existing block device file in current system.
+ # And bring out the first one.
+ #
+ devstr=$($DF -lht ufs | \
+ $GREP "^/dev/" | \
+ $AWK '{print $1}')
+ devstr=$($ECHO "$devstr" | \
+ $AWK '{print $1}')
+ [[ -z $devstr ]] && \
+ log_fail "Can not get block device file."
+ ;;
+ *)
+ log_unsupported "Unsupported fstype " \
+ "for / ($devtype)," \
+ "only ufs|zfs is supported."
+ ;;
+ esac
+
+ #
+ # Get the device file information. i.e:
+ # /dev/c0t0d0s0: block special (28/768)
+ #
+ devstr=$($FILE $devstr)
+
+ #
+ # Bring out major and minor number.
+ #
+ major=${devstr##*\(}
+ major=${major%%/*}
+ minor=${devstr##*/}
+ minor=${minor%\)}
+
+ log_must $MKNOD $filename b $major $minor
+ ;;
+ c)
+ #
+ # Create device file '/dev/null'
+ #
+ log_must $MKNOD $filename c 13 2
+ ;;
+ *)
+ log_fail "'$filetype' is wrong."
+ ;;
+ esac
+
+ return 0
+}
+
+function cleanup
+{
+ log_must $ZFS set devices=on $TESTPOOL/$TESTFS
+ log_must $RM -f $TESTDIR/$TESTFILE1
+ log_must $RM -f $TESTDIR/$TESTFILE2
+ log_must $RM -f $TESTDIR/$TESTFILE1.out
+ log_must $RM -f $TESTDIR/$TESTFILE2.out
+}
+
diff --git a/tests/sys/cddl/zfs/tests/devices/devices_test.sh b/tests/sys/cddl/zfs/tests/devices/devices_test.sh
new file mode 100755
index 000000000000..fc7b424a49a1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/devices/devices_test.sh
@@ -0,0 +1,110 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case devices_001_pos cleanup
+devices_001_pos_head()
+{
+ atf_set "descr" "Setting devices=on on file system, the devices files in this filesystem can be used."
+ atf_set "require.progs" "ksh93 zfs"
+}
+devices_001_pos_body()
+{
+ atf_expect_fail "The devices property is not yet supported on FreeBSD"
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/devices_common.kshlib
+ . $(atf_get_srcdir)/devices.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/devices_001_pos.ksh || atf_fail "Testcase failed"
+}
+devices_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/devices_common.kshlib
+ . $(atf_get_srcdir)/devices.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case devices_002_neg cleanup
+devices_002_neg_head()
+{
+ atf_set "descr" "Setting devices=off on file system, the devices files in this filesystem can not be used."
+ atf_set "require.progs" "ksh93 zfs"
+}
+devices_002_neg_body()
+{
+ atf_expect_fail "The devices property is not yet supported on FreeBSD"
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/devices_common.kshlib
+ . $(atf_get_srcdir)/devices.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/devices_002_neg.ksh || atf_fail "Testcase failed"
+}
+devices_002_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/devices_common.kshlib
+ . $(atf_get_srcdir)/devices.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case devices_003_pos cleanup
+devices_003_pos_head()
+{
+ atf_set "descr" "Writing random data into /dev/zfs should do no harm."
+}
+devices_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/devices_common.kshlib
+ . $(atf_get_srcdir)/devices.cfg
+
+ ksh93 $(atf_get_srcdir)/devices_003_pos.ksh || atf_fail "Testcase failed"
+}
+devices_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/devices_common.kshlib
+ . $(atf_get_srcdir)/devices.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case devices_001_pos
+ atf_add_test_case devices_002_neg
+ atf_add_test_case devices_003_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/devices/setup.ksh b/tests/sys/cddl/zfs/tests/devices/setup.ksh
new file mode 100644
index 000000000000..2ab3c8a94139
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/devices/setup.ksh
@@ -0,0 +1,30 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/exec/Makefile b/tests/sys/cddl/zfs/tests/exec/Makefile
new file mode 100644
index 000000000000..2a8efb84b079
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/exec/Makefile
@@ -0,0 +1,19 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/exec
+FILESDIR=${TESTSDIR}
+BINDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= exec_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= exec_001_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= exec_002_neg.ksh
+PROG= mmap_exec
+MAN=
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/exec/cleanup.ksh b/tests/sys/cddl/zfs/tests/exec/cleanup.ksh
new file mode 100644
index 000000000000..713cb16bb31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/exec/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/exec/exec_001_pos.ksh b/tests/sys/cddl/zfs/tests/exec/exec_001_pos.ksh
new file mode 100644
index 000000000000..9d953bc07774
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/exec/exec_001_pos.ksh
@@ -0,0 +1,72 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: exec_001_pos
+#
+# DESCRIPTION:
+# When set property exec=on on a filesystem, processes can be executed from
+# this filesystem.
+#
+# STRATEGY:
+# 1. Create pool and file system.
+# 2. Copy '/bin/ls' to the ZFS file system.
+# 3. Setting exec=on on this file system.
+# 4. Make sure '/bin/ls' can work in this ZFS file system.
+# 5. Make sure mmap which is using the PROT_EXEC calls succeed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-11)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ log_must $RM $TESTDIR/myls
+}
+
+log_assert "Setting exec=on on a filesystem, processes can be executed from " \
+ "this file system."
+log_onexit cleanup
+
+log_must $CP $LS $TESTDIR/myls
+log_must $ZFS set exec=on $TESTPOOL/$TESTFS
+log_must $TESTDIR/myls
+log_must mmap_exec $TESTDIR/myls
+
+log_pass "Setting exec=on on filesystem testing passed."
diff --git a/tests/sys/cddl/zfs/tests/exec/exec_002_neg.ksh b/tests/sys/cddl/zfs/tests/exec/exec_002_neg.ksh
new file mode 100644
index 000000000000..d2f318ba73be
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/exec/exec_002_neg.ksh
@@ -0,0 +1,93 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: exec_002_neg
+#
+# DESCRIPTION:
+# When set property exec=off on a filesystem, processes can not be executed from
+# this filesystem.
+#
+# STRATEGY:
+# 1. Create pool and file system.
+# 2. Copy '/bin/ls' to the ZFS file system.
+# 3. Setting exec=off on this file system.
+# 4. Make sure '/bin/ls' can not work in this ZFS file system.
+# 5. Make sure mmap which is using the PROT_EXEC calls failed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-11)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ log_must $RM $TESTDIR/myls
+}
+
+#
+# Execute and check if the return value is equal to expected.
+#
+# $1 expected value
+# $2..$n executed item
+#
+function exec_n_check
+{
+ typeset expect_value=$1
+
+ shift
+ $@
+ ret=$?
+ if [[ $ret != $expect_value ]]; then
+ log_fail "Unexpected return code: '$ret'"
+ fi
+
+ return 0
+}
+
+log_assert "Setting exec=off on a filesystem, processes can not be executed " \
+ "from this file system."
+log_onexit cleanup
+
+log_must $CP $LS $TESTDIR/myls
+log_must $ZFS set exec=off $TESTPOOL/$TESTFS
+
+log_must exec_n_check 126 $TESTDIR/myls
+log_must exec_n_check 13 mmap_exec $TESTDIR/myls
+
+log_pass "Setting exec=off on filesystem testing passed."
diff --git a/tests/sys/cddl/zfs/tests/exec/exec_test.sh b/tests/sys/cddl/zfs/tests/exec/exec_test.sh
new file mode 100755
index 000000000000..b57f75b2102b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/exec/exec_test.sh
@@ -0,0 +1,78 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case exec_001_pos cleanup
+exec_001_pos_head()
+{
+ atf_set "descr" "Setting exec=on on a filesystem, processes can be executed fromthis file system."
+ atf_set "require.progs" "ksh93 zfs"
+}
+exec_001_pos_body()
+{
+ export PATH=$(atf_get_srcdir):$PATH
+ . $(atf_get_srcdir)/../../include/default.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/exec_001_pos.ksh || atf_fail "Testcase failed"
+}
+exec_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case exec_002_neg cleanup
+exec_002_neg_head()
+{
+ atf_set "descr" "Setting exec=off on a filesystem, processes can not be executedfrom this file system."
+ atf_set "require.progs" "ksh93 zfs"
+}
+exec_002_neg_body()
+{
+ PATH=$(atf_get_srcdir):$PATH
+ . $(atf_get_srcdir)/../../include/default.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/exec_002_neg.ksh || atf_fail "Testcase failed"
+}
+exec_002_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case exec_001_pos
+ atf_add_test_case exec_002_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/exec/mmap_exec.c b/tests/sys/cddl/zfs/tests/exec/mmap_exec.c
new file mode 100644
index 000000000000..f84419b6f56b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/exec/mmap_exec.c
@@ -0,0 +1,68 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <errno.h>
+
+extern int errno;
+
+int
+main(int argc, char *argv[])
+{
+ int fd;
+ struct stat statbuf;
+
+ if (argc != 2) {
+ (void) printf("Error: missing binary name.\n");
+ (void) printf("Usage:\n\t%s <binary name>\n",
+ argv[0]);
+ return (1);
+ }
+
+ errno = 0;
+
+ if ((fd = open(argv[1], O_RDONLY)) < 0) {
+ perror("open");
+ return (errno);
+ }
+ if (fstat(fd, &statbuf) < 0) {
+ perror("fstat");
+ return (errno);
+ }
+
+ if (mmap(0, statbuf.st_size,
+ PROT_EXEC, MAP_SHARED, fd, 0) == MAP_FAILED) {
+ perror("mmap");
+ return (errno);
+ }
+
+ return (0);
+}
diff --git a/tests/sys/cddl/zfs/tests/exec/setup.ksh b/tests/sys/cddl/zfs/tests/exec/setup.ksh
new file mode 100644
index 000000000000..2ab3c8a94139
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/exec/setup.ksh
@@ -0,0 +1,30 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/grow_pool/Makefile b/tests/sys/cddl/zfs/tests/grow_pool/Makefile
new file mode 100644
index 000000000000..31fdf1fc9b39
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/grow_pool/Makefile
@@ -0,0 +1,16 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/grow_pool
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= grow_pool_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= grow_pool_001_pos.ksh
+${PACKAGE}FILES+= grow_pool.cfg
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= setup.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/grow_pool/cleanup.ksh b/tests/sys/cddl/zfs/tests/grow_pool/cleanup.ksh
new file mode 100644
index 000000000000..94060a55bdc4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/grow_pool/cleanup.ksh
@@ -0,0 +1,41 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+ismounted $TESTFS && \
+ log_must $ZFS umount $TESTDIR
+destroy_pool "$TESTPOOL"
+
+if [[ -z $DISK ]]; then
+ cleanup_devices "$DISK0 $DISK1"
+else
+ cleanup_devices "$DISK"
+fi
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/grow_pool/grow_pool.cfg b/tests/sys/cddl/zfs/tests/grow_pool/grow_pool.cfg
new file mode 100644
index 000000000000..3559fc0ef7c8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/grow_pool/grow_pool.cfg
@@ -0,0 +1,41 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+export DISK_ARRAY_LIMIT=2
+export BLOCK_SIZE=8192
+set_disks
+#
+# Do not make SIZE too large as the three slices may exceed
+# the size of the disk, and also slow down the test
+# which involves filling until ENOSPC
+#
+export SIZE="100mb"
+export PARTITION=1
+export SMALL_WRITE_COUNT=100
+export TESTFILE1=file${TESTCASE_ID}.1
+export WRITE_COUNT=65536000
diff --git a/tests/sys/cddl/zfs/tests/grow_pool/grow_pool_001_pos.ksh b/tests/sys/cddl/zfs/tests/grow_pool/grow_pool_001_pos.ksh
new file mode 100644
index 000000000000..3ceeb674c9f9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/grow_pool/grow_pool_001_pos.ksh
@@ -0,0 +1,82 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: grow_pool_001_pos
+#
+# DESCRIPTION:
+# A ZFS file system is limited by the amount of disk space
+# available to the pool. Growing the pool by adding a disk
+# increases the amount of space.
+#
+# STRATEGY:
+# 1) Fill a ZFS filesystem until ENOSPC by creating a large file
+# 2) Grow the pool by adding a disk
+# 3) Verify that more data can now be written to the file system
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "A zpool may be increased in capacity by adding a disk"
+
+log_must $ZFS set compression=off $TESTPOOL/$TESTFS
+$FILE_WRITE -o create -f $TESTDIR/$TESTFILE1 \
+ -b $BLOCK_SIZE -c $WRITE_COUNT -d 0
+typeset -i zret=$?
+readonly ENOSPC=28
+if [[ $zret -ne $ENOSPC ]]; then
+ log_fail "file_write completed w/o ENOSPC, aborting!!!"
+fi
+
+if [[ ! -s $TESTDIR/$TESTFILE1 ]]; then
+ log_fail "$TESTDIR/$TESTFILE1 was not created"
+fi
+
+if [[ -n $DISK ]]; then
+ log_must $ZPOOL add $TESTPOOL ${DISK}p2
+else
+ log_must $ZPOOL add $TESTPOOL $DISK1
+fi
+
+log_must $FILE_WRITE -o append -f $TESTDIR/$TESTFILE1 \
+ -b $BLOCK_SIZE -c $SMALL_WRITE_COUNT -d 0
+
+log_must $ZFS inherit compression $TESTPOOL/$TESTFS
+log_pass "TESTPOOL successfully grown"
diff --git a/tests/sys/cddl/zfs/tests/grow_pool/grow_pool_test.sh b/tests/sys/cddl/zfs/tests/grow_pool/grow_pool_test.sh
new file mode 100755
index 000000000000..9ede22c8a5f8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/grow_pool/grow_pool_test.sh
@@ -0,0 +1,55 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case grow_pool_001_pos cleanup
+grow_pool_001_pos_head()
+{
+ atf_set "descr" "A zpool may be increased in capacity by adding a disk"
+ atf_set "require.progs" "ksh93 zfs zpool"
+}
+grow_pool_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/grow_pool.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/grow_pool_001_pos.ksh || atf_fail "Testcase failed"
+}
+grow_pool_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/grow_pool.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case grow_pool_001_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/grow_pool/setup.ksh b/tests/sys/cddl/zfs/tests/grow_pool/setup.ksh
new file mode 100644
index 000000000000..72d14a9ade6e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/grow_pool/setup.ksh
@@ -0,0 +1,42 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+if [[ -n $DISK ]]; then
+ log_note "No spare disks available. Using slices on $DISK"
+ log_must partition_disk $SIZE $DISK 2
+ tmp=${DISK}p1
+else
+ wipe_partition_table $DISK0 $DISK1
+ log_must set_partition $PARTITION "" $SIZE $DISK0
+ log_must set_partition $PARTITION "" $SIZE $DISK1
+ tmp=$DISK0"p"$PARTITION
+fi
+
+default_setup $tmp
diff --git a/tests/sys/cddl/zfs/tests/grow_replicas/Makefile b/tests/sys/cddl/zfs/tests/grow_replicas/Makefile
new file mode 100644
index 000000000000..06ee0c68c435
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/grow_replicas/Makefile
@@ -0,0 +1,16 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/grow_replicas
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= grow_replicas_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= grow_replicas.cfg
+${PACKAGE}FILES+= grow_replicas_001_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/grow_replicas/cleanup.ksh b/tests/sys/cddl/zfs/tests/grow_replicas/cleanup.ksh
new file mode 100644
index 000000000000..9194f7da97f7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/grow_replicas/cleanup.ksh
@@ -0,0 +1,37 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+ismounted $TESTFS && \
+ log_must $ZFS umount $TESTDIR
+destroy_pool "$TESTPOOL"
+
+cleanup_devices $DISKS
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/grow_replicas/grow_replicas.cfg b/tests/sys/cddl/zfs/tests/grow_replicas/grow_replicas.cfg
new file mode 100644
index 000000000000..edda5f25f5c2
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/grow_replicas/grow_replicas.cfg
@@ -0,0 +1,69 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+function set_disks
+{
+ set -A disk_array $(find_disks $DISKS)
+ typeset -i i=0
+ typeset -i limit=4
+ while (( i < limit )); do
+ if [[ -n ${disk_array[$i]} ]]; then
+ export DISK${i}="${disk_array[$i]}"
+ else
+ export DISK=${DISKS%% *}
+ return
+ fi
+ ((i = i + 1))
+ done
+ export DISK=""
+}
+
+typeset -i index=`expr $RANDOM % 2`
+if (( $index == 0 )); then
+ POOLTYPE="mirror"
+else
+ POOLTYPE="raidz"
+fi
+export POOLTYPE
+
+export BLOCK_SIZE=8192
+set_disks
+#
+# Do not make SIZE too large as the three slices may exceed
+# the size of the disk, and also slow down the test
+# which involves filling until ENOSPC
+#
+export SIZE="100mb"
+if [[ $WRAPPER != *"smi"* ]]; then
+ export PARTITION=1
+else
+ export PARTITION=2
+fi
+export SMALL_WRITE_COUNT=100
+export TESTFILE1=file${TESTCASE_ID}.1
+export WRITE_COUNT=65536000
diff --git a/tests/sys/cddl/zfs/tests/grow_replicas/grow_replicas_001_pos.ksh b/tests/sys/cddl/zfs/tests/grow_replicas/grow_replicas_001_pos.ksh
new file mode 100644
index 000000000000..7550c278811c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/grow_replicas/grow_replicas_001_pos.ksh
@@ -0,0 +1,91 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: grow_replicas_001_pos
+#
+# DESCRIPTION:
+# A ZFS file system is limited by the amount of disk space
+# available to the pool. Growing the pool by adding a disk
+# increases the amount of space.
+#
+# STRATEGY:
+# 1) Fill a ZFS filesystem mirror/raidz until ENOSPC by creating lots
+# of files
+# 2) Grow the mirror/raidz by adding a disk
+# 3) Verify that more data can now be written to the file system
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-10-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "A zpool mirror/raidz may be increased in capacity by adding a disk."
+
+log_must $ZFS set compression=off $TESTPOOL/$TESTFS
+$FILE_WRITE -o create -f $TESTDIR/$TESTFILE1 \
+ -b $BLOCK_SIZE -c $WRITE_COUNT -d 0
+
+typeset -i zret=$?
+readonly ENOSPC=28
+if [[ $zret -ne $ENOSPC ]]; then
+ log_fail "file_write completed w/o ENOSPC, aborting!!!"
+fi
+
+if [[ ! -s $TESTDIR/$TESTFILE1 ]]; then
+ log_fail "$TESTDIR/$TESTFILE1 was not created"
+fi
+
+#
+# $DISK will be set if we're using slices on one disk
+#
+if [[ -n $DISK ]]; then
+ log_must $ZPOOL add $TESTPOOL $POOLTYPE ${DISK}p3 \
+ ${DISK}p4
+else
+ [[ -z $DISK2 || -z $DISK3 ]] && \
+ log_unsupported "No spare disks available."
+ log_must $ZPOOL add -f $TESTPOOL $POOLTYPE ${DISK2}p${PARTITION} \
+ ${DISK3}p${PARTITION}
+fi
+
+log_must $FILE_WRITE -o append -f $TESTDIR/$TESTFILE1 \
+ -b $BLOCK_SIZE -c $SMALL_WRITE_COUNT -d 0
+
+log_must $ZFS inherit compression $TESTPOOL/$TESTFS
+log_pass "TESTPOOL mirror/raidz successfully grown"
diff --git a/tests/sys/cddl/zfs/tests/grow_replicas/grow_replicas_test.sh b/tests/sys/cddl/zfs/tests/grow_replicas/grow_replicas_test.sh
new file mode 100755
index 000000000000..00a2d25b85d1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/grow_replicas/grow_replicas_test.sh
@@ -0,0 +1,56 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case grow_replicas_001_pos cleanup
+grow_replicas_001_pos_head()
+{
+ atf_set "descr" "A zpool mirror/raidz may be increased in capacity by adding a disk."
+ atf_set "require.progs" "ksh93 zfs zpool"
+}
+grow_replicas_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/grow_replicas.cfg
+
+
+ verify_disk_count "$DISKS" 4
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/grow_replicas_001_pos.ksh || atf_fail "Testcase failed"
+}
+grow_replicas_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/grow_replicas.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case grow_replicas_001_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/grow_replicas/setup.ksh b/tests/sys/cddl/zfs/tests/grow_replicas/setup.ksh
new file mode 100644
index 000000000000..7af1d0bb03c5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/grow_replicas/setup.ksh
@@ -0,0 +1,54 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+log_note "Creating pool type: $POOLTYPE"
+
+if [[ -n $DISK ]]; then
+ log_note "No spare disks available. Using slices on $DISK"
+ partition_disk $SIZE $DISK 4
+ create_pool $TESTPOOL $POOLTYPE ${DISK}p1 \
+ ${DISK}p2
+else
+ wipe_partition_table $DISK0 $DISK1 $DISK2 $DISK3
+ log_must set_partition $PARTITION "" $SIZE $DISK0
+ log_must set_partition $PARTITION "" $SIZE $DISK1
+ log_must set_partition $PARTITION "" $SIZE $DISK2
+ log_must set_partition $PARTITION "" $SIZE $DISK3
+ create_pool $TESTPOOL $POOLTYPE ${DISK0}p${PARTITION} \
+ ${DISK1}p${PARTITION}
+fi
+
+$RM -rf $TESTDIR || log_unresolved Could not remove $TESTDIR
+$MKDIR -p $TESTDIR || log_unresolved Could not create $TESTDIR
+
+log_must $ZFS create $TESTPOOL/$TESTFS
+log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/history/Makefile b/tests/sys/cddl/zfs/tests/history/Makefile
new file mode 100644
index 000000000000..8a8175abbaea
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/history/Makefile
@@ -0,0 +1,31 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/history
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= history_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= history_005_neg.ksh
+${PACKAGE}FILES+= history_004_pos.ksh
+${PACKAGE}FILES+= history_008_pos.ksh
+${PACKAGE}FILES+= i386.orig_history.txt
+${PACKAGE}FILES+= sparc.orig_history.txt
+${PACKAGE}FILES+= history_common.kshlib
+${PACKAGE}FILES+= history.cfg
+${PACKAGE}FILES+= sparc.migratedpool.DAT.Z
+${PACKAGE}FILES+= history_010_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zfs-pool-v4.dat.Z
+${PACKAGE}FILES+= history_009_pos.ksh
+${PACKAGE}FILES+= history_001_pos.ksh
+${PACKAGE}FILES+= history_006_neg.ksh
+${PACKAGE}FILES+= i386.migratedpool.DAT.Z
+${PACKAGE}FILES+= history_003_pos.ksh
+${PACKAGE}FILES+= history_007_pos.ksh
+${PACKAGE}FILES+= history_002_pos.ksh
+${PACKAGE}FILES+= cleanup.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/history/cleanup.ksh b/tests/sys/cddl/zfs/tests/history/cleanup.ksh
new file mode 100644
index 000000000000..85381177afbb
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/history/cleanup.ksh
@@ -0,0 +1,32 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+$ZPOOL history > /dev/null 2>&1
+(($? != 0)) && log_unsupported
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/history/history.cfg b/tests/sys/cddl/zfs/tests/history/history.cfg
new file mode 100644
index 000000000000..83c5d819c42a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/history/history.cfg
@@ -0,0 +1,43 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export ZFSROOT=
+
+export MPOOL=mpool.${TESTCASE_ID}
+
+export EXPECT_HISTORY=$TMPDIR/exp_history.${TESTCASE_ID}
+export REAL_HISTORY=$TMPDIR/real_history.${TESTCASE_ID}
+export ADD_HISTORY=$TMPDIR/add_history.${TESTCASE_ID}
+
+export MIGRATEDPOOLNAME=${MIGRATEDPOOLNAME:-history_pool}
+export TIMEZONE=${TIMEZONE:-US/Mountain}
+
+export STF_TIMEOUT=1800
+
+export HIST_USER="huser"
+export HIST_GROUP="hgroup"
+
+export TESTVOL=testvol.${TESTCASE_ID}
diff --git a/tests/sys/cddl/zfs/tests/history/history_001_pos.ksh b/tests/sys/cddl/zfs/tests/history/history_001_pos.ksh
new file mode 100644
index 000000000000..71945d65a364
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/history/history_001_pos.ksh
@@ -0,0 +1,116 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/history/history_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: history_001_pos
+#
+# DESCRIPTION:
+# Create a scenario to verify the following zpool subcommands are logged.
+# create, destroy, add, remove, offline, online, attach, detach, replace,
+# scrub, export, import, clear, upgrade.
+#
+# STRATEGY:
+# 1. Create three virtual disk files.
+# 2. Create a three-way mirror.
+# 3. Invoke every sub-commands to this mirror, except upgrade.
+# 4. Compare 'zpool history' log with expected log.
+# 5. Imported specified pool and upgrade it, verify 'upgrade' was logged.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-07-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ destroy_pool $MPOOL
+ destroy_pool $upgrade_pool
+
+ [[ -d $import_dir ]] && $RM -rf $import_dir
+ for file in $REAL_HISTORY $EXPECT_HISTORY \
+ $VDEV1 $VDEV2 $VDEV3 $VDEV4
+ do
+ [[ -f $file ]] && $RM -f $file
+ done
+}
+
+log_assert "Verify zpool sub-commands which modify state are logged."
+log_onexit cleanup
+
+(( $? != 0)) && log_fail "get_prop($TESTPOOL mountpoint)"
+VDEV1=$TMPDIR/vdev1; VDEV2=$TMPDIR/vdev2;
+VDEV3=$TMPDIR/vdev3; VDEV4=$TMPDIR/vdev4;
+
+log_must create_vdevs $VDEV1 $VDEV2 $VDEV3 $VDEV4
+$CAT /dev/null > $EXPECT_HISTORY
+
+exec_record $ZPOOL create $MPOOL mirror $VDEV1 $VDEV2
+exec_record $ZPOOL add -f $MPOOL spare $VDEV3
+exec_record $ZPOOL remove $MPOOL $VDEV3
+exec_record $ZPOOL offline $MPOOL $VDEV1
+exec_record $ZPOOL online $MPOOL $VDEV1
+exec_record $ZPOOL attach $MPOOL $VDEV1 $VDEV4
+exec_record $ZPOOL detach $MPOOL $VDEV4
+exec_record $ZPOOL replace -f $MPOOL $VDEV1 $VDEV4
+exec_record $ZPOOL export $MPOOL
+exec_record $ZPOOL import -d $TMPDIR $MPOOL
+exec_record $ZPOOL destroy $MPOOL
+exec_record $ZPOOL import -D -f -d $TMPDIR $MPOOL
+exec_record $ZPOOL clear $MPOOL
+
+format_history $MPOOL $REAL_HISTORY
+log_must $DIFF $REAL_HISTORY $EXPECT_HISTORY
+
+import_dir=$TMPDIR/import_dir.${TESTCASE_ID}
+log_must $MKDIR $import_dir
+log_must $CP $STF_SUITE/tests/history/zfs-pool-v4.dat.Z $import_dir
+log_must $UNCOMPRESS $import_dir/zfs-pool-v4.dat.Z
+
+# Truncate $EXPECT_HISTORY file
+log_must eval "$CAT /dev/null > $EXPECT_HISTORY"
+
+upgrade_pool=$($ZPOOL import -d $import_dir | $GREP "pool:" | $AWK '{print $2}')
+exec_record $ZPOOL import -d $import_dir $upgrade_pool
+# Get existing history
+format_history $upgrade_pool $EXPECT_HISTORY
+exec_record $ZPOOL upgrade $upgrade_pool
+
+format_history $upgrade_pool $REAL_HISTORY
+log_must $DIFF $REAL_HISTORY $EXPECT_HISTORY
+
+log_pass "zpool sub-commands which modify state are logged passed. "
diff --git a/tests/sys/cddl/zfs/tests/history/history_002_pos.ksh b/tests/sys/cddl/zfs/tests/history/history_002_pos.ksh
new file mode 100644
index 000000000000..aff892cb2565
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/history/history_002_pos.ksh
@@ -0,0 +1,172 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/history/history_common.kshlib
+. $STF_SUITE/tests/cli_root/zfs_rollback/zfs_rollback_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: history_002_pos
+#
+# DESCRIPTION:
+# Create a scenario to verify the following zfs subcommands are logged.
+# create, destroy, clone, rename, snapshot, rollback,
+# set, inherit, receive, promote.
+#
+# STRATEGY:
+# 1. Format zpool history to file $EXPECT_HISTORY.
+# 2. Invoke every sub-commands to this mirror.
+# 3. Compare 'zpool history' log with expected log.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-07-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ for FileToRm in $EXPECT_HISTORY $REAL_HISTORY $tmpfile $tmpfile2; do
+ [[ -f $FileToRm ]] && log_must $RM -f $FileToRm
+ done
+ for dataset in $fs $newfs $fsclone $vol $newvol $volclone; do
+ datasetexists $dataset && $ZFS destroy -Rf $dataset
+ done
+ log_must $RM -rf /history.${TESTCASE_ID}
+}
+
+log_assert "Verify zfs sub-commands which modify state are logged."
+log_onexit cleanup
+
+format_history $TESTPOOL $EXPECT_HISTORY
+
+fs=$TESTPOOL/$TESTFS1; newfs=$TESTPOOL/newfs; fsclone=$TESTPOOL/clone
+vol=$TESTPOOL/$TESTVOL ; newvol=$TESTPOOL/newvol; volclone=$TESTPOOL/volclone
+fssnap=$fs@fssnap; fssnap2=$fs@fssnap2
+volsnap=$vol@volsnap; volsnap2=$vol@volsnap2
+
+# property value property value
+#
+set -A props \
+ quota 64M recordsize 512 \
+ reservation 32M reservation none \
+ mountpoint /history.${TESTCASE_ID} mountpoint legacy \
+ mountpoint none sharenfs on \
+ sharenfs off \
+ compression on compression off \
+ compression lzjb aclmode discard \
+ aclmode groupmask aclmode passthrough \
+ atime on atime off \
+ exec on exec off \
+ setuid on setuid off \
+ readonly on readonly off \
+ snapdir hidden snapdir visible \
+ aclinherit discard aclinherit noallow \
+ aclinherit secure aclinherit passthrough \
+ canmount off canmount on \
+ compression gzip compression gzip-$((RANDOM%9 + 1)) \
+ copies $((RANDOM%3 +1))
+
+# Add a few extra properties not supported on FreeBSD, if applicable. The
+# currently unsupported list is in the source in libzfs_dataset.c.
+if [[ $os_name != "FreeBSD" ]]; then
+ set +A props \
+ devices on devices off \
+ zoned on zoned off \
+ shareiscsi on shareiscsi off \
+ xattr on xattr off
+fi
+
+tmpfile=$TMPDIR/tmpfile.${TESTCASE_ID} ; tmpfile2=$TMPDIR/tmpfile2.${TESTCASE_ID}
+
+exec_record $ZFS create $fs
+
+typeset enc=""
+enc=$(get_prop encryption $fs)
+if [[ $? -ne 0 ]] || [[ -z "$enc" ]] || [[ "$enc" == "off" ]]; then
+ typeset -i n=${#props[@]}
+
+ props[$n]=checksum ; props[((n+1))]="on"
+ props[((n+2))]=checksum ; props[((n+3))]="off"
+ props[((n+4))]=checksum ; props[((n+5))]="fletcher2"
+ props[((n+6))]=checksum ; props[((n+7))]="fletcher4"
+ props[((n+8))]=checksum ; props[((n+9))]="sha256"
+fi
+
+# Set all the property for filesystem
+typeset -i i=0
+while ((i < ${#props[@]})) ; do
+ exec_record $ZFS set ${props[$i]}=${props[((i+1))]} $fs
+
+ # quota, reservation, canmount can not be inherited.
+ #
+ if [[ ${props[$i]} != "quota" && \
+ ${props[$i]} != "reservation" && \
+ ${props[$i]} != "canmount" ]];
+ then
+ exec_record $ZFS inherit ${props[$i]} $fs
+ fi
+
+ ((i += 2))
+done
+exec_record $ZFS create -V 64M $vol
+exec_record $ZFS set volsize=32M $vol
+exec_record $ZFS snapshot $fssnap
+exec_record $ZFS snapshot $volsnap
+exec_record $ZFS snapshot $fssnap2
+exec_record $ZFS snapshot $volsnap2
+log_must eval "$ZFS send -i $fssnap $fssnap2 > $tmpfile"
+log_must eval "$ZFS send -i $volsnap $volsnap2 > $tmpfile2"
+exec_record $ZFS destroy $fssnap2
+exec_record $ZFS destroy $volsnap2
+exec_record eval "$ZFS receive $fs < $tmpfile"
+exec_record eval "$ZFS receive $vol < $tmpfile2"
+exec_record $ZFS rollback -r $fssnap
+exec_record $ZFS rollback -r $volsnap
+exec_record $ZFS clone $fssnap $fsclone
+exec_record $ZFS clone $volsnap $volclone
+exec_record $ZFS rename $fs $newfs
+exec_record $ZFS rename $vol $newvol
+exec_record $ZFS promote $fsclone
+exec_record $ZFS promote $volclone
+exec_record $ZFS destroy $newfs
+exec_record $ZFS destroy $newvol
+exec_record $ZFS destroy -rf $fsclone
+exec_record $ZFS destroy -rf $volclone
+
+format_history $TESTPOOL $REAL_HISTORY
+
+log_must $DIFF $REAL_HISTORY $EXPECT_HISTORY
+
+log_pass "zfs sub-commands which modify state are logged passed."
diff --git a/tests/sys/cddl/zfs/tests/history/history_003_pos.ksh b/tests/sys/cddl/zfs/tests/history/history_003_pos.ksh
new file mode 100644
index 000000000000..f06d7b3bc8a5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/history/history_003_pos.ksh
@@ -0,0 +1,84 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/history/history_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: history_003_pos
+#
+# DESCRIPTION:
+# zpool history can record and output huge log.
+#
+# STRATEGY:
+# 1. Create two 100M virtual disk files.
+# 2. Create test pool using the two virtual files.
+# 3. Loop N times to set compression to test pool.
+# 4. Make sure 'zpool history' output correctly.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-07-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "zpool history limitation test."
+
+typeset -i expect_count=300
+typeset -i orig_count=$($ZPOOL history $TESTPOOL | $WC -l | $AWK '{print $1}')
+
+typeset -i i=0
+typeset -i num_iters=0
+((num_iters = expect_count / 5))
+while ((i < num_iters)); do
+ $ZFS set compression=off $TESTPOOL/$TESTFS
+ $ZFS set compression=on $TESTPOOL/$TESTFS
+ $ZFS set compression=off $TESTPOOL/$TESTFS
+ $ZFS set compression=on $TESTPOOL/$TESTFS
+ $ZFS set compression=off $TESTPOOL/$TESTFS
+
+ ((i += 1))
+done
+
+typeset -i entry_count=$($ZPOOL history $TESTPOOL | $WC -l | $AWK '{print $1}')
+
+typeset -i count_diff=0
+((count_diff = entry_count - orig_count))
+if ((count_diff != expect_count)); then
+ echo "Zpool history is as follows:"
+ log_must $ZPOOL history $TESTPOOL
+ log_fail "Expected $expect_count new entries, got $count_diff"
+fi
+
+log_pass "zpool history limitation test passed."
diff --git a/tests/sys/cddl/zfs/tests/history/history_004_pos.ksh b/tests/sys/cddl/zfs/tests/history/history_004_pos.ksh
new file mode 100644
index 000000000000..b85a7aeb5d94
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/history/history_004_pos.ksh
@@ -0,0 +1,109 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/history/history_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: history_004_pos
+#
+# DESCRIPTION:
+# 'zpool history' can copes with many simultaneous command.
+#
+# STRATEGY:
+# 1. Create test pool and test fs.
+# 2. Loop 100 times, set properties to test fs simultaneously.
+# 3. Wait for all the command execution complete.
+# 4. Make sure all the commands was logged by 'zpool history'.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-07-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "'zpool history' can copes with many simultaneous command."
+
+typeset -i orig_count=$($ZPOOL history $TESTPOOL | $WC -l | $AWK '{print $1}')
+
+typeset -i i=0
+while ((i < 10)); do
+ $ZFS set compression=off $TESTPOOL/$TESTFS &
+ $ZFS set atime=off $TESTPOOL/$TESTFS &
+ $ZFS create $TESTPOOL/$TESTFS1 &
+ $ZFS create $TESTPOOL/$TESTFS2 &
+ $ZFS create $TESTPOOL/$TESTFS3 &
+
+ wait
+
+ $ZFS snapshot $TESTPOOL/$TESTFS1@snap &
+ $ZFS snapshot $TESTPOOL/$TESTFS2@snap &
+ $ZFS snapshot $TESTPOOL/$TESTFS3@snap &
+
+ wait
+
+ $ZFS clone $TESTPOOL/$TESTFS1@snap $TESTPOOL/clone1 &
+ $ZFS clone $TESTPOOL/$TESTFS2@snap $TESTPOOL/clone2 &
+ $ZFS clone $TESTPOOL/$TESTFS3@snap $TESTPOOL/clone3 &
+
+ wait
+
+ $ZFS promote $TESTPOOL/clone1 &
+ $ZFS promote $TESTPOOL/clone2 &
+ $ZFS promote $TESTPOOL/clone3 &
+
+ wait
+
+ $ZFS destroy $TESTPOOL/$TESTFS1 &
+ $ZFS destroy $TESTPOOL/$TESTFS2 &
+ $ZFS destroy $TESTPOOL/$TESTFS3 &
+
+ wait
+
+ $ZFS destroy -Rf $TESTPOOL/clone1 &
+ $ZFS destroy -Rf $TESTPOOL/clone2 &
+ $ZFS destroy -Rf $TESTPOOL/clone3 &
+
+ wait
+ ((i += 1))
+done
+
+typeset -i count=$($ZPOOL history $TESTPOOL | $WC -l | $AWK '{print $1}')
+
+if ((count - orig_count != 200)); then
+ $ZPOOL history $spool
+ log_fail "Expected 200 more than $orig_count entries, but got $count"
+fi
+
+log_pass "zpool history copes with simultaneous commands passed."
diff --git a/tests/sys/cddl/zfs/tests/history/history_005_neg.ksh b/tests/sys/cddl/zfs/tests/history/history_005_neg.ksh
new file mode 100644
index 000000000000..a0dfd10fcec7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/history/history_005_neg.ksh
@@ -0,0 +1,77 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/history/history_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: history_005_neg
+#
+# DESCRIPTION:
+# Verify the following zpool subcommands are not logged.
+# zpool list
+# zpool status
+# zpool iostat
+#
+# STRATEGY:
+# 1. Create a test pool.
+# 2. Separately invoke zpool list|status|iostat
+# 3. Verify they was not recored in pool history.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-07-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ [[ -f $EXPECT_HISTORY ]] && $RM -f $EXPECT_HISTORY
+ [[ -f $REAL_HISTORY ]] && $RM -f $REAL_HISTORY
+}
+
+log_assert "Verify 'zpool list|status|iostat' will not be logged."
+log_onexit cleanup
+
+# Save initial TESTPOOL history
+log_must eval "$ZPOOL history $TESTPOOL > $EXPECT_HISTORY"
+
+log_must $ZPOOL list $TESTPOOL > /dev/null
+log_must $ZPOOL status $TESTPOOL > /dev/null
+log_must $ZPOOL iostat $TESTPOOL > /dev/null
+
+log_must eval "$ZPOOL history $TESTPOOL > $REAL_HISTORY"
+log_must $DIFF $EXPECT_HISTORY $REAL_HISTORY
+
+log_pass "Verify 'zpool list|status|iostat' passed."
diff --git a/tests/sys/cddl/zfs/tests/history/history_006_neg.ksh b/tests/sys/cddl/zfs/tests/history/history_006_neg.ksh
new file mode 100644
index 000000000000..8b5f6ee2a641
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/history/history_006_neg.ksh
@@ -0,0 +1,90 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/history/history_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: history_006_neg
+#
+# DESCRIPTION:
+# Verify the following zfs subcommands are not logged.
+# list, get, mount, unmount, share, unshare, send
+#
+# STRATEGY:
+# 1. Create a test pool.
+# 2. Separately invoke zfs list|get|mount|unmount|share|unshare|send
+# 3. Verify they was not recored in pool history.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-07-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ [[ -f $EXPECT_HISTORY ]] && $RM -f $EXPECT_HISTORY
+ [[ -f $REAL_HISTORY ]] && $RM -f $REAL_HISTORY
+ if datasetexists $fs ; then
+ log_must $ZFS destroy -rf $fs
+ fi
+ log_must $ZFS create $fs
+}
+
+log_assert "Verify 'zfs list|get|mount|unmount|share|unshare|send' will not " \
+ "be logged."
+log_onexit cleanup
+
+# Create initial test environment
+fs=$TESTPOOL/$TESTFS; snap1=$fs@snap1; snap2=$fs@snap2
+log_must $ZFS set sharenfs=on $fs
+log_must $ZFS snapshot $snap1
+log_must $ZFS snapshot $snap2
+
+# Save initial TESTPOOL history
+log_must eval "$ZPOOL history $TESTPOOL > $EXPECT_HISTORY"
+
+log_must $ZFS list $fs > /dev/null
+log_must $ZFS get mountpoint $fs > /dev/null
+log_must $ZFS unmount $fs
+log_must $ZFS mount $fs
+log_must $ZFS share $fs
+log_must $ZFS unshare $fs
+log_must $ZFS send -i $snap1 $snap2 > /dev/null
+
+log_must eval "$ZPOOL history $TESTPOOL > $REAL_HISTORY"
+log_must $DIFF $EXPECT_HISTORY $REAL_HISTORY
+
+log_pass "Verify 'zfs list|get|mount|unmount|share|unshare|send' passed."
diff --git a/tests/sys/cddl/zfs/tests/history/history_007_pos.ksh b/tests/sys/cddl/zfs/tests/history/history_007_pos.ksh
new file mode 100644
index 000000000000..df81673c4e7e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/history/history_007_pos.ksh
@@ -0,0 +1,124 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/history/history_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: history_007_pos
+#
+# DESCRIPTION:
+# Verify command history moves with pool while pool being migrated
+#
+# STRATEGY:
+# 1. Import uniform platform and cross platform pools
+# 2. Contract the command history of the imported pool
+# 3. Compare imported history log with the previous log.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-10-25)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $migratedpoolname && \
+ log_must $ZPOOL destroy -f $migratedpoolname
+
+ [[ -d $import_dir ]] && $RM -rf $import_dir
+}
+
+log_assert "Verify command history moves with pool while migrating."
+log_onexit cleanup
+
+# The *.orig_history.txt files were made using the America/Denver timezone,
+# and since "zpool history" outputs timestamps in localtime, the test must
+# be run in that same timezone
+export TZ="America/Denver"
+
+tst_dir=$STF_SUITE/tests/history
+import_dir=$TESTDIR/importdir.${TESTCASE_ID}
+migrated_cmds_f=$import_dir/migrated_history.${TESTCASE_ID}
+migratedpoolname=$MIGRATEDPOOLNAME
+typeset -i RET=1
+typeset -i linenum=0
+
+[[ ! -d $import_dir ]] && log_must $MKDIR $import_dir
+
+# We test the migrations on both uniform platform and cross platform
+for arch in "i386" "sparc"; do
+ log_must $CP $tst_dir/${arch}.orig_history.txt $import_dir
+ orig_cmds_f=$import_dir/${arch}.orig_history.txt
+ #remove blank line
+ orig_cmds_f1=$import_dir/${arch}.orig_history_1.txt
+ $CAT $orig_cmds_f | $GREP -v "^$" > $orig_cmds_f1
+
+ log_must $CP $tst_dir/${arch}.migratedpool.DAT.Z $import_dir
+ log_must $UNCOMPRESS $import_dir/${arch}.migratedpool.DAT.Z
+
+ #destroy the pool with same name, so that import operation could succeed.
+ poolexists $migratedpoolname && \
+ log_must $ZPOOL destroy -f $migratedpoolname
+
+ log_must $ZPOOL import -d $import_dir $migratedpoolname
+ $ZPOOL history $migratedpoolname | $GREP -v "^$" >$migrated_cmds_f
+ RET=$?
+ (( $RET != 0 )) && log_fail "$ZPOOL histroy $migratedpoolname fails."
+
+ # The migrated history file should differ with original history file on
+ # two commands -- 'export' and 'import', which are included in migrated
+ # history file but not in original history file. so, check the two commands
+ # firstly in migrated history file and then delete them, and then compare
+ # this filtered file with the original history file. They should be identical
+ # at this time.
+ for subcmd in "export" "import"; do
+ $GREP "$subcmd" $migrated_cmds_f >/dev/null 2>&1
+ RET=$?
+ (( $RET != 0 )) && log_fail "zpool $subcmd is not logged for" \
+ "the imported pool $migratedpoolname."
+ done
+
+ tmpfile=$import_dir/cmds_tmp.${TESTCASE_ID}
+ linenum=`$CAT $migrated_cmds_f | $WC -l`
+ (( linenum = linenum - 2 ))
+ $HEAD -n $linenum $migrated_cmds_f > $tmpfile
+ log_must $DIFF $tmpfile $orig_cmds_f1
+
+ #cleanup for next loop testing
+ log_must $ZPOOL destroy -f $migratedpoolname
+ log_must $RM -f `$LS $import_dir`
+done
+
+log_pass "Command history moves with pool as expected while pool being migrated. "
diff --git a/tests/sys/cddl/zfs/tests/history/history_008_pos.ksh b/tests/sys/cddl/zfs/tests/history/history_008_pos.ksh
new file mode 100644
index 000000000000..4f944c5d6aea
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/history/history_008_pos.ksh
@@ -0,0 +1,142 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/history/history_common.kshlib
+. $STF_SUITE/tests/cli_root/zfs_rollback/zfs_rollback_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: history_008_pos
+#
+# DESCRIPTION:
+# Internal journal records all the recursively operations.
+#
+# STRATEGY:
+# 1. Create a filesystem and several sub-filesystems in it.
+# 2. Make recursively snapshot.
+# 3. Verify internal journal records all the recursively operations.
+# 4. Do the same verification to inherit, rollback and destroy.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-12-22)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+$ZFS 2>&1 | $GREP "allow" > /dev/null
+(($? != 0)) && log_unsupported
+
+function cleanup
+{
+ [[ -f $REAL_HISTORY ]] && $RM -f $REAL_HISTORY
+ [[ -f $ADD_HISTORY ]] && $RM -f $ADD_HISTORY
+ if datasetexists $root_testfs; then
+ log_must $ZFS destroy -rf $root_testfs
+ fi
+ log_must $ZFS create $root_testfs
+}
+
+log_assert "Internal journal records all the recursively operations."
+log_onexit cleanup
+
+root_testfs=$TESTPOOL/$TESTFS
+fs1=$root_testfs/fs1; fs2=$root_testfs/fs2; fs3=$root_testfs/fs3
+for fs in $fs1 $fs2 $fs3; do
+ log_must $ZFS create $fs
+done
+
+#
+# Verify 'zfs snapshot -r'
+#
+format_history $TESTPOOL $REAL_HISTORY -i
+log_must $ZFS snapshot -r ${root_testfs}@snap
+additional_history $TESTPOOL $ADD_HISTORY -i
+for ds in $fs1 $fs2 $fs3 ; do
+ log_must verify_history $ADD_HISTORY "snapshot" ${ds}@snap
+done
+
+log_must $ZFS snapshot ${root_testfs}@snap2
+log_must $ZFS snapshot ${root_testfs}@snap3
+typeset snap2_id=$(get_dataset_id ${root_testfs}@snap2)
+typeset snap3_id=$(get_dataset_id ${root_testfs}@snap3)
+
+#
+# Verify 'zfs rollback -r'
+#
+format_history $TESTPOOL $REAL_HISTORY -i
+log_must $ZFS rollback -r ${root_testfs}@snap
+additional_history $TESTPOOL $ADD_HISTORY -i
+
+cat $ADD_HISTORY
+for ds_id in ${snap2_id} ${snap3_id}; do
+ log_must verify_destroyed $ADD_HISTORY $ds_id
+done
+log_must verify_direct_history $ADD_HISTORY "rollback -r" $root_testfs
+
+#
+# Verify 'zfs inherit -r'
+#
+format_history $TESTPOOL $REAL_HISTORY -i
+log_must $ZFS inherit -r mountpoint $root_testfs
+additional_history $TESTPOOL $ADD_HISTORY -i
+cat $ADD_HISTORY
+for ds in $fs1 $fs2 $fs3 $root_testfs; do
+ log_must verify_history $ADD_HISTORY "inherit" $ds
+done
+log_must verify_direct_history $ADD_HISTORY "inherit -r mountpoint" $root_testfs
+
+# Initial original $REAL_HISTORY
+format_history $TESTPOOL $REAL_HISTORY -i
+
+fs1_id=$(get_dataset_id $fs1)
+fs2_id=$(get_dataset_id $fs2)
+fs3_id=$(get_dataset_id $fs3)
+root_id=$(get_dataset_id $root_testfs)
+fs1_snap_id=$(get_dataset_id ${fs1}@snap)
+fs2_snap_id=$(get_dataset_id ${fs2}@snap)
+fs3_snap_id=$(get_dataset_id ${fs3}@snap)
+root_snap_id=$(get_dataset_id ${root_testfs}@snap)
+
+#
+# Verify 'zfs destroy -r'
+#
+log_must $ZFS destroy -r $root_testfs
+additional_history $TESTPOOL $ADD_HISTORY -i
+cat $ADD_HISTORY
+for ds_id in ${fs1_id} ${fs2_id} ${fs3_id} ${root_id} ${fs1_snap_id} ${fs2_snap_id} ${fs3_snap_id} ${root_snap_id}; do
+ log_must verify_destroyed $ADD_HISTORY $ds_id
+done
+log_must verify_direct_history $ADD_HISTORY "destroy -r" $root_testfs
+
+log_pass "Internal journal records all the recursively operations passed."
diff --git a/tests/sys/cddl/zfs/tests/history/history_009_pos.ksh b/tests/sys/cddl/zfs/tests/history/history_009_pos.ksh
new file mode 100644
index 000000000000..1d862bebb916
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/history/history_009_pos.ksh
@@ -0,0 +1,175 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/history/history_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: history_009_pos
+#
+# DESCRIPTION:
+# Verify the delegation internal history are correctly.
+#
+# ul$<id> identifies permssions granted locally for this userid.
+# ud$<id> identifies permissions granted on descendent datasets for
+# this userid.
+#
+# Ul$<id> identifies permission sets granted locally for this userid.
+# Ud$<id> identifies permission sets granted on descendent datasets for
+# this userid.
+#
+# gl$<id> identifies permissions granted locally for this groupid.
+# gd$<id> identifies permissions granted on descendent datasets for
+# this groupid.
+#
+# Gl$<id> identifies permission sets granted locally for this groupid.
+# Gd$<id> identifies permission sets granted on descendent datasets for
+# this groupid.
+#
+# el$ identifies permissions granted locally for everyone.
+# ed$ identifies permissions granted on descendent datasets for
+# everyone.
+#
+# El$ identifies permission sets granted locally for everyone.
+# Ed$ identifies permission sets granted to descendent datasets
+# for everyone.
+#
+# c-$ identifies permission to create at dataset creation time.
+# C-$ identifies permission sets to grant locally at dataset
+# creation time.
+#
+# s-$@<name> permissions defined in specified set @<name>
+# S-$@<name> Sets defined in named set @<name>
+#
+# STRATEGY:
+# 1. Create test group and user.
+# 2. Define permission sets and verify the internal history correctly.
+# 3. Separately verify the internal history above is correct.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-12-26)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+$ZFS 2>&1 | $GREP "allow" > /dev/null
+(($? != 0)) && log_unsupported
+
+function cleanup
+{
+ if [[ -f $REAL_HISTORY ]]; then
+ log_must $RM -f $REAL_HISTORY
+ fi
+ if [[ -f $ADD_HISTORY ]]; then
+ log_must $RM -f $ADD_HISTORY
+ fi
+ del_user $HIST_USER
+ del_group $HIST_GROUP
+}
+
+log_assert "Verify the delegation internal history are correctly."
+log_onexit cleanup
+
+testfs=$TESTPOOL/$TESTFS
+# Create history test group and user and get user id and group id
+add_group $HIST_GROUP
+add_user $HIST_GROUP $HIST_USER
+
+uid=$($ID $HIST_USER | $AWK -F= '{print $2}'| $AWK -F"(" '{print $1}' )
+gid=$($ID $HIST_USER | $AWK -F= '{print $3}'| $AWK -F"(" '{print $1}' )
+
+# Initial original $REAL_HISTORY
+format_history $TESTPOOL $REAL_HISTORY -i
+
+#
+# Keyword subcmd operating allow_options
+#
+set -A array \
+ "s-\$@basic" "allow" "-s @basic snapshot" \
+ "S-\$@set" "allow" "-s @set @basic" \
+ "c-\\$" "allow" "-c create" \
+ "c-\\$" "unallow" "-c create" \
+ "C-\\$ @set" "allow" "-c @set" \
+ "C-\\$ @set" "unallow" "-c @set" \
+ "ul\$$uid" "allow" "-l -u $HIST_USER snapshot" \
+ "ul\$$uid" "allow" "-u $HIST_USER snapshot" \
+ "ul\$$uid" "unallow" "-u $HIST_USER snapshot" \
+ "Ul\$$uid" "allow" "-l -u $HIST_USER @set" \
+ "Ul\$$uid" "allow" "-u $HIST_USER @set" \
+ "Ul\$$uid" "unallow" "-u $HIST_USER @set" \
+ "ud\$$uid" "allow" "-d -u $HIST_USER snapshot" \
+ "ud\$$uid" "allow" "-u $HIST_USER snapshot" \
+ "ud\$$uid" "unallow" "-u $HIST_USER snapshot" \
+ "Ud\$$uid" "allow" "-d -u $HIST_USER @set" \
+ "Ud\$$uid" "allow" "-u $HIST_USER @set" \
+ "Ud\$$uid" "unallow" "-u $HIST_USER @set" \
+ "gl\$$gid" "allow" "-l -g $HIST_GROUP snapshot" \
+ "gl\$$gid" "allow" "-g $HIST_GROUP snapshot" \
+ "gl\$$gid" "unallow" "-g $HIST_GROUP snapshot" \
+ "Gl\$$gid" "allow" "-l -g $HIST_GROUP @set" \
+ "Gl\$$gid" "allow" "-g $HIST_GROUP @set" \
+ "Gl\$$gid" "unallow" "-g $HIST_GROUP @set" \
+ "gd\$$gid" "allow" "-d -g $HIST_GROUP snapshot" \
+ "gd\$$gid" "allow" "-g $HIST_GROUP snapshot" \
+ "gd\$$gid" "unallow" "-g $HIST_GROUP snapshot" \
+ "Gd\$$gid" "allow" "-d -g $HIST_GROUP @set" \
+ "Gd\$$gid" "allow" "-g $HIST_GROUP @set" \
+ "Gd\$$gid" "unallow" "-g $HIST_GROUP @set" \
+ "el\\$" "allow" "-l -e snapshot" \
+ "el\\$" "allow" "-e snapshot" \
+ "el\\$" "unallow" "-e snapshot" \
+ "El\\$" "allow" "-l -e @set" \
+ "El\\$" "allow" "-e @set" \
+ "El\\$" "unallow" "-e @set" \
+ "ed\\$" "allow" "-d -e snapshot" \
+ "ed\\$" "allow" "-e snapshot" \
+ "ed\\$" "unallow" "-e snapshot" \
+ "Ed\\$" "allow" "-d -e @set" \
+ "Ed\\$" "allow" "-e @set" \
+ "Ed\\$" "unallow" "-e @set"
+
+typeset -i i=0
+while ((i < ${#array[@]})); do
+ keyword=${array[$i]}
+ subcmd=${array[((i+1))]}
+ options=${array[((i+2))]}
+
+ log_must $ZFS $subcmd $options $testfs
+ additional_history $TESTPOOL $ADD_HISTORY -i
+ log_must verify_history $ADD_HISTORY $subcmd $testfs $keyword
+
+ ((i += 3))
+done
+
+log_pass "Verify the delegation internal history are correctly."
diff --git a/tests/sys/cddl/zfs/tests/history/history_010_pos.ksh b/tests/sys/cddl/zfs/tests/history/history_010_pos.ksh
new file mode 100644
index 000000000000..b101219aef5a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/history/history_010_pos.ksh
@@ -0,0 +1,103 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/history/history_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: history_010_pos
+#
+# DESCRIPTION:
+# Verify internal long history information are correct.
+#
+# STRATEGY:
+# 1. Create non-root test user and group.
+# 2. Do some zfs operation test by root and non-root user.
+# 3. Verify the long history information are correct.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-12-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+$ZFS 2>&1 | $GREP "allow" > /dev/null
+(($? != 0)) && log_unsupported
+
+function cleanup
+{
+ [[ -f $REAL_HISTORY ]] && $RM -f $REAL_HISTORY
+ [[ -f $EXPECT_HISTORY ]] && $RM -f $EXPECT_HISTORY
+
+ del_user $HIST_USER
+ del_group $HIST_GROUP
+
+ datasetexists $root_testfs && log_must $ZFS destroy -rf $root_testfs
+}
+
+log_assert "Verify internal long history information are correct."
+log_onexit cleanup
+
+root_testfs=$TESTPOOL/$TESTFS1
+
+# Create history test group and user and get user id and group id
+add_group $HIST_GROUP
+add_user $HIST_GROUP $HIST_USER
+uid=$($ID $HIST_USER | $AWK -F= '{print $2}'| $AWK -F"(" '{print $1}' )
+gid=$($ID $HIST_USER | $AWK -F= '{print $3}'| $AWK -F"(" '{print $1}' )
+
+# Get original long history
+format_history $TESTPOOL $EXPECT_HISTORY "-l"
+
+exec_record -l $ZFS create $root_testfs
+exec_record -l $ZFS allow $HIST_GROUP snapshot,mount $root_testfs
+exec_record -l $ZFS allow $HIST_USER destroy,mount $root_testfs
+exec_record -l $ZFS allow $HIST_USER reservation $root_testfs
+exec_record -l $ZFS allow $HIST_USER allow $root_testfs
+
+exec_record -l -u $HIST_USER "$ZFS snapshot $root_testfs@snap"
+exec_record -l -u $HIST_USER "$ZFS destroy $root_testfs@snap"
+exec_record -l -u $HIST_USER "$ZFS reservation=64M $root_testfs"
+exec_record -l -u $HIST_USER "$ZFS allow $HIST_USER reservation $root_testfs"
+exec_record -l $ZFS unallow $HIST_USER create $root_testfs
+exec_record -l $ZFS unallow $HIST_GROUP snapshot $root_testfs
+exec_record -l $ZFS destroy -r $root_testfs
+
+format_history $TESTPOOL $REAL_HISTORY "-l"
+log_must $DIFF $REAL_HISTORY $EXPECT_HISTORY
+
+del_user $HIST_USER
+del_group $HIST_GROUP
+
+log_pass "Verify internal long history information pass."
diff --git a/tests/sys/cddl/zfs/tests/history/history_common.kshlib b/tests/sys/cddl/zfs/tests/history/history_common.kshlib
new file mode 100644
index 000000000000..cebd4fb6fcff
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/history/history_common.kshlib
@@ -0,0 +1,263 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#
+# Execute arguments and record them to the log file.
+# Notice: EXPECT_HISTORY need be defined.
+#
+# $1-n arguments for execution.
+#
+function exec_record
+{
+ [[ -z $EXPECT_HISTORY ]] && log_fail "EXPECT_HISTORY is undefined."
+
+ typeset long_hist
+ typeset user='root'
+ typeset opt
+ while getopts ":lu:" opt; do
+ case $opt in
+ l) long_hist=1;;
+ u) user=$OPTARG ;;
+ esac
+ done
+ shift $(($OPTIND -1))
+
+ if [[ $user == 'root' ]]; then
+ log_must "$@"
+ else
+ log_must $SU $user -c "$@"
+ fi
+ user_id=$(id -u $user)
+
+ typeset cmdline="$@"
+ # Remove "eval" ">*" & "<*" for 'zfs send' and 'zfs receive'.
+ cmdline=${cmdline#eval}
+ cmdline=${cmdline%%\>*}
+ cmdline=${cmdline%%\<*}
+
+ # Remove additional blank
+ cmdline=${cmdline## }
+ cmdline=${cmdline%% }
+
+ # Get the basename of command. i.e: /usr/sbin/zpool -> zpool
+ typeset cmd=$($ECHO $cmdline | $AWK '{print $1}')
+ eval cmdline=\${cmdline#$cmd}
+ cmd=${cmd##*/}
+
+ # Write basic history to file
+ print -n $cmd $cmdline >> $EXPECT_HISTORY
+ if [[ -n $long_hist ]]; then
+ # Write long history to file
+ hn=$($HOSTNAME)
+ zn=$($ZONENAME)
+ [ "$zn" = "global" ] && zn=""
+ [ -n "$zn" ] && zn=":$zn"
+ print -n " [user $user_id ($user) on $hn$zn]" >> $EXPECT_HISTORY
+ fi
+ # Plus enter in the end of line
+ print >> $EXPECT_HISTORY
+}
+
+#
+# Format 'zpool history' output to specified file.
+#
+# $1 pool name
+# $2 output file.
+# $3 option
+#
+function format_history
+{
+ typeset pool=$1
+ typeset outfile=$2
+ typeset option=$3
+
+ [[ -z $pool || -z $outfile ]] && \
+ log_fail "Usage: format_history <pool> <outfile> [option]"
+
+ typeset temp_history=$TMPDIR/temp_history.format_history.${TESTCASE_ID}
+ $ZPOOL history $option $pool > $temp_history
+
+ # Truncate output file
+ $CAT /dev/null > $outfile
+
+ typeset line
+ typeset -i n=0
+ while read line; do
+ # Ignore the first line and empty line
+ if [[ $n -eq 0 || -z $line ]]; then
+ n=1; continue
+ fi
+ $ECHO ${line#* } >> $outfile
+ done < $temp_history
+
+ $RM -f $temp_history
+}
+
+#
+# Get the additional pool history.
+#
+# $1 pool name
+# $2 additional history file
+# $3 option
+#
+function additional_history
+{
+ typeset pool=$1
+ typeset add_his_file=$2
+ typeset option=$3
+
+ if [[ -z $pool || -z $add_his_file ]]; then
+ log_fail "Usage: additional_history <pool> " \
+ "<additional_history_file> [option]"
+ fi
+
+ typeset temp_history=$TMPDIR/temp_history.additional_history.${TESTCASE_ID}
+ # Current current history
+ format_history $pool $temp_history $option
+ # Figure out new history
+ $DIFF $temp_history $REAL_HISTORY | $GREP "^<" | $SED 's/^<[ ]*//' > \
+ $add_his_file
+
+ $CP $temp_history $REAL_HISTORY
+ $RM -f $temp_history
+}
+
+#
+# Get given dataset id
+#
+# $1 dataset name
+#
+function get_dataset_id
+{
+ typeset ds=$1
+
+ #
+ # The zdb information looks like:
+ #
+ # Dataset pool/fs [ZPL], ID 21, cr_txg 6, 18.0K, 4 objects
+ #
+ typeset dst_id=$($ZDB $ds | $GREP "^Dataset $ds " | \
+ $AWK -F\, '{print $2}' | $AWK '{print $2}')
+
+ $ECHO $dst_id
+}
+
+#
+# Special case of verify_history, but only for destroyed datasets. This is
+# needed because get_dataset_id depends on still having the original dataset
+# in order to obtain its dataset id.
+#
+function verify_destroyed #<his_file> <ds_id>
+{
+ typeset his_file=$1
+ typeset ds_id=$2
+
+ $GREP -E "\[txg:[0-9]+\] destroy [^ ]+ \($ds_id\)" $his_file \
+ > /dev/null 2>&1
+ (($? == 0)) && return 0
+ return 1
+}
+
+#
+# Verify directly executed commands in a history file. This differs from
+# verify_history in that it checks for explicit commands as opposed to
+# internally generated commands.
+#
+function verify_direct_history #<his_file> <subcmd> <ds>
+{
+ typeset his_file=$1
+ typeset subcmd=$2
+ typeset ds=$3
+
+ $GREP "zfs ${subcmd} ${ds}" ${his_file} > /dev/null 2>&1
+ (($? == 0)) && return 0
+ return 1
+}
+
+# This function mostly just helps to collapse the case statement
+# in verify_history. It returns whether the line matches (1==true).
+function verify_history_line
+{
+ typeset line=$1
+ typeset subcmd=$2
+ typeset ds=$3
+ typeset keyword=$4
+
+ typeset dst_id=$(get_dataset_id $ds)
+ log_note "Line: '$line'"
+ log_note "Checking cmd($subcmd) for $ds, keyword='$keyword'"
+ $ECHO $line | $GREP -E "\[txg:[0-9]+\] $subcmd $ds \($dst_id\)" | \
+ $GREP $keyword >/dev/null 2>&1
+ [[ $? == 0 ]] && return 1
+ return 0
+}
+
+#
+# Scan history file and check if it include expected internal history
+# information
+#
+# $1 history file
+# $2 subcmd
+# $3 dataset
+# $4 keyword
+#
+function verify_history #<his_file> <subcmd> <ds> [keyword]
+{
+ typeset his_file=$1
+ typeset subcmd=$2
+ typeset ds=$3
+ typeset keyword=$4
+
+ typeset line found=0
+ log_note "Test1"
+ while read line; do
+ case $subcmd in
+ snapshot|rollback|inherit)
+ # [txg:12] snapshot system/foo@0 (46)
+ keyword="$subcmd"
+ verify_history_line "$line" $subcmd $ds $keyword
+ [[ $? == 0 ]] && return 0
+ ;;
+ allow)
+ # [txg:10] permission update testpool.1477/testfs.1477 (40) s-$@basic snapshot
+ _subcmd="permission update"
+ verify_history_line "$line" "$_subcmd" $ds "$keyword"
+ [[ $? == 0 ]] && return 0
+ ;;
+ unallow)
+ # [txg:174] permission remove testpool.1477/testfs.1477 (40) El$ @set
+ _subcmd="permission remove"
+ verify_history_line "$line" "$_subcmd" $ds "$keyword"
+ [[ $? == 0 ]] && return 0
+ ;;
+ *)
+ ;;
+ esac
+ done < $his_file
+ return 1
+}
diff --git a/tests/sys/cddl/zfs/tests/history/history_test.sh b/tests/sys/cddl/zfs/tests/history/history_test.sh
new file mode 100755
index 000000000000..86e61fba8dff
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/history/history_test.sh
@@ -0,0 +1,299 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case history_001_pos
+history_001_pos_head()
+{
+ atf_set "descr" "Verify zpool sub-commands which modify state are logged."
+ atf_set "require.progs" "ksh93 zpool nawk"
+ atf_set "timeout" 1800
+}
+history_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/history_common.kshlib
+ . $(atf_get_srcdir)/history.cfg
+
+ ksh93 $(atf_get_srcdir)/history_001_pos.ksh || atf_fail "Testcase failed"
+}
+
+atf_test_case history_002_pos cleanup
+history_002_pos_head()
+{
+ atf_set "descr" "Verify zfs sub-commands which modify state are logged."
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 1800
+}
+history_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/history_common.kshlib
+ . $(atf_get_srcdir)/history.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/history_002_pos.ksh || atf_fail "Testcase failed"
+}
+history_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/history_common.kshlib
+ . $(atf_get_srcdir)/history.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case history_003_pos cleanup
+history_003_pos_head()
+{
+ atf_set "descr" "zpool history limitation test."
+ atf_set "require.progs" "ksh93 zpool zfs"
+ atf_set "timeout" 1800
+}
+history_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/history_common.kshlib
+ . $(atf_get_srcdir)/history.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/history_003_pos.ksh || atf_fail "Testcase failed"
+}
+history_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/history_common.kshlib
+ . $(atf_get_srcdir)/history.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case history_004_pos cleanup
+history_004_pos_head()
+{
+ atf_set "descr" "'zpool history' can copes with many simultaneous command."
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 1800
+}
+history_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/history_common.kshlib
+ . $(atf_get_srcdir)/history.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/history_004_pos.ksh || atf_fail "Testcase failed"
+}
+history_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/history_common.kshlib
+ . $(atf_get_srcdir)/history.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case history_005_neg cleanup
+history_005_neg_head()
+{
+ atf_set "descr" "Verify 'zpool list|status|iostat' will not be logged."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1800
+}
+history_005_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/history_common.kshlib
+ . $(atf_get_srcdir)/history.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/history_005_neg.ksh || atf_fail "Testcase failed"
+}
+history_005_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/history_common.kshlib
+ . $(atf_get_srcdir)/history.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case history_006_neg cleanup
+history_006_neg_head()
+{
+ atf_set "descr" "Verify 'zfs list|get|mount|unmount|share|unshare|send' will notbe logged."
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 1800
+}
+history_006_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/history_common.kshlib
+ . $(atf_get_srcdir)/history.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/history_006_neg.ksh || atf_fail "Testcase failed"
+}
+history_006_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/history_common.kshlib
+ . $(atf_get_srcdir)/history.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case history_007_pos cleanup
+history_007_pos_head()
+{
+ atf_set "descr" "Verify command history moves with pool while migrating."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1800
+}
+history_007_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/history_common.kshlib
+ . $(atf_get_srcdir)/history.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/history_007_pos.ksh || atf_fail "Testcase failed"
+}
+history_007_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/history_common.kshlib
+ . $(atf_get_srcdir)/history.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case history_008_pos cleanup
+history_008_pos_head()
+{
+ atf_set "descr" "Internal journal records all the recursively operations."
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 1800
+}
+history_008_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/history_common.kshlib
+ . $(atf_get_srcdir)/history.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/history_008_pos.ksh || atf_fail "Testcase failed"
+}
+history_008_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/history_common.kshlib
+ . $(atf_get_srcdir)/history.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case history_009_pos cleanup
+history_009_pos_head()
+{
+ atf_set "descr" "Verify the delegation internal history are correctly."
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 1800
+}
+history_009_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/history_common.kshlib
+ . $(atf_get_srcdir)/history.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/history_009_pos.ksh || atf_fail "Testcase failed"
+}
+history_009_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/history_common.kshlib
+ . $(atf_get_srcdir)/history.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case history_010_pos cleanup
+history_010_pos_head()
+{
+ atf_set "descr" "Verify internal long history information are correct."
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 1800
+}
+history_010_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/history_common.kshlib
+ . $(atf_get_srcdir)/history.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/history_010_pos.ksh || atf_fail "Testcase failed"
+}
+history_010_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/history_common.kshlib
+ . $(atf_get_srcdir)/history.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case history_001_pos
+ atf_add_test_case history_002_pos
+ atf_add_test_case history_003_pos
+ atf_add_test_case history_004_pos
+ atf_add_test_case history_005_neg
+ atf_add_test_case history_006_neg
+ atf_add_test_case history_007_pos
+ atf_add_test_case history_008_pos
+ atf_add_test_case history_009_pos
+ atf_add_test_case history_010_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/history/i386.migratedpool.DAT.Z b/tests/sys/cddl/zfs/tests/history/i386.migratedpool.DAT.Z
new file mode 100644
index 000000000000..21cd7fcf583a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/history/i386.migratedpool.DAT.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/history/i386.orig_history.txt b/tests/sys/cddl/zfs/tests/history/i386.orig_history.txt
new file mode 100644
index 000000000000..02e279eec52e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/history/i386.orig_history.txt
@@ -0,0 +1,13 @@
+History for 'history_pool':
+2006-10-20.13:18:37 zpool create history_pool /var/tmp/i386.migratedpool.DAT
+2006-10-20.13:18:37 zfs create history_pool/fs
+2006-10-20.13:18:37 zfs set compression=on history_pool/fs
+2006-10-20.13:18:37 zfs set checksum=on history_pool
+2006-10-20.13:18:37 zfs snapshot history_pool/fs@snap
+2006-10-20.13:18:37 zfs clone history_pool/fs@snap history_pool/clone
+2006-10-20.13:18:37 zfs promote history_pool/clone
+2006-10-20.13:18:37 zfs promote history_pool/fs
+2006-10-20.13:18:37 zfs destroy -r -R history_pool/fs
+2006-10-20.13:18:37 zpool export history_pool
+2007-04-05.00:05:38 zpool upgrade history_pool
+
diff --git a/tests/sys/cddl/zfs/tests/history/setup.ksh b/tests/sys/cddl/zfs/tests/history/setup.ksh
new file mode 100644
index 000000000000..5e4c5dbc884e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/history/setup.ksh
@@ -0,0 +1,33 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+$ZPOOL history > /dev/null 2>&1
+(($? != 0)) && log_unsupported
+
+DISK=${DISKS%% *}
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/history/sparc.migratedpool.DAT.Z b/tests/sys/cddl/zfs/tests/history/sparc.migratedpool.DAT.Z
new file mode 100644
index 000000000000..47a61ffd51a6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/history/sparc.migratedpool.DAT.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/history/sparc.orig_history.txt b/tests/sys/cddl/zfs/tests/history/sparc.orig_history.txt
new file mode 100644
index 000000000000..6ae7168bbba6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/history/sparc.orig_history.txt
@@ -0,0 +1,13 @@
+History for 'history_pool':
+2006-10-27.03:13:47 zpool create history_pool /var/tmp/sparc.migratedpool.DAT
+2006-10-27.03:13:48 zfs create history_pool/fs
+2006-10-27.03:13:48 zfs set compression=on history_pool/fs
+2006-10-27.03:13:48 zfs set checksum=on history_pool
+2006-10-27.03:13:48 zfs snapshot history_pool/fs@snap
+2006-10-27.03:13:48 zfs clone history_pool/fs@snap history_pool/clone
+2006-10-27.03:13:49 zfs promote history_pool/clone
+2006-10-27.03:13:49 zfs promote history_pool/fs
+2006-10-27.03:13:49 zfs destroy -r -R history_pool/fs
+2006-10-27.03:13:49 zpool export history_pool
+2007-04-05.00:41:55 zpool upgrade history_pool
+
diff --git a/tests/sys/cddl/zfs/tests/history/zfs-pool-v4.dat.Z b/tests/sys/cddl/zfs/tests/history/zfs-pool-v4.dat.Z
new file mode 100644
index 000000000000..bc32472f0084
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/history/zfs-pool-v4.dat.Z
Binary files differ
diff --git a/tests/sys/cddl/zfs/tests/hotplug/Makefile b/tests/sys/cddl/zfs/tests/hotplug/Makefile
new file mode 100644
index 000000000000..2264668f14bd
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotplug/Makefile
@@ -0,0 +1,19 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/hotplug
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= hotplug_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= hotplug.cfg
+${PACKAGE}FILES+= hotplug.kshlib
+${PACKAGE}FILES+= hotplug_001_pos.ksh
+${PACKAGE}FILES+= hotplug_008_pos.ksh
+${PACKAGE}FILES+= hotplug_011_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/hotplug/cleanup.ksh b/tests/sys/cddl/zfs/tests/hotplug/cleanup.ksh
new file mode 100644
index 000000000000..8efc1007dd80
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotplug/cleanup.ksh
@@ -0,0 +1,30 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/hotplug/hotplug.kshlib
+
+cleanup_testenv $TESTPOOL
+destroy_gnops $ALL_DISKS
diff --git a/tests/sys/cddl/zfs/tests/hotplug/hotplug.cfg b/tests/sys/cddl/zfs/tests/hotplug/hotplug.cfg
new file mode 100644
index 000000000000..8aab7dbf9b06
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotplug/hotplug.cfg
@@ -0,0 +1,31 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export DEV_DISKS="$DISK0 $DISK1 $DISK2"
+export SPARE_DISKS="$DISK3"
+export ALL_DISKS="$DEV_DISKS $SPARE_DISKS"
+
+set_disks
diff --git a/tests/sys/cddl/zfs/tests/hotplug/hotplug.kshlib b/tests/sys/cddl/zfs/tests/hotplug/hotplug.kshlib
new file mode 100644
index 000000000000..ef6480978140
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotplug/hotplug.kshlib
@@ -0,0 +1,458 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/include/libgnop.kshlib
+. $STF_SUITE/tests/hotplug/hotplug.cfg
+
+#
+# create lofi devices
+#
+# $1-n files
+#
+function create_lofi_device
+{
+ typeset lofi_files="$@"
+
+ typeset file
+ for file in $lofi_files ; do
+ if ! $LOFIADM $file > /dev/null 2>&1 ; then
+ insert_device $file
+ if (($? != 0)); then
+ return 1
+ fi
+ fi
+
+ shift
+ done
+
+ return 0
+}
+
+#
+# Check and destroy lofi devices
+#
+# $1-n lofi files or deviece
+#
+function destroy_lofi_device
+{
+ typeset -i ret=0
+ typeset dev_file="$@"
+
+ typeset file
+ for file in $dev_file ; do
+ if $LOFIADM $file > /dev/null 2>&1; then
+ $LOFIADM -d $file -f
+ if (($? != 0)); then
+ ((ret += 1))
+ fi
+ fi
+ done
+
+ return $ret
+}
+
+#
+# Setup test environment using DISKS[1-4]
+#
+# $1 pool type
+#
+function setup_testenv
+{
+ typeset type=$1
+
+ if [[ -z $type ]]; then
+ log_fail "Usage: setup_testenv <type>"
+ fi
+
+ log_must $ZPOOL create -f \
+ $TESTPOOL $type ${DISK0}.nop ${DISK1}.nop ${DISK2}.nop ${DISK3}.nop
+}
+
+#
+# Cleanup test envirnment according to pool name
+#
+# $1 pool
+#
+function cleanup_testenv
+{
+ destroy_pool $TESTPOOL
+}
+
+
+#
+# Start a background process to write file on given pool.
+#
+# $1 pool
+#
+function start_bg_write
+{
+ typeset pool=$1
+
+ if datasetnonexists $pool; then
+ return 1
+ fi
+
+ typeset mntpnt=$(get_prop mountpoint $pool)
+
+ while true; do
+ $DD if=/dev/random of=$mntpnt/foo count=10
+ $SYNC
+ $SLEEP 1
+ done &
+ BG_PID=$!
+
+ return 0
+}
+
+#
+# Kill the background write process.
+#
+function kill_bg_write
+{
+ typeset -i ret=0
+
+ if [[ -n $BG_PID ]]; then
+ kill -9 $BG_PID
+ ret=$?
+
+ if ((ret == 0)); then
+ BG_PID=''
+ fi
+ fi
+
+ $SLEEP 10
+ return $ret
+}
+
+#
+# Insert a given file into a given device slot
+#
+# $1 file
+# $2 device
+#
+function insert_device
+{
+ typeset file=$1
+ typeset device=$2
+
+ if [[ -z $file ]]; then
+ return 1
+ fi
+
+ #
+ # Make sure insert device succeed within 60 seconds
+ #
+ typeset -i i=0
+ while ((i < 6)); do
+ $SLEEP 10
+
+ $LOFIADM -a $file $device -f > /dev/null 2>&1
+ if (($? == 0)); then
+ return 0
+ fi
+
+ ((i += 1))
+ done
+
+ return 1
+}
+
+#
+# Remove the given lofi device
+#
+# $1 device or file
+#
+function remove_device
+{
+ typeset devfile=$1
+
+ if [[ -z $devfile ]]; then
+ return 1
+ fi
+
+ #
+ # Make sure remove device succeed within 60 seconds
+ #
+ typeset -i i=0
+ while ((i < 6)); do
+ $SLEEP 10
+ $LOFIADM -d $devfile -f
+ if (($? == 0)); then
+ return 0
+ fi
+
+ ((i += 1))
+ done
+
+ return 1
+}
+
+#
+# Verify the given devices have expected status in pool
+#
+# $1 pool
+# $2 device
+# $3 expected status
+#
+function verify_device_status
+{
+ typeset pool=$1
+ typeset device=$2
+ typeset expect_stat=$3
+
+ if [[ -z $pool || -z $expect_stat || -z $device ]]; then
+ log_note "Usage: verify_device_status <pool> <device> <status>"
+ return 1
+ fi
+
+ #
+ # 1.5 minute disk status checking, make sure zpool sync disk status.
+ #
+ typeset -i i=0
+ while ((i < 9)); do
+ $SLEEP 10
+
+ typeset str=$($ZPOOL status $pool | $GREP "lofi" | \
+ $AWK '{print $1 " " $2}')
+ typeset real_stat=${str##*$device }
+ real_stat=$($ECHO $real_stat | $AWK '{print $1}')
+
+ if [[ "$expect_stat" == "$real_stat" ]]; then
+ return 0
+ fi
+
+ ((i += 1))
+ done
+
+ log_note "Expected status($expect_stat), " \
+ "see status($real_stat) for $device"
+ $ZPOOL status -v $pool
+
+ return 1
+}
+
+#
+# Output fma event id to given file, the default output file is $FILE_EVENT_ID
+#
+function getfmri
+{
+ eval typeset output_file=${1:-$FILE_EVENT_ID}
+#
+# fmadm faulty output include several sections below
+#
+
+# --------------- ------------------------------------ -------------- ---------
+# TIME EVENT-ID MSG-ID SEVERITY
+# --------------- ------------------------------------ -------------- ---------
+# Aug 31 22:34:19 ec648a9e-0c9f-c495-e176-e38ba212e278 ZFS-8000-D3 Major
+# Aug 31 19:44:59 d69cdd12-b0cf-62ea-d0a3-8d2e9ebfeb50 ZFS-8000-D3
+# Aug 31 19:35:16 7213f0d5-00d4-ea32-ddfc-98cdd683c27e ZFS-8000-D3
+# Aug 31 19:29:11 33424bef-a973-4dae-94ef-cb97f2cb0759 ZFS-8000-D3
+# Aug 31 17:07:26 74219b66-ead4-6d2b-bbad-bc40547ca02e ZFS-8000-GH
+#
+# Fault class : fault.fs.zfs.device
+#
+# Description : A ZFS device failed. Refer to http://sun.com/msg/ZFS-8000-D3 for
+# more information.
+#
+# Response : No automated response will occur.
+#
+# Impact : Fault tolerance of the pool may be compromised.
+#
+# Action : Run 'zpool status -x' and replace the bad device.
+#
+ $FMADM faulty | $NAWK '
+ BEGIN {
+ start = 0
+ }
+ /^---/ && /---$/ {
+ if (start == 0) {
+ start = 1
+ }
+ }
+ /^TIME/ && /SEVERITY$/ {
+ if (start == 1) {
+ start = 2
+ }
+ }
+ /^---/ && /---$/ {
+ if (start == 2) {
+ start = 3
+ continue
+ }
+ }
+ /^$/ {
+ start = 0
+ }
+ (start == 3) {print $4}' > $output_file
+}
+
+#
+# Verify if ZFS FMA faulty error message are generated.
+#
+# $1 TRUE or FALSE
+#
+function fma_faulty
+{
+ #
+ # 1.5 minute for FMA faulty checking, make sure FMA sync with ZFS status
+ #
+ typeset expect=${1:-TRUE}
+
+ typeset -i fsize
+ typeset -i i=0
+ while ((i < 9)); do
+ $SLEEP 10
+
+ #
+ # try to get fma faulty
+ #
+ getfmri $FILE_EVENT_ID
+ fsize=$($LS -ld $FILE_EVENT_ID | $AWK '{print $5}')
+
+ case $expect in
+ TRUE)
+ if (( fsize != 0 )); then
+ return 0
+ fi
+ ;;
+ FALSE)
+ if (( fsize == 0 )); then
+ return 0
+ fi
+ ;;
+ *)
+ return 1
+ esac
+
+ ((i += 1))
+ done
+
+ return 1
+}
+
+#
+# Create fresh file
+#
+# $1 file size
+# $2 file name
+#
+function create_file
+{
+ typeset size=$1
+ typeset file=$2
+
+ if [[ -z $size || -z $file ]]; then
+ log_note "Usage: create_file <size> <file>"
+ return 1
+ fi
+ if [[ -f $file ]]; then
+ $RM -f $file
+ fi
+ $MKFILE $size $file
+ return $?
+}
+
+#
+# Unmount all filesystem, and disable syseventd and fmd piror to
+# unloading ZFS module
+#
+function unload_zfs
+{
+ # destroy /dev/zvol link
+ log_must $ZFS volfini
+
+ log_must $ZFS unmount -f -a
+ log_must $SVCADM disable -t sysevent fmd
+ $SLEEP 10
+
+ #
+ # 1 minute for ZFS module unload checking
+ #
+ # For example:
+ #
+ # 192 fffffffff7c92000 99858 182 1 zfs (ZFS storage pool version 6)
+ #
+ typeset -i i=0
+ while ((i < 10)); do
+ typeset id=$($MODINFO | $GREP "ZFS storage" | $AWK '{print $1}')
+
+ if [[ -n $id ]]; then
+ $MODUNLOAD -i $id
+ if (($? == 0)) ; then
+ return 0
+ fi
+ else
+ return 0
+ fi
+
+ $SLEEP 6
+ ((i += 1))
+ done
+
+ return 1
+}
+
+#
+# Load ZFS module and remount all filesystem
+#
+function load_zfs
+{
+ typeset -i ret=0
+ $SVCADM enable sysevent fmd
+ ((ret |= $?))
+
+ $SLEEP 10
+
+ $ZFS mount -a
+ ((ret |= $?))
+
+ # create /dev/zvol link
+ $ZFS volinit
+ ((ret |= $?))
+
+ return $ret
+}
+
+#
+# Convert file name to device name or reverse.
+#
+# $1-n lofi files or devices
+#
+function convert_lofi
+{
+ typeset n list
+
+ for n in "$@"; do
+ typeset item=$($LOFIADM $n)
+ list="$list $item"
+
+ shift
+ done
+
+ $ECHO $list
+}
diff --git a/tests/sys/cddl/zfs/tests/hotplug/hotplug_001_pos.ksh b/tests/sys/cddl/zfs/tests/hotplug/hotplug_001_pos.ksh
new file mode 100644
index 000000000000..d625b479e88f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotplug/hotplug_001_pos.ksh
@@ -0,0 +1,72 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/hotplug/hotplug.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotplug_001_pos
+#
+# DESCRIPTION:
+# When removing a device from a redundant pool, the device's state will
+# be indicated as 'REMOVED'.
+#
+# STRATEGY:
+# 1. Create mirror/raidz/raidz2 pool.
+# 2. Synchronise with device in the background.
+# 3. Remove one of device of pool.
+# 4. Detect removed devices status is 'REMOVED'.
+# 5. Detect no FMA faulty message.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+log_assert "When removing a device from a redundant pool, the device's " \
+ "state will be indicated as 'REMOVED'."
+
+for type in "mirror" "raidz" "raidz2"; do
+ log_note "Start $type testing ..."
+ setup_testenv $type
+
+ log_must destroy_gnop $DISK0
+ wait_for 15 1 check_state $TESTPOOL ${DISK0}.nop 'REMOVED'
+ log_must check_state $TESTPOOL ${DISK0}.nop 'REMOVED'
+
+ log_must create_gnop $DISK0
+ cleanup_testenv $TESTPOOL
+done
+
+log_pass "When removing a device from a redundant pool, the device's " \
+ "state will be indicated as 'REMOVED'."
diff --git a/tests/sys/cddl/zfs/tests/hotplug/hotplug_008_pos.ksh b/tests/sys/cddl/zfs/tests/hotplug/hotplug_008_pos.ksh
new file mode 100644
index 000000000000..49d77d95ffc2
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotplug/hotplug_008_pos.ksh
@@ -0,0 +1,69 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/hotplug/hotplug.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotplug_008_pos
+#
+# DESCRIPTION:
+# After hot spare device is revoved, the devices state will be 'REMOVED'.
+# No FMA faults was generated.
+#
+# STRATEGY:
+# 1. Create mirror/raidz/raidz2 pool with hot spare device.
+# 2. Synchronise with device in the background.
+# 3. Remove the hotspare device.
+# 4. Verify the device's status is 'REMOVED'.
+# 5. Verify no FMA fault was generated.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+log_assert "When removing hotspare device, verify device status is 'REMOVED'."
+
+for type in "mirror" "raidz" "raidz2"; do
+ log_must $ZPOOL create -f $TESTPOOL $type $DISK0.nop $DISK1.nop $DISK2.nop spare $DISK3.nop
+
+ log_must destroy_gnop $DISK3
+ wait_for 15 1 check_state $TESTPOOL $DISK3.nop 'REMOVED'
+ log_must check_state $TESTPOOL $DISK3.nop 'REMOVED'
+
+ log_must create_gnop $DISK3
+ cleanup_testenv $TESTPOOL
+done
+
+log_pass "When removing hotspare device, verify device status is 'REMOVED'."
diff --git a/tests/sys/cddl/zfs/tests/hotplug/hotplug_011_pos.ksh b/tests/sys/cddl/zfs/tests/hotplug/hotplug_011_pos.ksh
new file mode 100644
index 000000000000..18b69d6dc09b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotplug/hotplug_011_pos.ksh
@@ -0,0 +1,79 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/hotplug/hotplug.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotplug_011_pos
+#
+# DESCRIPTION:
+# Removing device offlined, verify device status is UNAVAIL, when the
+# system is onlined.
+#
+# STRATEGY:
+# 1. Create mirror/raidz/raidz2 pool w/a hot spare device.
+# 2. Synchronise with device in the background.
+# 3. Set or unset autoreplace
+# 4. Unmount all filesystems and disable syseventd and fmd.
+# 5. Unload ZFS module and remove devices.
+# 6. Load ZFS module and verify device the device's status is 'UNAVAIL'.
+# 7. Verify no FMA fault was generated.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+log_assert "If a vdev is missing when a pool is imported, its status will be " \
+ "UNAVAIL"
+
+for type in "mirror" "raidz" "raidz2"; do
+ setup_testenv $type
+
+ log_must $ZPOOL export $TESTPOOL
+
+ # Random remove one of devices
+ log_must destroy_gnop $DISK0
+
+ # reimport the pool
+ log_must $ZPOOL import $TESTPOOL
+
+ log_must check_state $TESTPOOL $DISK0.nop 'UNAVAIL'
+
+ log_must create_gnop $DISK0
+ cleanup_testenv $TESTPOOL
+done
+
+log_pass "If a vdev is missing when a pool is imported, its status will be " \
+ "UNAVAIL"
diff --git a/tests/sys/cddl/zfs/tests/hotplug/hotplug_test.sh b/tests/sys/cddl/zfs/tests/hotplug/hotplug_test.sh
new file mode 100755
index 000000000000..7b5a80ed0bb4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotplug/hotplug_test.sh
@@ -0,0 +1,109 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case hotplug_001_pos cleanup
+hotplug_001_pos_head()
+{
+ atf_set "descr" "When removing a device from a redundant pool, the device'sstate will be indicated as 'REMOVED'."
+ atf_set "require.progs" "ksh93 zpool gnop"
+}
+hotplug_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotplug.kshlib
+ . $(atf_get_srcdir)/hotplug.cfg
+
+ verify_disk_count "$DISKS" 4
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotplug_001_pos.ksh || atf_fail "Testcase failed"
+}
+hotplug_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotplug.kshlib
+ . $(atf_get_srcdir)/hotplug.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case hotplug_008_pos cleanup
+hotplug_008_pos_head()
+{
+ atf_set "descr" "When removing hotspare device, verify device status is 'REMOVED'."
+ atf_set "require.progs" "ksh93 zpool gnop"
+}
+hotplug_008_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotplug.kshlib
+ . $(atf_get_srcdir)/hotplug.cfg
+
+ verify_disk_count "$DISKS" 4
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotplug_008_pos.ksh || atf_fail "Testcase failed"
+}
+hotplug_008_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotplug.kshlib
+ . $(atf_get_srcdir)/hotplug.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case hotplug_011_pos cleanup
+hotplug_011_pos_head()
+{
+ atf_set "descr" "Removing device offlined, verify device status is UNAVAIL,when the system is onlined."
+ atf_set "require.progs" "ksh93 zpool gnop"
+}
+hotplug_011_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotplug.kshlib
+ . $(atf_get_srcdir)/hotplug.cfg
+
+ verify_disk_count "$DISKS" 4
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotplug_011_pos.ksh || atf_fail "Testcase failed"
+}
+hotplug_011_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotplug.kshlib
+ . $(atf_get_srcdir)/hotplug.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case hotplug_001_pos
+ atf_add_test_case hotplug_008_pos
+ atf_add_test_case hotplug_011_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/hotplug/setup.ksh b/tests/sys/cddl/zfs/tests/hotplug/setup.ksh
new file mode 100644
index 000000000000..f9259515161f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotplug/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/hotplug/hotplug.kshlib
+
+log_must destroy_gnops $ALL_DISKS
+log_must create_gnops $ALL_DISKS
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/hotspare/Makefile b/tests/sys/cddl/zfs/tests/hotspare/Makefile
new file mode 100644
index 000000000000..02a7aa3a1ac4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/Makefile
@@ -0,0 +1,43 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/hotspare
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= hotspare_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= hotspare_create_001_neg.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= hotspare_add_004_neg.ksh
+${PACKAGE}FILES+= hotspare_scrub_002_pos.ksh
+${PACKAGE}FILES+= hotspare_detach_002_pos.ksh
+${PACKAGE}FILES+= hotspare_remove_001_pos.ksh
+${PACKAGE}FILES+= hotspare_add_001_pos.ksh
+${PACKAGE}FILES+= hotspare_import_001_pos.ksh
+${PACKAGE}FILES+= hotspare_replace_002_neg.ksh
+${PACKAGE}FILES+= hotspare_clone_002_pos.ksh
+${PACKAGE}FILES+= hotspare_export_001_neg.ksh
+${PACKAGE}FILES+= hotspare_snapshot_001_pos.ksh
+${PACKAGE}FILES+= hotspare_remove_004_pos.ksh
+${PACKAGE}FILES+= hotspare_detach_003_pos.ksh
+${PACKAGE}FILES+= hotspare_onoffline_004_neg.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= hotspare_replace_001_neg.ksh
+${PACKAGE}FILES+= hotspare_scrub_001_pos.ksh
+${PACKAGE}FILES+= hotspare_detach_001_pos.ksh
+${PACKAGE}FILES+= hotspare_add_003_neg.ksh
+${PACKAGE}FILES+= hotspare_remove_003_neg.ksh
+${PACKAGE}FILES+= hotspare_add_002_pos.ksh
+${PACKAGE}FILES+= hotspare_detach_004_pos.ksh
+${PACKAGE}FILES+= hotspare_snapshot_002_pos.ksh
+${PACKAGE}FILES+= hotspare.kshlib
+${PACKAGE}FILES+= hotspare_detach_005_neg.ksh
+${PACKAGE}FILES+= hotspare_remove_002_neg.ksh
+${PACKAGE}FILES+= hotspare_onoffline_003_neg.ksh
+${PACKAGE}FILES+= hotspare_shared_001_pos.ksh
+${PACKAGE}FILES+= hotspare.cfg
+${PACKAGE}FILES+= hotspare_clone_001_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/hotspare/cleanup.ksh b/tests/sys/cddl/zfs/tests/hotspare/cleanup.ksh
new file mode 100644
index 000000000000..8a650edb581b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/cleanup.ksh
@@ -0,0 +1,36 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+verify_runnable "global"
+
+log_must cleanup_devices_all
+
+restart_zfsd
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare.cfg b/tests/sys/cddl/zfs/tests/hotspare/hotspare.cfg
new file mode 100644
index 000000000000..d5009993f3bd
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare.cfg
@@ -0,0 +1,55 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
+. $STF_SUITE/include/libtest.kshlib
+
+export DISK_ARRAY_NUM=0
+export DISKSARRAY=""
+
+#
+# Variables for hotspare_detach_001
+#
+export STF_TIMEOUT=3600
+
+set_disks
+
+export FILESIZE="100m"
+export FILESIZE1="150m"
+export SIZE="100m"
+export SIZE1="80m"
+export N_DEVARRAY_FILES=8
+
+export HOLES_FILESIZE=${HOLES_FILESIZE-"67108864"} # 64 Mb
+export HOLES_BLKSIZE=${HOLES_BLKSIZE-"512"}
+export HOLES_SEED=${HOLES_SEED-""}
+export HOLES_FILEOFFSET=${HOLES_FILEOFFSET-""}
+export HOLES_COUNT=${HOLES_COUNT-"16384"} # FILESIZE/BLKSIZE/8
+export HOLES_VERBOSE=${HOLES_VERBOSE-""}
+
+export MYTESTFILE=$STF_SUITE/include/default.cfg
+export TESTFILE=testfile.${TESTCASE_ID}
+export HOTSPARE_TMPDIR="$TMPDIR/hotspare_tmpdir"
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare.kshlib b/tests/sys/cddl/zfs/tests/hotspare/hotspare.kshlib
new file mode 100644
index 000000000000..53098abb6837
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare.kshlib
@@ -0,0 +1,121 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zpool_add/zpool_add.kshlib
+
+
+function cleanup_devices_all
+{
+ $RM -f ${devarray[*]}
+ [ -d "$HOTSPARE_TMPDIR" ] && rmdir $HOTSPARE_TMPDIR
+
+ return 0
+}
+
+typeset -a pooldevs
+typeset -a sparedevs
+typeset -a logdevs
+typeset -a keywords=("" "mirror" "raidz" "raidz2")
+typeset -a devarray
+
+function set_devs
+{
+ mkdir $HOTSPARE_TMPDIR
+ typeset -i i=0
+ while (( i < $N_DEVARRAY_FILES )) ; do
+ eval devarray[$i]=$HOTSPARE_TMPDIR/file.$i
+ log_must create_vdevs ${devarray[$i]}
+ (( i = i + 1 ))
+ done
+
+ sparedevs=("${devarray[0]}" "${devarray[1]}")
+
+ pooldevs=("${devarray[3]}" "${devarray[4]}" "${devarray[5]}")
+
+ logdevs="${devarray[7]}"
+
+}
+
+function partition_cleanup
+{
+ cleanup_devices_all
+ return 0
+}
+
+#
+# $1: keyword, should be "" "mirror" "raidz" "raidz2"
+# $2: hotspare list, default as $sparedevs
+#
+function setup_hotspares # keyword, spares
+{
+ typeset keyword=$1
+ shift
+ typeset spares=${@:-${sparedevs[@]}}
+
+ create_pool "$TESTPOOL" "$keyword" \
+ ${pooldevs[@]}
+ log_must poolexists "$TESTPOOL"
+ log_must $ZPOOL set autoreplace=on "$TESTPOOL"
+ log_must $ZPOOL add -f "$TESTPOOL" spare $spares
+ log_must iscontained "$TESTPOOL" "$spares"
+
+ if [[ -n ${logdevs[@]} ]] ; then
+ log_must $ZPOOL add -f "$TESTPOOL" log ${logdevs[@]}
+ log_must iscontained "$TESTPOOL" "${logdevs[@]}"
+ fi
+}
+
+#
+# $1: the function name that run for all hotspares
+# $2: hotspare list, default as $sparedevs
+#
+function iterate_over_hotspares # function, spares
+{
+ typeset function=$1
+ typeset spares=${2:-${sparedevs[@]}}
+
+ for spare in $spares
+ do
+ $function $spare
+ done
+}
+
+wait_until_resilvered() {
+typeset -i i=0
+typeset -i timeout=60
+while [[ $i -lt $timeout ]]; do
+ if is_pool_resilvered $TESTPOOL; then
+ break
+ fi
+ (( i += 1 ))
+ if [[ $i == $timeout ]]; then
+ $ZPOOL status $TESTPOOL
+ log_fail "Pool didn't resilver in ${timeout} seconds"
+ fi
+ $SLEEP 1
+done
+}
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_add_001_pos.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_add_001_pos.ksh
new file mode 100644
index 000000000000..5a910779bd84
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_add_001_pos.ksh
@@ -0,0 +1,85 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotspare_add_001_pos
+#
+# DESCRIPTION:
+# 'zpool add <pool> spare <vdev> ...' can successfully add the specified
+# devices to the hot spares list of the given pool
+#
+# STRATEGY:
+# 1. Create a storage pool
+# 2. Add hot spare devices to the pool
+# 3. Verify the devices are added to the spare list
+# of the given pool successfully
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2006-06-07)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TESTPOOL && \
+ destroy_pool $TESTPOOL
+
+ partition_cleanup
+}
+
+function verify_assertion # dev
+{
+ typeset dev=$1
+
+ log_must check_hotspare_state "$TESTPOOL" "$dev" "AVAIL"
+}
+
+log_assert "'zpool add <pool> spare <vdev> ...' can add devices to the pool."
+
+log_onexit cleanup
+
+set_devs
+
+for keyword in "${keywords[@]}" ; do
+ setup_hotspares "$keyword"
+
+ iterate_over_hotspares verify_assertion
+
+ destroy_pool "$TESTPOOL"
+done
+
+log_pass "'zpool add <pool> spare <vdev> ...' executes successfully"
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_add_002_pos.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_add_002_pos.ksh
new file mode 100644
index 000000000000..57400b7b6403
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_add_002_pos.ksh
@@ -0,0 +1,101 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotspare_add_002_pos
+#
+# DESCRIPTION:
+# 'zpool add <pool> spare <vdev> ...' can successfully add the specified
+# devices to the available list of the given pool while
+# there has activated hotspare already.
+#
+# STRATEGY:
+# 1. Create a storage pool
+# 2. Add hot spare devices to the pool
+# 3. Activate some of the hot spares.
+# 3. Verify the following devices could add to the spare list
+# of the given pool successfully
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2006-06-07)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TESTPOOL && \
+ destroy_pool $TESTPOOL
+
+ partition_cleanup
+}
+
+function verify_assertion # dev
+{
+ typeset dev=$1
+
+ log_must check_hotspare_state "$TESTPOOL" "$dev" "AVAIL"
+ log_must $ZPOOL replace $TESTPOOL ${pooldevs[0]} $dev
+
+ cleanup_devices $ndev
+
+ log_must $ZPOOL add "$TESTPOOL" spare $ndev
+ log_must $ZPOOL remove "$TESTPOOL" $ndev
+
+ cleanup_devices $ndev
+
+ log_must $ZPOOL add -f "$TESTPOOL" spare $ndev
+ log_must $ZPOOL remove "$TESTPOOL" $ndev
+
+ log_must $ZPOOL detach $TESTPOOL $dev
+}
+
+log_assert "'zpool add <pool> spare <vdev> ...' can add devices to the pool while it has spare-in device."
+
+log_onexit cleanup
+
+set_devs
+typeset ndev=${devarray[2]}
+
+for keyword in "${keywords[@]}" ; do
+ setup_hotspares "$keyword"
+
+ iterate_over_hotspares verify_assertion
+
+ destroy_pool "$TESTPOOL"
+done
+
+log_pass "'zpool add <pool> spare <vdev> ...' executes successfully while it has spare-in device"
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_add_003_neg.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_add_003_neg.ksh
new file mode 100644
index 000000000000..b417035a98bc
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_add_003_neg.ksh
@@ -0,0 +1,137 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotspare_add_003_neg
+#
+# DESCRIPTION:
+# 'zpool add' with hot spares will fail
+# while the hot spares belong to the following cases:
+# - nonexistent device,
+# - part of an active pool,
+# - currently mounted,
+# - a swap device,
+# - a dump device,
+# - identical with the basic or spares vdev within the pool,
+# - belong to a exported or potentially active ZFS pool,
+# - a volume device that belong to the given pool,
+#
+# STRATEGY:
+# 1. Create case scenarios
+# 2. For each scenario, try to add [-f] the device to the pool
+# 3. Verify the add operation failes as expected.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-07)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists "$TESTPOOL" && \
+ destroy_pool "$TESTPOOL"
+ poolexists "$TESTPOOL1" && \
+ destroy_pool "$TESTPOOL1"
+
+ log_onfail $UMOUNT $TMPDIR/mounted_dir
+ log_onfail $SWAPOFF $swap_dev
+ log_onfail $DUMPON -r $dump_dev
+
+ partition_cleanup
+}
+
+log_assert "'zpool add [-f]' with hot spares should fail with inapplicable scenarios."
+
+log_onexit cleanup
+
+set_devs
+
+mounted_dev=${DISK0}
+swap_dev=${DISK1}
+dump_dev=${DISK2}
+nonexist_dev=${DISK2}bad_slice_num
+
+create_pool "$TESTPOOL" "${pooldevs[0]}"
+log_must poolexists "$TESTPOOL"
+
+create_pool "$TESTPOOL1" "${pooldevs[1]}"
+log_must poolexists "$TESTPOOL1"
+
+log_must $MKDIR $TMPDIR/mounted_dir
+log_must $NEWFS $mounted_dev
+log_must $MOUNT $mounted_dev $TMPDIR/mounted_dir
+
+log_must $SWAPON $swap_dev
+
+log_must $DUMPON $dump_dev
+
+# - nonexistent device,
+# - part of an active pool,
+# - currently mounted,
+# - a swap device,
+# - identical with the basic or spares vdev within the pool,
+
+set -A arg "$nonexist_dev" \
+ "${pooldevs[0]}" \
+ "${pooldevs[1]}" \
+ "$mounted_dev" \
+ "$swap_dev"
+
+typeset -i i=0
+while (( i < ${#arg[*]} )); do
+ if [[ -n "${arg[i]}" ]]; then
+ log_mustnot $ZPOOL add $TESTPOOL spare ${arg[i]}
+ log_mustnot $ZPOOL add -f $TESTPOOL spare ${arg[i]}
+ fi
+ (( i = i + 1 ))
+done
+
+# - a dump device,
+# https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=241070
+# When that bug is fixed, add $dump_dev to $arg and remove this block.
+log_must $ZPOOL add $TESTPOOL spare ${dump_dev}
+log_must $ZPOOL remove $TESTPOOL ${dump_dev}
+log_must $ZPOOL add -f $TESTPOOL spare ${dump_dev}
+log_must $ZPOOL remove $TESTPOOL ${dump_dev}
+
+# - belong to a exported or potentially active ZFS pool,
+
+log_must $ZPOOL export $TESTPOOL1
+log_mustnot $ZPOOL add "$TESTPOOL" spare ${pooldevs[1]}
+log_must $ZPOOL import -d $HOTSPARE_TMPDIR $TESTPOOL1
+
+log_pass "'zpool add [-f]' with hot spares should fail with inapplicable scenarios."
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_add_004_neg.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_add_004_neg.ksh
new file mode 100644
index 000000000000..2ea8501b5571
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_add_004_neg.ksh
@@ -0,0 +1,90 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# Copyright 2013 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotspare_add_004_neg
+#
+# DESCRIPTION:
+# 'zpool add' will not allow a swap device to be used as a hotspare
+#
+# STRATEGY:
+# 1. Create pools
+# 2. Create a swap device
+# 3. Try to add [-f] the swap device to the pool
+# 4. Verify the add operation failes as expected.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2013-02-15)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists "$TESTPOOL" && \
+ destroy_pool "$TESTPOOL"
+
+ if $SWAPCTL -l | grep -q $SWAPDEV; then
+ log_must $SWAPOFF $SWAPDEV
+ fi
+
+ partition_cleanup
+}
+
+log_assert "'zpool add [-f]' will not allow a swap device to be used as a hotspare'"
+
+log_onexit cleanup
+
+set_devs
+
+SWAPDEV="$DISK1"
+
+if $SWAPON $SWAPDEV; then
+ true
+else
+ log_unsupported "Cannot activate $SWAPDEV as a swap device"
+fi
+
+create_pool "$TESTPOOL" "$DISK0"
+log_must poolexists "$TESTPOOL"
+
+log_mustnot "$ZPOOL" add "$TESTPOOL" spare "$SWAPDEV"
+log_mustnot "$ZPOOL" add -f "$TESTPOOL" spare "$SWAPDEV"
+
+log_pass "'zpool add [-f]' will not allow a swap device to be used as a hotspare'"
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_clone_001_pos.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_clone_001_pos.ksh
new file mode 100644
index 000000000000..0d9be3d63514
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_clone_001_pos.ksh
@@ -0,0 +1,123 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotspare_clone_001_pos
+#
+# DESCRIPTION:
+# If a storage pool has activated hot spares,
+# create clone and remove the hot spare,
+# the data in clone should keep integrity.
+#
+# STRATEGY:
+# 1. Create a storage pool with hot spares
+# 2. Create some files, create a snapshot & clone upon filesystem
+# 3. Activate a spare device to the pool
+# 4. Create some files, create an new snapshot & clone upon filesystem
+# 5. Do 'zpool detach' with the spare in device
+# 6. Verify the 2 clones are all kept, and verify the data integrity within them.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2006-06-07)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TESTPOOL && \
+ destroy_pool $TESTPOOL
+
+ partition_cleanup
+}
+
+function verify_assertion # dev
+{
+ typeset dev=$1
+ typeset odev=${pooldevs[0]}
+
+ log_must $CP $MYTESTFILE $mtpt/$TESTFILE0
+ log_must $ZFS snapshot $TESTPOOL@snap.0
+ log_must $ZFS clone $TESTPOOL@snap.0 \
+ $TESTPOOL/clone.0
+
+ log_must $ZPOOL replace $TESTPOOL $odev $dev
+
+ log_must $CP $MYTESTFILE $mtpt/$TESTFILE1
+ log_must $ZFS snapshot $TESTPOOL@snap.1
+ log_must $ZFS clone $TESTPOOL@snap.1 \
+ $TESTPOOL/clone.1
+
+ log_must $SYNC
+
+ log_must $ZPOOL detach $TESTPOOL $dev
+
+ for file in "$mtpt/clone.0/$TESTFILE0" \
+ "$mtpt/clone.1/$TESTFILE1" ; do
+ [[ ! -e $file ]] && \
+ log_fail "$file missing after detach hotspare."
+ checksum2=$($SUM $file | $AWK '{print $1}')
+ [[ "$checksum1" != "$checksum2" ]] && \
+ log_fail "Checksums differ ($checksum1 != $checksum2"
+ done
+
+ log_must $RM -rf $mtpt/clone.0/*
+ log_must $RM -rf $mtpt/clone.1/*
+ log_must $ZFS destroy -Rf $TESTPOOL@snap.1
+ log_must $ZFS destroy -Rf $TESTPOOL@snap.0
+}
+
+log_assert "'zpool detach <pool> <vdev> ...' against hotspare should do no harm to clone."
+
+log_onexit cleanup
+
+typeset mtpt=""
+
+set_devs
+
+checksum1=$($SUM $MYTESTFILE | $AWK '{print $1}')
+
+for keyword in "${keywords[@]}" ; do
+ setup_hotspares "$keyword"
+
+ mtpt=$(get_prop mountpoint $TESTPOOL)
+
+ iterate_over_hotspares verify_assertion
+
+ destroy_pool "$TESTPOOL"
+done
+
+log_pass "'zpool detach <pool> <vdev> ...' against hotspare should do no harm to clone."
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_clone_002_pos.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_clone_002_pos.ksh
new file mode 100644
index 000000000000..d20ea1747f05
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_clone_002_pos.ksh
@@ -0,0 +1,131 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotspare_clone_002_pos
+#
+# DESCRIPTION:
+# If a storage pool has activated hot spares,
+# create clone and then invoke "zpool detach" with the original device,
+# the data in clone should keep integrity.
+#
+# STRATEGY:
+# 1. Create a storage pool with hot spares activated.
+# 2. Create some files, create a snapshot & clone upon filesystem
+# 3. Activate a spare device to the pool
+# 4. Create some files, create an new snapshot & clone upon filesystem
+# 5. Do 'zpool detach' with the original device
+# 6. Verify the 2 clones are all kept, and verify the data integrity within them.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2006-06-07)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TESTPOOL && \
+ destroy_pool $TESTPOOL
+
+ partition_cleanup
+}
+
+function verify_assertion # dev
+{
+ typeset dev=$1
+ typeset odev=${pooldevs[0]}
+
+ log_must $CP $MYTESTFILE $mtpt/$TESTFILE0
+ log_must $ZFS snapshot $TESTPOOL@snap.0
+ log_must $ZFS clone $TESTPOOL@snap.0 \
+ $TESTPOOL/clone.0
+
+ log_must $ZPOOL replace $TESTPOOL $odev $dev
+
+ log_must $CP $MYTESTFILE $mtpt/$TESTFILE1
+ log_must $ZFS snapshot $TESTPOOL@snap.1
+ log_must $ZFS clone $TESTPOOL@snap.1 \
+ $TESTPOOL/clone.1
+
+ log_must $SYNC
+
+ log_must $ZPOOL detach $TESTPOOL $odev
+
+ for file in "$mtpt/clone.0/$TESTFILE0" \
+ "$mtpt/clone.1/$TESTFILE1" ; do
+ [[ ! -e $file ]] && \
+ log_fail "$file missing after detach hotspare."
+ checksum2=$($SUM $file | $AWK '{print $1}')
+ [[ "$checksum1" != "$checksum2" ]] && \
+ log_fail "Checksums differ ($checksum1 != $checksum2)"
+ done
+
+ log_must $RM -rf $mtpt/clone.0/*
+ log_must $RM -rf $mtpt/clone.1/*
+ log_must $ZFS destroy -Rf $TESTPOOL@snap.1
+ log_must $ZFS destroy -Rf $TESTPOOL@snap.0
+
+ log_must $RM -f $mtpt/*
+
+ log_must $ZPOOL add -f "$TESTPOOL" spare $odev
+ log_must $ZPOOL replace "$TESTPOOL" $dev $odev
+ log_must $SYNC
+ log_must $ZPOOL detach "$TESTPOOL" $dev
+ log_must $ZPOOL add -f "$TESTPOOL" spare $dev
+}
+
+log_assert "'zpool detach <pool> <vdev> ...' against basic vdev should do no harm to clone."
+
+log_onexit cleanup
+
+typeset mtpt=""
+
+set_devs
+
+checksum1=$($SUM $MYTESTFILE | $AWK '{print $1}')
+
+for keyword in "${keywords[@]}" ; do
+ setup_hotspares "$keyword"
+
+ mtpt=$(get_prop mountpoint $TESTPOOL)
+
+ iterate_over_hotspares verify_assertion
+
+ destroy_pool "$TESTPOOL"
+done
+
+log_pass "'zpool detach <pool> <vdev> ...' against basic vdev should do no harm to clone."
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_create_001_neg.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_create_001_neg.ksh
new file mode 100644
index 000000000000..a39b258bdc18
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_create_001_neg.ksh
@@ -0,0 +1,132 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotspare_create_001_neg
+#
+# DESCRIPTION:
+# 'zpool create [-f]' with hot spares will fail
+# while the hot spares belong to the following cases:
+# - existing pool
+# - nonexistent device,
+# - part of an active pool,
+# - currently mounted,
+# - a swap device,
+# - a dump device,
+# - identical with the basic vdev within the pool,
+#
+# STRATEGY:
+# 1. Create case scenarios
+# 2. For each scenario, try to create a new pool with hot spares
+# of the virtual devices
+# 3. Verify the creation is failed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-07)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ for pool in $TESTPOOL $TESTPOOL1
+ do
+ destroy_pool $pool
+ done
+
+ log_onfail $UMOUNT $TMPDIR/mounted_dir
+ log_onfail $SWAPOFF $swap_dev
+ log_onfail $DUMPON -r $dump_dev
+
+ partition_cleanup
+}
+
+log_assert "'zpool create [-f]' with hot spares should be failed " \
+ "with inapplicable scenarios."
+log_onexit cleanup
+
+set_devs
+
+mounted_dev=${DISK0}
+swap_dev=${DISK1}
+dump_dev=${DISK2}
+nonexist_dev=${disk}sbad_slice_num
+
+create_pool "$TESTPOOL" ${pooldevs[0]}
+
+log_must $MKDIR $TMPDIR/mounted_dir
+log_must $NEWFS $mounted_dev
+log_must $MOUNT $mounted_dev $TMPDIR/mounted_dir
+
+log_must $SWAPON $swap_dev
+
+log_must $DUMPON $dump_dev
+
+#
+# Set up the testing scenarios parameters
+# - existing pool
+# - nonexistent device,
+# - part of an active pool,
+# - currently mounted,
+# - a swap device,
+# - identical with the basic vdev within the pool,
+
+set -A arg "$TESTPOOL ${pooldevs[1]} spare ${pooldevs[2]}" \
+ "$TESTPOOL1 ${pooldevs[1]} spare $nonexist_dev" \
+ "$TESTPOOL1 ${pooldevs[1]} spare ${pooldevs[0]}" \
+ "$TESTPOOL1 ${pooldevs[1]} spare $mounted_dev" \
+ "$TESTPOOL1 ${pooldevs[1]} spare $swap_dev" \
+ "$TESTPOOL1 ${pooldevs[1]} spare ${pooldevs[1]}"
+
+typeset -i i=0
+while (( i < ${#arg[*]} )); do
+ log_mustnot $ZPOOL create ${arg[i]}
+ log_mustnot $ZPOOL create -f ${arg[i]}
+ (( i = i + 1 ))
+done
+
+# - a dump device,
+# https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=241070
+# When that bug is fixed, add $dump_dev to $arg and remove this block.
+log_must $ZPOOL create $TESTPOOL1 ${pooldevs[1]} spare $dump_dev
+log_must $ZPOOL destroy -f $TESTPOOL1
+log_must $ZPOOL create -f $TESTPOOL1 ${pooldevs[1]} spare $dump_dev
+log_must $ZPOOL destroy -f $TESTPOOL1
+
+# now destroy the pool to be polite
+log_must $ZPOOL destroy -f $TESTPOOL
+
+log_pass "'zpool create [-f]' with hot spare is failed as expected with inapplicable scenarios."
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_detach_001_pos.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_detach_001_pos.ksh
new file mode 100644
index 000000000000..31d7bb8dcc0b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_detach_001_pos.ksh
@@ -0,0 +1,99 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotspare_detach_001_pos
+#
+# DESCRIPTION:
+# If a hot spare have been activated,
+# and invoke "zpool detach" with this hot spare,
+# it will be returned to the set of available spares,
+# the original drive will remain in its current position.
+#
+# STRATEGY:
+# 1. Create a storage pool with hot spares
+# 2. Activate a spare device to the pool
+# 3. Do 'zpool detach' with the spare in device
+# 4. Verify the spare device returned to the set of available spares,
+# and the original drive will remain in its current position.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2006-06-07)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TESTPOOL && \
+ destroy_pool $TESTPOOL
+
+ partition_cleanup
+}
+
+function verify_assertion # dev
+{
+ typeset dev=$1
+ typeset odev=${pooldevs[0]}
+
+ log_must $MKFILE 100m /$TESTPOOL/$TESTFILE1
+ log_must $SYNC
+ log_must $ZPOOL replace $TESTPOOL $odev $dev
+
+ log_must resilver_happened $TESTPOOL
+ log_must check_hotspare_state "$TESTPOOL" "$dev" "INUSE"
+
+ log_must $ZPOOL detach $TESTPOOL $dev
+ log_must check_hotspare_state "$TESTPOOL" "$dev" "AVAIL"
+ log_must $RM -f /$TESTPOOL/$TESTFILE1
+ log_must $SYNC
+}
+
+log_assert "'zpool detach <pool> <vdev> ...' should deactivate the spared-in hot spare device successfully."
+
+log_onexit cleanup
+
+set_devs
+
+for keyword in "${keywords[@]}" ; do
+ setup_hotspares "$keyword"
+
+ iterate_over_hotspares verify_assertion
+
+ destroy_pool "$TESTPOOL"
+done
+
+log_pass "'zpool detach <pool> <vdev> ...' deactivate the spared-in hot spare device successfully."
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_detach_002_pos.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_detach_002_pos.ksh
new file mode 100644
index 000000000000..52a572a38ce3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_detach_002_pos.ksh
@@ -0,0 +1,109 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotspare_detach_002_pos
+#
+# DESCRIPTION:
+# If a hot spare have been activated,
+# and invoke "zpool detach" with the original device,
+# then the hot spare will become a functioning device,
+# and automatically be removed from the list of available hot spares
+# then the spare is automatically removed once the replace completes.
+#
+# STRATEGY:
+# 1. Create a storage pool with hot spares
+# 2. Activate a spare device to the pool
+# 3. Do 'zpool detach' with the original device
+# 4. Verify the spare device will become a functioning device,
+# be removed from the list of available spares as well,
+# and the original drive will removed once replace completes.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2006-06-07)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TESTPOOL && \
+ destroy_pool $TESTPOOL
+
+ partition_cleanup
+}
+
+function verify_assertion # dev
+{
+ typeset dev=$1
+ typeset odev=${pooldevs[0]}
+
+ log_must $ZPOOL replace $TESTPOOL $odev $dev
+ while check_state "$TESTPOOL" "replacing" \
+ "online" || \
+ ! is_pool_resilvered $TESTPOOL ; do
+ $SLEEP 2
+ done
+ log_must check_hotspare_state "$TESTPOOL" "$dev" "INUSE"
+
+ log_must $ZPOOL detach $TESTPOOL $odev
+ $SLEEP 5
+ log_must $ZPOOL replace $TESTPOOL $dev $odev
+
+ while check_state "$TESTPOOL" "replacing" \
+ "online" ; do
+ $SLEEP 2
+ done
+
+ log_must $ZPOOL add -f $TESTPOOL spare $dev
+ log_must check_hotspare_state "$TESTPOOL" "$dev" "AVAIL"
+}
+
+log_assert "'zpool detach <pool> <vdev> ...' against a functioning device that have spared should take the hot spare permanently swapping in successfully."
+
+log_onexit cleanup
+
+set_devs
+
+for keyword in "${keywords[@]}" ; do
+ setup_hotspares "$keyword"
+
+ iterate_over_hotspares verify_assertion
+
+ destroy_pool "$TESTPOOL"
+done
+
+log_pass "'zpool detach <pool> <vdev> ...' against a functioning device that have spared take the hot spare permanently swapping in successfully."
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_detach_003_pos.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_detach_003_pos.ksh
new file mode 100644
index 000000000000..0bdf047f7168
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_detach_003_pos.ksh
@@ -0,0 +1,102 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotspare_detach_003_pos
+#
+# DESCRIPTION:
+# If a hot spare have been activated,
+# and invoke "zpool replace" to replace the original device,
+# then the spare is automatically removed once the replace completes
+#
+# STRATEGY:
+# 1. Create a storage pool with hot spares
+# 2. Activate a spare device to the pool
+# 3. Do 'zpool replace' with the original device
+# 4. Verify the original device will replace by the new device,
+# and the spare should return to available once replace completes.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2006-06-07)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TESTPOOL && \
+ destroy_pool $TESTPOOL
+
+ partition_cleanup
+}
+
+function verify_assertion # dev
+{
+ typeset dev=$1
+ typeset odev=${pooldevs[0]}
+
+ log_must $ZPOOL replace $TESTPOOL $odev $dev
+ log_must check_hotspare_state "$TESTPOOL" "$dev" "INUSE"
+ log_must $ZPOOL replace -f $TESTPOOL $odev $ndev
+
+ while check_state $TESTPOOL "replacing" \
+ "online"; do
+ $SLEEP 5
+ done
+
+ log_mustnot iscontained "$TESTPOOL" "$odev"
+ log_must iscontained "$TESTPOOL" "$ndev"
+ log_must check_hotspare_state "$TESTPOOL" "$dev" "AVAIL"
+ log_must $ZPOOL replace $TESTPOOL $ndev $odev
+ $SLEEP 5
+}
+
+log_assert "'zpool replace <pool> <vdev> <ndev>' against a functioning device that have spared should complete and the hot spare should return to available."
+
+log_onexit cleanup
+
+set_devs
+typeset ndev=${devarray[2]}
+
+for keyword in "${keywords[@]}" ; do
+ setup_hotspares "$keyword"
+
+ iterate_over_hotspares verify_assertion
+
+ destroy_pool "$TESTPOOL"
+done
+
+log_pass "'zpool replace <pool> <vdev> <ndev>' against a functioning device that have spared successful and the hot spare return to available."
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_detach_004_pos.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_detach_004_pos.ksh
new file mode 100644
index 000000000000..d3f6f7f1d56b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_detach_004_pos.ksh
@@ -0,0 +1,103 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotspare_detach_004_pos
+#
+# DESCRIPTION:
+# If a hot spare is activated,
+# and invoke "zpool replace" with this hot spare to another hot spare,
+# the operation should run successfully.
+#
+# STRATEGY:
+# 1. Create a storage pool with multiple hot spares
+# 2. Activate a hot spare by 'zpool replace' with the basic dev,
+# make sure there still have enough hot spare in available list.
+# 3. Do 'zpool replace' with the hot spare to another AVAIL hot spare.
+# 4. Verify the operation runs successfully.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2006-06-07)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ if poolexists $TESTPOOL; then
+ if [[ "$STF_EXITCODE" -eq "$STF_FAIL" ]]; then
+ $ECHO "Testcase failed; dumping pool status:"
+ $ZPOOL status $TESTPOOL
+ fi
+ destroy_pool $TESTPOOL
+ fi
+
+ partition_cleanup
+}
+
+function verify_assertion # dev
+{
+ typeset dev=$1
+ typeset odev=${pooldevs[0]}
+
+ log_must $ZPOOL replace $TESTPOOL $odev $dev
+ log_must check_hotspare_state "$TESTPOOL" "$dev" "INUSE"
+ wait_for_state_exit "$TESTPOOL" "$dev" "resilvering"
+
+ log_must $ZPOOL replace $TESTPOOL $dev $ndev
+ wait_for_state_exit "$TESTPOOL" "$dev" "online"
+
+ log_must check_hotspare_state "$TESTPOOL" "$dev" "AVAIL"
+ log_must check_hotspare_state "$TESTPOOL" "$ndev" "INUSE"
+ log_must $ZPOOL detach $TESTPOOL $ndev
+}
+
+log_assert "'zpool replace <pool> <vdev> <ndev>' against a hot spare device that have been activated should successful while the another dev is a available hot spare."
+
+log_onexit cleanup
+
+set_devs
+typeset ndev=${devarray[2]}
+
+for keyword in "${keywords[@]}" ; do
+ setup_hotspares "$keyword" "${sparedevs[@]} $ndev"
+
+ iterate_over_hotspares verify_assertion
+
+ destroy_pool "$TESTPOOL"
+done
+
+log_pass "'zpool replace <pool> <vdev> <ndev>' against a hot spare device that have been activated should successful while the another dev is a available hot spare."
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_detach_005_neg.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_detach_005_neg.ksh
new file mode 100644
index 000000000000..521d332fba81
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_detach_005_neg.ksh
@@ -0,0 +1,88 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotspare_detach_005_neg
+#
+# DESCRIPTION:
+# If a hot spare is only in the list of available hot spares
+# but have NOT been activated,
+# invoke "zpool detach" with this hot spare will fail with
+# a return code of 255 and issue an error message.
+#
+# STRATEGY:
+# 1. Create a storage pool with hot spares
+# 2. Do 'zpool detach' with the hot spare device
+# 4. Verify the operation fail with return code of 255
+# and issue an error message.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2006-06-07)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TESTPOOL && \
+ destroy_pool $TESTPOOL
+
+ partition_cleanup
+}
+
+function verify_assertion # dev
+{
+ typeset dev=$1
+
+ log_mustnot $ZPOOL detach "$TESTPOOL" $dev
+ log_must check_hotspare_state "$TESTPOOL" "$dev" "AVAIL"
+}
+
+log_assert "'zpool detach <pool> <vdev>' against a hot spare device that NOT activated should fail and issue an error message."
+
+log_onexit cleanup
+
+set_devs
+
+for keyword in "${keywords[@]}" ; do
+ setup_hotspares "$keyword"
+
+ iterate_over_hotspares verify_assertion
+
+ destroy_pool "$TESTPOOL"
+done
+
+log_pass "'zpool detach <pool> <vdev>' against a hot spare device that NOT activated fail as expected."
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_export_001_neg.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_export_001_neg.ksh
new file mode 100644
index 000000000000..d2029a0a500e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_export_001_neg.ksh
@@ -0,0 +1,122 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotspare_export_001_neg
+#
+# DESCRIPTION:
+# If 2 storage pools have shared hotspares, if the shared hotspare was used by
+# one of the pool, the export of the pool that use hotspare will fail.
+#
+# STRATEGY:
+# 1. Create 2 storage pools with hot spares shared.
+# 2. Fail one vdev in one pool to make the hotspare in use.
+# 3. Export the pool that currently use the hotspare
+# 4. Verify the export will failed with warning message.
+# 5. Verify export -f will success.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2008-12-12)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+
+ if poolexists $TESTPOOL ; then
+ destroy_pool $TESTPOOL
+ else
+ $ZPOOL import -d $HOTSPARE_TMPDIR -f | $GREP \
+ "pool: $TESTPOOL">/dev/null 2>&1
+ if (( $? == 0 )); then
+ log_must $ZPOOL import -d $HOTSPARE_TMPDIR -f $TESTPOOL
+ destroy_pool $TESTPOOL
+ fi
+ fi
+
+ poolexists $TESTPOOL1 && \
+ destroy_pool $TESTPOOL1
+
+ partition_cleanup
+
+}
+
+
+log_onexit cleanup
+
+function verify_assertion # type, dev
+{
+ typeset pool_type=$1
+ typeset hotspare=$2
+
+ typeset err_dev=${devarray[3]}
+ typeset pool_dev="${devarray[6]}"
+ typeset mntp=$(get_prop mountpoint $TESTPOOL)
+
+ create_pool $TESTPOOL1 $pool_dev spare $hotspare
+
+ zpool replace $TESTPOOL $err_dev $hotspare
+ log_must check_hotspare_state "$TESTPOOL" "$hotspare" "INUSE"
+
+ log_must $ZPOOL status $TESTPOOL
+ log_must $ZPOOL status $TESTPOOL1
+
+ log_mustnot $ZPOOL export $TESTPOOL
+ log_must $ZPOOL export -f $TESTPOOL
+
+ log_must $ZPOOL import -d $HOTSPARE_TMPDIR -f $TESTPOOL
+ destroy_pool $TESTPOOL
+
+ destroy_pool $TESTPOOL1
+}
+
+log_onexit cleanup
+
+log_assert "export pool that using shared hotspares will fail"
+
+set_devs
+
+typeset share_spare="${devarray[0]}"
+set -A my_keywords "mirror" "raidz1" "raidz2"
+
+for keyword in "${my_keywords[@]}" ; do
+ setup_hotspares $keyword $share_spare
+ verify_assertion $keyword $share_spare
+done
+
+log_pass "export pool that using shared hotspares will fail"
+
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_import_001_pos.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_import_001_pos.ksh
new file mode 100644
index 000000000000..9b005ebfd1fc
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_import_001_pos.ksh
@@ -0,0 +1,162 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotspare_import_001_pos
+#
+# DESCRIPTION:
+# If a storage pool has hot spare,
+# regardless it has been activated or NOT,
+# invoke "zpool export" then import with this storage pool
+# should runs successfully, and the data should keep integrity
+# after import.
+#
+# STRATEGY:
+# 1. Create a storage pool with hot spares
+# 2. Do 'zpool export' then 'zpool import' with following scernarios
+# - the hotspare is only in available list
+# - the hotspare is activated
+# - the hotspare is activated but offline
+# - the hotspare is activated but the basic vdev is offline
+# 3. Verify the export/import runs successfully,
+# and the data keep integrity after import
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2006-06-14)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TESTPOOL && \
+ destroy_pool $TESTPOOL
+
+ partition_cleanup
+}
+
+function verify_export_import #pool #file #chksum
+{
+ typeset pool=$1
+ typeset file=$2
+ typeset checksum1=$3
+ typeset -i n=0
+
+ if ! $ZPOOL export $pool; then
+ # Rarely, this can fail with EBUSY if the pool's configuration
+ # has already changed within the same transaction group. In
+ # that case, it is appropriate to retry.
+ while ((n < 3)); do
+ $SYNC
+ log_note "$ZPOOL busy, retrying export (${n})..."
+ if ((n == 2)); then
+ log_must $ZPOOL export $pool
+ else
+ $ZPOOL export $pool && break
+ fi
+ $SLEEP 1
+ n=$((n + 1))
+ done
+ fi
+ log_must $ZPOOL import -d $HOTSPARE_TMPDIR $pool
+
+ [[ ! -e $file ]] && \
+ log_fail "$file missing after detach hotspare."
+ checksum2=$($SUM $file | $AWK '{print $1}')
+ [[ "$checksum1" != "$checksum2" ]] && \
+ log_fail "Checksums differ ($checksum1 != $checksum2)"
+
+ return 0
+}
+
+function verify_assertion # dev
+{
+ typeset dev=$1
+ typeset odev=${pooldevs[0]}
+
+ #
+ # - the hotspare is activated
+ #
+ log_must $ZPOOL replace $TESTPOOL $odev $dev
+ while ! is_pool_resilvered $TESTPOOL ; do
+ $SLEEP 2
+ done
+
+ verify_export_import $TESTPOOL \
+ $mtpt/$TESTFILE0 $checksum1
+
+ #
+ # - the hotspare is activated
+ # but the basic vdev is offline
+ #
+ log_must $ZPOOL offline $TESTPOOL $odev
+ verify_export_import $TESTPOOL \
+ $mtpt/$TESTFILE0 $checksum1
+
+ log_must $ZPOOL online $TESTPOOL $odev
+
+ log_must $ZPOOL detach $TESTPOOL $dev
+}
+
+log_assert "'zpool export/import <pool>' should runs successfully regardless the hotspare is only in list, activated, or offline."
+
+log_onexit cleanup
+
+typeset mtpt=""
+
+set_devs
+
+checksum1=$($SUM $MYTESTFILE | $AWK '{print $1}')
+
+for keyword in "${keywords[@]}" ; do
+ setup_hotspares "$keyword"
+
+ mtpt=$(get_prop mountpoint $TESTPOOL)
+ log_must $CP $MYTESTFILE $mtpt/$TESTFILE0
+
+ #
+ # - the hotspare is only in available list
+ #
+ verify_export_import $TESTPOOL \
+ $mtpt/$TESTFILE0 $checksum1
+
+ iterate_over_hotspares verify_assertion "${vdev%% *}"
+
+ log_must $RM -f $mtpt/$TESTFILE0
+ destroy_pool "$TESTPOOL"
+done
+
+log_pass "'zpool export/import <pool>' should runs successfully regardless the hotspare is only in list, activated, or offline."
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_onoffline_003_neg.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_onoffline_003_neg.ksh
new file mode 100644
index 000000000000..dd4e625164bd
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_onoffline_003_neg.ksh
@@ -0,0 +1,94 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotspare_onoffline_003_neg
+#
+# DESCRIPTION:
+# Regardless a hot spare is only in the available hot spare list,
+# or have been activated,
+# invoke "zpool offline" & "zpool online" with this hot spare
+# will fail with a return code of 1 and issue an error message.
+#
+# STRATEGY:
+# 1. Create a storage pool with hot spares
+# 2. Try 'zpool offline' & 'zpool online' with each hot spare
+# of following condition
+# - only in the list of available hot spares (fail)
+# - have been activated (fail)
+# 3. Verify offline/online results as expected.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2006-06-07)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TESTPOOL && \
+ destroy_pool $TESTPOOL
+
+ partition_cleanup
+}
+
+function verify_assertion # dev
+{
+ typeset dev=$1
+ typeset odev=${pooldevs[0]}
+
+ log_mustnot $ZPOOL offline $TESTPOOL $dev
+ log_must check_hotspare_state $TESTPOOL $dev "AVAIL"
+
+ log_mustnot $ZPOOL online $TESTPOOL $dev
+ log_must check_hotspare_state $TESTPOOL $dev "AVAIL"
+}
+
+log_assert "'zpool offline/online <pool> <vdev>' should fail on inactive spares"
+
+log_onexit cleanup
+
+set_devs
+
+for keyword in "${keywords[@]}" ; do
+ setup_hotspares "$keyword"
+
+ iterate_over_hotspares verify_assertion
+
+ destroy_pool "$TESTPOOL"
+done
+
+log_pass "'zpool offline/online <pool> <vdev>' against a hot spare works as expected."
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_onoffline_004_neg.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_onoffline_004_neg.ksh
new file mode 100644
index 000000000000..4c826158e099
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_onoffline_004_neg.ksh
@@ -0,0 +1,162 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotspare_onoffline_004_neg
+#
+# DESCRIPTION:
+# If a hot spare has been activated,
+# turning that basic vdev offline and back online during I/O completes.
+# Make sure the integrity of the file system and the resilvering.
+#
+# STRATEGY:
+# 1. Create a storage pool with hot spares
+# 2. Activate the hot spare
+# 3. Start some random I/O
+# 4. Try 'zpool offline' & 'zpool online' with the basic vdev
+# 5. Verify the integrity of the file system and the resilvering.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2006-06-07)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ kill_all_wp
+ poolexists $TESTPOOL && \
+ destroy_pool $TESTPOOL
+
+ [[ -e $TESTDIR ]] && log_must $RM -rf $TESTDIR/*
+
+ partition_cleanup
+}
+
+function kill_all_wp
+{
+ for wait_pid in $child_pids
+ do
+ $KILL $wait_pid
+ $WAIT $wait_pid
+ done
+}
+
+function start_all_wp
+{
+ typeset -i i=0
+ typeset -i iters=1
+
+ child_pids=""
+ while (( i < iters )); do
+ log_note "Invoking $FILE_TRUNC with: $options_display"
+ $FILE_TRUNC $options $TESTDIR/$TESTFILE.$i &
+ typeset pid=$!
+
+ child_pids="$child_pids $pid"
+ ((i = i + 1))
+ done
+}
+
+function verify_assertion # dev
+{
+ typeset dev=$1
+ typeset -i i=0
+ typeset -i iters=1
+ typeset odev=${pooldevs[0]}
+
+ log_must $ZPOOL replace $TESTPOOL $odev $dev
+
+ i=0
+ while (( i < iters )); do
+ start_all_wp
+ while true; do
+ if is_pool_resilvered "$TESTPOOL"; then
+ [ -s "$TESTDIR/$TESTFILE.$i" ] && break
+ fi
+ $SLEEP 2
+ done
+
+ kill_all_wp
+ log_must test -s $TESTDIR/$TESTFILE.$i
+
+ log_must $ZPOOL offline $TESTPOOL $odev
+ log_must check_state $TESTPOOL $odev "offline"
+
+ log_must $ZPOOL online $TESTPOOL $odev
+ log_must check_state $TESTPOOL $odev "online"
+ (( i = i + 1 ))
+ done
+
+ log_must $ZPOOL detach $TESTPOOL $dev
+}
+
+log_assert "'zpool offline/online <pool> <vdev>' against a spared basic vdev during I/O completes."
+
+log_onexit cleanup
+
+set_devs
+
+options=""
+options_display="default options"
+
+[[ -n "$HOLES_FILESIZE" ]] && options=" $options -f $HOLES_FILESIZE "
+
+[[ -n "$HOLES_BLKSIZE" ]] && options="$options -b $HOLES_BLKSIZE "
+
+[[ -n "$HOLES_COUNT" ]] && options="$options -c $HOLES_COUNT "
+
+[[ -n "$HOLES_SEED" ]] && options="$options -s $HOLES_SEED "
+
+[[ -n "$HOLES_FILEOFFSET" ]] && options="$options -o $HOLES_FILEOFFSET "
+
+options="$options -r"
+
+[[ -n "$options" ]] && options_display=$options
+
+typeset child_pid=""
+
+for keyword in "${keywords[@]}" ; do
+ setup_hotspares "$keyword"
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL
+
+ iterate_over_hotspares verify_assertion
+
+ verify_filesys "$TESTPOOL" "$TESTPOOL" "$HOTSPARE_TMPDIR"
+ destroy_pool "$TESTPOOL"
+done
+
+log_pass "'zpool offline/online <pool> <vdev>' against a spared basic vdev during I/O completes."
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_remove_001_pos.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_remove_001_pos.ksh
new file mode 100644
index 000000000000..c644e9272496
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_remove_001_pos.ksh
@@ -0,0 +1,87 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotspare_remove_001_pos
+#
+# DESCRIPTION:
+# 'zpool remove <pool> <vdev> ...' can successfully remove the specified
+# devices from the hot spares list of the given pool
+#
+# STRATEGY:
+# 1. Create a storage pool
+# 2. Add hot spare devices to the pool
+# 3. Remove hot spares one by one
+# 3. Verify the devices are removed fromo the spare list
+# of the given pool successfully
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2006-06-07)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TESTPOOL && \
+ destroy_pool $TESTPOOL
+
+ partition_cleanup
+}
+
+function verify_assertion # dev
+{
+ typeset dev=$1
+
+ log_must $ZPOOL remove $TESTPOOL $dev
+ log_mustnot iscontained "$TESTPOOL" $dev
+}
+
+log_assert "'zpool remove <pool> <vdev> ...' can remove spare device from the pool."
+
+log_onexit cleanup
+
+set_devs
+
+for keyword in "${keywords[@]}" ; do
+ setup_hotspares "$keyword"
+
+ iterate_over_hotspares verify_assertion
+
+ destroy_pool "$TESTPOOL"
+done
+
+log_pass "'zpool remove <pool> <vdev> ...' executes successfully"
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_remove_002_neg.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_remove_002_neg.ksh
new file mode 100644
index 000000000000..d01eff8eebf1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_remove_002_neg.ksh
@@ -0,0 +1,111 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotspare_remove_002_neg
+#
+# DESCRIPTION:
+# 'zpool remove <pool> <vdev> ...' should return fail if
+# - notexist device
+# - not within the hot spares of this pool
+# - hot spares that currently spared in
+#
+# STRATEGY:
+# 1. Create a storage pool
+# 2. Add hot spare devices to the pool
+# 3. For each scenario, try to remove the hot spares
+# 4. Verify the the remove operation get failed
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2005-09-27)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TESTPOOL && \
+ destroy_pool $TESTPOOL
+
+ partition_cleanup
+}
+
+function verify_assertion # dev
+{
+ typeset dev=$1
+ typeset odev=${pooldevs[0]}
+
+ log_must $ZPOOL replace $TESTPOOL $odev $dev
+ log_mustnot $ZPOOL remove $TESTPOOL $dev
+ log_must $ZPOOL detach $TESTPOOL $dev
+}
+
+log_assert "'zpool remove <pool> <vdev> ...' should fail with inapplicable scenarios."
+
+log_onexit cleanup
+
+typeset dev_nonexist dev_notinlist
+
+case $DISK_ARRAY_NUM in
+0|1)
+ dev_nonexist=/dev/${disk}sbad_slice_num
+ dev_notinlist=${disk}
+ ;;
+2|*)
+ dev_nonexist=/dev/${DISK0}sbad_slice_num
+ dev_notinlist="${DISK0} ${DISK1}"
+ ;;
+esac
+
+set_devs
+
+for keyword in "${keywords[@]}" ; do
+ setup_hotspares "$keyword"
+
+ for dev in $dev_nonexist ; do
+ log_mustnot $ZPOOL remove $TESTPOOL $dev
+ done
+
+ for dev in $dev_notinlist ; do
+ log_mustnot $ZPOOL remove $TESTPOOL $dev
+ done
+
+ iterate_over_hotspares verify_assertion
+
+ destroy_pool "$TESTPOOL"
+done
+
+log_pass "'zpool remove <pool> <vdev> ...' fail with inapplicable scenarios."
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_remove_003_neg.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_remove_003_neg.ksh
new file mode 100644
index 000000000000..244c3ce6421f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_remove_003_neg.ksh
@@ -0,0 +1,81 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotspare_remove_003_neg
+#
+# DESCRIPTION:
+# Executing 'zpool remove' command with bad options fails.
+#
+# STRATEGY:
+# 1. Create an array of badly formed 'zpool remove' options.
+# 2. Execute each element of the array.
+# 3. Verify an error code is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-19)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TESTPOOL && \
+ destroy_pool $TESTPOOL
+
+ partition_cleanup
+}
+
+
+set -A args "" "-?" "-t fakepool" "-f fakepool" "-ev fakepool" "fakepool" \
+ "$TESTPOOL" "fakepool ${devarray[0]}" "fakepool ${devarray[1]}"
+
+log_assert "Executing 'zpool remove' with bad options fails"
+log_onexit cleanup
+
+set_devs
+setup_hotspares
+
+typeset -i i=0
+
+while [[ $i -lt ${#args[*]} ]]; do
+
+ log_mustnot $ZPOOL remove ${args[$i]}
+
+ (( i = i + 1 ))
+done
+
+log_pass "'zpool remove' command with bad options failed as expected."
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_remove_004_pos.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_remove_004_pos.ksh
new file mode 100644
index 000000000000..4c697dcea0ca
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_remove_004_pos.ksh
@@ -0,0 +1,94 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotspare_remove_004_pos
+#
+# DESCRIPTION:
+# 'zpool remove <pool> <vdev> ...' can successfully remove the specified
+# devices from the hot spares even it no longer exists.
+#
+# STRATEGY:
+# 1. Create a storage pool
+# 2. Add hot spare devices to the pool
+# 3. Export the pool
+# 4. Remove the hotspare
+# 5. Import the pool
+# 6. Remove hot spares one by one
+# 7. Verify the devices are removed from the spare list
+# of the given pool successfully
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2008-02-25)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TESTPOOL && \
+ destroy_pool $TESTPOOL
+
+ partition_cleanup
+}
+
+function verify_assertion # dev
+{
+ typeset dev=$1
+
+ log_must $ZPOOL export $TESTPOOL
+ log_must $MV $dev $dev.bak
+ log_must $ZPOOL import -d $HOTSPARE_TMPDIR $TESTPOOL
+ log_must $ZPOOL remove $TESTPOOL $dev
+ log_mustnot iscontained "$TESTPOOL" $dev
+ log_must $MV $dev.bak $dev
+}
+
+log_assert "'zpool remove <pool> <vdev> ...' can remove spare device from the pool."
+
+log_onexit cleanup
+
+set_devs
+
+for keyword in "${keywords[@]}" ; do
+ setup_hotspares "$keyword"
+
+ iterate_over_hotspares verify_assertion
+
+ destroy_pool "$TESTPOOL"
+done
+
+log_pass "'zpool remove <pool> <vdev> ...' executes successfully"
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_replace_001_neg.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_replace_001_neg.ksh
new file mode 100644
index 000000000000..9e85966aad7d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_replace_001_neg.ksh
@@ -0,0 +1,107 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotspare_replace_001_neg
+#
+# DESCRIPTION:
+# 'zpool replace <pool> <odev> <ndev>...' should return fail if
+# - try to replace a basic vdev that already has an activated
+# hot spare.
+# - try to replace log device.
+#
+# STRATEGY:
+# 1. Create a storage pool
+# 2. Add hot spare devices to the pool
+# 3. For each scenario, try to replace the basic vdev with the given hot spares
+# 4. Verify the the replace operation get failed
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2006-06-07)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TESTPOOL && \
+ destroy_pool $TESTPOOL
+
+ partition_cleanup
+}
+
+function verify_assertion # dev
+{
+ typeset dev=$1
+
+ for odev in ${pooldevs[@]} ; do
+ log_must $ZPOOL replace $TESTPOOL $odev $dev
+ log_mustnot $ZPOOL replace $TESTPOOL $odev $availdev
+ log_must $ZPOOL detach $TESTPOOL $dev
+ done
+
+ if [[ -n ${logdevs[@]} ]] ; then
+ for odev in ${logdevs[@]} ; do
+ log_mustnot $ZPOOL replace $TESTPOOL $odev $dev
+ done
+ fi
+}
+
+log_assert "'zpool replace <pool> <odev> <ndev>' should fail with inapplicable scenarios."
+
+log_onexit cleanup
+
+set_devs
+
+typeset dev_nonexist
+typeset availdev=${devarray[2]}
+dev_nonexist=${disk}sbad_slice_num
+
+for keyword in "${keywords[@]}" ; do
+ setup_hotspares "$keyword" "${sparedevs[@]} $availdev"
+
+ for odev in ${pooldevs[@]} ; do
+ for ndev in $dev_nonexist ; do
+ log_mustnot $ZPOOL replace $TESTPOOL $odev $ndev
+ done
+ done
+
+ iterate_over_hotspares verify_assertion
+
+ destroy_pool "$TESTPOOL"
+done
+
+log_pass "'zpool replace <pool> <odev> <ndev>' fail with inapplicable scenarios."
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_replace_002_neg.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_replace_002_neg.ksh
new file mode 100644
index 000000000000..333c1e5e7487
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_replace_002_neg.ksh
@@ -0,0 +1,92 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotspare_replace_002_neg
+#
+# DESCRIPTION:
+# 'zpool replace <pool> <odev> <ndev>...' should return fail if
+# the size of hot spares is smaller than the basic vdev.
+#
+# STRATEGY:
+# 1. Create a storage pool
+# 2. Add hot spare devices to the pool
+# 3. Try to replace the basic vdev with the smaller hot spares
+# 4. Verify the the replace operation failes
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2006-06-07)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TESTPOOL && \
+ destroy_pool $TESTPOOL
+
+ partition_cleanup
+}
+
+function verify_assertion # dev
+{
+ typeset dev=$1
+
+ for odev in ${pooldevs[0]} ; do
+ log_mustnot $ZPOOL replace $TESTPOOL $odev $dev
+ done
+}
+
+log_assert "'zpool replace <pool> <odev> <ndev>' should fail while the hot spares smaller than the basic vdev."
+
+log_onexit cleanup
+
+set_devs
+
+typeset smalldev="${devarray[6]}"
+VDEV_SIZE=$SIZE1
+log_must create_vdevs $smalldev
+unset VDEV_SIZE
+
+for keyword in "${keywords[@]}" ; do
+ setup_hotspares "$keyword" "$smalldev"
+
+ iterate_over_hotspares verify_assertion "$smalldev"
+
+ destroy_pool "$TESTPOOL"
+done
+
+log_pass "'zpool replace <pool> <odev> <ndev>' should fail while the hot spares smaller than the basic vdev."
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_scrub_001_pos.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_scrub_001_pos.ksh
new file mode 100644
index 000000000000..fa189a9e7004
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_scrub_001_pos.ksh
@@ -0,0 +1,115 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotspare_scrub_001_pos
+#
+# DESCRIPTION:
+# If a storage pool has hot spare,
+# regardless it has been activated or NOT,
+# invoke "zpool scrub" with this storage pool should successful.
+#
+# STRATEGY:
+# 1. Create a storage pool with hot spares
+# 2. Make the storage pool dirty.
+# 3. Do 'zpool scrub' with following scernarios
+# - the hotspare is only in available list
+# - the hotspare is activated
+# 4. Verify the scrub runs successfully.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2006-06-07)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ # Record status so we can see the current state on failure.
+ $ZPOOL status
+ poolexists $TESTPOOL && \
+ destroy_pool $TESTPOOL
+
+ partition_cleanup
+}
+
+function verify_assertion # dev
+{
+ typeset dev=$1
+ typeset odev=${pooldevs[0]}
+
+ log_must $MKFILE 100m $mtpt/$TESTFILE0
+ log_must $ZPOOL scrub $TESTPOOL
+ while is_pool_scrubbing $TESTPOOL ; do
+ $SLEEP 2
+ done
+
+ log_must $MKFILE 100m $mtpt/$TESTFILE1
+ log_must $ZPOOL replace $TESTPOOL $odev $dev
+
+ while ! is_pool_resilvered $TESTPOOL ; do
+ $SLEEP 2
+ done
+
+ log_must $ZPOOL scrub $TESTPOOL
+
+ while is_pool_scrubbing $TESTPOOL ; do
+ $SLEEP 2
+ done
+
+ log_must $ZPOOL detach $TESTPOOL $dev
+ log_must $RM -f $mtpt/$TESTFILE0 \
+ $mtpt/$TESTFILE1
+}
+
+log_assert "'zpool scrub <pool>' should runs successfully regardless " \
+ "the hotspare is only in list or activated."
+
+log_onexit cleanup
+
+typeset mtpt=""
+
+set_devs
+
+for keyword in "${keywords[@]}" ; do
+ setup_hotspares "$keyword"
+ mtpt=$(get_prop mountpoint $TESTPOOL)
+ iterate_over_hotspares verify_assertion "${vdev%% *}"
+ destroy_pool "$TESTPOOL"
+done
+
+log_pass "'zpool scrub <pool>' runs successfully regardless " \
+ "the hotspare is only in list or activated."
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_scrub_002_pos.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_scrub_002_pos.ksh
new file mode 100644
index 000000000000..d75b1df8f50b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_scrub_002_pos.ksh
@@ -0,0 +1,133 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2013 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+#
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotspare_scrub_002_pos
+#
+# DESCRIPTION:
+# 'zpool scrub will scan spares as well as original devices'
+#
+# STRATEGY:
+# 1. Create a storage pool
+# 2. Add hot spare devices to the pool
+# 4. Replace one of the original devices with a spare
+# 5. Simulate errors on the spare
+# 6. Scrub the pool
+# 7. Verify that scrub detected the simulated errors
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2013-01-14)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TESTPOOL && destroy_pool $TESTPOOL
+ partition_cleanup
+}
+
+# Returns the number of checksums errors detected on the given vdev
+function get_cksum #pool, vdev
+{
+ typeset pool=$1
+ typeset vdev=$2
+ $ZPOOL status $pool | awk -v vdev=$vdev '$1~vdev {print $5; exit}'
+}
+
+function verify_assertion # odev
+{
+ typeset odev=$1
+
+ log_must $ZPOOL replace $TESTPOOL $odev $sdev
+ log_must check_state $TESTPOOL "$sdev" "INUSE"
+
+ # corrupt out the $TESTPOOL to make sdev in use
+ # Skip the first input block so we don't overwrite the vdev label
+ log_must $DD if=/dev/zero bs=1024k count=63 oseek=1 conv=notrunc of=$sdev
+
+ $SYNC
+ # The pool may already have started scrubbing, so don't assert this.
+ # Expected postconditions are checked below anyway.
+ $ZPOOL scrub $TESTPOOL
+ while is_pool_scrubbing $TESTPOOL ; do
+ $SLEEP 2
+ done
+
+ # Verify that scrub detected the errors
+ # Some vdevs (ie raidz1) will display the errors on the spare-0 line
+ # instead of on the basic vdev line
+ [[ $(get_cksum $TESTPOOL $sdev) > 0 ]]
+ sdev_errors=$?
+ [[ $(get_cksum $TESTPOOL "spare-0") > 0 ]]
+ spare0_errors=$?
+ log_must [ $sdev_errors -o $spare0_errors ]
+
+ # Now clear the old errors, remove the original device and scrub again.
+ # No new errors should be found, because the scrub should've found and
+ # fixed all errors
+ log_must $ZPOOL clear $TESTPOOL
+ log_must $ZPOOL detach $TESTPOOL $odev
+ $ZPOOL scrub $TESTPOOL
+ while is_pool_scrubbing $TESTPOOL ; do
+ $SLEEP 2
+ done
+ if [ $(get_cksum $TESTPOOL $sdev) -ne 0 ]; then
+ log_fail "ERROR: Scrub missed cksum errors on a spare vdev"
+ fi
+}
+
+log_assert "'zpool scrub' scans spare vdevs"
+
+log_onexit cleanup
+
+set_devs
+typeset odev="${devarray[3]}"
+typeset sdev="${devarray[0]}"
+
+# Don't test striped pools because they can't have spares
+set -A keywords "mirror" "raidz" "raidz2"
+for keyword in "${keywords[@]}" ; do
+ setup_hotspares "$keyword"
+
+ iterate_over_hotspares verify_assertion $odev
+
+ destroy_pool "$TESTPOOL"
+done
+
+log_pass "'zpool scrub scans spare vdevs'"
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_shared_001_pos.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_shared_001_pos.ksh
new file mode 100644
index 000000000000..307e4df13476
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_shared_001_pos.ksh
@@ -0,0 +1,91 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic Corp. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotspare_shared_001_pos
+#
+# DESCRIPTION:
+# It is possible to add the same vdev to multiple pools as a shared spare
+# even when that vdev is a disk instead of a file
+#
+# STRATEGY:
+# 1. Create various combinations of two pools
+# 2. 'zpool add' a hotspare disk to each of them
+# 3. verify that the addition worked
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2012-08-06)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TESTPOOL && \
+ destroy_pool $TESTPOOL
+ poolexists $TESTPOOL1 && \
+ destroy_pool $TESTPOOL1
+
+ partition_cleanup
+}
+
+function verify_assertion # dev
+{
+ typeset dev=$1
+
+ log_must $ZPOOL add $TESTPOOL1 spare $dev
+
+ log_must check_hotspare_state "$TESTPOOL" "$dev" "AVAIL"
+ log_must check_hotspare_state "$TESTPOOL1" "$dev" "AVAIL"
+}
+
+
+log_assert "'zpool add <pool> spare <vdev> ...' can add a disk as a shared spare to multiple pools."
+
+log_onexit cleanup
+
+set_devs
+typeset sdev=$DISK0
+typeset pool1devs="$DISK1 $DISK2 $DISK3 $DISK4"
+
+for keyword in "${keywords[@]}" ; do
+ setup_hotspares "$keyword" $sdev
+ log_must create_pool $TESTPOOL1 $keyword $pool1devs
+ iterate_over_hotspares verify_assertion $sdev
+
+ destroy_pool "$TESTPOOL"
+ destroy_pool "$TESTPOOL1"
+done
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_snapshot_001_pos.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_snapshot_001_pos.ksh
new file mode 100644
index 000000000000..018c6aeb2ae2
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_snapshot_001_pos.ksh
@@ -0,0 +1,121 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotspare_snapshot_001_pos
+#
+# DESCRIPTION:
+# If a hot spare have been activated, create snapshot upon filesystem,
+# then invoke "zpool detach" with this hot spare,
+# the data in snapshot should untouched.
+#
+# STRATEGY:
+# 1. Create a storage pool with hot spares
+# 2. Create some files, create a snapshot upon filesystem
+# 3. Activate a spare device to the pool
+# 4. Create some files, create an new snapshot upon filesystem
+# 5. Do 'zpool detach' with the spare in device
+# 6. Verify the 2 snapshots are all kept, and verify the data integrity within them.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2006-06-07)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TESTPOOL && \
+ destroy_pool $TESTPOOL
+
+ partition_cleanup
+}
+
+function verify_assertion # dev
+{
+ typeset dev=$1
+ typeset odev=${pooldevs[0]}
+
+ log_must $CP $MYTESTFILE $mtpt/$TESTFILE0
+ log_must $ZFS snapshot $TESTPOOL@snap.0
+
+ log_must $ZPOOL replace $TESTPOOL $odev $dev
+
+ log_must $CP $MYTESTFILE $mtpt/$TESTFILE1
+ log_must $ZFS snapshot $TESTPOOL@snap.1
+
+ log_must $SYNC
+
+ log_must $ZPOOL detach $TESTPOOL $dev
+
+ for file in \
+ "$snaproot/snap.0/$TESTFILE0" \
+ "$snaproot/snap.1/$TESTFILE0" \
+ "$snaproot/snap.1/$TESTFILE1" ; do
+ [[ ! -e $file ]] && \
+ log_fail "$file missing after detach hotspare."
+ checksum2=$($SUM $file | $AWK '{print $1}')
+ [[ "$checksum1" != "$checksum2" ]] && \
+ log_fail "Checksums differ ($checksum1 != $checksum2)"
+ done
+
+ log_must $RM -f $mtpt/*
+ log_must $ZFS destroy $TESTPOOL@snap.1
+ log_must $ZFS destroy $TESTPOOL@snap.0
+}
+
+log_assert "'zpool detach <pool> <vdev> ...' against hotspare should do no harm to snapshot."
+
+log_onexit cleanup
+
+typeset mtpt="" snaproot=""
+
+set_devs
+
+checksum1=$($SUM $MYTESTFILE | $AWK '{print $1}')
+
+for keyword in "${keywords[@]}" ; do
+ setup_hotspares "$keyword"
+
+ mtpt=$(get_prop mountpoint $TESTPOOL)
+ snaproot="$mtpt/$(get_snapdir_name)"
+
+ iterate_over_hotspares verify_assertion
+
+ destroy_pool "$TESTPOOL"
+done
+
+log_pass "'zpool detach <pool> <vdev> ...' against hotspare should do no harm to snapshot."
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_snapshot_002_pos.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_snapshot_002_pos.ksh
new file mode 100644
index 000000000000..c3b7a4e7544e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_snapshot_002_pos.ksh
@@ -0,0 +1,127 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: hotspare_snapshot_002_pos
+#
+# DESCRIPTION:
+# If a storage pool has activated hot spares,
+# create snapshot and detach the basic vdev,
+# the hot spare should become the functional device, and
+# the data in snapshot should keep integrity.
+#
+# STRATEGY:
+# 1. Create a storage pool with hot spares activated.
+# 2. Create some files, create a snapshot upon filesystem
+# 3. Activate a spare device to the pool
+# 4. Create some files, create an new snapshot upon filesystem
+# 5. Do 'zpool detach' with the original device
+# 6. Verify the 2 snapshots are all kept, and verify the data integrity within them.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2006-06-07)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TESTPOOL && \
+ destroy_pool $TESTPOOL
+
+ partition_cleanup
+}
+
+function verify_assertion # dev
+{
+ typeset dev=$1
+ typeset odev=${pooldevs[0]}
+
+ log_must $CP $MYTESTFILE $mtpt/$TESTFILE0
+ log_must $ZFS snapshot $TESTPOOL@snap.0
+
+ log_must $ZPOOL replace $TESTPOOL $odev $dev
+
+ log_must $CP $MYTESTFILE $mtpt/$TESTFILE1
+ log_must $ZFS snapshot $TESTPOOL@snap.1
+
+ log_must $SYNC
+
+ log_must $ZPOOL detach $TESTPOOL $odev
+
+ for file in \
+ "$snaproot/snap.0/$TESTFILE0" \
+ "$snaproot/snap.1/$TESTFILE0" \
+ "$snaproot/snap.1/$TESTFILE1" ; do
+ [[ ! -e $file ]] && \
+ log_fail "$file missing after detach hotspare."
+ checksum2=$($SUM $file | $AWK '{print $1}')
+ [[ "$checksum1" != "$checksum2" ]] && \
+ log_fail "Checksums differ ($checksum1 != $checksum2)"
+ done
+
+ log_must $ZFS destroy $TESTPOOL@snap.1
+ log_must $ZFS destroy $TESTPOOL@snap.0
+
+ log_must $ZPOOL add -f "$TESTPOOL" spare $odev
+ log_must $ZPOOL replace "$TESTPOOL" $dev $odev
+ log_must $SYNC
+ log_must $ZPOOL detach "$TESTPOOL" $dev
+ log_must $ZPOOL add -f "$TESTPOOL" spare $dev
+}
+
+log_assert "'zpool detach <pool> <vdev> ...' against basic vdev do no harm to snapshot."
+
+log_onexit cleanup
+
+typeset mtpt="" snaproot=""
+
+set_devs
+
+checksum1=$($SUM $MYTESTFILE | $AWK '{print $1}')
+
+for keyword in "${keywords[@]}" ; do
+ setup_hotspares "$keyword"
+
+ mtpt=$(get_prop mountpoint $TESTPOOL)
+ snaproot="$mtpt/$(get_snapdir_name)"
+
+ iterate_over_hotspares verify_assertion
+
+ destroy_pool "$TESTPOOL"
+done
+
+log_pass "'zpool detach <pool> <vdev> ...' against basic vdev should do no harm to snapshot."
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_test.sh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_test.sh
new file mode 100755
index 000000000000..1e4584ea128a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_test.sh
@@ -0,0 +1,761 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case hotspare_add_001_pos cleanup
+hotspare_add_001_pos_head()
+{
+ atf_set "descr" "'zpool add <pool> spare <vdev> ...' can add devices to the pool."
+ atf_set "timeout" 3600
+}
+hotspare_add_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotspare_add_001_pos.ksh || atf_fail "Testcase failed"
+}
+hotspare_add_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case hotspare_add_002_pos cleanup
+hotspare_add_002_pos_head()
+{
+ atf_set "descr" "'zpool add <pool> spare <vdev> ...' can add devices to the pool while it has spare-in device."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 3600
+}
+hotspare_add_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotspare_add_002_pos.ksh || atf_fail "Testcase failed"
+}
+hotspare_add_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case hotspare_add_003_neg cleanup
+hotspare_add_003_neg_head()
+{
+ atf_set "descr" "'zpool add [-f]' with hot spares should fail with inapplicable scenarios."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 3600
+}
+hotspare_add_003_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ verify_disk_count "$DISKS" 3
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotspare_add_003_neg.ksh || atf_fail "Testcase failed"
+}
+hotspare_add_003_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case hotspare_add_004_neg cleanup
+hotspare_add_004_neg_head()
+{
+ atf_set "descr" "'zpool add [-f]' will not allow a swap device to be used as a hotspare'"
+ atf_set "require.progs" "ksh93 zpool swapon swapoff swapctl"
+}
+hotspare_add_004_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotspare_add_004_neg.ksh || atf_fail "Testcase failed"
+}
+hotspare_add_004_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case hotspare_clone_001_pos cleanup
+hotspare_clone_001_pos_head()
+{
+ atf_set "descr" "'zpool detach <pool> <vdev> ...' against hotspare should do no harm to clone."
+ atf_set "require.progs" "ksh93 zfs zpool sum"
+ atf_set "timeout" 3600
+}
+hotspare_clone_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotspare_clone_001_pos.ksh || atf_fail "Testcase failed"
+}
+hotspare_clone_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case hotspare_clone_002_pos cleanup
+hotspare_clone_002_pos_head()
+{
+ atf_set "descr" "'zpool detach <pool> <vdev> ...' against basic vdev should do no harm to clone."
+ atf_set "require.progs" "ksh93 zfs zpool sum"
+ atf_set "timeout" 3600
+}
+hotspare_clone_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotspare_clone_002_pos.ksh || atf_fail "Testcase failed"
+}
+hotspare_clone_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case hotspare_create_001_neg cleanup
+hotspare_create_001_neg_head()
+{
+ atf_set "descr" "'zpool create [-f]' with hot spares should be failedwith inapplicable scenarios."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 3600
+}
+hotspare_create_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ verify_disk_count "$DISKS" 3
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotspare_create_001_neg.ksh || atf_fail "Testcase failed"
+}
+hotspare_create_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case hotspare_detach_001_pos cleanup
+hotspare_detach_001_pos_head()
+{
+ atf_set "descr" "'zpool detach <pool> <vdev> ...' should deactivate the spared-in hot spare device successfully."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 3600
+}
+hotspare_detach_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotspare_detach_001_pos.ksh || atf_fail "Testcase failed"
+}
+hotspare_detach_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case hotspare_detach_002_pos cleanup
+hotspare_detach_002_pos_head()
+{
+ atf_set "descr" "'zpool detach <pool> <vdev> ...' against a functioning device that have spared should take the hot spare permanently swapping in successfully."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 3600
+}
+hotspare_detach_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotspare_detach_002_pos.ksh || atf_fail "Testcase failed"
+}
+hotspare_detach_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case hotspare_detach_003_pos cleanup
+hotspare_detach_003_pos_head()
+{
+ atf_set "descr" "'zpool replace <pool> <vdev> <ndev>' against a functioning device that have spared should complete and the hot spare should return to available."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 3600
+}
+hotspare_detach_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotspare_detach_003_pos.ksh || atf_fail "Testcase failed"
+}
+hotspare_detach_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case hotspare_detach_004_pos cleanup
+hotspare_detach_004_pos_head()
+{
+ atf_set "descr" "'zpool replace <pool> <vdev> <ndev>' against a hot spare device that have been activated should successful while the another dev is a available hot spare."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 3600
+}
+hotspare_detach_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotspare_detach_004_pos.ksh || atf_fail "Testcase failed"
+}
+hotspare_detach_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case hotspare_detach_005_neg cleanup
+hotspare_detach_005_neg_head()
+{
+ atf_set "descr" "'zpool detach <pool> <vdev>' against a hot spare device that NOT activated should fail and issue an error message."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 3600
+}
+hotspare_detach_005_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotspare_detach_005_neg.ksh || atf_fail "Testcase failed"
+}
+hotspare_detach_005_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case hotspare_export_001_neg cleanup
+hotspare_export_001_neg_head()
+{
+ atf_set "descr" "export pool that using shared hotspares will fail"
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 3600
+}
+hotspare_export_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotspare_export_001_neg.ksh || atf_fail "Testcase failed"
+}
+hotspare_export_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case hotspare_import_001_pos cleanup
+hotspare_import_001_pos_head()
+{
+ atf_set "descr" "'zpool export/import <pool>' should runs successfully regardless the hotspare is only in list, activated, or offline."
+ atf_set "require.progs" "ksh93 zpool sum"
+ atf_set "timeout" 3600
+}
+hotspare_import_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotspare_import_001_pos.ksh || atf_fail "Testcase failed"
+}
+hotspare_import_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case hotspare_onoffline_003_neg cleanup
+hotspare_onoffline_003_neg_head()
+{
+ atf_set "descr" "'zpool offline/online <pool> <vdev>' should fail on inactive spares"
+ atf_set "require.progs" "ksh93 zpool zdb"
+ atf_set "timeout" 3600
+}
+hotspare_onoffline_003_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotspare_onoffline_003_neg.ksh || atf_fail "Testcase failed"
+}
+hotspare_onoffline_003_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case hotspare_onoffline_004_neg cleanup
+hotspare_onoffline_004_neg_head()
+{
+ atf_set "descr" "'zpool offline/online <pool> <vdev>' against a spared basic vdev during I/O completes."
+ atf_set "require.progs" "ksh93 zfs zpool zdb"
+ atf_set "timeout" 3600
+}
+hotspare_onoffline_004_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotspare_onoffline_004_neg.ksh || atf_fail "Testcase failed"
+}
+hotspare_onoffline_004_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case hotspare_remove_001_pos cleanup
+hotspare_remove_001_pos_head()
+{
+ atf_set "descr" "'zpool remove <pool> <vdev> ...' can remove spare device from the pool."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 3600
+}
+hotspare_remove_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotspare_remove_001_pos.ksh || atf_fail "Testcase failed"
+}
+hotspare_remove_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case hotspare_remove_002_neg cleanup
+hotspare_remove_002_neg_head()
+{
+ atf_set "descr" "'zpool remove <pool> <vdev> ...' should fail with inapplicable scenarios."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 3600
+}
+hotspare_remove_002_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotspare_remove_002_neg.ksh || atf_fail "Testcase failed"
+}
+hotspare_remove_002_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case hotspare_remove_003_neg cleanup
+hotspare_remove_003_neg_head()
+{
+ atf_set "descr" "Executing 'zpool remove' with bad options fails"
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 3600
+}
+hotspare_remove_003_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotspare_remove_003_neg.ksh || atf_fail "Testcase failed"
+}
+hotspare_remove_003_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case hotspare_remove_004_pos cleanup
+hotspare_remove_004_pos_head()
+{
+ atf_set "descr" "'zpool remove <pool> <vdev> ...' can remove spare device from the pool."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 3600
+}
+hotspare_remove_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotspare_remove_004_pos.ksh || atf_fail "Testcase failed"
+}
+hotspare_remove_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case hotspare_replace_001_neg cleanup
+hotspare_replace_001_neg_head()
+{
+ atf_set "descr" "'zpool replace <pool> <odev> <ndev>' should fail with inapplicable scenarios."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 3600
+}
+hotspare_replace_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotspare_replace_001_neg.ksh || atf_fail "Testcase failed"
+}
+hotspare_replace_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case hotspare_replace_002_neg cleanup
+hotspare_replace_002_neg_head()
+{
+ atf_set "descr" "'zpool replace <pool> <odev> <ndev>' should fail while the hot spares smaller than the basic vdev."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 3600
+}
+hotspare_replace_002_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotspare_replace_002_neg.ksh || atf_fail "Testcase failed"
+}
+hotspare_replace_002_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case hotspare_scrub_001_pos cleanup
+hotspare_scrub_001_pos_head()
+{
+ atf_set "descr" "'zpool scrub <pool>' should runs successfully regardlessthe hotspare is only in list or activated."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 3600
+}
+hotspare_scrub_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotspare_scrub_001_pos.ksh || atf_fail "Testcase failed"
+}
+hotspare_scrub_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case hotspare_scrub_002_pos cleanup
+hotspare_scrub_002_pos_head()
+{
+ atf_set "descr" "'zpool scrub' scans spare vdevs"
+ atf_set "require.progs" "ksh93 zpool"
+}
+hotspare_scrub_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ atf_expect_fail "PR 241069 scrub does not detect all errors on active spares"
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotspare_scrub_002_pos.ksh || atf_fail "Testcase failed"
+}
+hotspare_scrub_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case hotspare_shared_001_pos cleanup
+hotspare_shared_001_pos_head()
+{
+ atf_set "descr" "'zpool add <pool> spare <vdev> ...' can add a disk as a shared spare to multiple pools."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 3600
+}
+hotspare_shared_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ verify_disk_count "$DISKS" 5
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotspare_shared_001_pos.ksh || atf_fail "Testcase failed"
+}
+hotspare_shared_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case hotspare_snapshot_001_pos cleanup
+hotspare_snapshot_001_pos_head()
+{
+ atf_set "descr" "'zpool detach <pool> <vdev> ...' against hotspare should do no harm to snapshot."
+ atf_set "require.progs" "ksh93 zfs zpool sum"
+ atf_set "timeout" 3600
+}
+hotspare_snapshot_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotspare_snapshot_001_pos.ksh || atf_fail "Testcase failed"
+}
+hotspare_snapshot_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case hotspare_snapshot_002_pos cleanup
+hotspare_snapshot_002_pos_head()
+{
+ atf_set "descr" "'zpool detach <pool> <vdev> ...' against basic vdev do no harm to snapshot."
+ atf_set "require.progs" "ksh93 zfs zpool sum"
+ atf_set "timeout" 3600
+}
+hotspare_snapshot_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/hotspare_snapshot_002_pos.ksh || atf_fail "Testcase failed"
+}
+hotspare_snapshot_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/hotspare.kshlib
+ . $(atf_get_srcdir)/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case hotspare_add_001_pos
+ atf_add_test_case hotspare_add_002_pos
+ atf_add_test_case hotspare_add_003_neg
+ atf_add_test_case hotspare_add_004_neg
+ atf_add_test_case hotspare_clone_001_pos
+ atf_add_test_case hotspare_clone_002_pos
+ atf_add_test_case hotspare_create_001_neg
+ atf_add_test_case hotspare_detach_001_pos
+ atf_add_test_case hotspare_detach_002_pos
+ atf_add_test_case hotspare_detach_003_pos
+ atf_add_test_case hotspare_detach_004_pos
+ atf_add_test_case hotspare_detach_005_neg
+ atf_add_test_case hotspare_export_001_neg
+ atf_add_test_case hotspare_import_001_pos
+ atf_add_test_case hotspare_onoffline_003_neg
+ atf_add_test_case hotspare_onoffline_004_neg
+ atf_add_test_case hotspare_remove_001_pos
+ atf_add_test_case hotspare_remove_002_neg
+ atf_add_test_case hotspare_remove_003_neg
+ atf_add_test_case hotspare_remove_004_pos
+ atf_add_test_case hotspare_replace_001_neg
+ atf_add_test_case hotspare_replace_002_neg
+ atf_add_test_case hotspare_scrub_001_pos
+ atf_add_test_case hotspare_scrub_002_pos
+ atf_add_test_case hotspare_shared_001_pos
+ atf_add_test_case hotspare_snapshot_001_pos
+ atf_add_test_case hotspare_snapshot_002_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/hotspare/setup.ksh b/tests/sys/cddl/zfs/tests/hotspare/setup.ksh
new file mode 100644
index 000000000000..00f12836e646
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/hotspare/setup.ksh
@@ -0,0 +1,38 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+verify_runnable "global"
+
+log_must cleanup_devices_all
+
+# Most of the tests in this directory manually activate spares, which is not
+# possible while ZFSD is running.
+stop_zfsd
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/inheritance/Makefile b/tests/sys/cddl/zfs/tests/inheritance/Makefile
new file mode 100644
index 000000000000..8dc20cb7b3bd
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/Makefile
@@ -0,0 +1,64 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/inheritance
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= inheritance_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= config015.cfg
+${PACKAGE}FILES+= config012.cfg
+${PACKAGE}FILES+= state007.cfg
+${PACKAGE}FILES+= state009.cfg
+${PACKAGE}FILES+= config020.cfg
+${PACKAGE}FILES+= state024.cfg
+${PACKAGE}FILES+= state023.cfg
+${PACKAGE}FILES+= state018.cfg
+${PACKAGE}FILES+= state016.cfg
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= config003.cfg
+${PACKAGE}FILES+= state011.cfg
+${PACKAGE}FILES+= config004.cfg
+${PACKAGE}FILES+= config021.cfg
+${PACKAGE}FILES+= state008.cfg
+${PACKAGE}FILES+= config013.cfg
+${PACKAGE}FILES+= state006.cfg
+${PACKAGE}FILES+= config014.cfg
+${PACKAGE}FILES+= state001.cfg
+${PACKAGE}FILES+= state010.cfg
+${PACKAGE}FILES+= config005.cfg
+${PACKAGE}FILES+= state017.cfg
+${PACKAGE}FILES+= config002.cfg
+${PACKAGE}FILES+= state019.cfg
+${PACKAGE}FILES+= state022.cfg
+${PACKAGE}FILES+= state014.cfg
+${PACKAGE}FILES+= config001.cfg
+${PACKAGE}FILES+= state013.cfg
+${PACKAGE}FILES+= config006.cfg
+${PACKAGE}FILES+= config008.cfg
+${PACKAGE}FILES+= inherit_001_pos.ksh
+${PACKAGE}FILES+= state021.cfg
+${PACKAGE}FILES+= config022.cfg
+${PACKAGE}FILES+= config019.cfg
+${PACKAGE}FILES+= config017.cfg
+${PACKAGE}FILES+= state002.cfg
+${PACKAGE}FILES+= config010.cfg
+${PACKAGE}FILES+= state005.cfg
+${PACKAGE}FILES+= state020.cfg
+${PACKAGE}FILES+= inherit.kshlib
+${PACKAGE}FILES+= config009.cfg
+${PACKAGE}FILES+= state012.cfg
+${PACKAGE}FILES+= config007.cfg
+${PACKAGE}FILES+= state015.cfg
+${PACKAGE}FILES+= config011.cfg
+${PACKAGE}FILES+= inherit.cfg
+${PACKAGE}FILES+= state004.cfg
+${PACKAGE}FILES+= config016.cfg
+${PACKAGE}FILES+= state003.cfg
+${PACKAGE}FILES+= config018.cfg
+${PACKAGE}FILES+= config023.cfg
+${PACKAGE}FILES+= config024.cfg
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/inheritance/README.config b/tests/sys/cddl/zfs/tests/inheritance/README.config
new file mode 100644
index 000000000000..a72151f9c915
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/README.config
@@ -0,0 +1,61 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# The configX.cfg files describe both the dataset hierarchy to
+# be created but also the initial source values for the datasets
+# properties, i.e. whether they should be left with their default values
+# or set locally.
+#
+# Format for this file is as follows:
+# <dataset name> <dataset type> <inital property setting>
+#
+# <dataset name> - must be the full dataset name
+#
+# <dataset type> - recognised types are POOL, CTR and FS
+#
+#
+# <initial property setting> - can have one of the following
+# values:
+#
+# default property values are left unchanged
+#
+# local property values are set locally
+#
+# - property values are left unchanged (has the
+# same effect as 'default' but is used to indicate
+# that the property 'source' field may be 'inherited
+# from..' depending on the actions further up the
+# dataset hierarchy.
+#
+#
+# The configuration below creates a three tier dataset layout, consisting
+# of a pool, container and filesystem.
+#
+# The top tier pool's properties being set locally, and the two
+# lower tier properties each inheriting their values from the next tier
+# up.
+#
+TESTPOOL POOL local
+TESTPOOL/TESTCTR CTR -
+TESTPOOL/TESTCTR/TESTFS1 FS -
diff --git a/tests/sys/cddl/zfs/tests/inheritance/README.state b/tests/sys/cddl/zfs/tests/inheritance/README.state
new file mode 100644
index 000000000000..39447ed5302e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/README.state
@@ -0,0 +1,102 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# For every configX.cfg file there must be a corresponding stateX.cfg
+# file. The state file both drives the operations to be performed on the
+# dataset hierarchy which has been specified by the corresponding
+# configX.cfg file, and also specifies the expected state of the
+# properties after the operation has been completed.
+#
+# The format of the file is:
+# <target dataset>:<command>
+# <property source before command> <property src after command>
+# <property source before command> <property src after command>
+# .... ....
+# .... ....
+#
+# <target dataset> - dataset upon which the <command> is to be executed. Can
+# be any of the datasets specified in the corresponding
+# configX.cfg file. If no command is to be executed
+# then must be set to '-'
+#
+#
+# <command> - command to be executed upon the specified dataset.
+# Currently the only supported commands are 'inherit'
+# or 'inherit -r'. If no command is to be executed,
+# then must be set to '-'.
+#
+# <property src before command>
+# - the 'source' (as reported in 'zfs get') for the
+# the properties before <command> is executed. This
+# can be 'default', 'local' or the name of a dataset
+# from which the property is inherited. (The code
+# automatically adds in the 'inherited from..' part
+# of the string when doing the check.
+#
+# This field is for informational purposes only, to
+# aid the user in seeing how the changes trickle down
+# the data hierarchy.
+#
+# <property src after command>
+# - the expected value of the 'source' field after the
+# <command> has been executed on the <target dataset>.
+# As above can be 'default', 'local', or the dataset
+# from which the property is inherited.
+#
+# Two important things to note:
+# 1) there must be a <property src..> line corresponding to each dataset
+# line specified in the configX.cfg file.
+#
+#
+# 2) There can be as many <command>/<property src> blocks as desired, but
+# there must be at least one, and the effect of each block is cumulative
+# (i.e. the properties are not reset back to their default values between
+# each block. If that is desired then each block must be placed in its
+# own state file with its own corresponding configX.cfg file).
+#
+#
+# Below are two sample <command>/<property src> blocks.
+#
+# The first simply verifies that the properties on the top level dataset
+# were set locally, and that the middle and bottom datasets properties
+# were inherited from the top level pool (called TESTPOOL). Note the '-:-'
+# which means that no command is to be executed, but simply that the
+# properties settings are to be verified.
+#
+-:-
+#
+local local
+TESTPOOL TESTPOOL
+TESTPOOL TESTPOOL
+#
+#
+# The block below describes the expected state of the properties after
+# an 'inherit -r' command has been run on the top level pool (called
+# TESTPOOL).
+#
+TESTPOOL:inherit -r
+#
+local default
+TESTPOOL default
+TESTPOOL default
diff --git a/tests/sys/cddl/zfs/tests/inheritance/cleanup.ksh b/tests/sys/cddl/zfs/tests/inheritance/cleanup.ksh
new file mode 100644
index 000000000000..b2b441e574d0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/inheritance/config001.cfg b/tests/sys/cddl/zfs/tests/inheritance/config001.cfg
new file mode 100644
index 000000000000..ea8add69d5b4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/config001.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+testpool.inherit POOL local
+testpool.inherit/TESTCTR CTR -
+testpool.inherit/TESTCTR/TESTFS1 FS -
diff --git a/tests/sys/cddl/zfs/tests/inheritance/config002.cfg b/tests/sys/cddl/zfs/tests/inheritance/config002.cfg
new file mode 100644
index 000000000000..5beeb1608b28
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/config002.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+testpool.inherit POOL default
+testpool.inherit/TESTCTR CTR local
+testpool.inherit/TESTCTR/TESTFS1 FS -
diff --git a/tests/sys/cddl/zfs/tests/inheritance/config003.cfg b/tests/sys/cddl/zfs/tests/inheritance/config003.cfg
new file mode 100644
index 000000000000..5f15f52cefd0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/config003.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+testpool.inherit POOL default
+testpool.inherit/TESTCTR CTR default
+testpool.inherit/TESTCTR/TESTFS1 FS local
diff --git a/tests/sys/cddl/zfs/tests/inheritance/config004.cfg b/tests/sys/cddl/zfs/tests/inheritance/config004.cfg
new file mode 100644
index 000000000000..7de48089a7b1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/config004.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+testpool.inherit POOL default
+testpool.inherit/TESTCTR CTR local
+testpool.inherit/TESTCTR/TESTFS1 FS local
diff --git a/tests/sys/cddl/zfs/tests/inheritance/config005.cfg b/tests/sys/cddl/zfs/tests/inheritance/config005.cfg
new file mode 100644
index 000000000000..c6e2403b499a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/config005.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+testpool.inherit POOL local
+testpool.inherit/TESTCTR CTR local
+testpool.inherit/TESTCTR/TESTFS1 FS -
diff --git a/tests/sys/cddl/zfs/tests/inheritance/config006.cfg b/tests/sys/cddl/zfs/tests/inheritance/config006.cfg
new file mode 100644
index 000000000000..2082abb1504c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/config006.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+testpool.inherit POOL local
+testpool.inherit/TESTCTR CTR -
+testpool.inherit/TESTCTR/TESTFS1 FS local
diff --git a/tests/sys/cddl/zfs/tests/inheritance/config007.cfg b/tests/sys/cddl/zfs/tests/inheritance/config007.cfg
new file mode 100644
index 000000000000..5c92c13a5f3c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/config007.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+testpool.inherit POOL local
+testpool.inherit/TESTCTR CTR local
+testpool.inherit/TESTCTR/TESTFS1 FS local
diff --git a/tests/sys/cddl/zfs/tests/inheritance/config008.cfg b/tests/sys/cddl/zfs/tests/inheritance/config008.cfg
new file mode 100644
index 000000000000..ff4ec642d0e7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/config008.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+testpool.inherit POOL default
+testpool.inherit/TESTCTR CTR default
+testpool.inherit/TESTCTR/TESTFS1 FS default
diff --git a/tests/sys/cddl/zfs/tests/inheritance/config009.cfg b/tests/sys/cddl/zfs/tests/inheritance/config009.cfg
new file mode 100644
index 000000000000..ff4ec642d0e7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/config009.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+testpool.inherit POOL default
+testpool.inherit/TESTCTR CTR default
+testpool.inherit/TESTCTR/TESTFS1 FS default
diff --git a/tests/sys/cddl/zfs/tests/inheritance/config010.cfg b/tests/sys/cddl/zfs/tests/inheritance/config010.cfg
new file mode 100644
index 000000000000..ff4ec642d0e7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/config010.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+testpool.inherit POOL default
+testpool.inherit/TESTCTR CTR default
+testpool.inherit/TESTCTR/TESTFS1 FS default
diff --git a/tests/sys/cddl/zfs/tests/inheritance/config011.cfg b/tests/sys/cddl/zfs/tests/inheritance/config011.cfg
new file mode 100644
index 000000000000..5f15f52cefd0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/config011.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+testpool.inherit POOL default
+testpool.inherit/TESTCTR CTR default
+testpool.inherit/TESTCTR/TESTFS1 FS local
diff --git a/tests/sys/cddl/zfs/tests/inheritance/config012.cfg b/tests/sys/cddl/zfs/tests/inheritance/config012.cfg
new file mode 100644
index 000000000000..5f15f52cefd0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/config012.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+testpool.inherit POOL default
+testpool.inherit/TESTCTR CTR default
+testpool.inherit/TESTCTR/TESTFS1 FS local
diff --git a/tests/sys/cddl/zfs/tests/inheritance/config013.cfg b/tests/sys/cddl/zfs/tests/inheritance/config013.cfg
new file mode 100644
index 000000000000..7de48089a7b1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/config013.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+testpool.inherit POOL default
+testpool.inherit/TESTCTR CTR local
+testpool.inherit/TESTCTR/TESTFS1 FS local
diff --git a/tests/sys/cddl/zfs/tests/inheritance/config014.cfg b/tests/sys/cddl/zfs/tests/inheritance/config014.cfg
new file mode 100644
index 000000000000..7de48089a7b1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/config014.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+testpool.inherit POOL default
+testpool.inherit/TESTCTR CTR local
+testpool.inherit/TESTCTR/TESTFS1 FS local
diff --git a/tests/sys/cddl/zfs/tests/inheritance/config015.cfg b/tests/sys/cddl/zfs/tests/inheritance/config015.cfg
new file mode 100644
index 000000000000..5beeb1608b28
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/config015.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+testpool.inherit POOL default
+testpool.inherit/TESTCTR CTR local
+testpool.inherit/TESTCTR/TESTFS1 FS -
diff --git a/tests/sys/cddl/zfs/tests/inheritance/config016.cfg b/tests/sys/cddl/zfs/tests/inheritance/config016.cfg
new file mode 100644
index 000000000000..5beeb1608b28
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/config016.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+testpool.inherit POOL default
+testpool.inherit/TESTCTR CTR local
+testpool.inherit/TESTCTR/TESTFS1 FS -
diff --git a/tests/sys/cddl/zfs/tests/inheritance/config017.cfg b/tests/sys/cddl/zfs/tests/inheritance/config017.cfg
new file mode 100644
index 000000000000..c6e2403b499a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/config017.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+testpool.inherit POOL local
+testpool.inherit/TESTCTR CTR local
+testpool.inherit/TESTCTR/TESTFS1 FS -
diff --git a/tests/sys/cddl/zfs/tests/inheritance/config018.cfg b/tests/sys/cddl/zfs/tests/inheritance/config018.cfg
new file mode 100644
index 000000000000..c6e2403b499a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/config018.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+testpool.inherit POOL local
+testpool.inherit/TESTCTR CTR local
+testpool.inherit/TESTCTR/TESTFS1 FS -
diff --git a/tests/sys/cddl/zfs/tests/inheritance/config019.cfg b/tests/sys/cddl/zfs/tests/inheritance/config019.cfg
new file mode 100644
index 000000000000..ea8add69d5b4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/config019.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+testpool.inherit POOL local
+testpool.inherit/TESTCTR CTR -
+testpool.inherit/TESTCTR/TESTFS1 FS -
diff --git a/tests/sys/cddl/zfs/tests/inheritance/config020.cfg b/tests/sys/cddl/zfs/tests/inheritance/config020.cfg
new file mode 100644
index 000000000000..ea8add69d5b4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/config020.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+testpool.inherit POOL local
+testpool.inherit/TESTCTR CTR -
+testpool.inherit/TESTCTR/TESTFS1 FS -
diff --git a/tests/sys/cddl/zfs/tests/inheritance/config021.cfg b/tests/sys/cddl/zfs/tests/inheritance/config021.cfg
new file mode 100644
index 000000000000..2082abb1504c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/config021.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+testpool.inherit POOL local
+testpool.inherit/TESTCTR CTR -
+testpool.inherit/TESTCTR/TESTFS1 FS local
diff --git a/tests/sys/cddl/zfs/tests/inheritance/config022.cfg b/tests/sys/cddl/zfs/tests/inheritance/config022.cfg
new file mode 100644
index 000000000000..2082abb1504c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/config022.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+testpool.inherit POOL local
+testpool.inherit/TESTCTR CTR -
+testpool.inherit/TESTCTR/TESTFS1 FS local
diff --git a/tests/sys/cddl/zfs/tests/inheritance/config023.cfg b/tests/sys/cddl/zfs/tests/inheritance/config023.cfg
new file mode 100644
index 000000000000..5c92c13a5f3c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/config023.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+testpool.inherit POOL local
+testpool.inherit/TESTCTR CTR local
+testpool.inherit/TESTCTR/TESTFS1 FS local
diff --git a/tests/sys/cddl/zfs/tests/inheritance/config024.cfg b/tests/sys/cddl/zfs/tests/inheritance/config024.cfg
new file mode 100644
index 000000000000..5c92c13a5f3c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/config024.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+testpool.inherit POOL local
+testpool.inherit/TESTCTR CTR local
+testpool.inherit/TESTCTR/TESTFS1 FS local
diff --git a/tests/sys/cddl/zfs/tests/inheritance/inherit.cfg b/tests/sys/cddl/zfs/tests/inheritance/inherit.cfg
new file mode 100644
index 000000000000..d2f64776a849
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/inherit.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export ZFSROOT=
+export STF_TIMEOUT=2800
diff --git a/tests/sys/cddl/zfs/tests/inheritance/inherit.kshlib b/tests/sys/cddl/zfs/tests/inheritance/inherit.kshlib
new file mode 100644
index 000000000000..7f98d3bd6aba
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/inherit.kshlib
@@ -0,0 +1,110 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+#
+# Simple function to get the source of the specified property.
+# If unable to get the property then exits.
+#
+function get_prop_src # property dataset
+{
+ typeset prop_val
+ typeset prop=$1
+ typeset dataset=$2
+
+ prop_val=`$ZFS get -H -o source $prop $dataset`
+
+ if [[ $? -ne 0 ]]; then
+ log_fail "Unable to determine the source of $prop " \
+ "property for dataset $dataset"
+ else
+ print $prop_val
+ fi
+}
+
+#
+# Function to check the 'source' of a property. The source can
+# either be "default", "local", or "inherited from <parent dataset>".
+#
+# The 'expected src' argument must be either "default", "local", or
+# a dataset name.
+#
+# Returns 0 on success, 1 on failure.
+#
+function verify_prop_src # child_dataset property expected_src
+{
+ typeset target=$1
+ typeset prop=$2
+ typeset expected=$3
+
+ prop_src=`get_prop_src $prop $target`
+
+ #
+ # Rather than just checking if $prop_src == $expected
+ # we first determine what value $expected should have.
+ # This allows us to catch the case where a property
+ # has a source of "local" but we expected it to be
+ # "default"
+ #
+ if [[ $expected == "default" ]]; then
+ if [[ $prop_src != $expected ]]; then
+ log_note "Property $prop of $target has source"\
+ " $prop_src rather than $expected"
+ return 1
+ fi
+ elif [[ $expected == "local" ]]; then
+ if [[ $prop_src != $expected ]]; then
+ log_note "Property $prop of $target has source"\
+ " $prop_src rather than $expected"
+ return 1
+ fi
+ elif [[ $prop_src != "inherited from $expected" ]]; then
+ log_note "Property $prop of $expected has source $prop_src"\
+ " rather than 'inherited from $expected'"
+ return 1
+ fi
+
+ return 0
+}
+
+#
+# Simple function to set a property to a
+# specified value and verify it has changed
+# correctly.
+#
+function set_n_verify_prop #property value dataset
+{
+ typeset prop=$1
+ typeset prop_val=$2
+ typeset dataset=$3
+
+ $ZFS set $prop=$prop_val $dataset
+ check_val=`get_prop $prop $dataset`
+
+ if [[ $check_val != $prop_val ]]; then
+ log_fail "Property $prop of $dataset has value $check_val"\
+ " rather than $prop_val"
+ fi
+}
diff --git a/tests/sys/cddl/zfs/tests/inheritance/inherit_001_pos.ksh b/tests/sys/cddl/zfs/tests/inheritance/inherit_001_pos.ksh
new file mode 100644
index 000000000000..d82807a136b0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/inherit_001_pos.ksh
@@ -0,0 +1,435 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/inheritance/inherit.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: inherit_001_pos
+#
+# DESCRIPTION:
+# Test that properties are correctly inherited using 'zfs set',
+# 'zfs inherit' and 'zfs inherit -r'.
+#
+# STRATEGY:
+# 1) Read a configX.cfg file and create the specified datasets
+# 2) Read a stateX.cfg file and execute the commands within it
+# and verify that the properties have the correct values
+# 3) Repeat steps 1-2 for each configX and stateX files found.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Test properties are inherited correctly"
+
+#
+# Simple function to create specified datasets.
+#
+function create_dataset #name type disks
+{
+ typeset dataset=$1
+ typeset type=$2
+ typeset disks=$3
+
+ if [[ $type == "POOL" ]]; then
+ create_pool "$dataset" "$disks"
+ elif [[ $type == "CTR" ]]; then
+ log_must $ZFS create $dataset
+ log_must $ZFS set canmount=off $dataset
+ elif [[ $type == "FS" ]]; then
+ log_must $ZFS create $dataset
+ else
+ log_fail "ERROR: Unrecognised type $type"
+ fi
+
+ list="$list $dataset"
+}
+
+#
+# Function to walk through all the properties in a
+# dataset, setting them to a 'local' value if required.
+#
+function init_props #dataset init_code
+{
+ typeset dataset=$1
+ typeset init_code=$2
+ typeset new_val
+ typeset -i i=0
+
+ #
+ # Though the effect of '-' and 'default' is the same we
+ # call them out via a log_note to aid in debugging the
+ # config files
+ #
+ if [[ $init_code == "-" ]]; then
+ log_note "Leaving properties for $dataset unchanged."
+ [[ $def_recordsize == 0 ]] && \
+ update_recordsize $dataset $init_code
+ return;
+ elif [[ $init_code == "default" ]]; then
+ log_note "Leaving properties for $dataset at default values."
+ [[ $def_recordsize == 0 ]] && \
+ update_recordsize $dataset $init_code
+ return;
+ elif [[ $init_code == "local" ]]; then
+ log_note "Setting properties for $dataset to local values."
+ for (( ; i < ${#props[*]}; i += 2 )); do
+ if [[ ${props[i]} == "recordsize" ]]; then
+ update_recordsize $dataset $init_code
+ else
+ set_n_verify_prop ${props[i]} \
+ ${local_val[((i/2))]} $dataset
+ fi
+ done
+ else
+ log_fail "ERROR: Unrecognised init code $init_code"
+ fi
+}
+
+#
+# We enter this function either to update the recordsize value
+# in the default array, or to update the local value array.
+#
+function update_recordsize { #dataset init_code
+ typeset dataset=$1
+ typeset init_code=$2
+ typeset idx=0
+ typeset record_val
+
+ #
+ # First need to find where the recordsize property is
+ # located in the arrays
+ #
+ for (( ; idx < ${#props[*]}; idx += 2 )); do
+ [[ ${props[idx]} == "recordsize" ]] && \
+ break
+ done
+
+ (( idx = idx / 2 ))
+ record_val=`get_prop recordsize $dataset`
+ if [[ $init_code == "-" || \
+ $init_code == "default" ]]; then
+
+ def_val[idx]=$record_val
+ def_recordsize=1
+
+ elif [[ $init_code == "local" ]]; then
+
+ log_must $ZFS set recordsize=$record_val $dataset
+
+ local_val[idx]=$record_val
+ fi
+}
+
+#
+# The mountpoint property is slightly different from other properties and
+# so is handled here. For all other properties if they are set to a specific
+# value at a higher level in the data hierarchy (i.e. checksum=on) then that
+# value propogates down the hierarchy unchanged, with the source field being
+# set to 'inherited from <higher dataset>'.
+#
+# The mountpoint property is different in that while the value propogates
+# down the hierarchy, the value at each level is determined by a combination
+# of the top-level value and the current level in the hierarchy.
+#
+# For example consider the case where we have a pool (called pool1), containing
+# a dataset (ctr) which in turn contains a filesystem (fs). If we set the
+# mountpoint of the pool to '/mnt2' then the mountpoints for the dataset and
+# filesystem are '/mnt2/ctr' and /mnt2/ctr/fs' respectively, with the 'source'
+# field being set to 'inherited from pool1'.
+#
+# So at the filesystem level to calculate what our mountpoint property should
+# be set to we walk back up the hierarchy sampling the mountpoint property at
+# each level and forming up the expected mountpoint value piece by piece until
+# we reach the level specified in the 'source' field, which in this example is
+# the top-level pool.
+#
+function get_mntpt_val #dataset src index
+{
+ typeset dataset=$1
+ typeset src=$2
+ typeset idx=$3
+ typeset new_path=""
+ typeset dset
+ typeset mntpt=""
+
+ if [[ $src == "local" ]]; then
+ mntpt=${local_val[idx]}
+ elif [[ $src == "default" ]]; then
+ mntpt="$ZFSROOT/"$dataset
+ else
+ # Walk back up the hierarchy building up the
+ # expected mountpoint property value.
+ obj_name=${dataset##*/}
+
+ while [[ $src != $dataset ]]; do
+ dset=${dataset%/*}
+
+ mnt_val=`get_prop mountpoint $dset`
+
+ mod_prop_val=${mnt_val##*/}
+ new_path="/"$mod_prop_val$new_path
+ dataset=$dset
+ done
+
+ mntpt=$new_path"/"$obj_name
+ fi
+ print $mntpt
+}
+
+#
+# Simple function to verify that a property has the
+# expected value.
+#
+function verify_prop_val #property dataset src index
+{
+ typeset dataset=$1
+ typeset prop=$2
+ typeset src=$3
+ typeset idx=$4
+ typeset new_path=""
+ typeset dset
+ typeset exp_val
+ typeset prop_val
+
+ prop_val=`get_prop $prop $dataset`
+
+ # mountpoint property is handled as a special case
+ if [[ $prop == "mountpoint" ]]; then
+ exp_val=`get_mntpt_val $dataset $src $idx`
+ else
+ if [[ $src == "local" ]]; then
+ exp_val=${local_val[idx]}
+ elif [[ $src == "default" ]]; then
+ exp_val=${def_val[idx]}
+ else
+ #
+ # We are inheriting the value from somewhere
+ # up the hierarchy.
+ #
+ exp_val=`get_prop $prop $src`
+ fi
+ fi
+
+ [ "$prop_val" = "$exp_val" ] && return
+
+ # After putback PSARC/2008/231 Apr,09,2008, the default value of
+ # aclinherit has changed to be 'restricted' instead of 'secure',
+ # but the old interface of 'secure' still exist
+ [ "$prop" = "aclinherit" ] && return
+ [ "$exp_val" = "secure" ] && return
+ [ "$prop_val" = "restricted" ] && return
+
+ log_fail "ERROR: Property $prop (source $src index $idx) for $dataset" \
+ "was [$prop_val]; expected [$exp_val]"
+}
+
+#
+# Function to read the configX.cfg files and create the specified
+# dataset hierarchy
+#
+function scan_config #config-file
+{
+ typeset config_file=$1
+
+ DISK=${DISKS%% *}
+
+ list=""
+
+ grep "^[^#]" $config_file | {
+ while read name type init ; do
+ create_dataset $name $type $DISK
+ init_props $name $init
+ done
+ }
+}
+
+function check_state
+{
+ typeset i=$1
+ typeset j=$2
+ typeset op=$3
+ typeset target=$4
+
+ #
+ # The user can if they wish specify that no operation be performed
+ # (by specifying '-' rather than a command). This is not as
+ # useless as it sounds as it allows us to verify that the dataset
+ # hierarchy has been set up correctly as specified in the
+ # configX.cfg file (which includes 'set'ting properties at a higher
+ # level and checking that they propogate down to the lower levels.
+ #
+ # Note in a few places here, we use log_onfail, rather than
+ # log_must - this substantially reduces journal output.
+ #
+ if [[ $op != "-" ]]; then
+ # Unmount the test datasets if they are still mounted.
+ # Most often, they won't be, so discard the output
+ unmount_all_safe > /dev/null 2>&1
+
+ for p in ${props[i]} ${props[((i+1))]}; do
+ log_onfail $ZFS $op $p $target
+ done
+ fi
+ for check_obj in $list; do
+ read init_src final_src
+
+ for p in ${props[i]} ${props[((i+1))]}; do
+ verify_args="$check_obj $p $final_src"
+
+ log_onfail verify_prop_src $check_obj $p $final_src
+ log_onfail verify_prop_val $check_obj $p $final_src $j
+ done
+ done
+}
+
+#
+# Main function. Executes the commands specified in the stateX.cfg
+# files and then verifies that all the properties have the correct
+# values and 'source' fields.
+#
+function scan_state #state-file
+{
+ typeset state_file=$1
+ typeset -i i=0
+ typeset -i j=0
+
+ log_note "Reading state from $state_file"
+ for (( ; i < ${#props[*]}; i += 2, j += 1 )); do
+ grep "^[^#]" $state_file | {
+ while IFS=: read target op; do
+ check_state $i $j "$op" "$target"
+ done
+ }
+ done
+}
+
+
+set -A props "checksum" "" \
+ "compression" "compress" \
+ "atime" "" \
+ "exec" "" \
+ "setuid" "" \
+ "sharenfs" "" \
+ "recordsize" "recsize" \
+ "mountpoint" "" \
+ "snapdir" "" \
+ "aclmode" "" \
+ "aclinherit" "" \
+ "readonly" "rdonly"
+
+#
+# Note except for the mountpoint default value (which is handled in
+# the routine itself), each property specified in the 'props' array
+# above must have a corresponding entry in the two arrays below.
+#
+set -A def_val "on" \
+ "off" \
+ "on" \
+ "on" \
+ "on" \
+ "off" \
+ "" \
+ "" \
+ "hidden" \
+ "discard" \
+ "secure" \
+ "off"
+
+set -A local_val "off" "on" "off" "off" \
+ "off" "on" "" \
+ "$TESTDIR" "visible" "groupmask" "discard" \
+ "off"
+
+log_must $ZPOOL create $TESTPOOL ${DISKS%% *}
+
+# Append the "shareiscsi" property if it is supported
+$ZFS get shareiscsi $TESTPOOL > /dev/null 2>&1
+if [[ $? -eq 0 ]]; then
+ typeset -i i=${#props[*]}
+ props[i]="shareiscsi"
+ props[((i+1))]=""
+ def_val[((i/2))]="off"
+ local_val[((i/2))]="on"
+fi
+
+# Append the "devices" property if it is settable
+$ZFS set devices=off $TESTPOOL
+if [[ $? -eq 0 ]]; then
+ typeset -i i=${#props[*]}
+ props[i]="devices"
+ props[((i+1))]=""
+ def_val[((i/2))]="on"
+ local_val[((i/2))]="off"
+else
+ log_note "Setting devices=off is not supported on this system"
+fi
+
+log_must $ZPOOL destroy $TESTPOOL
+
+#
+# Global flag indicating whether the default record size had been
+# read.
+#
+typeset def_recordsize=0
+
+TDIR=$STF_SUITE/tests/inheritance
+set -A config_files $(ls $TDIR/config*[1-9]*.cfg)
+set -A state_files $(ls $TDIR/state*.cfg)
+
+#
+# Global list of datasets created.
+#
+list=""
+
+if [[ ${#config_files[*]} != ${#state_files[*]} ]]; then
+ log_fail "ERROR: Must have the same number of config files"\
+ "(${#config_files[*]}) and state files ${#state_files[*]}"
+fi
+
+typeset -i fnum=0
+for (( ; fnum < ${#config_files[*]}; fnum += 1 )); do
+ default_cleanup_noexit
+ def_recordsize=0
+
+ log_note "*** Testing configuration ${config_files[fnum]}"
+ scan_config ${config_files[fnum]}
+ scan_state ${state_files[fnum]}
+done
+
+log_pass "Properties correctly inherited as expected"
diff --git a/tests/sys/cddl/zfs/tests/inheritance/inheritance_test.sh b/tests/sys/cddl/zfs/tests/inheritance/inheritance_test.sh
new file mode 100755
index 000000000000..f69d2da921b2
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/inheritance_test.sh
@@ -0,0 +1,55 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case inherit_001_pos cleanup
+inherit_001_pos_head()
+{
+ atf_set "descr" "Test properties are inherited correctly"
+ atf_set "timeout" 1800
+ atf_set "require.progs" "ksh93 zfs"
+}
+inherit_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/inherit.kshlib
+ . $(atf_get_srcdir)/inherit.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/inherit_001_pos.ksh || atf_fail "Testcase failed"
+}
+inherit_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case inherit_001_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/inheritance/state001.cfg b/tests/sys/cddl/zfs/tests/inheritance/state001.cfg
new file mode 100644
index 000000000000..6d3031f20241
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/state001.cfg
@@ -0,0 +1,39 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# *** ASSERTION DESCRIPTION ***
+#
+# No command is actually run (hence '-:-') but rather this state file is
+# used to verify that the property that was set on the top level pool
+# via the 'local' keyword (in the config1.cfg file) has correctly
+# propogated down the hierarchy.
+#
+# *** ASSERTION DESCRIPTION ***
+#
+-:-
+#
+local local
+testpool.inherit testpool.inherit
+testpool.inherit testpool.inherit
diff --git a/tests/sys/cddl/zfs/tests/inheritance/state002.cfg b/tests/sys/cddl/zfs/tests/inheritance/state002.cfg
new file mode 100644
index 000000000000..24e23b5e9431
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/state002.cfg
@@ -0,0 +1,40 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# *** ASSERTION DESCRIPTION ***
+#
+# No command is actually run (hence '-:-') but rather this state file is
+# used to verify that the property that was set on the middle level
+# dataset via the 'local' keyword (in the configX.cfg file) has
+# correctly propogated down the hierarchy to the filesystem underneath,
+# while leaving the top level pools properties unchanged.
+#
+# *** ASSERTION DESCRIPTION ***
+#
+-:-
+#
+default default
+local local
+testpool.inherit/TESTCTR testpool.inherit/TESTCTR
diff --git a/tests/sys/cddl/zfs/tests/inheritance/state003.cfg b/tests/sys/cddl/zfs/tests/inheritance/state003.cfg
new file mode 100644
index 000000000000..faac0075e3af
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/state003.cfg
@@ -0,0 +1,38 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# *** ASSERTION DESCRIPTION ***
+#
+# No command is actually run (hence '-:-') but rather this state file is
+# used to verify that setting the filesystem's properties does not affect
+# the properties of datasets above it in the hierarchy.
+#
+# *** ASSERTION DESCRIPTION ***
+#
+-:-
+#
+default default
+default default
+local local
diff --git a/tests/sys/cddl/zfs/tests/inheritance/state004.cfg b/tests/sys/cddl/zfs/tests/inheritance/state004.cfg
new file mode 100644
index 000000000000..795d7bcaa988
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/state004.cfg
@@ -0,0 +1,39 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# *** ASSERTION DESCRIPTION ***
+#
+# No command is actually run (hence '-:-') but rather this state file is
+# used to verify that setting a property on a middle level dataset does
+# not prevent us from setting a property on the bottom level filesystem,
+# and that neither affects the top level pools properties.
+#
+# *** ASSERTION DESCRIPTION ***
+#
+-:-
+#
+default default
+local local
+local local
diff --git a/tests/sys/cddl/zfs/tests/inheritance/state005.cfg b/tests/sys/cddl/zfs/tests/inheritance/state005.cfg
new file mode 100644
index 000000000000..70034d6ef5d2
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/state005.cfg
@@ -0,0 +1,40 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# *** ASSERTION DESCRIPTION ***
+#
+# No command is actually run (hence '-:-') but rather this state file is
+# used to verify that when we set a property on a top level pool, followed
+# by a middle level dataset, that the bottom level filesystem inherits
+# the property from the middle level dataset, and not from the top level
+# pool.
+#
+# *** ASSERTION DESCRIPTION ***
+#
+-:-
+#
+local local
+local local
+testpool.inherit/TESTCTR testpool.inherit/TESTCTR
diff --git a/tests/sys/cddl/zfs/tests/inheritance/state006.cfg b/tests/sys/cddl/zfs/tests/inheritance/state006.cfg
new file mode 100644
index 000000000000..ca2b3bac332b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/state006.cfg
@@ -0,0 +1,42 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# *** ASSERTION DESCRIPTION ***
+#
+# No command is actually run (hence '-:-') but rather this state file is
+# used to verify that when we set a property on a top level pool, followed
+# by a bottom level filesystem, that the middle level dataset inherits
+# the property from the top level pool, and not from the bottom level
+# filesystem.
+#
+# *** ASSERTION DESCRIPTION ***
+#
+-:-
+#
+# <prop src before cmd> <prop src after cmd>
+#
+local local
+testpool.inherit testpool.inherit
+local local
diff --git a/tests/sys/cddl/zfs/tests/inheritance/state007.cfg b/tests/sys/cddl/zfs/tests/inheritance/state007.cfg
new file mode 100644
index 000000000000..bcbea550ed69
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/state007.cfg
@@ -0,0 +1,40 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# *** ASSERTION DESCRIPTION ***
+#
+# No command is actually run (hence '-:-') but rather this state file is
+# used to verify that when we can set properties on each level of the
+# hierarchy independently.
+#
+# *** ASSERTION DESCRIPTION ***
+#
+-:-
+#
+# <prop src before cmd> <prop src after cmd>
+#
+local local
+local local
+local local
diff --git a/tests/sys/cddl/zfs/tests/inheritance/state008.cfg b/tests/sys/cddl/zfs/tests/inheritance/state008.cfg
new file mode 100644
index 000000000000..179158f2fb6b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/state008.cfg
@@ -0,0 +1,39 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# *** ASSERTION DESCRIPTION ***
+#
+# No command is actually run (hence '-:-') but rather this state file is
+# used to verify that when we create a dataset hierarchy that the
+# properties of each dataset have their default values.
+#
+# *** ASSERTION DESCRIPTION ***
+#
+-:-
+#
+# <prop src before cmd> <prop src after cmd>
+default default
+default default
+default default
diff --git a/tests/sys/cddl/zfs/tests/inheritance/state009.cfg b/tests/sys/cddl/zfs/tests/inheritance/state009.cfg
new file mode 100644
index 000000000000..0f7a81f5bd0d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/state009.cfg
@@ -0,0 +1,52 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# *** ASSERTION DESCRIPTION ***
+#
+# Verify that executing 'zfs inherit' at different levels of a
+# data hierarchy where the properties of each dataset have their
+# default values leaves the values unchanged.
+#
+# *** ASSERTION DESCRIPTION ***
+#
+#
+testpool.inherit:inherit
+#
+default default
+default default
+default default
+#
+testpool.inherit/TESTCTR:inherit
+#
+default default
+default default
+default default
+#
+testpool.inherit/TESTCTR/TESTFS1:inherit
+#
+default default
+default default
+default default
+#
diff --git a/tests/sys/cddl/zfs/tests/inheritance/state010.cfg b/tests/sys/cddl/zfs/tests/inheritance/state010.cfg
new file mode 100644
index 000000000000..215e4f9eba62
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/state010.cfg
@@ -0,0 +1,51 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# *** ASSERTION DESCRIPTION ***
+#
+# Verify that executing 'zfs inherit -r' at different levels of a
+# data hierarchy where the properties of each dataset have their
+# default values leaves the values unchanged.
+#
+# *** ASSERTION DESCRIPTION ***
+#
+testpool.inherit:inherit -r
+#
+default default
+default default
+default default
+#
+testpool.inherit/TESTCTR:inherit -r
+#
+default default
+default default
+default default
+#
+testpool.inherit/TESTCTR/TESTFS1:inherit -r
+#
+default default
+default default
+default default
+#
diff --git a/tests/sys/cddl/zfs/tests/inheritance/state011.cfg b/tests/sys/cddl/zfs/tests/inheritance/state011.cfg
new file mode 100644
index 000000000000..0d6cb2a66479
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/state011.cfg
@@ -0,0 +1,53 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# *** ASSERTION DESCRIPTION ***
+#
+# Verify that running 'zfs inherit' at each level of the data hierarchy
+# when the bottom filesystem level properties have been set locally has
+# no effect except at the bottom level where the property values are
+# inherited from the middle level dataset.
+#
+# *** ASSERTION DESCRIPTION ***
+#
+#
+testpool.inherit:inherit
+#
+default default
+default default
+local local
+#
+testpool.inherit/TESTCTR:inherit
+#
+default default
+default default
+local local
+#
+testpool.inherit/TESTCTR/TESTFS1:inherit
+#
+default default
+default default
+local default
+#
diff --git a/tests/sys/cddl/zfs/tests/inheritance/state012.cfg b/tests/sys/cddl/zfs/tests/inheritance/state012.cfg
new file mode 100644
index 000000000000..651aa4c2f915
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/state012.cfg
@@ -0,0 +1,57 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# *** ASSERTION DESCRIPTION ***
+#
+# Verify that running 'zfs inherit -r' at each level of the data hierarchy
+# when the bottom filesystem level properties have been set locally results
+# in the top level property values being propogated down the data
+# hierarchy.
+#
+# Executing inherit -r at the middle level and bottom levels after
+# running it at the top level is somewhat redundant as the top level value
+# should propogate down the entire data hierarchy. Done for completeness
+# sake.
+#
+# *** ASSERTION DESCRIPTION ***
+#
+testpool.inherit:inherit -r
+#
+default default
+default default
+local default
+#
+testpool.inherit/TESTCTR:inherit -r
+#
+default default
+default default
+default default
+#
+testpool.inherit/TESTCTR/TESTFS1:inherit -r
+#
+default default
+default default
+default default
+#
diff --git a/tests/sys/cddl/zfs/tests/inheritance/state013.cfg b/tests/sys/cddl/zfs/tests/inheritance/state013.cfg
new file mode 100644
index 000000000000..370cb0f9feb9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/state013.cfg
@@ -0,0 +1,51 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# *** ASSERTION DESCRIPTION ***
+#
+# Verify that executing 'zfs inherit' on each level when the middle
+# and bottom levels properties are set locally results in the middle and
+# bottom levels inheriting values from the next level up in the hierarchy.
+#
+# *** ASSERTION DESCRIPTION ***
+#
+testpool.inherit:inherit
+#
+default default
+local local
+local local
+#
+testpool.inherit/TESTCTR:inherit
+#
+default default
+local default
+local local
+#
+testpool.inherit/TESTCTR/TESTFS1:inherit
+#
+default default
+default default
+local default
+#
diff --git a/tests/sys/cddl/zfs/tests/inheritance/state014.cfg b/tests/sys/cddl/zfs/tests/inheritance/state014.cfg
new file mode 100644
index 000000000000..34fabf81a76e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/state014.cfg
@@ -0,0 +1,57 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# *** ASSERTION DESCRIPTION ***
+#
+# Verify that running 'zfs inherit -r' at each level of the data hierarchy
+# when the bottom and middle level properties have been set locally results
+# in the top level property values being propogated down the data
+# hierarchy.
+#
+# Note : executing inherit -r at the middle level and bottom levels after
+# running it at the top level is somewhat redundant as the top level value
+# should propogate down the entire data hierarchy. Done for completeness
+# sake.
+#
+# *** ASSERTION DESCRIPTION ***
+#
+testpool.inherit:inherit -r
+#
+default default
+local default
+local default
+#
+testpool.inherit/TESTCTR:inherit -r
+#
+default default
+default default
+default default
+#
+testpool.inherit/TESTCTR/TESTFS1:inherit -r
+#
+default default
+default default
+default default
+#
diff --git a/tests/sys/cddl/zfs/tests/inheritance/state015.cfg b/tests/sys/cddl/zfs/tests/inheritance/state015.cfg
new file mode 100644
index 000000000000..ff6f2ae510de
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/state015.cfg
@@ -0,0 +1,61 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# *** ASSERTION DESCRIPTION ***
+#
+# Verify that executing 'zfs inherit' at the top level in the hierarchy
+# when the middle level properties are set locally, and the bottom
+# level has inherited its value from the middle level, results in no change
+# to the top level properties.
+#
+# Executing 'zfs inherit' at the middle level results in the middle level
+# inheriting its value from the top level, and passing the values down to
+# the bottom level.
+#
+# Executing 'zfs inherit' at the bottom level is somewhat redundant but
+# is done for completness sake.
+#
+# *** ASSERTION DESCRIPTION ***
+#
+#
+testpool.inherit:inherit
+#
+default default
+local local
+testpool.inherit/TESTCTR testpool.inherit/TESTCTR
+#
+#
+testpool.inherit/TESTCTR:inherit
+#
+default default
+local default
+testpool.inherit/TESTCTR default
+#
+#
+testpool.inherit/TESTCTR/TESTFS1:inherit
+#
+default default
+default default
+default default
diff --git a/tests/sys/cddl/zfs/tests/inheritance/state016.cfg b/tests/sys/cddl/zfs/tests/inheritance/state016.cfg
new file mode 100644
index 000000000000..e7b4268025bc
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/state016.cfg
@@ -0,0 +1,57 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# *** ASSERTION DESCRIPTION ***
+#
+# Verify that executing 'zfs inherit -r' at the top level in the hierarchy
+# when the middle level properties are set locally, and the bottom
+# level has inherited its value from the middle level, results in no change
+# to the top level properties and the middle and bottom properties changing
+# to the top level (default) values.
+#
+# Executing 'zfs inherit -r' at the bottom and middle levels after executing
+# at the top level is somewhat redundant but ss done for completness sake.
+#
+# *** ASSERTION DESCRIPTION ***
+#
+#
+testpool.inherit:inherit -r
+#
+default default
+local default
+testpool.inherit default
+#
+testpool.inherit/TESTCTR:inherit -r
+#
+default default
+default default
+default default
+#
+testpool.inherit/TESTCTR/TESTFS1:inherit -r
+#
+default default
+default default
+default default
+#
diff --git a/tests/sys/cddl/zfs/tests/inheritance/state017.cfg b/tests/sys/cddl/zfs/tests/inheritance/state017.cfg
new file mode 100644
index 000000000000..ca891a5b5df9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/state017.cfg
@@ -0,0 +1,62 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# *** ASSERTION DESCRIPTION ***
+#
+# Verify that executing 'zfs inherit' at the top level in the hierarchy
+# when the top level and middle level datasets properties are set locally,
+# and the bottom level has inherited its properties from the middle
+# level, results in the top level properties reverting back to their
+# default values.
+#
+# Executing 'zfs inherit' at the middle level results in the middle level
+# inheriting its value from the top level (which is now default), and passing
+# the values down to the bottom level.
+#
+# Executing 'zfs inherit' at the bottom level is somewhat redundant but
+# is done for completness sake.
+#
+# *** ASSERTION DESCRIPTION ***
+#
+#
+testpool.inherit:inherit
+#
+local default
+local local
+testpool.inherit/TESTCTR testpool.inherit/TESTCTR
+#
+#
+testpool.inherit/TESTCTR:inherit
+#
+default default
+local default
+testpool.inherit/TESTCTR default
+#
+#
+testpool.inherit/TESTCTR/TESTFS1:inherit
+#
+default default
+default default
+default default
diff --git a/tests/sys/cddl/zfs/tests/inheritance/state018.cfg b/tests/sys/cddl/zfs/tests/inheritance/state018.cfg
new file mode 100644
index 000000000000..717c5ef0b31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/state018.cfg
@@ -0,0 +1,59 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# *** ASSERTION DESCRIPTION ***
+#
+# Verify that executing 'zfs inherit -r' at the top level in the hierarchy
+# when the top level and middle level datasets properties are set locally,
+# and the bottom level has inherited its properties from the middle
+# level, results in the top level properties reverting back to their
+# default values and being propogated down to the other datasets in the
+# hierarchy.
+#
+# Executing 'zfs inherit -r' at the middle and bottom levels after executing
+# it at the top level is somewhat redundant but is done for completness sake.
+#
+# *** ASSERTION DESCRIPTION ***
+#
+#
+testpool.inherit:inherit -r
+#
+local default
+local default
+testpool.inherit/TESTCTR default
+#
+#
+testpool.inherit/TESTCTR:inherit -r
+#
+default default
+default default
+default default
+#
+#
+testpool.inherit/TESTCTR/TESTFS1:inherit
+#
+default default
+default default
+default default
diff --git a/tests/sys/cddl/zfs/tests/inheritance/state019.cfg b/tests/sys/cddl/zfs/tests/inheritance/state019.cfg
new file mode 100644
index 000000000000..1da746833df3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/state019.cfg
@@ -0,0 +1,58 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# *** ASSERTION DESCRIPTION ***
+#
+# Verify that executing 'zfs inherit' at the top level in the hierarchy
+# when the top level properties are set locally and the middle and bottom
+# datasets have inherited from the top level, results in the top level
+# properties reverting back to their default values, the middle and bottom
+# levels inheriting the changed values.
+#
+# Executing 'zfs inherit' at the middle and bottom levels is somewhat
+# redundant but is done for completness sake.
+#
+# *** ASSERTION DESCRIPTION ***
+#
+testpool.inherit:inherit
+#
+local default
+testpool.inherit default
+testpool.inherit default
+#
+#
+testpool.inherit/TESTCTR:inherit
+#
+default default
+default default
+default default
+#
+#
+testpool.inherit/TESTCTR/TESTFS1:inherit
+#
+default default
+default default
+default default
+#
diff --git a/tests/sys/cddl/zfs/tests/inheritance/state020.cfg b/tests/sys/cddl/zfs/tests/inheritance/state020.cfg
new file mode 100644
index 000000000000..49436f65bdce
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/state020.cfg
@@ -0,0 +1,59 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# *** ASSERTION DESCRIPTION ***
+#
+# Verify that executing 'zfs inherit -r' at the top level in the hierarchy
+# when the top level properties are set locally and the middle and bottom
+# datasets have inherited from the top level, results in the top level
+# properties reverting back to their default values, the middle and bottom
+# levels inheriting the changed values.
+#
+# Executing 'zfs inherit -r' at the middle and bottom levels is somewhat
+# redundant but is done for completness sake.
+#
+# *** ASSERTION DESCRIPTION ***
+#
+#
+testpool.inherit:inherit -r
+#
+local default
+testpool.inherit default
+testpool.inherit default
+#
+#
+testpool.inherit/TESTCTR:inherit -r
+#
+default default
+default default
+default default
+#
+#
+testpool.inherit/TESTCTR/TESTFS1:inherit -r
+#
+default default
+default default
+default default
+#
diff --git a/tests/sys/cddl/zfs/tests/inheritance/state021.cfg b/tests/sys/cddl/zfs/tests/inheritance/state021.cfg
new file mode 100644
index 000000000000..02eb15ec25ab
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/state021.cfg
@@ -0,0 +1,59 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# *** ASSERTION DESCRIPTION ***
+#
+# Verify that executing 'zfs inherit' at the top level in the hierarchy
+# when the top level and bottom level properties are set locally and the
+# middle dataset has inherited from the top level, results in the top level
+# properties reverting back to their default values, and the middle level
+# inheriting the new top level value.
+#
+# Executing 'zfs inherit' at the bottom level results in it inheriting
+# the middle level values.
+#
+# *** ASSERTION DESCRIPTION ***
+#
+#
+testpool.inherit:inherit
+#
+local default
+testpool.inherit default
+local local
+#
+#
+testpool.inherit/TESTCTR:inherit
+#
+default default
+default default
+local local
+#
+#
+testpool.inherit/TESTCTR/TESTFS1:inherit
+#
+default default
+default default
+local default
+#
diff --git a/tests/sys/cddl/zfs/tests/inheritance/state022.cfg b/tests/sys/cddl/zfs/tests/inheritance/state022.cfg
new file mode 100644
index 000000000000..e3d265d9de4a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/state022.cfg
@@ -0,0 +1,58 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# *** ASSERTION DESCRIPTION ***
+#
+# Verify that executing 'zfs inherit -r' at the top level in the hierarchy
+# when the top level and bottom level properties are set locally and the
+# middle dataset has inherited from the top level, results in the top level
+# properties reverting back to their default values, the middle and bottom
+# levels inheriting the changed values.
+#
+# Executing 'zfs inherit -r' at the middle and bottom levels is somewhat
+# redundant but is done for completness sake.
+#
+# *** ASSERTION DESCRIPTION ***
+#
+testpool.inherit:inherit -r
+#
+local default
+testpool.inherit default
+local default
+#
+#
+testpool.inherit/TESTCTR:inherit -r
+#
+default default
+default default
+default default
+#
+#
+testpool.inherit/TESTCTR/TESTFS1:inherit -r
+#
+default default
+default default
+default default
+#
diff --git a/tests/sys/cddl/zfs/tests/inheritance/state023.cfg b/tests/sys/cddl/zfs/tests/inheritance/state023.cfg
new file mode 100644
index 000000000000..83da984ef940
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/state023.cfg
@@ -0,0 +1,60 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# *** ASSERTION DESCRIPTION ***
+#
+# Verify that executing 'zfs inherit' at the top level in the hierarchy
+# when each levels properties are set locally, results in the top level
+# properties reverting back to their default values.
+#
+# Executing 'zfs inherit' at the middle level results in it inheriting
+# the top levels (now default) values
+#
+# Executing 'zfs inherit' at the bottom level results in it inheriting
+# the middle levels (now default) values
+#
+# *** ASSERTION DESCRIPTION ***
+#
+#
+testpool.inherit:inherit
+#
+local default
+local local
+local local
+#
+#
+testpool.inherit/TESTCTR:inherit
+#
+default default
+local default
+local local
+#
+#
+testpool.inherit/TESTCTR/TESTFS1:inherit
+#
+default default
+default default
+local default
+#
diff --git a/tests/sys/cddl/zfs/tests/inheritance/state024.cfg b/tests/sys/cddl/zfs/tests/inheritance/state024.cfg
new file mode 100644
index 000000000000..74837f172a5d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inheritance/state024.cfg
@@ -0,0 +1,58 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# *** ASSERTION DESCRIPTION ***
+#
+# Verify that executing 'zfs inherit -r' at the top level in the hierarchy
+# when each levels properties are set locally, results in the top level
+# properties reverting back to their default values, and the changed
+# values being propogated down the hierarchy.
+#
+# Executing 'zfs inherit -r' at the middle and bottom levels after doing so
+# at the top level is somewhat redundant but is done for completeness.
+#
+# *** ASSERTION DESCRIPTION ***
+#
+#
+testpool.inherit:inherit -r
+#
+local default
+local default
+local default
+#
+#
+testpool.inherit/TESTCTR:inherit
+#
+default default
+default default
+default default
+#
+#
+testpool.inherit/TESTCTR/TESTFS1:inherit
+#
+default default
+default default
+default default
+#
diff --git a/tests/sys/cddl/zfs/tests/interop/Makefile b/tests/sys/cddl/zfs/tests/interop/Makefile
new file mode 100644
index 000000000000..cd26031d634d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/interop/Makefile
@@ -0,0 +1,16 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/interop
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= interop_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= interop_001_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= interop.cfg
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/interop/cleanup.ksh b/tests/sys/cddl/zfs/tests/interop/cleanup.ksh
new file mode 100644
index 000000000000..abf8f589c76f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/interop/cleanup.ksh
@@ -0,0 +1,67 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+ismounted $TESTPOOL/$TESTFS
+(( $? == 0 )) && log_must $ZFS umount -f $TESTDIR
+destroy_pool $TESTPOOL
+
+$METASTAT $META_DEVICE_ID > /dev/null 2>&1
+if (( $? == 0 )); then
+ log_note "Clearing meta device ($META_DEVICE_ID)"
+ $METACLEAR -f $META_DEVICE_ID > /dev/null 2>&1
+fi
+
+typeset metadb=""
+typeset i=""
+
+metadb=`$METADB | $CUT -f6 | $GREP dev | $UNIQ`
+for i in $metadb; do
+ log_note "Clearing meta db ($i)"
+ $METADB -fd $i > /dev/null 2>&1
+done
+
+# recreate and destroy a zpool over the disks to restore the partitions to
+# normal
+case $DISK_COUNT in
+0)
+ log_note "No disk devices to restore"
+ ;;
+1)
+ log_must cleanup_devices $ZFS_DISK2
+ ;;
+2)
+ log_must cleanup_devices $META_DISK0 $ZFS_DISK2
+ ;;
+*)
+ log_must cleanup_devices $META_DISK0 $META_DISK1 $ZFS_DISK2
+ ;;
+esac
+
+log_pass "Cleanup has been successful"
diff --git a/tests/sys/cddl/zfs/tests/interop/interop.cfg b/tests/sys/cddl/zfs/tests/interop/interop.cfg
new file mode 100644
index 000000000000..919b59067008
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/interop/interop.cfg
@@ -0,0 +1,77 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+set -A disk_array $(find_disks $DISKS)
+case "${#disk_array[*]}" in
+0)
+ DISK_COUNT=0
+ ;;
+1)
+ # We need to repartition the single disk to two slices
+ DISK_COUNT=1
+ META_DISK0=${disk_array[0]}
+ SINGLE_DISK=$META_DISK0
+ META_DISK1=$META_DISK0
+ ZFS_DISK2=$META_DISK0
+ META_SIDE0=${SINGLE_DISK}s0
+ META_SIDE1=${SINGLE_DISK}s1
+ ZFS_SIDE2=${SINGLE_DISK}s3
+ ;;
+2)
+ # We need to repartition the single disk to two slices and allocate
+ # the 2nd disk to ZFS
+ DISK_COUNT=2
+ META_DISK0=${disk_array[0]}
+ SINGLE_DISK=$META_DISK0
+ META_DISK1=$META_DISK0
+ ZFS_DISK2=${disk_array[1]}
+ META_SIDE0=${SINGLE_DISK}s0
+ META_SIDE1=${SINGLE_DISK}s1
+ ZFS_SIDE2=${ZFS_DISK2}s0
+ ;;
+*)
+ # In this case there are at least enough disks to use.
+ DISK_COUNT=3
+ META_DISK0=${disk_array[0]}
+ META_DISK1=${disk_array[1]}
+ ZFS_DISK2=${disk_array[2]}
+ META_SIDE0=${META_DISK0}s0
+ META_SIDE1=${META_DISK1}s0
+ ZFS_SIDE2=${ZFS_DISK2}s0
+ ;;
+esac
+
+export DISK_COUNT META_DISK0 META_DISK1 ZFS_DISK2
+export META_SIDE0 META_SIDE1 ZFS_SIDE2 SINGLE_DISK
+
+export TESTFILE=testfile
+export FS_SIZE=500m
+export META_DEVICE_ID=d99
+export META_DEVICE_PATH=/dev/md/$META_DEVICE_ID
+export FILE_COUNT=20
+export FILE_SIZE=$(( 1024 * 1024 ))
diff --git a/tests/sys/cddl/zfs/tests/interop/interop_001_pos.ksh b/tests/sys/cddl/zfs/tests/interop/interop_001_pos.ksh
new file mode 100644
index 000000000000..1442eb4aef8a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/interop/interop_001_pos.ksh
@@ -0,0 +1,96 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: interop_001_pos
+#
+# DESCRIPTION:
+# Create a SVM device and add this to an existing ZFS pool
+#
+# STRATEGY:
+# 1. Create a SVM metadevice
+# 2. Create a ZFS file system
+# 3. Add SVM metadevice to the ZFS pool
+# 4. Create files and fill the pool.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+
+function cleanup
+{
+ $RM -rf $TESTDIR/*
+}
+
+log_assert "Create a SVM device and add this to an existing ZFS pool"
+
+log_onexit cleanup
+
+# the current size of the test pool
+typeset -i oldsize=`$ZFS get -pH -o value available $TESTPOOL`
+
+log_must $ZPOOL add $TESTPOOL $META_DEVICE_PATH
+log_must $ZPOOL iostat -v | $GREP $META_DEVICE_ID
+
+# the size of the test pool after adding the extra device
+typeset -i newsize=`$ZFS get -pH -o value available $TESTPOOL`
+
+(( $oldsize >= $newsize )) && \
+ log_fail "Pool space available ($oldsize) before adding a new device was "\
+ "larger than the space available ($newsize) afterwards."
+
+log_note "Pool space available was ($oldsize), it's now ($newsize)"
+
+typeset -i odirnum=1
+typeset -i idirnum=0
+typeset -i filenum=0
+typeset -i retval=0
+typeset bg=$TESTDIR/bigdirectory
+
+fill_fs $bg 20 25 $FILE_SIZE $FILE_COUNT
+retval=$?
+
+afterwritepoolavail=`$ZFS get -pH -o value available $TESTPOOL`
+readonly ENOSPC=28
+
+(( $retval == $ENOSPC && $afterwritepoolavail < $oldsize)) && \
+ log_pass "Successfully used ($(( $newsize - $oldsize )) bytes) in "\
+ "pool provided by SVM metadevice"
+
+log_fail "Failed to use space in pool ($(( $newsize - $oldsize ))bytes) "\
+ "provided by SVM metadevice"
diff --git a/tests/sys/cddl/zfs/tests/interop/interop_test.sh b/tests/sys/cddl/zfs/tests/interop/interop_test.sh
new file mode 100755
index 000000000000..ae4c2ea3e76c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/interop/interop_test.sh
@@ -0,0 +1,54 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case interop_001_pos cleanup
+interop_001_pos_head()
+{
+ atf_set "descr" "Create a SVM device and add this to an existing ZFS pool"
+ atf_set "require.progs" "ksh93 metadb zfs metaclear metastat metainit zpool"
+}
+interop_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/interop.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/interop_001_pos.ksh || atf_fail "Testcase failed"
+}
+interop_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/interop.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case interop_001_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/interop/setup.ksh b/tests/sys/cddl/zfs/tests/interop/setup.ksh
new file mode 100644
index 000000000000..484a4877091e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/interop/setup.ksh
@@ -0,0 +1,78 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+case $DISK_COUNT in
+0)
+ log_untested "Need at least 1 disk device for test"
+ ;;
+1)
+ log_note "Partitioning a single disk ($SINGLE_DISK)"
+ ;;
+2)
+ log_note "Partitioning a disks ($SINGLE_DISK) and ($ZFS_DISK2)"
+ ;;
+3)
+ log_note "Partitioning disks ($META_DISK0 $META_DISK1 $ZFS_DISK2)"
+ ;;
+esac
+
+wipe_partition_table $META_DISK0
+log_must set_partition ${META_SIDE0##*s} "" $FS_SIZE $META_DISK0
+if [[ $WRAPPER == *"smi"* && $META_DISK1 == $META_DISK0 ]]; then
+ typeset i=${META_SIDE0##*s}
+ typeset cyl=$(get_endslice $META_DISK0 $i)
+ log_must set_partition ${META_SIDE1##*s} "$cyl" $FS_SIZE $META_DISK1
+else
+ log_must set_partition ${META_SIDE1##*s} "" $FS_SIZE $META_DISK1
+fi
+if [[ $WRAPPER == *"smi"* && $ZFS_DISK2 == $META_DISK1 ]]; then
+ typeset i=${META_SIDE1##*s}
+ typeset cyl=$(get_endslice $META_DISK1 $i)
+ log_must set_partition ${ZFS_SIDE2##*s} "$cyl" $FS_SIZE $ZFS_DISK2
+else
+ log_must set_partition ${ZFS_SIDE2##*s} "" $FS_SIZE $ZFS_DISK2
+fi
+
+create_pool $TESTPOOL $ZFS_SIDE2
+
+$RM -rf $TESTDIR || log_unresolved Could not remove $TESTDIR
+$MKDIR -p $TESTDIR || log_unresolved Could not create $TESTDIR
+
+log_must $ZFS create $TESTPOOL/$TESTFS
+log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+log_must $ZFS set compression=off $TESTPOOL/$TESTFS
+
+log_note "Configuring metadb with $META_SIDE1"
+log_must $METADB -a -f -c 3 $META_SIDE1
+
+log_note "Configure $META_DEVICE_ID with $META_SIDE0"
+log_must $METAINIT $META_DEVICE_ID 1 1 $META_SIDE0
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/inuse/Makefile b/tests/sys/cddl/zfs/tests/inuse/Makefile
new file mode 100644
index 000000000000..81405a420e98
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inuse/Makefile
@@ -0,0 +1,16 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/inuse
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= inuse_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= inuse.cfg
+${PACKAGE}FILES+= inuse_005_pos.ksh
+${PACKAGE}FILES+= inuse_010_neg.ksh
+${PACKAGE}FILES+= setup.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/inuse/inuse.cfg b/tests/sys/cddl/zfs/tests/inuse/inuse.cfg
new file mode 100644
index 000000000000..31be53887d9b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inuse/inuse.cfg
@@ -0,0 +1,121 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_disk_count "$DISKS" 2
+set -A disk_array $(find_disks $DISKS)
+case "${#disk_array[@]}" in
+2)
+ FS_DISK0=${disk_array[0]}
+ FS_DISK1=${disk_array[1]}
+ FS_DISK2=${disk_array[0]}
+ FS_DISK3=${disk_array[1]}
+ FS_SIDE0=${FS_DISK0}p1
+ FS_SIDE1=${FS_DISK0}p2
+ FS_SIDE2=${FS_DISK1}p1
+ FS_SIDE3=${FS_DISK1}p2
+ disk0="$FS_SIDE0"
+ disk1="$FS_SIDE1"
+ disk2="$FS_SIDE2"
+ disk3="$FS_SIDE3"
+ disktargets="$disk0 $disk2"
+ rawdisk0="$FS_SIDE0"
+ rawdisk1="$FS_SIDE1"
+ rawdisk2="$FS_SIDE2"
+ rawdisk3="$FS_SIDE3"
+ rawtargets="$rawdisk0 $rawdisk2"
+ vdisks="$FS_DISK0"
+ sdisks="$FS_DISK1"
+ vslices="$FS_SIDE0 $FS_SIDE1 $FS_SIDE2"
+ sslices="$FS_SIDE3"
+ ;;
+3)
+ FS_DISK0=${disk_array[0]}
+ FS_DISK1=${disk_array[1]}
+ FS_DISK2=${disk_array[2]}
+ FS_DISK3=${disk_array[0]}
+ FS_SIDE0=${FS_DISK0}p1
+ FS_SIDE1=${FS_DISK0}p2
+ FS_SIDE2=${FS_DISK1}p1
+ FS_SIDE3=${FS_DISK2}p1
+ disk0="$FS_SIDE0"
+ disk1="$FS_SIDE1"
+ disk2="$FS_SIDE2"
+ disk3="$FS_SIDE3"
+ disktargets="$disk0 $disk2 $disk3"
+ rawdisk0="$FS_SIDE0"
+ rawdisk1="$FS_SIDE1"
+ rawdisk2="$FS_SIDE2"
+ rawdisk3="$FS_SIDE3"
+ rawtargets="$rawdisk0 $rawdisk2 $rawdisk3"
+ vdisks="$FS_DISK0 $FS_DISK1"
+ sdisks="$FS_DISK2"
+ vslices="$FS_SIDE0 $FS_SIDE2 $FS_SIDE3"
+ sslices="$FS_SIDE1"
+ ;;
+*)
+ FS_DISK0=${disk_array[0]}
+ FS_DISK1=${disk_array[1]}
+ FS_DISK2=${disk_array[2]}
+ FS_DISK3=${disk_array[3]}
+ FS_SIDE0=${FS_DISK0}p1
+ FS_SIDE1=${FS_DISK1}p1
+ FS_SIDE2=${FS_DISK2}p1
+ FS_SIDE3=${FS_DISK3}p1
+ disk0="$FS_SIDE0"
+ disk1="$FS_SIDE1"
+ disk2="$FS_SIDE2"
+ disk3="$FS_SIDE3"
+ disktargets="$disk0 $disk1 $disk2 $disk3"
+ rawdisk0="$FS_SIDE0"
+ rawdisk1="$FS_SIDE1"
+ rawdisk2="$FS_SIDE2"
+ rawdisk3="$FS_SIDE3"
+ rawtargets="$rawdisk0 $rawdisk1 $rawdisk2 $rawdisk3"
+ vdisks="$FS_DISK0 $FS_DISK1 $FS_DISK2"
+ sdisks="$FS_DISK3"
+ vslices="$FS_SIDE0 $FS_SIDE1 $FS_SIDE2"
+ sslices="$FS_SIDE3"
+ ;;
+esac
+
+export FS_DISK0 FS_DISK1 FS_DISK2 FS_DISK3 SINGLE_DISK
+export FS_SIDE0 FS_SIDE1 FS_SIDE2 FS_SIDE3
+export disk0 disk1 disk2 disk3 disktargets
+export rawdisk0 rawdisk1 rawdisk2 rawdisk3 rawtargets
+export vdisks sdisks vslices sslices
+
+export UFSMP=$TESTDIR/testinuseufsdump
+export FS_SIZE=1g
+export PREVDUMPDEV=""
+export PIDUFSDUMP=""
+export PIDUFSRESTORE=""
+# size of block to be written to test file - currently 1mb
+export BLOCK_SIZE=$(( 1024 * 1024 ))
+# number of blocks to write == size of file
+export BLOCK_COUNT=100
+export STF_TIMEOUT=1200 # 20 minutes max.
diff --git a/tests/sys/cddl/zfs/tests/inuse/inuse_005_pos.ksh b/tests/sys/cddl/zfs/tests/inuse/inuse_005_pos.ksh
new file mode 100644
index 000000000000..907ca3454e00
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inuse/inuse_005_pos.ksh
@@ -0,0 +1,69 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: inuse_005_pos
+#
+# DESCRIPTION:
+# newfs will not interfere with devices and spare devices that are in use
+# by active pool.
+#
+# STRATEGY:
+# 1. Create a with the given disk
+# 2. Try to newfs against the disk, verify it fails as expect.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-12-30)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+set_disks
+
+function cleanup
+{
+ poolexists $TESTPOOL1 && destroy_pool $TESTPOOL1
+}
+
+log_assert "Verify newfs over active pool fails."
+
+log_onexit cleanup
+
+create_pool $TESTPOOL1 $DISK0
+log_mustnot $NEWFS -s 1024 "$DISK0"
+destroy_pool $TESTPOOL1
+
+log_pass "Newfs over active pool fails."
diff --git a/tests/sys/cddl/zfs/tests/inuse/inuse_010_neg.ksh b/tests/sys/cddl/zfs/tests/inuse/inuse_010_neg.ksh
new file mode 100644
index 000000000000..6c8ea68a4b25
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inuse/inuse_010_neg.ksh
@@ -0,0 +1,51 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2017 Spectra Logic Corp. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+set_disks
+
+FSSIZE=1024 #reduce filesystem size, just to speed up newfs
+MOUNTPOINT=$TMPDIR/inuse_010_neg_mp
+
+function cleanup
+{
+ poolexists $TESTPOOL && destroy_pool $TESTPOOL
+ $UMOUNT $MOUNTPOINT
+ cleanup_devices $DISK0
+ $RMDIR $MOUNTPOINT
+}
+
+log_onexit cleanup
+
+log_assert "ZFS shouldn't be able to use a disk with a mounted filesystem"
+
+log_must $NEWFS -s $FSSIZE $DISK0
+log_must $MKDIR $MOUNTPOINT
+log_must $MOUNT $DISK0 $MOUNTPOINT
+log_mustnot $ZPOOL create $TESTPOOL $DISK0
+
+log_pass "ZFS cannot use a disk with a mounted filesystem"
diff --git a/tests/sys/cddl/zfs/tests/inuse/inuse_test.sh b/tests/sys/cddl/zfs/tests/inuse/inuse_test.sh
new file mode 100755
index 000000000000..b4b1f927e3bb
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inuse/inuse_test.sh
@@ -0,0 +1,64 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case inuse_005_pos
+inuse_005_pos_head()
+{
+ atf_set "descr" "Verify newfs over active pool fails."
+ atf_set "require.progs" "ksh93 newfs zpool"
+ atf_set "require.user" root
+}
+inuse_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/inuse.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/inuse_005_pos.ksh || atf_fail "Testcase failed"
+}
+
+
+atf_test_case inuse_010_neg
+inuse_010_neg_head()
+{
+ atf_set "descr" "ZFS shouldn't be able to use a disk with a mounted filesystem"
+ atf_set "require.progs" "ksh93 newfs zpool"
+ atf_set "require.user" root
+}
+inuse_010_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/inuse_010_neg.ksh || atf_fail "Testcase failed"
+}
+
+
+atf_init_test_cases()
+{
+ atf_add_test_case inuse_005_pos
+ atf_add_test_case inuse_010_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/inuse/setup.ksh b/tests/sys/cddl/zfs/tests/inuse/setup.ksh
new file mode 100644
index 000000000000..b9834d0002cd
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/inuse/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/iscsi/Makefile b/tests/sys/cddl/zfs/tests/iscsi/Makefile
new file mode 100644
index 000000000000..c531c390e4fe
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/iscsi/Makefile
@@ -0,0 +1,22 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/iscsi
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= iscsi_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= iscsi_004_pos.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= iscsi_005_pos.ksh
+${PACKAGE}FILES+= iscsi_001_pos.ksh
+${PACKAGE}FILES+= iscsi_common.kshlib
+${PACKAGE}FILES+= iscsi_002_neg.ksh
+${PACKAGE}FILES+= iscsi_006_neg.ksh
+${PACKAGE}FILES+= iscsi_003_neg.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= iscsi.cfg
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/iscsi/cleanup.ksh b/tests/sys/cddl/zfs/tests/iscsi/cleanup.ksh
new file mode 100644
index 000000000000..38cf053b4841
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/iscsi/cleanup.ksh
@@ -0,0 +1,33 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+iscsitgt_cleanup
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/iscsi/iscsi.cfg b/tests/sys/cddl/zfs/tests/iscsi/iscsi.cfg
new file mode 100644
index 000000000000..46233aace835
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/iscsi/iscsi.cfg
@@ -0,0 +1,29 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export TESTVOL=testvol${TESTCASE_ID}
+export VOLSIZE=512m
+export TESTVOL1=testvol1${TESTCASE_ID}
diff --git a/tests/sys/cddl/zfs/tests/iscsi/iscsi_001_pos.ksh b/tests/sys/cddl/zfs/tests/iscsi/iscsi_001_pos.ksh
new file mode 100644
index 000000000000..364d3cb0b564
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/iscsi/iscsi_001_pos.ksh
@@ -0,0 +1,94 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: iscsi_001_pos
+#
+# DESCRIPTION:
+# Verify setting shareiscsi property on volume will make it an iSCSI
+# target
+#
+# STRATEGY:
+# 1) Create a volume, turn on shareiscsi directly on the volume
+# 2) Check if the target is created or not
+# 3) Destroy the volume, then turn on shareiscsi property on parent
+# filesystem at first
+# 4) Then create the volume, check if the target is created or not
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-12-21)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ datasetexists $TESTPOOL/$TESTVOL && \
+ log_must $ZFS destroy $TESTPOOL/$TESTVOL
+ datasetexists $TESTPOOL/$TESTVOL1 && \
+ log_must $ZFS destroy $TESTPOOL/$TESTVOL1
+ if [[ "off" != $(get_prop shareiscsi $TESTPOOL) ]]; then
+ log_must $ZFS set shareiscsi=off $TESTPOOL
+ fi
+}
+
+log_onexit cleanup
+
+log_assert "Verify that setting shareiscsi property on volume will make it \
+ an iSCSI target as expected."
+
+
+# Check shareiscsi property directly on volume at first
+log_must $ZFS set shareiscsi=off $TESTPOOL
+log_must $ZFS create -V $VOLSIZE $TESTPOOL/$TESTVOL
+log_must $ZFS set shareiscsi=on $TESTPOOL/$TESTVOL
+if ! is_iscsi_target $TESTPOOL/$TESTVOL ; then
+ log_fail "iscsi target is not created via directly turning on \
+ shareiscsi property on volume"
+fi
+
+# Check setting shareiscsi property on parent filesystem also have
+# effects on volume
+log_must $ZFS set shareiscsi=on $TESTPOOL
+log_must $ZFS create -V $VOLSIZE $TESTPOOL/$TESTVOL1
+if ! is_iscsi_target $TESTPOOL/$TESTVOL1 ; then
+ log_fail "iscsi target is not created via turning on \
+ shareiscsi property on parent filesystem"
+fi
+
+log_pass "Verify that setting shareiscsi property on volume will make it \
+ an iSCSI target as expected."
diff --git a/tests/sys/cddl/zfs/tests/iscsi/iscsi_002_neg.ksh b/tests/sys/cddl/zfs/tests/iscsi/iscsi_002_neg.ksh
new file mode 100644
index 000000000000..fa634510b613
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/iscsi/iscsi_002_neg.ksh
@@ -0,0 +1,83 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+# __stc_assertion_start
+#
+# ID: iscsi_002_neg
+#
+# DESCRIPTION:
+# Verify file systems and snapshots can not be shared via iSCSI
+#
+# STRATEGY:
+# 1) Turn on shareiscsi property directly on the filesystem
+# 2) Check if the target is created or not
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-12-21)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ if [[ "off" != $(get_prop shareiscsi $TESTPOOL/$TESTFS) ]]; then
+ log_must $ZFS set shareiscsi=off $TESTPOOL/$TESTFS
+ fi
+ datasetexists $TESTPOOL/$TESTFS@snap && \
+ log_must $ZFS destroy -f $TESTPOOL/$TESTFS@snap
+}
+
+log_onexit cleanup
+
+log_assert "Verify file systems and snapshots can not be shared via iSCSI."
+
+if [[ "off" != $(get_prop shareiscsi $TESTPOOL/$TESTFS) ]]; then
+ log_fail "The default value of shareiscsi should be off."
+fi
+
+# Check shareiscsi property directly on filesystem at first
+log_must $ZFS set shareiscsi=on $TESTPOOL/$TESTFS
+if is_iscsi_target $TESTPOOL/$TESTFS ; then
+ log_fail "shareiscsi property on filesystem makes an iSCSI target \
+ unexpectedly."
+fi
+
+log_must $ZFS snapshot $TESTPOOL/$TESTFS@snap
+if is_iscsi_target $TESTPOOL/$TESTFS@snap ; then
+ log_fail "shareiscsi property on snapshot makes an iSCSI target \
+ unexpectedly."
+fi
+
+log_pass "Verify file systems and snapshots can not be shared via iSCSI."
diff --git a/tests/sys/cddl/zfs/tests/iscsi/iscsi_003_neg.ksh b/tests/sys/cddl/zfs/tests/iscsi/iscsi_003_neg.ksh
new file mode 100644
index 000000000000..eeafa6642676
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/iscsi/iscsi_003_neg.ksh
@@ -0,0 +1,74 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: iscsi_003_neg
+#
+# DESCRIPTION:
+# Verify invalid value of shareiscsi can not be set
+#
+# STRATEGY:
+# 1) verify a set of invalid value of shareiscsi can not be set
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-12-21)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ if [[ "off" != $(get_prop shareiscsi $TESTPOOL/$TESTFS) ]]; then
+ $ZFS set shareiscsi=off $TESTPOOL/$TESTFS
+ fi
+}
+
+log_onexit cleanup
+
+log_assert "Verify invalid value of shareiscsi can not be set"
+
+set -A inval_str "ON" "oN" "oFF" "Off" "0ff" "disK" "tape" "abc" "??" \
+ "type=abc" "type=DISk" "type=type" "TYPE=disk" \
+ "type=on" "type=off" "type=123"
+
+typeset str
+
+for str in ${inval_str[@]}; do
+ log_mustnot $ZFS set shareiscsi=$str $TESTPOOL/$TESTFS
+done
+
+log_pass "Verify invalid value of shareiscsi can not be set"
diff --git a/tests/sys/cddl/zfs/tests/iscsi/iscsi_004_pos.ksh b/tests/sys/cddl/zfs/tests/iscsi/iscsi_004_pos.ksh
new file mode 100644
index 000000000000..57abf40367ba
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/iscsi/iscsi_004_pos.ksh
@@ -0,0 +1,83 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: iscsi_004_pos
+#
+# DESCRIPTION:
+# Verify renaming a volume does not change target's iSCSI name
+#
+# STRATEGY:
+# 1) Create a volume, turn on shareiscsi directly on the volume
+# 2) Save the target's iSCSI name
+# 3) Rename the volume, compare the target's iSCSI name with the original
+# one
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-12-21)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ datasetexists $TESTPOOL/$TESTVOL && \
+ log_must $ZFS destroy $TESTPOOL/$TESTVOL
+ datasetexists $TESTPOOL/$TESTVOL1 && \
+ log_must $ZFS destroy $TESTPOOL/$TESTVOL1
+}
+
+log_onexit cleanup
+
+log_assert "Verify renaming a volume does not change target's iSCSI name."
+
+log_must $ZFS create -V $VOLSIZE -o shareiscsi=on $TESTPOOL/$TESTVOL
+
+typeset iname
+if ! is_iscsi_target $TESTPOOL/$TESTVOL ; then
+ log_fail "iscsi target is not created."
+fi
+
+iname=$(iscsi_name $TESTPOOL/$TESTVOL)
+
+log_must $ZFS rename $TESTPOOL/$TESTVOL $TESTPOOL/$TESTVOL1
+
+if [[ $iname != $(iscsi_name $TESTPOOL/$TESTVOL1) ]]; then
+ log_fail "The iSCSI name is changed after renaming the volume."
+fi
+
+log_pass "Verify renaming a volume does not change target's iSCSI name."
diff --git a/tests/sys/cddl/zfs/tests/iscsi/iscsi_005_pos.ksh b/tests/sys/cddl/zfs/tests/iscsi/iscsi_005_pos.ksh
new file mode 100644
index 000000000000..d5b1d57b3909
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/iscsi/iscsi_005_pos.ksh
@@ -0,0 +1,89 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: iscsi_005_pos
+#
+# DESCRIPTION:
+# Verify export/import pool with iSCSI
+#
+# STRATEGY:
+# 1) Create a volume, turn on shareiscsi directly on the volume
+# 2) Export the pool, check the target is gone after the operation
+# 3) Import the pool, check the target is back and its scsi name
+# not changed
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-12-21)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ datasetexists $TESTPOOL/$TESTVOL && \
+ log_must $ZFS destroy $TESTPOOL/$TESTVOL
+}
+
+log_onexit cleanup
+
+log_assert "Verify export/import have right effects on iSCSI targets."
+
+log_must $ZFS create -V $VOLSIZE -o shareiscsi=on $TESTPOOL/$TESTVOL
+
+typeset iname
+if ! is_iscsi_target $TESTPOOL/$TESTVOL ; then
+ log_fail "iscsi target is not created."
+fi
+
+iname=$(iscsi_name $TESTPOOL/$TESTVOL)
+
+log_must $ZPOOL export $TESTPOOL
+if is_iscsi_target $TESTPOOL/$TESTVOL ; then
+ log_fail "iscsi target is not removed after the pool is exported."
+fi
+
+typeset dir=$(get_device_dir $DISKS)
+log_must $ZPOOL import -d $dir -f $TESTPOOL
+if ! is_iscsi_target $TESTPOOL/$TESTVOL ; then
+ log_fail "iscsi target is not restored after the pool is imported."
+fi
+if [[ $iname != $(iscsi_name $TESTPOOL/$TESTVOL) ]]; then
+ log_fail "The iSCSI name is changed after export/import."
+fi
+
+log_pass "Verify export/import have right effects on iSCSI targets."
diff --git a/tests/sys/cddl/zfs/tests/iscsi/iscsi_006_neg.ksh b/tests/sys/cddl/zfs/tests/iscsi/iscsi_006_neg.ksh
new file mode 100644
index 000000000000..cd71000f3eec
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/iscsi/iscsi_006_neg.ksh
@@ -0,0 +1,83 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+# __stc_assertion_start
+#
+# ID: iscsi_006_neg
+#
+# DESCRIPTION:
+# Verify iscsioptions can not be changed by zfs command
+#
+# STRATEGY:
+# 1) Save iscsioptions first, then change it on purpose
+# 2) Check if the value is really changed
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-12-21)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ datasetexists $TESTPOOL/$TESTVOL && \
+ log_must $ZFS destroy -f $TESTPOOL/$TESTVOL
+}
+
+log_onexit cleanup
+
+log_assert "Verify iscsioptions can not be changed by zfs command."
+
+log_must $ZFS create -V $VOLSIZE -o shareiscsi=on $TESTPOOL/$TESTVOL
+
+typeset ioptions
+if ! is_iscsi_target $TESTPOOL/$TESTVOL ; then
+ log_fail "target is not created."
+fi
+
+# Check iscsioptions can not be seen in the output of 'zfs get all'
+$ZFS get all $TESTPOOL/$TESTVOL | $GREP iscsioptions
+typeset -i ret=$?
+[[ $ret -eq 0 ]] && log_fail "iscsioptions can be seen in ' zfs get all'. "
+
+ioptions=$(get_prop iscsioptions $TESTPOOL/$TESTVOL)
+
+$ZFS set iscsioptions="abc" $TESTPOOL/$TESTVOL
+
+if [[ $ioptions != $(get_prop iscsioptions $TESTPOOL/$TESTVOL) ]]; then
+ log_fail "iscsioptions property can be changed be $ZFS command."
+fi
+
+log_pass "Verify iscsioptions can not be changed by zfs command."
diff --git a/tests/sys/cddl/zfs/tests/iscsi/iscsi_common.kshlib b/tests/sys/cddl/zfs/tests/iscsi/iscsi_common.kshlib
new file mode 100644
index 000000000000..e676eaf50e24
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/iscsi/iscsi_common.kshlib
@@ -0,0 +1,82 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#
+# Check if a volume is a valide iscsi target
+# $1 volume name
+# return 0 if suceeds, otherwise, return 1
+#
+function is_iscsi_target
+{
+ typeset dataset=$1
+ typeset target targets
+
+ [[ -z $dataset ]] && return 1
+
+ targets=$($ISCSITADM list target | $GREP "Target:" | $AWK '{print $2}')
+ [[ -z $targets ]] && return 1
+
+ for target in $targets; do
+ [[ $dataset == $target ]] && return 0
+ done
+
+ return 1
+}
+
+#
+# Get the iSCSI name of a target
+# $1 target name
+#
+function iscsi_name
+{
+ typeset target=$1
+ typeset name
+
+ [[ -z $target ]] && log_fail "No parameter."
+
+ if ! is_iscsi_target $target ; then
+ log_fail "Not a target."
+ fi
+
+ name=$($ISCSITADM list target $target | $GREP "iSCSI Name:" \
+ | $AWK '{print $2}')
+
+ return $name
+}
+
+#
+# Check shareiscsi option is supported or not
+# return 0 if it is supported, otherwise return 1
+#
+function check_shareiscsi
+{
+ $ZFS set 2>&1 | $GREP shareiscsi
+ [[ $? -ne 0 ]] && return 1
+
+ return 0
+}
diff --git a/tests/sys/cddl/zfs/tests/iscsi/iscsi_test.sh b/tests/sys/cddl/zfs/tests/iscsi/iscsi_test.sh
new file mode 100755
index 000000000000..b1c859bcbf07
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/iscsi/iscsi_test.sh
@@ -0,0 +1,186 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case iscsi_001_pos cleanup
+iscsi_001_pos_head()
+{
+ atf_set "descr" "Verify that setting shareiscsi property on volume will make itan iSCSI target as expected."
+ atf_set "require.progs" "ksh93 zfs"
+}
+iscsi_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/iscsi_common.kshlib
+ . $(atf_get_srcdir)/iscsi.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/iscsi_001_pos.ksh || atf_fail "Testcase failed"
+}
+iscsi_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/iscsi_common.kshlib
+ . $(atf_get_srcdir)/iscsi.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case iscsi_002_neg cleanup
+iscsi_002_neg_head()
+{
+ atf_set "descr" "Verify file systems and snapshots can not be shared via iSCSI."
+ atf_set "require.progs" "ksh93 zfs"
+}
+iscsi_002_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/iscsi_common.kshlib
+ . $(atf_get_srcdir)/iscsi.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/iscsi_002_neg.ksh || atf_fail "Testcase failed"
+}
+iscsi_002_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/iscsi_common.kshlib
+ . $(atf_get_srcdir)/iscsi.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case iscsi_003_neg cleanup
+iscsi_003_neg_head()
+{
+ atf_set "descr" "Verify invalid value of shareiscsi can not be set"
+ atf_set "require.progs" "ksh93 zfs"
+}
+iscsi_003_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/iscsi_common.kshlib
+ . $(atf_get_srcdir)/iscsi.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/iscsi_003_neg.ksh || atf_fail "Testcase failed"
+}
+iscsi_003_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/iscsi_common.kshlib
+ . $(atf_get_srcdir)/iscsi.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case iscsi_004_pos cleanup
+iscsi_004_pos_head()
+{
+ atf_set "descr" "Verify renaming a volume does not change target's iSCSI name."
+ atf_set "require.progs" "ksh93 zfs"
+}
+iscsi_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/iscsi_common.kshlib
+ . $(atf_get_srcdir)/iscsi.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/iscsi_004_pos.ksh || atf_fail "Testcase failed"
+}
+iscsi_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/iscsi_common.kshlib
+ . $(atf_get_srcdir)/iscsi.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case iscsi_005_pos cleanup
+iscsi_005_pos_head()
+{
+ atf_set "descr" "Verify export/import have right effects on iSCSI targets."
+ atf_set "require.progs" "ksh93 zfs zpool"
+}
+iscsi_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/iscsi_common.kshlib
+ . $(atf_get_srcdir)/iscsi.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/iscsi_005_pos.ksh || atf_fail "Testcase failed"
+}
+iscsi_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/iscsi_common.kshlib
+ . $(atf_get_srcdir)/iscsi.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case iscsi_006_neg cleanup
+iscsi_006_neg_head()
+{
+ atf_set "descr" "Verify iscsioptions can not be changed by zfs command."
+ atf_set "require.progs" "ksh93 zfs"
+}
+iscsi_006_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/iscsi_common.kshlib
+ . $(atf_get_srcdir)/iscsi.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/iscsi_006_neg.ksh || atf_fail "Testcase failed"
+}
+iscsi_006_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/iscsi_common.kshlib
+ . $(atf_get_srcdir)/iscsi.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case iscsi_001_pos
+ atf_add_test_case iscsi_002_neg
+ atf_add_test_case iscsi_003_neg
+ atf_add_test_case iscsi_004_pos
+ atf_add_test_case iscsi_005_pos
+ atf_add_test_case iscsi_006_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/iscsi/setup.ksh b/tests/sys/cddl/zfs/tests/iscsi/setup.ksh
new file mode 100644
index 000000000000..070cc720f1a4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/iscsi/setup.ksh
@@ -0,0 +1,35 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+iscsitgt_setup
+
+DISK=${DISKS%% *}
+
+default_setup "$DISK"
diff --git a/tests/sys/cddl/zfs/tests/large_files/Makefile b/tests/sys/cddl/zfs/tests/large_files/Makefile
new file mode 100644
index 000000000000..e18bf056e290
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/large_files/Makefile
@@ -0,0 +1,16 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/large_files
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= large_files_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= large_files_001_pos.ksh
+${PACKAGE}FILES+= large_files.cfg
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/large_files/cleanup.ksh b/tests/sys/cddl/zfs/tests/large_files/cleanup.ksh
new file mode 100644
index 000000000000..713cb16bb31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/large_files/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/large_files/large_files.cfg b/tests/sys/cddl/zfs/tests/large_files/large_files.cfg
new file mode 100644
index 000000000000..a2d61f141178
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/large_files/large_files.cfg
@@ -0,0 +1,27 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export TESTFILE=testfile${TESTCASE_ID}
diff --git a/tests/sys/cddl/zfs/tests/large_files/large_files_001_pos.ksh b/tests/sys/cddl/zfs/tests/large_files/large_files_001_pos.ksh
new file mode 100644
index 000000000000..c6ed651017ac
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/large_files/large_files_001_pos.ksh
@@ -0,0 +1,62 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: large_files_001_pos
+#
+# DESCRIPTION:
+# Write a file to the allowable ZFS fs size.
+#
+# STRATEGY:
+# 1. largest_file will write to a file and increase its size
+# to the maximum allowable.
+# 2. The last byte of the file should be accessbile without error.
+# 3. Writing beyond the maximum file size generates an 'errno' of
+# EFBIG.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Write a file to the allowable ZFS fs size."
+
+log_note "Invoke 'largest_file' with $TESTDIR/$TESTFILE"
+log_must $LARGEST_FILE $TESTDIR/$TESTFILE
+
+log_pass "Successfully created a file to the maximum allowable size."
diff --git a/tests/sys/cddl/zfs/tests/large_files/large_files_test.sh b/tests/sys/cddl/zfs/tests/large_files/large_files_test.sh
new file mode 100755
index 000000000000..07dcf536ab90
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/large_files/large_files_test.sh
@@ -0,0 +1,54 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case large_files_001_pos cleanup
+large_files_001_pos_head()
+{
+ atf_set "descr" "Write a file to the allowable ZFS fs size."
+}
+large_files_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/large_files.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/large_files_001_pos.ksh || atf_fail "Testcase failed"
+}
+large_files_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/large_files.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case large_files_001_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/large_files/setup.ksh b/tests/sys/cddl/zfs/tests/large_files/setup.ksh
new file mode 100644
index 000000000000..eec6da535a17
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/large_files/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+disk=${DISKS%% *}
+
+default_setup $disk
diff --git a/tests/sys/cddl/zfs/tests/largest_pool/Makefile b/tests/sys/cddl/zfs/tests/largest_pool/Makefile
new file mode 100644
index 000000000000..12cb14f3a264
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/largest_pool/Makefile
@@ -0,0 +1,14 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/largest_pool
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= largest_pool_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= largest_pool.cfg
+${PACKAGE}FILES+= largest_pool_001_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/largest_pool/largest_pool.cfg b/tests/sys/cddl/zfs/tests/largest_pool/largest_pool.cfg
new file mode 100644
index 000000000000..159b626e1da5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/largest_pool/largest_pool.cfg
@@ -0,0 +1,40 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export TESTVOL=testvol${TESTCASE_ID}
+export STF_TIMEOUT=1800
+export VOL_PATH=/dev/zvol/${TESTPOOL2}/$TESTVOL
+export VOLSIZES=${VOLSIZES-"2pb 5pb 10pb 2eb 5eb 8eb 9eb"}
+
+# There're 3 different prompt messages while create
+# a volume that great than 1TB on 32-bit
+# - volume size exceeds limit for this system. (happy gate)
+# - max volume size is 1TB on 32-bit systems (s10u2)
+# - value is too large (old)
+
+export VOL_LIMIT_KEYWORD1="1TB on 32-bit"
+export VOL_LIMIT_KEYWORD2="value is too large"
+export VOL_LIMIT_KEYWORD3="volume size exceeds limit"
diff --git a/tests/sys/cddl/zfs/tests/largest_pool/largest_pool_001_pos.ksh b/tests/sys/cddl/zfs/tests/largest_pool/largest_pool_001_pos.ksh
new file mode 100644
index 000000000000..f522bcfd1649
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/largest_pool/largest_pool_001_pos.ksh
@@ -0,0 +1,163 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+# ##########################################################################
+#
+# start __stf_assertion__
+#
+# ASSERTION: largest_pool_001
+#
+# DESCRIPTION:
+# The largest pool can be created and a dataset in that
+# pool can be created and mounted.
+#
+# STRATEGY:
+# create a pool which will contain a volume device.
+# create a volume device of desired sizes.
+# create the largest pool allowed using the volume vdev.
+# create and mount a dataset in the largest pool.
+# create some files in the zfs file system.
+# do some zpool list commands and parse the output.
+#
+# end __stf_assertion__
+#
+# ##########################################################################
+
+verify_runnable "global"
+
+#
+# Parse the results of zpool & zfs creation with specified size
+#
+# $1: volume size
+#
+# return value:
+# 0 -> success
+# 1 -> failure
+#
+function parse_expected_output
+{
+ UNITS=`$ECHO $1 | $SED -e 's/^\([0-9].*\)\([a-z].\)/\2/'`
+ case "$UNITS" in
+ 'mb') CHKUNIT="M" ;;
+ 'gb') CHKUNIT="G" ;;
+ 'tb') CHKUNIT="T" ;;
+ 'pb') CHKUNIT="P" ;;
+ 'eb') CHKUNIT="E" ;;
+ *) CHKUNIT="M" ;;
+ esac
+
+ log_note "Detect zpool $TESTPOOL in this test machine."
+ log_must eval "$ZPOOL list $TESTPOOL > $TMPDIR/j.${TESTCASE_ID}"
+ log_must eval "$GREP $TESTPOOL $TMPDIR/j.${TESTCASE_ID} | \
+ $AWK '{print $2}' | $GREP $CHKUNIT"
+
+ log_note "Detect the file system in this test machine."
+ log_must eval "$DF -t zfs -h > $TMPDIR/j.${TESTCASE_ID}"
+ log_must eval "$GREP $TESTPOOL $TMPDIR/j.${TESTCASE_ID} | \
+ $AWK '{print $2}' | $GREP $CHKUNIT"
+
+ return 0
+}
+
+#
+# Check and destroy zfs, volume & zpool remove the temporary files
+#
+function cleanup
+{
+ log_note "Start cleanup the zfs and pool"
+
+ if datasetexists $TESTPOOL/$TESTFS ; then
+ if ismounted $TESTPOOL/$TESTFS ; then
+ log_must $ZFS unmount $TESTPOOL/$TESTFS
+ fi
+ log_must $ZFS destroy $TESTPOOL/$TESTFS
+ fi
+
+ destroy_pool $TESTPOOL
+
+ datasetexists $TESTPOOL2/$TESTVOL && \
+ log_must $ZFS destroy $TESTPOOL2/$TESTVOL
+
+ destroy_pool $TESTPOOL2
+
+ $RM -f $TMPDIR/j.* > /dev/null
+}
+
+log_assert "The largest pool can be created and a dataset in that" \
+ "pool can be created and mounted."
+
+# Set trigger. When the test case exit, cleanup is executed.
+log_onexit cleanup
+
+# -----------------------------------------------------------------------
+# volume sizes with unit designations.
+#
+# Note: specifying the number '1' as size will not give the correct
+# units for 'df'. It must be greater than one.
+# -----------------------------------------------------------------------
+typeset str
+typeset -i ret
+for volsize in $VOLSIZES; do
+ log_note "Create a pool which will contain a volume device"
+ create_pool $TESTPOOL2 "$DISKS"
+
+ log_note "Create a volume device of desired sizes: $volsize"
+ str=$($ZFS create -sV $volsize $TESTPOOL2/$TESTVOL 2>&1)
+ ret=$?
+ if (( ret != 0 )); then
+ if [[ $($ISAINFO -b) == 32 && \
+ $str == *${VOL_LIMIT_KEYWORD1}* || \
+ $str == *${VOL_LIMIT_KEYWORD2}* || \
+ $str == *${VOL_LIMIT_KEYWORD3}* ]]
+ then
+ log_unsupported \
+ "Max volume size is 1TB on 32-bit systems."
+ else
+ log_fail "$ZFS create -sV $volsize $TESTPOOL2/$TESTVOL"
+ fi
+ fi
+
+ log_note "Create the largest pool allowed using the volume vdev"
+ create_pool $TESTPOOL "$VOL_PATH"
+
+ log_note "Create a zfs file system in the largest pool"
+ log_must $ZFS create $TESTPOOL/$TESTFS
+
+ log_note "Parse the execution result"
+ parse_expected_output $volsize
+
+ log_note "unmount this zfs file system $TESTPOOL/$TESTFS"
+ log_must $ZFS unmount $TESTPOOL/$TESTFS
+
+ log_note "Destroy zfs, volume & zpool"
+ log_must $ZFS destroy $TESTPOOL/$TESTFS
+ destroy_pool $TESTPOOL
+ log_must $ZFS destroy $TESTPOOL2/$TESTVOL
+ destroy_pool $TESTPOOL2
+done
+
+log_pass "Dateset can be created, mounted & destroy in largest pool succeeded."
diff --git a/tests/sys/cddl/zfs/tests/largest_pool/largest_pool_test.sh b/tests/sys/cddl/zfs/tests/largest_pool/largest_pool_test.sh
new file mode 100755
index 000000000000..60227a3b4e6f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/largest_pool/largest_pool_test.sh
@@ -0,0 +1,55 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case largest_pool_001_pos cleanup
+largest_pool_001_pos_head()
+{
+ atf_set "descr" "The largest pool can be created and a dataset in thatpool can be created and mounted."
+ atf_set "require.progs" "ksh93 zfs zpool isainfo"
+ atf_set "timeout" 1800
+}
+largest_pool_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/largest_pool.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/largest_pool_001_pos.ksh || atf_fail "Testcase failed"
+}
+largest_pool_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/largest_pool.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case largest_pool_001_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/link_count/Makefile b/tests/sys/cddl/zfs/tests/link_count/Makefile
new file mode 100644
index 000000000000..fd9aab99f355
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/link_count/Makefile
@@ -0,0 +1,16 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/link_count
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= link_count_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= link_count_001.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= link_count.cfg
+${PACKAGE}FILES+= setup.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/link_count/cleanup.ksh b/tests/sys/cddl/zfs/tests/link_count/cleanup.ksh
new file mode 100644
index 000000000000..713cb16bb31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/link_count/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/link_count/link_count.cfg b/tests/sys/cddl/zfs/tests/link_count/link_count.cfg
new file mode 100644
index 000000000000..af0502455819
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/link_count/link_count.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export ITERS=10
+export NUMFILES=10000
diff --git a/tests/sys/cddl/zfs/tests/link_count/link_count_001.ksh b/tests/sys/cddl/zfs/tests/link_count/link_count_001.ksh
new file mode 100644
index 000000000000..e9af179282bc
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/link_count/link_count_001.ksh
@@ -0,0 +1,97 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: link_count_001
+#
+# DESCRIPTION:
+# Verify file link count is zero on zfs
+#
+# STRATEGY:
+# 1. Make sure this test executes on multi-processes system
+# 2. Make zero size files and remove them in the background
+# 3. Call the binary
+# 4. Make sure the files can be removed successfully
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-07-13)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify file link count is zero on zfs"
+
+# Detect and make sure this test must be executed on a multi-process system
+NCPUS=`sysctl -n kern.smp.cpus`
+#NCPUS=`sysctl -a | awk -F '"' '/cpu count="[0-9+]"/ {print $2; exit}'`
+if [[ $? -ne 0 || -z "$NCPUS" || "$NCPUS" -le 1 ]]; then
+ log_unsupported "This test must be executed on a multi-processor system."
+fi
+
+log_must $MKDIR -p ${TESTDIR}/tmp
+
+typeset -i i=0
+while [ $i -lt $NUMFILES ]; do
+ (( i = i + 1 ))
+ $TOUCH ${TESTDIR}/tmp/x$i > /dev/null 2>&1
+done
+
+sleep 3
+
+$RM -f ${TESTDIR}/tmp/x* >/dev/null 2>&1
+
+$RM_LNKCNT_ZERO_FILE ${TESTDIR}/tmp/test${TESTCASE_ID} > /dev/null 2>&1 &
+PID=$!
+log_note "$RM_LNKCNT_ZERO_FILE ${TESTDIR}/tmp/test${TESTCASE_ID} pid: $PID"
+
+i=0
+while [ $i -lt $ITERS ]; do
+ if ! $PGREP $RM_LNKCNT_ZERO_FILE > /dev/null ; then
+ log_note "$RM_LNKCNT_ZERO_FILE completes"
+ break
+ fi
+ log_must $SLEEP 10
+ (( i = i + 1 ))
+done
+
+if $PGREP $RM_LNKCNT_ZERO_FILE > /dev/null; then
+ log_must $KILL -9 $PID
+ log_fail "file link count is zero"
+fi
+
+log_must $RM -f ${TESTDIR}/tmp/test${TESTCASE_ID}*
+
+log_pass "Verify file link count is zero on zfs"
diff --git a/tests/sys/cddl/zfs/tests/link_count/link_count_test.sh b/tests/sys/cddl/zfs/tests/link_count/link_count_test.sh
new file mode 100755
index 000000000000..b0b73b9451c9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/link_count/link_count_test.sh
@@ -0,0 +1,53 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2014 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case link_count_001 cleanup
+link_count_001_head()
+{
+ atf_set "descr" "Verify file link count is zero on zfs"
+ atf_set "require.progs" "ksh93 zfs"
+}
+link_count_001_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/link_count.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/link_count_001.ksh || atf_fail "Testcase failed"
+}
+link_count_001_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/link_count.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case link_count_001
+}
diff --git a/tests/sys/cddl/zfs/tests/link_count/setup.ksh b/tests/sys/cddl/zfs/tests/link_count/setup.ksh
new file mode 100644
index 000000000000..326bc555f71b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/link_count/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/migration/Makefile b/tests/sys/cddl/zfs/tests/migration/Makefile
new file mode 100644
index 000000000000..68c9977d34b7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/migration/Makefile
@@ -0,0 +1,28 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/migration
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= migration_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= migration_011_pos.ksh
+${PACKAGE}FILES+= migration_008_pos.ksh
+${PACKAGE}FILES+= migration_004_pos.ksh
+${PACKAGE}FILES+= migration.cfg
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= migration_005_pos.ksh
+${PACKAGE}FILES+= migration_001_pos.ksh
+${PACKAGE}FILES+= migration_009_pos.ksh
+${PACKAGE}FILES+= migration_010_pos.ksh
+${PACKAGE}FILES+= migration_007_pos.ksh
+${PACKAGE}FILES+= migration_003_pos.ksh
+${PACKAGE}FILES+= migration_012_pos.ksh
+${PACKAGE}FILES+= migration.kshlib
+${PACKAGE}FILES+= migration_006_pos.ksh
+${PACKAGE}FILES+= migration_002_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/migration/cleanup.ksh b/tests/sys/cddl/zfs/tests/migration/cleanup.ksh
new file mode 100644
index 000000000000..b42cd675bce4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/migration/cleanup.ksh
@@ -0,0 +1,52 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+ismounted $NONZFS_TESTDIR ufs
+(( $? == 0 )) && log_must $UMOUNT -f $NONZFS_TESTDIR
+
+ismounted $TESTPOOL/$TESTFS
+[[ $? == 0 ]] && log_must $ZFS umount -f $TESTDIR
+destroy_pool $TESTPOOL
+
+# recreate and destroy a zpool over the disks to restore the partitions to
+# normal
+case $DISK_COUNT in
+0)
+ log_note "No disk devices to restore"
+ ;;
+1)
+ log_must cleanup_devices $ZFS_DISK
+ ;;
+*)
+ log_must cleanup_devices $ZFS_DISK $NONZFS_DISK
+ ;;
+esac
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/migration/migration.cfg b/tests/sys/cddl/zfs/tests/migration/migration.cfg
new file mode 100644
index 000000000000..4b16c3d05629
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/migration/migration.cfg
@@ -0,0 +1,62 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+set -A disk_array $(find_disks $DISKS)
+case "${#disk_array[*]}" in
+0)
+ DISK_COUNT=0
+ ;;
+1)
+ # We need to repartition the single disk to two partitions.
+ DISK_COUNT=1
+ ZFS_DISK=${disk_array[0]}
+ SINGLE_DISK=$ZFS_DISK
+ NONZFS_DISK=$ZFS_DISK
+ ZFSSIDE_DISK=${SINGLE_DISK}p1
+ NONZFSSIDE_DISK=${SINGLE_DISK}p2
+ ;;
+*)
+ # In this case there are at least enough disks to use.
+ DISK_COUNT=2
+ ZFS_DISK=${disk_array[0]}
+ NONZFS_DISK=${disk_array[1]}
+ ZFSSIDE_DISK=${ZFS_DISK}p1
+ NONZFSSIDE_DISK=${NONZFS_DISK}p1
+ ;;
+esac
+
+export DISK_COUNT ZFS_DISK NONZFS_DISK SINGLE_DISK ZFSSIDE_DISK NONZFSSIDE_DISK
+
+export TESTFILE=/etc/passwd
+export NONZFS_TESTDIR=$TESTDIR/nonzfstestdir${TESTCASE_ID}
+tmp=`$SUM $TESTFILE`
+export SUMA=`$ECHO $tmp | $AWK '{print $1}'`
+export SUMB=`$ECHO $tmp | $AWK '{print $2}'`
+export FS_SIZE=1g
+export BNAME=`$BASENAME $TESTFILE`
+export DNAME=`$DIRNAME $TESTFILE`
diff --git a/tests/sys/cddl/zfs/tests/migration/migration.kshlib b/tests/sys/cddl/zfs/tests/migration/migration.kshlib
new file mode 100644
index 000000000000..5c98c6bbd7fc
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/migration/migration.kshlib
@@ -0,0 +1,147 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+#
+# This function creates the test archive for migration.
+#
+# Usage:
+# prepare srcdir cmd
+#
+# Return value: 0 on success
+# 1 on failure
+#
+# Where:
+# srcdir: is the directory where the testfile is
+# cmd: is the command to be executed.
+# E.g.
+# $TAR cf $TESTDIR/tar${TESTCASE_ID}.tar
+#
+function prepare #srcdir cmd
+{
+ typeset srcdir=$1
+ typeset cmd=$2
+ typeset -i retval=0
+
+ cwd=$PWD
+ cd $srcdir
+ (( $? != 0 )) && return 1
+
+ $cmd
+ (( $? != 0 )) && return 1
+
+ cd $cwd
+ (( $? != 0 )) && return 1
+
+ return 0
+}
+
+#
+# This function executes a passed in command and then determines the chksum
+# of the resulting file. The chksum components are checked against the ones
+# passed in to determine if they are equal. If they are equal, 0 is returned
+# otherwise 1 is returned.
+#
+# Usage:
+# migrate destdir oldsuma oldsumb command_to_execute
+#
+# Return value: 0 on success
+# 1 on failure
+#
+# Where:
+# destdir: is the directory where the command is to be executed on
+# oldsuma: is the first part of the values returned by sum
+# oldsumb: is the second part of the values returned by sum
+# cmd: is the command to be executed;
+# E.g.
+# "$TAR xf $TESTDIR/tar${TESTCASE_ID}.tar"
+#
+function migrate #destdir oldsuma oldsumb cmd
+{
+ typeset destdir=$1
+ typeset oldsuma=$2
+ typeset oldsumb=$3
+ typeset cmd=$4
+ typeset -i retval=0
+
+ cwd=$PWD
+ cd $destdir
+ (( $? != 0 )) && return 1
+
+ $cmd
+ (( $? != 0 )) && return 1
+
+ sumy=`$SUM ./$BNAME`
+ suma=`$ECHO $sumy | $AWK '{print $1}'`
+ sumb=`$ECHO $sumy | $AWK '{print $2}'`
+
+ if (( $oldsuma != $suma )); then
+ log_note "$SUM values are not the same"
+ retval=1
+ fi
+
+ if (( $oldsumb != $sumb )); then
+ log_note "$SUM values are not the same"
+ retval=1
+ fi
+
+ cd $cwd
+ (( $? != 0 )) && return 1
+ return $retval
+}
+
+function migrate_cpio
+{
+ typeset destdir=$1
+ typeset archive=$2
+ typeset oldsuma=$3
+ typeset oldsumb=$4
+ typeset -i retval=0
+
+ cwd=$PWD
+ cd $destdir
+ (( $? != 0 )) && return 1
+
+ $CPIO -iv < $archive
+ (( $? != 0 )) && return 1
+
+ sumy=`$SUM ./$BNAME`
+ suma=`$ECHO $sumy | $AWK '{print $1}'`
+ sumb=`$ECHO $sumy | $AWK '{print $2}'`
+
+ if (( $oldsuma != $suma )); then
+ log_note "$SUM values are not the same"
+ retval=1
+ fi
+
+ if (( $oldsumb != $sumb )); then
+ log_note "$SUM values are not the same"
+ retval=1
+ fi
+
+ cd $cwd
+ (( $? != 0 )) && return 1
+ return $retval
+}
diff --git a/tests/sys/cddl/zfs/tests/migration/migration_001_pos.ksh b/tests/sys/cddl/zfs/tests/migration/migration_001_pos.ksh
new file mode 100644
index 000000000000..e2922fc3e39e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/migration/migration_001_pos.ksh
@@ -0,0 +1,75 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/migration/migration.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: migration_001_pos
+#
+# DESCRIPTION:
+# Migrating test file from ZFS fs to ZFS fs using tar.
+#
+# STRATEGY:
+# 1. Calculate chksum of testfile
+# 2. Tar up test file and place on a ZFS filesystem
+# 3. Extract tar contents to a ZFS file system
+# 4. Calculate chksum of extracted file
+# 5. Compare old and new chksums.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ $RM -rf $TESTDIR/tar${TESTCASE_ID}.tar
+ $RM -rf $TESTDIR/$BNAME
+}
+
+log_assert "Migrating test file from ZFS fs to ZFS fs using tar"
+
+log_onexit cleanup
+
+prepare $DNAME "$TAR cf $TESTDIR/tar${TESTCASE_ID}.tar $BNAME"
+(( $? != 0 )) && log_fail "Unable to create src archive"
+
+migrate $TESTDIR $SUMA $SUMB "$TAR xf $TESTDIR/tar${TESTCASE_ID}.tar"
+(( $? != 0 )) && log_fail "Uable to successfully migrate test file from" \
+ "ZFS fs to ZFS fs"
+
+log_pass "Successully migrated test file from ZFS fs to ZFS fs".
diff --git a/tests/sys/cddl/zfs/tests/migration/migration_002_pos.ksh b/tests/sys/cddl/zfs/tests/migration/migration_002_pos.ksh
new file mode 100644
index 000000000000..bc3028b62edd
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/migration/migration_002_pos.ksh
@@ -0,0 +1,75 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/migration/migration.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: migration_002_pos
+#
+# DESCRIPTION:
+# Migrating test file from ZFS fs to UFS fs using tar.
+#
+# STRATEGY:
+# 1. Calculate chksum of testfile
+# 2. Tar up test file and place on a ZFS filesystem
+# 3. Extract tar contents to a UFS file system
+# 4. Calculate chksum of extracted file
+# 5. Compare old and new chksums.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ $RM -rf $TESTDIR/tar${TESTCASE_ID}.tar
+ $RM -rf $NONZFS_TESTDIR/$BNAME
+}
+
+log_assert "Migrating test file from ZFS fs to UFS fs using tar"
+
+log_onexit cleanup
+
+prepare $DNAME "$TAR cf $TESTDIR/tar${TESTCASE_ID}.tar $BNAME"
+(( $? != 0 )) && log_fail "Unable to create src archive"
+
+migrate $NONZFS_TESTDIR $SUMA $SUMB "$TAR xf $TESTDIR/tar${TESTCASE_ID}.tar"
+(( $? != 0 )) && log_fail "Uable to successfully migrate test file from" \
+ "ZFS fs to UFS fs"
+
+log_pass "Successully migrated test file from ZFS fs to UFS fs".
diff --git a/tests/sys/cddl/zfs/tests/migration/migration_003_pos.ksh b/tests/sys/cddl/zfs/tests/migration/migration_003_pos.ksh
new file mode 100644
index 000000000000..004435b36ad6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/migration/migration_003_pos.ksh
@@ -0,0 +1,75 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/migration/migration.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: migration_003_pos
+#
+# DESCRIPTION:
+# Migrating test file from UFS fs to ZFS fs using tar.
+#
+# STRATEGY:
+# 1. Calculate chksum of testfile
+# 2. Tar up test file and place on a UFS filesystem
+# 3. Extract tar contents to a ZFS file system
+# 4. Calculate chksum of extracted file
+# 5. Compare old and new chksums.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ $RM -rf $NONZFS_TESTDIR/tar${TESTCASE_ID}.tar
+ $RM -rf $TESTDIR/$BNAME
+}
+
+log_assert "Migrating test file from UFS fs to ZFS fs using tar"
+
+log_onexit cleanup
+
+prepare $DNAME "$TAR cf $NONZFS_TESTDIR/tar${TESTCASE_ID}.tar $BNAME"
+(( $? != 0 )) && log_fail "Unable to create src archive"
+
+migrate $TESTDIR $SUMA $SUMB "$TAR xvf $NONZFS_TESTDIR/tar${TESTCASE_ID}.tar"
+(( $? != 0 )) && log_fail "Uable to successfully migrate test file from" \
+ "UFS fs to ZFS fs"
+
+log_pass "Successully migrated test file from UFS fs to ZFS fs".
diff --git a/tests/sys/cddl/zfs/tests/migration/migration_004_pos.ksh b/tests/sys/cddl/zfs/tests/migration/migration_004_pos.ksh
new file mode 100644
index 000000000000..50e16ed137d5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/migration/migration_004_pos.ksh
@@ -0,0 +1,82 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/migration/migration.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: migration_004_pos
+#
+# DESCRIPTION:
+# Migrating test file from ZFS fs to ZFS fs using cpio
+#
+# STRATEGY:
+# 1. Calculate chksum of testfile
+# 2. Cpio up test file and place on a ZFS filesystem
+# 3. Extract cpio contents to a ZFS file system
+# 4. Calculate chksum of extracted file
+# 5. Compare old and new chksums.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ $RM -rf $TESTDIR/cpio${TESTCASE_ID}.cpio
+ $RM -rf $TESTDIR/$BNAME
+}
+
+log_assert "Migrating test file from ZFS fs to ZFS fs using cpio"
+
+log_onexit cleanup
+
+cwd=$PWD
+cd $DNAME
+(( $? != 0 )) && log_untested "Could not change directory to $DNAME"
+
+$LS $BNAME | $CPIO -oc > $TESTDIR/cpio${TESTCASE_ID}.cpio
+(( $? != 0 )) && log_failED "Unable to create cpio archive"
+
+cd $cwd
+(( $? != 0 )) && log_untested "Could not change directory to $cwd"
+
+migrate_cpio $TESTDIR "$TESTDIR/cpio${TESTCASE_ID}.cpio" $SUMA $SUMB
+(( $? != 0 )) && log_fail "Uable to successfully migrate test file from" \
+ "ZFS fs to ZFS fs"
+
+log_pass "Successully migrated test file from ZFS fs to ZFS fs".
diff --git a/tests/sys/cddl/zfs/tests/migration/migration_005_pos.ksh b/tests/sys/cddl/zfs/tests/migration/migration_005_pos.ksh
new file mode 100644
index 000000000000..e27a730cf358
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/migration/migration_005_pos.ksh
@@ -0,0 +1,82 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/migration/migration.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: migration_005_pos
+#
+# DESCRIPTION:
+# Migrating test file from ZFS fs to UFS fs using cpio
+#
+# STRATEGY:
+# 1. Calculate chksum of testfile
+# 2. Cpio up test file and place on a ZFS filesystem
+# 3. Extract cpio contents to a UFS file system
+# 4. Calculate chksum of extracted file
+# 5. Compare old and new chksums.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ $RM -rf $TESTDIR/cpio${TESTCASE_ID}.cpio
+ $RM -rf $NONZFS_TESTDIR/$BNAME
+}
+
+log_assert "Migrating test file from ZFS fs to uFS fs using cpio"
+
+log_onexit cleanup
+
+cwd=$PWD
+cd $DNAME
+(( $? != 0 )) && log_untested "Could not change directory to $DNAME"
+
+$LS $BNAME | $CPIO -oc > $TESTDIR/cpio${TESTCASE_ID}.cpio
+(( $? != 0 )) && log_failED "Unable to create cpio archive"
+
+cd $cwd
+(( $? != 0 )) && log_untested "Could not change directory to $cwd"
+
+migrate_cpio $NONZFS_TESTDIR "$TESTDIR/cpio${TESTCASE_ID}.cpio" $SUMA $SUMB
+(( $? != 0 )) && log_fail "Uable to successfully migrate test file from" \
+ "ZFS fs to UFS fs"
+
+log_pass "Successully migrated test file from ZFS fs to UFS fs".
diff --git a/tests/sys/cddl/zfs/tests/migration/migration_006_pos.ksh b/tests/sys/cddl/zfs/tests/migration/migration_006_pos.ksh
new file mode 100644
index 000000000000..09babb624189
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/migration/migration_006_pos.ksh
@@ -0,0 +1,82 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/migration/migration.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: migration_006_pos
+#
+# DESCRIPTION:
+# Migrating test file from UFS fs to ZFS fs using cpio
+#
+# STRATEGY:
+# 1. Calculate chksum of testfile
+# 2. Cpio up test file and place on a UFS filesystem
+# 3. Extract cpio contents to a ZFS file system
+# 4. Calculate chksum of extracted file
+# 5. Compare old and new chksums.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ $RM -rf $NONZFS_TESTDIR/cpio${TESTCASE_ID}.cpio
+ $RM -rf $TESTDIR/$BNAME
+}
+
+log_assert "Migrating test file from UFS fs to ZFS fs using cpio"
+
+log_onexit cleanup
+
+cwd=$PWD
+cd $DNAME
+(( $? != 0 )) && log_untested "Could not change directory to $DNAME"
+
+$LS $BNAME | $CPIO -oc > $NONZFS_TESTDIR/cpio${TESTCASE_ID}.cpio
+(( $? != 0 )) && log_failED "Unable to create cpio archive"
+
+cd $cwd
+(( $? != 0 )) && log_untested "Could not change directory to $cwd"
+
+migrate_cpio $TESTDIR "$NONZFS_TESTDIR/cpio${TESTCASE_ID}.cpio" $SUMA $SUMB
+(( $? != 0 )) && log_fail "Uable to successfully migrate test file from" \
+ "ZFS fs to ZFS fs"
+
+log_pass "Successully migrated test file from UFS fs to ZFS fs".
diff --git a/tests/sys/cddl/zfs/tests/migration/migration_007_pos.ksh b/tests/sys/cddl/zfs/tests/migration/migration_007_pos.ksh
new file mode 100644
index 000000000000..da27f46f4c06
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/migration/migration_007_pos.ksh
@@ -0,0 +1,75 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/migration/migration.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: migration_007_pos
+#
+# DESCRIPTION:
+# Migrating test file from ZFS fs to ZFS fs using dd.
+#
+# STRATEGY:
+# 1. Calculate chksum of testfile
+# 2. Dd up test file and place on a ZFS filesystem
+# 3. Extract dd contents to a ZFS file system
+# 4. Calculate chksum of extracted file
+# 5. Compare old and new chksums.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ $RM -rf $TESTDIR/dd${TESTCASE_ID}.dd
+ $RM -rf $TESTDIR/$BNAME
+}
+
+log_assert "Migrating test file from ZFS fs to ZFS fs using dd"
+
+log_onexit cleanup
+
+prepare $DNAME "$DD if=$BNAME obs=128k of=$TESTDIR/dd${TESTCASE_ID}.dd"
+(( $? != 0 )) && log_fail "Unable to create src archive"
+
+migrate $TESTDIR $SUMA $SUMB "$DD if=$TESTDIR/dd${TESTCASE_ID}.dd obs=128k of=$BNAME"
+(( $? != 0 )) && log_fail "Uable to successfully migrate test file from" \
+ "ZFS fs to ZFS fs"
+
+log_pass "Successully migrated test file from ZFS fs to ZFS fs".
diff --git a/tests/sys/cddl/zfs/tests/migration/migration_008_pos.ksh b/tests/sys/cddl/zfs/tests/migration/migration_008_pos.ksh
new file mode 100644
index 000000000000..8c28b5000667
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/migration/migration_008_pos.ksh
@@ -0,0 +1,75 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/migration/migration.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: migration_008_pos
+#
+# DESCRIPTION:
+# Migrating test file from ZFS fs to UFS fs using dd.
+#
+# STRATEGY:
+# 1. Calculate chksum of testfile
+# 2. Dd up test file and place on a ZFS filesystem
+# 3. Extract dd contents to a UFS file system
+# 4. Calculate chksum of extracted file
+# 5. Compare old and new chksums.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ $RM -rf $TESTDIR/dd${TESTCASE_ID}.dd
+ $RM -rf $NONZFS_TESTDIR/$BNAME
+}
+
+log_assert "Migrating test file from ZFS fs to UFS fs using dd"
+
+log_onexit cleanup
+
+prepare $DNAME "$DD if=$BNAME obs=128k of=$TESTDIR/dd${TESTCASE_ID}.dd"
+(( $? != 0 )) && log_fail "Unable to create src archive"
+
+migrate $NONZFS_TESTDIR $SUMA $SUMB "$DD if=$TESTDIR/dd${TESTCASE_ID}.dd obs=128k of=$BNAME"
+(( $? != 0 )) && log_fail "Uable to successfully migrate test file from" \
+ "ZFS fs to ZFS fs"
+
+log_pass "Successully migrated test file from ZFS fs to UFS fs".
diff --git a/tests/sys/cddl/zfs/tests/migration/migration_009_pos.ksh b/tests/sys/cddl/zfs/tests/migration/migration_009_pos.ksh
new file mode 100644
index 000000000000..5e80089cf1f3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/migration/migration_009_pos.ksh
@@ -0,0 +1,75 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/migration/migration.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: migration_009_pos
+#
+# DESCRIPTION:
+# Migrating test file from UFS fs to ZFS fs using dd.
+#
+# STRATEGY:
+# 1. Calculate chksum of testfile
+# 2. Dd up test file and place on a UFS filesystem
+# 3. Extract dd contents to a ZFS file system
+# 4. Calculate chksum of extracted file
+# 5. Compare old and new chksums.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ $RM -rf $TESTDIR/dd${TESTCASE_ID}.dd
+ $RM -rf $NONZFS_TESTDIR/$BNAME
+}
+
+log_assert "Migrating test file from UFS fs to ZFS fs using dd"
+
+log_onexit cleanup
+
+prepare $DNAME "$DD if=$BNAME obs=128k of=$NONZFS_TESTDIR/dd${TESTCASE_ID}.dd"
+(( $? != 0 )) && log_fail "Unable to create src archive"
+
+migrate $TESTDIR $SUMA $SUMB "$DD if=$NONZFS_TESTDIR/dd${TESTCASE_ID}.dd obs=128k of=$BNAME"
+(( $? != 0 )) && log_fail "Uable to successfully migrate test file from" \
+ "ZFS fs to ZFS fs"
+
+log_pass "Successully migrated test file from UFS fs to ZFS fs".
diff --git a/tests/sys/cddl/zfs/tests/migration/migration_010_pos.ksh b/tests/sys/cddl/zfs/tests/migration/migration_010_pos.ksh
new file mode 100644
index 000000000000..152d6c47ff8c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/migration/migration_010_pos.ksh
@@ -0,0 +1,75 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/migration/migration.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: migration_010_pos
+#
+# DESCRIPTION:
+# Migrating test file from ZFS fs to ZFS fs using cp
+#
+# STRATEGY:
+# 1. Calculate chksum of testfile
+# 2. CP up test file and place on a ZFS filesystem
+# 3. Extract cp contents to a ZFS file system
+# 4. Calculate chksum of extracted file
+# 5. Compare old and new chksums.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ $RM -rf $TESTDIR/cp${TESTCASE_ID}.cp
+ $RM -rf $TESTDIR/$BNAME
+}
+
+log_assert "Migrating test file from ZFS fs to ZFS fs using cp"
+
+log_onexit cleanup
+
+prepare $DNAME "$CP $BNAME $TESTDIR/cp${TESTCASE_ID}.cp"
+(( $? != 0 )) && log_fail "Unable to create src archive"
+
+migrate $TESTDIR $SUMA $SUMB "$CP $TESTDIR/cp${TESTCASE_ID}.cp $BNAME"
+(( $? != 0 )) && log_fail "Uable to successfully migrate test file from" \
+ "ZFS fs to ZFS fs"
+
+log_pass "Successully migrated test file from ZFS fs to ZFS fs".
diff --git a/tests/sys/cddl/zfs/tests/migration/migration_011_pos.ksh b/tests/sys/cddl/zfs/tests/migration/migration_011_pos.ksh
new file mode 100644
index 000000000000..ceedd7796e89
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/migration/migration_011_pos.ksh
@@ -0,0 +1,75 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/migration/migration.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: migration_011_pos
+#
+# DESCRIPTION:
+# Migrating test file from ZFS fs to UFS fs using cp
+#
+# STRATEGY:
+# 1. Calculate chksum of testfile
+# 2. CP up test file and place on a ZFS filesystem
+# 3. Extract cp contents to a UFS file system
+# 4. Calculate chksum of extracted file
+# 5. Compare old and new chksums.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ $RM -rf $NONZFS_TESTDIR/cp${TESTCASE_ID}.cp
+ $RM -rf $TESTDIR/$BNAME
+}
+
+log_assert "Migrating test file from ZFS fs to UFS fs using cp"
+
+log_onexit cleanup
+
+prepare $DNAME "$CP $BNAME $TESTDIR/cp${TESTCASE_ID}.cp"
+(( $? != 0 )) && log_fail "Unable to create src archive"
+
+migrate $NONZFS_TESTDIR $SUMA $SUMB "$CP $TESTDIR/cp${TESTCASE_ID}.cp $BNAME"
+(( $? != 0 )) && log_fail "Uable to successfully migrate test file from" \
+ "ZFS fs to UFS fs"
+
+log_pass "Successully migrated test file from ZFS fs to UFS fs".
diff --git a/tests/sys/cddl/zfs/tests/migration/migration_012_pos.ksh b/tests/sys/cddl/zfs/tests/migration/migration_012_pos.ksh
new file mode 100644
index 000000000000..97fef06abbd6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/migration/migration_012_pos.ksh
@@ -0,0 +1,75 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/migration/migration.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: migration_012_pos
+#
+# DESCRIPTION:
+# Migrating test file from UFS fs to ZFS fs using cp
+#
+# STRATEGY:
+# 1. Calculate chksum of testfile
+# 2. CP up test file and place on a UFS filesystem
+# 3. Extract cp contents to a ZFS file system
+# 4. Calculate chksum of extracted file
+# 5. Compare old and new chksums.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ $RM -rf $TESTDIR/cp${TESTCASE_ID}.cp
+ $RM -rf $NONZFS_TESTDIR/$BNAME
+}
+
+log_assert "Migrating test file from UFS fs to ZFS fs using cp"
+
+log_onexit cleanup
+
+prepare $DNAME "$CP $BNAME $NONZFS_TESTDIR/cp${TESTCASE_ID}.cp"
+(( $? != 0 )) && log_fail "Unable to create src archive"
+
+migrate $TESTDIR $SUMA $SUMB "$CP $NONZFS_TESTDIR/cp${TESTCASE_ID}.cp $BNAME"
+(( $? != 0 )) && log_fail "Uable to successfully migrate test file from" \
+ "UFS fs to ZFS fs"
+
+log_pass "Successully migrated test file from UFS fs to ZFS fs".
diff --git a/tests/sys/cddl/zfs/tests/migration/migration_test.sh b/tests/sys/cddl/zfs/tests/migration/migration_test.sh
new file mode 100755
index 000000000000..4c303676ad05
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/migration/migration_test.sh
@@ -0,0 +1,354 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case migration_001_pos cleanup
+migration_001_pos_head()
+{
+ atf_set "descr" "Migrating test file from ZFS fs to ZFS fs using tar"
+ atf_set "require.progs" "ksh93 zfs"
+}
+migration_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/migration.kshlib
+ . $(atf_get_srcdir)/migration.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/migration_001_pos.ksh || atf_fail "Testcase failed"
+}
+migration_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/migration.kshlib
+ . $(atf_get_srcdir)/migration.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case migration_002_pos cleanup
+migration_002_pos_head()
+{
+ atf_set "descr" "Migrating test file from ZFS fs to UFS fs using tar"
+ atf_set "require.progs" "ksh93 zfs"
+}
+migration_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/migration.kshlib
+ . $(atf_get_srcdir)/migration.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/migration_002_pos.ksh || atf_fail "Testcase failed"
+}
+migration_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/migration.kshlib
+ . $(atf_get_srcdir)/migration.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case migration_003_pos cleanup
+migration_003_pos_head()
+{
+ atf_set "descr" "Migrating test file from UFS fs to ZFS fs using tar"
+ atf_set "require.progs" "ksh93 zfs"
+}
+migration_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/migration.kshlib
+ . $(atf_get_srcdir)/migration.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/migration_003_pos.ksh || atf_fail "Testcase failed"
+}
+migration_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/migration.kshlib
+ . $(atf_get_srcdir)/migration.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case migration_004_pos cleanup
+migration_004_pos_head()
+{
+ atf_set "descr" "Migrating test file from ZFS fs to ZFS fs using cpio"
+ atf_set "require.progs" "ksh93 zfs"
+}
+migration_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/migration.kshlib
+ . $(atf_get_srcdir)/migration.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/migration_004_pos.ksh || atf_fail "Testcase failed"
+}
+migration_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/migration.kshlib
+ . $(atf_get_srcdir)/migration.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case migration_005_pos cleanup
+migration_005_pos_head()
+{
+ atf_set "descr" "Migrating test file from ZFS fs to uFS fs using cpio"
+ atf_set "require.progs" "ksh93 zfs"
+}
+migration_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/migration.kshlib
+ . $(atf_get_srcdir)/migration.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/migration_005_pos.ksh || atf_fail "Testcase failed"
+}
+migration_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/migration.kshlib
+ . $(atf_get_srcdir)/migration.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case migration_006_pos cleanup
+migration_006_pos_head()
+{
+ atf_set "descr" "Migrating test file from UFS fs to ZFS fs using cpio"
+ atf_set "require.progs" "ksh93 zfs"
+}
+migration_006_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/migration.kshlib
+ . $(atf_get_srcdir)/migration.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/migration_006_pos.ksh || atf_fail "Testcase failed"
+}
+migration_006_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/migration.kshlib
+ . $(atf_get_srcdir)/migration.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case migration_007_pos cleanup
+migration_007_pos_head()
+{
+ atf_set "descr" "Migrating test file from ZFS fs to ZFS fs using dd"
+ atf_set "require.progs" "ksh93 zfs"
+}
+migration_007_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/migration.kshlib
+ . $(atf_get_srcdir)/migration.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/migration_007_pos.ksh || atf_fail "Testcase failed"
+}
+migration_007_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/migration.kshlib
+ . $(atf_get_srcdir)/migration.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case migration_008_pos cleanup
+migration_008_pos_head()
+{
+ atf_set "descr" "Migrating test file from ZFS fs to UFS fs using dd"
+ atf_set "require.progs" "ksh93 zfs"
+}
+migration_008_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/migration.kshlib
+ . $(atf_get_srcdir)/migration.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/migration_008_pos.ksh || atf_fail "Testcase failed"
+}
+migration_008_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/migration.kshlib
+ . $(atf_get_srcdir)/migration.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case migration_009_pos cleanup
+migration_009_pos_head()
+{
+ atf_set "descr" "Migrating test file from UFS fs to ZFS fs using dd"
+ atf_set "require.progs" "ksh93 zfs"
+}
+migration_009_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/migration.kshlib
+ . $(atf_get_srcdir)/migration.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/migration_009_pos.ksh || atf_fail "Testcase failed"
+}
+migration_009_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/migration.kshlib
+ . $(atf_get_srcdir)/migration.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case migration_010_pos cleanup
+migration_010_pos_head()
+{
+ atf_set "descr" "Migrating test file from ZFS fs to ZFS fs using cp"
+ atf_set "require.progs" "ksh93 zfs"
+}
+migration_010_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/migration.kshlib
+ . $(atf_get_srcdir)/migration.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/migration_010_pos.ksh || atf_fail "Testcase failed"
+}
+migration_010_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/migration.kshlib
+ . $(atf_get_srcdir)/migration.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case migration_011_pos cleanup
+migration_011_pos_head()
+{
+ atf_set "descr" "Migrating test file from ZFS fs to UFS fs using cp"
+ atf_set "require.progs" "ksh93 zfs"
+}
+migration_011_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/migration.kshlib
+ . $(atf_get_srcdir)/migration.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/migration_011_pos.ksh || atf_fail "Testcase failed"
+}
+migration_011_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/migration.kshlib
+ . $(atf_get_srcdir)/migration.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case migration_012_pos cleanup
+migration_012_pos_head()
+{
+ atf_set "descr" "Migrating test file from UFS fs to ZFS fs using cp"
+ atf_set "require.progs" "ksh93 zfs"
+}
+migration_012_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/migration.kshlib
+ . $(atf_get_srcdir)/migration.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/migration_012_pos.ksh || atf_fail "Testcase failed"
+}
+migration_012_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/migration.kshlib
+ . $(atf_get_srcdir)/migration.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case migration_001_pos
+ atf_add_test_case migration_002_pos
+ atf_add_test_case migration_003_pos
+ atf_add_test_case migration_004_pos
+ atf_add_test_case migration_005_pos
+ atf_add_test_case migration_006_pos
+ atf_add_test_case migration_007_pos
+ atf_add_test_case migration_008_pos
+ atf_add_test_case migration_009_pos
+ atf_add_test_case migration_010_pos
+ atf_add_test_case migration_011_pos
+ atf_add_test_case migration_012_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/migration/setup.ksh b/tests/sys/cddl/zfs/tests/migration/setup.ksh
new file mode 100644
index 000000000000..d15063e75917
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/migration/setup.ksh
@@ -0,0 +1,64 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+case $DISK_COUNT in
+0)
+ log_untested "Need at least 1 disk device for test"
+ ;;
+1)
+ log_note "Partitioning a single disk ($SINGLE_DISK)"
+ ;;
+*)
+ log_note "Partitioning disks ($ZFS_DISK $NONZFS_DISK)"
+ ;;
+esac
+
+wipe_partition_table $ZFS_DISK $NONZFS_DISK
+set_partition ${ZFSSIDE_DISK##*p} "" $FS_SIZE $ZFS_DISK
+set_partition ${NONZFSSIDE_DISK##*p} "" $FS_SIZE $NONZFS_DISK
+
+log_must create_pool $TESTPOOL "$ZFSSIDE_DISK"
+
+$RM -rf $TESTDIR || log_unresolved Could not remove $TESTDIR
+$MKDIR -p $TESTDIR || log_unresolved Could not create $TESTDIR
+
+log_must $ZFS create $TESTPOOL/$TESTFS
+log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+
+$RM -rf $NONZFS_TESTDIR || log_unresolved Could not remove $NONZFS_TESTDIR
+$MKDIR -p $NONZFS_TESTDIR || log_unresolved Could not create $NONZFS_TESTDIR
+
+$NEWFS $NONZFSSIDE_DISK
+(( $? != 0 )) &&
+ log_untested "Unable to setup a UFS file system"
+
+log_must $MOUNT $NONZFSSIDE_DISK $NONZFS_TESTDIR
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/mmap/Makefile b/tests/sys/cddl/zfs/tests/mmap/Makefile
new file mode 100644
index 000000000000..494b84b8e155
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/mmap/Makefile
@@ -0,0 +1,17 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/mmap
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= mmap_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= mmap.cfg
+${PACKAGE}FILES+= mmap_read_001_pos.ksh
+${PACKAGE}FILES+= mmap_write_001_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/mmap/cleanup.ksh b/tests/sys/cddl/zfs/tests/mmap/cleanup.ksh
new file mode 100644
index 000000000000..713cb16bb31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/mmap/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/mmap/mmap.cfg b/tests/sys/cddl/zfs/tests/mmap/mmap.cfg
new file mode 100644
index 000000000000..a2d61f141178
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/mmap/mmap.cfg
@@ -0,0 +1,27 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export TESTFILE=testfile${TESTCASE_ID}
diff --git a/tests/sys/cddl/zfs/tests/mmap/mmap_read_001_pos.ksh b/tests/sys/cddl/zfs/tests/mmap/mmap_read_001_pos.ksh
new file mode 100644
index 000000000000..94539bacc013
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/mmap/mmap_read_001_pos.ksh
@@ -0,0 +1,64 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+###########################################################################
+#
+# __stc_assertion_start
+#
+# ID: read_mmap_001_pos
+#
+# DESCRIPTION:
+# read()s from mmap()'ed file contain correct data.
+#
+# STRATEGY:
+# 1. Create a pool & dataset
+# 2. Call readmmap binary
+# 3. unmount this file system
+# 4. Verify the integrity of this pool & dateset
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "read()s from mmap()'ed file contain correct data."
+
+log_must $CHMOD 777 $TESTDIR
+log_must $READMMAP $TESTDIR/$TESTFILE
+log_must $ZFS unmount $TESTPOOL/$TESTFS
+
+typeset dir=$(get_device_dir $DISKS)
+verify_filesys "$TESTPOOL" "$TESTPOOL/$TESTFS" "$dir"
+
+log_pass "read(2) calls from a mmap(2)'ed file succeeded."
diff --git a/tests/sys/cddl/zfs/tests/mmap/mmap_test.sh b/tests/sys/cddl/zfs/tests/mmap/mmap_test.sh
new file mode 100755
index 000000000000..beab25030730
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/mmap/mmap_test.sh
@@ -0,0 +1,78 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case mmap_read_001_pos cleanup
+mmap_read_001_pos_head()
+{
+ atf_set "descr" "read()s from mmap()'ed file contain correct data."
+ atf_set "require.progs" "ksh93 zfs zdb"
+}
+mmap_read_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/mmap.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/mmap_read_001_pos.ksh || atf_fail "Testcase failed"
+}
+mmap_read_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/mmap.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case mmap_write_001_pos cleanup
+mmap_write_001_pos_head()
+{
+ atf_set "descr" "write()s to a file and mmap() that file at the same time does notresult in a deadlock."
+}
+mmap_write_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/mmap.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/mmap_write_001_pos.ksh || atf_fail "Testcase failed"
+}
+mmap_write_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/mmap.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case mmap_read_001_pos
+ atf_add_test_case mmap_write_001_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/mmap/mmap_write_001_pos.ksh b/tests/sys/cddl/zfs/tests/mmap/mmap_write_001_pos.ksh
new file mode 100644
index 000000000000..9fd4d332b63c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/mmap/mmap_write_001_pos.ksh
@@ -0,0 +1,88 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+# ##########################################################################
+#
+# __stc_assertion_start
+#
+# ID: mmap_write_001_pos
+#
+# DESCRIPTION:
+# Writing to a file and mmaping that file at the
+# same time does not result in a deadlock.
+#
+# STRATEGY:
+# 1. Make sure this test executes on multi-processes system.
+# 2. Call mmapwrite binary.
+# 3. wait 120s and make sure the test file existed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+# Default is 120 seconds or 2 minutes
+WAITTIME=${WAITTIME-120}
+
+log_assert "write()s to a file and mmap() that file at the same time does not "\
+ "result in a deadlock."
+
+# Detect and make sure this test must be executed on a multi-process system
+NCPUS=`sysctl -a | awk -F '"' '/cpu count="[0-9+]"/ {print $2; exit}'`
+if [[ $? -ne 0 || -z $NCPUS || $NCPUS -le 1 ]]; then
+ log_unsupported "This test must be executed on a multi-processor system."
+fi
+
+log_must $CHMOD 777 $TESTDIR
+$MMAPWRITE $TESTDIR/$TESTFILE &
+PID_MMAPWRITE=$!
+log_note "$MMAPWRITE $TESTDIR/$TESTFILE pid: $PID_MMAPWRITE"
+log_must $SLEEP 10
+
+typeset -i i=0
+while (( i < $WAITTIME )); do
+ if ! $PS -ef | $PGREP $MMAPWRITE > /dev/null ; then
+ log_must $WAIT $PID_MMAPWRITE
+ break
+ fi
+ $SLEEP 1
+ (( i += 1 ))
+done
+
+if $PS -ef | $PGREP $MMAPWRITE > /dev/null ; then
+ log_must $KILL -9 $PID_MMAPWRITE
+fi
+log_must $LS -l $TESTDIR/$TESTFILE
+
+log_pass "write(2) a mmap(2)'ing file succeeded."
diff --git a/tests/sys/cddl/zfs/tests/mmap/setup.ksh b/tests/sys/cddl/zfs/tests/mmap/setup.ksh
new file mode 100644
index 000000000000..326bc555f71b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/mmap/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/mount/Makefile b/tests/sys/cddl/zfs/tests/mount/Makefile
new file mode 100644
index 000000000000..6b00ecf96c1a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/mount/Makefile
@@ -0,0 +1,17 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/mount
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= mount_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= vars.cfg
+${PACKAGE}FILES+= mounttest.ksh
+${PACKAGE}FILES+= mount_test.sh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= setup.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/mount/cleanup.ksh b/tests/sys/cddl/zfs/tests/mount/cleanup.ksh
new file mode 100644
index 000000000000..713cb16bb31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/mount/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/mount/mount_test.sh b/tests/sys/cddl/zfs/tests/mount/mount_test.sh
new file mode 100755
index 000000000000..499707a56c31
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/mount/mount_test.sh
@@ -0,0 +1,84 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2014 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case umount_001 cleanup
+umount_001_head()
+{
+ atf_set "descr" "zfs umount should unmount a file system"
+ atf_set "require.progs" "ksh93 zfs"
+}
+umount_001_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/vars.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/mounttest.ksh -u umount || \
+ atf_fail "Testcase failed"
+}
+umount_001_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/vars.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case umountall_001 cleanup
+umountall_001_head()
+{
+ atf_set "descr" "zfs umount -a should unmount all ZFS file systems"
+ atf_set "require.progs" "ksh93 zfs"
+}
+umountall_001_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/vars.cfg
+
+ if other_pools_exist; then
+ atf_skip "Can't test unmount -a with existing pools"
+ fi
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/mounttest.ksh -u 'umount -a' || \
+ atf_fail "Testcase failed"
+}
+umountall_001_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/vars.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case umount_001
+ atf_add_test_case umountall_001
+}
diff --git a/tests/sys/cddl/zfs/tests/mount/mounttest.ksh b/tests/sys/cddl/zfs/tests/mount/mounttest.ksh
new file mode 100644
index 000000000000..971fbea34de1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/mount/mounttest.ksh
@@ -0,0 +1,96 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: mounttest
+#
+# DESCRIPTION:
+# zfs mount and unmount commands should mount and unmount existing
+# file systems.
+#
+# STRATEGY:
+# 1. Call zfs mount command
+# 2. Make sure the file systems were mounted
+# 3. Call zfs unmount command
+# 4. Make sure the file systems were unmounted
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+
+unmountcmd=""
+while getopts u: OPT ; do
+ case $OPT in
+ u) unmountcmd=$OPTARG
+ ;;
+ ?) log_fail Usage: $0 [-u unmountcmd]
+ ;;
+ esac
+done
+
+log_note Mount file systems
+typeset -i i=1
+for fs in $TESTFSS ; do
+ log_must $ZFS mount $fs
+ ((i = i + 1))
+done
+
+log_note Make sure the file systems were mounted
+for fs in $TESTFSS ; do
+ mounted $fs || log_fail File system $fs not mounted
+done
+
+log_note Unmount the file systems
+if [[ $unmountcmd = *all ]] ; then
+ log_must $ZFS $unmountcmd -F zfs
+else
+ if [[ $unmountcmd = *-a ]] ; then
+ log_must $ZFS $unmountcmd
+ else
+ for fs in $TESTFSS ; do
+ log_must $ZFS $unmountcmd $fs
+ done
+ fi
+fi
+
+log_note Make sure the file systems were unmounted
+for fs in $TESTFSS ; do
+ unmounted $fs || log_fail File system $fs not unmounted
+done
+
+log_pass All file systems are unmounted
diff --git a/tests/sys/cddl/zfs/tests/mount/setup.ksh b/tests/sys/cddl/zfs/tests/mount/setup.ksh
new file mode 100644
index 000000000000..98d7a44798e7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/mount/setup.ksh
@@ -0,0 +1,64 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+create_pool $TESTPOOL "$DISK"
+
+log_note Create file systems with mountpoints, so they are mounted automatically
+i=1
+TESTFSS=""
+TESTDIRS=""
+while [ $i -le $FS_CNT ] ; do
+ dir=$TESTDIR.$i
+ fs=$TESTPOOL/$TESTFS.$i
+
+ log_pos $RM -rf $dir || log_unresolved Could not remove $dir
+
+ log_pos $MKDIR -p $dir || log_unresolved Could not create $dir
+
+ TESTDIRS="$TESTDIRS $dir"
+
+ log_must $ZFS create $fs
+ log_must $ZFS set mountpoint=$dir $fs
+
+ TESTFSS="$TESTFSS $fs"
+
+ log_note Make sure file system $fs was mounted
+ mounted $fs || log_fail File system $fs is not mounted
+
+ log_note Unmount the file system
+ log_must $ZFS unmount $fs
+
+ log_note Make sure file system $fs is unmounted
+ unmounted $fs || log_fail File system $fs is not unmounted
+
+ (( i = i + 1 ))
+done
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/mount/vars.cfg b/tests/sys/cddl/zfs/tests/mount/vars.cfg
new file mode 100644
index 000000000000..ff7bb300a87d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/mount/vars.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export FS_CNT=3
+
diff --git a/tests/sys/cddl/zfs/tests/mv_files/Makefile b/tests/sys/cddl/zfs/tests/mv_files/Makefile
new file mode 100644
index 000000000000..b81bf3fa49a9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/mv_files/Makefile
@@ -0,0 +1,18 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/mv_files
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= mv_files_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= mv_files_common.kshlib
+${PACKAGE}FILES+= mv_files_002_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= mv_files_001_pos.ksh
+${PACKAGE}FILES+= mv_files.cfg
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/mv_files/cleanup.ksh b/tests/sys/cddl/zfs/tests/mv_files/cleanup.ksh
new file mode 100644
index 000000000000..1f132781335e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/mv_files/cleanup.ksh
@@ -0,0 +1,34 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+[[ -f $TMPDIR/exitsZero.ksh ]] && \
+ log_must $RM -f $TMPDIR/exitsZero.ksh
+[[ -f $TMPDIR/testbackgprocs.ksh ]] && \
+ log_must $RM -f $TMPDIR/testbackgprocs.ksh
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/mv_files/mv_files.cfg b/tests/sys/cddl/zfs/tests/mv_files/mv_files.cfg
new file mode 100644
index 000000000000..69c123523f28
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/mv_files/mv_files.cfg
@@ -0,0 +1,41 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export DISK=${DISKS%% *}
+
+export TESTFILE=testfile${TESTCASE_ID}
+
+export TESTDIR_TGT=${TEST_BASE_DIR%%/}/testdir_tgt${TESTCASE_ID}
+export TESTFS_TGT=testzfs_tgt${TESTCASE_ID}
+export OLDDIR=$TESTDIR/olddir${TESTCASE_ID}
+export NEWDIR_IN_FS=$TESTDIR/newdir${TESTCASE_ID}
+export NEWDIR_ACROSS_FS=$TESTDIR_TGT/newdir${TESTCASE_ID}
+
+export MVNUMFILES=2000 # <number of files to start>
+export MVNUMINCR=1000 # <number of files to be increased to>
+export GANGPIDS=50 # <number of limit for parallel background running process>
+
+export STF_TIMEOUT=1200
diff --git a/tests/sys/cddl/zfs/tests/mv_files/mv_files_001_pos.ksh b/tests/sys/cddl/zfs/tests/mv_files/mv_files_001_pos.ksh
new file mode 100644
index 000000000000..9b9ca84a78d5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/mv_files/mv_files_001_pos.ksh
@@ -0,0 +1,79 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/mv_files/mv_files_common.kshlib
+
+##############################################################################
+#
+# __stc_assertion_start
+#
+# ID: mv_files_001_pos
+#
+# DESCRIPTION:
+# Doing a 'mv' of a large amount of files between two directories
+# within a zfs filesystem works without errors.
+#
+# STRATEGY:
+#
+# 1. create a pool and a zfs filesystem
+# 2. create two directories within the filesystem
+# 3. create a large number of files within a directory
+# 4. Move files from one directory to another and back again
+# 5. validate file number
+# 6. increase the number of files to $MVNUMFILES + $MVNUMINCR
+# 7. repeat steps 3,4,5,6 above
+# 8. verify the data integrity
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ PIDS=""
+ $RM -f $OLDDIR/* >/dev/null 2>&1
+ $RM -f $NEWDIR_IN_FS/* >/dev/null 2>&1
+}
+
+log_assert "Doing a 'mv' of a large amount of files within a zfs filesystem" \
+ "works without errors."
+
+log_onexit cleanup
+
+mv_test $OLDDIR $NEWDIR_IN_FS
+(($? != 0 )) && \
+ log_fail "'mv' test failed to complete."
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/mv_files/mv_files_002_pos.ksh b/tests/sys/cddl/zfs/tests/mv_files/mv_files_002_pos.ksh
new file mode 100644
index 000000000000..5fc282137587
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/mv_files/mv_files_002_pos.ksh
@@ -0,0 +1,80 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/mv_files/mv_files_common.kshlib
+
+##############################################################################
+#
+# __stc_assertion_start
+#
+# ID: mv_files_002_pos
+#
+# DESCRIPTION:
+# Doing a 'mv' of a large amount of files between two directories across
+# two zfs filesystems works without errors.
+#
+# STRATEGY:
+#
+# 1. create a pool and two zfs filesystems
+# 2. create a directory in each filesystem
+# 3. create a large number of files in a directory of a filesystem
+# 4. Move files from the directory to another directory in another
+# filesystem and back again
+# 5. validate file number
+# 6. increase the number of files to $MVNUMFILES + $MVNUMINCR
+# 7. repeat steps 3,4,5,6 above
+# 8. verify the data integrity
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ PIDS=""
+ $RM -f $OLDDIR/* >/dev/null 2>&1
+ $RM -f $NEWDIR_ACROSS_FS/* >/dev/null 2>&1
+}
+
+log_assert "Doing a 'mv' of a large amount of files across two zfs filesystems" \
+ "works without errors."
+
+log_onexit cleanup
+
+mv_test $OLDDIR $NEWDIR_ACROSS_FS
+(($? != 0 )) && \
+ log_fail "'mv' test failed to complete."
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/mv_files/mv_files_common.kshlib b/tests/sys/cddl/zfs/tests/mv_files/mv_files_common.kshlib
new file mode 100644
index 000000000000..2816cd10ba78
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/mv_files/mv_files_common.kshlib
@@ -0,0 +1,211 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+#
+# Determine whether this version of the ksh being
+# executed has a bug where the limit of background
+# processes of 25.
+#
+function check_bg_procs_limit_num
+{
+$ECHO "#!/usr/local/bin/ksh93" > $TMPDIR/exitsZero.ksh
+$ECHO "exit 0" >> $TMPDIR/exitsZero.ksh
+$CHMOD 777 $TMPDIR/exitsZero.ksh
+
+$CAT <<EOF > $TMPDIR/testbackgprocs.ksh
+#!/usr/local/bin/ksh93
+#
+# exitsZero.ksh is a one line script
+# that exit with status of "0"
+#
+
+PIDS=""
+typeset -i i=1
+while [ \$i -le 50 ]
+do
+ $TMPDIR/exitsZero.ksh &
+ PIDS="\$PIDS \$!"
+ (( i = i + 1 ))
+done
+
+\$SLEEP 1
+
+for pid in \$PIDS
+do
+ \$WAIT \$pid
+ (( \$? == 127 )) && exit 1
+done
+exit 0
+EOF
+
+$KSH93 $TMPDIR/testbackgprocs.ksh
+if [[ $? -eq 1 ]]; then
+#
+# Current ksh being executed has a limit
+# of 25 background processes.
+#
+ return 1
+else
+ return 0
+fi
+}
+
+function init_setup
+{
+
+ typeset disklist=$1
+
+ create_pool $TESTPOOL "$disklist"
+
+ if ! is_global_zone ; then
+ reexport_pool
+ fi
+
+ $RM -rf $TESTDIR || log_unresolved Could not remove $TESTDIR
+ $MKDIR -p $TESTDIR || log_unresolved Could not create $TESTDIR
+
+ $RM -rf $TESTDIR_TGT || log_unresolved Could not remove $TESTDIR_TGT
+ $MKDIR -p $TESTDIR_TGT || log_unresolved Could not create $TESTDIR_TGT
+
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+
+ log_must $ZFS create $TESTPOOL/$TESTFS_TGT
+ log_must $ZFS set mountpoint=$TESTDIR_TGT $TESTPOOL/$TESTFS_TGT
+
+ $MKDIR -p $OLDDIR || log_unresolved Could not create $OLDDIR
+ $MKDIR -p $NEWDIR_IN_FS || log_unresolved Could not create $NEWDIR_IN_FS
+ $MKDIR -p $NEWDIR_ACROSS_FS || log_unresolved Could not create $NEWDIR_ACROSS_FS
+
+}
+
+function wait_pid
+{
+ for pid in $1
+ do
+ $PS -e | $GREP $pid >/dev/null 2>&1
+ (( $? == 0 )) && $WAIT $pid
+ done
+}
+
+
+#
+# Generate given number files in a
+# directory of zfs file system
+# $1 - the directory holds the generated files
+# $2 - number of to-be-generated files
+#
+
+function generate_files
+{
+ typeset -i count
+ typeset -i proc_num=0
+
+ if (( $2 == $MVNUMFILES )); then
+ count=1
+ else
+ count=$MVNUMFILES+1
+ fi
+
+ while (( count <= $2 ))
+ do
+ $CP /etc/passwd $1/file_$count \
+ > /dev/null 2>&1 &
+
+ PIDS="$PIDS $!"
+
+ proc_num=`$ECHO $PIDS | $WC -w`
+ if (( proc_num >= GANGPIDS )); then
+ wait_pid "$PIDS"
+ proc_num=0
+ PIDS=""
+ fi
+
+ (( count = count + 1 ))
+ done
+
+}
+
+#
+# Move given number files from one directory to
+# another directory in parallel
+# $1 - source directory
+# $2 - target directory
+#
+function mv_files
+{
+ $FIND $1 -type f -print | xargs -J % \
+ $MV % $2 > /dev/null 2>&1
+}
+
+#
+# Count the files number after moving, and
+# compare it with the original number
+# $1 - directory that to be operated
+# $2 - original files number
+#
+function count_files
+{
+ typeset -i file_num
+ file_num=`$FIND $1 -type f -print | \
+ wc -l`
+ (( file_num != $2 )) && \
+ log_fail "The file number of target directory"\
+ "$2 is not equal to that of the source "\
+ "directory $1"
+
+}
+
+#
+# Running the 'mv' test
+# $1 - old directory
+# $2 - new directory
+#
+function mv_test
+{
+ typeset old=$1
+ typeset new=$2
+
+ typeset -i inc_num=$(( MVNUMFILES + MVNUMINCR ))
+ typeset -i num=0
+
+ for num in $MVNUMFILES $inc_num
+ do
+ generate_files $old $num
+
+ mv_files $old $new
+ count_files $new $num
+
+ mv_files $new $old
+ count_files $old $num
+ done
+
+ typeset dir=$(get_device_dir $DISKS)
+ verify_filesys "$TESTPOOL" "$TESTPOOL/$TESTFS" "$dir"
+
+ return 0
+}
+
diff --git a/tests/sys/cddl/zfs/tests/mv_files/mv_files_test.sh b/tests/sys/cddl/zfs/tests/mv_files/mv_files_test.sh
new file mode 100755
index 000000000000..772095e1abfd
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/mv_files/mv_files_test.sh
@@ -0,0 +1,86 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case mv_files_001_pos cleanup
+mv_files_001_pos_head()
+{
+ atf_set "descr" "Doing a 'mv' of a large amount of files within a zfs filesystemworks without errors."
+ atf_set "require.progs" "ksh93 zfs zdb"
+ atf_set "timeout" 1200
+}
+mv_files_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/mv_files_common.kshlib
+ . $(atf_get_srcdir)/mv_files.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/mv_files_001_pos.ksh || atf_fail "Testcase failed"
+}
+mv_files_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/mv_files_common.kshlib
+ . $(atf_get_srcdir)/mv_files.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case mv_files_002_pos cleanup
+mv_files_002_pos_head()
+{
+ atf_set "descr" "Doing a 'mv' of a large amount of files across two zfs filesystemsworks without errors."
+ atf_set "require.progs" "ksh93 zfs zdb"
+ atf_set "timeout" 1200
+}
+mv_files_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/mv_files_common.kshlib
+ . $(atf_get_srcdir)/mv_files.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/mv_files_002_pos.ksh || atf_fail "Testcase failed"
+}
+mv_files_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/mv_files_common.kshlib
+ . $(atf_get_srcdir)/mv_files.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case mv_files_001_pos
+ atf_add_test_case mv_files_002_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/mv_files/setup.ksh b/tests/sys/cddl/zfs/tests/mv_files/setup.ksh
new file mode 100644
index 000000000000..3643b9f760cf
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/mv_files/setup.ksh
@@ -0,0 +1,42 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/mv_files/mv_files_common.kshlib
+
+verify_runnable "global"
+
+check_bg_procs_limit_num
+if [[ $? -ne 0 ]]; then
+ log_note "ksh background process limit number is 25"
+ export GANGPIDS=25
+fi
+
+export PIDS=""
+
+init_setup $DISK
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/nestedfs/Makefile b/tests/sys/cddl/zfs/tests/nestedfs/Makefile
new file mode 100644
index 000000000000..0ffdee46247a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/nestedfs/Makefile
@@ -0,0 +1,16 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/nestedfs
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= nestedfs_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= nestedfs.cfg
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= nestedfs_001_pos.ksh
+${PACKAGE}FILES+= cleanup.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/nestedfs/cleanup.ksh b/tests/sys/cddl/zfs/tests/nestedfs/cleanup.ksh
new file mode 100644
index 000000000000..9af80e992e94
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/nestedfs/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_container_cleanup
diff --git a/tests/sys/cddl/zfs/tests/nestedfs/nestedfs.cfg b/tests/sys/cddl/zfs/tests/nestedfs/nestedfs.cfg
new file mode 100644
index 000000000000..aa117bd82fbc
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/nestedfs/nestedfs.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export TESTFILE0=testfile0.${TESTCASE_ID}
+export TESTFILE1=testfile1.${TESTCASE_ID}
diff --git a/tests/sys/cddl/zfs/tests/nestedfs/nestedfs_001_pos.ksh b/tests/sys/cddl/zfs/tests/nestedfs/nestedfs_001_pos.ksh
new file mode 100644
index 000000000000..16bdd239c1e0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/nestedfs/nestedfs_001_pos.ksh
@@ -0,0 +1,74 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: nestedfs_001_pos
+#
+# DESCRIPTION:
+# Given a pool create a nested file system and a ZFS file system
+# in the nested file system. Populate the file system.
+#
+# As a sub-assertion, the test verifies that a nested file system with
+# a mounted file system cannot be destroyed.
+#
+# STRATEGY:
+# 1. Create a file in the new mountpoint
+# 2. Unmount the new mountpoint
+# 3. Show a nested file system with file systems cannot be destroyed
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+typeset OP=create
+typeset -i BLOCKSZ=8192
+typeset -i NUM_WRITES=600
+typeset -i DATA=0
+
+log_assert "Verify a nested file system can be created/destroyed."
+
+log_must $FILE_WRITE -o $OP -f $TESTDIR1/$TESTFILE0 -b $BLOCKSZ \
+ -c $NUM_WRITES -d $DATA
+
+log_must $ZFS unmount $TESTDIR1
+
+log_note "Verify that a nested file system with a mounted file system "\
+ "cannot be destroyed."
+log_mustnot $ZFS destroy $TESTPOOL/$TESTCTR
+
+log_pass "A nested file system was successfully populated."
diff --git a/tests/sys/cddl/zfs/tests/nestedfs/nestedfs_test.sh b/tests/sys/cddl/zfs/tests/nestedfs/nestedfs_test.sh
new file mode 100755
index 000000000000..034c4e671207
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/nestedfs/nestedfs_test.sh
@@ -0,0 +1,55 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case nestedfs_001_pos cleanup
+nestedfs_001_pos_head()
+{
+ atf_set "descr" "Verify a nested file system can be created/destroyed."
+ atf_set "require.progs" "ksh93 zfs"
+}
+nestedfs_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/nestedfs.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/nestedfs_001_pos.ksh || atf_fail "Testcase failed"
+}
+nestedfs_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/nestedfs.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case nestedfs_001_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/nestedfs/setup.ksh b/tests/sys/cddl/zfs/tests/nestedfs/setup.ksh
new file mode 100644
index 000000000000..67e9a299edb8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/nestedfs/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_container_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/no_space/Makefile b/tests/sys/cddl/zfs/tests/no_space/Makefile
new file mode 100644
index 000000000000..db55c2674165
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/no_space/Makefile
@@ -0,0 +1,16 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/no_space
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= no_space_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= enospc.cfg
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= enospc_001_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/no_space/cleanup.ksh b/tests/sys/cddl/zfs/tests/no_space/cleanup.ksh
new file mode 100644
index 000000000000..9d7544c1a096
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/no_space/cleanup.ksh
@@ -0,0 +1,42 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+DISK=${DISKS%% *}
+
+ismounted "$TESTPOOL/$TESTFS"
+(( $? == 0 )) && \
+ log_must $ZFS umount $TESTDIR
+
+destroy_pool $TESTPOOL
+#
+# Remove 100mb partition.
+#
+cleanup_devices "$DISK"
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/no_space/enospc.cfg b/tests/sys/cddl/zfs/tests/no_space/enospc.cfg
new file mode 100644
index 000000000000..3d94089625ee
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/no_space/enospc.cfg
@@ -0,0 +1,34 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export TESTFILE0=testfile0.${TESTCASE_ID}
+export TESTFILE1=testfile1.${TESTCASE_ID}
+
+export SIZE=100mb
+export ENOSPC=28
+export BLOCKSZ=8192
+export NUM_WRITES=65536
+export DATA=0
diff --git a/tests/sys/cddl/zfs/tests/no_space/enospc_001_pos.ksh b/tests/sys/cddl/zfs/tests/no_space/enospc_001_pos.ksh
new file mode 100644
index 000000000000..d2e6c2da0535
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/no_space/enospc_001_pos.ksh
@@ -0,0 +1,76 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: enospc_001
+#
+# DESCRIPTION:
+# ENOSPC is returned on an attempt to write a second file
+# to a file system after a first file was written that terminated
+# with ENOSPC on a cleanly initialized file system.
+#
+# STRATEGY:
+# 1. Write a file until the file system is full.
+# 2. Ensure that ENOSPC is returned.
+# 3. Write a second file while the file system remains full.
+# 4. Verify the return code is ENOSPC.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "ENOSPC is returned when file system is full."
+log_must $ZFS set compression=off $TESTPOOL/$TESTFS
+
+log_note "Writing file: $TESTFILE0 until ENOSPC."
+$FILE_WRITE -o create -f $TESTDIR/$TESTFILE0 -b $BLOCKSZ \
+ -c $NUM_WRITES -d $DATA
+ret=$?
+
+(( $ret != $ENOSPC )) && \
+ log_fail "$TESTFILE0 returned: $ret rather than ENOSPC."
+
+log_note "Write another file: $TESTFILE1 but expect ENOSPC."
+$FILE_WRITE -o create -f $TESTDIR/$TESTFILE1 -b $BLOCKSZ \
+ -c $NUM_WRITES -d $DATA
+ret=$?
+
+(( $ret != $ENOSPC )) && \
+ log_fail "$TESTFILE1 returned: $ret rather than ENOSPC."
+
+log_pass "ENOSPC returned as expected."
diff --git a/tests/sys/cddl/zfs/tests/no_space/no_space_test.sh b/tests/sys/cddl/zfs/tests/no_space/no_space_test.sh
new file mode 100755
index 000000000000..1ae5f20c2496
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/no_space/no_space_test.sh
@@ -0,0 +1,55 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case enospc_001_pos cleanup
+enospc_001_pos_head()
+{
+ atf_set "descr" "ENOSPC is returned when file system is full."
+ atf_set "require.progs" "ksh93 zfs"
+}
+enospc_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/enospc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/enospc_001_pos.ksh || atf_fail "Testcase failed"
+}
+enospc_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/enospc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case enospc_001_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/no_space/setup.ksh b/tests/sys/cddl/zfs/tests/no_space/setup.ksh
new file mode 100644
index 000000000000..e3bfc9b06ee5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/no_space/setup.ksh
@@ -0,0 +1,35 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+DISK=${DISKS%% *}
+
+wipe_partition_table $DISK
+log_must set_partition 1 "" $SIZE $DISK
+
+default_setup $DISK"p1"
diff --git a/tests/sys/cddl/zfs/tests/online_offline/Makefile b/tests/sys/cddl/zfs/tests/online_offline/Makefile
new file mode 100644
index 000000000000..5f0e68a5ccd5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/online_offline/Makefile
@@ -0,0 +1,16 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/online_offline
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= online_offline_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= online_offline_001_pos.ksh
+${PACKAGE}FILES+= online_offline.cfg
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= online_offline_002_neg.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/online_offline/cleanup.ksh b/tests/sys/cddl/zfs/tests/online_offline/cleanup.ksh
new file mode 100644
index 000000000000..960e738d5ae6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/online_offline/cleanup.ksh
@@ -0,0 +1,34 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+verify_runnable "global"
+
+reap_children
+default_cleanup_noexit
+cleanup_devices ${DISKS}
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/online_offline/online_offline.cfg b/tests/sys/cddl/zfs/tests/online_offline/online_offline.cfg
new file mode 100644
index 000000000000..eaf5f16ab2c6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/online_offline/online_offline.cfg
@@ -0,0 +1,29 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2014 Spectra Logic Corporation
+# Use is subject to license terms.
+#
+
+export TESTFILE=testfile.${TESTCASE_ID}
diff --git a/tests/sys/cddl/zfs/tests/online_offline/online_offline_001_pos.ksh b/tests/sys/cddl/zfs/tests/online_offline/online_offline_001_pos.ksh
new file mode 100644
index 000000000000..e0abb3bbce78
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/online_offline/online_offline_001_pos.ksh
@@ -0,0 +1,65 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2014 Spectra Logic Corporation.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+function verify_assertion
+{
+ busy_path $TESTDIR
+ for disk in $DISKS; do
+ log_must $ZPOOL offline $TESTPOOL $disk
+ check_state $TESTPOOL $disk "offline"
+ if [[ $? != 0 ]]; then
+ log_fail "$disk of $TESTPOOL is not offline."
+ fi
+
+ log_must $ZPOOL online $TESTPOOL $disk
+ check_state $TESTPOOL $disk "online"
+ if [[ $? != 0 ]]; then
+ log_fail "$disk of $TESTPOOL did not match online state"
+ fi
+ done
+ reap_children
+
+ typeset dir=$(get_device_dir $DISKS)
+ verify_filesys "$TESTPOOL" "$TESTPOOL/$TESTFS" "$dir"
+}
+
+log_assert "Turning a disk offline and back online during I/O completes."
+log_onexit cleanup
+
+for keyword in "mirror" "raidz"; do
+ typeset child_pid=""
+ default_setup_noexit "$keyword $DISKS"
+ verify_assertion
+ destroy_pool $TESTPOOL
+done
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/online_offline/online_offline_002_neg.ksh b/tests/sys/cddl/zfs/tests/online_offline/online_offline_002_neg.ksh
new file mode 100644
index 000000000000..7c5f1e78114c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/online_offline/online_offline_002_neg.ksh
@@ -0,0 +1,79 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2014 Spectra Logic Corporation.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+function verify_assertion
+{
+ keyword="$1"
+ typeset -i redundancy
+ set -A DISKLIST $DISKS
+
+ case "$keyword" in
+ "") redundancy=0 ;;
+ "mirror") (( redundancy=${#DISKLIST[@]} - 1 )) ;;
+ "raidz") redundancy=1 ;;
+ "raidz2") redundancy=2 ;;
+ "raidz3") redundancy=3 ;;
+ *) log_fail "Unknown keyword" ;;
+ esac
+
+ echo redundancy is $redundancy
+
+ if [ ${#DISKLIST[@]} -le "$redundancy" ]; then
+ log_fail "Insufficiently many disks configured for this test"
+ fi
+
+ busy_path $TESTDIR
+ # Offline the allowed number of disks
+ for ((i=0; i<$redundancy; i=$i+1 )); do
+ log_must $ZPOOL offline $TESTPOOL ${DISKLIST[$i]}
+ done
+
+ #Verify that offlining any additional disks should fail
+ for ((i=$redundancy; i<${#DISKLIST[@]}; i=$i+1 )); do
+ log_mustnot $ZPOOL offline $TESTPOOL ${DISKLIST[$i]}
+ done
+ reap_children
+
+ typeset dir=$(get_device_dir $DISKS)
+ verify_filesys "$TESTPOOL" "$TESTPOOL/$TESTFS" "$dir"
+}
+
+log_assert "Turning both disks offline should fail."
+
+for keyword in "" "mirror" "raidz" "raidz2"; do
+ child_pids=""
+ default_setup_noexit "$keyword $DISKS"
+ verify_assertion "$keyword"
+ destroy_pool $TESTPOOL
+done
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/online_offline/online_offline_test.sh b/tests/sys/cddl/zfs/tests/online_offline/online_offline_test.sh
new file mode 100755
index 000000000000..6eb9d508d998
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/online_offline/online_offline_test.sh
@@ -0,0 +1,80 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case online_offline_001_pos cleanup
+online_offline_001_pos_head()
+{
+ atf_set "descr" "Turning a disk offline and back online during I/O completes."
+ atf_set "require.progs" "ksh93 zpool zdb"
+ atf_set "timeout" 3600
+}
+online_offline_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/online_offline.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/online_offline_001_pos.ksh || atf_fail "Testcase failed"
+}
+online_offline_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/online_offline.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case online_offline_002_neg cleanup
+online_offline_002_neg_head()
+{
+ atf_set "descr" "Offlining a disk should fail if the pool would go critical"
+ atf_set "require.progs" "ksh93 zpool zdb"
+ atf_set "timeout" 3600
+}
+online_offline_002_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/online_offline.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/online_offline_002_neg.ksh || atf_fail "Testcase failed"
+}
+online_offline_002_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/online_offline.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case online_offline_001_pos
+ atf_add_test_case online_offline_002_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/pool_names/Makefile b/tests/sys/cddl/zfs/tests/pool_names/Makefile
new file mode 100644
index 000000000000..34350e28a800
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/pool_names/Makefile
@@ -0,0 +1,15 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/pool_names
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= pool_names_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= pool_names_002_neg.ksh
+${PACKAGE}FILES+= pool_names.cfg
+${PACKAGE}FILES+= pool_names_001_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/pool_names/pool_names.cfg b/tests/sys/cddl/zfs/tests/pool_names/pool_names.cfg
new file mode 100644
index 000000000000..4aefc3776d7d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/pool_names/pool_names.cfg
@@ -0,0 +1,27 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+export STF_TIMEOUT=1200
+export DISK=${DISKS%% *}
diff --git a/tests/sys/cddl/zfs/tests/pool_names/pool_names_001_pos.ksh b/tests/sys/cddl/zfs/tests/pool_names/pool_names_001_pos.ksh
new file mode 100644
index 000000000000..987d7abe44e1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/pool_names/pool_names_001_pos.ksh
@@ -0,0 +1,121 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: pool_names_001_pos
+#
+# DESCRIPTION:
+#
+# Test that a set of valid names can be used to create pools. Further
+# verify that the created pools can be destroyed.
+#
+# STRATEGY:
+# 1) For each valid character in the character set, try to create
+# and destroy the pool.
+# 2) Given a list of valid pool names, try to create and destroy
+# pools with the given names.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-11-21)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Ensure that pool names can use the ASCII subset of UTF-8"
+
+function cleanup
+{
+ [[ -n "$name" ]] && destroy_pool $name
+
+ if [[ -d $TESTDIR ]]; then
+ log_must $RM -rf $TESTDIR
+ fi
+
+}
+
+log_onexit cleanup
+
+if [[ ! -e $TESTDIR ]]; then
+ log_must $MKDIR $TESTDIR
+fi
+
+log_note "Ensure letters of the alphabet are allowable"
+
+typeset name=""
+
+for name in A B C D E F G H I J K L M \
+ N O P Q R S T U V W X Y Z \
+ a b c d e f g h i j k l m \
+ n o p q r s t u v w x y z
+do
+ log_must $ZPOOL create -m $TESTDIR $name $DISK
+ if ! poolexists $name; then
+ log_fail "Could not create a pool called '$name'"
+ fi
+
+ log_must $ZPOOL destroy $name
+done
+
+log_note "Ensure a variety of unusual names passes"
+
+name=""
+
+for name in "a.............................." "a_" "a-" "a:" \
+ "a." "a123456" "bc0t0d0" "m1rr0r_p00l" "ra1dz_p00l" \
+ "araidz2" "C0t2d0" "cc0t0" "raid2:-_." "mirr_:-." \
+ "m1rr0r-p00l" "ra1dz-p00l" "spar3_p00l" \
+ "spar3-p00l" "hiddenmirrorpool" "hiddenraidzpool" \
+ "hiddensparepool"
+do
+ log_must $ZPOOL create -m $TESTDIR $name $DISK
+ if ! poolexists $name; then
+ log_fail "Could not create a pool called '$name'"
+ fi
+
+ #
+ # Since the naming convention applies to datasets too,
+ # create datasets with the same names as above.
+ #
+ log_must $ZFS create $name/$name
+ log_must $ZFS snapshot $name/$name@$name
+ log_must $ZFS clone $name/$name@$name $name/clone_$name
+ log_must $ZFS create -V 150m $name/$name/$name
+
+ log_must $ZPOOL destroy $name
+done
+
+log_pass "Valid pool names were accepted correctly."
diff --git a/tests/sys/cddl/zfs/tests/pool_names/pool_names_002_neg.ksh b/tests/sys/cddl/zfs/tests/pool_names/pool_names_002_neg.ksh
new file mode 100644
index 000000000000..bd99e97ce4af
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/pool_names/pool_names_002_neg.ksh
@@ -0,0 +1,142 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: pool_names_002_neg
+#
+# DESCRIPTION:
+#
+# Ensure that a set of invalid names cannot be used to create pools.
+#
+# STRATEGY:
+# 1) For each invalid character in the character set, try to create
+# and destroy the pool. Verify it fails.
+# 2) Given a list of invalid pool names, ensure the pools are not
+# created.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-11-21)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Ensure that a set of invalid names cannot be used to create pools."
+
+# Global variable use to cleanup failures.
+POOLNAME=""
+
+function cleanup
+{
+ destroy_pool $POOLNAME
+
+ if [[ -d $TESTDIR ]]; then
+ log_must $RM -rf $TESTDIR
+ fi
+}
+
+log_onexit cleanup
+
+if [[ ! -e $TESTDIR ]]; then
+ log_must $MKDIR $TESTDIR
+fi
+
+log_note "Ensure invalid characters fail"
+for POOLNAME in "!" "\"" "#" "$" "%" "&" "'" "(" ")" \
+ "\*" "+" "," "-" "\." "/" "\\" \
+ ":" ";" "<" "=" ">" "\?" "@" \
+ "[" "]" "^" "_" "\`" "{" "|" "}" "~"
+do
+ log_mustnot $ZPOOL create -m $TESTDIR $POOLNAME $DISK
+ if poolexists $POOLNAME; then
+ log_fail "Unexpectedly created pool: '$POOLNAME'"
+ fi
+
+ log_mustnot $ZPOOL destroy $POOLNAME
+done
+
+# poolexists cannot be used to test pools with numeric names, because
+# "zpool list" will interpret the name as a repeat interval and never return.
+log_note "Ensure invalid characters fail"
+for POOLNAME in 0 1 2 3 4 5 6 7 8 9 2222222222222222222
+do
+ log_mustnot $ZPOOL create -m $TESTDIR $POOLNAME $DISK
+ log_mustnot $ZPOOL destroy $POOLNAME
+done
+
+log_note "Check that invalid octal values fail"
+for oct in "\000" "\001" "\002" "\003" "\004" "\005" "\006" "\007" \
+ "\010" "\011" "\012" "\013" "\014" "\015" "\017" \
+ "\020" "\021" "\022" "\023" "\024" "\025" "\026" "\027" \
+ "\030" "\031" "\032" "\033" "\034" "\035" "\036" "\037" \
+ "\040" "\177"
+do
+ # Be careful not to print the poolname, because it might be a terminal
+ # control character
+ POOLNAME=`eval "print x | tr 'x' '$oct'"`
+ $ZPOOL create -m $TESTDIR $POOLNAME $DISK > /dev/null 2>&1
+ if [ $? = 0 ]; then
+ log_fail "Unexpectedly created pool: \"$oct\""
+ elif poolexists $POOLNAME; then
+ log_fail "Unexpectedly created pool: \"$oct\""
+ fi
+
+ $ZPOOL destroy $POOLNAME > /dev/null 2>&1
+ if [ $? = 0 ]; then
+ log_fail "Unexpectedly destroyed pool: \"$oct\""
+ fi
+done
+
+log_note "Verify invalid pool names fail"
+set -A POOLNAME "c0t0d0s0" "c0t0d0" "c0t0d19" "c0t50000E0108D279d0" \
+ "mirror" "raidz" ",," ",,,,,,,,,,,,,,,,,,,,,,,,," \
+ "mirror_pool" "raidz_pool" \
+ "mirror-pool" "raidz-pool" "spare" "spare_pool" \
+ "spare-pool" "raidz1-" "raidz2:" ":aaa" "-bbb" "_ccc" ".ddd"
+POOLNAME[${#POOLNAME[@]}]='log'
+typeset -i i=0
+while ((i < ${#POOLNAME[@]})); do
+ log_mustnot $ZPOOL create -m $TESTDIR ${POOLNAME[$i]} $DISK
+ if poolexists ${POOLNAME[$i]}; then
+ log_fail "Unexpectedly created pool: '${POOLNAME[$i]}'"
+ fi
+
+ log_mustnot $ZPOOL destroy ${POOLNAME[$i]}
+
+ ((i += 1))
+done
+
+log_pass "Invalid names and characters were caught correctly"
diff --git a/tests/sys/cddl/zfs/tests/pool_names/pool_names_test.sh b/tests/sys/cddl/zfs/tests/pool_names/pool_names_test.sh
new file mode 100755
index 000000000000..e1c0b01a69f8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/pool_names/pool_names_test.sh
@@ -0,0 +1,66 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case pool_names_001_pos
+pool_names_001_pos_head()
+{
+ atf_set "descr" "Ensure that pool names can use the ASCII subset of UTF-8"
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 1200
+}
+pool_names_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/pool_names.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/pool_names_001_pos.ksh || atf_fail "Testcase failed"
+}
+
+
+atf_test_case pool_names_002_neg
+pool_names_002_neg_head()
+{
+ atf_set "descr" "Ensure that a set of invalid names cannot be used to create pools."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1200
+}
+pool_names_002_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/pool_names.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/pool_names_002_neg.ksh || atf_fail "Testcase failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case pool_names_001_pos
+ atf_add_test_case pool_names_002_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/poolversion/Makefile b/tests/sys/cddl/zfs/tests/poolversion/Makefile
new file mode 100644
index 000000000000..6c04aac0f5fb
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/poolversion/Makefile
@@ -0,0 +1,16 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/poolversion
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= poolversion_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= poolversion_002_pos.ksh
+${PACKAGE}FILES+= poolversion_001_pos.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= setup.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/poolversion/cleanup.ksh b/tests/sys/cddl/zfs/tests/poolversion/cleanup.ksh
new file mode 100644
index 000000000000..3b8ae256444d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/poolversion/cleanup.ksh
@@ -0,0 +1,40 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+verify_runnable "global"
+
+$ZPOOL set 2>&1 | $GREP version > /dev/null
+if [ $? -eq 1 ]
+then
+ log_unsupported "zpool version property not supported on this system."
+fi
+
+destroy_pool $TESTPOOL
+destroy_pool $TESTPOOL2
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/poolversion/poolversion_001_pos.ksh b/tests/sys/cddl/zfs/tests/poolversion/poolversion_001_pos.ksh
new file mode 100644
index 000000000000..3e526a65c460
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/poolversion/poolversion_001_pos.ksh
@@ -0,0 +1,68 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: poolversion_001_pos
+#
+# DESCRIPTION:
+#
+# zpool set version can upgrade a pool
+#
+# STRATEGY:
+# 1. Taking a version 1 pool
+# 2. For all known versions, set the version of the pool using zpool set
+# 3. Verify that pools version
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+log_assert "zpool set version can upgrade a pool"
+for version in 1 2 3 4 5 6 7 8
+do
+ log_must $ZPOOL set version=$version $TESTPOOL
+ ACTUAL=$($ZPOOL get version $TESTPOOL | $GREP version \
+ | $AWK '{print $3}')
+ if [ "$ACTUAL" != "$version" ]
+ then
+ log_fail "v. $ACTUAL set for $TESTPOOL, expected v. $version!"
+ fi
+done
+
+log_pass "zpool set version can upgrade a pool"
+
diff --git a/tests/sys/cddl/zfs/tests/poolversion/poolversion_002_pos.ksh b/tests/sys/cddl/zfs/tests/poolversion/poolversion_002_pos.ksh
new file mode 100644
index 000000000000..e50da638eb82
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/poolversion/poolversion_002_pos.ksh
@@ -0,0 +1,81 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: poolversion_002_pos
+#
+# DESCRIPTION:
+#
+# zpool set version can only increment pool version
+#
+# STRATEGY:
+# 1. Set a version 1 pool to be a version 6 pool
+# 2. Verify it's set to version 6
+# 3. Attempt to set prior versions
+# 4. Verify it's still set to version 6
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-07-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+log_assert "zpool set version can only increment pool version"
+
+log_must $ZPOOL set version=6 $TESTPOOL2
+# verify it's actually that version - by checking the version property
+# and also by trying to set bootfs (which should fail if it is not version 6)
+
+VERSION=$($ZPOOL get version $TESTPOOL2| $GREP version | $AWK '{print $3}')
+if [ "$VERSION" != "6" ]
+then
+ log_fail "Version $VERSION set for $TESTPOOL2 expected version 6!"
+fi
+log_must $ZPOOL set bootfs=$TESTPOOL2 $TESTPOOL2
+
+# now verify we can't downgrade the version
+log_mustnot $ZPOOL set version=5 $TESTPOOL2
+log_mustnot $ZPOOL set version=-1 $TESTPOOL2
+
+# verify the version is still 6
+VERSION=$($ZPOOL get version $TESTPOOL2 | $GREP version | $AWK '{print $3}')
+if [ "$VERSION" != "6" ]
+then
+ log_fail "Version $VERSION set for $TESTPOOL2, expected version 6!"
+fi
+
+log_pass "zpool set version can only increment pool version"
+
diff --git a/tests/sys/cddl/zfs/tests/poolversion/poolversion_test.sh b/tests/sys/cddl/zfs/tests/poolversion/poolversion_test.sh
new file mode 100755
index 000000000000..f8ee54bf865e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/poolversion/poolversion_test.sh
@@ -0,0 +1,76 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case poolversion_001_pos cleanup
+poolversion_001_pos_head()
+{
+ atf_set "descr" "zpool set version can upgrade a pool"
+ atf_set "require.progs" "ksh93 zpool"
+}
+poolversion_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/poolversion_001_pos.ksh || atf_fail "Testcase failed"
+}
+poolversion_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case poolversion_002_pos cleanup
+poolversion_002_pos_head()
+{
+ atf_set "descr" "zpool set version can only increment pool version"
+ atf_set "require.progs" "ksh93 zpool"
+}
+poolversion_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/poolversion_002_pos.ksh || atf_fail "Testcase failed"
+}
+poolversion_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case poolversion_001_pos
+ atf_add_test_case poolversion_002_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/poolversion/setup.ksh b/tests/sys/cddl/zfs/tests/poolversion/setup.ksh
new file mode 100644
index 000000000000..83b5d054c9aa
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/poolversion/setup.ksh
@@ -0,0 +1,45 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+verify_runnable "global"
+
+$ZPOOL set 2>&1 | $GREP version > /dev/null
+if [ $? -eq 1 ]
+then
+ log_unsupported "zpool version property not supported on this system."
+fi
+
+DISKS_ARRAY=($DISKS)
+# create a version 1 pool
+log_must $ZPOOL create -f -o version=1 $TESTPOOL ${DISKS_ARRAY[0]}
+
+
+# create another version 1 pool
+log_must $ZPOOL create -f -o version=1 $TESTPOOL2 ${DISKS_ARRAY[1]}
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/quota/Makefile b/tests/sys/cddl/zfs/tests/quota/Makefile
new file mode 100644
index 000000000000..beae44a4a503
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/quota/Makefile
@@ -0,0 +1,22 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/quota
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= quota_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= quota_003_pos.ksh
+${PACKAGE}FILES+= quota_006_neg.ksh
+${PACKAGE}FILES+= quota_002_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= quota.kshlib
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= quota_004_pos.ksh
+${PACKAGE}FILES+= quota_005_pos.ksh
+${PACKAGE}FILES+= quota_001_pos.ksh
+${PACKAGE}FILES+= quota.cfg
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/quota/cleanup.ksh b/tests/sys/cddl/zfs/tests/quota/cleanup.ksh
new file mode 100644
index 000000000000..9af80e992e94
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/quota/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_container_cleanup
diff --git a/tests/sys/cddl/zfs/tests/quota/quota.cfg b/tests/sys/cddl/zfs/tests/quota/quota.cfg
new file mode 100644
index 000000000000..dc13595beddc
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/quota/quota.cfg
@@ -0,0 +1,31 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export BLOCK_SIZE=8192
+export QUOTA_VALUE=10000000
+export TESTFILE1=file1.${TESTCASE_ID}
+export TESTFILE2=file2.${TESTCASE_ID}
+export TOLERANCE=131071
diff --git a/tests/sys/cddl/zfs/tests/quota/quota.kshlib b/tests/sys/cddl/zfs/tests/quota/quota.kshlib
new file mode 100644
index 000000000000..62ed34a1ec72
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/quota/quota.kshlib
@@ -0,0 +1,83 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+# BLOCK_SIZE, QUOTA_VALUE and TOLERANCE set in quota.cfg
+readonly EDQUOT=69
+
+#
+# Function to fill the quota of a zfs filesystem
+#
+# $1 - The File system or container to fill.
+# $2 - The mountpoint to use.
+#
+function fill_quota
+{
+ typeset FILESYSTEM="$1"
+ typeset MNTPT="$2"
+
+ log_must $ZFS set quota=$QUOTA_VALUE $FILESYSTEM
+
+ typeset -i write_size=0
+ (( write_size = 2 * QUOTA_VALUE ))
+
+ typeset -i zret=0
+ $FILE_WRITE -o create -f $MNTPT/$TESTFILE1 -b $BLOCK_SIZE \
+ -c $write_size -d 0
+ zret=$?
+ [[ $zret -ne EDQUOT ]] && log_fail "Got error $zret; expected $EDQUOT"
+
+ typeset -i file_size=`$LS -ls $MNTPT/$TESTFILE1 | $AWK '{ print $1 }'`
+ typeset -i limit=0
+ (( file_size = file_size * 512 ))
+ (( limit = QUOTA_VALUE + TOLERANCE ))
+ (( file_size > limit )) && \
+ log_fail "File was created larger than the quota value, aborting!!!"
+ return 0
+}
+
+#
+# Function attempts to write another file in a ZFS filesystem
+# that has already filled its quota
+#
+function exceed_quota
+{
+ typeset FILESYSTEM="$1"
+ typeset MNTPT="$2"
+
+ log_must fill_quota $FILESYSTEM $MNTPT
+ typeset -i write_size=0
+ (( write_size = 2 * QUOTA_VALUE ))
+ typeset -i zret=0
+ #
+ # Writing a file without API to access return code
+ #
+ log_note "Creating a file in a FS that has already exceeded its quota"
+ $FILE_WRITE -o create -f $MNTPT/$TESTFILE2 \
+ -b $BLOCK_SIZE -c $write_size -d 0
+ zret=$?
+ [[ $zret -ne EDQUOT ]] && log_fail "Got error $zret; expected $EDQUOT"
+ return 0
+}
diff --git a/tests/sys/cddl/zfs/tests/quota/quota_001_pos.ksh b/tests/sys/cddl/zfs/tests/quota/quota_001_pos.ksh
new file mode 100644
index 000000000000..b8b3e78e6895
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/quota/quota_001_pos.ksh
@@ -0,0 +1,86 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/quota/quota.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: quota_001_pos
+#
+# DESCRIPTION:
+#
+# A ZFS file system quota limits the amount of pool space
+# available to a file system. Apply a quota and verify
+# that no more file creates are permitted.
+#
+# STRATEGY:
+# 1) Apply quota to ZFS file system
+# 2) Create a file which is larger than the set quota
+# 3) Verify that the resulting file size is less than the quota limit
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify that file size is limited by the file system quota"
+
+#
+# cleanup to be used internally as otherwise quota assertions cannot be
+# run independently or out of order
+#
+function cleanup
+{
+ [[ -e $TESTDIR/$TESTFILE1 ]] && \
+ log_must $RM $TESTDIR/$TESTFILE1
+ #
+ # Need to allow time for space to be released back to
+ # pool, otherwise next test will fail trying to set a
+ # quota which is less than the space used.
+ #
+ sleep 5
+ log_must $ZFS set quota=none $TESTPOOL/$TESTFS
+}
+
+log_onexit cleanup
+
+#
+# Sets the quota value and attempts to fill it with a file
+# twice the size of the quota
+#
+log_must fill_quota $TESTPOOL/$TESTFS $TESTDIR
+
+log_pass "File size limited by quota"
diff --git a/tests/sys/cddl/zfs/tests/quota/quota_002_pos.ksh b/tests/sys/cddl/zfs/tests/quota/quota_002_pos.ksh
new file mode 100644
index 000000000000..a206185c6f85
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/quota/quota_002_pos.ksh
@@ -0,0 +1,83 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/quota/quota.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: quota_002_pos
+#
+# DESCRIPTION:
+# A zfs file system quota limits the amount of pool space
+# available to a given ZFS file system. Once exceeded, it is impossible
+# to write any more files to the file system.
+#
+# STRATEGY:
+# 1) Apply quota to the ZFS file system
+# 2) Exceed the quota
+# 3) Attempt to write another file
+# 4) Verify the attempt fails with error code 49 (EDQUOTA)
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify that a file write cannot exceed the file system quota"
+
+#
+# cleanup to be used internally as otherwise quota assertions cannot be
+# run independently or out of order
+#
+function cleanup
+{
+ [[ -e $TESTDIR/$TESTFILE1 ]] && \
+ log_must $RM $TESTDIR/$TESTFILE1
+
+ [[ -e $TESTDIR/$TESTFILE2 ]] && \
+ log_must $RM $TESTDIR/$TESTFILE2
+ log_must $ZFS set quota=none $TESTPOOL/$TESTFS
+}
+
+log_onexit cleanup
+
+#
+# Fills the quota & attempts to write another file
+#
+log_must exceed_quota $TESTPOOL/$TESTFS $TESTDIR
+
+log_pass "Could not write file. Quota limit enforced as expected"
diff --git a/tests/sys/cddl/zfs/tests/quota/quota_003_pos.ksh b/tests/sys/cddl/zfs/tests/quota/quota_003_pos.ksh
new file mode 100644
index 000000000000..96da143bff0b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/quota/quota_003_pos.ksh
@@ -0,0 +1,89 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/quota/quota.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: quota_003_pos
+#
+# DESCRIPTION:
+# A ZFS file system quota limits the amount of pool space
+# available to a file system dataset. Apply a quota and verify
+# that no more file creates are permitted.
+#
+# NOTE: THis test applies to a dataset rather than a file system.
+#
+# STRATEGY:
+# 1) Apply quota to ZFS file system dataset
+# 2) Create a file which is larger than the set quota
+# 3) Verify that the resulting file size is less than the quota limit
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify that file size is limited by the file system quota" \
+ "(dataset version)"
+
+#
+# cleanup to be used internally as otherwise quota assertions cannot be
+# run independently or out of order
+#
+function cleanup
+{
+ [[ -e $TESTDIR1/$TESTFILE1 ]] && \
+ log_must $RM $TESTDIR1/$TESTFILE1
+
+ #
+ # Need to allow time for space to be released back to
+ # pool, otherwise next test will fail trying to set a
+ # quota which is less than the space used.
+ #
+ sleep 5
+ log_must $ZFS set quota=none $TESTPOOL/$TESTFS
+}
+
+log_onexit cleanup
+
+#
+# Sets the quota value and attempts to fill it with a file
+# twice the size of the quota
+#
+log_must fill_quota $TESTPOOL/$TESTCTR/$TESTFS1 $TESTDIR1
+
+log_pass "File size limited by quota"
diff --git a/tests/sys/cddl/zfs/tests/quota/quota_004_pos.ksh b/tests/sys/cddl/zfs/tests/quota/quota_004_pos.ksh
new file mode 100644
index 000000000000..6df4c6e0eaf2
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/quota/quota_004_pos.ksh
@@ -0,0 +1,84 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/quota/quota.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: quota_004_pos
+#
+# DESCRIPTION:
+# A zfs file system quota limits the amount of pool space
+# available to a given ZFS file system dataset. Once exceeded, it
+# is impossible to write any more files to the file system.
+#
+# STRATEGY:
+# 1) Apply quota to the ZFS file system dataset
+# 2) Exceed the quota
+# 3) Attempt to write another file
+# 4) Verify the attempt fails with error code 49 (EDQUOTA)
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify that a file write cannot exceed the file system quota" \
+ "(dataset version)"
+
+#
+# cleanup to be used internally as otherwise quota assertions cannot be
+# run independently or out of order
+#
+function cleanup
+{
+ [[ -e $TESTDIR1/$TESTFILE1 ]] && \
+ log_must $RM $TESTDIR1/$TESTFILE1
+
+ [[ -e $TESTDIR1/$TESTFILE2 ]] && \
+ log_must $RM $TESTDIR1/$TESTFILE2
+ log_must $ZFS set quota=none $TESTPOOL/$TESTFS
+}
+
+log_onexit cleanup
+
+#
+# Fills the quota & attempts to write another file
+#
+log_must exceed_quota $TESTPOOL/$TESTCTR/$TESTFS1 $TESTDIR1
+
+log_pass "Could not write file. Quota limit enforced as expected"
diff --git a/tests/sys/cddl/zfs/tests/quota/quota_005_pos.ksh b/tests/sys/cddl/zfs/tests/quota/quota_005_pos.ksh
new file mode 100644
index 000000000000..dbfbae795911
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/quota/quota_005_pos.ksh
@@ -0,0 +1,83 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: quota_005_pos
+#
+# DESCRIPTION:
+#
+# Verify that quota doesn't inherit its value from parent.
+#
+# STRATEGY:
+# 1) Set quota for parents
+# 2) Create a filesystem tree
+# 3) Verify that the 'quota' for descendent doesnot inherit the value.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-08-17)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ datasetexists $fs_child && \
+ log_must $ZFS destroy $fs_child
+
+ log_must $ZFS set quota=none $fs
+}
+
+log_onexit cleanup
+
+log_assert "Verify that quota does not inherit its value from parent."
+log_onexit cleanup
+
+fs=$TESTPOOL/$TESTFS
+fs_child=$TESTPOOL/$TESTFS/$TESTFS
+
+typeset -l space_avail=$(get_prop available $fs)
+typeset -l quotasize=$space_avail
+((quotasize = quotasize * 2 ))
+log_must $ZFS list
+log_must $ZFS set quota=$quotasize $fs
+
+log_must $ZFS create $fs_child
+typeset -l quota_space=$(get_prop quota $fs_child)
+[[ $quota_space == $quotasize ]] && \
+ log_fail "The quota of child dataset inherits its value from parent."
+
+log_pass "quota doesnot inherit its value from parent as expected."
diff --git a/tests/sys/cddl/zfs/tests/quota/quota_006_neg.ksh b/tests/sys/cddl/zfs/tests/quota/quota_006_neg.ksh
new file mode 100644
index 000000000000..c5ca1ce53d09
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/quota/quota_006_neg.ksh
@@ -0,0 +1,79 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: quota_006_neg
+#
+# DESCRIPTION:
+#
+# Can't set a quota to less than currently being used by the dataset.
+#
+# STRATEGY:
+# 1) Create a filesystem
+# 2) Set a quota on the filesystem that is lower than the space
+# currently in use.
+# 3) Verify that the attempt fails.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-09-13)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify cannot set quota lower than the space currently in use"
+
+function cleanup
+{
+ log_must $ZFS set quota=none $TESTPOOL/$TESTFS
+}
+
+log_onexit cleanup
+
+typeset -l quota_integer_size=0
+typeset invalid_size="123! @456 7#89 0\$ abc123% 123%s 12%s3 %c123 123%d %x123 12%p3 \
+ ^def456 789&ghi"
+typeset -l space_used=`get_prop used $TESTPOOL/$TESTFS`
+(( quota_integer_size = space_used - 1 ))
+typeset -l quota_fp_size=${quota_integer_size}.123
+
+for size in 0 -1 $quota_integer_size -$quota_integer_size $quota_fp_size -$quota_fp_size \
+ $invalid_size ; do
+ log_mustnot $ZFS set quota=$size $TESTPOOL/$TESTFS
+done
+log_must $ZFS set quota=$space_used $TESTPOOL/$TESTFS
+
+log_pass "As expected cannot set quota lower than space currently in use"
diff --git a/tests/sys/cddl/zfs/tests/quota/quota_test.sh b/tests/sys/cddl/zfs/tests/quota/quota_test.sh
new file mode 100755
index 000000000000..60781ba2bf5c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/quota/quota_test.sh
@@ -0,0 +1,192 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case quota_001_pos cleanup
+quota_001_pos_head()
+{
+ atf_set "descr" "Verify that file size is limited by the file system quota"
+ atf_set "require.progs" "ksh93 zfs"
+}
+quota_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/quota.kshlib
+ . $(atf_get_srcdir)/quota.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/quota_001_pos.ksh || atf_fail "Testcase failed"
+}
+quota_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/quota.kshlib
+ . $(atf_get_srcdir)/quota.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case quota_002_pos cleanup
+quota_002_pos_head()
+{
+ atf_set "descr" "Verify that a file write cannot exceed the file system quota"
+ atf_set "require.progs" "ksh93 zfs"
+}
+quota_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/quota.kshlib
+ . $(atf_get_srcdir)/quota.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/quota_002_pos.ksh || atf_fail "Testcase failed"
+}
+quota_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/quota.kshlib
+ . $(atf_get_srcdir)/quota.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case quota_003_pos cleanup
+quota_003_pos_head()
+{
+ atf_set "descr" "Verify that file size is limited by the file system quota(dataset version)"
+ atf_set "require.progs" "ksh93 zfs"
+}
+quota_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/quota.kshlib
+ . $(atf_get_srcdir)/quota.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/quota_003_pos.ksh || atf_fail "Testcase failed"
+}
+quota_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/quota.kshlib
+ . $(atf_get_srcdir)/quota.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case quota_004_pos cleanup
+quota_004_pos_head()
+{
+ atf_set "descr" "Verify that a file write cannot exceed the file system quota(dataset version)"
+ atf_set "require.progs" "ksh93 zfs"
+}
+quota_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/quota.kshlib
+ . $(atf_get_srcdir)/quota.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/quota_004_pos.ksh || atf_fail "Testcase failed"
+}
+quota_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/quota.kshlib
+ . $(atf_get_srcdir)/quota.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case quota_005_pos cleanup
+quota_005_pos_head()
+{
+ atf_set "descr" "Verify that quota does not inherit its value from parent."
+ atf_set "require.progs" "ksh93 zfs"
+}
+quota_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/quota.kshlib
+ . $(atf_get_srcdir)/quota.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/quota_005_pos.ksh || atf_fail "Testcase failed"
+}
+quota_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/quota.kshlib
+ . $(atf_get_srcdir)/quota.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case quota_006_neg cleanup
+quota_006_neg_head()
+{
+ atf_set "descr" "Verify cannot set quota lower than the space currently in use"
+ atf_set "require.progs" "ksh93 zfs"
+}
+quota_006_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/quota.kshlib
+ . $(atf_get_srcdir)/quota.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/quota_006_neg.ksh || atf_fail "Testcase failed"
+}
+quota_006_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/quota.kshlib
+ . $(atf_get_srcdir)/quota.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case quota_001_pos
+ atf_add_test_case quota_002_pos
+ atf_add_test_case quota_003_pos
+ atf_add_test_case quota_004_pos
+ atf_add_test_case quota_005_pos
+ atf_add_test_case quota_006_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/quota/setup.ksh b/tests/sys/cddl/zfs/tests/quota/setup.ksh
new file mode 100644
index 000000000000..4fac0a11eca3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/quota/setup.ksh
@@ -0,0 +1,32 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/quota/quota.kshlib
+
+DISK=${DISKS%% *}
+
+default_container_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/redundancy/Makefile b/tests/sys/cddl/zfs/tests/redundancy/Makefile
new file mode 100644
index 000000000000..30345b04cc78
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/redundancy/Makefile
@@ -0,0 +1,20 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/redundancy
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= redundancy_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= redundancy.cfg
+${PACKAGE}FILES+= redundancy_003_pos.ksh
+${PACKAGE}FILES+= redundancy_002_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= redundancy.kshlib
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= redundancy_001_pos.ksh
+${PACKAGE}FILES+= redundancy_004_neg.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/redundancy/cleanup.ksh b/tests/sys/cddl/zfs/tests/redundancy/cleanup.ksh
new file mode 100644
index 000000000000..f180d1421cb3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/redundancy/cleanup.ksh
@@ -0,0 +1,33 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/redundancy/redundancy.kshlib
+
+verify_runnable "global"
+
+cleanup
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/redundancy/redundancy.cfg b/tests/sys/cddl/zfs/tests/redundancy/redundancy.cfg
new file mode 100644
index 000000000000..96ad0f2fcb91
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/redundancy/redundancy.cfg
@@ -0,0 +1,38 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export BASEDIR=$TMPDIR/basedir.${TESTCASE_ID}
+export TESTFILE=testfile.${TESTCASE_ID}
+
+export PRE_RECORD_FILE=$BASEDIR/pre-record-file.${TESTCASE_ID}
+export PST_RECORD_FILE=$BASEDIR/pst-record-file.${TESTCASE_ID}
+
+export DEV_SIZE=64M
+export STF_TIMEOUT=1800
+
+export BLOCKSZ=$(( 1024 * 1024 ))
+export NUM_WRITES=40
+
diff --git a/tests/sys/cddl/zfs/tests/redundancy/redundancy.kshlib b/tests/sys/cddl/zfs/tests/redundancy/redundancy.kshlib
new file mode 100644
index 000000000000..f17010b53bd1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/redundancy/redundancy.kshlib
@@ -0,0 +1,299 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+function cleanup
+{
+ # Log the status of the pool to assist failures.
+ poolexists $TESTPOOL && $ZPOOL status -v $TESTPOOL
+ destroy_pool $TESTPOOL
+ typeset dir
+ for dir in $TESTDIR $BASEDIR; do
+ if [[ -d $dir ]]; then
+ log_must $RM -rf $dir
+ fi
+ done
+}
+
+#
+# Record the directories construction and checksum all the files which reside
+# within the specified pool
+#
+# $1 The specified pool
+# $2 The file which save the record.
+#
+function record_data
+{
+ typeset pool=$1
+ typeset recordfile=$2
+
+ [[ -z $pool ]] && log_fail "No specified pool."
+ [[ -f $recordfile ]] && log_must $RM -f $recordfile
+
+ typeset mntpnt
+ mntpnt=$(get_prop mountpoint $pool)
+ log_must eval "$DU -a $mntpnt > $recordfile 2>&1"
+ #
+ # When the data was damaged, checksum is failing and return 1
+ # So, will not use log_must
+ #
+ $FIND $mntpnt -type f -exec $CKSUM {} + >> $recordfile 2>&1
+}
+
+#
+# Create test pool and fill with files and directories.
+#
+# $1 pool name
+# $2 pool type
+# $3 virtual devices number
+#
+function setup_test_env
+{
+ typeset pool=$1
+ typeset keyword=$2
+ typeset -i vdev_cnt=$3
+ typeset vdevs
+
+ typeset -i i=0
+ while (( i < vdev_cnt )); do
+ vdevs="$vdevs $BASEDIR/vdev$i"
+ ((i += 1))
+ done
+
+ log_must $MKDIR -p $BASEDIR
+ destroy_pool $pool
+ log_must create_vdevs $vdevs
+
+ $ECHO $vdevs | tr ' ' '\n' > $BASEDIR/vdevs
+ log_must $ZPOOL create -m $TESTDIR $pool $keyword $vdevs
+
+ typeset file=$TESTDIR/file
+ log_must $FILE_WRITE -o create -f $file -b $BLOCKSZ -c $NUM_WRITES
+ force_sync_path $BASEDIR
+ record_data $TESTPOOL $PRE_RECORD_FILE
+}
+
+#
+# Check pool data is valid
+#
+# $1 pool
+#
+function is_data_valid
+{
+ typeset pool=$1
+
+ record_data $pool $PST_RECORD_FILE
+ if ! $DIFF $PRE_RECORD_FILE $PST_RECORD_FILE > /dev/null 2>&1; then
+ return 1
+ fi
+
+ return 0
+}
+
+#
+# Get the specified count devices name
+#
+# $1 pool name
+# $2 devices count
+#
+function get_vdevs #pool cnt
+{
+ typeset pool=$1
+ typeset -i cnt=$2
+
+ head -$cnt $BASEDIR/vdevs | tr '\n' ' '
+}
+
+#
+# Synchronize all the data in pool
+#
+# $1 pool name
+#
+function sync_pool #pool
+{
+ typeset pool=$1
+
+ force_sync_path $BASEDIR
+
+ # If the OS has detected corruption on the pool, it will have
+ # automatically initiated a scrub. In that case, our "zpool scrub"
+ # command will fail. So we ignore its exit status and just check that
+ # the pool is scrubbing or has been scrubbed.
+ $ZPOOL scrub $pool >/dev/null 2>&1
+ is_pool_scrubbing $pool || is_pool_scrubbed $pool || \
+ log_fail "$ZPOOL scrub $pool failed."
+ log_note "$pool: $ZPOOL scrub issued."
+}
+
+#
+# Create and replace the same name virtual device files
+#
+# $1 pool name
+# $2-n virtual device files
+#
+function replace_missing_devs
+{
+ typeset pool=$1
+ shift
+
+ typeset vdev
+ for vdev in $@; do
+ [ ! -f $vdev ] && log_must create_vdevs $vdev
+ log_must $ZPOOL replace -f $pool $vdev $vdev
+ wait_for 20 1 is_pool_resilvered $pool
+ done
+}
+
+#
+# Damage the labels of the specified devices. Returns 0 if all such devices
+# are UNAVAIL, 1 otherwise.
+#
+function damage_dev_labels # pool <vdev> [vdev ...]
+{
+ typeset pool=$1
+ typeset -i ret=0
+ shift
+
+ for vdev in $*; do
+ check_state $pool $vdev UNAVAIL && continue
+ log_must create_vdevs $vdev
+ ret=1
+ done
+ [ $ret -eq 0 ] && return $ret
+ sync_pool $pool
+ return $ret
+}
+
+#
+# Damage the pool's virtual device files.
+#
+# $1 pool name
+# $2 Failing devices count
+# $3 damage vdevs method, if not null, we keep the label for the vdevs
+#
+function damage_devs
+{
+ typeset pool=$1
+ typeset -i cnt=$2
+ typeset label="$3"
+ typeset vdevs
+ typeset -i bs_count
+
+ vdevs=$(get_vdevs $pool $cnt)
+ log_note "Damaging pool $pool devices: $vdevs"
+ if [[ -n $label ]]; then
+ typeset -i i=0
+ log_mustnot pool_has_errors $pool
+ while [ $i -lt $cnt ]; do
+ corrupt_file $TESTPOOL $TESTDIR/file $i
+ (( i += 1 ))
+ done
+ sync_pool $pool
+ wait_for 20 1 is_pool_scrubbed $pool
+
+ log_must pool_has_errors $pool
+ else
+ # The pool can be syncing, thus fixing its labels. So we
+ # have to keep trying until all the devices go offline.
+ wait_for 20 1 damage_dev_labels $pool $vdevs
+ fi
+
+ log_note "Pool $pool vdevs $vdevs damage completed."
+}
+
+#
+# Clear errors in the pool caused by data corruptions
+#
+# $1 pool name
+#
+function clear_errors
+{
+ typeset pool=$1
+
+ log_must $ZPOOL clear $pool
+ # The pool may need to resilver (issued async by 'zpool clear'),
+ # give it a chance to do so.
+ wait_for 30 1 is_pool_healthy $pool
+
+ if ! is_data_valid $pool ; then
+ $ZPOOL status -x $pool
+ log_note "Data should be valid in $pool."
+ return 1
+ fi
+
+ return 0
+}
+
+#
+# Remove the specified pool's virtual device files
+#
+# $1 Pool name
+# $2 Missing devices count
+#
+function remove_devs
+{
+ typeset pool=$1
+ typeset -i cnt=$2
+ typeset vdevs
+
+ vdevs=$(get_vdevs $pool $cnt)
+ log_note "Removing pool $pool vdevs: $vdevs"
+ log_must $RM -f $vdevs
+
+ sync_pool $pool
+ for vdev in $vdevs; do
+ wait_for 20 1 check_state $pool $vdev UNAVAIL
+ done
+}
+
+#
+# Recover the bad or missing device files in the pool
+#
+# $1 Pool name
+# $2 Missing devices count
+#
+function recover_bad_missing_devs
+{
+ typeset pool=$1
+ typeset -i cnt=$2
+ typeset vdevs
+
+ vdevs=$(get_vdevs $pool $cnt)
+ log_note "Replacing missing pool $pool vdevs: $vdevs"
+ replace_missing_devs $pool $vdevs
+
+ if ! is_pool_healthy $pool ; then
+ log_note "$pool should be healthy."
+ return 1
+ fi
+ if ! is_data_valid $pool ; then
+ log_note "Data should be valid in $pool."
+ return 1
+ fi
+
+ return 0
+}
diff --git a/tests/sys/cddl/zfs/tests/redundancy/redundancy_001_pos.ksh b/tests/sys/cddl/zfs/tests/redundancy/redundancy_001_pos.ksh
new file mode 100644
index 000000000000..eafab3ed9f04
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/redundancy/redundancy_001_pos.ksh
@@ -0,0 +1,85 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/redundancy/redundancy.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: redundancy_001_pos
+#
+# DESCRIPTION:
+# A raidz pool can withstand at most 1 device failing or missing.
+#
+# STRATEGY:
+# 1. Create N(>2,<5) virtual disk files.
+# 2. Create raidz pool based on the virtual disk files.
+# 3. Fill the filesystem with directories and files.
+# 4. Record all the files and directories checksum information.
+# 5. Damaged one of the virtual disk file.
+# 6. Verify the data is correct to prove raidz can withstand 1 devicd is
+# failing.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-08-21)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Verify raidz pool can withstand one device is failing."
+
+for cnt in 3 2; do
+ setup_test_env $TESTPOOL raidz $cnt
+
+ #
+ # Inject data corruption error for raidz pool
+ #
+ damage_devs $TESTPOOL 1 "label"
+ log_must is_data_valid $TESTPOOL
+ log_must clear_errors $TESTPOOL
+
+ #
+ # Inject bad device error for raidz pool
+ #
+ damage_devs $TESTPOOL 1
+ log_must is_data_valid $TESTPOOL
+ log_must recover_bad_missing_devs $TESTPOOL 1
+
+ #
+ # Inject missing device error for raidz pool
+ #
+ remove_devs $TESTPOOL 1
+ log_must is_data_valid $TESTPOOL
+done
+
+log_pass "Raidz pool can withstand one devices is failing passed."
diff --git a/tests/sys/cddl/zfs/tests/redundancy/redundancy_002_pos.ksh b/tests/sys/cddl/zfs/tests/redundancy/redundancy_002_pos.ksh
new file mode 100644
index 000000000000..350a3eb56e63
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/redundancy/redundancy_002_pos.ksh
@@ -0,0 +1,93 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/redundancy/redundancy.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: redundancy_002_pos
+#
+# DESCRIPTION:
+# A raidz2 pool can withstand 2 devices are failing or missing.
+#
+# STRATEGY:
+# 1. Create N(>3,<5) virtual disk files.
+# 2. Create raidz2 pool based on the virtual disk files.
+# 3. Fill the filesystem with directories and files.
+# 4. Record all the files and directories checksum information.
+# 5. Damaged at most two of the virtual disk files.
+# 6. Verify the data is correct to prove raidz2 can withstand 2 devices
+# are failing.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-08-21)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Verify raidz2 pool can withstand two devices are failing."
+log_onexit cleanup
+
+for cnt in 3 4; do
+ setup_test_env $TESTPOOL raidz2 $cnt
+
+ #
+ # Inject data corruption errors for raidz2 pool
+ #
+ for i in 1 2; do
+ damage_devs $TESTPOOL $i "label"
+ log_must is_data_valid $TESTPOOL
+ log_must clear_errors $TESTPOOL
+ done
+
+ #
+ # Inject bad devices errors for raidz2 pool
+ #
+ for i in 1 2; do
+ damage_devs $TESTPOOL $i
+ log_must is_data_valid $TESTPOOL
+ log_must recover_bad_missing_devs $TESTPOOL $i
+ done
+
+ #
+ # Inject missing device errors for raidz2 pool
+ #
+ for i in 1 2; do
+ remove_devs $TESTPOOL $i
+ log_must is_data_valid $TESTPOOL
+ log_must recover_bad_missing_devs $TESTPOOL $i
+ done
+done
+
+log_pass "Raidz2 pool can withstand two devices are failing passed."
diff --git a/tests/sys/cddl/zfs/tests/redundancy/redundancy_003_pos.ksh b/tests/sys/cddl/zfs/tests/redundancy/redundancy_003_pos.ksh
new file mode 100644
index 000000000000..772117a931f9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/redundancy/redundancy_003_pos.ksh
@@ -0,0 +1,103 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/redundancy/redundancy.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: redundancy_003_pos
+#
+# DESCRIPTION:
+# A mirrored pool can withstand N-1 device are failing or missing.
+#
+# STRATEGY:
+# 1. Create N(>2,<5) virtual disk files.
+# 2. Create mirror pool based on the virtual disk files.
+# 3. Fill the filesystem with directories and files.
+# 4. Record all the files and directories checksum information.
+# 5. Damaged at most N-1 of the virtual disk files.
+# 6. Verify the data are correct to prove mirror can withstand N-1 devices
+# are failing.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-08-21)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Verify mirrored pool can withstand N-1 devices are failing or missing."
+log_onexit cleanup
+
+for cnt in 3 2; do
+ typeset -i i=1
+
+ setup_test_env $TESTPOOL mirror $cnt
+
+ #
+ # Inject data corruption errors for mirrored pool
+ #
+ while (( i < cnt )); do
+ damage_devs $TESTPOOL $i "label"
+ log_must is_data_valid $TESTPOOL
+ log_must clear_errors $TESTPOOL
+
+ (( i +=1 ))
+ done
+
+ #
+ # Inject bad devices errors for mirrored pool
+ #
+ i=1
+ while (( i < cnt )); do
+ damage_devs $TESTPOOL $i
+ log_must is_data_valid $TESTPOOL
+ log_must recover_bad_missing_devs $TESTPOOL $i
+
+ (( i +=1 ))
+ done
+
+ #
+ # Inject missing device errors for mirrored pool
+ #
+ i=1
+ while (( i < cnt )); do
+ remove_devs $TESTPOOL $i
+ log_must is_data_valid $TESTPOOL
+ log_must recover_bad_missing_devs $TESTPOOL $i
+
+ (( i +=1 ))
+ done
+done
+
+log_pass "Mirrored pool can withstand N-1 devices failing as expected."
diff --git a/tests/sys/cddl/zfs/tests/redundancy/redundancy_004_neg.ksh b/tests/sys/cddl/zfs/tests/redundancy/redundancy_004_neg.ksh
new file mode 100644
index 000000000000..20168076983e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/redundancy/redundancy_004_neg.ksh
@@ -0,0 +1,69 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/redundancy/redundancy.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: redundancy_004_neg
+#
+# DESCRIPTION:
+# Striped pool have no data redundancy. Any device errors will
+# cause data corruption.
+#
+# STRATEGY:
+# 1. Create N virtual disk file.
+# 2. Create stripe pool based on the virtual disk files.
+# 3. Fill the filesystem with directories and files.
+# 4. Record all the files and directories checksum information.
+# 5. Damage one of the virtual disk file.
+# 6. Verify the data is error.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-08-17)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Verify striped pool have no data redundancy."
+log_onexit cleanup
+
+for cnt in 2 3; do
+ setup_test_env $TESTPOOL "" $cnt
+ damage_devs $TESTPOOL 1 "keep_label"
+ log_must $ZPOOL clear $TESTPOOL
+ log_mustnot is_pool_healthy $TESTPOOL
+done
+
+log_pass "Striped pool has no data redundancy as expected."
diff --git a/tests/sys/cddl/zfs/tests/redundancy/redundancy_test.sh b/tests/sys/cddl/zfs/tests/redundancy/redundancy_test.sh
new file mode 100755
index 000000000000..83dfc7f89417
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/redundancy/redundancy_test.sh
@@ -0,0 +1,135 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case redundancy_001_pos cleanup
+redundancy_001_pos_head()
+{
+ atf_set "descr" "Verify raidz pool can withstand one device is failing."
+ atf_set "timeout" 1800
+}
+redundancy_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/redundancy.kshlib
+ . $(atf_get_srcdir)/redundancy.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/redundancy_001_pos.ksh || atf_fail "Testcase failed"
+}
+redundancy_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/redundancy.kshlib
+ . $(atf_get_srcdir)/redundancy.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case redundancy_002_pos cleanup
+redundancy_002_pos_head()
+{
+ atf_set "descr" "Verify raidz2 pool can withstand two devices are failing."
+ atf_set "timeout" 1800
+}
+redundancy_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/redundancy.kshlib
+ . $(atf_get_srcdir)/redundancy.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/redundancy_002_pos.ksh || atf_fail "Testcase failed"
+}
+redundancy_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/redundancy.kshlib
+ . $(atf_get_srcdir)/redundancy.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case redundancy_003_pos cleanup
+redundancy_003_pos_head()
+{
+ atf_set "descr" "Verify mirrored pool can withstand N-1 devices are failing or missing."
+ atf_set "timeout" 1800
+}
+redundancy_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/redundancy.kshlib
+ . $(atf_get_srcdir)/redundancy.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/redundancy_003_pos.ksh || atf_fail "Testcase failed"
+}
+redundancy_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/redundancy.kshlib
+ . $(atf_get_srcdir)/redundancy.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case redundancy_004_neg cleanup
+redundancy_004_neg_head()
+{
+ atf_set "descr" "Verify striped pool have no data redundancy."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1800
+}
+redundancy_004_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/redundancy.kshlib
+ . $(atf_get_srcdir)/redundancy.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/redundancy_004_neg.ksh || atf_fail "Testcase failed"
+}
+redundancy_004_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/redundancy.kshlib
+ . $(atf_get_srcdir)/redundancy.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case redundancy_001_pos
+ atf_add_test_case redundancy_002_pos
+ atf_add_test_case redundancy_003_pos
+ atf_add_test_case redundancy_004_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/redundancy/setup.ksh b/tests/sys/cddl/zfs/tests/redundancy/setup.ksh
new file mode 100644
index 000000000000..93c4aaa8af0d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/redundancy/setup.ksh
@@ -0,0 +1,32 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+verify_runnable "global"
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/refquota/Makefile b/tests/sys/cddl/zfs/tests/refquota/Makefile
new file mode 100644
index 000000000000..0b86cf46eaf5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/refquota/Makefile
@@ -0,0 +1,21 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/refquota
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= refquota_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= refquota_001_pos.ksh
+${PACKAGE}FILES+= refquota_005_pos.ksh
+${PACKAGE}FILES+= refquota.cfg
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= refquota_004_pos.ksh
+${PACKAGE}FILES+= refquota_002_pos.ksh
+${PACKAGE}FILES+= refquota_003_pos.ksh
+${PACKAGE}FILES+= refquota_006_neg.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/refquota/cleanup.ksh b/tests/sys/cddl/zfs/tests/refquota/cleanup.ksh
new file mode 100644
index 000000000000..fedd0b782464
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/refquota/cleanup.ksh
@@ -0,0 +1,34 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "both"
+if ! fs_prop_exist "refquota" ; then
+ log_unsupported "refquota is not supported by this release."
+fi
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/refquota/refquota.cfg b/tests/sys/cddl/zfs/tests/refquota/refquota.cfg
new file mode 100644
index 000000000000..a2d61f141178
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/refquota/refquota.cfg
@@ -0,0 +1,27 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export TESTFILE=testfile${TESTCASE_ID}
diff --git a/tests/sys/cddl/zfs/tests/refquota/refquota_001_pos.ksh b/tests/sys/cddl/zfs/tests/refquota/refquota_001_pos.ksh
new file mode 100644
index 000000000000..2eb05336b9f0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/refquota/refquota_001_pos.ksh
@@ -0,0 +1,86 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: refquota_001_pos
+#
+# DESCRIPTION:
+# refquota limits the amount of space a dataset can consume, but does
+# not include space used by descendents.
+#
+# STRATEGY:
+# 1. Setting refquota in given filesystem
+# 2. Create descendent filesystem
+# 3. Verify refquota limits the amount of space a dataset can consume
+# 4. Verify the limit does not impact descendents
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-12-13)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ log_must $ZFS destroy -rf $TESTPOOL/$TESTFS
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+}
+
+log_assert "refquota limits the amount of space a dataset can consume, " \
+ "but does not include space used by descendents."
+log_onexit cleanup
+
+fs=$TESTPOOL/$TESTFS
+sub=$fs/sub
+log_must $ZFS create $sub
+
+log_must $ZFS set refquota=10M $fs
+mntpnt=$(get_prop mountpoint $fs)
+
+log_mustnot $MKFILE 11M $mntpnt/file
+log_must $MKFILE 9M $mntpnt/file
+log_must $ZFS snapshot $fs@snap
+log_mustnot $MKFILE 2M $mntpnt/file2
+
+mntpnt=$(get_prop mountpoint $sub)
+log_must $MKFILE 10M $mntpnt/file
+log_must $ZFS snapshot $sub@snap
+log_must $MKFILE 10 $mntpnt/file2
+
+log_pass "refquota limits the amount of space a dataset can consume, " \
+ "but does not include space used by descendents."
diff --git a/tests/sys/cddl/zfs/tests/refquota/refquota_002_pos.ksh b/tests/sys/cddl/zfs/tests/refquota/refquota_002_pos.ksh
new file mode 100644
index 000000000000..d47ce5263314
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/refquota/refquota_002_pos.ksh
@@ -0,0 +1,98 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: refquota_002_pos
+#
+# DESCRIPTION:
+# Quotas are enforced using the minimum of the two properties:
+# quota & refquota
+#
+# STRATEGY:
+# 1. Set value for quota and refquota. Quota less than refquota.
+# 2. Creating file which should be limited by quota.
+# 3. Switch the value of quota and refquota.
+# 4. Verify file should be limited by refquota.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-11-02)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ log_must $ZFS destroy -rf $TESTPOOL/$TESTFS
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+}
+
+log_assert "Quotas are enforced using the minimum of the two properties"
+log_onexit cleanup
+
+fs=$TESTPOOL/$TESTFS
+log_must $ZFS set quota=15M $fs
+log_must $ZFS set refquota=25M $fs
+
+mntpnt=$(get_prop mountpoint $fs)
+log_mustnot $MKFILE 20M $mntpnt/$TESTFILE
+typeset -i used quota
+used=$(get_prop used $fs)
+quota=$(get_prop quota $fs)
+((used = used / (1024 * 1024)))
+((quota = quota / (1024 * 1024)))
+if [[ $used -ne $quota ]]; then
+ log_fail "ERROR: $used -ne $quota Quotas are not limited by quota"
+fi
+
+#
+# Switch the value of them and try again
+#
+[ -f $mntpnt/$TESTFILE ] && log_must $RM $mntpnt/$TESTFILE
+log_must $ZFS set quota=25M $fs
+log_must $ZFS set refquota=15M $fs
+
+log_mustnot $MKFILE 20M $mntpnt/$TESTFILE
+used=$(get_prop used $fs)
+refquota=$(get_prop refquota $fs)
+((used = used / (1024 * 1024)))
+((refquota = refquota / (1024 * 1024)))
+if [[ $used -ne $refquota ]]; then
+ log_fail "ERROR: $used -ne $refquota Quotas are not limited by refquota"
+fi
+
+log_pass "Quotas are enforced using the minimum of the two properties"
diff --git a/tests/sys/cddl/zfs/tests/refquota/refquota_003_pos.ksh b/tests/sys/cddl/zfs/tests/refquota/refquota_003_pos.ksh
new file mode 100644
index 000000000000..56d705eb84d8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/refquota/refquota_003_pos.ksh
@@ -0,0 +1,91 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: refquota_003_pos
+#
+# DESCRIPTION:
+# Sub-filesystem quotas are not enforced by property 'refquota'
+#
+# STRATEGY:
+# 1. Setting quota and refquota for parent. refquota < quota
+# 2. Verify sub-filesystem will not be limited by refquota
+# 3. Verify sub-filesystem will only be limited by quota
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-11-02)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ log_must $ZFS destroy -rf $TESTPOOL/$TESTFS
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+}
+
+log_assert "Sub-filesystem quotas are not enforced by property 'refquota'"
+log_onexit cleanup
+
+fs=$TESTPOOL/$TESTFS
+log_must $ZFS set quota=25M $fs
+log_must $ZFS set refquota=10M $fs
+log_must $ZFS create $fs/subfs
+
+mntpnt=$(get_prop mountpoint $fs/subfs)
+log_must $MKFILE 20M $mntpnt/$TESTFILE
+
+typeset -i used quota refquota
+used=$(get_prop used $fs)
+refquota=$(get_prop refquota $fs)
+((used = used / (1024 * 1024)))
+((refquota = refquota / (1024 * 1024)))
+if [[ $used -lt $refquota ]]; then
+ log_fail "ERROR: $used < $refquota subfs quotas are limited by refquota"
+fi
+
+log_mustnot $MKFILE 20M $mntpnt/$TESTFILE.2
+used=$(get_prop used $fs)
+quota=$(get_prop quota $fs)
+((used = used / (1024 * 1024)))
+((quota = quota / (1024 * 1024)))
+if [[ $used -gt $quota ]]; then
+ log_fail "ERROR: $used > $quota subfs quotas aren't limited by quota"
+fi
+
+log_pass "Sub-filesystem quotas are not enforced by property 'refquota'"
diff --git a/tests/sys/cddl/zfs/tests/refquota/refquota_004_pos.ksh b/tests/sys/cddl/zfs/tests/refquota/refquota_004_pos.ksh
new file mode 100644
index 000000000000..ce3578109349
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/refquota/refquota_004_pos.ksh
@@ -0,0 +1,84 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: refquota_004_pos
+#
+# DESCRIPTION:
+# refquotas are not limited by snapshots.
+#
+# STRATEGY:
+# 1. Setting refquota < quota
+# 2. Create file in filesytem, take snapshot and remove the file
+# 3. Verify snapshot will not consume refquota
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-11-02)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ log_must $ZFS destroy -rf $TESTPOOL/$TESTFS
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+}
+
+log_assert "refquotas are not limited by snapshots."
+log_onexit cleanup
+
+fs=$TESTPOOL/$TESTFS
+log_must $ZFS set quota=25M $fs
+log_must $ZFS set refquota=15M $fs
+
+mntpnt=$(get_prop mountpoint $fs)
+typeset -i i=0
+while ((i < 3)); do
+ log_must $MKFILE 7M $mntpnt/$TESTFILE.$i
+ log_must $ZFS snapshot $fs@snap.$i
+ log_must $RM $mntpnt/$TESTFILE.$i
+
+ ((i += 1))
+done
+
+#
+# Verify out of the limitation of 'quota'
+#
+log_mustnot $MKFILE 7M $mntpnt/$TESTFILE
+
+log_pass "refquotas are not limited by snapshots."
diff --git a/tests/sys/cddl/zfs/tests/refquota/refquota_005_pos.ksh b/tests/sys/cddl/zfs/tests/refquota/refquota_005_pos.ksh
new file mode 100644
index 000000000000..d73147be8cfa
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/refquota/refquota_005_pos.ksh
@@ -0,0 +1,85 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: refquota_005_pos
+#
+# DESCRIPTION:
+# refquotas are not limited by sub-filesystem snapshots.
+#
+# STRATEGY:
+# 1. Setting refquota < quota for parent
+# 2. Create file in sub-filesytem, take snapshot and remove the file
+# 3. Verify sub-filesystem snapshot will not consume refquota
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-11-02)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ log_must $ZFS destroy -rf $TESTPOOL/$TESTFS
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+}
+
+log_assert "refquotas are not limited by sub-filesystem snapshots."
+log_onexit cleanup
+
+fs=$TESTPOOL/$TESTFS
+log_must $ZFS set quota=25M $fs
+log_must $ZFS set refquota=15M $fs
+log_must $ZFS create $fs/subfs
+
+mntpnt=$(get_prop mountpoint $fs/subfs)
+typeset -i i=0
+while ((i < 3)); do
+ log_must $MKFILE 7M $mntpnt/$TESTFILE.$i
+ log_must $ZFS snapshot $fs/subfs@snap.$i
+ log_must $RM $mntpnt/$TESTFILE.$i
+
+ ((i += 1))
+done
+
+#
+# Verify out of the limitation of 'quota'
+#
+log_mustnot $MKFILE 7M $mntpnt/$TESTFILE
+
+log_pass "refquotas are not limited by sub-filesystem snapshots"
diff --git a/tests/sys/cddl/zfs/tests/refquota/refquota_006_neg.ksh b/tests/sys/cddl/zfs/tests/refquota/refquota_006_neg.ksh
new file mode 100644
index 000000000000..934718ba745a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/refquota/refquota_006_neg.ksh
@@ -0,0 +1,87 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: refquota_006_neg
+#
+# DESCRIPTION:
+# 'zfs set refquota/refreserv' can handle incorrect arguments correctly.
+#
+# STRATEGY:
+# 1. Setup incorrect arguments arrays.
+# 2. Set the bad argument to refquota.
+# 3. Verify zfs can handle it correctly.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-11-09)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ log_must $ZFS set refquota=none $TESTPOOL/$TESTFS
+ log_must $ZFS set refreserv=none $TESTPOOL/$TESTFS
+}
+
+log_assert "'zfs set refquota' can handle incorrect arguments correctly."
+log_onexit cleanup
+
+set -A badopt \
+ "None" "-1" "1TT" "%5" \
+ "123!" "@456" "7#89" "0\$" \
+ "abc123%" "123%s" "12%s3" "%c123" \
+ "123%d" "%x123" "12%p3" "^def456" \
+ "x0"
+
+typeset -i i=0
+while ((i < ${#badopt[@]})); do
+ log_mustnot $ZFS set refquota=${badopt[$i]} $TESTPOOL/$TESTFS
+ log_mustnot $ZFS set refreserv=${badopt[$i]} $TESTPOOL/$TESTFS
+
+ ((i += 1))
+done
+
+# Try using a null as the opt value. We can't use log_mustnot, because
+# that echoes the character, which screws up ATF by creating a non-well formed
+# XML file
+$ZFS set refquota="\0" $TESTPOOL/$TESTFS > /dev/null 2>&1
+[[ $? != 0 ]] || log_fail "FAILURE: zfs set refquota=\\\\0 passed"
+$ZFS set refreserv="\0" $TESTPOOL/$TESTFS > /dev/null 2>&1
+[[ $? != 0 ]] || log_fail "FAILURE: zfs set refreserv=\\\\0 passed"
+
+log_pass "'zfs set refquota' can handle incorrect arguments correctly."
diff --git a/tests/sys/cddl/zfs/tests/refquota/refquota_test.sh b/tests/sys/cddl/zfs/tests/refquota/refquota_test.sh
new file mode 100755
index 000000000000..06140febee92
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/refquota/refquota_test.sh
@@ -0,0 +1,180 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case refquota_001_pos cleanup
+refquota_001_pos_head()
+{
+ atf_set "descr" "refquota limits the amount of space a dataset can consume,but does not include space used by descendents."
+ atf_set "require.progs" "ksh93 zfs"
+}
+refquota_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/refquota.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/refquota_001_pos.ksh || atf_fail "Testcase failed"
+}
+refquota_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/refquota.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case refquota_002_pos cleanup
+refquota_002_pos_head()
+{
+ atf_set "descr" "Quotas are enforced using the minimum of the two properties"
+ atf_set "require.progs" "ksh93 zfs"
+}
+refquota_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/refquota.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/refquota_002_pos.ksh || atf_fail "Testcase failed"
+}
+refquota_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/refquota.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case refquota_003_pos cleanup
+refquota_003_pos_head()
+{
+ atf_set "descr" "Sub-filesystem quotas are not enforced by property 'refquota'"
+ atf_set "require.progs" "ksh93 zfs"
+}
+refquota_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/refquota.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/refquota_003_pos.ksh || atf_fail "Testcase failed"
+}
+refquota_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/refquota.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case refquota_004_pos cleanup
+refquota_004_pos_head()
+{
+ atf_set "descr" "refquotas are not limited by snapshots."
+ atf_set "require.progs" "ksh93 zfs"
+}
+refquota_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/refquota.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/refquota_004_pos.ksh || atf_fail "Testcase failed"
+}
+refquota_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/refquota.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case refquota_005_pos cleanup
+refquota_005_pos_head()
+{
+ atf_set "descr" "refquotas are not limited by sub-filesystem snapshots."
+ atf_set "require.progs" "ksh93 zfs"
+}
+refquota_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/refquota.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/refquota_005_pos.ksh || atf_fail "Testcase failed"
+}
+refquota_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/refquota.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case refquota_006_neg cleanup
+refquota_006_neg_head()
+{
+ atf_set "descr" "'zfs set refquota' can handle incorrect arguments correctly."
+ atf_set "require.progs" "ksh93 zfs"
+}
+refquota_006_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/refquota.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/refquota_006_neg.ksh || atf_fail "Testcase failed"
+}
+refquota_006_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/refquota.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case refquota_001_pos
+ atf_add_test_case refquota_002_pos
+ atf_add_test_case refquota_003_pos
+ atf_add_test_case refquota_004_pos
+ atf_add_test_case refquota_005_pos
+ atf_add_test_case refquota_006_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/refquota/setup.ksh b/tests/sys/cddl/zfs/tests/refquota/setup.ksh
new file mode 100644
index 000000000000..42392ae2988b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/refquota/setup.ksh
@@ -0,0 +1,35 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "both"
+if ! fs_prop_exist "refquota" ; then
+ log_unsupported "refquota is not supported by this release."
+fi
+
+DISK=${DISKS%% *}
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/refreserv/Makefile b/tests/sys/cddl/zfs/tests/refreserv/Makefile
new file mode 100644
index 000000000000..983cc768a338
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/refreserv/Makefile
@@ -0,0 +1,20 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/refreserv
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= refreserv_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= refreserv_004_pos.ksh
+${PACKAGE}FILES+= refreserv_005_pos.ksh
+${PACKAGE}FILES+= refreserv_001_pos.ksh
+${PACKAGE}FILES+= refreserv_003_pos.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= refreserv.cfg
+${PACKAGE}FILES+= refreserv_002_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/refreserv/cleanup.ksh b/tests/sys/cddl/zfs/tests/refreserv/cleanup.ksh
new file mode 100644
index 000000000000..d14804145cf9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/refreserv/cleanup.ksh
@@ -0,0 +1,34 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "both"
+if ! fs_prop_exist "refreservation" ; then
+ log_unsupported "refreservation is not supported in this system."
+fi
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/refreserv/refreserv.cfg b/tests/sys/cddl/zfs/tests/refreserv/refreserv.cfg
new file mode 100644
index 000000000000..a2d61f141178
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/refreserv/refreserv.cfg
@@ -0,0 +1,27 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export TESTFILE=testfile${TESTCASE_ID}
diff --git a/tests/sys/cddl/zfs/tests/refreserv/refreserv_001_pos.ksh b/tests/sys/cddl/zfs/tests/refreserv/refreserv_001_pos.ksh
new file mode 100644
index 000000000000..3e797893b4aa
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/refreserv/refreserv_001_pos.ksh
@@ -0,0 +1,83 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: refreserv_001_pos
+#
+# DESCRIPTION:
+# Reservations are enforced using the maximum of 'reserv' and 'refreserv'
+#
+# STRATEGY:
+# 1. Setting quota for parent filesystem.
+# 2. Setting reservation and refreservation for sub-filesystem.
+# 3. Verify the sub-fs reservation are enforced by the maximum of 'reserv'
+# and 'refreserv'.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-11-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ log_must $ZFS destroy -rf $TESTPOOL/$TESTFS
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+}
+
+log_assert "Reservations are enforced using the maximum of " \
+ "'reserv' and 'refreserv'"
+log_onexit cleanup
+
+fs=$TESTPOOL/$TESTFS ; subfs=$fs/subfs
+log_must $ZFS create $subfs
+log_must $ZFS set quota=25M $fs
+
+log_must $ZFS set reserv=10M $subfs
+log_must $ZFS set refreserv=20M $subfs
+mntpnt=$(get_prop mountpoint $fs)
+log_mustnot $MKFILE 15M $mntpnt/$TESTFILE
+
+log_must $RM -f $mntpnt/$TESTFILE
+
+log_must $ZFS set reserv=20M $subfs
+log_must $ZFS set refreserv=10M $subfs
+log_mustnot $MKFILE 15M $mntpnt/$TESTFILE
+
+log_pass "Reservations are enforced using the maximum of " \
+ "'reserv' and 'refreserv'"
diff --git a/tests/sys/cddl/zfs/tests/refreserv/refreserv_002_pos.ksh b/tests/sys/cddl/zfs/tests/refreserv/refreserv_002_pos.ksh
new file mode 100644
index 000000000000..f0fe99a39335
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/refreserv/refreserv_002_pos.ksh
@@ -0,0 +1,122 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: refreserv_002_pos
+#
+# DESCRIPTION:
+# Setting full size as refreservation, verify no snapshot can be created.
+#
+# STRATEGY:
+# 1. Setting full size as refreservation on pool
+# 2. Verify no snapshot can be created on this pool
+# 3. Setting full size as refreservation on filesystem
+# 4. Verify no snapshot can be created on it and its subfs
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-11-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if is_global_zone ; then
+ log_must $ZFS set refreservation=none $TESTPOOL
+
+ if datasetexists $TESTPOOL@snap ; then
+ log_must $ZFS destroy -f $TESTPOOL@snap
+ fi
+ fi
+ log_must $ZFS destroy -rf $TESTPOOL/$TESTFS
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+}
+
+# This function iteratively increases refreserv to its highest possible
+# value. Simply setting refreserv == quota can allow enough writes to
+# complete that the test fails.
+function max_refreserv
+{
+ typeset ds=$1
+ typeset -i incsize=131072
+ typeset -i rr=$(get_prop available $ds)
+
+ log_must $ZFS set refreserv=$rr $ds
+ while :; do
+ $ZFS set refreserv=$((rr + incsize)) $ds >/dev/null 2>&1
+ if [[ $? == 0 ]]; then
+ ((rr += incsize))
+ continue
+ else
+ ((incsize /= 2))
+ ((incsize == 0)) && break
+ fi
+ done
+}
+
+
+log_assert "Setting full size as refreservation, verify no snapshot " \
+ "can be created."
+log_onexit cleanup
+
+log_must $ZFS create $TESTPOOL/$TESTFS/subfs
+
+typeset datasets
+if is_global_zone; then
+ datasets="$TESTPOOL $TESTPOOL/$TESTFS $TESTPOOL/$TESTFS/subfs"
+else
+ datasets="$TESTPOOL/$TESTFS $TESTPOOL/$TESTFS/subfs"
+fi
+
+for ds in $datasets; do
+ #
+ # Verify refreservation on dataset
+ #
+ log_must $ZFS set quota=25M $ds
+ max_refreserv $ds
+ log_mustnot $ZFS snapshot $ds@snap
+ if datasetexists $ds@snap ; then
+ log_fail "ERROR: $ds@snap should not exists."
+ fi
+
+ log_must $ZFS set quota=none $ds
+ log_must $ZFS set refreservation=none $ds
+done
+
+log_pass "Setting full size as refreservation, verify no snapshot " \
+ "can be created."
diff --git a/tests/sys/cddl/zfs/tests/refreserv/refreserv_003_pos.ksh b/tests/sys/cddl/zfs/tests/refreserv/refreserv_003_pos.ksh
new file mode 100644
index 000000000000..35c45df1b758
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/refreserv/refreserv_003_pos.ksh
@@ -0,0 +1,85 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: refreserv_003_pos
+#
+# DESCRIPTION:
+# Verify a snapshot will only be allowed if there is enough free pool
+# space outside of this refreservation.
+#
+# STRATEGY:
+# 1. Setting quota and refservation
+# 2. Verify snapshot can be created, when used =< quota - refreserv
+# 3. Verify failed to create snapshot, when used > quota - refreserv
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-11-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ log_must $ZFS destroy -rf $TESTPOOL/$TESTFS
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+}
+
+log_assert "Verify a snapshot will only be allowed if there is enough " \
+ "free space outside of this refreservation."
+log_onexit cleanup
+
+fs=$TESTPOOL/$TESTFS
+log_must $ZFS set quota=25M $fs
+log_must $ZFS set refreservation=10M $fs
+
+mntpnt=$(get_prop mountpoint $fs)
+log_must $MKFILE 7M $mntpnt/$TESTFILE
+log_must $ZFS snapshot $fs@snap
+
+log_must $MKFILE 7M $mntpnt/$TESTFILE.2
+log_must $ZFS snapshot $fs@snap2
+
+log_must $MKFILE 7M $mntpnt/$TESTFILE.3
+log_mustnot $ZFS snapshot $fs@snap3
+if datasetexists $fs@snap3 ; then
+ log_fail "ERROR: $fs@snap3 should not exists."
+fi
+
+log_pass "Verify a snapshot will only be allowed if there is enough " \
+ "free space outside of this refreservation."
diff --git a/tests/sys/cddl/zfs/tests/refreserv/refreserv_004_pos.ksh b/tests/sys/cddl/zfs/tests/refreserv/refreserv_004_pos.ksh
new file mode 100644
index 000000000000..7b0ea3fd3c6d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/refreserv/refreserv_004_pos.ksh
@@ -0,0 +1,97 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: refreserv_004_pos
+#
+# DESCRIPTION:
+# Verify refreservation is limited by available space.
+#
+# STRATEGY:
+# 1. Setting quota and refreservation on parent filesystem.
+# 2. Get available space on sub-filesystem.
+# 3. Verify refreservation is limited by available on it.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-11-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if is_global_zone ; then
+ log_must $ZFS set refreservation=none $TESTPOOL
+ fi
+ log_must $ZFS destroy -rf $TESTPOOL/$TESTFS
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+}
+
+log_assert "Verify refreservation is limited by available space."
+log_onexit cleanup
+
+pool=$TESTPOOL ; fs=$pool/$TESTFS ; subfs=$fs/subfs
+log_must $ZFS create $subfs
+
+typeset datasets
+if is_global_zone; then
+ datasets="$pool $fs"
+else
+ datasets="$fs"
+fi
+
+for ds in $datasets; do
+ log_must $ZFS set quota=25M $ds
+ log_must $ZFS set refreservation=15M $ds
+
+ typeset avail
+ avail=5M
+ log_must $ZFS set refreservation=$avail $subfs
+ typeset mntpnt
+ mntpnt=$(get_prop mountpoint $subfs)
+ log_must $MKFILE $avail $mntpnt/$TESTFILE
+
+ typeset exceed
+ exceed=15M
+ log_mustnot $ZFS set refreservation=$exceed $subfs
+ log_mustnot $MKFILE $exceed $mntpnt/$TESTFILE
+
+ log_must $ZFS set quota=none $ds
+done
+
+log_pass "Verify refreservation is limited by available space."
diff --git a/tests/sys/cddl/zfs/tests/refreserv/refreserv_005_pos.ksh b/tests/sys/cddl/zfs/tests/refreserv/refreserv_005_pos.ksh
new file mode 100644
index 000000000000..50aa45af7bf0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/refreserv/refreserv_005_pos.ksh
@@ -0,0 +1,80 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: refreserv_005_pos
+#
+# DESCRIPTION:
+# Volume refreservation is limited by volsize
+#
+# STRATEGY:
+# 1. Create volume on filesystem
+# 2. Setting quota for parenet filesytem
+# 3. Verify volume refreservation is only limited by volsize
+# 4. Verify volume refreservation can be changed when volsize changed
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-11-07)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ log_must $ZFS destroy -rf $TESTPOOL/$TESTFS
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+}
+
+log_assert "Volume refreservation is limited by volsize"
+log_onexit cleanup
+
+fs=$TESTPOOL/$TESTFS; vol=$fs/vol
+log_must $ZFS create -V 10M $vol
+
+# Verify the parent filesystem does not affect volume
+log_must $ZFS set quota=25M $fs
+log_must $ZFS set refreservation=10M $vol
+avail=$(get_prop mountpoint $vol)
+log_mustnot $ZFS set refreservation=$avail $vol
+
+# Verify it is affected by volsize
+log_must $ZFS set volsize=15M $vol
+log_must $ZFS set refreservation=15M $vol
+log_mustnot $ZFS set refreservation=16M $vol
+
+log_pass "Volume refreservation is limited by volsize"
diff --git a/tests/sys/cddl/zfs/tests/refreserv/refreserv_test.sh b/tests/sys/cddl/zfs/tests/refreserv/refreserv_test.sh
new file mode 100755
index 000000000000..449662199ee5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/refreserv/refreserv_test.sh
@@ -0,0 +1,155 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case refreserv_001_pos cleanup
+refreserv_001_pos_head()
+{
+ atf_set "descr" "Reservations are enforced using the maximum of'reserv' and 'refreserv'"
+ atf_set "require.progs" "ksh93 zfs"
+}
+refreserv_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/refreserv.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/refreserv_001_pos.ksh || atf_fail "Testcase failed"
+}
+refreserv_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/refreserv.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case refreserv_002_pos cleanup
+refreserv_002_pos_head()
+{
+ atf_set "descr" "Setting full size as refreservation, verify no snapshotcan be created."
+ atf_set "require.progs" "ksh93 zfs"
+}
+refreserv_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/refreserv.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/refreserv_002_pos.ksh || atf_fail "Testcase failed"
+}
+refreserv_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/refreserv.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case refreserv_003_pos cleanup
+refreserv_003_pos_head()
+{
+ atf_set "descr" "Verify a snapshot will only be allowed if there is enoughfree space outside of this refreservation."
+ atf_set "require.progs" "ksh93 zfs"
+}
+refreserv_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/refreserv.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/refreserv_003_pos.ksh || atf_fail "Testcase failed"
+}
+refreserv_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/refreserv.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case refreserv_004_pos cleanup
+refreserv_004_pos_head()
+{
+ atf_set "descr" "Verify refreservation is limited by available space."
+ atf_set "require.progs" "ksh93 zfs"
+}
+refreserv_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/refreserv.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/refreserv_004_pos.ksh || atf_fail "Testcase failed"
+}
+refreserv_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/refreserv.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case refreserv_005_pos cleanup
+refreserv_005_pos_head()
+{
+ atf_set "descr" "Volume refreservation is limited by volsize"
+ atf_set "require.progs" "ksh93 zfs"
+}
+refreserv_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/refreserv.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/refreserv_005_pos.ksh || atf_fail "Testcase failed"
+}
+refreserv_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/refreserv.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case refreserv_001_pos
+ atf_add_test_case refreserv_002_pos
+ atf_add_test_case refreserv_003_pos
+ atf_add_test_case refreserv_004_pos
+ atf_add_test_case refreserv_005_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/refreserv/setup.ksh b/tests/sys/cddl/zfs/tests/refreserv/setup.ksh
new file mode 100644
index 000000000000..f45ef4b34ff3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/refreserv/setup.ksh
@@ -0,0 +1,35 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "both"
+if ! fs_prop_exist "refreservation" ; then
+ log_unsupported "refreservation is not supported in this system."
+fi
+
+DISK=${DISKS%% *}
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/rename_dirs/Makefile b/tests/sys/cddl/zfs/tests/rename_dirs/Makefile
new file mode 100644
index 000000000000..9aafbf759374
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rename_dirs/Makefile
@@ -0,0 +1,16 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/rename_dirs
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= rename_dirs_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= rename_dirs_001_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= rename_dirs.cfg
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/rename_dirs/cleanup.ksh b/tests/sys/cddl/zfs/tests/rename_dirs/cleanup.ksh
new file mode 100644
index 000000000000..713cb16bb31c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rename_dirs/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/rename_dirs/rename_dirs.cfg b/tests/sys/cddl/zfs/tests/rename_dirs/rename_dirs.cfg
new file mode 100644
index 000000000000..664c355b6f67
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rename_dirs/rename_dirs.cfg
@@ -0,0 +1,28 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export WAITTIME=600
+export STF_TIMEOUT=1200
diff --git a/tests/sys/cddl/zfs/tests/rename_dirs/rename_dirs_001_pos.ksh b/tests/sys/cddl/zfs/tests/rename_dirs/rename_dirs_001_pos.ksh
new file mode 100644
index 000000000000..9a134a0b82cd
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rename_dirs/rename_dirs_001_pos.ksh
@@ -0,0 +1,80 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+###########################################################################
+#
+# __stc_assertion_start
+#
+# ID: rename_dirs_001_pos
+#
+# DESCRIPTION:
+# Create two directory trees in ZFS filesystem, and concurently rename
+# directory across the two trees. ZFS should be able to handle the race
+# situation.
+#
+# STRATEGY:
+# 1. Create a ZFS filesystem
+# 2. Make two directory tree in the zfs file system
+# 3. Continually rename directory from one tree to another tree in two process
+# 4. After the specified time duration, the system should not be panic.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-02-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ log_must $RM -rf $TESTDIR/*
+}
+
+log_assert "ZFS can handle race directory rename operation."
+
+log_onexit cleanup
+
+$CD $TESTDIR
+$MKDIR -p 1/2/3/4/5 a/b/c/d/e
+
+$RENAME_DIR &
+
+$SLEEP $WAITTIME
+typeset -i retval=1
+$PGREP $RENAME_DIR >/dev/null 2>&1
+retval=$?
+if (( $retval == 0 )); then
+ $PKILL -9 $RENAME_DIR >/dev/null 2>&1
+fi
+
+log_pass "ZFS handle race directory rename operation as expected."
diff --git a/tests/sys/cddl/zfs/tests/rename_dirs/rename_dirs_test.sh b/tests/sys/cddl/zfs/tests/rename_dirs/rename_dirs_test.sh
new file mode 100755
index 000000000000..9e1f4a6cf147
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rename_dirs/rename_dirs_test.sh
@@ -0,0 +1,54 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case rename_dirs_001_pos cleanup
+rename_dirs_001_pos_head()
+{
+ atf_set "descr" "ZFS can handle race directory rename operation."
+ atf_set "timeout" 1200
+}
+rename_dirs_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/rename_dirs.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/rename_dirs_001_pos.ksh || atf_fail "Testcase failed"
+}
+rename_dirs_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/rename_dirs.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case rename_dirs_001_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/rename_dirs/setup.ksh b/tests/sys/cddl/zfs/tests/rename_dirs/setup.ksh
new file mode 100644
index 000000000000..2ab3c8a94139
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rename_dirs/setup.ksh
@@ -0,0 +1,30 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/replacement/Makefile b/tests/sys/cddl/zfs/tests/replacement/Makefile
new file mode 100644
index 000000000000..f818166cdaa8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/replacement/Makefile
@@ -0,0 +1,17 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/replacement
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= replacement_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= replacement_002_pos.ksh
+${PACKAGE}FILES+= replacement_003_pos.ksh
+${PACKAGE}FILES+= replacement_001_pos.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= replacement.kshlib
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/replacement/cleanup.ksh b/tests/sys/cddl/zfs/tests/replacement/cleanup.ksh
new file mode 100644
index 000000000000..cdcaef02dc8b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/replacement/cleanup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+verify_runnable "global"
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/replacement/replacement.kshlib b/tests/sys/cddl/zfs/tests/replacement/replacement.kshlib
new file mode 100644
index 000000000000..8425525c0b31
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/replacement/replacement.kshlib
@@ -0,0 +1,49 @@
+# vim: filetype=sh
+# Common routines for replacement tests.
+
+function check_vdev_action # <action> <expect> <opt> <disk1> [disk2]
+{
+ typeset action=$1
+ typeset expect=$2
+ typeset opt=$3
+ typeset disk1="$4"
+ typeset disk2="$5"
+
+ busy_path $TESTDIR
+ $expect $ZPOOL $action $opt $TESTPOOL $disk1 $disk2
+ reap_children
+ log_must $ZPOOL export $TESTPOOL
+ log_must $ZPOOL import $TESTPOOL
+ log_must $ZFS umount $TESTPOOL/$TESTFS
+ log_must $ZDB -cdui $TESTPOOL/$TESTFS
+ log_must $ZFS mount $TESTPOOL/$TESTFS
+}
+
+function pool_action # <tvd_type> <action> <actionexpect> <existexpect>
+{
+ typeset tvd_type=$1
+ typeset action=$2
+ typeset actionexpect=$3
+ typeset existexpect=$4
+
+ typeset disk=${disk_array[3]}
+ typeset short=${disk##/dev/}
+
+ for opt in "" "-f"; do
+ [ "$action" = "detach" -a "$opt" = "-f" ] && continue
+ create_pool $TESTPOOL $tvd_type ${disk_array[@]:0:3}
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+ check_vdev_action $action $actionexpect "$opt" \
+ ${disk_array[0]} $disk
+
+ $existexpect eval "$ZPOOL iostat -v $TESTPOOL | $GREP -q $short"
+ destroy_pool $TESTPOOL
+ done
+}
+
+function replacement_cleanup
+{
+ poolexists $TESTPOOL && log_must $ZPOOL status $TESTPOOL
+ reap_children
+}
diff --git a/tests/sys/cddl/zfs/tests/replacement/replacement_001_pos.ksh b/tests/sys/cddl/zfs/tests/replacement/replacement_001_pos.ksh
new file mode 100644
index 000000000000..d3d716c8bb0e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/replacement/replacement_001_pos.ksh
@@ -0,0 +1,42 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2014 Spectra Logic Corporation.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/replacement/replacement.kshlib
+
+verify_runnable "global"
+
+child_pids=""
+log_onexit replacement_cleanup
+set_disks
+
+log_assert "Replacing a disk during I/O completes."
+for pooltype in "" "raidz" "raidz1" "mirror"; do
+ pool_action "$pooltype" replace log_must log_must
+done
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/replacement/replacement_002_pos.ksh b/tests/sys/cddl/zfs/tests/replacement/replacement_002_pos.ksh
new file mode 100644
index 000000000000..bc9623a7fb77
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/replacement/replacement_002_pos.ksh
@@ -0,0 +1,48 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2014 Spectra Logic Corporation.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/replacement/replacement.kshlib
+
+verify_runnable "global"
+
+child_pids=""
+log_onexit replacement_cleanup
+set_disks
+
+log_assert "Attaching a disk during I/O completes for mirrors and stripes."
+for pooltype in "" "mirror"; do
+ pool_action "$pooltype" attach log_must log_must
+done
+
+log_note "Verify 'zpool attach' fails for RAIDZ."
+for pooltype in "raidz1" "raidz2"; do
+ pool_action "$pooltype" attach log_mustnot log_mustnot
+done
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/replacement/replacement_003_pos.ksh b/tests/sys/cddl/zfs/tests/replacement/replacement_003_pos.ksh
new file mode 100644
index 000000000000..ce941be81b93
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/replacement/replacement_003_pos.ksh
@@ -0,0 +1,46 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2014 Spectra Logic Corporation.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/replacement/replacement.kshlib
+
+verify_runnable "global"
+
+child_pids=""
+log_onexit replacement_cleanup
+set_disks
+
+log_assert "Detaching a disk during I/O completes for mirrors."
+pool_action mirror detach log_must log_mustnot
+
+log_note "Verify 'zpool detach' fails with non-mirrors."
+for pooltype in "" "raidz" "raidz1" ; do
+ pool_action "$pooltype" detach log_mustnot log_mustnot
+done
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/replacement/replacement_test.sh b/tests/sys/cddl/zfs/tests/replacement/replacement_test.sh
new file mode 100755
index 000000000000..bc40f0833a2f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/replacement/replacement_test.sh
@@ -0,0 +1,98 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case replacement_001_pos cleanup
+replacement_001_pos_head()
+{
+ atf_set "descr" "Replacing a disk during I/O completes."
+ atf_set "require.progs" "ksh93 zfs zpool zdb"
+ atf_set "timeout" 3600
+}
+replacement_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+
+ verify_disk_count "$DISKS" 3
+ ksh93 $(atf_get_srcdir)/replacement_001_pos.ksh || atf_fail "Testcase failed"
+}
+replacement_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case replacement_002_pos cleanup
+replacement_002_pos_head()
+{
+ atf_set "descr" "Replacing a disk during I/O completes."
+ atf_set "require.progs" "ksh93 zfs zpool zdb"
+ atf_set "timeout" 3600
+}
+replacement_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+
+ verify_disk_count "$DISKS" 3
+ ksh93 $(atf_get_srcdir)/replacement_002_pos.ksh || atf_fail "Testcase failed"
+}
+replacement_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case replacement_003_pos cleanup
+replacement_003_pos_head()
+{
+ atf_set "descr" "Replacing a disk during I/O completes."
+ atf_set "require.progs" "ksh93 zfs zpool zdb"
+ atf_set "timeout" 3600
+}
+replacement_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/replacement_003_pos.ksh || atf_fail "Testcase failed"
+}
+replacement_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+ atf_add_test_case replacement_001_pos
+ atf_add_test_case replacement_002_pos
+ atf_add_test_case replacement_003_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/reservation/Makefile b/tests/sys/cddl/zfs/tests/reservation/Makefile
new file mode 100644
index 000000000000..cda06d9c96c8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/reservation/Makefile
@@ -0,0 +1,34 @@
+.include <src.opts.mk>
+
+PACKAGE= tests
+TESTSDIR= ${TESTSBASE}/sys/cddl/zfs/tests/reservation
+FILESDIR= ${TESTSDIR}
+
+ATF_TESTS_KSH93+= reservation_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= reservation.cfg
+${PACKAGE}FILES+= reservation_007_pos.ksh
+${PACKAGE}FILES+= reservation_003_pos.ksh
+${PACKAGE}FILES+= reservation_012_pos.ksh
+${PACKAGE}FILES+= reservation_016_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= reservation_013_pos.ksh
+${PACKAGE}FILES+= reservation_017_pos.ksh
+${PACKAGE}FILES+= reservation_006_pos.ksh
+${PACKAGE}FILES+= reservation_002_pos.ksh
+${PACKAGE}FILES+= reservation_011_pos.ksh
+${PACKAGE}FILES+= reservation_015_pos.ksh
+${PACKAGE}FILES+= reservation_004_pos.ksh
+${PACKAGE}FILES+= reservation_008_pos.ksh
+${PACKAGE}FILES+= reservation_009_pos.ksh
+${PACKAGE}FILES+= reservation_005_pos.ksh
+${PACKAGE}FILES+= reservation_001_pos.ksh
+${PACKAGE}FILES+= reservation.kshlib
+${PACKAGE}FILES+= reservation_010_pos.ksh
+${PACKAGE}FILES+= reservation_014_pos.ksh
+${PACKAGE}FILES+= reservation_018_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/reservation/cleanup.ksh b/tests/sys/cddl/zfs/tests/reservation/cleanup.ksh
new file mode 100644
index 000000000000..b2b441e574d0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/reservation/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/reservation/reservation.cfg b/tests/sys/cddl/zfs/tests/reservation/reservation.cfg
new file mode 100644
index 000000000000..4a8a047bed8c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/reservation/reservation.cfg
@@ -0,0 +1,42 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export RESV_DELTA=5242880
+export RESV_TOLERANCE=5242880 # Acceptable limit (5MB) for diff in space stats
+export RESV_SIZE=52428800 # Default reservation size (50MB)
+export RESV_FREE_SPACE=52428800 # Amount of space (50MB) to leave free in a pool
+export RESV_NUM_FS=10 # Number of filesystems to create
+export RESV_ITER=10 # Number of iterations
+
+export VOLSIZE=64M
+export BLOCK_SIZE=8192
+export ENOSPC=28
+
+export ZFSROOT=
+export TESTVOL=testvol${TESTCASE_ID}
+export TESTVOL2=testvol2-${TESTCASE_ID}
+export TESTFILE1=file1.${TESTCASE_ID}
+export TESTFILE2=file2.${TESTCASE_ID}
diff --git a/tests/sys/cddl/zfs/tests/reservation/reservation.kshlib b/tests/sys/cddl/zfs/tests/reservation/reservation.kshlib
new file mode 100644
index 000000000000..916ae4b464dd
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/reservation/reservation.kshlib
@@ -0,0 +1,175 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+#
+# Function to set the reservation property of a dataset to
+# 'none' and verify that it is correctly set using both the
+# "normal" 'zfs get reservation' and the '-p' option which
+# gives a numerical value.
+#
+function zero_reservation
+{
+ typeset resv_val
+ dataset=$1
+
+ log_must $ZFS set reservation=none $dataset
+ log_must $ZFS set refreservation=none $dataset
+
+ resv_val=`$ZFS get -H refreservation $dataset | awk '{print $3}'`
+ if [[ $? -ne 0 ]]; then
+ log_fail "Unable to get reservation prop on $dataset"
+ elif [[ $resv_val != "none" ]]; then
+ log_fail "Reservation not 'none' ($resv_val) as expected"
+ fi
+
+
+ resv_val=`$ZFS get -pH refreservation $dataset | awk '{print $3}'`
+ if [[ $? -ne 0 ]]; then
+ log_fail "Unable to get reservation prop on $dataset"
+ elif [[ $resv_val -ne 0 ]]; then
+ log_fail "Reservation not 0 ($resv_val) as expected"
+ fi
+
+ return 0
+}
+
+#
+# Utility function to see if two values are within a certain specified
+# limit of each other. Used primarily to check that a dataset's parent
+# is correctly accounting for space used/available. Need this function as
+# currently there is some slop in the way space is accounted (i.e. can't
+# do a direct comparison).
+#
+function within_limits
+{
+ typeset -l valA=$1
+ typeset -l valB=$2
+ typeset -l delta=$3
+
+ if (( valA <= valB )); then
+ if (( (valB - valA) <= delta )); then
+ return 0
+ fi
+ elif (( valB <= valA )); then
+ if (( (valA - valB) <= delta )); then
+ return 0
+ fi
+ fi
+
+ return 1
+}
+
+#
+# Function to create and mount multiple filesystems. The filesystem
+# will be named according to the name specified with a suffix value
+# taken from the loop counter.
+#
+function create_multiple_fs # num_fs base_fs_name base_mnt_name
+{
+ typeset -i iter=0
+ typeset -i count=$1
+ typeset FS_NAME=$2
+ typeset MNT_NAME=$3
+
+ while (( $iter < $count )); do
+ log_must $ZFS create ${FS_NAME}$iter
+ log_must $ZFS set mountpoint=${MNT_NAME}$iter ${FS_NAME}$iter
+ (( iter = iter + 1 ))
+ done
+}
+
+#
+# This function compute the largest volume size which is multiple of volume
+# block size (default 8K) and not greater than the largest expected volsize.
+#
+# $1 The largest expected volume size.
+# $2 The volume block size
+#
+function floor_volsize #<largest_volsize> [volblksize]
+{
+ typeset -l largest_volsize=$1
+ typeset -l volblksize=${2:-8192}
+
+ if (( largest_volsize < volblksize )); then
+ log_fail "The largest_volsize must be greater than volblksize."
+ fi
+ typeset -l real_volsize
+ typeset -l n
+
+ (( n = largest_volsize / volblksize ))
+ (( largest_volsize = volblksize * n ))
+
+ print $largest_volsize
+}
+
+#
+# Simple function to get the expected reservation for a ZVOL given the
+# volume size, block size, and number of copies.
+#
+# NB: This routine must be kept in sync with the ZFS library function
+# libzfs_dataset.c:zvol_volsize_to_reservation(). Refer to that function
+# for the logic behind the calculations.
+#
+function zvol_volsize_to_reservation
+{
+ typeset resv_val
+ typeset nblocks
+ typeset numdb
+ typeset volsize=$1
+ typeset volblocksize=$2
+ typeset ncopies=$3
+ typeset ncopies_bp
+ typeset DN_MAX_INDBLKSHIFT=17
+ typeset SPA_BLKPTRSHIFT=7
+ typeset SPA_DVAS_PER_BP=3
+ typeset DVAS_PER_BP
+ typeset DNODES_PER_LEVEL_SHIFT
+ typeset DNODES_PER_LEVEL
+ typeset DN_MAX_INDBLKS
+
+ (( DNODES_PER_LEVEL_SHIFT = DN_MAX_INDBLKSHIFT - SPA_BLKPTRSHIFT ))
+ (( DNODES_PER_LEVEL = 1 << DNODES_PER_LEVEL_SHIFT ))
+ (( DN_MAX_INDBLKS = 1 << DN_MAX_INDBLKSHIFT ))
+
+ resv_val=$volsize
+ (( nblocks = volsize / volblocksize ))
+ numdb=7
+ while (( nblocks > 1 )); do
+ (( nblocks = nblocks + DNODES_PER_LEVEL - 1 ))
+ (( nblocks = nblocks / DNODES_PER_LEVEL ))
+ (( numdb = numdb + nblocks ))
+ done
+ (( ncopies_bp = ncopies + 1 ))
+ DVAS_PER_BP=$(min $SPA_DVAS_PER_BP $ncopies_bp)
+ (( numdb = numdb * DVAS_PER_BP ))
+ (( resv_val = volsize * ncopies ))
+ (( numdb = numdb * DN_MAX_INDBLKS ))
+ (( resv_val = resv_val + numdb ))
+
+ $ECHO $resv_val
+ return 0
+}
+
diff --git a/tests/sys/cddl/zfs/tests/reservation/reservation_001_pos.ksh b/tests/sys/cddl/zfs/tests/reservation/reservation_001_pos.ksh
new file mode 100644
index 000000000000..c02e156e67df
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/reservation/reservation_001_pos.ksh
@@ -0,0 +1,127 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/reservation/reservation.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: reservation_001_pos
+#
+# DESCRIPTION:
+#
+# ZFS allows reservations to be set on filesystems and volumes, provided
+# the reservation is less than the space available in the pool.
+#
+# STRATEGY:
+# 1) Create a regular and sparse volume
+# (filesystem already created by default_setup)
+# 2) Get the space available in the pool
+# 3) Set a reservation on the filesystem less than the space available.
+# 4) Verify that the 'reservation' property for the filesystem has
+# the correct value.
+# 5) Reset the reservation to 'none'
+# 6) Repeat steps 2-5 for both volume types
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-19)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "both"
+
+log_assert "Verify that to set a reservation on a filesystem" \
+ " or volume must use value smaller than space" \
+ " available property of pool"
+
+space_avail=`get_prop available $TESTPOOL`
+
+if ! is_global_zone ; then
+ OBJ_LIST=""
+else
+ OBJ_LIST="$TESTPOOL/$TESTVOL $TESTPOOL/$TESTVOL2"
+
+ (( vol_set_size = space_avail / 4 ))
+ vol_set_size=$(floor_volsize $vol_set_size)
+ (( sparse_vol_set_size = space_avail * 4 ))
+ sparse_vol_set_size=$(floor_volsize $sparse_vol_set_size)
+
+ #
+ # Note that when creating a regular volume we are implicitly
+ # setting a reservation upon it (i.e. the size of the volume)
+ # which we reset back to zero initially.
+ #
+ log_must $ZFS create -V $vol_set_size $TESTPOOL/$TESTVOL
+ log_must $ZFS set reservation=none $TESTPOOL/$TESTVOL
+ if fs_prop_exist refreserv; then
+ log_must $ZFS set refreservation=none $TESTPOOL/$TESTVOL
+ fi
+ log_must $ZFS create -s -V $sparse_vol_set_size $TESTPOOL/$TESTVOL2
+fi
+
+
+for obj in $TESTPOOL/$TESTFS $OBJ_LIST; do
+
+ space_avail=`get_prop available $TESTPOOL`
+ resv_size_set=`expr $space_avail - $RESV_DELTA`
+
+ #
+ # For a regular (non-sparse) volume the upper limit
+ # for reservations is not determined by the space
+ # available in the pool but rather by the size of
+ # the volume itself.
+ #
+ [[ $obj == $TESTPOOL/$TESTVOL ]] && \
+ (( resv_size_set = vol_set_size - RESV_DELTA ))
+
+ log_must $ZFS set reservation=$resv_size_set $obj
+
+ resv_size_get=`get_prop reservation $obj`
+ if [[ $resv_size_set != $resv_size_get ]]; then
+ log_fail "Reservation not the expected value "\
+ "($resv_size_set != $resv_size_get)"
+ fi
+
+ log_must zero_reservation $obj
+
+ new_space_avail=`get_prop available $obj`
+
+ #
+ # Due to the way space is consumed and released by metadata we
+ # can't do an exact check here, but we do do a basic sanity
+ # check.
+ #
+ log_must within_limits $space_avail $new_space_avail $RESV_TOLERANCE
+done
+
+log_pass "Successfully set reservation on filesystem and volume"
diff --git a/tests/sys/cddl/zfs/tests/reservation/reservation_002_pos.ksh b/tests/sys/cddl/zfs/tests/reservation/reservation_002_pos.ksh
new file mode 100644
index 000000000000..9c9d22843562
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/reservation/reservation_002_pos.ksh
@@ -0,0 +1,106 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/reservation/reservation.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: reservation_002_pos
+#
+# DESCRIPTION:
+#
+# Reservation values cannot exceed the amount of space available
+# in the pool. Verify that attempting to set a reservation greater
+# than this value fails.
+#
+# STRATEGY:
+# 1) Create a filesystem, regular and sparse volume
+# 2) Get the space available in the pool
+# 3) Attempt to set a reservation greater than the available space
+# on the filesystem and verify it fails.
+# 4) Verify that the reservation is still set to 'none' (or 0) on
+# the filesystem.
+# 5) Repeat 3-4 for regular and sparse volume
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-19)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Reservation values cannot exceed the amount of space" \
+ " available in the pool"
+
+space_avail=`get_prop available $TESTPOOL`
+
+if ! is_global_zone ; then
+ OBJ_LIST=""
+else
+ OBJ_LIST="$TESTPOOL/$TESTVOL $TESTPOOL/$TESTVOL2"
+
+ (( vol_set_size = space_avail / 4 ))
+ vol_set_size=$(floor_volsize $vol_set_size)
+ (( sparse_vol_set_size = space_avail * 4 ))
+ sparse_vol_set_size=$(floor_volsize $sparse_vol_set_size)
+
+ log_must $ZFS create -V $vol_set_size $TESTPOOL/$TESTVOL
+ log_must $ZFS set reservation=none $TESTPOOL/$TESTVOL
+ log_must $ZFS create -s -V $sparse_vol_set_size $TESTPOOL/$TESTVOL2
+fi
+
+for obj in $TESTPOOL/$TESTFS $OBJ_LIST ; do
+
+ space_avail=`get_prop available $TESTPOOL`
+ resv_size_set=`expr $space_avail + $RESV_DELTA`
+
+ #
+ # For regular (non-sparse) volumes the upper limit is determined
+ # not by the space available in the pool but rather by the size
+ # of the volume itself.
+ #
+ [[ $obj == $TESTPOOL/$TESTVOL ]] && \
+ (( resv_size_set = vol_set_size + RESV_DELTA ))
+
+ log_must zero_reservation $obj
+ log_mustnot $ZFS set reservation=$resv_size_set $obj
+
+ resv_size_get=`get_prop reservation $obj`
+
+ if (( $resv_size_get != 0 )); then
+ log_fail "Reservation value non-zero ($resv_size_get)"
+ fi
+done
+
+log_pass "Attempting to set too large reservation failed as expected"
diff --git a/tests/sys/cddl/zfs/tests/reservation/reservation_003_pos.ksh b/tests/sys/cddl/zfs/tests/reservation/reservation_003_pos.ksh
new file mode 100644
index 000000000000..27183931cd0d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/reservation/reservation_003_pos.ksh
@@ -0,0 +1,132 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/reservation/reservation.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: reservation_003_pos
+#
+# DESCRIPTION:
+#
+# Verify that it's possible to set a reservation on a filesystem,
+# or volume multiple times, without resetting the reservation
+# to none.
+#
+# STRATEGY:
+# 1) Create a regular volume and a sparse volume
+# 2) Get the space available in the pool
+# 3) Set a reservation on the filesystem less than the space available.
+# 4) Verify that the 'reservation' property for the filesystem has
+# the correct value.
+# 5) Repeat 2-4 for different reservation values
+# 6) Repeat 3-5 for regular and sparse volume
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-19)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify it is possible to set reservations multiple times " \
+ "on a filesystem regular and sparse volume"
+
+
+#
+# Set a reservation $RESV_ITER times on a dataset and verify that
+# the reservation is correctly set each time.
+#
+function multiple_resv { #dataset
+ typeset -i i=0
+
+ dataset=$1
+
+ log_must zero_reservation $dataset
+ space_avail=`get_prop available $TESTPOOL`
+
+ (( resv_size = ( space_avail - RESV_DELTA ) / RESV_ITER ))
+
+ #
+ # For regular (non-sparse) volumes the upper limit is determined
+ # not by the space available in the pool but rather by the size
+ # of the volume itself.
+ #
+ [[ $obj == $TESTPOOL/$TESTVOL ]] && \
+ (( resv_size = ( vol_set_size - RESV_DELTA ) / RESV_ITER ))
+
+ resv_size_set=$resv_size
+
+ while (( $i < $RESV_ITER )); do
+
+ (( i = i + 1 ))
+
+ (( resv_size_set = resv_size * i ))
+
+ log_must $ZFS set reservation=$resv_size_set $dataset
+
+ resv_size_get=`get_prop reservation $dataset`
+ if [[ $resv_size_set != $resv_size_get ]]; then
+ log_fail "Reservation not the expected value " \
+ "($resv_size_set != $resv_size_get)"
+ fi
+ done
+
+ log_must zero_reservation $dataset
+}
+
+space_avail=`get_prop available $TESTPOOL`
+
+if ! is_global_zone ; then
+ OBJ_LIST=""
+else
+ OBJ_LIST="$TESTPOOL/$TESTVOL $TESTPOOL/$TESTVOL2"
+
+ (( vol_set_size = space_avail / 4 ))
+ vol_set_size=$(floor_volsize $vol_set_size)
+ (( sparse_vol_set_size = space_avail * 4 ))
+ sparse_vol_set_size=$(floor_volsize $sparse_vol_set_size)
+
+
+ log_must $ZFS create -V $vol_set_size $TESTPOOL/$TESTVOL
+ log_must $ZFS set reservation=none $TESTPOOL/$TESTVOL
+ log_must $ZFS create -s -V $sparse_vol_set_size $TESTPOOL/$TESTVOL2
+fi
+
+for obj in $TESTPOOL/$TESTFS $OBJ_LIST ; do
+ multiple_resv $obj
+done
+
+log_pass "Multiple reservations successfully set on filesystem" \
+ " and both volume types"
diff --git a/tests/sys/cddl/zfs/tests/reservation/reservation_004_pos.ksh b/tests/sys/cddl/zfs/tests/reservation/reservation_004_pos.ksh
new file mode 100644
index 000000000000..8d1571620f68
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/reservation/reservation_004_pos.ksh
@@ -0,0 +1,126 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/reservation/reservation.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: reservation_004_pos
+#
+# DESCRIPTION:
+#
+# When a dataset which has a reservation set on it is destroyed,
+# the space consumed or reserved by that dataset should be released
+# back into the pool.
+#
+# STRATEGY:
+# 1) Create a filesystem, regular and sparse volume
+# 2) Get the space used and available in the pool
+# 3) Set a reservation on the filesystem less than the space available.
+# 4) Verify that the 'reservation' property for the filesystem has
+# the correct value.
+# 5) Destroy the filesystem without resetting the reservation value.
+# 6) Verify that the space used and available totals for the pool have
+# changed by the expected amounts (within tolerances).
+# 7) Repeat steps 3-6 for a regular volume and sparse volume
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-19)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify space released when a dataset with reservation is destroyed"
+
+log_must $ZFS create $TESTPOOL/$TESTFS2
+
+space_avail=`get_prop available $TESTPOOL`
+
+if ! is_global_zone ; then
+ OBJ_LIST="$TESTPOOL/$TESTFS2"
+else
+ OBJ_LIST="$TESTPOOL/$TESTFS2 \
+ $TESTPOOL/$TESTVOL $TESTPOOL/$TESTVOL2"
+
+ (( vol_set_size = space_avail / 4 ))
+ vol_set_size=$(floor_volsize $vol_set_size)
+ (( sparse_vol_set_size = space_avail * 4 ))
+ sparse_vol_set_size=$(floor_volsize $sparse_vol_set_size)
+
+ log_must $ZFS create -V $vol_set_size $TESTPOOL/$TESTVOL
+ if fs_prop_exist refreserv; then
+ log_must $ZFS set refreservation=none $TESTPOOL/$TESTVOL
+ fi
+ log_must $ZFS set reservation=none $TESTPOOL/$TESTVOL
+ log_must $ZFS create -s -V $sparse_vol_set_size $TESTPOOL/$TESTVOL2
+fi
+
+# re-calculate space available.
+space_avail=`get_prop available $TESTPOOL`
+
+# Calculate a large but valid reservation value.
+resv_size_set=`expr $space_avail - $RESV_DELTA`
+
+for obj in $OBJ_LIST ; do
+
+ space_avail=`get_prop available $TESTPOOL`
+ space_used=`get_prop used $TESTPOOL`
+
+ #
+ # For regular (non-sparse) volumes the upper limit is determined
+ # not by the space available in the pool but rather by the size
+ # of the volume itself.
+ #
+ [[ $obj == $TESTPOOL/$TESTVOL ]] && \
+ (( resv_size_set = vol_set_size - RESV_DELTA ))
+
+ log_must $ZFS set reservation=$resv_size_set $obj
+
+ resv_size_get=`get_prop reservation $obj`
+ if [[ $resv_size_set != $resv_size_get ]]; then
+ log_fail "Reservation not the expected value " \
+ "($resv_size_set != $resv_size_get)"
+ fi
+
+ log_must $ZFS destroy -f $obj
+
+ new_space_avail=`get_prop available $TESTPOOL`
+ new_space_used=`get_prop used $TESTPOOL`
+
+ log_must within_limits $space_used $new_space_used $RESV_TOLERANCE
+ log_must within_limits $space_avail $new_space_avail $RESV_TOLERANCE
+done
+
+log_pass "Space correctly released when dataset is destroyed"
diff --git a/tests/sys/cddl/zfs/tests/reservation/reservation_005_pos.ksh b/tests/sys/cddl/zfs/tests/reservation/reservation_005_pos.ksh
new file mode 100644
index 000000000000..1c2746e3c65e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/reservation/reservation_005_pos.ksh
@@ -0,0 +1,118 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/reservation/reservation.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: reservation_005_pos
+#
+# DESCRIPTION:
+#
+# When a reservation property of a filesystem, regular volume
+# or sparse volume is set to 'none' the space previously consumed by the
+# reservation should be released back to the pool
+#
+# STRATEGY:
+# 1) Create a filesystem, regular volume and sparse volume
+# 2) Get the space used and available in the pool
+# 3) Set a reservation on the filesystem less than the space available.
+# 4) Verify that the 'reservation' property for the filesystem has
+# the correct value.
+# 5) Reset the reservation value back to zero (or 'none')
+# 6) Verify that the space used and available totals for the pool have
+# changed by the expected amounts (within tolerances).
+# 7) Repeat steps 3-6 for a regular volume, sparse volume
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify space released when reservation on a dataset is set "\
+ "to 'none'"
+
+space_avail=`get_prop available $TESTPOOL`
+
+if ! is_global_zone ; then
+ OBJ_LIST=""
+else
+ OBJ_LIST="$TESTPOOL/$TESTVOL $TESTPOOL/$TESTVOL2"
+ (( vol_set_size = space_avail / 4 ))
+ vol_set_size=$(floor_volsize $vol_set_size)
+ (( sparse_vol_set_size = space_avail * 4 ))
+ sparse_vol_set_size=$(floor_volsize $sparse_vol_set_size)
+
+
+ log_must $ZFS create -V $vol_set_size $TESTPOOL/$TESTVOL
+ log_must $ZFS set reservation=none $TESTPOOL/$TESTVOL
+ log_must $ZFS create -s -V $sparse_vol_set_size $TESTPOOL/$TESTVOL2
+fi
+
+space_avail=`get_prop available $TESTPOOL`
+space_used=`get_prop used $TESTPOOL`
+
+# Calculate a large but valid reservation value.
+resv_size_set=`expr $space_avail - $RESV_DELTA`
+
+for obj in $TESTPOOL/$TESTFS $OBJ_LIST ; do
+
+ #
+ # For regular (non-sparse) volumes the upper limit is determined
+ # not by the space available in the pool but rather by the size
+ # of the volume itself.
+ #
+ [[ $obj == $TESTPOOL/$TESTVOL ]] && \
+ (( resv_size_set = vol_set_size - RESV_DELTA ))
+
+ log_must $ZFS set reservation=$resv_size_set $obj
+
+ resv_size_get=`get_prop reservation $obj`
+ if [[ $resv_size_set != $resv_size_get ]]; then
+ log_fail "Reservation not the expected value "\
+ "($resv_size_set != $resv_size_get)"
+ fi
+
+ log_must $ZFS set reservation=none $obj
+
+ new_space_avail=`get_prop available $TESTPOOL`
+ new_space_used=`get_prop used $TESTPOOL`
+
+ log_must within_limits $space_used $new_space_used $RESV_TOLERANCE
+ log_must within_limits $space_avail $new_space_avail $RESV_TOLERANCE
+done
+
+log_pass "Space correctly released when dataset reservation set to 'none'"
diff --git a/tests/sys/cddl/zfs/tests/reservation/reservation_006_pos.ksh b/tests/sys/cddl/zfs/tests/reservation/reservation_006_pos.ksh
new file mode 100644
index 000000000000..6978d7a4b21b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/reservation/reservation_006_pos.ksh
@@ -0,0 +1,82 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/reservation/reservation.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: reservation_006_pos
+#
+# DESCRIPTION:
+#
+# Reservations (if successfully set) guarantee a minimum amount of space
+# for a dataset. Unlike quotas however there should be no restrictions
+# on accessing space outside of the limits of the reservation (if the
+# space is available in the pool). Verify that in a filesystem with a
+# reservation set that its possible to create files both within the
+# reserved space and also outside.
+#
+# STRATEGY:
+# 1) Create a filesystem
+# 2) Get the space used and available in the pool
+# 3) Set a reservation on the filesystem
+# 4) Verify can write a file that is bigger than the reserved space
+#
+# i.e. we start writing within the reserved region and then continue
+# for 20MB outside it.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify can create files both inside and outside reserved areas"
+
+space_used=`get_prop used $TESTPOOL`
+
+log_must $ZFS set reservation=$RESV_SIZE $TESTPOOL/$TESTFS
+
+#
+# Calculate how many writes of BLOCK_SIZE it would take to fill
+# up RESV_SIZE + 20971520 (20 MB).
+#
+fill_size=`expr $RESV_SIZE + 20971520`
+write_count=`expr $fill_size / $BLOCK_SIZE`
+
+log_must $FILE_WRITE -o create -f $TESTDIR/$TESTFILE1 -b $BLOCK_SIZE \
+ -c $write_count -d 0
+
+log_pass "Able to create files inside and outside reserved area"
diff --git a/tests/sys/cddl/zfs/tests/reservation/reservation_007_pos.ksh b/tests/sys/cddl/zfs/tests/reservation/reservation_007_pos.ksh
new file mode 100644
index 000000000000..6d4636140f97
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/reservation/reservation_007_pos.ksh
@@ -0,0 +1,126 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/reservation/reservation.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: reservation_007_pos
+#
+# DESCRIPTION:
+#
+# Setting a reservation on dataset should have no effect on any other
+# dataset at the same level in the hierarchy beyond using up available
+# space in the pool.
+#
+# STRATEGY:
+# 1) Create a filesystem
+# 2) Set a reservation on the filesystem
+# 3) Create another filesystem at the same level
+# 4) Set a reservation on the second filesystem
+# 5) Destroy both the filesystems
+# 6) Verify space accounted for correctly
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify reservations on data sets doesn't affect other data sets at" \
+ " same level except for consuming space from common pool"
+
+space_avail=`get_prop available $TESTPOOL`
+space_used=`get_prop used $TESTPOOL`
+
+resv_size_set=`expr $space_avail / 3`
+
+#
+# Function which creates two datasets, sets reservations on them,
+# then destroys them and ensures that space is correctly accounted
+# for.
+#
+# Any special arguments for create are passed in via the args
+# parameter.
+#
+function create_resv_destroy { # args1 dataset1 args2 dataset2
+
+ args1=$1
+ dataset1=$2
+ args2=$3
+ dataset2=$4
+
+ log_must $ZFS create $args1 $dataset1
+
+ log_must $ZFS set reservation=$RESV_SIZE $dataset1
+
+ avail_aft_dset1=`get_prop available $TESTPOOL`
+ used_aft_dset1=`get_prop used $TESTPOOL`
+
+ log_must $ZFS create $args2 $dataset2
+
+ log_must $ZFS set reservation=$RESV_SIZE $dataset2
+
+
+ # After destroying the second dataset the space used and
+ # available totals should revert back to the values they
+ # had after creating the first dataset.
+ #
+ log_must $ZFS destroy -f $dataset2
+
+ avail_dest_dset2=`get_prop available $TESTPOOL`
+ used_dest_dset2=`get_prop used $TESTPOOL`
+
+ log_must within_limits $avail_aft_dset1 $avail_dest_dset2 $RESV_TOLERANCE
+ log_must within_limits $used_aft_dset1 $used_dest_dset2 $RESV_TOLERANCE
+
+
+ # After destroying the first dataset the space used and
+ # space available totals should revert back to the values
+ # they had when the pool was first created.
+ log_must $ZFS destroy -f $dataset1
+
+ avail_dest_dset1=`get_prop available $TESTPOOL`
+ used_dest_dset1=`get_prop used $TESTPOOL`
+
+ log_must within_limits $avail_dest_dset1 $space_avail $RESV_TOLERANCE
+ log_must within_limits $used_dest_dset1 $space_used $RESV_TOLERANCE
+}
+
+create_resv_destroy "" $TESTPOOL/$TESTFS1 "" $TESTPOOL/$TESTFS2
+create_resv_destroy "" $TESTPOOL/$TESTFS2 "" $TESTPOOL/$TESTFS1
+
+log_pass "Verify reservations on data sets doesn't affect other data sets at" \
+ " same level except for consuming space from common pool"
diff --git a/tests/sys/cddl/zfs/tests/reservation/reservation_008_pos.ksh b/tests/sys/cddl/zfs/tests/reservation/reservation_008_pos.ksh
new file mode 100644
index 000000000000..a8882e1fae6d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/reservation/reservation_008_pos.ksh
@@ -0,0 +1,118 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/reservation/reservation.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: reservation_008_pos
+#
+# DESCRIPTION:
+#
+# Setting a reservation reserves a defined minimum amount of space for
+# a dataset, and prevents other datasets using that space. Verify that
+# reducing the reservation on a filesystem allows other datasets in
+# the pool to use that space.
+#
+# STRATEGY:
+# 1) Create multiple filesystems
+# 2) Set reservations on all bar one of the filesystems
+# 3) Fill up the one non-reserved filesystem
+# 4) Reduce one of the reservations and verify can write more
+# data into the non-reserved filesystem
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify reducing reservation allows other datasets to use space"
+
+log_must create_multiple_fs $RESV_NUM_FS $TESTPOOL/$TESTFS $TESTDIR
+
+space_avail=`get_prop available $TESTPOOL`
+space_used=`get_prop used $TESTPOOL`
+
+#
+# To make sure this test doesn't take too long to execute on
+# large pools, we calculate a reservation setting which when
+# applied to all bar one of the filesystems (RESV_NUM_FS-1) will
+# ensure we have RESV_FREE_SPACE left free in the pool, which we will
+# be able to quickly fill.
+#
+resv_space_avail=`expr $space_avail - $RESV_FREE_SPACE`
+num_resv_fs=`expr $RESV_NUM_FS - 1` # Number of FS to which resv will be applied
+resv_size_set=`expr $resv_space_avail / $num_resv_fs`
+
+#
+# We set the reservations now, rather than when we created the filesystems
+# to allow us to take into account space used by the filsystem metadata
+#
+# Note we don't set a reservation on the first filesystem we created,
+# hence num=1 rather than zero below.
+#
+typeset -i num=1
+while (( $num < $RESV_NUM_FS )); do
+ log_must $ZFS set reservation=$resv_size_set $TESTPOOL/$TESTFS$num
+ (( num = num + 1 ))
+done
+
+space_avail_still=`get_prop available $TESTPOOL`
+
+fill_size=`expr $space_avail_still + $RESV_TOLERANCE`
+write_count=`expr $fill_size / $BLOCK_SIZE`
+
+# Now fill up the first filesystem (which doesn't have a reservation set
+# and thus will use up whatever free space is left in the pool).
+num=0
+log_note "Writing to $TESTDIR$num/$TESTFILE1"
+
+$FILE_WRITE -o create -f $TESTDIR$num/$TESTFILE1 -b $BLOCK_SIZE \
+ -c $write_count -d 0
+ret=$?
+if (( $ret != $ENOSPC )); then
+ log_fail "Did not get ENOSPC as expected (got $ret)."
+fi
+
+# Remove the reservation on one of the other filesystems and verify
+# can write more data to the original non-reservation filesystem.
+num=1
+log_must $ZFS set reservation=none $TESTPOOL/${TESTFS}$num
+num=0
+log_must $FILE_WRITE -o create -f ${TESTDIR}$num/$TESTFILE2 -b $BLOCK_SIZE \
+ -c 1000 -d 0
+
+log_pass "reducing reservation allows other datasets to use space"
diff --git a/tests/sys/cddl/zfs/tests/reservation/reservation_009_pos.ksh b/tests/sys/cddl/zfs/tests/reservation/reservation_009_pos.ksh
new file mode 100644
index 000000000000..20c0d629a121
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/reservation/reservation_009_pos.ksh
@@ -0,0 +1,100 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/reservation/reservation.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: reservation_009_pos
+#
+# DESCRIPTION:
+#
+# In pool with a full filesystem and another filesystem with a reservation
+# setting the reservation on the second filesystem to 'none' should allow more
+# data to be written to the first filesystem.
+#
+#
+# STRATEGY:
+# 1) Create a filesystem as a dataset
+# 2) Create a filesystem at the same level
+# 3) Set a reservation on the dataset filesystem
+# 4) Fill up the filesystem
+# 5) Set the reservation on the dataset filesystem to 'none'
+# 6) Verify we can write more data to the first filesystem
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Setting top level dataset reservation to 'none' allows more data to" \
+ " be written to top level filesystem"
+
+log_must $ZFS create $TESTPOOL/$TESTFS1
+
+space_avail=`get_prop available $TESTPOOL`
+
+#
+# To make sure this test doesn't take too long to execute on
+# large pools, we calculate a reservation setting which when
+# applied to the dataset will ensure we have RESV_FREE_SPACE
+# left free in the pool which we can quickly fill.
+#
+(( resv_size_set = space_avail - RESV_FREE_SPACE ))
+
+log_must $ZFS set reservation=$resv_size_set $TESTPOOL/$TESTFS1
+
+space_avail_still=`get_prop available $TESTPOOL`
+
+fill_size=`expr $space_avail_still + $RESV_TOLERANCE`
+write_count=`expr $fill_size / $BLOCK_SIZE`
+
+# Now fill up the filesystem (which doesn't have a reservation set
+# and thus will use up whatever free space is left in the pool).
+$FILE_WRITE -o create -f $TESTDIR/$TESTFILE1 -b $BLOCK_SIZE \
+ -c $write_count -d 0
+ret=$?
+if (( $ret != $ENOSPC )); then
+ log_fail "Did not get ENOSPC as expected (got $ret)."
+fi
+
+log_must $ZFS set reservation=none $TESTPOOL/$TESTFS1
+
+log_must $FILE_WRITE -o create -f $TESTDIR/$TESTFILE2 -b $BLOCK_SIZE \
+ -c 1000 -d 0
+
+log_pass "Setting top level dataset reservation to 'none' allows more " \
+ "data to be written to the top level filesystem"
diff --git a/tests/sys/cddl/zfs/tests/reservation/reservation_010_pos.ksh b/tests/sys/cddl/zfs/tests/reservation/reservation_010_pos.ksh
new file mode 100644
index 000000000000..e83d455f4a56
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/reservation/reservation_010_pos.ksh
@@ -0,0 +1,100 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/reservation/reservation.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: reservation_010_pos
+#
+# DESCRIPTION:
+#
+# In pool with a full filesystem and a filesystem with a reservation
+# destroying another filesystem should allow more data to be written to
+# the full filesystem
+#
+#
+# STRATEGY:
+# 1) Create a filesystem as dataset
+# 2) Create a filesystem at the same level
+# 3) Set a reservation on the dataset filesystem
+# 4) Fill up the second filesystem
+# 5) Destroy the dataset filesystem
+# 6) Verify can write more data to the full filesystem
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Destroying top level filesystem with reservation allows more data to" \
+ " be written to another top level filesystem"
+
+log_must $ZFS create $TESTPOOL/$TESTFS1
+
+space_avail=`get_prop available $TESTPOOL`
+
+#
+# To make sure this test doesn't take too long to execute on
+# large pools, we calculate a reservation setting which when
+# applied to the dataset filesystem will ensure we have
+# RESV_FREE_SPACE left free in the pool.
+#
+(( resv_size_set = space_avail - RESV_FREE_SPACE ))
+
+log_must $ZFS set reservation=$resv_size_set $TESTPOOL/$TESTFS1
+
+space_avail_still=`get_prop available $TESTPOOL`
+
+fill_size=`expr $space_avail_still + $RESV_TOLERANCE`
+write_count=`expr $fill_size / $BLOCK_SIZE`
+
+# Now fill up the filesystem (which doesn't have a reservation set
+# and thus will use up whatever free space is left in the pool).
+$FILE_WRITE -o create -f $TESTDIR/$TESTFILE1 -b $BLOCK_SIZE \
+ -c $write_count -d 0
+ret=$?
+if (( $ret != $ENOSPC )); then
+ log_fail "Did not get ENOSPC as expected (got $ret)."
+fi
+
+log_must $ZFS destroy -f $TESTPOOL/$TESTFS1
+
+log_must $FILE_WRITE -o create -f $TESTDIR/$TESTFILE2 -b $BLOCK_SIZE \
+ -c 1000 -d 0
+
+log_pass "Destroying top level filesystem with reservation allows more data to" \
+ " be written to another top level filesystem"
diff --git a/tests/sys/cddl/zfs/tests/reservation/reservation_011_pos.ksh b/tests/sys/cddl/zfs/tests/reservation/reservation_011_pos.ksh
new file mode 100644
index 000000000000..a4079afda8d1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/reservation/reservation_011_pos.ksh
@@ -0,0 +1,76 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/reservation/reservation.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: reservation_011_pos
+#
+# DESCRIPTION:
+#
+# ZFS has two mechanisms dealing with space for datasets, namely
+# reservations and quotas. Setting one should not affect the other,
+# provided the values are legal (i.e. enough space in pool etc).
+#
+# STRATEGY:
+# 1) Create one filesystem
+# 2) Get the current quota setting
+# 3) Set a reservation
+# 4) Verify that the quota value remains unchanged
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify reservation settings do not affect quota settings"
+
+space_avail=`get_prop available $TESTPOOL`
+
+(( resv_size_set = (space_avail - RESV_DELTA) / 2 ))
+
+fs_quota=`$ZFS get quota $TESTPOOL/$TESTFS`
+
+log_must $ZFS set reservation=$resv_size_set $TESTPOOL/$TESTFS
+
+new_fs_quota=`$ZFS get quota $TESTPOOL/$TESTFS`
+
+if [[ $fs_quota != $new_fs_quota ]]; then
+ log_fail "Quota value on $TESTFS has changed ($fs_quota != $new_fs_quota)"
+fi
+
+log_pass "Quota settings unaffected by reservation settings"
diff --git a/tests/sys/cddl/zfs/tests/reservation/reservation_012_pos.ksh b/tests/sys/cddl/zfs/tests/reservation/reservation_012_pos.ksh
new file mode 100644
index 000000000000..6341d3e936ca
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/reservation/reservation_012_pos.ksh
@@ -0,0 +1,93 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/reservation/reservation.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: reservation_012_pos
+#
+# DESCRIPTION:
+#
+# A reservation guarantees a certain amount of space for a dataset.
+# Nothing else which happens in the same pool should affect that
+# space, i.e. even if the rest of the pool fills up the reserved
+# space should still be accessible.
+#
+# STRATEGY:
+# 1) Create 2 filesystems
+# 2) Set a reservation on one filesystem
+# 3) Fill up the other filesystem (which does not have a reservation
+# set) until all space is consumed
+# 4) Verify can still write to the filesystem which has a reservation
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify reservations protect space"
+
+function cleanup
+{
+ $ZFS destroy -f $TESTPOOL/$TESTFS2
+ [[ -d $TESTDIR2 ]] && \
+ log_must $RM -rf $TESTDIR2
+}
+
+log_onexit cleanup
+
+log_must $ZFS create $TESTPOOL/$TESTFS2
+log_must $ZFS set mountpoint=$TESTDIR2 $TESTPOOL/$TESTFS2
+
+space_avail=`get_prop available $TESTPOOL`
+
+(( resv_size_set = space_avail - RESV_FREE_SPACE ))
+
+log_must $ZFS set reservation=$resv_size_set $TESTPOOL/$TESTFS
+
+(( write_count = ( RESV_FREE_SPACE + RESV_TOLERANCE ) / BLOCK_SIZE ))
+
+$FILE_WRITE -o create -f $TESTDIR2/$TESTFILE1 -b $BLOCK_SIZE -c $write_count -d 0
+ret=$?
+if [[ $ret != $ENOSPC ]]; then
+ log_fail "Did not get ENOSPC (got $ret) for non-reserved filesystem"
+fi
+
+(( write_count = ( RESV_FREE_SPACE - RESV_TOLERANCE ) / BLOCK_SIZE ))
+log_must $FILE_WRITE -o create -f $TESTDIR/$TESTFILE2 -b $BLOCK_SIZE -c $write_count -d 0
+
+log_pass "Reserved space preserved correctly"
diff --git a/tests/sys/cddl/zfs/tests/reservation/reservation_013_pos.ksh b/tests/sys/cddl/zfs/tests/reservation/reservation_013_pos.ksh
new file mode 100644
index 000000000000..f87ea951a9c4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/reservation/reservation_013_pos.ksh
@@ -0,0 +1,116 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/reservation/reservation.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: reservation_013_pos
+#
+# DESCRIPTION:
+#
+# Reservation properties on data objects should be preserved when the
+# pool within which they are contained is exported and then re-imported.
+#
+#
+# STRATEGY:
+# 1) Create a filesystem as dataset
+# 2) Create another filesystem at the same level
+# 3) Create a regular volume at the same level
+# 4) Create a sparse volume at the same level
+# 5) Create a filesystem within the dataset filesystem
+# 6) Set reservations on all filesystems
+# 7) Export the pool
+# 8) Re-import the pool
+# 9) Verify that the reservation settings are correct
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-19)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Reservation properties preserved across exports and imports"
+
+OBJ_LIST="$TESTPOOL/$TESTFS1/$TESTFS2 $TESTPOOL/$TESTFS1 \
+ $TESTPOOL/$TESTVOL $TESTPOOL/$TESTVOL2"
+
+log_must $ZFS create $TESTPOOL/$TESTFS1
+log_must $ZFS create $TESTPOOL/$TESTFS1/$TESTFS2
+
+space_avail=`get_prop available $TESTPOOL`
+[[ $? -ne 0 ]] && \
+ log_fail "Unable to get space available property for $TESTPOOL"
+
+(( resv_set = space_avail / 8 ))
+resv_set=$(floor_volsize $resv_set)
+(( sparse_vol_set_size = space_avail * 8 ))
+sparse_vol_set_size=$(floor_volsize $sparse_vol_set_size)
+reg_vol_blksz=8192
+
+# When initially created, a regular volume's refreservation property is set
+# equal to its size (unlike a sparse volume), so we don't need to set it
+# explicitly later on. However, since the zfs command modifies the
+# reservation based on the volume size, it is necessary to test it separately.
+log_must $ZFS create -b $reg_vol_blksz -V $resv_set $TESTPOOL/$TESTVOL
+log_must $ZFS create -s -V $sparse_vol_set_size $TESTPOOL/$TESTVOL2
+
+log_must $ZFS set refreservation=$resv_set $TESTPOOL/$TESTFS
+log_must $ZFS set refreservation=$resv_set $TESTPOOL/$TESTFS1
+log_must $ZFS set refreservation=$resv_set $TESTPOOL/$TESTFS1/$TESTFS2
+log_must $ZFS set refreservation=$resv_set $TESTPOOL/$TESTVOL2
+
+log_must $ZPOOL export $TESTPOOL
+
+typeset dir=$(get_device_dir $DISKS)
+log_must $ZPOOL import -d $dir $TESTPOOL
+
+alloc_vol_size=$(zvol_volsize_to_reservation $resv_set $reg_vol_blksz 1)
+resv_get=$(get_prop refreservation $TESTPOOL/$TESTVOL)
+[[ $resv_get != $alloc_vol_size ]] && \
+ log_fail "Reservation property for $TESTPOOL/$TESTVOL incorrect;" \
+ " expected $alloc_vol_size but got $resv_get"
+
+for obj in $TESTPOOL/$TESTFS $TESTPOOL/$TESTFS1 \
+ $TESTPOOL/$TESTVOL2 $TESTPOOL/$TESTFS1/$TESTFS2
+do
+ resv_get=`get_prop refreservation $obj`
+
+ [[ $resv_get != $resv_set ]] && \
+ log_fail "Reservation property for $obj incorrect " \
+ " expected $resv_set but got $resv_get"
+done
+
+log_pass "Reservation properties preserved across exports and imports"
diff --git a/tests/sys/cddl/zfs/tests/reservation/reservation_014_pos.ksh b/tests/sys/cddl/zfs/tests/reservation/reservation_014_pos.ksh
new file mode 100644
index 000000000000..d0921cadb965
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/reservation/reservation_014_pos.ksh
@@ -0,0 +1,121 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/reservation/reservation.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: reservation_014_pos
+#
+# DESCRIPTION:
+#
+# A reservation cannot exceed the quota on a dataset
+#
+# STRATEGY:
+# 1) Create a filesystem and volume
+# 2) Set a quota on the filesystem
+# 3) Attempt to set a reservation larger than the quota. Verify
+# that the attempt fails.
+# 4) Repeat 2-3 for volume
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify cannot set reservation larger than quota"
+
+space_avail=`get_prop available $TESTPOOL`
+
+if ! is_global_zone ; then
+ OBJ_LIST=""
+else
+ OBJ_LIST="$TESTPOOL/$TESTVOL $TESTPOOL/$TESTVOL2"
+
+ (( vol_set_size = space_avail / 4 ))
+ vol_set_size=$(floor_volsize $vol_set_size)
+ (( sparse_vol_set_size = space_avail * 4 ))
+ sparse_vol_set_size=$(floor_volsize $sparse_vol_set_size)
+
+
+ log_must $ZFS create -V $vol_set_size $TESTPOOL/$TESTVOL
+ log_must $ZFS create -s -V $sparse_vol_set_size $TESTPOOL/$TESTVOL2
+fi
+
+for obj in $TESTPOOL/$TESTFS $OBJ_LIST ; do
+
+ space_avail=`get_prop available $TESTPOOL`
+ (( quota_set_size = space_avail / 3 ))
+
+ #
+ # A regular (non-sparse) volume's size is effectively
+ # its quota so only need to explicitly set quotas for
+ # filesystems and datasets.
+ #
+ # A volumes size is effectively its quota. The maximum
+ # reservation value that can be set on a volume is
+ # determined by the size of the volume or the amount of
+ # space in the pool, whichever is smaller.
+ #
+ if [[ $obj == $TESTPOOL/$TESTFS ]]; then
+ log_must $ZFS set quota=$quota_set_size $obj
+ (( resv_set_size = quota_set_size + RESV_SIZE ))
+
+ elif [[ $obj == $TESTPOOL/$TESTVOL2 ]] ; then
+
+ (( resv_set_size = sparse_vol_set_size + RESV_SIZE ))
+
+ elif [[ $obj == $TESTPOOL/$TESTVOL ]] ; then
+
+ (( resv_set_size = vol_set_size + RESV_SIZE ))
+ fi
+
+ orig_quota=`get_prop quota $obj`
+
+ log_mustnot $ZFS set reservation=$resv_set_size $obj
+ new_quota=`get_prop quota $obj`
+
+ if [[ $orig_quota != $new_quota ]]; then
+ log_fail "Quota value changed from $orig_quota " \
+ "to $new_quota"
+ fi
+
+ if [[ $obj == $TESTPOOL/$TESTFS ]]; then
+ log_must $ZFS set quota=none $obj
+ fi
+done
+
+log_pass "As expected cannot set reservation larger than quota"
diff --git a/tests/sys/cddl/zfs/tests/reservation/reservation_015_pos.ksh b/tests/sys/cddl/zfs/tests/reservation/reservation_015_pos.ksh
new file mode 100644
index 000000000000..3fd4d0379912
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/reservation/reservation_015_pos.ksh
@@ -0,0 +1,109 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/reservation/reservation.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: reservation_015_pos
+#
+# DESCRIPTION:
+#
+# In pool with a full filesystem and a regular volume with an implicit
+# reservation, setting the reservation on the volume to 'none' should allow
+# more data to be written to the filesystem.
+#
+#
+# STRATEGY:
+# 1) Create a regular non-sparse volume (which implicitly sets the reservation
+# property to a value equal to the volume size)
+# 2) Create a filesystem at the same level
+# 3) Fill up the filesystem
+# 4) Set the reservation on the volume to 'none'
+# 5) Verify can write more data to the filesystem
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-19)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Setting volume reservation to 'none' allows more data to" \
+ " be written to top level filesystem"
+
+space_avail=`get_prop available $TESTPOOL`
+
+#
+# To make sure this test doesn't take too long to execute on
+# large pools, we calculate a volume size which when applied
+# to the volume will ensure we have RESV_FREE_SPACE
+# left free in the pool which we can quickly fill.
+#
+# For the volume itself, set a reservation to 95% of available
+# space to allow space for metadata.
+#
+(( resv_size_set = space_avail * 95 / 100 ))
+resv_size_set=$(floor_volsize $resv_size_set $BLOCK_SIZE)
+
+log_must $ZFS create -b $BLOCK_SIZE -V $resv_size_set $TESTPOOL/$TESTVOL
+
+# Create a secondary filesystem to soak up what's left to get
+# to RESV_FREE_SPACE for the pool.
+space_avail_still=`get_prop available $TESTPOOL`
+(( zfs_res = space_avail_still - RESV_FREE_SPACE ))
+log_must $ZFS create -o refreservation=$zfs_res $TESTPOOL/$TESTFS2
+
+space_avail_still=`get_prop available $TESTPOOL`
+
+fill_size=`expr $space_avail_still + $RESV_TOLERANCE`
+write_count=`expr $fill_size / $BLOCK_SIZE`
+
+# Now fill up the filesystem (which doesn't have a reservation set
+# and thus will use up whatever free space is left in the pool).
+$FILE_WRITE -o create -f $TESTDIR/$TESTFILE1 -b $BLOCK_SIZE \
+ -c $write_count -d 0
+ret=$?
+
+if (( $ret != $ENOSPC )); then
+ log_fail "Did not get ENOSPC as expected (got $ret)."
+fi
+
+log_must $ZFS set refreservation=none $TESTPOOL/$TESTVOL
+
+log_must $FILE_WRITE -o create -f $TESTDIR/$TESTFILE2 -b $BLOCK_SIZE \
+ -c 1000 -d 0
+
+log_pass "Setting top level volume reservation to 'none' allows more " \
+ "data to be written to the top level filesystem"
diff --git a/tests/sys/cddl/zfs/tests/reservation/reservation_016_pos.ksh b/tests/sys/cddl/zfs/tests/reservation/reservation_016_pos.ksh
new file mode 100644
index 000000000000..55b612026403
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/reservation/reservation_016_pos.ksh
@@ -0,0 +1,107 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/reservation/reservation.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: reservation_016_pos
+#
+# DESCRIPTION:
+#
+# In pool with a full filesystem and a regular volume (with implicit
+# reservation) destroying the volume should allow more data to be written
+# to the filesystem
+#
+#
+# STRATEGY:
+# 1) Create a regular (non-sparse) volume
+# 2) Create a filesystem at the same level
+# 3) Fill up the filesystem
+# 4) Destroy the volume
+# 5) Verify can write more data to the filesystem
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-19)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Destroying a regular volume with reservation allows more data to" \
+ " be written to top level filesystem"
+
+space_avail=`get_prop available $TESTPOOL`
+
+#
+# To make sure this test doesn't take too long to execute on
+# large pools, we calculate a volume size which will ensure we
+# have RESV_FREE_SPACE left free in the pool.
+#
+(( vol_set_size = space_avail * 95 / 100 ))
+vol_set_size=$(floor_volsize $vol_set_size $BLOCK_SIZE)
+
+# Creating a regular volume implicitly sets its reservation
+# property to the same value.
+log_must $ZFS create -b $BLOCK_SIZE -V $vol_set_size $TESTPOOL/$TESTVOL
+
+space_avail_still=`get_prop available $TESTPOOL`
+
+# Create a secondary filesystem to soak up what's left to get
+# to RESV_FREE_SPACE for the pool.
+space_avail_still=`get_prop available $TESTPOOL`
+(( zfs_res = space_avail_still - RESV_FREE_SPACE ))
+log_must $ZFS create -o refreservation=$zfs_res $TESTPOOL/$TESTFS2
+
+space_avail_still=`get_prop available $TESTPOOL`
+
+fill_size=`expr $space_avail_still + $RESV_TOLERANCE`
+write_count=`expr $fill_size / $BLOCK_SIZE`
+
+# Now fill up the filesystem (which doesn't have a reservation set
+# and thus will use up whatever free space is left in the pool).
+$FILE_WRITE -o create -f $TESTDIR/$TESTFILE1 -b $BLOCK_SIZE \
+ -c $write_count -d 0
+ret=$?
+if (( $ret != $ENOSPC )); then
+ log_fail "Did not get ENOSPC as expected (got $ret)."
+fi
+
+log_must $ZFS destroy -f $TESTPOOL/$TESTVOL
+
+log_must $FILE_WRITE -o create -f $TESTDIR/$TESTFILE2 -b $BLOCK_SIZE \
+ -c 1000 -d 0
+
+log_pass "Destroying volume with reservation allows more data to" \
+ " be written to top level filesystem"
diff --git a/tests/sys/cddl/zfs/tests/reservation/reservation_017_pos.ksh b/tests/sys/cddl/zfs/tests/reservation/reservation_017_pos.ksh
new file mode 100644
index 000000000000..0b5a2bf23c67
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/reservation/reservation_017_pos.ksh
@@ -0,0 +1,100 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/reservation/reservation.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: reservation_017_pos
+#
+# DESCRIPTION:
+#
+# For a sparse volume changes to the volsize are not reflected in the reservation
+#
+# STRATEGY:
+# 1) Create a regular and sparse volume
+# 2) Get the space available in the pool
+# 3) Set reservation with various size on the regular and sparse volume
+# 4) Verify that the 'reservation' property for the regular volume has
+# the correct value.
+# 5) Verify that the 'reservation' property for the sparse volume is set to 'none'
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-08-17)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+log_assert "Verify that the volsize changes of sparse volume are not reflected" \
+ "in the reservation"
+
+#Create a regular and sparse volume for testing.
+regvol=$TESTPOOL/$TESTVOL
+sparsevol=$TESTPOOL/$TESTVOL2
+log_must $ZFS create -V $VOLSIZE -o volblocksize=16k $regvol
+log_must $ZFS create -s -V $VOLSIZE -o volblocksize=16k $sparsevol
+
+typeset -l vsize=$(get_prop available $TESTPOOL)
+typeset -i iterate=10
+typeset -l regreserv
+typeset -l sparsereserv
+typeset -l vblksize1=$(get_prop volblocksize $regvol)
+typeset -l vblksize2=$(get_prop volblocksize $sparsevol)
+typeset -l blknum=0
+if [ "$vblksize1" != "$vblksize2" ]; then
+ log_must $ZFS set volblocksize=$vblksize1 $sparsevol
+fi
+(( blknum = vsize / vblksize1 ))
+
+typeset -i randomblknum
+while (( iterate > 1 )); do
+ (( randomblknum = 1 + $RANDOM % $blknum ))
+ #Make sure volsize is a multiple of volume block size
+ (( vsize = $randomblknum * $vblksize1 ))
+ log_must $ZFS set volsize=$vsize $regvol
+ log_must $ZFS set volsize=$vsize $sparsevol
+ regreserv=$(get_prop refreservation $regvol)
+ sparsereserv=$(get_prop refreservation $sparsevol)
+ reg_shouldreserv=$(zvol_volsize_to_reservation $vsize $vblksize1 1)
+
+ (( $sparsereserv == $vsize )) && \
+ log_fail "volsize changes of sparse volume is reflected in reservation."
+ (( $regreserv != $reg_shouldreserv )) && \
+ log_fail "volsize changes of regular volume isnot reflected in reservation."
+
+ (( iterate = iterate - 1 ))
+done
+
+log_pass "The volsize change of sparse volume is not reflected in reservation as expected."
diff --git a/tests/sys/cddl/zfs/tests/reservation/reservation_018_pos.ksh b/tests/sys/cddl/zfs/tests/reservation/reservation_018_pos.ksh
new file mode 100644
index 000000000000..0833ca3299e1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/reservation/reservation_018_pos.ksh
@@ -0,0 +1,73 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/reservation/reservation.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: reservation_018_pos
+#
+# DESCRIPTION:
+#
+# Verify that reservation doesn't inherit its value from parent.
+#
+# STRATEGY:
+# 1) Create a filesystem tree
+# 2) Set reservation for parents
+# 3) Verify that the 'reservation' for descendent doesnot inherit the value.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-08-17)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "both"
+
+log_assert "Verify that reservation doesnot inherit its value from parent."
+
+fs=$TESTPOOL/$TESTFS
+fs_child=$TESTPOOL/$TESTFS/$TESTFS
+
+space_avail=$(get_prop available $fs)
+reserv_val=$(get_prop reservation $fs)
+typeset -l reservsize=$space_avail
+((reservsize = reservsize / 2 ))
+log_must $ZFS set reservation=$reservsize $fs
+
+log_must $ZFS create $fs_child
+rsv_space=$(get_prop reservation $fs_child)
+[[ $rsv_space == $reservsize ]] && \
+ log_fail "The reservation of child dataset inherits its value from parent."
+
+log_pass "reservation doesnot inherit its value from parent as expected."
diff --git a/tests/sys/cddl/zfs/tests/reservation/reservation_test.sh b/tests/sys/cddl/zfs/tests/reservation/reservation_test.sh
new file mode 100755
index 000000000000..f31f415337fb
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/reservation/reservation_test.sh
@@ -0,0 +1,522 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case reservation_001_pos cleanup
+reservation_001_pos_head()
+{
+ atf_set "descr" "Verify that to set a reservation on a filesystem or volume must use value smaller than space \ available property of pool"
+ atf_set "require.progs" "ksh93 zfs"
+}
+reservation_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/reservation_001_pos.ksh || atf_fail "Testcase failed"
+}
+reservation_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case reservation_002_pos cleanup
+reservation_002_pos_head()
+{
+ atf_set "descr" "Reservation values cannot exceed the amount of space available in the pool"
+ atf_set "require.progs" "ksh93 zfs"
+}
+reservation_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/reservation_002_pos.ksh || atf_fail "Testcase failed"
+}
+reservation_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case reservation_003_pos cleanup
+reservation_003_pos_head()
+{
+ atf_set "descr" "Verify it is possible to set reservations multiple times on a filesystem regular and sparse volume"
+ atf_set "require.progs" "ksh93 zfs"
+}
+reservation_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/reservation_003_pos.ksh || atf_fail "Testcase failed"
+}
+reservation_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case reservation_004_pos cleanup
+reservation_004_pos_head()
+{
+ atf_set "descr" "Verify space released when a dataset with reservation is destroyed"
+ atf_set "require.progs" "ksh93 zfs"
+}
+reservation_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/reservation_004_pos.ksh || atf_fail "Testcase failed"
+}
+reservation_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case reservation_005_pos cleanup
+reservation_005_pos_head()
+{
+ atf_set "descr" "Verify space released when reservation on a dataset is setto 'none'"
+ atf_set "require.progs" "ksh93 zfs"
+}
+reservation_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/reservation_005_pos.ksh || atf_fail "Testcase failed"
+}
+reservation_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case reservation_006_pos cleanup
+reservation_006_pos_head()
+{
+ atf_set "descr" "Verify can create files both inside and outside reserved areas"
+ atf_set "require.progs" "ksh93 zfs"
+}
+reservation_006_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/reservation_006_pos.ksh || atf_fail "Testcase failed"
+}
+reservation_006_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case reservation_007_pos cleanup
+reservation_007_pos_head()
+{
+ atf_set "descr" "Verify reservations on data sets doesn't affect other data sets at same level except for consuming space from common pool"
+ atf_set "require.progs" "ksh93 zfs"
+}
+reservation_007_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/reservation_007_pos.ksh || atf_fail "Testcase failed"
+}
+reservation_007_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case reservation_008_pos cleanup
+reservation_008_pos_head()
+{
+ atf_set "descr" "Verify reducing reservation allows other datasets to use space"
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 600
+}
+reservation_008_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/reservation_008_pos.ksh || atf_fail "Testcase failed"
+}
+reservation_008_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case reservation_009_pos cleanup
+reservation_009_pos_head()
+{
+ atf_set "descr" "Setting top level dataset reservation to 'none' allows more data to be written to top level filesystem"
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 600
+}
+reservation_009_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/reservation_009_pos.ksh || atf_fail "Testcase failed"
+}
+reservation_009_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case reservation_010_pos cleanup
+reservation_010_pos_head()
+{
+ atf_set "descr" "Destroying top level filesystem with reservation allows more data to be written to another top level filesystem"
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 600
+}
+reservation_010_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/reservation_010_pos.ksh || atf_fail "Testcase failed"
+}
+reservation_010_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case reservation_011_pos cleanup
+reservation_011_pos_head()
+{
+ atf_set "descr" "Verify reservation settings do not affect quota settings"
+ atf_set "require.progs" "ksh93 zfs"
+}
+reservation_011_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/reservation_011_pos.ksh || atf_fail "Testcase failed"
+}
+reservation_011_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case reservation_012_pos cleanup
+reservation_012_pos_head()
+{
+ atf_set "descr" "Verify reservations protect space"
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 600
+}
+reservation_012_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/reservation_012_pos.ksh || atf_fail "Testcase failed"
+}
+reservation_012_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case reservation_013_pos cleanup
+reservation_013_pos_head()
+{
+ atf_set "descr" "Reservation properties preserved across exports and imports"
+ atf_set "require.progs" "ksh93 zfs zpool"
+}
+reservation_013_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/reservation_013_pos.ksh || atf_fail "Testcase failed"
+}
+reservation_013_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case reservation_014_pos cleanup
+reservation_014_pos_head()
+{
+ atf_set "descr" "Verify cannot set reservation larger than quota"
+ atf_set "require.progs" "ksh93 zfs"
+}
+reservation_014_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/reservation_014_pos.ksh || atf_fail "Testcase failed"
+}
+reservation_014_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case reservation_015_pos cleanup
+reservation_015_pos_head()
+{
+ atf_set "descr" "Setting volume reservation to 'none' allows more data to be written to top level filesystem"
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 600
+}
+reservation_015_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/reservation_015_pos.ksh || atf_fail "Testcase failed"
+}
+reservation_015_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case reservation_016_pos cleanup
+reservation_016_pos_head()
+{
+ atf_set "descr" "Destroying a regular volume with reservation allows more data to be written to top level filesystem"
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 600
+}
+reservation_016_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/reservation_016_pos.ksh || atf_fail "Testcase failed"
+}
+reservation_016_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case reservation_017_pos cleanup
+reservation_017_pos_head()
+{
+ atf_set "descr" "Verify that the volsize changes of sparse volume are not reflectedin the reservation"
+ atf_set "require.progs" "ksh93 zfs"
+}
+reservation_017_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/reservation_017_pos.ksh || atf_fail "Testcase failed"
+}
+reservation_017_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case reservation_018_pos cleanup
+reservation_018_pos_head()
+{
+ atf_set "descr" "Verify that reservation doesnot inherit its value from parent."
+ atf_set "require.progs" "ksh93 zfs"
+}
+reservation_018_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/reservation_018_pos.ksh || atf_fail "Testcase failed"
+}
+reservation_018_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/reservation.kshlib
+ . $(atf_get_srcdir)/reservation.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case reservation_001_pos
+ atf_add_test_case reservation_002_pos
+ atf_add_test_case reservation_003_pos
+ atf_add_test_case reservation_004_pos
+ atf_add_test_case reservation_005_pos
+ atf_add_test_case reservation_006_pos
+ atf_add_test_case reservation_007_pos
+ atf_add_test_case reservation_008_pos
+ atf_add_test_case reservation_009_pos
+ atf_add_test_case reservation_010_pos
+ atf_add_test_case reservation_011_pos
+ atf_add_test_case reservation_012_pos
+ atf_add_test_case reservation_013_pos
+ atf_add_test_case reservation_014_pos
+ atf_add_test_case reservation_015_pos
+ atf_add_test_case reservation_016_pos
+ atf_add_test_case reservation_017_pos
+ atf_add_test_case reservation_018_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/reservation/setup.ksh b/tests/sys/cddl/zfs/tests/reservation/setup.ksh
new file mode 100644
index 000000000000..8d2906e3cb9a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/reservation/setup.ksh
@@ -0,0 +1,38 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_setup_noexit $DISK
+#
+# Set compression=off to make sure the data is not compressed for
+# reservation testing.
+#
+log_must $ZFS set compression=off $TESTPOOL
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/rootpool/Makefile b/tests/sys/cddl/zfs/tests/rootpool/Makefile
new file mode 100644
index 000000000000..b4ddcc9b3256
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rootpool/Makefile
@@ -0,0 +1,17 @@
+.include <src.opts.mk>
+
+PACKAGE= tests
+TESTSDIR= ${TESTSBASE}/sys/cddl/zfs/tests/rootpool
+FILESDIR= ${TESTSDIR}
+
+ATF_TESTS_KSH93+= rootpool_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= rootpool_007_neg.ksh
+${PACKAGE}FILES+= rootpool_002_neg.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= rootpool_001_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/rootpool/cleanup.ksh b/tests/sys/cddl/zfs/tests/rootpool/cleanup.ksh
new file mode 100644
index 000000000000..e060e19c01f0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rootpool/cleanup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/rootpool/rootpool_001_pos.ksh b/tests/sys/cddl/zfs/tests/rootpool/rootpool_001_pos.ksh
new file mode 100644
index 000000000000..b3a3c958026e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rootpool/rootpool_001_pos.ksh
@@ -0,0 +1,66 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: rootpool_001_pos
+#
+# DESCRIPTION:
+#
+# rootpool's bootfs property must be equal to <rootfs>
+#
+# STRATEGY:
+# 1) check if the system is zfsroot or not.
+# 2) get the rootpool and rootfs if it's zfs root
+# 3) check the rootpool's bootfs value
+# 4) chek if the boofs equal to rootfs
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-01-21)
+#
+# __stc_assertion_end
+#
+################################################################################
+verify_runnable "global"
+log_assert "rootpool's bootfs property must be equal to <rootfs>"
+
+typeset rootfs=$(get_rootfs)
+typeset rootpool=$(get_rootpool)
+typeset bootfs=$(get_pool_prop bootfs $rootpool)
+
+if [[ $bootfs != $rootfs ]]; then
+ log_fail "rootfs is not same as bootfs."
+fi
+
+log_pass "rootpool's bootfs property equal to rootfs."
+
diff --git a/tests/sys/cddl/zfs/tests/rootpool/rootpool_002_neg.ksh b/tests/sys/cddl/zfs/tests/rootpool/rootpool_002_neg.ksh
new file mode 100644
index 000000000000..333c1e18a226
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rootpool/rootpool_002_neg.ksh
@@ -0,0 +1,66 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: rootpool_002_neg
+#
+# DESCRIPTION:
+#
+# the zfs rootpool can not be destroyed
+#
+# STRATEGY:
+# 1) check if the current system is installed as zfs root
+# 2) get the rootpool
+# 3) try to destroy the rootpool, which should fail.
+# 4) try to destroy the rootpool filesystem, which should fail.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-01-21)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+log_assert "zpool/zfs destory <rootpool> should return error"
+
+
+typeset rootpool=$(get_rootpool)
+
+log_mustnot $ZPOOL destroy $rootpool
+
+log_mustnot $ZFS destroy $rootpool
+
+log_pass "rootpool can not be destroyed"
+
diff --git a/tests/sys/cddl/zfs/tests/rootpool/rootpool_007_neg.ksh b/tests/sys/cddl/zfs/tests/rootpool/rootpool_007_neg.ksh
new file mode 100644
index 000000000000..731a05ae8dd0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rootpool/rootpool_007_neg.ksh
@@ -0,0 +1,79 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: rootpool_007_neg
+#
+# DESCRIPTION:
+#
+# the zfs rootfilesystem's compression property can not set to gzip[1-9]
+#
+# STRATEGY:
+# 1) check if the current system is installed as zfs root
+# 2) get the rootfs
+# 3) set the rootfs's compression to gzip 1-9 which should fail.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-07-08)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup {
+ log_must $ZFS set compression=$orig_compress $rootfs
+}
+
+log_onexit cleanup
+log_assert $assert_msg
+
+typeset rootpool=$(get_rootpool)
+typeset rootfs=$(get_pool_prop bootfs $rootpool)
+typeset orig_compress=$(get_prop compression $rootfs)
+
+typeset assert_msg="the zfs rootfs's compression property can not set to \
+ gzip and gzip[1-9]"
+
+set -A gtype "gzip" "gzip-1" "gzip-2" "gzip-3" "gzip-4" "gzip-5" \
+ "gzip-6" "gzip-7" "gzip-8" "gzip-9"
+
+typeset -i i=0
+while (( i < ${#gtype[@]} )); do
+ log_mustnot $ZFS set compression=${gtype[i]} $rootfs
+ (( i += 1 ))
+done
+
+log_pass $assert_msg
diff --git a/tests/sys/cddl/zfs/tests/rootpool/rootpool_test.sh b/tests/sys/cddl/zfs/tests/rootpool/rootpool_test.sh
new file mode 100755
index 000000000000..e96235db993b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rootpool/rootpool_test.sh
@@ -0,0 +1,105 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case rootpool_001_pos cleanup
+rootpool_001_pos_head()
+{
+ atf_set "descr" "rootpool's bootfs property must be equal to <rootfs>"
+}
+rootpool_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+
+ if ! is_zfsroot ; then
+ atf_skip "This test requires a ZFS root filesystem."
+ fi
+
+ ksh93 $(atf_get_srcdir)/setup.ksh
+ ksh93 $(atf_get_srcdir)/rootpool_001_pos.ksh || atf_fail "Testcase failed"
+}
+rootpool_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case rootpool_002_neg cleanup
+rootpool_002_neg_head()
+{
+ atf_set "descr" "zpool/zfs destory <rootpool> should return error"
+ atf_set "require.progs" "ksh93 zfs zpool"
+}
+rootpool_002_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+
+ if ! is_zfsroot ; then
+ atf_skip "This test requires a ZFS root filesystem."
+ fi
+
+ ksh93 $(atf_get_srcdir)/rootpool_002_neg.ksh || atf_fail "Testcase failed"
+}
+rootpool_002_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case rootpool_007_neg cleanup
+rootpool_007_neg_head()
+{
+ atf_set "descr" "the zfs rootfs's compression property can not set to gzip and gzip[1-9]"
+ atf_set "require.progs" "ksh93 zfs"
+}
+rootpool_007_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+
+ if ! is_zfsroot ; then
+ atf_skip "This test requires a ZFS root filesystem."
+ fi
+
+ ksh93 $(atf_get_srcdir)/rootpool_007_neg.ksh || atf_fail "Testcase failed"
+}
+rootpool_007_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case rootpool_001_pos
+ atf_add_test_case rootpool_002_neg
+ atf_add_test_case rootpool_007_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/rootpool/setup.ksh b/tests/sys/cddl/zfs/tests/rootpool/setup.ksh
new file mode 100644
index 000000000000..feec3362c0cb
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rootpool/setup.ksh
@@ -0,0 +1,34 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+if ! is_zfsroot ; then
+ log_unsupported "current system is not installed as zfs root fs"
+fi
+
diff --git a/tests/sys/cddl/zfs/tests/rsend/Makefile b/tests/sys/cddl/zfs/tests/rsend/Makefile
new file mode 100644
index 000000000000..708b53ed90e7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rsend/Makefile
@@ -0,0 +1,29 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/rsend
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= rsend_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= rsend_003_pos.ksh
+${PACKAGE}FILES+= rsend_007_pos.ksh
+${PACKAGE}FILES+= rsend_012_pos.ksh
+${PACKAGE}FILES+= rsend_013_pos.ksh
+${PACKAGE}FILES+= rsend_002_pos.ksh
+${PACKAGE}FILES+= rsend_006_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= rsend_011_pos.ksh
+${PACKAGE}FILES+= rsend.kshlib
+${PACKAGE}FILES+= rsend_008_pos.ksh
+${PACKAGE}FILES+= rsend_004_pos.ksh
+${PACKAGE}FILES+= rsend_001_pos.ksh
+${PACKAGE}FILES+= rsend_005_pos.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= rsend_009_pos.ksh
+${PACKAGE}FILES+= rsend.cfg
+${PACKAGE}FILES+= rsend_010_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/rsend/cleanup.ksh b/tests/sys/cddl/zfs/tests/rsend/cleanup.ksh
new file mode 100644
index 000000000000..d1367455c786
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rsend/cleanup.ksh
@@ -0,0 +1,48 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/rsend/rsend.kshlib
+
+verify_runnable "both"
+
+#
+# Check if the system support 'send -R'
+#
+$ZFS send 2>&1 | grep -w "[-R]" > /dev/null 2>&1
+if (($? != 0)); then
+ log_unsupported
+fi
+
+if is_global_zone ; then
+ destroy_pool $POOL
+ destroy_pool $POOL2
+else
+ cleanup_pool $POOL
+ cleanup_pool $POOL2
+fi
+log_must $RM -rf $BACKDIR $TESTDIR
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/rsend/rsend.cfg b/tests/sys/cddl/zfs/tests/rsend/rsend.cfg
new file mode 100644
index 000000000000..08ee33a3598a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rsend/rsend.cfg
@@ -0,0 +1,38 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export BACKDIR=${TEST_BASE_DIR%%/}/backdir${TESTCASE_ID}
+
+export DISK1=$($ECHO $DISKS | $AWK '{print $1}')
+export DISK2=$($ECHO $DISKS | $AWK '{print $2}')
+
+export POOL=$TESTPOOL
+export POOL2=$TESTPOOL1
+export FS=$TESTFS
+export CLONE=testclone.${TESTCASE_ID}
+export VOL=testvol.${TESTCASE_ID}
+
+export STF_TIMEOUT=2700
diff --git a/tests/sys/cddl/zfs/tests/rsend/rsend.kshlib b/tests/sys/cddl/zfs/tests/rsend/rsend.kshlib
new file mode 100644
index 000000000000..93277ccd81cd
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rsend/rsend.kshlib
@@ -0,0 +1,368 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#
+# Set up test model which includes various datasets
+#
+# @final
+# @snapB
+# @init
+# |
+# ______ pclone
+# | /
+# |@psnap
+# || @final
+# ||@final @final @snapC
+# ||@snapC @snapC @snapB
+# ||@snapA @snapB @snapA
+# ||@init @init @init
+# ||| | |
+# $pool -------- $FS ------- fs1 ------- fs2
+# \ \\_____ \ |
+# vol vol \____ \ @fsnap
+# | | \ \ \
+# @init @vsnap | ------------ fclone
+# @snapA @init \ | |
+# @final @snapB \ | @init
+# @snapC vclone @snapA
+# @final | @final
+# @init
+# @snapC
+# @final
+#
+# $1 pool name
+#
+function setup_test_model
+{
+ typeset pool=$1
+
+ log_must $ZFS create -p $pool/$FS/fs1/fs2
+
+ log_must $ZFS snapshot $pool@psnap
+ log_must $ZFS clone $pool@psnap $pool/pclone
+
+ if is_global_zone ; then
+ log_must $ZFS create -V 16M $pool/vol
+ log_must $ZFS create -V 16M $pool/$FS/vol
+
+ log_must $ZFS snapshot $pool/$FS/vol@vsnap
+ log_must $ZFS clone $pool/$FS/vol@vsnap $pool/$FS/vclone
+ fi
+
+ log_must snapshot_tree $pool/$FS/fs1/fs2@fsnap
+ log_must $ZFS clone $pool/$FS/fs1/fs2@fsnap $pool/$FS/fs1/fclone
+ log_must $ZFS snapshot -r $pool@init
+
+ log_must snapshot_tree $pool@snapA
+ log_must snapshot_tree $pool@snapC
+ log_must snapshot_tree $pool/pclone@snapB
+ log_must snapshot_tree $pool/$FS@snapB
+ log_must snapshot_tree $pool/$FS@snapC
+ log_must snapshot_tree $pool/$FS/fs1@snapA
+ log_must snapshot_tree $pool/$FS/fs1@snapB
+ log_must snapshot_tree $pool/$FS/fs1@snapC
+ log_must snapshot_tree $pool/$FS/fs1/fclone@snapA
+
+ if is_global_zone ; then
+ log_must $ZFS snapshot $pool/vol@snapA
+ log_must $ZFS snapshot $pool/$FS/vol@snapB
+ log_must $ZFS snapshot $pool/$FS/vol@snapC
+ log_must $ZFS snapshot $pool/$FS/vclone@snapC
+ fi
+
+ log_must $ZFS snapshot -r $pool@final
+
+ return 0
+}
+
+#
+# Cleanup the BACKDIR and given pool content and all the sub datasets
+#
+# $1 pool name
+#
+function cleanup_pool
+{
+ typeset pool=$1
+ log_must $RM -rf $BACKDIR/*
+
+ if is_global_zone ; then
+ log_must $ZFS destroy -Rf $pool
+ else
+ typeset list=$($ZFS list -H -r -t filesystem,snapshot,volume -o name $pool)
+ for ds in $list ; do
+ if [[ $ds != $pool ]] ; then
+ if datasetexists $ds ; then
+ log_must $ZFS destroy -Rf $ds
+ fi
+ fi
+ done
+ fi
+
+ typeset mntpnt=$(get_prop mountpoint $pool)
+ if ! ismounted $pool ; then
+ # Make sure mountpoint directory is empty
+ if [[ -d $mntpnt ]]; then
+ log_must $RM -rf $mntpnt/*
+ fi
+
+ log_must $ZFS mount $pool
+ fi
+ if [[ -d $mntpnt ]]; then
+ log_must $RM -rf $mntpnt/*
+ fi
+
+ return 0
+}
+
+#
+# Detect if the given two filesystems have same sub-datasets
+#
+# $1 source filesystem
+# $2 destination filesystem
+#
+function cmp_ds_subs
+{
+ typeset src_fs=$1
+ typeset dst_fs=$2
+
+ $ZFS list -r -H -t filesystem,snapshot,volume -o name $src_fs > $BACKDIR/src1
+ $ZFS list -r -H -t filesystem,snapshot,volume -o name $dst_fs > $BACKDIR/dst1
+
+ eval $SED -e 's:^$src_fs:PREFIX:g' < $BACKDIR/src1 > $BACKDIR/src
+ eval $SED -e 's:^$dst_fs:PREFIX:g' < $BACKDIR/dst1 > $BACKDIR/dst
+
+ $DIFF $BACKDIR/src $BACKDIR/dst
+ typeset -i ret=$?
+
+ $RM -f $BACKDIR/src $BACKDIR/dst $BACKDIR/src1 $BACKDIR/dst1
+
+ return $ret
+}
+
+#
+# Compare all the directores and files in two filesystems
+#
+# $1 source filesystem
+# $2 destination filesystem
+#
+function cmp_ds_cont
+{
+ typeset src_fs=$1
+ typeset dst_fs=$2
+
+ typeset srcdir dstdir
+ srcdir=$(get_prop mountpoint $src_fs)
+ dstdir=$(get_prop mountpoint $dst_fs)
+
+ $DIFF -r $srcdir $dstdir > /dev/null 2>&1
+ print $?
+}
+
+#
+# Compare the given two dataset properties
+#
+# $1 dataset 1
+# $2 dataset 2
+#
+function cmp_ds_prop
+{
+ typeset dtst1=$1
+ typeset dtst2=$2
+
+ for item in "type" "origin" "volblocksize" "aclinherit" "aclmode" \
+ "atime" "canmount" "checksum" "compression" "copies" "devices" \
+ "exec" "quota" "readonly" "recordsize" "reservation" "setuid" \
+ "shareiscsi" "sharenfs" "snapdir" "version" "volsize" "xattr" \
+ "zoned" "mountpoint";
+ do
+ $ZFS get -H -o property,value,source $item $dtst1 >> \
+ $BACKDIR/dtst1
+ $ZFS get -H -o property,value,source $item $dtst2 >> \
+ $BACKDIR/dtst2
+ done
+
+ eval $SED -e 's:$dtst1:PREFIX:g' < $BACKDIR/dtst1 > $BACKDIR/dtst1
+ eval $SED -e 's:$dtst2:PREFIX:g' < $BACKDIR/dtst2 > $BACKDIR/dtst2
+
+ $DIFF $BACKDIR/dtst1 $BACKDIR/dtst2
+ typeset -i ret=$?
+
+ $RM -f $BACKDIR/dtst1 $BACKDIR/dtst2
+
+ return $ret
+
+}
+
+#
+# Random create directories and files
+#
+# $1 directory
+#
+function random_tree
+{
+ typeset dir=$1
+
+ if [[ -d $dir ]]; then
+ $RM -rf $dir
+ fi
+ $MKDIR -p $dir
+ typeset -i ret=$?
+
+ typeset -i nl nd nf
+ ((nl = RANDOM % 6 + 1))
+ ((nd = RANDOM % 3 ))
+ ((nf = RANDOM % 5 ))
+ $MKTREE -b $dir -l $nl -d $nd -f $nf
+ ((ret |= $?))
+
+ return $ret
+}
+
+#
+# Put data in filesystem and take snapshot
+#
+# $1 snapshot name
+#
+function snapshot_tree
+{
+ typeset snap=$1
+ typeset ds=${snap%%@*}
+ typeset type=$(get_prop "type" $ds)
+
+ typeset -i ret=0
+ if [[ $type == "filesystem" ]]; then
+ typeset mntpnt=$(get_prop mountpoint $ds)
+ ((ret |= $?))
+
+ if ((ret == 0)) ; then
+ eval random_tree $mntpnt/${snap##$ds}
+ ((ret |= $?))
+ fi
+ fi
+
+ if ((ret == 0)) ; then
+ $ZFS snapshot $snap
+ ((ret |= $?))
+ fi
+
+ return $ret
+}
+
+#
+# Destroy the given snapshot and stuff
+#
+# $1 snapshot
+#
+function destroy_tree
+{
+ typeset -i ret=0
+ typeset snap
+ for snap in "$@" ; do
+ $ZFS destroy $snap
+ ret=$?
+
+ typeset ds=${snap%%@*}
+ typeset type=$(get_prop "type" $ds)
+ if [[ $type == "filesystem" ]]; then
+ typeset mntpnt=$(get_prop mountpoint $ds)
+ ((ret |= $?))
+
+ if ((ret != 0)); then
+ $RM -r $mntpnt/$snap
+ ((ret |= $?))
+ fi
+ fi
+
+ if ((ret != 0)); then
+ return $ret
+ fi
+ done
+
+ return 0
+}
+
+#
+# Get all the sub-datasets of give dataset with specific suffix
+#
+# $1 Given dataset
+# $2 Suffix
+#
+function getds_with_suffix
+{
+ typeset ds=$1
+ typeset suffix=$2
+
+ typeset list=$($ZFS list -r -H -t filesystem,snapshot,volume -o name $ds \
+ | $GREP "$suffix$")
+
+ $ECHO $list
+}
+
+#
+# Output inherited properties whitch is edited for file system
+#
+function fs_inherit_prop
+{
+ typeset fs_prop
+ if is_global_zone ; then
+ fs_prop=$($ZFS inherit 2>&1 | \
+ $AWK '$2=="YES" && $3=="YES" {print $1}')
+ else
+ fs_prop=$($ZFS inherit 2>&1 | \
+ $AWK '$2=="YES" && $3=="YES" {print $1}'|
+ $EGREP -v "devices|sharenfs|sharesmb|zoned")
+ fi
+
+ $ECHO $fs_prop
+}
+
+#
+# Output inherited properties for volume
+#
+function vol_inherit_prop
+{
+ $ECHO "checksum readonly shareiscsi"
+}
+
+#
+# Get the destination dataset to compare
+#
+function get_dst_ds
+{
+ typeset srcfs=$1
+ typeset dstfs=$2
+
+ #
+ # If the srcfs is not pool
+ #
+ if ! $ZPOOL list $srcfs > /dev/null 2>&1 ; then
+ eval dstfs="$dstfs/${srcfs#*/}"
+ fi
+
+ $ECHO $dstfs
+}
diff --git a/tests/sys/cddl/zfs/tests/rsend/rsend_001_pos.ksh b/tests/sys/cddl/zfs/tests/rsend/rsend_001_pos.ksh
new file mode 100644
index 000000000000..5579321a203e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rsend/rsend_001_pos.ksh
@@ -0,0 +1,82 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/rsend/rsend.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: rsend_001_pos
+#
+# DESCRIPTION:
+# zfs send -R send replication stream up to the named snap.
+#
+# STRATEGY:
+# 1. Back up all the data from POOL/FS
+# 2. Verify all the datasets and data can be recovered in POOL2
+# 3. Back up all the data from root filesystem POOL2
+# 4. Verify all the data can be recovered, too
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-08-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "zfs send -R send replication stream up to the named snap."
+log_onexit cleanup_pool $POOL2
+
+#
+# Verify the entire pool and sub-ds can be backup and restored.
+#
+log_must eval "$ZFS send -R $POOL@final > $BACKDIR/pool-final-R"
+log_must eval "$ZFS receive -d -F $POOL2 < $BACKDIR/pool-final-R"
+
+dstds=$(get_dst_ds $POOL $POOL2)
+log_must cmp_ds_subs $POOL $dstds
+log_must cmp_ds_cont $POOL $dstds
+
+# Cleanup POOL2
+log_must cleanup_pool $POOL2
+
+#
+# Verify all the filesystem and sub-fs can be backup and restored.
+#
+log_must eval "$ZFS send -R $POOL/$FS@final > $BACKDIR/fs-final-R"
+log_must eval "$ZFS receive -d $POOL2 < $BACKDIR/fs-final-R"
+
+dstds=$(get_dst_ds $POOL/$FS $POOL2)
+log_must cmp_ds_subs $POOL/$FS $dstds
+log_must cmp_ds_cont $POOL/$FS $dstds
+
+log_pass "zfs send -R send replication stream up to the named snap."
diff --git a/tests/sys/cddl/zfs/tests/rsend/rsend_002_pos.ksh b/tests/sys/cddl/zfs/tests/rsend/rsend_002_pos.ksh
new file mode 100644
index 000000000000..83161975e673
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rsend/rsend_002_pos.ksh
@@ -0,0 +1,102 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/rsend/rsend.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: rsend_002_pos
+#
+# DESCRIPTION:
+# zfs send -I sends all incrementals from fs@init to fs@final.
+#
+# STRATEGY:
+# 1. Create several snapshots in pool2
+# 2. Send -I @snapA @final
+# 3. Destroy all the snapshot except @snapA
+# 4. Make sure all the snapshots and content are recovered
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-08-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "zfs send -I sends all incrementals from fs@init to fs@final."
+log_onexit cleanup_pool $POOL2
+
+#
+# Duplicate POOL2
+#
+log_must eval "$ZFS send -R $POOL@final > $BACKDIR/pool-R"
+log_must eval "$ZFS receive -d -F $POOL2 < $BACKDIR/pool-R"
+
+if is_global_zone ; then
+ #
+ # Verify send -I will backup all the incrementals in pool
+ #
+ log_must eval "$ZFS send -I $POOL2@init $POOL2@final > " \
+ "$BACKDIR/pool-init-final-I"
+ log_must destroy_tree $POOL2@final $POOL2@snapC $POOL2@snapA
+ log_must eval "$ZFS receive -d -F $POOL2 < $BACKDIR/pool-init-final-I"
+ log_must cmp_ds_subs $POOL $POOL2
+ log_must cmp_ds_cont $POOL $POOL2
+fi
+
+dstds=$(get_dst_ds $POOL $POOL2)
+
+#
+# Verify send -I will backup all the incrementals in filesystem
+#
+log_must eval "$ZFS send -I @init $dstds/$FS@final > $BACKDIR/fs-init-final-I"
+log_must destroy_tree $dstds/$FS@final $dstds/$FS@snapC $dstds/$FS@snapB
+log_must eval "$ZFS receive -d -F $dstds < $BACKDIR/fs-init-final-I"
+log_must cmp_ds_subs $POOL $dstds
+log_must cmp_ds_cont $POOL $dstds
+
+if is_global_zone ; then
+ #
+ # Verify send -I will backup all the incrementals in volume
+ #
+ dataset=$POOL2/$FS/vol
+ log_must eval "$ZFS send -I @vsnap $dataset@final > " \
+ "$BACKDIR/vol-vsnap-final-I"
+ log_must destroy_tree $dataset@final $dataset@snapC \
+ $dataset@snapB $dataset@init
+ log_must eval "$ZFS receive -d -F $POOL2 < $BACKDIR/vol-vsnap-final-I"
+ log_must cmp_ds_subs $POOL $POOL2
+ log_must cmp_ds_cont $POOL $POOL2
+fi
+
+log_pass "zfs send -I sends all incrementals from fs@init to fs@final."
diff --git a/tests/sys/cddl/zfs/tests/rsend/rsend_003_pos.ksh b/tests/sys/cddl/zfs/tests/rsend/rsend_003_pos.ksh
new file mode 100644
index 000000000000..2b8a833f706a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rsend/rsend_003_pos.ksh
@@ -0,0 +1,104 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/rsend/rsend.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: rsend_003_pos
+#
+# DESCRIPTION:
+# zfs send -I dataset@init to clone@snap can create a clone
+#
+# STRATEGY:
+# 1. Setup test model
+# 2. send -I pool@init to clone@snap
+# 3. Verify the clone and snapshot can be recovered via receive
+# 4. Verify the similar operating in filesystem and volume
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-08-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "zfs send -I send all incrementals from dataset@init to clone@snap"
+log_onexit cleanup_pool $POOL2
+
+#
+# Duplicate POOL2
+#
+log_must eval "$ZFS send -R $POOL@final > $BACKDIR/pool-R"
+log_must eval "$ZFS receive -d -F $POOL2 < $BACKDIR/pool-R"
+
+if is_global_zone ; then
+ #
+ # Verify send -I backup all incrementals from pool
+ #
+ log_must eval "$ZFS send -I $POOL2@psnap $POOL2/pclone@final > " \
+ "$BACKDIR/pool-clone-I"
+ log_must $ZFS destroy -rf $POOL2/pclone
+ log_must eval "$ZFS receive -d -F $POOL2 < $BACKDIR/pool-clone-I"
+ log_must cmp_ds_subs $POOL $POOL2
+ log_must cmp_ds_cont $POOL $POOL2
+fi
+
+dstds=$(get_dst_ds $POOL $POOL2)
+
+#
+# Verify send -I backup all incrementals from filesystem
+#
+ds=$dstds/$FS/fs1
+log_must eval "$ZFS send -I $ds/fs2@fsnap $ds/fclone@final > " \
+ "$BACKDIR/fs-clone-I"
+log_must $ZFS destroy -rf $ds/fclone
+log_must eval "$ZFS receive -F $ds/fclone < $BACKDIR/fs-clone-I"
+
+log_must cmp_ds_subs $POOL $dstds
+log_must cmp_ds_cont $POOL $dstds
+
+if is_global_zone ; then
+ #
+ # Verify send -I backup all incrementals from volume
+ #
+ ds=$POOL2/$FS
+ log_must eval "$ZFS send -I $ds/vol@vsnap $ds/vclone@final > " \
+ "$BACKDIR/vol-clone-I"
+ log_must $ZFS destroy -rf $ds/vclone
+ log_must eval "$ZFS receive -d -F $POOL2 < $BACKDIR/vol-clone-I"
+ log_must cmp_ds_subs $POOL $POOL2
+ log_must cmp_ds_cont $POOL $POOL2
+fi
+
+log_pass "zfs send -I send all incrementals from dataset@init to clone@snap"
diff --git a/tests/sys/cddl/zfs/tests/rsend/rsend_004_pos.ksh b/tests/sys/cddl/zfs/tests/rsend/rsend_004_pos.ksh
new file mode 100644
index 000000000000..638bef8c6f22
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rsend/rsend_004_pos.ksh
@@ -0,0 +1,129 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/rsend/rsend.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: rsend_004_pos
+#
+# DESCRIPTION:
+# zfs send -R -i send incremental from fs@init to fs@final.
+#
+# STRATEGY:
+# 1. Create a set of snapshots and fill with data.
+# 2. Create sub filesystems.
+# 3. Create final snapshot
+# 4. Verify zfs send -R -i will backup all the datasets which has
+# snapshot suffix @final
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-08-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "zfs send -R -i send incremental from fs@init to fs@final."
+log_onexit cleanup_pool $POOL2
+
+#
+# Duplicate POOL2 for testing
+#
+log_must eval "$ZFS send -R $POOL@final > $BACKDIR/pool-final-R"
+log_must eval "$ZFS receive -d -F $POOL2 < $BACKDIR/pool-final-R"
+
+if is_global_zone ; then
+ #
+ # Testing send -R -i backup from pool
+ #
+ srclist=$(getds_with_suffix $POOL2 @final)
+ interlist="$srclist $(getds_with_suffix $POOL2 @snapC)"
+ interlist="$interlist $(getds_with_suffix $POOL2 @snapB)"
+ interlist="$interlist $(getds_with_suffix $POOL2 @snapA)"
+
+ log_must eval "$ZFS send -R -i @init $POOL2@final > " \
+ "$BACKDIR/pool-init-final-iR"
+ log_must destroy_tree $interlist
+ log_must eval "$ZFS receive -d -F $POOL2 < $BACKDIR/pool-init-final-iR"
+
+ # Get current datasets with suffix @final
+ dstlist=$(getds_with_suffix $POOL2 @final)
+ if [[ $srclist != $dstlist ]]; then
+ log_fail "Unexpected: srclist($srclist) != dstlist($dstlist)"
+ fi
+ log_must cmp_ds_cont $POOL $POOL2
+fi
+
+dstds=$(get_dst_ds $POOL $POOL2)
+#
+# Testing send -R -i backup from filesystem
+#
+log_must eval "$ZFS send -R -i @init $dstds/$FS@final > " \
+ "$BACKDIR/fs-init-final-iR"
+
+srclist=$(getds_with_suffix $dstds/$FS @final)
+interlist="$srclist $(getds_with_suffix $dstds/$FS @snapC)"
+interlist="$interlist $(getds_with_suffix $dstds/$FS @snapB)"
+interlist="$interlist $(getds_with_suffix $dstds/$FS @snapA)"
+log_must destroy_tree $interlist
+if is_global_zone ; then
+ log_must eval "$ZFS receive -d -F $POOL2 < $BACKDIR/fs-init-final-iR"
+else
+ $ZFS receive -F -d $dstds/$FS < $BACKDIR/fs-init-final-iR
+fi
+
+dstlist=$(getds_with_suffix $dstds/$FS @final)
+if [[ $srclist != $dstlist ]]; then
+ log_fail "Unexpected: srclist($srclist) != dstlist($dstlist)"
+fi
+log_must cmp_ds_cont $POOL $POOL2
+
+if is_global_zone ; then
+ #
+ # Testing send -R -i backup from volume
+ #
+ srclist=$(getds_with_suffix $POOL2/$FS/vol @final)
+ log_must eval "$ZFS send -R -i @init $POOL2/$FS/vol@final > " \
+ "$BACKDIR/vol-init-final-iR"
+ log_must destroy_tree $srclist
+ log_must eval "$ZFS receive -d $POOL2 < $BACKDIR/vol-init-final-iR"
+
+ dstlist=$(getds_with_suffix $POOL2/$FS/vol @final)
+ if [[ $srclist != $dstlist ]]; then
+ log_fail "Unexpected: srclist($srclist) != dstlist($dstlist)"
+ fi
+ log_must cmp_ds_cont $POOL $POOL2
+fi
+
+log_pass "zfs send -R -i send incremental from fs@init to fs@final."
diff --git a/tests/sys/cddl/zfs/tests/rsend/rsend_005_pos.ksh b/tests/sys/cddl/zfs/tests/rsend/rsend_005_pos.ksh
new file mode 100644
index 000000000000..20c4262d7e40
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rsend/rsend_005_pos.ksh
@@ -0,0 +1,113 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/rsend/rsend.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: rsend_005_pos
+#
+# DESCRIPTION:
+# zfs send -R -I send all the incremental between fs@init with fs@final
+#
+# STRATEGY:
+# 1. Setup test model
+# 2. Send -R -I @init @final on pool
+# 3. Destroy all the snapshots which is later than @init
+# 4. Verify receive can restore all the snapshots and data
+# 5. Do the same test on filesystem and volume
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-08-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "zfs send -R -I send all the incremental between @init with @final"
+log_onexit cleanup_pool $POOL2
+
+#
+# Duplicate POOL2 for testing
+#
+log_must eval "$ZFS send -R $POOL@final > $BACKDIR/pool-final-R"
+log_must eval "$ZFS receive -d -F $POOL2 < $BACKDIR/pool-final-R"
+
+if is_global_zone ; then
+ #
+ # Testing send -R -I from pool
+ #
+ log_must eval "$ZFS send -R -I @init $POOL2@final > " \
+ "$BACKDIR/pool-init-final-IR"
+ list=$(getds_with_suffix $POOL2 @snapA)
+ list="$list $(getds_with_suffix $POOL2 @snapB)"
+ list="$list $(getds_with_suffix $POOL2 @snapC)"
+ list="$list $(getds_with_suffix $POOL2 @final)"
+ log_must destroy_tree $list
+ log_must eval "$ZFS receive -d -F $POOL2 < $BACKDIR/pool-init-final-IR"
+ log_must cmp_ds_cont $POOL $POOL2
+fi
+
+dstds=$(get_dst_ds $POOL $POOL2)
+#
+# Testing send -R -I from filesystem
+#
+log_must eval "$ZFS send -R -I @init $dstds/$FS@final > " \
+ "$BACKDIR/fs-init-final-IR"
+list=$(getds_with_suffix $dstds/$FS @snapA)
+list="$list $(getds_with_suffix $dstds/$FS @snapB)"
+list="$list $(getds_with_suffix $dstds/$FS @snapC)"
+list="$list $(getds_with_suffix $dstds/$FS @final)"
+log_must destroy_tree $list
+if is_global_zone ; then
+ log_must eval "$ZFS receive -d -F $dstds < $BACKDIR/fs-init-final-IR"
+else
+ $ZFS receive -d -F $dstds < $BACKDIR/fs-init-final-IR
+fi
+log_must cmp_ds_subs $POOL $dstds
+log_must cmp_ds_cont $POOL $dstds
+
+if is_global_zone ; then
+ #
+ # Testing send -I -R for volume
+ #
+ vol=$POOL2/$FS/vol
+ log_must eval "$ZFS send -R -I @init $vol@final > " \
+ "$BACKDIR/vol-init-final-IR"
+ log_must destroy_tree $vol@snapB $vol@snapC $vol@final
+ log_must eval "$ZFS receive -d -F $POOL2 < $BACKDIR/vol-init-final-IR"
+ log_must cmp_ds_subs $POOL $POOL2
+ log_must cmp_ds_cont $POOL $POOL2
+fi
+
+log_pass "zfs send -R -I send all the incremental between @init with @final"
diff --git a/tests/sys/cddl/zfs/tests/rsend/rsend_006_pos.ksh b/tests/sys/cddl/zfs/tests/rsend/rsend_006_pos.ksh
new file mode 100644
index 000000000000..b03199924b53
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rsend/rsend_006_pos.ksh
@@ -0,0 +1,91 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/rsend/rsend.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: rsend_006_pos
+#
+# DESCRIPTION:
+# Rename snapshot name will not change the dependent order.
+#
+# STRATEGY:
+# 1. Set up a set of datasets.
+# 2. Rename part of snapshots.
+# 3. Send -R all the POOL
+# 4. Verify snapshot name will not change the dependent order.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-08-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+# Source Target
+#
+set -A snaps "$POOL@init" "$POOL@snap0" \
+ "$POOL@snapA" "$POOL@snap1" \
+ "$POOL@snapC" "$POOL@snap2" \
+ "$POOL@final" "$POOL@init"
+
+function cleanup
+{
+ log_must cleanup_pool $POOL
+ log_must cleanup_pool $POOL2
+
+ log_must setup_test_model $POOL
+}
+
+log_assert "Rename snapshot name will not change the dependent order."
+log_onexit cleanup
+
+typeset -i i=0
+while ((i < ${#snaps[@]})); do
+ log_must $ZFS rename -r ${snaps[$i]} ${snaps[((i+1))]}
+
+ ((i += 2))
+done
+
+#
+# Duplicate POOL2 for testing
+#
+log_must eval "$ZFS send -R $POOL@init > $BACKDIR/pool-final-R"
+log_must eval "$ZFS receive -d -F $POOL2 < $BACKDIR/pool-final-R"
+
+dstds=$(get_dst_ds $POOL $POOL2)
+log_must cmp_ds_subs $POOL $dstds
+log_must cmp_ds_cont $POOL $dstds
+
+log_pass "Rename snapshot name will not change the dependent order."
diff --git a/tests/sys/cddl/zfs/tests/rsend/rsend_007_pos.ksh b/tests/sys/cddl/zfs/tests/rsend/rsend_007_pos.ksh
new file mode 100644
index 000000000000..bdc75154c33b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rsend/rsend_007_pos.ksh
@@ -0,0 +1,108 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/rsend/rsend.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: rsend_007_pos
+#
+# DESCRIPTION:
+# Rename parent filesystem name will not change the dependent order.
+#
+# STRATEGY:
+# 1. Separately rename pool clone, filesystem and volume name.
+# 2. Send -R all the POOL
+# 3. Verify renamed dataset will not change the snapshot dependent order.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-08-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+set -A dtst \
+ "$POOL/pclone" "$POOL/$FS/pclone" \
+ "$POOL/$FS/fs1/fs2" "$POOL/fs2"
+if is_global_zone ; then
+ typeset -i n=${#dtst[@]}
+ dtst[((n))]="$POOL/vol"; dtst[((n+1))]="$POOL/$FS/fs1/vol"
+fi
+
+function cleanup
+{
+ log_must cleanup_pool $POOL
+ log_must cleanup_pool $POOL2
+
+ log_must setup_test_model $POOL
+}
+
+log_assert "Rename parent filesystem name will not change the dependent order."
+log_onexit cleanup
+
+typeset -i i=0
+while ((i < ${#dtst[@]})); do
+ log_must $ZFS rename ${dtst[$i]} ${dtst[((i+1))]}
+
+ ((i += 2))
+done
+
+#
+# Verify zfs send -R should succeed
+#
+log_must eval "$ZFS send -R $POOL@final > $BACKDIR/pool-final-R"
+log_must eval "$ZFS receive -d -F $POOL2 < $BACKDIR/pool-final-R"
+dstds=$(get_dst_ds $POOL $POOL2)
+log_must cmp_ds_subs $POOL $dstds
+log_must cmp_ds_cont $POOL $dstds
+
+#
+# Verify zfs send -R -I should succeed
+#
+log_must eval "$ZFS send -R -I @init $dstds@final > " \
+ "$BACKDIR/pool-init-final-IR"
+list=$(getds_with_suffix $dstds @snapA)
+list="$list $(getds_with_suffix $dstds @snapB)"
+list="$list $(getds_with_suffix $dstds @snapC)"
+list="$list $(getds_with_suffix $dstds @final)"
+log_must destroy_tree $list
+if is_global_zone ; then
+ log_must eval "$ZFS receive -d -F $dstds < $BACKDIR/pool-init-final-IR"
+else
+ $ZFS receive -d -F $dstds < $BACKDIR/pool-init-final-IR
+fi
+log_must cmp_ds_subs $POOL $dstds
+log_must cmp_ds_cont $POOL $dstds
+
+log_pass "Rename parent filesystem name will not change the dependent order."
diff --git a/tests/sys/cddl/zfs/tests/rsend/rsend_008_pos.ksh b/tests/sys/cddl/zfs/tests/rsend/rsend_008_pos.ksh
new file mode 100644
index 000000000000..44b6092a2153
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rsend/rsend_008_pos.ksh
@@ -0,0 +1,137 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/rsend/rsend.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: rsend_008_pos
+#
+# DESCRIPTION:
+# Changes made by 'zfs promote' can be properly received.
+#
+# STRATEGY:
+# 1. Separately promote pool clone, filesystem clone and volume clone.
+# 2. Recursively backup all the POOL and restore in POOL2
+# 3. Verify all the datesets and property be properly received.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-08-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+# Origin Clone
+#
+set -A dtst "$POOL" "$POOL/pclone" \
+ "$POOL/$FS/fs1/fs2" "$POOL/$FS/fs1/fclone"
+if is_global_zone ; then
+ typeset -i n=${#dtst[@]}
+ dtst[((n))]="$POOL/$FS/vol"; dtst[((n+1))]="$POOL/$FS/vclone"
+fi
+
+function cleanup
+{
+ typeset origin
+ typeset -i i=0
+ while ((i < ${#dtst[@]})); do
+ origin=$(get_prop origin ${dtst[$i]})
+
+ if [[ $origin != "-" ]]; then
+ log_must $ZFS promote ${dtst[$i]}
+ fi
+
+ ((i += 2))
+ done
+
+ origin=$(get_prop origin $POOL2)
+ if [[ $origin != "-" ]]; then
+ log_must $ZFS promote $POOL2
+ fi
+ log_must cleanup_pool $POOL2
+}
+
+log_assert "Changes made by 'zfs promote' can be properly received."
+log_onexit cleanup
+
+typeset -i i=0
+while ((i < ${#dtst[@]})); do
+ log_must $ZFS promote ${dtst[((i+1))]}
+
+ ((i += 2))
+done
+
+#
+# Verify zfs send -R should succeed
+#
+log_must eval "$ZFS send -R $POOL@final > $BACKDIR/pool-final-R"
+log_must eval "$ZFS receive -d -F $POOL2 < $BACKDIR/pool-final-R"
+
+dstds=$(get_dst_ds $POOL $POOL2)
+#
+# Define all the POOL/POOL2 datasets pair
+#
+set -A pair "$POOL" "$dstds" \
+ "$POOL/$FS" "$dstds/$FS" \
+ "$POOL/$FS/fs1" "$dstds/$FS/fs1" \
+ "$POOL/$FS/fs1/fs2" "$dstds/$FS/fs1/fs2" \
+ "$POOL/pclone" "$dstds/pclone" \
+ "$POOL/$FS/fs1/fclone" "$dstds/$FS/fs1/fclone"
+
+if is_global_zone ; then
+ typeset -i n=${#pair[@]}
+ pair[((n))]="$POOL/vol"; pair[((n+1))]="$dstds/vol"
+ pair[((n+2))]="$POOL/$FS/vol" pair[((n+3))]="$dstds/$FS/vol"
+fi
+
+#
+# Verify all the sub-datasets can be properly received.
+#
+log_must cmp_ds_subs $POOL $dstds
+typeset -i i=0
+while ((i < ${#pair[@]})); do
+ log_must cmp_ds_cont ${pair[$i]} ${pair[((i+1))]}
+ log_must cmp_ds_prop ${pair[$i]} ${pair[((i+1))]}
+
+ ((i += 2))
+done
+
+# Verify the original filesystem can be promoted
+log_must $ZFS promote $dstds
+if is_global_zone ; then
+ log_must $ZFS promote $dstds/$FS/vol
+fi
+log_must $ZFS promote $dstds/$FS/fs1/fs2
+
+log_pass "Changes made by 'zfs promote' can be properly received."
diff --git a/tests/sys/cddl/zfs/tests/rsend/rsend_009_pos.ksh b/tests/sys/cddl/zfs/tests/rsend/rsend_009_pos.ksh
new file mode 100644
index 000000000000..2181fc37276c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rsend/rsend_009_pos.ksh
@@ -0,0 +1,103 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/rsend/rsend.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: rsend_009_pos
+#
+# DESCRIPTION:
+# zfs receive can handle out of space correctly.
+#
+# STRATEGY:
+# 1. Create two pools, one is big and another is small.
+# 2. Fill the big pool with data.
+# 3. Take snapshot and backup the whole pool.
+# 4. Receive this stream in small pool.
+# 5. Verify zfs receive can handle the out of space error correctly.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-10-10)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ if datasetexists bpool ; then
+ log_must $ZPOOL destroy -f bpool
+ fi
+ if datasetexists spool ; then
+ log_must $ZPOOL destroy -f spool
+ fi
+}
+
+log_assert "Verify zfs receive can handle out of space correctly."
+log_onexit cleanup
+
+log_must $TRUNCATE -s 100M $TESTDIR/bfile
+log_must $TRUNCATE -s 64M $TESTDIR/sfile
+log_must $ZPOOL create bpool $TESTDIR/bfile
+log_must $ZPOOL create spool $TESTDIR/sfile
+
+#
+# Test out of space on sub-filesystem
+#
+log_must $ZFS create bpool/fs
+mntpnt=$(get_prop mountpoint bpool/fs)
+log_must $TRUNCATE -s 30M $mntpnt/file
+
+log_must $ZFS snapshot bpool/fs@snap
+log_must eval "$ZFS send -R bpool/fs@snap > $BACKDIR/fs-R"
+log_mustnot eval "$ZFS receive -d -F spool < $BACKDIR/fs-R"
+
+log_must datasetnonexists spool/fs
+log_must ismounted spool
+
+#
+# Test out of space on top filesystem
+#
+mntpnt2=$(get_prop mountpoint bpool)
+log_must $MV $mntpnt/file $mntpnt2
+log_must $ZFS destroy -rf bpool/fs
+
+log_must $ZFS snapshot bpool@snap
+log_must eval "$ZFS send -R bpool@snap > $BACKDIR/bpool-R"
+log_mustnot eval "$ZFS receive -d -F spool < $BACKDIR/bpool-R"
+
+log_must datasetnonexists spool/fs
+log_must ismounted spool
+
+log_pass "zfs receive can handle out of space correctly."
diff --git a/tests/sys/cddl/zfs/tests/rsend/rsend_010_pos.ksh b/tests/sys/cddl/zfs/tests/rsend/rsend_010_pos.ksh
new file mode 100644
index 000000000000..ab9d3a8cd323
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rsend/rsend_010_pos.ksh
@@ -0,0 +1,86 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/rsend/rsend.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: rsend_010_pos
+#
+# DESCRIPTION:
+# ZFS can handle stream with multiple identical (same GUID) snapshots
+#
+# STRATEGY:
+# 1. Recursively backup snapshot
+# 2. Restore it to the given filesystem
+# 3. Resend the snapshot again
+# 4. Verify this stream can be restore to this filesystem again
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-10-11)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "ZFS can handle stream with multiple identical (same GUID) snapshots"
+log_onexit cleanup_pool $POOL2
+
+log_must $ZFS create $POOL2/$FS
+log_must $ZFS snapshot $POOL2/$FS@snap
+
+#
+# First round restore the stream
+#
+log_must eval "$ZFS send -R $POOL2/$FS@snap > $BACKDIR/fs-R"
+log_must eval "$ZFS receive -d -F $POOL2/$FS < $BACKDIR/fs-R"
+
+#
+# In order to avoid 'zfs send -R' failed, create snapshot for
+# all the sub-systems
+#
+list=$($ZFS list -r -H -o name -t filesystem $POOL2/$FS)
+for item in $list ; do
+ if datasetnonexists $item@snap ; then
+ log_must $ZFS snapshot $item@snap
+ fi
+done
+
+#
+# Second round restore the stream
+#
+log_must eval "$ZFS send -R $POOL2/$FS@snap > $BACKDIR/fs-R"
+dstds=$(get_dst_ds $POOL2/$FS $POOL2/$FS)
+log_must eval "$ZFS receive -d -F $dstds < $BACKDIR/fs-R"
+
+log_pass "ZFS can handle stream with multiple identical (same GUID) snapshots"
diff --git a/tests/sys/cddl/zfs/tests/rsend/rsend_011_pos.ksh b/tests/sys/cddl/zfs/tests/rsend/rsend_011_pos.ksh
new file mode 100644
index 000000000000..f683b5393e55
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rsend/rsend_011_pos.ksh
@@ -0,0 +1,132 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/rsend/rsend.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: rsend_011_pos
+#
+# DESCRIPTION:
+# Changes made by 'zfs inherit' can be properly received.
+#
+# STRATEGY:
+# 1. Inherit property for filesystem and volume
+# 2. Send and restore them in the target pool
+# 3. Verify all the datasets can be properly backup and receive
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-10-10)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ unmount_all_safe > /dev/null 2>&1
+ log_must cleanup_pool $POOL
+ log_must cleanup_pool $POOL2
+
+ log_must setup_test_model $POOL
+}
+
+log_assert "Verify changes made by 'zfs inherit' can be properly received."
+log_onexit cleanup
+
+#
+# Setting all the $FS properties as local value,
+#
+for prop in $(fs_inherit_prop); do
+ value=$(get_prop $prop $POOL/$FS)
+ log_must $ZFS set $prop=$value $POOL/$FS
+done
+
+#
+# Inherit propertes in sub-datasets
+#
+for ds in "$POOL/$FS/fs1" "$POOL/$FS/fs1/fs2" "$POOL/$FS/fs1/fclone" ; do
+ for prop in $(fs_inherit_prop) ; do
+ $ZFS inherit $prop $ds
+ if (($? !=0 )); then
+ log_fail "$ZFS inherit $prop $ds"
+ fi
+ done
+done
+if is_global_zone ; then
+ for prop in $(vol_inherit_prop) ; do
+ $ZFS inherit $prop $POOL/$FS/vol
+ if (($? !=0 )); then
+ log_fail "$ZFS inherit $prop $POOL/$FS/vol"
+ fi
+ done
+fi
+
+#
+# Verify datasets can be backup and restore correctly
+# Unmount $POOL/$FS to avoid two fs mount in the same mountpoint
+#
+log_must eval "$ZFS send -R $POOL@final > $BACKDIR/pool-R"
+log_must $ZFS unmount -f $POOL/$FS
+log_must eval "$ZFS receive -d -F $POOL2 < $BACKDIR/pool-R"
+
+dstds=$(get_dst_ds $POOL $POOL2)
+#
+# Define all the POOL/POOL2 datasets pair
+#
+set -A pair "$POOL" "$dstds" \
+ "$POOL/$FS" "$dstds/$FS" \
+ "$POOL/$FS/fs1" "$dstds/$FS/fs1" \
+ "$POOL/$FS/fs1/fs2" "$dstds/$FS/fs1/fs2" \
+ "$POOL/pclone" "$dstds/pclone" \
+ "$POOL/$FS/fs1/fclone" "$dstds/$FS/fs1/fclone"
+
+if is_global_zone ; then
+ typeset -i n=${#pair[@]}
+ pair[((n))]="$POOL/vol"; pair[((n+1))]="$dstds/vol"
+ pair[((n+2))]="$POOL/$FS/vol" pair[((n+3))]="$dstds/$FS/vol"
+fi
+
+#
+# Verify all the sub-datasets can be properly received.
+#
+log_must cmp_ds_subs $POOL $dstds
+typeset -i i=0
+while ((i < ${#pair[@]})); do
+ log_must cmp_ds_cont ${pair[$i]} ${pair[((i+1))]}
+ log_must cmp_ds_prop ${pair[$i]} ${pair[((i+1))]}
+
+ ((i += 2))
+done
+
+log_pass "Changes made by 'zfs inherit' can be properly received."
diff --git a/tests/sys/cddl/zfs/tests/rsend/rsend_012_pos.ksh b/tests/sys/cddl/zfs/tests/rsend/rsend_012_pos.ksh
new file mode 100644
index 000000000000..506912c78100
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rsend/rsend_012_pos.ksh
@@ -0,0 +1,206 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/rsend/rsend.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: rsend_012_pos
+#
+# DESCRIPTION:
+# zfs send -R will backup all the filesystem properties correctly.
+#
+# STRATEGY:
+# 1. Setting properties for all the filesystem and volumes randomly
+# 2. Backup all the data from POOL by send -R
+# 3. Restore all the data in POOL2
+# 4. Verify all the perperties in two pools are same
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-08-27)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function rand_set_prop
+{
+ typeset dtst=$1
+ typeset prop=$2
+ shift 2
+ typeset value=$(random_get $@)
+
+ log_must eval "$ZFS set $prop='$value' $dtst"
+}
+
+function edited_prop
+{
+ typeset behaviour=$1
+ typeset ds=$2
+ typeset backfile=$TESTDIR/edited_prop_$ds
+
+ case $behaviour in
+ "get")
+ typeset props=$($ZFS inherit 2>&1 | \
+ $AWK '$2=="YES" {print $1}' | \
+ $EGREP -v "^vol|\.\.\.$")
+ for item in $props ; do
+ $ZFS get -H -o property,value $item $ds >> \
+ $backfile
+ if (($? != 0)); then
+ log_fail "zfs get -H -o property,value"\
+ "$item $ds > $backfile"
+ fi
+ done
+ ;;
+ "set")
+ if [[ ! -f $backfile ]] ; then
+ log_fail "$ds need backup properties firstly."
+ fi
+
+ typeset prop value
+ while read prop value ; do
+ eval $ZFS set $prop='$value' $ds
+ if (($? != 0)); then
+ log_fail "$ZFS set $prop=$value $ds"
+ fi
+ done < $backfile
+ ;;
+ *)
+ log_fail "Unrecognized behaviour: $behaviour"
+ esac
+}
+
+function cleanup
+{
+ log_must cleanup_pool $POOL
+ log_must cleanup_pool $POOL2
+
+ log_must edited_prop "set" $POOL
+ log_must edited_prop "set" $POOL2
+
+ typeset prop
+ for prop in $(fs_inherit_prop) ; do
+ log_must $ZFS inherit $prop $POOL
+ log_must $ZFS inherit $prop $POOL2
+ done
+
+ #if is_shared $POOL; then
+ # log_must $ZFS set sharenfs=off $POOL
+ #fi
+ log_must setup_test_model $POOL
+
+ if [[ -d $TESTDIR ]]; then
+ log_must $RM -rf $TESTDIR/*
+ fi
+}
+
+log_assert "Verify zfs send -R will backup all the filesystem properties " \
+ "correctly."
+log_onexit cleanup
+
+log_must edited_prop "get" $POOL
+log_must edited_prop "get" $POOL2
+
+for fs in "$POOL" "$POOL/pclone" "$POOL/$FS" "$POOL/$FS/fs1" \
+ "$POOL/$FS/fs1/fs2" "$POOL/$FS/fs1/fclone" ; do
+ rand_set_prop $fs aclinherit "discard" "noallow" "secure" "passthrough"
+ rand_set_prop $fs checksum "on" "off" "fletcher2" "fletcher4" "sha256"
+ rand_set_prop $fs aclmode "discard" "groupmask" "passthrough"
+ rand_set_prop $fs atime "on" "off"
+ rand_set_prop $fs checksum "on" "off" "fletcher2" "fletcher4" "sha256"
+ rand_set_prop $fs compression "on" "off" "lzjb" "gzip" \
+ "gzip-1" "gzip-2" "gzip-3" "gzip-4" "gzip-5" "gzip-6" \
+ "gzip-7" "gzip-8" "gzip-9"
+ rand_set_prop $fs copies "1" "2" "3"
+ rand_set_prop $fs devices "on" "off"
+ rand_set_prop $fs exec "on" "off"
+ rand_set_prop $fs quota "512M" "1024M"
+ rand_set_prop $fs recordsize "512" "2K" "8K" "32K" "128K"
+ rand_set_prop $fs setuid "on" "off"
+ rand_set_prop $fs snapdir "hidden" "visible"
+ rand_set_prop $fs xattr "on" "off"
+ rand_set_prop $fs user:prop "aaa" "bbb" "23421" "()-+?"
+done
+
+for vol in "$POOL/vol" "$POOL/$FS/vol" ; do
+ rand_set_prop $vol checksum "on" "off" "fletcher2" "fletcher4" "sha256"
+ rand_set_prop $vol compression "on" "off" "lzjb" "gzip" \
+ "gzip-1" "gzip-2" "gzip-3" "gzip-4" "gzip-5" "gzip-6" \
+ "gzip-7" "gzip-8" "gzip-9"
+ rand_set_prop $vol readonly "on" "off"
+ rand_set_prop $vol copies "1" "2" "3"
+ rand_set_prop $vol user:prop "aaa" "bbb" "23421" "()-+?"
+done
+
+
+# Verify inherited property can be received
+rand_set_prop $POOL sharenfs "on" "off" "rw"
+
+#
+# Duplicate POOL2 for testing
+#
+log_must eval "$ZFS send -R $POOL@final > $BACKDIR/pool-final-R"
+log_must eval "$ZFS receive -d -F $POOL2 < $BACKDIR/pool-final-R"
+
+#
+# Define all the POOL/POOL2 datasets pair
+#
+set -A pair "$POOL" "$POOL2" \
+ "$POOL/$FS" "$POOL2/$FS" \
+ "$POOL/$FS/fs1" "$POOL2/$FS/fs1" \
+ "$POOL/$FS/fs1/fs2" "$POOL2/$FS/fs1/fs2" \
+ "$POOL/pclone" "$POOL2/pclone" \
+ "$POOL/$FS/fs1/fclone" "$POOL2/$FS/fs1/fclone" \
+ "$POOL/vol" "$POOL2/vol" \
+ "$POOL/$FS/vol" "$POOL2/$FS/vol"
+
+typeset -i i=0
+while ((i < ${#pair[@]})); do
+ log_must cmp_ds_prop ${pair[$i]} ${pair[((i+1))]}
+
+ ((i += 2))
+done
+
+
+$ZPOOL upgrade -v | $GREP "Snapshot properties" > /dev/null 2>&1
+if (( $? == 0 )) ; then
+ i=0
+ while ((i < ${#pair[@]})); do
+ log_must cmp_ds_prop ${pair[$i]}@final ${pair[((i+1))]}@final
+ ((i += 2))
+ done
+fi
+
+log_pass "Verify zfs send -R will backup all the filesystem properties " \
+ "correctly."
diff --git a/tests/sys/cddl/zfs/tests/rsend/rsend_013_pos.ksh b/tests/sys/cddl/zfs/tests/rsend/rsend_013_pos.ksh
new file mode 100644
index 000000000000..a2433fa976bf
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rsend/rsend_013_pos.ksh
@@ -0,0 +1,95 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/rsend/rsend.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: rsend_013_pos
+#
+# DESCRIPTION:
+# zfs receive -dF with incremental stream will destroy all the
+# dataset that not exist on the sender side.
+#
+# STRATEGY:
+# 1. Setup test model
+# 2. Send -R @final on pool
+# 3. Destroy some dataset within the @final, and create @destroy
+# 4. Send -R -I @final @destroy on pool
+# 5. Verify receive -dF will destroy all the dataset that not exist
+# on the sender side.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-12-15)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ cleanup_pool $POOL2
+ cleanup_pool $POOL
+ log_must setup_test_model $POOL
+}
+
+log_assert "zfs receive -dF will destroy all the dataset that not exist" \
+ "on the sender side"
+log_onexit cleanup
+
+cleanup
+
+#
+# Duplicate POOL2 for testing
+#
+log_must eval "$ZFS send -R $POOL@final > $BACKDIR/pool-final-R"
+log_must eval "$ZFS receive -dF $POOL2 < $BACKDIR/pool-final-R"
+
+log_must $ZFS destroy -Rf $POOL/$FS
+log_must $ZFS destroy -Rf $POOL/pclone
+
+if is_global_zone ; then
+ log_must $ZFS destroy -Rf $POOL/vol
+fi
+log_must $ZFS snapshot -r $POOL@destroy
+
+log_must eval "$ZFS send -R -I @final $POOL@destroy > " \
+ "$BACKDIR/pool-final-destroy-IR"
+log_must eval "$ZFS receive -dF $POOL2 < $BACKDIR/pool-final-destroy-IR"
+
+dstds=$(get_dst_ds $POOL $POOL2)
+log_must cmp_ds_subs $POOL $dstds
+log_must cmp_ds_cont $POOL $dstds
+
+log_pass "zfs receive -dF will destroy all the dataset that not exist" \
+ "on the sender side"
diff --git a/tests/sys/cddl/zfs/tests/rsend/rsend_test.sh b/tests/sys/cddl/zfs/tests/rsend/rsend_test.sh
new file mode 100755
index 000000000000..26f3d5a971b3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rsend/rsend_test.sh
@@ -0,0 +1,394 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case rsend_001_pos cleanup
+rsend_001_pos_head()
+{
+ atf_set "descr" "zfs send -R send replication stream up to the named snap."
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 2700
+}
+rsend_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/rsend.kshlib
+ . $(atf_get_srcdir)/rsend.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/rsend_001_pos.ksh || atf_fail "Testcase failed"
+}
+rsend_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/rsend.kshlib
+ . $(atf_get_srcdir)/rsend.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case rsend_002_pos cleanup
+rsend_002_pos_head()
+{
+ atf_set "descr" "zfs send -I sends all incrementals from fs@init to fs@final."
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 2700
+}
+rsend_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/rsend.kshlib
+ . $(atf_get_srcdir)/rsend.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/rsend_002_pos.ksh || atf_fail "Testcase failed"
+}
+rsend_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/rsend.kshlib
+ . $(atf_get_srcdir)/rsend.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case rsend_003_pos cleanup
+rsend_003_pos_head()
+{
+ atf_set "descr" "zfs send -I send all incrementals from dataset@init to clone@snap"
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 2700
+}
+rsend_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/rsend.kshlib
+ . $(atf_get_srcdir)/rsend.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/rsend_003_pos.ksh || atf_fail "Testcase failed"
+}
+rsend_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/rsend.kshlib
+ . $(atf_get_srcdir)/rsend.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case rsend_004_pos cleanup
+rsend_004_pos_head()
+{
+ atf_set "descr" "zfs send -R -i send incremental from fs@init to fs@final."
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 2700
+}
+rsend_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/rsend.kshlib
+ . $(atf_get_srcdir)/rsend.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/rsend_004_pos.ksh || atf_fail "Testcase failed"
+}
+rsend_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/rsend.kshlib
+ . $(atf_get_srcdir)/rsend.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case rsend_005_pos cleanup
+rsend_005_pos_head()
+{
+ atf_set "descr" "zfs send -R -I send all the incremental between @init with @final"
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 2700
+}
+rsend_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/rsend.kshlib
+ . $(atf_get_srcdir)/rsend.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/rsend_005_pos.ksh || atf_fail "Testcase failed"
+}
+rsend_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/rsend.kshlib
+ . $(atf_get_srcdir)/rsend.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case rsend_006_pos cleanup
+rsend_006_pos_head()
+{
+ atf_set "descr" "Rename snapshot name will not change the dependent order."
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 2700
+}
+rsend_006_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/rsend.kshlib
+ . $(atf_get_srcdir)/rsend.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/rsend_006_pos.ksh || atf_fail "Testcase failed"
+}
+rsend_006_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/rsend.kshlib
+ . $(atf_get_srcdir)/rsend.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case rsend_007_pos cleanup
+rsend_007_pos_head()
+{
+ atf_set "descr" "Rename parent filesystem name will not change the dependent order."
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 2700
+}
+rsend_007_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/rsend.kshlib
+ . $(atf_get_srcdir)/rsend.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/rsend_007_pos.ksh || atf_fail "Testcase failed"
+}
+rsend_007_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/rsend.kshlib
+ . $(atf_get_srcdir)/rsend.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case rsend_008_pos cleanup
+rsend_008_pos_head()
+{
+ atf_set "descr" "Changes made by 'zfs promote' can be properly received."
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 2700
+}
+rsend_008_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/rsend.kshlib
+ . $(atf_get_srcdir)/rsend.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/rsend_008_pos.ksh || atf_fail "Testcase failed"
+}
+rsend_008_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/rsend.kshlib
+ . $(atf_get_srcdir)/rsend.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case rsend_009_pos cleanup
+rsend_009_pos_head()
+{
+ atf_set "descr" "Verify zfs receive can handle out of space correctly."
+ atf_set "require.progs" "ksh93 zpool zfs"
+ atf_set "timeout" 2700
+}
+rsend_009_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/rsend.kshlib
+ . $(atf_get_srcdir)/rsend.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/rsend_009_pos.ksh || atf_fail "Testcase failed"
+}
+rsend_009_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/rsend.kshlib
+ . $(atf_get_srcdir)/rsend.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case rsend_010_pos cleanup
+rsend_010_pos_head()
+{
+ atf_set "descr" "ZFS can handle stream with multiple identical (same GUID) snapshots"
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 2700
+}
+rsend_010_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/rsend.kshlib
+ . $(atf_get_srcdir)/rsend.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/rsend_010_pos.ksh || atf_fail "Testcase failed"
+}
+rsend_010_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/rsend.kshlib
+ . $(atf_get_srcdir)/rsend.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case rsend_011_pos cleanup
+rsend_011_pos_head()
+{
+ atf_set "descr" "Verify changes made by 'zfs inherit' can be properly received."
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 2700
+}
+rsend_011_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/rsend.kshlib
+ . $(atf_get_srcdir)/rsend.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/rsend_011_pos.ksh || atf_fail "Testcase failed"
+}
+rsend_011_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/rsend.kshlib
+ . $(atf_get_srcdir)/rsend.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case rsend_012_pos cleanup
+rsend_012_pos_head()
+{
+ atf_set "descr" "Verify zfs send -R will backup all the filesystem propertiescorrectly."
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 2700
+}
+rsend_012_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/rsend.kshlib
+ . $(atf_get_srcdir)/rsend.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/rsend_012_pos.ksh || atf_fail "Testcase failed"
+}
+rsend_012_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/rsend.kshlib
+ . $(atf_get_srcdir)/rsend.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case rsend_013_pos cleanup
+rsend_013_pos_head()
+{
+ atf_set "descr" "zfs receive -dF will destroy all the dataset that not existon the sender side"
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 2700
+}
+rsend_013_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/rsend.kshlib
+ . $(atf_get_srcdir)/rsend.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/rsend_013_pos.ksh || atf_fail "Testcase failed"
+}
+rsend_013_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/rsend.kshlib
+ . $(atf_get_srcdir)/rsend.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case rsend_001_pos
+ atf_add_test_case rsend_002_pos
+ atf_add_test_case rsend_003_pos
+ atf_add_test_case rsend_004_pos
+ atf_add_test_case rsend_005_pos
+ atf_add_test_case rsend_006_pos
+ atf_add_test_case rsend_007_pos
+ atf_add_test_case rsend_008_pos
+ atf_add_test_case rsend_009_pos
+ atf_add_test_case rsend_010_pos
+ atf_add_test_case rsend_011_pos
+ atf_add_test_case rsend_012_pos
+ atf_add_test_case rsend_013_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/rsend/setup.ksh b/tests/sys/cddl/zfs/tests/rsend/setup.ksh
new file mode 100644
index 000000000000..2859bb341a28
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/rsend/setup.ksh
@@ -0,0 +1,47 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/rsend/rsend.kshlib
+
+verify_runnable "both"
+
+#
+# Check if the system support 'send -R'
+#
+$ZFS send 2>&1 | grep -w "[-R]" > /dev/null 2>&1
+if (($? != 0)); then
+ log_unsupported
+fi
+
+if is_global_zone ; then
+ log_must $ZPOOL create $POOL $DISK1
+ log_must $ZPOOL create $POOL2 $DISK2
+fi
+log_must $MKDIR $BACKDIR $TESTDIR
+
+log_must setup_test_model $POOL
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/scrub_mirror/Makefile b/tests/sys/cddl/zfs/tests/scrub_mirror/Makefile
new file mode 100644
index 000000000000..504f6210f6a8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/scrub_mirror/Makefile
@@ -0,0 +1,20 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/scrub_mirror
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= scrub_mirror_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= scrub_mirror_004_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= scrub_mirror_001_pos.ksh
+${PACKAGE}FILES+= scrub_mirror_003_pos.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= default.cfg
+${PACKAGE}FILES+= scrub_mirror_002_pos.ksh
+${PACKAGE}FILES+= scrub_mirror_common.kshlib
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/scrub_mirror/cleanup.ksh b/tests/sys/cddl/zfs/tests/scrub_mirror/cleanup.ksh
new file mode 100644
index 000000000000..19e6e178b1d0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/scrub_mirror/cleanup.ksh
@@ -0,0 +1,43 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+$DF -t zfs -h | $GREP "$TESTFS " >/dev/null
+[[ $? == 0 ]] && log_must $ZFS umount -f $TESTDIR
+destroy_pool $TESTPOOL
+
+# recreate and destroy a zpool over the disks to restore the partitions to
+# normal
+if [[ -n $SINGLE_DISK ]]; then
+ log_must cleanup_devices $MIRROR_PRIMARY
+else
+ log_must cleanup_devices $MIRROR_PRIMARY $MIRROR_SECONDARY
+fi
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/scrub_mirror/default.cfg b/tests/sys/cddl/zfs/tests/scrub_mirror/default.cfg
new file mode 100644
index 000000000000..be0db44d74cd
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/scrub_mirror/default.cfg
@@ -0,0 +1,52 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+typeset -i NUMBER_OF_DISKS=0
+for i in $DISKS; do
+ [[ -n $MIRROR_PRIMARY ]] && MIRROR_SECONDARY=$i
+ [[ -z $MIRROR_PRIMARY ]] && MIRROR_PRIMARY=$i
+done
+
+if [[ -z $MIRROR_SECONDARY ]]; then
+ # We need to repartition the single disk to two partitions
+ SINGLE_DISK=$MIRROR_PRIMARY
+ MIRROR_SECONDARY=$MIRROR_PRIMARY
+ SIDE_PRIMARY=${SINGLE_DISK}p1
+ SIDE_SECONDARY=${SINGLE_DISK}p2
+else
+ SIDE_PRIMARY=${MIRROR_PRIMARY}p1
+ SIDE_SECONDARY=${MIRROR_SECONDARY}p1
+fi
+
+export MIRROR_PRIMARY MIRROR_SECONDARY SINGLE_DISK SIDE_PRIMARY SIDE_SECONDARY
+
+export FILE_COUNT=30
+export FILE_SIZE=$(( 1024 * 1024 ))
+export MIRROR_MEGS=70
+export MIRROR_SIZE=${MIRROR_MEGS}m # default mirror size
+export BLOCKSZ=$(( 64 * 1024 ))
+export WRITE_COUNT=$(( MIRROR_MEGS * 1024 * 1024 / BLOCKSZ ))
+export TESTFILE=testfile${TESTCASE_ID}
diff --git a/tests/sys/cddl/zfs/tests/scrub_mirror/scrub_mirror_001_pos.ksh b/tests/sys/cddl/zfs/tests/scrub_mirror/scrub_mirror_001_pos.ksh
new file mode 100644
index 000000000000..de2485b94fec
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/scrub_mirror/scrub_mirror_001_pos.ksh
@@ -0,0 +1,62 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/scrub_mirror/scrub_mirror_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: scrub_mirror_001_pos
+#
+# DESCRIPTION:
+# The primary side of a zpool mirror can be zeroed without causing damage
+# to the data in the pool
+#
+# STRATEGY:
+# 1) Write several files to the ZFS filesystem mirror
+# 2) dd from /dev/zero over the primary side of the mirror
+# 3) verify that all the file contents are unchanged on the file system
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-10-20)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "The primary side of a zpool mirror may be completely wiped" \
+ "without affecting the content of the pool"
+
+overwrite_verify_mirror $TESTPOOL $SIDE_PRIMARY /dev/zero
+
+log_pass "The overwrite had no effect on the data"
diff --git a/tests/sys/cddl/zfs/tests/scrub_mirror/scrub_mirror_002_pos.ksh b/tests/sys/cddl/zfs/tests/scrub_mirror/scrub_mirror_002_pos.ksh
new file mode 100644
index 000000000000..9e09fc907fee
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/scrub_mirror/scrub_mirror_002_pos.ksh
@@ -0,0 +1,62 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/scrub_mirror/scrub_mirror_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: scrub_mirror_002_pos
+#
+# DESCRIPTION:
+# The secondary side of a zpool mirror can be zeroed without causing damage
+# to the data in the pool
+#
+# STRATEGY:
+# 1) Write several files to the ZFS filesystem in the mirrored pool
+# 2) dd from /dev/zero over the secondary side of the mirror
+# 3) verify that all the file contents are unchanged on the file system
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-10-20)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "The primary side of a zpool mirror may be completely wiped" \
+ "without affecting the content of the pool"
+
+overwrite_verify_mirror $TESTPOOL $SIDE_SECONDARY /dev/zero
+
+log_pass "The overwrite had no effect on the data"
diff --git a/tests/sys/cddl/zfs/tests/scrub_mirror/scrub_mirror_003_pos.ksh b/tests/sys/cddl/zfs/tests/scrub_mirror/scrub_mirror_003_pos.ksh
new file mode 100644
index 000000000000..64afb359b655
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/scrub_mirror/scrub_mirror_003_pos.ksh
@@ -0,0 +1,62 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/scrub_mirror/scrub_mirror_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: scrub_mirror_003_pos
+#
+# DESCRIPTION:
+# The primary side of a zpool mirror can be mangled causing without damage
+# to the data in the pool
+#
+# STRATEGY:
+# 1) Write several files to the ZFS filesystem mirror
+# 2) dd from /dev/urandom over the primary side of the mirror
+# 3) verify that all the file contents are unchanged on the file system
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-10-20)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "The primary side of a zpool mirror may be completely mangled" \
+ "without affecting the content of the pool"
+
+overwrite_verify_mirror $TESTPOOL $SIDE_PRIMARY /dev/urandom
+
+log_pass "The overwrite did not have any effect on the data"
diff --git a/tests/sys/cddl/zfs/tests/scrub_mirror/scrub_mirror_004_pos.ksh b/tests/sys/cddl/zfs/tests/scrub_mirror/scrub_mirror_004_pos.ksh
new file mode 100644
index 000000000000..709b26291e4c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/scrub_mirror/scrub_mirror_004_pos.ksh
@@ -0,0 +1,62 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/scrub_mirror/scrub_mirror_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: scrub_mirror_004_pos
+#
+# DESCRIPTION:
+# The secondary side of a zpool mirror can be mangled causing without damage
+# to the data in the pool
+#
+# STRATEGY:
+# 1) Write several files to the ZFS filesystem in the mirrored pool
+# 2) dd from /dev/urandom over the secondary side of the mirror
+# 3) verify that all the file contents are unchanged on the file system
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-10-20)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "The primary side of a zpool mirror may be completely mangled" \
+ "without affecting the content of the pool"
+
+overwrite_verify_mirror $TESTPOOL $SIDE_SECONDARY /dev/urandom
+
+log_pass "The overwrite had no effect on the data"
diff --git a/tests/sys/cddl/zfs/tests/scrub_mirror/scrub_mirror_common.kshlib b/tests/sys/cddl/zfs/tests/scrub_mirror/scrub_mirror_common.kshlib
new file mode 100644
index 000000000000..6154661586f7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/scrub_mirror/scrub_mirror_common.kshlib
@@ -0,0 +1,78 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+function overwrite_verify_mirror
+{
+ typeset POOL=$1
+ typeset AFFECTED_DEVICE=$2
+ typeset OVERWRITING_DEVICE=$3
+
+ typeset atfile=0
+ set -A files
+ set -A cksums
+ set -A newcksums
+
+ fill_fs $TESTDIR -1 $FILE_COUNT $BLOCKSZ $WRITE_COUNT
+ while [ "$atfile" -lt "$FILE_COUNT" ]; do
+ if [ -f ${TESTDIR}/0/${TESTFILE}.${atfile} ]; then
+ cksums[$atfile]=$($CKSUM ${TESTDIR}/0/${TESTFILE}.${atfile})
+ fi
+ (( atfile = $atfile + 1 ))
+ done
+
+ # unmount and export before dd
+ log_must $ZPOOL export $POOL
+
+ # dd the affected side of the mirror
+ log_must $DD if=$OVERWRITING_DEVICE of=$(bsddevmap $AFFECTED_DEVICE) \
+ seek=8 bs=$BLOCKSZ count=$(( WRITE_COUNT - 8 )) conv=notrunc
+
+ # now remount and scrub
+ log_must $ZPOOL import $POOL
+ log_must $ZPOOL scrub $POOL
+ wait_for 60 1 is_pool_scrubbed $POOL
+
+ atfile=0
+ typeset -i failedcount=0
+ while [ "$atfile" -lt "$FILE_COUNT" ]; do
+ if [ -f ${TESTDIR}/0/${TESTFILE}.${atfile} ]; then
+ newcksum=$($CKSUM $TESTDIR/0/${TESTFILE}.${atfile})
+ if [[ $newcksum != ${cksums[$atfile]} ]]; then
+ (( failedcount = $failedcount + 1 ))
+ else
+ log_note "${TESTFILE}.${atfile} checksums match:"\
+ "old ${cksums[$atfile]} new $newcksum"
+ fi
+ $RM -f ${files[$atfile]}
+ fi
+ (( atfile = $atfile + 1 ))
+ done
+
+ if [ "$failedcount" -gt 0 ]; then
+ log_fail "of the $FILE_COUNT files $failedcount did not " \
+ "have the same checksum before and after."
+ fi
+}
diff --git a/tests/sys/cddl/zfs/tests/scrub_mirror/scrub_mirror_test.sh b/tests/sys/cddl/zfs/tests/scrub_mirror/scrub_mirror_test.sh
new file mode 100755
index 000000000000..95ec6406ea6f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/scrub_mirror/scrub_mirror_test.sh
@@ -0,0 +1,138 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case scrub_mirror_001_pos cleanup
+scrub_mirror_001_pos_head()
+{
+ atf_set "descr" "The primary side of a zpool mirror may be completely wipedwithout affecting the content of the pool"
+ atf_set "require.progs" "ksh93 zfs"
+}
+scrub_mirror_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/scrub_mirror_common.kshlib
+ . $(atf_get_srcdir)/default.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/scrub_mirror_001_pos.ksh || atf_fail "Testcase failed"
+}
+scrub_mirror_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/scrub_mirror_common.kshlib
+ . $(atf_get_srcdir)/default.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case scrub_mirror_002_pos cleanup
+scrub_mirror_002_pos_head()
+{
+ atf_set "descr" "The primary side of a zpool mirror may be completely wipedwithout affecting the content of the pool"
+ atf_set "require.progs" "ksh93 zfs"
+}
+scrub_mirror_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/scrub_mirror_common.kshlib
+ . $(atf_get_srcdir)/default.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/scrub_mirror_002_pos.ksh || atf_fail "Testcase failed"
+}
+scrub_mirror_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/scrub_mirror_common.kshlib
+ . $(atf_get_srcdir)/default.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case scrub_mirror_003_pos cleanup
+scrub_mirror_003_pos_head()
+{
+ atf_set "descr" "The primary side of a zpool mirror may be completely mangledwithout affecting the content of the pool"
+ atf_set "require.progs" "ksh93 zfs"
+}
+scrub_mirror_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/scrub_mirror_common.kshlib
+ . $(atf_get_srcdir)/default.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/scrub_mirror_003_pos.ksh || atf_fail "Testcase failed"
+}
+scrub_mirror_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/scrub_mirror_common.kshlib
+ . $(atf_get_srcdir)/default.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case scrub_mirror_004_pos cleanup
+scrub_mirror_004_pos_head()
+{
+ atf_set "descr" "The primary side of a zpool mirror may be completely mangledwithout affecting the content of the pool"
+ atf_set "require.progs" "ksh93 zfs"
+}
+scrub_mirror_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/scrub_mirror_common.kshlib
+ . $(atf_get_srcdir)/default.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/scrub_mirror_004_pos.ksh || atf_fail "Testcase failed"
+}
+scrub_mirror_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/scrub_mirror_common.kshlib
+ . $(atf_get_srcdir)/default.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case scrub_mirror_001_pos
+ atf_add_test_case scrub_mirror_002_pos
+ atf_add_test_case scrub_mirror_003_pos
+ atf_add_test_case scrub_mirror_004_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/scrub_mirror/setup.ksh b/tests/sys/cddl/zfs/tests/scrub_mirror/setup.ksh
new file mode 100644
index 000000000000..f45fec424e18
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/scrub_mirror/setup.ksh
@@ -0,0 +1,42 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+if [[ -n $SINGLE_DISK ]]; then
+ log_note "Partitioning a single disk ($SINGLE_DISK)"
+else
+ log_note "Partitioning disks ($MIRROR_PRIMARY $MIRROR_SECONDARY)"
+fi
+wipe_partition_table $SINGLE_DISK $MIRROR_PRIMARY $MIRROR_SECONDARY
+log_must set_partition ${SIDE_PRIMARY##*p} "" $MIRROR_SIZE $MIRROR_PRIMARY
+log_must set_partition ${SIDE_SECONDARY##*p} "" $MIRROR_SIZE $MIRROR_SECONDARY
+
+default_mirror_setup $SIDE_PRIMARY $SIDE_SECONDARY
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/slog/Makefile b/tests/sys/cddl/zfs/tests/slog/Makefile
new file mode 100644
index 000000000000..0cdbca1395f3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/slog/Makefile
@@ -0,0 +1,30 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/slog
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= slog_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= slog_012_neg.ksh
+${PACKAGE}FILES+= slog_013_pos.ksh
+${PACKAGE}FILES+= slog_002_pos.ksh
+${PACKAGE}FILES+= slog_006_pos.ksh
+${PACKAGE}FILES+= slog.kshlib
+${PACKAGE}FILES+= slog_003_pos.ksh
+${PACKAGE}FILES+= slog_007_pos.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= slog.cfg
+${PACKAGE}FILES+= slog_001_pos.ksh
+${PACKAGE}FILES+= slog_005_pos.ksh
+${PACKAGE}FILES+= slog_008_neg.ksh
+${PACKAGE}FILES+= slog_011_neg.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= slog_014_pos.ksh
+${PACKAGE}FILES+= slog_010_neg.ksh
+${PACKAGE}FILES+= slog_004_pos.ksh
+${PACKAGE}FILES+= slog_009_neg.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/slog/cleanup.ksh b/tests/sys/cddl/zfs/tests/slog/cleanup.ksh
new file mode 100644
index 000000000000..4ff2e0f3840f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/slog/cleanup.ksh
@@ -0,0 +1,44 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+if datasetexists $TESTPOOL ; then
+ log_must $ZPOOL destroy -f $TESTPOOL
+fi
+if datasetexists $TESTPOOL2 ; then
+ log_must $ZPOOL destroy -f $TESTPOOL2
+fi
+if [[ -d $VDIR ]]; then
+ log_must $RM -rf $VDIR
+fi
+if [[ -d $VDIR2 ]]; then
+ log_must $RM -rf $VDIR2
+fi
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/slog/setup.ksh b/tests/sys/cddl/zfs/tests/slog/setup.ksh
new file mode 100644
index 000000000000..ec07952bbfa0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/slog/setup.ksh
@@ -0,0 +1,40 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+if [[ -d $VDEV ]]; then
+ log_must $RM -rf $VDIR
+fi
+if [[ -d $VDEV2 ]]; then
+ log_must $RM -rf $VDIR2
+fi
+log_must $MKDIR -p $VDIR $VDIR2
+log_must create_vdevs $VDEV $SDEV $LDEV $VDEV2 $SDEV2 $LDEV2
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/slog/slog.cfg b/tests/sys/cddl/zfs/tests/slog/slog.cfg
new file mode 100644
index 000000000000..f97a5cbbd50a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/slog/slog.cfg
@@ -0,0 +1,39 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export SIZE=64M
+
+export VDIR=$TMPDIR/disk.${TESTCASE_ID}
+export VDIR2=$TMPDIR/disk2.${TESTCASE_ID}
+
+export VDEV="$VDIR/a $VDIR/b $VDIR/c"
+export SDEV="$VDIR/d"
+export LDEV="$VDIR/e $VDIR/f"
+export VDEV2="$VDIR2/a $VDIR2/b $VDIR2/c"
+export SDEV2="$VDIR2/d"
+export LDEV2="$VDIR2/e $VDIR2/f"
+
+export STF_TIMEOUT=1200
diff --git a/tests/sys/cddl/zfs/tests/slog/slog.kshlib b/tests/sys/cddl/zfs/tests/slog/slog.kshlib
new file mode 100644
index 000000000000..5fe2502b888a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/slog/slog.kshlib
@@ -0,0 +1,176 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+function cleanup
+{
+ poolexists $TESTPOOL && log_must $ZPOOL status $TESTPOOL
+ poolexists $TESTPOOL2 && log_must $ZPOOL status $TESTPOOL2
+ destroy_pool $TESTPOOL
+ destroy_pool $TESTPOOL2
+}
+
+#
+# Try zpool status/iostat for given pool
+#
+# $1 pool
+#
+function display_status
+{
+ typeset pool=$1
+
+ typeset -i ret=0
+ $ZPOOL status -xv $pool > /dev/null 2>&1
+ ret=$?
+
+ $ZPOOL iostat > /dev/null 2>&1
+ ((ret |= $?))
+
+ typeset mntpnt=$(get_prop mountpoint $pool)
+ $DD if=/dev/random of=$mntpnt/testfile.${TESTCASE_ID} &
+ typeset pid=$!
+
+ $ZPOOL iostat -v 1 3 > /dev/null
+ ((ret |= $?))
+
+ kill -9 $pid
+
+ return $ret
+}
+
+function slog_devstat_table
+{
+ typeset pool=$1
+
+ $ZPOOL status -v $pool | $NAWK '
+ BEGIN { start = 0; }
+ /\tlogs/ { start = 1; }
+ (start == 0) { next; }
+ /\t (\/|[a-zA-Z])/ { print "stripe:" $1 " " $2; }
+ /\t (\/|[a-zA-Z])/ { print "mirror:" $1 " " $2; }
+ /\t (\/|[0-9])/ {print "stripe:" $NF " " $2}
+ /\t (\/|[0-9])/ {print "mirror:" $NF " " $2}
+ # When a hotspare is replacing
+ /\t (\/|[a-zA-Z])/ {print "mirror:" $1 " " $2}
+ '
+}
+
+#
+# Verify the given slog device have correct type and status
+#
+# $1 pool name
+# $2 device name
+# $3 device status
+# $4 device type
+#
+function verify_slog_device
+{
+ typeset pool=$1
+ typeset device=$2
+ typeset status=$3
+ typeset type=$4
+
+ if [[ -z $pool || -z $device || -z $status ]]; then
+ log_fail "Usage: verify_slog_device <pool> <device> " \
+ "<status> [type]"
+ fi
+
+ if [[ $WRAPPER == *"smi"* ]]; then
+ $ECHO $device | $EGREP "^c[0-F]+([td][0-F]+)+$" > /dev/null 2>&1
+ if (( $? == 0 )); then
+ device=${device}s2
+ fi
+ fi
+
+ #
+ # Get all the slog devices and status table like below
+ #
+ # mirror:/disks/d ONLINE mirror:/disks/e ONLINE stripe:/disks/f ONLINE
+ #
+ set -A dev_stat_tab $(slog_devstat_table $pool)
+
+ typeset find=0
+ for (( i = 0; i < ${#dev_stat_tab[@]}; i += 2 )); do
+ typeset dev=${dev_stat_tab[$i]}
+ typeset stat=${dev_stat_tab[((i+1))]}
+
+ typeset statmsg="$dev: Status($stat) != Expected stat($status)"
+ case $dev in
+ stripe:$device)
+ if [[ "$type" == 'mirror' ]]; then
+ log_note "Unexpected type: mirror"
+ return 1
+ fi
+ if [[ $stat != $status ]]; then
+ log_note statmsg
+ return 1
+ fi
+ return 0
+ ;;
+ mirror:$device)
+ if [[ -z "$type" || $type == 'stripe' ]]; then
+ log_note "Unexpected type: stripe"
+ return 1
+ fi
+ if [[ $stat != $status ]]; then
+ log_note statmsg
+ return 1
+ fi
+ return 0
+ ;;
+ esac
+ done
+ return 1
+}
+
+# Calls <callback> [args...] <pooltype> <sparetype>.
+function slog_foreach_nologtype # <callback>
+{
+ typeset callback="$1"
+
+ for pooltype in "" "mirror" "raidz" "raidz2"; do
+ for sparetype in "" "spare"; do
+ $callback "$pooltype" "$sparetype"
+ done
+ done
+}
+
+# Calls <callback> [args...] <pooltype> <sparetype> <logtype>.
+# Unfortunately, this has to be duplicated because some arguments are empty,
+# so if they aren't explicitly forwarded they aren't arguments to $callback.
+function slog_foreach_all # <callback>
+{
+ typeset callback="$1"
+
+ for pooltype in "" "mirror" "raidz" "raidz2"; do
+ for sparetype in "" "spare"; do
+ for logtype in "" "mirror"; do
+ $callback "$pooltype" "$sparetype" "$logtype"
+ done
+ done
+ done
+}
diff --git a/tests/sys/cddl/zfs/tests/slog/slog_001_pos.ksh b/tests/sys/cddl/zfs/tests/slog/slog_001_pos.ksh
new file mode 100644
index 000000000000..bd2af1487d9b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/slog/slog_001_pos.ksh
@@ -0,0 +1,73 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/slog/slog.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: slog_001_pos
+#
+# DESCRIPTION:
+# Creating a pool with a log device succeeds.
+#
+# STRATEGY:
+# 1. Create pool with separated log devices.
+# 2. Display pool status
+# 3. Destroy and loop to create pool with different configuration.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-13)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Creating a pool with a log device succeeds."
+log_onexit cleanup
+
+function test_creating_with_slog # <pooltype> <sparetype> <logtype>
+{
+ typeset pooltype=$1
+ typeset sparetype=$2
+ typeset logtype=$3
+
+ create_pool $TESTPOOL $pooltype $VDEV $sparetype $SDEV log $logtype $LDEV
+ log_must display_status $TESTPOOL
+ ldev=$(random_get $LDEV)
+ log_must verify_slog_device $TESTPOOL $ldev ONLINE $logtype
+ destroy_pool $TESTPOOL
+}
+
+slog_foreach_all test_creating_with_slog
+
+log_pass "Creating a pool with a log device succeeds."
diff --git a/tests/sys/cddl/zfs/tests/slog/slog_002_pos.ksh b/tests/sys/cddl/zfs/tests/slog/slog_002_pos.ksh
new file mode 100644
index 000000000000..a96e2c55f435
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/slog/slog_002_pos.ksh
@@ -0,0 +1,71 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/slog/slog.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: slog_002_pos
+#
+# DESCRIPTION:
+# Adding a log device to normal pool works.
+#
+# STRATEGY:
+# 1. Create pool
+# 2. Add log devices with different configuration
+# 3. Display pool status
+# 4. Destroy and loop to create pool with different configuration.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-13)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Adding a log device to normal pool works."
+log_onexit cleanup
+
+function test_adding_slog # <pooltype> <sparetype> <logtype>
+{
+ create_pool $TESTPOOL $pooltype $VDEV $sparetype $SDEV
+ log_must $ZPOOL add $TESTPOOL log $logtype $LDEV
+ log_must display_status $TESTPOOL
+ typeset ldev=$(random_get $LDEV)
+ log_must verify_slog_device $TESTPOOL $ldev ONLINE $logtype
+ destroy_pool $TESTPOOL
+}
+
+slog_foreach_all test_adding_slog
+
+log_pass "Adding a log device to normal pool works."
diff --git a/tests/sys/cddl/zfs/tests/slog/slog_003_pos.ksh b/tests/sys/cddl/zfs/tests/slog/slog_003_pos.ksh
new file mode 100644
index 000000000000..65ab00079ff4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/slog/slog_003_pos.ksh
@@ -0,0 +1,78 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/slog/slog.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: slog_003_pos
+#
+# DESCRIPTION:
+# Adding an extra log device works
+#
+# STRATEGY:
+# 1. Create pool with separated log devices.
+# 2. Add an extra log devices
+# 3. Display pool status
+# 4. Destroy and loop to create pool with different configuration.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-13)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Adding an extra log device works."
+log_onexit cleanup
+
+function test_adding_extra_slog # <pooltype> <sparetype> <logtype>
+{
+ typeset pooltype=$1
+ typeset sparetype=$2
+ typeset logtype=$3
+
+ for newtype in "" "mirror"; do
+ create_pool $TESTPOOL $pooltype $VDEV $sparetype $SDEV \
+ log $logtype $LDEV
+ log_must $ZPOOL add $TESTPOOL log $newtype $LDEV2
+ log_must display_status $TESTPOOL
+ ldev=$(random_get $LDEV2)
+ log_must verify_slog_device $TESTPOOL $ldev ONLINE $newtype
+ destroy_pool $TESTPOOL
+ done
+}
+
+slog_foreach_all test_adding_extra_slog
+
+log_pass "Adding an extra log device works."
diff --git a/tests/sys/cddl/zfs/tests/slog/slog_004_pos.ksh b/tests/sys/cddl/zfs/tests/slog/slog_004_pos.ksh
new file mode 100644
index 000000000000..e1488f06424e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/slog/slog_004_pos.ksh
@@ -0,0 +1,77 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/slog/slog.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: slog_004_pos
+#
+# DESCRIPTION:
+# Attaching a log device passes.
+#
+# STRATEGY:
+# 1. Create pool with separated log devices.
+# 2. Attaching a log device for existing log device
+# 3. Display pool status
+# 4. Destroy and loop to create pool with different configuration.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-13)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Attaching a log device passes."
+log_onexit cleanup
+
+function test_attaching_slog # <pooltype> <sparetype> <logtype>
+{
+ typeset pooltype=$1
+ typeset sparetype=$2
+ typeset logtype=$3
+
+ create_pool $TESTPOOL $pooltype $VDEV $sparetype $SDEV \
+ log $logtype $LDEV
+ typeset ldev=$(random_get $LDEV)
+ typeset ldev2=$(random_get $LDEV2)
+ log_must $ZPOOL attach $TESTPOOL $ldev $ldev2
+ log_must display_status $TESTPOOL
+ log_must verify_slog_device $TESTPOOL $ldev ONLINE mirror
+ log_must verify_slog_device $TESTPOOL $ldev2 ONLINE mirror
+ destroy_pool $TESTPOOL
+}
+slog_foreach_all test_attaching_slog
+
+log_pass "Attaching a log device passes."
diff --git a/tests/sys/cddl/zfs/tests/slog/slog_005_pos.ksh b/tests/sys/cddl/zfs/tests/slog/slog_005_pos.ksh
new file mode 100644
index 000000000000..872fc5192b96
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/slog/slog_005_pos.ksh
@@ -0,0 +1,75 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/slog/slog.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: slog_005_pos
+#
+# DESCRIPTION:
+# Detaching a log device passes.
+#
+# STRATEGY:
+# 1. Create pool with mirror log devices.
+# 2. Detaching a log device
+# 3. Display pool status
+# 4. Destroy and loop to create pool with different configuration.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-13)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Detaching a log device passes."
+log_onexit cleanup
+
+function test_detaching_slog # <pooltype> <sparetype>
+{
+ typeset pooltype="$1"
+ typeset sparetype="$2"
+
+ log_note "test_detaching_slog args: $* -EOA-"
+ create_pool $TESTPOOL $pooltype $VDEV $sparetype $SDEV \
+ log mirror $LDEV mirror $LDEV2
+ ldev=$(random_get $LDEV $LDEV2)
+ log_must $ZPOOL detach $TESTPOOL $ldev
+ log_must display_status $TESTPOOL
+ log_mustnot verify_slog_device $TESTPOOL $ldev ONLINE mirror
+ destroy_pool $TESTPOOL
+}
+slog_foreach_nologtype test_detaching_slog
+
+log_pass "Detaching a log device passes."
diff --git a/tests/sys/cddl/zfs/tests/slog/slog_006_pos.ksh b/tests/sys/cddl/zfs/tests/slog/slog_006_pos.ksh
new file mode 100644
index 000000000000..dcf8c20cce57
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/slog/slog_006_pos.ksh
@@ -0,0 +1,75 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/slog/slog.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: slog_006_pos
+#
+# DESCRIPTION:
+# Replacing a log device passes.
+#
+# STRATEGY:
+# 1. Create pool with log devices.
+# 2. Replacing one log device
+# 3. Display pool status
+# 4. Destroy and loop to create pool with different configuration.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-13)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Replacing a log device passes."
+log_onexit cleanup
+
+function test_slog_replacing # <pooltype> <sparetype> <logtype>
+{
+ typeset pooltype=$1
+ typeset sparetype=$2
+ typeset logtype=$3
+
+ create_pool $TESTPOOL $pooltype $VDEV $sparetype $SDEV log $logtype $LDEV
+ sdev=$(random_get $LDEV)
+ tdev=$(random_get $LDEV2)
+ log_must $ZPOOL replace $TESTPOOL $sdev $tdev
+ log_must display_status $TESTPOOL
+ wait_for 15 1 verify_slog_device $TESTPOOL $tdev ONLINE $logtype
+ destroy_pool $TESTPOOL
+}
+slog_foreach_all test_slog_replacing
+
+log_pass "Replacing a log device passes."
diff --git a/tests/sys/cddl/zfs/tests/slog/slog_007_pos.ksh b/tests/sys/cddl/zfs/tests/slog/slog_007_pos.ksh
new file mode 100644
index 000000000000..3dbe95589b4c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/slog/slog_007_pos.ksh
@@ -0,0 +1,86 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/slog/slog.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: slog_007_pos
+#
+# DESCRIPTION:
+# Exporting and importing pool with log devices passes.
+#
+# STRATEGY:
+# 1. Create pool with log devices.
+# 2. Export and import the pool
+# 3. Display pool status
+# 4. Destroy and import the pool again
+# 5. Display pool status
+# 6. Destroy and loop to create pool with different configuration.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-13)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Exporting and importing pool with log devices passes."
+log_onexit cleanup
+
+function test_slog_exporting_importing # <pooltype> <sparetype> <logtype>
+{
+ typeset pooltype=$1
+ typeset sparetype=$2
+ typeset logtype=$3
+
+ create_pool $TESTPOOL $pooltype $VDEV $sparetype $SDEV \
+ log $logtype $LDEV $LDEV2
+ ldev=$(random_get $LDEV $LDEV2)
+ log_must verify_slog_device $TESTPOOL $ldev ONLINE $logtype
+ log_must $ZPOOL export $TESTPOOL
+ log_must $ZPOOL import -d $VDIR -d $VDIR2 $TESTPOOL
+ log_must display_status $TESTPOOL
+ ldev=$(random_get $LDEV $LDEV2)
+ log_must verify_slog_device $TESTPOOL $ldev ONLINE $logtype
+
+ destroy_pool $TESTPOOL
+ log_must $ZPOOL import -Df -d $VDIR -d $VDIR2 $TESTPOOL
+ log_must display_status $TESTPOOL
+ ldev=$(random_get $LDEV $LDEV2)
+ log_must verify_slog_device $TESTPOOL $ldev ONLINE $logtype
+ destroy_pool $TESTPOOL
+}
+slog_foreach_all test_slog_exporting_importing
+
+log_pass "Exporting and importing pool with log devices passes."
diff --git a/tests/sys/cddl/zfs/tests/slog/slog_008_neg.ksh b/tests/sys/cddl/zfs/tests/slog/slog_008_neg.ksh
new file mode 100644
index 000000000000..8202a29a0c5e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/slog/slog_008_neg.ksh
@@ -0,0 +1,72 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/slog/slog.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: slog_008_neg
+#
+# DESCRIPTION:
+# A raidz/raidz2 log is not supported.
+#
+# STRATEGY:
+# 1. Try to create pool with unsupported type
+# 2. Verify failed to create pool.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-13)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "A raidz/raidz2 log is not supported."
+log_onexit cleanup
+
+function test_no_raidz_slog # <pooltype> <sparetype>
+{
+ typeset pooltype=$1
+ typeset sparetype=$2
+
+ for logtype in "raidz" "raidz1" "raidz2"; do
+ log_mustnot $ZPOOL create $TESTPOOL $type $VDEV \
+ $spare $SDEV log $logtype $LDEV $LDEV2
+ ldev=$(random_get $LDEV $LDEV2)
+ log_mustnot verify_slog_device $TESTPOOL $ldev ONLINE $logtype
+ log_must datasetnonexists $TESTPOOL
+ done
+}
+slog_foreach_nologtype test_no_raidz_slog
+
+log_pass "A raidz/raidz2 log is not supported."
diff --git a/tests/sys/cddl/zfs/tests/slog/slog_009_neg.ksh b/tests/sys/cddl/zfs/tests/slog/slog_009_neg.ksh
new file mode 100644
index 000000000000..70aa3854269f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/slog/slog_009_neg.ksh
@@ -0,0 +1,73 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/slog/slog.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: slog_009_neg
+#
+# DESCRIPTION:
+# A raidz/raidz2 log can not be added to existed pool.
+#
+# STRATEGY:
+# 1. Create pool with or without log.
+# 2. Add a raidz/raidz2 log to this pool.
+# 3. Verify failed to add.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-20)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "A raidz/raidz2 log can not be added to existed pool."
+log_onexit cleanup
+
+function test_no_raidz_slog_add # <pooltype> <sparetype> <logtype>
+{
+ typeset pooltype=$1
+ typeset sparetype=$2
+ typeset logtype=$3
+
+ log=$(random_get_with_non "log")
+ create_pool $TESTPOOL $pooltype $VDEV $sparetype $SDEV $logtype $LDEV
+
+ log_mustnot $ZPOOL add $TESTPOOL log $logtype $LDEV2
+ ldev=$(random_get $LDEV2)
+ log_mustnot verify_slog_device $TESTPOOL $ldev 'ONLINE' $logtype
+ destroy_pool $TESTPOOL
+}
+
+log_pass "A raidz/raidz2 log can not be added to existed pool."
diff --git a/tests/sys/cddl/zfs/tests/slog/slog_010_neg.ksh b/tests/sys/cddl/zfs/tests/slog/slog_010_neg.ksh
new file mode 100644
index 000000000000..c4c1305f4237
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/slog/slog_010_neg.ksh
@@ -0,0 +1,73 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/slog/slog.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: slog_010_neg
+#
+# DESCRIPTION:
+# Slog device can not be replaced with spare device
+#
+# STRATEGY:
+# 1. Create a pool with hotspare and log devices.
+# 2. Verify slog device can not be replaced with hotspare device.
+# 3. Create pool2 with hotspare
+# 4. Verify slog device can not be replaced with hotspare device in pool2.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-20)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Slog device can not be replaced with spare device."
+log_onexit cleanup
+
+log_must $ZPOOL create $TESTPOOL $VDEV spare $SDEV log $LDEV
+sdev=$(random_get $SDEV)
+ldev=$(random_get $LDEV)
+log_mustnot $ZPOOL replace $TESTPOOL $ldev $sdev
+log_mustnot verify_slog_device $TESTPOOL $sdev 'ONLINE'
+log_must verify_slog_device $TESTPOOL $ldev 'ONLINE'
+
+log_must $ZPOOL create $TESTPOOL2 $VDEV2 spare $SDEV2
+sdev2=$(random_get $SDEV2)
+log_mustnot $ZPOOL replace $TESTPOOL $ldev $sdev2
+log_mustnot $ZPOOL replace -f $TESTPOOL $ldev $sdev2
+log_mustnot verify_slog_device $TESTPOOL $sdev2 'ONLINE'
+log_must verify_slog_device $TESTPOOL $ldev 'ONLINE'
+
+log_pass "Slog device can not be replaced with spare device."
diff --git a/tests/sys/cddl/zfs/tests/slog/slog_011_neg.ksh b/tests/sys/cddl/zfs/tests/slog/slog_011_neg.ksh
new file mode 100644
index 000000000000..cf7b4caf625b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/slog/slog_011_neg.ksh
@@ -0,0 +1,77 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/slog/slog.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: slog_011_neg
+#
+# DESCRIPTION:
+# Offline and online a log device passes.
+#
+# STRATEGY:
+# 1. Create pool with mirror log devices.
+# 2. Offine and online a log device
+# 3. Display pool status
+# 4. Destroy and loop to create pool with different configuration.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-20)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Offline and online a log device passes."
+log_onexit cleanup
+
+function test_slog_online_offline # <pooltype> <sparetype>
+{
+ create_pool $TESTPOOL $type $VDEV $spare $SDEV \
+ log mirror $LDEV mirror $LDEV2
+
+ ldev=$(random_get $LDEV $LDEV2)
+ log_must $ZPOOL offline $TESTPOOL $ldev
+ log_must display_status $TESTPOOL
+ log_must verify_slog_device $TESTPOOL $ldev OFFLINE mirror
+
+ log_must $ZPOOL online $TESTPOOL $ldev
+ log_must display_status $TESTPOOL
+ log_must verify_slog_device $TESTPOOL $ldev ONLINE mirror
+
+ destroy_pool $TESTPOOL
+}
+slog_foreach_nologtype test_slog_online_offline
+
+log_pass "Offline and online a log device passes."
diff --git a/tests/sys/cddl/zfs/tests/slog/slog_012_neg.ksh b/tests/sys/cddl/zfs/tests/slog/slog_012_neg.ksh
new file mode 100644
index 000000000000..9ee2dcea160e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/slog/slog_012_neg.ksh
@@ -0,0 +1,84 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/slog/slog.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: slog_012_neg
+#
+# DESCRIPTION:
+# Pool can survive when one of mirror log device get corrupted
+#
+# STRATEGY:
+# 1. Create pool with mirror slog devices
+# 2. Make corrupted on one disk
+# 3. Verify the pool is fine
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-20)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Pool can survive when one of mirror log device get corrupted."
+log_onexit cleanup
+
+function test_slog_mirror_corruption # <pooltype> <sparetype>
+{
+ typeset pooltype=$1
+ typeset sparetype=$2
+
+ create_pool $TESTPOOL $type $VDEV $spare $SDEV log mirror $LDEV
+
+ mntpnt=$(get_prop mountpoint $TESTPOOL)
+ #
+ # Create file in pool to trigger writing in slog devices
+ #
+ log_must $DD if=/dev/urandom of=$mntpnt/testfile.${TESTCASE_ID} count=100
+
+ ldev=$(random_get $LDEV)
+ eval `$STAT -s $ldev`
+ log_must $TRUNCATE -s0 $ldev
+ log_must $TRUNCATE -s $st_size $ldev
+ log_must $ZPOOL scrub $TESTPOOL
+
+ log_must display_status $TESTPOOL
+ log_must verify_slog_device $TESTPOOL $ldev UNAVAIL mirror
+
+ destroy_pool $TESTPOOL
+}
+slog_foreach_nologtype test_slog_mirror_corruption
+
+log_pass "Pool can survive when one of mirror log device get corrupted."
diff --git a/tests/sys/cddl/zfs/tests/slog/slog_013_pos.ksh b/tests/sys/cddl/zfs/tests/slog/slog_013_pos.ksh
new file mode 100644
index 000000000000..a5426a0db992
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/slog/slog_013_pos.ksh
@@ -0,0 +1,102 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/slog/slog.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: slog_013_pos
+#
+# DESCRIPTION:
+# Verify slog device can be disk, file, lofi device or any device that
+# presents a block interface.
+#
+# STRATEGY:
+# 1. Create a pool
+# 2. Loop to add different object as slog
+# 3. Verify it passes
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-06-20)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup_testenv
+{
+ cleanup
+ if datasetexists $TESTPOOL2 ; then
+ log_must $ZPOOL destroy -f $TESTPOOL2
+ fi
+ if [[ -n $lofidev ]]; then
+ $LOFIADM -d $lofidev
+ fi
+}
+
+log_assert "Verify slog device can be disk, file, lofi device or any device " \
+ "that presents a block interface."
+log_onexit cleanup_testenv
+
+dsk1=${DISKS%% *}
+log_must $ZPOOL create $TESTPOOL ${DISKS#$dsk1}
+
+# Add nomal disk
+log_must $ZPOOL add $TESTPOOL log $dsk1
+log_must verify_slog_device $TESTPOOL $dsk1 'ONLINE'
+# Add nomal file
+log_must $ZPOOL add $TESTPOOL log $LDEV
+ldev=$(random_get $LDEV)
+log_must verify_slog_device $TESTPOOL $ldev 'ONLINE'
+
+# Add lofi device
+lofidev=${LDEV2%% *}
+log_must $LOFIADM -a $lofidev
+lofidev=$($LOFIADM $lofidev)
+log_must $ZPOOL add $TESTPOOL log $lofidev
+log_must verify_slog_device $TESTPOOL $lofidev 'ONLINE'
+
+log_pass "Verify slog device can be disk, file, lofi device or any device " \
+ "that presents a block interface."
+
+# Temp disable fore bug 6569095
+# Add file which reside in the itself
+mntpnt=$(get_prop mountpoint $TESTPOOL)
+log_must create_vdevs $mntpnt/vdev
+log_must $ZPOOL add $TESTPOOL $mntpnt/vdev
+
+# Temp disable fore bug 6569072
+# Add ZFS volume
+vol=$TESTPOOL/vol
+log_must $ZPOOL create -V 64M $vol
+log_must $ZPOOL add $TESTPOOL /dev/zvol/$vol
diff --git a/tests/sys/cddl/zfs/tests/slog/slog_014_pos.ksh b/tests/sys/cddl/zfs/tests/slog/slog_014_pos.ksh
new file mode 100644
index 000000000000..2f147ea28ac4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/slog/slog_014_pos.ksh
@@ -0,0 +1,88 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/slog/slog.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: slog_014_pos
+#
+# DESCRIPTION:
+# log device can survive when one of pool device get corrupted
+#
+# STRATEGY:
+# 1. Create pool with slog devices
+# 2. remove one disk
+# 3. Verify the log is fine
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-05-26)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "log device can survive when one of the pool device get corrupted."
+log_onexit cleanup
+
+function test_slog_survives_pool_corruption # <pooltype> <sparetype>
+{
+ typeset pooltype=$1
+ typeset sparetype=$2
+
+ # This only works for pools that have redundancy
+ [ -z "$pooltype" ] && return
+
+ create_pool $TESTPOOL $pooltype $VDEV $sparetype $SDEV log $LDEV
+
+ # Remove one of the pool device, then scrub to make the pool DEGRADED
+ log_must $RM -f $VDIR/a
+ log_must $ZPOOL scrub $TESTPOOL
+
+ # Check and verify pool status
+ log_must display_status $TESTPOOL
+ log_must $ZPOOL status $TESTPOOL 2>&1 >/dev/null
+
+ # Check that there is some status: field informing us of a
+ # problem. The exact error string is unspecified.
+ log_must $ZPOOL status -v $TESTPOOL | $GREP "status:"
+ for l in $LDEV; do
+ log_must check_state $TESTPOOL $l ONLINE
+ done
+
+ destroy_pool $TESTPOOL
+ create_vdevs $VDIR/a
+}
+slog_foreach_nologtype test_slog_survives_pool_corruption
+
+log_pass "log device can survive when one of the pool device get corrupted."
diff --git a/tests/sys/cddl/zfs/tests/slog/slog_test.sh b/tests/sys/cddl/zfs/tests/slog/slog_test.sh
new file mode 100755
index 000000000000..692a5e16c91c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/slog/slog_test.sh
@@ -0,0 +1,409 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case slog_001_pos cleanup
+slog_001_pos_head()
+{
+ atf_set "descr" "Creating a pool with a log device succeeds."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1200
+}
+slog_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/slog.kshlib
+ . $(atf_get_srcdir)/slog.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/slog_001_pos.ksh || atf_fail "Testcase failed"
+}
+slog_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/slog.kshlib
+ . $(atf_get_srcdir)/slog.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case slog_002_pos cleanup
+slog_002_pos_head()
+{
+ atf_set "descr" "Adding a log device to normal pool works."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1200
+}
+slog_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/slog.kshlib
+ . $(atf_get_srcdir)/slog.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/slog_002_pos.ksh || atf_fail "Testcase failed"
+}
+slog_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/slog.kshlib
+ . $(atf_get_srcdir)/slog.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case slog_003_pos cleanup
+slog_003_pos_head()
+{
+ atf_set "descr" "Adding an extra log device works."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1200
+}
+slog_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/slog.kshlib
+ . $(atf_get_srcdir)/slog.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/slog_003_pos.ksh || atf_fail "Testcase failed"
+}
+slog_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/slog.kshlib
+ . $(atf_get_srcdir)/slog.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case slog_004_pos cleanup
+slog_004_pos_head()
+{
+ atf_set "descr" "Attaching a log device passes."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1200
+}
+slog_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/slog.kshlib
+ . $(atf_get_srcdir)/slog.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/slog_004_pos.ksh || atf_fail "Testcase failed"
+}
+slog_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/slog.kshlib
+ . $(atf_get_srcdir)/slog.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case slog_005_pos cleanup
+slog_005_pos_head()
+{
+ atf_set "descr" "Detaching a log device passes."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1200
+}
+slog_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/slog.kshlib
+ . $(atf_get_srcdir)/slog.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/slog_005_pos.ksh || atf_fail "Testcase failed"
+}
+slog_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/slog.kshlib
+ . $(atf_get_srcdir)/slog.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case slog_006_pos cleanup
+slog_006_pos_head()
+{
+ atf_set "descr" "Replacing a log device passes."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1200
+}
+slog_006_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/slog.kshlib
+ . $(atf_get_srcdir)/slog.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/slog_006_pos.ksh || atf_fail "Testcase failed"
+}
+slog_006_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/slog.kshlib
+ . $(atf_get_srcdir)/slog.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case slog_007_pos cleanup
+slog_007_pos_head()
+{
+ atf_set "descr" "Exporting and importing pool with log devices passes."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1200
+}
+slog_007_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/slog.kshlib
+ . $(atf_get_srcdir)/slog.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/slog_007_pos.ksh || atf_fail "Testcase failed"
+}
+slog_007_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/slog.kshlib
+ . $(atf_get_srcdir)/slog.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case slog_008_neg cleanup
+slog_008_neg_head()
+{
+ atf_set "descr" "A raidz/raidz2 log is not supported."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1200
+}
+slog_008_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/slog.kshlib
+ . $(atf_get_srcdir)/slog.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/slog_008_neg.ksh || atf_fail "Testcase failed"
+}
+slog_008_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/slog.kshlib
+ . $(atf_get_srcdir)/slog.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case slog_009_neg cleanup
+slog_009_neg_head()
+{
+ atf_set "descr" "A raidz/raidz2 log can not be added to existed pool."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1200
+}
+slog_009_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/slog.kshlib
+ . $(atf_get_srcdir)/slog.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/slog_009_neg.ksh || atf_fail "Testcase failed"
+}
+slog_009_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/slog.kshlib
+ . $(atf_get_srcdir)/slog.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case slog_010_neg cleanup
+slog_010_neg_head()
+{
+ atf_set "descr" "Slog device can not be replaced with spare device."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1200
+}
+slog_010_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/slog.kshlib
+ . $(atf_get_srcdir)/slog.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/slog_010_neg.ksh || atf_fail "Testcase failed"
+}
+slog_010_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/slog.kshlib
+ . $(atf_get_srcdir)/slog.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case slog_011_neg cleanup
+slog_011_neg_head()
+{
+ atf_set "descr" "Offline and online a log device passes."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1200
+}
+slog_011_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/slog.kshlib
+ . $(atf_get_srcdir)/slog.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/slog_011_neg.ksh || atf_fail "Testcase failed"
+}
+slog_011_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/slog.kshlib
+ . $(atf_get_srcdir)/slog.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case slog_012_neg cleanup
+slog_012_neg_head()
+{
+ atf_set "descr" "Pool can survive when one of mirror log device get corrupted."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1200
+}
+slog_012_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/slog.kshlib
+ . $(atf_get_srcdir)/slog.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/slog_012_neg.ksh || atf_fail "Testcase failed"
+}
+slog_012_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/slog.kshlib
+ . $(atf_get_srcdir)/slog.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case slog_013_pos cleanup
+slog_013_pos_head()
+{
+ atf_set "descr" "Verify slog device can be disk, file, lofi device or any devicethat presents a block interface."
+ atf_set "require.progs" "ksh93 zpool lofiadm"
+ atf_set "timeout" 1200
+}
+slog_013_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/slog.kshlib
+ . $(atf_get_srcdir)/slog.cfg
+
+ verify_disk_count "$DISKS" 2
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/slog_013_pos.ksh || atf_fail "Testcase failed"
+}
+slog_013_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/slog.kshlib
+ . $(atf_get_srcdir)/slog.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case slog_014_pos cleanup
+slog_014_pos_head()
+{
+ atf_set "descr" "log device can survive when one of the pool device get corrupted."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1200
+}
+slog_014_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/slog.kshlib
+ . $(atf_get_srcdir)/slog.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/slog_014_pos.ksh || atf_fail "Testcase failed"
+}
+slog_014_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/slog.kshlib
+ . $(atf_get_srcdir)/slog.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case slog_001_pos
+ atf_add_test_case slog_002_pos
+ atf_add_test_case slog_003_pos
+ atf_add_test_case slog_004_pos
+ atf_add_test_case slog_005_pos
+ atf_add_test_case slog_006_pos
+ atf_add_test_case slog_007_pos
+ atf_add_test_case slog_008_neg
+ atf_add_test_case slog_009_neg
+ atf_add_test_case slog_010_neg
+ atf_add_test_case slog_011_neg
+ atf_add_test_case slog_012_neg
+ atf_add_test_case slog_013_pos
+ atf_add_test_case slog_014_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/snapshot/Makefile b/tests/sys/cddl/zfs/tests/snapshot/Makefile
new file mode 100644
index 000000000000..369dde3ba7ad
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/Makefile
@@ -0,0 +1,40 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/snapshot
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= snapshot_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= rollback_001_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= snapshot_017_pos.ksh
+${PACKAGE}FILES+= snapshot_013_pos.ksh
+${PACKAGE}FILES+= ctldir_acl.txt
+${PACKAGE}FILES+= snapshot_002_pos.ksh
+${PACKAGE}FILES+= snapshot_006_pos.ksh
+${PACKAGE}FILES+= clone_001_pos.ksh
+${PACKAGE}FILES+= snapshot_003_pos.ksh
+${PACKAGE}FILES+= snapshot_007_pos.ksh
+${PACKAGE}FILES+= snapshot_016_pos.ksh
+${PACKAGE}FILES+= snapshot_012_pos.ksh
+${PACKAGE}FILES+= snapshot_009_pos.ksh
+${PACKAGE}FILES+= snapshot_001_pos.ksh
+${PACKAGE}FILES+= snapshot_005_pos.ksh
+${PACKAGE}FILES+= snapshot.cfg
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= rollback_002_pos.ksh
+${PACKAGE}FILES+= snapshot_014_pos.ksh
+${PACKAGE}FILES+= snapshot_010_pos.ksh
+${PACKAGE}FILES+= snapshot_018_pos.ksh
+${PACKAGE}FILES+= rollback_003_pos.ksh
+${PACKAGE}FILES+= snapshot_019_pos.ksh
+${PACKAGE}FILES+= snapshot_015_pos.ksh
+${PACKAGE}FILES+= snapshot_011_pos.ksh
+${PACKAGE}FILES+= snapshot_004_pos.ksh
+${PACKAGE}FILES+= snapshot_008_pos.ksh
+${PACKAGE}FILES+= snapshot_020_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/snapshot/cleanup.ksh b/tests/sys/cddl/zfs/tests/snapshot/cleanup.ksh
new file mode 100644
index 000000000000..9af80e992e94
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_container_cleanup
diff --git a/tests/sys/cddl/zfs/tests/snapshot/clone_001_pos.ksh b/tests/sys/cddl/zfs/tests/snapshot/clone_001_pos.ksh
new file mode 100644
index 000000000000..f6ad7f2021af
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/clone_001_pos.ksh
@@ -0,0 +1,138 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: clone_001_pos
+#
+# DESCRIPTION:
+# Create a snapshot from regular filesystem, volume,
+# or filesystem upon volume, Build a clone file system
+# from the snapshot and verify new files can be written.
+#
+# STRATEGY:
+# 1. Create snapshot use 3 combination:
+# - Regular filesystem
+# - Regular volume
+# - Filesystem upon volume
+# 2. Clone a new file system from the snapshot
+# 3. Verify the cloned file system is writable
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-08-25)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+# Setup array, 4 elements as a group, refer to:
+# i+0: name of a snapshot
+# i+1: mountpoint of the snapshot
+# i+2: clone created from the snapshot
+# i+3: mountpoint of the clone
+
+set -A args "$SNAPFS" "$SNAPDIR" "$TESTPOOL/$TESTCLONE" "$TESTDIR.0" \
+ "$SNAPFS1" "$SNAPDIR3" "$TESTPOOL/$TESTCLONE1" "" \
+ "$SNAPFS2" "$SNAPDIR2" "$TESTPOOL1/$TESTCLONE2" "$TESTDIR.2"
+
+function setup_all
+{
+ create_pool $TESTPOOL1 /dev/zvol/$TESTPOOL/$TESTVOL
+ log_must $ZFS create $TESTPOOL1/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR2 $TESTPOOL1/$TESTFS
+
+ return 0
+}
+
+function cleanup_all
+{
+ destroy_pool $TESTPOOL1
+
+ [[ -d $TESTDIR2 ]] && \
+ log_must $RM -rf $TESTDIR2
+
+ return 0
+}
+
+log_assert "Verify a cloned file system is writable."
+
+log_onexit cleanup_all
+
+setup_all
+
+[[ -n $TESTDIR ]] && \
+ log_must $RM -rf $TESTDIR/* > /dev/null 2>&1
+
+typeset -i COUNT=10
+
+for mtpt in $TESTDIR $TESTDIR2 ; do
+ log_note "Populate the $mtpt directory (prior to snapshot)"
+ populate_dir $mtpt/before_file $COUNT $NUM_WRITES $BLOCKSZ ITER
+done
+
+typeset -i i=0
+while (( i < ${#args[*]} )); do
+ #
+ # Take a snapshot of the test file system.
+ #
+ log_must $ZFS snapshot ${args[i]}
+
+ #
+ # Clone a new file system from the snapshot
+ #
+ log_must $ZFS clone ${args[i]} ${args[i+2]}
+ if [[ -n ${args[i+3]} ]] ; then
+ log_must $ZFS set mountpoint=${args[i+3]} ${args[i+2]}
+
+ FILE_COUNT=`$LS -Al ${args[i+3]} | $GREP -v "total" | wc -l`
+ if [[ $FILE_COUNT -ne $COUNT ]]; then
+ $LS -Al ${args[i+3]}
+ log_fail "AFTER: ${args[i+3]} contains $FILE_COUNT files(s)."
+ fi
+
+ log_note "Verify the ${args[i+3]} directory is writable"
+ populate_dir ${args[i+3]}/after_file $COUNT $NUM_WRITES \
+ $BLOCKSZ ITER
+
+ FILE_COUNT=`$LS -Al ${args[i+3]}/after* | $GREP -v "total" | wc -l`
+ if [[ $FILE_COUNT -ne $COUNT ]]; then
+ $LS -Al ${args[i+3]}
+ log_fail "${args[i+3]} contains $FILE_COUNT after* files(s)."
+ fi
+ fi
+
+ (( i = i + 4 ))
+done
+
+log_pass "The clone file system is writable."
diff --git a/tests/sys/cddl/zfs/tests/snapshot/ctldir_acl.txt b/tests/sys/cddl/zfs/tests/snapshot/ctldir_acl.txt
new file mode 100644
index 000000000000..d0afd0a2e5a7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/ctldir_acl.txt
@@ -0,0 +1,3 @@
+ owner@:r-x---a---c--s:-------:allow
+ group@:r-x---a---c--s:-------:allow
+ everyone@:r-x---a---c--s:-------:allow
diff --git a/tests/sys/cddl/zfs/tests/snapshot/rollback_001_pos.ksh b/tests/sys/cddl/zfs/tests/snapshot/rollback_001_pos.ksh
new file mode 100644
index 000000000000..983c835a5632
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/rollback_001_pos.ksh
@@ -0,0 +1,109 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: rollback_001_pos
+#
+# DESCRIPTION:
+# Populate a file system and take a snapshot. Add some more files to the
+# file system and rollback to the last snapshot. Verify no post snapshot
+# file exist.
+#
+# STRATEGY:
+# 1. Empty a file system
+# 2. Populate the file system
+# 3. Take a snapshot of the file system
+# 4. Add new files to the file system
+# 5. Perform a rollback
+# 6. Verify the snapshot and file system agree
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ snapexists $SNAPFS
+ [[ $? -eq 0 ]] && \
+ log_must $ZFS destroy $SNAPFS
+
+ [[ -e $TESTDIR ]] && \
+ log_must $RM -rf $TESTDIR/* > /dev/null 2>&1
+}
+
+log_assert "Verify that a rollback to a previous snapshot succeeds."
+
+log_onexit cleanup
+
+[[ -n $TESTDIR ]] && \
+ log_must $RM -rf $TESTDIR/* > /dev/null 2>&1
+
+typeset -i COUNT=10
+
+log_note "Populate the $TESTDIR directory (prior to snapshot)"
+populate_dir $TESTDIR/before_file $COUNT $NUM_WRITES $BLOCKSZ ITER
+log_must $ZFS snapshot $SNAPFS
+
+FILE_COUNT=`$LS -Al $SNAPDIR | $GREP -v "total" | wc -l`
+if [[ $FILE_COUNT -ne $COUNT ]]; then
+ $LS -Al $SNAPDIR
+ log_fail "AFTER: $SNAPFS contains $FILE_COUNT files(s)."
+fi
+
+log_note "Populate the $TESTDIR directory (post snapshot)"
+populate_dir $TESTDIR/after_file $COUNT $NUM_WRITES $BLOCKSZ ITER
+
+#
+# Now rollback to latest snapshot
+#
+log_must $ZFS rollback $SNAPFS
+
+FILE_COUNT=`$LS -Al $TESTDIR/after* 2> /dev/null | $GREP -v "total" | wc -l`
+if [[ $FILE_COUNT -ne 0 ]]; then
+ $LS -Al $TESTDIR
+ log_fail "$TESTDIR contains $FILE_COUNT after* files(s)."
+fi
+
+FILE_COUNT=`$LS -Al $TESTDIR/before* 2> /dev/null \
+ | $GREP -v "total" | wc -l`
+if [[ $FILE_COUNT -ne $COUNT ]]; then
+ $LS -Al $TESTDIR
+ log_fail "$TESTDIR contains $FILE_COUNT before* files(s)."
+fi
+
+log_pass "The rollback operation succeeded."
diff --git a/tests/sys/cddl/zfs/tests/snapshot/rollback_002_pos.ksh b/tests/sys/cddl/zfs/tests/snapshot/rollback_002_pos.ksh
new file mode 100644
index 000000000000..388b397e793e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/rollback_002_pos.ksh
@@ -0,0 +1,121 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: rollback_002_pos
+#
+# DESCRIPTION:
+# Verify that rollbacks are with respect to the latest snapshot.
+#
+# STRATEGY:
+# 1. Empty a file system
+# 2. Populate the file system
+# 3. Take a snapshot of the file system
+# 4. Add new files to the file system
+# 5. Take a snapshot
+# 6. Remove the original files
+# 7. Perform a rollback
+# 8. Verify the latest snapshot and file system agree
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ snapexists $SNAPFS.1
+ [[ $? -eq 0 ]] && \
+ log_must $ZFS destroy $SNAPFS.1
+
+ snapexists $SNAPFS
+ [[ $? -eq 0 ]] && \
+ log_must $ZFS destroy $SNAPFS
+
+ [[ -e $TESTDIR ]] && \
+ log_must $RM -rf $TESTDIR/* > /dev/null 2>&1
+}
+
+log_assert "Verify rollback is with respect to latest snapshot."
+
+log_onexit cleanup
+
+[[ -n $TESTDIR ]] && \
+ log_must $RM -rf $TESTDIR/* > /dev/null 2>&1
+
+typeset -i COUNT=10
+
+log_note "Populate the $TESTDIR directory (prior to first snapshot)"
+populate_dir $TESTDIR/original_file $COUNT $NUM_WRITES $BLOCKSZ ITER
+
+log_must $ZFS snapshot $SNAPFS
+
+FILE_COUNT=`$LS -Al $SNAPDIR | $GREP -v "total" | wc -l`
+if [[ $FILE_COUNT -ne $COUNT ]]; then
+ $LS -Al $SNAPDIR
+ log_fail "AFTER: $SNAPFS contains $FILE_COUNT files(s)."
+fi
+
+log_note "Populate the $TESTDIR directory (prior to second snapshot)"
+populate_dir $TESTDIR/afterfirst_file $COUNT $NUM_WRITES $BLOCKSZ ITER
+log_must $ZFS snapshot $SNAPFS.1
+
+log_note "Populate the $TESTDIR directory (Post second snapshot)"
+populate_dir $TESTDIR/aftersecond_file $COUNT $NUM_WRITES $BLOCKSZ ITER
+
+[[ -n $TESTDIR ]] && \
+ log_must $RM -rf $TESTDIR/original_file* > /dev/null 2>&1
+
+#
+# Now rollback to latest snapshot
+#
+log_must $ZFS rollback $SNAPFS.1
+
+FILE_COUNT=`$LS -Al $TESTDIR/aftersecond* 2> /dev/null \
+ | $GREP -v "total" | wc -l`
+if [[ $FILE_COUNT -ne 0 ]]; then
+ $LS -Al $TESTDIR
+ log_fail "$TESTDIR contains $FILE_COUNT aftersecond* files(s)."
+fi
+
+FILE_COUNT=`$LS -Al $TESTDIR/original* $TESTDIR/afterfirst*| $GREP -v "total" | wc -l`
+if [[ $FILE_COUNT -ne 20 ]]; then
+ $LS -Al $TESTDIR
+ log_fail "$TESTDIR contains $FILE_COUNT original* files(s)."
+fi
+
+log_pass "The rollback to the latest snapshot succeeded."
diff --git a/tests/sys/cddl/zfs/tests/snapshot/rollback_003_pos.ksh b/tests/sys/cddl/zfs/tests/snapshot/rollback_003_pos.ksh
new file mode 100644
index 000000000000..d1ef2ce5b926
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/rollback_003_pos.ksh
@@ -0,0 +1,109 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_rollback/zfs_rollback_common.kshlib
+
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: rollback_003_pos
+#
+# DESCRIPTION:
+# Verify that rollbacks succeed when there are nested file systems.
+#
+# STRATEGY:
+# 1) Snapshot an empty file system and rollback
+# 2) Create a file in the file system
+# 3) Rollback the file system to empty
+# 4) Create a nested file system with the same name as the file created in (2)
+# 5) Verify a rollback succeeds
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-01-17)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ typeset snap=""
+ typeset fs=""
+
+ log_must $ZFS mount -a
+
+ for snap in "$SNAPPOOL.1" "$SNAPPOOL"
+ do
+ snapexists $snap
+ [[ $? -eq 0 ]] && \
+ log_must $ZFS destroy $snap
+ done
+
+ for fs in "$TESTPOOL/$TESTFILE/$TESTFILE.1" "$TESTPOOL/$TESTFILE"
+ do
+ datasetexists $fs
+ [[ $? -eq 0 ]] && \
+ log_must $ZFS destroy -r $fs
+ done
+
+ [[ -e /$TESTPOOL ]] && \
+ log_must $RM -rf $TESTPOOL/*
+}
+
+log_assert "Verify rollback succeeds when there are nested file systems."
+
+log_onexit cleanup
+
+log_must $ZFS snapshot $SNAPPOOL
+log_must $ZFS rollback $SNAPPOOL
+log_mustnot $ZFS snapshot $SNAPPOOL
+
+log_must $TOUCH /$TESTPOOL/$TESTFILE
+
+log_must $ZFS rollback $SNAPPOOL
+log_must $ZFS create $TESTPOOL/$TESTFILE
+
+log_must $ZFS rollback $SNAPPOOL
+
+log_note "Verify rollback of multiple nested file systems succeeds."
+log_must $ZFS snapshot $TESTPOOL/$TESTFILE@$TESTSNAP
+log_must $ZFS snapshot $SNAPPOOL.1
+
+unmount_all_safe > /dev/null 2>&1
+log_must $ZFS mount -a
+
+log_must $TOUCH /$TESTPOOL/$TESTFILE/$TESTFILE.1
+
+log_must $ZFS rollback $SNAPPOOL.1
+
+log_pass "Rollbacks succeed when nested file systems are present."
diff --git a/tests/sys/cddl/zfs/tests/snapshot/setup.ksh b/tests/sys/cddl/zfs/tests/snapshot/setup.ksh
new file mode 100644
index 000000000000..db5cb7d70c9b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_container_volume_setup ${DISK}
diff --git a/tests/sys/cddl/zfs/tests/snapshot/snapshot.cfg b/tests/sys/cddl/zfs/tests/snapshot/snapshot.cfg
new file mode 100644
index 000000000000..658cceccc9e1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/snapshot.cfg
@@ -0,0 +1,57 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+export ZFSROOT=
+export TESTVOL=testvol${TESTCASE_ID}
+export TESTVOL1=testvol1.${TESTCASE_ID}
+export TESTSNAP=testsnap${TESTCASE_ID}
+export TESTSNAP1=testsnap1.${TESTCASE_ID}
+export TESTCLONE=testclone${TESTCASE_ID}
+export TESTCLONE1=testclone1${TESTCASE_ID}
+export TESTCLONE2=testclone2${TESTCASE_ID}
+export TESTFILE=testfile${TESTCASE_ID}
+export TESTFILE1=testfile1${TESTCASE_ID}
+export SNAPROOT="$(get_snapdir_name)"
+
+export SNAPPOOL="$TESTPOOL@$TESTSNAP"
+export SNAPFS="$TESTPOOL/$TESTFS@$TESTSNAP"
+export SNAPFS1="$TESTPOOL/$TESTVOL@$TESTSNAP"
+export SNAPFS2="$TESTPOOL1/$TESTFS@$TESTSNAP"
+export SNAPCTR="$TESTPOOL/$TESTCTR/$TESTFS1@$TESTSNAP"
+export SNAPDIR="$TESTDIR/$SNAPROOT/$TESTSNAP"
+export SNAPDIR1="$TESTDIR1/$SNAPROOT/$TESTSNAP"
+export SNAPDIR2="$TESTDIR2/$SNAPROOT/$TESTSNAP"
+export SNAPDIR3="$ZFSROOT/$SNAPFS1"
+
+export VOLSIZE=1gb
+export BLOCKSZ=8192
+export NUM_WRITES=20
+export DATA=0
+export LIMIT=524288 # tolerance measured in bytes, 512K
+export FSQUOTA=500m
+export FILESIZE=400m
+export FILESIZE1=200m
diff --git a/tests/sys/cddl/zfs/tests/snapshot/snapshot_001_pos.ksh b/tests/sys/cddl/zfs/tests/snapshot/snapshot_001_pos.ksh
new file mode 100644
index 000000000000..909ff16507ef
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/snapshot_001_pos.ksh
@@ -0,0 +1,98 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: snapshot_001_pos
+#
+# DESCRIPTION:
+# A zfs file system snapshot is identical to
+# the originally snapshot'd file system, after the file
+# system has been changed. Uses 'sum -r'.
+#
+# STRATEGY:
+# 1. Create a file in the zfs file system
+# 2. Checksum the file for later comparison
+# 3. Create a snapshot of the dataset
+# 4. Append to the original file
+# 5. Verify the snapshot and file agree
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ snapexists $SNAPFS
+ if [[ $? -eq 0 ]]; then
+ log_must $ZFS destroy $SNAPFS
+ fi
+
+ if [[ -e $SNAPDIR ]]; then
+ log_must $RM -rf $SNAPDIR > /dev/null 2>&1
+ fi
+
+ if [[ -e $TESTDIR ]]; then
+ log_must $RM -rf $TESTDIR/* > /dev/null 2>&1
+ fi
+}
+
+log_assert "Verify a file system snapshot is identical to original."
+
+log_onexit cleanup
+
+log_note "Create a file in the zfs filesystem..."
+log_must $FILE_WRITE -o create -f $TESTDIR/$TESTFILE -b $BLOCKSZ \
+ -c $NUM_WRITES -d $DATA
+
+log_note "Sum the file, save for later comparison..."
+FILE_SUM=`$SUM -r $TESTDIR/$TESTFILE | $AWK '{ print $1 }'`
+log_note "FILE_SUM = $FILE_SUM"
+
+log_note "Create a snapshot and mount it..."
+log_must $ZFS snapshot $SNAPFS
+
+log_note "Append to the original file..."
+log_must $FILE_WRITE -o append -f $TESTDIR/$TESTFILE -b $BLOCKSZ \
+ -c $NUM_WRITES -d $DATA
+
+SNAP_FILE_SUM=`$SUM -r $SNAPDIR/$TESTFILE | $AWK '{ print $1 }'`
+if [[ $SNAP_FILE_SUM -ne $FILE_SUM ]]; then
+ log_fail "Sums do not match, aborting!! ($SNAP_FILE_SUM != $FILE_SUM)"
+fi
+
+log_pass "Both Sums match. ($SNAP_FILE_SUM == $FILE_SUM)"
diff --git a/tests/sys/cddl/zfs/tests/snapshot/snapshot_002_pos.ksh b/tests/sys/cddl/zfs/tests/snapshot/snapshot_002_pos.ksh
new file mode 100644
index 000000000000..1ddc87da7a38
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/snapshot_002_pos.ksh
@@ -0,0 +1,134 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: snapshot_002_pos
+#
+# DESCRIPTION:
+# An archive of a zfs file system and an archive of its snapshot
+# is identical even though the original file system has
+# changed sinced the snapshot was taken.
+#
+# STRATEGY:
+# 1) Create files in all of the zfs file systems
+# 2) Create a tarball of the file system
+# 3) Create a snapshot of the dataset
+# 4) Remove all the files in the original file system
+# 5) Create a tarball of the snapshot
+# 6) Extract each tarball and compare directory structures
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if [[ -d $CWD ]]; then
+ cd $CWD || log_fail "Could not cd $CWD"
+ fi
+
+ snapexists $SNAPFS
+ if [[ $? -eq 0 ]]; then
+ log_must $ZFS destroy $SNAPFS
+ fi
+
+ if [[ -e $SNAPDIR ]]; then
+ log_must $RM -rf $SNAPDIR > /dev/null 2>&1
+ fi
+
+ if [[ -e $TESTDIR ]]; then
+ log_must $RM -rf $TESTDIR/* > /dev/null 2>&1
+ fi
+
+ if [[ -e $TMPDIR/zfs_snapshot2.${TESTCASE_ID} ]]; then
+ log_must $RM -rf $TMPDIR/zfs_snapshot2.${TESTCASE_ID} > /dev/null 2>&1
+ fi
+
+}
+
+log_assert "Verify an archive of a file system is identical to " \
+ "an archive of its snapshot."
+
+log_onexit cleanup
+
+typeset -i COUNT=21
+typeset OP=create
+
+[[ -n $TESTDIR ]] && \
+ $RM -rf $TESTDIR/* > /dev/null 2>&1
+
+log_note "Create files in the zfs filesystem..."
+populate_dir $TESTDIR/file $COUNT $NUM_WRITES $BLOCKSZ $DATA
+
+log_note "Create a tarball from $TESTDIR contents..."
+CWD=$PWD
+cd $TESTDIR || log_fail "Could not cd $TESTDIR"
+log_must $TAR cf $TESTDIR/tarball.original.tar file*
+cd $CWD || log_fail "Could not cd $CWD"
+
+log_note "Create a snapshot and mount it..."
+log_must $ZFS snapshot $SNAPFS
+
+log_note "Remove all of the original files..."
+log_must $RM -f $TESTDIR/file* > /dev/null 2>&1
+
+log_note "Create tarball of snapshot..."
+CWD=$PWD
+cd $SNAPDIR || log_fail "Could not cd $SNAPDIR"
+log_must $TAR cf $TESTDIR/tarball.snapshot.tar file*
+cd $CWD || log_fail "Could not cd $CWD"
+
+log_must $MKDIR $TESTDIR/original
+log_must $MKDIR $TESTDIR/snapshot
+
+CWD=$PWD
+cd $TESTDIR/original || log_fail "Could not cd $TESTDIR/original"
+log_must $TAR xf $TESTDIR/tarball.original.tar
+
+cd $TESTDIR/snapshot || log_fail "Could not cd $TESTDIR/snapshot"
+log_must $TAR xf $TESTDIR/tarball.snapshot.tar
+
+cd $CWD || log_fail "Could not cd $CWD"
+
+$DIRCMP $TESTDIR/original $TESTDIR/snapshot > $TMPDIR/zfs_snapshot2.${TESTCASE_ID}
+$GREP different $TMPDIR/zfs_snapshot2.${TESTCASE_ID} >/dev/null 2>&1
+if [[ $? -ne 1 ]]; then
+ log_fail "Directory structures differ."
+fi
+
+log_pass "Directory structures match."
diff --git a/tests/sys/cddl/zfs/tests/snapshot/snapshot_003_pos.ksh b/tests/sys/cddl/zfs/tests/snapshot/snapshot_003_pos.ksh
new file mode 100644
index 000000000000..d316c966fa8e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/snapshot_003_pos.ksh
@@ -0,0 +1,103 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: snapshot_003_pos
+#
+# DESCRIPTION:
+# Verify that many snapshots can be made on a zfs file system.
+#
+# STRATEGY:
+# 1) Create a files in the zfs file system
+# 2) Create a snapshot of the dataset
+# 3) Remove all the files from the original file system
+# 4) Verify consistency of each snapshot directory
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ typeset -i i=1
+ while [ $i -lt $COUNT ]; do
+ snapexists $SNAPFS.$i
+ if [[ $? -eq 0 ]]; then
+ log_must $ZFS destroy $SNAPFS.$i
+ fi
+
+ if [[ -e $SNAPDIR.$i ]]; then
+ log_must $RM -rf $SNAPDIR.$i > /dev/null 2>&1
+ fi
+
+ (( i = i + 1 ))
+ done
+
+ if [[ -e $TESTDIR ]]; then
+ log_must $RM -rf $TESTDIR/* > /dev/null 2>&1
+ fi
+}
+
+log_assert "Verify many snapshots of a file system can be taken."
+
+log_onexit cleanup
+
+[[ -n $TESTDIR ]] && \
+ log_must $RM -rf $TESTDIR/* > /dev/null 2>&1
+
+typeset -i COUNT=10
+
+log_note "Create some files in the $TESTDIR directory..."
+populate_dir $TESTDIR/file $COUNT $NUM_WRITES $BLOCKSZ ITER $SNAPFS
+
+log_note "Remove all of the original files"
+[[ -n $TESTDIR ]] && \
+ log_must $RM -rf $TESTDIR/file* > /dev/null 2>&1
+
+i=1
+while [[ $i -lt $COUNT ]]; do
+ FILECOUNT=`$LS $SNAPDIR.$i/file* | wc -l`
+ typeset j=1
+ while [ $j -lt $FILECOUNT ]; do
+ log_must $FILE_CHECK $SNAPDIR.$i/file.$j $j
+ (( j = j + 1 ))
+ done
+ (( i = i + 1 ))
+done
+
+log_pass "All files are consistent"
diff --git a/tests/sys/cddl/zfs/tests/snapshot/snapshot_004_pos.ksh b/tests/sys/cddl/zfs/tests/snapshot/snapshot_004_pos.ksh
new file mode 100644
index 000000000000..daa270460a39
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/snapshot_004_pos.ksh
@@ -0,0 +1,91 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: snapshot_004_pos
+#
+# DESCRIPTION:
+# Create a null snapshot i.e. a snapshot created before file system
+# activity is empty.
+#
+# STRATEGY:
+# 1. Empty a file system
+# 2. Take a snapshot of the empty file system.
+# 3. Populate the file system
+# 4. Verify the snapshot is still empty
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ snapexists $SNAPFS
+ [[ $? -eq 0 ]] && \
+ log_must $ZFS destroy $SNAPFS
+
+ [[ -e $TESTDIR ]] && \
+ log_must $RM -rf $TESTDIR/* > /dev/null 2>&1
+}
+
+log_assert "Verify that a snapshot of an empty file system remains empty."
+
+log_onexit cleanup
+
+[[ -n $TESTDIR ]] && \
+ log_must $RM -rf $TESTDIR/* > /dev/null 2>&1
+
+log_must $ZFS snapshot $SNAPFS
+FILE_COUNT=`$LS -Al $SNAPDIR | $GREP -v "total 0" | wc -l`
+if [[ $FILE_COUNT -ne 0 ]]; then
+ $LS $SNAPDIR
+ log_fail "BEFORE: $SNAPDIR contains $FILE_COUNT files(s)."
+fi
+
+typeset -i COUNT=10
+
+log_note "Populate the $TESTDIR directory"
+populate_dir $TESTDIR/file $COUNT $NUM_WRITES $BLOCKSZ ITER
+
+FILE_COUNT=`$LS -Al $SNAPDIR | $GREP -v "total 0" | wc -l`
+if [[ $FILE_COUNT -ne 0 ]]; then
+ $LS $SNAPDIR
+ log_fail "AFTER: $SNAPDIR contains $FILE_COUNT files(s)."
+fi
+
+log_pass "The NULL snapshot remains empty."
diff --git a/tests/sys/cddl/zfs/tests/snapshot/snapshot_005_pos.ksh b/tests/sys/cddl/zfs/tests/snapshot/snapshot_005_pos.ksh
new file mode 100644
index 000000000000..cb869a51447c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/snapshot_005_pos.ksh
@@ -0,0 +1,97 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: snapshot_005_pos
+#
+# DESCRIPTION:
+# to the originally snapshot'd file system, after the file
+# system has been changed. Uses 'sum -r'.
+#
+# STRATEGY:
+# 1) Create a file in the zfs dataset
+# 2) Sum the file for later comparison
+# 3) Create a snapshot of the dataset
+# 4) Append to the original file
+# 5) Verify both checksums match
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ snapexists $SNAPCTR
+ if [[ $? -eq 0 ]]; then
+ log_must $ZFS destroy $SNAPCTR
+ fi
+
+ if [[ -e $SNAPDIR1 ]]; then
+ log_must $RM -rf $SNAPDIR1 > /dev/null 2>&1
+ fi
+
+ if [[ -e $TESTDIR ]]; then
+ log_must $RM -rf $TESTDIR/* > /dev/null 2>&1
+ fi
+}
+
+log_assert "Verify that a snapshot of a dataset is identical to " \
+ "the original dataset."
+log_onexit cleanup
+
+log_note "Create a file in the zfs filesystem..."
+log_must $FILE_WRITE -o create -f $TESTDIR1/$TESTFILE -b $BLOCKSZ \
+ -c $NUM_WRITES -d $DATA
+
+log_note "Sum the file, save for later comparison..."
+FILE_SUM=`$SUM -r $TESTDIR1/$TESTFILE | $AWK '{ print $1 }'`
+log_note "FILE_SUM = $FILE_SUM"
+
+log_note "Create a snapshot and mount it..."
+log_must $ZFS snapshot $SNAPCTR
+
+log_note "Append to the original file..."
+log_must $FILE_WRITE -o append -f $TESTDIR1/$TESTFILE -b $BLOCKSZ \
+ -c $NUM_WRITES -d $DATA
+
+SNAP_FILE_SUM=`$SUM -r $SNAPDIR1/$TESTFILE | $AWK '{ print $1 }'`
+if [[ $SNAP_FILE_SUM -ne $FILE_SUM ]]; then
+ log_fail "Sums do not match, aborting!! ($SNAP_FILE_SUM != $FILE_SUM)"
+fi
+
+log_pass "Both Sums match. ($SNAP_FILE_SUM == $FILE_SUM)"
diff --git a/tests/sys/cddl/zfs/tests/snapshot/snapshot_006_pos.ksh b/tests/sys/cddl/zfs/tests/snapshot/snapshot_006_pos.ksh
new file mode 100644
index 000000000000..70e9569954dd
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/snapshot_006_pos.ksh
@@ -0,0 +1,132 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: snapshot_006_pos
+#
+# DESCRIPTION:
+# An archive of a zfs dataset and an archive of its snapshot
+# changed sinced the snapshot was taken.
+#
+# STRATEGY:
+# 1) Create some files in a ZFS dataset
+# 2) Create a tarball of the dataset
+# 3) Create a snapshot of the dataset
+# 4) Remove all the files in the original dataset
+# 5) Create a tarball of the snapshot
+# 6) Extract each tarball and compare directory structures
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if [[ -d $CWD ]]; then
+ cd $CWD || log_fail "Could not cd $CWD"
+ fi
+
+ snapexists $SNAPCTR
+ if [[ $? -eq 0 ]]; then
+ log_must $ZFS destroy $SNAPCTR
+ fi
+
+ if [[ -e $SNAPDIR1 ]]; then
+ log_must $RM -rf $SNAPDIR1 > /dev/null 2>&1
+ fi
+
+ if [[ -e $TESTDIR1 ]]; then
+ log_must $RM -rf $TESTDIR1/* > /dev/null 2>&1
+ fi
+
+ if [[ -e $TMPDIR/zfs_snapshot2.${TESTCASE_ID} ]]; then
+ log_must $RM -rf $TMPDIR/zfs_snapshot2.${TESTCASE_ID} > /dev/null 2>&1
+ fi
+
+}
+
+log_assert "Verify that an archive of a dataset is identical to " \
+ "an archive of the dataset's snapshot."
+
+log_onexit cleanup
+
+typeset -i COUNT=21
+typeset OP=create
+
+[[ -n $TESTDIR1 ]] && $RM -rf $TESTDIR1/* > /dev/null 2>&1
+
+log_note "Create files in the zfs dataset ..."
+populate_dir $TESTDIR1/file $COUNT $NUM_WRITES $BLOCKSZ $DATA
+
+log_note "Create a tarball from $TESTDIR1 contents..."
+CWD=$PWD
+cd $TESTDIR1 || log_fail "Could not cd $TESTDIR1"
+log_must $TAR cf $TESTDIR1/tarball.original.tar file*
+cd $CWD || log_fail "Could not cd $CWD"
+
+log_note "Create a snapshot and mount it..."
+log_must $ZFS snapshot $SNAPCTR
+
+log_note "Remove all of the original files..."
+log_must $RM -f $TESTDIR1/file* > /dev/null 2>&1
+
+log_note "Create tarball of snapshot..."
+CWD=$PWD
+cd $SNAPDIR1 || log_fail "Could not cd $SNAPDIR1"
+log_must $TAR cf $TESTDIR1/tarball.snapshot.tar file*
+cd $CWD || log_fail "Could not cd $CWD"
+
+log_must $MKDIR $TESTDIR1/original
+log_must $MKDIR $TESTDIR1/snapshot
+
+CWD=$PWD
+cd $TESTDIR1/original || log_fail "Could not cd $TESTDIR1/original"
+log_must $TAR xf $TESTDIR1/tarball.original.tar
+
+cd $TESTDIR1/snapshot || log_fail "Could not cd $TESTDIR1/snapshot"
+log_must $TAR xf $TESTDIR1/tarball.snapshot.tar
+
+cd $CWD || log_fail "Could not cd $CWD"
+
+$DIRCMP $TESTDIR1/original $TESTDIR1/snapshot > $TMPDIR/zfs_snapshot2.${TESTCASE_ID}
+$GREP different $TMPDIR/zfs_snapshot2.${TESTCASE_ID} >/dev/null 2>&1
+if [[ $? -ne 1 ]]; then
+ log_fail "Directory structures differ."
+fi
+
+log_pass "Directory structures match."
diff --git a/tests/sys/cddl/zfs/tests/snapshot/snapshot_007_pos.ksh b/tests/sys/cddl/zfs/tests/snapshot/snapshot_007_pos.ksh
new file mode 100644
index 000000000000..41a73b4cf043
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/snapshot_007_pos.ksh
@@ -0,0 +1,107 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: snapshot_007_pos
+#
+# DESCRIPTION:
+# Verify that many snapshots can be made on a zfs dataset.
+#
+# STRATEGY:
+# 1) Create a file in the zfs dataset
+# 2) Create a snapshot of the dataset
+# 3) Remove all the files from the original dataset
+# 4) For each snapshot directory verify consistency
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ typeset -i i=1
+ while [ $i -lt $COUNT ]; do
+ snapexists $SNAPCTR.$i
+ if [[ $? -eq 0 ]]; then
+ log_must $ZFS destroy $SNAPCTR.$i
+ fi
+
+ if [[ -e $SNAPDIR.$i ]]; then
+ log_must $RM -rf $SNAPDIR1.$i > /dev/null 2>&1
+ fi
+
+ (( i = i + 1 ))
+ done
+
+ if [[ -e $SNAPDIR1 ]]; then
+ log_must $RM -rf $SNAPDIR1 > /dev/null 2>&1
+ fi
+
+ if [[ -e $TESTDIR ]]; then
+ log_must $RM -rf $TESTDIR/* > /dev/null 2>&1
+ fi
+}
+
+log_assert "Verify that many snapshots can be made on a zfs dataset."
+
+log_onexit cleanup
+
+[[ -n $TESTDIR ]] && \
+ log_must $RM -rf $TESTDIR/* > /dev/null 2>&1
+
+typeset -i COUNT=10
+
+log_note "Create some files in the $TESTDIR directory..."
+populate_dir $TESTDIR1/file $COUNT $NUM_WRITES $BLOCKSZ ITER $SNAPCTR
+
+log_note "Remove all of the original files"
+[[ -n $TESTDIR ]] && \
+ log_must $RM -rf $TESTDIR1/file* > /dev/null 2>&1
+
+i=1
+while [[ $i -lt $COUNT ]]; do
+ FILECOUNT=`$LS $SNAPDIR1.$i/file* | wc -l`
+ typeset j=1
+ while [ $j -lt $FILECOUNT ]; do
+ log_must $FILE_CHECK $SNAPDIR1.$i/file.$j $j
+ (( j = j + 1 ))
+ done
+ (( i = i + 1 ))
+done
+
+log_pass "All files are consistent"
diff --git a/tests/sys/cddl/zfs/tests/snapshot/snapshot_008_pos.ksh b/tests/sys/cddl/zfs/tests/snapshot/snapshot_008_pos.ksh
new file mode 100644
index 000000000000..ef98179e2981
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/snapshot_008_pos.ksh
@@ -0,0 +1,100 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: snapshot_008_pos
+#
+# DESCRIPTION:
+# Verify that destroying snapshots returns space to the pool.
+#
+# STRATEGY:
+# 1. Create a file system and populate it while snapshotting.
+# 2. Destroy the snapshots and remove the files.
+# 3. Verify the space returns to the pool.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-09-29)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ typeset -i i=1
+ while [[ $i -lt $COUNT ]]; do
+ snapexists $SNAPFS.$i
+ [[ $? -eq 0 ]] && \
+ log_must $ZFS destroy $SNAPFS.$i
+
+ (( i = i + 1 ))
+ done
+
+ [[ -e $TESTDIR ]] && \
+ log_must $RM -rf $TESTDIR/* > /dev/null 2>&1
+}
+
+log_assert "Verify that destroying snapshots returns space to the pool."
+
+log_onexit cleanup
+
+[[ -n $TESTDIR ]] && \
+ log_must $RM -rf $TESTDIR/* > /dev/null 2>&1
+
+typeset -i COUNT=10
+
+orig_size=`get_prop available $TESTPOOL`
+
+log_note "Populate the $TESTDIR directory"
+populate_dir $TESTDIR/file $COUNT $NUM_WRITES $BLOCKSZ ITER $SNAPFS
+
+typeset -i i=1
+while [[ $i -lt $COUNT ]]; do
+ log_must rm -f $TESTDIR/file.$i > /dev/null 2>&1
+ log_must $ZFS destroy $SNAPFS.$i
+
+ (( i = i + 1 ))
+done
+
+new_size=`get_prop available $TESTPOOL`
+
+typeset -i tolerance=0
+
+(( tolerance = new_size - orig_size))
+if (( tolerance > LIMIT )); then
+ log_fail "Space not freed. ($orig_size != $new_size)"
+fi
+
+log_pass "After destroying snapshots, the space is returned to the pool."
diff --git a/tests/sys/cddl/zfs/tests/snapshot/snapshot_009_pos.ksh b/tests/sys/cddl/zfs/tests/snapshot/snapshot_009_pos.ksh
new file mode 100644
index 000000000000..e346376bc65c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/snapshot_009_pos.ksh
@@ -0,0 +1,126 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: snapshot_009_pos
+#
+# DESCRIPTION:
+# Verify 'snapshot -r' and 'destroy -r' can correctly create and destroy
+# snapshot tree respectively.
+#
+# STRATEGY:
+# 1. Use the snapshot -r to create snapshot for top level pool
+# 2. Verify the children snapshots are created correctly.
+# 3. Use destroy -r to destroy the top level snapshot
+# 4. Verify that all children snapshots are destroyed too.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-20)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ typeset ds
+ typeset snap
+
+ for ds in $ctr/$TESTVOL1 $ctr/$TESTCLONE; do
+ datasetexists $ds && \
+ log_must $ZFS destroy -f $ds
+ done
+
+ for snap in $ctr/$TESTFS1@$TESTSNAP1 \
+ $snappool $snapvol $snapctr $snapctrvol \
+ $snapctrclone $snapctrfs
+ do
+ snapexists $snap && \
+ log_must $ZFS destroy -rf $snap
+ done
+
+}
+
+log_assert "Verify snapshot -r can correctly create a snapshot tree."
+log_onexit cleanup
+
+ctr=$TESTPOOL/$TESTCTR
+ctrfs=$ctr/$TESTFS1
+ctrclone=$ctr/$TESTCLONE
+ctrvol=$ctr/$TESTVOL1
+snappool=$SNAPPOOL
+snapfs=$SNAPFS
+snapctr=$ctr@$TESTSNAP
+snapvol=$SNAPFS1
+snapctrvol=$ctrvol@$TESTSNAP
+snapctrclone=$ctrclone@$TESTSNAP
+snapctrfs=$SNAPCTR
+
+#preparation for testing
+log_must $ZFS snapshot $ctrfs@$TESTSNAP1
+log_must $ZFS clone $ctrfs@$TESTSNAP1 $ctrclone
+if is_global_zone; then
+ log_must $ZFS create -V $VOLSIZE $ctrvol
+else
+ log_must $ZFS create $ctrvol
+fi
+
+log_must $ZFS snapshot -r $snappool
+
+#verify the snapshot -r results
+for snap in $snappool $snapfs $snapvol $snapctr $snapctrvol \
+ $snapctrclone $snapctrfs
+do
+ ! snapexists $snap && \
+ log_fail "The snapshot $snap is not created via -r option."
+done
+
+log_note "Verify that destroy -r can destroy the snapshot tree."
+
+log_must $ZFS destroy -r $snappool
+for snap in $snappool $snapfs $snapvol $snapctr $snapctrvol \
+ $snapctrclone $snapctrfs
+do
+ snapexists $snap && \
+ log_fail "The snapshot $snap is not destroyed correctly."
+done
+
+log_note "Verify that the snapshot with different name should \
+ be not destroyed."
+! snapexists $ctrfs@$TESTSNAP1 && \
+ log_fail "destroy -r incorrectly destroys the snapshot \
+ $ctrfs@$TESTSNAP1."
+
+log_pass "snapshot|destroy -r with snapshot tree works as expected."
diff --git a/tests/sys/cddl/zfs/tests/snapshot/snapshot_010_pos.ksh b/tests/sys/cddl/zfs/tests/snapshot/snapshot_010_pos.ksh
new file mode 100644
index 000000000000..6f7b61ffa98a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/snapshot_010_pos.ksh
@@ -0,0 +1,108 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: snapshot_010_pos
+#
+# DESCRIPTION:
+# Verify 'destroy -r' can correctly destroy a snapshot tree at any point.
+#
+# STRATEGY:
+# 1. Use the snapshot -r to create snapshot for top level pool
+# 2. Select a middle point of the snapshot tree, use destroy -r to destroy all
+# snapshots beneath the point.
+# 3. Verify the destroy results.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-20)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ typeset snap
+
+ datasetexists $ctrvol && \
+ log_must $ZFS destroy -f $ctrvol
+
+ for snap in $ctrfs@$TESTSNAP1 \
+ $snappool $snapvol $snapctr $snapctrvol \
+ $snapctrclone $snapctrfs
+ do
+ snapexists $snap && \
+ log_must $ZFS destroy -rf $snap
+ done
+
+}
+
+log_assert "Verify 'destroy -r' can correctly destroy a snapshot subtree at any point."
+log_onexit cleanup
+
+ctr=$TESTPOOL/$TESTCTR
+ctrfs=$ctr/$TESTFS1
+ctrvol=$ctr/$TESTVOL1
+snappool=$SNAPPOOL
+snapfs=$SNAPFS
+snapctr=$ctr@$TESTSNAP
+snapvol=$SNAPFS1
+snapctrvol=$ctr/$TESTVOL1@$TESTSNAP
+snapctrclone=$ctr/$TESTCLONE@$TESTSNAP
+snapctrfs=$SNAPCTR
+
+#preparation for testing
+log_must $ZFS snapshot $ctrfs@$TESTSNAP1
+if is_global_zone; then
+ log_must $ZFS create -V $VOLSIZE $ctrvol
+else
+ log_must $ZFS create $ctrvol
+fi
+
+log_must $ZFS snapshot -r $snappool
+
+#select the $TESTCTR as destroy point, $TESTCTR is a child of $TESTPOOL
+log_must $ZFS destroy -r $snapctr
+for snap in $snapctr $snapctrvol $snapctrclone $snapctrfs; do
+ snapexists $snap && \
+ log_fail "The snapshot $snap is not destroyed correctly."
+done
+
+for snap in $snappool $snapfs $snapvol $ctrfs@$TESTSNAP1;do
+ ! snapexists $snap && \
+ log_fail "The snapshot $snap should be not destroyed."
+done
+
+log_pass "'destroy -r' destroys snapshot subtree as expected."
diff --git a/tests/sys/cddl/zfs/tests/snapshot/snapshot_011_pos.ksh b/tests/sys/cddl/zfs/tests/snapshot/snapshot_011_pos.ksh
new file mode 100644
index 000000000000..e0abe386ab11
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/snapshot_011_pos.ksh
@@ -0,0 +1,109 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: snapshot_011_pos
+#
+# DESCRIPTION:
+# use 'snapshot -r' to create a snapshot tree, add some files to one child
+# filesystem, rollback the child filesystem snapshot, verify that the child
+# filesystem gets back to the status while taking the snapshot.
+#
+# STRATEGY:
+# 1. Add some files to a target child filesystem
+# 2. snapshot -r the parent filesystem
+# 3. Add some other files to the target child filesystem
+# 4. rollback the child filesystem snapshot
+# 5. verify that the child filesystem get back to the status while being
+# snapshot'd
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-20)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ snapexists $SNAPPOOL && \
+ log_must $ZFS destroy -r $SNAPPOOL
+
+ [[ -e $TESTDIR ]] && \
+ log_must $RM -rf $TESTDIR/* > /dev/null 2>&1
+}
+
+log_assert "Verify that rollback to a snapshot created by snapshot -r succeeds."
+log_onexit cleanup
+
+[[ -n $TESTDIR ]] && \
+ log_must $RM -rf $TESTDIR/* > /dev/null 2>&1
+
+typeset -i COUNT=10
+
+log_note "Populate the $TESTDIR directory (prior to snapshot)"
+populate_dir $TESTDIR/before_file $COUNT $NUM_WRITES $BLOCKSZ ITER
+
+log_must $ZFS snapshot -r $SNAPPOOL
+
+FILE_COUNT=`$LS -Al $SNAPDIR | $GREP -v "total" | wc -l`
+if (( FILE_COUNT != COUNT )); then
+ $LS -Al $SNAPDIR
+ log_fail "AFTER: $SNAPFS contains $FILE_COUNT files(s)."
+fi
+
+log_note "Populate the $TESTDIR directory (post snapshot)"
+typeset -i i=0
+populate_dir $TESTDIR/after_file $COUNT $NUM_WRITES $BLOCKSZ ITER
+
+#
+# Now rollback to latest snapshot
+#
+log_must $ZFS rollback $SNAPFS
+
+FILE_COUNT=`$LS -Al $TESTDIR/after* 2> /dev/null | $GREP -v "total" | wc -l`
+if (( FILE_COUNT != 0 )); then
+ $LS -Al $TESTDIR
+ log_fail "$TESTDIR contains $FILE_COUNT after* files(s)."
+fi
+
+FILE_COUNT=`$LS -Al $TESTDIR/before* 2> /dev/null \
+ | $GREP -v "total" | wc -l`
+if (( FILE_COUNT != $COUNT )); then
+ $LS -Al $TESTDIR
+ log_fail "$TESTDIR contains $FILE_COUNT before* files(s)."
+fi
+
+log_pass "Rollback with child snapshot works as expected."
diff --git a/tests/sys/cddl/zfs/tests/snapshot/snapshot_012_pos.ksh b/tests/sys/cddl/zfs/tests/snapshot/snapshot_012_pos.ksh
new file mode 100644
index 000000000000..78625d7f6d03
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/snapshot_012_pos.ksh
@@ -0,0 +1,111 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: snapshot_012_pos
+#
+# DESCRIPTION:
+# Verify 'snapshot -r' can create snapshot for promoted clone, and vice
+# versa, a clone filesystem from the snapshot created by 'snapshot -r'
+# can be correctly promoted.
+#
+# STRATEGY:
+# 1. Create a dataset tree
+# 2. snapshot a filesystem and clone the snapshot
+# 3. promote the clone
+# 4. snapshot -r the dataset tree
+# 5. verify that the snapshot of cloned filesystem is created correctly
+# 6. clone a snapshot from the snapshot tree
+# 7. promote the clone
+# 8. verify that the clone is promoted correctly.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-20)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ if datasetexists $clone1; then
+ log_must $ZFS promote $ctrfs
+ log_must $ZFS destroy $clone1
+ fi
+
+ snapexists $snapctr && \
+ log_must $ZFS destroy -r $snapctr
+
+ if snapexists $clone@$TESTSNAP1; then
+ log_must $ZFS promote $ctrfs
+ log_must $ZFS destroy -rR $ctrfs@$TESTSNAP1
+ fi
+}
+
+log_assert "Verify that 'snapshot -r' can work with 'zfs promote'."
+log_onexit cleanup
+
+ctr=$TESTPOOL/$TESTCTR
+ctrfs=$ctr/$TESTFS1
+clone=$ctr/$TESTCLONE
+clone1=$ctr/$TESTCLONE1
+snappool=$SNAPPOOL
+snapfs=$SNAPFS
+snapctr=$ctr@$TESTSNAP
+snapctrclone=$clone@$TESTSNAP
+snapctrclone1=$clone1@$TESTSNAP
+snapctrfs=$SNAPCTR
+
+#preparation for testing
+log_must $ZFS snapshot $ctrfs@$TESTSNAP1
+log_must $ZFS clone $ctrfs@$TESTSNAP1 $clone
+log_must $ZFS promote $clone
+
+log_must $ZFS snapshot -r $snapctr
+
+! snapexists $snapctrclone && \
+ log_fail "'snapshot -r' fails to create $snapctrclone for $ctr/$TESTCLONE."
+
+log_must $ZFS clone $snapctrfs $clone1
+log_must $ZFS promote $clone1
+
+#verify the origin value is correct.
+orig_value=$(get_prop origin $ctrfs)
+if ! snapexists $snapctrclone1 || [[ "$orig_value" != "$snapctrclone1" ]]; then
+ log_fail "'zfs promote' fails to promote $clone which is cloned from \
+ $snapctrfs."
+fi
+
+log_pass "'snapshot -r' can work with 'zfs promote' as expected."
diff --git a/tests/sys/cddl/zfs/tests/snapshot/snapshot_013_pos.ksh b/tests/sys/cddl/zfs/tests/snapshot/snapshot_013_pos.ksh
new file mode 100644
index 000000000000..fe7c640fd51c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/snapshot_013_pos.ksh
@@ -0,0 +1,101 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: snapshot_013_pos
+#
+# DESCRIPTION:
+# verify that the snapshots created by 'snapshot -r' can be used for
+# zfs send/recv
+#
+# STRATEGY:
+# 1. create a dataset tree and populate a filesystem
+# 2. snapshot -r the dataset tree
+# 3. select one snapshot used for zfs send/recv
+# 4. verify the data integrity after zfs send/recv
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-06-20)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ datasetexists $ctrfs && \
+ $ZFS destroy -r $ctrfs
+
+ snapexists $snappool && \
+ log_must $ZFS destroy -r $snappool
+
+ [[ -e $TESTDIR ]] && \
+ log_must $RM -rf $TESTDIR/* > /dev/null 2>&1
+}
+
+log_assert "Verify snapshots from 'snapshot -r' can be used for zfs send/recv"
+log_onexit cleanup
+
+ctr=$TESTPOOL/$TESTCTR
+ctrfs=$ctr/$TESTFS
+snappool=$SNAPPOOL
+snapfs=$SNAPFS
+snapctr=$ctr@$TESTSNAP
+snapctrfs=$ctrfs@$TESTSNAP
+fsdir=/$ctrfs
+snapdir=$fsdir/$(get_snapdir_name)/$TESTSNAP
+
+[[ -n $TESTDIR ]] && \
+ log_must $RM -rf $TESTDIR/* > /dev/null 2>&1
+
+typeset -i COUNT=10
+
+log_note "Populate the $TESTDIR directory (prior to snapshot)"
+populate_dir $TESTDIR/file $COUNT $NUM_WRITES $BLOCKSZ ITER
+
+log_must $ZFS snapshot -r $snappool
+
+$ZFS send $snapfs | $ZFS receive $ctrfs >/dev/null 2>&1
+if ! datasetexists $ctrfs || ! snapexists $snapctrfs; then
+ log_fail "zfs send/receive fails with snapshot $snapfs."
+fi
+
+for dir in $fsdir $snapdir; do
+ FILE_COUNT=`$LS -Al $dir | $GREP -v "total" | wc -l`
+ (( FILE_COUNT != COUNT )) && \
+ log_fail "The data gets changed after zfs send/recv."
+done
+
+log_pass "'zfs send/receive' works as expected with snapshots from 'snapshot -r'"
diff --git a/tests/sys/cddl/zfs/tests/snapshot/snapshot_014_pos.ksh b/tests/sys/cddl/zfs/tests/snapshot/snapshot_014_pos.ksh
new file mode 100644
index 000000000000..74a97cbaed35
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/snapshot_014_pos.ksh
@@ -0,0 +1,85 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: snapshot_014_pos
+#
+# DESCRIPTION:
+# verify that creating/destroying snapshots do things clean
+#
+# STRATEGY:
+# 1. create a dataset and set a quota with 500m
+# 2. create file of size 400m on the dataset
+# 3. take a snapshot and destroy it
+# 4. then create file to use all spaces in the dataset
+# 5. verify removing the first file should succeed
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-10-13)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ [[ -e $TESTDIR1 ]] && \
+ log_must $RM -rf $TESTDIR1/* > /dev/null 2>&1
+
+ snapexists $SNAPCTR && \
+ log_must $ZFS destroy $SNAPCTR
+
+ datasetexists $TESTPOOL/$TESTCTR/$TESTFS1 && \
+ log_must $ZFS set quota=none $TESTPOOL/$TESTCTR/$TESTFS1
+
+}
+
+log_assert "Verify creating/destroying snapshots do things clean"
+log_onexit cleanup
+
+log_must $ZFS set quota=$FSQUOTA $TESTPOOL/$TESTCTR/$TESTFS1
+log_must $MKFILE $FILESIZE $TESTDIR1/$TESTFILE
+
+log_must $ZFS snapshot $SNAPCTR
+log_must $ZFS destroy $SNAPCTR
+
+log_note "Make the quota of filesystem is reached"
+log_mustnot $MKFILE $FILESIZE1 $TESTDIR1/$TESTFILE1
+
+log_note "Verify removing the first file should succeed after the snapshot is \
+ removed"
+log_must $RM $TESTDIR1/$TESTFILE
+
+log_pass "Verify creating/destroying snapshots do things clean"
diff --git a/tests/sys/cddl/zfs/tests/snapshot/snapshot_015_pos.ksh b/tests/sys/cddl/zfs/tests/snapshot/snapshot_015_pos.ksh
new file mode 100644
index 000000000000..27dbf357f5cd
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/snapshot_015_pos.ksh
@@ -0,0 +1,131 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_rollback/zfs_rollback_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: snapshot_015_pos
+#
+# DESCRIPTION:
+# Verify snapshot can be created or destroy via mkdir or rm
+# in $(get_snapdir_name).
+#
+# STRATEGY:
+# 1. Verify make directories only successfully in $(get_snapdir_name).
+# 2. Verify snapshot can be created and destroy via mkdir and remove
+# directories in $(get_snapdir_name).
+# 3. Verify rollback to previous snapshot can succeed.
+# 4. Verify remove directory in snapdir can destroy snapshot.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-10-17)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ typeset -i i=0
+ while ((i < snap_cnt)); do
+ typeset snap=$fs@snap.$i
+ datasetexists $snap && log_must $ZFS destroy -f $snap
+
+ ((i += 1))
+ done
+}
+
+$ZFS 2>&1 | $GREP "allow" > /dev/null
+(($? != 0)) && log_unsupported
+
+log_assert "Verify snapshot can be created via mkdir in $(get_snapdir_name)."
+log_onexit cleanup
+
+[[ $os_name == "FreeBSD" ]] && \
+ log_uninitiated "Directory operations on the $(get_snapdir_name) directory are not yet supported in FreeBSD"
+
+fs=$TESTPOOL/$TESTFS
+# Verify all the other directories are readonly.
+mntpnt=$(get_prop mountpoint $fs)
+snapdir=$mntpnt/.zfs
+set -A ro_dirs "$snapdir" "$snapdir/snap" "$snapdir/snapshot"
+for dir in ${ro_dirs[@]}; do
+ if [[ -d $dir ]]; then
+ log_mustnot $RM -rf $dir
+ log_mustnot $TOUCH $dir/testfile
+ else
+ log_mustnot $MKDIR $dir
+ fi
+done
+
+# Verify snapshot can be created via mkdir in $(get_snapdir_name)
+typeset -i snap_cnt=5
+typeset -i cnt=0
+while ((cnt < snap_cnt)); do
+ testfile=$mntpnt/testfile.$cnt
+ log_must $MKFILE 1M $testfile
+ log_must $MKDIR $mntpnt/$(get_snapdir_name)/snap.$cnt
+ if ! datasetexists $fs@snap.$cnt ; then
+ log_fail "ERROR: $fs@snap.$cnt should exists."
+ fi
+
+ ((cnt += 1))
+done
+
+# Verify rollback to previous snapshot succeed.
+((cnt = RANDOM % snap_cnt))
+log_must $ZFS rollback -r $fs@snap.$cnt
+
+typeset -i i=0
+while ((i < snap_cnt)); do
+ testfile=$mntpnt/testfile.$i
+ if ((i <= cnt)); then
+ if [[ ! -f $testfile ]]; then
+ log_fail "ERROR: $testfile should exists."
+ fi
+ else
+ if [[ -f $testfile ]]; then
+ log_fail "ERROR: $testfile should not exists."
+ fi
+ fi
+
+ ((i += 1))
+done
+
+# Verify remove directory in snapdir can destroy snapshot.
+log_must $RMDIR $mntpnt/$(get_snapdir_name)/snap.$cnt
+log_mustnot datasetexists $fs@snap.$cnt
+
+log_pass "Verify snapshot can be created via mkdir in $(get_snapdir_name) passed."
diff --git a/tests/sys/cddl/zfs/tests/snapshot/snapshot_016_pos.ksh b/tests/sys/cddl/zfs/tests/snapshot/snapshot_016_pos.ksh
new file mode 100644
index 000000000000..73cb68c30049
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/snapshot_016_pos.ksh
@@ -0,0 +1,111 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: snapshot_016_pos
+#
+# DESCRIPTION:
+# Verify renamed snapshots via mv can be destroyed
+#
+# STRATEGY:
+# 1. Create snapshot
+# 2. Rename the snapshot via mv command
+# 2. Verify destroying the renamed snapshot via 'zfs destroy' succeeds
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-01-26)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ datasetexists $SNAPFS && \
+ log_must $ZFS destroy -Rf $SNAPFS
+ datasetexists $TESTPOOL/$TESTFS@snap_a && \
+ log_must $ZFS destroy -Rf $TESTPOOL/$TESTFS@snap_a
+ datasetexists $TESTPOOL/$TESTCLONE@snap_a && \
+ log_must $ZFS destroy -Rf $TESTPOOL/$TESTCLONE@snap_a
+
+ datasetexists $TESTPOOL/$TESTCLONE && \
+ log_must $ZFS destroy $TESTPOOL/$TESTCLONE
+ datasetexists $TESTPOOL/$TESTFS && \
+ log_must $ZFS destroy $TESTPOOL/$TESTFS
+
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+}
+
+log_assert "Verify renamed snapshots via mv can be destroyed."
+log_onexit cleanup
+
+[[ $os_name == "FreeBSD" ]] && \
+ log_uninitiated "Directory operations on the $(get_snapdir_name) directory are not yet supported in FreeBSD"
+
+# scenario 1
+
+log_must $ZFS snapshot $SNAPFS
+log_must $MV $TESTDIR/$SNAPROOT/$TESTSNAP $TESTDIR/$SNAPROOT/snap_a
+
+datasetexists $TESTPOOL/$TESTFS@snap_a || \
+ log_fail "rename snapshot via mv in $(get_snapdir_name) fails."
+log_must $ZFS destroy $TESTPOOL/$TESTFS@snap_a
+
+# scenario 2
+
+log_must $ZFS snapshot $SNAPFS
+log_must $ZFS clone $SNAPFS $TESTPOOL/$TESTCLONE
+log_must $MV $TESTDIR/$SNAPROOT/$TESTSNAP $TESTDIR/$SNAPROOT/snap_a
+
+datasetexists $TESTPOOL/$TESTFS@snap_a || \
+ log_fail "rename snapshot via mv in $(get_snapdir_name) fails."
+log_must $ZFS promote $TESTPOOL/$TESTCLONE
+# promote back to $TESTPOOL/$TESTFS for scenario 3
+log_must $ZFS promote $TESTPOOL/$TESTFS
+log_must $ZFS destroy $TESTPOOL/$TESTCLONE
+log_must $ZFS destroy $TESTPOOL/$TESTFS@snap_a
+
+# scenario 3
+
+log_must $ZFS snapshot $SNAPFS
+log_must $ZFS clone $SNAPFS $TESTPOOL/$TESTCLONE
+log_must $ZFS rename $SNAPFS $TESTPOOL/$TESTFS@snap_a
+log_must $ZFS promote $TESTPOOL/$TESTCLONE
+log_must $ZFS destroy $TESTPOOL/$TESTFS
+log_must $ZFS destroy $TESTPOOL/$TESTCLONE@snap_a
+
+log_pass "Verify renamed snapshots via mv can be destroyed."
diff --git a/tests/sys/cddl/zfs/tests/snapshot/snapshot_017_pos.ksh b/tests/sys/cddl/zfs/tests/snapshot/snapshot_017_pos.ksh
new file mode 100644
index 000000000000..419b6f09c780
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/snapshot_017_pos.ksh
@@ -0,0 +1,208 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: snapshot_017_pos
+#
+# DESCRIPTION:
+#
+# Directory structure of snapshots reflects filesystem structure.
+#
+# STRATEGY:
+#
+# This test makes sure that the directory structure of snapshots is
+# a proper reflection of the filesystem the snapshot was taken of.
+#
+# 1. Create a simple directory structure of files and directories
+# 2. Take a snapshot of the filesystem
+# 3. Modify original filesystem
+# 4. Walk down the snapshot directory structure verifying it
+# checking with both absolute and relative paths
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-05-31)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ cd $SAVED_DIR
+
+ if datasetexists $TESTPOOL/$TESTFS ; then
+ log_must $ZFS destroy -Rf $TESTPOOL/$TESTFS
+ fi
+
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+}
+
+function verify_structure {
+
+ # check absolute paths
+ DIR=$PWD
+ verify_file $DIR/file1
+ verify_file $DIR/file2
+ verify_file $DIR/dir1/file3
+ verify_file $DIR/dir1/file4
+ verify_file $DIR/dir1/dir2/file5
+ verify_file $DIR/dir1/dir2/file6
+
+ verify_no_file $DIR/file99
+
+ # check relative paths
+ verify_file ./file1
+ verify_file ./file2
+ verify_file ./dir1/file3
+ verify_file ./dir1/file4
+ verify_file ./dir1/dir2/file5
+ verify_file ./dir1/dir2/file6
+
+ cd dir1
+ verify_file ../file1
+ verify_file ../file2
+ verify_file ./file3
+ verify_file ./file4
+
+ verify_no_file ../file99
+
+ cd dir2
+ verify_file ./file5
+ verify_file ./file6
+ verify_file ../file3
+ verify_file ../file4
+ verify_no_file ../file99
+
+ verify_file ../../file1
+ verify_file ../../file2
+ verify_no_file ../../file99
+}
+
+function verify_file {
+ if [ ! -e $1 ]
+ then
+ log_note "Working dir is $PWD"
+ log_fail "File $1 does not exist!"
+ fi
+}
+
+function verify_no_file {
+ if [ -e $1 ]
+ then
+ log_note "Working dir is $PWD"
+ log_fail "File $1 exists when it should not!"
+ fi
+}
+
+function verify_dir {
+ if [ ! -d $1 ]
+ then
+ log_note "Working dir is $PWD"
+ log_fail "Directory $1 does not exist!"
+ fi
+}
+
+log_assert "Directory structure of snapshots reflects filesystem structure."
+log_onexit cleanup
+
+SAVED_DIR=$PWD
+
+#
+# Create a directory structure with the following files
+#
+# ./file1
+# ./file2
+# ./dir1/file3
+# ./dir1/file4
+# ./dir1/dir2/file5
+# ./dir1/dir2/file6
+
+cd $TESTDIR
+$TOUCH file1
+$TOUCH file2
+$MKDIR dir1
+cd dir1
+$TOUCH file3
+$TOUCH file4
+$MKDIR dir2
+cd dir2
+$TOUCH file5
+$TOUCH file6
+
+# Now walk the directory structure verifying it
+cd $TESTDIR
+verify_structure
+
+# Take snapshots
+log_must $ZFS snapshot $TESTPOOL/$TESTFS@snap_a
+log_must $ZFS snapshot $TESTPOOL/$TESTFS@snap_b
+
+# Change the filesystem structure by renaming files in the original structure
+# The snapshot file structure should not change
+cd $TESTDIR
+log_must $MV file2 file99
+cd dir1
+log_must $MV file4 file99
+cd dir2
+log_must $MV file6 file99
+
+# verify the top level snapshot directories
+verify_dir $TESTDIR/$(get_snapdir_name)
+verify_dir $TESTDIR/$(get_snapdir_name)
+verify_dir $TESTDIR/$(get_snapdir_name)/snap_a
+verify_dir $TESTDIR/$(get_snapdir_name)/snap_b
+
+cd $TESTDIR/$(get_snapdir_name)/snap_a
+verify_structure
+
+cd $TESTDIR/$(get_snapdir_name)/snap_b
+verify_structure
+
+verify_dir $TESTDIR/$(get_snapdir_name)
+cd $TESTDIR/$(get_snapdir_name)
+verify_dir snap_a
+verify_dir snap_b
+
+cd snap_a
+verify_dir ../snap_a
+verify_dir ../snap_b
+
+cd ..
+verify_dir snap_a
+verify_dir snap_b
+
+log_pass "Directory structure of snapshots reflects filesystem structure."
diff --git a/tests/sys/cddl/zfs/tests/snapshot/snapshot_018_pos.ksh b/tests/sys/cddl/zfs/tests/snapshot/snapshot_018_pos.ksh
new file mode 100644
index 000000000000..1c7f2f77a6a4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/snapshot_018_pos.ksh
@@ -0,0 +1,93 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+#
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: snapshot_018_pos
+#
+# DESCRIPTION:
+# Snapshot directory supports ACL operations
+#
+# STRATEGY:
+# 1. Create a dataset
+# 2. Set the snapdir property to visible
+# 3. Use getconf to verify that acls are supported
+# 4. Use getfacl to verify that the acl can be read
+# 5. Verify that the acl is correct
+# 6. Verify that ls doesn't print any errors because acl_get_link fails
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2013-01-03)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+log_assert "Verify that the snapshot directory supports ACL operations"
+
+log_must dataset_setprop $TESTPOOL/$TESTFS "snapdir" "visible"
+
+function verify_dir # directory
+{
+ typeset DIR=$1
+ # 3. Verify that ACLs are supported by the ctldir
+ log_must $GETCONF TRUSTEDBSD_ACL_EXTENDED $DIR
+ # 4. Verify that we can read ACLs
+ log_must $GETFACL $DIR
+ # 5. Verify that the ACLs are correct
+ typeset PROG_DIR=$STF_SUITE/tests/snapshot
+ typeset SUM=$($SHA1 -q $PROG_DIR/ctldir_acl.txt)
+ if [[ $( $GETFACL -q $DIR | $SHA1 -q ) != $SUM ]]; then
+ log_fail "ACL is incorrect"
+ fi
+
+}
+
+typeset -a dirlist
+dirlist=( "$TESTDIR/.zfs" "$TESTDIR/.zfs/snapshot" )
+ctldir=".zfs"
+for d in ${dirlist[@]}; do
+ verify_dir $d
+done
+
+# 6. Check for errors with ls
+LS_STDERR=$( $LS -la $TESTDIR/$ctldir 2>&1 > /dev/null)
+LS_R_STDERR=$( $LS -lar $TESTDIR/$ctldir 2>&1 > /dev/null)
+if [[ -n $LS_STDERR || -n $LS_R_STDERR ]]; then
+ log_fail "ls encountered errors"
+fi
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/snapshot/snapshot_019_pos.ksh b/tests/sys/cddl/zfs/tests/snapshot/snapshot_019_pos.ksh
new file mode 100644
index 000000000000..1e259cc43b63
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/snapshot_019_pos.ksh
@@ -0,0 +1,117 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2013 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+#
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: snapshot_019_pos
+#
+# DESCRIPTION:
+# Accessing snapshots and unmounting them in parallel does not panic.
+# FreeBSD PR kern/184677
+#
+# STRATEGY:
+# 1. Create a dataset
+# 2. Set the snapdir property to visible
+# 3. Do the following in parallel
+# a. Repeatedly access the snapshot
+# b. Repeatedly unmount the snapshot
+# 4. Verify that the system does not panic
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2013-12-23)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+KILL_SWITCH=${PWD}/kill_switch
+
+function stat_snapshot
+{
+ while [ ! -f ${KILL_SWITCH} ]; do
+ cd $SNAPDIR # Exercise forced unmount
+ stat "$SNAPDIR" > /dev/null 2>&1
+ done
+}
+
+function ls_snapshot
+{
+ # Pre-generate the argument list.
+ ls_args=""
+ for ((num=0; $num<100; num=$num+1)); do
+ ls_args="$ls_args $SNAPDIR/.."
+ done
+
+ while [ ! -f ${KILL_SWITCH} ]; do
+ ls $ls_args >/dev/null 2>&1
+ done
+}
+
+log_assert "Accessing snapshots and unmounting them in parallel does not panic"
+
+log_must dataset_setprop $TESTPOOL "snapdir" "visible"
+
+# Take snapshots
+log_must $ZFS snapshot "$TESTPOOL/$TESTFS@$TESTSNAP"
+
+# Repeatedly access the snapshot directory
+stat_snapshot &
+stat_pid="$!"
+ls_snapshot &
+ls_pid="$!"
+
+# Repeatedly unmount the snapshot directory
+for ((i=0; $i<100; i=$i+1)); do
+ umount "$SNAPDIR" >/dev/null 2>&1
+ log_note "$i non-forced done"
+ # Sleep just long enough for the other "threads" to remount.
+ sleep 0.1
+ umount -f "$SNAPDIR" >/dev/null 2>&1
+ log_note "$i forced done"
+ sleep 0.1
+done
+
+# Kill the other "threads" and wait for them to die.
+touch $KILL_SWITCH
+log_note "Waiting for all child processes to die..."
+wait
+
+# Test that no reference leaks occurred and we can cleanup without forcing.
+log_must $ZFS unmount $TESTPOOL/$TESTFS
+log_must $ZFS destroy -r $TESTPOOL/$TESTFS
+
+# If we get here, we managed to not panic, deadlock, or leak references.
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/snapshot/snapshot_020_pos.ksh b/tests/sys/cddl/zfs/tests/snapshot/snapshot_020_pos.ksh
new file mode 100644
index 000000000000..7759782d8cd9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/snapshot_020_pos.ksh
@@ -0,0 +1,87 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2014 Spectra Logic Corporation
+#
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: snapshot_020_pos
+#
+# DESCRIPTION:
+# Verify that snapshots can be mounted in the ctldir, then renamed, then
+# destroyed. The original bug that this regresses was that the rename
+# command failed to unmount the snapshot from its old location, leaving
+# the user unable to either mount it or destroy it.
+#
+# STRATEGY:
+# 1. Create snapshot
+# 2. Access the snapshot through the ctldir to automount it
+# 3. Rename the snapshot
+# 4. Destroy the snapshot
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2014-06-02)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ datasetexists $SNAPFS && \
+ log_must $ZFS destroy -Rf $SNAPFS
+ datasetexists $TESTPOOL/$TESTFS@snap_a && \
+ log_must $ZFS destroy -Rf $TESTPOOL/$TESTFS@snap_a
+ datasetexists $TESTPOOL/$TESTCLONE@snap_a && \
+ log_must $ZFS destroy -Rf $TESTPOOL/$TESTCLONE@snap_a
+
+ datasetexists $TESTPOOL/$TESTCLONE && \
+ log_must $ZFS destroy $TESTPOOL/$TESTCLONE
+ datasetexists $TESTPOOL/$TESTFS && \
+ log_must $ZFS destroy $TESTPOOL/$TESTFS
+
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+}
+
+log_assert "Verify that mounted snapshots can be renamed and destroyed"
+log_onexit cleanup
+
+log_must $ZFS set snapdir=visible $TESTPOOL/$TESTFS
+log_must $ZFS snapshot $SNAPFS
+log_must stat $TESTDIR/$SNAPROOT/$TESTSNAP
+log_must $ZFS rename $SNAPFS $TESTPOOL/$TESTFS@$TESTSNAP1
+log_must stat $TESTDIR/$SNAPROOT/$TESTSNAP1
+log_must $ZFS destroy $TESTPOOL/$TESTFS@$TESTSNAP1
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/snapshot/snapshot_test.sh b/tests/sys/cddl/zfs/tests/snapshot/snapshot_test.sh
new file mode 100755
index 000000000000..d00365eec977
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapshot/snapshot_test.sh
@@ -0,0 +1,632 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case clone_001_pos cleanup
+clone_001_pos_head()
+{
+ atf_set "descr" "Verify a cloned file system is writable."
+ atf_set "require.progs" "ksh93 zfs"
+}
+clone_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ verify_zvol_recursive
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/clone_001_pos.ksh || atf_fail "Testcase failed"
+}
+clone_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case rollback_001_pos cleanup
+rollback_001_pos_head()
+{
+ atf_set "descr" "Verify that a rollback to a previous snapshot succeeds."
+ atf_set "require.progs" "ksh93 zfs"
+}
+rollback_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/rollback_001_pos.ksh || atf_fail "Testcase failed"
+}
+rollback_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case rollback_002_pos cleanup
+rollback_002_pos_head()
+{
+ atf_set "descr" "Verify rollback is with respect to latest snapshot."
+ atf_set "require.progs" "ksh93 zfs"
+}
+rollback_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/rollback_002_pos.ksh || atf_fail "Testcase failed"
+}
+rollback_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case rollback_003_pos cleanup
+rollback_003_pos_head()
+{
+ atf_set "descr" "Verify rollback succeeds when there are nested file systems."
+ atf_set "require.progs" "ksh93 zfs"
+}
+rollback_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/rollback_003_pos.ksh || atf_fail "Testcase failed"
+}
+rollback_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case snapshot_001_pos cleanup
+snapshot_001_pos_head()
+{
+ atf_set "descr" "Verify a file system snapshot is identical to original."
+ atf_set "require.progs" "ksh93 zfs sum"
+}
+snapshot_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/snapshot_001_pos.ksh || atf_fail "Testcase failed"
+}
+snapshot_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case snapshot_002_pos cleanup
+snapshot_002_pos_head()
+{
+ atf_set "descr" "Verify an archive of a file system is identical toan archive of its snapshot."
+ atf_set "require.progs" "ksh93 zfs"
+}
+snapshot_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/snapshot_002_pos.ksh || atf_fail "Testcase failed"
+}
+snapshot_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case snapshot_003_pos cleanup
+snapshot_003_pos_head()
+{
+ atf_set "descr" "Verify many snapshots of a file system can be taken."
+ atf_set "require.progs" "ksh93 zfs"
+}
+snapshot_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/snapshot_003_pos.ksh || atf_fail "Testcase failed"
+}
+snapshot_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case snapshot_004_pos cleanup
+snapshot_004_pos_head()
+{
+ atf_set "descr" "Verify that a snapshot of an empty file system remains empty."
+ atf_set "require.progs" "ksh93 zfs"
+}
+snapshot_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/snapshot_004_pos.ksh || atf_fail "Testcase failed"
+}
+snapshot_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case snapshot_005_pos cleanup
+snapshot_005_pos_head()
+{
+ atf_set "descr" "Verify that a snapshot of a dataset is identical tothe original dataset."
+ atf_set "require.progs" "ksh93 zfs sum"
+}
+snapshot_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/snapshot_005_pos.ksh || atf_fail "Testcase failed"
+}
+snapshot_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case snapshot_006_pos cleanup
+snapshot_006_pos_head()
+{
+ atf_set "descr" "Verify that an archive of a dataset is identical toan archive of the dataset's snapshot."
+ atf_set "require.progs" "ksh93 zfs"
+}
+snapshot_006_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/snapshot_006_pos.ksh || atf_fail "Testcase failed"
+}
+snapshot_006_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case snapshot_007_pos cleanup
+snapshot_007_pos_head()
+{
+ atf_set "descr" "Verify that many snapshots can be made on a zfs dataset."
+ atf_set "require.progs" "ksh93 zfs"
+}
+snapshot_007_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/snapshot_007_pos.ksh || atf_fail "Testcase failed"
+}
+snapshot_007_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case snapshot_008_pos cleanup
+snapshot_008_pos_head()
+{
+ atf_set "descr" "Verify that destroying snapshots returns space to the pool."
+ atf_set "require.progs" "ksh93 zfs"
+}
+snapshot_008_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/snapshot_008_pos.ksh || atf_fail "Testcase failed"
+}
+snapshot_008_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case snapshot_009_pos cleanup
+snapshot_009_pos_head()
+{
+ atf_set "descr" "Verify snapshot -r can correctly create a snapshot tree."
+ atf_set "require.progs" "ksh93 zfs"
+}
+snapshot_009_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/snapshot_009_pos.ksh || atf_fail "Testcase failed"
+}
+snapshot_009_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case snapshot_010_pos cleanup
+snapshot_010_pos_head()
+{
+ atf_set "descr" "Verify 'destroy -r' can correctly destroy a snapshot subtree at any point."
+ atf_set "require.progs" "ksh93 zfs"
+}
+snapshot_010_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/snapshot_010_pos.ksh || atf_fail "Testcase failed"
+}
+snapshot_010_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case snapshot_011_pos cleanup
+snapshot_011_pos_head()
+{
+ atf_set "descr" "Verify that rollback to a snapshot created by snapshot -r succeeds."
+ atf_set "require.progs" "ksh93 zfs"
+}
+snapshot_011_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/snapshot_011_pos.ksh || atf_fail "Testcase failed"
+}
+snapshot_011_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case snapshot_012_pos cleanup
+snapshot_012_pos_head()
+{
+ atf_set "descr" "Verify that 'snapshot -r' can work with 'zfs promote'."
+ atf_set "require.progs" "ksh93 zfs"
+}
+snapshot_012_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/snapshot_012_pos.ksh || atf_fail "Testcase failed"
+}
+snapshot_012_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case snapshot_013_pos cleanup
+snapshot_013_pos_head()
+{
+ atf_set "descr" "Verify snapshots from 'snapshot -r' can be used for zfs send/recv"
+ atf_set "require.progs" "ksh93 zfs"
+}
+snapshot_013_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/snapshot_013_pos.ksh || atf_fail "Testcase failed"
+}
+snapshot_013_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case snapshot_014_pos cleanup
+snapshot_014_pos_head()
+{
+ atf_set "descr" "Verify creating/destroying snapshots do things clean"
+ atf_set "require.progs" "ksh93 zfs"
+}
+snapshot_014_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/snapshot_014_pos.ksh || atf_fail "Testcase failed"
+}
+snapshot_014_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case snapshot_015_pos cleanup
+snapshot_015_pos_head()
+{
+ atf_set "descr" "Verify snapshot can be created via mkdir in .zfs/snapshot."
+ atf_set "require.progs" "ksh93 zfs"
+}
+snapshot_015_pos_body()
+{
+ atf_expect_fail "Not all directory operations on the .zfs/snapshot directory are yet supported by FreeBSD"
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/snapshot_015_pos.ksh || atf_fail "Testcase failed"
+}
+snapshot_015_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case snapshot_016_pos cleanup
+snapshot_016_pos_head()
+{
+ atf_set "descr" "Verify renamed snapshots via mv can be destroyed."
+ atf_set "require.progs" "ksh93 zfs"
+}
+snapshot_016_pos_body()
+{
+ atf_expect_fail "Not all directory operations on the .zfs/snapshot directory are yet supported by FreeBSD"
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/snapshot_016_pos.ksh || atf_fail "Testcase failed"
+}
+snapshot_016_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case snapshot_017_pos cleanup
+snapshot_017_pos_head()
+{
+ atf_set "descr" "Directory structure of snapshots reflects filesystem structure."
+ atf_set "require.progs" "ksh93 zfs"
+}
+snapshot_017_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/snapshot_017_pos.ksh || atf_fail "Testcase failed"
+}
+snapshot_017_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case snapshot_018_pos cleanup
+snapshot_018_pos_head()
+{
+ atf_set "descr" "Snapshot directory supports ACL operations"
+ atf_set "require.progs" "ksh93 zfs getfacl getconf sha1"
+}
+snapshot_018_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/snapshot_018_pos.ksh || atf_fail "Testcase failed"
+}
+snapshot_018_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case snapshot_019_pos cleanup
+snapshot_019_pos_head()
+{
+ atf_set "descr" "Accessing snapshots and unmounting them in parallel does not panic"
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1200
+}
+snapshot_019_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/snapshot_019_pos.ksh || atf_fail "Testcase failed"
+}
+snapshot_019_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case snapshot_020_pos cleanup
+snapshot_020_pos_head()
+{
+ atf_set "descr" "Verify mounted snapshots can be renamed and destroyed"
+ atf_set "require.progs" "ksh93 zfs"
+}
+snapshot_020_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/snapshot_020_pos.ksh || atf_fail "Testcase failed"
+}
+snapshot_020_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapshot.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+ atf_add_test_case clone_001_pos
+ atf_add_test_case rollback_001_pos
+ atf_add_test_case rollback_002_pos
+ atf_add_test_case rollback_003_pos
+ atf_add_test_case snapshot_001_pos
+ atf_add_test_case snapshot_002_pos
+ atf_add_test_case snapshot_003_pos
+ atf_add_test_case snapshot_004_pos
+ atf_add_test_case snapshot_005_pos
+ atf_add_test_case snapshot_006_pos
+ atf_add_test_case snapshot_007_pos
+ atf_add_test_case snapshot_008_pos
+ atf_add_test_case snapshot_009_pos
+ atf_add_test_case snapshot_010_pos
+ atf_add_test_case snapshot_011_pos
+ atf_add_test_case snapshot_012_pos
+ atf_add_test_case snapshot_013_pos
+ atf_add_test_case snapshot_014_pos
+ atf_add_test_case snapshot_015_pos
+ atf_add_test_case snapshot_016_pos
+ atf_add_test_case snapshot_017_pos
+ atf_add_test_case snapshot_018_pos
+ atf_add_test_case snapshot_019_pos
+ atf_add_test_case snapshot_020_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/snapused/Makefile b/tests/sys/cddl/zfs/tests/snapused/Makefile
new file mode 100644
index 000000000000..f831ac5dc699
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapused/Makefile
@@ -0,0 +1,21 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/snapused
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= snapused_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= snapused_005_pos.ksh
+${PACKAGE}FILES+= snapused_001_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= snapused_004_pos.ksh
+${PACKAGE}FILES+= snapused.kshlib
+${PACKAGE}FILES+= snapused.cfg
+${PACKAGE}FILES+= snapused_002_pos.ksh
+${PACKAGE}FILES+= snapused_003_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/snapused/cleanup.ksh b/tests/sys/cddl/zfs/tests/snapused/cleanup.ksh
new file mode 100644
index 000000000000..bb9e846c9e9a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapused/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/snapused/setup.ksh b/tests/sys/cddl/zfs/tests/snapused/setup.ksh
new file mode 100644
index 000000000000..014cf03b1713
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapused/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/snapused/snapused.cfg b/tests/sys/cddl/zfs/tests/snapused/snapused.cfg
new file mode 100644
index 000000000000..d5a0cf11d047
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapused/snapused.cfg
@@ -0,0 +1,29 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+export USEDTEST=$TESTPOOL/$TESTFS/usedtest${TESTCASE_ID}
diff --git a/tests/sys/cddl/zfs/tests/snapused/snapused.kshlib b/tests/sys/cddl/zfs/tests/snapused/snapused.kshlib
new file mode 100644
index 000000000000..fd03ee41bd3f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapused/snapused.kshlib
@@ -0,0 +1,186 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+function usedby_supported
+{
+ fs_prop_exist "usedbysnapshots"
+ return $?
+}
+
+function _check_used # dataset
+{
+ typeset dataset=$1
+
+ if [[ "$(get_prop type $dataset)" == "snapshot" ]]; then
+ return
+ fi
+
+ used=$(get_prop used $dataset)
+ usedbychildren=$(get_prop usedbychildren $dataset)
+ usedbydataset=$(get_prop usedbydataset $dataset)
+ usedbyrefreservation=$(get_prop usedbyrefreservation $dataset)
+ usedbysnapshots=$(get_prop usedbysnapshots $dataset)
+ (( used_sum = usedbychildren + usedbydataset + \
+ usedbyrefreservation + usedbysnapshots ))
+ if (( used != used_sum )); then
+ log_fail "$dataset: used($used) is not the sum($used_sum) of usedby*"
+ fi
+}
+
+function check_used # dataset
+{
+ typeset dataset=$1
+ for child in $($ZFS list -rH -t filesystem,volume -o name $dataset)
+ do
+ _check_used $child
+ done
+}
+
+function check_usedbychildren # dataset
+{
+ typeset dataset=$1
+ typeset -i usedbychildren_sum=0
+ typeset -i parent_usedbychildren=0
+ for child in $($ZFS list -rH -t filesystem,volume -o name $dataset)
+ do
+ if [[ "$(get_prop type $child)" == "snapshot" ]]; then
+ continue
+ fi
+
+ # parent
+ if [[ "$child" == "$dataset" ]]; then
+ parent_usedbychildren=$(get_prop usedbychildren $child)
+ else #child
+ reservation=$(get_prop reservation $child)
+ used=$(get_prop used $child)
+ if (( reservation > used )); then
+ (( usedbychildren_sum += reservation ))
+ else
+ (( usedbychildren_sum += used ))
+ fi
+ fi
+ done
+
+ if (( parent_usedbychildren != usedbychildren_sum )); then
+ log_fail "$dataset: usedbychildren($parent_usedbychildren) is not the sum($usedbychildren_sum) of used by children"
+ fi
+}
+
+function _check_usedbydataset # dataset
+{
+ typeset dataset=$1
+ if [[ "$(get_prop type $dataset)" == "snapshot" ]]; then
+ return
+ fi
+
+ usedbydataset=$(get_prop usedbydataset $dataset)
+ referenced=$(get_prop referenced $dataset)
+
+ is_cloned=$(get_prop is:cloned $dataset)
+
+ if [[ "$is_cloned" == "yes" ]]; then
+ if (( usedbydataset > referenced )); then
+ log_fail "$dataset(cloned): usedbydataset($usedbydataset) is more than referenced($referenced)"
+ fi
+ else
+ #
+ # if non-clones, should usedbydataset == referenced
+ #
+ if (( usedbydataset != referenced )); then
+ log_fail "$dataset: usedbydataset($usedbydataset) is not equal to referenced($referenced)"
+ fi
+ fi
+}
+
+function check_usedbydataset # dataset
+{
+ typeset dataset=$1
+ for child in $($ZFS list -rH -t filesystem,volume -o name $dataset)
+ do
+ _check_usedbydataset $child
+ done
+}
+
+function _check_usedbyrefreservation # dataset
+{
+ typeset dataset=$1
+ if [[ "$(get_prop type $dataset)" == "snapshot" ]]; then
+ return
+ fi
+
+ usedbyrefreservation=$(get_prop usedbyrefreservation $dataset)
+ referenced=$(get_prop referenced $dataset)
+ refreservation=$(get_prop refreservation $dataset)
+ (( diff_ref = refreservation - referenced ))
+ if (( usedbyrefreservation > refreservation || \
+ usedbyrefreservation < diff_ref )); then
+ log_fail "$dataset: usedbyrefreservation($usedbyrefreservation) checking is not ok"
+ fi
+}
+
+function check_usedbyrefreservation # dataset
+{
+ typeset dataset=$1
+ for child in $($ZFS list -rH -t filesystem,volume -o name $dataset)
+ do
+ _check_usedbyrefreservation $child
+ done
+}
+
+function check_usedbysnapshots # dataset
+{
+ typeset dataset=$1
+ typeset -i usedbysnapshots_sum=0
+ typeset -i parent_usedbysnapshots=0
+ for child in $($ZFS list -rH -t filesystem,volume,snapshot -o name $dataset)
+ do
+ # parent
+ if [[ "$child" == "$dataset" ]]; then
+ parent_usedbysnapshots=$(get_prop usedbysnapshots $child)
+ continue
+ fi
+
+ if [[ "$(get_prop type $child)" != "snapshot" ]]; then
+ continue
+ fi
+
+ if [[ "$child" != "$dataset@"* ]]; then
+ continue
+ fi
+
+ # snapshot
+ used=$(get_prop used $child)
+ (( usedbysnapshots_sum += used ))
+ done
+
+ if (( parent_usedbysnapshots < usedbysnapshots_sum )); then
+ log_fail "$dataset: usedbysnapshots($parent_usedbysnapshots) is not more than or equal to" \
+ "the sum($usedbysnapshots_sum) of used of snapshots"
+ fi
+}
+
diff --git a/tests/sys/cddl/zfs/tests/snapused/snapused_001_pos.ksh b/tests/sys/cddl/zfs/tests/snapused/snapused_001_pos.ksh
new file mode 100644
index 000000000000..ddcf70da2460
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapused/snapused_001_pos.ksh
@@ -0,0 +1,105 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/snapused/snapused.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: snapused_001_pos
+#
+# DESCRIPTION:
+# Verify used is correct.
+#
+# STRATEGY:
+# 1. Create a filesystem.
+# 2. Set refreservation of the filesystem.
+# 3. Make file in the filesystem.
+# 4. Create sub filesystem and make file in it.
+# 5. Create volume under it.
+# 6. Snapshot it.
+# 7. Check used=usedbychildren+usedbydataset+
+# usedbyrefreservation+usedbysnapshots.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-04-28)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+if ! usedby_supported ; then
+ log_unsupported "snapused property is not supported."
+fi
+
+verify_runnable "both"
+
+function cleanup
+{
+ log_must $ZFS destroy -rR $USEDTEST
+}
+
+log_assert "Verify used is correct."
+log_onexit cleanup
+
+log_must $ZFS create $USEDTEST
+check_used $USEDTEST
+
+typeset -i i=0
+typeset -i r_size=0
+mntpnt=$(get_prop mountpoint $USEDTEST)
+while (( i < 5 )); do
+ ((r_size=(i+1)*16))
+
+ #usedbyrefreservation
+ log_must $ZFS set refreservation="$r_size"M $USEDTEST
+
+ #usedbydataset
+ log_must $MKFILE 16M $mntpnt/file$i
+
+ #usedbychildren
+ log_must $ZFS create $USEDTEST/fs$i
+ log_must $MKFILE 16M $mntpnt/fs$i/file$i
+
+ if is_global_zone; then
+ log_must $ZFS create -V 16M $USEDTEST/vol$i
+ fi
+
+ #usedbysnapshots
+ log_must $ZFS snapshot -r $USEDTEST@snap$i
+
+ check_used $USEDTEST
+
+ ((i = i + 1))
+done
+
+log_pass "Verify used is correct."
+
diff --git a/tests/sys/cddl/zfs/tests/snapused/snapused_002_pos.ksh b/tests/sys/cddl/zfs/tests/snapused/snapused_002_pos.ksh
new file mode 100644
index 000000000000..2eebeda8efd9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapused/snapused_002_pos.ksh
@@ -0,0 +1,96 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/snapused/snapused.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: snapused_002_pos
+#
+# DESCRIPTION:
+# Verify usedbychildren is correct.
+#
+# STRATEGY:
+# 1. Create a filesystem.
+# 2. Create sub filesystem and make file in it.
+# 3. Set reservation of the sub filesystem.
+# 4. Create volume under it.
+# 5. Snapshot it.
+# 6. Check usedbychildren is correct.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-04-28)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+if ! usedby_supported ; then
+ log_unsupported "snapused property is not supported."
+fi
+
+verify_runnable "both"
+
+function cleanup
+{
+ log_must $ZFS destroy -rR $USEDTEST
+}
+
+log_assert "Verify usedbychildren is correct."
+log_onexit cleanup
+
+log_must $ZFS create $USEDTEST
+check_usedbychildren $USEDTEST
+
+typeset -i i=0
+typeset -i r_size=0
+mntpnt=$(get_prop mountpoint $USEDTEST)
+while (( i < 5 )); do
+ ((r_size=(i+1)*16))
+
+ log_must $ZFS create $USEDTEST/fs$i
+ log_must $ZFS set reservation="$r_size"M $USEDTEST/fs$i
+ log_must $MKFILE 48M $mntpnt/fs$i/file$i
+
+ if is_global_zone; then
+ log_must $ZFS create -V 32M $USEDTEST/vol$i
+ fi
+
+ log_must $ZFS snapshot -r $USEDTEST@snap$i
+
+ check_usedbychildren $USEDTEST
+
+ ((i = i + 1))
+done
+
+log_pass "Verify usedbychildren is correct."
+
diff --git a/tests/sys/cddl/zfs/tests/snapused/snapused_003_pos.ksh b/tests/sys/cddl/zfs/tests/snapused/snapused_003_pos.ksh
new file mode 100644
index 000000000000..8a067b997d1f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapused/snapused_003_pos.ksh
@@ -0,0 +1,96 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/snapused/snapused.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: snapused_003_pos
+#
+# DESCRIPTION:
+# Verify usedbydataset is correct.
+#
+# STRATEGY:
+# 1. Create a filesystem.
+# 2. Make file in the filesystem.
+# 3. Snapshot it.
+# 4. Clone it and make file in the cloned filesystem.
+# 5. Check usedbydataset is correct.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-04-28)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+if ! usedby_supported ; then
+ log_unsupported "snapused property is not supported."
+fi
+
+verify_runnable "both"
+
+function cleanup
+{
+ log_must $ZFS destroy -rR $USEDTEST
+}
+
+log_assert "Verify usedbydataset is correct."
+log_onexit cleanup
+
+log_must $ZFS create $USEDTEST
+check_usedbydataset $USEDTEST
+
+typeset -i i=0
+typeset -i r_size=0
+mntpnt=$(get_prop mountpoint $USEDTEST)
+while (( i < 5 )); do
+ ((r_size=(i+1)*16))
+
+ log_must $MKFILE 16M $mntpnt/file$i
+ log_must $MKFILE "$r_size"M $mntpnt/file_var$i
+ log_must $ZFS snapshot -r $USEDTEST@snap$i
+
+ log_must $ZFS clone $USEDTEST@snap$i $USEDTEST/cln$i
+ log_must $ZFS set is:cloned=yes $USEDTEST/cln$i
+
+ mntpnt_cln=$(get_prop mountpoint $USEDTEST/cln$i)
+ log_must $MKFILE 16M $mntpnt_cln/file_cln$i
+ log_must $MKFILE "$r_size"M $mntpnt_cln/file_cln_var$i
+
+ check_usedbydataset $USEDTEST
+
+ ((i = i + 1))
+done
+
+log_pass "Verify usedbydataset is correct."
+
diff --git a/tests/sys/cddl/zfs/tests/snapused/snapused_004_pos.ksh b/tests/sys/cddl/zfs/tests/snapused/snapused_004_pos.ksh
new file mode 100644
index 000000000000..22acd6a3f376
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapused/snapused_004_pos.ksh
@@ -0,0 +1,109 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/snapused/snapused.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: snapused_004_pos
+#
+# DESCRIPTION:
+# Verify usedbyrefreservation is correct.
+#
+# STRATEGY:
+# 1. Create a filesystem.
+# 2. Set refreservation of the filesystem.
+# 3. Make file in the filesystem.
+# 4. Create sub filesystem and make file in it.
+# 5. Set refreservation of the sub filesystem.
+# 6. Create volume under it.
+# 7. Snapshot it.
+# 8. Clone it and set refreservation of the cloned filesystem.
+# 9. Makefile the cloned filesystem.
+# 10. Check usedbyrefreservation is correct.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-04-28)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+if ! usedby_supported ; then
+ log_unsupported "snapused property is not supported."
+fi
+
+verify_runnable "both"
+
+function cleanup
+{
+ log_must $ZFS destroy -rR $USEDTEST
+}
+
+log_assert "Verify usedbyrefreservation is correct."
+log_onexit cleanup
+
+log_must $ZFS create $USEDTEST
+check_usedbyrefreservation $USEDTEST
+
+typeset -i i=0
+typeset -i r_size=0
+mntpnt=$(get_prop mountpoint $USEDTEST)
+while (( i < 5 )); do
+ ((r_size=(i+1)*16))
+ log_must $ZFS set refreservation="$r_size"M $USEDTEST
+
+ log_must $MKFILE 16M $mntpnt/file$i
+
+ log_must $ZFS create $USEDTEST/fs$i
+ log_must $ZFS set refreservation="$r_size"M $USEDTEST/fs$i
+ log_must $MKFILE 16M $mntpnt/fs$i/file$i
+
+ if is_global_zone; then
+ log_must $ZFS create -V 16M $USEDTEST/vol$i
+ fi
+
+ log_must $ZFS snapshot -r $USEDTEST@snap$i
+
+ log_must $ZFS clone $USEDTEST@snap$i $USEDTEST/cln$i
+
+ mntpnt_cln=$(get_prop mountpoint $USEDTEST/cln$i)
+ log_must $ZFS set refreservation="$r_size"M $USEDTEST/cln$i
+ log_must $MKFILE 16M $mntpnt_cln/file_cln$i
+
+ check_usedbyrefreservation $USEDTEST
+
+ ((i = i + 1))
+done
+
+log_pass "Verify usedbyrefreservation is correct."
+
diff --git a/tests/sys/cddl/zfs/tests/snapused/snapused_005_pos.ksh b/tests/sys/cddl/zfs/tests/snapused/snapused_005_pos.ksh
new file mode 100644
index 000000000000..9df109204ecc
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapused/snapused_005_pos.ksh
@@ -0,0 +1,87 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/snapused/snapused.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: snapused_005_pos
+#
+# DESCRIPTION:
+# Verify usedbysnapshots is correct.
+#
+# STRATEGY:
+# 1. Create a filesystem.
+# 2. Make file in the filesystem.
+# 3. Snapshot it.
+# 4. Check check_usedbysnapshots is correct.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2009-04-28)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+if ! usedby_supported ; then
+ log_unsupported "snapused property is not supported."
+fi
+
+verify_runnable "both"
+
+function cleanup
+{
+ log_must $ZFS destroy -rR $USEDTEST
+}
+
+log_assert "Verify usedbysnapshots is correct."
+log_onexit cleanup
+
+log_must $ZFS create $USEDTEST
+check_usedbysnapshots $USEDTEST
+
+typeset -i i=0
+typeset -i r_size=0
+mntpnt=$(get_prop mountpoint $USEDTEST)
+while (( i < 5 )); do
+ ((r_size=(i+1)*16))
+
+ log_must $MKFILE "$r_size"M $mntpnt/file$i
+
+ log_must $ZFS snapshot $USEDTEST@snap$i
+ check_usedbysnapshots $USEDTEST
+
+ ((i = i + 1))
+done
+
+log_pass "Verify usedbysnapshots is correct."
+
diff --git a/tests/sys/cddl/zfs/tests/snapused/snapused_test.sh b/tests/sys/cddl/zfs/tests/snapused/snapused_test.sh
new file mode 100755
index 000000000000..5a28c4401c13
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/snapused/snapused_test.sh
@@ -0,0 +1,165 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case snapused_001_pos cleanup
+snapused_001_pos_head()
+{
+ atf_set "descr" "Verify used is correct."
+ atf_set "require.progs" "ksh93 zfs"
+}
+snapused_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapused.kshlib
+ . $(atf_get_srcdir)/snapused.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/snapused_001_pos.ksh || atf_fail "Testcase failed"
+}
+snapused_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapused.kshlib
+ . $(atf_get_srcdir)/snapused.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case snapused_002_pos cleanup
+snapused_002_pos_head()
+{
+ atf_set "descr" "Verify usedbychildren is correct."
+ atf_set "require.progs" "ksh93 zfs"
+}
+snapused_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapused.kshlib
+ . $(atf_get_srcdir)/snapused.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/snapused_002_pos.ksh || atf_fail "Testcase failed"
+}
+snapused_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapused.kshlib
+ . $(atf_get_srcdir)/snapused.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case snapused_003_pos cleanup
+snapused_003_pos_head()
+{
+ atf_set "descr" "Verify usedbydataset is correct."
+ atf_set "require.progs" "ksh93 zfs"
+}
+snapused_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapused.kshlib
+ . $(atf_get_srcdir)/snapused.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/snapused_003_pos.ksh || atf_fail "Testcase failed"
+}
+snapused_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapused.kshlib
+ . $(atf_get_srcdir)/snapused.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case snapused_004_pos cleanup
+snapused_004_pos_head()
+{
+ atf_set "descr" "Verify usedbyrefreservation is correct."
+ atf_set "require.progs" "ksh93 zfs"
+}
+snapused_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapused.kshlib
+ . $(atf_get_srcdir)/snapused.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/snapused_004_pos.ksh || atf_fail "Testcase failed"
+}
+snapused_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapused.kshlib
+ . $(atf_get_srcdir)/snapused.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case snapused_005_pos cleanup
+snapused_005_pos_head()
+{
+ atf_set "descr" "Verify usedbysnapshots is correct."
+ atf_set "require.progs" "ksh93 zfs"
+}
+snapused_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapused.kshlib
+ . $(atf_get_srcdir)/snapused.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/snapused_005_pos.ksh || atf_fail "Testcase failed"
+}
+snapused_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/snapused.kshlib
+ . $(atf_get_srcdir)/snapused.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case snapused_001_pos
+ atf_add_test_case snapused_002_pos
+ atf_add_test_case snapused_003_pos
+ atf_add_test_case snapused_004_pos
+ atf_add_test_case snapused_005_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/sparse/Makefile b/tests/sys/cddl/zfs/tests/sparse/Makefile
new file mode 100644
index 000000000000..c606a58e3b40
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/sparse/Makefile
@@ -0,0 +1,16 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/sparse
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= sparse_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= sparse_001_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= sparse.cfg
+${PACKAGE}FILES+= cleanup.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/sparse/cleanup.ksh b/tests/sys/cddl/zfs/tests/sparse/cleanup.ksh
new file mode 100644
index 000000000000..b2b441e574d0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/sparse/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/sparse/setup.ksh b/tests/sys/cddl/zfs/tests/sparse/setup.ksh
new file mode 100644
index 000000000000..7c86ba0b6e28
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/sparse/setup.ksh
@@ -0,0 +1,32 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+
+disk=${DISKS%% *}
+
+default_setup $disk
diff --git a/tests/sys/cddl/zfs/tests/sparse/sparse.cfg b/tests/sys/cddl/zfs/tests/sparse/sparse.cfg
new file mode 100644
index 000000000000..0a190a10c205
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/sparse/sparse.cfg
@@ -0,0 +1,33 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export TESTFILE=testfile.${TESTCASE_ID}
+export HOLES_FILESIZE=${HOLES_FILESIZE-"67108864"} # 64 Mb
+export HOLES_BLKSIZE=${HOLES_BLKSIZE-"512"}
+export HOLES_SEED=${HOLES_SEED-""}
+export HOLES_FILEOFFSET=${HOLES_FILEOFFSET-""}
+export HOLES_COUNT=${HOLES_COUNT-"16384"} # FILESIZE/BLKSIZE/8
+export STF_TIMEOUT=3600
diff --git a/tests/sys/cddl/zfs/tests/sparse/sparse_001_pos.ksh b/tests/sys/cddl/zfs/tests/sparse/sparse_001_pos.ksh
new file mode 100644
index 000000000000..5638e287816b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/sparse/sparse_001_pos.ksh
@@ -0,0 +1,88 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: sparse_001_pos
+#
+# DESCRIPTION:
+# Holes in ZFS files work correctly.
+#
+# STRATEGY:
+# 1. Open file
+# 2. Write random blocks in random places
+# 3. Read each block back to check for correctness.
+# 4. Repeat steps 2 and 3 lots of times
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ [[ -e $TESTDIR ]] && log_must $RM -rf $TESTDIR/*
+}
+
+log_assert "Ensure random blocks are read back correctly"
+
+options=""
+options_display="default options"
+
+log_onexit cleanup
+
+[[ -n "$HOLES_FILESIZE" ]] && options=" $options -f $HOLES_FILESIZE "
+
+[[ -n "$HOLES_BLKSIZE" ]] && options="$options -b $HOLES_BLKSIZE "
+
+[[ -n "$HOLES_COUNT" ]] && options="$options -c $HOLES_COUNT "
+
+[[ -n "$HOLES_SEED" ]] && options="$options -s $HOLES_SEED "
+
+[[ -n "$HOLES_FILEOFFSET" ]] && options="$options -o $HOLES_FILEOFFSET "
+
+options="$options -r "
+
+[[ -n "$options" ]] && options_display=$options
+
+log_note "Invoking $FILE_TRUNC with: $options_display"
+log_must $FILE_TRUNC $options $TESTDIR/$TESTFILE
+
+typeset dir=$(get_device_dir $DISKS)
+verify_filesys "$TESTPOOL" "$TESTPOOL/$TESTFS" "$dir"
+
+log_pass "Random blocks have been read back correctly."
diff --git a/tests/sys/cddl/zfs/tests/sparse/sparse_test.sh b/tests/sys/cddl/zfs/tests/sparse/sparse_test.sh
new file mode 100755
index 000000000000..445e275ef71e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/sparse/sparse_test.sh
@@ -0,0 +1,56 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case sparse_001_pos cleanup
+sparse_001_pos_head()
+{
+ atf_set "descr" "Ensure random blocks are read back correctly"
+ atf_set "require.progs" "ksh93 zdb"
+ atf_set "timeout" 3600
+}
+sparse_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/sparse.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/sparse_001_pos.ksh || atf_fail "Testcase failed"
+}
+sparse_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/sparse.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case sparse_001_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/threadsappend/Makefile b/tests/sys/cddl/zfs/tests/threadsappend/Makefile
new file mode 100644
index 000000000000..fd179e27e715
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/threadsappend/Makefile
@@ -0,0 +1,20 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/threadsappend
+FILESDIR=${TESTSDIR}
+BINDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= threadsappend_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= threadsappend_001_pos.ksh
+${PACKAGE}FILES+= threadsappend.cfg
+${PACKAGE}FILES+= setup.ksh
+PROG+= threadsappend
+LIBADD+= pthread
+MAN=
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/threadsappend/cleanup.ksh b/tests/sys/cddl/zfs/tests/threadsappend/cleanup.ksh
new file mode 100644
index 000000000000..b2b441e574d0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/threadsappend/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/threadsappend/setup.ksh b/tests/sys/cddl/zfs/tests/threadsappend/setup.ksh
new file mode 100644
index 000000000000..f26277124261
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/threadsappend/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_setup ${DISK}
diff --git a/tests/sys/cddl/zfs/tests/threadsappend/threadsappend.c b/tests/sys/cddl/zfs/tests/threadsappend/threadsappend.c
new file mode 100644
index 000000000000..7e140130f339
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/threadsappend/threadsappend.c
@@ -0,0 +1,133 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+
+/*
+ * The size of the output file, "go.out", should be 80*8192*2 = 1310720
+ *
+ * $ cd /tmp; go; ls -l go.out
+ * done.
+ * -rwxr-xr-x 1 jdm staff 1310720 Apr 13 19:45 go.out
+ * $ cd /zfs; go; ls -l go.out
+ * done.
+ * -rwxr-xr-x 1 jdm staff 663552 Apr 13 19:45 go.out
+ *
+ * The file on zfs is short as it does not appear that zfs is making the
+ * implicit seek to EOF and the actual write atomic. From the SUSv3
+ * interface spec, behavior is undefined if concurrent writes are performed
+ * from multi-processes to a single file. So I don't know if this is a
+ * standards violation, but I cannot find any such disclaimers in our
+ * man pages. This issue came up at a customer site in another context, and
+ * the suggestion was to open the file with O_APPEND, but that wouldn't
+ * help with zfs(see 4977529). Also see bug# 5031301.
+ */
+
+static int outfd = 0;
+
+static void *
+go(void *data)
+{
+ int i = 0, n = *(int *)data;
+ ssize_t ret = 0;
+ char buf[8192] = {0};
+ (void) memset(buf, n, sizeof (buf));
+
+ for (i = 0; i < 80; i++) {
+ ret = write(outfd, buf, sizeof (buf));
+ }
+ return (NULL);
+}
+
+static void
+usage()
+{
+ (void) fprintf(stderr,
+ "usage: zfs_threadsappend <file name>\n");
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ pthread_t threads[2];
+ int ret = 0;
+ long ncpus = 0;
+ int i;
+
+ if (argc != 2) {
+ usage();
+ }
+
+ ncpus = sysconf(_SC_NPROCESSORS_ONLN);
+ if (ncpus < 0) {
+ (void) fprintf(stderr,
+ "Invalid return from sysconf(_SC_NPROCESSORS_ONLN)"
+ " : errno (decimal)=%d\n", errno);
+ exit(1);
+ }
+ if (ncpus < 2) {
+ (void) fprintf(stderr,
+ "Must execute this binary on a multi-processor system\n");
+ exit(1);
+ }
+
+ outfd = open(argv[optind++], O_RDWR|O_CREAT|O_APPEND|O_TRUNC, 0777);
+ if (outfd == -1) {
+ (void) fprintf(stderr,
+ "zfs_threadsappend: "
+ "open(%s, O_RDWR|O_CREAT|O_APPEND|O_TRUNC, 0777)"
+ " failed\n", argv[optind]);
+ perror("open");
+ exit(1);
+ }
+
+ for (i = 0; i < 2; i++) {
+ ret = pthread_create(&threads[i], NULL, go, (void *)&i);
+ if (ret != 0) {
+ (void) fprintf(stderr,
+ "zfs_threadsappend: thr_create(#%d) "
+ "failed error=%d\n", i+1, ret);
+ exit(1);
+ }
+ }
+
+ for (i = 0; i < 2; i++) {
+ if (pthread_join(threads[i], NULL) != 0)
+ break;
+ }
+
+ return (0);
+}
diff --git a/tests/sys/cddl/zfs/tests/threadsappend/threadsappend.cfg b/tests/sys/cddl/zfs/tests/threadsappend/threadsappend.cfg
new file mode 100644
index 000000000000..a2d61f141178
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/threadsappend/threadsappend.cfg
@@ -0,0 +1,27 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export TESTFILE=testfile${TESTCASE_ID}
diff --git a/tests/sys/cddl/zfs/tests/threadsappend/threadsappend_001_pos.ksh b/tests/sys/cddl/zfs/tests/threadsappend/threadsappend_001_pos.ksh
new file mode 100644
index 000000000000..4687c6fa33b7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/threadsappend/threadsappend_001_pos.ksh
@@ -0,0 +1,90 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: threadsappend_001_pos
+#
+# DESCRIPTION:
+#
+# Ensure multiple threads performing write appends to the same ZFS
+# file succeed.
+#
+# STRATEGY:
+# 1) Verify this is a multi-processor system
+# 2) Create multiple threads with each appending to a file
+# 3) Verify that the resulting file is the expected size
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "both"
+
+log_assert "Ensure multiple threads performing write appends to the same" \
+ "ZFS file succeed"
+
+#
+# $FILE_SIZE is hardcoded into threadsappend.c and is the expected
+# size of the file after all the threads have appended to it
+#
+typeset -i FILE_SIZE=1310720
+
+#
+# This test should be run on a multi-processor system because otherwise the FS
+# will not be concurrently used by the threads
+#
+NCPUS=`sysctl -a | awk -F '"' '/cpu count="[0-9+]"/ {print $2; exit}'`
+if [[ $? -ne 0 || -z $NCPUS || $NCPUS -le 1 ]]; then
+ log_unsupported \
+ "This test should be executed on a multi-processor system."
+fi
+
+#
+# zfs_threadsappend tries to append to $TESTFILE using threads
+# so that the resulting file is $FILE_SIZE bytes in size
+#
+log_must threadsappend ${TESTDIR}/${TESTFILE}
+
+#
+# Check the size of the resulting file
+#
+SIZE=`$LS -l ${TESTDIR}/${TESTFILE} | $AWK '{print $5}'`
+if [[ $SIZE -ne $FILE_SIZE ]]; then
+ log_fail "'The length of ${TESTDIR}/${TESTFILE}' doesnt equal 1310720."
+fi
+
+log_pass "Multiple thread appends succeeded. File size as expected"
diff --git a/tests/sys/cddl/zfs/tests/threadsappend/threadsappend_test.sh b/tests/sys/cddl/zfs/tests/threadsappend/threadsappend_test.sh
new file mode 100755
index 000000000000..2cf66f03f38a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/threadsappend/threadsappend_test.sh
@@ -0,0 +1,55 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case threadsappend_001_pos cleanup
+threadsappend_001_pos_head()
+{
+ atf_set "descr" "Ensure multiple threads performing write appends to the sameZFS file succeed"
+}
+threadsappend_001_pos_body()
+{
+ export PATH=$(atf_get_srcdir):$PATH
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/threadsappend.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/threadsappend_001_pos.ksh || atf_fail "Testcase failed"
+}
+threadsappend_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/threadsappend.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case threadsappend_001_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/truncate/Makefile b/tests/sys/cddl/zfs/tests/truncate/Makefile
new file mode 100644
index 000000000000..98c8e085408d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/truncate/Makefile
@@ -0,0 +1,17 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/truncate
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= truncate_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= truncate_001_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= truncate_002_pos.ksh
+${PACKAGE}FILES+= truncate.cfg
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/truncate/cleanup.ksh b/tests/sys/cddl/zfs/tests/truncate/cleanup.ksh
new file mode 100644
index 000000000000..b2b441e574d0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/truncate/cleanup.ksh
@@ -0,0 +1,29 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/truncate/setup.ksh b/tests/sys/cddl/zfs/tests/truncate/setup.ksh
new file mode 100644
index 000000000000..1089059bd66a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/truncate/setup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+disk=${DISKS%% *}
+
+default_setup $disk
diff --git a/tests/sys/cddl/zfs/tests/truncate/truncate.cfg b/tests/sys/cddl/zfs/tests/truncate/truncate.cfg
new file mode 100644
index 000000000000..9e7b913cf159
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/truncate/truncate.cfg
@@ -0,0 +1,33 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export TESTFILE=testfile.${TESTCASE_ID}
+export TRUNC_FILESIZE=${TRUNC_FILESIZE-"67108864"} # 64 Mb
+export TRUNC_BLKSIZE=${TRUNC_BLKSIZE-"512"}
+export TRUNC_SEED=${TRUNC_SEED-""}
+export TRUNC_FILEOFFSET=${TRUNC_FILEOFFSET-""}
+export TRUNC_COUNT=${TRUNC_COUNT-"16384"} # FILESIZE/BLKSIZE/8
+export STF_TIMEOUT=3600
diff --git a/tests/sys/cddl/zfs/tests/truncate/truncate_001_pos.ksh b/tests/sys/cddl/zfs/tests/truncate/truncate_001_pos.ksh
new file mode 100644
index 000000000000..f67b00f93450
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/truncate/truncate_001_pos.ksh
@@ -0,0 +1,87 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: truncate_001_pos
+#
+# DESCRIPTION:
+# Tests file truncation within ZFS.
+#
+# STRATEGY:
+# 1. Open file
+# 2. Write random blocks in random places
+# 3. Truncate the file
+# 4. Repeat steps 2 and 3 lots of times
+# 5. Close the file.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ [[ -e $TESTDIR ]] && log_must $RM -rf $TESTDIR/*
+}
+
+log_assert "Ensure file with random blocks is truncated properly"
+
+options=""
+options_display="default options"
+
+log_onexit cleanup
+
+[[ -n "$TRUNC_FILESIZE" ]] && options=" $options -f $TRUNC_FILESIZE "
+
+[[ -n "$TRUNC_BLKSIZE" ]] && options="$options -b $TRUNC_BLKSIZE "
+
+[[ -n "$TRUNC_COUNT" ]] && options="$options -c $TRUNC_COUNT "
+
+[[ -n "$TRUNC_SEED" ]] && options="$options -s $TRUNC_SEED "
+
+[[ -n "$TRUNC_FILEOFFSET" ]] && options="$options -o $TRUNC_FILEOFFSET "
+
+[[ -n "$options" ]] && options_display=$options
+
+log_note "Invoking $FILE_TRUNC with: $options_display"
+log_must $FILE_TRUNC $options $TESTDIR/$TESTFILE
+
+typeset dir=$(get_device_dir $DISKS)
+verify_filesys "$TESTPOOL" "$TESTPOOL/$TESTFS" "$dir"
+
+log_pass "Random blocks have been truncated properly."
diff --git a/tests/sys/cddl/zfs/tests/truncate/truncate_002_pos.ksh b/tests/sys/cddl/zfs/tests/truncate/truncate_002_pos.ksh
new file mode 100644
index 000000000000..4444fe822752
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/truncate/truncate_002_pos.ksh
@@ -0,0 +1,75 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: truncate_002_pos
+#
+# DESCRIPTION:
+# Tests file truncation within ZFS while a sync operation is in progress.
+#
+# STRATEGY:
+# 1. Copy a file to ZFS filesystem
+# 2. Copy /dev/null to same file on ZFS filesystem
+# 3. Execute a sync command
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ [[ -e $TESTDIR ]] && log_must $RM -rf ${TESTDIR}/*
+}
+
+log_assert "Ensure zeroed file gets written correctly during a sync operation"
+
+srcfilename="$STF_SUITE/include/libtest.kshlib"
+
+log_onexit cleanup
+
+log_note "Copying $srcfilename to $TESTFILE"
+log_must $CP $srcfilename ${TESTDIR}/${TESTFILE}
+
+log_note "Copying /dev/null to $TESTFILE"
+log_must $CP /dev/null ${TESTDIR}/${TESTFILE}
+
+log_note "Now 'sync' the filesystem"
+(cd $TESTDIR; log_must $SYNC)
+
+log_pass "Successful truncation within ZFS while a sync operation is in progress."
diff --git a/tests/sys/cddl/zfs/tests/truncate/truncate_test.sh b/tests/sys/cddl/zfs/tests/truncate/truncate_test.sh
new file mode 100755
index 000000000000..5784743eebef
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/truncate/truncate_test.sh
@@ -0,0 +1,81 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case truncate_001_pos cleanup
+truncate_001_pos_head()
+{
+ atf_set "descr" "Ensure file with random blocks is truncated properly"
+ atf_set "require.progs" "ksh93 zdb"
+ atf_set "timeout" 3600
+}
+truncate_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/truncate.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/truncate_001_pos.ksh || atf_fail "Testcase failed"
+}
+truncate_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/truncate.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case truncate_002_pos cleanup
+truncate_002_pos_head()
+{
+ atf_set "descr" "Ensure zeroed file gets written correctly during a sync operation"
+ atf_set "timeout" 3600
+}
+truncate_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/truncate.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/truncate_002_pos.ksh || atf_fail "Testcase failed"
+}
+truncate_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/truncate.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case truncate_001_pos
+ atf_add_test_case truncate_002_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/txg_integrity/Makefile b/tests/sys/cddl/zfs/tests/txg_integrity/Makefile
new file mode 100644
index 000000000000..6a200b0c12b0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/txg_integrity/Makefile
@@ -0,0 +1,24 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/txg_integrity
+FILESDIR=${TESTSDIR}
+BINDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= txg_integrity_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= txg_integrity_001_pos.ksh
+${PACKAGE}FILES+= fsync_integrity_001_pos.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= txg_integrity.cfg
+PROGS+= txg_integrity
+PROGS+= fsync_integrity
+LIBADD+= pthread
+
+SRCS.fsync_integrity= fsync_integrity.c
+SRCS.txg_integrity= txg_integrity.c
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/txg_integrity/cleanup.ksh b/tests/sys/cddl/zfs/tests/txg_integrity/cleanup.ksh
new file mode 100644
index 000000000000..207670309091
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/txg_integrity/cleanup.ksh
@@ -0,0 +1,31 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2011 Spectra logic. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+destroy_pool $TESTPOOL
+$RM -rf $TESTDIR
+$MDCONFIG -d -u $TESTCASE_ID
diff --git a/tests/sys/cddl/zfs/tests/txg_integrity/fsync_integrity.c b/tests/sys/cddl/zfs/tests/txg_integrity/fsync_integrity.c
new file mode 100644
index 000000000000..5aba7a83225f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/txg_integrity/fsync_integrity.c
@@ -0,0 +1,516 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2013 Spectra Logic. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Verify the integrity of non-aligned writes to the same blocks within the same
+ * transaction group, where an fsync is issued by a non-final writer.
+ *
+ * This test verifies that the unoverride in the following sequence of events is
+ * handled correctly:
+ *
+ * 1) A new transaction group opens
+ * 2) A write is issued to a certain block
+ * 3) The writer fsyncs() that file
+ * 4) TBD module immediately writes that block, then places an override in the
+ * syncer's TBD data structure, indicating that it doesn't need to write that
+ * block when syncing.
+ * 5) Another write is issued to the same block, with different data.
+ * 6) TBD module unoverrides that block in the syncer's TBD data structure
+ * 7) The syncer writes that block
+ *
+ * Outline:
+ * Create a big zero-filled file.
+ * Create a bunch of different IO patterns. Each IO pattern consists of:
+ * * A partition of the file range into 64 different non-overlapping chunks.
+ * * A permutation of those chunks
+ * For each repetition:
+ * For each IO pattern:
+ * Create one binary semaphore per chunk, per (n-1) threads
+ * Create n threads.
+ * The first thread will write each chunk in order. It will post a
+ * semaphore after each write to indicate that it has completed
+ * writing that chunk.
+ * The second thread will pend on those semaphores in order. Each time it
+ * receives a semaphore, it will write a different pattern to that
+ * chunk. Then it will post a different semaphore to signal the next
+ * thread.
+ * The final thread will not post any semaphores
+ * Every even-numbered thread, starting with the first (0th), will fsync()
+ * the file after its write. The final thread, being odd-numbered,
+ * won't.
+ * Join all threads
+ * Read the entire file and verify that
+ * 1) Every write went to the correct location
+ * 2) The final thread's write is left in the file
+ */
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <semaphore.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <assert.h>
+#include <arpa/inet.h>
+
+#define NUM_REPETITIONS 16
+#define CLUSTERSIZE (1 << 17)
+#define NUM_CHUNKS 64
+#define FSIZE (64 * (CLUSTERSIZE)) //FSIZE may range from NUM_CHUNKS clusters to 8GB
+#define USE_THREADS 1
+#define NUM_THREADS 8
+
+typedef struct {
+ //partitions describes the boundaries between chunks. Each element is a
+ //fraction of the filesize in 1.31 fixed point format. So the boundary
+ //between chunk n and chunk n-1 is (FSIZE * partitions[n-1] / (1<<31) .
+ //partitions[-1] is understood to be 0 and partitions[NUM_CHUNKS] must be 1.0
+ //partitions is sorted, of course.
+ //Partition boundaries must be dword aligned. Thus, in order to work with
+ //multiple values of FSIZE, partitions values must be aligned to multiples of
+ //8 / (NUM_CHUNKS * CLUSTERSIZE) = 1 / 524288 = 0x0.00002
+ uint32_t partitions[NUM_CHUNKS];
+ int permutation[NUM_CHUNKS]; //the order in which to write the chunks
+} pattern_t;
+
+typedef struct {
+ int thread_num;
+ const pattern_t* pat;
+} thread_data_t;
+
+
+/* Returns (via begin and end) the range of a chunk. Begin is inclusive,
+ * end is exclusive */
+void get_chunk_range(const pattern_t* pat, int chunk, uint32_t* begin, uint32_t* end){
+ if (chunk == 0){
+ *begin = 0;
+ }
+ else{
+ *begin = (uint32_t)((uint64_t)FSIZE * (uint64_t)pat->partitions[chunk - 1] >> 31);
+ }
+ *end = (uint32_t)((uint64_t)FSIZE * (uint64_t)pat->partitions[chunk] >> 31);
+}
+
+
+/* The most basic, trivial IO pattern. Fully sequential, and the follower
+ * writes every other block */
+const pattern_t trivial_pattern = {
+ {0x2000000, 0x4000000, 0x6000000, 0x8000000, 0xa000000, 0xc000000, 0xe000000, 0x10000000,
+ 0x12000000, 0x14000000, 0x16000000, 0x18000000, 0x1a000000, 0x1c000000, 0x1e000000, 0x20000000,
+ 0x22000000, 0x24000000, 0x26000000, 0x28000000, 0x2a000000, 0x2c000000, 0x2e000000, 0x30000000,
+ 0x32000000, 0x34000000, 0x36000000, 0x38000000, 0x3a000000, 0x3c000000, 0x3e000000, 0x40000000,
+ 0x42000000, 0x44000000, 0x46000000, 0x48000000, 0x4a000000, 0x4c000000, 0x4e000000, 0x50000000,
+ 0x52000000, 0x54000000, 0x56000000, 0x58000000, 0x5a000000, 0x5c000000, 0x5e000000, 0x60000000,
+ 0x62000000, 0x64000000, 0x66000000, 0x68000000, 0x6a000000, 0x6c000000, 0x6e000000, 0x70000000,
+ 0x72000000, 0x74000000, 0x76000000, 0x78000000, 0x7a000000, 0x7c000000, 0x7e000000, 0x80000000},
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63},
+};
+
+//The below patterns were randomly generated
+const pattern_t pat0 = {
+ { 0x1eac000, 0x88a4000, 0xaffe000, 0xcdb7000, 0xd2d5000, 0xe16f000, 0xe499000, 0x11f71000, 0x1242d000, 0x12c07000, 0x143bc000, 0x1460a000, 0x15dd7000, 0x1700e000, 0x1be7e000, 0x1e14d000, 0x1e6ac000, 0x21097000, 0x24b74000, 0x27166000, 0x27669000, 0x30539000, 0x3218b000, 0x37591000, 0x37b60000, 0x39818000, 0x39d08000, 0x3c90e000, 0x3e54f000, 0x3fb99000, 0x42c8e000, 0x43a62000, 0x43f50000, 0x4c0c9000, 0x4c422000, 0x4c737000, 0x4d41e000, 0x4d738000, 0x4da71000, 0x4f4e8000, 0x508e3000, 0x51396000, 0x51ab5000, 0x52a02000, 0x54238000, 0x54d6a000, 0x55029000, 0x5584b000, 0x5c42c000, 0x5c4a7000, 0x5dac5000, 0x5fe4d000, 0x63f86000, 0x66ad0000, 0x67b3d000, 0x69ce5000, 0x6c179000, 0x6e79e000, 0x6f83f000, 0x71165000, 0x72bd9000, 0x7ac79000, 0x7dc94000, 0x80000000, },
+ { 57, 16, 28, 25, 10, 59, 52, 46, 30, 6, 40, 36, 39, 9, 21, 51, 33, 45, 44, 19, 2, 50, 55, 5, 58, 13, 23, 0, 12, 53, 42, 32, 31, 48, 35, 61, 49, 54, 18, 24, 8, 41, 62, 4, 47, 17, 1, 3, 34, 14, 63, 22, 15, 26, 38, 56, 27, 60, 29, 11, 7, 43, 20, 37, },
+};
+const pattern_t pat1 = {
+ { 0x2b5000, 0x16db000, 0x5eb5000, 0x93a0000, 0xa7cb000, 0xa9e9000, 0xd144000, 0xe7c2000, 0xeb7d000, 0x10919000, 0x10cbd000, 0x11f85000, 0x17360000, 0x1760a000, 0x18eab000, 0x1ae6b000, 0x1c5f6000, 0x1df38000, 0x21bec000, 0x239d1000, 0x26b81000, 0x2747b000, 0x27a03000, 0x2b3cc000, 0x2cbf9000, 0x2ec0f000, 0x30a68000, 0x30bea000, 0x30c64000, 0x311af000, 0x35823000, 0x35d23000, 0x3b20e000, 0x405d8000, 0x414c8000, 0x43a91000, 0x44049000, 0x4ab4e000, 0x4ae21000, 0x4d293000, 0x511e5000, 0x516fc000, 0x52d77000, 0x57229000, 0x5da57000, 0x5dbe6000, 0x6070e000, 0x60fc0000, 0x64b24000, 0x67636000, 0x67658000, 0x6b040000, 0x6b28f000, 0x6e551000, 0x707c0000, 0x71b5c000, 0x72062000, 0x762a1000, 0x788a0000, 0x7a1e1000, 0x7b06a000, 0x7e04c000, 0x7f4cf000, 0x80000000, },
+ { 45, 8, 55, 9, 21, 54, 41, 7, 6, 22, 31, 47, 23, 11, 48, 53, 0, 61, 63, 50, 17, 27, 12, 19, 10, 40, 14, 51, 39, 59, 2, 43, 18, 42, 52, 28, 16, 44, 3, 5, 15, 35, 58, 33, 57, 49, 34, 30, 46, 4, 37, 60, 32, 36, 25, 56, 24, 13, 20, 38, 29, 26, 62, 1, },
+};
+const pattern_t pat2 = {
+ { 0x912d000, 0xe610000, 0xf755000, 0x116df000, 0x128e5000, 0x1bd51000, 0x24e9a000, 0x27643000, 0x28cf4000, 0x292c9000, 0x2c907000, 0x2d389000, 0x2d941000, 0x2eb3f000, 0x30e94000, 0x31738000, 0x3343b000, 0x342ce000, 0x34d12000, 0x3536d000, 0x35e1a000, 0x35e4d000, 0x35fd5000, 0x3642b000, 0x3924d000, 0x392a5000, 0x3e531000, 0x3f0ee000, 0x3fdf8000, 0x41593000, 0x41c80000, 0x43959000, 0x43bc0000, 0x461c8000, 0x48922000, 0x49519000, 0x4f6fa000, 0x50274000, 0x508ae000, 0x536ed000, 0x54154000, 0x59894000, 0x5a666000, 0x5b0a6000, 0x5b9ff000, 0x5c109000, 0x5d8d0000, 0x5ddc5000, 0x5fcc5000, 0x63366000, 0x63adc000, 0x645b6000, 0x670eb000, 0x6b1b1000, 0x6c996000, 0x6ed2a000, 0x6ee4f000, 0x71fcd000, 0x734a3000, 0x76bdf000, 0x77b3f000, 0x7c65a000, 0x7d200000, 0x80000000, },
+ { 31, 35, 36, 52, 27, 56, 40, 13, 51, 49, 43, 37, 62, 42, 24, 29, 48, 25, 7, 61, 22, 57, 11, 32, 2, 54, 41, 6, 55, 15, 20, 26, 63, 44, 12, 4, 19, 58, 60, 59, 47, 23, 30, 50, 53, 34, 9, 38, 45, 8, 28, 3, 16, 33, 5, 21, 1, 10, 46, 18, 0, 14, 39, 17, },
+};
+const pattern_t pat3 = {
+ { 0x553000, 0x19de000, 0x6a20000, 0x8a53000, 0x8ef9000, 0xc325000, 0x1132e000, 0x139fa000, 0x1426b000, 0x150ff000, 0x1bbc1000, 0x1e84c000, 0x1f43e000, 0x1f731000, 0x21ec8000, 0x231f4000, 0x23440000, 0x23466000, 0x260b6000, 0x286a7000, 0x29518000, 0x29e35000, 0x2fdb7000, 0x3089d000, 0x362e0000, 0x3c1f9000, 0x3df2d000, 0x3fce6000, 0x402f3000, 0x4117f000, 0x41e06000, 0x4374f000, 0x451e5000, 0x45a59000, 0x4956b000, 0x4960f000, 0x4a934000, 0x4bc6f000, 0x4d462000, 0x4eef8000, 0x4f609000, 0x50dc1000, 0x51022000, 0x54396000, 0x5641b000, 0x578f1000, 0x589cf000, 0x59093000, 0x5da6b000, 0x5fbf0000, 0x605a2000, 0x65428000, 0x65530000, 0x6705a000, 0x6db65000, 0x71cef000, 0x725a2000, 0x73bf5000, 0x75acb000, 0x76065000, 0x7614c000, 0x77aab000, 0x78f70000, 0x80000000, },
+ { 15, 30, 31, 16, 49, 13, 55, 59, 4, 24, 26, 44, 17, 0, 18, 54, 10, 3, 46, 34, 29, 22, 45, 5, 38, 32, 39, 50, 48, 53, 12, 25, 35, 56, 51, 52, 1, 33, 43, 63, 47, 37, 23, 20, 60, 14, 11, 21, 8, 57, 27, 41, 6, 58, 62, 2, 19, 61, 28, 36, 40, 7, 9, 42, },
+};
+const pattern_t pat4 = {
+ { 0x425000, 0x8e8000, 0x4b90000, 0x883c000, 0x968e000, 0xbacc000, 0x10e59000, 0x125a1000, 0x12f00000, 0x14e7c000, 0x156de000, 0x192a1000, 0x1a2b9000, 0x1b4a0000, 0x1be9c000, 0x1d3bd000, 0x24242000, 0x2516b000, 0x2b88d000, 0x2b96a000, 0x2bcd3000, 0x2c5a9000, 0x2da74000, 0x2dba1000, 0x3097f000, 0x332ef000, 0x34525000, 0x36193000, 0x3725c000, 0x37e66000, 0x3d315000, 0x3e813000, 0x404ae000, 0x40c68000, 0x42f93000, 0x44b14000, 0x44b15000, 0x473b2000, 0x49048000, 0x4c794000, 0x50b60000, 0x52b3d000, 0x58c61000, 0x5b7d4000, 0x5ce71000, 0x5d21d000, 0x5d63e000, 0x5e00f000, 0x60e8b000, 0x66381000, 0x66c70000, 0x68430000, 0x707c2000, 0x71979000, 0x72681000, 0x74017000, 0x7721d000, 0x7a1be000, 0x7a2cd000, 0x7b225000, 0x7c311000, 0x7e03a000, 0x7e402000, 0x80000000, },
+ { 52, 62, 28, 47, 51, 37, 31, 36, 4, 58, 26, 29, 16, 59, 57, 33, 22, 27, 49, 44, 19, 56, 34, 23, 5, 14, 45, 48, 21, 25, 18, 12, 43, 53, 60, 17, 46, 15, 63, 30, 42, 38, 41, 8, 39, 20, 1, 10, 54, 40, 32, 24, 9, 2, 35, 3, 7, 0, 61, 11, 13, 55, 6, 50, },
+};
+const pattern_t pat5 = {
+ { 0xae7000, 0x436e000, 0x81e1000, 0xb276000, 0xf8bf000, 0xfb26000, 0xfe7e000, 0x137ad000, 0x14b8e000, 0x157aa000, 0x1981a000, 0x1a32f000, 0x1bc9e000, 0x1def5000, 0x1e8ef000, 0x2068f000, 0x22692000, 0x22a6c000, 0x255bf000, 0x26977000, 0x27619000, 0x2977c000, 0x2ce0c000, 0x2e1ec000, 0x2e26c000, 0x31ce8000, 0x34e6c000, 0x365cd000, 0x37e87000, 0x385e3000, 0x3a7e2000, 0x3a9c7000, 0x41597000, 0x42e8a000, 0x453cc000, 0x454bf000, 0x4b24c000, 0x4ba54000, 0x4e307000, 0x4f059000, 0x55d5a000, 0x56277000, 0x56b90000, 0x5882d000, 0x5a2c5000, 0x5b369000, 0x5d442000, 0x5d671000, 0x5fdd0000, 0x60ce0000, 0x63713000, 0x64130000, 0x65973000, 0x67ad9000, 0x68764000, 0x68bb2000, 0x690d1000, 0x6a2c8000, 0x73e9f000, 0x74e75000, 0x77861000, 0x77ee5000, 0x7cddb000, 0x80000000, },
+ { 42, 25, 15, 58, 32, 61, 30, 56, 48, 62, 38, 50, 7, 45, 16, 29, 12, 4, 41, 3, 27, 18, 57, 10, 51, 17, 21, 14, 35, 19, 44, 47, 49, 26, 59, 63, 28, 55, 20, 13, 5, 6, 37, 54, 40, 22, 23, 46, 11, 36, 34, 31, 2, 60, 9, 52, 24, 1, 53, 0, 39, 43, 8, 33, },
+};
+const pattern_t pat6 = {
+ { 0xad2000, 0x222f000, 0x64b4000, 0x6c66000, 0x6f35000, 0x9e50000, 0xe744000, 0xf129000, 0x101bb000, 0x11bf8000, 0x14b89000, 0x1691c000, 0x17a0d000, 0x1817a000, 0x1997a000, 0x1d736000, 0x1db33000, 0x1fdd8000, 0x21e56000, 0x2266c000, 0x22875000, 0x22b84000, 0x230ed000, 0x239c5000, 0x24e1a000, 0x275f5000, 0x29036000, 0x29f69000, 0x2e538000, 0x2efca000, 0x2f0bc000, 0x2f1bf000, 0x305cb000, 0x31ce7000, 0x345c4000, 0x35d4f000, 0x36e56000, 0x3ae9e000, 0x3cc27000, 0x40117000, 0x4299f000, 0x434c3000, 0x443d4000, 0x4552d000, 0x4a8a8000, 0x4cdea000, 0x51bd5000, 0x580c4000, 0x58381000, 0x59dc0000, 0x5ba7f000, 0x5d88b000, 0x5e0c4000, 0x5ee57000, 0x61f3f000, 0x63a4a000, 0x68a8a000, 0x68ec5000, 0x6937b000, 0x720be000, 0x72cf5000, 0x74fc8000, 0x76464000, 0x80000000, },
+ { 31, 46, 36, 22, 63, 12, 51, 60, 13, 44, 41, 6, 11, 17, 42, 24, 16, 61, 20, 26, 35, 21, 29, 55, 50, 45, 62, 19, 54, 9, 30, 34, 53, 52, 10, 39, 0, 49, 48, 38, 40, 28, 23, 56, 2, 5, 4, 59, 14, 57, 3, 25, 43, 32, 27, 47, 8, 7, 37, 33, 1, 18, 58, 15, },
+};
+const pattern_t pat7 = {
+ { 0xd83000, 0x1597000, 0x245b000, 0x6a75000, 0x8fda000, 0x960e000, 0xd310000, 0xe6cd000, 0x1409a000, 0x15221000, 0x16059000, 0x1b3a4000, 0x1ceea000, 0x1ed1a000, 0x1ef0f000, 0x21723000, 0x21efc000, 0x24594000, 0x26d7f000, 0x28c4f000, 0x2fa89000, 0x304f0000, 0x30dbb000, 0x30de3000, 0x3365d000, 0x36dbc000, 0x3acb2000, 0x3e291000, 0x3f7da000, 0x41352000, 0x41a0f000, 0x435c8000, 0x4475a000, 0x47536000, 0x47726000, 0x4a81f000, 0x4be4e000, 0x4bf05000, 0x4c15b000, 0x515b4000, 0x52ef5000, 0x548cc000, 0x5692a000, 0x59ef2000, 0x5b97c000, 0x5c4f0000, 0x5d1b9000, 0x618ed000, 0x61bcc000, 0x61e07000, 0x639a3000, 0x65302000, 0x68041000, 0x6be56000, 0x721a3000, 0x72c99000, 0x740b9000, 0x7586d000, 0x75eca000, 0x76406000, 0x7b68a000, 0x7dd26000, 0x7ed55000, 0x80000000, },
+ { 44, 57, 22, 35, 63, 11, 15, 49, 61, 40, 29, 20, 19, 42, 32, 12, 41, 6, 46, 60, 52, 5, 36, 10, 2, 8, 3, 33, 54, 39, 58, 48, 62, 7, 51, 34, 0, 1, 18, 9, 55, 31, 23, 38, 25, 21, 17, 24, 13, 50, 16, 14, 43, 53, 45, 28, 59, 37, 26, 30, 47, 27, 56, 4, },
+};
+const pattern_t pat8 = {
+ { 0x1b8000, 0x27eb000, 0x5a4d000, 0x6ecc000, 0xb52e000, 0xb70e000, 0xc6db000, 0xd83d000, 0xed51000, 0x13c59000, 0x13fef000, 0x142e1000, 0x192d0000, 0x1aa63000, 0x1e230000, 0x1f464000, 0x20de4000, 0x2234b000, 0x25459000, 0x27018000, 0x28263000, 0x29cc7000, 0x32227000, 0x32c63000, 0x34af0000, 0x37e27000, 0x3afc9000, 0x3c166000, 0x3df20000, 0x405bd000, 0x41273000, 0x45c39000, 0x471be000, 0x4758e000, 0x4b3fc000, 0x4c6b2000, 0x4c80f000, 0x4ccd6000, 0x4d9e0000, 0x4e07f000, 0x4eeda000, 0x541ae000, 0x58aa7000, 0x5a2c6000, 0x5a628000, 0x5ab94000, 0x5bddc000, 0x5d1d4000, 0x5e643000, 0x5f72f000, 0x64771000, 0x67bd4000, 0x6a28c000, 0x6c977000, 0x6cc4e000, 0x710c4000, 0x74b86000, 0x75cf7000, 0x77d4b000, 0x7870e000, 0x7c47c000, 0x7eb52000, 0x7fbea000, 0x80000000, },
+ { 7, 29, 62, 8, 54, 38, 35, 45, 60, 55, 1, 40, 4, 19, 50, 63, 48, 51, 13, 27, 33, 39, 52, 46, 10, 9, 56, 2, 42, 43, 47, 44, 17, 5, 25, 6, 57, 23, 15, 58, 59, 22, 14, 26, 32, 61, 30, 0, 11, 12, 36, 24, 53, 49, 3, 20, 31, 28, 34, 18, 41, 21, 16, 37, },
+};
+const pattern_t pat9 = {
+ { 0x5b59000, 0xa6d7000, 0xbad3000, 0xdf91000, 0x115ad000, 0x13fde000, 0x17618000, 0x1b8e9000, 0x1e1b7000, 0x1e97d000, 0x21737000, 0x21a5e000, 0x24140000, 0x2558f000, 0x2647a000, 0x28257000, 0x285f6000, 0x2cb7a000, 0x2ebb1000, 0x30ae8000, 0x31543000, 0x315cb000, 0x31616000, 0x335ba000, 0x33ed6000, 0x35cf3000, 0x4162b000, 0x4409b000, 0x4629a000, 0x4b745000, 0x4c0ba000, 0x4cbc5000, 0x4dd97000, 0x4f34b000, 0x4f637000, 0x539d6000, 0x53f3d000, 0x56383000, 0x5642b000, 0x5a71f000, 0x5affa000, 0x5b486000, 0x5b8ef000, 0x60d88000, 0x61629000, 0x625cd000, 0x63326000, 0x6735e000, 0x67379000, 0x6a26a000, 0x6a281000, 0x6b997000, 0x6c50d000, 0x6cc6c000, 0x6f496000, 0x717ad000, 0x732ec000, 0x744dc000, 0x771e8000, 0x77cf0000, 0x79cad000, 0x7bb21000, 0x7e7b4000, 0x80000000, },
+ { 35, 9, 46, 6, 29, 2, 3, 54, 55, 57, 41, 16, 44, 5, 0, 59, 10, 61, 22, 42, 47, 12, 14, 50, 39, 34, 21, 32, 25, 15, 26, 8, 38, 60, 28, 53, 62, 49, 58, 43, 36, 37, 52, 7, 19, 63, 17, 11, 45, 33, 23, 27, 24, 18, 48, 56, 31, 13, 51, 30, 4, 20, 40, 1, },
+};
+const pattern_t pat10 = {
+ { 0xa72000, 0x180a000, 0x6406000, 0x66df000, 0x83bb000, 0xa96f000, 0xd193000, 0x13b9b000, 0x13dae000, 0x16109000, 0x1853d000, 0x18887000, 0x19f0a000, 0x22151000, 0x229ba000, 0x26b58000, 0x2aaf4000, 0x2bf50000, 0x31a2e000, 0x31d4e000, 0x32196000, 0x3513a000, 0x36a2d000, 0x3746b000, 0x389ad000, 0x39d27000, 0x3dad3000, 0x3de55000, 0x3ea9b000, 0x3ec06000, 0x3f921000, 0x432d3000, 0x43bec000, 0x43dda000, 0x47b2b000, 0x4886e000, 0x4928e000, 0x49ad2000, 0x4d0df000, 0x4f40d000, 0x50959000, 0x54fa4000, 0x56091000, 0x5688d000, 0x5b7d8000, 0x5f6fd000, 0x601e4000, 0x64eaa000, 0x6752e000, 0x67fff000, 0x6a184000, 0x6ad7a000, 0x6adbc000, 0x6c434000, 0x6f451000, 0x6ffb4000, 0x707ee000, 0x71161000, 0x7146b000, 0x75dbf000, 0x77259000, 0x7acd4000, 0x7af71000, 0x80000000, },
+ { 61, 38, 5, 23, 62, 11, 53, 9, 17, 45, 30, 29, 41, 60, 39, 21, 40, 19, 44, 33, 42, 50, 56, 28, 32, 46, 43, 20, 16, 3, 54, 8, 4, 26, 15, 34, 47, 12, 6, 27, 48, 0, 1, 2, 57, 59, 7, 58, 49, 35, 24, 37, 52, 63, 10, 55, 36, 13, 14, 25, 18, 22, 31, 51, },
+};
+const pattern_t pat11 = {
+ { 0x996000, 0xaff000, 0x199a000, 0x46f3000, 0x74c0000, 0x758d000, 0xcd09000, 0xe48c000, 0xe8de000, 0xf111000, 0xf87b000, 0x10b1c000, 0x15d63000, 0x17b21000, 0x182d3000, 0x19167000, 0x198ce000, 0x1bd47000, 0x1dff1000, 0x1edc0000, 0x1f890000, 0x20860000, 0x23207000, 0x29bd5000, 0x2ac0f000, 0x2e395000, 0x2e707000, 0x329de000, 0x3497f000, 0x3807f000, 0x38a94000, 0x40a19000, 0x4168e000, 0x42ca0000, 0x42de9000, 0x45194000, 0x464f2000, 0x4700f000, 0x47dbb000, 0x4dae7000, 0x50660000, 0x535a8000, 0x5546b000, 0x57b55000, 0x5860a000, 0x5a9ee000, 0x5b8d9000, 0x5c49f000, 0x5cb4d000, 0x5d28c000, 0x60dcd000, 0x62557000, 0x64b0c000, 0x654cb000, 0x65746000, 0x65e29000, 0x6648f000, 0x66c56000, 0x6999e000, 0x6a11c000, 0x6ca04000, 0x79e60000, 0x7edce000, 0x80000000, },
+ { 16, 28, 2, 38, 10, 57, 21, 26, 61, 43, 46, 31, 56, 7, 47, 48, 58, 25, 63, 4, 59, 15, 32, 50, 1, 40, 53, 18, 17, 24, 29, 30, 55, 36, 49, 42, 41, 37, 23, 39, 6, 51, 33, 9, 45, 5, 35, 19, 44, 11, 34, 0, 27, 12, 60, 62, 20, 13, 22, 8, 14, 54, 3, 52, },
+};
+const pattern_t pat12 = {
+ { 0x513000, 0x1b72000, 0x1e27000, 0x3a63000, 0x1115c000, 0x158b4000, 0x1664f000, 0x1b667000, 0x1f838000, 0x21410000, 0x260c7000, 0x2cd8f000, 0x2ce37000, 0x2df16000, 0x2e59e000, 0x2e8eb000, 0x2ebd2000, 0x2f1d2000, 0x2fc42000, 0x30d00000, 0x31ef1000, 0x3301a000, 0x38097000, 0x38a1e000, 0x3d818000, 0x3e898000, 0x3f90f000, 0x47710000, 0x478bb000, 0x485ab000, 0x48e54000, 0x4cfe1000, 0x53a89000, 0x53d10000, 0x56308000, 0x56f3b000, 0x577f6000, 0x58734000, 0x5889b000, 0x58ad7000, 0x5923a000, 0x59aef000, 0x5dad3000, 0x5e32f000, 0x63b3a000, 0x665c9000, 0x68cde000, 0x69252000, 0x6a777000, 0x6a79a000, 0x6c1f0000, 0x6cb9a000, 0x6d319000, 0x6dc82000, 0x6dd4d000, 0x6e188000, 0x7184d000, 0x7206e000, 0x73980000, 0x740ad000, 0x75473000, 0x7614d000, 0x79b17000, 0x80000000, },
+ { 4, 60, 10, 35, 6, 31, 5, 18, 53, 17, 20, 8, 56, 29, 7, 48, 40, 0, 12, 39, 2, 43, 15, 61, 42, 30, 50, 14, 49, 38, 34, 58, 24, 55, 33, 63, 28, 51, 59, 46, 11, 22, 45, 41, 13, 44, 23, 47, 3, 32, 16, 54, 26, 19, 25, 52, 27, 57, 36, 9, 37, 21, 62, 1, },
+};
+const pattern_t pat13 = {
+ { 0x351e000, 0x5917000, 0xa992000, 0xc471000, 0xc69c000, 0xc6ed000, 0xc919000, 0xd713000, 0xec14000, 0xfa31000, 0x17567000, 0x1d81a000, 0x1f3c8000, 0x215b5000, 0x26e41000, 0x2a2d4000, 0x2b750000, 0x2bea0000, 0x2c5ae000, 0x2ca2c000, 0x30a94000, 0x31074000, 0x314d3000, 0x31b1e000, 0x31de2000, 0x32062000, 0x33da5000, 0x37838000, 0x385ec000, 0x38740000, 0x387f9000, 0x38be6000, 0x3d2f7000, 0x3eaf5000, 0x40266000, 0x402f3000, 0x40fda000, 0x4a4bd000, 0x4b831000, 0x4bfc9000, 0x4ccaa000, 0x4ea43000, 0x50190000, 0x547c8000, 0x58cc6000, 0x58ea5000, 0x59de1000, 0x5c7f1000, 0x5f713000, 0x63f9a000, 0x6686d000, 0x675c1000, 0x6cccc000, 0x6e409000, 0x6fb6d000, 0x71a70000, 0x72f60000, 0x77bd9000, 0x79013000, 0x7a8d3000, 0x7b341000, 0x7d8f7000, 0x7fe43000, 0x80000000, },
+ { 10, 52, 48, 8, 34, 4, 35, 19, 3, 17, 54, 45, 31, 38, 24, 44, 21, 36, 22, 11, 43, 40, 39, 26, 5, 30, 2, 7, 57, 12, 20, 32, 62, 15, 55, 14, 25, 58, 6, 33, 49, 9, 59, 27, 13, 63, 42, 61, 1, 51, 0, 50, 37, 47, 16, 18, 41, 56, 60, 46, 23, 28, 53, 29, },
+};
+const pattern_t pat14 = {
+ { 0xf8e000, 0x169a000, 0x3816000, 0x67a9000, 0x89f3000, 0xac97000, 0xc8da000, 0xf077000, 0x119f1000, 0x13902000, 0x19785000, 0x1ca7f000, 0x1f958000, 0x2027d000, 0x2251f000, 0x24661000, 0x25604000, 0x2b924000, 0x2be5f000, 0x2ec27000, 0x330a5000, 0x3349e000, 0x33a84000, 0x344fa000, 0x34514000, 0x37966000, 0x37f0b000, 0x37fcd000, 0x386d0000, 0x39600000, 0x39de4000, 0x3e601000, 0x3e7f1000, 0x42c61000, 0x48806000, 0x4d3d0000, 0x4f5ff000, 0x512c1000, 0x53fd4000, 0x59440000, 0x5b386000, 0x5e8a0000, 0x5fca3000, 0x6016c000, 0x61ca8000, 0x64915000, 0x66b99000, 0x67226000, 0x69b2f000, 0x6a473000, 0x6a590000, 0x6c844000, 0x6cb8c000, 0x713b5000, 0x7558a000, 0x75eab000, 0x76d15000, 0x77efe000, 0x78762000, 0x7bddc000, 0x7ce92000, 0x7dc44000, 0x7f54b000, 0x80000000, },
+ { 54, 31, 48, 10, 51, 49, 55, 19, 38, 18, 44, 5, 17, 20, 16, 11, 9, 3, 42, 59, 63, 45, 25, 60, 57, 21, 40, 29, 0, 39, 26, 7, 53, 12, 13, 2, 58, 41, 22, 8, 14, 28, 46, 24, 27, 6, 52, 32, 56, 4, 30, 36, 15, 47, 23, 37, 43, 35, 50, 33, 61, 34, 1, 62, },
+};
+const pattern_t pat15 = {
+ { 0xe16000, 0xec6000, 0xf6b000, 0x634b000, 0x6896000, 0x91db000, 0xc2c8000, 0xe083000, 0xfd7f000, 0x10479000, 0x17740000, 0x18292000, 0x1aaca000, 0x1cb55000, 0x1d2be000, 0x222af000, 0x2cb03000, 0x2fabc000, 0x32034000, 0x35c0f000, 0x3c5ec000, 0x40908000, 0x4128e000, 0x44411000, 0x44bcd000, 0x4f0ac000, 0x5167b000, 0x5541c000, 0x581bc000, 0x584e5000, 0x588fb000, 0x593d3000, 0x5b25d000, 0x5dc99000, 0x60b35000, 0x60ffc000, 0x638e8000, 0x63cf8000, 0x64ee1000, 0x6523c000, 0x654a1000, 0x687bf000, 0x68bef000, 0x69826000, 0x69d90000, 0x6a622000, 0x6d1b9000, 0x6d1e1000, 0x6d886000, 0x6fe4a000, 0x6feac000, 0x720b2000, 0x734dc000, 0x73530000, 0x73f1e000, 0x7479c000, 0x76e33000, 0x786dc000, 0x79b40000, 0x79e49000, 0x7b66c000, 0x7b904000, 0x7c906000, 0x80000000, },
+ { 6, 17, 62, 20, 61, 32, 22, 14, 28, 18, 3, 42, 63, 43, 46, 34, 29, 30, 35, 1, 37, 40, 10, 26, 5, 31, 15, 54, 8, 33, 9, 4, 39, 53, 23, 25, 41, 59, 12, 13, 60, 2, 7, 56, 58, 27, 11, 38, 36, 45, 47, 0, 57, 50, 48, 16, 51, 49, 55, 52, 44, 24, 19, 21, },
+};
+const pattern_t pat16 = {
+ { 0x596a000, 0x8644000, 0xa943000, 0xd59e000, 0x1062f000, 0x1082a000, 0x10c1b000, 0x10f9e000, 0x11e64000, 0x12e73000, 0x15ce7000, 0x16037000, 0x16d2e000, 0x17035000, 0x185ad000, 0x18d9b000, 0x19ac7000, 0x1b2fa000, 0x1cd6c000, 0x1d5f0000, 0x1f72c000, 0x20891000, 0x24bfa000, 0x25c1d000, 0x28e24000, 0x2a5f8000, 0x2e0ae000, 0x2fddf000, 0x3119d000, 0x332ee000, 0x3480a000, 0x34ea5000, 0x3534e000, 0x3538b000, 0x362e2000, 0x38f58000, 0x39ab0000, 0x3a519000, 0x3a62b000, 0x3b006000, 0x3d523000, 0x3e0f7000, 0x42366000, 0x42feb000, 0x44013000, 0x46b98000, 0x49794000, 0x4dce7000, 0x4f1f3000, 0x57ecd000, 0x5aaa2000, 0x5f419000, 0x61517000, 0x6797d000, 0x69a20000, 0x6a070000, 0x70575000, 0x75322000, 0x75a9e000, 0x79043000, 0x79875000, 0x7addc000, 0x7de88000, 0x80000000, },
+ { 26, 25, 6, 50, 32, 53, 34, 27, 3, 16, 49, 28, 46, 38, 56, 4, 18, 24, 51, 36, 63, 5, 48, 13, 43, 55, 0, 62, 35, 7, 41, 21, 44, 60, 31, 39, 14, 8, 61, 58, 52, 23, 59, 33, 10, 37, 20, 30, 40, 22, 11, 54, 57, 1, 29, 47, 2, 17, 19, 45, 15, 9, 12, 42, },
+};
+const pattern_t pat17 = {
+ { 0x28ab000, 0x3ac8000, 0x3fe1000, 0x63a7000, 0x90fc000, 0xb3f2000, 0xd2f2000, 0xe032000, 0x12d4c000, 0x13135000, 0x14652000, 0x15331000, 0x1570c000, 0x1688e000, 0x16bc3000, 0x1cbe3000, 0x1fe0f000, 0x2517f000, 0x26c6b000, 0x2a284000, 0x2a4e2000, 0x2add5000, 0x2bd06000, 0x2ca3a000, 0x2eb11000, 0x324d1000, 0x35662000, 0x38695000, 0x38ce7000, 0x391ac000, 0x398f9000, 0x39949000, 0x401f3000, 0x457f0000, 0x45c6d000, 0x4b561000, 0x522fc000, 0x54ef0000, 0x559f8000, 0x562a7000, 0x56a04000, 0x57b68000, 0x59702000, 0x5ffc9000, 0x63a76000, 0x63c37000, 0x65d3e000, 0x67130000, 0x6a03a000, 0x6bcd6000, 0x6be96000, 0x6bf52000, 0x6fcd9000, 0x7038c000, 0x70a47000, 0x72881000, 0x72ed0000, 0x75035000, 0x75c11000, 0x77fa5000, 0x797c9000, 0x79813000, 0x7bbbb000, 0x80000000, },
+ { 18, 15, 56, 5, 25, 47, 39, 55, 12, 14, 51, 33, 0, 7, 9, 44, 50, 31, 62, 59, 3, 35, 23, 17, 30, 60, 11, 24, 40, 20, 52, 2, 22, 8, 57, 42, 32, 54, 36, 48, 49, 13, 58, 10, 28, 63, 16, 41, 27, 21, 37, 4, 1, 29, 19, 6, 53, 45, 46, 38, 34, 43, 61, 26, },
+};
+const pattern_t pat18 = {
+ { 0x38d6000, 0x5379000, 0x5cae000, 0x5d20000, 0xa248000, 0xb4d0000, 0xd7c0000, 0xf731000, 0x116ae000, 0x151d2000, 0x1747d000, 0x1bfb6000, 0x1d758000, 0x2053d000, 0x24dda000, 0x25274000, 0x269c0000, 0x273e8000, 0x2a5d0000, 0x2ad34000, 0x3016b000, 0x30d1a000, 0x32960000, 0x34b3b000, 0x36e4f000, 0x37934000, 0x38c42000, 0x3c2d2000, 0x3d23d000, 0x3d89a000, 0x3dc85000, 0x3e9a7000, 0x3f25b000, 0x45bd1000, 0x48d94000, 0x4b126000, 0x4e17c000, 0x4f377000, 0x50908000, 0x51957000, 0x53410000, 0x5412c000, 0x55256000, 0x56b17000, 0x5707b000, 0x5bbe5000, 0x5d067000, 0x5e1c1000, 0x6380b000, 0x66009000, 0x68240000, 0x69fc4000, 0x6c327000, 0x6c5d2000, 0x6f69d000, 0x739c7000, 0x744bc000, 0x74cd8000, 0x787b8000, 0x78c61000, 0x7969d000, 0x79aae000, 0x7b032000, 0x80000000, },
+ { 16, 48, 50, 60, 13, 39, 20, 4, 63, 18, 14, 30, 55, 8, 62, 37, 43, 41, 11, 0, 36, 33, 34, 49, 17, 58, 38, 22, 19, 5, 21, 12, 47, 25, 57, 61, 7, 3, 10, 23, 52, 24, 6, 53, 2, 26, 1, 31, 28, 46, 42, 9, 45, 29, 27, 54, 32, 56, 51, 44, 35, 59, 40, 15, },
+};
+const pattern_t pat19 = {
+ { 0x297d000, 0x34e0000, 0x7801000, 0x9664000, 0x96fa000, 0xbb9f000, 0xc192000, 0xc4a5000, 0xca74000, 0xcce8000, 0x173d8000, 0x1a8d8000, 0x1b299000, 0x1b52d000, 0x1e813000, 0x2185e000, 0x21abe000, 0x2b9a4000, 0x2c4b6000, 0x2fa27000, 0x343ba000, 0x356fd000, 0x37c6e000, 0x38365000, 0x3a9e4000, 0x3b599000, 0x4296b000, 0x43196000, 0x4381e000, 0x44783000, 0x47a75000, 0x4bd78000, 0x4d05d000, 0x4edb2000, 0x4eefd000, 0x4fecc000, 0x51f68000, 0x5252b000, 0x5439e000, 0x55fb3000, 0x5814f000, 0x5939d000, 0x60a78000, 0x62a86000, 0x633b0000, 0x64a68000, 0x64b62000, 0x66207000, 0x66540000, 0x67f90000, 0x68bf3000, 0x6a069000, 0x6d2ac000, 0x70c9f000, 0x71bab000, 0x724bc000, 0x783d8000, 0x7900e000, 0x79399000, 0x79763000, 0x7c8a8000, 0x7e680000, 0x7f6de000, 0x80000000, },
+ { 14, 59, 60, 24, 18, 22, 62, 12, 45, 2, 32, 11, 25, 37, 13, 7, 50, 39, 56, 17, 47, 40, 29, 43, 15, 34, 4, 57, 31, 38, 21, 28, 36, 27, 42, 1, 23, 33, 5, 61, 44, 55, 8, 30, 10, 41, 19, 48, 16, 52, 49, 46, 54, 58, 6, 0, 51, 3, 26, 20, 53, 9, 35, 63, },
+};
+const pattern_t pat20 = {
+ { 0x8f7000, 0xa01000, 0x38e3000, 0x5299000, 0x6875000, 0x7f3e000, 0x827f000, 0x9413000, 0xca71000, 0xfb11000, 0x10beb000, 0x176be000, 0x1924f000, 0x1cfd6000, 0x1d20d000, 0x1ebb7000, 0x22c93000, 0x23601000, 0x2cf7a000, 0x2d3af000, 0x2e391000, 0x2f294000, 0x318ad000, 0x34ddd000, 0x365e4000, 0x3b8d2000, 0x3c0f8000, 0x3d2fd000, 0x3e431000, 0x3f0fe000, 0x4074e000, 0x40d1c000, 0x41936000, 0x4347b000, 0x452d7000, 0x486d3000, 0x4b47e000, 0x4b709000, 0x4c349000, 0x4ff13000, 0x50faa000, 0x51a07000, 0x52f30000, 0x55f29000, 0x57ad4000, 0x5909f000, 0x5a0f6000, 0x5de57000, 0x60d2f000, 0x625ff000, 0x6288e000, 0x65077000, 0x6a707000, 0x6a73f000, 0x6d720000, 0x6e390000, 0x6edc9000, 0x6ee78000, 0x77ac2000, 0x77ad3000, 0x7868f000, 0x79a1b000, 0x7aec6000, 0x80000000, },
+ { 21, 56, 38, 11, 62, 9, 30, 47, 34, 23, 37, 16, 5, 49, 10, 43, 4, 45, 36, 7, 42, 1, 53, 57, 20, 59, 55, 50, 46, 39, 60, 27, 12, 31, 48, 25, 15, 22, 44, 52, 14, 33, 0, 29, 17, 18, 2, 32, 24, 19, 6, 41, 54, 8, 35, 26, 61, 3, 51, 13, 63, 28, 40, 58, },
+};
+const pattern_t pat21 = {
+ { 0x2a8e000, 0xa4bd000, 0xa935000, 0xcc05000, 0xdfe4000, 0xe014000, 0xff46000, 0x18c8c000, 0x199ec000, 0x19ce0000, 0x1f684000, 0x1ff5c000, 0x22d58000, 0x27651000, 0x280e5000, 0x2e2a4000, 0x2e432000, 0x2f96e000, 0x2f9c3000, 0x3343d000, 0x338bb000, 0x34032000, 0x34101000, 0x368c2000, 0x37b95000, 0x39492000, 0x39932000, 0x3b611000, 0x3c89e000, 0x40aa9000, 0x42358000, 0x4890e000, 0x495c9000, 0x4a79d000, 0x4c58e000, 0x4df9a000, 0x4f304000, 0x4fa4c000, 0x54d1d000, 0x58461000, 0x58f43000, 0x5a3d1000, 0x5a765000, 0x5c5c0000, 0x60488000, 0x60fad000, 0x613e5000, 0x61d61000, 0x62d17000, 0x641ff000, 0x67f8b000, 0x69c5d000, 0x6b931000, 0x6efd4000, 0x70333000, 0x70857000, 0x721f6000, 0x72f53000, 0x74450000, 0x746f7000, 0x76067000, 0x7774a000, 0x77ea6000, 0x80000000, },
+ { 28, 51, 33, 2, 30, 55, 29, 17, 40, 48, 32, 9, 39, 1, 49, 50, 37, 43, 62, 11, 10, 26, 22, 6, 8, 7, 45, 47, 46, 42, 60, 5, 12, 56, 4, 23, 35, 25, 13, 16, 61, 54, 31, 63, 34, 19, 41, 59, 38, 24, 0, 58, 53, 44, 3, 18, 52, 20, 36, 27, 14, 21, 57, 15, },
+};
+const pattern_t pat22 = {
+ { 0x314d000, 0x4452000, 0x6673000, 0xab09000, 0xc80d000, 0x10eda000, 0x129c2000, 0x12f1f000, 0x13e9b000, 0x1450c000, 0x15aeb000, 0x1667c000, 0x190b2000, 0x19ac5000, 0x1c0ac000, 0x1c229000, 0x1ece8000, 0x1fc48000, 0x22abd000, 0x24268000, 0x2adce000, 0x2b809000, 0x30a11000, 0x31d08000, 0x36700000, 0x39e6a000, 0x3b84b000, 0x41e84000, 0x46301000, 0x4a326000, 0x50fda000, 0x5299a000, 0x56acf000, 0x57f66000, 0x586ab000, 0x58df5000, 0x591cc000, 0x59b91000, 0x59cbd000, 0x5b4d0000, 0x5cca7000, 0x5cfce000, 0x5d120000, 0x5d51a000, 0x5eaa0000, 0x5ebac000, 0x5f0e1000, 0x5f285000, 0x5f4d3000, 0x5ff61000, 0x60b51000, 0x61435000, 0x651fd000, 0x6b954000, 0x705aa000, 0x71a41000, 0x73ec7000, 0x75f92000, 0x76854000, 0x77cb9000, 0x782ca000, 0x7a2af000, 0x7eaa6000, 0x80000000, },
+ { 16, 8, 5, 59, 4, 18, 26, 43, 33, 57, 6, 47, 56, 46, 10, 54, 52, 0, 50, 30, 39, 24, 38, 63, 28, 25, 49, 31, 55, 62, 3, 17, 23, 13, 37, 53, 34, 14, 44, 12, 19, 36, 27, 61, 51, 42, 41, 60, 45, 1, 7, 35, 21, 58, 20, 15, 2, 9, 22, 29, 48, 32, 40, 11, },
+};
+const pattern_t pat23 = {
+ { 0x47000, 0x680000, 0x176a000, 0x1db8000, 0x600e000, 0x808c000, 0x9e58000, 0xa82f000, 0xaebb000, 0xc938000, 0xd0eb000, 0xdc5e000, 0xe503000, 0x11e56000, 0x12dbd000, 0x14681000, 0x15200000, 0x18256000, 0x1be75000, 0x1d2a0000, 0x1ed67000, 0x27e8a000, 0x2bc39000, 0x2bf4b000, 0x2c94f000, 0x2d575000, 0x2d82e000, 0x2e440000, 0x2ecda000, 0x2fbc2000, 0x33ab0000, 0x360b8000, 0x39630000, 0x3a654000, 0x3d2b6000, 0x3eeff000, 0x41590000, 0x417ea000, 0x42ff9000, 0x4bca4000, 0x503b0000, 0x508e4000, 0x52a2e000, 0x535b6000, 0x54335000, 0x57412000, 0x57dc6000, 0x590d9000, 0x5fc0a000, 0x60552000, 0x60665000, 0x6168d000, 0x65b16000, 0x67328000, 0x6742d000, 0x68c88000, 0x6b802000, 0x6f6d2000, 0x7040e000, 0x77e31000, 0x79c71000, 0x7da4a000, 0x7e26b000, 0x80000000, },
+ { 63, 50, 51, 33, 28, 35, 24, 14, 4, 23, 47, 11, 37, 41, 12, 55, 62, 32, 34, 30, 25, 43, 16, 0, 3, 49, 61, 15, 57, 46, 59, 44, 31, 27, 21, 53, 5, 2, 8, 56, 52, 22, 60, 40, 20, 1, 48, 18, 17, 19, 54, 29, 9, 38, 42, 6, 39, 45, 13, 10, 26, 58, 36, 7, },
+};
+const pattern_t pat24 = {
+ { 0x3890000, 0x3fd9000, 0x62d6000, 0x7df0000, 0x895c000, 0xab61000, 0xc23b000, 0x10ab3000, 0x1247c000, 0x13f00000, 0x16604000, 0x1a444000, 0x1c2c8000, 0x1c467000, 0x1d396000, 0x1e683000, 0x21080000, 0x2442a000, 0x27fea000, 0x282eb000, 0x28e2c000, 0x2a625000, 0x2b3b2000, 0x2bbd5000, 0x2c886000, 0x2cbe8000, 0x31518000, 0x35425000, 0x355f4000, 0x35d7a000, 0x3851d000, 0x396a6000, 0x3d10f000, 0x3d890000, 0x49238000, 0x4ab7e000, 0x4fadf000, 0x50603000, 0x5233a000, 0x53279000, 0x5586c000, 0x56968000, 0x58101000, 0x588b6000, 0x5bc19000, 0x5cc10000, 0x623f6000, 0x629f4000, 0x63176000, 0x63dcc000, 0x681d0000, 0x69c0e000, 0x6a9fa000, 0x6ae5e000, 0x6d2ba000, 0x6e422000, 0x73f94000, 0x77932000, 0x78b24000, 0x794c1000, 0x795d2000, 0x7ae08000, 0x7b3ce000, 0x80000000, },
+ { 56, 47, 54, 62, 29, 43, 25, 59, 41, 7, 52, 63, 15, 21, 16, 14, 39, 17, 45, 11, 27, 24, 55, 31, 53, 4, 6, 2, 20, 23, 5, 37, 32, 58, 13, 51, 1, 8, 3, 57, 46, 30, 35, 49, 18, 40, 9, 22, 42, 38, 34, 0, 19, 33, 26, 60, 10, 48, 36, 61, 44, 12, 50, 28, },
+};
+const pattern_t pat25 = {
+ { 0xcb000, 0x22bf000, 0x2461000, 0x246b000, 0x5c6f000, 0x5fb4000, 0x69a9000, 0x718c000, 0x92e6000, 0xbb2e000, 0xd916000, 0xf3dc000, 0xf568000, 0x10246000, 0x12d53000, 0x14dfd000, 0x1598a000, 0x1956a000, 0x1b01b000, 0x1b3b8000, 0x1ce06000, 0x20bc5000, 0x21351000, 0x233b0000, 0x23f2b000, 0x24e41000, 0x29cca000, 0x2b5bd000, 0x2ba68000, 0x2bf7f000, 0x31a58000, 0x34570000, 0x39941000, 0x3b765000, 0x3cd13000, 0x3d251000, 0x3fa05000, 0x40745000, 0x45c68000, 0x4a282000, 0x4ad19000, 0x4b4aa000, 0x4ca18000, 0x4e0b8000, 0x4eb97000, 0x4f68b000, 0x4fca2000, 0x52466000, 0x52edf000, 0x5602a000, 0x57f60000, 0x5cc18000, 0x5cee3000, 0x5da37000, 0x5dba4000, 0x64b77000, 0x66e9f000, 0x68b9d000, 0x6aac9000, 0x6d873000, 0x6f7e6000, 0x71036000, 0x75a54000, 0x80000000, },
+ { 1, 58, 13, 5, 62, 10, 49, 48, 19, 24, 54, 57, 20, 39, 35, 41, 28, 42, 6, 44, 34, 45, 55, 3, 2, 60, 38, 36, 30, 25, 7, 23, 53, 50, 61, 29, 40, 47, 22, 12, 27, 0, 52, 31, 8, 15, 37, 11, 46, 32, 4, 56, 17, 33, 26, 43, 51, 16, 9, 59, 63, 21, 14, 18, },
+};
+const pattern_t pat26 = {
+ { 0x1f4e000, 0x46f7000, 0x90a7000, 0xafe3000, 0xb2a8000, 0xb8b0000, 0xc480000, 0xc4e1000, 0xf03c000, 0xf075000, 0x14dbd000, 0x17728000, 0x1a146000, 0x1ab02000, 0x1af49000, 0x1be7a000, 0x1dc7a000, 0x1de5e000, 0x1f4f9000, 0x2092b000, 0x20a63000, 0x22937000, 0x22fe3000, 0x23e1c000, 0x28c46000, 0x294d6000, 0x2ad19000, 0x2b18b000, 0x2b233000, 0x2b685000, 0x2c792000, 0x2e6c2000, 0x2fa86000, 0x3320f000, 0x36f17000, 0x38406000, 0x38b1b000, 0x3a132000, 0x3b269000, 0x3cc43000, 0x3e2a2000, 0x3e3bb000, 0x3e83b000, 0x3ea14000, 0x4035d000, 0x4137f000, 0x4615b000, 0x50531000, 0x517c8000, 0x519e6000, 0x55ebc000, 0x594f8000, 0x5a732000, 0x5d2cb000, 0x5e409000, 0x5f394000, 0x5f3a8000, 0x60dc6000, 0x61373000, 0x6ebd6000, 0x6fd61000, 0x77161000, 0x7ce81000, 0x80000000, },
+ { 61, 48, 12, 55, 3, 58, 51, 56, 15, 29, 54, 11, 31, 49, 40, 37, 7, 4, 23, 35, 25, 18, 27, 43, 6, 41, 17, 45, 52, 53, 47, 16, 42, 0, 30, 13, 38, 62, 1, 8, 21, 28, 57, 9, 60, 19, 44, 50, 14, 36, 22, 2, 32, 59, 34, 10, 63, 39, 5, 24, 33, 20, 46, 26, },
+};
+const pattern_t pat27 = {
+ { 0xa6a000, 0x4c7a000, 0x5183000, 0x8dda000, 0x9cbd000, 0xb860000, 0x10c24000, 0x12dda000, 0x147ab000, 0x14aa4000, 0x16c8f000, 0x17d5b000, 0x18b5c000, 0x1a163000, 0x1b0a1000, 0x24221000, 0x25ef8000, 0x267f1000, 0x268b7000, 0x26b07000, 0x273ad000, 0x27bc2000, 0x2856c000, 0x29896000, 0x2efeb000, 0x331a7000, 0x348e8000, 0x3707f000, 0x3f444000, 0x3fe2a000, 0x433b3000, 0x435d3000, 0x46d82000, 0x4a9d3000, 0x4c6cf000, 0x4ca36000, 0x4ec42000, 0x4f79c000, 0x53cd3000, 0x58c78000, 0x5d910000, 0x616cc000, 0x62800000, 0x65ded000, 0x68831000, 0x6b321000, 0x6cd46000, 0x6d0fa000, 0x6d2f9000, 0x6e353000, 0x6fd5e000, 0x706c5000, 0x7249f000, 0x75d6c000, 0x77528000, 0x783ad000, 0x79738000, 0x79bfe000, 0x79ee9000, 0x7b74a000, 0x7bb41000, 0x7bbeb000, 0x7bbfb000, 0x80000000, },
+ { 61, 53, 12, 15, 26, 30, 32, 2, 16, 5, 39, 43, 20, 21, 49, 37, 11, 51, 18, 44, 31, 19, 24, 40, 1, 35, 50, 6, 57, 14, 46, 17, 22, 48, 29, 7, 34, 45, 10, 63, 23, 41, 54, 38, 4, 25, 42, 13, 56, 62, 36, 28, 33, 59, 55, 3, 9, 0, 58, 60, 47, 8, 52, 27, },
+};
+const pattern_t pat28 = {
+ { 0x439a000, 0x6860000, 0xd252000, 0x1105c000, 0x113c8000, 0x1429a000, 0x14922000, 0x15f32000, 0x1992f000, 0x1a1db000, 0x1a87c000, 0x1b260000, 0x1b292000, 0x1c253000, 0x1ea33000, 0x20bbc000, 0x215ae000, 0x25249000, 0x27c89000, 0x27e36000, 0x28bf2000, 0x29c27000, 0x2a575000, 0x2c6fa000, 0x31639000, 0x3184a000, 0x319c3000, 0x348a7000, 0x38aa8000, 0x39dd5000, 0x3a067000, 0x3c0dd000, 0x3cfd4000, 0x3ebb6000, 0x43259000, 0x46494000, 0x46fcb000, 0x4a050000, 0x4b5c4000, 0x4cff3000, 0x4edaa000, 0x4f025000, 0x542e1000, 0x55364000, 0x56338000, 0x56ef8000, 0x5711b000, 0x573d1000, 0x5943b000, 0x5b912000, 0x61ce2000, 0x65211000, 0x65dca000, 0x6dee2000, 0x6df30000, 0x7334d000, 0x73e76000, 0x7473a000, 0x75846000, 0x75fd0000, 0x77174000, 0x773e9000, 0x7a8db000, 0x80000000, },
+ { 43, 11, 8, 56, 5, 22, 42, 55, 14, 32, 2, 47, 24, 51, 35, 25, 15, 58, 41, 27, 33, 37, 4, 36, 7, 53, 26, 48, 38, 19, 29, 28, 40, 10, 1, 46, 59, 63, 61, 62, 60, 30, 21, 39, 44, 57, 20, 18, 17, 54, 49, 52, 3, 12, 45, 13, 50, 9, 16, 23, 0, 6, 31, 34, },
+};
+const pattern_t pat29 = {
+ { 0x363000, 0x9bc000, 0x1907000, 0x41d5000, 0x5a6e000, 0x9f36000, 0xa3ee000, 0x14b98000, 0x1845c000, 0x188ea000, 0x1b297000, 0x1c024000, 0x1e1eb000, 0x1f3a4000, 0x2047f000, 0x2420a000, 0x28871000, 0x296dd000, 0x2c92c000, 0x2dd42000, 0x3444f000, 0x35b90000, 0x3683c000, 0x3d8ea000, 0x3fe6b000, 0x4200e000, 0x421cf000, 0x42a46000, 0x44463000, 0x44e61000, 0x45c82000, 0x485f8000, 0x48fe8000, 0x4a532000, 0x4a6fd000, 0x4c8f9000, 0x4dbd7000, 0x5052a000, 0x512bb000, 0x5281d000, 0x5315d000, 0x5a202000, 0x5a9fc000, 0x5c11a000, 0x6010b000, 0x62aa3000, 0x63d05000, 0x6774c000, 0x6776d000, 0x68105000, 0x699d5000, 0x69bc2000, 0x6b1b9000, 0x704d5000, 0x73d5c000, 0x73d94000, 0x78483000, 0x78c8c000, 0x78cc5000, 0x7ac8d000, 0x7ae00000, 0x7b597000, 0x7e6ab000, 0x80000000, },
+ { 52, 40, 59, 29, 42, 34, 63, 44, 33, 37, 51, 23, 5, 36, 38, 43, 9, 4, 28, 55, 1, 6, 21, 26, 13, 24, 30, 15, 35, 17, 46, 20, 16, 10, 49, 48, 39, 62, 19, 14, 61, 27, 53, 2, 57, 58, 45, 7, 56, 50, 54, 25, 31, 11, 22, 47, 3, 0, 32, 12, 8, 41, 60, 18, },
+};
+const pattern_t pat30 = {
+ { 0x45c5000, 0x6bf3000, 0xc293000, 0xe470000, 0xe5b7000, 0x1256c000, 0x1444d000, 0x15699000, 0x16e86000, 0x1a4d5000, 0x1b803000, 0x1dcf9000, 0x1dd6f000, 0x1f57f000, 0x22879000, 0x263e9000, 0x29423000, 0x2a1a9000, 0x2a699000, 0x2c8fb000, 0x2d2e0000, 0x2ec5e000, 0x317a4000, 0x35a64000, 0x36967000, 0x37299000, 0x37c07000, 0x3b9bb000, 0x3c054000, 0x3ccbc000, 0x3d94a000, 0x3e2e9000, 0x3e7a4000, 0x40b98000, 0x44658000, 0x44738000, 0x44fe3000, 0x451d9000, 0x4536c000, 0x46df2000, 0x48855000, 0x503ce000, 0x53104000, 0x531fc000, 0x54c1b000, 0x56086000, 0x5642b000, 0x573a4000, 0x5887f000, 0x5a871000, 0x5c970000, 0x5e566000, 0x62b8f000, 0x642ce000, 0x65ee5000, 0x66db3000, 0x6727c000, 0x6a9a2000, 0x74a8f000, 0x7c29a000, 0x7cc57000, 0x7f221000, 0x7f28f000, 0x80000000, },
+ { 29, 35, 8, 49, 30, 55, 27, 38, 58, 61, 0, 28, 15, 39, 5, 37, 32, 42, 46, 54, 12, 14, 1, 31, 59, 11, 47, 9, 13, 50, 2, 62, 60, 18, 20, 51, 23, 24, 53, 6, 25, 48, 41, 3, 33, 57, 44, 19, 22, 52, 4, 45, 10, 21, 56, 36, 17, 43, 7, 63, 16, 34, 26, 40, },
+};
+const pattern_t pat31 = {
+ { 0x143f000, 0x2068000, 0x328c000, 0x70a6000, 0x92a7000, 0x93dd000, 0xa3a8000, 0xbe51000, 0xbfc8000, 0xe353000, 0x1272f000, 0x143a4000, 0x16825000, 0x20bf8000, 0x20d9f000, 0x21e32000, 0x22426000, 0x2246b000, 0x22cea000, 0x25dc2000, 0x29324000, 0x29cd1000, 0x2aa44000, 0x2cd84000, 0x2dafb000, 0x2e74b000, 0x2f5b1000, 0x3a7a9000, 0x3bb38000, 0x3c11a000, 0x3c30a000, 0x3e2f1000, 0x4187b000, 0x42190000, 0x44e34000, 0x4d850000, 0x53ceb000, 0x540db000, 0x54937000, 0x5530a000, 0x5a111000, 0x5c280000, 0x5ef17000, 0x5fccf000, 0x64434000, 0x6498e000, 0x662c4000, 0x6a7e2000, 0x6b5a1000, 0x6c11f000, 0x6dd97000, 0x6ef1b000, 0x6f44e000, 0x7084f000, 0x73b53000, 0x7872c000, 0x78ed7000, 0x7935b000, 0x79bf9000, 0x7a6af000, 0x7b6fd000, 0x7bd42000, 0x7f233000, 0x80000000, },
+ { 6, 25, 28, 44, 27, 43, 58, 33, 23, 21, 16, 48, 30, 26, 5, 20, 49, 38, 2, 45, 11, 61, 17, 0, 53, 13, 7, 52, 40, 31, 36, 4, 10, 8, 24, 22, 42, 63, 35, 60, 47, 29, 46, 19, 1, 3, 34, 55, 59, 14, 39, 12, 32, 50, 62, 54, 56, 51, 57, 15, 41, 18, 37, 9, },
+};
+const pattern_t* patterns[] = {&trivial_pattern,
+ &pat0, &pat1, &pat2, &pat3, &pat4, &pat5, &pat6, &pat7,
+ &pat8, &pat9, &pat10, &pat11, &pat12, &pat13, &pat14, &pat15,
+ &pat16, &pat17, &pat18, &pat19, &pat20, &pat21, &pat22, &pat23,
+ &pat24, &pat25, &pat26, &pat27, &pat28, &pat29, &pat30, &pat31};
+
+//static variables used by the worker threads
+static int outfd = 0;
+static sem_t chunk_sems[NUM_THREADS - 1][NUM_CHUNKS];
+
+static void
+usage()
+{
+ (void) fprintf(stderr, "usage: fsync_integrity <file name>\n");
+ exit(2);
+}
+
+
+/* Fills a buffer with a special marker. The marker contains information about
+ * the file offset where this buffer is supposed to go, and whether it will
+ * be written by a leader or a follower */
+static void
+marker_fill(uint64_t* buf, int file_ofs, size_t len, int thread_num){
+ int ofs;
+ uint32_t thread_mark = thread_num;
+ uint32_t final_mark = 0xe005b0ca; //"CABOOSE" in little endian
+ for (ofs = file_ofs; ofs < file_ofs + len; ofs += sizeof(uint64_t)){
+ uint64_t mark = ((thread_num == (NUM_THREADS - 1) ?
+ (uint64_t)final_mark : (uint64_t)thread_mark) << (uint64_t)32) |
+ htonl(ofs & 0xFFFFFFFF);
+ int buf_idx = (ofs - file_ofs) / sizeof(uint64_t);
+ buf[buf_idx] = mark;
+ }
+}
+
+static int
+verify_file(int fd, const pattern_t* p_pat){
+ int chunk_idx;
+ int good_data = 1;
+ int err = 0;
+
+ for(chunk_idx=0; chunk_idx < NUM_CHUNKS; chunk_idx++){
+ int i;
+ uint32_t chunk_start, chunk_end;
+ get_chunk_range(p_pat, chunk_idx, &chunk_start, &chunk_end);
+ size_t size = chunk_end - chunk_start;
+ uint64_t* desired_buf = malloc(size);
+ uint64_t* actual_buf = malloc(size);
+ marker_fill(desired_buf, chunk_start, size, NUM_THREADS - 1);
+
+ //read the actual data from the file
+ if( read(fd, actual_buf, size) <= 0 ){
+ perror("read");
+ exit(1);
+ }
+
+ //verify the data
+ for(i=0; i < size / sizeof(uint64_t); i++){
+ int chunk_offset = sizeof(uint64_t) * i;
+ int file_offset = chunk_start + chunk_offset;
+ if (good_data && (actual_buf[i] != desired_buf[i])){
+ fprintf(stderr, "fsync_integrity: miscompare at "
+ "chunk %i, chunk offset %x, file offset %x\n",
+ chunk_idx, chunk_offset, file_offset);
+ fprintf(stderr, "Expected %016lx, got %016lx\n",
+ desired_buf[i], actual_buf[i]);
+ err = 1;
+ good_data = 0;
+ }
+ else if (!good_data && (actual_buf[i] == desired_buf[i])) {
+ fprintf(stderr, "fsync_integrity: miscompare ends at "
+ "chunk %i, chunk offset %x, file offset %x\n",
+ chunk_idx, chunk_offset, file_offset);
+ good_data = 1;
+ }
+ }
+ free(desired_buf);
+ free(actual_buf);
+ }
+
+ return (err);
+}
+
+/* Writes a special marker to every byte within the chunk */
+static void
+write_chunk(const pattern_t* p_pat, int chunk_idx, int thread_num)
+{
+ uint32_t chunk_start, chunk_end;
+ get_chunk_range(p_pat, chunk_idx, &chunk_start, &chunk_end);
+ size_t size = chunk_end - chunk_start;
+ uint64_t* buf = malloc(size);
+ marker_fill(buf, chunk_start, size, thread_num);
+ pwrite(outfd, (void*)buf, size, chunk_start);
+ free(buf);
+}
+
+static void
+my_sync(int fd){
+ if (fsync(fd)){
+ perror("fsync");
+ exit(1);
+ }
+}
+
+
+static void*
+worker(void* args)
+{
+ int perm_idx, thread_num;
+ thread_data_t* data;
+
+ data = (thread_data_t*)args;
+ thread_num = data->thread_num;
+
+ for(perm_idx = 0; perm_idx < NUM_CHUNKS; perm_idx++)
+ {
+ int chunk_idx = data->pat->permutation[perm_idx];
+ /* Acquire the semaphore, if necessary */
+ if (thread_num > 0) {
+ if (-1 == sem_wait(&chunk_sems[thread_num - 1][chunk_idx])){
+ perror("sem_wait");
+ exit(1);
+ }
+ }
+ /* Write the data */
+ write_chunk(data->pat, chunk_idx, thread_num);
+ /* Sync, if we are an even thread */
+ if ((thread_num % 2) == 0)
+ my_sync(outfd);
+ /* Post the final semaphore, if necessary */
+ if (thread_num < NUM_THREADS - 1) {
+ if (sem_post(&chunk_sems[thread_num][chunk_idx]) == -1){
+ perror("sem_post");
+ exit(1);
+ }
+ }
+ }
+ return 0;
+}
+
+
+int
+main(int argc, char** argv)
+{
+ int rep;
+ int pat;
+ pthread_t threads[NUM_THREADS];
+ thread_data_t thread_data[NUM_THREADS];
+
+ if (argc != 2){
+ usage();
+ }
+
+ for(rep=0; rep < NUM_REPETITIONS; rep++){
+ printf("Starting repetition %d\n", rep);
+ for(pat=0; pat < sizeof(patterns) / sizeof(patterns[0]); pat++){
+ int i;
+ const pattern_t *pat_p = patterns[pat];
+/* pattern_t *pat_p = (void *)(uintptr_t)(const void *)patterns[pat];*/
+ int sem_idx;
+ int ofs=0;
+
+/* printf("Starting on patterns[%d]\n" , pat);*/
+ outfd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0644);
+ if (outfd == -1){
+ perror("open");
+ exit(1);
+ }
+
+ //set the file size
+ if ( ftruncate(outfd, FSIZE)){
+ perror("ftruncate");
+ exit(1);
+ }
+
+ //Zero-fill the file to avoid fragmentation, as recommended by mmap(2).
+ for(ofs=0; ofs < FSIZE; ofs+=CLUSTERSIZE){
+ char buffer[CLUSTERSIZE];
+ bzero(buffer, CLUSTERSIZE);
+ if ( -1 == write(outfd, buffer, CLUSTERSIZE)){
+ perror("write");
+ exit(1);
+ }
+ }
+ //Return the file pointer to the beginning prior to mmap
+ if (-1 == lseek(outfd, 0, SEEK_SET)){
+ perror("lseek");
+ }
+
+ //Create the semaphores
+ for(i=0; i < NUM_THREADS - 1; i++) {
+ for(sem_idx=0; sem_idx < NUM_CHUNKS; sem_idx++){
+ if (sem_init(&chunk_sems[i][sem_idx], 0, 0)){
+ perror("sem_init");
+ exit(1);
+ }
+ }
+ }
+
+ //Create the worker threads
+ for(i=0; i < NUM_THREADS; i++) {
+ thread_data[i].pat = pat_p;
+ thread_data[i].thread_num = i;
+ if (pthread_create(&threads[i], NULL, worker, (void*)&thread_data[i])){
+ perror("pthread_create");
+ exit(1);
+ }
+ }
+
+ //Join the threads
+ for(i=0; i < NUM_THREADS; i++) {
+ if (pthread_join(threads[i], NULL)){
+ perror("pthread_join");
+ exit(1);
+ }
+ }
+
+ //destroy the semaphores
+ for(i=0; i < NUM_THREADS - 1; i++) {
+ for(sem_idx=0; sem_idx < NUM_CHUNKS; sem_idx++){
+ if (sem_destroy(&chunk_sems[i][sem_idx])){
+ perror("sem_destory");
+ exit(1);
+ }
+ }
+ }
+ //printf("destroyed semaphores\n");
+
+
+ //Verify the contents of the file.
+ if (verify_file(outfd, patterns[pat])) {
+ exit(1);
+ }
+ //printf("finished verify_file\n");
+
+ //close the file:
+ if (close(outfd)){
+ perror("close");
+ exit(1);
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/tests/sys/cddl/zfs/tests/txg_integrity/fsync_integrity_001_pos.ksh b/tests/sys/cddl/zfs/tests/txg_integrity/fsync_integrity_001_pos.ksh
new file mode 100644
index 000000000000..eb9d4b9597fd
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/txg_integrity/fsync_integrity_001_pos.ksh
@@ -0,0 +1,81 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2013 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: fsync_integrity_001_pos
+#
+# DESCRIPTION:
+#
+# Verify the integrity of non-aligned writes to the same blocks within the same
+# transaction group, where an fsync is issued by a non-final writer.
+#
+# STRATEGY:
+
+# This test verifies that the unoverride in the following sequence of events is
+# handled correctly:
+#
+# 1) A new transaction group opens
+# 2) A write is issued to a certain block
+# 3) The writer fsyncs() that file
+# 4) TBD module immediately writes that block, then places an override in the
+# syncer's TBD data structure, indicating that it doesn't need to write that
+# block when syncing.
+# 5) Another write is issued to the same block, with different data.
+# 6) TBD module unoverrides that block in the syncer's TBD data structure
+# 7) The syncer writes that block
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: BEGIN (2013-1-21)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "both"
+
+log_assert "Verify the integrity of non-aligned writes to the same blocks within the same transaction group, where an fsync is issued by a non-final writer."
+
+# Run the test program
+fsync_integrity ${TESTDIR}/${TESTFILE}
+
+# Success is indicated by the return status
+if [[ $? -ne 0 ]]; then
+ log_fail "Test failed to execute or file became corrupted"
+else
+ log_pass
+fi
+
+
diff --git a/tests/sys/cddl/zfs/tests/txg_integrity/make_patterns.py b/tests/sys/cddl/zfs/tests/txg_integrity/make_patterns.py
new file mode 100644
index 000000000000..7dcd541ea64e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/txg_integrity/make_patterns.py
@@ -0,0 +1,57 @@
+#! /usr/bin/env python
+
+# Generate random IO patterns for the txg_integrity test
+# We do this statically and embed the results into the code so that the
+# Testing will be more repeatable compared to generating the tables at runtime
+
+import random
+
+CLUSTERSIZE = (1 << 16)
+NUM_CHUNKS = 64
+
+
+def rand_partition():
+ partitions = []
+ while len(partitions) != NUM_CHUNKS:
+ # We don't want any duplicates, so we make a set and then check that
+ # its length is correct
+ partitions = sorted(
+ list(
+ set(
+ [random.randrange(0,
+ 2**31,
+ (2**31) * 8 / (NUM_CHUNKS * CLUSTERSIZE))
+ for i in range(NUM_CHUNKS - 1)] + [2**31])))
+ return partitions
+
+
+def rand_permutation():
+ perm = range(NUM_CHUNKS)
+ random.shuffle(perm)
+ return perm
+
+
+def rand_follower_bitmap():
+ bmp = 0
+ chunks = random.sample(range(NUM_CHUNKS), NUM_CHUNKS / 2)
+ for chunk in chunks:
+ bmp |= (1 << chunk)
+ return bmp
+
+
+def print_pattern(n):
+ print "const pattern_t pat%d = {" % n
+ print " {",
+ for p in rand_partition():
+ print "%#x, " % p,
+ print " },"
+ print " {",
+ for p in rand_permutation():
+ print "%d, " % p,
+ print " },"
+ print " %#x" % rand_follower_bitmap()
+ print "};"
+
+
+for n in range(32):
+ print_pattern(n)
diff --git a/tests/sys/cddl/zfs/tests/txg_integrity/setup.ksh b/tests/sys/cddl/zfs/tests/txg_integrity/setup.ksh
new file mode 100644
index 000000000000..abf6ba4e28a4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/txg_integrity/setup.ksh
@@ -0,0 +1,44 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2011 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+# For this test, we create an MD instead of using the defined DISKS.
+# Data corrupts much more quickly on an MD.
+# Make it small enough that we can tar up the entire pool for post-mortem
+# analysis
+log_must $MDCONFIG -a -t swap -s 1g -u $TESTCASE_ID
+
+log_must create_pool $TESTPOOL $TESTDEV
+$RM -rf $TESTDIR
+$MKDIR -p $TESTDIR
+
+log_must $ZFS create $TESTPOOL/$TESTFS
+log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+
+log_pass
+
+
diff --git a/tests/sys/cddl/zfs/tests/txg_integrity/txg_integrity.c b/tests/sys/cddl/zfs/tests/txg_integrity/txg_integrity.c
new file mode 100644
index 000000000000..388d838f4d34
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/txg_integrity/txg_integrity.c
@@ -0,0 +1,608 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2011-2012 Spectra Logic. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Verify the integrity of non-aligned writes to the same blocks that cross
+ * transaction groups.
+ *
+ * This test verifies that non-aligned writes are correctly committed to the
+ * file system, even adjacent transaction groups include writes to the same
+ * blocks. The test runs through multiple repetitions in an attempt to trigger
+ * race conditions.
+ *
+ * Outline:
+ * Create a 32MB file.
+ * Create a bunch of different IO patterns. Each IO pattern consists of:
+ * * A partition of the 32MB range into 64 different non-overlapping chunks.
+ * * A permutation of those chunks
+ * * A selection of 32 chunks that will be written by the follower.
+ * For each repetitions:
+ * For each IO pattern:
+ * For each phase (leader syncs and follower syncs):
+ * Create one binary semaphore per chunk.
+ * Create two threads, a leader and a follower thread.
+ * The leader thread will write each chunk in order. It will post a
+ * semaphore after each write to indicate that it has completed
+ * writing that chunk. If this is the "leader syncs" phase, it will
+ * call fsync() halfway through to force the closure of a transaction
+ * group.
+ * The follower will pend on those semaphores in order. Each time it
+ * receives a semaphore, it will write a different pattern to that
+ * chunk. If this is the "follower syncs" phase, it will call fsync()
+ * halfway through to force the closure of a transaction group.
+ * Join both threads
+ * Read the entire file and verify that
+ * 1) Every write went to the correct location
+ * 2) Each of the follower's writes overwrote the leader's.
+ */
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <semaphore.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <assert.h>
+#include <arpa/inet.h>
+
+#define NUM_REPETITIONS 15
+#define CLUSTERSIZE (1 << 16)
+#define NUM_CHUNKS 64
+#define FSIZE (512 * (CLUSTERSIZE)) //FSIZE may range from NUM_CHUNKS clusters to 8GB
+#define USE_THREADS 1
+#define USE_MMAP 0
+
+typedef struct {
+ //partitions describes the boundaries between chunks. Each element is a
+ //fraction of the filesize in 1.31 fixed point format. So the boundary
+ //between chunk n and chunk n-1 is (FSIZE * partitions[n-1] / (1<<31) .
+ //partitions[-1] is understood to be 0 and partitions[NUM_CHUNKS] must be 1.0
+ //partitions is sorted, of course.
+ //Partition boundaries must be dword aligned. Thus, in order to work with
+ //multiple values of FSIZE, partitions values must be aligned to multiples of
+ //8 / (NUM_CHUNKS * CLUSTERSIZE) = 1 / 524288 = 0x0.00002
+ uint32_t partitions[NUM_CHUNKS];
+ int permutation[NUM_CHUNKS]; //the order in which to write the chunks
+ //a bitmap of the chunks that should be written by the follower
+ //chunk 0 corresponds to bit 1, chunk 1 to bit 2, etc
+ uint64_t follower_chunks;
+} pattern_t;
+
+
+/* Returns (via begin and end) the range of a chunk. Begin is inclusive,
+ * end is exclusive */
+void get_chunk_range(const pattern_t* pat, int chunk, uint32_t* begin, uint32_t* end){
+ if (chunk == 0){
+ *begin = 0;
+ }
+ else{
+ *begin = (uint32_t)((uint64_t)FSIZE * (uint64_t)pat->partitions[chunk - 1] >> 31);
+ }
+ *end = (uint32_t)((uint64_t)FSIZE * (uint64_t)pat->partitions[chunk] >> 31);
+}
+
+
+typedef enum{
+ leader_syncs = 0,
+ follower_syncs,
+ NUM_PHASES
+} phase_t;
+
+/* The most basic, trivial IO pattern. Fully sequential, and the follower
+ * writes every other block */
+const pattern_t trivial_pattern = {
+ {0x2000000, 0x4000000, 0x6000000, 0x8000000, 0xa000000, 0xc000000, 0xe000000, 0x10000000,
+ 0x12000000, 0x14000000, 0x16000000, 0x18000000, 0x1a000000, 0x1c000000, 0x1e000000, 0x20000000,
+ 0x22000000, 0x24000000, 0x26000000, 0x28000000, 0x2a000000, 0x2c000000, 0x2e000000, 0x30000000,
+ 0x32000000, 0x34000000, 0x36000000, 0x38000000, 0x3a000000, 0x3c000000, 0x3e000000, 0x40000000,
+ 0x42000000, 0x44000000, 0x46000000, 0x48000000, 0x4a000000, 0x4c000000, 0x4e000000, 0x50000000,
+ 0x52000000, 0x54000000, 0x56000000, 0x58000000, 0x5a000000, 0x5c000000, 0x5e000000, 0x60000000,
+ 0x62000000, 0x64000000, 0x66000000, 0x68000000, 0x6a000000, 0x6c000000, 0x6e000000, 0x70000000,
+ 0x72000000, 0x74000000, 0x76000000, 0x78000000, 0x7a000000, 0x7c000000, 0x7e000000, 0x80000000},
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63},
+ 0x5555555555555555
+};
+
+//The below patterns were randomly generated
+const pattern_t pat0 = {
+ { 0x1eac000, 0x88a4000, 0xaffe000, 0xcdb7000, 0xd2d5000, 0xe16f000, 0xe499000, 0x11f71000, 0x1242d000, 0x12c07000, 0x143bc000, 0x1460a000, 0x15dd7000, 0x1700e000, 0x1be7e000, 0x1e14d000, 0x1e6ac000, 0x21097000, 0x24b74000, 0x27166000, 0x27669000, 0x30539000, 0x3218b000, 0x37591000, 0x37b60000, 0x39818000, 0x39d08000, 0x3c90e000, 0x3e54f000, 0x3fb99000, 0x42c8e000, 0x43a62000, 0x43f50000, 0x4c0c9000, 0x4c422000, 0x4c737000, 0x4d41e000, 0x4d738000, 0x4da71000, 0x4f4e8000, 0x508e3000, 0x51396000, 0x51ab5000, 0x52a02000, 0x54238000, 0x54d6a000, 0x55029000, 0x5584b000, 0x5c42c000, 0x5c4a7000, 0x5dac5000, 0x5fe4d000, 0x63f86000, 0x66ad0000, 0x67b3d000, 0x69ce5000, 0x6c179000, 0x6e79e000, 0x6f83f000, 0x71165000, 0x72bd9000, 0x7ac79000, 0x7dc94000, 0x80000000, },
+ { 57, 16, 28, 25, 10, 59, 52, 46, 30, 6, 40, 36, 39, 9, 21, 51, 33, 45, 44, 19, 2, 50, 55, 5, 58, 13, 23, 0, 12, 53, 42, 32, 31, 48, 35, 61, 49, 54, 18, 24, 8, 41, 62, 4, 47, 17, 1, 3, 34, 14, 63, 22, 15, 26, 38, 56, 27, 60, 29, 11, 7, 43, 20, 37, },
+ 0xa2f002a1a7795bbb
+};
+const pattern_t pat1 = {
+ { 0x2b5000, 0x16db000, 0x5eb5000, 0x93a0000, 0xa7cb000, 0xa9e9000, 0xd144000, 0xe7c2000, 0xeb7d000, 0x10919000, 0x10cbd000, 0x11f85000, 0x17360000, 0x1760a000, 0x18eab000, 0x1ae6b000, 0x1c5f6000, 0x1df38000, 0x21bec000, 0x239d1000, 0x26b81000, 0x2747b000, 0x27a03000, 0x2b3cc000, 0x2cbf9000, 0x2ec0f000, 0x30a68000, 0x30bea000, 0x30c64000, 0x311af000, 0x35823000, 0x35d23000, 0x3b20e000, 0x405d8000, 0x414c8000, 0x43a91000, 0x44049000, 0x4ab4e000, 0x4ae21000, 0x4d293000, 0x511e5000, 0x516fc000, 0x52d77000, 0x57229000, 0x5da57000, 0x5dbe6000, 0x6070e000, 0x60fc0000, 0x64b24000, 0x67636000, 0x67658000, 0x6b040000, 0x6b28f000, 0x6e551000, 0x707c0000, 0x71b5c000, 0x72062000, 0x762a1000, 0x788a0000, 0x7a1e1000, 0x7b06a000, 0x7e04c000, 0x7f4cf000, 0x80000000, },
+ { 45, 8, 55, 9, 21, 54, 41, 7, 6, 22, 31, 47, 23, 11, 48, 53, 0, 61, 63, 50, 17, 27, 12, 19, 10, 40, 14, 51, 39, 59, 2, 43, 18, 42, 52, 28, 16, 44, 3, 5, 15, 35, 58, 33, 57, 49, 34, 30, 46, 4, 37, 60, 32, 36, 25, 56, 24, 13, 20, 38, 29, 26, 62, 1, },
+ 0xc88ca673fa160cbe
+};
+const pattern_t pat2 = {
+ { 0x912d000, 0xe610000, 0xf755000, 0x116df000, 0x128e5000, 0x1bd51000, 0x24e9a000, 0x27643000, 0x28cf4000, 0x292c9000, 0x2c907000, 0x2d389000, 0x2d941000, 0x2eb3f000, 0x30e94000, 0x31738000, 0x3343b000, 0x342ce000, 0x34d12000, 0x3536d000, 0x35e1a000, 0x35e4d000, 0x35fd5000, 0x3642b000, 0x3924d000, 0x392a5000, 0x3e531000, 0x3f0ee000, 0x3fdf8000, 0x41593000, 0x41c80000, 0x43959000, 0x43bc0000, 0x461c8000, 0x48922000, 0x49519000, 0x4f6fa000, 0x50274000, 0x508ae000, 0x536ed000, 0x54154000, 0x59894000, 0x5a666000, 0x5b0a6000, 0x5b9ff000, 0x5c109000, 0x5d8d0000, 0x5ddc5000, 0x5fcc5000, 0x63366000, 0x63adc000, 0x645b6000, 0x670eb000, 0x6b1b1000, 0x6c996000, 0x6ed2a000, 0x6ee4f000, 0x71fcd000, 0x734a3000, 0x76bdf000, 0x77b3f000, 0x7c65a000, 0x7d200000, 0x80000000, },
+ { 31, 35, 36, 52, 27, 56, 40, 13, 51, 49, 43, 37, 62, 42, 24, 29, 48, 25, 7, 61, 22, 57, 11, 32, 2, 54, 41, 6, 55, 15, 20, 26, 63, 44, 12, 4, 19, 58, 60, 59, 47, 23, 30, 50, 53, 34, 9, 38, 45, 8, 28, 3, 16, 33, 5, 21, 1, 10, 46, 18, 0, 14, 39, 17, },
+ 0x51ac7692bd1a78d2
+};
+const pattern_t pat3 = {
+ { 0x553000, 0x19de000, 0x6a20000, 0x8a53000, 0x8ef9000, 0xc325000, 0x1132e000, 0x139fa000, 0x1426b000, 0x150ff000, 0x1bbc1000, 0x1e84c000, 0x1f43e000, 0x1f731000, 0x21ec8000, 0x231f4000, 0x23440000, 0x23466000, 0x260b6000, 0x286a7000, 0x29518000, 0x29e35000, 0x2fdb7000, 0x3089d000, 0x362e0000, 0x3c1f9000, 0x3df2d000, 0x3fce6000, 0x402f3000, 0x4117f000, 0x41e06000, 0x4374f000, 0x451e5000, 0x45a59000, 0x4956b000, 0x4960f000, 0x4a934000, 0x4bc6f000, 0x4d462000, 0x4eef8000, 0x4f609000, 0x50dc1000, 0x51022000, 0x54396000, 0x5641b000, 0x578f1000, 0x589cf000, 0x59093000, 0x5da6b000, 0x5fbf0000, 0x605a2000, 0x65428000, 0x65530000, 0x6705a000, 0x6db65000, 0x71cef000, 0x725a2000, 0x73bf5000, 0x75acb000, 0x76065000, 0x7614c000, 0x77aab000, 0x78f70000, 0x80000000, },
+ { 15, 30, 31, 16, 49, 13, 55, 59, 4, 24, 26, 44, 17, 0, 18, 54, 10, 3, 46, 34, 29, 22, 45, 5, 38, 32, 39, 50, 48, 53, 12, 25, 35, 56, 51, 52, 1, 33, 43, 63, 47, 37, 23, 20, 60, 14, 11, 21, 8, 57, 27, 41, 6, 58, 62, 2, 19, 61, 28, 36, 40, 7, 9, 42, },
+ 0x7d291499a7ecc986
+};
+const pattern_t pat4 = {
+ { 0x425000, 0x8e8000, 0x4b90000, 0x883c000, 0x968e000, 0xbacc000, 0x10e59000, 0x125a1000, 0x12f00000, 0x14e7c000, 0x156de000, 0x192a1000, 0x1a2b9000, 0x1b4a0000, 0x1be9c000, 0x1d3bd000, 0x24242000, 0x2516b000, 0x2b88d000, 0x2b96a000, 0x2bcd3000, 0x2c5a9000, 0x2da74000, 0x2dba1000, 0x3097f000, 0x332ef000, 0x34525000, 0x36193000, 0x3725c000, 0x37e66000, 0x3d315000, 0x3e813000, 0x404ae000, 0x40c68000, 0x42f93000, 0x44b14000, 0x44b15000, 0x473b2000, 0x49048000, 0x4c794000, 0x50b60000, 0x52b3d000, 0x58c61000, 0x5b7d4000, 0x5ce71000, 0x5d21d000, 0x5d63e000, 0x5e00f000, 0x60e8b000, 0x66381000, 0x66c70000, 0x68430000, 0x707c2000, 0x71979000, 0x72681000, 0x74017000, 0x7721d000, 0x7a1be000, 0x7a2cd000, 0x7b225000, 0x7c311000, 0x7e03a000, 0x7e402000, 0x80000000, },
+ { 52, 62, 28, 47, 51, 37, 31, 36, 4, 58, 26, 29, 16, 59, 57, 33, 22, 27, 49, 44, 19, 56, 34, 23, 5, 14, 45, 48, 21, 25, 18, 12, 43, 53, 60, 17, 46, 15, 63, 30, 42, 38, 41, 8, 39, 20, 1, 10, 54, 40, 32, 24, 9, 2, 35, 3, 7, 0, 61, 11, 13, 55, 6, 50, },
+ 0xd0e57ce640cdc726
+};
+const pattern_t pat5 = {
+ { 0xae7000, 0x436e000, 0x81e1000, 0xb276000, 0xf8bf000, 0xfb26000, 0xfe7e000, 0x137ad000, 0x14b8e000, 0x157aa000, 0x1981a000, 0x1a32f000, 0x1bc9e000, 0x1def5000, 0x1e8ef000, 0x2068f000, 0x22692000, 0x22a6c000, 0x255bf000, 0x26977000, 0x27619000, 0x2977c000, 0x2ce0c000, 0x2e1ec000, 0x2e26c000, 0x31ce8000, 0x34e6c000, 0x365cd000, 0x37e87000, 0x385e3000, 0x3a7e2000, 0x3a9c7000, 0x41597000, 0x42e8a000, 0x453cc000, 0x454bf000, 0x4b24c000, 0x4ba54000, 0x4e307000, 0x4f059000, 0x55d5a000, 0x56277000, 0x56b90000, 0x5882d000, 0x5a2c5000, 0x5b369000, 0x5d442000, 0x5d671000, 0x5fdd0000, 0x60ce0000, 0x63713000, 0x64130000, 0x65973000, 0x67ad9000, 0x68764000, 0x68bb2000, 0x690d1000, 0x6a2c8000, 0x73e9f000, 0x74e75000, 0x77861000, 0x77ee5000, 0x7cddb000, 0x80000000, },
+ { 42, 25, 15, 58, 32, 61, 30, 56, 48, 62, 38, 50, 7, 45, 16, 29, 12, 4, 41, 3, 27, 18, 57, 10, 51, 17, 21, 14, 35, 19, 44, 47, 49, 26, 59, 63, 28, 55, 20, 13, 5, 6, 37, 54, 40, 22, 23, 46, 11, 36, 34, 31, 2, 60, 9, 52, 24, 1, 53, 0, 39, 43, 8, 33, },
+ 0xc8b474decf8e9c40
+};
+const pattern_t pat6 = {
+ { 0xad2000, 0x222f000, 0x64b4000, 0x6c66000, 0x6f35000, 0x9e50000, 0xe744000, 0xf129000, 0x101bb000, 0x11bf8000, 0x14b89000, 0x1691c000, 0x17a0d000, 0x1817a000, 0x1997a000, 0x1d736000, 0x1db33000, 0x1fdd8000, 0x21e56000, 0x2266c000, 0x22875000, 0x22b84000, 0x230ed000, 0x239c5000, 0x24e1a000, 0x275f5000, 0x29036000, 0x29f69000, 0x2e538000, 0x2efca000, 0x2f0bc000, 0x2f1bf000, 0x305cb000, 0x31ce7000, 0x345c4000, 0x35d4f000, 0x36e56000, 0x3ae9e000, 0x3cc27000, 0x40117000, 0x4299f000, 0x434c3000, 0x443d4000, 0x4552d000, 0x4a8a8000, 0x4cdea000, 0x51bd5000, 0x580c4000, 0x58381000, 0x59dc0000, 0x5ba7f000, 0x5d88b000, 0x5e0c4000, 0x5ee57000, 0x61f3f000, 0x63a4a000, 0x68a8a000, 0x68ec5000, 0x6937b000, 0x720be000, 0x72cf5000, 0x74fc8000, 0x76464000, 0x80000000, },
+ { 31, 46, 36, 22, 63, 12, 51, 60, 13, 44, 41, 6, 11, 17, 42, 24, 16, 61, 20, 26, 35, 21, 29, 55, 50, 45, 62, 19, 54, 9, 30, 34, 53, 52, 10, 39, 0, 49, 48, 38, 40, 28, 23, 56, 2, 5, 4, 59, 14, 57, 3, 25, 43, 32, 27, 47, 8, 7, 37, 33, 1, 18, 58, 15, },
+ 0xe5172947f6d4e10a
+};
+const pattern_t pat7 = {
+ { 0xd83000, 0x1597000, 0x245b000, 0x6a75000, 0x8fda000, 0x960e000, 0xd310000, 0xe6cd000, 0x1409a000, 0x15221000, 0x16059000, 0x1b3a4000, 0x1ceea000, 0x1ed1a000, 0x1ef0f000, 0x21723000, 0x21efc000, 0x24594000, 0x26d7f000, 0x28c4f000, 0x2fa89000, 0x304f0000, 0x30dbb000, 0x30de3000, 0x3365d000, 0x36dbc000, 0x3acb2000, 0x3e291000, 0x3f7da000, 0x41352000, 0x41a0f000, 0x435c8000, 0x4475a000, 0x47536000, 0x47726000, 0x4a81f000, 0x4be4e000, 0x4bf05000, 0x4c15b000, 0x515b4000, 0x52ef5000, 0x548cc000, 0x5692a000, 0x59ef2000, 0x5b97c000, 0x5c4f0000, 0x5d1b9000, 0x618ed000, 0x61bcc000, 0x61e07000, 0x639a3000, 0x65302000, 0x68041000, 0x6be56000, 0x721a3000, 0x72c99000, 0x740b9000, 0x7586d000, 0x75eca000, 0x76406000, 0x7b68a000, 0x7dd26000, 0x7ed55000, 0x80000000, },
+ { 44, 57, 22, 35, 63, 11, 15, 49, 61, 40, 29, 20, 19, 42, 32, 12, 41, 6, 46, 60, 52, 5, 36, 10, 2, 8, 3, 33, 54, 39, 58, 48, 62, 7, 51, 34, 0, 1, 18, 9, 55, 31, 23, 38, 25, 21, 17, 24, 13, 50, 16, 14, 43, 53, 45, 28, 59, 37, 26, 30, 47, 27, 56, 4, },
+ 0xc509d471a7ae3363
+};
+const pattern_t pat8 = {
+ { 0x1b8000, 0x27eb000, 0x5a4d000, 0x6ecc000, 0xb52e000, 0xb70e000, 0xc6db000, 0xd83d000, 0xed51000, 0x13c59000, 0x13fef000, 0x142e1000, 0x192d0000, 0x1aa63000, 0x1e230000, 0x1f464000, 0x20de4000, 0x2234b000, 0x25459000, 0x27018000, 0x28263000, 0x29cc7000, 0x32227000, 0x32c63000, 0x34af0000, 0x37e27000, 0x3afc9000, 0x3c166000, 0x3df20000, 0x405bd000, 0x41273000, 0x45c39000, 0x471be000, 0x4758e000, 0x4b3fc000, 0x4c6b2000, 0x4c80f000, 0x4ccd6000, 0x4d9e0000, 0x4e07f000, 0x4eeda000, 0x541ae000, 0x58aa7000, 0x5a2c6000, 0x5a628000, 0x5ab94000, 0x5bddc000, 0x5d1d4000, 0x5e643000, 0x5f72f000, 0x64771000, 0x67bd4000, 0x6a28c000, 0x6c977000, 0x6cc4e000, 0x710c4000, 0x74b86000, 0x75cf7000, 0x77d4b000, 0x7870e000, 0x7c47c000, 0x7eb52000, 0x7fbea000, 0x80000000, },
+ { 7, 29, 62, 8, 54, 38, 35, 45, 60, 55, 1, 40, 4, 19, 50, 63, 48, 51, 13, 27, 33, 39, 52, 46, 10, 9, 56, 2, 42, 43, 47, 44, 17, 5, 25, 6, 57, 23, 15, 58, 59, 22, 14, 26, 32, 61, 30, 0, 11, 12, 36, 24, 53, 49, 3, 20, 31, 28, 34, 18, 41, 21, 16, 37, },
+ 0x8b938ea7b599224d
+};
+const pattern_t pat9 = {
+ { 0x5b59000, 0xa6d7000, 0xbad3000, 0xdf91000, 0x115ad000, 0x13fde000, 0x17618000, 0x1b8e9000, 0x1e1b7000, 0x1e97d000, 0x21737000, 0x21a5e000, 0x24140000, 0x2558f000, 0x2647a000, 0x28257000, 0x285f6000, 0x2cb7a000, 0x2ebb1000, 0x30ae8000, 0x31543000, 0x315cb000, 0x31616000, 0x335ba000, 0x33ed6000, 0x35cf3000, 0x4162b000, 0x4409b000, 0x4629a000, 0x4b745000, 0x4c0ba000, 0x4cbc5000, 0x4dd97000, 0x4f34b000, 0x4f637000, 0x539d6000, 0x53f3d000, 0x56383000, 0x5642b000, 0x5a71f000, 0x5affa000, 0x5b486000, 0x5b8ef000, 0x60d88000, 0x61629000, 0x625cd000, 0x63326000, 0x6735e000, 0x67379000, 0x6a26a000, 0x6a281000, 0x6b997000, 0x6c50d000, 0x6cc6c000, 0x6f496000, 0x717ad000, 0x732ec000, 0x744dc000, 0x771e8000, 0x77cf0000, 0x79cad000, 0x7bb21000, 0x7e7b4000, 0x80000000, },
+ { 35, 9, 46, 6, 29, 2, 3, 54, 55, 57, 41, 16, 44, 5, 0, 59, 10, 61, 22, 42, 47, 12, 14, 50, 39, 34, 21, 32, 25, 15, 26, 8, 38, 60, 28, 53, 62, 49, 58, 43, 36, 37, 52, 7, 19, 63, 17, 11, 45, 33, 23, 27, 24, 18, 48, 56, 31, 13, 51, 30, 4, 20, 40, 1, },
+ 0xfc4c41bb3d34a11d
+};
+const pattern_t pat10 = {
+ { 0xa72000, 0x180a000, 0x6406000, 0x66df000, 0x83bb000, 0xa96f000, 0xd193000, 0x13b9b000, 0x13dae000, 0x16109000, 0x1853d000, 0x18887000, 0x19f0a000, 0x22151000, 0x229ba000, 0x26b58000, 0x2aaf4000, 0x2bf50000, 0x31a2e000, 0x31d4e000, 0x32196000, 0x3513a000, 0x36a2d000, 0x3746b000, 0x389ad000, 0x39d27000, 0x3dad3000, 0x3de55000, 0x3ea9b000, 0x3ec06000, 0x3f921000, 0x432d3000, 0x43bec000, 0x43dda000, 0x47b2b000, 0x4886e000, 0x4928e000, 0x49ad2000, 0x4d0df000, 0x4f40d000, 0x50959000, 0x54fa4000, 0x56091000, 0x5688d000, 0x5b7d8000, 0x5f6fd000, 0x601e4000, 0x64eaa000, 0x6752e000, 0x67fff000, 0x6a184000, 0x6ad7a000, 0x6adbc000, 0x6c434000, 0x6f451000, 0x6ffb4000, 0x707ee000, 0x71161000, 0x7146b000, 0x75dbf000, 0x77259000, 0x7acd4000, 0x7af71000, 0x80000000, },
+ { 61, 38, 5, 23, 62, 11, 53, 9, 17, 45, 30, 29, 41, 60, 39, 21, 40, 19, 44, 33, 42, 50, 56, 28, 32, 46, 43, 20, 16, 3, 54, 8, 4, 26, 15, 34, 47, 12, 6, 27, 48, 0, 1, 2, 57, 59, 7, 58, 49, 35, 24, 37, 52, 63, 10, 55, 36, 13, 14, 25, 18, 22, 31, 51, },
+ 0xbaba0d4f32308c7d
+};
+const pattern_t pat11 = {
+ { 0x996000, 0xaff000, 0x199a000, 0x46f3000, 0x74c0000, 0x758d000, 0xcd09000, 0xe48c000, 0xe8de000, 0xf111000, 0xf87b000, 0x10b1c000, 0x15d63000, 0x17b21000, 0x182d3000, 0x19167000, 0x198ce000, 0x1bd47000, 0x1dff1000, 0x1edc0000, 0x1f890000, 0x20860000, 0x23207000, 0x29bd5000, 0x2ac0f000, 0x2e395000, 0x2e707000, 0x329de000, 0x3497f000, 0x3807f000, 0x38a94000, 0x40a19000, 0x4168e000, 0x42ca0000, 0x42de9000, 0x45194000, 0x464f2000, 0x4700f000, 0x47dbb000, 0x4dae7000, 0x50660000, 0x535a8000, 0x5546b000, 0x57b55000, 0x5860a000, 0x5a9ee000, 0x5b8d9000, 0x5c49f000, 0x5cb4d000, 0x5d28c000, 0x60dcd000, 0x62557000, 0x64b0c000, 0x654cb000, 0x65746000, 0x65e29000, 0x6648f000, 0x66c56000, 0x6999e000, 0x6a11c000, 0x6ca04000, 0x79e60000, 0x7edce000, 0x80000000, },
+ { 16, 28, 2, 38, 10, 57, 21, 26, 61, 43, 46, 31, 56, 7, 47, 48, 58, 25, 63, 4, 59, 15, 32, 50, 1, 40, 53, 18, 17, 24, 29, 30, 55, 36, 49, 42, 41, 37, 23, 39, 6, 51, 33, 9, 45, 5, 35, 19, 44, 11, 34, 0, 27, 12, 60, 62, 20, 13, 22, 8, 14, 54, 3, 52, },
+ 0x6458c1af941ce1f7
+};
+const pattern_t pat12 = {
+ { 0x513000, 0x1b72000, 0x1e27000, 0x3a63000, 0x1115c000, 0x158b4000, 0x1664f000, 0x1b667000, 0x1f838000, 0x21410000, 0x260c7000, 0x2cd8f000, 0x2ce37000, 0x2df16000, 0x2e59e000, 0x2e8eb000, 0x2ebd2000, 0x2f1d2000, 0x2fc42000, 0x30d00000, 0x31ef1000, 0x3301a000, 0x38097000, 0x38a1e000, 0x3d818000, 0x3e898000, 0x3f90f000, 0x47710000, 0x478bb000, 0x485ab000, 0x48e54000, 0x4cfe1000, 0x53a89000, 0x53d10000, 0x56308000, 0x56f3b000, 0x577f6000, 0x58734000, 0x5889b000, 0x58ad7000, 0x5923a000, 0x59aef000, 0x5dad3000, 0x5e32f000, 0x63b3a000, 0x665c9000, 0x68cde000, 0x69252000, 0x6a777000, 0x6a79a000, 0x6c1f0000, 0x6cb9a000, 0x6d319000, 0x6dc82000, 0x6dd4d000, 0x6e188000, 0x7184d000, 0x7206e000, 0x73980000, 0x740ad000, 0x75473000, 0x7614d000, 0x79b17000, 0x80000000, },
+ { 4, 60, 10, 35, 6, 31, 5, 18, 53, 17, 20, 8, 56, 29, 7, 48, 40, 0, 12, 39, 2, 43, 15, 61, 42, 30, 50, 14, 49, 38, 34, 58, 24, 55, 33, 63, 28, 51, 59, 46, 11, 22, 45, 41, 13, 44, 23, 47, 3, 32, 16, 54, 26, 19, 25, 52, 27, 57, 36, 9, 37, 21, 62, 1, },
+ 0x1a7a56b619472b96
+};
+const pattern_t pat13 = {
+ { 0x351e000, 0x5917000, 0xa992000, 0xc471000, 0xc69c000, 0xc6ed000, 0xc919000, 0xd713000, 0xec14000, 0xfa31000, 0x17567000, 0x1d81a000, 0x1f3c8000, 0x215b5000, 0x26e41000, 0x2a2d4000, 0x2b750000, 0x2bea0000, 0x2c5ae000, 0x2ca2c000, 0x30a94000, 0x31074000, 0x314d3000, 0x31b1e000, 0x31de2000, 0x32062000, 0x33da5000, 0x37838000, 0x385ec000, 0x38740000, 0x387f9000, 0x38be6000, 0x3d2f7000, 0x3eaf5000, 0x40266000, 0x402f3000, 0x40fda000, 0x4a4bd000, 0x4b831000, 0x4bfc9000, 0x4ccaa000, 0x4ea43000, 0x50190000, 0x547c8000, 0x58cc6000, 0x58ea5000, 0x59de1000, 0x5c7f1000, 0x5f713000, 0x63f9a000, 0x6686d000, 0x675c1000, 0x6cccc000, 0x6e409000, 0x6fb6d000, 0x71a70000, 0x72f60000, 0x77bd9000, 0x79013000, 0x7a8d3000, 0x7b341000, 0x7d8f7000, 0x7fe43000, 0x80000000, },
+ { 10, 52, 48, 8, 34, 4, 35, 19, 3, 17, 54, 45, 31, 38, 24, 44, 21, 36, 22, 11, 43, 40, 39, 26, 5, 30, 2, 7, 57, 12, 20, 32, 62, 15, 55, 14, 25, 58, 6, 33, 49, 9, 59, 27, 13, 63, 42, 61, 1, 51, 0, 50, 37, 47, 16, 18, 41, 56, 60, 46, 23, 28, 53, 29, },
+ 0x8eb1b741cb9906f4
+};
+const pattern_t pat14 = {
+ { 0xf8e000, 0x169a000, 0x3816000, 0x67a9000, 0x89f3000, 0xac97000, 0xc8da000, 0xf077000, 0x119f1000, 0x13902000, 0x19785000, 0x1ca7f000, 0x1f958000, 0x2027d000, 0x2251f000, 0x24661000, 0x25604000, 0x2b924000, 0x2be5f000, 0x2ec27000, 0x330a5000, 0x3349e000, 0x33a84000, 0x344fa000, 0x34514000, 0x37966000, 0x37f0b000, 0x37fcd000, 0x386d0000, 0x39600000, 0x39de4000, 0x3e601000, 0x3e7f1000, 0x42c61000, 0x48806000, 0x4d3d0000, 0x4f5ff000, 0x512c1000, 0x53fd4000, 0x59440000, 0x5b386000, 0x5e8a0000, 0x5fca3000, 0x6016c000, 0x61ca8000, 0x64915000, 0x66b99000, 0x67226000, 0x69b2f000, 0x6a473000, 0x6a590000, 0x6c844000, 0x6cb8c000, 0x713b5000, 0x7558a000, 0x75eab000, 0x76d15000, 0x77efe000, 0x78762000, 0x7bddc000, 0x7ce92000, 0x7dc44000, 0x7f54b000, 0x80000000, },
+ { 54, 31, 48, 10, 51, 49, 55, 19, 38, 18, 44, 5, 17, 20, 16, 11, 9, 3, 42, 59, 63, 45, 25, 60, 57, 21, 40, 29, 0, 39, 26, 7, 53, 12, 13, 2, 58, 41, 22, 8, 14, 28, 46, 24, 27, 6, 52, 32, 56, 4, 30, 36, 15, 47, 23, 37, 43, 35, 50, 33, 61, 34, 1, 62, },
+ 0xa13fa3d92bcc578
+};
+const pattern_t pat15 = {
+ { 0xe16000, 0xec6000, 0xf6b000, 0x634b000, 0x6896000, 0x91db000, 0xc2c8000, 0xe083000, 0xfd7f000, 0x10479000, 0x17740000, 0x18292000, 0x1aaca000, 0x1cb55000, 0x1d2be000, 0x222af000, 0x2cb03000, 0x2fabc000, 0x32034000, 0x35c0f000, 0x3c5ec000, 0x40908000, 0x4128e000, 0x44411000, 0x44bcd000, 0x4f0ac000, 0x5167b000, 0x5541c000, 0x581bc000, 0x584e5000, 0x588fb000, 0x593d3000, 0x5b25d000, 0x5dc99000, 0x60b35000, 0x60ffc000, 0x638e8000, 0x63cf8000, 0x64ee1000, 0x6523c000, 0x654a1000, 0x687bf000, 0x68bef000, 0x69826000, 0x69d90000, 0x6a622000, 0x6d1b9000, 0x6d1e1000, 0x6d886000, 0x6fe4a000, 0x6feac000, 0x720b2000, 0x734dc000, 0x73530000, 0x73f1e000, 0x7479c000, 0x76e33000, 0x786dc000, 0x79b40000, 0x79e49000, 0x7b66c000, 0x7b904000, 0x7c906000, 0x80000000, },
+ { 6, 17, 62, 20, 61, 32, 22, 14, 28, 18, 3, 42, 63, 43, 46, 34, 29, 30, 35, 1, 37, 40, 10, 26, 5, 31, 15, 54, 8, 33, 9, 4, 39, 53, 23, 25, 41, 59, 12, 13, 60, 2, 7, 56, 58, 27, 11, 38, 36, 45, 47, 0, 57, 50, 48, 16, 51, 49, 55, 52, 44, 24, 19, 21, },
+ 0xef2ebd111c482f52
+};
+const pattern_t pat16 = {
+ { 0x596a000, 0x8644000, 0xa943000, 0xd59e000, 0x1062f000, 0x1082a000, 0x10c1b000, 0x10f9e000, 0x11e64000, 0x12e73000, 0x15ce7000, 0x16037000, 0x16d2e000, 0x17035000, 0x185ad000, 0x18d9b000, 0x19ac7000, 0x1b2fa000, 0x1cd6c000, 0x1d5f0000, 0x1f72c000, 0x20891000, 0x24bfa000, 0x25c1d000, 0x28e24000, 0x2a5f8000, 0x2e0ae000, 0x2fddf000, 0x3119d000, 0x332ee000, 0x3480a000, 0x34ea5000, 0x3534e000, 0x3538b000, 0x362e2000, 0x38f58000, 0x39ab0000, 0x3a519000, 0x3a62b000, 0x3b006000, 0x3d523000, 0x3e0f7000, 0x42366000, 0x42feb000, 0x44013000, 0x46b98000, 0x49794000, 0x4dce7000, 0x4f1f3000, 0x57ecd000, 0x5aaa2000, 0x5f419000, 0x61517000, 0x6797d000, 0x69a20000, 0x6a070000, 0x70575000, 0x75322000, 0x75a9e000, 0x79043000, 0x79875000, 0x7addc000, 0x7de88000, 0x80000000, },
+ { 26, 25, 6, 50, 32, 53, 34, 27, 3, 16, 49, 28, 46, 38, 56, 4, 18, 24, 51, 36, 63, 5, 48, 13, 43, 55, 0, 62, 35, 7, 41, 21, 44, 60, 31, 39, 14, 8, 61, 58, 52, 23, 59, 33, 10, 37, 20, 30, 40, 22, 11, 54, 57, 1, 29, 47, 2, 17, 19, 45, 15, 9, 12, 42, },
+ 0xb19913245ddeb436
+};
+const pattern_t pat17 = {
+ { 0x28ab000, 0x3ac8000, 0x3fe1000, 0x63a7000, 0x90fc000, 0xb3f2000, 0xd2f2000, 0xe032000, 0x12d4c000, 0x13135000, 0x14652000, 0x15331000, 0x1570c000, 0x1688e000, 0x16bc3000, 0x1cbe3000, 0x1fe0f000, 0x2517f000, 0x26c6b000, 0x2a284000, 0x2a4e2000, 0x2add5000, 0x2bd06000, 0x2ca3a000, 0x2eb11000, 0x324d1000, 0x35662000, 0x38695000, 0x38ce7000, 0x391ac000, 0x398f9000, 0x39949000, 0x401f3000, 0x457f0000, 0x45c6d000, 0x4b561000, 0x522fc000, 0x54ef0000, 0x559f8000, 0x562a7000, 0x56a04000, 0x57b68000, 0x59702000, 0x5ffc9000, 0x63a76000, 0x63c37000, 0x65d3e000, 0x67130000, 0x6a03a000, 0x6bcd6000, 0x6be96000, 0x6bf52000, 0x6fcd9000, 0x7038c000, 0x70a47000, 0x72881000, 0x72ed0000, 0x75035000, 0x75c11000, 0x77fa5000, 0x797c9000, 0x79813000, 0x7bbbb000, 0x80000000, },
+ { 18, 15, 56, 5, 25, 47, 39, 55, 12, 14, 51, 33, 0, 7, 9, 44, 50, 31, 62, 59, 3, 35, 23, 17, 30, 60, 11, 24, 40, 20, 52, 2, 22, 8, 57, 42, 32, 54, 36, 48, 49, 13, 58, 10, 28, 63, 16, 41, 27, 21, 37, 4, 1, 29, 19, 6, 53, 45, 46, 38, 34, 43, 61, 26, },
+ 0x710633dc35c17b71
+};
+const pattern_t pat18 = {
+ { 0x38d6000, 0x5379000, 0x5cae000, 0x5d20000, 0xa248000, 0xb4d0000, 0xd7c0000, 0xf731000, 0x116ae000, 0x151d2000, 0x1747d000, 0x1bfb6000, 0x1d758000, 0x2053d000, 0x24dda000, 0x25274000, 0x269c0000, 0x273e8000, 0x2a5d0000, 0x2ad34000, 0x3016b000, 0x30d1a000, 0x32960000, 0x34b3b000, 0x36e4f000, 0x37934000, 0x38c42000, 0x3c2d2000, 0x3d23d000, 0x3d89a000, 0x3dc85000, 0x3e9a7000, 0x3f25b000, 0x45bd1000, 0x48d94000, 0x4b126000, 0x4e17c000, 0x4f377000, 0x50908000, 0x51957000, 0x53410000, 0x5412c000, 0x55256000, 0x56b17000, 0x5707b000, 0x5bbe5000, 0x5d067000, 0x5e1c1000, 0x6380b000, 0x66009000, 0x68240000, 0x69fc4000, 0x6c327000, 0x6c5d2000, 0x6f69d000, 0x739c7000, 0x744bc000, 0x74cd8000, 0x787b8000, 0x78c61000, 0x7969d000, 0x79aae000, 0x7b032000, 0x80000000, },
+ { 16, 48, 50, 60, 13, 39, 20, 4, 63, 18, 14, 30, 55, 8, 62, 37, 43, 41, 11, 0, 36, 33, 34, 49, 17, 58, 38, 22, 19, 5, 21, 12, 47, 25, 57, 61, 7, 3, 10, 23, 52, 24, 6, 53, 2, 26, 1, 31, 28, 46, 42, 9, 45, 29, 27, 54, 32, 56, 51, 44, 35, 59, 40, 15, },
+ 0xb53f1e4266db260c
+};
+const pattern_t pat19 = {
+ { 0x297d000, 0x34e0000, 0x7801000, 0x9664000, 0x96fa000, 0xbb9f000, 0xc192000, 0xc4a5000, 0xca74000, 0xcce8000, 0x173d8000, 0x1a8d8000, 0x1b299000, 0x1b52d000, 0x1e813000, 0x2185e000, 0x21abe000, 0x2b9a4000, 0x2c4b6000, 0x2fa27000, 0x343ba000, 0x356fd000, 0x37c6e000, 0x38365000, 0x3a9e4000, 0x3b599000, 0x4296b000, 0x43196000, 0x4381e000, 0x44783000, 0x47a75000, 0x4bd78000, 0x4d05d000, 0x4edb2000, 0x4eefd000, 0x4fecc000, 0x51f68000, 0x5252b000, 0x5439e000, 0x55fb3000, 0x5814f000, 0x5939d000, 0x60a78000, 0x62a86000, 0x633b0000, 0x64a68000, 0x64b62000, 0x66207000, 0x66540000, 0x67f90000, 0x68bf3000, 0x6a069000, 0x6d2ac000, 0x70c9f000, 0x71bab000, 0x724bc000, 0x783d8000, 0x7900e000, 0x79399000, 0x79763000, 0x7c8a8000, 0x7e680000, 0x7f6de000, 0x80000000, },
+ { 14, 59, 60, 24, 18, 22, 62, 12, 45, 2, 32, 11, 25, 37, 13, 7, 50, 39, 56, 17, 47, 40, 29, 43, 15, 34, 4, 57, 31, 38, 21, 28, 36, 27, 42, 1, 23, 33, 5, 61, 44, 55, 8, 30, 10, 41, 19, 48, 16, 52, 49, 46, 54, 58, 6, 0, 51, 3, 26, 20, 53, 9, 35, 63, },
+ 0x64f54c0a0deae9ab
+};
+const pattern_t pat20 = {
+ { 0x8f7000, 0xa01000, 0x38e3000, 0x5299000, 0x6875000, 0x7f3e000, 0x827f000, 0x9413000, 0xca71000, 0xfb11000, 0x10beb000, 0x176be000, 0x1924f000, 0x1cfd6000, 0x1d20d000, 0x1ebb7000, 0x22c93000, 0x23601000, 0x2cf7a000, 0x2d3af000, 0x2e391000, 0x2f294000, 0x318ad000, 0x34ddd000, 0x365e4000, 0x3b8d2000, 0x3c0f8000, 0x3d2fd000, 0x3e431000, 0x3f0fe000, 0x4074e000, 0x40d1c000, 0x41936000, 0x4347b000, 0x452d7000, 0x486d3000, 0x4b47e000, 0x4b709000, 0x4c349000, 0x4ff13000, 0x50faa000, 0x51a07000, 0x52f30000, 0x55f29000, 0x57ad4000, 0x5909f000, 0x5a0f6000, 0x5de57000, 0x60d2f000, 0x625ff000, 0x6288e000, 0x65077000, 0x6a707000, 0x6a73f000, 0x6d720000, 0x6e390000, 0x6edc9000, 0x6ee78000, 0x77ac2000, 0x77ad3000, 0x7868f000, 0x79a1b000, 0x7aec6000, 0x80000000, },
+ { 21, 56, 38, 11, 62, 9, 30, 47, 34, 23, 37, 16, 5, 49, 10, 43, 4, 45, 36, 7, 42, 1, 53, 57, 20, 59, 55, 50, 46, 39, 60, 27, 12, 31, 48, 25, 15, 22, 44, 52, 14, 33, 0, 29, 17, 18, 2, 32, 24, 19, 6, 41, 54, 8, 35, 26, 61, 3, 51, 13, 63, 28, 40, 58, },
+ 0x786427ef60db40f4
+};
+const pattern_t pat21 = {
+ { 0x2a8e000, 0xa4bd000, 0xa935000, 0xcc05000, 0xdfe4000, 0xe014000, 0xff46000, 0x18c8c000, 0x199ec000, 0x19ce0000, 0x1f684000, 0x1ff5c000, 0x22d58000, 0x27651000, 0x280e5000, 0x2e2a4000, 0x2e432000, 0x2f96e000, 0x2f9c3000, 0x3343d000, 0x338bb000, 0x34032000, 0x34101000, 0x368c2000, 0x37b95000, 0x39492000, 0x39932000, 0x3b611000, 0x3c89e000, 0x40aa9000, 0x42358000, 0x4890e000, 0x495c9000, 0x4a79d000, 0x4c58e000, 0x4df9a000, 0x4f304000, 0x4fa4c000, 0x54d1d000, 0x58461000, 0x58f43000, 0x5a3d1000, 0x5a765000, 0x5c5c0000, 0x60488000, 0x60fad000, 0x613e5000, 0x61d61000, 0x62d17000, 0x641ff000, 0x67f8b000, 0x69c5d000, 0x6b931000, 0x6efd4000, 0x70333000, 0x70857000, 0x721f6000, 0x72f53000, 0x74450000, 0x746f7000, 0x76067000, 0x7774a000, 0x77ea6000, 0x80000000, },
+ { 28, 51, 33, 2, 30, 55, 29, 17, 40, 48, 32, 9, 39, 1, 49, 50, 37, 43, 62, 11, 10, 26, 22, 6, 8, 7, 45, 47, 46, 42, 60, 5, 12, 56, 4, 23, 35, 25, 13, 16, 61, 54, 31, 63, 34, 19, 41, 59, 38, 24, 0, 58, 53, 44, 3, 18, 52, 20, 36, 27, 14, 21, 57, 15, },
+ 0x615a5f41d4c3d653
+};
+const pattern_t pat22 = {
+ { 0x314d000, 0x4452000, 0x6673000, 0xab09000, 0xc80d000, 0x10eda000, 0x129c2000, 0x12f1f000, 0x13e9b000, 0x1450c000, 0x15aeb000, 0x1667c000, 0x190b2000, 0x19ac5000, 0x1c0ac000, 0x1c229000, 0x1ece8000, 0x1fc48000, 0x22abd000, 0x24268000, 0x2adce000, 0x2b809000, 0x30a11000, 0x31d08000, 0x36700000, 0x39e6a000, 0x3b84b000, 0x41e84000, 0x46301000, 0x4a326000, 0x50fda000, 0x5299a000, 0x56acf000, 0x57f66000, 0x586ab000, 0x58df5000, 0x591cc000, 0x59b91000, 0x59cbd000, 0x5b4d0000, 0x5cca7000, 0x5cfce000, 0x5d120000, 0x5d51a000, 0x5eaa0000, 0x5ebac000, 0x5f0e1000, 0x5f285000, 0x5f4d3000, 0x5ff61000, 0x60b51000, 0x61435000, 0x651fd000, 0x6b954000, 0x705aa000, 0x71a41000, 0x73ec7000, 0x75f92000, 0x76854000, 0x77cb9000, 0x782ca000, 0x7a2af000, 0x7eaa6000, 0x80000000, },
+ { 16, 8, 5, 59, 4, 18, 26, 43, 33, 57, 6, 47, 56, 46, 10, 54, 52, 0, 50, 30, 39, 24, 38, 63, 28, 25, 49, 31, 55, 62, 3, 17, 23, 13, 37, 53, 34, 14, 44, 12, 19, 36, 27, 61, 51, 42, 41, 60, 45, 1, 7, 35, 21, 58, 20, 15, 2, 9, 22, 29, 48, 32, 40, 11, },
+ 0x828366dbf1f83654
+};
+const pattern_t pat23 = {
+ { 0x47000, 0x680000, 0x176a000, 0x1db8000, 0x600e000, 0x808c000, 0x9e58000, 0xa82f000, 0xaebb000, 0xc938000, 0xd0eb000, 0xdc5e000, 0xe503000, 0x11e56000, 0x12dbd000, 0x14681000, 0x15200000, 0x18256000, 0x1be75000, 0x1d2a0000, 0x1ed67000, 0x27e8a000, 0x2bc39000, 0x2bf4b000, 0x2c94f000, 0x2d575000, 0x2d82e000, 0x2e440000, 0x2ecda000, 0x2fbc2000, 0x33ab0000, 0x360b8000, 0x39630000, 0x3a654000, 0x3d2b6000, 0x3eeff000, 0x41590000, 0x417ea000, 0x42ff9000, 0x4bca4000, 0x503b0000, 0x508e4000, 0x52a2e000, 0x535b6000, 0x54335000, 0x57412000, 0x57dc6000, 0x590d9000, 0x5fc0a000, 0x60552000, 0x60665000, 0x6168d000, 0x65b16000, 0x67328000, 0x6742d000, 0x68c88000, 0x6b802000, 0x6f6d2000, 0x7040e000, 0x77e31000, 0x79c71000, 0x7da4a000, 0x7e26b000, 0x80000000, },
+ { 63, 50, 51, 33, 28, 35, 24, 14, 4, 23, 47, 11, 37, 41, 12, 55, 62, 32, 34, 30, 25, 43, 16, 0, 3, 49, 61, 15, 57, 46, 59, 44, 31, 27, 21, 53, 5, 2, 8, 56, 52, 22, 60, 40, 20, 1, 48, 18, 17, 19, 54, 29, 9, 38, 42, 6, 39, 45, 13, 10, 26, 58, 36, 7, },
+ 0xd8456d32bb91872e
+};
+const pattern_t pat24 = {
+ { 0x3890000, 0x3fd9000, 0x62d6000, 0x7df0000, 0x895c000, 0xab61000, 0xc23b000, 0x10ab3000, 0x1247c000, 0x13f00000, 0x16604000, 0x1a444000, 0x1c2c8000, 0x1c467000, 0x1d396000, 0x1e683000, 0x21080000, 0x2442a000, 0x27fea000, 0x282eb000, 0x28e2c000, 0x2a625000, 0x2b3b2000, 0x2bbd5000, 0x2c886000, 0x2cbe8000, 0x31518000, 0x35425000, 0x355f4000, 0x35d7a000, 0x3851d000, 0x396a6000, 0x3d10f000, 0x3d890000, 0x49238000, 0x4ab7e000, 0x4fadf000, 0x50603000, 0x5233a000, 0x53279000, 0x5586c000, 0x56968000, 0x58101000, 0x588b6000, 0x5bc19000, 0x5cc10000, 0x623f6000, 0x629f4000, 0x63176000, 0x63dcc000, 0x681d0000, 0x69c0e000, 0x6a9fa000, 0x6ae5e000, 0x6d2ba000, 0x6e422000, 0x73f94000, 0x77932000, 0x78b24000, 0x794c1000, 0x795d2000, 0x7ae08000, 0x7b3ce000, 0x80000000, },
+ { 56, 47, 54, 62, 29, 43, 25, 59, 41, 7, 52, 63, 15, 21, 16, 14, 39, 17, 45, 11, 27, 24, 55, 31, 53, 4, 6, 2, 20, 23, 5, 37, 32, 58, 13, 51, 1, 8, 3, 57, 46, 30, 35, 49, 18, 40, 9, 22, 42, 38, 34, 0, 19, 33, 26, 60, 10, 48, 36, 61, 44, 12, 50, 28, },
+ 0x3e9c5e14d18c9e65
+};
+const pattern_t pat25 = {
+ { 0xcb000, 0x22bf000, 0x2461000, 0x246b000, 0x5c6f000, 0x5fb4000, 0x69a9000, 0x718c000, 0x92e6000, 0xbb2e000, 0xd916000, 0xf3dc000, 0xf568000, 0x10246000, 0x12d53000, 0x14dfd000, 0x1598a000, 0x1956a000, 0x1b01b000, 0x1b3b8000, 0x1ce06000, 0x20bc5000, 0x21351000, 0x233b0000, 0x23f2b000, 0x24e41000, 0x29cca000, 0x2b5bd000, 0x2ba68000, 0x2bf7f000, 0x31a58000, 0x34570000, 0x39941000, 0x3b765000, 0x3cd13000, 0x3d251000, 0x3fa05000, 0x40745000, 0x45c68000, 0x4a282000, 0x4ad19000, 0x4b4aa000, 0x4ca18000, 0x4e0b8000, 0x4eb97000, 0x4f68b000, 0x4fca2000, 0x52466000, 0x52edf000, 0x5602a000, 0x57f60000, 0x5cc18000, 0x5cee3000, 0x5da37000, 0x5dba4000, 0x64b77000, 0x66e9f000, 0x68b9d000, 0x6aac9000, 0x6d873000, 0x6f7e6000, 0x71036000, 0x75a54000, 0x80000000, },
+ { 1, 58, 13, 5, 62, 10, 49, 48, 19, 24, 54, 57, 20, 39, 35, 41, 28, 42, 6, 44, 34, 45, 55, 3, 2, 60, 38, 36, 30, 25, 7, 23, 53, 50, 61, 29, 40, 47, 22, 12, 27, 0, 52, 31, 8, 15, 37, 11, 46, 32, 4, 56, 17, 33, 26, 43, 51, 16, 9, 59, 63, 21, 14, 18, },
+ 0x7bae8ea344ec83e
+};
+const pattern_t pat26 = {
+ { 0x1f4e000, 0x46f7000, 0x90a7000, 0xafe3000, 0xb2a8000, 0xb8b0000, 0xc480000, 0xc4e1000, 0xf03c000, 0xf075000, 0x14dbd000, 0x17728000, 0x1a146000, 0x1ab02000, 0x1af49000, 0x1be7a000, 0x1dc7a000, 0x1de5e000, 0x1f4f9000, 0x2092b000, 0x20a63000, 0x22937000, 0x22fe3000, 0x23e1c000, 0x28c46000, 0x294d6000, 0x2ad19000, 0x2b18b000, 0x2b233000, 0x2b685000, 0x2c792000, 0x2e6c2000, 0x2fa86000, 0x3320f000, 0x36f17000, 0x38406000, 0x38b1b000, 0x3a132000, 0x3b269000, 0x3cc43000, 0x3e2a2000, 0x3e3bb000, 0x3e83b000, 0x3ea14000, 0x4035d000, 0x4137f000, 0x4615b000, 0x50531000, 0x517c8000, 0x519e6000, 0x55ebc000, 0x594f8000, 0x5a732000, 0x5d2cb000, 0x5e409000, 0x5f394000, 0x5f3a8000, 0x60dc6000, 0x61373000, 0x6ebd6000, 0x6fd61000, 0x77161000, 0x7ce81000, 0x80000000, },
+ { 61, 48, 12, 55, 3, 58, 51, 56, 15, 29, 54, 11, 31, 49, 40, 37, 7, 4, 23, 35, 25, 18, 27, 43, 6, 41, 17, 45, 52, 53, 47, 16, 42, 0, 30, 13, 38, 62, 1, 8, 21, 28, 57, 9, 60, 19, 44, 50, 14, 36, 22, 2, 32, 59, 34, 10, 63, 39, 5, 24, 33, 20, 46, 26, },
+ 0x465275bfa2868e8b
+};
+const pattern_t pat27 = {
+ { 0xa6a000, 0x4c7a000, 0x5183000, 0x8dda000, 0x9cbd000, 0xb860000, 0x10c24000, 0x12dda000, 0x147ab000, 0x14aa4000, 0x16c8f000, 0x17d5b000, 0x18b5c000, 0x1a163000, 0x1b0a1000, 0x24221000, 0x25ef8000, 0x267f1000, 0x268b7000, 0x26b07000, 0x273ad000, 0x27bc2000, 0x2856c000, 0x29896000, 0x2efeb000, 0x331a7000, 0x348e8000, 0x3707f000, 0x3f444000, 0x3fe2a000, 0x433b3000, 0x435d3000, 0x46d82000, 0x4a9d3000, 0x4c6cf000, 0x4ca36000, 0x4ec42000, 0x4f79c000, 0x53cd3000, 0x58c78000, 0x5d910000, 0x616cc000, 0x62800000, 0x65ded000, 0x68831000, 0x6b321000, 0x6cd46000, 0x6d0fa000, 0x6d2f9000, 0x6e353000, 0x6fd5e000, 0x706c5000, 0x7249f000, 0x75d6c000, 0x77528000, 0x783ad000, 0x79738000, 0x79bfe000, 0x79ee9000, 0x7b74a000, 0x7bb41000, 0x7bbeb000, 0x7bbfb000, 0x80000000, },
+ { 61, 53, 12, 15, 26, 30, 32, 2, 16, 5, 39, 43, 20, 21, 49, 37, 11, 51, 18, 44, 31, 19, 24, 40, 1, 35, 50, 6, 57, 14, 46, 17, 22, 48, 29, 7, 34, 45, 10, 63, 23, 41, 54, 38, 4, 25, 42, 13, 56, 62, 36, 28, 33, 59, 55, 3, 9, 0, 58, 60, 47, 8, 52, 27, },
+ 0x5ba5aa760ec14c3e
+};
+const pattern_t pat28 = {
+ { 0x439a000, 0x6860000, 0xd252000, 0x1105c000, 0x113c8000, 0x1429a000, 0x14922000, 0x15f32000, 0x1992f000, 0x1a1db000, 0x1a87c000, 0x1b260000, 0x1b292000, 0x1c253000, 0x1ea33000, 0x20bbc000, 0x215ae000, 0x25249000, 0x27c89000, 0x27e36000, 0x28bf2000, 0x29c27000, 0x2a575000, 0x2c6fa000, 0x31639000, 0x3184a000, 0x319c3000, 0x348a7000, 0x38aa8000, 0x39dd5000, 0x3a067000, 0x3c0dd000, 0x3cfd4000, 0x3ebb6000, 0x43259000, 0x46494000, 0x46fcb000, 0x4a050000, 0x4b5c4000, 0x4cff3000, 0x4edaa000, 0x4f025000, 0x542e1000, 0x55364000, 0x56338000, 0x56ef8000, 0x5711b000, 0x573d1000, 0x5943b000, 0x5b912000, 0x61ce2000, 0x65211000, 0x65dca000, 0x6dee2000, 0x6df30000, 0x7334d000, 0x73e76000, 0x7473a000, 0x75846000, 0x75fd0000, 0x77174000, 0x773e9000, 0x7a8db000, 0x80000000, },
+ { 43, 11, 8, 56, 5, 22, 42, 55, 14, 32, 2, 47, 24, 51, 35, 25, 15, 58, 41, 27, 33, 37, 4, 36, 7, 53, 26, 48, 38, 19, 29, 28, 40, 10, 1, 46, 59, 63, 61, 62, 60, 30, 21, 39, 44, 57, 20, 18, 17, 54, 49, 52, 3, 12, 45, 13, 50, 9, 16, 23, 0, 6, 31, 34, },
+ 0x6236b5e97869acb
+};
+const pattern_t pat29 = {
+ { 0x363000, 0x9bc000, 0x1907000, 0x41d5000, 0x5a6e000, 0x9f36000, 0xa3ee000, 0x14b98000, 0x1845c000, 0x188ea000, 0x1b297000, 0x1c024000, 0x1e1eb000, 0x1f3a4000, 0x2047f000, 0x2420a000, 0x28871000, 0x296dd000, 0x2c92c000, 0x2dd42000, 0x3444f000, 0x35b90000, 0x3683c000, 0x3d8ea000, 0x3fe6b000, 0x4200e000, 0x421cf000, 0x42a46000, 0x44463000, 0x44e61000, 0x45c82000, 0x485f8000, 0x48fe8000, 0x4a532000, 0x4a6fd000, 0x4c8f9000, 0x4dbd7000, 0x5052a000, 0x512bb000, 0x5281d000, 0x5315d000, 0x5a202000, 0x5a9fc000, 0x5c11a000, 0x6010b000, 0x62aa3000, 0x63d05000, 0x6774c000, 0x6776d000, 0x68105000, 0x699d5000, 0x69bc2000, 0x6b1b9000, 0x704d5000, 0x73d5c000, 0x73d94000, 0x78483000, 0x78c8c000, 0x78cc5000, 0x7ac8d000, 0x7ae00000, 0x7b597000, 0x7e6ab000, 0x80000000, },
+ { 52, 40, 59, 29, 42, 34, 63, 44, 33, 37, 51, 23, 5, 36, 38, 43, 9, 4, 28, 55, 1, 6, 21, 26, 13, 24, 30, 15, 35, 17, 46, 20, 16, 10, 49, 48, 39, 62, 19, 14, 61, 27, 53, 2, 57, 58, 45, 7, 56, 50, 54, 25, 31, 11, 22, 47, 3, 0, 32, 12, 8, 41, 60, 18, },
+ 0xa3cfc2e527b1c191
+};
+const pattern_t pat30 = {
+ { 0x45c5000, 0x6bf3000, 0xc293000, 0xe470000, 0xe5b7000, 0x1256c000, 0x1444d000, 0x15699000, 0x16e86000, 0x1a4d5000, 0x1b803000, 0x1dcf9000, 0x1dd6f000, 0x1f57f000, 0x22879000, 0x263e9000, 0x29423000, 0x2a1a9000, 0x2a699000, 0x2c8fb000, 0x2d2e0000, 0x2ec5e000, 0x317a4000, 0x35a64000, 0x36967000, 0x37299000, 0x37c07000, 0x3b9bb000, 0x3c054000, 0x3ccbc000, 0x3d94a000, 0x3e2e9000, 0x3e7a4000, 0x40b98000, 0x44658000, 0x44738000, 0x44fe3000, 0x451d9000, 0x4536c000, 0x46df2000, 0x48855000, 0x503ce000, 0x53104000, 0x531fc000, 0x54c1b000, 0x56086000, 0x5642b000, 0x573a4000, 0x5887f000, 0x5a871000, 0x5c970000, 0x5e566000, 0x62b8f000, 0x642ce000, 0x65ee5000, 0x66db3000, 0x6727c000, 0x6a9a2000, 0x74a8f000, 0x7c29a000, 0x7cc57000, 0x7f221000, 0x7f28f000, 0x80000000, },
+ { 29, 35, 8, 49, 30, 55, 27, 38, 58, 61, 0, 28, 15, 39, 5, 37, 32, 42, 46, 54, 12, 14, 1, 31, 59, 11, 47, 9, 13, 50, 2, 62, 60, 18, 20, 51, 23, 24, 53, 6, 25, 48, 41, 3, 33, 57, 44, 19, 22, 52, 4, 45, 10, 21, 56, 36, 17, 43, 7, 63, 16, 34, 26, 40, },
+ 0xf2ae48cdd4d05e58
+};
+const pattern_t pat31 = {
+ { 0x143f000, 0x2068000, 0x328c000, 0x70a6000, 0x92a7000, 0x93dd000, 0xa3a8000, 0xbe51000, 0xbfc8000, 0xe353000, 0x1272f000, 0x143a4000, 0x16825000, 0x20bf8000, 0x20d9f000, 0x21e32000, 0x22426000, 0x2246b000, 0x22cea000, 0x25dc2000, 0x29324000, 0x29cd1000, 0x2aa44000, 0x2cd84000, 0x2dafb000, 0x2e74b000, 0x2f5b1000, 0x3a7a9000, 0x3bb38000, 0x3c11a000, 0x3c30a000, 0x3e2f1000, 0x4187b000, 0x42190000, 0x44e34000, 0x4d850000, 0x53ceb000, 0x540db000, 0x54937000, 0x5530a000, 0x5a111000, 0x5c280000, 0x5ef17000, 0x5fccf000, 0x64434000, 0x6498e000, 0x662c4000, 0x6a7e2000, 0x6b5a1000, 0x6c11f000, 0x6dd97000, 0x6ef1b000, 0x6f44e000, 0x7084f000, 0x73b53000, 0x7872c000, 0x78ed7000, 0x7935b000, 0x79bf9000, 0x7a6af000, 0x7b6fd000, 0x7bd42000, 0x7f233000, 0x80000000, },
+ { 6, 25, 28, 44, 27, 43, 58, 33, 23, 21, 16, 48, 30, 26, 5, 20, 49, 38, 2, 45, 11, 61, 17, 0, 53, 13, 7, 52, 40, 31, 36, 4, 10, 8, 24, 22, 42, 63, 35, 60, 47, 29, 46, 19, 1, 3, 34, 55, 59, 14, 39, 12, 32, 50, 62, 54, 56, 51, 57, 15, 41, 18, 37, 9, },
+ 0xc8cbea4e3899aca5
+};
+const pattern_t* patterns[] = {&trivial_pattern,
+ &pat0, &pat1, &pat2, &pat3, &pat4, &pat5, &pat6, &pat7,
+ &pat8, &pat9, &pat10, &pat11, &pat12, &pat13, &pat14, &pat15,
+ &pat16, &pat17, &pat18, &pat19, &pat20, &pat21, &pat22, &pat23,
+ &pat24, &pat25, &pat26, &pat27, &pat28, &pat29, &pat30, &pat31};
+
+//static variables used by the worker threads
+static int outfd = 0;
+static sem_t chunk_sems[NUM_CHUNKS];
+static phase_t phase;
+#if USE_MMAP
+static void* p_file;
+#endif
+
+static void
+usage()
+{
+ (void) fprintf(stderr, "usage: txg_integrity <file name>\n");
+ exit(2);
+}
+
+
+/* Fills a buffer with a special marker. The marker contains information about
+ * the file offset where this buffer is supposed to go, and whether it will
+ * be written by a leader or a follower */
+static void
+marker_fill(uint64_t* buf, int file_ofs, size_t len, int is_follower){
+ int ofs;
+ uint32_t lead_mark = 0x4441454c; //"LEAD" in little endian
+ uint32_t follow_mark = 0x4c4c4f46; //"FOLL" in little endian
+ for (ofs = file_ofs; ofs < file_ofs + len; ofs += sizeof(uint64_t)){
+ uint64_t mark = ((is_follower ?
+ (uint64_t)follow_mark : (uint64_t)lead_mark) << (uint64_t)32) |
+ htonl((ofs & 0xFFFFFFFF));
+ int buf_idx = (ofs - file_ofs) / sizeof(uint64_t);
+ buf[buf_idx] = mark;
+ }
+}
+
+static int
+verify_file(int fd, const pattern_t* p_pat){
+ int chunk_idx;
+ int good_data = 1;
+ int err = 0;
+
+ for(chunk_idx=0; chunk_idx < NUM_CHUNKS; chunk_idx++){
+ int i;
+ uint32_t chunk_start, chunk_end;
+ get_chunk_range(p_pat, chunk_idx, &chunk_start, &chunk_end);
+ size_t size = chunk_end - chunk_start;
+ uint64_t* desired_buf = malloc(size);
+ uint64_t* actual_buf = malloc(size);
+ if ((1 << chunk_idx) & p_pat->follower_chunks){
+ marker_fill(desired_buf, chunk_start, size, 1);
+ }
+ else{
+ marker_fill(desired_buf, chunk_start, size, 0);
+ }
+
+ //read the actual data from the file
+ if( read(fd, actual_buf, size) <= 0 ){
+ perror("read");
+ exit(1);
+ }
+
+ //verify the data
+ for(i=0; i < size / sizeof(uint64_t); i++){
+ int chunk_offset = sizeof(uint64_t) * i;
+ int file_offset = chunk_start + chunk_offset;
+ if (good_data && (actual_buf[i] != desired_buf[i])){
+ fprintf(stderr, "txg_integrity: miscompare at "
+ "chunk %i, chunk offset %x, file offset %x\n",
+ chunk_idx, chunk_offset, file_offset);
+ fprintf(stderr, "Expected %16lx, got %16lx\n",
+ desired_buf[i], actual_buf[i]);
+ err = 1;
+ good_data = 0;
+ }
+ else if (!good_data && (actual_buf[i] == desired_buf[i])) {
+ fprintf(stderr, "txg_integrity: miscompare ends at "
+ "chunk %i, chunk offset %x, file offset %x\n",
+ chunk_idx, chunk_offset, file_offset);
+ good_data = 1;
+ }
+ }
+ free(desired_buf);
+ free(actual_buf);
+ }
+
+ return (err);
+}
+
+/* Writes a special marker to every byte within the chunk */
+static void
+write_chunk(pattern_t* p_pat, int chunk_idx, int is_follower)
+{
+ uint32_t chunk_start, chunk_end;
+ get_chunk_range(p_pat, chunk_idx, &chunk_start, &chunk_end);
+ size_t size = chunk_end - chunk_start;
+ uint64_t* buf = malloc(size);
+ marker_fill(buf, chunk_start, size, is_follower);
+#if USE_MMAP
+ memcpy((void*)( (char*)p_file + chunk_start ), (void*)buf, size);
+#else
+ pwrite(outfd, (void*)buf, size, chunk_start);
+#endif
+ free(buf);
+}
+
+static void
+my_sync(int fd){
+ //FreeBSD's msync(2) recommends that msync is obsolete and fsync should
+ //be used instead, even for memory-mapped files
+ if (fsync(fd)){
+ perror("fsync");
+ exit(1);
+ }
+}
+
+static void*
+follower(void* data)
+{
+ int perm_idx;
+ pattern_t* p_pat = (pattern_t*)data;
+/* printf("Follower started\n");*/
+ for(perm_idx = 0; perm_idx < NUM_CHUNKS; perm_idx++)
+ {
+ int chunk_idx = p_pat->permutation[perm_idx];
+ if (perm_idx == NUM_CHUNKS / 2 && phase == follower_syncs){
+/* printf("about to sync in follower\n");*/
+ my_sync(outfd);
+ }
+
+ if ( (1 << chunk_idx) & p_pat->follower_chunks){
+/* printf("about to pend on semaphore\n");*/
+#if USE_THREADS
+ if (-1 == sem_wait(&chunk_sems[chunk_idx])){
+ perror("sem_wait");
+ exit(1);
+ }
+#endif
+/* printf("about to write chunk in follower\n");*/
+ write_chunk(p_pat, chunk_idx, 1);
+ }
+ }
+ return 0;
+}
+
+static void*
+leader(void* data)
+{
+ int perm_idx;
+ pattern_t* p_pat = (pattern_t*)data;
+/* printf("Leader started\n");*/
+ for(perm_idx = 0; perm_idx < NUM_CHUNKS; perm_idx++)
+ {
+ int chunk_idx = p_pat->permutation[perm_idx];
+ if (perm_idx == NUM_CHUNKS / 2 && phase == leader_syncs){
+ //printf("about to sync in leader\n");
+ my_sync(outfd);
+ }
+ //printf("about to write chunk in leader\n");
+ write_chunk(p_pat, chunk_idx, 0);
+ //printf("about to post semaphore\n");
+#if USE_THREADS
+ if (sem_post(&chunk_sems[chunk_idx]) == -1){
+ perror("sem_post");
+ exit(1);
+ }
+#endif
+ }
+ return 0;
+}
+
+int
+main(int argc, char** argv)
+{
+ int rep;
+ int pat;
+#if USE_THREADS
+ pthread_t leader_th, follower_th;
+#endif
+
+ if (argc != 2){
+ usage();
+ }
+
+ //follower_chunks bitmap must have enough space to represent every chunk
+ assert(NUM_CHUNKS <= 8 * sizeof(((pattern_t*) 0)->follower_chunks));
+
+ for(rep=0; rep < NUM_REPETITIONS; rep++){
+ printf("Starting repetition %d\n", rep);
+ for(pat=0; pat < sizeof(patterns) / sizeof(patterns[0]); pat++){
+ void *pat_p = (void *)(uintptr_t)(const void *)patterns[pat];
+ for(phase=leader_syncs; phase < NUM_PHASES; phase++){
+ int sem_idx;
+ int ofs=0;
+
+/* printf("Starting on patterns[%d]\n" , pat);*/
+ outfd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0644);
+ if (outfd == -1){
+ perror("open");
+ exit(1);
+ }
+
+ //set the file size
+ if ( ftruncate(outfd, FSIZE)){
+ perror("ftruncate");
+ exit(1);
+ }
+
+ //Zero-fill the file to avoid fragmentation, as recommended by mmap(2).
+ for(ofs=0; ofs < FSIZE; ofs+=CLUSTERSIZE){
+ char buffer[CLUSTERSIZE];
+ bzero(buffer, CLUSTERSIZE);
+ if ( -1 == write(outfd, buffer, CLUSTERSIZE)){
+ perror("write");
+ exit(1);
+ }
+ }
+ //Return the file pointer to the beginning prior to mmap
+ if (-1 == lseek(outfd, 0, SEEK_SET)){
+ perror("lseek");
+ }
+
+#if USE_MMAP
+ //mmap the file
+ p_file = mmap(0, FSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, outfd, 0);
+ if (p_file == MAP_FAILED){
+ perror("mmap");
+ exit(1);
+ }
+#endif
+#if USE_THREADS
+ //Create the semaphores
+ for(sem_idx=0; sem_idx < NUM_CHUNKS; sem_idx++){
+ if (sem_init(&chunk_sems[sem_idx], 0, 0)){
+ perror("sem_init");
+ exit(1);
+ }
+ }
+
+ //Create the worker threads
+ if (pthread_create(&follower_th, NULL, follower, pat_p)){
+ perror("pthread_create follower");
+ exit(1);
+ }
+ if (pthread_create(&leader_th, NULL, leader, pat_p)){
+ perror("pthread_create leader");
+ exit(1);
+ }
+
+ //Join the threads
+ if (pthread_join(leader_th, NULL)){
+ perror("pthread_join leader");
+ exit(1);
+ }
+/* printf("Joined leader\n");*/
+ if (pthread_join(follower_th, NULL)){
+ perror("pthread_join follower");
+ exit(1);
+ }
+/* printf("Joined follower\n");*/
+
+ //destroy the semaphores
+ for(sem_idx=0; sem_idx < NUM_CHUNKS; sem_idx++){
+ if (sem_destroy(&chunk_sems[sem_idx])){
+ perror("sem_destory");
+ exit(1);
+ }
+ }
+ //printf("destroyed semaphores\n");
+#else
+ leader(pat_p); follower(pat_p);
+#endif
+
+#if USE_MMAP
+ //unmap the file prior to verifying it
+ if (msync(p_file, 0, 0)){
+ perror("msync");
+ exit(1);
+ }
+ //printf("finished msync\n");
+
+ if (munmap(p_file, FSIZE)){
+ perror("munmap");
+ exit(1);
+ }
+ //printf("finished munmap\n");
+#endif
+
+ //Verify the contents of the file.
+ if (verify_file(outfd, patterns[pat])) {
+ exit(1);
+ }
+ //printf("finished verify_file\n");
+
+ //close the file:
+ if (close(outfd)){
+ perror("close");
+ exit(1);
+ }
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/tests/sys/cddl/zfs/tests/txg_integrity/txg_integrity.cfg b/tests/sys/cddl/zfs/tests/txg_integrity/txg_integrity.cfg
new file mode 100644
index 000000000000..d63eda65f146
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/txg_integrity/txg_integrity.cfg
@@ -0,0 +1,29 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2011 Spectra logic. All rights reserved.
+# Use is subject to license terms.
+
+export TESTFILE=testfile${TESTCASE_ID}
+export DISK="/dev/md${TESTCASE_ID}"
+export TESTDEV=${DISK}
diff --git a/tests/sys/cddl/zfs/tests/txg_integrity/txg_integrity.d b/tests/sys/cddl/zfs/tests/txg_integrity/txg_integrity.d
new file mode 100644
index 000000000000..12b482afd0ba
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/txg_integrity/txg_integrity.d
@@ -0,0 +1,115 @@
+#!/usr/sbin/dtrace -s
+
+/* This D script attempts to verify whether a single dbuf is being held by
+ * multiple transaction groups at the time it is fixed up. The purpose is
+ * to verify whether the txg_integrity test exercises the kernel code paths
+ * that we want it to.
+ * XXX this is a work in progress. It is not yet usable
+ */
+
+/* zfs:kernel:dbuf_fix_old_data:entry
+{
+ printf("entry");
+}*/
+
+dtrace:::BEGIN
+{
+ dmu_write_uio_dnode_size = 0;
+ dmu_write_uio_dnode_loffset = 0;
+ dmu_write_uio_dnode_numbufs = 0;
+ dbuf_dirty_size = 0;
+ dbuf_dirty_offset = 0;
+ uio_stats2_refcount = 0,
+ uio_stats2_dirtycnt = 0,
+ dbuf_dirty_entry_refcount = 0;
+ dbuf_dirty_entry_dirtycnt = 0;
+ dbuf_dirty_refcount = 0;
+ dbuf_dirty_dirtycnt = 0;
+ ge2TrackerName[0] = "";
+ ge2TrackerCount[0] = 0;
+}
+
+zfs:kernel:dbuf_fix_old_data:db_get_spa
+{
+ /*printf("db_get_spa");*/
+ /* stack(); */
+ printf("uio_stats: size=%d\tloffset=%x\tnumbufs=%d\n\trefcount(holds)=%d\tdirtycnt=%d\n",
+ dmu_write_uio_dnode_size ,
+ dmu_write_uio_dnode_loffset,
+ dmu_write_uio_dnode_numbufs ,
+ uio_stats2_refcount,
+ uio_stats2_dirtycnt);
+ printf("dbuf_dirty stats: size=%d\toffset=%x\n\tentry: refcount(holds)=%d\tdirtycnt=%d\n\texit: refcount(holds)=%d\tdirtycnt=%d\n",
+ dbuf_dirty_size,
+ dbuf_dirty_offset,
+ dbuf_dirty_entry_refcount,
+ dbuf_dirty_entry_dirtycnt,
+ dbuf_dirty_refcount,
+ dbuf_dirty_dirtycnt);
+ printf("DR_TXG=%d\tDB address = %x\n", args[0], (long long)args[1]);
+ printf("ge2Tracker entry: name=%s\tcount=%d", ge2TrackerName[(long long)args[1]], ge2TrackerCount[(long long)args[1]]);
+ exit(0);
+}
+
+zfs:kernel:dbuf_dirty:entry
+{
+ dbuf_dirty_size = args[0];
+ dbuf_dirty_offset = args[1];
+ dbuf_dirty_entry_refcount = args[2];
+ dbuf_dirty_entry_dirtycnt = args[3];
+}
+
+
+zfs2:kernel:dmu_write_uio_dnode:uio_stats
+{
+ dmu_write_uio_dnode_size = args[0];
+ dmu_write_uio_dnode_loffset = args[1];
+ dmu_write_uio_dnode_numbufs = args[2];
+ printf("uio_stats: size=%d\tloffset=%x\tnumbufs=%d", args[0], args[1], args[2]);
+}
+
+zfs2:kernel:dmu_write_uio_dnode:uio_stats_two
+{
+ uio_stats2_refcount = args[0];
+ uio_stats2_dirtycnt = args[1];
+ printf("uio_stats2: refcount=%d\tdirtycnt=%d\tdb=%x", args[0], args[1], (long long)args[2]);
+}
+
+zfs2:kernel:dmu_write_uio_dnode:uio_stats_three
+{
+ printf("uio_stats3: io_txg=%d", args[0]);
+}
+
+zfs:kernel:dbuf_dirty:no_db_nofill
+{
+ dbuf_dirty_refcount = args[0];
+ dbuf_dirty_dirtycnt = args[1];
+}
+
+/* refcount:kernel::ge2
+{
+ stack();
+} */
+
+zfs:kernel:dbuf_hold_impl:ge2
+{
+ ge2TrackerName[(long long)args[0]] = "dbuf_hold_impl";
+ ge2TrackerCount[(long long)args[0]] = args[1];
+ printf(">= 2: db=%x\trc=%d", (long long)args[0], args[1]);
+ /* stack(); */
+ /* ustack(10); */
+}
+
+/* zfs:kernel:dbuf_add_ref:ge2
+{
+ ge2TrackerName[(long long)args[0]] = "dbuf_add_ref";
+ ge2TrackerCount[(long long)args[0]] = args[1];
+} */
+
+/* zfs2:kernel:dmu_bonus_hold:ge2
+{
+ ge2TrackerName[(long long)args[0]] = "dmu_bonus_hold";
+ ge2TrackerCount[(long long)args[0]] = args[1];
+}*/
+
+
diff --git a/tests/sys/cddl/zfs/tests/txg_integrity/txg_integrity_001_pos.ksh b/tests/sys/cddl/zfs/tests/txg_integrity/txg_integrity_001_pos.ksh
new file mode 100644
index 000000000000..011dd067fd1d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/txg_integrity/txg_integrity_001_pos.ksh
@@ -0,0 +1,72 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2011 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: transaction_group_integrity_001_pos
+#
+# DESCRIPTION:
+#
+# Verify the integrity of non-aligned writes to the same blocks that cross
+# transaction groups.
+#
+# STRATEGY:
+# This test verifies that non-aligned writes are correctly committed to the
+# file system, even adjacent transaction groups include writes to the same
+# blocks. The test runs through multiple repetitions in an attempt to trigger
+# race conditions.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: BEGIN (2011-10-20)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "both"
+
+log_assert "Ensure that non-aligned writes to the same blocks that cross" \
+ "transaction groups do not corrupt the file."
+
+# Run the test program
+txg_integrity ${TESTDIR}/${TESTFILE}
+
+# Success is indicated by the return status
+if [[ $? -ne 0 ]]; then
+ log_fail "Test failed to execute or file became corrupted"
+else
+ log_pass "Multiple unaligned writes from multiple transactions groups succeeded"
+fi
+
+
diff --git a/tests/sys/cddl/zfs/tests/txg_integrity/txg_integrity_test.sh b/tests/sys/cddl/zfs/tests/txg_integrity/txg_integrity_test.sh
new file mode 100755
index 000000000000..cf92c3605b21
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/txg_integrity/txg_integrity_test.sh
@@ -0,0 +1,111 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case txg_integrity_001_pos cleanup
+txg_integrity_001_pos_head()
+{
+ atf_set "descr" "Ensure that non-aligned writes to the same blocks that cross transaction groups do not corrupt the file."
+ atf_set "timeout" 1800
+}
+txg_integrity_001_pos_body()
+{
+ export PATH=$(atf_get_srcdir):$PATH
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/txg_integrity.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/txg_integrity_001_pos.ksh
+ if [[ $? != 0 ]]; then
+ save_artifacts
+ atf_fail "Testcase failed"
+ fi
+}
+txg_integrity_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/txg_integrity.cfg
+
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case fsync_integrity_001_pos cleanup
+fsync_integrity_001_pos_head()
+{
+ atf_set "descr" "Verify the integrity of non-aligned writes to the same blocks within the same transaction group, where an fsync is issued by a non-final writer."
+ atf_set "timeout" 1800
+}
+fsync_integrity_001_pos_body()
+{
+ export PATH=$(atf_get_srcdir):$PATH
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/txg_integrity.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/fsync_integrity_001_pos.ksh
+ if [[ $? != 0 ]]; then
+ save_artifacts
+ atf_fail "Testcase failed"
+ fi
+}
+fsync_integrity_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/txg_integrity.cfg
+ export DISK="/dev/md${TESTCASE_ID}"
+ export TESTDEV=${DISK}p1
+
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case txg_integrity_001_pos
+ atf_add_test_case fsync_integrity_001_pos
+}
+
+
+save_artifacts()
+{
+ # If ARTIFACTS_DIR is defined, save test artifacts for
+ # post-mortem analysis
+ if [[ -n $ARTIFACTS_DIR ]]; then
+ TC_ARTIFACTS_DIR=${ARTIFACTS_DIR}/sys/cddl/zfs/tests/txg_integrity/$(atf_get ident)
+ mkdir -p $TC_ARTIFACTS_DIR
+ cp -a $TESTDIR/$TESTFILE $TC_ARTIFACTS_DIR
+ bzip2 $TC_ARTIFACTS_DIR/$TESTFILE
+ # Now export the pool and tar up the entire thing
+ zpool export $TESTPOOL
+ dd if=$TESTDEV bs=131072 of=$TC_ARTIFACTS_DIR/pool
+ bzip2 $TC_ARTIFACTS_DIR/pool
+ # Reimport it so that the cleanup script will work
+ zpool import $TESTPOOL
+ fi
+}
diff --git a/tests/sys/cddl/zfs/tests/userquota/Makefile b/tests/sys/cddl/zfs/tests/userquota/Makefile
new file mode 100644
index 000000000000..678678ced1dd
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/userquota/Makefile
@@ -0,0 +1,32 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/userquota
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= userquota_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= groupspace_001_pos.ksh
+${PACKAGE}FILES+= userquota_common.kshlib
+${PACKAGE}FILES+= userquota_010_pos.ksh
+${PACKAGE}FILES+= userquota_001_pos.ksh
+${PACKAGE}FILES+= userquota_009_pos.ksh
+${PACKAGE}FILES+= userquota_008_pos.ksh
+${PACKAGE}FILES+= userquota_004_pos.ksh
+${PACKAGE}FILES+= userquota_005_neg.ksh
+${PACKAGE}FILES+= userspace_001_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= userquota_011_pos.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= userquota_002_pos.ksh
+${PACKAGE}FILES+= userquota_006_pos.ksh
+${PACKAGE}FILES+= userquota_012_neg.ksh
+${PACKAGE}FILES+= groupspace_002_pos.ksh
+${PACKAGE}FILES+= userquota_003_pos.ksh
+${PACKAGE}FILES+= userquota_007_pos.ksh
+${PACKAGE}FILES+= userquota.cfg
+${PACKAGE}FILES+= userspace_002_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/userquota/cleanup.ksh b/tests/sys/cddl/zfs/tests/userquota/cleanup.ksh
new file mode 100644
index 000000000000..e1c1ba6854a1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/userquota/cleanup.ksh
@@ -0,0 +1,36 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/userquota/userquota_common.kshlib
+
+if ! is_userquota_supported; then
+ log_unsupported "userquota is not supported in this system."
+fi
+
+log_must clean_user_group
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/userquota/groupspace_001_pos.ksh b/tests/sys/cddl/zfs/tests/userquota/groupspace_001_pos.ksh
new file mode 100644
index 000000000000..b12dcbfd7369
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/userquota/groupspace_001_pos.ksh
@@ -0,0 +1,93 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: groupspace_001_pos
+#
+# DESCRIPTION:
+# Check the zfs groupspace with all parameters
+#
+#
+# STRATEGY:
+# 1. set zfs groupquota to a fs
+# 2. write some data to the fs with specified user and group
+# 3. use zfs groupspace with all possible parameters to check the result
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2009-04-16)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/userquota/userquota_common.kshlib
+
+function cleanup
+{
+ if datasetexists $snap_fs; then
+ log_must $ZFS destroy $snap_fs
+ fi
+
+ log_must cleanup_quota
+}
+
+log_onexit cleanup
+
+log_assert "Check the zfs groupspace with all possible parameters"
+
+set -A params -- "-h" "--help" "-n" "-H" "-p" "-o type,name,used,quota" \
+ "-o name,used,quota" "-o used,quota" "-o used" "-o quota" \
+ "-s type" "-s name" "-s used" "-s quota" \
+ "-S type" "-S name" "-S used" "-S quota" \
+ "-t posixuser" "-t posixgroup" "-t all"
+
+if check_version "5.11" ; then
+ set -A params -- "${params[@]}" "-i" "-t smbuser" "-t smbgroup"
+fi
+
+typeset snap_fs=$QFS@snap
+
+log_must $ZFS set groupquota@$QGROUP=500m $QFS
+mkmount_writable $QFS
+log_must user_run $QUSER1 $MKFILE 50m $QFILE
+
+$SYNC
+
+log_must $ZFS snapshot $snap_fs
+
+for param in "${params[@]}"; do
+ log_must eval "$ZFS groupspace $param $QFS >/dev/null 2>&1"
+ log_must eval "$ZFS groupspace $param $snap_fs >/dev/null 2>&1"
+done
+
+log_pass "Check the zfs groupspace with all possible parameters"
diff --git a/tests/sys/cddl/zfs/tests/userquota/groupspace_002_pos.ksh b/tests/sys/cddl/zfs/tests/userquota/groupspace_002_pos.ksh
new file mode 100644
index 000000000000..f3fe2269b2f5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/userquota/groupspace_002_pos.ksh
@@ -0,0 +1,88 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: groupspace_002_pos
+#
+# DESCRIPTION:
+# Check the user used and groupspace size in zfs groupspace
+#
+#
+# STRATEGY:
+# 1. set zfs groupquota to a fs
+# 2. write some data to the fs with specified user and size
+# 3. use zfs groupspace to check the used size and quota size
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2009-04-16)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/userquota/userquota_common.kshlib
+
+function cleanup
+{
+ if datasetexists $snapfs; then
+ log_must $ZFS destroy $snapfs
+ fi
+ log_must cleanup_quota
+}
+
+log_onexit cleanup
+
+log_assert "Check the zfs groupspace used and quota"
+
+log_must $ZFS set groupquota@$QGROUP=500m $QFS
+mkmount_writable $QFS
+log_must user_run $QUSER1 $TRUNCATE -s 100m $QFILE
+
+$SYNC
+
+typeset snapfs=$QFS@snap
+
+log_must $ZFS snapshot $snapfs
+
+log_must eval "$ZFS groupspace $QFS >/dev/null 2>&1"
+log_must eval "$ZFS groupspace $snapfs >/dev/null 2>&1"
+
+for fs in "$QFS" "$snapfs"; do
+ log_note "check the quota size in zfs groupspace $fs"
+ log_must eval "$ZFS groupspace $fs | $GREP $QGROUP | $GREP 500M"
+
+ log_note "check the user used size in zfs groupspace $fs"
+ log_must eval "$ZFS groupspace $fs | $GREP $QGROUP | $GREP 100M"
+done
+
+log_pass "Check the zfs groupspace used and quota pass as expect"
diff --git a/tests/sys/cddl/zfs/tests/userquota/setup.ksh b/tests/sys/cddl/zfs/tests/userquota/setup.ksh
new file mode 100644
index 000000000000..9521504e342f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/userquota/setup.ksh
@@ -0,0 +1,43 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/userquota/userquota_common.kshlib
+
+verify_runnable "both"
+
+if ! is_userquota_supported; then
+ log_unsupported "userquota is not supported in this system."
+fi
+
+log_must clean_user_group
+
+log_must add_group $QGROUP
+log_must add_user $QGROUP $QUSER1
+log_must add_user $QGROUP $QUSER2
+
+DISK=${DISKS%% *}
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/userquota/userquota.cfg b/tests/sys/cddl/zfs/tests/userquota/userquota.cfg
new file mode 100644
index 000000000000..bd93920f047a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/userquota/userquota.cfg
@@ -0,0 +1,43 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export QUSER1=quser1
+export QUSER2=quser2
+
+export QGROUP=qgroup
+export QGROUP1=qgroup1
+export QGROUP1=qgroup2
+
+export UQUOTA_SIZE=1000000
+export GQUOTA_SIZE=4000000
+
+export QFS=$TESTPOOL/$TESTFS
+export QFILE=$TESTDIR/qf.${TESTCASE_ID}
+export OFILE=$TESTDIR/of.${TESTCASE_ID}
+
+export SNAP_QUOTA=100m
+export TEST_QUOTA=88888
+
diff --git a/tests/sys/cddl/zfs/tests/userquota/userquota_001_pos.ksh b/tests/sys/cddl/zfs/tests/userquota/userquota_001_pos.ksh
new file mode 100644
index 000000000000..2f333f8cf035
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/userquota/userquota_001_pos.ksh
@@ -0,0 +1,83 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+################################################################################
+#
+#
+# __stc_assertion_start
+#
+# ID: userquota_001_pos
+#
+# DESCRIPTION:
+# Check the basic function of the userquota and groupquota
+#
+#
+# STRATEGY:
+# 1. Set userquota and overwrite the quota size
+# 2. The write operation should fail with Disc quota exceeded
+# 3. Set groupquota and overwrite the quota size
+# 4. The write operation should fail with Disc quota exceeded
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2009-04-16)
+#
+# __stc_assertion_end
+#
+#
+###############################################################################
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/userquota/userquota_common.kshlib
+
+function cleanup
+{
+ cleanup_quota
+}
+
+log_onexit cleanup
+
+log_assert "If write operation overwrite {user|group}quota size, it will fail"
+
+mkmount_writable $QFS
+log_note "Check the userquota@$QUSER1"
+log_must $ZFS set userquota@$QUSER1=$UQUOTA_SIZE $QFS
+log_must user_run $QUSER1 $TRUNCATE -s UQUOTA_SIZE $QFILE
+$SYNC
+log_mustnot user_run $QUSER1 $TRUNCATE -s 1 $OFILE
+cleanup_quota
+
+log_note "Check the groupquota@$QGROUP"
+log_must $ZFS set groupquota@$QGROUP=$GQUOTA_SIZE $QFS
+mkmount_writable $QFS
+log_must user_run $QUSER1 $TRUNCATE -s $GQUOTA_SIZE $QFILE
+$SYNC
+log_mustnot user_run $TRUNCATE -s 1 $OFILE
+
+cleanup_quota
+
+log_pass "Write operation overwrite {user|group}quota size, it as expect"
diff --git a/tests/sys/cddl/zfs/tests/userquota/userquota_002_pos.ksh b/tests/sys/cddl/zfs/tests/userquota/userquota_002_pos.ksh
new file mode 100644
index 000000000000..f1c4049630f6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/userquota/userquota_002_pos.ksh
@@ -0,0 +1,93 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: userquota_002_pos
+#
+# DESCRIPTION:
+# the userquota and groupquota can be set during zpool or zfs creation"
+#
+#
+# STRATEGY:
+# 1. Set userquota and groupquota via "zpool -O or zfs create -o"
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2009-04-16)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/userquota/userquota_common.kshlib
+
+verify_runnable "global"
+
+function cleanup
+{
+ destroy_pool $TESTPOOL1
+
+ if [[ -f $pool_vdev ]]; then
+ $RM -f $pool_vdev
+ fi
+}
+
+log_onexit cleanup
+
+log_assert \
+ "the userquota and groupquota can be set during zpool,zfs creation"
+
+typeset pool_vdev=$TMPDIR/pool_dev.${TESTCASE_ID}
+
+log_must create_vdevs $pool_vdev
+destroy_pool $TESTPOOL1
+
+log_must $ZPOOL create -O userquota@$QUSER1=$UQUOTA_SIZE \
+ -O groupquota@$QGROUP=$GQUOTA_SIZE $TESTPOOL1 $pool_vdev
+
+log_must eval "$ZFS list -r -o userquota@$QUSER1,groupquota@$QGROUP \
+ $TESTPOOL1 > /dev/null 2>&1"
+
+log_must check_quota "userquota@$QUSER1" $TESTPOOL1 "$UQUOTA_SIZE"
+log_must check_quota "groupquota@$QGROUP" $TESTPOOL1 "$GQUOTA_SIZE"
+
+log_must $ZFS create -o userquota@$QUSER1=$UQUOTA_SIZE \
+ -o groupquota@$QGROUP=$GQUOTA_SIZE $TESTPOOL1/fs
+
+log_must eval "$ZFS list -r -o userquota@$QUSER1,groupquota@$QGROUP \
+ $TESTPOOL1 > /dev/null 2>&1"
+
+log_must check_quota "userquota@$QUSER1" $TESTPOOL1/fs "$UQUOTA_SIZE"
+log_must check_quota "groupquota@$QGROUP" $TESTPOOL1/fs "$GQUOTA_SIZE"
+
+log_pass \
+ "the userquota and groupquota can be set during zpool,zfs creation"
diff --git a/tests/sys/cddl/zfs/tests/userquota/userquota_003_pos.ksh b/tests/sys/cddl/zfs/tests/userquota/userquota_003_pos.ksh
new file mode 100644
index 000000000000..cd4327d461c8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/userquota/userquota_003_pos.ksh
@@ -0,0 +1,70 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: userquota_003_pos
+#
+# DESCRIPTION:
+# Check the basic function of set/get userquota and groupquota on fs
+#
+#
+# STRATEGY:
+# 1. Set userquota on fs and check the zfs get
+# 2. Set groupquota on fs and check the zfs get
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2009-04-16)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/userquota/userquota_common.kshlib
+
+function cleanup
+{
+ cleanup_quota
+}
+
+log_onexit cleanup
+
+log_assert "Check the basic function of set/get userquota and groupquota on fs"
+
+log_note "Check the set|get userquota@$QUSER1 and groupquota@QGROUP"
+log_must $ZFS set userquota@$QUSER1=$UQUOTA_SIZE $QFS
+log_must check_quota "userquota@$QUSER1" $QFS "$UQUOTA_SIZE"
+
+log_must $ZFS set groupquota@$QGROUP=$GQUOTA_SIZE $QFS
+log_must check_quota "groupquota@$QGROUP" $QFS "$GQUOTA_SIZE"
+
+log_pass "Check the basic function of set/get userquota on fs passed as expect"
diff --git a/tests/sys/cddl/zfs/tests/userquota/userquota_004_pos.ksh b/tests/sys/cddl/zfs/tests/userquota/userquota_004_pos.ksh
new file mode 100644
index 000000000000..d814ceca9dd9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/userquota/userquota_004_pos.ksh
@@ -0,0 +1,90 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: userquota_004_pos
+#
+# DESCRIPTION:
+# Check the basic function user|group used
+#
+#
+# STRATEGY:
+# 1. Write some data to fs by normal user and check the user|group used
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2009-04-16)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/userquota/userquota_common.kshlib
+
+function cleanup
+{
+ cleanup_quota
+}
+
+log_onexit cleanup
+
+log_assert "Check the basic function of {user|group} used"
+
+typeset user_used=$(get_value "userused@$QUSER1" $QFS)
+typeset group_used=$(get_value "groupused@$QGROUP" $QFS)
+
+if [[ $user_used != 0 ]]; then
+ log_fail "FAIL: userused should be 0"
+fi
+if [[ $group_used != 0 ]]; then
+ log_fail "FAIL: groupused should be 0"
+fi
+
+mkmount_writable $QFS
+log_must user_run $QUSER1 $TRUNCATE -s 100m $QFILE
+$SYNC
+
+user_used=$(get_value "userused@$QUSER1" $QFS)
+group_used=$(get_value "groupused@$QGROUP" $QFS)
+
+if [[ $user_used != "100M" ]]; then
+ log_note "user $QUSER1 used is $user_used"
+ log_fail "userused for user $QUSER1 expected to be 50.0M, not $user_used"
+fi
+
+if [[ $user_used != $group_used ]]; then
+ log_note "user $QUSER1 used is $user_used"
+ log_note "group $QGROUP used is $group_used"
+ log_fail "FAIL: userused should equal to groupused"
+fi
+
+log_pass "Check the basic function of {user|group}used pass as expect"
diff --git a/tests/sys/cddl/zfs/tests/userquota/userquota_005_neg.ksh b/tests/sys/cddl/zfs/tests/userquota/userquota_005_neg.ksh
new file mode 100644
index 000000000000..1dccefcee2cc
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/userquota/userquota_005_neg.ksh
@@ -0,0 +1,103 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: userquota_005_neg
+#
+# DESCRIPTION:
+# Check the invalid parameter of zfs set user|group quota
+#
+#
+# STRATEGY:
+# 1. check the invalid zfs set user|group quota to fs
+# 1. check the valid zfs set user|group quota to snapshots
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2009-04-16)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/userquota/userquota_common.kshlib
+
+function cleanup
+{
+ if datasetexists $snap_fs; then
+ log_must $ZFS destroy $snap_fs
+ fi
+
+ log_must cleanup_quota
+}
+
+log_onexit cleanup
+
+log_assert "Check the invalid parameter of zfs set user|group quota"
+typeset snap_fs=$QFS@snap
+
+log_must $ZFS snapshot $snap_fs
+
+set -A no_users "mms1234" "ss@#" "root-122"
+for user in "${no_users[@]}"; do
+ log_mustnot $ID $user
+ log_mustnot $ZFS set userquota@$user=100m $QFS
+done
+
+log_note "can set all numberic id even that id is not existed"
+log_must $ZFS set userquota@12345678=100m $QFS
+log_mustnot $ZFS set userquota@12345678=100m $snap_fs
+
+set -A sizes "100mfsd" "m0.12m" "GGM" "-1234-m" "123m-m"
+
+for size in "${sizes[@]}"; do
+ log_note "can not set user quota with invalid size parameter"
+ log_mustnot $ZFS set userquota@root=$size $QFS
+done
+
+log_note "can not set user quota to snapshot $snap_fs"
+log_mustnot $ZFS set userquota@root=100m $snap_fs
+
+
+set -A no_groups "aidsf@dfsd@" "123223-dsfds#sdfsd" "mss_#ss" "@@@@"
+for group in "${no_groups[@]}"; do
+ log_mustnot eval "$GREP $group /etc/group"
+ log_mustnot $ZFS set groupquota@$group=100m $QFS
+done
+
+log_note "can not set group quota with invalid size parameter"
+log_mustnot $ZFS set groupquota@root=100msfsd $QFS
+
+log_note "can not set group quota to snapshot $snap_fs"
+log_mustnot $ZFS set groupquota@root=100m $snap_fs
+
+log_pass "Check the invalid parameter of zfs set user|group quota pas as expect"
diff --git a/tests/sys/cddl/zfs/tests/userquota/userquota_006_pos.ksh b/tests/sys/cddl/zfs/tests/userquota/userquota_006_pos.ksh
new file mode 100644
index 000000000000..1f5a90820e4c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/userquota/userquota_006_pos.ksh
@@ -0,0 +1,84 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: userquota_006_pos
+#
+# DESCRIPTION:
+# Check the invalid parameter of zfs get user|group quota
+#
+#
+# STRATEGY:
+# 1. check the invalid zfs get user|group quota to fs
+# 2. check the valid zfs get user|group quota to snapshots
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2009-04-16)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/userquota/userquota_common.kshlib
+
+function cleanup
+{
+ if datasetexists $snap_fs; then
+ log_must $ZFS destroy $snap_fs
+ fi
+
+ log_must cleanup_quota
+}
+
+log_onexit cleanup
+
+log_assert "Check the invalid parameter of zfs get user|group quota"
+typeset snap_fs=$QFS@snap
+
+log_must $ZFS snapshot $snap_fs
+
+set -A no_users "mms1234" "ss@#" "root-122" "1234"
+for user in "${no_users[@]}"; do
+ log_mustnot eval "$ID $user >/dev/null 2>&1"
+ log_must eval "$ZFS get userquota@$user $QFS >/dev/null 2>&1"
+ log_must eval "$ZFS get userquota@$user $snap_fs >/dev/null 2>&1"
+done
+
+set -A no_groups "aidsf@dfsd@" "123223-dsfds#sdfsd" "mss_#ss" "1234"
+for group in "${no_groups[@]}"; do
+ log_mustnot eval "$GROUPDEL $group > /dev/null 2>&1"
+ log_must eval "$ZFS get groupquota@$group $QFS >/dev/null 2>&1"
+ log_must eval "$ZFS get groupquota@$group $snap_fs >/dev/null 2>&1"
+done
+
+log_pass "Check the invalid parameter of zfs get user|group quota pass as expect"
diff --git a/tests/sys/cddl/zfs/tests/userquota/userquota_007_pos.ksh b/tests/sys/cddl/zfs/tests/userquota/userquota_007_pos.ksh
new file mode 100644
index 000000000000..c684f79ecd31
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/userquota/userquota_007_pos.ksh
@@ -0,0 +1,84 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: userquota_007_pos
+#
+# DESCRIPTION:
+#
+# userquota/groupquota can be set beyond the fs quota
+# userquota/groupquota can be set at a smaller size than its current usage.
+#
+# STRATEGY:
+# 1. set quota to a fs and set a larger size of userquota and groupquota
+# 2. write some data to the fs and set a smaller userquota and groupquota
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2009-04-16)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/userquota/userquota_common.kshlib
+
+function cleanup
+{
+ log_must cleanup_quota
+ log_must $ZFS set quota=none $QFS
+}
+
+log_onexit cleanup
+
+log_assert "Check set user|group quota to larger than the quota size of a fs"
+
+log_must $ZFS set quota=200m $QFS
+log_must $ZFS set userquota@$QUSER1=500m $QFS
+log_must $ZFS set groupquota@$QGROUP=600m $QFS
+
+log_must $ZFS get userquota@$QUSER1 $QFS
+log_must $ZFS get groupquota@$QGROUP $QFS
+
+log_note "write some data to the $QFS"
+mkmount_writable $QFS
+log_must user_run $QUSER1 $MKFILE 100m $QFILE
+$SYNC
+
+log_note "set user|group quota at a smaller size than it current usage"
+log_must $ZFS set userquota@$QUSER1=90m $QFS
+log_must $ZFS set groupquota@$QGROUP=90m $QFS
+
+log_must $ZFS get userquota@$QUSER1 $QFS
+log_must $ZFS get groupquota@$QGROUP $QFS
+
+log_pass "set user|group quota to larger than quota size of a fs pass as expect"
diff --git a/tests/sys/cddl/zfs/tests/userquota/userquota_008_pos.ksh b/tests/sys/cddl/zfs/tests/userquota/userquota_008_pos.ksh
new file mode 100644
index 000000000000..15c44b6b33d0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/userquota/userquota_008_pos.ksh
@@ -0,0 +1,69 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: userquota_008_pos
+#
+# DESCRIPTION:
+#
+# zfs get all <fs> does not print out userquota/groupquota
+#
+# STRATEGY:
+# 1. set userquota and groupquota to a fs
+# 2. check zfs get all fs
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2009-04-16)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/userquota/userquota_common.kshlib
+
+function cleanup
+{
+ log_must cleanup_quota
+}
+
+log_onexit cleanup
+
+log_assert "Check zfs get all will not print out user|group quota"
+
+log_must $ZFS set userquota@$QUSER1=50m $QFS
+log_must $ZFS set groupquota@$QGROUP=100m $QFS
+
+log_mustnot $ZFS get all $QFS | $GREP userquota
+log_mustnot $ZFS get all $QFS | $GREP groupquota
+
+log_pass "zfs get all will not print out user|group quota"
diff --git a/tests/sys/cddl/zfs/tests/userquota/userquota_009_pos.ksh b/tests/sys/cddl/zfs/tests/userquota/userquota_009_pos.ksh
new file mode 100644
index 000000000000..537a0bccd012
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/userquota/userquota_009_pos.ksh
@@ -0,0 +1,101 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: userquota_009_pos
+#
+# DESCRIPTION:
+# Check user|group quota to snapshot that:
+# 1) can not set user|group quota to snapshot directly
+# 2) snapshot can inherit the parent fs's user|groupquota
+# 3) the user|group quota will not change even the parent fs's quota changed.
+#
+#
+# STRATEGY:
+# 1. create a snapshot of a fs
+# 2. set the user|group quota to snapshot and expect fail
+# 3. set user|group quota to fs and check the snapshot
+# 4. re-set user|group quota to fs and check the snapshot's value
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2009-04-16)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/userquota/userquota_common.kshlib
+
+function cleanup
+{
+ if datasetexists $snap_fs; then
+ log_must $ZFS destroy $snap_fs
+ fi
+
+ log_must cleanup_quota
+}
+
+log_onexit cleanup
+
+log_assert "Check the snapshot's user|group quota"
+typeset snap_fs=$QFS@snap
+
+
+log_must $ZFS set userquota@$QUSER1=$UQUOTA_SIZE $QFS
+log_must check_quota "userquota@$QUSER1" $QFS "$UQUOTA_SIZE"
+
+log_must $ZFS set groupquota@$QGROUP=$GQUOTA_SIZE $QFS
+log_must check_quota "groupquota@$QGROUP" $QFS "$GQUOTA_SIZE"
+
+log_must $ZFS snapshot $snap_fs
+
+log_note "check the snapshot $snap_fs user|group quota"
+log_must check_quota "userquota@$QUSER1" $snap_fs "$UQUOTA_SIZE"
+log_must check_quota "groupquota@$QGROUP" $snap_fs "$GQUOTA_SIZE"
+
+log_note "set userquota and groupquota to $snap_fs which will fail"
+log_mustnot $ZFS set userquota@$QUSER1=$SNAP_QUOTA $snap_fs
+log_mustnot $ZFS set groupquota@$QGROUP=$SNAP_QUOTA $snap_fs
+
+log_note "change the parent's userquota and groupquota"
+log_must $ZFS set userquota@$QUSER1=$TEST_QUOTA $QFS
+log_must $ZFS set groupquota@$QGROUP=$TEST_QUOTA $QFS
+
+log_must check_quota "userquota@$QUSER1" $QFS $TEST_QUOTA
+log_must check_quota "groupquota@$QGROUP" $QFS $TEST_QUOTA
+
+log_note "check the snapshot $snap_fs userquota and groupquota"
+log_must check_quota "userquota@$QUSER1" $snap_fs "$UQUOTA_SIZE"
+log_must check_quota "groupquota@$QGROUP" $snap_fs "$GQUOTA_SIZE"
+
+log_pass "Check the snapshot's user|group quota pass as expect"
diff --git a/tests/sys/cddl/zfs/tests/userquota/userquota_010_pos.ksh b/tests/sys/cddl/zfs/tests/userquota/userquota_010_pos.ksh
new file mode 100644
index 000000000000..a47459df0142
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/userquota/userquota_010_pos.ksh
@@ -0,0 +1,83 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: userquota_010_pos
+#
+# DESCRIPTION:
+# Check userquota and groupquota be overwrited at same time
+#
+#
+# STRATEGY:
+# 1. Set userquota and groupquota to a fs
+# 2. write to exceed the userquota size to check the result
+# 3. write to exceed the groupquota size to check the result
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2009-04-16)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/userquota/userquota_common.kshlib
+
+function cleanup
+{
+ cleanup_quota
+}
+
+log_onexit cleanup
+
+log_assert "overwrite any of the {user|group}quota size, it will fail"
+
+log_note "overwrite to $QFS to make it exceed userquota"
+log_must $ZFS set userquota@$QUSER1=$UQUOTA_SIZE $QFS
+log_must $ZFS set groupquota@$QGROUP=$GQUOTA_SIZE $QFS
+
+mkmount_writable $QFS
+log_must user_run $QUSER1 $MKFILE $UQUOTA_SIZE $QFILE
+$SYNC
+
+log_must eval "$ZFS get -p userused@$QUSER1 $QFS >/dev/null 2>&1"
+log_must eval "$ZFS get -p groupused@$GROUPUSED $QFS >/dev/null 2>&1"
+
+log_mustnot user_run $QUSER1 $MKFILE 1 $OFILE
+
+log_must $RM -f $QFILE
+
+log_note "overwrite to $QFS to make it exceed userquota"
+log_mustnot user_run $QUSER1 $MKFILE $GQUOTA_SIZE $QFILE
+
+log_must eval "$ZFS get -p userused@$QUSER1 $QFS >/dev/null 2>&1"
+log_must eval "$ZFS get -p groupused@$GROUPUSED $QFS >/dev/null 2>&1"
+
+log_pass "overwrite any of the {user|group}quota size, it fail as expect"
diff --git a/tests/sys/cddl/zfs/tests/userquota/userquota_011_pos.ksh b/tests/sys/cddl/zfs/tests/userquota/userquota_011_pos.ksh
new file mode 100644
index 000000000000..64642caf9b9b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/userquota/userquota_011_pos.ksh
@@ -0,0 +1,136 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: userquota_011_pos
+#
+# DESCRIPTION:
+# the userquota and groupquota will not change during zfs actions, such as
+# snapshot,clone,rename,upgrade,send,receive.
+#
+#
+# STRATEGY:
+# 1. Create a pool, and create fs with preset user,group quota
+# 2. Check set user|group quota via zfs snapshot|clone|list -o
+# 3. Check the user|group quota can not change during zfs rename|upgrade|promote
+# 4. Check the user|group quota can not change during zfs clone
+# 5. Check the user|group quota can not change during zfs send/receive
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2009-04-16)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/userquota/userquota_common.kshlib
+
+function cleanup
+{
+ for ds in $TESTPOOL/fs $TESTPOOL/fs-rename $TESTPOOL/fs-clone; do
+ if datasetexists $ds; then
+ log_must $ZFS destroy -rRf $ds
+ fi
+ done
+}
+
+log_onexit cleanup
+
+log_assert \
+ "the userquota and groupquota can't change during zfs actions"
+
+cleanup
+
+log_must $ZFS create -o userquota@$QUSER1=$UQUOTA_SIZE \
+ -o groupquota@$QGROUP=$GQUOTA_SIZE $TESTPOOL/fs
+
+log_must $ZFS snapshot $TESTPOOL/fs@snap
+log_must eval "$ZFS list -r -o userquota@$QUSER1,groupquota@$QGROUP \
+ $TESTPOOL >/dev/null 2>&1"
+
+log_must check_quota "userquota@$QUSER1" $TESTPOOL/fs@snap "$UQUOTA_SIZE"
+log_must check_quota "groupquota@$QGROUP" $TESTPOOL/fs@snap "$GQUOTA_SIZE"
+
+
+log_note "clone fs gets its parent's userquota/groupquota initially"
+log_must $ZFS clone -o userquota@$QUSER1=$UQUOTA_SIZE \
+ -o groupquota@$QGROUP=$GQUOTA_SIZE \
+ $TESTPOOL/fs@snap $TESTPOOL/fs-clone
+
+log_must eval "$ZFS list -r -o userquota@$QUSER1,groupquota@$QGROUP \
+ $TESTPOOL >/dev/null 2>&1"
+
+log_must check_quota "userquota@$QUSER1" $TESTPOOL/fs-clone "$UQUOTA_SIZE"
+log_must check_quota "groupquota@$QGROUP" $TESTPOOL/fs-clone "$GQUOTA_SIZE"
+
+log_must eval "$ZFS list -o userquota@$QUSER1,groupquota@$QGROUP \
+ $TESTPOOL/fs-clone >/dev/null 2>&1"
+
+log_note "zfs promote can not change the previously set user|group quota"
+log_must $ZFS promote $TESTPOOL/fs-clone
+
+log_must eval "$ZFS list -r -o userquota@$QUSER1,groupquota@$QGROUP \
+ $TESTPOOL >/dev/null 2>&1"
+
+log_must check_quota "userquota@$QUSER1" $TESTPOOL/fs-clone "$UQUOTA_SIZE"
+log_must check_quota "groupquota@$QGROUP" $TESTPOOL/fs-clone "$GQUOTA_SIZE"
+
+log_note "zfs send receive can not change the previously set user|group quota"
+log_must $ZFS send $TESTPOOL/fs-clone@snap | $ZFS receive $TESTPOOL/fs-rev
+
+log_must eval "$ZFS list -r -o userquota@$QUSER1,groupquota@$QGROUP \
+ $TESTPOOL >/dev/null 2>&1"
+
+log_must check_quota "userquota@$QUSER1" $TESTPOOL/fs-rev "$UQUOTA_SIZE"
+log_must check_quota "groupquota@$QGROUP" $TESTPOOL/fs-rev "$GQUOTA_SIZE"
+
+log_note "zfs rename can not change the previously set user|group quota"
+log_must $ZFS rename $TESTPOOL/fs-rev $TESTPOOL/fs-rename
+
+log_must eval "$ZFS list -r -o userquota@$QUSER1,groupquota@$QGROUP \
+ $TESTPOOL >/dev/null 2>&1"
+
+log_must check_quota "userquota@$QUSER1" $TESTPOOL/fs-rename "$UQUOTA_SIZE"
+log_must check_quota "groupquota@$QGROUP" $TESTPOOL/fs-rename "$GQUOTA_SIZE"
+
+log_note "zfs upgrade can not change the previously set user|group quota"
+log_must $ZFS upgrade $TESTPOOL/fs-rename
+
+log_must eval "$ZFS list -r -o userquota@$QUSER1,groupquota@$QGROUP \
+ $TESTPOOL >/dev/null 2>&1"
+
+log_must check_quota "userquota@$QUSER1" $TESTPOOL/fs-rename "$UQUOTA_SIZE"
+log_must check_quota "groupquota@$QGROUP" $TESTPOOL/fs-rename "$GQUOTA_SIZE"
+
+log_pass \
+ "the userquota and groupquota can't change during zfs actions"
diff --git a/tests/sys/cddl/zfs/tests/userquota/userquota_012_neg.ksh b/tests/sys/cddl/zfs/tests/userquota/userquota_012_neg.ksh
new file mode 100644
index 000000000000..ef94700e9227
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/userquota/userquota_012_neg.ksh
@@ -0,0 +1,76 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: userquota_012_neg
+#
+# DESCRIPTION:
+# userquota and groupquota can not be set against snapshot
+#
+#
+# STRATEGY:
+# 1. Set userquota on snap and check the zfs get
+# 2. Set groupquota on snap and check the zfs get
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2009-04-16)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/userquota/userquota_common.kshlib
+
+function cleanup
+{
+ cleanup_quota
+
+ if datasetexists $snap_fs; then
+ log_must $ZFS destroy $snap_fs
+ fi
+}
+
+log_onexit cleanup
+
+typeset snap_fs=$QFS@snap
+log_assert "Check set userquota and groupquota on snapshot"
+
+log_note "Check can not set user|group quuota on snapshot"
+log_must $ZFS snapshot $snap_fs
+
+log_mustnot $ZFS set userquota@$QUSER1=$UQUOTA_SIZE $snap_fs
+
+log_mustnot $ZFS set groupquota@$QGROUP=$GQUOTA_SIZE $snap_fs
+
+log_pass "Check set userquota and groupquota on snapshot"
+t"
diff --git a/tests/sys/cddl/zfs/tests/userquota/userquota_common.kshlib b/tests/sys/cddl/zfs/tests/userquota/userquota_common.kshlib
new file mode 100644
index 000000000000..ad5692ac2fdd
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/userquota/userquota_common.kshlib
@@ -0,0 +1,142 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/userquota/userquota.cfg
+
+
+#
+# Check if the test box support userquota or not.
+#
+function is_userquota_supported
+{
+ if ! fs_prop_exist "userquota@..."; then
+ return 1
+ fi
+
+ return 0
+}
+
+#
+# reset the userquota and groupquota and delete temporary files
+#
+function cleanup_quota
+{
+ if datasetexists $QFS; then
+ log_must $ZFS set userquota@$QUSER1=none $QFS
+ log_must $ZFS set userquota@$QUSER2=none $QFS
+ log_must $ZFS set groupquota@$QGROUP=none $QFS
+ recovery_writable $QFS
+ fi
+
+ [[ -f $QFILE ]] && log_must $RM -f $QFILE
+ [[ -f $OFILE ]] && log_must $RM -f $OFILE
+
+ return 0
+}
+
+#
+# delete user and group that created during the test
+#
+function clean_user_group
+{
+ for usr in $QUSER1 $QUSER2; do
+ log_must del_user $usr
+ done
+
+ log_must del_group $QGROUP
+
+ return 0
+}
+
+#
+# make the $QFS's mountpoint writable for all users
+#
+function mkmount_writable
+{
+ typeset fs=$1
+ typeset mntp=$(get_prop mountpoint $fs)
+ log_must $CHMOD 0777 $mntp
+}
+
+#
+# recovery the directory permission for $QFS
+#
+function recovery_writable
+{
+ typeset fs=$1
+ typeset mntp=$(get_prop mountpoint $fs)
+ log_must $CHMOD 0755 $mntp
+}
+
+#
+# run command as specific user
+#
+function user_run
+{
+ typeset user=$1
+ typeset group=$($GROUPS $user)
+
+ shift
+
+ eval \$RUNWATTR -u \$user -g \$group \"$@\" > /dev/null 2>&1
+ return $?
+}
+
+#
+# check the quota value of a specific FS
+#
+function check_quota
+{
+ typeset fs=$2
+ typeset prop=$1
+ typeset expected=$3
+ typeset value=$(get_prop $prop $fs)
+
+ if (( $value != $expected )); then
+ return 1
+ fi
+}
+
+#
+# zfs get prop, which return raw value not -p value.
+#
+function get_value # property dataset
+{
+ typeset prop_val
+ typeset prop=$1
+ typeset dataset=$2
+
+ prop_val=$($ZFS get -H -o value $prop $dataset 2>/dev/null)
+ if [[ $? -ne 0 ]]; then
+ log_note "Unable to get $prop property for dataset " \
+ "$dataset"
+ return 1
+ fi
+
+ $ECHO $prop_val
+ return 0
+}
diff --git a/tests/sys/cddl/zfs/tests/userquota/userquota_test.sh b/tests/sys/cddl/zfs/tests/userquota/userquota_test.sh
new file mode 100755
index 000000000000..5747ceed3180
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/userquota/userquota_test.sh
@@ -0,0 +1,462 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case groupspace_001_pos cleanup
+groupspace_001_pos_head()
+{
+ atf_set "descr" "Check the zfs groupspace with all possible parameters"
+ atf_set "require.progs" "ksh93 zfs runwattr"
+}
+groupspace_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/groupspace_001_pos.ksh || atf_fail "Testcase failed"
+}
+groupspace_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case groupspace_002_pos cleanup
+groupspace_002_pos_head()
+{
+ atf_set "descr" "Check the zfs groupspace used and quota"
+ atf_set "require.progs" "ksh93 zfs runwattr"
+}
+groupspace_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/groupspace_002_pos.ksh || atf_fail "Testcase failed"
+}
+groupspace_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case userquota_001_pos cleanup
+userquota_001_pos_head()
+{
+ atf_set "descr" "If write operation overwrite {user|group}quota size, it will fail"
+ atf_set "require.progs" "ksh93 zfs runwattr"
+}
+userquota_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/userquota_001_pos.ksh || atf_fail "Testcase failed"
+}
+userquota_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case userquota_002_pos cleanup
+userquota_002_pos_head()
+{
+ atf_set "descr" "the userquota and groupquota can be set during zpool,zfs creation"
+ atf_set "require.progs" "ksh93 zpool zfs"
+}
+userquota_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/userquota_002_pos.ksh || atf_fail "Testcase failed"
+}
+userquota_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case userquota_003_pos cleanup
+userquota_003_pos_head()
+{
+ atf_set "descr" "Check the basic function of set/get userquota and groupquota on fs"
+ atf_set "require.progs" "ksh93 zfs"
+}
+userquota_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/userquota_003_pos.ksh || atf_fail "Testcase failed"
+}
+userquota_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case userquota_004_pos cleanup
+userquota_004_pos_head()
+{
+ atf_set "descr" "Check the basic function of {user|group} used"
+ atf_set "require.progs" "ksh93 runwattr"
+}
+userquota_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/userquota_004_pos.ksh || atf_fail "Testcase failed"
+}
+userquota_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case userquota_005_neg cleanup
+userquota_005_neg_head()
+{
+ atf_set "descr" "Check the invalid parameter of zfs set user|group quota"
+ atf_set "require.progs" "ksh93 zfs"
+}
+userquota_005_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/userquota_005_neg.ksh || atf_fail "Testcase failed"
+}
+userquota_005_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case userquota_006_pos cleanup
+userquota_006_pos_head()
+{
+ atf_set "descr" "Check the invalid parameter of zfs get user|group quota"
+ atf_set "require.progs" "ksh93 zfs"
+}
+userquota_006_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/userquota_006_pos.ksh || atf_fail "Testcase failed"
+}
+userquota_006_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case userquota_007_pos cleanup
+userquota_007_pos_head()
+{
+ atf_set "descr" "Check set user|group quota to larger than the quota size of a fs"
+ atf_set "require.progs" "ksh93 zfs runwattr"
+}
+userquota_007_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/userquota_007_pos.ksh || atf_fail "Testcase failed"
+}
+userquota_007_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case userquota_008_pos cleanup
+userquota_008_pos_head()
+{
+ atf_set "descr" "Check zfs get all will not print out user|group quota"
+ atf_set "require.progs" "ksh93 zfs"
+}
+userquota_008_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/userquota_008_pos.ksh || atf_fail "Testcase failed"
+}
+userquota_008_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case userquota_009_pos cleanup
+userquota_009_pos_head()
+{
+ atf_set "descr" "Check the snapshot's user|group quota"
+ atf_set "require.progs" "ksh93 zfs"
+}
+userquota_009_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/userquota_009_pos.ksh || atf_fail "Testcase failed"
+}
+userquota_009_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case userquota_010_pos cleanup
+userquota_010_pos_head()
+{
+ atf_set "descr" "overwrite any of the {user|group}quota size, it will fail"
+ atf_set "require.progs" "ksh93 zfs runwattr"
+}
+userquota_010_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/userquota_010_pos.ksh || atf_fail "Testcase failed"
+}
+userquota_010_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case userquota_011_pos cleanup
+userquota_011_pos_head()
+{
+ atf_set "descr" "the userquota and groupquota can't change during zfs actions"
+ atf_set "require.progs" "ksh93 zfs"
+}
+userquota_011_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/userquota_011_pos.ksh || atf_fail "Testcase failed"
+}
+userquota_011_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case userquota_012_neg cleanup
+userquota_012_neg_head()
+{
+ atf_set "descr" "Check set userquota and groupquota on snapshot"
+ atf_set "require.progs" "ksh93 zfs"
+}
+userquota_012_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/userquota_012_neg.ksh || atf_fail "Testcase failed"
+}
+userquota_012_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case userspace_001_pos cleanup
+userspace_001_pos_head()
+{
+ atf_set "descr" "Check the zfs userspace with all possible parameters"
+ atf_set "require.progs" "ksh93 zfs runwattr"
+}
+userspace_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/userspace_001_pos.ksh || atf_fail "Testcase failed"
+}
+userspace_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case userspace_002_pos cleanup
+userspace_002_pos_head()
+{
+ atf_set "descr" "Check the zfs userspace used and quota"
+ atf_set "require.progs" "ksh93 zfs runwattr"
+}
+userspace_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/userspace_002_pos.ksh || atf_fail "Testcase failed"
+}
+userspace_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/userquota_common.kshlib
+ . $(atf_get_srcdir)/userquota.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case groupspace_001_pos
+ atf_add_test_case groupspace_002_pos
+ atf_add_test_case userquota_001_pos
+ atf_add_test_case userquota_002_pos
+ atf_add_test_case userquota_003_pos
+ atf_add_test_case userquota_004_pos
+ atf_add_test_case userquota_005_neg
+ atf_add_test_case userquota_006_pos
+ atf_add_test_case userquota_007_pos
+ atf_add_test_case userquota_008_pos
+ atf_add_test_case userquota_009_pos
+ atf_add_test_case userquota_010_pos
+ atf_add_test_case userquota_011_pos
+ atf_add_test_case userquota_012_neg
+ atf_add_test_case userspace_001_pos
+ atf_add_test_case userspace_002_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/userquota/userspace_001_pos.ksh b/tests/sys/cddl/zfs/tests/userquota/userspace_001_pos.ksh
new file mode 100644
index 000000000000..a2099c7b0c7c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/userquota/userspace_001_pos.ksh
@@ -0,0 +1,93 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: userspace_001_pos
+#
+# DESCRIPTION:
+# Check the zfs userspace with all parameters
+#
+#
+# STRATEGY:
+# 1. set zfs userspace to a fs
+# 2. write some data to the fs with specified user
+# 3. use zfs userspace with all possible parameters to check the result
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2009-04-16)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/userquota/userquota_common.kshlib
+
+function cleanup
+{
+ if datasetexists $snap_fs; then
+ log_must $ZFS destroy $snap_fs
+ fi
+
+ log_must cleanup_quota
+}
+
+log_onexit cleanup
+
+log_assert "Check the zfs userspace with all possible parameters"
+
+set -A params -- "-h" "--help" "-n" "-H" "-p" "-o type,name,used,quota" \
+ "-o name,used,quota" "-o used,quota" "-o used" "-o quota" \
+ "-s type" "-s name" "-s used" "-s quota" \
+ "-S type" "-S name" "-S used" "-S quota" \
+ "-t posixuser" "-t posixgroup" "-t all"
+
+if check_version "5.11" ; then
+ set -A params -- "${params[@]}" "-i" "-t smbuser" "-t smbgroup"
+fi
+
+typeset snap_fs=$QFS@snap
+
+log_must $ZFS set userquota@$QUSER1=100m $QFS
+mkmount_writable $QFS
+log_must user_run $QUSER1 $MKFILE 50m $QFILE
+$SYNC
+
+log_must $ZFS snapshot $snap_fs
+
+for param in "${params[@]}"; do
+ log_must eval "$ZFS userspace $param $QFS >/dev/null 2>&1"
+ log_must eval "$ZFS userspace $param $snap_fs >/dev/null 2>&1"
+done
+
+log_pass "zfs userspace with all possible parameters pass as expect"
+
diff --git a/tests/sys/cddl/zfs/tests/userquota/userspace_002_pos.ksh b/tests/sys/cddl/zfs/tests/userquota/userspace_002_pos.ksh
new file mode 100644
index 000000000000..b2b7df691168
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/userquota/userspace_002_pos.ksh
@@ -0,0 +1,90 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: userspace_002_pos
+#
+# DESCRIPTION:
+# Check the user used size and quota in zfs userspace
+#
+#
+# STRATEGY:
+# 1. set zfs userquota to a fs
+# 2. write some data to the fs with specified user and size
+# 3. use zfs userspace to check the used size and quota size
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2009-04-16)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/userquota/userquota_common.kshlib
+
+function cleanup
+{
+ if datasetexists $snapfs; then
+ log_must $ZFS destroy $snapfs
+ fi
+
+ log_must cleanup_quota
+}
+
+log_onexit cleanup
+
+log_assert "Check the zfs userspace used and quota"
+
+log_must $ZFS set userquota@$QUSER1=100m $QFS
+
+mkmount_writable $QFS
+
+log_must user_run $QUSER1 $MKFILE 50m $QFILE
+$SYNC
+
+typeset snapfs=$QFS@snap
+
+log_must $ZFS snapshot $snapfs
+
+log_must eval "$ZFS userspace $QFS >/dev/null 2>&1"
+log_must eval "$ZFS userspace $snapfs >/dev/null 2>&1"
+
+for fs in "$QFS" "$snapfs"; do
+ log_note "check the quota size in zfs userspace $fs"
+ log_must eval "$ZFS userspace $fs | $GREP $QUSER1 | $GREP 100M"
+
+ log_note "check the user used size in zfs userspace $fs"
+ log_must eval "$ZFS userspace $fs | $GREP $QUSER1 | $GREP 50.0M"
+done
+
+log_pass "Check the zfs userspace used and quota"
diff --git a/tests/sys/cddl/zfs/tests/utils_test/Makefile b/tests/sys/cddl/zfs/tests/utils_test/Makefile
new file mode 100644
index 000000000000..e14bf4586580
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/utils_test/Makefile
@@ -0,0 +1,25 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/utils_test
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= utils_test_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= utils_test_007_pos.ksh
+${PACKAGE}FILES+= utils_test_003_pos.ksh
+${PACKAGE}FILES+= utils_test_006_pos.ksh
+${PACKAGE}FILES+= utils_test_002_pos.ksh
+${PACKAGE}FILES+= utils_test.kshlib
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= utils_test_004_pos.ksh
+${PACKAGE}FILES+= utils_test_008_pos.ksh
+${PACKAGE}FILES+= utils_test.cfg
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= utils_test_009_pos.ksh
+${PACKAGE}FILES+= utils_test_005_pos.ksh
+${PACKAGE}FILES+= utils_test_001_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/utils_test/cleanup.ksh b/tests/sys/cddl/zfs/tests/utils_test/cleanup.ksh
new file mode 100644
index 000000000000..12e893d101d9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/utils_test/cleanup.ksh
@@ -0,0 +1,28 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/utils_test/setup.ksh b/tests/sys/cddl/zfs/tests/utils_test/setup.ksh
new file mode 100644
index 000000000000..de21dbe5dbcf
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/utils_test/setup.ksh
@@ -0,0 +1,28 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/utils_test/utils_test.cfg b/tests/sys/cddl/zfs/tests/utils_test/utils_test.cfg
new file mode 100644
index 000000000000..5e767ecafb26
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/utils_test/utils_test.cfg
@@ -0,0 +1,36 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export TESTSNAP=testsnap${TESTCASE_ID}
+export TESTCLCT=testclct${TESTCASE_ID}
+export TESTFILE=testfile${TESTCASE_ID}
+
+export WRITE_COUNT=20
+export BLOCKSZ=8192
+export DATA=0
+export NUM_FILES=10
+
+export DISK=${DISKS%% *}
diff --git a/tests/sys/cddl/zfs/tests/utils_test/utils_test.kshlib b/tests/sys/cddl/zfs/tests/utils_test/utils_test.kshlib
new file mode 100644
index 000000000000..e4559e522b47
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/utils_test/utils_test.kshlib
@@ -0,0 +1,25 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
diff --git a/tests/sys/cddl/zfs/tests/utils_test/utils_test_001_pos.ksh b/tests/sys/cddl/zfs/tests/utils_test/utils_test_001_pos.ksh
new file mode 100644
index 000000000000..7f5925900266
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/utils_test/utils_test_001_pos.ksh
@@ -0,0 +1,70 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/utils_test/utils_test.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: utils_test_001_pos
+#
+# DESCRIPTION:
+# Ensure that the clri(1M) utility fails on a ZFS file system.
+#
+# STRATEGY:
+# 1. Populate a ZFS directory with a number of files.
+# 2. Run clri against the raw device.
+# 3. Ensure it fails.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ $RM -rf $TESTDIR/*
+}
+
+log_onexit cleanup
+
+log_assert "Ensure that the clri(1M) utility fails on a ZFS file system."
+
+populate_dir $TESTDIR/$TESTFILE $NUM_FILES $WRITE_COUNT $BLOCKSZ $DATA
+
+inode=`$LS -i $TESTDIR/$TESTFILE.0 | $AWK '{print $1}'`
+log_mustnot $CLRI /dev/$DISK $inode
+log_mustnot $CLRI -F zfs /dev/$DISK $inode
+
+log_pass "clri(1M) returned an error as expected."
diff --git a/tests/sys/cddl/zfs/tests/utils_test/utils_test_002_pos.ksh b/tests/sys/cddl/zfs/tests/utils_test/utils_test_002_pos.ksh
new file mode 100644
index 000000000000..313ea55174b8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/utils_test/utils_test_002_pos.ksh
@@ -0,0 +1,76 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/utils_test/utils_test.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: utils_test_002_pos
+#
+# DESCRIPTION:
+# Ensure that the labelit(1M) utility fails on a ZFS file system.
+#
+# STRATEGY:
+# 1. Populate a ZFS file system with some files.
+# 2. Run labelit(1M) against the device.
+# 3. Ensure it fails.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ ismounted $TESTPOOL/$TESTFS
+ (( $? != 0 )) && \
+ log_must $ZFS mount $TESTPOOL/$TESTFS
+
+ $RM -rf $TESTDIR/*
+}
+
+log_onexit cleanup
+
+log_assert "Ensure that the labelit(1M) utility fails on a ZFS file system."
+
+test_requires LABELIT
+
+populate_dir $TESTDIR/$TESTFILE $NUM_FILES $WRITE_COUNT $BLOCKSZ $DATA
+
+log_must $ZFS unmount $TESTDIR
+
+log_mustnot $LABELIT /dev/${DISK}s0 mfiles ${DISK}s0
+
+log_pass "labelit(1M) returned an error as expected."
diff --git a/tests/sys/cddl/zfs/tests/utils_test/utils_test_003_pos.ksh b/tests/sys/cddl/zfs/tests/utils_test/utils_test_003_pos.ksh
new file mode 100644
index 000000000000..dd970d2c10da
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/utils_test/utils_test_003_pos.ksh
@@ -0,0 +1,71 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/utils_test/utils_test.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: utils_test_003_pos
+#
+# DESCRIPTION:
+# Ensure that the fsdb(1M) utility fails on a ZFS file system.
+#
+# STRATEGY:
+# 1. Populate a ZFS directory with a number of files.
+# 2. Run fsdb against the raw device.
+# 3. Ensure it fails.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ $RM -rf $TESTDIR/*
+}
+
+log_onexit cleanup
+
+log_assert "Ensure that the fsdb(1M) utility fails on a ZFS file system."
+
+populate_dir $TESTDIR/$TESTFILE $NUM_FILES $WRITE_COUNT $BLOCKSZ $DATA
+inode_num=`$LS -li $TESTDIR/$TESTFILE.0 | $AWK '{print $1}'`
+[[ -z $inode_num ]] && \
+ log_fail "Failed to determine inode of file: $TESTDIR/$TESTFILE.0"
+
+log_mustnot $ECHO ":inode $inode_num" | $FSDB ${DISK}a
+
+log_pass "fsdb(1M) returned an error as expected."
diff --git a/tests/sys/cddl/zfs/tests/utils_test/utils_test_004_pos.ksh b/tests/sys/cddl/zfs/tests/utils_test/utils_test_004_pos.ksh
new file mode 100644
index 000000000000..d62a2caf23cd
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/utils_test/utils_test_004_pos.ksh
@@ -0,0 +1,68 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/utils_test/utils_test.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: utils_test_004_pos
+#
+# DESCRIPTION:
+# Ensure that the quotaon(1M) utility fails on a ZFS file system.
+#
+# STRATEGY:
+# 1. Enable a quota on a ZFS file system.
+# 2. Run quotaon against the device.
+# 3. Ensure it fails.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ $RM -rf $TESTDIR/*
+}
+
+log_onexit cleanup
+
+log_assert "Ensure that the quotaon(1M) utility fails on a ZFS file system."
+
+log_must $ZFS set quota=1099511627776 $TESTPOOL/$TESTFS
+log_must $TOUCH $TESTDIR/quotas
+log_mustnot $QUOTAON ${DISK}a
+
+log_pass "quotaon(1M) returned an error as expected."
diff --git a/tests/sys/cddl/zfs/tests/utils_test/utils_test_005_pos.ksh b/tests/sys/cddl/zfs/tests/utils_test/utils_test_005_pos.ksh
new file mode 100644
index 000000000000..d872b9de5eda
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/utils_test/utils_test_005_pos.ksh
@@ -0,0 +1,70 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/utils_test/utils_test.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: utils_test_005_pos
+#
+# DESCRIPTION:
+# Ensure that the ff(1M) utility fails on a ZFS file system.
+#
+# STRATEGY:
+# 1. Populate a ZFS file system with some files.
+# 2. Run ff(1M) against the device.
+# 3. Ensure it fails.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ $RM -rf $TESTDIR/*
+}
+
+log_onexit cleanup
+
+test_requires FF
+
+log_assert "Ensure that the ff(1M) utility fails on a ZFS file system."
+
+populate_dir $TESTDIR/$TESTFILE $NUM_FILES $WRITE_COUNT $BLOCKSZ $DATA
+
+log_mustnot $FF -F zfs /dev/${DISK}s0
+
+log_pass "ff(1M) returned an error as expected."
diff --git a/tests/sys/cddl/zfs/tests/utils_test/utils_test_006_pos.ksh b/tests/sys/cddl/zfs/tests/utils_test/utils_test_006_pos.ksh
new file mode 100644
index 000000000000..157d3cb39a5d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/utils_test/utils_test_006_pos.ksh
@@ -0,0 +1,74 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/utils_test/utils_test.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: utils_test_006_pos
+#
+# DESCRIPTION:
+# Ensure that the fsirand(1M) utility fails on a ZFS file system.
+#
+# STRATEGY:
+# 1. Populate a ZFS file system with some files.
+# 2. Run fsirand(1M) against the device.
+# 3. Ensure it fails.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ ismounted $TESTPOOL/$TESTFS
+ (( $? != 0 )) && \
+ log_must $ZFS mount $TESTPOOL/$TESTFS
+
+ $RM -rf $TESTDIR/*
+}
+
+log_onexit cleanup
+
+log_assert "Ensure that the fsirand(1M) utility fails on a ZFS file system."
+
+populate_dir $TESTDIR/$TESTFILE $NUM_FILES $WRITE_COUNT $BLOCKSZ $DATA
+
+log_must $ZFS unmount $TESTDIR
+
+log_mustnot $FSIRAND /dev/${DISK}s0
+
+log_pass "fsirand(1M) returned an error as expected."
diff --git a/tests/sys/cddl/zfs/tests/utils_test/utils_test_007_pos.ksh b/tests/sys/cddl/zfs/tests/utils_test/utils_test_007_pos.ksh
new file mode 100644
index 000000000000..af66ae2cc55f
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/utils_test/utils_test_007_pos.ksh
@@ -0,0 +1,80 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/utils_test/utils_test.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: utils_test_007_pos
+#
+# DESCRIPTION:
+# Ensure that the fstyp(8) utility succeeds on a ZFS file system.
+#
+# STRATEGY:
+# 1. Populate a ZFS file system with some files.
+# 2. Run fstyp(8) against the device.
+# 3. Ensure it passes
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ ismounted $TESTPOOL/$TESTFS
+ (( $? != 0 )) && \
+ log_must $ZFS mount $TESTPOOL/$TESTFS
+
+ $RM -rf $TESTDIR/*
+}
+
+log_onexit cleanup
+
+log_assert "Ensure that the fstyp(8) utility succeeds on a ZFS file system."
+
+test_requires FSTYP
+
+populate_dir $TESTDIR/$TESTFILE $NUM_FILES $WRITE_COUNT $BLOCKSZ $DATA
+
+log_must $ZFS unmount $TESTDIR
+
+log_must $FSTYP -u $DISK
+detected_filesystem=$( $FSTYP -u $DISK )
+if [ "$detected_filesystem" != "zfs" ]; then
+ log_fail "fstyp(8) detected $detected_filesystem instead of zfs"
+fi
+
+log_pass "fstyp(8) returned successfully."
diff --git a/tests/sys/cddl/zfs/tests/utils_test/utils_test_008_pos.ksh b/tests/sys/cddl/zfs/tests/utils_test/utils_test_008_pos.ksh
new file mode 100644
index 000000000000..67450ac72231
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/utils_test/utils_test_008_pos.ksh
@@ -0,0 +1,76 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/utils_test/utils_test.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: utils_test_008_pos
+#
+# DESCRIPTION:
+# Ensure that the ncheck(1M) utility fails on a ZFS file system.
+#
+# STRATEGY:
+# 1. Populate a ZFS file system with some files.
+# 2. Run ncheck(1M) against the device.
+# 3. Ensure it fails.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ ismounted $TESTPOOL/$TESTFS
+ (( $? != 0 )) && \
+ log_must $ZFS mount $TESTPOOL/$TESTFS
+
+ $RM -rf $TESTDIR/*
+}
+
+log_onexit cleanup
+
+test_requires NCHECK
+
+log_assert "Ensure that the ncheck(1M) utility fails on a ZFS file system."
+
+populate_dir $TESTDIR/$TESTFILE $NUM_FILES $WRITE_COUNT $BLOCKSZ $DATA
+
+log_must $ZFS unmount $TESTDIR
+
+log_mustnot $NCHECK /dev/${DISK}s0
+
+log_pass "ncheck(1M) returned an error as expected."
diff --git a/tests/sys/cddl/zfs/tests/utils_test/utils_test_009_pos.ksh b/tests/sys/cddl/zfs/tests/utils_test/utils_test_009_pos.ksh
new file mode 100644
index 000000000000..bf34fd68f04c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/utils_test/utils_test_009_pos.ksh
@@ -0,0 +1,68 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/utils_test/utils_test.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: utils_test_009_pos
+#
+# DESCRIPTION:
+# Ensure that the tunefs(1M) utility fails on a ZFS file system.
+#
+# STRATEGY:
+# 1. Populate a ZFS file system with some files.
+# 2. Run tunefs(1M) against the device.
+# 3. Ensure it fails.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ $RM -rf $TESTDIR/*
+}
+
+log_onexit cleanup
+
+log_assert "Ensure that the tunefs(1M) utility fails on a ZFS file system."
+
+populate_dir $TESTDIR/$TESTFILE $NUM_FILES $WRITE_COUNT $BLOCKSZ $DATA
+
+log_mustnot $TUNEFS -m 80 /dev/${DISK}s0
+
+log_pass "tunefs(1M) returned an error as expected."
diff --git a/tests/sys/cddl/zfs/tests/utils_test/utils_test_test.sh b/tests/sys/cddl/zfs/tests/utils_test/utils_test_test.sh
new file mode 100755
index 000000000000..63093c4d5664
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/utils_test/utils_test_test.sh
@@ -0,0 +1,274 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case utils_test_001_pos cleanup
+utils_test_001_pos_head()
+{
+ atf_set "descr" "Ensure that the clri(1M) utility fails on a ZFS file system."
+ atf_set "require.progs" "ksh93 clri"
+}
+utils_test_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/utils_test.kshlib
+ . $(atf_get_srcdir)/utils_test.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/utils_test_001_pos.ksh || atf_fail "Testcase failed"
+}
+utils_test_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/utils_test.kshlib
+ . $(atf_get_srcdir)/utils_test.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case utils_test_002_pos cleanup
+utils_test_002_pos_head()
+{
+ atf_set "descr" "Ensure that the labelit(1M) utility fails on a ZFS file system."
+ atf_set "require.progs" "ksh93 zfs labelit"
+}
+utils_test_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/utils_test.kshlib
+ . $(atf_get_srcdir)/utils_test.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/utils_test_002_pos.ksh || atf_fail "Testcase failed"
+}
+utils_test_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/utils_test.kshlib
+ . $(atf_get_srcdir)/utils_test.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case utils_test_003_pos cleanup
+utils_test_003_pos_head()
+{
+ atf_set "descr" "Ensure that the fsdb(1M) utility fails on a ZFS file system."
+ atf_set "require.progs" "ksh93 fsdb"
+}
+utils_test_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/utils_test.kshlib
+ . $(atf_get_srcdir)/utils_test.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/utils_test_003_pos.ksh || atf_fail "Testcase failed"
+}
+utils_test_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/utils_test.kshlib
+ . $(atf_get_srcdir)/utils_test.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case utils_test_004_pos cleanup
+utils_test_004_pos_head()
+{
+ atf_set "descr" "Ensure that the quotaon(1M) utility fails on a ZFS file system."
+ atf_set "require.progs" "ksh93 zfs quotaon"
+}
+utils_test_004_pos_body()
+{
+ atf_expect_fail "FreeBSD's quotaon utility exits 0 even when you supply a nonexistent filesystem"
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/utils_test.kshlib
+ . $(atf_get_srcdir)/utils_test.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/utils_test_004_pos.ksh || atf_fail "Testcase failed"
+}
+utils_test_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/utils_test.kshlib
+ . $(atf_get_srcdir)/utils_test.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case utils_test_005_pos cleanup
+utils_test_005_pos_head()
+{
+ atf_set "descr" "Ensure that the ff(1M) utility fails on a ZFS file system."
+ atf_set "require.progs" "ksh93 ff"
+}
+utils_test_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/utils_test.kshlib
+ . $(atf_get_srcdir)/utils_test.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/utils_test_005_pos.ksh || atf_fail "Testcase failed"
+}
+utils_test_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/utils_test.kshlib
+ . $(atf_get_srcdir)/utils_test.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case utils_test_006_pos cleanup
+utils_test_006_pos_head()
+{
+ atf_set "descr" "Ensure that the fsirand(1M) utility fails on a ZFS file system."
+ atf_set "require.progs" "ksh93 zfs fsirand"
+}
+utils_test_006_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/utils_test.kshlib
+ . $(atf_get_srcdir)/utils_test.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/utils_test_006_pos.ksh || atf_fail "Testcase failed"
+}
+utils_test_006_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/utils_test.kshlib
+ . $(atf_get_srcdir)/utils_test.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case utils_test_007_pos cleanup
+utils_test_007_pos_head()
+{
+ atf_set "descr" "Ensure that the fstyp(1M) utility succeeds on a ZFS file system."
+ atf_set "require.progs" "ksh93 zfs fstyp"
+}
+utils_test_007_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/utils_test.kshlib
+ . $(atf_get_srcdir)/utils_test.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/utils_test_007_pos.ksh || atf_fail "Testcase failed"
+}
+utils_test_007_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/utils_test.kshlib
+ . $(atf_get_srcdir)/utils_test.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case utils_test_008_pos cleanup
+utils_test_008_pos_head()
+{
+ atf_set "descr" "Ensure that the ncheck(1M) utility fails on a ZFS file system."
+ atf_set "require.progs" "ksh93 zfs ncheck"
+}
+utils_test_008_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/utils_test.kshlib
+ . $(atf_get_srcdir)/utils_test.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/utils_test_008_pos.ksh || atf_fail "Testcase failed"
+}
+utils_test_008_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/utils_test.kshlib
+ . $(atf_get_srcdir)/utils_test.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case utils_test_009_pos cleanup
+utils_test_009_pos_head()
+{
+ atf_set "descr" "Ensure that the tunefs(1M) utility fails on a ZFS file system."
+ atf_set "require.progs" "ksh93 tunefs"
+}
+utils_test_009_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/utils_test.kshlib
+ . $(atf_get_srcdir)/utils_test.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/utils_test_009_pos.ksh || atf_fail "Testcase failed"
+}
+utils_test_009_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/utils_test.kshlib
+ . $(atf_get_srcdir)/utils_test.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case utils_test_001_pos
+ atf_add_test_case utils_test_002_pos
+ atf_add_test_case utils_test_003_pos
+ atf_add_test_case utils_test_004_pos
+ atf_add_test_case utils_test_005_pos
+ atf_add_test_case utils_test_006_pos
+ atf_add_test_case utils_test_007_pos
+ atf_add_test_case utils_test_008_pos
+ atf_add_test_case utils_test_009_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/write_dirs/Makefile b/tests/sys/cddl/zfs/tests/write_dirs/Makefile
new file mode 100644
index 000000000000..69d11aa30034
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/write_dirs/Makefile
@@ -0,0 +1,17 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/write_dirs
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= write_dirs_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= write_dirs.cfg
+${PACKAGE}FILES+= write_dirs_002_pos.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= write_dirs_001_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/write_dirs/cleanup.ksh b/tests/sys/cddl/zfs/tests/write_dirs/cleanup.ksh
new file mode 100644
index 000000000000..c392b76cc80e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/write_dirs/cleanup.ksh
@@ -0,0 +1,32 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+destroy_pool $TESTPOOL
+log_must wipe_partition_table $DISKS
+log_must $RM -rf $TESTDIR
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/write_dirs/setup.ksh b/tests/sys/cddl/zfs/tests/write_dirs/setup.ksh
new file mode 100644
index 000000000000..be183a07b42e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/write_dirs/setup.ksh
@@ -0,0 +1,36 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+DISK=${DISKS%% *}
+
+wipe_partition_table $DISK
+log_must set_partition $PARTITION "" $SIZE $DISK
+
+default_setup "$DISK"p"$PARTITION"
diff --git a/tests/sys/cddl/zfs/tests/write_dirs/write_dirs.cfg b/tests/sys/cddl/zfs/tests/write_dirs/write_dirs.cfg
new file mode 100644
index 000000000000..1767e6ed7874
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/write_dirs/write_dirs.cfg
@@ -0,0 +1,31 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export TESTFILE=testfile${TESTCASE_ID}
+export STF_TIMEOUT=1200
+
+export SIZE="1gb"
+export PARTITION=1
diff --git a/tests/sys/cddl/zfs/tests/write_dirs/write_dirs_001_pos.ksh b/tests/sys/cddl/zfs/tests/write_dirs/write_dirs_001_pos.ksh
new file mode 100644
index 000000000000..f0af0c7da006
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/write_dirs/write_dirs_001_pos.ksh
@@ -0,0 +1,85 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+###########################################################################
+#
+# __stc_assertion_start
+#
+# ID: write_dirs_001_pos
+#
+# DESCRIPTION:
+# Create as many directories with 50 big files each until the file system
+# is full. The zfs file system should be stable and works well.
+#
+# STRATEGY:
+# 1. Create a pool & dateset
+# 2. Make directories in the zfs file system
+# 3. Create 50 big files in each directories
+# 4. Test case exit when the disk is full.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ for file in `$FIND $TESTDIR -type f`; do
+ $CAT /dev/null > $file
+ done
+ log_must $SYNC
+ log_must $RM -rf $TESTDIR/*
+}
+
+typeset -i retval=0
+log_assert "Creating directories with 50 big files in each, until file system "\
+ "is full."
+
+log_onexit cleanup
+
+typeset -i bytes=8192
+typeset -i num_writes=300000
+typeset -i dirnum=50
+typeset -i filenum=50
+
+fill_fs "" $dirnum $filenum $bytes $num_writes
+retval=$?
+if (( retval == 28 )); then
+ log_note "No space left on device."
+elif (( retval != 0 )); then
+ log_fail "Unexpected exit: $retval"
+fi
+
+log_pass "Write big files in a directory succeeded."
diff --git a/tests/sys/cddl/zfs/tests/write_dirs/write_dirs_002_pos.ksh b/tests/sys/cddl/zfs/tests/write_dirs/write_dirs_002_pos.ksh
new file mode 100644
index 000000000000..e8f4a1510af5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/write_dirs/write_dirs_002_pos.ksh
@@ -0,0 +1,86 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+###########################################################################
+#
+# __stc_assertion_start
+#
+# ID: write_dirs_002_pos
+#
+# DESCRIPTION:
+# Create as many directories with 5000 files each until the file system
+# is full. The zfs file system should be work well and stable.
+#
+# STRATEGY:
+# 1. Create a pool & dateset
+# 2. Make directories in the zfs file system
+# 3. Create 5000 files in each directories
+# 4. Test case exit when the disk is full
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ for file in `$FIND $TESTDIR -type f`; do
+ $CAT /dev/null > $file
+ done
+ log_must $SYNC
+ log_must $RM -rf $TESTDIR/*
+}
+
+typeset -i retval=0
+
+log_assert "Creating directories with 5000 files in each, until file system " \
+ "is full."
+
+log_onexit cleanup
+
+typeset -i bytes=8192
+typeset -i num_writes=20
+typeset -i dirnum=50
+typeset -i filenum=5000
+
+fill_fs "" $dirnum $filenum $bytes $num_writes
+retval=$?
+if (( retval == 28 )); then
+ log_note "No space left on device."
+elif (( retval != 0 )); then
+ log_fail "Unexpected exit: $retval"
+fi
+
+log_pass "Create many files in a directory succeeded."
diff --git a/tests/sys/cddl/zfs/tests/write_dirs/write_dirs_test.sh b/tests/sys/cddl/zfs/tests/write_dirs/write_dirs_test.sh
new file mode 100755
index 000000000000..eaadd572702d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/write_dirs/write_dirs_test.sh
@@ -0,0 +1,82 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case write_dirs_001_pos cleanup
+write_dirs_001_pos_head()
+{
+ atf_set "descr" "Creating directories with 50 big files in each, until file systemis full."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1200
+}
+write_dirs_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/write_dirs.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/write_dirs_001_pos.ksh || atf_fail "Testcase failed"
+}
+write_dirs_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/write_dirs.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case write_dirs_002_pos cleanup
+write_dirs_002_pos_head()
+{
+ atf_set "descr" "Creating directories with 5000 files in each, until file systemis full."
+ atf_set "require.progs" "ksh93 zpool"
+ atf_set "timeout" 1200
+}
+write_dirs_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/write_dirs.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/write_dirs_002_pos.ksh || atf_fail "Testcase failed"
+}
+write_dirs_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/write_dirs.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case write_dirs_001_pos
+ atf_add_test_case write_dirs_002_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/xattr/Makefile b/tests/sys/cddl/zfs/tests/xattr/Makefile
new file mode 100644
index 000000000000..a1f76ef79d72
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/xattr/Makefile
@@ -0,0 +1,29 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/xattr
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= xattr_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= xattr_002_neg.ksh
+${PACKAGE}FILES+= xattr_012_pos.ksh
+${PACKAGE}FILES+= xattr_013_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= xattr_003_neg.ksh
+${PACKAGE}FILES+= xattr_007_neg.ksh
+${PACKAGE}FILES+= xattr_common.kshlib
+${PACKAGE}FILES+= xattr.cfg
+${PACKAGE}FILES+= xattr_006_pos.ksh
+${PACKAGE}FILES+= xattr_011_pos.ksh
+${PACKAGE}FILES+= xattr_010_neg.ksh
+${PACKAGE}FILES+= xattr_009_neg.ksh
+${PACKAGE}FILES+= xattr_008_pos.ksh
+${PACKAGE}FILES+= xattr_004_pos.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= xattr_005_pos.ksh
+${PACKAGE}FILES+= xattr_001_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/xattr/cleanup.ksh b/tests/sys/cddl/zfs/tests/xattr/cleanup.ksh
new file mode 100644
index 000000000000..154eaaf0a6e5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/xattr/cleanup.ksh
@@ -0,0 +1,38 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+. ${STF_SUITE}/tests/xattr/xattr.cfg
+
+del_user $ZFS_USER
+
+if [ "${USES_NIS}" == "true" ]
+then
+ $SVCADM enable svc:/network/nis/client:default
+fi
+
+$RM $TMPDIR/zfs-xattr-test-user.txt
+$RM $TMPDIR/zfs-xattr-test-nis.txt
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/xattr/setup.ksh b/tests/sys/cddl/zfs/tests/xattr/setup.ksh
new file mode 100644
index 000000000000..318147c54ab6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/xattr/setup.ksh
@@ -0,0 +1,57 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+. ${STF_SUITE}/tests/xattr/xattr.cfg
+
+# if we're running NIS, turn it off until we clean up
+# (it can cause useradd to take a long time, hitting our TIMEOUT)
+$SVCS svc:/network/nis/client:default | $GREP online > /dev/null
+if [ $? -eq 0 ]
+then
+ $SVCADM disable -t svc:/network/nis/client:default
+ USES_NIS=true
+fi
+
+# Make sure we use a brand new user for this
+while [ -z "${FOUND}" ]
+do
+ COUNT=0
+ USER_EXISTS=$( $GREP $ZFS_USER /etc/passwd )
+ if [ ! -z "${USER_EXISTS}" ]
+ then
+ ZFS_USER="${ZFS_USER}${COUNT}"
+ COUNT=$(( $COUNT + 1 ))
+ else
+ FOUND="true"
+ fi
+done
+
+log_must add_user $ZFS_GROUP $ZFS_USER
+
+$ECHO $ZFS_USER > $TMPDIR/zfs-xattr-test-user.txt
+$ECHO $USES_NIS > $TMPDIR/zfs-xattr-test-nis.txt
+
+DISK=${DISKS%% *}
+default_setup $DISK
diff --git a/tests/sys/cddl/zfs/tests/xattr/xattr.cfg b/tests/sys/cddl/zfs/tests/xattr/xattr.cfg
new file mode 100644
index 000000000000..e3915f210053
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/xattr/xattr.cfg
@@ -0,0 +1,49 @@
+#!/bin/ksh -p
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+# a variable we set during setup to indicate whether we need to
+# re-enable the nis/client during cleanup.
+USES_NIS=false
+
+# A username we can create for the course of the zfs xattr
+# testing (modified by setup if it already exists)
+ZFS_USER=zxtr
+ZFS_GROUP=staff
+
+# These variables may also have been tweaked by the setup script
+# Let's import them if necessary.
+if [ -f $TMPDIR/zfs-xattr-test-nis.txt ]
+then
+ USES_NIS=$($CAT $TMPDIR/zfs-xattr-test-nis.txt)
+fi
+
+if [ -f $TMPDIR/zfs-xattr-test-user.txt ]
+then
+ ZFS_USER=$($CAT $TMPDIR/zfs-xattr-test-user.txt)
+fi
+
+export ZFS_USER
+export ZFS_GROUP
+export USES_NIS
diff --git a/tests/sys/cddl/zfs/tests/xattr/xattr_001_pos.ksh b/tests/sys/cddl/zfs/tests/xattr/xattr_001_pos.ksh
new file mode 100644
index 000000000000..6e2104d5dda1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/xattr/xattr_001_pos.ksh
@@ -0,0 +1,72 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/xattr/xattr_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: xattr_001_pos
+#
+# DESCRIPTION:
+#
+# Creating, reading and writing xattrs on ZFS filesystems works as expected
+#
+# STRATEGY:
+# 1. Create an xattr on a ZFS-based file using runat
+# 2. Read an empty xattr directory
+# 3. Write the xattr using runat and cat
+# 3. Read the xattr using runat
+# 4. Delete the xattr
+# 5. List the xattr namespace successfully, checking for deletion
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-12-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function cleanup {
+
+ if [ -f $TESTDIR/myfile.${TESTCASE_ID} ]
+ then
+ log_must $RM $TESTDIR/myfile.${TESTCASE_ID}
+ fi
+}
+
+log_assert "Create/read/write/append of xattrs works"
+log_onexit cleanup
+
+log_must $TOUCH $TESTDIR/myfile.${TESTCASE_ID}
+create_xattr $TESTDIR/myfile.${TESTCASE_ID} passwd /etc/passwd
+verify_write_xattr $TESTDIR/myfile.${TESTCASE_ID} passwd
+delete_xattr $TESTDIR/myfile.${TESTCASE_ID} passwd
+
+log_pass "Create/read/write of xattrs works"
diff --git a/tests/sys/cddl/zfs/tests/xattr/xattr_002_neg.ksh b/tests/sys/cddl/zfs/tests/xattr/xattr_002_neg.ksh
new file mode 100644
index 000000000000..ee46b61d011c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/xattr/xattr_002_neg.ksh
@@ -0,0 +1,65 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/xattr/xattr_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: xattr_002_neg
+#
+# DESCRIPTION:
+#
+# Trying to read a non-existent xattr should fail.
+#
+# STRATEGY:
+# 1. Create a file
+# 2. Try to read a non-existent xattr, check that an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-12-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function cleanup {
+
+ log_must $RM $TESTDIR/myfile.${TESTCASE_ID}
+
+}
+
+log_assert "A read of a non-existent xattr fails"
+log_onexit cleanup
+
+# create a file
+log_must $TOUCH $TESTDIR/myfile.${TESTCASE_ID}
+log_mustnot eval "$CAT $TESTDIR/myfile.${TESTCASE_ID} not-here.txt > /dev/null 2>&1"
+
+log_pass "A read of a non-existent xattr fails"
diff --git a/tests/sys/cddl/zfs/tests/xattr/xattr_003_neg.ksh b/tests/sys/cddl/zfs/tests/xattr/xattr_003_neg.ksh
new file mode 100644
index 000000000000..4febb8e2b5c1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/xattr/xattr_003_neg.ksh
@@ -0,0 +1,76 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/xattr/xattr_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: xattr_003_neg
+#
+# DESCRIPTION:
+#
+# Attempting to read an xattr on a file for which we have no permissions
+# should fail.
+#
+# STRATEGY:
+# 1. Create a file, and set an with an xattr
+# 2. Set the octal file permissions to 000 on the file.
+# 3. Check that we're unable to read the xattr as a non-root user
+# 4. Check that we're unable to write an xattr as a non-root user
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-12-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function cleanup {
+
+ log_must $RM $TESTDIR/myfile.${TESTCASE_ID}
+
+}
+
+log_assert "read/write xattr on a file with no permissions fails"
+log_onexit cleanup
+
+test_requires RUNAT
+
+log_must $TOUCH $TESTDIR/myfile.${TESTCASE_ID}
+create_xattr $TESTDIR/myfile.${TESTCASE_ID} passwd /etc/passwd
+
+log_must $CHMOD 000 $TESTDIR/myfile.${TESTCASE_ID}
+log_mustnot $RUNWATTR -u $ZFS_USER -g $ZFS_GROUP \
+ "$RUNAT $TESTDIR/myfile.${TESTCASE_ID} $CAT passwd"
+
+log_mustnot $RUNWATTR -u $ZFS_USER -g $ZFS_GROUP \
+ "$RUNAT $TESTDIR/myfile.${TESTCASE_ID} $CP /etc/passwd ."
+
+log_pass "read/write xattr on a file with no permissions fails"
diff --git a/tests/sys/cddl/zfs/tests/xattr/xattr_004_pos.ksh b/tests/sys/cddl/zfs/tests/xattr/xattr_004_pos.ksh
new file mode 100644
index 000000000000..26489132c016
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/xattr/xattr_004_pos.ksh
@@ -0,0 +1,97 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/xattr/xattr_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: xattr_004_pos
+#
+# DESCRIPTION:
+#
+# Creating files on ufs and tmpfs, and copying those files to ZFS with
+# appropriate cp flags, the xattrs will still be readable.
+#
+# STRATEGY:
+# 1. Create files in ufs and tmpfs with xattrs
+# 2. Copy those files to zfs
+# 3. Ensure the xattrs can be read and written
+# 4. Do the same in reverse.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-12-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+# we need to be able to create zvols to hold our test
+# ufs filesystem.
+verify_runnable "global"
+
+# Make sure we clean up properly
+function cleanup {
+
+ if [ $( ismounted $TMPDIR/ufs.${TESTCASE_ID} ufs ) ]
+ then
+ log_must $UMOUNT $TMPDIR/ufs.${TESTCASE_ID}
+ log_must $RM -rf $TMPDIR/ufs.${TESTCASE_ID}
+ fi
+}
+
+log_assert "Files from ufs,tmpfs with xattrs copied to zfs retain xattr info."
+log_onexit cleanup
+
+test_requires RUNAT
+
+# Create a UFS file system that we can work in
+log_must $ZFS create -V128m $TESTPOOL/$TESTFS/zvol
+log_must eval "$ECHO y | $NEWFS /dev/zvol/$TESTPOOL/$TESTFS/zvol > /dev/null 2>&1"
+
+log_must $MKDIR $TMPDIR/ufs.${TESTCASE_ID}
+log_must $MOUNT /dev/zvol/$TESTPOOL/$TESTFS/zvol $TMPDIR/ufs.${TESTCASE_ID}
+
+# Create files in ufs and tmpfs, and set some xattrs on them.
+log_must $TOUCH $TMPDIR/ufs.${TESTCASE_ID}/ufs-file.${TESTCASE_ID}
+log_must $TOUCH $TMPDIR/tmpfs-file.${TESTCASE_ID}
+
+log_must $RUNAT $TMPDIR/ufs.${TESTCASE_ID}/ufs-file.${TESTCASE_ID} $CP /etc/passwd .
+log_must $RUNAT $TMPDIR/tmpfs-file.${TESTCASE_ID} $CP /etc/group .
+
+# copy those files to ZFS
+log_must $CP -@ $TMPDIR/ufs.${TESTCASE_ID}/ufs-file.${TESTCASE_ID} $TESTDIR
+log_must $CP -@ $TMPDIR/tmpfs-file.${TESTCASE_ID} $TESTDIR
+
+# ensure the xattr information has been copied correctly
+log_must $RUNAT $TESTDIR/ufs-file.${TESTCASE_ID} $DIFF passwd /etc/passwd
+log_must $RUNAT $TESTDIR/tmpfs-file.${TESTCASE_ID} $DIFF group /etc/group
+
+log_must $UMOUNT $TMPDIR/ufs.${TESTCASE_ID}
+log_pass "Files from ufs,tmpfs with xattrs copied to zfs retain xattr info."
diff --git a/tests/sys/cddl/zfs/tests/xattr/xattr_005_pos.ksh b/tests/sys/cddl/zfs/tests/xattr/xattr_005_pos.ksh
new file mode 100644
index 000000000000..b40b91cbbb4b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/xattr/xattr_005_pos.ksh
@@ -0,0 +1,87 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/xattr/xattr_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: xattr_005_pos
+#
+# DESCRIPTION:
+# read/write/create/delete xattr on a clone filesystem
+#
+#
+# STRATEGY:
+# 1. Create an xattr on a filesystem
+# 2. Snapshot the filesystem and clone it
+# 3. Verify the xattr can still be read, written, deleted
+# 4. Verify we can create new xattrs on new files created on the clone
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-12-13)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function cleanup {
+
+ log_must $ZFS destroy $TESTPOOL/$TESTFS/clone
+ log_must $ZFS destroy $TESTPOOL/$TESTFS@snapshot1
+ log_must $RM $TESTDIR/myfile.${TESTCASE_ID}
+}
+
+log_assert "read/write/create/delete xattr on a clone filesystem"
+log_onexit cleanup
+
+# create a file, and an xattr on it
+log_must $TOUCH $TESTDIR/myfile.${TESTCASE_ID}
+create_xattr $TESTDIR/myfile.${TESTCASE_ID} passwd /etc/passwd
+
+# snapshot & clone the filesystem
+log_must $ZFS snapshot $TESTPOOL/$TESTFS@snapshot1
+log_must $ZFS clone $TESTPOOL/$TESTFS@snapshot1 $TESTPOOL/$TESTFS/clone
+log_must $ZFS set mountpoint=$TESTDIR/clone $TESTPOOL/$TESTFS/clone
+
+# check for the xattrs on the clone
+verify_xattr $TESTDIR/clone/myfile.${TESTCASE_ID} passwd /etc/passwd
+
+# check we can create xattrs on the clone
+create_xattr $TESTDIR/clone/myfile.${TESTCASE_ID} foo /etc/passwd
+delete_xattr $TESTDIR/clone/myfile.${TESTCASE_ID} foo
+
+# delete the original dataset xattr
+delete_xattr $TESTDIR/myfile.${TESTCASE_ID} passwd
+
+# verify it's still there on the clone
+verify_xattr $TESTDIR/clone/myfile.${TESTCASE_ID} passwd /etc/passwd
+delete_xattr $TESTDIR/clone/myfile.${TESTCASE_ID} passwd
+
+log_pass "read/write/create/delete xattr on a clone filesystem"
diff --git a/tests/sys/cddl/zfs/tests/xattr/xattr_006_pos.ksh b/tests/sys/cddl/zfs/tests/xattr/xattr_006_pos.ksh
new file mode 100644
index 000000000000..05122f967456
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/xattr/xattr_006_pos.ksh
@@ -0,0 +1,72 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/xattr/xattr_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: xattr_006_pos
+#
+# DESCRIPTION:
+# Xattrs present on a file in a snapshot should be visible.
+#
+# STRATEGY:
+# 1. Create a file and give it an xattr
+# 2. Take a snapshot of the filesystem
+# 3. Verify that we can take a snapshot of it.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-12-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function cleanup {
+
+ log_must $ZFS destroy $TESTPOOL/$TESTFS@snap
+ log_must $RM $TESTDIR/myfile.${TESTCASE_ID}
+
+}
+
+log_assert "read xattr on a snapshot"
+log_onexit cleanup
+
+# create a file, and an xattr on it
+log_must $TOUCH $TESTDIR/myfile.${TESTCASE_ID}
+create_xattr $TESTDIR/myfile.${TESTCASE_ID} passwd /etc/passwd
+
+# snapshot the filesystem
+log_must $ZFS snapshot $TESTPOOL/$TESTFS@snap
+
+# check for the xattr on the snapshot
+verify_xattr $TESTDIR/$(get_snapdir_name)/snap/myfile.${TESTCASE_ID} passwd /etc/passwd
+
+log_pass "read xattr on a snapshot"
diff --git a/tests/sys/cddl/zfs/tests/xattr/xattr_007_neg.ksh b/tests/sys/cddl/zfs/tests/xattr/xattr_007_neg.ksh
new file mode 100644
index 000000000000..06d44e5f4860
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/xattr/xattr_007_neg.ksh
@@ -0,0 +1,97 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/xattr/xattr_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: xattr_007_neg
+#
+# DESCRIPTION:
+# Creating and writing xattrs on files in snapshot directories fails. Also,
+# we shouldn't be able to list the xattrs of files in snapshots who didn't have
+# xattrs when the snapshot was created (the xattr namespace wouldn't have been
+# created yet, and snapshots are read-only) See fsattr(5) for more details.
+#
+# STRATEGY:
+# 1. Create a file and add an xattr to it.
+# 2. Create another file, but don't add an xattr to it.
+# 3. Snapshot the filesystem
+# 4. Verify we're unable to alter the xattr on the first file
+# 5. Verify we're unable to list the xattrs on the second file
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-12-13)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function cleanup {
+ log_must $ZFS destroy $TESTPOOL/$TESTFS@snap
+ log_must $RM $TESTDIR/myfile2.${TESTCASE_ID}
+ log_must $RM $TESTDIR/myfile.${TESTCASE_ID}
+ log_must $RM $TMPDIR/output.${TESTCASE_ID}
+ [[ -e $TMPDIR/expected_output.${TESTCASE_ID} ]] && log_must $RM \
+ $TMPDIR/expected_output.${TESTCASE_ID}
+
+}
+
+log_assert "create/write xattr on a snapshot fails"
+log_onexit cleanup
+
+# create a file, and an xattr on it
+log_must $TOUCH $TESTDIR/myfile.${TESTCASE_ID}
+create_xattr $TESTDIR/myfile.${TESTCASE_ID} passwd /etc/passwd
+
+# create another file that doesn't have an xattr
+log_must $TOUCH $TESTDIR/myfile2.${TESTCASE_ID}
+
+# snapshot the filesystem
+log_must $ZFS snapshot $TESTPOOL/$TESTFS@snap
+
+# we shouldn't be able to alter the first file's xattr
+log_mustnot eval " $RUNAT $TESTDIR/$(get_snapdir_name)/snap/myfile.${TESTCASE_ID} \
+ $CP /etc/passwd . >$TMPDIR/output.${TESTCASE_ID} 2>&1"
+log_must $GREP -i Read-only $TMPDIR/output.${TESTCASE_ID}
+
+if check_version "5.10"
+then
+ # we shouldn't be able to list xattrs at all on the second file
+ log_mustnot eval " $RUNAT $TESTDIR/$(get_snapdir_name)/snap/myfile2.${TESTCASE_ID} \
+ $LS >$TMPDIR/output.${TESTCASE_ID} 2>&1"
+ log_must $GREP -i Read-only $TMPDIR/output.${TESTCASE_ID}
+else
+ log_must eval "$RUNAT $TESTDIR/$(get_snapdir_name)/snap/myfile2.${TESTCASE_ID} \
+ $LS >$TMPDIR/output.${TESTCASE_ID} 2>&1"
+ create_expected_output $TMPDIR/expected_output.${TESTCASE_ID} SUNWattr_ro \
+ SUNWattr_rw
+ log_must $DIFF $TMPDIR/output.${TESTCASE_ID} $TMPDIR/expected_output.${TESTCASE_ID}
+fi
+log_pass "create/write xattr on a snapshot fails"
diff --git a/tests/sys/cddl/zfs/tests/xattr/xattr_008_pos.ksh b/tests/sys/cddl/zfs/tests/xattr/xattr_008_pos.ksh
new file mode 100644
index 000000000000..f629b398d7fe
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/xattr/xattr_008_pos.ksh
@@ -0,0 +1,107 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/xattr/xattr_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: xattr_008_pos
+#
+# DESCRIPTION:
+# We verify that the special . and .. dirs work as expected for xattrs.
+#
+# STRATEGY:
+# 1. Create a file and an xattr on that file
+# 2. List the . directory, verifying the output
+# 3. Verify we're unable to list the ../ directory
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-12-05)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function cleanup {
+ typeset file
+
+ for file in $TMPDIR/output.${TESTCASE_ID} $TMPDIR/expected-output.${TESTCASE_ID} \
+ $TESTDIR/myfile.${TESTCASE_ID} ; do
+ log_must $RM -f $file
+ done
+}
+
+log_assert "special . and .. dirs work as expected for xattrs"
+log_onexit cleanup
+
+test_requires RUNAT
+
+# create a file, and an xattr on it
+log_must $TOUCH $TESTDIR/myfile.${TESTCASE_ID}
+create_xattr $TESTDIR/myfile.${TESTCASE_ID} passwd /etc/passwd
+
+if check_version "5.10"
+then
+ # listing the directory . should show one file
+ OUTPUT=$($RUNAT $TESTDIR/myfile.${TESTCASE_ID} $LS .)
+ if [ "$OUTPUT" != "passwd" ]
+ then
+ log_fail "Listing the . directory doesn't show \"passwd\" as expected."
+ fi
+ # list the directory . long form
+ log_must eval "$RUNAT $TESTDIR/myfile.${TESTCASE_ID} $LS -a . > $TMPDIR/output.${TESTCASE_ID}"
+ # create a file that should be the same as the command above
+ create_expected_output $TMPDIR/expected-output.${TESTCASE_ID} . .. passwd
+ # compare them
+ log_must $DIFF $TMPDIR/output.${TESTCASE_ID} $TMPDIR/expected-output.${TESTCASE_ID}
+else
+ # listing the directory .
+ log_must eval "$RUNAT $TESTDIR/myfile.${TESTCASE_ID} $LS . > $TMPDIR/output.${TESTCASE_ID}"
+ create_expected_output $TMPDIR/expected-output.${TESTCASE_ID} \
+ SUNWattr_ro SUNWattr_rw passwd
+ log_must $DIFF $TMPDIR/output.${TESTCASE_ID} $TMPDIR/expected-output.${TESTCASE_ID}
+ # list the directory . long form
+ log_must eval "$RUNAT $TESTDIR/myfile.${TESTCASE_ID} $LS -a . > $TMPDIR/output.${TESTCASE_ID}"
+ create_expected_output $TMPDIR/expected-output.${TESTCASE_ID} . .. \
+ SUNWattr_ro SUNWattr_rw passwd
+ log_must $DIFF $TMPDIR/output.${TESTCASE_ID} $TMPDIR/expected-output.${TESTCASE_ID}
+fi
+
+# list the directory .. expecting one file
+OUTPUT=$($RUNAT $TESTDIR/myfile.${TESTCASE_ID} $LS ..)
+if [ "$OUTPUT" != ".." ]
+then
+ log_fail "Listing the .. directory doesn't show \"..\" as expected."
+fi
+
+# verify we can't list ../
+log_mustnot eval "$RUNAT $TESTDIR/myfile.${TESTCASE_ID} $LS ../ > /dev/null 2>&1"
+
+log_pass "special . and .. dirs work as expected for xattrs"
diff --git a/tests/sys/cddl/zfs/tests/xattr/xattr_009_neg.ksh b/tests/sys/cddl/zfs/tests/xattr/xattr_009_neg.ksh
new file mode 100644
index 000000000000..a1f2abdd8c48
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/xattr/xattr_009_neg.ksh
@@ -0,0 +1,73 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/xattr/xattr_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: xattr_009_neg
+#
+# DESCRIPTION:
+# links between xattr and normal file namespace fail
+#
+# STRATEGY:
+# 1. Create a file and add an xattr to it (to ensure the namespace exists)
+# 2. Verify we're unable to create a symbolic link
+# 3. Verify we're unable to create a hard link
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-12-13)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function cleanup {
+
+ log_must $RM $TESTDIR/myfile.${TESTCASE_ID}
+
+}
+
+log_assert "links between xattr and normal file namespace fail"
+log_onexit cleanup
+
+test_requires RUNAT
+
+# create a file, and an xattr on it
+log_must $TOUCH $TESTDIR/myfile.${TESTCASE_ID}
+create_xattr $TESTDIR/myfile.${TESTCASE_ID} passwd /etc/passwd
+
+# Try to create a soft link from the xattr namespace to the default namespace
+log_mustnot $RUNAT $TESTDIR/myfile.${TESTCASE_ID} $LN -s /etc/passwd foo
+
+# Try to create a hard link from the xattr namespace to the default namespace
+log_mustnot $RUNAT $TESTDIR/myfile.${TESTCASE_ID} $LN /etc/passwd foo
+
+log_pass "links between xattr and normal file namespace fail"
diff --git a/tests/sys/cddl/zfs/tests/xattr/xattr_010_neg.ksh b/tests/sys/cddl/zfs/tests/xattr/xattr_010_neg.ksh
new file mode 100644
index 000000000000..ad259b4d6e34
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/xattr/xattr_010_neg.ksh
@@ -0,0 +1,77 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/xattr/xattr_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: xattr_010_neg
+#
+# DESCRIPTION:
+# Verify that mkdir and various mknods fail inside the xattr namespace
+#
+# STRATEGY:
+# 1. Create a file and add an xattr to it (to ensure the namespace exists)
+# 2. Verify that mkdir fails inside the xattr namespace
+# 3. Verify that various mknods fails inside the xattr namespace
+#
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-12-13)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function cleanup {
+
+ log_must $RM $TESTDIR/myfile.${TESTCASE_ID}
+}
+
+log_assert "mkdir, mknod fail"
+log_onexit cleanup
+
+test_requires RUNAT
+
+# create a file, and an xattr on it
+log_must $TOUCH $TESTDIR/myfile.${TESTCASE_ID}
+create_xattr $TESTDIR/myfile.${TESTCASE_ID} passwd /etc/passwd
+
+# Try to create directory in the xattr namespace
+log_mustnot $RUNAT $TESTDIR/myfile.${TESTCASE_ID} $MKDIR foo
+
+# Try to create a range of different filetypes in the xattr namespace
+log_mustnot $RUNAT $TESTDIR/myfile.${TESTCASE_ID} $MKNOD block b 888 888
+
+log_mustnot $RUNAT $TESTDIR/myfile.${TESTCASE_ID} $MKNOD char c
+
+log_mustnot $RUNAT $TESTDIR/myfile.${TESTCASE_ID} $MKNOD fifo p
+
+log_pass "mkdir, mknod fail"
diff --git a/tests/sys/cddl/zfs/tests/xattr/xattr_011_pos.ksh b/tests/sys/cddl/zfs/tests/xattr/xattr_011_pos.ksh
new file mode 100644
index 000000000000..dfb064843271
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/xattr/xattr_011_pos.ksh
@@ -0,0 +1,198 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/xattr/xattr_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: xattr_011_pos
+#
+# DESCRIPTION:
+#
+# Basic applications work with xattrs: cpio cp find mv pax tar
+#
+# STRATEGY:
+# 1. For each application
+# 2. Create an xattr and archive/move/copy/find files with xattr support
+# 3. Also check that when appropriate flag is not used, the xattr
+# doesn't get copied
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-12-15)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function cleanup {
+
+ log_must $RM $TESTDIR/myfile.${TESTCASE_ID}
+}
+
+log_assert "Basic applications work with xattrs: cpio cp find mv pax tar"
+log_onexit cleanup
+
+test_requires RUNAT
+
+# Create a file, and set an xattr on it. This file is used in several of the
+# test scenarios below.
+log_must $TOUCH $TESTDIR/myfile.${TESTCASE_ID}
+create_xattr $TESTDIR/myfile.${TESTCASE_ID} passwd /etc/passwd
+
+
+# For the archive applications below (tar, cpio, pax)
+# we create two archives, one with xattrs, one without
+# and try various cpio options extracting the archives
+# with and without xattr support, checking for correct behaviour
+
+
+log_note "Checking cpio"
+log_must $TOUCH $TESTDIR/cpio.${TESTCASE_ID}
+create_xattr $TESTDIR/cpio.${TESTCASE_ID} passwd /etc/passwd
+$ECHO $TESTDIR/cpio.${TESTCASE_ID} | $CPIO -o@ > $TMPDIR/xattr.${TESTCASE_ID}.cpio
+$ECHO $TESTDIR/cpio.${TESTCASE_ID} | $CPIO -o > $TMPDIR/noxattr.${TESTCASE_ID}.cpio
+
+# we should have no xattr here
+log_must $CPIO -iu < $TMPDIR/xattr.${TESTCASE_ID}.cpio
+log_mustnot eval "$RUNAT $TESTDIR/cpio.${TESTCASE_ID} $CAT passwd > /dev/null 2>&1"
+
+# we should have an xattr here
+log_must $CPIO -iu@ < $TMPDIR/xattr.${TESTCASE_ID}.cpio
+log_must eval "$RUNAT $TESTDIR/cpio.${TESTCASE_ID} $CAT passwd > /dev/null 2>&1"
+
+# we should have no xattr here
+log_must $CPIO -iu < $TMPDIR/noxattr.${TESTCASE_ID}.cpio
+log_mustnot eval "$RUNAT $TESTDIR/cpio.${TESTCASE_ID} $CAT passwd > /dev/null 2>&1"
+
+# we should have no xattr here
+log_must $CPIO -iu@ < $TMPDIR/noxattr.${TESTCASE_ID}.cpio
+log_mustnot eval "$RUNAT $TESTDIR/cpio.${TESTCASE_ID} $CAT passwd > /dev/null 2>&1"
+log_must $RM $TESTDIR/cpio.${TESTCASE_ID} $TMPDIR/xattr.${TESTCASE_ID}.cpio $TMPDIR/noxattr.${TESTCASE_ID}.cpio
+
+
+
+log_note "Checking cp"
+# check that with the right flag, the xattr is preserved
+log_must $CP -@ $TESTDIR/myfile.${TESTCASE_ID} $TESTDIR/myfile2.${TESTCASE_ID}
+compare_xattrs $TESTDIR/myfile.${TESTCASE_ID} $TESTDIR/myfile2.${TESTCASE_ID} passwd
+log_must $RM $TESTDIR/myfile2.${TESTCASE_ID}
+
+# without the right flag, there should be no xattr
+log_must $CP $TESTDIR/myfile.${TESTCASE_ID} $TESTDIR/myfile2.${TESTCASE_ID}
+log_mustnot eval "$RUNAT $TESTDIR/myfile2.${TESTCASE_ID} $LS passwd > /dev/null 2>&1"
+log_must $RM $TESTDIR/myfile2.${TESTCASE_ID}
+
+
+
+log_note "Checking find"
+# create a file without xattrs, and check that find -xattr only finds
+# our test file that has an xattr.
+log_must $MKDIR $TESTDIR/noxattrs
+log_must $TOUCH $TESTDIR/noxattrs/no-xattr
+
+$FIND $TESTDIR -xattr | $GREP myfile.${TESTCASE_ID}
+if [ $? -ne 0 ]
+then
+ log_fail "find -xattr didn't find our file that had an xattr."
+fi
+$FIND $TESTDIR -xattr | $GREP no-xattr
+if [ $? -eq 0 ]
+then
+ log_fail "find -xattr found a file that didn't have an xattr."
+fi
+log_must $RM -rf $TESTDIR/noxattrs
+
+
+
+log_note "Checking mv"
+# mv doesn't have any flags to preserve/ommit xattrs - they're
+# always moved.
+log_must $TOUCH $TESTDIR/mvfile.${TESTCASE_ID}
+create_xattr $TESTDIR/mvfile.${TESTCASE_ID} passwd /etc/passwd
+log_must $MV $TESTDIR/mvfile.${TESTCASE_ID} $TESTDIR/mvfile2.${TESTCASE_ID}
+verify_xattr $TESTDIR/mvfile2.${TESTCASE_ID} passwd /etc/passwd
+log_must $RM $TESTDIR/mvfile2.${TESTCASE_ID}
+
+
+log_note "Checking pax"
+log_must $TOUCH $TESTDIR/pax.${TESTCASE_ID}
+create_xattr $TESTDIR/pax.${TESTCASE_ID} passwd /etc/passwd
+log_must $PAX -w -f $TESTDIR/noxattr.pax $TESTDIR/pax.${TESTCASE_ID}
+log_must $PAX -w@ -f $TESTDIR/xattr.pax $TESTDIR/pax.${TESTCASE_ID}
+log_must $RM $TESTDIR/pax.${TESTCASE_ID}
+
+# we should have no xattr here
+log_must $PAX -r -f $TESTDIR/noxattr.pax
+log_mustnot eval "$RUNAT $TESTDIR/pax.${TESTCASE_ID} $CAT passwd > /dev/null 2>&1"
+log_must $RM $TESTDIR/pax.${TESTCASE_ID}
+
+# we should have no xattr here
+log_must $PAX -r@ -f $TESTDIR/noxattr.pax
+log_mustnot eval "$RUNAT $TESTDIR/pax.${TESTCASE_ID} $CAT passwd > /dev/null 2>&1"
+log_must $RM $TESTDIR/pax.${TESTCASE_ID}
+
+
+# we should have an xattr here
+log_must $PAX -r@ -f $TESTDIR/xattr.pax
+verify_xattr $TESTDIR/pax.${TESTCASE_ID} passwd /etc/passwd
+log_must $RM $TESTDIR/pax.${TESTCASE_ID}
+
+# we should have no xattr here
+log_must $PAX -r -f $TESTDIR/xattr.pax
+log_mustnot eval "$RUNAT $TESTDIR/pax.${TESTCASE_ID} $CAT passwd > /dev/null 2>&1"
+log_must $RM $TESTDIR/pax.${TESTCASE_ID} $TESTDIR/noxattr.pax $TESTDIR/xattr.pax
+
+
+log_note "Checking tar"
+log_must $TOUCH $TESTDIR/tar.${TESTCASE_ID}
+create_xattr $TESTDIR/tar.${TESTCASE_ID} passwd /etc/passwd
+log_must $TAR cf $TESTDIR/noxattr.tar $TESTDIR/tar.${TESTCASE_ID}
+log_must $TAR c@f $TESTDIR/xattr.tar $TESTDIR/tar.${TESTCASE_ID}
+log_must $RM $TESTDIR/tar.${TESTCASE_ID}
+
+# we should have no xattr here
+log_must $TAR xf $TESTDIR/xattr.tar
+log_mustnot eval "$RUNAT $TESTDIR/tar.${TESTCASE_ID} $CAT passwd > /dev/null 2>&1"
+log_must $RM $TESTDIR/tar.${TESTCASE_ID}
+
+# we should have an xattr here
+log_must $TAR x@f $TESTDIR/xattr.tar
+verify_xattr $TESTDIR/tar.${TESTCASE_ID} passwd /etc/passwd
+log_must $RM $TESTDIR/tar.${TESTCASE_ID}
+
+# we should have no xattr here
+log_must $TAR xf $TESTDIR/noxattr.tar
+log_mustnot eval "$RUNAT $TESTDIR/tar.${TESTCASE_ID} $CAT passwd > /dev/null 2>&1"
+log_must $RM $TESTDIR/tar.${TESTCASE_ID}
+
+# we should have no xattr here
+log_must $TAR x@f $TESTDIR/noxattr.tar
+log_mustnot eval "$RUNAT $TESTDIR/tar.${TESTCASE_ID} $CAT passwd > /dev/null 2>&1"
+log_must $RM $TESTDIR/tar.${TESTCASE_ID} $TESTDIR/noxattr.tar $TESTDIR/xattr.tar
diff --git a/tests/sys/cddl/zfs/tests/xattr/xattr_012_pos.ksh b/tests/sys/cddl/zfs/tests/xattr/xattr_012_pos.ksh
new file mode 100644
index 000000000000..c8734241499d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/xattr/xattr_012_pos.ksh
@@ -0,0 +1,114 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/xattr/xattr_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: xattr_012_pos
+#
+# DESCRIPTION:
+# xattr file sizes count towards normal disk usage
+#
+# STRATEGY:
+# 1. Create a file, and check pool and filesystem usage
+# 2. Create a 200mb xattr in that file
+# 3. Check pool and filesystem usage, to ensure it reflects the size
+# of the xattr
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-12-15)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function cleanup {
+ log_must $RM $TESTDIR/myfile.${TESTCASE_ID}
+}
+
+function get_pool_size {
+ poolname=$1
+ psize=$( $ZPOOL list -H -o used $poolname )
+ if [[ $psize == *[mM] ]]
+ then
+ returnvalue=$($ECHO $psize | $SED -e 's/m//g' -e 's/M//g')
+ returnvalue=$(( returnvalue * 1024 ))
+ else
+ returnvalue=$($ECHO $psize | $SED -e 's/k//g' -e 's/K//g')
+ fi
+ print $returnvalue
+}
+
+log_assert "xattr file sizes count towards normal disk usage"
+log_onexit cleanup
+
+test_requires RUNAT
+
+log_must $TOUCH $TESTDIR/myfile.${TESTCASE_ID}
+
+POOL_SIZE=0
+NEW_POOL_SIZE=0
+
+if is_global_zone
+then
+ # get pool and filesystem sizes. Since we're starting with an empty
+ # pool, the usage should be small - a few k.
+ POOL_SIZE=$(get_pool_size $TESTPOOL)
+fi
+
+FS_SIZE=$( $ZFS get -p -H -o value used $TESTPOOL/$TESTFS )
+
+log_must $RUNAT $TESTDIR/myfile.${TESTCASE_ID} $MKFILE 200m xattr
+
+#Make sure the newly created file is counted into zpool usage
+log_must $SYNC
+
+# now check to see if our pool disk usage has increased
+if is_global_zone
+then
+ NEW_POOL_SIZE=$(get_pool_size $TESTPOOL)
+ if (( $NEW_POOL_SIZE <= $POOL_SIZE ))
+ then
+ log_fail "The new pool size $NEW_POOL_SIZE was less \
+ than or equal to the old pool size $POOL_SIZE."
+ fi
+
+fi
+
+# also make sure our filesystem usage has increased
+NEW_FS_SIZE=$( $ZFS get -p -H -o value used $TESTPOOL/$TESTFS )
+if (( $NEW_FS_SIZE <= $FS_SIZE ))
+then
+ log_fail "The new filesystem size $NEW_FS_SIZE was less \
+ than or equal to the old filesystem size $FS_SIZE."
+fi
+
+log_pass "xattr file sizes count towards normal disk usage"
diff --git a/tests/sys/cddl/zfs/tests/xattr/xattr_013_pos.ksh b/tests/sys/cddl/zfs/tests/xattr/xattr_013_pos.ksh
new file mode 100644
index 000000000000..b1c147a61056
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/xattr/xattr_013_pos.ksh
@@ -0,0 +1,99 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/xattr/xattr_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: xattr_013_pos
+#
+# DESCRIPTION:
+# The noxattr mount option functions as expected
+#
+# STRATEGY:
+# 1. Create a file on a filesystem and add an xattr to it
+# 2. Unmount the filesystem, and mount it -o noxattr
+# 3. Verify that the xattr cannot be read and new files
+# cannot have xattrs set on them.
+# 4. Unmount and mount the filesystem normally
+# 5. Verify that xattrs can be set and accessed again
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-12-15)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function cleanup {
+
+ log_must $RM $TESTDIR/myfile.${TESTCASE_ID}
+}
+
+
+log_assert "The noxattr mount option functions as expected"
+log_onexit cleanup
+
+test_requires RUNAT
+
+$ZFS set 2>&1 | $GREP xattr > /dev/null
+if [ $? -ne 0 ]
+then
+ log_unsupported "noxattr mount option not supported on this release."
+fi
+
+log_must $TOUCH $TESTDIR/myfile.${TESTCASE_ID}
+create_xattr $TESTDIR/myfile.${TESTCASE_ID} passwd /etc/passwd
+
+log_must $UMOUNT $TESTDIR
+log_must $ZFS mount -o noxattr $TESTPOOL/$TESTFS
+
+# check that we can't perform xattr operations
+log_mustnot eval "$RUNAT $TESTDIR/myfile.${TESTCASE_ID} $CAT passwd > /dev/null 2>&1"
+log_mustnot eval "$RUNAT $TESTDIR/myfile.${TESTCASE_ID} $RM passwd > /dev/null 2>&1"
+log_mustnot eval "$RUNAT $TESTDIR/myfile.${TESTCASE_ID} $CP /etc/passwd . > /dev/null 2>&1"
+
+log_must $TOUCH $TESTDIR/new.${TESTCASE_ID}
+log_mustnot eval "$RUNAT $TESTDIR/new.${TESTCASE_ID} $CP /etc/passwd . > /dev/null 2>&1"
+log_mustnot eval "$RUNAT $TESTDIR/new.${TESTCASE_ID} $RM passwd > /dev/null 2>&1"
+
+# now mount the filesystem again as normal
+log_must $UMOUNT $TESTDIR
+log_must $ZFS mount $TESTPOOL/$TESTFS
+
+# we should still have an xattr on the first file
+verify_xattr $TESTDIR/myfile.${TESTCASE_ID} passwd /etc/passwd
+
+# there should be no xattr on the file we created while the fs was mounted
+# -o noxattr
+log_mustnot eval "$RUNAT $TESTDIR/new.${TESTCASE_ID} $CAT passwd > /dev/null 2>&1"
+create_xattr $TESTDIR/new.${TESTCASE_ID} passwd /etc/passwd
+
+log_pass "The noxattr mount option functions as expected"
diff --git a/tests/sys/cddl/zfs/tests/xattr/xattr_common.kshlib b/tests/sys/cddl/zfs/tests/xattr/xattr_common.kshlib
new file mode 100644
index 000000000000..857fc520b561
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/xattr/xattr_common.kshlib
@@ -0,0 +1,103 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+# a function that takes a file, then creates and verifies
+# an xattr on that file. The xattr_contents is the file
+# that should appear in the xattr namespace.
+function create_xattr { # filename xattr_name xattr_contents
+ typeset FILE=$1
+ typeset XATTR_NAME=$2
+ typeset XATTR_CONTENTS=$3
+
+ # read any empty xattr on that file
+ log_must $RUNAT $FILE $LS
+ # create the xattr
+ log_must $RUNAT $FILE $CP $XATTR_CONTENTS $XATTR_NAME
+
+ verify_xattr $FILE $XATTR_NAME $XATTR_CONTENTS
+}
+
+# a function that compares the a single xattr between two files
+# and checks to see if their contents are identical
+function compare_xattrs { # filename1 filename2 xattr_name
+ typeset FILE1=$1
+ typeset FILE2=$2
+ typeset XATTR_NAME=$3
+
+ $RUNAT $FILE1 $CAT $XATTR_NAME > $TMPDIR/file1.${TESTCASE_ID}
+ $RUNAT $FILE2 $CAT $XATTR_NAME > $TMPDIR/file2.${TESTCASE_ID}
+
+ log_must $DIFF $TMPDIR/file1.${TESTCASE_ID} $TMPDIR/file2.${TESTCASE_ID}
+ log_must $RM $TMPDIR/file1.${TESTCASE_ID} $TMPDIR/file2.${TESTCASE_ID}
+}
+
+function verify_xattr { # filename xattr_name xattr_contents
+ typeset FILE=$1
+ typeset XATTR_NAME=$2
+ typeset XATTR_CONTENTS=$3
+
+ # read the xattr, writing it to a temp file
+ log_must eval "$RUNAT $FILE $CAT $XATTR_NAME > $TMPDIR/$XATTR_NAME.${TESTCASE_ID} 2>&1"
+ log_must $DIFF $XATTR_CONTENTS $TMPDIR/$XATTR_NAME.${TESTCASE_ID}
+ $RM $TMPDIR/$XATTR_NAME.${TESTCASE_ID}
+}
+
+function delete_xattr { # filename xattr_name
+ typeset FILE=$1
+ typeset XATTR_NAME=$2
+
+ # delete the xattr
+ log_must $RUNAT $FILE $RM $XATTR_NAME
+ log_mustnot eval "$RUNAT $FILE $LS $XATTR_NAME > /dev/null 2>&1"
+}
+
+# not sure about this : really this should be testing write/append
+function verify_write_xattr { # filename xattr_name
+ typeset FILE=$1
+ typeset XATTR_NAME=$2
+
+ log_must eval "$RUNAT $FILE $DD if=/etc/passwd of=$XATTR_NAME"
+ log_must eval "$RUNAT $FILE $CAT $XATTR_NAME > $TMPDIR/$XATTR_NAME.${TESTCASE_ID} 2>&1"
+ log_must $DD if=/etc/passwd of=$TMPDIR/passwd_dd.${TESTCASE_ID}
+ log_must $DIFF $TMPDIR/passwd_dd.${TESTCASE_ID} $TMPDIR/$XATTR_NAME.${TESTCASE_ID}
+ log_must $RM $TMPDIR/passwd_dd.${TESTCASE_ID} $TMPDIR/$XATTR_NAME.${TESTCASE_ID}
+}
+
+# this function is to create the expected output
+function create_expected_output { # expected_output_file contents_of_the_output
+ typeset FILE=$1
+ shift
+ if [[ -f $FILE ]]; then
+ log_must $RM $FILE
+ fi
+
+ for line in $@
+ do
+ log_must eval "$ECHO $line >> $FILE"
+ done
+ }
diff --git a/tests/sys/cddl/zfs/tests/xattr/xattr_test.sh b/tests/sys/cddl/zfs/tests/xattr/xattr_test.sh
new file mode 100755
index 000000000000..4a57f2bcc858
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/xattr/xattr_test.sh
@@ -0,0 +1,368 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case xattr_001_pos cleanup
+xattr_001_pos_head()
+{
+ atf_set "descr" "Create/read/write/append of xattrs works"
+ atf_set "require.progs" "ksh93 svcadm svcs"
+}
+xattr_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/xattr_common.kshlib
+ . $(atf_get_srcdir)/xattr.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/xattr_001_pos.ksh || atf_fail "Testcase failed"
+}
+xattr_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/xattr_common.kshlib
+ . $(atf_get_srcdir)/xattr.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case xattr_002_neg cleanup
+xattr_002_neg_head()
+{
+ atf_set "descr" "A read of a non-existent xattr fails"
+ atf_set "require.progs" "ksh93 svcadm svcs"
+}
+xattr_002_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/xattr_common.kshlib
+ . $(atf_get_srcdir)/xattr.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/xattr_002_neg.ksh || atf_fail "Testcase failed"
+}
+xattr_002_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/xattr_common.kshlib
+ . $(atf_get_srcdir)/xattr.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case xattr_003_neg cleanup
+xattr_003_neg_head()
+{
+ atf_set "descr" "read/write xattr on a file with no permissions fails"
+ atf_set "require.progs" "ksh93 svcs svcadm runat runwattr"
+}
+xattr_003_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/xattr_common.kshlib
+ . $(atf_get_srcdir)/xattr.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/xattr_003_neg.ksh || atf_fail "Testcase failed"
+}
+xattr_003_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/xattr_common.kshlib
+ . $(atf_get_srcdir)/xattr.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case xattr_004_pos cleanup
+xattr_004_pos_head()
+{
+ atf_set "descr" "Files from ufs,tmpfs with xattrs copied to zfs retain xattr info."
+ atf_set "require.progs" "ksh93 zfs svcadm runat svcs"
+}
+xattr_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/xattr_common.kshlib
+ . $(atf_get_srcdir)/xattr.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/xattr_004_pos.ksh || atf_fail "Testcase failed"
+}
+xattr_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/xattr_common.kshlib
+ . $(atf_get_srcdir)/xattr.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case xattr_005_pos cleanup
+xattr_005_pos_head()
+{
+ atf_set "descr" "read/write/create/delete xattr on a clone filesystem"
+ atf_set "require.progs" "ksh93 zfs svcadm svcs"
+}
+xattr_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/xattr_common.kshlib
+ . $(atf_get_srcdir)/xattr.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/xattr_005_pos.ksh || atf_fail "Testcase failed"
+}
+xattr_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/xattr_common.kshlib
+ . $(atf_get_srcdir)/xattr.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case xattr_006_pos cleanup
+xattr_006_pos_head()
+{
+ atf_set "descr" "read xattr on a snapshot"
+ atf_set "require.progs" "ksh93 zfs svcadm svcs"
+}
+xattr_006_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/xattr_common.kshlib
+ . $(atf_get_srcdir)/xattr.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/xattr_006_pos.ksh || atf_fail "Testcase failed"
+}
+xattr_006_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/xattr_common.kshlib
+ . $(atf_get_srcdir)/xattr.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case xattr_007_neg cleanup
+xattr_007_neg_head()
+{
+ atf_set "descr" "create/write xattr on a snapshot fails"
+ atf_set "require.progs" "ksh93 zfs svcadm runat svcs"
+}
+xattr_007_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/xattr_common.kshlib
+ . $(atf_get_srcdir)/xattr.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/xattr_007_neg.ksh || atf_fail "Testcase failed"
+}
+xattr_007_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/xattr_common.kshlib
+ . $(atf_get_srcdir)/xattr.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case xattr_008_pos cleanup
+xattr_008_pos_head()
+{
+ atf_set "descr" "special . and .. dirs work as expected for xattrs"
+ atf_set "require.progs" "ksh93 svcadm runat svcs"
+}
+xattr_008_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/xattr_common.kshlib
+ . $(atf_get_srcdir)/xattr.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/xattr_008_pos.ksh || atf_fail "Testcase failed"
+}
+xattr_008_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/xattr_common.kshlib
+ . $(atf_get_srcdir)/xattr.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case xattr_009_neg cleanup
+xattr_009_neg_head()
+{
+ atf_set "descr" "links between xattr and normal file namespace fail"
+ atf_set "require.progs" "ksh93 svcadm runat svcs"
+}
+xattr_009_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/xattr_common.kshlib
+ . $(atf_get_srcdir)/xattr.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/xattr_009_neg.ksh || atf_fail "Testcase failed"
+}
+xattr_009_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/xattr_common.kshlib
+ . $(atf_get_srcdir)/xattr.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case xattr_010_neg cleanup
+xattr_010_neg_head()
+{
+ atf_set "descr" "mkdir, mknod fail"
+ atf_set "require.progs" "ksh93 svcadm runat svcs"
+}
+xattr_010_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/xattr_common.kshlib
+ . $(atf_get_srcdir)/xattr.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/xattr_010_neg.ksh || atf_fail "Testcase failed"
+}
+xattr_010_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/xattr_common.kshlib
+ . $(atf_get_srcdir)/xattr.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case xattr_011_pos cleanup
+xattr_011_pos_head()
+{
+ atf_set "descr" "Basic applications work with xattrs: cpio cp find mv pax tar"
+ atf_set "require.progs" "ksh93 pax svcadm runat svcs"
+}
+xattr_011_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/xattr_common.kshlib
+ . $(atf_get_srcdir)/xattr.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/xattr_011_pos.ksh || atf_fail "Testcase failed"
+}
+xattr_011_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/xattr_common.kshlib
+ . $(atf_get_srcdir)/xattr.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case xattr_012_pos cleanup
+xattr_012_pos_head()
+{
+ atf_set "descr" "xattr file sizes count towards normal disk usage"
+ atf_set "require.progs" "ksh93 svcadm zfs runat zpool svcs"
+}
+xattr_012_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/xattr_common.kshlib
+ . $(atf_get_srcdir)/xattr.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/xattr_012_pos.ksh || atf_fail "Testcase failed"
+}
+xattr_012_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/xattr_common.kshlib
+ . $(atf_get_srcdir)/xattr.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case xattr_013_pos cleanup
+xattr_013_pos_head()
+{
+ atf_set "descr" "The noxattr mount option functions as expected"
+ atf_set "require.progs" "ksh93 zfs svcadm runat svcs"
+}
+xattr_013_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/xattr_common.kshlib
+ . $(atf_get_srcdir)/xattr.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/xattr_013_pos.ksh || atf_fail "Testcase failed"
+}
+xattr_013_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/xattr_common.kshlib
+ . $(atf_get_srcdir)/xattr.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case xattr_001_pos
+ atf_add_test_case xattr_002_neg
+ atf_add_test_case xattr_003_neg
+ atf_add_test_case xattr_004_pos
+ atf_add_test_case xattr_005_pos
+ atf_add_test_case xattr_006_pos
+ atf_add_test_case xattr_007_neg
+ atf_add_test_case xattr_008_pos
+ atf_add_test_case xattr_009_neg
+ atf_add_test_case xattr_010_neg
+ atf_add_test_case xattr_011_pos
+ atf_add_test_case xattr_012_pos
+ atf_add_test_case xattr_013_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/zfsd/Makefile b/tests/sys/cddl/zfs/tests/zfsd/Makefile
new file mode 100644
index 000000000000..e34e24b40906
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/Makefile
@@ -0,0 +1,39 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/zfsd
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zfsd_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= hotspare_cleanup.ksh
+${PACKAGE}FILES+= hotspare_setup.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zfsd.cfg
+${PACKAGE}FILES+= zfsd.kshlib
+${PACKAGE}FILES+= zfsd_autoreplace_001_neg.ksh
+${PACKAGE}FILES+= zfsd_autoreplace_002_pos.ksh
+${PACKAGE}FILES+= zfsd_autoreplace_003_pos.ksh
+${PACKAGE}FILES+= zfsd_degrade_001_pos.ksh
+${PACKAGE}FILES+= zfsd_degrade_002_pos.ksh
+${PACKAGE}FILES+= zfsd_fault_001_pos.ksh
+${PACKAGE}FILES+= zfsd_fault_002_pos.ksh
+${PACKAGE}FILES+= zfsd_hotspare_001_pos.ksh
+${PACKAGE}FILES+= zfsd_hotspare_002_pos.ksh
+${PACKAGE}FILES+= zfsd_hotspare_003_pos.ksh
+${PACKAGE}FILES+= zfsd_hotspare_004_pos.ksh
+${PACKAGE}FILES+= zfsd_hotspare_005_pos.ksh
+${PACKAGE}FILES+= zfsd_hotspare_006_pos.ksh
+${PACKAGE}FILES+= zfsd_hotspare_007_pos.ksh
+${PACKAGE}FILES+= zfsd_hotspare_008_neg.ksh
+${PACKAGE}FILES+= zfsd_import_001_pos.ksh
+${PACKAGE}FILES+= zfsd_replace_001_pos.ksh
+${PACKAGE}FILES+= zfsd_replace_002_pos.ksh
+${PACKAGE}FILES+= zfsd_replace_003_pos.ksh
+${PACKAGE}FILES+= zfsd_replace_004_pos.ksh
+${PACKAGE}FILES+= zfsd_replace_005_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/zfsd/cleanup.ksh b/tests/sys/cddl/zfs/tests/zfsd/cleanup.ksh
new file mode 100644
index 000000000000..4adafb4065be
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/cleanup.ksh
@@ -0,0 +1,36 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2012,2013 Spectra Logic Corporation. All rights reserved.
+# Use is subject to license terms.
+#
+
+. ${STF_SUITE}/include/libtest.kshlib
+. ${STF_SUITE}/include/libgnop.kshlib
+
+# Rotate logs now, because this test can generate a great volume of log entries
+newsyslog
+
+default_cleanup_noexit
+destroy_gnops ${DISKS}
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zfsd/hotspare_cleanup.ksh b/tests/sys/cddl/zfs/tests/zfsd/hotspare_cleanup.ksh
new file mode 100644
index 000000000000..a1754563de62
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/hotspare_cleanup.ksh
@@ -0,0 +1,45 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# Copyright 2013 Spectra Logic Corp. All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+# This is the cleanup script for ZFSD tests that are based on the hotspare
+# framework. It is almost identical to tests/hotspare/cleanup.ksh,
+# but does not restart ZFSD.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+verify_runnable "global"
+
+log_must cleanup_devices_all
+
+log_pass
+
+
diff --git a/tests/sys/cddl/zfs/tests/zfsd/hotspare_setup.ksh b/tests/sys/cddl/zfs/tests/zfsd/hotspare_setup.ksh
new file mode 100644
index 000000000000..cd1f83eb4a17
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/hotspare_setup.ksh
@@ -0,0 +1,43 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# Copyright 2013 Spectra Logic Corp. All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+# This is the setup script for ZFSD tests that are based on the hotspare
+# framework. It is almost identical to tests/hotspare/setup.ksh,
+# but does not stop ZFSD.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+verify_runnable "global"
+
+log_must cleanup_devices_all
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zfsd/setup.ksh b/tests/sys/cddl/zfs/tests/zfsd/setup.ksh
new file mode 100644
index 000000000000..25061c043191
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/setup.ksh
@@ -0,0 +1,33 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2012,2013 Spectra Logic Corporation. All rights reserved.
+# Use is subject to license terms.
+#
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+# Rotate logs now, because this test can generate a great volume of log entries
+newsyslog
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zfsd/zfsd.cfg b/tests/sys/cddl/zfs/tests/zfsd/zfsd.cfg
new file mode 100644
index 000000000000..d556690c28a3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/zfsd.cfg
@@ -0,0 +1,30 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2012,2013 Spectra Logic Corporation. All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+export TESTFILE=testfile.${TESTCASE_ID}
+export STF_TIMEOUT=300
diff --git a/tests/sys/cddl/zfs/tests/zfsd/zfsd.kshlib b/tests/sys/cddl/zfs/tests/zfsd/zfsd.kshlib
new file mode 100644
index 000000000000..6369387ba069
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/zfsd.kshlib
@@ -0,0 +1,101 @@
+#!/usr/local/bin/ksh93 -p
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2013 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+# Common routines used by multiple zfsd tests
+
+function wait_for_pool_dev_state_change
+{
+ typeset -i timeout=$1
+ typeset disk=$2
+ typeset state=$3
+ typeset pool=$4
+
+ if [ -z "$pool" ]; then
+ pool=$TESTPOOL
+ fi
+
+ log_note "Waiting up to $timeout seconds for $disk to become $state ..."
+ for ((; $timeout > 0; timeout=$timeout-1)); do
+ check_state $pool "$disk" "$state"
+ [ $? -eq 0 ] && return
+ $SLEEP 1
+ done
+ log_must $ZPOOL status $pool
+ log_fail "ERROR: Disk $disk not marked as $state in $pool"
+}
+
+function wait_for_pool_removal
+{
+ typeset -i timeout=$1
+ wait_for_pool_dev_state_change $timeout $REMOVAL_DISK "REMOVED|UNAVAIL|FAULTED"
+}
+
+function wait_until_scrubbed
+{
+ typeset pool=$1
+
+ while is_pool_scrubbing $pool; do
+ log_note "$pool still scrubbing..."
+ $SLEEP 1
+ done
+}
+
+function corrupt_pool_vdev
+{
+ typeset pool=$1
+ typeset vdev=$2
+ typeset file=$3
+ typeset -li start=0
+ typeset -li now=0
+ typeset -li timeout=60
+
+ # do some IO on the pool
+ log_must $DD if=/dev/zero of=$file bs=1024k count=64
+ $FSYNC $file
+
+ # ZFS rate limits checksum errors to about 20 per second. So in order
+ # to ensure that we reach zfsd's threshold, we must alternately
+ # scribble and scrub.
+ while (( "$now" - "$start" < "$timeout" )); do
+ # scribble on the underlying file to corrupt the vdev
+ log_must $DD if=/dev/urandom of=$vdev bs=1024k count=64 conv=notrunc
+
+ # Scrub the pool to detect and repair the corruption
+ log_must $ZPOOL scrub $pool
+ wait_until_scrubbed $pool
+ now=`date +%s`
+ if [ "$start" -eq 0 ]; then
+ start=`date +%s`
+ fi
+ check_state "$pool" "$vdev" DEGRADED && return
+ $SLEEP 1
+ done
+
+ log_must $ZPOOL status "$pool"
+ log_fail "ERROR: Disk $vdev not marked as DEGRADED in $pool"
+}
diff --git a/tests/sys/cddl/zfs/tests/zfsd/zfsd_autoreplace_001_neg.ksh b/tests/sys/cddl/zfs/tests/zfsd/zfsd_autoreplace_001_neg.ksh
new file mode 100644
index 000000000000..039c8fd2bf29
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/zfsd_autoreplace_001_neg.ksh
@@ -0,0 +1,91 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2013 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+. $STF_SUITE/tests/zfsd/zfsd.kshlib
+. $STF_SUITE/include/libgnop.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfsd_autoreplace_001_neg
+#
+# DESCRIPTION:
+# In a pool without the autoreplace property set, a vdev will not be
+# replaced by physical path
+#
+# STRATEGY:
+# 1. Create 1 storage pool without hot spares
+# 2. Remove a vdev
+# 4. Create a new vdev with the same physical path as the first one
+# 9. Verify that it does not get added to the pool.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2013-02-4)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+log_assert "A pool without the autoreplace property set will not replace disks by physical path"
+
+function verify_assertion
+{
+ # 9. Verify that it does not get added to the pool
+ for ((timeout=0; timeout<4; timeout=$timeout+1)); do
+ log_mustnot check_state $TESTPOOL $NEW_DISK "ONLINE"
+ $SLEEP 5
+ done
+}
+
+typeset PHYSPATH="some_physical_path"
+typeset REMOVAL_DISK=$DISK0
+typeset REMOVAL_NOP=${DISK0}.nop
+typeset NEW_DISK=$DISK4
+typeset NEW_NOP=${DISK4}.nop
+typeset OTHER_DISKS="${DISK1} ${DISK2} ${DISK3}"
+typeset ALLDISKS="${DISK0} ${DISK1} ${DISK2} ${DISK3}"
+typeset ALLNOPS=${ALLDISKS//~(E)([[:space:]]+|$)/.nop\1}
+set -A MY_KEYWORDS "mirror" "raidz1" "raidz2"
+ensure_zfsd_running
+log_must create_gnops $OTHER_DISKS
+for keyword in "${MY_KEYWORDS[@]}" ; do
+ log_must create_gnop $REMOVAL_DISK $PHYSPATH
+ log_must create_pool $TESTPOOL $keyword $ALLNOPS
+ log_must $ZPOOL set autoreplace=off $TESTPOOL
+
+ log_must destroy_gnop $REMOVAL_DISK
+ log_must wait_for_pool_removal 20
+ log_must create_gnop $NEW_DISK $PHYSPATH
+ verify_assertion
+ destroy_pool "$TESTPOOL"
+ log_must destroy_gnop $NEW_DISK
+done
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zfsd/zfsd_autoreplace_002_pos.ksh b/tests/sys/cddl/zfs/tests/zfsd/zfsd_autoreplace_002_pos.ksh
new file mode 100644
index 000000000000..1a777690b3ff
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/zfsd_autoreplace_002_pos.ksh
@@ -0,0 +1,88 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2013 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+. $STF_SUITE/tests/zfsd/zfsd.kshlib
+. $STF_SUITE/include/libgnop.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfsd_autoreplace_002_pos
+#
+# DESCRIPTION:
+# In a pool with the autoreplace property set, a vdev will be
+# replaced by physical path
+#
+# STRATEGY:
+# 1. Create 1 storage pool without hot spares
+# 2. Remove a vdev
+# 4. Create a new vdev with the same physical path as the first one
+# 9. Verify that it does get added to the pool.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2013-02-4)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+log_assert "A pool with the autoreplace property will replace disks by physical path"
+
+function verify_assertion
+{
+ wait_for_pool_dev_state_change 20 $NEW_DISK ONLINE
+}
+
+
+typeset PHYSPATH="some_physical_path"
+typeset REMOVAL_DISK=$DISK0
+typeset REMOVAL_NOP=${DISK0}.nop
+typeset NEW_DISK=$DISK4
+typeset NEW_NOP=${DISK4}.nop
+typeset OTHER_DISKS="${DISK1} ${DISK2} ${DISK3}"
+typeset ALLDISKS="${DISK0} ${DISK1} ${DISK2} ${DISK3}"
+typeset ALLNOPS=${ALLDISKS//~(E)([[:space:]]+|$)/.nop\1}
+set -A MY_KEYWORDS "mirror" "raidz1" "raidz2"
+ensure_zfsd_running
+log_must create_gnops $OTHER_DISKS
+for keyword in "${MY_KEYWORDS[@]}" ; do
+ log_must create_gnop $REMOVAL_DISK $PHYSPATH
+ log_must create_pool $TESTPOOL $keyword $ALLNOPS
+ log_must $ZPOOL set autoreplace=on $TESTPOOL
+
+ log_must destroy_gnop $REMOVAL_DISK
+ log_must wait_for_pool_removal 20
+ log_must create_gnop $NEW_DISK $PHYSPATH
+ verify_assertion
+ destroy_pool "$TESTPOOL"
+ log_must destroy_gnop $NEW_DISK
+done
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zfsd/zfsd_autoreplace_003_pos.ksh b/tests/sys/cddl/zfs/tests/zfsd/zfsd_autoreplace_003_pos.ksh
new file mode 100644
index 000000000000..7ad7a9113402
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/zfsd_autoreplace_003_pos.ksh
@@ -0,0 +1,107 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2014 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+#
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+. $STF_SUITE/tests/zfsd/zfsd.kshlib
+. $STF_SUITE/include/libgnop.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfsd_autoreplace_003_pos
+#
+# DESCRIPTION:
+# In a pool with the autoreplace property set, a vdev will be
+# replaced by physical path even if a spare is already active for that
+# vdev
+#
+# STRATEGY:
+# 1. Create 1 storage pool with a hot spare
+# 2. Remove a vdev
+# 3. Wait for the hotspare to fully resilver
+# 4. Create a new vdev with the same physical path as the first one
+# 10. Verify that it does get added to the pool.
+# 11. Verify that the hotspare gets removed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2013-05-13)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+log_assert "A pool with the autoreplace property will replace disks by physical path, even if a spare is active"
+
+function verify_assertion
+{
+ # Verify that the replacement disk gets added to the pool
+ wait_for_pool_dev_state_change 20 $NEW_DISK ONLINE
+
+ # Wait for resilvering to complete
+ wait_until_resilvered
+
+ # Check that the spare is deactivated
+ wait_for_pool_dev_state_change 20 "$SPARE_DISK" "AVAIL"
+}
+
+
+typeset PHYSPATH="some_physical_path"
+typeset REMOVAL_DISK=$DISK0
+typeset REMOVAL_NOP=${DISK0}.nop
+typeset NEW_DISK=$DISK3
+typeset NEW_NOP=${DISK3}.nop
+typeset SPARE_DISK=${DISK4}
+typeset SPARE_NOP=${DISK4}.nop
+typeset OTHER_DISKS="${DISK1} ${DISK2}"
+typeset OTHER_NOPS=${OTHER_DISKS//~(E)([[:space:]]+|$)/.nop\1}
+set -A MY_KEYWORDS "mirror" "raidz1" "raidz2"
+set -A MY_FAILURES "FAULTED" "REMOVED"
+ensure_zfsd_running
+log_must create_gnops $OTHER_DISKS $SPARE_DISK
+for failure in "${MY_FAILURES[@]}" ; do
+ for keyword in "${MY_KEYWORDS[@]}" ; do
+ log_must create_gnop $REMOVAL_DISK $PHYSPATH
+ log_must create_pool $TESTPOOL $keyword $REMOVAL_NOP $OTHER_NOPS spare $SPARE_NOP
+ log_must $ZPOOL set autoreplace=on $TESTPOOL
+
+ if [ $failure = "FAULTED" ]; then
+ log_must zinject -d $REMOVAL_NOP -A fault $TESTPOOL
+ fi
+ log_must destroy_gnop $REMOVAL_DISK
+ log_must wait_for_pool_removal 20
+ log_must create_gnop $NEW_DISK $PHYSPATH
+ verify_assertion
+ destroy_pool "$TESTPOOL"
+ log_must destroy_gnop $NEW_DISK
+ done
+done
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zfsd/zfsd_degrade_001_pos.ksh b/tests/sys/cddl/zfs/tests/zfsd/zfsd_degrade_001_pos.ksh
new file mode 100644
index 000000000000..8f334711eb19
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/zfsd_degrade_001_pos.ksh
@@ -0,0 +1,89 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2012,2013 Spectra Logic Corporation. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/zfsd/zfsd.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfsd_degrade_001_pos
+#
+# DESCRIPTION:
+# If a vdev experiences checksum errors, it will become degraded.
+#
+#
+# STRATEGY:
+# 1. Create a storage pool. Only use the file vdevs because it is easy to
+# generate checksum errors on them.
+# 2. Mostly fill the pool with data.
+# 3. Corrupt it by DDing to the underlying vdev
+# 4. Verify that the vdev becomes DEGRADED.
+# 5. ONLINE it and verify that it resilvers and joins the pool.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2012-08-09)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+VDEV0=${TMPDIR}/file0.${TESTCASE_ID}
+VDEV1=${TMPDIR}/file1.${TESTCASE_ID}
+VDEVS="${VDEV0} ${VDEV1}"
+TESTFILE=/$TESTPOOL/testfile
+VDEV_SIZE=192m
+
+
+function cleanup
+{
+ destroy_pool $TESTPOOL
+ $RM -f $VDEVS
+}
+
+log_assert "ZFS will degrade a vdev that produces checksum errors"
+
+log_onexit cleanup
+
+log_must create_vdevs $VDEV0 $VDEV1
+ensure_zfsd_running
+for type in "raidz" "mirror"; do
+ log_note "Testing raid type $type"
+
+ create_pool $TESTPOOL $type ${VDEVS}
+ corrupt_pool_vdev $TESTPOOL $VDEV1 $TESTFILE
+ destroy_pool $TESTPOOL
+done
+
+cleanup
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zfsd/zfsd_degrade_002_pos.ksh b/tests/sys/cddl/zfs/tests/zfsd/zfsd_degrade_002_pos.ksh
new file mode 100644
index 000000000000..09d9c1b1de40
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/zfsd_degrade_002_pos.ksh
@@ -0,0 +1,101 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2012-2014 Spectra Logic Corporation. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/zfsd/zfsd.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfsd_degrade_002_pos
+#
+# DESCRIPTION:
+# If an active hotspare experiences checksum errors, it will become degraded.
+#
+#
+# STRATEGY:
+# 1. Create a storage pool with a hotspare. Only use the file vdevs because
+# it is easy to generate checksum errors on them.
+# 2. fault a vdev to active the hotspare
+# 3. Mostly fill the pool with data.
+# 4. Corrupt it by DDing to the hotspare's underlying file.
+# 5. Verify that the hotspare becomes DEGRADED.
+# 6. ONLINE it and verify that it resilvers and joins the pool.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2014-05-13)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+VDEV0=${TMPDIR}/file0.${TESTCASE_ID}
+VDEV1=${TMPDIR}/file1.${TESTCASE_ID}
+SPARE_VDEV=${TMPDIR}/file2.${TESTCASE_ID}
+BASIC_VDEVS="${VDEV0} ${VDEV1}"
+VDEVS="${BASIC_VDEVS} ${SPARE_VDEV}"
+TESTFILE=/$TESTPOOL/testfile
+VDEV_SIZE=192m
+
+
+function cleanup
+{
+ destroy_pool $TESTPOOL
+ $RM -f $VDEVS
+}
+
+log_assert "ZFS will degrade a spare vdev that produces checksum errors"
+
+log_onexit cleanup
+
+ensure_zfsd_running
+log_must create_vdevs $VDEV0 $VDEV1 $SPARE_VDEV
+
+for type in "mirror" "raidz"; do
+ log_note "Testing raid type $type"
+
+ create_pool $TESTPOOL $type ${BASIC_VDEVS} spare ${SPARE_VDEV}
+
+ # Activate the hotspare
+ $ZINJECT -d ${VDEV0} -A fault $TESTPOOL
+
+ # ZFSD can take up to 60 seconds to replace a failed device
+ # (though it's usually faster).
+ wait_for_pool_dev_state_change 60 $SPARE_VDEV INUSE
+
+ corrupt_pool_vdev $TESTPOOL $SPARE_VDEV $TESTFILE
+ destroy_pool $TESTPOOL
+done
+
+cleanup
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zfsd/zfsd_fault_001_pos.ksh b/tests/sys/cddl/zfs/tests/zfsd/zfsd_fault_001_pos.ksh
new file mode 100644
index 000000000000..df704e183fb0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/zfsd_fault_001_pos.ksh
@@ -0,0 +1,123 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2012,2013 Spectra Logic Corporation. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/include/libgnop.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfsd_fault_001_pos
+#
+# DESCRIPTION:
+# If a vdev experiences IO errors, it will become faulted.
+#
+#
+# STRATEGY:
+# 1. Create a storage pool. Use gnop vdevs so we can inject I/O errors.
+# 2. Inject IO errors while doing IO to the pool.
+# 3. Verify that the vdev becomes FAULTED.
+# 4. ONLINE it and verify that it resilvers and joins the pool.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2012-08-09)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+log_assert "ZFS will fault a vdev that produces IO errors"
+
+ensure_zfsd_running
+
+DISK0_NOP=${DISK0}.nop
+DISK1_NOP=${DISK1}.nop
+
+log_must create_gnops $DISK0 $DISK1
+
+for type in "raidz" "mirror"; do
+ log_note "Testing raid type $type"
+
+ # Create a pool on the supplied disks
+ create_pool $TESTPOOL $type "$DISK0_NOP" "$DISK1_NOP"
+ log_must $ZFS create $TESTPOOL/$TESTFS
+
+ # Cause some IO errors writing to the pool
+ while true; do
+ log_must gnop configure -e 5 -w 100 "$DISK1_NOP"
+ $DD if=/dev/zero bs=128k count=1 >> \
+ /$TESTPOOL/$TESTFS/$TESTFILE 2> /dev/null
+ $FSYNC /$TESTPOOL/$TESTFS/$TESTFILE
+ # Due to a bug outside of zfsd, it may be necessary to reopen
+ # the pool before it will become DEGRADED.
+ # https://github.com/openzfs/zfs/issues/16245
+ $ZPOOL reopen $TESTPOOL
+ # Check to see if the pool is faulted yet
+ $ZPOOL status $TESTPOOL | grep -q 'state: DEGRADED'
+ if [ $? == 0 ]
+ then
+ log_note "$TESTPOOL got degraded"
+ break
+ fi
+ done
+
+ log_must check_state $TESTPOOL $TMPDISK "FAULTED"
+
+ # Heal and reattach the failed disk
+ log_must gnop configure -w 0 "$DISK1_NOP"
+ log_must $ZPOOL online $TESTPOOL "$DISK1_NOP"
+
+ # Verify that the pool resilvers and goes to the ONLINE state
+ for (( retries=60; $retries>0; retries=$retries+1 ))
+ do
+ $ZPOOL status $TESTPOOL | egrep -q "scan:.*resilvered"
+ RESILVERED=$?
+ $ZPOOL status $TESTPOOL | egrep -q "state:.*ONLINE"
+ ONLINE=$?
+ if test $RESILVERED -a $ONLINE
+ then
+ break
+ fi
+ $SLEEP 2
+ done
+
+ if [ $retries == 0 ]
+ then
+ log_fail "$TESTPOOL never resilvered in the allowed time"
+ fi
+
+ destroy_pool $TESTPOOL
+ log_must $RM -rf /$TESTPOOL
+done
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zfsd/zfsd_fault_002_pos.ksh b/tests/sys/cddl/zfs/tests/zfsd/zfsd_fault_002_pos.ksh
new file mode 100644
index 000000000000..d8de1ceaff4b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/zfsd_fault_002_pos.ksh
@@ -0,0 +1,99 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2023 Axcient. All rights reserved.
+# Use is subject to license terms.
+#
+# $FreeBSD$
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/include/libgnop.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfsd_fault_002_pos
+#
+# DESCRIPTION:
+# If a vdev experiences delayed I/O, it will become faulted.
+#
+#
+# STRATEGY:
+# 1. Create a storage pool. Use gnop vdevs so we can inject I/O delays.
+# 2. Inject IO delays while doing IO to the pool.
+# 3. Verify that the vdev becomes FAULTED.
+# 4. ONLINE it and verify that it resilvers and joins the pool.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+log_assert "ZFS will fault a vdev that experiences delayed I/O"
+
+ensure_zfsd_running
+
+DISK0_NOP=${DISK0}.nop
+DISK1_NOP=${DISK1}.nop
+
+log_must create_gnops $DISK0 $DISK1
+
+for type in "raidz" "mirror"; do
+ log_note "Testing raid type $type"
+
+ # Create a pool on the supplied disks
+ create_pool $TESTPOOL $type "$DISK0_NOP" "$DISK1_NOP"
+ log_must $ZFS create $TESTPOOL/$TESTFS
+
+ # Cause some IO delays writing to the pool
+ while true; do
+ # ZFS currently considers an I/O to be "slow" if it's delayed
+ # for 30 seconds (zio_slow_io_ms).
+ log_must gnop configure -d 31000 -x 100 "$DISK1_NOP"
+ $DD if=/dev/zero bs=128k count=1 >> \
+ /$TESTPOOL/$TESTFS/$TESTFILE 2> /dev/null
+ $FSYNC /$TESTPOOL/$TESTFS/$TESTFILE
+ # Check to see if the pool is faulted yet
+ $ZPOOL status $TESTPOOL | grep -q 'state: DEGRADED'
+ if [ $? == 0 ]
+ then
+ log_note "$TESTPOOL got degraded"
+ $ZPOOL status -s $TESTPOOL
+ break
+ fi
+ done
+
+ log_must check_state $TESTPOOL $TMPDISK "FAULTED"
+
+ log_must gnop configure -x 0 "$DISK1_NOP"
+ destroy_pool $TESTPOOL
+ log_must $RM -rf /$TESTPOOL
+done
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_001_pos.ksh b/tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_001_pos.ksh
new file mode 100644
index 000000000000..74e2654cfab2
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_001_pos.ksh
@@ -0,0 +1,149 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2014 Spectra Logic Corp. All rights reserved.
+# Use is subject to license terms.
+#
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfsd_hotspare_001_pos
+#
+# DESCRIPTION:
+# If an active spare fails, it will be replaced by an available spare.
+#
+# STRATEGY:
+# 1. Create a storage pool with two hot spares
+# 2. Fail one vdev
+# 3. Verify that a spare gets activated
+# 4. Fail the spare
+# 5. Verify the failed spare was replaced by the other spare.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2014-05-13)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ if poolexists $TESTPOOL ; then
+ destroy_pool $TESTPOOL
+ fi
+
+ partition_cleanup
+}
+
+
+log_onexit cleanup
+
+function verify_assertion # type
+{
+ typeset pool_type=$1
+
+ typeset err_dev=${devarray[3]}
+ typeset raidz2_dev="${devarray[4]}"
+ typeset mntp=$(get_prop mountpoint $TESTPOOL)
+
+ # fail a basic vdev
+ $ZINJECT -d $err_dev -A fault $TESTPOOL
+
+ # ZFSD can take up to 60 seconds to replace a failed device
+ # (though it's usually faster).
+ for ((timeout=0; $timeout<10; timeout=$timeout+1)); do
+ check_state $TESTPOOL "$fail_spare" "INUSE"
+ spare_inuse=$?
+ if [[ $spare_inuse == 0 ]]; then
+ break
+ fi
+ $SLEEP 6
+ done
+ log_must $ZPOOL status $TESTPOOL
+ log_must check_state $TESTPOOL "$fail_spare" "INUSE"
+
+ # The zpool history should log when a spare device becomes active
+ log_must $ZPOOL history -i $TESTPOOL | $GREP "internal vdev attach" | \
+ $GREP "spare in vdev=$fail_spare for vdev=$err_dev" > /dev/null
+
+ ######################################################################
+ # Now fail the active hotspare, and check that the second comes online
+ ######################################################################
+
+ # fail the spare vdev
+ $ZINJECT -d $fail_spare -A fault $TESTPOOL
+
+ # ZFSD can take up to 60 seconds to replace a failed device
+ # (though it's usually faster).
+ for ((timeout=0; $timeout<10; timeout=$timeout+1)); do
+ check_state $TESTPOOL "$standby_spare" "INUSE"
+ spare_inuse=$?
+ if [[ $spare_inuse == 0 ]]; then
+ break
+ fi
+ $SLEEP 6
+ done
+ log_must $ZPOOL status $TESTPOOL
+
+ # The standby spare should be in use, while the original spare should
+ # be faulted.
+ log_must check_state $TESTPOOL $standby_spare "online"
+ log_must check_state $TESTPOOL $standby_spare "INUSE"
+ log_mustnot check_state $TESTPOOL $fail_spare "online"
+
+ # The zpool history should log when a spare device becomes active
+ log_must $ZPOOL history -i $TESTPOOL | $GREP "internal vdev attach" | \
+ $GREP "spare in vdev=$standby_spare for vdev=$fail_spare" > /dev/null
+
+ # do cleanup
+ destroy_pool $TESTPOOL
+}
+
+log_onexit cleanup
+
+log_assert "An active damaged spare will be replaced by an available spare"
+
+ensure_zfsd_running
+set_devs
+
+typeset fail_spare="${devarray[0]}"
+typeset standby_spare="${devarray[1]}"
+typeset spares="$fail_spare $standby_spare"
+
+set -A my_keywords "mirror" "raidz1" "raidz2"
+
+for keyword in "${my_keywords[@]}"; do
+ setup_hotspares "$keyword"
+ verify_assertion "$keyword"
+done
+
+log_pass "If one of the spare fail, the other available spare will be in use"
diff --git a/tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_002_pos.ksh b/tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_002_pos.ksh
new file mode 100644
index 000000000000..cc75c1b0eba5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_002_pos.ksh
@@ -0,0 +1,109 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfsd_hotspare_002_pos
+#
+# DESCRIPTION:
+# If a vdev becomes degraded in a pool with a spare, the spare will be
+# activated.
+#
+#
+# STRATEGY:
+# 1. Create 1 storage pools with hot spares.
+# 2. Artificially degrade one vdev in the pool
+# 3. Verify that the spare is in use.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2012-08-06)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ if poolexists $TESTPOOL ; then
+ destroy_pool $TESTPOOL
+ fi
+
+ partition_cleanup
+}
+
+
+log_onexit cleanup
+
+function verify_assertion # type
+{
+ typeset sdev=$1
+ typeset err_dev=${devarray[3]}
+ typeset mntp=$(get_prop mountpoint $TESTPOOL)
+
+ # Artificially degrade the vdev
+ log_must $ZINJECT -d $err_dev -A degrade $TESTPOOL
+ log_must check_state $TESTPOOL $err_dev "DEGRADED"
+
+ # ZFSD can take up to 60 seconds to degrade an array in response to
+ # errors (though it's usually faster).
+ for ((timeout=0; $timeout<10; timeout=$timeout+1)); do
+ check_state $TESTPOOL "$sdev" "INUSE"
+ spare_inuse=$?
+ if [[ $spare_inuse == 0 ]]; then
+ break
+ fi
+ $SLEEP 6
+ done
+ log_must $ZPOOL status $TESTPOOL
+ log_must check_state $TESTPOOL "$sdev" "INUSE"
+
+ # do cleanup
+ destroy_pool $TESTPOOL
+}
+
+log_onexit cleanup
+
+log_assert "If a vdev becomes degraded, the spare will be activated."
+
+ensure_zfsd_running
+set_devs
+
+typeset sdev="${devarray[0]}"
+
+set -A my_keywords "mirror" "raidz1" "raidz2"
+
+for keyword in "${my_keywords[@]}"; do
+ setup_hotspares "$keyword"
+ verify_assertion $sdev
+done
diff --git a/tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_003_pos.ksh b/tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_003_pos.ksh
new file mode 100644
index 000000000000..04d52e8e06ce
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_003_pos.ksh
@@ -0,0 +1,104 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_hotspare_003_pos
+#
+# DESCRIPTION:
+# If a vdev becomes faulted in a pool with a spare, the spare will be
+# activated.
+#
+#
+# STRATEGY:
+# 1. Create 1 storage pools with hot spares.
+# 2. Artificially fault one vdev in the pool to make 1 hotspare in use.
+# 3. Verify that the spare is in use.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2012-08-06)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TESTPOOL && \
+ destroy_pool $TESTPOOL
+
+ partition_cleanup
+}
+
+function verify_assertion # spare_dev
+{
+ typeset err_dev=${devarray[3]}
+ typeset sdev=$1
+
+ log_must $ZINJECT -d $err_dev -A degrade $TESTPOOL
+ log_must check_state $TESTPOOL $err_dev "DEGRADED"
+
+ # ZFSD can take up to 60 seconds to degrade an array in response to
+ # errors (though it's usually faster).
+ for ((timeout=0; $timeout<10; timeout=$timeout+1)); do
+ check_state $TESTPOOL "$sdev" "INUSE"
+ spare_inuse=$?
+ if [[ $spare_inuse == 0 ]]; then
+ break
+ fi
+ $SLEEP 6
+ done
+ log_must $ZPOOL status $TESTPOOL
+ log_must check_state $TESTPOOL "$sdev" "INUSE"
+
+ # do cleanup
+ destroy_pool $TESTPOOL
+}
+
+
+log_assert "A faulted vdev will be replaced by an available spare"
+
+log_onexit cleanup
+
+ensure_zfsd_running
+set_devs
+typeset sdev="${devarray[0]}"
+
+set -A my_keywords "mirror" "raidz1" "raidz2"
+for keyword in "${my_keywords[@]}" ; do
+ setup_hotspares "$keyword"
+ verify_assertion $sdev
+done
+
+log_pass "A faulted vdev will be replaced by an available spare"
diff --git a/tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_004_pos.ksh b/tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_004_pos.ksh
new file mode 100644
index 000000000000..fe4ef43a29a2
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_004_pos.ksh
@@ -0,0 +1,103 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+. $STF_SUITE/tests/zfsd/zfsd.kshlib
+. $STF_SUITE/include/libgnop.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_hotspare_004_pos
+#
+# DESCRIPTION:
+# If a vdev gets removed from a pool with a spare, the spare will be
+# activated.
+#
+#
+# STRATEGY:
+# 1. Create 1 storage pools with hot spares.
+# 2. Remove one vdev
+# 3. Verify that the spare is in use.
+# 4. Recreate the vdev
+# 5. Verify that the vdev gets resilvered and the spare gets removed
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2012-08-10)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+log_assert "Removing a disk from a pool results in the spare activating"
+
+function verify_assertion # spare_dev
+{
+ typeset spare_dev=$1
+ log_must destroy_gnop $REMOVAL_DISK
+
+ # Check to make sure ZFS sees the disk as removed
+ wait_for_pool_removal 20
+
+ # Check that the spare was activated
+ wait_for_pool_dev_state_change 20 $spare_dev INUSE
+ log_must $ZPOOL status $TESTPOOL
+
+ # Re-enable the missing disk
+ log_must create_gnop $REMOVAL_DISK $PHYSPATH
+
+ # Check that the disk has rejoined the pool & resilvered
+ wait_for_pool_dev_state_change 20 $REMOVAL_NOP ONLINE
+ wait_until_resilvered
+
+ # Finally, check that the spare deactivated
+ wait_for_pool_dev_state_change 20 $spare_dev AVAIL
+}
+
+
+typeset PHYSPATH="some_physical_path"
+typeset REMOVAL_DISK=$DISK0
+typeset REMOVAL_NOP=${DISK0}.nop
+typeset SPARE_DISK=$DISK4
+typeset SPARE_NOP=${DISK4}.nop
+typeset OTHER_DISKS="${DISK1} ${DISK2} ${DISK3}"
+typeset OTHER_NOPS=${OTHER_DISKS//~(E)([[:space:]]+|$)/.nop\1}
+set -A MY_KEYWORDS "mirror" "raidz1" "raidz2"
+ensure_zfsd_running
+log_must create_gnops $OTHER_DISKS $SPARE_DISK
+log_must create_gnop $REMOVAL_DISK $PHYSPATH
+for keyword in "${MY_KEYWORDS[@]}" ; do
+ log_must create_pool $TESTPOOL $keyword $REMOVAL_NOP $OTHER_NOPS spare $SPARE_NOP
+ log_must $ZPOOL set autoreplace=on $TESTPOOL
+ iterate_over_hotspares verify_assertion $SPARE_NOP
+
+ destroy_pool "$TESTPOOL"
+done
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_005_pos.ksh b/tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_005_pos.ksh
new file mode 100644
index 000000000000..be12762fa286
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_005_pos.ksh
@@ -0,0 +1,107 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfsd_hotspare_005_pos
+#
+# DESCRIPTION:
+# If a spare gets added to an already damaged pool, the spare will be
+# activated
+#
+# STRATEGY:
+# 1. Create 1 storage pool without hot spares
+# 2. Fail one vdev by using zinject to degrade or fault it
+# 3. Verify that it gets degraded or faulted, respectively
+# 4. Add a hotspare
+# 5. Verify that the spare is in use.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2012-08-10)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ if poolexists $TESTPOOL ; then
+ destroy_pool $TESTPOOL
+ fi
+
+ partition_cleanup
+}
+
+function verify_assertion # damage_type
+{
+ typeset damage=$1
+ typeset err_dev=${devarray[3]}
+ typeset mntp=$(get_prop mountpoint $TESTPOOL)
+
+ log_must $ZINJECT -d $err_dev -A $damage $TESTPOOL
+ log_must check_state $TESTPOOL $err_dev ${damage_status[$damage]}
+
+ # Add the spare, and check that it is in use
+ log_must $ZPOOL add $TESTPOOL spare $sdev
+ for ((timeout=0; $timeout<10; timeout=$timeout+1)); do
+ if check_state $TESTPOOL "$sdev" "INUSE"; then
+ break
+ fi
+ $SLEEP 6
+ done
+ log_must $ZPOOL status $TESTPOOL
+ log_must check_state $TESTPOOL "$sdev" "INUSE"
+}
+
+log_onexit cleanup
+
+log_assert "A spare that is added to a degraded pool will be activated"
+
+ensure_zfsd_running
+set_devs
+
+typeset sdev="${sparedevs[0]}"
+typeset -A damage_status
+damage_status["degrade"]="DEGRADED"
+damage_status["fault"]="FAULTED"
+
+set -A my_keywords "mirror" "raidz1" "raidz2"
+
+for keyword in "${my_keywords[@]}"; do
+ for damage in "degrade" "fault"; do
+ log_must create_pool $TESTPOOL $keyword ${pooldevs[@]}
+ verify_assertion $damage
+ destroy_pool $TESTPOOL
+ done
+done
diff --git a/tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_006_pos.ksh b/tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_006_pos.ksh
new file mode 100644
index 000000000000..22a9c0ce5b3d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_006_pos.ksh
@@ -0,0 +1,142 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2014 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+#
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_hotspare_004_pos
+#
+# DESCRIPTION:
+# If two vdevs get removed from a pool with two spares at the same time,
+# both spares will be activated.
+#
+#
+# STRATEGY:
+# 1. Create 1 storage pool with two hot spares.
+# 2. Stop the zfsd process.
+# 3. Fault two vdevs
+# 4. Resume the zfsd process
+# 5. Verify that the spares are in use.
+# 6. Pause zfsd
+# 7. Clear the errors on the faulted vdevs
+# 8. Resume zfsd
+# 9. Verify that the vdevs ges resilvered and the spares get removed
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2014-05-13)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ poolexists $TESTPOOL && \
+ destroy_pool $TESTPOOL
+
+ partition_cleanup
+ $KILL -s SIGCONT `$PGREP zfsd`
+}
+
+function verify_assertion # spare_dev
+{
+ typeset err_dev1=${devarray[3]}
+ typeset err_dev2=${devarray[4]}
+ typeset sdev=$1
+
+ $KILL -s SIGSTOP `$PGREP zfsd`
+
+ log_must $ZINJECT -d $err_dev1 -A fault $TESTPOOL
+ log_must $ZINJECT -d $err_dev2 -A fault $TESTPOOL
+ log_must check_state $TESTPOOL $err_dev1 "FAULTED"
+ log_must check_state $TESTPOOL $err_dev2 "FAULTED"
+
+ $KILL -s SIGCONT `$PGREP zfsd`
+
+ # ZFSD can take up to 60 seconds to degrade an array in response to
+ # errors (though it's usually faster).
+ for ((timeout=0; $timeout<10; timeout=$timeout+1)); do
+ check_state $TESTPOOL "$sdev0" "INUSE"
+ cond1=$?
+ check_state $TESTPOOL "$sdev1" "INUSE"
+ cond2=$?
+ if [[ $cond1 -eq 0 && $cond2 -eq 0 ]]; then
+ break
+ fi
+ $SLEEP 6
+ done
+ log_must $ZPOOL status $TESTPOOL
+ log_must check_state $TESTPOOL "$sdev1" "INUSE"
+ log_must check_state $TESTPOOL "$sdev2" "INUSE"
+
+ $KILL -s SIGSTOP `$PGREP zfsd`
+ $ZPOOL clear $TESTPOOL
+ $KILL -s SIGCONT `$PGREP zfsd`
+
+ for ((timeout=0; $timeout<10; timeout=$timeout+1)); do
+ check_state $TESTPOOL "$sdev0" "AVAIL"
+ cond1=$?
+ check_state $TESTPOOL "$sdev1" "AVAIL"
+ cond2=$?
+ if [[ $cond1 -eq 0 && $cond2 -eq 0 ]]; then
+ break
+ fi
+ $SLEEP 6
+ done
+ log_must $ZPOOL status $TESTPOOL
+ log_must check_state $TESTPOOL "$sdev1" "AVAIL"
+ log_must check_state $TESTPOOL "$sdev2" "AVAIL"
+
+ # do cleanup
+ destroy_pool $TESTPOOL
+}
+
+
+log_assert "Two simultaneously faulted vdevs will be replaced by available spares"
+
+log_onexit cleanup
+
+ensure_zfsd_running
+set_devs
+typeset sdev0="${devarray[0]}"
+typeset sdev1="${devarray[1]}"
+
+set -A my_keywords "mirror" "raidz2"
+for keyword in "${my_keywords[@]}" ; do
+ setup_hotspares "$keyword"
+ verify_assertion $sdev
+done
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_007_pos.ksh b/tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_007_pos.ksh
new file mode 100644
index 000000000000..e80167857cae
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_007_pos.ksh
@@ -0,0 +1,104 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+. $STF_SUITE/tests/zfsd/zfsd.kshlib
+. $STF_SUITE/include/libgnop.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfs_hotspare_007_pos
+#
+# DESCRIPTION:
+# If a vdev gets removed from a pool with a spare while zfsd is shut
+# down, then the spare will be activated when zfsd restarts
+#
+#
+# STRATEGY:
+# 1. Create 1 storage pools with hot spares.
+# 2. Turn off zfsd
+# 3. Remove one vdev
+# 4. Restart zfsd
+# 5. Verify that the spare is in use.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2014-09-17)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+log_assert "zfsd will spare missing drives on startup"
+
+
+function verify_assertion # spare_dev
+{
+ typeset spare_dev=$1
+ stop_zfsd
+
+ log_must destroy_gnop $REMOVAL_DISK
+
+ # Check to make sure ZFS sees the disk as removed
+ wait_for_pool_removal 20
+
+ restart_zfsd
+
+ # Check that the spare was activated
+ wait_for_pool_dev_state_change 20 $spare_dev INUSE
+
+ # Re-enable the missing disk
+ log_must create_gnop $REMOVAL_DISK $PHYSPATH
+
+ # And now the spare should be released
+ wait_for_pool_dev_state_change 20 $spare_dev AVAIL
+}
+
+typeset PHYSPATH="some_physical_path"
+typeset REMOVAL_DISK=$DISK0
+typeset REMOVAL_NOP=${DISK0}.nop
+typeset SPARE_DISK=$DISK4
+typeset SPARE_NOP=${DISK4}.nop
+typeset OTHER_DISKS="${DISK1} ${DISK2} ${DISK3}"
+typeset OTHER_NOPS=${OTHER_DISKS//~(E)([[:space:]]+|$)/.nop\1}
+set -A MY_KEYWORDS "mirror" "raidz1" "raidz2"
+ensure_zfsd_running
+log_must create_gnops $OTHER_DISKS $SPARE_DISK
+log_must create_gnop $REMOVAL_DISK $PHYSPATH
+for keyword in "${MY_KEYWORDS[@]}" ; do
+ log_must create_pool $TESTPOOL $keyword $REMOVAL_NOP $OTHER_NOPS spare $SPARE_NOP
+ log_must $ZPOOL set autoreplace=on $TESTPOOL
+ iterate_over_hotspares verify_assertion $SPARE_NOP
+
+ destroy_pool "$TESTPOOL"
+done
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_008_neg.ksh b/tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_008_neg.ksh
new file mode 100644
index 000000000000..6a9d280708f6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/zfsd_hotspare_008_neg.ksh
@@ -0,0 +1,81 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2017 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+
+verify_runnable "global"
+
+function cleanup
+{
+ $ZPOOL status $TESTPOOL
+ if poolexists $TESTPOOL ; then
+ destroy_pool $TESTPOOL
+ fi
+
+ partition_cleanup
+}
+
+function verify_assertion # damage_type
+{
+ typeset mntp=$(get_prop mountpoint $TESTPOOL)
+
+ # Write some data to the pool so the replacing vdev doesn't complete
+ # immediately.
+ $TIMEOUT 60s $DD if=/dev/zero of=$mntp/zerofile bs=131072
+
+ log_must $ZINJECT -d $FAULT_DISK -A fault $TESTPOOL
+ log_must check_state $TESTPOOL $FAULT_DISK FAULTED
+
+ # Replace the failed device. Realistically, the new device would have
+ # the same physical path as the failed one, but it doesn't matter for
+ # our purposes.
+ log_must $ZPOOL replace $TESTPOOL $FAULT_DISK $REPLACEMENT_DISK
+
+ # Add the spare, and check that it does not activate
+ log_must $ZPOOL add $TESTPOOL spare $SDEV
+
+ # Wait a few seconds before verifying the state
+ $SLEEP 10
+ log_must check_state $TESTPOOL "$SDEV" "AVAIL"
+}
+
+log_onexit cleanup
+
+log_assert "zfsd will not use newly added spares on replacing vdevs"
+
+ensure_zfsd_running
+
+typeset FAULT_DISK=$DISK0
+typeset REPLACEMENT_DISK=$DISK2
+typeset SDEV=$DISK3
+typeset POOLDEVS="$DISK0 $DISK1"
+set -A MY_KEYWORDS "mirror"
+for keyword in "${MY_KEYWORDS[@]}" ; do
+ log_must create_pool $TESTPOOL $keyword $POOLDEVS
+ verify_assertion
+
+ destroy_pool "$TESTPOOL"
+done
diff --git a/tests/sys/cddl/zfs/tests/zfsd/zfsd_import_001_pos.ksh b/tests/sys/cddl/zfs/tests/zfsd/zfsd_import_001_pos.ksh
new file mode 100644
index 000000000000..acc19b247fc7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/zfsd_import_001_pos.ksh
@@ -0,0 +1,147 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2013 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+. $STF_SUITE/tests/zfsd/zfsd.kshlib
+. $STF_SUITE/include/libgnop.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zfsd_import_001_pos
+#
+# DESCRIPTION:
+# If a removed drive gets reinserted while the pool is exported, it will
+# replace its spare when reimported.
+#
+# This also applies to drives that get reinserted while the machine is
+# powered off.
+#
+#
+# STRATEGY:
+# 1. Create 1 storage pools with hot spares.
+# 2. Remove one disk
+# 3. Verify that the spare is in use.
+# 4. Export the pool
+# 5. Recreate the vdev
+# 6. Import the pool
+# 7. Verify that the vdev gets resilvered and the spare gets removed
+# 8. Use additional zpool history data to verify that the pool
+# finished resilvering _before_ zfsd detached the spare.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING STATUS: COMPLETED (2012-08-10)
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "global"
+
+function verify_assertion # spare_dev
+{
+ typeset spare_dev=$1
+ log_must destroy_gnop $REMOVAL_DISK
+
+ # Check to make sure ZFS sees the disk as removed
+ wait_for_pool_removal 20
+
+ # Wait for zfsd to activate the spare
+ wait_for_pool_dev_state_change 20 $spare_dev INUSE
+ log_must $ZPOOL status $TESTPOOL
+
+ # Export the pool
+ log_must $ZPOOL export $TESTPOOL
+
+ # Re-enable the missing disk
+ log_must create_gnop $REMOVAL_DISK
+
+ # Import the pool
+ log_must $ZPOOL import $TESTPOOL
+
+ # Check that the disk has rejoined the pool
+ wait_for_pool_dev_state_change 20 $REMOVAL_DISK ONLINE
+
+ # Check that the pool resilvered
+ while ! is_pool_resilvered $TESTPOOL; do
+ $SLEEP 2
+ done
+ log_must $ZPOOL status $TESTPOOL
+
+ #Finally, check that the spare deactivated
+ wait_for_pool_dev_state_change 20 $spare_dev AVAIL
+
+ # Verify that the spare was detached after the scrub was complete
+ # Note that resilvers and scrubs are recorded identically in zpool
+ # history
+ $ZPOOL history -i $TESTPOOL | awk '
+ BEGIN {
+ scrub_txg=0;
+ detach_txg=0
+ }
+ /scrub done/ {
+ split($6, s, "[:\\]]");
+ t=s[2];
+ scrub_txg = scrub_txg > t ? scrub_txg : t
+ }
+ /vdev detach/ {
+ split($6, s, "[:\\]]");
+ t=s[2];
+ done_txg = done_txg > t ? done_txg : t
+ }
+ END {
+ print("Scrub completed at txg", scrub_txg);
+ print("Spare detached at txg", detach_txg);
+ exit(detach_txg > scrub_txg)
+ }'
+ [ $? -ne 0 ] && log_fail "The spare detached before the resilver completed"
+}
+
+
+log_assert "If a removed drive gets reinserted while the pool is exported, \
+ it will replace its spare when reinserted."
+
+ensure_zfsd_running
+
+typeset REMOVAL_DISK=$DISK0
+typeset REMOVAL_NOP=${DISK0}.nop
+typeset SPARE_DISK=$DISK4
+typeset SPARE_NOP=${DISK4}.nop
+typeset OTHER_DISKS="${DISK1} ${DISK2} ${DISK3}"
+typeset OTHER_NOPS=${OTHER_DISKS//~(E)([[:space:]]+|$)/.nop\1}
+set -A MY_KEYWORDS "mirror" "raidz1" "raidz2"
+ensure_zfsd_running
+log_must create_gnops $REMOVAL_DISK $OTHER_DISKS $SPARE_DISK
+for keyword in "${MY_KEYWORDS[@]}" ; do
+ log_must create_pool $TESTPOOL $keyword $REMOVAL_NOP $OTHER_NOPS spare $SPARE_NOP
+ verify_assertion
+ destroy_pool "$TESTPOOL"
+done
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zfsd/zfsd_replace_001_pos.ksh b/tests/sys/cddl/zfs/tests/zfsd/zfsd_replace_001_pos.ksh
new file mode 100644
index 000000000000..ebae3c062ab6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/zfsd_replace_001_pos.ksh
@@ -0,0 +1,79 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# Copyright 2012,2013 Spectra Logic Corporation. All rights reserved.
+# Use is subject to license terms.
+#
+# Portions taken from:
+
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+. $STF_SUITE/tests/zfsd/zfsd.kshlib
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/include/libgnop.kshlib
+
+log_assert "ZFSD will automatically replace a SAS disk that disappears and reappears in the same location, with the same devname"
+
+ensure_zfsd_running
+
+set_disks
+
+typeset REMOVAL_DISK=$DISK0
+typeset REMOVAL_NOP=${DISK0}.nop
+typeset OTHER_DISKS="${DISK1} ${DISK2}"
+typeset ALLDISKS="${DISK0} ${DISK1} ${DISK2}"
+typeset ALLNOPS=${ALLDISKS//~(E)([[:space:]]+|$)/.nop\1}
+
+log_must create_gnops $ALLDISKS
+for type in "raidz" "mirror"; do
+ # Create a pool on the supplied disks
+ create_pool $TESTPOOL $type $ALLNOPS
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+
+ # Disable the first disk.
+ log_must destroy_gnop $REMOVAL_DISK
+ log_must wait_for_pool_removal 20
+
+ # Write out data to make sure we can do I/O after the disk failure
+ log_must $DD if=/dev/zero of=$TESTDIR/$TESTFILE bs=1m count=1
+ log_must $FSYNC $TESTDIR/$TESTFILE
+
+ # Check to make sure ZFS sees the disk as removed
+ wait_for_pool_dev_state_change 20 $REMOVAL_NOP REMOVED
+
+ # Re-enable the disk
+ log_must create_gnop $REMOVAL_DISK
+
+ # Disk should auto-join the zpool & be resilvered.
+ wait_for_pool_dev_state_change 20 $REMOVAL_NOP ONLINE
+ wait_until_resilvered
+
+ $ZPOOL status $TESTPOOL
+ destroy_pool $TESTPOOL
+ log_must $RM -rf /$TESTPOOL
+done
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zfsd/zfsd_replace_002_pos.ksh b/tests/sys/cddl/zfs/tests/zfsd/zfsd_replace_002_pos.ksh
new file mode 100644
index 000000000000..5be9757f20a6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/zfsd_replace_002_pos.ksh
@@ -0,0 +1,73 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# Copyright 2012-2018 Spectra Logic Corporation. All rights reserved.
+# Use is subject to license terms.
+#
+# Portions taken from:
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/include/libgnop.kshlib
+
+function is_pool_unavail # pool
+{
+ is_pool_state "$1" "UNAVAIL"
+}
+
+log_assert "zfsd will reactivate a pool after all disks are failed and reappeared"
+
+log_unsupported "This feature has not yet been implemented in zfsd"
+
+ensure_zfsd_running
+set_disks
+typeset ALLDISKS="${DISK0} ${DISK1} ${DISK2}"
+typeset ALLNOPS=${ALLDISKS//~(E)([[:space:]]+|$)/.nop\1}
+
+log_must create_gnops $ALLDISKS
+for type in "raidz" "mirror"; do
+ # Create a pool on the supplied disks
+ create_pool $TESTPOOL $type $ALLNOPS
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+
+ # Disable all vdevs. The pool should become UNAVAIL
+ log_must destroy_gnop $DISK0
+ log_must destroy_gnop $DISK1
+ log_must destroy_gnop $DISK2
+ wait_for 5 1 is_pool_unavail $TESTPOOL
+
+ # Renable all vdevs. The pool should become healthy again
+ log_must create_gnop $DISK0
+ log_must create_gnop $DISK1
+ log_must create_gnop $DISK2
+
+ wait_for 5 1 is_pool_healthy $TESTPOOL
+
+ destroy_pool $TESTPOOL
+ log_must $RM -rf /$TESTPOOL
+done
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zfsd/zfsd_replace_003_pos.ksh b/tests/sys/cddl/zfs/tests/zfsd/zfsd_replace_003_pos.ksh
new file mode 100644
index 000000000000..3cd545aff939
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/zfsd_replace_003_pos.ksh
@@ -0,0 +1,122 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# Copyright 2012,2013 Spectra Logic Corporation. All rights reserved.
+# Use is subject to license terms.
+#
+# Portions taken from:
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/include/libgnop.kshlib
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+. $STF_SUITE/tests/zfsd/zfsd.kshlib
+
+function cleanup
+{
+ destroy_pool $TESTPOOL
+ [[ -e $TESTDIR ]] && log_must $RM -rf $TESTDIR/*
+ for md in $MD0 $MD1 $MD2 $MD3; do
+ gnop destroy -f $md
+ for ((i=0; i<5; i=i+1)); do
+ $MDCONFIG -d -u $md && break
+ $SLEEP 1
+ done
+ done
+}
+
+log_assert "ZFSD will correctly replace disks that disappear and reappear \
+ with different devnames"
+
+# Outline
+# Use gnop on top of file-backed md devices
+# * file-backed md devices so we can destroy them and recreate them with
+# different devnames
+# * gnop so we can destroy them while still in use
+# Create a double-parity pool
+# Remove two vdevs
+# Destroy the md devices and recreate in the opposite order
+# Check that the md's devnames have swapped
+# Verify that the pool regains its health
+
+log_onexit cleanup
+ensure_zfsd_running
+
+
+N_DEVARRAY_FILES=4
+set_devs
+typeset FILE0="${devarray[0]}"
+typeset FILE1="${devarray[1]}"
+typeset FILE2="${devarray[2]}"
+typeset FILE3="${devarray[3]}"
+typeset MD0=`$MDCONFIG -a -t vnode -f ${FILE0}`
+[ $? -eq 0 ] || atf_fail "Failed to create md device"
+typeset MD1=`$MDCONFIG -a -t vnode -f ${FILE1}`
+[ $? -eq 0 ] || atf_fail "Failed to create md device"
+typeset MD2=`$MDCONFIG -a -t vnode -f ${FILE2}`
+[ $? -eq 0 ] || atf_fail "Failed to create md device"
+typeset MD3=`$MDCONFIG -a -t vnode -f ${FILE3}`
+[ $? -eq 0 ] || atf_fail "Failed to create md device"
+log_must create_gnops $MD0 $MD1 $MD2 $MD3
+
+for type in "raidz2" "mirror"; do
+ # Create a pool on the supplied disks
+ create_pool $TESTPOOL $type ${MD0}.nop ${MD1}.nop ${MD2}.nop ${MD3}.nop
+
+ log_must destroy_gnop $MD0
+ for ((i=0; i<5; i=i+1)); do
+ $MDCONFIG -d -u $MD0 && break
+ $SLEEP 1
+ done
+ [ -c /dev/$MD0.nop ] && atf_fail "failed to destroy $MD0"
+ log_must destroy_gnop $MD1
+ for ((i=0; i<5; i=i+1)); do
+ $MDCONFIG -d -u $MD1 && break
+ $SLEEP 1
+ done
+ [ -c /dev/$MD1.nop ] && atf_fail "failed to destroy $MD0"
+
+ # Make sure that the pool is degraded
+ $ZPOOL status $TESTPOOL |grep "state:" |grep DEGRADED > /dev/null
+ if [ $? != 0 ]; then
+ log_fail "Pool $TESTPOOL not listed as DEGRADED"
+ fi
+
+ # Do some I/O to ensure that the old vdevs will be out of date
+ log_must $DD if=/dev/random of=/$TESTPOOL/randfile bs=1m count=1
+ log_must $SYNC
+
+ # Recreate the vdevs in the opposite order
+ typeset MD0=`$MDCONFIG -a -t vnode -f ${FILE1}`
+ [ $? -eq 0 ] || atf_fail "Failed to create md device"
+ typeset MD1=`$MDCONFIG -a -t vnode -f ${FILE0}`
+ [ $? -eq 0 ] || atf_fail "Failed to create md device"
+ log_must create_gnops $MD0 $MD1
+
+ wait_until_resilvered
+ destroy_pool $TESTPOOL
+done
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zfsd/zfsd_replace_004_pos.ksh b/tests/sys/cddl/zfs/tests/zfsd/zfsd_replace_004_pos.ksh
new file mode 100644
index 000000000000..4abd0ce0a89b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/zfsd_replace_004_pos.ksh
@@ -0,0 +1,63 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2023 Axcient. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+. $STF_SUITE/tests/zfsd/zfsd.kshlib
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/include/libgnop.kshlib
+
+log_assert "ZFSD will automatically replace a spare that disappears and reappears in the same location, with the same devname"
+
+ensure_zfsd_running
+
+set_disks
+
+typeset DISK0_NOP=${DISK0}.nop
+typeset DISK1_NOP=${DISK1}.nop
+
+log_must create_gnops $DISK0 $DISK1
+
+# Create a pool on the supplied disks
+create_pool $TESTPOOL $DISK0_NOP spare $DISK1_NOP
+
+# Disable the first disk.
+log_must destroy_gnop $DISK1
+
+# Check to make sure ZFS sees the disk as removed
+wait_for_pool_dev_state_change 20 $DISK1_NOP REMOVED
+
+# Re-enable the disk
+log_must create_gnop $DISK1
+
+# Disk should auto-join the zpool
+wait_for_pool_dev_state_change 20 $DISK1_NOP AVAIL
+
+$ZPOOL status $TESTPOOL
+destroy_pool $TESTPOOL
+log_must $RM -rf /$TESTPOOL
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zfsd/zfsd_replace_005_pos.ksh b/tests/sys/cddl/zfs/tests/zfsd/zfsd_replace_005_pos.ksh
new file mode 100644
index 000000000000..2541db573221
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/zfsd_replace_005_pos.ksh
@@ -0,0 +1,69 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2023 Axcient. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/tests/hotspare/hotspare.kshlib
+. $STF_SUITE/tests/zfsd/zfsd.kshlib
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/include/libgnop.kshlib
+
+log_assert "ZFSD will automatically replace a multi-pool spare that disappears and reappears"
+
+ensure_zfsd_running
+
+set_disks
+
+typeset DISK0_NOP=${DISK0}.nop
+typeset DISK1_NOP=${DISK1}.nop
+typeset DISK2_NOP=${DISK2}.nop
+
+log_must create_gnops $DISK0 $DISK1 $DISK2
+
+# Create pools on the supplied disks
+create_pool $TESTPOOL $DISK0_NOP spare $DISK2_NOP
+create_pool $TESTPOOL1 $DISK1_NOP spare $DISK2_NOP
+
+# Disable the spare disk.
+log_must destroy_gnop $DISK2
+
+# Check to make sure ZFS sees the disk as removed
+wait_for_pool_dev_state_change 20 $DISK2_NOP REMOVED
+wait_for_pool_dev_state_change 20 $DISK2_NOP REMOVED $TESTPOOL1
+
+# Re-enable the disk
+log_must create_gnop $DISK2
+
+# Disk should auto-join the zpools
+wait_for_pool_dev_state_change 20 $DISK2_NOP AVAIL
+wait_for_pool_dev_state_change 20 $DISK2_NOP AVAIL $TESTPOOL1
+
+$ZPOOL status $TESTPOOL
+$ZPOOL status $TESTPOOL1
+destroy_pool $TESTPOOL
+destroy_pool $TESTPOOL1
+log_must $RM -rf /$TESTPOOL
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zfsd/zfsd_test.sh b/tests/sys/cddl/zfs/tests/zfsd/zfsd_test.sh
new file mode 100755
index 000000000000..fe4ac4866ed3
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zfsd/zfsd_test.sh
@@ -0,0 +1,703 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012,2013 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zfsd_fault_001_pos cleanup
+zfsd_fault_001_pos_head()
+{
+ atf_set "descr" "ZFS will fault a vdev that produces IO errors"
+ atf_set "require.progs" "ksh93 gnop zfs zpool zfsd"
+ atf_set "timeout" 300
+}
+zfsd_fault_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/../hotspare/hotspare.kshlib
+ . $(atf_get_srcdir)/../hotspare/hotspare.cfg
+ . $(atf_get_srcdir)/zfsd.cfg
+
+ verify_disk_count "$DISKS" 2
+ verify_zfsd_running
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfsd_fault_001_pos.ksh
+ if [[ $? != 0 ]]; then
+ save_artifacts
+ atf_fail "Testcase failed"
+ fi
+}
+zfsd_fault_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zfsd.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfsd_fault_002_pos cleanup
+zfsd_fault_002_pos_head()
+{
+ atf_set "descr" "ZFS will fault a vdev that experiences delayed I/O"
+ atf_set "require.progs" "ksh93 gnop zfs zpool zfsd"
+ atf_set "timeout" 300
+}
+zfsd_fault_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/../hotspare/hotspare.kshlib
+ . $(atf_get_srcdir)/../hotspare/hotspare.cfg
+ . $(atf_get_srcdir)/zfsd.cfg
+
+ verify_disk_count "$DISKS" 2
+ verify_zfsd_running
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfsd_fault_002_pos.ksh
+ if [[ $? != 0 ]]; then
+ save_artifacts
+ atf_fail "Testcase failed"
+ fi
+}
+zfsd_fault_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zfsd.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case zfsd_degrade_001_pos cleanup
+zfsd_degrade_001_pos_head()
+{
+ atf_set "descr" "ZFS will degrade a vdev that produces checksum errors"
+ atf_set "require.progs" "ksh93 zpool zfsd"
+ atf_set "timeout" 600
+}
+zfsd_degrade_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zfsd.cfg
+
+ verify_disk_count "$DISKS" 2
+ verify_zfsd_running
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfsd_degrade_001_pos.ksh
+ if [[ $? != 0 ]]; then
+ save_artifacts
+ atf_fail "Testcase failed"
+ fi
+}
+zfsd_degrade_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zfsd.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case zfsd_degrade_002_pos cleanup
+zfsd_degrade_002_pos_head()
+{
+ atf_set "descr" "ZFS will degrade a spare that produces checksum errors"
+ atf_set "require.progs" "ksh93 zpool zfsd"
+ atf_set "timeout" 600
+}
+zfsd_degrade_002_pos_body()
+{
+ atf_expect_fail "https://www.illumos.org/issues/8614 Checksum errors on a mirrored child of a raidz are incorrectly accounted"
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zfsd.cfg
+
+ verify_disk_count "$DISKS" 5
+ verify_zfsd_running
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfsd_degrade_002_pos.ksh
+ if [[ $? != 0 ]]; then
+ save_artifacts
+ atf_fail "Testcase failed"
+ fi
+}
+zfsd_degrade_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zfsd.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfsd_hotspare_001_pos cleanup
+zfsd_hotspare_001_pos_head()
+{
+ atf_set "descr" "An active, damaged spare will be replaced by an available spare"
+ atf_set "require.progs" "ksh93 zpool zfsd"
+ atf_set "timeout" 3600
+}
+zfsd_hotspare_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/../hotspare/hotspare.kshlib
+ . $(atf_get_srcdir)/../hotspare/hotspare.cfg
+
+ verify_zfsd_running
+ ksh93 $(atf_get_srcdir)/hotspare_setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfsd_hotspare_001_pos.ksh
+ if [[ $? != 0 ]]; then
+ save_artifacts
+ atf_fail "Testcase failed"
+ fi
+}
+zfsd_hotspare_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/../hotspare/hotspare.kshlib
+ . $(atf_get_srcdir)/../hotspare/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/hotspare_cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case zfsd_hotspare_002_pos cleanup
+zfsd_hotspare_002_pos_head()
+{
+ atf_set "descr" "If a vdev becomes degraded, the spare will be activated."
+ atf_set "require.progs" "ksh93 zpool zfsd zinject"
+ atf_set "timeout" 3600
+}
+zfsd_hotspare_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/../hotspare/hotspare.kshlib
+ . $(atf_get_srcdir)/../hotspare/hotspare.cfg
+
+ verify_zfsd_running
+ ksh93 $(atf_get_srcdir)/hotspare_setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfsd_hotspare_002_pos.ksh
+ if [[ $? != 0 ]]; then
+ save_artifacts
+ atf_fail "Testcase failed"
+ fi
+}
+zfsd_hotspare_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/../hotspare/hotspare.kshlib
+ . $(atf_get_srcdir)/../hotspare/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/hotspare_cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfsd_hotspare_003_pos cleanup
+zfsd_hotspare_003_pos_head()
+{
+ atf_set "descr" "A faulted vdev will be replaced by an available spare"
+ atf_set "require.progs" "ksh93 zpool zfsd zinject"
+ atf_set "timeout" 3600
+}
+zfsd_hotspare_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/../hotspare/hotspare.kshlib
+ . $(atf_get_srcdir)/../hotspare/hotspare.cfg
+
+ verify_disk_count "$DISKS" 5
+ verify_zfsd_running
+ ksh93 $(atf_get_srcdir)/hotspare_setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfsd_hotspare_003_pos.ksh
+ if [[ $? != 0 ]]; then
+ save_artifacts
+ atf_fail "Testcase failed"
+ fi
+}
+zfsd_hotspare_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/../hotspare/hotspare.kshlib
+ . $(atf_get_srcdir)/../hotspare/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/hotspare_cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case zfsd_hotspare_004_pos cleanup
+zfsd_hotspare_004_pos_head()
+{
+ atf_set "descr" "Removing a disk from a pool results in the spare activating"
+ atf_set "require.progs" "ksh93 gnop zpool"
+ atf_set "timeout" 3600
+}
+zfsd_hotspare_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/../hotspare/hotspare.kshlib
+ . $(atf_get_srcdir)/../hotspare/hotspare.cfg
+
+ verify_disk_count "$DISKS" 5
+ verify_zfsd_running
+ ksh93 $(atf_get_srcdir)/hotspare_setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfsd_hotspare_004_pos.ksh
+ if [[ $? != 0 ]]; then
+ save_artifacts
+ atf_fail "Testcase failed"
+ fi
+}
+zfsd_hotspare_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/../hotspare/hotspare.kshlib
+ . $(atf_get_srcdir)/../hotspare/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case zfsd_hotspare_005_pos cleanup
+zfsd_hotspare_005_pos_head()
+{
+ atf_set "descr" "A spare that is added to a degraded pool will be activated"
+ atf_set "require.progs" "ksh93 zpool zfsd zinject"
+ atf_set "timeout" 3600
+}
+zfsd_hotspare_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/../hotspare/hotspare.kshlib
+ . $(atf_get_srcdir)/../hotspare/hotspare.cfg
+
+ verify_zfsd_running
+ ksh93 $(atf_get_srcdir)/hotspare_setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfsd_hotspare_005_pos.ksh
+ if [[ $? != 0 ]]; then
+ save_artifacts
+ atf_fail "Testcase failed"
+ fi
+}
+zfsd_hotspare_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/../hotspare/hotspare.kshlib
+ . $(atf_get_srcdir)/../hotspare/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/hotspare_cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case zfsd_hotspare_006_pos cleanup
+zfsd_hotspare_006_pos_head()
+{
+ atf_set "descr" "zfsd will replace two vdevs that fail simultaneously"
+ atf_set "require.progs" "ksh93 zpool zfsd zinject"
+ atf_set "timeout" 3600
+}
+zfsd_hotspare_006_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/../hotspare/hotspare.kshlib
+ . $(atf_get_srcdir)/../hotspare/hotspare.cfg
+
+ verify_zfsd_running
+ ksh93 $(atf_get_srcdir)/hotspare_setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfsd_hotspare_006_pos.ksh
+ if [[ $? != 0 ]]; then
+ save_artifacts
+ atf_fail "Testcase failed"
+ fi
+}
+zfsd_hotspare_006_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/../hotspare/hotspare.kshlib
+ . $(atf_get_srcdir)/../hotspare/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/hotspare_cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case zfsd_hotspare_007_pos cleanup
+zfsd_hotspare_007_pos_head()
+{
+ atf_set "descr" "zfsd will swap failed drives at startup"
+ atf_set "require.progs" "ksh93 gnop zpool"
+ atf_set "timeout" 3600
+}
+zfsd_hotspare_007_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/../hotspare/hotspare.kshlib
+ . $(atf_get_srcdir)/../hotspare/hotspare.cfg
+
+ verify_disk_count "$DISKS" 5
+ verify_zfsd_running
+ ksh93 $(atf_get_srcdir)/hotspare_setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfsd_hotspare_007_pos.ksh
+ if [[ $? != 0 ]]; then
+ save_artifacts
+ atf_fail "Testcase failed"
+ fi
+}
+zfsd_hotspare_007_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/../hotspare/hotspare.kshlib
+ . $(atf_get_srcdir)/../hotspare/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case zfsd_hotspare_008_neg cleanup
+zfsd_hotspare_008_neg_head()
+{
+ atf_set "descr" "zfsd will not use newly added spares on replacing vdevs"
+ atf_set "require.progs" "ksh93 zpool zfsd"
+ atf_set "timeout" 3600
+}
+zfsd_hotspare_008_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/../hotspare/hotspare.kshlib
+ . $(atf_get_srcdir)/../hotspare/hotspare.cfg
+
+ verify_disk_count "$DISKS" 4
+ ksh93 $(atf_get_srcdir)/hotspare_setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfsd_hotspare_008_neg.ksh
+ if [[ $? != 0 ]]; then
+ save_artifacts
+ atf_fail "Testcase failed"
+ fi
+}
+zfsd_hotspare_008_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/../hotspare/hotspare.kshlib
+ . $(atf_get_srcdir)/../hotspare/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/hotspare_cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case zfsd_autoreplace_001_neg cleanup
+zfsd_autoreplace_001_neg_head()
+{
+ atf_set "descr" "A pool without autoreplace set will not replace by physical path"
+ atf_set "require.progs" "ksh93 zpool gnop"
+ atf_set "timeout" 3600
+}
+zfsd_autoreplace_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/../hotspare/hotspare.kshlib
+ . $(atf_get_srcdir)/../hotspare/hotspare.cfg
+
+ verify_disk_count "$DISKS" 5
+ ksh93 $(atf_get_srcdir)/hotspare_setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfsd_autoreplace_001_neg.ksh
+ if [[ $? != 0 ]]; then
+ save_artifacts
+ atf_fail "Testcase failed"
+ fi
+}
+zfsd_autoreplace_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/../hotspare/hotspare.kshlib
+ . $(atf_get_srcdir)/../hotspare/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case zfsd_autoreplace_002_pos cleanup
+zfsd_autoreplace_002_pos_head()
+{
+ atf_set "descr" "A pool with autoreplace set will replace by physical path"
+ atf_set "require.progs" "ksh93 gnop zpool zfsd"
+ atf_set "timeout" 3600
+}
+zfsd_autoreplace_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/../hotspare/hotspare.kshlib
+ . $(atf_get_srcdir)/../hotspare/hotspare.cfg
+
+ verify_disk_count "$DISKS" 5
+ verify_zfsd_running
+ ksh93 $(atf_get_srcdir)/hotspare_setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfsd_autoreplace_002_pos.ksh
+ if [[ $? != 0 ]]; then
+ save_artifacts
+ atf_fail "Testcase failed"
+ fi
+}
+zfsd_autoreplace_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/../hotspare/hotspare.kshlib
+ . $(atf_get_srcdir)/../hotspare/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case zfsd_autoreplace_003_pos cleanup
+zfsd_autoreplace_003_pos_head()
+{
+ atf_set "descr" "A pool with autoreplace set will replace by physical path even if a spare is active"
+ atf_set "require.progs" "ksh93 zpool gnop"
+ atf_set "timeout" 3600
+}
+zfsd_autoreplace_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/../hotspare/hotspare.kshlib
+ . $(atf_get_srcdir)/../hotspare/hotspare.cfg
+
+ verify_disk_count "$DISKS" 5
+ verify_zfsd_running
+ ksh93 $(atf_get_srcdir)/hotspare_setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfsd_autoreplace_003_pos.ksh
+ if [[ $? != 0 ]]; then
+ save_artifacts
+ atf_fail "Testcase failed"
+ fi
+}
+zfsd_autoreplace_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/../hotspare/hotspare.kshlib
+ . $(atf_get_srcdir)/../hotspare/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case zfsd_replace_001_pos cleanup
+zfsd_replace_001_pos_head()
+{
+ atf_set "descr" "ZFSD will automatically replace a SAS disk that disappears and reappears in the same location, with the same devname"
+ atf_set "require.progs" "ksh93 zpool zfs gnop"
+}
+zfsd_replace_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zfsd.cfg
+
+ verify_disk_count "$DISKS" 3
+ verify_zfsd_running
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfsd_replace_001_pos.ksh
+ if [[ $? != 0 ]]; then
+ save_artifacts
+ atf_fail "Testcase failed"
+ fi
+}
+zfsd_replace_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zfsd.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zfsd_replace_002_pos cleanup
+zfsd_replace_002_pos_head()
+{
+ atf_set "descr" "zfsd will reactivate a pool after all disks are failed and reappeared"
+ atf_set "require.progs" "ksh93 zpool zfs"
+}
+zfsd_replace_002_pos_body()
+{
+ atf_expect_fail "Not yet implemented in zfsd"
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zfsd.cfg
+
+ verify_disk_count "$DISKS" 3
+ verify_zfsd_running
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfsd_replace_002_pos.ksh
+ if [[ $? != 0 ]]; then
+ save_artifacts
+ atf_fail "Testcase failed"
+ fi
+}
+zfsd_replace_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zfsd.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case zfsd_replace_003_pos cleanup
+zfsd_replace_003_pos_head()
+{
+ atf_set "descr" "ZFSD will correctly replace disks that dissapear and reappear with different devnames"
+ atf_set "require.progs" "ksh93 zpool zfs gnop"
+}
+zfsd_replace_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/../hotspare/hotspare.kshlib
+ . $(atf_get_srcdir)/../hotspare/hotspare.cfg
+
+ verify_zfsd_running
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfsd_replace_003_pos.ksh
+ if [[ $? != 0 ]]; then
+ save_artifacts
+ atf_fail "Testcase failed"
+ fi
+}
+zfsd_replace_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zfsd.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case zfsd_replace_004_pos cleanup
+zfsd_replace_004_pos_head()
+{
+ atf_set "descr" "ZFSD will automatically replace a spare that disappears and reappears in the same location, with the same devname"
+ atf_set "require.progs" "ksh93 zpool zfs gnop"
+}
+zfsd_replace_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zfsd.cfg
+
+ verify_disk_count "$DISKS" 2
+ verify_zfsd_running
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfsd_replace_004_pos.ksh
+ if [[ $? != 0 ]]; then
+ save_artifacts
+ atf_fail "Testcase failed"
+ fi
+}
+zfsd_replace_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zfsd.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case zfsd_replace_005_pos cleanup
+zfsd_replace_005_pos_head()
+{
+ atf_set "descr" "ZFSD will automatically replace a multi-pool spare that disappears and reappears"
+ atf_set "require.progs" "ksh93 zpool zfs gnop"
+}
+zfsd_replace_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zfsd.cfg
+
+ verify_disk_count "$DISKS" 3
+ verify_zfsd_running
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfsd_replace_005_pos.ksh
+ if [[ $? != 0 ]]; then
+ save_artifacts
+ atf_fail "Testcase failed"
+ fi
+}
+zfsd_replace_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zfsd.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+atf_test_case zfsd_import_001_pos cleanup
+zfsd_import_001_pos_head()
+{
+ atf_set "descr" "If a removed drive gets reinserted while the pool is exported, it will detach its spare when imported."
+ atf_set "require.progs" "ksh93 gnop zfsd zpool"
+ atf_set "timeout" 3600
+}
+zfsd_import_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/../hotspare/hotspare.kshlib
+ . $(atf_get_srcdir)/../hotspare/hotspare.cfg
+
+ verify_disk_count "$DISKS" 5
+ verify_zfsd_running
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zfsd_import_001_pos.ksh || atf_fail "Testcase failed"
+ if [[ $? != 0 ]]; then
+ save_artifacts
+ atf_fail "Testcase failed"
+ fi
+}
+zfsd_import_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/../hotspare/hotspare.kshlib
+ . $(atf_get_srcdir)/../hotspare/hotspare.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+
+
+atf_init_test_cases()
+{
+ atf_add_test_case zfsd_fault_001_pos
+ atf_add_test_case zfsd_fault_002_pos
+ atf_add_test_case zfsd_degrade_001_pos
+ atf_add_test_case zfsd_degrade_002_pos
+ atf_add_test_case zfsd_hotspare_001_pos
+ atf_add_test_case zfsd_hotspare_002_pos
+ atf_add_test_case zfsd_hotspare_003_pos
+ atf_add_test_case zfsd_hotspare_004_pos
+ atf_add_test_case zfsd_hotspare_005_pos
+ atf_add_test_case zfsd_hotspare_006_pos
+ atf_add_test_case zfsd_hotspare_007_pos
+ atf_add_test_case zfsd_hotspare_008_neg
+ atf_add_test_case zfsd_autoreplace_001_neg
+ atf_add_test_case zfsd_autoreplace_002_pos
+ atf_add_test_case zfsd_autoreplace_003_pos
+ atf_add_test_case zfsd_replace_001_pos
+ atf_add_test_case zfsd_replace_002_pos
+ atf_add_test_case zfsd_replace_003_pos
+ atf_add_test_case zfsd_replace_004_pos
+ atf_add_test_case zfsd_replace_005_pos
+ atf_add_test_case zfsd_import_001_pos
+}
+
+save_artifacts()
+{
+ # If ARTIFACTS_DIR is defined, save test artifacts for
+ # post-mortem analysis
+ if [[ -n $ARTIFACTS_DIR ]]; then
+ TC_ARTIFACTS_DIR=${ARTIFACTS_DIR}/sys/cddl/zfs/tests/zfsd/$(atf_get ident)
+ mkdir -p $TC_ARTIFACTS_DIR
+ cp -a /var/log/zfsd.log* $TC_ARTIFACTS_DIR
+ bzip2 $TC_ARTIFACTS_DIR/zfsd.log
+ fi
+}
+
+verify_zfsd_running()
+{
+ service zfsd onestatus || \
+ atf_skip "zfsd(8) must be enabled and running for this test"
+}
diff --git a/tests/sys/cddl/zfs/tests/zil/Makefile b/tests/sys/cddl/zfs/tests/zil/Makefile
new file mode 100644
index 000000000000..db30bfc9f6ec
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zil/Makefile
@@ -0,0 +1,18 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/zil
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zil_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zil_002_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zil.cfg
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zil_001_pos.ksh
+${PACKAGE}FILES+= zil.kshlib
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/zil/cleanup.ksh b/tests/sys/cddl/zfs/tests/zil/cleanup.ksh
new file mode 100644
index 000000000000..126a2331bc17
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zil/cleanup.ksh
@@ -0,0 +1,34 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+# The tests may leave the drive in an exported state, so we must explicitly
+# clear its label with labelclear instead of relying on zpool destroy
+DISK=${DISKS%% *}
+$ZPOOL destroy -f $TESTPOOL
+[ -n "$DISK" ] && $ZPOOL labelclear -f $DISK
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zil/setup.ksh b/tests/sys/cddl/zfs/tests/zil/setup.ksh
new file mode 100644
index 000000000000..3dd1926df13b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zil/setup.ksh
@@ -0,0 +1,35 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic Corporation. All rights reserved.
+# Use is subject to license terms.
+#
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+DISK=${DISKS%% *}
+
+default_setup_noexit $DISK
+log_must $ZFS set compression=on $TESTPOOL
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zil/zil.cfg b/tests/sys/cddl/zfs/tests/zil/zil.cfg
new file mode 100644
index 000000000000..871f387a5bc9
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zil/zil.cfg
@@ -0,0 +1,29 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic Corporation. All rights reserved.
+# Use is subject to license terms.
+#
+
+export POOLSIZE=1073741824 # 100MiB
+export BLOCK_SIZE=4096
diff --git a/tests/sys/cddl/zfs/tests/zil/zil.kshlib b/tests/sys/cddl/zfs/tests/zil/zil.kshlib
new file mode 100644
index 000000000000..d5997f215112
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zil/zil.kshlib
@@ -0,0 +1,53 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic Corporation. All rights reserved.
+# Use is subject to license terms.
+#
+
+# Every test must restart the frozen pool state.
+function zil_setup
+{
+ log_must $ZPOOL list $TESTPOOL
+
+ # In order for a frozen zpool to have a ZIL, it is necessary to
+ # sync it first. If a sync is not done first, no ZIL will exist,
+ # and consequently no post-freeze log records will be created.
+ log_must $TOUCH $TESTDIR/initial_data
+ log_must $FSYNC $TESTDIR/initial_data
+ log_must $ZPOOL freeze $TESTPOOL
+ log_must filesys_has_zil $TESTPOOL
+ return 0
+}
+
+function zil_reimport_pool
+{
+ typeset pool="$1"
+ log_must filesys_has_zil $pool
+ log_must pool_maps_intact $pool
+ log_must $ZPOOL export $pool
+ log_must $ZPOOL import $pool
+ log_must pool_maps_intact $pool
+ log_mustnot filesys_has_zil $pool
+ return 0
+}
diff --git a/tests/sys/cddl/zfs/tests/zil/zil_001_pos.ksh b/tests/sys/cddl/zfs/tests/zil/zil_001_pos.ksh
new file mode 100644
index 000000000000..c5c883783db1
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zil/zil_001_pos.ksh
@@ -0,0 +1,82 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic Corporation. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/zil/zil.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zil_001_pos
+#
+# DESCRIPTION:
+#
+# XXX XXX XXX
+#
+# STRATEGY:
+# 1) XXX
+# 2) XXX
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (YYYY-MM-DD) XXX
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "both"
+
+function cleanup
+{
+ log_must $LS -lr $TESTDIR
+ log_must $RM -rf $TESTDIR/*
+}
+
+log_onexit cleanup
+
+log_assert "Verify that basic files and directory operations work"
+
+zil_setup
+log_must $TOUCH $TESTDIR/0
+log_must $MV $TESTDIR/0 $TESTDIR/1
+log_must ln -s $TESTDIR/1 $TESTDIR/2
+log_must ln $TESTDIR/1 $TESTDIR/3
+log_must $MKDIR $TESTDIR/4
+log_must $RMDIR $TESTDIR/4
+
+zil_reimport_pool $TESTPOOL
+log_mustnot test -f $TESTDIR/0
+log_must test -f $TESTDIR/1
+log_must test -L $TESTDIR/2
+log_must test -e $TESTDIR/3
+log_mustnot test -d $TESTDIR/4
+
+log_pass "Success running basic files and directory operations"
diff --git a/tests/sys/cddl/zfs/tests/zil/zil_002_pos.ksh b/tests/sys/cddl/zfs/tests/zil/zil_002_pos.ksh
new file mode 100644
index 000000000000..833b293a8fb6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zil/zil_002_pos.ksh
@@ -0,0 +1,104 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic Corporation. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/zil/zil.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zil_002_pos
+#
+# DESCRIPTION:
+#
+# XXX XXX XXX
+#
+# STRATEGY:
+# 1) XXX
+# 2) XXX
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (YYYY-MM-DD) XXX
+#
+# __stc_assertion_end
+#
+###############################################################################
+
+verify_runnable "both"
+
+td5=$TESTDIR/5
+tf1=$td5/1
+tf2=$td5/2
+tf3=$td5/3
+file_size=`expr $POOLSIZE / 4`
+write_count=`expr $file_size / $BLOCK_SIZE`
+
+function check_file
+{
+ typeset fname="$1"
+ typeset -i expected_size="$2"
+
+ log_must test -f $fname
+ log_must test $expected_size == $(size_of_file $fname)
+}
+
+function cleanup
+{
+ ls -lr $TESTDIR
+ log_must $RM -rf $TESTDIR/*
+}
+
+log_onexit cleanup
+
+log_assert "Verify that creating and deleting content works"
+
+# Run the pre-export tests.
+zil_setup
+log_must $MKDIR $td5
+log_must $FILE_WRITE -o create -f $tf1 -b $BLOCK_SIZE -c $write_count -d 0
+check_file $tf1 $file_size
+log_must $CP $tf1 $tf2
+log_must $CP $tf2 $tf3
+check_file $tf2 $file_size
+log_must $CMP $tf1 $tf2
+log_must $RM -f $tf3
+
+# Now run the post-export tests.
+zil_reimport_pool $TESTPOOL
+check_file $tf1 $file_size
+check_file $tf2 $file_size
+log_must $CMP $tf1 $tf2
+log_mustnot test -f $tf3
+log_must test -f $tf1
+cur_file_size=$(size_of_file $tf1)
+log_must test $file_size -eq $cur_file_size
+
+log_pass "Success creating and deleting content"
diff --git a/tests/sys/cddl/zfs/tests/zil/zil_test.sh b/tests/sys/cddl/zfs/tests/zil/zil_test.sh
new file mode 100755
index 000000000000..73e7e5ec278e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zil/zil_test.sh
@@ -0,0 +1,84 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zil_001_pos cleanup
+zil_001_pos_head()
+{
+ atf_set "descr" "Verify that basic files and directory operations work"
+ atf_set "require.progs" "ksh93 zfs zpool zdb"
+}
+zil_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zil.kshlib
+ . $(atf_get_srcdir)/zil.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zil_001_pos.ksh || atf_fail "Testcase failed"
+}
+zil_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zil.kshlib
+ . $(atf_get_srcdir)/zil.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zil_002_pos cleanup
+zil_002_pos_head()
+{
+ atf_set "descr" "Verify that creating and deleting content works"
+ atf_set "require.progs" "ksh93 zfs zpool zdb"
+}
+zil_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zil.kshlib
+ . $(atf_get_srcdir)/zil.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zil_002_pos.ksh || atf_fail "Testcase failed"
+}
+zil_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zil.kshlib
+ . $(atf_get_srcdir)/zil.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zil_001_pos
+ atf_add_test_case zil_002_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/zinject/Makefile b/tests/sys/cddl/zfs/tests/zinject/Makefile
new file mode 100644
index 000000000000..6f37d7060abf
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zinject/Makefile
@@ -0,0 +1,20 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/zinject
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zinject_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zinject.cfg
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zinject_004_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zinject_001_pos.ksh
+${PACKAGE}FILES+= zinject.kshlib
+${PACKAGE}FILES+= zinject_003_pos.ksh
+${PACKAGE}FILES+= zinject_002_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/zinject/cleanup.ksh b/tests/sys/cddl/zfs/tests/zinject/cleanup.ksh
new file mode 100644
index 000000000000..56cdaeb2a7ac
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zinject/cleanup.ksh
@@ -0,0 +1,35 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/zinject/zinject.kshlib
+
+verify_runnable "global"
+
+cleanup_env
+
+cleanup_devices $DISKS
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zinject/setup.ksh b/tests/sys/cddl/zfs/tests/zinject/setup.ksh
new file mode 100644
index 000000000000..fd0b7f98bf8a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zinject/setup.ksh
@@ -0,0 +1,47 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+verify_runnable "global"
+
+if [[ -n $DISK ]]; then
+ #
+ # Use 'zpool create' to clean up the infomation in
+ # in the given disk to avoid slice overlapping.
+ #
+ cleanup_devices $DISK
+
+ partition_disk $SIZE $DISK 7
+else
+ for disk in `$ECHO $DISKSARRAY`; do
+ cleanup_devices $disk
+
+ partition_disk $SIZE $disk 7
+ done
+fi
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zinject/zinject.cfg b/tests/sys/cddl/zfs/tests/zinject/zinject.cfg
new file mode 100644
index 000000000000..8cdb03482698
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zinject/zinject.cfg
@@ -0,0 +1,58 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/cli_root/cli.cfg
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cache/cache.kshlib
+
+if [[ -e /usr/sbin/zinject ]] ; then
+ export ZINJECT="/usr/sbin/zinject"
+else
+ log_untested "zinject is not integrated."
+fi
+
+export STF_TIMEOUT=1800
+export DISK_ARRAY_LIMIT=4
+export DISKSARRAY=""
+
+set_disks
+
+export SIZE="200m"
+
+export VDEV0=${DISK0}p1
+export VDEV1=${DISK0}p2
+export VDEV2=${DISK0}p3
+export VDEV3=${DISK0}p4
+export VDEV4=${DISK0}p5
+export VDEV5=${DISK0}p6
+export VDEV6=${DISK0}p7
+
+export pooldevs="$VDEV0 $VDEV1 $VDEV2"
+export sparedevs="$VDEV3"
+
+export logdevs="$VDEV4"
+
+export alldevs="$pooldevs $sparedevs $logdevs $cachedevs"
diff --git a/tests/sys/cddl/zfs/tests/zinject/zinject.kshlib b/tests/sys/cddl/zfs/tests/zinject/zinject.kshlib
new file mode 100644
index 000000000000..c527e88a1f08
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zinject/zinject.kshlib
@@ -0,0 +1,222 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/cli_root/zfs_set/zfs_set_common.kshlib
+. $STF_SUITE/tests/cache/cache.kshlib
+
+function cleanup_env
+{
+ inject_clear
+ poolexists $TESTPOOL && \
+ destroy_pool $TESTPOOL
+ [[ -d $TESTDIR ]] && \
+ log_must $RM -rf $TESTDIR
+}
+
+#
+# Inject an error into object
+# $1: data,dnode,mos,mosdir,config,bplist,spacemap,metaslab,errlog
+# $2: if $1 is data or dnode, $2 should be a file or dir.
+# otherwise, $2 should be poolname
+# $3: "io" or "checksum"
+# $4: expect return value of zinject, default is 0
+#
+function inject_fault #type, object, error, expect
+{
+ typeset type=$1
+ typeset object=$2
+ typeset error=${3:-io}
+ typeset -i expect=${4:-0}
+
+ if (( expect == 0 )); then
+ log_must eval '$ZINJECT -t $type -e $error \
+ -m -a -q $object > /dev/null 2>&1'
+ else
+ log_mustnot eval '$ZINJECT -t $type -e $error \
+ -m -a -q $object > /dev/null 2>&1'
+ fi
+ $SLEEP 1
+ return 0
+}
+
+#
+# Clear all registrated handler and do scrub to keep integrity
+#
+function inject_clear
+{
+ log_must eval '$ZINJECT -c all > /dev/null 2>&1'
+ $SLEEP 1
+ if poolexists $TESTPOOL ; then
+ while ! check_pool_status $TESTPOOL "state" "ONLINE" ; do
+ log_must $ZPOOL clear $TESTPOOL
+ $SLEEP 2
+ done
+ log_must $ZPOOL scrub $TESTPOOL
+ while ! is_pool_scrubbed $TESTPOOL && ! is_pool_resilvered $TESTPOOL ; do
+ $SLEEP 2
+ done
+ fi
+ return 0
+}
+
+#
+# Inject a fault into a particular device
+# $1: device name
+# $2: pool name
+# $3: errno, can either be 'nxio' (the default) or 'io'.
+#
+function inject_device #device, pool, errno
+{
+ typeset device=$1
+ typeset pool=$2
+ typeset errno=$3
+
+ log_must eval '$ZINJECT -d $device -e $errno -q $pool > /dev/null 2>&1'
+ $SLEEP 1
+ return 0
+}
+
+#
+# Check if the ereport is occurred after the given timestamp
+#
+function check_ereport #timestamp, etype
+{
+ typeset etime=$1
+ typeset ereport
+ typeset -i i=0
+ typeset -i maxtimes=20
+
+ shift
+
+ for type in $@ ; do
+ i=0
+ while (( i < maxtimes )); do
+ (( i = i + 1 ))
+ ereport=$($FMDUMP -t "$etime" -e -v -c $type | \
+ $NAWK '(NR != 1) {print $0}')
+ if [[ -n $ereport ]]; then
+ break
+ elif (( i == maxtimes )) ; then
+ $FMDUMP -t "$etime" -e -v
+ log_fail "$type not found"
+ fi
+ $SLEEP 3
+ done
+ done
+ return 0
+}
+
+#
+# Check if the fault is occurred after the given timestamp
+#
+function check_fault #timestamp, fault_class
+{
+ typeset after_time=$1
+ typeset ereport
+ typeset -i i=0
+ typeset -i maxtimes=20
+
+ shift
+
+ for fault in $@ ; do
+ i=0
+ while (( i < maxtimes )); do
+ (( i = i + 1 ))
+ ereport=$($FMDUMP -av -t "$after_time" | $GREP $fault)
+ if [[ -n $ereport ]]; then
+ break
+ elif (( i == maxtimes )) ; then
+ $FMDUMP -av -t "$after_time"
+ log_fail "$fault not found"
+ fi
+ $SLEEP 3
+ done
+ done
+ return 0
+}
+
+#
+# Check if 'zpool status -v' contain the permanent error as expected
+#
+function check_status #poolname, errors
+{
+ typeset poolname=$1
+ typeset errors=$2
+
+ for err in $errors ; do
+ ereport=$($ZPOOL status -v $poolname | $GREP "$err")
+ if [[ -z $ereport ]]; then
+ $ZPOOL status -v $poolname
+ log_fail "$err not found"
+ fi
+ done
+ return 0
+}
+
+#
+# Invoke the trigger function according to the fault type corresponded
+#
+function trigger_inject #etype, object, objtype
+{
+ typeset etype=$1
+ typeset object=$2
+ typeset objtype=$3
+
+ if [[ $etype == "bplist" ]] ; then
+ $ECHO "ZFS Fault Harness" > $object
+ fi
+
+ case $objtype in
+ dir)
+ $LS -l $object > /dev/null 2>&1
+ ;;
+ file)
+ $CAT $object > /dev/null 2>&1
+ ;;
+ esac
+}
+
+function populate_test_env #basedir #count
+{
+ typeset basedir=$1
+ typeset -i count=$2
+ typeset -i i=1
+
+ if [[ -d $basedir ]]; then
+ log_must $RM -rf $basedir/*
+ else
+ log_must $MKDIR -p $basedir
+ fi
+
+ while (( i <= count )); do
+ $ECHO "ZFS Fault Harness" > $basedir/testfile.$i
+ $MKDIR -p $basedir/testdir.$i
+ (( i = i + 1 ))
+ done
+
+ return 0
+}
diff --git a/tests/sys/cddl/zfs/tests/zinject/zinject_001_pos.ksh b/tests/sys/cddl/zfs/tests/zinject/zinject_001_pos.ksh
new file mode 100644
index 000000000000..795d09c73f99
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zinject/zinject_001_pos.ksh
@@ -0,0 +1,142 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zinject_001_pos
+#
+# DESCRIPTION:
+#
+# Inject an error into the plain file contents of a file.
+# Verify fmdump will get the expect ereport
+#
+# STRATEGY:
+# 1) Populate ZFS file system
+# 2) Inject an error into the plain file contents of a file.
+# 3) Verify fmdump get the ereport as expect.
+# <Errno> <Expect ereport> <Comments>
+# io ereport.fs.zfs.io
+# ereport.fs.zfs.data
+# checksum ereport.fs.zfs.checksum Non-stripe pool
+# ereport.fs.zfs.data
+# checksum ereport.fs.zfs.data Stripe pool only
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-02-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+. $STF_SUITE/tests/zinject/zinject.kshlib
+
+verify_runnable "global"
+
+log_assert "Verify fault inject handle content error successfully."
+log_onexit cleanup_env
+
+set -A types "" "mirror" "raidz" "raidz2"
+
+typeset -i maxnumber=300
+
+function test_zinject_unit
+{
+ typeset etype=$1
+ typeset object=$2
+ typeset errno=$3
+ typeset ereport=$4
+ typeset now
+
+ typeset otype="file"
+ [[ -d $object ]] && otype="dir"
+
+ now=`date '+%m/%d/%y %H:%M:%S'`
+ inject_fault $etype $object $errno
+
+ trigger_inject $etype $object $otype
+
+ log_must check_ereport "$now" $ereport
+
+ log_must check_status $TESTPOOL $object
+
+ inject_clear
+}
+
+function test_zinject
+{
+ typeset basedir=$1
+ typeset pooltype=$2
+ typeset -i i=0
+ typeset etype="data"
+
+ set -A errset "io" "ereport.fs.zfs.io ereport.fs.zfs.data"
+
+ ((i=${#errset[*]}))
+ if [[ -n $pooltype ]] ; then
+ errset[i]="checksum"
+ errset[((i+1))]="ereport.fs.zfs.checksum ereport.fs.zfs.data"
+ else
+ errset[i]="checksum"
+ errset[((i+1))]="ereport.fs.zfs.data"
+ fi
+
+ i=0
+ while ((i < ${#errset[*]} )); do
+
+ for object in $basedir/testfile.$maxnumber \
+ $basedir/testdir.$maxnumber ; do
+
+ test_zinject_unit $etype $object \
+ ${errset[i]} "${errset[((i+1))]}"
+ done
+
+ (( i = i + 2 ))
+ done
+}
+
+inject_clear
+for type in "${types[@]}"; do
+ create_pool $TESTPOOL $type $pooldevs spare $sparedevs
+
+ log_must $ZPOOL add -f $TESTPOOL log $logdevs
+ log_must $ZPOOL add -f $TESTPOOL cache $cachedevs
+
+ log_must $ZPOOL replace $TESTPOOL $VDEV0 $sparedevs
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+
+ populate_test_env $TESTDIR $maxnumber
+ test_zinject $TESTDIR $type
+
+ cleanup_env
+done
+
+log_pass "Fault inject handle content error successfully."
diff --git a/tests/sys/cddl/zfs/tests/zinject/zinject_002_pos.ksh b/tests/sys/cddl/zfs/tests/zinject/zinject_002_pos.ksh
new file mode 100644
index 000000000000..35b9e8e4209a
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zinject/zinject_002_pos.ksh
@@ -0,0 +1,143 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zinject_002_pos
+#
+# DESCRIPTION:
+#
+# Inject an error into the metadnode in the block
+# corresponding to the dnode for a file or directory
+# Verify fmdump will get the expect ereport
+#
+# STRATEGY:
+# 1) Populate ZFS file system
+# 2) Inject an error into the metadnode in the block.
+# 3) Verify fmdump get the ereport as expect.
+# <Errno> <Expect ereport> <Comments>
+# io ereport.fs.zfs.io
+# ereport.fs.zfs.data
+# checksum ereport.fs.zfs.checksum Non-stripe pool
+# ereport.fs.zfs.data
+# checksum ereport.fs.zfs.data Stripe pool only
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-02-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+. $STF_SUITE/tests/zinject/zinject.kshlib
+
+verify_runnable "global"
+
+log_assert "Verify fault inject handle metadnode error successfully."
+log_onexit cleanup_env
+
+set -A types "" "mirror" "raidz" "raidz2"
+
+typeset -i maxnumber=1
+
+function test_zinject_unit
+{
+ typeset etype=$1
+ typeset object=$2
+ typeset errno=$3
+ typeset ereport=$4
+ typeset now
+
+ typeset otype="file"
+ [[ -d $object ]] && otype="dir"
+
+ now=`date '+%m/%d/%y %H:%M:%S'`
+ inject_fault $etype $object $errno
+
+ trigger_inject $etype $object $otype
+
+ log_must check_ereport "$now" $ereport
+
+ log_must check_status $TESTPOOL "$TESTPOOL/$TESTFS:<0x0>"
+
+ inject_clear
+}
+
+function test_zinject
+{
+ typeset basedir=$1
+ typeset pooltype=$2
+ typeset -i i=0
+ typeset etype="dnode"
+
+ set -A errset "io" "ereport.fs.zfs.io ereport.fs.zfs.data"
+
+ ((i=${#errset[*]}))
+ if [[ -n $pooltype ]] ; then
+ errset[i]="checksum"
+ errset[((i+1))]="ereport.fs.zfs.checksum ereport.fs.zfs.data"
+ else
+ errset[i]="checksum"
+ errset[((i+1))]="ereport.fs.zfs.data"
+ fi
+
+ i=0
+ while ((i < ${#errset[*]} )); do
+ for object in $basedir/testfile.$maxnumber \
+ $basedir/testdir.$maxnumber ; do
+ test_zinject_unit $etype $object \
+ ${errset[i]} "${errset[((i+1))]}"
+ done
+
+ (( i = i + 2 ))
+ done
+}
+
+inject_clear
+for type in "${types[@]}"; do
+ create_pool $TESTPOOL $type $pooldevs spare $sparedevs
+
+ log_must $ZPOOL add -f $TESTPOOL log $logdevs
+ log_must $ZPOOL add -f $TESTPOOL cache $cachedevs
+
+ log_must $ZPOOL replace $TESTPOOL $VDEV0 $sparedevs
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+
+ populate_test_env $TESTDIR/safe_dir 64
+ populate_test_env $TESTDIR/bad_dir $maxnumber
+
+ test_zinject $TESTDIR/bad_dir $type
+
+ cleanup_env
+done
+
+log_pass "Fault inject handle metadnode error successfully."
diff --git a/tests/sys/cddl/zfs/tests/zinject/zinject_003_pos.ksh b/tests/sys/cddl/zfs/tests/zinject/zinject_003_pos.ksh
new file mode 100644
index 000000000000..aa0014bf76e0
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zinject/zinject_003_pos.ksh
@@ -0,0 +1,139 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zinject_003_pos
+#
+# DESCRIPTION:
+#
+# Inject an error into the first metadnode in the block
+# Verify the filesystem unmountable since dnode be injected.
+#
+# STRATEGY:
+# 1) Populate ZFS file system
+# 2) Inject an error into the first metadnode in the block.
+# 3) Verify the filesystem unmountable,
+# and 'zpool status -v' will display the error as expect.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-02-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+. $STF_SUITE/tests/zinject/zinject.kshlib
+
+verify_runnable "global"
+
+log_assert "Verify fault inject handle into first metadnode " \
+ "cause filesystem unmountable."
+log_onexit cleanup_env
+
+set -A types "" "mirror" "raidz" "raidz2"
+
+typeset -i maxnumber=1
+
+function test_zinject_unit
+{
+ typeset etype=$1
+ typeset object=$2
+ typeset errno=$3
+ typeset ereport=$4
+ typeset now
+
+ typeset otype="file"
+ [[ -d $object ]] && otype="dir"
+
+ now=`date '+%m/%d/%y %H:%M:%S'`
+ inject_fault $etype $object $errno 1
+
+ unmounted $TESTPOOL/$TESTFS || \
+ log_fail "$TESTPOOL/$TESTFS mount unexpected."
+
+ log_must check_status $TESTPOOL "$TESTPOOL/$TESTFS:<0x0>"
+
+ inject_clear
+
+ log_must $ZFS mount -a
+}
+
+function test_zinject
+{
+ typeset basedir=$1
+ typeset pooltype=$2
+ typeset -i i=0
+ typeset etype="dnode"
+
+ set -A errset "io" "ereport.fs.zfs.io ereport.fs.zfs.data"
+
+ ((i=${#errset[*]}))
+ if [[ -n $pooltype ]] ; then
+ errset[i]="checksum"
+ errset[((i+1))]="ereport.fs.zfs.checksum ereport.fs.zfs.data"
+ else
+ errset[i]="checksum"
+ errset[((i+1))]="ereport.fs.zfs.data"
+ fi
+
+ i=0
+ while ((i < ${#errset[*]} )); do
+ for object in $basedir/testfile.$maxnumber \
+ $basedir/testdir.$maxnumber ; do
+ test_zinject_unit $etype $object \
+ ${errset[i]} "${errset[((i+1))]}"
+ done
+
+ (( i = i + 2 ))
+ done
+}
+
+inject_clear
+for type in "${types[@]}"; do
+ create_pool $TESTPOOL $type $pooldevs spare $sparedevs
+
+ log_must $ZPOOL add -f $TESTPOOL log $logdevs
+ log_must $ZPOOL add -f $TESTPOOL cache $cachedevs
+
+ log_must $ZPOOL replace $TESTPOOL $VDEV0 $sparedevs
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+
+ populate_test_env $TESTDIR/bad_dir $maxnumber
+
+ test_zinject $TESTDIR/bad_dir $type
+
+ cleanup_env
+done
+
+log_pass "Fault inject handle into first metadnode " \
+ "cause filesystem unmountable."
diff --git a/tests/sys/cddl/zfs/tests/zinject/zinject_004_pos.ksh b/tests/sys/cddl/zfs/tests/zinject/zinject_004_pos.ksh
new file mode 100644
index 000000000000..6353f3ffa78c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zinject/zinject_004_pos.ksh
@@ -0,0 +1,125 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zinject_004_pos
+#
+# DESCRIPTION:
+#
+# Inject an error into the device of a pool.
+# Verify fmdump will get the expect ereport,
+# and the fault class of "fault.fs.zfs.vdev.io" be generated.
+#
+# STRATEGY:
+# 1) Populate ZFS file system
+# 2) Inject an error into the device of the pool.
+# 3) Verify fmdump get the ereport as expected.
+# <Errno> <Expect ereport>
+# nxio ereport.fs.zfs.probe_failure
+# io ereport.fs.zfs.probe_failure
+# 4) Verify the fault class of "fault.fs.zfs.vdev.io" be generated.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-05-31)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+. $STF_SUITE/tests/zinject/zinject.kshlib
+
+verify_runnable "global"
+
+log_assert "Verify fault inject handle device error successfully."
+log_onexit cleanup_env
+
+set -A types "mirror" "raidz" "raidz2"
+
+typeset -i maxnumber=1
+
+function test_zinject
+{
+ typeset basedir=$1
+ typeset -i i=0
+ typeset etype="device"
+ typeset fclass="fault.fs.zfs.vdev.io"
+
+ set -A errset \
+ "nxio" "ereport.fs.zfs.probe_failure" \
+ "io" "ereport.fs.zfs.probe_failure"
+
+ set -A alldevarray $alldevs
+
+ for device in $(random_string alldevarray 1); do
+ i=0
+ while ((i < ${#errset[*]} )); do
+ now=`date '+%m/%d/%y %H:%M:%S'`
+ inject_device $device $TESTPOOL ${errset[i]}
+
+ trigger_inject $etype $basedir/testfile.$maxnumber "file"
+ log_must check_ereport "$now" ${errset[((i+1))]}
+ log_must check_fault "$now" $fclass
+
+ inject_clear
+
+ now=`date '+%m/%d/%y %H:%M:%S'`
+ inject_device $device $TESTPOOL ${errset[i]}
+
+ trigger_inject $etype $basedir/testdir.$maxnumber "dir"
+ log_must check_ereport "$now" ${errset[((i+1))]}
+ log_must check_fault "$now" $fclass
+
+ inject_clear
+
+ (( i = i + 2 ))
+ done
+ done
+}
+
+inject_clear
+for type in "${types[@]}"; do
+ create_pool $TESTPOOL $type $pooldevs spare $sparedevs
+
+ log_must $ZPOOL add -f $TESTPOOL log $logdevs
+ log_must $ZPOOL add -f $TESTPOOL cache $cachedevs
+
+ log_must $ZPOOL replace $TESTPOOL $VDEV0 $sparedevs
+ log_must $ZFS create $TESTPOOL/$TESTFS
+ log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
+
+ populate_test_env $TESTDIR $maxnumber
+ test_zinject $TESTDIR
+
+ cleanup_env
+done
+
+log_pass "Fault inject handle device error successfully."
diff --git a/tests/sys/cddl/zfs/tests/zinject/zinject_test.sh b/tests/sys/cddl/zfs/tests/zinject/zinject_test.sh
new file mode 100755
index 000000000000..1f05e20f57c6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zinject/zinject_test.sh
@@ -0,0 +1,138 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zinject_001_pos cleanup
+zinject_001_pos_head()
+{
+ atf_set "descr" "Verify fault inject handle content error successfully."
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 1800
+}
+zinject_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zinject.kshlib
+ . $(atf_get_srcdir)/zinject.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zinject_001_pos.ksh || atf_fail "Testcase failed"
+}
+zinject_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zinject.kshlib
+ . $(atf_get_srcdir)/zinject.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zinject_002_pos cleanup
+zinject_002_pos_head()
+{
+ atf_set "descr" "Verify fault inject handle metadnode error successfully."
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 1800
+}
+zinject_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zinject.kshlib
+ . $(atf_get_srcdir)/zinject.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zinject_002_pos.ksh || atf_fail "Testcase failed"
+}
+zinject_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zinject.kshlib
+ . $(atf_get_srcdir)/zinject.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zinject_003_pos cleanup
+zinject_003_pos_head()
+{
+ atf_set "descr" "Verify fault inject handle into first metadnodecause filesystem unmountable."
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 1800
+}
+zinject_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zinject.kshlib
+ . $(atf_get_srcdir)/zinject.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zinject_003_pos.ksh || atf_fail "Testcase failed"
+}
+zinject_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zinject.kshlib
+ . $(atf_get_srcdir)/zinject.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zinject_004_pos cleanup
+zinject_004_pos_head()
+{
+ atf_set "descr" "Verify fault inject handle device error successfully."
+ atf_set "require.progs" "ksh93 zfs zpool"
+ atf_set "timeout" 1800
+}
+zinject_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zinject.kshlib
+ . $(atf_get_srcdir)/zinject.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zinject_004_pos.ksh || atf_fail "Testcase failed"
+}
+zinject_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zinject.kshlib
+ . $(atf_get_srcdir)/zinject.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zinject_001_pos
+ atf_add_test_case zinject_002_pos
+ atf_add_test_case zinject_003_pos
+ atf_add_test_case zinject_004_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/zones/Makefile b/tests/sys/cddl/zfs/tests/zones/Makefile
new file mode 100644
index 000000000000..4b27462d1c0b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zones/Makefile
@@ -0,0 +1,21 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/zones
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zones_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zones.cfg
+${PACKAGE}FILES+= zones_005_pos.ksh
+${PACKAGE}FILES+= zones_001_pos.ksh
+${PACKAGE}FILES+= zones_004_pos.ksh
+${PACKAGE}FILES+= zones_common.kshlib
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zones_002_pos.ksh
+${PACKAGE}FILES+= zones_003_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/zones/cleanup.ksh b/tests/sys/cddl/zfs/tests/zones/cleanup.ksh
new file mode 100644
index 000000000000..01b867eb1bd8
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zones/cleanup.ksh
@@ -0,0 +1,61 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+
+verify_runnable "both"
+
+if ! is_global_zone ; then
+ default_cleanup
+fi
+
+# Zone installs that may have been interrupted can leave the system in
+# an odd state, lots of processes hanging around that will block
+# our attempts to delete the zone. Remove these first.
+for program in zoneadm lucreatezone lupi_zones lupi_bebasic pkginstall
+do
+ $PKILL -9 $program
+done
+
+# Zone installs that may have been interrupted can leave the system
+# with lofs mounts from our test pool. Unmount these.
+FS=$($MOUNT | $GREP $TESTPOOL | $AWK '{print $1}')
+
+for fs in $FS
+do
+ $UMOUNT -f $fs
+done
+
+for zone in $ZONE $ZONE2 $ZONE3 $ZONE4 ; do
+ $ZONEADM -z $zone list > /dev/null 2>&1
+ if (( $? == 0 )) ; then
+ $ZONEADM -z $zone halt > /dev/null 2>&1
+ $ZONEADM -z $zone uninstall -F > /dev/null 2>&1
+ $ZONECFG -z $zone delete -F > /dev/null 2>&1
+ fi
+done
+
+default_cleanup
diff --git a/tests/sys/cddl/zfs/tests/zones/setup.ksh b/tests/sys/cddl/zfs/tests/zones/setup.ksh
new file mode 100644
index 000000000000..18a790156422
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zones/setup.ksh
@@ -0,0 +1,60 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+. ${STF_SUITE}/tests/zones/zones_common.kshlib
+
+verify_runnable "both"
+check_version "5.11"
+if [ $? -eq 1 ]
+then
+ log_unsupported "ZFS zone clone tests unsupported on this release."
+fi
+
+if ! is_global_zone ; then
+ log_pass
+fi
+
+DISK=${DISKS%% *}
+default_setup_noexit $DISK
+
+# create a zone on ZFS
+create_zone $ZONE /$TESTPOOL
+install_zone $ZONE
+
+log_must $MKDIR -p -m 0700 /$TESTPOOL/simple_dir
+
+# create a normal zone - again on ZFS, but with the zonepath
+# being a simple directory, rather than a top-level filesystem.
+# We also create this as a branded zone.
+create_zone $ZONE2 /$TESTPOOL/simple_dir SUNWsn1
+install_zone $ZONE2
+
+# Now make sure those zones are visible
+log_must eval "$ZONEADM -z $ZONE list > /dev/null 2>&1"
+log_must eval "$ZONEADM -z $ZONE2 list > /dev/null 2>&1"
+
+log_pass "Setup created zones $ZONE and $ZONE2"
diff --git a/tests/sys/cddl/zfs/tests/zones/zones.cfg b/tests/sys/cddl/zfs/tests/zones/zones.cfg
new file mode 100644
index 000000000000..056d6712db58
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zones/zones.cfg
@@ -0,0 +1,78 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+# Variables we'll use to name our zones
+# The first two zones are created by setup,
+# then destroyed in zones_004_pos - or cleanup.
+ZONE="${ZONE:=zone}"
+ZONE2="${ZONE2:=zonetwo}"
+
+# These zones are created and destroyed by zones_003_pos - or cleanup.
+ZONE3="${ZONE3:=zonethree}"
+ZONE4="${ZONE4:=zonefour}"
+
+# A function which runs through a loop, adding a different characters
+# to the stem passed as $1, to make sure we have a unique name that
+# doesn't clash with an existing zone name. Once we've found a unique
+# name, we echo that to stdout.
+#
+function find_unique_zonename { # initial name of zone
+
+ NAME=$1
+ COUNT=0
+ while [ -z "$FOUND" ]
+ do
+ NAME="${NAME}${COUNT}"
+ $ZONEADM -z $NAME list > /dev/null 2>&1
+ if [ $? -eq 1 ]
+ then
+ FOUND=true
+ fi
+ COUNT=$(( $COUNT + 1 ))
+ done
+ echo $NAME
+}
+
+# Need a longer timeout for zone installation
+export STF_TIMEOUT=3600
+
+# Make sure that multiple sourcing of this script doesn't change the zone name
+if [ -z "${RUNCONFIG}" ]
+then
+ ZONE=$(find_unique_zonename $ZONE)
+ ZONE2=$(find_unique_zonename $ZONE2)
+ ZONE3=$(find_unique_zonename $ZONE3)
+ ZONE4=$(find_unique_zonename $ZONE4)
+
+ log_note "zones.cfg gave us new zone names $ZONE,$ZONE2,$ZONE3,$ZONE4"
+
+ export ZONE
+ export ZONE2
+ export ZONE3
+ export ZONE4
+
+ export RUNCONFIG="true"
+fi
diff --git a/tests/sys/cddl/zfs/tests/zones/zones_001_pos.ksh b/tests/sys/cddl/zfs/tests/zones/zones_001_pos.ksh
new file mode 100644
index 000000000000..b9837f46b049
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zones/zones_001_pos.ksh
@@ -0,0 +1,107 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zones_001_pos
+#
+# DESCRIPTION:
+#
+# The zone created by the default zones setup should have ZFS zvols,
+# datasets and filesystems present.
+#
+# STRATEGY:
+# 1. For each ZFS object type
+# 2. Perform a basic sanity check for that object in the local zone.
+# 3. Check that the top level dataset is read only.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-10-18)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "local"
+
+
+log_assert "Local zone contains ZFS datasets as expected."
+
+
+# check to see if our zvol exists:
+if [ ! -b /dev/zvol/zonepool/zone_zvol ]
+then
+ log_fail "block device /dev/zvol/zonepool/zone_zvol not found!"
+fi
+
+if [ ! -c /dev/zvol/zonepool/zone_zvol ]
+then
+ log_fail "char device /dev/zvol/zonepool/zone_zvol not found!"
+fi
+
+# check to see if the device appears sane - create a UFS filesystem on it.
+$ECHO y | $NEWFS /dev/zvol/zonepool/zone_zvol > /dev/null
+
+if [ $? -ne 0 ]
+then
+ log_fail "Failed to create UFS filesystem on a zvol in the zone!"
+fi
+
+$MKDIR /ufs.${TESTCASE_ID}
+log_must $MOUNT /dev/zvol/zonepool/zone_zvol /ufs.${TESTCASE_ID}
+log_must $UMOUNT /ufs.${TESTCASE_ID}
+$RM -rf /ufs.${TESTCASE_ID}
+
+
+# Next check to see if the datasets exist as expected.
+for dataset in 0 1 2 3 4
+do
+ DATASET=zonepool/zonectr${dataset}
+ if [ ! -d /${DATASET} ]
+ then
+ log_note "Missing zone dataset ${DATASET}!"
+ fi
+ log_must $ZFS create ${DATASET}/fs
+ if [ ! -d /${DATASET}/fs ]
+ then
+ log_fail "ZFS create failed to create child dataset of ${DATASET}"
+ fi
+ log_must $ZFS destroy ${DATASET}/fs
+done
+
+# Next check to see that the root dataset is readonly
+log_mustnot $ZFS create zonepool/fs
+log_mustnot $ZFS mount zonepool
+
+log_pass "Local zone contains ZFS datasets as expected."
+
diff --git a/tests/sys/cddl/zfs/tests/zones/zones_002_pos.ksh b/tests/sys/cddl/zfs/tests/zones/zones_002_pos.ksh
new file mode 100644
index 000000000000..72a88a4b9902
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zones/zones_002_pos.ksh
@@ -0,0 +1,76 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+. ${STF_SUITE}/tests/zones/zones_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zones_002_pos
+#
+# DESCRIPTION:
+#
+# A zone created where the zonepath parent dir is the top level of a ZFS
+# file system has a new ZFS filesystem created for it.
+#
+# STRATEGY:
+# 1. The setup script should have created the zone.
+# 2. Verify that a new ZFS filesystem has been created.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-10-11)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "A ZFS fs is created when the parent dir of zonepath is a ZFS fs."
+
+# check to see if our zone exists:
+if [ ! -d /$TESTPOOL/$ZONE ]
+then
+ log_fail "Zone dir in /$TESTPOOL/$ZONE not found!"
+fi
+
+if [ ! -d /$TESTPOOL/simple_dir/$ZONE2 ]
+then
+ log_fail "Zone dir /$TESTPOOL/simple_dir/$ZONE2 not found!"
+fi
+
+# we should have a new ZFS fs for the zone
+log_must eval "$ZFS list $TESTPOOL/$ZONE > /dev/null"
+
+# we should not have a new ZFS fs for the non-ZFS zone.
+log_mustnot eval "$ZFS list $TESTPOOL/simple_dir/$ZONE2 > /dev/null"
+
+log_pass "A ZFS fs is created when the parent dir of zonepath is a ZFS fs."
diff --git a/tests/sys/cddl/zfs/tests/zones/zones_003_pos.ksh b/tests/sys/cddl/zfs/tests/zones/zones_003_pos.ksh
new file mode 100644
index 000000000000..4ea3aa27d1fa
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zones/zones_003_pos.ksh
@@ -0,0 +1,128 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+. ${STF_SUITE}/tests/zones/zones_common.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zones_003_pos
+#
+# DESCRIPTION:
+#
+# Zone cloning via ZFS snapshots works as expected.
+# We can clone zones where the zonepath is the top level of a ZFS filesystem
+# using snapshots. Where the zone is not at the top level of a ZFS filesystem,
+# cloning the zone uses the normal method of copying the files when
+# performing the clone operation.
+#
+# STRATEGY:
+# 1. The setup script should have created the zone.
+# 2. Clone a zone-on-ZFS
+# 3. Verify that ZFS snapshots were taken and used for the clone and that
+# the new zone is indeed a clone (in the ZFS sense)
+# 4. Clone a normal zone & verify that no snapshots were taken.
+# 5. Clone a zone-on-ZFS, but specify the "copy" method & verify that no
+# snapshots were taken.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-10-12)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+function cleanup {
+
+ log_must $ZONEADM -z $ZONE3 uninstall -F
+ log_must $ZONECFG -z $ZONE3 delete -F
+
+ log_must $ZONEADM -z $ZONE4 uninstall -F
+ log_must $ZONECFG -z $ZONE4 delete -F
+}
+
+
+verify_runnable "global"
+log_onexit cleanup
+
+log_assert "Zone cloning via ZFS snapshots works as expected."
+
+
+# Make sure our zones exist:
+if [ ! -d /$TESTPOOL/$ZONE ]
+then
+ log_fail "Zone dir in /$TESTPOOL/$ZONE not found!"
+fi
+
+if [ ! -d /$TESTPOOL/simple_dir/$ZONE2 ]
+then
+ log_fail "Zone dir /$TESTPOOL/simple_dir/$ZONE2 not found!"
+fi
+
+
+create_zone $ZONE3 /$TESTPOOL
+create_zone $ZONE4 /$TESTPOOL/simple_dir
+
+# Create a new zone3 based on cloning our zone
+log_note "Cloning ZFS rooted zone"
+log_must $ZONEADM -z $ZONE3 clone $ZONE
+
+# Make sure our snapshot and the new filesystem is there
+log_must snapexists $TESTPOOL/$ZONE@SUNWzone1
+log_must datasetexists $TESTPOOL/$ZONE3
+
+# verify that it is in fact a clone:
+ORIGIN=$($ZFS get -H -o value origin $TESTPOOL/$ZONE3)
+if [ "$ORIGIN" != "$TESTPOOL/$ZONE@SUNWzone1" ]
+then
+ log_fail "$ZONE3 does not appear to have been ZFS cloned from $ZONE"
+fi
+
+# Now uninstall that zone & the snapshot it was cloned from
+log_must $ZONEADM -z $ZONE3 uninstall -F
+log_must $ZONECFG -z $ZONE3 delete -F
+
+# Again create a new zone3, but clone the non-ZFS-rooted zone2
+# A snapshot should not have been created this time, but a new filesys
+# should still be created.
+create_zone $ZONE3 /$TESTPOOL SUNWsn1
+log_note "Cloning non-ZFS rooted zone2"
+log_must $ZONEADM -z $ZONE3 clone $ZONE2
+log_mustnot snapexists $TESTPOOL/$ZONE2@SUNWzone1
+log_must datasetexists $TESTPOOL/$ZONE3
+
+# Finally, clone a zone using the old copy method, where
+# a snapshot should not be taken.
+log_note "Cloning ZFS rooted zone using copy method"
+log_must $ZONEADM -z $ZONE4 clone -m copy $ZONE
+log_mustnot snapexists $TESTPOOl/$ZONE@SUNWzone1
+
+log_pass "Zone cloning via ZFS snapshots works as expected."
diff --git a/tests/sys/cddl/zfs/tests/zones/zones_004_pos.ksh b/tests/sys/cddl/zfs/tests/zones/zones_004_pos.ksh
new file mode 100644
index 000000000000..ea54dd0d730d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zones/zones_004_pos.ksh
@@ -0,0 +1,86 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zones_004_pos
+#
+# DESCRIPTION:
+#
+# Deleting a zone, where the zonepath parent dir is the top level of a ZFS
+# file system, causes that underlying filesystem to be deleted. Deleting
+# the non-ZFS zone does not delete any filesystems.
+#
+# STRATEGY:
+# 1. The setup script should have created the zone.
+# 2. Delete our ZFS rooted zone, verify the filesystem has been deleted.
+# 3. Delete our non-ZFS rooted zone, the zonepath dir should still exist.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-10-11)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "A ZFS fs is destroyed when the zone it was created for is deleted."
+
+# Make sure our zones exist:
+if [ ! -d /$TESTPOOL/$ZONE ]
+then
+ log_fail "Zone dir in /$TESTPOOL/$ZONE not found!"
+fi
+
+if [ ! -d /$TESTPOOL/simple_dir/$ZONE2 ]
+then
+ log_fail "Zone dir /$TESTPOOL/simple_dir/$ZONE2 not found!"
+fi
+
+
+# delete our ZFS rooted zone
+log_must $ZONEADM -z $ZONE uninstall -F
+log_must $ZONECFG -z $ZONE delete -F
+log_mustnot eval "$ZFS list $TESTPOOL/$ZONE > /dev/null 2>&1"
+
+# delete our non-ZFS rooted zone
+log_must $ZONEADM -z $ZONE2 uninstall -F
+log_must $ZONECFG -z $ZONE2 delete -F
+if [ ! -d /$TESTPOOL/simple_dir ]
+then
+ log_fail "On deleting $ZONE2, the dir above zonepath was destroyed!"
+fi
+
+
+log_pass "A ZFS fs is destroyed when the zone it was created for is deleted."
diff --git a/tests/sys/cddl/zfs/tests/zones/zones_005_pos.ksh b/tests/sys/cddl/zfs/tests/zones/zones_005_pos.ksh
new file mode 100644
index 000000000000..d0f4dae8a38e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zones/zones_005_pos.ksh
@@ -0,0 +1,67 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zones_005_pos
+#
+# DESCRIPTION:
+#
+# Pool properties can be read but can't be set within a zone
+#
+# STRATEGY:
+# 1. Verify we can read pool properties in a zone
+# 2. Verify we can't set a pool property in a zone
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2007-04-03)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "local"
+
+log_assert "Pool properties can be read but can't be set within a zone"
+
+log_must $ZPOOL get all zonepool
+log_must $ZPOOL get bootfs zonepool
+log_mustnot $ZPOOL set boofs=zonepool zonepool
+
+# verify that the property hasn't been set.
+log_must eval "$ZPOOL get bootfs zonepool > $TMPDIR/output.${TESTCASE_ID}"
+log_must $GREP "zonepool bootfs -" $TMPDIR/output.${TESTCASE_ID}
+
+$RM $TMPDIR/output.${TESTCASE_ID}
+
+log_pass "Pool properties can be read but can't be set within a zone"
diff --git a/tests/sys/cddl/zfs/tests/zones/zones_common.kshlib b/tests/sys/cddl/zfs/tests/zones/zones_common.kshlib
new file mode 100644
index 000000000000..fb29aea10527
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zones/zones_common.kshlib
@@ -0,0 +1,65 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/zones/zones.cfg
+
+# a simple zone creation function
+function create_zone { # $ZONENAME $ZONEPATH $BRAND (optional)
+ ZNAME=$1
+ ZPATH=$2
+ BRAND=$3
+
+ if [ ! -z $BRAND ]
+ then
+ # The SUNWsn1 brand only works on debug kernels for sparc, so we
+ # check for a known symbol to see if that's there.
+ $ECHO "logmap_logscan_debug/p" | $MDB -k > /dev/null 2>&1
+ if [ $? -eq 1 ]
+ then
+ BRAND=""
+ else
+ BRAND="-t $BRAND "
+ fi
+ fi
+
+ # create a zone config file
+ $CAT > $TMPDIR/zone.${TESTCASE_ID}.cfg <<EOF
+create $BRAND
+set zonepath=$ZPATH/$ZNAME
+set autoboot=true
+exit
+EOF
+ log_note "Creating ${BRAND}zone $ZNAME on $ZPATH/$ZNAME."
+ log_must $ZONECFG -z $ZNAME -f $TMPDIR/zone.${TESTCASE_ID}.cfg > /dev/null
+ $RM $TMPDIR/zone.${TESTCASE_ID}.cfg
+}
+
+function install_zone { # $ZONENAME
+ ZNAME=$1
+ log_note "Installing $ZNAME. This may take some time."
+ log_must $ZONEADM -z $ZNAME install > /dev/null
+}
diff --git a/tests/sys/cddl/zfs/tests/zones/zones_test.sh b/tests/sys/cddl/zfs/tests/zones/zones_test.sh
new file mode 100755
index 000000000000..d10a5a2f3617
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zones/zones_test.sh
@@ -0,0 +1,165 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zones_001_pos cleanup
+zones_001_pos_head()
+{
+ atf_set "descr" "Local zone contains ZFS datasets as expected."
+ atf_set "require.progs" "ksh93 zfs zoneadm zonecfg"
+ atf_set "timeout" 3600
+}
+zones_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zones_common.kshlib
+ . $(atf_get_srcdir)/zones.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zones_001_pos.ksh || atf_fail "Testcase failed"
+}
+zones_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zones_common.kshlib
+ . $(atf_get_srcdir)/zones.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zones_002_pos cleanup
+zones_002_pos_head()
+{
+ atf_set "descr" "A ZFS fs is created when the parent dir of zonepath is a ZFS fs."
+ atf_set "require.progs" "ksh93 zfs zoneadm zonecfg"
+ atf_set "timeout" 3600
+}
+zones_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zones_common.kshlib
+ . $(atf_get_srcdir)/zones.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zones_002_pos.ksh || atf_fail "Testcase failed"
+}
+zones_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zones_common.kshlib
+ . $(atf_get_srcdir)/zones.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zones_003_pos cleanup
+zones_003_pos_head()
+{
+ atf_set "descr" "Zone cloning via ZFS snapshots works as expected."
+ atf_set "require.progs" "ksh93 zfs zoneadm zonecfg"
+ atf_set "timeout" 3600
+}
+zones_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zones_common.kshlib
+ . $(atf_get_srcdir)/zones.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zones_003_pos.ksh || atf_fail "Testcase failed"
+}
+zones_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zones_common.kshlib
+ . $(atf_get_srcdir)/zones.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zones_004_pos cleanup
+zones_004_pos_head()
+{
+ atf_set "descr" "A ZFS fs is destroyed when the zone it was created for is deleted."
+ atf_set "require.progs" "ksh93 zfs zoneadm zonecfg"
+ atf_set "timeout" 3600
+}
+zones_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zones_common.kshlib
+ . $(atf_get_srcdir)/zones.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zones_004_pos.ksh || atf_fail "Testcase failed"
+}
+zones_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zones_common.kshlib
+ . $(atf_get_srcdir)/zones.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zones_005_pos cleanup
+zones_005_pos_head()
+{
+ atf_set "descr" "Pool properties can be read but can't be set within a zone"
+ atf_set "require.progs" "ksh93 zpool zonecfg zoneadm"
+ atf_set "timeout" 3600
+}
+zones_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zones_common.kshlib
+ . $(atf_get_srcdir)/zones.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zones_005_pos.ksh || atf_fail "Testcase failed"
+}
+zones_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zones_common.kshlib
+ . $(atf_get_srcdir)/zones.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zones_001_pos
+ atf_add_test_case zones_002_pos
+ atf_add_test_case zones_003_pos
+ atf_add_test_case zones_004_pos
+ atf_add_test_case zones_005_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/zvol/Makefile b/tests/sys/cddl/zfs/tests/zvol/Makefile
new file mode 100644
index 000000000000..a0c4899ae106
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/Makefile
@@ -0,0 +1,15 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/zvol
+FILESDIR=${TESTSDIR}
+
+${PACKAGE}FILES+= zvol.cfg
+${PACKAGE}FILES+= zvol_common.kshlib
+
+TESTS_SUBDIRS+= zvol_ENOSPC
+TESTS_SUBDIRS+= zvol_swap
+TESTS_SUBDIRS+= zvol_misc
+TESTS_SUBDIRS+= zvol_cli
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol.cfg b/tests/sys/cddl/zfs/tests/zvol/zvol.cfg
new file mode 100644
index 000000000000..fe2566ea3547
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol.cfg
@@ -0,0 +1,34 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+export DISK=${DISKS%% *}
+
+export TESTVOL=testvol${TESTCASE_ID}
+export TESTFILE=testfile${TESTCASE_ID}
+export TESTSNAP=testsnap${TESTCASE_ID}
+export VOLSIZE=2g
+export DATA=0
+export ENOSPC=28
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_ENOSPC/Makefile b/tests/sys/cddl/zfs/tests/zvol/zvol_ENOSPC/Makefile
new file mode 100644
index 000000000000..ffcc0f79e88c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_ENOSPC/Makefile
@@ -0,0 +1,16 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/zvol/zvol_ENOSPC
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zvol_ENOSPC_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zvol_ENOSPC_001_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zvol_ENOSPC.cfg
+${PACKAGE}FILES+= cleanup.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_ENOSPC/cleanup.ksh b/tests/sys/cddl/zfs/tests/zvol/zvol_ENOSPC/cleanup.ksh
new file mode 100644
index 000000000000..1478b071b333
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_ENOSPC/cleanup.ksh
@@ -0,0 +1,34 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/zvol/zvol_common.kshlib
+
+verify_runnable "global"
+
+default_zvol_cleanup
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_ENOSPC/setup.ksh b/tests/sys/cddl/zfs/tests/zvol/zvol_ENOSPC/setup.ksh
new file mode 100644
index 000000000000..073078a1e1d2
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_ENOSPC/setup.ksh
@@ -0,0 +1,38 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/zvol/zvol_common.kshlib
+
+verify_runnable "global"
+
+default_zvol_setup $DISK $VOLSIZE
+
+log_must $NEWFS /dev/zvol/$TESTPOOL/$TESTVOL
+log_must $MKDIR $TESTDIR
+log_must $MOUNT /dev/zvol/$TESTPOOL/$TESTVOL $TESTDIR
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_ENOSPC/zvol_ENOSPC.cfg b/tests/sys/cddl/zfs/tests/zvol/zvol_ENOSPC/zvol_ENOSPC.cfg
new file mode 100644
index 000000000000..b3278e1df5ff
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_ENOSPC/zvol_ENOSPC.cfg
@@ -0,0 +1,30 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/zvol/zvol.cfg
+
+export BLOCKSZ=$(( 1024 * 1024 ))
+export NUM_WRITES=40
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_ENOSPC/zvol_ENOSPC_001_pos.ksh b/tests/sys/cddl/zfs/tests/zvol/zvol_ENOSPC/zvol_ENOSPC_001_pos.ksh
new file mode 100644
index 000000000000..7224c211a265
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_ENOSPC/zvol_ENOSPC_001_pos.ksh
@@ -0,0 +1,77 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zvol_ENOSPC_001_pos
+#
+# DESCRIPTION:
+# A zvol volume will return ENOSPC when the underlying pool runs out of
+# space.
+#
+# STRATEGY:
+# 1. Create a pool
+# 2. Create a zvol volume
+# 3. Create a ufs file system ontop of the zvol
+# 4. Mount the ufs file system
+# 5. Fill volume until ENOSPC is returned
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ # unmounting the UFS filesystem can take more than 60s, and Kyua has a
+ # hardcoded 60s limit for the cleanup phase. So we must unmount the
+ # filesystem here rather than cleanup.ksh.
+ ismounted $TESTDIR ufs && log_must $UMOUNT -f $TESTDIR
+ $RMDIR $TESTDIR
+}
+
+log_assert "A zvol volume will return ENOSPC when the underlying pool " \
+ "runs out of space."
+
+log_onexit cleanup
+
+typeset -i fn=0
+typeset -i retval=0
+
+log_mustbe ENOSPC fill_fs $TESTDIR -1 50 $BLOCKSZ $NUM_WRITES
+
+log_pass "ENOSPC was returned as expected"
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_ENOSPC/zvol_ENOSPC_test.sh b/tests/sys/cddl/zfs/tests/zvol/zvol_ENOSPC/zvol_ENOSPC_test.sh
new file mode 100755
index 000000000000..e25e107d4b6c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_ENOSPC/zvol_ENOSPC_test.sh
@@ -0,0 +1,54 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zvol_ENOSPC_001_pos cleanup
+zvol_ENOSPC_001_pos_head()
+{
+ atf_set "descr" "A zvol volume will return ENOSPC when the underlying poolruns out of space."
+}
+zvol_ENOSPC_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_ENOSPC.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zvol_ENOSPC_001_pos.ksh || atf_fail "Testcase failed"
+}
+zvol_ENOSPC_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_ENOSPC.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zvol_ENOSPC_001_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_cli/Makefile b/tests/sys/cddl/zfs/tests/zvol/zvol_cli/Makefile
new file mode 100644
index 000000000000..4282538eecbc
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_cli/Makefile
@@ -0,0 +1,18 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/zvol/zvol_cli
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zvol_cli_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zvol_cli_003_neg.ksh
+${PACKAGE}FILES+= zvol_cli_002_pos.ksh
+${PACKAGE}FILES+= zvol_cli.cfg
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zvol_cli_001_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_cli/cleanup.ksh b/tests/sys/cddl/zfs/tests/zvol/zvol_cli/cleanup.ksh
new file mode 100644
index 000000000000..73da9255b50b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_cli/cleanup.ksh
@@ -0,0 +1,34 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/zvol/zvol_common.kshlib
+
+verify_runnable "global"
+
+default_zvol_cleanup
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_cli/setup.ksh b/tests/sys/cddl/zfs/tests/zvol/zvol_cli/setup.ksh
new file mode 100644
index 000000000000..bf9de1a1c578
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_cli/setup.ksh
@@ -0,0 +1,34 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/zvol/zvol_common.kshlib
+
+verify_runnable "global"
+
+default_zvol_setup $DISK $VOLSIZE
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_cli/zvol_cli.cfg b/tests/sys/cddl/zfs/tests/zvol/zvol_cli/zvol_cli.cfg
new file mode 100644
index 000000000000..587613231142
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_cli/zvol_cli.cfg
@@ -0,0 +1,30 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/zvol/zvol.cfg
+
+export LONGPNAME="poolname50charslong_012345678901234567890123456789"
+export LONGVOLNAME="volumename50charslong_0123456789012345678901234567"
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_cli/zvol_cli_001_pos.ksh b/tests/sys/cddl/zfs/tests/zvol/zvol_cli/zvol_cli_001_pos.ksh
new file mode 100644
index 000000000000..6f11ec127f5e
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_cli/zvol_cli_001_pos.ksh
@@ -0,0 +1,70 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zvol_cli_001_pos
+#
+# DESCRIPTION:
+# Executing well-formed 'zfs list' commands should return success
+#
+# STRATEGY:
+# 1. Create an array of valid options.
+# 2. Execute each element in the array.
+# 3. Verify success is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+set -A args "list" "list -r" \
+ "list $TESTPOOL/$TESTVOL" "list -r $TESTPOOL/$TESTVOL" \
+ "list -H $TESTPOOL/$TESTVOL" "list -Hr $TESTPOOL/$TESTVOL" \
+ "list -rH $TESTPOOL/$TESTVOL" "list -o name $TESTPOOL/$TESTVOL" \
+ "list -r -o name $TESTPOOL/$TESTVOL" "list -H -o name $TESTPOOL/$TESTVOL" \
+ "list -rH -o name $TESTPOOL/$TESTVOL"
+
+log_assert "Executing well-formed 'zfs list' commands should return success"
+
+typeset -i i=0
+while (( $i < ${#args[*]} )); do
+ log_must eval "$ZFS ${args[i]} > /dev/null"
+ ((i = i + 1))
+done
+
+log_pass "Executing zfs list on volume works as expected"
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_cli/zvol_cli_002_pos.ksh b/tests/sys/cddl/zfs/tests/zvol/zvol_cli/zvol_cli_002_pos.ksh
new file mode 100644
index 000000000000..c50d3a5030fc
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_cli/zvol_cli_002_pos.ksh
@@ -0,0 +1,69 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zvol_cli_002_pos
+#
+# DESCRIPTION:
+# Creating a volume with a 50 letter name should work.
+#
+# STRATEGY:
+# 1. Using a very long name, create a zvol
+# 2. Verify volume exists
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ datasetexists $TESTPOOL/$LONGVOLNAME && \
+ $ZFS destroy $TESTPOOL/$LONGVOLNAME
+}
+
+log_onexit cleanup
+
+log_assert "Creating a volume a 50 letter name should work."
+
+log_must $ZFS create -V $VOLSIZE $TESTPOOL/$LONGVOLNAME
+
+datasetexists $TESTPOOL/$LONGVOLNAME || \
+ log_fail "Couldn't find long volume name"
+
+log_pass "Created a 50-letter zvol volume name"
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_cli/zvol_cli_003_neg.ksh b/tests/sys/cddl/zfs/tests/zvol/zvol_cli/zvol_cli_003_neg.ksh
new file mode 100644
index 000000000000..ea34ab9bab07
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_cli/zvol_cli_003_neg.ksh
@@ -0,0 +1,68 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zvol_cli_003_neg
+#
+# DESCRIPTION:
+# Try each ZFS volume sub-command without parameters to make sure
+# it returns an error.
+#
+# STRATEGY:
+# 1. Create an array of parameters
+# 2. For each parameter in the array, execute the sub-command
+# 3. Verify an error is returned.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+set -A args "" "create -V" "create -V $TESTPOOL" \
+ "create -V $TESTPOOL/$TESTVOL@" "create -V blah" "destroy"
+
+log_assert "Try each ZFS volume sub-command without parameters to make sure" \
+ " it returns an error."
+
+typeset -i i=0
+while (( $i < ${#args[*]} )); do
+ log_mustnot $ZFS ${args[i]}
+ (( i = i + 1 ))
+done
+
+log_pass "Badly formed ZFS volume sub-commands fail as expected."
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_cli/zvol_cli_test.sh b/tests/sys/cddl/zfs/tests/zvol/zvol_cli/zvol_cli_test.sh
new file mode 100755
index 000000000000..2aa6a69a50cf
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_cli/zvol_cli_test.sh
@@ -0,0 +1,105 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zvol_cli_001_pos cleanup
+zvol_cli_001_pos_head()
+{
+ atf_set "descr" "Executing well-formed 'zfs list' commands should return success"
+ atf_set "require.progs" "ksh93 zfs"
+}
+zvol_cli_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_cli.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zvol_cli_001_pos.ksh || atf_fail "Testcase failed"
+}
+zvol_cli_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_cli.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zvol_cli_002_pos cleanup
+zvol_cli_002_pos_head()
+{
+ atf_set "descr" "Creating a volume a 50 letter name should work."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zvol_cli_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_cli.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zvol_cli_002_pos.ksh || atf_fail "Testcase failed"
+}
+zvol_cli_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_cli.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zvol_cli_003_neg cleanup
+zvol_cli_003_neg_head()
+{
+ atf_set "descr" "Try each ZFS volume sub-command without parameters to make sure it returns an error."
+ atf_set "require.progs" "ksh93 zfs"
+}
+zvol_cli_003_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_cli.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zvol_cli_003_neg.ksh || atf_fail "Testcase failed"
+}
+zvol_cli_003_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_cli.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zvol_cli_001_pos
+ atf_add_test_case zvol_cli_002_pos
+ atf_add_test_case zvol_cli_003_neg
+}
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_common.kshlib b/tests/sys/cddl/zfs/tests/zvol/zvol_common.kshlib
new file mode 100644
index 000000000000..0044fc2d82f5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_common.kshlib
@@ -0,0 +1,160 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#
+# Create a simple zvol volume
+#
+# Where disk_device: is the name of the disk to be used
+# volume_size: is the size of the volume, e.g. 2G
+#
+function default_zvol_setup # disk_device volume_size
+{
+ typeset disk=$1
+ typeset size=$2
+ typeset savedumpdev
+ typeset -i output
+
+ create_pool $TESTPOOL "$disk"
+
+ log_must $ZFS create -V $size $TESTPOOL/$TESTVOL
+
+ if [[ -n $DUMPADM ]]; then
+ if is_dumpswap_supported $TESTPOOL ; then
+ set_dumpsize $TESTPOOL/$TESTVOL
+ fi
+ fi
+}
+
+#
+# Destroy the default zvol which was setup using
+# default_zvol_setup().
+#
+function default_zvol_cleanup
+{
+ if datasetexists $TESTPOOL/$TESTVOL ; then
+ log_must $ZFS destroy $TESTPOOL/$TESTVOL
+ fi
+
+ destroy_pool $TESTPOOL
+}
+
+#
+# Check if the given pool support "Swap and crash dumps"
+#
+function is_dumpswap_supported #pool
+{
+ typeset pool=$1
+
+ if [[ -z $pool ]] ; then
+ log_fail "No pool given."
+ fi
+
+ typeset -i SPA_VER_DUMPSWAP=10
+ typeset -i vp=$(get_pool_prop version $pool)
+
+ if (( vp >= SPA_VER_DUMPSWAP )) ; then
+ return 0
+ fi
+
+ return 1
+}
+
+function get_dumpdevice
+{
+ typeset ret=$($DUMPADM | $GREP "Dump device:" | $AWK '{print $3}')
+ print $ret
+}
+
+function set_dumpsize
+{
+ typeset volume=$1
+
+ if [[ -z $volume ]] ; then
+ log_note "No volume specified."
+ return 1
+ fi
+
+ log_must $ZFS set volsize=64m $volume
+
+ output=$($DUMPADM -d /dev/zvol/$volume 2>&1 | \
+ $TAIL -1 | $AWK '{print $3}')
+
+ if [[ -n $output ]]; then
+ (( output = output / 1024 / 1024 ))
+ (( output = output + output / 5 ))
+ log_must $ZFS set volsize=${output}m $volume
+ fi
+ return 0
+}
+
+function safe_dumpadm
+{
+ typeset device=$1
+
+ if [[ -z $device || $device == "none" ]] ; then
+ log_note "No dump device volume specified."
+ return 1
+ fi
+ if [[ $device == "/dev/zvol/"* ]] ; then
+ typeset volume=${device#/dev/zvol/}
+ set_dumpsize $volume
+ log_must $DUMPADM -d $device
+ else
+ log_must $SWAPADD
+ if ! is_swap_inuse $device ; then
+ log_must $SWAP -a $device
+ fi
+ log_must $DUMPADM -d swap
+ fi
+}
+
+function is_zvol_dumpified
+{
+ typeset volume=$1
+
+ if [[ -z $volume ]] ; then
+ log_note "No volume specified."
+ return 1
+ fi
+
+ $ZDB -dddd $volume 2 | $GREP "dumpsize" > /dev/null 2>&1
+ return $?
+}
+
+function is_swap_inuse
+{
+ typeset device=$1
+
+ if [[ -z $device ]] ; then
+ log_note "No device specified."
+ return 1
+ fi
+
+ $SWAP -l | $GREP -w $device > /dev/null 2>&1
+ return $?
+}
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_misc/Makefile b/tests/sys/cddl/zfs/tests/zvol/zvol_misc/Makefile
new file mode 100644
index 000000000000..5f29f33a9b35
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_misc/Makefile
@@ -0,0 +1,24 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/zvol/zvol_misc
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zvol_misc_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zvol_misc.cfg
+${PACKAGE}FILES+= zvol_misc_006_pos.ksh
+${PACKAGE}FILES+= zvol_misc_002_pos.ksh
+${PACKAGE}FILES+= zvol_misc_003_neg.ksh
+${PACKAGE}FILES+= zvol_misc_007_pos.ksh
+${PACKAGE}FILES+= zvol_misc_009_pos.ksh
+${PACKAGE}FILES+= zvol_misc_004_pos.ksh
+${PACKAGE}FILES+= zvol_misc_008_pos.ksh
+${PACKAGE}FILES+= zvol_misc_001_neg.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zvol_misc_005_neg.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_misc/cleanup.ksh b/tests/sys/cddl/zfs/tests/zvol/zvol_misc/cleanup.ksh
new file mode 100644
index 000000000000..8f71461118a6
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_misc/cleanup.ksh
@@ -0,0 +1,34 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/zvol/zvol_common.kshlib
+
+verify_runnable "global"
+
+default_zvol_cleanup
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_misc/setup.ksh b/tests/sys/cddl/zfs/tests/zvol/zvol_misc/setup.ksh
new file mode 100644
index 000000000000..40293de6e994
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_misc/setup.ksh
@@ -0,0 +1,34 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/zvol/zvol_common.kshlib
+
+verify_runnable "global"
+
+default_zvol_setup $DISK $VOLSIZE
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc.cfg b/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc.cfg
new file mode 100644
index 000000000000..bba262182002
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc.cfg
@@ -0,0 +1,31 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/zvol/zvol.cfg
+
+export BLOCKSZ=$(( 1024 * 1024 ))
+export NUM_WRITES=40
+export RESERVESNAP=%dumpdev
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_001_neg.ksh b/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_001_neg.ksh
new file mode 100644
index 000000000000..22259314f31d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_001_neg.ksh
@@ -0,0 +1,75 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/zvol/zvol_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zvol_misc_001_neg
+#
+# DESCRIPTION:
+# Verify that using ZFS volume as a dump device fails until
+# dumpswap supported.
+#
+# STRATEGY:
+# 1. Create a ZFS volume
+# 2. Use dumpon add the volume as dump device
+# 3. Verify the return code as expected.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-03-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ typeset dumpdev=$(get_dumpdevice)
+
+ if [[ $dumpdev != $savedumpdev ]] ; then
+ safe_dumpadm $savedumpdev
+ fi
+}
+
+log_assert "Verify that ZFS volume cannot act as dump device until dumpswap supported."
+log_onexit cleanup
+
+voldev=/dev/zvol/$TESTPOOL/$TESTVOL
+savedumpdev=$(get_dumpdevice)
+
+# FreeBSD doesn't support using zvols as dump devices for any pool version
+log_mustnot $DUMPON $voldev
+
+log_pass "ZFS volume cannot act as dump device until dumpswap supported as expected."
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_002_pos.ksh b/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_002_pos.ksh
new file mode 100644
index 000000000000..2c73059cfddc
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_002_pos.ksh
@@ -0,0 +1,84 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zvol_misc_002_pos
+#
+# DESCRIPTION:
+# Verify that ZFS volume snapshot could be fscked
+#
+# STRATEGY:
+# 1. Create a ZFS volume
+# 2. Copy some files and create snapshot
+# 3. Verify fsck on the snapshot is OK
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-10-13)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ snapexists $TESTPOOL/$TESTVOL@snap && \
+ $ZFS destroy $TESTPOOL/$TESTVOL@snap
+
+ ismounted $TESTDIR ufs && log_must $UMOUNT $TESTDIR
+ [[ -e $TESTDIR ]] && $RM -rf $TESTDIR
+}
+
+log_assert "Verify that ZFS volume snapshot could be fscked"
+log_onexit cleanup
+
+$NEWFS /dev/zvol/$TESTPOOL/$TESTVOL >/dev/null 2>&1
+(( $? != 0 )) && log_fail "Unable to newfs(1M) $TESTPOOL/$TESTVOL"
+
+log_must $MKDIR $TESTDIR
+log_must $MOUNT /dev/zvol/$TESTPOOL/$TESTVOL $TESTDIR
+
+typeset -i fn=0
+typeset -i retval=0
+
+# Write about 200MB of data.
+populate_dir $TESTDIR/testfile 5 $NUM_WRITES $BLOCKSZ 0
+
+log_must sync
+log_must $MOUNT -o rw -u $TESTDIR
+log_must $ZFS snapshot $TESTPOOL/$TESTVOL@snap
+log_must $FSCK -t ufs -n /dev/zvol/$TESTPOOL/$TESTVOL@snap >/dev/null 2>&1
+
+log_pass "Verify that ZFS volume snapshot could be fscked"
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_003_neg.ksh b/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_003_neg.ksh
new file mode 100644
index 000000000000..af3b2f48b19d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_003_neg.ksh
@@ -0,0 +1,88 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/zvol/zvol_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zvol_misc_003_neg
+#
+# DESCRIPTION:
+# Verify create storage pool or newfs over volume as dump device is denied.
+#
+# STRATEGY:
+# 1. Create a ZFS volume
+# 2. Use dumpadm set the volume as dump device
+# 3. Verify create pool & newfs over the volume return an error.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-01-07)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ typeset dumpdev=$(get_dumpdevice)
+ if [[ $dumpdev != $savedumpdev ]] ; then
+ safe_dumpadm $savedumpdev
+ fi
+
+ if poolexists $TESTPOOL1 ; then
+ destroy_pool $TESTPOOL1
+ fi
+}
+
+log_assert "Verify create storage pool or newfs over dump volume is denied."
+if ! is_dumpswap_supported $TESTPOOL ; then
+ log_unsupported "dumpswap not currently supported."
+fi
+log_onexit cleanup
+
+test_requires DUMPADM
+
+voldev=/dev/zvol/$TESTPOOL/$TESTVOL
+savedumpdev=$(get_dumpdevice)
+
+safe_dumpadm $voldev
+
+$ECHO "y" | $NEWFS $voldev > /dev/null 2>&1
+if (( $? == 0 )) ; then
+ log_fail "newfs over dump volume succeed unexpected"
+fi
+
+log_mustnot $ZPOOL create $TESTPOOL1 $voldev
+
+log_pass "Verify create storage pool or newfs over dump volume is denied."
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_004_pos.ksh b/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_004_pos.ksh
new file mode 100644
index 000000000000..6f101a34e195
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_004_pos.ksh
@@ -0,0 +1,124 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/zvol/zvol_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zvol_misc_004_pos
+#
+# DESCRIPTION:
+# Verify permit to create snapshot over active dumpswap zvol.
+#
+# STRATEGY:
+# 1. Create a ZFS volume
+# 2. Set the volume as dump or swap
+# 3. Verify create snapshot over the zvol succeed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-01-07)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ typeset dumpdev=$(get_dumpdevice)
+ if [[ $dumpdev != $savedumpdev ]] ; then
+ safe_dumpadm $savedumpdev
+ fi
+
+ $SWAP -l | $GREP -w $voldev > /dev/null 2>&1
+ if (( $? == 0 )); then
+ log_must $SWAP -d $voldev
+ fi
+
+ typeset snap
+ for snap in snap0 snap1 ; do
+ if datasetexists $TESTPOOL/$TESTVOL@$snap ; then
+ log_must $ZFS destroy $TESTPOOL/$TESTVOL@$snap
+ fi
+ done
+}
+
+function verify_snapshot
+{
+ typeset volume=$1
+
+ log_must $ZFS snapshot $volume@snap0
+ log_must $ZFS snapshot $volume@snap1
+ log_must datasetexists $volume@snap0 $volume@snap1
+
+ log_must $ZFS destroy $volume@snap1
+ log_must $ZFS snapshot $volume@snap1
+
+ log_mustnot $ZFS rollback -r $volume@snap0
+ log_must datasetexists $volume@snap0
+ log_must datasetexists $volume@snap1
+
+ log_must $ZFS destroy -r $volume@snap0
+}
+
+log_assert "Verify permit to create snapshot over dumpswap."
+if ! is_dumpswap_supported $TESTPOOL ; then
+ log_unsupported "dumpswap not currently supported."
+fi
+log_onexit cleanup
+
+test_requires DUMPADM
+
+voldev=/dev/zvol/$TESTPOOL/$TESTVOL
+savedumpdev=$(get_dumpdevice)
+
+# create snapshot over dump zvol
+safe_dumpadm $voldev
+log_must is_zvol_dumpified $TESTPOOL/$TESTVOL
+
+verify_snapshot $TESTPOOL/$TESTVOL
+
+safe_dumpadm $savedumpdev
+log_mustnot is_zvol_dumpified $TESTPOOL/$TESTVOL
+
+# create snapshot over swap zvol
+
+log_must $SWAP -a $voldev
+log_mustnot is_zvol_dumpified $TESTPOOL/$TESTVOL
+
+verify_snapshot $TESTPOOL/$TESTVOL
+
+log_must $SWAP -d $voldev
+log_mustnot is_zvol_dumpified $TESTPOOL/$TESTVOL
+
+log_pass "Create snapshot over dumpswap zvol succeed."
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_005_neg.ksh b/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_005_neg.ksh
new file mode 100644
index 000000000000..bbe3a424ec21
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_005_neg.ksh
@@ -0,0 +1,89 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/zvol/zvol_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zvol_misc_005_neg
+#
+# DESCRIPTION:
+# Verify a device cannot be dump and swap at the same time.
+#
+# STRATEGY:
+# 1. Create a ZFS volume
+# 2. Set it as swap device.
+# 3. Verify dumpadm with this zvol will fail.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-03-10)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ $SWAP -l | $GREP $voldev > /dev/null 2>&1
+ if (( $? == 0 )) ; then
+ log_must $SWAP -d $voldev
+ fi
+
+ typeset dumpdev=$(get_dumpdevice)
+ if [[ $dumpdev != $savedumpdev ]] ; then
+ safe_dumpadm $savedumpdev
+ fi
+}
+
+log_assert "Verify a device cannot be dump and swap at the same time."
+if ! is_dumpswap_supported $TESTPOOL ; then
+ log_unsupported "dumpswap not currently supported."
+fi
+log_onexit cleanup
+
+test_requires DUMPADM
+
+voldev=/dev/zvol/$TESTPOOL/$TESTVOL
+savedumpdev=$(get_dumpdevice)
+
+# If device in swap list, it cannot be dump device
+log_must $SWAP -a $voldev
+log_mustnot $DUMPADM -d $voldev
+log_must $SWAP -d $voldev
+
+# If device has dedicated as dump device, it cannot add into swap list
+safe_dumpadm $voldev
+log_mustnot $SWAP -a $voldev
+
+log_pass "A device cannot be dump and swap at the same time."
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_006_pos.ksh b/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_006_pos.ksh
new file mode 100644
index 000000000000..1d217fae3457
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_006_pos.ksh
@@ -0,0 +1,87 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/zvol/zvol_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zvol_misc_006_pos
+#
+# DESCRIPTION:
+# ZFS volume as dump device, it should always have 128k volblocksize
+#
+# STRATEGY:
+# 1. Create a ZFS volume
+# 2. Use dumpadm set the volume as dump device
+# 3. Verify the volume's volblocksize=128k
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-12-01)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ typeset dumpdev=$(get_dumpdevice)
+ if [[ $dumpdev != $savedumpdev ]] ; then
+ safe_dumpadm $savedumpdev
+ fi
+}
+
+log_assert "zfs volume as dumpdevice should have 128k volblocksize"
+
+if ! is_dumpswap_supported $TESTPOOL ; then
+ log_unsupported "dumpswap not currently supported."
+fi
+log_onexit cleanup
+
+test_requires DUMPADM
+
+voldev=/dev/zvol/$TESTPOOL/$TESTVOL
+savedumpdev=$(get_dumpdevice)
+
+typeset oblksize=$($ZFS get -H -o value volblocksize $TESTPOOL/$TESTVOL)
+log_note "original $TESTPOOL/$TESTVOL volblocksize=$oblksize"
+
+safe_dumpadm $voldev
+
+typeset blksize=$($ZFS get -H -o value volblocksize $TESTPOOL/$TESTVOL)
+
+if [[ $blksize != "128K" ]]; then
+ log_fail "ZFS volume $TESTPOOL/$TESTVOL volblocksize=$blksize"
+fi
+
+log_pass "zfs volume as dumpdevice should have 128k volblocksize"
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_007_pos.ksh b/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_007_pos.ksh
new file mode 100644
index 000000000000..319ac68e57da
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_007_pos.ksh
@@ -0,0 +1,128 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/zvol/zvol_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zvol_misc_007_pos
+#
+# DESCRIPTION:
+# Verify that device nodes are modified appropriately during zfs command
+# operations on volumes.
+#
+# STRATEGY:
+# For a certain number of iterations, with root setup for each test set:
+# - Recursively snapshot the root.
+# - Recursively rename the snapshot 3 times.
+# - Destroy the root.
+#
+# - Recursively snapshot the root.
+# - Clone the volume to another name in the root.
+# - Rename the root.
+# - Destroy the renamed root.
+#
+# - Recursively snapshot the root.
+# - Send|Receive the root to another root.
+# - Destroy the original and received roots.
+#
+# At each stage, the device nodes are checked to match the expectations.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-03-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Verify that ZFS volume device nodes are handled properly (part 1)."
+
+ROOTPREFIX=$TESTPOOL/007
+DIRS="dir0 dir1"
+VOLS="vol0 dir0/dvol0 dir1/dvol1"
+
+typeset -i NUM_RENAMES=5
+typeset -i NUM_ITERATIONS=10
+
+function onexit_callback
+{
+ log_must $ZFS list -t all
+ log_note "Char devices in /dev/zvol:"
+ find /dev/zvol -type c
+}
+log_onexit onexit_callback
+
+function root_setup
+{
+ rootds=$1
+
+ log_must $ZFS create $rootds
+ for dir in $DIRS; do
+ log_must $ZFS create $rootds/$dir
+ done
+ for vol in $VOLS; do
+ log_must $ZFS create -V 100M $rootds/$vol
+ log_must test -c /dev/zvol/$rootds/$vol
+ done
+}
+
+typeset -i i=0
+root=""
+while (( i != NUM_ITERATIONS )); do
+ root=${ROOTPREFIX}_iter${i}
+ # Test set 1: Recursive snapshot, recursive rename, and destroy
+ typeset -i cur=0
+ log_mustnot test -e /dev/zvol/$root/vol0
+ root_setup $root
+ log_must $ZFS snapshot -r $root@$cur
+ for vol in $VOLS; do
+ log_must test -c /dev/zvol/$root/$vol@$cur
+ done
+ while ((cur < $NUM_RENAMES)); do
+ ((next = cur + 1))
+ log_must $ZFS rename -r $root@$cur $root@$next
+ for vol in $VOLS; do
+ v=$root/$vol
+ log_mustnot test -e /dev/zvol/$v@$cur
+ log_must test -c /dev/zvol/$v@$next
+ done
+ cur=$next
+ done
+ log_must $ZFS destroy -r $root
+ log_mustnot test -e /dev/zvol/$root/vol0
+
+ (( i += 1 ))
+done
+
+log_pass "ZFS volume device nodes are handled properly."
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_008_pos.ksh b/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_008_pos.ksh
new file mode 100644
index 000000000000..71abd3e42a24
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_008_pos.ksh
@@ -0,0 +1,154 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/zvol/zvol_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zvol_misc_008_pos
+#
+# DESCRIPTION:
+# Verify that device nodes are modified appropriately during zfs command
+# operations on volumes.
+#
+# STRATEGY:
+# For a certain number of iterations, with root setup for each test set:
+# - Recursively snapshot the root.
+# - Clone the volume to another name in the root.
+# - Promote the clone.
+# - Demote the original clone.
+# - Snapshot & clone the clone.
+# - Rename the root.
+# - Destroy the renamed root.
+#
+# At each stage, the device nodes are checked to match the expectations.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-03-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Verify that ZFS volume device nodes are handled properly (part 2)."
+
+ROOTPREFIX=$TESTPOOL/008
+DIRS="dir0 dir1"
+VOLS="vol0 dir0/dvol0 dir1/dvol1"
+
+typeset -i NUM_ITERATIONS=10
+
+function onexit_callback
+{
+ log_must $ZFS list -t all
+ log_note "Char devices in /dev/zvol:"
+ find /dev/zvol -type c
+}
+log_onexit onexit_callback
+
+function root_setup
+{
+ rootds=$1
+
+ log_must $ZFS create $rootds
+ for dir in $DIRS; do
+ log_must $ZFS create $rootds/$dir
+ done
+ for vol in $VOLS; do
+ log_must $ZFS create -V 100M $rootds/$vol
+ log_must test -c /dev/zvol/$rootds/$vol
+ done
+}
+
+function test_exists
+{
+ for zvolds in $*; do
+ log_must test -c /dev/zvol/${zvolds}
+ done
+}
+
+function test_notexists
+{
+ for zvolds in $*; do
+ log_mustnot test -e /dev/zvol/${zvolds}
+ done
+}
+
+typeset -i i=0
+while (( i != NUM_ITERATIONS )); do
+ root=${ROOTPREFIX}_iter${i}
+ # Test set 2: Recursive snapshot, cloning/promoting, and root-rename
+ root_setup $root
+ log_must $ZFS snapshot -r $root@snap
+ log_must $ZFS clone $root/vol0@snap $root/vol1
+ test_exists $root/vol1
+ test_notexists $root/vol1@snap
+
+ log_must $ZFS promote $root/vol1
+ test_exists $root/vol0 $root/vol1 $root/vol1@snap
+ test_notexists $root/vol0@snap
+
+ # Re-promote the original volume.
+ log_must $ZFS promote $root/vol0
+ test_exists $root/vol0 $root/vol1 $root/vol0@snap
+ test_notexists $root/vol1@snap
+
+ # Clone a clone's snapshot.
+ log_must $ZFS snapshot $root/vol1@newsnap
+ log_must $ZFS clone $root/vol1@newsnap $root/vol2
+ test_exists $root/vol2
+ test_notexists $root/vol2@snap
+
+ # Now promote *that* clone.
+ log_must $ZFS promote $root/vol2
+ test_exists $root/vol0 $root/vol0@snap \
+ $root/vol1 $root/vol2 $root/vol2@newsnap
+ test_notexists $root/vol1@snap $root/vol1@newsnap
+
+ renamed=${root}_renamed
+ log_must $ZFS rename $root $renamed
+ # Ensure that the root rename applies to clones and promoted clones.
+ test_exists $renamed/vol1 $renamed/vol2 $renamed/vol2@newsnap
+ test_notexists $root/vol1 $renamed/vol1@snap $renamed/vol1@newsnap
+ for vol in $VOLS; do
+ test_notexists $root/$vol $root/$vol@snap
+ test_exists $renamed/$vol $renamed/$vol@snap
+ done
+
+ log_must $ZFS destroy -r $renamed
+ test_notexists $renamed/vol0 $renamed/vol1 $renamed/vol2
+
+ (( i += 1 ))
+done
+log_pass "ZFS volume device nodes are handled properly (part 2)."
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_009_pos.ksh b/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_009_pos.ksh
new file mode 100644
index 000000000000..6479b6b20333
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_009_pos.ksh
@@ -0,0 +1,121 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/zvol/zvol_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zvol_misc_009_pos
+#
+# DESCRIPTION:
+# Verify that device nodes are modified appropriately during zfs command
+# operations on volumes.
+#
+# STRATEGY:
+# For a certain number of iterations, with root setup for each test set:
+# - Recursively snapshot the root.
+# - Send|Receive the root to another root.
+# - Destroy the original and received roots.
+#
+# At each stage, the device nodes are checked to match the expectations.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2008-03-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "Verify that ZFS volume device nodes are handled properly (part 3)."
+
+ROOTPREFIX=$TESTPOOL/009
+DIRS="dir0 dir1"
+VOLS="vol0 dir0/dvol0 dir1/dvol1"
+
+typeset -i NUM_ITERATIONS=5
+
+function onexit_callback
+{
+ log_must $ZFS list -t all
+ log_note "Char devices in /dev/zvol:"
+ find /dev/zvol -type c
+}
+log_onexit onexit_callback
+
+function root_setup
+{
+ rootds=$1
+
+ log_must $ZFS create $rootds
+ for dir in $DIRS; do
+ log_must $ZFS create $rootds/$dir
+ done
+ for vol in $VOLS; do
+ log_must $ZFS create -V 100M $rootds/$vol
+ log_must test -c /dev/zvol/$rootds/$vol
+ done
+}
+
+typeset -i i=0
+while (( i != NUM_ITERATIONS )); do
+ root=${ROOTPREFIX}_iter${i}
+ # Test set 3: send|receive & receive with forced rollback
+ root_setup $root
+ log_must $ZFS snapshot -r $root@snap
+ received=${root}_recv
+ log_must eval "$ZFS send -R $root@snap | $ZFS receive -F ${received}"
+ for vol in $VOLS; do
+ log_must test -c /dev/zvol/${received}/${vol}
+ log_must test -c /dev/zvol/${received}/${vol}@snap
+ done
+ # Re-send with -F, to ensure that actual receive rollback also works.
+ # For good measure, pre-destroy one of the volumes. Also, destroy
+ # the snapshot we received earlier for this to work.
+ log_must $ZFS destroy -r $received/vol0
+ log_must $ZFS destroy -r $received@snap
+ log_must eval "$ZFS send -R $root@snap | $ZFS receive -F ${received}"
+ for vol in $VOLS; do
+ log_must test -c /dev/zvol/${received}/${vol}
+ log_must test -c /dev/zvol/${received}/${vol}@snap
+ done
+ log_must $ZFS destroy -r ${root}
+ log_mustnot test -e /dev/zvol/${root}/vol0
+ log_mustnot test -e /dev/zvol/${root}/vol0@snap
+ log_must $ZFS destroy -r ${received}
+ log_mustnot test -e /dev/zvol/${received}/vol0
+ log_mustnot test -e /dev/zvol/${received}/vol0@snap
+
+ (( i += 1 ))
+done
+log_pass "ZFS volume device nodes are handled properly (part 3)."
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_test.sh b/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_test.sh
new file mode 100755
index 000000000000..a7702486e4c7
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_misc/zvol_misc_test.sh
@@ -0,0 +1,263 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zvol_misc_001_neg cleanup
+zvol_misc_001_neg_head()
+{
+ atf_set "descr" "Verify that ZFS volume cannot act as dump device until dumpswap supported."
+}
+zvol_misc_001_neg_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zvol_misc_001_neg.ksh || atf_fail "Testcase failed"
+}
+zvol_misc_001_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zvol_misc_002_pos cleanup
+zvol_misc_002_pos_head()
+{
+ atf_set "descr" "Verify that ZFS volume snapshot could be fscked"
+ atf_set "require.progs" "ksh93 zfs"
+}
+zvol_misc_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zvol_misc_002_pos.ksh || atf_fail "Testcase failed"
+}
+zvol_misc_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zvol_misc_003_neg cleanup
+zvol_misc_003_neg_head()
+{
+ atf_set "descr" "Verify create storage pool or newfs over dump volume is denied."
+ atf_set "require.progs" "ksh93 dumpadm zpool"
+}
+zvol_misc_003_neg_body()
+{
+ atf_skip "FreeBSD does not yet support dumping to a zvol"
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zvol_misc_003_neg.ksh || atf_fail "Testcase failed"
+}
+zvol_misc_003_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zvol_misc_004_pos cleanup
+zvol_misc_004_pos_head()
+{
+ atf_set "descr" "Verify permit to create snapshot over dumpswap."
+ atf_set "require.progs" "ksh93 zfs swap"
+}
+zvol_misc_004_pos_body()
+{
+ atf_skip "FreeBSD does not yet support dumping to a zvol"
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zvol_misc_004_pos.ksh || atf_fail "Testcase failed"
+}
+zvol_misc_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zvol_misc_005_neg cleanup
+zvol_misc_005_neg_head()
+{
+ atf_set "descr" "Verify a device cannot be dump and swap at the same time."
+ atf_set "require.progs" "ksh93 dumpadm swap"
+}
+zvol_misc_005_neg_body()
+{
+ atf_skip "FreeBSD does not yet support dumping to a zvol"
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zvol_misc_005_neg.ksh || atf_fail "Testcase failed"
+}
+zvol_misc_005_neg_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zvol_misc_006_pos cleanup
+zvol_misc_006_pos_head()
+{
+ atf_set "descr" "zfs volume as dumpdevice should have 128k volblocksize"
+ atf_set "require.progs" "ksh93 dumpadm zfs"
+}
+zvol_misc_006_pos_body()
+{
+ atf_skip "FreeBSD does not yet support dumping to a zvol"
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zvol_misc_006_pos.ksh || atf_fail "Testcase failed"
+}
+zvol_misc_006_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zvol_misc_007_pos cleanup
+zvol_misc_007_pos_head()
+{
+ atf_set "descr" "zfs volume device nodes are modified appropriately"
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1200
+}
+zvol_misc_007_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_misc.cfg
+ atf_expect_fail "PR 225223 zfs rename -r of a snapshot doesn't rename zvol snapshots' device nodes"
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zvol_misc_007_pos.ksh || atf_fail "Testcase failed"
+}
+zvol_misc_007_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zvol_misc_008_pos cleanup
+zvol_misc_008_pos_head()
+{
+ atf_set "descr" "zfs volume device nodes are modified appropriately"
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1200
+}
+zvol_misc_008_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_misc.cfg
+ atf_expect_fail "PR 225200 zfs promote of a zvol doesn't rename device nodes for snapshots"
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zvol_misc_008_pos.ksh || atf_fail "Testcase failed"
+}
+zvol_misc_008_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zvol_misc_009_pos cleanup
+zvol_misc_009_pos_head()
+{
+ atf_set "descr" "zfs volume device nodes are modified appropriately"
+ atf_set "require.progs" "ksh93 zfs"
+ atf_set "timeout" 1200
+}
+zvol_misc_009_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_misc.cfg
+
+ verify_disk_count "$DISKS" 1
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zvol_misc_009_pos.ksh || atf_fail "Testcase failed"
+}
+zvol_misc_009_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_misc.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zvol_misc_001_neg
+ atf_add_test_case zvol_misc_002_pos
+ atf_add_test_case zvol_misc_003_neg
+ atf_add_test_case zvol_misc_004_pos
+ atf_add_test_case zvol_misc_005_neg
+ atf_add_test_case zvol_misc_006_pos
+ atf_add_test_case zvol_misc_007_pos
+ atf_add_test_case zvol_misc_008_pos
+ atf_add_test_case zvol_misc_009_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_swap/Makefile b/tests/sys/cddl/zfs/tests/zvol/zvol_swap/Makefile
new file mode 100644
index 000000000000..21acc05d8446
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_swap/Makefile
@@ -0,0 +1,21 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/zvol/zvol_swap
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zvol_swap_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= zvol_swap_004_pos.ksh
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= zvol_swap_005_pos.ksh
+${PACKAGE}FILES+= zvol_swap_001_pos.ksh
+${PACKAGE}FILES+= zvol_swap.cfg
+${PACKAGE}FILES+= zvol_swap_003_pos.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zvol_swap_006_pos.ksh
+${PACKAGE}FILES+= zvol_swap_002_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_swap/cleanup.ksh b/tests/sys/cddl/zfs/tests/zvol/zvol_swap/cleanup.ksh
new file mode 100644
index 000000000000..08262bf3cd96
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_swap/cleanup.ksh
@@ -0,0 +1,47 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/zvol/zvol_common.kshlib
+
+verify_runnable "global"
+
+log_must $SWAPADD
+for swapdev in $SAVESWAPDEVS
+do
+ if ! is_swap_inuse $swapdev ; then
+ log_must $SWAP -a $swapdev >/dev/null 2>&1
+ fi
+done
+
+voldev=/dev/zvol/$TESTPOOL/$TESTVOL
+if is_swap_inuse $voldev ; then
+ log_must $SWAP -d $voldev
+fi
+
+default_zvol_cleanup
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_swap/setup.ksh b/tests/sys/cddl/zfs/tests/zvol/zvol_swap/setup.ksh
new file mode 100644
index 000000000000..8326f6b0d773
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_swap/setup.ksh
@@ -0,0 +1,38 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/zvol/zvol_common.kshlib
+
+verify_runnable "global"
+
+for i in $SAVESWAPDEVS ; do
+ log_must eval "$SWAP -d $i >/dev/null 2>&1"
+done
+
+default_zvol_setup $DISK $VOLSIZE
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap.cfg b/tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap.cfg
new file mode 100644
index 000000000000..e5e4458b696b
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap.cfg
@@ -0,0 +1,36 @@
+# vim: filetype=sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/tests/zvol/zvol.cfg
+
+#
+# Remember swap devices
+#
+SAVESWAPDEVS=$($SWAP -l | $NAWK '(NR != 1) {print $1}')
+
+export BLOCKSZ=$(( 1024 * 1024 ))
+export NUM_WRITES=40
+export SAVESWAPDEVS
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap_001_pos.ksh b/tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap_001_pos.ksh
new file mode 100644
index 000000000000..2535b3576f3d
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap_001_pos.ksh
@@ -0,0 +1,88 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/zvol/zvol_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zvol_swap_001_pos
+#
+# DESCRIPTION:
+# Verify that a zvol can be used as a swap device
+#
+# STRATEGY:
+# 1. Create a pool
+# 2. Create a zvol volume
+# 3. Use zvol as swap space
+# 4. Create a file under $TMPDIR
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ $RM -rf $TMPDIR/$TESTFILE
+
+ if is_swap_inuse $voldev ; then
+ log_must $SWAP -d $voldev
+ fi
+}
+
+log_assert "Verify that a zvol can be used as a swap device"
+
+log_onexit cleanup
+
+test_requires SWAP
+
+voldev=/dev/zvol/$TESTPOOL/$TESTVOL
+log_note "Add zvol volume as swap space"
+log_must $SWAP -a $voldev
+
+log_note "Create a file under $TMPDIR"
+log_must $FILE_WRITE -o create -f $TMPDIR/$TESTFILE \
+ -b $BLOCKSZ -c $NUM_WRITES -d $DATA
+
+[[ ! -f $TMPDIR/$TESTFILE ]] &&
+ log_fail "Unable to create file under $TMPDIR"
+
+filesize=`$LS -l $TMPDIR/$TESTFILE | $AWK '{print $5}'`
+tf_size=$(( BLOCKSZ * NUM_WRITES ))
+(( $tf_size != $filesize )) &&
+ log_fail "testfile is ($filesize bytes), expected ($tf_size bytes)"
+
+log_pass "Successfully added a zvol to swap area."
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap_002_pos.ksh b/tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap_002_pos.ksh
new file mode 100644
index 000000000000..113816dd2653
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap_002_pos.ksh
@@ -0,0 +1,95 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/zvol/zvol_common.kshlib
+
+###############################################################################
+#
+# __stc_assertion_start
+#
+# ID: zvol_swap_002_pos
+#
+# DESCRIPTION:
+# Using a zvol as swap space, fill with files until ENOSPC returned.
+#
+# STRATEGY:
+# 1. Create a pool
+# 2. Create a zvol volume
+# 3. Add zvol to swap space
+# 4. Fill swap space until ENOSPC is returned
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+log_unsupported "Fill swap will cause system hang, hide this case temporarily."
+
+verify_runnable "global"
+
+function cleanup
+{
+ $RM -rf $TMPDIR/$TESTDIR
+
+ if is_swap_inuse $voldev ; then
+ log_must $SWAP -d $voldev
+ fi
+}
+
+log_assert "Using a zvol as swap space, fill with files until ENOSPC returned."
+
+if ! is_dumpswap_supported $TESTPOOL ; then
+ log_unsupported "ZVOLs as swap devices are not currently supported."
+fi
+
+log_onexit cleanup
+
+voldev=/dev/zvol/$TESTPOOL/$TESTVOL
+
+$SWAP -l | $GREP zvol
+if (( $? != 0 )) ; then
+ log_note "Add zvol volume as swap space"
+ log_must $SWAP -a $voldev
+fi
+
+typeset -i filenum=0
+typeset -i retval=0
+typeset testdir=$TMPDIR/$TESTDIR
+
+log_note "Attempt to fill $TMPDIR until ENOSPC is hit"
+fill_fs $testdir -1 100 $BLOCKSZ $NUM_WRITES
+retval=$?
+
+(( $retval != $ENOSPC )) && \
+ log_fail "ENOSPC was not returned, $retval was returned instead"
+
+log_pass "ENOSPC was returned as expected"
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap_003_pos.ksh b/tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap_003_pos.ksh
new file mode 100644
index 000000000000..3ba0541983a4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap_003_pos.ksh
@@ -0,0 +1,158 @@
+#! /usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/zvol/zvol_common.kshlib
+
+##################################################################
+#
+# __stc_assertion_start
+#
+# ID: zvol_swap_003_pos
+#
+# DESCRIPTION:
+# Verify that a zvol device can be used as a swap device
+# through /etc/vfstab configuration.
+#
+# STRATEGY:
+# 1. Create a pool
+# 2. Create a zvol volume
+# 3. Save current swaps info and delete current swaps
+# 4. Modify /etc/vfstab to add entry for zvol as swap device
+# 5. Use /sbin/swapadd to zvol as swap device throuth /etc/vfstab
+# 6. Create a file under $TMPDIR
+# 7. Verify the file
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2005-07-04)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ if [[ -f $TMPDIR/$TESTFILE ]]; then
+ log_must $RM -rf $TMPDIR/$TESTFILE
+ fi
+
+ if [[ -f $TMP_VFSTAB_FILE ]]; then
+ log_must $RM -rf $TMP_VFSTAB_FILE
+ fi
+
+ if [[ -f $NEW_VFSTAB_FILE ]]; then
+ log_must $RM -f $NEW_VFSTAB_FILE
+ fi
+
+ if [[ -f $PREV_VFSTAB_FILE ]]; then
+ log_must $MV $PREV_VFSTAB_FILE $VFSTAB_FILE
+ fi
+
+ log_must $SWAPADD $VFSTAB_FILE
+
+ if is_swap_inuse $voldev ; then
+ log_must $SWAP -d $voldev
+ fi
+
+}
+
+
+log_assert "Verify that a zvol device can be used as a swap device"\
+ "through /etc/vfstab configuration."
+
+log_onexit cleanup
+
+test_requires SWAPADD
+
+voldev=/dev/zvol/$TESTPOOL/$TESTVOL
+VFSTAB_FILE=/etc/vfstab
+NEW_VFSTAB_FILE=$TMPDIR/zvol_vfstab.${TESTCASE_ID}
+PREV_VFSTAB_FILE=$TMPDIR/zvol_vfstab.PREV.${TESTCASE_ID}
+TMP_VFSTAB_FILE=$TMPDIR/zvol_vfstab.tmp.${TESTCASE_ID}
+
+if [[ -f $NEW_VFSTAB_FILE ]]; then
+ $RM -f $NEW_VFSTAB_FILE
+fi
+$TOUCH $NEW_VFSTAB_FILE
+$CHMOD 777 $NEW_VFSTAB_FILE
+
+#
+# Go through each line of /etc/vfstab and
+# exclude the comment line and formulate
+# a new file with an entry of zvol device
+# swap device.
+#
+
+$GREP -v "^#" $VFSTAB_FILE > $TMP_VFSTAB_FILE
+
+typeset -i fndswapline=0
+while read -r i
+do
+ line=`$ECHO "$i" | $AWK '{print $4}'`
+ if [[ $line == "swap" ]]; then
+ if [[ $fndswapline -eq 0 ]]; then
+ $ECHO "$voldev"\
+ "\t-\t-\tswap\t-"\
+ "\tno\t-" \
+ >> $NEW_VFSTAB_FILE
+ fndswapline=1
+ $ECHO "Add an entry of zvol device as"\
+ "swap device in $VFSTAB_FILE."
+ fi
+ else
+ $ECHO "$i" >> $NEW_VFSTAB_FILE
+ fi
+
+done < $TMP_VFSTAB_FILE
+
+if [[ $fndswapline -eq 1 ]]; then
+ log_must $CP $VFSTAB_FILE $PREV_VFSTAB_FILE
+ log_must $CP $NEW_VFSTAB_FILE $VFSTAB_FILE
+else
+ log_fail "The system has no swap device configuration in /etc/vfstab"
+fi
+
+log_note "Add zvol volume as swap space"
+log_must $SWAPADD $VFSTAB_FILE
+
+log_note "Create a file under $TMPDIR"
+log_must $FILE_WRITE -o create -f $TMPDIR/$TESTFILE \
+ -b $BLOCKSZ -c $NUM_WRITES -d $DATA
+
+[[ ! -f $TMPDIR/$TESTFILE ]] &&
+ log_fail "Unable to create file under $TMPDIR"
+
+filesize=`$LS -l $TMPDIR/$TESTFILE | $AWK '{print $5}'`
+tf_size=$(( BLOCKSZ * NUM_WRITES ))
+(( $tf_size != $filesize )) && \
+ log_fail "testfile is ($filesize bytes), expected ($tf_size bytes)"
+
+log_pass "Successfully added a zvol to swap area through /etc/vfstab."
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap_004_pos.ksh b/tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap_004_pos.ksh
new file mode 100644
index 000000000000..390c21dd04ad
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap_004_pos.ksh
@@ -0,0 +1,103 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zvol_swap_004_pos
+#
+# DESCRIPTION:
+# The minimum volume size for swap should be a multiple of 2 pagesize
+# bytes.
+#
+# STRATEGY:
+# 1. Get test system page size.
+# 2. Create different size volumes.
+# 3. Verify 'swap -a' has correct behaviour.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-12-12)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ typeset tmp
+ for tmp in $swaplist ; do
+ log_must $SWAP -d $tmp
+ done
+ for tmp in $vollist ; do
+ log_must $ZFS destroy $tmp
+ done
+}
+
+log_assert "The minimum volume size should be a multiple of 2 pagesize bytes."
+log_onexit cleanup
+
+test_requires SWAP
+
+typeset -i volblksize pagesize=$($PAGESIZE)
+((volblksize = pagesize / 2))
+#
+# volume size for swap Expected results
+#
+set -A array \
+ $((volblksize)) "fail" \
+ $((2 * volblksize)) "fail" \
+ $((3 * volblksize)) "fail" \
+ $((4 * volblksize)) "pass" \
+ $((5 * volblksize)) "pass" \
+ $((6 * volblksize)) "pass"
+
+typeset -i i=0
+while ((i < ${#array[@]})); do
+ vol="$TESTPOOL/vol_${array[$i]}"
+ vollist="$vollist $vol"
+
+ log_must $ZFS create -b $volblksize -V ${array[$i]} $vol
+
+ swapname="/dev/zvol/$vol"
+ if [[ ${array[((i+1))]} == "fail" ]]; then
+ log_mustnot $SWAP -a $swapname
+ else
+ log_must $SWAP -a $swapname
+ swaplist="$swaplist $swapname"
+ fi
+
+ ((i += 2))
+done
+
+log_pass "Verify the minimum volume size pass."
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap_005_pos.ksh b/tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap_005_pos.ksh
new file mode 100644
index 000000000000..ccdb1fcd42e5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap_005_pos.ksh
@@ -0,0 +1,104 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/zvol/zvol_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zvol_swap_005_pos
+#
+# DESCRIPTION:
+# swaplow + swaplen must be less than or equal to the volume size.
+#
+# STRATEGY:
+# 1. Get test system page size and test volume size.
+# 2. Random get swaplow and swaplen.
+# 3. Verify swap -a should succeed when swaplow + swaplen <= volume size.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-12-12)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+log_assert "swaplow + swaplen must be less than or equal to the volume size."
+
+test_requires SWAP
+
+typeset vol=$TESTPOOL/$TESTVOL
+typeset -i pageblocks volblocks
+#
+# Both swaplow and swaplen are the desired length of
+# the swap area in 512-byte blocks.
+#
+((pageblocks = $($PAGESIZE) / 512))
+((volblocks = $(get_prop volsize $vol) / 512))
+
+typeset -i i=0
+while ((i < 10)) ; do
+ while true; do
+ ((swaplow = RANDOM % volblocks))
+ # Upwards increment
+ ((swaplow += pageblocks))
+ ((swaplow -= (swaplow % pageblocks)))
+
+ # At lease one page size was left for swap area
+ ((swaplow != volblocks)) && break
+ done
+
+ while true; do
+ ((swaplen = RANDOM % (volblocks - swaplow)))
+ # Downward increment
+ ((swaplen -= (swaplen % pageblocks)))
+
+ # At lease one page size was left for swap area
+ ((swaplen != 0)) && break
+ done
+
+ # The minimum swap size should be 2 pagesize.
+ ((swaplow + swaplen < pageblocks * 2)) && continue
+
+ swapname="/dev/zvol/$vol"
+ if is_swap_inuse $swapname ; then
+ log_must $SWAP -d $swapname
+ fi
+
+ log_must $SWAP -a $swapname $swaplow $swaplen
+ log_must $SWAP -d $swapname $swaplow
+
+ ((i += 1))
+done
+
+log_pass "Verify swaplow + swaplen must be less than or equal to volsize passed."
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap_006_pos.ksh b/tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap_006_pos.ksh
new file mode 100644
index 000000000000..a7f299298579
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap_006_pos.ksh
@@ -0,0 +1,115 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/tests/zvol/zvol_common.kshlib
+
+#################################################################################
+#
+# __stc_assertion_start
+#
+# ID: zvol_swap_006_pos
+#
+# DESCRIPTION:
+# A volume can be add as several segments, but overlapping are not
+# allowed.
+#
+# STRATEGY:
+# 1. Figure out three groups swaplow and swaplen.
+# 2. Verify different volume segments can be added correctly.
+# 3. Verify overlapping swap volume are not allowed.
+#
+# TESTABILITY: explicit
+#
+# TEST_AUTOMATION_LEVEL: automated
+#
+# CODING_STATUS: COMPLETED (2006-12-13)
+#
+# __stc_assertion_end
+#
+################################################################################
+
+verify_runnable "global"
+
+function cleanup
+{
+ typeset -i i=0
+
+ while ((count > 0)); do
+ log_must $SWAP -d $swapname ${swap_opt[$i]}
+
+ ((i += 2))
+ ((count -= 1))
+ done
+}
+
+log_assert "Verify volume can be add as several segments, but overlapping " \
+ "are not allowed."
+log_onexit cleanup
+
+test_requires SWAP
+
+typeset vol=$TESTPOOL/$TESTVOL
+typeset -i pageblocks volblocks
+((pageblocks = $($PAGESIZE) / 512))
+((volblocks = $(get_prop volsize $vol) / 512))
+
+log_note "Verify volume can be add as several segments."
+
+#
+# swaplow swaplen
+set -A swap_opt $((pageblocks)) \
+ $((pageblocks * ((RANDOM % 50) + 1) + (RANDOM % pageblocks) )) \
+ $((volblocks / 3)) \
+ $((pageblocks * ((RANDOM % 50) + 1) + (RANDOM % pageblocks) )) \
+ $((volblocks / 2)) \
+ $((pageblocks * ((RANDOM % 50) + 1) + (RANDOM % pageblocks) )) \
+ $(((volblocks*2) / 3)) \
+ $((pageblocks * ((RANDOM % 50) + 1) + (RANDOM % pageblocks) ))
+
+swapname=/dev/zvol/$vol
+typeset -i i=0 count=0
+
+if is_swap_inuse $swapname ; then
+ log_must $SWAP -d $swapname
+fi
+
+while ((i < ${#swap_opt[@]})); do
+ log_must $SWAP -a $swapname ${swap_opt[$i]} ${swap_opt[((i+1))]}
+
+ ((i += 2))
+ ((count += 1))
+done
+
+log_note "Verify overlapping swap volume are not allowed"
+i=0
+while ((i < ${#swap_opt[@]})); do
+ log_mustnot $SWAP -a $swapname ${swap_opt[$i]}
+
+ ((i += 2))
+done
+
+log_pass "Verify volume can be added as several segments passed."
diff --git a/tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap_test.sh b/tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap_test.sh
new file mode 100755
index 000000000000..8479f37f0c2c
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol/zvol_swap/zvol_swap_test.sh
@@ -0,0 +1,174 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zvol_swap_001_pos cleanup
+zvol_swap_001_pos_head()
+{
+ atf_set "descr" "Verify that a zvol can be used as a swap device"
+ atf_set "require.progs" "ksh93 swap swapadd"
+}
+zvol_swap_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_swap.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zvol_swap_001_pos.ksh || atf_fail "Testcase failed"
+}
+zvol_swap_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_swap.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zvol_swap_002_pos cleanup
+zvol_swap_002_pos_head()
+{
+ atf_set "descr" "Using a zvol as swap space, fill with files until ENOSPC returned."
+ atf_set "require.progs" "ksh93 swap swapadd"
+}
+zvol_swap_002_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_swap.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zvol_swap_002_pos.ksh || atf_fail "Testcase failed"
+}
+zvol_swap_002_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_swap.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zvol_swap_003_pos cleanup
+zvol_swap_003_pos_head()
+{
+ atf_set "descr" "Verify that a zvol device can be used as a swap devicethrough /etc/vfstab configuration."
+ atf_set "require.progs" "ksh93 swapadd swap"
+}
+zvol_swap_003_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_swap.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zvol_swap_003_pos.ksh || atf_fail "Testcase failed"
+}
+zvol_swap_003_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_swap.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zvol_swap_004_pos cleanup
+zvol_swap_004_pos_head()
+{
+ atf_set "descr" "The minimum volume size should be a multiple of 2 pagesize bytes."
+ atf_set "require.progs" "ksh93 zfs swap pagesize swapadd"
+}
+zvol_swap_004_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_swap.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zvol_swap_004_pos.ksh || atf_fail "Testcase failed"
+}
+zvol_swap_004_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_swap.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zvol_swap_005_pos cleanup
+zvol_swap_005_pos_head()
+{
+ atf_set "descr" "swaplow + swaplen must be less than or equal to the volume size."
+ atf_set "require.progs" "ksh93 swap pagesize swapadd"
+}
+zvol_swap_005_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_swap.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zvol_swap_005_pos.ksh || atf_fail "Testcase failed"
+}
+zvol_swap_005_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_swap.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_test_case zvol_swap_006_pos cleanup
+zvol_swap_006_pos_head()
+{
+ atf_set "descr" "Verify volume can be add as several segments, but overlappingare not allowed."
+ atf_set "require.progs" "ksh93 swap pagesize swapadd"
+}
+zvol_swap_006_pos_body()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_swap.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zvol_swap_006_pos.ksh || atf_fail "Testcase failed"
+}
+zvol_swap_006_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_swap.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zvol_swap_001_pos
+ atf_add_test_case zvol_swap_002_pos
+ atf_add_test_case zvol_swap_003_pos
+ atf_add_test_case zvol_swap_004_pos
+ atf_add_test_case zvol_swap_005_pos
+ atf_add_test_case zvol_swap_006_pos
+}
diff --git a/tests/sys/cddl/zfs/tests/zvol_thrash/Makefile b/tests/sys/cddl/zfs/tests/zvol_thrash/Makefile
new file mode 100644
index 000000000000..71c8f330b156
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol_thrash/Makefile
@@ -0,0 +1,16 @@
+.include <src.opts.mk>
+
+PACKAGE=tests
+TESTSDIR=${TESTSBASE}/sys/cddl/zfs/tests/zvol_thrash
+FILESDIR=${TESTSDIR}
+
+ATF_TESTS_KSH93+= zvol_thrash_test
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= setup.ksh
+${PACKAGE}FILES+= cleanup.ksh
+${PACKAGE}FILES+= zvol_thrash.cfg
+${PACKAGE}FILES+= zvol_thrash_001_pos.ksh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/cddl/zfs/tests/zvol_thrash/cleanup.ksh b/tests/sys/cddl/zfs/tests/zvol_thrash/cleanup.ksh
new file mode 100644
index 000000000000..88fa5783ca93
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol_thrash/cleanup.ksh
@@ -0,0 +1,35 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+. ${STF_SUITE}/include/libgnop.kshlib
+
+# Rotate logs now, because this test can generate a great volume of log entries
+newsyslog
+
+default_cleanup_noexit
+destroy_gnops ${DISKS}
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zvol_thrash/setup.ksh b/tests/sys/cddl/zfs/tests/zvol_thrash/setup.ksh
new file mode 100644
index 000000000000..296f8d544c74
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol_thrash/setup.ksh
@@ -0,0 +1,35 @@
+#!/usr/local/bin/ksh93 -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+. ${STF_SUITE}/include/libtest.kshlib
+. $STF_SUITE/include/libgnop.kshlib
+
+log_must create_gnops ${DISKS}
+
+# Rotate logs now, because this test can generate a great volume of log entries
+newsyslog
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zvol_thrash/zvol_thrash.cfg b/tests/sys/cddl/zfs/tests/zvol_thrash/zvol_thrash.cfg
new file mode 100644
index 000000000000..847930f43ecd
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol_thrash/zvol_thrash.cfg
@@ -0,0 +1,32 @@
+# vim: filetype=sh
+#
+# Copyright (c) 2010 Spectra Logic Corporation
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions, and the following disclaimer,
+# without modification.
+# 2. Redistributions in binary form must reproduce at minimum a disclaimer
+# substantially similar to the "NO WARRANTY" disclaimer below
+# ("Disclaimer") and any redistribution must be conditioned upon
+# including a substantially similar Disclaimer requirement for further
+# binary redistribution.
+#
+# NO WARRANTY
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGES.
+#
+#
+export RUNTIME=180
diff --git a/tests/sys/cddl/zfs/tests/zvol_thrash/zvol_thrash_001_pos.ksh b/tests/sys/cddl/zfs/tests/zvol_thrash/zvol_thrash_001_pos.ksh
new file mode 100644
index 000000000000..48ba347c2bd4
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol_thrash/zvol_thrash_001_pos.ksh
@@ -0,0 +1,111 @@
+#!/usr/local/bin/ksh93
+#
+# Copyright (c) 2010 Spectra Logic Corporation
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions, and the following disclaimer,
+# without modification.
+# 2. Redistributions in binary form must reproduce at minimum a disclaimer
+# substantially similar to the "NO WARRANTY" disclaimer below
+# ("Disclaimer") and any redistribution must be conditioned upon
+# including a substantially similar Disclaimer requirement for further
+# binary redistribution.
+#
+# NO WARRANTY
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGES.
+#
+#
+
+. $STF_SUITE/include/libtest.kshlib
+. $STF_SUITE/include/libgnop.kshlib
+
+# Cleanup function. Kill each of the children.
+function docleanup
+{
+ for CPID in $CHILDREN
+ do
+ echo "Killing $CPID"
+ kill $CPID
+ done
+ for CPID in $CHILDREN
+ do
+ wait $CPID
+ done
+}
+
+function childcleanup
+{
+ exit 0
+}
+
+# Wait for the timeout, and then kill the child processes.
+function childrentimeout
+{
+ log_note "childrentimeout process waiting $1 seconds"
+ sleep $1
+ docleanup
+}
+
+function mk_vols
+{
+ ADISKS=($DISKS) #Create an array for convenience
+ N_DISKS=${#ADISKS[@]}
+ N_MIRRORS=$(($N_DISKS / 2 ))
+ # Use a special ksh93 expansion to generate the list of gnop devices
+ GNOPS=${DISKS//~(E)([[:space:]]+|$)/.nop\1}
+ setup_mirrors $N_MIRRORS $GNOPS
+ for pool in `all_pools`; do
+ # Create 4 ZVols per pool. Write a geom label to each, just so
+ # that we have another geom class between zvol and the vdev
+ # taster. That thwarts detection of zvols based on a geom
+ # producer's class name, as was attempted by Perforce change
+ # 538882
+ for ((j=0; $j<4; j=$j+1)); do
+ $ZFS create -V 10G $pool/testvol.$j
+ glabel label testlabel$j /dev/zvol/$pool/testvol.$j
+ done
+ done
+}
+
+export CHILDREN=""
+
+log_onexit docleanup
+
+log_assert "Cause frequent device removal and arrival in the prescence of zvols. ZFS should not crash or hang while tasting them for VDev GUIDs."
+mk_vols
+for p in `all_pools`
+do
+ #Take the first gnop in the pool
+ typeset gnop
+ typeset disk
+ gnop=`get_disklist $p | cut -d " " -f 1`
+ disk=${gnop%.nop}
+
+ log_note "thrashing $gnop"
+ trap childcleanup INT TERM && while `true`; do
+ log_must destroy_gnop $disk
+ $SLEEP 5
+ log_must create_gnop $disk
+ $SLEEP 5
+ done &
+ CHILDREN="$CHILDREN $!"
+done
+
+log_note "Waiting $RUNTIME seconds for potential ZFS failure"
+childrentimeout $RUNTIME
+
+log_pass
diff --git a/tests/sys/cddl/zfs/tests/zvol_thrash/zvol_thrash_test.sh b/tests/sys/cddl/zfs/tests/zvol_thrash/zvol_thrash_test.sh
new file mode 100755
index 000000000000..fb167c4b63b5
--- /dev/null
+++ b/tests/sys/cddl/zfs/tests/zvol_thrash/zvol_thrash_test.sh
@@ -0,0 +1,55 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2012 Spectra Logic. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+atf_test_case zvol_thrash_001_pos cleanup
+zvol_thrash_001_pos_head()
+{
+ atf_set "descr" "Cause frequent device removal and arrival in the prescence of zvols. ZFS should not misbehave while tasting them for VDev GUIDs."
+ atf_set "require.progs" "ksh93 zfs zpool gnop"
+ atf_set "timeout" 900
+}
+zvol_thrash_001_pos_body()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_thrash.cfg
+
+ ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
+ ksh93 $(atf_get_srcdir)/zvol_thrash_001_pos.ksh || atf_fail "Testcase failed"
+}
+zvol_thrash_001_pos_cleanup()
+{
+ . $(atf_get_srcdir)/../../include/default.cfg
+ . $(atf_get_srcdir)/zvol_thrash.cfg
+
+ ksh93 $(atf_get_srcdir)/cleanup.ksh || atf_fail "Cleanup failed"
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case zvol_thrash_001_pos
+}
diff --git a/tests/sys/common/Makefile b/tests/sys/common/Makefile
new file mode 100644
index 000000000000..269aa9d52f9a
--- /dev/null
+++ b/tests/sys/common/Makefile
@@ -0,0 +1,12 @@
+PACKAGE= tests
+TESTSDIR= ${TESTSBASE}/sys/common
+${PACKAGE}FILES+= vnet.subr
+${PACKAGE}FILES+= divert.py
+${PACKAGE}FILES+= sender.py
+${PACKAGE}FILES+= net_receiver.py
+
+${PACKAGE}FILESMODE_divert.py=0555
+${PACKAGE}FILESMODE_sender.py=0555
+${PACKAGE}FILESMODE_net_receiver.py=0555
+
+.include <bsd.test.mk>
diff --git a/tests/sys/common/divert.py b/tests/sys/common/divert.py
new file mode 100755
index 000000000000..830b61a585a0
--- /dev/null
+++ b/tests/sys/common/divert.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+# -
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Alexander V. Chernikov
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+
+from socket import socket, PF_DIVERT, SOCK_RAW
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sc
+import argparse
+
+
+def parse_args():
+ parser = argparse.ArgumentParser(description='divert socket tester')
+ parser.add_argument('--dip', type=str, help='destination packet IP')
+ parser.add_argument('--sip', type=str, help='source packet IP')
+ parser.add_argument('--divert_port', type=int, default=6668,
+ help='divert port to use')
+ parser.add_argument('--test_name', type=str, required=True,
+ help='test name to run')
+ return parser.parse_args()
+
+
+def ipdivert_ip_output_remote_success(args):
+ packet = sc.IP(dst=args.dip) / sc.ICMP(type='echo-request')
+ with socket(PF_DIVERT, SOCK_RAW, 0) as s:
+ s.bind(('0.0.0.0', args.divert_port))
+ s.sendto(bytes(packet), ('0.0.0.0', 0))
+
+
+def ipdivert_ip6_output_remote_success(args):
+ packet = sc.IPv6(dst=args.dip) / sc.ICMPv6EchoRequest()
+ with socket(PF_DIVERT, SOCK_RAW, 0) as s:
+ s.bind(('0.0.0.0', args.divert_port))
+ s.sendto(bytes(packet), ('0.0.0.0', 0))
+
+
+def ipdivert_ip_input_local_success(args):
+ """Sends IPv4 packet to OS stack as inbound local packet."""
+ packet = sc.IP(dst=args.dip,src=args.sip) / sc.ICMP(type='echo-request')
+ with socket(PF_DIVERT, SOCK_RAW, 0) as s:
+ s.bind(('0.0.0.0', args.divert_port))
+ s.sendto(bytes(packet), (args.dip, 0))
+
+
+# XXX: IPv6 local divert is currently not supported
+# TODO: add IPv4 ifname output verification
+
+
+def main():
+ args = parse_args()
+ test_ptr = globals()[args.test_name]
+ test_ptr(args)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/common/net_receiver.py b/tests/sys/common/net_receiver.py
new file mode 100755
index 000000000000..63c8a6927fd2
--- /dev/null
+++ b/tests/sys/common/net_receiver.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+# -
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Alexander V. Chernikov
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+
+from functools import partial
+import socket
+import select
+import argparse
+import time
+
+
+def parse_args():
+ parser = argparse.ArgumentParser(description='divert socket tester')
+ parser.add_argument('--sip', type=str, default='', help='IP to listen on')
+ parser.add_argument('--family', type=str, help='inet/inet6')
+ parser.add_argument('--ports', type=str, help='packet ports 1,2,3')
+ parser.add_argument('--match_str', type=str, help='match string to use')
+ parser.add_argument('--count', type=int, default=1,
+ help='Number of messages to receive')
+ parser.add_argument('--test_name', type=str, required=True,
+ help='test name to run')
+ return parser.parse_args()
+
+
+def test_listen_tcp(args):
+ if args.family == 'inet6':
+ fam = socket.AF_INET6
+ else:
+ fam = socket.AF_INET
+ sockets = []
+ ports = [int(port) for port in args.ports.split(',')]
+ for port in ports:
+ s = socket.socket(fam, socket.SOCK_STREAM)
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ s.setblocking(0)
+ s.bind((args.sip, port))
+ print('binding on {}:{}'.format(args.sip, port))
+ s.listen(5)
+ sockets.append(s)
+ inputs = sockets
+ count = 0
+ while count < args.count:
+ readable, writable, exceptional = select.select(inputs, [], inputs)
+ for s in readable:
+ (c, address) = s.accept()
+ print('C: {}'.format(address))
+ data = c.recv(9000)
+ if args.match_str and args.match_str.encode('utf-8') != data:
+ raise Exception('Expected "{}" but got "{}"'.format(
+ args.match_str, data.decode('utf-8')))
+ count += 1
+ c.close()
+
+
+def test_listen_udp(args):
+ if args.family == 'inet6':
+ fam = socket.AF_INET6
+ else:
+ fam = socket.AF_INET
+ sockets = []
+ ports = [int(port) for port in args.ports.split(',')]
+ for port in ports:
+ s = socket.socket(fam, socket.SOCK_DGRAM)
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ s.setblocking(0)
+ s.bind((args.sip, port))
+ print('binding on {}:{}'.format(args.sip, port))
+ sockets.append(s)
+ inputs = sockets
+ count = 0
+ while count < args.count:
+ readable, writable, exceptional = select.select(inputs, [], inputs)
+ for s in readable:
+ (data, address) = s.recvfrom(9000)
+ print('C: {}'.format(address))
+ if args.match_str and args.match_str.encode('utf-8') != data:
+ raise Exception('Expected "{}" but got "{}"'.format(
+ args.match_str, data.decode('utf-8')))
+ count += 1
+
+
+def main():
+ args = parse_args()
+ test_ptr = globals()[args.test_name]
+ test_ptr(args)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/common/sender.py b/tests/sys/common/sender.py
new file mode 100755
index 000000000000..b2a64371e8bb
--- /dev/null
+++ b/tests/sys/common/sender.py
@@ -0,0 +1,202 @@
+#!/usr/bin/env python
+# -
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Alexander V. Chernikov
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+
+from functools import partial
+import socket
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sc
+import argparse
+import time
+
+
+def parse_args():
+ parser = argparse.ArgumentParser(description='divert socket tester')
+ parser.add_argument('--dip', type=str, help='destination packet IP')
+ parser.add_argument('--sip', type=str, help='source packet IP')
+ parser.add_argument('--dmac', type=str, help='packet dst mac')
+ parser.add_argument('--smac', type=str, help='packet src mac')
+ parser.add_argument('--iface', type=str, help='interface to use')
+ parser.add_argument('--test_name', type=str, required=True,
+ help='test name to run')
+ return parser.parse_args()
+
+
+def send_packet(args, pkt):
+ sc.sendp(pkt, iface=args.iface, verbose=False)
+
+
+def is_icmp6_echo_request(pkt):
+ return pkt.type == 0x86DD and pkt.payload.nh == 58 and \
+ pkt.payload.payload.type == 128
+
+
+def check_forwarded_ip_packet(orig_pkt, fwd_pkt):
+ """
+ Checks that forwarded ICMP packet @fwd_ptk is the same as
+ @orig_pkt. Assumes router-on-the-stick forwarding behaviour:
+ * src/dst macs are swapped
+ * TTL is decremented
+ """
+ # Check ether fields
+ assert orig_pkt.src == fwd_pkt.dst
+ assert orig_pkt.dst == fwd_pkt.src
+ assert len(orig_pkt) == len(fwd_pkt)
+ # Check IP
+ fwd_ip = fwd_pkt[sc.IP]
+ orig_ip = orig_pkt[sc.IP]
+ assert orig_ip.src == orig_ip.src
+ assert orig_ip.dst == fwd_ip.dst
+ assert orig_ip.ttl == fwd_ip.ttl + 1
+ # Check ICMP
+ fwd_icmp = fwd_ip[sc.ICMP]
+ orig_icmp = orig_ip[sc.ICMP]
+ assert bytes(orig_ip.payload) == bytes(fwd_ip.payload)
+
+
+def fwd_ip_icmp_fast(args):
+ """
+ Sends ICMP packet via args.iface interface.
+ Receives and checks the forwarded packet.
+ Assumes forwarding router decrements TTL
+ """
+
+ def filter_f(x):
+ return x.src == args.dmac and x.type == 0x0800
+
+ e = sc.Ether(src=args.smac, dst=args.dmac)
+ ip = sc.IP(src=args.sip, dst=args.dip)
+ icmp = sc.ICMP(type='echo-request')
+ pkt = e / ip / icmp
+
+ send_cb = partial(send_packet, args, pkt)
+ packets = sc.sniff(iface=args.iface, started_callback=send_cb,
+ stop_filter=filter_f, lfilter=filter_f, timeout=5)
+ assert len(packets) > 0
+ fwd_pkt = packets[-1]
+ try:
+ check_forwarded_ip_packet(pkt, fwd_pkt)
+ except Exception as e:
+ print('Original packet:')
+ pkt.show()
+ print('Forwarded packet:')
+ fwd_pkt.show()
+ for a_packet in packets:
+ a_packet.summary()
+ raise Exception from e
+
+
+def fwd_ip_icmp_slow(args):
+ """
+ Sends ICMP packet via args.iface interface.
+ Forces slow path processing by introducing IP option.
+ Receives and checks the forwarded packet.
+ Assumes forwarding router decrements TTL
+ """
+
+ def filter_f(x):
+ return x.src == args.dmac and x.type == 0x0800
+
+ e = sc.Ether(src=args.smac, dst=args.dmac)
+ # Add IP option to switch to 'normal' IP processing
+ stream_id = sc.IPOption_Stream_Id(security=0xFFFF)
+ ip = sc.IP(src=args.sip, dst=args.dip,
+ options=[sc.IPOption_Stream_Id(security=0xFFFF)])
+ icmp = sc.ICMP(type='echo-request')
+ pkt = e / ip / icmp
+
+ send_cb = partial(send_packet, args, pkt)
+ packets = sc.sniff(iface=args.iface, started_callback=send_cb,
+ stop_filter=filter_f, lfilter=filter_f, timeout=5)
+ assert len(packets) > 0
+ check_forwarded_ip_packet(pkt, packets[-1])
+
+
+def check_forwarded_ip6_packet(orig_pkt, fwd_pkt):
+ """
+ Checks that forwarded ICMP packet @fwd_ptk is the same as
+ @orig_pkt. Assumes router-on-the-stick forwarding behaviour:
+ * src/dst macs are swapped
+ * TTL is decremented
+ """
+ # Check ether fields
+ assert orig_pkt.src == fwd_pkt.dst
+ assert orig_pkt.dst == fwd_pkt.src
+ assert len(orig_pkt) == len(fwd_pkt)
+ # Check IP
+ fwd_ip = fwd_pkt[sc.IPv6]
+ orig_ip = orig_pkt[sc.IPv6]
+ assert orig_ip.src == orig_ip.src
+ assert orig_ip.dst == fwd_ip.dst
+ assert orig_ip.hlim == fwd_ip.hlim + 1
+ # Check ICMPv6
+ assert bytes(orig_ip.payload) == bytes(fwd_ip.payload)
+
+
+def fwd_ip6_icmp(args):
+ """
+ Sends ICMPv6 packet via args.iface interface.
+ Receives and checks the forwarded packet.
+ Assumes forwarding router decrements TTL
+ """
+
+ def filter_f(x):
+ return x.src == args.dmac and is_icmp6_echo_request(x)
+
+ e = sc.Ether(src=args.smac, dst=args.dmac)
+ ip = sc.IPv6(src=args.sip, dst=args.dip)
+ icmp = sc.ICMPv6EchoRequest()
+ pkt = e / ip / icmp
+
+ send_cb = partial(send_packet, args, pkt)
+ packets = sc.sniff(iface=args.iface, started_callback=send_cb,
+ stop_filter=filter_f, lfilter=filter_f, timeout=5)
+ assert len(packets) > 0
+ fwd_pkt = packets[-1]
+ try:
+ check_forwarded_ip6_packet(pkt, fwd_pkt)
+ except Exception as e:
+ print('Original packet:')
+ pkt.show()
+ print('Forwarded packet:')
+ fwd_pkt.show()
+ for idx, a_packet in enumerate(packets):
+ print('{}: {}'.format(idx, a_packet.summary()))
+ raise Exception from e
+
+
+def main():
+ args = parse_args()
+ test_ptr = globals()[args.test_name]
+ test_ptr(args)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/common/vnet.subr b/tests/sys/common/vnet.subr
new file mode 100644
index 000000000000..bd98b02da33f
--- /dev/null
+++ b/tests/sys/common/vnet.subr
@@ -0,0 +1,132 @@
+# VNET/jail utility functions
+##
+
+list_interface()
+{
+ echo $1 >> created_interfaces.lst
+}
+
+unlist_interface()
+{
+ sed -i "" /^$1\$/d created_interfaces.lst
+}
+
+_vnet_check_req()
+{
+ type=$1
+
+ if kldstat -q -n if_${type}.ko; then
+ return
+ fi
+
+ if ! kldload -n -q if_${type}; then
+ atf_skip "if_${type}.ko is required to run this test."
+ return
+ fi
+}
+
+vnet_init()
+{
+ if [ "`sysctl -i -n kern.features.vimage`" != 1 ]; then
+ atf_skip "This test requires VIMAGE"
+ fi
+
+ # Check if we can create if_epair or if_bridge interfaces.
+ # We may be running in a jail already, unable to load modules.
+ # If so, skip this test because it very likely (but not certainly)
+ # wants at least one of those
+ _vnet_check_req epair
+ _vnet_check_req bridge
+}
+
+vnet_mkepair()
+{
+ ifname=$(ifconfig epair create)
+ list_interface $ifname
+ list_interface ${ifname%a}b
+ echo ${ifname%a}
+}
+
+vnet_init_bridge()
+{
+ if ! kldstat -q -m if_bridge; then
+ atf_skip "This test requires if_bridge"
+ fi
+}
+
+vnet_mkbridge()
+{
+ ifname=$(ifconfig bridge create)
+ list_interface $ifname
+ echo ${ifname}
+}
+
+vnet_mkvlan()
+{
+ ifname=$(ifconfig vlan create)
+ list_interface $ifname
+ echo ${ifname}
+}
+
+vnet_mkloopback()
+{
+ ifname=$(ifconfig lo create)
+ list_interface $ifname
+ echo ${ifname}
+}
+
+vnet_mkjail()
+{
+ jailname=$1
+ shift
+
+ vnet_interfaces=
+ for ifname in $@
+ do
+ vnet_interfaces="${vnet_interfaces} vnet.interface=${ifname}"
+ unlist_interface $ifname
+ done
+ jail -c name=${jailname} persist vnet ${vnet_interfaces}
+
+ echo $jailname $@ >> created_jails.lst
+}
+
+vnet_ifmove()
+{
+ ifname=$1
+ jailname=$2
+
+ ifconfig ${ifname} vnet ${jailname}
+ unlist_interface $ifname
+ sed -i "" "/^${jailname}/s/\$/ ${ifname}/" created_jails.lst
+}
+
+vnet_ifrename_jail()
+{
+ jailname=$1
+ ifname=$2
+ ifnewname=$3
+
+ ifconfig -j ${jailname} $ifname name $ifnewname
+ sed -i "" "/^${jailname}/s/${ifname}/${ifnewname}/" created_jails.lst
+}
+
+vnet_cleanup()
+{
+ if [ -f created_jails.lst ]; then
+ while read jailname ifnames; do
+ for ifname in ${ifnames}; do
+ ifconfig -j ${jailname} ${ifname} destroy
+ done
+ jail -r ${jailname}
+ done < created_jails.lst
+ rm created_jails.lst
+ fi
+
+ if [ -f created_interfaces.lst ]; then
+ while read ifname; do
+ ifconfig ${ifname} destroy
+ done < created_interfaces.lst
+ rm created_interfaces.lst
+ fi
+}
diff --git a/tests/sys/compat32/Makefile b/tests/sys/compat32/Makefile
new file mode 100644
index 000000000000..e1a35422410e
--- /dev/null
+++ b/tests/sys/compat32/Makefile
@@ -0,0 +1,5 @@
+.if exists(${.CURDIR}/${MACHINE_ARCH})
+SUBDIR+= ${MACHINE_ARCH}
+.endif
+
+.include <bsd.subdir.mk>
diff --git a/tests/sys/compat32/Makefile.inc b/tests/sys/compat32/Makefile.inc
new file mode 100644
index 000000000000..7f870e3701fb
--- /dev/null
+++ b/tests/sys/compat32/Makefile.inc
@@ -0,0 +1,3 @@
+TESTSDIR= ${TESTSBASE}/sys/compat32
+
+.include "../Makefile.inc"
diff --git a/tests/sys/compat32/aarch64/Makefile b/tests/sys/compat32/aarch64/Makefile
new file mode 100644
index 000000000000..f5961b06cc8b
--- /dev/null
+++ b/tests/sys/compat32/aarch64/Makefile
@@ -0,0 +1,27 @@
+# XXX: Doesn't work with GCC and requires an LLVM with the ARM backend
+.if 0
+PACKAGE= tests
+FILESGROUPS+= asmprogs
+
+ACFLAGS= -target armv7-unknown-freebsd${OS_REVISION} -nostdlib -Wl,-e -Wl,main -static
+
+TAP_TESTS_SH+= swp_cond_test
+TAP_TESTS_SH+= swp_test
+${PACKAGE}FILES+= common.sh
+
+# Each test will individually respect the compat.arm.emul_swp
+# sysctl upon entry.
+TEST_METADATA.swp_cond_test+= is_exclusive=true
+TEST_METADATA.swp_test+= is_exclusive=true
+
+asmprogsMODE= 0755
+asmprogs+= swp_cond_test_impl swp_test_impl
+asmprogsDIR= ${TESTSDIR}
+
+.for aprog in ${asmprogs}
+${aprog}: ${aprog}.S
+ ${CC} ${ACFLAGS} -o ${.TARGET} ${.ALLSRC}
+.endfor
+
+.include <bsd.test.mk>
+.endif
diff --git a/tests/sys/compat32/aarch64/common.sh b/tests/sys/compat32/aarch64/common.sh
new file mode 100644
index 000000000000..f8b02e8ac157
--- /dev/null
+++ b/tests/sys/compat32/aarch64/common.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+if ! sysctl -n kern.features.compat_freebsd_32bit >/dev/null 2>&1; then
+ echo "1..0 # Skipped: Kernel not built with COMPAT_FREEBSD32"
+ exit 0
+elif ! sysctl -n kern.supported_archs | grep -q '\<armv7\>'; then
+ echo "1..0 # Skipped: 32-bit ARM not supported on this hardware"
+ exit 0
+fi
diff --git a/tests/sys/compat32/aarch64/swp_cond_test.sh b/tests/sys/compat32/aarch64/swp_cond_test.sh
new file mode 100644
index 000000000000..8edfa43b3e7e
--- /dev/null
+++ b/tests/sys/compat32/aarch64/swp_cond_test.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+scriptdir=$(dirname $(realpath "$0"))
+
+. ${scriptdir}/common.sh
+
+# Ensure emul_swp is enabled just for this test; we'll turn it back off if
+# it wasn't enabled before the test.
+emul_swpval=$(sysctl -n compat.arm.emul_swp)
+sysctl compat.arm.emul_swp=1 >/dev/null
+${scriptdir}/swp_test_impl
+if [ "$emul_swpval" -ne 1 ]; then
+ sysctl compat.arm.emul_swp="$emul_swpval" >/dev/null
+fi
diff --git a/tests/sys/compat32/aarch64/swp_cond_test_impl.S b/tests/sys/compat32/aarch64/swp_cond_test_impl.S
new file mode 100644
index 000000000000..d29db44832b1
--- /dev/null
+++ b/tests/sys/compat32/aarch64/swp_cond_test_impl.S
@@ -0,0 +1,413 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2021 Warner Losh
+ * Copyright (c) 2023 Stormshield
+ * Copyright (c) 2023 Klara, Inc.
+ */
+
+#include <sys/syscall.h>
+
+#define STDOUT_FILENO 1
+#define SWP_MAGIC 0xffc0
+#define SWPB_MAGIC 0xc0c0
+
+ .text
+ .file "swp_test.S"
+ .syntax unified
+ .globl main
+ .p2align 2
+ .type main,%function
+ .code 32
+
+main:
+ sub sp, #0x04
+ /* r4 is our failed test counter */
+ mov r4, #0
+ /* r6 is our current teset counter */
+ mov r6, #1
+
+ movw r0, :lower16:.L.testheader
+ movt r0, :upper16:.L.testheader
+ ldr r1, =(.L.testheaderEnd - .L.testheader - 1)
+ bl print
+
+ /* eq */
+ bl reset
+ mov r1, #SWP_MAGIC
+ cmp r1, r1
+ swpeq r0, r1, [r0]
+ bl expect_success
+
+ /* Returned 0 (bad) or 1 (ok) */
+ cmp r0, #0
+ beq 1f
+
+ /* !eq */
+ bl reset
+ mov r1, #SWP_MAGIC
+ mov r2, #0
+ cmp r1, r2
+ swpeq r0, r1, [r0]
+ bl expect_fail
+
+ /* Don't care about the return of the second one, just print */
+1:
+ movw r0, :lower16:.L.eq
+ movt r0, :upper16:.L.eq
+ ldr r1, =(.L.eqEnd - .L.eq - 1)
+ bl print_result
+ add r6, r6, #1 /* Next test */
+
+ /* cs */
+ bl reset
+ mov r1, #SWP_MAGIC
+ movw r3, #0xffff
+ movt r3, #0xffff
+ /* Overflow */
+ adds r2, r3, r3
+ swpcs r0, r1, [r0]
+ bl expect_success
+
+ /* Returned 0 (bad) or 1 (ok) */
+ cmp r0, #0
+ beq 2f
+
+ /* !cs */
+ bl reset
+ mov r1, #SWP_MAGIC
+ mov r3, #0x00
+ adds r2, r3, #0x08
+ swpcs r0, r1, [r0]
+ bl expect_fail
+
+ /* Don't care about the return of the second one, just print */
+2:
+ movw r0, :lower16:.L.cs
+ movt r0, :upper16:.L.cs
+ ldr r1, =(.L.csEnd - .L.cs - 1)
+ bl print_result
+ add r6, r6, #1 /* Next test */
+
+ /* mi */
+ bl reset
+ mov r1, #SWP_MAGIC
+ mov r2, #0
+ /* Underflow */
+ subs r2, r2, #0x05
+ swpmi r0, r1, [r0]
+ bl expect_success
+
+ /* Returned 0 (bad) or 1 (ok) */
+ cmp r0, #0
+ beq 3f
+
+ /* !mi */
+ bl reset
+ mov r1, #SWP_MAGIC
+ mov r2, #0x10
+ subs r2, r2, #0x08
+ swpmi r0, r1, [r0]
+ bl expect_fail
+
+ /* Don't care about the return of the second one, just print */
+3:
+ movw r0, :lower16:.L.mi
+ movt r0, :upper16:.L.mi
+ ldr r1, =(.L.miEnd - .L.mi - 1)
+ bl print_result
+ add r6, r6, #1 /* Next test */
+
+ /* vs */
+ bl reset
+ mov r1, #SWP_MAGIC
+ movw r3, #0xffff
+ movt r3, #0x7fff
+ /* Overflow */
+ adds r2, r3, #0x10
+ swpvs r0, r1, [r0]
+ bl expect_success
+
+ /* Returned 0 (bad) or 1 (ok) */
+ cmp r0, #0
+ beq 4f
+
+ /* !vs */
+ bl reset
+ mov r1, #SWP_MAGIC
+ mov r3, #0x00
+ adds r2, r3, #0x08
+ swpvs r0, r1, [r0]
+ bl expect_fail
+
+ /* Don't care about the return of the second one, just print */
+4:
+ movw r0, :lower16:.L.vs
+ movt r0, :upper16:.L.vs
+ ldr r1, =(.L.vsEnd - .L.vs - 1)
+ bl print_result
+ add r6, r6, #1 /* Next test */
+
+ /* hi */
+ bl reset
+ mov r1, #SWP_MAGIC
+ mov r2, #0x00
+ mov r3, #0x01
+ cmp r3, r2
+ swphi r0, r1, [r0]
+ bl expect_success
+
+ /* Returned 0 (bad) or 1 (ok) */
+ cmp r0, #0
+ beq 5f
+
+ /* !hi */
+ bl reset
+ mov r1, #SWP_MAGIC
+ mov r2, #0x00
+ mov r3, #0x01
+ cmp r2, r3
+ swphi r0, r1, [r0]
+ bl expect_fail
+
+ /* Don't care about the return of the second one, just print */
+5:
+ movw r0, :lower16:.L.hi
+ movt r0, :upper16:.L.hi
+ ldr r1, =(.L.hiEnd - .L.hi - 1)
+ bl print_result
+ add r6, r6, #1 /* Next test */
+
+ /* ge */
+ bl reset
+ mov r1, #SWP_MAGIC
+ mov r2, #0x01
+ cmp r2, r2
+ swpge r0, r1, [r0]
+ bl expect_success
+
+ /* Returned 0 (bad) or 1 (ok) */
+ cmp r0, #0
+ beq 6f
+
+ /* !ge */
+ bl reset
+ mov r1, #SWP_MAGIC
+ mov r2, #0x00
+ mov r3, #0x01
+ cmp r2, r3
+ swpge r0, r1, [r0]
+ bl expect_fail
+
+ /* Don't care about the return of the second one, just print */
+6:
+ movw r0, :lower16:.L.ge
+ movt r0, :upper16:.L.ge
+ ldr r1, =(.L.geEnd - .L.ge - 1)
+ bl print_result
+ add r6, r6, #1 /* Next test */
+
+ /* gt */
+ bl reset
+ mov r1, #SWP_MAGIC
+ mov r2, #0x00
+ mov r3, #0x01
+ cmp r3, r2
+ swpgt r0, r1, [r0]
+ bl expect_success
+
+ /* Returned 0 (bad) or 1 (ok) */
+ cmp r0, #0
+ beq 7f
+
+ /* !ge */
+ bl reset
+ mov r1, #SWP_MAGIC
+ mov r2, #0x00
+ mov r3, #0x01
+ cmp r2, r3
+ swpgt r0, r1, [r0]
+ bl expect_fail
+
+ /* Don't care about the return of the second one, just print */
+7:
+ movw r0, :lower16:.L.gt
+ movt r0, :upper16:.L.gt
+ ldr r1, =(.L.gtEnd - .L.gt - 1)
+ bl print_result
+ add r6, r6, #1 /* Next test */
+
+ mov r0, r4 /* retval */
+ ldr r7, =SYS_exit
+ swi 0
+
+ .p2align 2
+ .type print_result,%function
+ .code 32
+print_result:
+ push {r4, r5, lr}
+ /* Save the label, size for our result */
+ mov r4, r0
+ mov r5, r1
+
+ movw r0, :lower16:.L.ok
+ movt r0, :upper16:.L.ok
+ ldr r1, =(.L.okEnd - .L.ok - 1)
+ bl print
+ mov r0, r6
+ add r0, #0x30 /* "0" + test number */
+ mov r1, #0x01
+ str r0, [sp]
+ mov r0, sp
+ bl print
+ movw r0, :lower16:.L.swp
+ movt r0, :upper16:.L.swp
+ ldr r1, =(.L.swpEnd - .L.swp - 1)
+ bl print
+ mov r0, r4
+ mov r1, r5
+ bl print
+ movw r0, :lower16:.L.term
+ movt r0, :upper16:.L.term
+ ldr r1, =(.L.termEnd - .L.term - 1)
+ bl print
+
+ pop {r4, r5, lr}
+ bx lr
+
+ .p2align 2
+ .type reset,%function
+ .code 32
+reset:
+ /* Reset sp[0] and return the address used */
+ mov r0, #0x03
+ str r0, [sp]
+ mov r0, sp
+ bx lr
+
+ .p2align 2
+ .type expect_fail,%function
+ .code 32
+expect_fail:
+ /* Just check the stack value */
+ ldr r0, [sp]
+ mov r1, #0x03
+ cmp r0, r1
+ bne 1f
+
+ /* Success (not swapped) */
+ mov r0, #1
+ bx lr
+
+1:
+ /* Fail (swapped) */
+ /* Print the "not" part */
+ movw r0, :lower16:.L.not
+ movt r0, :upper16:.L.not
+ ldr r1, =(.L.notEnd - .L.not - 1)
+ push {lr}
+ bl print
+ pop {lr}
+
+ /* Failed */
+ add r4, r4, #1
+ mov r0, #0
+ bx lr
+
+ .p2align 2
+ .type expect_success,%function
+ .code 32
+expect_success:
+ /* Old value should be 3 */
+ cmp r0, #0x03
+ beq 1f
+ b 3f
+
+1:
+ /* Check stack value */
+ ldr r0, [sp]
+ mov r1, #SWP_MAGIC
+ cmp r0, r1
+ beq 2f
+ b 3f
+
+2:
+ mov r0, #1
+ bx lr
+
+3:
+ /* Print the "not" part */
+ movw r0, :lower16:.L.not
+ movt r0, :upper16:.L.not
+ ldr r1, =(.L.notEnd - .L.not - 1)
+ push {lr}
+ bl print
+ pop {lr}
+
+ /* Failed */
+ add r4, r4, #1
+ mov r0, #0
+ bx lr
+
+ .p2align 2
+ .type print,%function
+ .code 32
+print:
+ /* r0 - string, r1 = size */
+ mov r2, r1
+ mov r1, r0
+ ldr r0, =STDOUT_FILENO
+ ldr r7, =SYS_write
+ swi 0
+
+ bx lr
+
+.L.testheader:
+ .asciz "1..7\n"
+.L.testheaderEnd:
+ .size .L.testheader, .L.testheaderEnd - .L.testheader
+
+.L.not:
+ .asciz "not "
+.L.notEnd:
+ .size .L.not, .L.notEnd - .L.not
+.L.ok:
+ .asciz "ok "
+.L.okEnd:
+ .size .L.ok, .L.okEnd - .L.ok
+.L.swp:
+ .asciz " - swp"
+.L.swpEnd:
+ .size .L.swp, .L.swpEnd - .L.swp
+.L.eq:
+ .asciz "eq"
+.L.eqEnd:
+ .size .L.eq, .L.eqEnd - .L.eq
+.L.cs:
+ .asciz "cs"
+.L.csEnd:
+ .size .L.cs, .L.csEnd - .L.cs
+.L.mi:
+ .asciz "mi"
+.L.miEnd:
+ .size .L.mi, .L.miEnd - .L.mi
+.L.vs:
+ .asciz "vs"
+.L.vsEnd:
+ .size .L.vs, .L.vsEnd - .L.vs
+.L.hi:
+ .asciz "hi"
+.L.hiEnd:
+ .size .L.hi, .L.hiEnd - .L.hi
+.L.ge:
+ .asciz "ge"
+.L.geEnd:
+ .size .L.ge, .L.geEnd - .L.ge
+.L.gt:
+ .asciz "gt"
+.L.gtEnd:
+ .size .L.gt, .L.gtEnd - .L.gt
+.L.term:
+ .asciz "\n"
+.L.termEnd:
+ .size .L.term, .L.termEnd - .L.term
diff --git a/tests/sys/compat32/aarch64/swp_test.sh b/tests/sys/compat32/aarch64/swp_test.sh
new file mode 100644
index 000000000000..8edfa43b3e7e
--- /dev/null
+++ b/tests/sys/compat32/aarch64/swp_test.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+scriptdir=$(dirname $(realpath "$0"))
+
+. ${scriptdir}/common.sh
+
+# Ensure emul_swp is enabled just for this test; we'll turn it back off if
+# it wasn't enabled before the test.
+emul_swpval=$(sysctl -n compat.arm.emul_swp)
+sysctl compat.arm.emul_swp=1 >/dev/null
+${scriptdir}/swp_test_impl
+if [ "$emul_swpval" -ne 1 ]; then
+ sysctl compat.arm.emul_swp="$emul_swpval" >/dev/null
+fi
diff --git a/tests/sys/compat32/aarch64/swp_test_impl.S b/tests/sys/compat32/aarch64/swp_test_impl.S
new file mode 100644
index 000000000000..0e8047f1a6cf
--- /dev/null
+++ b/tests/sys/compat32/aarch64/swp_test_impl.S
@@ -0,0 +1,216 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2021 Warner Losh
+ * Copyright (c) 2023 Stormshield
+ * Copyright (c) 2023 Klara, Inc.
+ */
+
+#include <sys/syscall.h>
+
+#define STDOUT_FILENO 1
+#define SWP_MAGIC 0xffc0
+#define SWPB_MAGIC 0xc0c0
+
+ .text
+ .file "swp_test.S"
+ .syntax unified
+ .globl main
+ .p2align 2
+ .type main,%function
+ .code 32
+
+main:
+ sub sp, #0x04
+ /* r4 is our failed test counter */
+ mov r4, #0
+
+ movw r0, :lower16:.L.testheader
+ movt r0, :upper16:.L.testheader
+ ldr r1, =(.L.testheaderEnd - .L.testheader - 1)
+ bl print
+
+ /* Target address */
+ mov r0, #0x03
+ str r0, [sp]
+ mov r0, sp
+
+ /* Load value */
+ mov r1, #SWP_MAGIC
+
+ /* swp it */
+ swp r0, r1, [r0]
+
+ /* Old value should be 3 */
+ cmp r0, #0x03
+ bne 1f
+
+ /* Check stack value */
+ ldr r0, [sp]
+ mov r1, #SWP_MAGIC
+ cmp r0, r1
+ bne 1f
+ b 2f
+
+1:
+ /* Denote the failed test */
+ add r4, #1
+ /* "No" part of the notification */
+ movw r0, :lower16:.L.boknot
+ movt r0, :upper16:.L.boknot
+ ldr r1, =(.L.boknotEnd - .L.boknot - 1)
+ bl print
+
+2:
+ /* Notify */
+ movw r0, :lower16:.L.ok1
+ movt r0, :upper16:.L.ok1
+ ldr r1, =(.L.ok1End - .L.ok1 - 1)
+ bl print
+
+ movw r5, #SWPB_MAGIC
+ movt r5, #SWPB_MAGIC
+
+ /* Using r6 as our accumulator */
+ mov r6, sp
+ /* Simplify the loop */
+ sub r6, #1
+3:
+ /* Restore our magic value every time */
+ str r5, [sp]
+ /* Move on to the next byte */
+ add r6, #1
+
+ /* swp it in */
+ mov r0, r6
+ mov r1, #3
+ swpb r0, r1, [r0]
+
+ /* Check the old value */
+ cmp r0, #0xc0
+ bne 6f
+
+ /* Check the stack value */
+ ldrb r0, [r6]
+ cmp r0, #0x03
+ bne 6f
+
+ /* Just loop over the rest of the word and check those values. */
+ mov r1, r6
+ sub r1, sp
+
+ mov r0, #0x00
+4:
+ cmp r0, r1
+ beq 5f
+
+ /* Check the next byte */
+ ldrb r3, [sp, r0]
+ cmp r3, #0xc0
+ bne 6f
+
+5:
+ add r0, #0x01
+ cmp r0, #0x04
+ /* Hit the end, this one succeeded */
+ beq 7f
+
+ /* Move on to the next byte */
+ b 4b
+
+6:
+ /* Denote the failed test */
+ add r4, #1
+ /* "No" part of the notification */
+ movw r0, :lower16:.L.boknot
+ movt r0, :upper16:.L.boknot
+ ldr r1, =(.L.boknotEnd - .L.boknot - 1)
+ bl print
+
+ /* FALLTHROUGH */
+7:
+ /* "ok" part */
+ movw r0, :lower16:.L.bok
+ movt r0, :upper16:.L.bok
+ ldr r1, =(.L.bokEnd - .L.bok - 1)
+ bl print
+
+ /* test number */
+ mov r0, r6
+ sub r0, sp
+ add r0, #0x32 /* "0" + 2 because we start at test 2. */
+ mov r1, #0x01
+ str r0, [sp]
+ mov r0, sp
+ bl print
+
+ /* boklabel */
+ movw r0, :lower16:.L.boklabel
+ movt r0, :upper16:.L.boklabel
+ ldr r1, =(.L.boklabelEnd - .L.boklabel - 1)
+ bl print
+
+ /* index */
+ mov r0, r6
+ sub r0, sp
+ add r0, #0x30 /* "0" */
+ str r0, [sp]
+ mov r0, sp
+ mov r1, #0x01
+ bl print
+
+ /* bokterm */
+ movw r0, :lower16:.L.bokterm
+ movt r0, :upper16:.L.bokterm
+ ldr r1, =(.L.boktermEnd - .L.bokterm - 1)
+ bl print
+
+ mov r0, sp
+ add r0, #0x3
+ cmp r0, r6
+ bne 3b
+
+ mov r0, r4 /* retval */
+ ldr r7, =SYS_exit
+ swi 0
+
+ .p2align 2
+ .type print,%function
+ .code 32
+print:
+ /* r0 - string, r1 = size */
+ mov r2, r1
+ mov r1, r0
+ ldr r0, =STDOUT_FILENO
+ ldr r7, =SYS_write
+ swi 0
+
+ bx lr
+
+.L.testheader:
+ .asciz "1..5\n"
+.L.testheaderEnd:
+ .size .L.testheader, .L.testheaderEnd - .L.testheader
+
+ /* Maybe not the most efficient, but meh. */
+.L.ok1:
+ .asciz "ok 1 - swp\n"
+.L.ok1End:
+ .size .L.ok1, .L.ok1End - .L.ok1
+
+.L.boknot:
+ .asciz "not "
+.L.boknotEnd:
+ .size .L.boknot, .L.boknotEnd - .L.boknot
+.L.bok:
+ .asciz "ok "
+.L.bokEnd:
+ .size .L.bok, .L.bokEnd - .L.bok
+.L.boklabel:
+ .asciz " - swpb["
+.L.boklabelEnd:
+ .size .L.boklabel, .L.boklabelEnd - .L.boklabel
+.L.bokterm:
+ .asciz "]\n"
+.L.boktermEnd:
+ .size .L.bokterm, .L.boktermEnd - .L.bokterm
diff --git a/tests/sys/devrandom/Makefile b/tests/sys/devrandom/Makefile
new file mode 100644
index 000000000000..342925246aee
--- /dev/null
+++ b/tests/sys/devrandom/Makefile
@@ -0,0 +1,23 @@
+.include <src.opts.mk>
+
+SDEVRANDOM= ${SRCTOP}/sys/dev/random
+.PATH: ${SDEVRANDOM}
+
+TESTSDIR= ${TESTSBASE}/sys/devrandom
+
+CFLAGS+= -I${SRCTOP}/sys
+
+ATF_TESTS_C+= uint128_test
+
+# Test Chacha CTR behavior <-> uint128
+LDADD.uint128_test+= ${SDEVRANDOM}/hash.c
+LDFLAGS.uint128_test+= -Wno-unused-parameter
+
+# hash.c deps:
+LIBADD.uint128_test+= md # SHA256
+LDADD.uint128_test+= ${SRCTOP}/sys/crypto/rijndael/rijndael-alg-fst.c
+LDADD.uint128_test+= ${SRCTOP}/sys/crypto/rijndael/rijndael-api-fst.c
+LDFLAGS.uint128_test+= -Wno-cast-align
+
+
+.include <bsd.test.mk>
diff --git a/tests/sys/devrandom/uint128_test.c b/tests/sys/devrandom/uint128_test.c
new file mode 100644
index 000000000000..a59adba1a7c7
--- /dev/null
+++ b/tests/sys/devrandom/uint128_test.c
@@ -0,0 +1,278 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Conrad Meyer <cem@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/random.h>
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdbool.h>
+
+#include <crypto/chacha20/chacha.h>
+#include <crypto/rijndael/rijndael-api-fst.h>
+#include <crypto/sha2/sha256.h>
+
+#include <dev/random/hash.h>
+#include <dev/random/uint128.h>
+
+#include <atf-c.h>
+
+static void
+vec_u32_tole128(uint8_t dst[static 16], const uint32_t src[static 4])
+{
+ le32enc(dst, src[0]);
+ le32enc(&dst[4], src[1]);
+ le32enc(&dst[8], src[2]);
+ le32enc(&dst[12], src[3]);
+}
+
+static void
+le128_to_vec_u32(uint32_t dst[static 4], const uint8_t src[static 16])
+{
+ dst[0] = le32dec(src);
+ dst[1] = le32dec(&src[4]);
+ dst[2] = le32dec(&src[8]);
+ dst[3] = le32dec(&src[12]);
+}
+
+static void
+formatu128(char buf[static 52], uint128_t x)
+{
+ uint8_t le128x[16];
+ uint32_t vx[4];
+ size_t sz, i;
+ int rc;
+
+ le128enc(le128x, x);
+ le128_to_vec_u32(vx, le128x);
+
+ sz = 52;
+ for (i = 0; i < 4; i++) {
+ rc = snprintf(buf, sz, "0x%x ", vx[i]);
+ ATF_REQUIRE(rc > 0 && (size_t)rc < sz);
+
+ buf += rc;
+ sz -= rc;
+ }
+ /* Delete last trailing space */
+ buf[-1] = '\0';
+}
+
+static void
+u128_check_equality(uint128_t a, uint128_t b, const char *descr)
+{
+ char fmtbufa[52], fmtbufb[52];
+
+ formatu128(fmtbufa, a);
+ formatu128(fmtbufb, b);
+
+ ATF_CHECK_MSG(uint128_equals(a, b),
+ "Expected: [%s] != Actual: [%s]: %s", fmtbufa, fmtbufb, descr);
+}
+
+ATF_TC_WITHOUT_HEAD(uint128_inc);
+ATF_TC_BODY(uint128_inc, tc)
+{
+ static const struct u128_inc_tc {
+ uint32_t input[4];
+ uint32_t expected[4];
+ const char *descr;
+ } tests[] = {
+ {
+ .input = { 0, 0, 0, 0 },
+ .expected = { 1, 0, 0, 0 },
+ .descr = "0 -> 1",
+ },
+ {
+ .input = { 1, 0, 0, 0 },
+ .expected = { 2, 0, 0, 0 },
+ .descr = "0 -> 2",
+ },
+ {
+ .input = { 0xff, 0, 0, 0 },
+ .expected = { 0x100, 0, 0, 0 },
+ .descr = "0xff -> 0x100 (byte carry)",
+ },
+ {
+ .input = { UINT32_MAX, 0, 0, 0 },
+ .expected = { 0, 1, 0, 0 },
+ .descr = "2^32 - 1 -> 2^32 (word carry)",
+ },
+ {
+ .input = { UINT32_MAX, UINT32_MAX, 0, 0 },
+ .expected = { 0, 0, 1, 0 },
+ .descr = "2^64 - 1 -> 2^64 (u128t_word0 carry)",
+ },
+ {
+ .input = { UINT32_MAX, UINT32_MAX, UINT32_MAX, 0 },
+ .expected = { 0, 0, 0, 1 },
+ .descr = "2^96 - 1 -> 2^96 (word carry)",
+ },
+ };
+ uint8_t inputle[16], expectedle[16];
+ uint128_t a;
+ size_t i;
+
+ for (i = 0; i < nitems(tests); i++) {
+ vec_u32_tole128(inputle, tests[i].input);
+ vec_u32_tole128(expectedle, tests[i].expected);
+
+ a = le128dec(inputle);
+ uint128_increment(&a);
+ u128_check_equality(le128dec(expectedle), a, tests[i].descr);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(uint128_add64);
+ATF_TC_BODY(uint128_add64, tc)
+{
+ static const struct u128_add64_tc {
+ uint32_t input[4];
+ uint64_t addend;
+ uint32_t expected[4];
+ const char *descr;
+ } tests[] = {
+ {
+ .input = { 0, 0, 0, 0 },
+ .addend = 1,
+ .expected = { 1, 0, 0, 0 },
+ .descr = "0 + 1 -> 1",
+ },
+ {
+ .input = { 1, 0, 0, 0 },
+ .addend = UINT32_MAX,
+ .expected = { 0, 1, 0, 0 },
+ .descr = "1 + (2^32 - 1) -> 2^32 (word carry)",
+ },
+ {
+ .input = { 1, 0, 0, 0 },
+ .addend = UINT64_MAX,
+ .expected = { 0, 0, 1, 0 },
+ .descr = "1 + (2^64 - 1) -> 2^64 (u128t_word0 carry)",
+ },
+ {
+ .input = { 0x11111111, 0x11111111, 0, 0 },
+ .addend = 0xf0123456789abcdeULL,
+ .expected = { 0x89abcdef, 0x01234567, 1, 0 },
+ .descr = "0x1111_1111_1111_1111 +"
+ "0xf012_3456_789a_bcde ->"
+ "0x1_0123_4567_89ab_cdef",
+ },
+ {
+ .input = { 1, 0, UINT32_MAX, 0 },
+ .addend = UINT64_MAX,
+ .expected = { 0, 0, 0, 1 },
+ .descr = "Carry ~2^96",
+ },
+ };
+ uint8_t inputle[16], expectedle[16];
+ uint128_t a;
+ size_t i;
+
+ for (i = 0; i < nitems(tests); i++) {
+ vec_u32_tole128(inputle, tests[i].input);
+ vec_u32_tole128(expectedle, tests[i].expected);
+
+ a = le128dec(inputle);
+ uint128_add64(&a, tests[i].addend);
+ u128_check_equality(le128dec(expectedle), a, tests[i].descr);
+ }
+}
+
+/*
+ * Test assumptions about Chacha incrementing counter in the same way as
+ * uint128.h
+ */
+ATF_TC_WITHOUT_HEAD(uint128_chacha_ctr);
+ATF_TC_BODY(uint128_chacha_ctr, tc)
+{
+ static const struct u128_chacha_tc {
+ uint32_t input[4];
+ uint32_t expected[4];
+ const char *descr;
+ } tests[] = {
+ {
+ .input = { 0, 0, 0, 0 },
+ .expected = { 1, 0, 0, 0 },
+ .descr = "Single block",
+ },
+ {
+ .input = { 1, 0, 0, 0 },
+ .expected = { 2, 0, 0, 0 },
+ .descr = "0 -> 2",
+ },
+ {
+ .input = { 0xff, 0, 0, 0 },
+ .expected = { 0x100, 0, 0, 0 },
+ .descr = "0xff -> 0x100 (byte carry)",
+ },
+ {
+ .input = { UINT32_MAX, 0, 0, 0 },
+ .expected = { 0, 1, 0, 0 },
+ .descr = "2^32 - 1 -> 2^32 (word carry)",
+ },
+ {
+ .input = { UINT32_MAX, UINT32_MAX, 0, 0 },
+ .expected = { 0, 0, 1, 0 },
+ .descr = "2^64 - 1 -> 2^64 (u128t_word0 carry)",
+ },
+ {
+ .input = { UINT32_MAX, UINT32_MAX, UINT32_MAX, 0 },
+ .expected = { 0, 0, 0, 1 },
+ .descr = "2^96 - 1 -> 2^96 (word carry)",
+ },
+ };
+ union randomdev_key context;
+ uint8_t inputle[16], expectedle[16], trash[CHACHA_BLOCKLEN];
+ uint8_t notrandomkey[RANDOM_KEYSIZE] = { 0 };
+ uint128_t a;
+ size_t i;
+
+ random_chachamode = true;
+ randomdev_encrypt_init(&context, notrandomkey);
+
+ for (i = 0; i < nitems(tests); i++) {
+ vec_u32_tole128(inputle, tests[i].input);
+ vec_u32_tole128(expectedle, tests[i].expected);
+
+ a = le128dec(inputle);
+ randomdev_keystream(&context, &a, trash, sizeof(trash));
+ u128_check_equality(le128dec(expectedle), a, tests[i].descr);
+ }
+
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, uint128_inc);
+ ATF_TP_ADD_TC(tp, uint128_add64);
+ ATF_TP_ADD_TC(tp, uint128_chacha_ctr);
+ return (atf_no_error());
+}
diff --git a/tests/sys/fifo/Makefile b/tests/sys/fifo/Makefile
new file mode 100644
index 000000000000..6d4c859288d9
--- /dev/null
+++ b/tests/sys/fifo/Makefile
@@ -0,0 +1,12 @@
+TESTSDIR= ${TESTSBASE}/sys/fifo
+
+PLAIN_TESTS_C+= fifo_create
+PLAIN_TESTS_C+= fifo_io
+ATF_TESTS_C+= fifo_kqueue
+PLAIN_TESTS_C+= fifo_misc
+PLAIN_TESTS_C+= fifo_open
+
+TEST_METADATA.fifo_create+= required_user="root"
+TEST_METADATA.fifo_open+= required_user="root"
+
+.include <bsd.test.mk>
diff --git a/tests/sys/fifo/Makefile.depend b/tests/sys/fifo/Makefile.depend
new file mode 100644
index 000000000000..84b8ddd67e34
--- /dev/null
+++ b/tests/sys/fifo/Makefile.depend
@@ -0,0 +1,16 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/fifo/fifo_create.c b/tests/sys/fifo/fifo_create.c
new file mode 100644
index 000000000000..1c41e7e810c5
--- /dev/null
+++ b/tests/sys/fifo/fifo_create.c
@@ -0,0 +1,280 @@
+/*-
+ * Copyright (c) 2005-2008 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * Simple regression test for the creation and destruction of POSIX fifos in
+ * the file system name space. Using a specially created directory, create
+ * a fifo in it and check that the following properties are present, as
+ * specified in IEEE Std 1003.1, 2004 Edition:
+ *
+ * - When mkfifo() or mknod(S_IFIFO) is called, on success, a fifo is
+ * created.
+ *
+ * - On an error, no fifo is created. (XXX: Not tested)
+ *
+ * - The mode bits on the fifo are a product of combining the umask and
+ * requested mode.
+ *
+ * - The fifo's owner will be the processes effective user ID. (XXX: Not
+ * tested)
+ *
+ * - The fifo's group will be the parent directory's group or the effective
+ * group ID of the process. For historical reasons, BSD prefers the group
+ * ID of the process, so we will generate an error if it's not that. (XXX:
+ * Not tested)
+ *
+ * - The st_atime, st_ctime, st_mtime of the fifo will be set appropriately,
+ * and st_ctime and st_mtime on the directory will be updated. (XXX: We
+ * test they are updated, not correct)
+ *
+ * - EEXIST is returned if the named file already exists.
+ *
+ * In addition, we check that we can unlink the fifo, and that if we do, it
+ * disappears.
+ *
+ * This test must run as root in order to usefully frob the process
+ * credential to test permission parts.
+ */
+
+/*
+ * All activity occurs within a temporary directory created early in the
+ * test.
+ */
+static char temp_dir[PATH_MAX];
+
+static void __unused
+atexit_temp_dir(void)
+{
+
+ rmdir(temp_dir);
+}
+
+/*
+ * Basic creation tests: verify that mkfifo(2) (or mknod(2)) creates a fifo,
+ * that the time stamps on the directory are updated, that if we try twice we
+ * get EEXIST, and that we can unlink it.
+ */
+static void
+fifo_create_test(int use_mkfifo)
+{
+ struct stat old_dirsb, dirsb, fifosb;
+ const char *testname;
+ char path[] = "testfifo";
+ int error;
+
+ if (use_mkfifo)
+ testname = "mkfifo";
+ else
+ testname = "mknod";
+
+ /*
+ * Sleep to make sure that the time stamp on the directory will be
+ * updated.
+ */
+ if (stat(".", &old_dirsb) < 0)
+ err(-1, "basic_create_test: %s: stat: %s", testname,
+ temp_dir);
+
+ sleep(2);
+
+ if (use_mkfifo) {
+ if (mkfifo(path, 0600) < 0)
+ err(-1, "basic_create_test: %s: %s", testname, path);
+ } else {
+ if (mknod(path, S_IFIFO | 0600, 0) < 0)
+ err(-1, "basic_create_test: %s: %s", testname, path);
+ }
+
+ if (stat(path, &fifosb) < 0) {
+ error = errno;
+ (void)unlink(path);
+ errno = error;
+ err(-1, "basic_create_test: %s: stat: %s", testname, path);
+ }
+
+ if (!(S_ISFIFO(fifosb.st_mode))) {
+ (void)unlink(path);
+ errx(-1, "basic_create_test: %s produced non-fifo",
+ testname);
+ }
+
+ if (use_mkfifo) {
+ if (mkfifo(path, 0600) == 0)
+ errx(-1, "basic_create_test: dup %s succeeded",
+ testname);
+ } else {
+ if (mknod(path, S_IFIFO | 0600, 0) == 0)
+ errx(-1, "basic_create_test: dup %s succeeded",
+ testname);
+ }
+
+ if (errno != EEXIST)
+ err(-1, "basic_create_test: dup %s unexpected error",
+ testname);
+
+ if (stat(".", &dirsb) < 0) {
+ error = errno;
+ (void)unlink(path);
+ errno = error;
+ err(-1, "basic_create_test: %s: stat: %s", testname,
+ temp_dir);
+ }
+
+ if (old_dirsb.st_ctime == dirsb.st_ctime) {
+ (void)unlink(path);
+ errx(-1, "basic_create_test: %s: old_dirsb.st_ctime == "
+ "dirsb.st_ctime", testname);
+ }
+
+ if (old_dirsb.st_mtime == dirsb.st_mtime) {
+ (void)unlink(path);
+ errx(-1, "basic_create_test: %s: old_dirsb.st_mtime == "
+ "dirsb.st_mtime", testname);
+ }
+
+ if (unlink(path) < 0)
+ err(-1, "basic_create_test: %s: unlink: %s", testname, path);
+
+ if (stat(path, &fifosb) == 0)
+ errx(-1, "basic_create_test: %s: unlink failed to unlink",
+ testname);
+ if (errno != ENOENT)
+ err(-1, "basic_create_test: %s: unlink unexpected error",
+ testname);
+}
+
+/*
+ * Having determined that basic create/remove/etc functionality is present
+ * for fifos, now make sure that the umask, requested permissions, and
+ * resulting mode are handled properly.
+ */
+static const struct permission_test {
+ mode_t pt_umask;
+ mode_t pt_reqmode;
+ mode_t pt_mode;
+} permission_test[] = {
+ {0000, 0, S_IFIFO},
+ {0000, S_IRWXU, S_IFIFO | S_IRWXU},
+ {0000, S_IRWXU | S_IRWXG | S_IRWXO, S_IFIFO | S_IRWXU | S_IRWXG |
+ S_IRWXO },
+ {0077, S_IRWXU, S_IFIFO | S_IRWXU},
+ {0077, S_IRWXU | S_IRWXG | S_IRWXO, S_IFIFO | S_IRWXU},
+};
+static const int permission_test_count = sizeof(permission_test) /
+ sizeof(struct permission_test);
+
+static void
+fifo_permission_test(int use_mkfifo)
+{
+ const struct permission_test *ptp;
+ mode_t __unused old_umask;
+ char path[] = "testfifo";
+ const char *testname;
+ struct stat sb;
+ int error, i;
+
+ if (use_mkfifo)
+ testname = "mkfifo";
+ else
+ testname = "mknod";
+
+ old_umask = umask(0022);
+ for (i = 0; i < permission_test_count; i++) {
+ ptp = &permission_test[i];
+
+ umask(ptp->pt_umask);
+ if (use_mkfifo) {
+ if (mkfifo(path, ptp->pt_reqmode) < 0)
+ err(-1, "fifo_permission_test: %s: %08o "
+ "%08o %08o\n", testname, ptp->pt_umask,
+ ptp->pt_reqmode, ptp->pt_mode);
+ } else {
+ if (mknod(path, S_IFIFO | ptp->pt_reqmode, 0) < 0)
+ err(-1, "fifo_permission_test: %s: %08o "
+ "%08o %08o\n", testname, ptp->pt_umask,
+ ptp->pt_reqmode, ptp->pt_mode);
+ }
+
+ if (stat(path, &sb) < 0) {
+ error = errno;
+ (void)unlink(path);
+ errno = error;
+ err(-1, "fifo_permission_test: %s: %s", testname,
+ path);
+ }
+
+ if (sb.st_mode != ptp->pt_mode) {
+ (void)unlink(path);
+ errx(-1, "fifo_permission_test: %s: %08o %08o %08o "
+ "got %08o", testname, ptp->pt_umask,
+ ptp->pt_reqmode, ptp->pt_mode, sb.st_mode);
+ }
+
+ if (unlink(path) < 0)
+ err(-1, "fifo_permission_test: %s: unlink: %s",
+ testname, path);
+ }
+ umask(old_umask);
+}
+
+int
+main(void)
+{
+ int i;
+
+ if (geteuid() != 0)
+ errx(-1, "must be run as root");
+
+ strcpy(temp_dir, "fifo_create.XXXXXXXXXXX");
+ if (mkdtemp(temp_dir) == NULL)
+ err(-1, "mkdtemp");
+ atexit(atexit_temp_dir);
+
+ if (chdir(temp_dir) < 0)
+ err(-1, "chdir");
+
+ /*
+ * Run each test twice, once with mknod(2) and a second time with
+ * mkfifo(2). Historically, BSD has not allowed mknod(2) to be used
+ * to create fifos, but the Single UNIX Specification requires it.
+ */
+ for (i = 0; i < 2; i++) {
+ fifo_create_test(i);
+ fifo_permission_test(i);
+ }
+
+ return (0);
+}
diff --git a/tests/sys/fifo/fifo_io.c b/tests/sys/fifo/fifo_io.c
new file mode 100644
index 000000000000..6df5dcd136a2
--- /dev/null
+++ b/tests/sys/fifo/fifo_io.c
@@ -0,0 +1,1397 @@
+/*-
+ * Copyright (c) 2005 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/ioctl.h>
+#include <sys/select.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * Regression test to exercise POSIX fifo I/O.
+ *
+ * We test a number of aspect of behavior, including:
+ *
+ * - If there's no data to read, then for blocking fifos, we block, and for
+ * non-blocking, we return EAGAIN.
+ *
+ * - If we write ten bytes, ten bytes can be read, and they're the same
+ * bytes, in the same order.
+ *
+ * - If we write two batches of five bytes, we can read the same ten bytes in
+ * one read of ten bytes.
+ *
+ * - If we write ten bytes, we can read the same ten bytes in two reads of
+ * five bytes each.
+ *
+ * - If we over-fill a buffer (by writing 512k, which we take to be a large
+ * number above default buffer sizes), we block if there is no reader.
+ *
+ * - That once 512k (ish) is read from the other end, the blocked writer
+ * wakes up.
+ *
+ * - When a fifo is empty, poll, select, kqueue, and fionread report it is
+ * writable but not readable.
+ *
+ * - When a fifo has data in it, poll, select, and kqueue report that it is
+ * writable.
+ *
+ * - XXX: blocked reader semantics?
+ *
+ * - XXX: event behavior on remote close?
+ *
+ * Although behavior of O_RDWR isn't defined for fifos by POSIX, we expect
+ * "reasonable" behavior, and run some additional tests relating to event
+ * management on O_RDWR fifo descriptors.
+ */
+
+#define KQUEUE_MAX_EVENT 8
+
+/*
+ * All activity occurs within a temporary directory created early in the
+ * test.
+ */
+static char temp_dir[PATH_MAX];
+
+static void __unused
+atexit_temp_dir(void)
+{
+
+ rmdir(temp_dir);
+}
+
+static void
+makefifo(const char *fifoname, const char *testname)
+{
+
+ if (mkfifo(fifoname, 0700) < 0)
+ err(-1, "%s: makefifo: mkfifo: %s", testname, fifoname);
+}
+
+static void
+cleanfifo2(const char *fifoname, int fd1, int fd2)
+{
+
+ if (fd1 != -1)
+ close(fd1);
+ if (fd2 != -1)
+ close(fd2);
+ (void)unlink(fifoname);
+}
+
+static void
+cleanfifo3(const char *fifoname, int fd1, int fd2, int fd3)
+{
+
+ if (fd3 != -1)
+ close(fd3);
+ cleanfifo2(fifoname, fd1, fd2);
+}
+
+/*
+ * Open two different file descriptors for a fifo: one read, one write. Do
+ * so using non-blocking opens in order to avoid deadlocking the process.
+ */
+static int
+openfifo(const char *fifoname, int *reader_fdp, int *writer_fdp)
+{
+ int error, fd1, fd2;
+
+ fd1 = open(fifoname, O_RDONLY | O_NONBLOCK);
+ if (fd1 < 0)
+ return (-1);
+ fd2 = open(fifoname, O_WRONLY | O_NONBLOCK);
+ if (fd2 < 0) {
+ error = errno;
+ close(fd1);
+ errno = error;
+ return (-1);
+ }
+ *reader_fdp = fd1;
+ *writer_fdp = fd2;
+
+ return (0);
+}
+
+/*
+ * Open one file descriptor for the fifo, supporting both read and write.
+ */
+static int
+openfifo_rw(const char *fifoname, int *fdp)
+{
+ int fd;
+
+ fd = open(fifoname, O_RDWR);
+ if (fd < 0)
+ return (-1);
+ *fdp = fd;
+
+ return (0);
+}
+
+static int
+set_nonblocking(int fd, const char *testname)
+{
+ int flags;
+
+ flags = fcntl(fd, F_GETFL);
+ if (flags < 0) {
+ warn("%s: fcntl(fd, F_GETFL)", testname);
+ return(-1);
+ }
+
+ flags |= O_NONBLOCK;
+
+ if (fcntl(fd, F_SETFL, flags) < 0) {
+ warn("%s: fcntl(fd, 0x%x)", testname, flags);
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+set_blocking(int fd, const char *testname)
+{
+ int flags;
+
+ flags = fcntl(fd, F_GETFL);
+ if (flags < 0) {
+ warn("%s: fcntl(fd, F_GETFL)", testname);
+ return(-1);
+ }
+
+ flags &= ~O_NONBLOCK;
+
+ if (fcntl(fd, F_SETFL, flags) < 0) {
+ warn("%s: fcntl(fd, 0x%x)", testname, flags);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Drain a file descriptor (fifo) of any readable data. Note: resets the
+ * blocking state.
+ */
+static int
+drain_fd(int fd, const char *testname)
+{
+ ssize_t len;
+ u_char ch;
+
+ if (set_nonblocking(fd, testname) < 0)
+ return (-1);
+
+ while ((len = read(fd, &ch, sizeof(ch))) > 0);
+ if (len < 0) {
+ switch (errno) {
+ case EAGAIN:
+ return (0);
+ default:
+ warn("%s: drain_fd: read", testname);
+ return (-1);
+ }
+ }
+ warn("%s: drain_fd: read: returned 0 bytes", testname);
+ return (-1);
+}
+
+/*
+ * Simple I/O test: write ten integers, and make sure we get back the same
+ * integers in the same order. This assumes a minimum fifo buffer > 10
+ * bytes in order to not block and deadlock.
+ */
+static void
+test_simpleio(void)
+{
+ int i, reader_fd, writer_fd;
+ u_char buffer[10];
+ ssize_t len;
+
+ makefifo("testfifo", __func__);
+ if (openfifo("testfifo", &reader_fd, &writer_fd)
+ < 0) {
+ warn("test_simpleio: openfifo: testfifo");
+ cleanfifo2("testfifo", -1, -1);
+ exit(-1);
+ }
+
+ for (i = 0; i < 10; i++)
+ buffer[i] = i;
+
+ len = write(writer_fd, (char *)buffer, sizeof(buffer));
+ if (len < 0) {
+ warn("test_simpleio: write");
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+ if (len != sizeof(buffer)) {
+ warnx("test_simplio: tried %zu but wrote %zd", sizeof(buffer),
+ len);
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ len = read(reader_fd, (char *)buffer, sizeof(buffer));
+ if (len < 0) {
+ warn("test_simpleio: read");
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+ if (len != sizeof(buffer)) {
+ warnx("test_simpleio: tried %zu but read %zd", sizeof(buffer),
+ len);
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+ for (i = 0; i < 10; i++) {
+ if (buffer[i] == i)
+ continue;
+ warnx("test_simpleio: write byte %d as 0x%02x, but read "
+ "0x%02x", i, i, buffer[i]);
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+}
+
+static volatile int alarm_fired;
+/*
+ * Non-destructive SIGALRM handler.
+ */
+static void
+sigalarm(int signum __unused)
+{
+
+ alarm_fired = 1;
+}
+
+/*
+ * Wrapper function for write, which uses a timer to interrupt any blocking.
+ * Because we can't reliably detect EINTR for blocking I/O, we also track
+ * whether or not our timeout fired.
+ */
+static int __unused
+timed_write(int fd, void *data, size_t len, ssize_t *written_lenp,
+ int timeout, int *timedoutp, const char *testname)
+{
+ struct sigaction act, oact;
+ ssize_t written_len;
+ int error;
+
+ alarm_fired = 0;
+ bzero(&act, sizeof(oact));
+ act.sa_handler = sigalarm;
+ if (sigaction(SIGALRM, &act, &oact) < 0) {
+ warn("%s: timed_write: sigaction", testname);
+ return (-1);
+ }
+ alarm(timeout);
+ written_len = write(fd, data, len);
+ error = errno;
+ alarm(0);
+ if (sigaction(SIGALRM, &oact, NULL) < 0) {
+ warn("%s: timed_write: sigaction", testname);
+ return (-1);
+ }
+ if (alarm_fired)
+ *timedoutp = 1;
+ else
+ *timedoutp = 0;
+
+ errno = error;
+ if (written_len < 0)
+ return (-1);
+ *written_lenp = written_len;
+ return (0);
+}
+
+/*
+ * Wrapper function for read, which uses a timer to interrupt any blocking.
+ * Because we can't reliably detect EINTR for blocking I/O, we also track
+ * whether or not our timeout fired.
+ */
+static int
+timed_read(int fd, void *data, size_t len, ssize_t *read_lenp,
+ int timeout, int *timedoutp, const char *testname)
+{
+ struct sigaction act, oact;
+ ssize_t read_len;
+ int error;
+
+ alarm_fired = 0;
+ bzero(&act, sizeof(oact));
+ act.sa_handler = sigalarm;
+ if (sigaction(SIGALRM, &act, &oact) < 0) {
+ warn("%s: timed_write: sigaction", testname);
+ return (-1);
+ }
+ alarm(timeout);
+ read_len = read(fd, data, len);
+ error = errno;
+ alarm(0);
+ if (sigaction(SIGALRM, &oact, NULL) < 0) {
+ warn("%s: timed_write: sigaction", testname);
+ return (-1);
+ }
+ if (alarm_fired)
+ *timedoutp = 1;
+ else
+ *timedoutp = 0;
+
+ errno = error;
+ if (read_len < 0)
+ return (-1);
+ *read_lenp = read_len;
+ return (0);
+}
+
+/*
+ * This test operates on blocking and non-blocking fifo file descriptors, in
+ * order to determine whether they block at good moments or not. By good we
+ * mean: don't block for non-blocking sockets, and do block for blocking
+ * ones, assuming there isn't I/O buffer to satisfy the request.
+ *
+ * We use a timeout of 5 seconds, concluding that in 5 seconds either all I/O
+ * that can take place will, and that if we reach the end of the timeout,
+ * then blocking has occurred.
+ *
+ * We assume that the buffer size on a fifo is <512K, and as such, that
+ * writing that much data without an active reader will result in blocking.
+ */
+static void
+test_blocking_read_empty(void)
+{
+ int reader_fd, ret, timedout, writer_fd;
+ ssize_t len;
+ u_char ch;
+
+ makefifo("testfifo", __func__);
+ if (openfifo("testfifo", &reader_fd, &writer_fd)
+ < 0) {
+ warn("test_blocking_read_empty: openfifo: testfifo");
+ cleanfifo2("testfifo", -1, -1);
+ exit(-1);
+ }
+
+ /*
+ * Read one byte from an empty blocking fifo, block as there is no
+ * data.
+ */
+ if (set_blocking(reader_fd, __func__) < 0) {
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ ret = timed_read(reader_fd, &ch, sizeof(ch), &len, 5, &timedout,
+ __func__);
+ if (ret != -1) {
+ warnx("test_blocking_read_empty: timed_read: returned "
+ "success");
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+ if (errno != EINTR) {
+ warn("test_blocking_read_empty: timed_read");
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ /*
+ * Read one byte from an empty non-blocking fifo, return EAGAIN as
+ * there is no data.
+ */
+ if (set_nonblocking(reader_fd, __func__) < 0) {
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ ret = timed_read(reader_fd, &ch, sizeof(ch), &len, 5, &timedout,
+ __func__);
+ if (ret != -1) {
+ warnx("test_blocking_read_empty: timed_read: returned "
+ "success");
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+ if (errno != EAGAIN) {
+ warn("test_blocking_read_empty: timed_read");
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+}
+
+/*
+ * Write one byte to an empty fifo, then try to read one byte and make sure
+ * we don't block in either the write or the read. This tests both for
+ * improper blocking in the send and receive code.
+ */
+static void
+test_blocking_one_byte(void)
+{
+ int reader_fd, ret, timedout, writer_fd;
+ ssize_t len;
+ u_char ch;
+
+ makefifo("testfifo", __func__);
+ if (openfifo("testfifo", &reader_fd, &writer_fd) < 0) {
+ warn("test_blocking: openfifo: testfifo");
+ cleanfifo2("testfifo", -1, -1);
+ exit(-1);
+ }
+
+ if (set_blocking(writer_fd, __func__) < 0) {
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+ if (set_blocking(reader_fd, __func__) < 0) {
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ ch = 0xfe;
+ ret = timed_write(writer_fd, &ch, sizeof(ch), &len, 5, &timedout,
+ __func__);
+ if (ret < 0) {
+ warn("test_blocking_one_byte: timed_write");
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+ if (len != sizeof(ch)) {
+ warnx("test_blocking_one_byte: timed_write: tried to write "
+ "%zu, wrote %zd", sizeof(ch), len);
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ ch = 0xab;
+ ret = timed_read(reader_fd, &ch, sizeof(ch), &len, 5, &timedout,
+ __func__);
+ if (ret < 0) {
+ warn("test_blocking_one_byte: timed_read");
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+ if (len != sizeof(ch)) {
+ warnx("test_blocking_one_byte: timed_read: wanted %zu, "
+ "read %zd", sizeof(ch), len);
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+ if (ch != 0xfe) {
+ warnx("test_blocking_one_byte: timed_read: expected to read "
+ "0x%02x, read 0x%02x", 0xfe, ch);
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+}
+
+/*
+ * Write one byte to an empty fifo, then try to read one byte and make sure
+ * we don't get back EAGAIN.
+ */
+static void
+test_nonblocking_one_byte(void)
+{
+ int reader_fd, ret, timedout, writer_fd;
+ ssize_t len;
+ u_char ch;
+
+ makefifo("testfifo", __func__);
+ if (openfifo("testfifo", &reader_fd, &writer_fd) < 0) {
+ warn("test_nonblocking: openfifo: testfifo");
+ cleanfifo2("testfifo", -1, -1);
+ exit(-1);
+ }
+
+ if (set_nonblocking(reader_fd, __func__) < 0) {
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ ch = 0xfe;
+ ret = timed_write(writer_fd, &ch, sizeof(ch), &len, 5, &timedout,
+ __func__);
+ if (ret < 0) {
+ warn("test_nonblocking_one_byte: timed_write");
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+ if (len != sizeof(ch)) {
+ warnx("test_nonblocking_one_byte: timed_write: tried to write "
+ "%zu, wrote %zd", sizeof(ch), len);
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ ch = 0xab;
+ ret = timed_read(reader_fd, &ch, sizeof(ch), &len, 5, &timedout,
+ __func__);
+ if (ret < 0) {
+ warn("test_nonblocking_one_byte: timed_read");
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+ if (len != sizeof(ch)) {
+ warnx("test_nonblocking_one_byte: timed_read: wanted %zu, read "
+ "%zd", sizeof(ch), len);
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+ if (ch != 0xfe) {
+ warnx("test_nonblocking_one_byte: timed_read: expected to read "
+ "0x%02x, read 0x%02x", 0xfe, ch);
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+}
+
+/*
+ * First of two test cases involving a 512K buffer: write the buffer into a
+ * blocking file descriptor. We'd like to know it blocks, but the closest we
+ * can get is to see if SIGALRM fired during the I/O resulting in a partial
+ * write.
+ */
+static void
+test_blocking_partial_write(void)
+{
+ int reader_fd, ret, timedout, writer_fd;
+ u_char *buffer;
+ ssize_t len;
+
+ makefifo("testfifo", __func__);
+ if (openfifo("testfifo", &reader_fd, &writer_fd) < 0) {
+ warn("test_blocking_partial_write: openfifo: testfifo");
+ cleanfifo2("testfifo", -1, -1);
+ exit(-1);
+ }
+
+ if (set_blocking(writer_fd, __func__) < 0) {
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ buffer = malloc(512*1024);
+ if (buffer == NULL) {
+ warn("test_blocking_partial_write: malloc");
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+ bzero(buffer, 512*1024);
+
+ ret = timed_write(writer_fd, buffer, 512*1024, &len, 5, &timedout,
+ __func__);
+ if (ret < 0) {
+ warn("test_blocking_partial_write: timed_write");
+ free(buffer);
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ if (!timedout) {
+ warnx("test_blocking_partial_write: timed_write: blocking "
+ "socket didn't time out");
+ free(buffer);
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ free(buffer);
+
+ if (drain_fd(reader_fd, __func__) < 0) {
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+}
+
+/*
+ * Write a 512K buffer to an empty fifo using a non-blocking file descriptor,
+ * and make sure it doesn't block.
+ */
+static void
+test_nonblocking_partial_write(void)
+{
+ int reader_fd, ret, timedout, writer_fd;
+ u_char *buffer;
+ ssize_t len;
+
+ makefifo("testfifo", __func__);
+ if (openfifo("testfifo", &reader_fd, &writer_fd) < 0) {
+ warn("test_blocking_partial_write: openfifo: testfifo");
+ cleanfifo2("testfifo", -1, -1);
+ exit(-1);
+ }
+
+ if (set_nonblocking(writer_fd, __func__) < 0) {
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ buffer = malloc(512*1024);
+ if (buffer == NULL) {
+ warn("test_blocking_partial_write: malloc");
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+ bzero(buffer, 512*1024);
+
+ ret = timed_write(writer_fd, buffer, 512*1024, &len, 5, &timedout,
+ __func__);
+ if (ret < 0) {
+ warn("test_blocking_partial_write: timed_write");
+ free(buffer);
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ if (timedout) {
+ warnx("test_blocking_partial_write: timed_write: "
+ "non-blocking socket timed out");
+ free(buffer);
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ if (len == 0 || len >= 512*1024) {
+ warnx("test_blocking_partial_write: timed_write: requested "
+ "%d, sent %zd", 512*1024, len);
+ free(buffer);
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ free(buffer);
+
+ if (drain_fd(reader_fd, __func__) < 0) {
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+}
+
+/*
+ * test_coalesce_big_read() verifies that data mingles in the fifo across
+ * message boundaries by performing two small writes, then a bigger read
+ * that should return data from both writes.
+ */
+static void
+test_coalesce_big_read(void)
+{
+ int i, reader_fd, writer_fd;
+ u_char buffer[10];
+ ssize_t len;
+
+ makefifo("testfifo", __func__);
+ if (openfifo("testfifo", &reader_fd, &writer_fd) < 0) {
+ warn("test_coalesce_big_read: openfifo: testfifo");
+ cleanfifo2("testfifo", -1, -1);
+ exit(-1);
+ }
+
+ /* Write five, write five, read ten. */
+ for (i = 0; i < 10; i++)
+ buffer[i] = i;
+
+ len = write(writer_fd, buffer, 5);
+ if (len < 0) {
+ warn("test_coalesce_big_read: write 5");
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+ if (len != 5) {
+ warnx("test_coalesce_big_read: write 5 wrote %zd", len);
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ len = write(writer_fd, buffer + 5, 5);
+ if (len < 0) {
+ warn("test_coalesce_big_read: write 5");
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+ if (len != 5) {
+ warnx("test_coalesce_big_read: write 5 wrote %zd", len);
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ len = read(reader_fd, buffer, 10);
+ if (len < 0) {
+ warn("test_coalesce_big_read: read 10");
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+ if (len != 10) {
+ warnx("test_coalesce_big_read: read 10 read %zd", len);
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ for (i = 0; i < 10; i++) {
+ if (buffer[i] == i)
+ continue;
+ warnx("test_coalesce_big_read: expected to read 0x%02x, "
+ "read 0x%02x", i, buffer[i]);
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ cleanfifo2("testfifo", -1, -1);
+}
+
+/*
+ * test_coalesce_big_write() verifies that data mingles in the fifo across
+ * message boundaries by performing one big write, then two smaller reads
+ * that should return sequential elements of data from the write.
+ */
+static void
+test_coalesce_big_write(void)
+{
+ int i, reader_fd, writer_fd;
+ u_char buffer[10];
+ ssize_t len;
+
+ makefifo("testfifo", __func__);
+ if (openfifo("testfifo", &reader_fd, &writer_fd) < 0) {
+ warn("test_coalesce_big_write: openfifo: testfifo");
+ cleanfifo2("testfifo", -1, -1);
+ exit(-1);
+ }
+
+ /* Write ten, read five, read five. */
+ for (i = 0; i < 10; i++)
+ buffer[i] = i;
+
+ len = write(writer_fd, buffer, 10);
+ if (len < 0) {
+ warn("test_coalesce_big_write: write 10");
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+ if (len != 10) {
+ warnx("test_coalesce_big_write: write 10 wrote %zd", len);
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ len = read(reader_fd, buffer, 5);
+ if (len < 0) {
+ warn("test_coalesce_big_write: read 5");
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+ if (len != 5) {
+ warnx("test_coalesce_big_write: read 5 read %zd", len);
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ len = read(reader_fd, buffer + 5, 5);
+ if (len < 0) {
+ warn("test_coalesce_big_write: read 5");
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+ if (len != 5) {
+ warnx("test_coalesce_big_write: read 5 read %zd", len);
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ for (i = 0; i < 10; i++) {
+ if (buffer[i] == i)
+ continue;
+ warnx("test_coalesce_big_write: expected to read 0x%02x, "
+ "read 0x%02x", i, buffer[i]);
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ cleanfifo2("testfifo", -1, -1);
+}
+
+static int
+poll_status(int fd, int *readable, int *writable, int *exception,
+ const char *testname)
+{
+ struct pollfd fds[1];
+
+ fds[0].fd = fd;
+ fds[0].events = POLLIN | POLLOUT | POLLERR;
+ fds[0].revents = 0;
+
+ if (poll(fds, 1, 0) < 0) {
+ warn("%s: poll", testname);
+ return (-1);
+ }
+ *readable = (fds[0].revents & POLLIN) ? 1 : 0;
+ *writable = (fds[0].revents & POLLOUT) ? 1 : 0;
+ *exception = (fds[0].revents & POLLERR) ? 1 : 0;
+ return (0);
+}
+
+static int
+select_status(int fd, int *readable, int *writable, int *exception,
+ const char *testname)
+{
+ struct fd_set readfds, writefds, exceptfds;
+ struct timeval timeout;
+
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ FD_ZERO(&exceptfds);
+ FD_SET(fd, &readfds);
+ FD_SET(fd, &writefds);
+ FD_SET(fd, &exceptfds);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+ if (select(fd+1, &readfds, &writefds, &exceptfds, &timeout) < 0) {
+ warn("%s: select", testname);
+ return (-1);
+ }
+ *readable = FD_ISSET(fd, &readfds) ? 1 : 0;
+ *writable = FD_ISSET(fd, &writefds) ? 1 : 0;
+ *exception = FD_ISSET(fd, &exceptfds) ? 1 : 0;
+ return (0);
+}
+
+/*
+ * Given an existing kqueue, set up read and write event filters for the
+ * passed file descriptor. Typically called once for the read endpoint, and
+ * once for the write endpoint.
+ */
+static int
+kqueue_setup(int kqueue_fd, int fd, const char *testname)
+{
+ struct kevent kevent_changelist[2];
+ struct kevent kevent_eventlist[KQUEUE_MAX_EVENT], *kp;
+ struct timespec timeout;
+ int i, ret;
+
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+
+ bzero(&kevent_changelist, sizeof(kevent_changelist));
+ EV_SET(&kevent_changelist[0], fd, EVFILT_READ, EV_ADD, 0, 0, 0);
+ EV_SET(&kevent_changelist[1], fd, EVFILT_WRITE, EV_ADD, 0, 0, 0);
+
+ bzero(&kevent_eventlist, sizeof(kevent_eventlist));
+ ret = kevent(kqueue_fd, kevent_changelist, 2, kevent_eventlist,
+ KQUEUE_MAX_EVENT, &timeout);
+ if (ret < 0) {
+ warn("%s:%s: kevent initial register", testname, __func__);
+ return (-1);
+ }
+
+ /*
+ * Verify that the events registered alright.
+ */
+ for (i = 0; i < ret; i++) {
+ kp = &kevent_eventlist[i];
+ if (kp->flags != EV_ERROR)
+ continue;
+ errno = kp->data;
+ warn("%s:%s: kevent register index %d", testname, __func__,
+ i);
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+kqueue_status(int kqueue_fd, int fd, int *readable, int *writable,
+ int *exception, const char *testname)
+{
+ struct kevent kevent_eventlist[KQUEUE_MAX_EVENT], *kp;
+ struct timespec timeout;
+ int i, ret;
+
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+
+ ret = kevent(kqueue_fd, NULL, 0, kevent_eventlist, KQUEUE_MAX_EVENT,
+ &timeout);
+ if (ret < 0) {
+ warn("%s: %s: kevent", testname, __func__);
+ return (-1);
+ }
+
+ *readable = *writable = *exception = 0;
+ for (i = 0; i < ret; i++) {
+ kp = &kevent_eventlist[i];
+ if (kp->ident != (u_int)fd)
+ continue;
+ if (kp->filter == EVFILT_READ)
+ *readable = 1;
+ if (kp->filter == EVFILT_WRITE)
+ *writable = 1;
+ }
+
+ return (0);
+}
+
+static int
+fionread_status(int fd, int *readable, const char *testname)
+{
+ int i;
+
+ if (ioctl(fd, FIONREAD, &i) < 0) {
+ warn("%s: ioctl(FIONREAD)", testname);
+ return (-1);
+ }
+
+ if (i > 0)
+ *readable = 1;
+ else
+ *readable = 0;
+ return (0);
+}
+
+#define READABLE 1
+#define WRITABLE 1
+#define EXCEPTION 1
+
+#define NOT_READABLE 0
+#define NOT_WRITABLE 0
+#define NOT_EXCEPTION 0
+
+static int
+assert_status(int fd, int kqueue_fd, int assert_readable,
+ int assert_writable, int assert_exception, const char *testname,
+ const char *conditionname, const char *fdname)
+{
+ int readable, writable, exception;
+
+ if (poll_status(fd, &readable, &writable, &exception, testname) < 0)
+ return (-1);
+
+ if (readable != assert_readable || writable != assert_writable ||
+ exception != assert_exception) {
+ warnx("%s: %s polls r:%d, w:%d, e:%d on %s", testname,
+ fdname, readable, writable, exception, conditionname);
+ return (-1);
+ }
+
+ if (select_status(fd, &readable, &writable, &exception, testname) < 0)
+ return (-1);
+
+ if (readable != assert_readable || writable != assert_writable ||
+ exception != assert_exception) {
+ warnx("%s: %s selects r:%d, w:%d, e:%d on %s", testname,
+ fdname, readable, writable, exception, conditionname);
+ return (-1);
+ }
+
+ if (kqueue_status(kqueue_fd, fd, &readable, &writable, &exception,
+ testname) < 0)
+ return (-1);
+
+ if (readable != assert_readable || writable != assert_writable ||
+ exception != assert_exception) {
+ warnx("%s: %s kevent r:%d, w:%d, e:%d on %s", testname,
+ fdname, readable, writable, exception, conditionname);
+ return (-1);
+ }
+
+ if (fionread_status(fd, &readable, __func__) < 0)
+ return (-1);
+
+ if (readable != assert_readable) {
+ warnx("%s: %s fionread r:%d on %s", testname, fdname,
+ readable, conditionname);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * test_events() uses poll(), select(), and kevent() to query the status of
+ * fifo file descriptors and determine whether they match expected state
+ * based on earlier semantic tests: specifically, whether or not poll/select/
+ * kevent will correctly inform on readable/writable state following I/O.
+ *
+ * It would be nice to also test status changes as a result of closing of one
+ * or another fifo endpoint.
+ */
+static void
+test_events_outofbox(void)
+{
+ int kqueue_fd, reader_fd, writer_fd;
+
+ makefifo("testfifo", __func__);
+ if (openfifo("testfifo", &reader_fd, &writer_fd) < 0) {
+ warn("test_events_outofbox: openfifo: testfifo");
+ cleanfifo2("testfifo", -1, -1);
+ exit(-1);
+ }
+
+ kqueue_fd = kqueue();
+ if (kqueue_fd < 0) {
+ warn("%s: kqueue", __func__);
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ if (kqueue_setup(kqueue_fd, reader_fd, __func__) < 0) {
+ cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+ exit(-1);
+ }
+
+ if (kqueue_setup(kqueue_fd, writer_fd, __func__) < 0) {
+ cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+ exit(-1);
+ }
+
+ /*
+ * Make sure that fresh, out-of-the-box fifo file descriptors have
+ * good initial states. The reader_fd should have no active state,
+ * since it will not be readable (no data in pipe), writable (it's
+ * a read-only descriptor), and there's no reason for error yet.
+ */
+ if (assert_status(reader_fd, kqueue_fd, NOT_READABLE, NOT_WRITABLE,
+ NOT_EXCEPTION, __func__, "create", "reader_fd") < 0) {
+ cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+ exit(-1);
+ }
+
+ /*
+ * Make sure that fresh, out-of-the-box fifo file descriptors have
+ * good initial states. The writer_fd should be ready to write.
+ */
+ if (assert_status(writer_fd, kqueue_fd, NOT_READABLE, WRITABLE,
+ NOT_EXCEPTION, __func__, "create", "writer_fd") < 0) {
+ cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+ exit(-1);
+ }
+
+ cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+}
+
+static void
+test_events_write_read_byte(void)
+{
+ int kqueue_fd, reader_fd, writer_fd;
+ ssize_t len;
+ u_char ch;
+
+ makefifo("testfifo", __func__);
+ if (openfifo("testfifo", &reader_fd, &writer_fd) < 0) {
+ warn("test_events_write_read_byte: openfifo: testfifo");
+ cleanfifo2("testfifo", -1, -1);
+ exit(-1);
+ }
+
+ kqueue_fd = kqueue();
+ if (kqueue_fd < 0) {
+ warn("%s: kqueue", __func__);
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ if (kqueue_setup(kqueue_fd, reader_fd, __func__) < 0) {
+ cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+ exit(-1);
+ }
+
+ if (kqueue_setup(kqueue_fd, writer_fd, __func__) < 0) {
+ cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+ exit(-1);
+ }
+
+ /*
+ * Write a byte to the fifo, and make sure that the read end becomes
+ * readable, and that the write end remains writable (small write).
+ */
+ ch = 0x00;
+ len = write(writer_fd, &ch, sizeof(ch));
+ if (len < 0) {
+ warn("%s: write", __func__);
+ cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+ exit(-1);
+ }
+
+ if (assert_status(reader_fd, kqueue_fd, READABLE, NOT_WRITABLE,
+ NOT_EXCEPTION, __func__, "write", "reader_fd") < 0) {
+ cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+ exit(-1);
+ }
+
+ /*
+ * the writer_fd should remain writable.
+ */
+ if (assert_status(writer_fd, kqueue_fd, NOT_READABLE, WRITABLE,
+ NOT_EXCEPTION, __func__, "write", "writer_fd") < 0) {
+ cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+ exit(-1);
+ }
+
+ /*
+ * Read the byte from the reader_fd, and now confirm that the fifo
+ * becomes unreadable.
+ */
+ len = read(reader_fd, &ch, sizeof(ch));
+ if (len < 0) {
+ warn("%s: read", __func__);
+ cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+ exit(-1);
+ }
+
+ if (assert_status(reader_fd, kqueue_fd, NOT_READABLE, NOT_WRITABLE,
+ NOT_EXCEPTION, __func__, "write+read", "reader_fd") < 0) {
+ cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+ exit(-1);
+ }
+
+ /*
+ * The writer_fd should remain writable.
+ */
+ if (assert_status(writer_fd, kqueue_fd, NOT_READABLE, WRITABLE,
+ NOT_EXCEPTION, __func__, "write+read", "writer_fd") < 0) {
+ cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+ exit(-1);
+ }
+
+ cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+}
+
+/*
+ * Write a 512k buffer to the fifo in non-blocking mode, and make sure that
+ * the write end becomes un-writable as a result of a partial write that
+ * fills the fifo buffer.
+ */
+static void
+test_events_partial_write(void)
+{
+ int kqueue_fd, reader_fd, writer_fd;
+ u_char *buffer;
+ ssize_t len;
+
+ makefifo("testfifo", __func__);
+ if (openfifo("testfifo", &reader_fd, &writer_fd) < 0) {
+ warn("test_events_partial_write: openfifo: testfifo");
+ cleanfifo2("testfifo", -1, -1);
+ exit(-1);
+ }
+
+ kqueue_fd = kqueue();
+ if (kqueue_fd < 0) {
+ warn("%s: kqueue", __func__);
+ cleanfifo2("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ if (kqueue_setup(kqueue_fd, reader_fd, __func__) < 0) {
+ cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+ exit(-1);
+ }
+
+ if (kqueue_setup(kqueue_fd, writer_fd, __func__) < 0) {
+ cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+ exit(-1);
+ }
+
+ if (set_nonblocking(writer_fd, "test_events") < 0) {
+ cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+ exit(-1);
+ }
+
+ buffer = malloc(512*1024);
+ if (buffer == NULL) {
+ warn("test_events_partial_write: malloc");
+ cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+ exit(-1);
+ }
+ bzero(buffer, 512*1024);
+
+ len = write(writer_fd, buffer, 512*1024);
+ if (len < 0) {
+ warn("test_events_partial_write: write");
+ free(buffer);
+ cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+ exit(-1);
+ }
+
+ free(buffer);
+
+ if (assert_status(writer_fd, kqueue_fd, NOT_READABLE, NOT_WRITABLE,
+ NOT_EXCEPTION, __func__, "big write", "writer_fd") < 0) {
+ cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+ exit(-1);
+ }
+
+ if (drain_fd(reader_fd, "test_events") < 0) {
+ cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+ exit(-1);
+ }
+
+ /*
+ * Test that the writer_fd has been restored to writable state after
+ * draining.
+ */
+ if (assert_status(writer_fd, kqueue_fd, NOT_READABLE, WRITABLE,
+ NOT_EXCEPTION, __func__, "big write + drain", "writer_fd") < 0) {
+ cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+ exit(-1);
+ }
+
+ cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+}
+
+/*
+ * We don't comprehensively test O_RDWR file descriptors, but do run a couple
+ * of event tests to make sure that the fifo implementation doesn't mixed up
+ * status checks. In particular, at least one past FreeBSD bug exists in
+ * which the FIONREAD test was performed on the wrong socket implementing the
+ * fifo, resulting in the fifo never returning readable.
+ */
+static void
+test_events_rdwr(void)
+{
+ int fd, kqueue_fd;
+ ssize_t len;
+ char ch;
+
+ makefifo("testfifo", __func__);
+ if (openfifo_rw("testfifo", &fd) < 0) {
+ warn("%s: openfifo_rw: testfifo", __func__);
+ cleanfifo2("testfifo", -1, -1);
+ exit(-1);
+ }
+
+ kqueue_fd = kqueue();
+ if (kqueue_fd < 0) {
+ warn("%s: kqueue", __func__);
+ cleanfifo2("testifo", fd, -1);
+ exit(-1);
+ }
+
+ if (kqueue_setup(kqueue_fd, fd, __func__) < 0) {
+ cleanfifo2("testfifo", fd, kqueue_fd);
+ exit(-1);
+ }
+
+ /*
+ * On first creation, the O_RDWR descriptor should be writable but
+ * not readable.
+ */
+ if (assert_status(fd, kqueue_fd, NOT_READABLE, WRITABLE,
+ NOT_EXCEPTION, __func__, "create", "fd") < 0) {
+ cleanfifo2("testfifo", fd, kqueue_fd);
+ exit(-1);
+ }
+
+ /*
+ * Write a byte, which should cause the file descriptor to become
+ * readable and writable.
+ */
+ ch = 0x00;
+ len = write(fd, &ch, sizeof(ch));
+ if (len < 0) {
+ warn("%s: write", __func__);
+ cleanfifo2("testfifo", fd, kqueue_fd);
+ exit(-1);
+ }
+
+ if (assert_status(fd, kqueue_fd, READABLE, WRITABLE, NOT_EXCEPTION,
+ __func__, "write", "fd") < 0) {
+ cleanfifo2("testfifo", fd, kqueue_fd);
+ exit(-1);
+ }
+
+ /*
+ * Read a byte, which should cause the file descriptor to return to
+ * simply being writable.
+ */
+ len = read(fd, &ch, sizeof(ch));
+ if (len < 0) {
+ warn("%s: read", __func__);
+ cleanfifo2("testfifo", fd, kqueue_fd);
+ exit(-1);
+ }
+
+ if (assert_status(fd, kqueue_fd, NOT_READABLE, WRITABLE,
+ NOT_EXCEPTION, __func__, "write+read", "fd") < 0) {
+ cleanfifo2("testfifo", fd, kqueue_fd);
+ exit(-1);
+ }
+
+ cleanfifo2("testfifo", fd, kqueue_fd);
+}
+
+int
+main(void)
+{
+
+ strcpy(temp_dir, "fifo_io.XXXXXXXXXXX");
+ if (mkdtemp(temp_dir) == NULL)
+ err(-1, "mkdtemp");
+ atexit(atexit_temp_dir);
+
+ if (chdir(temp_dir) < 0)
+ err(-1, "chdir %s", temp_dir);
+
+ test_simpleio();
+ test_blocking_read_empty();
+ test_blocking_one_byte();
+ test_nonblocking_one_byte();
+ test_blocking_partial_write();
+ test_nonblocking_partial_write();
+ test_coalesce_big_read();
+ test_coalesce_big_write();
+ test_events_outofbox();
+ test_events_write_read_byte();
+ test_events_partial_write();
+ test_events_rdwr();
+
+ return (0);
+}
diff --git a/tests/sys/fifo/fifo_kqueue.c b/tests/sys/fifo/fifo_kqueue.c
new file mode 100644
index 000000000000..9052ed599282
--- /dev/null
+++ b/tests/sys/fifo/fifo_kqueue.c
@@ -0,0 +1,430 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2020 Jan Kokemüller
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/event.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(fifo_kqueue__writes);
+ATF_TC_BODY(fifo_kqueue__writes, tc)
+{
+ int p[2] = { -1, -1 };
+
+ ATF_REQUIRE(mkfifo("testfifo", 0600) == 0);
+
+ ATF_REQUIRE((p[0] = open("testfifo",
+ O_RDONLY | O_CLOEXEC | O_NONBLOCK)) >= 0);
+ ATF_REQUIRE((p[1] = open("testfifo",
+ O_WRONLY | O_CLOEXEC | O_NONBLOCK)) >= 0);
+
+ int kq = kqueue();
+ ATF_REQUIRE(kq >= 0);
+
+ struct kevent kev[32];
+ EV_SET(&kev[0], p[1], EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0, 0);
+ EV_SET(&kev[1], p[1], EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, 0);
+
+ ATF_REQUIRE(kevent(kq, kev, 2, NULL, 0, NULL) == 0);
+
+ /* A new writer should immediately get a EVFILT_WRITE event. */
+
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev),
+ &(struct timespec) { 0, 0 }) == 1);
+ ATF_REQUIRE(kev[0].ident == (uintptr_t)p[1]);
+ ATF_REQUIRE(kev[0].filter == EVFILT_WRITE);
+ ATF_REQUIRE(kev[0].flags == EV_CLEAR);
+ ATF_REQUIRE(kev[0].fflags == 0);
+ ATF_REQUIRE(kev[0].data == 16384);
+ ATF_REQUIRE(kev[0].udata == 0);
+
+ /* Filling up the pipe should make the EVFILT_WRITE disappear. */
+
+ char c = 0;
+ ssize_t r;
+ while ((r = write(p[1], &c, 1)) == 1) {
+ }
+ ATF_REQUIRE(r < 0);
+ ATF_REQUIRE(errno == EAGAIN || errno == EWOULDBLOCK);
+
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev),
+ &(struct timespec) { 0, 0 }) == 0);
+
+ /* Reading (PIPE_BUF - 1) bytes will not trigger a EVFILT_WRITE yet. */
+
+ for (int i = 0; i < PIPE_BUF - 1; ++i) {
+ ATF_REQUIRE(read(p[0], &c, 1) == 1);
+ }
+
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev),
+ &(struct timespec) { 0, 0 }) == 0);
+
+ /* Reading one additional byte triggers the EVFILT_WRITE. */
+
+ ATF_REQUIRE(read(p[0], &c, 1) == 1);
+
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev),
+ &(struct timespec) { 0, 0 }) == 1);
+ ATF_REQUIRE(kev[0].ident == (uintptr_t)p[1]);
+ ATF_REQUIRE(kev[0].filter == EVFILT_WRITE);
+ ATF_REQUIRE(kev[0].flags == EV_CLEAR);
+ ATF_REQUIRE(kev[0].fflags == 0);
+ ATF_REQUIRE(kev[0].data == PIPE_BUF);
+ ATF_REQUIRE(kev[0].udata == 0);
+
+ /*
+ * Reading another byte triggers the EVFILT_WRITE again with a changed
+ * 'data' field.
+ */
+
+ ATF_REQUIRE(read(p[0], &c, 1) == 1);
+
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev),
+ &(struct timespec) { 0, 0 }) == 1);
+ ATF_REQUIRE(kev[0].ident == (uintptr_t)p[1]);
+ ATF_REQUIRE(kev[0].filter == EVFILT_WRITE);
+ ATF_REQUIRE(kev[0].flags == EV_CLEAR);
+ ATF_REQUIRE(kev[0].fflags == 0);
+ ATF_REQUIRE(kev[0].data == PIPE_BUF + 1);
+ ATF_REQUIRE(kev[0].udata == 0);
+
+ /*
+ * Closing the read end should make a EV_EOF appear but leave the 'data'
+ * field unchanged.
+ */
+
+ ATF_REQUIRE(close(p[0]) == 0);
+
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev), NULL) == 1);
+ ATF_REQUIRE(kev[0].ident == (uintptr_t)p[1]);
+ ATF_REQUIRE(kev[0].filter == EVFILT_WRITE);
+ ATF_REQUIRE(kev[0].flags == (EV_CLEAR | EV_EOF));
+ ATF_REQUIRE(kev[0].fflags == 0);
+ ATF_REQUIRE(kev[0].data == PIPE_BUF + 1);
+ ATF_REQUIRE(kev[0].udata == 0);
+
+ ATF_REQUIRE(close(kq) == 0);
+ ATF_REQUIRE(close(p[1]) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(fifo_kqueue__connecting_reader);
+ATF_TC_BODY(fifo_kqueue__connecting_reader, tc)
+{
+ int p[2] = { -1, -1 };
+
+ ATF_REQUIRE(mkfifo("testfifo", 0600) == 0);
+
+ ATF_REQUIRE((p[0] = open("testfifo",
+ O_RDONLY | O_CLOEXEC | O_NONBLOCK)) >= 0);
+ ATF_REQUIRE((p[1] = open("testfifo",
+ O_WRONLY | O_CLOEXEC | O_NONBLOCK)) >= 0);
+
+ int kq = kqueue();
+ ATF_REQUIRE(kq >= 0);
+
+ struct kevent kev[32];
+ EV_SET(&kev[0], p[1], EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0, 0);
+ EV_SET(&kev[1], p[1], EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, 0);
+
+ ATF_REQUIRE(kevent(kq, kev, 2, NULL, 0, NULL) == 0);
+
+ /* A new writer should immediately get a EVFILT_WRITE event. */
+
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev),
+ &(struct timespec) { 0, 0 }) == 1);
+ ATF_REQUIRE(kev[0].ident == (uintptr_t)p[1]);
+ ATF_REQUIRE(kev[0].filter == EVFILT_WRITE);
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev),
+ &(struct timespec) { 0, 0 }) == 0);
+
+ /*
+ * Filling the pipe, reading (PIPE_BUF + 1) bytes, then closing the
+ * read end leads to a EVFILT_WRITE with EV_EOF set.
+ */
+
+ char c = 0;
+ ssize_t r;
+ while ((r = write(p[1], &c, 1)) == 1) {
+ }
+ ATF_REQUIRE(r < 0);
+ ATF_REQUIRE(errno == EAGAIN || errno == EWOULDBLOCK);
+
+ for (int i = 0; i < PIPE_BUF + 1; ++i) {
+ ATF_REQUIRE(read(p[0], &c, 1) == 1);
+ }
+
+ ATF_REQUIRE(close(p[0]) == 0);
+
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev), NULL) == 1);
+ ATF_REQUIRE(kev[0].filter == EVFILT_WRITE);
+ ATF_REQUIRE((kev[0].flags & EV_EOF) != 0);
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev),
+ &(struct timespec) { 0, 0 }) == 0);
+
+ /* Opening the reader again must trigger the EVFILT_WRITE. */
+
+ ATF_REQUIRE((p[0] = open("testfifo",
+ O_RDONLY | O_CLOEXEC | O_NONBLOCK)) >= 0);
+
+ r = kevent(kq, NULL, 0, kev, nitems(kev), &(struct timespec) { 1, 0 });
+ ATF_REQUIRE(r == 1);
+ ATF_REQUIRE(kev[0].ident == (uintptr_t)p[1]);
+ ATF_REQUIRE(kev[0].filter == EVFILT_WRITE);
+ ATF_REQUIRE(kev[0].flags == EV_CLEAR);
+ ATF_REQUIRE(kev[0].fflags == 0);
+ ATF_REQUIRE(kev[0].data == PIPE_BUF + 1);
+ ATF_REQUIRE(kev[0].udata == 0);
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev),
+ &(struct timespec) { 0, 0 }) == 0);
+
+ ATF_REQUIRE(close(kq) == 0);
+ ATF_REQUIRE(close(p[0]) == 0);
+ ATF_REQUIRE(close(p[1]) == 0);
+}
+
+/* Check that EVFILT_READ behaves sensibly on a FIFO reader. */
+ATF_TC_WITHOUT_HEAD(fifo_kqueue__reads);
+ATF_TC_BODY(fifo_kqueue__reads, tc)
+{
+ struct kevent kev[32];
+ ssize_t bytes, i, n;
+ int kq, p[2];
+ char c;
+
+ ATF_REQUIRE(mkfifo("testfifo", 0600) == 0);
+
+ ATF_REQUIRE((p[0] = open("testfifo",
+ O_RDONLY | O_CLOEXEC | O_NONBLOCK)) >= 0);
+ ATF_REQUIRE((p[1] = open("testfifo",
+ O_WRONLY | O_CLOEXEC | O_NONBLOCK)) >= 0);
+
+ bytes = 0;
+ c = 0;
+ while ((n = write(p[1], &c, 1)) == 1)
+ bytes++;
+ ATF_REQUIRE(n < 0);
+ ATF_REQUIRE(errno == EAGAIN || errno == EWOULDBLOCK);
+ ATF_REQUIRE(bytes > 1);
+
+ for (i = 0; i < bytes / 2; i++)
+ ATF_REQUIRE(read(p[0], &c, 1) == 1);
+ bytes -= i;
+
+ kq = kqueue();
+ ATF_REQUIRE(kq >= 0);
+
+ EV_SET(&kev[0], p[0], EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, 0);
+
+ ATF_REQUIRE(kevent(kq, kev, 1, NULL, 0, NULL) == 0);
+
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev),
+ &(struct timespec){ 0, 0 }) == 1);
+ ATF_REQUIRE(kev[0].ident == (uintptr_t)p[0]);
+ ATF_REQUIRE(kev[0].filter == EVFILT_READ);
+ ATF_REQUIRE(kev[0].flags == EV_CLEAR);
+ ATF_REQUIRE(kev[0].fflags == 0);
+ ATF_REQUIRE(kev[0].data == bytes);
+ ATF_REQUIRE(kev[0].udata == 0);
+
+ while (bytes-- > 0)
+ ATF_REQUIRE(read(p[0], &c, 1) == 1);
+ n = read(p[0], &c, 1);
+ ATF_REQUIRE(n < 0);
+ ATF_REQUIRE(errno == EAGAIN || errno == EWOULDBLOCK);
+
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev),
+ &(struct timespec) { 0, 0 }) == 0);
+
+ ATF_REQUIRE(close(kq) == 0);
+ ATF_REQUIRE(close(p[0]) == 0);
+ ATF_REQUIRE(close(p[1]) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(fifo_kqueue__read_eof_wakeups);
+ATF_TC_BODY(fifo_kqueue__read_eof_wakeups, tc)
+{
+ int p[2] = { -1, -1 };
+
+ ATF_REQUIRE(mkfifo("testfifo", 0600) == 0);
+
+ ATF_REQUIRE((p[0] = open("testfifo",
+ O_RDONLY | O_CLOEXEC | O_NONBLOCK)) >= 0);
+ ATF_REQUIRE((p[1] = open("testfifo",
+ O_WRONLY | O_CLOEXEC | O_NONBLOCK)) >= 0);
+
+ int kq = kqueue();
+ ATF_REQUIRE(kq >= 0);
+
+ struct kevent kev[32];
+
+ EV_SET(&kev[0], p[0], EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, 0);
+ ATF_REQUIRE(kevent(kq, kev, 1, NULL, 0, NULL) == 0);
+
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev),
+ &(struct timespec) { 0, 0 }) == 0);
+
+ /*
+ * Closing the writer must trigger a EVFILT_READ edge with EV_EOF set.
+ */
+
+ ATF_REQUIRE(close(p[1]) == 0);
+
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev),
+ &(struct timespec) { 0, 0 }) == 1);
+ ATF_REQUIRE(kev[0].ident == (uintptr_t)p[0]);
+ ATF_REQUIRE(kev[0].filter == EVFILT_READ);
+ ATF_REQUIRE(kev[0].flags == (EV_EOF | EV_CLEAR));
+ ATF_REQUIRE(kev[0].fflags == 0);
+ ATF_REQUIRE(kev[0].data == 0);
+ ATF_REQUIRE(kev[0].udata == 0);
+
+ /*
+ * Trying to read from a closed pipe should not trigger EVFILT_READ
+ * edges.
+ */
+
+ char c;
+ ATF_REQUIRE(read(p[0], &c, 1) == 0);
+
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev),
+ &(struct timespec) { 0, 0 }) == 0);
+
+ ATF_REQUIRE(close(kq) == 0);
+ ATF_REQUIRE(close(p[0]) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(fifo_kqueue__read_eof_state_when_reconnecting);
+ATF_TC_BODY(fifo_kqueue__read_eof_state_when_reconnecting, tc)
+{
+ int p[2] = { -1, -1 };
+
+ ATF_REQUIRE(mkfifo("testfifo", 0600) == 0);
+
+ ATF_REQUIRE((p[0] = open("testfifo",
+ O_RDONLY | O_CLOEXEC | O_NONBLOCK)) >= 0);
+ ATF_REQUIRE((p[1] = open("testfifo",
+ O_WRONLY | O_CLOEXEC | O_NONBLOCK)) >= 0);
+
+ int kq = kqueue();
+ ATF_REQUIRE(kq >= 0);
+
+ struct kevent kev[32];
+
+ EV_SET(&kev[0], p[0], EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, 0);
+ ATF_REQUIRE(kevent(kq, kev, 1, NULL, 0, NULL) == 0);
+
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev),
+ &(struct timespec) { 0, 0 }) == 0);
+
+ /*
+ * Closing the writer must trigger a EVFILT_READ edge with EV_EOF set.
+ */
+
+ ATF_REQUIRE(close(p[1]) == 0);
+
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev),
+ &(struct timespec) { 0, 0 }) == 1);
+ ATF_REQUIRE(kev[0].ident == (uintptr_t)p[0]);
+ ATF_REQUIRE(kev[0].filter == EVFILT_READ);
+ ATF_REQUIRE(kev[0].flags == (EV_EOF | EV_CLEAR));
+ ATF_REQUIRE(kev[0].fflags == 0);
+ ATF_REQUIRE(kev[0].data == 0);
+ ATF_REQUIRE(kev[0].udata == 0);
+
+ /* A new reader shouldn't see the EOF flag. */
+
+ {
+ int new_reader;
+ ATF_REQUIRE((new_reader = open("testfifo",
+ O_RDONLY | O_CLOEXEC | O_NONBLOCK)) >= 0);
+
+ int new_kq = kqueue();
+ ATF_REQUIRE(new_kq >= 0);
+
+ struct kevent new_kev[32];
+ EV_SET(&new_kev[0], new_reader, EVFILT_READ, EV_ADD | EV_CLEAR,
+ 0, 0, 0);
+ ATF_REQUIRE(kevent(new_kq, new_kev, 1, NULL, 0, NULL) == 0);
+
+ ATF_REQUIRE(kevent(new_kq, NULL, 0, new_kev, nitems(new_kev),
+ &(struct timespec) { 0, 0 }) == 0);
+
+ ATF_REQUIRE(close(new_kq) == 0);
+ ATF_REQUIRE(close(new_reader) == 0);
+ }
+
+ /*
+ * Simply reopening the writer does not trigger the EVFILT_READ again --
+ * EV_EOF should be cleared, but there is no data yet so the filter
+ * does not trigger.
+ */
+
+ ATF_REQUIRE((p[1] = open("testfifo",
+ O_WRONLY | O_CLOEXEC | O_NONBLOCK)) >= 0);
+
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev),
+ &(struct timespec) { 0, 0 }) == 0);
+
+ /* Writing a byte should trigger a EVFILT_READ. */
+
+ char c = 0;
+ ATF_REQUIRE(write(p[1], &c, 1) == 1);
+
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev),
+ &(struct timespec) { 0, 0 }) == 1);
+ ATF_REQUIRE(kev[0].ident == (uintptr_t)p[0]);
+ ATF_REQUIRE(kev[0].filter == EVFILT_READ);
+ ATF_REQUIRE(kev[0].flags == EV_CLEAR);
+ ATF_REQUIRE(kev[0].fflags == 0);
+ ATF_REQUIRE(kev[0].data == 1);
+ ATF_REQUIRE(kev[0].udata == 0);
+
+ ATF_REQUIRE(close(kq) == 0);
+ ATF_REQUIRE(close(p[0]) == 0);
+ ATF_REQUIRE(close(p[1]) == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, fifo_kqueue__writes);
+ ATF_TP_ADD_TC(tp, fifo_kqueue__connecting_reader);
+ ATF_TP_ADD_TC(tp, fifo_kqueue__reads);
+ ATF_TP_ADD_TC(tp, fifo_kqueue__read_eof_wakeups);
+ ATF_TP_ADD_TC(tp, fifo_kqueue__read_eof_state_when_reconnecting);
+
+ return atf_no_error();
+}
diff --git a/tests/sys/fifo/fifo_misc.c b/tests/sys/fifo/fifo_misc.c
new file mode 100644
index 000000000000..c5860d1f0262
--- /dev/null
+++ b/tests/sys/fifo/fifo_misc.c
@@ -0,0 +1,334 @@
+/*-
+ * Copyright (c) 2005 Robert N. M. Watson
+ * Copyright (c) 2012 Jilles Tjoelker
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/filio.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * Regression test for piddling details of fifos.
+ */
+
+/*
+ * All activity occurs within a temporary directory created early in the
+ * test.
+ */
+static char temp_dir[PATH_MAX];
+
+static void __unused
+atexit_temp_dir(void)
+{
+
+ rmdir(temp_dir);
+}
+
+static void
+makefifo(const char *fifoname, const char *testname)
+{
+
+ if (mkfifo(fifoname, 0700) < 0)
+ err(-1, "%s: makefifo: mkfifo: %s", testname, fifoname);
+}
+
+static void
+cleanfifo(const char *fifoname, int fd1, int fd2)
+{
+
+ if (fd1 != -1)
+ close(fd1);
+ if (fd2 != -1)
+ close(fd2);
+ (void)unlink(fifoname);
+}
+
+static int
+openfifo(const char *fifoname, int *reader_fdp, int *writer_fdp)
+{
+ int error, fd1, fd2;
+
+ fd1 = open(fifoname, O_RDONLY | O_NONBLOCK);
+ if (fd1 < 0)
+ return (-1);
+ fd2 = open(fifoname, O_WRONLY | O_NONBLOCK);
+ if (fd2 < 0) {
+ error = errno;
+ close(fd1);
+ errno = error;
+ return (-1);
+ }
+ *reader_fdp = fd1;
+ *writer_fdp = fd2;
+
+ return (0);
+}
+
+/*
+ * POSIX does not allow lseek(2) on fifos, so we expect ESPIPE as a result.
+ */
+static void
+test_lseek(void)
+{
+ int reader_fd, writer_fd;
+
+ makefifo("testfifo", __func__);
+
+ if (openfifo("testfifo", &reader_fd, &writer_fd) < 0) {
+ warn("%s: openfifo", __func__);
+ cleanfifo("testfifo", -1, -1);
+ exit(-1);
+ }
+
+ if (lseek(reader_fd, 1, SEEK_CUR) >= 0) {
+ warnx("%s: lseek succeeded instead of returning ESPIPE",
+ __func__);
+ cleanfifo("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+ if (errno != ESPIPE) {
+ warn("%s: lseek returned instead of ESPIPE", __func__);
+ cleanfifo("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ cleanfifo("testfifo", reader_fd, writer_fd);
+}
+
+/*
+ * truncate(2) on FIFO should silently return success.
+ */
+static void
+test_truncate(void)
+{
+
+ makefifo("testfifo", __func__);
+
+ if (truncate("testfifo", 1024) != 0) {
+ warn("%s: truncate", __func__);
+ cleanfifo("testfifo", -1, -1);
+ exit(-1);
+ }
+
+ cleanfifo("testfifo", -1, -1);
+}
+
+static int
+test_ioctl_setclearflag(int fd, unsigned long flag, const char *testname,
+ const char *fdname, const char *flagname)
+{
+ int i;
+
+ i = 1;
+ if (ioctl(fd, flag, &i) < 0) {
+ warn("%s:%s: ioctl(%s, %s, 1)", testname, __func__, fdname,
+ flagname);
+ cleanfifo("testfifo", -1, -1);
+ exit(-1);
+ }
+
+ i = 0;
+ if (ioctl(fd, flag, &i) < 0) {
+ warn("%s:%s: ioctl(%s, %s, 0)", testname, __func__, fdname,
+ flagname);
+ cleanfifo("testfifo", -1, -1);
+ exit(-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Test that various ioctls can be issued against the file descriptor. We
+ * don't currently test the semantics of these changes here.
+ */
+static void
+test_ioctl(void)
+{
+ int reader_fd, writer_fd;
+
+ makefifo("testfifo", __func__);
+
+ if (openfifo("testfifo", &reader_fd, &writer_fd) < 0) {
+ warn("%s: openfifo", __func__);
+ cleanfifo("testfifo", -1, -1);
+ exit(-1);
+ }
+
+ /*
+ * Set and remove the non-blocking I/O flag.
+ */
+ if (test_ioctl_setclearflag(reader_fd, FIONBIO, __func__,
+ "reader_fd", "FIONBIO") < 0) {
+ cleanfifo("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ if (test_ioctl_setclearflag(writer_fd, FIONBIO, __func__,
+ "writer_fd", "FIONBIO") < 0) {
+ cleanfifo("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ /*
+ * Set and remove the async I/O flag.
+ */
+ if (test_ioctl_setclearflag(reader_fd, FIOASYNC, __func__,
+ "reader_fd", "FIOASYNC") < 0) {
+ cleanfifo("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ if (test_ioctl_setclearflag(writer_fd, FIOASYNC, __func__,
+ "writer_fd", "FIONASYNC") < 0) {
+ cleanfifo("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ cleanfifo("testfifo", reader_fd, writer_fd);
+}
+
+/*
+ * fchmod(2)/fchown(2) on FIFO should work.
+ */
+static void
+test_chmodchown(void)
+{
+ struct stat sb;
+ int reader_fd, writer_fd;
+ uid_t u;
+ gid_t g;
+
+ makefifo("testfifo", __func__);
+
+ if (openfifo("testfifo", &reader_fd, &writer_fd) < 0) {
+ warn("%s: openfifo", __func__);
+ cleanfifo("testfifo", -1, -1);
+ exit(-1);
+ }
+
+ if (fchmod(reader_fd, 0666) != 0) {
+ warn("%s: fchmod", __func__);
+ cleanfifo("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ if (stat("testfifo", &sb) != 0) {
+ warn("%s: stat", __func__);
+ cleanfifo("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ if ((sb.st_mode & 0777) != 0666) {
+ warnx("%s: stat chmod result", __func__);
+ cleanfifo("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ if (fstat(writer_fd, &sb) != 0) {
+ warn("%s: fstat", __func__);
+ cleanfifo("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ if ((sb.st_mode & 0777) != 0666) {
+ warnx("%s: fstat chmod result", __func__);
+ cleanfifo("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ if (fchown(reader_fd, -1, -1) != 0) {
+ warn("%s: fchown 1", __func__);
+ cleanfifo("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ u = geteuid();
+ if (u == 0)
+ u = 1;
+ g = getegid();
+ if (fchown(reader_fd, u, g) != 0) {
+ warn("%s: fchown 2", __func__);
+ cleanfifo("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+ if (stat("testfifo", &sb) != 0) {
+ warn("%s: stat", __func__);
+ cleanfifo("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ if (sb.st_uid != u || sb.st_gid != g) {
+ warnx("%s: stat chown result", __func__);
+ cleanfifo("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ if (fstat(writer_fd, &sb) != 0) {
+ warn("%s: fstat", __func__);
+ cleanfifo("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ if (sb.st_uid != u || sb.st_gid != g) {
+ warnx("%s: fstat chown result", __func__);
+ cleanfifo("testfifo", reader_fd, writer_fd);
+ exit(-1);
+ }
+
+ cleanfifo("testfifo", -1, -1);
+}
+
+int
+main(void)
+{
+
+ strcpy(temp_dir, "fifo_misc.XXXXXXXXXXX");
+ if (mkdtemp(temp_dir) == NULL)
+ err(-1, "mkdtemp");
+ atexit(atexit_temp_dir);
+
+ if (chdir(temp_dir) < 0)
+ err(-1, "chdir %s", temp_dir);
+
+ test_lseek();
+ test_truncate();
+ test_ioctl();
+ test_chmodchown();
+
+ return (0);
+}
diff --git a/tests/sys/fifo/fifo_open.c b/tests/sys/fifo/fifo_open.c
new file mode 100644
index 000000000000..d30ed243fdec
--- /dev/null
+++ b/tests/sys/fifo/fifo_open.c
@@ -0,0 +1,474 @@
+/*-
+ * Copyright (c) 2005 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * Regression test to exercise various POSIX-defined parts of fifo behavior
+ * described for open(2):
+ *
+ * O_NONBLOCK
+ * When opening a FIFO with O_RDONLY or O_WRONLY set:
+ *
+ * - If O_NONBLOCK is set, an open() for reading-only shall return without
+ * delay. An open() for writing-only shall return an error if no process
+ * currently has the file open for reading.
+ *
+ * - If O_NONBLOCK is clear, an open() for reading-only shall block the
+ * calling thread until a thread opens the file for writing. An open()
+ * for writing-only shall block the calling thread until a thread opens
+ * the file for reading.
+ *
+ * When opening a block special or character special file that supports
+ * non-blocking opens:
+ *
+ * - If O_NONBLOCK is set, the open() function shall return without blocking
+ * for the device to be ready or available. Subsequent behavior of the
+ * device is device-specific.
+ *
+ * - If O_NONBLOCK is clear, the open() function shall block the calling
+ * thread until the device is ready or available before returning.
+ *
+ * Special errors:
+ *
+ * [ENXIO]
+ * O_NONBLOCK is set, the named file is a FIFO, O_WRONLY is set, and no
+ * process has the file open for reading.
+ */
+
+/*
+ * In order to test blocking/non-blocking behavior, test processes must
+ * potentially block themselves until released. As bugs in blocking result
+ * in processes that won't un-block, we must sacrifice a process to the task,
+ * watching and potentially killing it after a time-out. The main test
+ * process is never used to open or act directly on a fifo (other than to
+ * create or unlink it) in order to avoid the main test process being
+ * blocked.
+ */
+
+/*
+ * All activity occurs within a temporary directory created early in the
+ * test.
+ */
+static char temp_dir[PATH_MAX];
+
+static void __unused
+atexit_temp_dir(void)
+{
+
+ rmdir(temp_dir);
+}
+
+/*
+ * Run a function in a particular test process.
+ */
+static int
+run_in_process(int (*func)(void), pid_t *pidp, const char *errstr)
+{
+ pid_t pid;
+
+ pid = fork();
+ if (pid < 0) {
+ warn("%s: run_in_process: fork", errstr);
+ return (-1);
+ }
+
+ if (pid == 0)
+ exit(func());
+
+ if (pidp != NULL)
+ *pidp = pid;
+
+ return (0);
+}
+
+/*
+ * Wait for a process on a timeout, and if the timeout expires, kill the
+ * process. Test each second rather than waiting the full timeout at once to
+ * minimize the amount of time spent hanging around unnecessarily.
+ */
+static int
+wait_and_timeout(pid_t pid, int timeout, int *status, const char *errstr)
+{
+ pid_t wpid;
+ int i;
+
+ /*
+ * Count up to the timeout, but do a non-hanging waitpid() after each
+ * second so we can avoid waiting a lot of extra time.
+ */
+ for (i = 0; i < timeout; i++) {
+ wpid = waitpid(pid, status, WNOHANG);
+ if (wpid < 0) {
+ warn("%s: wait_and_timeout: waitpid %d", errstr, pid);
+ return (-1);
+ }
+
+ if (wpid == pid)
+ return (0);
+
+ sleep(1);
+ }
+
+ wpid = waitpid(pid, status, WNOHANG);
+ if (wpid < 0) {
+ warn("%s: wait_and_timeout: waitpid %d", errstr, pid);
+ return (-1);
+ }
+
+ if (wpid == pid)
+ return (0);
+
+ if (kill(pid, SIGTERM) < 0) {
+ warn("%s: wait_and_timeout: kill %d", errstr, pid);
+ return (-1);
+ }
+
+ wpid = waitpid(pid, status, 0);
+ if (wpid < 0) {
+ warn("%s: wait_and_timeout: waitpid %d", errstr, pid);
+ return (-1);
+ }
+
+ if (wpid != pid) {
+ warn("%s: waitpid: returned %d not %d", errstr, wpid, pid);
+ return (-1);
+ }
+
+ warnx("%s: process blocked", errstr);
+ return (-1);
+}
+
+static int
+non_blocking_open_reader(void)
+{
+ int fd;
+
+ fd = open("testfifo", O_RDONLY | O_NONBLOCK);
+ if (fd < 0)
+ return (errno);
+ close(fd);
+
+ return (0);
+}
+
+static int
+non_blocking_open_writer(void)
+{
+ int fd;
+
+ fd = open("testfifo", O_WRONLY | O_NONBLOCK);
+ if (fd < 0)
+ return (errno);
+ close(fd);
+
+ return (0);
+}
+
+static int
+blocking_open_reader(void)
+{
+ int fd;
+
+ fd = open("testfifo", O_RDONLY);
+ if (fd < 0)
+ return (errno);
+ close(fd);
+
+ return (0);
+}
+
+static int
+blocking_open_writer(void)
+{
+ int fd;
+
+ fd = open("testfifo", O_WRONLY);
+ if (fd < 0)
+ return (errno);
+ close(fd);
+
+ return (0);
+}
+
+static void
+test_blocking_reader(void)
+{
+ pid_t reader_pid, writer_pid, wpid;
+ int error, status;
+
+ if (mkfifo("testfifo", 0600) < 0)
+ err(-1, "test_blocking_reader: mkfifo: testfifo");
+
+ /*
+ * Block a process in opening the fifo.
+ */
+ if (run_in_process(blocking_open_reader, &reader_pid,
+ "test_blocking_reader: blocking_open_reader") < 0) {
+ (void)unlink("testfifo");
+ exit(-1);
+ }
+
+ /*
+ * Test that it blocked.
+ */
+ sleep(5);
+ wpid = waitpid(reader_pid, &status, WNOHANG);
+ if (wpid < 0) {
+ error = errno;
+ (void)unlink("testfifo");
+ errno = error;
+ err(-1, "test_blocking_reader: waitpid %d", reader_pid);
+ }
+
+ if (wpid != 0 && wpid != reader_pid) {
+ (void)unlink("testfifo");
+ errx(-1, "test_blocking_reader: waitpid %d returned %d",
+ reader_pid, wpid);
+ }
+
+ if (wpid == reader_pid) {
+ (void)unlink("testfifo");
+ errx(-1, "test_blocking_reader: blocking child didn't "
+ "block");
+ }
+
+ /*
+ * Unblock the blocking reader.
+ */
+ if (run_in_process(blocking_open_writer, &writer_pid,
+ "test_blocking_reader: blocking_open_writer") < 0) {
+ (void)unlink("testfifo");
+ (void)kill(reader_pid, SIGTERM);
+ (void)waitpid(reader_pid, &status, 0);
+ exit(-1);
+ }
+
+ /*
+ * Make sure both processes exited quickly (<1 second) to make sure
+ * they didn't block, and GC.
+ */
+ if (wait_and_timeout(reader_pid, 1, &status,
+ "test_blocking_reader: blocking_open_reader") < 0) {
+ (void)unlink("testinfo");
+ (void)kill(reader_pid, SIGTERM);
+ (void)kill(writer_pid, SIGTERM);
+ exit(-1);
+ }
+
+ if (wait_and_timeout(writer_pid, 1, &status,
+ "test_blocking_reader: blocking_open_writer") < 0) {
+ (void)unlink("testinfo");
+ (void)kill(writer_pid, SIGTERM);
+ exit(-1);
+ }
+
+ if (unlink("testfifo") < 0)
+ err(-1, "test_blocking_reader: unlink: testfifo");
+}
+static void
+test_blocking_writer(void)
+{
+ pid_t reader_pid, writer_pid, wpid;
+ int error, status;
+
+ if (mkfifo("testfifo", 0600) < 0)
+ err(-1, "test_blocking_writer: mkfifo: testfifo");
+
+ /*
+ * Block a process in opening the fifo.
+ */
+ if (run_in_process(blocking_open_writer, &writer_pid,
+ "test_blocking_writer: blocking_open_writer") < 0) {
+ (void)unlink("testfifo");
+ exit(-1);
+ }
+
+ /*
+ * Test that it blocked.
+ */
+ sleep(5);
+ wpid = waitpid(writer_pid, &status, WNOHANG);
+ if (wpid < 0) {
+ error = errno;
+ (void)unlink("testfifo");
+ errno = error;
+ err(-1, "test_blocking_writer: waitpid %d", writer_pid);
+ }
+
+ if (wpid != 0 && wpid != writer_pid) {
+ (void)unlink("testfifo");
+ errx(-1, "test_blocking_writer: waitpid %d returned %d",
+ writer_pid, wpid);
+ }
+
+ if (wpid == writer_pid) {
+ (void)unlink("testfifo");
+ errx(-1, "test_blocking_writer: blocking child didn't "
+ "block");
+ }
+
+ /*
+ * Unblock the blocking writer.
+ */
+ if (run_in_process(blocking_open_reader, &reader_pid,
+ "test_blocking_writer: blocking_open_reader") < 0) {
+ (void)unlink("testfifo");
+ (void)kill(writer_pid, SIGTERM);
+ (void)waitpid(writer_pid, &status, 0);
+ exit(-1);
+ }
+
+ /*
+ * Make sure both processes exited quickly (<1 second) to make sure
+ * they didn't block, and GC.
+ */
+ if (wait_and_timeout(writer_pid, 1, &status,
+ "test_blocking_writer: blocking_open_writer") < 0) {
+ (void)unlink("testinfo");
+ (void)kill(writer_pid, SIGTERM);
+ (void)kill(reader_pid, SIGTERM);
+ (void)waitpid(writer_pid, &status, 0);
+ (void)waitpid(reader_pid, &status, 0);
+ exit(-1);
+ }
+
+ if (wait_and_timeout(reader_pid, 1, &status,
+ "test_blocking_writer: blocking_open_reader") < 0) {
+ (void)unlink("testinfo");
+ (void)kill(reader_pid, SIGTERM);
+ (void)waitpid(reader_pid, &status, 0);
+ exit(-1);
+ }
+
+ if (unlink("testfifo") < 0)
+ err(-1, "test_blocking_writer: unlink: testfifo");
+}
+
+static void
+test_non_blocking_reader(void)
+{
+ int status;
+ pid_t pid;
+
+ if (mkfifo("testfifo", 0600) < 0)
+ err(-1, "test_non_blocking_reader: mkfifo: testfifo");
+
+ if (run_in_process(non_blocking_open_reader, &pid,
+ "test_non_blocking_reader: non_blocking_open_reader") < 0) {
+ (void)unlink("testfifo");
+ exit(-1);
+ }
+
+ status = -1;
+ if (wait_and_timeout(pid, 5, &status,
+ "test_non_blocking_reader: non_blocking_open_reader") < 0) {
+ (void)unlink("testfifo");
+ exit(-1);
+ }
+
+ if (WEXITSTATUS(status) != 0) {
+ (void)unlink("testfifo");
+ errno = WEXITSTATUS(status);
+ err(-1, "test_non_blocking_reader: "
+ "non_blocking_open_reader: open: testfifo");
+ }
+
+ if (unlink("testfifo") < 0)
+ err(-1, "test_non_blocking_reader: unlink: testfifo");
+}
+
+static void
+test_non_blocking_writer(void)
+{
+ int status;
+ pid_t pid;
+
+ if (mkfifo("testfifo", 0600) < 0)
+ err(-1, "test_non_blocking_writer: mkfifo: testfifo");
+
+ if (run_in_process(non_blocking_open_writer, &pid,
+ "test_non_blocking_writer: non_blocking_open_writer") < 0) {
+ (void)unlink("testfifo");
+ exit(-1);
+ }
+
+ status = -1;
+ if (wait_and_timeout(pid, 5, &status,
+ "test_non_blocking_writer: non_blocking_open_writer") < 0) {
+ (void)unlink("testfifo");
+ exit(-1);
+ }
+
+ if (WEXITSTATUS(status) != ENXIO) {
+ (void)unlink("testfifo");
+
+ errno = WEXITSTATUS(status);
+ if (errno == 0)
+ errx(-1, "test_non_blocking_writer: "
+ "non_blocking_open_writer: open succeeded");
+ err(-1, "test_non_blocking_writer: "
+ "non_blocking_open_writer: open: testfifo");
+ }
+
+ if (unlink("testfifo") < 0)
+ err(-1, "test_non_blocking_writer: unlink: testfifo");
+}
+
+int
+main(void)
+{
+
+ if (geteuid() != 0)
+ errx(-1, "must be run as root");
+
+ strcpy(temp_dir, "fifo_open.XXXXXXXXXXX");
+ if (mkdtemp(temp_dir) == NULL)
+ err(-1, "mkdtemp");
+ if (chdir(temp_dir) < 0)
+ err(-1, "chdir: %s", temp_dir);
+ atexit(atexit_temp_dir);
+
+ test_non_blocking_reader();
+ test_non_blocking_writer();
+
+ test_blocking_reader();
+ test_blocking_writer();
+
+ return (0);
+}
diff --git a/tests/sys/file/Makefile b/tests/sys/file/Makefile
new file mode 100644
index 000000000000..f80d1b271b85
--- /dev/null
+++ b/tests/sys/file/Makefile
@@ -0,0 +1,22 @@
+TESTSDIR= ${TESTSBASE}/sys/file
+
+BINDIR= ${TESTSDIR}
+
+ATF_TESTS_C+= path_test
+TAP_TESTS_C+= closefrom_test
+TAP_TESTS_C+= dup_test
+TAP_TESTS_C+= fcntlflags_test
+TAP_TESTS_SH+= flock_test
+PLAIN_TESTS_C+= ftruncate_test
+PLAIN_TESTS_C+= newfileops_on_fork_test
+ATF_TESTS_C+= fspacectl_test
+
+PROGS+= flock_helper
+
+LIBADD.closefrom_test= util
+
+LIBADD.flock_helper= pthread
+
+LIBADD.newfileops_on_fork_test= pthread
+
+.include <bsd.test.mk>
diff --git a/tests/sys/file/Makefile.depend b/tests/sys/file/Makefile.depend
new file mode 100644
index 000000000000..a4ed9f81d5f5
--- /dev/null
+++ b/tests/sys/file/Makefile.depend
@@ -0,0 +1,18 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libthr \
+ lib/libutil \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/file/closefrom_test.c b/tests/sys/file/closefrom_test.c
new file mode 100644
index 000000000000..7dccf858c772
--- /dev/null
+++ b/tests/sys/file/closefrom_test.c
@@ -0,0 +1,393 @@
+/*-
+ * Copyright (c) 2009 Hudson River Trading LLC
+ * Written by: John H. Baldwin <jhb@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+/*
+ * Regression tests for the closefrom(2) system call.
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/user.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <paths.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+struct shared_info {
+ int failed;
+ char tag[64];
+ char message[0];
+};
+
+static int test = 1;
+
+static void
+ok(const char *descr)
+{
+
+ printf("ok %d - %s\n", test, descr);
+ test++;
+}
+
+static void
+fail(const char *descr, const char *fmt, ...)
+{
+ va_list ap;
+
+ printf("not ok %d - %s", test, descr);
+ test++;
+ if (fmt) {
+ va_start(ap, fmt);
+ printf(" # ");
+ vprintf(fmt, ap);
+ va_end(ap);
+ }
+ printf("\n");
+ exit(1);
+}
+
+#define fail_err(descr) fail((descr), "%s", strerror(errno))
+
+static void
+cok(struct shared_info *info, const char *descr)
+{
+
+ info->failed = 0;
+ strlcpy(info->tag, descr, sizeof(info->tag));
+ exit(0);
+}
+
+static void
+cfail(struct shared_info *info, const char *descr, const char *fmt, ...)
+{
+ va_list ap;
+
+ info->failed = 1;
+ strlcpy(info->tag, descr, sizeof(info->tag));
+ if (fmt) {
+ va_start(ap, fmt);
+ vsprintf(info->message, fmt, ap);
+ va_end(ap);
+ }
+ exit(0);
+}
+
+#define cfail_err(info, descr) cfail((info), (descr), "%s", strerror(errno))
+
+/*
+ * Use kinfo_getfile() to fetch the list of file descriptors and figure out
+ * the highest open file descriptor.
+ */
+static int
+highest_fd(void)
+{
+ struct kinfo_file *kif;
+ int cnt, i, highest;
+
+ kif = kinfo_getfile(getpid(), &cnt);
+ if (kif == NULL)
+ fail_err("kinfo_getfile");
+ highest = INT_MIN;
+ for (i = 0; i < cnt; i++)
+ if (kif[i].kf_fd > highest)
+ highest = kif[i].kf_fd;
+ free(kif);
+ return (highest);
+}
+
+static int
+devnull(void)
+{
+ int fd;
+
+ fd = open(_PATH_DEVNULL, O_RDONLY);
+ if (fd < 0)
+ fail_err("open(\" "_PATH_DEVNULL" \")");
+ return (fd);
+}
+
+int
+main(void)
+{
+ struct shared_info *info;
+ pid_t pid;
+ int fd, flags, i, start;
+
+ printf("1..22\n");
+
+ /* We'd better start up with fd's 0, 1, and 2 open. */
+ start = devnull();
+ if (start == -1)
+ fail("open", "bad descriptor %d", start);
+ ok("open");
+
+ /* Make sure highest_fd() works. */
+ fd = highest_fd();
+ if (start != fd)
+ fail("highest_fd", "bad descriptor %d != %d", start, fd);
+ ok("highest_fd");
+
+ /* Try to use closefrom() for just closing fd 3. */
+ closefrom(start + 1);
+ fd = highest_fd();
+ if (fd != start)
+ fail("closefrom", "highest fd %d", fd);
+ ok("closefrom");
+
+ /* Eat up 16 descriptors. */
+ for (i = 0; i < 16; i++)
+ (void)devnull();
+ fd = highest_fd();
+ if (fd != start + 16)
+ fail("open 16", "highest fd %d", fd);
+ ok("open 16");
+
+ /* Close half of them. */
+ closefrom(11);
+ fd = highest_fd();
+ if (fd != 10)
+ fail("closefrom", "highest fd %d", fd);
+ ok("closefrom");
+
+ /* Explicitly close descriptors 6 and 8 to create holes. */
+ if (close(6) < 0 || close(8) < 0)
+ fail_err("close2 ");
+ ok("close 2");
+
+ /* Verify that close on 6 and 8 fails with EBADF. */
+ if (close(6) == 0)
+ fail("close(6)", "did not fail");
+ if (errno != EBADF)
+ fail_err("close(6)");
+ ok("close(6)");
+ if (close(8) == 0)
+ fail("close(8)", "did not fail");
+ if (errno != EBADF)
+ fail_err("close(8)");
+ ok("close(8)");
+
+ /* Close from 4 on. */
+ closefrom(4);
+ fd = highest_fd();
+ if (fd != 3)
+ fail("closefrom", "highest fd %d", fd);
+ ok("closefrom");
+
+ /* Allocate a small SHM region for IPC with our child. */
+ info = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_ANON |
+ MAP_SHARED, -1, 0);
+ if (info == MAP_FAILED)
+ fail_err("mmap");
+ ok("mmap");
+
+ /* Fork a child process to test closefrom(0). */
+ pid = fork();
+ if (pid < 0)
+ fail_err("fork");
+ if (pid == 0) {
+ /* Child. */
+ closefrom(0);
+ fd = highest_fd();
+ if (fd >= 0)
+ cfail(info, "closefrom(0)", "highest fd %d", fd);
+ cok(info, "closefrom(0)");
+ }
+ if (wait(NULL) < 0)
+ fail_err("wait");
+ if (info->failed)
+ fail(info->tag, "%s", info->message);
+ ok(info->tag);
+
+ /* Fork a child process to test closefrom(-1). */
+ pid = fork();
+ if (pid < 0)
+ fail_err("fork");
+ if (pid == 0) {
+ /* Child. */
+ closefrom(-1);
+ fd = highest_fd();
+ if (fd >= 0)
+ cfail(info, "closefrom(-1)", "highest fd %d", fd);
+ cok(info, "closefrom(-1)");
+ }
+ if (wait(NULL) < 0)
+ fail_err("wait");
+ if (info->failed)
+ fail(info->tag, "%s", info->message);
+ ok(info->tag);
+
+ /* Dup stdout to 6. */
+ if (dup2(1, 6) < 0)
+ fail_err("dup2");
+ fd = highest_fd();
+ if (fd != 6)
+ fail("dup2", "highest fd %d", fd);
+ ok("dup2");
+
+ /* Do a closefrom() starting in a hole. */
+ closefrom(4);
+ fd = highest_fd();
+ if (fd != 3)
+ fail("closefrom", "highest fd %d", fd);
+ ok("closefrom");
+
+ /* Do a closefrom() beyond our highest open fd. */
+ closefrom(32);
+ fd = highest_fd();
+ if (fd != 3)
+ fail("closefrom", "highest fd %d", fd);
+ ok("closefrom");
+
+ /* Chew up another 8 fd */
+ for (i = 0; i < 8; i++)
+ (void)devnull();
+ fd = highest_fd();
+ start = fd - 7;
+
+ /* close_range() a hole in the middle */
+ close_range(start + 3, start + 5, 0);
+ for (i = start + 3; i < start + 6; ++i) {
+ if (close(i) == 0 || errno != EBADF) {
+ --i;
+ break;
+ }
+ }
+ if (i != start + 6)
+ fail("close_range", "failed to close at %d in %d - %d", i + 1,
+ start + 3, start + 6);
+ ok("close_range");
+
+ /* close_range from the middle of the hole */
+ close_range(start + 4, start + 6, 0);
+ if ((i = highest_fd()) != fd)
+ fail("close_range", "highest fd %d", i);
+ ok("close_range");
+
+ /* close_range to the end; effectively closefrom(2) */
+ close_range(start + 3, ~0L, 0);
+ if ((i = highest_fd()) != start + 2)
+ fail("close_range", "highest fd %d", i);
+ ok("close_range");
+
+ /* Now close the rest */
+ close_range(start, start + 4, 0);
+ fd = highest_fd();
+ if (fd != 3)
+ fail("close_range", "highest fd %d", fd);
+ ok("close_range");
+
+ /* Fork a child process to test closefrom(0) twice. */
+ pid = fork();
+ if (pid < 0)
+ fail_err("fork");
+ if (pid == 0) {
+ /* Child. */
+ closefrom(0);
+ closefrom(0);
+ cok(info, "closefrom(0)");
+ }
+ if (wait(NULL) < 0)
+ fail_err("wait");
+ if (info->failed)
+ fail(info->tag, "%s", info->message);
+ ok(info->tag);
+
+ /* test CLOSE_RANGE_CLOEXEC */
+ for (i = 0; i < 8; i++)
+ (void)devnull();
+ fd = highest_fd();
+ start = fd - 8;
+ if (close_range(start + 1, start + 4, CLOSE_RANGE_CLOEXEC) < 0)
+ fail_err("close_range(..., CLOSE_RANGE_CLOEXEC)");
+ flags = fcntl(start, F_GETFD);
+ if (flags < 0)
+ fail_err("fcntl(.., F_GETFD)");
+ if ((flags & FD_CLOEXEC) != 0)
+ fail("close_range", "CLOSE_RANGE_CLOEXEC set close-on-exec "
+ "when it should not have on fd %d", start);
+ for (i = start + 1; i <= start + 4; i++) {
+ flags = fcntl(i, F_GETFD);
+ if (flags < 0)
+ fail_err("fcntl(.., F_GETFD)");
+ if ((flags & FD_CLOEXEC) == 0)
+ fail("close_range", "CLOSE_RANGE_CLOEXEC did not set "
+ "close-on-exec on fd %d", i);
+ }
+ for (; i < start + 8; i++) {
+ flags = fcntl(i, F_GETFD);
+ if (flags < 0)
+ fail_err("fcntl(.., F_GETFD)");
+ if ((flags & FD_CLOEXEC) != 0)
+ fail("close_range", "CLOSE_RANGE_CLOEXEC set close-on-exec "
+ "when it should not have on fd %d", i);
+ }
+ if (close_range(start, start + 8, 0) < 0)
+ fail_err("close_range");
+ ok("close_range(..., CLOSE_RANGE_CLOEXEC)");
+
+ /* test CLOSE_RANGE_CLOFORK */
+ for (i = 0; i < 8; i++)
+ (void)devnull();
+ fd = highest_fd();
+ start = fd - 8;
+ if (close_range(start + 1, start + 4, CLOSE_RANGE_CLOFORK) < 0)
+ fail_err("close_range(..., CLOSE_RANGE_CLOFORK)");
+ flags = fcntl(start, F_GETFD);
+ if (flags < 0)
+ fail_err("fcntl(.., F_GETFD)");
+ if ((flags & FD_CLOFORK) != 0)
+ fail("close_range", "CLOSE_RANGE_CLOFORK set close-on-exec "
+ "when it should not have on fd %d", start);
+ for (i = start + 1; i <= start + 4; i++) {
+ flags = fcntl(i, F_GETFD);
+ if (flags < 0)
+ fail_err("fcntl(.., F_GETFD)");
+ if ((flags & FD_CLOFORK) == 0)
+ fail("close_range", "CLOSE_RANGE_CLOFORK did not set "
+ "close-on-exec on fd %d", i);
+ }
+ for (; i < start + 8; i++) {
+ flags = fcntl(i, F_GETFD);
+ if (flags < 0)
+ fail_err("fcntl(.., F_GETFD)");
+ if ((flags & FD_CLOFORK) != 0)
+ fail("close_range", "CLOSE_RANGE_CLOFORK set close-on-exec "
+ "when it should not have on fd %d", i);
+ }
+ if (close_range(start, start + 8, 0) < 0)
+ fail_err("close_range");
+ ok("close_range(..., CLOSE_RANGE_CLOFORK)");
+
+ return (0);
+}
diff --git a/tests/sys/file/dup_test.c b/tests/sys/file/dup_test.c
new file mode 100644
index 000000000000..455115eda8c8
--- /dev/null
+++ b/tests/sys/file/dup_test.c
@@ -0,0 +1,480 @@
+/*
+ * $OpenBSD: dup2test.c,v 1.3 2003/07/31 21:48:08 deraadt Exp $
+ * $OpenBSD: dup2_self.c,v 1.3 2003/07/31 21:48:08 deraadt Exp $
+ * $OpenBSD: fcntl_dup.c,v 1.2 2003/07/31 21:48:08 deraadt Exp $
+ *
+ * Written by Artur Grabowski <art@openbsd.org> 2002 Public Domain.
+ */
+
+/*
+ * Test #1: check if dup(2) works.
+ * Test #2: check if dup2(2) works.
+ * Test #3: check if dup2(2) returned a fd we asked for.
+ * Test #4: check if dup2(2) cleared close-on-exec flag for duped fd.
+ * Test #5: check if dup2(2) allows to dup fd to itself.
+ * Test #6: check if dup2(2) returned a fd we asked for.
+ * Test #7: check if dup2(2) did not clear close-on-exec flag for duped fd.
+ * Test #8: check if fcntl(F_DUPFD) works.
+ * Test #9: check if fcntl(F_DUPFD) cleared close-on-exec flag for duped fd.
+ * Test #10: check if dup2() to a fd > current maximum number of open files
+ * limit work.
+ * Test #11: check if fcntl(F_DUP2FD) works.
+ * Test #12: check if fcntl(F_DUP2FD) returned a fd we asked for.
+ * Test #13: check if fcntl(F_DUP2FD) cleared close-on-exec flag for duped fd.
+ * Test #14: check if fcntl(F_DUP2FD) allows to dup fd to itself.
+ * Test #15: check if fcntl(F_DUP2FD) returned a fd we asked for.
+ * Test #16: check if fcntl(F_DUP2FD) did not clear close-on-exec flag for
+ * duped fd.
+ * Test #17: check if fcntl(F_DUP2FD) to a fd > current maximum number of open
+ * files limit work.
+ * Test #18: check if fcntl(F_DUPFD_CLOEXEC) works.
+ * Test #19: check if fcntl(F_DUPFD_CLOEXEC) set close-on-exec flag for duped
+ * fd.
+ * Test #20: check if fcntl(F_DUP2FD_CLOEXEC) works.
+ * Test #21: check if fcntl(F_DUP2FD_CLOEXEC) returned a fd we asked for.
+ * Test #22: check if fcntl(F_DUP2FD_CLOEXEC) set close-on-exec flag for duped
+ * fd.
+ * Test #23: check if fcntl(F_DUP2FD_CLOEXEC) to a fd > current maximum number
+ * of open files limit work.
+ * Test #24: check if dup3(O_CLOEXEC) works.
+ * Test #25: check if dup3(O_CLOEXEC) returned a fd we asked for.
+ * Test #26: check if dup3(O_CLOEXEC) set close-on-exec flag for duped fd.
+ * Test #27: check if dup3(0) works.
+ * Test #28: check if dup3(0) returned a fd we asked for.
+ * Test #29: check if dup3(0) cleared close-on-exec flag for duped fd.
+ * Test #30: check if dup3(O_CLOEXEC) fails if oldfd == newfd.
+ * Test #31: check if dup3(0) fails if oldfd == newfd.
+ * Test #32: check if dup3(O_CLOEXEC) to a fd > current maximum number of
+ * open files limit work.
+ * Tests #33-43 : Same as #18-26, 30 & 32 with O_CLOFORK instead of O_CLOEXEC,
+ * except F_DUP2FD_CLOEXEC.
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static int getafile(void);
+
+static int
+getafile(void)
+{
+ int fd;
+
+ char temp[] = "/tmp/dup2XXXXXXXXX";
+ if ((fd = mkstemp(temp)) < 0)
+ err(1, "mkstemp");
+ remove(temp);
+ if (ftruncate(fd, 1024) != 0)
+ err(1, "ftruncate");
+ return (fd);
+}
+
+int
+main(int __unused argc, char __unused *argv[])
+{
+ struct rlimit rlp;
+ int orgfd, fd1, fd2, test = 0;
+
+ orgfd = getafile();
+
+ printf("1..43\n");
+
+ /* If dup(2) ever work? */
+ if ((fd1 = dup(orgfd)) < 0)
+ err(1, "dup");
+ printf("ok %d - dup(2) works\n", ++test);
+
+ /* Set close-on-exec */
+ if (fcntl(fd1, F_SETFD, 1) != 0)
+ err(1, "fcntl(F_SETFD)");
+
+ /* If dup2(2) ever work? */
+ if ((fd2 = dup2(fd1, fd1 + 1)) < 0)
+ err(1, "dup2");
+ printf("ok %d - dup2(2) works\n", ++test);
+
+ /* Do we get the right fd? */
+ ++test;
+ if (fd2 != fd1 + 1)
+ printf("no ok %d - dup2(2) didn't give us the right fd\n",
+ test);
+ else
+ printf("ok %d - dup2(2) returned a correct fd\n", test);
+
+ /* Was close-on-exec cleared? */
+ ++test;
+ if (fcntl(fd2, F_GETFD) != 0)
+ printf("not ok %d - dup2(2) didn't clear close-on-exec\n",
+ test);
+ else
+ printf("ok %d - dup2(2) cleared close-on-exec\n", test);
+
+ /*
+ * Dup to itself.
+ *
+ * We're testing a small tweak in dup2 semantics.
+ * Normally dup and dup2 will clear the close-on-exec
+ * flag on the new fd (which appears to be an implementation
+ * mistake from start and not some planned behavior).
+ * In today's implementations of dup and dup2 we have to make
+ * an effort to really clear that flag. But all tested
+ * implementations of dup2 have another tweak. If we
+ * dup2(old, new) when old == new, the syscall short-circuits
+ * and returns early (because there is no need to do all the
+ * work (and there is a risk for serious mistakes)).
+ * So although the docs say that dup2 should "take 'old',
+ * close 'new' perform a dup(2) of 'old' into 'new'"
+ * the docs are not really followed because close-on-exec
+ * is not cleared on 'new'.
+ *
+ * Since everyone has this bug, we pretend that this is
+ * the way it is supposed to be and test here that it really
+ * works that way.
+ *
+ * This is a fine example on where two separate implementation
+ * fuckups take out each other and make the end-result the way
+ * it was meant to be.
+ */
+ if ((fd2 = dup2(fd1, fd1)) < 0)
+ err(1, "dup2");
+ printf("ok %d - dup2(2) to itself works\n", ++test);
+
+ /* Do we get the right fd? */
+ ++test;
+ if (fd2 != fd1)
+ printf("not ok %d - dup2(2) didn't give us the right fd\n",
+ test);
+ else
+ printf("ok %d - dup2(2) to itself returned a correct fd\n",
+ test);
+
+ /* Was close-on-exec cleared? */
+ ++test;
+ if (fcntl(fd2, F_GETFD) == 0)
+ printf("not ok %d - dup2(2) cleared close-on-exec\n", test);
+ else
+ printf("ok %d - dup2(2) didn't clear close-on-exec\n", test);
+
+ /* Does fcntl(F_DUPFD) work? */
+ if ((fd2 = fcntl(fd1, F_DUPFD, 10)) < 0)
+ err(1, "fcntl(F_DUPFD)");
+ if (fd2 < 10)
+ printf("not ok %d - fcntl(F_DUPFD) returned wrong fd %d\n",
+ ++test, fd2);
+ else
+ printf("ok %d - fcntl(F_DUPFD) works\n", ++test);
+
+ /* Was close-on-exec cleared? */
+ ++test;
+ if (fcntl(fd2, F_GETFD) != 0)
+ printf(
+ "not ok %d - fcntl(F_DUPFD) didn't clear close-on-exec\n",
+ test);
+ else
+ printf("ok %d - fcntl(F_DUPFD) cleared close-on-exec\n", test);
+
+ ++test;
+ if (getrlimit(RLIMIT_NOFILE, &rlp) < 0)
+ err(1, "getrlimit");
+ if ((fd2 = dup2(fd1, rlp.rlim_cur + 1)) >= 0)
+ printf("not ok %d - dup2(2) bypassed NOFILE limit\n", test);
+ else
+ printf("ok %d - dup2(2) didn't bypass NOFILE limit\n", test);
+
+ /* If fcntl(F_DUP2FD) ever work? */
+ if ((fd2 = fcntl(fd1, F_DUP2FD, fd1 + 1)) < 0)
+ err(1, "fcntl(F_DUP2FD)");
+ printf("ok %d - fcntl(F_DUP2FD) works\n", ++test);
+
+ /* Do we get the right fd? */
+ ++test;
+ if (fd2 != fd1 + 1)
+ printf(
+ "no ok %d - fcntl(F_DUP2FD) didn't give us the right fd\n",
+ test);
+ else
+ printf("ok %d - fcntl(F_DUP2FD) returned a correct fd\n",
+ test);
+
+ /* Was close-on-exec cleared? */
+ ++test;
+ if (fcntl(fd2, F_GETFD) != 0)
+ printf(
+ "not ok %d - fcntl(F_DUP2FD) didn't clear close-on-exec\n",
+ test);
+ else
+ printf("ok %d - fcntl(F_DUP2FD) cleared close-on-exec\n",
+ test);
+
+ /* Dup to itself */
+ if ((fd2 = fcntl(fd1, F_DUP2FD, fd1)) < 0)
+ err(1, "fcntl(F_DUP2FD)");
+ printf("ok %d - fcntl(F_DUP2FD) to itself works\n", ++test);
+
+ /* Do we get the right fd? */
+ ++test;
+ if (fd2 != fd1)
+ printf(
+ "not ok %d - fcntl(F_DUP2FD) didn't give us the right fd\n",
+ test);
+ else
+ printf(
+ "ok %d - fcntl(F_DUP2FD) to itself returned a correct fd\n",
+ test);
+
+ /* Was close-on-exec cleared? */
+ ++test;
+ if (fcntl(fd2, F_GETFD) == 0)
+ printf("not ok %d - fcntl(F_DUP2FD) cleared close-on-exec\n",
+ test);
+ else
+ printf("ok %d - fcntl(F_DUP2FD) didn't clear close-on-exec\n",
+ test);
+
+ ++test;
+ if (getrlimit(RLIMIT_NOFILE, &rlp) < 0)
+ err(1, "getrlimit");
+ if ((fd2 = fcntl(fd1, F_DUP2FD, rlp.rlim_cur + 1)) >= 0)
+ printf("not ok %d - fcntl(F_DUP2FD) bypassed NOFILE limit\n",
+ test);
+ else
+ printf("ok %d - fcntl(F_DUP2FD) didn't bypass NOFILE limit\n",
+ test);
+
+ /* Does fcntl(F_DUPFD_CLOEXEC) work? */
+ if ((fd2 = fcntl(fd1, F_DUPFD_CLOEXEC, 10)) < 0)
+ err(1, "fcntl(F_DUPFD_CLOEXEC)");
+ if (fd2 < 10)
+ printf("not ok %d - fcntl(F_DUPFD_CLOEXEC) returned wrong fd %d\n",
+ ++test, fd2);
+ else
+ printf("ok %d - fcntl(F_DUPFD_CLOEXEC) works\n", ++test);
+
+ /* Was close-on-exec cleared? */
+ ++test;
+ if (fcntl(fd2, F_GETFD) != 1)
+ printf(
+ "not ok %d - fcntl(F_DUPFD_CLOEXEC) didn't set close-on-exec\n",
+ test);
+ else
+ printf("ok %d - fcntl(F_DUPFD_CLOEXEC) set close-on-exec\n",
+ test);
+
+ /* If fcntl(F_DUP2FD_CLOEXEC) ever work? */
+ if ((fd2 = fcntl(fd1, F_DUP2FD_CLOEXEC, fd1 + 1)) < 0)
+ err(1, "fcntl(F_DUP2FD_CLOEXEC)");
+ printf("ok %d - fcntl(F_DUP2FD_CLOEXEC) works\n", ++test);
+
+ /* Do we get the right fd? */
+ ++test;
+ if (fd2 != fd1 + 1)
+ printf(
+ "no ok %d - fcntl(F_DUP2FD_CLOEXEC) didn't give us the right fd\n",
+ test);
+ else
+ printf("ok %d - fcntl(F_DUP2FD_CLOEXEC) returned a correct fd\n",
+ test);
+
+ /* Was close-on-exec set? */
+ ++test;
+ if (fcntl(fd2, F_GETFD) != FD_CLOEXEC)
+ printf(
+ "not ok %d - fcntl(F_DUP2FD_CLOEXEC) didn't set close-on-exec\n",
+ test);
+ else
+ printf("ok %d - fcntl(F_DUP2FD_CLOEXEC) set close-on-exec\n",
+ test);
+
+ /*
+ * It is unclear what F_DUP2FD_CLOEXEC should do when duplicating a
+ * file descriptor onto itself.
+ */
+
+ ++test;
+ if (getrlimit(RLIMIT_NOFILE, &rlp) < 0)
+ err(1, "getrlimit");
+ if ((fd2 = fcntl(fd1, F_DUP2FD_CLOEXEC, rlp.rlim_cur + 1)) >= 0)
+ printf("not ok %d - fcntl(F_DUP2FD_CLOEXEC) bypassed NOFILE limit\n",
+ test);
+ else
+ printf("ok %d - fcntl(F_DUP2FD_CLOEXEC) didn't bypass NOFILE limit\n",
+ test);
+
+ /* Does dup3(O_CLOEXEC) ever work? */
+ if ((fd2 = dup3(fd1, fd1 + 1, O_CLOEXEC)) < 0)
+ err(1, "dup3(O_CLOEXEC)");
+ printf("ok %d - dup3(O_CLOEXEC) works\n", ++test);
+
+ /* Do we get the right fd? */
+ ++test;
+ if (fd2 != fd1 + 1)
+ printf(
+ "no ok %d - dup3(O_CLOEXEC) didn't give us the right fd\n",
+ test);
+ else
+ printf("ok %d - dup3(O_CLOEXEC) returned a correct fd\n",
+ test);
+
+ /* Was close-on-exec set? */
+ ++test;
+ if (fcntl(fd2, F_GETFD) != FD_CLOEXEC)
+ printf(
+ "not ok %d - dup3(O_CLOEXEC) didn't set close-on-exec\n",
+ test);
+ else
+ printf("ok %d - dup3(O_CLOEXEC) set close-on-exec\n",
+ test);
+
+ /* Does dup3(0) ever work? */
+ if ((fd2 = dup3(fd1, fd1 + 1, 0)) < 0)
+ err(1, "dup3(0)");
+ printf("ok %d - dup3(0) works\n", ++test);
+
+ /* Do we get the right fd? */
+ ++test;
+ if (fd2 != fd1 + 1)
+ printf(
+ "no ok %d - dup3(0) didn't give us the right fd\n",
+ test);
+ else
+ printf("ok %d - dup3(0) returned a correct fd\n",
+ test);
+
+ /* Was close-on-exec cleared? */
+ ++test;
+ if (fcntl(fd2, F_GETFD) != 0)
+ printf(
+ "not ok %d - dup3(0) didn't clear close-on-exec\n",
+ test);
+ else
+ printf("ok %d - dup3(0) cleared close-on-exec\n",
+ test);
+
+ /* dup3() does not allow duplicating to the same fd */
+ ++test;
+ if (dup3(fd1, fd1, O_CLOEXEC) != -1)
+ printf(
+ "not ok %d - dup3(fd1, fd1, O_CLOEXEC) succeeded\n", test);
+ else
+ printf("ok %d - dup3(fd1, fd1, O_CLOEXEC) failed\n", test);
+
+ ++test;
+ if (dup3(fd1, fd1, 0) != -1)
+ printf(
+ "not ok %d - dup3(fd1, fd1, 0) succeeded\n", test);
+ else
+ printf("ok %d - dup3(fd1, fd1, 0) failed\n", test);
+
+ ++test;
+ if (getrlimit(RLIMIT_NOFILE, &rlp) < 0)
+ err(1, "getrlimit");
+ if ((fd2 = dup3(fd1, rlp.rlim_cur + 1, O_CLOEXEC)) >= 0)
+ printf("not ok %d - dup3(O_CLOEXEC) bypassed NOFILE limit\n",
+ test);
+ else
+ printf("ok %d - dup3(O_CLOEXEC) didn't bypass NOFILE limit\n",
+ test);
+
+ /* Does fcntl(F_DUPFD_CLOFORK) work? */
+ if ((fd2 = fcntl(fd1, F_DUPFD_CLOFORK, 10)) < 0)
+ err(1, "fcntl(F_DUPFD_CLOFORK)");
+ if (fd2 < 10)
+ printf("not ok %d - fcntl(F_DUPFD_CLOFORK) returned wrong fd %d\n",
+ ++test, fd2);
+ else
+ printf("ok %d - fcntl(F_DUPFD_CLOFORK) works\n", ++test);
+
+ /* Was close-on-fork cleared? */
+ ++test;
+ if (fcntl(fd2, F_GETFD) != FD_CLOFORK)
+ printf(
+ "not ok %d - fcntl(F_DUPFD_CLOFORK) didn't set close-on-fork\n",
+ test);
+ else
+ printf("ok %d - fcntl(F_DUPFD_CLOFORK) set close-on-fork\n",
+ test);
+
+ /* Does dup3(O_CLOFORK) ever work? */
+ if ((fd2 = dup3(fd1, fd1 + 1, O_CLOFORK)) < 0)
+ err(1, "dup3(O_CLOFORK)");
+ printf("ok %d - dup3(O_CLOFORK) works\n", ++test);
+
+ /* Do we get the right fd? */
+ ++test;
+ if (fd2 != fd1 + 1)
+ printf(
+ "no ok %d - dup3(O_CLOFORK) didn't give us the right fd\n",
+ test);
+ else
+ printf("ok %d - dup3(O_CLOFORK) returned a correct fd\n",
+ test);
+
+ /* Was close-on-fork set? */
+ ++test;
+ if (fcntl(fd2, F_GETFD) != FD_CLOFORK)
+ printf(
+ "not ok %d - dup3(O_CLOFORK) didn't set close-on-fork\n",
+ test);
+ else
+ printf("ok %d - dup3(O_CLOFORK) set close-on-fork\n",
+ test);
+
+ /* Does dup3(0) ever work? */
+ if ((fd2 = dup3(fd1, fd1 + 1, 0)) < 0)
+ err(1, "dup3(0)");
+ printf("ok %d - dup3(0) works\n", ++test);
+
+ /* Do we get the right fd? */
+ ++test;
+ if (fd2 != fd1 + 1)
+ printf(
+ "no ok %d - dup3(0) didn't give us the right fd\n",
+ test);
+ else
+ printf("ok %d - dup3(0) returned a correct fd\n",
+ test);
+
+ /* Was close-on-fork cleared? */
+ ++test;
+ if (fcntl(fd2, F_GETFD) != 0)
+ printf(
+ "not ok %d - dup3(0) didn't clear close-on-fork\n",
+ test);
+ else
+ printf("ok %d - dup3(0) cleared close-on-fork\n",
+ test);
+
+ /* dup3() does not allow duplicating to the same fd */
+ ++test;
+ if (dup3(fd1, fd1, O_CLOFORK) != -1)
+ printf(
+ "not ok %d - dup3(fd1, fd1, O_CLOFORK) succeeded\n", test);
+ else
+ printf("ok %d - dup3(fd1, fd1, O_CLOFORK) failed\n", test);
+
+ ++test;
+ if (dup3(fd1, fd1, 0) != -1)
+ printf(
+ "not ok %d - dup3(fd1, fd1, 0) succeeded\n", test);
+ else
+ printf("ok %d - dup3(fd1, fd1, 0) failed\n", test);
+
+ ++test;
+ if (getrlimit(RLIMIT_NOFILE, &rlp) < 0)
+ err(1, "getrlimit");
+ if ((fd2 = dup3(fd1, rlp.rlim_cur + 1, O_CLOFORK)) >= 0)
+ printf("not ok %d - dup3(O_CLOFORK) bypassed NOFILE limit\n",
+ test);
+ else
+ printf("ok %d - dup3(O_CLOFORK) didn't bypass NOFILE limit\n",
+ test);
+
+ return (0);
+}
diff --git a/tests/sys/file/fcntlflags_test.c b/tests/sys/file/fcntlflags_test.c
new file mode 100644
index 000000000000..c5026e38c48b
--- /dev/null
+++ b/tests/sys/file/fcntlflags_test.c
@@ -0,0 +1,108 @@
+/*-
+ * Copyright (c) 2013 Jilles Tjoelker
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+/*
+ * O_ACCMODE is currently defined incorrectly. This is what it should be.
+ * Various code depends on the incorrect value.
+ */
+#define CORRECT_O_ACCMODE (O_ACCMODE | O_EXEC)
+
+static int testnum;
+
+static void
+subtests(const char *path, int omode, const char *omodetext)
+{
+ int fd, flags1, flags2, flags3;
+
+ fd = open(path, omode);
+ if (fd == -1)
+ printf("not ok %d - open(\"%s\", %s) failed\n",
+ testnum++, path, omodetext);
+ else
+ printf("ok %d - open(\"%s\", %s) succeeded\n",
+ testnum++, path, omodetext);
+ flags1 = fcntl(fd, F_GETFL);
+ if (flags1 == -1)
+ printf("not ok %d - fcntl(F_GETFL) failed\n", testnum++);
+ else if ((flags1 & CORRECT_O_ACCMODE) == omode)
+ printf("ok %d - fcntl(F_GETFL) gave correct result\n",
+ testnum++);
+ else
+ printf("not ok %d - fcntl(F_GETFL) gave incorrect result "
+ "(%#x & %#x != %#x)\n",
+ testnum++, flags1, CORRECT_O_ACCMODE, omode);
+ if (fcntl(fd, F_SETFL, flags1) == -1)
+ printf("not ok %d - fcntl(F_SETFL) same flags failed\n",
+ testnum++);
+ else
+ printf("ok %d - fcntl(F_SETFL) same flags succeeded\n",
+ testnum++);
+ flags2 = fcntl(fd, F_GETFL);
+ if (flags2 == -1)
+ printf("not ok %d - fcntl(F_GETFL) failed\n", testnum++);
+ else if (flags2 == flags1)
+ printf("ok %d - fcntl(F_GETFL) gave same result\n",
+ testnum++);
+ else
+ printf("not ok %d - fcntl(F_SETFL) caused fcntl(F_GETFL) to "
+ "change from %#x to %#x\n",
+ testnum++, flags1, flags2);
+ if (fcntl(fd, F_SETFL, flags2 | O_NONBLOCK) == -1)
+ printf("not ok %d - fcntl(F_SETFL) O_NONBLOCK failed\n",
+ testnum++);
+ else
+ printf("ok %d - fcntl(F_SETFL) O_NONBLOCK succeeded\n",
+ testnum++);
+ flags3 = fcntl(fd, F_GETFL);
+ if (flags3 == -1)
+ printf("not ok %d - fcntl(F_GETFL) failed\n", testnum++);
+ else if (flags3 == (flags2 | O_NONBLOCK))
+ printf("ok %d - fcntl(F_GETFL) gave expected result\n",
+ testnum++);
+ else
+ printf("not ok %d - fcntl(F_SETFL) gave unexpected result "
+ "(%#x != %#x)\n",
+ testnum++, flags3, flags2 | O_NONBLOCK);
+ (void)close(fd);
+}
+
+int
+main(int argc __unused, char **argv __unused)
+{
+ printf("1..24\n");
+ testnum = 1;
+ subtests("/dev/null", O_RDONLY, "O_RDONLY");
+ subtests("/dev/null", O_WRONLY, "O_WRONLY");
+ subtests("/dev/null", O_RDWR, "O_RDWR");
+ subtests("/bin/sh", O_EXEC, "O_EXEC");
+ return (0);
+}
diff --git a/tests/sys/file/flock_helper.c b/tests/sys/file/flock_helper.c
new file mode 100644
index 000000000000..cb864c90e378
--- /dev/null
+++ b/tests/sys/file/flock_helper.c
@@ -0,0 +1,1596 @@
+/*-
+ * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+ * Authors: Doug Rabson <dfr@rabson.org>
+ * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#ifdef __FreeBSD__
+#include <sys/mount.h>
+#endif
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef __FreeBSD__
+#if __FreeBSD_version >= 800028
+#define HAVE_SYSID
+#endif
+#include <sys/cdefs.h>
+#else
+#ifndef nitems
+#define nitems(x) (sizeof((x)) / sizeof((x)[0]))
+#endif
+
+#ifndef __unused
+#ifdef __GNUC__
+#define __unused __attribute__((__unused__))
+#else
+#define __unused
+#endif
+#endif
+#endif
+
+static int verbose = 0;
+
+static int
+make_file(const char *pathname, off_t sz)
+{
+ struct stat st;
+ const char *template = "/flocktempXXXXXX";
+ size_t len;
+ char *filename;
+ int fd;
+
+ if (stat(pathname, &st) == 0) {
+ if (S_ISREG(st.st_mode)) {
+ fd = open(pathname, O_RDWR);
+ if (fd < 0)
+ err(1, "open(%s)", pathname);
+ if (ftruncate(fd, sz) < 0)
+ err(1, "ftruncate");
+ return (fd);
+ }
+ }
+
+ len = strlen(pathname) + strlen(template) + 1;
+ filename = malloc(len);
+ strcpy(filename, pathname);
+ strcat(filename, template);
+ fd = mkstemp(filename);
+ if (fd < 0)
+ err(1, "mkstemp");
+ if (ftruncate(fd, sz) < 0)
+ err(1, "ftruncate");
+ if (unlink(filename) < 0)
+ err(1, "unlink");
+ free(filename);
+
+ return (fd);
+}
+
+static void
+ignore_alarm(int __unused sig)
+{
+}
+
+static int
+safe_waitpid(pid_t pid)
+{
+ int save_errno;
+ int status;
+
+ save_errno = errno;
+ errno = 0;
+ while (waitpid(pid, &status, 0) != pid) {
+ if (errno == EINTR)
+ continue;
+ err(1, "waitpid");
+ }
+ errno = save_errno;
+
+ return (status);
+}
+
+#define FAIL(test) \
+ do { \
+ if (test) { \
+ printf("FAIL (%s)\n", #test); \
+ return -1; \
+ } \
+ } while (0)
+
+#define SUCCEED \
+ do { printf("SUCCEED\n"); return 0; } while (0)
+
+/*
+ * Test 1 - F_GETLK on unlocked region
+ *
+ * If no lock is found that would prevent this lock from being
+ * created, the structure is left unchanged by this function call
+ * except for the lock type which is set to F_UNLCK.
+ */
+static int
+test1(int fd, __unused int argc, const __unused char **argv)
+{
+ struct flock fl1, fl2;
+
+ memset(&fl1, 1, sizeof(fl1));
+ fl1.l_type = F_WRLCK;
+ fl1.l_whence = SEEK_SET;
+ fl2 = fl1;
+
+ if (fcntl(fd, F_GETLK, &fl1) < 0)
+ err(1, "F_GETLK");
+
+ printf("1 - F_GETLK on unlocked region: ");
+ FAIL(fl1.l_start != fl2.l_start);
+ FAIL(fl1.l_len != fl2.l_len);
+ FAIL(fl1.l_pid != fl2.l_pid);
+ FAIL(fl1.l_type != F_UNLCK);
+ FAIL(fl1.l_whence != fl2.l_whence);
+#ifdef HAVE_SYSID
+ FAIL(fl1.l_sysid != fl2.l_sysid);
+#endif
+
+ SUCCEED;
+}
+
+/*
+ * Test 2 - F_SETLK on locked region
+ *
+ * If a shared or exclusive lock cannot be set, fcntl returns
+ * immediately with EACCES or EAGAIN.
+ */
+static int
+test2(int fd, __unused int argc, const __unused char **argv)
+{
+ /*
+ * We create a child process to hold the lock which we will
+ * test. We use a pipe to communicate with the child.
+ */
+ int pid;
+ int pfd[2];
+ struct flock fl;
+ char ch;
+ int res;
+
+ if (pipe(pfd) < 0)
+ err(1, "pipe");
+
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+
+ pid = fork();
+ if (pid < 0)
+ err(1, "fork");
+
+ if (pid == 0) {
+ /*
+ * We are the child. We set a write lock and then
+ * write one byte back to the parent to tell it. The
+ * parent will kill us when its done.
+ */
+ if (fcntl(fd, F_SETLK, &fl) < 0)
+ err(1, "F_SETLK (child)");
+ if (write(pfd[1], "a", 1) < 0)
+ err(1, "writing to pipe (child)");
+ pause();
+ exit(0);
+ }
+
+ /*
+ * Wait until the child has set its lock and then perform the
+ * test.
+ */
+ if (read(pfd[0], &ch, 1) != 1)
+ err(1, "reading from pipe (child)");
+
+ /*
+ * fcntl should return -1 with errno set to either EACCES or
+ * EAGAIN.
+ */
+ printf("2 - F_SETLK on locked region: ");
+ res = fcntl(fd, F_SETLK, &fl);
+ kill(pid, SIGTERM);
+ safe_waitpid(pid);
+ close(pfd[0]);
+ close(pfd[1]);
+ FAIL(res == 0);
+ FAIL(errno != EACCES && errno != EAGAIN);
+
+ SUCCEED;
+}
+
+/*
+ * Test 3 - F_SETLKW on locked region
+ *
+ * If a shared or exclusive lock is blocked by other locks, the
+ * process waits until the request can be satisfied.
+ *
+ * XXX this test hangs on FreeBSD NFS filesystems due to limitations
+ * in FreeBSD's client (and server) lockd implementation.
+ */
+static int
+test3(int fd, __unused int argc, const __unused char **argv)
+{
+ /*
+ * We create a child process to hold the lock which we will
+ * test. We use a pipe to communicate with the child.
+ */
+ int pid;
+ int pfd[2];
+ struct flock fl;
+ char ch;
+ int res;
+
+ if (pipe(pfd) < 0)
+ err(1, "pipe");
+
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+
+ pid = fork();
+ if (pid < 0)
+ err(1, "fork");
+
+ if (pid == 0) {
+ /*
+ * We are the child. We set a write lock and then
+ * write one byte back to the parent to tell it. The
+ * parent will kill us when its done.
+ */
+ if (fcntl(fd, F_SETLK, &fl) < 0)
+ err(1, "F_SETLK (child)");
+ if (write(pfd[1], "a", 1) < 0)
+ err(1, "writing to pipe (child)");
+ pause();
+ exit(0);
+ }
+
+ /*
+ * Wait until the child has set its lock and then perform the
+ * test.
+ */
+ if (read(pfd[0], &ch, 1) != 1)
+ err(1, "reading from pipe (child)");
+
+ /*
+ * fcntl should wait until the alarm and then return -1 with
+ * errno set to EINTR.
+ */
+ printf("3 - F_SETLKW on locked region: ");
+
+ alarm(1);
+
+ res = fcntl(fd, F_SETLKW, &fl);
+ kill(pid, SIGTERM);
+ safe_waitpid(pid);
+ close(pfd[0]);
+ close(pfd[1]);
+ FAIL(res == 0);
+ FAIL(errno != EINTR);
+
+ SUCCEED;
+}
+
+/*
+ * Test 4 - F_GETLK on locked region
+ *
+ * Get the first lock that blocks the lock.
+ */
+static int
+test4(int fd, __unused int argc, const __unused char **argv)
+{
+ /*
+ * We create a child process to hold the lock which we will
+ * test. We use a pipe to communicate with the child.
+ */
+ int pid;
+ int pfd[2];
+ struct flock fl;
+ char ch;
+
+ if (pipe(pfd) < 0)
+ err(1, "pipe");
+
+ fl.l_start = 0;
+ fl.l_len = 99;
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+
+ pid = fork();
+ if (pid < 0)
+ err(1, "fork");
+
+ if (pid == 0) {
+ /*
+ * We are the child. We set a write lock and then
+ * write one byte back to the parent to tell it. The
+ * parent will kill us when its done.
+ */
+ if (fcntl(fd, F_SETLK, &fl) < 0)
+ err(1, "F_SETLK (child)");
+ if (write(pfd[1], "a", 1) < 0)
+ err(1, "writing to pipe (child)");
+ pause();
+ exit(0);
+ }
+
+ /*
+ * Wait until the child has set its lock and then perform the
+ * test.
+ */
+ if (read(pfd[0], &ch, 1) != 1)
+ err(1, "reading from pipe (child)");
+
+ /*
+ * fcntl should return a lock structure reflecting the lock we
+ * made in the child process.
+ */
+ if (fcntl(fd, F_GETLK, &fl) < 0)
+ err(1, "F_GETLK");
+
+ printf("4 - F_GETLK on locked region: ");
+ FAIL(fl.l_start != 0);
+ FAIL(fl.l_len != 99);
+ FAIL(fl.l_type != F_WRLCK);
+ FAIL(fl.l_pid != pid);
+#ifdef HAVE_SYSID
+ FAIL(fl.l_sysid != 0);
+#endif
+
+ kill(pid, SIGTERM);
+ safe_waitpid(pid);
+ close(pfd[0]);
+ close(pfd[1]);
+
+ SUCCEED;
+}
+
+/*
+ * Test 5 - F_SETLKW simple deadlock
+ *
+ * If a blocking shared lock request would cause a deadlock (i.e. the
+ * lock request is blocked by a process which is itself blocked on a
+ * lock currently owned by the process making the new request),
+ * EDEADLK is returned.
+ */
+static int
+test5(int fd, __unused int argc, const __unused char **argv)
+{
+ /*
+ * We create a child process to hold the lock which we will
+ * test. Because our test relies on the child process being
+ * blocked on the parent's lock, we can't easily use a pipe to
+ * synchronize so we just sleep in the parent to given the
+ * child a chance to setup.
+ *
+ * To create the deadlock condition, we arrange for the parent
+ * to lock the first byte of the file and the child to lock
+ * the second byte. After locking the second byte, the child
+ * will attempt to lock the first byte of the file, and
+ * block. The parent will then attempt to lock the second byte
+ * (owned by the child) which should cause deadlock.
+ */
+ int pid;
+ struct flock fl;
+ int res;
+
+ /*
+ * Lock the first byte in the parent.
+ */
+ fl.l_start = 0;
+ fl.l_len = 1;
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+ if (fcntl(fd, F_SETLK, &fl) < 0)
+ err(1, "F_SETLK 1 (parent)");
+
+ pid = fork();
+ if (pid < 0)
+ err(1, "fork");
+
+ if (pid == 0) {
+ /*
+ * Lock the second byte in the child and then block on
+ * the parent's lock.
+ */
+ fl.l_start = 1;
+ if (fcntl(fd, F_SETLK, &fl) < 0)
+ err(1, "F_SETLK (child)");
+ fl.l_start = 0;
+ if (fcntl(fd, F_SETLKW, &fl) < 0)
+ err(1, "F_SETLKW (child)");
+ exit(0);
+ }
+
+ /*
+ * Wait until the child has set its lock and then perform the
+ * test.
+ */
+ sleep(1);
+
+ /*
+ * fcntl should immediately return -1 with errno set to
+ * EDEADLK. If the alarm fires, we failed to detect the
+ * deadlock.
+ */
+ alarm(1);
+ printf("5 - F_SETLKW simple deadlock: ");
+
+ fl.l_start = 1;
+ res = fcntl(fd, F_SETLKW, &fl);
+ kill(pid, SIGTERM);
+ safe_waitpid(pid);
+
+ FAIL(res == 0);
+ FAIL(errno != EDEADLK);
+
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_type = F_UNLCK;
+ if (fcntl(fd, F_SETLK, &fl) < 0)
+ err(1, "F_UNLCK");
+
+ /*
+ * Cancel the alarm to avoid confusing later tests.
+ */
+ alarm(0);
+
+ SUCCEED;
+}
+
+/*
+ * Test 6 - F_SETLKW complex deadlock.
+ *
+ * This test involves three process, P, C1 and C2. We set things up so
+ * that P locks byte zero, C1 locks byte 1 and C2 locks byte 2. We
+ * also block C2 by attempting to lock byte zero. Lastly, P attempts
+ * to lock a range including byte 1 and 2. This represents a deadlock
+ * (due to C2's blocking attempt to lock byte zero).
+ */
+static int
+test6(int fd, __unused int argc, const __unused char **argv)
+{
+ /*
+ * Because our test relies on the child process being blocked
+ * on the parent's lock, we can't easily use a pipe to
+ * synchronize so we just sleep in the parent to given the
+ * children a chance to setup.
+ */
+ int pid1, pid2;
+ struct flock fl;
+ int res;
+
+ /*
+ * Lock the first byte in the parent.
+ */
+ fl.l_start = 0;
+ fl.l_len = 1;
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+ if (fcntl(fd, F_SETLK, &fl) < 0)
+ err(1, "F_SETLK 1 (parent)");
+
+ pid1 = fork();
+ if (pid1 < 0)
+ err(1, "fork");
+
+ if (pid1 == 0) {
+ /*
+ * C1
+ * Lock the second byte in the child and then sleep
+ */
+ fl.l_start = 1;
+ if (fcntl(fd, F_SETLK, &fl) < 0)
+ err(1, "F_SETLK (child1)");
+ pause();
+ exit(0);
+ }
+
+ pid2 = fork();
+ if (pid2 < 0)
+ err(1, "fork");
+
+ if (pid2 == 0) {
+ /*
+ * C2
+ * Lock the third byte in the child and then block on
+ * the parent's lock.
+ */
+ fl.l_start = 2;
+ if (fcntl(fd, F_SETLK, &fl) < 0)
+ err(1, "F_SETLK (child2)");
+ fl.l_start = 0;
+ if (fcntl(fd, F_SETLKW, &fl) < 0)
+ err(1, "F_SETLKW (child2)");
+ exit(0);
+ }
+
+ /*
+ * Wait until the children have set their locks and then
+ * perform the test.
+ */
+ sleep(1);
+
+ /*
+ * fcntl should immediately return -1 with errno set to
+ * EDEADLK. If the alarm fires, we failed to detect the
+ * deadlock.
+ */
+ alarm(1);
+ printf("6 - F_SETLKW complex deadlock: ");
+
+ fl.l_start = 1;
+ fl.l_len = 2;
+ res = fcntl(fd, F_SETLKW, &fl);
+ kill(pid1, SIGTERM);
+ safe_waitpid(pid1);
+ kill(pid2, SIGTERM);
+ safe_waitpid(pid2);
+
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_type = F_UNLCK;
+ if (fcntl(fd, F_SETLK, &fl) < 0)
+ err(1, "F_UNLCK");
+
+ FAIL(res == 0);
+ FAIL(errno != EDEADLK);
+
+ /*
+ * Cancel the alarm to avoid confusing later tests.
+ */
+ alarm(0);
+
+ SUCCEED;
+}
+
+/*
+ * Test 7 - F_SETLK shared lock on exclusive locked region
+ *
+ * If a shared or exclusive lock cannot be set, fcntl returns
+ * immediately with EACCES or EAGAIN.
+ */
+static int
+test7(int fd, __unused int argc, const __unused char **argv)
+{
+ /*
+ * We create a child process to hold the lock which we will
+ * test. We use a pipe to communicate with the child.
+ */
+ int pid;
+ int pfd[2];
+ struct flock fl;
+ char ch;
+ int res;
+
+ if (pipe(pfd) < 0)
+ err(1, "pipe");
+
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+
+ pid = fork();
+ if (pid < 0)
+ err(1, "fork");
+
+ if (pid == 0) {
+ /*
+ * We are the child. We set a write lock and then
+ * write one byte back to the parent to tell it. The
+ * parent will kill us when its done.
+ */
+ if (fcntl(fd, F_SETLK, &fl) < 0)
+ err(1, "F_SETLK (child)");
+ if (write(pfd[1], "a", 1) < 0)
+ err(1, "writing to pipe (child)");
+ pause();
+ exit(0);
+ }
+
+ /*
+ * Wait until the child has set its lock and then perform the
+ * test.
+ */
+ if (read(pfd[0], &ch, 1) != 1)
+ err(1, "reading from pipe (child)");
+
+ /*
+ * fcntl should wait until the alarm and then return -1 with
+ * errno set to EINTR.
+ */
+ printf("7 - F_SETLK shared lock on exclusive locked region: ");
+
+ fl.l_type = F_RDLCK;
+ res = fcntl(fd, F_SETLK, &fl);
+ kill(pid, SIGTERM);
+ safe_waitpid(pid);
+ close(pfd[0]);
+ close(pfd[1]);
+
+ FAIL(res == 0);
+ FAIL(errno != EACCES && errno != EAGAIN);
+
+ SUCCEED;
+}
+
+/*
+ * Test 8 - F_SETLK shared lock on share locked region
+ *
+ * When a shared lock is set on a segment of a file, other processes
+ * shall be able to set shared locks on that segment or a portion of
+ * it.
+ */
+static int
+test8(int fd, __unused int argc, const __unused char **argv)
+{
+ /*
+ * We create a child process to hold the lock which we will
+ * test. We use a pipe to communicate with the child.
+ */
+ int pid;
+ int pfd[2];
+ struct flock fl;
+ char ch;
+ int res;
+
+ if (pipe(pfd) < 0)
+ err(1, "pipe");
+
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_type = F_RDLCK;
+ fl.l_whence = SEEK_SET;
+
+ pid = fork();
+ if (pid < 0)
+ err(1, "fork");
+
+ if (pid == 0) {
+ /*
+ * We are the child. We set a write lock and then
+ * write one byte back to the parent to tell it. The
+ * parent will kill us when its done.
+ */
+ if (fcntl(fd, F_SETLK, &fl) < 0)
+ err(1, "F_SETLK (child)");
+ if (write(pfd[1], "a", 1) < 0)
+ err(1, "writing to pipe (child)");
+ pause();
+ exit(0);
+ }
+
+ /*
+ * Wait until the child has set its lock and then perform the
+ * test.
+ */
+ if (read(pfd[0], &ch, 1) != 1)
+ err(1, "reading from pipe (child)");
+
+ /*
+ * fcntl should wait until the alarm and then return -1 with
+ * errno set to EINTR.
+ */
+ printf("8 - F_SETLK shared lock on share locked region: ");
+
+ fl.l_type = F_RDLCK;
+ res = fcntl(fd, F_SETLK, &fl);
+
+ kill(pid, SIGTERM);
+ safe_waitpid(pid);
+ close(pfd[0]);
+ close(pfd[1]);
+
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_type = F_UNLCK;
+ if (fcntl(fd, F_SETLK, &fl) < 0)
+ err(1, "F_UNLCK");
+
+ FAIL(res != 0);
+
+ SUCCEED;
+}
+
+/*
+ * Test 9 - F_SETLK exclusive lock on share locked region
+ *
+ * If a shared or exclusive lock cannot be set, fcntl returns
+ * immediately with EACCES or EAGAIN.
+ */
+static int
+test9(int fd, __unused int argc, const __unused char **argv)
+{
+ /*
+ * We create a child process to hold the lock which we will
+ * test. We use a pipe to communicate with the child.
+ */
+ int pid;
+ int pfd[2];
+ struct flock fl;
+ char ch;
+ int res;
+
+ if (pipe(pfd) < 0)
+ err(1, "pipe");
+
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_type = F_RDLCK;
+ fl.l_whence = SEEK_SET;
+
+ pid = fork();
+ if (pid < 0)
+ err(1, "fork");
+
+ if (pid == 0) {
+ /*
+ * We are the child. We set a write lock and then
+ * write one byte back to the parent to tell it. The
+ * parent will kill us when its done.
+ */
+ if (fcntl(fd, F_SETLK, &fl) < 0)
+ err(1, "F_SETLK (child)");
+ if (write(pfd[1], "a", 1) < 0)
+ err(1, "writing to pipe (child)");
+ pause();
+ exit(0);
+ }
+
+ /*
+ * Wait until the child has set its lock and then perform the
+ * test.
+ */
+ if (read(pfd[0], &ch, 1) != 1)
+ err(1, "reading from pipe (child)");
+
+ /*
+ * fcntl should wait until the alarm and then return -1 with
+ * errno set to EINTR.
+ */
+ printf("9 - F_SETLK exclusive lock on share locked region: ");
+
+ fl.l_type = F_WRLCK;
+ res = fcntl(fd, F_SETLK, &fl);
+ kill(pid, SIGTERM);
+ safe_waitpid(pid);
+ close(pfd[0]);
+ close(pfd[1]);
+
+ FAIL(res == 0);
+ FAIL(errno != EACCES && errno != EAGAIN);
+
+ SUCCEED;
+}
+
+/*
+ * Test 10 - trying to set bogus pid or sysid values
+ *
+ * The l_pid and l_sysid fields are only used with F_GETLK to return
+ * the process ID of the process holding a blocking lock and the
+ * system ID of the system that owns that process
+ */
+static int
+test10(int fd, __unused int argc, const __unused char **argv)
+{
+ /*
+ * We create a child process to hold the lock which we will
+ * test. We use a pipe to communicate with the child.
+ */
+ int pid;
+ int pfd[2];
+ struct flock fl;
+ char ch;
+
+ if (pipe(pfd) < 0)
+ err(1, "pipe");
+
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_pid = 9999;
+#ifdef HAVE_SYSID
+ fl.l_sysid = 9999;
+#endif
+
+ pid = fork();
+ if (pid < 0)
+ err(1, "fork");
+
+ if (pid == 0) {
+ /*
+ * We are the child. We set a write lock and then
+ * write one byte back to the parent to tell it. The
+ * parent will kill us when its done.
+ */
+ if (fcntl(fd, F_SETLK, &fl) < 0)
+ err(1, "F_SETLK (child)");
+ if (write(pfd[1], "a", 1) < 0)
+ err(1, "writing to pipe (child)");
+ pause();
+ exit(0);
+ }
+
+ /*
+ * Wait until the child has set its lock and then perform the
+ * test.
+ */
+ if (read(pfd[0], &ch, 1) != 1)
+ err(1, "reading from pipe (child)");
+
+ printf("10 - trying to set bogus pid or sysid values: ");
+
+ if (fcntl(fd, F_GETLK, &fl) < 0)
+ err(1, "F_GETLK");
+
+ kill(pid, SIGTERM);
+ safe_waitpid(pid);
+ close(pfd[0]);
+ close(pfd[1]);
+
+ FAIL(fl.l_pid != pid);
+#ifdef HAVE_SYSID
+ FAIL(fl.l_sysid != 0);
+#endif
+
+ SUCCEED;
+}
+
+/*
+ * Test 11 - remote locks
+ *
+ * XXX temporary interface which will be removed when the kernel lockd
+ * is added.
+ */
+static int
+test11(int fd, __unused int argc, const __unused char **argv)
+{
+#ifdef F_SETLK_REMOTE
+ struct flock fl;
+ int res;
+
+ if (geteuid() != 0)
+ return 0;
+
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_pid = 9999;
+ fl.l_sysid = 1001;
+
+ printf("11 - remote locks: ");
+
+ res = fcntl(fd, F_SETLK_REMOTE, &fl);
+ FAIL(res != 0);
+
+ fl.l_sysid = 1002;
+ res = fcntl(fd, F_SETLK_REMOTE, &fl);
+ FAIL(res == 0);
+ FAIL(errno != EACCES && errno != EAGAIN);
+
+ res = fcntl(fd, F_GETLK, &fl);
+ FAIL(res != 0);
+ FAIL(fl.l_pid != 9999);
+ FAIL(fl.l_sysid != 1001);
+
+ fl.l_type = F_UNLCK;
+ fl.l_sysid = 1001;
+ fl.l_start = 0;
+ fl.l_len = 0;
+ res = fcntl(fd, F_SETLK_REMOTE, &fl);
+ FAIL(res != 0);
+
+ fl.l_pid = 1234;
+ fl.l_sysid = 1001;
+ fl.l_start = 0;
+ fl.l_len = 1;
+ fl.l_whence = SEEK_SET;
+ fl.l_type = F_RDLCK;
+ res = fcntl(fd, F_SETLK_REMOTE, &fl);
+ FAIL(res != 0);
+
+ fl.l_sysid = 1002;
+ res = fcntl(fd, F_SETLK_REMOTE, &fl);
+ FAIL(res != 0);
+
+ fl.l_type = F_UNLCKSYS;
+ fl.l_sysid = 1001;
+ res = fcntl(fd, F_SETLK_REMOTE, &fl);
+ FAIL(res != 0);
+
+ fl.l_type = F_WRLCK;
+ res = fcntl(fd, F_GETLK, &fl);
+ FAIL(res != 0);
+ FAIL(fl.l_pid != 1234);
+ FAIL(fl.l_sysid != 1002);
+
+ fl.l_type = F_UNLCKSYS;
+ fl.l_sysid = 1002;
+ res = fcntl(fd, F_SETLK_REMOTE, &fl);
+ FAIL(res != 0);
+
+ SUCCEED;
+#else
+ return 0;
+#endif
+}
+
+/*
+ * Test 12 - F_SETLKW on locked region which is then unlocked
+ *
+ * If a shared or exclusive lock is blocked by other locks, the
+ * process waits until the request can be satisfied.
+ */
+static int
+test12(int fd, __unused int argc, const __unused char **argv)
+{
+ /*
+ * We create a child process to hold the lock which we will
+ * test. We use a pipe to communicate with the child.
+ */
+ int pid;
+ int pfd[2];
+ struct flock fl;
+ char ch;
+ int res;
+
+ if (pipe(pfd) < 0)
+ err(1, "pipe");
+
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+
+ pid = fork();
+ if (pid < 0)
+ err(1, "fork");
+
+ if (pid == 0) {
+ /*
+ * We are the child. We set a write lock and then
+ * write one byte back to the parent to tell it. The
+ * parent will kill us when its done.
+ */
+ if (fcntl(fd, F_SETLK, &fl) < 0)
+ err(1, "F_SETLK (child)");
+ if (write(pfd[1], "a", 1) < 0)
+ err(1, "writing to pipe (child)");
+
+ sleep(1);
+ exit(0);
+ }
+
+ /*
+ * Wait until the child has set its lock and then perform the
+ * test.
+ */
+ if (read(pfd[0], &ch, 1) != 1)
+ err(1, "reading from pipe (child)");
+
+ /*
+ * fcntl should wait until the alarm and then return -1 with
+ * errno set to EINTR.
+ */
+ printf("12 - F_SETLKW on locked region which is then unlocked: ");
+
+ //alarm(1);
+
+ res = fcntl(fd, F_SETLKW, &fl);
+ kill(pid, SIGTERM);
+ safe_waitpid(pid);
+ close(pfd[0]);
+ close(pfd[1]);
+ FAIL(res != 0);
+
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_type = F_UNLCK;
+ if (fcntl(fd, F_SETLK, &fl) < 0)
+ err(1, "F_UNLCK");
+
+ SUCCEED;
+}
+
+/*
+ * Test 13 - F_SETLKW on locked region, race with owner
+ *
+ * If a shared or exclusive lock is blocked by other locks, the
+ * process waits until the request can be satisfied.
+ */
+static int
+test13(int fd, __unused int argc, const __unused char **argv)
+{
+ /*
+ * We create a child process to hold the lock which we will
+ * test. We use a pipe to communicate with the child.
+ */
+ int i;
+ int pid;
+ int pfd[2];
+ struct flock fl;
+ char ch;
+ int res;
+ struct itimerval itv;
+
+ printf("13 - F_SETLKW on locked region, race with owner: ");
+ fflush(stdout);
+
+ for (i = 0; i < 100; i++) {
+ if (pipe(pfd) < 0)
+ err(1, "pipe");
+
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+
+ pid = fork();
+ if (pid < 0)
+ err(1, "fork");
+
+ if (pid == 0) {
+ /*
+ * We are the child. We set a write lock and then
+ * write one byte back to the parent to tell it. The
+ * parent will kill us when its done.
+ */
+ if (fcntl(fd, F_SETLK, &fl) < 0)
+ err(1, "F_SETLK (child)");
+ if (write(pfd[1], "a", 1) < 0)
+ err(1, "writing to pipe (child)");
+
+ usleep(1);
+ exit(0);
+ }
+
+ /*
+ * Wait until the child has set its lock and then perform the
+ * test.
+ */
+ while (read(pfd[0], &ch, 1) != 1) {
+ if (errno == EINTR)
+ continue;
+ err(1, "reading from pipe (child)");
+ }
+
+ /*
+ * fcntl should wait until the alarm and then return -1 with
+ * errno set to EINTR.
+ */
+ itv.it_interval.tv_sec = 0;
+ itv.it_interval.tv_usec = 0;
+ itv.it_value.tv_sec = 0;
+ itv.it_value.tv_usec = 2;
+ setitimer(ITIMER_REAL, &itv, NULL);
+
+ res = fcntl(fd, F_SETLKW, &fl);
+ kill(pid, SIGTERM);
+ safe_waitpid(pid);
+ close(pfd[0]);
+ close(pfd[1]);
+ FAIL(!(res == 0 || (res == -1 && errno == EINTR)));
+
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_type = F_UNLCK;
+ if (fcntl(fd, F_SETLK, &fl) < 0)
+ err(1, "F_UNLCK");
+ }
+ SUCCEED;
+}
+
+/*
+ * Test 14 - soak test
+ */
+static int
+test14(int fd, int argc, const char **argv)
+{
+#define CHILD_COUNT 20
+ /*
+ * We create a set of child processes and let each one run
+ * through a random sequence of locks and unlocks.
+ */
+ int i, j, id, id_base;
+ int pids[CHILD_COUNT], pid;
+ char buf[128];
+ char tbuf[128];
+ int map[128];
+ char outbuf[512];
+ struct flock fl;
+ struct itimerval itv;
+ int status;
+
+ id_base = 0;
+ if (argc >= 2)
+ id_base = strtol(argv[1], NULL, 0);
+
+ printf("14 - soak test: ");
+ fflush(stdout);
+
+ for (i = 0; i < 128; i++)
+ map[i] = F_UNLCK;
+
+ for (i = 0; i < CHILD_COUNT; i++) {
+
+ pid = fork();
+ if (pid < 0)
+ err(1, "fork");
+ if (pid) {
+ /*
+ * Parent - record the pid and continue.
+ */
+ pids[i] = pid;
+ continue;
+ }
+
+ /*
+ * Child - do some work and exit.
+ */
+ id = id_base + i;
+ srandom(getpid());
+
+ for (j = 0; j < 50; j++) {
+ int start, end, len;
+ int set, wrlock;
+
+ do {
+ start = random() & 127;
+ end = random() & 127;
+ } while (end <= start);
+
+ set = random() & 1;
+ wrlock = random() & 1;
+
+ len = end - start;
+ fl.l_start = start;
+ fl.l_len = len;
+ fl.l_whence = SEEK_SET;
+ if (set)
+ fl.l_type = wrlock ? F_WRLCK : F_RDLCK;
+ else
+ fl.l_type = F_UNLCK;
+
+ itv.it_interval.tv_sec = 0;
+ itv.it_interval.tv_usec = 0;
+ itv.it_value.tv_sec = 0;
+ itv.it_value.tv_usec = 3000;
+ setitimer(ITIMER_REAL, &itv, NULL);
+
+ if (fcntl(fd, F_SETLKW, &fl) < 0) {
+ if (errno == EDEADLK || errno == EINTR) {
+ if (verbose) {
+ snprintf(outbuf, sizeof(outbuf),
+ "%d[%d]: %s [%d .. %d] %s\n",
+ id, j,
+ set ? (wrlock ? "write lock"
+ : "read lock")
+ : "unlock", start, end,
+ errno == EDEADLK
+ ? "deadlock"
+ : "interrupted");
+ write(1, outbuf,
+ strlen(outbuf));
+ }
+ continue;
+ } else {
+ perror("fcntl");
+ }
+ }
+
+ itv.it_interval.tv_sec = 0;
+ itv.it_interval.tv_usec = 0;
+ itv.it_value.tv_sec = 0;
+ itv.it_value.tv_usec = 0;
+ setitimer(ITIMER_REAL, &itv, NULL);
+
+ if (verbose) {
+ snprintf(outbuf, sizeof(outbuf),
+ "%d[%d]: %s [%d .. %d] succeeded\n",
+ id, j,
+ set ? (wrlock ? "write lock" : "read lock")
+ : "unlock", start, end);
+ write(1, outbuf, strlen(outbuf));
+ }
+
+ if (set) {
+ if (wrlock) {
+ /*
+ * We got a write lock - write
+ * our ID to each byte that we
+ * managed to claim.
+ */
+ for (i = start; i < end; i++)
+ map[i] = F_WRLCK;
+ memset(&buf[start], id, len);
+ if (pwrite(fd, &buf[start], len,
+ start) != len) {
+ printf("%d: short write\n", id);
+ exit(1);
+ }
+ } else {
+ /*
+ * We got a read lock - read
+ * the bytes which we claimed
+ * so that we can check that
+ * they don't change
+ * unexpectedly.
+ */
+ for (i = start; i < end; i++)
+ map[i] = F_RDLCK;
+ if (pread(fd, &buf[start], len,
+ start) != len) {
+ printf("%d: short read\n", id);
+ exit(1);
+ }
+ }
+ } else {
+ for (i = start; i < end; i++)
+ map[i] = F_UNLCK;
+ }
+
+ usleep(1000);
+
+ /*
+ * Read back the whole region so that we can
+ * check that all the bytes we have some kind
+ * of claim to have the correct value.
+ */
+ if (pread(fd, tbuf, sizeof(tbuf), 0) != sizeof(tbuf)) {
+ printf("%d: short read\n", id);
+ exit(1);
+ }
+
+ for (i = 0; i < 128; i++) {
+ if (map[i] != F_UNLCK && buf[i] != tbuf[i]) {
+ snprintf(outbuf, sizeof(outbuf),
+ "%d: byte %d expected %d, "
+ "got %d\n", id, i, buf[i], tbuf[i]);
+ write(1, outbuf, strlen(outbuf));
+ exit(1);
+ }
+ }
+ }
+ if (verbose)
+ printf("%d[%d]: done\n", id, j);
+
+ exit(0);
+ }
+
+ status = 0;
+ for (i = 0; i < CHILD_COUNT; i++) {
+ status += safe_waitpid(pids[i]);
+ }
+ if (status)
+ FAIL(status != 0);
+
+ SUCCEED;
+}
+
+/*
+ * Test 15 - flock(2) semantcs
+ *
+ * When a lock holder has a shared lock and attempts to upgrade that
+ * shared lock to exclusive, it must drop the shared lock before
+ * blocking on the exclusive lock.
+ *
+ * To test this, we first arrange for two shared locks on the file,
+ * and then attempt to upgrade one of them to exclusive. This should
+ * drop one of the shared locks and block. We interrupt the blocking
+ * lock request and examine the lock state of the file after dropping
+ * the other shared lock - there should be no active locks at this
+ * point.
+ */
+static int
+test15(int fd, __unused int argc, const __unused char **argv)
+{
+#ifdef LOCK_EX
+ /*
+ * We create a child process to hold the lock which we will
+ * test. We use a pipe to communicate with the child.
+ *
+ * Since we only have one file descriptors and lock ownership
+ * for flock(2) goes with the file descriptor, we use fcntl to
+ * set the child's shared lock.
+ */
+ int pid;
+ int pfd[2];
+ struct flock fl;
+ char ch;
+ int res;
+
+ if (pipe(pfd) < 0)
+ err(1, "pipe");
+
+ pid = fork();
+ if (pid < 0)
+ err(1, "fork");
+
+ if (pid == 0) {
+ /*
+ * We are the child. We set a shared lock and then
+ * write one byte back to the parent to tell it. The
+ * parent will kill us when its done.
+ */
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_type = F_RDLCK;
+ fl.l_whence = SEEK_SET;
+ if (fcntl(fd, F_SETLK, &fl) < 0)
+ err(1, "fcntl(F_SETLK) (child)");
+ if (write(pfd[1], "a", 1) < 0)
+ err(1, "writing to pipe (child)");
+ pause();
+ exit(0);
+ }
+
+ /*
+ * Wait until the child has set its lock and then perform the
+ * test.
+ */
+ if (read(pfd[0], &ch, 1) != 1)
+ err(1, "reading from pipe (child)");
+
+ (void)dup(fd);
+ if (flock(fd, LOCK_SH) < 0)
+ err(1, "flock shared");
+
+ /*
+ * flock should wait until the alarm and then return -1 with
+ * errno set to EINTR.
+ */
+ printf("15 - flock(2) semantics: ");
+
+ alarm(1);
+ flock(fd, LOCK_EX);
+
+ /*
+ * Kill the child to force it to drop its locks.
+ */
+ kill(pid, SIGTERM);
+ safe_waitpid(pid);
+
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+ res = fcntl(fd, F_GETLK, &fl);
+
+ close(pfd[0]);
+ close(pfd[1]);
+ FAIL(res != 0);
+ FAIL(fl.l_type != F_UNLCK);
+
+ SUCCEED;
+#else
+ return 0;
+#endif
+}
+
+struct test_ctx {
+ struct flock tc_fl;
+ int tc_fd;
+};
+
+static void *
+test16_func(void *tc_in)
+{
+ uintptr_t error;
+ struct test_ctx *tc = tc_in;
+
+ error = fcntl(tc->tc_fd, F_SETLKW, &tc->tc_fl);
+
+ pthread_exit((void *)error);
+}
+
+#define THREADS 10
+
+/*
+ * Test 16 - F_SETLKW from two threads
+ *
+ * If two threads within a process are blocked on a lock and the lock
+ * is granted, make sure things are sane.
+ */
+static int
+test16(int fd, __unused int argc, const __unused char **argv)
+{
+ /*
+ * We create a child process to hold the lock which we will
+ * test. We use a pipe to communicate with the child.
+ */
+ int pid;
+ int pfd[2];
+ struct test_ctx tc = { .tc_fd = fd };
+ char ch;
+ int i;
+ int error;
+ pthread_t thr[THREADS];
+
+ if (pipe(pfd) < 0)
+ err(1, "pipe");
+
+ tc.tc_fl.l_start = 0;
+ tc.tc_fl.l_len = 0;
+ tc.tc_fl.l_type = F_WRLCK;
+ tc.tc_fl.l_whence = SEEK_SET;
+
+ pid = fork();
+ if (pid < 0)
+ err(1, "fork");
+
+ if (pid == 0) {
+ /*
+ * We are the child. We set a write lock and then
+ * write one byte back to the parent to tell it. The
+ * parent will kill us when its done.
+ */
+ if (fcntl(fd, F_SETLK, &tc.tc_fl) < 0)
+ err(1, "F_SETLK (child)");
+ if (write(pfd[1], "a", 1) < 0)
+ err(1, "writing to pipe (child)");
+ pause();
+ exit(0);
+ }
+
+ /*
+ * Wait until the child has set its lock and then perform the
+ * test.
+ */
+ if (read(pfd[0], &ch, 1) != 1)
+ err(1, "reading from pipe (child)");
+
+ /*
+ * fcntl should wait until the alarm and then return -1 with
+ * errno set to EINTR.
+ */
+ printf("16 - F_SETLKW on locked region by two threads: ");
+
+ for (i = 0; i < THREADS; i++) {
+ error = pthread_create(&thr[i], NULL, test16_func, &tc);
+ if (error)
+ err(1, "pthread_create");
+ }
+
+ /*
+ * Sleep, then kill the child. This makes me a little sad, but it's
+ * tricky to tell whether the threads are all really blocked by this
+ * point.
+ */
+ sleep(1);
+ kill(pid, SIGTERM);
+ safe_waitpid(pid);
+ close(pfd[0]);
+ close(pfd[1]);
+
+ for (i = 0; i < THREADS; i++) {
+ void *res;
+ error = pthread_join(thr[i], &res);
+ if (error)
+ err(1, "pthread_join");
+ FAIL((uintptr_t)res != 0);
+ }
+
+ SUCCEED;
+}
+
+struct test {
+ int (*testfn)(int, int, const char **); /* function to perform the test */
+ int num; /* test number */
+ int intr; /* non-zero if the test interrupts a lock */
+};
+
+static struct test tests[] = {
+ { test1, 1, 0 },
+ { test2, 2, 0 },
+ { test3, 3, 1 },
+ { test4, 4, 0 },
+ { test5, 5, 1 },
+ { test6, 6, 1 },
+ { test7, 7, 0 },
+ { test8, 8, 0 },
+ { test9, 9, 0 },
+ { test10, 10, 0 },
+ { test11, 11, 1 },
+ { test12, 12, 0 },
+ { test13, 13, 1 },
+ { test14, 14, 0 },
+ { test15, 15, 1 },
+ { test16, 16, 1 },
+};
+
+int
+main(int argc, const char *argv[])
+{
+ int testnum;
+ int fd;
+ int nointr;
+ unsigned i;
+ struct sigaction sa;
+ int test_argc;
+ const char **test_argv;
+
+ if (argc < 2) {
+ errx(1, "usage: flock <directory> [test number] ...");
+ }
+
+ fd = make_file(argv[1], 1024);
+ if (argc >= 3) {
+ testnum = strtol(argv[2], NULL, 0);
+ test_argc = argc - 2;
+ test_argv = argv + 2;
+ } else {
+ testnum = 0;
+ test_argc = 0;
+ test_argv = NULL;
+ }
+
+ sa.sa_handler = ignore_alarm;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction(SIGALRM, &sa, 0);
+
+ nointr = 0;
+#if defined(__FreeBSD__) && __FreeBSD_version < 800040
+ {
+ /*
+ * FreeBSD with userland NLM can't interrupt a blocked
+ * lock request on an NFS mounted filesystem.
+ */
+ struct statfs st;
+ fstatfs(fd, &st);
+ nointr = !strcmp(st.f_fstypename, "nfs");
+ }
+#endif
+
+ for (i = 0; i < nitems(tests); i++) {
+ if (tests[i].intr && nointr)
+ continue;
+ if (!testnum || tests[i].num == testnum)
+ tests[i].testfn(fd, test_argc, test_argv);
+ }
+
+ return 0;
+}
diff --git a/tests/sys/file/flock_test.sh b/tests/sys/file/flock_test.sh
new file mode 100644
index 000000000000..0b58d108b83d
--- /dev/null
+++ b/tests/sys/file/flock_test.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+#
+# Copyright 2014 EMC Corp.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of Google Inc. nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# Testcase # 11 is racy; uses an undocumented kernel interface for testing
+# locking
+last_testcase=16
+
+echo "1..$last_testcase"
+
+for n in `seq 1 $last_testcase`; do
+ todomsg=""
+
+ if [ $n -eq 11 ]; then
+ todomsg=" # TODO: racy testcase"
+ fi
+
+ output=$($(dirname $0)/flock_helper . $n)
+ if echo "$output" | grep -q SUCCEED; then
+ echo "ok $n$todomsg"
+ else
+ echo "not ok $n$todomsg"
+ echo "$output" >&2
+ fi
+done
diff --git a/tests/sys/file/fspacectl_test.c b/tests/sys/file/fspacectl_test.c
new file mode 100644
index 000000000000..714474b76b05
--- /dev/null
+++ b/tests/sys/file/fspacectl_test.c
@@ -0,0 +1,338 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2021 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Ka Ho Ng under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <malloc.h>
+
+static off_t file_max_blocks = 32;
+static const char byte_to_fill = 0x5f;
+
+static int
+fill(int fd, off_t offset, off_t len)
+{
+ int error;
+ size_t blen;
+ char *buf;
+ struct stat statbuf;
+ blksize_t blocksize;
+
+ if (fstat(fd, &statbuf) == -1)
+ return (1);
+ blocksize = statbuf.st_blksize;
+ error = 0;
+ buf = malloc(blocksize);
+ if (buf == NULL)
+ return (1);
+
+ while (len > 0) {
+ blen = len < (off_t)blocksize ? len : blocksize;
+ memset(buf, byte_to_fill, blen);
+ if (pwrite(fd, buf, blen, offset) != (ssize_t)blen) {
+ error = 1;
+ break;
+ }
+ len -= blen;
+ offset += blen;
+ }
+
+ free(buf);
+ return (error);
+}
+
+static blksize_t
+fd_get_blksize(void)
+{
+ struct statfs statfsbuf;
+
+ if (statfs(".", &statfsbuf) == -1)
+ return (-1);
+ return statfsbuf.f_iosize;
+}
+
+static int
+check_content_dealloc(int fd, off_t hole_start, off_t hole_len, off_t file_sz)
+{
+ int error;
+ size_t blen;
+ off_t offset, resid;
+ struct stat statbuf;
+ char *buf, *sblk;
+ blksize_t blocksize;
+
+ blocksize = fd_get_blksize();
+ if (blocksize == -1)
+ return (1);
+ error = 0;
+ buf = malloc(blocksize * 2);
+ if (buf == NULL)
+ return (1);
+ sblk = buf + blocksize;
+
+ memset(sblk, 0, blocksize);
+
+ if ((uint64_t)hole_start + hole_len > (uint64_t)file_sz)
+ hole_len = file_sz - hole_start;
+
+ /*
+ * Check hole is zeroed.
+ */
+ offset = hole_start;
+ resid = hole_len;
+ while (resid > 0) {
+ blen = resid < (off_t)blocksize ? resid : blocksize;
+ if (pread(fd, buf, blen, offset) != (ssize_t)blen) {
+ error = 1;
+ break;
+ }
+ if (memcmp(buf, sblk, blen) != 0) {
+ error = 1;
+ break;
+ }
+ resid -= blen;
+ offset += blen;
+ }
+
+ memset(sblk, byte_to_fill, blocksize);
+
+ /*
+ * Check file region before hole is zeroed.
+ */
+ offset = 0;
+ resid = hole_start;
+ while (resid > 0) {
+ blen = resid < (off_t)blocksize ? resid : blocksize;
+ if (pread(fd, buf, blen, offset) != (ssize_t)blen) {
+ error = 1;
+ break;
+ }
+ if (memcmp(buf, sblk, blen) != 0) {
+ error = 1;
+ break;
+ }
+ resid -= blen;
+ offset += blen;
+ }
+
+ /*
+ * Check file region after hole is zeroed.
+ */
+ offset = hole_start + hole_len;
+ resid = file_sz - offset;
+ while (resid > 0) {
+ blen = resid < (off_t)blocksize ? resid : blocksize;
+ if (pread(fd, buf, blen, offset) != (ssize_t)blen) {
+ error = 1;
+ break;
+ }
+ if (memcmp(buf, sblk, blen) != 0) {
+ error = 1;
+ break;
+ }
+ resid -= blen;
+ offset += blen;
+ }
+
+ /*
+ * Check file size matches with expected file size.
+ */
+ if (fstat(fd, &statbuf) == -1)
+ error = -1;
+ if (statbuf.st_size != file_sz)
+ error = -1;
+
+ free(buf);
+ return (error);
+}
+
+/*
+ * Check aligned deallocation
+ */
+ATF_TC_WITHOUT_HEAD(aligned_dealloc);
+ATF_TC_BODY(aligned_dealloc, tc)
+{
+ struct spacectl_range range;
+ off_t offset, length;
+ blksize_t blocksize;
+ int fd;
+
+ ATF_REQUIRE((blocksize = fd_get_blksize()) != -1);
+ range.r_offset = offset = blocksize;
+ range.r_len = length = (file_max_blocks - 1) * blocksize -
+ range.r_offset;
+
+ ATF_REQUIRE((fd = open("sys_fspacectl_testfile",
+ O_CREAT | O_RDWR | O_TRUNC, 0600)) != -1);
+ ATF_REQUIRE(fill(fd, 0, file_max_blocks * blocksize) == 0);
+ ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range, 0, &range) == 0);
+ ATF_CHECK(check_content_dealloc(fd, offset, length,
+ file_max_blocks * blocksize) == 0);
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+/*
+ * Check unaligned deallocation
+ */
+ATF_TC_WITHOUT_HEAD(unaligned_dealloc);
+ATF_TC_BODY(unaligned_dealloc, tc)
+{
+ struct spacectl_range range;
+ off_t offset, length;
+ blksize_t blocksize;
+ int fd;
+
+ ATF_REQUIRE((blocksize = fd_get_blksize()) != -1);
+ range.r_offset = offset = blocksize / 2;
+ range.r_len = length = (file_max_blocks - 1) * blocksize +
+ blocksize / 2 - offset;
+
+ ATF_REQUIRE((fd = open("sys_fspacectl_testfile",
+ O_CREAT | O_RDWR | O_TRUNC, 0600)) != -1);
+ ATF_REQUIRE(fill(fd, 0, file_max_blocks * blocksize) == 0);
+ ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range, 0, &range) == 0);
+ ATF_CHECK(check_content_dealloc(fd, offset, length,
+ file_max_blocks * blocksize) == 0);
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+/*
+ * Check aligned deallocation from certain offset to OFF_MAX
+ */
+ATF_TC_WITHOUT_HEAD(aligned_dealloc_offmax);
+ATF_TC_BODY(aligned_dealloc_offmax, tc)
+{
+ struct spacectl_range range;
+ off_t offset, length;
+ blksize_t blocksize;
+ int fd;
+
+ ATF_REQUIRE((blocksize = fd_get_blksize()) != -1);
+ range.r_offset = offset = blocksize;
+ range.r_len = length = OFF_MAX - offset;
+
+ ATF_REQUIRE((fd = open("sys_fspacectl_testfile",
+ O_CREAT | O_RDWR | O_TRUNC, 0600)) != -1);
+ ATF_REQUIRE(fill(fd, 0, file_max_blocks * blocksize) == 0);
+ ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range, 0, &range) == 0);
+ ATF_CHECK(check_content_dealloc(fd, offset, length,
+ file_max_blocks * blocksize) == 0);
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+/*
+ * Check unaligned deallocation from certain offset to OFF_MAX
+ */
+ATF_TC_WITHOUT_HEAD(unaligned_dealloc_offmax);
+ATF_TC_BODY(unaligned_dealloc_offmax, tc)
+{
+ struct spacectl_range range;
+ off_t offset, length;
+ blksize_t blocksize;
+ int fd;
+
+ ATF_REQUIRE((blocksize = fd_get_blksize()) != -1);
+ range.r_offset = offset = blocksize / 2;
+ range.r_len = length = OFF_MAX - offset;
+
+ ATF_REQUIRE((fd = open("sys_fspacectl_testfile",
+ O_CREAT | O_RDWR | O_TRUNC, 0600)) != -1);
+ ATF_REQUIRE(fill(fd, 0, file_max_blocks * blocksize) == 0);
+ ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range, 0, &range) == 0);
+ ATF_CHECK(check_content_dealloc(fd, offset, length,
+ file_max_blocks * blocksize) == 0);
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+/*
+ * Check aligned deallocation around EOF
+ */
+ATF_TC_WITHOUT_HEAD(aligned_dealloc_eof);
+ATF_TC_BODY(aligned_dealloc_eof, tc)
+{
+ struct spacectl_range range;
+ off_t offset, length;
+ blksize_t blocksize;
+ int fd;
+
+ ATF_REQUIRE((blocksize = fd_get_blksize()) != -1);
+ range.r_offset = offset = blocksize;
+ range.r_len = length = (file_max_blocks + 1) * blocksize -
+ range.r_offset;
+
+ ATF_REQUIRE((fd = open("sys_fspacectl_testfile",
+ O_CREAT | O_RDWR | O_TRUNC, 0600)) != -1);
+ ATF_REQUIRE(fill(fd, 0, file_max_blocks * blocksize) == 0);
+ ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range, 0, &range) == 0);
+ ATF_CHECK(check_content_dealloc(fd, offset, length,
+ file_max_blocks * blocksize) == 0);
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+/*
+ * Check unaligned deallocation around EOF
+ */
+ATF_TC_WITHOUT_HEAD(unaligned_dealloc_eof);
+ATF_TC_BODY(unaligned_dealloc_eof, tc)
+{
+ struct spacectl_range range;
+ off_t offset, length;
+ blksize_t blocksize;
+ int fd;
+
+ ATF_REQUIRE((blocksize = fd_get_blksize()) != -1);
+ range.r_offset = offset = blocksize / 2;
+ range.r_len = length = file_max_blocks * blocksize + blocksize / 2 -
+ range.r_offset;
+
+ ATF_REQUIRE((fd = open("sys_fspacectl_testfile",
+ O_CREAT | O_RDWR | O_TRUNC, 0600)) != -1);
+ ATF_REQUIRE(fill(fd, 0, file_max_blocks * blocksize) == 0);
+ ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range, 0, &range) == 0);
+ ATF_CHECK(check_content_dealloc(fd, offset, length,
+ file_max_blocks * blocksize) == 0);
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, aligned_dealloc);
+ ATF_TP_ADD_TC(tp, unaligned_dealloc);
+ ATF_TP_ADD_TC(tp, aligned_dealloc_eof);
+ ATF_TP_ADD_TC(tp, unaligned_dealloc_eof);
+ ATF_TP_ADD_TC(tp, aligned_dealloc_offmax);
+ ATF_TP_ADD_TC(tp, unaligned_dealloc_offmax);
+
+ return atf_no_error();
+}
diff --git a/tests/sys/file/ftruncate_test.c b/tests/sys/file/ftruncate_test.c
new file mode 100644
index 000000000000..09c7c76cc4ac
--- /dev/null
+++ b/tests/sys/file/ftruncate_test.c
@@ -0,0 +1,174 @@
+/*-
+ * Copyright (c) 2006 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Very simple regression test.
+ *
+ * Future tests that might be of interest:
+ *
+ * - Make sure we get EISDIR on a directory.
+ */
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <unistd.h>
+
+/*
+ * Select various potentially interesting lengths at and around power of 2
+ * edges.
+ */
+static off_t lengths[] = {0, 1, 2, 3, 4, 127, 128, 129, 511, 512, 513, 1023,
+ 1024, 1025, 2047, 2048, 2049, 4095, 4096, 4097, 8191, 8192, 8193, 16383,
+ 16384, 16385};
+static int lengths_count = sizeof(lengths) / sizeof(off_t);
+
+int
+main(void)
+{
+ int error, fd, fds[2], i, read_only_fd;
+ char path[] = "ftruncate_file";
+ struct stat sb;
+ ssize_t size;
+ off_t len;
+ char ch;
+
+ /*
+ * Tests using a writable file: grow and then shrink a file
+ * using ftruncate and various lengths. Make sure that a negative
+ * file length is rejected. Make sure that when we grow the file,
+ * bytes now in the range of the file size return 0.
+ *
+ * Save a read-only reference to the file to use later for read-only
+ * descriptor tests.
+ */
+ fd = open(path, O_RDWR|O_CREAT, 0600);
+ if (fd < 0)
+ err(1, "open(%s, O_RDWR|O_CREAT, 0600)", path);
+ read_only_fd = open(path, O_RDONLY);
+ if (read_only_fd < 0) {
+ error = errno;
+ (void)unlink(path);
+ errno = error;
+ err(1, "open(%s, O_RDONLY)", path);
+ }
+ (void)unlink(path);
+
+ if (ftruncate(fd, -1) == 0)
+ errx(1, "ftruncate(fd, -1) succeeded unexpectedly");
+ if (errno != EINVAL)
+ err(1, "ftruncate(fd, -1) returned wrong error");
+
+ for (i = 0; i < lengths_count; i++) {
+ len = lengths[i];
+ if (ftruncate(fd, len) < 0)
+ err(1, "ftruncate(%jd) up", (intmax_t)len);
+ if (fstat(fd, &sb) < 0)
+ err(1, "stat");
+ if (sb.st_size != len)
+ errx(-1, "fstat with len=%jd returned len %jd up",
+ (intmax_t)len, (intmax_t)sb.st_size);
+ if (len != 0) {
+ size = pread(fd, &ch, sizeof(ch), len - 1);
+ if (size < 0)
+ err(1, "pread on len %jd up", (intmax_t)len);
+ if (size != sizeof(ch))
+ errx(-1, "pread len %jd size %jd up",
+ (intmax_t)len, (intmax_t)size);
+ if (ch != 0)
+ errx(-1,
+ "pread length %jd size %jd ch %d up",
+ (intmax_t)len, (intmax_t)size, ch);
+ }
+ }
+
+ for (i = lengths_count - 1; i >= 0; i--) {
+ len = lengths[i];
+ if (ftruncate(fd, len) < 0)
+ err(1, "ftruncate(%jd) down", (intmax_t)len);
+ if (fstat(fd, &sb) < 0)
+ err(1, "stat");
+ if (sb.st_size != len)
+ errx(-1, "fstat(%jd) returned %jd down", (intmax_t)len,
+ sb.st_size);
+ }
+ close(fd);
+
+ /*
+ * Make sure that a read-only descriptor can't be truncated.
+ */
+ if (ftruncate(read_only_fd, 0) == 0)
+ errx(-1, "ftruncate(read_only_fd) succeeded");
+ if (errno != EINVAL)
+ err(1, "ftruncate(read_only_fd) returned wrong error");
+ close(read_only_fd);
+
+ /*
+ * Make sure that ftruncate on sockets doesn't work.
+ */
+ fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0)
+ err(1, "socket(PF_UNIX, SOCK_STREAM, 0)");
+ if (ftruncate(fd, 0) == 0)
+ errx(-1, "ftruncate(socket) succeeded");
+ if (errno != EINVAL)
+ err(1, "ftruncate(socket) returned wrong error");
+ close(fd);
+
+ /*
+ * Make sure that ftruncate on pipes doesn't work.
+ */
+ if (pipe(fds) < 0)
+ err(1, "pipe");
+ if (ftruncate(fds[0], 0) == 0)
+ errx(-1, "ftruncate(pipe) succeeded");
+ if (errno != EINVAL)
+ err(1, "ftruncate(pipe) returned wrong error");
+ close(fds[0]);
+ close(fds[1]);
+
+ /*
+ * Make sure that ftruncate on kqueues doesn't work.
+ */
+ fd = kqueue();
+ if (fd < 0)
+ err(1, "kqueue");
+ if (ftruncate(fds[0], 0) == 0)
+ errx(-1, "ftruncate(kqueue) succeeded");
+ if (errno != EINVAL)
+ err(1, "ftruncate(kqueue) returned wrong error");
+ close(fd);
+
+ return (0);
+}
diff --git a/tests/sys/file/newfileops_on_fork_test.c b/tests/sys/file/newfileops_on_fork_test.c
new file mode 100644
index 000000000000..fe5e9938599d
--- /dev/null
+++ b/tests/sys/file/newfileops_on_fork_test.c
@@ -0,0 +1,122 @@
+/*-
+ * Copyright (c) 2009 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed at the University of Cambridge Computer
+ * Laboratory with support from a grant from Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * When a multi-threaded application calls fork(2) from one thread while
+ * another thread is blocked in accept(2), we prefer that the file descriptor
+ * to be returned by accept(2) not appear in the child process. Test this by
+ * creating a thread blocked in accept(2), then forking a child and seeing if
+ * the fd it would have returned is defined in the child or not.
+ */
+
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+
+#include <err.h>
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifndef PORT
+#define PORT 9000
+#endif
+
+static int listen_fd;
+
+static void *
+do_accept(__unused void *arg)
+{
+ int accept_fd;
+
+ accept_fd = accept(listen_fd, NULL, NULL);
+ if (accept_fd < 0)
+ err(1, "accept");
+
+ close(accept_fd);
+ return (NULL);
+}
+
+static void
+do_fork(void)
+{
+ int pid;
+
+ pid = fork();
+ if (pid < 0)
+ err(1, "fork");
+ if (pid > 0) {
+ waitpid(pid, NULL, 0);
+ exit(0);
+ }
+
+ /*
+ * We will call ftruncate(2) on the next available file descriptor,
+ * listen_fd+1, and get back EBADF if it's not a valid descriptor,
+ * and EINVAL if it is. This (currently) works fine in practice.
+ */
+ if (ftruncate(listen_fd + 1, 0) < 0) {
+ if (errno == EBADF)
+ exit(0);
+ else if (errno == EINVAL)
+ errx(1, "file descriptor still open in child");
+ else
+ err(1, "unexpected error");
+ } else
+ errx(1, "ftruncate succeeded");
+}
+
+int
+main(void)
+{
+ struct sockaddr_in sin;
+ pthread_t accept_thread;
+
+ listen_fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (listen_fd < 0)
+ err(1, "socket");
+ bzero(&sin, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof(sin);
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sin.sin_port = htons(PORT);
+ if (bind(listen_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+ err(1, "bind");
+ if (listen(listen_fd, -1) <0)
+ err(1, "listen");
+ if (pthread_create(&accept_thread, NULL, do_accept, NULL) != 0)
+ err(1, "pthread_create");
+ sleep(1); /* Easier than using a CV. */
+ do_fork();
+ exit(0);
+}
diff --git a/tests/sys/file/path_test.c b/tests/sys/file/path_test.c
new file mode 100644
index 000000000000..b3b8b7cebd4d
--- /dev/null
+++ b/tests/sys/file/path_test.c
@@ -0,0 +1,1039 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2021 The FreeBSD Foundation
+ *
+ * This software was developed by Mark Johnston under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Basic regression tests for handling of O_PATH descriptors.
+ */
+
+#include <sys/param.h>
+#include <sys/capsicum.h>
+#include <sys/event.h>
+#include <sys/ioctl.h>
+#include <sys/memrange.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+
+#include <aio.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#define FMT_ERR(s) s ": %s", strerror(errno)
+
+#define CHECKED_CLOSE(fd) \
+ ATF_REQUIRE_MSG(close(fd) == 0, FMT_ERR("close"))
+
+/* Create a temporary regular file containing some data. */
+static void
+mktfile(char path[PATH_MAX], const char *template)
+{
+ char buf[BUFSIZ];
+ int fd;
+
+ snprintf(path, PATH_MAX, "%s", template);
+ fd = mkstemp(path);
+ ATF_REQUIRE_MSG(fd >= 0, FMT_ERR("mkstemp"));
+ memset(buf, 0, sizeof(buf));
+ ATF_REQUIRE_MSG(write(fd, buf, sizeof(buf)) == sizeof(buf),
+ FMT_ERR("write"));
+ CHECKED_CLOSE(fd);
+}
+
+/* Make a temporary directory. */
+static void
+mktdir(char path[PATH_MAX], const char *template)
+{
+ snprintf(path, PATH_MAX, "%s", template);
+ ATF_REQUIRE_MSG(mkdtemp(path) == path, FMT_ERR("mkdtemp"));
+}
+
+/* Wait for a child process to exit with status 0. */
+static void
+waitchild(pid_t child, int exstatus)
+{
+ int error, status;
+
+ error = waitpid(child, &status, 0);
+ ATF_REQUIRE_MSG(error != -1, FMT_ERR("waitpid"));
+ ATF_REQUIRE_MSG(WIFEXITED(status), "child exited abnormally, status %d",
+ status);
+ ATF_REQUIRE_MSG(WEXITSTATUS(status) == exstatus,
+ "child exit status is %d, expected %d",
+ WEXITSTATUS(status), exstatus);
+}
+
+ATF_TC_WITHOUT_HEAD(path_access);
+ATF_TC_BODY(path_access, tc)
+{
+ char path[PATH_MAX];
+ struct stat sb;
+ struct timespec ts[2];
+ struct timeval tv[2];
+ int pathfd;
+
+ mktfile(path, "path_access.XXXXXX");
+
+ pathfd = open(path, O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ ATF_REQUIRE_ERRNO(EBADF, fchmod(pathfd, 0666) == -1);
+ ATF_REQUIRE_ERRNO(EBADF, fchown(pathfd, getuid(), getgid()) == -1);
+ ATF_REQUIRE_ERRNO(EBADF, fchflags(pathfd, UF_NODUMP) == -1);
+ memset(tv, 0, sizeof(tv));
+ ATF_REQUIRE_ERRNO(EBADF, futimes(pathfd, tv) == -1);
+ memset(ts, 0, sizeof(ts));
+ ATF_REQUIRE_ERRNO(EBADF, futimens(pathfd, ts) == -1);
+
+ /* fpathconf(2) and fstat(2) are permitted. */
+ ATF_REQUIRE_MSG(fstat(pathfd, &sb) == 0, FMT_ERR("fstat"));
+ ATF_REQUIRE_MSG(fpathconf(pathfd, _PC_LINK_MAX) != -1,
+ FMT_ERR("fpathconf"));
+
+ CHECKED_CLOSE(pathfd);
+}
+
+/* Basic tests to verify that AIO operations fail. */
+ATF_TC_WITHOUT_HEAD(path_aio);
+ATF_TC_BODY(path_aio, tc)
+{
+ struct aiocb aio;
+ char buf[BUFSIZ], path[PATH_MAX];
+ int pathfd;
+
+ mktfile(path, "path_aio.XXXXXX");
+
+ pathfd = open(path, O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ memset(&aio, 0, sizeof(aio));
+ aio.aio_buf = buf;
+ aio.aio_nbytes = sizeof(buf);
+ aio.aio_fildes = pathfd;
+ aio.aio_offset = 0;
+
+ ATF_REQUIRE_ERRNO(EBADF, aio_read(&aio) == -1);
+ ATF_REQUIRE_ERRNO(EBADF, aio_write(&aio) == -1);
+ ATF_REQUIRE_ERRNO(EBADF, aio_fsync(O_SYNC, &aio) == -1);
+ ATF_REQUIRE_ERRNO(EBADF, aio_fsync(O_DSYNC, &aio) == -1);
+
+ CHECKED_CLOSE(pathfd);
+}
+
+/* Basic tests to verify that Capsicum restrictions apply to path fds. */
+ATF_TC_WITHOUT_HEAD(path_capsicum);
+ATF_TC_BODY(path_capsicum, tc)
+{
+ char path[PATH_MAX];
+ cap_rights_t rights;
+ int truefd;
+ pid_t child;
+
+ mktfile(path, "path_capsicum.XXXXXX");
+
+ /* Make sure that filesystem namespace restrictions apply to O_PATH. */
+ child = fork();
+ ATF_REQUIRE_MSG(child != -1, FMT_ERR("fork"));
+ if (child == 0) {
+ if (cap_enter() != 0)
+ _exit(1);
+ if (open(path, O_PATH) >= 0)
+ _exit(2);
+ if (errno != ECAPMODE)
+ _exit(3);
+ if (open("/usr/bin/true", O_PATH | O_EXEC) >= 0)
+ _exit(4);
+ if (errno != ECAPMODE)
+ _exit(5);
+ _exit(0);
+ }
+ waitchild(child, 0);
+
+ /* Make sure that CAP_FEXECVE is required. */
+ child = fork();
+ ATF_REQUIRE_MSG(child != -1, FMT_ERR("fork"));
+ if (child == 0) {
+ truefd = open("/usr/bin/true", O_PATH | O_EXEC);
+ if (truefd < 0)
+ _exit(1);
+ cap_rights_init(&rights);
+ if (cap_rights_limit(truefd, &rights) != 0)
+ _exit(2);
+ (void)fexecve(truefd,
+ (char * const[]){__DECONST(char *, "/usr/bin/true"), NULL},
+ NULL);
+ if (errno != ENOTCAPABLE)
+ _exit(3);
+ _exit(4);
+ }
+ waitchild(child, 4);
+}
+
+/*
+ * Check that a pathfd can be converted to a regular fd using openat() in
+ * capability mode, but that rights on the pathfd are respected.
+ */
+ATF_TC_WITHOUT_HEAD(path_capsicum_empty);
+ATF_TC_BODY(path_capsicum_empty, tc)
+{
+ char path[PATH_MAX];
+ cap_rights_t rights;
+ int dfd, fd, pathfd, pathdfd;
+
+ mktfile(path, "path_capsicum.XXXXXX");
+
+ pathdfd = open(".", O_PATH);
+ ATF_REQUIRE_MSG(pathdfd >= 0, FMT_ERR("open"));
+ pathfd = open(path, O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ ATF_REQUIRE(cap_enter() == 0);
+
+ dfd = openat(pathdfd, "", O_DIRECTORY | O_EMPTY_PATH);
+ ATF_REQUIRE(dfd >= 0);
+ CHECKED_CLOSE(dfd);
+
+ /*
+ * CAP_READ and CAP_LOOKUP should be sufficient to open a directory.
+ */
+ cap_rights_init(&rights, CAP_READ, CAP_LOOKUP);
+ ATF_REQUIRE(cap_rights_limit(pathdfd, &rights) == 0);
+ dfd = openat(pathdfd, "", O_DIRECTORY | O_EMPTY_PATH);
+ ATF_REQUIRE(dfd >= 0);
+ CHECKED_CLOSE(dfd);
+
+ /*
+ * ... CAP_READ on its own is not.
+ */
+ cap_rights_init(&rights, CAP_READ);
+ ATF_REQUIRE(cap_rights_limit(pathdfd, &rights) == 0);
+ dfd = openat(pathdfd, "", O_DIRECTORY | O_EMPTY_PATH);
+ ATF_REQUIRE_ERRNO(ENOTCAPABLE, dfd == -1);
+
+ /*
+ * Now try with a regular file.
+ */
+ fd = openat(pathfd, "", O_RDWR | O_EMPTY_PATH);
+ ATF_REQUIRE(fd >= 0);
+ CHECKED_CLOSE(fd);
+
+ cap_rights_init(&rights, CAP_READ, CAP_LOOKUP, CAP_WRITE);
+ ATF_REQUIRE(cap_rights_limit(pathfd, &rights) == 0);
+ fd = openat(pathfd, "", O_RDWR | O_EMPTY_PATH | O_APPEND);
+ ATF_REQUIRE(fd >= 0);
+ CHECKED_CLOSE(fd);
+
+ /*
+ * CAP_SEEK is needed to open a file for writing without O_APPEND.
+ */
+ cap_rights_init(&rights, CAP_READ, CAP_LOOKUP, CAP_WRITE);
+ ATF_REQUIRE(cap_rights_limit(pathfd, &rights) == 0);
+ fd = openat(pathfd, "", O_RDWR | O_EMPTY_PATH);
+ ATF_REQUIRE_ERRNO(ENOTCAPABLE, fd == -1);
+
+ /*
+ * CAP_LOOKUP isn't sufficient to open a file for reading.
+ */
+ cap_rights_init(&rights, CAP_LOOKUP);
+ ATF_REQUIRE(cap_rights_limit(pathfd, &rights) == 0);
+ fd = openat(pathfd, "", O_RDONLY | O_EMPTY_PATH);
+ ATF_REQUIRE_ERRNO(ENOTCAPABLE, fd == -1);
+
+ CHECKED_CLOSE(pathfd);
+ CHECKED_CLOSE(pathdfd);
+}
+
+/* Make sure that ptrace(PT_COREDUMP) cannot be used to write to a path fd. */
+ATF_TC_WITHOUT_HEAD(path_coredump);
+ATF_TC_BODY(path_coredump, tc)
+{
+ char path[PATH_MAX];
+ struct ptrace_coredump pc;
+ int error, pathfd, status;
+ pid_t child;
+
+ mktdir(path, "path_coredump.XXXXXX");
+
+ child = fork();
+ ATF_REQUIRE_MSG(child != -1, FMT_ERR("fork"));
+ if (child == 0) {
+ while (true)
+ (void)sleep(1);
+ }
+
+ pathfd = open(path, O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ error = ptrace(PT_ATTACH, child, 0, 0);
+ ATF_REQUIRE_MSG(error == 0, FMT_ERR("ptrace"));
+ error = waitpid(child, &status, 0);
+ ATF_REQUIRE_MSG(error != -1, FMT_ERR("waitpid"));
+ ATF_REQUIRE_MSG(WIFSTOPPED(status), "unexpected status %d", status);
+
+ pc.pc_fd = pathfd;
+ pc.pc_flags = 0;
+ pc.pc_limit = 0;
+ error = ptrace(PT_COREDUMP, child, (void *)&pc, sizeof(pc));
+ ATF_REQUIRE_ERRNO(EBADF, error == -1);
+
+ error = ptrace(PT_DETACH, child, 0, 0);
+ ATF_REQUIRE_MSG(error == 0, FMT_ERR("ptrace"));
+
+ ATF_REQUIRE_MSG(kill(child, SIGKILL) == 0, FMT_ERR("kill"));
+
+ CHECKED_CLOSE(pathfd);
+}
+
+/* Verify operations on directory path descriptors. */
+ATF_TC_WITHOUT_HEAD(path_directory);
+ATF_TC_BODY(path_directory, tc)
+{
+ struct dirent de;
+ struct stat sb;
+ char path[PATH_MAX];
+ int fd, pathfd;
+
+ mktdir(path, "path_directory.XXXXXX");
+
+ pathfd = open(path, O_PATH | O_DIRECTORY);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ /* Should not be possible to list directory entries. */
+ ATF_REQUIRE_ERRNO(EBADF,
+ getdirentries(pathfd, (char *)&de, sizeof(de), NULL) == -1);
+
+ /* It should be possible to create files under pathfd. */
+ fd = openat(pathfd, "test", O_RDWR | O_CREAT, 0600);
+ ATF_REQUIRE_MSG(fd >= 0, FMT_ERR("open"));
+ ATF_REQUIRE_MSG(fstatat(pathfd, "test", &sb, 0) == 0,
+ FMT_ERR("fstatat"));
+ CHECKED_CLOSE(fd);
+
+ /* ... but doing so requires write access. */
+ if (geteuid() != 0) {
+ ATF_REQUIRE_ERRNO(EBADF, fchmod(pathfd, 0500) == -1);
+ ATF_REQUIRE_MSG(chmod(path, 0500) == 0, FMT_ERR("chmod"));
+ ATF_REQUIRE_ERRNO(EACCES,
+ openat(pathfd, "test2", O_RDWR | O_CREAT, 0600) < 0);
+ }
+
+ /* fchdir(2) is permitted. */
+ ATF_REQUIRE_MSG(fchdir(pathfd) == 0, FMT_ERR("fchdir"));
+
+ CHECKED_CLOSE(pathfd);
+}
+
+/* Verify access permission checking for a directory path fd. */
+ATF_TC_WITH_CLEANUP(path_directory_not_root);
+ATF_TC_HEAD(path_directory_not_root, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "unprivileged");
+}
+ATF_TC_BODY(path_directory_not_root, tc)
+{
+ char path[PATH_MAX];
+ int pathfd;
+
+ mktdir(path, "path_directory.XXXXXX");
+
+ pathfd = open(path, O_PATH | O_DIRECTORY);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ ATF_REQUIRE_ERRNO(EBADF, fchmod(pathfd, 0500) == -1);
+ ATF_REQUIRE_MSG(chmod(path, 0500) == 0, FMT_ERR("chmod"));
+ ATF_REQUIRE_ERRNO(EACCES,
+ openat(pathfd, "test2", O_RDWR | O_CREAT, 0600) < 0);
+
+ CHECKED_CLOSE(pathfd);
+}
+ATF_TC_CLEANUP(path_directory_not_root, tc)
+{
+}
+
+/* Validate system calls that handle AT_EMPTY_PATH. */
+ATF_TC_WITHOUT_HEAD(path_empty);
+ATF_TC_BODY(path_empty, tc)
+{
+ char path[PATH_MAX];
+ struct timespec ts[2];
+ struct stat sb;
+ int pathfd;
+
+ mktfile(path, "path_empty.XXXXXX");
+
+ pathfd = open(path, O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ /* Various *at operations should work on path fds. */
+ ATF_REQUIRE_MSG(faccessat(pathfd, "", F_OK, AT_EMPTY_PATH) == 0,
+ FMT_ERR("faccessat"));
+ ATF_REQUIRE_MSG(chflagsat(pathfd, "", UF_NODUMP, AT_EMPTY_PATH) == 0,
+ FMT_ERR("chflagsat"));
+ ATF_REQUIRE_MSG(fchmodat(pathfd, "", 0600, AT_EMPTY_PATH) == 0,
+ FMT_ERR("fchmodat"));
+ ATF_REQUIRE_MSG(fchownat(pathfd, "", getuid(), getgid(),
+ AT_EMPTY_PATH) == 0, FMT_ERR("fchownat"));
+ ATF_REQUIRE_MSG(fstatat(pathfd, "", &sb, AT_EMPTY_PATH) == 0,
+ FMT_ERR("fstatat"));
+ ATF_REQUIRE_MSG(sb.st_size == BUFSIZ,
+ "unexpected size %ju", (uintmax_t)sb.st_size);
+ memset(ts, 0, sizeof(ts));
+ ATF_REQUIRE_MSG(utimensat(pathfd, "", ts, AT_EMPTY_PATH) == 0,
+ FMT_ERR("utimensat"));
+
+ CHECKED_CLOSE(pathfd);
+}
+
+/* Verify that various operations on a path fd have access checks. */
+ATF_TC_WITH_CLEANUP(path_empty_not_root);
+ATF_TC_HEAD(path_empty_not_root, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "unprivileged");
+}
+ATF_TC_BODY(path_empty_not_root, tc)
+{
+ int pathfd;
+
+ pathfd = open("/dev/null", O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ ATF_REQUIRE_ERRNO(EPERM,
+ chflagsat(pathfd, "", UF_NODUMP, AT_EMPTY_PATH) == -1);
+ ATF_REQUIRE_ERRNO(EPERM,
+ fchownat(pathfd, "", getuid(), getgid(), AT_EMPTY_PATH) == -1);
+ ATF_REQUIRE_ERRNO(EPERM,
+ fchmodat(pathfd, "", 0600, AT_EMPTY_PATH) == -1);
+ ATF_REQUIRE_ERRNO(EPERM,
+ linkat(pathfd, "", AT_FDCWD, "test", AT_EMPTY_PATH) == -1);
+
+ CHECKED_CLOSE(pathfd);
+}
+ATF_TC_CLEANUP(path_empty_not_root, tc)
+{
+}
+
+/* Test linkat(2) with AT_EMPTY_PATH, which requires privileges. */
+ATF_TC_WITH_CLEANUP(path_empty_root);
+ATF_TC_HEAD(path_empty_root, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(path_empty_root, tc)
+{
+ char path[PATH_MAX];
+ struct stat sb, sb2;
+ int pathfd;
+
+ mktfile(path, "path_empty_root.XXXXXX");
+
+ pathfd = open(path, O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+ ATF_REQUIRE_MSG(fstatat(pathfd, "", &sb, AT_EMPTY_PATH) == 0,
+ FMT_ERR("fstatat"));
+
+ ATF_REQUIRE_MSG(linkat(pathfd, "", AT_FDCWD, "test", AT_EMPTY_PATH) ==
+ 0, FMT_ERR("linkat"));
+ ATF_REQUIRE_MSG(fstatat(AT_FDCWD, "test", &sb2, 0) == 0,
+ FMT_ERR("fstatat"));
+ ATF_REQUIRE_MSG(sb.st_dev == sb2.st_dev, "st_dev mismatch");
+ ATF_REQUIRE_MSG(sb.st_ino == sb2.st_ino, "st_ino mismatch");
+
+ CHECKED_CLOSE(pathfd);
+
+}
+ATF_TC_CLEANUP(path_empty_root, tc)
+{
+}
+
+/* poll(2) never returns an event for path fds, but kevent(2) does. */
+ATF_TC_WITHOUT_HEAD(path_event);
+ATF_TC_BODY(path_event, tc)
+{
+ char buf[BUFSIZ], path[PATH_MAX];
+ struct kevent ev;
+ struct pollfd pollfd;
+ int kq, pathfd;
+
+ mktfile(path, "path_event.XXXXXX");
+
+ pathfd = open(path, O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ /* poll(2) should return POLLNVAL. */
+ pollfd.fd = pathfd;
+ pollfd.events = POLLIN;
+ pollfd.revents = 0;
+ ATF_REQUIRE_MSG(poll(&pollfd, 1, 0) == 1, FMT_ERR("poll"));
+ ATF_REQUIRE_MSG(pollfd.revents == POLLNVAL, "unexpected revents %x",
+ pollfd.revents);
+ pollfd.events = POLLOUT;
+ pollfd.revents = 0;
+ ATF_REQUIRE_MSG(poll(&pollfd, 1, 0) == 1, FMT_ERR("poll"));
+ ATF_REQUIRE_MSG(pollfd.revents == POLLNVAL, "unexpected revents %x",
+ pollfd.revents);
+
+ /* Try to get a EVFILT_READ event through a path fd. */
+ kq = kqueue();
+ ATF_REQUIRE_MSG(kq >= 0, FMT_ERR("kqueue"));
+ EV_SET(&ev, pathfd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0);
+ ATF_REQUIRE_MSG(kevent(kq, &ev, 1, NULL, 0, NULL) == 0,
+ FMT_ERR("kevent"));
+ ATF_REQUIRE_MSG(kevent(kq, NULL, 0, &ev, 1, NULL) == 1,
+ FMT_ERR("kevent"));
+ ATF_REQUIRE_MSG((ev.flags & EV_ERROR) == 0, "EV_ERROR is set");
+ ATF_REQUIRE_MSG(ev.data == sizeof(buf),
+ "data is %jd", (intmax_t)ev.data);
+ EV_SET(&ev, pathfd, EVFILT_READ, EV_DELETE, 0, 0, 0);
+ ATF_REQUIRE_MSG(kevent(kq, &ev, 1, NULL, 0, NULL) == 0,
+ FMT_ERR("kevent"));
+
+ /* Try to get a EVFILT_VNODE/NOTE_DELETE event through a path fd. */
+ EV_SET(&ev, pathfd, EVFILT_VNODE, EV_ADD | EV_ENABLE, NOTE_DELETE, 0,
+ 0);
+ ATF_REQUIRE_MSG(kevent(kq, &ev, 1, NULL, 0, NULL) == 0,
+ FMT_ERR("kevent"));
+ ATF_REQUIRE_MSG(funlinkat(AT_FDCWD, path, pathfd, 0) == 0,
+ FMT_ERR("funlinkat"));
+ ATF_REQUIRE_MSG(kevent(kq, NULL, 0, &ev, 1, NULL) == 1,
+ FMT_ERR("kevent"));
+ ATF_REQUIRE_MSG(ev.fflags == NOTE_DELETE,
+ "unexpected fflags %#x", ev.fflags);
+ EV_SET(&ev, pathfd, EVFILT_VNODE, EV_DELETE, 0, 0, 0);
+ ATF_REQUIRE_MSG(kevent(kq, &ev, 1, NULL, 0, NULL) == 0,
+ FMT_ERR("kevent"));
+
+ CHECKED_CLOSE(kq);
+ CHECKED_CLOSE(pathfd);
+}
+
+/* Check various fcntl(2) operations on a path desriptor. */
+ATF_TC_WITHOUT_HEAD(path_fcntl);
+ATF_TC_BODY(path_fcntl, tc)
+{
+ char path[PATH_MAX];
+ int flags, pathfd, pathfd2;
+
+ mktfile(path, "path_fcntl.XXXXXX");
+
+ pathfd = open(path, O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ /* O_PATH should appear in the fd flags. */
+ flags = fcntl(pathfd, F_GETFL);
+ ATF_REQUIRE_MSG(flags != -1, FMT_ERR("fcntl"));
+ ATF_REQUIRE_MSG((flags & O_PATH) != 0, "O_PATH not set");
+
+ ATF_REQUIRE_ERRNO(EBADF,
+ fcntl(pathfd, F_SETFL, flags & ~O_PATH));
+ ATF_REQUIRE_ERRNO(EBADF,
+ fcntl(pathfd, F_SETFL, flags | O_APPEND));
+
+ /* A dup'ed O_PATH fd had better have O_PATH set too. */
+ pathfd2 = fcntl(pathfd, F_DUPFD, 0);
+ ATF_REQUIRE_MSG(pathfd2 >= 0, FMT_ERR("fcntl"));
+ flags = fcntl(pathfd2, F_GETFL);
+ ATF_REQUIRE_MSG(flags != -1, FMT_ERR("fcntl"));
+ ATF_REQUIRE_MSG((flags & O_PATH) != 0, "O_PATH not set");
+ CHECKED_CLOSE(pathfd2);
+
+ /* Double check with dup(2). */
+ pathfd2 = dup(pathfd);
+ ATF_REQUIRE_MSG(pathfd2 >= 0, FMT_ERR("dup"));
+ flags = fcntl(pathfd2, F_GETFL);
+ ATF_REQUIRE_MSG(flags != -1, FMT_ERR("fcntl"));
+ ATF_REQUIRE_MSG((flags & O_PATH) != 0, "O_PATH not set");
+ CHECKED_CLOSE(pathfd2);
+
+ /* It should be possible to set O_CLOEXEC. */
+ ATF_REQUIRE_MSG(fcntl(pathfd, F_SETFD, FD_CLOEXEC) == 0,
+ FMT_ERR("fcntl"));
+ ATF_REQUIRE_MSG(fcntl(pathfd, F_GETFD) == FD_CLOEXEC,
+ FMT_ERR("fcntl"));
+
+ CHECKED_CLOSE(pathfd);
+}
+
+/* Verify that we can execute a file opened with O_PATH. */
+ATF_TC_WITHOUT_HEAD(path_fexecve);
+ATF_TC_BODY(path_fexecve, tc)
+{
+ char path[PATH_MAX];
+ pid_t child;
+ int fd, pathfd;
+
+ child = fork();
+ ATF_REQUIRE_MSG(child != -1, FMT_ERR("fork"));
+ if (child == 0) {
+ pathfd = open("/usr/bin/true", O_PATH | O_EXEC);
+ if (pathfd < 0)
+ _exit(1);
+ fexecve(pathfd,
+ (char * const[]){__DECONST(char *, "/usr/bin/true"), NULL},
+ NULL);
+ _exit(2);
+ }
+ waitchild(child, 0);
+
+ /*
+ * Also verify that access permissions are checked when opening with
+ * O_PATH.
+ */
+ snprintf(path, sizeof(path), "path_fexecve.XXXXXX");
+ ATF_REQUIRE_MSG(mktemp(path) == path, FMT_ERR("mktemp"));
+
+ fd = open(path, O_CREAT | O_RDONLY, 0600);
+ ATF_REQUIRE_MSG(fd >= 0, FMT_ERR("open"));
+
+ pathfd = open(path, O_PATH | O_EXEC);
+ ATF_REQUIRE_ERRNO(EACCES, pathfd < 0);
+}
+
+/* Make sure that O_PATH restrictions apply to named pipes as well. */
+ATF_TC_WITHOUT_HEAD(path_fifo);
+ATF_TC_BODY(path_fifo, tc)
+{
+ char path[PATH_MAX], buf[BUFSIZ];
+ struct kevent ev;
+ int kq, pathfd;
+
+ snprintf(path, sizeof(path), "path_fifo.XXXXXX");
+ ATF_REQUIRE_MSG(mktemp(path) == path, FMT_ERR("mktemp"));
+
+ ATF_REQUIRE_MSG(mkfifo(path, 0666) == 0, FMT_ERR("mkfifo"));
+
+ pathfd = open(path, O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+ memset(buf, 0, sizeof(buf));
+ ATF_REQUIRE_ERRNO(EBADF, write(pathfd, buf, sizeof(buf)));
+ ATF_REQUIRE_ERRNO(EBADF, read(pathfd, buf, sizeof(buf)));
+
+ kq = kqueue();
+ ATF_REQUIRE_MSG(kq >= 0, FMT_ERR("kqueue"));
+ EV_SET(&ev, pathfd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0);
+ ATF_REQUIRE_ERRNO(EBADF, kevent(kq, &ev, 1, NULL, 0, NULL) == -1);
+
+ CHECKED_CLOSE(pathfd);
+}
+
+/* Files may be unlinked using a path fd. */
+ATF_TC_WITHOUT_HEAD(path_funlinkat);
+ATF_TC_BODY(path_funlinkat, tc)
+{
+ char path[PATH_MAX];
+ struct stat sb;
+ int pathfd;
+
+ mktfile(path, "path_rights.XXXXXX");
+
+ pathfd = open(path, O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ ATF_REQUIRE_MSG(funlinkat(AT_FDCWD, path, pathfd, 0) == 0,
+ FMT_ERR("funlinkat"));
+ ATF_REQUIRE_ERRNO(ENOENT, stat(path, &sb) == -1);
+
+ CHECKED_CLOSE(pathfd);
+}
+
+/* Verify that various I/O operations fail on an O_PATH descriptor. */
+ATF_TC_WITHOUT_HEAD(path_io);
+ATF_TC_BODY(path_io, tc)
+{
+ char path[PATH_MAX], path2[PATH_MAX];
+ char buf[BUFSIZ];
+ struct iovec iov;
+ size_t page_size;
+ int error, fd, pathfd, sd[2];
+
+ /* It is allowed to create new files with O_PATH. */
+ snprintf(path, sizeof(path), "path_io.XXXXXX");
+ ATF_REQUIRE_MSG(mktemp(path) == path, FMT_ERR("mktemp"));
+ pathfd = open(path, O_PATH | O_CREAT, 0600);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open(O_PATH|O_CREAT)"));
+ /* Ensure that this is indeed O_PATH fd */
+ ATF_REQUIRE_ERRNO(EBADF, write(pathfd, path, strlen(path)) == -1);
+ CHECKED_CLOSE(pathfd);
+
+ /* Create a non-empty file for use in the rest of the tests. */
+ mktfile(path, "path_io.XXXXXX");
+
+ pathfd = open(path, O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ /* Make sure that basic I/O operations aren't possible. */
+ iov.iov_base = path;
+ iov.iov_len = strlen(path);
+ ATF_REQUIRE_ERRNO(EBADF,
+ write(pathfd, iov.iov_base, iov.iov_len) == -1);
+ ATF_REQUIRE_ERRNO(EBADF,
+ pwrite(pathfd, iov.iov_base, iov.iov_len, 0) == -1);
+ ATF_REQUIRE_ERRNO(EBADF,
+ writev(pathfd, &iov, 1) == -1);
+ ATF_REQUIRE_ERRNO(EBADF,
+ pwritev(pathfd, &iov, 1, 0) == -1);
+ ATF_REQUIRE_ERRNO(EBADF,
+ read(pathfd, path, 1) == -1);
+ ATF_REQUIRE_ERRNO(EBADF,
+ pread(pathfd, path, 1, 0) == -1);
+ ATF_REQUIRE_ERRNO(EBADF,
+ readv(pathfd, &iov, 1) == -1);
+ ATF_REQUIRE_ERRNO(EBADF,
+ preadv(pathfd, &iov, 1, 0) == -1);
+
+ /* copy_file_range() should not be permitted. */
+ mktfile(path2, "path_io.XXXXXX");
+ fd = open(path2, O_RDWR);
+ ATF_REQUIRE_ERRNO(EBADF,
+ copy_file_range(fd, NULL, pathfd, NULL, sizeof(buf), 0) == -1);
+ ATF_REQUIRE_ERRNO(EBADF,
+ copy_file_range(pathfd, NULL, fd, NULL, sizeof(buf), 0) == -1);
+ CHECKED_CLOSE(fd);
+
+ /* sendfile() should not be permitted. */
+ ATF_REQUIRE_MSG(socketpair(PF_LOCAL, SOCK_STREAM, 0, sd) == 0,
+ FMT_ERR("socketpair"));
+ ATF_REQUIRE_ERRNO(EBADF,
+ sendfile(pathfd, sd[0], 0, 0, NULL, NULL, 0));
+ CHECKED_CLOSE(sd[0]);
+ CHECKED_CLOSE(sd[1]);
+
+ /* No seeking. */
+ ATF_REQUIRE_ERRNO(ESPIPE,
+ lseek(pathfd, 0, SEEK_SET) == -1);
+
+ /* No operations on the file extent. */
+ ATF_REQUIRE_ERRNO(EINVAL,
+ ftruncate(pathfd, 0) == -1);
+ error = posix_fallocate(pathfd, 0, sizeof(buf) * 2);
+ ATF_REQUIRE_MSG(error == ESPIPE, "posix_fallocate() returned %d", error);
+ error = posix_fadvise(pathfd, 0, sizeof(buf), POSIX_FADV_NORMAL);
+ ATF_REQUIRE_MSG(error == ESPIPE, "posix_fadvise() returned %d", error);
+
+ /* mmap() is not allowed. */
+ page_size = getpagesize();
+ ATF_REQUIRE_ERRNO(ENODEV,
+ mmap(NULL, page_size, PROT_READ, MAP_SHARED, pathfd, 0) ==
+ MAP_FAILED);
+ ATF_REQUIRE_ERRNO(ENODEV,
+ mmap(NULL, page_size, PROT_NONE, MAP_SHARED, pathfd, 0) ==
+ MAP_FAILED);
+ ATF_REQUIRE_ERRNO(ENODEV,
+ mmap(NULL, page_size, PROT_READ, MAP_PRIVATE, pathfd, 0) ==
+ MAP_FAILED);
+
+ /* No fsync() or fdatasync(). */
+ ATF_REQUIRE_ERRNO(EBADF, fsync(pathfd) == -1);
+ ATF_REQUIRE_ERRNO(EBADF, fdatasync(pathfd) == -1);
+
+ CHECKED_CLOSE(pathfd);
+}
+
+/* ioctl(2) is not permitted on path fds. */
+ATF_TC_WITHOUT_HEAD(path_ioctl);
+ATF_TC_BODY(path_ioctl, tc)
+{
+ char path[PATH_MAX];
+ struct mem_extract me;
+ int pathfd, val;
+
+ mktfile(path, "path_ioctl.XXXXXX");
+
+ /* Standard file descriptor ioctls should fail. */
+ pathfd = open(path, O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ val = 0;
+ ATF_REQUIRE_ERRNO(EBADF, ioctl(pathfd, FIONBIO, &val) == -1);
+ ATF_REQUIRE_ERRNO(EBADF, ioctl(pathfd, FIONREAD, &val) == -1);
+ ATF_REQUIRE_ERRNO(EBADF, ioctl(pathfd, FIONWRITE, &val) == -1);
+ ATF_REQUIRE_ERRNO(EBADF, ioctl(pathfd, FIONSPACE, &val) == -1);
+
+ CHECKED_CLOSE(pathfd);
+
+ /* Device ioctls should fail. */
+ pathfd = open("/dev/mem", O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ me.me_vaddr = (uintptr_t)&me;
+ ATF_REQUIRE_ERRNO(EBADF, ioctl(pathfd, MEM_EXTRACT_PADDR, &me) == -1);
+
+ CHECKED_CLOSE(pathfd);
+}
+
+ATF_TC_WITHOUT_HEAD(path_lock);
+ATF_TC_BODY(path_lock, tc)
+{
+ char buf[BUFSIZ], path[PATH_MAX];
+ struct flock flk;
+ int fd, pathfd;
+
+ snprintf(path, sizeof(path), "path_rights.XXXXXX");
+ fd = mkostemp(path, O_SHLOCK);
+ ATF_REQUIRE_MSG(fd >= 0, FMT_ERR("mkostemp"));
+ memset(buf, 0, sizeof(buf));
+ ATF_REQUIRE_MSG(write(fd, buf, sizeof(buf)) == sizeof(buf),
+ FMT_ERR("write()"));
+
+ /* Verify that O_EXLOCK is ignored when combined with O_PATH. */
+ pathfd = open(path, O_PATH | O_EXLOCK);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ CHECKED_CLOSE(fd);
+
+ /* flock(2) is prohibited. */
+ ATF_REQUIRE_ERRNO(EOPNOTSUPP, flock(pathfd, LOCK_SH) == -1);
+ ATF_REQUIRE_ERRNO(EOPNOTSUPP, flock(pathfd, LOCK_EX) == -1);
+ ATF_REQUIRE_ERRNO(EOPNOTSUPP, flock(pathfd, LOCK_SH | LOCK_NB) == -1);
+ ATF_REQUIRE_ERRNO(EOPNOTSUPP, flock(pathfd, LOCK_EX | LOCK_NB) == -1);
+ ATF_REQUIRE_ERRNO(EOPNOTSUPP, flock(pathfd, LOCK_UN) == -1);
+
+ /* fcntl(2) file locks are prohibited. */
+ memset(&flk, 0, sizeof(flk));
+ flk.l_whence = SEEK_CUR;
+ ATF_REQUIRE_ERRNO(EBADF, fcntl(pathfd, F_GETLK, &flk) == -1);
+ flk.l_type = F_RDLCK;
+ ATF_REQUIRE_ERRNO(EBADF, fcntl(pathfd, F_SETLK, &flk) == -1);
+ ATF_REQUIRE_ERRNO(EBADF, fcntl(pathfd, F_SETLKW, &flk) == -1);
+ flk.l_type = F_WRLCK;
+ ATF_REQUIRE_ERRNO(EBADF, fcntl(pathfd, F_SETLK, &flk) == -1);
+ ATF_REQUIRE_ERRNO(EBADF, fcntl(pathfd, F_SETLKW, &flk) == -1);
+
+ CHECKED_CLOSE(pathfd);
+}
+
+/*
+ * Verify fstatat(AT_EMPTY_PATH) on non-regular dirfd.
+ * Verify that fstatat(AT_EMPTY_PATH) on NULL path returns EFAULT.
+ */
+ATF_TC_WITHOUT_HEAD(path_pipe_fstatat);
+ATF_TC_BODY(path_pipe_fstatat, tc)
+{
+ struct stat sb;
+ int fd[2];
+
+ ATF_REQUIRE_MSG(pipe(fd) == 0, FMT_ERR("pipe"));
+ ATF_REQUIRE_MSG(fstatat(fd[0], "", &sb, AT_EMPTY_PATH) == 0,
+ FMT_ERR("fstatat pipe"));
+ ATF_REQUIRE_ERRNO(EFAULT, fstatat(fd[0], NULL, &sb,
+ AT_EMPTY_PATH) == -1);
+ CHECKED_CLOSE(fd[0]);
+ CHECKED_CLOSE(fd[1]);
+}
+
+/* Verify that we can send an O_PATH descriptor over a unix socket. */
+ATF_TC_WITHOUT_HEAD(path_rights);
+ATF_TC_BODY(path_rights, tc)
+{
+ char path[PATH_MAX];
+ struct cmsghdr *cmsg;
+ struct msghdr msg;
+ struct iovec iov;
+ int flags, pathfd, pathfd_copy, sd[2];
+ char c;
+
+ ATF_REQUIRE_MSG(socketpair(PF_LOCAL, SOCK_STREAM, 0, sd) == 0,
+ FMT_ERR("socketpair"));
+
+ mktfile(path, "path_rights.XXXXXX");
+
+ pathfd = open(path, O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ /* Package up the O_PATH and send it over the socket pair. */
+ cmsg = malloc(CMSG_SPACE(sizeof(pathfd)));
+ ATF_REQUIRE_MSG(cmsg != NULL, FMT_ERR("malloc"));
+
+ cmsg->cmsg_len = CMSG_LEN(sizeof(pathfd));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ *(int *)(void *)CMSG_DATA(cmsg) = pathfd;
+
+ c = 0;
+ iov.iov_base = &c;
+ iov.iov_len = 1;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cmsg;
+ msg.msg_controllen = CMSG_SPACE(sizeof(pathfd));
+
+ ATF_REQUIRE_MSG(sendmsg(sd[0], &msg, 0) == sizeof(c),
+ FMT_ERR("sendmsg"));
+
+ /* Grab the pathfd copy from the other end of the pair. */
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cmsg;
+ msg.msg_controllen = CMSG_SPACE(sizeof(pathfd));
+
+ ATF_REQUIRE_MSG(recvmsg(sd[1], &msg, 0) == 1,
+ FMT_ERR("recvmsg"));
+ pathfd_copy = *(int *)(void *)CMSG_DATA(cmsg);
+ ATF_REQUIRE_MSG(pathfd_copy != pathfd,
+ "pathfd and pathfd_copy are equal");
+
+ /* Verify that the copy has O_PATH properties. */
+ flags = fcntl(pathfd_copy, F_GETFL);
+ ATF_REQUIRE_MSG(flags != -1, FMT_ERR("fcntl"));
+ ATF_REQUIRE_MSG((flags & O_PATH) != 0, "O_PATH is not set");
+ ATF_REQUIRE_ERRNO(EBADF,
+ read(pathfd_copy, &c, 1) == -1);
+ ATF_REQUIRE_ERRNO(EBADF,
+ write(pathfd_copy, &c, 1) == -1);
+
+ CHECKED_CLOSE(pathfd);
+ CHECKED_CLOSE(pathfd_copy);
+ CHECKED_CLOSE(sd[0]);
+ CHECKED_CLOSE(sd[1]);
+}
+
+/* Verify that a local socket can be opened with O_PATH. */
+ATF_TC_WITHOUT_HEAD(path_unix);
+ATF_TC_BODY(path_unix, tc)
+{
+ char buf[BUFSIZ], path[PATH_MAX];
+ struct kevent ev;
+ struct sockaddr_un sun;
+ struct stat sb;
+ int kq, pathfd, sd;
+
+ snprintf(path, sizeof(path), "path_unix.XXXXXX");
+ ATF_REQUIRE_MSG(mktemp(path) == path, FMT_ERR("mktemp"));
+
+ sd = socket(PF_LOCAL, SOCK_STREAM, 0);
+ ATF_REQUIRE_MSG(sd >= 0, FMT_ERR("socket"));
+
+ memset(&sun, 0, sizeof(sun));
+ sun.sun_family = PF_LOCAL;
+ (void)strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
+ ATF_REQUIRE_MSG(bind(sd, (struct sockaddr *)&sun, SUN_LEN(&sun)) == 0,
+ FMT_ERR("bind"));
+
+ pathfd = open(path, O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ ATF_REQUIRE_MSG(fstatat(pathfd, "", &sb, AT_EMPTY_PATH) == 0,
+ FMT_ERR("fstatat"));
+ ATF_REQUIRE_MSG(sb.st_mode & S_IFSOCK, "socket mode %#x", sb.st_mode);
+ ATF_REQUIRE_MSG(sb.st_ino != 0, "socket has inode number 0");
+
+ memset(buf, 0, sizeof(buf));
+ ATF_REQUIRE_ERRNO(EBADF, write(pathfd, buf, sizeof(buf)));
+ ATF_REQUIRE_ERRNO(EBADF, read(pathfd, buf, sizeof(buf)));
+
+ /* kevent() is disallowed with sockets. */
+ kq = kqueue();
+ ATF_REQUIRE_MSG(kq >= 0, FMT_ERR("kqueue"));
+ EV_SET(&ev, pathfd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0);
+ ATF_REQUIRE_ERRNO(EBADF, kevent(kq, &ev, 1, NULL, 0, NULL) == -1);
+
+ /* Should not be able to open a socket without O_PATH. */
+ ATF_REQUIRE_ERRNO(EOPNOTSUPP, openat(pathfd, "", O_EMPTY_PATH) == -1);
+
+ ATF_REQUIRE_MSG(funlinkat(AT_FDCWD, path, pathfd, 0) == 0,
+ FMT_ERR("funlinkat"));
+
+ CHECKED_CLOSE(sd);
+ CHECKED_CLOSE(pathfd);
+}
+
+/*
+ * Check that we can perform operations using an O_PATH fd for an unlinked file.
+ */
+ATF_TC_WITHOUT_HEAD(path_unlinked);
+ATF_TC_BODY(path_unlinked, tc)
+{
+ char path[PATH_MAX];
+ struct stat sb;
+ int pathfd;
+
+ mktfile(path, "path_rights.XXXXXX");
+
+ pathfd = open(path, O_PATH);
+ ATF_REQUIRE_MSG(pathfd >= 0, FMT_ERR("open"));
+
+ ATF_REQUIRE_MSG(fstatat(pathfd, "", &sb, AT_EMPTY_PATH) == 0,
+ FMT_ERR("fstatat"));
+ ATF_REQUIRE(sb.st_nlink == 1);
+ ATF_REQUIRE_MSG(fstat(pathfd, &sb) == 0, FMT_ERR("fstat"));
+ ATF_REQUIRE(sb.st_nlink == 1);
+
+ ATF_REQUIRE_MSG(unlink(path) == 0, FMT_ERR("unlink"));
+
+ ATF_REQUIRE_MSG(fstatat(pathfd, "", &sb, AT_EMPTY_PATH) == 0,
+ FMT_ERR("fstatat"));
+ ATF_REQUIRE(sb.st_nlink == 0);
+ ATF_REQUIRE_MSG(fstat(pathfd, &sb) == 0, FMT_ERR("fstat"));
+ ATF_REQUIRE(sb.st_nlink == 0);
+
+ CHECKED_CLOSE(pathfd);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, path_access);
+ ATF_TP_ADD_TC(tp, path_aio);
+ ATF_TP_ADD_TC(tp, path_capsicum);
+ ATF_TP_ADD_TC(tp, path_capsicum_empty);
+ ATF_TP_ADD_TC(tp, path_coredump);
+ ATF_TP_ADD_TC(tp, path_directory);
+ ATF_TP_ADD_TC(tp, path_directory_not_root);
+ ATF_TP_ADD_TC(tp, path_empty);
+ ATF_TP_ADD_TC(tp, path_empty_not_root);
+ ATF_TP_ADD_TC(tp, path_empty_root);
+ ATF_TP_ADD_TC(tp, path_event);
+ ATF_TP_ADD_TC(tp, path_fcntl);
+ ATF_TP_ADD_TC(tp, path_fexecve);
+ ATF_TP_ADD_TC(tp, path_fifo);
+ ATF_TP_ADD_TC(tp, path_funlinkat);
+ ATF_TP_ADD_TC(tp, path_io);
+ ATF_TP_ADD_TC(tp, path_ioctl);
+ ATF_TP_ADD_TC(tp, path_lock);
+ ATF_TP_ADD_TC(tp, path_pipe_fstatat);
+ ATF_TP_ADD_TC(tp, path_rights);
+ ATF_TP_ADD_TC(tp, path_unix);
+ ATF_TP_ADD_TC(tp, path_unlinked);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/fs/Makefile b/tests/sys/fs/Makefile
new file mode 100644
index 000000000000..e36a97d2335a
--- /dev/null
+++ b/tests/sys/fs/Makefile
@@ -0,0 +1,29 @@
+.include <src.opts.mk>
+.include <bsd.compiler.mk>
+
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/fs
+
+TESTSRC= ${SRCTOP}/contrib/netbsd-tests/fs
+
+#TESTS_SUBDIRS+= nullfs # XXX: needs rump
+# fusefs tests cannot be compiled/used without the googletest infrastructure.
+.if ${COMPILER_FEATURES:Mc++14} && ${MK_GOOGLETEST} != "no"
+TESTS_SUBDIRS+= fusefs
+.endif
+TESTS_SUBDIRS+= tarfs
+TESTS_SUBDIRS+= tmpfs
+
+${PACKAGE}FILES+= h_funcs.subr
+${PACKAGE}FILESDIR= ${TESTSDIR}
+
+CLEANFILES+= h_funcs.subr
+CLEANFILES+= h_funcs.subr.tmp
+
+h_funcs.subr: ${TESTSRC}/h_funcs.subr
+ cat ${.ALLSRC} | \
+ sed -e '/atf_require_prog mount_$${name}/d' >>${.TARGET}.tmp
+ mv ${.TARGET}.tmp ${.TARGET}
+
+.include <bsd.test.mk>
diff --git a/tests/sys/fs/Makefile.depend b/tests/sys/fs/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/fs/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/fs/Makefile.inc b/tests/sys/fs/Makefile.inc
new file mode 100644
index 000000000000..01b5f23410c8
--- /dev/null
+++ b/tests/sys/fs/Makefile.inc
@@ -0,0 +1 @@
+.include "../Makefile.inc"
diff --git a/tests/sys/fs/fusefs/Makefile b/tests/sys/fs/fusefs/Makefile
new file mode 100644
index 000000000000..a21512798597
--- /dev/null
+++ b/tests/sys/fs/fusefs/Makefile
@@ -0,0 +1,100 @@
+.include <bsd.compiler.mk>
+
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/fs/fusefs
+
+ATF_TESTS_SH+= ctl
+
+# We could simply link all of these files into a single executable. But since
+# Kyua treats googletest programs as plain tests, it's better to separate them
+# out, so we get more granular reporting.
+GTESTS+= access
+GTESTS+= allow_other
+GTESTS+= bad_server
+GTESTS+= bmap
+GTESTS+= cache
+GTESTS+= copy_file_range
+GTESTS+= create
+GTESTS+= default_permissions
+GTESTS+= default_permissions_privileged
+GTESTS+= destroy
+GTESTS+= dev_fuse_poll
+GTESTS+= fallocate
+GTESTS+= fifo
+GTESTS+= flush
+GTESTS+= forget
+GTESTS+= fsync
+GTESTS+= fsyncdir
+GTESTS+= getattr
+GTESTS+= interrupt
+GTESTS+= io
+GTESTS+= last_local_modify
+GTESTS+= link
+GTESTS+= locks
+GTESTS+= lookup
+GTESTS+= lseek
+GTESTS+= mkdir
+GTESTS+= mknod
+GTESTS+= mount
+GTESTS+= nfs
+GTESTS+= notify
+GTESTS+= open
+GTESTS+= opendir
+GTESTS+= pre-init
+GTESTS+= read
+GTESTS+= readdir
+GTESTS+= readlink
+GTESTS+= release
+GTESTS+= releasedir
+GTESTS+= rename
+GTESTS+= rmdir
+GTESTS+= setattr
+GTESTS+= statfs
+GTESTS+= symlink
+GTESTS+= unlink
+GTESTS+= write
+GTESTS+= xattr
+
+.for p in ${GTESTS}
+SRCS.$p+= ${p}.cc
+SRCS.$p+= mockfs.cc
+SRCS.$p+= utils.cc
+.endfor
+
+TEST_METADATA.default_permissions+= required_user="unprivileged"
+TEST_METADATA.default_permissions_privileged+= required_user="root"
+TEST_METADATA.mknod+= required_user="root"
+TEST_METADATA.nfs+= required_user="root"
+# ctl must be exclusive because it disables/enables camsim
+TEST_METADATA.ctl+= is_exclusive="true"
+TEST_METADATA.ctl+= required_user="root"
+
+TEST_METADATA+= timeout=10
+TEST_METADATA+= required_kmods="fusefs"
+
+FUSEFS= ${SRCTOP}/sys/fs/fuse
+# Suppress warnings that GCC generates for the libc++ and gtest headers.
+CXXWARNFLAGS.gcc+= -Wno-placement-new -Wno-attributes
+# Suppress Wcast-align for readdir.cc, because it is unavoidable when using
+# getdirentries.
+CXXWARNFLAGS.readdir.cc+= -Wno-cast-align
+.if ${COMPILER_TYPE} == "gcc" && ${COMPILER_VERSION} >= 80000
+CXXWARNFLAGS+= -Wno-class-memaccess
+.endif
+# Suppress false warning about set but unused DNAME in inval_entry_below_root
+CXXWARNFLAGS.gcc+= -Wno-unused-but-set-variable
+# Suppress warnings about deprecated implicit copy constructors in gtest.
+CXXWARNFLAGS+= -Wno-deprecated-copy
+.if ${COMPILER_TYPE} == "clang" && ${COMPILER_VERSION} >= 180000
+# clang 18.0.0 introduces a new warning about variable length arrays in C++.
+CXXWARNFLAGS+= -Wno-vla-cxx-extension
+.endif
+CXXFLAGS+= -I${SRCTOP}/tests
+CXXFLAGS+= -I${FUSEFS}
+
+LIBADD+= pthread
+LIBADD+= gmock gtest
+LIBADD+= util
+
+.include <bsd.test.mk>
diff --git a/tests/sys/fs/fusefs/access.cc b/tests/sys/fs/fusefs/access.cc
new file mode 100644
index 000000000000..5762269fac7b
--- /dev/null
+++ b/tests/sys/fs/fusefs/access.cc
@@ -0,0 +1,301 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <sys/types.h>
+#include <sys/extattr.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class Access: public FuseTest {
+public:
+virtual void SetUp() {
+ FuseTest::SetUp();
+ // Clear the default FUSE_ACCESS expectation
+ Mock::VerifyAndClearExpectations(m_mock);
+}
+
+void expect_lookup(const char *relpath, uint64_t ino)
+{
+ FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, 1);
+}
+
+/*
+ * Expect that FUSE_ACCESS will never be called for the given inode, with any
+ * bits in the supplied access_mask set
+ */
+void expect_noaccess(uint64_t ino, mode_t access_mask)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_ACCESS &&
+ in.header.nodeid == ino &&
+ in.body.access.mask & access_mask);
+ }, Eq(true)),
+ _)
+ ).Times(0);
+}
+
+};
+
+class RofsAccess: public Access {
+public:
+virtual void SetUp() {
+ m_ro = true;
+ Access::SetUp();
+}
+};
+
+/*
+ * Change the mode of a file.
+ *
+ * There should never be a FUSE_ACCESS sent for this operation, except for
+ * search permissions on the parent directory.
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=245689
+ */
+TEST_F(Access, chmod)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ const mode_t newmode = 0644;
+
+ expect_access(FUSE_ROOT_ID, X_OK, 0);
+ expect_lookup(RELPATH, ino);
+ expect_noaccess(ino, 0);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino; // Must match nodeid
+ out.body.attr.attr.mode = S_IFREG | newmode;
+ })));
+
+ EXPECT_EQ(0, chmod(FULLPATH, newmode)) << strerror(errno);
+}
+
+/*
+ * Create a new file
+ *
+ * There should never be a FUSE_ACCESS sent for this operation, except for
+ * search permissions on the parent directory.
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=245689
+ */
+TEST_F(Access, create)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ mode_t mode = S_IFREG | 0755;
+ uint64_t ino = 42;
+
+ expect_access(FUSE_ROOT_ID, X_OK, 0);
+ expect_noaccess(FUSE_ROOT_ID, R_OK | W_OK);
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_noaccess(ino, 0);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_CREATE);
+ }, Eq(true)),
+ _)
+ ).WillOnce(ReturnErrno(EPERM));
+
+ EXPECT_EQ(-1, open(FULLPATH, O_CREAT | O_EXCL, mode));
+ EXPECT_EQ(EPERM, errno);
+}
+
+/* The error case of FUSE_ACCESS. */
+TEST_F(Access, eaccess)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ mode_t access_mode = X_OK;
+
+ expect_access(FUSE_ROOT_ID, X_OK, 0);
+ expect_lookup(RELPATH, ino);
+ expect_access(ino, access_mode, EACCES);
+
+ ASSERT_NE(0, access(FULLPATH, access_mode));
+ ASSERT_EQ(EACCES, errno);
+}
+
+/*
+ * If the filesystem returns ENOSYS, then it is treated as a permanent success,
+ * and subsequent VOP_ACCESS calls will succeed automatically without querying
+ * the daemon.
+ */
+TEST_F(Access, enosys)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ mode_t access_mode = R_OK;
+
+ expect_access(FUSE_ROOT_ID, X_OK, ENOSYS);
+ FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2);
+
+ ASSERT_EQ(0, access(FULLPATH, access_mode)) << strerror(errno);
+ ASSERT_EQ(0, access(FULLPATH, access_mode)) << strerror(errno);
+}
+
+TEST_F(RofsAccess, erofs)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ mode_t access_mode = W_OK;
+
+ expect_access(FUSE_ROOT_ID, X_OK, 0);
+ expect_lookup(RELPATH, ino);
+
+ ASSERT_NE(0, access(FULLPATH, access_mode));
+ ASSERT_EQ(EROFS, errno);
+}
+
+
+/*
+ * Lookup an extended attribute
+ *
+ * There should never be a FUSE_ACCESS sent for this operation, except for
+ * search permissions on the parent directory.
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=245689
+ */
+TEST_F(Access, Getxattr)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ char data[80];
+ int ns = EXTATTR_NAMESPACE_USER;
+ ssize_t r;
+
+ expect_access(FUSE_ROOT_ID, X_OK, 0);
+ expect_lookup(RELPATH, ino);
+ expect_noaccess(ino, 0);
+ expect_getxattr(ino, "user.foo", ReturnErrno(ENOATTR));
+
+ r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data));
+ ASSERT_EQ(-1, r);
+ ASSERT_EQ(ENOATTR, errno);
+}
+
+/* The successful case of FUSE_ACCESS. */
+TEST_F(Access, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ mode_t access_mode = R_OK;
+
+ expect_access(FUSE_ROOT_ID, X_OK, 0);
+ expect_lookup(RELPATH, ino);
+ expect_access(ino, access_mode, 0);
+
+ ASSERT_EQ(0, access(FULLPATH, access_mode)) << strerror(errno);
+}
+
+/*
+ * Unlink a file
+ *
+ * There should never be a FUSE_ACCESS sent for this operation, except for
+ * search permissions on the parent directory.
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=245689
+ */
+TEST_F(Access, unlink)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+
+ expect_access(FUSE_ROOT_ID, X_OK, 0);
+ expect_noaccess(FUSE_ROOT_ID, W_OK | R_OK);
+ expect_noaccess(ino, 0);
+ expect_lookup(RELPATH, ino);
+ expect_unlink(1, RELPATH, EPERM);
+
+ ASSERT_NE(0, unlink(FULLPATH));
+ ASSERT_EQ(EPERM, errno);
+}
+
+/*
+ * Unlink a file whose parent diretory's sticky bit is set
+ *
+ * There should never be a FUSE_ACCESS sent for this operation, except for
+ * search permissions on the parent directory.
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=245689
+ */
+TEST_F(Access, unlink_sticky_directory)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+
+ expect_access(FUSE_ROOT_ID, X_OK, 0);
+ expect_noaccess(FUSE_ROOT_ID, W_OK | R_OK);
+ expect_noaccess(ino, 0);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == FUSE_ROOT_ID);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out)
+ {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = FUSE_ROOT_ID;
+ out.body.attr.attr.mode = S_IFDIR | 01777;
+ out.body.attr.attr.uid = 0;
+ out.body.attr.attr_valid = UINT64_MAX;
+ })));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_ACCESS &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).Times(0);
+ expect_lookup(RELPATH, ino);
+ expect_unlink(FUSE_ROOT_ID, RELPATH, EPERM);
+
+ ASSERT_EQ(-1, unlink(FULLPATH));
+ ASSERT_EQ(EPERM, errno);
+}
diff --git a/tests/sys/fs/fusefs/allow_other.cc b/tests/sys/fs/fusefs/allow_other.cc
new file mode 100644
index 000000000000..24a161166a90
--- /dev/null
+++ b/tests/sys/fs/fusefs/allow_other.cc
@@ -0,0 +1,306 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Tests for the "allow_other" mount option. They must be in their own
+ * file so they can be run as root
+ */
+
+extern "C" {
+#include <sys/types.h>
+#include <sys/extattr.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <unistd.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+const static char FULLPATH[] = "mountpoint/some_file.txt";
+const static char RELPATH[] = "some_file.txt";
+
+class NoAllowOther: public FuseTest {
+
+public:
+virtual void SetUp() {
+ if (geteuid() != 0) {
+ GTEST_SKIP() << "This test must be run as root";
+ }
+
+ FuseTest::SetUp();
+}
+};
+
+class AllowOther: public NoAllowOther {
+
+public:
+virtual void SetUp() {
+ m_allow_other = true;
+ NoAllowOther::SetUp();
+}
+};
+
+TEST_F(AllowOther, allowed)
+{
+ int status;
+
+ fork(true, &status, [&] {
+ uint64_t ino = 42;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ expect_open(ino, 0, 1);
+ expect_flush(ino, 1, ReturnErrno(0));
+ expect_release(ino, FH);
+ }, []() {
+ int fd;
+
+ fd = open(FULLPATH, O_RDONLY);
+ if (fd < 0) {
+ perror("open");
+ return(1);
+ }
+
+ leak(fd);
+ return 0;
+ }
+ );
+ ASSERT_EQ(0, WEXITSTATUS(status));
+}
+
+/* Check that fusefs uses the correct credentials for FUSE operations */
+TEST_F(AllowOther, creds)
+{
+ int status;
+ uid_t uid;
+ gid_t gid;
+
+ get_unprivileged_id(&uid, &gid);
+ fork(true, &status, [=] {
+ EXPECT_CALL(*m_mock, process( ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_LOOKUP &&
+ in.header.uid == uid &&
+ in.header.gid == gid);
+ }, Eq(true)),
+ _)
+ ).Times(1)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ }, []() {
+ eaccess(FULLPATH, F_OK);
+ return 0;
+ }
+ );
+ ASSERT_EQ(0, WEXITSTATUS(status));
+}
+
+/*
+ * A variation of the Open.multiple_creds test showing how the bug can lead to a
+ * privilege elevation. The first process is privileged and opens a file only
+ * visible to root. The second process is unprivileged and shouldn't be able
+ * to open the file, but does thanks to the bug
+ */
+TEST_F(AllowOther, privilege_escalation)
+{
+ int fd1, status;
+ const static uint64_t ino = 42;
+ const static uint64_t fh = 100;
+
+ /* Fork a child to open the file with different credentials */
+ fork(true, &status, [&] {
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0600, 0, 2);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_OPEN &&
+ in.header.pid == (uint32_t)getpid() &&
+ in.header.uid == (uint32_t)geteuid() &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(
+ ReturnImmediate([](auto in __unused, auto& out) {
+ out.body.open.fh = fh;
+ out.header.len = sizeof(out.header);
+ SET_OUT_HEADER_LEN(out, open);
+ })));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_OPEN &&
+ in.header.pid != (uint32_t)getpid() &&
+ in.header.uid != (uint32_t)geteuid() &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).Times(AnyNumber())
+ .WillRepeatedly(Invoke(ReturnErrno(EPERM)));
+
+ fd1 = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd1) << strerror(errno);
+ }, [] {
+ int fd0;
+
+ fd0 = open(FULLPATH, O_RDONLY);
+ if (fd0 >= 0) {
+ fprintf(stderr, "Privilege escalation!\n");
+ return 1;
+ }
+ if (errno != EPERM) {
+ fprintf(stderr, "Unexpected error %s\n",
+ strerror(errno));
+ return 1;
+ }
+ leak(fd0);
+ return 0;
+ }
+ );
+ ASSERT_EQ(0, WEXITSTATUS(status));
+ leak(fd1);
+}
+
+TEST_F(NoAllowOther, disallowed)
+{
+ int status;
+
+ fork(true, &status, [] {
+ }, []() {
+ int fd;
+
+ fd = open(FULLPATH, O_RDONLY);
+ if (fd >= 0) {
+ fprintf(stderr, "open should've failed\n");
+ leak(fd);
+ return(1);
+ } else if (errno != EPERM) {
+ fprintf(stderr, "Unexpected error: %s\n",
+ strerror(errno));
+ return(1);
+ }
+ return 0;
+ }
+ );
+ ASSERT_EQ(0, WEXITSTATUS(status));
+}
+
+/*
+ * When -o allow_other is not used, users other than the owner aren't allowed
+ * to open anything inside of the mount point, not just the mountpoint itself
+ * This is a regression test for bug 237052
+ */
+TEST_F(NoAllowOther, disallowed_beneath_root)
+{
+ const static char RELPATH2[] = "other_dir";
+ const static uint64_t ino = 42;
+ const static uint64_t ino2 = 43;
+ int dfd, status;
+
+ expect_lookup(RELPATH, ino, S_IFDIR | 0755, 0, 1);
+ EXPECT_LOOKUP(ino, RELPATH2)
+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = ino2;
+ out.body.entry.attr.nlink = 1;
+ out.body.entry.attr_valid = UINT64_MAX;
+ })));
+ expect_opendir(ino);
+ dfd = open(FULLPATH, O_DIRECTORY);
+ ASSERT_LE(0, dfd) << strerror(errno);
+
+ fork(true, &status, [] {
+ }, [&]() {
+ int fd;
+
+ fd = openat(dfd, RELPATH2, O_RDONLY);
+ if (fd >= 0) {
+ fprintf(stderr, "openat should've failed\n");
+ leak(fd);
+ return(1);
+ } else if (errno != EPERM) {
+ fprintf(stderr, "Unexpected error: %s\n",
+ strerror(errno));
+ return(1);
+ }
+ return 0;
+ }
+ );
+ ASSERT_EQ(0, WEXITSTATUS(status));
+
+ leak(dfd);
+}
+
+/*
+ * Provide coverage for the extattr methods, which have a slightly different
+ * code path
+ */
+TEST_F(NoAllowOther, setextattr)
+{
+ int ino = 42, status;
+
+ fork(true, &status, [&] {
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = ino;
+ })));
+
+ /*
+ * lookup the file to get it into the cache.
+ * Otherwise, the unprivileged lookup will fail with
+ * EACCES
+ */
+ ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
+ }, [&]() {
+ const char value[] = "whatever";
+ ssize_t value_len = strlen(value) + 1;
+ int ns = EXTATTR_NAMESPACE_USER;
+ ssize_t r;
+
+ r = extattr_set_file(FULLPATH, ns, "foo",
+ (const void*)value, value_len);
+ if (r >= 0) {
+ fprintf(stderr, "should've failed\n");
+ return(1);
+ } else if (errno != EPERM) {
+ fprintf(stderr, "Unexpected error: %s\n",
+ strerror(errno));
+ return(1);
+ }
+ return 0;
+ }
+ );
+ ASSERT_EQ(0, WEXITSTATUS(status));
+}
diff --git a/tests/sys/fs/fusefs/bad_server.cc b/tests/sys/fs/fusefs/bad_server.cc
new file mode 100644
index 000000000000..af2ca146e431
--- /dev/null
+++ b/tests/sys/fs/fusefs/bad_server.cc
@@ -0,0 +1,103 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Axcient
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <fcntl.h>
+#include <unistd.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class BadServer: public FuseTest {};
+
+/*
+ * If the server sends a response for an unknown request, the kernel should
+ * gracefully return EINVAL.
+ */
+TEST_F(BadServer, UnknownUnique)
+{
+ mockfs_buf_out out;
+
+ out.header.len = sizeof(out.header);
+ out.header.error = 0;
+ out.header.unique = 99999; // Invalid!
+ out.expected_errno = EINVAL;
+ m_mock->write_response(out);
+}
+
+/*
+ * If the server sends less than a header's worth of data, the kernel should
+ * gracefully return EINVAL.
+ */
+TEST_F(BadServer, ShortWrite)
+{
+ mockfs_buf_out out;
+
+ out.header.len = sizeof(out.header) - 1;
+ out.header.error = 0;
+ out.header.unique = 0; // Asynchronous notification
+ out.expected_errno = EINVAL;
+ m_mock->write_response(out);
+}
+
+/*
+ * It is illegal to report an error, and also send back a payload.
+ */
+TEST_F(BadServer, ErrorWithPayload)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke([&](auto in, auto &out) {
+ // First send an invalid response
+ std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
+ out0->header.unique = in.header.unique;
+ out0->header.error = -ENOENT;
+ SET_OUT_HEADER_LEN(*out0, entry); // Invalid!
+ out0->expected_errno = EINVAL;
+ out.push_back(std::move(out0));
+
+ // Then, respond to the lookup so we can complete the test
+ std::unique_ptr<mockfs_buf_out> out1(new mockfs_buf_out);
+ out1->header.unique = in.header.unique;
+ out1->header.error = -ENOENT;
+ out1->header.len = sizeof(out1->header);
+ out.push_back(std::move(out1));
+
+ // The kernel may disconnect us for bad behavior, so don't try
+ // to read any more.
+ m_mock->m_quit = true;
+ }));
+
+ EXPECT_NE(0, access(FULLPATH, F_OK));
+
+ EXPECT_EQ(ENOENT, errno);
+}
diff --git a/tests/sys/fs/fusefs/bmap.cc b/tests/sys/fs/fusefs/bmap.cc
new file mode 100644
index 000000000000..30612079657d
--- /dev/null
+++ b/tests/sys/fs/fusefs/bmap.cc
@@ -0,0 +1,266 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/filio.h>
+
+#include <fcntl.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+const static char FULLPATH[] = "mountpoint/foo";
+const static char RELPATH[] = "foo";
+
+class Bmap: public FuseTest {
+public:
+virtual void SetUp() {
+ m_maxreadahead = UINT32_MAX;
+ FuseTest::SetUp();
+}
+void expect_bmap(uint64_t ino, uint64_t lbn, uint32_t blocksize, uint64_t pbn)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_BMAP &&
+ in.header.nodeid == ino &&
+ in.body.bmap.block == lbn &&
+ in.body.bmap.blocksize == blocksize);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, bmap);
+ out.body.bmap.block = pbn;
+ })));
+}
+
+void expect_lookup(const char *relpath, uint64_t ino, off_t size)
+{
+ FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1,
+ UINT64_MAX);
+}
+};
+
+class BmapEof: public Bmap, public WithParamInterface<int> {};
+
+/*
+ * Test FUSE_BMAP
+ */
+TEST_F(Bmap, bmap)
+{
+ struct fiobmap2_arg arg;
+ /*
+ * Pick fsize and lbn large enough that max length runs won't reach
+ * either beginning or end of file
+ */
+ const off_t filesize = 1 << 30;
+ int64_t lbn = 100;
+ int64_t pbn = 12345;
+ const ino_t ino = 42;
+ int fd;
+
+ expect_lookup(RELPATH, 42, filesize);
+ expect_open(ino, 0, 1);
+ expect_bmap(ino, lbn, m_maxbcachebuf, pbn);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ arg.bn = lbn;
+ arg.runp = -1;
+ arg.runb = -1;
+ ASSERT_EQ(0, ioctl(fd, FIOBMAP2, &arg)) << strerror(errno);
+ EXPECT_EQ(arg.bn, pbn);
+ /*
+ * XXX The FUSE protocol does not include the runp and runb variables,
+ * so those must be guessed in-kernel. There's no "right" answer, so
+ * just check that they're within reasonable limits.
+ */
+ EXPECT_LE(arg.runb, lbn);
+ EXPECT_LE((unsigned long)arg.runb, m_maxreadahead / m_maxbcachebuf);
+ EXPECT_LE((unsigned long)arg.runb, m_maxphys / m_maxbcachebuf);
+ EXPECT_GT(arg.runb, 0);
+ EXPECT_LE(arg.runp, filesize / m_maxbcachebuf - lbn);
+ EXPECT_LE((unsigned long)arg.runp, m_maxreadahead / m_maxbcachebuf);
+ EXPECT_LE((unsigned long)arg.runp, m_maxphys / m_maxbcachebuf);
+ EXPECT_GT(arg.runp, 0);
+
+ leak(fd);
+}
+
+/*
+ * If the daemon does not implement VOP_BMAP, fusefs should return sensible
+ * defaults.
+ */
+TEST_F(Bmap, default_)
+{
+ struct fiobmap2_arg arg;
+ const off_t filesize = 1 << 30;
+ const ino_t ino = 42;
+ int64_t lbn;
+ int fd;
+
+ expect_lookup(RELPATH, 42, filesize);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_BMAP);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(ENOSYS)));
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ /* First block */
+ lbn = 0;
+ arg.bn = lbn;
+ arg.runp = -1;
+ arg.runb = -1;
+ ASSERT_EQ(0, ioctl(fd, FIOBMAP2, &arg)) << strerror(errno);
+ EXPECT_EQ(arg.bn, 0);
+ EXPECT_EQ((unsigned long )arg.runp, m_maxphys / m_maxbcachebuf - 1);
+ EXPECT_EQ(arg.runb, 0);
+
+ /* In the middle */
+ lbn = filesize / m_maxbcachebuf / 2;
+ arg.bn = lbn;
+ arg.runp = -1;
+ arg.runb = -1;
+ ASSERT_EQ(0, ioctl(fd, FIOBMAP2, &arg)) << strerror(errno);
+ EXPECT_EQ(arg.bn, lbn * m_maxbcachebuf / DEV_BSIZE);
+ EXPECT_EQ((unsigned long )arg.runp, m_maxphys / m_maxbcachebuf - 1);
+ EXPECT_EQ((unsigned long )arg.runb, m_maxphys / m_maxbcachebuf - 1);
+
+ /* Last block */
+ lbn = filesize / m_maxbcachebuf - 1;
+ arg.bn = lbn;
+ arg.runp = -1;
+ arg.runb = -1;
+ ASSERT_EQ(0, ioctl(fd, FIOBMAP2, &arg)) << strerror(errno);
+ EXPECT_EQ(arg.bn, lbn * m_maxbcachebuf / DEV_BSIZE);
+ EXPECT_EQ(arg.runp, 0);
+ EXPECT_EQ((unsigned long )arg.runb, m_maxphys / m_maxbcachebuf - 1);
+
+ leak(fd);
+}
+
+/*
+ * VOP_BMAP should not query the server for the file's size, even if its cached
+ * attributes have expired.
+ * Regression test for https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=256937
+ */
+TEST_P(BmapEof, eof)
+{
+ /*
+ * Outline:
+ * 1) lookup the file, setting attr_valid=0
+ * 2) Read more than one block, causing the kernel to issue VOP_BMAP to
+ * plan readahead.
+ * 3) Nothing should panic
+ * 4) Repeat the tests, truncating the file after different numbers of
+ * GETATTR operations.
+ */
+ Sequence seq;
+ const off_t filesize = 2 * m_maxbcachebuf;
+ const ino_t ino = 42;
+ mode_t mode = S_IFREG | 0644;
+ char *buf;
+ int fd;
+ int ngetattrs;
+
+ ngetattrs = GetParam();
+ FuseTest::expect_lookup(RELPATH, ino, mode, filesize, 1, 0);
+ expect_open(ino, 0, 1);
+ // Depending on ngetattrs, FUSE_READ could be called with either
+ // filesize or filesize / 2 .
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READ &&
+ in.header.nodeid == ino &&
+ in.body.read.offset == 0 &&
+ ( in.body.read.size == filesize ||
+ in.body.read.size == filesize / 2));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in, auto& out) {
+ size_t osize = in.body.read.size;
+
+ assert(osize < sizeof(out.body.bytes));
+ out.header.len = sizeof(struct fuse_out_header) + osize;
+ bzero(out.body.bytes, osize);
+ })));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).Times(Between(ngetattrs - 1, ngetattrs))
+ .InSequence(seq)
+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr_valid = 0;
+ out.body.attr.attr.ino = ino;
+ out.body.attr.attr.mode = S_IFREG | 0644;
+ out.body.attr.attr.size = filesize;
+ })));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).InSequence(seq)
+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr_valid = 0;
+ out.body.attr.attr.ino = ino;
+ out.body.attr.attr.mode = S_IFREG | 0644;
+ out.body.attr.attr.size = filesize / 2;
+ })));
+
+ buf = new char[filesize]();
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ read(fd, buf, filesize);
+
+ delete[] buf;
+ leak(fd);
+}
+
+INSTANTIATE_TEST_SUITE_P(BE, BmapEof,
+ Values(1, 2, 3)
+);
diff --git a/tests/sys/fs/fusefs/cache.cc b/tests/sys/fs/fusefs/cache.cc
new file mode 100644
index 000000000000..ea6d87674da2
--- /dev/null
+++ b/tests/sys/fs/fusefs/cache.cc
@@ -0,0 +1,218 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2020 Alan Somers
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <sys/param.h>
+#include <fcntl.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+/*
+ * Tests for thorny cache problems not specific to any one opcode
+ */
+
+using namespace testing;
+
+/*
+ * Parameters
+ * - reopen file - If true, close and reopen the file between reads
+ * - cache lookups - If true, allow lookups to be cached
+ * - cache attrs - If true, allow file attributes to be cached
+ * - cache_mode - uncached, writeback, or writethrough
+ * - initial size - File size before truncation
+ * - truncated size - File size after truncation
+ */
+typedef tuple<tuple<bool, bool, bool>, cache_mode, ssize_t, ssize_t> CacheParam;
+
+class Cache: public FuseTest, public WithParamInterface<CacheParam> {
+public:
+bool m_direct_io;
+
+Cache(): m_direct_io(false) {};
+
+virtual void SetUp() {
+ int cache_mode = get<1>(GetParam());
+ switch (cache_mode) {
+ case Uncached:
+ m_direct_io = true;
+ break;
+ case WritebackAsync:
+ m_async = true;
+ /* FALLTHROUGH */
+ case Writeback:
+ m_init_flags |= FUSE_WRITEBACK_CACHE;
+ /* FALLTHROUGH */
+ case Writethrough:
+ break;
+ default:
+ FAIL() << "Unknown cache mode";
+ }
+ m_noatime = true; // To prevent SETATTR for atime on close
+
+ FuseTest::SetUp();
+ if (IsSkipped())
+ return;
+}
+
+void expect_getattr(uint64_t ino, int times, uint64_t size, uint64_t attr_valid)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).Times(times)
+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr_valid = attr_valid;
+ out.body.attr.attr.ino = ino;
+ out.body.attr.attr.mode = S_IFREG | 0644;
+ out.body.attr.attr.size = size;
+ })));
+}
+
+void expect_lookup(const char *relpath, uint64_t ino,
+ uint64_t size, uint64_t entry_valid, uint64_t attr_valid)
+{
+ EXPECT_LOOKUP(FUSE_ROOT_ID, relpath)
+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.nlink = 1;
+ out.body.entry.attr_valid = attr_valid;
+ out.body.entry.attr.size = size;
+ out.body.entry.entry_valid = entry_valid;
+ })));
+}
+
+void expect_open(uint64_t ino, int times)
+{
+ FuseTest::expect_open(ino, m_direct_io ? FOPEN_DIRECT_IO: 0, times);
+}
+
+void expect_release(uint64_t ino, ProcessMockerT r)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_RELEASE &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(r));
+}
+
+};
+
+// If the server truncates the file behind the kernel's back, the kernel should
+// invalidate cached pages beyond the new EOF
+TEST_P(Cache, truncate_by_surprise_invalidates_cache)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefghijklmnopqrstuvwxyz";
+ uint64_t ino = 42;
+ uint64_t attr_valid, entry_valid;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+ uint8_t buf[bufsize];
+ bool reopen = get<0>(get<0>(GetParam()));
+ bool cache_lookups = get<1>(get<0>(GetParam()));
+ bool cache_attrs = get<2>(get<0>(GetParam()));
+ ssize_t osize = get<2>(GetParam());
+ ssize_t nsize = get<3>(GetParam());
+
+ ASSERT_LE(osize, bufsize);
+ ASSERT_LE(nsize, bufsize);
+ if (cache_attrs)
+ attr_valid = UINT64_MAX;
+ else
+ attr_valid = 0;
+ if (cache_lookups)
+ entry_valid = UINT64_MAX;
+ else
+ entry_valid = 0;
+
+ expect_lookup(RELPATH, ino, osize, entry_valid, attr_valid);
+ expect_open(ino, 1);
+ if (!cache_attrs)
+ expect_getattr(ino, 2, osize, attr_valid);
+ expect_read(ino, 0, osize, osize, CONTENTS);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(osize, read(fd, buf, bufsize)) << strerror(errno);
+ ASSERT_EQ(0, memcmp(buf, CONTENTS, osize));
+
+ // Now truncate the file behind the kernel's back. The next read
+ // should discard cache and fetch from disk again.
+ if (reopen) {
+ // Close and reopen the file
+ expect_flush(ino, 1, ReturnErrno(ENOSYS));
+ expect_release(ino, ReturnErrno(0));
+ ASSERT_EQ(0, close(fd));
+ expect_lookup(RELPATH, ino, nsize, entry_valid, attr_valid);
+ expect_open(ino, 1);
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+ }
+
+ if (!cache_attrs)
+ expect_getattr(ino, 1, nsize, attr_valid);
+ expect_read(ino, 0, nsize, nsize, CONTENTS);
+ ASSERT_EQ(0, lseek(fd, 0, SEEK_SET));
+ ASSERT_EQ(nsize, read(fd, buf, bufsize)) << strerror(errno);
+ ASSERT_EQ(0, memcmp(buf, CONTENTS, nsize));
+
+ leak(fd);
+}
+
+INSTANTIATE_TEST_SUITE_P(Cache, Cache,
+ Combine(
+ /* Test every combination that:
+ * - does not cache at least one of entries and attrs
+ * - either doesn't cache attrs, or reopens the file
+ * In the other combinations, the kernel will never learn that
+ * the file's size has changed.
+ */
+ Values(
+ std::make_tuple(false, false, false),
+ std::make_tuple(false, true, false),
+ std::make_tuple(true, false, false),
+ std::make_tuple(true, false, true),
+ std::make_tuple(true, true, false)
+ ),
+ Values(Writethrough, Writeback),
+ /* Test both reductions and extensions to file size */
+ Values(20),
+ Values(10, 25)
+ )
+);
diff --git a/tests/sys/fs/fusefs/copy_file_range.cc b/tests/sys/fs/fusefs/copy_file_range.cc
new file mode 100644
index 000000000000..806ecf3c3653
--- /dev/null
+++ b/tests/sys/fs/fusefs/copy_file_range.cc
@@ -0,0 +1,802 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2020 Alan Somers
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class CopyFileRange: public FuseTest {
+public:
+
+void expect_maybe_lseek(uint64_t ino)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_LSEEK &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).Times(AtMost(1))
+ .WillRepeatedly(Invoke(ReturnErrno(ENOSYS)));
+}
+
+void expect_open(uint64_t ino, uint32_t flags, int times, uint64_t fh)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_OPEN &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).Times(times)
+ .WillRepeatedly(Invoke(
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ out.header.len = sizeof(out.header);
+ SET_OUT_HEADER_LEN(out, open);
+ out.body.open.fh = fh;
+ out.body.open.open_flags = flags;
+ })));
+}
+
+void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
+ uint64_t osize, const void *contents)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *buf = (const char*)in.body.bytes +
+ sizeof(struct fuse_write_in);
+
+ return (in.header.opcode == FUSE_WRITE &&
+ in.header.nodeid == ino &&
+ in.body.write.offset == offset &&
+ in.body.write.size == isize &&
+ 0 == bcmp(buf, contents, isize));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, write);
+ out.body.write.size = osize;
+ })));
+}
+
+};
+
+
+class CopyFileRange_7_27: public CopyFileRange {
+public:
+virtual void SetUp() {
+ m_kernel_minor_version = 27;
+ CopyFileRange::SetUp();
+}
+};
+
+class CopyFileRangeNoAtime: public CopyFileRange {
+public:
+virtual void SetUp() {
+ m_noatime = true;
+ CopyFileRange::SetUp();
+}
+};
+
+class CopyFileRangeRlimitFsize: public CopyFileRange {
+public:
+static sig_atomic_t s_sigxfsz;
+struct rlimit m_initial_limit;
+
+virtual void SetUp() {
+ s_sigxfsz = 0;
+ getrlimit(RLIMIT_FSIZE, &m_initial_limit);
+ CopyFileRange::SetUp();
+}
+
+void TearDown() {
+ struct sigaction sa;
+
+ setrlimit(RLIMIT_FSIZE, &m_initial_limit);
+
+ bzero(&sa, sizeof(sa));
+ sa.sa_handler = SIG_DFL;
+ sigaction(SIGXFSZ, &sa, NULL);
+
+ FuseTest::TearDown();
+}
+
+};
+
+sig_atomic_t CopyFileRangeRlimitFsize::s_sigxfsz = 0;
+
+void sigxfsz_handler(int __unused sig) {
+ CopyFileRangeRlimitFsize::s_sigxfsz = 1;
+}
+
+TEST_F(CopyFileRange, eio)
+{
+ const char FULLPATH1[] = "mountpoint/src.txt";
+ const char RELPATH1[] = "src.txt";
+ const char FULLPATH2[] = "mountpoint/dst.txt";
+ const char RELPATH2[] = "dst.txt";
+ const uint64_t ino1 = 42;
+ const uint64_t ino2 = 43;
+ const uint64_t fh1 = 0xdeadbeef1a7ebabe;
+ const uint64_t fh2 = 0xdeadc0de88c0ffee;
+ off_t fsize1 = 1 << 20; /* 1 MiB */
+ off_t fsize2 = 1 << 19; /* 512 KiB */
+ off_t start1 = 1 << 18;
+ off_t start2 = 3 << 17;
+ ssize_t len = 65536;
+ int fd1, fd2;
+
+ expect_lookup(RELPATH1, ino1, S_IFREG | 0644, fsize1, 1);
+ expect_lookup(RELPATH2, ino2, S_IFREG | 0644, fsize2, 1);
+ expect_open(ino1, 0, 1, fh1);
+ expect_open(ino2, 0, 1, fh2);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_COPY_FILE_RANGE &&
+ in.header.nodeid == ino1 &&
+ in.body.copy_file_range.fh_in == fh1 &&
+ (off_t)in.body.copy_file_range.off_in == start1 &&
+ in.body.copy_file_range.nodeid_out == ino2 &&
+ in.body.copy_file_range.fh_out == fh2 &&
+ (off_t)in.body.copy_file_range.off_out == start2 &&
+ in.body.copy_file_range.len == (size_t)len &&
+ in.body.copy_file_range.flags == 0);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(EIO)));
+
+ fd1 = open(FULLPATH1, O_RDONLY);
+ fd2 = open(FULLPATH2, O_WRONLY);
+ ASSERT_EQ(-1, copy_file_range(fd1, &start1, fd2, &start2, len, 0));
+ EXPECT_EQ(EIO, errno);
+}
+
+/*
+ * copy_file_range should evict cached data for the modified region of the
+ * destination file.
+ */
+TEST_F(CopyFileRange, evicts_cache)
+{
+ const char FULLPATH1[] = "mountpoint/src.txt";
+ const char RELPATH1[] = "src.txt";
+ const char FULLPATH2[] = "mountpoint/dst.txt";
+ const char RELPATH2[] = "dst.txt";
+ char *buf0, *buf1, *buf;
+ const uint64_t ino1 = 42;
+ const uint64_t ino2 = 43;
+ const uint64_t fh1 = 0xdeadbeef1a7ebabe;
+ const uint64_t fh2 = 0xdeadc0de88c0ffee;
+ off_t fsize1 = 1 << 20; /* 1 MiB */
+ off_t fsize2 = 1 << 19; /* 512 KiB */
+ off_t start1 = 1 << 18;
+ off_t start2 = 3 << 17;
+ ssize_t len = m_maxbcachebuf;
+ int fd1, fd2;
+
+ buf0 = new char[m_maxbcachebuf];
+ memset(buf0, 42, m_maxbcachebuf);
+
+ expect_lookup(RELPATH1, ino1, S_IFREG | 0644, fsize1, 1);
+ expect_lookup(RELPATH2, ino2, S_IFREG | 0644, fsize2, 1);
+ expect_open(ino1, 0, 1, fh1);
+ expect_open(ino2, 0, 1, fh2);
+ expect_read(ino2, start2, m_maxbcachebuf, m_maxbcachebuf, buf0, -1,
+ fh2);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_COPY_FILE_RANGE &&
+ in.header.nodeid == ino1 &&
+ in.body.copy_file_range.fh_in == fh1 &&
+ (off_t)in.body.copy_file_range.off_in == start1 &&
+ in.body.copy_file_range.nodeid_out == ino2 &&
+ in.body.copy_file_range.fh_out == fh2 &&
+ (off_t)in.body.copy_file_range.off_out == start2 &&
+ in.body.copy_file_range.len == (size_t)len &&
+ in.body.copy_file_range.flags == 0);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, write);
+ out.body.write.size = len;
+ })));
+
+ fd1 = open(FULLPATH1, O_RDONLY);
+ fd2 = open(FULLPATH2, O_RDWR);
+
+ // Prime cache
+ buf = new char[m_maxbcachebuf];
+ ASSERT_EQ(m_maxbcachebuf, pread(fd2, buf, m_maxbcachebuf, start2))
+ << strerror(errno);
+ EXPECT_EQ(0, memcmp(buf0, buf, m_maxbcachebuf));
+
+ // Tell the FUSE server overwrite the region we just read
+ ASSERT_EQ(len, copy_file_range(fd1, &start1, fd2, &start2, len, 0));
+
+ // Read again. This should bypass the cache and read direct from server
+ buf1 = new char[m_maxbcachebuf];
+ memset(buf1, 69, m_maxbcachebuf);
+ start2 -= len;
+ expect_read(ino2, start2, m_maxbcachebuf, m_maxbcachebuf, buf1, -1,
+ fh2);
+ ASSERT_EQ(m_maxbcachebuf, pread(fd2, buf, m_maxbcachebuf, start2))
+ << strerror(errno);
+ EXPECT_EQ(0, memcmp(buf1, buf, m_maxbcachebuf));
+
+ delete[] buf1;
+ delete[] buf0;
+ delete[] buf;
+ leak(fd1);
+ leak(fd2);
+}
+
+/*
+ * If the server doesn't support FUSE_COPY_FILE_RANGE, the kernel should
+ * fallback to a read/write based implementation.
+ */
+TEST_F(CopyFileRange, fallback)
+{
+ const char FULLPATH1[] = "mountpoint/src.txt";
+ const char RELPATH1[] = "src.txt";
+ const char FULLPATH2[] = "mountpoint/dst.txt";
+ const char RELPATH2[] = "dst.txt";
+ const uint64_t ino1 = 42;
+ const uint64_t ino2 = 43;
+ const uint64_t fh1 = 0xdeadbeef1a7ebabe;
+ const uint64_t fh2 = 0xdeadc0de88c0ffee;
+ off_t fsize2 = 0;
+ off_t start1 = 0;
+ off_t start2 = 0;
+ const char *contents = "Hello, world!";
+ ssize_t len;
+ int fd1, fd2;
+
+ len = strlen(contents);
+
+ /*
+ * Ensure that we read to EOF, just so the buffer cache's read size is
+ * predictable.
+ */
+ expect_lookup(RELPATH1, ino1, S_IFREG | 0644, start1 + len, 1);
+ expect_lookup(RELPATH2, ino2, S_IFREG | 0644, fsize2, 1);
+ expect_open(ino1, 0, 1, fh1);
+ expect_open(ino2, 0, 1, fh2);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_COPY_FILE_RANGE &&
+ in.header.nodeid == ino1 &&
+ in.body.copy_file_range.fh_in == fh1 &&
+ (off_t)in.body.copy_file_range.off_in == start1 &&
+ in.body.copy_file_range.nodeid_out == ino2 &&
+ in.body.copy_file_range.fh_out == fh2 &&
+ (off_t)in.body.copy_file_range.off_out == start2 &&
+ in.body.copy_file_range.len == (size_t)len &&
+ in.body.copy_file_range.flags == 0);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(ENOSYS)));
+ expect_maybe_lseek(ino1);
+ expect_read(ino1, start1, len, len, contents, 0);
+ expect_write(ino2, start2, len, len, contents);
+
+ fd1 = open(FULLPATH1, O_RDONLY);
+ ASSERT_GE(fd1, 0);
+ fd2 = open(FULLPATH2, O_WRONLY);
+ ASSERT_GE(fd2, 0);
+ ASSERT_EQ(len, copy_file_range(fd1, &start1, fd2, &start2, len, 0));
+}
+
+/*
+ * Writes via mmap should not conflict with using copy_file_range. Any dirty
+ * pages that overlap with copy_file_range's input should be flushed before
+ * FUSE_COPY_FILE_RANGE is sent.
+ */
+TEST_F(CopyFileRange, mmap_write)
+{
+ const char FULLPATH[] = "mountpoint/src.txt";
+ const char RELPATH[] = "src.txt";
+ uint8_t *wbuf, *fbuf;
+ void *p;
+ size_t fsize = 0x6000;
+ size_t wsize = 0x3000;
+ ssize_t r;
+ off_t offset2_in = 0;
+ off_t offset2_out = wsize;
+ size_t copysize = wsize;
+ const uint64_t ino = 42;
+ const uint64_t fh = 0xdeadbeef1a7ebabe;
+ int fd;
+ const mode_t mode = 0644;
+
+ fbuf = new uint8_t[fsize]();
+ wbuf = new uint8_t[wsize];
+ memset(wbuf, 1, wsize);
+
+ expect_lookup(RELPATH, ino, S_IFREG | mode, fsize, 1);
+ expect_open(ino, 0, 1, fh);
+ /* This read is initiated by the mmap write */
+ expect_read(ino, 0, fsize, fsize, fbuf, -1, fh);
+ /* This write flushes the buffer filled by the mmap write */
+ expect_write(ino, 0, wsize, wsize, wbuf);
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_COPY_FILE_RANGE &&
+ (off_t)in.body.copy_file_range.off_in == offset2_in &&
+ (off_t)in.body.copy_file_range.off_out == offset2_out &&
+ in.body.copy_file_range.len == copysize
+ );
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([&](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, write);
+ out.body.write.size = copysize;
+ })));
+
+ fd = open(FULLPATH, O_RDWR);
+
+ /* First, write some data via mmap */
+ p = mmap(NULL, wsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ ASSERT_NE(MAP_FAILED, p) << strerror(errno);
+ memmove((uint8_t*)p, wbuf, wsize);
+ ASSERT_EQ(0, munmap(p, wsize)) << strerror(errno);
+
+ /*
+ * Then copy it around the file via copy_file_range. This should
+ * trigger a FUSE_WRITE to flush the pages written by mmap.
+ */
+ r = copy_file_range(fd, &offset2_in, fd, &offset2_out, copysize, 0);
+ ASSERT_EQ(copysize, (size_t)r) << strerror(errno);
+
+ delete[] wbuf;
+ delete[] fbuf;
+}
+
+
+/*
+ * copy_file_range should send SIGXFSZ and return EFBIG when the operation
+ * would exceed the limit imposed by RLIMIT_FSIZE.
+ */
+TEST_F(CopyFileRangeRlimitFsize, signal)
+{
+ const char FULLPATH1[] = "mountpoint/src.txt";
+ const char RELPATH1[] = "src.txt";
+ const char FULLPATH2[] = "mountpoint/dst.txt";
+ const char RELPATH2[] = "dst.txt";
+ struct rlimit rl;
+ const uint64_t ino1 = 42;
+ const uint64_t ino2 = 43;
+ const uint64_t fh1 = 0xdeadbeef1a7ebabe;
+ const uint64_t fh2 = 0xdeadc0de88c0ffee;
+ off_t fsize1 = 1 << 20; /* 1 MiB */
+ off_t fsize2 = 1 << 19; /* 512 KiB */
+ off_t start1 = 1 << 18;
+ off_t start2 = fsize2;
+ ssize_t len = 65536;
+ int fd1, fd2;
+
+ expect_lookup(RELPATH1, ino1, S_IFREG | 0644, fsize1, 1);
+ expect_lookup(RELPATH2, ino2, S_IFREG | 0644, fsize2, 1);
+ expect_open(ino1, 0, 1, fh1);
+ expect_open(ino2, 0, 1, fh2);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_COPY_FILE_RANGE);
+ }, Eq(true)),
+ _)
+ ).Times(0);
+
+ rl.rlim_cur = fsize2;
+ rl.rlim_max = m_initial_limit.rlim_max;
+ ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno);
+ ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno);
+
+ fd1 = open(FULLPATH1, O_RDONLY);
+ fd2 = open(FULLPATH2, O_WRONLY);
+ ASSERT_EQ(-1, copy_file_range(fd1, &start1, fd2, &start2, len, 0));
+ EXPECT_EQ(EFBIG, errno);
+ EXPECT_EQ(1, s_sigxfsz);
+}
+
+/*
+ * When crossing the RLIMIT_FSIZE boundary, writes should be truncated, not
+ * aborted.
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=266611
+ */
+TEST_F(CopyFileRangeRlimitFsize, truncate)
+{
+ const char FULLPATH1[] = "mountpoint/src.txt";
+ const char RELPATH1[] = "src.txt";
+ const char FULLPATH2[] = "mountpoint/dst.txt";
+ const char RELPATH2[] = "dst.txt";
+ struct rlimit rl;
+ const uint64_t ino1 = 42;
+ const uint64_t ino2 = 43;
+ const uint64_t fh1 = 0xdeadbeef1a7ebabe;
+ const uint64_t fh2 = 0xdeadc0de88c0ffee;
+ off_t fsize1 = 1 << 20; /* 1 MiB */
+ off_t fsize2 = 1 << 19; /* 512 KiB */
+ off_t start1 = 1 << 18;
+ off_t start2 = fsize2;
+ ssize_t len = 65536;
+ off_t limit = start2 + len / 2;
+ int fd1, fd2;
+
+ expect_lookup(RELPATH1, ino1, S_IFREG | 0644, fsize1, 1);
+ expect_lookup(RELPATH2, ino2, S_IFREG | 0644, fsize2, 1);
+ expect_open(ino1, 0, 1, fh1);
+ expect_open(ino2, 0, 1, fh2);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_COPY_FILE_RANGE &&
+ (off_t)in.body.copy_file_range.off_out == start2 &&
+ in.body.copy_file_range.len == (size_t)len / 2
+ );
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, write);
+ out.body.write.size = len / 2;
+ })));
+
+ rl.rlim_cur = limit;
+ rl.rlim_max = m_initial_limit.rlim_max;
+ ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno);
+ ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno);
+
+ fd1 = open(FULLPATH1, O_RDONLY);
+ fd2 = open(FULLPATH2, O_WRONLY);
+ ASSERT_EQ(len / 2, copy_file_range(fd1, &start1, fd2, &start2, len, 0));
+}
+
+TEST_F(CopyFileRange, ok)
+{
+ const char FULLPATH1[] = "mountpoint/src.txt";
+ const char RELPATH1[] = "src.txt";
+ const char FULLPATH2[] = "mountpoint/dst.txt";
+ const char RELPATH2[] = "dst.txt";
+ const uint64_t ino1 = 42;
+ const uint64_t ino2 = 43;
+ const uint64_t fh1 = 0xdeadbeef1a7ebabe;
+ const uint64_t fh2 = 0xdeadc0de88c0ffee;
+ off_t fsize1 = 1 << 20; /* 1 MiB */
+ off_t fsize2 = 1 << 19; /* 512 KiB */
+ off_t start1 = 1 << 18;
+ off_t start2 = 3 << 17;
+ ssize_t len = 65536;
+ int fd1, fd2;
+
+ expect_lookup(RELPATH1, ino1, S_IFREG | 0644, fsize1, 1);
+ expect_lookup(RELPATH2, ino2, S_IFREG | 0644, fsize2, 1);
+ expect_open(ino1, 0, 1, fh1);
+ expect_open(ino2, 0, 1, fh2);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_COPY_FILE_RANGE &&
+ in.header.nodeid == ino1 &&
+ in.body.copy_file_range.fh_in == fh1 &&
+ (off_t)in.body.copy_file_range.off_in == start1 &&
+ in.body.copy_file_range.nodeid_out == ino2 &&
+ in.body.copy_file_range.fh_out == fh2 &&
+ (off_t)in.body.copy_file_range.off_out == start2 &&
+ in.body.copy_file_range.len == (size_t)len &&
+ in.body.copy_file_range.flags == 0);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, write);
+ out.body.write.size = len;
+ })));
+
+ fd1 = open(FULLPATH1, O_RDONLY);
+ fd2 = open(FULLPATH2, O_WRONLY);
+ ASSERT_EQ(len, copy_file_range(fd1, &start1, fd2, &start2, len, 0));
+}
+
+/*
+ * copy_file_range can make copies within a single file, as long as the ranges
+ * don't overlap.
+ * */
+TEST_F(CopyFileRange, same_file)
+{
+ const char FULLPATH[] = "mountpoint/src.txt";
+ const char RELPATH[] = "src.txt";
+ const uint64_t ino = 4;
+ const uint64_t fh = 0xdeadbeefa7ebabe;
+ off_t fsize = 1 << 20; /* 1 MiB */
+ off_t off_in = 1 << 18;
+ off_t off_out = 3 << 17;
+ ssize_t len = 65536;
+ int fd;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
+ expect_open(ino, 0, 1, fh);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_COPY_FILE_RANGE &&
+ in.header.nodeid == ino &&
+ in.body.copy_file_range.fh_in == fh &&
+ (off_t)in.body.copy_file_range.off_in == off_in &&
+ in.body.copy_file_range.nodeid_out == ino &&
+ in.body.copy_file_range.fh_out == fh &&
+ (off_t)in.body.copy_file_range.off_out == off_out &&
+ in.body.copy_file_range.len == (size_t)len &&
+ in.body.copy_file_range.flags == 0);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, write);
+ out.body.write.size = len;
+ })));
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_EQ(len, copy_file_range(fd, &off_in, fd, &off_out, len, 0));
+
+ leak(fd);
+}
+
+/*
+ * copy_file_range should update the destination's mtime and ctime, and
+ * the source's atime.
+ */
+TEST_F(CopyFileRange, timestamps)
+{
+ const char FULLPATH1[] = "mountpoint/src.txt";
+ const char RELPATH1[] = "src.txt";
+ const char FULLPATH2[] = "mountpoint/dst.txt";
+ const char RELPATH2[] = "dst.txt";
+ struct stat sb1a, sb1b, sb2a, sb2b;
+ const uint64_t ino1 = 42;
+ const uint64_t ino2 = 43;
+ const uint64_t fh1 = 0xdeadbeef1a7ebabe;
+ const uint64_t fh2 = 0xdeadc0de88c0ffee;
+ off_t fsize1 = 1 << 20; /* 1 MiB */
+ off_t fsize2 = 1 << 19; /* 512 KiB */
+ off_t start1 = 1 << 18;
+ off_t start2 = 3 << 17;
+ ssize_t len = 65536;
+ int fd1, fd2;
+
+ expect_lookup(RELPATH1, ino1, S_IFREG | 0644, fsize1, 1);
+ expect_lookup(RELPATH2, ino2, S_IFREG | 0644, fsize2, 1);
+ expect_open(ino1, 0, 1, fh1);
+ expect_open(ino2, 0, 1, fh2);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_COPY_FILE_RANGE &&
+ in.header.nodeid == ino1 &&
+ in.body.copy_file_range.fh_in == fh1 &&
+ (off_t)in.body.copy_file_range.off_in == start1 &&
+ in.body.copy_file_range.nodeid_out == ino2 &&
+ in.body.copy_file_range.fh_out == fh2 &&
+ (off_t)in.body.copy_file_range.off_out == start2 &&
+ in.body.copy_file_range.len == (size_t)len &&
+ in.body.copy_file_range.flags == 0);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, write);
+ out.body.write.size = len;
+ })));
+
+ fd1 = open(FULLPATH1, O_RDONLY);
+ ASSERT_GE(fd1, 0);
+ fd2 = open(FULLPATH2, O_WRONLY);
+ ASSERT_GE(fd2, 0);
+ ASSERT_EQ(0, fstat(fd1, &sb1a)) << strerror(errno);
+ ASSERT_EQ(0, fstat(fd2, &sb2a)) << strerror(errno);
+
+ nap();
+
+ ASSERT_EQ(len, copy_file_range(fd1, &start1, fd2, &start2, len, 0));
+ ASSERT_EQ(0, fstat(fd1, &sb1b)) << strerror(errno);
+ ASSERT_EQ(0, fstat(fd2, &sb2b)) << strerror(errno);
+
+ EXPECT_NE(sb1a.st_atime, sb1b.st_atime);
+ EXPECT_EQ(sb1a.st_mtime, sb1b.st_mtime);
+ EXPECT_EQ(sb1a.st_ctime, sb1b.st_ctime);
+ EXPECT_EQ(sb2a.st_atime, sb2b.st_atime);
+ EXPECT_NE(sb2a.st_mtime, sb2b.st_mtime);
+ EXPECT_NE(sb2a.st_ctime, sb2b.st_ctime);
+
+ leak(fd1);
+ leak(fd2);
+}
+
+/*
+ * copy_file_range can extend the size of a file
+ * */
+TEST_F(CopyFileRange, extend)
+{
+ const char FULLPATH[] = "mountpoint/src.txt";
+ const char RELPATH[] = "src.txt";
+ struct stat sb;
+ const uint64_t ino = 4;
+ const uint64_t fh = 0xdeadbeefa7ebabe;
+ off_t fsize = 65536;
+ off_t off_in = 0;
+ off_t off_out = 65536;
+ ssize_t len = 65536;
+ int fd;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
+ expect_open(ino, 0, 1, fh);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_COPY_FILE_RANGE &&
+ in.header.nodeid == ino &&
+ in.body.copy_file_range.fh_in == fh &&
+ (off_t)in.body.copy_file_range.off_in == off_in &&
+ in.body.copy_file_range.nodeid_out == ino &&
+ in.body.copy_file_range.fh_out == fh &&
+ (off_t)in.body.copy_file_range.off_out == off_out &&
+ in.body.copy_file_range.len == (size_t)len &&
+ in.body.copy_file_range.flags == 0);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, write);
+ out.body.write.size = len;
+ })));
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_GE(fd, 0);
+ ASSERT_EQ(len, copy_file_range(fd, &off_in, fd, &off_out, len, 0));
+
+ /* Check that cached attributes were updated appropriately */
+ ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
+ EXPECT_EQ(fsize + len, sb.st_size);
+
+ leak(fd);
+}
+
+/* With older protocol versions, no FUSE_COPY_FILE_RANGE should be attempted */
+TEST_F(CopyFileRange_7_27, fallback)
+{
+ const char FULLPATH1[] = "mountpoint/src.txt";
+ const char RELPATH1[] = "src.txt";
+ const char FULLPATH2[] = "mountpoint/dst.txt";
+ const char RELPATH2[] = "dst.txt";
+ const uint64_t ino1 = 42;
+ const uint64_t ino2 = 43;
+ const uint64_t fh1 = 0xdeadbeef1a7ebabe;
+ const uint64_t fh2 = 0xdeadc0de88c0ffee;
+ off_t fsize2 = 0;
+ off_t start1 = 0;
+ off_t start2 = 0;
+ const char *contents = "Hello, world!";
+ ssize_t len;
+ int fd1, fd2;
+
+ len = strlen(contents);
+
+ /*
+ * Ensure that we read to EOF, just so the buffer cache's read size is
+ * predictable.
+ */
+ expect_lookup(RELPATH1, ino1, S_IFREG | 0644, start1 + len, 1);
+ expect_lookup(RELPATH2, ino2, S_IFREG | 0644, fsize2, 1);
+ expect_open(ino1, 0, 1, fh1);
+ expect_open(ino2, 0, 1, fh2);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_COPY_FILE_RANGE);
+ }, Eq(true)),
+ _)
+ ).Times(0);
+ expect_maybe_lseek(ino1);
+ expect_read(ino1, start1, len, len, contents, 0);
+ expect_write(ino2, start2, len, len, contents);
+
+ fd1 = open(FULLPATH1, O_RDONLY);
+ ASSERT_GE(fd1, 0);
+ fd2 = open(FULLPATH2, O_WRONLY);
+ ASSERT_GE(fd2, 0);
+ ASSERT_EQ(len, copy_file_range(fd1, &start1, fd2, &start2, len, 0));
+
+ leak(fd1);
+ leak(fd2);
+}
+
+/*
+ * With -o noatime, copy_file_range should update the destination's mtime and
+ * ctime, but not the source's atime.
+ */
+TEST_F(CopyFileRangeNoAtime, timestamps)
+{
+ const char FULLPATH1[] = "mountpoint/src.txt";
+ const char RELPATH1[] = "src.txt";
+ const char FULLPATH2[] = "mountpoint/dst.txt";
+ const char RELPATH2[] = "dst.txt";
+ struct stat sb1a, sb1b, sb2a, sb2b;
+ const uint64_t ino1 = 42;
+ const uint64_t ino2 = 43;
+ const uint64_t fh1 = 0xdeadbeef1a7ebabe;
+ const uint64_t fh2 = 0xdeadc0de88c0ffee;
+ off_t fsize1 = 1 << 20; /* 1 MiB */
+ off_t fsize2 = 1 << 19; /* 512 KiB */
+ off_t start1 = 1 << 18;
+ off_t start2 = 3 << 17;
+ ssize_t len = 65536;
+ int fd1, fd2;
+
+ expect_lookup(RELPATH1, ino1, S_IFREG | 0644, fsize1, 1);
+ expect_lookup(RELPATH2, ino2, S_IFREG | 0644, fsize2, 1);
+ expect_open(ino1, 0, 1, fh1);
+ expect_open(ino2, 0, 1, fh2);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_COPY_FILE_RANGE &&
+ in.header.nodeid == ino1 &&
+ in.body.copy_file_range.fh_in == fh1 &&
+ (off_t)in.body.copy_file_range.off_in == start1 &&
+ in.body.copy_file_range.nodeid_out == ino2 &&
+ in.body.copy_file_range.fh_out == fh2 &&
+ (off_t)in.body.copy_file_range.off_out == start2 &&
+ in.body.copy_file_range.len == (size_t)len &&
+ in.body.copy_file_range.flags == 0);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, write);
+ out.body.write.size = len;
+ })));
+
+ fd1 = open(FULLPATH1, O_RDONLY);
+ ASSERT_GE(fd1, 0);
+ fd2 = open(FULLPATH2, O_WRONLY);
+ ASSERT_GE(fd2, 0);
+ ASSERT_EQ(0, fstat(fd1, &sb1a)) << strerror(errno);
+ ASSERT_EQ(0, fstat(fd2, &sb2a)) << strerror(errno);
+
+ nap();
+
+ ASSERT_EQ(len, copy_file_range(fd1, &start1, fd2, &start2, len, 0));
+ ASSERT_EQ(0, fstat(fd1, &sb1b)) << strerror(errno);
+ ASSERT_EQ(0, fstat(fd2, &sb2b)) << strerror(errno);
+
+ EXPECT_EQ(sb1a.st_atime, sb1b.st_atime);
+ EXPECT_EQ(sb1a.st_mtime, sb1b.st_mtime);
+ EXPECT_EQ(sb1a.st_ctime, sb1b.st_ctime);
+ EXPECT_EQ(sb2a.st_atime, sb2b.st_atime);
+ EXPECT_NE(sb2a.st_mtime, sb2b.st_mtime);
+ EXPECT_NE(sb2a.st_ctime, sb2b.st_ctime);
+
+ leak(fd1);
+ leak(fd2);
+}
diff --git a/tests/sys/fs/fusefs/create.cc b/tests/sys/fs/fusefs/create.cc
new file mode 100644
index 000000000000..08126ffc9056
--- /dev/null
+++ b/tests/sys/fs/fusefs/create.cc
@@ -0,0 +1,493 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <fcntl.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class Create: public FuseTest {
+public:
+
+void expect_create(const char *relpath, mode_t mode, ProcessMockerT r)
+{
+ mode_t mask = umask(0);
+ (void)umask(mask);
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *name = (const char*)in.body.bytes +
+ sizeof(fuse_create_in);
+ return (in.header.opcode == FUSE_CREATE &&
+ in.body.create.mode == mode &&
+ in.body.create.umask == mask &&
+ (0 == strcmp(relpath, name)));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(r));
+}
+
+};
+
+/* FUSE_CREATE operations for a protocol 7.8 server */
+class Create_7_8: public Create {
+public:
+virtual void SetUp() {
+ m_kernel_minor_version = 8;
+ Create::SetUp();
+}
+
+void expect_create(const char *relpath, mode_t mode, ProcessMockerT r)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *name = (const char*)in.body.bytes +
+ sizeof(fuse_open_in);
+ return (in.header.opcode == FUSE_CREATE &&
+ in.body.create.mode == mode &&
+ (0 == strcmp(relpath, name)));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(r));
+}
+
+};
+
+/* FUSE_CREATE operations for a server built at protocol <= 7.11 */
+class Create_7_11: public FuseTest {
+public:
+virtual void SetUp() {
+ m_kernel_minor_version = 11;
+ FuseTest::SetUp();
+}
+
+void expect_create(const char *relpath, mode_t mode, ProcessMockerT r)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *name = (const char*)in.body.bytes +
+ sizeof(fuse_open_in);
+ return (in.header.opcode == FUSE_CREATE &&
+ in.body.create.mode == mode &&
+ (0 == strcmp(relpath, name)));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(r));
+}
+
+};
+
+
+/*
+ * If FUSE_CREATE sets attr_valid, then subsequent GETATTRs should use the
+ * attribute cache
+ */
+TEST_F(Create, attr_cache)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ mode_t mode = S_IFREG | 0755;
+ uint64_t ino = 42;
+ int fd;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_create(RELPATH, mode,
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, create);
+ out.body.create.entry.attr.mode = mode;
+ out.body.create.entry.nodeid = ino;
+ out.body.create.entry.entry_valid = UINT64_MAX;
+ out.body.create.entry.attr_valid = UINT64_MAX;
+ }));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).Times(0);
+
+ fd = open(FULLPATH, O_CREAT | O_EXCL, mode);
+ ASSERT_LE(0, fd) << strerror(errno);
+ leak(fd);
+}
+
+/* A successful CREATE operation should purge the parent dir's attr cache */
+TEST_F(Create, clear_attr_cache)
+{
+ const char FULLPATH[] = "mountpoint/src";
+ const char RELPATH[] = "src";
+ mode_t mode = S_IFREG | 0755;
+ uint64_t ino = 42;
+ int fd;
+ struct stat sb;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == FUSE_ROOT_ID);
+ }, Eq(true)),
+ _)
+ ).Times(2)
+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = FUSE_ROOT_ID;
+ out.body.attr.attr.mode = S_IFDIR | 0755;
+ out.body.attr.attr_valid = UINT64_MAX;
+ })));
+
+ expect_create(RELPATH, mode,
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, create);
+ out.body.create.entry.attr.mode = mode;
+ out.body.create.entry.nodeid = ino;
+ out.body.create.entry.entry_valid = UINT64_MAX;
+ out.body.create.entry.attr_valid = UINT64_MAX;
+ }));
+
+ EXPECT_EQ(0, stat("mountpoint", &sb)) << strerror(errno);
+ fd = open(FULLPATH, O_CREAT | O_EXCL, mode);
+ ASSERT_LE(0, fd) << strerror(errno);
+ EXPECT_EQ(0, stat("mountpoint", &sb)) << strerror(errno);
+
+ leak(fd);
+}
+
+/*
+ * The fuse daemon fails the request with EEXIST. This usually indicates a
+ * race condition: some other FUSE client created the file in between when the
+ * kernel checked for it with lookup and tried to create it with create
+ */
+TEST_F(Create, eexist)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ mode_t mode = S_IFREG | 0755;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_create(RELPATH, mode, ReturnErrno(EEXIST));
+ EXPECT_EQ(-1, open(FULLPATH, O_CREAT | O_EXCL, mode));
+ EXPECT_EQ(EEXIST, errno);
+}
+
+/*
+ * If the daemon doesn't implement FUSE_CREATE, then the kernel should fallback
+ * to FUSE_MKNOD/FUSE_OPEN
+ */
+TEST_F(Create, Enosys)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ mode_t mode = S_IFREG | 0755;
+ uint64_t ino = 42;
+ int fd;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_create(RELPATH, mode, ReturnErrno(ENOSYS));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *name = (const char*)in.body.bytes +
+ sizeof(fuse_mknod_in);
+ return (in.header.opcode == FUSE_MKNOD &&
+ in.body.mknod.mode == (S_IFREG | mode) &&
+ in.body.mknod.rdev == 0 &&
+ (0 == strcmp(RELPATH, name)));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.entry_valid = UINT64_MAX;
+ out.body.entry.attr_valid = UINT64_MAX;
+ })));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_OPEN &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([](auto in __unused, auto& out) {
+ out.header.len = sizeof(out.header);
+ SET_OUT_HEADER_LEN(out, open);
+ })));
+
+ fd = open(FULLPATH, O_CREAT | O_EXCL, mode);
+ ASSERT_LE(0, fd) << strerror(errno);
+ leak(fd);
+}
+
+/*
+ * Creating a new file after FUSE_LOOKUP returned a negative cache entry
+ */
+TEST_F(Create, entry_cache_negative)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ mode_t mode = S_IFREG | 0755;
+ uint64_t ino = 42;
+ int fd;
+ /*
+ * Set entry_valid = 0 because this test isn't concerned with whether
+ * or not we actually cache negative entries, only with whether we
+ * interpret negative cache responses correctly.
+ */
+ struct timespec entry_valid = {.tv_sec = 0, .tv_nsec = 0};
+
+ /* create will first do a LOOKUP, adding a negative cache entry */
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(ReturnNegativeCache(&entry_valid));
+ expect_create(RELPATH, mode,
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, create);
+ out.body.create.entry.attr.mode = mode;
+ out.body.create.entry.nodeid = ino;
+ out.body.create.entry.entry_valid = UINT64_MAX;
+ out.body.create.entry.attr_valid = UINT64_MAX;
+ }));
+
+ fd = open(FULLPATH, O_CREAT | O_EXCL, mode);
+ ASSERT_LE(0, fd) << strerror(errno);
+ leak(fd);
+}
+
+/*
+ * Creating a new file should purge any negative namecache entries
+ */
+TEST_F(Create, entry_cache_negative_purge)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ mode_t mode = S_IFREG | 0755;
+ uint64_t ino = 42;
+ int fd;
+ struct timespec entry_valid = {.tv_sec = TIME_T_MAX, .tv_nsec = 0};
+
+ /* create will first do a LOOKUP, adding a negative cache entry */
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH).Times(1)
+ .WillOnce(Invoke(ReturnNegativeCache(&entry_valid)))
+ .RetiresOnSaturation();
+
+ /* Then the CREATE should purge the negative cache entry */
+ expect_create(RELPATH, mode,
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, create);
+ out.body.create.entry.attr.mode = mode;
+ out.body.create.entry.nodeid = ino;
+ out.body.create.entry.attr_valid = UINT64_MAX;
+ }));
+
+ fd = open(FULLPATH, O_CREAT | O_EXCL, mode);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ /* Finally, a subsequent lookup should query the daemon */
+ expect_lookup(RELPATH, ino, S_IFREG | mode, 0, 1);
+
+ ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
+ leak(fd);
+}
+
+/*
+ * The daemon is responsible for checking file permissions (unless the
+ * default_permissions mount option was used)
+ */
+TEST_F(Create, eperm)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ mode_t mode = S_IFREG | 0755;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_create(RELPATH, mode, ReturnErrno(EPERM));
+
+ EXPECT_EQ(-1, open(FULLPATH, O_CREAT | O_EXCL, mode));
+ EXPECT_EQ(EPERM, errno);
+}
+
+TEST_F(Create, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ mode_t mode = S_IFREG | 0755;
+ uint64_t ino = 42;
+ int fd;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_create(RELPATH, mode,
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, create);
+ out.body.create.entry.attr.mode = mode;
+ out.body.create.entry.nodeid = ino;
+ out.body.create.entry.entry_valid = UINT64_MAX;
+ out.body.create.entry.attr_valid = UINT64_MAX;
+ }));
+
+ fd = open(FULLPATH, O_CREAT | O_EXCL, mode);
+ ASSERT_LE(0, fd) << strerror(errno);
+ leak(fd);
+}
+
+/*
+ * Nothing bad should happen if the server returns the parent's inode number
+ * for the newly created file. Regression test for bug 263662
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=263662
+ */
+TEST_F(Create, parent_inode)
+{
+ const char FULLPATH[] = "mountpoint/some_dir/some_file.txt";
+ const char RELDIRPATH[] = "some_dir";
+ const char RELPATH[] = "some_file.txt";
+ mode_t mode = 0755;
+ uint64_t ino = 42;
+ int fd;
+
+ expect_lookup(RELDIRPATH, ino, S_IFDIR | mode, 0, 1);
+ EXPECT_LOOKUP(ino, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_create(RELPATH, S_IFREG | mode,
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, create);
+ out.body.create.entry.attr.mode = S_IFREG | mode;
+ /* Return the same inode as the parent dir */
+ out.body.create.entry.nodeid = ino;
+ out.body.create.entry.entry_valid = UINT64_MAX;
+ out.body.create.entry.attr_valid = UINT64_MAX;
+ }));
+ // FUSE_RELEASE happens asynchronously, so it may or may not arrive
+ // before the test completes.
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_RELEASE);
+ }, Eq(true)),
+ _)
+ ).Times(AtMost(1))
+ .WillOnce(Invoke([=](auto in __unused, auto &out __unused) { }));
+
+ fd = open(FULLPATH, O_CREAT | O_EXCL, mode);
+ ASSERT_EQ(-1, fd);
+ EXPECT_EQ(EIO, errno);
+}
+
+/*
+ * A regression test for a bug that affected old FUSE implementations:
+ * open(..., O_WRONLY | O_CREAT, 0444) should work despite the seeming
+ * contradiction between O_WRONLY and 0444
+ *
+ * For example:
+ * https://bugs.launchpad.net/ubuntu/+source/sshfs-fuse/+bug/44886
+ */
+TEST_F(Create, wronly_0444)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ mode_t mode = S_IFREG | 0444;
+ uint64_t ino = 42;
+ int fd;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_create(RELPATH, mode,
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, create);
+ out.body.create.entry.attr.mode = mode;
+ out.body.create.entry.nodeid = ino;
+ out.body.create.entry.entry_valid = UINT64_MAX;
+ out.body.create.entry.attr_valid = UINT64_MAX;
+ }));
+
+ fd = open(FULLPATH, O_CREAT | O_WRONLY, mode);
+ ASSERT_LE(0, fd) << strerror(errno);
+ leak(fd);
+}
+
+TEST_F(Create_7_8, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ mode_t mode = S_IFREG | 0755;
+ uint64_t ino = 42;
+ int fd;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_create(RELPATH, mode,
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, create_7_8);
+ out.body.create_7_8.entry.attr.mode = mode;
+ out.body.create_7_8.entry.nodeid = ino;
+ out.body.create_7_8.entry.entry_valid = UINT64_MAX;
+ out.body.create_7_8.entry.attr_valid = UINT64_MAX;
+ out.body.create_7_8.open.fh = FH;
+ }));
+ expect_flush(ino, 1, ReturnErrno(0));
+ expect_release(ino, FH);
+
+ fd = open(FULLPATH, O_CREAT | O_EXCL, mode);
+ ASSERT_LE(0, fd) << strerror(errno);
+ close(fd);
+}
+
+TEST_F(Create_7_11, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ mode_t mode = S_IFREG | 0755;
+ uint64_t ino = 42;
+ int fd;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_create(RELPATH, mode,
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, create);
+ out.body.create.entry.attr.mode = mode;
+ out.body.create.entry.nodeid = ino;
+ out.body.create.entry.entry_valid = UINT64_MAX;
+ out.body.create.entry.attr_valid = UINT64_MAX;
+ }));
+
+ fd = open(FULLPATH, O_CREAT | O_EXCL, mode);
+ ASSERT_LE(0, fd) << strerror(errno);
+ leak(fd);
+}
diff --git a/tests/sys/fs/fusefs/ctl.sh b/tests/sys/fs/fusefs/ctl.sh
new file mode 100644
index 000000000000..7d2e7593cbdc
--- /dev/null
+++ b/tests/sys/fs/fusefs/ctl.sh
@@ -0,0 +1,69 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 ConnectWise
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS DOCUMENTATION IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+. $(atf_get_srcdir)/../../cam/ctl/ctl.subr
+
+# Regression test for https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=283402
+#
+# Almost any fuse file system would work, but this tests uses fusefs-ext2
+# because it's simple and its download is very small.
+atf_test_case remove_lun_with_atime cleanup
+remove_lun_with_atime_head()
+{
+ atf_set "descr" "Remove a fuse-backed CTL LUN when atime is enabled"
+ atf_set "require.user" "root"
+ atf_set "require.progs" "fuse-ext2 mkfs.ext2"
+}
+remove_lun_with_atime_body()
+{
+ MOUNTPOINT=$PWD/mnt
+ atf_check mkdir $MOUNTPOINT
+ atf_check truncate -s 1g ext2.img
+ atf_check mkfs.ext2 -q ext2.img
+ # Note: both default_permissions and atime must be enabled
+ atf_check fuse-ext2 -o default_permissions,allow_other,rw+ ext2.img \
+ $MOUNTPOINT
+
+ atf_check truncate -s 1m $MOUNTPOINT/file
+ create_block -o file=$MOUNTPOINT/file
+
+ # Force fusefs to open the file, and dirty its atime
+ atf_check dd if=/dev/$dev of=/dev/null count=1 status=none
+
+ # Finally, remove the LUN. Hopefully it won't panic.
+ atf_check -o ignore ctladm remove -b block -l $LUN
+
+ rm lun-create.txt # So we don't try to remove the LUN twice
+}
+remove_lun_with_atime_cleanup()
+{
+ cleanup
+ umount $PWD/mnt
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case remove_lun_with_atime
+}
diff --git a/tests/sys/fs/fusefs/default_permissions.cc b/tests/sys/fs/fusefs/default_permissions.cc
new file mode 100644
index 000000000000..4b04297d97ac
--- /dev/null
+++ b/tests/sys/fs/fusefs/default_permissions.cc
@@ -0,0 +1,1644 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Tests for the "default_permissions" mount option. They must be in their own
+ * file so they can be run as an unprivileged user.
+ */
+
+extern "C" {
+#include <sys/types.h>
+#include <sys/extattr.h>
+
+#include <fcntl.h>
+#include <semaphore.h>
+#include <unistd.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class DefaultPermissions: public FuseTest {
+
+virtual void SetUp() {
+ m_default_permissions = true;
+ FuseTest::SetUp();
+ if (HasFatalFailure() || IsSkipped())
+ return;
+
+ if (geteuid() == 0) {
+ GTEST_SKIP() << "This test requires an unprivileged user";
+ }
+
+ /* With -o default_permissions, FUSE_ACCESS should never be called */
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_ACCESS);
+ }, Eq(true)),
+ _)
+ ).Times(0);
+}
+
+public:
+void expect_chmod(uint64_t ino, mode_t mode, uint64_t size = 0)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino &&
+ in.body.setattr.valid == FATTR_MODE &&
+ in.body.setattr.mode == mode);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino; // Must match nodeid
+ out.body.attr.attr.mode = S_IFREG | mode;
+ out.body.attr.attr.size = size;
+ out.body.attr.attr_valid = UINT64_MAX;
+ })));
+}
+
+void expect_create(const char *relpath, uint64_t ino)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *name = (const char*)in.body.bytes +
+ sizeof(fuse_create_in);
+ return (in.header.opcode == FUSE_CREATE &&
+ (0 == strcmp(relpath, name)));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, create);
+ out.body.create.entry.attr.mode = S_IFREG | 0644;
+ out.body.create.entry.nodeid = ino;
+ out.body.create.entry.entry_valid = UINT64_MAX;
+ out.body.create.entry.attr_valid = UINT64_MAX;
+ })));
+}
+
+void expect_copy_file_range(uint64_t ino_in, uint64_t off_in, uint64_t ino_out,
+ uint64_t off_out, uint64_t len)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_COPY_FILE_RANGE &&
+ in.header.nodeid == ino_in &&
+ in.body.copy_file_range.off_in == off_in &&
+ in.body.copy_file_range.nodeid_out == ino_out &&
+ in.body.copy_file_range.off_out == off_out &&
+ in.body.copy_file_range.len == len);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, write);
+ out.body.write.size = len;
+ })));
+}
+
+void expect_getattr(uint64_t ino, mode_t mode, uint64_t attr_valid, int times,
+ uid_t uid = 0, gid_t gid = 0)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).Times(times)
+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino; // Must match nodeid
+ out.body.attr.attr.mode = mode;
+ out.body.attr.attr.size = 0;
+ out.body.attr.attr.uid = uid;
+ out.body.attr.attr.gid = gid;
+ out.body.attr.attr_valid = attr_valid;
+ })));
+}
+
+void expect_lookup(const char *relpath, uint64_t ino, mode_t mode,
+ uint64_t attr_valid, uid_t uid = 0, gid_t gid = 0)
+{
+ FuseTest::expect_lookup(relpath, ino, mode, 0, 1, attr_valid, uid, gid);
+}
+
+};
+
+class Access: public DefaultPermissions {};
+class Chown: public DefaultPermissions {};
+class Chgrp: public DefaultPermissions {};
+class CopyFileRange: public DefaultPermissions {};
+class Fspacectl: public DefaultPermissions {};
+class Lookup: public DefaultPermissions {};
+class Open: public DefaultPermissions {};
+class PosixFallocate: public DefaultPermissions {};
+class Read: public DefaultPermissions {};
+class Setattr: public DefaultPermissions {};
+class Unlink: public DefaultPermissions {};
+class Utimensat: public DefaultPermissions {};
+class Write: public DefaultPermissions {};
+
+/*
+ * Test permission handling during create, mkdir, mknod, link, symlink, and
+ * rename vops (they all share a common path for permission checks in
+ * VOP_LOOKUP)
+ */
+class Create: public DefaultPermissions {};
+
+class Deleteextattr: public DefaultPermissions {
+public:
+void expect_removexattr()
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_REMOVEXATTR);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(0)));
+}
+};
+
+class Getextattr: public DefaultPermissions {
+public:
+void expect_getxattr(ProcessMockerT r)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETXATTR);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(r));
+}
+};
+
+class Listextattr: public DefaultPermissions {
+public:
+void expect_listxattr()
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_LISTXATTR);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([](auto i __unused, auto& out) {
+ out.body.listxattr.size = 0;
+ SET_OUT_HEADER_LEN(out, listxattr);
+ })));
+}
+};
+
+class Rename: public DefaultPermissions {
+public:
+ /*
+ * Expect a rename and respond with the given error. Don't both to
+ * validate arguments; the tests in rename.cc do that.
+ */
+ void expect_rename(int error)
+ {
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_RENAME);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(error)));
+ }
+};
+
+class Setextattr: public DefaultPermissions {
+public:
+void expect_setxattr(int error)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_SETXATTR);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(error)));
+}
+};
+
+/* Return a group to which this user does not belong */
+static gid_t excluded_group()
+{
+ int i, ngroups = 64;
+ gid_t newgid, groups[ngroups];
+
+ getgrouplist(getlogin(), getegid(), groups, &ngroups);
+ for (newgid = 0; ; newgid++) {
+ bool belongs = false;
+
+ for (i = 0; i < ngroups; i++) {
+ if (groups[i] == newgid)
+ belongs = true;
+ }
+ if (!belongs)
+ break;
+ }
+ /* newgid is now a group to which the current user does not belong */
+ return newgid;
+}
+
+TEST_F(Access, eacces)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ mode_t access_mode = X_OK;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX);
+
+ ASSERT_NE(0, access(FULLPATH, access_mode));
+ ASSERT_EQ(EACCES, errno);
+}
+
+TEST_F(Access, eacces_no_cached_attrs)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ mode_t access_mode = X_OK;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, 0, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0);
+ expect_getattr(ino, S_IFREG | 0644, 0, 1);
+ /*
+ * Once default_permissions is properly implemented, there might be
+ * another FUSE_GETATTR or something in here. But there should not be
+ * a FUSE_ACCESS
+ */
+
+ ASSERT_NE(0, access(FULLPATH, access_mode));
+ ASSERT_EQ(EACCES, errno);
+}
+
+TEST_F(Access, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ mode_t access_mode = R_OK;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX);
+ /*
+ * Once default_permissions is properly implemented, there might be
+ * another FUSE_GETATTR or something in here.
+ */
+
+ ASSERT_EQ(0, access(FULLPATH, access_mode)) << strerror(errno);
+}
+
+/* Unprivileged users may chown a file to their own uid */
+TEST_F(Chown, chown_to_self)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ const mode_t mode = 0755;
+ uid_t uid;
+
+ uid = geteuid();
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1, uid);
+ expect_lookup(RELPATH, ino, S_IFREG | mode, UINT64_MAX, uid);
+ /* The OS may optimize chown by omitting the redundant setattr */
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_SETATTR);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out){
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.mode = S_IFREG | mode;
+ out.body.attr.attr.uid = uid;
+ })));
+
+ EXPECT_EQ(0, chown(FULLPATH, uid, -1)) << strerror(errno);
+}
+
+/*
+ * A successful chown by a non-privileged non-owner should clear a file's SUID
+ * bit
+ */
+TEST_F(Chown, clear_suid)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ const mode_t oldmode = 06755;
+ const mode_t newmode = 0755;
+ uid_t uid = geteuid();
+ uint32_t valid = FATTR_UID | FATTR_MODE;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1, uid);
+ expect_lookup(RELPATH, ino, S_IFREG | oldmode, UINT64_MAX, uid);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino &&
+ in.body.setattr.valid == valid &&
+ in.body.setattr.mode == newmode);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino; // Must match nodeid
+ out.body.attr.attr.mode = S_IFREG | newmode;
+ out.body.attr.attr_valid = UINT64_MAX;
+ })));
+
+ EXPECT_EQ(0, chown(FULLPATH, uid, -1)) << strerror(errno);
+}
+
+
+/* Only root may change a file's owner */
+TEST_F(Chown, eperm)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ const mode_t mode = 0755;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1, geteuid());
+ expect_lookup(RELPATH, ino, S_IFREG | mode, UINT64_MAX, geteuid());
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_SETATTR);
+ }, Eq(true)),
+ _)
+ ).Times(0);
+
+ EXPECT_NE(0, chown(FULLPATH, 0, -1));
+ EXPECT_EQ(EPERM, errno);
+}
+
+/*
+ * A successful chgrp by a non-privileged non-owner should clear a file's SUID
+ * bit
+ */
+TEST_F(Chgrp, clear_suid)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ const mode_t oldmode = 06755;
+ const mode_t newmode = 0755;
+ uid_t uid = geteuid();
+ gid_t gid = getegid();
+ uint32_t valid = FATTR_GID | FATTR_MODE;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1, uid);
+ expect_lookup(RELPATH, ino, S_IFREG | oldmode, UINT64_MAX, uid, gid);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino &&
+ in.body.setattr.valid == valid &&
+ in.body.setattr.mode == newmode);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino; // Must match nodeid
+ out.body.attr.attr.mode = S_IFREG | newmode;
+ out.body.attr.attr_valid = UINT64_MAX;
+ })));
+
+ EXPECT_EQ(0, chown(FULLPATH, -1, gid)) << strerror(errno);
+}
+
+/* non-root users may only chgrp a file to a group they belong to */
+TEST_F(Chgrp, eperm)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ const mode_t mode = 0755;
+ uid_t uid;
+ gid_t gid, newgid;
+
+ uid = geteuid();
+ gid = getegid();
+ newgid = excluded_group();
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1, uid, gid);
+ expect_lookup(RELPATH, ino, S_IFREG | mode, UINT64_MAX, uid, gid);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_SETATTR);
+ }, Eq(true)),
+ _)
+ ).Times(0);
+
+ EXPECT_NE(0, chown(FULLPATH, -1, newgid));
+ EXPECT_EQ(EPERM, errno);
+}
+
+TEST_F(Chgrp, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ const mode_t mode = 0755;
+ uid_t uid;
+ gid_t gid, newgid;
+
+ uid = geteuid();
+ gid = 0;
+ newgid = getegid();
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1, uid, gid);
+ expect_lookup(RELPATH, ino, S_IFREG | mode, UINT64_MAX, uid, gid);
+ /* The OS may optimize chgrp by omitting the redundant setattr */
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out){
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.mode = S_IFREG | mode;
+ out.body.attr.attr.uid = uid;
+ out.body.attr.attr.gid = newgid;
+ })));
+
+ EXPECT_EQ(0, chown(FULLPATH, -1, newgid)) << strerror(errno);
+}
+
+/* A write by a non-owner should clear a file's SGID bit */
+TEST_F(CopyFileRange, clear_sgid)
+{
+ const char FULLPATH_IN[] = "mountpoint/in.txt";
+ const char RELPATH_IN[] = "in.txt";
+ const char FULLPATH_OUT[] = "mountpoint/out.txt";
+ const char RELPATH_OUT[] = "out.txt";
+ struct stat sb;
+ uint64_t ino_in = 42;
+ uint64_t ino_out = 43;
+ mode_t oldmode = 02777;
+ mode_t newmode = 0777;
+ off_t fsize = 16;
+ off_t off_in = 0;
+ off_t off_out = 8;
+ off_t len = 8;
+ int fd_in, fd_out;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ FuseTest::expect_lookup(RELPATH_IN, ino_in, S_IFREG | oldmode, fsize, 1,
+ UINT64_MAX, 0, 0);
+ expect_open(ino_in, 0, 1);
+ FuseTest::expect_lookup(RELPATH_OUT, ino_out, S_IFREG | oldmode, fsize,
+ 1, UINT64_MAX, 0, 0);
+ expect_open(ino_out, 0, 1);
+ expect_copy_file_range(ino_in, off_in, ino_out, off_out, len);
+ expect_chmod(ino_out, newmode, fsize);
+
+ fd_in = open(FULLPATH_IN, O_RDONLY);
+ ASSERT_LE(0, fd_in) << strerror(errno);
+ fd_out = open(FULLPATH_OUT, O_WRONLY);
+ ASSERT_LE(0, fd_out) << strerror(errno);
+ ASSERT_EQ(len,
+ copy_file_range(fd_in, &off_in, fd_out, &off_out, len, 0))
+ << strerror(errno);
+ ASSERT_EQ(0, fstat(fd_out, &sb)) << strerror(errno);
+ EXPECT_EQ(S_IFREG | newmode, sb.st_mode);
+ ASSERT_EQ(0, fstat(fd_in, &sb)) << strerror(errno);
+ EXPECT_EQ(S_IFREG | oldmode, sb.st_mode);
+
+ leak(fd_in);
+ leak(fd_out);
+}
+
+/* A write by a non-owner should clear a file's SUID bit */
+TEST_F(CopyFileRange, clear_suid)
+{
+ const char FULLPATH_IN[] = "mountpoint/in.txt";
+ const char RELPATH_IN[] = "in.txt";
+ const char FULLPATH_OUT[] = "mountpoint/out.txt";
+ const char RELPATH_OUT[] = "out.txt";
+ struct stat sb;
+ uint64_t ino_in = 42;
+ uint64_t ino_out = 43;
+ mode_t oldmode = 04777;
+ mode_t newmode = 0777;
+ off_t fsize = 16;
+ off_t off_in = 0;
+ off_t off_out = 8;
+ off_t len = 8;
+ int fd_in, fd_out;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ FuseTest::expect_lookup(RELPATH_IN, ino_in, S_IFREG | oldmode, fsize, 1,
+ UINT64_MAX, 0, 0);
+ expect_open(ino_in, 0, 1);
+ FuseTest::expect_lookup(RELPATH_OUT, ino_out, S_IFREG | oldmode, fsize,
+ 1, UINT64_MAX, 0, 0);
+ expect_open(ino_out, 0, 1);
+ expect_copy_file_range(ino_in, off_in, ino_out, off_out, len);
+ expect_chmod(ino_out, newmode, fsize);
+
+ fd_in = open(FULLPATH_IN, O_RDONLY);
+ ASSERT_LE(0, fd_in) << strerror(errno);
+ fd_out = open(FULLPATH_OUT, O_WRONLY);
+ ASSERT_LE(0, fd_out) << strerror(errno);
+ ASSERT_EQ(len,
+ copy_file_range(fd_in, &off_in, fd_out, &off_out, len, 0))
+ << strerror(errno);
+ ASSERT_EQ(0, fstat(fd_out, &sb)) << strerror(errno);
+ EXPECT_EQ(S_IFREG | newmode, sb.st_mode);
+ ASSERT_EQ(0, fstat(fd_in, &sb)) << strerror(errno);
+ EXPECT_EQ(S_IFREG | oldmode, sb.st_mode);
+
+ leak(fd_in);
+ leak(fd_out);
+}
+
+TEST_F(Create, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int fd;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0777, UINT64_MAX, 1);
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_create(RELPATH, ino);
+
+ fd = open(FULLPATH, O_CREAT | O_EXCL, 0644);
+ ASSERT_LE(0, fd) << strerror(errno);
+ leak(fd);
+}
+
+TEST_F(Create, eacces)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+
+ ASSERT_EQ(-1, open(FULLPATH, O_CREAT | O_EXCL, 0644));
+ EXPECT_EQ(EACCES, errno);
+}
+
+TEST_F(Deleteextattr, eacces)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int ns = EXTATTR_NAMESPACE_USER;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, 0);
+
+ ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo"));
+ ASSERT_EQ(EACCES, errno);
+}
+
+TEST_F(Deleteextattr, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int ns = EXTATTR_NAMESPACE_USER;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, geteuid());
+ expect_removexattr();
+
+ ASSERT_EQ(0, extattr_delete_file(FULLPATH, ns, "foo"))
+ << strerror(errno);
+}
+
+/* Delete system attributes requires superuser privilege */
+TEST_F(Deleteextattr, system)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int ns = EXTATTR_NAMESPACE_SYSTEM;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | 0666, UINT64_MAX, geteuid());
+
+ ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo"));
+ ASSERT_EQ(EPERM, errno);
+}
+
+/* Anybody with write permission can set both timestamps to UTIME_NOW */
+TEST_F(Utimensat, utime_now)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ /* Write permissions for everybody */
+ const mode_t mode = 0666;
+ uid_t owner = 0;
+ const timespec times[2] = {
+ {.tv_sec = 0, .tv_nsec = UTIME_NOW},
+ {.tv_sec = 0, .tv_nsec = UTIME_NOW},
+ };
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | mode, UINT64_MAX, owner);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino &&
+ in.body.setattr.valid & FATTR_ATIME &&
+ in.body.setattr.valid & FATTR_MTIME);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.mode = S_IFREG | mode;
+ })));
+
+ ASSERT_EQ(0, utimensat(AT_FDCWD, FULLPATH, &times[0], 0))
+ << strerror(errno);
+}
+
+/* Anybody can set both timestamps to UTIME_OMIT */
+TEST_F(Utimensat, utime_omit)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ /* Write permissions for no one */
+ const mode_t mode = 0444;
+ uid_t owner = 0;
+ const timespec times[2] = {
+ {.tv_sec = 0, .tv_nsec = UTIME_OMIT},
+ {.tv_sec = 0, .tv_nsec = UTIME_OMIT},
+ };
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | mode, UINT64_MAX, owner);
+
+ ASSERT_EQ(0, utimensat(AT_FDCWD, FULLPATH, &times[0], 0))
+ << strerror(errno);
+}
+
+/* Deleting user attributes merely requires WRITE privilege */
+TEST_F(Deleteextattr, user)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int ns = EXTATTR_NAMESPACE_USER;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | 0666, UINT64_MAX, 0);
+ expect_removexattr();
+
+ ASSERT_EQ(0, extattr_delete_file(FULLPATH, ns, "foo"))
+ << strerror(errno);
+}
+
+TEST_F(Getextattr, eacces)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ char data[80];
+ int ns = EXTATTR_NAMESPACE_USER;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | 0600, UINT64_MAX, 0);
+
+ ASSERT_EQ(-1,
+ extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data)));
+ ASSERT_EQ(EACCES, errno);
+}
+
+TEST_F(Getextattr, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ char data[80];
+ const char value[] = "whatever";
+ ssize_t value_len = strlen(value) + 1;
+ int ns = EXTATTR_NAMESPACE_USER;
+ ssize_t r;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ /* Getting user attributes only requires read access */
+ expect_lookup(RELPATH, ino, S_IFREG | 0444, UINT64_MAX, 0);
+ expect_getxattr(
+ ReturnImmediate([&](auto in __unused, auto& out) {
+ memcpy((void*)out.body.bytes, value, value_len);
+ out.header.len = sizeof(out.header) + value_len;
+ })
+ );
+
+ r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data));
+ ASSERT_EQ(value_len, r) << strerror(errno);
+ EXPECT_STREQ(value, data);
+}
+
+/* Getting system attributes requires superuser privileges */
+TEST_F(Getextattr, system)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ char data[80];
+ int ns = EXTATTR_NAMESPACE_SYSTEM;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | 0666, UINT64_MAX, geteuid());
+
+ ASSERT_EQ(-1,
+ extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data)));
+ ASSERT_EQ(EPERM, errno);
+}
+
+TEST_F(Listextattr, eacces)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int ns = EXTATTR_NAMESPACE_USER;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0777, UINT64_MAX, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | 0600, UINT64_MAX, 0);
+
+ ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0));
+ ASSERT_EQ(EACCES, errno);
+}
+
+TEST_F(Listextattr, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int ns = EXTATTR_NAMESPACE_USER;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0777, UINT64_MAX, 1);
+ /* Listing user extended attributes merely requires read access */
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, 0);
+ expect_listxattr();
+
+ ASSERT_EQ(0, extattr_list_file(FULLPATH, ns, NULL, 0))
+ << strerror(errno);
+}
+
+/* Listing system xattrs requires superuser privileges */
+TEST_F(Listextattr, system)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int ns = EXTATTR_NAMESPACE_SYSTEM;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0777, UINT64_MAX, 1);
+ /* Listing user extended attributes merely requires read access */
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, geteuid());
+
+ ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0));
+ ASSERT_EQ(EPERM, errno);
+}
+
+/* A write by a non-owner should clear a file's SGID bit */
+TEST_F(Fspacectl, clear_sgid)
+{
+ const char FULLPATH[] = "mountpoint/file.txt";
+ const char RELPATH[] = "file.txt";
+ struct stat sb;
+ struct spacectl_range rqsr;
+ uint64_t ino = 42;
+ mode_t oldmode = 02777;
+ mode_t newmode = 0777;
+ off_t fsize = 16;
+ off_t off = 8;
+ off_t len = 8;
+ int fd;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ FuseTest::expect_lookup(RELPATH, ino, S_IFREG | oldmode, fsize,
+ 1, UINT64_MAX, 0, 0);
+ expect_open(ino, 0, 1);
+ expect_fallocate(ino, off, len,
+ FUSE_FALLOC_FL_KEEP_SIZE | FUSE_FALLOC_FL_PUNCH_HOLE, 0);
+ expect_chmod(ino, newmode, fsize);
+
+ fd = open(FULLPATH, O_WRONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+ rqsr.r_len = len;
+ rqsr.r_offset = off;
+ EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, NULL));
+ ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
+ EXPECT_EQ(S_IFREG | newmode, sb.st_mode);
+
+ leak(fd);
+}
+
+/* A write by a non-owner should clear a file's SUID bit */
+TEST_F(Fspacectl, clear_suid)
+{
+ const char FULLPATH[] = "mountpoint/file.txt";
+ const char RELPATH[] = "file.txt";
+ struct stat sb;
+ struct spacectl_range rqsr;
+ uint64_t ino = 42;
+ mode_t oldmode = 04777;
+ mode_t newmode = 0777;
+ off_t fsize = 16;
+ off_t off = 8;
+ off_t len = 8;
+ int fd;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ FuseTest::expect_lookup(RELPATH, ino, S_IFREG | oldmode, fsize,
+ 1, UINT64_MAX, 0, 0);
+ expect_open(ino, 0, 1);
+ expect_fallocate(ino, off, len,
+ FUSE_FALLOC_FL_KEEP_SIZE | FUSE_FALLOC_FL_PUNCH_HOLE, 0);
+ expect_chmod(ino, newmode, fsize);
+
+ fd = open(FULLPATH, O_WRONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+ rqsr.r_len = len;
+ rqsr.r_offset = off;
+ EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, NULL));
+ ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
+ EXPECT_EQ(S_IFREG | newmode, sb.st_mode);
+
+ leak(fd);
+}
+
+/*
+ * fspacectl() of a file without writable permissions should succeed as
+ * long as the file descriptor is writable. This is important when combined
+ * with O_CREAT
+ */
+TEST_F(Fspacectl, posix_fallocate_of_newly_created_file)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ struct spacectl_range rqsr;
+ const uint64_t ino = 42;
+ off_t off = 8;
+ off_t len = 8;
+ int fd;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0777, UINT64_MAX, 1);
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_create(RELPATH, ino);
+ expect_fallocate(ino, off, len,
+ FUSE_FALLOC_FL_KEEP_SIZE | FUSE_FALLOC_FL_PUNCH_HOLE, 0);
+
+ fd = open(FULLPATH, O_CREAT | O_RDWR, 0);
+ ASSERT_LE(0, fd) << strerror(errno);
+ rqsr.r_len = len;
+ rqsr.r_offset = off;
+ EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, NULL));
+ leak(fd);
+}
+
+/* A component of the search path lacks execute permissions */
+TEST_F(Lookup, eacces)
+{
+ const char FULLPATH[] = "mountpoint/some_dir/some_file.txt";
+ const char RELDIRPATH[] = "some_dir";
+ uint64_t dir_ino = 42;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ expect_lookup(RELDIRPATH, dir_ino, S_IFDIR | 0700, UINT64_MAX, 0);
+
+ EXPECT_EQ(-1, access(FULLPATH, F_OK));
+ EXPECT_EQ(EACCES, errno);
+}
+
+TEST_F(Open, eacces)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX);
+
+ EXPECT_EQ(-1, open(FULLPATH, O_RDWR));
+ EXPECT_EQ(EACCES, errno);
+}
+
+TEST_F(Open, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int fd;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX);
+ expect_open(ino, 0, 1);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+ leak(fd);
+}
+
+/* A write by a non-owner should clear a file's SGID bit */
+TEST_F(PosixFallocate, clear_sgid)
+{
+ const char FULLPATH[] = "mountpoint/file.txt";
+ const char RELPATH[] = "file.txt";
+ struct stat sb;
+ uint64_t ino = 42;
+ mode_t oldmode = 02777;
+ mode_t newmode = 0777;
+ off_t fsize = 16;
+ off_t off = 8;
+ off_t len = 8;
+ int fd;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ FuseTest::expect_lookup(RELPATH, ino, S_IFREG | oldmode, fsize,
+ 1, UINT64_MAX, 0, 0);
+ expect_open(ino, 0, 1);
+ expect_fallocate(ino, off, len, 0, 0);
+ expect_chmod(ino, newmode, fsize);
+
+ fd = open(FULLPATH, O_WRONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+ EXPECT_EQ(0, posix_fallocate(fd, off, len)) << strerror(errno);
+ ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
+ EXPECT_EQ(S_IFREG | newmode, sb.st_mode);
+
+ leak(fd);
+}
+
+/* A write by a non-owner should clear a file's SUID bit */
+TEST_F(PosixFallocate, clear_suid)
+{
+ const char FULLPATH[] = "mountpoint/file.txt";
+ const char RELPATH[] = "file.txt";
+ struct stat sb;
+ uint64_t ino = 42;
+ mode_t oldmode = 04777;
+ mode_t newmode = 0777;
+ off_t fsize = 16;
+ off_t off = 8;
+ off_t len = 8;
+ int fd;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ FuseTest::expect_lookup(RELPATH, ino, S_IFREG | oldmode, fsize,
+ 1, UINT64_MAX, 0, 0);
+ expect_open(ino, 0, 1);
+ expect_fallocate(ino, off, len, 0, 0);
+ expect_chmod(ino, newmode, fsize);
+
+ fd = open(FULLPATH, O_WRONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+ EXPECT_EQ(0, posix_fallocate(fd, off, len)) << strerror(errno);
+ ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
+ EXPECT_EQ(S_IFREG | newmode, sb.st_mode);
+
+ leak(fd);
+}
+
+/*
+ * posix_fallocate() of a file without writable permissions should succeed as
+ * long as the file descriptor is writable. This is important when combined
+ * with O_CREAT
+ */
+TEST_F(PosixFallocate, posix_fallocate_of_newly_created_file)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ off_t off = 8;
+ off_t len = 8;
+ int fd;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0777, UINT64_MAX, 1);
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_create(RELPATH, ino);
+ expect_fallocate(ino, off, len, 0, 0);
+
+ fd = open(FULLPATH, O_CREAT | O_RDWR, 0);
+ ASSERT_LE(0, fd) << strerror(errno);
+ EXPECT_EQ(0, posix_fallocate(fd, off, len)) << strerror(errno);
+ leak(fd);
+}
+
+TEST_F(Rename, eacces_on_srcdir)
+{
+ const char FULLDST[] = "mountpoint/d/dst";
+ const char RELDST[] = "d/dst";
+ const char FULLSRC[] = "mountpoint/src";
+ const char RELSRC[] = "src";
+ uint64_t ino = 42;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1, 0);
+ expect_lookup(RELSRC, ino, S_IFREG | 0644, UINT64_MAX);
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDST)
+ .Times(AnyNumber())
+ .WillRepeatedly(Invoke(ReturnErrno(ENOENT)));
+
+ ASSERT_EQ(-1, rename(FULLSRC, FULLDST));
+ ASSERT_EQ(EACCES, errno);
+}
+
+TEST_F(Rename, eacces_on_dstdir_for_creating)
+{
+ const char FULLDST[] = "mountpoint/d/dst";
+ const char RELDSTDIR[] = "d";
+ const char RELDST[] = "dst";
+ const char FULLSRC[] = "mountpoint/src";
+ const char RELSRC[] = "src";
+ uint64_t src_ino = 42;
+ uint64_t dstdir_ino = 43;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0777, UINT64_MAX, 1, 0);
+ expect_lookup(RELSRC, src_ino, S_IFREG | 0644, UINT64_MAX);
+ expect_lookup(RELDSTDIR, dstdir_ino, S_IFDIR | 0755, UINT64_MAX);
+ EXPECT_LOOKUP(dstdir_ino, RELDST).WillOnce(Invoke(ReturnErrno(ENOENT)));
+
+ ASSERT_EQ(-1, rename(FULLSRC, FULLDST));
+ ASSERT_EQ(EACCES, errno);
+}
+
+TEST_F(Rename, eacces_on_dstdir_for_removing)
+{
+ const char FULLDST[] = "mountpoint/d/dst";
+ const char RELDSTDIR[] = "d";
+ const char RELDST[] = "dst";
+ const char FULLSRC[] = "mountpoint/src";
+ const char RELSRC[] = "src";
+ uint64_t src_ino = 42;
+ uint64_t dstdir_ino = 43;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0777, UINT64_MAX, 1, 0);
+ expect_lookup(RELSRC, src_ino, S_IFREG | 0644, UINT64_MAX);
+ expect_lookup(RELDSTDIR, dstdir_ino, S_IFDIR | 0755, UINT64_MAX);
+ EXPECT_LOOKUP(dstdir_ino, RELDST).WillOnce(Invoke(ReturnErrno(ENOENT)));
+
+ ASSERT_EQ(-1, rename(FULLSRC, FULLDST));
+ ASSERT_EQ(EACCES, errno);
+}
+
+TEST_F(Rename, eperm_on_sticky_srcdir)
+{
+ const char FULLDST[] = "mountpoint/d/dst";
+ const char FULLSRC[] = "mountpoint/src";
+ const char RELSRC[] = "src";
+ uint64_t ino = 42;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 01777, UINT64_MAX, 1, 0);
+ expect_lookup(RELSRC, ino, S_IFREG | 0644, UINT64_MAX);
+
+ ASSERT_EQ(-1, rename(FULLSRC, FULLDST));
+ ASSERT_EQ(EPERM, errno);
+}
+
+/*
+ * A user cannot move out a subdirectory that he does not own, because that
+ * would require changing the subdirectory's ".." dirent
+ */
+TEST_F(Rename, eperm_for_subdirectory)
+{
+ const char FULLDST[] = "mountpoint/d/dst";
+ const char FULLSRC[] = "mountpoint/src";
+ const char RELDSTDIR[] = "d";
+ const char RELDST[] = "dst";
+ const char RELSRC[] = "src";
+ uint64_t ino = 42;
+ uint64_t dstdir_ino = 43;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0777, UINT64_MAX, 1, 0);
+ expect_lookup(RELSRC, ino, S_IFDIR | 0755, UINT64_MAX, 0);
+ expect_lookup(RELDSTDIR, dstdir_ino, S_IFDIR | 0777, UINT64_MAX, 0);
+ EXPECT_LOOKUP(dstdir_ino, RELDST).WillOnce(Invoke(ReturnErrno(ENOENT)));
+
+ ASSERT_EQ(-1, rename(FULLSRC, FULLDST));
+ ASSERT_EQ(EACCES, errno);
+}
+
+/*
+ * A user _can_ rename a subdirectory to which he lacks write permissions, if
+ * it will keep the same parent
+ */
+TEST_F(Rename, subdirectory_to_same_dir)
+{
+ const char FULLDST[] = "mountpoint/dst";
+ const char FULLSRC[] = "mountpoint/src";
+ const char RELDST[] = "dst";
+ const char RELSRC[] = "src";
+ uint64_t ino = 42;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0777, UINT64_MAX, 1, 0);
+ expect_lookup(RELSRC, ino, S_IFDIR | 0755, UINT64_MAX, 0);
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDST)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_rename(0);
+
+ ASSERT_EQ(0, rename(FULLSRC, FULLDST)) << strerror(errno);
+}
+
+TEST_F(Rename, eperm_on_sticky_dstdir)
+{
+ const char FULLDST[] = "mountpoint/d/dst";
+ const char RELDSTDIR[] = "d";
+ const char RELDST[] = "dst";
+ const char FULLSRC[] = "mountpoint/src";
+ const char RELSRC[] = "src";
+ uint64_t src_ino = 42;
+ uint64_t dstdir_ino = 43;
+ uint64_t dst_ino = 44;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0777, UINT64_MAX, 1, 0);
+ expect_lookup(RELSRC, src_ino, S_IFREG | 0644, UINT64_MAX);
+ expect_lookup(RELDSTDIR, dstdir_ino, S_IFDIR | 01777, UINT64_MAX);
+ EXPECT_LOOKUP(dstdir_ino, RELDST)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = dst_ino;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ out.body.entry.attr.uid = 0;
+ })));
+
+ ASSERT_EQ(-1, rename(FULLSRC, FULLDST));
+ ASSERT_EQ(EPERM, errno);
+}
+
+/* Successfully rename a file, overwriting the destination */
+TEST_F(Rename, ok)
+{
+ const char FULLDST[] = "mountpoint/dst";
+ const char RELDST[] = "dst";
+ const char FULLSRC[] = "mountpoint/src";
+ const char RELSRC[] = "src";
+ // The inode of the already-existing destination file
+ uint64_t dst_ino = 2;
+ uint64_t ino = 42;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0777, UINT64_MAX, 1, geteuid());
+ expect_lookup(RELSRC, ino, S_IFREG | 0644, UINT64_MAX);
+ expect_lookup(RELDST, dst_ino, S_IFREG | 0644, UINT64_MAX);
+ expect_rename(0);
+
+ ASSERT_EQ(0, rename(FULLSRC, FULLDST)) << strerror(errno);
+}
+
+TEST_F(Rename, ok_to_remove_src_because_of_stickiness)
+{
+ const char FULLDST[] = "mountpoint/dst";
+ const char RELDST[] = "dst";
+ const char FULLSRC[] = "mountpoint/src";
+ const char RELSRC[] = "src";
+ uint64_t ino = 42;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 01777, UINT64_MAX, 1, 0);
+ expect_lookup(RELSRC, ino, S_IFREG | 0644, UINT64_MAX, geteuid());
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDST)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_rename(0);
+
+ ASSERT_EQ(0, rename(FULLSRC, FULLDST)) << strerror(errno);
+}
+
+// Don't update atime during close after read, if we lack permissions to write
+// that file.
+TEST_F(Read, atime_during_close)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = 100;
+ uint8_t buf[bufsize];
+ const char *CONTENTS = "abcdefgh";
+ ssize_t fsize = sizeof(CONTENTS);
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0755, fsize,
+ 1, UINT64_MAX, 0, 0);
+ expect_open(ino, 0, 1);
+ expect_read(ino, 0, fsize, fsize, CONTENTS);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([&](auto in) {
+ return (in.header.opcode == FUSE_SETATTR);
+ }, Eq(true)),
+ _)
+ ).Times(0);
+ expect_flush(ino, 1, ReturnErrno(0));
+ expect_release(ino, FuseTest::FH);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ /* Ensure atime will be different than during lookup */
+ nap();
+
+ ASSERT_EQ(fsize, read(fd, buf, bufsize)) << strerror(errno);
+
+ close(fd);
+}
+
+TEST_F(Setattr, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ const mode_t oldmode = 0755;
+ const mode_t newmode = 0644;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | oldmode, UINT64_MAX, geteuid());
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino &&
+ in.body.setattr.mode == newmode);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.mode = S_IFREG | newmode;
+ })));
+
+ EXPECT_EQ(0, chmod(FULLPATH, newmode)) << strerror(errno);
+}
+
+TEST_F(Setattr, eacces)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ const mode_t oldmode = 0755;
+ const mode_t newmode = 0644;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | oldmode, UINT64_MAX, 0);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_SETATTR);
+ }, Eq(true)),
+ _)
+ ).Times(0);
+
+ EXPECT_NE(0, chmod(FULLPATH, newmode));
+ EXPECT_EQ(EPERM, errno);
+}
+
+/*
+ * ftruncate() of a file without writable permissions should succeed as long as
+ * the file descriptor is writable. This is important when combined with
+ * O_CREAT
+ */
+TEST_F(Setattr, ftruncate_of_newly_created_file)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ const mode_t mode = 0000;
+ int fd;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0777, UINT64_MAX, 1);
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_create(RELPATH, ino);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino &&
+ (in.body.setattr.valid & FATTR_SIZE));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino;
+ out.body.attr.attr.mode = S_IFREG | mode;
+ out.body.attr.attr_valid = UINT64_MAX;
+ })));
+
+ fd = open(FULLPATH, O_CREAT | O_RDWR, 0);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(0, ftruncate(fd, 100)) << strerror(errno);
+ leak(fd);
+}
+
+/*
+ * Setting the sgid bit should fail for an unprivileged user who doesn't belong
+ * to the file's group
+ */
+TEST_F(Setattr, sgid_by_non_group_member)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ const mode_t oldmode = 0755;
+ const mode_t newmode = 02755;
+ uid_t uid = geteuid();
+ gid_t gid = excluded_group();
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | oldmode, UINT64_MAX, uid, gid);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_SETATTR);
+ }, Eq(true)),
+ _)
+ ).Times(0);
+
+ EXPECT_NE(0, chmod(FULLPATH, newmode));
+ EXPECT_EQ(EPERM, errno);
+}
+
+/* Only the superuser may set the sticky bit on a non-directory */
+TEST_F(Setattr, sticky_regular_file)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ const mode_t oldmode = 0644;
+ const mode_t newmode = 01644;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | oldmode, UINT64_MAX, geteuid());
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_SETATTR);
+ }, Eq(true)),
+ _)
+ ).Times(0);
+
+ EXPECT_NE(0, chmod(FULLPATH, newmode));
+ EXPECT_EQ(EFTYPE, errno);
+}
+
+TEST_F(Setextattr, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ const char value[] = "whatever";
+ ssize_t value_len = strlen(value) + 1;
+ int ns = EXTATTR_NAMESPACE_USER;
+ ssize_t r;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, geteuid());
+ expect_setxattr(0);
+
+ r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value,
+ value_len);
+ ASSERT_EQ(value_len, r) << strerror(errno);
+}
+
+TEST_F(Setextattr, eacces)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ const char value[] = "whatever";
+ ssize_t value_len = strlen(value) + 1;
+ int ns = EXTATTR_NAMESPACE_USER;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, 0);
+
+ ASSERT_EQ(-1, extattr_set_file(FULLPATH, ns, "foo", (const void*)value,
+ value_len));
+ ASSERT_EQ(EACCES, errno);
+}
+
+// Setting system attributes requires superuser privileges
+TEST_F(Setextattr, system)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ const char value[] = "whatever";
+ ssize_t value_len = strlen(value) + 1;
+ int ns = EXTATTR_NAMESPACE_SYSTEM;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | 0666, UINT64_MAX, geteuid());
+
+ ASSERT_EQ(-1, extattr_set_file(FULLPATH, ns, "foo", (const void*)value,
+ value_len));
+ ASSERT_EQ(EPERM, errno);
+}
+
+// Setting user attributes merely requires write privileges
+TEST_F(Setextattr, user)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ const char value[] = "whatever";
+ ssize_t value_len = strlen(value) + 1;
+ int ns = EXTATTR_NAMESPACE_USER;
+ ssize_t r;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | 0666, UINT64_MAX, 0);
+ expect_setxattr(0);
+
+ r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value,
+ value_len);
+ ASSERT_EQ(value_len, r) << strerror(errno);
+}
+
+TEST_F(Unlink, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ sem_t sem;
+
+ ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0777, UINT64_MAX, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, geteuid());
+ expect_unlink(FUSE_ROOT_ID, RELPATH, 0);
+ expect_forget(ino, 1, &sem);
+
+ ASSERT_EQ(0, unlink(FULLPATH)) << strerror(errno);
+
+ sem_wait(&sem);
+ sem_destroy(&sem);
+}
+
+/*
+ * Ensure that a cached name doesn't cause unlink to bypass permission checks
+ * in VOP_LOOKUP.
+ *
+ * This test should pass because lookup(9) purges the namecache entry by doing
+ * a vfs_cache_lookup with ~MAKEENTRY when nameiop == DELETE.
+ */
+TEST_F(Unlink, cached_unwritable_directory)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .Times(AnyNumber())
+ .WillRepeatedly(Invoke(
+ ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = ino;
+ out.body.entry.entry_valid = UINT64_MAX;
+ }))
+ );
+
+ /* Fill name cache */
+ ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
+ /* Despite cached name , unlink should fail */
+ ASSERT_EQ(-1, unlink(FULLPATH));
+ ASSERT_EQ(EACCES, errno);
+}
+
+TEST_F(Unlink, unwritable_directory)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, geteuid());
+
+ ASSERT_EQ(-1, unlink(FULLPATH));
+ ASSERT_EQ(EACCES, errno);
+}
+
+TEST_F(Unlink, sticky_directory)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 01777, UINT64_MAX, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, 0);
+
+ ASSERT_EQ(-1, unlink(FULLPATH));
+ ASSERT_EQ(EPERM, errno);
+}
+
+/* A write by a non-owner should clear a file's SUID bit */
+TEST_F(Write, clear_suid)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ struct stat sb;
+ uint64_t ino = 42;
+ mode_t oldmode = 04777;
+ mode_t newmode = 0777;
+ char wbuf[1] = {'x'};
+ int fd;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | oldmode, UINT64_MAX);
+ expect_open(ino, 0, 1);
+ expect_write(ino, 0, sizeof(wbuf), sizeof(wbuf), 0, 0, wbuf);
+ expect_chmod(ino, newmode, sizeof(wbuf));
+
+ fd = open(FULLPATH, O_WRONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(1, write(fd, wbuf, sizeof(wbuf))) << strerror(errno);
+ ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
+ EXPECT_EQ(S_IFREG | newmode, sb.st_mode);
+ leak(fd);
+}
+
+/* A write by a non-owner should clear a file's SGID bit */
+TEST_F(Write, clear_sgid)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ struct stat sb;
+ uint64_t ino = 42;
+ mode_t oldmode = 02777;
+ mode_t newmode = 0777;
+ char wbuf[1] = {'x'};
+ int fd;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | oldmode, UINT64_MAX);
+ expect_open(ino, 0, 1);
+ expect_write(ino, 0, sizeof(wbuf), sizeof(wbuf), 0, 0, wbuf);
+ expect_chmod(ino, newmode, sizeof(wbuf));
+
+ fd = open(FULLPATH, O_WRONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(1, write(fd, wbuf, sizeof(wbuf))) << strerror(errno);
+ ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
+ EXPECT_EQ(S_IFREG | newmode, sb.st_mode);
+ leak(fd);
+}
+
+/* Regression test for a specific recurse-of-nonrecursive-lock panic
+ *
+ * With writeback caching, we can't call vtruncbuf from fuse_io_strategy, or it
+ * may panic. That happens if the FUSE_SETATTR response indicates that the
+ * file's size has changed since the write.
+ */
+TEST_F(Write, recursion_panic_while_clearing_suid)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ mode_t oldmode = 04777;
+ mode_t newmode = 0777;
+ char wbuf[1] = {'x'};
+ int fd;
+
+ expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755, UINT64_MAX, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | oldmode, UINT64_MAX);
+ expect_open(ino, 0, 1);
+ expect_write(ino, 0, sizeof(wbuf), sizeof(wbuf), 0, 0, wbuf);
+ /* XXX Return a smaller file size than what we just wrote! */
+ expect_chmod(ino, newmode, 0);
+
+ fd = open(FULLPATH, O_WRONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(1, write(fd, wbuf, sizeof(wbuf))) << strerror(errno);
+ leak(fd);
+}
diff --git a/tests/sys/fs/fusefs/default_permissions_privileged.cc b/tests/sys/fs/fusefs/default_permissions_privileged.cc
new file mode 100644
index 000000000000..43f2141c1984
--- /dev/null
+++ b/tests/sys/fs/fusefs/default_permissions_privileged.cc
@@ -0,0 +1,124 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Tests for the "default_permissions" mount option that require a privileged
+ * user.
+ */
+
+extern "C" {
+#include <sys/types.h>
+#include <sys/extattr.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class DefaultPermissionsPrivileged: public FuseTest {
+virtual void SetUp() {
+ m_default_permissions = true;
+ FuseTest::SetUp();
+ if (HasFatalFailure() || IsSkipped())
+ return;
+
+ if (geteuid() != 0) {
+ GTEST_SKIP() << "This test requires a privileged user";
+ }
+
+ /* With -o default_permissions, FUSE_ACCESS should never be called */
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_ACCESS);
+ }, Eq(true)),
+ _)
+ ).Times(0);
+}
+
+public:
+void expect_getattr(uint64_t ino, mode_t mode, uint64_t attr_valid, int times,
+ uid_t uid = 0, gid_t gid = 0)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).Times(times)
+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino; // Must match nodeid
+ out.body.attr.attr.mode = mode;
+ out.body.attr.attr.size = 0;
+ out.body.attr.attr.uid = uid;
+ out.body.attr.attr.gid = gid;
+ out.body.attr.attr_valid = attr_valid;
+ })));
+}
+
+void expect_lookup(const char *relpath, uint64_t ino, mode_t mode,
+ uint64_t attr_valid, uid_t uid = 0, gid_t gid = 0)
+{
+ FuseTest::expect_lookup(relpath, ino, mode, 0, 1, attr_valid, uid, gid);
+}
+
+};
+
+class Setattr: public DefaultPermissionsPrivileged {};
+
+TEST_F(Setattr, sticky_regular_file)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ const mode_t oldmode = 0644;
+ const mode_t newmode = 01644;
+
+ expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1);
+ expect_lookup(RELPATH, ino, S_IFREG | oldmode, UINT64_MAX, geteuid());
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_SETATTR);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.mode = S_IFREG | newmode;
+ })));
+
+ EXPECT_EQ(0, chmod(FULLPATH, newmode)) << strerror(errno);
+}
+
+
diff --git a/tests/sys/fs/fusefs/destroy.cc b/tests/sys/fs/fusefs/destroy.cc
new file mode 100644
index 000000000000..45acb1f99724
--- /dev/null
+++ b/tests/sys/fs/fusefs/destroy.cc
@@ -0,0 +1,157 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <fcntl.h>
+#include <pthread.h>
+#include <semaphore.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+/* Tests for orderly unmounts */
+class Destroy: public FuseTest {};
+
+/* Tests for unexpected deaths of the server */
+class Death: public FuseTest{};
+
+static void* open_th(void* arg) {
+ int fd;
+ const char *path = (const char*)arg;
+
+ fd = open(path, O_RDONLY);
+ EXPECT_EQ(-1, fd);
+ EXPECT_EQ(ENOTCONN, errno);
+ return 0;
+}
+
+/*
+ * The server dies with unsent operations still on the message queue.
+ * Check for any memory leaks like this:
+ * 1) kldunload fusefs, if necessary
+ * 2) kldload fusefs
+ * 3) ./destroy --gtest_filter=Death.unsent_operations
+ * 4) kldunload fusefs
+ * 5) check /var/log/messages for anything like this:
+Freed UMA keg (fuse_ticket) was not empty (31 items). Lost 2 pages of memory.
+Warning: memory type fuse_msgbuf leaked memory on destroy (68 allocations, 428800 bytes leaked).
+ */
+TEST_F(Death, unsent_operations)
+{
+ const char FULLPATH0[] = "mountpoint/some_file.txt";
+ const char FULLPATH1[] = "mountpoint/other_file.txt";
+ const char RELPATH0[] = "some_file.txt";
+ const char RELPATH1[] = "other_file.txt";
+ pthread_t th0, th1;
+ ino_t ino0 = 42, ino1 = 43;
+ sem_t sem;
+ mode_t mode = S_IFREG | 0644;
+
+ sem_init(&sem, 0, 0);
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH0)
+ .WillRepeatedly(Invoke(
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino0;
+ out.body.entry.attr.nlink = 1;
+ })));
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH1)
+ .WillRepeatedly(Invoke(
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino1;
+ out.body.entry.attr.nlink = 1;
+ })));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([&](auto in) {
+ return (in.header.opcode == FUSE_OPEN);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke([&](auto in __unused, auto &out __unused) {
+ sem_post(&sem);
+ pause();
+ }));
+
+ /*
+ * One thread's operation will be sent to the daemon and block, and the
+ * other's will be stuck in the message queue.
+ */
+ ASSERT_EQ(0, pthread_create(&th0, NULL, open_th,
+ __DECONST(void*, FULLPATH0))) << strerror(errno);
+ ASSERT_EQ(0, pthread_create(&th1, NULL, open_th,
+ __DECONST(void*, FULLPATH1))) << strerror(errno);
+
+ /* Wait for the first thread to block */
+ sem_wait(&sem);
+ /* Give the second thread time to block */
+ nap();
+
+ m_mock->kill_daemon();
+
+ pthread_join(th0, NULL);
+ pthread_join(th1, NULL);
+
+ sem_destroy(&sem);
+}
+
+/*
+ * On unmount the kernel should send a FUSE_DESTROY operation. It should also
+ * send FUSE_FORGET operations for all inodes with lookup_count > 0.
+ */
+TEST_F(Destroy, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2);
+ expect_forget(ino, 2);
+ expect_destroy(0);
+
+ /*
+ * access(2) the file to force a lookup. Access it twice to double its
+ * lookup count.
+ */
+ ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
+ ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
+
+ /*
+ * Unmount, triggering a FUSE_DESTROY and also causing a VOP_RECLAIM
+ * for every vnode on this mp, triggering FUSE_FORGET for each of them.
+ */
+ m_mock->unmount();
+}
diff --git a/tests/sys/fs/fusefs/dev_fuse_poll.cc b/tests/sys/fs/fusefs/dev_fuse_poll.cc
new file mode 100644
index 000000000000..181cd69de665
--- /dev/null
+++ b/tests/sys/fs/fusefs/dev_fuse_poll.cc
@@ -0,0 +1,229 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This file tests different polling methods for the /dev/fuse device
+ */
+
+extern "C" {
+#include <fcntl.h>
+#include <semaphore.h>
+#include <unistd.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+const char FULLPATH[] = "mountpoint/some_file.txt";
+const char RELPATH[] = "some_file.txt";
+const uint64_t ino = 42;
+const mode_t access_mode = R_OK;
+
+/*
+ * Translate a poll method's string representation to the enum value.
+ * Using strings with ::testing::Values gives better output with
+ * --gtest_list_tests
+ */
+enum poll_method poll_method_from_string(const char *s)
+{
+ if (0 == strcmp("BLOCKING", s))
+ return BLOCKING;
+ else if (0 == strcmp("KQ", s))
+ return KQ;
+ else if (0 == strcmp("POLL", s))
+ return POLL;
+ else
+ return SELECT;
+}
+
+class DevFusePoll: public FuseTest, public WithParamInterface<const char *> {
+ virtual void SetUp() {
+ m_pm = poll_method_from_string(GetParam());
+ FuseTest::SetUp();
+ }
+};
+
+class Kqueue: public FuseTest {
+ virtual void SetUp() {
+ m_pm = KQ;
+ FuseTest::SetUp();
+ }
+};
+
+TEST_P(DevFusePoll, access)
+{
+ expect_access(FUSE_ROOT_ID, X_OK, 0);
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ expect_access(ino, access_mode, 0);
+
+ ASSERT_EQ(0, access(FULLPATH, access_mode)) << strerror(errno);
+}
+
+/* Ensure that we wake up pollers during unmount */
+TEST_P(DevFusePoll, destroy)
+{
+ expect_destroy(0);
+
+ m_mock->unmount();
+}
+
+INSTANTIATE_TEST_SUITE_P(PM, DevFusePoll,
+ ::testing::Values("BLOCKING", "KQ", "POLL", "SELECT"));
+
+static void* statter(void* arg) {
+ const char *name;
+ struct stat sb;
+
+ name = (const char*)arg;
+ return ((void*)(intptr_t)stat(name, &sb));
+}
+
+/*
+ * A kevent's data field should contain the number of operations available to
+ * be immediately read.
+ */
+TEST_F(Kqueue, data)
+{
+ pthread_t th0, th1, th2;
+ sem_t sem0, sem1;
+ int nready0, nready1, nready2;
+ uint64_t foo_ino = 42;
+ uint64_t bar_ino = 43;
+ uint64_t baz_ino = 44;
+ Sequence seq;
+ void *th_ret;
+
+ ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);
+ ASSERT_EQ(0, sem_init(&sem1, 0, 0)) << strerror(errno);
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, "foo")
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.entry_valid = UINT64_MAX;
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = foo_ino;
+ })));
+ EXPECT_LOOKUP(FUSE_ROOT_ID, "bar")
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.entry_valid = UINT64_MAX;
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = bar_ino;
+ })));
+ EXPECT_LOOKUP(FUSE_ROOT_ID, "baz")
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.entry_valid = UINT64_MAX;
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = baz_ino;
+ })));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == foo_ino);
+ }, Eq(true)),
+ _)
+ )
+ .WillOnce(Invoke(ReturnImmediate([&](auto in, auto& out) {
+ nready0 = m_mock->m_nready;
+
+ sem_post(&sem0);
+ // Block the daemon so we can accumulate a few more ops
+ sem_wait(&sem1);
+
+ out.header.unique = in.header.unique;
+ out.header.error = -EIO;
+ out.header.len = sizeof(out.header);
+ })));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ (in.header.nodeid == bar_ino ||
+ in.header.nodeid == baz_ino));
+ }, Eq(true)),
+ _)
+ ).InSequence(seq)
+ .WillOnce(Invoke(ReturnImmediate([&](auto in, auto& out) {
+ nready1 = m_mock->m_nready;
+ out.header.unique = in.header.unique;
+ out.header.error = -EIO;
+ out.header.len = sizeof(out.header);
+ })));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ (in.header.nodeid == bar_ino ||
+ in.header.nodeid == baz_ino));
+ }, Eq(true)),
+ _)
+ ).InSequence(seq)
+ .WillOnce(Invoke(ReturnImmediate([&](auto in, auto& out) {
+ nready2 = m_mock->m_nready;
+ out.header.unique = in.header.unique;
+ out.header.error = -EIO;
+ out.header.len = sizeof(out.header);
+ })));
+
+ /*
+ * Create cached lookup entries for these files. It seems that only
+ * one thread at a time can be in VOP_LOOKUP for a given directory
+ */
+ access("mountpoint/foo", F_OK);
+ access("mountpoint/bar", F_OK);
+ access("mountpoint/baz", F_OK);
+ ASSERT_EQ(0, pthread_create(&th0, NULL, statter,
+ __DECONST(void*, "mountpoint/foo"))) << strerror(errno);
+ EXPECT_EQ(0, sem_wait(&sem0)) << strerror(errno);
+ ASSERT_EQ(0, pthread_create(&th1, NULL, statter,
+ __DECONST(void*, "mountpoint/bar"))) << strerror(errno);
+ ASSERT_EQ(0, pthread_create(&th2, NULL, statter,
+ __DECONST(void*, "mountpoint/baz"))) << strerror(errno);
+
+ nap(); // Allow th1 and th2 to send their ops to the daemon
+ EXPECT_EQ(0, sem_post(&sem1)) << strerror(errno);
+
+ pthread_join(th0, &th_ret);
+ ASSERT_EQ(-1, (intptr_t)th_ret);
+ pthread_join(th1, &th_ret);
+ ASSERT_EQ(-1, (intptr_t)th_ret);
+ pthread_join(th2, &th_ret);
+ ASSERT_EQ(-1, (intptr_t)th_ret);
+
+ EXPECT_EQ(1, nready0);
+ EXPECT_EQ(2, nready1);
+ EXPECT_EQ(1, nready2);
+
+ sem_destroy(&sem0);
+ sem_destroy(&sem1);
+}
diff --git a/tests/sys/fs/fusefs/fallocate.cc b/tests/sys/fs/fusefs/fallocate.cc
new file mode 100644
index 000000000000..4e5b047b78b7
--- /dev/null
+++ b/tests/sys/fs/fusefs/fallocate.cc
@@ -0,0 +1,779 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2021 Alan Somers
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+
+#include <fcntl.h>
+#include <mntopts.h> // for build_iovec
+#include <signal.h>
+#include <unistd.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+/* Is buf all zero? */
+static bool
+is_zero(const char *buf, uint64_t size)
+{
+ return buf[0] == 0 && !memcmp(buf, buf + 1, size - 1);
+}
+
+class Fallocate: public FuseTest {
+public:
+/*
+ * expect VOP_DEALLOCATE to be implemented by vop_stddeallocate.
+ */
+void expect_vop_stddeallocate(uint64_t ino, uint64_t off, uint64_t length)
+{
+ /* XXX read offset and size may depend on cache mode */
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READ &&
+ in.header.nodeid == ino &&
+ in.body.read.offset <= off &&
+ in.body.read.offset + in.body.read.size >=
+ off + length);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in, auto& out) {
+ assert(in.body.read.size <= sizeof(out.body.bytes));
+ out.header.len = sizeof(struct fuse_out_header) +
+ in.body.read.size;
+ memset(out.body.bytes, 'X', in.body.read.size);
+ }))).RetiresOnSaturation();
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *buf = (const char*)in.body.bytes +
+ sizeof(struct fuse_write_in);
+
+ assert(length <= sizeof(in.body.bytes) -
+ sizeof(struct fuse_write_in));
+ return (in.header.opcode == FUSE_WRITE &&
+ in.header.nodeid == ino &&
+ in.body.write.offset == off &&
+ in.body.write.size == length &&
+ is_zero(buf, length));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, write);
+ out.body.write.size = length;
+ })));
+}
+};
+
+class Fspacectl: public Fallocate {};
+
+class Fspacectl_7_18: public Fspacectl {
+public:
+virtual void SetUp() {
+ m_kernel_minor_version = 18;
+ Fspacectl::SetUp();
+}
+};
+
+class FspacectlCache: public Fspacectl, public WithParamInterface<cache_mode> {
+public:
+bool m_direct_io;
+
+FspacectlCache(): m_direct_io(false) {};
+
+virtual void SetUp() {
+ int cache_mode = GetParam();
+ switch (cache_mode) {
+ case Uncached:
+ m_direct_io = true;
+ break;
+ case WritebackAsync:
+ m_async = true;
+ /* FALLTHROUGH */
+ case Writeback:
+ m_init_flags |= FUSE_WRITEBACK_CACHE;
+ /* FALLTHROUGH */
+ case Writethrough:
+ break;
+ default:
+ FAIL() << "Unknown cache mode";
+ }
+
+ FuseTest::SetUp();
+ if (IsSkipped())
+ return;
+}
+};
+
+class PosixFallocate: public Fallocate {
+public:
+static sig_atomic_t s_sigxfsz;
+
+void SetUp() {
+ s_sigxfsz = 0;
+ FuseTest::SetUp();
+}
+
+void TearDown() {
+ struct sigaction sa;
+
+ bzero(&sa, sizeof(sa));
+ sa.sa_handler = SIG_DFL;
+ sigaction(SIGXFSZ, &sa, NULL);
+
+ Fallocate::TearDown();
+}
+
+};
+
+sig_atomic_t PosixFallocate::s_sigxfsz = 0;
+
+void sigxfsz_handler(int __unused sig) {
+ PosixFallocate::s_sigxfsz = 1;
+}
+
+class PosixFallocate_7_18: public PosixFallocate {
+public:
+virtual void SetUp() {
+ m_kernel_minor_version = 18;
+ PosixFallocate::SetUp();
+}
+};
+
+
+/*
+ * If the server returns ENOSYS, it indicates that the server does not support
+ * FUSE_FALLOCATE. This and future calls should fall back to vop_stddeallocate.
+ */
+TEST_F(Fspacectl, enosys)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ off_t fsize = 1 << 20;
+ off_t off0 = 100;
+ off_t len0 = 500;
+ struct spacectl_range rqsr = { .r_offset = off0, .r_len = len0 };
+ uint64_t ino = 42;
+ uint64_t off1 = fsize;
+ uint64_t len1 = 1000;
+ off_t off2 = fsize / 2;
+ off_t len2 = 500;
+ int fd;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
+ expect_open(ino, 0, 1);
+ expect_fallocate(ino, off0, len0,
+ FUSE_FALLOC_FL_KEEP_SIZE | FUSE_FALLOC_FL_PUNCH_HOLE, ENOSYS);
+ expect_vop_stddeallocate(ino, off0, len0);
+ expect_vop_stddeallocate(ino, off2, len2);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, NULL));
+
+ /* Subsequent calls shouldn't query the daemon either */
+ rqsr.r_offset = off2;
+ rqsr.r_len = len2;
+ EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, NULL));
+
+ /* Neither should posix_fallocate query the daemon */
+ EXPECT_EQ(EINVAL, posix_fallocate(fd, off1, len1));
+
+ leak(fd);
+}
+
+/*
+ * EOPNOTSUPP means "the file system does not support fallocate with the
+ * supplied mode on this particular file". So we should fallback, but not
+ * assume anything about whether the operation will fail on a different file or
+ * with a different mode.
+ */
+TEST_F(Fspacectl, eopnotsupp)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ struct spacectl_range rqsr;
+ uint64_t ino = 42;
+ uint64_t fsize = 1 << 20;
+ uint64_t off0 = 500;
+ uint64_t len = 1000;
+ uint64_t off1 = fsize / 2;
+ int fd;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
+ expect_open(ino, 0, 1);
+ expect_fallocate(ino, off0, len,
+ FUSE_FALLOC_FL_KEEP_SIZE | FUSE_FALLOC_FL_PUNCH_HOLE,
+ EOPNOTSUPP);
+ expect_vop_stddeallocate(ino, off0, len);
+ expect_fallocate(ino, off1, len,
+ FUSE_FALLOC_FL_KEEP_SIZE | FUSE_FALLOC_FL_PUNCH_HOLE,
+ EOPNOTSUPP);
+ expect_vop_stddeallocate(ino, off1, len);
+ expect_fallocate(ino, fsize, len, 0, 0);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ /*
+ * Though the FUSE daemon will reject the call, the kernel should fall
+ * back to a read-modify-write approach.
+ */
+ rqsr.r_offset = off0;
+ rqsr.r_len = len;
+ EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, NULL));
+
+ /* Subsequent calls should still query the daemon */
+ rqsr.r_offset = off1;
+ rqsr.r_len = len;
+ EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, NULL));
+
+ /* But subsequent posix_fallocate calls _should_ query the daemon */
+ EXPECT_EQ(0, posix_fallocate(fd, fsize, len));
+
+ leak(fd);
+}
+
+TEST_F(Fspacectl, erofs)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ struct statfs statbuf;
+ uint64_t fsize = 2000;
+ struct spacectl_range rqsr = { .r_offset = 0, .r_len = 1 };
+ struct iovec *iov = NULL;
+ int iovlen = 0;
+ uint64_t ino = 42;
+ int fd;
+ int newflags;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_STATFS);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out)
+ {
+ /*
+ * All of the fields except f_flags are don't care, and f_flags
+ * is set by the VFS
+ */
+ SET_OUT_HEADER_LEN(out, statfs);
+ })));
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ /* Remount read-only */
+ ASSERT_EQ(0, statfs("mountpoint", &statbuf)) << strerror(errno);
+ newflags = statbuf.f_flags | MNT_UPDATE | MNT_RDONLY;
+ build_iovec(&iov, &iovlen, "fstype", (void*)statbuf.f_fstypename, -1);
+ build_iovec(&iov, &iovlen, "fspath", (void*)statbuf.f_mntonname, -1);
+ build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
+ ASSERT_EQ(0, nmount(iov, iovlen, newflags)) << strerror(errno);
+ free_iovec(&iov, &iovlen);
+
+ EXPECT_EQ(-1, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, NULL));
+ EXPECT_EQ(EROFS, errno);
+
+ leak(fd);
+}
+
+/*
+ * If FUSE_GETATTR fails when determining the size of the file, fspacectl
+ * should fail gracefully. This failure mode is easiest to trigger when
+ * attribute caching is disabled.
+ */
+TEST_F(Fspacectl, getattr_fails)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ Sequence seq;
+ struct spacectl_range rqsr;
+ const uint64_t ino = 42;
+ const uint64_t fsize = 2000;
+ int fd;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1, 0);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).Times(1)
+ .InSequence(seq)
+ .WillOnce(Invoke(ReturnImmediate([](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino;
+ out.body.attr.attr.mode = S_IFREG | 0644;
+ out.body.attr.attr.size = fsize;
+ out.body.attr.attr_valid = 0;
+ })));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).InSequence(seq)
+ .WillOnce(ReturnErrno(EIO));
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ rqsr.r_offset = 500;
+ rqsr.r_len = 1000;
+ EXPECT_EQ(-1, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, NULL));
+ EXPECT_EQ(EIO, errno);
+
+ leak(fd);
+}
+
+TEST_F(Fspacectl, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ struct spacectl_range rqsr, rmsr;
+ struct stat sb0, sb1;
+ uint64_t ino = 42;
+ uint64_t fsize = 2000;
+ uint64_t offset = 500;
+ uint64_t length = 1000;
+ int fd;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
+ expect_open(ino, 0, 1);
+ expect_fallocate(ino, offset, length,
+ FUSE_FALLOC_FL_KEEP_SIZE | FUSE_FALLOC_FL_PUNCH_HOLE, 0);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(0, fstat(fd, &sb0)) << strerror(errno);
+ rqsr.r_offset = offset;
+ rqsr.r_len = length;
+ EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, &rmsr));
+ EXPECT_EQ(0, rmsr.r_len);
+ EXPECT_EQ((off_t)(offset + length), rmsr.r_offset);
+
+ /*
+ * The file's attributes should not have been invalidated, so this fstat
+ * will not requery the daemon.
+ */
+ EXPECT_EQ(0, fstat(fd, &sb1));
+ EXPECT_EQ(fsize, (uint64_t)sb1.st_size);
+
+ /* mtime and ctime should be updated */
+ EXPECT_EQ(sb0.st_atime, sb1.st_atime);
+ EXPECT_NE(sb0.st_mtime, sb1.st_mtime);
+ EXPECT_NE(sb0.st_ctime, sb1.st_ctime);
+
+ leak(fd);
+}
+
+/* The returned rqsr.r_off should be clipped at EoF */
+TEST_F(Fspacectl, past_eof)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ struct spacectl_range rqsr, rmsr;
+ uint64_t ino = 42;
+ uint64_t fsize = 1000;
+ uint64_t offset = 1500;
+ uint64_t length = 1000;
+ int fd;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
+ expect_open(ino, 0, 1);
+ expect_fallocate(ino, offset, length,
+ FUSE_FALLOC_FL_KEEP_SIZE | FUSE_FALLOC_FL_PUNCH_HOLE, 0);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ rqsr.r_offset = offset;
+ rqsr.r_len = length;
+ EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, &rmsr));
+ EXPECT_EQ(0, rmsr.r_len);
+ EXPECT_EQ((off_t)fsize, rmsr.r_offset);
+
+ leak(fd);
+}
+
+/* The returned rqsr.r_off should be clipped at EoF */
+TEST_F(Fspacectl, spans_eof)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ struct spacectl_range rqsr, rmsr;
+ uint64_t ino = 42;
+ uint64_t fsize = 1000;
+ uint64_t offset = 500;
+ uint64_t length = 1000;
+ int fd;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
+ expect_open(ino, 0, 1);
+ expect_fallocate(ino, offset, length,
+ FUSE_FALLOC_FL_KEEP_SIZE | FUSE_FALLOC_FL_PUNCH_HOLE, 0);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ rqsr.r_offset = offset;
+ rqsr.r_len = length;
+ EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, &rmsr));
+ EXPECT_EQ(0, rmsr.r_len);
+ EXPECT_EQ((off_t)fsize, rmsr.r_offset);
+
+ leak(fd);
+}
+
+/*
+ * With older servers, no FUSE_FALLOCATE should be attempted. The kernel
+ * should fall back to vop_stddeallocate.
+ */
+TEST_F(Fspacectl_7_18, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ struct spacectl_range rqsr, rmsr;
+ char *buf;
+ uint64_t ino = 42;
+ uint64_t fsize = 2000;
+ uint64_t offset = 500;
+ uint64_t length = 1000;
+ int fd;
+
+ buf = new char[length];
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
+ expect_open(ino, 0, 1);
+ expect_vop_stddeallocate(ino, offset, length);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ rqsr.r_offset = offset;
+ rqsr.r_len = length;
+ EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, &rmsr));
+ EXPECT_EQ(0, rmsr.r_len);
+ EXPECT_EQ((off_t)(offset + length), rmsr.r_offset);
+
+ leak(fd);
+ delete[] buf;
+}
+
+/*
+ * A successful fspacectl should clear the zeroed data from the kernel cache.
+ */
+TEST_P(FspacectlCache, clears_cache)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefghijklmnopqrstuvwxyz";
+ struct spacectl_range rqsr, rmsr;
+ uint64_t ino = 42;
+ ssize_t bufsize = strlen(CONTENTS);
+ uint64_t fsize = bufsize;
+ uint8_t buf[bufsize];
+ char zbuf[bufsize];
+ uint64_t offset = 0;
+ uint64_t length = bufsize;
+ int fd;
+
+ bzero(zbuf, bufsize);
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
+ expect_open(ino, 0, 1);
+ /* NB: expectations are applied in LIFO order */
+ expect_read(ino, 0, fsize, fsize, zbuf);
+ expect_read(ino, 0, fsize, fsize, CONTENTS);
+ expect_fallocate(ino, offset, length,
+ FUSE_FALLOC_FL_KEEP_SIZE | FUSE_FALLOC_FL_PUNCH_HOLE, 0);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ /* Populate the cache */
+ ASSERT_EQ(fsize, (uint64_t)pread(fd, buf, bufsize, 0))
+ << strerror(errno);
+ ASSERT_EQ(0, memcmp(buf, CONTENTS, fsize));
+
+ /* Zero the file */
+ rqsr.r_offset = offset;
+ rqsr.r_len = length;
+ EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, &rmsr));
+ EXPECT_EQ(0, rmsr.r_len);
+ EXPECT_EQ((off_t)(offset + length), rmsr.r_offset);
+
+ /* Read again. This should query the daemon */
+ ASSERT_EQ(fsize, (uint64_t)pread(fd, buf, bufsize, 0))
+ << strerror(errno);
+ ASSERT_EQ(0, memcmp(buf, zbuf, fsize));
+
+ leak(fd);
+}
+
+INSTANTIATE_TEST_SUITE_P(FspacectlCache, FspacectlCache,
+ Values(Uncached, Writethrough, Writeback)
+);
+
+/*
+ * If the server returns ENOSYS, it indicates that the server does not support
+ * FUSE_FALLOCATE. This and future calls should return EINVAL.
+ */
+TEST_F(PosixFallocate, enosys)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ uint64_t off0 = 0;
+ uint64_t len0 = 1000;
+ off_t off1 = 100;
+ off_t len1 = 200;
+ uint64_t fsize = 500;
+ struct spacectl_range rqsr = { .r_offset = off1, .r_len = len1 };
+ int fd;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
+ expect_open(ino, 0, 1);
+ expect_fallocate(ino, off0, len0, 0, ENOSYS);
+ expect_vop_stddeallocate(ino, off1, len1);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ EXPECT_EQ(EINVAL, posix_fallocate(fd, off0, len0));
+
+ /* Subsequent calls shouldn't query the daemon*/
+ EXPECT_EQ(EINVAL, posix_fallocate(fd, off0, len0));
+
+ /* Neither should VOP_DEALLOCATE query the daemon */
+ EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, NULL));
+
+ leak(fd);
+}
+
+/*
+ * EOPNOTSUPP means "the file system does not support fallocate with the
+ * supplied mode on this particular file". So we should fallback, but not
+ * assume anything about whether the operation will fail on a different file or
+ * with a different mode.
+ */
+TEST_F(PosixFallocate, eopnotsupp)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ struct spacectl_range rqsr;
+ uint64_t ino = 42;
+ uint64_t fsize = 2000;
+ uint64_t offset = 0;
+ uint64_t length = 1000;
+ int fd;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
+ expect_open(ino, 0, 1);
+ expect_fallocate(ino, fsize, length, 0, EOPNOTSUPP);
+ expect_fallocate(ino, offset, length, 0, EOPNOTSUPP);
+ expect_fallocate(ino, offset, length,
+ FUSE_FALLOC_FL_KEEP_SIZE | FUSE_FALLOC_FL_PUNCH_HOLE, 0);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ EXPECT_EQ(EINVAL, posix_fallocate(fd, fsize, length));
+
+ /* Subsequent calls should still query the daemon*/
+ EXPECT_EQ(EINVAL, posix_fallocate(fd, offset, length));
+
+ /* And subsequent VOP_DEALLOCATE calls should also query the daemon */
+ rqsr.r_len = length;
+ rqsr.r_offset = offset;
+ EXPECT_EQ(0, fspacectl(fd, SPACECTL_DEALLOC, &rqsr, 0, NULL));
+
+ leak(fd);
+}
+
+/* EIO is not a permanent error, and may be retried */
+TEST_F(PosixFallocate, eio)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ uint64_t offset = 0;
+ uint64_t length = 1000;
+ int fd;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ expect_open(ino, 0, 1);
+ expect_fallocate(ino, offset, length, 0, EIO);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ EXPECT_EQ(EIO, posix_fallocate(fd, offset, length));
+
+ expect_fallocate(ino, offset, length, 0, 0);
+
+ EXPECT_EQ(0, posix_fallocate(fd, offset, length));
+
+ leak(fd);
+}
+
+TEST_F(PosixFallocate, erofs)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ struct statfs statbuf;
+ struct iovec *iov = NULL;
+ int iovlen = 0;
+ uint64_t ino = 42;
+ uint64_t offset = 0;
+ uint64_t length = 1000;
+ int fd;
+ int newflags;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_STATFS);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out)
+ {
+ /*
+ * All of the fields except f_flags are don't care, and f_flags
+ * is set by the VFS
+ */
+ SET_OUT_HEADER_LEN(out, statfs);
+ })));
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ /* Remount read-only */
+ ASSERT_EQ(0, statfs("mountpoint", &statbuf)) << strerror(errno);
+ newflags = statbuf.f_flags | MNT_UPDATE | MNT_RDONLY;
+ build_iovec(&iov, &iovlen, "fstype", (void*)statbuf.f_fstypename, -1);
+ build_iovec(&iov, &iovlen, "fspath", (void*)statbuf.f_mntonname, -1);
+ build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
+ ASSERT_EQ(0, nmount(iov, iovlen, newflags)) << strerror(errno);
+ free_iovec(&iov, &iovlen);
+
+ EXPECT_EQ(EROFS, posix_fallocate(fd, offset, length));
+
+ leak(fd);
+}
+
+TEST_F(PosixFallocate, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ struct stat sb0, sb1;
+ uint64_t ino = 42;
+ uint64_t offset = 0;
+ uint64_t length = 1000;
+ int fd;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = ino;
+ out.body.entry.entry_valid = UINT64_MAX;
+ out.body.entry.attr_valid = UINT64_MAX;
+ })));
+ expect_open(ino, 0, 1);
+ expect_fallocate(ino, offset, length, 0, 0);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(0, fstat(fd, &sb0)) << strerror(errno);
+ EXPECT_EQ(0, posix_fallocate(fd, offset, length));
+ /*
+ * Despite the originally cached file size of zero, stat should now
+ * return either the new size or requery the daemon.
+ */
+ EXPECT_EQ(0, stat(FULLPATH, &sb1));
+ EXPECT_EQ(length, (uint64_t)sb1.st_size);
+
+ /* mtime and ctime should be updated */
+ EXPECT_EQ(sb0.st_atime, sb1.st_atime);
+ EXPECT_NE(sb0.st_mtime, sb1.st_mtime);
+ EXPECT_NE(sb0.st_ctime, sb1.st_ctime);
+
+ leak(fd);
+}
+
+/* fusefs should respect RLIMIT_FSIZE */
+TEST_F(PosixFallocate, rlimit_fsize)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ struct rlimit rl;
+ uint64_t ino = 42;
+ uint64_t offset = 0;
+ uint64_t length = 1'000'000;
+ int fd;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ expect_open(ino, 0, 1);
+
+ rl.rlim_cur = length / 2;
+ rl.rlim_max = 10 * length;
+ ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno);
+ ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ EXPECT_EQ(EFBIG, posix_fallocate(fd, offset, length));
+ EXPECT_EQ(1, s_sigxfsz);
+
+ leak(fd);
+}
+
+/* With older servers, no FUSE_FALLOCATE should be attempted */
+TEST_F(PosixFallocate_7_18, einval)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ uint64_t offset = 0;
+ uint64_t length = 1000;
+ int fd;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ expect_open(ino, 0, 1);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ EXPECT_EQ(EINVAL, posix_fallocate(fd, offset, length));
+
+ leak(fd);
+}
diff --git a/tests/sys/fs/fusefs/fifo.cc b/tests/sys/fs/fusefs/fifo.cc
new file mode 100644
index 000000000000..3ec21f3f779b
--- /dev/null
+++ b/tests/sys/fs/fusefs/fifo.cc
@@ -0,0 +1,211 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <fcntl.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+const char FULLPATH[] = "mountpoint/some_fifo";
+const char RELPATH[] = "some_fifo";
+const char MESSAGE[] = "Hello, World!\n";
+const int msgsize = sizeof(MESSAGE);
+
+class Fifo: public FuseTest {
+public:
+pthread_t m_child;
+
+Fifo(): m_child(NULL) {};
+
+void TearDown() {
+ if (m_child != NULL) {
+ pthread_join(m_child, NULL);
+ }
+ FuseTest::TearDown();
+}
+};
+
+class Socket: public Fifo {};
+
+/* Writer thread */
+static void* writer(void* arg) {
+ ssize_t sent = 0;
+ int fd;
+
+ fd = *(int*)arg;
+ while (sent < msgsize) {
+ ssize_t r;
+
+ r = write(fd, MESSAGE + sent, msgsize - sent);
+ if (r < 0)
+ return (void*)(intptr_t)errno;
+ else
+ sent += r;
+
+ }
+ return 0;
+}
+
+/*
+ * Reading and writing FIFOs works. None of the I/O actually goes through FUSE
+ */
+TEST_F(Fifo, read_write)
+{
+ mode_t mode = S_IFIFO | 0755;
+ const int bufsize = 80;
+ char message[bufsize];
+ ssize_t recvd = 0, r;
+ uint64_t ino = 42;
+ int fd;
+
+ expect_lookup(RELPATH, ino, mode, 0, 1);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(0, pthread_create(&m_child, NULL, writer, &fd))
+ << strerror(errno);
+ while (recvd < msgsize) {
+ r = read(fd, message + recvd, bufsize - recvd);
+ ASSERT_LE(0, r) << strerror(errno);
+ ASSERT_LT(0, r) << "unexpected EOF";
+ recvd += r;
+ }
+ ASSERT_STREQ(message, MESSAGE);
+
+ leak(fd);
+}
+
+/* Writer thread */
+static void* socket_writer(void* arg __unused) {
+ ssize_t sent = 0;
+ int fd, err;
+ struct sockaddr_un sa;
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("socket");
+ return (void*)(intptr_t)errno;
+ }
+ sa.sun_family = AF_UNIX;
+ strlcpy(sa.sun_path, FULLPATH, sizeof(sa.sun_path));
+ sa.sun_len = sizeof(FULLPATH);
+ err = connect(fd, (struct sockaddr*)&sa, sizeof(sa));
+ if (err < 0) {
+ perror("connect");
+ return (void*)(intptr_t)errno;
+ }
+
+ while (sent < msgsize) {
+ ssize_t r;
+
+ r = write(fd, MESSAGE + sent, msgsize - sent);
+ if (r < 0)
+ return (void*)(intptr_t)errno;
+ else
+ sent += r;
+
+ }
+
+ FuseTest::leak(fd);
+ return 0;
+}
+
+/*
+ * Reading and writing unix-domain sockets works. None of the I/O actually
+ * goes through FUSE.
+ */
+TEST_F(Socket, read_write)
+{
+ mode_t mode = S_IFSOCK | 0755;
+ const int bufsize = 80;
+ char message[bufsize];
+ struct sockaddr_un sa;
+ ssize_t recvd = 0, r;
+ uint64_t ino = 42;
+ int fd, connected;
+ Sequence seq;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_MKNOD);
+ }, Eq(true)),
+ _)
+ ).InSequence(seq)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.entry_valid = UINT64_MAX;
+ out.body.entry.attr_valid = UINT64_MAX;
+ })));
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .InSequence(seq)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.nlink = 1;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ ASSERT_LE(0, fd) << strerror(errno);
+ sa.sun_family = AF_UNIX;
+ strlcpy(sa.sun_path, FULLPATH, sizeof(sa.sun_path));
+ sa.sun_len = sizeof(FULLPATH);
+ ASSERT_EQ(0, bind(fd, (struct sockaddr*)&sa, sizeof(sa)))
+ << strerror(errno);
+ listen(fd, 5);
+ ASSERT_EQ(0, pthread_create(&m_child, NULL, socket_writer, NULL))
+ << strerror(errno);
+ connected = accept(fd, 0, 0);
+ ASSERT_LE(0, connected) << strerror(errno);
+
+ while (recvd < msgsize) {
+ r = read(connected, message + recvd, bufsize - recvd);
+ ASSERT_LE(0, r) << strerror(errno);
+ ASSERT_LT(0, r) << "unexpected EOF";
+ recvd += r;
+ }
+ ASSERT_STREQ(message, MESSAGE);
+
+ leak(fd);
+}
diff --git a/tests/sys/fs/fusefs/flush.cc b/tests/sys/fs/fusefs/flush.cc
new file mode 100644
index 000000000000..7ba1218b3287
--- /dev/null
+++ b/tests/sys/fs/fusefs/flush.cc
@@ -0,0 +1,272 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <fcntl.h>
+#include <unistd.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class Flush: public FuseTest {
+
+public:
+void
+expect_flush(uint64_t ino, int times, pid_t lo, ProcessMockerT r)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_FLUSH &&
+ in.header.nodeid == ino &&
+ in.body.flush.lock_owner == (uint64_t)lo &&
+ in.body.flush.fh == FH);
+ }, Eq(true)),
+ _)
+ ).Times(times)
+ .WillRepeatedly(Invoke(r));
+}
+
+void expect_lookup(const char *relpath, uint64_t ino, int times)
+{
+ FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, times);
+}
+
+/*
+ * When testing FUSE_FLUSH, the FUSE_RELEASE calls are uninteresting. This
+ * expectation will silence googlemock warnings
+ */
+void expect_release()
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_RELEASE);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnErrno(0)));
+}
+};
+
+class FlushWithLocks: public Flush {
+ virtual void SetUp() {
+ m_init_flags = FUSE_POSIX_LOCKS;
+ Flush::SetUp();
+ }
+};
+
+/*
+ * If multiple file descriptors refer to the same file handle, closing each
+ * should send FUSE_FLUSH
+ */
+TEST_F(Flush, open_twice)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int fd, fd2;
+
+ expect_lookup(RELPATH, ino, 2);
+ expect_open(ino, 0, 1);
+ expect_flush(ino, 2, getpid(), ReturnErrno(0));
+ expect_release();
+
+ fd = open(FULLPATH, O_WRONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ fd2 = open(FULLPATH, O_WRONLY);
+ ASSERT_LE(0, fd2) << strerror(errno);
+
+ EXPECT_EQ(0, close(fd2)) << strerror(errno);
+ EXPECT_EQ(0, close(fd)) << strerror(errno);
+}
+
+/**
+ * Test for FOPEN_NOFLUSH: we expect that zero flush calls will be performed.
+ */
+TEST_F(Flush, open_noflush)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ uint64_t pid = (uint64_t)getpid();
+ int fd;
+
+ expect_lookup(RELPATH, ino, 1);
+ expect_open(ino, FOPEN_NOFLUSH, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_FLUSH &&
+ in.header.nodeid == ino &&
+ in.body.flush.lock_owner == pid &&
+ in.body.flush.fh == FH);
+ }, Eq(true)),
+ _)
+ ).Times(0);
+ expect_release();
+
+ fd = open(FULLPATH, O_WRONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+ // close MUST not flush
+ EXPECT_EQ(0, close(fd)) << strerror(errno);
+}
+
+/*
+ * Some FUSE filesystem cache data internally and flush it on release. Such
+ * filesystems may generate errors during release. On Linux, these get
+ * returned by close(2). However, POSIX does not require close(2) to return
+ * this error. FreeBSD's fuse(4) should return EIO if it returns an error at
+ * all.
+ */
+/* http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html */
+TEST_F(Flush, eio)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int fd;
+
+ expect_lookup(RELPATH, ino, 1);
+ expect_open(ino, 0, 1);
+ expect_flush(ino, 1, getpid(), ReturnErrno(EIO));
+ expect_release();
+
+ fd = open(FULLPATH, O_WRONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_TRUE(0 == close(fd) || errno == EIO) << strerror(errno);
+}
+
+/*
+ * If the filesystem returns ENOSYS, it will be treated as success and
+ * no more FUSE_FLUSH operations will be sent to the daemon
+ */
+TEST_F(Flush, enosys)
+{
+ const char FULLPATH0[] = "mountpoint/some_file.txt";
+ const char RELPATH0[] = "some_file.txt";
+ const char FULLPATH1[] = "mountpoint/other_file.txt";
+ const char RELPATH1[] = "other_file.txt";
+ uint64_t ino0 = 42;
+ uint64_t ino1 = 43;
+ int fd0, fd1;
+
+ expect_lookup(RELPATH0, ino0, 1);
+ expect_open(ino0, 0, 1);
+ /* On the 2nd close, FUSE_FLUSH won't be sent at all */
+ expect_flush(ino0, 1, getpid(), ReturnErrno(ENOSYS));
+ expect_release();
+
+ expect_lookup(RELPATH1, ino1, 1);
+ expect_open(ino1, 0, 1);
+ /* On the 2nd close, FUSE_FLUSH won't be sent at all */
+ expect_release();
+
+ fd0 = open(FULLPATH0, O_WRONLY);
+ ASSERT_LE(0, fd0) << strerror(errno);
+
+ fd1 = open(FULLPATH1, O_WRONLY);
+ ASSERT_LE(0, fd1) << strerror(errno);
+
+ EXPECT_EQ(0, close(fd0)) << strerror(errno);
+ EXPECT_EQ(0, close(fd1)) << strerror(errno);
+}
+
+/* A FUSE_FLUSH should be sent on close(2) */
+TEST_F(Flush, flush)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int fd;
+
+ expect_lookup(RELPATH, ino, 1);
+ expect_open(ino, 0, 1);
+ expect_flush(ino, 1, getpid(), ReturnErrno(0));
+ expect_release();
+
+ fd = open(FULLPATH, O_WRONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_TRUE(0 == close(fd)) << strerror(errno);
+}
+
+/*
+ * When closing a file with a POSIX file lock, flush should release the lock,
+ * _even_if_ it's not the process's last file descriptor for this file.
+ */
+TEST_F(FlushWithLocks, unlock_on_close)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int fd, fd2;
+ struct flock fl;
+ pid_t pid = getpid();
+
+ expect_lookup(RELPATH, ino, 2);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_SETLK &&
+ in.header.nodeid == ino &&
+ in.body.setlk.lk.type == F_RDLCK &&
+ in.body.setlk.fh == FH);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(0)));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_SETLK &&
+ in.header.nodeid == ino &&
+ in.body.setlk.lk.type == F_UNLCK &&
+ in.body.setlk.fh == FH);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(0)));
+ expect_flush(ino, 1, pid, ReturnErrno(0));
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_pid = pid;
+ fl.l_type = F_RDLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_sysid = 0;
+ ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
+
+ fd2 = open(FULLPATH, O_WRONLY);
+ ASSERT_LE(0, fd2) << strerror(errno);
+ ASSERT_EQ(0, close(fd2)) << strerror(errno);
+ leak(fd);
+ leak(fd2);
+}
diff --git a/tests/sys/fs/fusefs/forget.cc b/tests/sys/fs/fusefs/forget.cc
new file mode 100644
index 000000000000..1e7764ac4782
--- /dev/null
+++ b/tests/sys/fs/fusefs/forget.cc
@@ -0,0 +1,177 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <sys/types.h>
+#include <sys/mount.h>
+
+#include <fcntl.h>
+#include <semaphore.h>
+#include <unistd.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class Forget: public FuseTest {
+public:
+void SetUp() {
+ if (geteuid() != 0)
+ GTEST_SKIP() << "Only root may use " << reclaim_mib;
+
+ FuseTest::SetUp();
+}
+
+};
+
+/*
+ * When a fusefs vnode is reclaimed, it should send a FUSE_FORGET operation.
+ */
+TEST_F(Forget, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ mode_t mode = S_IFREG | 0755;
+ sem_t sem;
+
+ ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .Times(3)
+ .WillRepeatedly(Invoke(
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.nlink = 1;
+ out.body.entry.attr_valid = UINT64_MAX;
+ })));
+ expect_forget(ino, 3, &sem);
+
+ /*
+ * access(2) the file to force a lookup. Access it twice to double its
+ * lookup count.
+ */
+ ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
+ ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
+
+ reclaim_vnode(FULLPATH);
+
+ sem_wait(&sem);
+ sem_destroy(&sem);
+}
+
+/*
+ * When a directory is reclaimed, the names of its entries vanish from the
+ * namecache
+ */
+TEST_F(Forget, invalidate_names)
+{
+ const char FULLFPATH[] = "mountpoint/some_dir/some_file.txt";
+ const char FULLDPATH[] = "mountpoint/some_dir";
+ const char DNAME[] = "some_dir";
+ const char FNAME[] = "some_file.txt";
+ uint64_t dir_ino = 42;
+ uint64_t file_ino = 43;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, DNAME)
+ .Times(2)
+ .WillRepeatedly(Invoke(
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFDIR | 0755;
+ out.body.entry.nodeid = dir_ino;
+ out.body.entry.attr.nlink = 2;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+
+ /*
+ * Even though we don't reclaim FNAME and its entry is cacheable, we
+ * should get two lookups because the reclaim of DNAME will invalidate
+ * the cached FNAME entry.
+ */
+ EXPECT_LOOKUP(dir_ino, FNAME)
+ .Times(2)
+ .WillRepeatedly(Invoke(
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = file_ino;
+ out.body.entry.attr.nlink = 1;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+ expect_forget(dir_ino, 1);
+
+ /* Access the file to cache its name */
+ ASSERT_EQ(0, access(FULLFPATH, F_OK)) << strerror(errno);
+
+ /* Reclaim the directory, invalidating its children from namecache */
+ reclaim_vnode(FULLDPATH);
+
+ /* Access the file again, causing another lookup */
+ ASSERT_EQ(0, access(FULLFPATH, F_OK)) << strerror(errno);
+}
+
+/*
+ * Reclaiming the root inode should not send a FUSE_FORGET request, nor should
+ * it interfere with further lookup operations.
+ */
+TEST_F(Forget, root)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ mode_t mode = S_IFREG | 0755;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillRepeatedly(Invoke(
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.nlink = 1;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+
+ /* access(2) the file to force a lookup. */
+ ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
+
+ reclaim_vnode("mountpoint");
+ nap();
+
+ /* Access it again, to make sure it's still possible. */
+ ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
+}
diff --git a/tests/sys/fs/fusefs/fsync.cc b/tests/sys/fs/fusefs/fsync.cc
new file mode 100644
index 000000000000..d6f4e6f70da0
--- /dev/null
+++ b/tests/sys/fs/fusefs/fsync.cc
@@ -0,0 +1,285 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <aio.h>
+#include <fcntl.h>
+#include <unistd.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+/*
+ * TODO: remove FUSE_FSYNC_FDATASYNC definition when upgrading to protocol 7.28.
+ * This bit was actually part of kernel protocol version 5.2, but never
+ * documented until after 7.28
+ */
+#ifndef FUSE_FSYNC_FDATASYNC
+#define FUSE_FSYNC_FDATASYNC 1
+#endif
+
+class Fsync: public FuseTest {
+public:
+void expect_fsync(uint64_t ino, uint32_t flags, int error, int times = 1)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_FSYNC &&
+ in.header.nodeid == ino &&
+ /*
+ * TODO: reenable pid check after fixing
+ * bug 236379
+ */
+ //(pid_t)in.header.pid == getpid() &&
+ in.body.fsync.fh == FH &&
+ in.body.fsync.fsync_flags == flags);
+ }, Eq(true)),
+ _)
+ ).Times(times)
+ .WillRepeatedly(Invoke(ReturnErrno(error)));
+}
+
+void expect_lookup(const char *relpath, uint64_t ino, int times = 1)
+{
+ FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, times);
+}
+
+void expect_write(uint64_t ino, uint64_t size, const void *contents)
+{
+ FuseTest::expect_write(ino, 0, size, size, 0, 0, contents);
+}
+
+};
+
+class AioFsync: public Fsync {
+virtual void SetUp() {
+ if (!is_unsafe_aio_enabled())
+ GTEST_SKIP() <<
+ "vfs.aio.enable_unsafe must be set for this test";
+ FuseTest::SetUp();
+}
+};
+
+/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */
+TEST_F(AioFsync, aio_fsync)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ ssize_t bufsize = strlen(CONTENTS);
+ uint64_t ino = 42;
+ struct aiocb iocb, *piocb;
+ int fd;
+
+ expect_lookup(RELPATH, ino);
+ expect_open(ino, 0, 1);
+ expect_write(ino, bufsize, CONTENTS);
+ expect_fsync(ino, 0, 0);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
+
+ bzero(&iocb, sizeof(iocb));
+ iocb.aio_fildes = fd;
+
+ ASSERT_EQ(0, aio_fsync(O_SYNC, &iocb)) << strerror(errno);
+ ASSERT_EQ(0, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
+
+ leak(fd);
+}
+
+/*
+ * fuse(4) should NOT fsync during VOP_RELEASE or VOP_INACTIVE
+ *
+ * This test only really make sense in writeback caching mode, but it should
+ * still pass in any cache mode.
+ */
+TEST_F(Fsync, close)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ ssize_t bufsize = strlen(CONTENTS);
+ uint64_t ino = 42;
+ int fd;
+
+ expect_lookup(RELPATH, ino);
+ expect_open(ino, 0, 1);
+ expect_write(ino, bufsize, CONTENTS);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_SETATTR);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino; // Must match nodeid
+ })));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_FSYNC);
+ }, Eq(true)),
+ _)
+ ).Times(0);
+ expect_flush(ino, 1, ReturnErrno(0));
+ expect_release(ino, FH);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
+ close(fd);
+}
+
+TEST_F(Fsync, eio)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ ssize_t bufsize = strlen(CONTENTS);
+ uint64_t ino = 42;
+ int fd;
+
+ expect_lookup(RELPATH, ino);
+ expect_open(ino, 0, 1);
+ expect_write(ino, bufsize, CONTENTS);
+ expect_fsync(ino, FUSE_FSYNC_FDATASYNC, EIO);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
+ ASSERT_NE(0, fdatasync(fd));
+ ASSERT_EQ(EIO, errno);
+
+ leak(fd);
+}
+
+/*
+ * If the filesystem returns ENOSYS, it will be treated as success and
+ * subsequent calls to VOP_FSYNC will succeed automatically without being sent
+ * to the filesystem daemon
+ */
+TEST_F(Fsync, enosys)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ ssize_t bufsize = strlen(CONTENTS);
+ uint64_t ino = 42;
+ int fd;
+
+ expect_lookup(RELPATH, ino);
+ expect_open(ino, 0, 1);
+ expect_write(ino, bufsize, CONTENTS);
+ expect_fsync(ino, FUSE_FSYNC_FDATASYNC, ENOSYS);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
+ EXPECT_EQ(0, fdatasync(fd));
+
+ /* Subsequent calls shouldn't query the daemon*/
+ EXPECT_EQ(0, fdatasync(fd));
+ leak(fd);
+}
+
+
+TEST_F(Fsync, fdatasync)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ ssize_t bufsize = strlen(CONTENTS);
+ uint64_t ino = 42;
+ int fd;
+
+ expect_lookup(RELPATH, ino);
+ expect_open(ino, 0, 1);
+ expect_write(ino, bufsize, CONTENTS);
+ expect_fsync(ino, FUSE_FSYNC_FDATASYNC, 0);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
+ ASSERT_EQ(0, fdatasync(fd)) << strerror(errno);
+
+ leak(fd);
+}
+
+TEST_F(Fsync, fsync)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ ssize_t bufsize = strlen(CONTENTS);
+ uint64_t ino = 42;
+ int fd;
+
+ expect_lookup(RELPATH, ino);
+ expect_open(ino, 0, 1);
+ expect_write(ino, bufsize, CONTENTS);
+ expect_fsync(ino, 0, 0);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
+ ASSERT_EQ(0, fsync(fd)) << strerror(errno);
+
+ leak(fd);
+}
+
+/* If multiple FUSE file handles are active, we must fsync them all */
+TEST_F(Fsync, two_handles)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ ssize_t bufsize = strlen(CONTENTS);
+ uint64_t ino = 42;
+ int fd1, fd2;
+
+ expect_lookup(RELPATH, ino, 2);
+ expect_open(ino, 0, 2);
+ expect_write(ino, bufsize, CONTENTS);
+ expect_fsync(ino, 0, 0, 2);
+
+ fd1 = open(FULLPATH, O_WRONLY);
+ ASSERT_LE(0, fd1) << strerror(errno);
+ fd2 = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd2) << strerror(errno);
+ ASSERT_EQ(bufsize, write(fd1, CONTENTS, bufsize)) << strerror(errno);
+ ASSERT_EQ(0, fsync(fd1)) << strerror(errno);
+
+ leak(fd1);
+ leak(fd2);
+}
diff --git a/tests/sys/fs/fusefs/fsyncdir.cc b/tests/sys/fs/fusefs/fsyncdir.cc
new file mode 100644
index 000000000000..aef93a0fb1d5
--- /dev/null
+++ b/tests/sys/fs/fusefs/fsyncdir.cc
@@ -0,0 +1,195 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <aio.h>
+#include <fcntl.h>
+#include <unistd.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+/*
+ * TODO: remove FUSE_FSYNC_FDATASYNC definition when upgrading to protocol 7.28.
+ * This bit was actually part of kernel protocol version 5.2, but never
+ * documented until after 7.28
+ */
+#ifndef FUSE_FSYNC_FDATASYNC
+#define FUSE_FSYNC_FDATASYNC 1
+#endif
+
+class FsyncDir: public FuseTest {
+public:
+void expect_fsyncdir(uint64_t ino, uint32_t flags, int error)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_FSYNCDIR &&
+ in.header.nodeid == ino &&
+ /*
+ * TODO: reenable pid check after fixing
+ * bug 236379
+ */
+ //(pid_t)in.header.pid == getpid() &&
+ in.body.fsyncdir.fh == FH &&
+ in.body.fsyncdir.fsync_flags == flags);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(error)));
+}
+
+void expect_lookup(const char *relpath, uint64_t ino)
+{
+ FuseTest::expect_lookup(relpath, ino, S_IFDIR | 0755, 0, 1);
+}
+
+};
+
+class AioFsyncDir: public FsyncDir {
+virtual void SetUp() {
+ if (!is_unsafe_aio_enabled())
+ GTEST_SKIP() <<
+ "vfs.aio.enable_unsafe must be set for this test";
+ FuseTest::SetUp();
+}
+};
+
+/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */
+TEST_F(AioFsyncDir, aio_fsync)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ struct aiocb iocb, *piocb;
+ int fd;
+
+ expect_lookup(RELPATH, ino);
+ expect_opendir(ino);
+ expect_fsyncdir(ino, 0, 0);
+
+ fd = open(FULLPATH, O_DIRECTORY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ bzero(&iocb, sizeof(iocb));
+ iocb.aio_fildes = fd;
+
+ ASSERT_EQ(0, aio_fsync(O_SYNC, &iocb)) << strerror(errno);
+ ASSERT_EQ(0, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
+
+ leak(fd);
+}
+
+TEST_F(FsyncDir, eio)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ uint64_t ino = 42;
+ int fd;
+
+ expect_lookup(RELPATH, ino);
+ expect_opendir(ino);
+ expect_fsyncdir(ino, 0, EIO);
+
+ fd = open(FULLPATH, O_DIRECTORY);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_NE(0, fsync(fd));
+ ASSERT_EQ(EIO, errno);
+
+ leak(fd);
+}
+
+/*
+ * If the filesystem returns ENOSYS, it will be treated as success and
+ * subsequent calls to VOP_FSYNC will succeed automatically without being sent
+ * to the filesystem daemon
+ */
+TEST_F(FsyncDir, enosys)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ uint64_t ino = 42;
+ int fd;
+
+ expect_lookup(RELPATH, ino);
+ expect_opendir(ino);
+ expect_fsyncdir(ino, 0, ENOSYS);
+
+ fd = open(FULLPATH, O_DIRECTORY);
+ ASSERT_LE(0, fd) << strerror(errno);
+ EXPECT_EQ(0, fsync(fd)) << strerror(errno);
+
+ /* Subsequent calls shouldn't query the daemon*/
+ EXPECT_EQ(0, fsync(fd)) << strerror(errno);
+
+ leak(fd);
+}
+
+TEST_F(FsyncDir, fsyncdata)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ uint64_t ino = 42;
+ int fd;
+
+ expect_lookup(RELPATH, ino);
+ expect_opendir(ino);
+ expect_fsyncdir(ino, FUSE_FSYNC_FDATASYNC, 0);
+
+ fd = open(FULLPATH, O_DIRECTORY);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(0, fdatasync(fd)) << strerror(errno);
+
+ leak(fd);
+}
+
+/*
+ * Unlike regular files, the kernel doesn't know whether a directory is or
+ * isn't dirty, so fuse(4) should always send FUSE_FSYNCDIR on fsync(2)
+ */
+TEST_F(FsyncDir, fsync)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ uint64_t ino = 42;
+ int fd;
+
+ expect_lookup(RELPATH, ino);
+ expect_opendir(ino);
+ expect_fsyncdir(ino, 0, 0);
+
+ fd = open(FULLPATH, O_DIRECTORY);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(0, fsync(fd)) << strerror(errno);
+
+ leak(fd);
+}
diff --git a/tests/sys/fs/fusefs/getattr.cc b/tests/sys/fs/fusefs/getattr.cc
new file mode 100644
index 000000000000..98a757fdff94
--- /dev/null
+++ b/tests/sys/fs/fusefs/getattr.cc
@@ -0,0 +1,367 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <sys/param.h>
+
+#include <semaphore.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class Getattr : public FuseTest {
+public:
+void expect_lookup(const char *relpath, uint64_t ino, mode_t mode,
+ uint64_t size, int times, uint64_t attr_valid, uint32_t attr_valid_nsec)
+{
+ EXPECT_LOOKUP(FUSE_ROOT_ID, relpath)
+ .Times(times)
+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.nlink = 1;
+ out.body.entry.attr_valid = attr_valid;
+ out.body.entry.attr_valid_nsec = attr_valid_nsec;
+ out.body.entry.attr.size = size;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+}
+};
+
+class Getattr_7_8: public FuseTest {
+public:
+virtual void SetUp() {
+ m_kernel_minor_version = 8;
+ FuseTest::SetUp();
+}
+};
+
+/*
+ * If getattr returns a non-zero cache timeout, then subsequent VOP_GETATTRs
+ * should use the cached attributes, rather than query the daemon
+ */
+TEST_F(Getattr, attr_cache)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ struct stat sb;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = ino;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr_valid = UINT64_MAX;
+ out.body.attr.attr.ino = ino; // Must match nodeid
+ out.body.attr.attr.mode = S_IFREG | 0644;
+ })));
+ EXPECT_EQ(0, stat(FULLPATH, &sb));
+ /* The second stat(2) should use cached attributes */
+ EXPECT_EQ(0, stat(FULLPATH, &sb));
+}
+
+/*
+ * If getattr returns a finite but non-zero cache timeout, then we should
+ * discard the cached attributes and requery the daemon after the timeout
+ * period passes.
+ */
+TEST_F(Getattr, attr_cache_timeout)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ struct stat sb;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1, 0, 0);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).Times(2)
+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr_valid_nsec = NAP_NS / 2;
+ out.body.attr.attr_valid = 0;
+ out.body.attr.attr.ino = ino; // Must match nodeid
+ out.body.attr.attr.mode = S_IFREG | 0644;
+ })));
+
+ EXPECT_EQ(0, stat(FULLPATH, &sb));
+ nap();
+ /* Timeout has expired. stat(2) should requery the daemon */
+ EXPECT_EQ(0, stat(FULLPATH, &sb));
+}
+
+/*
+ * If attr.blksize is zero, then the kernel should use a default value for
+ * st_blksize
+ */
+TEST_F(Getattr, blksize_zero)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ struct stat sb;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 1, 1, 0, 0);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.mode = S_IFREG | 0644;
+ out.body.attr.attr.ino = ino; // Must match nodeid
+ out.body.attr.attr.blksize = 0;
+ out.body.attr.attr.size = 1;
+ })));
+
+ ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno);
+ EXPECT_EQ((blksize_t)PAGE_SIZE, sb.st_blksize);
+}
+
+TEST_F(Getattr, enoent)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ struct stat sb;
+ const uint64_t ino = 42;
+ sem_t sem;
+
+ ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1, 0, 0);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(ENOENT)));
+ // Since FUSE_GETATTR returns ENOENT, the kernel will reclaim the vnode
+ // and send a FUSE_FORGET
+ expect_forget(ino, 1, &sem);
+
+ EXPECT_NE(0, stat(FULLPATH, &sb));
+ EXPECT_EQ(ENOENT, errno);
+
+ sem_wait(&sem);
+ sem_destroy(&sem);
+}
+
+TEST_F(Getattr, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ struct stat sb;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 1, 1, 0, 0);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.body.getattr.getattr_flags == 0 &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino; // Must match nodeid
+ out.body.attr.attr.mode = S_IFREG | 0644;
+ out.body.attr.attr.size = 1;
+ out.body.attr.attr.blocks = 2;
+ out.body.attr.attr.atime = 3;
+ out.body.attr.attr.mtime = 4;
+ out.body.attr.attr.ctime = 5;
+ out.body.attr.attr.atimensec = 6;
+ out.body.attr.attr.mtimensec = 7;
+ out.body.attr.attr.ctimensec = 8;
+ out.body.attr.attr.nlink = 9;
+ out.body.attr.attr.uid = 10;
+ out.body.attr.attr.gid = 11;
+ out.body.attr.attr.rdev = 12;
+ out.body.attr.attr.blksize = 12345;
+ })));
+
+ ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno);
+ EXPECT_EQ(1, sb.st_size);
+ EXPECT_EQ(2, sb.st_blocks);
+ EXPECT_EQ(3, sb.st_atim.tv_sec);
+ EXPECT_EQ(6, sb.st_atim.tv_nsec);
+ EXPECT_EQ(4, sb.st_mtim.tv_sec);
+ EXPECT_EQ(7, sb.st_mtim.tv_nsec);
+ EXPECT_EQ(5, sb.st_ctim.tv_sec);
+ EXPECT_EQ(8, sb.st_ctim.tv_nsec);
+ EXPECT_EQ(9ull, sb.st_nlink);
+ EXPECT_EQ(10ul, sb.st_uid);
+ EXPECT_EQ(11ul, sb.st_gid);
+ EXPECT_EQ(12ul, sb.st_rdev);
+ EXPECT_EQ((blksize_t)12345, sb.st_blksize);
+ EXPECT_EQ(ino, sb.st_ino);
+ EXPECT_EQ(S_IFREG | 0644, sb.st_mode);
+
+ /*
+ * st_birthtim and st_flags are not supported by the fuse protocol.
+ * They're only supported as OS-specific extensions to OSX. For
+ * birthtime, the convention for "not supported" is "negative one
+ * second".
+ */
+ EXPECT_EQ(-1, sb.st_birthtim.tv_sec);
+ EXPECT_EQ(0, sb.st_birthtim.tv_nsec);
+ EXPECT_EQ(0u, sb.st_flags);
+}
+
+/*
+ * FUSE_GETATTR returns a different file type, even though the entry cache
+ * hasn't expired. This is a server bug! It probably means that the server
+ * removed the file and recreated it with the same inode but a different vtyp.
+ * The best thing fusefs can do is return ENOENT to the caller. After all, the
+ * entry must not have existed recently.
+ */
+TEST_F(Getattr, vtyp_conflict)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ struct stat sb;
+ sem_t sem;
+
+ ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.nlink = 1;
+ out.body.entry.attr_valid = 0;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.body.getattr.getattr_flags == 0 &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino; // Must match nodeid
+ out.body.attr.attr.mode = S_IFDIR | 0755; // Changed!
+ out.body.attr.attr.nlink = 2;
+ })));
+ // We should reclaim stale vnodes
+ expect_forget(ino, 1, &sem);
+
+ ASSERT_NE(0, stat(FULLPATH, &sb));
+ EXPECT_EQ(errno, ENOENT);
+
+ sem_wait(&sem);
+ sem_destroy(&sem);
+}
+
+TEST_F(Getattr_7_8, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ struct stat sb;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry_7_8);
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.nlink = 1;
+ out.body.entry.attr.size = 1;
+ })));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr_7_8);
+ out.body.attr.attr.ino = ino; // Must match nodeid
+ out.body.attr.attr.mode = S_IFREG | 0644;
+ out.body.attr.attr.size = 1;
+ out.body.attr.attr.blocks = 2;
+ out.body.attr.attr.atime = 3;
+ out.body.attr.attr.mtime = 4;
+ out.body.attr.attr.ctime = 5;
+ out.body.attr.attr.atimensec = 6;
+ out.body.attr.attr.mtimensec = 7;
+ out.body.attr.attr.ctimensec = 8;
+ out.body.attr.attr.nlink = 9;
+ out.body.attr.attr.uid = 10;
+ out.body.attr.attr.gid = 11;
+ out.body.attr.attr.rdev = 12;
+ })));
+
+ ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno);
+ EXPECT_EQ(1, sb.st_size);
+ EXPECT_EQ(2, sb.st_blocks);
+ EXPECT_EQ(3, sb.st_atim.tv_sec);
+ EXPECT_EQ(6, sb.st_atim.tv_nsec);
+ EXPECT_EQ(4, sb.st_mtim.tv_sec);
+ EXPECT_EQ(7, sb.st_mtim.tv_nsec);
+ EXPECT_EQ(5, sb.st_ctim.tv_sec);
+ EXPECT_EQ(8, sb.st_ctim.tv_nsec);
+ EXPECT_EQ(9ull, sb.st_nlink);
+ EXPECT_EQ(10ul, sb.st_uid);
+ EXPECT_EQ(11ul, sb.st_gid);
+ EXPECT_EQ(12ul, sb.st_rdev);
+ EXPECT_EQ(ino, sb.st_ino);
+ EXPECT_EQ(S_IFREG | 0644, sb.st_mode);
+
+ //st_birthtim and st_flags are not supported by protocol 7.8. They're
+ //only supported as OS-specific extensions to OSX.
+}
diff --git a/tests/sys/fs/fusefs/interrupt.cc b/tests/sys/fs/fusefs/interrupt.cc
new file mode 100644
index 000000000000..3bfd4a834932
--- /dev/null
+++ b/tests/sys/fs/fusefs/interrupt.cc
@@ -0,0 +1,796 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <sys/types.h>
+#include <sys/extattr.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <signal.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+/* Initial size of files used by these tests */
+const off_t FILESIZE = 1000;
+/* Access mode used by all directories in these tests */
+const mode_t MODE = 0755;
+const char FULLDIRPATH0[] = "mountpoint/some_dir";
+const char RELDIRPATH0[] = "some_dir";
+const char FULLDIRPATH1[] = "mountpoint/other_dir";
+const char RELDIRPATH1[] = "other_dir";
+
+static sem_t *blocked_semaphore;
+static sem_t *signaled_semaphore;
+
+static bool killer_should_sleep = false;
+
+/* Don't do anything; all we care about is that the syscall gets interrupted */
+void sigusr2_handler(int __unused sig) {
+ if (verbosity > 1) {
+ printf("Signaled! thread %p\n", pthread_self());
+ }
+
+}
+
+void* killer(void* target) {
+ /* Wait until the main thread is blocked in fdisp_wait_answ */
+ if (killer_should_sleep)
+ nap();
+ else
+ sem_wait(blocked_semaphore);
+ if (verbosity > 1)
+ printf("Signalling! thread %p\n", target);
+ pthread_kill((pthread_t)target, SIGUSR2);
+ if (signaled_semaphore != NULL)
+ sem_post(signaled_semaphore);
+
+ return(NULL);
+}
+
+class Interrupt: public FuseTest {
+public:
+pthread_t m_child;
+
+Interrupt(): m_child(NULL) {};
+
+void expect_lookup(const char *relpath, uint64_t ino)
+{
+ FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, FILESIZE, 1);
+}
+
+/*
+ * Expect a FUSE_MKDIR but don't reply. Instead, just record the unique value
+ * to the provided pointer
+ */
+void expect_mkdir(uint64_t *mkdir_unique)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_MKDIR);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke([=](auto in, auto &out __unused) {
+ *mkdir_unique = in.header.unique;
+ sem_post(blocked_semaphore);
+ }));
+}
+
+/*
+ * Expect a FUSE_READ but don't reply. Instead, just record the unique value
+ * to the provided pointer
+ */
+void expect_read(uint64_t ino, uint64_t *read_unique)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READ &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke([=](auto in, auto &out __unused) {
+ *read_unique = in.header.unique;
+ sem_post(blocked_semaphore);
+ }));
+}
+
+/*
+ * Expect a FUSE_WRITE but don't reply. Instead, just record the unique value
+ * to the provided pointer
+ */
+void expect_write(uint64_t ino, uint64_t *write_unique)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_WRITE &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke([=](auto in, auto &out __unused) {
+ *write_unique = in.header.unique;
+ sem_post(blocked_semaphore);
+ }));
+}
+
+void setup_interruptor(pthread_t target, bool sleep = false)
+{
+ ASSERT_NE(SIG_ERR, signal(SIGUSR2, sigusr2_handler)) << strerror(errno);
+ killer_should_sleep = sleep;
+ ASSERT_EQ(0, pthread_create(&m_child, NULL, killer, (void*)target))
+ << strerror(errno);
+}
+
+void SetUp() {
+ const int mprot = PROT_READ | PROT_WRITE;
+ const int mflags = MAP_ANON | MAP_SHARED;
+
+ signaled_semaphore = NULL;
+
+ blocked_semaphore = (sem_t*)mmap(NULL, sizeof(*blocked_semaphore),
+ mprot, mflags, -1, 0);
+ ASSERT_NE(MAP_FAILED, blocked_semaphore) << strerror(errno);
+ ASSERT_EQ(0, sem_init(blocked_semaphore, 1, 0)) << strerror(errno);
+ ASSERT_EQ(0, siginterrupt(SIGUSR2, 1));
+
+ FuseTest::SetUp();
+}
+
+void TearDown() {
+ struct sigaction sa;
+
+ if (m_child != NULL) {
+ pthread_join(m_child, NULL);
+ }
+ bzero(&sa, sizeof(sa));
+ sa.sa_handler = SIG_DFL;
+ sigaction(SIGUSR2, &sa, NULL);
+
+ sem_destroy(blocked_semaphore);
+ munmap(blocked_semaphore, sizeof(*blocked_semaphore));
+
+ FuseTest::TearDown();
+}
+};
+
+class Intr: public Interrupt {};
+
+class Nointr: public Interrupt {
+ void SetUp() {
+ m_nointr = true;
+ Interrupt::SetUp();
+ }
+};
+
+static void* mkdir0(void* arg __unused) {
+ ssize_t r;
+
+ r = mkdir(FULLDIRPATH0, MODE);
+ if (r >= 0)
+ return 0;
+ else
+ return (void*)(intptr_t)errno;
+}
+
+static void* read1(void* arg) {
+ const size_t bufsize = FILESIZE;
+ char buf[bufsize];
+ int fd = (int)(intptr_t)arg;
+ ssize_t r;
+
+ r = read(fd, buf, bufsize);
+ if (r >= 0)
+ return 0;
+ else
+ return (void*)(intptr_t)errno;
+}
+
+/*
+ * An interrupt operation that gets received after the original command is
+ * complete should generate an EAGAIN response.
+ */
+/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
+TEST_F(Intr, already_complete)
+{
+ uint64_t ino = 42;
+ pthread_t self;
+ uint64_t mkdir_unique = 0;
+ Sequence seq;
+
+ self = pthread_self();
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
+ .InSequence(seq)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_mkdir(&mkdir_unique);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([&](auto in) {
+ return (in.header.opcode == FUSE_INTERRUPT &&
+ in.body.interrupt.unique == mkdir_unique);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke([&](auto in, auto &out) {
+ // First complete the mkdir request
+ std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
+ out0->header.unique = mkdir_unique;
+ SET_OUT_HEADER_LEN(*out0, entry);
+ out0->body.create.entry.attr.mode = S_IFDIR | MODE;
+ out0->body.create.entry.nodeid = ino;
+ out.push_back(std::move(out0));
+
+ // Then, respond EAGAIN to the interrupt request
+ std::unique_ptr<mockfs_buf_out> out1(new mockfs_buf_out);
+ out1->header.unique = in.header.unique;
+ out1->header.error = -EAGAIN;
+ out1->header.len = sizeof(out1->header);
+ out.push_back(std::move(out1));
+ }));
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
+ .InSequence(seq)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFDIR | MODE;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.nlink = 2;
+ })));
+
+ setup_interruptor(self);
+ EXPECT_EQ(0, mkdir(FULLDIRPATH0, MODE)) << strerror(errno);
+ /*
+ * The final syscall simply ensures that the test's main thread doesn't
+ * end before the daemon finishes responding to the FUSE_INTERRUPT.
+ */
+ EXPECT_EQ(0, access(FULLDIRPATH0, F_OK)) << strerror(errno);
+}
+
+/*
+ * If a FUSE file system returns ENOSYS for a FUSE_INTERRUPT operation, the
+ * kernel should not attempt to interrupt any other operations on that mount
+ * point.
+ */
+TEST_F(Intr, enosys)
+{
+ uint64_t ino0 = 42, ino1 = 43;;
+ uint64_t mkdir_unique;
+ pthread_t self, th0;
+ sem_t sem0, sem1;
+ void *thr0_value;
+ Sequence seq;
+
+ self = pthread_self();
+ ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);
+ ASSERT_EQ(0, sem_init(&sem1, 0, 0)) << strerror(errno);
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH1)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_mkdir(&mkdir_unique);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([&](auto in) {
+ return (in.header.opcode == FUSE_INTERRUPT &&
+ in.body.interrupt.unique == mkdir_unique);
+ }, Eq(true)),
+ _)
+ ).InSequence(seq)
+ .WillOnce(Invoke([&](auto in, auto &out) {
+ // reject FUSE_INTERRUPT and respond to the FUSE_MKDIR
+ std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
+ std::unique_ptr<mockfs_buf_out> out1(new mockfs_buf_out);
+
+ out0->header.unique = in.header.unique;
+ out0->header.error = -ENOSYS;
+ out0->header.len = sizeof(out0->header);
+ out.push_back(std::move(out0));
+
+ SET_OUT_HEADER_LEN(*out1, entry);
+ out1->body.create.entry.attr.mode = S_IFDIR | MODE;
+ out1->body.create.entry.nodeid = ino1;
+ out1->header.unique = mkdir_unique;
+ out.push_back(std::move(out1));
+ }));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([&](auto in) {
+ return (in.header.opcode == FUSE_MKDIR);
+ }, Eq(true)),
+ _)
+ ).InSequence(seq)
+ .WillOnce(Invoke([&](auto in, auto &out) {
+ std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
+
+ sem_post(&sem0);
+ sem_wait(&sem1);
+
+ SET_OUT_HEADER_LEN(*out0, entry);
+ out0->body.create.entry.attr.mode = S_IFDIR | MODE;
+ out0->body.create.entry.nodeid = ino0;
+ out0->header.unique = in.header.unique;
+ out.push_back(std::move(out0));
+ }));
+
+ setup_interruptor(self);
+ /* First mkdir operation should finish synchronously */
+ ASSERT_EQ(0, mkdir(FULLDIRPATH1, MODE)) << strerror(errno);
+
+ ASSERT_EQ(0, pthread_create(&th0, NULL, mkdir0, NULL))
+ << strerror(errno);
+
+ sem_wait(&sem0);
+ /*
+ * th0 should be blocked waiting for the fuse daemon thread.
+ * Signal it. No FUSE_INTERRUPT should result
+ */
+ pthread_kill(th0, SIGUSR1);
+ /* Allow the daemon thread to proceed */
+ sem_post(&sem1);
+ pthread_join(th0, &thr0_value);
+ /* Second mkdir should've finished without error */
+ EXPECT_EQ(0, (intptr_t)thr0_value);
+}
+
+/*
+ * A FUSE filesystem is legally allowed to ignore INTERRUPT operations, and
+ * complete the original operation whenever it damn well pleases.
+ */
+/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
+TEST_F(Intr, ignore)
+{
+ uint64_t ino = 42;
+ pthread_t self;
+ uint64_t mkdir_unique;
+
+ self = pthread_self();
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_mkdir(&mkdir_unique);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([&](auto in) {
+ return (in.header.opcode == FUSE_INTERRUPT &&
+ in.body.interrupt.unique == mkdir_unique);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke([&](auto in __unused, auto &out) {
+ // Ignore FUSE_INTERRUPT; respond to the FUSE_MKDIR
+ std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
+ out0->header.unique = mkdir_unique;
+ SET_OUT_HEADER_LEN(*out0, entry);
+ out0->body.create.entry.attr.mode = S_IFDIR | MODE;
+ out0->body.create.entry.nodeid = ino;
+ out.push_back(std::move(out0));
+ }));
+
+ setup_interruptor(self);
+ ASSERT_EQ(0, mkdir(FULLDIRPATH0, MODE)) << strerror(errno);
+}
+
+/*
+ * A restartable operation (basically, anything except write or setextattr)
+ * that hasn't yet been sent to userland can be interrupted without sending
+ * FUSE_INTERRUPT, and will be automatically restarted.
+ */
+TEST_F(Intr, in_kernel_restartable)
+{
+ const char FULLPATH1[] = "mountpoint/other_file.txt";
+ const char RELPATH1[] = "other_file.txt";
+ uint64_t ino0 = 42, ino1 = 43;
+ int fd1;
+ pthread_t self, th0, th1;
+ sem_t sem0, sem1;
+ void *thr0_value, *thr1_value;
+
+ ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);
+ ASSERT_EQ(0, sem_init(&sem1, 0, 0)) << strerror(errno);
+ self = pthread_self();
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_lookup(RELPATH1, ino1);
+ expect_open(ino1, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_MKDIR);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([&](auto in __unused, auto& out) {
+ /* Let the next write proceed */
+ sem_post(&sem1);
+ /* Pause the daemon thread so it won't read the next op */
+ sem_wait(&sem0);
+
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.create.entry.attr.mode = S_IFDIR | MODE;
+ out.body.create.entry.nodeid = ino0;
+ })));
+ FuseTest::expect_read(ino1, 0, FILESIZE, 0, NULL);
+
+ fd1 = open(FULLPATH1, O_RDONLY);
+ ASSERT_LE(0, fd1) << strerror(errno);
+
+ /* Use a separate thread for each operation */
+ ASSERT_EQ(0, pthread_create(&th0, NULL, mkdir0, NULL))
+ << strerror(errno);
+
+ sem_wait(&sem1); /* Sequence the two operations */
+
+ ASSERT_EQ(0, pthread_create(&th1, NULL, read1, (void*)(intptr_t)fd1))
+ << strerror(errno);
+
+ setup_interruptor(self, true);
+
+ pause(); /* Wait for signal */
+
+ /* Unstick the daemon */
+ ASSERT_EQ(0, sem_post(&sem0)) << strerror(errno);
+
+ /* Wait awhile to make sure the signal generates no FUSE_INTERRUPT */
+ nap();
+
+ pthread_join(th1, &thr1_value);
+ pthread_join(th0, &thr0_value);
+ EXPECT_EQ(0, (intptr_t)thr1_value);
+ EXPECT_EQ(0, (intptr_t)thr0_value);
+ sem_destroy(&sem1);
+ sem_destroy(&sem0);
+
+ leak(fd1);
+}
+
+/*
+ * An operation that hasn't yet been sent to userland can be interrupted
+ * without sending FUSE_INTERRUPT. If it's a non-restartable operation (write
+ * or setextattr) it will return EINTR.
+ */
+TEST_F(Intr, in_kernel_nonrestartable)
+{
+ const char FULLPATH1[] = "mountpoint/other_file.txt";
+ const char RELPATH1[] = "other_file.txt";
+ const char value[] = "whatever";
+ ssize_t value_len = strlen(value) + 1;
+ uint64_t ino0 = 42, ino1 = 43;
+ int ns = EXTATTR_NAMESPACE_USER;
+ int fd1;
+ pthread_t self, th0;
+ sem_t sem0, sem1;
+ void *thr0_value;
+ ssize_t r;
+
+ ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);
+ ASSERT_EQ(0, sem_init(&sem1, 0, 0)) << strerror(errno);
+ self = pthread_self();
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_lookup(RELPATH1, ino1);
+ expect_open(ino1, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_MKDIR);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([&](auto in __unused, auto& out) {
+ /* Let the next write proceed */
+ sem_post(&sem1);
+ /* Pause the daemon thread so it won't read the next op */
+ sem_wait(&sem0);
+
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.create.entry.attr.mode = S_IFDIR | MODE;
+ out.body.create.entry.nodeid = ino0;
+ })));
+
+ fd1 = open(FULLPATH1, O_WRONLY);
+ ASSERT_LE(0, fd1) << strerror(errno);
+
+ /* Use a separate thread for the first write */
+ ASSERT_EQ(0, pthread_create(&th0, NULL, mkdir0, NULL))
+ << strerror(errno);
+
+ sem_wait(&sem1); /* Sequence the two operations */
+
+ setup_interruptor(self, true);
+
+ r = extattr_set_fd(fd1, ns, "foo", (const void*)value, value_len);
+ EXPECT_NE(0, r);
+ EXPECT_EQ(EINTR, errno);
+
+ /* Unstick the daemon */
+ ASSERT_EQ(0, sem_post(&sem0)) << strerror(errno);
+
+ /* Wait awhile to make sure the signal generates no FUSE_INTERRUPT */
+ nap();
+
+ pthread_join(th0, &thr0_value);
+ EXPECT_EQ(0, (intptr_t)thr0_value);
+ sem_destroy(&sem1);
+ sem_destroy(&sem0);
+
+ leak(fd1);
+}
+
+/*
+ * A syscall that gets interrupted while blocking on FUSE I/O should send a
+ * FUSE_INTERRUPT command to the fuse filesystem, which should then send EINTR
+ * in response to the _original_ operation. The kernel should ultimately
+ * return EINTR to userspace
+ */
+/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
+TEST_F(Intr, in_progress)
+{
+ pthread_t self;
+ uint64_t mkdir_unique;
+
+ self = pthread_self();
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_mkdir(&mkdir_unique);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([&](auto in) {
+ return (in.header.opcode == FUSE_INTERRUPT &&
+ in.body.interrupt.unique == mkdir_unique);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke([&](auto in __unused, auto &out) {
+ std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
+ out0->header.error = -EINTR;
+ out0->header.unique = mkdir_unique;
+ out0->header.len = sizeof(out0->header);
+ out.push_back(std::move(out0));
+ }));
+
+ setup_interruptor(self);
+ ASSERT_EQ(-1, mkdir(FULLDIRPATH0, MODE));
+ EXPECT_EQ(EINTR, errno);
+}
+
+/* Reads should also be interruptible */
+TEST_F(Intr, in_progress_read)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const size_t bufsize = 80;
+ char buf[bufsize];
+ uint64_t ino = 42;
+ int fd;
+ pthread_t self;
+ uint64_t read_unique;
+
+ self = pthread_self();
+
+ expect_lookup(RELPATH, ino);
+ expect_open(ino, 0, 1);
+ expect_read(ino, &read_unique);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([&](auto in) {
+ return (in.header.opcode == FUSE_INTERRUPT &&
+ in.body.interrupt.unique == read_unique);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke([&](auto in __unused, auto &out) {
+ std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
+ out0->header.error = -EINTR;
+ out0->header.unique = read_unique;
+ out0->header.len = sizeof(out0->header);
+ out.push_back(std::move(out0));
+ }));
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ setup_interruptor(self);
+ ASSERT_EQ(-1, read(fd, buf, bufsize));
+ EXPECT_EQ(EINTR, errno);
+
+ leak(fd);
+}
+
+/*
+ * When mounted with -o nointr, fusefs will block signals while waiting for the
+ * server.
+ */
+TEST_F(Nointr, block)
+{
+ uint64_t ino = 42;
+ pthread_t self;
+ sem_t sem0;
+
+ ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);
+ signaled_semaphore = &sem0;
+ self = pthread_self();
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_MKDIR);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([&](auto in __unused, auto& out) {
+ /* Let the killer proceed */
+ sem_post(blocked_semaphore);
+
+ /* Wait until after the signal has been sent */
+ sem_wait(signaled_semaphore);
+ /* Allow time for the mkdir thread to receive the signal */
+ nap();
+
+ /* Finally, complete the original op */
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.create.entry.attr.mode = S_IFDIR | MODE;
+ out.body.create.entry.nodeid = ino;
+ })));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([&](auto in) {
+ return (in.header.opcode == FUSE_INTERRUPT);
+ }, Eq(true)),
+ _)
+ ).Times(0);
+
+ setup_interruptor(self);
+ ASSERT_EQ(0, mkdir(FULLDIRPATH0, MODE)) << strerror(errno);
+
+ sem_destroy(&sem0);
+}
+
+/* FUSE_INTERRUPT operations should take priority over other pending ops */
+TEST_F(Intr, priority)
+{
+ Sequence seq;
+ uint64_t ino1 = 43;
+ uint64_t mkdir_unique;
+ pthread_t th0;
+ sem_t sem0, sem1;
+
+ ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);
+ ASSERT_EQ(0, sem_init(&sem1, 0, 0)) << strerror(errno);
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH1)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_MKDIR);
+ }, Eq(true)),
+ _)
+ ).InSequence(seq)
+ .WillOnce(Invoke(ReturnImmediate([&](auto in, auto& out) {
+ mkdir_unique = in.header.unique;
+
+ /* Let the next mkdir proceed */
+ sem_post(&sem1);
+
+ /* Pause the daemon thread so it won't read the next op */
+ sem_wait(&sem0);
+
+ /* Finally, interrupt the original op */
+ out.header.error = -EINTR;
+ out.header.unique = mkdir_unique;
+ out.header.len = sizeof(out.header);
+ })));
+ /*
+ * FUSE_INTERRUPT should be received before the second FUSE_MKDIR,
+ * even though it was generated later
+ */
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([&](auto in) {
+ return (in.header.opcode == FUSE_INTERRUPT &&
+ in.body.interrupt.unique == mkdir_unique);
+ }, Eq(true)),
+ _)
+ ).InSequence(seq)
+ .WillOnce(Invoke(ReturnErrno(EAGAIN)));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([&](auto in) {
+ return (in.header.opcode == FUSE_MKDIR);
+ }, Eq(true)),
+ _)
+ ).InSequence(seq)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.create.entry.attr.mode = S_IFDIR | MODE;
+ out.body.create.entry.nodeid = ino1;
+ })));
+
+ /* Use a separate thread for the first mkdir */
+ ASSERT_EQ(0, pthread_create(&th0, NULL, mkdir0, NULL))
+ << strerror(errno);
+
+ signaled_semaphore = &sem0;
+
+ sem_wait(&sem1); /* Sequence the two mkdirs */
+ setup_interruptor(th0, true);
+ ASSERT_EQ(0, mkdir(FULLDIRPATH1, MODE)) << strerror(errno);
+
+ pthread_join(th0, NULL);
+ sem_destroy(&sem1);
+ sem_destroy(&sem0);
+}
+
+/*
+ * If the FUSE filesystem receives the FUSE_INTERRUPT operation before
+ * processing the original, then it should wait for "some timeout" for the
+ * original operation to arrive. If not, it should send EAGAIN to the
+ * INTERRUPT operation, and the kernel should requeue the INTERRUPT.
+ *
+ * In this test, we'll pretend that the INTERRUPT arrives too soon, gets
+ * EAGAINed, then the kernel requeues it, and the second time around it
+ * successfully interrupts the original
+ */
+/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
+TEST_F(Intr, too_soon)
+{
+ Sequence seq;
+ pthread_t self;
+ uint64_t mkdir_unique;
+
+ self = pthread_self();
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_mkdir(&mkdir_unique);
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([&](auto in) {
+ return (in.header.opcode == FUSE_INTERRUPT &&
+ in.body.interrupt.unique == mkdir_unique);
+ }, Eq(true)),
+ _)
+ ).InSequence(seq)
+ .WillOnce(Invoke(ReturnErrno(EAGAIN)));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([&](auto in) {
+ return (in.header.opcode == FUSE_INTERRUPT &&
+ in.body.interrupt.unique == mkdir_unique);
+ }, Eq(true)),
+ _)
+ ).InSequence(seq)
+ .WillOnce(Invoke([&](auto in __unused, auto &out __unused) {
+ std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
+ out0->header.error = -EINTR;
+ out0->header.unique = mkdir_unique;
+ out0->header.len = sizeof(out0->header);
+ out.push_back(std::move(out0));
+ }));
+
+ setup_interruptor(self);
+ ASSERT_EQ(-1, mkdir(FULLDIRPATH0, MODE));
+ EXPECT_EQ(EINTR, errno);
+}
+
+
+// TODO: add a test where write returns EWOULDBLOCK
diff --git a/tests/sys/fs/fusefs/io.cc b/tests/sys/fs/fusefs/io.cc
new file mode 100644
index 000000000000..ced291836da0
--- /dev/null
+++ b/tests/sys/fs/fusefs/io.cc
@@ -0,0 +1,609 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+}
+
+#include <iomanip>
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+/*
+ * For testing I/O like fsx does, but deterministically and without a real
+ * underlying file system
+ */
+
+using namespace testing;
+
+const char FULLPATH[] = "mountpoint/some_file.txt";
+const char RELPATH[] = "some_file.txt";
+const uint64_t ino = 42;
+
+static void compare(const void *tbuf, const void *controlbuf, off_t baseofs,
+ ssize_t size)
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ if (((const char*)tbuf)[i] != ((const char*)controlbuf)[i]) {
+ off_t ofs = baseofs + i;
+ FAIL() << "miscompare at offset "
+ << std::hex
+ << std::showbase
+ << ofs
+ << ". expected = "
+ << std::setw(2)
+ << (unsigned)((const uint8_t*)controlbuf)[i]
+ << " got = "
+ << (unsigned)((const uint8_t*)tbuf)[i];
+ }
+ }
+}
+
+typedef tuple<bool, uint32_t, cache_mode, uint32_t> IoParam;
+
+class Io: public FuseTest, public WithParamInterface<IoParam> {
+public:
+int m_backing_fd, m_control_fd, m_test_fd;
+off_t m_filesize;
+bool m_direct_io;
+
+Io(): m_backing_fd(-1), m_control_fd(-1), m_test_fd(-1), m_filesize(0),
+ m_direct_io(false) {};
+
+void SetUp()
+{
+ m_backing_fd = open("backing_file", O_RDWR | O_CREAT | O_TRUNC, 0644);
+ if (m_backing_fd < 0)
+ FAIL() << strerror(errno);
+ m_control_fd = open("control", O_RDWR | O_CREAT | O_TRUNC, 0644);
+ if (m_control_fd < 0)
+ FAIL() << strerror(errno);
+ srandom(22'9'1982); // Seed with my birthday
+
+ if (get<0>(GetParam()))
+ m_init_flags |= FUSE_ASYNC_READ;
+ m_maxwrite = get<1>(GetParam());
+ switch (get<2>(GetParam())) {
+ case Uncached:
+ m_direct_io = true;
+ break;
+ case WritebackAsync:
+ m_async = true;
+ /* FALLTHROUGH */
+ case Writeback:
+ m_init_flags |= FUSE_WRITEBACK_CACHE;
+ /* FALLTHROUGH */
+ case Writethrough:
+ break;
+ default:
+ FAIL() << "Unknown cache mode";
+ }
+ m_kernel_minor_version = get<3>(GetParam());
+ m_noatime = true; // To prevent SETATTR for atime on close
+
+ FuseTest::SetUp();
+ if (IsSkipped())
+ return;
+
+ if (verbosity > 0) {
+ printf("Test Parameters: init_flags=%#x maxwrite=%#x "
+ "%sasync cache=%s kernel_minor_version=%d\n",
+ m_init_flags, m_maxwrite, m_async? "" : "no",
+ cache_mode_to_s(get<2>(GetParam())),
+ m_kernel_minor_version);
+ }
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ expect_open(ino, m_direct_io ? FOPEN_DIRECT_IO : 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_WRITE &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto& out) {
+ const char *buf = (const char*)in.body.bytes +
+ sizeof(struct fuse_write_in);
+ ssize_t isize = in.body.write.size;
+ off_t iofs = in.body.write.offset;
+
+ assert((size_t)isize <= sizeof(in.body.bytes) -
+ sizeof(struct fuse_write_in));
+ ASSERT_EQ(isize, pwrite(m_backing_fd, buf, isize, iofs))
+ << strerror(errno);
+ SET_OUT_HEADER_LEN(out, write);
+ out.body.write.size = isize;
+ })));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READ &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto& out) {
+ ssize_t isize = in.body.write.size;
+ off_t iofs = in.body.write.offset;
+ void *buf = out.body.bytes;
+ ssize_t osize;
+
+ assert((size_t)isize <= sizeof(out.body.bytes));
+ osize = pread(m_backing_fd, buf, isize, iofs);
+ ASSERT_LE(0, osize) << strerror(errno);
+ out.header.len = sizeof(struct fuse_out_header) + osize;
+ })));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino &&
+ (in.body.setattr.valid & FATTR_SIZE));
+
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto& out) {
+ ASSERT_EQ(0, ftruncate(m_backing_fd, in.body.setattr.size))
+ << strerror(errno);
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino;
+ out.body.attr.attr.mode = S_IFREG | 0755;
+ out.body.attr.attr.size = in.body.setattr.size;
+ out.body.attr.attr_valid = UINT64_MAX;
+ })));
+ /* Any test that close()s will send FUSE_FLUSH and FUSE_RELEASE */
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_FLUSH &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnErrno(0)));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_RELEASE &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnErrno(0)));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_COPY_FILE_RANGE &&
+ in.header.nodeid == ino &&
+ in.body.copy_file_range.nodeid_out == ino &&
+ in.body.copy_file_range.flags == 0);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto& out) {
+ off_t off_in = in.body.copy_file_range.off_in;
+ off_t off_out = in.body.copy_file_range.off_out;
+ ASSERT_EQ((ssize_t)in.body.copy_file_range.len,
+ copy_file_range(m_backing_fd, &off_in, m_backing_fd,
+ &off_out, in.body.copy_file_range.len, 0));
+ SET_OUT_HEADER_LEN(out, write);
+ out.body.write.size = in.body.copy_file_range.len;
+ })));
+ /* Claim that we don't support FUSE_LSEEK */
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_LSEEK);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnErrno(ENOSYS)));
+
+ m_test_fd = open(FULLPATH, O_RDWR );
+ EXPECT_LE(0, m_test_fd) << strerror(errno);
+}
+
+void TearDown()
+{
+ if (m_test_fd >= 0)
+ close(m_test_fd);
+ if (m_backing_fd >= 0)
+ close(m_backing_fd);
+ if (m_control_fd >= 0)
+ close(m_control_fd);
+ FuseTest::TearDown();
+ leak(m_test_fd);
+}
+
+void do_closeopen()
+{
+ ASSERT_EQ(0, close(m_test_fd)) << strerror(errno);
+ m_test_fd = open("backing_file", O_RDWR);
+ ASSERT_LE(0, m_test_fd) << strerror(errno);
+
+ ASSERT_EQ(0, close(m_control_fd)) << strerror(errno);
+ m_control_fd = open("control", O_RDWR);
+ ASSERT_LE(0, m_control_fd) << strerror(errno);
+}
+
+void do_copy_file_range(off_t off_in, off_t off_out, size_t size)
+{
+ ssize_t r;
+ off_t test_off_in = off_in;
+ off_t test_off_out = off_out;
+ off_t test_size = size;
+ off_t control_off_in = off_in;
+ off_t control_off_out = off_out;
+ off_t control_size = size;
+
+ while (test_size > 0) {
+ r = copy_file_range(m_test_fd, &test_off_in, m_test_fd,
+ &test_off_out, test_size, 0);
+ ASSERT_GT(r, 0) << strerror(errno);
+ test_size -= r;
+ }
+ while (control_size > 0) {
+ r = copy_file_range(m_control_fd, &control_off_in, m_control_fd,
+ &control_off_out, control_size, 0);
+ ASSERT_GT(r, 0) << strerror(errno);
+ control_size -= r;
+ }
+ m_filesize = std::max(m_filesize, off_out + (off_t)size);
+}
+
+void do_ftruncate(off_t offs)
+{
+ ASSERT_EQ(0, ftruncate(m_test_fd, offs)) << strerror(errno);
+ ASSERT_EQ(0, ftruncate(m_control_fd, offs)) << strerror(errno);
+ m_filesize = offs;
+}
+
+void do_mapread(off_t offs, ssize_t size)
+{
+ char *control_buf;
+ void *p;
+ off_t pg_offset, page_mask;
+ size_t map_size;
+
+ page_mask = getpagesize() - 1;
+ pg_offset = offs & page_mask;
+ map_size = pg_offset + size;
+
+ p = mmap(NULL, map_size, PROT_READ, MAP_FILE | MAP_SHARED, m_test_fd,
+ offs - pg_offset);
+ ASSERT_NE(p, MAP_FAILED) << strerror(errno);
+
+ control_buf = new char[size];
+
+ ASSERT_EQ(size, pread(m_control_fd, control_buf, size, offs))
+ << strerror(errno);
+
+ compare((void*)((char*)p + pg_offset), control_buf, offs, size);
+
+ ASSERT_EQ(0, munmap(p, map_size)) << strerror(errno);
+ delete[] control_buf;
+}
+
+void do_read(off_t offs, ssize_t size)
+{
+ char *test_buf, *control_buf;
+ ssize_t r;
+
+ test_buf = new char[size];
+ control_buf = new char[size];
+
+ errno = 0;
+ r = pread(m_test_fd, test_buf, size, offs);
+ ASSERT_NE(-1, r) << strerror(errno);
+ ASSERT_EQ(size, r) << "unexpected short read";
+ r = pread(m_control_fd, control_buf, size, offs);
+ ASSERT_NE(-1, r) << strerror(errno);
+ ASSERT_EQ(size, r) << "unexpected short read";
+
+ compare(test_buf, control_buf, offs, size);
+
+ delete[] control_buf;
+ delete[] test_buf;
+}
+
+void do_mapwrite(off_t offs, ssize_t size)
+{
+ char *buf;
+ void *p;
+ off_t pg_offset, page_mask;
+ size_t map_size;
+ long i;
+
+ page_mask = getpagesize() - 1;
+ pg_offset = offs & page_mask;
+ map_size = pg_offset + size;
+
+ buf = new char[size];
+ for (i=0; i < size; i++)
+ buf[i] = random();
+
+ if (offs + size > m_filesize) {
+ /*
+ * Must manually extend. vm_mmap_vnode will not implicitly
+ * extend a vnode
+ */
+ do_ftruncate(offs + size);
+ }
+
+ p = mmap(NULL, map_size, PROT_READ | PROT_WRITE,
+ MAP_FILE | MAP_SHARED, m_test_fd, offs - pg_offset);
+ ASSERT_NE(p, MAP_FAILED) << strerror(errno);
+
+ bcopy(buf, (char*)p + pg_offset, size);
+ ASSERT_EQ(size, pwrite(m_control_fd, buf, size, offs))
+ << strerror(errno);
+
+ delete[] buf;
+ ASSERT_EQ(0, munmap(p, map_size)) << strerror(errno);
+}
+
+void do_write(off_t offs, ssize_t size)
+{
+ char *buf;
+ long i;
+
+ buf = new char[size];
+ for (i=0; i < size; i++)
+ buf[i] = random();
+
+ ASSERT_EQ(size, pwrite(m_test_fd, buf, size, offs ))
+ << strerror(errno);
+ ASSERT_EQ(size, pwrite(m_control_fd, buf, size, offs))
+ << strerror(errno);
+ m_filesize = std::max(m_filesize, offs + size);
+
+ delete[] buf;
+}
+
+};
+
+class IoCacheable: public Io {
+public:
+virtual void SetUp() {
+ Io::SetUp();
+}
+};
+
+class IoCopyFileRange: public Io {
+public:
+virtual void SetUp() {
+ Io::SetUp();
+}
+};
+
+/*
+ * Extend a file with dirty data in the last page of the last block.
+ *
+ * fsx -WR -P /tmp -S8 -N3 fsx.bin
+ */
+TEST_P(Io, extend_from_dirty_page)
+{
+ off_t wofs = 0x21a0;
+ ssize_t wsize = 0xf0a8;
+ off_t rofs = 0xb284;
+ ssize_t rsize = 0x9b22;
+ off_t truncsize = 0x28702;
+
+ do_write(wofs, wsize);
+ do_ftruncate(truncsize);
+ do_read(rofs, rsize);
+}
+
+/*
+ * mapwrite into a newly extended part of a file.
+ *
+ * fsx -c 100 -i 100 -l 524288 -o 131072 -N5 -P /tmp -S19 fsx.bin
+ */
+TEST_P(IoCacheable, extend_by_mapwrite)
+{
+ do_mapwrite(0x29a3a, 0x849e); /* [0x29a3a, 0x31ed7] */
+ do_mapwrite(0x3c7d8, 0x3994); /* [0x3c7d8, 0x4016b] */
+ do_read(0x30c16, 0xf556); /* [0x30c16, 0x4016b] */
+}
+
+/*
+ * When writing the last page of a file, it must be written synchronously.
+ * Otherwise the cached page can become invalid by a subsequent extend
+ * operation.
+ *
+ * fsx -WR -P /tmp -S642 -N3 fsx.bin
+ */
+TEST_P(Io, last_page)
+{
+ do_write(0x1134f, 0xcc77); /* [0x1134f, 0x1dfc5] */
+ do_write(0x2096a, 0xdfa7); /* [0x2096a, 0x2e910] */
+ do_read(0x1a3aa, 0xb5b7); /* [0x1a3aa, 0x25960] */
+}
+
+/*
+ * Read a hole using mmap
+ *
+ * fsx -c 100 -i 100 -l 524288 -o 131072 -N11 -P /tmp -S14 fsx.bin
+ */
+TEST_P(IoCacheable, mapread_hole)
+{
+ do_write(0xf205, 0x123b7); /* [0xf205, 0x215bb] */
+ do_mapread(0x2f4c, 0xeeea); /* [0x2f4c, 0x11e35] */
+}
+
+/*
+ * Read a hole from a block that contains some cached data.
+ *
+ * fsx -WR -P /tmp -S55 fsx.bin
+ */
+TEST_P(Io, read_hole_from_cached_block)
+{
+ off_t wofs = 0x160c5;
+ ssize_t wsize = 0xa996;
+ off_t rofs = 0x472e;
+ ssize_t rsize = 0xd8d5;
+
+ do_write(wofs, wsize);
+ do_read(rofs, rsize);
+}
+
+/*
+ * Truncating a file into a dirty buffer should not causing anything untoward
+ * to happen when that buffer is eventually flushed.
+ *
+ * fsx -WR -P /tmp -S839 -d -N6 fsx.bin
+ */
+TEST_P(Io, truncate_into_dirty_buffer)
+{
+ off_t wofs0 = 0x3bad7;
+ ssize_t wsize0 = 0x4529;
+ off_t wofs1 = 0xc30d;
+ ssize_t wsize1 = 0x5f77;
+ off_t truncsize0 = 0x10916;
+ off_t rofs = 0xdf17;
+ ssize_t rsize = 0x29ff;
+ off_t truncsize1 = 0x152b4;
+
+ do_write(wofs0, wsize0);
+ do_write(wofs1, wsize1);
+ do_ftruncate(truncsize0);
+ do_read(rofs, rsize);
+ do_ftruncate(truncsize1);
+ close(m_test_fd);
+}
+
+/*
+ * Truncating a file into a dirty buffer should not causing anything untoward
+ * to happen when that buffer is eventually flushed, even when the buffer's
+ * dirty_off is > 0.
+ *
+ * Based on this command with a few steps removed:
+ * fsx -WR -P /tmp -S677 -d -N8 fsx.bin
+ */
+TEST_P(Io, truncate_into_dirty_buffer2)
+{
+ off_t truncsize0 = 0x344f3;
+ off_t wofs = 0x2790c;
+ ssize_t wsize = 0xd86a;
+ off_t truncsize1 = 0x2de38;
+ off_t rofs2 = 0x1fd7a;
+ ssize_t rsize2 = 0xc594;
+ off_t truncsize2 = 0x31e71;
+
+ /* Sets the file size to something larger than the next write */
+ do_ftruncate(truncsize0);
+ /*
+ * Creates a dirty buffer. The part in lbn 2 doesn't flush
+ * synchronously.
+ */
+ do_write(wofs, wsize);
+ /* Truncates part of the dirty buffer created in step 2 */
+ do_ftruncate(truncsize1);
+ /* XXX ?I don't know why this is necessary? */
+ do_read(rofs2, rsize2);
+ /* Truncates the dirty buffer */
+ do_ftruncate(truncsize2);
+ close(m_test_fd);
+}
+
+/*
+ * Regression test for a bug introduced in r348931
+ *
+ * Sequence of operations:
+ * 1) The first write reads lbn so it can modify it
+ * 2) The first write flushes lbn 3 immediately because it's the end of file
+ * 3) The first write then flushes lbn 4 because it's the end of the file
+ * 4) The second write modifies the cached versions of lbn 3 and 4
+ * 5) The third write's getblkx invalidates lbn 4's B_CACHE because it's
+ * extending the buffer. Then it flushes lbn 4 because B_DELWRI was set but
+ * B_CACHE was clear.
+ * 6) fuse_write_biobackend erroneously called vfs_bio_clrbuf, putting the
+ * buffer into a weird write-only state. All read operations would return
+ * 0. Writes were apparently still processed, because the buffer's contents
+ * were correct when examined in a core dump.
+ * 7) The third write reads lbn 4 because cache is clear
+ * 9) uiomove dutifully copies new data into the buffer
+ * 10) The buffer's dirty is flushed to lbn 4
+ * 11) The read returns all zeros because of step 6.
+ *
+ * Based on:
+ * fsx -WR -l 524388 -o 131072 -P /tmp -S6456 -q fsx.bin
+ */
+TEST_P(Io, resize_a_valid_buffer_while_extending)
+{
+ do_write(0x36ee6, 0x14530); /* [0x36ee6, 0x4b415] */
+ do_write(0x33256, 0x1507c); /* [0x33256, 0x482d1] */
+ do_write(0x4c03d, 0x175c); /* [0x4c03d, 0x4d798] */
+ do_read(0x3599c, 0xe277); /* [0x3599c, 0x43c12] */
+ close(m_test_fd);
+}
+
+/*
+ * mmap of a suitable region could trigger a panic. I'm not sure what
+ * combination of size and offset counts as "suitable". Regression test for
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=276191
+ */
+TEST_P(IoCacheable, vnode_pager_generic_putpage_clean_block_at_eof)
+{
+ do_mapwrite(0x3b4e0, 0x1bbc3);
+}
+
+/*
+ * A copy_file_range that follows an mmap write to the input area needs to
+ * flush the mmap buffer first.
+ */
+TEST_P(IoCopyFileRange, copy_file_range_from_mapped_write)
+{
+ do_mapwrite(0, 0x1000);
+ do_copy_file_range(0, 0x1000, 0x1000);
+ do_read(0x1000, 0x1000);
+}
+
+
+INSTANTIATE_TEST_SUITE_P(Io, Io,
+ Combine(Bool(), /* async read */
+ Values(0x1000, 0x10000, 0x20000), /* m_maxwrite */
+ Values(Uncached, Writethrough, Writeback, WritebackAsync),
+ Values(28) /* kernel_minor_vers */
+ )
+);
+
+INSTANTIATE_TEST_SUITE_P(Io, IoCacheable,
+ Combine(Bool(), /* async read */
+ Values(0x1000, 0x10000, 0x20000), /* m_maxwrite */
+ Values(Writethrough, Writeback, WritebackAsync),
+ Values(28) /* kernel_minor_vers */
+ )
+);
+
+INSTANTIATE_TEST_SUITE_P(Io, IoCopyFileRange,
+ Combine(Values(true), /* async read */
+ Values(0x10000), /* m_maxwrite */
+ Values(Writethrough, Writeback, WritebackAsync),
+ Values(27, 28) /* kernel_minor_vers */
+ )
+);
diff --git a/tests/sys/fs/fusefs/last_local_modify.cc b/tests/sys/fs/fusefs/last_local_modify.cc
new file mode 100644
index 000000000000..5fcd3c36c892
--- /dev/null
+++ b/tests/sys/fs/fusefs/last_local_modify.cc
@@ -0,0 +1,513 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2021 Alan Somers
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <pthread.h>
+#include <semaphore.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+/*
+ * "Last Local Modify" bugs
+ *
+ * This file tests a class of race conditions caused by one thread fetching a
+ * file's size with FUSE_LOOKUP while another thread simultaneously modifies it
+ * with FUSE_SETATTR, FUSE_WRITE, FUSE_COPY_FILE_RANGE or similar. It's
+ * possible for the second thread to start later yet finish first. If that
+ * happens, the first thread must not override the size set by the second
+ * thread.
+ *
+ * FUSE_GETATTR is not vulnerable to the same race, because it is always called
+ * with the vnode lock held.
+ *
+ * A few other operations like FUSE_LINK can also trigger the same race but
+ * with the file's ctime instead of size. However, the consequences of an
+ * incorrect ctime are much less disastrous than an incorrect size, so fusefs
+ * does not attempt to prevent such races.
+ */
+
+enum Mutator {
+ VOP_ALLOCATE,
+ VOP_COPY_FILE_RANGE,
+ VOP_SETATTR,
+ VOP_WRITE,
+};
+
+/*
+ * Translate a poll method's string representation to the enum value.
+ * Using strings with ::testing::Values gives better output with
+ * --gtest_list_tests
+ */
+enum Mutator writer_from_str(const char* s) {
+ if (0 == strcmp("VOP_ALLOCATE", s))
+ return VOP_ALLOCATE;
+ else if (0 == strcmp("VOP_COPY_FILE_RANGE", s))
+ return VOP_COPY_FILE_RANGE;
+ else if (0 == strcmp("VOP_SETATTR", s))
+ return VOP_SETATTR;
+ else
+ return VOP_WRITE;
+}
+
+uint32_t fuse_op_from_mutator(enum Mutator mutator) {
+ switch(mutator) {
+ case VOP_ALLOCATE:
+ return(FUSE_FALLOCATE);
+ case VOP_COPY_FILE_RANGE:
+ return(FUSE_COPY_FILE_RANGE);
+ case VOP_SETATTR:
+ return(FUSE_SETATTR);
+ case VOP_WRITE:
+ return(FUSE_WRITE);
+ }
+}
+
+class LastLocalModify: public FuseTest, public WithParamInterface<const char*> {
+public:
+virtual void SetUp() {
+ m_init_flags = FUSE_EXPORT_SUPPORT;
+
+ FuseTest::SetUp();
+}
+};
+
+static void* allocate_th(void* arg) {
+ int fd;
+ ssize_t r;
+ sem_t *sem = (sem_t*) arg;
+
+ if (sem)
+ sem_wait(sem);
+
+ fd = open("mountpoint/some_file.txt", O_RDWR);
+ if (fd < 0)
+ return (void*)(intptr_t)errno;
+
+ r = posix_fallocate(fd, 0, 15);
+ LastLocalModify::leak(fd);
+ if (r >= 0)
+ return 0;
+ else
+ return (void*)(intptr_t)errno;
+}
+
+static void* copy_file_range_th(void* arg) {
+ ssize_t r;
+ int fd;
+ sem_t *sem = (sem_t*) arg;
+ off_t off_in = 0;
+ off_t off_out = 10;
+ ssize_t len = 5;
+
+ if (sem)
+ sem_wait(sem);
+ fd = open("mountpoint/some_file.txt", O_RDWR);
+ if (fd < 0)
+ return (void*)(intptr_t)errno;
+
+ r = copy_file_range(fd, &off_in, fd, &off_out, len, 0);
+ if (r >= 0) {
+ LastLocalModify::leak(fd);
+ return 0;
+ } else
+ return (void*)(intptr_t)errno;
+}
+
+static void* setattr_th(void* arg) {
+ int fd;
+ ssize_t r;
+ sem_t *sem = (sem_t*) arg;
+
+ if (sem)
+ sem_wait(sem);
+
+ fd = open("mountpoint/some_file.txt", O_RDWR);
+ if (fd < 0)
+ return (void*)(intptr_t)errno;
+
+ r = ftruncate(fd, 15);
+ LastLocalModify::leak(fd);
+ if (r >= 0)
+ return 0;
+ else
+ return (void*)(intptr_t)errno;
+}
+
+static void* write_th(void* arg) {
+ ssize_t r;
+ int fd;
+ sem_t *sem = (sem_t*) arg;
+ const char BUF[] = "abcdefghijklmn";
+
+ if (sem)
+ sem_wait(sem);
+ fd = open("mountpoint/some_file.txt", O_RDWR);
+ if (fd < 0)
+ return (void*)(intptr_t)errno;
+
+ r = write(fd, BUF, sizeof(BUF));
+ if (r >= 0) {
+ LastLocalModify::leak(fd);
+ return 0;
+ } else
+ return (void*)(intptr_t)errno;
+}
+
+/*
+ * VOP_LOOKUP should discard attributes returned by the server if they were
+ * modified by another VOP while the VOP_LOOKUP was in progress.
+ *
+ * Sequence of operations:
+ * * Thread 1 calls a mutator like ftruncate, which acquires the vnode lock
+ * exclusively.
+ * * Thread 2 calls stat, which does VOP_LOOKUP, which sends FUSE_LOOKUP to the
+ * server. The server replies with the old file length. Thread 2 blocks
+ * waiting for the vnode lock.
+ * * Thread 1 sends the mutator operation like FUSE_SETATTR that changes the
+ * file's size and updates the attribute cache. Then it releases the vnode
+ * lock.
+ * * Thread 2 acquires the vnode lock. At this point it must not add the
+ * now-stale file size to the attribute cache.
+ *
+ * Regression test for https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=259071
+ */
+TEST_P(LastLocalModify, lookup)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ Sequence seq;
+ uint64_t ino = 3;
+ uint64_t mutator_unique;
+ const uint64_t oldsize = 10;
+ const uint64_t newsize = 15;
+ pthread_t th0;
+ void *thr0_value;
+ struct stat sb;
+ static sem_t sem;
+ Mutator mutator;
+ uint32_t mutator_op;
+ size_t mutator_size;
+
+ mutator = writer_from_str(GetParam());
+ mutator_op = fuse_op_from_mutator(mutator);
+
+ ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .InSequence(seq)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ /* Called by the mutator, caches attributes but not entries */
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.size = oldsize;
+ out.body.entry.attr_valid_nsec = NAP_NS / 2;
+ out.body.entry.attr.ino = ino;
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ })));
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == mutator_op &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).InSequence(seq)
+ .WillOnce(Invoke([&](auto in, auto &out __unused) {
+ /*
+ * The mutator changes the file size, but in order to simulate
+ * a race, don't reply. Instead, just save the unique for
+ * later.
+ */
+ mutator_unique = in.header.unique;
+ switch(mutator) {
+ case VOP_WRITE:
+ mutator_size = in.body.write.size;
+ break;
+ case VOP_COPY_FILE_RANGE:
+ mutator_size = in.body.copy_file_range.len;
+ break;
+ default:
+ break;
+ }
+ /* Allow the lookup thread to proceed */
+ sem_post(&sem);
+ }));
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .InSequence(seq)
+ .WillOnce(Invoke([&](auto in __unused, auto& out) {
+ std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
+ std::unique_ptr<mockfs_buf_out> out1(new mockfs_buf_out);
+
+ /* First complete the lookup request, returning the old size */
+ out0->header.unique = in.header.unique;
+ SET_OUT_HEADER_LEN(*out0, entry);
+ out0->body.entry.attr.mode = S_IFREG | 0644;
+ out0->body.entry.nodeid = ino;
+ out0->body.entry.attr.ino = ino;
+ out0->body.entry.entry_valid = UINT64_MAX;
+ out0->body.entry.attr_valid = UINT64_MAX;
+ out0->body.entry.attr.size = oldsize;
+ out.push_back(std::move(out0));
+
+ /* Then, respond to the mutator request */
+ out1->header.unique = mutator_unique;
+ switch(mutator) {
+ case VOP_ALLOCATE:
+ out1->header.error = 0;
+ out1->header.len = sizeof(out1->header);
+ break;
+ case VOP_COPY_FILE_RANGE:
+ SET_OUT_HEADER_LEN(*out1, write);
+ out1->body.write.size = mutator_size;
+ break;
+ case VOP_SETATTR:
+ SET_OUT_HEADER_LEN(*out1, attr);
+ out1->body.attr.attr.ino = ino;
+ out1->body.attr.attr.mode = S_IFREG | 0644;
+ out1->body.attr.attr.size = newsize; // Changed size
+ out1->body.attr.attr_valid = UINT64_MAX;
+ break;
+ case VOP_WRITE:
+ SET_OUT_HEADER_LEN(*out1, write);
+ out1->body.write.size = mutator_size;
+ break;
+ }
+ out.push_back(std::move(out1));
+ }));
+
+ /* Start the mutator thread */
+ switch(mutator) {
+ case VOP_ALLOCATE:
+ ASSERT_EQ(0, pthread_create(&th0, NULL, allocate_th,
+ NULL)) << strerror(errno);
+ break;
+ case VOP_COPY_FILE_RANGE:
+ ASSERT_EQ(0, pthread_create(&th0, NULL, copy_file_range_th,
+ NULL)) << strerror(errno);
+ break;
+ case VOP_SETATTR:
+ ASSERT_EQ(0, pthread_create(&th0, NULL, setattr_th, NULL))
+ << strerror(errno);
+ break;
+ case VOP_WRITE:
+ ASSERT_EQ(0, pthread_create(&th0, NULL, write_th, NULL))
+ << strerror(errno);
+ break;
+ }
+
+
+ /* Wait for FUSE_SETATTR to be sent */
+ sem_wait(&sem);
+
+ /* Lookup again, which will race with setattr */
+ ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno);
+ ASSERT_EQ((off_t)newsize, sb.st_size);
+
+ /* ftruncate should've completed without error */
+ pthread_join(th0, &thr0_value);
+ EXPECT_EQ(0, (intptr_t)thr0_value);
+}
+
+/*
+ * VFS_VGET should discard attributes returned by the server if they were
+ * modified by another VOP while the VFS_VGET was in progress.
+ *
+ * Sequence of operations:
+ * * Thread 1 calls fhstat, entering VFS_VGET, and issues FUSE_LOOKUP
+ * * Thread 2 calls a mutator like ftruncate, which acquires the vnode lock
+ * exclusively and issues a FUSE op like FUSE_SETATTR.
+ * * Thread 1's FUSE_LOOKUP returns with the old size, but the thread blocks
+ * waiting for the vnode lock.
+ * * Thread 2's FUSE op returns, and that thread sets the file's new size
+ * in the attribute cache. Finally it releases the vnode lock.
+ * * The vnode lock acquired, thread 1 must not overwrite the attr cache's size
+ * with the old value.
+ *
+ * Regression test for https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=259071
+ */
+TEST_P(LastLocalModify, vfs_vget)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ Sequence seq;
+ uint64_t ino = 3;
+ uint64_t lookup_unique;
+ const uint64_t oldsize = 10;
+ const uint64_t newsize = 15;
+ pthread_t th0;
+ void *thr0_value;
+ struct stat sb;
+ static sem_t sem;
+ fhandle_t fhp;
+ Mutator mutator;
+ uint32_t mutator_op;
+
+ if (geteuid() != 0)
+ GTEST_SKIP() << "This test requires a privileged user";
+
+ mutator = writer_from_str(GetParam());
+ mutator_op = fuse_op_from_mutator(mutator);
+
+ ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .Times(1)
+ .InSequence(seq)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out)
+ {
+ /* Called by getfh, caches attributes but not entries */
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.size = oldsize;
+ out.body.entry.attr_valid_nsec = NAP_NS / 2;
+ out.body.entry.attr.ino = ino;
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ })));
+ EXPECT_LOOKUP(ino, ".")
+ .InSequence(seq)
+ .WillOnce(Invoke([&](auto in, auto &out __unused) {
+ /* Called by fhstat. Block to simulate a race */
+ lookup_unique = in.header.unique;
+ sem_post(&sem);
+ }));
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .Times(1)
+ .InSequence(seq)
+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out)
+ {
+ /* Called by VOP_SETATTR, caches attributes but not entries */
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.size = oldsize;
+ out.body.entry.attr_valid_nsec = NAP_NS / 2;
+ out.body.entry.attr.ino = ino;
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ })));
+
+ /* Called by the mutator thread */
+ expect_open(ino, 0, 1);
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == mutator_op &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).InSequence(seq)
+ .WillOnce(Invoke([&](auto in __unused, auto& out) {
+ std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
+ std::unique_ptr<mockfs_buf_out> out1(new mockfs_buf_out);
+
+ /* First complete the lookup request, returning the old size */
+ out0->header.unique = lookup_unique;
+ SET_OUT_HEADER_LEN(*out0, entry);
+ out0->body.entry.attr.mode = S_IFREG | 0644;
+ out0->body.entry.nodeid = ino;
+ out0->body.entry.attr.ino = ino;
+ out0->body.entry.entry_valid = UINT64_MAX;
+ out0->body.entry.attr_valid = UINT64_MAX;
+ out0->body.entry.attr.size = oldsize;
+ out.push_back(std::move(out0));
+
+ /* Then, respond to the mutator request */
+ out1->header.unique = in.header.unique;
+ switch(mutator) {
+ case VOP_ALLOCATE:
+ out1->header.error = 0;
+ out1->header.len = sizeof(out1->header);
+ break;
+ case VOP_COPY_FILE_RANGE:
+ SET_OUT_HEADER_LEN(*out1, write);
+ out1->body.write.size = in.body.copy_file_range.len;
+ break;
+ case VOP_SETATTR:
+ SET_OUT_HEADER_LEN(*out1, attr);
+ out1->body.attr.attr.ino = ino;
+ out1->body.attr.attr.mode = S_IFREG | 0644;
+ out1->body.attr.attr.size = newsize; // Changed size
+ out1->body.attr.attr_valid = UINT64_MAX;
+ break;
+ case VOP_WRITE:
+ SET_OUT_HEADER_LEN(*out1, write);
+ out1->body.write.size = in.body.write.size;
+ break;
+ }
+ out.push_back(std::move(out1));
+ }));
+
+ /* First get a file handle */
+ ASSERT_EQ(0, getfh(FULLPATH, &fhp)) << strerror(errno);
+
+ /* Start the mutator thread */
+ switch(mutator) {
+ case VOP_ALLOCATE:
+ ASSERT_EQ(0, pthread_create(&th0, NULL, allocate_th,
+ (void*)&sem)) << strerror(errno);
+ break;
+ case VOP_COPY_FILE_RANGE:
+ ASSERT_EQ(0, pthread_create(&th0, NULL, copy_file_range_th,
+ (void*)&sem)) << strerror(errno);
+ break;
+ case VOP_SETATTR:
+ ASSERT_EQ(0, pthread_create(&th0, NULL, setattr_th,
+ (void*)&sem)) << strerror(errno);
+ break;
+ case VOP_WRITE:
+ ASSERT_EQ(0, pthread_create(&th0, NULL, write_th, (void*)&sem))
+ << strerror(errno);
+ break;
+ }
+
+ /* Lookup again, which will race with setattr */
+ ASSERT_EQ(0, fhstat(&fhp, &sb)) << strerror(errno);
+
+ ASSERT_EQ((off_t)newsize, sb.st_size);
+
+ /* mutator should've completed without error */
+ pthread_join(th0, &thr0_value);
+ EXPECT_EQ(0, (intptr_t)thr0_value);
+}
+
+
+INSTANTIATE_TEST_SUITE_P(LLM, LastLocalModify,
+ Values(
+ "VOP_ALLOCATE",
+ "VOP_COPY_FILE_RANGE",
+ "VOP_SETATTR",
+ "VOP_WRITE"
+ )
+);
diff --git a/tests/sys/fs/fusefs/link.cc b/tests/sys/fs/fusefs/link.cc
new file mode 100644
index 000000000000..ac77347cd03b
--- /dev/null
+++ b/tests/sys/fs/fusefs/link.cc
@@ -0,0 +1,280 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <unistd.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class Link: public FuseTest {
+public:
+void expect_link(uint64_t ino, const char *relpath, mode_t mode, uint32_t nlink)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *name = (const char*)in.body.bytes
+ + sizeof(struct fuse_link_in);
+ return (in.header.opcode == FUSE_LINK &&
+ in.body.link.oldnodeid == ino &&
+ (0 == strcmp(name, relpath)));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.mode = mode;
+ out.body.entry.attr.nlink = nlink;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+}
+
+void expect_lookup(const char *relpath, uint64_t ino)
+{
+ FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, 1);
+}
+};
+
+class Link_7_8: public FuseTest {
+public:
+virtual void SetUp() {
+ m_kernel_minor_version = 8;
+ FuseTest::SetUp();
+}
+
+void expect_link(uint64_t ino, const char *relpath, mode_t mode, uint32_t nlink)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *name = (const char*)in.body.bytes
+ + sizeof(struct fuse_link_in);
+ return (in.header.opcode == FUSE_LINK &&
+ in.body.link.oldnodeid == ino &&
+ (0 == strcmp(name, relpath)));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry_7_8);
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.mode = mode;
+ out.body.entry.attr.nlink = nlink;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+}
+
+void expect_lookup(const char *relpath, uint64_t ino)
+{
+ FuseTest::expect_lookup_7_8(relpath, ino, S_IFREG | 0644, 0, 1);
+}
+};
+
+/*
+ * A successful link should clear the parent directory's attribute cache,
+ * because the fuse daemon should update its mtime and ctime
+ */
+TEST_F(Link, clear_attr_cache)
+{
+ const char FULLPATH[] = "mountpoint/src";
+ const char RELPATH[] = "src";
+ const char FULLDST[] = "mountpoint/dst";
+ const char RELDST[] = "dst";
+ const uint64_t ino = 42;
+ mode_t mode = S_IFREG | 0644;
+ struct stat sb;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == FUSE_ROOT_ID);
+ }, Eq(true)),
+ _)
+ ).Times(2)
+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = FUSE_ROOT_ID;
+ out.body.attr.attr.mode = S_IFDIR | 0755;
+ out.body.attr.attr_valid = UINT64_MAX;
+ })));
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDST)
+
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.nlink = 1;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+ expect_link(ino, RELPATH, mode, 2);
+
+ EXPECT_EQ(0, stat("mountpoint", &sb)) << strerror(errno);
+ EXPECT_EQ(0, link(FULLDST, FULLPATH)) << strerror(errno);
+ EXPECT_EQ(0, stat("mountpoint", &sb)) << strerror(errno);
+}
+
+TEST_F(Link, emlink)
+{
+ const char FULLPATH[] = "mountpoint/lnk";
+ const char RELPATH[] = "lnk";
+ const char FULLDST[] = "mountpoint/dst";
+ const char RELDST[] = "dst";
+ uint64_t dst_ino = 42;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_lookup(RELDST, dst_ino);
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *name = (const char*)in.body.bytes
+ + sizeof(struct fuse_link_in);
+ return (in.header.opcode == FUSE_LINK &&
+ in.body.link.oldnodeid == dst_ino &&
+ (0 == strcmp(name, RELPATH)));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(EMLINK)));
+
+ EXPECT_EQ(-1, link(FULLDST, FULLPATH));
+ EXPECT_EQ(EMLINK, errno);
+}
+
+/*
+ * A hard link should always have the same inode as its source. If it doesn't,
+ * then it's not a hard link.
+ */
+TEST_F(Link, bad_inode)
+{
+ const char FULLPATH[] = "mountpoint/src";
+ const char RELPATH[] = "src";
+ const char FULLDST[] = "mountpoint/dst";
+ const char RELDST[] = "dst";
+ const uint64_t src_ino = 42;
+ const uint64_t dst_ino = 43;
+ mode_t mode = S_IFREG | 0644;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDST)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = dst_ino;
+ out.body.entry.attr.nlink = 1;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *name = (const char*)in.body.bytes
+ + sizeof(struct fuse_link_in);
+ return (in.header.opcode == FUSE_LINK &&
+ in.body.link.oldnodeid == dst_ino &&
+ (0 == strcmp(name, RELPATH)));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.nodeid = src_ino;
+ out.body.entry.attr.mode = mode;
+ out.body.entry.attr.nlink = 2;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+
+ ASSERT_EQ(-1, link(FULLDST, FULLPATH));
+ ASSERT_EQ(EIO, errno);
+}
+
+TEST_F(Link, ok)
+{
+ const char FULLPATH[] = "mountpoint/src";
+ const char RELPATH[] = "src";
+ const char FULLDST[] = "mountpoint/dst";
+ const char RELDST[] = "dst";
+ const uint64_t ino = 42;
+ mode_t mode = S_IFREG | 0644;
+ struct stat sb;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDST)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.nlink = 1;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+ expect_link(ino, RELPATH, mode, 2);
+
+ ASSERT_EQ(0, link(FULLDST, FULLPATH)) << strerror(errno);
+ // Check that the original file's nlink count has increased.
+ ASSERT_EQ(0, stat(FULLDST, &sb)) << strerror(errno);
+ EXPECT_EQ(2ul, sb.st_nlink);
+}
+
+TEST_F(Link_7_8, ok)
+{
+ const char FULLPATH[] = "mountpoint/src";
+ const char RELPATH[] = "src";
+ const char FULLDST[] = "mountpoint/dst";
+ const char RELDST[] = "dst";
+ const uint64_t ino = 42;
+ mode_t mode = S_IFREG | 0644;
+ struct stat sb;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDST)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry_7_8);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.nlink = 1;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+ expect_link(ino, RELPATH, mode, 2);
+
+ ASSERT_EQ(0, link(FULLDST, FULLPATH)) << strerror(errno);
+ // Check that the original file's nlink count has increased.
+ ASSERT_EQ(0, stat(FULLDST, &sb)) << strerror(errno);
+ EXPECT_EQ(2ul, sb.st_nlink);
+}
diff --git a/tests/sys/fs/fusefs/locks.cc b/tests/sys/fs/fusefs/locks.cc
new file mode 100644
index 000000000000..222217c3cfbe
--- /dev/null
+++ b/tests/sys/fs/fusefs/locks.cc
@@ -0,0 +1,730 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <sys/file.h>
+#include <fcntl.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+/* This flag value should probably be defined in fuse_kernel.h */
+#define OFFSET_MAX 0x7fffffffffffffffLL
+
+using namespace testing;
+
+/* For testing filesystems without posix locking support */
+class Fallback: public FuseTest {
+public:
+
+void expect_lookup(const char *relpath, uint64_t ino, uint64_t size = 0)
+{
+ FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1);
+}
+
+};
+
+/* For testing filesystems with posix locking support */
+class Locks: public Fallback {
+ virtual void SetUp() {
+ m_init_flags = FUSE_POSIX_LOCKS;
+ Fallback::SetUp();
+ }
+};
+
+class Fcntl: public Locks {
+public:
+void expect_setlk(uint64_t ino, pid_t pid, uint64_t start, uint64_t end,
+ uint32_t type, int err)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_SETLK &&
+ in.header.nodeid == ino &&
+ in.body.setlk.fh == FH &&
+ in.body.setlk.owner == (uint32_t)pid &&
+ in.body.setlk.lk.start == start &&
+ in.body.setlk.lk.end == end &&
+ in.body.setlk.lk.type == type &&
+ in.body.setlk.lk.pid == (uint64_t)pid);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(err)));
+}
+void expect_setlkw(uint64_t ino, pid_t pid, uint64_t start, uint64_t end,
+ uint32_t type, int err)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_SETLKW &&
+ in.header.nodeid == ino &&
+ in.body.setlkw.fh == FH &&
+ in.body.setlkw.owner == (uint32_t)pid &&
+ in.body.setlkw.lk.start == start &&
+ in.body.setlkw.lk.end == end &&
+ in.body.setlkw.lk.type == type &&
+ in.body.setlkw.lk.pid == (uint64_t)pid);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(err)));
+}
+};
+
+class Flock: public Locks {
+public:
+void expect_setlk(uint64_t ino, uint32_t type, int err)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_SETLK &&
+ in.header.nodeid == ino &&
+ in.body.setlk.fh == FH &&
+ /*
+ * The owner should be set to the address of
+ * the vnode. That's hard to verify.
+ */
+ /* in.body.setlk.owner == ??? && */
+ in.body.setlk.lk.type == type);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(err)));
+}
+};
+
+class FlockFallback: public Fallback {};
+class GetlkFallback: public Fallback {};
+class Getlk: public Fcntl {};
+class SetlkFallback: public Fallback {};
+class Setlk: public Fcntl {};
+class SetlkwFallback: public Fallback {};
+class Setlkw: public Fcntl {};
+
+/*
+ * If the fuse filesystem does not support flock locks, then the kernel should
+ * fall back to local locks.
+ */
+TEST_F(FlockFallback, local)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int fd;
+
+ expect_lookup(RELPATH, ino);
+ expect_open(ino, 0, 1);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(0, flock(fd, LOCK_EX)) << strerror(errno);
+ leak(fd);
+}
+
+/*
+ * Even if the fuse file system supports POSIX locks, we must implement flock
+ * locks locally until protocol 7.17. Protocol 7.9 added partial buggy support
+ * but we won't implement that.
+ */
+TEST_F(Flock, local)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int fd;
+
+ expect_lookup(RELPATH, ino);
+ expect_open(ino, 0, 1);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(0, flock(fd, LOCK_EX)) << strerror(errno);
+ leak(fd);
+}
+
+/* Set a new flock lock with FUSE_SETLK */
+/* TODO: enable after upgrading to protocol 7.17 */
+TEST_F(Flock, DISABLED_set)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int fd;
+
+ expect_lookup(RELPATH, ino);
+ expect_open(ino, 0, 1);
+ expect_setlk(ino, F_WRLCK, 0);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(0, flock(fd, LOCK_EX)) << strerror(errno);
+ leak(fd);
+}
+
+/* Fail to set a flock lock in non-blocking mode */
+/* TODO: enable after upgrading to protocol 7.17 */
+TEST_F(Flock, DISABLED_eagain)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int fd;
+
+ expect_lookup(RELPATH, ino);
+ expect_open(ino, 0, 1);
+ expect_setlk(ino, F_WRLCK, EAGAIN);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_NE(0, flock(fd, LOCK_EX | LOCK_NB));
+ ASSERT_EQ(EAGAIN, errno);
+ leak(fd);
+}
+
+/*
+ * If the fuse filesystem does not support posix file locks, then the kernel
+ * should fall back to local locks.
+ */
+TEST_F(GetlkFallback, local)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ struct flock fl;
+ int fd;
+
+ expect_lookup(RELPATH, ino);
+ expect_open(ino, 0, 1);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ fl.l_start = 10;
+ fl.l_len = 1000;
+ fl.l_pid = 0;
+ fl.l_type = F_RDLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_sysid = 0;
+ ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno);
+ leak(fd);
+}
+
+/*
+ * If the filesystem has no locks that fit the description, the filesystem
+ * should return F_UNLCK
+ */
+TEST_F(Getlk, no_locks)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ struct flock fl;
+ int fd;
+ pid_t pid = getpid();
+
+ expect_lookup(RELPATH, ino);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETLK &&
+ in.header.nodeid == ino &&
+ in.body.getlk.fh == FH &&
+ /*
+ * Though it seems useless, libfuse expects the
+ * owner and pid fields to be set during
+ * FUSE_GETLK.
+ */
+ in.body.getlk.owner == (uint32_t)pid &&
+ in.body.getlk.lk.pid == (uint64_t)pid &&
+ in.body.getlk.lk.start == 10 &&
+ in.body.getlk.lk.end == 1009 &&
+ in.body.getlk.lk.type == F_RDLCK);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in, auto& out) {
+ SET_OUT_HEADER_LEN(out, getlk);
+ out.body.getlk.lk = in.body.getlk.lk;
+ out.body.getlk.lk.type = F_UNLCK;
+ })));
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ fl.l_start = 10;
+ fl.l_len = 1000;
+ fl.l_pid = 42;
+ fl.l_type = F_RDLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_sysid = 42;
+ ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno);
+
+ /*
+ * If no lock is found that would prevent this lock from being created,
+ * the structure is left unchanged by this system call except for the
+ * lock type which is set to F_UNLCK.
+ */
+ ASSERT_EQ(F_UNLCK, fl.l_type);
+ ASSERT_EQ(fl.l_pid, 42);
+ ASSERT_EQ(fl.l_start, 10);
+ ASSERT_EQ(fl.l_len, 1000);
+ ASSERT_EQ(fl.l_whence, SEEK_SET);
+ ASSERT_EQ(fl.l_sysid, 42);
+
+ leak(fd);
+}
+
+/* A different pid does have a lock */
+TEST_F(Getlk, lock_exists)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ struct flock fl;
+ int fd;
+ pid_t pid = getpid();
+ pid_t pid2 = 1235;
+
+ expect_lookup(RELPATH, ino);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETLK &&
+ in.header.nodeid == ino &&
+ in.body.getlk.fh == FH &&
+ /*
+ * Though it seems useless, libfuse expects the
+ * owner and pid fields to be set during
+ * FUSE_GETLK.
+ */
+ in.body.getlk.owner == (uint32_t)pid &&
+ in.body.getlk.lk.pid == (uint64_t)pid &&
+ in.body.getlk.lk.start == 10 &&
+ in.body.getlk.lk.end == 1009 &&
+ in.body.getlk.lk.type == F_RDLCK);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, getlk);
+ out.body.getlk.lk.start = 100;
+ out.body.getlk.lk.end = 199;
+ out.body.getlk.lk.type = F_WRLCK;
+ out.body.getlk.lk.pid = (uint32_t)pid2;;
+ })));
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ fl.l_start = 10;
+ fl.l_len = 1000;
+ fl.l_pid = 0;
+ fl.l_type = F_RDLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_sysid = 0;
+ ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno);
+ EXPECT_EQ(100, fl.l_start);
+ EXPECT_EQ(100, fl.l_len);
+ EXPECT_EQ(pid2, fl.l_pid);
+ EXPECT_EQ(F_WRLCK, fl.l_type);
+ EXPECT_EQ(SEEK_SET, fl.l_whence);
+ EXPECT_EQ(0, fl.l_sysid);
+ leak(fd);
+}
+
+/*
+ * F_GETLK with SEEK_CUR
+ */
+TEST_F(Getlk, seek_cur)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ struct flock fl;
+ int fd;
+ pid_t pid = getpid();
+
+ expect_lookup(RELPATH, ino, 1024);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETLK &&
+ in.header.nodeid == ino &&
+ in.body.getlk.fh == FH &&
+ /*
+ * Though it seems useless, libfuse expects the
+ * owner and pid fields to be set during
+ * FUSE_GETLK.
+ */
+ in.body.getlk.owner == (uint32_t)pid &&
+ in.body.getlk.lk.pid == (uint64_t)pid &&
+ in.body.getlk.lk.start == 500 &&
+ in.body.getlk.lk.end == 509 &&
+ in.body.getlk.lk.type == F_RDLCK);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, getlk);
+ out.body.getlk.lk.start = 400;
+ out.body.getlk.lk.end = 499;
+ out.body.getlk.lk.type = F_WRLCK;
+ out.body.getlk.lk.pid = (uint32_t)pid + 1;
+ })));
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_NE(-1, lseek(fd, 500, SEEK_SET));
+
+ fl.l_start = 0;
+ fl.l_len = 10;
+ fl.l_pid = 42;
+ fl.l_type = F_RDLCK;
+ fl.l_whence = SEEK_CUR;
+ fl.l_sysid = 0;
+ ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno);
+
+ /*
+ * After a successful F_GETLK request, the value of l_whence is
+ * SEEK_SET.
+ */
+ EXPECT_EQ(F_WRLCK, fl.l_type);
+ EXPECT_EQ(fl.l_pid, pid + 1);
+ EXPECT_EQ(fl.l_start, 400);
+ EXPECT_EQ(fl.l_len, 100);
+ EXPECT_EQ(fl.l_whence, SEEK_SET);
+ ASSERT_EQ(fl.l_sysid, 0);
+
+ leak(fd);
+}
+
+/*
+ * F_GETLK with SEEK_END
+ */
+TEST_F(Getlk, seek_end)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ struct flock fl;
+ int fd;
+ pid_t pid = getpid();
+
+ expect_lookup(RELPATH, ino, 1024);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETLK &&
+ in.header.nodeid == ino &&
+ in.body.getlk.fh == FH &&
+ /*
+ * Though it seems useless, libfuse expects the
+ * owner and pid fields to be set during
+ * FUSE_GETLK.
+ */
+ in.body.getlk.owner == (uint32_t)pid &&
+ in.body.getlk.lk.pid == (uint64_t)pid &&
+ in.body.getlk.lk.start == 512 &&
+ in.body.getlk.lk.end == 1023 &&
+ in.body.getlk.lk.type == F_RDLCK);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, getlk);
+ out.body.getlk.lk.start = 400;
+ out.body.getlk.lk.end = 499;
+ out.body.getlk.lk.type = F_WRLCK;
+ out.body.getlk.lk.pid = (uint32_t)pid + 1;
+ })));
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_NE(-1, lseek(fd, 500, SEEK_SET));
+
+ fl.l_start = -512;
+ fl.l_len = 512;
+ fl.l_pid = 42;
+ fl.l_type = F_RDLCK;
+ fl.l_whence = SEEK_END;
+ fl.l_sysid = 0;
+ ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno);
+
+ /*
+ * After a successful F_GETLK request, the value of l_whence is
+ * SEEK_SET.
+ */
+ EXPECT_EQ(F_WRLCK, fl.l_type);
+ EXPECT_EQ(fl.l_pid, pid + 1);
+ EXPECT_EQ(fl.l_start, 400);
+ EXPECT_EQ(fl.l_len, 100);
+ EXPECT_EQ(fl.l_whence, SEEK_SET);
+ ASSERT_EQ(fl.l_sysid, 0);
+
+ leak(fd);
+}
+
+/*
+ * If the fuse filesystem does not support posix file locks, then the kernel
+ * should fall back to local locks.
+ */
+TEST_F(SetlkFallback, local)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ struct flock fl;
+ int fd;
+
+ expect_lookup(RELPATH, ino);
+ expect_open(ino, 0, 1);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ fl.l_start = 10;
+ fl.l_len = 1000;
+ fl.l_pid = getpid();
+ fl.l_type = F_RDLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_sysid = 0;
+ ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
+ leak(fd);
+}
+
+/* Clear a lock with FUSE_SETLK */
+TEST_F(Setlk, clear)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ struct flock fl;
+ int fd;
+ pid_t pid = getpid();
+
+ expect_lookup(RELPATH, ino);
+ expect_open(ino, 0, 1);
+ expect_setlk(ino, pid, 10, 1009, F_UNLCK, 0);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ fl.l_start = 10;
+ fl.l_len = 1000;
+ fl.l_pid = 0;
+ fl.l_type = F_UNLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_sysid = 0;
+ ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
+ leak(fd);
+}
+
+/* Set a new lock with FUSE_SETLK */
+TEST_F(Setlk, set)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ struct flock fl;
+ int fd;
+ pid_t pid = getpid();
+
+ expect_lookup(RELPATH, ino);
+ expect_open(ino, 0, 1);
+ expect_setlk(ino, pid, 10, 1009, F_RDLCK, 0);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ fl.l_start = 10;
+ fl.l_len = 1000;
+ fl.l_pid = 0;
+ fl.l_type = F_RDLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_sysid = 0;
+ ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
+ leak(fd);
+}
+
+/* l_len = 0 is a flag value that means to lock until EOF */
+TEST_F(Setlk, set_eof)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ struct flock fl;
+ int fd;
+ pid_t pid = getpid();
+
+ expect_lookup(RELPATH, ino);
+ expect_open(ino, 0, 1);
+ expect_setlk(ino, pid, 10, OFFSET_MAX, F_RDLCK, 0);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ fl.l_start = 10;
+ fl.l_len = 0;
+ fl.l_pid = 0;
+ fl.l_type = F_RDLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_sysid = 0;
+ ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
+ leak(fd);
+}
+
+/* Set a new lock with FUSE_SETLK, using SEEK_CUR for l_whence */
+TEST_F(Setlk, set_seek_cur)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ struct flock fl;
+ int fd;
+ pid_t pid = getpid();
+
+ expect_lookup(RELPATH, ino, 1024);
+ expect_open(ino, 0, 1);
+ expect_setlk(ino, pid, 500, 509, F_RDLCK, 0);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_NE(-1, lseek(fd, 500, SEEK_SET));
+
+ fl.l_start = 0;
+ fl.l_len = 10;
+ fl.l_pid = 0;
+ fl.l_type = F_RDLCK;
+ fl.l_whence = SEEK_CUR;
+ fl.l_sysid = 0;
+ ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
+
+ leak(fd);
+}
+
+/* Set a new lock with FUSE_SETLK, using SEEK_END for l_whence */
+TEST_F(Setlk, set_seek_end)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ struct flock fl;
+ int fd;
+ pid_t pid = getpid();
+
+ expect_lookup(RELPATH, ino, 1024);
+ expect_open(ino, 0, 1);
+ expect_setlk(ino, pid, 1000, 1009, F_RDLCK, 0);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ fl.l_start = -24;
+ fl.l_len = 10;
+ fl.l_pid = 0;
+ fl.l_type = F_RDLCK;
+ fl.l_whence = SEEK_END;
+ fl.l_sysid = 0;
+ ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
+
+ leak(fd);
+}
+
+/* Fail to set a new lock with FUSE_SETLK due to a conflict */
+TEST_F(Setlk, eagain)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ struct flock fl;
+ int fd;
+ pid_t pid = getpid();
+
+ expect_lookup(RELPATH, ino);
+ expect_open(ino, 0, 1);
+ expect_setlk(ino, pid, 10, 1009, F_RDLCK, EAGAIN);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ fl.l_start = 10;
+ fl.l_len = 1000;
+ fl.l_pid = 0;
+ fl.l_type = F_RDLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_sysid = 0;
+ ASSERT_EQ(-1, fcntl(fd, F_SETLK, &fl));
+ ASSERT_EQ(EAGAIN, errno);
+ leak(fd);
+}
+
+/*
+ * If the fuse filesystem does not support posix file locks, then the kernel
+ * should fall back to local locks.
+ */
+TEST_F(SetlkwFallback, local)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ struct flock fl;
+ int fd;
+
+ expect_lookup(RELPATH, ino);
+ expect_open(ino, 0, 1);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ fl.l_start = 10;
+ fl.l_len = 1000;
+ fl.l_pid = 0;
+ fl.l_type = F_RDLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_sysid = 0;
+ ASSERT_NE(-1, fcntl(fd, F_SETLKW, &fl)) << strerror(errno);
+ leak(fd);
+}
+
+/*
+ * Set a new lock with FUSE_SETLK. If the lock is not available, then the
+ * command should block. But to the kernel, that's the same as just being
+ * slow, so we don't need a separate test method
+ */
+TEST_F(Setlkw, set)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ struct flock fl;
+ int fd;
+ pid_t pid = getpid();
+
+ expect_lookup(RELPATH, ino);
+ expect_open(ino, 0, 1);
+ expect_setlkw(ino, pid, 10, 1009, F_RDLCK, 0);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ fl.l_start = 10;
+ fl.l_len = 1000;
+ fl.l_pid = 0;
+ fl.l_type = F_RDLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_sysid = 0;
+ ASSERT_NE(-1, fcntl(fd, F_SETLKW, &fl)) << strerror(errno);
+ leak(fd);
+}
diff --git a/tests/sys/fs/fusefs/lookup.cc b/tests/sys/fs/fusefs/lookup.cc
new file mode 100644
index 000000000000..2cfe888b6b08
--- /dev/null
+++ b/tests/sys/fs/fusefs/lookup.cc
@@ -0,0 +1,662 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class Lookup: public FuseTest {};
+
+class Lookup_7_8: public Lookup {
+public:
+virtual void SetUp() {
+ m_kernel_minor_version = 8;
+ Lookup::SetUp();
+}
+};
+
+class LookupExportable: public Lookup {
+public:
+virtual void SetUp() {
+ m_init_flags = FUSE_EXPORT_SUPPORT;
+ Lookup::SetUp();
+}
+};
+
+/*
+ * If lookup returns a non-zero cache timeout, then subsequent VOP_GETATTRs
+ * should use the cached attributes, rather than query the daemon
+ */
+TEST_F(Lookup, attr_cache)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ const uint64_t generation = 13;
+ struct stat sb;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.attr.ino = ino; // Must match nodeid
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.attr.size = 1;
+ out.body.entry.attr.blocks = 2;
+ out.body.entry.attr.atime = 3;
+ out.body.entry.attr.mtime = 4;
+ out.body.entry.attr.ctime = 5;
+ out.body.entry.attr.atimensec = 6;
+ out.body.entry.attr.mtimensec = 7;
+ out.body.entry.attr.ctimensec = 8;
+ out.body.entry.attr.nlink = 9;
+ out.body.entry.attr.uid = 10;
+ out.body.entry.attr.gid = 11;
+ out.body.entry.attr.rdev = 12;
+ out.body.entry.generation = generation;
+ })));
+ /* stat(2) issues a VOP_LOOKUP followed by a VOP_GETATTR */
+ ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno);
+ EXPECT_EQ(1, sb.st_size);
+ EXPECT_EQ(2, sb.st_blocks);
+ EXPECT_EQ(3, sb.st_atim.tv_sec);
+ EXPECT_EQ(6, sb.st_atim.tv_nsec);
+ EXPECT_EQ(4, sb.st_mtim.tv_sec);
+ EXPECT_EQ(7, sb.st_mtim.tv_nsec);
+ EXPECT_EQ(5, sb.st_ctim.tv_sec);
+ EXPECT_EQ(8, sb.st_ctim.tv_nsec);
+ EXPECT_EQ(9ull, sb.st_nlink);
+ EXPECT_EQ(10ul, sb.st_uid);
+ EXPECT_EQ(11ul, sb.st_gid);
+ EXPECT_EQ(12ul, sb.st_rdev);
+ EXPECT_EQ(ino, sb.st_ino);
+ EXPECT_EQ(S_IFREG | 0644, sb.st_mode);
+
+ // fuse(4) does not _yet_ support inode generations
+ //EXPECT_EQ(generation, sb.st_gen);
+
+ /*
+ * st_birthtim and st_flags are not supported by the fuse protocol.
+ * They're only supported as OS-specific extensions to OSX. For
+ * birthtime, the convention for "not supported" is "negative one
+ * second".
+ */
+ EXPECT_EQ(-1, sb.st_birthtim.tv_sec);
+ EXPECT_EQ(0, sb.st_birthtim.tv_nsec);
+ EXPECT_EQ(0u, sb.st_flags);
+}
+
+/*
+ * If lookup returns a finite but non-zero cache timeout, then we should discard
+ * the cached attributes and requery the daemon.
+ */
+TEST_F(Lookup, attr_cache_timeout)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ struct stat sb;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .Times(2)
+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr_valid_nsec = NAP_NS / 2;
+ out.body.entry.attr.ino = ino; // Must match nodeid
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ })));
+
+ /* access(2) will issue a VOP_LOOKUP and fill the attr cache */
+ ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
+ /* Next access(2) will use the cached attributes */
+ nap();
+ /* The cache has timed out; VOP_GETATTR should query the daemon*/
+ ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno);
+}
+
+TEST_F(Lookup, dot)
+{
+ const char FULLPATH[] = "mountpoint/some_dir/.";
+ const char RELDIRPATH[] = "some_dir";
+ uint64_t ino = 42;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFDIR | 0755;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+
+ /*
+ * access(2) is one of the few syscalls that will not (always) follow
+ * up a successful VOP_LOOKUP with another VOP.
+ */
+ ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
+}
+
+TEST_F(Lookup, dotdot)
+{
+ const char FULLPATH[] = "mountpoint/some_dir/..";
+ const char RELDIRPATH[] = "some_dir";
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFDIR | 0755;
+ out.body.entry.nodeid = 14;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+
+ /*
+ * access(2) is one of the few syscalls that will not (always) follow
+ * up a successful VOP_LOOKUP with another VOP.
+ */
+ ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
+}
+
+/*
+ * Lookup ".." when that vnode's entry cache has timed out, but its child's
+ * hasn't. Since this file system doesn't set FUSE_EXPORT_SUPPORT, we have no
+ * choice but to use the cached entry, even though it expired.
+ */
+TEST_F(Lookup, dotdot_entry_cache_timeout)
+{
+ uint64_t foo_ino = 42;
+ uint64_t bar_ino = 43;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, "foo")
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFDIR | 0755;
+ out.body.entry.nodeid = foo_ino;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = 0; // immediate timeout
+ })));
+ EXPECT_LOOKUP(foo_ino, "bar")
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFDIR | 0755;
+ out.body.entry.nodeid = bar_ino;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+ expect_opendir(bar_ino);
+
+ int fd = open("mountpoint/foo/bar", O_EXEC| O_DIRECTORY);
+ ASSERT_LE(0, fd) << strerror(errno);
+ EXPECT_EQ(0, faccessat(fd, "../..", F_OK, 0)) << strerror(errno);
+}
+
+/*
+ * Lookup ".." for a vnode with no valid parent nid
+ * Regression test for https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=259974
+ * Since the file system is not exportable, we have no choice but to return an
+ * error.
+ */
+TEST_F(Lookup, dotdot_no_parent_nid)
+{
+ uint64_t foo_ino = 42;
+ uint64_t bar_ino = 43;
+ int fd;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, "foo")
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFDIR | 0755;
+ out.body.entry.nodeid = foo_ino;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+ EXPECT_LOOKUP(foo_ino, "bar")
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFDIR | 0755;
+ out.body.entry.nodeid = bar_ino;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_OPENDIR);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, open);
+ })));
+ expect_forget(foo_ino, 1, NULL);
+
+ fd = open("mountpoint/foo/bar", O_EXEC| O_DIRECTORY);
+ ASSERT_LE(0, fd) << strerror(errno);
+ // Try (and fail) to unmount the file system, to reclaim the mountpoint
+ // and foo vnodes.
+ ASSERT_NE(0, unmount("mountpoint", 0));
+ EXPECT_EQ(EBUSY, errno);
+ nap(); // Because vnode reclamation is asynchronous
+ EXPECT_NE(0, faccessat(fd, "../..", F_OK, 0));
+ EXPECT_EQ(ESTALE, errno);
+}
+
+/*
+ * A daemon that returns an illegal error value should be handled gracefully.
+ * Regression test for https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=263220
+ */
+TEST_F(Lookup, ejustreturn)
+{
+ const char FULLPATH[] = "mountpoint/does_not_exist";
+ const char RELPATH[] = "does_not_exist";
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ out.header.len = sizeof(out.header);
+ out.header.error = 2;
+ out.expected_errno = EINVAL;
+ })));
+
+ EXPECT_NE(0, access(FULLPATH, F_OK));
+
+ EXPECT_EQ(EIO, errno);
+}
+
+TEST_F(Lookup, enoent)
+{
+ const char FULLPATH[] = "mountpoint/does_not_exist";
+ const char RELPATH[] = "does_not_exist";
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ EXPECT_NE(0, access(FULLPATH, F_OK));
+ EXPECT_EQ(ENOENT, errno);
+}
+
+TEST_F(Lookup, enotdir)
+{
+ const char FULLPATH[] = "mountpoint/not_a_dir/some_file.txt";
+ const char RELPATH[] = "not_a_dir";
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.entry_valid = UINT64_MAX;
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = 42;
+ })));
+
+ ASSERT_EQ(-1, access(FULLPATH, F_OK));
+ ASSERT_EQ(ENOTDIR, errno);
+}
+
+/*
+ * If lookup returns a non-zero entry timeout, then subsequent VOP_LOOKUPs
+ * should use the cached inode rather than requery the daemon
+ */
+TEST_F(Lookup, entry_cache)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.entry_valid = UINT64_MAX;
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = 14;
+ })));
+ ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
+ /* The second access(2) should use the cache */
+ ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
+}
+
+/*
+ * If the daemon returns an error of 0 and an inode of 0, that's a flag for
+ * "ENOENT and cache it" with the given entry_timeout
+ */
+TEST_F(Lookup, entry_cache_negative)
+{
+ struct timespec entry_valid = {.tv_sec = TIME_T_MAX, .tv_nsec = 0};
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, "does_not_exist")
+ .Times(1)
+ .WillOnce(Invoke(ReturnNegativeCache(&entry_valid)));
+
+ EXPECT_NE(0, access("mountpoint/does_not_exist", F_OK));
+ EXPECT_EQ(ENOENT, errno);
+ EXPECT_NE(0, access("mountpoint/does_not_exist", F_OK));
+ EXPECT_EQ(ENOENT, errno);
+}
+
+/* Negative entry caches should timeout, too */
+TEST_F(Lookup, entry_cache_negative_timeout)
+{
+ const char *RELPATH = "does_not_exist";
+ const char *FULLPATH = "mountpoint/does_not_exist";
+ struct timespec entry_valid = {.tv_sec = 0, .tv_nsec = NAP_NS / 2};
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .Times(2)
+ .WillRepeatedly(Invoke(ReturnNegativeCache(&entry_valid)));
+
+ EXPECT_NE(0, access(FULLPATH, F_OK));
+ EXPECT_EQ(ENOENT, errno);
+
+ nap();
+
+ /* The cache has timed out; VOP_LOOKUP should requery the daemon*/
+ EXPECT_NE(0, access(FULLPATH, F_OK));
+ EXPECT_EQ(ENOENT, errno);
+}
+
+/*
+ * If lookup returns a finite but non-zero entry cache timeout, then we should
+ * discard the cached inode and requery the daemon
+ */
+TEST_F(Lookup, entry_cache_timeout)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .Times(2)
+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.entry_valid_nsec = NAP_NS / 2;
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = 14;
+ })));
+
+ /* access(2) will issue a VOP_LOOKUP and fill the entry cache */
+ ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
+ /* Next access(2) will use the cached entry */
+ ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
+ nap();
+ /* The cache has timed out; VOP_LOOKUP should requery the daemon*/
+ ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
+}
+
+TEST_F(Lookup, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = 14;
+ })));
+ /*
+ * access(2) is one of the few syscalls that will not (always) follow
+ * up a successful VOP_LOOKUP with another VOP.
+ */
+ ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
+}
+
+/*
+ * Lookup in a subdirectory of the fuse mount. The naughty server returns the
+ * same inode for the child as for the parent.
+ */
+TEST_F(Lookup, parent_inode)
+{
+ const char FULLPATH[] = "mountpoint/some_dir/some_file.txt";
+ const char DIRPATH[] = "some_dir";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t dir_ino = 2;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, DIRPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFDIR | 0755;
+ out.body.entry.nodeid = dir_ino;
+ })));
+ EXPECT_LOOKUP(dir_ino, RELPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = dir_ino;
+ })));
+ /*
+ * access(2) is one of the few syscalls that will not (always) follow
+ * up a successful VOP_LOOKUP with another VOP.
+ */
+ ASSERT_EQ(-1, access(FULLPATH, F_OK));
+ ASSERT_EQ(EIO, errno);
+}
+
+// Lookup in a subdirectory of the fuse mount
+TEST_F(Lookup, subdir)
+{
+ const char FULLPATH[] = "mountpoint/some_dir/some_file.txt";
+ const char DIRPATH[] = "some_dir";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t dir_ino = 2;
+ uint64_t file_ino = 3;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, DIRPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFDIR | 0755;
+ out.body.entry.nodeid = dir_ino;
+ })));
+ EXPECT_LOOKUP(dir_ino, RELPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = file_ino;
+ })));
+ /*
+ * access(2) is one of the few syscalls that will not (always) follow
+ * up a successful VOP_LOOKUP with another VOP.
+ */
+ ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
+}
+
+/*
+ * The server returns two different vtypes for the same nodeid. This is
+ * technically allowed if the entry's cache has already expired.
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=258022
+ */
+TEST_F(Lookup, vtype_conflict)
+{
+ const char FIRSTFULLPATH[] = "mountpoint/foo";
+ const char SECONDFULLPATH[] = "mountpoint/bar";
+ const char FIRSTRELPATH[] = "foo";
+ const char SECONDRELPATH[] = "bar";
+ uint64_t ino = 42;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, FIRSTRELPATH)
+ .WillOnce(Invoke(
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFDIR | 0644;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.nlink = 1;
+ })));
+ expect_lookup(SECONDRELPATH, ino, S_IFREG | 0755, 0, 1, UINT64_MAX);
+ // VOP_FORGET happens asynchronously, so it may or may not arrive
+ // before the test completes.
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_FORGET &&
+ in.header.nodeid == ino &&
+ in.body.forget.nlookup == 1);
+ }, Eq(true)),
+ _)
+ ).Times(AtMost(1))
+ .WillOnce(Invoke([=](auto in __unused, auto &out __unused) { }));
+
+ ASSERT_EQ(0, access(FIRSTFULLPATH, F_OK)) << strerror(errno);
+ EXPECT_EQ(0, access(SECONDFULLPATH, F_OK)) << strerror(errno);
+}
+
+TEST_F(Lookup_7_8, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry_7_8);
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = 14;
+ })));
+ /*
+ * access(2) is one of the few syscalls that will not (always) follow
+ * up a successful VOP_LOOKUP with another VOP.
+ */
+ ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
+}
+
+/*
+ * Lookup ".." when that vnode's entry cache has timed out, but its child's
+ * hasn't.
+ */
+TEST_F(LookupExportable, dotdot_entry_cache_timeout)
+{
+ uint64_t foo_ino = 42;
+ uint64_t bar_ino = 43;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, "foo")
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFDIR | 0755;
+ out.body.entry.nodeid = foo_ino;
+ out.body.entry.attr.ino = foo_ino;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = 0; // immediate timeout
+ })));
+ EXPECT_LOOKUP(foo_ino, "bar")
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFDIR | 0755;
+ out.body.entry.nodeid = bar_ino;
+ out.body.entry.attr.ino = bar_ino;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+ expect_opendir(bar_ino);
+ EXPECT_LOOKUP(foo_ino, "..")
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFDIR | 0755;
+ out.body.entry.nodeid = FUSE_ROOT_ID;
+ out.body.entry.attr.ino = FUSE_ROOT_ID;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+
+ int fd = open("mountpoint/foo/bar", O_EXEC| O_DIRECTORY);
+ ASSERT_LE(0, fd) << strerror(errno);
+ /* FreeBSD's fusefs driver always uses the same cache expiration time
+ * for ".." as for the directory itself. So we need to look up two
+ * levels to find an expired ".." cache entry.
+ */
+ EXPECT_EQ(0, faccessat(fd, "../..", F_OK, 0)) << strerror(errno);
+}
+
+/*
+ * Lookup ".." for a vnode with no valid parent nid
+ * Regression test for https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=259974
+ * Since the file system is exportable, we should resolve the problem by
+ * sending a FUSE_LOOKUP for "..".
+ */
+TEST_F(LookupExportable, dotdot_no_parent_nid)
+{
+ uint64_t foo_ino = 42;
+ uint64_t bar_ino = 43;
+ int fd;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, "foo")
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFDIR | 0755;
+ out.body.entry.nodeid = foo_ino;
+ out.body.entry.attr.ino = foo_ino;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+ EXPECT_LOOKUP(foo_ino, "bar")
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFDIR | 0755;
+ out.body.entry.nodeid = bar_ino;
+ out.body.entry.attr.ino = bar_ino;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_OPENDIR);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, open);
+ })));
+ expect_forget(foo_ino, 1, NULL);
+ EXPECT_LOOKUP(bar_ino, "..")
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFDIR | 0755;
+ out.body.entry.nodeid = foo_ino;
+ out.body.entry.attr.ino = foo_ino;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+ EXPECT_LOOKUP(foo_ino, "..")
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFDIR | 0755;
+ out.body.entry.nodeid = FUSE_ROOT_ID;
+ out.body.entry.attr.ino = FUSE_ROOT_ID;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+
+ fd = open("mountpoint/foo/bar", O_EXEC| O_DIRECTORY);
+ ASSERT_LE(0, fd) << strerror(errno);
+ // Try (and fail) to unmount the file system, to reclaim the mountpoint
+ // and foo vnodes.
+ ASSERT_NE(0, unmount("mountpoint", 0));
+ EXPECT_EQ(EBUSY, errno);
+ nap(); // Because vnode reclamation is asynchronous
+ EXPECT_EQ(0, faccessat(fd, "../..", F_OK, 0)) << strerror(errno);
+}
diff --git a/tests/sys/fs/fusefs/lseek.cc b/tests/sys/fs/fusefs/lseek.cc
new file mode 100644
index 000000000000..12d41f7af1b2
--- /dev/null
+++ b/tests/sys/fs/fusefs/lseek.cc
@@ -0,0 +1,518 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2020 Alan Somers
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <sys/param.h>
+
+#include <fcntl.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class Lseek: public FuseTest {};
+class LseekPathconf: public Lseek {};
+class LseekPathconf_7_23: public LseekPathconf {
+public:
+virtual void SetUp() {
+ m_kernel_minor_version = 23;
+ FuseTest::SetUp();
+}
+};
+class LseekSeekHole: public Lseek {};
+class LseekSeekData: public Lseek {};
+
+/*
+ * If a previous lseek operation has already returned enosys, then pathconf can
+ * return EINVAL immediately.
+ */
+TEST_F(LseekPathconf, already_enosys)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ off_t fsize = 1 << 30; /* 1 GiB */
+ off_t offset_in = 1 << 28;
+ int fd;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_LSEEK);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(ENOSYS)));
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd);
+
+ EXPECT_EQ(offset_in, lseek(fd, offset_in, SEEK_DATA));
+ EXPECT_EQ(-1, fpathconf(fd, _PC_MIN_HOLE_SIZE));
+ EXPECT_EQ(EINVAL, errno);
+
+ leak(fd);
+}
+
+/*
+ * If a previous lseek operation has already returned successfully, then
+ * pathconf can return 1 immediately. 1 means "holes are reported, but size is
+ * not specified".
+ */
+TEST_F(LseekPathconf, already_seeked)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ off_t fsize = 1 << 30; /* 1 GiB */
+ off_t offset = 1 << 28;
+ int fd;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_LSEEK);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto i, auto& out) {
+ SET_OUT_HEADER_LEN(out, lseek);
+ out.body.lseek.offset = i.body.lseek.offset;
+ })));
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd);
+ EXPECT_EQ(offset, lseek(fd, offset, SEEK_DATA));
+
+ EXPECT_EQ(1, fpathconf(fd, _PC_MIN_HOLE_SIZE));
+
+ leak(fd);
+}
+
+/*
+ * Use pathconf on a file not already opened. The server returns EACCES when
+ * the kernel tries to open it. The kernel should return EACCES, and make no
+ * judgement about whether the server does or does not support FUSE_LSEEK.
+ */
+TEST_F(LseekPathconf, eacces)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ off_t fsize = 1 << 30; /* 1 GiB */
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.entry_valid = UINT64_MAX;
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.size = fsize;
+ })));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_OPEN &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).Times(2)
+ .WillRepeatedly(Invoke(ReturnErrno(EACCES)));
+
+ EXPECT_EQ(-1, pathconf(FULLPATH, _PC_MIN_HOLE_SIZE));
+ EXPECT_EQ(EACCES, errno);
+ /* Check again, to ensure that the kernel didn't record the response */
+ EXPECT_EQ(-1, pathconf(FULLPATH, _PC_MIN_HOLE_SIZE));
+ EXPECT_EQ(EACCES, errno);
+}
+
+/*
+ * If the server returns some weird error when we try FUSE_LSEEK, send that to
+ * the caller but don't record the answer.
+ */
+TEST_F(LseekPathconf, eio)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ off_t fsize = 1 << 30; /* 1 GiB */
+ int fd;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_LSEEK);
+ }, Eq(true)),
+ _)
+ ).Times(2)
+ .WillRepeatedly(Invoke(ReturnErrno(EIO)));
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd);
+
+ EXPECT_EQ(-1, fpathconf(fd, _PC_MIN_HOLE_SIZE));
+ EXPECT_EQ(EIO, errno);
+ /* Check again, to ensure that the kernel didn't record the response */
+ EXPECT_EQ(-1, fpathconf(fd, _PC_MIN_HOLE_SIZE));
+ EXPECT_EQ(EIO, errno);
+
+ leak(fd);
+}
+
+/*
+ * If no FUSE_LSEEK operation has been attempted since mount, try once as soon
+ * as a pathconf request comes in.
+ */
+TEST_F(LseekPathconf, enosys_now)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ off_t fsize = 1 << 30; /* 1 GiB */
+ int fd;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_LSEEK);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(ENOSYS)));
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd);
+
+ EXPECT_EQ(-1, fpathconf(fd, _PC_MIN_HOLE_SIZE));
+ EXPECT_EQ(EINVAL, errno);
+
+ leak(fd);
+}
+
+/*
+ * Use pathconf, rather than fpathconf, on a file not already opened.
+ * Regression test for https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=278135
+ */
+TEST_F(LseekPathconf, pathconf)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ off_t fsize = 1 << 30; /* 1 GiB */
+ off_t offset_out = 1 << 29;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_LSEEK);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, lseek);
+ out.body.lseek.offset = offset_out;
+ })));
+ expect_release(ino, FuseTest::FH);
+
+ EXPECT_EQ(1, pathconf(FULLPATH, _PC_MIN_HOLE_SIZE)) << strerror(errno);
+}
+
+/*
+ * If no FUSE_LSEEK operation has been attempted since mount, try one as soon
+ * as a pathconf request comes in. This is the typical pattern of bsdtar. It
+ * will only try SEEK_HOLE/SEEK_DATA if fpathconf says they're supported.
+ */
+TEST_F(LseekPathconf, seek_now)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ off_t fsize = 1 << 30; /* 1 GiB */
+ off_t offset_initial = 1 << 27;
+ off_t offset_out = 1 << 29;
+ int fd;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_LSEEK);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, lseek);
+ out.body.lseek.offset = offset_out;
+ })));
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd);
+ EXPECT_EQ(offset_initial, lseek(fd, offset_initial, SEEK_SET));
+ EXPECT_EQ(1, fpathconf(fd, _PC_MIN_HOLE_SIZE));
+ /* And check that the file pointer hasn't changed */
+ EXPECT_EQ(offset_initial, lseek(fd, 0, SEEK_CUR));
+
+ leak(fd);
+}
+
+/*
+ * If the user calls pathconf(_, _PC_MIN_HOLE_SIZE) on a fully sparse or
+ * zero-length file, then SEEK_DATA will return ENXIO. That should be
+ * interpreted as success.
+ */
+TEST_F(LseekPathconf, zerolength)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ off_t fsize = 0;
+ int fd;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_LSEEK &&
+ in.header.nodeid == ino &&
+ in.body.lseek.whence == SEEK_DATA);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(ENXIO)));
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd);
+ EXPECT_EQ(1, fpathconf(fd, _PC_MIN_HOLE_SIZE));
+ /* Check again, to ensure that the kernel recorded the response */
+ EXPECT_EQ(1, fpathconf(fd, _PC_MIN_HOLE_SIZE));
+
+ leak(fd);
+}
+
+/*
+ * For servers using older protocol versions, no FUSE_LSEEK should be attempted
+ */
+TEST_F(LseekPathconf_7_23, already_enosys)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ off_t fsize = 1 << 30; /* 1 GiB */
+ int fd;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_LSEEK);
+ }, Eq(true)),
+ _)
+ ).Times(0);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd);
+ EXPECT_EQ(-1, fpathconf(fd, _PC_MIN_HOLE_SIZE));
+ EXPECT_EQ(EINVAL, errno);
+
+ leak(fd);
+}
+
+TEST_F(LseekSeekData, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ off_t fsize = 1 << 30; /* 1 GiB */
+ off_t offset_in = 1 << 28;
+ off_t offset_out = 1 << 29;
+ int fd;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_LSEEK &&
+ in.header.nodeid == ino &&
+ in.body.lseek.fh == FH &&
+ (off_t)in.body.lseek.offset == offset_in &&
+ in.body.lseek.whence == SEEK_DATA);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, lseek);
+ out.body.lseek.offset = offset_out;
+ })));
+ fd = open(FULLPATH, O_RDONLY);
+ EXPECT_EQ(offset_out, lseek(fd, offset_in, SEEK_DATA));
+ EXPECT_EQ(offset_out, lseek(fd, 0, SEEK_CUR));
+
+ leak(fd);
+}
+
+/*
+ * If the server returns ENOSYS, fusefs should fall back to the default
+ * behavior, and never query the server again.
+ */
+TEST_F(LseekSeekData, enosys)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ off_t fsize = 1 << 30; /* 1 GiB */
+ off_t offset_in = 1 << 28;
+ int fd;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_LSEEK &&
+ in.header.nodeid == ino &&
+ in.body.lseek.fh == FH &&
+ (off_t)in.body.lseek.offset == offset_in &&
+ in.body.lseek.whence == SEEK_DATA);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(ENOSYS)));
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd);
+
+ /*
+ * Default behavior: ENXIO if offset is < 0 or >= fsize, offset
+ * otherwise.
+ */
+ EXPECT_EQ(offset_in, lseek(fd, offset_in, SEEK_DATA));
+ EXPECT_EQ(-1, lseek(fd, -1, SEEK_HOLE));
+ EXPECT_EQ(ENXIO, errno);
+ EXPECT_EQ(-1, lseek(fd, fsize, SEEK_HOLE));
+ EXPECT_EQ(ENXIO, errno);
+
+ leak(fd);
+}
+
+TEST_F(LseekSeekHole, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ off_t fsize = 1 << 30; /* 1 GiB */
+ off_t offset_in = 1 << 28;
+ off_t offset_out = 1 << 29;
+ int fd;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_LSEEK &&
+ in.header.nodeid == ino &&
+ in.body.lseek.fh == FH &&
+ (off_t)in.body.lseek.offset == offset_in &&
+ in.body.lseek.whence == SEEK_HOLE);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, lseek);
+ out.body.lseek.offset = offset_out;
+ })));
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd);
+ EXPECT_EQ(offset_out, lseek(fd, offset_in, SEEK_HOLE));
+ EXPECT_EQ(offset_out, lseek(fd, 0, SEEK_CUR));
+
+ leak(fd);
+}
+
+/*
+ * If the server returns ENOSYS, fusefs should fall back to the default
+ * behavior, and never query the server again.
+ */
+TEST_F(LseekSeekHole, enosys)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ off_t fsize = 1 << 30; /* 1 GiB */
+ off_t offset_in = 1 << 28;
+ int fd;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_LSEEK &&
+ in.header.nodeid == ino &&
+ in.body.lseek.fh == FH &&
+ (off_t)in.body.lseek.offset == offset_in &&
+ in.body.lseek.whence == SEEK_HOLE);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(ENOSYS)));
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd);
+
+ /*
+ * Default behavior: ENXIO if offset is < 0 or >= fsize, fsize
+ * otherwise.
+ */
+ EXPECT_EQ(fsize, lseek(fd, offset_in, SEEK_HOLE));
+ EXPECT_EQ(-1, lseek(fd, -1, SEEK_HOLE));
+ EXPECT_EQ(ENXIO, errno);
+ EXPECT_EQ(-1, lseek(fd, fsize, SEEK_HOLE));
+ EXPECT_EQ(ENXIO, errno);
+
+ leak(fd);
+}
+
+/* lseek should return ENXIO when offset points to EOF */
+TEST_F(LseekSeekHole, enxio)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ off_t fsize = 1 << 30; /* 1 GiB */
+ off_t offset_in = fsize;
+ int fd;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_LSEEK &&
+ in.header.nodeid == ino &&
+ in.body.lseek.fh == FH &&
+ (off_t)in.body.lseek.offset == offset_in &&
+ in.body.lseek.whence == SEEK_HOLE);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(ENXIO)));
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd);
+ EXPECT_EQ(-1, lseek(fd, offset_in, SEEK_HOLE));
+ EXPECT_EQ(ENXIO, errno);
+
+ leak(fd);
+}
diff --git a/tests/sys/fs/fusefs/mkdir.cc b/tests/sys/fs/fusefs/mkdir.cc
new file mode 100644
index 000000000000..f020feb94ed8
--- /dev/null
+++ b/tests/sys/fs/fusefs/mkdir.cc
@@ -0,0 +1,274 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <fcntl.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class Mkdir: public FuseTest {};
+class Mkdir_7_8: public FuseTest {
+public:
+virtual void SetUp() {
+ m_kernel_minor_version = 8;
+ FuseTest::SetUp();
+}
+};
+
+/*
+ * EMLINK is possible on filesystems that limit the number of hard links to a
+ * single file, like early versions of BtrFS
+ */
+TEST_F(Mkdir, emlink)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ mode_t mode = 0755;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *name = (const char*)in.body.bytes +
+ sizeof(fuse_mkdir_in);
+ return (in.header.opcode == FUSE_MKDIR &&
+ in.body.mkdir.mode == (S_IFDIR | mode) &&
+ (0 == strcmp(RELPATH, name)));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(EMLINK)));
+
+ ASSERT_NE(1, mkdir(FULLPATH, mode));
+ ASSERT_EQ(EMLINK, errno);
+}
+
+/*
+ * Creating a new directory after FUSE_LOOKUP returned a negative cache entry
+ */
+TEST_F(Mkdir, entry_cache_negative)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ mode_t mode = 0755;
+ uint64_t ino = 42;
+ /*
+ * Set entry_valid = 0 because this test isn't concerned with whether
+ * or not we actually cache negative entries, only with whether we
+ * interpret negative cache responses correctly.
+ */
+ struct timespec entry_valid = {.tv_sec = 0, .tv_nsec = 0};
+
+ /* mkdir will first do a LOOKUP, adding a negative cache entry */
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(ReturnNegativeCache(&entry_valid));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *name = (const char*)in.body.bytes +
+ sizeof(fuse_open_in);
+ return (in.header.opcode == FUSE_MKDIR &&
+ in.body.mkdir.mode == (S_IFDIR | mode) &&
+ (0 == strcmp(RELPATH, name)));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.create.entry.attr.mode = S_IFDIR | mode;
+ out.body.create.entry.nodeid = ino;
+ out.body.create.entry.entry_valid = UINT64_MAX;
+ out.body.create.entry.attr_valid = UINT64_MAX;
+ })));
+
+ ASSERT_EQ(0, mkdir(FULLPATH, mode)) << strerror(errno);
+}
+
+/*
+ * Creating a new directory should purge any negative namecache entries
+ */
+TEST_F(Mkdir, entry_cache_negative_purge)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ mode_t mode = 0755;
+ uint64_t ino = 42;
+ struct timespec entry_valid = {.tv_sec = TIME_T_MAX, .tv_nsec = 0};
+
+ /* mkdir will first do a LOOKUP, adding a negative cache entry */
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .Times(1)
+ .WillOnce(Invoke(ReturnNegativeCache(&entry_valid)))
+ .RetiresOnSaturation();
+
+ /* Then the MKDIR should purge the negative cache entry */
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *name = (const char*)in.body.bytes +
+ sizeof(fuse_open_in);
+ return (in.header.opcode == FUSE_MKDIR &&
+ in.body.mkdir.mode == (S_IFDIR | mode) &&
+ (0 == strcmp(RELPATH, name)));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFDIR | mode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr_valid = UINT64_MAX;
+ })));
+
+ ASSERT_EQ(0, mkdir(FULLPATH, mode)) << strerror(errno);
+
+ /* Finally, a subsequent lookup should query the daemon */
+ expect_lookup(RELPATH, ino, S_IFDIR | mode, 0, 1);
+
+ ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
+}
+
+TEST_F(Mkdir, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ mode_t mode = 0755;
+ uint64_t ino = 42;
+ mode_t mask;
+
+ mask = umask(0);
+ (void)umask(mask);
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *name = (const char*)in.body.bytes +
+ sizeof(fuse_mkdir_in);
+ return (in.header.opcode == FUSE_MKDIR &&
+ in.body.mkdir.mode == (S_IFDIR | mode) &&
+ in.body.mkdir.umask == mask &&
+ (0 == strcmp(RELPATH, name)));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.create.entry.attr.mode = S_IFDIR | mode;
+ out.body.create.entry.nodeid = ino;
+ out.body.create.entry.entry_valid = UINT64_MAX;
+ out.body.create.entry.attr_valid = UINT64_MAX;
+ })));
+
+ ASSERT_EQ(0, mkdir(FULLPATH, mode)) << strerror(errno);
+}
+
+/*
+ * Nothing bad should happen if the server returns the parent's inode number
+ * for the newly created directory. Regression test for bug 263662.
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=263662
+ */
+TEST_F(Mkdir, parent_inode)
+{
+ const char FULLPATH[] = "mountpoint/parent/some_dir";
+ const char PPATH[] = "parent";
+ const char RELPATH[] = "some_dir";
+ mode_t mode = 0755;
+ uint64_t ino = 42;
+ mode_t mask;
+
+ mask = umask(0);
+ (void)umask(mask);
+
+ expect_lookup(PPATH, ino, S_IFDIR | 0755, 0, 1);
+ EXPECT_LOOKUP(ino, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *name = (const char*)in.body.bytes +
+ sizeof(fuse_mkdir_in);
+ return (in.header.opcode == FUSE_MKDIR &&
+ in.body.mkdir.mode == (S_IFDIR | mode) &&
+ in.body.mkdir.umask == mask &&
+ (0 == strcmp(RELPATH, name)));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.create.entry.attr.mode = S_IFDIR | mode;
+ out.body.create.entry.nodeid = ino;
+ out.body.create.entry.entry_valid = UINT64_MAX;
+ out.body.create.entry.attr_valid = UINT64_MAX;
+ })));
+ // FUSE_FORGET happens asynchronously, so it may or may not arrive
+ // before the test completes.
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_FORGET);
+ }, Eq(true)),
+ _)
+ ).Times(AtMost(1))
+ .WillOnce(Invoke([=](auto in __unused, auto &out __unused) { }));
+
+ ASSERT_EQ(-1, mkdir(FULLPATH, mode));
+ ASSERT_EQ(EIO, errno);
+}
+
+TEST_F(Mkdir_7_8, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ mode_t mode = 0755;
+ uint64_t ino = 42;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *name = (const char*)in.body.bytes +
+ sizeof(fuse_mkdir_in);
+ return (in.header.opcode == FUSE_MKDIR &&
+ in.body.mkdir.mode == (S_IFDIR | mode) &&
+ (0 == strcmp(RELPATH, name)));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry_7_8);
+ out.body.create.entry.attr.mode = S_IFDIR | mode;
+ out.body.create.entry.nodeid = ino;
+ out.body.create.entry.entry_valid = UINT64_MAX;
+ out.body.create.entry.attr_valid = UINT64_MAX;
+ })));
+
+ ASSERT_EQ(0, mkdir(FULLPATH, mode)) << strerror(errno);
+}
diff --git a/tests/sys/fs/fusefs/mknod.cc b/tests/sys/fs/fusefs/mknod.cc
new file mode 100644
index 000000000000..eb745f19acd5
--- /dev/null
+++ b/tests/sys/fs/fusefs/mknod.cc
@@ -0,0 +1,318 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <semaphore.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+#ifndef VNOVAL
+#define VNOVAL (-1) /* Defined in sys/vnode.h */
+#endif
+
+class Mknod: public FuseTest {
+
+mode_t m_oldmask;
+const static mode_t c_umask = 022;
+
+public:
+
+Mknod() {
+ m_oldmask = umask(c_umask);
+}
+
+virtual void SetUp() {
+ if (geteuid() != 0) {
+ GTEST_SKIP() << "Only root may use most mknod(2) variations";
+ }
+ FuseTest::SetUp();
+}
+
+virtual void TearDown() {
+ FuseTest::TearDown();
+ (void)umask(m_oldmask);
+}
+
+/* Test an OK creation of a file with the given mode and device number */
+void expect_mknod(uint64_t parent_ino, const char* relpath, uint64_t ino,
+ mode_t mode, dev_t dev)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *name = (const char*)in.body.bytes +
+ sizeof(fuse_mknod_in);
+ return (in.header.nodeid == parent_ino &&
+ in.header.opcode == FUSE_MKNOD &&
+ in.body.mknod.mode == mode &&
+ in.body.mknod.rdev == (uint32_t)dev &&
+ in.body.mknod.umask == c_umask &&
+ (0 == strcmp(relpath, name)));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.entry_valid = UINT64_MAX;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.attr.rdev = dev;
+ })));
+}
+
+};
+
+class Mknod_7_11: public FuseTest {
+public:
+virtual void SetUp() {
+ m_kernel_minor_version = 11;
+ if (geteuid() != 0) {
+ GTEST_SKIP() << "Only root may use most mknod(2) variations";
+ }
+ FuseTest::SetUp();
+}
+
+void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
+{
+ FuseTest::expect_lookup_7_8(relpath, ino, S_IFREG | 0644, size, 1);
+}
+
+/* Test an OK creation of a file with the given mode and device number */
+void expect_mknod(uint64_t parent_ino, const char* relpath, uint64_t ino,
+ mode_t mode, dev_t dev)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *name = (const char*)in.body.bytes +
+ FUSE_COMPAT_MKNOD_IN_SIZE;
+ return (in.header.nodeid == parent_ino &&
+ in.header.opcode == FUSE_MKNOD &&
+ in.body.mknod.mode == mode &&
+ in.body.mknod.rdev == (uint32_t)dev &&
+ (0 == strcmp(relpath, name)));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.entry_valid = UINT64_MAX;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.attr.rdev = dev;
+ })));
+}
+
+};
+
+/*
+ * mknod(2) should be able to create block devices on a FUSE filesystem. Even
+ * though FreeBSD doesn't use block devices, this is useful when copying media
+ * from or preparing media for other operating systems.
+ */
+TEST_F(Mknod, blk)
+{
+ const char FULLPATH[] = "mountpoint/some_node";
+ const char RELPATH[] = "some_node";
+ mode_t mode = S_IFBLK | 0755;
+ dev_t rdev = 0xfe00; /* /dev/vda's device number on Linux */
+ uint64_t ino = 42;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_mknod(FUSE_ROOT_ID, RELPATH, ino, mode, rdev);
+
+ EXPECT_EQ(0, mknod(FULLPATH, mode, rdev)) << strerror(errno);
+}
+
+TEST_F(Mknod, chr)
+{
+ const char FULLPATH[] = "mountpoint/some_node";
+ const char RELPATH[] = "some_node";
+ mode_t mode = S_IFCHR | 0755;
+ dev_t rdev = 54; /* /dev/fuse's device number */
+ uint64_t ino = 42;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_mknod(FUSE_ROOT_ID, RELPATH, ino, mode, rdev);
+
+ EXPECT_EQ(0, mknod(FULLPATH, mode, rdev)) << strerror(errno);
+}
+
+/*
+ * The daemon is responsible for checking file permissions (unless the
+ * default_permissions mount option was used)
+ */
+TEST_F(Mknod, eperm)
+{
+ const char FULLPATH[] = "mountpoint/some_node";
+ const char RELPATH[] = "some_node";
+ mode_t mode = S_IFIFO | 0755;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *name = (const char*)in.body.bytes +
+ sizeof(fuse_mknod_in);
+ return (in.header.opcode == FUSE_MKNOD &&
+ in.body.mknod.mode == mode &&
+ (0 == strcmp(RELPATH, name)));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(EPERM)));
+ EXPECT_NE(0, mkfifo(FULLPATH, mode));
+ EXPECT_EQ(EPERM, errno);
+}
+
+TEST_F(Mknod, fifo)
+{
+ const char FULLPATH[] = "mountpoint/some_node";
+ const char RELPATH[] = "some_node";
+ mode_t mode = S_IFIFO | 0755;
+ dev_t rdev = VNOVAL; /* Fifos don't have device numbers */
+ uint64_t ino = 42;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_mknod(FUSE_ROOT_ID, RELPATH, ino, mode, rdev);
+
+ EXPECT_EQ(0, mkfifo(FULLPATH, mode)) << strerror(errno);
+}
+
+/*
+ * Create a unix-domain socket.
+ *
+ * This test case doesn't actually need root privileges.
+ */
+TEST_F(Mknod, socket)
+{
+ const char FULLPATH[] = "mountpoint/some_node";
+ const char RELPATH[] = "some_node";
+ mode_t mode = S_IFSOCK | 0755;
+ struct sockaddr_un sa;
+ int fd;
+ dev_t rdev = -1; /* Really it's a don't care */
+ uint64_t ino = 42;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_mknod(FUSE_ROOT_ID, RELPATH, ino, mode, rdev);
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ ASSERT_LE(0, fd) << strerror(errno);
+ sa.sun_family = AF_UNIX;
+ strlcpy(sa.sun_path, FULLPATH, sizeof(sa.sun_path));
+ sa.sun_len = sizeof(FULLPATH);
+ ASSERT_EQ(0, bind(fd, (struct sockaddr*)&sa, sizeof(sa)))
+ << strerror(errno);
+
+ leak(fd);
+}
+
+/*
+ * Nothing bad should happen if the server returns the parent's inode number
+ * for the newly created file. Regression test for bug 263662.
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=263662
+ */
+TEST_F(Mknod, parent_inode)
+{
+ const char FULLPATH[] = "mountpoint/parent/some_node";
+ const char PPATH[] = "parent";
+ const char RELPATH[] = "some_node";
+ mode_t mode = S_IFSOCK | 0755;
+ struct sockaddr_un sa;
+ sem_t sem;
+ int fd;
+ dev_t rdev = -1; /* Really it's a don't care */
+ uint64_t ino = 42;
+
+ ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
+
+ expect_lookup(PPATH, ino, S_IFDIR | 0755, 0, 1);
+ EXPECT_LOOKUP(ino, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_mknod(ino, RELPATH, ino, mode, rdev);
+ expect_forget(ino, 1, &sem);
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ ASSERT_LE(0, fd) << strerror(errno);
+ sa.sun_family = AF_UNIX;
+ strlcpy(sa.sun_path, FULLPATH, sizeof(sa.sun_path));
+ sa.sun_len = sizeof(FULLPATH);
+ ASSERT_EQ(-1, bind(fd, (struct sockaddr*)&sa, sizeof(sa)));
+ ASSERT_EQ(EIO, errno);
+
+ leak(fd);
+ sem_wait(&sem);
+ sem_destroy(&sem);
+}
+
+/*
+ * fusefs(4) lacks VOP_WHITEOUT support. No bugzilla entry, because that's a
+ * feature, not a bug
+ */
+TEST_F(Mknod, DISABLED_whiteout)
+{
+ const char FULLPATH[] = "mountpoint/some_node";
+ const char RELPATH[] = "some_node";
+ mode_t mode = S_IFWHT | 0755;
+ dev_t rdev = VNOVAL; /* whiteouts don't have device numbers */
+ uint64_t ino = 42;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_mknod(FUSE_ROOT_ID, RELPATH, ino, mode, rdev);
+
+ EXPECT_EQ(0, mknod(FULLPATH, mode, 0)) << strerror(errno);
+}
+
+/* A server built at protocol version 7.11 or earlier can still use mknod */
+TEST_F(Mknod_7_11, fifo)
+{
+ const char FULLPATH[] = "mountpoint/some_node";
+ const char RELPATH[] = "some_node";
+ mode_t mode = S_IFIFO | 0755;
+ dev_t rdev = VNOVAL;
+ uint64_t ino = 42;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_mknod(FUSE_ROOT_ID, RELPATH, ino, mode, rdev);
+
+ EXPECT_EQ(0, mkfifo(FULLPATH, mode)) << strerror(errno);
+}
diff --git a/tests/sys/fs/fusefs/mockfs.cc b/tests/sys/fs/fusefs/mockfs.cc
new file mode 100644
index 000000000000..65cdc3919652
--- /dev/null
+++ b/tests/sys/fs/fusefs/mockfs.cc
@@ -0,0 +1,1060 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <sys/param.h>
+
+#include <sys/mount.h>
+#include <sys/select.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/user.h>
+
+#include <fcntl.h>
+#include <libutil.h>
+#include <mntopts.h> // for build_iovec
+#include <poll.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+}
+
+#include <cinttypes>
+
+#include <gtest/gtest.h>
+
+#include "mockfs.hh"
+
+using namespace testing;
+
+int verbosity = 0;
+
+const char* opcode2opname(uint32_t opcode)
+{
+ const char* table[] = {
+ "Unknown (opcode 0)",
+ "LOOKUP",
+ "FORGET",
+ "GETATTR",
+ "SETATTR",
+ "READLINK",
+ "SYMLINK",
+ "Unknown (opcode 7)",
+ "MKNOD",
+ "MKDIR",
+ "UNLINK",
+ "RMDIR",
+ "RENAME",
+ "LINK",
+ "OPEN",
+ "READ",
+ "WRITE",
+ "STATFS",
+ "RELEASE",
+ "Unknown (opcode 19)",
+ "FSYNC",
+ "SETXATTR",
+ "GETXATTR",
+ "LISTXATTR",
+ "REMOVEXATTR",
+ "FLUSH",
+ "INIT",
+ "OPENDIR",
+ "READDIR",
+ "RELEASEDIR",
+ "FSYNCDIR",
+ "GETLK",
+ "SETLK",
+ "SETLKW",
+ "ACCESS",
+ "CREATE",
+ "INTERRUPT",
+ "BMAP",
+ "DESTROY",
+ "IOCTL",
+ "POLL",
+ "NOTIFY_REPLY",
+ "BATCH_FORGET",
+ "FALLOCATE",
+ "READDIRPLUS",
+ "RENAME2",
+ "LSEEK",
+ "COPY_FILE_RANGE",
+ };
+ if (opcode >= nitems(table))
+ return ("Unknown (opcode > max)");
+ else
+ return (table[opcode]);
+}
+
+ProcessMockerT
+ReturnErrno(int error)
+{
+ return([=](auto in, auto &out) {
+ std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
+ out0->header.unique = in.header.unique;
+ out0->header.error = -error;
+ out0->header.len = sizeof(out0->header);
+ out.push_back(std::move(out0));
+ });
+}
+
+/* Helper function used for returning negative cache entries for LOOKUP */
+ProcessMockerT
+ReturnNegativeCache(const struct timespec *entry_valid)
+{
+ return([=](auto in, auto &out) {
+ /* nodeid means ENOENT and cache it */
+ std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
+ out0->body.entry.nodeid = 0;
+ out0->header.unique = in.header.unique;
+ out0->header.error = 0;
+ out0->body.entry.entry_valid = entry_valid->tv_sec;
+ out0->body.entry.entry_valid_nsec = entry_valid->tv_nsec;
+ SET_OUT_HEADER_LEN(*out0, entry);
+ out.push_back(std::move(out0));
+ });
+}
+
+ProcessMockerT
+ReturnImmediate(std::function<void(const mockfs_buf_in& in,
+ struct mockfs_buf_out &out)> f)
+{
+ return([=](auto& in, auto &out) {
+ std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
+ out0->header.unique = in.header.unique;
+ f(in, *out0);
+ out.push_back(std::move(out0));
+ });
+}
+
+void sigint_handler(int __unused sig) {
+ // Don't do anything except interrupt the daemon's read(2) call
+}
+
+void MockFS::debug_request(const mockfs_buf_in &in, ssize_t buflen)
+{
+ printf("%-11s ino=%2" PRIu64, opcode2opname(in.header.opcode),
+ in.header.nodeid);
+ if (verbosity > 1) {
+ printf(" uid=%5u gid=%5u pid=%5u unique=%" PRIu64 " len=%u"
+ " buflen=%zd",
+ in.header.uid, in.header.gid, in.header.pid,
+ in.header.unique, in.header.len, buflen);
+ }
+ switch (in.header.opcode) {
+ const char *name, *value;
+
+ case FUSE_ACCESS:
+ printf(" mask=%#x", in.body.access.mask);
+ break;
+ case FUSE_BMAP:
+ printf(" block=%" PRIx64 " blocksize=%#x",
+ in.body.bmap.block, in.body.bmap.blocksize);
+ break;
+ case FUSE_COPY_FILE_RANGE:
+ printf(" off_in=%" PRIu64 " ino_out=%" PRIu64
+ " off_out=%" PRIu64 " size=%" PRIu64,
+ in.body.copy_file_range.off_in,
+ in.body.copy_file_range.nodeid_out,
+ in.body.copy_file_range.off_out,
+ in.body.copy_file_range.len);
+ if (verbosity > 1)
+ printf(" fh_in=%" PRIu64 " fh_out=%" PRIu64
+ " flags=%" PRIx64,
+ in.body.copy_file_range.fh_in,
+ in.body.copy_file_range.fh_out,
+ in.body.copy_file_range.flags);
+ break;
+ case FUSE_CREATE:
+ if (m_kernel_minor_version >= 12)
+ name = (const char*)in.body.bytes +
+ sizeof(fuse_create_in);
+ else
+ name = (const char*)in.body.bytes +
+ sizeof(fuse_open_in);
+ printf(" flags=%#x name=%s",
+ in.body.open.flags, name);
+ break;
+ case FUSE_FALLOCATE:
+ printf(" fh=%#" PRIx64 " offset=%" PRIu64
+ " length=%" PRIx64 " mode=%#x",
+ in.body.fallocate.fh,
+ in.body.fallocate.offset,
+ in.body.fallocate.length,
+ in.body.fallocate.mode);
+ break;
+ case FUSE_FLUSH:
+ printf(" fh=%#" PRIx64 " lock_owner=%" PRIu64,
+ in.body.flush.fh,
+ in.body.flush.lock_owner);
+ break;
+ case FUSE_FORGET:
+ printf(" nlookup=%" PRIu64, in.body.forget.nlookup);
+ break;
+ case FUSE_FSYNC:
+ printf(" flags=%#x", in.body.fsync.fsync_flags);
+ break;
+ case FUSE_FSYNCDIR:
+ printf(" flags=%#x", in.body.fsyncdir.fsync_flags);
+ break;
+ case FUSE_GETLK:
+ printf(" fh=%#" PRIx64
+ " type=%u pid=%u",
+ in.body.getlk.fh,
+ in.body.getlk.lk.type,
+ in.body.getlk.lk.pid);
+ if (verbosity >= 2) {
+ printf(" range=[%" PRIi64 ":%" PRIi64 "]",
+ in.body.getlk.lk.start,
+ in.body.getlk.lk.end);
+ }
+ break;
+ case FUSE_INTERRUPT:
+ printf(" unique=%" PRIu64, in.body.interrupt.unique);
+ break;
+ case FUSE_LINK:
+ printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid);
+ break;
+ case FUSE_LISTXATTR:
+ printf(" size=%" PRIu32, in.body.listxattr.size);
+ break;
+ case FUSE_LOOKUP:
+ printf(" %s", in.body.lookup);
+ break;
+ case FUSE_LSEEK:
+ switch (in.body.lseek.whence) {
+ case SEEK_HOLE:
+ printf(" SEEK_HOLE offset=%jd",
+ in.body.lseek.offset);
+ break;
+ case SEEK_DATA:
+ printf(" SEEK_DATA offset=%jd",
+ in.body.lseek.offset);
+ break;
+ default:
+ printf(" whence=%u offset=%jd",
+ in.body.lseek.whence, in.body.lseek.offset);
+ break;
+ }
+ break;
+ case FUSE_MKDIR:
+ name = (const char*)in.body.bytes +
+ sizeof(fuse_mkdir_in);
+ printf(" name=%s mode=%#o umask=%#o", name,
+ in.body.mkdir.mode, in.body.mkdir.umask);
+ break;
+ case FUSE_MKNOD:
+ if (m_kernel_minor_version >= 12)
+ name = (const char*)in.body.bytes +
+ sizeof(fuse_mknod_in);
+ else
+ name = (const char*)in.body.bytes +
+ FUSE_COMPAT_MKNOD_IN_SIZE;
+ printf(" mode=%#o rdev=%x umask=%#o name=%s",
+ in.body.mknod.mode, in.body.mknod.rdev,
+ in.body.mknod.umask, name);
+ break;
+ case FUSE_OPEN:
+ printf(" flags=%#x", in.body.open.flags);
+ break;
+ case FUSE_OPENDIR:
+ printf(" flags=%#x", in.body.opendir.flags);
+ break;
+ case FUSE_READ:
+ printf(" offset=%" PRIu64 " size=%u",
+ in.body.read.offset,
+ in.body.read.size);
+ if (verbosity > 1)
+ printf(" flags=%#x", in.body.read.flags);
+ break;
+ case FUSE_READDIR:
+ printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u",
+ in.body.readdir.fh, in.body.readdir.offset,
+ in.body.readdir.size);
+ break;
+ case FUSE_RELEASE:
+ printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64,
+ in.body.release.fh,
+ in.body.release.flags,
+ in.body.release.lock_owner);
+ break;
+ case FUSE_RENAME:
+ {
+ const char *src = (const char*)in.body.bytes +
+ sizeof(fuse_rename_in);
+ const char *dst = src + strlen(src) + 1;
+ printf(" src=%s newdir=%" PRIu64 " dst=%s",
+ src, in.body.rename.newdir, dst);
+ }
+ break;
+ case FUSE_SETATTR:
+ if (verbosity <= 1) {
+ printf(" valid=%#x", in.body.setattr.valid);
+ break;
+ }
+ if (in.body.setattr.valid & FATTR_MODE)
+ printf(" mode=%#o", in.body.setattr.mode);
+ if (in.body.setattr.valid & FATTR_UID)
+ printf(" uid=%u", in.body.setattr.uid);
+ if (in.body.setattr.valid & FATTR_GID)
+ printf(" gid=%u", in.body.setattr.gid);
+ if (in.body.setattr.valid & FATTR_SIZE)
+ printf(" size=%" PRIu64, in.body.setattr.size);
+ if (in.body.setattr.valid & FATTR_ATIME)
+ printf(" atime=%" PRIu64 ".%u",
+ in.body.setattr.atime,
+ in.body.setattr.atimensec);
+ if (in.body.setattr.valid & FATTR_MTIME)
+ printf(" mtime=%" PRIu64 ".%u",
+ in.body.setattr.mtime,
+ in.body.setattr.mtimensec);
+ if (in.body.setattr.valid & FATTR_FH)
+ printf(" fh=%" PRIu64 "", in.body.setattr.fh);
+ break;
+ case FUSE_SETLK:
+ printf(" fh=%#" PRIx64 " owner=%" PRIu64
+ " type=%u pid=%u",
+ in.body.setlk.fh, in.body.setlk.owner,
+ in.body.setlk.lk.type,
+ in.body.setlk.lk.pid);
+ if (verbosity >= 2) {
+ printf(" range=[%" PRIi64 ":%" PRIi64 "]",
+ in.body.setlk.lk.start,
+ in.body.setlk.lk.end);
+ }
+ break;
+ case FUSE_SETXATTR:
+ /*
+ * In theory neither the xattr name and value need be
+ * ASCII, but in this test suite they always are.
+ */
+ name = (const char*)in.body.bytes +
+ sizeof(fuse_setxattr_in);
+ value = name + strlen(name) + 1;
+ printf(" %s=%s", name, value);
+ break;
+ case FUSE_WRITE:
+ printf(" fh=%#" PRIx64 " offset=%" PRIu64
+ " size=%u write_flags=%u",
+ in.body.write.fh,
+ in.body.write.offset, in.body.write.size,
+ in.body.write.write_flags);
+ if (verbosity > 1)
+ printf(" flags=%#x", in.body.write.flags);
+ break;
+ default:
+ break;
+ }
+ printf("\n");
+}
+
+/*
+ * Debug a FUSE response.
+ *
+ * This is mostly useful for asynchronous notifications, which don't correspond
+ * to any request
+ */
+void MockFS::debug_response(const mockfs_buf_out &out) {
+ const char *name;
+
+ if (verbosity == 0)
+ return;
+
+ switch (out.header.error) {
+ case FUSE_NOTIFY_INVAL_ENTRY:
+ name = (const char*)out.body.bytes +
+ sizeof(fuse_notify_inval_entry_out);
+ printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n",
+ out.body.inval_entry.parent, name);
+ break;
+ case FUSE_NOTIFY_INVAL_INODE:
+ printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64
+ " len=%" PRIi64 "\n",
+ out.body.inval_inode.ino,
+ out.body.inval_inode.off,
+ out.body.inval_inode.len);
+ break;
+ case FUSE_NOTIFY_STORE:
+ printf("<- STORE ino=%" PRIu64 " off=%" PRIu64
+ " size=%" PRIu32 "\n",
+ out.body.store.nodeid,
+ out.body.store.offset,
+ out.body.store.size);
+ break;
+ default:
+ break;
+ }
+}
+
+MockFS::MockFS(int max_read, int max_readahead, bool allow_other,
+ bool default_permissions,
+ bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
+ uint32_t kernel_minor_version, uint32_t max_write, bool async,
+ bool noclusterr, unsigned time_gran, bool nointr, bool noatime,
+ const char *fsname, const char *subtype, bool no_auto_init)
+ : m_daemon_id(NULL),
+ m_kernel_minor_version(kernel_minor_version),
+ m_kq(pm == KQ ? kqueue() : -1),
+ m_maxread(max_read),
+ m_maxreadahead(max_readahead),
+ m_pid(getpid()),
+ m_uniques(new std::unordered_set<uint64_t>),
+ m_pm(pm),
+ m_time_gran(time_gran),
+ m_child_pid(-1),
+ m_maxwrite(MIN(max_write, max_max_write)),
+ m_nready(-1),
+ m_quit(false)
+{
+ struct sigaction sa;
+ struct iovec *iov = NULL;
+ int iovlen = 0;
+ char fdstr[15];
+ const bool trueval = true;
+
+ /*
+ * Kyua sets pwd to a testcase-unique tempdir; no need to use
+ * mkdtemp
+ */
+ /*
+ * googletest doesn't allow ASSERT_ in constructors, so we must throw
+ * instead.
+ */
+ if (mkdir("mountpoint" , 0755) && errno != EEXIST)
+ throw(std::system_error(errno, std::system_category(),
+ "Couldn't make mountpoint directory"));
+
+ switch (m_pm) {
+ case BLOCKING:
+ m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
+ break;
+ default:
+ m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK);
+ break;
+ }
+ if (m_fuse_fd < 0)
+ throw(std::system_error(errno, std::system_category(),
+ "Couldn't open /dev/fuse"));
+
+ build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
+ build_iovec(&iov, &iovlen, "fspath",
+ __DECONST(void *, "mountpoint"), -1);
+ build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
+ sprintf(fdstr, "%d", m_fuse_fd);
+ build_iovec(&iov, &iovlen, "fd", fdstr, -1);
+ if (m_maxread > 0) {
+ char val[10];
+
+ snprintf(val, sizeof(val), "%d", m_maxread);
+ build_iovec(&iov, &iovlen, "max_read=", &val, -1);
+ }
+ if (allow_other) {
+ build_iovec(&iov, &iovlen, "allow_other",
+ __DECONST(void*, &trueval), sizeof(bool));
+ }
+ if (default_permissions) {
+ build_iovec(&iov, &iovlen, "default_permissions",
+ __DECONST(void*, &trueval), sizeof(bool));
+ }
+ if (push_symlinks_in) {
+ build_iovec(&iov, &iovlen, "push_symlinks_in",
+ __DECONST(void*, &trueval), sizeof(bool));
+ }
+ if (ro) {
+ build_iovec(&iov, &iovlen, "ro",
+ __DECONST(void*, &trueval), sizeof(bool));
+ }
+ if (async) {
+ build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval),
+ sizeof(bool));
+ }
+ if (noatime) {
+ build_iovec(&iov, &iovlen, "noatime",
+ __DECONST(void*, &trueval), sizeof(bool));
+ }
+ if (noclusterr) {
+ build_iovec(&iov, &iovlen, "noclusterr",
+ __DECONST(void*, &trueval), sizeof(bool));
+ }
+ if (nointr) {
+ build_iovec(&iov, &iovlen, "nointr",
+ __DECONST(void*, &trueval), sizeof(bool));
+ } else {
+ build_iovec(&iov, &iovlen, "intr",
+ __DECONST(void*, &trueval), sizeof(bool));
+ }
+ if (*fsname) {
+ build_iovec(&iov, &iovlen, "fsname=",
+ __DECONST(void*, fsname), -1);
+ }
+ if (*subtype) {
+ build_iovec(&iov, &iovlen, "subtype=",
+ __DECONST(void*, subtype), -1);
+ }
+ if (nmount(iov, iovlen, 0))
+ throw(std::system_error(errno, std::system_category(),
+ "Couldn't mount filesystem"));
+ free_iovec(&iov, &iovlen);
+
+ // Setup default handler
+ ON_CALL(*this, process(_, _))
+ .WillByDefault(Invoke(this, &MockFS::process_default));
+
+ if (!no_auto_init)
+ init(flags);
+
+ bzero(&sa, sizeof(sa));
+ sa.sa_handler = sigint_handler;
+ sa.sa_flags = 0; /* Don't set SA_RESTART! */
+ if (0 != sigaction(SIGUSR1, &sa, NULL))
+ throw(std::system_error(errno, std::system_category(),
+ "Couldn't handle SIGUSR1"));
+ if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
+ throw(std::system_error(errno, std::system_category(),
+ "Couldn't Couldn't start fuse thread"));
+}
+
+MockFS::~MockFS() {
+ kill_daemon();
+ join_daemon();
+ ::unmount("mountpoint", MNT_FORCE);
+ rmdir("mountpoint");
+ if (m_kq >= 0)
+ close(m_kq);
+}
+
+void MockFS::audit_request(const mockfs_buf_in &in, ssize_t buflen) {
+ uint32_t inlen = in.header.len;
+ size_t fih = sizeof(in.header);
+ switch (in.header.opcode) {
+ case FUSE_LOOKUP:
+ case FUSE_RMDIR:
+ case FUSE_SYMLINK:
+ case FUSE_UNLINK:
+ EXPECT_GT(inlen, fih) << "Missing request filename";
+ // No redundant information for checking buflen
+ break;
+ case FUSE_FORGET:
+ EXPECT_EQ(inlen, fih + sizeof(in.body.forget));
+ EXPECT_EQ((size_t)buflen, inlen);
+ break;
+ case FUSE_GETATTR:
+ EXPECT_EQ(inlen, fih + sizeof(in.body.getattr));
+ EXPECT_EQ((size_t)buflen, inlen);
+ break;
+ case FUSE_SETATTR:
+ EXPECT_EQ(inlen, fih + sizeof(in.body.setattr));
+ EXPECT_EQ((size_t)buflen, inlen);
+ break;
+ case FUSE_READLINK:
+ EXPECT_EQ(inlen, fih) << "Unexpected request body";
+ EXPECT_EQ((size_t)buflen, inlen);
+ break;
+ case FUSE_MKNOD:
+ {
+ size_t s;
+ if (m_kernel_minor_version >= 12)
+ s = sizeof(in.body.mknod);
+ else
+ s = FUSE_COMPAT_MKNOD_IN_SIZE;
+ EXPECT_GE(inlen, fih + s) << "Missing request body";
+ EXPECT_GT(inlen, fih + s) << "Missing request filename";
+ // No redundant information for checking buflen
+ break;
+ }
+ case FUSE_MKDIR:
+ EXPECT_GE(inlen, fih + sizeof(in.body.mkdir)) <<
+ "Missing request body";
+ EXPECT_GT(inlen, fih + sizeof(in.body.mkdir)) <<
+ "Missing request filename";
+ // No redundant information for checking buflen
+ break;
+ case FUSE_RENAME:
+ EXPECT_GE(inlen, fih + sizeof(in.body.rename)) <<
+ "Missing request body";
+ EXPECT_GT(inlen, fih + sizeof(in.body.rename)) <<
+ "Missing request filename";
+ // No redundant information for checking buflen
+ break;
+ case FUSE_LINK:
+ EXPECT_GE(inlen, fih + sizeof(in.body.link)) <<
+ "Missing request body";
+ EXPECT_GT(inlen, fih + sizeof(in.body.link)) <<
+ "Missing request filename";
+ // No redundant information for checking buflen
+ break;
+ case FUSE_OPEN:
+ EXPECT_EQ(inlen, fih + sizeof(in.body.open));
+ EXPECT_EQ((size_t)buflen, inlen);
+ break;
+ case FUSE_READ:
+ EXPECT_EQ(inlen, fih + sizeof(in.body.read));
+ EXPECT_EQ((size_t)buflen, inlen);
+ break;
+ case FUSE_WRITE:
+ {
+ size_t s;
+
+ if (m_kernel_minor_version >= 9)
+ s = sizeof(in.body.write);
+ else
+ s = FUSE_COMPAT_WRITE_IN_SIZE;
+ // I suppose a 0-byte write should be allowed
+ EXPECT_GE(inlen, fih + s) << "Missing request body";
+ EXPECT_EQ((size_t)buflen, fih + s + in.body.write.size);
+ break;
+ }
+ case FUSE_DESTROY:
+ case FUSE_STATFS:
+ EXPECT_EQ(inlen, fih);
+ EXPECT_EQ((size_t)buflen, inlen);
+ break;
+ case FUSE_RELEASE:
+ EXPECT_EQ(inlen, fih + sizeof(in.body.release));
+ EXPECT_EQ((size_t)buflen, inlen);
+ break;
+ case FUSE_FSYNC:
+ case FUSE_FSYNCDIR:
+ EXPECT_EQ(inlen, fih + sizeof(in.body.fsync));
+ EXPECT_EQ((size_t)buflen, inlen);
+ break;
+ case FUSE_SETXATTR:
+ EXPECT_GE(inlen, fih + sizeof(in.body.setxattr)) <<
+ "Missing request body";
+ EXPECT_GT(inlen, fih + sizeof(in.body.setxattr)) <<
+ "Missing request attribute name";
+ // No redundant information for checking buflen
+ break;
+ case FUSE_GETXATTR:
+ EXPECT_GE(inlen, fih + sizeof(in.body.getxattr)) <<
+ "Missing request body";
+ EXPECT_GT(inlen, fih + sizeof(in.body.getxattr)) <<
+ "Missing request attribute name";
+ // No redundant information for checking buflen
+ break;
+ case FUSE_LISTXATTR:
+ EXPECT_EQ(inlen, fih + sizeof(in.body.listxattr));
+ EXPECT_EQ((size_t)buflen, inlen);
+ break;
+ case FUSE_REMOVEXATTR:
+ EXPECT_GT(inlen, fih) << "Missing request attribute name";
+ // No redundant information for checking buflen
+ break;
+ case FUSE_FLUSH:
+ EXPECT_EQ(inlen, fih + sizeof(in.body.flush));
+ EXPECT_EQ((size_t)buflen, inlen);
+ break;
+ case FUSE_INIT:
+ EXPECT_EQ(inlen, fih + sizeof(in.body.init));
+ EXPECT_EQ((size_t)buflen, inlen);
+ break;
+ case FUSE_OPENDIR:
+ EXPECT_EQ(inlen, fih + sizeof(in.body.opendir));
+ EXPECT_EQ((size_t)buflen, inlen);
+ break;
+ case FUSE_READDIR:
+ EXPECT_EQ(inlen, fih + sizeof(in.body.readdir));
+ EXPECT_EQ((size_t)buflen, inlen);
+ break;
+ case FUSE_RELEASEDIR:
+ EXPECT_EQ(inlen, fih + sizeof(in.body.releasedir));
+ EXPECT_EQ((size_t)buflen, inlen);
+ break;
+ case FUSE_GETLK:
+ EXPECT_EQ(inlen, fih + sizeof(in.body.getlk));
+ EXPECT_EQ((size_t)buflen, inlen);
+ break;
+ case FUSE_SETLK:
+ case FUSE_SETLKW:
+ EXPECT_EQ(inlen, fih + sizeof(in.body.setlk));
+ EXPECT_EQ((size_t)buflen, inlen);
+ break;
+ case FUSE_ACCESS:
+ EXPECT_EQ(inlen, fih + sizeof(in.body.access));
+ EXPECT_EQ((size_t)buflen, inlen);
+ break;
+ case FUSE_CREATE:
+ EXPECT_GE(inlen, fih + sizeof(in.body.create)) <<
+ "Missing request body";
+ EXPECT_GT(inlen, fih + sizeof(in.body.create)) <<
+ "Missing request filename";
+ // No redundant information for checking buflen
+ break;
+ case FUSE_INTERRUPT:
+ EXPECT_EQ(inlen, fih + sizeof(in.body.interrupt));
+ EXPECT_EQ((size_t)buflen, inlen);
+ break;
+ case FUSE_FALLOCATE:
+ EXPECT_EQ(inlen, fih + sizeof(in.body.fallocate));
+ EXPECT_EQ((size_t)buflen, inlen);
+ break;
+ case FUSE_BMAP:
+ EXPECT_EQ(inlen, fih + sizeof(in.body.bmap));
+ EXPECT_EQ((size_t)buflen, inlen);
+ break;
+ case FUSE_LSEEK:
+ EXPECT_EQ(inlen, fih + sizeof(in.body.lseek));
+ EXPECT_EQ((size_t)buflen, inlen);
+ break;
+ case FUSE_COPY_FILE_RANGE:
+ EXPECT_EQ(inlen, fih + sizeof(in.body.copy_file_range));
+ EXPECT_EQ(0ul, in.body.copy_file_range.flags);
+ EXPECT_EQ((size_t)buflen, inlen);
+ break;
+ case FUSE_NOTIFY_REPLY:
+ case FUSE_BATCH_FORGET:
+ case FUSE_IOCTL:
+ case FUSE_POLL:
+ case FUSE_READDIRPLUS:
+ FAIL() << "Unsupported opcode?";
+ default:
+ FAIL() << "Unknown opcode " << in.header.opcode;
+ }
+ /* Verify that the ticket's unique value is actually unique. */
+ if (m_uniques->find(in.header.unique) != m_uniques->end())
+ FAIL() << "Non-unique \"unique\" value";
+ m_uniques->insert(in.header.unique);
+}
+
+void MockFS::init(uint32_t flags) {
+ ssize_t buflen;
+
+ std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
+ std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
+
+ read_request(*in, buflen);
+ if (verbosity > 0)
+ debug_request(*in, buflen);
+ audit_request(*in, buflen);
+ ASSERT_EQ(FUSE_INIT, in->header.opcode);
+
+ out->header.unique = in->header.unique;
+ out->header.error = 0;
+ out->body.init.major = FUSE_KERNEL_VERSION;
+ out->body.init.minor = m_kernel_minor_version;;
+ out->body.init.flags = in->body.init.flags & flags;
+ out->body.init.max_write = m_maxwrite;
+ out->body.init.max_readahead = m_maxreadahead;
+
+ if (m_kernel_minor_version < 23) {
+ SET_OUT_HEADER_LEN(*out, init_7_22);
+ } else {
+ out->body.init.time_gran = m_time_gran;
+ SET_OUT_HEADER_LEN(*out, init);
+ }
+
+ write(m_fuse_fd, out.get(), out->header.len);
+}
+
+void MockFS::kill_daemon() {
+ m_quit = true;
+ if (m_daemon_id != NULL)
+ pthread_kill(m_daemon_id, SIGUSR1);
+ // Closing the /dev/fuse file descriptor first allows unmount to
+ // succeed even if the daemon doesn't correctly respond to commands
+ // during the unmount sequence.
+ close(m_fuse_fd);
+ m_fuse_fd = -1;
+}
+
+void MockFS::join_daemon() {
+ if (m_daemon_id != NULL) {
+ pthread_join(m_daemon_id, NULL);
+ m_daemon_id = NULL;
+ }
+}
+
+void MockFS::loop() {
+ std::vector<std::unique_ptr<mockfs_buf_out>> out;
+
+ std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
+ ASSERT_TRUE(in != NULL);
+ while (!m_quit) {
+ ssize_t buflen;
+
+ bzero(in.get(), sizeof(*in));
+ read_request(*in, buflen);
+ if (m_quit)
+ break;
+ if (verbosity > 0)
+ debug_request(*in, buflen);
+ audit_request(*in, buflen);
+ if (pid_ok((pid_t)in->header.pid)) {
+ process(*in, out);
+ } else {
+ /*
+ * Reject any requests from unknown processes. Because
+ * we actually do mount a filesystem, plenty of
+ * unrelated system daemons may try to access it.
+ */
+ if (verbosity > 1)
+ printf("\tREJECTED (wrong pid %d)\n",
+ in->header.pid);
+ process_default(*in, out);
+ }
+ for (auto &it: out)
+ write_response(*it);
+ out.clear();
+ }
+}
+
+int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen)
+{
+ std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
+
+ out->header.unique = 0; /* 0 means asynchronous notification */
+ out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
+ out->body.inval_entry.parent = parent;
+ out->body.inval_entry.namelen = namelen;
+ strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
+ name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
+ out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
+ namelen;
+ debug_response(*out);
+ write_response(*out);
+ return 0;
+}
+
+int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
+{
+ std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
+
+ out->header.unique = 0; /* 0 means asynchronous notification */
+ out->header.error = FUSE_NOTIFY_INVAL_INODE;
+ out->body.inval_inode.ino = ino;
+ out->body.inval_inode.off = off;
+ out->body.inval_inode.len = len;
+ out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
+ debug_response(*out);
+ write_response(*out);
+ return 0;
+}
+
+int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size)
+{
+ std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
+
+ out->header.unique = 0; /* 0 means asynchronous notification */
+ out->header.error = FUSE_NOTIFY_STORE;
+ out->body.store.nodeid = ino;
+ out->body.store.offset = off;
+ out->body.store.size = size;
+ bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
+ out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
+ debug_response(*out);
+ write_response(*out);
+ return 0;
+}
+
+bool MockFS::pid_ok(pid_t pid) {
+ if (pid == m_pid) {
+ return (true);
+ } else if (pid == m_child_pid) {
+ return (true);
+ } else {
+ struct kinfo_proc *ki;
+ bool ok = false;
+
+ ki = kinfo_getproc(pid);
+ if (ki == NULL)
+ return (false);
+ /*
+ * Allow access by the aio daemon processes so that our tests
+ * can use aio functions
+ */
+ if (0 == strncmp("aiod", ki->ki_comm, 4))
+ ok = true;
+ free(ki);
+ return (ok);
+ }
+}
+
+void MockFS::process_default(const mockfs_buf_in& in,
+ std::vector<std::unique_ptr<mockfs_buf_out>> &out)
+{
+ std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
+ out0->header.unique = in.header.unique;
+ out0->header.error = -EOPNOTSUPP;
+ out0->header.len = sizeof(out0->header);
+ out.push_back(std::move(out0));
+}
+
+void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) {
+ int nready = 0;
+ fd_set readfds;
+ pollfd fds[1];
+ struct kevent changes[1];
+ struct kevent events[1];
+ struct timespec timeout_ts;
+ struct timeval timeout_tv;
+ const int timeout_ms = 999;
+ int timeout_int, nfds;
+ int fuse_fd;
+
+ switch (m_pm) {
+ case BLOCKING:
+ break;
+ case KQ:
+ timeout_ts.tv_sec = 0;
+ timeout_ts.tv_nsec = timeout_ms * 1'000'000;
+ while (nready == 0) {
+ EV_SET(&changes[0], m_fuse_fd, EVFILT_READ,
+ EV_ADD | EV_ONESHOT, 0, 0, 0);
+ nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
+ &timeout_ts);
+ if (m_quit)
+ return;
+ }
+ ASSERT_LE(0, nready) << strerror(errno);
+ ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
+ if (events[0].flags & EV_ERROR)
+ FAIL() << strerror(events[0].data);
+ else if (events[0].flags & EV_EOF)
+ FAIL() << strerror(events[0].fflags);
+ m_nready = events[0].data;
+ break;
+ case POLL:
+ timeout_int = timeout_ms;
+ fds[0].fd = m_fuse_fd;
+ fds[0].events = POLLIN;
+ while (nready == 0) {
+ nready = poll(fds, 1, timeout_int);
+ if (m_quit)
+ return;
+ }
+ ASSERT_LE(0, nready) << strerror(errno);
+ ASSERT_TRUE(fds[0].revents & POLLIN);
+ break;
+ case SELECT:
+ fuse_fd = m_fuse_fd;
+ if (fuse_fd < 0)
+ break;
+ timeout_tv.tv_sec = 0;
+ timeout_tv.tv_usec = timeout_ms * 1'000;
+ nfds = fuse_fd + 1;
+ while (nready == 0) {
+ FD_ZERO(&readfds);
+ FD_SET(fuse_fd, &readfds);
+ nready = select(nfds, &readfds, NULL, NULL,
+ &timeout_tv);
+ if (m_quit)
+ return;
+ }
+ ASSERT_LE(0, nready) << strerror(errno);
+ ASSERT_TRUE(FD_ISSET(fuse_fd, &readfds));
+ break;
+ default:
+ FAIL() << "not yet implemented";
+ }
+ res = read(m_fuse_fd, &in, sizeof(in));
+
+ if (res < 0 && !m_quit) {
+ m_quit = true;
+ FAIL() << "read: " << strerror(errno);
+ }
+ ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
+ /*
+ * Inconsistently, fuse_in_header.len is the size of the entire
+ * request,including header, even though fuse_out_header.len excludes
+ * the size of the header.
+ */
+ ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit);
+}
+
+void MockFS::write_response(const mockfs_buf_out &out) {
+ fd_set writefds;
+ pollfd fds[1];
+ struct kevent changes[1];
+ struct kevent events[1];
+ int nready, nfds;
+ ssize_t r;
+
+ switch (m_pm) {
+ case BLOCKING:
+ break;
+ case KQ:
+ EV_SET(&changes[0], m_fuse_fd, EVFILT_WRITE,
+ EV_ADD | EV_ONESHOT, 0, 0, 0);
+ nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
+ NULL);
+ ASSERT_LE(0, nready) << strerror(errno);
+ ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
+ if (events[0].flags & EV_ERROR)
+ FAIL() << strerror(events[0].data);
+ else if (events[0].flags & EV_EOF)
+ FAIL() << strerror(events[0].fflags);
+ m_nready = events[0].data;
+ break;
+ case POLL:
+ fds[0].fd = m_fuse_fd;
+ fds[0].events = POLLOUT;
+ nready = poll(fds, 1, INFTIM);
+ ASSERT_LE(0, nready) << strerror(errno);
+ ASSERT_EQ(1, nready) << "NULL timeout expired?";
+ ASSERT_TRUE(fds[0].revents & POLLOUT);
+ break;
+ case SELECT:
+ FD_ZERO(&writefds);
+ FD_SET(m_fuse_fd, &writefds);
+ nfds = m_fuse_fd + 1;
+ nready = select(nfds, NULL, &writefds, NULL, NULL);
+ ASSERT_LE(0, nready) << strerror(errno);
+ ASSERT_EQ(1, nready) << "NULL timeout expired?";
+ ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
+ break;
+ default:
+ FAIL() << "not yet implemented";
+ }
+ r = write(m_fuse_fd, &out, out.header.len);
+ if (out.expected_errno) {
+ ASSERT_EQ(-1, r);
+ ASSERT_EQ(out.expected_errno, errno) << strerror(errno);
+ } else {
+ if (r <= 0 && errno == EINVAL) {
+ printf("Failed to write response. unique=%" PRIu64
+ ":\n", out.header.unique);
+ }
+ ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
+ }
+}
+
+void* MockFS::service(void *pthr_data) {
+ MockFS *mock_fs = (MockFS*)pthr_data;
+
+ mock_fs->loop();
+
+ return (NULL);
+}
+
+void MockFS::unmount() {
+ ::unmount("mountpoint", 0);
+}
diff --git a/tests/sys/fs/fusefs/mockfs.hh b/tests/sys/fs/fusefs/mockfs.hh
new file mode 100644
index 000000000000..ba6f7fded9d0
--- /dev/null
+++ b/tests/sys/fs/fusefs/mockfs.hh
@@ -0,0 +1,441 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <sys/types.h>
+
+#include <pthread.h>
+
+#include "fuse_kernel.h"
+}
+
+#include <unordered_set>
+
+#include <gmock/gmock.h>
+
+#define TIME_T_MAX (std::numeric_limits<time_t>::max())
+
+/*
+ * A pseudo-fuse errno used indicate that a fuse operation should have no
+ * response, at least not immediately
+ */
+#define FUSE_NORESPONSE 9999
+
+#define SET_OUT_HEADER_LEN(out, variant) { \
+ (out).header.len = (sizeof((out).header) + \
+ sizeof((out).body.variant)); \
+}
+
+/*
+ * Create an expectation on FUSE_LOOKUP and return it so the caller can set
+ * actions.
+ *
+ * This must be a macro instead of a method because EXPECT_CALL returns a type
+ * with a deleted constructor.
+ */
+#define EXPECT_LOOKUP(parent, path) \
+ EXPECT_CALL(*m_mock, process( \
+ ResultOf([=](auto in) { \
+ return (in.header.opcode == FUSE_LOOKUP && \
+ in.header.nodeid == (parent) && \
+ strcmp(in.body.lookup, (path)) == 0); \
+ }, Eq(true)), \
+ _) \
+ )
+
+extern int verbosity;
+
+/*
+ * The maximum that a test case can set max_write, limited by the buffer
+ * supplied when reading from /dev/fuse. This limitation is imposed by
+ * fusefs-libs, but not by the FUSE protocol.
+ */
+const uint32_t max_max_write = 0x20000;
+
+
+/* This struct isn't defined by fuse_kernel.h or libfuse, but it should be */
+struct fuse_create_out {
+ struct fuse_entry_out entry;
+ struct fuse_open_out open;
+};
+
+/* Protocol 7.8 version of struct fuse_attr */
+struct fuse_attr_7_8
+{
+ uint64_t ino;
+ uint64_t size;
+ uint64_t blocks;
+ uint64_t atime;
+ uint64_t mtime;
+ uint64_t ctime;
+ uint32_t atimensec;
+ uint32_t mtimensec;
+ uint32_t ctimensec;
+ uint32_t mode;
+ uint32_t nlink;
+ uint32_t uid;
+ uint32_t gid;
+ uint32_t rdev;
+};
+
+/* Protocol 7.8 version of struct fuse_attr_out */
+struct fuse_attr_out_7_8
+{
+ uint64_t attr_valid;
+ uint32_t attr_valid_nsec;
+ uint32_t dummy;
+ struct fuse_attr_7_8 attr;
+};
+
+/* Protocol 7.8 version of struct fuse_entry_out */
+struct fuse_entry_out_7_8 {
+ uint64_t nodeid; /* Inode ID */
+ uint64_t generation; /* Inode generation: nodeid:gen must
+ be unique for the fs's lifetime */
+ uint64_t entry_valid; /* Cache timeout for the name */
+ uint64_t attr_valid; /* Cache timeout for the attributes */
+ uint32_t entry_valid_nsec;
+ uint32_t attr_valid_nsec;
+ struct fuse_attr_7_8 attr;
+};
+
+/* Output struct for FUSE_CREATE for protocol 7.8 servers */
+struct fuse_create_out_7_8 {
+ struct fuse_entry_out_7_8 entry;
+ struct fuse_open_out open;
+};
+
+/* Output struct for FUSE_INIT for protocol 7.22 and earlier servers */
+struct fuse_init_out_7_22 {
+ uint32_t major;
+ uint32_t minor;
+ uint32_t max_readahead;
+ uint32_t flags;
+ uint16_t max_background;
+ uint16_t congestion_threshold;
+ uint32_t max_write;
+};
+
+union fuse_payloads_in {
+ fuse_access_in access;
+ fuse_bmap_in bmap;
+ /*
+ * In fusefs-libs 3.4.2 and below the buffer size is fixed at 0x21000
+ * minus the header sizes. fusefs-libs 3.4.3 (and FUSE Protocol 7.29)
+ * add a FUSE_MAX_PAGES option that allows it to be greater.
+ *
+ * See fuse_kern_chan.c in fusefs-libs 2.9.9 and below, or
+ * FUSE_DEFAULT_MAX_PAGES_PER_REQ in fusefs-libs 3.4.3 and above.
+ */
+ uint8_t bytes[
+ max_max_write + 0x1000 - sizeof(struct fuse_in_header)
+ ];
+ fuse_copy_file_range_in copy_file_range;
+ fuse_create_in create;
+ fuse_fallocate_in fallocate;
+ fuse_flush_in flush;
+ fuse_fsync_in fsync;
+ fuse_fsync_in fsyncdir;
+ fuse_forget_in forget;
+ fuse_getattr_in getattr;
+ fuse_interrupt_in interrupt;
+ fuse_lk_in getlk;
+ fuse_getxattr_in getxattr;
+ fuse_init_in init;
+ fuse_link_in link;
+ fuse_listxattr_in listxattr;
+ char lookup[0];
+ fuse_lseek_in lseek;
+ fuse_mkdir_in mkdir;
+ fuse_mknod_in mknod;
+ fuse_open_in open;
+ fuse_open_in opendir;
+ fuse_read_in read;
+ fuse_read_in readdir;
+ fuse_release_in release;
+ fuse_release_in releasedir;
+ fuse_rename_in rename;
+ char rmdir[0];
+ fuse_setattr_in setattr;
+ fuse_setxattr_in setxattr;
+ fuse_lk_in setlk;
+ fuse_lk_in setlkw;
+ char unlink[0];
+ fuse_write_in write;
+};
+
+struct mockfs_buf_in {
+ fuse_in_header header;
+ union fuse_payloads_in body;
+};
+
+union fuse_payloads_out {
+ fuse_attr_out attr;
+ fuse_attr_out_7_8 attr_7_8;
+ fuse_bmap_out bmap;
+ fuse_create_out create;
+ fuse_create_out_7_8 create_7_8;
+ /*
+ * The protocol places no limits on the size of bytes. Choose
+ * a size big enough for anything we'll test.
+ */
+ uint8_t bytes[0x40000];
+ fuse_entry_out entry;
+ fuse_entry_out_7_8 entry_7_8;
+ fuse_lk_out getlk;
+ fuse_getxattr_out getxattr;
+ fuse_init_out init;
+ fuse_init_out_7_22 init_7_22;
+ fuse_lseek_out lseek;
+ /* The inval_entry structure should be followed by the entry's name */
+ fuse_notify_inval_entry_out inval_entry;
+ fuse_notify_inval_inode_out inval_inode;
+ /* The store structure should be followed by the data to store */
+ fuse_notify_store_out store;
+ fuse_listxattr_out listxattr;
+ fuse_open_out open;
+ fuse_statfs_out statfs;
+ /*
+ * The protocol places no limits on the length of the string. This is
+ * merely convenient for testing.
+ */
+ char str[80];
+ fuse_write_out write;
+};
+
+struct mockfs_buf_out {
+ fuse_out_header header;
+ union fuse_payloads_out body;
+ /* the expected errno of the write to /dev/fuse */
+ int expected_errno;
+
+ /* Default constructor: zero everything */
+ mockfs_buf_out() {
+ memset(this, 0, sizeof(*this));
+ }
+};
+
+/* A function that can be invoked in place of MockFS::process */
+typedef std::function<void (const mockfs_buf_in& in,
+ std::vector<std::unique_ptr<mockfs_buf_out>> &out)>
+ProcessMockerT;
+
+/*
+ * Helper function used for setting an error expectation for any fuse operation.
+ * The operation will return the supplied error
+ */
+ProcessMockerT ReturnErrno(int error);
+
+/* Helper function used for returning negative cache entries for LOOKUP */
+ProcessMockerT ReturnNegativeCache(const struct timespec *entry_valid);
+
+/* Helper function used for returning a single immediate response */
+ProcessMockerT ReturnImmediate(
+ std::function<void(const mockfs_buf_in& in,
+ struct mockfs_buf_out &out)> f);
+
+/* How the daemon should check /dev/fuse for readiness */
+enum poll_method {
+ BLOCKING,
+ SELECT,
+ POLL,
+ KQ
+};
+
+/*
+ * Fake FUSE filesystem
+ *
+ * "Mounts" a filesystem to a temporary directory and services requests
+ * according to the programmed expectations.
+ *
+ * Operates directly on the fusefs(4) kernel API, not the libfuse(3) user api.
+ */
+class MockFS {
+ /*
+ * thread id of the fuse daemon thread
+ *
+ * It must run in a separate thread so it doesn't deadlock with the
+ * client test code.
+ */
+ pthread_t m_daemon_id;
+
+ /* file descriptor of /dev/fuse control device */
+ volatile int m_fuse_fd;
+
+ /* The minor version of the kernel API that this mock daemon targets */
+ uint32_t m_kernel_minor_version;
+
+ int m_kq;
+
+ /*
+ * If nonzero, the maximum size in bytes of a read that the kernel will
+ * send to the server.
+ */
+ int m_maxread;
+
+ /* The max_readahead file system option */
+ uint32_t m_maxreadahead;
+
+ /* pid of the test process */
+ pid_t m_pid;
+
+ /* Every "unique" value of a fuse ticket seen so far */
+ std::unique_ptr<std::unordered_set<uint64_t>> m_uniques;
+
+ /* Method the daemon should use for I/O to and from /dev/fuse */
+ enum poll_method m_pm;
+
+ /* Timestamp granularity in nanoseconds */
+ unsigned m_time_gran;
+
+ void audit_request(const mockfs_buf_in &in, ssize_t buflen);
+ void debug_request(const mockfs_buf_in&, ssize_t buflen);
+ void debug_response(const mockfs_buf_out&);
+
+ /* Initialize a session after mounting */
+ void init(uint32_t flags);
+
+ /* Is pid from a process that might be involved in the test? */
+ bool pid_ok(pid_t pid);
+
+ /* Default request handler */
+ void process_default(const mockfs_buf_in&,
+ std::vector<std::unique_ptr<mockfs_buf_out>>&);
+
+ /* Entry point for the daemon thread */
+ static void* service(void*);
+
+ /*
+ * Read, but do not process, a single request from the kernel
+ *
+ * @param in Return storage for the FUSE request
+ * @param res Return value of read(2). If positive, the amount of
+ * data read from the fuse device.
+ */
+ void read_request(mockfs_buf_in& in, ssize_t& res);
+
+ public:
+ /* Write a single response back to the kernel */
+ void write_response(const mockfs_buf_out &out);
+
+ /* pid of child process, for two-process test cases */
+ pid_t m_child_pid;
+
+ /* Maximum size of a FUSE_WRITE write */
+ uint32_t m_maxwrite;
+
+ /*
+ * Number of events that were available from /dev/fuse after the last
+ * kevent call. Only valid when m_pm = KQ.
+ */
+ int m_nready;
+
+ /* Tell the daemon to shut down ASAP */
+ bool m_quit;
+
+ /* Create a new mockfs and mount it to a tempdir */
+ MockFS(int max_read, int max_readahead, bool allow_other,
+ bool default_permissions, bool push_symlinks_in, bool ro,
+ enum poll_method pm, uint32_t flags,
+ uint32_t kernel_minor_version, uint32_t max_write, bool async,
+ bool no_clusterr, unsigned time_gran, bool nointr,
+ bool noatime, const char *fsname, const char *subtype,
+ bool no_auto_init);
+
+ virtual ~MockFS();
+
+ /* Kill the filesystem daemon without unmounting the filesystem */
+ void kill_daemon();
+
+ /* Wait until the daemon thread terminates */
+ void join_daemon();
+
+ /* Process FUSE requests endlessly */
+ void loop();
+
+ /*
+ * Send an asynchronous notification to invalidate a directory entry.
+ * Similar to libfuse's fuse_lowlevel_notify_inval_entry
+ *
+ * This method will block until the client has responded, so it should
+ * generally be run in a separate thread from request processing.
+ *
+ * @param parent Parent directory's inode number
+ * @param name name of dirent to invalidate
+ * @param namelen size of name, including the NUL
+ */
+ int notify_inval_entry(ino_t parent, const char *name, size_t namelen);
+
+ /*
+ * Send an asynchronous notification to invalidate an inode's cached
+ * data and/or attributes. Similar to libfuse's
+ * fuse_lowlevel_notify_inval_inode.
+ *
+ * This method will block until the client has responded, so it should
+ * generally be run in a separate thread from request processing.
+ *
+ * @param ino File's inode number
+ * @param off offset at which to begin invalidation. A
+ * negative offset means to invalidate attributes
+ * only.
+ * @param len Size of region of data to invalidate. 0 means
+ * to invalidate all cached data.
+ */
+ int notify_inval_inode(ino_t ino, off_t off, ssize_t len);
+
+ /*
+ * Send an asynchronous notification to store data directly into an
+ * inode's cache. Similar to libfuse's fuse_lowlevel_notify_store.
+ *
+ * This method will block until the client has responded, so it should
+ * generally be run in a separate thread from request processing.
+ *
+ * @param ino File's inode number
+ * @param off Offset at which to store data
+ * @param data Pointer to the data to cache
+ * @param len Size of data
+ */
+ int notify_store(ino_t ino, off_t off, const void* data, ssize_t size);
+
+ /*
+ * Request handler
+ *
+ * This method is expected to provide the responses to each FUSE
+ * operation. For an immediate response, push one buffer into out.
+ * For a delayed response, push nothing. For an immediate response
+ * plus a delayed response to an earlier operation, push two bufs.
+ * Test cases must define each response using Googlemock expectations
+ */
+ MOCK_METHOD2(process, void(const mockfs_buf_in&,
+ std::vector<std::unique_ptr<mockfs_buf_out>>&));
+
+ /* Gracefully unmount */
+ void unmount();
+};
diff --git a/tests/sys/fs/fusefs/mount.cc b/tests/sys/fs/fusefs/mount.cc
new file mode 100644
index 000000000000..ece518b09f66
--- /dev/null
+++ b/tests/sys/fs/fusefs/mount.cc
@@ -0,0 +1,200 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/uio.h>
+
+#include <mntopts.h> // for build_iovec
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class Mount: public FuseTest {
+public:
+void expect_statfs() {
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_STATFS);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, statfs);
+ })));
+}
+};
+
+class Fsname: public Mount {
+ void SetUp() {
+ m_fsname = "http://something";
+ Mount::SetUp();
+ }
+};
+
+class Subtype: public Mount {
+ void SetUp() {
+ m_subtype = "myfs";
+ Mount::SetUp();
+ }
+};
+
+class UpdateOk: public Mount, public WithParamInterface<const char*> {};
+class UpdateErr: public Mount, public WithParamInterface<const char*> {};
+
+int mntflag_from_string(const char *s)
+{
+ if (0 == strcmp("MNT_RDONLY", s))
+ return MNT_RDONLY;
+ else if (0 == strcmp("MNT_NOEXEC", s))
+ return MNT_NOEXEC;
+ else if (0 == strcmp("MNT_NOSUID", s))
+ return MNT_NOSUID;
+ else if (0 == strcmp("MNT_NOATIME", s))
+ return MNT_NOATIME;
+ else if (0 == strcmp("MNT_SUIDDIR", s))
+ return MNT_SUIDDIR;
+ else if (0 == strcmp("MNT_USER", s))
+ return MNT_USER;
+ else
+ return 0;
+}
+
+TEST_F(Fsname, fsname)
+{
+ struct statfs statbuf;
+
+ expect_statfs();
+
+ ASSERT_EQ(0, statfs("mountpoint", &statbuf)) << strerror(errno);
+ ASSERT_STREQ("http://something", statbuf.f_mntfromname);
+}
+
+TEST_F(Subtype, subtype)
+{
+ struct statfs statbuf;
+
+ expect_statfs();
+
+ ASSERT_EQ(0, statfs("mountpoint", &statbuf)) << strerror(errno);
+ ASSERT_STREQ("fusefs.myfs", statbuf.f_fstypename);
+}
+
+/* Some mount options can be changed by mount -u */
+TEST_P(UpdateOk, update)
+{
+ struct statfs statbuf;
+ struct iovec *iov = NULL;
+ int iovlen = 0;
+ int flag;
+ int newflags = MNT_UPDATE | MNT_SYNCHRONOUS;
+
+ flag = mntflag_from_string(GetParam());
+ if (flag == MNT_NOSUID && 0 != geteuid())
+ GTEST_SKIP() << "Only root may clear MNT_NOSUID";
+ if (flag == MNT_SUIDDIR && 0 != geteuid())
+ GTEST_SKIP() << "Only root may set MNT_SUIDDIR";
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_STATFS);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ /*
+ * All of the fields except f_flags are don't care, and f_flags is set by
+ * the VFS
+ */
+ SET_OUT_HEADER_LEN(out, statfs);
+ })));
+
+ ASSERT_EQ(0, statfs("mountpoint", &statbuf)) << strerror(errno);
+ newflags = (statbuf.f_flags | MNT_UPDATE) ^ flag;
+
+ build_iovec(&iov, &iovlen, "fstype", (void*)statbuf.f_fstypename, -1);
+ build_iovec(&iov, &iovlen, "fspath", (void*)statbuf.f_mntonname, -1);
+ build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
+ ASSERT_EQ(0, nmount(iov, iovlen, newflags)) << strerror(errno);
+
+ ASSERT_EQ(0, statfs("mountpoint", &statbuf)) << strerror(errno);
+ EXPECT_FALSE((newflags ^ statbuf.f_flags) & flag);
+}
+
+/* Some mount options cannnot be changed by mount -u */
+TEST_P(UpdateErr, update)
+{
+ struct statfs statbuf;
+ struct iovec *iov = NULL;
+ int iovlen = 0;
+ int flag;
+ int newflags = MNT_UPDATE | MNT_SYNCHRONOUS;
+
+ flag = mntflag_from_string(GetParam());
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_STATFS);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ /*
+ * All of the fields except f_flags are don't care, and f_flags is set by
+ * the VFS
+ */
+ SET_OUT_HEADER_LEN(out, statfs);
+ })));
+
+ ASSERT_EQ(0, statfs("mountpoint", &statbuf)) << strerror(errno);
+ newflags = (statbuf.f_flags | MNT_UPDATE) ^ flag;
+
+ build_iovec(&iov, &iovlen, "fstype", (void*)statbuf.f_fstypename, -1);
+ build_iovec(&iov, &iovlen, "fspath", (void*)statbuf.f_mntonname, -1);
+ build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
+ /*
+ * Don't check nmount's return value, because vfs_domount may "fix" the
+ * options for us. The important thing is to check the final value of
+ * statbuf.f_flags below.
+ */
+ (void)nmount(iov, iovlen, newflags);
+
+ ASSERT_EQ(0, statfs("mountpoint", &statbuf)) << strerror(errno);
+ EXPECT_TRUE((newflags ^ statbuf.f_flags) & flag);
+}
+
+INSTANTIATE_TEST_SUITE_P(Mount, UpdateOk,
+ ::testing::Values("MNT_RDONLY", "MNT_NOEXEC", "MNT_NOSUID", "MNT_NOATIME",
+ "MNT_SUIDDIR")
+);
+
+INSTANTIATE_TEST_SUITE_P(Mount, UpdateErr,
+ ::testing::Values( "MNT_USER")
+);
diff --git a/tests/sys/fs/fusefs/nfs.cc b/tests/sys/fs/fusefs/nfs.cc
new file mode 100644
index 000000000000..2fa2b290f383
--- /dev/null
+++ b/tests/sys/fs/fusefs/nfs.cc
@@ -0,0 +1,480 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* This file tests functionality needed by NFS servers */
+extern "C" {
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace std;
+using namespace testing;
+
+
+class Nfs: public FuseTest {
+public:
+virtual void SetUp() {
+ if (geteuid() != 0)
+ GTEST_SKIP() << "This test requires a privileged user";
+ FuseTest::SetUp();
+}
+};
+
+class Exportable: public Nfs {
+public:
+virtual void SetUp() {
+ m_init_flags = FUSE_EXPORT_SUPPORT;
+ Nfs::SetUp();
+}
+};
+
+class Fhstat: public Exportable {};
+class FhstatNotExportable: public Nfs {};
+class Getfh: public Exportable {};
+class Readdir: public Exportable {};
+
+/* If the server returns a different generation number, then file is stale */
+TEST_F(Fhstat, estale)
+{
+ const char FULLPATH[] = "mountpoint/some_dir/.";
+ const char RELDIRPATH[] = "some_dir";
+ fhandle_t fhp;
+ struct stat sb;
+ const uint64_t ino = 42;
+ const mode_t mode = S_IFDIR | 0755;
+ Sequence seq;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH)
+ .InSequence(seq)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.ino = ino;
+ out.body.entry.generation = 1;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = 0;
+ })));
+
+ EXPECT_LOOKUP(ino, ".")
+ .InSequence(seq)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.ino = ino;
+ out.body.entry.generation = 2;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = 0;
+ })));
+
+ ASSERT_EQ(0, getfh(FULLPATH, &fhp)) << strerror(errno);
+ ASSERT_EQ(-1, fhstat(&fhp, &sb));
+ EXPECT_EQ(ESTALE, errno);
+}
+
+/* If we must lookup an entry from the server, send a LOOKUP request for "." */
+TEST_F(Fhstat, lookup_dot)
+{
+ const char FULLPATH[] = "mountpoint/some_dir/.";
+ const char RELDIRPATH[] = "some_dir";
+ fhandle_t fhp;
+ struct stat sb;
+ const uint64_t ino = 42;
+ const mode_t mode = S_IFDIR | 0755;
+ const uid_t uid = 12345;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.ino = ino;
+ out.body.entry.generation = 1;
+ out.body.entry.attr.uid = uid;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = 0;
+ })));
+
+ EXPECT_LOOKUP(ino, ".")
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.ino = ino;
+ out.body.entry.generation = 1;
+ out.body.entry.attr.uid = uid;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = 0;
+ })));
+
+ ASSERT_EQ(0, getfh(FULLPATH, &fhp)) << strerror(errno);
+ ASSERT_EQ(0, fhstat(&fhp, &sb)) << strerror(errno);
+ EXPECT_EQ(uid, sb.st_uid);
+ EXPECT_EQ(mode, sb.st_mode);
+}
+
+/* Gracefully handle failures to lookup ".". */
+TEST_F(Fhstat, lookup_dot_error)
+{
+ const char FULLPATH[] = "mountpoint/some_dir/.";
+ const char RELDIRPATH[] = "some_dir";
+ fhandle_t fhp;
+ struct stat sb;
+ const uint64_t ino = 42;
+ const mode_t mode = S_IFDIR | 0755;
+ const uid_t uid = 12345;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.ino = ino;
+ out.body.entry.generation = 1;
+ out.body.entry.attr.uid = uid;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = 0;
+ })));
+
+ EXPECT_LOOKUP(ino, ".")
+ .WillOnce(Invoke(ReturnErrno(EDOOFUS)));
+
+ ASSERT_EQ(0, getfh(FULLPATH, &fhp)) << strerror(errno);
+ ASSERT_EQ(-1, fhstat(&fhp, &sb));
+ EXPECT_EQ(EDOOFUS, errno);
+}
+
+/* Use a file handle whose entry is still cached */
+TEST_F(Fhstat, cached)
+{
+ const char FULLPATH[] = "mountpoint/some_dir/.";
+ const char RELDIRPATH[] = "some_dir";
+ fhandle_t fhp;
+ struct stat sb;
+ const uint64_t ino = 42;
+ const mode_t mode = S_IFDIR | 0755;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.ino = ino;
+ out.body.entry.generation = 1;
+ out.body.entry.attr.ino = ino;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+
+ ASSERT_EQ(0, getfh(FULLPATH, &fhp)) << strerror(errno);
+ ASSERT_EQ(0, fhstat(&fhp, &sb)) << strerror(errno);
+ EXPECT_EQ(ino, sb.st_ino);
+}
+
+/* File handle entries should expire from the cache, too */
+TEST_F(Fhstat, cache_expired)
+{
+ const char FULLPATH[] = "mountpoint/some_dir/.";
+ const char RELDIRPATH[] = "some_dir";
+ fhandle_t fhp;
+ struct stat sb;
+ const uint64_t ino = 42;
+ const mode_t mode = S_IFDIR | 0755;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.ino = ino;
+ out.body.entry.generation = 1;
+ out.body.entry.attr.ino = ino;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid_nsec = NAP_NS / 2;
+ })));
+
+ EXPECT_LOOKUP(ino, ".")
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.ino = ino;
+ out.body.entry.generation = 1;
+ out.body.entry.attr.ino = ino;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = 0;
+ })));
+
+ ASSERT_EQ(0, getfh(FULLPATH, &fhp)) << strerror(errno);
+ ASSERT_EQ(0, fhstat(&fhp, &sb)) << strerror(errno);
+ EXPECT_EQ(ino, sb.st_ino);
+
+ nap();
+
+ /* Cache should be expired; fuse should issue a FUSE_LOOKUP */
+ ASSERT_EQ(0, fhstat(&fhp, &sb)) << strerror(errno);
+ EXPECT_EQ(ino, sb.st_ino);
+}
+
+/*
+ * If the server returns a FUSE_LOOKUP response for a nodeid that we didn't
+ * lookup, it's a bug. But we should handle it gracefully.
+ */
+TEST_F(Fhstat, inconsistent_nodeid)
+{
+ const char FULLPATH[] = "mountpoint/some_dir/.";
+ const char RELDIRPATH[] = "some_dir";
+ fhandle_t fhp;
+ struct stat sb;
+ const uint64_t ino_in = 42;
+ const uint64_t ino_out = 43;
+ const mode_t mode = S_IFDIR | 0755;
+ const uid_t uid = 12345;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.nodeid = ino_in;
+ out.body.entry.attr.ino = ino_in;
+ out.body.entry.attr.mode = mode;
+ out.body.entry.generation = 1;
+ out.body.entry.attr.uid = uid;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = 0;
+ })));
+
+ EXPECT_LOOKUP(ino_in, ".")
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.nodeid = ino_out;
+ out.body.entry.attr.ino = ino_out;
+ out.body.entry.attr.mode = mode;
+ out.body.entry.generation = 1;
+ out.body.entry.attr.uid = uid;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = 0;
+ })));
+
+ ASSERT_EQ(0, getfh(FULLPATH, &fhp)) << strerror(errno);
+ EXPECT_NE(0, fhstat(&fhp, &sb)) << strerror(errno);
+ EXPECT_EQ(EIO, errno);
+}
+
+/*
+ * If the server returns a FUSE_LOOKUP response where the nodeid doesn't match
+ * the inode number, and the file system is exported, it's a bug. But we
+ * should handle it gracefully.
+ */
+TEST_F(Fhstat, inconsistent_ino)
+{
+ const char FULLPATH[] = "mountpoint/some_dir/.";
+ const char RELDIRPATH[] = "some_dir";
+ fhandle_t fhp;
+ struct stat sb;
+ const uint64_t nodeid = 42;
+ const uint64_t ino = 711; // Could be anything that != nodeid
+ const mode_t mode = S_IFDIR | 0755;
+ const uid_t uid = 12345;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.nodeid = nodeid;
+ out.body.entry.attr.ino = nodeid;
+ out.body.entry.attr.mode = mode;
+ out.body.entry.generation = 1;
+ out.body.entry.attr.uid = uid;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = 0;
+ })));
+
+ EXPECT_LOOKUP(nodeid, ".")
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.nodeid = nodeid;
+ out.body.entry.attr.ino = ino;
+ out.body.entry.attr.mode = mode;
+ out.body.entry.generation = 1;
+ out.body.entry.attr.uid = uid;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = 0;
+ })));
+
+ ASSERT_EQ(0, getfh(FULLPATH, &fhp)) << strerror(errno);
+ /*
+ * The fhstat operation will actually succeed. But future operations
+ * will likely fail.
+ */
+ ASSERT_EQ(0, fhstat(&fhp, &sb)) << strerror(errno);
+ EXPECT_EQ(ino, sb.st_ino);
+}
+
+/*
+ * If the server doesn't set FUSE_EXPORT_SUPPORT, then we can't do NFS-style
+ * lookups
+ */
+TEST_F(FhstatNotExportable, lookup_dot)
+{
+ const char FULLPATH[] = "mountpoint/some_dir/.";
+ const char RELDIRPATH[] = "some_dir";
+ fhandle_t fhp;
+ const uint64_t ino = 42;
+ const mode_t mode = S_IFDIR | 0755;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.ino = ino;
+ out.body.entry.generation = 1;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = 0;
+ })));
+
+ ASSERT_EQ(-1, getfh(FULLPATH, &fhp));
+ ASSERT_EQ(EOPNOTSUPP, errno);
+}
+
+/* FreeBSD's fid struct doesn't have enough space for 64-bit generations */
+TEST_F(Getfh, eoverflow)
+{
+ const char FULLPATH[] = "mountpoint/some_dir/.";
+ const char RELDIRPATH[] = "some_dir";
+ fhandle_t fhp;
+ uint64_t ino = 42;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFDIR | 0755;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.ino = ino;
+ out.body.entry.generation = (uint64_t)UINT32_MAX + 1;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+
+ ASSERT_NE(0, getfh(FULLPATH, &fhp));
+ EXPECT_EQ(EOVERFLOW, errno);
+}
+
+/* Get an NFS file handle */
+TEST_F(Getfh, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_dir/.";
+ const char RELDIRPATH[] = "some_dir";
+ fhandle_t fhp;
+ uint64_t ino = 42;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFDIR | 0755;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.ino = ino;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+
+ ASSERT_EQ(0, getfh(FULLPATH, &fhp)) << strerror(errno);
+}
+
+/*
+ * Call readdir via a file handle.
+ *
+ * This is how a userspace nfs server like nfs-ganesha or unfs3 would call
+ * readdir. The in-kernel NFS server never does any equivalent of open. I
+ * haven't discovered a way to mimic nfsd's behavior short of actually running
+ * nfsd.
+ */
+TEST_F(Readdir, getdirentries)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ uint64_t ino = 42;
+ mode_t mode = S_IFDIR | 0755;
+ fhandle_t fhp;
+ int fd;
+ char buf[8192];
+ ssize_t r;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.ino = ino;
+ out.body.entry.generation = 1;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = 0;
+ })));
+
+ EXPECT_LOOKUP(ino, ".")
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.ino = ino;
+ out.body.entry.generation = 1;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = 0;
+ })));
+
+ expect_opendir(ino);
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READDIR &&
+ in.header.nodeid == ino &&
+ in.body.readdir.size == sizeof(buf));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ out.header.error = 0;
+ out.header.len = sizeof(out.header);
+ })));
+
+ ASSERT_EQ(0, getfh(FULLPATH, &fhp)) << strerror(errno);
+ fd = fhopen(&fhp, O_DIRECTORY);
+ ASSERT_LE(0, fd) << strerror(errno);
+ r = getdirentries(fd, buf, sizeof(buf), 0);
+ ASSERT_EQ(0, r) << strerror(errno);
+
+ leak(fd);
+}
diff --git a/tests/sys/fs/fusefs/notify.cc b/tests/sys/fs/fusefs/notify.cc
new file mode 100644
index 000000000000..1e22bde13db7
--- /dev/null
+++ b/tests/sys/fs/fusefs/notify.cc
@@ -0,0 +1,546 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <pthread.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+/*
+ * FUSE asynchonous notification
+ *
+ * FUSE servers can send unprompted notification messages for things like cache
+ * invalidation. This file tests our client's handling of those messages.
+ */
+
+class Notify: public FuseTest {
+public:
+/* Ignore an optional FUSE_FSYNC */
+void maybe_expect_fsync(uint64_t ino)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_FSYNC &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(0)));
+}
+
+void expect_lookup(uint64_t parent, const char *relpath, uint64_t ino,
+ off_t size, Sequence &seq)
+{
+ EXPECT_LOOKUP(parent, relpath)
+ .InSequence(seq)
+ .WillOnce(Invoke(
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.ino = ino;
+ out.body.entry.attr.nlink = 1;
+ out.body.entry.attr.size = size;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+}
+};
+
+class NotifyWriteback: public Notify {
+public:
+virtual void SetUp() {
+ m_init_flags |= FUSE_WRITEBACK_CACHE;
+ m_async = true;
+ Notify::SetUp();
+ if (IsSkipped())
+ return;
+}
+
+void expect_write(uint64_t ino, uint64_t offset, uint64_t size,
+ const void *contents)
+{
+ FuseTest::expect_write(ino, offset, size, size, 0, 0, contents);
+}
+
+};
+
+struct inval_entry_args {
+ MockFS *mock;
+ ino_t parent;
+ const char *name;
+ size_t namelen;
+};
+
+static void* inval_entry(void* arg) {
+ const struct inval_entry_args *iea = (struct inval_entry_args*)arg;
+ ssize_t r;
+
+ r = iea->mock->notify_inval_entry(iea->parent, iea->name, iea->namelen);
+ if (r >= 0)
+ return 0;
+ else
+ return (void*)(intptr_t)errno;
+}
+
+struct inval_inode_args {
+ MockFS *mock;
+ ino_t ino;
+ off_t off;
+ ssize_t len;
+};
+
+struct store_args {
+ MockFS *mock;
+ ino_t nodeid;
+ off_t offset;
+ ssize_t size;
+ const void* data;
+};
+
+static void* inval_inode(void* arg) {
+ const struct inval_inode_args *iia = (struct inval_inode_args*)arg;
+ ssize_t r;
+
+ r = iia->mock->notify_inval_inode(iia->ino, iia->off, iia->len);
+ if (r >= 0)
+ return 0;
+ else
+ return (void*)(intptr_t)errno;
+}
+
+static void* store(void* arg) {
+ const struct store_args *sa = (struct store_args*)arg;
+ ssize_t r;
+
+ r = sa->mock->notify_store(sa->nodeid, sa->offset, sa->data, sa->size);
+ if (r >= 0)
+ return 0;
+ else
+ return (void*)(intptr_t)errno;
+}
+
+/* Invalidate a nonexistent entry */
+TEST_F(Notify, inval_entry_nonexistent)
+{
+ const static char *name = "foo";
+ struct inval_entry_args iea;
+ void *thr0_value;
+ pthread_t th0;
+
+ iea.mock = m_mock;
+ iea.parent = FUSE_ROOT_ID;
+ iea.name = name;
+ iea.namelen = strlen(name);
+ ASSERT_EQ(0, pthread_create(&th0, NULL, inval_entry, &iea))
+ << strerror(errno);
+ pthread_join(th0, &thr0_value);
+ /* It's not an error for an entry to not be cached */
+ EXPECT_EQ(0, (intptr_t)thr0_value);
+}
+
+/* Invalidate a cached entry */
+TEST_F(Notify, inval_entry)
+{
+ const static char FULLPATH[] = "mountpoint/foo";
+ const static char RELPATH[] = "foo";
+ struct inval_entry_args iea;
+ struct stat sb;
+ void *thr0_value;
+ uint64_t ino0 = 42;
+ uint64_t ino1 = 43;
+ Sequence seq;
+ pthread_t th0;
+
+ expect_lookup(FUSE_ROOT_ID, RELPATH, ino0, 0, seq);
+ expect_lookup(FUSE_ROOT_ID, RELPATH, ino1, 0, seq);
+
+ /* Fill the entry cache */
+ ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno);
+ EXPECT_EQ(ino0, sb.st_ino);
+
+ /* Now invalidate the entry */
+ iea.mock = m_mock;
+ iea.parent = FUSE_ROOT_ID;
+ iea.name = RELPATH;
+ iea.namelen = strlen(RELPATH);
+ ASSERT_EQ(0, pthread_create(&th0, NULL, inval_entry, &iea))
+ << strerror(errno);
+ pthread_join(th0, &thr0_value);
+ EXPECT_EQ(0, (intptr_t)thr0_value);
+
+ /* The second lookup should return the alternate ino */
+ ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno);
+ EXPECT_EQ(ino1, sb.st_ino);
+}
+
+/*
+ * Invalidate a cached entry beneath the root, which uses a slightly different
+ * code path.
+ */
+TEST_F(Notify, inval_entry_below_root)
+{
+ const static char FULLPATH[] = "mountpoint/some_dir/foo";
+ const static char DNAME[] = "some_dir";
+ const static char FNAME[] = "foo";
+ struct inval_entry_args iea;
+ struct stat sb;
+ void *thr0_value;
+ uint64_t dir_ino = 41;
+ uint64_t ino0 = 42;
+ uint64_t ino1 = 43;
+ Sequence seq;
+ pthread_t th0;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, DNAME)
+ .WillOnce(Invoke(
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFDIR | 0755;
+ out.body.entry.nodeid = dir_ino;
+ out.body.entry.attr.nlink = 2;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+ expect_lookup(dir_ino, FNAME, ino0, 0, seq);
+ expect_lookup(dir_ino, FNAME, ino1, 0, seq);
+
+ /* Fill the entry cache */
+ ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno);
+ EXPECT_EQ(ino0, sb.st_ino);
+
+ /* Now invalidate the entry */
+ iea.mock = m_mock;
+ iea.parent = dir_ino;
+ iea.name = FNAME;
+ iea.namelen = strlen(FNAME);
+ ASSERT_EQ(0, pthread_create(&th0, NULL, inval_entry, &iea))
+ << strerror(errno);
+ pthread_join(th0, &thr0_value);
+ EXPECT_EQ(0, (intptr_t)thr0_value);
+
+ /* The second lookup should return the alternate ino */
+ ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno);
+ EXPECT_EQ(ino1, sb.st_ino);
+}
+
+/* Invalidating an entry invalidates the parent directory's attributes */
+TEST_F(Notify, inval_entry_invalidates_parent_attrs)
+{
+ const static char FULLPATH[] = "mountpoint/foo";
+ const static char RELPATH[] = "foo";
+ struct inval_entry_args iea;
+ struct stat sb;
+ void *thr0_value;
+ uint64_t ino = 42;
+ Sequence seq;
+ pthread_t th0;
+
+ expect_lookup(FUSE_ROOT_ID, RELPATH, ino, 0, seq);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == FUSE_ROOT_ID);
+ }, Eq(true)),
+ _)
+ ).Times(2)
+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.mode = S_IFDIR | 0755;
+ out.body.attr.attr_valid = UINT64_MAX;
+ })));
+
+ /* Fill the attr and entry cache */
+ ASSERT_EQ(0, stat("mountpoint", &sb)) << strerror(errno);
+ ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno);
+
+ /* Now invalidate the entry */
+ iea.mock = m_mock;
+ iea.parent = FUSE_ROOT_ID;
+ iea.name = RELPATH;
+ iea.namelen = strlen(RELPATH);
+ ASSERT_EQ(0, pthread_create(&th0, NULL, inval_entry, &iea))
+ << strerror(errno);
+ pthread_join(th0, &thr0_value);
+ EXPECT_EQ(0, (intptr_t)thr0_value);
+
+ /* /'s attribute cache should be cleared */
+ ASSERT_EQ(0, stat("mountpoint", &sb)) << strerror(errno);
+}
+
+
+TEST_F(Notify, inval_inode_nonexistent)
+{
+ struct inval_inode_args iia;
+ ino_t ino = 42;
+ void *thr0_value;
+ pthread_t th0;
+
+ iia.mock = m_mock;
+ iia.ino = ino;
+ iia.off = 0;
+ iia.len = 0;
+ ASSERT_EQ(0, pthread_create(&th0, NULL, inval_inode, &iia))
+ << strerror(errno);
+ pthread_join(th0, &thr0_value);
+ /* It's not an error for an inode to not be cached */
+ EXPECT_EQ(0, (intptr_t)thr0_value);
+}
+
+TEST_F(Notify, inval_inode_with_clean_cache)
+{
+ const static char FULLPATH[] = "mountpoint/foo";
+ const static char RELPATH[] = "foo";
+ const char CONTENTS0[] = "abcdefgh";
+ const char CONTENTS1[] = "ijklmnopqrstuvwxyz";
+ struct inval_inode_args iia;
+ struct stat sb;
+ ino_t ino = 42;
+ void *thr0_value;
+ Sequence seq;
+ uid_t uid = 12345;
+ pthread_t th0;
+ ssize_t size0 = sizeof(CONTENTS0);
+ ssize_t size1 = sizeof(CONTENTS1);
+ char buf[80];
+ int fd;
+
+ expect_lookup(FUSE_ROOT_ID, RELPATH, ino, size0, seq);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.mode = S_IFREG | 0644;
+ out.body.attr.attr_valid = UINT64_MAX;
+ out.body.attr.attr.size = size1;
+ out.body.attr.attr.uid = uid;
+ })));
+ expect_read(ino, 0, size0, size0, CONTENTS0);
+ expect_read(ino, 0, size1, size1, CONTENTS1);
+
+ /* Fill the data cache */
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(size0, read(fd, buf, size0)) << strerror(errno);
+ EXPECT_EQ(0, memcmp(buf, CONTENTS0, size0));
+
+ /* Evict the data cache */
+ iia.mock = m_mock;
+ iia.ino = ino;
+ iia.off = 0;
+ iia.len = 0;
+ ASSERT_EQ(0, pthread_create(&th0, NULL, inval_inode, &iia))
+ << strerror(errno);
+ pthread_join(th0, &thr0_value);
+ EXPECT_EQ(0, (intptr_t)thr0_value);
+
+ /* cache attributes were purged; this will trigger a new GETATTR */
+ ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno);
+ EXPECT_EQ(uid, sb.st_uid);
+ EXPECT_EQ(size1, sb.st_size);
+
+ /* This read should not be serviced by cache */
+ ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
+ ASSERT_EQ(size1, read(fd, buf, size1)) << strerror(errno);
+ EXPECT_EQ(0, memcmp(buf, CONTENTS1, size1));
+
+ leak(fd);
+}
+
+/* FUSE_NOTIFY_STORE with a file that's not in the entry cache */
+/* disabled because FUSE_NOTIFY_STORE is not yet implemented */
+TEST_F(Notify, DISABLED_store_nonexistent)
+{
+ struct store_args sa;
+ ino_t ino = 42;
+ void *thr0_value;
+ pthread_t th0;
+
+ sa.mock = m_mock;
+ sa.nodeid = ino;
+ sa.offset = 0;
+ sa.size = 0;
+ ASSERT_EQ(0, pthread_create(&th0, NULL, store, &sa)) << strerror(errno);
+ pthread_join(th0, &thr0_value);
+ /* It's not an error for a file to be unknown to the kernel */
+ EXPECT_EQ(0, (intptr_t)thr0_value);
+}
+
+/* Store data into for a file that does not yet have anything cached */
+/* disabled because FUSE_NOTIFY_STORE is not yet implemented */
+TEST_F(Notify, DISABLED_store_with_blank_cache)
+{
+ const static char FULLPATH[] = "mountpoint/foo";
+ const static char RELPATH[] = "foo";
+ const char CONTENTS1[] = "ijklmnopqrstuvwxyz";
+ struct store_args sa;
+ ino_t ino = 42;
+ void *thr0_value;
+ Sequence seq;
+ pthread_t th0;
+ ssize_t size1 = sizeof(CONTENTS1);
+ char buf[80];
+ int fd;
+
+ expect_lookup(FUSE_ROOT_ID, RELPATH, ino, size1, seq);
+ expect_open(ino, 0, 1);
+
+ /* Fill the data cache */
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ /* Evict the data cache */
+ sa.mock = m_mock;
+ sa.nodeid = ino;
+ sa.offset = 0;
+ sa.size = size1;
+ sa.data = (const void*)CONTENTS1;
+ ASSERT_EQ(0, pthread_create(&th0, NULL, store, &sa)) << strerror(errno);
+ pthread_join(th0, &thr0_value);
+ EXPECT_EQ(0, (intptr_t)thr0_value);
+
+ /* This read should be serviced by cache */
+ ASSERT_EQ(size1, read(fd, buf, size1)) << strerror(errno);
+ EXPECT_EQ(0, memcmp(buf, CONTENTS1, size1));
+
+ leak(fd);
+}
+
+TEST_F(NotifyWriteback, inval_inode_with_dirty_cache)
+{
+ const static char FULLPATH[] = "mountpoint/foo";
+ const static char RELPATH[] = "foo";
+ const char CONTENTS[] = "abcdefgh";
+ struct inval_inode_args iia;
+ ino_t ino = 42;
+ void *thr0_value;
+ Sequence seq;
+ pthread_t th0;
+ ssize_t bufsize = sizeof(CONTENTS);
+ int fd;
+
+ expect_lookup(FUSE_ROOT_ID, RELPATH, ino, 0, seq);
+ expect_open(ino, 0, 1);
+
+ /* Fill the data cache */
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd);
+ ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
+
+ expect_write(ino, 0, bufsize, CONTENTS);
+ /*
+ * The FUSE protocol does not require an fsync here, but FreeBSD's
+ * bufobj_invalbuf sends it anyway
+ */
+ maybe_expect_fsync(ino);
+
+ /* Evict the data cache */
+ iia.mock = m_mock;
+ iia.ino = ino;
+ iia.off = 0;
+ iia.len = 0;
+ ASSERT_EQ(0, pthread_create(&th0, NULL, inval_inode, &iia))
+ << strerror(errno);
+ pthread_join(th0, &thr0_value);
+ EXPECT_EQ(0, (intptr_t)thr0_value);
+
+ leak(fd);
+}
+
+TEST_F(NotifyWriteback, inval_inode_attrs_only)
+{
+ const static char FULLPATH[] = "mountpoint/foo";
+ const static char RELPATH[] = "foo";
+ const char CONTENTS[] = "abcdefgh";
+ struct inval_inode_args iia;
+ struct stat sb;
+ uid_t uid = 12345;
+ ino_t ino = 42;
+ void *thr0_value;
+ Sequence seq;
+ pthread_t th0;
+ ssize_t bufsize = sizeof(CONTENTS);
+ int fd;
+
+ expect_lookup(FUSE_ROOT_ID, RELPATH, ino, 0, seq);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_WRITE);
+ }, Eq(true)),
+ _)
+ ).Times(0);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.mode = S_IFREG | 0644;
+ out.body.attr.attr_valid = UINT64_MAX;
+ out.body.attr.attr.size = bufsize;
+ out.body.attr.attr.uid = uid;
+ })));
+
+ /* Fill the data cache */
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
+
+ /* Evict the attributes, but not data cache */
+ iia.mock = m_mock;
+ iia.ino = ino;
+ iia.off = -1;
+ iia.len = 0;
+ ASSERT_EQ(0, pthread_create(&th0, NULL, inval_inode, &iia))
+ << strerror(errno);
+ pthread_join(th0, &thr0_value);
+ EXPECT_EQ(0, (intptr_t)thr0_value);
+
+ /* cache attributes were been purged; this will trigger a new GETATTR */
+ ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno);
+ EXPECT_EQ(uid, sb.st_uid);
+ EXPECT_EQ(bufsize, sb.st_size);
+
+ leak(fd);
+}
diff --git a/tests/sys/fs/fusefs/open.cc b/tests/sys/fs/fusefs/open.cc
new file mode 100644
index 000000000000..1212a7047f26
--- /dev/null
+++ b/tests/sys/fs/fusefs/open.cc
@@ -0,0 +1,307 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <sys/wait.h>
+
+#include <fcntl.h>
+#include <semaphore.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class Open: public FuseTest {
+
+public:
+
+/* Test an OK open of a file with the given flags */
+void test_ok(int os_flags, int fuse_flags) {
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int fd;
+
+ FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_OPEN &&
+ in.body.open.flags == (uint32_t)fuse_flags &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([](auto in __unused, auto& out) {
+ out.header.len = sizeof(out.header);
+ SET_OUT_HEADER_LEN(out, open);
+ })));
+
+ fd = open(FULLPATH, os_flags);
+ ASSERT_LE(0, fd) << strerror(errno);
+ leak(fd);
+}
+};
+
+/*
+ * fusefs(4) does not support I/O on device nodes (neither does UFS). But it
+ * shouldn't crash
+ */
+TEST_F(Open, chr)
+{
+ const char FULLPATH[] = "mountpoint/zero";
+ const char RELPATH[] = "zero";
+ uint64_t ino = 42;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFCHR | 0644;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.nlink = 1;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.attr.rdev = 44; /* /dev/zero's rdev */
+ })));
+
+ ASSERT_EQ(-1, open(FULLPATH, O_RDONLY));
+ EXPECT_EQ(EOPNOTSUPP, errno);
+}
+
+/*
+ * The fuse daemon fails the request with enoent. This usually indicates a
+ * race condition: some other FUSE client removed the file in between when the
+ * kernel checked for it with lookup and tried to open it
+ */
+TEST_F(Open, enoent)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ sem_t sem;
+
+ ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_OPEN &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(ENOENT)));
+ // Since FUSE_OPEN returns ENOENT, the kernel will reclaim the vnode
+ // and send a FUSE_FORGET
+ expect_forget(ino, 1, &sem);
+
+ ASSERT_EQ(-1, open(FULLPATH, O_RDONLY));
+ EXPECT_EQ(ENOENT, errno);
+
+ sem_wait(&sem);
+ sem_destroy(&sem);
+}
+
+/*
+ * The daemon is responsible for checking file permissions (unless the
+ * default_permissions mount option was used)
+ */
+TEST_F(Open, eperm)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_OPEN &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(EPERM)));
+ ASSERT_EQ(-1, open(FULLPATH, O_RDONLY));
+ EXPECT_EQ(EPERM, errno);
+}
+
+/*
+ * fusefs must issue multiple FUSE_OPEN operations if clients with different
+ * credentials open the same file, even if they use the same mode. This is
+ * necessary so that the daemon can validate each set of credentials.
+ */
+TEST_F(Open, multiple_creds)
+{
+ const static char FULLPATH[] = "mountpoint/some_file.txt";
+ const static char RELPATH[] = "some_file.txt";
+ int fd1, status;
+ const static uint64_t ino = 42;
+ const static uint64_t fh0 = 100, fh1 = 200;
+
+ /* Fork a child to open the file with different credentials */
+ fork(false, &status, [&] {
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_OPEN &&
+ in.header.pid == (uint32_t)getpid() &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(
+ ReturnImmediate([](auto in __unused, auto& out) {
+ out.body.open.fh = fh0;
+ out.header.len = sizeof(out.header);
+ SET_OUT_HEADER_LEN(out, open);
+ })));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_OPEN &&
+ in.header.pid != (uint32_t)getpid() &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(
+ ReturnImmediate([](auto in __unused, auto& out) {
+ out.body.open.fh = fh1;
+ out.header.len = sizeof(out.header);
+ SET_OUT_HEADER_LEN(out, open);
+ })));
+ expect_flush(ino, 2, ReturnErrno(0));
+ expect_release(ino, fh0);
+ expect_release(ino, fh1);
+
+ fd1 = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd1) << strerror(errno);
+ }, [] {
+ int fd0;
+
+ fd0 = open(FULLPATH, O_RDONLY);
+ if (fd0 < 0) {
+ perror("open");
+ return(1);
+ }
+ leak(fd0);
+ return 0;
+ }
+ );
+ ASSERT_EQ(0, WEXITSTATUS(status));
+
+ close(fd1);
+}
+
+/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236340 */
+TEST_F(Open, DISABLED_o_append)
+{
+ test_ok(O_WRONLY | O_APPEND, O_WRONLY | O_APPEND);
+}
+
+/* The kernel is supposed to filter out this flag */
+TEST_F(Open, o_creat)
+{
+ test_ok(O_WRONLY | O_CREAT, O_WRONLY);
+}
+
+/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236340 */
+TEST_F(Open, DISABLED_o_direct)
+{
+ test_ok(O_WRONLY | O_DIRECT, O_WRONLY | O_DIRECT);
+}
+
+/* The kernel is supposed to filter out this flag */
+TEST_F(Open, o_excl)
+{
+ test_ok(O_WRONLY | O_EXCL, O_WRONLY);
+}
+
+TEST_F(Open, o_exec)
+{
+ test_ok(O_EXEC, O_EXEC);
+}
+
+/* The kernel is supposed to filter out this flag */
+TEST_F(Open, o_noctty)
+{
+ test_ok(O_WRONLY | O_NOCTTY, O_WRONLY);
+}
+
+TEST_F(Open, o_rdonly)
+{
+ test_ok(O_RDONLY, O_RDONLY);
+}
+
+/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236340 */
+TEST_F(Open, DISABLED_o_trunc)
+{
+ test_ok(O_WRONLY | O_TRUNC, O_WRONLY | O_TRUNC);
+}
+
+TEST_F(Open, o_wronly)
+{
+ test_ok(O_WRONLY, O_WRONLY);
+}
+
+TEST_F(Open, o_rdwr)
+{
+ test_ok(O_RDWR, O_RDWR);
+}
+
+/*
+ * If a fuse server returns ENOSYS to a
+ * FUSE_OPEN, then it and subsequent FUSE_OPEN and FUSE_RELEASE operations will
+ * also succeed automatically without being sent to the server.
+ */
+TEST_F(Open, enosys)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int fd;
+
+ FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_OPEN &&
+ in.body.open.flags == (uint32_t)O_RDONLY &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).Times(1)
+ .WillOnce(Invoke(ReturnErrno(ENOSYS)));
+ expect_flush(ino, 1, ReturnErrno(ENOSYS));
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+ close(fd);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ leak(fd);
+}
diff --git a/tests/sys/fs/fusefs/opendir.cc b/tests/sys/fs/fusefs/opendir.cc
new file mode 100644
index 000000000000..e1fed59635fc
--- /dev/null
+++ b/tests/sys/fs/fusefs/opendir.cc
@@ -0,0 +1,197 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <dirent.h>
+
+#include <fcntl.h>
+#include <semaphore.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class Opendir: public FuseTest {
+public:
+void expect_lookup(const char *relpath, uint64_t ino)
+{
+ FuseTest::expect_lookup(relpath, ino, S_IFDIR | 0755, 0, 1);
+}
+
+void expect_opendir(uint64_t ino, uint32_t flags, ProcessMockerT r)
+{
+ /* opendir(3) calls fstatfs */
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_STATFS);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, statfs);
+ })));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_OPENDIR &&
+ in.header.nodeid == ino &&
+ in.body.opendir.flags == flags);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(r));
+}
+
+};
+
+
+/*
+ * The fuse daemon fails the request with enoent. This usually indicates a
+ * race condition: some other FUSE client removed the file in between when the
+ * kernel checked for it with lookup and tried to open it
+ */
+TEST_F(Opendir, enoent)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ uint64_t ino = 42;
+ sem_t sem;
+
+ ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
+
+ expect_lookup(RELPATH, ino);
+ expect_opendir(ino, O_RDONLY, ReturnErrno(ENOENT));
+ // Since FUSE_OPENDIR returns ENOENT, the kernel will reclaim the vnode
+ // and send a FUSE_FORGET
+ expect_forget(ino, 1, &sem);
+
+ ASSERT_EQ(-1, open(FULLPATH, O_DIRECTORY));
+ EXPECT_EQ(ENOENT, errno);
+
+ sem_wait(&sem);
+ sem_destroy(&sem);
+}
+
+/*
+ * The daemon is responsible for checking file permissions (unless the
+ * default_permissions mount option was used)
+ */
+TEST_F(Opendir, eperm)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ uint64_t ino = 42;
+
+ expect_lookup(RELPATH, ino);
+ expect_opendir(ino, O_RDONLY, ReturnErrno(EPERM));
+
+ EXPECT_EQ(-1, open(FULLPATH, O_DIRECTORY));
+ EXPECT_EQ(EPERM, errno);
+}
+
+TEST_F(Opendir, open)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ uint64_t ino = 42;
+ int fd;
+
+ expect_lookup(RELPATH, ino);
+ expect_opendir(ino, O_RDONLY,
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, open);
+ }));
+
+ fd = open(FULLPATH, O_DIRECTORY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ leak(fd);
+}
+
+/* Directories can be opened O_EXEC for stuff like fchdir(2) */
+TEST_F(Opendir, open_exec)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ uint64_t ino = 42;
+ int fd;
+
+ expect_lookup(RELPATH, ino);
+ expect_opendir(ino, O_EXEC,
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, open);
+ }));
+
+ fd = open(FULLPATH, O_EXEC | O_DIRECTORY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ leak(fd);
+}
+
+TEST_F(Opendir, opendir)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ uint64_t ino = 42;
+
+ expect_lookup(RELPATH, ino);
+ expect_opendir(ino, O_RDONLY,
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, open);
+ }));
+
+ errno = 0;
+ EXPECT_NE(nullptr, opendir(FULLPATH)) << strerror(errno);
+}
+
+/*
+ * If a fuse server returns ENOSYS to a
+ * FUSE_OPENDIR, then it and subsequent FUSE_OPENDIR and FUSE_RELEASEDIR
+ * operations will also succeed automatically without being sent to the server.
+ */
+TEST_F(Opendir, enosys)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int fd;
+
+ FuseTest::expect_lookup(RELPATH, ino, S_IFDIR | 0755, 0, 2);
+ expect_opendir(ino, O_RDONLY, ReturnErrno(ENOSYS));
+
+ fd = open(FULLPATH, O_DIRECTORY);
+ ASSERT_LE(0, fd) << strerror(errno);
+ close(fd);
+
+ fd = open(FULLPATH, O_DIRECTORY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ leak(fd);
+}
diff --git a/tests/sys/fs/fusefs/pre-init.cc b/tests/sys/fs/fusefs/pre-init.cc
new file mode 100644
index 000000000000..e990d3cafffa
--- /dev/null
+++ b/tests/sys/fs/fusefs/pre-init.cc
@@ -0,0 +1,154 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 ConnectWise
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/signal.h>
+#include <sys/wait.h>
+
+#include <fcntl.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <signal.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+/* Tests for behavior that happens before the server responds to FUSE_INIT */
+class PreInit: public FuseTest {
+void SetUp() {
+ m_no_auto_init = true;
+ FuseTest::SetUp();
+}
+};
+
+static void* unmount1(void* arg __unused) {
+ ssize_t r;
+
+ r = unmount("mountpoint", 0);
+ if (r >= 0)
+ return 0;
+ else
+ return (void*)(intptr_t)errno;
+}
+
+/*
+ * Attempting to unmount the file system before it fully initializes should
+ * work fine. The unmount will complete after initialization does.
+ */
+TEST_F(PreInit, unmount_before_init)
+{
+ sem_t sem0;
+ pthread_t th1;
+
+ ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_INIT);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([&](auto in, auto& out) {
+ SET_OUT_HEADER_LEN(out, init);
+ out.body.init.major = FUSE_KERNEL_VERSION;
+ out.body.init.minor = FUSE_KERNEL_MINOR_VERSION;
+ out.body.init.flags = in.body.init.flags & m_init_flags;
+ out.body.init.max_write = m_maxwrite;
+ out.body.init.max_readahead = m_maxreadahead;
+ out.body.init.time_gran = m_time_gran;
+ sem_wait(&sem0);
+ })));
+ expect_destroy(0);
+
+ ASSERT_EQ(0, pthread_create(&th1, NULL, unmount1, NULL));
+ nap(); /* Wait for th1 to block in unmount() */
+ sem_post(&sem0);
+ /* The daemon will quit after receiving FUSE_DESTROY */
+ m_mock->join_daemon();
+}
+
+/*
+ * Don't panic in this very specific scenario:
+ *
+ * The server does not respond to FUSE_INIT in timely fashion.
+ * Some other process tries to do unmount.
+ * That other process gets killed by a signal.
+ * The server finally responds to FUSE_INIT.
+ *
+ * Regression test for bug 287438
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=287438
+ */
+TEST_F(PreInit, signal_during_unmount_before_init)
+{
+ sem_t sem0;
+ pid_t child;
+
+ ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_INIT);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([&](auto in, auto& out) {
+ SET_OUT_HEADER_LEN(out, init);
+ out.body.init.major = FUSE_KERNEL_VERSION;
+ /*
+ * Use protocol 7.19, like libfuse2 does. The server must use
+ * protocol 7.27 or older to trigger the bug.
+ */
+ out.body.init.minor = 19;
+ out.body.init.flags = in.body.init.flags & m_init_flags;
+ out.body.init.max_write = m_maxwrite;
+ out.body.init.max_readahead = m_maxreadahead;
+ out.body.init.time_gran = m_time_gran;
+ sem_wait(&sem0);
+ })));
+
+ if ((child = ::fork()) == 0) {
+ /*
+ * In child. This will block waiting for FUSE_INIT to complete
+ * or the receipt of an asynchronous signal.
+ */
+ (void) unmount("mountpoint", 0);
+ _exit(0); /* Unreachable, unless parent dies after fork */
+ } else if (child > 0) {
+ /* In parent. Wait for child process to start, then kill it */
+ nap();
+ kill(child, SIGINT);
+ waitpid(child, NULL, WEXITED);
+ } else {
+ FAIL() << strerror(errno);
+ }
+ m_mock->m_quit = true; /* Since we are by now unmounted. */
+ sem_post(&sem0);
+ m_mock->join_daemon();
+}
diff --git a/tests/sys/fs/fusefs/read.cc b/tests/sys/fs/fusefs/read.cc
new file mode 100644
index 000000000000..e9c79ba2ffda
--- /dev/null
+++ b/tests/sys/fs/fusefs/read.cc
@@ -0,0 +1,1497 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/uio.h>
+
+#include <aio.h>
+#include <fcntl.h>
+#include <semaphore.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <unistd.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class Read: public FuseTest {
+
+public:
+void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
+{
+ FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1);
+}
+};
+
+class RofsRead: public Read {
+public:
+virtual void SetUp() {
+ m_ro = true;
+ Read::SetUp();
+}
+};
+
+class Read_7_8: public FuseTest {
+public:
+virtual void SetUp() {
+ m_kernel_minor_version = 8;
+ FuseTest::SetUp();
+}
+
+void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
+{
+ FuseTest::expect_lookup_7_8(relpath, ino, S_IFREG | 0644, size, 1);
+}
+};
+
+class AioRead: public Read {
+public:
+virtual void SetUp() {
+ if (!is_unsafe_aio_enabled())
+ GTEST_SKIP() <<
+ "vfs.aio.enable_unsafe must be set for this test";
+ FuseTest::SetUp();
+}
+};
+
+class AsyncRead: public AioRead {
+ virtual void SetUp() {
+ m_init_flags = FUSE_ASYNC_READ;
+ AioRead::SetUp();
+ }
+};
+
+class ReadAhead: public Read,
+ public WithParamInterface<tuple<bool, int>>
+{
+ virtual void SetUp() {
+ int val;
+ const char *node = "vfs.maxbcachebuf";
+ size_t size = sizeof(val);
+ ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
+ << strerror(errno);
+
+ m_maxreadahead = val * get<1>(GetParam());
+ m_noclusterr = get<0>(GetParam());
+ Read::SetUp();
+ }
+};
+
+class ReadMaxRead: public Read {
+ virtual void SetUp() {
+ m_maxread = 16384;
+ Read::SetUp();
+ }
+};
+
+class ReadNoatime: public Read {
+ virtual void SetUp() {
+ m_noatime = true;
+ Read::SetUp();
+ }
+};
+
+class ReadSigbus: public Read
+{
+public:
+static jmp_buf s_jmpbuf;
+static void *s_si_addr;
+
+void TearDown() {
+ struct sigaction sa;
+
+ bzero(&sa, sizeof(sa));
+ sa.sa_handler = SIG_DFL;
+ sigaction(SIGBUS, &sa, NULL);
+
+ FuseTest::TearDown();
+}
+
+};
+
+static void
+handle_sigbus(int signo __unused, siginfo_t *info, void *uap __unused) {
+ ReadSigbus::s_si_addr = info->si_addr;
+ longjmp(ReadSigbus::s_jmpbuf, 1);
+}
+
+jmp_buf ReadSigbus::s_jmpbuf;
+void *ReadSigbus::s_si_addr;
+
+class TimeGran: public Read, public WithParamInterface<unsigned> {
+public:
+virtual void SetUp() {
+ m_time_gran = 1 << GetParam();
+ Read::SetUp();
+}
+};
+
+/* AIO reads need to set the header's pid field correctly */
+/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */
+TEST_F(AioRead, aio_read)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+ uint8_t buf[bufsize];
+ struct aiocb iocb, *piocb;
+
+ expect_lookup(RELPATH, ino, bufsize);
+ expect_open(ino, 0, 1);
+ expect_read(ino, 0, bufsize, bufsize, CONTENTS);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ iocb.aio_nbytes = bufsize;
+ iocb.aio_fildes = fd;
+ iocb.aio_buf = buf;
+ iocb.aio_offset = 0;
+ iocb.aio_sigevent.sigev_notify = SIGEV_NONE;
+ ASSERT_EQ(0, aio_read(&iocb)) << strerror(errno);
+ ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
+ ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
+
+ leak(fd);
+}
+
+/*
+ * Without the FUSE_ASYNC_READ mount option, fuse(4) should ensure that there
+ * is at most one outstanding read operation per file handle
+ */
+TEST_F(AioRead, async_read_disabled)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = 50;
+ char buf0[bufsize], buf1[bufsize];
+ off_t off0 = 0;
+ off_t off1 = m_maxbcachebuf;
+ struct aiocb iocb0, iocb1;
+ volatile sig_atomic_t read_count = 0;
+
+ expect_lookup(RELPATH, ino, 131072);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READ &&
+ in.header.nodeid == ino &&
+ in.body.read.fh == FH &&
+ in.body.read.offset == (uint64_t)off0);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke([&](auto in __unused, auto &out __unused) {
+ read_count++;
+ /* Filesystem is slow to respond */
+ }));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READ &&
+ in.header.nodeid == ino &&
+ in.body.read.fh == FH &&
+ in.body.read.offset == (uint64_t)off1);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke([&](auto in __unused, auto &out __unused) {
+ read_count++;
+ /* Filesystem is slow to respond */
+ }));
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ /*
+ * Submit two AIO read requests, and respond to neither. If the
+ * filesystem ever gets the second read request, then we failed to
+ * limit outstanding reads.
+ */
+ iocb0.aio_nbytes = bufsize;
+ iocb0.aio_fildes = fd;
+ iocb0.aio_buf = buf0;
+ iocb0.aio_offset = off0;
+ iocb0.aio_sigevent.sigev_notify = SIGEV_NONE;
+ ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno);
+
+ iocb1.aio_nbytes = bufsize;
+ iocb1.aio_fildes = fd;
+ iocb1.aio_buf = buf1;
+ iocb1.aio_offset = off1;
+ iocb1.aio_sigevent.sigev_notify = SIGEV_NONE;
+ ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno);
+
+ /*
+ * Sleep for awhile to make sure the kernel has had a chance to issue
+ * the second read, even though the first has not yet returned
+ */
+ nap();
+ EXPECT_EQ(read_count, 1);
+
+ m_mock->kill_daemon();
+ /* Wait for AIO activity to complete, but ignore errors */
+ (void)aio_waitcomplete(NULL, NULL);
+
+ leak(fd);
+}
+
+/*
+ * With the FUSE_ASYNC_READ mount option, fuse(4) may issue multiple
+ * simultaneous read requests on the same file handle.
+ */
+TEST_F(AsyncRead, async_read)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = 50;
+ char buf0[bufsize], buf1[bufsize];
+ off_t off0 = 0;
+ off_t off1 = m_maxbcachebuf;
+ off_t fsize = 2 * m_maxbcachebuf;
+ struct aiocb iocb0, iocb1;
+ sem_t sem;
+
+ ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
+
+ expect_lookup(RELPATH, ino, fsize);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READ &&
+ in.header.nodeid == ino &&
+ in.body.read.fh == FH &&
+ in.body.read.offset == (uint64_t)off0);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke([&](auto in __unused, auto &out __unused) {
+ sem_post(&sem);
+ /* Filesystem is slow to respond */
+ }));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READ &&
+ in.header.nodeid == ino &&
+ in.body.read.fh == FH &&
+ in.body.read.offset == (uint64_t)off1);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke([&](auto in __unused, auto &out __unused) {
+ sem_post(&sem);
+ /* Filesystem is slow to respond */
+ }));
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ /*
+ * Submit two AIO read requests, but respond to neither. Ensure that
+ * we received both.
+ */
+ iocb0.aio_nbytes = bufsize;
+ iocb0.aio_fildes = fd;
+ iocb0.aio_buf = buf0;
+ iocb0.aio_offset = off0;
+ iocb0.aio_sigevent.sigev_notify = SIGEV_NONE;
+ ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno);
+
+ iocb1.aio_nbytes = bufsize;
+ iocb1.aio_fildes = fd;
+ iocb1.aio_buf = buf1;
+ iocb1.aio_offset = off1;
+ iocb1.aio_sigevent.sigev_notify = SIGEV_NONE;
+ ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno);
+
+ /* Wait until both reads have reached the daemon */
+ ASSERT_EQ(0, sem_wait(&sem)) << strerror(errno);
+ ASSERT_EQ(0, sem_wait(&sem)) << strerror(errno);
+
+ m_mock->kill_daemon();
+ /* Wait for AIO activity to complete, but ignore errors */
+ (void)aio_waitcomplete(NULL, NULL);
+
+ leak(fd);
+}
+
+/* The kernel should update the cached atime attribute during a read */
+TEST_F(Read, atime)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ struct stat sb1, sb2;
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+ uint8_t buf[bufsize];
+
+ expect_lookup(RELPATH, ino, bufsize);
+ expect_open(ino, 0, 1);
+ expect_read(ino, 0, bufsize, bufsize, CONTENTS);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(0, fstat(fd, &sb1));
+
+ /* Ensure atime will be different than it was during lookup */
+ nap();
+
+ ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
+ ASSERT_EQ(0, fstat(fd, &sb2));
+
+ /* The kernel should automatically update atime during read */
+ EXPECT_TRUE(timespeccmp(&sb1.st_atim, &sb2.st_atim, <));
+ EXPECT_TRUE(timespeccmp(&sb1.st_ctim, &sb2.st_ctim, ==));
+ EXPECT_TRUE(timespeccmp(&sb1.st_mtim, &sb2.st_mtim, ==));
+
+ leak(fd);
+}
+
+/* The kernel should update the cached atime attribute during a cached read */
+TEST_F(Read, atime_cached)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ struct stat sb1, sb2;
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+ uint8_t buf[bufsize];
+
+ expect_lookup(RELPATH, ino, bufsize);
+ expect_open(ino, 0, 1);
+ expect_read(ino, 0, bufsize, bufsize, CONTENTS);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(bufsize, pread(fd, buf, bufsize, 0)) << strerror(errno);
+ ASSERT_EQ(0, fstat(fd, &sb1));
+
+ /* Ensure atime will be different than it was during the first read */
+ nap();
+
+ ASSERT_EQ(bufsize, pread(fd, buf, bufsize, 0)) << strerror(errno);
+ ASSERT_EQ(0, fstat(fd, &sb2));
+
+ /* The kernel should automatically update atime during read */
+ EXPECT_TRUE(timespeccmp(&sb1.st_atim, &sb2.st_atim, <));
+ EXPECT_TRUE(timespeccmp(&sb1.st_ctim, &sb2.st_ctim, ==));
+ EXPECT_TRUE(timespeccmp(&sb1.st_mtim, &sb2.st_mtim, ==));
+
+ leak(fd);
+}
+
+/* dirty atime values should be flushed during close */
+TEST_F(Read, atime_during_close)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ struct stat sb;
+ uint64_t ino = 42;
+ const mode_t newmode = 0755;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+ uint8_t buf[bufsize];
+
+ expect_lookup(RELPATH, ino, bufsize);
+ expect_open(ino, 0, 1);
+ expect_read(ino, 0, bufsize, bufsize, CONTENTS);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([&](auto in) {
+ uint32_t valid = FATTR_ATIME;
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino &&
+ in.body.setattr.valid == valid &&
+ (time_t)in.body.setattr.atime ==
+ sb.st_atim.tv_sec &&
+ (long)in.body.setattr.atimensec ==
+ sb.st_atim.tv_nsec);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino;
+ out.body.attr.attr.mode = S_IFREG | newmode;
+ })));
+ expect_flush(ino, 1, ReturnErrno(0));
+ expect_release(ino, FuseTest::FH);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ /* Ensure atime will be different than during lookup */
+ nap();
+
+ ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
+ ASSERT_EQ(0, fstat(fd, &sb));
+
+ close(fd);
+}
+
+/*
+ * When not using -o default_permissions, the daemon may make its own decisions
+ * regarding access permissions, and these may be unpredictable. If it rejects
+ * our attempt to set atime, that should not cause close(2) to fail.
+ */
+TEST_F(Read, atime_during_close_eacces)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+ uint8_t buf[bufsize];
+
+ expect_lookup(RELPATH, ino, bufsize);
+ expect_open(ino, 0, 1);
+ expect_read(ino, 0, bufsize, bufsize, CONTENTS);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([&](auto in) {
+ uint32_t valid = FATTR_ATIME;
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino &&
+ in.body.setattr.valid == valid);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(EACCES)));
+ expect_flush(ino, 1, ReturnErrno(0));
+ expect_release(ino, FuseTest::FH);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ /* Ensure atime will be different than during lookup */
+ nap();
+
+ ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
+
+ ASSERT_EQ(0, close(fd));
+}
+
+/* A cached atime should be flushed during FUSE_SETATTR */
+TEST_F(Read, atime_during_setattr)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ struct stat sb;
+ uint64_t ino = 42;
+ const mode_t newmode = 0755;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+ uint8_t buf[bufsize];
+
+ expect_lookup(RELPATH, ino, bufsize);
+ expect_open(ino, 0, 1);
+ expect_read(ino, 0, bufsize, bufsize, CONTENTS);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([&](auto in) {
+ uint32_t valid = FATTR_MODE | FATTR_ATIME;
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino &&
+ in.body.setattr.valid == valid &&
+ (time_t)in.body.setattr.atime ==
+ sb.st_atim.tv_sec &&
+ (long)in.body.setattr.atimensec ==
+ sb.st_atim.tv_nsec);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino;
+ out.body.attr.attr.mode = S_IFREG | newmode;
+ })));
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ /* Ensure atime will be different than during lookup */
+ nap();
+
+ ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
+ ASSERT_EQ(0, fstat(fd, &sb));
+ ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno);
+
+ leak(fd);
+}
+
+/* 0-length reads shouldn't cause any confusion */
+TEST_F(Read, direct_io_read_nothing)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int fd;
+ uint64_t offset = 100;
+ char buf[80];
+
+ expect_lookup(RELPATH, ino, offset + 1000);
+ expect_open(ino, FOPEN_DIRECT_IO, 1);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(0, pread(fd, buf, 0, offset)) << strerror(errno);
+ leak(fd);
+}
+
+/*
+ * With direct_io, reads should not fill the cache. They should go straight to
+ * the daemon
+ */
+TEST_F(Read, direct_io_pread)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ int fd;
+ uint64_t offset = 100;
+ ssize_t bufsize = strlen(CONTENTS);
+ uint8_t buf[bufsize];
+
+ expect_lookup(RELPATH, ino, offset + bufsize);
+ expect_open(ino, FOPEN_DIRECT_IO, 1);
+ expect_read(ino, offset, bufsize, bufsize, CONTENTS);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno);
+ ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
+
+ // With FOPEN_DIRECT_IO, the cache should be bypassed. The server will
+ // get a 2nd read request.
+ expect_read(ino, offset, bufsize, bufsize, CONTENTS);
+ ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno);
+ ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
+ leak(fd);
+}
+
+/*
+ * With direct_io, filesystems are allowed to return less data than is
+ * requested. fuse(4) should return a short read to userland.
+ */
+TEST_F(Read, direct_io_short_read)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefghijklmnop";
+ uint64_t ino = 42;
+ int fd;
+ uint64_t offset = 100;
+ ssize_t bufsize = strlen(CONTENTS);
+ ssize_t halfbufsize = bufsize / 2;
+ uint8_t buf[bufsize];
+
+ expect_lookup(RELPATH, ino, offset + bufsize);
+ expect_open(ino, FOPEN_DIRECT_IO, 1);
+ expect_read(ino, offset, bufsize, halfbufsize, CONTENTS);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(halfbufsize, pread(fd, buf, bufsize, offset))
+ << strerror(errno);
+ ASSERT_EQ(0, memcmp(buf, CONTENTS, halfbufsize));
+ leak(fd);
+}
+
+TEST_F(Read, eio)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+ uint8_t buf[bufsize];
+
+ expect_lookup(RELPATH, ino, bufsize);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READ);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(EIO)));
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(-1, read(fd, buf, bufsize)) << strerror(errno);
+ ASSERT_EQ(EIO, errno);
+ leak(fd);
+}
+
+/*
+ * If the server returns a short read when direct io is not in use, that
+ * indicates EOF, because of a server-side truncation. We should invalidate
+ * all cached attributes. We may update the file size,
+ */
+TEST_F(Read, eof)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefghijklmnop";
+ uint64_t ino = 42;
+ int fd;
+ uint64_t offset = 100;
+ ssize_t bufsize = strlen(CONTENTS);
+ ssize_t partbufsize = 3 * bufsize / 4;
+ ssize_t r;
+ uint8_t buf[bufsize];
+ struct stat sb;
+
+ expect_lookup(RELPATH, ino, offset + bufsize);
+ expect_open(ino, 0, 1);
+ expect_read(ino, 0, offset + bufsize, offset + partbufsize, CONTENTS);
+ expect_getattr(ino, offset + partbufsize);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ r = pread(fd, buf, bufsize, offset);
+ ASSERT_LE(0, r) << strerror(errno);
+ EXPECT_EQ(partbufsize, r) << strerror(errno);
+ ASSERT_EQ(0, fstat(fd, &sb));
+ EXPECT_EQ((off_t)(offset + partbufsize), sb.st_size);
+ leak(fd);
+}
+
+/* Like Read.eof, but causes an entire buffer to be invalidated */
+TEST_F(Read, eof_of_whole_buffer)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefghijklmnop";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+ off_t old_filesize = m_maxbcachebuf * 2 + bufsize;
+ uint8_t buf[bufsize];
+ struct stat sb;
+
+ expect_lookup(RELPATH, ino, old_filesize);
+ expect_open(ino, 0, 1);
+ expect_read(ino, 2 * m_maxbcachebuf, bufsize, bufsize, CONTENTS);
+ expect_read(ino, m_maxbcachebuf, m_maxbcachebuf, 0, CONTENTS);
+ expect_getattr(ino, m_maxbcachebuf);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ /* Cache the third block */
+ ASSERT_EQ(bufsize, pread(fd, buf, bufsize, m_maxbcachebuf * 2))
+ << strerror(errno);
+ /* Try to read the 2nd block, but it's past EOF */
+ ASSERT_EQ(0, pread(fd, buf, bufsize, m_maxbcachebuf))
+ << strerror(errno);
+ ASSERT_EQ(0, fstat(fd, &sb));
+ EXPECT_EQ((off_t)(m_maxbcachebuf), sb.st_size);
+ leak(fd);
+}
+
+/*
+ * With the keep_cache option, the kernel may keep its read cache across
+ * multiple open(2)s.
+ */
+TEST_F(Read, keep_cache)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ int fd0, fd1;
+ ssize_t bufsize = strlen(CONTENTS);
+ uint8_t buf[bufsize];
+
+ FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2);
+ expect_open(ino, FOPEN_KEEP_CACHE, 2);
+ expect_read(ino, 0, bufsize, bufsize, CONTENTS);
+
+ fd0 = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd0) << strerror(errno);
+ ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
+
+ fd1 = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd1) << strerror(errno);
+
+ /*
+ * This read should be serviced by cache, even though it's on the other
+ * file descriptor
+ */
+ ASSERT_EQ(bufsize, read(fd1, buf, bufsize)) << strerror(errno);
+
+ leak(fd0);
+ leak(fd1);
+}
+
+/*
+ * Without the keep_cache option, the kernel should drop its read caches on
+ * every open
+ */
+TEST_F(Read, keep_cache_disabled)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ int fd0, fd1;
+ ssize_t bufsize = strlen(CONTENTS);
+ uint8_t buf[bufsize];
+
+ FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2);
+ expect_open(ino, 0, 2);
+ expect_read(ino, 0, bufsize, bufsize, CONTENTS);
+
+ fd0 = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd0) << strerror(errno);
+ ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
+
+ fd1 = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd1) << strerror(errno);
+
+ /*
+ * This read should not be serviced by cache, even though it's on the
+ * original file descriptor
+ */
+ expect_read(ino, 0, bufsize, bufsize, CONTENTS);
+ ASSERT_EQ(0, lseek(fd0, 0, SEEK_SET)) << strerror(errno);
+ ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
+
+ leak(fd0);
+ leak(fd1);
+}
+
+TEST_F(Read, mmap)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t len;
+ size_t bufsize = strlen(CONTENTS);
+ void *p;
+
+ len = getpagesize();
+
+ expect_lookup(RELPATH, ino, bufsize);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READ &&
+ in.header.nodeid == ino &&
+ in.body.read.fh == Read::FH &&
+ in.body.read.offset == 0 &&
+ in.body.read.size == bufsize);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ out.header.len = sizeof(struct fuse_out_header) + bufsize;
+ memmove(out.body.bytes, CONTENTS, bufsize);
+ })));
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
+ ASSERT_NE(MAP_FAILED, p) << strerror(errno);
+
+ ASSERT_EQ(0, memcmp(p, CONTENTS, bufsize));
+
+ ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
+ leak(fd);
+}
+
+
+/* When max_read is set, large reads will be split up as necessary */
+TEST_F(ReadMaxRead, split)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = 65536;
+ ssize_t fragsize = bufsize / 4;
+ char *rbuf, *frag0, *frag1, *frag2, *frag3;
+
+ rbuf = new char[bufsize]();
+ frag0 = new char[fragsize]();
+ frag1 = new char[fragsize]();
+ frag2 = new char[fragsize]();
+ frag3 = new char[fragsize]();
+ memset(frag0, '0', fragsize);
+ memset(frag1, '1', fragsize);
+ memset(frag2, '2', fragsize);
+ memset(frag3, '3', fragsize);
+
+ expect_lookup(RELPATH, ino, bufsize);
+ expect_open(ino, 0, 1);
+ expect_read(ino, 0, fragsize, fragsize, frag0);
+ expect_read(ino, fragsize, fragsize, fragsize, frag1);
+ expect_read(ino, 2 * fragsize, fragsize, fragsize, frag2);
+ expect_read(ino, 3 * fragsize, fragsize, fragsize, frag3);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(bufsize, read(fd, rbuf, bufsize)) << strerror(errno);
+ ASSERT_EQ(0, memcmp(rbuf, frag0, fragsize));
+ ASSERT_EQ(0, memcmp(rbuf + fragsize, frag1, fragsize));
+ ASSERT_EQ(0, memcmp(rbuf + 2 * fragsize, frag2, fragsize));
+ ASSERT_EQ(0, memcmp(rbuf + 3 * fragsize, frag3, fragsize));
+
+ delete[] frag3;
+ delete[] frag2;
+ delete[] frag1;
+ delete[] frag0;
+ delete[] rbuf;
+ leak(fd);
+}
+
+/*
+ * The kernel should not update the cached atime attribute during a read, if
+ * MNT_NOATIME is used.
+ */
+TEST_F(ReadNoatime, atime)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ struct stat sb1, sb2;
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+ uint8_t buf[bufsize];
+
+ expect_lookup(RELPATH, ino, bufsize);
+ expect_open(ino, 0, 1);
+ expect_read(ino, 0, bufsize, bufsize, CONTENTS);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(0, fstat(fd, &sb1));
+
+ nap();
+
+ ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
+ ASSERT_EQ(0, fstat(fd, &sb2));
+
+ /* The kernel should not update atime during read */
+ EXPECT_TRUE(timespeccmp(&sb1.st_atim, &sb2.st_atim, ==));
+ EXPECT_TRUE(timespeccmp(&sb1.st_ctim, &sb2.st_ctim, ==));
+ EXPECT_TRUE(timespeccmp(&sb1.st_mtim, &sb2.st_mtim, ==));
+
+ leak(fd);
+}
+
+/*
+ * The kernel should not update the cached atime attribute during a cached
+ * read, if MNT_NOATIME is used.
+ */
+TEST_F(ReadNoatime, atime_cached)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ struct stat sb1, sb2;
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+ uint8_t buf[bufsize];
+
+ expect_lookup(RELPATH, ino, bufsize);
+ expect_open(ino, 0, 1);
+ expect_read(ino, 0, bufsize, bufsize, CONTENTS);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(bufsize, pread(fd, buf, bufsize, 0)) << strerror(errno);
+ ASSERT_EQ(0, fstat(fd, &sb1));
+
+ nap();
+
+ ASSERT_EQ(bufsize, pread(fd, buf, bufsize, 0)) << strerror(errno);
+ ASSERT_EQ(0, fstat(fd, &sb2));
+
+ /* The kernel should automatically update atime during read */
+ EXPECT_TRUE(timespeccmp(&sb1.st_atim, &sb2.st_atim, ==));
+ EXPECT_TRUE(timespeccmp(&sb1.st_ctim, &sb2.st_ctim, ==));
+ EXPECT_TRUE(timespeccmp(&sb1.st_mtim, &sb2.st_mtim, ==));
+
+ leak(fd);
+}
+
+/* Read of an mmap()ed file fails */
+TEST_F(ReadSigbus, mmap_eio)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ struct sigaction sa;
+ uint64_t ino = 42;
+ int fd;
+ ssize_t len;
+ size_t bufsize = strlen(CONTENTS);
+ void *p;
+
+ len = getpagesize();
+
+ expect_lookup(RELPATH, ino, bufsize);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READ &&
+ in.header.nodeid == ino &&
+ in.body.read.fh == Read::FH);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnErrno(EIO)));
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
+ ASSERT_NE(MAP_FAILED, p) << strerror(errno);
+
+ /* Accessing the mapped page should return SIGBUS. */
+
+ bzero(&sa, sizeof(sa));
+ sa.sa_handler = SIG_DFL;
+ sa.sa_sigaction = handle_sigbus;
+ sa.sa_flags = SA_RESETHAND | SA_SIGINFO;
+ ASSERT_EQ(0, sigaction(SIGBUS, &sa, NULL)) << strerror(errno);
+ if (setjmp(ReadSigbus::s_jmpbuf) == 0) {
+ atomic_signal_fence(std::memory_order::memory_order_seq_cst);
+ volatile char x __unused = *(volatile char*)p;
+ FAIL() << "shouldn't get here";
+ }
+
+ ASSERT_EQ(p, ReadSigbus::s_si_addr);
+ ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
+ leak(fd);
+}
+
+/*
+ * A read via mmap comes up short, indicating that the file was truncated
+ * server-side.
+ */
+TEST_F(Read, mmap_eof)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t len;
+ size_t bufsize = strlen(CONTENTS);
+ struct stat sb;
+ void *p;
+
+ len = getpagesize();
+
+ expect_lookup(RELPATH, ino, m_maxbcachebuf);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READ &&
+ in.header.nodeid == ino &&
+ in.body.read.fh == Read::FH &&
+ in.body.read.offset == 0 &&
+ in.body.read.size == (uint32_t)m_maxbcachebuf);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ out.header.len = sizeof(struct fuse_out_header) + bufsize;
+ memmove(out.body.bytes, CONTENTS, bufsize);
+ })));
+ expect_getattr(ino, bufsize);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
+ ASSERT_NE(MAP_FAILED, p) << strerror(errno);
+
+ /* The file size should be automatically truncated */
+ ASSERT_EQ(0, memcmp(p, CONTENTS, bufsize));
+ ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
+ EXPECT_EQ((off_t)bufsize, sb.st_size);
+
+ ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
+ leak(fd);
+}
+
+/*
+ * During VOP_GETPAGES, the FUSE server fails a FUSE_GETATTR operation. This
+ * almost certainly indicates a buggy FUSE server, and our goal should be not
+ * to panic. Instead, generate SIGBUS.
+ */
+TEST_F(ReadSigbus, mmap_getblksz_fail)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ struct sigaction sa;
+ Sequence seq;
+ uint64_t ino = 42;
+ int fd;
+ ssize_t len;
+ size_t bufsize = strlen(CONTENTS);
+ mode_t mode = S_IFREG | 0644;
+ void *p;
+
+ len = getpagesize();
+
+ FuseTest::expect_lookup(RELPATH, ino, mode, bufsize, 1, 0);
+ /* Expect two GETATTR calls that succeed, followed by one that fail. */
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).Times(2)
+ .InSequence(seq)
+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino;
+ out.body.attr.attr.mode = mode;
+ out.body.attr.attr.size = bufsize;
+ out.body.attr.attr_valid = 0;
+ })));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).InSequence(seq)
+ .WillRepeatedly(Invoke(ReturnErrno(EIO)));
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READ);
+ }, Eq(true)),
+ _)
+ ).Times(0);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
+ ASSERT_NE(MAP_FAILED, p) << strerror(errno);
+
+ /* Accessing the mapped page should return SIGBUS. */
+ bzero(&sa, sizeof(sa));
+ sa.sa_handler = SIG_DFL;
+ sa.sa_sigaction = handle_sigbus;
+ sa.sa_flags = SA_RESETHAND | SA_SIGINFO;
+ ASSERT_EQ(0, sigaction(SIGBUS, &sa, NULL)) << strerror(errno);
+ if (setjmp(ReadSigbus::s_jmpbuf) == 0) {
+ atomic_signal_fence(std::memory_order::memory_order_seq_cst);
+ volatile char x __unused = *(volatile char*)p;
+ FAIL() << "shouldn't get here";
+ }
+
+ ASSERT_EQ(p, ReadSigbus::s_si_addr);
+ ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
+ leak(fd);
+}
+
+/*
+ * Just as when FOPEN_DIRECT_IO is used, reads with O_DIRECT should bypass
+ * cache and to straight to the daemon
+ */
+TEST_F(Read, o_direct)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+ uint8_t buf[bufsize];
+
+ expect_lookup(RELPATH, ino, bufsize);
+ expect_open(ino, 0, 1);
+ expect_read(ino, 0, bufsize, bufsize, CONTENTS);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ // Fill the cache
+ ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
+ ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
+
+ // Reads with o_direct should bypass the cache
+ expect_read(ino, 0, bufsize, bufsize, CONTENTS);
+ ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
+ ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
+ ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
+ ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
+
+ leak(fd);
+}
+
+TEST_F(Read, pread)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ int fd;
+ /*
+ * Set offset to a maxbcachebuf boundary so we'll be sure what offset
+ * to read from. Without this, the read might start at a lower offset.
+ */
+ uint64_t offset = m_maxbcachebuf;
+ ssize_t bufsize = strlen(CONTENTS);
+ uint8_t buf[bufsize];
+
+ expect_lookup(RELPATH, ino, offset + bufsize);
+ expect_open(ino, 0, 1);
+ expect_read(ino, offset, bufsize, bufsize, CONTENTS);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno);
+ ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
+ leak(fd);
+}
+
+TEST_F(Read, read)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+ uint8_t buf[bufsize];
+
+ expect_lookup(RELPATH, ino, bufsize);
+ expect_open(ino, 0, 1);
+ expect_read(ino, 0, bufsize, bufsize, CONTENTS);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
+ ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
+
+ leak(fd);
+}
+
+TEST_F(Read_7_8, read)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+ uint8_t buf[bufsize];
+
+ expect_lookup(RELPATH, ino, bufsize);
+ expect_open(ino, 0, 1);
+ expect_read(ino, 0, bufsize, bufsize, CONTENTS);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
+ ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
+
+ leak(fd);
+}
+
+/*
+ * If cacheing is enabled, the kernel should try to read an entire cache block
+ * at a time.
+ */
+TEST_F(Read, cache_block)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS0 = "abcdefghijklmnop";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = 8;
+ ssize_t filesize = m_maxbcachebuf * 2;
+ char *contents;
+ char buf[bufsize];
+ const char *contents1 = CONTENTS0 + bufsize;
+
+ contents = new char[filesize]();
+ memmove(contents, CONTENTS0, strlen(CONTENTS0));
+
+ expect_lookup(RELPATH, ino, filesize);
+ expect_open(ino, 0, 1);
+ expect_read(ino, 0, m_maxbcachebuf, m_maxbcachebuf,
+ contents);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
+ ASSERT_EQ(0, memcmp(buf, CONTENTS0, bufsize));
+
+ /* A subsequent read should be serviced by cache */
+ ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
+ ASSERT_EQ(0, memcmp(buf, contents1, bufsize));
+ leak(fd);
+ delete[] contents;
+}
+
+/* Reading with sendfile should work (though it obviously won't be 0-copy) */
+TEST_F(Read, sendfile)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ int fd;
+ size_t bufsize = strlen(CONTENTS);
+ uint8_t buf[bufsize];
+ int sp[2];
+ off_t sbytes;
+
+ expect_lookup(RELPATH, ino, bufsize);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READ &&
+ in.header.nodeid == ino &&
+ in.body.read.fh == Read::FH &&
+ in.body.read.offset == 0 &&
+ in.body.read.size == bufsize);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ out.header.len = sizeof(struct fuse_out_header) + bufsize;
+ memmove(out.body.bytes, CONTENTS, bufsize);
+ })));
+
+ ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp))
+ << strerror(errno);
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0))
+ << strerror(errno);
+ ASSERT_EQ(static_cast<ssize_t>(bufsize), read(sp[0], buf, bufsize))
+ << strerror(errno);
+ ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
+
+ close(sp[1]);
+ close(sp[0]);
+ leak(fd);
+}
+
+/* sendfile should fail gracefully if fuse declines the read */
+/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236466 */
+TEST_F(Read, sendfile_eio)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+ int sp[2];
+ off_t sbytes;
+
+ expect_lookup(RELPATH, ino, bufsize);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READ);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(EIO)));
+
+ ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp))
+ << strerror(errno);
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_NE(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0));
+
+ close(sp[1]);
+ close(sp[0]);
+ leak(fd);
+}
+
+/*
+ * Sequential reads should use readahead. And if allowed, large reads should
+ * be clustered.
+ */
+TEST_P(ReadAhead, readahead) {
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int fd, maxcontig, clustersize;
+ ssize_t bufsize = 4 * m_maxbcachebuf;
+ ssize_t filesize = bufsize;
+ uint64_t len;
+ char *rbuf, *contents;
+ off_t offs;
+
+ contents = new char[filesize];
+ memset(contents, 'X', filesize);
+ rbuf = new char[bufsize]();
+
+ expect_lookup(RELPATH, ino, filesize);
+ expect_open(ino, 0, 1);
+ maxcontig = m_noclusterr ? m_maxbcachebuf :
+ m_maxbcachebuf + m_maxreadahead;
+ clustersize = MIN((unsigned long )maxcontig, m_maxphys);
+ for (offs = 0; offs < bufsize; offs += clustersize) {
+ len = std::min((size_t)clustersize, (size_t)(filesize - offs));
+ expect_read(ino, offs, len, len, contents + offs);
+ }
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ /* Set the internal readahead counter to a "large" value */
+ ASSERT_EQ(0, fcntl(fd, F_READAHEAD, 1'000'000'000)) << strerror(errno);
+
+ ASSERT_EQ(bufsize, read(fd, rbuf, bufsize)) << strerror(errno);
+ ASSERT_EQ(0, memcmp(rbuf, contents, bufsize));
+
+ leak(fd);
+ delete[] rbuf;
+ delete[] contents;
+}
+
+INSTANTIATE_TEST_SUITE_P(RA, ReadAhead,
+ Values(tuple<bool, int>(false, 0),
+ tuple<bool, int>(false, 1),
+ tuple<bool, int>(false, 2),
+ tuple<bool, int>(false, 3),
+ tuple<bool, int>(true, 0),
+ tuple<bool, int>(true, 1),
+ tuple<bool, int>(true, 2)));
+
+/* With read-only mounts, fuse should never update atime during close */
+TEST_F(RofsRead, atime_during_close)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+ uint8_t buf[bufsize];
+
+ expect_lookup(RELPATH, ino, bufsize);
+ expect_open(ino, 0, 1);
+ expect_read(ino, 0, bufsize, bufsize, CONTENTS);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([&](auto in) {
+ return (in.header.opcode == FUSE_SETATTR);
+ }, Eq(true)),
+ _)
+ ).Times(0);
+ expect_flush(ino, 1, ReturnErrno(0));
+ expect_release(ino, FuseTest::FH);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ /* Ensure atime will be different than during lookup */
+ nap();
+
+ ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
+
+ close(fd);
+}
+
+/* fuse_init_out.time_gran controls the granularity of timestamps */
+TEST_P(TimeGran, atime_during_setattr)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ ssize_t bufsize = strlen(CONTENTS);
+ uint8_t buf[bufsize];
+ uint64_t ino = 42;
+ const mode_t newmode = 0755;
+ int fd;
+
+ expect_lookup(RELPATH, ino, bufsize);
+ expect_open(ino, 0, 1);
+ expect_read(ino, 0, bufsize, bufsize, CONTENTS);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ uint32_t valid = FATTR_MODE | FATTR_ATIME;
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino &&
+ in.body.setattr.valid == valid &&
+ in.body.setattr.atimensec % m_time_gran == 0);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino;
+ out.body.attr.attr.mode = S_IFREG | newmode;
+ })));
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
+ ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno);
+
+ leak(fd);
+}
+
+INSTANTIATE_TEST_SUITE_P(TG, TimeGran, Range(0u, 10u));
diff --git a/tests/sys/fs/fusefs/readdir.cc b/tests/sys/fs/fusefs/readdir.cc
new file mode 100644
index 000000000000..6b78e3a70697
--- /dev/null
+++ b/tests/sys/fs/fusefs/readdir.cc
@@ -0,0 +1,516 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <dirent.h>
+#include <fcntl.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+using namespace std;
+
+class Readdir: public FuseTest {
+public:
+void expect_lookup(const char *relpath, uint64_t ino)
+{
+ FuseTest::expect_lookup(relpath, ino, S_IFDIR | 0755, 0, 1);
+}
+};
+
+class Readdir_7_8: public Readdir {
+public:
+virtual void SetUp() {
+ m_kernel_minor_version = 8;
+ Readdir::SetUp();
+}
+
+void expect_lookup(const char *relpath, uint64_t ino)
+{
+ FuseTest::expect_lookup_7_8(relpath, ino, S_IFDIR | 0755, 0, 1);
+}
+};
+
+const char dot[] = ".";
+const char dotdot[] = "..";
+
+/* FUSE_READDIR returns nothing but "." and ".." */
+TEST_F(Readdir, dots)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ uint64_t ino = 42;
+ DIR *dir;
+ struct dirent *de;
+ vector<struct dirent> ents(2);
+ vector<struct dirent> empty_ents(0);
+
+ expect_lookup(RELPATH, ino);
+ expect_opendir(ino);
+ ents[0].d_fileno = 2;
+ ents[0].d_off = 2000;
+ ents[0].d_namlen = sizeof(dotdot);
+ ents[0].d_type = DT_DIR;
+ strncpy(ents[0].d_name, dotdot, ents[0].d_namlen);
+ ents[1].d_fileno = 3;
+ ents[1].d_off = 3000;
+ ents[1].d_namlen = sizeof(dot);
+ ents[1].d_type = DT_DIR;
+ strncpy(ents[1].d_name, dot, ents[1].d_namlen);
+ expect_readdir(ino, 0, ents);
+ expect_readdir(ino, 3000, empty_ents);
+
+ errno = 0;
+ dir = opendir(FULLPATH);
+ ASSERT_NE(nullptr, dir) << strerror(errno);
+
+ errno = 0;
+ de = readdir(dir);
+ ASSERT_NE(nullptr, de) << strerror(errno);
+ EXPECT_EQ(2ul, de->d_fileno);
+ EXPECT_EQ(DT_DIR, de->d_type);
+ EXPECT_EQ(sizeof(dotdot), de->d_namlen);
+ EXPECT_EQ(0, strcmp(dotdot, de->d_name));
+
+ errno = 0;
+ de = readdir(dir);
+ ASSERT_NE(nullptr, de) << strerror(errno);
+ EXPECT_EQ(3ul, de->d_fileno);
+ EXPECT_EQ(DT_DIR, de->d_type);
+ EXPECT_EQ(sizeof(dot), de->d_namlen);
+ EXPECT_EQ(0, strcmp(dot, de->d_name));
+
+ ASSERT_EQ(nullptr, readdir(dir));
+ ASSERT_EQ(0, errno);
+
+ leakdir(dir);
+}
+
+TEST_F(Readdir, eio)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ uint64_t ino = 42;
+ DIR *dir;
+ struct dirent *de;
+
+ expect_lookup(RELPATH, ino);
+ expect_opendir(ino);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READDIR &&
+ in.header.nodeid == ino &&
+ in.body.readdir.offset == 0);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(EIO)));
+
+ errno = 0;
+ dir = opendir(FULLPATH);
+ ASSERT_NE(nullptr, dir) << strerror(errno);
+
+ errno = 0;
+ de = readdir(dir);
+ ASSERT_EQ(nullptr, de);
+ ASSERT_EQ(EIO, errno);
+
+ leakdir(dir);
+}
+
+/*
+ * getdirentries(2) can use a larger buffer size than readdir(3). It also has
+ * some additional non-standardized fields in the returned dirent.
+ */
+TEST_F(Readdir, getdirentries_empty)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ uint64_t ino = 42;
+ int fd;
+ char buf[8192];
+ ssize_t r;
+
+ expect_lookup(RELPATH, ino);
+ expect_opendir(ino);
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READDIR &&
+ in.header.nodeid == ino &&
+ in.body.readdir.size == 8192);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ out.header.error = 0;
+ out.header.len = sizeof(out.header);
+ })));
+
+ fd = open(FULLPATH, O_DIRECTORY);
+ ASSERT_LE(0, fd) << strerror(errno);
+ r = getdirentries(fd, buf, sizeof(buf), 0);
+ ASSERT_EQ(0, r) << strerror(errno);
+
+ leak(fd);
+}
+
+/*
+ * The dirent.d_off field can be used with lseek to position the directory so
+ * that getdirentries will return the subsequent dirent.
+ */
+TEST_F(Readdir, getdirentries_seek)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ vector<struct dirent> ents0(2);
+ vector<struct dirent> ents1(1);
+ uint64_t ino = 42;
+ int fd;
+ const size_t bufsize = 8192;
+ char buf[bufsize];
+ struct dirent *de0, *de1;
+ ssize_t r;
+
+ expect_lookup(RELPATH, ino);
+ expect_opendir(ino);
+
+ ents0[0].d_fileno = 2;
+ ents0[0].d_off = 2000;
+ ents0[0].d_namlen = sizeof(dotdot);
+ ents0[0].d_type = DT_DIR;
+ strncpy(ents0[0].d_name, dotdot, ents0[0].d_namlen);
+ expect_readdir(ino, 0, ents0);
+ ents0[1].d_fileno = 3;
+ ents0[1].d_off = 3000;
+ ents0[1].d_namlen = sizeof(dot);
+ ents0[1].d_type = DT_DIR;
+ ents1[0].d_fileno = 3;
+ ents1[0].d_off = 3000;
+ ents1[0].d_namlen = sizeof(dot);
+ ents1[0].d_type = DT_DIR;
+ strncpy(ents1[0].d_name, dot, ents1[0].d_namlen);
+ expect_readdir(ino, 0, ents0);
+ expect_readdir(ino, 2000, ents1);
+
+ fd = open(FULLPATH, O_DIRECTORY);
+ ASSERT_LE(0, fd) << strerror(errno);
+ r = getdirentries(fd, buf, sizeof(buf), 0);
+ ASSERT_LT(0, r) << strerror(errno);
+ de0 = (struct dirent*)&buf[0];
+ ASSERT_EQ(2000, de0->d_off);
+ ASSERT_LT(de0->d_reclen + offsetof(struct dirent, d_fileno), bufsize);
+ de1 = (struct dirent*)(&(buf[de0->d_reclen]));
+ ASSERT_EQ(3ul, de1->d_fileno);
+
+ r = lseek(fd, de0->d_off, SEEK_SET);
+ ASSERT_LE(0, r);
+ r = getdirentries(fd, buf, sizeof(buf), 0);
+ ASSERT_LT(0, r) << strerror(errno);
+ de0 = (struct dirent*)&buf[0];
+ ASSERT_EQ(3000, de0->d_off);
+}
+
+/*
+ * Nothing bad should happen if getdirentries is called on two file descriptors
+ * which were concurrently open, but one has already been closed.
+ * This is a regression test for a specific bug dating from r238402.
+ */
+TEST_F(Readdir, getdirentries_concurrent)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ uint64_t ino = 42;
+ int fd0, fd1;
+ char buf[8192];
+ ssize_t r;
+
+ FuseTest::expect_lookup(RELPATH, ino, S_IFDIR | 0755, 0, 2);
+ expect_opendir(ino);
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READDIR &&
+ in.header.nodeid == ino &&
+ in.body.readdir.size == 8192);
+ }, Eq(true)),
+ _)
+ ).Times(2)
+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ out.header.error = 0;
+ out.header.len = sizeof(out.header);
+ })));
+
+ fd0 = open(FULLPATH, O_DIRECTORY);
+ ASSERT_LE(0, fd0) << strerror(errno);
+
+ fd1 = open(FULLPATH, O_DIRECTORY);
+ ASSERT_LE(0, fd1) << strerror(errno);
+
+ r = getdirentries(fd0, buf, sizeof(buf), 0);
+ ASSERT_EQ(0, r) << strerror(errno);
+
+ EXPECT_EQ(0, close(fd0)) << strerror(errno);
+
+ r = getdirentries(fd1, buf, sizeof(buf), 0);
+ ASSERT_EQ(0, r) << strerror(errno);
+
+ leak(fd0);
+ leak(fd1);
+}
+
+/*
+ * FUSE_READDIR returns nothing, not even "." and "..". This is legal, though
+ * the filesystem obviously won't be fully functional.
+ */
+TEST_F(Readdir, nodots)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ uint64_t ino = 42;
+ DIR *dir;
+
+ expect_lookup(RELPATH, ino);
+ expect_opendir(ino);
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READDIR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ out.header.error = 0;
+ out.header.len = sizeof(out.header);
+ })));
+
+ errno = 0;
+ dir = opendir(FULLPATH);
+ ASSERT_NE(nullptr, dir) << strerror(errno);
+ errno = 0;
+ ASSERT_EQ(nullptr, readdir(dir));
+ ASSERT_EQ(0, errno);
+
+ leakdir(dir);
+}
+
+/*
+ * FUSE_READDIR returns a path with an embedded NUL. Obviously illegal, but
+ * nothing bad should happen.
+ */
+TEST_F(Readdir, nul)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ uint64_t ino = 42;
+ DIR *dir;
+ struct dirent *de;
+ vector<struct dirent> ents(1);
+ vector<struct dirent> empty_ents(0);
+ const char nul[] = "foo\0bar";
+
+ expect_lookup(RELPATH, ino);
+ expect_opendir(ino);
+ ents[0].d_fileno = 4;
+ ents[0].d_off = 4000;
+ ents[0].d_namlen = sizeof(nul);
+ ents[0].d_type = DT_REG;
+ strncpy(ents[0].d_name, nul, ents[0].d_namlen);
+ expect_readdir(ino, 0, ents);
+ expect_readdir(ino, 4000, empty_ents);
+
+ errno = 0;
+ dir = opendir(FULLPATH);
+ ASSERT_NE(nullptr, dir) << strerror(errno);
+
+ errno = 0;
+ de = readdir(dir);
+ ASSERT_NE(nullptr, de) << strerror(errno);
+ EXPECT_EQ(4ul, de->d_fileno);
+ EXPECT_EQ(DT_REG, de->d_type);
+ EXPECT_EQ(sizeof(nul), de->d_namlen);
+ EXPECT_EQ(0, strcmp(nul, de->d_name));
+
+ ASSERT_EQ(nullptr, readdir(dir));
+ ASSERT_EQ(0, errno);
+
+ leakdir(dir);
+}
+
+
+/* telldir(3) and seekdir(3) should work with fuse */
+TEST_F(Readdir, seekdir)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ uint64_t ino = 42;
+ DIR *dir;
+ struct dirent *de;
+ /*
+ * use enough entries to be > 4096 bytes, so getdirentries must be
+ * called
+ * multiple times.
+ */
+ vector<struct dirent> ents0(122), ents1(102), ents2(30);
+ long bookmark;
+ int i = 0;
+
+ for (auto& it: ents0) {
+ snprintf(it.d_name, MAXNAMLEN, "file.%d", i);
+ it.d_fileno = 2 + i;
+ it.d_off = (2 + i) * 1000;
+ it.d_namlen = strlen(it.d_name);
+ it.d_type = DT_REG;
+ i++;
+ }
+ for (auto& it: ents1) {
+ snprintf(it.d_name, MAXNAMLEN, "file.%d", i);
+ it.d_fileno = 2 + i;
+ it.d_off = (2 + i) * 1000;
+ it.d_namlen = strlen(it.d_name);
+ it.d_type = DT_REG;
+ i++;
+ }
+ for (auto& it: ents2) {
+ snprintf(it.d_name, MAXNAMLEN, "file.%d", i);
+ it.d_fileno = 2 + i;
+ it.d_off = (2 + i) * 1000;
+ it.d_namlen = strlen(it.d_name);
+ it.d_type = DT_REG;
+ i++;
+ }
+
+ expect_lookup(RELPATH, ino);
+ expect_opendir(ino);
+
+ expect_readdir(ino, 0, ents0);
+ expect_readdir(ino, 123000, ents1);
+ expect_readdir(ino, 225000, ents2);
+
+ errno = 0;
+ dir = opendir(FULLPATH);
+ ASSERT_NE(nullptr, dir) << strerror(errno);
+
+ for (i=0; i < 128; i++) {
+ errno = 0;
+ de = readdir(dir);
+ ASSERT_NE(nullptr, de) << strerror(errno);
+ EXPECT_EQ(2 + (ino_t)i, de->d_fileno);
+ }
+ bookmark = telldir(dir);
+
+ for (; i < 232; i++) {
+ errno = 0;
+ de = readdir(dir);
+ ASSERT_NE(nullptr, de) << strerror(errno);
+ EXPECT_EQ(2 + (ino_t)i, de->d_fileno);
+ }
+
+ seekdir(dir, bookmark);
+ de = readdir(dir);
+ ASSERT_NE(nullptr, de) << strerror(errno);
+ EXPECT_EQ(130ul, de->d_fileno);
+
+ leakdir(dir);
+}
+
+/*
+ * FUSE_READDIR returns a path with an embedded /. Obviously illegal, but
+ * nothing bad should happen.
+ */
+TEST_F(Readdir, slash)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ uint64_t ino = 42;
+ DIR *dir;
+ struct dirent *de;
+ vector<struct dirent> ents(1);
+ vector<struct dirent> empty_ents(0);
+ const char foobar[] = "foo/bar";
+
+ expect_lookup(RELPATH, ino);
+ expect_opendir(ino);
+ ents[0].d_fileno = 4;
+ ents[0].d_off = 4000;
+ ents[0].d_namlen = sizeof(foobar);
+ ents[0].d_type = DT_REG;
+ strncpy(ents[0].d_name, foobar, ents[0].d_namlen);
+ expect_readdir(ino, 0, ents);
+ expect_readdir(ino, 4000, empty_ents);
+
+ errno = 0;
+ dir = opendir(FULLPATH);
+ ASSERT_NE(nullptr, dir) << strerror(errno);
+
+ errno = 0;
+ de = readdir(dir);
+ ASSERT_NE(nullptr, de) << strerror(errno);
+ EXPECT_EQ(4ul, de->d_fileno);
+ EXPECT_EQ(DT_REG, de->d_type);
+ EXPECT_EQ(sizeof(foobar), de->d_namlen);
+ EXPECT_EQ(0, strcmp(foobar, de->d_name));
+
+ ASSERT_EQ(nullptr, readdir(dir));
+ ASSERT_EQ(0, errno);
+
+ leakdir(dir);
+}
+
+TEST_F(Readdir_7_8, nodots)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ uint64_t ino = 42;
+ DIR *dir;
+
+ expect_lookup(RELPATH, ino);
+ expect_opendir(ino);
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READDIR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ out.header.error = 0;
+ out.header.len = sizeof(out.header);
+ })));
+
+ errno = 0;
+ dir = opendir(FULLPATH);
+ ASSERT_NE(nullptr, dir) << strerror(errno);
+ errno = 0;
+ ASSERT_EQ(nullptr, readdir(dir));
+ ASSERT_EQ(0, errno);
+
+ leakdir(dir);
+}
diff --git a/tests/sys/fs/fusefs/readlink.cc b/tests/sys/fs/fusefs/readlink.cc
new file mode 100644
index 000000000000..30815f2cd4b6
--- /dev/null
+++ b/tests/sys/fs/fusefs/readlink.cc
@@ -0,0 +1,162 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <sys/param.h>
+
+#include <unistd.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class Readlink: public FuseTest {
+public:
+void expect_lookup(const char *relpath, uint64_t ino)
+{
+ FuseTest::expect_lookup(relpath, ino, S_IFLNK | 0777, 0, 1);
+}
+void expect_readlink(uint64_t ino, ProcessMockerT r)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READLINK &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(r));
+}
+
+};
+
+class PushSymlinksIn: public Readlink {
+ virtual void SetUp() {
+ m_push_symlinks_in = true;
+ Readlink::SetUp();
+ }
+};
+
+TEST_F(Readlink, eloop)
+{
+ const char FULLPATH[] = "mountpoint/src";
+ const char RELPATH[] = "src";
+ const uint64_t ino = 42;
+ char buf[80];
+
+ expect_lookup(RELPATH, ino);
+ expect_readlink(ino, ReturnErrno(ELOOP));
+
+ EXPECT_EQ(-1, readlink(FULLPATH, buf, sizeof(buf)));
+ EXPECT_EQ(ELOOP, errno);
+}
+
+/*
+ * If a malicious or buggy server returns a NUL in the FUSE_READLINK result, it
+ * should be handled gracefully.
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=274268
+ */
+TEST_F(Readlink, embedded_nul)
+{
+ const char FULLPATH[] = "mountpoint/src";
+ const char RELPATH[] = "src";
+ const char dst[] = "dst\0stuff";
+ char buf[80];
+ const uint64_t ino = 42;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFLNK | 0777;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READLINK &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ memcpy(out.body.str, dst, sizeof(dst));
+ out.header.len = sizeof(out.header) + sizeof(dst) + 1;
+ })));
+
+ EXPECT_EQ(-1, readlink(FULLPATH, buf, sizeof(buf)));
+ EXPECT_EQ(EIO, errno);
+ EXPECT_EQ(-1, access(FULLPATH, R_OK));
+ EXPECT_EQ(EIO, errno);
+}
+
+TEST_F(Readlink, ok)
+{
+ const char FULLPATH[] = "mountpoint/src";
+ const char RELPATH[] = "src";
+ const char dst[] = "dst";
+ const uint64_t ino = 42;
+ char buf[80];
+
+ expect_lookup(RELPATH, ino);
+ expect_readlink(ino, ReturnImmediate([=](auto in __unused, auto& out) {
+ strlcpy(out.body.str, dst, sizeof(out.body.str));
+ out.header.len = sizeof(out.header) + strlen(dst) + 1;
+ }));
+
+ EXPECT_EQ(static_cast<ssize_t>(strlen(dst)) + 1,
+ readlink(FULLPATH, buf, sizeof(buf)));
+ EXPECT_STREQ(dst, buf);
+}
+
+TEST_F(PushSymlinksIn, readlink)
+{
+ const char FULLPATH[] = "mountpoint/src";
+ const char RELPATH[] = "src";
+ const char dst[] = "/dst";
+ const uint64_t ino = 42;
+ char buf[MAXPATHLEN], wd[MAXPATHLEN], want[MAXPATHLEN];
+ int len;
+
+ expect_lookup(RELPATH, ino);
+ expect_readlink(ino, ReturnImmediate([=](auto in __unused, auto& out) {
+ strlcpy(out.body.str, dst, sizeof(out.body.str));
+ out.header.len = sizeof(out.header) + strlen(dst) + 1;
+ }));
+
+ ASSERT_NE(nullptr, getcwd(wd, sizeof(wd))) << strerror(errno);
+ len = snprintf(want, sizeof(want), "%s/mountpoint%s", wd, dst);
+ ASSERT_LE(0, len) << strerror(errno);
+
+ EXPECT_EQ(static_cast<ssize_t>(len) + 1,
+ readlink(FULLPATH, buf, sizeof(buf)));
+ EXPECT_STREQ(want, buf);
+}
diff --git a/tests/sys/fs/fusefs/release.cc b/tests/sys/fs/fusefs/release.cc
new file mode 100644
index 000000000000..b664ba512b64
--- /dev/null
+++ b/tests/sys/fs/fusefs/release.cc
@@ -0,0 +1,235 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <fcntl.h>
+#include <unistd.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class Release: public FuseTest {
+
+public:
+void expect_lookup(const char *relpath, uint64_t ino, int times)
+{
+ FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, times);
+}
+
+void expect_release(uint64_t ino, uint64_t lock_owner,
+ uint32_t flags, int error)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_RELEASE &&
+ in.header.nodeid == ino &&
+ in.body.release.lock_owner == lock_owner &&
+ in.body.release.fh == FH &&
+ in.body.release.flags == flags);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(error)))
+ .RetiresOnSaturation();
+}
+};
+
+class ReleaseWithLocks: public Release {
+ virtual void SetUp() {
+ m_init_flags = FUSE_POSIX_LOCKS;
+ Release::SetUp();
+ }
+};
+
+
+/* If a file descriptor is duplicated, only the last close causes RELEASE */
+TEST_F(Release, dup)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int fd, fd2;
+
+ expect_lookup(RELPATH, ino, 1);
+ expect_open(ino, 0, 1);
+ expect_flush(ino, 1, ReturnErrno(0));
+ expect_release(ino, getpid(), O_RDONLY, 0);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ fd2 = dup(fd);
+ ASSERT_LE(0, fd2) << strerror(errno);
+
+ ASSERT_EQ(0, close(fd2)) << strerror(errno);
+ ASSERT_EQ(0, close(fd)) << strerror(errno);
+}
+
+/*
+ * Some FUSE filesystem cache data internally and flush it on release. Such
+ * filesystems may generate errors during release. On Linux, these get
+ * returned by close(2). However, POSIX does not require close(2) to return
+ * this error. FreeBSD's fuse(4) should return EIO if it returns an error at
+ * all.
+ */
+/* http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html */
+TEST_F(Release, eio)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int fd;
+
+ expect_lookup(RELPATH, ino, 1);
+ expect_open(ino, 0, 1);
+ expect_flush(ino, 1, ReturnErrno(0));
+ expect_release(ino, getpid(), O_WRONLY, EIO);
+
+ fd = open(FULLPATH, O_WRONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_TRUE(0 == close(fd) || errno == EIO) << strerror(errno);
+}
+
+/*
+ * FUSE_RELEASE should contain the same flags used for FUSE_OPEN
+ */
+/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236340 */
+TEST_F(Release, DISABLED_flags)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int fd;
+
+ expect_lookup(RELPATH, ino, 1);
+ expect_open(ino, 0, 1);
+ expect_flush(ino, 1, ReturnErrno(0));
+ expect_release(ino, getpid(), O_RDWR | O_APPEND, 0);
+
+ fd = open(FULLPATH, O_RDWR | O_APPEND);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(0, close(fd)) << strerror(errno);
+}
+
+/*
+ * fuse(4) will issue multiple FUSE_OPEN operations for the same file if it's
+ * opened with different modes. Each FUSE_OPEN should get its own
+ * FUSE_RELEASE.
+ */
+TEST_F(Release, multiple_opens)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int fd, fd2;
+
+ expect_lookup(RELPATH, ino, 2);
+ expect_open(ino, 0, 2);
+ expect_flush(ino, 2, ReturnErrno(0));
+ expect_release(ino, getpid(), O_RDONLY, 0);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ expect_release(ino, getpid(), O_WRONLY, 0);
+ fd2 = open(FULLPATH, O_WRONLY);
+ ASSERT_LE(0, fd2) << strerror(errno);
+
+ ASSERT_EQ(0, close(fd2)) << strerror(errno);
+ ASSERT_EQ(0, close(fd)) << strerror(errno);
+}
+
+TEST_F(Release, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int fd;
+
+ expect_lookup(RELPATH, ino, 1);
+ expect_open(ino, 0, 1);
+ expect_flush(ino, 1, ReturnErrno(0));
+ expect_release(ino, getpid(), O_RDONLY, 0);
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(0, close(fd)) << strerror(errno);
+}
+
+/* When closing a file with a POSIX file lock, release should release the lock*/
+TEST_F(ReleaseWithLocks, unlock_on_close)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int fd;
+ struct flock fl;
+ pid_t pid = getpid();
+
+ expect_lookup(RELPATH, ino, 1);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_SETLK &&
+ in.header.nodeid == ino &&
+ in.body.setlk.lk.type == F_RDLCK &&
+ in.body.setlk.fh == FH);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(0)));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_SETLK &&
+ in.header.nodeid == ino &&
+ in.body.setlk.lk.type == F_UNLCK &&
+ in.body.setlk.fh == FH);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(0)));
+ expect_flush(ino, 1, ReturnErrno(0));
+ expect_release(ino, static_cast<uint64_t>(pid), O_RDWR, 0);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_pid = pid;
+ fl.l_type = F_RDLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_sysid = 0;
+ ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno);
+
+ ASSERT_EQ(0, close(fd)) << strerror(errno);
+}
diff --git a/tests/sys/fs/fusefs/releasedir.cc b/tests/sys/fs/fusefs/releasedir.cc
new file mode 100644
index 000000000000..0f9337a5b761
--- /dev/null
+++ b/tests/sys/fs/fusefs/releasedir.cc
@@ -0,0 +1,116 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <dirent.h>
+#include <fcntl.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class ReleaseDir: public FuseTest {
+
+public:
+void expect_lookup(const char *relpath, uint64_t ino)
+{
+ FuseTest::expect_lookup(relpath, ino, S_IFDIR | 0755, 0, 1);
+}
+};
+
+/* If a file descriptor is duplicated, only the last close causes RELEASE */
+TEST_F(ReleaseDir, dup)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ DIR *dir, *dir2;
+
+ expect_lookup(RELPATH, ino);
+ expect_opendir(ino);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READDIR &&
+ in.header.nodeid == ino &&
+ in.body.readdir.offset == 0);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ out.header.error = 0;
+ out.header.len = sizeof(out.header);
+ })));
+ expect_releasedir(ino, ReturnErrno(0));
+
+ dir = opendir(FULLPATH);
+ ASSERT_NE(nullptr, dir) << strerror(errno);
+
+ dir2 = fdopendir(dup(dirfd(dir)));
+ ASSERT_NE(nullptr, dir2) << strerror(errno);
+
+ ASSERT_EQ(0, closedir(dir)) << strerror(errno);
+ ASSERT_EQ(0, closedir(dir2)) << strerror(errno);
+}
+
+TEST_F(ReleaseDir, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ uint64_t ino = 42;
+ DIR *dir;
+
+ expect_lookup(RELPATH, ino);
+ expect_opendir(ino);
+ expect_releasedir(ino, ReturnErrno(0));
+
+ dir = opendir(FULLPATH);
+ ASSERT_NE(nullptr, dir) << strerror(errno);
+
+ ASSERT_EQ(0, closedir(dir)) << strerror(errno);
+}
+
+/* Directories opened O_EXEC should be properly released, too */
+TEST_F(ReleaseDir, o_exec)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ uint64_t ino = 42;
+ int fd;
+
+ expect_lookup(RELPATH, ino);
+ expect_opendir(ino);
+ expect_releasedir(ino, ReturnErrno(0));
+
+ fd = open(FULLPATH, O_EXEC | O_DIRECTORY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(0, close(fd)) << strerror(errno);
+}
diff --git a/tests/sys/fs/fusefs/rename.cc b/tests/sys/fs/fusefs/rename.cc
new file mode 100644
index 000000000000..6b5687e209c6
--- /dev/null
+++ b/tests/sys/fs/fusefs/rename.cc
@@ -0,0 +1,330 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <stdlib.h>
+#include <unistd.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class Rename: public FuseTest {
+ public:
+ int tmpfd = -1;
+ char tmpfile[80] = "/tmp/fuse.rename.XXXXXX";
+
+ virtual void TearDown() {
+ if (tmpfd >= 0) {
+ close(tmpfd);
+ unlink(tmpfile);
+ }
+
+ FuseTest::TearDown();
+ }
+};
+
+// EINVAL, dst is subdir of src
+TEST_F(Rename, einval)
+{
+ const char FULLDST[] = "mountpoint/src/dst";
+ const char RELDST[] = "dst";
+ const char FULLSRC[] = "mountpoint/src";
+ const char RELSRC[] = "src";
+ uint64_t src_ino = 42;
+
+ expect_lookup(RELSRC, src_ino, S_IFDIR | 0755, 0, 2);
+ EXPECT_LOOKUP(src_ino, RELDST).WillOnce(Invoke(ReturnErrno(ENOENT)));
+
+ ASSERT_NE(0, rename(FULLSRC, FULLDST));
+ ASSERT_EQ(EINVAL, errno);
+}
+
+// source does not exist
+TEST_F(Rename, enoent)
+{
+ const char FULLDST[] = "mountpoint/dst";
+ const char FULLSRC[] = "mountpoint/src";
+ const char RELSRC[] = "src";
+ // FUSE hardcodes the mountpoint to inode 1
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELSRC)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+
+ ASSERT_NE(0, rename(FULLSRC, FULLDST));
+ ASSERT_EQ(ENOENT, errno);
+}
+
+/*
+ * Renaming a file after FUSE_LOOKUP returned a negative cache entry for dst
+ */
+TEST_F(Rename, entry_cache_negative)
+{
+ const char FULLDST[] = "mountpoint/dst";
+ const char RELDST[] = "dst";
+ const char FULLSRC[] = "mountpoint/src";
+ const char RELSRC[] = "src";
+ uint64_t dst_dir_ino = FUSE_ROOT_ID;
+ uint64_t ino = 42;
+ /*
+ * Set entry_valid = 0 because this test isn't concerned with whether
+ * or not we actually cache negative entries, only with whether we
+ * interpret negative cache responses correctly.
+ */
+ struct timespec entry_valid = {.tv_sec = 0, .tv_nsec = 0};
+
+ expect_lookup(RELSRC, ino, S_IFREG | 0644, 0, 1);
+ /* LOOKUP returns a negative cache entry for dst */
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDST)
+ .WillOnce(ReturnNegativeCache(&entry_valid));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *src = (const char*)in.body.bytes +
+ sizeof(fuse_rename_in);
+ const char *dst = src + strlen(src) + 1;
+ return (in.header.opcode == FUSE_RENAME &&
+ in.body.rename.newdir == dst_dir_ino &&
+ (0 == strcmp(RELDST, dst)) &&
+ (0 == strcmp(RELSRC, src)));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(0)));
+
+ ASSERT_EQ(0, rename(FULLSRC, FULLDST)) << strerror(errno);
+}
+
+/*
+ * Renaming a file should purge any negative namecache entries for the dst
+ */
+TEST_F(Rename, entry_cache_negative_purge)
+{
+ const char FULLDST[] = "mountpoint/dst";
+ const char RELDST[] = "dst";
+ const char FULLSRC[] = "mountpoint/src";
+ const char RELSRC[] = "src";
+ uint64_t dst_dir_ino = FUSE_ROOT_ID;
+ uint64_t ino = 42;
+ struct timespec entry_valid = {.tv_sec = TIME_T_MAX, .tv_nsec = 0};
+
+ expect_lookup(RELSRC, ino, S_IFREG | 0644, 0, 1);
+ /* LOOKUP returns a negative cache entry for dst */
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDST)
+ .WillOnce(ReturnNegativeCache(&entry_valid))
+ .RetiresOnSaturation();
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *src = (const char*)in.body.bytes +
+ sizeof(fuse_rename_in);
+ const char *dst = src + strlen(src) + 1;
+ return (in.header.opcode == FUSE_RENAME &&
+ in.body.rename.newdir == dst_dir_ino &&
+ (0 == strcmp(RELDST, dst)) &&
+ (0 == strcmp(RELSRC, src)));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(0)));
+
+ ASSERT_EQ(0, rename(FULLSRC, FULLDST)) << strerror(errno);
+
+ /* Finally, a subsequent lookup should query the daemon */
+ expect_lookup(RELDST, ino, S_IFREG | 0644, 0, 1);
+
+ ASSERT_EQ(0, access(FULLDST, F_OK)) << strerror(errno);
+}
+
+TEST_F(Rename, exdev)
+{
+ const char FULLB[] = "mountpoint/src";
+ const char RELB[] = "src";
+ // FUSE hardcodes the mountpoint to inode 1
+ uint64_t b_ino = 42;
+
+ tmpfd = mkstemp(tmpfile);
+ ASSERT_LE(0, tmpfd) << strerror(errno);
+
+ expect_lookup(RELB, b_ino, S_IFREG | 0644, 0, 2);
+
+ ASSERT_NE(0, rename(tmpfile, FULLB));
+ ASSERT_EQ(EXDEV, errno);
+
+ ASSERT_NE(0, rename(FULLB, tmpfile));
+ ASSERT_EQ(EXDEV, errno);
+}
+
+TEST_F(Rename, ok)
+{
+ const char FULLDST[] = "mountpoint/dst";
+ const char RELDST[] = "dst";
+ const char FULLSRC[] = "mountpoint/src";
+ const char RELSRC[] = "src";
+ uint64_t dst_dir_ino = FUSE_ROOT_ID;
+ uint64_t ino = 42;
+
+ expect_lookup(RELSRC, ino, S_IFREG | 0644, 0, 1);
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDST)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *src = (const char*)in.body.bytes +
+ sizeof(fuse_rename_in);
+ const char *dst = src + strlen(src) + 1;
+ return (in.header.opcode == FUSE_RENAME &&
+ in.body.rename.newdir == dst_dir_ino &&
+ (0 == strcmp(RELDST, dst)) &&
+ (0 == strcmp(RELSRC, src)));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(0)));
+
+ ASSERT_EQ(0, rename(FULLSRC, FULLDST)) << strerror(errno);
+}
+
+/* When moving a file to a new directory, update its parent */
+TEST_F(Rename, parent)
+{
+ const char FULLDST[] = "mountpoint/dstdir/dst";
+ const char RELDSTDIR[] = "dstdir";
+ const char RELDST[] = "dst";
+ const char FULLSRC[] = "mountpoint/src";
+ const char RELSRC[] = "src";
+ const char FULLDSTPARENT[] = "mountpoint/dstdir";
+ const char FULLDSTDOTDOT[] = "mountpoint/dstdir/dst/..";
+ Sequence seq;
+ uint64_t dst_dir_ino = 43;
+ uint64_t ino = 42;
+ struct stat sb;
+
+ expect_lookup(RELSRC, ino, S_IFDIR | 0755, 0, 1);
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDSTDIR)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.nodeid = dst_dir_ino;
+ out.body.entry.entry_valid = UINT64_MAX;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.attr.mode = S_IFDIR | 0755;
+ out.body.entry.attr.ino = dst_dir_ino;
+ out.body.entry.attr.nlink = 2;
+ })));
+ EXPECT_LOOKUP(dst_dir_ino, RELDST)
+ .InSequence(seq)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *src = (const char*)in.body.bytes +
+ sizeof(fuse_rename_in);
+ const char *dst = src + strlen(src) + 1;
+ return (in.header.opcode == FUSE_RENAME &&
+ in.body.rename.newdir == dst_dir_ino &&
+ (0 == strcmp(RELDST, dst)) &&
+ (0 == strcmp(RELSRC, src)));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(0)));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == 1);
+ }, Eq(true)),
+ _)
+ ).InSequence(seq)
+ .WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr_valid = UINT64_MAX;
+ out.body.attr.attr.ino = 1;
+ out.body.attr.attr.mode = S_IFDIR | 0755;
+ out.body.attr.attr.nlink = 2;
+ })));
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELDSTDIR)
+ .InSequence(seq)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.nodeid = dst_dir_ino;
+ out.body.entry.entry_valid = UINT64_MAX;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.attr.mode = S_IFDIR | 0755;
+ out.body.entry.attr.ino = dst_dir_ino;
+ out.body.entry.attr.nlink = 3;
+ })));
+ EXPECT_LOOKUP(dst_dir_ino, RELDST)
+ .InSequence(seq)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFDIR | 0755;
+ out.body.entry.nodeid = ino;
+ out.body.entry.entry_valid = UINT64_MAX;
+ out.body.entry.attr_valid = UINT64_MAX;
+ })));
+
+ ASSERT_EQ(0, rename(FULLSRC, FULLDST)) << strerror(errno);
+
+ ASSERT_EQ(0, stat("mountpoint", &sb)) << strerror(errno);
+ EXPECT_EQ(2ul, sb.st_nlink);
+
+ ASSERT_EQ(0, stat(FULLDSTPARENT, &sb)) << strerror(errno);
+ EXPECT_EQ(3ul, sb.st_nlink);
+
+ ASSERT_EQ(0, stat(FULLDSTDOTDOT, &sb)) << strerror(errno);
+ ASSERT_EQ(dst_dir_ino, sb.st_ino);
+}
+
+// Rename overwrites an existing destination file
+TEST_F(Rename, overwrite)
+{
+ const char FULLDST[] = "mountpoint/dst";
+ const char RELDST[] = "dst";
+ const char FULLSRC[] = "mountpoint/src";
+ const char RELSRC[] = "src";
+ // The inode of the already-existing destination file
+ uint64_t dst_ino = 2;
+ uint64_t dst_dir_ino = FUSE_ROOT_ID;
+ uint64_t ino = 42;
+
+ expect_lookup(RELSRC, ino, S_IFREG | 0644, 0, 1);
+ expect_lookup(RELDST, dst_ino, S_IFREG | 0644, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *src = (const char*)in.body.bytes +
+ sizeof(fuse_rename_in);
+ const char *dst = src + strlen(src) + 1;
+ return (in.header.opcode == FUSE_RENAME &&
+ in.body.rename.newdir == dst_dir_ino &&
+ (0 == strcmp(RELDST, dst)) &&
+ (0 == strcmp(RELSRC, src)));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(0)));
+
+ ASSERT_EQ(0, rename(FULLSRC, FULLDST)) << strerror(errno);
+}
diff --git a/tests/sys/fs/fusefs/rmdir.cc b/tests/sys/fs/fusefs/rmdir.cc
new file mode 100644
index 000000000000..146b916f3d9f
--- /dev/null
+++ b/tests/sys/fs/fusefs/rmdir.cc
@@ -0,0 +1,162 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <fcntl.h>
+#include <semaphore.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class Rmdir: public FuseTest {
+public:
+void expect_lookup(const char *relpath, uint64_t ino, int times=1)
+{
+ EXPECT_LOOKUP(FUSE_ROOT_ID, relpath)
+ .Times(times)
+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.attr.mode = S_IFDIR | 0755;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.nlink = 2;
+ })));
+}
+
+void expect_rmdir(uint64_t parent, const char *relpath, int error)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_RMDIR &&
+ 0 == strcmp(relpath, in.body.rmdir) &&
+ in.header.nodeid == parent);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(error)));
+}
+};
+
+/*
+ * A successful rmdir should clear the parent directory's attribute cache,
+ * because the fuse daemon should update its mtime and ctime
+ */
+TEST_F(Rmdir, parent_attr_cache)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ struct stat sb;
+ sem_t sem;
+ uint64_t ino = 42;
+ Sequence seq;
+
+ ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
+
+ expect_lookup(RELPATH, ino);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_RMDIR &&
+ 0 == strcmp(RELPATH, in.body.rmdir) &&
+ in.header.nodeid == FUSE_ROOT_ID);
+ }, Eq(true)),
+ _)
+ ).InSequence(seq)
+ .WillOnce(Invoke(ReturnErrno(0)));
+ expect_forget(ino, 1, &sem);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == FUSE_ROOT_ID);
+ }, Eq(true)),
+ _)
+ ).InSequence(seq)
+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = FUSE_ROOT_ID;
+ out.body.attr.attr.mode = S_IFDIR | 0755;
+ out.body.attr.attr_valid = UINT64_MAX;
+ })));
+
+ ASSERT_EQ(0, rmdir(FULLPATH)) << strerror(errno);
+ EXPECT_EQ(0, stat("mountpoint", &sb)) << strerror(errno);
+ sem_wait(&sem);
+ sem_destroy(&sem);
+}
+
+TEST_F(Rmdir, enotempty)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ uint64_t ino = 42;
+
+ expect_lookup(RELPATH, ino);
+ expect_rmdir(FUSE_ROOT_ID, RELPATH, ENOTEMPTY);
+
+ ASSERT_NE(0, rmdir(FULLPATH));
+ ASSERT_EQ(ENOTEMPTY, errno);
+}
+
+/* Removing a directory should expire its entry cache */
+TEST_F(Rmdir, entry_cache)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ sem_t sem;
+ uint64_t ino = 42;
+
+ expect_lookup(RELPATH, ino, 2);
+ expect_rmdir(FUSE_ROOT_ID, RELPATH, 0);
+ expect_forget(ino, 1, &sem);
+
+ ASSERT_EQ(0, rmdir(FULLPATH)) << strerror(errno);
+ ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
+ sem_wait(&sem);
+ sem_destroy(&sem);
+}
+
+TEST_F(Rmdir, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_dir";
+ const char RELPATH[] = "some_dir";
+ sem_t sem;
+ uint64_t ino = 42;
+
+ ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
+
+ expect_lookup(RELPATH, ino);
+ expect_rmdir(FUSE_ROOT_ID, RELPATH, 0);
+ expect_forget(ino, 1, &sem);
+
+ ASSERT_EQ(0, rmdir(FULLPATH)) << strerror(errno);
+ sem_wait(&sem);
+ sem_destroy(&sem);
+}
diff --git a/tests/sys/fs/fusefs/setattr.cc b/tests/sys/fs/fusefs/setattr.cc
new file mode 100644
index 000000000000..79559db33b12
--- /dev/null
+++ b/tests/sys/fs/fusefs/setattr.cc
@@ -0,0 +1,862 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <fcntl.h>
+#include <semaphore.h>
+#include <signal.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class Setattr : public FuseTest {
+public:
+static sig_atomic_t s_sigxfsz;
+};
+
+class RofsSetattr: public Setattr {
+public:
+virtual void SetUp() {
+ s_sigxfsz = 0;
+ m_ro = true;
+ Setattr::SetUp();
+}
+};
+
+class Setattr_7_8: public Setattr {
+public:
+virtual void SetUp() {
+ m_kernel_minor_version = 8;
+ Setattr::SetUp();
+}
+};
+
+
+sig_atomic_t Setattr::s_sigxfsz = 0;
+
+void sigxfsz_handler(int __unused sig) {
+ Setattr::s_sigxfsz = 1;
+}
+
+/*
+ * If setattr returns a non-zero cache timeout, then subsequent VOP_GETATTRs
+ * should use the cached attributes, rather than query the daemon
+ */
+TEST_F(Setattr, attr_cache)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ struct stat sb;
+ const mode_t newmode = 0644;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = ino;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino; // Must match nodeid
+ out.body.attr.attr.mode = S_IFREG | newmode;
+ out.body.attr.attr_valid = UINT64_MAX;
+ })));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_GETATTR);
+ }, Eq(true)),
+ _)
+ ).Times(0);
+
+ /* Set an attribute with SETATTR */
+ ASSERT_EQ(0, chmod(FULLPATH, newmode)) << strerror(errno);
+
+ /* The stat(2) should use cached attributes */
+ ASSERT_EQ(0, stat(FULLPATH, &sb));
+ EXPECT_EQ(S_IFREG | newmode, sb.st_mode);
+}
+
+/* Change the mode of a file */
+TEST_F(Setattr, chmod)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ const mode_t oldmode = 0755;
+ const mode_t newmode = 0644;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | oldmode;
+ out.body.entry.nodeid = ino;
+ })));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ uint32_t valid = FATTR_MODE;
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino &&
+ in.body.setattr.valid == valid &&
+ in.body.setattr.mode == newmode);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino; // Must match nodeid
+ out.body.attr.attr.mode = S_IFREG | newmode;
+ })));
+ EXPECT_EQ(0, chmod(FULLPATH, newmode)) << strerror(errno);
+}
+
+/*
+ * Chmod a multiply-linked file with cached attributes. Check that both files'
+ * attributes have changed.
+ */
+TEST_F(Setattr, chmod_multiply_linked)
+{
+ const char FULLPATH0[] = "mountpoint/some_file.txt";
+ const char RELPATH0[] = "some_file.txt";
+ const char FULLPATH1[] = "mountpoint/other_file.txt";
+ const char RELPATH1[] = "other_file.txt";
+ struct stat sb;
+ const uint64_t ino = 42;
+ const mode_t oldmode = 0777;
+ const mode_t newmode = 0666;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH0)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | oldmode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.nlink = 2;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH1)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | oldmode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.nlink = 2;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ uint32_t valid = FATTR_MODE;
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino &&
+ in.body.setattr.valid == valid &&
+ in.body.setattr.mode == newmode);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino;
+ out.body.attr.attr.mode = S_IFREG | newmode;
+ out.body.attr.attr.nlink = 2;
+ out.body.attr.attr_valid = UINT64_MAX;
+ })));
+
+ /* For a lookup of the 2nd file to get it into the cache*/
+ ASSERT_EQ(0, stat(FULLPATH1, &sb)) << strerror(errno);
+ EXPECT_EQ(S_IFREG | oldmode, sb.st_mode);
+
+ ASSERT_EQ(0, chmod(FULLPATH0, newmode)) << strerror(errno);
+ ASSERT_EQ(0, stat(FULLPATH0, &sb)) << strerror(errno);
+ EXPECT_EQ(S_IFREG | newmode, sb.st_mode);
+ ASSERT_EQ(0, stat(FULLPATH1, &sb)) << strerror(errno);
+ EXPECT_EQ(S_IFREG | newmode, sb.st_mode);
+}
+
+
+/* Change the owner and group of a file */
+TEST_F(Setattr, chown)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ const gid_t oldgroup = 66;
+ const gid_t newgroup = 99;
+ const uid_t olduser = 33;
+ const uid_t newuser = 44;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.gid = oldgroup;
+ out.body.entry.attr.uid = olduser;
+ })));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ uint32_t valid = FATTR_GID | FATTR_UID;
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino &&
+ in.body.setattr.valid == valid &&
+ in.body.setattr.uid == newuser &&
+ in.body.setattr.gid == newgroup);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino; // Must match nodeid
+ out.body.attr.attr.mode = S_IFREG | 0644;
+ out.body.attr.attr.uid = newuser;
+ out.body.attr.attr.gid = newgroup;
+ })));
+ EXPECT_EQ(0, chown(FULLPATH, newuser, newgroup)) << strerror(errno);
+}
+
+
+
+/*
+ * FUSE daemons are allowed to check permissions however they like. If the
+ * daemon returns EPERM, even if the file permissions "should" grant access,
+ * then fuse(4) should return EPERM too.
+ */
+TEST_F(Setattr, eperm)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | 0777;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.uid = in.header.uid;
+ out.body.entry.attr.gid = in.header.gid;
+ })));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(EPERM)));
+ EXPECT_NE(0, truncate(FULLPATH, 10));
+ EXPECT_EQ(EPERM, errno);
+}
+
+/* Change the mode of an open file, by its file descriptor */
+TEST_F(Setattr, fchmod)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int fd;
+ const mode_t oldmode = 0755;
+ const mode_t newmode = 0644;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | oldmode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr_valid = UINT64_MAX;
+ })));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_OPEN &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ out.header.len = sizeof(out.header);
+ SET_OUT_HEADER_LEN(out, open);
+ })));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ uint32_t valid = FATTR_MODE;
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino &&
+ in.body.setattr.valid == valid &&
+ in.body.setattr.mode == newmode);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino; // Must match nodeid
+ out.body.attr.attr.mode = S_IFREG | newmode;
+ })));
+
+ fd = open(FULLPATH, O_RDONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno);
+ leak(fd);
+}
+
+/* Change the size of an open file, by its file descriptor */
+TEST_F(Setattr, ftruncate)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int fd;
+ uint64_t fh = 0xdeadbeef1a7ebabe;
+ const off_t oldsize = 99;
+ const off_t newsize = 12345;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | 0755;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.attr.size = oldsize;
+ })));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_OPEN &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ out.header.len = sizeof(out.header);
+ SET_OUT_HEADER_LEN(out, open);
+ out.body.open.fh = fh;
+ })));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ uint32_t valid = FATTR_SIZE | FATTR_FH;
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino &&
+ in.body.setattr.valid == valid &&
+ in.body.setattr.fh == fh);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino; // Must match nodeid
+ out.body.attr.attr.mode = S_IFREG | 0755;
+ out.body.attr.attr.size = newsize;
+ })));
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(0, ftruncate(fd, newsize)) << strerror(errno);
+ leak(fd);
+}
+
+/* Change the size of the file */
+TEST_F(Setattr, truncate) {
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ const uint64_t oldsize = 100'000'000;
+ const uint64_t newsize = 20'000'000;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.size = oldsize;
+ })));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ uint32_t valid = FATTR_SIZE;
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino &&
+ in.body.setattr.valid == valid &&
+ in.body.setattr.size == newsize);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino; // Must match nodeid
+ out.body.attr.attr.mode = S_IFREG | 0644;
+ out.body.attr.attr.size = newsize;
+ })));
+ EXPECT_EQ(0, truncate(FULLPATH, newsize)) << strerror(errno);
+}
+
+/*
+ * Truncating a file should discard cached data past the truncation point.
+ * This is a regression test for bug 233783.
+ *
+ * There are two distinct failure modes. The first one is a failure to zero
+ * the portion of the file's final buffer past EOF. It can be reproduced by
+ * fsx -WR -P /tmp -S10 fsx.bin
+ *
+ * The second is a failure to drop buffers beyond that. It can be reproduced by
+ * fsx -WR -P /tmp -S18 -n fsx.bin
+ * Also reproducible in sh with:
+ * $> /path/to/libfuse/build/example/passthrough -d /tmp/mnt
+ * $> cd /tmp/mnt/tmp
+ * $> dd if=/dev/random of=randfile bs=1k count=192
+ * $> truncate -s 1k randfile && truncate -s 192k randfile
+ * $> xxd randfile | less # xxd will wrongly show random data at offset 0x8000
+ */
+TEST_F(Setattr, truncate_discards_cached_data) {
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ char *w0buf, *r0buf, *r1buf, *expected;
+ off_t w0_offset = 0;
+ size_t w0_size = 0x30000;
+ off_t r0_offset = 0;
+ off_t r0_size = w0_size;
+ size_t trunc0_size = 0x400;
+ size_t trunc1_size = w0_size;
+ off_t r1_offset = trunc0_size;
+ off_t r1_size = w0_size - trunc0_size;
+ size_t cur_size = 0;
+ const uint64_t ino = 42;
+ mode_t mode = S_IFREG | 0644;
+ int fd, r;
+ bool should_have_data = false;
+
+ w0buf = new char[w0_size];
+ memset(w0buf, 'X', w0_size);
+
+ r0buf = new char[r0_size];
+ r1buf = new char[r1_size];
+
+ expected = new char[r1_size]();
+
+ expect_lookup(RELPATH, ino, mode, 0, 1);
+ expect_open(ino, O_RDWR, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnImmediate([&](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino;
+ out.body.attr.attr.mode = mode;
+ out.body.attr.attr.size = cur_size;
+ })));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_WRITE);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnImmediate([&](auto in, auto& out) {
+ SET_OUT_HEADER_LEN(out, write);
+ out.body.attr.attr.ino = ino;
+ out.body.write.size = in.body.write.size;
+ cur_size = std::max(static_cast<uint64_t>(cur_size),
+ in.body.write.size + in.body.write.offset);
+ })));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino &&
+ (in.body.setattr.valid & FATTR_SIZE));
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnImmediate([&](auto in, auto& out) {
+ auto trunc_size = in.body.setattr.size;
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino;
+ out.body.attr.attr.mode = mode;
+ out.body.attr.attr.size = trunc_size;
+ cur_size = trunc_size;
+ })));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READ);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnImmediate([&](auto in, auto& out) {
+ auto osize = std::min(
+ static_cast<uint64_t>(cur_size) - in.body.read.offset,
+ static_cast<uint64_t>(in.body.read.size));
+ assert(osize <= sizeof(out.body.bytes));
+ out.header.len = sizeof(struct fuse_out_header) + osize;
+ if (should_have_data)
+ memset(out.body.bytes, 'X', osize);
+ else
+ bzero(out.body.bytes, osize);
+ })));
+
+ fd = open(FULLPATH, O_RDWR, 0644);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ /* Fill the file with Xs */
+ ASSERT_EQ(static_cast<ssize_t>(w0_size),
+ pwrite(fd, w0buf, w0_size, w0_offset));
+ should_have_data = true;
+ /* Fill the cache */
+ ASSERT_EQ(static_cast<ssize_t>(r0_size),
+ pread(fd, r0buf, r0_size, r0_offset));
+ /* 1st truncate should discard cached data */
+ EXPECT_EQ(0, ftruncate(fd, trunc0_size)) << strerror(errno);
+ should_have_data = false;
+ /* 2nd truncate extends file into previously cached data */
+ EXPECT_EQ(0, ftruncate(fd, trunc1_size)) << strerror(errno);
+ /* Read should return all zeros */
+ ASSERT_EQ(static_cast<ssize_t>(r1_size),
+ pread(fd, r1buf, r1_size, r1_offset));
+
+ r = memcmp(expected, r1buf, r1_size);
+ ASSERT_EQ(0, r);
+
+ delete[] expected;
+ delete[] r1buf;
+ delete[] r0buf;
+ delete[] w0buf;
+
+ leak(fd);
+}
+
+/* truncate should fail if it would cause the file to exceed RLIMIT_FSIZE */
+TEST_F(Setattr, truncate_rlimit_rsize)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ struct rlimit rl;
+ const uint64_t ino = 42;
+ const uint64_t oldsize = 0;
+ const uint64_t newsize = 100'000'000;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.size = oldsize;
+ })));
+
+ rl.rlim_cur = newsize / 2;
+ rl.rlim_max = 10 * newsize;
+ ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno);
+ ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno);
+
+ EXPECT_EQ(-1, truncate(FULLPATH, newsize));
+ EXPECT_EQ(EFBIG, errno);
+ EXPECT_EQ(1, s_sigxfsz);
+}
+
+/* Change a file's timestamps */
+TEST_F(Setattr, utimensat) {
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ const timespec oldtimes[2] = {
+ {.tv_sec = 1, .tv_nsec = 2},
+ {.tv_sec = 3, .tv_nsec = 4},
+ };
+ const timespec newtimes[2] = {
+ {.tv_sec = 5, .tv_nsec = 6},
+ {.tv_sec = 7, .tv_nsec = 8},
+ };
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.attr.atime = oldtimes[0].tv_sec;
+ out.body.entry.attr.atimensec = oldtimes[0].tv_nsec;
+ out.body.entry.attr.mtime = oldtimes[1].tv_sec;
+ out.body.entry.attr.mtimensec = oldtimes[1].tv_nsec;
+ })));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ uint32_t valid = FATTR_ATIME | FATTR_MTIME;
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino &&
+ in.body.setattr.valid == valid &&
+ (time_t)in.body.setattr.atime ==
+ newtimes[0].tv_sec &&
+ (long)in.body.setattr.atimensec ==
+ newtimes[0].tv_nsec &&
+ (time_t)in.body.setattr.mtime ==
+ newtimes[1].tv_sec &&
+ (long)in.body.setattr.mtimensec ==
+ newtimes[1].tv_nsec);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino; // Must match nodeid
+ out.body.attr.attr.mode = S_IFREG | 0644;
+ out.body.attr.attr.atime = newtimes[0].tv_sec;
+ out.body.attr.attr.atimensec = newtimes[0].tv_nsec;
+ out.body.attr.attr.mtime = newtimes[1].tv_sec;
+ out.body.attr.attr.mtimensec = newtimes[1].tv_nsec;
+ })));
+ EXPECT_EQ(0, utimensat(AT_FDCWD, FULLPATH, &newtimes[0], 0))
+ << strerror(errno);
+}
+
+/* Change a file mtime but not its atime */
+TEST_F(Setattr, utimensat_mtime_only) {
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ const timespec oldtimes[2] = {
+ {.tv_sec = 1, .tv_nsec = 2},
+ {.tv_sec = 3, .tv_nsec = 4},
+ };
+ const timespec newtimes[2] = {
+ {.tv_sec = 5, .tv_nsec = UTIME_OMIT},
+ {.tv_sec = 7, .tv_nsec = 8},
+ };
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.attr.atime = oldtimes[0].tv_sec;
+ out.body.entry.attr.atimensec = oldtimes[0].tv_nsec;
+ out.body.entry.attr.mtime = oldtimes[1].tv_sec;
+ out.body.entry.attr.mtimensec = oldtimes[1].tv_nsec;
+ })));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ uint32_t valid = FATTR_MTIME;
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino &&
+ in.body.setattr.valid == valid &&
+ (time_t)in.body.setattr.mtime ==
+ newtimes[1].tv_sec &&
+ (long)in.body.setattr.mtimensec ==
+ newtimes[1].tv_nsec);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino; // Must match nodeid
+ out.body.attr.attr.mode = S_IFREG | 0644;
+ out.body.attr.attr.atime = oldtimes[0].tv_sec;
+ out.body.attr.attr.atimensec = oldtimes[0].tv_nsec;
+ out.body.attr.attr.mtime = newtimes[1].tv_sec;
+ out.body.attr.attr.mtimensec = newtimes[1].tv_nsec;
+ })));
+ EXPECT_EQ(0, utimensat(AT_FDCWD, FULLPATH, &newtimes[0], 0))
+ << strerror(errno);
+}
+
+/*
+ * Set a file's mtime and atime to now
+ *
+ * The design of FreeBSD's VFS does not allow fusefs to set just one of atime
+ * or mtime to UTIME_NOW; it's both or neither.
+ */
+TEST_F(Setattr, utimensat_utime_now) {
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ const timespec oldtimes[2] = {
+ {.tv_sec = 1, .tv_nsec = 2},
+ {.tv_sec = 3, .tv_nsec = 4},
+ };
+ const timespec newtimes[2] = {
+ {.tv_sec = 0, .tv_nsec = UTIME_NOW},
+ {.tv_sec = 0, .tv_nsec = UTIME_NOW},
+ };
+ /* "now" is whatever the server says it is */
+ const timespec now[2] = {
+ {.tv_sec = 5, .tv_nsec = 7},
+ {.tv_sec = 6, .tv_nsec = 8},
+ };
+ struct stat sb;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ out.body.entry.attr.atime = oldtimes[0].tv_sec;
+ out.body.entry.attr.atimensec = oldtimes[0].tv_nsec;
+ out.body.entry.attr.mtime = oldtimes[1].tv_sec;
+ out.body.entry.attr.mtimensec = oldtimes[1].tv_nsec;
+ })));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ uint32_t valid = FATTR_ATIME | FATTR_ATIME_NOW |
+ FATTR_MTIME | FATTR_MTIME_NOW;
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino &&
+ in.body.setattr.valid == valid);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino; // Must match nodeid
+ out.body.attr.attr.mode = S_IFREG | 0644;
+ out.body.attr.attr.atime = now[0].tv_sec;
+ out.body.attr.attr.atimensec = now[0].tv_nsec;
+ out.body.attr.attr.mtime = now[1].tv_sec;
+ out.body.attr.attr.mtimensec = now[1].tv_nsec;
+ out.body.attr.attr_valid = UINT64_MAX;
+ })));
+ ASSERT_EQ(0, utimensat(AT_FDCWD, FULLPATH, &newtimes[0], 0))
+ << strerror(errno);
+ ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno);
+ EXPECT_EQ(now[0].tv_sec, sb.st_atim.tv_sec);
+ EXPECT_EQ(now[0].tv_nsec, sb.st_atim.tv_nsec);
+ EXPECT_EQ(now[1].tv_sec, sb.st_mtim.tv_sec);
+ EXPECT_EQ(now[1].tv_nsec, sb.st_mtim.tv_nsec);
+}
+
+/*
+ * FUSE_SETATTR returns a different file type, even though the entry cache
+ * hasn't expired. This is a server bug! It probably means that the server
+ * removed the file and recreated it with the same inode but a different vtyp.
+ * The best thing fusefs can do is return ENOENT to the caller. After all, the
+ * entry must not have existed recently.
+ */
+TEST_F(Setattr, vtyp_conflict)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ uid_t newuser = 12345;
+ sem_t sem;
+
+ ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | 0777;
+ out.body.entry.nodeid = ino;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino;
+ out.body.attr.attr.mode = S_IFDIR | 0777; // Changed!
+ out.body.attr.attr.uid = newuser;
+ })));
+ // We should reclaim stale vnodes
+ expect_forget(ino, 1, &sem);
+
+ EXPECT_NE(0, chown(FULLPATH, newuser, -1));
+ EXPECT_EQ(ENOENT, errno);
+
+ sem_wait(&sem);
+ sem_destroy(&sem);
+}
+
+/* On a read-only mount, no attributes may be changed */
+TEST_F(RofsSetattr, erofs)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ const mode_t oldmode = 0755;
+ const mode_t newmode = 0644;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | oldmode;
+ out.body.entry.nodeid = ino;
+ })));
+
+ ASSERT_EQ(-1, chmod(FULLPATH, newmode));
+ ASSERT_EQ(EROFS, errno);
+}
+
+/* Change the mode of a file */
+TEST_F(Setattr_7_8, chmod)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ const mode_t oldmode = 0755;
+ const mode_t newmode = 0644;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry_7_8);
+ out.body.entry.attr.mode = S_IFREG | oldmode;
+ out.body.entry.nodeid = ino;
+ })));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ uint32_t valid = FATTR_MODE;
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino &&
+ in.body.setattr.valid == valid &&
+ in.body.setattr.mode == newmode);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr_7_8);
+ out.body.attr.attr.ino = ino; // Must match nodeid
+ out.body.attr.attr.mode = S_IFREG | newmode;
+ })));
+ EXPECT_EQ(0, chmod(FULLPATH, newmode)) << strerror(errno);
+}
diff --git a/tests/sys/fs/fusefs/statfs.cc b/tests/sys/fs/fusefs/statfs.cc
new file mode 100644
index 000000000000..1dd96e8073d7
--- /dev/null
+++ b/tests/sys/fs/fusefs/statfs.cc
@@ -0,0 +1,171 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <semaphore.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class Statfs: public FuseTest {};
+
+TEST_F(Statfs, eio)
+{
+ struct statfs statbuf;
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_STATFS);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(EIO)));
+
+ ASSERT_NE(0, statfs("mountpoint", &statbuf));
+ ASSERT_EQ(EIO, errno);
+}
+
+/*
+ * When the daemon is dead but the filesystem is still mounted, fuse(4) fakes
+ * the statfs(2) response, which is necessary for unmounting.
+ */
+TEST_F(Statfs, enotconn)
+{
+ struct statfs statbuf;
+ char mp[PATH_MAX];
+
+ m_mock->kill_daemon();
+
+ ASSERT_NE(nullptr, getcwd(mp, PATH_MAX)) << strerror(errno);
+ strlcat(mp, "/mountpoint", PATH_MAX);
+ ASSERT_EQ(0, statfs("mountpoint", &statbuf)) << strerror(errno);
+
+ EXPECT_EQ(getuid(), statbuf.f_owner);
+ EXPECT_EQ(0, strcmp("fusefs", statbuf.f_fstypename));
+ EXPECT_EQ(0, strcmp("/dev/fuse", statbuf.f_mntfromname));
+ EXPECT_EQ(0, strcmp(mp, statbuf.f_mntonname));
+}
+
+static void* statfs_th(void* arg) {
+ ssize_t r;
+ struct statfs *sb = (struct statfs*)arg;
+
+ r = statfs("mountpoint", sb);
+ if (r >= 0)
+ return 0;
+ else
+ return (void*)(intptr_t)errno;
+}
+
+/*
+ * Like the enotconn test, but in this case the daemon dies after we send the
+ * FUSE_STATFS operation but before we get a response.
+ */
+TEST_F(Statfs, enotconn_while_blocked)
+{
+ struct statfs statbuf;
+ void *thr0_value;
+ pthread_t th0;
+ char mp[PATH_MAX];
+ sem_t sem;
+
+ ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_STATFS);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke([&](auto in __unused, auto &out __unused) {
+ sem_post(&sem);
+ /* Just block until the daemon dies */
+ }));
+
+ ASSERT_NE(nullptr, getcwd(mp, PATH_MAX)) << strerror(errno);
+ strlcat(mp, "/mountpoint", PATH_MAX);
+ ASSERT_EQ(0, pthread_create(&th0, NULL, statfs_th, (void*)&statbuf))
+ << strerror(errno);
+
+ ASSERT_EQ(0, sem_wait(&sem)) << strerror(errno);
+ m_mock->kill_daemon();
+
+ pthread_join(th0, &thr0_value);
+ ASSERT_EQ(0, (intptr_t)thr0_value);
+
+ EXPECT_EQ(getuid(), statbuf.f_owner);
+ EXPECT_EQ(0, strcmp("fusefs", statbuf.f_fstypename));
+ EXPECT_EQ(0, strcmp("/dev/fuse", statbuf.f_mntfromname));
+ EXPECT_EQ(0, strcmp(mp, statbuf.f_mntonname));
+}
+
+TEST_F(Statfs, ok)
+{
+ struct statfs statbuf;
+ char mp[PATH_MAX];
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_STATFS);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, statfs);
+ out.body.statfs.st.blocks = 1000;
+ out.body.statfs.st.bfree = 100;
+ out.body.statfs.st.bavail = 200;
+ out.body.statfs.st.files = 5;
+ out.body.statfs.st.ffree = 6;
+ out.body.statfs.st.namelen = 128;
+ out.body.statfs.st.frsize = 1024;
+ })));
+
+ ASSERT_NE(nullptr, getcwd(mp, PATH_MAX)) << strerror(errno);
+ strlcat(mp, "/mountpoint", PATH_MAX);
+ ASSERT_EQ(0, statfs("mountpoint", &statbuf)) << strerror(errno);
+ EXPECT_EQ(1024ul, statbuf.f_bsize);
+ /*
+ * fuse(4) ignores the filesystem's reported optimal transfer size, and
+ * chooses a size that works well with the rest of the system instead
+ */
+ EXPECT_EQ(1000ul, statbuf.f_blocks);
+ EXPECT_EQ(100ul, statbuf.f_bfree);
+ EXPECT_EQ(200l, statbuf.f_bavail);
+ EXPECT_EQ(5ul, statbuf.f_files);
+ EXPECT_EQ(6l, statbuf.f_ffree);
+ EXPECT_EQ(128u, statbuf.f_namemax);
+ EXPECT_EQ(getuid(), statbuf.f_owner);
+ EXPECT_EQ(0, strcmp("fusefs", statbuf.f_fstypename));
+ EXPECT_EQ(0, strcmp("/dev/fuse", statbuf.f_mntfromname));
+ EXPECT_EQ(0, strcmp(mp, statbuf.f_mntonname));
+}
diff --git a/tests/sys/fs/fusefs/symlink.cc b/tests/sys/fs/fusefs/symlink.cc
new file mode 100644
index 000000000000..bd355497a8bd
--- /dev/null
+++ b/tests/sys/fs/fusefs/symlink.cc
@@ -0,0 +1,208 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <semaphore.h>
+#include <unistd.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class Symlink: public FuseTest {
+public:
+
+void expect_symlink(uint64_t ino, const char *target, const char *relpath)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *name = (const char*)in.body.bytes;
+ const char *linkname = name + strlen(name) + 1;
+ return (in.header.opcode == FUSE_SYMLINK &&
+ (0 == strcmp(linkname, target)) &&
+ (0 == strcmp(name, relpath)));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFLNK | 0777;
+ out.body.entry.nodeid = ino;
+ out.body.entry.entry_valid = UINT64_MAX;
+ out.body.entry.attr_valid = UINT64_MAX;
+ })));
+}
+
+};
+
+class Symlink_7_8: public FuseTest {
+public:
+virtual void SetUp() {
+ m_kernel_minor_version = 8;
+ FuseTest::SetUp();
+}
+
+void expect_symlink(uint64_t ino, const char *target, const char *relpath)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *name = (const char*)in.body.bytes;
+ const char *linkname = name + strlen(name) + 1;
+ return (in.header.opcode == FUSE_SYMLINK &&
+ (0 == strcmp(linkname, target)) &&
+ (0 == strcmp(name, relpath)));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry_7_8);
+ out.body.entry.attr.mode = S_IFLNK | 0777;
+ out.body.entry.nodeid = ino;
+ out.body.entry.entry_valid = UINT64_MAX;
+ out.body.entry.attr_valid = UINT64_MAX;
+ })));
+}
+
+};
+
+/*
+ * A successful symlink should clear the parent directory's attribute cache,
+ * because the fuse daemon should update its mtime and ctime
+ */
+TEST_F(Symlink, clear_attr_cache)
+{
+ const char FULLPATH[] = "mountpoint/src";
+ const char RELPATH[] = "src";
+ const char dst[] = "dst";
+ const uint64_t ino = 42;
+ struct stat sb;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == FUSE_ROOT_ID);
+ }, Eq(true)),
+ _)
+ ).Times(2)
+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = FUSE_ROOT_ID;
+ out.body.attr.attr.mode = S_IFDIR | 0755;
+ out.body.attr.attr_valid = UINT64_MAX;
+ })));
+ expect_symlink(ino, dst, RELPATH);
+
+ EXPECT_EQ(0, stat("mountpoint", &sb)) << strerror(errno);
+ EXPECT_EQ(0, symlink(dst, FULLPATH)) << strerror(errno);
+ EXPECT_EQ(0, stat("mountpoint", &sb)) << strerror(errno);
+}
+
+TEST_F(Symlink, enospc)
+{
+ const char FULLPATH[] = "mountpoint/lnk";
+ const char RELPATH[] = "lnk";
+ const char dst[] = "dst";
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *name = (const char*)in.body.bytes;
+ const char *linkname = name + strlen(name) + 1;
+ return (in.header.opcode == FUSE_SYMLINK &&
+ (0 == strcmp(linkname, dst)) &&
+ (0 == strcmp(name, RELPATH)));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(ENOSPC)));
+
+ EXPECT_EQ(-1, symlink(dst, FULLPATH));
+ EXPECT_EQ(ENOSPC, errno);
+}
+
+TEST_F(Symlink, ok)
+{
+ const char FULLPATH[] = "mountpoint/src";
+ const char RELPATH[] = "src";
+ const char dst[] = "dst";
+ const uint64_t ino = 42;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_symlink(ino, dst, RELPATH);
+
+ EXPECT_EQ(0, symlink(dst, FULLPATH)) << strerror(errno);
+}
+
+/*
+ * Nothing bad should happen if the server returns the parent's inode number
+ * for the newly created symlink. Regression test for bug 263662.
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=263662
+ */
+TEST_F(Symlink, parent_ino)
+{
+ const char FULLPATH[] = "mountpoint/parent/src";
+ const char PPATH[] = "parent";
+ const char RELPATH[] = "src";
+ const char dst[] = "dst";
+ sem_t sem;
+ const uint64_t ino = 42;
+
+ ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
+
+ expect_lookup(PPATH, ino, S_IFDIR | 0755, 0, 1);
+ EXPECT_LOOKUP(ino, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_symlink(ino, dst, RELPATH);
+ expect_forget(ino, 1, &sem);
+
+ EXPECT_EQ(-1, symlink(dst, FULLPATH));
+ EXPECT_EQ(EIO, errno);
+
+ sem_wait(&sem);
+ sem_destroy(&sem);
+}
+
+TEST_F(Symlink_7_8, ok)
+{
+ const char FULLPATH[] = "mountpoint/src";
+ const char RELPATH[] = "src";
+ const char dst[] = "dst";
+ const uint64_t ino = 42;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_symlink(ino, dst, RELPATH);
+
+ EXPECT_EQ(0, symlink(dst, FULLPATH)) << strerror(errno);
+}
diff --git a/tests/sys/fs/fusefs/unlink.cc b/tests/sys/fs/fusefs/unlink.cc
new file mode 100644
index 000000000000..1d8a371649ee
--- /dev/null
+++ b/tests/sys/fs/fusefs/unlink.cc
@@ -0,0 +1,237 @@
+/*-
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <fcntl.h>
+#include <semaphore.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class Unlink: public FuseTest {
+public:
+void expect_lookup(const char *relpath, uint64_t ino, int times, int nlink=1)
+{
+ EXPECT_LOOKUP(FUSE_ROOT_ID, relpath)
+ .Times(times)
+ .WillRepeatedly(Invoke(
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.nlink = nlink;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.attr.size = 0;
+ })));
+}
+
+};
+
+/*
+ * Unlinking a multiply linked file should update its ctime and nlink. This
+ * could be handled simply by invalidating the attributes, necessitating a new
+ * GETATTR, but we implement it in-kernel for efficiency's sake.
+ */
+TEST_F(Unlink, attr_cache)
+{
+ const char FULLPATH0[] = "mountpoint/some_file.txt";
+ const char RELPATH0[] = "some_file.txt";
+ const char FULLPATH1[] = "mountpoint/other_file.txt";
+ const char RELPATH1[] = "other_file.txt";
+ uint64_t ino = 42;
+ struct stat sb_old, sb_new;
+ int fd1;
+
+ expect_lookup(RELPATH0, ino, 1, 2);
+ expect_lookup(RELPATH1, ino, 1, 2);
+ expect_open(ino, 0, 1);
+ expect_unlink(1, RELPATH0, 0);
+
+ fd1 = open(FULLPATH1, O_RDONLY);
+ ASSERT_LE(0, fd1) << strerror(errno);
+
+ ASSERT_EQ(0, fstat(fd1, &sb_old)) << strerror(errno);
+ ASSERT_EQ(0, unlink(FULLPATH0)) << strerror(errno);
+ ASSERT_EQ(0, fstat(fd1, &sb_new)) << strerror(errno);
+ EXPECT_NE(sb_old.st_ctime, sb_new.st_ctime);
+ EXPECT_EQ(1u, sb_new.st_nlink);
+
+ leak(fd1);
+}
+
+/*
+ * A successful unlink should clear the parent directory's attribute cache,
+ * because the fuse daemon should update its mtime and ctime
+ */
+TEST_F(Unlink, parent_attr_cache)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ struct stat sb;
+ uint64_t ino = 42;
+ Sequence seq;
+
+ /* Use nlink=2 so we don't get a FUSE_FORGET */
+ expect_lookup(RELPATH, ino, 1, 2);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_UNLINK &&
+ 0 == strcmp(RELPATH, in.body.unlink) &&
+ in.header.nodeid == FUSE_ROOT_ID);
+ }, Eq(true)),
+ _)
+ ).InSequence(seq)
+ .WillOnce(Invoke(ReturnErrno(0)));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == FUSE_ROOT_ID);
+ }, Eq(true)),
+ _)
+ ).InSequence(seq)
+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = FUSE_ROOT_ID;
+ out.body.attr.attr.mode = S_IFDIR | 0755;
+ out.body.attr.attr_valid = UINT64_MAX;
+ })));
+
+ ASSERT_EQ(0, unlink(FULLPATH)) << strerror(errno);
+ EXPECT_EQ(0, stat("mountpoint", &sb)) << strerror(errno);
+}
+
+TEST_F(Unlink, eperm)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+
+ expect_lookup(RELPATH, ino, 1);
+ expect_unlink(1, RELPATH, EPERM);
+
+ ASSERT_NE(0, unlink(FULLPATH));
+ ASSERT_EQ(EPERM, errno);
+}
+
+/*
+ * Unlinking a file should expire its entry cache, even if it's multiply linked
+ */
+TEST_F(Unlink, entry_cache)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+
+ expect_lookup(RELPATH, ino, 2, 2);
+ expect_unlink(1, RELPATH, 0);
+
+ ASSERT_EQ(0, unlink(FULLPATH)) << strerror(errno);
+ ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
+}
+
+/*
+ * Unlink a multiply-linked file. There should be no FUSE_FORGET because the
+ * file is still linked.
+ */
+TEST_F(Unlink, multiply_linked)
+{
+ const char FULLPATH0[] = "mountpoint/some_file.txt";
+ const char RELPATH0[] = "some_file.txt";
+ const char FULLPATH1[] = "mountpoint/other_file.txt";
+ const char RELPATH1[] = "other_file.txt";
+ uint64_t ino = 42;
+
+ expect_lookup(RELPATH0, ino, 1, 2);
+ expect_unlink(1, RELPATH0, 0);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_FORGET &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).Times(0);
+ expect_lookup(RELPATH1, ino, 1, 1);
+
+ ASSERT_EQ(0, unlink(FULLPATH0)) << strerror(errno);
+
+ /*
+ * The final syscall simply ensures that no FUSE_FORGET was ever sent,
+ * by scheduling an arbitrary different operation after a FUSE_FORGET
+ * would've been sent.
+ */
+ ASSERT_EQ(0, access(FULLPATH1, F_OK)) << strerror(errno);
+}
+
+TEST_F(Unlink, ok)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ sem_t sem;
+
+ ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
+
+ expect_lookup(RELPATH, ino, 1);
+ expect_unlink(1, RELPATH, 0);
+ expect_forget(ino, 1, &sem);
+
+ ASSERT_EQ(0, unlink(FULLPATH)) << strerror(errno);
+ sem_wait(&sem);
+ sem_destroy(&sem);
+}
+
+/* Unlink an open file */
+TEST_F(Unlink, open_but_deleted)
+{
+ const char FULLPATH0[] = "mountpoint/some_file.txt";
+ const char RELPATH0[] = "some_file.txt";
+ const char FULLPATH1[] = "mountpoint/other_file.txt";
+ const char RELPATH1[] = "other_file.txt";
+ uint64_t ino = 42;
+ int fd;
+
+ expect_lookup(RELPATH0, ino, 2);
+ expect_open(ino, 0, 1);
+ expect_unlink(1, RELPATH0, 0);
+ expect_lookup(RELPATH1, ino, 1, 1);
+
+ fd = open(FULLPATH0, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(0, unlink(FULLPATH0)) << strerror(errno);
+
+ /*
+ * The final syscall simply ensures that no FUSE_FORGET was ever sent,
+ * by scheduling an arbitrary different operation after a FUSE_FORGET
+ * would've been sent.
+ */
+ ASSERT_EQ(0, access(FULLPATH1, F_OK)) << strerror(errno);
+ leak(fd);
+}
diff --git a/tests/sys/fs/fusefs/utils.cc b/tests/sys/fs/fusefs/utils.cc
new file mode 100644
index 000000000000..125b7e2d6fc7
--- /dev/null
+++ b/tests/sys/fs/fusefs/utils.cc
@@ -0,0 +1,673 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <sys/wait.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <semaphore.h>
+#include <unistd.h>
+}
+
+#include <gtest/gtest.h>
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+/*
+ * The default max_write is set to this formula in libfuse, though
+ * individual filesystems can lower it. The "- 4096" was added in
+ * commit 154ffe2, with the commit message "fix".
+ */
+const uint32_t libfuse_max_write = 32 * getpagesize() + 0x1000 - 4096;
+
+/* Check that fusefs(4) is accessible and the current user can mount(2) */
+void check_environment()
+{
+ const char *devnode = "/dev/fuse";
+ const char *bsdextended_node = "security.mac.bsdextended.enabled";
+ int bsdextended_val = 0;
+ size_t bsdextended_size = sizeof(bsdextended_val);
+ int bsdextended_found;
+ const char *usermount_node = "vfs.usermount";
+ int usermount_val = 0;
+ size_t usermount_size = sizeof(usermount_val);
+ if (eaccess(devnode, R_OK | W_OK)) {
+ if (errno == ENOENT) {
+ GTEST_SKIP() << devnode << " does not exist";
+ } else if (errno == EACCES) {
+ GTEST_SKIP() << devnode <<
+ " is not accessible by the current user";
+ } else {
+ GTEST_SKIP() << strerror(errno);
+ }
+ }
+ // mac_bsdextended(4), when enabled, generates many more GETATTR
+ // operations. The fusefs tests' expectations don't account for those,
+ // and adding extra code to handle them obfuscates the real purpose of
+ // the tests. Better just to skip the fusefs tests if mac_bsdextended
+ // is enabled.
+ bsdextended_found = sysctlbyname(bsdextended_node, &bsdextended_val,
+ &bsdextended_size, NULL, 0);
+ if (bsdextended_found == 0 && bsdextended_val != 0)
+ GTEST_SKIP() <<
+ "The fusefs tests are incompatible with mac_bsdextended.";
+ ASSERT_EQ(sysctlbyname(usermount_node, &usermount_val, &usermount_size,
+ NULL, 0),
+ 0);
+ if (geteuid() != 0 && !usermount_val)
+ GTEST_SKIP() << "current user is not allowed to mount";
+}
+
+const char *cache_mode_to_s(enum cache_mode cm) {
+ switch (cm) {
+ case Uncached:
+ return "Uncached";
+ case Writethrough:
+ return "Writethrough";
+ case Writeback:
+ return "Writeback";
+ case WritebackAsync:
+ return "WritebackAsync";
+ default:
+ return "Unknown";
+ }
+}
+
+bool is_unsafe_aio_enabled(void) {
+ const char *node = "vfs.aio.enable_unsafe";
+ int val = 0;
+ size_t size = sizeof(val);
+
+ if (sysctlbyname(node, &val, &size, NULL, 0)) {
+ perror("sysctlbyname");
+ return (false);
+ }
+ return (val != 0);
+}
+
+class FuseEnv: public Environment {
+ virtual void SetUp() {
+ check_environment();
+ }
+};
+
+void FuseTest::SetUp() {
+ const char *maxbcachebuf_node = "vfs.maxbcachebuf";
+ const char *maxphys_node = "kern.maxphys";
+ size_t size;
+
+ size = sizeof(m_maxbcachebuf);
+ ASSERT_EQ(0, sysctlbyname(maxbcachebuf_node, &m_maxbcachebuf, &size,
+ NULL, 0)) << strerror(errno);
+ size = sizeof(m_maxphys);
+ ASSERT_EQ(0, sysctlbyname(maxphys_node, &m_maxphys, &size, NULL, 0))
+ << strerror(errno);
+ /*
+ * Set the default max_write to a distinct value from MAXPHYS to catch
+ * bugs that confuse the two.
+ */
+ if (m_maxwrite == 0)
+ m_maxwrite = MIN(libfuse_max_write, (uint32_t)m_maxphys / 2);
+
+ try {
+ m_mock = new MockFS(m_maxread, m_maxreadahead, m_allow_other,
+ m_default_permissions, m_push_symlinks_in, m_ro,
+ m_pm, m_init_flags, m_kernel_minor_version,
+ m_maxwrite, m_async, m_noclusterr, m_time_gran,
+ m_nointr, m_noatime, m_fsname, m_subtype,
+ m_no_auto_init);
+ /*
+ * FUSE_ACCESS is called almost universally. Expecting it in
+ * each test case would be super-annoying. Instead, set a
+ * default expectation for FUSE_ACCESS and return ENOSYS.
+ *
+ * Individual test cases can override this expectation since
+ * googlemock evaluates expectations in LIFO order.
+ */
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_ACCESS);
+ }, Eq(true)),
+ _)
+ ).Times(AnyNumber())
+ .WillRepeatedly(Invoke(ReturnErrno(ENOSYS)));
+ /*
+ * FUSE_BMAP is called for most test cases that read data. Set
+ * a default expectation and return ENOSYS.
+ *
+ * Individual test cases can override this expectation since
+ * googlemock evaluates expectations in LIFO order.
+ */
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_BMAP);
+ }, Eq(true)),
+ _)
+ ).Times(AnyNumber())
+ .WillRepeatedly(Invoke(ReturnErrno(ENOSYS)));
+ } catch (std::system_error err) {
+ FAIL() << err.what();
+ }
+}
+
+void
+FuseTest::expect_access(uint64_t ino, mode_t access_mode, int error)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_ACCESS &&
+ in.header.nodeid == ino &&
+ in.body.access.mask == access_mode);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(error)));
+}
+
+void
+FuseTest::expect_destroy(int error)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_DESTROY);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in, auto& out) {
+ m_mock->m_quit = true;
+ out.header.len = sizeof(out.header);
+ out.header.unique = in.header.unique;
+ out.header.error = -error;
+ })));
+}
+
+void
+FuseTest::expect_fallocate(uint64_t ino, uint64_t offset, uint64_t length,
+ uint32_t mode, int error, int times)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_FALLOCATE &&
+ in.header.nodeid == ino &&
+ in.body.fallocate.offset == offset &&
+ in.body.fallocate.length == length &&
+ in.body.fallocate.mode == mode);
+ }, Eq(true)),
+ _)
+ ).Times(times)
+ .WillRepeatedly(Invoke(ReturnErrno(error)));
+}
+
+void
+FuseTest::expect_flush(uint64_t ino, int times, ProcessMockerT r)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_FLUSH &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).Times(times)
+ .WillRepeatedly(Invoke(r));
+}
+
+void
+FuseTest::expect_forget(uint64_t ino, uint64_t nlookup, sem_t *sem)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_FORGET &&
+ in.header.nodeid == ino &&
+ in.body.forget.nlookup == nlookup);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke([=](auto in __unused, auto &out __unused) {
+ if (sem != NULL)
+ sem_post(sem);
+ /* FUSE_FORGET has no response! */
+ }));
+}
+
+void FuseTest::expect_getattr(uint64_t ino, uint64_t size)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino; // Must match nodeid
+ out.body.attr.attr.mode = S_IFREG | 0644;
+ out.body.attr.attr.size = size;
+ out.body.attr.attr_valid = UINT64_MAX;
+ })));
+}
+
+void FuseTest::expect_getxattr(uint64_t ino, const char *attr, ProcessMockerT r)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *a = (const char*)in.body.bytes +
+ sizeof(fuse_getxattr_in);
+ return (in.header.opcode == FUSE_GETXATTR &&
+ in.header.nodeid == ino &&
+ 0 == strcmp(attr, a));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(r));
+}
+
+void FuseTest::expect_lookup(const char *relpath, uint64_t ino, mode_t mode,
+ uint64_t size, int times, uint64_t attr_valid, uid_t uid, gid_t gid)
+{
+ EXPECT_LOOKUP(FUSE_ROOT_ID, relpath)
+ .Times(times)
+ .WillRepeatedly(Invoke(
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.nlink = 1;
+ out.body.entry.attr_valid = attr_valid;
+ out.body.entry.attr.size = size;
+ out.body.entry.attr.uid = uid;
+ out.body.entry.attr.gid = gid;
+ })));
+}
+
+void FuseTest::expect_lookup_7_8(const char *relpath, uint64_t ino, mode_t mode,
+ uint64_t size, int times, uint64_t attr_valid, uid_t uid, gid_t gid)
+{
+ EXPECT_LOOKUP(FUSE_ROOT_ID, relpath)
+ .Times(times)
+ .WillRepeatedly(Invoke(
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry_7_8);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.nlink = 1;
+ out.body.entry.attr_valid = attr_valid;
+ out.body.entry.attr.size = size;
+ out.body.entry.attr.uid = uid;
+ out.body.entry.attr.gid = gid;
+ })));
+}
+
+void FuseTest::expect_open(uint64_t ino, uint32_t flags, int times)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_OPEN &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).Times(times)
+ .WillRepeatedly(Invoke(
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ out.header.len = sizeof(out.header);
+ SET_OUT_HEADER_LEN(out, open);
+ out.body.open.fh = FH;
+ out.body.open.open_flags = flags;
+ })));
+}
+
+void FuseTest::expect_opendir(uint64_t ino)
+{
+ /* opendir(3) calls fstatfs */
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_STATFS);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(
+ ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, statfs);
+ })));
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_OPENDIR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ out.header.len = sizeof(out.header);
+ SET_OUT_HEADER_LEN(out, open);
+ out.body.open.fh = FH;
+ })));
+}
+
+void FuseTest::expect_read(uint64_t ino, uint64_t offset, uint64_t isize,
+ uint64_t osize, const void *contents, int flags, uint64_t fh)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READ &&
+ in.header.nodeid == ino &&
+ in.body.read.fh == fh &&
+ in.body.read.offset == offset &&
+ in.body.read.size == isize &&
+ (flags == -1 ?
+ (in.body.read.flags == O_RDONLY ||
+ in.body.read.flags == O_RDWR)
+ : in.body.read.flags == (uint32_t)flags));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ assert(osize <= sizeof(out.body.bytes));
+ out.header.len = sizeof(struct fuse_out_header) + osize;
+ memmove(out.body.bytes, contents, osize);
+ }))).RetiresOnSaturation();
+}
+
+void FuseTest::expect_readdir(uint64_t ino, uint64_t off,
+ std::vector<struct dirent> &ents)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_READDIR &&
+ in.header.nodeid == ino &&
+ in.body.readdir.fh == FH &&
+ in.body.readdir.offset == off);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto& out) {
+ struct fuse_dirent *fde = (struct fuse_dirent*)&(out.body);
+ int i = 0;
+
+ out.header.error = 0;
+ out.header.len = 0;
+
+ for (const auto& it: ents) {
+ size_t entlen, entsize;
+
+ fde->ino = it.d_fileno;
+ fde->off = it.d_off;
+ fde->type = it.d_type;
+ fde->namelen = it.d_namlen;
+ strncpy(fde->name, it.d_name, it.d_namlen);
+ entlen = FUSE_NAME_OFFSET + fde->namelen;
+ entsize = FUSE_DIRENT_SIZE(fde);
+ /*
+ * The FUSE protocol does not require zeroing out the
+ * unused portion of the name. But it's a good
+ * practice to prevent information disclosure to the
+ * FUSE client, even though the client is usually the
+ * kernel
+ */
+ memset(fde->name + fde->namelen, 0, entsize - entlen);
+ if (out.header.len + entsize > in.body.read.size) {
+ printf("Overflow in readdir expectation: i=%d\n"
+ , i);
+ break;
+ }
+ out.header.len += entsize;
+ fde = (struct fuse_dirent*)
+ ((intmax_t*)fde + entsize / sizeof(intmax_t));
+ i++;
+ }
+ out.header.len += sizeof(out.header);
+ })));
+
+}
+void FuseTest::expect_release(uint64_t ino, uint64_t fh)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_RELEASE &&
+ in.header.nodeid == ino &&
+ in.body.release.fh == fh);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(0)));
+}
+
+void FuseTest::expect_releasedir(uint64_t ino, ProcessMockerT r)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_RELEASEDIR &&
+ in.header.nodeid == ino &&
+ in.body.release.fh == FH);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(r));
+}
+
+void FuseTest::expect_unlink(uint64_t parent, const char *path, int error)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_UNLINK &&
+ 0 == strcmp(path, in.body.unlink) &&
+ in.header.nodeid == parent);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(error)));
+}
+
+void FuseTest::expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
+ uint64_t osize, uint32_t flags_set, uint32_t flags_unset,
+ const void *contents)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *buf = (const char*)in.body.bytes +
+ sizeof(struct fuse_write_in);
+ bool pid_ok;
+ uint32_t wf = in.body.write.write_flags;
+
+ assert(isize <= sizeof(in.body.bytes) -
+ sizeof(struct fuse_write_in));
+ if (wf & FUSE_WRITE_CACHE)
+ pid_ok = true;
+ else
+ pid_ok = (pid_t)in.header.pid == getpid();
+
+ return (in.header.opcode == FUSE_WRITE &&
+ in.header.nodeid == ino &&
+ in.body.write.fh == FH &&
+ in.body.write.offset == offset &&
+ in.body.write.size == isize &&
+ pid_ok &&
+ (wf & flags_set) == flags_set &&
+ (wf & flags_unset) == 0 &&
+ (in.body.write.flags == O_WRONLY ||
+ in.body.write.flags == O_RDWR) &&
+ 0 == bcmp(buf, contents, isize));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, write);
+ out.body.write.size = osize;
+ })));
+}
+
+void FuseTest::expect_write_7_8(uint64_t ino, uint64_t offset, uint64_t isize,
+ uint64_t osize, const void *contents)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *buf = (const char*)in.body.bytes +
+ FUSE_COMPAT_WRITE_IN_SIZE;
+ bool pid_ok = (pid_t)in.header.pid == getpid();
+
+ assert(isize <= sizeof(in.body.bytes) -
+ FUSE_COMPAT_WRITE_IN_SIZE);
+ return (in.header.opcode == FUSE_WRITE &&
+ in.header.nodeid == ino &&
+ in.body.write.fh == FH &&
+ in.body.write.offset == offset &&
+ in.body.write.size == isize &&
+ pid_ok &&
+ 0 == bcmp(buf, contents, isize));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, write);
+ out.body.write.size = osize;
+ })));
+}
+
+void
+get_unprivileged_id(uid_t *uid, gid_t *gid)
+{
+ struct passwd *pw;
+ struct group *gr;
+
+ /*
+ * First try "tests", Kyua's default unprivileged user. XXX after
+ * GoogleTest gains a proper Kyua wrapper, get this with the Kyua API
+ */
+ pw = getpwnam("tests");
+ if (pw == NULL) {
+ /* Fall back to "nobody" */
+ pw = getpwnam("nobody");
+ }
+ if (pw == NULL)
+ GTEST_SKIP() << "Test requires an unprivileged user";
+ /* Use group "nobody", which is Kyua's default unprivileged group */
+ gr = getgrnam("nobody");
+ if (gr == NULL)
+ GTEST_SKIP() << "Test requires an unprivileged group";
+ *uid = pw->pw_uid;
+ *gid = gr->gr_gid;
+}
+
+void
+FuseTest::fork(bool drop_privs, int *child_status,
+ std::function<void()> parent_func,
+ std::function<int()> child_func)
+{
+ sem_t *sem;
+ int mprot = PROT_READ | PROT_WRITE;
+ int mflags = MAP_ANON | MAP_SHARED;
+ pid_t child;
+ uid_t uid;
+ gid_t gid;
+
+ if (drop_privs) {
+ get_unprivileged_id(&uid, &gid);
+ if (IsSkipped())
+ return;
+ }
+
+ sem = (sem_t*)mmap(NULL, sizeof(*sem), mprot, mflags, -1, 0);
+ ASSERT_NE(MAP_FAILED, sem) << strerror(errno);
+ ASSERT_EQ(0, sem_init(sem, 1, 0)) << strerror(errno);
+
+ if ((child = ::fork()) == 0) {
+ /* In child */
+ int err = 0;
+
+ if (sem_wait(sem)) {
+ perror("sem_wait");
+ err = 1;
+ goto out;
+ }
+
+ if (drop_privs && 0 != setegid(gid)) {
+ perror("setegid");
+ err = 1;
+ goto out;
+ }
+ if (drop_privs && 0 != setreuid(-1, uid)) {
+ perror("setreuid");
+ err = 1;
+ goto out;
+ }
+ err = child_func();
+
+out:
+ sem_destroy(sem);
+ _exit(err);
+ } else if (child > 0) {
+ /*
+ * In parent. Cleanup must happen here, because it's still
+ * privileged.
+ */
+ m_mock->m_child_pid = child;
+ ASSERT_NO_FATAL_FAILURE(parent_func());
+
+ /* Signal the child process to go */
+ ASSERT_EQ(0, sem_post(sem)) << strerror(errno);
+
+ ASSERT_LE(0, wait(child_status)) << strerror(errno);
+ } else {
+ FAIL() << strerror(errno);
+ }
+ munmap(sem, sizeof(*sem));
+ return;
+}
+
+void
+FuseTest::reclaim_vnode(const char *path)
+{
+ int err;
+
+ err = sysctlbyname(reclaim_mib, NULL, 0, path, strlen(path) + 1);
+ ASSERT_EQ(0, err) << strerror(errno);
+}
+
+static void usage(char* progname) {
+ fprintf(stderr, "Usage: %s [-v]\n\t-v increase verbosity\n", progname);
+ exit(2);
+}
+
+int main(int argc, char **argv) {
+ int ch;
+ FuseEnv *fuse_env = new FuseEnv;
+
+ InitGoogleTest(&argc, argv);
+ AddGlobalTestEnvironment(fuse_env);
+
+ while ((ch = getopt(argc, argv, "v")) != -1) {
+ switch (ch) {
+ case 'v':
+ verbosity++;
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ return (RUN_ALL_TESTS());
+}
diff --git a/tests/sys/fs/fusefs/utils.hh b/tests/sys/fs/fusefs/utils.hh
new file mode 100644
index 000000000000..91bbba909672
--- /dev/null
+++ b/tests/sys/fs/fusefs/utils.hh
@@ -0,0 +1,277 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+struct _sem;
+typedef struct _sem sem_t;
+struct _dirdesc;
+typedef struct _dirdesc DIR;
+
+/* Nanoseconds to sleep, for tests that must */
+#define NAP_NS (100'000'000)
+
+void get_unprivileged_id(uid_t *uid, gid_t *gid);
+inline void nap()
+{
+ usleep(NAP_NS / 1000);
+}
+
+enum cache_mode {
+ Uncached,
+ Writethrough,
+ Writeback,
+ WritebackAsync
+};
+
+const char *cache_mode_to_s(enum cache_mode cm);
+bool is_unsafe_aio_enabled(void);
+
+extern const uint32_t libfuse_max_write;
+class FuseTest : public ::testing::Test {
+ protected:
+ uint32_t m_maxread;
+ uint32_t m_maxreadahead;
+ uint32_t m_maxwrite;
+ uint32_t m_init_flags;
+ bool m_allow_other;
+ bool m_default_permissions;
+ uint32_t m_kernel_minor_version;
+ enum poll_method m_pm;
+ bool m_noatime;
+ bool m_push_symlinks_in;
+ bool m_ro;
+ bool m_async;
+ bool m_noclusterr;
+ bool m_nointr;
+ bool m_no_auto_init;
+ unsigned m_time_gran;
+ MockFS *m_mock = NULL;
+ const static uint64_t FH = 0xdeadbeef1a7ebabe;
+ const char *reclaim_mib = "debug.try_reclaim_vnode";
+ const char *m_fsname;
+ const char *m_subtype;
+
+ public:
+ int m_maxbcachebuf;
+ unsigned long m_maxphys;
+
+ FuseTest():
+ m_maxread(0),
+ m_maxreadahead(0),
+ m_maxwrite(0),
+ m_init_flags(0),
+ m_allow_other(false),
+ m_default_permissions(false),
+ m_kernel_minor_version(FUSE_KERNEL_MINOR_VERSION),
+ m_pm(BLOCKING),
+ m_noatime(false),
+ m_push_symlinks_in(false),
+ m_ro(false),
+ m_async(false),
+ m_noclusterr(false),
+ m_nointr(false),
+ m_no_auto_init(false),
+ m_time_gran(1),
+ m_fsname(""),
+ m_subtype(""),
+ m_maxbcachebuf(0),
+ m_maxphys(0)
+ {}
+
+ virtual void SetUp();
+
+ virtual void TearDown() {
+ if (m_mock)
+ delete m_mock;
+ }
+
+ /*
+ * Create an expectation that FUSE_ACCESS will be called once for the
+ * given inode with the given access_mode, returning the given errno
+ */
+ void expect_access(uint64_t ino, mode_t access_mode, int error);
+
+ /* Expect FUSE_DESTROY and shutdown the daemon */
+ void expect_destroy(int error);
+
+ /*
+ * Create an expectation that FUSE_FALLOCATE will be called with the
+ * given inode, offset, length, and mode, exactly times times and
+ * returning error
+ */
+ void expect_fallocate(uint64_t ino, uint64_t offset, uint64_t length,
+ uint32_t mode, int error, int times=1);
+
+ /*
+ * Create an expectation that FUSE_FLUSH will be called times times for
+ * the given inode
+ */
+ void expect_flush(uint64_t ino, int times, ProcessMockerT r);
+
+ /*
+ * Create an expectation that FUSE_FORGET will be called for the given
+ * inode. There will be no response. If sem is provided, it will be
+ * posted after the operation is received by the daemon.
+ */
+ void expect_forget(uint64_t ino, uint64_t nlookup, sem_t *sem = NULL);
+
+ /*
+ * Create an expectation that FUSE_GETATTR will be called for the given
+ * inode any number of times. It will respond with a few basic
+ * attributes, like the given size and the mode S_IFREG | 0644
+ */
+ void expect_getattr(uint64_t ino, uint64_t size);
+
+ /*
+ * Create an expectation that FUSE_GETXATTR will be called once for the
+ * given inode.
+ */
+ void expect_getxattr(uint64_t ino, const char *attr, ProcessMockerT r);
+
+ /*
+ * Create an expectation that FUSE_LOOKUP will be called for the given
+ * path exactly times times and cache validity period. It will respond
+ * with inode ino, mode mode, filesize size.
+ */
+ void expect_lookup(const char *relpath, uint64_t ino, mode_t mode,
+ uint64_t size, int times, uint64_t attr_valid = UINT64_MAX,
+ uid_t uid = 0, gid_t gid = 0);
+
+ /* The protocol 7.8 version of expect_lookup */
+ void expect_lookup_7_8(const char *relpath, uint64_t ino, mode_t mode,
+ uint64_t size, int times, uint64_t attr_valid = UINT64_MAX,
+ uid_t uid = 0, gid_t gid = 0);
+
+ /*
+ * Create an expectation that FUSE_OPEN will be called for the given
+ * inode exactly times times. It will return with open_flags flags and
+ * file handle FH.
+ */
+ void expect_open(uint64_t ino, uint32_t flags, int times);
+
+ /*
+ * Create an expectation that FUSE_OPENDIR will be called exactly once
+ * for inode ino.
+ */
+ void expect_opendir(uint64_t ino);
+
+ /*
+ * Create an expectation that FUSE_READ will be called exactly once for
+ * the given inode, at offset offset and with size isize. It will
+ * return the first osize bytes from contents
+ *
+ * Protocol 7.8 tests can use this same expectation method because
+ * nothing currently validates the size of the fuse_read_in struct.
+ */
+ void expect_read(uint64_t ino, uint64_t offset, uint64_t isize,
+ uint64_t osize, const void *contents, int flags = -1,
+ uint64_t fh = FH);
+
+ /*
+ * Create an expectation that FUSE_READIR will be called any number of
+ * times on the given ino with the given offset, returning (by copy)
+ * the provided entries
+ */
+ void expect_readdir(uint64_t ino, uint64_t off,
+ std::vector<struct dirent> &ents);
+
+ /*
+ * Create an expectation that FUSE_RELEASE will be called exactly once
+ * for the given inode and filehandle, returning success
+ */
+ void expect_release(uint64_t ino, uint64_t fh);
+
+ /*
+ * Create an expectation that FUSE_RELEASEDIR will be called exactly
+ * once for the given inode
+ */
+ void expect_releasedir(uint64_t ino, ProcessMockerT r);
+
+ /*
+ * Create an expectation that FUSE_UNLINK will be called exactly once
+ * for the given path, returning an errno
+ */
+ void expect_unlink(uint64_t parent, const char *path, int error);
+
+ /*
+ * Create an expectation that FUSE_WRITE will be called exactly once
+ * for the given inode, at offset offset, with size isize and buffer
+ * contents. Any flags present in flags_set must be set, and any
+ * present in flags_unset must not be set. Other flags are don't care.
+ * It will return osize.
+ */
+ void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
+ uint64_t osize, uint32_t flags_set, uint32_t flags_unset,
+ const void *contents);
+
+ /* Protocol 7.8 version of expect_write */
+ void expect_write_7_8(uint64_t ino, uint64_t offset, uint64_t isize,
+ uint64_t osize, const void *contents);
+
+ /*
+ * Helper that runs code in a child process.
+ *
+ * First, parent_func runs in the parent process.
+ * Then, child_func runs in the child process, dropping privileges if
+ * desired.
+ * Finally, fusetest_fork returns.
+ *
+ * # Returns
+ *
+ * fusetest_fork may SKIP the test, which the caller should detect with
+ * the IsSkipped() method. If not, then the child's exit status will
+ * be returned in status.
+ */
+ void fork(bool drop_privs, int *status,
+ std::function<void()> parent_func,
+ std::function<int()> child_func);
+
+ /*
+ * Deliberately leak a file descriptor.
+ *
+ * Closing a file descriptor on fusefs would cause the server to
+ * receive FUSE_CLOSE and possibly FUSE_INACTIVE. Handling those
+ * operations would needlessly complicate most tests. So most tests
+ * deliberately leak the file descriptors instead. This method serves
+ * to document the leakage, and provide a single point of suppression
+ * for static analyzers.
+ */
+ /* coverity[+close: arg-0] */
+ static void leak(int fd __unused) {}
+
+ /*
+ * Deliberately leak a DIR* pointer
+ *
+ * See comments for FuseTest::leak
+ */
+ static void leakdir(DIR* dirp __unused) {}
+
+ /* Manually reclaim a vnode. Requires root privileges. */
+ void reclaim_vnode(const char *fullpath);
+};
diff --git a/tests/sys/fs/fusefs/write.cc b/tests/sys/fs/fusefs/write.cc
new file mode 100644
index 000000000000..1fe2e3cc522d
--- /dev/null
+++ b/tests/sys/fs/fusefs/write.cc
@@ -0,0 +1,1589 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+
+#include <aio.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+class Write: public FuseTest {
+
+public:
+void SetUp() {
+ FuseTest::SetUp();
+}
+
+void TearDown() {
+ struct sigaction sa;
+
+ bzero(&sa, sizeof(sa));
+ sa.sa_handler = SIG_DFL;
+ sigaction(SIGXFSZ, &sa, NULL);
+
+ FuseTest::TearDown();
+}
+
+void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
+{
+ FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1);
+}
+
+void expect_release(uint64_t ino, ProcessMockerT r)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_RELEASE &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(r));
+}
+
+void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
+ uint64_t osize, const void *contents)
+{
+ FuseTest::expect_write(ino, offset, isize, osize, 0, 0, contents);
+}
+
+/* Expect a write that may or may not come, depending on the cache mode */
+void maybe_expect_write(uint64_t ino, uint64_t offset, uint64_t size,
+ const void *contents)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *buf = (const char*)in.body.bytes +
+ sizeof(struct fuse_write_in);
+
+ assert(size <= sizeof(in.body.bytes) -
+ sizeof(struct fuse_write_in));
+ return (in.header.opcode == FUSE_WRITE &&
+ in.header.nodeid == ino &&
+ in.body.write.offset == offset &&
+ in.body.write.size == size &&
+ 0 == bcmp(buf, contents, size));
+ }, Eq(true)),
+ _)
+ ).Times(AtMost(1))
+ .WillRepeatedly(Invoke(
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, write);
+ out.body.write.size = size;
+ })
+ ));
+}
+
+};
+
+class Write_7_8: public FuseTest {
+
+public:
+virtual void SetUp() {
+ m_kernel_minor_version = 8;
+ FuseTest::SetUp();
+}
+
+void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
+{
+ FuseTest::expect_lookup_7_8(relpath, ino, S_IFREG | 0644, size, 1);
+}
+
+};
+
+class AioWrite: public Write {
+virtual void SetUp() {
+ if (!is_unsafe_aio_enabled())
+ GTEST_SKIP() <<
+ "vfs.aio.enable_unsafe must be set for this test";
+ FuseTest::SetUp();
+}
+};
+
+/* Tests for the writeback cache mode */
+class WriteBack: public Write {
+public:
+virtual void SetUp() {
+ m_init_flags |= FUSE_WRITEBACK_CACHE;
+ FuseTest::SetUp();
+ if (IsSkipped())
+ return;
+}
+
+void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
+ uint64_t osize, const void *contents)
+{
+ FuseTest::expect_write(ino, offset, isize, osize, FUSE_WRITE_CACHE, 0,
+ contents);
+}
+};
+
+class WriteBackAsync: public WriteBack {
+public:
+virtual void SetUp() {
+ m_async = true;
+ m_maxwrite = 65536;
+ WriteBack::SetUp();
+}
+};
+
+class TimeGran: public WriteBackAsync, public WithParamInterface<unsigned> {
+public:
+virtual void SetUp() {
+ m_time_gran = 1 << GetParam();
+ WriteBackAsync::SetUp();
+}
+};
+
+/* Tests for clustered writes with WriteBack cacheing */
+class WriteCluster: public WriteBack {
+public:
+virtual void SetUp() {
+ m_async = true;
+ m_maxwrite = UINT32_MAX; // Anything larger than MAXPHYS will suffice
+ WriteBack::SetUp();
+ if (m_maxphys < 2 * DFLTPHYS)
+ GTEST_SKIP() << "MAXPHYS must be at least twice DFLTPHYS"
+ << " for this test";
+ if (m_maxphys < 2 * (unsigned long )m_maxbcachebuf)
+ GTEST_SKIP() << "MAXPHYS must be at least twice maxbcachebuf"
+ << " for this test";
+}
+};
+
+/* Tests relating to the server's max_write property */
+class WriteMaxWrite: public Write {
+public:
+virtual void SetUp() {
+ /*
+ * For this test, m_maxwrite must be less than either m_maxbcachebuf or
+ * maxphys.
+ */
+ m_maxwrite = 32768;
+ Write::SetUp();
+}
+};
+
+class WriteEofDuringVnopStrategy: public Write, public WithParamInterface<int>
+{};
+
+class WriteRlimitFsize: public Write, public WithParamInterface<int> {
+public:
+static sig_atomic_t s_sigxfsz;
+struct rlimit m_initial_limit;
+
+void SetUp() {
+ s_sigxfsz = 0;
+ getrlimit(RLIMIT_FSIZE, &m_initial_limit);
+ FuseTest::SetUp();
+}
+
+void TearDown() {
+ setrlimit(RLIMIT_FSIZE, &m_initial_limit);
+
+ FuseTest::TearDown();
+}
+};
+
+sig_atomic_t WriteRlimitFsize::s_sigxfsz = 0;
+
+void sigxfsz_handler(int __unused sig) {
+ WriteRlimitFsize::s_sigxfsz = 1;
+}
+
+/* AIO writes need to set the header's pid field correctly */
+/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */
+TEST_F(AioWrite, DISABLED_aio_write)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ uint64_t offset = 4096;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+ struct aiocb iocb, *piocb;
+
+ expect_lookup(RELPATH, ino, 0);
+ expect_open(ino, 0, 1);
+ expect_write(ino, offset, bufsize, bufsize, CONTENTS);
+
+ fd = open(FULLPATH, O_WRONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ iocb.aio_nbytes = bufsize;
+ iocb.aio_fildes = fd;
+ iocb.aio_buf = __DECONST(void *, CONTENTS);
+ iocb.aio_offset = offset;
+ iocb.aio_sigevent.sigev_notify = SIGEV_NONE;
+ ASSERT_EQ(0, aio_write(&iocb)) << strerror(errno);
+ ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
+ leak(fd);
+}
+
+/*
+ * When a file is opened with O_APPEND, we should forward that flag to
+ * FUSE_OPEN (tested by Open.o_append) but still attempt to calculate the
+ * offset internally. That way we'll work both with filesystems that
+ * understand O_APPEND (and ignore the offset) and filesystems that don't (and
+ * simply use the offset).
+ *
+ * Note that verifying the O_APPEND flag in FUSE_OPEN is done in the
+ * Open.o_append test.
+ */
+TEST_F(Write, append)
+{
+ const ssize_t BUFSIZE = 9;
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char CONTENTS[BUFSIZE] = "abcdefgh";
+ uint64_t ino = 42;
+ /*
+ * Set offset to a maxbcachebuf boundary so we don't need to RMW when
+ * using writeback caching
+ */
+ uint64_t initial_offset = m_maxbcachebuf;
+ int fd;
+
+ expect_lookup(RELPATH, ino, initial_offset);
+ expect_open(ino, 0, 1);
+ expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS);
+
+ /* Must open O_RDWR or fuse(4) implicitly sets direct_io */
+ fd = open(FULLPATH, O_RDWR | O_APPEND);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
+ leak(fd);
+}
+
+/* If a file is cached, then appending to the end should not cause a read */
+TEST_F(Write, append_to_cached)
+{
+ const ssize_t BUFSIZE = 9;
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ char *oldcontents, *oldbuf;
+ const char CONTENTS[BUFSIZE] = "abcdefgh";
+ uint64_t ino = 42;
+ /*
+ * Set offset in between maxbcachebuf boundary to test buffer handling
+ */
+ uint64_t oldsize = m_maxbcachebuf / 2;
+ int fd;
+
+ oldcontents = new char[oldsize]();
+ oldbuf = new char[oldsize];
+
+ expect_lookup(RELPATH, ino, oldsize);
+ expect_open(ino, 0, 1);
+ expect_read(ino, 0, oldsize, oldsize, oldcontents);
+ maybe_expect_write(ino, oldsize, BUFSIZE, CONTENTS);
+
+ /* Must open O_RDWR or fuse(4) implicitly sets direct_io */
+ fd = open(FULLPATH, O_RDWR | O_APPEND);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ /* Read the old data into the cache */
+ ASSERT_EQ((ssize_t)oldsize, read(fd, oldbuf, oldsize))
+ << strerror(errno);
+
+ /* Write the new data. There should be no more read operations */
+ ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
+ leak(fd);
+ delete[] oldbuf;
+ delete[] oldcontents;
+}
+
+TEST_F(Write, append_direct_io)
+{
+ const ssize_t BUFSIZE = 9;
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char CONTENTS[BUFSIZE] = "abcdefgh";
+ uint64_t ino = 42;
+ uint64_t initial_offset = 4096;
+ int fd;
+
+ expect_lookup(RELPATH, ino, initial_offset);
+ expect_open(ino, FOPEN_DIRECT_IO, 1);
+ expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS);
+
+ fd = open(FULLPATH, O_WRONLY | O_APPEND);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
+ leak(fd);
+}
+
+/* A direct write should evict any overlapping cached data */
+TEST_F(Write, direct_io_evicts_cache)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char CONTENTS0[] = "abcdefgh";
+ const char CONTENTS1[] = "ijklmnop";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS0) + 1;
+ char readbuf[bufsize];
+
+ expect_lookup(RELPATH, ino, bufsize);
+ expect_open(ino, 0, 1);
+ expect_read(ino, 0, bufsize, bufsize, CONTENTS0);
+ expect_write(ino, 0, bufsize, bufsize, CONTENTS1);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ // Prime cache
+ ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
+
+ // Write directly, evicting cache
+ ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
+ ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
+ ASSERT_EQ(bufsize, write(fd, CONTENTS1, bufsize)) << strerror(errno);
+
+ // Read again. Cache should be bypassed
+ expect_read(ino, 0, bufsize, bufsize, CONTENTS1);
+ ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
+ ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
+ ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
+ ASSERT_STREQ(readbuf, CONTENTS1);
+
+ leak(fd);
+}
+
+/*
+ * If the server doesn't return FOPEN_DIRECT_IO during FUSE_OPEN, then it's not
+ * allowed to return a short write for that file handle. However, if it does
+ * then we should still do our darndest to handle it by resending the unwritten
+ * portion.
+ */
+TEST_F(Write, indirect_io_short_write)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefghijklmnop";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+ ssize_t bufsize0 = 11;
+ ssize_t bufsize1 = strlen(CONTENTS) - bufsize0;
+ const char *contents1 = CONTENTS + bufsize0;
+
+ expect_lookup(RELPATH, ino, 0);
+ expect_open(ino, 0, 1);
+ expect_write(ino, 0, bufsize, bufsize0, CONTENTS);
+ expect_write(ino, bufsize0, bufsize1, bufsize1, contents1);
+
+ fd = open(FULLPATH, O_WRONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
+ leak(fd);
+}
+
+/* It is an error if the daemon claims to have written more data than we sent */
+TEST_F(Write, indirect_io_long_write)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefghijklmnop";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+ ssize_t bufsize_out = 100;
+ off_t some_other_size = 25;
+ struct stat sb;
+
+ expect_lookup(RELPATH, ino, 0);
+ expect_open(ino, 0, 1);
+ expect_write(ino, 0, bufsize, bufsize_out, CONTENTS);
+ expect_getattr(ino, some_other_size);
+
+ fd = open(FULLPATH, O_WRONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(-1, write(fd, CONTENTS, bufsize)) << strerror(errno);
+ ASSERT_EQ(EINVAL, errno);
+
+ /*
+ * Following such an error, we should requery the server for the file's
+ * size.
+ */
+ fstat(fd, &sb);
+ ASSERT_EQ(sb.st_size, some_other_size);
+
+ leak(fd);
+}
+
+/*
+ * Don't crash if the server returns a write that can't be represented as a
+ * signed 32 bit number. Regression test for
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=263263
+ */
+TEST_F(Write, indirect_io_very_long_write)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefghijklmnop";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+ ssize_t bufsize_out = 3 << 30;
+
+ expect_lookup(RELPATH, ino, 0);
+ expect_open(ino, 0, 1);
+ expect_write(ino, 0, bufsize, bufsize_out, CONTENTS);
+
+ fd = open(FULLPATH, O_WRONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(-1, write(fd, CONTENTS, bufsize)) << strerror(errno);
+ ASSERT_EQ(EINVAL, errno);
+ leak(fd);
+}
+
+/*
+ * When the direct_io option is used, filesystems are allowed to write less
+ * data than requested. We should return the short write to userland.
+ */
+TEST_F(Write, direct_io_short_write)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefghijklmnop";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+ ssize_t halfbufsize = bufsize / 2;
+
+ expect_lookup(RELPATH, ino, 0);
+ expect_open(ino, FOPEN_DIRECT_IO, 1);
+ expect_write(ino, 0, bufsize, halfbufsize, CONTENTS);
+
+ fd = open(FULLPATH, O_WRONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(halfbufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
+ leak(fd);
+}
+
+/*
+ * An insidious edge case: the filesystem returns a short write, and the
+ * difference between what we requested and what it actually wrote crosses an
+ * iov element boundary
+ */
+TEST_F(Write, direct_io_short_write_iov)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS0 = "abcdefgh";
+ const char *CONTENTS1 = "ijklmnop";
+ const char *EXPECTED0 = "abcdefghijklmnop";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t size0 = strlen(CONTENTS0) - 1;
+ ssize_t size1 = strlen(CONTENTS1) + 1;
+ ssize_t totalsize = size0 + size1;
+ struct iovec iov[2];
+
+ expect_lookup(RELPATH, ino, 0);
+ expect_open(ino, FOPEN_DIRECT_IO, 1);
+ expect_write(ino, 0, totalsize, size0, EXPECTED0);
+
+ fd = open(FULLPATH, O_WRONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ iov[0].iov_base = __DECONST(void*, CONTENTS0);
+ iov[0].iov_len = strlen(CONTENTS0);
+ iov[1].iov_base = __DECONST(void*, CONTENTS1);
+ iov[1].iov_len = strlen(CONTENTS1);
+ ASSERT_EQ(size0, writev(fd, iov, 2)) << strerror(errno);
+ leak(fd);
+}
+
+/* fusefs should respect RLIMIT_FSIZE */
+TEST_P(WriteRlimitFsize, rlimit_fsize)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ struct rlimit rl;
+ ssize_t bufsize = strlen(CONTENTS);
+ off_t offset = 1'000'000'000;
+ uint64_t ino = 42;
+ int fd, oflag;
+
+ oflag = GetParam();
+
+ expect_lookup(RELPATH, ino, 0);
+ expect_open(ino, 0, 1);
+
+ rl.rlim_cur = offset;
+ rl.rlim_max = m_initial_limit.rlim_max;
+ ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno);
+ ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno);
+
+ fd = open(FULLPATH, O_WRONLY | oflag);
+
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(-1, pwrite(fd, CONTENTS, bufsize, offset));
+ EXPECT_EQ(EFBIG, errno);
+ EXPECT_EQ(1, s_sigxfsz);
+ leak(fd);
+}
+
+/*
+ * When crossing the RLIMIT_FSIZE boundary, writes should be truncated, not
+ * aborted.
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=164793
+ */
+TEST_P(WriteRlimitFsize, rlimit_fsize_truncate)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefghijklmnopqrstuvwxyz";
+ struct rlimit rl;
+ ssize_t bufsize = strlen(CONTENTS);
+ uint64_t ino = 42;
+ off_t offset = 1 << 30;
+ off_t limit = offset + strlen(CONTENTS) / 2;
+ int fd, oflag;
+
+ oflag = GetParam();
+
+ expect_lookup(RELPATH, ino, 0);
+ expect_open(ino, 0, 1);
+ expect_write(ino, offset, bufsize / 2, bufsize / 2, CONTENTS);
+
+ rl.rlim_cur = limit;
+ rl.rlim_max = m_initial_limit.rlim_max;
+ ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno);
+ ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno);
+
+ fd = open(FULLPATH, O_WRONLY | oflag);
+
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(bufsize / 2, pwrite(fd, CONTENTS, bufsize, offset))
+ << strerror(errno);
+ leak(fd);
+}
+
+INSTANTIATE_TEST_SUITE_P(W, WriteRlimitFsize,
+ Values(0, O_DIRECT)
+);
+
+/*
+ * A short read indicates EOF. Test that nothing bad happens if we get EOF
+ * during the R of a RMW operation.
+ */
+TEST_F(Write, eof_during_rmw)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ const char *INITIAL = "XXXXXXXXXX";
+ uint64_t ino = 42;
+ uint64_t offset = 1;
+ ssize_t bufsize = strlen(CONTENTS) + 1;
+ off_t orig_fsize = 10;
+ off_t truncated_fsize = 5;
+ int fd;
+
+ FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, orig_fsize, 1);
+ expect_open(ino, 0, 1);
+ expect_read(ino, 0, orig_fsize, truncated_fsize, INITIAL, O_RDWR);
+ maybe_expect_write(ino, offset, bufsize, CONTENTS);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
+ << strerror(errno);
+ leak(fd);
+}
+
+/*
+ * VOP_STRATEGY should not query the server for the file's size, even if its
+ * cached attributes have expired.
+ * Regression test for https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=256937
+ */
+TEST_P(WriteEofDuringVnopStrategy, eof_during_vop_strategy)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ Sequence seq;
+ const off_t filesize = 2 * m_maxbcachebuf;
+ char *contents;
+ uint64_t ino = 42;
+ uint64_t attr_valid = 0;
+ uint64_t attr_valid_nsec = 0;
+ mode_t mode = S_IFREG | 0644;
+ int fd;
+ int ngetattrs;
+
+ ngetattrs = GetParam();
+ contents = new char[filesize]();
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillRepeatedly(Invoke(
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.nlink = 1;
+ out.body.entry.attr.size = filesize;
+ out.body.entry.attr_valid = attr_valid;
+ out.body.entry.attr_valid_nsec = attr_valid_nsec;
+ })));
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).Times(Between(ngetattrs - 1, ngetattrs))
+ .InSequence(seq)
+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino;
+ out.body.attr.attr.mode = mode;
+ out.body.attr.attr_valid = attr_valid;
+ out.body.attr.attr_valid_nsec = attr_valid_nsec;
+ out.body.attr.attr.size = filesize;
+ })));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).InSequence(seq)
+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino;
+ out.body.attr.attr.mode = mode;
+ out.body.attr.attr_valid = attr_valid;
+ out.body.attr.attr_valid_nsec = attr_valid_nsec;
+ out.body.attr.attr.size = filesize / 2;
+ })));
+ expect_write(ino, 0, filesize / 2, filesize / 2, contents);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(filesize / 2, write(fd, contents, filesize / 2))
+ << strerror(errno);
+
+}
+
+INSTANTIATE_TEST_SUITE_P(W, WriteEofDuringVnopStrategy,
+ Values(1, 2, 3)
+);
+
+/*
+ * If the kernel cannot be sure which uid, gid, or pid was responsible for a
+ * write, then it must set the FUSE_WRITE_CACHE bit
+ */
+/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236378 */
+TEST_F(Write, mmap)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+ void *p;
+ uint64_t offset = 10;
+ size_t len;
+ char *zeros, *expected;
+
+ len = getpagesize();
+
+ zeros = new char[len]();
+ expected = new char[len]();
+ memmove((uint8_t*)expected + offset, CONTENTS, bufsize);
+
+ expect_lookup(RELPATH, ino, len);
+ expect_open(ino, 0, 1);
+ expect_read(ino, 0, len, len, zeros);
+ /*
+ * Writes from the pager may or may not be associated with the correct
+ * pid, so they must set FUSE_WRITE_CACHE.
+ */
+ FuseTest::expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, 0, expected);
+ expect_flush(ino, 1, ReturnErrno(0));
+ expect_release(ino, ReturnErrno(0));
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ ASSERT_NE(MAP_FAILED, p) << strerror(errno);
+
+ memmove((uint8_t*)p + offset, CONTENTS, bufsize);
+
+ ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
+ close(fd); // Write mmap'd data on close
+
+ delete[] expected;
+ delete[] zeros;
+
+ leak(fd);
+}
+
+TEST_F(Write, pwrite)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ uint64_t offset = m_maxbcachebuf;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+
+ expect_lookup(RELPATH, ino, 0);
+ expect_open(ino, 0, 1);
+ expect_write(ino, offset, bufsize, bufsize, CONTENTS);
+
+ fd = open(FULLPATH, O_WRONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
+ << strerror(errno);
+ leak(fd);
+}
+
+/* Writing a file should update its cached mtime and ctime */
+TEST_F(Write, timestamps)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ ssize_t bufsize = strlen(CONTENTS);
+ uint64_t ino = 42;
+ struct stat sb0, sb1;
+ int fd;
+
+ expect_lookup(RELPATH, ino, 0);
+ expect_open(ino, 0, 1);
+ maybe_expect_write(ino, 0, bufsize, CONTENTS);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(0, fstat(fd, &sb0)) << strerror(errno);
+ ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
+
+ nap();
+
+ ASSERT_EQ(0, fstat(fd, &sb1)) << strerror(errno);
+
+ EXPECT_EQ(sb0.st_atime, sb1.st_atime);
+ EXPECT_NE(sb0.st_mtime, sb1.st_mtime);
+ EXPECT_NE(sb0.st_ctime, sb1.st_ctime);
+
+ leak(fd);
+}
+
+TEST_F(Write, write)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+
+ expect_lookup(RELPATH, ino, 0);
+ expect_open(ino, 0, 1);
+ expect_write(ino, 0, bufsize, bufsize, CONTENTS);
+
+ fd = open(FULLPATH, O_WRONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
+ leak(fd);
+}
+
+/* fuse(4) should not issue writes of greater size than the daemon requests */
+TEST_F(WriteMaxWrite, write)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ int *contents;
+ uint64_t ino = 42;
+ int fd;
+ ssize_t halfbufsize, bufsize;
+
+ halfbufsize = m_mock->m_maxwrite;
+ if (halfbufsize >= m_maxbcachebuf ||
+ (unsigned long )halfbufsize >= m_maxphys)
+ GTEST_SKIP() << "Must lower m_maxwrite for this test";
+ bufsize = halfbufsize * 2;
+ contents = new int[bufsize / sizeof(int)];
+ for (int i = 0; i < (int)bufsize / (int)sizeof(i); i++) {
+ contents[i] = i;
+ }
+
+ expect_lookup(RELPATH, ino, 0);
+ expect_open(ino, 0, 1);
+ maybe_expect_write(ino, 0, halfbufsize, contents);
+ maybe_expect_write(ino, halfbufsize, halfbufsize,
+ &contents[halfbufsize / sizeof(int)]);
+
+ fd = open(FULLPATH, O_WRONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(bufsize, write(fd, contents, bufsize)) << strerror(errno);
+ leak(fd);
+
+ delete[] contents;
+}
+
+TEST_F(Write, write_nothing)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = 0;
+
+ expect_lookup(RELPATH, ino, 0);
+ expect_open(ino, 0, 1);
+
+ fd = open(FULLPATH, O_WRONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
+ leak(fd);
+}
+
+TEST_F(Write_7_8, write)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+
+ expect_lookup(RELPATH, ino, 0);
+ expect_open(ino, 0, 1);
+ expect_write_7_8(ino, 0, bufsize, bufsize, CONTENTS);
+
+ fd = open(FULLPATH, O_WRONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
+ leak(fd);
+}
+
+/* In writeback mode, dirty data should be written on close */
+TEST_F(WriteBackAsync, close)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+
+ expect_lookup(RELPATH, ino, 0);
+ expect_open(ino, 0, 1);
+ expect_write(ino, 0, bufsize, bufsize, CONTENTS);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_SETATTR);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino; // Must match nodeid
+ })));
+ expect_flush(ino, 1, ReturnErrno(0));
+ expect_release(ino, ReturnErrno(0));
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
+ close(fd);
+}
+
+/* In writeback mode, adjacent writes will be clustered together */
+TEST_F(WriteCluster, clustering)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int i, fd;
+ char *wbuf, *wbuf2x;
+ ssize_t bufsize = m_maxbcachebuf;
+ off_t filesize = 5 * bufsize;
+
+ wbuf = new char[bufsize];
+ memset(wbuf, 'X', bufsize);
+ wbuf2x = new char[2 * bufsize];
+ memset(wbuf2x, 'X', 2 * bufsize);
+
+ expect_lookup(RELPATH, ino, filesize);
+ expect_open(ino, 0, 1);
+ /*
+ * Writes of bufsize-bytes each should be clustered into greater sizes.
+ * The amount of clustering is adaptive, so the first write actually
+ * issued will be 2x bufsize and subsequent writes may be larger
+ */
+ expect_write(ino, 0, 2 * bufsize, 2 * bufsize, wbuf2x);
+ expect_write(ino, 2 * bufsize, 2 * bufsize, 2 * bufsize, wbuf2x);
+ expect_flush(ino, 1, ReturnErrno(0));
+ expect_release(ino, ReturnErrno(0));
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ for (i = 0; i < 4; i++) {
+ ASSERT_EQ(bufsize, write(fd, wbuf, bufsize))
+ << strerror(errno);
+ }
+ close(fd);
+ delete[] wbuf2x;
+ delete[] wbuf;
+}
+
+/*
+ * When clustering writes, an I/O error to any of the cluster's children should
+ * not panic the system on unmount
+ */
+/*
+ * Regression test for bug 238585
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=238565
+ */
+TEST_F(WriteCluster, cluster_write_err)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int i, fd;
+ char *wbuf;
+ ssize_t bufsize = m_maxbcachebuf;
+ off_t filesize = 4 * bufsize;
+
+ wbuf = new char[bufsize];
+ memset(wbuf, 'X', bufsize);
+
+ expect_lookup(RELPATH, ino, filesize);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_WRITE);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnErrno(EIO)));
+ expect_flush(ino, 1, ReturnErrno(0));
+ expect_release(ino, ReturnErrno(0));
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ for (i = 0; i < 3; i++) {
+ ASSERT_EQ(bufsize, write(fd, wbuf, bufsize))
+ << strerror(errno);
+ }
+ close(fd);
+ delete[] wbuf;
+}
+
+/*
+ * In writeback mode, writes to an O_WRONLY file could trigger reads from the
+ * server. The FUSE protocol explicitly allows that.
+ */
+TEST_F(WriteBack, rmw)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ const char *INITIAL = "XXXXXXXXXX";
+ uint64_t ino = 42;
+ uint64_t offset = 1;
+ off_t fsize = 10;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+
+ FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
+ expect_open(ino, 0, 1);
+ expect_read(ino, 0, fsize, fsize, INITIAL, O_WRONLY);
+ maybe_expect_write(ino, offset, bufsize, CONTENTS);
+
+ fd = open(FULLPATH, O_WRONLY);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
+ << strerror(errno);
+ leak(fd);
+}
+
+/*
+ * Without direct_io, writes should be committed to cache
+ */
+TEST_F(WriteBack, cache)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+ uint8_t readbuf[bufsize];
+
+ expect_lookup(RELPATH, ino, 0);
+ expect_open(ino, 0, 1);
+ expect_write(ino, 0, bufsize, bufsize, CONTENTS);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
+ /*
+ * A subsequent read should be serviced by cache, without querying the
+ * filesystem daemon
+ */
+ ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
+ ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
+ leak(fd);
+}
+
+/*
+ * With O_DIRECT, writes should be not committed to cache. Admittedly this is
+ * an odd test, because it would be unusual to use O_DIRECT for writes but not
+ * reads.
+ */
+TEST_F(WriteBack, o_direct)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+ uint8_t readbuf[bufsize];
+
+ expect_lookup(RELPATH, ino, 0);
+ expect_open(ino, 0, 1);
+ FuseTest::expect_write(ino, 0, bufsize, bufsize, 0, FUSE_WRITE_CACHE,
+ CONTENTS);
+ expect_read(ino, 0, bufsize, bufsize, CONTENTS);
+
+ fd = open(FULLPATH, O_RDWR | O_DIRECT);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
+ /* A subsequent read must query the daemon because cache is empty */
+ ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
+ ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
+ ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
+ leak(fd);
+}
+
+TEST_F(WriteBack, direct_io)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+ uint8_t readbuf[bufsize];
+
+ expect_lookup(RELPATH, ino, 0);
+ expect_open(ino, FOPEN_DIRECT_IO, 1);
+ FuseTest::expect_write(ino, 0, bufsize, bufsize, 0, FUSE_WRITE_CACHE,
+ CONTENTS);
+ expect_read(ino, 0, bufsize, bufsize, CONTENTS);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
+ /* A subsequent read must query the daemon because cache is empty */
+ ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
+ ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
+ ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
+ leak(fd);
+}
+
+/*
+ * mmap should still be possible even if the server used direct_io. Mmap will
+ * still use the cache, though.
+ *
+ * Regression test for bug 247276
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=247276
+ */
+TEST_F(WriteBack, mmap_direct_io)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ int fd;
+ size_t len;
+ ssize_t bufsize = strlen(CONTENTS);
+ char *zeros;
+ void *p;
+
+ len = getpagesize();
+ zeros = new char[len]();
+
+ expect_lookup(RELPATH, ino, len);
+ expect_open(ino, FOPEN_DIRECT_IO, 1);
+ expect_read(ino, 0, len, len, zeros);
+ expect_flush(ino, 1, ReturnErrno(0));
+ FuseTest::expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, 0, zeros);
+ expect_release(ino, ReturnErrno(0));
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ ASSERT_NE(MAP_FAILED, p) << strerror(errno);
+
+ memmove((uint8_t*)p, CONTENTS, bufsize);
+
+ ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
+ close(fd); // Write mmap'd data on close
+
+ delete[] zeros;
+}
+
+/*
+ * When mounted with -o async, the writeback cache mode should delay writes
+ */
+TEST_F(WriteBackAsync, delay)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+
+ expect_lookup(RELPATH, ino, 0);
+ expect_open(ino, 0, 1);
+ /* Write should be cached, but FUSE_WRITE shouldn't be sent */
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_WRITE);
+ }, Eq(true)),
+ _)
+ ).Times(0);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
+
+ /* Don't close the file because that would flush the cache */
+ leak(fd);
+}
+
+/*
+ * A direct write should not evict dirty cached data from outside of its own
+ * byte range.
+ */
+TEST_F(WriteBackAsync, direct_io_ignores_unrelated_cached)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char CONTENTS0[] = "abcdefgh";
+ const char CONTENTS1[] = "ijklmnop";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS0) + 1;
+ ssize_t fsize = 2 * m_maxbcachebuf;
+ char readbuf[bufsize];
+ char *zeros;
+
+ zeros = new char[m_maxbcachebuf]();
+
+ expect_lookup(RELPATH, ino, fsize);
+ expect_open(ino, 0, 1);
+ expect_read(ino, 0, m_maxbcachebuf, m_maxbcachebuf, zeros);
+ FuseTest::expect_write(ino, m_maxbcachebuf, bufsize, bufsize, 0, 0,
+ CONTENTS1);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ // Cache first block with dirty data. This will entail first reading
+ // the existing data.
+ ASSERT_EQ(bufsize, pwrite(fd, CONTENTS0, bufsize, 0))
+ << strerror(errno);
+
+ // Write directly to second block
+ ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
+ ASSERT_EQ(bufsize, pwrite(fd, CONTENTS1, bufsize, m_maxbcachebuf))
+ << strerror(errno);
+
+ // Read from the first block again. Should be serviced by cache.
+ ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
+ ASSERT_EQ(bufsize, pread(fd, readbuf, bufsize, 0)) << strerror(errno);
+ ASSERT_STREQ(readbuf, CONTENTS0);
+
+ leak(fd);
+ delete[] zeros;
+}
+
+/*
+ * If a direct io write partially overlaps one or two blocks of dirty cached
+ * data, No dirty data should be lost. Admittedly this is a weird test,
+ * because it would be unusual to use O_DIRECT and the writeback cache.
+ */
+TEST_F(WriteBackAsync, direct_io_partially_overlaps_cached_block)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ int fd;
+ off_t bs = m_maxbcachebuf;
+ ssize_t fsize = 3 * bs;
+ char *readbuf, *zeros, *ones, *zeroones, *onezeros;
+
+ readbuf = new char[bs];
+ zeros = new char[3 * bs]();
+ ones = new char[2 * bs];
+ memset(ones, 1, 2 * bs);
+ zeroones = new char[bs]();
+ memset((uint8_t*)zeroones + bs / 2, 1, bs / 2);
+ onezeros = new char[bs]();
+ memset(onezeros, 1, bs / 2);
+
+ expect_lookup(RELPATH, ino, fsize);
+ expect_open(ino, 0, 1);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ /* Cache first and third blocks with dirty data. */
+ ASSERT_EQ(3 * bs, pwrite(fd, zeros, 3 * bs, 0)) << strerror(errno);
+
+ /*
+ * Write directly to all three blocks. The partially written blocks
+ * will be flushed because they're dirty.
+ */
+ FuseTest::expect_write(ino, 0, bs, bs, 0, 0, zeros);
+ FuseTest::expect_write(ino, 2 * bs, bs, bs, 0, 0, zeros);
+ /* The direct write is split in two because of the m_maxwrite value */
+ FuseTest::expect_write(ino, bs / 2, bs, bs, 0, 0, ones);
+ FuseTest::expect_write(ino, 3 * bs / 2, bs, bs, 0, 0, ones);
+ ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
+ ASSERT_EQ(2 * bs, pwrite(fd, ones, 2 * bs, bs / 2)) << strerror(errno);
+
+ /*
+ * Read from both the valid and invalid portions of the first and third
+ * blocks again. This will entail FUSE_READ operations because these
+ * blocks were invalidated by the direct write.
+ */
+ expect_read(ino, 0, bs, bs, zeroones);
+ expect_read(ino, 2 * bs, bs, bs, onezeros);
+ ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
+ ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 0)) << strerror(errno);
+ EXPECT_EQ(0, memcmp(zeros, readbuf, bs / 2));
+ ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 5 * bs / 2))
+ << strerror(errno);
+ EXPECT_EQ(0, memcmp(zeros, readbuf, bs / 2));
+ ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, bs / 2))
+ << strerror(errno);
+ EXPECT_EQ(0, memcmp(ones, readbuf, bs / 2));
+ ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 2 * bs))
+ << strerror(errno);
+ EXPECT_EQ(0, memcmp(ones, readbuf, bs / 2));
+
+ leak(fd);
+ delete[] zeroones;
+ delete[] onezeros;
+ delete[] ones;
+ delete[] zeros;
+ delete[] readbuf;
+}
+
+/*
+ * In WriteBack mode, writes may be cached beyond what the server thinks is the
+ * EOF. In this case, a short read at EOF should _not_ cause fusefs to update
+ * the file's size.
+ */
+TEST_F(WriteBackAsync, eof)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS0 = "abcdefgh";
+ const char *CONTENTS1 = "ijklmnop";
+ uint64_t ino = 42;
+ int fd;
+ off_t offset = m_maxbcachebuf;
+ ssize_t wbufsize = strlen(CONTENTS1);
+ off_t old_filesize = (off_t)strlen(CONTENTS0);
+ ssize_t rbufsize = 2 * old_filesize;
+ char readbuf[rbufsize];
+ size_t holesize = rbufsize - old_filesize;
+ char hole[holesize];
+ struct stat sb;
+ ssize_t r;
+
+ expect_lookup(RELPATH, ino, 0);
+ expect_open(ino, 0, 1);
+ expect_read(ino, 0, m_maxbcachebuf, old_filesize, CONTENTS0);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ /* Write and cache data beyond EOF */
+ ASSERT_EQ(wbufsize, pwrite(fd, CONTENTS1, wbufsize, offset))
+ << strerror(errno);
+
+ /* Read from the old EOF */
+ r = pread(fd, readbuf, rbufsize, 0);
+ ASSERT_LE(0, r) << strerror(errno);
+ EXPECT_EQ(rbufsize, r) << "read should've synthesized a hole";
+ EXPECT_EQ(0, memcmp(CONTENTS0, readbuf, old_filesize));
+ bzero(hole, holesize);
+ EXPECT_EQ(0, memcmp(hole, readbuf + old_filesize, holesize));
+
+ /* The file's size should still be what was established by pwrite */
+ ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
+ EXPECT_EQ(offset + wbufsize, sb.st_size);
+ leak(fd);
+}
+
+/*
+ * When a file has dirty writes that haven't been flushed, the server's notion
+ * of its mtime and ctime will be wrong. The kernel should ignore those if it
+ * gets them from a FUSE_GETATTR before flushing.
+ */
+TEST_F(WriteBackAsync, timestamps)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ ssize_t bufsize = strlen(CONTENTS);
+ uint64_t ino = 42;
+ uint64_t attr_valid = 0;
+ uint64_t attr_valid_nsec = 0;
+ uint64_t server_time = 12345;
+ mode_t mode = S_IFREG | 0644;
+ int fd;
+
+ struct stat sb;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillRepeatedly(Invoke(
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.nlink = 1;
+ out.body.entry.attr_valid = attr_valid;
+ out.body.entry.attr_valid_nsec = attr_valid_nsec;
+ })));
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(
+ ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino;
+ out.body.attr.attr.mode = mode;
+ out.body.attr.attr_valid = attr_valid;
+ out.body.attr.attr_valid_nsec = attr_valid_nsec;
+ out.body.attr.attr.atime = server_time;
+ out.body.attr.attr.mtime = server_time;
+ out.body.attr.attr.ctime = server_time;
+ })));
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
+
+ ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
+ EXPECT_EQ((time_t)server_time, sb.st_atime);
+ EXPECT_NE((time_t)server_time, sb.st_mtime);
+ EXPECT_NE((time_t)server_time, sb.st_ctime);
+
+ leak(fd);
+}
+
+/* Any dirty timestamp fields should be flushed during a SETATTR */
+TEST_F(WriteBackAsync, timestamps_during_setattr)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ ssize_t bufsize = strlen(CONTENTS);
+ uint64_t ino = 42;
+ const mode_t newmode = 0755;
+ int fd;
+
+ expect_lookup(RELPATH, ino, 0);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ uint32_t valid = FATTR_MODE | FATTR_MTIME | FATTR_CTIME;
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino &&
+ in.body.setattr.valid == valid);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino;
+ out.body.attr.attr.mode = S_IFREG | newmode;
+ })));
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
+ ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno);
+
+ leak(fd);
+}
+
+/* fuse_init_out.time_gran controls the granularity of timestamps */
+TEST_P(TimeGran, timestamps_during_setattr)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ ssize_t bufsize = strlen(CONTENTS);
+ uint64_t ino = 42;
+ const mode_t newmode = 0755;
+ int fd;
+
+ expect_lookup(RELPATH, ino, 0);
+ expect_open(ino, 0, 1);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ uint32_t valid = FATTR_MODE | FATTR_MTIME | FATTR_CTIME;
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino &&
+ in.body.setattr.valid == valid &&
+ in.body.setattr.mtimensec % m_time_gran == 0 &&
+ in.body.setattr.ctimensec % m_time_gran == 0);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino;
+ out.body.attr.attr.mode = S_IFREG | newmode;
+ })));
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
+ ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno);
+
+ leak(fd);
+}
+
+INSTANTIATE_TEST_SUITE_P(RA, TimeGran, Range(0u, 10u));
+
+/*
+ * Without direct_io, writes should be committed to cache
+ */
+TEST_F(Write, writethrough)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+ uint8_t readbuf[bufsize];
+
+ expect_lookup(RELPATH, ino, 0);
+ expect_open(ino, 0, 1);
+ expect_write(ino, 0, bufsize, bufsize, CONTENTS);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
+ /*
+ * A subsequent read should be serviced by cache, without querying the
+ * filesystem daemon
+ */
+ ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
+ ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
+ leak(fd);
+}
+
+/* Writes that extend a file should update the cached file size */
+TEST_F(Write, update_file_size)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ struct stat sb;
+ uint64_t ino = 42;
+ int fd;
+ ssize_t bufsize = strlen(CONTENTS);
+
+ expect_lookup(RELPATH, ino, 0);
+ expect_open(ino, 0, 1);
+ expect_write(ino, 0, bufsize, bufsize, CONTENTS);
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
+ /* Get cached attributes */
+ ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
+ ASSERT_EQ(bufsize, sb.st_size);
+ leak(fd);
+}
diff --git a/tests/sys/fs/fusefs/xattr.cc b/tests/sys/fs/fusefs/xattr.cc
new file mode 100644
index 000000000000..0ab203c96254
--- /dev/null
+++ b/tests/sys/fs/fusefs/xattr.cc
@@ -0,0 +1,891 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 The FreeBSD Foundation
+ *
+ * This software was developed by BFF Storage Systems, LLC under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Tests for all things relating to extended attributes and FUSE */
+
+extern "C" {
+#include <sys/types.h>
+#include <sys/extattr.h>
+#include <sys/wait.h>
+#include <semaphore.h>
+#include <signal.h>
+#include <string.h>
+}
+
+#include "mockfs.hh"
+#include "utils.hh"
+
+using namespace testing;
+
+const char FULLPATH[] = "mountpoint/some_file.txt";
+const char RELPATH[] = "some_file.txt";
+static sem_t killer_semaphore;
+
+void* killer(void* target) {
+ pid_t pid = *(pid_t*)target;
+ sem_wait(&killer_semaphore);
+ if (verbosity > 1)
+ printf("Killing! pid %d\n", pid);
+ kill(pid, SIGINT);
+
+ return(NULL);
+}
+
+class Xattr: public FuseTest {
+public:
+void expect_listxattr(uint64_t ino, uint32_t size, ProcessMockerT r,
+ Sequence *seq = NULL)
+{
+ if (seq == NULL) {
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_LISTXATTR &&
+ in.header.nodeid == ino &&
+ in.body.listxattr.size == size);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(r))
+ .RetiresOnSaturation();
+ } else {
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_LISTXATTR &&
+ in.header.nodeid == ino &&
+ in.body.listxattr.size == size);
+ }, Eq(true)),
+ _)
+ ).InSequence(*seq)
+ .WillOnce(Invoke(r))
+ .RetiresOnSaturation();
+ }
+}
+
+void expect_removexattr(uint64_t ino, const char *attr, int error)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *a = (const char*)in.body.bytes;
+ return (in.header.opcode == FUSE_REMOVEXATTR &&
+ in.header.nodeid == ino &&
+ 0 == strcmp(attr, a));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnErrno(error)));
+}
+
+void expect_setxattr(uint64_t ino, const char *attr, const char *value,
+ ProcessMockerT r)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *a = (const char*)in.body.bytes +
+ sizeof(fuse_setxattr_in);
+ const char *v = a + strlen(a) + 1;
+ return (in.header.opcode == FUSE_SETXATTR &&
+ in.header.nodeid == ino &&
+ in.body.setxattr.size == (strlen(value) + 1) &&
+ in.body.setxattr.setxattr_flags == 0 &&
+ 0 == strcmp(attr, a) &&
+ 0 == strcmp(value, v));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(r));
+}
+
+};
+
+class Xattr_7_32:public FuseTest {
+public:
+virtual void SetUp()
+{
+ m_kernel_minor_version = 32;
+ FuseTest::SetUp();
+}
+
+void expect_setxattr_7_32(uint64_t ino, const char *attr, const char *value,
+ ProcessMockerT r)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *a = (const char *)in.body.bytes +
+ FUSE_COMPAT_SETXATTR_IN_SIZE;
+ const char *v = a + strlen(a) + 1;
+ return (in.header.opcode == FUSE_SETXATTR &&
+ in.header.nodeid == ino &&
+ in.body.setxattr.size == (strlen(value) + 1) &&
+ 0 == strcmp(attr, a) &&
+ 0 == strcmp(value, v));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(r));
+}
+};
+
+class Getxattr: public Xattr {};
+
+class Listxattr: public Xattr {};
+
+/* Listxattr tests that need to use a signal */
+class ListxattrSig: public Listxattr {
+public:
+pthread_t m_killer_th;
+pid_t m_child;
+
+void SetUp() {
+ /*
+ * Mount with -o nointr so the mount can't get interrupted while
+ * waiting for a response from the server
+ */
+ m_nointr = true;
+ FuseTest::SetUp();
+
+ ASSERT_EQ(0, sem_init(&killer_semaphore, 0, 0)) << strerror(errno);
+}
+
+void TearDown() {
+ if (m_killer_th != NULL) {
+ pthread_join(m_killer_th, NULL);
+ }
+
+ sem_destroy(&killer_semaphore);
+
+ FuseTest::TearDown();
+}
+};
+
+class Removexattr: public Xattr {};
+class Setxattr: public Xattr {};
+class Setxattr_7_32:public Xattr_7_32 {};
+class RofsXattr: public Xattr {
+public:
+virtual void SetUp() {
+ m_ro = true;
+ Xattr::SetUp();
+}
+};
+
+/*
+ * If the extended attribute does not exist on this file, the daemon should
+ * return ENOATTR (ENODATA on Linux, but it's up to the daemon to choose the
+ * correct errror code)
+ */
+TEST_F(Getxattr, enoattr)
+{
+ char data[80];
+ uint64_t ino = 42;
+ int ns = EXTATTR_NAMESPACE_USER;
+ ssize_t r;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ expect_getxattr(ino, "user.foo", ReturnErrno(ENOATTR));
+
+ r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data));
+ ASSERT_EQ(-1, r);
+ ASSERT_EQ(ENOATTR, errno);
+}
+
+/*
+ * If the filesystem returns ENOSYS, then it will be treated as a permanent
+ * failure and all future VOP_GETEXTATTR calls will fail with EOPNOTSUPP
+ * without querying the filesystem daemon
+ */
+TEST_F(Getxattr, enosys)
+{
+ char data[80];
+ uint64_t ino = 42;
+ int ns = EXTATTR_NAMESPACE_USER;
+ ssize_t r;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2);
+ expect_getxattr(ino, "user.foo", ReturnErrno(ENOSYS));
+
+ r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data));
+ ASSERT_EQ(-1, r);
+ EXPECT_EQ(EOPNOTSUPP, errno);
+
+ /* Subsequent attempts should not query the filesystem at all */
+ r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data));
+ ASSERT_EQ(-1, r);
+ EXPECT_EQ(EOPNOTSUPP, errno);
+}
+
+/*
+ * On FreeBSD, if the user passes an insufficiently large buffer then the
+ * filesystem is supposed to copy as much of the attribute's value as will fit.
+ *
+ * On Linux, however, the filesystem is supposed to return ERANGE.
+ *
+ * libfuse specifies the Linux behavior. However, that's probably an error.
+ * It would probably be correct for the filesystem to use platform-dependent
+ * behavior.
+ *
+ * This test case covers a filesystem that uses the Linux behavior
+ * TODO: require FreeBSD Behavior.
+ */
+TEST_F(Getxattr, erange)
+{
+ char data[10];
+ uint64_t ino = 42;
+ int ns = EXTATTR_NAMESPACE_USER;
+ ssize_t r;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ expect_getxattr(ino, "user.foo", ReturnErrno(ERANGE));
+
+ r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data));
+ ASSERT_EQ(-1, r);
+ ASSERT_EQ(ERANGE, errno);
+}
+
+/*
+ * If the user passes a 0-length buffer, then the daemon should just return the
+ * size of the attribute
+ */
+TEST_F(Getxattr, size_only)
+{
+ uint64_t ino = 42;
+ int ns = EXTATTR_NAMESPACE_USER;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ expect_getxattr(ino, "user.foo",
+ ReturnImmediate([](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, getxattr);
+ out.body.getxattr.size = 99;
+ })
+ );
+
+ ASSERT_EQ(99, extattr_get_file(FULLPATH, ns, "foo", NULL, 0))
+ << strerror(errno);;
+}
+
+/*
+ * Successfully get an attribute from the system namespace
+ */
+TEST_F(Getxattr, system)
+{
+ uint64_t ino = 42;
+ char data[80];
+ const char value[] = "whatever";
+ ssize_t value_len = strlen(value) + 1;
+ int ns = EXTATTR_NAMESPACE_SYSTEM;
+ ssize_t r;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ expect_getxattr(ino, "system.foo",
+ ReturnImmediate([&](auto in __unused, auto& out) {
+ memcpy((void*)out.body.bytes, value, value_len);
+ out.header.len = sizeof(out.header) + value_len;
+ })
+ );
+
+ r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data));
+ ASSERT_EQ(value_len, r) << strerror(errno);
+ EXPECT_STREQ(value, data);
+}
+
+/*
+ * Successfully get an attribute from the user namespace
+ */
+TEST_F(Getxattr, user)
+{
+ uint64_t ino = 42;
+ char data[80];
+ const char value[] = "whatever";
+ ssize_t value_len = strlen(value) + 1;
+ int ns = EXTATTR_NAMESPACE_USER;
+ ssize_t r;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ expect_getxattr(ino, "user.foo",
+ ReturnImmediate([&](auto in __unused, auto& out) {
+ memcpy((void*)out.body.bytes, value, value_len);
+ out.header.len = sizeof(out.header) + value_len;
+ })
+ );
+
+ r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data));
+ ASSERT_EQ(value_len, r) << strerror(errno);
+ EXPECT_STREQ(value, data);
+}
+
+/*
+ * If the filesystem returns ENOSYS, then it will be treated as a permanent
+ * failure and all future VOP_LISTEXTATTR calls will fail with EOPNOTSUPP
+ * without querying the filesystem daemon
+ */
+TEST_F(Listxattr, enosys)
+{
+ uint64_t ino = 42;
+ int ns = EXTATTR_NAMESPACE_USER;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2);
+ expect_listxattr(ino, 0, ReturnErrno(ENOSYS));
+
+ ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0));
+ EXPECT_EQ(EOPNOTSUPP, errno);
+
+ /* Subsequent attempts should not query the filesystem at all */
+ ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0));
+ EXPECT_EQ(EOPNOTSUPP, errno);
+}
+
+/*
+ * Listing extended attributes failed because they aren't configured on this
+ * filesystem
+ */
+TEST_F(Listxattr, enotsup)
+{
+ uint64_t ino = 42;
+ int ns = EXTATTR_NAMESPACE_USER;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ expect_listxattr(ino, 0, ReturnErrno(ENOTSUP));
+
+ ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0));
+ ASSERT_EQ(ENOTSUP, errno);
+}
+
+/*
+ * On FreeBSD, if the user passes an insufficiently large buffer to
+ * extattr_list_file(2) or VOP_LISTEXTATTR(9), then the file system is supposed
+ * to copy as much of the attribute's value as will fit.
+ *
+ * On Linux, however, the file system is supposed to return ERANGE if an
+ * insufficiently large buffer is passed to listxattr(2).
+ *
+ * fusefs(4) must guarantee the usual FreeBSD behavior.
+ */
+TEST_F(Listxattr, erange)
+{
+ uint64_t ino = 42;
+ int ns = EXTATTR_NAMESPACE_USER;
+ char attrs[9] = "user.foo";
+ char expected[3] = {3, 'f', 'o'};
+ char buf[3];
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out)
+ {
+ out.body.listxattr.size = sizeof(attrs);
+ SET_OUT_HEADER_LEN(out, listxattr);
+ }));
+ expect_listxattr(ino, sizeof(attrs),
+ ReturnImmediate([&](auto in __unused, auto& out) {
+ memcpy((void*)out.body.bytes, attrs, sizeof(attrs));
+ out.header.len = sizeof(fuse_out_header) + sizeof(attrs);
+ }));
+
+
+ ASSERT_EQ(static_cast<ssize_t>(sizeof(buf)),
+ extattr_list_file(FULLPATH, ns, buf, sizeof(buf)));
+ ASSERT_EQ(0, memcmp(expected, buf, sizeof(buf)));
+}
+
+/*
+ * A buggy or malicious file system always returns ERANGE, even if we pass an
+ * appropriately sized buffer. That will send the kernel into an infinite
+ * loop. This test will ensure that the loop is interruptible by killing the
+ * blocked process with SIGINT.
+ */
+TEST_F(ListxattrSig, erange_forever)
+{
+ uint64_t ino = 42;
+ uint32_t lie_size = 10;
+ int status;
+
+ fork(false, &status, [&] {
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillRepeatedly(Invoke(
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.nlink = 1;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_LISTXATTR &&
+ in.header.nodeid == ino &&
+ in.body.listxattr.size == 0);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(ReturnImmediate([=](auto i __unused, auto& out)
+ {
+ /* The file system requests 10 bytes, but it's a lie */
+ out.body.listxattr.size = lie_size;
+ SET_OUT_HEADER_LEN(out, listxattr);
+ /*
+ * We can send the signal any time after fusefs enters
+ * VOP_LISTEXTATTR
+ */
+ sem_post(&killer_semaphore);
+ }));
+ /*
+ * Even though the kernel faithfully respects our size request,
+ * we'll return ERANGE anyway.
+ */
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_LISTXATTR &&
+ in.header.nodeid == ino &&
+ in.body.listxattr.size == lie_size);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(ReturnErrno(ERANGE));
+
+ ASSERT_EQ(0, pthread_create(&m_killer_th, NULL, killer,
+ &m_mock->m_child_pid))
+ << strerror(errno);
+
+ }, [] {
+ /* Child process will block until it gets signaled */
+ int ns = EXTATTR_NAMESPACE_USER;
+ char buf[3];
+ extattr_list_file(FULLPATH, ns, buf, sizeof(buf));
+ return 0;
+ }
+ );
+
+ ASSERT_TRUE(WIFSIGNALED(status));
+}
+
+/*
+ * Get the size of the list that it would take to list no extended attributes
+ */
+TEST_F(Listxattr, size_only_empty)
+{
+ uint64_t ino = 42;
+ int ns = EXTATTR_NAMESPACE_USER;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out) {
+ out.body.listxattr.size = 0;
+ SET_OUT_HEADER_LEN(out, listxattr);
+ }));
+
+ ASSERT_EQ(0, extattr_list_file(FULLPATH, ns, NULL, 0))
+ << strerror(errno);
+}
+
+/*
+ * Get the size of the list that it would take to list some extended
+ * attributes. Due to the format differences between a FreeBSD and a
+ * Linux/FUSE extended attribute list, fuse(4) will actually allocate a buffer
+ * and get the whole list, then convert it, just to figure out its size.
+ */
+TEST_F(Listxattr, size_only_nonempty)
+{
+ uint64_t ino = 42;
+ int ns = EXTATTR_NAMESPACE_USER;
+ char attrs[9] = "user.foo";
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out)
+ {
+ out.body.listxattr.size = sizeof(attrs);
+ SET_OUT_HEADER_LEN(out, listxattr);
+ }));
+
+ expect_listxattr(ino, sizeof(attrs),
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ size_t l = sizeof(attrs);
+ strlcpy((char*)out.body.bytes, attrs, l);
+ out.header.len = sizeof(fuse_out_header) + l;
+ })
+ );
+
+ ASSERT_EQ(4, extattr_list_file(FULLPATH, ns, NULL, 0))
+ << strerror(errno);
+}
+
+/*
+ * The list of extended attributes grows in between the server's two calls to
+ * FUSE_LISTXATTR.
+ */
+TEST_F(Listxattr, size_only_race_bigger)
+{
+ uint64_t ino = 42;
+ int ns = EXTATTR_NAMESPACE_USER;
+ char attrs0[9] = "user.foo";
+ char attrs1[18] = "user.foo\0user.bar";
+ Sequence seq;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillRepeatedly(Invoke(
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = S_IFREG | 0644;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.nlink = 1;
+ out.body.entry.attr_valid = UINT64_MAX;
+ out.body.entry.entry_valid = UINT64_MAX;
+ })));
+ expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out)
+ {
+ out.body.listxattr.size = sizeof(attrs0);
+ SET_OUT_HEADER_LEN(out, listxattr);
+ }), &seq);
+
+ /*
+ * After the first FUSE_LISTXATTR the list grew, so the second
+ * operation returns ERANGE.
+ */
+ expect_listxattr(ino, sizeof(attrs0), ReturnErrno(ERANGE), &seq);
+
+ /* And now the kernel retries */
+ expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out)
+ {
+ out.body.listxattr.size = sizeof(attrs1);
+ SET_OUT_HEADER_LEN(out, listxattr);
+ }), &seq);
+ expect_listxattr(ino, sizeof(attrs1),
+ ReturnImmediate([&](auto in __unused, auto& out) {
+ memcpy((char*)out.body.bytes, attrs1, sizeof(attrs1));
+ out.header.len = sizeof(fuse_out_header) +
+ sizeof(attrs1);
+ }), &seq
+ );
+
+ /* Userspace should never know about the retry */
+ ASSERT_EQ(8, extattr_list_file(FULLPATH, ns, NULL, 0))
+ << strerror(errno);
+}
+
+/*
+ * The list of extended attributes shrinks in between the server's two calls to
+ * FUSE_LISTXATTR
+ */
+TEST_F(Listxattr, size_only_race_smaller)
+{
+ uint64_t ino = 42;
+ int ns = EXTATTR_NAMESPACE_USER;
+ char attrs0[18] = "user.foo\0user.bar";
+ char attrs1[9] = "user.foo";
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out)
+ {
+ out.body.listxattr.size = sizeof(attrs0);
+ SET_OUT_HEADER_LEN(out, listxattr);
+ }));
+ expect_listxattr(ino, sizeof(attrs0),
+ ReturnImmediate([&](auto in __unused, auto& out) {
+ memcpy((char*)out.body.bytes, attrs1, sizeof(attrs1));
+ out.header.len = sizeof(fuse_out_header) +
+ sizeof(attrs1);
+ })
+ );
+
+ ASSERT_EQ(4, extattr_list_file(FULLPATH, ns, NULL, 0))
+ << strerror(errno);
+}
+
+TEST_F(Listxattr, size_only_really_big)
+{
+ uint64_t ino = 42;
+ int ns = EXTATTR_NAMESPACE_USER;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out) {
+ out.body.listxattr.size = 16000;
+ SET_OUT_HEADER_LEN(out, listxattr);
+ }));
+
+ expect_listxattr(ino, 16000,
+ ReturnImmediate([](auto in __unused, auto& out) {
+ const char l[16] = "user.foobarbang";
+ for (int i=0; i < 1000; i++) {
+ memcpy(&out.body.bytes[16 * i], l, 16);
+ }
+ out.header.len = sizeof(fuse_out_header) + 16000;
+ })
+ );
+
+ ASSERT_EQ(11000, extattr_list_file(FULLPATH, ns, NULL, 0))
+ << strerror(errno);
+}
+
+/*
+ * List all of the user attributes of a file which has both user and system
+ * attributes
+ */
+TEST_F(Listxattr, user)
+{
+ uint64_t ino = 42;
+ int ns = EXTATTR_NAMESPACE_USER;
+ char data[80];
+ char expected[9] = {3, 'f', 'o', 'o', 4, 'b', 'a', 'n', 'g'};
+ char attrs[28] = "user.foo\0system.x\0user.bang";
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ expect_listxattr(ino, 0,
+ ReturnImmediate([&](auto in __unused, auto& out) {
+ out.body.listxattr.size = sizeof(attrs);
+ SET_OUT_HEADER_LEN(out, listxattr);
+ })
+ );
+
+ expect_listxattr(ino, sizeof(attrs),
+ ReturnImmediate([&](auto in __unused, auto& out) {
+ memcpy((void*)out.body.bytes, attrs, sizeof(attrs));
+ out.header.len = sizeof(fuse_out_header) + sizeof(attrs);
+ }));
+
+ ASSERT_EQ(static_cast<ssize_t>(sizeof(expected)),
+ extattr_list_file(FULLPATH, ns, data, sizeof(data)))
+ << strerror(errno);
+ ASSERT_EQ(0, memcmp(expected, data, sizeof(expected)));
+}
+
+/*
+ * List all of the system attributes of a file which has both user and system
+ * attributes
+ */
+TEST_F(Listxattr, system)
+{
+ uint64_t ino = 42;
+ int ns = EXTATTR_NAMESPACE_SYSTEM;
+ char data[80];
+ char expected[2] = {1, 'x'};
+ char attrs[28] = "user.foo\0system.x\0user.bang";
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ expect_listxattr(ino, 0,
+ ReturnImmediate([&](auto in __unused, auto& out) {
+ out.body.listxattr.size = sizeof(attrs);
+ SET_OUT_HEADER_LEN(out, listxattr);
+ })
+ );
+
+ expect_listxattr(ino, sizeof(attrs),
+ ReturnImmediate([&](auto in __unused, auto& out) {
+ memcpy((void*)out.body.bytes, attrs, sizeof(attrs));
+ out.header.len = sizeof(fuse_out_header) + sizeof(attrs);
+ }));
+
+ ASSERT_EQ(static_cast<ssize_t>(sizeof(expected)),
+ extattr_list_file(FULLPATH, ns, data, sizeof(data)))
+ << strerror(errno);
+ ASSERT_EQ(0, memcmp(expected, data, sizeof(expected)));
+}
+
+/* Fail to remove a nonexistent attribute */
+TEST_F(Removexattr, enoattr)
+{
+ uint64_t ino = 42;
+ int ns = EXTATTR_NAMESPACE_USER;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ expect_removexattr(ino, "user.foo", ENOATTR);
+
+ ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo"));
+ ASSERT_EQ(ENOATTR, errno);
+}
+
+/*
+ * If the filesystem returns ENOSYS, then it will be treated as a permanent
+ * failure and all future VOP_DELETEEXTATTR calls will fail with EOPNOTSUPP
+ * without querying the filesystem daemon
+ */
+TEST_F(Removexattr, enosys)
+{
+ uint64_t ino = 42;
+ int ns = EXTATTR_NAMESPACE_USER;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2);
+ expect_removexattr(ino, "user.foo", ENOSYS);
+
+ ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo"));
+ EXPECT_EQ(EOPNOTSUPP, errno);
+
+ /* Subsequent attempts should not query the filesystem at all */
+ ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo"));
+ EXPECT_EQ(EOPNOTSUPP, errno);
+}
+
+/* Successfully remove a user xattr */
+TEST_F(Removexattr, user)
+{
+ uint64_t ino = 42;
+ int ns = EXTATTR_NAMESPACE_USER;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ expect_removexattr(ino, "user.foo", 0);
+
+ ASSERT_EQ(0, extattr_delete_file(FULLPATH, ns, "foo"))
+ << strerror(errno);
+}
+
+/* Successfully remove a system xattr */
+TEST_F(Removexattr, system)
+{
+ uint64_t ino = 42;
+ int ns = EXTATTR_NAMESPACE_SYSTEM;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ expect_removexattr(ino, "system.foo", 0);
+
+ ASSERT_EQ(0, extattr_delete_file(FULLPATH, ns, "foo"))
+ << strerror(errno);
+}
+
+
+/*
+ * If the filesystem returns ENOSYS, then it will be treated as a permanent
+ * failure and all future VOP_SETEXTATTR calls will fail with EOPNOTSUPP
+ * without querying the filesystem daemon
+ */
+TEST_F(Setxattr, enosys)
+{
+ uint64_t ino = 42;
+ const char value[] = "whatever";
+ ssize_t value_len = strlen(value) + 1;
+ int ns = EXTATTR_NAMESPACE_USER;
+ ssize_t r;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2);
+ expect_setxattr(ino, "user.foo", value, ReturnErrno(ENOSYS));
+
+ r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value,
+ value_len);
+ ASSERT_EQ(-1, r);
+ EXPECT_EQ(EOPNOTSUPP, errno);
+
+ /* Subsequent attempts should not query the filesystem at all */
+ r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value,
+ value_len);
+ ASSERT_EQ(-1, r);
+ EXPECT_EQ(EOPNOTSUPP, errno);
+}
+
+/*
+ * SETXATTR will return ENOTSUP if the namespace is invalid or the filesystem
+ * as currently configured doesn't support extended attributes.
+ */
+TEST_F(Setxattr, enotsup)
+{
+ uint64_t ino = 42;
+ const char value[] = "whatever";
+ ssize_t value_len = strlen(value) + 1;
+ int ns = EXTATTR_NAMESPACE_USER;
+ ssize_t r;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ expect_setxattr(ino, "user.foo", value, ReturnErrno(ENOTSUP));
+
+ r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value,
+ value_len);
+ ASSERT_EQ(-1, r);
+ EXPECT_EQ(ENOTSUP, errno);
+}
+
+/*
+ * Successfully set a user attribute.
+ */
+TEST_F(Setxattr, user)
+{
+ uint64_t ino = 42;
+ const char value[] = "whatever";
+ ssize_t value_len = strlen(value) + 1;
+ int ns = EXTATTR_NAMESPACE_USER;
+ ssize_t r;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ expect_setxattr(ino, "user.foo", value, ReturnErrno(0));
+
+ r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value,
+ value_len);
+ ASSERT_EQ(value_len, r) << strerror(errno);
+}
+
+/*
+ * Successfully set a system attribute.
+ */
+TEST_F(Setxattr, system)
+{
+ uint64_t ino = 42;
+ const char value[] = "whatever";
+ ssize_t value_len = strlen(value) + 1;
+ int ns = EXTATTR_NAMESPACE_SYSTEM;
+ ssize_t r;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ expect_setxattr(ino, "system.foo", value, ReturnErrno(0));
+
+ r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value,
+ value_len);
+ ASSERT_EQ(value_len, r) << strerror(errno);
+}
+
+
+TEST_F(Setxattr_7_32, ok)
+{
+ uint64_t ino = 42;
+ const char value[] = "whatever";
+ ssize_t value_len = strlen(value) + 1;
+ int ns = EXTATTR_NAMESPACE_USER;
+ ssize_t r;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+ expect_setxattr_7_32(ino, "user.foo", value, ReturnErrno(0));
+
+ r = extattr_set_file(FULLPATH, ns, "foo", (const void *)value,
+ value_len);
+ ASSERT_EQ(value_len, r) << strerror(errno);
+}
+
+TEST_F(RofsXattr, deleteextattr_erofs)
+{
+ uint64_t ino = 42;
+ int ns = EXTATTR_NAMESPACE_USER;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+
+ ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo"));
+ ASSERT_EQ(EROFS, errno);
+}
+
+TEST_F(RofsXattr, setextattr_erofs)
+{
+ uint64_t ino = 42;
+ const char value[] = "whatever";
+ ssize_t value_len = strlen(value) + 1;
+ int ns = EXTATTR_NAMESPACE_USER;
+ ssize_t r;
+
+ expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
+
+ r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value,
+ value_len);
+ ASSERT_EQ(-1, r);
+ EXPECT_EQ(EROFS, errno);
+}
diff --git a/tests/sys/fs/tarfs/Makefile b/tests/sys/fs/tarfs/Makefile
new file mode 100644
index 000000000000..72355a08a158
--- /dev/null
+++ b/tests/sys/fs/tarfs/Makefile
@@ -0,0 +1,10 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/fs/tarfs
+BINDIR= ${TESTSDIR}
+
+PROGS+= mktar tarsum
+
+ATF_TESTS_SH+= tarfs_test
+
+.include <bsd.test.mk>
diff --git a/tests/sys/fs/tarfs/mktar.c b/tests/sys/fs/tarfs/mktar.c
new file mode 100644
index 000000000000..b4ec271c73a1
--- /dev/null
+++ b/tests/sys/fs/tarfs/mktar.c
@@ -0,0 +1,273 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Klara, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#define PROGNAME "mktar"
+
+#define SUBDIRNAME "directory"
+#define NORMALFILENAME "file"
+#define SPARSEFILENAME "sparse_file"
+#define HARDLINKNAME "hard_link"
+#define SHORTLINKNAME "short_link"
+#define LONGLINKNAME "long_link"
+
+static bool opt_g;
+static bool opt_v;
+
+static void verbose(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (!opt_v)
+ return;
+ fprintf(stderr, "%s: ", PROGNAME);
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+}
+
+static void
+mknormalfile(const char *filename, mode_t mode)
+{
+ char buf[512];
+ ssize_t res;
+ int fd;
+
+ if ((fd = open(filename, O_RDWR|O_CREAT|O_EXCL, mode)) < 0)
+ err(1, "%s", filename);
+ for (unsigned int i = 0; i < sizeof(buf); i++)
+ buf[i] = 32 + i % 64;
+ res = write(fd, buf, sizeof(buf));
+ if (res < 0)
+ err(1, "%s", filename);
+ if (res != sizeof(buf))
+ errx(1, "%s: short write", filename);
+ close(fd);
+}
+
+static void
+mksparsefile(const char *filename, mode_t mode)
+{
+ char buf[511];
+ ssize_t res;
+ int fd;
+
+ if ((fd = open(filename, O_RDWR|O_CREAT|O_EXCL, mode)) < 0)
+ err(1, "%s", filename);
+ for (unsigned int i = 33; i <= 126; i++) {
+ memset(buf, i, sizeof(buf));
+ if (lseek(fd, 1048576LU * (i - 32), SEEK_SET) < 0)
+ err(1, "%s", filename);
+ res = write(fd, buf, sizeof(buf));
+ if (res < 0)
+ err(1, "%s", filename);
+ if (res != sizeof(buf))
+ errx(1, "%s: short write", filename);
+ }
+ close(fd);
+}
+
+static char *
+mklonglinktarget(const char *dirname, const char *filename)
+{
+ char *piece, *target;
+
+ if (asprintf(&piece, "%1$s/../%1$s/../%1$s/../%1$s/../", dirname) < 0)
+ err(1, "asprintf()");
+ if (asprintf(&target, "%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s%2$s", piece, filename) < 0)
+ err(1, "asprintf()");
+ free(piece);
+ return target;
+}
+
+static void
+mktar(void)
+{
+ char *linktarget;
+
+ /* create a subdirectory */
+ verbose("mkdir %s", SUBDIRNAME);
+ if (mkdir(SUBDIRNAME, 0755) != 0)
+ err(1, "%s", SUBDIRNAME);
+
+ /* create a normal file */
+ verbose("creating %s", NORMALFILENAME);
+ mknormalfile(NORMALFILENAME, 0644);
+
+ /* create a sparse file */
+ verbose("creating %s", SPARSEFILENAME);
+ mksparsefile(SPARSEFILENAME, 0644);
+ chflags(SPARSEFILENAME, UF_NODUMP);
+
+ /* create a hard link */
+ verbose("link %s %s", SPARSEFILENAME, HARDLINKNAME);
+ if (link(SPARSEFILENAME, HARDLINKNAME) != 0)
+ err(1, "%s", HARDLINKNAME);
+
+ /* create a symbolic link with a short target */
+ verbose("symlink %s %s", SPARSEFILENAME, SHORTLINKNAME);
+ if (symlink(SPARSEFILENAME, SHORTLINKNAME) != 0)
+ err(1, "%s", SHORTLINKNAME);
+
+ /* create a symbolic link with a long target */
+ linktarget = mklonglinktarget(SUBDIRNAME, SPARSEFILENAME);
+ verbose("symlink %s %s", linktarget, LONGLINKNAME);
+ if (symlink(linktarget, LONGLINKNAME) != 0)
+ err(1, "%s", LONGLINKNAME);
+ free(linktarget);
+}
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "usage: %s [-gv] tarfile\n", PROGNAME);
+ exit(EXIT_FAILURE);
+}
+
+int
+main(int argc, char *argv[])
+{
+ const char *tarfilename;
+ char *dirname;
+ int opt, wstatus;
+ pid_t pid;
+
+ while ((opt = getopt(argc, argv, "gv")) != -1)
+ switch (opt) {
+ case 'g':
+ opt_g = true;
+ break;
+ case 'v':
+ opt_v = true;
+ break;
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ usage();
+ tarfilename = *argv;
+
+ if (asprintf(&dirname, "%s%s.XXXXXXXX", _PATH_TMP, PROGNAME) < 0)
+ err(1, "asprintf()");
+ if (mkdtemp(dirname) == NULL)
+ err(1, "%s", dirname);
+ verbose("mkdir %s", dirname);
+
+ /* fork a child to create the files */
+ if ((pid = fork()) < 0)
+ err(1, "fork()");
+ if (pid == 0) {
+ verbose("cd %s", dirname);
+ if (chdir(dirname) != 0)
+ err(1, "%s", dirname);
+ verbose("umask 022");
+ umask(022);
+ mktar();
+ verbose("cd -");
+ exit(0);
+ }
+ if (waitpid(pid, &wstatus, 0) < 0)
+ err(1, "waitpid()");
+ if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0)
+ errx(1, "child failed");
+
+ /* fork a child to create the tarball */
+ if ((pid = fork()) < 0)
+ err(1, "fork()");
+ if (pid == 0) {
+ verbose("creating tarball");
+ execlp(opt_g ? "gtar" : "tar",
+ "tar",
+ "-c",
+ "-f", tarfilename,
+ "-C", dirname,
+ "--posix",
+ "--zstd",
+#if 0
+ "--options", "zstd:frame-per-file",
+#endif
+ "./" SUBDIRNAME "/../" NORMALFILENAME,
+ "./" SPARSEFILENAME,
+ "./" HARDLINKNAME,
+ "./" SHORTLINKNAME,
+ "./" SUBDIRNAME,
+ "./" LONGLINKNAME,
+ NULL);
+ err(1, "execlp()");
+ }
+ if (waitpid(pid, &wstatus, 0) < 0)
+ err(1, "waitpid()");
+ if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0)
+ errx(1, "child failed");
+
+ /* fork a child to delete everything */
+ if ((pid = fork()) < 0)
+ err(1, "fork()");
+ if (pid == 0) {
+ verbose("cd %s", dirname);
+ if (chdir(dirname) != 0)
+ err(1, "%s", dirname);
+ verbose("rm %s", LONGLINKNAME);
+ (void)unlink(LONGLINKNAME);
+ verbose("rm %s", SHORTLINKNAME);
+ (void)unlink(SHORTLINKNAME);
+ verbose("rm %s", HARDLINKNAME);
+ (void)unlink(HARDLINKNAME);
+ verbose("rm %s", SPARSEFILENAME);
+ (void)unlink(SPARSEFILENAME);
+ verbose("rmdir %s", SUBDIRNAME);
+ (void)rmdir(SUBDIRNAME);
+ verbose("cd -");
+ exit(0);
+ }
+ if (waitpid(pid, &wstatus, 0) < 0)
+ err(1, "waitpid()");
+ if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0)
+ errx(1, "child failed");
+ verbose("rmdir %s", dirname);
+ (void)rmdir(dirname);
+
+ exit(0);
+}
diff --git a/tests/sys/fs/tarfs/tarfs_test.sh b/tests/sys/fs/tarfs/tarfs_test.sh
new file mode 100644
index 000000000000..20baadfea5c5
--- /dev/null
+++ b/tests/sys/fs/tarfs/tarfs_test.sh
@@ -0,0 +1,417 @@
+#!/bin/sh
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2023-2024 Klara, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+mnt="$(realpath ${TMPDIR:-/tmp})/mnt"
+
+# expected SHA256 checksum of file contained in test tarball
+sum=4da2143234486307bb44eaa610375301781a577d1172f362b88bb4b1643dee62
+
+tar() {
+ if [ -n "${TARFS_USE_GNU_TAR}" ] ; then
+ gtar --posix --absolute-names "$@"
+ else
+ bsdtar "$@"
+ fi
+}
+
+mktar() {
+ "$(atf_get_srcdir)"/mktar ${TARFS_USE_GNU_TAR+-g} "$@"
+}
+
+tarsum() {
+ "$(atf_get_srcdir)"/tarsum
+}
+
+tarfs_setup() {
+ mkdir "${mnt}"
+}
+
+tarfs_cleanup() {
+ umount -f "${mnt}" 2>/dev/null || true
+}
+
+atf_test_case tarfs_basic cleanup
+tarfs_basic_head() {
+ atf_set "descr" "Basic function test"
+ atf_set "require.user" "root"
+ atf_set "require.kmods" "tarfs"
+}
+tarfs_basic_body() {
+ tarfs_setup
+ local tarball="${PWD}/tarfs_test.tar.zst"
+ mktar "${tarball}"
+ atf_check mount -rt tarfs "${tarball}" "${mnt}"
+ atf_check -o match:"^${tarball} on ${mnt} \(tarfs," mount
+ atf_check_equal "$(stat -f%d,%i "${mnt}"/sparse_file)" "$(stat -f%d,%i "${mnt}"/hard_link)"
+ atf_check_equal "$(stat -f%d,%i "${mnt}"/sparse_file)" "$(stat -L -f%d,%i "${mnt}"/short_link)"
+ atf_check_equal "$(stat -f%d,%i "${mnt}"/sparse_file)" "$(stat -L -f%d,%i "${mnt}"/long_link)"
+ atf_check -o inline:"${sum}\n" sha256 -q "${mnt}"/sparse_file
+ atf_check -o inline:"2,40755\n" stat -f%l,%p "${mnt}"/directory
+ atf_check -o inline:"1,100644\n" stat -f%l,%p "${mnt}"/file
+ atf_check -o inline:"2,100644\n" stat -f%l,%p "${mnt}"/hard_link
+ atf_check -o inline:"1,120755\n" stat -f%l,%p "${mnt}"/long_link
+ atf_check -o inline:"1,120755\n" stat -f%l,%p "${mnt}"/short_link
+ atf_check -o inline:"2,100644\n" stat -f%l,%p "${mnt}"/sparse_file
+ atf_check -o inline:"3,40755\n" stat -f%l,%p "${mnt}"
+}
+tarfs_basic_cleanup() {
+ tarfs_cleanup
+}
+
+atf_test_case tarfs_basic_gnu cleanup
+tarfs_basic_gnu_head() {
+ atf_set "descr" "Basic function test using GNU tar"
+ atf_set "require.user" "root"
+ atf_set "require.kmods" "tarfs"
+ atf_set "require.progs" "gtar"
+}
+tarfs_basic_gnu_body() {
+ TARFS_USE_GNU_TAR=true
+ tarfs_basic_body
+}
+tarfs_basic_gnu_cleanup() {
+ tarfs_basic_cleanup
+}
+
+atf_test_case tarfs_notdir_device cleanup
+tarfs_notdir_device_head() {
+ atf_set "descr" "Regression test for PR 269519 and 269561"
+ atf_set "require.user" "root"
+ atf_set "require.kmods" "tarfs"
+}
+tarfs_notdir_device_body() {
+ tarfs_setup
+ atf_check mknod d b 0xdead 0xbeef
+ tar -cf tarfs_notdir.tar d
+ rm d
+ mkdir d
+ echo "boom" >d/f
+ tar -rf tarfs_notdir.tar d/f
+ atf_check -s not-exit:0 -e match:"Invalid" \
+ mount -rt tarfs tarfs_notdir.tar "${mnt}"
+}
+tarfs_notdir_device_cleanup() {
+ tarfs_cleanup
+}
+
+atf_test_case tarfs_notdir_device_gnu cleanup
+tarfs_notdir_device_gnu_head() {
+ atf_set "descr" "Regression test for PR 269519 and 269561 using GNU tar"
+ atf_set "require.user" "root"
+ atf_set "require.kmods" "tarfs"
+ atf_set "require.progs" "gtar"
+}
+tarfs_notdir_device_gnu_body() {
+ TARFS_USE_GNU_TAR=true
+ tarfs_notdir_device_body
+}
+tarfs_notdir_device_gnu_cleanup() {
+ tarfs_notdir_device_cleanup
+}
+
+atf_test_case tarfs_notdir_dot cleanup
+tarfs_notdir_dot_head() {
+ atf_set "descr" "Regression test for PR 269519 and 269561"
+ atf_set "require.user" "root"
+ atf_set "require.kmods" "tarfs"
+}
+tarfs_notdir_dot_body() {
+ tarfs_setup
+ echo "hello" >d
+ tar -cf tarfs_notdir.tar d
+ rm d
+ mkdir d
+ echo "world" >d/f
+ tar -rf tarfs_notdir.tar d/./f
+ atf_check -s not-exit:0 -e match:"Invalid" \
+ mount -rt tarfs tarfs_notdir.tar "${mnt}"
+}
+tarfs_notdir_dot_cleanup() {
+ tarfs_cleanup
+}
+
+atf_test_case tarfs_notdir_dot_gnu cleanup
+tarfs_notdir_dot_gnu_head() {
+ atf_set "descr" "Regression test for PR 269519 and 269561 using GNU tar"
+ atf_set "require.user" "root"
+ atf_set "require.kmods" "tarfs"
+ atf_set "require.progs" "gtar"
+}
+tarfs_notdir_dot_gnu_body() {
+ TARFS_USE_GNU_TAR=true
+ tarfs_notdir_dot_body
+}
+tarfs_notdir_dot_gnu_cleanup() {
+ tarfs_notdir_dot_cleanup
+}
+
+atf_test_case tarfs_notdir_dotdot cleanup
+tarfs_notdir_dotdot_head() {
+ atf_set "descr" "Regression test for PR 269519 and 269561"
+ atf_set "require.user" "root"
+ atf_set "require.kmods" "tarfs"
+}
+tarfs_notdir_dotdot_body() {
+ tarfs_setup
+ echo "hello" >d
+ tar -cf tarfs_notdir.tar d
+ rm d
+ mkdir d
+ echo "world" >f
+ tar -rf tarfs_notdir.tar d/../f
+ atf_check -s not-exit:0 -e match:"Invalid" \
+ mount -rt tarfs tarfs_notdir.tar "${mnt}"
+}
+tarfs_notdir_dotdot_cleanup() {
+ tarfs_cleanup
+}
+
+atf_test_case tarfs_notdir_dotdot_gnu cleanup
+tarfs_notdir_dotdot_gnu_head() {
+ atf_set "descr" "Regression test for PR 269519 and 269561 using GNU tar"
+ atf_set "require.user" "root"
+ atf_set "require.kmods" "tarfs"
+ atf_set "require.progs" "gtar"
+}
+tarfs_notdir_dotdot_gnu_body() {
+ TARFS_USE_GNU_TAR=true
+ tarfs_notdir_dotdot_body
+}
+tarfs_notdir_dotdot_gnu_cleanup() {
+ tarfs_notdir_dotdot_cleanup
+}
+
+atf_test_case tarfs_notdir_file cleanup
+tarfs_notdir_file_head() {
+ atf_set "descr" "Regression test for PR 269519 and 269561"
+ atf_set "require.user" "root"
+ atf_set "require.kmods" "tarfs"
+}
+tarfs_notdir_file_body() {
+ tarfs_setup
+ echo "hello" >d
+ tar -cf tarfs_notdir.tar d
+ rm d
+ mkdir d
+ echo "world" >d/f
+ tar -rf tarfs_notdir.tar d/f
+ atf_check -s not-exit:0 -e match:"Invalid" \
+ mount -rt tarfs tarfs_notdir.tar "${mnt}"
+}
+tarfs_notdir_file_cleanup() {
+ tarfs_cleanup
+}
+
+atf_test_case tarfs_notdir_file_gnu cleanup
+tarfs_notdir_file_gnu_head() {
+ atf_set "descr" "Regression test for PR 269519 and 269561 using GNU tar"
+ atf_set "require.user" "root"
+ atf_set "require.kmods" "tarfs"
+ atf_set "require.progs" "gtar"
+}
+tarfs_notdir_file_gnu_body() {
+ TARFS_USE_GNU_TAR=true
+ tarfs_notdir_file_body
+}
+tarfs_notdir_file_gnu_cleanup() {
+ tarfs_notdir_file_cleanup
+}
+
+atf_test_case tarfs_emptylink cleanup
+tarfs_emptylink_head() {
+ atf_set "descr" "Regression test for PR 277360: empty link target"
+ atf_set "require.user" "root"
+ atf_set "require.kmods" "tarfs"
+}
+tarfs_emptylink_body() {
+ tarfs_setup
+ touch z
+ ln -f z hard
+ ln -fs z soft
+ tar -cf - z hard soft | dd bs=512 skip=1 | tr z '\0' | \
+ tarsum >> tarfs_emptylink.tar
+ atf_check -s not-exit:0 -e match:"Invalid" \
+ mount -rt tarfs tarfs_emptylink.tar "${mnt}"
+}
+tarfs_emptylink_cleanup() {
+ tarfs_cleanup
+}
+
+atf_test_case tarfs_linktodir cleanup
+tarfs_linktodir_head() {
+ atf_set "descr" "Regression test for PR 277360: link to directory"
+ atf_set "require.user" "root"
+ atf_set "require.kmods" "tarfs"
+}
+tarfs_linktodir_body() {
+ tarfs_setup
+ mkdir d
+ tar -cf - d | dd bs=512 count=1 > tarfs_linktodir.tar
+ rmdir d
+ touch d
+ ln -f d link
+ tar -cf - d link | dd bs=512 skip=1 >> tarfs_linktodir.tar
+ atf_check -s not-exit:0 -e match:"Invalid" \
+ mount -rt tarfs tarfs_linktodir.tar "${mnt}"
+}
+tarfs_linktodir_cleanup() {
+ tarfs_cleanup
+}
+
+atf_test_case tarfs_linktononexistent cleanup
+tarfs_linktononexistent_head() {
+ atf_set "descr" "Regression test for PR 277360: link to nonexistent target"
+ atf_set "require.user" "root"
+ atf_set "require.kmods" "tarfs"
+}
+tarfs_linktononexistent_body() {
+ tarfs_setup
+ touch f
+ ln -f f link
+ tar -cf - f link | dd bs=512 skip=1 >> tarfs_linktononexistent.tar
+ atf_check -s not-exit:0 -e match:"Invalid" \
+ mount -rt tarfs tarfs_linktononexistent.tar "${mnt}"
+}
+tarfs_linktononexistent_cleanup() {
+ tarfs_cleanup
+}
+
+atf_test_case tarfs_checksum cleanup
+tarfs_checksum_head() {
+ atf_set "descr" "Verify that the checksum covers header padding"
+ atf_set "require.user" "root"
+ atf_set "require.kmods" "tarfs"
+}
+tarfs_checksum_body() {
+ tarfs_setup
+ touch f
+ tar -cf tarfs_checksum.tar f
+ truncate -s 500 tarfs_checksum.tar
+ printf "\1\1\1\1\1\1\1\1\1\1\1\1" >> tarfs_checksum.tar
+ dd if=/dev/zero bs=512 count=2 >> tarfs_checksum.tar
+ hexdump -C tarfs_checksum.tar
+ atf_check -s not-exit:0 -e match:"Invalid" \
+ mount -rt tarfs tarfs_checksum.tar "${mnt}"
+}
+tarfs_checksum_cleanup() {
+ tarfs_cleanup
+}
+
+atf_test_case tarfs_long_names cleanup
+tarfs_long_names_head() {
+ atf_set "descr" "Verify that tarfs supports long file names"
+ atf_set "require.user" "root"
+ atf_set "require.kmods" "tarfs"
+}
+tarfs_long_names_body() {
+ tarfs_setup
+ local a b c d e
+ a="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ b="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
+ c="cccccccccccccccccccccccccccccccccccccccc"
+ d="dddddddddddddddddddddddddddddddddddddddd"
+ e="eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
+ mkdir -p "${a}"
+ touch "${a}/${b}_${c}_${d}_${e}_foo"
+ ln "${a}/${b}_${c}_${d}_${e}_foo" "${a}/${b}_${c}_${d}_${e}_bar"
+ ln -s "${b}_${c}_${d}_${e}_bar" "${a}/${b}_${c}_${d}_${e}_baz"
+ tar -cf tarfs_long_names.tar "${a}"
+ atf_check mount -rt tarfs tarfs_long_names.tar "${mnt}"
+}
+tarfs_long_names_cleanup() {
+ tarfs_cleanup
+}
+
+atf_test_case tarfs_long_paths cleanup
+tarfs_long_paths_head() {
+ atf_set "descr" "Verify that tarfs supports long paths"
+ atf_set "require.user" "root"
+ atf_set "require.kmods" "tarfs"
+}
+tarfs_long_paths_body() {
+ tarfs_setup
+ local a b c d e
+ a="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ b="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
+ c="cccccccccccccccccccccccccccccccccccccccc"
+ d="dddddddddddddddddddddddddddddddddddddddd"
+ e="eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
+ mkdir -p "${a}/${b}/${c}/${d}/${e}"
+ touch "${a}/${b}/${c}/${d}/${e}/foo"
+ ln "${a}/${b}/${c}/${d}/${e}/foo" "${a}/${b}/${c}/${d}/${e}/bar"
+ ln -s "${b}/${c}/${d}/${e}/bar" "${a}/baz"
+ tar -cf tarfs_long_paths.tar "${a}"
+ atf_check mount -rt tarfs tarfs_long_paths.tar "${mnt}"
+}
+tarfs_long_paths_cleanup() {
+ tarfs_cleanup
+}
+
+atf_test_case tarfs_git_archive cleanup
+tarfs_git_archive_head() {
+ atf_set "descr" "Verify that tarfs supports archives created by git"
+ atf_set "require.user" "root"
+ atf_set "require.kmods" "tarfs"
+ atf_set "require.progs" "git"
+}
+tarfs_git_archive_body() {
+ tarfs_setup
+ mkdir foo
+ echo "Hello, world!" >foo/bar
+ git -C foo init --initial-branch=tarfs
+ git -C foo config user.name "File System"
+ git -C foo config user.email fs@freebsd.org
+ git -C foo add bar
+ git -C foo commit -m bar
+ git -C foo archive --output=../tarfs_git_archive.tar HEAD
+ atf_check mount -rt tarfs tarfs_git_archive.tar "${mnt}"
+ atf_check -o file:foo/bar cat "${mnt}"/bar
+}
+tarfs_git_archive_cleanup() {
+ tarfs_cleanup
+}
+
+atf_init_test_cases() {
+ atf_add_test_case tarfs_basic
+ atf_add_test_case tarfs_basic_gnu
+ atf_add_test_case tarfs_notdir_device
+ atf_add_test_case tarfs_notdir_device_gnu
+ atf_add_test_case tarfs_notdir_dot
+ atf_add_test_case tarfs_notdir_dot_gnu
+ atf_add_test_case tarfs_notdir_dotdot
+ atf_add_test_case tarfs_notdir_dotdot_gnu
+ atf_add_test_case tarfs_notdir_file
+ atf_add_test_case tarfs_notdir_file_gnu
+ atf_add_test_case tarfs_emptylink
+ atf_add_test_case tarfs_linktodir
+ atf_add_test_case tarfs_linktononexistent
+ atf_add_test_case tarfs_checksum
+ atf_add_test_case tarfs_long_names
+ atf_add_test_case tarfs_long_paths
+ atf_add_test_case tarfs_git_archive
+}
diff --git a/tests/sys/fs/tarfs/tarsum.c b/tests/sys/fs/tarfs/tarsum.c
new file mode 100644
index 000000000000..73ead2230a5e
--- /dev/null
+++ b/tests/sys/fs/tarfs/tarsum.c
@@ -0,0 +1,128 @@
+/*-
+ * Copyright (c) 2024 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * This program reads a tarball from stdin, recalculates the checksums of
+ * all ustar records within it, and writes the result to stdout.
+ */
+
+#include <err.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static bool opt_v;
+
+static int
+verbose(const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ if (!opt_v)
+ return (0);
+ va_start(ap, fmt);
+ ret = vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ return (ret);
+}
+
+static void
+tarsum(FILE *in, const char *ifn, FILE *out, const char *ofn)
+{
+ union {
+ uint8_t bytes[512];
+ struct {
+ uint8_t prelude[148];
+ char checksum[8];
+ uint8_t interlude[101];
+ char magic[6];
+ char version[2];
+ char postlude[];
+ };
+ } ustar;
+ unsigned long sum;
+ off_t offset = 0;
+ ssize_t ret;
+ size_t i;
+
+ for (;;) {
+ if ((ret = fread(&ustar, 1, sizeof(ustar), in)) < 0)
+ err(1, "%s", ifn);
+ else if (ret == 0)
+ break;
+ else if ((size_t)ret < sizeof(ustar))
+ errx(1, "%s: Short read", ifn);
+ if (strcmp(ustar.magic, "ustar") == 0 &&
+ ustar.version[0] == '0' && ustar.version[1] == '0') {
+ verbose("header found at offset %#lx\n", offset);
+ verbose("current checksum %.*s\n",
+ (int)sizeof(ustar.checksum), ustar.checksum);
+ memset(ustar.checksum, ' ', sizeof(ustar.checksum));
+ for (sum = i = 0; i < sizeof(ustar); i++)
+ sum += ustar.bytes[i];
+ verbose("calculated checksum %#lo\n", sum);
+ sprintf(ustar.checksum, "%#lo", sum);
+ }
+ if ((ret = fwrite(&ustar, 1, sizeof(ustar), out)) < 0)
+ err(1, "%s", ofn);
+ else if ((size_t)ret < sizeof(ustar))
+ errx(1, "%s: Short write", ofn);
+ offset += sizeof(ustar);
+ }
+ verbose("%lu bytes processed\n", offset);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: tarsum [-v] [-o output] [input]\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ const char *ifn, *ofn = NULL;
+ FILE *in, *out;
+ int opt;
+
+ while ((opt = getopt(argc, argv, "o:v")) != -1) {
+ switch (opt) {
+ case 'o':
+ ofn = optarg;
+ break;
+ case 'v':
+ opt_v = true;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc == 0 || strcmp(*argv, "-") == 0) {
+ ifn = "stdin";
+ in = stdin;
+ } else if (argc == 1) {
+ ifn = *argv;
+ if ((in = fopen(ifn, "rb")) == NULL)
+ err(1, "%s", ifn);
+ } else {
+ usage();
+ }
+ if (ofn == NULL || strcmp(ofn, "-") == 0) {
+ ofn = "stdout";
+ out = stdout;
+ } else {
+ if ((out = fopen(ofn, "wb")) == NULL)
+ err(1, "%s", ofn);
+ }
+ tarsum(in, ifn, out, ofn);
+ return (0);
+}
diff --git a/tests/sys/fs/tmpfs/Makefile b/tests/sys/fs/tmpfs/Makefile
new file mode 100644
index 000000000000..ce4ca959838c
--- /dev/null
+++ b/tests/sys/fs/tmpfs/Makefile
@@ -0,0 +1,62 @@
+PACKAGE= tests
+
+FILESYSTEM?= ${.CURDIR:T}
+
+TESTSDIR= ${TESTSBASE}/sys/fs/${FILESYSTEM}
+
+TESTSRC= ${SRCTOP}/contrib/netbsd-tests/fs/${FILESYSTEM}
+
+# XXX: uses /dev/MAKEDEV to create pseudo /dev/{null,zero} character devices
+#NETBSD_ATF_TESTS_SH+= devices_test
+NETBSD_ATF_TESTS_SH+= create_test
+NETBSD_ATF_TESTS_SH+= read_write_test
+NETBSD_ATF_TESTS_SH+= dots_test
+NETBSD_ATF_TESTS_SH+= exec_test
+NETBSD_ATF_TESTS_SH+= link_test
+NETBSD_ATF_TESTS_SH+= mkdir_test
+NETBSD_ATF_TESTS_SH+= mknod_test
+NETBSD_ATF_TESTS_SH+= mount_test
+# XXX: need to replace `mknod ... p` with something more sensible
+#NETBSD_ATF_TESTS_SH+= pipes_test
+NETBSD_ATF_TESTS_SH+= trail_slash_test
+NETBSD_ATF_TESTS_SH+= readdir_test
+NETBSD_ATF_TESTS_SH+= remove_test
+NETBSD_ATF_TESTS_SH+= rename_test
+NETBSD_ATF_TESTS_SH+= rmdir_test
+NETBSD_ATF_TESTS_SH+= setattr_test
+NETBSD_ATF_TESTS_SH+= sizes_test
+NETBSD_ATF_TESTS_SH+= sockets_test
+NETBSD_ATF_TESTS_SH+= statvfs_test
+NETBSD_ATF_TESTS_SH+= symlink_test
+NETBSD_ATF_TESTS_SH+= times_test
+NETBSD_ATF_TESTS_SH+= truncate_test
+NETBSD_ATF_TESTS_SH+= vnd_test
+NETBSD_ATF_TESTS_SH+= vnode_leak_test
+
+${PACKAGE}FILES+= h_funcs.subr
+${PACKAGE}FILESDIR= ${TESTSDIR}
+
+PROGS+= h_tools
+BINDIR.h_tools= ${TESTSDIR}
+
+ATF_TESTS_SH_SED_mknod_test= \
+ -e 's,mknod pipe p,mkfifo pipe,g' \
+ -e 's,mknod dir/pipe p,mkfifo dir/pipe,g'
+
+ATF_TESTS_SH_SED_mount_test= \
+ -e 's,-o -g,-o gid=,g' \
+ -e 's,-o -m,-o mode=,g' \
+ -e 's,-o -s,-o size=,g' \
+ -e 's,-o -u,-o uid=,g' \
+ -e 's,mount_${FILESYSTEM},mount -t ${FILESYSTEM},g'
+ATF_TESTS_SH_SED_readdir_test= -e 's,mknod fifo p,mkfifo fifo,g'
+ATF_TESTS_SH_SED_sizes_test= -e 's,-o -s,-o size=,g'
+ATF_TESTS_SH_SED_statvfs_test= -e 's,-o -s,-o size=,g'
+ATF_TESTS_SH_SED_vnd_test= \
+ -e 's,vndconfig -u /dev/vnd3,mdconfig -d -u $$md_dev,g' \
+ -e 's,/dev/vnd3,/dev/$$md_dev,g'
+ATF_TESTS_SH_SED_vnode_leak_test= -e 's,-o -s,-o size=,g'
+
+.include <netbsd-tests.test.mk>
+
+.include <bsd.test.mk>
diff --git a/tests/sys/fs/tmpfs/Makefile.depend b/tests/sys/fs/tmpfs/Makefile.depend
new file mode 100644
index 000000000000..84b8ddd67e34
--- /dev/null
+++ b/tests/sys/fs/tmpfs/Makefile.depend
@@ -0,0 +1,16 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/geom/Makefile b/tests/sys/geom/Makefile
new file mode 100644
index 000000000000..78257e180cf9
--- /dev/null
+++ b/tests/sys/geom/Makefile
@@ -0,0 +1,5 @@
+TESTSDIR= ${TESTSBASE}/sys/geom
+
+TESTS_SUBDIRS+= class
+
+.include <bsd.test.mk>
diff --git a/tests/sys/geom/Makefile.depend b/tests/sys/geom/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/geom/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/geom/class/Makefile b/tests/sys/geom/class/Makefile
new file mode 100644
index 000000000000..3cf3a15273ac
--- /dev/null
+++ b/tests/sys/geom/class/Makefile
@@ -0,0 +1,25 @@
+.include <src.opts.mk>
+
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/geom/class
+
+TESTS_SUBDIRS+= concat
+.if ${MK_OPENSSL} != "no"
+TESTS_SUBDIRS+= eli
+.endif
+TESTS_SUBDIRS+= gate
+TESTS_SUBDIRS+= mirror
+TESTS_SUBDIRS+= multipath
+TESTS_SUBDIRS+= nop
+TESTS_SUBDIRS+= part
+TESTS_SUBDIRS+= raid3
+TESTS_SUBDIRS+= shsec
+TESTS_SUBDIRS+= stripe
+TESTS_SUBDIRS+= union
+TESTS_SUBDIRS+= uzip
+TESTS_SUBDIRS+= virstor
+
+${PACKAGE}FILES+= geom_subr.sh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/geom/class/Makefile.depend b/tests/sys/geom/class/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/geom/class/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/geom/class/Makefile.inc b/tests/sys/geom/class/Makefile.inc
new file mode 100644
index 000000000000..cec69b26e149
--- /dev/null
+++ b/tests/sys/geom/class/Makefile.inc
@@ -0,0 +1 @@
+.include "${SRCTOP}/tests/Makefile.inc0"
diff --git a/tests/sys/geom/class/concat/1_test.sh b/tests/sys/geom/class/concat/1_test.sh
new file mode 100644
index 000000000000..126768370140
--- /dev/null
+++ b/tests/sys/geom/class/concat/1_test.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+. `dirname $0`/conf.sh
+
+echo '1..1'
+
+attach_md us0 -t malloc -s 1M || exit 1
+attach_md us1 -t malloc -s 2M || exit 1
+attach_md us2 -t malloc -s 3M || exit 1
+
+gconcat create $name /dev/$us0 /dev/$us1 /dev/$us2 || exit 1
+devwait
+
+# Size of created device should be 1MB + 2MB + 3MB.
+
+size=`diskinfo /dev/concat/${name} | awk '{print $3}'`
+
+if [ $size -eq 6291456 ]; then
+ echo "ok - Size is 6291456"
+else
+ echo "not ok - Size is 6291456"
+fi
diff --git a/tests/sys/geom/class/concat/2_test.sh b/tests/sys/geom/class/concat/2_test.sh
new file mode 100644
index 000000000000..63e545f12297
--- /dev/null
+++ b/tests/sys/geom/class/concat/2_test.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+. `dirname $0`/conf.sh
+
+echo '1..1'
+
+tsize=6
+src=`mktemp $base.XXXXXX` || exit 1
+dst=`mktemp $base.XXXXXX` || exit 1
+
+attach_md us0 -t malloc -s 1M || exit 1
+attach_md us1 -t malloc -s 2M || exit 1
+attach_md us2 -t malloc -s 3M || exit 1
+
+dd if=/dev/random of=${src} bs=1m count=$tsize >/dev/null 2>&1
+
+gconcat create $name /dev/$us0 /dev/$us1 /dev/$us2 || exit 1
+devwait
+
+dd if=${src} of=/dev/concat/${name} bs=1m count=$tsize >/dev/null 2>&1
+dd if=/dev/concat/${name} of=${dst} bs=1m count=$tsize >/dev/null 2>&1
+
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok - md5 checksum comparison"
+else
+ echo "ok - md5 checksum comparison"
+fi
+
+rm -f ${src} ${dst}
diff --git a/tests/sys/geom/class/concat/Makefile b/tests/sys/geom/class/concat/Makefile
new file mode 100644
index 000000000000..d5457b64bd2a
--- /dev/null
+++ b/tests/sys/geom/class/concat/Makefile
@@ -0,0 +1,16 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/geom/class/${.CURDIR:T}
+
+TAP_TESTS_SH+= 1_test
+TAP_TESTS_SH+= 2_test
+TAP_TESTS_SH+= append1
+TAP_TESTS_SH+= append2
+
+${PACKAGE}FILES+= conf.sh
+
+.for t in ${TAP_TESTS_SH}
+TEST_METADATA.$t+= required_user="root"
+.endfor
+
+.include <bsd.test.mk>
diff --git a/tests/sys/geom/class/concat/Makefile.depend b/tests/sys/geom/class/concat/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/geom/class/concat/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/geom/class/concat/append1.sh b/tests/sys/geom/class/concat/append1.sh
new file mode 100644
index 000000000000..b6ac3772022b
--- /dev/null
+++ b/tests/sys/geom/class/concat/append1.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+# A basic regression test for gconcat append using "gconcat create",
+# i.e., manual mode.
+
+gconcat_check_size()
+{
+ local actual expected name
+
+ name=$1
+ expected=$2
+
+ actual=$(diskinfo /dev/concat/${name} | awk '{print $3}')
+ if [ $actual -eq $expected ]; then
+ echo "ok - Size is ${actual}"
+ else
+ echo "not ok - Size is ${actual}"
+ fi
+}
+
+. `dirname $0`/conf.sh
+
+echo '1..3'
+
+attach_md us0 -t malloc -s 1M || exit 1
+attach_md us1 -t malloc -s 1M || exit 1
+attach_md us2 -t malloc -s 1M || exit 1
+
+gconcat create $name /dev/$us0 /dev/$us1 || exit 1
+devwait
+
+# We should have a 2MB device. Add another disk and verify that the
+# reported size of the concat device grows accordingly.
+gconcat_check_size "${name}" $((2 * 1024 * 1024))
+gconcat append $name /dev/$us2 || exit 1
+gconcat_check_size "${name}" $((3 * 1024 * 1024))
+
+# Write some data and make sure that we can read it back.
+tmpfile=$(mktemp) || exit 1
+dd if=/dev/random of=$tmpfile bs=1M count=3 || exit 1
+dd if=$tmpfile of=/dev/concat/${name} || exit 1
+if cmp -s $tmpfile /dev/concat/${name}; then
+ echo "ok - Data matches what was written"
+else
+ echo "not ok - Data matches what was written"
+fi
diff --git a/tests/sys/geom/class/concat/append2.sh b/tests/sys/geom/class/concat/append2.sh
new file mode 100644
index 000000000000..d008e976cf57
--- /dev/null
+++ b/tests/sys/geom/class/concat/append2.sh
@@ -0,0 +1,77 @@
+#!/bin/sh
+
+# A basic regression test for gconcat append using "gconcat label",
+# i.e., automatic mode.
+
+gconcat_check_size()
+{
+ local actual expected name
+
+ name=$1
+ expected=$2
+
+ actual=$(diskinfo /dev/concat/${name} | awk '{print $3}')
+ if [ $actual -eq $expected ]; then
+ echo "ok - Size is ${actual}"
+ else
+ echo "not ok - Size is ${actual}"
+ fi
+}
+
+. `dirname $0`/conf.sh
+
+echo '1..4'
+
+ss=512
+
+f1=$(mktemp) || exit 1
+truncate -s $((1024 * 1024 + $ss)) $f1
+f2=$(mktemp) || exit 1
+truncate -s $((1024 * 1024 + $ss)) $f2
+f3=$(mktemp) || exit 1
+truncate -s $((1024 * 1024 + $ss)) $f3
+
+attach_md us0 -f $f1 -S $ss || exit 1
+attach_md us1 -f $f2 -S $ss || exit 1
+attach_md us2 -f $f3 -S $ss || exit 1
+
+gconcat label $name /dev/$us0 /dev/$us1 || exit 1
+devwait
+
+# We should have a 2MB device. Add another disk and verify that the
+# reported size of the concat device grows accordingly. A sector from
+# each disk is reserved for the metadata sector.
+gconcat_check_size "${name}" $((2 * 1024 * 1024))
+gconcat append $name /dev/$us2 || exit 1
+gconcat_check_size "${name}" $((3 * 1024 * 1024))
+
+copy=$(mktemp) || exit 1
+dd if=/dev/random of=$copy bs=1M count=3 || exit 1
+dd if=$copy of=/dev/concat/${name} || exit 1
+
+# Stop the concat device and destroy the backing providers.
+gconcat stop ${name} || exit 1
+detach_md $us0
+detach_md $us1
+detach_md $us2
+
+# Re-create the providers and verify that the concat device comes
+# back and that the data is still there.
+attach_md us0 -f $f1 -S $ss || exit 1
+attach_md us1 -f $f2 -S $ss || exit 1
+attach_md us2 -f $f3 -S $ss || exit 1
+
+devwait
+
+# Make sure that the
+if [ -c /dev/concat/${name} ]; then
+ echo "ok - concat device was instantiated"
+else
+ echo "not ok - concat device was instantiated"
+fi
+
+if cmp -s $copy /dev/concat/${name}; then
+ echo "ok - Data was persisted across gconcat stop"
+else
+ echo "not ok - Data was persisted across gconcat stop"
+fi
diff --git a/tests/sys/geom/class/concat/conf.sh b/tests/sys/geom/class/concat/conf.sh
new file mode 100644
index 000000000000..e915f0d8aca9
--- /dev/null
+++ b/tests/sys/geom/class/concat/conf.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+name="$(mktemp -u concat.XXXXXX)"
+class="concat"
+base=`basename $0`
+
+gconcat_test_cleanup()
+{
+ [ -c /dev/$class/$name ] && gconcat destroy $name
+ geom_test_cleanup
+}
+trap gconcat_test_cleanup ABRT EXIT INT TERM
+
+. `dirname $0`/../geom_subr.sh
diff --git a/tests/sys/geom/class/eli/Makefile b/tests/sys/geom/class/eli/Makefile
new file mode 100644
index 000000000000..e1a28f39d5d2
--- /dev/null
+++ b/tests/sys/geom/class/eli/Makefile
@@ -0,0 +1,44 @@
+.PATH: ${SRCTOP}/sys/geom/eli ${SRCTOP}/sys/crypto/sha2
+
+PACKAGE= tests
+
+WARNS?= 3
+
+TESTSDIR= ${TESTSBASE}/sys/geom/class/${.CURDIR:T}
+
+ATF_TESTS_C= pbkdf2_test
+ATF_TESTS_SH+= attach_test
+ATF_TESTS_SH+= configure_test
+ATF_TESTS_SH+= delkey_test
+ATF_TESTS_SH+= detach_test
+ATF_TESTS_SH+= init_test
+ATF_TESTS_SH+= integrity_test
+ATF_TESTS_SH+= kill_test
+ATF_TESTS_SH+= misc_test
+ATF_TESTS_SH+= onetime_test
+ATF_TESTS_SH+= online_resize_test
+ATF_TESTS_SH+= reentrancy_test
+ATF_TESTS_SH+= resize_test
+ATF_TESTS_SH+= setkey_test
+
+${PACKAGE}FILES+= conf.sh
+
+CFLAGS.pbkdf2_test= -I${SRCTOP}/sys
+
+SRCS.pbkdf2_test= \
+ hmac_test.c \
+ g_eli_crypto.c \
+ g_eli_hmac.c \
+ pkcs5v2.c \
+ sha512c.c \
+ sha256c.c
+
+LIBADD.pbkdf2_test= crypto
+
+PROGS+= unaligned_io
+BINDIR?= ${TESTSDIR}
+
+testvect.h:
+ python gentestvect.py > ${.TARGET}
+
+.include <bsd.test.mk>
diff --git a/tests/sys/geom/class/eli/Makefile.depend b/tests/sys/geom/class/eli/Makefile.depend
new file mode 100644
index 000000000000..2004c39a3dab
--- /dev/null
+++ b/tests/sys/geom/class/eli/Makefile.depend
@@ -0,0 +1,18 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+ secure/lib/libcrypto \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/geom/class/eli/attach_test.sh b/tests/sys/geom/class/eli/attach_test.sh
new file mode 100644
index 000000000000..b6b1848f2d37
--- /dev/null
+++ b/tests/sys/geom/class/eli/attach_test.sh
@@ -0,0 +1,158 @@
+
+. $(atf_get_srcdir)/conf.sh
+
+atf_test_case attach_d cleanup
+attach_d_head()
+{
+ atf_set "descr" "geli attach -d will cause the provider to detach on last close"
+ atf_set "require.user" "root"
+}
+attach_d_body()
+{
+ geli_test_setup
+
+ sectors=100
+ attach_md md -t malloc -s `expr $sectors + 1`
+
+ atf_check dd if=/dev/random of=keyfile bs=512 count=16 status=none
+
+ atf_check geli init -B none -P -K keyfile ${md}
+ atf_check geli attach -d -p -k keyfile ${md}
+
+ # Be sure it doesn't detach on read.
+ atf_check dd if=/dev/${md}.eli of=/dev/null status=none
+ sleep 1
+ if [ ! -c /dev/${md}.eli ]; then
+ atf_fail "Detached on last close of a reader"
+ fi
+
+ # It should detach on last close of a writer
+ true > /dev/${md}.eli
+ sleep 1
+ if [ -c /dev/${md}.eli ]; then
+ atf_fail "Did not detach on last close of a writer"
+ fi
+
+}
+attach_d_cleanup()
+{
+ geli_test_cleanup
+}
+
+atf_test_case atach_multiple_fails cleanup
+attach_multiple_fails_head()
+{
+ atf_set "descr" "test multiple failed attach of geli provider"
+ atf_set "require.user" "root"
+}
+attach_multiple_fails_body()
+{
+ geli_test_setup
+
+ sectors=1000
+ attach_md md -t malloc -s `expr $sectors + 1`
+ atf_check dd if=/dev/random of=keyfile bs=512 count=16 status=none
+
+ atf_check geli init -B none -P -K keyfile ${md}
+ atf_check geli attach -d -p -k keyfile ${md}
+
+ for i in $(jot 100); do
+ atf_check -s not-exit:0 -e ignore -- geli attach -d -p -k keyfile ${md}
+ done
+ atf_check -o ignore -- newfs ${md}.eli
+}
+attach_multiple_fails_cleanup()
+{
+ geli_test_cleanup
+}
+
+
+atf_test_case attach_r cleanup
+attach_r_head()
+{
+ atf_set "descr" "geli attach -r will create a readonly provider"
+ atf_set "require.user" "root"
+}
+attach_r_body()
+{
+ geli_test_setup
+
+ sectors=100
+ attach_md md -t malloc -s `expr $sectors + 1`
+ atf_check dd if=/dev/random of=keyfile bs=512 count=16 status=none
+
+ atf_check geli init -B none -P -K keyfile ${md}
+ atf_check geli attach -r -p -k keyfile ${md}
+
+ atf_check -o match:"^Flags: .*READ-ONLY" geli list ${md}.eli
+
+ # Verify that writes are verbotten
+ atf_check -s not-exit:0 -e match:"Read-only" \
+ dd if=/dev/zero of=/dev/${md}.eli count=1
+}
+attach_r_cleanup()
+{
+ geli_test_cleanup
+}
+
+atf_test_case attach_multiple cleanup
+attach_multiple_head()
+{
+ atf_set "descr" "geli attach can attach multiple providers"
+ atf_set "require.user" "root"
+}
+attach_multiple_body()
+{
+ geli_test_setup
+
+ sectors=100
+ attach_md md0 -t malloc -s `expr $sectors + 1`
+ attach_md md1 -t malloc -s `expr $sectors + 1`
+ attach_md md2 -t malloc -s `expr $sectors + 1`
+ atf_check dd if=/dev/random of=keyfile bs=512 count=16 status=none
+
+ atf_check geli init -B none -P -K keyfile ${md0}
+ atf_check geli init -B none -P -K keyfile ${md1}
+ atf_check geli init -B none -P -K keyfile ${md2}
+ atf_check geli attach -p -k keyfile ${md0} ${md1} ${md2}
+ # verify that it did create all 3 geli devices
+ atf_check -s exit:0 test -c /dev/${md0}.eli
+ atf_check -s exit:0 test -c /dev/${md1}.eli
+ atf_check -s exit:0 test -c /dev/${md2}.eli
+}
+attach_multiple_cleanup()
+{
+ geli_test_cleanup
+}
+
+atf_test_case nokey cleanup
+nokey_head()
+{
+ atf_set "descr" "geli attach fails if called with no key component"
+ atf_set "require.user" "root"
+}
+nokey_body()
+{
+ geli_test_setup
+
+ sectors=100
+ attach_md md -t malloc -s `expr $sectors + 1`
+ atf_check dd if=/dev/random of=keyfile bs=512 count=16 status=none
+
+ atf_check geli init -B none -P -K keyfile ${md}
+ atf_check -s not-exit:0 -e match:"No key components given" \
+ geli attach -p ${md} 2>/dev/null
+}
+nokey_cleanup()
+{
+ geli_test_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case attach_d
+ atf_add_test_case attach_r
+ atf_add_test_case attach_multiple
+ atf_add_test_case attach_multiple_fails
+ atf_add_test_case nokey
+}
diff --git a/tests/sys/geom/class/eli/conf.sh b/tests/sys/geom/class/eli/conf.sh
new file mode 100644
index 000000000000..05ee4e9bb550
--- /dev/null
+++ b/tests/sys/geom/class/eli/conf.sh
@@ -0,0 +1,94 @@
+#!/bin/sh
+
+class="eli"
+base=$(atf_get ident)
+MAX_SECSIZE=8192
+
+attach_md()
+{
+ local _md
+ local rv=$1
+ shift
+
+ [ -c /dev/mdctl ] || atf_skip "no /dev/mdctl to create md devices"
+ _md=$(mdconfig -a "$@") || atf_fail "failed to allocate md(4)"
+ echo $_md >> $TEST_MDS_FILE || exit
+ eval "${rv}='${_md}'"
+}
+
+# Execute `func` for each combination of cipher, sectorsize, and hmac algo
+# `func` usage should be:
+# func <cipher> <aalgo> <secsize>
+for_each_geli_config() {
+ func=$1
+ backing_filename=$2
+
+ # Double the sector size to allow for the HMACs' storage space.
+ osecsize=$(( $MAX_SECSIZE * 2 ))
+ # geli needs 512B for the label.
+ bytes=`expr $osecsize \* $sectors + 512`b
+
+ if [ -n "$backing_filename" ]; then
+ # Use a file-backed md(4) device, so we can deliberatly corrupt
+ # it without detaching the geli device first.
+ truncate -s $bytes backing_file
+ attach_md md -t vnode -f backing_file
+ else
+ attach_md md -t malloc -s $bytes
+ fi
+
+ for cipher in aes-xts:128 aes-xts:256 \
+ aes-cbc:128 aes-cbc:192 aes-cbc:256 \
+ camellia-cbc:128 camellia-cbc:192 camellia-cbc:256; do
+ ealgo=${cipher%%:*}
+ keylen=${cipher##*:}
+ for aalgo in hmac/sha1 hmac/ripemd160 hmac/sha256 \
+ hmac/sha384 hmac/sha512; do
+ for secsize in 512 1024 2048 4096 $MAX_SECSIZE; do
+ ${func} $cipher $aalgo $secsize
+ geli detach ${md} 2>/dev/null
+ done
+ done
+ done
+}
+
+# Execute `func` for each combination of cipher, and sectorsize, with no hmac
+# `func` usage should be:
+# func <cipher> <secsize>
+for_each_geli_config_nointegrity() {
+ func=$1
+
+ # geli needs 512B for the label.
+ bytes=`expr $MAX_SECSIZE \* $sectors + 512`b
+ attach_md md -t malloc -s $bytes
+ for cipher in aes-xts:128 aes-xts:256 \
+ aes-cbc:128 aes-cbc:192 aes-cbc:256 \
+ camellia-cbc:128 camellia-cbc:192 camellia-cbc:256; do
+ ealgo=${cipher%%:*}
+ keylen=${cipher##*:}
+ for secsize in 512 1024 2048 4096 $MAX_SECSIZE; do
+ ${func} $cipher $secsize
+ geli detach ${md} 2>/dev/null
+ done
+ done
+}
+
+geli_test_cleanup()
+{
+ if [ -f "$TEST_MDS_FILE" ]; then
+ while read md; do
+ [ -c /dev/${md}.eli ] && \
+ geli detach $md.eli 2>/dev/null
+ mdconfig -d -u $md 2>/dev/null
+ done < $TEST_MDS_FILE
+ fi
+ true
+}
+
+geli_test_setup()
+{
+ geom_atf_test_setup
+}
+
+ATF_TEST=true
+. `dirname $0`/../geom_subr.sh
diff --git a/tests/sys/geom/class/eli/configure_test.sh b/tests/sys/geom/class/eli/configure_test.sh
new file mode 100644
index 000000000000..4185b68c13ec
--- /dev/null
+++ b/tests/sys/geom/class/eli/configure_test.sh
@@ -0,0 +1,59 @@
+
+. $(atf_get_srcdir)/conf.sh
+
+atf_test_case configure_b_B cleanup
+configure_b_B_head()
+{
+ atf_set "descr" "geli configure -b will set the BOOT flag"
+ atf_set "require.user" "root"
+}
+configure_b_B_body()
+{
+ geli_test_setup
+
+ sectors=100
+ attach_md md -t malloc -s `expr $sectors + 1`
+
+ atf_check geli init -B none -P -K /dev/null ${md}
+
+ atf_check -s exit:0 -o match:'flags: 0x200$' geli dump ${md}
+
+ atf_check geli init -B none -b -P -K /dev/null ${md}
+
+ atf_check -s exit:0 -o match:'flags: 0x202$' geli dump ${md}
+
+ atf_check geli configure -B ${md}
+
+ atf_check -s exit:0 -o match:'flags: 0x200$' geli dump ${md}
+
+ atf_check geli configure -b ${md}
+
+ atf_check -s exit:0 -o match:'flags: 0x202$' geli dump ${md}
+
+ atf_check geli attach -p -k /dev/null ${md}
+
+ atf_check -s exit:0 -o match:'^Flags: .*BOOT' geli list ${md}.eli
+
+ atf_check geli configure -B ${md}
+
+ atf_check -o not-match:'^Flags: .*BOOT' geli list ${md}.eli
+
+ atf_check -s exit:0 -o match:'flags: 0x200$' geli dump ${md}
+
+ atf_check geli configure -b ${md}
+
+ atf_check -s exit:0 -o match:'^Flags: .*BOOT' geli list ${md}.eli
+
+ atf_check -s exit:0 -o match:'flags: 0x202$' geli dump ${md}
+
+ atf_check geli detach ${md}
+}
+configure_b_B_cleanup()
+{
+ geli_test_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case configure_b_B
+}
diff --git a/tests/sys/geom/class/eli/delkey_test.sh b/tests/sys/geom/class/eli/delkey_test.sh
new file mode 100644
index 000000000000..2d03a6555ab3
--- /dev/null
+++ b/tests/sys/geom/class/eli/delkey_test.sh
@@ -0,0 +1,113 @@
+#!/bin/sh
+
+. $(atf_get_srcdir)/conf.sh
+
+atf_test_case delkey cleanup
+delkey_head()
+{
+ atf_set "descr" "geli delkey can destroy the master key"
+ atf_set "require.user" "root"
+}
+delkey_body()
+{
+ geli_test_setup
+
+ sectors=100
+ attach_md md -t malloc -s `expr $sectors + 1`
+
+ atf_check dd if=/dev/random of=keyfile1 bs=512 count=16 status=none
+ atf_check dd if=/dev/random of=keyfile2 bs=512 count=16 status=none
+ atf_check dd if=/dev/random of=keyfile3 bs=512 count=16 status=none
+ atf_check dd if=/dev/random of=keyfile4 bs=512 count=16 status=none
+
+ atf_check geli init -B none -P -K keyfile1 ${md}
+ atf_check geli attach -p -k keyfile1 ${md}
+ atf_check -s exit:0 -o ignore geli setkey -n 1 -P -K keyfile2 ${md}
+
+ # Remove key 0 for attached provider.
+ atf_check geli delkey -n 0 ${md}
+ atf_check geli detach ${md}
+
+ # We cannot use keyfile1 anymore.
+ atf_check -s not-exit:0 -e match:"Wrong key" \
+ geli attach -p -k keyfile1 ${md}
+
+ # Attach with key 1.
+ atf_check geli attach -p -k keyfile2 ${md}
+
+ # We cannot remove last key without -f option (for attached provider).
+ atf_check -s not-exit:0 -e match:"This is the last Master Key" \
+ geli delkey -n 1 ${md}
+
+ # Remove last key for attached provider.
+ atf_check geli delkey -f -n 1 ${md}
+
+ # If there are no valid keys, but provider is attached, we can save situation.
+ atf_check -s exit:0 -o ignore geli setkey -n 0 -P -K keyfile3 ${md}
+ atf_check geli detach ${md}
+
+ # We cannot use keyfile2 anymore.
+ atf_check -s not-exit:0 -e match:"Wrong key" \
+ geli attach -p -k keyfile2 ${md}
+
+ # Attach with key 0.
+ atf_check geli attach -p -k keyfile3 ${md}
+
+ # Setup key 1.
+ atf_check -s exit:0 -o ignore geli setkey -n 1 -P -K keyfile4 ${md}
+ atf_check geli detach ${md}
+
+ # Remove key 1 for detached provider.
+ atf_check geli delkey -n 1 ${md}
+
+ # We cannot use keyfile4 anymore.
+ atf_check -s not-exit:0 -e match:"Wrong key" \
+ geli attach -p -k keyfile4 ${md}
+
+ # We cannot remove last key without -f option (for detached provider).
+ atf_check -s not-exit:0 -e match:"This is the last Master Key" \
+ geli delkey -n 0 ${md}
+
+ # Remove last key for detached provider.
+ atf_check geli delkey -f -n 0 ${md}
+
+ # We cannot use keyfile3 anymore.
+ atf_check -s not-exit:0 -e match:"No valid keys" \
+ geli attach -p -k keyfile3 ${md}
+}
+delkey_cleanup()
+{
+ geli_test_cleanup
+}
+
+atf_test_case delkey_readonly cleanup
+delkey_readonly_head()
+{
+ atf_set "descr" "geli delkey cannot work on a read-only provider"
+ atf_set "require.user" "root"
+}
+delkey_readonly_body()
+{
+ geli_test_setup
+
+ sectors=100
+ attach_md md -t malloc -s `expr $sectors + 1`
+ atf_check dd if=/dev/random of=keyfile bs=512 count=16 status=none
+
+ atf_check geli init -B none -P -K keyfile ${md}
+ atf_check geli attach -r -p -k keyfile ${md}
+
+ atf_check -s not-exit:0 -e match:"read-only" geli delkey -n 0 ${md}
+ # Even with -f (force) it should still fail
+ atf_check -s not-exit:0 -e match:"read-only" geli delkey -f -n 0 ${md}
+}
+delkey_readonly_cleanup()
+{
+ geli_test_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case delkey
+ atf_add_test_case delkey_readonly
+}
diff --git a/tests/sys/geom/class/eli/detach_test.sh b/tests/sys/geom/class/eli/detach_test.sh
new file mode 100644
index 000000000000..6acae8dea732
--- /dev/null
+++ b/tests/sys/geom/class/eli/detach_test.sh
@@ -0,0 +1,46 @@
+
+. $(atf_get_srcdir)/conf.sh
+
+atf_test_case detach_l cleanup
+detach_l_head()
+{
+ atf_set "descr" "geli detach -l will cause a provider to detach on last close"
+ atf_set "require.user" "root"
+}
+detach_l_body()
+{
+ geli_test_setup
+
+ sectors=100
+ attach_md md -t malloc -s `expr $sectors + 1`
+
+ atf_check dd if=/dev/random of=keyfile bs=512 count=16 status=none
+
+ atf_check geli init -B none -P -K keyfile ${md}
+ atf_check geli attach -p -k keyfile ${md}
+
+ # Be sure it doesn't detach before 'detach -l'.
+ atf_check dd if=/dev/${md}.eli of=/dev/null status=none
+ sleep 1
+ if [ ! -c /dev/${md}.eli ]; then
+ atf_fail "provider detached on last close without detach -l"
+ fi
+ atf_check geli detach -l ${md}
+ if [ ! -c /dev/${md}.eli ]; then
+ atf_fail "Provider detached before last close"
+ fi
+ atf_check dd if=/dev/${md}.eli of=/dev/null status=none
+ sleep 1
+ if [ -c /dev/${md}.eli ]; then
+ atf_fail "Provider did not detach on last close"
+ fi
+}
+detach_l_cleanup()
+{
+ geli_test_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case detach_l
+}
diff --git a/tests/sys/geom/class/eli/gentestvect.py b/tests/sys/geom/class/eli/gentestvect.py
new file mode 100644
index 000000000000..e1bf6cf39ffd
--- /dev/null
+++ b/tests/sys/geom/class/eli/gentestvect.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+
+from hashlib import pbkdf2_hmac
+import hashlib
+import itertools
+import string
+
+#From: https://stackoverflow.com/questions/14945095/how-to-escape-string-for-generated-c
+def cstring(s, encoding='ascii'):
+ if isinstance(s, unicode):
+ s = s.encode(encoding)
+
+ result = ''
+ for c in s:
+ if not (32 <= ord(c) < 127) or c in ('\\', '"'):
+ result += '\\%03o' % ord(c)
+ else:
+ result += c
+
+ return '"' + result + '"'
+
+intarr = lambda y: ', '.join(map(lambda x: str(ord(x)), y))
+
+_randfd = open('/dev/urandom', 'rb')
+_maketrans = string.maketrans('', '')
+def randgen(l, delchrs=None):
+ if delchrs is None:
+ return _randfd.read(l)
+
+ s = ''
+ while len(s) < l:
+ s += string.translate(_randfd.read(l - len(s)), _maketrans,
+ delchrs)
+ return s
+
+def printhmacres(salt, passwd, itr, hmacout):
+ print '\t{ %s, %d, %s, %d, %s, %d },' % (cstring(salt), len(salt),
+ cstring(passwd), itr, cstring(hmacout), len(hmacout))
+
+if __name__ == '__main__':
+ import sys
+
+ if len(sys.argv) == 1:
+ hashfun = 'sha512'
+ else:
+ hashfun = sys.argv[1]
+
+ if hashfun not in hashlib.algorithms:
+ print 'Invalid hash function: %s' % `hashfun`
+ sys.exit(1)
+
+ print '/* Test Vectors for PBKDF2-%s */' % hashfun.upper()
+ print '\t/* salt, saltlen, passwd, itr, hmacout, hmacoutlen */'
+ for saltl in xrange(8, 64, 8):
+ for itr in itertools.chain(xrange(100, 1000, 100), xrange(1000,
+ 10000, 1000)):
+ for passlen in xrange(8, 80, 8):
+ salt = randgen(saltl)
+ passwd = randgen(passlen, '\x00')
+ hmacout = pbkdf2_hmac(hashfun, passwd, salt,
+ itr)
+ printhmacres(salt, passwd, itr, hmacout)
diff --git a/tests/sys/geom/class/eli/hmac_test.c b/tests/sys/geom/class/eli/hmac_test.c
new file mode 100644
index 000000000000..6e22c23fb41b
--- /dev/null
+++ b/tests/sys/geom/class/eli/hmac_test.c
@@ -0,0 +1,40 @@
+/*
+ */
+
+#include <sys/param.h>
+#include <atf-c.h>
+
+#include <geom/eli/pkcs5v2.h>
+
+const struct {
+ char *salt;
+ size_t saltlen;
+ char *passwd;
+ int iterations;
+ char *hmacout;
+ size_t hmaclen;
+} testdata[] = {
+#include "testvect.h"
+};
+
+ATF_TC_WITHOUT_HEAD(hmactest);
+ATF_TC_BODY(hmactest, tc)
+{
+ size_t i;
+ uint8_t hmacout[64];
+
+ for (i = 0; i < nitems(testdata); i++) {
+ pkcs5v2_genkey(hmacout, testdata[i].hmaclen,
+ (uint8_t *)testdata[i].salt, testdata[i].saltlen,
+ testdata[i].passwd, testdata[i].iterations);
+ ATF_REQUIRE(bcmp(hmacout, testdata[i].hmacout,
+ testdata[i].hmaclen) == 0);
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, hmactest);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/geom/class/eli/init_test.sh b/tests/sys/geom/class/eli/init_test.sh
new file mode 100644
index 000000000000..534fc724d05d
--- /dev/null
+++ b/tests/sys/geom/class/eli/init_test.sh
@@ -0,0 +1,379 @@
+#!/bin/sh
+
+. $(atf_get_srcdir)/conf.sh
+
+init_test()
+{
+ cipher=$1
+ secsize=$2
+ ealgo=${cipher%%:*}
+ keylen=${cipher##*:}
+
+ atf_check -s exit:0 -e ignore \
+ geli init -B none -e $ealgo -l $keylen -P -K keyfile \
+ -s $secsize ${md}
+ atf_check geli attach -p -k keyfile ${md}
+
+ atf_check dd if=rnd of=/dev/${md}.eli bs=${secsize} count=${sectors} \
+ status=none
+
+ md_rnd=`dd if=rnd bs=${secsize} count=${sectors} status=none | md5`
+ atf_check_equal 0 $?
+ md_ddev=`dd if=/dev/${md}.eli bs=${secsize} count=${sectors} status=none | md5`
+ atf_check_equal 0 $?
+ md_edev=`dd if=/dev/${md} bs=${secsize} count=${sectors} status=none | md5`
+ atf_check_equal 0 $?
+
+ if [ ${md_rnd} != ${md_ddev} ]; then
+ atf_fail "Miscompare for ealgo=${ealgo} keylen=${keylen} sec=${secsize}"
+ fi
+ if [ ${md_rnd} == ${md_edev} ]; then
+ atf_fail "Data was not encrypted for ealgo=${ealgo} keylen=${keylen} sec=${secsize}"
+ fi
+}
+atf_test_case init cleanup
+init_head()
+{
+ atf_set "descr" "Basic I/O with geli"
+ atf_set "require.user" "root"
+ atf_set "timeout" 600
+}
+init_body()
+{
+ geli_test_setup
+
+ sectors=32
+
+ atf_check dd if=/dev/random of=keyfile bs=512 count=16 status=none
+ atf_check dd if=/dev/random of=rnd bs=$MAX_SECSIZE count=${sectors} \
+ status=none
+ for_each_geli_config_nointegrity init_test
+}
+init_cleanup()
+{
+ geli_test_cleanup
+}
+
+atf_test_case init_B cleanup
+init_B_head()
+{
+ atf_set "descr" "init -B can select an alternate backup metadata file"
+ atf_set "require.user" "root"
+}
+init_B_body()
+{
+ geli_test_setup
+
+ sectors=100
+
+ atf_check dd if=/dev/random of=keyfile bs=512 count=16 status=none
+
+ attach_md md -t malloc -s $sectors
+
+ # -B none
+ rm -f /var/backups/${md}.eli
+ atf_check -s exit:0 -o ignore geli init -B none -P -K keyfile ${md}
+ if [ -f /var/backups/${md}.eli ]; then
+ atf_fail "geli created a backup file even with -B none"
+ fi
+
+ # no -B
+ rm -f /var/backups/${md}.eli
+ atf_check -s exit:0 -o ignore geli init -P -K keyfile ${md}
+ if [ ! -f /var/backups/${md}.eli ]; then
+ atf_fail "geli did not create a backup file"
+ fi
+ atf_check geli clear ${md}
+ atf_check -s not-exit:0 -e ignore geli attach -p -k keyfile ${md}
+ atf_check -s exit:0 -o ignore geli restore /var/backups/${md}.eli ${md}
+ atf_check -s exit:0 -o ignore geli attach -p -k keyfile ${md}
+ atf_check geli detach ${md}
+ rm -f /var/backups/${md}.eli
+
+ # -B file
+ rm -f backupfile
+ atf_check -s exit:0 -o ignore \
+ geli init -B backupfile -P -K keyfile ${md}
+ if [ ! -f backupfile ]; then
+ atf_fail "geli init -B did not create a backup file"
+ fi
+ atf_check geli clear ${md}
+ atf_check -s not-exit:0 -e ignore geli attach -p -k keyfile ${md}
+ atf_check geli restore backupfile ${md}
+ atf_check geli attach -p -k keyfile ${md}
+}
+init_B_cleanup()
+{
+ geli_test_cleanup
+}
+
+atf_test_case init_J cleanup
+init_J_head()
+{
+ atf_set "descr" "init -J accepts a passfile"
+ atf_set "require.user" "root"
+}
+init_J_body()
+{
+ geli_test_setup
+
+ sectors=100
+ attach_md md -t malloc -s `expr $sectors + 1`
+
+ atf_check dd if=/dev/random of=keyfile0 bs=512 count=16 status=none
+ atf_check dd if=/dev/random of=keyfile1 bs=512 count=16 status=none
+ dd if=/dev/random bs=512 count=16 status=none | sha1 > passfile0
+ atf_check_equal 0 $?
+ dd if=/dev/random bs=512 count=16 status=none | sha1 > passfile1
+ atf_check_equal 0 $?
+
+ for iter in -1 0 64; do
+ atf_check -s not-exit:0 -e ignore \
+ geli init -i ${iter} -B none -J passfile0 -P ${md}
+ atf_check -s not-exit:0 -e ignore \
+ geli init -i ${iter} -B none -J passfile0 -P -K keyfile0 ${md}
+ atf_check geli init -i ${iter} -B none -J passfile0 -K keyfile0 ${md}
+ atf_check -s not-exit:0 -e ignore \
+ geli attach -k keyfile0 -p ${md}
+ atf_check -s not-exit:0 -e ignore \
+ geli attach -j passfile0 ${md}
+ atf_check -s not-exit:0 -e ignore \
+ geli attach -j keyfile0 ${md}
+ atf_check -s not-exit:0 -e ignore \
+ geli attach -k passfile0 -p ${md}
+ atf_check -s not-exit:0 -e ignore \
+ geli attach -j keyfile0 -k passfile0 ${md}
+ atf_check -s not-exit:0 -e ignore \
+ geli attach -j keyfile0 -k keyfile0 ${md}
+ atf_check -s not-exit:0 -e ignore \
+ geli attach -j passfile0 -k passfile0 ${md}
+ atf_check -s exit:0 -e ignore \
+ geli attach -j passfile0 -k keyfile0 ${md}
+ atf_check -s exit:0 -e ignore geli detach ${md}
+ atf_check -s exit:0 -e ignore -x \
+ "cat keyfile0 | geli attach -j passfile0 -k - ${md}"
+ atf_check -s exit:0 -e ignore geli detach ${md}
+ atf_check -s exit:0 -e ignore -x \
+ "cat passfile0 | geli attach -j - -k keyfile0 ${md}"
+ atf_check -s exit:0 -e ignore geli detach ${md}
+
+ atf_check -s not-exit:0 -e ignore \
+ geli init -i ${iter} -B none -J passfile0 -J passfile1 -P ${md}
+ atf_check -s not-exit:0 -e ignore \
+ geli init -i ${iter} -B none -J passfile0 -J passfile1 -P -K keyfile0 -K keyfile1 ${md}
+ atf_check -s exit:0 -e ignore \
+ geli init -i ${iter} -B none -J passfile0 -J passfile1 -K keyfile0 -K keyfile1 ${md}
+ atf_check -s not-exit:0 -e ignore \
+ geli attach -k keyfile0 -p ${md}
+ atf_check -s not-exit:0 -e ignore \
+ geli attach -k keyfile1 -p ${md}
+ atf_check -s not-exit:0 -e ignore \
+ geli attach -j passfile0 ${md}
+ atf_check -s not-exit:0 -e ignore \
+ geli attach -j passfile1 ${md}
+ atf_check -s not-exit:0 -e ignore \
+ geli attach -k keyfile0 -k keyfile1 -p ${md}
+ atf_check -s not-exit:0 -e ignore \
+ geli attach -j passfile0 -j passfile1 ${md}
+ atf_check -s not-exit:0 -e ignore \
+ geli attach -k keyfile0 -j passfile0 ${md}
+ atf_check -s not-exit:0 -e ignore \
+ geli attach -k keyfile0 -j passfile1 ${md}
+ atf_check -s not-exit:0 -e ignore \
+ geli attach -k keyfile1 -j passfile0 ${md}
+ atf_check -s not-exit:0 -e ignore \
+ geli attach -k keyfile1 -j passfile1 ${md}
+ atf_check -s not-exit:0 -e ignore \
+ geli attach -k keyfile0 -j passfile0 -j passfile1 ${md}
+ atf_check -s not-exit:0 -e ignore \
+ geli attach -k keyfile1 -j passfile0 -j passfile1 ${md}
+ atf_check -s not-exit:0 -e ignore \
+ geli attach -k keyfile0 -k keyfile1 -j passfile0 ${md}
+ atf_check -s not-exit:0 -e ignore \
+ geli attach -k keyfile0 -k keyfile1 -j passfile1 ${md}
+ atf_check -s not-exit:0 -e ignore \
+ geli attach -k keyfile1 -k keyfile0 -j passfile0 -j passfile1 ${md}
+ atf_check -s not-exit:0 -e ignore \
+ geli attach -k keyfile0 -k keyfile1 -j passfile1 -j passfile0 ${md}
+ atf_check -s not-exit:0 -e ignore \
+ geli attach -k keyfile1 -k keyfile0 -j passfile1 -j passfile0 ${md}
+ atf_check -s exit:0 -e ignore \
+ geli attach -j passfile0 -j passfile1 -k keyfile0 -k keyfile1 ${md}
+ atf_check -s exit:0 -e ignore geli detach ${md}
+ atf_check -s exit:0 -e ignore -x \
+ "cat passfile0 | geli attach -j - -j passfile1 -k keyfile0 -k keyfile1 ${md}"
+ atf_check -s exit:0 -e ignore geli detach ${md}
+ atf_check -s exit:0 -e ignore -x \
+ "cat passfile1 | geli attach -j passfile0 -j - -k keyfile0 -k keyfile1 ${md}"
+ atf_check -s exit:0 -e ignore geli detach ${md}
+ atf_check -s exit:0 -e ignore -x \
+ "cat keyfile0 | geli attach -j passfile0 -j passfile1 -k - -k keyfile1 ${md}"
+ atf_check -s exit:0 -e ignore geli detach ${md}
+ atf_check -s exit:0 -e ignore -x \
+ "cat keyfile1 | geli attach -j passfile0 -j passfile1 -k keyfile0 -k - ${md}"
+ atf_check -s exit:0 -e ignore geli detach ${md}
+ atf_check -s exit:0 -e ignore -x \
+ "cat keyfile0 keyfile1 | geli attach -j passfile0 -j passfile1 -k - ${md}"
+ atf_check -s exit:0 -e ignore geli detach ${md}
+ atf_check -s exit:0 -e ignore -x \
+ "cat passfile0 passfile1 | awk '{printf \"%s\", \$0}' | geli attach -j - -k keyfile0 -k keyfile1 ${md}"
+ atf_check -s exit:0 -e ignore geli detach ${md}
+ done
+}
+init_J_cleanup()
+{
+ geli_test_cleanup
+}
+
+init_a_test()
+{
+ cipher=$1
+ aalgo=$2
+ secsize=$3
+ ealgo=${cipher%%:*}
+ keylen=${cipher##*:}
+
+ atf_check -s exit:0 -e ignore \
+ geli init -B none -a $aalgo -e $ealgo -l $keylen -P -K keyfile \
+ -s $secsize ${md}
+ atf_check geli attach -p -k keyfile ${md}
+
+ atf_check dd if=rnd of=/dev/${md}.eli bs=${secsize} count=${sectors} status=none
+
+ md_rnd=`dd if=rnd bs=${secsize} count=${sectors} status=none | md5`
+ atf_check_equal 0 $?
+ md_ddev=`dd if=/dev/${md}.eli bs=${secsize} count=${sectors} status=none | md5`
+ atf_check_equal 0 $?
+
+ if [ ${md_rnd} != ${md_ddev} ]; then
+ atf_fail "Miscompare for aalgo=${aalgo} ealgo=${ealgo} keylen=${keylen} sec=${secsize}"
+ fi
+}
+atf_test_case init_a cleanup
+init_a_head()
+{
+ atf_set "descr" "I/O with geli and HMACs"
+ atf_set "require.user" "root"
+ atf_set "timeout" 3600
+}
+init_a_body()
+{
+ geli_test_setup
+
+ sectors=100
+
+ atf_check dd if=/dev/random of=keyfile bs=512 count=16 status=none
+ atf_check dd if=/dev/random of=rnd bs=$MAX_SECSIZE count=${sectors} \
+ status=none
+ for_each_geli_config init_a_test
+ true
+}
+init_a_cleanup()
+{
+ geli_test_cleanup
+}
+
+init_alias_test() {
+ ealgo=$1
+ keylen=$2
+ expected_ealgo=$3
+ expected_keylen=$4
+
+ atf_check geli init -B none -e $ealgo -l $keylen -P -K keyfile ${md}
+ atf_check geli attach -p -k keyfile ${md}
+ real_ealgo=`geli list ${md}.eli | awk '/EncryptionAlgorithm/ {print $2}'`
+ real_keylen=`geli list ${md}.eli | awk '/KeyLength/ {print $2}'`
+
+ if [ "${real_ealgo}" != "${expected_ealgo}" ]; then
+ atf_fail "expected ${expected_ealgo} but got ${real_ealgo}"
+ fi
+
+ if [ "${real_keylen}" != "${expected_keylen}" ]; then
+ atf_fail "expected ${expected_keylen} but got ${real_keylen}"
+ fi
+ atf_check geli detach ${md}
+}
+atf_test_case init_alias cleanup
+init_alias_head()
+{
+ atf_set "descr" "geli init accepts cipher aliases"
+ atf_set "require.user" "root"
+}
+init_alias_body()
+{
+ geli_test_setup
+
+ attach_md md -t malloc -s 1024k
+ atf_check dd if=/dev/random of=keyfile bs=512 count=16 status=none
+
+ for spec in aes:0:AES-XTS:128 aes:128:AES-XTS:128 aes:256:AES-XTS:256 \
+ camellia:0:CAMELLIA-CBC:128 camellia:128:CAMELLIA-CBC:128 \
+ camellia:256:CAMELLIA-CBC:256 ; do
+
+ ealgo=`echo $spec | cut -d : -f 1`
+ keylen=`echo $spec | cut -d : -f 2`
+ expected_ealgo=`echo $spec | cut -d : -f 3`
+ expected_keylen=`echo $spec | cut -d : -f 4`
+
+ init_alias_test $ealgo $keylen $expected_ealgo $expected_keylen
+ done
+}
+init_alias_cleanup()
+{
+ geli_test_cleanup
+}
+
+atf_test_case init_i_P cleanup
+init_i_P_head()
+{
+ atf_set "descr" "geli: Options -i and -P are mutually exclusive"
+ atf_set "require.user" "root"
+}
+init_i_P_body()
+{
+ geli_test_setup
+
+ sectors=100
+ attach_md md -t malloc -s `expr $sectors + 1`
+
+ atf_check dd if=/dev/random of=keyfile bs=512 count=16 status=none
+
+ atf_check -s not-exit:0 -e "match:Options -i and -P are mutually exclusive"\
+ geli init -B none -i 64 -P -K keyfile $md
+}
+init_i_P_cleanup()
+{
+ geli_test_cleanup
+}
+
+atf_test_case nokey cleanup
+nokey_head()
+{
+ atf_set "descr" "geli init fails if called with no key component"
+ atf_set "require.user" "root"
+}
+nokey_body()
+{
+ geli_test_setup
+
+ sectors=100
+ attach_md md -t malloc -s `expr $sectors + 1`
+
+ atf_check -s not-exit:0 -e match:"No key components given" \
+ geli init -B none -P ${md}
+}
+nokey_cleanup()
+{
+ geli_test_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case init
+ atf_add_test_case init_B
+ atf_add_test_case init_J
+ atf_add_test_case init_a
+ atf_add_test_case init_alias
+ atf_add_test_case init_i_P
+ atf_add_test_case nokey
+}
diff --git a/tests/sys/geom/class/eli/integrity_test.sh b/tests/sys/geom/class/eli/integrity_test.sh
new file mode 100644
index 000000000000..c7f7cc0df0a3
--- /dev/null
+++ b/tests/sys/geom/class/eli/integrity_test.sh
@@ -0,0 +1,163 @@
+
+. $(atf_get_srcdir)/conf.sh
+
+copy_test() {
+ cipher=$1
+ aalgo=$2
+ secsize=$3
+ ealgo=${cipher%%:*}
+ keylen=${cipher##*:}
+
+ atf_check -s exit:0 -e ignore \
+ geli init -B none -a $aalgo -e $ealgo -l $keylen -P \
+ -K keyfile -s $secsize ${md}
+ atf_check geli attach -p -k keyfile ${md}
+
+ atf_check dd if=rnd of=/dev/${md}.eli bs=${secsize} count=1 status=none
+
+ # Copy first small sector to the second small sector.
+ # This should be detected as corruption.
+ atf_check dd if=backing_file of=sector bs=512 count=1 \
+ conv=notrunc status=none
+ atf_check dd if=sector of=backing_file bs=512 count=1 seek=1 \
+ conv=notrunc status=none
+
+ atf_check -s not-exit:0 -e ignore \
+ dd if=/dev/${md}.eli of=/dev/null bs=${secsize} count=1
+
+ # Fix the corruption
+ atf_check dd if=rnd of=/dev/${md}.eli bs=${secsize} count=2 status=none
+ atf_check dd if=/dev/${md}.eli of=/dev/null bs=${secsize} count=2 \
+ status=none
+
+ # Copy first big sector to the second big sector.
+ # This should be detected as corruption.
+ ms=`diskinfo /dev/${md} | awk '{print $3 - 512}'`
+ ns=`diskinfo /dev/${md}.eli | awk '{print $4}'`
+ usecsize=`echo "($ms / $ns) - (($ms / $ns) % 512)" | bc`
+ atf_check dd if=backing_file bs=512 count=$(( ${usecsize} / 512 )) \
+ seek=$(( $secsize / 512 )) of=sector conv=notrunc status=none
+ atf_check dd of=backing_file bs=512 count=$(( ${usecsize} / 512 )) \
+ seek=$(( $secsize / 256 )) if=sector conv=notrunc status=none
+ atf_check -s not-exit:0 -e ignore \
+ dd if=/dev/${md}.eli of=/dev/null bs=${secsize} count=$ns
+}
+
+atf_test_case copy cleanup
+copy_head()
+{
+ atf_set "descr" "geli will detect misdirected writes as corruption"
+ atf_set "require.user" "root"
+ atf_set "timeout" 3600
+}
+copy_body()
+{
+ geli_test_setup
+
+ sectors=2
+
+ atf_check dd if=/dev/random of=keyfile bs=512 count=16 status=none
+ dd if=/dev/random of=rnd bs=${MAX_SECSIZE} count=${sectors} status=none
+
+ for_each_geli_config copy_test backing_file
+}
+copy_cleanup()
+{
+ geli_test_cleanup
+}
+
+
+data_test() {
+ cipher=$1
+ aalgo=$2
+ secsize=$3
+ ealgo=${cipher%%:*}
+ keylen=${cipher##*:}
+
+ atf_check -s exit:0 -e ignore \
+ geli init -B none -a $aalgo -e $ealgo -l $keylen -P -K keyfile \
+ -s $secsize ${md}
+
+ # Corrupt 8 bytes of data.
+ atf_check dd if=/dev/${md} of=sector bs=512 count=1 status=none
+ atf_check dd if=rnd of=sector bs=1 count=8 seek=64 conv=notrunc status=none
+ atf_check dd if=sector of=/dev/${md} bs=512 count=1 status=none
+ atf_check geli attach -p -k keyfile ${md}
+
+ # Try to read from the corrupt sector
+ atf_check -s not-exit:0 -e ignore \
+ dd if=/dev/${md}.eli of=/dev/null bs=${secsize} count=1
+}
+
+atf_test_case data cleanup
+data_head()
+{
+ atf_set "descr" "With HMACs, geli will detect data corruption"
+ atf_set "require.user" "root"
+ atf_set "timeout" 1800
+}
+data_body()
+{
+ geli_test_setup
+
+ sectors=2
+
+ atf_check dd if=/dev/random of=keyfile bs=512 count=16 status=none
+ dd if=/dev/random of=rnd bs=${MAX_SECSIZE} count=${sectors} status=none
+ for_each_geli_config data_test
+}
+data_cleanup()
+{
+ geli_test_cleanup
+}
+
+hmac_test() {
+ cipher=$1
+ aalgo=$2
+ secsize=$3
+ ealgo=${cipher%%:*}
+ keylen=${cipher##*:}
+
+ atf_check -s exit:0 -e ignore \
+ geli init -B none -a $aalgo -e $ealgo -l $keylen -P -K keyfile \
+ -s $secsize ${md}
+
+ # Corrupt 8 bytes of HMAC.
+ atf_check dd if=/dev/${md} of=sector bs=512 count=1 status=none
+ atf_check dd if=rnd of=sector bs=1 count=16 conv=notrunc status=none
+ atf_check dd if=sector of=/dev/${md} bs=512 count=1 status=none
+ atf_check geli attach -p -k keyfile ${md}
+
+ # Try to read from the corrupt sector
+ atf_check -s not-exit:0 -e ignore \
+ dd if=/dev/${md}.eli of=/dev/null bs=${secsize} count=1
+}
+
+atf_test_case hmac cleanup
+hmac_head()
+{
+ atf_set "descr" "geli will detect corruption of HMACs"
+ atf_set "require.user" "root"
+ atf_set "timeout" 1800
+}
+hmac_body()
+{
+ geli_test_setup
+
+ sectors=2
+
+ atf_check dd if=/dev/random of=keyfile bs=512 count=16 status=none
+ dd if=/dev/random of=rnd bs=${MAX_SECSIZE} count=${sectors} status=none
+ for_each_geli_config hmac_test
+}
+hmac_cleanup()
+{
+ geli_test_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case copy
+ atf_add_test_case data
+ atf_add_test_case hmac
+}
diff --git a/tests/sys/geom/class/eli/kill_test.sh b/tests/sys/geom/class/eli/kill_test.sh
new file mode 100644
index 000000000000..eeaa440f11a9
--- /dev/null
+++ b/tests/sys/geom/class/eli/kill_test.sh
@@ -0,0 +1,101 @@
+
+. $(atf_get_srcdir)/conf.sh
+
+atf_test_case kill cleanup
+kill_head()
+{
+ atf_set "descr" "geli kill will wipe a provider's metadata"
+ atf_set "require.user" "root"
+}
+kill_body()
+{
+ geli_test_setup
+
+ sectors=100
+ attach_md md -t malloc -s `expr $sectors + 1`
+
+ atf_check dd if=/dev/random of=keyfile1 bs=512 count=16 status=none
+ atf_check dd if=/dev/random of=keyfile2 bs=512 count=16 status=none
+
+ atf_check geli init -B none -P -K keyfile1 ${md}
+ atf_check geli attach -p -k keyfile1 ${md}
+ atf_check -s exit:0 -o ignore geli setkey -n 1 -P -K keyfile2 ${md}
+
+ # Kill attached provider.
+ atf_check geli kill ${md}
+ sleep 1
+ # Provider should be automatically detached.
+ if [ -c /dev/${md}.eli ]; then
+ atf_fail "Provider did not detach when killed"
+ fi
+
+ # We cannot use keyfile1 anymore.
+ atf_check -s not-exit:0 -e match:"Cannot read metadata" \
+ geli attach -p -k keyfile1 ${md}
+
+ # We cannot use keyfile2 anymore.
+ atf_check -s not-exit:0 -e match:"Cannot read metadata" \
+ geli attach -p -k keyfile2 ${md}
+
+ atf_check geli init -B none -P -K keyfile1 ${md}
+ atf_check -s exit:0 -o ignore \
+ geli setkey -n 1 -p -k keyfile1 -P -K keyfile2 ${md}
+
+ # Should be possible to attach with keyfile1.
+ atf_check geli attach -p -k keyfile1 ${md}
+ atf_check geli detach ${md}
+
+ # Should be possible to attach with keyfile2.
+ atf_check geli attach -p -k keyfile2 ${md}
+ atf_check geli detach ${md}
+
+ # Kill detached provider.
+ atf_check geli kill ${md}
+
+ # We cannot use keyfile1 anymore.
+ atf_check -s not-exit:0 -e match:"Cannot read metadata" \
+ geli attach -p -k keyfile1 ${md}
+
+ # We cannot use keyfile2 anymore.
+ atf_check -s not-exit:0 -e match:"Cannot read metadata" \
+ geli attach -p -k keyfile2 ${md}
+}
+kill_cleanup()
+{
+ geli_test_cleanup
+}
+
+atf_test_case kill_readonly cleanup
+kill_readonly_head()
+{
+ atf_set "descr" "geli kill will not destroy the keys of a readonly provider"
+ atf_set "require.user" "root"
+}
+kill_readonly_body()
+{
+ geli_test_setup
+
+ sectors=100
+ attach_md md -t malloc -s `expr $sectors + 1`
+ atf_check dd if=/dev/random of=keyfile bs=512 count=16 status=none
+
+ atf_check geli init -B none -P -K keyfile ${md}
+ # Attach read-only
+ atf_check geli attach -r -p -k keyfile ${md}
+
+ atf_check geli kill ${md}
+ # The provider will be detached
+ atf_check [ ! -c /dev/${md}.eli ]
+ # But its keys should not be destroyed
+ atf_check geli attach -p -k keyfile ${md}
+}
+kill_readonly_cleanup()
+{
+ geli_test_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case kill
+ atf_add_test_case kill_readonly
+}
diff --git a/tests/sys/geom/class/eli/misc_test.sh b/tests/sys/geom/class/eli/misc_test.sh
new file mode 100644
index 000000000000..1cbeb0491ffd
--- /dev/null
+++ b/tests/sys/geom/class/eli/misc_test.sh
@@ -0,0 +1,206 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2018 Alan Somers
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/conf.sh
+
+atf_test_case preserve_props cleanup
+preserve_props_head()
+{
+ atf_set "descr" "geli should preserve basic GEOM properties"
+ atf_set "require.user" "root"
+ atf_set "timeout" 15
+}
+preserve_props_body()
+{
+ geli_test_setup
+
+ attach_md md -s1m
+ atf_check geli onetime /dev/${md}
+ md_secsize=$(diskinfo ${md} | cut -wf 2)
+ md_stripesize=$(diskinfo ${md} | cut -wf 5)
+ eli_secsize=$(diskinfo ${md}.eli | cut -wf 2)
+ eli_stripesize=$(diskinfo ${md}.eli | cut -wf 5)
+ atf_check_equal "$md_secsize" "$eli_secsize"
+ atf_check_equal "$md_stripesize" "$eli_stripesize"
+}
+preserve_props_cleanup()
+{
+ geli_test_cleanup
+}
+
+atf_test_case preserve_disk_props cleanup
+preserve_disk_props_head()
+{
+ atf_set "descr" "geli should preserve properties for disks"
+ atf_set "require.user" "root"
+ atf_set "require.config" "disks"
+ atf_set "timeout" 15
+}
+preserve_disk_props_body()
+{
+ geli_test_setup
+
+ disks=`atf_config_get disks`
+ disk=${disks%% *}
+ if [ -z "$disk" ]; then
+ atf_skip "Must define disks (see tests(7))"
+ fi
+ atf_check geli onetime ${disk}
+
+ disk_ident=$(diskinfo -s ${disk})
+ disk_descr=$(diskinfo -v ${disk} | awk '/Disk descr/ {print $1}')
+ disk_rotrate=$(diskinfo -v ${disk} | awk '/Rotation rate/ {print $1}')
+ disk_zonemode=$(diskinfo -v ${disk} | awk '/Zone Mode/ {print $1}')
+ eli_ident=$(diskinfo -s ${disk}.eli)
+ eli_descr=$(diskinfo -v ${disk}.eli | awk '/Disk descr/ {print $1}')
+ eli_rotrate=$(diskinfo -v ${disk}.eli | awk '/Rotation/ {print $1}')
+ eli_zonemode=$(diskinfo -v ${disk}.eli | awk '/Zone Mode/ {print $1}')
+ atf_check_equal "$disk_ident" "$eli_ident"
+ atf_check_equal "$disk_descr" "$eli_descr"
+ atf_check_equal "$disk_rotrate" "$eli_rotrate"
+ atf_check_equal "$disk_zonemode" "$eli_zonemode"
+}
+preserve_disk_props_cleanup()
+{
+ disk_cleanup
+ geli_test_cleanup
+}
+
+atf_test_case physpath cleanup
+physpath_head()
+{
+ atf_set "descr" "geli should append /eli to the underlying device's physical path"
+ atf_set "require.user" "root"
+ atf_set "timeout" 15
+}
+physpath_body()
+{
+ geli_test_setup
+ if ! error_message=$(geom_load_class_if_needed nop); then
+ atf_skip "$error_message"
+ fi
+
+ attach_md md -s1m
+ # If the underlying device has no physical path, then geli should not
+ # create one.
+ atf_check -o empty -e ignore diskinfo -p $md
+ atf_check -s exit:0 geli onetime $md
+ atf_check -o empty -e ignore diskinfo -p $md.eli
+ atf_check -s exit:0 geli kill $md
+
+ # If the underlying device does have a physical path, then geli should
+ # append "/eli"
+ physpath="some/physical/path"
+ atf_check gnop create -z $physpath ${md}
+ atf_check -s exit:0 geli onetime $md.nop
+ atf_check -o match:"^${physpath}/eli$" diskinfo -p $md.nop.eli
+}
+physpath_cleanup()
+{
+ if [ -f "$TEST_MDS_FILE" ]; then
+ while read md; do
+ [ -c /dev/${md}.nop.eli ] && \
+ geli detach $md.nop.eli 2>/dev/null
+ [ -c /dev/${md}.nop ] && \
+ gnop destroy -f $md.nop 2>/dev/null
+ [ -c /dev/${md}.eli ] && \
+ geli detach $md.eli 2>/dev/null
+ mdconfig -d -u $md 2>/dev/null
+ done < $TEST_MDS_FILE
+ fi
+ true
+}
+
+unaligned_io_test()
+{
+ cipher=$1
+ secsize=$2
+ ealgo=${cipher%%:*}
+ keylen=${cipher##*:}
+
+ atf_check -s exit:0 -e ignore \
+ geli init -B none -e $ealgo -l $keylen -P -K keyfile \
+ -s $secsize ${md}
+ atf_check geli attach -p -k keyfile ${md}
+
+ atf_check $(atf_get_srcdir)/unaligned_io /dev/${md}.eli
+}
+
+atf_test_case unaligned_io cleanup
+unaligned_io_head()
+{
+ atf_set "descr" "regression test for PR 271766"
+ atf_set "require.user" "root"
+}
+unaligned_io_body()
+{
+ geli_test_setup
+
+ sectors=4
+
+ atf_check dd if=/dev/random of=keyfile bs=512 count=16 status=none
+ for_each_geli_config_nointegrity unaligned_io_test
+}
+unaligned_io_cleanup()
+{
+ geli_test_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case physpath
+ atf_add_test_case preserve_props
+ atf_add_test_case preserve_disk_props
+ atf_add_test_case unaligned_io
+}
+
+common_cleanup()
+{
+ if [ -f "$MD_DEVS" ]; then
+ while read test_md; do
+ gnop destroy -f ${test_md}.nop 2>/dev/null
+ mdconfig -d -u $test_md 2>/dev/null
+ done < $MD_DEVS
+ rm $MD_DEVS
+ fi
+
+ if [ -f "$PLAINFILES" ]; then
+ while read f; do
+ rm -f ${f}
+ done < ${PLAINFILES}
+ rm ${PLAINFILES}
+ fi
+ true
+}
+
+disk_cleanup()
+{
+ disks=`atf_config_get disks`
+ disk=${disks%% *}
+ if [ -n "$disk" ]; then
+ geli kill ${disk} 2>/dev/null
+ fi
+}
diff --git a/tests/sys/geom/class/eli/onetime_test.sh b/tests/sys/geom/class/eli/onetime_test.sh
new file mode 100644
index 000000000000..65306840384a
--- /dev/null
+++ b/tests/sys/geom/class/eli/onetime_test.sh
@@ -0,0 +1,182 @@
+
+. $(atf_get_srcdir)/conf.sh
+
+onetime_test()
+{
+ cipher=$1
+ secsize=$2
+ ealgo=${cipher%%:*}
+ keylen=${cipher##*:}
+
+ atf_check -s exit:0 -o ignore -e ignore \
+ geli onetime -e $ealgo -l $keylen -s $secsize ${md}
+
+ atf_check dd if=rnd of=/dev/${md}.eli bs=${secsize} count=${sectors} status=none
+
+ md_rnd=`dd if=rnd bs=${secsize} count=${sectors} status=none | md5`
+ atf_check_equal 0 $?
+ md_ddev=`dd if=/dev/${md}.eli bs=${secsize} count=${sectors} status=none | md5`
+ atf_check_equal 0 $?
+ md_edev=`dd if=/dev/${md} bs=${secsize} count=${sectors} status=none | md5`
+ atf_check_equal 0 $?
+
+ if [ ${md_rnd} != ${md_ddev} ]; then
+ atf_fail "geli did not return the original data"
+ fi
+ if [ ${md_rnd} == ${md_edev} ]; then
+ atf_fail "geli did not encrypt the data"
+ fi
+}
+atf_test_case onetime cleanup
+onetime_head()
+{
+ atf_set "descr" "geli onetime can create temporary providers"
+ atf_set "require.user" "root"
+ atf_set "timeout" 1800
+}
+onetime_body()
+{
+ geli_test_setup
+
+ sectors=100
+
+ dd if=/dev/random of=rnd bs=${MAX_SECSIZE} count=${sectors} status=none
+ for_each_geli_config_nointegrity onetime_test
+}
+onetime_cleanup()
+{
+ geli_test_cleanup
+}
+
+onetime_a_test()
+{
+ cipher=$1
+ aalgo=$2
+ secsize=$3
+ ealgo=${cipher%%:*}
+ keylen=${cipher##*:}
+
+ atf_check -s exit:0 -o ignore -e ignore \
+ geli onetime -a $aalgo -e $ealgo -l $keylen -s $secsize ${md}
+
+ atf_check dd if=rnd of=/dev/${md}.eli bs=${secsize} count=${sectors} status=none
+
+ md_rnd=`dd if=rnd bs=${secsize} count=${sectors} status=none | md5`
+ atf_check_equal 0 $?
+ md_ddev=`dd if=/dev/${md}.eli bs=${secsize} count=${sectors} status=none | md5`
+ atf_check_equal 0 $?
+
+ if [ ${md_rnd} != ${md_ddev} ]; then
+ atf_fail "Miscompare for aalgo=${aalgo} ealgo=${ealgo} keylen=${keylen} sec=${secsize}"
+ fi
+}
+atf_test_case onetime_a cleanup
+onetime_a_head()
+{
+ atf_set "descr" "geli onetime with HMACs"
+ atf_set "require.user" "root"
+ atf_set "timeout" 1800
+}
+onetime_a_body()
+{
+ geli_test_setup
+
+ sectors=8
+
+ atf_check dd if=/dev/random of=rnd bs=$MAX_SECSIZE count=$sectors \
+ status=none
+ for_each_geli_config onetime_a_test
+}
+onetime_a_cleanup()
+{
+ geli_test_cleanup
+}
+
+atf_test_case onetime_d cleanup
+onetime_d_head()
+{
+ atf_set "descr" "geli onetime -d will create providers that detach on last close"
+ atf_set "require.user" "root"
+}
+onetime_d_body()
+{
+ geli_test_setup
+
+ sectors=100
+ attach_md md -t malloc -s $sectors
+
+ atf_check geli onetime -d ${md}
+ if [ ! -c /dev/${md}.eli ]; then
+ atf_fail "Provider not created, or immediately detached"
+ fi
+
+ # Be sure it doesn't detach on read.
+ atf_check dd if=/dev/${md}.eli of=/dev/null status=none
+ sleep 1
+ if [ ! -c /dev/${md}.eli ]; then
+ atf_fail "Provider detached when a reader closed"
+ fi
+
+ # It should detach when a writer closes
+ true > /dev/${md}.eli
+ sleep 1
+ if [ -c /dev/${md}.eli ]; then
+ atf_fail "Provider didn't detach on last close of a writer"
+ fi
+}
+onetime_d_cleanup()
+{
+ geli_test_cleanup
+}
+
+atf_test_case onetime_null cleanup
+onetime_null_head()
+{
+ atf_set "descr" "geli onetime can use the null cipher"
+ atf_set "require.user" "root"
+}
+onetime_null_body()
+{
+ geli_test_setup
+
+ sectors=100
+
+ dd if=/dev/random of=rnd bs=${MAX_SECSIZE} count=${sectors} status=none
+
+ secsize=512
+ ealgo=${cipher%%:*}
+ keylen=${cipher##*:}
+
+ attach_md md -t malloc -s 100k
+
+ atf_check -s exit:0 -o ignore -e ignore \
+ geli onetime -e null -s ${secsize} ${md}
+
+ atf_check dd if=rnd of=/dev/${md}.eli bs=${secsize} count=${sectors} status=none
+
+ md_rnd=`dd if=rnd bs=${secsize} count=${sectors} status=none | md5`
+ atf_check_equal 0 $?
+ md_ddev=`dd if=/dev/${md}.eli bs=${secsize} count=${sectors} status=none | md5`
+ atf_check_equal 0 $?
+ md_edev=`dd if=/dev/${md} bs=${secsize} count=${sectors} status=none | md5`
+ atf_check_equal 0 $?
+
+ if [ ${md_rnd} != ${md_ddev} ]; then
+ atf_fail "geli did not return the original data"
+ fi
+ if [ ${md_rnd} != ${md_edev} ]; then
+ atf_fail "geli encrypted the data even with the null cipher"
+ fi
+}
+onetime_null_cleanup()
+{
+ geli_test_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case onetime
+ atf_add_test_case onetime_a
+ atf_add_test_case onetime_d
+ atf_add_test_case onetime_null
+}
diff --git a/tests/sys/geom/class/eli/online_resize_test.sh b/tests/sys/geom/class/eli/online_resize_test.sh
new file mode 100755
index 000000000000..ef6fe85c63d0
--- /dev/null
+++ b/tests/sys/geom/class/eli/online_resize_test.sh
@@ -0,0 +1,191 @@
+#!/bin/sh
+
+. $(atf_get_srcdir)/conf.sh
+
+atf_test_case online_resize cleanup
+online_resize_head()
+{
+ atf_set "descr" "online resize of geli providers"
+ atf_set "require.user" "root"
+}
+online_resize_body()
+{
+ geli_test_setup
+
+ (
+ echo "m 512 none 10485248 1 1 20971008 1 1 31456768 1 1"
+ echo "m 4096 none 10481664 1 1 20967424 1 1 31453184 1 1"
+ echo "m 512 HMAC/SHA256 5242368 1 1 10485248 1 1 15728128 1 1"
+ echo "m 4096 HMAC/SHA256 9318400 1 1 18640896 1 1 27959296 1 1"
+ echo "p 512 none 11258999068425728 [0-9] 20971520 22517998136851968 [0-9] 41943040 33776997205278208 [0-9] 62914560"
+ echo "p 4096 none 11258999068422144 [0-9] 2621440 22517998136848384 [0-9] 5242880 33776997205274624 [0-9] 7864320"
+ echo "p 512 HMAC/SHA256 5629499534212608 [0-9] 20971520 11258999068425728 [0-9] 41943040 16888498602638848 [0-9] 62914560"
+ echo "p 4096 HMAC/SHA256 10007999171932160 [0-9] 20971520 20015998343868416 [0-9] 41943040 30023997515800576 [0-9] 62914560"
+ ) | while read prefix sector auth esize10 ka10 kt10 esize20 ka20 kt20 esize30 ka30 kt30; do
+ if [ "${auth}" = "none" ]; then
+ aalgo=""
+ eflags="0x200"
+ dflags="0x0"
+ else
+ aalgo="-a ${auth}"
+ eflags="0x210"
+ dflags="0x10"
+ fi
+
+ if [ "${prefix}" = "m" ]; then
+ psize10="10485760"
+ psize20="20971520"
+ psize30="31457280"
+ else
+ psize10="11258999068426240"
+ psize20="22517998136852480"
+ psize30="33776997205278720"
+ fi
+
+ attach_md md -t malloc -s40${prefix}
+
+ # Initialise
+ atf_check -s exit:0 -o ignore gpart create -s GPT ${md}
+ atf_check -s exit:0 -o ignore gpart add -t freebsd-ufs -s 10${prefix} ${md}
+
+ echo secret >tmp.key
+
+ atf_check geli init ${aalgo} -s ${sector} -Bnone -PKtmp.key ${md}p1
+ # Autoresize is set by default.
+ atf_check -s exit:0 -o match:"flags: ${eflags}$" geli dump ${md}p1
+
+ atf_check geli configure -R ${md}p1
+ atf_check -s exit:0 -o match:"flags: ${dflags}$" geli dump ${md}p1
+ atf_check geli configure -r ${md}p1
+ atf_check -s exit:0 -o match:"flags: ${eflags}$" geli dump ${md}p1
+
+ atf_check geli init -R ${aalgo} -s ${sector} -Bnone -PKtmp.key ${md}p1
+ atf_check -s exit:0 -o match:"flags: ${dflags}$" geli dump ${md}p1
+
+ atf_check geli configure -r ${md}p1
+ atf_check -s exit:0 -o match:"flags: ${eflags}$" geli dump ${md}p1
+ atf_check geli configure -R ${md}p1
+ atf_check -s exit:0 -o match:"flags: ${dflags}$" geli dump ${md}p1
+
+ atf_check geli init ${aalgo} -s ${sector} -Bnone -PKtmp.key ${md}p1
+ atf_check geli attach -pk tmp.key ${md}p1
+ atf_check -s exit:0 -o match:'^Flags: .*AUTORESIZE' geli list ${md}p1.eli
+ atf_check geli configure -R ${md}p1
+ atf_check -s exit:0 -o match:"flags: ${dflags}$" geli dump ${md}p1
+ atf_check -o not-match:'^Flags: .*AUTORESIZE' geli list ${md}p1.eli
+ atf_check geli configure -r ${md}p1
+ atf_check -s exit:0 -o match:"flags: ${eflags}$" geli dump ${md}p1
+ atf_check -s exit:0 -o match:'^Flags: .*AUTORESIZE' geli list ${md}p1.eli
+
+ atf_check geli configure -R ${md}p1
+ atf_check -s exit:0 -o match:"provsize: ${psize10}$" geli dump ${md}p1
+ atf_check -s exit:0 -o match:resized gpart resize -i 1 -s 20${prefix} ${md}
+ # Autoresize turned off - we lose metadata.
+ atf_check -s exit:1 -o empty -e ignore geli dump ${md}p1
+ atf_check geli detach ${md}p1.eli
+ # When we recover previous size, the metadata should be there.
+ atf_check -s exit:0 -o match:resized gpart resize -i 1 -s 10${prefix} ${md}
+ atf_check -s exit:0 -o match:"flags: ${dflags}$" geli dump ${md}p1
+
+ atf_check geli configure -r ${md}p1
+ atf_check geli attach -pk tmp.key ${md}p1
+ atf_check -s exit:0 -o match:"^[[:space:]]${esize10}[[:space:]]+# mediasize in bytes" diskinfo -v ${md}p1.eli
+ atf_check -s exit:0 -o match:"^KeysAllocated: ${ka10}$" geli list ${md}p1.eli
+ atf_check -s exit:0 -o match:"^KeysTotal: ${kt10}$" geli list ${md}p1.eli
+
+ atf_check -s exit:0 -o match:resized gpart resize -i 1 -s 20${prefix} ${md}
+ atf_check -s exit:0 -o match:"provsize: ${psize20}$" geli dump ${md}p1
+ atf_check -s exit:0 -o match:"^[[:space:]]${esize20}[[:space:]]+# mediasize in bytes" diskinfo -v ${md}p1.eli
+ atf_check -s exit:0 -o match:"^KeysAllocated: ${ka20}$" geli list ${md}p1.eli
+ atf_check -s exit:0 -o match:"^KeysTotal: ${kt20}$" geli list ${md}p1.eli
+ atf_check -s exit:0 -o match:"flags: ${eflags}$" geli dump ${md}p1
+ atf_check -s exit:0 -o match:'^Flags: .*AUTORESIZE' geli list ${md}p1.eli
+ if [ "${prefix}" = "m" ]; then
+ atf_check -s exit:1 -o empty -e match:"^${esize20} bytes transferred " dd if=/dev/random of=/dev/${md}p1.eli bs=1m
+ atf_check -s exit:0 -o empty -e match:"^${esize20} bytes transferred " dd if=/dev/${md}p1.eli of=/dev/null bs=1m
+ fi
+
+ atf_check -s exit:0 -o match:resized gpart resize -i 1 -s 30${prefix} ${md}
+ atf_check -s exit:0 -o match:"provsize: ${psize30}$" geli dump ${md}p1
+ atf_check -s exit:0 -o match:"^[[:space:]]${esize30}[[:space:]]+# mediasize in bytes" diskinfo -v ${md}p1.eli
+ atf_check -s exit:0 -o match:"^KeysAllocated: ${ka30}$" geli list ${md}p1.eli
+ atf_check -s exit:0 -o match:"^KeysTotal: ${kt30}$" geli list ${md}p1.eli
+ atf_check -s exit:0 -o match:"flags: ${eflags}$" geli dump ${md}p1
+ atf_check -s exit:0 -o match:'^Flags: .*AUTORESIZE' geli list ${md}p1.eli
+ if [ "${prefix}" = "m" ]; then
+ atf_check -s exit:1 -o empty -e match:"^${esize30} bytes transferred " dd if=/dev/random of=/dev/${md}p1.eli bs=1m
+ atf_check -s exit:0 -o empty -e match:"^${esize30} bytes transferred " dd if=/dev/${md}p1.eli of=/dev/null bs=1m
+ fi
+
+ atf_check geli detach ${md}p1.eli
+
+ # Make sure that the old metadata is removed.
+ atf_check -s exit:0 -o match:resized gpart resize -i 1 -s 20${prefix} ${md}
+ atf_check -s exit:1 -o empty -e ignore geli dump ${md}p1
+ atf_check -s exit:0 -o match:resized gpart resize -i 1 -s 10${prefix} ${md}
+ atf_check -s exit:1 -o empty -e ignore geli dump ${md}p1
+
+ # Test geli with onetime keys.
+ if [ "${auth}" = "none" ]; then
+ osize10="${psize10}"
+ osize20="${psize20}"
+ osize30="${psize30}"
+ else
+ osize10="${esize10}"
+ osize20="${esize20}"
+ osize30="${esize30}"
+ if [ "${sector}" -eq 512 ]; then
+ osize10=$((osize10+sector))
+ osize20=$((osize20+sector))
+ osize30=$((osize30+sector))
+ fi
+ fi
+ atf_check geli onetime ${aalgo} -s ${sector} ${md}p1
+ atf_check -s exit:0 -o match:"^[[:space:]]${osize10}[[:space:]]+# mediasize in bytes" diskinfo -v ${md}p1.eli
+ atf_check -s exit:0 -o match:'^Flags: .*AUTORESIZE' geli list ${md}p1.eli
+ atf_check -s exit:0 -o match:resized gpart resize -i 1 -s 20${prefix} ${md}
+ atf_check -s exit:0 -o match:"^[[:space:]]${osize20}[[:space:]]+# mediasize in bytes" diskinfo -v ${md}p1.eli
+ atf_check -s exit:0 -o match:resized gpart resize -i 1 -s 30${prefix} ${md}
+ atf_check -s exit:0 -o match:"^[[:space:]]${osize30}[[:space:]]+# mediasize in bytes" diskinfo -v ${md}p1.eli
+ atf_check geli detach ${md}p1.eli
+
+ atf_check -s exit:0 -o match:resized gpart resize -i 1 -s 10${prefix} ${md}
+ atf_check geli onetime -R ${aalgo} -s ${sector} ${md}p1
+ atf_check -s exit:0 -o match:"^[[:space:]]${osize10}[[:space:]]+# mediasize in bytes" diskinfo -v ${md}p1.eli
+ atf_check -o not-match:'^Flags: .*AUTORESIZE' geli list ${md}p1.eli
+ atf_check -s exit:0 -o match:resized gpart resize -i 1 -s 20${prefix} ${md}
+ atf_check -s exit:0 -o match:"^[[:space:]]${osize10}[[:space:]]+# mediasize in bytes" diskinfo -v ${md}p1.eli
+ atf_check -s exit:0 -o match:resized gpart resize -i 1 -s 30${prefix} ${md}
+ atf_check -s exit:0 -o match:"^[[:space:]]${osize10}[[:space:]]+# mediasize in bytes" diskinfo -v ${md}p1.eli
+ atf_check geli detach ${md}p1.eli
+
+ atf_check -s exit:0 -o match:resized gpart resize -i 1 -s 10${prefix} ${md}
+ atf_check geli onetime ${aalgo} -s ${sector} ${md}p1
+ atf_check -s exit:0 -o match:"^[[:space:]]${osize10}[[:space:]]+# mediasize in bytes" diskinfo -v ${md}p1.eli
+ atf_check -s exit:0 -o match:'^Flags: .*AUTORESIZE' geli list ${md}p1.eli
+ atf_check geli configure -R ${md}p1
+ atf_check -o not-match:'^Flags: .*AUTORESIZE' geli list ${md}p1.eli
+ atf_check -s exit:0 -o match:resized gpart resize -i 1 -s 20${prefix} ${md}
+ atf_check -s exit:0 -o match:"^[[:space:]]${osize10}[[:space:]]+# mediasize in bytes" diskinfo -v ${md}p1.eli
+ atf_check geli configure -r ${md}p1
+ atf_check -s exit:0 -o match:'^Flags: .*AUTORESIZE' geli list ${md}p1.eli
+ atf_check -s exit:0 -o match:resized gpart resize -i 1 -s 30${prefix} ${md}
+ atf_check -s exit:0 -o match:"^[[:space:]]${osize30}[[:space:]]+# mediasize in bytes" diskinfo -v ${md}p1.eli
+ done
+}
+online_resize_cleanup()
+{
+ if [ -f "$TEST_MDS_FILE" ]; then
+ while read md; do
+ atf_check -s ignore -e ignore -o ignore geli detach ${md}p1.eli
+ atf_check -s ignore -e ignore -o ignore gpart delete -i 1 ${md}
+ atf_check -s ignore -e ignore -o ignore gpart destroy ${md}
+ done < $TEST_MDS_FILE
+ fi
+ geli_test_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case online_resize
+}
diff --git a/tests/sys/geom/class/eli/reentrancy_test.sh b/tests/sys/geom/class/eli/reentrancy_test.sh
new file mode 100755
index 000000000000..92a85d4df10a
--- /dev/null
+++ b/tests/sys/geom/class/eli/reentrancy_test.sh
@@ -0,0 +1,68 @@
+
+# Test various operations for geli-on-geli providers, to ensure that geli is
+# reentrant.
+
+. $(atf_get_srcdir)/conf.sh
+
+init_test()
+{
+ cipher=$1
+ aalgo=$2
+ secsize=$3
+ ealgo=${cipher%%:*}
+ keylen=${cipher##*:}
+
+ atf_check dd if=/dev/random of=testdata bs=$secsize count=1 status=none
+ atf_check dd if=/dev/random of=keyfile bs=$secsize count=16 status=none
+
+ # Create the lower geli device
+ atf_check -s exit:0 -e ignore \
+ geli init -B none -a $aalgo -e $ealgo -l $keylen -P -K keyfile \
+ -s $secsize ${md}
+ atf_check geli attach -p -k keyfile ${md}
+ # Create the upper geli device
+ atf_check -s exit:0 -e ignore \
+ geli init -B none -a $aalgo -e $ealgo -l $keylen -P -K keyfile \
+ -s $secsize ${md}.eli
+ atf_check geli attach -p -k keyfile ${md}.eli
+ echo ${md} > layered_md_device
+
+ # Ensure we can read and write.
+ atf_check dd if=testdata of=/dev/${md}.eli.eli bs=$secsize count=1 \
+ status=none
+ atf_check dd if=/dev/${md}.eli.eli of=cmpdata bs=$secsize count=1 \
+ status=none
+ atf_check cmp -s testdata cmpdata
+
+ geli detach ${md}.eli 2>/dev/null
+}
+
+atf_test_case init cleanup
+init_head()
+{
+ atf_set "descr" "Initialize a geli provider on top of another"
+ atf_set "require.user" "root"
+ atf_set "timeout" 600
+}
+init_body()
+{
+ sectors=2
+ geli_test_setup
+
+ for_each_geli_config init_test
+}
+init_cleanup()
+{
+ if [ -f layered_md_device ]; then
+ while read provider; do
+ [ -c /dev/${md}.eli.eli ] && \
+ geli detach $md.eli.eli 2>/dev/null
+ done < layered_md_device
+ fi
+ geli_test_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case init
+}
diff --git a/tests/sys/geom/class/eli/resize_test.sh b/tests/sys/geom/class/eli/resize_test.sh
new file mode 100644
index 000000000000..832bfeee5c92
--- /dev/null
+++ b/tests/sys/geom/class/eli/resize_test.sh
@@ -0,0 +1,88 @@
+#!/bin/sh
+
+. $(atf_get_srcdir)/conf.sh
+
+atf_test_case resize cleanup
+resize_head()
+{
+ atf_set "descr" "geli resize will resize a geli provider"
+ atf_set "require.user" "root"
+}
+resize_body()
+{
+ geli_test_setup
+
+ BLK=512
+ BLKS_PER_MB=2048
+
+ attach_md md -t malloc -s40m
+
+ # Initialise
+ atf_check -s exit:0 -o ignore gpart create -s BSD ${md}
+ atf_check -s exit:0 -o ignore gpart add -t freebsd-ufs -s 10m ${md}
+
+ echo secret >tmp.key
+ atf_check geli init -Bnone -PKtmp.key ${md}a
+ atf_check geli attach -pk tmp.key ${md}a
+
+ atf_check -s exit:0 -o ignore newfs -U ${md}a.eli
+ atf_check -s exit:7 -o ignore fsck_ffs -Ffy ${md}a.eli
+
+ # Doing a backup, resize & restore must be forced (with -f) as geli
+ # verifies that the provider size in the metadata matches the consumer.
+
+ atf_check geli backup ${md}a tmp.meta
+ atf_check geli detach ${md}a.eli
+ atf_check -s exit:0 -o match:resized gpart resize -i1 -s 20m ${md}
+ atf_check -s not-exit:0 -e ignore geli attach -pktmp.key ${md}a
+ atf_check -s not-exit:0 -e ignore geli restore tmp.meta ${md}a
+ atf_check geli restore -f tmp.meta ${md}a
+ atf_check geli attach -pktmp.key ${md}a
+ atf_check -s exit:0 -o ignore growfs -y ${md}a.eli
+ atf_check -s exit:7 -o ignore fsck_ffs -Ffy ${md}a.eli
+
+ # Now do the resize properly
+
+ atf_check geli detach ${md}a.eli
+ atf_check -s exit:0 -o match:resized gpart resize -i1 -s 30m ${md}
+ atf_check geli resize -s20m ${md}a
+ atf_check -s not-exit:0 -e match:"Inconsistent provider.*metadata" \
+ geli resize -s20m ${md}a
+ atf_check geli attach -pktmp.key ${md}a
+ atf_check -s exit:0 -o ignore growfs -y ${md}a.eli
+ atf_check -s exit:7 -o ignore fsck_ffs -Ffy ${md}a.eli
+
+ atf_check geli detach ${md}a.eli
+ atf_check -s exit:0 -o ignore gpart destroy -F $md
+
+
+ # Verify that the man page example works, changing ada0 to $md,
+ # 1g to 20m, 2g to 30m and keyfile to tmp.key, and adding -B none
+ # to geli init.
+
+ atf_check -s exit:0 -o ignore gpart create -s GPT $md
+ atf_check -s exit:0 -o ignore gpart add -s 20m -t freebsd-ufs -i 1 $md
+ atf_check geli init -B none -K tmp.key -P ${md}p1
+ atf_check -s exit:0 -o match:resized gpart resize -s 30m -i 1 $md
+ atf_check geli resize -s 20m ${md}p1
+ atf_check geli attach -k tmp.key -p ${md}p1
+}
+resize_cleanup()
+{
+ if [ -f "$TEST_MDS_FILE" ]; then
+ while read md; do
+ [ -c /dev/${md}a.eli ] && \
+ geli detach ${md}a.eli 2>/dev/null
+ [ -c /dev/${md}p1.eli ] && \
+ geli detach ${md}p1.eli
+ [ -c /dev/${md}.eli ] && \
+ geli detach ${md}.eli 2>/dev/null
+ mdconfig -d -u $md 2>/dev/null
+ done < $TEST_MDS_FILE
+ fi
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case resize
+}
diff --git a/tests/sys/geom/class/eli/setkey_test.sh b/tests/sys/geom/class/eli/setkey_test.sh
new file mode 100644
index 000000000000..92387a8a8ab6
--- /dev/null
+++ b/tests/sys/geom/class/eli/setkey_test.sh
@@ -0,0 +1,221 @@
+#!/bin/sh
+
+. $(atf_get_srcdir)/conf.sh
+
+atf_test_case setkey cleanup
+setkey_head()
+{
+ atf_set "descr" "geli setkey can change the key for an existing provider"
+ atf_set "require.user" "root"
+}
+setkey_body()
+{
+ geli_test_setup
+
+ sectors=100
+ attach_md md -t malloc -s `expr $sectors + 1`
+
+ atf_check dd if=/dev/random of=rnd bs=512 count=${sectors} status=none
+ hash1=`dd if=rnd bs=512 count=${sectors} status=none | md5`
+ atf_check_equal 0 $?
+ atf_check dd if=/dev/random of=keyfile1 bs=512 count=16 status=none
+ atf_check dd if=/dev/random of=keyfile2 bs=512 count=16 status=none
+ atf_check dd if=/dev/random of=keyfile3 bs=512 count=16 status=none
+ atf_check dd if=/dev/random of=keyfile4 bs=512 count=16 status=none
+ atf_check dd if=/dev/random of=keyfile5 bs=512 count=16 status=none
+
+ atf_check geli init -B none -P -K keyfile1 ${md}
+ atf_check geli attach -p -k keyfile1 ${md}
+
+ atf_check \
+ dd if=rnd of=/dev/${md}.eli bs=512 count=${sectors} status=none
+ hash2=`dd if=/dev/${md}.eli bs=512 count=${sectors} 2>/dev/null | md5`
+ atf_check_equal 0 $?
+
+ # Change current key (0) for attached provider.
+ atf_check -s exit:0 -o ignore geli setkey -P -K keyfile2 ${md}
+ atf_check geli detach ${md}
+
+ # We cannot use keyfile1 anymore.
+ atf_check -s not-exit:0 -e match:"Wrong key" \
+ geli attach -p -k keyfile1 ${md}
+
+ # Attach with new key.
+ atf_check geli attach -p -k keyfile2 ${md}
+ hash3=`dd if=/dev/${md}.eli bs=512 count=${sectors} 2>/dev/null | md5`
+ atf_check_equal 0 $?
+
+ # Change key 1 for attached provider.
+ atf_check -s exit:0 -o ignore geli setkey -n 1 -P -K keyfile3 ${md}
+ atf_check geli detach ${md}
+
+ # Attach with key 1.
+ atf_check geli attach -p -k keyfile3 ${md}
+ hash4=`dd if=/dev/${md}.eli bs=512 count=${sectors} 2>/dev/null | md5`
+ atf_check_equal 0 $?
+ atf_check geli detach ${md}
+
+ # Change current (1) key for detached provider.
+ atf_check -s exit:0 -o ignore geli setkey -p -k keyfile3 -P -K keyfile4 ${md}
+
+ # We cannot use keyfile3 anymore.
+ atf_check -s not-exit:0 -e match:"Wrong key" \
+ geli attach -p -k keyfile3 ${md}
+
+ # Attach with key 1.
+ atf_check geli attach -p -k keyfile4 ${md}
+ hash5=`dd if=/dev/${md}.eli bs=512 count=${sectors} 2>/dev/null | md5`
+ atf_check_equal 0 $?
+ atf_check geli detach ${md}
+
+ # Change key 0 for detached provider.
+ atf_check -s exit:0 -o ignore geli setkey -n 0 -p -k keyfile4 -P -K keyfile5 ${md}
+
+ # We cannot use keyfile2 anymore.
+ atf_check -s not-exit:0 -e match:"Wrong key" \
+ geli attach -p -k keyfile2 ${md} 2>/dev/null
+
+ # Attach with key 0.
+ atf_check geli attach -p -k keyfile5 ${md}
+ hash6=`dd if=/dev/${md}.eli bs=512 count=${sectors} 2>/dev/null | md5`
+ atf_check_equal 0 $?
+ atf_check geli detach ${md}
+
+ atf_check_equal ${hash1} ${hash2}
+ atf_check_equal ${hash1} ${hash3}
+ atf_check_equal ${hash1} ${hash4}
+ atf_check_equal ${hash1} ${hash5}
+ atf_check_equal ${hash1} ${hash6}
+}
+setkey_cleanup()
+{
+ geli_test_cleanup
+}
+
+atf_test_case setkey_passphrase cleanup
+setkey_passphrase_head()
+{
+ atf_set "descr" "geli setkey can change the passphrase for a provider"
+ atf_set "require.user" "root"
+}
+setkey_passphrase_body()
+{
+ geli_test_setup
+
+ sectors=100
+ attach_md md -t malloc -s `expr $sectors + 1`
+
+ atf_check dd if=/dev/random of=rnd bs=512 count=${sectors} status=none
+ hash1=`dd if=rnd bs=512 count=${sectors} status=none | md5`
+ atf_check_equal 0 $?
+ atf_check dd if=/dev/random of=pass1 bs=512 count=1 status=none
+ atf_check dd if=/dev/random of=pass2 bs=512 count=1 status=none
+ atf_check dd if=/dev/random of=pass3 bs=512 count=1 status=none
+
+ atf_check geli init -B none -J pass1 ${md}
+ atf_check geli attach -j pass1 ${md}
+
+ atf_check \
+ dd if=rnd of=/dev/${md}.eli bs=512 count=${sectors} status=none
+ hash2=`dd if=/dev/${md}.eli bs=512 count=${sectors} 2>/dev/null | md5`
+ atf_check_equal 0 $?
+
+ atf_check geli detach ${md}
+
+ # Change from passphrase 1 to passphrase 2 for the detached provider.
+ atf_check -s exit:0 -o ignore geli setkey -j pass1 -J pass2 ${md}
+
+ # Make sure that we can attach with passphrase 2 but not with
+ # passphrase 1.
+ atf_check -s not-exit:0 -e match:"Wrong key" \
+ geli attach -j pass1 ${md}
+ atf_check -s exit:0 geli attach -j pass2 ${md}
+ hash3=`dd if=/dev/${md}.eli bs=512 count=${sectors} 2>/dev/null | md5`
+
+ # Change from passphrase 2 to passphrase 3 for the attached provider.
+ atf_check -s exit:0 -o ignore geli setkey -j pass2 -J pass3 ${md}
+ hash4=`dd if=/dev/${md}.eli bs=512 count=${sectors} 2>/dev/null | md5`
+ atf_check geli detach ${md}
+
+ # Make sure that we cannot attach with passphrase 2 anymore.
+ atf_check -s not-exit:0 -e match:"Wrong key" \
+ geli attach -j pass2 ${md}
+
+ atf_check_equal ${hash1} ${hash2}
+ atf_check_equal ${hash1} ${hash3}
+ atf_check_equal ${hash1} ${hash4}
+}
+setkey_passphrase_cleanup()
+{
+ geli_test_cleanup
+}
+
+atf_test_case setkey_readonly cleanup
+setkey_readonly_head()
+{
+ atf_set "descr" "geli setkey cannot change the keys of a readonly provider"
+ atf_set "require.user" "root"
+}
+setkey_readonly_body()
+{
+ geli_test_setup
+
+ sectors=100
+ attach_md md -t malloc -s `expr $sectors + 1`
+ atf_check dd if=/dev/random of=keyfile bs=512 count=16 status=none
+
+ atf_check geli init -B none -P -K keyfile ${md}
+ atf_check geli attach -r -p -k keyfile ${md}
+
+ atf_check -s not-exit:0 -e match:"read-only" \
+ geli setkey -n 1 -P -K /dev/null ${md}
+}
+setkey_readonly_cleanup()
+{
+ geli_test_cleanup
+}
+
+atf_test_case nokey cleanup
+nokey_head()
+{
+ atf_set "descr" "geli setkey can change the key for an existing provider"
+ atf_set "require.user" "root"
+}
+nokey_body()
+{
+ geli_test_setup
+
+ sectors=100
+ attach_md md -t malloc -s `expr $sectors + 1`
+ atf_check dd if=/dev/random of=keyfile1 bs=512 count=16 status=none
+ atf_check dd if=/dev/random of=keyfile2 bs=512 count=16 status=none
+
+ atf_check geli init -B none -P -K keyfile1 ${md}
+
+ # Try to set the key for a detached device without providing any
+ # components for the old key.
+ atf_check -s not-exit:0 -e match:"No key components given" \
+ geli setkey -n 0 -p -P -K keyfile2 ${md}
+
+ # Try to set the key for a detached device without providing any
+ # components for the new key
+ atf_check -s not-exit:0 -e match:"No key components given" \
+ geli setkey -n 0 -p -k keyfile1 -P ${md}
+
+ # Try to set a new key for an attached device with no components
+ atf_check geli attach -p -k keyfile1 ${md}
+ atf_check -s not-exit:0 -e match:"No key components given" \
+ geli setkey -n 0 -P ${md}
+}
+nokey_cleanup()
+{
+ geli_test_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case setkey
+ atf_add_test_case setkey_passphrase
+ atf_add_test_case setkey_readonly
+ atf_add_test_case nokey
+}
diff --git a/tests/sys/geom/class/eli/testvect.h b/tests/sys/geom/class/eli/testvect.h
new file mode 100644
index 000000000000..49fb0588f596
--- /dev/null
+++ b/tests/sys/geom/class/eli/testvect.h
@@ -0,0 +1,1136 @@
+/* Test Vectors for PBKDF2-SHA512 */
+ /* salt, saltlen, passwd, itr, hmacout, hmacoutlen */
+ { "\331}\035\215\000\272\350\261", 8, "\323\303\210\231\260\225\217\035", 100, "J\257\275\352n\371\300!tp\367\257\347c'\000\243F\246\376\274H\263\312m\336\304\3515P\222Cb\037-\313W\0067\232\024%\235\252\322\035\225k\025\2248\251r\312]v\316\021j\231\224\2556\350", 64 },
+ { "\321\213\277\330\210E\207x", 8, "(C\371\344\354\356\232\212\346\014\335M(\234g\226", 100, "\035MoB\245\001\000\315\332\235\356?alD\231I[%A\372\367\027\267,\303\022\324\004\302a\302t\257\306S\251\250;-pa\246Z\200\003*+\026\305_\250\214n\321\340.\032a3\371\346\025\042", 64 },
+ { "\344\353\215\257\020\315\245\367", 8, "/pj@N\344\271\243^\352\236gu\352\344os\366FR\362\001\347\301", 100, "\223eS:\015\311\023\317\230O\012\347\014\32070\321\337p\353\305\361\223\323\333&\011'\360?\022\374P\336\337\031\266,~@!\037\000\320v\360!\225\221@C\266|k\370@\305\202\235+\314\247\305&", 64 },
+ { "\364\275\222\376\0064\240g", 8, "_\2000 A\233#\372\351\332\247\010\313\274\334\255\362J\356\333\302\345\317\305*\357 \366Aj\344\346", 100, "\223\015X\340\024\244\2767\027\253\261\232\360(?\350H7\326K\322\246\341\222\202:@\254\3218\016T\275\212^\005\344\202\230\220s\311\201\025\233~K6\241*b\244\203\273\355\270\277\242\300V3\037\224\311", 64 },
+ { "\272\207\326\030\010\374\263f", 8, "\323\230\233\317\337\001\241]G\312\347\320K\3374\247\016\343\246\353\300\373\267\335\314h\216\245$\373\210\214\014b\340\377\256\0202E", 100, "a\274S.\032\246\330\321\322\207\004\241n\303\363\361\361\215\367Xi\250\275\246\037\355\255\342\004\253\250\243&\032\357\250\204\207K\2612\0318\3353\253\335Z\351\337\253v\345\322r\030\0066\230jp\345\373\203", 64 },
+ { "j\276\230\215\031$\326v", 8, "gy\264\354\363\322(w\262\272\203\306{\356\031\376\277+\276L\337\270G\345!>29\010\204m\240!\326\255\326\321\272(\325T\033B\262[\267]\272", 100, "\2563\024\201\310\372\017\373\270\232\277\265:j\231\365\032zS\3723\012\341i\302V\233l\247\030:\355\262]\337\361JUe:\350|#D\262\335)\260\333\273\221\252\005\226\322'\371+a\270a\302\276p", 64 },
+ { ",;\270P\012\035\262\235", 8, "\375 \232J\304\271_\272\033\027\363\021\227\260\323\310\304k\004.\327\354\217,\357\353]I0\332We\245\257\371,\260\360\325\224\026q[VuS\217\004\262\376T\353\262\321\241\024", 100, "\313\246&~\374Zq(0\000\002\332\356\355M\353Z?\310~\234\327\011\242\370\032GK\250\020\203sJ\234I,\271\012\230\204g\323\345\343\343\350\231\227\015\350b\256\012\367Q\255v\365\210\247\2614\210\365", 64 },
+ { "\021\325\267u\203$\352\274", 8, "\232\032\225'\021*'a\042\023{\250\361r\262\365\370\032\347\336\363Re28\033J\3763^\311I\322:\036\204\320\033\245\200\262\225\341\377\334\241p\314\334\005O,\374\225\234\014\266\365\030i6\210a\205", 100, "\220\006\216\2420-8m\2766\353(6\212\306\261C@\252\2104\005\205\274\204\365\226\373\016\345\332\207\361A\244\023W\3051:\277\2705Z\253\262\371M\024L\215\224\340p\336\355\250\030*8\300\017\254f", 64 },
+ { "[\353e\224\2423\270m", 8, "\1776\374\302o0\007\307w\032\307\266\376\320\042/\266\352\233\224\316\3616\314!`\015d\316\370L{\201B\245\317\366@V\036\227\273I\205\251\306GW\200*\340P\267\202#\354\330\266O\347\254(\203\270\366w\274\375\272\016\240\313", 100, "?j\005\340=\022\012NM\024\231\220_u\023@\042\214\014b\375\251\023\244y3m\330\377\341\271~V0\263\223\274\325Rmug\026Dt!\347%\270\244\371@\177\350\250N\320\246\034\320\013|\276\027", 64 },
+ { "\262\304(\244\326\272\262\311", 8, "\210C(\273\376\013/\034", 200, "6+\233\201\350\374\2053\013\377SA\007\037\303\027RyrC\361\303\321\031\225d\215\314\213\210\006\031\217\035L)\377'\016\263\310\206\326\372\362\230\226.F\134\277\367\35357(\214\262\036\310@\363#\020", 64 },
+ { "\246<\216\226^F\004\036", 8, "\344].\210\212\236\252f\256n\006\250\2615\325\371", 200, "\213J\234\270\227$St\265\035\323\001&\025\027T>\015F\205\325\361_\003l\245|\213S\232@\254o\227\235l$\355\037\214\220\032\213\320\265lM\334\320\313+\205.\355\310\201K\370^\025\134\346[K", 64 },
+ { "9s72\020\270\251\350", 8, "\323\370:k\323o\271Y\335\306\375#\333\222\246?C\313\023\202>\005\025\216", 200, "\371\373\341\202(\231\010\325_\335\205\253\216\371\227\354\352Os2\356e\033L\236znr;\337a'\025\335n\032f\304\177u\001n{\236`\261\346N\2748R\017\243\226[\256@<\021m\202r\363g", 64 },
+ { "\032\216\254\215\022l\001H", 8, "\027J\232\302\240lTSU\215y\262\360\017\346\3253\276r\276\211\306>\3740\263\264\002\333J;\262", 200, "\3206K,\267r<\302r\207y\373\3625]2Y\3710\343\272\211\202\3465g\340jG\001`\341\352\313\203\250x\221K\254iV\350w\2220\257Yc\360Z\263\237\255\042\325\341\307\330\232s@?\210", 64 },
+ { "D\302g\023\316\224l\271", 8, "\262t\301{\366\345Bv\271!\213\303\244O8\314\336\363\325|\206x\034d$#\001<Z\233\272\014J=\2079?\134\015(", 200, "\334\222G\300~'\042LS\0218\006,\261\207]\277\245GH\007\246\357f\205\325-\3044\337\347\007\373\304\261\317\212\224\276pl\302?\271\321\010\375Q\202\236M\3105\251\310\234\032g~\301_\007\244R", 64 },
+ { "\316@&\224\241\245\211{", 8, "\276\217\365\352O\212\377\257\177\350f\276\330\3035\204\215\327f\256\300\364\212\271\306q\242?-\307\324\317y^\201t\272\371\331M;c\210\202\201\221\363\030", 200, "\340\001\331YS\230\231|\356\024D8\243f\223\335\207\361\2241=\264\0366\030\230Z0\340\226K\352\241C\355\260\236b\1346\035\030\3059\022\335n#\311\025\351L\334\001\366\003\042\302\265\275\347\134]\204", 64 },
+ { "\234^\035\320rt-B", 8, "\357\274\204\366N9\273:\216\331,D\300t\320\361\324F\313\220E\250u\203\307\003\221\365X\303O\230\312~\342\336\007R\232\244M\305U\320\367\272\367\366?\267c\253\354\301\221X", 200, "\017m\031\004\022\364(\016\310U\3621\301\316}k\362\306\227:\233+\000\230\334:,\005\307q\204\332L[\236\223\225\204y\037\037\262o\257]\012\365t\036\351(>x9\012\241\256W\241\334\307\240\207F", 64 },
+ { "\037\346\010\241]\004x\204", 8, "T\334\371\316I\33656Q\027\277\203\033\303\240\371\231\352\214\3701@\002\015?2\264\002\257R\205\250\264}\021\012\326\366wF\311\267\271\0042C:/\341\031\242\337\237.\021V\034\263`W\306Y\3533", 200, "V\370Nt\341W\226|\367\226\200\033|\006Kii\263\304\307\347\301#L\371\335l|O\304\300v\361d\255h^\322\026\332U\224\270\255\227\022\226G\326\363\303\244\234\235\016/<\331\247\307\242\355\226\307", 64 },
+ { "\364\303X\325\327\256\342f", 8, "\227E\237\320\214GB\332\005\0333\250=\020c(\345\007?\032)\2643\243\202\371\200\030\002\225\011/\030\030\003Q\354\344\361B\214\333.\015\336`\376.\360\243$\017\367K\207\212\347L\356S\230\346\034*\307\207\203\233a\310\370\255", 200, "L(\217#(\300jA\343\037\251rR\312G\024\016dj:\212|\024\373\275\134\375\374\247\266FpF\275:\373_\314\134\244\016\373\203k\265\225\243\212t\240\310\347\226\262%\233\213\332w\042WL\302\331", 64 },
+ { "c\027\266\220\010\031\241\313", 8, "F#\323\2367\260\323\273", 300, "@\200\033\254\020\246\206L$\203\026.T\223\205\364\362@\204*\316\232u\033\321\212;l\202\313s\205\333<1~\211\267\213\336Z\276\233W\322\366!\3629l\343@O\216+[!\262I\350\262\312\022\357", 64 },
+ { "\001\277`\003x\301n\313", 8, "\257'\372i.M$\356\230\032\252yq\302\004c", 300, "?\037\327=m\335R\367?\242q\002\023V\203\017q8\252\325?6\213\241\274\275\356\344*\312/p~.<\305\346]\377\242\361\025\311\375\303\247\305\345@U\230\364F\275\211n\250Ay\352\026+\253V", 64 },
+ { "\203\020{)\016R\324\350", 8, "\261\200\031:\346l\242u\352\300\014_\320\221y\213e\214\352\042c\371\257\361", 300, "\001\2111R\305R'b.L\007$\042\267\346\304(|+2\240\260\021\042\243J\344\237\006\236\203A\032\363\356\251\015.\321\336\03431X\002\355\221\313]\264\310\315o\316aw\246|\312bT:\325\300", 64 },
+ { "\002X\311\253\033h\036'", 8, "P\206\257!\305E\377\275\310\005\364Bn\005\342>!w\017@}\245\350\177\305\312\323\361,\301y\022", 300, "\360\301\033\007\367,\374\242\177\3153D\251*_>{\005\272NQ\021\024:0\036\357\333c\254/\001\035\202\341\330\005\0245\265b}\261\232\273\0318\225\227\252\210\361\300\016\357\323\204\202\246\376\277@\225w", 64 },
+ { ">+\207\003k\311\225\276", 8, "((\260Eak\253\305\3243Q\332*N\276\323\325_48\317\264&\340\005\323N\224p\003\331Fl\215\317 w\034u?", 300, "\276\317.\310Gj \217\3502.za\021\230\322C6\255\301\354-\263\247\002\352\377\267]\243\201n\245\213D\240\010D|\324\336\251\326\342e\231\350*\300\315\007\234\236\002}\372\013\364\257\232\262:\207e", 64 },
+ { "\265G\357\330S\302?8", 8, "7S-\203\036\340\015\356\027\253\302\376\222\037\2276\0141|p\255\313\325\211\206\265\230\333\027\343\231\007\335\366\356\011 $ \312`\333y\267K4\317\277", 300, "\004\264\241\262\204\361YL%#v\373\013@E/\335\353 l\365\242u\215\301p\360O\030{\0236\033\206$\340\263\202bC\032\014\252\365\323\325\350u\210\234\020\037\015\275\000{h\363a\244\024\177\356\236", 64 },
+ { "I\257\321\376\365\317\267I", 8, "\200!XxW\233B\374,\362\027,\343\022\204\220\373\231=\376\343\362\246\224 \001\233\015\0338\337\224#%\320\037\342\276\314X{h\036\210w\377\011@=\021\034\3429!\211\350", 300, "`\277s c\215t\251,\306:L\267`u%D\244\021\340\036C\304\032\014u\042\134Mzo\313\225\3641\017\037\271q*\321\023\227\253\215\330\351\373\202\220\274`P\267\277\373\007\366\370\031\350\015\214\245", 64 },
+ { "\324\366\347;\363R\300!", 8, "y\262~C\031\252 \004\215a\033\355l9\024C\237b/\032Kk\316q\257K\017\017\036\316\003\246\005\225(\0168\264\032pT\030\250\334\341\020\012X K\325\002\254mh\265\134N*?\233\007Zn", 300, "\202\252\261h$8%\335rv\260\0005\313Lj\244\262\342\202\352\277\005 \2418\2740Y\333\036\014[\015\030'O\351\377|D\0114O\344\203\177\360\337\3610\315\323\2528\247\307\3535R\366)\033\236", 64 },
+ { "\301\357\263\261\347O\344,", 8, "\216\376\312\271K}\224Pt\022\2572\230lR\177\340m\224\271\305\237Z\374\252\204\313M\307&\330]^W[\001\317\207\264\305\017\224\331\245\361\301Z\371r\264\223\202\201\353\252k\223Bk\361\323c\307\255(\010:x%\257\3352", 300, "\312\240!.\3606\236]\235\315\2409\042?\027\015\271\030\352l>\272s\345\340\231\317\263\306W6[\342\360\307\305 !\357\003|g][\275j=ptx\344\265\271~\036_\361\004\246\261\313\241D)", 64 },
+ { "\257\000;+\233Q\214b", 8, "eoq{\270\246ua", 400, "\251B\370\245m\000>\134\007\256\373\001\251\262,\310\367\376\323\334{K\202\177\235\376x\365D(+\367\361\373\031\376\254H\243\305/N\267\205\2108\301*\025@\030\311\012.\261\237\377\015\205h\261\042\372\034", 64 },
+ { "hU\325\246:obb", 8, "?/\352\332\327c\027\331\305N'\321\355\337\316\241", 400, "\177\312\2275\203 \026aNXF\010\377\336=\240\025\312\252r\01363\234\351\2754[y7E\334CQ\371\243F\010l\2545\253\214\341\323\300u\202\225\236\250\356\007Q\000Q\302\207M@g\350m:", 64 },
+ { "p(\256-\313\020{\300", 8, "\334\310\227\225J\356\360\307!\353\023\332|\214\306\036\026\253|-\267\341\321\223", 400, "\267\250\271J-\265\303\247\337ib\237l>\352\344\302\3321\3006\240\233\232N\2661\236\022s+\300\1342\375%\251Yl\032kK}\227T`\364\233v0|/\217\026\032\261\020\225\335k@`\021\227", 64 },
+ { ",\032\034\337\304,\340\334", 8, "\011F\350\213N\0058\212}N\267\007\007E\276\203C\315\332\013\030`\372\030\370\001\240\030\354@9\327", 400, "\206A\034\355\042\030\025\220\000\251\025`\356\372Fr\313\246L\307\255@;\366\354\346\362\206h\023>A\271\273r\036\224\325Q\246\2527 ^\014!\240:|\335G\322\333\032\005ok~\371\372\255oD\257", 64 },
+ { "H\322\305\000\020\341\365\243", 8, "\330\313R\037\276\004\355\327\363QH\327\364r\305}\014\370\007B\252xS\345\003\346\202\366\250\375\237\315 \205\355\214Rj\037\365", 400, "u\346\0049k\347\134\211\023\330\015\360o`\217\266\234\012\007\003\270\025A}\373^\366d:n\344\000PqM\314\200\213<\204:N\0279\323z\337\271\031\337x\0007\352\220\032b \303\346`\201\2729", 64 },
+ { "\236\271\202\214\310\003r\323", 8, "\020>\356\364\222*\307l\337\312\351\343R\273pxL6|\305\324\362\273>\332\245t\3267\212\276\231\310\343\243\034X\360,\224il\313\374\217\2407\134", 400, "\224?^QsO\227|\332iTx\204=g\360\033\352\240`\330L\212\276(\201Dx\017\216\016\231\307^\204K%^\327\360\257\236\005j\010\233\371H\203n`zx\230cH\320,M\315\344%\215\024", 64 },
+ { "\353h~\2307\320gN", 8, "\235H\252\037+\014\003\265\235*v\020I\376\207\331\222\266\274\0270tb\341\276.\306w^\331\270\263k\341%\270\200\232\321b/\244\310\016\361\316\362:\375/\371\255B\307./", 400, "\221Y)\236*\214\226e\360\324\257\367\230R\014_\314\214\236\027O\004\305\370\317w\257\260\001w\245\003P\371\335Y\240\227\365\243bh\254\372\247\375\301)k\255\245&\262\036\374_\026\224\326D\220\336\246\351", 64 },
+ { "\362\023\013 \331\240-M", 8, "\353~\003\260'd@\026_<\301\360]EU\332i\336\177g\347\313\232\337\3562\010\250\345c\235\265\236\343;\322\302~\317\371\002~\027\012\332\301r\014#\322\004\353\261\275g\263\017(W>{`\322P", 400, "SA\003b\271\034\227\224gN\023\203\207 mW\365\257Nz\267wn\016\221\017g%(n\256/\330\311\377+\335\261\343CA\015A\272\352-\345`\245\370W6\020Y\215\2212\000\272\001\022$*\376", 64 },
+ { "\250c`\236vN\302\210", 8, "\257(\256\0171z\243\023\042\134\363\223\252}GzB\307\261\220\301\270\027\327\326\353\253>\313\276\006UAd\355}\276\320)\375`E\353*-\370\226\370\021\004\203\374e\016e\275\202\313\227AP/?5\260\225\217\243\276Ag\377", 400, "\203\022\352c\27522Z\265mo\224\016G\376/C\026\000{\0201\015(\224\005!fV\262\204N\265\206\202\336\255\362\036\015\330\241!5\247\035rVG\346v\301W\306\204\335HS\371Tlq\207\334", 64 },
+ { "\327\002\265\344\005\337@O", 8, "\233\317\326\221:\2409Y", 500, "\134\021\253\352\370\313yg<\015\200\022b\317Vq\262\220\257fL\315\302\201\266\036lxQ\371\021 J\337)(2\346\017V2\310\217\030\274I\357\215\2115\277p\351\236\012\272\346V:\317\003\326\2062", 64 },
+ { "\320u\027+-\245\0217", 8, "\216\257\376\3147\035d\300\366\365\315\320w\304\236$", 500, "\001_\134H\237\352\244\340\034\274\366\034H\321\311\216\255R_U\263\221\206J\301\2641\304J~\274oH\007\340\246a\313\204>\216\330\311x*<\334T\216\251S\322at\360\234\243w3\033\2061PL", 64 },
+ { "#@\256\000\333y\350\327", 8, "Rb\213R\263\262W\317\333\354\261\340\246\26119H\334\277U\253\266C\247", 500, "\343\224\023a\337\367\347\276\245\310-(\363\021\271\221_\360\245\233\356S\236\363\027\212\0046*\244\376\3032\352\007\023Fs-\332\203#+\355\244\33654\205a\200\302w\314\335\0157\212S\227\271l\202*", 64 },
+ { "\350e\303Y\257\366M\223", 8, "d\311\323\036\030%\266j\372\006\016\024;\344\350\256\234\026\341el\325\270\236\226\020\312b[\212\353\343", 500, "F\026H&\347}\264@\307\246ap\356W\272P\023\254\204/-\006\337u\230K\221\204\312h\337Q\021\037p9\222U\032\262k~\013e\017X\134/\243\264>j\251=DBu_*\350Z\260\010z", 64 },
+ { "\277\212\2413\001K\016_", 8, "\005<<y\266oky\302\237\177\024\352Z\342]\033\340d\200|\243\204\211\253\017\001\212\237\134KR\0069\202\3539\365\375\372", 500, " :\341\020\036\347\334\222\354\253\226E`;\355k\016\2340\337\023\303x\230\204\234\222\316W\002h5\251\256\264)\302\347\007\034f]\015\217k\263@\236\004W\366]7\352E\033\345\201\311\204Y\353\2310", 64 },
+ { "\366@\217\206\246\210\347\233", 8, "T\203Q\310Z\350\354Y\231\213n\276c\134\3137\035+;\306\254j\247>#\022(`\277\021/J4~\363\354x\207\271\211Ft\233\026\252\326:\236", 500, "\220\344\307\355\203\332\013\207\2504\364\207\303\026\316\251\026X\367\373sN\225\2540\243\006\233/l\224\370\310\346\352\227}K\267\223E,\010G{e\270>})\274\312\001\3350\311\261\367?7\251|i\243", 64 },
+ { "\271\035\035\334\321\366u\304", 8, "\325L\272\252\211;\134\014}^\270\007<\316[68S\361\3264lA\240}\025l\032\265\036\247\227!\035x\354C\262<?\375oQ\205\377\201\026%#\211\037\274\345\020\347\301", 500, "\246\035\300\203\200@\345TZt\032\374\337\024!\350\023\221P\017;\257\025\376J}\317\305P/\013\312\375G\014\245\001\211\212H\015R\006#\274ca\360\255nf\276<s\220T\373#\271\345\305\211\015\304", 64 },
+ { "\241\322Q\333\203\362\233\316", 8, "\214\375\234\011\345\017\211/\020\021\006\026\257KLP\346\263\042\242]\346\216d\251\177v\301\375\332Ys\321@\217\3775\355\216\313\271\351\336\253\326tA^\242\307)^\022\371\342\217D\030\042\206\346\134\025\361", 500, "ET\245\031\000Z8\234\203\270\363\325\033`\220j\205\134J\343\020@K\030|[\370\362Px\037m\215\300F\216\220\305\134\035dQ\2661\315\300\250\366\022\303\012\257,.\026%\344g\267oy\271\307a", 64 },
+ { "\230l#\3474V\000k", 8, "\277\2702U\260\313\030\303\001\210c\206o\374B7\033\363\230\257\333\372\244\214\362&\377\037H3u\012\204\222\221N\042\327\235\301Z~\361\356\273\304\365\310\033\350\024\034\264\013\361\242h\305N\327d\001\252\357\027\322B\263\337\322\262\034", 500, "\034:\344l\216\335U\036\314\346Z{\215\377\233G\271\351\307J\273l\370b\007\203\036=\024g\207J\007!\015\327\335\333\000\214\306\307.?`\240\241J\236g`\305\264\342\327M!\262\3735\235\236\016\243", 64 },
+ { "\350b\311\324\315c\012h", 8, "(\007\243m\002r\024\360", 600, "bO\231yF\305\225K\256\036\277\234)\134\263&\030P\352B\010<A\025\376\306\331?\213\276i\016\021.\265\310\335\346\323\354x\367\301V\014/\207\317\231L\201g8zM\017\323\212\221\021\367g$p", 64 },
+ { "4\252\245\207\245\212^\274", 8, "Q\376\241\252\2757\010\225\006 \227\200c\015\253\262", 600, "t\345\035\277Ok\031\022\275>[\177\320\324\305%\333{\317*V\220\027\367\322_\234\017\253]\024\347dDB#>\373\003\216\347\333r\220Z\334\301\346\356']\323 \375t7cQh\010\004v\2253", 64 },
+ { "\275\303\226\316\005\315-\300", 8, "\223\241?\356\014`\255l\337ad\241k\345\355\226\334\266@\217C\221\237\005", 600, "\312\375\227\367e\311\261\367\012\304\303\372K:\226\326\010E\235\355A\225\331j\026n\3264\303\301\324\214YnM\261\310\020\002bi\324\311\247\011\237{;\013\216C<> \017-xk\370\265\303\333\311\014", 64 },
+ { "J\242\213\023\026l\3506", 8, "\303\020\346\376\230\303\347=_\355#\0015\363\345+\233\216\203l\347rSa\272Q9\336>\034\257\233", 600, "\3331\377\232\361\326D\362V#\2124l\237\327\215\231\375%|)aM\037\201\030\027\341OxFU<\306\302\355fO\326\325\242\177A\254\316H\247V\227\342\032\377\002\256VY\220\303\363\206[j\222\226", 64 },
+ { "\222\360%qi`\034\316", 8, "\370Hx\232\025]yH\337ys\310\032f\317g\371\315-zT\225c4\033Uy\021\314\013\337\260\247G_4s%\261\004", 600, "\336lk\337\003M\223\020y\344p\356\250\000\301z\337\263.WN),\335!\375\227\210\367d\263Z\214Y\330\333\264\030\004\326\011W\005\323\3448\354(^T'\200\255\301\320\012\355F\240\271\203\012\353\313", 64 },
+ { "0\015\376\022\354\343\255/", 8, "\304\347\345\335\327g\331\204\343\025\246\030\247y\365-\266\264\361\212\366\211\035\216\276+\235<\371\321\226[\250\336\346tn<}\247\2076%\231\373\227o\351", 600, "\201\306eLu\262\032\355\246H`2\246\247-\007\217H;\017\370\277\237)\275='\001HG$\134\223N\000\335n\304y\272\205N\2265\310\305o\250# \235r\312\236\217W\324\360\323\007\351\215\232+", 64 },
+ { "\030\253\370\026\377\013s\252", 8, "\337\202p\313\334&[ql\346\042\350\016\272+\322|\004S5d\223]9}\250\253EW\357+\215\315\255\246\023\352>\274\001\027\231x\225#\261:n\342\012l\253\005\036\134\327", 600, "\223\241\274\227M\2322t\205\027\016\377\267\015\350\012Y \321\217\000\305\305Y\322\301\260\316\230\200\003rQ\235\245\177\003\305\342\022Iu\375\323>1W\002\013\321k\264\334?\225:!\377\014\331\206\270\371'", 64 },
+ { "a\033o\014\314\261\004\243", 8, "y\340j\235ZH+4\023}\372=4\262\303\213\134|\220\252\255\0014\021\262\355\224\212\312\341\277 l^\021t\261S^j\007\313N\005\277\340\224\345\320\346\226eH\042\313q\271i\317\023U]B\331", 600, "\355=1\261\000\236>\260t@\241\370\305\323\2139\016\316vb\352\2533\336L\015\371\321\265\211_\273\177c\311UAa8P\263\017\272\037\217<\201\327\205\002IV\227\034\273\002\002\3312#\243\225\327b", 64 },
+ { "[$\035\042a\220:c", 8, "j\312\316\373\207\241\335\304\0327\033\263\274[\305\234\371\224\253\012\266x\340\224V\230\335\374l3\320L\252\306qA\344\3069C&H\265\211\306X\347\311:\376\223It,\251\300!h\24791\371V\007\375\233J\032\330.\236U", 600, "`l\213nT\353\237\304\251I\256 >C\231\000X\377\332\206\204K\262Z\353\327\022\265}\245y\232\3368WI\332/\213\316\042\371\200\033\0378\322U4J\363\360\357\350\361*\367.>\363\377\334\0232", 64 },
+ { "\354\304\015\017\355\240\021\276", 8, "\247\274\225<\353\357\376c", 700, "\374\221\245\277Y\007w\274\375\266'l\316\243\333\312\223\257\301\270\303K\337q_\331\354\347\376\012\223\275\344\001t\373F`\235\302\3108W\332\205Q\223d\004\207\223aki\277\307\313}\326\254\234v\042\341", 64 },
+ { "IZ\367\257\273\377\332k", 8, "i\027\226\010\004~\257w\273?\222\224\212\274 C", 700, "\253\034\346./iA\3661T32Zw\312\266.\357\034\032\202\213\332\320\356\356\327\275\217\251\326\356\042\377^\270\362\346W\367\353\237\331B[\021\034\310& Me]|\355\274\207\325\372=\373iR\177", 64 },
+ { "G\334S\242\304\334Y<", 8, "\330x<\264\323\016\237-\300\231\260\364\210B\314bW\255/\235\014\277\3450", 700, ";U\042\252\241\200\357\247\366\020?gb\0358\231W\354\331\377MS\273 F\3361<U<Z\330\257\327\036\273Vrw\017\241\310\221\332\200\221 _up\365,\346\030\231\206c:,t\227*\276\335", 64 },
+ { "}\276z\315\217\015\352\372", 8, "/K/$\233}sC_\300SM\357\361\355\006\376\303\177S\207\317\322\200\300\2030|\272*\357\201", 700, "\012s\226x\326\261\376\341\276\240Yf\256p\027\235\363\0332#/\275\005\372@\241\202\362\237\256>\313\377\344\361\004edjn\276\261[\0221\225\017\3731\203`\350a\301a\005\036wwQ\034m\323\226", 64 },
+ { "T\236\334\030&\300sl", 8, "\006\377L|^\220T\213\307-f\214\024\215\251\366\224\240\250L\364\346=\224\001m\206D\335\375i\371.\261\3026\364\270\225\320", 700, "|i\376\031T\307y\012)\250\211\242c\200\244\370\232\212\206\316$-\031~\274\264\302IJ\243\356\367\232C\314c\013U\037\236\305\313\264\207\322}\273VHX\300\321\002Yi\237\261\345e\031\005{L|", 64 },
+ { "A\313\345\257\373[@]", 8, ")\227\310\042\306\361\235\256\363y\337\341\265\3144\344\012\342\3540\260>\343\246\214id\013\254c:\203>\020\365\267x\243\013B7w4\235\267\015\330,", 700, "\0428\377Wc\042\366\266\227\254\030_w\263g\202\376\326\267\253\336\265\353\026\200t\017\354l\227\352\314[;\364\263\361\203\215\220\374.\272\042:.@m\033\250\274}\3646\000\330b^\363\364\327(A\015", 64 },
+ { "\210\332a\312\367\024()", 8, "]\215\337\017p\215\251/\2747[\206\312\314Zk\267'\324\223c4\265Z%\315\210&\014\021&\262\270U\274@\374\221\0262\2720\3360?\274\214\330\302\373\240G\205RwQ", 700, "0\3646)\364\337\3250\333!\010\356\306D\241]wn\353\303\265:\374\315~6C%\250\346\234\231\367\330\367\220\221\324\255\221\375\344mc\271+R\324\374z\027\237H?k\343\211\022\014\375\134J\256\014", 64 },
+ { "\305\354v] \232v\253", 8, "9\230\361c\001XS\355\303\361*\366\267z\363\226\237\331E\336\222#\305\037\326\005v\336\375_+=\277\212_\0269!A=\235\007h\254\370Z7K\346\277\246\360*\015Kb6\303\022\224\322\026M\313", 700, "\253\232\256))\226\212\353\237S+\307u<>$\3440\367\363v\222\250\321k:\323^\234P\351\236f\205?\003F\326\016\355\311\244\023\346\321\243|\256!\273\2243y\226P\026\323m\250\306\360Q\267p", 64 },
+ { "\001\363\342\257~\177S\275", 8, "B\012\033\376\003<9&\264 \214\003\212\001j\004;/\321@\231r\267\024\302\270\344c\0306\262\215\205@\263_\360\033j\3177Z\220\042E#\206\231X_}j\012\340\365\325\336\376D[\273g\366O\377\300\265\373\256\203\2779", 700, "\013\331\003N\240.\215\336\022\230\317\271\042\376lv\311\036\305[f4\351\314\237f\323e2u2\272g\225\261b1\027\246>\251\235\261\326$\243\201\342\250CG\224MY\3737\225\312o\355TU\032\275", 64 },
+ { "\017\327\331x\230\001\363\353", 8, ".\331\317\373;\330h_", 800, "\265udMk\214\004v\236Q\366c\336\020\010/\243\236\262\020\320\004\016\241\371\242\232i |)\240\305=\266\316\201\350UAY$J\243\230\267P\377\011\000o\033+\211\236\020r\356|\261\317\033\355u", 64 },
+ { "\032\276\352\212\347\313\231;", 8, "\311\365\134;\311+\024N\037\134\0020\362\354\324x", 800, "\276\004-\013\036\334\372\350\311\270\306T3\014\230\351\015\230^@\330\342\304\324\034F\264e\367\013\273\355\251\260\271O4\257\020x\362*\354il)\363\201\017\232\032\344:\312\037\005\341\325\3720\326\366\002b", 64 },
+ { "]]Yk\304T\222f", 8, "H\031\273\324T\210$\267\222\324\027\270_\2514\214\347\317o\177C\272\313\012", 800, "\325\007\134%\256\3015\242\203%;\261\350\324!\262\367\002H\177\355\247Mk\042m\373\263\306#\301\3472\354\344\353\031\346\246\017\202\342\306V\005D\266giX\205\012\016H3\231\344bn:;v\225,", 64 },
+ { "\346\320\306\0348\265\356\000", 8, "\376S\210\356d\366\033O0\352\265\314ZV\263\3701p\023\262f\227\356\025\361\002EV\204\337\304\206", 800, "Z\2104\306`\244\211\342\010\204%\343\240\207VL\375_T\342\360\031\017\257_\207\345\327\014\347Fn?\331QY\310\375\334b\244\337\241\003\370\202\0356!\305\326z\330@\201\0241\244\332:v\371/\264", 64 },
+ { "r\206Tl\304\226\231\356", 8, "g_B@\2277\267\027l\314\247W\306\346\221\226\223\201\013\263R\245A<\024\177\007\230\222|\216\331~\233{\350n\244\341\362", 800, "f8ma\263y|\237\2600q\021'\276I\3642\276\344\255\345\032\270\273\032\262\365\026\004\364j\211x:\330_\207\250\336\311\016g\376\261\366\307\345\223\204k\362\355\252l!\357X\211\003\035L`]\341", 64 },
+ { "\016\320!\037;\311\210\213", 8, "\347\042\302\3575\315\031\013\267\340\377)\031DY\177`G\316D\251\025\327o\311\233We3\236\022L\017\354\314\342d\207\343\017\345\235\010H\210\225E\345", 800, "4V\003\007\215ht\377\262zSoW\277\3314\316L\376oAw\367\015\224\304\201\2176\277\375}9r\042\304hZU\374\306\202\205\354\2167M!(\371]\313\357]\233\005\345\256N\252]\177Q#", 64 },
+ { "\347\274e\2157\327\016 ", 8, "\272xp\134\303\305\032\372\217\330\225\227\354\372?D\027\354\212\376\311\236\260?H\362\257\006\261\003\350|p\266\331R\363\036{\332\335t\021\327\213.\253\202Ss \335\252#*%", 800, "\1346\322\026\247m\011\012,\203\263\2449,\344\250\235\360|t>\225\277.Z\215\037\226)5\356\345JV\334\234\211\357c\024=\2534\257\026\225\005\023\221\330\275\010\246\011^\374\034A\246Pd\212\234\317", 64 },
+ { "Z\207\036\243J\207\322D", 8, "\323\326\355\204\245}\341\034\322\331\364\234\240\241\260\2203,X\355\362\335O9^V\212\373;\214\342\334\020\316\234\214\324H\317wH_\313S\257\335\240e\007n\372\036](\317\024\331\231`\031\023\002\243\033", 800, "\224\343\262\023\213\042XE\317\300\260\327\316\217{{\322\307\234yqc\011BMd\312\373\375M\323]@X\3400\012\2175k\235_X\307a\134\023\376\035\323Z\312\011\376?\346\006C\356x \332\323\227", 64 },
+ { "4\324\256]z\277\006\304", 8, "Os\324\324\223\211\353X\217\245\344\317\334w C\265\024\371e5\007\235\230\364CH*\036\243\251\222,|/\270+\31386ywY\202.ByB J\245p\237(\315\024l\362\240SC\352\261D\24176\026\024D\023u", 800, "\014\217}\334\346\012\202\320\323x\317\341\363\307y\261\134\355\0259|\237\224\200Q\002q#\232t\336\215\303\355\020\263\221\0257\205)\374-\316\271sj(\000\332h\303&\003\323%\3052x\035\230\3673\267", 64 },
+ { "\2311\362\360(*\314c", 8, "\226\205\332a\235\200\177\002", 900, "\266N>\311\351\345\223>\327\365/\202\006\211\317\345\265z\001\276\340'\355\303QyH\367\311\042\032\350\331\012\034\261\327po\323\275E\317\000D\234\355\032\241\242\332]e\276Pk\264\000\211ia0q5", 64 },
+ { "\020\277\212\360H>\226S", 8, "\333\342rh\323\302\366\223vK\376\004\340\213\316;", 900, "\344\254\354@\367\243\012\310\303\042\263\330\230\230\307\266-\011\134\024\221[\371\320\324\244\375^S\314u\303\335:<Q\220\026 o\356G\134\363\015\256\313\250,\0069\302eFSC9\211\000#\372x2d", 64 },
+ { "\302\324)\317\203\362C>", 8, "\035)\304;g\254m\313n\232Rn\276\022\264\317.\210WB\272\026M~", 900, "[i{\304\237n\213\314\375\2223w\134T\257\027\265\307\015\264\016\022\245~\250\031\257\342w\211Q\242{\012\370&\003P\016\311\032+\235,F\260\362\327&2wxw\030\277D#\365'c\216\303A\356", 64 },
+ { "\324\210\245\303\375jc\134", 8, "\003\042\006\004.8\256\321\013\216'\227\210\353\226.\364\037\300\351e\206\357JY\201\254x\015\352\023\212", 900, "\2727$\177\202\237\0142`&\034\321\224P\3620\013\367pg\371h\315 \134\227\306\241\257\3333uS\025\206\030\2704n\013\354^\032\034\266]\024\254\000\256\356\2356\331nz\263\310Q\2178\201\310\320", 64 },
+ { "\322;F\214L\230wH", 8, "\330%\316\307\264\310\375q;+\256*\315h\300\274\215yZ\320\360\203)\353|\310!\016u\260\350\222\323\177\303\226\212o\3255", 900, "m\267\007?\353\220\220$\231\316;f\201\253X\312\320\006{\307\014\334s\3257\277[^\0055\2567\134\270yr9A\342\273N\217i\374\314\364\301\312\335G\036\014R\341|w\323ei\226\014I\034C", 64 },
+ { "\345\342>\335\2457/,", 8, "\355\262\340\201\134\012o\2011F\234^\321\211\216w\207\022[\361\310\004\236\256`x`j^\263v*\2250\336=*\340\017\317R|\00164\353<\244", 900, "\004\240\005\275jv\352M\360\350\036U\306l\237[\031C\224\205C0iT\370\242\011K\042\030\333\375\302^\255B\301\354\310\240*\323\257\310\222\240\354\233\221\346h\311;\214\321\240\341]\363\326\335\022\216\224", 64 },
+ { ".\245\313\031\277\374\042l", 8, "-]\026q\347=q\004<\002@^\242\277\355\212\334\337M\304B\351\015:p\323!lA\357\204\206\364*N\317\363\016\016\230D6\227\337\347*t\243\177\021]Y|\240\035\006", 900, "p\006\336\010\351\337\304\260\372\210\267\013\231 t\265\231\274{%DScF\3726\320\205\275 \011h'\251B\363\323L\014V\260\362#z'\233\322H_\306t\273\027\353\272\021f0Q\222\244\0316\225", 64 },
+ { "\014Y>\242\331\270\367G", 8, "\222\344 \364|\347CP.9\372Tu\341n\014\333\321\034\204k4y\312\314\363\323(\221\365\023\032\342Y!\010n\273\213ot\346\310h\304\220y./\235\342Pg\214\037\217\352\316\374\276\3740\256e", 900, "\211\042{m\201\233_\3020\350\237<\315\272\256\200\010\323)\210\210*\042\200~\266\343\177ic\212\300\363L\215\271\022\2609_\361eWbF8\203\271\363\214t\272\275X5\011\355<\2233[T]P", 64 },
+ { "\334\023\304\233\305\200\206\272", 8, "\314\332|aD\003\341\033_\3733\374\263\042L&9\014g\336\253\355Ir`\004\014\377\306\2031\212nX)uG\236I\021\031\337\336u0m;\3126u\332\025Q\370u\303$2*YH\211\343Z\3628\237\274\2061sw", 900, "\005Zx\257\262D\357n\005b\014(\012\277O<\313\361M\331\373mB8\031\215\035/\247\005B\343\346\305\337o\024\275\357|Y\235@\221}\202M1\0325\306\325A\253\250\241JY\326\255\032\272\314c", 64 },
+ { "Z\270\301ED\237U|", 8, "\0272-\237\010\275\377\042", 1000, "\360\355\220\212\252\371b9P\343K\301,\024$\227|\305\346\327$>\215R\243\214Vs\273W{\306\234x\347Jlb\211\355\321\310\015\002\267\352[\210\335Zl\341\023\236u{\225\264\027\370Wc\343&", 64 },
+ { "\262\3435,q\037^\014", 8, "G\330\246\256\356\260\201)P|\336R\254\301\265m", 1000, "\314\320h\267\227\350\034\216p\334\250\372\230\300\273\220s\352K\341\347\253\317\321\362\042R\311fN\177S[\376b\354@R\320\010\251W\374\256]\243\262NO\000\206\347\005\306`\215u4/!\365\025T\026", 64 },
+ { "\033\341\202Z\263\274s\025", 8, "\317\320\367\371\200\035\361Z\240oE~t\347\300\227\330V]g\337r\316@", 1000, "\234\313t+9d;3\226!\325\256\242\211`\323\030\352\336\377\306\215rZ\263#{\306\037{7\307\203\325M\032\031Q^\260[8$a\272\215\337?p\022A\335n%u\214\232\303^\352\241\251~\247", 64 },
+ { "uc\214\232\3031l\300", 8, "\013(7Vl\341\013JA\276\222u\345\271\363\015\134\317\340R\214\335\312\245\264z\324\326\366L\344\250", 1000, "'\352\216\246\314\215\365\333%\334\202\342\334\352\347\211%\312\215(R\312k\246\330\207\223\004LhVFw\374\322\331\275\265\334k7\343\013\312\010_\264\362b[\254\366\226\224hvgG\307\200\347\216\033\036", 64 },
+ { "cD\020\266.C`u", 8, "\247&^M\360@\266Y\365\322'\367\262l\376\250\042KU\360\237nOl\207\346\2520\203\0428\003\332o\344\0046`\226a", 1000, "\241u\001\027W\202YT\341\275\204.Y\242_U\016t\003\025\001\340\304\213\364L\264)\023\346\031(d\234\256\313\204\260\262x\220N\017\274\340\315\000\276q\017\001;x\375v\004\025\300\276U9\336\260\375", 64 },
+ { "a\0162\201\011\221d\017", 8, "\256aW\215\327\374\235\334;\303\032\021\037wg\306K@\001d\213L\244>\343Z\234u\336\313\024b\344`\233\271\234\333\365\305Z\306\0218\310UK\306", 1000, "\304\023e\243|\032k\320ni\270\002\252\306\036tZ\015j\373\340\331\207\245\347\032\261L\354kh\012\356\363e7\015\334\352\206\036;\320~\331\260\330\003\257\310\244\364\244\370\305\233:J\363\367\324\362\025[", 64 },
+ { "\324\304\265\306\270\217\276\015", 8, "\276\027jRk0X\215\227lG\236\240RpJ\220N\005\250b\345tQ\221h\321\354P\371\035\244$\256l\023HZ[\031\255- O\236\222:\267t\254d\313's\317\024", 1000, "\250\316\327\215\271\374\206\207\220\351D\263\306Q\362\366c|\273VQ\241\016\377\004n\314f\215\253\305\321\350\236.ZjX\037|!\314m\331\3775\011\034n_\263x\267\345\241\322j\230l^G\334\256L", 64 },
+ { "\030\302&\234\200)\230\305", 8, "+L\221:5T\274h\237\256q\323\353m\333fh\201}Q\351*\351\240\267\276\327\007\254\316\214J\342\350\351\367\367\266\301\010\3775\015\014\351\337\3264\355K\027\355\327m\300\241@?#\232\225\263\037\007", 1000, "\201\355F\216\035\037@\350_\232\325Mx5\353\004*\372\250\370%X\247)\315\324\314\256\315\242}?\373L3\346a\331\302\134\334=\237n\275 \330_OZ]VW\007q\312\254\244\276\024\034K,C", 64 },
+ { "?\362\015\253q\331|\250", 8, "}\020\020\337\215V\263\334p\346\003G\363\301A\333\134\177\222w\271\262\306>m\2774\343\006#P\260\306mS#Gs\250\363u\230\227\377\015\361\235\336\273V\230\316\214\276l\201\272\241}\361\260\357Aw\015\263\246/\366\026\375\037", 1000, "u'\305L\233\270\032\326\225\2422\323\347\335g\374\270\023\352E\305\332\240\231\244\035\343\226\322z\264\346\354M\036\211ye:A1\257K\236\215\333B#BV!\333\245\336|\217\317P\257>\257\265\252\003", 64 },
+ { "(\134\324\014\245*\260?", 8, "\263nO\374\320BL\042", 2000, "\354`\262\357X\301\351\240\333\374\3536\307\367A\273gVR\366B\1775\217_]\214\016\2641j$kE\201g\270,\276$\213\205\250\237\005\211D$\351\241\334\222\337\326\360\372\277P\002k\224\235\2137", 64 },
+ { "VC\260\247\227+K\325", 8, "\214y\202\334\330\323;\206\274k\026\333#X\247H", 2000, "\005\366 t\203\277\030z|\241z+\222\344\134C\306XV\367\042\032Y\2074j\211\034g~\013\263Z}\354d\342\246\005\011\250\230\244\222\336(\224\320\320\221\237lj\227\003\345c\311P\023t\000\243<", 64 },
+ { "\226\264\332\250Q\332\033?", 8, "6\177z\010St\034J\240\224\003\367\252I\322/\034ZS\243\322\307\374\311", 2000, "\225\325\364`R\004\354\021\306\2123\337\341M\273\023xsu\030\210\267U\375\011\223{\002!3a\031\254\247\200\302\265\016\277\301\235\024\014Vj\363\022\367H\224\372\205\337\302G\035\351\257~\316\371\252D\342", 64 },
+ { "\347\016\225\220\026\324\301Y", 8, "\351\314\237M\230\305o\321\374\374\253H\236(*=1\364\203\007\210\344\001\245\2736\022\340C\314\265\224", 2000, "kN3z\003R\340\020D\033\221{\234\352\211\201\315\356\017\234\370\270\214\035J\335KM\255\200I\010=:\3512\030\260(+\257\276\275\001\361\263j\030\002\224\352\372\031$q_\023\330\333\021\233\240\222x", 64 },
+ { "\340r\267}u]\377\350", 8, "\242\360\243\361\363\020jX_g\376\016X\352dF\022;'+\306\213\375\036\224\214A\242L\022\334\025\231\015\267\305\001:\032\270", 2000, "\230D8\251\010\031,p\016\330\310\233\241O\031#\310[\324u\261\207\2042\361u\310;\366\342[9\201\005\037\20261\316\351;&\273\325\263\305\325@\251\027\343Ak\225\270\014K\342\037\015\305\247e\231", 64 },
+ { "\013n\007\254\357\255\217\340", 8, "\334\015\217\312\237\315sW\026\357{W\321,\031\034\374\023\2720y\251\235q\030\2170\357\251\366\370\364\312E\205.w\273r\242`\246\005}>,\231\250", 2000, "w\210\363\2605\020\134\374\231\024\327\314\205\310\325\247\033%\322T\226\271@\302\004\266q\304\345\326\275\347\260\322\361\2435\344\010\356\300%g\307\217K\0423!`R,\005\327\201\0037U\266\2705\275\343r", 64 },
+ { "\202\361W\024^\252\307\235", 8, "\356\247\020\315\037(p\361\006\223 \261\035\332b\222m\236\027\367n\020dw\323\247\3017:8\034\256\302\363\263|\273\134TF\260M_D\3418h\237\365\260\262\257\007\344\231\351", 2000, "\327G\376I\211\335s\3060\367\277\200\015\270\327\351\023\3539\245\203\356\244\367Y\244\340\277\207\235\360\027\347\352\343.\301\3570\347\330\324\352\353S\242>\324w\213\271\262\013\211\250\035\261a\011\271\370\034\3633", 64 },
+ { "\207-J\204\243\334\335\016", 8, "\261\134\241\340\024\3441\306'\32061\256\202\267\232\370\2327\257\357\342R\202\326\345\300\362Cp\315!;\320\305\240\013Zm\234\343\217\373f)#\223\343s7\320R\342d\224\027\212,+\304\262k\243l", 2000, "\271\317r\177#\201M\306\320\314\317e\236\042k\031\032\242W\364\321\255\026YkJ\371zp`\337\264\212\253x\034Ef\2003\214-\370o\277\305\3671\355\013\320C\247d\214nA\273!C\375\025\2218", 64 },
+ { "\300H\213\371\272>\337\241", 8, "\001qO\241\310Gs\274\036\375\272\362\033\014\256\327v^\037\321\246\277e\224\300\235\231U\367\316'\014\327\232\265Z\033*s\302\252}xo\374(a\231\355\301\0350\021\231\024F\177\276\341\221N\316\025f\320\324\134\247\376\335\274c", 2000, "\301\360\262PJf1\243rl\320\364\320\017\312@~\240\031\206\211\261\330\035?\314\356\253\263>\016\252wt\341\213\001!\014.N\264\350\377]\243\234\245\354\325Au\353\227\236;L\333\332k\340\343\352z", 64 },
+ { "z\015\256n_HF\264", 8, "IO\305\325\253\232\001\022", 3000, "\021\032\252P\005v\354\234q:\204\326\203U\2162\246\371\343\340b\327+\037\330\360\241_\215\134H\214c\373Ix\364j>\036L\016\355i\226\034Q\370\320'\320'\014i\336A\210Z|D\200\006\006\202", 64 },
+ { "\027\035\353\321\372\222%>", 8, "\375\3772\334\316\222?\333\300\325\265\005\360\361\347F", 3000, "\231\0011>Jk\031l\324q\271\2308\316\025B\362/JA\010\370\021\377C[3\016G\260/l~\042\350_8\252%\373Q\275-\263}>\356\330\337&\005\265L\353\377\021\324~\255\371\346\316R\204", 64 },
+ { "'\331\374\255\355\315\206^", 8, "~Wx\355\211\365/\372@\022c3\337\311\233\253\007\215j\356\264\254\321\032", 3000, "}\344w\013/\000n<Z\343TQOIyz\327\340,o\367\240}\006\023\233\010\0006\313z\311\343\007\0073\036\011i,Jp\006\210\201\246\225\221\230\033\205\037\361B\247L\001F\373\011\347\366#1", 64 },
+ { "\207\214\016^\012\327\344\220", 8, "\335\222\345{\030\333\011\253\244V\255\206[\304u\202\265\202\312\275\232\314)\303\252I\375J>\371<7", 3000, "f8\205}\321\371=\330\3410\034\030\354\323\033\270\333\227\320vUS\336\305\010\332`\306q8\351\027\034n\372\356\363$u\376@\017\244\3467Ft6H{\264\345\275k\302G \334\202\275\257\375\242r", 64 },
+ { "\241\364\000\231\250^_\277", 8, "R4\332\217\332\374?\341\030\277\345\025~Q\364#l\307\273\331\357\0352\361\373\246\320M\220\250\265EP\225\221(\236\026\332\272", 3000, "\177\243\277\322\005\134\016\015E\251\300\205\250\352\366e_~\377\251+\223\011\021\326\042\033\277\267\206\037\224\231g\260\031_V\341.\300{\243-x\025Q>\177u8\223\177\032\201T3\360\033\336\201\255\234\024", 64 },
+ { "\020\011\277\006(\215\353j", 8, "\215\341\262\207s/M\323\274\341\223\322q\215t\177\225\246\367?\326\245Y\330\026\221:\370`u1\305\377\261\215p\311[D\0229\200(\205\216\341\247\230", 3000, "\236\022O\372{|'\313\011\216[d\364\210\030\36249\2122\266\221\022xD\266\363\207'z\215t\2448\007Be{\272\244\207\251\352\365_\353t\333u\355\317\327\327\205\222\250\003\330\201B\023\004#5", 64 },
+ { "C\251\361u\026\34102", 8, "5\343\200wqFK\2739\260#\224\301\256\323$\247\033\272\322\346_\227k\274\250Wzt\3062\134VI\202\234\325fv\013*m\003\355=R\366\214\233\305O\333|\200\376b", 3000, "\030\177WT\007\014.\134XcB\245\202\004\363\021\313E\344M\216\332<\330W\360N\314\134\341\255\030%\031\323\024\035\312O\370\213\017\263\034*\261\344\031\356\237\271Oi\213.\276\315\314\367\222\212\027\024W", 64 },
+ { "\311\254\331ZS\226@L", 8, "\013\320= \345\032S\317t\255\2550\245<G\371\373\277\254\243\377\332e\356\235\335\006k\224\012>\3062\232\316\241\002\235\333\015$Lx\376\026\334K\010\360=\237*;\007\134\3506O\352,\037\322\342\013", 3000, "\344\202\347\365^\026e&\200\260\214A\324\264\022\313\225\351\033\254\254o\357\255(\215^z\034\200\220\312K\301\240\227MO\030\231\366\020\031=\203\012\224=\024\004>\316W2:\363D\365\231hy\366\365j", 64 },
+ { "\134#\216 D[\241\223", 8, "\017P\274\200^(R\220\266g;\211Z\265\036\345\337\360\2310y\031\330\206rF\253\254\375\271dr\321\367iT\304\2003fV\367\333\264\247\2370S:\377&\216\360yfs\202h>\327\347C\261\345\274\357q\037\331^\305\134", 3000, "\177\373nj\013\324\202\021\035]!)\370\370%\005\353\016~W\330\265\217\362\337\301Z\275\321V\000g71\335\377v\013R\335K\315\241\225\216qE\312\272@.\020\315b\247l\3359:\242\265\000)\310", 64 },
+ { "\304\326\303\344\272\260A?", 8, "`f\262P\027'\211\217", 4000, "\012^\363\365\134\265*$\014\270]F\274|\375>p\341\003dT\242\337u{\314\205\307\260\360\322\025\214\007E\307y\365\352\310\240\372\134Fji\370\241\017\305\336\262\351\332\002{\026\270\376\363\266\134\274;", 64 },
+ { "1\2004\011\006-\032R", 8, "\353\371\330A\261\263)\213u\007\233-\354\230!\202", 4000, "{o\014.\322\016\323\244\021\037&W4*\362\300)mGv\014\016\306\307\237/6G[\267\033\213u\313\266J'\276GtN!\000\216\363^7Y\037\370\021\014\333\250\374\332\274\235l\230\263\220\347\222", 64 },
+ { "\354n\227\202\363\306j8", 8, "T\334\212\366\246\020\042\264\342r\271\317a\0047\322\317\354O\031\202\342\370o", 4000, "\312~\260\260\027\266G\247\236\312`n\335\342\262Ec\247\272x\001\003\252y=\002\362\362\004\326\350\007\0252!\263\312\360H\344\246\016\207vEO\022\205\212\306\276\251O\325;Gz\307O\266}\020\322\376", 64 },
+ { "\360\263\275l\234\244zO", 8, "\335\375\273\001\261%\233,\007\350\255\247\342\235\310-\032\035\314\246\252\027+a\233f\204\274M5W\262", 4000, "\317\365.\214hyP\313\205\356(4Y\251\224W\367\263\361\027\262\324\277\255M\010z\263\314?0\025\003\367\332\236.\004\331\302\232\011R\332D8;]Dz`v\000\246\242a\247b\033M\031\270\011?", 64 },
+ { "v\327\346&\035C\320\322", 8, "\260 ^\376\355\375\2567_\272\252\255Wb\376v\0057M\226\346\031\272a#Up\264#\015\311\342+\2128`(\224\224V", 4000, "`s7\365\307p\250\356x\310^7\320f\2442mm\242\0207\023\360\300\264\336\252.\266\244\202\134\042\311\023F\374\206\225'\371|\327R\371\277\357\307\304\250\352E\016\253\010N@-/9\257'\0235", 64 },
+ { "\252\317\025\232\012l\372\275", 8, "\216\020\363\015\245F\277y5\254}\016'\272\204Rf\226\340\341E\230L\024\352\205\312\306\340\042\241\017\263\375\337\355\013\020KJ\273\342\3572^\313\312U", 4000, "\222\010Cw\275\317./j@\012\032LH^\222s\205\240\335\011\026\310\275i\246\264\347\305\302\365\302\336\221}R\016]\231\006#G\321\222\255\215E\262\272\013\202\260%~\027\350U\031\220(U\210\221\314", 64 },
+ { "\035dQ9@Z\267$", 8, "\204]\013SSO\332\324\322\327\216\362D\203\253\241/\262\21093\374Z\213\001t\376>\207\024e\273\371.\037|\036\334p\005}\353\277'/\346\014#NP\362\2504w6/", 4000, "\252BW%6\265\317\000\271\024C\010C\202nYZ\027\006@\334\321\365g\371z\017\3469J\374\262\366\242G\200\017\363\313\223=\251s\213`\343\362\007\000~_CV\336\207\240\352F*\313#x\212P", 64 },
+ { "\344\263\246\321\357 \003\347", 8, "aT\363\245-\012\023\032\333\253\3049D\372\033\332I\370\005o\267\177\340\226\211&\356T(\210\017\355\372\003\207\277F\012\010\221\213\274s\314\200xLST\260\375\217\013\331wG\240\343\344\347\347\301\324\302", 4000, "\324\336\003\003\337\337\215\354\327z\217\0319E\000\020\353\011\366\347\001\035x\3660D\332\014\276\374\2363\3635\026\227\031f0G#=\015\213\275\210\237\032\207\342s\002\263\2438h'\301)M\351\213\200\274", 64 },
+ { "fJ\325L\311M\364\333", 8, "\2640\311\202\3471\2310\011\271\345\002\265\225\257>\220\277\205\311f_I3\247|l\351\222\341\353j\240|2G\035(\353\203\250\031Jl]\317\226\223\343\205]Z\324\356\245,\264v\030\333~t\035\220*f\027k\207\364Yr", 4000, "42\335%\022\372\310&W\2374\335bp\217\247m#\210\006\207\021\371\134N\306\335a]S\274\350y\247oZ\024\272j%\326\213;\324\273\347w\300E]\347%'Z\261\206w\2350(\265\205\301\302", 64 },
+ { "\363\237$\002\006\023\357\001", 8, "f\327\370v\0061\026\200", 5000, "\331\303\025\301\344\370\222\004z\275\364\374h\253Y9i\027md\032\020<\042\017\250\261\317%#ne\303Z\324!\354\300\006\235\374[Y\341\036`\373a0\357\357\015\246\3464\352*'\241\272-\317\325M", 64 },
+ { "\332|{6Q\346v\313", 8, "\134!gL\026\024\376\2430~\306\2743\232c\206", 5000, "3\325\201\34598\202\330\263Y\005\244\321}}w+\245:\274\177\255\365\265\000e7\375\312gc\261'OG\266hPp\365\250\364\042\333\373\323|\205m\005\2572\021\027\271]\233\360\216-;\210\371\274", 64 },
+ { "\333\220G-\240N<G", 8, "c\353\354]\365w\033\206\365\244\366\247>\205\232\303\177 \012\324\374rl\034", 5000, "U\342\2200*K\262\010\177\273\024\2455|\367\336\2311\225\366/\005b\305\345{\233\301k\030\023?e\250Cf\007\134\036:\377\213\027)\353%\323R\322:b\255Rqr\007!\020r6\305a)F", 64 },
+ { "\234\004/\345\032\000v\220", 8, "ar\031\272\305\245\232*\007\247\025HL\274\010\336E+\311\336\2557\266\300\263%\3031\337F\227<", 5000, "\320\254\3477Do\003\227NL\334\355\217)\317\302\221\344\300\320\356\204\266\350\305\223\275\273&r\327\014\261\352\324@\356\032\325\221\330C\346\202\233\013\007\304\031#h\202\244L\207\236p\351\371\335\213O\341^", 64 },
+ { "k\317u\332\217\324\322\216", 8, "\177Te\253\271\260\255\276\353o\223D\324\001s\317L\033V\242\247,\2566\233\206\313\315\014\332b\317\312\134\322\216\314d\344\344", 5000, "\316\021\033\027\375R\207\327\021\341\340\253Y\230\016\257\355\024\027B\226\322\346\025\017\2429.\336\2262\352\324\375\225\356\367\353\235\012X\011\361$Q,s\240\033\372\254p\202>\343\034^\303<\211\242\273AA", 64 },
+ { "`\226\222T\332\377\223\015", 8, "\351\350a \311\352J/ <U\222Hk\374\355l\264\236\357\307\205aH\004)\377\340\277\355\272\274\252\351\360\364\331\310:\236\032q{\211\331\036\016\007", 5000, "\240_\025\006Q\261\013\276\237G4\304.\260\203V\320\372\010@\334\233::\330@z\227sz1H\0061N\202\365\341W\244\273\357\360\222\333>\015\270I~3\360\2378\265kP\302\371q\202\3006\000", 64 },
+ { "\303\327\253eb|\362\227", 8, "\30630\364^\006>\227\224\315o\217]aL\236*\256DZ\270<^N.\326\014y!!Ng\375\325P-v\220\341\206\315'>\314X\315\364\276\014L\324\212\374\354e\230", 5000, "\267\272\262\235\213^\042E\255\330\372\010&\012p\354nBG\344\246\266#t\300\237a\271)\275\010x\356b\274a7^\327\303S9s\343\203\236!=\332n06\241\312\254s\335\265\334\356\324\367\177\375", 64 },
+ { "Z\3378}!\257\020\301", 8, "\301.\2642Q\250\277$\271\334M\307\250jz\364\354\361+K\322\275\343\242\322\021Z\2747\333\213\241\224\011\0320j\267\011\337O\023~{7/y\350\012_\202\331\216L\206\003\006\026iQ^\310\036\360", 5000, "\216\202S\222}\336pr\331?\232X\323M!\202<\372\263%\353\264\334\035\370_\323<V\336H\234\254]f\315\335\357\255H\313\245\270\343\205\232\262\201\246\261\310\315\333}\266\340\204\363B\256:\300\006\365", 64 },
+ { "\344\342_\244\230\227\337x", 8, "7\255\0124\272=\224\246\241\357E\2452?<c!\223\037l\252\222\316\362J\034\204UL\324v\015\234\252\366I\351\212[\267\312\003_\275\364X\312\204\006=\3528\253\252~!\202\225B\203\3253\333\005/\270\222b\332\342\001\020", 5000, "\342o\255kBZ2\361e\364\203\035\013\343\323!\276\331\271\207WE\207\375\304\222 mI\365\377\332\270\042\344\032\033\225oe\317\247w\336\305\345\3711\321\226.\277\2514\327\031\324:\347\255\227\006 \323", 64 },
+ { "\344f\014\337\306\314\200:", 8, "\274\251\230\012(\271\313_", 6000, "\317,\335\301(<M\214r-\333\206$\325\241\214\304\274\343\037J\015\305\245\026\314\373\270\317\271\314\3022|\232\271\214\020\200\036|7\271`Ag\2151S\036\266\234|\333+\376\200\261!\315\26021\243", 64 },
+ { "\212>\363\217\322\014\301\177", 8, "\344\303W\177\317\032+u\032B\353\375\317,\225r", 6000, "\212\205\266\373(\344\246\213'\247\262\257\030vZ\355L)\242\375\353p\227\3250h\022Xg\356\310\350=\353\247=/k%\021\364\030\223\013\272-\177\277iX\005?\000\200\325\310\245\0137\247\365\034\234\007", 64 },
+ { "\235z\200\0425D\206\326", 8, "ZC\014\243\032t3g\336&\0058\016`U\317\210K$\205\236\264\274\224", 6000, "\233__\371\276\213\263L\005\211\250jO\042\364m,B\020S9\014\332\263~`R\334\236\020\224d\374\037\201\311\327\262:l\221\315&\277\351A\251\262\000\012\200R\203\360\011\371.\373\323b\000\316\277\275", 64 },
+ { "\013\216\203\231;\177\030\022", 8, ";\250\2605\215\314[\2478\314\343\342gM\352\211\020:\336\3271E\260\042\3444\274\305\234:\226\221", 6000, "k\255\012\344\344\350\306;fm\253<\277T\017\020?\234\215\216A\373\303\3375l\325\333\027P\301\006\214\221\202\375\342\240\213\012\307g3`\377@*F\3518\000?\010\002\021\005\023\227+E\177\363\022\236", 64 },
+ { "\211\354\257\301\023\201\377\374", 8, "t\235\202\224\274\013\350\134Cnm77\352w\200B>\250\213\211l\275\357\234/\004\342\311\216)\260a\016B\216o\253&\317", 6000, "\304\225\214\273\016\374\375V\215\207G\264?/\273*\276\257\343\012\234/\025&/\017\331\211\355\016\351+\301bS\004x\017\326\252\032&\205\030\222\347\272\002\036\231\261\033\200\276\330\367\007\252\376\005#\217~\212", 64 },
+ { "{\355d\364\020MGs", 8, "\324\301)\225SI\220\322\320\333\263[\375\2154\006X\303\014\275x\300a\313O:E]\011\236\134\203\344\220wj\231\320\300\015\222\321\324\177\341\320\237\372", 6000, "]\005'\026\210\036\250\037{}hs7\254d\327\243$Ch\000.\011E|\213_\347\3579d\342\321E\212\334\251~\231\221o9\006\323\317\013L9L\312\206\357\372\007Hv\234Q\311'+\001\276\034", 64 },
+ { "DR\0073?q\272\374", 8, "\250l\002\017#4\320=p\032\202\257s\207\352\347I\373\244z\377jD\227<I8\210\213\306\372,\204\324]']\271\346\347`$Rw\261\224\037\006\242\216\245 \027%f\351", 6000, "\005T\364\022\034\357\242\240\243\2461D\0034\313\322\232\340\363s\335\233\245\003Bb\230\037\033[$L\276\013DJ\024\234\306\217\246\013\353\270\262\3364\245\324)\342\352S\253`\027\312k%/e\373}\261", 64 },
+ { "s\355\341\224\375?7$", 8, "y$u\342\341\261\215\244P\211!6\034u0Z\251\335$\354r\306o\3605qd\237\344B.\274\234\231f\036\260\032!\267O6(\366<\250~\252\276\250\332\037;\205vo<\353\376y}3\211k", 6000, "\310d\310\246\367\305\310\013\003\330\037\365>\214IG\364\206\004\322\261-\217\305\301\214.\314\206\373p\341 TSu\323\377L\373Bu{\032\247\370\270Q\260\307PZ\337\203\304\235\004\254\354\322\363\260\001\004", 64 },
+ { "PB\323`z9_2", 8, "\373\317\015\021\275\311\335\315\323\231\212}\307j\336::\201\343Y\210\042P+M\263Gh\031d1\034jz&\134\366\363\012\374\274\212\244\315j|\207\331 \345\312\027x\026\261f\016\311\035Mt\370x\243\323\3550A\014R\033h", 6000, "\134L\334\310\334X\035\356S\015v\243\242|\002\037\305\022\375\372qK]\020\300\226\001\334\310P\357\376s\203\225 \037\305\373o\261\020\303\264\370\256\262\337\237\251\273>:E\335e\260\316\246U`c}\016", 64 },
+ { "\027\223!\256N\260<\200", 8, "\020\374\203\034\005b\230\201", 7000, "\302I|/\305\025\001\003\233\244@\332R<&1\333\030B<\010\3315\267\213\317\231U\225\303\336S\217\037\337\212C\202\022p\270\207o\205D\177!}j\221\215\027)\306~s\004h\323\342\336Z\252\377", 64 },
+ { "\201\016\210\273m\345&W", 8, "\253\363\011\215\2646\222:gr\001:h{\012Y", 7000, "\244*>\204\042@\302A*b\010\024j\270\220P!\2114\327\031\237\304\231Lb\303\336J\325\321K\263d0\200\327\005l\320\344\336\353\323\012y}N\251!h\275\363\376\320\225\341f\246RE\310\2418", 64 },
+ { "\276\257\256+\244\247\211\226", 8, "?\235\347\030\024\362ng\234\254\017@\033\321\331TO_\255\277!\042\233t", 7000, "\216I\365\206\200\134\002\353\213I$\327$U\275\240(\351\352\2738{\313H\202\275F/\322\013h(8\263\205\326\303\042\2621\032\204M\264hT\221\205\310\263\214\273\262\313\221\267\206m|)G\324\241\025", 64 },
+ { "\2020\274j\042\224\231\221", 8, "\334\260p\216\333[3\014\301-\223\026&\315G|\231\207\205{\310\336@H\246\3257M\020\031\362-", 7000, "x\213\321\215\223\332\313\016\323\012\310\330?o\300\277\177j\312\220\255H\247&\366\232BX\232\352\362N0\225=\373\010\260\027'\271v\376\220\324\263\225~\354\035\277r\222f\221\260R\253\215m\203|$\334", 64 },
+ { "\372+1\367\246\333\271w", 8, "`\220^\277$Y\341q?\315!\014J\221\210\242\232ySd'\354\361z\306\301\016\252\274\342\267sso_TZ\200fe", 7000, "d\224\227\252\306\204\012g\201\237\314i~\216\214QV(1C\271\320\233K\341\351\313\005\342\343>\361\341\377\030\204b7N\340\356\274Ma\023\212\314\222\240\211\033\013\211-\356\264\267\027\262\005\313\270[\204", 64 },
+ { "e/\227\361\021\350\237E", 8, "-NQ\342\360?\267#\011s\303\241\227\353\232\227\010f\003f\012;[\022\217iu\317\024g,caX\370.\214\321\273\035|\337EB\014\2636\235", 7000, "\247_\330\233!\237~,D\351\274\202\265Z\026G\224a}\277q1\250?\023!\276\011j\254e\233\255\374\246b\024\275ss\200\031\036\312\322\257i\357\367\371\022\324\213W\014\246<}_\003G\317\204\247", 64 },
+ { "\230\373\305\254hVc5", 8, "\362\306\303B\214\210\020QR\316\205\356\241\373>W\307q\237\261\227\355\251R\334\332j\303\231\202Y\356~\213C$l\353f\017\031US\334\324\016\034D:\374\345\252\235D\303\214", 7000, "\331[#\367\002\331}\306\266\3258\246n\226\357M\204u\226\320\306\323n\015$\320\247\305;\265~\2478\025V\364\222\366s\240f\270\314\034\250\277\347d\275\351\326\310]\213\244d\264v\303\317\371p\235\263", 64 },
+ { "j\332\311\021\016q~\273", 8, "G\026\002J\024d]\255~\231\271\322d\335T1\203\371(\350\331b\013\302n\244\004pi\231\210\205\332\327oE\205\345\011\300\216df>F@\323\020\240I)\026\335\362\345ndx\321b|a\304U", 7000, "\371?q\263\265\2344%\200;\333\251G\020E\316\333\331RD`\275\260\006\261\356\332b\027Tv\251\243\001S(p\361\313\011(g\327^\241\0030\376\365\3425\376\363\307\216\234\316\216\377b{\310\347\003", 64 },
+ { "oS\366\311V \247u", 8, "\217\350\316\355\003>f\334\306W\026\213\307? \304a\245\370\333\364[\236\0256\267\320g\360xn{\037\015\356H\271\010\255\360\265\3136\025\266\342\324\246\204\211\311k\244\322\342\231\307\204o\2376\361cy3w\314\347/\246@\022", 7000, "\211\314\257hDW;\257\3702\271\243\322\314\3131\352\250e\313\006\364/\001\207\327J\350\214\350\232U\353\216\024\344\177(\325\366K\031Rt\220\201L\265\263{\366\305\305\215]\373x\177\373\020K\224\346\350", 64 },
+ { "\2713\267\214\012\302\311g", 8, "bo\2109\255\305P\036", 8000, "\236bG>\215\363\021\012\242\265P8\373\332\017,\367o\217}\210\320\035\320\255\316}1\005\021m'\205\214B\236y\232\377\300\262&\240pJRXQ9\226\011\212\316\346E3\302\252!l\266\304\134\017", 64 },
+ { "l\364\370\012\341\025\344z", 8, "\201\004\012\261\355\255N]*\324i\346\002z\300O", 8000, "\024\220\237\352h\017\263\237Vk7\015\250\007\000\253\203\305\255\220\337|\306\233\240\023\361\226d\304\315g!GGY\363g\331\005\376\354\243\371\207Q\212\246Ez\030|\341\315;h?Pe\351\256\004\364\373", 64 },
+ { "\302\241:W\337\336\036\342", 8, "\273\233\362\231\357\212^\310 $n\370\314\324a\225\235\004L|\322\015\357\256", 8000, "\357\333\324\221\253\207\002\244\006\352S#\326\352Y\201\034\307\301e\22120\252\036\333\2151\010\134*~\235T\322\007\200\020x\221phGw4p\221d\251\262\355\013=\236I_Vm\241\003CN\245]", 64 },
+ { "\250\275\025\013\365\363\227\210", 8, "\030\02582\351Z8\344\017\0252\203\305sTM6\322\336\214\037\303\223!H\306\276R3\235\323D", 8000, "\337/\303\204\376H\020/\366\036\3336O\234\237b\012Q,=9z*4VZ<\320\214\270\341\277\262gjEu,I\224\242\316c\217h\042\211\005\300+\033&\336\024\254&\227&\301\324\004\362\377r", 64 },
+ { "\366\345\301\371\301\220\3759", 8, "\315\012\003\227M\042[2[ \036\264dB\022a\326\372\203%\223\005\330&\007\304\012\300n\347\233\012\263o\330)\177DC\323", 8000, "\023\006\222\026\010\342gH\220\370\372\277Z\327/?\240P\244\244-\326\333e\234g8p\323\263OG2K_\272S=\276\012\011\314\217G\027{\215\246\360 \024\224\244\352\363\003\307\371o\305\212\341\3706", 64 },
+ { "\272\010\025[\365PR<", 8, "\270\205:\014\2338\264k\246~\025\245z3\345\001?\005Nk\006\202\357\220:|\251\2522\276Z\243\232<~8\341i\326IQq\265\355\013$c\241", 8000, "\031\240\307X\344 m0\221(p%\247>\215B\244\325\211#\036\355\215\356\231\247\263=B\232G\264E\260\222\214`\006v\315L\011\355\025F\235\352\347\365Pm\230\211\273\255N\0023\021\202\362\370\004\340", 64 },
+ { "\031\322*2\357\375S\022", 8, "\366\252\324S\223=\016\205>\011\374\2703\234\226\316\004\327\231\1774K\236\030T\232_\315AS'\374H\326\226E\037\225\263.s\004g\332\360\372\0147\200\364Z\003\262\225GJ", 8000, "Y$ \014\261L\304\307\264F\212M\362\364\272\375f5\223\016\275&3z|\227\021]mvP-\024\225!+\361\371\250\335\375\377\310\035\017=\233\347\225\235\042\335\241\300\264\260E*\327\034\220E\204\325", 64 },
+ { "\031\220H$e\221aZ", 8, "\366\331\016o\241W!J\177\212U\230\316F\042\345xO@\324\314cQfu\315\261\312j\252\005{\001\035\362/`i|\361\237\367GmJ\303\356\035\366sV<\031\346\2736\266t\302\024x\001\360t", 8000, "IB\042P\213'\260\225\275a\244\255\225\361\346a%\321tT\251t?gbp\344@x\016\002A\025\242\264\177\224\256'~;\256\216\277\301Nc\302a\216\325\220~\213\240\245\252\260E2\371\216w\134", 64 },
+ { "\004\341juF>\010\303", 8, "\012\374\210H\310\034\235\215\205\261\207r\3443\224\300<G:\272 \230>\027\275\304\271\366\021\300\034\024b\256\337\0164\007w\335\243$T\300m\010O\202]%\306h\205\311\260B\206c\236\011\233m\305\305V\042\020\274\001[zM", 8000, "e\327,[\030\205\3270?\251o\320\177\305\134\272`\256\332~\363\017\200(\035s=\343\300\244A\231\243\267\247\032\353\325\275\256k\221\364\230\301\301\2658\260D\035\244\207>\211\354\035\220\202$:\012\345\177", 64 },
+ { "\244\276\254\240~1|k", 8, "\251>\326\357\223\360I\224", 9000, "\202\327C\273\265\315pa&\261\02517B\232V\007\200q\302i\240\265\250\302FH\230\002]\357\216\204\245\351\221\223\335\250[\0274\265\307\214\362\347_\213\003]\244\360\344\014\011\003t\226\031\016\354\034L", 64 },
+ { "v\015\177M_\312\0052", 8, "\366;\234/9\024b`\042Np7\3674\335\254", 9000, "\363{J[\233\031\001=k\307d\011w?\300 `\320\005\253\303\373\134\004Q\212\334\344} \355v\221\370\315a\314\205\236\343\363H=\357/\026\2531\024\316ZC\200D\345\212\3025\011\333!`:q", 64 },
+ { "\272\021\314kDz,\250", 8, "\1774.\331\031\134\003wT\204\366\216@\2057N\223\237m<\341\307Z\307", 9000, "\323`\024\332+\270\220\374#eZ\314\217\274\015\275\234>X\342\363\240l\202\321\3030\245n44\267\007\377\042\334\225\323>t\310\316\301\333\213\215\205N\330\274+\214\323(\342\012\336\255\233\233T\220\345\352", 64 },
+ { "\346G\354\015KEg9", 8, "\320i\372\224\255\204t\310p\216(.\260j\241\215\217\245\350o\362=\024d\323\350\364\212\010\300\351\265", 9000, "\237\006\200\214I\354\024/O\134\026\322\201i\203\031vjkO\024\234\311\351M\020\202\350\016vO\331}^\333\223\247\006\366\251\260\3661()\027\322\361\350v\310F\005n\337\277'\361\327\220\330\031\374\037", 64 },
+ { "\350V\321\365\262\311q\250", 8, "\343m\037\250\273\367\033\260\306+\343\236\021\337\216;&\366\361\237\301\244\224\035\017m\033\241T\206\011\006\030\220\240\222(\226=%", 9000, "\215,\364\267-?M\243\134l\345\312s\270\224lu\363@\337\360Gz\357 =0\374\320\027E~\206t\347\031\222\371\211H\333\242\210p\371H\023E\262u\004\042@\250\341.\316IYA%\364\010o", 64 },
+ { "\134\012\274\273\331\3564\247", 8, "o\2669\261\335\134\307M'\373{\361\367\325\253\331\243qy\316^\031\315\223>\024\007\177\375L\216\001\272Y\235\246\340\341$rG\352<\203s\255%#", 9000, "-\017\003\263@\273\011\274\3138Fx\036\312\320/3y\26458\035V\222\376\004\312\330]\263\317*\265q\324\340a%\016\214\241\255v\033U4j\006\002n\346\014\326\011\211\201\237I4\330\034\0002\335", 64 },
+ { "\276Z\205\372\215/E^", 8, "\260X\264\010\316\224\021_\014\005\246\0046\005I\377j\356N\365\013\253}\007\250BlGu\224\027\37541\352\316\3704\305\024\343%\037\342\265\236YA\200\005j\352\203\254\315\237", 9000, "\266t\3301\355\364\233_\353*\333\220Z\007\360\343t\343&\260\036\334\201\254\335R\373&b\237\027\221\343]U\304\212+\0367\307\375w\250\227\233\042U\227\215\324\017`_\254\3125.E\002D\321\200;", 64 },
+ { "\226\005\201\252\315+\266a", 8, "\377\315Y\201\220\361\037\222\262r \237\226*r\005\236\225\036\323\313(n\257b\011\330\357H:# \321\271\344\202,\231Q\016\013\034\222\003\364\205|m\335\232\0172V\355\275*vH\262\177\205\307\210D", 9000, "\234GS\240:\220\213\346l\004u\204\234\243\351I\370\260\375\010\201\035\223_E\354\271v\036\250\266h\227[\243=\337\315?\220\3253\300\366\346\024[\307w<v;\233\235P$m\005B\022sp\326'", 64 },
+ { "8\260\371\214PU|Y", 8, "q\301\012\024AGM\017\210k=\134\332hh\227\354[\332\273\220\213`\303~\344\250\232\231\312#\227\236\335\244\276\202\223E\350J&Y\253\342\262\254\334\215$\306\003\3649\202IF\251\224\205\230\010/>\325\313y&l\027\010-", 9000, "\307\367\342\016j\337\217Jr\210\226)\242A\315v6U\313S\042\025`\335\346\002\240'U\312\342\210]=|DZU\253\237\320\235\264\311p\200\221EP\311z*\207\267\305J\273P\033Utu\254\300", 64 },
+ { "\347H\205\004\340w\177\321\313\337\042G\204\274\033\334", 16, "h}o\344Iv:b", 100, "\037 \023\347\332\024\007\042\036e\253\276\016\270RM\224\223w\012i\223\305\2474]_=\257\303.\331\025EqB}\214\204s\253sD\037\317b\034\000\376e\012\340V\220\224\00532+\024f\206\032a", 64 },
+ { "S>\007\330\027\271yp\211\357\017\246\200q\302\376", 16, "\266\042\300\276\377Z\362X\241\271\005\257\355l\232\235", 100, "\320\302\134\002$F\034\315\343\211\010\256\264\222\021\363\177\003T\030\225\027\333\376\213\256\357\202\324\272\350\201\3161\233\277\327\04293 \332>?\327A\377\361\265b\274\202\246\3123\236\366\001g\211u\223\354\354", 64 },
+ { "B\031~Q\003^\310\367\210K\014\256\245\202\316j", 16, "\015-L\002\032\352\005\215\322f\024\344\3559\360\273\222\336\347\221\356_c\320", 100, "\177\207_s#\007\301m\206\013\2300!\365\000\2125o\261\376\343\365x\220\313\356\264p\304\013\033\306\354\246}\304\004Z\311l]\002b\362yd\013\234c\007\340\262Bh\001\013\314\364\021l=T\344~", 64 },
+ { "\245\360\247\005\370r{\215\303#0\353\344>\002d", 16, "_k\016jF9i\254\253\026,\251e\003\315\3136\016\023\213\337/\331q\343\356n_\355\242\200\231", 100, "\303\207[\317\226\277\341}\225.\324<\006jL\035\372\247\245\332j\334V\015\266\012\366\273\213;\213\275\021\020\341\271\325W\277\303oy\260\215\021U1<sqH\325\365w\267\330\012Tq\007Z\212u\355", 64 },
+ { "dC\335\234s\307\200N\275\374+<\344\034{\377", 16, "\265\260\003\355\317\205\027(\201\346`~\235p\222\253d\337\356\202B\315\257x\225j\242\365(/\2146\203c\3558X\035o\004", 100, "\373er\245\374_{\365\002zgBZ\361]<\020%\255\215Teg\011\371&K\304\355\307C\203\377}\375\022\325\237\274O\200F\177\015\322\347\264\0151\034\360M\206\264\235pc\177\345\005r\012\2779", 64 },
+ { "\225\204\264\2129\205\026$q\227\006\221\250\330-<", 16, "\006?E\232\340\334Y\3517\213{\017\315zQ\274v\3122;\237\031C\012\210! \364m\361\331\015\267c\014\207\236A\271\002\317\260\036\231&x\202\253", 100, "\240\034\354\377\037_;\322\013\3704j\021 8\217qT1[\014\031\227\213\350\377vg\370:\032LR\260\336\023.\212\023\2658q\244\232o\256\014\374\002C\341\342\017x\364\0322y D\315\042\332\230", 64 },
+ { "\204\307\332m\134va\332\276\322\307Q~\2166\274", 16, "~f\344^m\013\033\337\034\014\225\035I\244\260|\002EsJ3\005G\333\2734\377\362\327\375\010\271\201\227(\376\325\352\346?\024\354F\236<?\300\035.\357\354\226\035?\344\242", 100, "Y\376\307y\275\304\3730\355J\022\305t\242\272;\007\313\370m\251`}\341\261\266\205\321\203\262^\256b\346\362\010\371;\241al3\177D\016\014X\026K\363\235H\373\216f\022\134\325)\026@8\272\374", 64 },
+ { "A\003*\333\311\305\365L\134\232\373\332O\371\312\275", 16, "\372\011\216Z\3042P/\277#\253\315\177\261\300\026\2136\222\310ekk\263\032k\033qQ\231\222\030\134\341\377\241<\277\024\032!\014V\357e\276ck\020\307\327\373K\217J]R\017-LB\022\253\202", 100, "D\244_!\201\266\242\014.\305L\262\363B\210\251q\246\236~\205\361\177\332JaZ}\006L2Wp\333\234\345\301\351\236\234Y\273\034\300t\203\002\2024\264\355\246\036\206\014\035\251\342q-\237%\204\241", 64 },
+ { "%\343yf\223~F\304<\227j\221\342\233\327f", 16, "\2639\134\317\005n\321q\237\270@\327\264\255\201\016u\304V\134\250TtuI\245N\266\334:t{V\327\022\302@\260!\362\016\2506\367\007\223Gx\023h\341\266c\336B\376\260\001\242\007\235\273s\370\266\261\242\326~\354\245r", 100, "M%S\014\270\306\265\253@\253\036(j\216\223\233\316\325\341\324\020c3\324A7\352D\215\313\035\333p\030\310\370\034\276\023\376\026\345\357U\3515\355\375L\005\002\304\333#q;\262\244\367\207\235\2635\363", 64 },
+ { "\3161r!\203\342m2G\011~\365A\246\331\270", 16, "1\257\201\240\223\217W{", 200, "S`\203\030\240\261\241\304w\301\302\276%\035.U\225\224Vjts\352\005a\230yiJ\355\217D>\333\205D-`-[<\335E$V\253\014\213mg\267\355k\262\211(7(\016\003\036\231$\257", 64 },
+ { "\3746\000\2150\247\037#\313\331\351l\012\333\247A", 16, "\234y\004\241[\363vH\207.\227X3\234\366G", 200, "\314z=\265I\243\201;\010\234\015.\042\377\265`\220\223M \006\234\276\035\036\2571\322\237+~C\241qv\223\006\223I\034\225qT'Q\232q7\3102\336\371\271\237~F(~\350\317W\236D\000", 64 },
+ { "\305\363\361\241\301\257\317\177\3327\134\252\316f \307", 16, "\324)\212\012\226\034\330\315\313\323n\361&s(,\205\322\034\211a\236\335\375", 200, "&\3403J!17\345\026 \211\235C\025R\212\177\374\273\343\027\3772\026}\232\347\016\362U\352\025|-\373V\300Q\221\003\317,\037x\334Dq\230j\277\206(iW\037.\336vJ\230W=\013\376", 64 },
+ { "\342\015\026\337\254d\021\237\010\216O\314\271\015\202>", 16, "\272\010\351vz\243\205y\234\345\354\262[\353^z\346G\324\177@\217\357\252z\254\354\240\241Yb0", 200, "_\235S\033v\365x\3567c\255\216L\267\200\307\204\006\365\271\276>H#\260\015\335\025d\000\230H4\255\365\264\370& \204[\345w\035ne\301\254\312\246$\213\345\362\261\256s;,\321\304\371o\333", 64 },
+ { "P\220Zg>x\313,\234\260\240\262\260\351\177{", 16, "\3762s\2309\276B\215}-\215&\363Z\374\305\233\325\203\254\367\212\250\266H\222\002\256\362~m\374mr\354\305\317\330\215\214", 200, "B\2700\007hU\301p|\227\275\342e\005%\374\023D\242x\311l\0344\211\256k(\264%\011\362\266:a\305\330\252\320\303\365uh6\240\0033\271\236c\220M:}\365|\210\010\305g\000\364\027\347", 64 },
+ { "\032\222\254\003\000n\311\010x\225ny~\226p\016", 16, "\303\3346\310>\2656\271\251L\272\243\007\315\221OZ\300\212q\335\367\246t\234`\213\214\347\332U\340\352\275\265\134\354\203\333( ?\256\304\035D\012?", 200, "t\301\031U\330\0141Jw\010\027\212\334q,\306\307\313\354\177L^h\376&\033\310-\231\220\273=\277?n\201>~\0263\216\012M\341\224\037\034\244\036:\325\017\2024\230\342\006i=$\3546*\243", 64 },
+ { "z\012!\011\346\202\316+8\217e\255\023>\350\221", 16, "\177\015\264RK\134=\005j\210\255\202\373z\327\304\377\027\267g\254\3139\333\354\215\273\177\0112\237\327\343D\205>\213\231j#\235\355\363,\237\315t\021\335Q\300t.\346v\210", 200, "\233c\377\253\202\010\203\256\372\357<\200\231\334\3074\207\371\232\243\206\023\212zj\272\273\216\217\210f\200`\031\202Z\337/\2467?\177\226\321\215\300\216\260\222t\320i\361\012oT\017\302\340\353=\302\240z", 64 },
+ { "8\346\2320\027\300\302e_\224V5\347!\037<", 16, "\247b\235u\341\201tX7\372$\260M\312R\344\275\241i\233\255\253\030\206\256u!\334\201\275\202\042\036_\237\335=\031,\015\230\372\303\224\312\334\220V\035\323\226\340;\177\250v\375{k\344\331\255\214$", 200, "\216\323\264\300\362\373?\027\346\000\323\275\267q\001\222 y\303D\366{@f\034\226O\234\341\216\230/\226\324\034{\001\264\042\326\357\015\360\037<Q\210%\374\261\022c!LDvL\335G1+\005A\332", 64 },
+ { "\207\216&\025\344\223`\034;\343\303r/j\007\313", 16, "\204\266\035\374\206\335<=^\3426:.\200\320\024\031\320\303\351-Z\265\253\212?`\036u\215\220\334\031\327i\267\261o1\134\274uo\002f\212\365;gb\344\302i\2514Ys\246~\0047\022\201_,P\272\221?\276f\323", 200, "U\012\365]\223\021\2637)\033\005\011\374\134\3653\207s\276\134i\351\3359\241e\033\344\370\3560\225x6>\362\2602\306\277.s\244\001t\346V\333\016\3111\015r0\316\005b\330s\204\317uxc", 64 },
+ { "`\204\233i\237g%EA\265\241\220\240\311\271\277", 16, "\007{\3171vCb\215", 300, "\231\365\3613x\302\240\021\024\331\3726O\333\340E\304 OVy^\0121\360\353\237\210PM\261P\011\325\311\307{ -\307\016\204R\300+\003\324M\312h\003\304+\251\214\005\205&\002\252\022\240\3702", 64 },
+ { "t\271\276\220\376\026\002Q\320(\350\3108\032\234\227", 16, "\275'o.\375\363f\266\245\373IN`\331\016]", 300, "\234\203\367\265\303\221\335\243F\363!\225\007..\177v\266\321oe\227\013_/3\032\330\225d3\233*\252\042A\323gR\303}A\374\300A\353s*~\205LdO\305\002[\201\204\033>\244\207\330\030", 64 },
+ { "\200|\315\327\367T6\016E/+~\016\267\042\366", 16, "hR\302J\352\242*>\306R\202R\354\214^\317\216\270\277\014\316\322P*", 300, "\215.\042\007PDE\315\3073\355\007^\2619\323ET\250\004\334|\364lQ8\020/f|\206\344Q\010p')\013\220\277\231\362\272\00462\344r\211%i\273U\012y\346\322<\005s\204\017\200\274", 64 },
+ { "\302\354a`\2242\277\372\213\134P\021lU\200u", 16, "\320\311\272#\313\335\306\264\251J\255lR}\002\273G\3006\371\205h\235Ah-X\001\312\3341\027", 300, "\022\220u_\275S\011\301u+\222\351\307~q\226\007\233d\231\276\026e\251x\3123(\243B\227\021\215\277\031\315\226\315Y<\360\213$h\203\017\266\335\024\247cSC\350<\340\303\343\237\241*\200}\265", 64 },
+ { "/\342EJ\341\2354E\202N)\376\311\231S\231", 16, "\266\012\010\366\027c\3434\364\272\373\260 \263\207\262\344<U\207\134\236\275\014\331\313\3632X\310\306\217%C\202\240\224T\025\272", 300, "\010o\235\266\302 P\020_p\277\341-Go\371\362\324\202H@\231\2337CqY\367\356\355\311\026\343e\022]\372n\221\234\277\2640{+kO?;2\335M\275\315\377\217q\234\337\335cr\263\314", 64 },
+ { "\224\327G\362\204r\233\256\261Gnu\317O\267\307", 16, "\365\332\350D\026)y\270\241n\252\364\375\371\006ke\306\342\255|3\220/\017\231\256\330\355\211sj\247\274\016\365\243\213\037\206\010\221\273@Z\224Fo", 300, "\347\2317\346\344\255\016\300\025\002g\226U1\252\233,\335K\134\347\336\336hw\202\250\270\204\204\013|a\020O\226\341F\307-t\362\342P\322\370:\376\272jRm\005\255\200\236\315\320\346\334#\212\321\011", 64 },
+ { "\014cTy\324$\201z\331\244\337\372\020\206\004\004", 16, "\265\367\261\204v5\371W\332r/\007\310-\010\204\264\022\317~O\361\316a*\2773$$2\021\321\240\372\316B\267c\323J\354\042\022l\242P\203U\243\371\216\2674\221\266\212", 300, "_\346\213N~n\353\034E!\216\2141j\031\0171\344\303\222p\301t\356\254G\373O\240\260P\215\034\345\211m\350\342\332\210\034\313d=\317p\215\247\302\027\212\324}|3\245C\242v\000\242\333\326\027", 64 },
+ { "\030T\270\036\224\037\265\023f\255\242\2064)\376\366", 16, "\035\370\211.;\345\356(\223zz\376\376\371\323\267\2034\326]\271\245\252\244\345\304\352\347{\250\030\267?\275\011\255\224\223\353\010\236Sy\2256\004h\3632\036\255\003\353\247\337:$\375\303\273~\351\260\260", 300, "aNdOT\0026\300D\377W\256\322\200wZa\276\235W\262\313Z\342\220\307\321\377\223\276\2735\311\253\353\307j\343\257-hv\200`\345_\371I\232\372\250\006n\335R\331/;\210\002\027\367\355U", 64 },
+ { "\255\021\324~\275N\274g\255\042\242/\312:\177\367", 16, "\200w\215\352\253\031\350\310$4u\376\363Z-\315\231\375\331\320Gp;s\231)\251\024x\261\354\021\353\177\024i\314\370Q*\206\345\275r\353\255\246\305\275\257\336\316\035\335\024\356\323Ya\022\301\247\202H\350f\235<\177\026\200\352", 300, "\010\360'C\342\355\042\307\216\356\335~c\037t\001\273q\357\253MK\217/\353\337\317\031\333\333X\025T\276c\252\226V9@\277\344S\360\347\261&P\036x%d\260\375'\364h\250\037\320\267\211\336b", 64 },
+ { "\032\230)\010U\321\363\256<\2418\352\364?sT", 16, "K\245\210\270<\375$\305", 400, "R\276R\016\305\241\307(PiZ\036d\234?2\255m;O\027\242\277K\006\021\315\221y\023)\003\276\341T\254\236\323!\370(\242|\261Ah\216$\303bE\365\222\020\357dH\247-_\313\013?\207", 64 },
+ { "\361\206Y\022\331\265\007\326\022E\366{\321\334\202`", 16, "^\357\274\264\322\277bu\351\034\213\220-\370\275%", 400, "\352.\227\274pWs\177O\260 \351\000G\3132n\223\231\213+\042n\245^\216D\313P\267\323\031V\236\035\225\005\365\304\273\254\227?\202\221L@\377\204\236\372\322\310;\317\267\244\356\302\337\311\326($", 64 },
+ { "\333$\332\236\256\323\210\250\276f\2306\256!\373\245", 16, "p\025g\221|\037`E\215\363\204,6o\263Rt\346\304\351\032\240\205\377", 400, "\316)\361\005\357\302\270\277=\224J\301\271G\260\373L.c\011o5\331%\222\202\324r\012\216\327\222C\214\340\354x\273A*\364\316F\351!\315\343\267\351K\005\252\223,e\346t\216\237\236ZHt\363", 64 },
+ { "\022\021\361\351Er\243I\231\314\334\325\303\352\335L", 16, "\303Vj\271t\253T\272\372\317\0070\351\013\230\031\306\023\240\255<S2\310y5\306\322\2468\022.", 400, "~\255\277\277$\336\252\037\237\347\253\277\200]\014\377\242\001J\232\304\351j\037X\307xJ\307\024\035\265\177\364\223\346\345c\3726M\344\257\255\305\210\370\361T\316\254\271\325A.\031\375[\270\027\326\313LQ", 64 },
+ { "\026\360z\243\245\346\356n\337\340G\366@\271\332\356", 16, "-\017t\0314st\365.\234\352\357\251\203X\252\134Bk}\331\231G\212n,\363\263\271\247\321YY\274\3376\373`\325\014", 400, "\316\024o\372\015\253\331\202\3058\300\201N\375!\325\034\036\377\272z\017\2072v\24190\031\344GA\241\216\213\262p\361$xl\011dJ\306Y\304\224\276V\376\273\0375%=\340\213\373\261=\200qN", 64 },
+ { "\275;#\207\242t\304G\340\257\302/\377\304\210.", 16, "\370\3540\232\367\017\205\322+\275C\024\372cY\237\023\245\257h\200;\310u+;\232u.8\300\373\225+\033\254\346{\360T\204\012\215\231\357\010\372\252", 400, "l#n\313q\010\223\025c\351\347\335\340\207\042\005<\042(\321\376\365\224\365\360D\210\010\235\134y\267\034?-\271\304\007E\364\350\351AN\214\220\231\235\374T\241:\203\0372?{\202\3554\251[\377W", 64 },
+ { "\376g\244>\237\350\271Y\245+\011 \341\344\277", 16, "\326&\352]\243\333[\211\035t!\366\204\204.\351\005\036:\260F<\210?\343m\226\035_\134\255@\016\260\017\347\033\256\377\361Cd,\343@\007\264e\177L\275f\216\346rP", 400, "\300\042\035WU\336\316\023i\201\215\215KSU\215aH=\010\332C\207\276\207\254}\373\331\325\312\013)%<)WM\330r+ \014\214\217\012c\266\023\364\330\356t\264\042(\031\312\271\312\351\3356J", 64 },
+ { "\260\354\027\023%\230\372\345'\315\035rq\356<\327", 16, "\300#\263\231\336\337J9\346\354\007\273\351\244z\013\247C\342\337\042cF\221\022\221\203z\200\244\323H\015\366p\364*1\2271\134\326\377>\002{\260:\242\224\323\035\211\002\277\347\246\227\237\223\315\315\361=", 400, "/\015\317\021n>l.\252\256A\340'/\327vM\233\262C*\241\361\036\353\203sjK\003\007\331\361][\204\3643\237$\234\353\205_\206\226\337\275\375\025\255\314\263\250\311\244.\362P\336w8_\344", 64 },
+ { "\366\317\345\225\251\365g\205\211\324*\313\001E\314R", 16, "{-\027(\230\231a\371\302\271rc\302\204\32402\204\312\005f\227\336R@f+%\250\372%\023\340\002!\203\037\237!\1346\012\355\345\246\020\346\263\204\204\222\356\221\2334=\3333\364)bs\242\314w\001g\214\212\020Gq", 400, "\212K\270\302:\017\317\343\266*H-\373\241\321\200\021\204y\276\272\252\347#\333V\025sI&\325@\337\015t\312JR\312\222\243le-\204\354WAi\364d\353\364+\023\376\363\210}\261\236\242\262\222", 64 },
+ { "'\225\026<J\233\370j\177\376\303\004t\245e\022", 16, "\323$\345\255\0226\340\213", 500, "\207\004\3713\226'\232\001B\245\206d\217\341\347 Y!\202\231\271a'T\235\207\332j\275\276\325\345\352_~\362\233)d~\343\222e\257gc\024\254\007AS\227\304\265\244E\211,A\256\355\341\314\276", 64 },
+ { "\347\0059\307Jj\001\214W\314\346\300\014A*\227", 16, "\030D\250+\305\240t\201\316=\314\036C\373\356>", 500, "\341\213,\254\202dg\312T3&z\363\354#\350\301\204b\302\357\253~\315a\342\235Z\206J\002\341\042\004\342h+\274Z\305\350.8^\377C\241c'\231+UI\216`a\002\203>>H\276q\330", 64 },
+ { "\355\2508\036\235/\211\331\356\0223S\2121`\224", 16, "Zk\3217\020B\320\302u\222N\357<B\030\230+\232\373\236\336\134\231\264", 500, "\001\247\340{\214\204N\310\016\217\020\307\007\246\214\327\017\226\200.\277Z\001\314\020\011s \033s\340Qu\304\346\320\032\254F\221\326\311\255+\274\320H\013\013U!\315\2352cI\233\205\260\333 \211\334\332", 64 },
+ { "\031\234.\215%6\373A\206\272\274\042!\317\217f", 16, "\203\177'z[V\001\351s6(\367\212\325\3164s\361\322\310\372\371Q\220\322\255\257\262$6;\335", 500, "`\377\010'\332J_ra6\325[~\314c\333j\243\377FA\225\376\327\037\274\327\365\002\017\006\026sa\352AI\246o\227i\341}Cq\226A<0_E\271L\346\334J\272dz\240<\235oG", 64 },
+ { "\303-i\022C|@\340\327\022\203(H\212\244\226", 16, "\231\266$)L ]d\015\036\027#\214J*\247rY\216\341\016\031\205\177\232DC\267\236C\001z\2371\035\323ssq\243", 500, "\273\316\217\230&jG,\325\217\215\346\002\367\221s\021#\321}\316\034R6U\345\255\2170\340\2165,\361O\177K\230n\0052s\036\260r!\223\303\027\022\222\225@\213\015\275\351\311}\252\343\237c\321", 64 },
+ { "\275\363\307\201\216\362\240]\211\024\371\235\235\213\011s", 16, "f\233\330\275\352\231X\307\247\341\276\021\325M\352YD\255\273\001\230\255\026u\212C\200,j3\267h+\316a\377\030\215\211\002\002\213\247j\011}q\336", 500, "~6\341PS\357\366\303\206C\341\272f\272\250k\364\340z\366\317!\134\002o\320\245#\301vrE\331rMX\026\334,\350\323}k\267z\177\217\367\210\011\260KX\224I:\370-\247i\263\337\236\220", 64 },
+ { "\237U\344h\375\271R\302\302\204\203\331\253\234\252/", 16, "\207\254\042\210|\022\345l\012\364FJ\255\331a\0066\206\257>UBC\364\221M<\344\210Rq\254\327[v\266\347\251Y\022\367E\376m\303<AR$,\253\357\3070-\010", 500, "b\0106\220\000\252\277^\365*\214\346m\265\326?H%\311r4?\234\030\033M!\031\213c(\246)c\367R\020\010\241\002\331Sl\274\313\241\3309\366\007\322\325\300\344\013#\020\213\360E_\016{O", 64 },
+ { "C%\004\016%\275\272D&JRYE\310\023w", 16, "\314\337\034\217\005\224]\007\340=1B\302\010 \254\032\325\320E\254\014m\017s:\276\247B\024elC\227n\134\177%\345\317u\023[\337\266-\367\304\242\020\275\243h\260\016h\0057\335\325p\307t8", 500, "g7\263\346\134\244\212\267\377\264\312\244#\244\367\213\263-\315k\231\210\243hw\247mF\321V\334R\322\311\253\376c\214\320\360\351\253\373{\0300\260\021j\254\307\267\220\232\006\367v\026\020\042\246\314\252\230", 64 },
+ { "\042\244+\354\317u\022\256xC{z\200\365t\203", 16, "4\260be\177!A\327X\275\207\035\252{Yy\267X\274\216\245\322*\317\335\236\346\216a\311\201\215\310\042\007g+\273\033gm%\365\012\245\255w\206t\353\216\303\032\342\300\266\216\223\017\354dzH\347\356\321m|xh7\264", 500, "\272\266f\004\021c\344\271&\275\332\340owE\221\0426\230\213\236z\337F\246\215s\327\036\042]\314\276e\021\372\267KY\223}'\203^U%\216\340\377<\376\341\272jhn\340\217\314V\343\201Y\367", 64 },
+ { "G\372r\346\225\177\025\000\241\347\316a\303l\340\260", 16, "\273\360'=o\001\332\206", 600, "kX\276\343~\243\351\324\017\035\024x\004>\332\134\270\020\332\333\026\224\204\367\254\362U\011 \324\276m\330 \032\222^W\2304\345F\005fw\364\277\322VXJ\345\0167KM\213\022fv'~\032k", 64 },
+ { "\134\244y5\2355\245\326E\016:R\206\032\334\202", 16, "\233\364\201H8\215y\376\014\277\016\334\013\337Jm", 600, "8\337\036\337\322h\016\272\377\261\302jZ\271\312!F\372\364\327>XPTg\232\226F\021\362\024%~\371@\314\226;\026\244\2356\312\134\134\236K\217\243\341\0052J\304\014\354n\205\321\365\256\214t\337", 64 },
+ { "Q\361\217DR\030\023l0f\316\321\317\370\000\270", 16, "\210\323\252\322\0060{\223\006\022On0fm\202\011\315\357a\031\373*\317", 600, "\300\241\266\226\264\202\202\216m\0166|\321<:^\367\256B\327\211\036d\030\002i\207\311\322RSC\252\303G*xD\203\213\003\343\252=D\261b6\033?\351\257Y\251\261)\254j6I\203e<\333", 64 },
+ { "W\240\211i \373\342$O\315\042y\243\371o\203", 16, "m\225\254MJ\265\037\231)\021\336\371\254\253\256\317.\204\210N\300\226\320w\354\021G>\014\256\310\034", 600, "\373\356\000o\004\330\262\234\262\250\346f\212ap\337e\220,\007f\333\202\270\253_ms\351\013\373\234\251j\204\360\033\257e\344\360\200A\324S\320\251\235)\015\013m\202\206$eE\370\243\001\014\221\300\275", 64 },
+ { "\305\337\271 *\214\274\272\356\013\307u\215/<'", 16, "\323>\273\2563S\220)\265\367\346\246\002l),\025\177\027f\332\223\014\004\260}A%R0 #Z\357h\340e\261\025>", 600, "\333\224\212N\256t\331\335\002\315\266\324W\214\017\016\273\250\303\021\326\340\016fC\015&\305\213\266Pi\261\344D\325A\367z\251\326n\321\305\2120L\324v\006\262r\320\211[\023\253\301\330\376\017\245w\000", 64 },
+ { "\227\317\337v\243\316\223\362\003=\022\307\243\250o\216", 16, "\247\270\205\312\252b\325\344\227\372q6\3660\004\035i&r\212 \253\177\211\365\034\207`\213\310&\005\307zo\332\363\344R`\217\267\270l<\275\321\317", 600, "\312\021O\242AA,?\251\335\207\244\357\274\2753\020\006\321\240\362\262\304\253\210\370\231>\323v}\335C\000r\362<d\2412\254\241\200\255MC\372jgIc\271\347\362'\274(7\004\254\330\327\376\214", 64 },
+ { "\356\036|j\017iK\004\321 \025u\242\327\244\316", 16, "\230M\377Z\240zp\210\2750\342K\023\200\005\337\264&\224~\002\340\202\222\267\206\235\267\262j\254\3448\272<I\200\307J\013N\216\277\262\203\305\200c\312\035s\246/]S\210", 600, "\276\207\371\037\244\344R\327\312\330\344\316G/\345\341\042\304\026\246F\376\022\375\236\005d\211K!\320:\037S\346;\231\220\353\275\235\256y\300\326\356\312\343\342wp5\346\260\374\233\003P\364\322\025\323\042$", 64 },
+ { "3]\266l\353\305\337\311\016\344o&\357\017\317\017", 16, "zt\201\022)\365\233\245\216\024\031\213\254\265\015Ws,J\333Q\337 \027\347+Y8\375\347\326\242\306\204$}\363\351Ru\256\234\220\212\373\3738bjg\365_\022\211\201\364_\307\267qi.\021[", 600, "\333\371\263/\027\334\204\362M\300L\374\032~)\270D\256Oc\321\370<\225\346g\327\264\031\340\347\253\225\330\226\015\016\017\250\200\200\364<miJ\036\316lR\254\211\221\303\366\016\312\206\326\357fg?\017", 64 },
+ { "0\206\034\374\010\323=jw'\222\226\216\022\313M", 16, "-\231\206v9\237\214\223\304\006\237I\322\361\314\354%\234\204\217\223.D`\341|SU\226\007\327\342\2041\3539\310<2J\332\212V6m\301\313\322:12+7m\225 U\2731\245\333\305\262n\225\320\237\242\223\035Ax", 600, "\334A\033H\030\334\327k)\024\377\340\202~\016\336\223u\257:A\253\360\012\033j\202\351\245\023!\236\231%b \335\035\214V]\023J\272rr\240\030\324\027\360\305$\335? t\203\017\351\323\210\225\356", 64 },
+ { "3a,\340\2156\013\030\034r3\375\351c@\327", 16, "\345\306cH\353\360\246\345", 700, "\270P\206\031\330\027\3040\266\342\302\362\324\241E\352\030}\221x\251\030\356\037\350\030\326T\377\353\026\271\217\024%9\026Q!=/\362\263|\243\353E\010\250\375bB\262\322\032\356m\277}\251E\3261\300", 64 },
+ { "/\257xg\230\231n\356rG\376\2620v\241\273", 16, "\373\236\277.U\233e`\204z<\200\015\303+!", 700, "KF\216z\177a,rjH\312\003\232\227\300Q\021\266\3301\315\221w\007\021\270\271B^_]\300:0\263\002\300\351\215\373\265\302\231t\177\220\324\223\375\252\214\321\261\207\007\337\036\347\306\245|{\227\373", 64 },
+ { "\202P\001\305L\343@\203\372\017k\254nS\370P", 16, "P\322\260\346j\204\250\374\341Q\234p\226\200s\034\334\351\315]\356\224G\266", 700, "-\332)\326k\210r\373U\207p4\014\030\205$C\260\206\344n1\200\227\241D\357U\363\224\321\350\025g\022\317\255\001\343\204\276\260\0251\022\270\211W\247^\343:\374\263I\355%\024\002\257{\250'#", 64 },
+ { "Y\352\246\230v.\224^\2668\252\316\337\366\3338", 16, "3_~\337c-\311\010l\313\373I_\213\035\253\311(\313|\223\264\237X\327NZ$K\335\014T", 700, "\020\275\314M\357\326\251M\022\325\023.\332Se\364\200c\0177>|f\273\246\225\314\354\244\272\260\330o\221e\233\005ea'7\336\232\232:\221S\031\334r`\316\2015\257\377\203:j\221\264d@\342", 64 },
+ { "\255\377\250\342\216\0156\217\331\352\220r\302\236\376w", 16, "\377I\023\011\206\313\214=\006\344\0217\037\034\312Q\013\303;#v\275,\276b\036\023\375r\332\302G\335\307\333B\325\374\347\271", 700, "0\2629)\037Z\226\272\312\021\215\345\266_\036\373\277\012\023\364\271%\271\272\366\353\205\322\243\220\1342<\233\372\311M\232\027,\215<\334Z\357N6F\207O#\031n\215\235<\256\333\304k\223\323\240]", 64 },
+ { "\243<+\014\304\027\016\025\023\373@\377rt@\336", 16, "\316\023W\361\200\325\241\255_\347\247V\0324U\031.\377u1F=\234\206\365\342\256\210\243\346\217\317m\3334\267\242\263|.\015H9\212\313\360\331,", 700, "B\322\302\014Tt\014A\243K\347\353K\031\364_)\340Q\356\216\006\367\275\355\246\035\362k)\322$\242jn\3077\255@\036d#\266\267\274\267\343ti\211\376\032\200x\313[\306\267\247n\222\222\347\021", 64 },
+ { "\371)r\237\3421+4\356s\277\234\343\330\212\313", 16, "\262\017\333\260\372\305\253\325SWj\350\233\231\222\372s\277\032\272[p;5\204T\014\243\017A\345\314\217\210\323&9\222!y\247U\3673'\351\266W\216N\215\265\0331\331\373", 700, "r\016\300\215\261\246E\350\234\360\035\340\025\033\206C`\206b.\326\223\367sV\320\232\006\374\227\234N\027\222\376\273\264\356\206\340\317\337>\237\266r\207\206\224zp\343\335b\333\237k\240\306\204I9qF", 64 },
+ { "\351\236t\270\232A\203a}#a\340\217\314\235\246", 16, "\345a\00716#31\263\314\220\255\005<\306\277\3574;\025\004\341\323\215\023\210l\373|\226\234\251O\242\2334\311\336g\260\033<\261\236\237\271\357\230\035\273\016c\212\200\3406\361g\022\263\030\311E\277", 700, "\300\270 \346\234{<\357^\225\303\255u\351\042\254\267A6\032?\023\251\226\320\361AToOc\201\220\335\241\276\363.\277\227G\042i\300x^`\230\253\247S\205\231\2700\370]\304\343y-\134\262\274", 64 },
+ { "\326\315\030\330\327m\324g`\357\314\374,m\313\027", 16, "\222\231=\273\242\212\226\031\325\251e5\213\256\244i\036\370\247\361\2530\377\372E\244\265\367\346\251\312\300<\304\221\035\201<nh\3467\037\326;\016\007:\002\2045m!\042\277q\361P;\337\026'n\3731\177\2606\316\216\205X", 700, "@\315\352\037#l\270<\236\354\360\376\001\335#/l`\305\375\013\027\021\334m\221^\377\220\222\202\226\314RWq\3007}w-\035\025}\243J\345$=4<\302\272/\351\356\006X\3256\025\0113\000", 64 },
+ { "\206{K\024`\326\224\214U\327Z\207\266\012jO", 16, "\340N\030\355<(1\034", 800, "\270\207o\260\013\311UX\253\004!\317\203\251<\350ke=\300FyfU\346\253(\3346!,j\266\002\251)]\207\232\312\232%\323\310j*4\342\213\243\246H/\012\002\207\030\316\240{c\245\213W", 64 },
+ { "#\010-`\240\273\343zl\372\213r\261\371-h", 16, "\231\006\305\205;Qm\210\275Q\263\331\336\020\324,", 800, "\267j\314\261\015\207\272\237#+4\205M\254\237L\352\2248\337\026\303\004\220\262\232\323\004\212^$Wy\042\266\313T\345\374\305\376/\227\334\341\315\317\364\370\134$\247\270EC\237\365K@\356]\002t\024", 64 },
+ { "\034_M(\213\033\010`\253=Da\352\231\014\026", 16, "\356\204lV\262b\024\340P\306\373\273&\204\203a\251\004\316\371\253l-5", 800, "\205ek/~/_d\023q@e\205\327\240\226\215J\232K\345'\377\256\334P\226^(\220O\255*\227\257@Pt\323pP\375\010\234\177h\264\032\254\031\356%}.\257\2040\251\331\334\226\262\202O", 64 },
+ { "k\264\374\363\246\260\027[\226\207\201C\327\200=\342", 16, "\343\360\005w3'\253\362\353\030#\346\254\254\227=R\274\230\001\177_-\004d\320\317\263\223g\027\013", 800, "/\007\331v\327\007\365\277\237\361\200\2643\235)9\247\320\371vm\216d\236\266X\262 \3254\300\005RSI6\260B\277m\032@L=\207l\256\036jw\365\236\215\216\365i\305\001\037\213\262\236f\237", 64 },
+ { "\3144\231\023\012\010\377\351]5\341!\252\026m7", 16, "\332u\261q_\015\003\347\370\330\274?\202\316\336'\223L\346w-\024\346\236Y\363j\3019\211e8\304\006\241I/V\247\277", 800, "\016\262\272\311\010\201\270c\006Le\301D\362sR\274\010\356\231\275\002\260\267\374~\233M\134\217\204\233~\360\246\042\305\015\230'\233\005 D\371K\231NT\323M\362\206!\0100\316\247\202\333\233\2570N", 64 },
+ { "\264M0\374\215\246O\340h\022?\042\326x?\254", 16, "\330=\266\357\362\233\013aM\377\273j\317\366#\342\240I\267\323\203\264\233\362\341]\261\011\206\035\345\234\372\247\015\322\205\2212\2123\363Y\274\226!\270$", 800, "g\350\315-uW\360\247T/_AH\336R\251\355\374Q\274\361\257\353;;-\303\341\252\006j\3071\334P\271b\374\306\272=\301\274\317!r+\322\342nAO\223\260h\254\270A\217\360\221C\020\375", 64 },
+ { "\262\234\242\024\023\246;\320?Rm\313\203\134\257\253", 16, "\011-L\245\210\020\275\003\265.\006\325d\005Q\012j\325{\357\312\007\036~\324\011c\203A{\363\010\244@I\276v4\330a\316\234$lL\351\227Ml\033\245\016\035\350\230:", 800, "\362~\243\251j\037Ef\332\032Tf\002\003D\254_\361\220|8\325\177\304\021')K!\230\226\006n/.+\354\236\007\264\0053\3451o57\211x\217\033h=\360&\255+\265\242\217;6U\302", 64 },
+ { "\244\034\321}s\264x7\325\015\366j\037\3222\011", 16, "\345\345X\367~\021\235\341#\225_\305+\326\310%\346\200w\004\340{\226\226\021\014M\2509\013\242Q\012\206\035\234+\2672\214\210,\306u\004;\345\303\002\221\231\320\037\310w\300B\327G[\357#?\231", 800, "l\017\322\2302z:bu\331X\333\236~\227\337\255\330\345\364\335\016\305\3370\243\275n`FnB\346H\134(\025\022\231WJ\037\253\212\245\210U\354`\0269\354\246\243\213\275Nqn\352`\2649\004", 64 },
+ { "H\235\224~X\271\344\241\303\035`\265\0324\2460", 16, "n'\340=s\250u\025a\267\361\030n\326o\357\255\021\313\2600g\200\222\277zL\370E\223\277\357 \340\030G\207\031\007\277\227\302\233\254\320\375F\370\262O\303\030\262-\216\214\042P\323o\2002\337\220\334R\314\372\223\007\373\207", 800, "[\316\226 \270\134\257\204\241\336^\273\236\332G\333\214\343<\2759\037\202)j|\233\310\032)\214w\207\264&\335\366\214\352\370\030\343\353\2047\327\353\255\027\362\351\361m\264\226\227\207 \270\260]\373|\027", 64 },
+ { "!\206i\223\011X\356\016B\030\270\305\207\375-I", 16, "\027\240;R3\271|\332", 900, "\360*\314r\326\366\254l\023\203P\222\026\371'7G\375O\250\016\245b\267\277\233\320s\256=%'\254\316\353\023g\177\235\210f\206\351U\336\3409\312E\221\205\321\335cD\350,\325>\277\342\304\015@", 64 },
+ { "o}J\242\212Q\201\317\222\006\331dI:\362T", 16, "\227\363\374\030\2151\324W\373\366>QP\367\032\315", 900, "\374\010\253\244u\317\244|\366\2348h\341\373\322\350\234,{x>V\036\006\266m\042\374\335,\351Vl\376\341\026\000\233\302\021\013u\274\246\363#F\321\234\226A[J\323\253\371\201\344\306\312\222\300\232!", 64 },
+ { "fW`\315\042.\323\234/\024e/\235.\367{", 16, "\377\001Rk\017\237\367\371WR4q\271\242\200\034\374\224\224\026\371\3056\305", 900, "\314e3\234\224\216\231\334\257\005\354\223\241\322\363t\134 T\343l\246q\237[\300n.\014F\207I6 Hp21\271l\244b\023\352\214\356\331+\331\006'\321\215\301}\223!\3318Mp7\005\330", 64 },
+ { "\365\016\371I\230s\357\307\245\037\373\023N\2579\236", 16, "+\214\007\212\252%\034\006\337Z\013\267\134l\211\030\302\271\267\376W\014!\370 \354\272k\013 \265\035", 900, "\026\345\251\321>\361;\226\251\251\277\3261$\233\224tk\373\011h\030\256N\030\314e\352M\2200\273\341\351\350\302\257\03796Lc\3615\215\033J\326q\347\364T\0232B<\015\202\251b\021\352\271\224", 64 },
+ { "\357\305\300\016h4\217\213\205|\365\275\2402M\212", 16, "\232{\261\235\227\366}\037?xZ\217{\271\352\3539\3628\370T\207\370\222\334\221PJ6\237`\235\343\020n\323\370}+\025", 900, "\006An\011@\267Z\023\026aW\274\002\263\316S\006\374\372\362%\003\357C\355\307\202\023\266YN\245\361\324:\311\201/Pa\342e4\2122\203\255\312T\201\243l\357\337}\361\302\347\034}\315l\007\301", 64 },
+ { "\254\373N\035\251\017\360\304\005\333`\256\301\240\351\020", 16, "\257\231q%\353\262\257\37167\260\364e\222*\0135\242\214-\263;\011\232e\237\221\202\026\352\334\034=\326\346\246\016\353\375hH\020\2666;\276\254\254", 900, "{1\200C\304@\350\255\323d\266\042|\2019\262\264\177/T\362\020\346V=\032\360\226WC\234\260u\244X/6\326\202\363\025[\327Y1\240\243\030\373m$Yp8\220\272v\321]\361\343\305?\226", 64 },
+ { "\200\323\244\005\342\327}\330\0329D\266v\037\332\234", 16, "\305sYC\035z\357\246\025\315'\376\305Do\345\022\320\016L\321\236rW)\0128>;\042\215\2640\244\370\245\271\312\220\222\211j\247\323\252\324\252\316G\014\307\303\220\026>\241", 900, ":\015\356\340\031MC[\210\244\201QVg/u5\331\215\134Q\277J\322E\306\222\372\346X0\210N^\207'\300I\345\011\020\366R\014\177\213\037\3425&\252>\264H\316\335\235W\264\265\256\3774\002", 64 },
+ { "\033t\350)6\260R\333\031\346'3\021x\014)", 16, "\317\036\207|'\372vD9\324\306m\004B96\364<o\007\317f\274e\355ty\341;\311#\031\311\203\321\341\034\201\373\315\352\353\302\267\361\034A\206=\353&\346\367\260G\220\013\244\013\030\334\254\257l", 900, "t\261\230\3379L\220\244)\266t\031\022tW\027\314x\205\017@\345 \027d\006\211\266g{k\255a\031C0\253nVA\025\215\257\027P\376Pc\316J\341x\273\272\241\332\315I\360\2035T\264,", 64 },
+ { "\320RQ\307\356\3355\246\351\031\230\013o)C\227", 16, "\273\250 {\345\241\252\366\333o\345\2065>3\007/\337\212\015\042\015\317\022\375\3434\3669\266\243\234+\336\261\234!@P\204\377\220d\236\370\254\370vx\312\134\017\204\3509\225+\034\351\024\267aH}p\364fW\006+;=", 900, "\023\234\016G\310\260[R\353\236BW\207\317\016H\245'\203'\3209L\0148\230(x\027\337\025\256\355&O\2170\024\177\312\200\005\320\365Z\353c\365\207\333\134\264?\321j\0170\312\263\366\350\2733\030", 64 },
+ { "}\026\215\320q$\364\307\271\035\234s\210bw\265", 16, "\260d\255\307\300\313\321\370", 1000, "ko~\027fI:q\276\226\305\216\270\317\010\340)d\020\353l\010\274H\013-\306\331\014\277\252\254}\007~\371\327{\352\216\255\211<\366\221\201\311\001\340\267JV\227+\347\253.\222\200U\317F_\276", 64 },
+ { "\302\316l\266\270\312\0316W\200lJD\344\263\264", 16, "\353L\235\037:<\341\377\357z\035R\322\221b\010", 1000, "\357\261'\225|\274\201H\253\332.\240:\2151\3776\250\032Sa\351\3659\223\366\240H\200\2439\007\223\240\300k?,\004\203$\200\220M\236\202\314\032\321/\231;\210\312\013\214=U\0129\246\247\347\324", 64 },
+ { "\351%\241\263p\261\200\362S\377%\206e\350\237p", 16, "\347x\025h\232\331\240\015\006\035\273\353\233\001@\004\177>\254We\361\353\215", 1000, "\302\000\266c\330\375\3350W\022\272\3446\345EY+\311~\244\0162\006=\353N\212\343\257/-B\271\270\343!\030Q\210\242F\307r\276342\261F[\342\337\277\321\217\360v\015\215\301\005\367\232C", 64 },
+ { "\224U\367\245\357\276\004zv\004\017`\350\315\220^", 16, "\230G\361u\016\305\221\367\253\334\344Dg&x0\314\301/j\042\211~\320\207\207y\217r\237p\354", 1000, "{8\226FL\023\270\334N,\247>\033\032B\032\354k\010\001\373\222;t\023X\304\371\276\244\203\316Vw\352\214\310yI\010x\025\215\250}\301\027\205 .\042P`\306Xrx\322\257\235\340J;\277", 64 },
+ { "\225t\353\356\367\376\013\326RX\246\016\134D\243\336", 16, "D\006}S\362\262u\220p\364\313\365&{\346n\277\322Jv\2202xdk\230g\327\341C1\220\374\322>;C\263\254W", 1000, "\026.X\262\251\361\032\206=\326\251\214\2721.!5n:\266-\267\015\213\216\313\335vP\013\305\034*\301\177\226\010\345\030\322\213C\313\233\251n\245p*\207tO\021\022\221\020)\006\212\237\014\223\020\256", 64 },
+ { ")\035\350\023\325\370\321\370\243\005\263dm\3147\252", 16, "\015m$\343M\220\3573\304\200,C\032m\237}\256\366=A@\200\223\177z|~\263a\321\320\344=z\0229\244\200\222\253\013\224\021\265\224\023Hf", 1000, "x\351\246kB\345\355\201\134K M\003\321V\307\024\257\246\251\031\224>\244\021e]\014\262\272\004\206>\251\270D\372\226\206\352\225\273\010\036!\364_\327\244\001\353\012\250\232\227W(\034\231p\301\031\203\325", 64 },
+ { "\246\337^`\273\277pp\204\370\235\023\256\026&Z", 16, "\327 h\205i7+eN\362\265T\320\321\237\214s\036,\326\021\357:7S\302\212\253\016e+\251\253\334z]\022\256]c\007\310`\223\364\022\206\245\217O\036\251\366\214\242\370", 1000, "\346\364s\202y\026\347\275\220gk\304\232\326\234\025\257Rg\254\233\216\344a\306\225\366N\216?\276\367\032pn\364-\322\331\367y\002*\276\322x\340Q\016\354:\341\216\320b\216w:\267_\227>\274\235", 64 },
+ { ";\355],\232J\026\265\200\333\220Di\271\362\325", 16, "\217p\015\025\3636\334\234\011:\304\3378\203T\247\207\350qA\370\365\307\021\230\250\312:\214l\311~\274\220\303\225S\250\230\343hc\261\374\343gI\374JO\013\325N\271\255A\264\032\215\013>>\262\021", 1000, "Tm\271\346\333\031\034\261o\233\342G\006\277\264\2759\015$\351\352\331\214\200BL\375\356\322\376\340\337)f\356%\346Tm@\277F\027\012\315>\327\276\001\302\2576\313\244\315\276H3\346A\266\226\312T", 64 },
+ { "1\317C\247\340O\362+\332\245\006\261\2069\252\342", 16, "l\31548\373\321n\375\007\337\320\277WV\334\205Q\230:\021\010*g?\3702\004\005\306\305\324\025\256 \203\351^\276\347H\306\345=k\310\247G}\326\020\347\363\350\016\215{\023\374\357\335\2669\315\353\336\250\224\303W\370\246\221", 1000, "E66r;h\253\215\200\205\375d\261\331\275\264\330\3146o\011W\210\217NI\343w\256;\226\351\351\000h\346\340wi\221\275\302C}l{\336\272\030\177\273o[6zV3j6\007<z\301\012", 64 },
+ { "\374v\346\261\317NF\250\231\373\033\2245\2710e", 16, "&\323\263\377\023\341\277\134", 2000, "[\223\375&G\271K\011\025\134\370\0106\031C\004\026\342S~\322o\341\255\022\005C\010&a\035\2518\363\042\344*=\023@\033\366\340w.n\354\200\360\365hp\377y\245\337:\251\006\245\355]\240\257", 64 },
+ { "\004\3662\363\223\211\214!8R\001\342\222@\240\007", 16, "s\134\376\266\373 \303\003v\371\241\351\333$.\030", 2000, "{w\276SN\361\322\134'B\322\251\020\355T\370\313\030cT\330\320\223%P\013\363\241K\257\000\257G\312-=\244\213\276%r/I\367\013*\241,\303\376R\217\330\000\250C\311\371\134\042=\260g\214", 64 },
+ { "\326\177\361\022NR\032\230\371\324L\276\245\201\013^", 16, "\205w1\025*\032d\307J\134\335-\314s\313cb\227Np\367J?\200", 2000, "\347S8\233X\213Z\345\247v\342\237\354\302\025\237`\315\204\221A\253\245\260\327\275\244\300\0167\216\307F1o=\012\341\207\307\305u\220\340_D]5~JC[$\254\034\005O\336\036\232\223\253O\007", 64 },
+ { "\032[1B\237\025C\314\315,y\242c\361\363\201", 16, "$\344\215n\370\134\225tJm4\011\012U\004\201w#\034\273)^\236\202\334\266l\232\205*F\333", 2000, "\0323i\231\017)\341A\202c\335\200H}\017v;\037\226\036P.\270\205\035\000\372)\257\023y\340M\277\232b\000\037\341\266>)L\244T\025\256\353\2726\023\241\3501\221Qz\342\001S*^\327x", 64 },
+ { "^u\024=)!f\324L\251Z\200\377#\134\362", 16, "\224v\363\204\345\353\263&e\242g\310\227\323\231\370\320\350u(3>\346\013?\3316\360n\316\2560\032\264n\003\177\352b\360", 2000, "u64\012\241KEM\001(>Z\237\042\027\240\361|\252\216\357\214\017-\302\002\365\224\355\277\351\033\011\030\324\363\034\320q\340&F4uCc\235{\347\013l\2671zB\025\236\3174\215k\361IJ", 64 },
+ { "\256\002\200\356\321\334\300\0423Y\021fv\323iD", 16, "\224\307\134\316\002\320\273^[g\332G\320U\0139\004\022\220\010\372\200'iw\376\236\012\034\304\017\035\334a%/\322\277\3206o\262*8\014<\004\202", 2000, "4\274\031mr\012\340\305s\042/\217\205y3\374`\224\223\311\377\222\301\202\214\351\337\010\015\315m\213\263\334g\314oT\372\330\246(\333\306X\321\376X2\206\026\230\333\361\325z=\277n98\334\377@", 64 },
+ { "\250\346\251\243\207\200\303\011\307\231z\220\332\004\367\021", 16, "F\251%7\247?A\003\200\015@A\260\042\247\310\356\004/`#\222\244\201\317\375>\134B\023\024\007\032\251z\330\371\3456\006Be\207\343\354CR<\261O\352\362J\361\377\367", 2000, "\276\207\365,\306\033P\0200\231\2013c\351-\301\032\207\212#\301\3233D\374b\027\206\357\335\341\321\205TO<\316\225\011\271\013Ca\235\315>\214\325\222R$\003a\224\005v\253\226\243s$\375\345\240", 64 },
+ { "\222\324\307\304'\000\210\255 [),\322\357l\332", 16, "\007\345\242+\012\357t\231\202\266\324\250iv\277\257n\315\354r\025\004\356\233\317n\256\042\202\372\251\223\230\226c\203=\334\331\230\237\034\2129\215\365\317\006\250\010\361\335\226\261\367u[zT63|\365!", 2000, "\371\270dr\215\025\035\212\004\012q>\263\267*L\201\373g<\333+\215@\334\260E\253,\302\261>\222\201)Y\006\316,{?\303H\240\253\326\362b\134\374\177\013c\351\237h\025\2350\313\3422\313\272", 64 },
+ { "\325\351\200@r/\240=\323\331\372\023\244H\013\236", 16, "\326\307\360\340u/\260\312d\314E\240r\134\025h\272\202\230\177\364\346\134-L\345\362\260Y$\207\027fv\257(\336a\307\215\341!X{\024\0317\345\257\240\263q\332\032Y\230k\026\254\037-t\031\244'\353\324\344\3014pD", 2000, "sf\252}\216![5`hs\210\324T5H\346\037v\314\344\346\330c\240\373\021\023-s\350\356kj\224M~i\206\325\246\325b\347\335\252\303+\277n\003\307lgw\217\245\025R\205\301\257gb", 64 },
+ { "\244 \262P\000\300\360\311\042\251hn\226\023A\270", 16, "R{\337\244\226\014\276w", 3000, "b\254\205l3\271@=Y`\251\226<\316\334\013$\321\357[\357\324P\201\246!\355W;\362{\367h\2072\270\247\353 \271\357\233_\330C\322\231\341\251\315\010xm\270\350\260\352\304\035\304\204K$m", 64 },
+ { "\015}\205\262\251\331\300M\312\216\024H;\313O\030", 16, "@\225\032!\031\231W`\325%\332\334+\345L\257", 3000, "S\042\245F\263\330\001\203\365\020Q(Q\032O\302\236\311jA[Yq$\000\210\266\026BIFj\255\263\362L\357^\304I\266\222\223\001~\321N2\202\206\301Ml?.R\321\215h\327*\347\317\324", 64 },
+ { "\270\257g\226\273\231t\256;\025\270\267mL\177y", 16, "\224\2678\3320\274\306\365\254a\260\307\276\377\012Y\231\372-\3652\026\225\026", 3000, "5\3706\342[/{PT\265sHl\345\275\215\273W\300\354\341\342 \334\011\013T\003l\225Q\333\277B\240s\211\231s\360\353\301A4yj&`\024\005_\307\216\325E3\235R\032>\213\034[4", 64 },
+ { "\312\207\223Gfp\250\306\342\002|\233\1349U\004", 16, "4\321\374\364\375ycx\020g\345\224\352\0424\313\011c*p\267\376\232_\370|\240\010.\3227\033", 3000, "\341.\134\262?0\256n\334\263\356\273\3141m*\352\344\237\302\300\364>\347Kq,mu<\327\010\267.lg\002\232\226\350\354\010\263\215+\032\014\230\262\375R\264\032\204\334\351/\314q\361w\246D\274", 64 },
+ { "\2645\264\241X\321\351\352\342\015\253\257\241\354xn", 16, "\214Ihf0\343\371\323=X6\244\327\237l%\340\300\021\006\271Q\340\002\267\331\234\0100\231#j\005\342[\367\012\042J\237", 3000, "A?@\303a\325\243\015\200\206\256\025m\2471\254|\252\203j$Y(\013'\346\336\011\217\355I\346m.\001\337\033V\022\342\2177\216\264\211\000\253\330\007\315\323s\365o\371\330t\361\235:\0231\266#", 64 },
+ { "\250b\326\230\271\352\346\357\305\245\303:\3236\036\300", 16, "Fe\247\220E?\206\201p\303G\006\357\027\204xD\264\237\247t\042\357N\335\351\351B6\177Q\301\307\334\012H>\2572\275.'tu#\014\371\260", 3000, "\3270\222r\024-Gf*\352\204\271\305\315l\234\370\364\313\304\250N\267\321i_=5\370R-\302Oi\251\305\236\244\376\005\033\270\265\031\007\242\234\301\02166q\243\042\035\326j\224w\035\232J#m", 64 },
+ { "\226 \316\360\363n\302\012\207I\3022\252\232\271\221", 16, "\213\316\027t\212l\242\354\014\304\376\344\377\352o\207\330\376\037?t/\214\316\017\027\363Y\010\017\027N\326\007\003\263\274\042\177\230z\264x\340\017\204\005\203\200}w8R\304|g", 3000, "x\330\300Nq\0371t=\326=O'i\0176\314k\351\301\335\134\372s\200\331\010\322\226e3N\016\341\251\3670QT\322\200\346\217\300\202w\010\253\242\342\272\024&|i\307\270[H\357\306Vu\004", 64 },
+ { "ng,\206\357\313\353!`\263a\237\375\256\224s", 16, "\334\370\260\331\016\0355\342]/\207\227K\2323\256\003\004\314\256\020\020Wc\241\367!s\366\223\025\254\245\227`\335\226-<\375\367\010_p\2151*\204\2501\263\242r\324y\231>Ej\324\374\265\247+", 3000, "\233\241\316\316\335\307~\260\336\332\273\337\263yqH^\202\037\265\375\032\353\364\277\241\241\2456\325\3558\364\360ft\316\031F\350\300\026\026\264;\345{Ru\002~\027\340U\316\313\231\205\205p\3167\256*", 64 },
+ { "]\340\304\315\250W\316\336J\263\231\002\216\251\364\242", 16, "\201\376\003\177\021\346\237\211\320\262\334k\320Hu\220r\357\237%\222\357\013\350\330\042{\210t\227\376\372\016\3707\340\230f\200\360\300u\312\034\220\376]ct\225\311\275\005z1\243l\373-\234\2154\317zZ\0306\312\2435\032C", 3000, "V\373\262\256p\232H\0424\034\215\015\273)\217\366\237\253\323R\226\256A\257\334\355\035\376\331=\251.T)OVJd0\005q#\356\346\012\223,\017\015S\227\007\034\010\026\370\271\233\267\030\010\357*\244", 64 },
+ { "\021\225f`U!\334\331P\355\312\351-\032E)", 16, "+d'\210\235<\262\204", 4000, "'\276\227\331e\015\206\231\234\323\2578\306/\0338\007U8\354}%\310\265\335%O%\255\377\376Lo\303\350n0\353Zi\355\277\315l\010\252\366'G\272\263f\341\310N2V\346\334\257\214\333\002G", 64 },
+ { "^\314\361\023\376q\261\003\245N\240\353\031\300\031I", 16, "\355^\377\321#:\253X@7\201\251\215\257&\310", 4000, "\266=p\000\366\231n\365r\310\011\266\315\357\242\011\333\237o\030k\364\341T\042E\332\313\255\227\246\251.^\372\012\317\222\355\244\2066\034\336\000\250\277x\000\315\002\373\263]\037b\350S\036\303\341X\002\335", 64 },
+ { "\300&m\242\343a\326$\210e\200x\012\335\036Z", 16, "SG\363E\013\273\017VO\360a#J\220,\263\375\322\202\227\242;Pr", 4000, "\242\351.\17783>g\001<\034\004\030\002\312\345\270\021k\342\375\243/\342-\346\240\017\260bj\342e\206\225\134\367x\330\020/\363v\214\241\025\205lg\347$\275\300\315\205\021u-1\246SH\205$", 64 },
+ { "\235QV\311\015\001\301Sd\204*\270o\026\177A", 16, "w\020\211E\315W\014\015\342\344j\251X\341\2528\354\371\336\214\260B\367)\264r\345r~\221k\255", 4000, "\3260\375\203\347\030\375\023\351t]\015=\301\267\303\035\204\2345cJ\2565\243\266G\211\333\024\311\320\2556\031\302\310\257\273{\340\274A\011\305\206\272\232\264\336\217\012)\375\036~q]\241\267\220[\203\330", 64 },
+ { "\303g\3655f\271\332\250\306${\317S'\270U", 16, "\300RV\321<\333\2779\031\216\346/\305Dv\021\333a\374\344\303\355\370\0367\201\315\346]\242\375)\323=e\023\013\201\306 ", 4000, "@\253ur\230\326\344l!\004\026\352D\222,\021l\0316S\205-,\035\374#E\023q\034\312\0003?\234\000bs\334\011\011\204w\2558\356v\343\255y\260\023\320\305T\314\300K\232\000\013\352\305:", 64 },
+ { "\366\307\305\200i\370k\246sc\035zh#`\035", 16, "\341@\032\010\373\0114'\275\204P\256\314\324>\030\266\343 \034jB\372\0124\255^\234\207\274\322\227\357\244VJ\273wq\326\224\373x\360\327k\3508", 4000, "\323u\002\267\022\271\274t\002\305B\017\015\366y`\256\201{w\017\240/\225p\356\036yj\277\277\242\243)$\244\333\272j\367\024\223>\021o\370Aq\343nV\237\031\234\350z\226\201\300\024\256\006Gh", 64 },
+ { "\365\236\311\322\134\327\033F\230F\370_\217\221H\315", 16, "\214\207u\217\253\234Y\236\377\343qB\374\035\231b\267\266\314\226V\207T\367\352V\001jY<\337~q\203\345\265\230zB\2110\346U0\231\205\246\254\013\213\011\134\236\351YO", 4000, "\315\360d\246\273\364\265\217\210\306dnuA\004\347\022w\200\375\267\341\336\340\226jlc\354\205\310\316\251G\373\347\237Cee]O\351\237UN\236`\305\244W6al;#\336\377n\335(\337qO", 64 },
+ { "\341\325\003\214aV\254\361\365\314\333y}\005\036p", 16, "\274\271\3200UV\327\273\263H\022\362@\322\300\362)SMQ\335'6}\017\255\332e\341=\377v}\024\012\316\233\324xJ\367\011\016\260[\260N\322\257\011\005h\273\237L\010C\263\3300\321C\365H", 4000, "\336=?\327\210\002\245G\275:Q\371\271\233\300\273v\274@\002\363\340A\305\372\272%\355\365\343\012\000a\204\276\243$|\237\223\221\240*\351\311\262\327\367U\000\316$G\224q\036\033\327\224\247\371rzY", 64 },
+ { "E\352\312,\222$\365\267\303\016\331\276\345f\305\134", 16, "\01152\236\032\242H}L\263\334qk-9\013\006\356\357\251\372\026Tb\304\256\226#Ad\216\3024\333\264\240S\217:S\257\024\347\030\321\026\223\024f\301\261]\301\002\272\350\023M<\254\255A\270]Yh\251g\2235\206*", 4000, "\352\317\323\300\357w\365m\200D*\277\024\330\246H\207\347\257\211^\240\362\005\001D\237\310\332@T\243pB\247\025\236\204V\377\346u\242\203q\324\307\320\314\207\372\376\222\206m\134\037\365[\307,\240\377c", 64 },
+ { "ho\014\234\352\310\134-\224\021\301\134\015d\356\351", 16, "'\256\217k~\376y\335", 5000, "_\341\264f\012\003\275\012It\363\322\301\032\342\337\331j$'\006\014!3\3029\313\200n\266\003\254@\236[\011\214XK\372\020\210\335\031\134\303\011\042-\004\365\003sn5\235NK\375\013`\022\336\025", 64 },
+ { "4\020\304\323y$t\014\202P\251H\371\235|\035", 16, "\303\370Y \366f\325\317\352\030^\245\234\366\010u", 5000, "#W\033\270\241m\245%\357\270\243\340\211\271\263y*\341-\002\277I\332\227\042\271x\321\344\246\030x\252\233\326-s\302\334\327G:(!\315\220c\346\240\375\012@F8@\205\271Vx\321\202\024!}", 64 },
+ { "\034.{\0068\255D\335\340\303m\273\356\223x\204", 16, "\247z\325\244A\337\245'5\373\351\336X\330\003\2576\243H\020s\226\266p", 5000, "\353\206?\351#+\346\206\330\225;ua\020\263\312p\024\2229\213h\015\336q\254\216P\256 \004}\001\355\230\214emU\323E\371g--\237\303\022'\016\033\355\274\267\271\351T\0327\300\306\253\031v", 64 },
+ { "\015\363\324\364\323\220\323f\177rR&v\375\027U", 16, "C\020\362\262F\351H\240l\271\245_G\134\034mC}\277\354\017\377_Ab,l\322_6[v", 5000, "\370\357\015*{\001\221\310\244\235\134\214k\335\310%\274\226\3203o6\223\361\243\356\200\000\340\240\330~\225\266\004\224\254\211\276\270\202^\251P4\237\350^\036\3671MV\317\214\227\022\211\344\022\321\001\257}", 64 },
+ { "\312\272\240\233x{\372:;\253\370\330\334#\267A", 16, "\323&\213|\016\3346>\224\345\032;R\233\0056\316:\037\315V?\221!h\0308\231\333\207\221k\035\222\252\014I\2215K", 5000, "\004\012\245\017y\316\355)\320\007\331\236c\324+\3771\247\257|\336C\332\331\317\037\007KG\321\334\235\237\265\301\360\315Im\212\260\012d\033DIb\042\260\025\304\311I6\2158\3527n[\032\347x\371", 64 },
+ { "\215R\005M\032\350\240=\023x\0165\246\231$.", 16, "\021\272d\364Y\217\317\346\026\0161\327\214\001\357\335D\030\364OE\264\266\012\270\350c\276\340\327\254(\2638\361O\253\254!Lb\021\014S\234Z8Y", 5000, "_\3748\356H\220\264;\253\233\323\376\2548\235\004\357)\002\277#Gz\253\363j\026\015^\335\024#\211\002\354\207\373E\364\2513\344(f`\214\227\3319\031SIh\260\234\202\301\375\204\315 \002\347\376", 64 },
+ { "\245\313\007\234\303h\224\215\356\243\352O\206,e\266", 16, "\031\033p\375\350W\006\004\213\313]3\214\257v\333\0357\016\375\364\021\344\305\323,;_\330E\224\351&N\244\026\275J\354\024\340\310'/(\012V\217\253P\361\236\273PMl", 5000, "v\276{&\342:\355Rm1\362\320E\344J?\015\023\323l\315\331\252\031\245\206\231\014\303\340\022\231\241\332qe\036\226qF\213\254\037^\220\042\324L\204\011q\277\233T\303\350o\217k[\327x2\346", 64 },
+ { "\273E~\201\327n\340\231\001G\001\367l\206\021\030", 16, "<0=\371\010\317Aa\375\340\261\234F\275\251\236\024\310d\003\265^\345|\372-\223\305\235\272\344\350\271\3669-Q\264\312S\323 \236\343\352x\304)\341\334\355>j\024\251\377\252-\356\241.QW\256", 5000, "\307\241^1\271\302UOY$\250\372pd\343l\031\205\353\272s\004\311\002R\276\006\232I?\327\020\237[\363\012\2656-\361Z\177\025\210V\204ZF\351w|\206RqU\042T\205+\222Op\235\376", 64 },
+ { "[\034\223\310a\210w\351\020J\351\263\240z\034\261", 16, "<\217A\265\347 \264\242\024\324:\011\370b\263\345R\012'\271.\005\350d\027\314\017\034\361W\316Tj\225n\1778Y\356'\012ie\307\342cF\266?M\300. \271\247`\042\344\272\371\013\273s\316\357X\207\304\357\013\322\315", 5000, "\241\224\024\317\303\227\241\317\332\351\301\3567j\326\320w)\275\223\234m\241{B\356\274P\274,\012|\235\367.E\345c ^Z\321\031\352I\042\005\365\264\360\245\357K;\306\202h\350\206fV1\000\325", 64 },
+ { "5\265\376M\262\3518<\353of\232AA \374", 16, "WSKsW#\354W", 6000, "o\027\243%Q.\3759\331k\011R\363\367jK\234\357o\363\317\220|'\245\233l\250\001N\203\322\002Q\026\027H\267\253v.\351\242\220\257e=}\010/v!9\346\352\3749\2014q-\340N\376", 64 },
+ { "\350\203\337\210]\367\213\335\270\213\232\026\221|,\370", 16, "\346\327\263\312\213\345\005\011'~\221\027\355\241J\231", 6000, "*\022\205kO\256\322\231(\361\367\342\277\266\220\000nj\330xh\340\215O\356d\210\225\303\2373\340\2130;\005\254S\3115R\257g\344\233\023\347\205\237\274M\307\367\327\364-W\246W\203\275\373\237\272", 64 },
+ { "m\326\335]k)2U\200G\347\000\311\010\311\302", 16, "\340\250^(]nb\003\212\354b\177\344\011y\300P\016cl/0\016\213", 6000, "\273\333\252\306\015\232\306\321\251_]Y\006\042\345\350r\222\010\372\026\205Mg\373\347\267\014\234\032\016d\210i@n*aP\316s\366\206\200\004\015\017\034\325g\334OS\177\214\374\230\211eP\213rNp", 64 },
+ { "f\323\354%,\247\330/\027\373\267@~\345\352\256", 16, "=\354\324\017\336\012\214\370\014Pd\217\317f\364\301a\207\032+\227\2247\201\317\215\333\020\276B&0", 6000, "\245+\223Y\302\363Wl\312\0344J\245)\347\217\027l\027\321\0121\341\365/\263\374t\330\262\210\220\250\354\240q\020^\033M\274\261\350Sp\325\254_\262\304\357U\031*\262$\363\371\030\036ay\200\242", 64 },
+ { "|\035\377\326\376\2752^\335\207\230\271\341\326z*", 16, "P\271\315\034\230[z$j\377\266\015\205\3064h_\316\254\271\353\342\301*\366\366\350iV\004\351f}\260wr\257\236\025\241", 6000, "\301\354S\253]c\332 \277\205!Y\356v%\225\367c`gB\006\024\247t\314\300Q\323\225\020d\036\024A\353d\335\007\025\014\245?\252\246W\222w'\301\263\003M\014\300.\014\2420\011\355\342XA", 64 },
+ { "\364;\377\002H\026A\016P\352\331g\2407[\261", 16, "p\271\243\3110\372w{O\317\315Q\035\371\221\016\236\215\002\222\317Z{\372\375X\316 8\226\270\024\344\373^>\235\274\320\337\313\366\223\246\336\250\134K", 6000, "\340Lv+w\360\311\322\355_\036@Hd\246\275\324\033\235\274\013RJr&zD\230\345\033\216\234\213\255\237[\217U\367\300\356\331\372\237\021)x\362\216\306/\351\374\314\210\2341h\227|\230,\336D", 64 },
+ { "\134.\271\033\351\232P\020\356k\321X\351q\331\004", 16, "\277\207!\216\312\324\347\223\234\202\360\220}m\3773\210\232pe\020\254\371\334\266(\227\254?\305\017T5\020\210\223\006O\223\375\357!+\363\003\245.\325\255vF\350\3565s\221", 6000, "\352*\003U\323P\225\256\254\374\001\337\312\377[\134>\2306\270\001\217V\033\006<\243\265\012\355+\024&\374\313\031\254|\302\006\216\216\250wD\352\320\3128zf\000\205[P\323\013E{\025\353\276\316\034", 64 },
+ { "\015\372\205\371:m\005T\000n\3255\366\220\2653", 16, "\266\234\036\134e\020:],5\005\201\331`S\305\224\017kW=@.\025\376\017\353\022\210\007#|M6\354\210\3121N\356\331\033\223\230\344\305sR\241\232p*t\217\366\243\352E\255\332\004\270\365\361", 6000, "qq\245\012\237#\004\316%\345a6\322\340\353\0344\265\013LH`\237A\232\337\357]\363\023[\353\2631\000\277\013\317\336\371\3125\312y\305`\236*]]\204\236\302\344e#L\270F\232\372\267\232\250", 64 },
+ { "F\256\277\366N\234\235\316\042\230\263\256\025Q\320'", 16, "\200\254Yq.\205\373\373\0056\331\214\376C\235\277\323\350\261Rc<\177.<\326\237B\353\373\360\313\257\333^\212\303\023>\223\004\327Q\333\271\011I\303\230N\325N\0172vT\011\214\330a\260\216n\017\207xd\264\327\272f\276", 6000, "9\343\311g\224\222\221\275\256Mb\206nD\361\232L\327s\222P\005;\255\027\240\261R-M\231\347\315\3164\264\314F\347\245\372\247\247\303Q\341\241\271\003\035\207\303\304\335,\012\360?Y\320Y\227\376q", 64 },
+ { "#\256)c\376QG\273\245\222Y\010\357x\221;", 16, "\315Ysi\036\266I\332", 7000, "Z\213\027\2367\231\205\005\224\300'\224>u*\372\031\003U\205\242e\223\274Q6F\362;?\022\327_\343!\214\276\262\247L\260MLX\247N*Y+\331\361\215\220l\331\213\361\335c\314\203.\330~", 64 },
+ { "\360\343\200\367\344\250O\260\215`\247Z\341\274\346\270", 16, "\310\2524/j\334(\344\303\017Bs-\201\264\312", 7000, "\037$}\362F\213\254\306\345R\345i\326Z&\262Q\017\304\210\026\216\336#\220:A]\210[W\246\272\005\011z\337\036g\366\206)\245\201\214K\335#\212\205\350B\376h\034!\235\006M\272\256\225\257\220", 64 },
+ { "<\230\261\363\334\357\217\022\276\000R\306\240\210Dg", 16, "FQ\263I\223\375\335\2135\006\374-1/\227\207S\341\202!\372\206\253\236", 7000, "\266|\263\374\266\265}\000\261eB\307;\233\241V\253C\014\224\230\012\326q\276\242>M\214[wt \361\361\205\303\311\320\355dV\245\242A\237MAt\237\340\242K\3133\355\357\034\303\200\0277\267U", 64 },
+ { "\213.\310\205\014W\307\364sd\223u\363\035M\260", 16, "\331\007K\015\367D\224\016Q@\243a\264\342\346\216t\2161\030p\345{\227\245]\245\274\235{\322\245", 7000, "nX\013K/^l\377\237\3044\010\277\367\134.\222\215/\3471\023&:]!^\261\177g\272\011\177\374#2\177\326v\251\003\005\374\232z\030\274@\311\321n\323\227*\024H\225PA\241y(\276\361", 64 },
+ { "`k\271x\304Cf\230\313;O\277\236\2448\207", 16, "r(\245\265\003\237kT\204@\321\252\202j\242\352\365\366FZZ:\366\272#\323\017\2666U\240v\252\236S\332\011\001\361p", 7000, "W\200\361\034j\376\022\254\325\205\257|\232\255w/0\373\267n~\355u_\331\007\242jXi\034E\254\032S\205\034/\023i\356\310\250\226\3517\201\311\360\237\201\221O\267w\355`\366\217\257\006z\357\321", 64 },
+ { "bg\316Zg(\330\371\016:\011^\202\000\235f", 16, "_\177\346\022\375\256%\310\347m\336p`_J\333\334\003\312k\254X,Z\361\372\303#\305q;\343&XT\316`\006\036\215\306\207\327\311\255(\203/", 7000, "\313\000\3670T+\346TF\365[6\223rBb\241\267_\035\321y\315o\255\321\035\361\230\371\230\015\264\347y\364'\376-\240yL\036\260\265\234\265n.\217\312\261a\234\032\227\257\207a\331\354\010\263X", 64 },
+ { "\350\026px{\014~\0317\015\314\231h\336\237\217", 16, "\347A\307^u\267Qc\027\235j\323\012\315\311\031\234\2003R'\015\221\370\030\227\003)h\214\236\012Qok\210`\320\227\307\354\303\305\317\314~|\014Z\012WA\036M\322I", 7000, "\227|\356\271\333<f0\014\3640u\2273Vq\3353\2653\134JW20\020\016\023\335\243\023jm8\316Co\245\204\273\304B7H7G|\302)x\342!.\252\244\216\3137\253\365\202\256 \263", 64 },
+ { "%\020@8\324O\311\316\032\222\200J>\257\235G", 16, "\311\372\032x.?\3452L\331*\235\356\356\261sY\313\266\260\014\202\333\016\006\322j\3516\007U\377\215p\177s\267&\010\352\010A\361\315\357<z\241\213\202L\302\314\334\316\244\202\234g.\361\202\326D", 7000, "`<\340.\017\300\273\033\250>\0103k\220\311\042\250\267\022\312\030\034>\343\250\320\300\002\0123i\361\025~V\037\273\277\322\272sI\375m\375\337(\345*\220\362w\231\373\315y\230\273l3\017\226\260`", 64 },
+ { ",<\361\027\343\375\355\265O\264\234[w5\261\340", 16, "dS:\242\350\2612,\314\224}\367\361kW\335+Gj\357Q\355\251i@\326r\024K\346\034B\340\360\242\356\016\272\216m\232u\220\300\236\333\312\363\036\241(\322&\244\211=\275\376\006.\377\013\034\220\020\306\353\274\374\042[\300", 7000, "\277\364m'[]\305CMd`\345G\237\352\371\225\215\373j>V\362\241!\364l\033<\236\364O\023\276?n\231\207\3735\201r\270\305\337\177'b\031\014\251\213\217mZ\320\266O\211\364&\0304x", 64 },
+ { "\225\306\272{\341v\275 [\231\230\317\371\363Je", 16, "\343\036pA\252Q\264\242", 8000, "sJ\331\240\3206G\226\015\2441\370e\317\012u\356\372V>}\251l\1340\033U\177\215\316\317\037v\226\016\272O\275Z\341\307\370\317q\311\010\332\304\012a\375\343y\336ZT\377-2\361\203 \042\336", 64 },
+ { "\320\231-\337g\034\212\004\342\037QU:X\277\316", 16, "\216k\037\361.T\244\366\003\010E\323U\233v\240", 8000, "\021\375\214G\326\017\316\350\013*\236-\013\026S\223\310,d\036L\311\032CM\006\0352\241\232\000\342\224\025x:\215S\2146wvh\205\306-[p2\003\322M\224\353\237\263/\216\134`Z8\273_", 64 },
+ { "*/E\206\367\224\243\334\243\262r\326\360\231*H", 16, "\345\315t\032\021\207@\026\330=HF\320\204\304\330!\232Om\322\246\0260", 8000, "\200\306R\350\230V\334N\020\001Ol\242\024yN:\350\241\332\214\262\016\2629V\264(\2059\220\326b\340\303\020h\232gb\202ZCiR\275z\347+\326\267\027+A\246!\345\206k6\344\255\264*", 64 },
+ { "S\345P\373\311\016\352\202\352`)Ecl\336\361", 16, "\376\007\354p2\333q\006\237q%\037\220/,\007\264\366\006-\335\273\235\377\036\345\035:\351\042k<", 8000, "\204\256G\244\332\302{GC\274\335x\200\216\001\232\211>\263\211\360\032\031\267\236\317\257\211\273\031u?.M\022\014\220!\202\225\310_VC)L\001\323\362\014\231\341\244\350\222\022\243\361t\364\357\316G\257", 64 },
+ { "F\042a\201rU \373\374e\273\351=\364w\222", 16, "\212\030=]!}o\373VP\036\303\376/\373\322\025\222n\234\300Rl\264\010\2311(Y\275\232\3137\013i>\327\233)\207", 8000, "\233\276*\331\257\377\012\270|\212\272\2466cAh\225\024\334\2332\026\177\330'\006\210\210V\277\323\251\211\230\212\363W\2258y\224\236=9\337(]\010\3171N<k\242\015Yy|\341\352\347\207~\207", 64 },
+ { "\034\341\200H\203<\277\020\2368\307 }A\340\347", 16, "vZE-\014t\314D\300\2448\266\177\005{\025K \307\307\004W\262\005g\336\017\227i\217Z\203\351^\337MQ\314\253\311\246|O\334\377~v\042", 8000, "\364\037\206\254\004m&7\332$\036\012)\267M \241\332\245\241\262\201\032V\010\134e\323N\2445\351L=\231\316\306\233GS\234R\236,\257}-\0130(<\035\032;\212o{\312A\331\011(\350@", 64 },
+ { "p\256\034\024\365\010\220\260\312\026\350\042PO5\347", 16, "\006h(k\243\227\366\276-a\363\360\134[\034b\022Oj\264\351\2013]\271\366\307s\342\361P\306\243\323\006\020\230L\374\347vy\226?\235\300\230\255 2\362\261y\341%\256", 8000, "\332\253\037X\211\234`\315\340\253\202\275?\356\366 [\357\275up$\275\317\204\267\301\255\372Y \235\350\031\000H0\306\372\330\030!\2324\304\035]\220\033\336\014}Bj\2449\242yq\341\330\014S\343", 64 },
+ { "\343\333\355\3028:j\326\017\246J\255a\340\371\202", 16, "\217=\347\210g\256\017\252\3509\244zE\010\237<\311\271\342\3100\223\350\037\324Zf\245\356<\357,\005\323\351\261e\3544]l\313\215\341\020\355\316&O\231\255N\036\330\337!\307\370!\265\030\023\206Z", 8000, "v\245\254(/P\3105\353\335G4\277\250\243c.\260\355\343\376\021\017eK\224\007\036\331./=\134\220\234\026\337$\261Q\006\022\201\305@\277\036\267-\360\240ds9e\221\303s\3768\216\344R8", 64 },
+ { "\246\214g#qw!]\335\300\024\276\231$\245F", 16, " \331\012\010\316,\374\312\371\0232'%0\3142\016a\356\307\266\233%\270\007\034e\306\232\327R\240\231mj\262\037H]\275\2011\217\246\343O\206\237\003/\2128I\222_\373n{\377\210\220x\217\224\375\327:\317\374\001\353j", 8000, "\334\020\265\311is\344\340\002|\352\365\362\252\257\347?\001WPB(9Y7O\353\313\343\244\276\217\257\217\302v\260s>\342\016.Cjm\322Y\356\26574\207\332\355^\351f\0258\343\267w\361B", 64 },
+ { "\305?\017\233\177\271m4\354\221@\216j\376%_", 16, "_,H\223\264<\314\342", 9000, "\236\314\367\273Z6(]M\214\215U\254\264\325Gb\24793\042l66\014g\317\342a\236\317\251\254CaS\326![\210\303\323\202\032f\014M\247\034w\225a\220\303\332\024L\317M\324\022\023\206\316", 64 },
+ { "?0`\205\22034\220\217B\361\242\217+'\022", 16, "\177\315\237t\250\213S\313\337\265\337\371]l8\026", 9000, "\377n\327S\215\306\365\346\375Gj\247\245e\2004\343\202\221^_\275}\323\2345\302\007\333\337p\353\265\222\350t\227g\356\032\272G't\242r3V\276\362Lf\222\016\244Ez\200\315\247\370B|\372", 64 },
+ { "\306$\211\276,\353}W.\355\274\330\215Ay\346", 16, "\330\362\015\255\012+e\264\241.\036#\377\327\001\011\205\254i;[P\223\363", 9000, "\204\003\342\235\365\213\361\303\3248_\324\346\330RpT4\235J\0106j\263\032\2113bjHj\233\332\365oA\222\247s;\250\247\017\377U\354-f\005\277p\377\345\345&ILO05\232R\325\311", 64 },
+ { "\257\364\250\253\267\304/\330\357ch\230xu]1", 16, "\247\336\344\240\322\251G\223\222\225P\370\322\311/\204f}\253\222\252\363y\214\346\016x\262&N_\002", 9000, "\262\003\321\0362H\220\310\344\306\265\020\373\300T3\2652+\252g\275\225\224\036\357\2050B?\375E\273\232\344\024\310\344Zh\004(%@e)\264\202\2022iAZ\030\210\033r\346\211\332x\363\023\032", 64 },
+ { "\340\000\323\220e,\371U\242\254f\245\272\337\001\357", 16, "\215\306\001}9&\307\2221d\253\212q\236\3316G\005|\015\042\012\361+\005w\270\367\211\332N\026\331\037\250\014\212d\330\246", 9000, "\260\342\217\247s\321\350A\016\221\371\222W\304&<`\312c#\366\002\225\241fJ\033\211F\252\300\251Z\313\225q\202\036w\253\264\277+\361\256\235\354\300\203g\334qz\310[\022\326\212\223\314H\247Wg", 64 },
+ { "\025[\275\212\134\0013L[|\034k\356Q\321\267", 16, "#YZ\033(t\272\257\363\262\134\225y\206zF\234rB\266\221\254\367\310\042\264\270\236\026\372#v\354\213\265\372\221\304-i\020\042\0107\344\253\3678", 9000, "E\267\177\372_&\305\201\215\351\335\007irx\302U\037\252\012q4\201U\356%\366m\245\202\373bqh\244;\271z\372l\2516u\267\004\013\246z\274F\021\312E\177\2159|\037\033C\340\351\332\353", 64 },
+ { "\312\016\206\247\361E\252\245\001\033\031\234\227\246L\332", 16, "^S8\353\353\0307\031}\012\247}o\352@\027\217\200s\220\206c\320`D\270\232\275#\361\223\210ur\361%\003\034\3607\255E\016\271p\336\274\260Q\027\0136r\246\025\263", 9000, "\202d\317\362\315$\311J\377+\037\244V\342.\336\363Mx^\347\202\206Vm:\363[\3353\324\316\3403\012\357\351\364\221TN\340\275ALC\033mC\224\225\274\000{n>i\327\006\221p\360:\020", 64 },
+ { "I-\350\031\2302\352\226OO\234*?me\030", 16, "\004'\254\034\206\216\214\277\213/\235\237\007\312K\347\034\361?\376\327C64w\313\300\004Z\226\207\303\363\0066\377\375=\341\371\207Aq\247V\237\024T\246\200X\342]\005\356\337\273\023tq\226\032\021\202", 9000, "\367\317\013/\030\210_\377\013k\366C\234s\237\011\212\330!.\016\263@\353\017\007\034\345B\212s\204\371q\336;\334\032\313E\324>\326e\344\304v\205\004\342\000\343v\025\346ZO\2243Y\264\366Z{", 64 },
+ { "\237\234O\246\003\264m\356\326\201\266\313\345\227j\324", 16, "\027\016\205\250\331\027\275\260Q\032'\204\373j<\233N\251v\234\346T\250\270n\017\244\225\275\340\354\346LJ\376\2779\2311\311\312P\335\036\323\202h\255\254\376\261\350kO\3324\277\266\223\207Q\263\373\351&\0030\247\330lL9", 9000, "r\036\301q\371B\034\017/H\010>\242\326\247F\353\014\333:)9\353\261c\003\177\223\3277\250Sm\263\306\316\261\341\321\321\134\232b\312\235\014@\336l\330\3356\001nw>\363\015\210O!*{Z", 64 },
+ { "\227\346\344\330,\020\250\376\033v\253F\236\207\374\245C\230\255&\277\300\310M", 24, "\333\256\034\031\307s\315'", 100, "\267@0\237\025s\217\307\3646\231O\246za\000\340\233Q\254$\277/=)M\250\213i\245\275t\333\322U\256S\025\030<\200fFkO\2528Y\272\243:\033\315\304\313\237\017\356\255\274\357m\323\215", 64 },
+ { "\3410\3103\201\025\037\366'2\340N\277\256q\345Gc\020\036kH\373w", 24, "\302\330?\225\200d\3510\335\346\211\317\324\365\366*", 100, "FN\014\374\031a\240\212\202H\314\016\371|{\025\267\243\353r\257\365{35\134q\252\205\357]\317,Y\350G\206\365Tuf9\356\267\365S\371\332+\245f\332\213\371\320\215\215\300\242\243\216F\361-", 64 },
+ { "\265\001\263W\336\011\274\204=\356p6\210\000\216k\216k\203\365>W\275k", 24, "\270\342\331\242\013|j\306\324$X\035R\365\326\242\307\036Z\321\303\357hi", 100, "\005\301\361qhJ\263\253\2069O\003\242E\333Z\014\003\274\336f\322De(\376\024T\336\250\2111T\270\320>\366\266\311P\177\234\264(\015Vd\260+ De[\0301}gzZ/\263\263ma", 64 },
+ { "C\347\306\033\005#\201X+OL\177\310\360:\207'L\316B\325\264\324\264", 24, "\021\357\353jzG\377\235V\012u\262'\256\354:\007\343A-'{Z\204\204\226\321GbTP\270", 100, "\200GB\211ui\244\352\343Sj\207|\250\213\035\010[\335\312\345k)&a\035\363\213\371\217\212\317\364\031{]\355\315E\222@n\026\375k\357\004\3505\025\226w<\134\226\324\326\254\345$\322l\302\247", 64 },
+ { "\236\030\042\231=f\332wq\260\342\364o\372S\374t^\343\027\374\006\002\024", 24, "\344\273\215\371\325wt\273n\225\221\020\263\214~\226\266\237\226\323w\026\313\327\377\201\022\226\356.7`C\362jR\217,\362.", 100, "\235\361\025\375f\302\004\271\274\003\354w\027\001f\034\246\323HME~<%\303I\037 \266\042\335PY\306\134<]\364\377\026\254d^a\327\275\012\134\223\027\265\277\177\035W\3347,d\362\250`\006\342", 64 },
+ { "\035x\237%7I1\271N\370\236\340\214p\331\367@\355o\134\256\032\355\243", 24, "3w\231\014p'd\220\317\366[Kz\241\271\010\200u\225.E\277\313q\262\341\313\031\271\205\016\024\3616\021\345\030\254\367\314\312] \026\211\273fs", 100, "-\300\221\205rc1\013\374\370_xC\315\013Y\21477\320\202m(\345(%:\260\232\304\004\263\303\275\304\317D\376\245\331K\310\027\343\322\221q\200.Kp\206\311\231\306!a\240\203\324^)%\026", 64 },
+ { "\023\366\263t\006\015l\264\316Gq\271\3468j\220\271\232\361\223tW\224\020", 24, "\365\367`yT7jO\360\247/;-K\241\031\227\034\211\251[\236S\015\035P\304\256\342E!q\205WJ\207\373\016\006\355`\235\367[~\300\264'\232\200u\241\234\210io", 100, "\271\034\330\262\216K\312\224\375\206\243\341{\372}\232\210R2H\032n\264\377J\021*\263\022\240\277i\242\356GS\302\243b\230@\034<\261\276\025\016\340EIt$;\347\221\024W\311\275R\254\272A\037", 64 },
+ { "\373Oc{\307\313=\3414>\340\016\245\217jar]\035\307\252\010\031\025", 24, "kk\262\256\340\255Z\310\235\020\320_\032\345\260\243\352\202\321\301\246s\300)\3314FJ\267\361b\237\034*BrjY=z\336\300\344\370\371\035\227\334[5*\325\3152\233Z\022\302\3733Wr\012\005", 100, "\246\372\3420\204\346\034\314$wZT/\240\376d\252\354?\331y\225\037\252\374Ah.\304\3737\242\013K\351\035tH(\255\353\240\214\304\350\245s\273\326\265\340\300\253,\022/\311\223\024\3369\002{V", 64 },
+ { "\201\012\000\332_\206`\206GD\336\346Gm\227j>\301Y\333\2658\354\001", 24, "\361\321\374qZ\346\016\307Xm(a\256\301\002\245\352\216\244/ca\247,\315V\017+H\004\355wmR\225Upz\373\217\333\206\337>\257\313\275\307\260\342PS\013\260\356\377\322\267e\027\250\030\262B\012\346Q\260\025C\311\317", 100, "\254\012\230\016\257A\017\210\264Z&\234\320\375BRb?\266\012\2509\222\355UgM\322Y^\321b\225\243\353\374\374\224\2545$q\030\030\370\332M\302\350\014F\023p\033\201\316\004\000\270b\016!)$", 64 },
+ { "n( \210$O\004\366\207T\3158\361!\212\3606\317<\330G\250\260\272", 24, "\204+\330\321\244\030h\275", 200, "\353Ko\207&\357t\224\036\266ob\222)Cz\360#p\020E\357k\320\370\007R\375\311'\347\007\364p\340\341\275\220\341\260\032\316]-Rthu\206\202\255QY\251.\214\245\330\007\312\354@\375\204", 64 },
+ { "'\273`n\012l\345{r4\347\264\223\376LW\311\363P_h\302\001C", 24, "\324\200pH\263\220\2615\305\341\371Gp:\323\322", 200, "\205-\343\243<\333\375na\337\314\271J\354S\300D7\011J\364\023?\2222\360\267\221\010Fr\256\366{\366\235\255\331K 1\370\307V\373\020o\363\367\036\226\004\246G\331o\321%\314\220\254\234\317\033", 64 },
+ { "\345/\334G\221\360\212(\211i\247\012\215\223h(P\366\215\371\300w\336\274", 24, "o\341\236\363w\371x\217Y)\013\331\006\034\222\007\221oA\022\037\332\252-", 200, "os\235_H\326N\300\244\310\361o0\202j4\334YO \345#:/\227\042=$@\363h\212\365\376\277\34220\301\371\373\250\327\201\374\012!)_\001\346Y\2061\220\302e\323\004E\364\025\334Q", 64 },
+ { "|fm*#\220r\246\343f\364\364\241\370P\313\315}\216x\303,\335w", 24, "\342\177\034\326\226\223Oo\035\214\227\361\305\316\314\307\323\223\353\271\303_\213^\337\017\204C\316\037t\345", 200, "\2571!\373\364\361]\225\332K\267\042\317\314\030\2360.\257\037H\215E\276I,\356\033\13411\267\335>;\226\254g\2532\024\323t\024\237\275\014\021\267\251\004\004\331o\305\036\256\221\234\242\210[\273\271", 64 },
+ { "\315\3742x\230\331D\375G\256R\363g\316 \024Q\034\317C\271X\240K", 24, "<3~d\026V9\2178\224\202\324U\335\006%\270=\225^\001l\212\212UC\277\270\233r\211lR\300\275\223\275\300\346J", 200, "`[\366\233Zk==\042\033\211\322-r\354e6D\210\302\275\247\274\374\0266\203d\234\315\331\244\2671\226\014\336\243\350\216^\206\255\242\023ueK\017%CN\002t\300N\244\272?/s\243\200\327", 64 },
+ { "v\313\001I\364\011(l\005=7\330\0012\000C@w\317\206\230\306\363\000", 24, " \300\300\271\3443iMC\254\301*%S\325\211\212Rw\350\3027N\233\237\334\3134\204\332\031HS\374\3749\214 \3503FN\367\002\030\312\351D", 200, "S\007a\011\031\231\366\371g\003@\3407\024\016\376yl\373M ksH\327=6\210Y\225Q\325(\2348\334\362E/w#\375\354qY5\324\356F\212\232:\033\231\306\235\302\321\330y\226\005\260X", 64 },
+ { "\335\002\277\030F@\273\340E\000\300\304\372\322n\230\262\354\032\250/*\351\230", 24, "\270\207\0010\377\337\347\375\213\331q\315\033\371\253\012n\275\262D\267\226G\367\371P\213*xA!\364!J\342\2113\242\321:\205\203hF\015\315D\026\313\353[`\213`\374j", 200, "3\301\365]\214s%u\230\234\215e\331<.\347<\320T/\254\351\243huF\003dA\341\342\372\206\243\005\277\257\033\364\042[\200Y\326\017@\015\271R\225\035\330z\37523*\313\3347\027\226\325\355", 64 },
+ { "C\325RQ/\246$:h\177\362\331\223\261\312\006\237%\326zY\375\033\341", 24, "\002d\2740-\177h\221\356\310\211B\036K/H\335\336X\326\324\3304\251D\365\377\234\220\375\325,`\003O?\340\276=1\246+\231\336\233\211\272\265\257K\036\316\333\204\223g\035\336s\347h\230\270f", 200, "[\037\344w\257\272\235\230\032\270\263xh\361\014\273\350:\273\201Z[\314W\254\224\222\364;z\303\372D:\022a\366\266\364U\3467\314\337\357\246su\014FO\240\030:\325\362ZA\363`\362\005q!", 64 },
+ { "=e\0029fU\375,?\262\327\273+f\013\263\201\021\027N\026y\341\340", 24, "\312\204\344\332=f\0336\341\017\264#j\216\206\236k\022Z`B\242\355'Y\016\267h?F\270\213B0\245;\367f\363\246J\012\200\330y\230\200}\347$\243\343\216\032\037\006\271\214\322X\255\256\032\276\021%\263aM\264\252G", 200, "H)\352\343\007'}5\333\013:e\354\225zG\271\032\340~\262\351\261\177\004\227~\333I\363\301u\210S\252\231\340+R\271\336\317\027\354\351\364<\276\220\042a\373\320en\370\001\301\305\356\344\244\236,", 64 },
+ { "\376\374`\300n\003\362\372\017\2253-\307\232\264a\351F\247\002\011\270\336\311", 24, "\023\265B\220\374\244\304\323", 300, "{\346'N\227\254\343\334b\323\220\316&\335\375\227\013\314K\214~E\374$n.Mi\337\223\326L\361\342\237\242\257D\207'#\237\373.\2729\363\005\003O\216\011\005$\261O\214\343\275(X\336]\317", 64 },
+ { "\177\337\370\037J\313\252R\367\027-\303\274\322?(]\370\017_?\010\360a", 24, "~hCg\251\003\302}\372\2644,v\264q\251", 300, "\003\253\302X\215\255\034\206\337\272\327Z}\206\000\333\311\026\377T\336\347\315\341>\210gsS*\276@3(T\340\263#\325\327\335\027~\310\201;C\2723?\042\341R\346\247\273\355\344\002\300\341\314\245\345", 64 },
+ { "mD\363\333\332\276\206\023\340y\331\010\326\322\331vHX\265:\277\042\350_", 24, "w\011\263\376{\016R3\025\013<\033\261\320\024\017\356)\371\235c\021\353\262", 300, "\304\010\302K\273#u\237\227\3135^\134\2743z\326F\265=}0#m\240@\303\020\356\373\042Xl\223^)\242\031\374\215\031T\316\020\203\255\273$\024\034h1\370\270;\276\277P\250k\252\352\232\371", 64 },
+ { "Z\2556U\206\026\34258E\247BT\276%L\367\0119r\032\233L\272", 24, "8@\034\305j[D\015A\317!\227jgy\313D\315ZIu\367\266{@]\335\311}\351dC", 300, "\022\23418\340\022\235\005\024\016\010m\217\370\271\262\307z\325\003\237:a\216\255w\023y=#fx\323k\346\215\331\275\206YGw_\261\353\007<e\201\376\200L\030Af\352T\314\310\010I\266\333>", 64 },
+ { "\247\005\202\025\315\206zP\360\241\134\036C<-\006\327\363\201q\251\002`\267", 24, "\271]\336\352\244%\215\020\330\3458\377\225\350\302\237\253\033\202\367\265\373\331\261o\250\031Q\374q\217\254\233o\230\375\236\327\333f", 300, " ~\303\251\370\211U\337,\237Ln}E\320.k\245\246i\234\202r\314\324MU\365\377v\3177\266nXT\002%\346<;\350\3354\262\330\275\303W\242\014\024UN\274Hd\210 W\244\222Fv", 64 },
+ { "%\312/\275\212\305\002\376\340\362\311b\224\3203\012\317R\370]\336\204\260>", 24, "\343';\334\363\247E\332\354\203kj\3654>+_E\310\353~xO\202n\234\217\227\360\371\261\343\314E\331-\210\203\032]\177;)$~\333\367\313", 300, "\2553\037\301\022E\356\010\274\241\216\332\333\037!9X\374z\314\306T\345\274'Z\256\375\036\355HL\337Dw\276\235D\225\213\304\214\350\250\031\316\200B\317\320:\0253\233\233\025n\264\016Y\271\242\035\316", 64 },
+ { "\273o\033\015`\0168\005\343\233[\262$q\314\315\246\011\036(\233\203\211'", 24, "Qsa\3029\213*\321F\261}\270\357\330\030\200\335\375'\261cT$\2101\303\035\253\024\267\240\246\377&\316\357\331\221\361nl\0327B\015[\332h\333\376\351\0258\232\203\321", 300, "\260<4h\346\217Q\237=\376N\213\261R\257\374@!&\300\311\027\263\346'\230\377\224\134\225\237\002vQu\006\033y\036D\357T\362\316\311.\321\271\207r8\304\035T\226\233\326\345,\327\361W\217\364", 64 },
+ { "-\207,\240\261txU\001\360Q\213R3\332\236\037_7\016H\261d,", 24, ",U^\351\036\003\300?0&+'`\363\252j\202\270V\375\007\355DR^\032\011\237}B\304\031\267.[\266J\273]\325y\335\327\2555\201m\233\254\352A\315\214\337\022Q\270\330\367?\363A\021f", 300, "\220\326\330u\2268\305\223\255\2359\332\203\273F\203I~\012\232g\001\355jb\224\212\226\032\277\036z\326\0361\265\025[N\226\261$f\216\311\236X:.\366\036_\020:\247\004d\276_\276\237`\323=", 64 },
+ { "\276\035\274eI\033\356\262M3\021I\315\006j\241T\025\216\342\246\223\035}", 24, "1\316\345\252\312\377\027_\302v+.\276[\002j\006\266\233\037\265UR\023\255\244o\263\333\276+\264s\336\304f'\250\377\003\300\006\320\006B\375\205\300\332\341p\241\223\254\024j\274\231\024:\003\276\233\232\006\011\201\220\356\257\257\361", 300, "\271\353O+ C\226\325Qu\362\201Cb\304)\014\367\001\376\243\027\360U\273\356Y\235\234\357\243\002a\210\324\017`\304F\317;\022\014\021Y\375?\356\343\253\306\371\006\330fdD\333O6\042\203\214\354", 64 },
+ { "\342$\336\336[\306\001\033/\216\277\201\367\014\257\362\331\320\374\036\034\361\371N", 24, "\220F\317Y\0232\2022", 400, "\351\321\325*\035\023\026\377\273d.\016s\225\276\321.\330u\014V+\331Z\223\365\034\371\377\236^\203\213\240\375\220/\313P\024WS\331\034\251\347\220ux\231[$\334:x\014|\344\010\233U)\276\344", 64 },
+ { "D\255;\367\374\305_\373\372\020$\201\275\3735p`S\330\333\246\271\316)", 24, "\300M\273:\024\362\017\002\247j\376#8\321\260\346", 400, "rW\235\326[\326(\001\223\221\340\316\006z Z$\217\321\372\3722\277\024\334\025I\252\374\134\232\342!\2111\351\323}^\324\217L\032\350n1\372>\311\001\323\3239\321\345i\262\323\013\014\262j \336", 64 },
+ { "\200q\243(\134z\361\042cG\335'v\231\025X:\016\316\0170\342hH", 24, "!8\347\001?\220\313\251\3042\306\201\004l\012\356.\177\367~e\305\3269", 400, "\371\230\253b7m\177\266\325\344\350\375>!<gIL\313I\276\357\327\375\031\016\227v\272\216fO\250\226\3245T4\1774[\221\246\205\306rU\245\010:\314?k7&\337\337\010\274\003\031\305\210{", 64 },
+ { "\376mL\306W\276\307I\220\0377\307~=\224(\177_i\025\016KW\021", 24, "uM\345\215\317o9o\020\220@\034\0078_\301\376\251\001\244\271\023rt\357\237U|\277I\370F", 400, "\222C\346\307\233\311\251)5%\273}j\207\312\356a\254-\207\276V\010=v:L\210\372$\257lxO\331X\265\320a\177Y\005W\210n?\366aI\032DX8\010\372\231\360\222\241\260\301\353\342\042", 64 },
+ { "\365\016\366\0250 \037\313M\265\367\354\327Q\021\011\276c\0155\026\340\037)", 24, "\255^@\2710\375\217|\031\241\022\250#\347\217\201/\031\377.M\274$\206`D3\274F\213\253q\250|\302\203\221\300\344\301", 400, "\330\000\3218Hl\224\367\226\324B;N\337'\223.\2572\023\372N\277\310\210'HY\321L\206x\331\177\301s\373\226Eh@\360+\234bXu\327\037N \011\037aR_+\343\350A\020-_\222", 64 },
+ { "\031\367P\265S\025\224\354\273G\203\211L\3560\234:\134\026\315\250\304?\217", 24, "\322[\307,\212(e\3775=\2660\252\346(bQ\212\307Fs\234L;\177I\010\313\272\227\256\042WpF\362V\024\357\225F\355M\201C\340x\211", 400, "y\001\326\022\223\014',D\302F\247nk\3156?\342\230\014\003|\331\024I\216!\237\366}\237\351fMhM\035\015\225\362gk\246Z\334s\020\221'\035\027\002fU\234\324q;\260\240\350\245\220\000", 64 },
+ { "\361\232\331\355\367{X\357\033\346K \204\003\215\215\322\305re\241\245\342s", 24, "_\323\217h\017`\236\2053\017$mx\027{\241j\367\333\233\262\234d\042i=\006\222\210\334\313\253k\276\206\350\031&\2064\2421.~\306\261\015\042U\331vE\237\034\206u", 400, "[\273{\337%\242\275P\227\273\352x\267#\012\370`\305\372\261\270v\202\317\024c\372\274$\262\016h\3420\250\244\223\210\3579\335\230_\252\177Y7\276\371\313x\307\304\341n\201k\312\361\3505=\230(", 64 },
+ { "\042\230l\031lO\001\333k\036\327]\342:\314x\265\035\2663>D\246b", 24, "\314\354\361@\255\027O\232\037\322w\032\316\356i\023\333\331\342n-\301Q\002\244\2328z\026\304$\372\366\260cc\310_S\236\241\237\304\212[\022'\365\226e\016_X\303\323\005'\374\010}O\336\242\321", 400, "\273\265\023\023\343\261\201\026t\300\270#E\336\012I8\300\271\011\016`\234\326\2417\015\311t\012\322\341\355T\335\254\217L\243\253\211\364\234d\222-\344\203\027o\342y1\305\266\261$\316\376\364\220\317I\267", 64 },
+ { "\275\272\371\314\250`\370\331\324d(\227\364e\203!\014\322m\254kH\012\354", 24, "\2617\007\347\243\255u9\320\226\035d\341\311\344L\272\352\023\256\361\332\364\2700\014\247\331s]\234\223\223\210/\273V\364\335\015\032\267\357_\001q\310\367\260E\374!E\335\200\337\316sQ\205\023\240M\355\267\250\020{\207\254\231i", 400, "R\300\302\270\242\312\331\300\376P\201\353\0128\370\241o\233\316\260\030\222\303\322)\376\017>\007\011\005\222JPb\361\233\340)\036\352\242\035*>\373R\020\203\355\340\000\0212K8\311\240\266\134\347\351\216\326", 64 },
+ { "\330\2011\262\366s\232\025\317~\260\312\237+J\202\020t?\134l\207\310\304", 24, "cE\245\266X\315\3545", 500, "\315\247\030\277\337=T\214\254\331&\364\374\030\246\000]\220\345\274\373k\243O\0234\006\210\2606\031\360y\205\226\221\212I\341\006\216M\206K\211\342\314s\343\327\316\353A]}z@b\0053\202\212R[", 64 },
+ { "\250+>b\307\310\001\233\331\261o\360>\206\2733Dp\340y\017\372V\023", 24, "E\241\134\345\365\327\016\336D,\217\032\022E\233\262", 500, "O5\263\352\317g89k\247\351\020/\037\3033\370T7\304\256o\216\337\227\321\225\254hH\211G\316\224j^c\271?\340\036\003\177\001\205Y\303a3\376f \021\211\344q\362\255Q\341\264\244u\321", 64 },
+ { "\2153$\257\243\042\257\230\3443\276Y\275\000\354^\363\317\342#\343\272~?", 24, ")\246LD\006}w(\243\307w\260\336\241\024w{O\237/\236\004\230\016", 500, "\304\373j\036\0062\302._\327q\002\302 \023~\367\007.TC,\206\177DX\275Y]\224\366\232\212\001U\207z\305\233\317\320\203\3543\353s\374\012\272q`]\333W\305\250E\336VG\223\343I\257", 64 },
+ { "\224\302\305\230\315\313\231\243pxO_\014\374\342\256\237\231\356\330\215:\260S", 24, "\310D\2217\341\363\362\243&@$\221\340\320\266\213\236`LOy:'\300\244\314\212\302^Q\373\376", 500, "i\311\241\337\357s\333\356P+\023'\330\310\273\205D\354\233\343\310\306;\037\213N6\230\006\273\261H\272X\243\367\341t\234\035\263\2034{\203p\034\371gY\265^_\010\267\321~\272\34364\227\322\027", 64 },
+ { "m#\277k\323P\307\256\351\316\020\370d\202\377\023P~l\024\201p\033\015", 24, "\366g\225\335\215\223\322\241\007\333\007Y\275\343\330\267\346\003\006\306q\332\317\355\355\377\373}\303i\3322\262\322\221\226\217\305\027\354", 500, "\351\011\202M\306\035&\257KE\357\361\3644\243^\272\346\024\375\267gK4\010y\013\021\361o\253\025\2719\267yk\374\261 [\010\036\360yP\311\351v\0252\236\270i\217\356\022\006\214\224k@L\343", 64 },
+ { "\264\276S\014\276;\012]s|\330\273\223+\222\230\004j-\035u\260\336\364", 24, "\177\010jO\370\237\274\370J\333u\240\013\255\374\3621\342\013\240H\374l\372\225\016\266Vy\315\371\030*\024\231\302pLl\234\336\255\271T\001V\254\263", 500, "\225$\317\031\226?\314Cy\201\035-<S\2020g\215\037\202\327\001a=\372\225-\211\275\323K\262\271\244\243\317\244~\301\367\364T\260\204YC\030\304L>@<\030\252\205\014\245\231\277\271\013\317\375\260", 64 },
+ { ".\373qW\226\272\303\354\235\374\302\320h\345\357\034\014\250\344\266\244\325\244\270", 24, "\240/%\223\314\333\316\363\361\034q\245\312\134]\254\263\267\255f\225\217\313\313!ax\035\273\264\272_\027\037\323c\253\271\372\246\013\361\305\364\014\016@-\310\361\233Tc(\015\014", 500, "\010\250\337\244J\255\022/\001\035M\334\2426i\327\255\012i\333\217Y4\250uG\007D\225:\276\344|\027(\311\302\307\321!!A\003\343y\202\247\267\253R\032\312Y\243\134\007\252\321\236\251\364\307\342\366", 64 },
+ { "3\244\330\322\257\350\201\244$\032V\270\032\236T:YU\211U\265\327\262\355", 24, "\227o]\354 ;iq\224\345\203Z\231L9+\374\316\265\226\226{\375\377\204\007\223\274\273RH\244\134\243\224A\337W-k\362\372\317'\207\023\272\361\347\015\3371\344\334\226\277\215\277\237\351O\307\362}", 500, "I^J\373\327{\005\200L\373\304\022M\022\250J\253Iu`\345;/\023\325\362\001\032\367{B\017\366_\034a\257x\000\025}tA\347\332]#_\260\023\211{\301\310/@.&\272-\005\323\262\355", 64 },
+ { "\343~<_Kd\253{\262\357W95\256\337n\325\336\357\257\260-\227\011", 24, "\251\014\025\254\015\257!\326\256@1\225Ay\027\311\313\204'#\230\306\254\036\324p\221e>\230{\373ZP\236\025\025\3610\001\313\002\275\315\204\263\332\315\342\003-\326\356\216[+\270g\255p%\224O\306\365\241H\262\001q\231\346", 500, "\307\204\357\347\320l\0328\015\373G\336\271\322h\326\034\245\301,G\215Jf\031\177\355fhtW\034g\266\316V\034\222\042\346U\253Ac\036\304g$1\004\244\245VP=41\313\200\272FJe\374", 64 },
+ { ".\246\322\250\363\276\370\266\342\207\226\210p\366P\025FM\371\232\203*\036\303", 24, "m\221\370\307\016\020\374\317", 600, "\252D\266\320\035\2649\007=\003\326\355p\332;\021\277\360\200\255\033\313w\366}\201a\325\377A\033\365\321\320S\357G\241e\344\375)\0303E;\256\017 \031'\223\204\2630\027\253\225\354\305T\366\360\005", 64 },
+ { "\277\010D\025\241\325\032%\235\032y2<s\011<Y\377\225\352\244\330\000L", 24, "\370i\352\334*\266\264\012\3658l\372*\306\372\357", 600, "\364\254\356\215\306\033\275\312B\255\314\214\267F\307-\025-tv\3412\251\361W\370t\240\316S\302 Iv\224\246\004\375nyqJ1fS\204\016\000\272\302W\244+\377a(\2510\003l\366\002\202\374", 64 },
+ { "\364@\374/\361\272\034(p\366L\357\303\342\237\265q\200=\335m\346HZ", 24, "\311\2429I\020y\324\235$\345\013\260\300\323\230K\317\250O\017#\364\264\303", 600, "\324;$\316\314\300! L\200\337\003\301\276\346\032\204\212T\346x\027tn\206\252A\316\001\243\177\362j\033Xj,\214\301\025\024\004\017\315V}I\355]\314\250K\276@\027L\362e\2667\373\215\324\344", 64 },
+ { "8\343\210K\202+\330o&\213\367s\237\275\202\330k\262\020\217\272\254u\247", 24, "\347U'\014#\363\233\207\301\241\317*gUE\230\346\313\362J\037E\0357\226\273\037\225\226\265\026\321", 600, "K\213\220\325y=\007\255\361\203\376\376\354\303v\215+\244\360\272m\020\273\212BV\300\261\334\250\214N\304\333\034r)\3515\357\025\377\334\231[\351\013JE\222\332\322\250>A;H\020\214\333\306\356j$", 64 },
+ { "\002\261>m$`\016\02494\263\307\353\263\252r\006\356x\223\215\321\236\037", 24, ".\222M\266\201\371\370\375\214\134\223)\242\011\371\225\365Y\203*\365\275\252\037\262\315\024;\032\256SuK\275hn\231\233HN", 600, "q\223\223]O\3412\371D\304\246Z\277P\267\307\247\327\213\036$\223\234Q\021e\332u\004\240\254\316_\012\036!\230\213\233~=\233Ay/\313\010\204\207\220\312j*\016\324\347\042=\017\217Qn4\302", 64 },
+ { "\216i\036Z\247\365\202\373\255\243\035o\301\221\013\3434*r\347\214\002\342T", 24, "F-\311\364o3?M\260\205\340:)\302.>#\203O\340\245@6\375\356\313X\267\302c0\014\325\261{\013\267\253\277\027W\254\004a\353\350\255b", 600, "Uq\242\257\354/\372\365\027\376\020\267#S\277\237\300\314t\021\234\370\034\320\206-\357T\272\370\204r\002ZAsX\356j}\012\2259\247\231\211<6-\372\027V8ZM]\200\2344\227\276q\033\322", 64 },
+ { "g,R\261\255\355\242\315\344\302\207\312\216\326$&\246\352\351\004\230\372\357\342", 24, "\011\237\227\033\026b\224\320Z\340\2019r\220\367\304m\256%&&\3424zt!CY F\014\347\345v\255\230Se\372\021\242\203YH\277\262\213\301\327\267\314\036\205\0020$", 600, "\020*\217~@\030\030z\241\300Cf\230\362\037\343r\203\344A\304Z\223\2310\331X\377\365k\237\245'l\223\024\012`\345*\324?\310L\344!\270\340>\001 aL\220\001\221KD\235n9\333\362\227", 64 },
+ { "-^[\305D\215\276\210\007\255\365\315\023\340\343\011\212_\340?\215(\221|", 24, "'(\221\177\261K\003\247;*\201=\251?\240\345\026U\225\252\355lg\342\310\021-\006\004\270-\256\340&\007e\304\042;]K\306b ;h\222d\353\277\030\332}\355\030\001\347o\375a:#\207\232", 600, "\213{K\321\232\301\253\312\037\324\340\2454\262\376J\335\210J\235\252\220\370-\004\042\134C\316\025\275Te\250\333\212\227\324\035\006\244c\273\004Mq2\276\373\005w'\212f\266u\010\320(\361\332\217W\000", 64 },
+ { "E\223\025>\255\312\337\310X\234\377\032\243\275\366\347~QmZ\027\013\257l", 24, "\202\021:\372K\001\252\250\363`\260Lk\255\042^|m\312\370\200,Gv'\255\024\022<\357\346\002\016b\252\315,\303\374\247\326\324\352\254\322\250L\306;\315\311\374\264\042z\354\256\304\021BD\231%\236s\321n\236\251\022\006\262", 600, "T\024\252$\261\307\0362\033@Y92dA\366\341\204\33236\265\357\205\232\266\027oI\245J\272\233O\215\317\302\246\374Ko\204\270z\356\236\042\315\360\312l\231{\274\026\225\221\345\377\343MG\321\320", 64 },
+ { "ew\357\210D\321\347\2421\315\323\264_\036\253\332^\253\325\271I\211\212\250", 24, "h5\202`\361F\354\312", 700, "o\325\241\254!\217\211\331~e\346{\211Z5\371+\353\334\134\034\207\213\243\270I\334\317S\312X\344\326y\217f\261\021\243\223\251\015h\022C\303\026\036\222E,\203\366G\362Sy`\3241\3205\262\265", 64 },
+ { "\275\247\376Y\223o+\2440\255\024e\351\324_\203\206\326t\333\134\332J7", 24, "trXoC\241\333\350\3415x\210\356\307]\243", 700, "gyBJl\234\206\356:\023K\310gd\312\200\346\320\2479V\272\340\000\024J\316\362PI\271\263\202\022Yhs\302L1\234\037\267\200\312\3641\0171\265\217\215\205S6\243\222\300d5x9\306\275", 64 },
+ { "\001\024\003\337\276\355UDd\202\271\302\262m\021\205b\021\234\3108\220\226\343", 24, "<\015&\212c@\264\015n4z\261\252>\355\025\231\363C\224\311n\313-", 700, "g\2301\253WQM\014\240kG#\037t\014gaF\007\234\317:\261|C=\344+\252)~*Pr_\037\376\013y,\377\376\344\2419\266J\227\260\2123\225s\344s\224(\204\024\344@\001c\255", 64 },
+ { ">\030*\353\341\240\203\312r\031t\260\265\012\345\331\354\016\014}\306SI\351", 24, "Z\020\013K\254\006\0164\347J\264)O0^a\025T_(\243\025p\261\210j\203\260\233\026\263\360", 700, "\223\3078V\010{G\373\301\030Ea\340\335\315\251\237\257W&B@\022+yHu\2165Y\377p2\344\003\340\341\326f\134\262\267\253\037\300d\312\030t\030\324\211\032Y\255\257T\237K\332\220\310\307P", 64 },
+ { "\374\310\024\302\321W\213\250C\252\275\336\024\341i''\022\373xg\007\247x", 24, "\3404>\007'\303\215\331\2719\357\362\010\2146Nf,\272\304Hd\226%\251\2659\252\336\224@\355\357W\024\322\265\341\355\226", 700, "\273\207\247\374n\350\247\030\2469m\313-'\204\200h\022\356\320\341\302\001%ON\343\244\316\227W\212\3252\317\234\366\275,\033\261}\325\350\341\207\203\306y\010\034{%z]r\350\206\201{\017\330|\031", 64 },
+ { "\240+\274h}\2040K(f\267\320\015\265CC\337\226\200\335\032E2[", 24, "\240\266\274n\2077\260C\005\223\036\004\011]\322q^*3\276\235\226\320\027=\344$M^\227N\370\022\234\313\007Wk\366.\205\016\365\350\200\020\217E", 700, "\212\232\234:U\323\302L(\362\221\016[\270\266\001v3\253\225\340t{\016\226|EC2Jx\377\264hn6\331\266\014W\245\233\277m\020\353\245\023\221\016H\000|c\307\362P\027)5\323\0230\344", 64 },
+ { "\030\307\267{\345\270MM\201\325\330:O\224f\313\005\311\325\310\210t<C", 24, "\240\201]i\274A\034i\352:}\134\024\367'\300T8\256\217\377cm\027H\3617\250\210\276\013\364\031E\240}\252\004\034\222[\330\373\013\021\003\025\036%\011@\362\030\232\342\250", 700, "\374:\213\373\257\356B\374\274\021+\352i\022I\206\346\205[A\376\275\322?\245\212s\030\262,\301\015*\370\350\326\024\227Bdv\362[\177!\016\261\276L\322\226\265\0337\016\207\325\321\260`J\277\363\270", 64 },
+ { "\325\272g\264\270iVI\230\366IkD\347sy~\232\272\200\347\337\337u", 24, "\134\247r\3219\313\346\200\227]\377\235\304d\214\310u\317\014\243\261\345\267O\256\010\013D\035K\245\226\375\217\3611\001\013\307\220k:T\025\226\304*\225&\255e:\207\246\375\317\224\304\251\275Z\206;q", 700, "\262\323\010\0153-\007\321\273]\337\032\205\230;c\210\310\343\034D\214\003\234\003\355\274j6.\326s\037\315\216\237\000\371\350\213\347\365\351\340\010\356\260\366\201\227\340\3711\262\3773j\306\042]\273\211\332\032", 64 },
+ { "\357\334>Ja\022\002\010\374\335S/\247\253\270\010-\205f\257?\330\234\032", 24, "\335I!\203\271D\364\336OF\220\225]! \362\013%Bsp\326\341\343\360K\267\267\367L\354\370@\371\035\227\225\360Z\205mx\276\371F\247P\344.\223\264\221x}\322\225!\346\350\256\2742\253\265C\367\377\022\036;\017P", 700, "(\036gv\030n\0139/\250P\272\313\230\351\226\211\363\237\312Y\037\272\311\265=\351\343\305\374_\246\235A\264YD\304\334\202K>\037\274\336)\301%\366\242\246D\324\134A\303\2414\276*\031Y\376\357", 64 },
+ { "#\0427\244K\257\006l\005*\256m~8+\317\373\325x`\224.\320\221", 24, "\261f\203\226F\362\245\334", 800, "\267c\357A}cz\243\206\233+\230\256=\203\034H\007[\32169\375\366\322J=hG\336o60\336{\244\037\2775\326\243\213\320)\337\003\370\323Q\232\242A\306ZRT\016\242\242\350\372\015\220\260", 64 },
+ { "\347\377\324\375\314\303n\375%,\010\336{\375\3320\227\344\2662\0024\325\365", 24, "\361\311\366\227\314\275\335}\213\1773o\371\231\357K", 800, "\273\267\010\210e`\307P\264k\012\345K\247\362\367\033\310=\000\225\035V\220\312\213X\314\341\273|9\325\275\304\241m\012EI\271\302\2747\331A~\177\032#\233\376\353]N\301\365^Mh\272.+\260", 64 },
+ { "\357\2528\231\262a\032ZJm\3157\260\331<[\232\27441\372\310\267\036", 24, "l\001\255\324\355\0220\246E\206G-~-\333\004\331Y\026\363\303R\010\324", 800, "\246\354\213\206<\223h\204\367\333W\021!2G\376\014:\373\013\327m\225\227\336\250\342\221\310\321\363\253z\354\022uk\342\177\302\213F\3758\021\341mgESmN\257\356\014\276\344ju\372\332\361\014\011", 64 },
+ { "u\267\0421[S\023km]\353\002\364\331~J\3503\376Ay\335L\012", 24, "\352\204\321\301fO\005\370g\003m\307\304\344\354:\030\244\216\215\026\331\262\2267\223\3655qX\252x", 800, "\011\254b\343o*\216\240Dp\215<U\244\235\272\372_\042k\233\034ia\244|=\215\206t\363\333|\276HB0\311\233\023\257@\004\2428\017\251\234\237J\003\207\333\335\263(\370\243Yo\206\244\031\235", 64 },
+ { "\233\342\010\335\331\134\3669g\031\337\033K\261\267\0421\177\332&E\340.\337", 24, "i\367\302\236\300d\014\025ch\323\320\376\231\211w\223%a\372\365\257\237\337\243\277}]\322\177u\375A>\015A!.E0", 800, "\375\240\241\236\314\214\274}\266\250\225p\024zo\012\354\220\377#_\210\001\301\343KG2\362\312B\335\331\304L\224\205\267o\320E\305\233G\021\031\2726iE\267\257E\217\310\313\247\312\232\200p`\266\375", 64 },
+ { "\255\032\237A_\223m\000!7E\226\354wSZ \375\207\316\2177d\031", 24, "\324\234\005\333;6(\316k9\134\271r_\035p%\350Rm6\2418\025\204vv\212b$\321\027\314\250_\3478\233y\303\301\370\016\200\301IZ\316", 800, "\316\312\267%f\212!y0\237 \243\207\371\274\321\134\274!\327\362\004\014\210D\250\232\307PbslaL:\252\262\201#\315<\220\232\275\273\246G\311\333/\032p;D\253\235\2401b\016\224\215\134\341", 64 },
+ { "z\006\205PE\271u\361w$g\362u'\134\013\240U\303\0055\305!U", 24, "\0279\275\233\304 \031Fb\231\224\204\276\203\021\011kM@\341\223\236+ \262q\337\212\376\222\3471\243\3615\333\247\340`7\004\3137'\263\231\302\327\3253\353\326VP[\263", 800, "\011\253 \313\264\371\333\363\017o3(F\001\234\207\243\263\376G)Y>x\271\332`\270!\250I\375V\300$\000C\206}v\345{\247\257\314\276\017*\365%\332\374\242\242\343\271\243\210D\200{+\254;", 64 },
+ { "\367\212\237\347\371`\021\024\317\363m\366\226P\240\271~\353l\015\204[\350\306", 24, "B\343\221\021\345\275\004\353ZB\274\223t7\313:\233\255\233\363\361\351\014\315\314\325\343^\370\301S\012\212\317>%\022\023\210\352<2(\224\311\312\027\232\327:\227\214\230\363\023\037\337.\024\010\372\317h\023", 800, "\327\304K\020\214\277B\366\002\330C:n\353t\334fXe\351P\033\032\311M+`\320/r\265!\301B\231\350`\001\013\311c?\220\333\326\202\216\243\365\245^\245j\377\372\210\307\272\303\342\216\370\210\355", 64 },
+ { "\212\264Q\307\376[/\236\013n\346\243\030\322\026\321\205\216\324\214\360=\370}", 24, "\202e\341xV\033\272\236\340\342\216:\016\2516y2G\267D\211\375!\247I\342\021\277b\031P*\207\035P\205\244\235\311\310_2\346\023\354T\004\304\327.\332TR\312\346\034\212\302\251\134\005\310\016\004\200\257L|\205c\303\205", 800, "\220\242\272\200\024,\341E\312\361`\270\304\345\016\037\273\273\341\301\134\224\316\032\003\324$(\316\234o<\250\263?6\351\024\312\042d\373\366\320\036Pa\015'nX\213\033:\313N{i\134/s$=@", 64 },
+ { "\274\322\370Z\025\306%\003\310\201(b\362P\000g\264\011\212\340{3\244\236", 24, "\256e\007,5\002=\262", 900, "\005\001w|\222\320\330\021\015P\305/k\244+(\002\366\277\207\243a\302\350\301\227;Mdc\250\024\370\006\311\007\226\302\037o\367Wg\377q?k\005\223u\021\036\375U\321@\363\232\317\363\225\177\3303", 64 },
+ { "f\247\021,n\027\321:\311Y\243M\345.\204Qe\302\206\301(\205\031\235", 24, "\037^\213\344\352\177&\134\004\203\203{%&\027i", 900, "\253m\201\326M\344} \345K\2152,\245a\255\306'\265\353\216\011\217\004\207Y\271b\234\222\230m\016mu\340\231\035\235\215\3413:\004\332\015\310yR\260\023b\206\000\230\007\314\214\370 8\011\210\313", 64 },
+ { "U\257m1\225~y\202\247\245\346\262\261&\252\253\261\375\022\303\247<j\344", 24, "\317nW\220k.\354<\216,\361BpOg\344,\361\243\364\006c\205\372", 900, "\304|\026z\316?\247\327\240\300,\306\315,q\333uV$\364\3113a\301\222\362\266E\345\303\3540Ck\254q/\204mB&\234\311\234\320m\353\261\225\355R\355\214\001\215\362$\346\336\304\214I\243u", 64 },
+ { "r\322o\336\354\307\264\377Z\343\001b\007Z\274c\305\275\214\265<A\372\201", 24, "\264\025-\211\214\371.\260\356\334\002f\277\134\265jd\341d\277\234pN\210'\361\353\377\220\350R\200", 900, "gH\223-MD\365\211\322^\211.\251\200\346\004\336\252\221+\265\304\026v\234\026\305.\305\270:dU\365L-\276\042j\241\264BX\376\260\313\032\367\026\317\342$\244\210F\010\234\335\377\334\027b?\030", 64 },
+ { "\357\253B\361\221\317\276\014\001\344\310\024\3269k\233\324\252.\351\374.3\372", 24, "r\252oh-\222)c\226\012\0159Em\303N\240\313\336\313P\267oWX\277\2155\032V\367\324[\006\320tl%\007\305", 900, "-\035\234&\233\305\302]h\300\343\351\370\274\217\2001\346\312\276r\300SL\025wJ\257\251x\205\311\367T\332\262\233\221\367\256\003\234\316`\273\226\262\312\205&'\2449\375\260\206\216\267zD\010b\024\266", 64 },
+ { "\021teT\012\013\023\355\233\377\036\003t\344D\333\251\336\034\211\332\344\375q", 24, "\271\376\324\336\2374\201\300\303:c\0305\010W=\302\327e\375:\305}\244\275\210\317\246\255\005$\204\215Ft\225\031v\034\2326\273w\012)\317\302\253", 900, "\372yMd6\017\2368\014uJJ\271\0174\363\271\205V\274J\206\365\250\026#PB\214\201,\021d\0264\221k\030\255\222*\317\315\234\032\235`\274i\364\231A\017\042\013\325\375\220ySb\246_\023", 64 },
+ { "\317`T\2038\305\247\261O\235n\021Nf\255\014b\237\316\226hK)8", 24, "\274\346,z\027d\020\274'z\350fUh\247\364\375\324\015v&\327\237A6<}b\337\033\375E\205<K\017\032\213\001\307\3357\312d\274\312k\245Ku\030\035U\267Zd", 900, "\327\352\362\257\276\005\015\355\134T\201C8&\030\220\213{e\014i2!s\207\346\327\316\262\352e>e\351\342\321\260\237\275L\220^)\3519\017\261\013\364\243\037\030\277\375MkIo\275y\004_j\241", 64 },
+ { "\367B\325\214\226\364F9!\304<\320{\342kg\315I\315\023\316\372A\336", 24, "\240a\265\361\241\204\221O\347\345\026K2\336a\357\304\037F\311\341\276\216\343\360A\334\014E\317\265\203Ik\346]\317\031\272~\365\267ft\241\015\350GX\235\021\037;\024\033\354\2124J\337\377\304\203\250", 900, "\202\242\3013\2208U:\340\237\377\261,\312\016\251^\260\271l\324\213\034\311j4\011\236\254\360\251\341\347\310{H\017\320 }\232\274Jg{\217\324\257\363o\357\344\026\026G\277\310\004\242\235\263\357-\222", 64 },
+ { "\016\270E&%\327f4\252\212>4r\344d\252\234\271'\254S\324\352\350", 24, "\375\306\361\014e1\036\264\033W\024j\367\214\265\027*\201\337\333\233\275y?\027\270~F\274\346dC\364w\211`L@\364PCo\257)#\346\343?)m(_k\252K\375\275\003\3456\376\2240\036\360\222\373C\265\217\322\251", 900, "\302\205\312\343Gl\252\336J7\013\234\355z\377E\3711U\311\204\213\332X07Z\222\333\017s\351k3\333\370\273\352\361nvB,w\315\202\214\327\367\351W\3560\350\377\016\2111\323\315Rd\360\241", 64 },
+ { "X\241g:\042LD\026\033\305\2103\347%\313\222M%IxT\3311[", 24, "\354*\024\334zTk&", 1000, "TW\205I\026\261\327\304\271\241.\340\134\260\241U\257\312\000L\177\012\002\020c\266\333W\352q\331P\336\027\357\363j\356P\3036y\357\306.\361C\232\134\245[\366\203\213\307}r\255\350\324~\330J@", 64 },
+ { "P\273\256\313\320\002{\007\246Q\235\261\314\225\325f+\265c\221f3\037|", 24, "\3772#\270\014I\036\251\274<S\330N\376\215m", 1000, "\347\3452\211_k\005\334\023\365\360}\245\213\242\367\300\315HLN\212R\230\037\224QL\241\220\374;\334\023\332\306\255\017^X\234\374ak\370 \247\001`\345o\255O\315\275\3052L\012P\340\307\014\305", 64 },
+ { "J\242`\033\017\337\255\360\324\326\225\221}7\335\307\357\376\315\360\211\273\246d", 24, "\275j\034\275\221T\245\036+e*\225`\301\342\0125\2330\330V\212jt", 1000, "\016J&\254\205\215\330\2111\350\013\034\372c\273\363=|u\0377t\025S\025\0177\302\332\213m\326\0169d\263\271\037X\263\251\334\035V\2564`\260\033N\356\237\245\344\322\323-\022\356\203YB\337\314", 64 },
+ { "\001Q]=\016v\371)=sJ\245\042\233\335\322c`\000\303i\265|i", 24, "\226*l\351\350\326\021\027\201\3548\223=\376\250|\226}\234\352\316\224\327R\324\027^\235)\232\230u", 1000, "\244\257\326\351lw\257J\300:\373\206\027G\326\217\014}mB\243\340\361T-\030p\236\034\367\265\350\234\326\270\300\265\274\211\213\215\303\365\341\257\227-\363]\245 \351\006\364\275\037>\343\270*\254r\244S", 64 },
+ { "~\021+\245uZ\362\213\366\200\022\206\215\334\351NYt\355\377{\353m\027", 24, "\2447u\244\364\217\330\177\207zVy\210\026|h\302\255\343\012l\034W\235\214w\263\270\256\236\255\312\134j\272Z\350\134v\357", 1000, "\323\021P\223\322\235O\310\243\376\254\014W\021\250#\357\307'\305\362)z\177Y\177\354w\265\015qzf\333>\020G}\011\355\350O]\253<\221\251A\013\315:\023\201\267\010\210\372\277B6\241\244*\314", 64 },
+ { "`\216\220\304\345\323\032\361\230\006\373k\0015w\247j2xL\303\242\021\301", 24, "\354\314=\001]\034\307\013\035\250P\231Pi&\031\205\203\027Y?\360\215nEQ\021\336\373\215\332O}h\264\207\253Ah\377\376a\264u\251\002z\252", 1000, "\321=R\262\037DS\377\205\222t\316\371\353\206\267\315\313\246\364\3673\010%\224\351\307z2\230\360\273\220\311aGp\2636\015l9\300\345\003\326r\356m\203Q\235\232O\227 \352\341\021{\267M;E", 64 },
+ { "\354\004\006\232\301c\330+\321\346{\032\251+\226\274\273r\2126K\000\000\320", 24, "\244\371\011l\010r\042\231\366N\007\354\251\304\237\021\200\314\321d\330P\027Jgm\022NQ\015u\354\016\023s\363\0059\003x\243\366\361'\375s\330\266\310\334]\350\201\037\330\177", 1000, "\262\340\313\343-\0366\257\034\321\223hwX\317D\305h_\036\012\323\303\360(\016\014\351\244\256|;\031\316\355\317&\244\221\374Do\370\351\372\233\344\027\237x\225\212\032\026\321>\203\335{\263\343h\254\245", 64 },
+ { "\306\321\026\304\337ii\326\313\346\326Vw\314&B=\233:\237?\221\201\353", 24, ")\344\304\012\2374O\241\033;\035\3024u\021\243\347\201\314\277u\304\210h\252\022\241\237y\031In&\3374\356\315b\214\004\277\370;\330~\237\335\262\275[\373\3121t\005',\211\201=I\321\216\334", 1000, "\376L\243\307O\223 ;d\203\232\017\217\324\246\260*=>\377\373z8\226\237\255\341\320\342Z\301\015m\363\005\201X\026\202\221\017\351\301\003M\366J\031\263\035\001{:\307R\026\356\367Ww\226\2106N", 64 },
+ { "a\201~E\177k^\377\225\372\322\254\303%\007\300\327\315\250\025\257VA0", 24, "\270SB\012l\322\243\037q\035\317;\324\307\324\351\347\030]\245\265\366X\2650e\322e=P\205R4\344\245\227\221F\272\356\034&\323&\022m`z2\275\303\266:\207\212\213U:,\035\227I\275\347\257\023\211\206\211\324\377\364", 1000, "\224F\035\020\272\0235\366!5\234\200\355y^\042+\321\275nk\247\366\034#b\235o\214\277\020\375\344q\374\035\261\263\222\246TI\221 \240u\232[\214\346HB\221\322\003\377r\371wG\025\035y\345", 64 },
+ { "(A\2328\302\034m\303m\265$2&\227\250\016Q\3779\275,\313\013h", 24, "\250\337%\227r\334&S", 2000, "\241}n\035\312\202l\273\322\352\272\252\212)\335\230\226\325\012\362eE\004\315\357\177\245'\022\0052\244\024\014\225\227\007\243As\277\251\351-\262\246u4\312Z\214\274\202O\376\324\262m\035\201\360*\343\006", 64 },
+ { "\224\227\346\024\316\015\012\374\275#\026\307#\012\262Nr\313\353+ \216\372\032", 24, "t[\215\345V\325<\254\367\361\255\343\245\023\017C", 2000, "u-\242\357\275\260\2226\270\312\037\230\275\372rS\327\216l9\316Q\356\210\323\210E5@\217\347\036\032C\202)\033\230f<\227\023\256'o\233\247\006c\320j\244\341\020\134\177\313\177A\214\336\215\230\322", 64 },
+ { "b\247o\224K\245\234t/\302\265\264>\310\225\024\015#\255\227>\225\022@", 24, "\211e\230C\273\3177\2211j\365\030\320\203H\255\017^\256\324S\212\266\372", 2000, "\302\004\0071\000\034\042\360R\205>\020.\016\334\042W\230c\250\222zs\337-\377\373r\333c\001\371\272z\363\0003'\362\202raS\244V\354\377\344\336$\201\336\322\372Uqy`>{B\021\234\030", 64 },
+ { "\002,v\342\377\027yk\360b6]\032eS\242\353\011\353\204T\307\207\022", 24, "\236\177\303M\345\365\027T\017\323:\360\225z\271\361\220\265\017\300\273\030\223\2531U\316T\020)\225\375", 2000, "\340\214P\252yt\0126\301i\3054U\217\267\024\364\035\306\003\376!/\345\374?9\250\207,S\031K\276\223\257\364\346\367\241 &\256\271\314-:\272+n\367\023\272\311Y\346\004\225\315\016\236\205\306\350", 64 },
+ { "\004\000\227\245\266\231UzT\033sW\240t\234\004\356\320xazQZ\033", 24, "\257\341\327r\023?z\022Y\205t\302{\252\341\027\341\333\260_d\030\206\354\367\026\302*M2\225\347\336N\253\251&Ad\276", 2000, "\353\364R\036\042\354w$=(^!\317\207\266\342\212\215\263\250+\307H#P\011\3448'\212'\030m\037V\021\212\375\3601\311B\233\361!9\036\2135\034\337\264T\331\346-k\222z6I;\303\360", 64 },
+ { "\015\213l\014\376\305F\340\317\356[<8l\000\235L\361\327\3623\251,\032", 24, "\356~\011\332\242\011\203:\271\222\204xV\274#\0064\016\237\364[\377\231|j\031\336\272Q\007\331\371$\010\361\231\346\205\337\340U%\230\241\221\202f\267", 2000, "*\230O\020\204]\354H\245&VSOk\247\352Jd>\243\322\351(kX\302c\177UhS]\001hq\245K\320oB\201;\335 %\3164\256\024`\233\273\256\304\324\247\321\345|\023$?cJ", 64 },
+ { "\203\371\026\261\020\021\004\370\246\244o\321\365\355j\004\200\367V\304>)\355\300", 24, "\260\347Jg9\373\303\270u\270\253\343C\300\336\010\307?\016bu\366<\334!\312\337v\240w\177[j\227\224AU'\037{\032\245\347n\343\011\370\342o)\214\354\265\236\234B", 2000, "G\345\0377\201\012\226\231\207F\224\006\035\236p9\303\365\3250\223\226>,\313\330d\317\237\312\3238BN.\257\0155h\377\034\236\331\223\037\307\3416\317]\203\302\252\356\234\230^\027\220\375|\0341\006", 64 },
+ { "!\323\234\201\027.\265K1\243\334w\375W\367s\264\232]^\011\243'k", 24, "`C\241\225\020\3131\226V\311\257\355\361\313\326\304\003K\206\266\311L:\327'\370@\317U\300\027\207\012[\246h\247\004d Re\027\247\222\323\212\225\324\020v\252_\252\255\361\367\303\020(b\015\346\336", 2000, "\332\363\361a\364\375z\356\215\366N\256\330L\022H\2644\134I\362\373\310\212\213\262\326%\366\351`)\315\014\356a\266\037\352\262\236\365\326z)uU\374\364cq\307=\331\013\316\243\335\033{\273\244'c", 64 },
+ { "\263\266<g\264\006\270t@\246\036\020\370ng\270q\2354\326<8g\322", 24, "\011\354\243T\342\245\336\376\205\342}\200\240\246\245\342jwQ\026\334fj\214\217 \343\324\233\036\0135\303\353O\337z\326y\300\354\273\016\364I\222\363B\321\023\234\200\221\032\010\255p\213\222r\241d\247\360=\032\252\031\276Q\344P", 2000, "\275q\253 \305[\023\277\357y\331)\360c\277\256q-\272_:\020\301\026H!V\247$\341\250\304#+\352Y\263\0176a<\276\202\271\010\235\322\317\001\003\007S\300\326\311\254\024\271jp!d\226V", 64 },
+ { ";|\254\203Z\375\011\366\211:\027\033\373s\035\020\3351\034\214\036\240/\371", 24, "\352z.\3715\305\236\346", 3000, "\021\327 \326\306\266\022}\355\375\217\206\205\224\220&\035\301Yz<)5D\177\264&,/S\362\374\336ey\216{\320\241t\354p\207\025\360\301\355K\272\276\300\356\221\025jq\232\371\251\343k\253\215\307", 64 },
+ { "\227\017>\230s\247\032\243\235\271\203y\245\243\012\276\015\230C\230\262\213T\346", 24, "4GO\256\202.5\272\256\313\017\307\220\252\234E", 3000, "\223\021;\244\350D\254W\275\346\222\261N:\235\364\316N`V\307\134\004\037\005m\332\222J\324H\020^\325=\233\356\350'!\035^\245\025\263\022.\272\004\254\342\243?\010\011k\224\331\331\2322\336R\242", 64 },
+ { "\237V\216}VW3\357\037qC\305\317\207\302\220\351\331\037\375L:\022 ", 24, "\336\277\323o\333\220q\304\274\313h\237|a\327\323\230\261(\371~\020\327d", 3000, "\211\017E*\360`\315\036-Q\374A^B\343\020\200y\034\177U7\340\260\371?U\221\323}\321\212\261\255\3411L\246\315\334\004%K\01749\207\341\027\313X\230v\300\032=\304\340\307\240\005cu\036", 64 },
+ { "\277^\326\265S(\042\033\000\346Y\234M\000\310\025\344\3171\343\230\017s:", 24, "j\301\025\325\364\345h6p,\377h\322\372[\215\262\261\235{\355\352ae3:\042\337o^\270\226", 3000, "\367\314\334~\313\305\216R\375\313\317( W\2524\237\367t\006V\356l\270\305\025b\351\372\016\205\274\032yn\321\366B,Z\300\245\310\364\202\374\336y\325\015\221\342\343\223\254)\340GH\354\021\235u\354", 64 },
+ { "oP\0024\027P6\376C\272\232\025\207\225\3616\361\26629\313\004\375$", 24, "\031r\357\216B\274\223?U\2367\002<&\2615Lj\012\276\303\327~\373\314\373\3762\010N\177\231\324\036n\334`\317\013\200", 3000, "&\322\212\306\227\365|H\226f\313\2425.p\030V\261j\350|\352\205:|L\316[\023\305\222`W\264\246-\232DO*+\343:\241\251\320\233\354\377\203\246\235S\027\341\250\316\327C<\226~\346o", 64 },
+ { "O7\204\017\015\252\361\327R\030\367$\314\307\217\207\252\323\333\270\213\207]0", 24, "'\234\271/\326l\237\264-\337%\332\012n\372\276H\262\134\263\336\213\337\021M\234\321Q\227\333r\266\304XS\343\211\256T\322\303\234\211\260\241[\037\271", 3000, "_\203,Z\033\317!\350W\356\335\2426T\015IE\012s\331~\301D\012\374\306\235V\321p\340$\353\263\331\237(76I+\000P\342/\001N\345\313\241\214\377\333\377\360Z9}\3544\236\201\370\351", 64 },
+ { "\244\350\3759f9f\300\002\015\214P\344\022\200\351[\134RvR\363\225~", 24, "\204\253i\244\217\223s=\217_\313\242\024<\252\241\3530\215,\212^\335S\257j\331\003DNLn\2414\035\017,p{\364\360\342U\032\254\007\021\036\256\345t\253\316M\021\254", 3000, "F\264\340\263\355%\367\007CHC\304!5o\030K^=\305\204P\305gIJ\274\177s\232?\341\000\364\014>\214n\236\004\363\250\323\324\211g\025\371\004Y\357c\351\033(O%\312\267\214\313\001\003\274", 64 },
+ { " \373\314\232\000\2625\230_\303\350zD\210\252*\034\305\005\017\355\356\227\270", 24, "R)\032\244p\363 !9\274-\205\343'\231)X\346\025E\323r\032\357\024\020Fr@\013K\221M\376a\336$\024q1\310zU\031\271\274\325(\352\265\017\042\210\016\276\271]\330\252#\244X\261O", 3000, "\241\362\014\177\002\312\211G\001\312\001z5_3\207\232\300\227\245\201J\255\376n/\177(\371x<\303f$\353\210\014\232\232[\244\277h\353\341\022\320\325\002TF\352\037\264Tf\2676\370\271\321\363!\307", 64 },
+ { "\333\025O\227@h\274\2608\237\300\004\377\223\022\320\341\312e\202`xN\263", 24, "a\042V\324\255~rU\355\206\366\202\005\354\002}w\033\215\266\010` \215\226-@\231\246\376\340\010\023\015\216)z\221\365\327\372\262\203\035j=5\265\243\242E\035n\036\014\0177\367\205\237\363\031\346\264!K\357Y\350d\375\373", 3000, "\003\242\243\204\364\011Z\213&\276\020\270\364\204\275&v\013\015?/d\352XM\302\306\371\242l\360\033/\247=\277c\331:\021OUK\262^l\270]qh\242\257b\213TJ4L1\364\366\253\201r", 64 },
+ { "(n\026\312\205\343\005\033\363+;\013\305ww\340`ET\312*\2063\371", 24, "\327\336n\263\257uk\336", 4000, "Fj?_\352\260\304u^\354\210\205e\025_K\026\277\346Vrq(\224n\204\371\024\221\021dc\360\030z\270eu*\203\365\336p\32321\305w\347\245\265\337\212}S\302\247LR\377{\002\203\235", 64 },
+ { "\275\277G\270\316\265\317Y\033\321\261\3341^\036)\235\253\253\274\014\227\327\374", 24, ":\311\227\357\355\353V\261iP\265L\354xr\262", 4000, "\021\332D\300\265\336P\356\226\215\014[=\000\234\027\224eX\022\260P\220\207\315\274~\030(e'\314\134\256KX\015\022\361`\315N7\021$\005\213\027\327\230\300\212\246\267\010UF\322}\260\276\006[\273", 64 },
+ { "e\3241\366\225:\037\220p\032\242\346\364\341\237s\332\235+\033<xe\027", 24, "\305\270~\333\313s9K\307i\247\303f\313\270\243[E\266\334\344\335;\007", 4000, "\240\277\036$\372\225\232Q\003\3203\373\367\343\370\025\225\305x\307d]\011\374\335aq\022\034\022C\355\011\340\341\366\301P\332\3016q\021\343\247F\245\321\273\224\327\323(\250@S\2052\255\2013$1\334", 64 },
+ { "\306T\011\2261\274\303\245\340@\300\374>`\007\333\260$\241\007p\013j\007", 24, "q\326\233\230 \340\376\340\330\241\255\270}Qb\037\331L\363\243\021Fe\012\204\210\220 \246'e\323", 4000, "\233A\343!1\343\230\230\201Xu\272V\314L\330\204\217\301\265\376Ph\035s\321\253_\303\224\201\262M\350\365\375\302\325\217&D\234\314\320D\210S\370L\3674\217B\311\251\313\237\243\202)\030\022\307\336", 64 },
+ { "\266\015\355@-\222\245\034\2232\353\327a\326[\223H>\212BBP\212\221", 24, "\207Q\350>\367*\313\020LH\353Ap\025/e\240\033\230\221=\367\325\243\326Bp\037KHzi\302\017\247\254\306\375@\346", 4000, "Ii\225\355?a\327\357\035d\007@`A\307M\214\027\304\205\376\343\2641.\325^\336\030g\275u\351\333_\205@Q2v\255\263\353O\243\006Q\243\303\031\252\252\036_\020\3739\202\307\177C\314{\271", 64 },
+ { "\362\272&\200\012\014\365\001\341\023T\232,j\201\202\324\0042\256h\377L\250", 24, "=\013\254\322\351\270\307\341\322\310Y\262\211%\357\356\273\233\346'\265\277\350\313\342\263\244\017\361\005?\033\352\374+\305\244e\270\232$bB\375/k\355\316", 4000, "\222a\254\336Z1\3035)c\302\226\031v\216\345qr\234\247\211\134\004O\010\033\205\221YR\313\365\034`\244\276m8\204\255\217}\333\013p\017\307,\245\270\344~\037.\027@\243\3008\226\346\364\004^", 64 },
+ { "g\027\243\247\030\210\246\017\355\022\306\212\347\226pup\007v\374H\261\006b", 24, "s\006v\241m\353\271\320{X]\346V\027\345\331\360\017\302\210\010\213\243\134\361`\235\306\265\036U\025\264\373T\373\324n\033\226\037\252\031\340s_xtA\310\335a|A\252\274", 4000, "l\247\361\333NE h\255\336\021\374\356\327}\347\314\347lZ\336\335\377\341\354\342\206\317\310\036\302G\2039-\374\005\027\204\313\304\340\334\032\032z{S\273\330\1772Q\220a\201\260\264\312\222\003\317\034U", 64 },
+ { ":\243\340\307\302'<pH\011\377\3357RG8e`\273\361 \0344\310", 24, ")N\346\003\210\316\011a\015\241Q\361\013\214\202\346\362\033gK\224600\360\336C\213\351u\024\343\306\001\312\037D\250PH\211\374Zg\361\377\232eT\300\323O*&A\236\316L\356\261\2314\320.", 4000, "\271D\336\350\333\027\332\363.\371\351\321q&T\032W\225c\031\203\373\361\340\372\216Y\245\027y\362[\257l\233\212\211\243\370\012\013_3Ww\236\221q\201\370\330\224\310\313:\214\345\263\033d\224i\205\233", 64 },
+ { "\214\037\033\217\010-\262\005S\033\374\344\304\226\266\221]jN\317R8#a", 24, "\377\260D \264\301\200\271\361\026\227B(\210\336\004\315\343\3651\306u\357\271\271b\251;Jm\261\234\327\273\344+\003\261hF\321eP'9\363C\021\305\203\004\200Dj\313\205\005`$&'\134\276\037\367\033\205\006\330\365\207!", 4000, "\340\310\353\232\220:\034\301\332\357\226\332\377\365\306\025\2256\325\357\017/(\134\351CE\241\032\261\207\265POQ\303S\323\274\013CM\257\256\364\327\2351S\022\211C\211\225E\233\177yl\027\232\274%8", 64 },
+ { "\205\247]\253`\230\036\334\353\377\005\303\314\331\376\030\322\340g-\004\235\027\032", 24, "#\002\015\020uG\356\206", 5000, "\3705\320lm?\250B\023\0274\267\022x\005\220:=\333\234M.l\347\263\323o\251NI\330\260\325\377\031\004\327\251\354\300\301\246\037C\241_A\274@\3306\365\274\207K\234/L\305\276hB(\177", 64 },
+ { "!B=J\001\350-\300:\2314\241\256(;\273\271\330\305\267.C\351H", 24, "t\0022\334\212\342\356\356\033\254\256Cd\303\212C", 5000, "DB\037\011\037}\330b\230\342\2150\224\211\244\220\231\2744L\042d\342\042V\024+\004\224\330GCZ\312\340i\004&5\204\017\320$\013\015\262\226\035\300\222\215\201\345q\022B\0132\206CI*:\310", 64 },
+ { "5C\225w7\027IS\023\304\217n6\032\320,\217\235\300*\351\277@\272", 24, "\234\247\326I\214\006\274\036]u=u\255>\256\134\326\301W\356]\307\3327", 5000, "\205\254\255LW\275\210\264\205a\214\001\323\312\254\251\233%O\222!0\037\177\274:\212\357\354`\005\335W\254\265[\275>S\243s\225\032\341\313\214\321D\177*\246\001\366\005S\234\230\010\300\301\024\337\3777", 64 },
+ { "i\347u\243(?];\2174p\027|w\252\232\374\252\031\360\013<\244\313", 24, "U\204\031A\265u\316;\325\340\215C\340\026\245\366\027V#\3607\320\223\315\352\364\316\240\020\276\034\342", 5000, "p\216\236P/^Q\034\303A/:C\012-V\363\242J\370\310\265\333b4\2267\177!pX:e\306\242(c\325\033\306\315\312B{\311\026\250k\026\257\207\217E\306\013\246>:@%\203\272\001A", 64 },
+ { "#E2\2447&#\202\264\042-e\346\315\327\212S`\354\357T\373\374\356", 24, "}+\227\036\352<\035B\325jB\225\343w\237\034\206\255\235\260\233\336\204Sx\216\317\202\001\261\242wb\035\352\304\235D\221\267", 5000, "\242\246\366\007v\255\267\332>O\332\005\024)K>f\350\031\325)\224\236\277/j9\373w\375\224\207\327B\321\354\265!f\373\013\375\024o\3768\345c\351\202\305~\366\221\301\237\3504}\215\313\306aK", 64 },
+ { "n\342f\266\256\206\242}}3\011Iw\350\264de\356\210\365.A\357I", 24, "\313\366\372-\305\017\251\134\366\265\2732\304\203\300\275\224\300\314ZKR\016\260\363\273\014\255\344\304kW\217\275\021\357\334*\342\022\243\312\303\265\016OM1", 5000, "9H\012\364@\023gk\224\324\007\340\017\215U\013\247(\006\007\305\335\006xAp\372\343\267\322\355\250\344#385;D\342\310Q\030\214\271b\275\265S\246A\020\247\030%\233,$C\267\334\036I\020", 64 },
+ { "p\224\002t\321\207- x\331MZn\352\042*\220Cv\230\303\244u\233", 24, "DE.\236\2209#`\026\303\274.\352\300\310\230\216\032\216\214\307\254\024\253\317U\033b\327\3532\033\231\370\344<\376\013-\330\0032\260\264L\012\305\020\254\356N\375c\333\010\236", 5000, "\324\235\2268\20247\200[\222\042\315\006\244\002\207\367h\31422p\371\205\206Y\202\331\307V\227\210\024@\002q\226\225\345\305`iwI\274\334\373\310\345\330\271\226N\331\242\371g{\231\266\035F\330\263", 64 },
+ { "I\2423\320\015(\222H\242\017\252\211\353\375Ja8\033\340\3765\314\322u", 24, "\346M\375k'| \307\351{\221\376\304T\026\0058)!\025\306\022\271\227\032G\254\200+\201\033\021e\032\334\002\301\317\201V\3658$\042\266\362\216i\217\266\021Y\312\017r-\354j9R\022\037\321)", 5000, "\224I\371i\265\005\262\257\262\253\373zV\250\240<\311\201\257\362\222UH\340\367\200Q\212\017\335\251J\020\002\341PB\202\030\022\017\277\032fT\256\013\266\355\227=\224!D\371\225\240J\215w\002\255>\300", 64 },
+ { "\205\027X\235\021\3344\2458U\361\222k\375\353\370\226z\3140\321v\353\026", 24, "\254\365t\304\343\317\275\254\0273g\241\003\320C\326\264\034Ep\360\267\273\2332\210B'\222\210\303W\202\021\364\246\302\320\236\256#\3074\324\313\216V\231\242c*\003\2345l\0365\353n\347\276c^\366>\257\215\227\215-R\207", 5000, "\337F\336\013{\253\267\314\001wC\321\201G\344<AZ0\204\371I?\376\376\035\255\362\306q\315_\0041\274\271j\037\2603\021\213\313\242S`\334\262!\217\264\220\344\006\366\251\233x\375\134y\177\215\362", 64 },
+ { "O\311\352s\262i8`u\374q\212\026Y0\262\361\211\344\3648\343/\342", 24, "\367\033\267\204E\231y\270", 6000, "\357\355\000\253&\257a\257W\134.o' j\236#+\326\003i\207\205O\0016\274\364\305p\006^WG\351\214\233\255\240x\2511\303!\370\203\253\030\312ZC\275~\373\220\024\010\345yio\032\310\344", 64 },
+ { "|\242P\225\031<B\246\003\270\007f\361n\364$*e!s\305\233\266\274", 24, ";\276\370\217l\336{\273j\331F8\037\205\223\335", 6000, "8\304\347\235\034\311'\334N|*Z\200\347L~\257\251w\321\363b\362,-\370\273l\0065\004\332 p\301\224\212\274'\275\3045\224m\036\214\004z\001\260 1\236\344R7\0216\377\300\243\311\311\030", 64 },
+ { "\024\334SfoW\372\005\016{e\017\307?\320\351\275\340!\007_\264\250\016", 24, "|N\264\010\2442\254\356\210\226\375 )\021\0102\202\343\244]\375\256c\324", 6000, "8j\333\364\246d\320\244\264T\374L\352\204\014\037\217\025\006\032\333pX\220B@\333\317\332d\333\354\304\352\037\236\253\306\250\201\340@X\215\316=\335-\2502\315o\255\016\372,Z\301a\232T\230\251\226", 64 },
+ { "\236\357$\340/T3\376\3370E\134\303\265+\232\244\230I\301\315\271\265\232", 24, "\244\027:r\356 `u\207\211\305W\261\336\350\005\201\035\2720\335\306g\305\222\177\302\361\036i4\367", 6000, "I\316\212\361~\310+\254H[\262\016{?\321\353\374\334\327\324\347\250\266\373s\022\315\236\371\232\276\027\217\337+@\245\210\220\271\260b\270\312\265\207\272\004\345\324\206\246\254\024\351Q\367!V\311\355HB\037", 64 },
+ { "\031t\203\3355\251\001F|'\261\335\224a\244\324<\206L\315\206Oh\306", 24, "\243\341z\234\361'\337~\204\247\376!\261\036K\254N\027\360\024,\264_l\216`\360T\223\251\327\323f\016\267\304P@j`", 6000, "`-?\220!\256\364\036\215\002\014g\201\261\237\252\354\313\266\330\033\004f\016\335\020O}!c\357\314\326\326\271=\375\331B4\200\311|\213\247\036hH\254i,\3004\020\210\332\204\210\014{\2136q-", 64 },
+ { "-\002U\017^\036K\372\367h#\0059^\2679\261\232\243\360\343EKL", 24, "\177\330B\025(\220\227b\320\243\213\042\255\327-c&\231\204\205\255@\300F\042^\362\020\277\2773\231b2W\256\274\032\134\265\217\305\367\022\276\342e>", 6000, "\226\341q(.\361\024R\272\037\201Z\202\300\271\002\216AM[g\263=\0039\034m\250pb\235_\205\3468A\007\213\276\366oK\273]\277k\010\011#\255\264V.)m\032_|\372x\230mnv", 64 },
+ { ";\346\354\013\230\227\031F\357Nz\350\262\002\025\305\013\337\377\332\350\256\014\030", 24, "\222G\375\327\177$\353\274\353\357@\007\026\356\266\303\240\370\271\241h\241\003f\233\0061=U\264\177\320\311\312\334\036\031\025\275'\324\341QQ`\277A\347J|\012\3315\243\230\217", 6000, "\332\363q\304\333D\240\347\241\032i\333P\231b\271\344\317\024\355\314\301\267\316\021\237\321<l\361h?\345r\000t\324\030\231\217\305\334\235\012\343\236\261\211\2728\352Mi\002\226\270\205\2675\177\363\321\011\250", 64 },
+ { "\373\253'*\353\374\273\310L\000\244J\362\244\36404\023M\361e\212\001\353", 24, "\217\252\271@\215R\277\205\340\235\300GJ\340\033)\320Q\322A\276\330\017\334\233\363\240\322\014\027\210g.\003\3302\027R\361\305X\256^\321\325\304\005\231^\332\034\226R\377uu%\3745\346\352\023\244-", 6000, "\023%\374\376\017\226\316]4N#\370\230N\001\356 \212F\312cRw<*\376@W\271W\332\254`\324\317K15*\223\272\316T\006Y==5v\352\033\334\302\204c9h+\357\337\360\217\034c", 64 },
+ { "\235D\371t\022@\313\322\301\303\367\177\362\355\210\035\017\347T\253\376\354\214x", 24, "\016\352\350&o\250\006D\321H\272\361,\312\375\263\0228+oE\015\216\337\306o\363\261x\3618\255\005\273\226\350\265NE\234\302\375\334k\333\331\251\204\204\222\003\234\342\313Wj#\226)+W1S\207\273i\246yA8X;", 6000, " \230\212\310\275x\361\313\246\032\207\364}s\2368r\301~\257^\313Y\0215(#?\271X\335\245y*\377\353\330\202^k\376Lad\311\000\350q\222\241\205\004\277%\306wS\311\3762+\326\227\334", 64 },
+ { "?;\330*\023p\010B\134\023?2\350\023\004\355._r\215\354l\353D", 24, "\363\377\344\232\235\373\265\341", 7000, "\355\245w\013\015vz\277p\223j\225`<\037U\264\3475\257\304\246D\245#y\355\232\012\302\037)\332\315\221z\003Y\242\362\017*\365\031\261\014\267\012\341\203\354L\260\316\343\213N\362\233\000\272F\344\306", 64 },
+ { "k9\317\360E3\264\030\032\316\253Oq\247\232\220\010\376\354\244\243,m\012", 24, "\266t\031\337)\034\306\036\212\036M,i\002\355C", 7000, ",\236\266v\336\331o\322\226~\253>`\013\215\202l\365\330\311g\235\247\232\361\336\257\206,\321\317\330\006\253\233\302$T\357\324\035\241\0316\030\036(\204\251\331\376M^\2559.\225@\0110[j\201\350", 64 },
+ { "7Y\231\310\325x\221\333\3057~t\254\265\325\335\247\326\227\363:\134\355\201", 24, "\340\341y\031.\267\217\264\012\020M\250\347\274l\311_\255\361o\232\260I\342", 7000, "\202\267\254}\0042\357\253\273\261\022\024H\020\203\013\032to}\347m\337\376\371$\250\255\321r\016\006\274\367\311\323\243^wsG\237Vw\004p]\325\261\365\027\356b\230\205\322L]\021\203\371\2709d", 64 },
+ { "\177\335\200\3270AM\247Y\322\010*\234\035A\261C\366n\036>r`l", 24, "\214\014\337\257?\303=\024\240\307\343\00255#\326\0235\267\327K>\354\375\211\321b\021[i6>", 7000, "K\000\352K\222\245\260\275\361\361o\365\326\364\036\2770\207\331 \312LL\350c\006\226\247\014\217\217\261/;AP2N\327\343\013\246d@f\250-\364\304\013\002\314T\032\025\247\017\217c\366\003\305\037\362", 64 },
+ { "\200s3/\021\217F\036\305x\321\206\215\004mk\314<\000\300\226(*\244", 24, "\247\250\3112z~\236L\327\215\003\367\022\244\217\201\373f\010$HE\221\026%w\326>\033\017\226\020\207G\367\352iL\322/", 7000, "\311\374\230\374<ac\322]\333S\011$`\320V\351\034\012\225t\241\301\361\0110\272\2529|\357\205\264\235\326\322WTM\223\374\240/\007\230\007\334\252?H\306`\006\345k\267\333\275\252y\314\263Gf", 64 },
+ { "m\252\243^\355E4f\270w>$\026\042\360\254\307\307\252\207ZP\232\364", 24, "\207tG[%\035\246\377\337\325\331N\022~\365\236/\304\201\374\021@U\372] \226\375\037E8\007\254\354\370}5vZ\031\206`\366!\322\366\342^", 7000, "\245\210\355\305?\007\012\026-\377\272\025\2700\270\375T\332iM?\216$tT\216.}\356\005ExPe\005\262?\223\226,O'\365\000k\246E\202\021\213\324\035qRg'c\024'\200X\026\240o", 64 },
+ { "\021\300\216*\364\017\360\1340\321\362\221`G\234W\363\333h\324\216b\217\307", 24, "+E\251\013\223\005\246S3R\255\016G`\212\214\246\352\012t\353\324\237\230\242\241c\227\021\374\316#\260\351\317\0252\243\007\002\346\026\237\023\216\3117\344S}.9\274\203h\371", 7000, "\362\315\306z\261\307\255\373<zn\003\252\001\014\310$\213\365\351\007\006\364\307F\303\036\336\323\366\236Z?3\3569=\202\134\215\205{\355\366~p4\011\326@\215u\265\337L\300\011\317\300t\243%\012\336", 64 },
+ { "]-\377\321\373\264'r\036\250\350R\036{\374\332\015\267J<\257\243\237\362", 24, "`\366\021\261\372\032\353:\005w\271A\215@\323u\353\342v\022\254 Z\0015\265b\314~\245\374^\036\230m\255\274]\356 \325t\002\246g\376>\276u6\243\021\347\234C\370\206\306Bv\257\211\347\242", 7000, "\033\256\213\370\341\215jM\215\216\262\324d\007\316\365\227\371\2034\134\272\012\206h8\212\253Z\027\314\305\017z\371\010\216s\343\313\277{A\262>\312k\376`*d\300\360\2516\241:%aj\215\242\275\375", 64 },
+ { "2\214\342\377V\332?\2433\233\177\347:\352\242\364\337\340\331X\237\257}6", 24, "\320\326\244\261\006:\026\037p\252$B\205\265\036`\362\327%w\241\271\317\367\004\026\215\364\260\023F\3245\220\276\367\347\260G&g$,\2219\015\177a\224\211\313\016\346\027+ \011\354\017`\026#>a\327\022\372\177|_\023\231", 7000, "\332\3414\351\013\042V\243\371\033\321o\023\023m7T^\220X\343\300H\010\306a\307+9\326\315\323\226\032\205\030\024b\371\365j\220n\014\312\024\003p\345X\017\341=\321\304\205\340Q\300\362`\307\314\365", 64 },
+ { "55v.A\333M\011\253\244\245\331H\317t\353\235\243\306\200\307\241\330d", 24, "\301\032\244\327Z\351#\344", 8000, ">\334a\343\370\316\221c\363\253\306\042\325\007\310UU\351g\340|&\203l\034Q*\36754\206\134\264<\341q\342\332bg\014\000\301}yR:pI\342\301\343\250x\232p.\373\211\033o\016\015\300", 64 },
+ { "u\034\247\2249\312\031?K\134\367\227\373\015w\005\303v\375\311\227Y\264P", 24, "\347\374w\031d0O\321\342+\036q\026\015+L", 8000, "\000\307\323\245\336\306|\361i\245\221\003\013Mn\233\211\227\230=\272\211Ybv.\007\256\244u\364\350\201k\274RA\022\327\212\325\234Or\0137gx\340$\365`\355\255\3310r\205CF\246\3554\003", 64 },
+ { ">\372\324z\264\3456\210u\275\205\315\302%$\323\023\204.\245\305O\266\215", 24, "\210\036\242\335\310\002\367\363\365\034\003\030\207\017\007\022m\0369Z\322\032\272\213", 8000, "wO\311(\236\027@\354\006YJ\353\207~\360\237`Y\316\242\247\307uz)\307\023\267\262\340\007\307u\2256\0220\347\1774\274\042\271M\335\026D\342`\356\004\255\2702\21587\0270\346\310\231\303\215", 64 },
+ { "\205\015pw\204\330\315l\327UI\003\134\005\033j!\202\007M9\030\017\257", 24, "\243\006_`Ge\333\270\232\361\256\305\266\204\351\231\232\310k\230}\035\225oh,F \177\213\3508", 8000, "|\206d\356K\204\337\023\357\324\325\377U\314\343\001\177Q{m\277Y\3232\347Z\264\343\032b\367\337\204\347\237\340\220\000\005\240\015\256\207\240\266\020{#p\216Kd\031\245\266\237\366\327\003\352\20096\270", 64 },
+ { "\234\317\267X\261\316p\305\317\270\037\223\364s`)\266\360\255\372Cx\034\344", 24, "\037*ZQG\262z\310\302\331\247\036\355\331S\227\271\245\240=K\342\360}\015o\376\275\353\370v\326\011\352\366\011\035\346t\315", 8000, "X\246h\312\331\270l\217;7\371z\357\213}\021\200`\035\335\277S\211#\024\257\253\367\270$\306\334\346\236e\030r\031\356Z\314\340_\237\242\025\134\314z\360\216\020\337\205\272z\314\036L\216Y\217G>", 64 },
+ { "\216\336\334\260\010\376\034\330\025Ilx\217\376+\341?A\303\320\212)\242[", 24, "\002\257\245\0163\344\244v\375\344\023\272\244;\353\242\371|\010H\337(\200ut\252\252W\260\256\355\251\244|\013H\257\350P\003\210\330Ji\026\361\230\313", 8000, "H\320\325\372\034/\365\265\000Z\356\250\353{\205\202\247\006\313|I\032\013+F\373\270\134S\015\036\3443\240\376\334\3018\3343h^\344\200\243\312Nk\264,\302K\365\267\315\346y\030!\020\313\373\241\272", 64 },
+ { "\364[\025a\015\244\351\226\361\277\362RnY\016Vg\011\004\032\0243V\334", 24, "\303\035t\266\010\251('\331\304\035\276E2\030\216\305\330\026\3153\271\227szf\361\307Jg\217\003\200.\031\034ga{\247\042\021VG\262\216\264t\015\006\014\232\236\300\335\360", 8000, "\373Ng\266cw\232\023\340\345\134(\241\223\330\251P*.\251p\023I\216\222\016s\274\2610\2719-QX\017s'\364\367\203[z;\321\275\367N]\016\205\216\032\246\351\013\014\230u:a!\340\344", 64 },
+ { "\036-\324\033\321\251\200\377\035C\2173\352\252);\211\025\316\215\375\214w\215", 24, "\300\007\353\005\230\026\313\261\017\376~\245\312\375R\275\336UpX\223\261R\321\012\257\305\361\354\305y\016[P\026\302\367\203\210\222\320\267\341A\374\323Lk\237\360\277\303\226v\214\362\372\303\363\2325\032Y\216", 8000, "G\277\034+\204mQ\037Jh\3735M\226yb\271T\333\216\320\233+\3414.\010\243\335\030vR\223r*\272O\263\222\310\204\366\266)\037\012o\344\333#\027\260\037\300F\010f\336\236\236\367\203\213\222", 64 },
+ { "\266c\307\213\340;\202\254\364tO\370\023\221\332\006w3Q\352d\224\000a", 24, "p\035\261\247\274*,[\341\236\263\237,\272\361\301\232\301w\203\023\024}\265]\313E\203g\364\247o\005\134Az_\007G\007\010\220;\326RR\216\262\025\200\255\262>\330\321\276\316\331\356\310\334(\241e\345D\263\330Gx\324\325", 8000, "\253h\230\377\267\373 \214\322-\273\273~\336\356\177\024\205\277\240\277[\227\023\332\316\315\374\000\272\351\357Z\011\021\300\377\371\376o\223\336\235\312\021U\330\257mz\250N\206\210jB\134m%\276/o\357\354", 64 },
+ { "\245\221f\320]\012k\303\031\336Wr\336'kl%\250\3559=\266P\274", 24, "\243 \351Hp\230\003\351", 9000, "Z0\012\276\353\351\232\246\223\035\333\243S\367\365\272\376;\035\314\004HG\030\213\275yU\355\005'\341\134\002O\014\310\134\21217\020\011\317\311\032\031\317\355/=h\201N\305\243\232\300\354\342\005\206\216\200", 64 },
+ { "K\213x;{\306\322N-=\201\352v\366\255\013\005\012\221\2142\336\014\300", 24, "\200>\242~\026\241\226\204\221\351\213\303\223\237*\345", 9000, "\273\325T\251\032q\370\2077\310\220FF9e\204\342\226\374\247BP\370_\273\024%\314r/Y\270\231\343\323\353A{r\334\013$\315\224-\226\272\276\244x\337\220d*H\021G\307\271\347\251\304\360a", 64 },
+ { "X8\022-Z\303\263\357\3236\371-\322\352\212}\226xVF\200KH\271", 24, "\352\321\241\333\217\016\012\344\245\240\216\017-\367\340\324\260i/\320\377{$Z", 9000, "\363\313A;\203\243u\267\276\002\327\036\2234\323\217o(k\372\333\023\240\221\300\254\2677\234\2040\300\350uv\327\371\262\215\272x'\213\274\010\021\034|\352\3338\353\273\027\272\265\376n\134\210r\300\006\247", 64 },
+ { "\335y\005\355Q\326\204\360\222$g\356s\374o\200W\011\220\001\247\025'u", 24, "\035*\242\310`G,\244Q\372\256\301\305p\034\271)t4bW\2076\225\177y\335\322\317\245\256&", 9000, "f\326\017\227\016\302R\363\324\314\010\271\320U\341Y\222\370\265\277z\370\313\303TA\334b\371\226\254\264pq\336^D\330\134Q/n\324\320,`ay8\203-\035\223\351\301%3\032\355\217<\233\256\236", 64 },
+ { " \250t\357\202 ;\3730`p\201\373k+r]\354;\370\244\030p?", 24, "\363\217\315\313~\351\231+$\215\216\362W\26569\223\027\300<\252\336=?\320\015t\333\004\265\316$'Z\010\337\032\300\356o", 9000, "\013\263\374\335\225\200S$\237\251\312#@\227@iE\373\364tpE_\342\203\204\364q\350\333\215\237\345\262n\217X\023\020`\257\327\257\236a\273\007\271\020\213`9\363:\011\240\014\257u\013B\302\365Z", 64 },
+ { "\037\211\342J\233OL\256\313,\272\317E\355__\006\211\354\343)a\326\366", 24, "\3069v\303-\210\007`\270\242\024-\010\203d\242\210\250\347g\031\343\265}\035\317WY\2600n\342\263\317\300\010\007A\023\313\311\320\361Y\266\206\326\256", 9000, "\037\232\322\324l|\277\265I9\377\203-F\026`\202\206;f\256-6\312\374b\0152\016@p\017\374*\250\213\255i|\274.\326\364y\336}@\252\012\377D\202:LL\274'\010\222\304\265\360\324V", 64 },
+ { ";I\027SS_\355\250\257M\025\034\252\332C*}=A{S\012\267\020", 24, "h\324z\036\2161^\373N\027iO\210.\344\361\370\213\300\274\204!q6\277rja\216tk\243\371#\353\201\272t\312j\206+L\005*fh\033?B\323K\213\300u\310", 9000, "\323\004\241\010\242'?&\207\347n\2659\262c\327\356\373Af{\363k\244u\315\271\257\215\002&#\350\244\253\345\004\362\245\341!\370\367\235L\324t\325V\232i\261\177\3241\010~\310\347z:\263\177\211", 64 },
+ { "\331\363[@JRZ\206\223\334\330\027\234\035\3774\273l\316\322]\310\212`", 24, "\250\0109m\246\376;\177\2612T<\274\327\2363\344\324U\343\343\340\206%;\347()\364\322}?\376\214?\246\361\005#\273\020r\200\260\3608\335\217\017\202\212F7\202\325\353\323\367\305\034M\033\303{", 9000, "\205\373\010\365Z\241;\3701a\253D\025\351\374\254eX\244\037?u\372f\346\231g-t\274\325h\030\365X\363\322N\011@\270#\027\212\271\307\221\302\275(\033{\221\270^\264ku\017H\367H\201\002", 64 },
+ { ".\354\201\342\256\244\227\276\215j(\026Va\3359\206l\377\020\253\376vu", 24, "\014\346\240i\217\206\263\300Aq\336\242;,,\240\035-\253\3070\367B\352\325\042\366\351\013\200\134\372\245\227u\233B\212\016\244.\021\362!\214\221\016%\224A\231\004f/\346\343|\007\3014K\334\015\2660\302\332\243\330\331\277\225", 9000, "?\341\377#\006\027\300O\217\020\267]\3708K\301b\177Flug\301\212T{\304\255>\260\357\266\2368\336^\134\326\335\0268\357\214\230R&&\372\223\315\212\374\341\320\234\224\203\017\310f\254\017\365\371", 64 },
+ { "\035Uw\245\207fy\341\213Cf\313\250\356\333#\365\330\025\214V\365V\315C\301\312A;\265\324\333", 32, "\237\306\371M\211\275\020}", 100, "\277\325\220\301\016\372\256@NS\004\207\201\3778`\215_\211w%.\337\374\353\367\220Z\272\373\326]\275\224,^\036\311a\206\211\260\005\317U\0154\001\237\273\305\316\033uu\251Q\213z\351x\036\243\265", 64 },
+ { "I\333\366\014@\0225Jgoz\373\270\002\020\037(\012\177V$\232\250(\264;\320\371!\324p\235", 32, "p\340\214\234\324\205\224\324\020h\221\357Ej\330J", 100, "\317]\000c\226\304\271\261\023h\365\3636ZV\016\353\356\222y4 \340\013\223\223\301\356<\004\234+\300\201V\230\370\375z(\177m\364\306\351\231s\254\360\227T\351ebLA\316\207\011\273\353n\323c", 64 },
+ { "\342\206\301N\363g\353|\226Wd\356\240\3630,z\343\225\024\031\252a\275Jg\337\361\000\224_\273", 32, "F\342{{\365w\331/Pi,}$\303\260\344\265,\032\134r\362s?", 100, "A_o\223\300\360\016\264Z\223&\300D\2500\310\316U\347\004/\226\364\336\353\034\217\011](\237r\356hd\245\205k\037\252\042/n\241'eZ\316\310K\233\017{\247\234\303/\205,J\373l\362.", 64 },
+ { "k\007z\262\2607\030\372\326\240\273\317\332\2406\031\203\014\211KOT\330H\001\356@\360:6\310\336", 32, "\301\261#\301\224\212~\323\326\237\037\202\036N\342\034\323\034\332\245s\002&n\035\265)kU\314+\244", 100, ":\276\200\342\261\1345\243N\265\304\334\375\364\323\371\304\213KB\336\372i$5\205\344\313\012.X\273\021\306n\222\025\224\207\321\365\265\333\304\363\225C\256Yf$\235\340d\026\027\207\216\267\006\361\030\353@", 64 },
+ { "\212nj\377V\332\013\370O\246\366+\370\264\321V\252\246\341\232\000\250t\341\241y\217\2764\213\234\203", 32, "\210\370\011\356\265\343X\235\3101\255m],h\253[\347{\134B\360x\215\222\324\275=\212}\363\247\326\004m\034\003C\241t", 100, "pc\205\224\356\344\247b\310\251\222\224\357\315\277]\000\272/\303T'\371\336\221u\370\355*\264\226~L\357\004\237\345\215\236\224\310n\032n\345\357\347\204\231\2543\020\234\032\042'\014\361,o\134\305\2233", 64 },
+ { "\227\222\262P\202\346\315\316\216#\250\032\346\007U\375-\317j\020<\220F\344\215X\234\004\374uM#", 32, "\215\215\254\311\351\001\253\313\331\364\304\315\032\276r6(\261\325\224\346 d\357\257\013\240T%\314\305\270\200\350\204\343\276\371\021\011\206\221(\310!+\034)", 100, "1W\302\356\244\310\375,&oW\334\355\230ipU/\026\236\352\250X\010p\335c$\032V=\375\316\037T&ja1{\225\262\177\313M\360Q\313\220\177M\307/.6\206\333\256@<[\331x\212", 64 },
+ { "\016j\376}|\201\341\346\214\014}\023\353||\360K`\336\205\261\336\015J\243MP\341-Z\016\224", 32, "\013\350b=%\356\202\247\222\201\3362\222gr\331Lf\201\340=\365\002?>\011h\246\306C\361\274\306mK\002\005Z\230\267\372\0364\342\254<\032'\215FR\231\210re\035", 100, "Fr\342\330}\351*D4\314\003G\366\270z\016\345\233\241\331\225j\262\324\362o\016\306\205\272\333J\255\350G\001\365\225\2548c\202\036\275\353\375-\031\032\023m\220u\337\236\265f\357\033O\341\332\235\246", 64 },
+ { "\037h\035C\201\200\227\347\245\205a\334g\343\330F\330\267\276\254\350\234=\317$\220e_?7\014\020", 32, "\363\34664\0316\003!\264\262\016C\225\026-\230(\363\007(R\347\013\014\012\304\244S\032\367\203\336x\251^\365\200\004G\354Vv\236\277S_2\363\260MU+\240\263p[\274\357\343\227\264TN\244", 100, "=\304\257c\200^\221\207\253\275\301\300\245\370\303\326Lt\026\235\337\334K\026B/&\0273\352q\024Pf\377\305\326\015\353\217~:\223bxyC\036/\021\134@\324,\337@O\024\356\252\352\314\001w", 64 },
+ { "<ck\222`\012f$\315\356\376\035O3\007\231*\320\3771\321\367\265\277f\026\235]\212\346p\265", 32, "\200\2359\362\263\334\216\034\256\314aK_\026R#\204M=Z\317\042\245?7\321\243\270\004\253#\227z\207+\236\236u\031e\235\242}(\342B\332\316\013\276\2202V\344\351\240\371\3477;\356I\345\265\024\202%\214\001x\317Z", 100, ";\323\242\262\007t\327f\312\341,)\203'4\316s\215\367\234u26\257=\344\364G_\314\026v\266\211@\303\374\253\363]c\177.\322)\372\264\267\300\277\011\270\233\134\243\207\221\034Y\255[!\227\243", 64 },
+ { "\247C\355@\333R\261b=\000\350\037f\035=\316Y\036\001wPf\235`\276\212\2444|%i\355", 32, "&\223g:#\333D\303", 200, "\012gG7\364~|\311\306\274\255\333}\234H\031\200\377\023BQ\276y\366\134\317\366\277\332U\253\277o\300R1\332\276\200\036\365\332:\033-\203W\006\362.\002\267\360.)`\354agA\235\260\263\347", 64 },
+ { "\215j\251\011\356\335\233\360\027\313J\233k\222\335\311\263Tl#\376J\2344\220\265'\220\037T\314\253", 32, "\370\034\005\326VK-\357s}\236dX\264\024c", 200, "v\356E\210\252B\031\264\265\202\247H\252>\350B\315\204\370D;A\224\200\004{,\202\302Ba\310=\214\012U\002\007\265h\007\262ou\251f\372\337\227\012\310\357\277\346\321\351\236?\310\303.\263\377`", 64 },
+ { "\372\252\370u<\365\355\222\214g\324\027\302\020\214\357\013;\334A\042\331\227\267c\3001\035\215^\245\316", 32, "8Kt_\242\015\235\007d\013\376\241ml\211Bf\225k\357\226\031\042m", 200, "\214\323$\343\001&.\301\226\220\260R\263\253\207\226\321.\276E\233\267\351BW\230\365sc\273\034\005\342\006\033\2051\227*1\017\216\221\024\276Db\222\212Y\032\017X\333\320\210\177\356U\366\321\256\002\216", 64 },
+ { "\203\000$\374>\2270*\334\000*\254(8\020o\311\213!\000\253q\333`r<\201IS\020\217\246", 32, "d\315\226\314\202\321\343\360\351\203\033\343\312\315\245\024x\351\274&\032\034\364m\241}\376}T\253]d", 200, "}1)\202\266\331\2179l!M\277\251\265=\3449\310\220\346\310w\330\210\026\305\251\375*\207\233\276s\277\273\202o\326wes\224\023\3222\223\365\304\375\371K\274\344t+S\314M\217d\320\221I'", 64 },
+ { "\2519\015GJ\241\362a*\252\307\253B+\226\204\226\320\276TlW]xHr\244L\354<1\363", 32, "1\207\307\303\232\276\322\277\037}b\023\256\211A\371\333\220\217\342\216\304\011`\3450\270\207\343\365\277\311\301\351p\002\223\011\372\362", 200, "\354\034\351Z\226\347\033\212*\253\011X\355\254\336s\363\365\257]/\202\256c\305\203X>\251\027A\233\357s5\337\022wW\240A2\351\271v'D\376\205[\246\313\361\375\301\251\307c\202\365tf\000\305", 64 },
+ { "+we\241*9\342\353\272\365\313\366\241\031\272\231R=\310#\372'\322s\014\253y\372ym\314\260", 32, "\230\327\217\225\203D\015\036\364D'2\042,\001&W\310\326*\375n\255\2530*\323\210~\203\361\234m\375\215\2151\342`\360\353\224\224\2510cW\260", 200, "\017\317\2567\134\357G\343 f\3122\011\377\334R<\365j\230\021_N\230\235\006\245\035\274\037-P\012P\225D\024\021Zy\256o\223\242\030\032Y\024\213z\265\012\374c\325\342\2735\370+\274\360&\020", 64 },
+ { "\234T\251\237\350\325\232\3613\253\322p\252\327Y\212\213,\374\326\203\235\037\274\203\2665kO\025\356\012", 32, "\263Bg\330\260$a\243\027$\303M\2553\2347Qm^8\2041\011`\216@\013\011}\302%\352\360\316\352\370\003\220?\321F,\236\360\362F\327\350R@\327\247d\325\272I", 200, "\244\015\341\362\031\363\032!/\221\207\204\325\303\254xl=\267\373\336\357-\322\304o#\314\027Q\015G~\012\211l\314\265\3305T\005\247\324\000\2406vp\372t\352\204u\374O\347\032\010h\277[8\257", 64 },
+ { "\257\330\002\330n\015\300\364\026\251V\016\3308\231\221\207m\003\024CL\271\251\364\324r\036\313[u&", 32, "\240\032\225W\314\311S\204\0351\027\177\361\367eO\224\260\214\274\266Yj\275#V\265\32533\244\00139m\356\256\363\312\221\015u\334\036r\205q\300\024\264\002\311\277Z\271\333\006a\326\206M\357\253B", 200, "\270\367\357\237\004\177T\024\266\332\342 g\261\336\024 \035\243\255a\336\2006Uq\217-\362\372\037S\301\314\314\327+n\252\235c\004\042\020f\311\337\177\211S\266]\346\033\210\244\254\377\333\305\314\007\355\311", 64 },
+ { "\224!/\213\360\357\220!5,h\322LnTY\025{\334XqV\236qY\026A'`\021\364/", 32, "\247\213\203\223K\362\366\255\020\217\2265\222%\030T\265\313:SW\237Y\346\256\324\012\2358\235\257J\276\034\031a`\337\202\317\352Z\017\323\303 \264\042\016\015\012\253\004\264$i:F+\003\301\266)\270Im\234@\271\014\026\312", 200, "\207\261\336J\2314\262\330\025I\012\316}\354|\357\033t\006\361\326\333\232\031\325y\003K\277\3518\334\301\357\365\234\301.w\230K\256\201\266\350@~\340\222\310\014\376\355Fk\257x\242D\374\037gxU", 64 },
+ { "_\005\231\003\225\334q\370mQ\321\252X\031\265=I\224{\343u)\217\201\370J?\364\013\315D\177", 32, "Q,\356e\355p<b", 300, "G\004\232 C\216\210\013!\042\243O\204(B\302\252\030\254;\030\2248\267U\364\304<\006\302\323P\006w\311\364\261\354I\302\036\370jnL\313\2700\273M\306\311\005T\255n\363+\355Wz\270\275\355", 64 },
+ { "\374`\241\254\336\034\021\310z\030\203\033\352|\027\301\242\216&E_D\224\373-\0137\314i\023\377\245", 32, "\020\334$L\013D\217\205?\230\230\264\364@\324\363", 300, "\256\342+/\370e\321\252\275\015\2036\020\307\266\213\026\300\034\353\255\263B\334\305\367\323\276\246\336\346E\000\030\010\246)\2521\267\321\342\330nuVb\214\355\311a,\334\033\267\274Z\033@\012\326#\001F", 64 },
+ { "|\341\014\005(\367\003\373\213\017\321\0056|\006w{t\2053Y\224\202y\230\006\221\017^\330\333\021", 32, "\315cD\364\265\351I\036\0256Y\035\243A=b\360\025\251\357r=C\027", 300, "!D\236+\010\242\301\363\225\3330z\332\347\277\367\201m\355\225\004\013 B\346\376\340\350\255^\326e]\254\367\246\264\2143\260!\030\363]\341l\247)\252\327Z\205Xp\2570Y\233\325\303MB\200\235", 64 },
+ { "\2568\003\323g\134\242o\362\201\313i\372w\372\003LYKt\032\203\3145\257\001Sc% e\277", 32, "\322\325-\363E\042t{I.\033\016\210zJ(\310\264\245uT1\2043W\331\315\352<\356\261\271", 300, "A\200\233\372\306\134\034\2735\352\317D\244\234\307\015\014\212.n\312Q\231I\031\240Y\265z)\236I\374+r\2048\316\263\364\320\205\202\342Fi=\244\205\032\037\007n\211\302\005\017(\261v\042\007\361\257", 64 },
+ { "JU\366K\332-\204l\206\252M\3202c0\344\035\226XM\327\331\032sMmp6\232m\212\355", 32, "\205\300\243}\267\302\364\034\210\246\012\177\342\015\345\337\262\362e\021\224j\370\032z\241.\042\037\013\245\015I\037\326\272\134\335~@", 300, "\214\355e\010dE\015\265`\2106\023[\322\361\366\1341!|w\007\024\235\217\036\005r=\313\3310o\345\134~\327l\255>\312\232\311\270O\333\024g\301vY\364\032\351\352Nw\320\247!\241\367\003\006", 64 },
+ { "92\200 \224\243\3026Vp\376\270X,\300V\315\265\270$H`\262\324\271\337\320\253\367P\2525", 32, "\360\371\017\302\303~\016~\334\013(|S\017\315\2465\264\227\3060\334\014w6\235C\256I\346\022Z\012O\023\221\262\252\355\237\346\323F\3111>L.", 300, "\274\0251\006\345\321\275[id\316<\370{\026\210\350KjB\324,lS:\345O\263\301\355J\364\346\271/\3262\223j\347\231\245\234\022\241\254h\002\265\234J\275\2521V\325Xg)\215\300VFN", 64 },
+ { "\224-3\372E\373y\366q6\375r\201\250\276\235\212\276R\275\347\317\331I\2572\031{k\354Sm", 32, "\335\213\324\011\243y'\302X`\317 \300nj\240\002\355\177\322\003\350\004\250\3265\333L\210\134\354\260\327Qty\007\033\337\350\234\216\340)\230/\321!|\361\2669\246\335\266\360", 300, "\240\270\256\340\027\016\010W\030L\305\234~\362\011C\234\022\267g\207\301\246p\216`\234>\245`0n\231\326\303\032\2464.\215\310\347\236\237i\220 \336i/\275q\315\205\034$Y\307\2310\326\276\252\224", 64 },
+ { "\347w?8\316\356\356u\030FA\011cQ0\032\301{\322z\364\007\344\244P\003\312t\271\370\022g", 32, "\270\003\340}\246\274jA\267\033\366Z\275\177\227\331\205%v\323\260-\012\340\015?t\2752'i\302z\212\300\332\317\231\234\220\015\241\035\3523g\025\263\244\042y\340\274\322a\252\005\251\336:\275B\020E", 300, "v7\377au\016Z%\270\215\300\011\237\324S\021\207\014\357<\335-p\306\034\224\362Dn\331cK@Ji\354\217\324p\324\212\310\313Ho.\224\340\031/H\373*\335\033\357\253\037\222\204\234\265\037-", 64 },
+ { "\255\002vG\234h\247N\257\033\370\323\353\003Z\352.\361\373!\003\304Z\374R\012\265\255K\273\223\005", 32, "\335'+\321\275\250\200\315\216I\235\253\307\266\310\357G\004\372\301\311\031\352y\004\247\252\277\325$-,\340\352j3\331\042\360S:}>\276\313:%N*\226\273\311\264\363W\217yBLK\225?\343W\020\360\261\347?\267\344\203", 300, "\212\336y\264\270\212\272\345y\231nX\323\343t\236|\334\370\346<\347\322[\013Z\036\200\267\333t\014\3361\347O\210\357\206\031?\314Q!\275\310)%_\325T9\376\204\260\241\223\003\363\334{K\360f", 64 },
+ { "\257\324S\205\231\324\023n\317*\321\373\341\307p\302\300A\251\304\014\325\276&p=\307#\341\220\274\000", 32, "\203%ft\036\202Iv", 400, "5\213\375C\213\233\346\347\003\243\243\304\255\035\213xV\007\275\042\013\265\015\231\267\322\021~rZ\271\227~\010 T\277rS^\326\211\305\032]4\023G\224K%x\005\312\3131No\177\230O#\0423", 64 },
+ { "\234$\001\017m\223\035\315\265X\360u&\202D&\321\020\216\334\261\236QA\302\004D\367R\302\255\341", 32, "\247\334\314\177\366|5\235\231h\323\217\332\364O\016", 400, ";\015\000\340\014\206\252\307\206\376\314v$\224x/\033g\027S\003\262\270\273bT\036\020\001!\261\003h\314u\003T\014\030\037V\202\005\327\216\324\213\336\020\303Le\3237B\012\374B\310\035\322\320\006\324", 64 },
+ { "!B\007Pw^\277\024m\232\342jH\2722\376\326+?\3568\217WT\037\273\037\311\352\240\007\273", 32, "\374Vhd\263\246\255W\226\015\276h\241\253.\274*\012o\003D1\360\021", 400, "\0119\322\250\350\027\226G\260\203 \316\245\027[lV\313\005d6K/\217}L\353\337\273*\250]J~\223\320\266\331\225\222p\262\027\007\227DV\223\340\332\305|\211\303~N\211~\353U\340\324\261\042", 64 },
+ { "`m\302\221<\360\306\306)\220)\2751sl\022\216\363\233*\257\000\251^m\006\261\367k\007\355?", 32, "\300_\016\250\265\234\3131\241\030\033L\253F\003B|\272\250\021\242z\235\011c\013\231ty\277M\027", 400, "Rb\332\252\340\0128\177\311\030p\330\0265\337\206\212\217\213\322c\240\370\200\016\370hC\012\026HB\275W\255l\316\354}\225\354M\310A(\307\372_\254\260mY\277\032\375F[{\323P\030\273\321.", 64 },
+ { "\020\301r\264\274\330C\264`DH\360>V\370\005\234\013\270\273\223\234\02137\345\275{\042\326\236X", 32, "\274(\277C\310%D\305\035zk\212\360\020\324\324\223\212\234+\312$Y\221\250\006\357\325F>9\212\313n\250\2323\314\343 ", 400, "\276\012\347\225\301\271\344\223V\3001\004\212\033y\001\351\313vU\331\021C\036\360\343!\324\321%\222sh\221\232\313lo\3063\2471\354\335s\256\237.\241\267\323\247\261\340=\272\370c\276\336\134\214>\204", 64 },
+ { "H\325\277\243\331\206\273\317R\005\324\030^,\370t\276j\0003\230~\370i\346\363!\223\273\244d\333", 32, "\367\241s\025\264\023%I\355:^XHr\003\230\177\331\245\347\025\372\234i\325\350-_\177\375\323\370#\320\332\320\311\241nED;\134\370:\012\026=", 400, "5\325\243\265{\033-\344\370u\020\1342B\017r\301\277\134+4\016\001+_T\367\354\012\325\311\257\023\200s \262[7jy,@\205\254\364\350\033\212\223\275\236\332o\031\350\026c\366%\377\272\333,", 64 },
+ { "\012rD\010t\322\202\251\355\253W<\201J\363\312\206\323\337dN\244V\025zG\324\375\361\207RK", 32, "\207\227\350\201v\263\376\206\215\360\221\3046\022\217\002+\022[0\216G\225bQ)s\204\321,\225\352\256`!\250\333\216\364\217\30602i\017\262\013\352\326\301\250\022\244iLf", 400, "\034\033\242g\257\230{\316\363{U\253\0049\000\257\265\264\030\264\011E\320J1<\274)\201[c\252\323\266\353\222\022Jy\366\301\024\252\024m\272\261\307=\2047\371Ril\222/\230eL\003\325c\276", 64 },
+ { "\207xrV%\324mBu\004\356\222\302;`T\347\0040\302\356\324\012\335\015#\324\330\214\335\221\205", 32, "\346)|\356\355q\266\340?\377\220\364\361\022\004\274eTvh>@\231\205\003\3354\234\001\337\260\031\225\3546C0i-\377\3657\021\340\354\225\352@\314\313\336\035\313\014\347\327\320\250\314\276\247#\356`", 400, "\341\017\177\253\177\265\002.\322\235\224\177\244$k\307H\245&fLi\354|u\241^^\0231#\300\317<\316\230\254\0302\264\134\011\016\275\356)\307\374\365\313\324\311\267\246\236\371Sl\366\327\254\333c!", 64 },
+ { "\024\201\3045\325ty\362\270\271\253!\260hE4\220W\255\240\277\377\326\320\202\206t\264I\324\215\204", 32, "_\271\352z\017\375Q\326\256`\372g=\274\261c\367\033#\275\356\0242\315<\220[#\023\371\2636\3131\360:\324\375\242 mC\227\315\220\302\006#\204\273\011\252\245\002\026\030&_\241:T\215(\002\372\213\252\331lX\017\370", 400, "\242\304\265c@\230\004N\354\001\252\263\211\037\201\276-]\015g\365\204\323\252\355\272\3165iB\364\357HS\345\253\303+\006\251g]\370\323\204\267\346\031[#`\225\343\272\201\253Rxn\347l-\3050", 64 },
+ { "A\316\347I\012\032\011\224\206\310LF}\377\212\035q.\031\206\241Tt\346\252\220(o\223\272\325\257", 32, "\011\025\200\201\221V\177\373", 500, "C\275\213\366A\324\330s\323\322\231\342\353\355F\226Z\253T| f\331\134\377\337K\325 ^[#\275\012\231\325\032\362S\371\027\312\223*\356\222\364\226\042\311\006\260s\360\236\034\253\310\357\365\265h\374\236", 64 },
+ { "663\230\3430\362\027D#\210\010\013P\017\224>\020\005\216\272m\006\035\225\355Zly\221\005\223", 32, "\335\374\334\026Y\254Z\211\272\353\306L\354)\251\002", 500, "\022e\035\371\012\311q\335%W\011\312B\377\263=\376\214'\275}K\254\221R\205\321\212'X\221\354\337_e\327\362\372\323\320\311\300b\263\253\273\020\305\302\251\002\266\005|F\245g_?<\361\314P\201", 64 },
+ { "\031\236|\263)\012v8?A\033\332\003\363\2020sLS\230&\353B\031\242\010\245=\027\217\037p", 32, "\012]\202\012\2107`\233\373\265r\217\234\273\213|M|\315\357Yue\327", 500, "g,v\236\024\021\267gL{\262\341\213\346\356B\250a\361\354\276\267-\313\201d\211[\001bu\314`T\036\134\212\022\330o\366_\254\3731\354\024Z\300\272\250>%Y&Y\354v\271K<\316\246H", 64 },
+ { "\301\031\014\272\325+ \361B\361\360\204\343A\230\220l\221\017\337\250\316\134T\351\211\037eO\305.x", 32, "J\244-\214\267\363s8\302\242\237\204\351\274q\204\3478\227z\263\311u\312~\336\317\250\241\341\206q", 500, "\316\205\233\374\320\350\232\001\355\277E\227\230:\372q\230\024\373y\356\264\306F3\2551\320\246\275\025\036\206{\323\264\272f\276\330\1778\262?\235\204\363\363\261w\030\251\242\347}\002\305\223|)\340{\264\257", 64 },
+ { "\364wG\247F\300\250\203\364rB\337\311\272Td\221\207\034\251\303\265\217\014\273P\034\377\342\254\363\037", 32, "\361\134\360\257\305\010\030w\345\030Y\244\236\002ge\324\326\340\276\022\362\335\353\366\362\205\307\020A\232\274\034\240\366\274\310C\242v", 500, "\260\311x\272\213L,\223\333U\363\022\355\366?\231Fr\227\025\253\321\203\025\013M(\033\300\302\226\256\272\344\370\217\034\256o\376\325[\004\035\007\004M\220\005\334y\234O\367\334wp\2750'\032y\306T", 64 },
+ { "t\246\251p\322\247`B\337\225i\007\362\317\201\023\010\263H]\373\344\226\005\304^@z\226\210>\250", 32, "\315\310T\325\200ys9\324G\3320\034J\376\024\366\365\310\351\350\221\362\251u\372\334xiUD\205Q\247\206\276\232\204\015\2034\264\347x\303\270\375[", 500, ">F\372\306+\2150\024\263\031\340\226\215d\350\373!\237\272\263\340\362aBZ\354\263N\377!r\351\307\223\257\215\224\322\305\253j\016\201W\303\3706\177%\262$\212\334P\015uwj#\267\037(\254\204", 64 },
+ { "\351\275\340\351\264\341\017\033f|\233\331pOK\277)\013\354\312\356\211\344@(\320\321\322,\367Z\212", 32, "\360\340\021bp\367\336\246\352\231\244.\232\370M\257\204\033/v\236\226I\215\033\243}#e\362\030>g\361\242'\334\220\177\003t\353<\370\023f\034\354\177\017\267\013\335\315\350\355", 500, "\001\266i\240c\265i\314\204Y\252!\335\042\373\211\332U\303(\273\304m\261\020l!\360\300\006\376j8\2547E\037+\242yO8\002\377]\256\245Q\032}R\343\311\344H\353/\227\3566\270V\263D", 64 },
+ { "\217\025\363\021\024\006\013\355\005N2/\250-+\3704\354M\001t\245\000\233\361j\324:\340\266D\036", 32, "\177\226\351\134a]/i\247`\370\346\243\006Q8\302\211r\021gQb\006\244s\350\273\303\202\220\236\341:\372\026\335_\247f\203\254\303\010\231\035gG\252e\375 \326H\3469\270\014d\215\273{yw", 500, "\270\334>\273H\035}\215\322\350,\353r\212\020\362Hd\033\214\021g}\366\352\346\2725K\022K\217\220z\221n;M5\024\012\310\225Uv\377.\203\260\026W\351\000\263\003\266\017\202\324x\313~/\302", 64 },
+ { "\214k\277\217\215\031\022\204\207\011_E\321BRrkE\020\015\375\223\367\346\370\013f2\306R\217\356", 32, "\307\337r\321\366\245\307A\013\015t\352\362\006?\245 \333\334\264f\260N\250\225\341\030J\334`#\263j\275\231WU8\366\203K\223\365X,\266?n(_v\347\303\305|\316\207\013\042\251\233\232\011\324\3714g\010#C\241\336", 500, "\007YI\203\310+\236\336\020\221YU\351\376\232\266DF@Y\006\226\242\256\270r\303eO'\366?+\323\032\225\345\320\015\227rsge\317\260\342jp\231\372\306nG\350Mx/\337f\2757\0121", 64 },
+ { "\134\355\220\225k\203\013\271o\037N\364\237\343\352c\336:\335n0\213\247\365K\204A\253\005\205\211=", 32, "u\267\316\322\344I\327a", 600, "\263OO\366\217\232\213i\216se\234\231\2740!\311#\256~2\267\134\025\367\310\020\3541-T\223\227\025\010\276\212c~\265Sf1\200\017MO\002\006\352\374\316}\313\032\317\221\007\322]S\224jl", 64 },
+ { "\014\274\263\346f\231?\005\223\3336\036u\301z\361\324\277\351\013\320\244\247_\365\315n\222\345s\023\134", 32, "\336I\3260\310g\341\234\3012\376\205\002\301f\311", 600, "\362\246 u\364\216c\037\232*\037\253\031\335\347\022[\007\256j\303O\225\270\204/\031E\244\253FdC\272\305\233i\343\357\327b\273\241j\243\261\321\255\345\304\365\363\337\277\222(&hL\325\361\011J\266", 64 },
+ { "YC\314\276\211\370\003\272\320\325\232?v\203\001\274\2738qt\256\330\365\276Ad)\256$5\353v", 32, "g\346W\016*\233\311\344_\267D\346\261\322>\031\274J\300A:\355\232\330", 600, "\304\2457j\205\3131Z\021oA\234FI\254(O\026\334N\017\324\206\033_C\375\231$\314\270\212\245aV\035+\220GC\264\035@\241\231\0124\235\014<\334j\257d9\355\301(\236\226\210$\235\230", 64 },
+ { "\207j\363\010\013\313\273v\326\270\265e\260\033\313\3237\211*tQ\366\215\216J\212\235\324 W\035\033", 32, "\303\374\023\372y\363\307\320(w\236\200H]\371\262\010\006\020\361M\030\346\222\361\216S!bc\3612", 600, "Y\301\037?z\341Y\350_\374@\210\324\274\014f\377\317\023\303lOh\032\253\035\332\217|\375.m\203\031\361^\320v\323^ s\031\347\215Z\012Zj\335\206\363\024:F\310\035\211\255\216\301\203\251M", 64 },
+ { "\216PY\256\343yt=H\205\303\011\353\232\340\2630^\021\365\266\004r\001\307wV\251a3k\370", 32, "I\256\322\255a<l\3439N\224\314\377\232J\032\350\351n\356tKV\267=;\304\353\3357P\216n\024\233\342\031#\234\225", 600, "\357\177\2642\031G!\365r\307\365\342\013YA\347$\250\315\324N\263EI\262\250\266\006\305n\241\347\202\036Q-\134M\2663hh\327\201k46\345\373\362\256\333\314\322\236e\340\325\337\322\002x\021j", 64 },
+ { "\316\332=j\346D\326Ma\303\263\332\341\337\005\243\3549f%}\366\323%\351\261\312K\234\234t\362", 32, "\042z\332/\2006}\3233tJ\021\377\266\214\306\314T*\252\342v\316Ru\036\240\341\236\034M\022YY\226\0212@\243_\374\346\004f\262z\311C", 600, "\245\305,%L\261C\022\251\134\323/=\231R\024\350D\233\202\351\235\247\243V\031FL\033\277\246\003\037Y0\375\276\314\233\224\323\350d\210\211\201\254\341 \343\352\276\267,4\335\210'\223\207R)l_", 64 },
+ { "\242\231\024'\371J\340\326\256\221\320\223\177\234\306\245,\035\201\261\361m\344\360\001\276t\254\363\247\260\002", 32, "\305\014\300v\224\372\353d\240P\006uq\230\354\031\245\335\317\022h64\332$\021\350\367sz4\326n\315\257\370r\275\374\334%\011{\305\376\201t\256\023\376\025B\222\266\206\134", 600, "Y\264|\001\365\341\327\351:\014/ftNl\243\365NC\345\032\256\042\343E\227Y\253\177\223*l\331\340h\257\377\206\035\261\134\310c\021\246\363\000\250OQ\211\011\233\007\300\375\207\312\212\351\226\205\312*", 64 },
+ { "L)\226\364\250lr\252\205d\356\000\333\3306\004\323\034\022A\375\033\337\306\345\327B\373\232\306jJ", 32, "_\134\277<e}4\373W\363=\010[\237\011\323~\304\201K\237:k\321\315\004\347\377@dY\270\015F r\007tQ\335\023\377\016\360PR\240\306\0210 \357\234\357\0243\011\222|\037&\344(\323", 600, "\375\134}\265=n\252h\020\275\246B\346z\012\203o\226io\037\202\234\371\022l\352\350\356\216\017+\225z\350\315\244\323\224\314\241\006\275\344\031\273\373\365\303\330\005\362)y\213Wx\265\351\211&\354\267<", 64 },
+ { "\303\217\343`\352]\360\222\315\247\371\372\345\263XT\3216,j\220\240\225*\257\031\225\205[,Y\246", 32, "\314\325\324\200\024\204y\312\373M\244_\255J\322\217\244@\223\306\350)Z\021{v\352!g\273B\360h\357^I\236\343\305,<mB\273\011Xp\267\247\311\312\347$*XM7k*\201\324\230|\336e\300\214\350<\225\006\367", 600, "\236\037\004M\014+2\027@k\240\336;S\303\366\011\024\363 \325\301``\301,o\177x\346c\306\263\354\364\177\352\024*_oHB\007\275\223\036\231\005\231\216&kz\354\255\007\356\237\337\014V\245\213", 64 },
+ { "|\227`~\012\236[\232\253\2406\326\003\213\215\313\255\222>tb\305@\004Z\352H\032\205\212\254\215", 32, "vU\273'/j`\351", 700, "\360\255\227x\024|z\360JY\364\273B\240\037+\027\376\346\207?\030A`K\216Ba\366\241^A\030n\027\363\225\301z]Y_e\012\273*0\222\274\242\313Q\003\201\336\034\210T\361\2268E\243\303", 64 },
+ { "\352J\265\255z\256\252C\363\273\332\3044\351\214\311\012\2516~\226\355\225/\347T\264\260A\330\377\355", 32, "dv\016\032\340<\207.\260\037\354\234Csm\337", 700, "\360|\337\257\240\016)LW\230]\035T\016\001\256\252Ew\222\016\003{yE\025^A\274\377\346\265\370W\361\336`\352\0118~\241\356BW\272\315\260P\240\273\273v\322.P\322\301\177\033`\317\244\346", 64 },
+ { "Q\026o\230\010\276b\336kNrzjJ\021D\302\364'\353'1\202\345\207\203\207\201\034\203\000\257", 32, "lwS\255\204\030M\357jIx\363\301\271q\304!r\215s\356\322\340$", 700, "Fx\263\350\210|\223Thrl\326\215A\366u\2168z\225U\255\314_v\020\017\314\025U\304S\023}N\021\363a\314\267G\2444\205C\200\011^Q\253{\216}[zfQG<\232\242r\314\334", 64 },
+ { "Y+b\271\212\342\325\025\343\277\012\223\0121\243d\004\240\342<\205\013f0k\307\254\335\014e\364\261", 32, "\343j4%\254\352o \0116ksr)-\341\366\366P)\021\362\305\305\003T\204\302\317\312\313f", 700, "\016r\343>\207\376\003\177dw\037AN\262\334\017Y\236\374L\253<,\226\027d\317;\250`\216\326\334d\251\355\300i3eV\241Iy\220\001\356\035\364[\303\220:\242|eN\313\354\3039\3309\272", 64 },
+ { "\354\261t\321\217\227\3512\321}N\214E\257\251~\006\014\372\200R\017\261\007\021\220\371\370@@P\222", 32, "\277\301+;*+3M\352\373\365\246\304\356\250nf\320-\241\325\244\306\357kXYX\325R/u\326*\277\265<\200\360\270", 700, "\265%\225}\007\257W\255.\306\000\201U>\271\330F*\253DD\317\021aj\374\223`3\367f;\026\010\342f\0150o\232\303\015\377\017\205\357%\277]\277\216m&\236\344\366\313?\373P\214\223\000\324", 64 },
+ { "\310F\324\335\367\301A4\252\337\342\331\016\000\241V\341>\237\034C\204\301\276\255e\276q\341\3778\301", 32, "\214\222\134\343\224:\336\012B\323z\310\262\021\205\225\216<\313|\227L\267\024^s\316\257\014\2259\261\371tB\242\373\366\232\374\353\336[\343\026k\307\256", 700, "u:_q\370\012\235@\315X|\367\377C\217\353(\3051a\251\001\003\226\205\342\227\212\306\337$\274\201\212P\315\017\221\347\006\021\230\354\266\200.v\213\361'\320\227\331Wi\034\374\346)\332\273O\372\225", 64 },
+ { "o\210\323\375q@\013U\011)\020\252TE\206\233\306\2058\324\371\354P\374?\212\235e;r\207\261", 32, "U[{7\230\215\213\276\344\336\223\322,P\214\360\205\007\202\276e\005\025H\2048\212\247Vq\232q\303\272r\273\001=\201\261\265I\311\266\323\206\203\207J\212\323\200A\375!\276", 700, "\211B\011\227\272\0275k\016\015\266\202B\0301\223\226@\037\005\377H#\302~B\020|\002\226\015\242C\342\273T\306\245\376\307\320I\301f\021f\316[_Q\356\210M!\271m\011p\027\361x\316\353]", 64 },
+ { "\033\310\253\302A\013u\205\252\020.\233;\372vs\243\275\351 \015G\220l);\265\245\362c0[", 32, "\306\233y\253,\303\222I\343\335\211\242\243\026\320\253\177\211x\215\033\234\177*\333\343~\215\364\333t\326\005\240\256\300\362\015\022\344\212+)\237\277k\354;FFL\323\373\2342\022\001\254S\353\265\253\323\363", 700, "(#\342K\312\320\243\035\234y\366\000\333\023\256\305\042\361u\035\026\201?\252}Y\015=\203\332\235;&\344\027\235f\353\252\235\257Z\042k\377:\346\365l\236\335\035\274X\237WL\342\237U.y\003H", 64 },
+ { "\003!:\341;,\031hr\233W\265|O\015!eO\352\272\355\210\304\360\3318q\004pw)\364", 32, "\317\243\314\370\315\317\025\017\002\273\020\344\354\222^\007\217\245\336\362\002\307c\207\376\236[\373`\257\226\332\025\202\246\010\230>\311|#\273\134\322A\364y\354V\021*=\344\326jb_:Z\221\214\203\011\277h\042v\212\211\241\303r", 700, "\252'\212\362^\0326\2114\216J#\367z\024KZIf{~\375\345C\302\307\236\030\002\262\346\012\033\317\272\230\244\320\220\272\242*\021\327\240>k`\222\263DT\231\204';\301i\027.\302\223\320\236", 64 },
+ { "\321\334^\2313\347D\003\200\003RyA\002\241\321b\264d\337k]\005\260\265\226\001\017\3361$\341", 32, "6\327\246\342\376\260\013\303", 800, "\304\350\334c\025Q\240R\263\243M\320\011D\351\230\375\257Q\134\346V\303\267i\362\221\277?\323\363\207?`Qx\023\356H*wm\032\343F\017\304\033\327\204\242\2064^\214\204n3\000oqF3\347", 64 },
+ { "<\204\330om\325\200\322\261\262\343\010\364z\017\317\223\366\313\311'\263\344V\270\134\2008\005'\267K", 32, "\015<\014\200\270\270\134\036\277\352\025\350Gk,\301", 800, "\331[p+\273\317\247\205\026\353\262\311\226D0Q\273U\014\366Rc\371\346>\255\206Z\253\260\267\226\200\321\357\134\3309\002X\014\015\241Wu2Y2$\331\265M\267\377\262\255r\252-\021d\201\341\217", 64 },
+ { "\206\362\247\200tY~\344\264,\2113{Myk\366\370\3025\310_\343g6 \261nK ~\310", 32, "\323\372Uv{\243\026\035\350\261\323(B\257.\334v\024K\016\334\227\034\266", 800, "]\275\033\351\007\357\036\375\264\375$\360\322{\213Jaxg\027\21729\251\357\274\246\222\267K\271\351\237\321o\2451\035\310\2423\267|\272~\254\222\030\356c\003\240\337\373\231\033\270\340\277\240\3505V`", 64 },
+ { "y\207\206\361\236\374\200\304St\364<fC\306\355 \306\234\022\223:\3310\342\030\214\234\275<\356\000", 32, "i\236X\220\025\3665\010J\230#\007\227\017Z;H\2152c\246$9\347\222\215\200y0\015P.", 800, "\234P\243\025Z-\322\006\333\226r\260\336\323\231\374\204Z\012o\320\335W83\020\205Z\344H\207\332\331\331\215;\322\031H\267j>l\263z\244\375\270!\355\334\370\205\372(\027#\320!xij\313\314", 64 },
+ { "\200\364\370;\271\323\015f\203%k\217\262E?*\305\243\211S\370\010fV\255\271IA\312sL\303", 32, "P'\305'\353+.\324[\364\241]\231\231_\215\357\004\345\322\2315\250F\335I\3471u\026\325{\204\241l\235\365\233\357x", 800, "\226v\235\300y\206\023\337\027\266B+1\377\312\214/\027\343m~\036\361\222R\024}\337S$\003\001\330\317cW\200iKJ\362sy\227\307\2670S\341;\252dE\343\217\375\270\206\373,1\020\352\312", 64 },
+ { "ZN\344\3253\000'\217\224\223\206\257\3662\321\235j\204K\236\264'\312\030\327\276*\363\001q\272\223", 32, "\246\017~\226\231#gE\374\224\224E\374d\367\301\027\336XL\242+-\247\227\020F\215e\354\020i\272\214\271F>v\030\230t\007n\263\301\275\231\214", 800, "\332\003m>\274\2375.p0[\0234\037\245\256\227\2617%2\374u/\201,\316\324\200\323\225a\242\023c\332\233\211\342\254\011\230\032AV\235`\246PC\222\301\222/\342\354\261\251;\3505\357ey", 64 },
+ { "\371\374\236\365\2432\210\021\205\300A[\365\017\207\2611\252\236`\015\031\205\326E\324\010\253\037\312\267\304", 32, "e\316\2516%\025\333\014\300QA\220O\343\366\001J5\251\353\300\262\262A\2748-R\025\027\263\227z\231E}Z\207\017\250\365\030\001\344\231\376\002j!\375\317\206 \347\3046", 800, "S]:v\220\373\206M\244y$\240\260>\030q\335+\206\205\305\361\251\326\0039<X\246f\272\026\304\346\373X2\321;\013\252\233\360\351\242r6Iy\230x\010L\336-Y\211\234\306$\346\344\241\300", 64 },
+ { "\311\245\037\240\343\023\324LW\311\013\250\035\275\332\230G\317\255\267\0206\374m\275\375\336\005!\342Q\241", 32, "\361\016\341\005\310\206e\336E\256\223\266N\236L\033\035\3649\261\260h\202\303\2130\024\250L\036M\266\257Y_\203\211\311F\233\306\272\304\327\022!\267w\037\003\357\277\356\377NP\237\263\367\313\233\371Y\236", 800, ">\332.\034X\250x\004O\016\221\022\027\001\022\221,r\377F\262M\307=\033\042\341\312\014/\024v\224\265\200R\363\223|\360\235\200qp\225$o\201\020\2500\375\024^\217d\347*\355\257\211L \007", 64 },
+ { "\206\307\0424\261\034\255\242\033\233\205VS*\003W8\226n\302\042\263\366YL\343\033\256'R\007\331", 32, "\035\241\217\002\364\341\305\313Cu\347@)\020\376\336\372\375\257Y3\202\237\266\365)J\270\351\265\310\212v$\201\207_@u\013\301\177j94\202\304k\345W_\324\310z\215_\304\2554U\236\245\314\330\313r\271\001\335\0075\322", 800, "\237\225\217\301\200\264\242I\240Z}\303\365n\227ps\365\302\013U3\373\311\273`KQ\354\360\335\212p\227\245h\332\225\374\375\374\300\261#[\240\246y\212\013\332/\342\212ns\243'\243\036\023\375\002[", 64 },
+ { "d\375\042\033\372\337\277O4\004\270\037G|\234oY\032\214\211|6\374\374\207\221\205\261Z\034h\362", 32, "\332jcz)%\345\206", 900, "\312\233\266M|)\265Y\277UY\323MYj\034\203\011\353\215\361\247:(/o\270\321\362\034&\302$\2402\277\260\262\371\231\030\204\373\230;\276\371\000\323\302\025\314\027\205\035\2404\257]Kyl\363\203", 64 },
+ { "\272\211\350\227\343N9\013\232\254RC\003\362)-M\324)\211\002\343l\331\300\177`\255\373\017r[", 32, "O[\2002X\323wW\374\204vg\355\270\003t", 900, "\243FM\260\206\333G\331\341\347\337\336\001;rr\306\343\211\360\272'g\207>'\224\334\2664nLA\311\221\321\030\024\015\036\246[\224\364P\270\223q\221QN\254\272+]\246G\337z@\211FQ~", 64 },
+ { "x\357\024\247\310&1j\252\035r\024\301\217\241\012\336\300[\342\201\312\335<\307y(\273\321Wp\323", 32, "u#j\303\340\326h\242\367\023&\377\334M\201\202Y\342F\205\241\357\220\036", 900, "1\343\316\341e\365\233Z\355\313\013\021\302@\027\274\223\342\331\367\343z\353R\276r [\273\344\332~\015\311\255\324\325\014i\266dH\357s\3040\304\002\355\257\274\022\212\346\27511\300\272\215\210m\042|", 64 },
+ { "Y}\011\134e\265|\3432\274gZi\334\340\320\011E\360\353\033\374\373\004\023n\025Z\325j\036\003", 32, "~\034V#\3239\346\017\020m\212/\203\370\372'\341\005\021\246\304\341v\352\275\224\036M\313S\211\257", 900, "\377\374\030KE\325Clh$K|\350#Ns\267X\3442\031%\270\012\024\17744_P\317Y\244\316s[\240\312\347F\305Q\322\021\013D\350\022V\014\2677\017q\306\361\222\260\306\037\257\236\010\263", 64 },
+ { "\301\271z:r\325\332&s\200|W\361\220\270V\327\337\376\266Q\343/Z\224\245\021\226f\012\227\321", 32, "\1342m}<\220\234r\367W?\235p\234k\216.Z\260Q\304\212u\006\323\317\3103\300&@\257\354\276\245\311_9\004\252", 900, "\351\010\311ad_\347\271\204\243\007\377\311\236C\027,\215e\200\030\024=:B\253fp,1C\221\311d\214l \317T\027\376\030\320\204o:\362\235h\270\366\353\001eJu\004\202\300`\215%\355\201", 64 },
+ { "\241\371#\267\251}\342\250k\275/\364dhP\000\340rX\010\006f\241\371\377>>\032\370\265h\253", 32, "\333/\273\006\213P\330\305\304\357\357\324.A!\002\250\210brD\302{\026n\007\350}\327,\371\260\240\322\253Q\243\266\254\037\001fm\233\257Z\315-", 900, "\030{p\375!\357\134*\353\017C%\217C+\225m\005_\206+\004 J:N\370\367\2041o7\241\236\222\261 \313\222\177\340\134\376r;l\224cC\035v?\022\316\321D \372K\214\317\216A>", 64 },
+ { "\014b\217 (,\360\311\026\343\360yQ\215\363?\030\3467f\352\251%4\361Pt\037\261\371\014b", 32, "\032\257\353]'\003&l`\315\012\274\356L\346\001\255\303\3150{?/K\270\305\346\2311$O\244C--\351\004\206n\327S\217[\134\274c\372P}\243\355A\354\266\207\006", 900, "\007t\223\367\034\352H\312\205\030\314|\272\240\360\362\006\002(@\261\224\004\225\204Q,{qhp\240\271S\340B\015C\376f/\370B&Gf\024\022\312M\262O\316\177 \325i>\274\377E&\220\220", 64 },
+ { "=`\230\314\234r\206\304\134\367\363!\023\2774\301\323\026\011\366\265\265/(\343!\007\335\242\214P\012", 32, "e\024\351\002y\227a\260\331\026\213Y\267\251\310*\230\030W\214p\255\230u\001j\3704\315\375MSX\030c\336\306w\325\346\262\2640\255\2465\253\3334\337Nex\234\334\216\316\314|-\257\017\022(", 900, "t\221\347C\244\016:\216QD[$r\347\227\325\256\343P\203.\012\277\3216N\241\351!\037\211\322U\017h\021\042\352\030\221=\317r\306 \367G\372\270\214\004\000\207n1\3646\344\034\371R\215K\305", 64 },
+ { "\355\361\227|\310\247\256\014p\026\017\037?\364\277\355\314\030\236\306\032\306\240\321%\262\256\265\244Y\216\252", 32, "\263\354<\350\215\312\311\366\242xX'\234\312}f$>Ez\327\023x\320\355\331'\372uF\231\257\251^\034\364d\230\022\304\245\322\261>\325,\273\357\303\311N\3374~\323Q\014\342b\342\025\226y\307\245\026\231Q\266dm\215", 900, "\325\361\300\010\312\311\037\017Mh\310]\254@NC\324\205+Y6\027\362'b`x\353\007\211\035\264b\005\225|&\321AH+W\007z\377#\361\274\265:\371=\257\225\361ec]\315\203Jv\203R", 64 },
+ { "\242\341U\355j\004k\356\204\330\351ufb\327\337\357A\223\341\233\3668\220\352\134\247\202\373\006\310\323", 32, "]\331\327\016\205\272\246\206", 1000, "?\230\007\216(\272\307M\015J\344W\242_\233\230\2201\243iM\254\362+\254\333\3658I'\335\241\377\2061\013d\212T<\327\201\257*U=\014\226\321\251\227y\370y\016\216=C\363$j\272\241\365", 64 },
+ { "\017\376\364^4\254p\016=\353\344\366\352\006\227)\030\357\004\263R\373\177#\211 \335V*[\344\257", 32, "\3722\017\275\232\262\323fcgiW\232\224\262f", 1000, "\373P\274\357\005\026n\3021wkI\355\325\026\376\021<E\344\0372\020\277\014\351\314\011\312J\317\3756\321\216:\353\270\271+\003k\020\275\022\370j\3327\3621g%\362D4V\205\255\326\311\214\246^", 64 },
+ { "9\243\362\223\261\241\356\331\2720`\272\252?\334nJ\263\210`\347\203s)\023\315\005\354\340\232+\307", 32, "JFS\355;\213\234\354\267\337U\326\012\231H\355}s\316Mc\371\335h", 1000, "\343k?& \367a{\302\031\024T \034\223m\134\217\245\015\250\037_!\134\317\346Tr9\025\206\250\032\366\274yg\243j6\243\370\236X\365\0050\376\357\302\224\006B\026\034Sa\200\262jg\224\246", 64 },
+ { "m\363\264.\272\327\200\261,h\203\315\002\005\017\205\201\217\252\020\2113\363$\262\177%\360n\326\030\010", 32, "\025\271-\275,\260h\337}\257Z\021\213\330m\354\220\370\216\211J\335\206KY]\027T\226\2063\363", 1000, "\215psKGX\266\370\313\377\372\340Q\013\2700\034\273U$\033\022v\326r\015D\315\365\340\331\2533n~\010\342Jj\356d\351?\011Xr\306\277\374\002r\350\325\234m\322\223\275\042^\042]\022>", 64 },
+ { "\042t\014\315\227K\273\323\237\220!\270\0172O?\2645\030\265J\325F\343!\241\344\323\301\241\024\272", 32, "\316\027A>\233\225\212m0\037Z\277d-f\017\200\303z\334Y@\261\230\254XT\227P\301\260\014\242\266\244\262f\334\365\324", 1000, "q2\353W\236\364\266\204\340G2\337\224;\220\223/|\2132\202%\215+\362\023\340>\210\360]\217n\374'\353-\010\217\000\212U\335,\012*_\325\001\370\037\322\007uCu\037\331\242\212eb\343\035", 64 },
+ { "|{v\001\223\245`d\005\263$\321\225f\344\340\010,\305\273\025$\370\275<\315\373j\304\226\350\233", 32, "\224\263<\367\330\351A*\260\300\235\337))y\221\367\306\373G\324\266\227\372\027g\013OH\321f\326\025\333\334|\013\002e\005\025\205\004j\212\206\244\216", 1000, "\341\371\031\340\344\313\002M\255\020I\333\324\265\242ra\371mhr\017\214\366\274\337\200l\336R\305\276F\024\264\035:'Hg\231\245\217\350\306\251\376\032\310\010\311\250\013\354Tr\017c\367\241\300\031h\211", 64 },
+ { "\303\341\317.\250\305\257\235F\324\011OjZ2\312s\224st\2041KS\233H\022gm\354\276<", 32, "%\264\212\004p\266<\314:\376\324\250\341\3151\251 F~\223\267\032\252\245\370\001[\257~v\363\242\253\240g\042j\306\233\336\312\042\345\257:\266Rc?\011$\344\313\267t|", 1000, ">a>\272\354,h\337\205\232\204Z\212\230\257e\307D\322\263\2539,\264\271!\034Rg=\201\334~\201\252@etE\326\241Q3f\205\314cc\237\216\373\272>,\0112\010\273 \307\332z\223\274", 64 },
+ { "\211\211\354\215f\332\255Z\340\337P\031\217G\266c\200J\323\042\361\250[\210\022\007R\341A\017xD", 32, "\256\303\241\352Ij\317\253XL\262\221\031\375S\343\275\225\301\033\356\264\332'\014\004\326\230\036\275\002\325\260\301-2\223\247\0041m\251\210a\356\251\274\324\204\361AP,\312\025\247^\363t[\350\223WY", 1000, "\311\035\334V-zy\017\370\244\203\300W_f\2177\002\237T;\301\261\221G[S.\037\2030`1\321\010\300(b~\371\217\034\301\276\250\023\2462\342\374\332`\372\204x\035\247\012\237ju\275\224\362", 64 },
+ { " \313\304\230\206\234\222pk\244\005\2711u \340 \223\322\345\002w\275\020\324\316\224\325\036r\223;", 32, "\372\344\357\324a\333\254c\215i\177\320\300\242\020\025[\005\3252;b\277&@I\270\015}F.;\242\304!\177\343\012=U\253\253o[qR\371\201n\357\361\330\277\253\346\177^\332\042\322$\201\377\340.IOk\273\363\005w", 1000, "\310\012_\254\372k\220\263\272\361\134l,\345/\371\371\357\237\301>\205\237\237m\036\272~^L\355\0210X\221x\331\0256LD\264(1f\357\014#?w\034\023'\210&\355\261\235\361^\004\277\313\014", 64 },
+ { "\373\253'\357\233\227]\260\212\305\037\273`\364\001\032H\177\203\342\012\022\225\2572o`\240\277|\275M", 32, "U7%\340\031!\012%", 2000, "\024\2525\230j}\315\267\244\345e6oY\226\027\347\017\336\134\316\355*\252F,\034\247\267\270'\017\245\201I\2735\205^\205!\205\312\221\352\216\270\274\222\276\011 |-E\301#\240ifGA\375\210", 64 },
+ { "\207K/>\226\027\221N{?\037s\003\323&S\243\355$R\361\254Q\026R9\2540\231Zx\374", 32, "\314\260\226\231\312\037\245u\230\345]T&\037>\375", 2000, "\010\267\374\351\370\303s,\323\030N\244\353\252e\275\205\253\206TL\340\3205E\004\253\204f\220\362\330\231\037?\204'D\274+\367\363\316\340\340x:\230\254\313U\016\347\023|G?\363\017\037M\177l\321", 64 },
+ { "\215\3425\215\223\0224\311Y\251\213\337\240nw\233\343\320\335\244U!\300\023\023\347\012w1\222\011\263", 32, "\010uJ\261\030JR\357vV\257Wd\012\004i\257\351\360\266\303\200/\256", 2000, "\211\243QK\231\376\321\212\322\350\321*\245~\311+6S\013E$\351\217\227\224\217B\006\230\212\251L\264\245.ZJ\350$!u\000Q]\021}`\022\370U\364\224\215\274%\353\333Ys\177*\265#\314", 64 },
+ { "O\2649\000z2\177w\372mk@\345\277\270$\231\330\246\001\334\350\271-\264\343\307\342Nw\231\221", 32, "\201VZ\223\246\217\224\263\241AO\325\367\251\031\322\221\200g\354BM\2600\340x\276\307\246zIT", 2000, "\316\341$\022\261\303\322yZh\227\205\310(\347\233\326g\031\220>\011\226.\352J\242\311\274\247\211\010\355\336\220\214,\2440\214\311\241\253I\042^\257\361\336\241\360\177UB\356G^\325\203A\3040,\042", 64 },
+ { "\177\016\252\362g\010?|\0168\364*!(\042\343\015]m\253>\012\012/\3471\205\015\321\014jh", 32, "%$\263U\023\275\273\230SxJ\363o\373\241JK\234\252<)\207\026\302\267\215\016C\357\014V(\021J\336pq%\360\214", 2000, "\301\365\314\263\264K\025\305[161\030\015\211\207\0358h\020\231\042\351\031\323\253wC\320\330\351\205Nm\216\024\033\201e\324\037\354\006Z\331\305O\335\272\333\233\243\262\271b\023/\342\320l\177\311L\000", 64 },
+ { "L\027CQ\252\262\232\343\237\205\245\344C\014F4\134\210\345\213G\256.~;\240\252<\332\202(\032", 32, "\313\376\250X\267T\235\320\236T\242[\255'\0117\207\332 \2130\242kz\246}\336\321\022_\316\377\323}\340_H.\001\337\327\205\372q\022\355\013m", 2000, "\302\002\343\242\245\365L\016\366\205WD\242\015\330\375\300\357\357\020\206\344j\237\022\002\036\247\265>\350\036\212\027\242\325\2135a\320\312\344\330O\366\335=h\253\330b\216\221\017\031\026\027\233 \260\254\204\345#", 64 },
+ { "\013\207\005\265\235\252\020\354d\367\342\006\341\2004\311\021l\000\256\246\241\342v\342\026B\225\333&\214L", 32, "xW\265\230\242\345\002d\230\216\315\347!\232\272\3751J7\3271\011;-\3309:\207\374\204\244\203\244\333\026\011\344\366\134\364j\324\004y4\347^\005\313\202)_\030\217\236\355", 2000, "F<\236\376\203\266\204\306\361\367/\001:\220\350a\366\035\326\374\332\244\304`\202\367oA\335\331\013\330\204\361\353$\347\206\345\231\005\231l\215\013L\351\302\267]\244\006\335\374G\342\311)\245q\301B_I", 64 },
+ { "\241\021vc\346\211\270V\212A\302t8\233G\232&c\001_\263\211k\232~\241\001</\001\362\022", 32, "\246_\022\207>\305z;}\204\371F<\200\350\260%E\036\374\013Nu\200\242>\321\234\316Y\232\375\220\036\243C\250)N\375\341\3775\336\357@O\232\304\225gH\351\270\316\003V\364\003\206;\352.\320", 2000, "\360\350\222\354\320\364\042y\245\343W\2413\024\250\221\377\245\325GH\277ZAu\274\026\265\275\231\304\2722.\010np\217\302f#\346Z\347]@\216a\302\177F\233k?\326\337x\301}\230ek\300\332", 64 },
+ { "\030\344U\2107\244N\237\230d\255\376\324\213\343l\224\201\325\027Xb\201sH\200\245\263\244\035\005\300", 32, "]\371\274\014\217\177([--\347Z{\021\350\330\301\134\255n/&@N/\177?\2006\2026\360\241'\3025\215\362\253\322\027\034\201mA\353>3\247\366d\327\007g\335Z\367|L\026\234\301\340\016\325\204\261\244i\036\016*", 2000, "\225q\223-\325\221\257:\351t\266\032n\250\357\002\350\003:\020\242\302\236\311\245zi(\333\010B.\311\256>\265Uue7B\030-\243((%\332\362\303D\325\242\314\243\321g\264\201\331\001@\207{", 64 },
+ { ".\356\217\343\212\004\001a\027\213\207[,\042\231Xj\221\315\276HB\037F+\372B\251|\265t\026", 32, "\330R]2\031E\351U", 3000, "Z\271\372U\365\246\007\342\272\236\251\271%V\347\027Q\303\237\356\304\242\220\231\026GJ\011\031R>\252Y\223\014\017]\311\365\265>8\370\211\323\014\2027\225\304\030\273\000[Jj\204\015\025c\211\302\361\331", 64 },
+ { "\333?\003\246\275\035\007?\027Y\354w\012\344f$\3309\030Rf\263\366\306\350]\332\002\221\346/t", 32, "\361A\234}\315\361C\366\227\177h\325\235'\001\205", 3000, "f\367\374\346\006B\013x%\010]\371I]\256F\042\264\334\233#'\013\271|\347\355I?#7\266\257\253\353\027\217\025\276/G\334\020'\271\004>A\033\233WM\237\377S)\347}\2357\252=\277\313", 64 },
+ { "\334\276\227$\003\200y\2567l0kC1\3330\310*x>V\273\021\015\305\246=\250\353\361Y\221", 32, "\364\023\034&\371R\363\231\003\342\350\236\0131\333u\266\314\327\266-8\243\374", 3000, "(\004s\333p/\003\201\333\376F\024K\313\004\374\371\310W{\037\313\377J!\304[\303:\3329\340\200E\204\177\212p\235^\221pu~\270\361Jvl\267;W\225\034\311Lm\371Yl\301Q\032f", 64 },
+ { "\221\326\310\317\321-OH\334\006\007P\374uz\213\300\226h\231G\2220E\237\235\323\030\235=\316\267", 32, "ZO\241\0178\261\034\025\311\353E=t\353\331\003\364\243}\005\343\364\006\340\024t\341w\310\303\235\035", 3000, "\363Nm\010\373\315G\216_\354\021\200I\242]\014y\026u\301\346?\273/\304\213z\264\231e\322\304\010\217\205\303\204\277.\234\371\22699L\242\342\313\027\210\316\026z\252\215\235\307\347\361\002\306\037D\302", 64 },
+ { "\032\370|S\365\270uTK\326O\353r\255\271\034#\327\307\370\242\262\374\266\312\263#\277J\215\251\362", 32, "\301\225\303\340w\312\303}~\011+\261z\216\320\220c\237\250\037\011\253\300%\267\011q\224\266z\271\025m\266\361|\341\374\301\007", 3000, "\364\037VE\343\326\327v\222\042\300\250\272Mr\036\023\270~o\316\314?Z\321\232c\261\374Rq\020~\032\344\333\134\272\223)\035\005Di\205\345\012;\3076\227\205Y/Z\350\016\236\317J\253\323`=", 64 },
+ { "\245\223u0\342\000R\223\240\033e\340**\244\254K\302\031\363\177`M\240$\247\361\317Y\220\227\344", 32, "\372U\371\002\134:\304\002\023\314\037x\372\231\375\241\210\360\224_}r\334\302+\372t\2741I6\035\232\226\036\336\012\211\316\260\205\220\3374\316\256m\343", 3000, "\273\361\376/\034`\322Sy\031\333*\033\134U~$b\362\363\236\361$\313H\022\243\203\306\352\314\375\204\347\223S\246\314\230jB,%\241\000\335\237\364I\000\215\266\300DR\211`\322\271\306\026.\215\341", 64 },
+ { "\273G\256e\220\336\234\0325]\177\324\012\204\230\031\322\023<\020~;\347J\355\027\244\032\342\304\340t", 32, "A\350>\021x_\311\275\017\322!\315\376\357\177t\236\001~\355\315Tg\344_\012\002j\2454\002 \233\012&\212g\224\221m\242\315\353\250e\322o\004L\005<)\220 \177R", 3000, "S:z\324\320\361s\320>bW\326\0118\026b\316[\021\265\375\273\021\343\031\205u7-.\307+\360W\237\321\236 z_\347H\256q\255\022\242%\2746\032\251B\224\2446^\266\261\177|\272\010\246", 64 },
+ { "\367>aQ\260\374\262\260\020\201%\260[M\244G3\337H\200\025g:\002\275@\373H\364Q\030x", 32, "\321~\273\020-L\224;2\342G\223\025\235i {\256\342 =\316e\376\014\215\221\034\252\235\25762\007\210\3158\257(\235\277i\350\272\011\016\231fb\007\243)\307\376\2409\234\011\034\207\262\307\026\230", 3000, "\307\312QT\215\034\320`\314\352\251]\210w&\203\004\2716\352\251\311\311\261\001\220\276\224\301Y\020\241\245\364L\305\335NP#$]\251\023}z\304\351\304?\214\000\354r\323\000\300\220\003\343@Q4\366", 64 },
+ { "1\211\332\336\2713\223\3103\310\247]\376\015\275K\036\233\266\036\275\362\211\343\013\312\134b\306v\227\272", 32, "\134\253eO\347\267\226\220n\360+a\374\362a\314\202\304\221oy\275\020\367\201}:Eu\214\303\2002[\2270jFO\245\237<\241\323N\032\200>\314\330\261V\342\364\361S\243\215.H\012gTu\345\212M\026\024q\236\337", 3000, "m\324)\245\253\323\022\134HK\340v\314\270\315)~Y5y\270,T\3453\366\376\360 @\234\362\006\012\216\210\216.\360G\233.\326\363\253\006\031\277q\303&\215O\203\257\214\002\037\223\365\023\022\303\361", 64 },
+ { "v\352_\013\314\201C\025V\042\015h\374\375\272=\361\320\337\302i<\264\275H\002\2622\321\3266\265", 32, "\361H\3748\322\242\2249", 4000, "\025\332a\365\371\230\207\270h\260\011d5`\307%\235\311\354\337r\012P\323\005\214\330\202aT\356\202\225(Z1'\237\246\177:I_]@\327\317\332\266E\241\233h$\312q\217\325\256\230L>\353k", 64 },
+ { ")2\223N,\364T\011\236\322\2671\214\240\220\306m,Xt\322\025\241\013\276'/\027\022A}\367", 32, "Pv\327\262\275\253WCP\010\004\341,\227\242\263", 4000, "\206\345\207oIg\320\275\200\246~\243\233S\303\330a(\301\367;wh\212\016\243\335/\034\202\201yN\352\200A\261;\226\277\222\230\256\243\342\021\340#\006T7=\324=\013\231\376\255\216\025\251C\224!", 64 },
+ { "A\375*|\023?\215\251\2717\204\366\2310\356\004%\206\331\326]\022-\234\372q\243\371\231\314\207W", 32, "\031\003f\276o\314\232\2038@\307#z\312\222\376\007\376\351\346[g\005i", 4000, "\252e\003QJl\010#\235*5+8\210)A\251\250\032<6\003\215\221\364k\211\233\312\225\356\026f\220\254\303H0k\201\2565Adv\236\247Dt\3735](\3508\2602\315\035V\240\247\013\266", 64 },
+ { "_\026N\013=\247\000\330\377\233a0!\007H\0054\021\337\027\242\315\361\004\373\360t\216\203\240qa", 32, "<\252\210&-\264*1\312\334\235q\316\011\024-Cm2bX&\356\033\233\207\001&\277t\311\325", 4000, "\002R\002W\3036\276\213w8\007Y\362\377y~#\347\207+\216\225\2272\271Jh9\354*\016\361\2554\233\263\025\353\267\015\026K\016Y\340\340\377\304\015=iz\341-\275q\315Ed2n:~4", 64 },
+ { "\217\243/\333S*\205\365\302\330HZ\235\347C\007\003m\364\015,\316\032$\036\274\225\317\010,\244\213", 32, ":\226<BnxW\025\357\300\377\342}s\014\2531\030\337\275\034\324lj\373\242\134\011\331\212\003hxeX\267\332\372\267P", 4000, ";\005\007\365\022.\212\307GD1\023|),\226\325\225b\256\3054\247\235F\232\033\233::k H1\001\340k\213\3640m2\274\361\0317v\002\215jkv\330\001D\312\322\345\025*\245C\321h", 64 },
+ { "\213\25132\207OJ0!'\2722\341r\340x\314\332\330\365g8\254\260\270M\334\302\311\3033\364", 32, "u\204\234\237r#\231\267\314>\370{n\035?\032\006(\257\302\366\036\301\275HI\342\230\303 \230\301\230\226k_2Ur\210=O\3602\253\277s\205", 4000, "\266x72y\026W\037Yu\222\207\244(0K\340\305\233\310s\021\011\275\022\004\213|\210D\002\240S\276\207\000\207v'\206w\271\320\362\302{q\231\355ff\361\374B\022\363\272;\223\316|[\301\217", 64 },
+ { "Yf\027I8L\371^\314Y\006*BK\36582A\221\252\030\322\352D_\266ux\377\035\367\023", 32, "\362d\335&o`\242r@\363\2426n}\234\004t\275\213\254\236\020\341\021\304.\042\321\361'o.\002\223[\266\224\233?%\004\337\240\340<\323\267\3039\220u\334\330\242\3239", 4000, "d\315\310\234\260\364\012\230\023\235\2756I\352\305\247_F\260\3602\350\324\2067\353\341-\232\020?\247\302\014\232\253\006S\016\241\320\251\000\022!DA\022\011P\356\372qL\277\263o\303\242\007\347\225\370\331", 64 },
+ { "@>\004\006\352\316\314\230\032\255\237\021\276h\037i\032/=^<}}R\313\265\001\270n\267\274\340", 32, "3\245B\276\234\220I\261\336\323\014\353\325?6\360_O/I#\270S\362\331\270\200\360\242Mp\213\361\356;2\316\304\251\227\003\352\327\027\003\271\026\275\230\237\233'\316\003\332j\260\032\014\323\243SO\006", 4000, "_\347Q\374R\016[\274\365~\217(\266\016\364\253\306\365~W\335\223:`Q\301L>\033\344(\253\027\211\372\352.\355\251\233\005\036\225\134\220\034\357'<\031\227\223f\253\240[>\031\344\300+\025\243\365", 64 },
+ { "\017\024\253{G\272\276x`\224}\324\216\324W\363\357o\266{Z\347\200\342*s;\3159\036\320]", 32, "/\203\306Y\243\30672\2236\257\335\233\352,W\351R2\004\237n\325y*\3525\371!^r\222\364A\217\311\370\301\3677A#9\212\030q\220r\042\321\031\303\036\003\235\274F\252\134\327XKI\344\370\215\266\316\013\3704\352", 4000, "\216o\275\201\220\001\2209\010\241\307\260\352\274\220\020\004\352`'\3552\372T\204{M\221\202oy\226\223lhIe%\260\325\303\314BX\347\331\316TN\311\332\177\251;W12\006X\376\307\235^\227", 64 },
+ { "\375\367\261\310P\032\253\300s\271\362\034Q\000\267\231wh\274[\367N\031\272\313\333Lg\016\233\032\246", 32, "w4\376Oe\334\337(", 5000, "\265\320\254NT0=/r<\341)\226*\376\345\364\004\134\201\024m\020\375Z\216t\212l\313G-\232%\321\272\256\336W$\371\310]m\335\334\242\306\241\367\220\263}F0\216\267\364\341D^\212\251\341", 64 },
+ { "\341\3615\215\010Wr\235\353\224\272`%vA\336\2122KT\237t\233\321(\213\316$\340\206\237\001", 32, "\337\362\246Z\225\307J\215c\244\007W\221/m\027", 5000, "z\033/\267\300\001z\320\334\020\034\255m\3321\364d\227\012\247\032\017\370C\031\340$\307TCn\004Y$\017\220\371\274/\350\306\303\002\012,]T\034\301\017\3459\365\252\032\321\316j\231\245\341\322%)", 64 },
+ { "\355p\310\241\334\272\230LP\241\235\324\302\312\202\002bm#\375\324\314\266L\212\016\340x\0370\333\304", 32, "\031\3057\204`\205\372\200;\320i\264\027I\007\027\267*-\365K\134\243Y", 5000, "mB\302\302\253l\313x;_y\012{\273N\340\303ohA\210TQ\310\321[lK\015lWL/\252\134iB\224a\265\207+[\016\241\330\005DRam\250\335}\356wZ\006\015\256\226a\216h", 64 },
+ { "\240\367\032Y\306a\312*U\314\322\335\022\016\377\213jMmr\244\042\274\257)\267\220\3307\221\033A", 32, "\027\277\033\343S\023\203\300\354nzy\226\254a\307\2146\215\033 p\256\342 \016B1\015C\027\266", 5000, "j=:\035`\342S/}\343\240\311\033N\227\377\327\270|\215\353\241\310\206\042\265\367\206\243\222\315\023a\303\274G\037\245\345\323H`\010\231\344\342\351X\001^r\353\314 &\234\024\002\242\317\021\254\340.", 64 },
+ { "\250\277w^\361\217\026b\210\177\306\3461%\252=\266\234A\211\002q\264\357e\232\3257;\036-\362", 32, "\261\363r\305^\275\322\224\3328\261\352\242\275\201O\317\275{\020\263{YD\241%2;\340\352?\366t\300\272h\351W\303+", 5000, "d\364y\350\264o\356\017\320\011\2317\326\027\254\033V\326/\200\276\374\360d\321\265\015\212_x\017\246bE\003\256\337F\265\260X!N\354\031o\216\335O\3278\202\027\222\266\262\356\311]Ok5=m", 64 },
+ { "\374;\134\361\232\355\314\370sC\252\275N\372\2732\335\213*\232*\310\352\0312\0047\3758\230\022\010", 32, "\332\205i\265\023\022\002\372\234S\253,W*\303^o\204\316\360\332\353\237[x\030\210\2003eBX\342u\224\363\232Q\262\220\212`$\026l\207\330\302", 5000, "d\021\307\254\221XR\257$\322]9\306}\306aqr\333\224\337M\266\245-\001\2148\203\346\002\032\031ax2\336\230\024W8p\346\310\300!\213\203\241\306|\222\206i.\272\251\026\022O\007\207\262?", 64 },
+ { "\204K\335O\134`\227I\204P\241\370G\324\350\313\305\346X\221\312\313S:\371t\341\011\275\327\301W", 32, ";h\002\331bk\375\014\232b:\334\367\341\361h\261\005\266\265\316!$N/i\201\002\272\005\351\212\031<\332Y3A\243\036\303\254\255\317\317\0356\020LD\210\337aU\007\023", 5000, "\002\036\314;\347rW\266\310(9\304\223\015\220\367\334~\3175W\354\233(\372\177n\304)\270\221\352\233\256[\370\320\222\042\202!\265\201\251\262\214m\234N\352\371\314\2724\201\001\272\3643\347\265/d\205", 64 },
+ { "I\304\026\242\014M\013\255)\257wq\205\273\337\252\256\2626\233faR\365\262\257\010\010\333@~\322", 32, "\377\235?i\251n\222\2608\250N\304r\353\331\313Byx\236u\212\340\322\243\376y=fv\367\244\303\232A\214\244*p\253\333\134\335\250X\223\320\325R\311\243G\2524\337\344\134I\340E=\253\300\301", 5000, "\335\011\020j[V\242\266q\257^\2139\027XpWl\367G+\224\210\371\320\002\2170\007\223\361s\271\033\374\134\301A\320\234N\357\252:\343\275\025'\012\035]\311/H\341\315\310\303\310}Q\021\375r", 64 },
+ { "Dml\351(\305\351#\355\004||\231$h\227j\361\3136\210l\375T\213\315\375\234~dF.", 32, "n\353',\204G\017\242\020\255N\026Io\221\224v$\324I(\246\016\036\035\374[\343\260]\326\021\013ne\371\356\301D\350\030k+\357nGJ\270E\262\035\351R\303\010f\233\246>\326\355|:\2552\003\021\234>dr\313", 5000, "\220\243\001\307\364\032\262\304\372\017\266%r\204;\301!\374\042[+,Z\321\373\020S\014\243s\305\335r\331\350\370\216\277\267\224GT\035\301\030\261\237kge\006\017|z\220\224\274\033\215\201\301\026uJ", 64 },
+ { "\270\0170\011\344\012A%\357\323\236(t.v\263l\231\00450?bA\325*?<\251j\256\230", 32, ";\354h\272\035\251\026\346", 6000, "<\220\344\337G\206\302WjF\342\302\323\276\371\3105d\240\376QF\003 VL\024\357\217%\237\331\360g\326\362\224p\322\032\204Y\254\230\227A\2048\201\023AaN\363\264h\347F\222'\232\352\356\042", 64 },
+ { "+\2547\326e\016\371\243\360E\373\0228\222sO\203\374\322\204\247\240\320f\227\201\364\371\223\323\216-", 32, "\215L\237\227\342\276aw\036\025\374\246\201\030\010\352", 6000, "oT:^\276\374\027\266\300\375v`Yz\330\206\325[~/\134\364o*hW\371\213\375w&\275\006\011\361\037!j\370\317\337\301\346\234\341\354\225\321\264\305\346iz\313&\324|O\305\317\2629?\022", 64 },
+ { "\354\332\023}\256\222\332\241 \362\302\351MG\3479\276\332\334\023\362\271\253\211u\275\200\334/\246\013\244", 32, "\266\243\134H\272\356\332/\011\305Q\357n\371\354\001\020\307\177\333\037\362\315\242", 6000, "\306\231\246\333A\300/\22328l\017\305+\010\037\256\341\202\313pq9'X\264(Z\246m\177\300.\352 \372\20368*\274p\305\033\2251ar\211.\2624,\326B\031\134\335o\033\310\037\2469", 64 },
+ { "\327'\333\2626\365\015l\373\244\377\016\314T\205\023\255T\244\217\346\304s\275\370\134o\226\241\000\3653", 32, "\204\340\237\333\347B Zh\352@\2206\262\303\002|\312\203\327\333<\372o\200\035\276G\210S\134Z", 6000, "\334B\327\177g\003$\351\375\134F\266\375\211\212\241\366y\323\375\373_\267\215l[\203\305\370\216\202$p>\241\264\377\032\022\211I\257,:&\317f\245\242w\364*q\037.\347\276\277\275\244\205?\034\317", 64 },
+ { "\234>s\026\201USYC\367\314\261\324w\355\264\037\377\236\274\021\3211\006\372\354\201\250o\20011", 32, "'&A<4\356\331\213\213y\222#=_(\361\346\226\350\3564\177\261\024\350\3528 \331\235g?\007\361rYB\003\220}", 6000, "\304\232K\203\301\022\0351\220\354\260\350\311\321\337\023\264\354\256\365\333\215\373\325\201\274\204\216\310=\252y\006I7\250Y\210\204\226N]\036h<\016@\231$\234\134\271\357\307\225\273\331\245\272\2650\357q\203", 64 },
+ { "\351\000z&j\354\276\2739+7\300\022\264\327\244\275\345\003\365>\204;\276\217\307)`XP:\345", 32, "\234\365\001{\277}\042\367\222\230\030\327\264\201\016>\241\001\037\336\340z%>\324\032#\352\204\351\212eC\304\027i\205\362\215Sn\226i\215\313\374\210\354", 6000, "?A\017\245L]\214\344\373i?\212\306\252\013Lg\277\015\256\260\001\306C\375\354D\025L\134\337\263\363\025V\336\227\013\237\353>F]_\035\314D\240ES\253\357\210\344\014r\220!\222\220\010\374-\265", 64 },
+ { "T\217D\316|\373{}\037\354\234\177S\327\270\016-\231\316RE2\376E\030\245't\005)<\200", 32, "2\316\204\337k\213\260o\273\020-\351q,\220\202]e+\376ci\312\367L'\331\237\035\024\025,|\323\220z\332\211B\314\226\261E:.\002\0238\376\315\320\274.&\233\036", 6000, "\312X.<\3273\237\320\2349\267Rg'\337\230\326~\223\323}\204\010\005\352\035\221\223\244\374\262D;O\2356i0\311\333\360W\203%\321m\037\3220\267\0162\254w\0161\342\320\031u\206\032\370\271", 64 },
+ { "D\243\026\222\253]\312\252\020% ^w\024\024\377\312~Q\221\334t\215\377\365#\353\305\340\273\341\364", 32, "r\012Vc\246\013\020\364\221>\227\273DJc\233\2373F\235\001\307G\223\210\260\033\236\361\365J\037K$\372\245\270\370\2402x|\360\033,\002b\307#\305\026\345\2572\325\241\321Sk#\364\035\342\363", 6000, "\342\3053\272\022\221\021F\344AvW~\350\355\314\342|\303\324\315\002U\311\236\315#\2776)\3219\033.T?0\035\264\207\350d\247\247\042w\365\371\262\217\2051\001E}\201\301\034\025\177\016\314\246\312", 64 },
+ { ";\370<_m\217w\363\0054hx\333\347:\247\033\337\364\363n\370\042\244\303\014\306G\322\027\323U", 32, "4\331\020,\014\345\314\367~DR7\345\013\2001P\261\273\333\257\306\373\300\033\361\257\256\271\362\302\134\342<\320\301\224\034\034g\033\206\022\005\220N\260\014\207\347\265|\206'Q\323\214\276\305\357\356O\355Gr>\237\204Z\371\354\375", 6000, "\027\304\207\017\213\207_\0117\262Yu\004\012J.\274\307xu\273\372V\004z\243P\007n\363n\222:A\311\351\031\212\2748f\352@\217\347;[@\177:\023nq\260\221n\244\014\246*\361\033\212\042", 64 },
+ { "~_\236f\256=\004\026S\320\325Tz\237U\327d2#w\202)Ir\003%\220>$u4\215", 32, "\372v\342\354;>9\312", 7000, "\134\017I=\243`|\314\225\014\263\254K\014r\372l4R\015<\3138'3Pw\214\3051 J!^G\364l\334\037\314\177m\220\247\321\001\307P\317B;\316\037BC\246\310@\250\250\367V\031\223", 64 },
+ { "\371\311\302+J{\330+%\236\312\021P\377\377I\235\356\2664\000[\270Q\042\022\357\235\374\307.=", 32, "r\244\035~\327Y\310\266#\364\324D\203hd\266", 7000, "\272\304O|Wj\3136{g_+\347\355j\265/\300!lR+\341\231\2032\322:\032\272\350\331J\347Q\203DQc'4\335@i\347\334}))a\004(\313\255\3220\206\000\346\014\206\206\365\012", 64 },
+ { "4\203\215\242\377.\236%\324\260\315@\275i\310C\241U\350\363\3107\337\371\2607\223\036\015:tQ", 32, "y;\315-J\314\274\357\264\210\020\222\007\240\272N\015\331\370\037\334Y\267\016", 7000, "\351\334\341R\305\301\022\273C8\001\000\270i\226\343\304\016z\322d\370_Q\377\2671\215Zu\376nS\245\030Za\374\353\255\305H\024\202\024\0110\037i9\003\363\337\377\304\214\331\207\366\226W\012\307\376", 64 },
+ { "l?\242\3334\336\353R\233b\216|C!\013\000\022\274^$xfn\231\237!\037\357k\351\345\374", 32, "\005\276\347\3550\023\213\226w\2313\312\351\253PuJ+\027\003!\255\322\340\006\334__\017\317!\341", 7000, "=\015\237\267?\333$u\274\201\353e\204.Zls}H\344O\020!W\036@\222\362Qp\013\212\225T\274\265\256\024\2221#v\245\254\203K\202\020\355\0118\262\001\333\346\364\332\240\347\017\3227>\245", 64 },
+ { "\027\344\376D\230{\335\276\012\027xc\247h\300\221\253\2004\231N\337XVPH\350\177\202\324\336(", 32, "\014;\3008\375\017N\372G\354\331\201\031\263A\027a\351\315\256\344rE\326P\247G\375\2502\262\375\353\344NA\035\317i\212", 7000, "Y\222\200\251;I<\227Q]\356\335\317\263\351\231\042Q\322s\313n\006-P'RKDZ\330\237q\213\332\374c\266\022\352,y6\231\226\001\233\354\027\245k22\312\331^i\026\2764\244\353\306\251", 64 },
+ { "\267\366\277\006\327\363\330(~,Wd\275\303A\203\315\016\230\333\327X=*\326yB\364\213v&%", 32, "\274M\257\261N\266.k\356\343p \331wdX\324>\366a\031\362\207\3732\323\302\255\013\212r\003\321:d\220\200PI\010\0029HA\011\003\326\220", 7000, "i\364Q(V\223\200\223\340\007\374\330&H\342E\032\215\2515\3556X\022\215O\365\363\264\247Vwu>\371\320\332j\305\354\267R\323\022\274\232\216\262\361\301\225\3124\004\215=\236\237\341\347\365z(/", 64 },
+ { ";~ \241\225\021\325\357\016\232\310=\214\261\3451\270\316\233-\303g\361\216\033Nxz\377\361.\236", 32, "\272x^\233\036p\375\304\204\200\034\306\224\352\307g\361\310\003K\224\276\211%\202\336\346;c\207\240A\021\005\206\255\006%6\312\233G\017,3\230\310\301\202\226A% \0341\353", 7000, "\331\326\014\373X\343aJI\247Cn-?\201B\223\271R\230\015\346\204\350\350w\012Y\373\240\265\020\274\232\000\366\207s\263\360\323\0254S\226i\007\371]\354\314\015aV>\337\303\260f\030\275\214\233\372", 64 },
+ { "zt\362W\316\317\032\273\343zzZ\207)ZLm\224\345=\350_\361:\033X\324`\305\306\202-", 32, "\010\306xU:i\204\2235\375\346w6Q?\036\010M\267\275\265\240\223[p\022\255\213K-iKnR\263oP\263\002\036\331\200J\363\365\314\214\036\026\343\304\316\352=a\211\274*I\340.\266\015\265", 7000, "\337\242\214[V\263\350\233\377\006\301\201\275G-\365;\260\241\030\035\355\010\242k\361,\271\344\377bB<\304\242h\357\015\225\313\203\266b\031\322\270\235\376\365\351h\212b\2250\317\2567\254\033\223PLq", 64 },
+ { "\004\221\352\003\252\354\313\377UT\030\207\205\362+\004\033\015\2140\004\261Yr\001\340jA2\241\2625", 32, "\256\300\3735\301\013\223\035\221Hl\304mj\340\375\215\254\337\0332\3472\274\254g(.\323\322g\0122\177\347\377\343\353\271t\010R\024\215\331\033\0027e\237\305{U\034k\357\220S\240v ;R\266\177\344p|Tw,\230", 7000, "\335\227U\002aP\016>\270\376g\266\312\211+\305&\013\273\312\262\364\365*\002\227\256/tg\007\341\3777 \351\323\337\031\246\025\227\301!\330\221\032;tX\007\030\004\353\004oX\277j\250\257\323J\230", 64 },
+ { "\347\324\310U\204\323j\341\215\317&\325zo\327\025D\024&\352'gZ+>w5\256x\3500\353", 32, "\311b;\237!Zc\350", 8000, "\032\210\262\273W\324\134P\216\315\300\312\325\270\346\032^\031\022\260\372u\313\336\234\315h\005\264\2010\373$4u/\271\311VX\226V\304\261\042B\241A\227}\265jR@O\3651\364!\012w\246\335\200", 64 },
+ { "Dn\207\341\204X\030\206\355vO@\340B\275\354\000\023\311\265\225T\237m\013\364\034\247\353\217\316w", 32, "\372\366\303\033\331\272uy\262\371j\302\256\356\334\257", 8000, "\250KJ\204a\031\225\204\016\325x\310?\230\013\2070\322\302\263\231r\264\001\240VR\237\270F#\330\353!\337\330V\256\223\357\262\030\2421\205\037x\272\305L\350Z\031\245W|\360\325\305w\223\020oc", 64 },
+ { "\355\015e\250>p\225X\225*\011\316{\311\244f\364g\372\300\336\351=\216-W\324\257\316\373i\376", 32, "\226l\330`I\331\035r\203A\354~\251\373^yQK\322\343.Z E", 8000, "70`<\225^\221|\350*\017|\244\004`\317\221\134\376\352}%\245\324\360\216IY\042\311l|\201\350\230\210{\346\034\271\264R,&#\001\337\261[I\357kBl\336\335\213T\352;\015\024\301\033", 64 },
+ { "\317|uV\275\303X\214t\250w\274\335\0028z\376\025\223^2\326F\250i\006\024\006Jm\303\017", 32, "\012\375\360\206\245\022J<\212H\177\267\247\370gr\326\322\251\011\324\331\010B^_IC\317JP\327", 8000, "`n\314\361\221\2161\263%w}y\006-\323\010S\354\240\332\026l)\325\266\023\013\036.\273\000u\247'\273Y\316j,ap\345\206*\264\324inn\022\203tj\260pb\023\210c\306R\234-\320", 64 },
+ { "\231\230\221\276\330\2648\017\330v\311\346E\364\272<\037\243'\355\356\252*\210f\006\036\206Z\346[I", 32, "/Ws\213\262\202\020\005n\351i\334\233\303\375~\374\341\251]\227\247aeU\246H\263\031\035\015[\250\316\205\031{\222\016\205", 8000, "T(\016UCC$\370\236\251|\013s\237\023\325G\014\327\376Q\013\306\244*\233E;\014\264sZ\2449\242!m\200\243\264\342M\257\0350\370\357\224\373\262\201K\023\204\334\320\340\016y\272P\311A\217", 64 },
+ { "\372\310Z\030\0367\234\257\361J\306F\267\310\216\2069+\013\224\275T/S\257\3554e\177P\243\134", 32, "p\237\036hc\362\305I\350\222\316\022\310v\240\240rE\200\277\241\212\245\330\260\035y_i\374\332\240\304\031w-QX\357\022Q\025\343n\371\273D\201", 8000, "c&\346\353\305F\004P\307\212\3268\356\255\252\225vy\214\213%\340Zz\242\252\301\263R\344qUhOG\334\363\177\203\030\014m\241\306\313\366m<\322\241\254{\334A\007\371\035\0210\211\320\357\042\014", 64 },
+ { "\274\023\233g\226\220\266b~\011M\215\240\304:\345\216)\2338O\031\205c2nj\364\332\245\252\275", 32, "\023x\251\256\246*\363\004nr\336\240\357\031\011\315J\210\243:\023f\263\202H\333u0s\323\345\2516i\212\012>d\344\004\231:\217\371%P3\004\333\225M\037\321\036'z", 8000, "\305\373\334\202\333DvE\007\302L\246\273O1\312\006\327\203m 1\227\322\262\275\262$}\343n\377\275\325\034\323c\242\337e\352\247\217\264\200\276l\203\207*MG\245wI`4\261\200\025w\277\374\273", 64 },
+ { "\310M\231/BX\015\3022,D\221\343\336{AB\373\305\232\223~\332\001$p%\236\311?\307\255", 32, "\346\237C\3322L\307\240\003+\342@\244\356\210\3652@\231uyajh\3672)\3133k\015\247\011\221\360\256$}G\232\326\344sB9\266\326a\273\276\273t\134\330Q)\2555O\212\204\207K\251", 8000, "Fg\027\253\252e\334-\306!7\300\374\277\235\274\261\350\005D\327\262\302\037\025\016\306|p3+\031\230\216\224\362\370&34\314\240LA\326\010\307b\371\177Pw\310\2605\210\275\305q$YPw\024", 64 },
+ { "\243%\302\344P\210:6\271\3316\330|\234\377\325x\317\255\356u\266\204_\264\232\306\221H\235\323\233", 32, "\203\355\374\337L8\346\314\340\310\310)iB\261\313\303`\254\263\356\011\360LVf[\240\372\033\263\031 \237\361\263\264\007\257\323\224\205\207\366)\344\220\270H\216\005K\310\200\256~\341w\352\365m\305\316\231\315!\354\232\270\030\327\027", 8000, "/p\266KV\336\250\270\214\3752\255>\253\011\261\353Lu\022y\313+\300v\201\005^\333\310\370\016\375x\023N\340v&\222\042\353W'\277\014\226ss\240\314T/\020\347\2303\225i\337\255\217\275\310", 64 },
+ { "\020\352\211\2704\337\355MK\251\237\315\207w=\345M\253j\013Q\312A\323v\256\212\273\346\314\206Z", 32, "\356\327\312\324\304XV\042", 9000, "<\252\305rA1WHK\030\310K\220\312\214/\225\222#\211\020y5J.\373.\375\250\246\344=U\326_\322\205\303\357\316\212*2\252\1347yP)/y\330\030q(;\275\222\316B\345N/'", 64 },
+ { "\246\200\343|\000\363\320\004c1/\252 \230\027f\221\010\300L8<\315\333\014\000~\013=\224m`", 32, "\344h\347\244\325\331\22483=Nft\360\302\261", 9000, "\330Y+Q\337\302\005\230&\257\271\201\020\266\346rXi\310\342\234l\365\374}\242\365&\377\214Fn\240\315\272\026\322\326\2318\332\325\001\362\007G\316\346T\001\225\273\377\042\345?\377\330\302\361:\354Q\270", 64 },
+ { "\300^\277,{\327^\347;\023\366\331\344\214@\224\226/\231\304z\212\024\264\343\305u\353Y\301\305\031", 32, "e0H}\001o\0238|<\317\200\275\255I\300\2211SZ\350\024\010x", 9000, "\346\352\302\366Gt<\261;\010((\022\360\232w\323\024~\3107\323\001\003\331X\006\245\235\364V\302\000\233~9\370\225\340\312\2120\011\362\274W\036\272\270\3168\311d\366\361\270\254E\363Zg\001\326\227", 64 },
+ { "\302\3170F\365\303\367\304\340\000\332\3223F|\363\231o\336\315\010\334\000\327\225,\300\205\214\301\272)", 32, "\017\015*/]\217\334\262S\312\317\235r\371m\225\273\013\215N\360\016\324\003 \257\222\315\371\014\326t", 9000, "Q\213LROW\365\360C\375\251\233\235\237\273f\370\333&Qv\035\231bj|\2621\134\365\213\254\264\221\377N\321\305\036\272\236I\331K\375 \276\212\177R(T\370\030\376\322\201!a\230\014g2\330", 64 },
+ { "VW\226?\011\206\237\332\316^\201\211F\363\242#\2414\260\034f\253F\020\015\356\022\026\030wy\207", 32, "\134X>oO\341\2221p\023\236\217\357\355\213qc\222\237$q\311\271\224\317\005\354\243\010'\365\246Q\371)N_\226\353m", 9000, "\310Z1\202\257\017\276\0118bF\322\213U\222~\243\033\323[\324\371\365(\344R\363\347\313\342\255\033\320\316\374]\207\371\004\353e\311+\316`%p\236\327\326o\017\235yu\234=+\216\303\3420\246\333", 64 },
+ { "\265\321\227\226\355\037\225\225f\000`\331\364x_h\261\236\302F\356\277\305\203\337n\324\242XlX\276", 32, "\353\325\230[\314*\371$\233U\335d\302\356+M+\217K\255:\305i\217\315U5dmm1r\034`^\033\200\311\366\323\244\254\134\354*.C\261", 9000, "\203\205\206;\334K\261)~\005a\365\232$.\246\250\333\351\036\237\227T\326\007\266\277\337\267\012i\252\277<v\304j\372=\202\312\335\262\355\200z\004\215\216\037_$\334d\020\323\2272\316j>f/\367", 64 },
+ { "g\342u\261\360\205W\253\3757\224>\2007\261$\321\326[A\030Nl>\300\255\270\310j\230\000\317", 32, "\205R\344\134\355S;_$R9\207\216\351\035\355\344\246\230\347\305\225y\365'\264\273\273\002\3004\3074\321%j\237\246\352\345Y(\224\334\371\274\012WL\232$z\246\331m\020", 9000, "\042\307\336\011n\273.\027\006Z\200\335\244N)\210\325\177M\000\270U\2167\007\331\217\002u\262\030\257\223\325]\371WI\207\226\211\331s\007.\224P\3330\023E\273\3256\324\2250|h\332\134\306M}", 64 },
+ { "\205\222\255d0\015\236\365\024\002w\355:\025Q\206(\000\014\315*`6\343\233\217\377\036\227\231\001\310", 32, "\031\345\002\252\206*\014\226\235[\012\333\340K\207$\010\010\332s8*\346|\245\323h}^\202`\233\200\2460\304H9\031H\2321x\013\033\363\322=B\223\224\321\2335\204\262#%\245#N\236\205\360", 9000, "\376\304\247X\265W\252\204.\025\177:\360\227\0259$\005h\205\364\030o\324p\023\206\006\325\302B:\311\252\240\015\253\000\370\313\007\002\276:\037/\001\240-T\241\221\007\247`Z\012|M\027\316\2303\004", 64 },
+ { "\262\332x\2240\1348{\351ow\377\220.\005\010\351\313\365w\310\006\042\312O\234\256\254I\225P\236", 32, "\003\262\327\213\033E\352\255k\031x_V1\330\217\232\333TCjCG\257\222\207\302aA_&\303\022\242T2V\301l\230U=\204\255\013?9\212W\217R\254\246\264J\310\243\340\215\276\324\264\211\371H~d\334V\0048\210", 9000, "\2428\360\032\336\004$\015\303\304\203\271/\302\252\361\204\366\314\201\231\324\323\340\031E\355\205\372\201?JX\264\256\177\242L\362\001oc\310\134{pO\232\234\200x03\270\364@\346\134\214\330\202\010\254\350", 64 },
+ { "`\210\003\226\023\271\363\251\376u\032@4R\227A\233\177\204+\331D\302jJ\035I~\033Va\334\306\134\337\3708\3159\374", 40, "g:\354\200:\353\237\314", 100, "\247\204y~\264\003\374\263YbJ1\265\256\370b\020\377?\034\240{\014\275Z\011\347\213\206\356\037Q\010^\306\207Q9\021\374q\270\242DI\034-\232o=\212\202\134\037+\305pB\2152CI\275[", 64 },
+ { "\330\3067\035\364 \2267\266\226]\315\242\015X{\270Vn9\260\277\277\375a\014\251k2P\003f{Z\037[t|o=", 40, "\375\276EL5\306\235A\371\032\272\364\014\340\014\374", 100, "8\255\030MN[\002\3162\250\340\323\262\011O\0374\012V'\303a\010\026\372\337\022p\002\321*\032\255\205\217\377`\264\015\255\217\005\300`\352\261\313\370`\217en.\252\205V_\331b\261\302g\333\273", 64 },
+ { "\247\3113>'\341Z\226\015E\235\221\200}d\217\021\263\134r\205NX\241\366\014\257\243\261y\212r\263D\272\243\021\207\235\216", 40, "(\213\353B\221\216\0302\003R}\310}\331\006\343\001\333\326\236\017x3\005", 100, "\261\201\255\301\037\027-\004\242}\361\027\264\250\210r\231\274=d\010\371\2662\357\017\233d\224\230\0300\356\273\264\245\212A*%\333 \326(c#\273X\026\312\226\317\031X\257,\353\213\307\254\304\337C\326", 64 },
+ { "\024B\327M\362\134\210]Iu3\223\322\277\361\3646\276H\263\030\2348XC\271\206d5\242\231\263*\334w\317\015\376\002\014", 40, "\321\357!\351~\005{\304\267\313\205\027\005\307\232a\367\212r\302\306\032\231\26567\246\356\227!R2", 100, "`\236%\333\327{\331~X\022m\274\337\203\257\326\012\247\021\326\270l\3271W\244A:T\261\250\252n\230\327\231\305v\024\245\371\377\346\007\305\204\360\215\252\267G\007\005_E\0221\223\340\3714\243\220\303", 64 },
+ { "\002\325\230\270\245\022\320|\042<\274\236\243{IF\255\2123~1\324NX\343\212\002\230+,\321\253\320\334\246\273\264\265\215\350", 40, "M\361.*\033\362\022\226*k\333\225B\320\377\266\011\307\034w4s\274\316\217\034\221W\373\003\256\244\210e\260\242)\206\254\223", 100, "\371d\211g\2024\010\257M\023f\177\247\376\000e\036\353\372\375%l4\272\370\026\343\330\240\022L\214y\025\235\360\0213\014\375B\326\331\300G\336\212}\342*,\254~\001B\204\370\200d\254 \303E\367", 64 },
+ { "\243\035i\361t\264\370\31688\017\261Ph\267\224\271\357Y4\035gZ\2631\021\253\236\364\311\031E\364K\240\313xi\253o", 40, "\313h\035\345a9\260^n\335\014\337v'\371\330D\215`\332|1\357\023\253\215\213X,\215h\306X\244C\232\236h\256\372\247\020N\275\020d\360\005", 100, "{\320\345i\277g\336.\253(|\256\347\302\022\260\330\315\247j3\217\257\236\260\302\024\204\000,+}I\202Ow\321\205\013\315\324\001\332\331\003\367E%C\205\354\345\366E\314\351\032\340\314\2568\233=#", 64 },
+ { "R.\220Q\306\005\331\013_\274\263\364\305\363\253ut\215T\042\354}\011\014\256\254J\245c\353f/R3s\342$<\205\366", 40, "\042L;\375\240\241\376\322\232GY\375\004PV\261\331}\305#d=\210T\261\233\246\322\377\365\350\365\304\202\256\264\003WX\021=\273\247;\007\215\377Y\360\212\004\324\2525\260\223", 100, "]\370(3IL\232v\357\336\347\350j\3428=\337\205g\331\211=w\023\373\311q\321fM\372\134\037o\312\270\207E\232$\246\224\0069q\245;_U\256\207\246\224~\265lfX\217+\241\177\012\000", 64 },
+ { "\005F\364\361\037\250}\236\013\315z\330\022B\011U\222\372\236Z\033ZH|G\313j\247\263\363\222\350\362\243~;8\203\276Y", 40, "'\006e\223)\331-D/\272>1\236\236\202\360\034\015\253\305\312\036\202;\306\254\036\313)\235\011\217\270y\235 9'\355cs5\311\300I\042\235ls}\202\364\002\303S\223L\3528\262\305\001\203\011", 100, "NY\261\274\311\225i^\034\326=\353\301\204\312\357y\263r\031\234\240O<%\253\221\022\321\264\015rH\311\327\302\201yi\304\360\3748z\036\022\205\236W\026\023\006\256B\227mJ\234\231\263\253\3368\202", 64 },
+ { "\006\336\254*o{\322\024\361\337\007S\353\357\243\370\244\033\261\024`\3159\311\001\027:\342h\275)R#_S\030\217V\336\024", 40, "\001/Wmb8\260\026\246\270#\346W\360\277/\347\177\233\322t%|\226\334\261~\001tJE\326+\204X\304\324&\036\134\252\004\362\020Y\347V\245\015\023\332EQ\345U\002F\321\202T\012\356\313\235f\206h\356]+\314\324", 100, "I\177\227\177z\320<g\030\252m\320\377\232\350U\255\333\271-\033\377\001?\302\021\012\016\377\317\342H\337X\0059\312r\346!<\362\023\264\275~\313]l\373\333\231\000Q\373i`8\016\017\255!\001\312", 64 },
+ { "\271\341\030C\026\343P\310\2118\241\324\361X'\023\251h\2657\300\276t\032'\034m\210\014\350\217\345\227\275B\177\035u\255\214", 40, "\263\305_\247\272\021A\323", 200, "\247\225P5\302)JX\034\322\3619\251u\325\245\224\23119X\365\364K\037\321Z]\363O*\227\034\350<\020\036\331B\327\346\345\310\0214d\325T/\233\351B\134\371\304\210\362\236 \327z\321\301\014", 64 },
+ { "\350\221\234\275\355Q\240\246I\234\215\374\264#\204\352(abs\220?\017\221\331\340\277\243vt%\366\334\233~n\30314\321", 40, "?\223\034\316]\373\026TS\035\231\031\220\323E\375", 200, ">\023\255\352\036\233\272\216\032\231\236\236\220\034\304\263\006x\212\004\340~\371a\034\375R\254T\321\237,\351>\367Be\335\020d\343\363\216\271\011!\344\345\271\325u\006Jdv\004\350\277\301g\007\211_\336", 64 },
+ { "\011d*\215\352[\313\3051?\362\360c\277?7\1771\306\221\271\022\275\205Y1E\240M\214T\225R\363\010\3130!*y", 40, "\310l3\2262\346\215Z\223\005\236\302\241\025~\276'\334r<\030Ou\273", 200, "\022\251\266\330\274\244\022\001\042g?\014Q\334^\032\320\203\004\023\231VI\236\351\032\234\200\220\272}/\255\356\322C\342\315\340mR/\024\003WvC\362}0\204\274x\315u\216\014\203\337\353\0350\216\241", 64 },
+ { "N{\366HE\323\251BA2\337\035lQ9y\377\206_\354\217\210\210\237\301\277Tm\234\001\033\237\200\261:\314$\007Ro", 40, "\226L\252\227\244 P\217\303\022\353\351\357u|\367X\206\134h\354]\006\257\247b\277ug\266\371\305", 200, "\331\343\322@\363\303\324CF\204on\225pD1\260\342\250h\337\376<\376oqrOrC\260,\014\330\234@ \363\236v\206\201;\335\020+\253#\232x\267-\035\035P\010G\277\322\200d\241d\337", 64 },
+ { "RjMU\235u\300\005\020s\3026\244C \010T\277\204\025}6\221\260t\030\347\262y\246\004\004\022nP\003E\224B\275", 40, "\361|Oc\337\006\334\314}y\037}5'L\363\3776(W\331\015+\005y\211\036X\006\33121\012Y\017\011c\016\353\025", 200, "<V\312\266\223u5\270\312\347H\210\237^\213q\031\042!J\257\320pXe\326f}~\035\322I\247\313\012\307 \0022\215\224)j_\365\336\245\214S\244+p\345[\322\231\236\277\345\272\034X\350'", 64 },
+ { ".\316\012\350\042\022\257=/.\310\354\276\336K\326A\233\223k\213!fb\242\371\230\312\276\026\316\016\361\361\134\246\206}\234(", 40, "\235\225\015\206\365)\227E}\330P\262\013\322,\010Px\034\020`5@\325\373\316\266X\010\200\177\307\367\260sK\316\352\226\363\003\300\253Y\200\363\253t", 200, "\255\273_\317K\352/\277\221\035\005\010\365L\216\031\246\206\253\251\23277\36755\200E\367\311z.1F\244\374Z~do\335%\312\217\272oVa\273\301\323\000\376\251\037\326f\306\243\134 \225\212a", 64 },
+ { "Y\244\247|\340\205\376\355\004&H\253\342C\372\257\252\025\376\260\225-\320\264\257I\316\256G\327\231a\271\361\022\324o\257V\257", 40, "G=BUR/\221e\200\374\316\371\350\027\0341|\363\134\241\254\213%\003\042\321\253\370m\243V:\024\262\011\270\347\026\331\321\375R\271\222\025u9[)\364\357\357X\227\210\305", 200, "$E\233\337\202\243\311\237\356\201U\332\222\364Z\240\337n2\374\250K\202~&s\306\332\011}b\303\340\353!\250\313\311\254\324\350\230Q\273o-`6\333\207l\035\261}7\331G\365\237KT2o\033", 64 },
+ { "..m\365n\264\333\204p\263\221?\362\372O\301~D\227U;\234^\036\300m\363uZ\361~\201\363\344\030\251l\337J\361", 40, "\217)\332X\242\356+\313*\202\256\021\353\232`\314\310\277\267jk\255\274\350\275\267E:\313\255\274\347\177 \033#\205\235c,\216\223\241\377r\262\336\366n\247\375\024N\322$a`\304\022wk\200\020\303", 200, "\035\363\013AGy7\304H<?I\036c\254\026\334fMb\276\304 X\210\267\2552fS\223\244i\210\242\356w\364\015\211W\042K\232H0\001aH\336.\370\003\251\001\225\212\376O\314u:\254V", 64 },
+ { "\347+$\314\2676\026\335\340\275j\342*\246\313\245=\323\226\320}\367[\335)\226a\336q\365G\350\337MI\274\223l`Q", 40, "N0\357I\034\2230\216U\320\267\013`\013\277\216!\032:5\261\024\004\370U\260&\202jDN\233l\271\337\022\217\350\327\272\263[\272\351\004\313\304G\267Q\366\202\275\350\332\372\022\357^\265F\227\272\332\337o\021\205\231\261?#", 200, "mEe\243\363\344R?R\035\316\212C\033*\351\322\221\210\037\177\221/r\204\022\313}\273ju\227\275\310\304\362\206\263\011\233\234\012\227\300\225\330\331\322\317N\244=\217\134\201\004Uf\042\002\233\352;\032", 64 },
+ { "C\267\015\266Lr.!\374\322\330<)\373\221i\226\253\033\272\267C\027\034\005H\350\247\204\020,\264.<\364\211'\015\310\274", 40, "\323LJm\012t\260\336", 300, "2\257x\276`\036_k\257Lf{\232UyO\312=\306\310\036n-\235\321\335\330\037\334\202\376@Z\200p\236\037\251\253\021\031k*\201\373\205\3369\321\0278\234\371g\357\216\350&D\325\012\373=\022", 64 },
+ { "6\362\372EM\003q\252w\004\001\335\303\002\0256\271\030\306\3062\324\020\373\026~\010|\264\2616\263\013\240\036\270\304\030@&", 40, "\036\023\276\377\200\3138Eq,\226#>\037^\036", 300, "^b\177X\310\203*\016\340f\343R\233\025\025\222\017l|R\203\227F\217\311@\261\207\325\013\273\042\375\260\256b\333\036\221\220\026\377\223X\336\243\217\235h\006\3110\231R\314\247\3401q\365>dA\010", 64 },
+ { "\350$\307\023\016\225\330\3677\315F]9\3447F\306\036?w\000w\266\266\214\244|w\354\204*9~\331y\262x4\237E", 40, "\023*\200k:\322 \321O\214W\374R\223>\232G\202\230\223u\231.\266", 300, "z\223\217\302\200\321_U\300\304W\241/a~\367X\24385\244\207\224M/\255,\232\252\366C\377>5\312\210\273k\001\377\353>g\204\032>\017\303\207#1\021\373\257\031(M*\014 h\334P\370", 64 },
+ { "\341\320\235C\357(E\316\332\224\353$\002\330\352{m\215\310l%\255\247\035>fQN\270\217\001\211\355\265@\2036W\263\210", 40, "\232\001\020\307\220Q\244C\312\213%\0206bK~y\272Bh\302\323D8OE\205\323\356\003\235\300", 300, "\227\366^\335\033\330\023e+w\242!B*~t\226\326\322\201\020\251\025O\336 \302\354\355b=\3732,\304\311\025\005y\032\376\005y\347I\031\260\336\360puq/\340\235\325(\337\023\344y\225Hs", 64 },
+ { "E,\342\3250\250\275N5\307?b\016f\215\200E7\3260\2062z\026X\243R\245A\210R\014\262\240r\305Z\303\211\042", 40, "\213n0gD'Y$\2742\031\330\027\247\253\343Y\345\263Y\335\354\270\0275\205`\015\227\002\3527\336\367\354\337\024\204v0", 300, "\305\042\312\304t\351\241\223H<~\212\257N\201\022\363\032~\231\241\373\300L\344\333\302F\272\233\306\037\231\260\236\243\326\035\370lOM[v6N\214\042\362D\224\001\372\316Z\372\220t\177\301>\020}2", 64 },
+ { "&\3372)\301\232E\222\030&{\236\272\013\210\011\034%1\352s\310\341^\310\251\352\177\257l*\3071}\343\011\317\377\012\221", 40, "\332%\357\310,%\202/Ei\2600\0219\256\305\225}\344F\273*\231\272\317\004@G\334\242\311\270F\0232\240\264\003\234JL\304P\2627\271\234U", 300, "\017\356\256\272\201\031Kf\300\365Z\246\362Z\2765b\346\272c\011\222\241\033\337L\356\323h\034%\001\270\020\016u5\251\030\013u\343%\361%c\005d\242\340p\224L\255\261\370\025\216=9S\225\027X", 64 },
+ { "\023\225\225\225a\205\3544\023J\002\345\037\024\217$\252P\240\347\025-\206\0266wn\353*Jx\327\323\213qo\322~JV", 40, "<\225(\247f\351\21182x\003\036Z\333I\376\347\211\037\340{\241\0017jPQ\037\206N(\365\354$$\001X\253\3713\253?\263.W?]\260\327')\347\200\240%\354", 300, "\362wD\306Q!\204\002\0347J\363\256\214\210\3030\266\007\3170\323\346\375\301n+\300\010\025\345\010\0316\363\344%*\377\360\260#\212Ut\324\347\334;\011\356(&A\207\0332\013\320\3060\367\271\333", 64 },
+ { "\267\244\274\263\273\230jf&\202Y\220\226\354\216\363\010\031\206\326p\363ux\3536%.~<\213\324;vCi\0217\004\311", 40, "\230\250\225\200\265%,\326\017\035?\274\237{D\272\224\303\3037ea\205:\042\022B1\272\017\275G\263\317\344 \306\204\233\244\273\254\235m/1\317\223\344Uf \273\372.\305\306?\005\011[\217\302p", 300, "\360\214I\303\037J\034r\211\204\333\023u\217\274\134\373G\024\327Z\016\331\022\210aOM\014\212\226\240\257N\215nH\266\343\006e?X\244\370&-\313|\252%\353\351{\345\340\004\345\271\004Q\314^0", 64 },
+ { "G.\325\033\317\202j\005\253z1\227\033\207\2061<0\003\311+\3337L9\264\004h&\027@:\030H\331\313/N\270/", 40, "\006\032\324w1\247)\271\337H\335\312\316\2467\214\042\207\245\373\361\250J\207\354c\253f\371\001\276o\343\300h\010A\300\366\331\301NI\255\322\270\350lf\246\020\257\263m\231\311H\3175{>\257\223\313\262Y\245\254hghv", 300, "d8\023\233e\020}\215\007\036\002\367H\003\367\336\010\260\364\255 \360\200\223\265\252\203y\315\322\013\252\376E-gi\000IY\325\367\353)\322\311g\023\301\245\245\134\223n\211\324\245\301coXc.\320", 64 },
+ { "\302\347\272e\366\307\3749\015< ^\330\275\344*\370\033\274_\361\227=-\350d\340[V\025#\225\255\017\252g\325;H9", 40, "\272w\001\242\254\207\370\351", 400, "\304>v\275T\012F\025\366\200\362K'\263v\211\232\267oM$\337\134R~&\262\205IM\300\372\3549\014\177\267H{!\241$\234S=t\355T\247O|?\310\247U\263\326y\000\221\214\2404\266", 64 },
+ { "8\217\216\363!u\361\212Kw?\134\251\346\316\012\000\0055\042g\230\010T\374\365y\312\213\333\307z\022\363\313\330d\354\226\325", 40, "\331\326\007\325[\226\374\225\250\021\225\214\012c\240\310", 400, "\016e\302\330?9\223s=\3620\302X\221\336==v\365\351iF\340\227\223;?b\005;\010\201)\327\262\006\247\0077\345\224\250=\026\341tP\303-+M\025\014[\367\007\022w\013k\374\032\234&", 64 },
+ { "\027\271\230\271\345\254vz%`\006\201P\331\3222-\323\374\222[S!\003\011\323\231\262+\325\224\250=|\277 \201\254\201_", 40, "\010\214t\242\242\352\314\021\304\320\367\322I\3120\314Ry:\334u\354\272\370", 400, "\334\230\272\330r `\333\030E^\250m\023}\302\022\025th\024\364UC7b\001\301\023d\272\265\207.\037\377\371\011\362\344\270o\2101Bk@\340\332\351\253\257yp\361\377\331v\016\3105\020\363\317", 64 },
+ { "B>\351\352\241\021\035\330U73\250J\266\243\374+\340\306\337\263\316\377{U\331\250\304\033jy\0032\216\021\211\345N)d", 40, "\250,\252\367X\257\022\264\331\373\042\223\226Z\021PQ`d\021\371\201\227w\374r\2028\033\3662\377", 400, "\315j\243\042p\000\266\016]BH\350\270\356\257\236\013\214'\347\347\001\010\341v\357\271\355avY<Q]8\337\036\343\017\035\247\2306X\011#\210\315i\036\032\265T\306\320$\226k\276\322\305\262\334\257", 64 },
+ { "`\320>\354\250\367\032\260:dRv\377\134?Abn\262\026\020\010\371\255#\227F\035\326,lx\251+D\372E.\263A", 40, "\350\3618b\270\207+:1\033\366\322`\321,\240\244/TDD\315\362\335\354\231\327\333hI\365~\270\271\244\374EU\237\270", 400, "jY\035\262\337\213\327\204\367\352u\230\024\020\021/\027\261\021\037D\2740\354\276G\035\222~\037\3065\201\002\350\042\371\276ye\020\253^B0\234\237v\363\252\267\221\305\373\336\016\034\351\236\243\215\375\236\305", 64 },
+ { "\270\322\236\314\345\206Gv@\370=\210\267\316\245\215!F\264\211I7\032\231\225\360?\020\375\256\371\0020V\204&\134\273yk", 40, "\265\257\037\360V\302\357\200\373\024R1X\211!\362\327\027\275\134>X\371\334W9jt=1\331r\241\0138\265\344{hWe^?rZ\301\305\375", 400, "o\212\230=\031.r\255\037}3Q\3413\237%\320\323\362\260/\014\377w\312\370!\236\335\371\010\377\240hR\252\254\016\000\030\332\004\243\305G\371\024<\025r\243X\312\371\320p%n\340K\317\346\2710", 64 },
+ { "v\203'\352\031\202O\037i\246\205\355\211^AW\331\251g\257\356vw5\003v\247\304\225\310\232\206\373kqS\360'\252\255", 40, "\252\275\277Tw\316\320$\304s>\372\236\264\330\354\341\314\275\315\034E\240f\001\330=ZK\001\026b\317\333\007\224\230\272\020\366\314\332\027\265\221\033\244\034fA\323\214\032\227\010\225", 400, "\327\262\021>\322}w \177&\302\357\351\356\350\345\217\300\004\24057\0254*\367\365a\200\263\353\000\134h!\226O\243\202\231I+\003\006\244JK\210\005\336\363\032\352\242\206\330\266JQL\013\356\356\307", 64 },
+ { "@\257T\377\244\313W+}\242\241gP_\252\2466u\016U\335\230\344\210S\323\030\01608\237\351`\320\332a\3702t\327", 40, "lB;^\004{\301\301\2000\367\227\226\026\222cQl\322\225\374\221\351\277\134\301\023,\002_c[V\333\010z\271\200tQP\237;\277\213\265C6\3474\361=cs\267\200\247\012h\253\233\017k\025", 400, "b\016\304n\314l\007\256\316\042\300\033\006\033\356\305@\237H\356s\026u\276\013Z\016%g\340\344a\0330W\004\244 \241K\216\255V\345\360;\026+u\340\374\006$\201\371\004X\274\332\360D\233s\011", 64 },
+ { "h\026\013#^#\3608\361\241y\335\311\364\257\251\0033\202s.0\032I\254\256\250\305[L\332x\032\035M\275\373#41", 40, "n\272\237\200{L\204\300\313\253c\332\220c\225\325\024\243\266$$\364s\344\271\327\270i\245\375\002yu\245`\250\042!\341\200\025\017\203w\253\002{9i~Y\337\316\215G\222\273\031\200\005\221)\370\004e\200\313\2531\335*)", 400, "]J\212\037\332v\225s\225A\361\336b\015`X\010\301\352C\225\020\333\215\226\354\264\205/\034\033\345Q\316\310\311\335\025@\323\376L\373\365N\014\207\333\377\373\030c?\263o \270y1\327'\011\275\300", 64 },
+ { "\037$\023\200l\203\315\362\001r:\251Y\265N\006\033\223\177\266\276}\235\042.{\340@(\361\042u\250\364\374/\017\225\203?", 40, "o'P\035\276\332b\015", 500, "\027+\241\256=XP\216\265\203C\372\367\026_g\042W\277\042\017Q\200\260\363\021\031\370\017\354\214H#\247\233\012\244*\277\325fi\261\344\005\026\206\262v->\344\340\332\367C6RpF\273Y(c", 64 },
+ { "i\356+\334\331\004\246\024\364t\321l&\254\240['=B^\351Y\222e\332\267\227++\320,\351\315$\025H4\036\307\305", 40, "\232%\344]\343?\304T\007\016\323\211\341\264\350\003", 500, "\376%\230\250'\221\371L\315\021\017^\256\034\273;\335\337\360p\305k\331\256\370\021\371\230kZk\234\353\026\227\340\313{\203\206r\032\034\034\374J\025\352\015\335 \373V\365\347\024\005\276*\363\021\331\3446", 64 },
+ { "B\276J\227\335\347dq0\204\341\373\352\264v$\311g\306b\314,\252a\030^,\333\202\327\353(\245l\240\313\267B\243s", 40, "]r\312\3217\237OJfZR\274\021\261Q (\244\241\013bm\306\001", 500, "\020\307H\233\2222\307\276\233\134\220Sc\226&\230\273\353\315\265\004*\253\030\222\031\016\203\022`\242\360/}\301z\226\270\231L\203\346\034sh\201\216\030\201 \223j\341Y\337\350\366\256\347\301X\372V?", 64 },
+ { "\367\373\350:\033`v#:q\264G\032\235Y\3419(\211bK\002\012\317\237\217\261\277|\367'F\225\346\322M\262oO\032", 40, "\017q*\266,_U\373\216\351\001\035\3237u\354\251^r\022'X\237\206\230]\331{@%\301\311", 500, "\211\366\134\340\227K~6!L>7(#\032\2176\230\000J\344\321\013\371A6\206#^\350/N\021\341\365V\211\230;\266v\013\327\000\221X\360\025ag]\344\011\002\322\357\363\254\270\324j\333e\232", 64 },
+ { "`r\253B\0145\341=\303\240Yp%\023\256\003\232\354\212!F\246Q\232\207\224\337\347Z\216\261\235\244c\332\242w\206-\337", 40, "Z\216\370\311}==\002\367]w65\340\302\340\221\002\032@\302\210AP\273\215\335\322Q>\011I[\004\231\241\310\246\022\322", 500, "H:DD\322bY\205\003\134,\367\345\265\352\021\331\375\007\261\2251\334{g\336\022bso\015\203\277\312\264)\005\2372(\335RK\225^qM]\2626j\266\324\006\023n\371\321\340\260\354/\357\225", 64 },
+ { "\036;{cq\252\200V\205y\367HY\042\373\323<\235\252F4\31601v\341\235\005%\204x&\177\316.\222\032\214\357>", 40, "\023\321:\305B\361\352\322\345J\042\202\333Q\214c\007-\026MN\352\252\231t\205\012Y\365\340=\212\374\245\275\021\177?g)\006c\0104%6A/", 500, "\250\303\354\237a\347#'\230\270\035^\357\275\231\254\000\001z\315{\312@u\365s\310x\326z=\007S\213\336\026`}/\324\220\275\217\232\323\321\273Z@\027\317|\223\011e,2\023P\021^\325\000b", 64 },
+ { "*z\225F\206\0151/t\213\026\2309~\313\313\374\205\235*\342\026\003\267\347\223\333\034:N\2051\001O\3031\207\375H\265", 40, "/?\0267\245\223\272\370\225Ad\214,H\332\265\376'\377NE\035bP\342\311\030\202\031\311\014\340\276\266 bW]\373\255+W\316\020\211,\277S\035c\020%\365g\350\271", 500, "\031Q\035\3656\223\042s\342\21538\340\235\226v\014\357\260\013\363\201g\241\271\226\213\220\316Zj\000\306\000\220\021V\027\250\230\302\265'\255\235\334N\230\355\251\326&\207\307;4\2550p\377d\315\342\222", 64 },
+ { "\247g\321K\211\224K\024H-}\217\260\373\332\325;L/e.\307e-,\013\263n\255\271\312\213\231\241\266\264\264\010\220\177", 40, "\026\227\357\264\367\222d\234F\311\363\371\316:,\366\007\007\240b\272\361\244\215\007U$PW\007\321jGm\033]M)@\326\234\341K~G\005$\024MU\266f\333\247\374\002_\34273\020\301\036D", 500, "\2177\022\2258\227\032v\042f\134\2542D\314\315\271\320\315\026M<\2708r|@c!\351E%\035@v~j\245m\276a\367\364\311\237\016\370Zy\352k\230\322\315\355a\3212\367\252'8\027\010", 64 },
+ { "1\010\366:\322\246\037\312\007Jc|d\302)\233\366\134\224\351\272\237\3669\305\247T}\245*\3402m\241\214\035\365\341\274U", 40, "\260\042\205\205\337\006\014\3020%\243\020\221J\361\352\337\003\317\352-\006\303\032\353\333\030:y)aP\003\206\376K\313>\332,\340\202\245\331\333\034Hd\342q\315m\002\256M@4\340:99o6\013\3201\253\355\347R;\022", 500, "f_\215\220\321\254\317rr\306_[\007H\267\334\332\271P\270\220\360mbZ?\012\220\322\357\351e\330E\250N\251\256/?z\261J\276\013\226%\224.4\327\367\310.\320\324bk#x\250P\205`", 64 },
+ { "\205\310\260\004\314\016\205\266\337>I\276\336XNQ\371\2057\001oj0|\033\022u\255\233\250\307R#\220_u\021\350\222\317", 40, "\321\234C\254C=\026\005", 600, "\230m\022Q\007v\331W\031'/0\0336\235\241\254g\231v\367\244\013[8\007\016\351\200\371\305b\203\231r\241\324\363/\246\256\222\234\001\223\325\231\267\245\005Pav\241\257\323\265\254\355\134\250\224\033\245", 64 },
+ { "\032G5\256\134!\321-\371\005\331b3\321\321\351'%v\361\316\2378\213\356\032{o\042\220\355\006z\277\351\332\342\304\3146", 40, "\200h\016\361\347\034\302\327\3315\371\344:\203\331W", 600, "X\275\364\247\307\262\230\202f\255^X\225\330\225\254\223\203iL\037Gq\001M\304{&\330\342\354\002\322\252\001\301=\231\313[)\236v\012CT\2719\340S~\371\001=\312\234H\323\217\343\020\022m\237", 64 },
+ { "18\333\277\367\221\366w\230\253K\012\351\2526\234^\270/\247JD\204,\034\361!Y\266\336\274\354\322\226R6\030\207\305\304", 40, "g\241\307h\373\326X\200\134\002s\362C\011\230-\012\277-\011\377\001\274`", 600, "\236\207\032|\264\025\321-\203\236Ug\306\360\366\262L\022\333\370s\207O]\344+\326\334U\266\340\310>=\360re\227\017Nz\026\216\331\204n$\007\365\323\257>\334;M\213\024\320\252_E\010$\042", 64 },
+ { "|\017\325Z\222\240\336\245\247\207\000\326`\221e\2770\360\226\232$\274\375(B\024\264\336t`SwhX\354|D\327Z\236", 40, "\356^\021\042^\200\272j:Z\377\216\247KVH\334\241\263\307\220\223Gi\337r\001Q\246\261 \207", 600, "\323\247\260\306\013&\234n\235\213;{\234\276\241\016\270\012\006\340:6\337g+\234\0264cV\367\307\361\341Iqu\210\307\370\0056\002\343Oy\264w\230X\3239H\024\207\027\377\315\204\333\340\302\220\250", 64 },
+ { "}\017\344\277/u\246\350\315\226\323\033\277\356o\024h\004\234\343\036\005\014s\265\375\023\001\363\206d\265a\266\374\036\344\200*\364", 40, ":\361\031\206\006\321\276\321\206^*\330\027\257:\204i\311\012\233\3404\236\0040Zk\326\007\013\015\310\022\367i\313)m\341\026", 600, "\236\317\231\200E\323\025aM\347\231R\345\020\324\364\254\367{\024F~]c\204\350G\364\364\204\355k'4k\343\221\034W}TL\311B\027T\246\353\311\333|\340\2266\231\017\323\307\254\337A\007\373\333", 64 },
+ { "\247KD\327\323`\024v \344}H4C;\307#J*\323\314\021\362/\214\254ED\201\042:\341\204\313\231U<Y\266W", 40, "\324\336h\314\262\251<Q\007P\374\310\233\224\3674H{O.\022q\221\213\363\347\252\352\2307%\226\326-\003T\365\030\344SLe@\305S\024|\037", 600, "\017Q\207\261\035x^\263F\023\134N\036]rHf\035\334\207\200\262;o\216\365\3141\026\254\350\253\276\226\350\215eM\224Y\273\001\204\345\376\257?\023c'\237\000\363I\231}\313\351qWH\016\022\361", 64 },
+ { "6\012.\223\353\036\330\263\337?\033\315?\016\341\331\360\036\237\134\011s\227\366w_\346Q\355/\254\370.F\237{\372/)\243", 40, "[\015\033 U\365\014z\026A\357\177\013\266K\355|\361Z\302\377{\277\206\0261\022*\266\036\263\314\375\332t\011Sk\354\007\346r\001\201r\256\333\204m\210hf\202\217E\345", 600, "\202\327\336)u\223h\241\021\317\261H\227\225\256\351C)\364{T\013\035\322E\256\300\003EtS\3749\316$\323\027\361O\276\230\255A\231\324 \320&\254\313\027B;C\034Aq\347\023\207\242\340m\334", 64 },
+ { "\331C\247\317\363x\301d\000LXa\313\324\301I\342\010\300N\345*n\332\260\007@\265ZS\026t7MMS\037\277\270+", 40, "7\303e\337\354\025\007@4\274\017V\334\304\353A\343\267\233\313\203\011\010v\204)\341V\246\204\002\352\322\012\326\224(\250)p\256EF3\315+\265\374\224\265,\352YI\277f\261atO\376TH,", 600, "@\215\3746\033\370\2115\003:&\015\3407l\334\377?lH\372E\264X\214\304\177'\320\314\215\214\272\343\244\242\263\273j\360X\022_){\232P\372\005\310\324\323\235\034\357\2550i\004\266\211\346)0", 64 },
+ { "\203A\344a\377W\007z\276F\237(\217\033x\177E%\302\014\277\336\204ggrR\262y\267\314\355\202U\315\2747\205*{", 40, "\221+\350\220N/\347VK3b\0372n\352E\325\361\013\235\010\253@\352?\262\246\265\232\250\222gV[\011\236\331\353\030\330b\021\016\205Po\011\2661)\242\333o\217\025\316c\316\262\036\331\253\202\267\225\337\211\031\256\230\232]", 600, "\002&\314\231R\247\361\236\177\243\250\007\341\257\005{\324\353\221\327\220\206\217\312\375*@h\254\264(\355'<o\224Pi\314}\367\202\017\323\322\303h-\301\013\350\364')\265\270\012H\374(\302\004\233\373", 64 },
+ { "\272{e\323\3454kl\257\2049\003\026\001\362)^\023\226%\327\206\177\317\301i\346$+\373\304\252|\232\227)9\234\222I", 40, "h\347P\235\022\335\355K", 700, "\373\3774\354pl\302{\007\015\347!\226\236G\221|\306\236\004\023A\017a4\345\0151\300\027\276=oA\324W\305\221L\377\257|\374\252v\221\211\273HTc\325\335\010E3u\010\134\354l\032$\337", 64 },
+ { "5\321\353\336\306P\201\015\200\3739#Ae\376h;\213\224\207\257\027\031\231cv\332\261\017Ti\275\000\256h\227\230o\361E", 40, "\372\263\224\037\247g~\373L\273\134\335\006\263\212\263", 700, "CH\344 \327_\261H\217[\211\024D\277\307\243E\037\267\324\211&|\243\317\257\376@%\032\242[\312\222\271!\355\324\015\272\207\275\256\371\036\264!\254\363\000\002\366\315\3555)\014\345\345\250\205f\224\247", 64 },
+ { "C\205I\257]\201\265\013\325[\333\327\003Ow~\315\025\365\027\231\276K\254mJ\262\204\363\321\217\035\277\305\031\343\014\377\253\375", 40, "\351gQ\345\255\300\323\213,o\374\341\353\374\2231\371^\342\017n\326\313\015", 700, "\225\365\267B\337\243x\035\213\034\253\266\316\250\034`?\311\346$\012w\311\032 \202F\013oE\356\216-@\321\276\035R\357\267\315\016=\361{,\307~|\205\315_\236&\034\345\257_\205\214|\331\267\232", 64 },
+ { "$\373\272\306\024\335\367;\275P~\253\307\370\024\233\334ntaS9\036\361\267]v\260\276\042\226\334\376\244\011\344\232\012b\365", 40, "\322e\315\334\202\261\001\255t\245\202\354\270\363\240R\271-\244\366*\371\372\330\361\265\353=\037:Eg", 700, "\263\244\240\304c\2666\007\217\370\313\315/\346o\260}\021M\305\270W\014\024\344\306\036\036\335\360\024\326\313A\377\223\013\371\270\232e9\357F\224sG`\346\300\266\312\204\203\002\321D\343E\377\2241C\036", 64 },
+ { "\027\001\004+[S\204D\251\324\001\365P5\250t<Q\247$\272\0121\024\217$\363f\370%\244\035\253x6\262\270~-\212", 40, "\017\032\304\352\351F\355\032%*d#/OL\030%pjcK\230\215\232:\321{\360\240\310z\204\3463\202V\266\023MG", 700, "\351\033?=\272L\2303_\277C\326\005\316\300\035\212\346\321 \012\302:tB\341\264\325e\373\341<M\034\375V[\010\263_XV\355\242\015\3505\004*Y\322\367t\240\351G\203\250g\267e\001\354\221", 64 },
+ { "E\013c*#?\370\020q\3402\241s\315\262\370\337;6\252\263\232r\024j\346\276\3650d\007\260\005\022O10\367\344\227", 40, "9\314\020d1.8;\376M\272\307\246\310fF\325Y\254\2016w\250\276f+\340\042w\237\335\337\222y\3719\010:\331;\251\244PK5\007w\003", 700, "\334\311\244\277\321\266fr\301\304\353OK\217'\134\210\377}\205\276\027\257\253\0365A\313\370@:1C\254\3006\015vz\215*\2777\003c^4ok;\304\343\216\357\322\241\020\314bW\316\364\012Z", 64 },
+ { "\315\342\314\275'! \250\013\374\300\014_\347\020\326\310\206!\353`\374`\323R\375\314\035\365\222\262gf\307C\004bqR\030", 40, "\033v\004\311\315\205_\211L\340C\356>;\351\362\336\362\024\223^\341L\0328\350\235\247\342\371\254'\257J\357\323\2046\362\363\247\260 V\025w\244\253\246\177&n\275\212.V", 700, "\310\245K\3722\340P\316g\224`\021\2139\225\207\324\277\251{;\036\376\001c\235\342[E\314\007\324\217J\316\331y\216\216\305O\210\374\277\3710\227\276/\033i\205\316\247/\215\256\330\037\271\273\251\310 ", 64 },
+ { "\250\003\231s\236\374(\2562\314\007\365\037&\2730O\376=k\236#mm\035J\315\333\335\022\377\236\2177\304\266\177\037\352\201", 40, "\207\207\017\306y\205G\030\023\012\312-\215g\345\252\016\244Z\215\200b\021\201D\253\306\001|\024\227\363V\313~`\207o\003\263\207^\246\345\025\262\252\036~\262\304\350kvD\365|\377\301\346(\252}D", 700, "\002\345y\325*\313\214\003-7\371d\134\220\017\215j\246\304iH\271Sx)q\275n\016\361L\253\213\362\206\367\022\017\375\272Q\245\220\353.\264\011?\257\311\233\013\3441\005\370]Z\235@\200\010\231h", 64 },
+ { "\266\361)\254\304\357\342\2513\207\262ND\246\022\210\234\262\013m\331tD\036\216\256W\221\337tW\201\245\254\035.\252j\037\246", 40, "\317\036!DOSD\017\215RzN\235\344\266R\320\275,R\030\356\3542\241\217\346\310\002\020\264\260\235A\022\016\037\247\027zb\236\023\323{\301\205\323B\342B[-2\312\363 \217~\216\332\347\317\355vnZ:T6/M", 700, "\254\355\201.s;\233PQ\344\304\255\230\272\016s\332\341\035\014\023C\254\352\2167\010R\037\253\025\325PV\305k\336\364\316\347\370\375\317\247\274\3319\015\375*\265U\222g\00126Y\265\220\331W\026\300", 64 },
+ { "\236\003\332\3109\303\331\310\316\216\276\331\011\305\003\217\316R\343oW\036\362\015\321\205`\224:#cpm8v\202\200z.\316", 40, "\226\1340\320oh\347\016", 800, "N\251K\255\026\242\251J\361\374\033\356\007.\006\205\035\317\006\202\272[&<\255\244D&D\032\017m\307\345 \001\016\272k\024\134\252-$\346Y\330\031\035W\250\031~>,\346</\265d\254\272A\022", 64 },
+ { "\003\307z\220f`Al\264A\241\224\354\237,S\2351\2305\376\241\000\241\274\246'\134Ym\222\355w\250\215\206\263\024\276\203", 40, "h\256\023\227\233\265\305sH\021\306\012\3042\020\325", 800, "\007\235\346g}\177\221\322\004\250\373 \244\336\026\255e\200\007\010\211\0311\243>\260\242\352\001'Iw1\363\365\223\255\3738\276\344\202\325\042C\2714\252\254\273\215\244\250q;\375\344?\201\233a\334\332]", 64 },
+ { "\272\210\031:*\250\216=\324\274\240L\340\220#\351\042s\203zR\2328\367\012\014\265&\372\225p\204S\310vFS\360\221\304", 40, "\244D\351Q\254>(\306\235&\226\314|\306\211\364\010\247\323\361\356\247\224\340", 800, "\360\245`M\036\222\270\011\207\000\377,LH\254<Um8\272\256\252T\321\021\203?<q\013\366\301\012\262\232\265\370!\250\032\344+\207~\245\367(\206\016m\270\271\367\262\011\326\306\212o\276#\332\255\345", 64 },
+ { "H\327\332%K\321OtRX\306\004\201\2110\233\222y\265w\351\317\304-cK\206\017\206/\323\023\2258h\335\321\035I\262", 40, "\206'/\005j\365\334\211\342\215+Jp>\217\034\273\023[\367C^\366<\213\302\352u\035\355\367H", 800, "m\230\007Ou^\233\342\007\326MA\333J\010\256^\336\231mw\3463S\203\035~63\363\222U\201\375\311:\032\3244T\316Z\031\345\021;\356\222\010\245\262\214\204\316\273\320\266\036^\371W\005\372[", 64 },
+ { "\025U\274\365\264D\325M\362\270\031\134\277y\240\201\017\367\011=\234B\206\216\275Z_:\310\022\034\226#\276\311z\356^\006m", 40, "\375\252kc`9\356\355\347\361w<iK\313\220\254\276\371L\272!.\212\214\342\177\346\376wm\223\024]K\340\256\204\2421", 800, "&\355\250\033\263\244\250\314\275Sk\021\306L\370\025b[\202\254\312\357\213}N\223\351[wL\327\207\035\362\354@u\345VG\250\271\364_\243\311\256x\372\234!wF\042\260\177{\214\242A\214\301\357U", 64 },
+ { "k\260/(;\016]&wH\037\213\042\300+VD\313\256l\3041\276\371\002Y\223\003\000\211\347?c'Hu\177u\033\350", 40, "\027\245\352\266M\245\221\263\035\270Z\275\014\210D\261\246\317p\271\331\364,U\235\337\007\201\021 ]\222\334\023\303\314m\321\342\020*\376\3037P$\265\224", 800, "\363\342\216\273\307z\253\373\247\247jh\273\305\007(A=\035X\327Bi-l\233\320\340\032}\312)\216\313\315\277a\224\357\237\216\274R\000p\347s\236\232Jm\345L\227B\022\237F\005*\037-s\344", 64 },
+ { "\206\334\365y\204xn\223\304\375\304\360\234[\226BS\134\364\3620e{}\306\230\030\134\374.\215H\347s\017\217[\327O\025", 40, "\356I\212\244\276\271}/\042rU\350YEiM\337\303 \220\230\230\360\223\352\246\025\256\365\007\002&\274/\342`\362Y1d\222D?\231\336\264\350\346\177Q\264\326\324\326\034\371", 800, "\250\307\350\317 \245\254\016\234%:d\323\367\351`\277\252\007_\224\264\344\374\371\253+r\204\343\311\337\024\326U\3339\351g\202\037\017\204\016\212\223 \337\212\250\033g\030\016e\323>v4~\324\024\324\242", 64 },
+ { "\2277\226\352\341\316T\3078\305\005\370\357\257\3065\270d\027\031\200I\245\016\243JC_\301\332\230p\323\345\274\252U\345\364\374", 40, "\321\257\023dE\264\202\246\274\342\024Y\307\377L~\244N\254\213\202\027\2641>\015\235\205\020\010/\333\275Z\350\030\305\006\3614\266\034\244\251\211X\241\217\212M\315\310K\252\374\351]f\267$X_\266\242", 800, "\303W\001v\301H\013*\303D\267\013\246\003\333Nr\213\363\214\023\231\300\334B\271\240\020A\214+\321\006\250\037\251p\230\373\020\262\333\255\210\251\031\013\206\366\000\246%\005\250\266\006I\274\257\337\031n\250D", 64 },
+ { "v\036\315v\000\305_\303\343^\006?L$\025\304Xr\014\203'\257y\2462\274p\251\351\221,\211\001\313\024\234ua2S", 40, "\004\222\311 d\350\256\035pO'\302\255\260%5\210j\277T6a%\251F\017\013\300|]e\360\317\0245\037B\216\276mMZo\247\340\021\017~\305\326\242=OJu]shHr\212Y\254\304Gl\236\332\2600\042\216", 800, "\323\004MM>\322@<\321\23672O#\360\030\234\335~\352\345;\016\301^\237\304\260T\274\304\010\247\356iT\207kU\207\134\353\235\251 \377\270M\303#\300\274m\3151k\2259\240\327t\275\371d", 64 },
+ { "\272\246\030\303\247i@\262\342\042sn<rR\310\330>\0006\222\247\366\013\002\205\034\363\246b\237\226\032\377\226\244r\243\377\362", 40, "1\324K\006\307I\330R", 900, "\215G|*\014\234\000^>$\313\363p\327\250QD\343\337\232D\042\256\331-\303LE\3602\0019\3137]\337\006\352\017\026 }Q\035\036\325\261\246\320\027+\357\007HP\200+\345\264\300\362\026\374\320", 64 },
+ { "P\255'\277m\2654\026\225\362y\206\002\311\313\032J\213\315.R\201\375\025rP\236\254\333\357*\221,Nk\025\004\014\005\271", 40, "\322\002\261\273*\303\310\2203\375\240s\317\027\206\265", 900, "\311{\342#h\025\214A0\351T\253.\361\350w\200\336\367\331\241\023\006\201\375U\264\376,AL\276\373\360y\266\243\017\253\252'\232\346#\207\223\254\023\002Q);r\223\271\025\322\035\336\266\001*}\314", 64 },
+ { "\375\021:w<-\363\227v\242\220s\343\37651\006\257x\331\004\213\363\336\266w9I\336\323QCN\216\354\215\306>[ ", 40, "Xm?\205\340\010\207\256\003#g\224\331\333XE\251\303\331Gr\025N\217", 900, "\316YO\221\237+`\004\0179 \317\322\220\374<\266rl\377\246\331T~3\235\304\014\023\335.'\273\034W\340\226\235\244 \033~J\222\021\240\014\327rD\007\235lR\022\321u\264~Yd\337\014\200", 64 },
+ { "\035\264\036.]6\335\000\277zG\326\304\363\236\012\215\227\334\226 \375\220\325g\312D^jv\014\011\001>\263\364\330\260\271U", 40, "+\134<\316[s\224\272\360\201\201\031#\233n\261\317\205v\363\230\262^\215\213b\2162\244r\322Q", 900, "\373fya$\300>\001=\340\335\240\200T\321(\317\310\227xM\241q\237\305\351\335\253r\027/s\277vc\212\232w\332\026W\362:\361=\302\377\350\317\227\365+\223\033g\337\347p\344\273\012\366\317\345", 64 },
+ { "\007Z\255E%IM\035\302\343\304d\276\354\312\042\211\015\266>|0Z\377\320m\303\254\334\231\206\266eFw\314\025\2734n", 40, "\354\272\341\373\202=\177\030\134A\224\256\246\217\336,\207\375\230\241D{\035\222q\275\220:o\240\321\375fP\303\375&Ak\205", 900, "7>\250Ar\323b\375\207\336\000\003\257\204\244\203\0137b\213\042\260\134<\372m\333*\3515\200\024\211\313\365y\345\276$\016\276\276\253\032Y\321\237x\367\000u\245\026s\015\014\326+\347\245j\251\221\236", 64 },
+ { "\234\012J\305\000\361\2322b\357\3757\252\2230^Y=\310\353|\002>\013\257eF\230\200s\275V\214X\222\272|_j\215", 40, "\266\305(\042U\226dza\035\006h@\035t\335L\247#\360IqL\260\307\347\2761~_\316\032U7\206\255_\222\205\301\207\324\325<\222y\373\335", 900, "\373q\021\255\334\2365\216.\366\206i\034\217\256\227\210\3675\314\274\3716G\032\360\030$S\270\313Y\314k\022y\2272M\033C\277\003\221\334:h\001x\306\302yx\221\217\016\277;\012N\261%\211\240", 64 },
+ { ">\346'MZ\252D.\350\326\241\240\2335cH\334\261f\371\224\236\032\375e\312b~\0165\324[\012\134\215=<O\243o", 40, "A?a4\221\216\211o`V15\375xM\205\306\134\367gH\331=\034\273o\3366Z\206\216\134&9\216\233i\003\275Q(\31463\033\356\3222\033X\033\245\177>\277$", 900, "\027@\215A\225!\233*\003m\364\006\373+L\031^\007-l\010\242\377\242Y6aa\235\313\265\260\262b4\325\305T4\203\011\315\320\336_>\004\342\264\353O\351\266O\226;\313U\214\353\362RI\277", 64 },
+ { "\223)\222Fg\257\022\2141\375\232Q\356\251\270A\021\237u\213C\010\371\243\250Z\233&\002\334-|\3267\340\026\354\276k\221", 40, "\243\370\024\263dz\312\234+\326\225\267g\254\317\205\346\350\303L\311\026\225\024\042\20679\266RG=\022\273\276\021\342\324\336\314\353\232M,M\334\025T\035\340\210\225\357\316)\374\265l\031\215\222\011\201K", 900, " \321\337\221\312.4*\351\042\242\026\134\221\002f#u\342\266\033\015\007\335\230\205\037E\016\324P\273\013q'\210\002\344AE6\206P\323N\221\260\362%\346\301t\246It\230RY\366\020\220\334K\017", 64 },
+ { "V\215\330\206\025D\027\363\346\134\010\376\201\305\275_\366\034\344i\231\017\236\007\006\330s\351\033\336\011g\325<mk\325#\215\232", 40, "\250\34170\264nk+\035\033L\023\210\015\344:\244\177\275T\356\371\367\377\241\315{G\021:\265\3209}!\2077\2657\322\331E5\0370\273\373dl\320\216\353\371\035\244\201\220\2469\206\231h\034j(j\002\340\232\230\273\017", 900, "X\2529\243\373P7\177\260\350\242\023/\373aE\036(\330\333mw\237\372\014I\200\251\345\341zI\031\207\262\025\315\217l\274u}\335\363\2123$\017\264\265[3\320\266\275\223\205,\345\036\134\305U~", 64 },
+ { "+\377\033\222\206,\265\206\231\274\207\004#\235\344m\025\352\362\332\210^\332B\226\212j[\023_\350y\363\012D\226\134\303\242\347", 40, "\012\325e>\037\3173\023", 1000, "\211\272(\272\325\230{{\033k\220\260\341\026\225m\365\201|\276)\374\355\255\221\223\352;\235M\001\010\006\023tE<\340?o\001\246\226\232\012\343\306\234\001Iq\371\373r9\021\032\016:i\277\351C[", 64 },
+ { "d\211\205{8\004\024\223f\011\313y\323\344\034\326\362\304X\320\241sG\235W\201\352\340u\215$\271\330\320\376\267\237 \324s", 40, "\310\034\021\375\023\330\206\027\323\1773D*N\003\317", 1000, "Pf\365\012\011i[?I\015%\343W\233\027\345\005\345\344\024\271\203\355\311fS\212\012\3022\021k\366\316\200[\250]\377.J\322u{\341\313`\323.i&*\037\327(\325\237\372\377\015)C\3507", 64 },
+ { "\263\011\250\275\025\371Z\0101]\350mf\343\257p(\232p\375\013\350\2748J\327\024W\310-Xo\033.I\213\263\307T\350", 40, "/|\3329\253\374\350\017{\246Y\213\340\232J\226\261|A6\2306\220\267", 1000, "r>\177\026\256\271\343\015\236\212\217l\216R\0140pA\371\321\027:<\005\270,%\364\263#\221\274\344{8(;cR\346\302,<\035jG\247B\242\213\301\310\334\025\257\026B5[\023U\235\231\021", 64 },
+ { "6P\203\023g\301\004\023\216)\306^h}\224-\020u\277\202\005\215\326q&\301\305-\001\313DU\343i8LJ\261%#", 40, "H\373x\252\220\007\246\251\353b\013S\223\314\364\017\305\326\341\347|\003\361\2411\215\363\2177\362}\333", 1000, "\035G\260X5\372\306\321\216\255\336\177\226.\2416j\302\315\220\270\2442\343(\236\017\376t\344\377\355!y\236L;\352\200\2603e\255$f\305\304\025\024\034\320+\000g>\371.)d&\201\225\211\001", 64 },
+ { "\275\267\241\311\255\003A\347la\224=\205\012\312\320\315nNQ`B:8E9\257z\261\210\251#\003\321\007\360b\210\352t", 40, "\326\364~\371\340\012\011\247\221\223\373H\221g16\321\317\335\271\264\263:\217\201\373Cy6\344bG\315J\376?@\353\341?", 1000, "\334g[\011\227?\316|\030~\233Q\030\326\237 \244\376\016\026C~E\032_\022S\214\317\331L\270\3726\030\306\333\344ce\006\016\371\254\251\254m\254\225\305\277\301\000\257\035\363\340\321\350\211\332\000\023\304", 64 },
+ { "\313\260\003\264?r\237\205$\220~n\016n\036\252\311\2724\241_\356}\362G\340K\373m\034\233\362\000\373\2329\204\253?q", 40, "\213\252\265?\016+v\221\020\234\261\012\254\305\200\326\027~a[\252^\024\350<\334\205\253%v\362\277Z\327\356\305/\253\215\010\261\221\276\224\306\355\237\033", 1000, "O\257}r\305\306\214\240p\023N\367u\336\275Q\340\011\005\377}\001\203\231}SE\221\303{\251\310\027\200\326\026\300\204p3\2726\212\224\355\237u\200\233\004\215dNd\243~\254\216\323\361\201\233\220\277", 64 },
+ { "\217\252\036 M-\337\272Pzu\215\221?!\253\134\220\3331\320\352\2520,\232z\372\304\3133X\320\270ba\042\363\222\335", 40, "\331E\026\277\027J\200\301MA\345>\306@*L~\347-X\242\361\035\042\251\230\177\334Q\235\330\371c\322\300\242*c\322\364>|\250[\326\350\022\241\216\303\243\221\267\206\253\225", 1000, "\312G{\134\201\277o\201EE`\260E=~dQ& \212|O!\2507\177\336\274\220\0060G\354\316\006\243o\254lC\231\371\002v\004\013\234\251;\361wZ\210(JFj[\210\360t\263\363t", 64 },
+ { "c\346\307Q\376\034zU\020%+\207\274\361.\222\231W\233\034\024\371\302\335\243\214\034\215\244\0076W\3629\367e\233\031Z\032", 40, "\042\006\220\030}-~\351a~\0349\354\270\336#c\301/\347X\014o\256\265>\357\004~\002\261q\215\012\042\025\370\260Q\236\324\262\356C\207\010\245\376*+\031P\320j\277.\234\231\377~+\003\022\356", 1000, "\233i\253\245\234\027Pe\201\215\321l\262\350\352\271\202\232\307\004\000Aaj\327W\265\372 \210\211 S;\037M\037/p\341\267\246\017\273R%\003\316\313\226\342\365\012\347\000\274\001\331\022z\313\2502\310", 64 },
+ { "'\241\373B\206`\215\306\266\042\273F\242\205\356L\323\370\370N^\332\331\312=|\015B%o\004\337\361\346`\372\001\325\314J", 40, "\273\037G\277\315+\250\305\332`Q}ou\247\2227\357\012\336\255\366\303\027\330\247N\262\212n\277\213\241o\0010\231Z\210+\312\277\021\246\010m\211\377\001\376\325\234\303\033W\3723\222\240\351&\215\005\214\220K\257\3311%g%", 1000, "\276\005n\341E@}\262\034\244t\311O&\246;9s\034a\342\211H\004\275d){G\013\374\207|\271\023\316\303\365\337DdUY\337V\0375_\320\221\337c\202Evx\024\032\266\221\274\213\031\025", 64 },
+ { "m\134\004\204\357\247%}5\327hN\356!\226\021\240\014\006\222\300\202\233H\366\245\235`\263Ua\000\220K\361\222\315\030\030V", 40, "Q\241\243\224|\243\305\211", 2000, "\320DN\260\222\254\336<\304\3538\324(\233\233\242\274S\372\224\216]\267\042\340\251\375S\213\335\217\243\3523\356c\325R\255\011\002\347\022\236fb4\242\363~n\301\226P\255\237P\030\362\324\252$\372\204", 64 },
+ { "\342\007S\226t\2532\237\254u\361MMY\246\177\214i\256\343M\246\362\204\355'\335#\264\276\315[\321\236!!G\134\273\217", 40, "\246 y\253.\353\205\022se\355\015\273\352iI", 2000, "\201\303L9\227\266\301\245\372\243\036\235\016@,\027\370\223a;\331>\271^9\342P(\003\344\227\265=\032\033\022\224\0165\337\002\375>A\316\203<\222mp\016\0243\3068/\353\367\306\270\306%\177H", 64 },
+ { "/\252\340\020\222?k\371?|p\371-\271\012O\002\255O\316\240&\303.\226U\204\347Zp\352\355!X\221\225\034\205\355\331", 40, "\241/\342\261\015ZAZM\217\316\374\357\212c\330+\203$\375\255\2075\341", 2000, "9\213W0\242u\022^\357\327Z[m\023\177\310\237\346J\340#(?4&\031y\310\005\34293\232o$?~\200\277O\255&=\360x\310\336\304\314Y5\214?2\313\034(\312\375\350\012=\334\263", 64 },
+ { "\2058\365\033v\273$a\001\305%}QW\037\376\212\377!\005Z\347\366\375\010\253t\032\240\012P\247TC\267\330Y\335\206z", 40, "N\027>D\017\2778\324\346\012\264?\222w\276\202\207#\367:\206.\0313E\363\330\275.\265x7", 2000, "\254L\354\232\275\274\246|\323\213\236X=9'\332\233uX\333deG\013\217\262y\223\314\007\350\331S\244\356m75\016\015\314\331\334i\350za\010*\360\373\206;\272\003\336|\007\226!L_C\252", 64 },
+ { "H\004\267\271\232\367\366y\362\342,\221$\037/82, \256\014`1\226\215\035\303%#p5\303\015\376\333\362\371\352\342\310", 40, "\022\3154\234\374[\232 \030HA~S\321\207\265\272\330z\236\356\212\360\236\323\215\373\231_\273Gk\220\256\331\264\005\230\267H", 2000, "<\360\033NOuN\014\207)\325@\302\0262\210\355!\367\033\302\253wN\372\370?|\217m\215\234\242e\243\004z\010:\330\032\315/y\326\213\267U1\362\242O5_=\264J\253\377\364\032\035W\375", 64 },
+ { "R\225J\021F\232\376(\260\300\0304y\230%\327\352%\352\204\211\013\362\265\375Jc\0326A\206\0357\042\005G\025\354\253\006", 40, "\232\212\252\221\374`\030(\227%{\023\327~$BM\376\226?\177T\3368\353\214\321\216b\367\353S\366\042\177\330;\341\230\353\214\363\235\324\005s\234E", 2000, "O\021\336_6\247)\246\005CX\031\302\346\241\035\365CQ\310\037\274\265\377\247\235\257&\254J\315\250'\210\356hl>\027\246\013)\350p\004\267'=\025\236;\220\002x'\035Ij0\373\265\013\357\023", 64 },
+ { "\374:\010\203(\0329\201D\346\246`\372\311\221\036C\256\030\024\335?\312\277\340l\344D\307\254\246\342+3;\312\301O\235\303", 40, "\237\233\317JL\336\027\026lj\264A\012)\005\225\210!b\267fn0\264e\2521Ko\235zd8]\031\213@\230T\3726\256\304\213K\211\231s\307\015\031!\237\020\233w", 2000, "S2\030\011\244\331Oq/{\000\330\177v-1X)\207\226}8\020\032H\310\000\215m\214;\011g\215\233\033\000\222\201\317c\237\2358T`:@\234\316\034\007A\337^\235\315lW\256A\214\237l", 64 },
+ { "\270\224\207\301\240\001`\311\327\371\211\2550\254>\220%k\025d\306xg\250\276\322\245;\326N\237\311\276-\372\321l\214\321D", 40, "l+#\215\304\301\264\331\303\363\021\304\237\366gT\333\340\240\302{\200\370\207\221\205F\243\375t\357w\303<\371GM\237\006\352Q\344lg\266q\020\223X\274\023>\302\007\371CI\240e1U&u\223", 2000, "\017\360_\264\037\250\221\031\134\274\315\265\177\377m\355\205\246\202{\016|\214OI/\327\205\270\017\220G\3402n\014\233\236i\017Z{\036b\356\340O\033~\037\362\306\253j\324I'\265L\211wO\000\237", 64 },
+ { "HU\270>(\233{\312\014\211\005\242\206\341\027\033\347\217KY\260\231\255\010\207\340\030s\037\340'\031\003\231\267W\031)\324\270", 40, "\304\232F#\277'\371\351GwI\343\233\374\2569\004Q\336\322z\305'\243n\270\023\377\330h\375\235\365F)\244yq\027\276\223'*\033\311\362h\221\322g\300\356+\241\300\365\301K_\231\363\005?[L\374\227\255j\216H\347", 2000, "\377)\253\322@\211\324\134CI\333\034>>m\374\270\207\375\025\372\367$2\235\337|\331t\251 \244\370\271F\012\341\250$\0221\210\276 \026\134\031\224\360k,8\034\262\013\360\225\260\352\031\240@\303\243", 64 },
+ { "v\014\202\367\330\015\0016v\220\351\311\003u\263\236\205\251\334\260\257\203\020\300\204\332\245\257-\350\3476\303\325\377t#<[%", 40, "\346x\031\351=\212\362\002", 3000, "\236f'\326l\345\210x9\310\206\304Qb\247\335\351\246\310\255\200\007\373\301\272{Z\360h7W\005\355e{n\312\226\203/\357}\332o\314\2270\026'\357\342\012\331\376\000E\337U\374&\256\367\252`", 64 },
+ { "tHN\335\351\026j\345\347\265\303js&\276\000\027`A\251\242\263\016 #\267\313\272\242\275\024\3430\333ah\276\342C.", 40, "\334\020y\342a\350!Y\332p\012a\247\217\223t", 3000, "\343ur\205\201\3444\263\011\373snRl\207\002\211\2332\260\302{\022\250t\210\250\030\177\277\304[\315}Fzi\333\210\303\234\010\337\244\216\360V\007*\305\243\327\371\007[\330J \027\257\1770:\001", 64 },
+ { "\020((\271~1\371\354\134\256\025\362\323\332\010\200\010i]3Fw\003\324\237\250\301\247p\311\240\342G\335\255\006s\265\351\373", 40, "y\365\011\223\001\346\262\360#\245\027\313\026i\213Q\372\031\005\017sr\242\221", 3000, "\042?\357Y\375x\355a\024\200\003Y\245\356A0\267k^\203 \224\232I\260\304UQp\355D\233\253\326T\353\271\022l\377\247\300\307\351c\177\233v\002\266\336>\035\2711\307WM\321\377\000\371\007D", 64 },
+ { "J\236\237\344\246\252\331\360~/\033\032kpw\321\235\036-}\233)\250Z\017\316W\3158\042\334(\216\227\373\324V\374QL", 40, "\216G\344\346]\013\356|g\005\363k\250\360\255E\225\263\327n\332\225\253\033\364\222.4#^-\355", 3000, "\324\224\245<*\230=\324Mf\352\224:\004\024\031\255qI\327\377'\327\223F\007\264v\037\367p7\301\366\253\225\266y\320\025=AZ\373\310\034\326n\311\272\270t%X\253\232\372\357X\270\205\201\376\036", 64 },
+ { "\350\312\011|2E\205K3\036eK\177\355\265M\344\001\255\277(\231\376CG\301'\237\346pN]&\177\273\000R\015\336\353", 40, "Ui\256\334\260\242}$\372\237U*\265\020\023\264\027\247\367T\234\351MN>\0126lLv\360\016\223j\343\220\034\325\374\365", 3000, "#\012 \354\261\226\226=\251\271l{\001\013\301\260\012W\247\020\366\204*\341\352R\371\353\010\036V\375\037\345o\244*,\000\262\013Y\375\311,\033@R\3140\037\264L\031\360\020\354A\210\213\022\201\336M", 64 },
+ { ",\347+3\364\305P\245\223\300<\323C=\213*S\373\0163>T\357\360\304[\202*P\004\004\224\33585\3249\332\032\253", 40, "Gs\271\225\331\274\026?XX\021\254<W\334\015p\261\224=\275\364\245\357\252\2220\042\230\265$\022k\315;\010h\316\027\234\211\316o\011\334`|\312", 3000, ">\205\205Mrn\341s_\217\006\000\134\375:\301\334!\341\322\202\262\002\332\365\257O1B\305\376]\016\021l2\225\277\025I\012U\321\363\221\002\206,y\210\224\217g>\0258\254\261M\340\3714\274\013", 64 },
+ { "\277\313O\320\373~\303\235\255\327\022\212&z\367\015r\270i\332A\222\320q\215\025\263\233\215\371Rc\337\012W\360t>\202\337", 40, "\005\271\372\326x{W\034\256\244\343\013\305\332\342\036R>\205'Im?z\350\014\006W\227\032\022\235S\352\025@O\036^\002\364\333G\373\242\201R\325\214\011W3\325$G\024", 3000, "\033\333ct\032Lr+\011C5\034>k5JL/l\003y$\225\022~3\347I\361fd\321\376\351\366\004'\365if\320\014\362\007\377\204\313\254\347\210]\033\331\025\005\243\255\271\037 \243\2633\254", 64 },
+ { "\022 \253~\327\267\303s\016\037\331T\333zT=\330}-1\364\022\343\360D\307\377<\232\364Z\001\205\360\236\030\376\2163\243", 40, "I\222\331\177\265\220\372\015\351\001\220\213'{\334\362G\365\252s\201&#\263\357&\366D\302\023k\360#\2510bJ\342e\306\220\005T#\323\365`\013aQ%j\223\275\335|\363\200\304]m\212\251\304", 3000, "#U.\217\271\211\346\352y\3342\365DQ\035\374r\207\331\356u\346\211\246\255\347s\011\252/\374'\357\002\227zv{y\211Rz\300\206\254U\225>\207b\011\222f\313\005\374\202\010\272\313\032\242'i", 64 },
+ { "\221\335\035[!\200\342\272\204\016\320H\250x\225\264Y\371rO]{\247I\316\020\314\376\256\267\331\302\311?\300[TGK\302", 40, "\214\217\316\227\270t6\374\010\211\320\217\213\220\023\004\305O\251m\306(\222\361\037\001r\3364\331\376u\203\344\273\342\266\224\201\215%\230=\235\341\331X\255\232\021\304\227\346V\340\331y\272\3321\216lKo\310\233\202\301\200Uk\355", 3000, "\222~6LF\263sIa\313\266\021\262\264 \250\012\2010\247\336\275\011zySN\270~\272\025N\025wT)%\206t\312@\253\352\033;Eby\270\231\025\207\362\363\356\3531\304\244*\364!\361s", 64 },
+ { ";\244\030\035\035\032\256BP)X\202\277\214|]c\002\361r\214\220\367\037hU\347V\241\314\012\250\250\321p \032\254\335\031", 40, "\377\311\272\264\0173\245+", 4000, "\317\333,\233.\220\351\334\236B\341\200\252\021\306\254\001\355u\307\262\022\275_\236h\035\322|\370\346n\004\303\205)\037)\321\253\267\221\236\2446\252\226\250\334\225s\2761\334\244=\323\376\240\364\237\037\206~", 64 },
+ { ".\362\232\346\221\3370e\363\2018S\366n\220\212CM\354K\015L\214\205\271\215\301J\003Q\240=f\001\240\036\027\363\245\210", 40, "\270$\3356\362\262.g\261\204\001\177tH\303\223", 4000, "\365S+K\204p\330\243\342\230\343\225\342\266\277\306\0113\001\027\214\326\2070r\334h\256\361$5O\033ze\336#\242\260W\321\311\211\020\262\342v~G-\223S\267\263\372J\305Kw\247\274\354\233Q", 64 },
+ { "\221^\330\236-6\207\017\240\250:\317\255\035\234\362o_\344:w\334\236i'JH\304V\320`\255;\363\011H^\207J8", 40, "\305OM\233\221\204\0010\314d-}Z\376pSi\022\323\313\023\370\346\024", 4000, "\315\312\356\271\366\301\134=4\222\013qYj\206\352|\015J\312C\206\327t\305D\225X\324f\337v``\035u|\317\206\342\000\367E\233]M\372\351\134\304A\245\271\206~\301\362p#F`|+\206", 64 },
+ { "RL\275F\026S\037q\0277\023\207\253\245s\301\363\002=m\307i\003\337\313\026\336x=\017A\212\343\206tGb\346,^", 40, "\262\215\200|\220`FH\353\321[\022\211\321\357\207\355\352\361LY\3416\032.pv$\202\200\311,", 4000, "\327\303\255\024\231\307\253\007\274%9\207\240SxB\223\037\367\203\246O\316\351\271\260<\312\223\305r\025\366(\214\276\334u\266\274\023}\343)Sr\332\206\224\333\375x\354\020D\243\013Z\374:\263d\355\230", 64 },
+ { "zoK\020E\344\266\327\350\326\301\042ay\277/\027;k\036\210\343[\377\014W\307\277H#%\023d\323\255\373\3728\355\334", 40, "\224{J\344\244\356]\345\376\235\2521|D\210u\376\0337\367\245\343\360\035w\342y\351\340\030]Ys\320\3052H\036\253j", 4000, "kX\307\311\033v\211\230i\303S\020\231\313\334e\251p\010QnHK\261)\001W`\275\261:\347\345N\232\346m^\042\336oI\216\342JB\245\347W\227\234K\264\330\376\373\003\301\277Z\007k\311\003", 64 },
+ { ";\346\033\200\372(h;\347\364\342\013\240\320J\254\311\313\326\332\207\020\012\276SlpS\221\206(\276*\012]2\264\357\353\333", 40, "#\233j\343\021\317\331\033\241\204O\326\207\375!\221\247<\271\237p\343R\253\017&\327\356\221[\255i\251.g\212Q\014\320\276~\244\005\3155k\251\010", 4000, "\276\001\247\265\317\006\331\235\337\310\301\212m\344\013v:K\337\134\360t\024\012\244\023\311u[C\303\324\033\351E\015]\234\363\351>}D7gL\324\364\244\306f\267\235\377\231z(I\214g\346\240\3447", 64 },
+ { "}\251_'\352\042\262{u\213e\353\301&,\023\227\327\006\206\356\215\016\233\255B\015\033\211l\015\360\237\322\225\013zx|t", 40, "F'm\243\321/\261\3521 \366\342n\017\322k\333Q\026\010o\306\004x\254a,C\364\201\027\231\232\215\353\003ZS\227$Xk%\313\201tX\327\216|\275\242\313s\320d", 4000, "\217g3\243_\275\301\025G\345\354\363\253\220/\024lL/\351p\337\302&\263\011\300&\23191\345uu\345\301\230\337\362\042\360\352rZ>\312M:\355]`\360\273\205\223D \002\042\221\003\306tH", 64 },
+ { "\245HR\177'\360\330\340\032[\203\254Z(,\272G\360\223\201\306\364\234'\010u\217\013\273\227\026\037\251\221\007\246 )[\235", 40, "\371\255\347\205\201U;\255\350_(\222q\235\261p\376\325\326&\203\042\224\3215Jy\352:\205\362\211f@l\221\015E\002P\207\252\200\233B\207\3329\202\042\256\001_\334h\023\001\003\374\216\237\353qJ", 4000, "\311\262\026\036\313\3243&UG\006\336\224\261\001\353\210\355\032\2372W\316Sw{\254\267\315\234\263\024\365\256\220\021\220A\231F\001A1\347\255q\224\305\220'[0\315Q\240a\221\236*\221\034\376i\313", 64 },
+ { "j \331\356\021\266\302\373\305\037\234\305y\013\367\372i\222\236#\253E\221\215\222\022\223\007\203\134\207a\300\321\017\346\222?=v", 40, "\336\220\315rU\233\013\342D\314f\016\037\230\372\007'\003\361\037\234Q\221pf\356\017\203\010\264\321\370\211\360\251V\353\236\232l\220@\004\027\334b\366\265\006p\225JW\236\227\251fiN\232\233\200o\234|\274\015\265\317\001\200\012", 4000, "~M\276\371\346\345\324\242\202\212\337\025\277\227\024+\037\371P\263U\015\016\007\362\177T\370Y\346w\025h1I\307F\364\303<\023F\014\350\221\215H\276\353\206J\244kT\031r\330\246\016hj\250x/", 64 },
+ { "\302E\227\301\254y7\247\337\332\015I\243\021\025-\320\361\315\262\377\327Q\277\367Y\203=\011\366u\362\006Ci\374\323\203B\236", 40, "\003V\223D\273\375{V", 5000, "\231\305\276)\251'^7\336\227\212\234\230\350\034\353\304=\215\033\230~\340,\261\351\317\246I.\212\023\217?_\225\012z\002\356\201v\215rA_6o\214/\264\347\006W:\006v\030\212=o\327\226+", 64 },
+ { "4\314(\203E\260'\215c\345\331\341\017V\311\346\006;\013e\312\022\215\021D]Y\251D\227\027\366c_\276V\022\037\340\264", 40, "\2103\375Ru\276NAI\345\254\036/\252w2", 5000, "\214\345$\014\000\333\027\204>\326p\320-\020En\034\371+\207\023\327<\314\266\312\277\010\353H\2049\037J3\220\247\3302=\032'\205-}\005BB\372\355\371X\365\325}A\025&!.\311\252|\236", 64 },
+ { "_[\347\031\345\200`\000t\302\207\327=\366\270/\322\350;\007\221\207A\027\256\250\364\313\355\370T\240\317Xb\224\363e\313b", 40, "\337\303I\267n\336+\240\134\243_\351\3100\317ZF\016\302m\263\220\302_", 5000, "\213\263Za\221\346\370\303+R\024\335\214C\222\372\332u\313\252#\347\353\006\332 \010\324\034\276\310\321\2165\2403\016\342\306\322\026Y\134~&g\367[\346\021t\221X\035\223\317t\270j\212\211\334\003Q", 64 },
+ { "\361m\332Y\307\254=\374\376\312Km\012\013\312\012\206\210B\313j\227:\323&\233>\240\314\211\353\226%\322\035 +\346\305\302", 40, "\211\014jR\233\265\360\224\042\274\034\326y\311\310\200\016\376\356\301\216\205\353+6V\302q\276\247\253\240", 5000, "\201\205\336\014]\323\211\027\312]\276\307\034f\002g\035\310\214)\310d=\376\334\221\252\271L\014\307\202\373\357\226i\0365\210H\0201\260\227\277\2070\222\354\230\277\265\341\251\037\030?\270L\300I\342\030k", 64 },
+ { "\302\265\353\372,\300O\312\032\035\205\376^\333?\330\362ZW\004\004\030\264\036J\255\341^b^\024\177\035_\0129\230\335\206o", 40, "\347/\326gV\324l\210\233\301\013\375\3151\314T%\271J\374\327\331\177!\035\312m:\376\247-\271k$JE\262\243}f", 5000, "\042bQM\317\245\256\010YW\336e_\023\354\372\206v=Y_\033!R\342\2103\331\374\204\034\016k\324H\374o\332\032*\012\014\031d\255k\324\300VI\351\244I\230\027\025\357G\206\300\000\222Mk", 64 },
+ { "u\263\272CY\021`n\345\307\203\030\012\357T)\377\317\357f\025\361\015\337QM\253\215\367\022\217_\275a\244=/\205\360)", 40, "0M\352w 0\255\033\242penz\365\252O\327\002e\241\263_\015%xG\333g\344}\340\243v|\377\240e\011\354\203\316\352\377T\2143-\370", 5000, "49\310^\216\307t%\355\337\227\301\342\365\302\225MG\223\201-e\203\370Q\376\205w]\264\250)\010\002\367\213\303\256\363\265z\364)\201\276f\006\002\366\2458})4F\250\364\233m)\207 R9", 64 },
+ { "6\353u\336\003\275APas\275*\356\210\325\270\274Z\303`N\022\261m\250\235\247\205\344\033\003\343\322o\000;\335R\023\273", 40, "N&{\2130%\355\335\335H\033\2166L\222\371o\374\246\012\033\256\301\235\271\314\345\206V\267?\013~\030\032)k(5\362\267\377#\276xRs\377\205\322\006!\225z\314~", 5000, "\256\213v\006\364ft\342-\254t#\024\244\020\015xM\036\337}\036\267\313C#*N\210\207d\351NG\037s\250\336\257v'\333F\253\244\242\207?\227\317h\337\025\344\2602.G\325:\214\337\373\202", 64 },
+ { "\261q\320\011;tg\211\011\333\365\306pUw\272\271\276Tm4\370\373FXt\201\262\031\270\3704\177\0214J0\234\235\324", 40, "\316\264\004\246\371=\031\371*xZ\010\256\245*\355\022\012\260\231h\026z\214\363\277\372\250\302X>\016`\336\345\007\304/ >\234\217\330[\362Z]\343\275e\341\257t\316\232\235 \320\273\004f\261$5", 5000, "?*\034kY\205\002\241\226\003-\242e\003$\020?\345\005\311y\237>1\241\3324s\363\230\356:\206n\042\257\326\366\276n/\177\360\217\322z\335\016\203\353\356\037\213+iH\374\350\371\346y{\177\304", 64 },
+ { "\027\252\311\265\024\350\347@\042Uh\245\021_\024;\241\265\2175\345hmj\365\325\300\362\000o\205u;x{cX\313\004\351", 40, "?\177\260\340\0031\020n\352t\261\032\361g\275\207`o\246\031\226\020\361$\352)oN\334\330,_S\2148\005E8\016\263\354i\243\322`\032\024w\307\225\011\275^i\312\032Y-,;\333c\375aX\002\214\327w\374\253\240", 5000, "\252\310U\245\245\134N\247\374z\317DEN;+\342h\231n\000h\015\032S;\333\245\244\201\340-\235\015\332\015\277\253\377\200\271\203\223p\204\252\341\264<\231\134\300\224c/\366u\253f9\337j\200\224", 64 },
+ { "\001\257f\007F!\225t\202w\366\277\211\232 \202\246n\204\200pgdp7\365\033#\221\341\023\310\230\216\013\245#\201\365\274", 40, "\372\277\012\266\031\334\375\215", 6000, "\273HFL\315W\015\217\2275\377\356*\225\2074\351p\227\035\365\271\016\312=\373\220\251be\344\345\223\363\261\035e\330\2327\377z\226\355\015\242\314V\326\331Skm\311\323\252Y\3536?.\334\013(", 64 },
+ { "\200\313\010\003\253\353HE\362\346\303J\205\346\232R\2477C\217\312&\023\244{\324A\310\2625\260\370\303o\353\360\262\267\236y", 40, "('z_\337\037M9\2131\036\204r[i1", 6000, "7\134(\341\220\2024\317Zm7\323Jg\267\274\376x7\377\253\016\254\366\213\363\24001\236\245'dr(\303\311#\015\335\223\273\342\323\353;\372\342\252\026\372\023tW\241\240\364\357\306!\3761\201\010", 64 },
+ { "d\345L\034\356D\017-\277,\007n:5\226\014\227\210jN\000e\020\307C?K\267\323\010\011\257\347\277\350\032^\017\227]", 40, "\3435\273ab+KN\264O\201\211\242\325\251\233\311Hi\363\007=U\026", 6000, "\016u\265\242\011RP\313\341\316\345\370\303\365\327+\225\205\352\312\221g\001/\266\027\312\333!\247\361z\277\336\303\000A1\3715\341\262\374\224y{\223\013+'UB\242u\003R\217\311\316\042N\231\265\033", 64 },
+ { "K\264\033\201\243)o'2\253\305C\007\355O\353r\310h\321\256zd>.\221\0330\244j\3349\015\240\225\317\347\344\260\220", 40, "/T!4a/\015v\243}\342\211f\274p\314q\267M\2352v\251\236r\015\327u\211\343\212w", 6000, "\037\211\024g\307|V\037\007O4\006\262\227\335\3174\306\011\015\230\277\022\207\374x\014\234\234_V\026.\352\232\231h,\236\205\035\333\271\227\335]\373\020\320\262\263\216\372\243_lA\244IEK\251}\272", 64 },
+ { "\003\006I\015\227\203\320\264\024k\261\002\261k\375`\001\247\333\260\307\364\351\217\3228\377\342)\243\213\3446\351\266)\355\232\353\271", 40, "\245\033r\214\247\344II\366\256@\036\015\015{\260[\270\227\253\254\350\265\355\024\247&g~E\301\015\027\006D\346`\243\317=", 6000, "J\334\343\350\306\134w\367\333\377\220\004\201\025b\236\245J-\005\370\004\235[:\3718\222G8\027\036\215R\021\317\012\013\321MF+R_\341\004\206#\230\134\346\025\353=\026\200\216\256\203\342@\344\251\244", 64 },
+ { "*\266\352zh\265\215\235\307~\357~&\262\354\232I\235\376\233;\3240\273r\243#^\003\354\0300\267\325\035bB\346\355\274", 40, "\270\274{\245'\246\304\0049\366\304\300\227^\302\024-\2140\261\236\323\273%:O\022\013\250I\354\275\223\372\037\270\177\301\304f4\2508\252\247\005pb", 6000, "\2323\251\356eT\177\244\020\004\264\251\320\327\0007:\037\322\227b\253Z\010z\305\204Z*\303(-v2W\266u\001\256\373\352~\017\215>\316\340&\333\331\206\030W\366\336D\3000\235Ma\270\031\003", 64 },
+ { "\257\247u\250\333z\015\007\221\013\031nz\017\272\216C4I$\361h\310N\324\260D\223|\213\252\237l\007\300\361\2308\033\265", 40, "\300\351\273P\263c\222\222=\356r\337%\037a \226\002\355\330A\207\033\306z\214I\374qc\342\347\251Cp\237\204\322\253\214\010\221\361\324\351\314\030\0330\266\202\002=A\320D", 6000, "A\371[p\255$\364]A\313\177\322\346\354\270\250h\364\347m0 [*\376\213%\211\025L\263!\224\307ZR\263&\202\324n!\223\3475\374:s\2036\225'\347\356\025N\207\375\337r\011b\037\274", 64 },
+ { "\251(R\225\234\026\3011t)IIC\345\235a\345\203\270\201\226\235\000y\024\356\240D\325f\210\200#\332\333\277\266p`\254", 40, "\375\010\333?yf\325\367e\202l70+\257\312\3228CX\2546\235q\203\245\134bk\316/\202O\0152\320D\002\3327\037\013\007>\222\247\351\334\2152\316\310H\304\251\200h\230\360\277Bh#D", 6000, "\030s0\2213\201\230\035\216*s\037\014}d\272{\006\372\177\237\036&\246Ylz\255\345\373s\042\333\034\005\244\235M%_[\026^\361\217\3119[\030\004\311e\205\316\003@\010\322I\017A%\246\370", 64 },
+ { "\012'`d\013B\231\266\374\302\004\336\016\202\214\333\0120\246?\302\270a\242\003\334\343\022\341\241\352q/\273\323;z\012\355\306", 40, ")mKR[\252\253\024\321\034\236\033\306\243\360\320\331\300\261\346W\361_\263\226\310\013\346\010:\037\200\333\3300\313\255g\223l\177<\306&#7S\216\325\346\335\027\244s ~#\256\224#\011\214\342G\350\015\021\315\307\335\214\027", 6000, "\023\220\224\005&\237LK\2207\363\221\020\037dL,\0230(\230\017x\375*\272\344\031\351\247\012|g\364\342\022\220\2566\221H\020\200\350\211\011I2\341\3277\3609\250\3164\332\377\370\241\227'\243\340", 64 },
+ { "\262\362R(~\026\210J(H\232\034\237\314\321\227\376\272\252\242\226\002<Eo\245[\272\201s\312IKt\341\362\277j\372?", 40, "\253FT\2072\260\012\021", 7000, "\361\330\260\377\214\211\335\310\2328\330?_/\030=\365ec\213>7uC\035\042\242R\377Pl\016\220T\242H\030i\320\215$\254`f\333>\024(\322\364H!\0008NK\000quQ>\027\363\013", 64 },
+ { "\230,\310\226\255\024)\207\357`x\314\210l\252\361CC\240|\363t\322X'8Xbz\231\245d\000\003\025]\260;\350\245", 40, "\255\007Y\022Gk\265r\304]\207W\352\027TC", 7000, "\355\353\317\011\342\342f\336v\370\313[a\363\352\234?\371\234\254*\017%=\032}\257\375<\355\000\232q\305I%\327\017\300\032\253md\250\037\032\306\241\320t\211+\231\364\247\375\314[\204'j\322\272\022", 64 },
+ { "B ko\350vU=\244~\353q\233\037@\022\215\273J\313\235\376\001\241\237\341\330\000\310\371J#)\335\262\245\000\207\325\023", 40, "\320\265\271\302]5\246-:J\0011Q\355#\373\271]\236;:l.\244", 7000, "HS\236-\206\233bC-0\327\311'=\206\014-\343gW\021\232P\246L\020:C\226\252(,\134M\240FIa[\260\277\225\015\275\376\000\273X\213\033p\241\371b\212\356\272\316\253\204LuK=", 64 },
+ { "\210\323\042\001Y\3122\230\004\020\236g\001\016)\260\323.\204Xj2\360\320\343\357aW\214n1`:\277\352\264\376\356\367\227", 40, "X\014\020\274\005N\342\004\211j5=\2640\262B[?U9\334\020\277\341\255\234:mzGD\270", 7000, "\035$\354\365k%\310\367W\3049\001f(\207s\242\376\026\361v&\226\042\361\247\201x\322\212\233$\210\227\214\250I\346\236p\370lp\012a\215\351\217\304:\326(1\013\017\257\243\352!d\261j#'", 64 },
+ { "~@<\225p\276\020\313]\245\331^8\017\204>\310\231E)\346\006_\362\236\331\253}x6\316O\237\271\216\000\253\304\371E", 40, "BD\027\320\345\300\267\013H\342\332\216.\227\247G \212\367\207\017i\365\257\212\317\333\351\217<\361b\010Xy|l\037\216^", 7000, ";t\042r\011\377\204\033\036\235%.\271~\235q\355F\264\215\236\250w\022#\255u1\252M\376\267z\337\004\236\346\203.sqh\223\025\002\033BE\204h\317\317x\026K\332;\372\357G\203\362\346\275", 64 },
+ { "\027\231\333\257\306\3570\325\033\266\036\204\206#\030\006nT \255\356\277B\331\201\340A{\212ttQ\177y\374\353\254\025\274`", 40, "\321\005\246\023\042\306\027\177Hv\351W\025\0158\315s'\022KX\036~\367<P\203)d\365\013)Z|\317\321\214h\310\274\005`\345\022\304\262R\372", 7000, "\201nK\001\254\2440\024\271I8\334\331W\224dI\042\207\241c8\326 b\202Uk\353i\300do\205\246bF&\316B\375\341\227P\250\372\324\016h\361_\0304i\271a\231a\366n\000\235\245\217", 64 },
+ { "\366\322\352PF\300b\247:\201\300\264\312b\320\271Dv\240pz\372\2741\236\364\006z\247\012|\004F\357\267O\261\370\3171", 40, "R\207Q\367X)\022\370\037\245\233}\307\007\3326H\322Z\216\333K\373\334\366\220\362\356}\263\002;\302\346\326\331\245!\315S\252\263j\343\323\331\3619\134\3739\017\317/\241^", 7000, "\371K'\333\224E\026\035\020\033@\315\214\02337\2348\03679\355J\247U\005\033\315\026\201R\333X|uM\0343T\257\277\276\215*\247\002\245\372\264jh\030\307M\037\351\355M\226\017\2357\364~", 64 },
+ { "l\272/po\312\302\256\357x\350\335\260T)\224\274@$\263\331\361\275^x\015\256{\007Bf\265+a\257\314\222\261_\313", 40, "H\314\240>/4\247\223\377^z\324_Q\233\206\023\355H\265l0RkB^\241\207\250\366\240\320\340@\376\362\270;\267#\363\277q=%\365I\261\310\3268\201\324x\257\350c\250\256\311\355\2006\366", 7000, "\350\312+\222.\134\265\366+j\362t\042\317\014\307\2450\236\361/\207\324\362\222\360\262Q,I\254s\370\277w\346\203\261s\302\376\300\030\321\224\327\003\371U\016.$\274\256\362\004\016\312A\236O\242i\235", 64 },
+ { "\035\376\031\3777\014\372b\372\316\207\314\334\245\242\374\022\233v\226\227A\303B\245\367\006U\032R\006\032\357\205\345\275Xe\344\001", 40, "v\277\337qw\301\203B\207\372N_\216\332\311p\0101\134\035O\232b\314\331\362z\036\005\327\205R\261\024&dSW\255\252\370\315UY\2716\335-DH\260\222\3627\371o\217\313\236\323\365H\310@\226\032\031\256\347lN\302", 7000, "iz\243\257\217P\205\341\3410t\250\341\244k\351\022\334\311\347I\221\216\244h\352\242y\355\253\374y\323\237g3\235J\232D}\333\310\217p\220\231\304U\026Kf\350\013\365\251\042\275\334\306e9\234e", 64 },
+ { "so|\334\340\356\262\312\334\317@GX{l\234\03104Tw!'\332\237a\270\271\0246\333\31447M)3?\036\357", 40, "\357\2626\371\375L\006Q", 8000, "[\347\034s\255S\2004\012\233*\013c_\343\277\213p\314r\274@\373\230\252):;+\007\232D\276\3402\372\300\330\3713\307\227\303H`\376c<<\306\252\251\0241]u\256u\272U\332@l\330", 64 },
+ { "\222y\345\357\037\366\020t\321\270\236H\262\273]\244@\253o\365\224\376,tN\245?^\216\034{\361\240`\022\001Q\354\362\357", 40, "\356\310w\353\307t\362\346'\204\202.\277\207\310\341", 8000, "\350\247\235\274\265\301\315\232Fj\366\3460\255\225*C\203G\033\320]\311j\273\264B\276\202\370\274&O\303\374\327$\373\023%7kX\0061\212\274\252\3371\332\213\232\313Cn\353\364U\326\215\352\347*", 64 },
+ { "I\221@)\032\264\220^\211\253\025\222\202\236z\335\177\333\225\247\134\321\377\217\321\224\024Ve\304\364\302G3(3Y\266\221\020", 40, "%\242\270\266\326\262\215|\351\353\370B\336\312g\222\343\227\366z\341\036\207=", 8000, "\234D\201\323^\351\320\034\2358\026\360\032fU\357\030\234P\354\247/N\250J\355\011&;\277\134Z\024\343o\262\270\206m\253\254\366\375:<r\020\314\277\277\302\262cQ\202\3358\311D\253\365\342,6", 64 },
+ { "\346\273\005\267\036\367\210\203EO\307\327\260\221\314W\202A\307\042p\230\322\030\025/\235\270k\212\203\245&bl\232\326\274\353y", 40, "\204\002BA5L\033g\373\203\354c\134sb\350\004\264\267\034T\034.\306\005\353\032p\357$A'", 8000, "E\312\203\323\236\305,\225\377\343s^\354\025\325\241~\035\230q\316`C\200\371{\361\240E_\270\206T\231\323\202YXHA\377G\251\230\343\012d\007F\035\230U\014\025&NIa\241\376\005C0%", 64 },
+ { "\373\256X\365\013-\301\314y\357x\002\354\235\366\272n\021d\325\341\026y\346\271*\020\317-\371\337\301\034L\305r\024v\270\331", 40, "\202P\205\367\346\016C\300&\257\346U\357'\274Y\213\310\321Jp\275\242\335W\2311\260\200\233\243v\357T\313\030\355\224\250\261", 8000, "\256q\200&n\301\330\300\200\3450\2610\0228\313\027\363\334\344\317\252\314V9x\232\356/j\034\000\305\236\366\306\307d#\253\240}\023GC\032n\211\277)\242l\022\026g\246\276Jp<\201\323\312\310", 64 },
+ { "\262\211\011\232\006h\205;M\216\327\373\207\327\363\226\342J\264g\256\236\300\014\025\370;\331\222\364\275\272\373\343/\242\264\303\264y", 40, "\016G\014\037\307$v)\231\306\230\231r\213*Q\227<*\351\177\134\334q\321:\351m/\024\241\365*\213\254=7\006:\230\3223\325\326\216\364OY", 8000, "\0362 \353t\315\311\345\034&\016O\365`\026\037s\257\323\345\333\366\352\257M\250,\036\343\017'\323R=\204\021)\240\022sG~\263\335t|\035I@\270xf\003\356\023\340\024\311\211\205s\231\202\024", 64 },
+ { "\227x\225\261F\374\302\015\3003\374\331^a\216^\256\265\235\211pgp*\027gk\360\366\350\326$6\365zv\341\373\377\222", 40, "m\303\220\336\354\350\224\231\023?\036\003K\337\255\265A(}\042OL\255\2240\036a\362\263K[\227m\005\346\232,\330+k\244e\251\134\267ys\271o`\316\211H\011J\352", 8000, "\347'\275\025\366\230\006\302@\225\011\011V\330\035\321\212u\361\356\335;\337\300c\2342\267_:v\005t\325C\343\242\251\271,\315'\312\361\200b\271\352a\370=\001\303\251f\371\016\030x1z\216O&", 64 },
+ { "\213\253\240\373\200\317\021}\006\021)\366\0036\301+\004:btw\240\307\214q\256T\341:\241TS\025\026\343\027_\033J\015", 40, "\214\302^\251(i\006\315\207\265E\011\033+\224K]\013\303F\307\252u\303\015buJ\250\177R\215\034\353\021\334\216\004\332\266\353s\321\334\327\237\200\022}\033\324\215C\222\263\134f\334\253\274\254\306\027\241", 8000, "\363\346tc#\372H\351\342\372\202Q\005\031\372#\260\274p\354Q\206\210\372m\360s\257\223\363@l\337\201\244\371\255\220\017n\262\253kV\315@\324\240\267\301\250\2674\036\334\352'\372\311\211/\340\325<", 64 },
+ { "~\320\005c\367`g+om\315GD\257LX\006L\025\342mD\304\353\333\273*w\207\002\010>\265$\034\321\200\011\261\347", 40, "x\373\357\225\035v\364\3700:\356eB\206\206\266\037L\356ip\301@eI\027\371\253X]]\326$\245D\357\322\007G\214\347\374\350~@h2o\021\001\251\247\224\030\250;}a8\256\247\005$\206\242.\212\261 ^\341\004", 8000, "pQ\266K\355\240CT\242N\326\002\026\241\300w\220\240\026NkK6\201\010N@\177\315\324\276\214\210\333\200\240:iX\216\206\252yy\322\333\330\317_+\235\273\317g\220\134 \203\302D\254$\360N", 64 },
+ { "\207\356hc\332\311\262\221\017!\224L`\234\322g\242;\311\030t\240\371\247\304_\367\262\204xz=\306\351\367\007\3268\002\022", 40, "\024\231\371\007*\236k\272", 9000, "\232\035\200\0108\362\327\310\016\210h\244bi\365}v\342\243\034=\366\313\310Is\364\332\360\216\247\231g\352\322\364~R\360v\370\225\225{\034\007\325\305\231\025%\327O\231\272\3101\011\341\356\357\322\204\243", 64 },
+ { "\347\232\037t\232\325\134\270\246\231:\000\216\341\364\327\243\233\336\374\233\254:f\234;\261\023T\211^\023\274e\277\331\224\254\361\022", 40, "\231\374k\017\264\022Z\310E\224\373\215G0\323l", 9000, "\242]\315\226Pn@\346\220\257~\134\340\031\325w\0057P\354Z\227\213\004w\255\215\244\305&\020\324\220`NY\262\227B\261\357\337\347\035\35701,jH\237\031\0423\262\321\344W\307\242\042\204\370?", 64 },
+ { "\351\222\0071B\005V\022\340\224\323\035\331ry\303\333\214g\374WJ\346\310N\034\027\371H\021K\261b|\212\221k!\227\374", 40, "\235\3570\3671\254\004\034^:g3\274\230\042\314F\310\274\355\226\001\253\315", 9000, "#\335\027Q\374\272[\264x\366?\330\347\372N\366b\336\306[<5\314J\333%\223\321\016\266\333x\251\036W\266\336v\212\332\333\3400\006\302 \334\370{h\015n\374Q\276\026M\244P\026\253\352`\206", 64 },
+ { "A9\335N\304O\264hp\360\036V\277c{\356\037$S\331\272\246\023#\254\341\345\252\372{\213q\005a\022\202 \336G\032", 40, "r\333\017\004\376\276\347\351F\362\241\221\026\211HC\374\032\022\004\0101dU\326(\343*y\2774\370", 9000, "cux \267k\007\254\005\211\304\235G\336\200\207\003tZ\010 WP<\360\3709\037\367\233\377\222\360\210\325Q\351\002/\325\134\234O\275~\363\355:_\267\006\316\362n\012\225\265\210i\2451\355\327Q", 64 },
+ { "~\327n\210\367Y\1346o\001\235@\031\265\026\331\247;\262v\347,=\310\212}d\327A+V\372\246\011\034F\307r\021\236", 40, "=\232\244p\320Q\003B\224,\020}\340\267\270o\2408\321\256\261\360\332\211\346qM\345\276\004lS\270\010\336|\274\215\224\257", 9000, "\255\314\336\220vu\346\367\321\212W5\010+J\134\275\310\202\350\334\214\252,f\205t\2525\252\344\344\023\277G`<\353=1h\257\326U\025SY\027\352 x\340\026\272\363[\016{B\364T\347\337\025", 64 },
+ { "&\007\346\021Eya_^(\217&\372\222\020X\246\367Q\015o,y\240\273\357\352\345\321\005\264p\031\034}e\244\274\003\247", 40, "\341E\304{\336\344\364w\231.Q)\334c\3024\305V\375jvam\004:\235\312\305t{T\205w\313\374\021\345\251\023l\264\036\325u\375\352\3710", 9000, "M\2779\257\330,\023S\354jg\345M\306tTW\036)\015%{\313\032\005\261_\377\370w\220\360N\266\366H\222U;\032`\031\261xzOS\356~\327\232\030\017\321!\334.\216\271\203\205\246\304]", 64 },
+ { "\214e\042X\244P>-7\351\310\252\242\311\223\032PFX\215,\333\313\036N\0322\322\325\3340\366\242\226\366K\345y~\306", 40, "\134\307Ui \271\307\301p\225)\267\024\373\303\363v\214\002\017\270#\300\321\024e\025\371\025\305Y\252w;;\327\357\337\351\2130\244,\252\005\316D2\375\261\034^\367Ar\002", 9000, "_\227\217\237;:(\330\254\277(\202\301\303W>\346\372x\012#8\272\244G\371\134\214S\301\252\010\241\315\313\362\027\035\203\023zUV%\347\337\354,\225\375\345\275e]U\234.\007\023-\320o\272X", 64 },
+ { "\254|F\033\270\264\331\247\266.9\233\013\212\036\3258H\203}\237\003\267O\325\317\336\375XC\220\362n\241d0\256\012E%", 40, "\340$\356g^;\224Y\340\2251\367\212\235\134\264\216\316\023\316;s\3010\272\340\242\313\244\206\310\354J\305\264\021\334\265\216g\331\304E\212r\010\341\224D\252d0\333F\246\212)\220\313\007W\2412\323", 9000, "\207w8\211 T\261\360\276u.\263)\252\303\233\270\317p\352\213G;\021-\025\2572C\250U\260\031\263\025?\343\216bs\042'\341[\007\242\337\342\022\374\207\237L\266\005\3305\305\366\335\231SDx", 64 },
+ { "\212\341\3665{\373\267\340\360b/\234\313\327\235N\242%\334\246\274\216\024\350\304\134\271\321\0316P\013$\332\370\346\010r\351\326", 40, "\372\263P1\315\027\012\301u)\234\334\365\211\333\310g\303\203\240a\312_\276\2755\333\324r\275l\014\2311\356,\017\272\010t\033\354$\010\347`\321\235A\221\311\205\327\276\310\257\305h\262\245\315\204\366\334\013\313\363\027\303\216\202\015", 9000, "\257\316\015\202\024\245\365\303#%\303\351L\327\332L^\317j\370\250\261\337\177\277\276\321\302\246i\275\270\324\311\224\264\252\024\346\262Q9^\001\205\223l\250\343\007+\372\344w\270\350\372\350\326K\015v\346\014", 64 },
+ { "\031tE\340P\214\336\351\311:\226@\254\206?MP\247\224d\271\251\266y_\036\371_\134\275\227\333\311\235Sb\307;\272\024\303\302\373\007M\3414\037", 48, "\021\201\255\007\253>\337z", 100, "\177?Y\235<\025\237c-q\203\035K\253_\276bE\236?L\220\016\237d\317W\376\337C}|\370d\000\004\304\331sJ\264\251\345\322\011\033\352k\341\272\323-\003\313ss\020F\201\021Yy\274\370", 64 },
+ { "\361\225EV\034Lo\324\302\177\265\004\352\340\300\271K\372\263'b=\021\014<\244\035I\231\276\270\273o#\334P\227\317\364\026W\316\267\033\030L;\331", 48, "\347\276C\014\313y\201\013\217\230|\315\356=\256\213", 100, "\373\302\027\231[\013&'\375J2)\3601\356\260\356\226\377q?9\342\027\023\255\346\2219\302\301\256k\004\224\005\361\027\301d\255\322g\375\177\033\302\226V\232Iu\300\000W\364\010'd\033'\002\271\243", 64 },
+ { "\232)\225y\274\233\203\256O[*\212\311\217Cr\005\332\031]I\212H\327\370e\203k;\340G\263>\321x\214s\350\262{\276\042f\370n-\325c", 48, "\317\277C\314B3!\207\217\214Y4m#\202\036\011\264\206\310L,}\225", 100, "U\263q5\313\207\331.v\021z\020I\204g\322\255+\326<\304@d\256d\020\203\205\342m\237!\337\005\261\353\302\216\215)\334\235-t\203\303\324P\3069d\314\253\301u\223\223\303\224yA\325\311\206", 64 },
+ { "gk\0051\366\256\225\374T*:8\016\307\211\042\332\356\020\206?\014\353 UK\256\300)\371\274\356c\374\250\042\311\377\275\242\334\276\247\311\015\001\321g", 48, "K\346\020\021 \322\220\223\362\0178i\341\312b\223\200\241\310\011\027\020bR\343G\305\211\220\340\315\004", 100, "\3516\020\200.\042\206\302\212K\340\037\370\271@x\0013\251\301\012k\272\352wA\216\006y\270%.\343?\341\237\253A\236\232\377\217\240(\002W\345\311.Z\327\224K\304\015\251\200\252C\021\316]X\307", 64 },
+ { "\241\243\332j\033\357\354S\364\247\334i\302\264[>8\351\326;A\030\003F\270\016S\320\014*\013\3555\320\270D\226\371`v\003\357\331\326\306\231\311&", 48, "\026|\301 \027`\301\032\2317\230\246\376\352>R D\250\263!\250^Q]H8\322\217\2377N@\341$\273\341\031\306\357", 100, "\377\025\011\030Ve\223\272K\275\230\002&=V\271\2043\243\013\004\270\016\201\377z1\335\241\005q\035\016\016-\314XR\177\3371N\252RR\206v\351\032q\370\207\321\033*\3410\277yjA\320\236X", 64 },
+ { "\304\2053\034\271~\263W\025\300\311\253\321\265\201\303s\336s\262\230\212-\374\3737\336\331\0256\205\226o sp\264\274B\267\356\034\362\0130B\255R", 48, "\376\306\264\014\224\245\200\022\344\367\353\213s\376J\261U\200Q\371\330!\263\206\360\251\351_\0140\205\004\255\257\205\261\017\243[U\351\372~:\0350\211~", 100, "\272a\373KE\010KD\001\361\134\311\345Z\005\341H\036\035\324.r\233O\037;\217Z>\273\355\267\035=\204\215\017%\270,\256&p\212YI\356\246RL(\025Y\026Yd\237\264p\364\244\134\011\361", 64 },
+ { "\246nj~QCr|u\223\234\331<-\226\320|\356\360\236j\273hr\021R4\254\034\345\032\025\262\037\042S\274\216\236%\375\361\224\006\243\357f\335", 48, "\200\025\363A\033\210\2445\333\263&\244\002R\026\227\221\024\177\236\201\2766^.\250\262\240W:\352\334F\233\210Hf\022\273\305-\273\312\246\274\256\257v\344\361\346\333\233\216\375\033", 100, "\336\007\257\321\234\327\323\354\251\223\244\266\331&\032\352\220\335ZJ\000\002\222y-\022\3648\256'\355\003\256\333\211\276\321fY'u\252\017\202U\353\0123G\012R\002\206\374\023\026\341@\356\202\330\243\357\017", 64 },
+ { "\334^\236\035+\253\251\261\313#*C\273\226\221\266\223W:\322f\340F\234\241\277\031\002\263\340\347\271\017<\025\261\334<AU\315OIv\344:\024\037", 48, "\202+\226\014pS\017\366B\211R'\301n\242}\231GsMa\363Dn\233\001s\230z\037\232\3155\002\317\362nu\341z\375\346\370YV\325\217\306\042\004\2313\0246\225\202d\266\025_\375[&\302", 100, "\3634:\235n\204\257I\263Rg\300\006c\262\350\226\015\004\312\373\326\263G\261\234\265\014_\020\334,\372\243\204\003\017\204>G\313\035\343\042~\342\320\221\2420\042<2ZN\227Z}\221\231\216\134\334\263", 64 },
+ { "\322<\332T\271\207f\242\337\256P\376\331\274\220E\351\251\331b\324{\261\315\353\237~\267\134O0P\244\205\334D@AR\020Q\305!\217}\204'\034", 48, "\134\277\013\215[R+\223V;yS\134\334~\311\351\017\321\246k\0349\207\300\221\332T1\227z7\256\003\371Wh\313]m\037G)4\257\177\031\373I\353\227\355\3219\365g4\010\362\243O\251\333\342\015\300\227\205M-\303\261", 100, "Z\032\013\325z\312$7B\024\241\035?$5\005\374h\300b\267\375Ah\251\311\210l\200\323M\333}\203XIf@\367\222\261\357T\301\256(\004'60\334\304\255\237h\300\361, \336\331(\364\335", 64 },
+ { "\333'(\350\331\304A]*\271\315\031\002\372\361\011\035\211\317'\010\355\320.\376\265\262\343F\222A\006\312\037k\224_\327\003\312\256x\337>\304\205\2778", 48, "\215a\037X\3719K(", 200, "\203\363`'\026\233\226/+\260O%\306s\361\015\257\363w\326\042\212\250V\025\023\271\344\351/73\211\206G\2627\355Y8p\324\224\335\242\213\247DJ&\343\031]\362\343cQ\360D_\031\300A\005", 64 },
+ { "y\042\366\351\206\036<\213\015Y\013Jb\245,\200bcRZ\253\013\034\346\276,\352\221\365{m\215\255(\251\274aA\231Qo\314\220\000\215[\025_", 48, "/\272\034\233d\274\376i\001\316s5,.qf", 200, "\301\322mH\217C4\207\042\375a\020[\315#$\336\264\012-\236\372\247A\323p\016\370e#6\221\224w\305\331l;A\233\374\311\231\030\236\244\022\002\336\313\202\300\333@)\316\325\042\017Wl\010\352\244", 64 },
+ { "\243\034\205\371_\266\334W\037\345\300+@*7\033DN+\025\366\316\235\3720\177\267J\007\004\3030a\356\245!{/Ph8\223\037 %;\320~", 48, "\211\332\202T\321|k\027\207d!\223x\036*O\210U\005\032m\245\306\276", 200, "\214$\026\211\256*\222r|\334\364\000\232\005/I\277\030\015\264\336\344\000q7\235G\327\274AKx\221\257:\004\244:?\030\257^r:\251\312vj\036V\304\231K\317\325\215\015\202?\305\357\225\311\223", 64 },
+ { "BS\204Y\226\027rk\273\322\022uFq2\343\324\373x~?\026\003\350$\370\276\242\314\374\177\325Z'\035uh\2213\204Z\314\340\243\221=g\037", 48, "<\225\331HzR\273\272\323c\203\312Z\302\344\226q\305\0241AE\042\251\336\274r<+_X\244", 200, "k\042\212\314OLc\3329\373a\353\256\316\2749>=\244P\260\134x,w\006\203\000L\004\230\223\025&)\241:\255\226\210\265K~)%\134a^\236\215I\023\3725\346Z\327\204\221\350\032\334\376\355", 64 },
+ { "\025B\316\263\210\305R\004\013\362\2013\345)\237\023\006uR\305a\366\347\310\001\215\246\021|\014\006~\340\274\335\302\017~\350C\310\0151\204&\015_\332", 48, "\243j\023\234O\272\233|\212\264\2748\340\023\240\360j\314,\213H\367\344=\320\205\336\312\333O\314\344\002\230\316\362e\347\033x", 200, "\341u\033\206O\346\300\206_!\331>\002\203\273\352\351i\007\272\371 \2139:\353\231\255\015\223\262\035\355\375W\213\004\237>&f\042xi\242\266\365g,h\350\324\007\271dt\350\323\267\317\232\346G\247", 64 },
+ { "3\303\365\236\271x\021y\227j\002\351\255\225&+{\221\326cUA\331\315\012\325X\035.\014\037\215ng$\370\345gG`\004\353\305\366\226\350\344t", 48, "\366@\247\226\256\020\017\215w\241/F-\033\315\347\204\250\252/\277\336\337\022\211\365\314\036\204z\261\226T\016o2\361}\321A\256\017\270>c\216\010\231", 200, "i\350\326-\342\324\032\205\034>U\346\2228q\207\207Z~\315\375\253\317\222>\373\035\212\021Kiq\316\352\036\3025\037\220\011g7!NHAO\332\257\220\304\206\202\236\246c*\3167\227\024\234U\207", 64 },
+ { "\311\3651R\231\201cT@H_\210\352\224;\273)\3454n\213R\377\022Z\244[\222z!\234\004\3760\3162m\310\333}\022\353J\016Vb\367\245", 48, "\267G\341\021\361\231D\345\301\325\277\376g\305\256\2147\271\202Q\320\341\034M\333\251wz\247'\274\311\373\037\263\250\345\233\004\337\345l\325\321\266\370k\222\017M\030'n\033\210\363", 200, "zq/h\233Q\377\001\007i\020d\371\241\217\354\307\022$h\236\210\210\042\233\370=\246\323\236o\222\337;\316\213+\332\251\343\020\320\006\003g\013\025\230\250p\263\252\367\362+L\304\213\356\222P\317\024:", 64 },
+ { "\030\263\375\013(\031\042\343_\203P#\310\021v\261LD\272\340\025\214\214\257\216\345\030Y\373\011\303ei\005Ni\351\274\301\233&\177\315\327b\2152\234", 48, "a\314:\244\203,G\3300\354O\351\365^}-C\261\323\264D\347T\032\343\306\260`\217\365\377\355\272\031\010+\354#hm\032\030\317\231\311^k\307x\273(b\377\216P<TW$\307\261\237\033\024", 200, "Y\247\035I`\256\255\214\310\241\015\026lS\012\221$\306\301,\236\243\244\267\356R\371\361\252j\232\336v_\134\241\036\347\177\030\232\341m\275\321\004V\302\004\257\313\326\252\213\273\017\022DjThp\310\010", 64 },
+ { "\331\034\367\005\345\315\220R\216\265\011\251M\376\301\344\245\215\3747\243\374\013]\342?6\325\027\222\317\322\364\214^D\351\235\374\205E\246\303\346\235\375z\301", 48, "Gk)\360\264'\264\352\010Bk4\207\214\377,\246is' \336\2467\257?\244\336&OU\324\227\206n\231\205\277x\244@\220\273\017\323\200\026\230\310\177rx\346g?\275\036\255\303\350\010\231\302\306AI\313\177\256Q\225Z", 200, "\302\317\001\330\016\375\277\254\207G\265}y\242.\311\275\323n\344\030)\007\207\222\321\245\013\026Gm\327\301\261\013\350>S~\213(\2049B\331\031\210LR\030\304\017\276\033\231\243\013\005l\213\356\371\250\373", 64 },
+ { "7\203E@\331\362\342v\215H\331TC\026\203\3529\3126%u\000\371\243Y\356\345\012\216r\010U&\255\250\230\374j}w3X\344i\005\375\251\357", 48, ">yI\373BV\344\272", 300, "\320!\237\325\213\376WNEzz\377\263\027\376o3\337\275\021\042{\031\275b_\276\037\255\362r\273\033\333i\032\235\331P1\022\222\366o\002\026J\272\254=<@\325\333\310\314s\206\322\222\217\003\261\253", 64 },
+ { "\243^\316\005Lgw%\224?\224\312\327\213`#t<Ws\267i\355X\350v\335\376\200Q\042)^\303\242l\2570\031s;\323\331T\317\355\204\325", 48, "\354j\360L\225\263z\373\344\214\303\232\025H,\222", 300, "K0o\374\235z\027\311o\303\233)\230s\322\354\0076\334\302\232\002\324E\323\027\354\025E9\363Z\016\340(>\212\016z_\253\334M\004\366-u\001\213\210W\276\351\230\026\356k\214\001d\010\332\000\364", 64 },
+ { "\023\134;\300q\314\030\265\207\212\361\313\372\264\332X\243\264(\256(v\253}o\177\213\311\032\257\255o\323\372\201~\315\211.0\271\302T\340\353\267\235i", 48, "D\212\335qs\015\003\036\222\2433\1777Y\304\203\010\354\344\224\211\225HU", 300, "\026\377.\360\022\037?\020\355S-R\016\007eQ\037\346\240\011\256f\247\220\275\316\322P\235,A\003\203\356\265_\013\263v\001\205\033\240\332T0.KQ#\023<\363\321\365'\214[\3431\021\042=4", 64 },
+ { "i\370\260\321E#\005\300\262\243\307\235l\134\036\300\246\317i\303\357nc\011\026\244]\323\336m0\254\343\222\036\242\316(v\253,1\013\272?\226\316\324", 48, "j\207\261d\374\353\025\324%B\3405\277\242\221vw\362.\371z\231\256\3656\205\235\025\247w\010:", 300, "\202\272\354t-\235\251\270+\342\276\221\177\315'\300\0139\257\362K\010G\350Xq\252.\007\000\277GU`\221}\36154\027\004\330\017\336\350\015\255=\330\274.\340\231\257\002-\005\315c\221\270eo\001", 64 },
+ { "\227a8\244\377H\027\274=\343\334\340\007\366\363\017\202\216F\330\274\351\376`\326\326\301\273\230\217H\2314\242\370tSFKZ\373)5\235\231\360IQ", 48, "\221G\231\014\177\335\260\337>\302c\361z\357(x\252\023\245\026:\242\377\016\036G\020nv\134\305W\010\007\214\201\300\242\370+", 300, "\220\134\335]\016y\345X\015\331\252Rtn\202p\360\370\363T],\265\246J\003\327pw\327d8Z\201u\023\316O\310\034fG\034\273\266S:q3\013 \322$\313\0035\371)\307\320\277\352\3237", 64 },
+ { "\377\223\322V\2421\377\021;\314A\312\013I\002`*\005\206\310`P\212\210r\267\352y\207'\001Le+\177\035\215\362%\227$\023\366\367riop", 48, "\010\213-\360\006\216\252\220\242C\241m\001\005D\017\274\206\220\007\004\134\273\213\303U\274K+\276:\212\011\275\027\024\212\032xet\2322Ml\3006\003", 300, "E\007\311b\314\240\305\203G\221|\007\203x\245u\261\042^\000\237o\205\252\002\272\343\372\376u\360\217iWRTE\0233]\250\267^\036\237\341\255o`\301\177\3269cM\253\235\232N\342g\007\021\032", 64 },
+ { "W(\0051\335}\220:N\267EJ\316{\242\334\006M\0343\347-\217\364=\314@\270\274\035w\242\304h\301*P\177\252\346\310\022m\237\017T\271\376", 48, "\310\204\034\365\366\004\3349\213\306;\215D\255\214<4\317@\274\261\272\343B\343\034Q\374\315?1Q\317\037*\002\306\244)\203~\005`\256\335\375\316l\322R\234B\214\265\370\203", 300, "\035\014\134\361\032l~\346\263\261\204\352\2608\012\261\3559D.\014\342\134\253\335\266\000(\003\300\022\200\003\037L\343{\201\364\242o\342\301F\367\222B\211\325\226\027[n\363(\273\341x\330H\256\346l\307", 64 },
+ { "\273\305O\363\244\251\012RTN[O<\200:x\371\134\252Z-\221\030'\217\032\222\177\253\321M\321\257\226G\365\335\203\347(7\310\005\262\312\366+o", 48, "C+k\220G\014P\262P\347\343w|\262\376\021\241Zp\200}\206I\310\364Ex\343z\255\263\240\267\005\274c \276\236i\031/<\200R\307B9\207zRU\307X\005\251v:H*w\033M\310", 300, "u\315O\345\351-\037:C:\266\343\334P\352\377\020XX\206\343s\306\331\260\215\015\362m\000\233H\035\271{\272\374\232\253\035y\320\270\015\367\240\304\372\277\021\356\237\376\276\355\273\370p\322\304\314N\212\031", 64 },
+ { "\037\377\234r\001=\326\224fV\371b\277dT\313\211\342\022\3301O\023\333\325E+^j*\266w*\016\355\217-\210\315\314\323Io\260\254\200\027f", 48, "\366\1344\335j\302\272\357\030\213\010)Z\200\303\256\252\002\236w\375t\206\272\002X\364\277\214\017g\016\213'\346|em\277\271\303\262\277\236\217\012\220\210\225R\325\363n\042\031]\245\323\355w\002\305:\324u\376\225\332\300\247\300\244", 300, "\310\366\343\314Ew\224/\024\374\010F\315\303\005\266A\346Nh\271h\027\273aL\371\024L\276\035\327\3775\001\361\247?\035\366+\361\331\021\250f\343^\231\015\334\265-\015\207\324\377\213\242\235\225\325\342\202", 64 },
+ { "q\216\352\220\031\240\322?\257\356\011\021A\025\366\2248gI\362\345l\024zM\215h`\360\354\042pW\242\201\177\372\324c6@~vf]m\315\270", 48, "\336\322H\015\245\215L\261", 400, "~5\026|\306\036\242\241X8\335\301_\333\342R\256W\361\337\3755R\0067\014v\203\345r?\350\365$+\365\326\353\241\351\345Q\377\203v\333g\251\260\265\235#\250Zzu\364/\322\342:9\020\312", 64 },
+ { "\254\002\323B\222\254\231\261\351\216\333\242\326`\343\223\134\243\236\217\030\311;q\323\257\337\267U\336\270\255\321\247s\042Ys\326u\324R\344\335\020\241m\256", 48, "\325\331%W\363v\345W\025DJ\267 \364vS", 400, "\235\322\177\312r]1sV{\202',\003\364\230X\005\316\021\301r9\323\214g\223\357\322i\210\355\032\205+Kh\037\247\216\360\247\024 \372\023\263[\266\033\213I\335\234u\030\007\311>\312\004\247\377\242", 64 },
+ { "\265\265e\302\307_\364\373\276.\2365g\272\004q$\030v\017r^\333\333\354<\302\020\343\232\376\215\031\2677a:\213\231\277S\232\261\017\010\042\241\361", 48, "\012\336\215\206T\254~\376\336\370\250_\362\017\336G\370\026Q\214\302\3651~", 400, "\025\0248\217\317\026\016\213\3650\257\203??\206\015\310\034y(5\365\265\277\344\342~w\336\220\004\335>\2166D\002a?\017\341\373\345K\274l\336f\304k!W\344\377\315O\023\217\303\243\366%\200p", 64 },
+ { "\340\263\354\340u\240\277Vg\021\256\236\340\215\342u\323\230\306\225)CE\013\330 v.\005\216\3626\007P\342\134bF5\277\021o\030\020\042\341W\333", 48, "\333\203@\327\021g\014\204\347\341G\300/D\226\363\366,m.\212\204gyyv\224\277\376\3474\224", 400, "\366K\013\224\363[\340\332\217\030\377~\236\344\251\241I\015DV>v\244\2319\254}\316\010\270\375Zob\025\354f\262\240}\231\316\310D\347\216\342\332\266\340\007\246\213\215!\022d\352rC\345w\320\301", 64 },
+ { "n\331\266k\177\321H\344\257\006M\206\270\313\010\035\226\3450z\345\235\213D\010\342,Dk\233\344x\2470y'^\353Y\264\177\012\220^\231\037'\322", 48, "#sr\373\330\006SzghX\015\241\336\340[\246\235\345\273x\303\020<\357\377\347t\222\323L\333\302\354}e\342\2500\232", 400, "#F\335T+\356\202S\240\036\262\301\212\035M\211\303\254\212a\313\270\263m\360\274\325#\225\241\222\333\010\244n\202\334e\353C%\364\3125\340\226\006\336Y0.\272qy\375\202g}\330\300\234\347\333[", 64 },
+ { ",\250f`d\312\224o\3502_\307\205q\231(\032\260\346\356&\274\224\201}\307?8-\241\270?lF\177\230d\313T\3034[-\000\2478J\232", 48, "\304]\024\312\306\300\227\350o}\211g\265Y\375\353\245\026\033S;\350\247n\326{7\340\352\260\366\257\206P\207\204#%\032IA\325\250\210DYAO", 400, "b\273\002\314\350|E\207\361\362C\215{\200\037\315\253\034K\275\201\273\217UQ\364\223\326s\215\330e\014V\241\376?\315k\134a\360\241M\317\266\312F?\032\332\036\347\312\216\341\362\031Wy\234\026d\323", 64 },
+ { "&\334>B\230\255\211J\214\377\011\243\316\006\240J\320l\007\353\335\343\332\3404\031\3302j\330\214(%<i\254\232G\206-~Vw\361*\226x\042", 48, "\256l\314+\350\021\200V\202k^y)\036!I\252\346\317\202\302\252!\232f\2167\316\232\254\272\027c{\265\006HYxl\320,\324ge5\012T\243\305\333\331\034*cf", 400, "i\253!cG\312\257\317\2517\221\337\022>\210wa_\021M\016\363x|\351\264q\252\247\037q\204\260\252\207\363\334\022\305\303(\014\254kh^\3033>1\257\232\267K9\321\252bj\231~\300\363O", 64 },
+ { "]\332?$\004\366\254\016\341:E\226x\306E/\020G\020m\026I\377\276\3517\331,\215\3616G\214\242\004\244\3532@\323\011\366\223\017}\327l\241", 48, "k\306\002\317\350d\233|\226J1\304\207_\012\134p\265/\324X\377c\323\247\315\353\213(y\011k\316R4\002\010~\355B\343\004\233A\361\215\202\360\350\225l\272\237\037\231\370M\014\272\350\376\373\304\016", 400, "F\306+R\313\313\356T]\265Q\302M\033,\302]0N\336\324\247\376\036r\200\000\177XH^\326}\032F\301(\267\322\005\317\210\276\345\360>b\311k#\236\232\230\001\261\334i\324dovY\276\016", 64 },
+ { "\3002\251i\014\011\374s\231\015kG\350\042\210\134\315G\301Y\247\360({\214\233\265?\262\235.f\354\001a\177\025\002\333,\253Fk\246)\033\265\247", 48, "\022\026\023\327;\252\020\230\316\273D 2\2074-\332\305\324H\236-\376\243\023\200h\235[|\245\033\311\025`9iw\356\330`\366\252\305\342\013\202\201\363K\0048\177\274[\013\0050\026\226\353S#\354\016\326y`\273cA\014", 400, "\321\352\231\215\0032\300\237\2400\015\276\334=\241\211\220\025q\201q\375\205!\314\036'\032\215\205\0041\245\032j\220j\274\233\237\177\034~%s%\317\265\037|>\037\247\021$\255\305\244@\304\227iJ\024", 64 },
+ { "\347\261\210\236[\316\372Q\373\250a]\234\250\256`\354\007u3\325\363(w\237\356*\212\012\367S\302\3633\016<\201\267\275C9\355\313\366oT\233F", 48, "(\350\024\322\324\365?V", 500, "\216\260OQ\232\243\336\366\227\227.6\271\255:\375d\272\255:\262<\376P\2549\0140v\276p\355\312\315q>cW\004\256k\177\200\352\373\134\035\230+\275E\242\341Z\331a9\305wnc\033;\341", 64 },
+ { "\332\321\232\255=\252\364\2101\220\275\324\022)\272.,\207\005\354\260\373*Tp\332\213\002\2209\302\225\377UN\371\226\271\347\035\344\016\314$Z\005IB", 48, "h\225/\013\200\311\354\235\201\312\212<\310i8\015", 500, "u/#\332\222d\355\2471*\030bp\331$Q\254[\252\232\223\365b\243\274\216)\302u\244,U\317b\015~\307\343\032\351\333\025\243L\201\256B\223l\013\200\267M\035D\346\203\324\301\037Q\000\007\226", 64 },
+ { "\345\3040\031\263\343t\315q<\316_\202\027\247X\240_\301\275;\203\244\363\257\257\200fm\334\370L\216<5\313?\305\211W\262\001\314\212D\237X\332", 48, "\367\215)\3624\242s0\026R7H\242O\221v9\316\236L\321h\321A", 500, "\246\314\355\264\242\254\245\2515~\235\036jS\276\307\364\204'\272I\310UY\256\300(\276R\313\336\007__\031E\240\300/\355\302\253+<slD\374\134\334\217D\017\277\213\313\012\257\345\264u#\230+", 64 },
+ { ")<\202\025c\336\000\134\314\322\362R]\267\016\315M\024L0A\212`\3066\241\331\332\360Z\346\026[\374\224\322\252\356f\034\275,j>Z\213\244\262", 48, "Ub\241\246\347Ra\255\251\336\265#K\007\226\252\220\300\013\207\037\330\325\347\332U}\377\031|\270\341", 500, "R\2132\007\270\222\307\022\200\364k\005H\305g\371d\243R\215Q\235\024\006\352t\320-\317\356\374?\367kW\353\006\253\355\220Z;\227t\306)\034\134\314=/\376\206\200$\226\211\007`\374\323\215\365X", 64 },
+ { "pJ\004^n@\211\310I\346\177\373 \366+\036w\030\022\211v\316\257E!\344\2632\234\355T\216\003\336\213f\262<,\302J\302\304\363@\224\310\267", 48, "\343%\240\355?TI\033n\020\017\177\317\256\325N\015\251|\011\347z\200<\271\3260\213\260EV\312\327\030\313\357We\033\313", 500, "\342\014o\300\351l\227\010\364\241!v7\252\370\325\202\325\002R\347\264VT\022\361z\303\310\036\203Q[\357\367r\262\375I\245\377.\221\320\023P\336\377W\273c.\3756c\277\273\233:p\330\325\042\226", 64 },
+ { "\210ll\2324T\257k\254\312\330\015\341S\301\377\355\212D\265\014N\260\202v\254\306.\371\264\264\347\270%\313\241\307\030\250\240S\340I\010\261\026+\322", 48, "AU6\242\222QF\265\340\334z~\264\340\232k\011bK\313\377|!\377\355p7\241[\302d\274Gb\032G\266\017O\250\373I\2458\340\012\217\025", 500, "\247\301.V\025C\231\205\373Jnl\326\2618\212\313m\210y,G\020\007\354oQ&|\270i\031B%#\232\311\3333\353_.gQ&[\010\262\2363N\321@\376\034\000\213\023\236\305\000\311\226\042", 64 },
+ { "0\326\250\274~\355\236\276X#\262\307\275K(e/\257\266\344\212\234\325h>\322\321\023 d\0236\203\2079> 3\345 z\343\362\271\335\0155x", 48, "\005\230c\213\230{\007i'\013\361ABj\027h\264o\307+\004\374\233\223\332\025m\134<\250\242\257\267\024\215\243\025\030\210\324\024\030\243\3245\001\021\335\3000\274\211\364\033\3308", 500, "\341\304\000\2766\033v\3629UDS\225wg\213\331\360\244\367\252\372\034L\373\307\273\377IP\207\247V\273AK\324V\234\263_\263\263\036\004 uH\202\340\213b\236\254\336\375\203\017\247\362\016q\220\346", 64 },
+ { "\237V\2613\263e\030*\265P\032\345\337d\301\335\255\030\217|Q?\026\346Tz\012\015\367 Z\312\344\201$\240\235\031\377|5H\345\215\301\015t\327", 48, "\301z|\375\223\324\351Y\177\325\372\031\244#\227v\311\322\316\306?v.F\037\221G\233\346?\377\200\237\224\343\011\353_\362M\042\301yi\030\260\201=\177\351\260\264w\005\250\214v\205\354\336\010\363\365\004", 500, "\362n\263\316\332^\2014\345\205\300\305\365\376BY\376\236\024idk\032\355u\271yaJ\333\351M\347\331v\015\223#\016\375W\237\025t\030bT\005\316\010\270\024uaW\226\247\213\355K\371\241\031\023", 64 },
+ { "Z\324<}Z\312\004\205\034\362\365\225\212^\232\177\257\300(r\267\030\0117\335\270\354\312+4Y\241\042E\315\212\020\033\211]\004&v\306\303\361\277B", 48, "\225h<\331\365\310\362\347oq\354\022\333\021m\232\204a\007(xP;T\302\262q\275\200\206\300\255\206\220\213\354@\333e\033\2401w\241\242\347\264\001.B\042\036}9%Ni\253\205\235\271Z\3507\304U\265\254\3725\236\265", 500, "Z\277\007\350\355e\223\237o\254\371!\243\351\042\350-\016\364\233\247&P3\341\350\205;\247\273O\252Xre\372\362\263\344)SN%\251AF\012\235L\305\222\031nq\017\334HWP\231+\304\036\221", 64 },
+ { "\362+\275\020l\220-\027\255\302\003\212L\352\300\371/\042\321y\361\331\224\037X\325Gbd\042V\262\370\177\271\357\314\251BP*S\262\244\014\351{\334", 48, "W\333\001kC\344\327Z", 600, "\347\012+?\027\303\377<\247Y\200\360m\247\332\272\226\273\256\033\254\001\362\266)N\265pVX\033\241\251oI\333\3353\200\270\035\237\265!\026\353Z\030\364m-up;`H\263F\321\233\321!k-", 64 },
+ { "\314\311#\207T?\270\042x\212\336\253\220\025\372\314\334\237\020_\254?\312\352\344\320.\333p/\273\351[d\250\012O\361\354\035>\343\327y\251)\254\274", 48, "&\315\257\244q\375jM\260\011\013F\216\326\2114", 600, "\015\316\321x\304\314s\252G\345O~\354c\271\254\371j\002B\317\007\357\345\362\273\230\326\230\320,'\203\335\330f\346%\360\372\257q'\005\177)\240?\240\230\200\364j\027\235\200\266(r-\335\027u^", 64 },
+ { "\243V\242\374-\376|\230y\305\314\334\337$\3249bK\360\3320\310\022i)rK\332\206\315a\303\345\011$\261\036\277\001\015$\373n\020\2140\377\351", 48, "Fw\252\245@00\247\345\207\201\236\374\007\267:\260.\202\035\320*\372\234", 600, "q\272\365\007\204%\247\323+\300\214X\374(\236\020\377\277S}\222\304R\016\303\332\033*\2563:f\000\262\215\343G\327\363T:\010\301g\033$\251\307\264\230m\315\226\271\234\343\363\374\010`\012\247\377\263", 64 },
+ { "LB\232)\011\316\200\323\234>\215\367\321E\017V\134,\200&a\236\134\370k,\365\350%B\026\275E\006-\031\360>e{\262\303\365Ta:8\033", 48, "zBr\3266zM\310\350\035\361M3\205\357\300\213\215a\306h\365\250\202g\337\241z\275\364\210\301", 600, "\304uC\301t;\321(/WD\0335d\3120\024f<\312g\372\000\032L\337\234#0f%\230\366(e\271\255\024\335\021\215Y\336\373\340O\034\301\256\032\223\330#\342p\325\222<\244\306>(\2753", 64 },
+ { ")C1;*\260\313J\252\203\034\314F\371\345\214<m\227\372pAl\327\203[\326\014\236\356\371\354'\005W\301\317\310\310\232; \210t\246\370^\223", 48, "\0036\217t\366`]\266\222\267\001(mr\245r\350\037\202Wr\027\266\031l\243\274\332\357\376\273\211\027\213s\3266E\343H", 600, "\303$\034\015\336\257!\303\352Z\200{\305+\305\375;a\331>\330\210\235\317js3\232\207r\320\036/\034\241\260\014\233~#\375\214\013q\327\025\365\001j\213+v\015t\257\303\352\352*H\257K\306\216", 64 },
+ { "\224\321t\031\261\362I\335m.\206\266\227}\223^i\236\276\262\233}\222\306\252\002\206,v\216\257 h\022YL\003\277I\266+p\000P\226\340[A", 48, "(\273\350\346\347k\353\022\022\201\352e\134\317\042\230\031\255\036\317\313?\042g`\231DS \025D\042\350\243\327\210\134\0016\042L\023e=\335\033+!", 600, "\233\23450\212\372m\212\331\233w\270\032\374\373j\002^<\316.\261Ps\247\3500w{\351d^\320\330\254\352\347\233\333\203\364\347\201\260\007\225\274\027\343\311\226T\221\207\265 ;\003\271~\275\237*\260", 64 },
+ { "\247\241R\241\303x\201\231\303\2228\270\210\225h\230S\206\376\017\210\256V\215\371\223\031\226\017e\357+\244h\227\273$7\223G\233\322E!\200tB\221", 48, "\324\341\244\226\271\020\034\217\024\010\352\015$\273X\277\302\375\004\011I}?\001\230@\313 \225u\357\312\022\011_t\036\204\012\252\023\345\244\336%\370\243\376i\3665\015Z\357\134\202", 600, "\036\224U\257a\306\355\351O<\372\250\351\014\302\360\020~\361\242\030%\270\033\301\310\014\256\301#\204\341\004\272*lP\351\340\017\274\004\321=\203\271\363\004V\311.>\322\362\365U4\346`0\321\220\276\343", 64 },
+ { "\342\223\355\255m\206H\340-\134\367\357\363\242Cy\207\362s\033a\367\377$\332g=7%;\321_u\213%\325,oO\334\256?\241\1770\270\005<", 48, "T\221\256#\230\334^\234\246t\301\256\2019\004\260\240n\374{\006<\375u\020-\361\264\303CW\027c%kF\004\316lf\313\222zr\365\025FC\246\343\351I\330\233\001\345\371i\266s\033c\237+", 600, "\225nTZQ\036\2149\344\253\357\027H5.\276\235\365\252\224:C\014W\303q\257\006\315\004w`r\233l\026S\013\230G\247\035\343_\021\134H@I(\303\257m\356\353\361\354L'A\355V4\362", 64 },
+ { "\212\030\031\247\326%\221\3514+@B\027\306\333\274\374\005.oX&e\354\326\255\345\253a\360\232\356\257\343K\374\237\317\023\270\244R(\352\303\034\012@", 48, "\367\231Q\247&y!h\315\323\315Ie\352\330\336\205\332t+t\001c\016c\302pPW\325\313\352Y\007I\320\364\0059x\24245!\0176\275\360\306\276\340\303\011*S\207\275\232\275\006\370\003\300x\313y\301\272\310\364\356\246", 600, "\323\242vD\355X\240\211\213V\235\204oP\327\200\224\365\3275OO\006\232\360\263\273~\255\323\3613\362\365]\255\250r\010\352\366\0308\010\341\310Z\2400\206\242\373V\236\230z\333\241\032\032\303A\234\001", 64 },
+ { "\244\324\240 =\357~\230\377N93\227\276\357\224\031 \366o\371\377G\251d\012\013\346\236\351\375\342\227\271\271\346\373\366w\042W\322\310\235\005]B\241", 48, "1\246T\373\275\002\241\246", 700, "1\030\274\257\036\365<E\251m\356.\206\206\364\333\267\271,\205[\367r`X\365a\230\011\0321.N\025\270\204Tv\250\350\014\246\214\260H\265\264_\253b\134\016\042\360\210\362El)\2119!\003j", 64 },
+ { "\327\023\345\310\322\203\277\0165\330\354\030C\204\241\036?G\016PU^\223\377\372*WR\215V\3168\226\243J\201\250=\210\037\034\345?\264\306\236\322I", 48, "+/\303EY,\264\024\254\332\223|\371\210f\362", 700, "Ds\250\230\265\306\352\303=\314\020\221\203\330\331\230H\300\015\251YD\037\042\353(\001\262\223\333L6<Z\336\242\037\004\311\012\303OJ\371Ht\261\255\355\371Zq\335\022\364I\330\333#f\272\027\233U", 64 },
+ { "\210\036\031\220\267\013=\3369\021\202\220\203R\241a$52\0239\243\365\371\277g\272E\345\241\262\350s\371z\365\364\022\200\322\027\203\345\251\012\232\020F", 48, "\337\345\207>\235S\230\232\301\331\344\261-\205\274\244\257j\007PX\017\037\032", 700, "\360\017\002l\347\336\213\357\000\240t\330\006%\301\341\215f\3521\365i\345\270@\372iT2]\342\345e\311u\3417\274\014\263\261\373\3254\221f\275\343u\321\365\210\365\277y\365]\340\372;\363G|\232", 64 },
+ { "\357\217\262f\233\325\3650r\024\267\274\264\221\023\377i\273MUv\215\236I\345\302<\273\325\316\265\274]1RH\270yM\021\000.\332`\247\207=G", 48, "~\214\270*\361C\037\207|x\267\021\333\027\363\3306I_\376\345\355\0267{\042\375\006ap\227\005", 700, "s,\213\3052}\245\352r\353E;\351~U|\333O\276,k\240\177e+\242\024:;^\012\255K4\245\212\042\376\037\245\033\355\042\236p\207\273\3456\257u\005\227\024q\237\312\232lArBH~", 64 },
+ { "\0018\243Y\355)\330\007|\373bm\266\337CI\333(\346\235\332\177s\201\340\232;WlO\013\353Iy,\270\300\012\356\357\345\264\321_4\330j\357", 48, "\363uu\356\257\354\022\002\327@\005\344\314H`\250\265\134\005b\3403Y\023\364\333\210\264\2741\351\2079\216\225EJ`&\035", 700, "&$\224\203[\324\0071\206R\231K]\245\215\015\256\376o=Z\223}\262\233\370\370\361\020\012n\377\024c\263O\306\030\004Az\242:\320\316\020\263\037E2\234C\227q\220\232\201Og\247?\265\276\244", 64 },
+ { "\217\033d4\231CTki\200\177\035l\364I\370<v\0300\330\334\013\315/^\2334t\315\334\367\247\361\034'\342!\241\006\277\363\252\364E&\335\317", 48, "\260\023\306\245~\274-GK\326\326S\267\212\200\350\260\215\004\310\361\367\212\341\373\035\004\247\212\317X{\211\260\243R\320<\326(\247G\034@\016?\267]", 700, "\200\000\202\325yun\304\207\241\211\237G\340\0352[v\225\042\310d:\345\024[\327t_A\252oi\250\030\376\344}\277\023Ob\013Xiq\226\223\223 Lq\241d\220\323\311\274E\273eL\313c", 64 },
+ { "\205H*\371U\266\223\300\330\251\236\305\214u@/\134\366)&\307\033\017#>\042|\360\030.\343\314\311\316\366D\235\355!\316\020 0}\034\030\267\317", 48, "\004\335P\231\267X\217\216c\347\303-\007u\023{\357^\212\255\231TN'\337\374\264\036i\305\220\327[\372h?\321\331\227\012\200\257zc\340\004\221\211\246)\257/^\243u\301", 700, "h\237\233f\334\277-\365e:I\267\2437p\204\350\331\350i\005\267\272\370\257\232\315,,\332\351\315\360K\177\3314\013\341\334\227Wi\252\333;m|:\012%;\210\230\353\030\305<\215\220F\005\343\234", 64 },
+ { ")\022\305\306\361\300\211\276\342\335^\263\360\257\015\345\322\2214Z^B\211\002P\343\367=\241\217Z}\023\355`3\221\323H\031\214\307\323O\002\374\221U", 48, "\006\025\224\362\004\313\003\032\304\331\345\373/\372\376\0053\377+\356\314nzI\366\307asa\023\353\227\261^\377\212\353\004j:\377\237\264\203n\326\314\362Jy\320O\217@\227\200B3\021\371Q9\267\033", 700, "\212\261\233,\042\243\250\260\355d\326\256f~\343\274<\225E\277\310a\237\267\276v\312\232U\272\257\177\226\2462]\262\037\365\304\355\336\354\203g\035\311\265\035\206\017My}Lk\222\034K\362\362I\222:", 64 },
+ { "\020\240\200\016Q\273\3750\000\0070i&bR*\375`a\224\005v3\030\01362\037\302n7\217\364`\307}[7\352\375\235\134\215\023\333\2273/", 48, "\300\322E\333\374O\2276{!\326/\355\277I{Q\341\031\024`\222\337w.\200\252\003=}\203\315\224\033\034\042\325\311\334Z\215\360\234\221\262D\206\257v<R\331\275\240\215>XN4\360\263\311Nf[x\366\320\205\257\015\201", 700, "k\031\342\357\021U\327\031O\214o\214\265\014|[h{\220S2\275\005j\016\025\230\222\235\002\221\012L\260\234\200`\324\370\134\275V\355}\261\344\306p8\241 \311D\336\341\236\256\243k\312%\225\042Y", 64 },
+ { "B';u\226\317C\307z\011\313\345\012=\3379\372M\200\331G\325o\372\014\367\221\001sf\275\355\201\221\033\042{\247tU\347Nf\025\301U\341\267", 48, "\303\2054\237\224\337+\267", 800, "\300:\200 \244\004l1U*\251\235N\006-\263\037\216\257\277\235\366\214\330\256\134)\373\377\242:{\337>\376!\022\037\232R\333(\221\313\353m\322\371\321\236q\373\272\313d\320\355\213]47\243\243\004", 64 },
+ { "\224\334X\314\371p\0310\2779rv\320JV\271\217\361\014)\367\024\365\345\006\254\224\254W\342\265\223{Q\2174~\372\312@g\003\015\314\365\242~\347", 48, "[\336y+B\026\336\220\304F\220x\357\277`\042", 800, "\306)\375$\247\2557\307\213\245\306I~\020G6+\374\250\231?L\275\030@\245\200\274\245\265T#F\3222?\224h\216Z\334\334\2002|\266\333\363\203R\033\316\243\021\217G\215^\005\356@\230\260\277", 64 },
+ { "\234\214!\015\311\276(\370\327\340\215r\272\251I\025\366\347\373^\240u\274\212|\317(\271\205\217e\277\373\252\243\223\346 \316\347\3356\321*\035\212\332\212", 48, "\261+\374\0333,\372Jz\034\222\345\233=\235G\300\307\257\244\333\253&g", 800, "Ty+\353m6\340\005A\316\257\336{Sd\2755\205\275u<\274\232\356)\333\134\006n\244\357\347\317\226\271\305\237@\347\202\020\316\256\353\247^\373=-\253bQ\001\370\376i\203\216\265\215\375\2636S", 64 },
+ { "\000\315\257\224\310\010\271\202m9\212\034.2\305:\316t\375q\212+\355\260A/\022\032\014\225\350\277\007M\273&\002\3738L\353}\016!\134c\037C", 48, "\333\257kres\311,\263\242\350B\203\361\204\322\216\207j\246d\267\037\342e\235\031\302\320\374c\376", 800, "\211\301\301\237\314\002\205\216\030{\015\274h}D\216/\262\314s\331\206P\275\332F\372\264\300U\216\034O\330\202\362`\3476\000d\337\037\032w\327\365#\233\377Wfo\361\376\244\302\177\361o\370Q\270\354", 64 },
+ { "\222ma\214\201\372YU\315 \371\0023\242\340\235G\361+\3025<\313E\235\276*yi7\277\213\3009\210\265\223\334\355\344s\322\224\240\312\263B\210", 48, "\367f\326\215\006\346%L\022U\305AI\343\307\223G\372[\320\012\017\205>\031\243\313jp\223\250m\213v\251V\177\276~#", 800, "\344\224\222\246h\225\325\010\301W\042\033i[\354'|\366\036\244lc\270J\217%\351\032\257,\177J\031Q\017\007\270@\254&,\336\317\312Z\321}\010\332p\200\237\014\247F\277\042Og\304\247\227\177\367", 64 },
+ { "\305T\312l\0366\032\354\011\006}\324s\203\365#\335 \324\375\305{\340D}\300\247'\254\303`\310s\266\023\003|[\367\371dw\2262F\361uh", 48, "I\225\007Fu-^\010b\363\356\312\306\034\002E\024\262\3241\377V\270MZ\334\270\314^Lm%\011R\3446\244\261\335\355%J\342;\301\353\337\237", 800, "\241\377\320\274$n\333@\2541*\312\320\245\211dJb: \266;\370[\2713\306\253\275_\217Ex\306{\271x\025\236\253c\024\311t*\336~)\355\026\016\014\022\000T\335\340[\360\264z\016|\323", 64 },
+ { "u\217]\362\346\353\367\227:b\317t\237a\344\024\267rn\332'\001\364n\042ar\020\134\357\134\314N:\264-c\306\012\331\342sRH\334\213\242.", 48, "\352\134\314`%\177\025\022\230|\001\026\271\314-\001e\006\360\250i\214\201\261Y\313\325]\354j\3024J\364\327\304\302\340\242\373.\263\246: \026\376)\265\210\031\212\365i\254\201", 800, "\366/U'\255>-\370\322\007\215^\264\321\302\234s\010\275\234\321H\316A\236ES\026\341\361\212\251\214is\367yj\316l\2133D\347\346=p\006\254\007\256\016\021}\367M\013<\374T\210$\357\005", 64 },
+ { "6s3\342\014\013\351\345\270\270\207\354\004l\213aV\227<9\267v\223\260N@\376y\3072\273\333\274\353Ni\231e\372\316\022\031\375b\365\336\042\023", 48, "\367\231as\364\300q\204At\274\235\343:\205`\017#\210fhG\367@\333\007\3350<\303t\377\025[F\036\235\214\246\326\024\312\257\372\003\232\201\013\212\220\354<\377\251\315\314\2205\360\304\020\233Z\215", 800, "\241\341\300\227\005\242I\331\031\003L\263Z\361Bp\262k\134u\330 };\234f\203H\217\033k=\301N`vwY1\017j\331\177$|\360j\014\021]*s\177~\223\237$Oe\037\216T\321y", 64 },
+ { "M|\210\012\365\274\254wp+\240G\372\252TY\020\227j\022\025en\0219)\235\020\314\364`#RV\236\261\342R\211\312\034.\3324P\0170\342", 48, "{\031t\3021\3312grv\026\210\373\311\024\024\271q\246\333'^\307\355\253\207\256\345\351\365\030\377\374\366\355D\332 '[`\177 e\354_4\242\316\221^\0170\315$\336\355#\037\343\215\224\244RL>\245\234\247\244\272\243", 800, "\304\257D\322\243\321\011:2U\022\270D,\375\011\236\265\262\331a0G\230\003L\302\241\220\217P\332\244\364\016\037k\222\375\200\277\321\332\3647\033\360A\261\365\013Q\337\036\355\2522N\263 \000:\2164", 64 },
+ { "^\372x\204\356\231\244)r\277\370\0230\001\365\232\216\214f\3107\302K\351\020J\252@=\377\261\247cy\177yz\211\035\354w-\347\313\327\363\253w", 48, "\261\352\305:|I$\225", 900, "p\326+\010W\257\325\370`Y\007P\030\035`\373\3055\215\031\346u\033\230\025\367xs\007j\272\266\260\357\265w\365\017\264\353)\364;\275\026\215p\0212r\206\277\260\207\272\373p\245\363<\224\247\300\375", 64 },
+ { "\306N\274\353\353\372z\375Gg\351\310\0275\021O\333fd\031\246M\223\211$Pt\320\253<\226rG6\333n\030\330<\265\243.1tM\035R\276", 48, "=6\352\317\376%,\275\010\326Y\005~\330\214\350", 900, "\012\237\260X\323\007\306\233\331h\3306VN\350\366\337d\232c\364\370\360\253~\017|=K#\251p{<Y\001\250\016\324\034m\251\332\247\341Z\253\272}\010kE\342( \303\367\315\270n\336q\225\357", 64 },
+ { "\017\022\307QF\026\277\346\3408\263\230\263j\257k-\247\354{\020\337*\337\017\340|\002\2542\244\024Q\243\257\201\232u\243+\013\030\330Wa\2631*", 48, "\254\366\243\320\356/\367\316\025\264\305\010\200\241\257\320)\264^\273/\367\014\341", 900, "D\021\265}\250\3378w\241\036d\274K;\374\037\320`\246\343\237}vi>\327\260<\230\021\330N\036'\233\200Y\2201\303\177\247\224\316\236\243\2265g<\374\363#,A\340\332\326\036\1347\373\372\346", 64 },
+ { "\354t\342\3266db\242\371\260\214\214:1v{\305\361\203\325\006\360\237\220>$\341\235@\201s\345\304E$\305\021eRg\007w\272\037\252v(\334", 48, "\221\370\256\213ef\313\016\345)\3576\021\026\302\365\037\134\353c\340DhGJ6\042\355\375\3328\377", 900, "\345l\321\036\300%\257\015@E^\357Q\275\266>\303\007\334\324\323\032\346\302n\345\214w\311\031\300WU\201\030x3\361iP<\012\321\360\314\017\245\351H\3768P5Y\255b(\367\377\225QD\244G", 64 },
+ { "\212h\203\333I\202:\236Ev\276F\360I?\017\270\313\366\342\033Q\204\013\025\020\255\271\261N\327-\327H\134Of\300z!\252\020\205\330\352\3010\273", 48, "\352\304i\241\366\217\357\216\232b p\017l\214\224\346\260\014\320&@\264m\367'C*\363\343\316\223:i\032\357\312R\333q", 900, "\334\371GE\312\010\314\257m\035k\020\026\204\266\255\300\251C\343\243fR\303D^\302\365B\014\266J\034\227\214 2\215\374g\012\361\261\247E#\327\314\331\326\224\023\267\015\266\212*\371\346\014\305b\342\020", 64 },
+ { "\251\205\204\301\302\247\005<\367\134\236\323C>\205\277\311\327\013\270\256s\364\212\366{|\252\374%\311\335C]W\215\312$\377\205\206\325\231z\220\370\367\355", 48, "\222\334\262\311]\217\213J\362\262\354\035\236\301\316\300l\210\205X\233\026\034\253\372a\037\025\035\235\311\224\313lf\316\346{\300\225dB\217\267Q\303\265\277", 900, "/r\256\217\345a\345|\227\372\323\205\274i\013\367\222\222w&R4\216\337k\271\345\342\371\345\205-\263\333\263$\217\204\347%2\206?\347\216\020]\202\240\011\276A\232\031:\252\257{\245\340\177\005\017F", 64 },
+ { "o\203@)\270@p\026\240\016>\025e+\365\367\243h\253K\134\252_|Z\247\311\374\331\220!\304v\203~Y[\001\314\316&\235\3662~'T\313", 48, "3Z\266`\240\015h\363\033;H\005f\316\213\360w\276Ic\224N\006\026\241R\345\252\317M\256.\217T[9yc\2416\220q h\214X\303@\264\352X\033\013i'4", 900, "qY~e81x\267\010\355\256\346=\217E\3054\367\033\223\305\265\240iW\205p\016[\367\253\201<I\014\002\012\232\314\342Kk.\006\033\025\212\306\257\201VC\215\315\211\345\010S~\230\011\306\336\232", 64 },
+ { ",\001y/\340\256\035g8\377\042\253\220k\304\360\314\232'\302\352\004\304\215T\233\241\231\221\220R\230\332\212\214\036\310-\004\344\213\260$\013\014\236\262o", 48, "\220\277\016\213X\031\232\241\275O\313\2761\201tm\004\346\257-jr\326\207\2622\271\305\301\036Y \005\324Lt\274\234KR\366\025z3!/\244%9\372\316)\337\320q\322\034\370J\313D\011\325y", 900, "\013W)@\026\234j\033\211\263\042\252\344\244'T\030,\315\236\354\262\232\271\015\324\267\214\030jV\003\034\300\376\023$\031\334p\252\2203E|l!5'\213\201\226\005\336\303\232!\317\223\024\020\265\314E", 64 },
+ { "Py\305\207\302\350\255Ah\004\361:\243\340Gw\317e\204\246Ts\335=\240\346\326\024K%\003>\253{\327\375\3426\226_\315\323\340:,\321I\253", 48, "\210\226\334JU\226\325\266\244\370\345\005\026\340\004\024\034\261\250\302\343\237j\246\213\011\250\262\351(\221\017\263\346\252z\023\353[\234\217\022\263\270v@\042\021\360\264Qq\266\347\007\352\253Df\025*/\233(\030\261]\035\305\365;\216", 900, "\262)_\014[\213/Ped\016\343\251\006\032W\376\035\321\022f\030\360J\212\031\327\301:\342\020\324M\347\250;@\032\342As`\272\006\261\364i\333\230d\341y\361\356\001\347G\341\026C\374S\244\323", 64 },
+ { "\337\340\0338\311BN\317'\345\351\252$3\346\367\035a\346\204V\341\244P\333\351?\304\014\356V}7\274\235\011\272\134\337\023Z%RB\214\360\035\236", 48, "\342_\030\013\242\335\241\032", 1000, "\233\012\250\243\257`\267\003\230\221o\226{\3123\032L\3124\301\353\317Ky\215\320\342\013\035\236)f\025\214\315Uh\015\217\266\012\006W\021\003\210\327\263_\021}]=M\357\3613T\325\017\272m\235\341", 64 },
+ { "\337\245B\343\267\275\006*\002n,!?\206\365z)\211\020$I\326fA\217\335^0o\335f\351\004\364\177E\244\016\325%\036\342*Z='\332\303", 48, "\231\263\316\356f\366(\017.\272Af\207\263\015\237", 1000, "\366\331\243\333\005*o\210hM\134\333\007\216\207\134\001\346y\272kq\013\256\340x\260!Hb-\276^\343\243\223\015=\001\271\3758\021Y-nX|\361;|.g\352\265\341\367\236\042\324\201*\177r", 64 },
+ { "4J'\377v0\337*\246\270\014'\357>%\333\012\352\014b'e\277R!{\340\020\305\377\017\242\357\331-\042\324\042I\225\306\316i\242)\026\212\234", 48, "\370U\302\377X\352\001(\222\226\2464\316>\362\240[CF\230@\021o\215", 1000, "\003\013u\035\376g\025\240h=6\312\213\361\342_\366\363\230\237\217\246b}6\221\002~.u\315tI\200\263\370\270\200I\351\234+\266Q,}\022c\007\317\274\352&\257\317\234\276tRI\013s\221\215", 64 },
+ { "\254\355jOV%\361 (\374W\3124aq@;\207s\010\270rU\204\001N\023m\340\312p*Z\245Zt\333Kr\223\347\200a\355j\020K-", 48, "x>\324'\200\030\360\316\304\012AL|\0303!\237(\245\212P-C=S\375\037i\225\324\362\265", 1000, "3\026>\262\353\324\014S\233\316\233\302v\2516:e#\262W\273Jb}po\305MV\036\035C\355\371\317\304\306\326\026j-\345\241hW+\234B\375}f\273\244V\321;\220\343\272\376\365\214k\267", 64 },
+ { "\220\006\340z8\343\225\036\035\177:\232\026\227\343\356\342\3432\360\362R\213Y\225\217\330P<8A\277\307TOY1}\311\304\263\343\342V0\000\012\223", 48, "GG\036J\266\271\226\234\207\222\0130\014\300\244\263\233\267\215\331a\216Yxas\224\376\222\023i\317]\134\3245\306~\251/", 1000, "\363\205\315\022\320\352![\327\042\242\255v\343\252\252\243\324F\244DmR\207\347\340\215\020\224n\212\270\267{s\243d\203}\2477\342\033V\265<\270\306\020w'\330\027\301\023\006\026P\025\005\260\002\251\027", 64 },
+ { "\025@\366W\027\237i\355\013\010\332\335\263P\237\354Z\243\240\335\322\204\025\275\007\037n\304\266W\366+\321S\005\216\273[,\244v\026\372\021n\326J\323", 48, "\0339-$\266`\367\240\245\215\206\027\372x\252`\265\177.<\227\306\365\031\275\001\373\011*Nt\2434x\312\2426\344hK\336\275\240$l{\211O", 1000, "\030\363\332\336\2309\300\376\2105K\232]U\260\214I\315\372\310\005s\134\2602\367<\325\362\001uy\274\024\251\241\320\205\0200+\020\372\020\2508=\036l\371u\206\377W\306\233\317\011\034\007\335\252\262;", 64 },
+ { "\306z\302\361)\273\335\030\357Y\233 \371\307\225\013\357;(\372\177\311J\0321\030\215\300\334\016`Hp\000\031.\2527\270\223\320\243\022\255i\303/*", 48, "[\227\377\230I+\177\3633\223\205\206\332fx\307n\311?\330\333\017\314\311\260R\325\327YOA\323\267\227Z\271\255\217\363\310+'\377p\376t\011\032\024\370\265\036R\242\373\015", 1000, ",\020\2650\007=\026=\341\204a\301\0055I\266./[\231o\215T\212=UyCm\037$\370\020\360\003\335(=\205\355^\235\317n\035/\224\204EDe\277\373\240\312\267\007$\177Eo1\2219", 64 },
+ { ":b\302_(\317\016\2317B\264\327)\3143:\022\342\006\301+\232\213\330\306\230\374~\370\240\012kE\341s\335aW\251/\312+JW\027\263U\006", 48, "\3375\236Q1\357\250\336\333\006\021\304\360r\200C\021U5\340\010\245:\277\253D\271\370\334\030V\225\262\314\017r\3349\253\023p\324tn\325\244\207\271\017\030\260@cQ$\243\015U\004K\013U\354\033", 1000, "ic\217\365m#\204\032\232\010E\014\365\342\0233\020&\275\373\222\234\363\325\332\243\372\333\243D/B$\315\301\012\242b\274\2227=\267\332\014\2423ex(\366\000\246\373\345\022\210\275+)\220#\360\023", 64 },
+ { "D\315\013V\224+\374\237n\031\224'\255\013\232c\253\306\375>\262\021\031S\226\201\306k\376-w\361\307Z\310/+\217\354\275E\343\300v\012\244x\210", 48, "\020\347@\330\323d\010JOB\256\253\246u\216\337Xc\021x\267\205(\267\223\270\243O\207#\314\321?\373\013=#\364NbI1\376\353 y\200\2405J\271??\212\311\0110\343]-\367\367\2138%\310\037\307}:\331M", 1000, "\213.\303\017\302d\026\245\260\374\325\377\261\222z\035\373;\026\230BxS\007\270\331\223 \337\311O\003D\374\306\323 Q)1+\357N\200\264M]\376\207\335\007\025g\263`\004\363'Q\252\376d\277\373", 64 },
+ { "i\013]{\003\324\227Q)^>C#/Z\262\301\265\302H\322\351\253\270I\351\306jdR\201\307\204\023S\317+\362\215\225\275\217\312\245\335\263\002B", 48, "\347\245d\24117\217X", 2000, "\252\021\376z\035ajL[#\211I\212d}|;\340\330\020n\263\233\030\267\304\317\027\377\177\336\263\343\360\0338\274\357V&\232\302u\306Ni\245i7*\221\366\360\201;\363\003\3616V\250:\221\342", 64 },
+ { "!\264\005\243\327 -\361\350q\242\274\342\2628\340\356)<\202FZ\323w\270h\033\004\207!\005\362G\227\033t\377\376\243%\177\216\024\370\372h\330\246", 48, "\352\242o\245'\2623\223\200\042(Y\230L\320\226", 2000, "\355\002\347<.\274\221\344h\015\206\006(\237\036<\355\002\272X\346\332!\255\233\012\273F\243\202\252\042\004\204\344\021\253{&<z.\254hr\037\370\315\304\311\011\233\347\013\027<k\213/\373\261\247O\333", 64 },
+ { "l\274\276*e8\254J\371\240\245R\276\010\023\212M\353\255L\374\355p;Z\340e\364\024\310Q\355&\357\332C\327R\374\030*\030\365$\214\251\262\265", 48, "\013:\341#F[\241\251\0339q\334\014\322\373\242qr\222\010\253p\307*", 2000, "\306\301\274D\315\206\245#\224\343\326\363&K\334\011M\347\003\346\005\212M\011\276l\0209\340\203\336\264\223\272\353\261\362\342\377\002\261!D\252$>\375\217v\242\265h\341\224\011#\273\333\253\265\262\030\330E", 64 },
+ { "M\340\026\216z\244\214\273\267\211\2041%:\240t\355\224~\333\021\361;\271p8\025\220\007\377\356\333\213\206\037\014\320\253\034x\011oC\231\344\305ja", 48, "V\300$\325:\253Z\302E\030\214R4\216\327\232\315\2723\253\363\006\213\320\217\274\240\331\372\305s\227", 2000, "\300\357\340\235\202\210\212\275\015\205?\025\231\247\274\210H\300\263\317\2613\260\221h\271O\255?e\364\355\312\320\366\300'Y\321N2\317\253Ya\236)G\221Z\325\307g;\354k\014\271\354\365/\332b\374", 64 },
+ { "\375\212C\210Q\364\343)\3230\344U\330u\321J\021\357\233|%z\031\015\224\241\327m\017\327\222\002\233\261!*\363\227\262,\331\352\026\015X\204\276U", 48, "\353\2518\267\016r\004l\332\207\315\035\225Q\350.\006l\206\032'\016\333\214\320\363\351+\376w5\2366\373\012RQk\240\376", 2000, "[\320\320>\243{\326ur\261\213\262\233\332D(\015:f\345\350\311\373\331f\264=\035\377F\014\354\0119\003e\335\251\014Y\342h\2705e\257\012\253\330D\355\226\312\220\255\263\316\253\274IF\256\302\356", 64 },
+ { "\233L\025j\314\030PY\235@\036B\204\001\210\244\2427)\011\255m<\372 :\324\337Vp\024g\214\254\042\336\240j\367\322\003\367+\266(eK\206", 48, "\012b\352\220P\200x\257\204\203\205h.\013!\247\214\243x\255\203\215\375\200\042\217g\262Gn\275\245\222\217C\304\222\326\020\026\304\270}\031\202\310\003\001", 2000, "\215\256n\367\035\246VM[\303\337\002;\371u1j\3772\365\025Om\376\344\206\203\247Z-aS\367\315f\306j\272\034zt\223\313\317\267x\015K\304\321\024\340\214e\252\014\225\346\021\311\266y\377G", 64 },
+ { "PP\000\272\244\310c\353\031@\350dg\375\031\012\334\360l\235Z\306\031\201\024A\311\376\023C\261xB/\247Kx\210-5\042\234B\261U\025\235F", 48, "c\314\347\257\272\001\350w\215\300\317Z\254\214\231\266&\201v\245!a\032\305\253h\316\363NG\326r\271F\324\370\312'f\001~Lw\337\375\333\257\225\002l\315\022\134\305\246\300", 2000, "F\355\345\302=rO0\210\341-\350\007}5\237]\220\200\010\001\312N\215Y\252\374&\343a\011\012\030\017\220\324\377VB\224\315\307.\351\310}\372\321rl\277oX>\311T\007\321\312\377\036\211^\250", 64 },
+ { "\357Q\2640\367\013[\237\343\200\235(\262\251\2104[\000J\360\000\337\260\264\205/\273\016\033\005\373\036\336{~4{L_\017eNq\302\342\023\270\350", 48, "\035\320\035\370\304\326\354$\262\261\351a\327\357&\263gU\030y}\207^\200\217\270\316rL\335\304\012;e\250by\134H\042\325G\326\314\360\211\262\355\020\275m^\031;\350\020=\367$)\024\305#\042", 2000, "\275UOs\241\037c\027[@\215b\316\014\2558]G\202\346o\365\223\301+\226a\361\303\012\031m\304\244\371'\212\034-\036Q\264\374\317\241G\001f\211z\306O\225\360/B\366\0345\376\301\332\354\253", 64 },
+ { "\241\344U\007\300\201.$O@f\017\377\351>\243\003G\317\366\271\324\000\012B\235u\021\217\332\226\032N\2350\350\223\241g\303S\312\264\372>\012\257\213", 48, "\033L\372\351F\221i\236\310\273\3213\354\232_TrQ\266\310[\225J\034\220\354\236\205\360K\271\223\320\273\002\205@\313\335eI\267\232\246\236\3417\266\210\246c\244=\260\314\365]\367\312\246\010\202\372\015H\354\336\337)$f2", 2000, "\3719\006\261\234\375]Z\246B\227ji\320]\221@\263\354;\223\373B\252\216T\375\271\263\220\366\353\266\260\016\241y\261\217\2078\300\263 \204\331b\000\304kQ\233\363\250\030\306%\263\217]r\276\217\376", 64 },
+ { "\011\251\033\027W\032\0163\012\321\2718\307\354N\317\266+\246\213\235\254d\323\344\203\001T\2328\303\001\340\213\032\251\253\022\007H\223\334\250^\267e\357\214", 48, "\242\250(\362\371\203\3470", 3000, "\042jz{\220q\271\020]\035\344V\275\242\245H\266\275\212<\0156\354\231\375\252\263\362l\037\365\361\002\315\262m\246l\241c\270|\220\254\317v\042k-\255h\006\332\232\305l\243\316+\344\324U\200N", 64 },
+ { "\350\372\007\023\273Z\254\346\261\245&\340\367v\216\222\034\012u\277\003`\227\271Cu\015d\332D;\000Y\247{\273\313\250\003\024WVV8\262\325\314\237", 48, "\365\245\257\230\323\214\245\206\206c\036\016\026L0\313", 3000, "\230\372\346\242a\010!u\234Fd\303\013\324&\361L\036\033\344\240$\260\013A}\304\010b\323\321\177\031\326Ti\042q\312\276\223\242\254 \042\366\264\306o\274\247\310\367\005u\0322\2355|r\250\357\313", 64 },
+ { "p\327\375E\323.;\341\267\210\266\314\244\010\210n\353\204\352\260\221\357N_\042s\242\006\374\315a\013\276m\015\316\012H\257\307C|\215*\224c\235\341", 48, "\304\250-\345$'x\301i!= \226\320V\310\361z\213p^\225\033]", 3000, "\232\022\311\007\230\342#z\334\363\375\304\274\337\346\272i\321\333\304\301\320\341N\377\270u0!my\300\311\023\3241\255\277s\206-&\245t\245$\375^\347#Gg\332\015~%*\243fL\236x_\317", 64 },
+ { "\011\241]\247\315\307\354\015\314\272\351hk\3047U\354O\260n5\374V\023\366\353XmY\257\270+\276\364\370\333\273\213\257\216\2041\267\2101\320\221i", 48, "N\200\330\312\255\302\343\305\340N\326\361U\305c\242s(8\370d\276\306u\2260U\214_\215\367>", 3000, "\255\011'p\346%'UV\324\025/\215\356\265\262N(`w'\023\371\010\005C\272\370\331\325\222\307~\374L\016\042\027>\244\355{\001\227\246?\2450`\222\264d4Le\242o\323>\271\344\264\237\326", 64 },
+ { "\022B[x\327\005\226EU*\134\207%J9\322\366\315\277q\036\275;\027\277px\362\012y]/w\035;\014*\255i\356\364\034\272/ \217\234\346", 48, "\210\320K\265\034C\207t\341\010j\232\321\037\012\214\344\320\233v/z\244\206\375\325\303\327\324\325\350r\313\243\321\332<-\260\016", 3000, ")\377\352\240]\226\024\322\357[P\205j\005\234AS\2677\036 vu\034q5\250G\325:P9\311\021\344\177]\214\307\342\016\374p\234[\2658\370\351\267\237n\340\355EM%b\317efO\005\230", 64 },
+ { "\037\032\0421\375.\312\217\315%\333}%\301R\310To\202s\210\221\3768\216\022LG\216 \236\267\025\222\177\240=\014\013m\377-\271\253}\362> ", 48, "d\302P_\366e\211,\215\037\344^St-9a$\030\033A\235\004T~}5\344\260)*\205\031\312\215\305\221\330\3048IJ1\352\351\271d\225", 3000, "he\347\374\343\203\012\310T\325/\311of%\225\362s^\023\021\027dh\004\210\207\245ou\033\207\036\215:\006\015\3118{\272I\014{4\247\373\231g\306m\022\221\227)\211s\370\026|\000\261\035h", 64 },
+ { "\325\337\347\355~\224n\005\241p+e\350\351\034b;oP\302\025\342\211_\254\374t,#\241jSh]\366[\003\2036\310\2625\354\224+\276\207%", 48, "\015\371\213\314\256\360\017\223+\036\362\003S#\004\032\256\260\301\321\352A\357?NTh{Q\235\027\272\252I\216d\332\0320W\224\0204%\372!\265\313;\344\2342\365\3002\225", 3000, "\004\331\012\301k\262\314'@+*\314+\134!c\306\224\030\310\241\013=\017J\234\207Gr\304\212\220\032g\232\315Z~qg\217\336\2405se\260\355\177\302u\134\370\251l\213\342\325\207\207h\362 \017", 64 },
+ { "\2202=\274\020\024\341re\233w\032m3\255\235\237\234\253\224=V`\344\002\214C\036J\364B\261v\211L\236\246a2\365+a\212\300\375\3218\244", 48, "a\177\237Z\273m\345Q\025%2\012+\265\024\252\357\037f\271i%x6'\276\211*\373bKs\331\252E~\211>\010\215\020\013\256\322\205N\323\337$\251\342\017\306F\033=bu\331K\251\214\203/", 3000, "\035\344A\362\262 GZ\226\226b(\332\021>\336I\375\360\241\373QhAXD\247SM\300\205\362z\327\330s\017\351\257i\036\001\324\255IgD\3202\227\021\300\274\232\223\370\202i\033\311\260I\266\376", 64 },
+ { "\034\210\177n\354\347\241t\016\207;\333n\370w\247\036\226\214\1346\362\015[x^(\2728j\264\006\225D\240\217,\315\235=\246\250\012\3513~\335\355", 48, "F|\371\315\340\304\207u\251\277/\356\375\342\305\036\023\245\250g~\346\306X .\024\010\250:q\310\0231\307\020A\204P\351\004H$h\327W\015\024Z\027hi\226\323\232F\312\231\211\321\367\235\340\0053f\345s\031\315\237\004", 3000, "\342\210\306\337\352@7g\277\0363\037\232\2449W\324\213\345'6\016\325\354\247<\253[%\224\3461\3136\267D\373~o\255\344\215\211hX\313\200\217\240n&:Q]\257\343&\323\321J&\247~\256", 64 },
+ { "8\374\360\327\244\242\3112V\356\023\253\201\343J\333\370k\247\304~\341G\326E\021o\333\262\016-\233\134#E\3341bpM\001aD\246\230\027\003q", 48, "+\355\360\253\224iM\011", 4000, "\203\026BJ\336\023^h\371\300\255\212\342\245<v/\024\012\220xn\212\272'\301{y\020\202\013\370\300\216\267\020\345o\316\202}OR\374a\376\204\304\020\3279-Kba\371\033\247\013\254\261vq\024", 64 },
+ { "\357\023\232*\001\372\2751\230{\316\033\000\0006F\201\364\364u\217\326)S}+M\012\240\230\0128O\317\351\322\3323Y_\224\367\232\371\304\022l|", 48, "\336\226\337\332\234~\346\022X\371\312-\022\247\342\365", 4000, "%\217\250\325izsgW[\271K{\023\001\035y+M\033\221\374\302.\355\366\270'?\331\222\323\353k\207\213#\372\206\315M\004\235\134\332\225'\042\234\233E\2252\2318\016\277\134m\234~R\016\210", 64 },
+ { "\205\033\342\032\242\024\211-\314G}\227\251\023\004P[\277\240\311<\257X\337\032\276\302\3136P\000\265\201\233\3301[,\225\355\206\357S\320\016D.Z", 48, "\324\363IS\003\357[4\372k\037K\325\035\253ZE\233\301\212\213\003t;", 4000, "G\351\236\245\016\345\371\320\225\202;1u\201`\333\042\016\362x\355\317\177|\003\365\306\334\346K\312CFV\250\031\367*\367X\376\377\246\374Y:\260r\236BF\372\314H\345\0215Qx`\273\241\220\250", 64 },
+ { "\364\027mR\260\232XLw^\364\256\271\344'\301\223\216'e\021\220d\255\202\255\016+\210\206\244\0261\313h\034\371:\377lP\273\025Lx\247[\365", 48, "\317~\373\314\230\331\321\003'\012\363\274\343\370s\350\324\321\256\206i\323\315\273VE\310\032j\223\312\037", 4000, "i?\351\304\337\377&C\367bm\253^m\335\311~|Rj\244\225L\302\237j\311\330\2759\234\276\343\241FmO\024d\247\301 \314\011\303Gu\177J\311\3435\272\004oB\2203\325h\212\235\001\370", 64 },
+ { "\246\201\265\310\201(\371\206\011\310od\207'\360\134\226\366\025\316J\217\005]\357\240\020\331\242\042\366x\253t\343-\017\245\261\373Z\327;\023\205\233\360,", 48, "I$\373e\314\232)\033\256\376\322\324\234\213%cb\266\313\271\003\235\011-s\360\310\033\225\326\263\211-\2736\337\341V\307\302", 4000, "\012\003[\344\302\016B\006\036\370\247n\236\325\204{\312e\304\321\320\342\031\353\326\231\212\010\237\007B\334\245l0W/\327\342^\331\312\2517\261,\215\027\310\010\343\2025\3477\013\271)\345\177G\3765,", 64 },
+ { "\231]\314\344\213\022\042\205z\247\246{e5\362\243\330?\032<9\244\374\270yP\025\261L\014\223J\0228\270\352&\034\307\231\202\337\031)\356\201\032\012", 48, "z\200\004\375\370\325\264\223,\022\271\014d\354\005\320*}\206\375/\365\306\333&\032\255\337\276g!\007\034uIhI\311\247\312\244;\307\204\317\306y\277", 4000, "\327\177\031G\255\205\003\021\034\242\022\201X\002\200]@^\322mi\245\320p]L`\266\252\302\343j\134s`ku:\360\367DHnT\2411}\346(\022\036\315\266\362\347SKdh\213'\204 \016", 64 },
+ { "\223&t\237.\0160\037h<\244\272\326lA\017\006\301\367c\042>\242\212$\313\275\010\022q\317\023=M?\245\3243f\242^)V\011\001'\321v", 48, "R\134\353\337\330,\216\250\301*\3737\0046d$\362\331\335\366\362HZ\236\213p\247\214\241\134\225\304\016\200p\234B%\332\372D\377u6\226\017g\354\233\232\014\2565\2067+", 4000, "'\035y~\311L\376\346\274\277s\377c\376_lgN\042X\313\265\257\207\042.\264\262\016\023\006R\326\332<\000\013[\001\023I\224\134\007|\277\203,\322\353cF \331E\021\331l\217!\220\221$\365", 64 },
+ { "F=\037\315#\006\331\015D\027\243}\342\276\200\275\364Q\265,l\353\350\011\021\234U\026\333\260\372\274bM\244\323\344\001dt\347~\265PFr\301\252", 48, "\007\302\227'\205~H\223\003<\261\020D\200\337\311K\216\017y\360\204\337pxm\323Po-\275\304\207\032\314V\310\247X\254|s~\313\335@}i\371\226\254\374\304C\215|\327<\023S\2637\330:", 4000, "\012\324\351\266\333\245\343\256\371\303x\256\005v\321&\235(\235\235B\240z\352\335\266\204\344H\312\030\306\221,u\021\343 \365\005\204\023\330\001\202\271v\355\215\254\363j\254'\327X1\360\201\252o\034LU", 64 },
+ { "\023\234F\343\274\225qk\015\243\251\233\275\030\345\306\312\253\305\027a5@i\226\232J<x:*\325\334\230\306jb\273\315R\210\231\302\223\370\216\007i", 48, ")C\310h\001\024\202\337\375t2\222;9\225Ut\204G\017\012p}\134E[\376B\371\233+=Jqj\215\210L\367\334\366\241\374\017\036\032\201\324\003h\345\022!\370oW{7\212\377@\374\264\363\330-\013\2515f\242\327", 4000, "\200H\231]z\021\355\235\325%\2032\355\177\341\361\262?I\042\314\031w\306Z\203\342\265q\266\010\376\257V\377\364\326J\314\302\032\357F\207\371h#\276\266IU.z\242\036\2219\332\303\035\343\042\003@", 64 },
+ { "\243`q\363\217\265\014\242\232\340\356\222\246\3719\025,kK\360\210\012\226z\370,pV!\375?\225;q2\340\213Z\261b\203&\314,\177\264\260n", 48, "5J\251t2B\353\337", 5000, "\3733\350Za}\257\370\210\310u\363e\234\216F\255\222\205\230\212\367m\303\3756`\015\023v<\203\215\240\360)\275\370\274\213*%m0\350z\354_\3135\261]\265tV\134\255\377\357\314r\324\217\226", 64 },
+ { "!\351\373\2400\311\327\326\217fW[J\246\207\300Zb\352\320\366i\234Sk\275\372(\343!\261\227\273\305r9\372\000$\270}\304\020<_\231W\232", 48, "!Q\264\271\377\245NB\344d\203\015\206\306o\005", 5000, "\356\265\266\204\011\266\353\022\304\037xoV\320\350\3215\373\350\305f\020q\307X3\202\356)\346-\375\017n\265f\376(\015\262\366b\3703\024'\265A\242iA\363\134%\011r\344\261\012\340O\031f\232", 64 },
+ { "\241\315913\212\201\274\363\366\203rR\0043Z.[\204,{\321\210{M\212\000\310a@\2032\303F\256\251J\320;CQ\022}\225D\202\367\327", 48, "\016t\343\351\005\223\2432\311\257\2026H\004\2600/\324\353v\331\305\335\373", 5000, "\032\352i\346\351\034\370P\347\257\331Lvi\315h\226\026\352\026/w+\021\022\204\010?\201\010\343X4}\361X\372\275m&\312Z\330g\003\236mP\026\022E\356\031\300]@.Z\33603\352G\355", 64 },
+ { "\337)\013{\377\265{\015\2248\250\226\241\243\237\004\266\374\037\033>\343\242g|qW\267\315:\377kB\352h\357|\252+\362O\376\305!1\015\320\235", 48, "Kr\275\216o\247\366\204\374\356\325\271$P\244\030\0234\347\212\332\246\337\236I\273\134\210<\006\266\263", 5000, "\370\355\024\360\323t\303\313J\336\317\261\275\231\002\363\366\250\222]\007\367\367\240\245\271\177SZ\273\025\034\326qy\234H\007\261P1\345\241\327\313,\2257\004\345\255\036\2435\220\244L\024gF\326\210;\016", 64 },
+ { "\213c\353\311\230pI\234N\210\337s\351<{\011\206\001=\331\330^\024\341\004\202\351\210\330_\264\334E\233\376,\002=U}r\014\246\210\254\272`t", 48, "q\237\222\276+\321|\252\350S>\357%2U\365\355\314\213\272\2704\037\276\346j\342\352\226\225\366\273\030\353=\236\234M\230;", 5000, "\324\240\327/UF\025\207\247\256\031\207\333\020K\320\331\0038\221\217n)\032\310$\235\345\027\373\233\247$^\206[\274\211Z9\245\002\344\010\345\277K\300\201{\317\304\204e2\027&\301[\213}\020\270g", 64 },
+ { "\352l\231\276\004\222(\213@-\210\331gUw\021\350\216'\036V\204\271\373[6e,\225\246\3039\030d!,\222\226/Q#\351HUT\325\245B", 48, "z\356\375_\243'\235\334\0145\211\240\031&Y\256\211\205T\210\024K\262N\235\021?\211\014\305]\345\271\337\341\213p}=\306\027\203\220\303\273TQ\315", 5000, "K\037\010^\360\223\277'\320\341!N\015q\037- 2\363\277F\177\304n\221E\272}\364\231\333\340\003\030H\014\027\213I}87\214\2126\204\272\235\326\232\0349\371\312\311\334\262\341\257\356\034\034\233\042", 64 },
+ { "\323\332\347\327&\217\310\364'\3106\2004\223\013\330\317\201v\306\357{\246H\315\260c\274\334O\356\374\234\272\021\311a\251\233nN\252\326\226\343r\276G", 48, "\245\374\354p\243\215\017\253\253q?\323m\006\333\335\311\223IC\033z&\370\177\245\363\042\202*\363\0069\216\343\353eY;=C\013\274\022z\233\011]\374\352\250\366\317\267\3122", 5000, "s\376^^}\223\305-\342\007\035\301):\022)k\266\363\037kcv_}\310\345\223\324\361q\355G\300\365\242\266u\213\275\252\246\022Tha*\002\262&\330\005\366&\011\223\025\217\347\035\134\2571\365", 64 },
+ { "'\355\206\021\304B\220,\312,1\246/\371@>\275\032\265\313\014\223\366\343\310|\000\210\331<7\375.k\236>D\325@+ {\017\254\343\255\267\272", 48, "\365\271\004\226\353\033n\356\215F\020R\271\301r\254\335\357\251\256\211\021\015R \375L\372[\327&\266\207\350\304\230\226\246\276\333G\240M\234\316!\361]\313\017V\316\300XIM^\366\234\252\042.\227\007", 5000, "\030\024\256\214\270\375'\000z\200#\000\342\203\321\267I\351\212,\357\3654\231\213`\264[`K\347\224sC\321\303d\347S\245\336\241\330-:Bxe\370\002\264\265&\335=F{\362\025P\324\214OQ", 64 },
+ { "]0\343\335\304\302\210y\374P\037\267j\222\3041\375eq\216\007\265{\327k\025\205QV\274\002C\300\272@\262\316\304\302\260\024|\237\261U\274;\304", 48, "\346\021\323\003\216\205t\265\022\345\335W\352\347\346\324F6E\227\201;\012\262\372\304\255dPhVx\277}\011.\001\255\004\232\356\031\300\313\247Q\034`\225#\025qq\231\207nM\222\370\207mI\216k\227D{\304\342?\322\367", 5000, "\264\246\376Y\226\205\015\312\251\312=\370Y\213\241'\375\372\275\202\011Cg\307j\320\312\2272\377\302\240\232u-oE\244\222A\036\0045\322\221%#\212\036\014\263\302\366\326\333\376\035aT+C\244\302\335", 64 },
+ { "&\314\267\351\254\337\360\243|\246g\134p7\225-\222\352\376\325\207\333Q\306\217\230x\247]\134\375\037\273\360\373B\235\241\333\236U\007\315\005\250\345O\035", 48, "t\335\342\323\366\315\332\216", 6000, "\340\342\325\222\362\036\255\2425Ep\336H\004\027\315\256\270\275+Q\224\273\256\230\036\303\351\035\220\023\326#\270\342\265\2144!\320+U=\200C\326\367\012\343-^r\305(\255\311\2550b\014\013,C1", 64 },
+ { "r\226\021\010\302\035\242\206Iv\266,\365u\252\220\200\200\311N\366G\25216.Y5j\234\023W\006\326:\254\007\222n\314\037\334^\244@\020\2737", 48, "J\220\003R\307\360\321\302\352\204\255\226\271O\352\332", 6000, "\374\236\255\373E\223\332\214\005\205\320\376\311\314\214\366\326Q\314z\220\264\035\263\031\201q\360\007L\235\205\014\307\256\241\342\230\027?+\364\001\263\362\346\016\355\300P\033\201\365enL\223\010]`\262Lh\210", 64 },
+ { "L;\320\273\354\350\215;4\315j\215\244\020Ym8Y\220\042\235*\315\303\340\317\301g\231nx\226e\253\344\033\317'\241~%\252\241\225n\375\016\361", 48, "\017\357\243\317\226\303r\217\325\237t\264\2722\236:\221ov\211q\316\304'", 6000, "\337\331f_H\255\303\312P^^\373PR&\322\000\3774\337{\251u\2611,\232a-\021P(xPMnO\335\3768n\246\207\307\015w\367\326\210\022\303\013\242u\316\325\254\373\363\223\340\235\3568", 64 },
+ { "\026z\355g\254\372\266\217i\200|\033\341+\333\302w\014e\257a\261\235\233=\360\325\206\361\007\245;\004P6\201\307\272\3722i\333Y\365\2472\004\357", 48, "\203g \356\266\241!\202M\221\302\274\374\222\227\2624=\3567(\304T\030 \034[\042\313\321,\353", 6000, "^\326\234H\2378\261\330\300\316=_\220\206\013N\342\376\352m\316\311VD\021\237\273\213/\341\362\341>\246\200\037\375b\013D\242\226I\324\253w\266\036Q\015\366\032\030\337\033\042,\033\020\002\254\354\032\242", 64 },
+ { "\343\340C\210\270\266$\260:&\177c[$\253\002\002~\312\023_QP\314T\343c\260\322\315\325Z\001\311\313\260v6\367J<$\023\231\263B~\321", 48, "|\254\3234\277\240\314|\231\223\315%\307N\027i\3460\277\353\341\257oJ\350\007\313[b\264\373\320\352\013\323D\310w\374L", 6000, "\276?Q\275\236*\251p\201\300\011\221f\203b\316\220/SF\371TjN\241\354hM\244\263\336\360>)Q\3546\312\022\247k\042\207=\216I\230\324\242\202\242\372gw\034Gk\357Z6\277\370\336/", 64 },
+ { "R\361\275*\210\356\262[\2123\366\242\260\237^\2774\177V!\237\033\303\032\034\206\256\346\021\357u'\215M\241\026\305\350\3443\015&\003_n\247d\327", 48, "\311\353\274\027EKE\022\363\277+\202P\324{V\201?\220(\266,\344\216w\024l\263LYKx\214s$\3730\354M\002\371\234\015\201\305\035\012M", 6000, "\344,\004\363\340\373>(\311\007)\224J\235\227\177\373$m\252\205G\011\205\301\276\222j\245\360\316\262\214%\220\272Q\311\246\373\335\343\026\317%\245\223\330*\005\025\215\304)\370\031\363$<\030\217\347\302\035", 64 },
+ { "\342\000.k!\021\356\317\366\030e\177\037^\321B\177\272(3\267]\3522!\323\025\263B\365L\343\245\343\277BdJ\334G\255\230H\354'S\316\227", 48, "9\223\367\206\021\213\002\275\002\235\362\017\016\306\277J\314\357\220{O\1340\026\233\310\267g\276\322\205\023\347\343\357\024\361\263\354>\324\226\336\303\243Xr\3423\324\245\243\034}fL", 6000, "\277j\214\037\253\214\326\003\337pzF\020\200\307\210\002T\256\214\340\356\266Z\261\262\220D<V\266\316T\361\0337\273\337bj\215b\251\004B\310\037\216(\272\363PV;<\256\227S\350,\017\373\042\263", 64 },
+ { "\201\211{\341a\230\360U^G\377\226\217);\202\371\230a[+N\361\005\035\261\365\021\322\023H\256\346\261:\3271\342\367~m\350K\372\275P\255\305", 48, "\323\277v\210ie=\134!\314X\220\223\236\370\033\213.\2469\331W\262A\275\364\015MK\347\357\2207HDR.\300`yx\270\042\351m\235\356\255\033m+t\240\310z\225\263\327\177#\037\236\372\302", 6000, "<zB\316/\266k\375g)\2221\310\273\203\030Z\270\326&?\235d\260\326\001l^\267\2221\230\227,np\1345\220\330$\340\307\315;\373\252\372\250\342j\341\025\201=\236\300=c\034W\375{\243", 64 },
+ { "\362\024\344Z%\336\260\242&f\027\317\242g[Q\261\012\337\274\273Y\227\277\342\202\011\341\030\363M\347-\271R\312\315G\203\276\327\212 \035\361\016\324\363", 48, "\030@\373\337\177T\302\006\362\220\253\315\232\217\237H~\336\212\017\320\226w\014\314\232\252\315\351|\215\263\357\016\025?\200\241O\375q\362\3135O\366/\270\202v>\264\003\315\362$W\333\271D\305\223A\330[\302\240q\251\211\350\235", 6000, "W<\311\356\037\256j\327\035\206b\373\235\343\260\363$\224\3011U\214\334\247\035\241\037\326V\235\270^\346\375\375\251\042w \222\302\0153\202\017>Fi`\345\210Zt\014+\305u\204`\004\326hI\306", 64 },
+ { "\207\335.W\320\222^,#\374\342\226\224\016\236\210Xx*5R\005\0368G%\357?|\305\355@L\032\372|j\020\376\251\275\336\351\230%{Q\222", 48, "\204\232\373f\211Nj\361", 7000, "\312\237\260\312r\315\032\210jz\302I\276\243\250\265/\027u\037?4(i\216\025G\352\033\272\362D8\230\304\304D\007C\223\335v+wm\371>\244\313\275\223\226\255O\273\265\322\302\026\266\210\033,\032", 64 },
+ { "\351\315\015\007s\036N?;K\276\313\305\027\3443\241`\3168\353\264\273\021N\000x5m\020%O\372\011d\210MeY\303\316<8\033\026\027\330\274", 48, "L\250\331\250\232\324\035\255\344\227\313\334\367T\221\343", 7000, "\345O\261\350\203\357\344\350bs\264=\3658\343\337\373H\266BtY\252\352a?1\330\244\323\366w\357`\327\316\312\253\327Z\277\032\330!)Y\277\270\214Hh\372\277\371t\017\365j\246\204Cn\257\277", 64 },
+ { "\015\316\026\222\224Vf0\216\243\304\215\330\317\303\245\261~\333zMF\362\033q\266\2464\351#\206\247\244\002d\315H\356IV\200\226\352\023\230Lr\220", 48, "\201\320\347\200BI\226\316\323f^=n}\360o\302\253\021\364_\017#\230", 7000, "\263\315,\274\011\320\250\250\204a%qyyc\203\305\334;\271;\226\307\342\337\331\311\252\211\374O\211\222\3216\275\355\335Ct-\357nS:W\367/o\330\263\026\033\026\220CmA\011gr@\025\270", 64 },
+ { "XxQ`/\267\322\333\311\314\2604r\247.,N%,<\343{\260]\203\213\224\341e\267\016&\300%\223\372\306\356\344\036m5\376\277_\307\215\365", 48, "a\322\236\225\2352\213D\371F;T\265`6*\242\252\354\005\254\235\222x[\026\373{\347\364S\311", 7000, "\037\000\251\334x\275\265\323,\026\256\3703\206\375:x!\373\337\270\306\005\202\315\354\015\3161B,\235m\366\267\037\374h\273jgM\244+@\366H\217=\223E*\203 \372p\214B`\372\223\250\355(", 64 },
+ { "\320r\000A\354\217\332}\235\330O\006~\362\321\212\242!\204\377\275y\337[Mv\355f\370\334\365\207\260\247\262a\355\353\306\323\207\305:\356\023aE\227", 48, "\006\374\337\262\336\367\244\022\313\134\371\266\263\003\206\301\262\300\260\217!N\274\352Z\031\267\376\264\305\024]\027XK\006J\244o<", 7000, "\275\245\364\263\007\213\236a\353{\250\227\231\027\263\320\254\216\376Z\275\260\311\251\001O-\266\364h;\001\236oZ\035\211rI X\234?i;\013\330\267\254\223\310\001\367\327\216lU\371\212\277=x\334t", 64 },
+ { "4\253\333\315\017d3=\373D4DP\354\305eE\237uAh\337\2405\362\3017E\345\377\225\222\345\321kD\240\365\222\334IZr\011\277a\307T", 48, "\017\205\042\033O\271\030)}c*\006\037j~\34208\276\254\373\233V\266Y\317\2051\215\277i\003U\375>\320\234\334$`\271\301\251Ir=]\025", 7000, ".\331\203H\033\314\212\327R\264g\305d\305\252IS\300\202\265\017[\263cj\353\222\274\363\302\245\326q\270LV\257\262\177t4*\177\213\365\347,Xx\027]\325w\210\362u\205\334\305`_\343W\251", 64 },
+ { "\302\357\007k\362\234\310_\213K6\226\006\311\313\342\227\347\226y\210\253\273\302i)R\305\001\365o \335\206\030<\352\311\030hL\361\320\342\2430p\201", 48, "\216v\267\216\340\203`\177\232F>^~\357\222'\013#\032?*?\025(\206I\307R\227N\235\214r\266\202@\242\202\215C\356\246'\014\005-{I\331\367\240Z\224\227\247\360", 7000, "#o&\232\242\232\323\234\371`y\242\302\373\002\276\300\013\255\272\225\242\261\223\354Vvt?\372\034\265v\357\375\206`]\236\376jl\202H\207\272\250E\304\005q\357\220\300\335A\266\316\314(\377\000$\204", 64 },
+ { "\221\031\331\246\220\371y\353t\234h}\010\235\134\014\244k\273\274@\224|&\273R\254\223\012>D\333\025\273\262\012\222\370\255\201s\234\262>\311\345y$", 48, "\252Q&V\341\266\330\215E\337c\302yt\227\314\264\036c8\356\240\026-\250\0220\271\203\311\326\260\320\262\007\375q\242\200\323\345\042|\304\367I\231\202\333'$S\245\252G,X\343QRK\3553X", 7000, "\027\3340\256v\217\244\245}\245B\350s\270pF+\037t\340\3040P\325\224\2044\006,b'T\235\013:\367\273\336\255\227\230Ao\243\355\251^\324\370\325\307J\202\275\004\371\256\321\260\305hIF\023", 64 },
+ { "\325S\032^\015G\353\362^AO\356\302\266\246\251\226/6\276\271\210T\320t\252\362\034\314\366z(q\005\031\033\342A;M\235q(\005\265_\332%", 48, ".\022\232\355\300\311\020\276\304\300\331\006\273\224\244\305Bx\306\317\245%2\331\266\201zR\376oei~\325\255\025\366\320\356(\315\216\024\345\001\021O\2571G\222\244t\012\230]\327%n\353\253\3527l f\356G\364\345\213\315", 7000, "\324!\305T\363L\134\004I]\010Z\335\242p\250\335x\240J\277U\270\231\027c =\335UU\365\266g4\003\360\025*xa\304\264\017-\311\204\236\355\341\273\212\252\320%G\356\240\344\034T\267?\217", 64 },
+ { "\326+\305]3\260\0238\247\332\3650\203\234\222\232\272b\202\012\022-\363Z\022\313\261/\375\242\323\227\327\203\033R\322\221I\232\274D\002\305\207*\320\207", 48, "\322K\205\015w\314\267\314", 8000, "89A2\310\212\272\365\307\347\023%\263+\263\307~8\240\335\350\374\013\000\304\370m\244\346\311yH\273\020\002\237\366\362\346$\021\027\320\277\327\361\353^Y\024~\1340\037\203\002\035wS\273R\244\274a", 64 },
+ { "\356~cy\304\371+X^9\335\304\257\0078\262\274\237\212\326RZ\213enC\237\225$X,\265\177\226\2052\275\025QU\002\203\204'8E\230\277", 48, "\362\356$\042\252\310\332\267\355\011\303\374\341\317\023\351", 8000, "\022\336\207\312\365\207\005\232<\303THN\303x\275\201\251\320EQ\363\337\253\242H\227+\346\003W\235\321,\3438\227\205\244_\376\252k|\004\373\370\271ZY\341\332t%\235\304;.\377\343\352\310\230\334", 64 },
+ { "\203\020KLv\231\231l\311\355\270[\202H\231\350(N\333=\223\020$\271\350V\005\361!\254L~^\011\003>\223\313\216\314\334\363G~Nw\001\315", 48, "\200\016!Cw\267\245}\322\354U_*\331\015)\375}\273\2213\373\314\005", 8000, "\307\340\243\202o\300\337\213+\0209\266\311\252\251\036\371\2202T\000\037\220t\224\255gD\362a\327\314fU\246\237s\006\237\376\246'm\0250\370\206\373\234\247je\347J\256\036\004\003fB\247O\011~", 64 },
+ { "\002\342c*n\2159\201\361\007\267\221\245k5/!R\224\257\201Z\352\271\256!gJ\3163\323\204),L\204\256\257L\233\221\310\326\357\016\334B@", 48, "\255\352\265\017\375\006\365\360\372M\376\355@By#\313\251\177Q\277.\235\015\230\261\015\002h\220[\315", 8000, "\314\310\364C[\214\206\177\205\313l\373\357\226N\226O\231\005\221\251'6\257\036\234\014w0Y?\330\017\024\030\245@\015\232\373\323\361\2165\360\273n\311\203\207\313t\012\360?n\012\374\370\2178y\250\340", 64 },
+ { "F\220,\342\271\325\224T\276\021\206\271\276\014\204\027*\002T\030\003%\251\226N2\007\274\235dKT\341\320B;\220\042\021\220\240\313\025@\213\361|B", 48, "\2715\352\303y\267\260F3\042o\376\220\241\370\322+x>\006}\011\237\26568B\355.\333\217\220]\352\205\314Z\033\024\230", 8000, "\325\200\311\010\255\014\015\012\0312\227\023t0\010\257\036\321\035Ub\356\313\263rW\246\023\370\021\134d9\251\225\366\253\324\035i\003\312u=9[|F\375\343i\251\274m\2166\2128\355Kr\021\036e", 64 },
+ { "\3470~>\252\201\367\3254\2660`|\260\265\263\306!\004t\241Z\312[\234\240\316\272\001fLF^,k\034|(\360\201W\351=`a\2559+", 48, "\203>\324\207\242&;\023\272Y\300+\357\321r~\302i\341\336\335Y\271Be\313\260\345#\315\361\373C\207M^\013\276h\231{{B\255\376}\237}", 8000, "\023\012\014{u\274J\027\303\022\322HV!\015\321\376\204\332\301\306\001sN\366]\215\276\237\206\257\314\245\004:\225\260\221\327^0J\351+|\334\376\323\002\355\302\310\000\012\361\252\307\334%\303\340~h\336", 64 },
+ { "\322\200B\315\306\217\003;o\030\200\3471%$\366\005,\332wno\352\224f\360E6\215\240\374o\212\302\352\366\321\310};R\334r\265\307\356\0129", 48, "\301H\225\260?'\355,T\004\020\030a\360\221\340\207\021o\014\027\246I\262\351\226\342Y8\017\3427[\372M\316e\034\343]\335}\334s\343\006\322\255\345k>$\317\243j+", 8000, "\331n\235\013o8\255r\261\356\317\262\206\337\206BU\272\272\042\207\372}H\252\332\331\337\033\001\336\372\364\023q\360\313\020\250k\315\365.\134\201\316,\2169\3411i\357^I\204\325\374Qk7\315\205Z", 64 },
+ { "\240\276\020c\257\224(\207\033\214\361\277K5S\241\177xl\335\266\325\221[\005\220\031\376\264\277Ip]\251t\220\240a/\260,\277n\365\254=OO", 48, "\231\006\313\201\376O(1j,HT\376\334\306\242>b\356\347\360\350\353\326\033\206\252\277\021\216P\037iN\014\341\371i`ZR4\016<\215\245\013\020\255@\002\267\035D\352k{&\2365\273[`C", 8000, "\264\261\232\201\346\355\306\350\271{^\217\276\005\237S\202\320\020\322\205\334Q\345z\210\357P\217y\216\2704\015\357'\254\021\234Jo\355\322\037\0423\363\027o\227\345\316\3609\372\310$\307\236\370\322K\213\252", 64 },
+ { "{\262\366\317\260L\3010\312\213I\2542\251:\314\317^B\355\011|[o'\316\312\300?\221\021\331H[\253\2024,2\362\332\021\374g\252\221\375\213", 48, "r\254\355+\274N\204\306\266\333\332\003\272Jrn\324\352r,\322\376\253\363\031K\201\312\004\277\356V\212\231R+\306\230\341\242P\306U\014\357\227\220\306B\302\230S\211\030H\240\311\214\247x\340\033\032\302\134J~\212)}\315\324", 8000, "\336?\2265r#\010\232\375\031y\371Z\024&\357\366 \263\024i\234\316\233]\243\222\353T\224\002\005\015`L\227]V\361\256\012\203u\004\002\312KT\003]\241hy\001\306X<\211H\332>\357\210`", 64 },
+ { "\327L!\213\262\001-_\342\022\310\370-\277\240\035m\200\025\343Ae\202\257N\004\355\017\307\362^\035\325\356\277\013\271\001\320\266nda3\313\346\336\337", 48, "\303\376\271%f\011Q\244", 9000, "2t\312O\303\020s+ \231\346^\312\273\000\330N\206\362\226\254\206\362\315)f\357\316\034\002\005 \250\306\354=\336\247o\313\312a7`_\242c\356o>l^\034\267\032/d\302Ru|X\331\272", 64 },
+ { "N\326Q\311\276\010\312\331\314\341\234\342@{YA\252\3404\314\031\2613P\020\011\330\310 \224\177\030\032\247\262w\321\2302\370D\006\235\027\360j\034\042", 48, "\365\031\352lZZ\210\223\223\202\302\3472'\215e", 9000, "Q\271\224\222e\316A\255Z\177l\026\030NT\242\263\3773\025]\376l, Jk\134\221\025\316!yj\270R\242q>\260\277A\213Yr4\211\354\345qH\200\134\304j\026\030\275vG\042`\205\234", 64 },
+ { "\201\345\257gf\2623\322\007\016\3369\245$\351\216z\224\311\217;s\202/\002J\364mr\312\262\315\270ytO\001+$\273\205Y\327\212r\322\177\230", 48, "d\210\377\354v3\326\227\321\200\315\340K\022\035\2222<)\035\336\325,\376", 9000, "\013}\246\202pG\243\307! \277\177z8\307~\303G\223L\317\225\312y}\320;\033\206\360\243\227\035\374\253b\252\035\235c9\200\347@\262\231\313\330\301v4\222n\273\221M\016\025\326\331(\243B\316", 64 },
+ { "\205\031N\267\2314.\365h\210\246\265\323(\221\367f\211\300&\303D\036\251\024\331g\354\344>\001/BJ \246\226\003C\260hj!\210\343&r1", 48, "2\315\225\207\307C\267e\233\013\261r\234Sz\255 #\316_\267\355\345#\361d\376X\304S\330r", 9000, "J\222\216Z\305\020u%\261\3464\220[\353\254\271t\377\2558\313\312\262\302\341\001r\037\335\261E\210P\010\365Xe\2034B\377\254\003\256$\307s\213\000\033\371\377?0\255\031\225GGO\274\370W;", 64 },
+ { "\346 \313`\013\340\264\251M\026\365\376\001\371\362]\244\256\335\203d\355 H\355Vt\305\235\034b5.d\210\211H\317\000\001O\370\177\317k\355\277\015", 48, "8Y\351\207Y\302\312\327\343C\206\335\011)'G\206\272\366\206\037\320-l\275.$[D<\312\330\032\232\236\037\265\361\243U", 9000, "!\217x\241we\322\037-\177\375B\222Na\017\376\224{z\250\254\235N\263\355\351\215\223D\006J\366\262\342\277v\262\335\334\2752\336O\360\250\256\324\300+\257sA\257\205x\375w\271\016\263R3\006", 64 },
+ { "\214\275\361}?2\257\011a\256\243\362\270\356i\270\260-|c\227\311\0175\226C\355O\006\201R4\027a@\004tpx\271\256\371FR\016C\012d", 48, "\264OAA\274\375l\304\035\255\222\230\340KX\003\237\270\240s\134\035\213\360`#\253\256\222\346c\234!\311?\245\036\2402\225\267\033\214\002\251\266\325\363", 9000, "\221\257\256\354\355@5\025\375\014\230\203\257\005z\235\355<\316\312\201\306\265\264\376\233\001\315a\340\033B\260u3>\350~\360q\017\233\367\2368+\210+W*B8ZA\225i\313\364\3755\260,\355\021", 64 },
+ { "\024+ob\033o\000\317G\244\001\341j$\271i\270\323\214\227\313\362\022\236\230\333\002I\374\206(o\355\352\223M\367\360\015\134rl\207\275\345$%\261", 48, "\014\212\304\314\363\206\374\206\042f\035Po\265S7\011\337U\275Y\3268\271!y\255\222w`\321-(\245\314\275\326I\204\004l\251\312\3308r5mWq\302y\031\204Zd", 9000, "\007\242\343\025\014\305\013\374\007u\334'\006@\026\301D\330\241\252;00g\200\326\300S\335\327\212h\316izd;\272R\225\037\003\032\272\362\201\351\012\370&\356]m\024-\260\270\250\205\1340\374\277\311", 64 },
+ { "JO\370\260\3003\320v\033\332\353\250\340\303H\305\276\010\002|\236DK\246\312B\2345\0427~\244\341\021-\226\260\022\134\225\134\134\342X\216\320>f", 48, "o{\276\324v\343\323_\217\010\307,\371\337e\310\260Kmn\277\351\034\022\260M\226I\275\241)1uFY\372N\375\037T\234\340P\355\276\313+]p\035@\0068\301\215\207\020Xry\302\267\265\212", 9000, "N\005L\3772Y\243bo4^\207\034@Nvf\310\340\342\306\254\3429X\207uVK;\210\346\206.\042\271\013\337M\341d\352\234\335\020\371\316\034\271\134\350-\343\263\234\014#\301/\215'\017Vi", 64 },
+ { "\312\367\010X(\322\245\2355IP{\034\252p\332\316\012\224\311\245\366\216\355\261@\042C\341\134\273\021|\351\223\221\375\336\255t\011\022\372\216\370\333pN", 48, "h\201\327\177\322\026\243\273\335\206c\260\337\2178J\247\022X&\314\347\356\2576Z\371\012\036L{/\324\205C\227\352A\017\346(\001p\322\354|\306i93\274\360\214\302M\331\134y\375\021$\376]\021\333\272R\335\261=?U", 9000, "D\220\257\020#\326,%A^M)\203C\031\323\315\212*\366upkT\346(\276\340\033\351O\236T$D\030\345f\340d\370\000\325\263\372\336\246:q2\253\216\236\316\222|\273\215c\013\035\341\346\326", 64 },
+ { "\012\0021\2554K\025.\250L\177\034\021\271+\217\314\267\312\217/\201jW\304\134,w\371\030\026~b\345\252\032*l\307A\344tG\201\0277\272B2\274\342[\213\353\232\225", 56, "|\330\307\300\344TdF", 100, "f\325\322\343\327\376\252L'bX\232\025\242\260ML\370L\351\012\200\235s\234\346\035\254\214\346}H\307_\350_)Z\252dw\017a\003\323\320\2130I|r\263\260\252\250 *\314\275\316W\213\365T", 64 },
+ { "\265\277&3\336\000p\271\375\024\027\035\316\001\254\254\335?\211A\257\330\023z\275P\334^M\355I\361a\372n\220\236\333\226w#_\241\242\004\355\220\377\317\0139!]l\232\354", 56, "\303\322\337A\240\361\204\250#\325**!30\366", 100, "\261F\372\232\217\353R_7M\315\363WC\340/\014\244\373\0326\330\231}\007Yn\031\274Bp\310\200,\253\020Gq\225\237\366\265\032w\213&\343\345\320HG\004\237\312\256\225({\042\352\220\364=\232", 64 },
+ { "ZM\361\367F\257\262^\035/\022\245j\330\256\256\347\246\267\320Tb\231-\224\322\223\264qk\035\0316\326\236\010L\300w\326\031\376\304\246\022\335\200SQ\234\031\023\320\271\030R", 56, "\263\213\022g_7\373\002\213\266\332\344m\215X\035]K\200g\014I\251\370", 100, "\260\220\236&>k\310I\270&\302\250\246%\326x\371\213F#\265\251\221\023l\347\253`\253W1\240\300{;\200\264\213\251\003\240O=\214\240\001\214\227%\271P\275 ;}\216\031{3\237\203c\212e", 64 },
+ { "*\255\343C\246<&\316 i\270\001\022~\347X \214,\255R\322\223\023\317rk\231{10\012\002a\235\263&f\344\364\326\017ZN\325\377\330\323!f\314\236\372\246\371[", 56, "\254_I-'\322\233$\007\235\371\306\023\012Q\300\036\341L\233N\324\325p\317L+\013v\311\316\354", 100, ":\233\376\345\015\356\005GU\306t\212\010\042\276\274\263\205\221\353\365\320\315\330Ew\340\020\032\003\367\345\366\016\276\134B\0347%\134\2110 r\272\031\277\024\257\321\222?[T\270\251&\200\013!\206\036F", 64 },
+ { "\2373\301v\271\235\276\027\036\310\364\032\002\3031\342\220\363\343\355\315\227\032\177\264\224A\240\010\310\332m\376F\330\212\003\253\264\022'\2329\350\372\007\367\237\363\031c\312\357\015\036\247", 56, "\306\020\351%\320Q\236\371\241\204D\020\273\264\325\322\347\276\202k\347\225\312\270'\364\244\267\253\324\256)\361\362\363T\020#\177I", 100, "\307\203\024\042X\2128R\350\013i`\227\217kE@i\275/\371\365\352F\3261\013\203\253\226\024I\214\005\340\266\0024;\021.\004\325\353\333\340\353,\253V\204w!z\360gV{(\200\200\243\203\203", 64 },
+ { "x\012\013\003e\212\033\346\266y:=\322\266\034\021%h\337\263\305%\212\214\345\2142&\347\316o\331\277\344\134`\245[\256\222\134\241\323\006H0\333R\345C\035X\036\011w\343", 56, "\324+\317\332\345%*uC*\367\331\231\375:\012\344\322L8\036H2=\370\337\011\254\313<\231\341Y\037_\203bY\235\316\245D\261\275\260\277v\321", 100, "w\371\345\322vt\024H\034uV\345\273\201Y\263\336\317\352m\326\023\373\210\205\270\270\365:e:\232\341\034\340\315\362\300Z\252\227o\244\260\005s\014\373\344V\010@S\0163s\304\373)\026\215S\030u", 64 },
+ { "/\357:\266A\017\267\203\302\030\374\011\337yPe\211\002\325\371PI\036[s&\315\007\363\326\227\224h\202\345\241p\236\263\207R\233\223\255\237\201O\213\300\230h\027\372\270\342\252", 56, "\021\037\205\367\202\371=\373\311$\3276\204w8{\322\326`\222N>)\236\250\236K\333\005\134\243\255 \214\375V\204\343\333\201\031pG\236\212+A\321{\222\231\350t_\262\243", 100, "_1\033g\364\204\217\016J\201\3137(\240K\200a\241d\3443\377\266\017\3118\3247\255\343=\251\042\343>sFI(\237\022\034]\007\002\301B\376\020XE+w\344lm\273j]\236\234+q\007", 64 },
+ { "\200\315o\042J-\2216|\324{\301\366\352(\313\215\277\202\134&\354^Z~\224\360\030E\341\262\274>\234x\005\201\350j?\363\275\3310\334\303h\247\214d\251f\322\270_\261", 56, "\003Z\010\015'\3659\371\213a\032\026wr\275]\201oD;'z\336H\360\362\262&\312\372%L:\271\254U\252\177l\015jR\256Z\202\341\342^\344:\321\016\310\225W\246\350\031\260Or\247>\312", 100, "g\260\207\013\373\201\012\363\253\001Q\004\347Brs\317\333\350$\016\325\000$5\347\373\235\020\212Xaz\2310\362\310\304|\342\302qdi\272\020j\370R+ \275\357\327l\233\357p\214\020\025n\260\234", 64 },
+ { "\027\214X%t|\276uD\2646Sa\0229U\342\263\204\215\017\265\220\262nl&S\323\335\272c\313\016E\343\327{q\010u\215\271\336\350\236{\010\333`t\321\303\330\243H", 56, "\030F\023\257\276\014@tb9U;z\250\262Q\352\317\240\320\222L\177H\371\213?\334@\277$\3062\006\347)q\340\356OjJ\177q\250]C@>Bp\275f\372\203g\200N*\234.h\256\201\251\312\260\363\211\334\207\332", 100, "?L\013\234\225\011\245S\276\333\202\356\012\347X\343\223\312\310lx\307\243L!t}l\322\360KFqu\263Wy\362!\323\373}\316\276\374\350\202\361\036 \336\237x{N3\261\373\037\303\013;\254\302", 64 },
+ { "\201\024\017\256\016\004\012\266\346{\253~\241+K\242\032\340T\200\207:G\234+\336\266\321\374\240\371\313\213\322{\2001a\314vkv\253\270\347\306\241\377u\233o\340r{u\215", 56, "\351\256\262\270j\374*\375", 200, "\225\256W=\253\033\267\207\010~\367W0\375\035C/g9|\3500\237\011}\277\313\023\032\003\017~0\032w\037\327z\257\221\371\272\000\030V\257Z\203p\324\202Bc\262\302\360\266A\027\255\347\016x\277", 64 },
+ { "\361\362)H\255\223\262f!\237q}\211a\313\340\013\273\255\244\177\005&\226\216{$5\206z'&\232\273/\370G\216\201\034\205\307\236\330\2132\033\210\257\235\001j\026]^\365", 56, "=\364\001\274f\263e\006b(p\2263\035\205\231", 200, "1\337w.o\2238@\015\221\025g\241\342x\236\342\034jT!\003\023\003\3037\255lvx\252R\377bt\037\010-\263\316\326`rBb\353\202W\270\365\234\250\251\253\013\303\357\007\352Uz)\037~", 64 },
+ { "\257\277\134)\332\355d1d2+\353\373\030\375\367\033\252\200\240K`9I5\357-\216\020'p\315c#\221\222\2375\036}T?Q\357?\370\234\226d\015\014\024\362.\230\225", 56, ">\316\225q:\330\227\235\001[\025\200\360Q\020W\213\035\005d\232\310\003\362", 200, "*\337\342 5\215V\033\277\266\235S\275G\3654\274g\323\377\200\327\221\3660\342\337\242\365\025\322\264q\257\354\307z\242\355\276GS$NM\373\212|J\211E\027\234k\215F\241\214\351j\323\227\243R", 64 },
+ { ":\334\025\003\012\272\216\010\265\303dz\332\270\244\370\337\337\362]\320\001\374\247\375\211\236K\236\233/\016R\356\242\030k\224\005o\322+_\241\013\337D\345\325\246\277S\245\335\262\370", 56, "$Z\306*\301\024\365\341\272\252\241\256\004f\260\313~`\253\227\307\015.@td\027\352\023\303\037\245", 200, "\205|\242\366\361\322\316\360\003\330?\212\227d\011\367\306H\364]\321\015\013\233Cy\236\301E Y&\336\013\265hE~_d\207V\367\270\362\211\377\351\012\020\037H\323\321\356\272 \345TK%P\317U", 64 },
+ { "\231\277x%\233\335%\020O\317\273|\332\300\025Z\375`\254Y\001\374F\277\347R9Jp\257\355\305\314+\222mhE\134\364\261Y\220\253T\016\341\230\220\2049\350T.36", 56, "\273\270\005p7\266fex%\352{\216\324\317\230\347\201-p%\273\305h\030\324\317\005\012XQ\252\204\004b~%\300\350\322", 200, "\225\266#V\362>\224s\003\004\343\234\013\364\250\2561`dF\270+\257\226\314\007\014X\302{;$\376\376b\034\300\350J4U\207w`\200y\011V<6U\217\274\320\134\036\231.\263$\003@6E", 64 },
+ { "\335h\233\222\333\376\221l\310\277Qr\311\245\030\026\231~\264\376\374\010\010\352l\367\014\346\2428\204\207r\034\236\367|\202\311\014\200]\042\350\230\014\042\227\315\304\006d\037\354\022\236", 56, "\327q\034X\210F\233\134\316\225\3179\216\311\335\262\213\011+\300\242*\324]~n\300\330\301z\345\267\350M\012\036\267\222\221u\317]*\271\271\350`\027", 200, "X\025\236k\211#\272\010w\252OD(\042\234\244\252b\004\014\017G\377\206\201\367\311\346\225\347\271\013\253\022\224\375:\015M\030a\337+\371\301Y\342][\245V\202F\373\230\325\246\301\030\307\016\316\367z", 64 },
+ { "\321h\237?\226|\246\300\002<w\226F(\252\261\020<{\230w\235*7,C\331\272\007\375Tl\010\360\240C\267\007u\037\220\322\356\271\333+\352;\336_\277|\030&\035K", 56, "\304\304t\314\323)\354-\225\204\037\225\365\001\227]\242\264\3710}G\042\032r>\2461\036r\302\201\347G!\345\243\352\230\212N\212\301io\002\265\224\234r\234)\014$\371_", 200, "\265\256\260C\322\362\025UZ\373\310\042#\355V\2627\214vv\031;\305\177\026\337\033\004\255\344\201\006J\232a\227\215\134\263M+\277vvF\263B\244\366\224\321\200\336\360\273\332\030\273a\177\343WQs", 64 },
+ { "\237\177\212\014\014k\321\033U\252tY\371\002#\332.\271\206._\341A5L\365\305\026\303\231k\362\200\314)\236\250\351\212b\3224P\225\361\332\247\275F\321\310]\330\213\341\210", 56, "(\233\022(\235Z\311\220\371\003\035\315$T\361\220s\035$X\370\271\260.\013s\012/k\001s\246\001\216\221\346+\324\262\363\030\331\230#\370\246Z\004\036z\310\034\301\026$W$\322\356\270\315\241Xt", 200, "BP\254{+\275\311\2262%\303\320\352\313\334\326\347\013\025\272.\332`*\242\302\315ABZ\372\211}\352fy\377\276?tQ\002\320s\033d\343\351\036t\033E\326\336\315\335v\310\037\203}}\017\223", 64 },
+ { "T\375L\020\260q\210a\271\242\270\231'\317f\366\331\017\022\004\253\275~]S\012\253\010\022\265\134\035\332\351a\371\362d\376\021\006\300\205\010\264$\027V\322;[}2\023\346\364", 56, "~\323Y\311\222IiD\042\377EK3\021\217\306\235\250U/\033\236B\236j\032\134\302\006\304\006pm\2145cN\234aR\017$\356-wwH$^\001\015\317n\014x~;~\312\335\256\227u\237\342\312\217\323\335\250\023\331", 200, "\002s\037\213\325\205)\134>\016\217\302\001+\004B}\232\304\333\211\213\312\037?\020\374\302f\256\204aY\275j\277\020^\005\256F)G\360\212\240v\012\327\257+X\324~M\245\320d\340\360y\357\312?", 64 },
+ { "r]\363\000hr\356vY\302\354\336\273d\322\265\201X\323\205\361\277\230\224\273b\320\354\336\241ff\322\356\376\256S\025\375\012d\376J\327\360\263\250\363\265\031\217|\005\217\177\330", 56, "\035\205\201\0145\016\364\255", 300, "\234W/lg\273Pn\323\134\015\0159\035\264\330S\345T$8\376C\327\255\367-\310'\253\355wbj\016\305Yo'$`\327\177\315\254k\317|\270\300\201\016\270T\361\334\237\302\356\262\025\361\365<", 64 },
+ { "[\362\344]**s\334`9nhC\212\320(%\224\341\366\347\206\215\337\211N\346I0R5hZeU-\3144\351U\364\272\004\231\316l0\3422D\346\376]3\211\224", 56, "4\2300\026\333\211\255'B\033\267\363\204|`~", 300, ")\333\035#\321\025a\0216\306|\273\331\025\302\306\342\231\036C\012A\240\356\364\001\030y\032\023x\031i*\346\341;\212\261\263\212\274a\017\352gM/)hN\232\262\031\371\267%\032\271@\017\313\342i", 64 },
+ { "\0232\236\245\236\367\265U\031\235si\306\327\024\240\2143\261\213\265\377k{\225\2673\026\037G\007{Q\013\2020<\375\340/>\2354p\030\315\354p\375\267\321\266P\2365Z", 56, "\262\3702\216=e\326f\231R\303z\255r\345%/\315\316\340\2004\300T", 300, "\273\376\362\0214\002\345.\313\332\230g(?\206\3403]yk\360\025\233\373H\016$\354\003\240\313\304y\373\326\210[-~\360\200C\023\340\247p\323\224\3437\022\322\255i\364\317\217\354\301\015-\020yA", 64 },
+ { "T\317^\247\304\021a\362a\235R{\266[\323\3054\252\276\036\005\327\342vn\134V\340\336\257\005\327\253\236qt\253\356f\003y\333\302\217\224m\030\264I!\207V\271fXn", 56, "\336\036\015\030\323t\010\037h2V\2514\2561\325>\3605\3354\226\363\310$\355@\261\324\321\333\272", 300, "(\202\245\265$K\256\300\037%\010\230\337\266\334\305\207\016\340J\314\233\315\313\257o\363\346\253\306W\245ID\315+\321\247O\240\007\273LG\203\2564\351\371\342\326\210\234\360\356\236\300\037\256\317\313\273\363\374", 64 },
+ { "\351{\362n\251\033=\004\304\323\332\276\304\306>\375\250\360\207\263\0359\033\244}\370\0060y\275lA\037Go\351\265>\345\270:\346\011\223\356\233\003\242{\371V\372\030\011\235M", 56, "\205\300\276\134\262\237\230\026U\042O4\354Z\324\017x\370\326~\240{i\211k\326\014\350\350\223<\011\233q\315\271)\201\002V", 300, "\250\302W`\353 ;\013\354EmG\210E\344\244\346\343\303\364s3\207\352\262\012L\200c\342\275_xF\301\302!\030\310\277\305\031p;\327R4]\241\012H]\374y\271\273;\331t\323E\211\346\223", 64 },
+ { "\373\344\235\025\313\213\037\314sE\305\217\201\207\200\255>\016\002s\270\276|\2027*t\223\211\253\350\310D\202\223Q\202\273\211\341\214>,|\334\327\375\016\242\004,J\351\207\320B", 56, "\242X\031e\233f\003@\274\340z\364f\303\215\301>\234\335\243\243\334\341\254\004M\006\275\276\266\300mx\346D(\303\207\042\263\300\306\372\240I\252\016\324", 300, "\263\336h\205\364\347\361\237\246\230\226\032\372\2349\331k@\276\255Bc\217\012\0247z\304i\353\367\206\310\311\366SzJc\232\255l1\317\025\2470\235\324>E\367\200\024\027\212H\276\310\370Z\237\227{", 64 },
+ { "\242\220\207S\211\213\272\343\333L\223p\271#0\016\251\363%\213\347\017\204;R<E\204\261\204\260\205\375e3\177\203\224Hp\377<\216\361x\236d\251\001\022i\265\026\264L\321", 56, "\202v\374\247?\356Q\037\272*C[jDkw\321\233\222\244=\206D\3264\326JS\363! \025\316\211x\374\036\273\266\212\2716\332GM\275\263\352je\005\3318\315\335Q", 300, "~\201\352\3747\372\036\276\201'\037tI\014\261^a\324s]\007\251/\276\002w7\333IO\3128)R\032\012S\263\023/i5\3013\240\253\201sj\344\214C)\361\011k\327\232\267\000%\333R\004", 64 },
+ { "\017\301-\345\223\003T\010)\134\275\373Ww\257q\2155K\324\250\200\334\315\007\246\350B\263\216\202l\304R%z!\372\370\264\332\331\324B\340\024\264\274\015\202+\033oj\354 ", 56, "\22616;\243%\012\376\342\271\275\015d\222/\327\257\215\335\253\211\373\341\324'\304\031Qh\241\353zP\266\305D\3150?l_\242\344@\354\0059iI^\265\211\343\257\205;\234\263r\224\016)\277\013", 300, "\330\007\326/\254\033\134f\373[\322\022?UM\013\355\337\313\264\2425'\231\312\341p\350!\333\370\360.z\200\030`\365\011\221\371\017\305/\203\265\007J\374\012\242\341Q\030\330\245BYW\311G\0362\345", 64 },
+ { "r\272\266\025)\000\216.\306\327b\326\376\307\364\317\304\313\034\242\304\263\321x\202~\302\002\201\212\370\213\240\335\364\012}h\001L>U\010\270\216\250S@\223\243%\234\013\311'\350", 56, "\272\221\362\224\225\266O-\360\335\247\377\371\205d\236\270=;\2716\353\305B\2476\243K\034\222\007\265\025\314\307\264\230\303g}[\271\253\211\271\012\303v\301\273\214\331\236\267`\366\255\321\241(\342Y\004\224\011\210a\315:\324\264\277", 300, "Pp\361\036\353\372\221[\013\324D\014\252\0254[\234\345Z\245\367\311\234L)\373\242\273\024\233\376S+\3751\373R\030\223!>H\260\010`\015\355\361F\372\263\343G\030\375\222p|\265/_h+&", 64 },
+ { "\326.\362@\324h\237\007\317\030\307\035\215[\2572\312\232`\302\265\017\2659-\314\364\241\346\2323\251\225\204\235$OT\271\340\231\377\210\211\254O\013\036\340t\324~\350\346\305t", 56, "\362\042\271\224\360\031\217F", 400, "-\033\3460\367 `g#\240ShxVOKzW\261\330X2\333\232\370\251\274\325i\327\354-\222\257\2552:\373\305\330O\007\227\015\350\014}G\2259\021\320'\261[Gp%?Qy\014\321\240", 64 },
+ { "\004\307GI[\200\276k\233\014\225.\363xI\373Z\327\356\255AK\356]\366vV\202\377\213C\2176-\223Ty\221YWH\232\332\364H\236c&\367\375\243^\222\035:\337", 56, "\035\241\360U\224\261\261G\020\334;\225\277L\333|", 400, "\302%ni\223\031M}\257R\257\022N\314\205\367*'\177n^\272\300*_c\320b\134\251\230\346\021*\225X\245\017\336|\134}\177\277\315\310*A\247@\330:\000\370}=\034\010\203\376\373\023\236@", 64 },
+ { "\010\202\234\205\036)bJ\310\323\276\032\230%\177\200'S\377vNv\3722\217f\265XA\177\366\223\034\042s\377\317\330\371b\014\333x\240f\366\011R%Dc\366\207K\200\333", 56, "GU\2662\202\205[B\0369\034\035\232c\322W\201\372)%CIV\374", 400, "AvE*\202\274k\201o-{!IEA1,\204\352\330WY\042W+\223\2136\365\300W\324z\355-\303[\013\0224x\222>\025\014H\007\360\234\266w\310@\276\344w \301\200W}\037N\303", 64 },
+ { "\234\364\262\000\230\271b\374\255a\300\224e\024\336\346\326\220\022g?H\2258oY6d\335j\004\376\257\254],\005\0062\227\017@Dr\331\027\204\331\012#\303\012=\225\035\363", 56, "\2467\305\015/+\364L\303\350oz\253\3162\204@\375\370\257\233\305W\345-\004O\277\267\227\312v", 400, "\240k\030R\020\201\027\303\363\242$3\312Y\307\276\010\231\333A;e?\005\023Pu\036[w\277\317\300\006\262\277\276(y\334\201D\253\226Q\3711\036H\134\320\253\301\017\225\224j\371\033\320\304\336\035&", 64 },
+ { "\037\257\213\333f05\350\261v\2161\360\355\200\033\025~\206\226\363\011\331\343G\013\207O\364U\250\211\216\242\017\242\021\3148?!\024\261\211\200\265\036&\000\3374\207\022\221v\221", 56, "\256\337=\015;6\264Z\355\255\277\257\252\030\273O\375H\350\027\234|jK\222\234\331\033\2259\134\250\011f\267 gd\264\203", 400, "\317X\2557\226\351\276\234\3122\235,.\265\247\003jFE\300\0035\373\3407\001g\202W\337\335\325w\252P\030\272\356f\267\344\276M\205\334\024n(\350_(E\335|\224\303\220\020\232\2223\272\2260", 64 },
+ { "M\242\373\013\256\274i\320D\361\373`\235\216\204\374\312Jz\270\266U(W\265|\013\316\254\253?\242\357\134\020\274\352\266:nT\210r\234\034wm:J\257\264\360\023*\015r", 56, "a\273=\277\347*\301~9\022d\027};\355]\344\377\274\236\334H~\304B\202^+\240\367\335\356\204\255+\356\356-\311\371\212\324\201.-B\273&", 400, "\020[1\211\376r{\005\214\205\254G\247V\341\010X\012\020P\235jo\256\032\216\312\002\024\210\034\015\305i\246\031\023\020j\021K\257\025U\352\027\232\357\313\240B\034\214x\305l\233\345|~+\312\233\264", 64 },
+ { "\217\266\276\265'6J\215~\014/\3073\036\225JL\323\226?\272\134\216\323p\306\315\353SM\245\364\222\260W\362\242\215\031\221\276N\366E\216\177\042\366%\004\006\030~\345\374\345", 56, "\223\210\035l]P\232\362\025\350\241S\232\012\010J\023\321\374\340\247\014tWQ\307\227\307Q\243\231JO\005\312\010nt\2361\022'8U\017b\261\261%\2319\265.Gix", 400, "\333\355\310\265i%\210\022^\370\231\221\3660_\212\042\274\336\030x\267za\216\260\257c:\251vJ{\340\3651e\013\312>\016C\016V\277=\327\233\363z\244\201\317\247u\344\235\356\212\373\255x\134q", 64 },
+ { "\004\033\236\331\011'\042\323i\013\333\304e\236\371\260s8zb\252\200u\025\313D\207H\267V\215\177r\205\3439`UY\370z\033^1\322@\276,A\004Q#\371U\016\006", 56, "\001\265\022Z\004\037e;`\2647\326\311\272\316\232F\267|\344j~\373\340\306\372\263\325 \031wp\234\034\236\025\023[7\352~\330\031uU\356bF\236l%\350^\200\312\012\240\025\351x\007{\247\270", 400, "\372\2501\204\346\276.\2567i\242\306\303\001EZ\342m$\357\302JAt3\201\316\346f2Ut\322B!$\217\332B\216o_T\020\264\035\316\340\2001\243\012\364C\232\224\373\245\260\316\330g)A", 64 },
+ { "\213\223\354\023\313j\023\220q~\260\252\263\027\300\332\0118\026\252\311\332\326\212U\301\033\303Y\017\017\244\217s\307\2501\026\007$K9\260\277\361\0043j\350\220\2632\006\211\332\007", 56, "h\026\365iw\313\2138\012\2264\351\335\014\2474d\362C_\024\225\024JXC\305$\215Z\375\373-\236\227VE\254\022\267\363r\372\317\265\325\376\013\241:\256r%\256\326\272\236EA~\261\2327\005?]\316\207ZxfR", 400, "\204u1w\363K\350\277\343\042\223/\257\031nS\341.\313G\374\342,\030]C\324En\373h\031\267\237wB\2101U\314\375\023i&V%@6\310\360\252\214\335\353*\314\225\036\037\273\203\221\312\344", 64 },
+ { "8\240\204\313Z\214\303\037\032\177\020\331rM\221\265\375\375\342\313\224\205\347O\206\322\354\267\351\207\346\347k \353\272G\241E\326\336\240:K~%i\372\2757\341z\202\212\030\210", 56, "B\313\0113B\2333\215", 500, "8\362\033\014\244Aj\352OV\203\261\267\326E1\034\316R\217\026')\262}0#\220\273\377eN\224\375}z\320\226\031\315\042p\377\015\232\341\261hC\304W\322e\204\210\001*T,\323\343\223m6", 64 },
+ { "\371\222\270\331L\332\315O\2555\300\244\374Uf\224\242z\202\202e\232\205\234L\316\372\320\025\230\031\366M`\357d7\312\022\300\260\341\016\324\313\354\317VH=\265\343H\312d#", 56, "!\006\353F\360\305\222\013\012\364SEB\373\245!", 500, "\220\303\300\031\253\227e\134\203\326\373\024\247\231\262\276]\355\361\027\331\241\307\373\346?eKjt\347\371\357\205\345\313VJ3\363\252F\007\177&#\354\240\351=\010BtFa3Z+\232\042[4$D", 64 },
+ { "R\265\240lF\235\024\367Z\231\263\251\216\332\252\303m[\316=\3151j\322\3674\307!\006\356\2235\237\027c\301\344\377\230T\372\346]~Bxm9\020-\234\244\3047\010\320", 56, "*\374\372\266\201`\266q @\376w\353S\356q*A\306\230\376\301|\012", 500, "\367nb\322\341vl<\003a\327\016\340z\3251\262\025>K*c\314c\345R\24464\324\306\264\267\330\352RN\3539-\342?;n\373\013 \240\042\351J\247\001\265K\264r\232b\021\277y\325\371", 64 },
+ { "\370\336\306 \221D/\203,\255 `\010\026\322)\320\374V,\204$\2301vdmC\351jXg\225\013cB\250\272\352.\2229\222\236\177\313\206\250Y\341\330\354\313`\217\225", 56, "\377\260~\010\305\305\317\244\3077\342\345j\263\042\013\016\134]\233\263\2756\341\234\031\215\227\211\213\326\036", 500, "\315\244lW\361Y\243\037\271\365\225oJ\214\345\303\276=\210wA\243\020H\347\205\223sB#\307~\325\364\325E\201`\017\2369\223\0229\032\177\206r\024!j\020U\231R\362C\226\000%G#\353j", 64 },
+ { "\316&\225G\355\351\364B\251hv\363\000\324{>\214W\000\3614\306\205\177\302\363,\216\346\250\215\200%\335Yo\020\017\3641\377GhM\233\366Hg\354~ \031\242q-\234", 56, "a\217\345\023\221\220\317\354\275\235\300\00708\375&\267x\03222\374\236\307:zY\200\265\352qk\334\024\010\240\332\300\031y", 500, "_R\245\215_hC\024\037\024\3230j#7\210\334\245\0162\236\010zL}8\306\006\024\373\356h\317\026\364\345<r\250z\007sS\033\257\017+\3245\270\263}\014||GsYK\233\356\211'\312", 64 },
+ { "\350\205\324D}\357\233ZW\247\277\315\335\301m\313\211\254\221\234\317\205\236\337I\250^\354\242\006b\3752\266\316\316\310\365\317\020\033?{\267V`\232\255\226\0333p\032A<\201", 56, "oR\311\013AY\374\330\252\350\364\341\222\342\037Z\273\014fet\016\030\037\316&\366\360\033\345\277Fb\352\002$Y\307\017:h2\364)\205\234\357\025", 500, "Z\006\244\336\245\201\255\361\210\324T\301F\204\270\037\001\270)Ha\336\255\346\202Y8\372D\342\372\354Q\014j\254\023!H\210\024o\242\004\353\375$J\213\221\364}F\020\312\237^\357\313R^\276\342\177", 64 },
+ { "\352(\177\002\336\371'\034\021\246\3708\220tv\325\243\224\320\343\370BLL\356\134\023\356\271\201=\223\320\020\024M\233P\377\376\330\326\003\217\247\256d\204\2611\0142\326\237\313\273", 56, "\253\255\305\215\376\251\002\033\224\247\353\235p1\324^0\215J\2769\364B\241(\263da\317%\372P\326.\242A\352J\220\320W\224\230\275\3619\241o\201F\206\255Y7\304h", 500, "\265\325a$6\262\355h\243\225\302\211m\323\201\2703\230\255\210\337S.\302\002v\270\233\353\335\344\035\264..\336\201b\352sb\370\222\227\256\022\316bc<3;\274\231\257\031\037\275\206\202\227\304\214z", 64 },
+ { "\242\022<?\216\255\332\362\353\342Qf\335R\030\200\302\023x\247\224\225#\225\376*\032v\007P$\266w\274\232\021\330\3334\377\375\360\277\330b\006\206\304-\334\372\222\314\026*\330", 56, "I\311\017\202\030\344\204k\206\226Y\350\275b+wu\342S\320\036\025D\330\215$\222\372\256\011\030te\343\3403\352R\237qj\231\331\020n@\325%\357\010\253\356\377\247\264\265\347\362j\320\017\202\226\010", 500, "\254g\373!\203\263\013]\2700#dr\225\320_q\004\215RP\2773r\342\366/e\262n\232\252s\371D\263\024[\243f\333F\247_%W\336\011\335\205\207/HhC\337\272\370\242\313L\376\024\372", 64 },
+ { "\220\353\227\035+\226 \217\372F\356%y\231\335\214!\340\235%ucM\307\250|k\376\251Q\244\335W\376\346\312\002`b\261\021Qo/\030\361S\261Q\330(#Mc\364\330", 56, "\273\213\274U\371\241\270\314\332\271\034\221\235\326\374\337~\315\373\376\241%\204\300F\025W\226\032\005\333\322 \0135L\233\340y@\210=\250\314\236\006h\2274d\301\014\346\237U\243\034\364\256\234%\367>~\341p\024*B\243\277a", 500, "c\247\212b\321\262\346\313\253N\321\345\201\206\334\254[\373L\225\200\353\000\201\313\234i4\230r\223\225\371\033\203\327ZZ*\320\017\345`\302&pa\332\2166\245\0213S=_\021y\001\274\341*\337\015", 64 },
+ { "9\331\351&r\205\345X\365\201a\003T\207\241\036\347@&\221\200\242\042\237\000\277$2>\006\324;\255$\273\354\310\177\254\134\273\360--\330?\347\042\356cf(\260v&d", 56, "\204i\217\313o\315\006\017", 600, "r\332';\213H87i?\177:\367\356\233:\224vY\254\355\366u\01143\356\001Pj\3678\256\305\270\273\352\323\303\242g\006\342|\226\374\252\233<R\376\234\242\264\343\350\0028$t\275\342\321\367", 64 },
+ { "\357\267)\355\357/\241\260K(\232j3z\022\373\236\004N\233\224\010\377\300}}\016\027\214\3340\026\343\262\232\222\237A\277\031\330y+\026li\323\332\004\314J\310\000\313\016\014", 56, "\263_g\016Bk\261\020\204\236IKM5\327\251", 600, "9\375%)H\274\355\031-\255&\374\206\027\277\314\314\366L\326\336n\032\367\2477\340O.\351\233\305\202\000\376\307\272\007\210~\134*7\134-\366\341\345\250\330\301\261\031\272P8\346h\302d\023&(G", 64 },
+ { "\213\333zW\216\235UX\210\032\034\250P\370\270\217Y\361\211\210\263M\324 \205N\006p\235\010\350\320([;\341I,\034\025Vp\263\014q\245\0148_3\256\246\177O\273Z", 56, "\231\301m\336\271\2521\254\262\035\354\027VR\232d\306]d\036\362/\245\255", 600, "\031\236\356\222s\347'#\201Bs$}Hn\012\3701\225rI\276\247\305\006\240\317\245>\276\250]\324rF\024\032\301\273\256\255m\377NO\265\304\367\203\321\266\310\356\342s\225\032\357+\312\213Uw?", 64 },
+ { "\270U\034\335n\235\270\363\250U\320\023\007\303\222'\272\216\363\242\032\275SG\210\330\3754Lc\222\350\134/h\332\371<\224WF\223o\263;\233\353\345\025\350\256\364\327\224\335&", 56, "t4 \226\006\027QS\313\233i\310X\355\276a\331\261\266\2512.=\2357\312\322G\247k\357\302", 600, "$i.\353\372\375\260\262\325\351\0369\026\030\134G<g\370\012\322,P\027\015*Pz\224PUUy\263\344\333%t\367IU\304\277\010\250\025\241\333\226\037\220\026\350H\331>a\203\275,cf\355\334", 64 },
+ { "\346m\257\241#\351\207\231\301\364,(\240\272$\033\314\033\026\012~\303ZxP\361\313v9^\334n\030\017U\352\351\352\322\352\355\341\27733\330\042w\027>\247z\005\371\006\226", 56, "-\243o\371d\035\335%L\226\267O x\350\304>\214\256\213\3356:\351\277g\306 \353\221\270\345\301#\037\001\320\260B1", 600, "z\134\243\023\005\301\366\210{\310{\356\254\207t\205;\342d\330\226\274E\005\016\011A\322\242J@\327\364\026\313\016\332\201/\240\024-\004\016\304\264\222\326t\315p\274Z\242b\337\254\3725\377|jIs", 64 },
+ { "\250\225i\221wd\264\223\242\322\230Yb\362 \310l\376\353\231\016\230tp*\264\300H0\207\354\033\273@\342\207\220\006*\351\355uc\024\243\257\315\234n0M\342\373\310\033\220", 56, "O\306[7\256]\327\010\244\317`{\320\370\317\362\252\320\237\2253$;\334\266\210\327\350-\033\232A\342e\325\367\3747\222J\230\227\201q\210\315\2072", 600, "WI\236\335P\013\367\250i?\134\202\362\205u\302\026\257\265\341\343\012\007\202\211w\335\314&\321\374\271*-\014\002\214\264\367\270\326\240\3664\325\377y\372\350\244\300\304\300E\201\357\015\375\234S\264\316\271k", 64 },
+ { "\317\372\363\304\351\254J\322\330\333\343\342\273X\005eNe\311hQ#\025f\372U\2062\241vTFe\223M\342\232\340\204X\226,\307\327\301\007o\231\023\316 K\347\320\323\260", 56, "9\313\372\250|\034*S\357j\353rj\312\220/\363\032E\263s}\324\261\237S\371\347\017\337\355\250\243\214\347\031\340\346\305\0070\227\371\005A]\231\020\011+\330\310\016\221\337\373", 600, "P\263ay~\314\235\215\300\223\357\3164\035\273M\211\010\315\323\305\374\366\305\2254\345\242\245\243\334\301\202\315K>\361Y\231\214\3736\2526\205\345\362\360s\261jP9=\305\247jDcM\302x\300\203", 64 },
+ { "\231\020r\351\222\326\232\3676\363\365e,\345\2735M\305\206D\233\364\004\226\3011\021\350\335\274\332\211\220\244\333\322\233\346\235s\230\331\243\006\272\3071\273\012q\001<\330g\274S", 56, "\005|\314\037\367\277\250\037\357\241\001\177\015\223_\344\034\206\232\015f\365@(7\315$C \236\352Y\245\335\335\273\023\256\224)\3341f\177\240\264O\340s\023\007\2762\203\345\237P\274c\231\226\352\340e", 600, "\305\321\337W\216\330\313m\207O\363\360>\272\366\260\357\323\363\276*U\254\015\2625^\222KpA\305\272\325Q\033HRv\005\257\244\2535\350`\230\367iE\007\035L\370\245\351\376\271\324Z\322b2j", 64 },
+ { "\037N\217\212|}\007S\020\333\014\232\251p\372\303K\221\012\246vM2a\015R@\352\207\222L\305,:Z>\366\315\011\337\235\231\005\360\201I4\250\134wA56\272\360l", 56, "4H\225\022^\246\212\243\273\232\202\355\2763\021%\027B`\263\237\262\2154\204\243\206\216V\322\234-s\2118\231\217\231su\271\350O{H\210\3771\336\23025\202\035\033G\3415\002\222o\205h\214\270\205\3148\274b\345U", 600, "Z\265\226\035a\361\003\364\343\370\313*\260FA\335\363\252,1\224\300\031\306\215\323\230\017\032\272\275\001\3052'^Ip\223Y\367\322)\211+\206F\244}\377n\323d\322\301\237\014\223\330\215\205eY\243", 64 },
+ { "\376\017\306\373\021\035\314L\023y\325\312\233V\010\210O\177\307pK\372\207\214\273@\320\333\265C\262\224\276,\265\324\311\325\347M\324\362\034\255\025\015\351\325\232\007\216~e?\275\346", 56, ")\344;\206@JMm", 700, "}\216\030\376\247\240\353\010H\313\326\233H\314\300!\3035\032\213\011\273\247\336\015\031\356\221\021\0135IP\004(o\353\266\302\042\345 YNx\2168G\260\000\021\300\230C\263\364\246;\312X\357r'c", 64 },
+ { "\200}\3266\3355np\266\024\225\022\220\024\033\35004_\227|\244KK\035z\330\025Z\252\2433\267\366\014,\242\227Xu\243\224\202\134g\364)\343\250\254\027g,h\261\352", 56, "\277m\222\260 \221%\375l R\023\037;\012\211", 700, "\033\343\371\36231\001\307\257\236\264\211\022\323\322\317\274\255\223t^D2\042AS\201w\316\313\346\001z\177\364\274\345\3602L@\330-=\373\251\213\244\304\334\2513\257\244\241C6\257\272p\275\262\320\305", 64 },
+ { "\033B\026\222C\254\310\342\304\321\016z\205k\224\252a\000!\200M/Rt\250\353\020D@\255\215\020\246\203\347\014\306k\006\331\301\256\317\275-\012i\355\305\031O\330\225\210\235\341", 56, "\231R0\266\357i\320z\241\257\006\001\305\255t-u.\024\024h\362\303\010", 700, "l7\324@z\220\002\376\016\346\267K\353\372\312\024\27406r\310@\274?bUR\323\257\255\326v\236\242M-\017\002~\264\374\225\260\317\347Q\223\304\343\177\020\302\300\246\200~H7\244\304\200\024f\003", 64 },
+ { "=L6\333n4<\267\177\003I\226I-\012\004QV\006}~[{\361\215\263\0223\317\247\322\323\201=G8EC>\001WD\264\246\006'\035\264\255z3~\014\243C}", 56, "z\321\231E(\307\255J'\354)|=\027\244u]\012\375\005\317556\351\3456p\3569\0200", 700, "\211\261\347\230\222\312FWB\3207\257cP%+\2160\254bX\311\00638\260\344\215s\317\267\252\241\240\352\305\334\261\261\305\232\272\272\263\021\304 \261O\321\304m\371\007\265\024V\231~t\017s\321R", 64 },
+ { "\300\241\015\235w\211\316\205@\320[\230\373\257\343\177\202i1\227\325\276\376%8\257j\345d\332\205\261\374\2751\206b$\306H9x\306\323\257V\024\272\177I0\353\201\277\306V", 56, "\011\254kz\217>\305\2321\3031@m\376\376\331\021]ASZ\036\237\020\231p\263\203\263\024\372\347\264\3618\205\253\207.\306", 700, "\263\200\330C\011\000e\325XDU\233\255\324k\014\243\360i\027/\302?\241y\367<\021\315\177\032\347\277+\000\033\227\015\223\322\367\252K=\326\373\226O6+\334\250\207}?\001%\272\204\235`D\346\306", 64 },
+ { "\347\317\361o\3477X\336\203\206l\366'\212M\042\375\364\374\035\233\025\042\360\344O\241^\341O\015\016\212\341q\233\0156\312u\261\320\253!Kh\314\253\250\302\253\262\005\252\375\376", 56, ",<\3611.\312\336\360\214\022\202T_\330\303!\021\017\016\332\373\032\360\346\307\023\225u\264\236\254\323\336\275JbO\345\304\361\306\026A\032\010\252y\364", 700, "S\323\226+\001\343\205\255/\035F'B\134\273\312\365\350r\203\200\035\340,K\243:7\254\373\340\261\237\317\323\264%\310'\270P\345CE\042K\214\233X\215\214V\233@\330o\351\030A\300\224\326\305\213", 64 },
+ { "\307Ss\376.\010\032\270o\023\363\303X\345wR*f\355\2278\346\215\271\311\202\333q\372\007G\033W|\326_\222X\205l\305\372\205\246|\306\240\265\320\207\235\222\335N\233\032", 56, "\226\277\036\252Q\033'F5l\212t\261+'Ty\015L%\006\013\2471\216e\352'T\323\344\346+\362\005\270a\027\220.\242iw\336\030@\321\340\213\371\035\255}\351\330V", 700, "\223\2313\331\263\312\244\352\354\370\016\224\316\224u\3119\012\364\327\273k\037v\311h\317\221\340\275\241\231\026\265\354,-\211$\356s\213\243| \3423a@%5\276\267\3574g;\003\216\264\374\235\370\012", 64 },
+ { "\234\134l\273=\360GI\370\350U\324J\330\251Y\315\012EX\346\230\245\201-X\2156;\026r\345Q\226G\304\134\317\322\274\3055\246\2700S{\373\343C\012\366\322\231g\333", 56, "\300\372X\134wN\035\301\010o\270S\177rmj\031\356I\241\024\362\326T\031\373\025\227\310uC\233+59\216{\016\345\226\353\220aN\351!M\332\311S}\365\377\303p\302\215\307h\206\275!k\304", 700, "\233\354\022\017M\373\371\232\003P\033\216\242\3115x=k\226Cn, \327\207\227_\010\035\276j\210\304\210\260\027\237\036\021'\032\367o\2638\346^ *\233P\372C\005\355\266\326g\376\370d\177\376\315", 64 },
+ { "\236E\212\232\211L2\265\264\007\242Y\311g\321\356\341A\250\210)v\245\204\226F\020\222\354+#\042\020\233\016\335\042\350\275\313\316U>\253\373R\255\033l\000-\2604\320HL", 56, "&\326\306\023(\224\272\353Oz\021\030VyE\204C\242\325]{qB\202\337\360\302`\210\243\376\213\316\277\2300\375\251\326j=u\033y\366\256\370\320wh7\364R\270\274\200\351'\221\367H\226\355h\250pg4\305\303~\366", 700, "\010\357\316\342\301\241\177Bj\225\314Z\344\334M\326\214cK\222\035\347I\200!G\030\276k\252\322\263\333\320\310\214_\346\033{s\2359j\010\024!\361\350\275\365\025\372\354\360=2)\323[\200\260\261\367", 64 },
+ { "\331t\327\313EYj\302\250;|\000\346\337_S1\026V!\360\364\236\033\0107,n8\264\214\0155\204\260ov2\215\317u{\030\036k\314\033\217W\002\314r\006CC*", 56, "=\006\361\134\010a\223\010", 800, "\367\243k\353\347\331\003\316gv\237)tH\371#\370\312W>\217\306&\306[\2226\016\350\2446$\350G\267l\253\303\335\205\020\000\004\314\376(\252\224\373\203\031\335\021n\361 \016\274\223\036f\035\335\261", 64 },
+ { "\226\220\3256G:\242\225\364\205Qg-\354\372d\363\256XO\242\275\032\325\014a\277i\010\263Y\221\343\0047\034\231>\222\306\306\227\313\022\260QNjw\303\231O\243=\342>", 56, "H7\265\214\251:\317#Z\272Y\221\240P\025\257", 800, "T\304\334j\357\322[\306\257\254\275\360\371#fRz\253\372G\017\002\246\355\222|\300\271L\303\025NgC\025-\261\220k:\022\352\207\264*\363NB\310\010,$\333\245\351\246\352\300\376\365M\013\240'", 64 },
+ { "-\007\335\006p\013v\225\356\002\334\371uL\313\233c\317\220\307+\017\262Z}k\352\334\202\016\251g\204\224\006\356j1-\214z_\272\372\274\224\342\2422.\022\352I\246\303\262", 56, "sC\344\326\346\214\034\354UQLk\364\300}\2477/\373\327\346\210o\033", 800, "\012\223\220\202\003\013-\365\262\214*\345#'\246^9c\311\347\373\031\2731cL\230\034\340\177\213Va\002:\006\357XW+@\334\326\015qN\026\256C-\275W\215\245Euc\026\374q\307(\202\211", 64 },
+ { "92\235\215|\036EF\211\322\350\247vP\177\347\305E&u\033\2126Dz\351?\000\134\212\302\215\322\327e\374\227\356_\322\346\275\236\042\351oU\031\011UN\344\323\363\006\201", 56, "\320\011L\355\260\334\247\227`h\265 )E\350h\337\014X\334\357\352\372\260\242\013\257\317:+u\226", 800, "\177\255\370z>\216\202\260\030\342L\331\252~\027\211\035\261\325\321\3237,!\237mQ\371\377\270\221\337F\321@Qr\315\322\312o\233\311\0226\331\311\220\250\3133\335\030v\015\261\325i\362w\3025\256\263", 64 },
+ { "*\004^\322\344\321\374\214\034\274\267t\232P\373\214NJ\201BN \373vS\220\027\244m\236\255+!9\006\332\274\036Z\265\321\262\371n\042\317\345\347\3353\247\310!6M\004", 56, "\215\025t\202\003\320\234\240\002=\302\266\002v=\233]:\032#hM\237Y\362\222\366\3609Q\026\330\334\344A\027\025\271\034/", 800, "\362\042\0257\314\336J\177\213+\211\335<l\205\3046\274\305;;\372#\370\204\346_\320\306\322o\032\273\227~\017\025\370;6q\372\322\0074\215\201Y\344\300j\373W!\3409\017Ne\366\237:Z\011", 64 },
+ { "\021\224\336;\367\315-\007/I0.N\000\226\376\311\215\203=\225\267\177\245\315r\305z\371\215\214p\346\373\301!Z\253\245\177Z\350\212{\016\373Wv\202\321J5z\232v\302", 56, "\360!\367\327\302Q\327k\207D.\210\212\012\254{1\310\215\212B|W\317\005\267\257\311d\243Q\245Z\364\270/\341\355\037M2\036\260\310K\257\263\237", 800, "\364\342\201\226\254\257f\342:{\004\355>\345\350\350\260[\317\263d2\316|v\332G\021a\210\006\034\274m\006\210\3024gM\022\354bU\323\255\014#F\011nM\303\352\350_\257L\321\242B`\256\340", 64 },
+ { "L\204\346D{!\253\215\200\270p\012\237;<\264(;&m\0102{\025W\277Q7\224\354\342\2443\300nD\364\222\026'\0215e\346\234?\320K\203\2238@#\345\030\007", 56, "O\271c\371\326\253\036\205%\331\031\241?GB\177!\334\254f\303?\361\251\245\303&]ES\300;q\016\267(`\272\327\311\206\327+\017\241n$\225\210\216)\032(\230\327\360", 800, "\134vO?\214\307\042]\0237\300\314EL\026\236\320\035\311\217~\247#L\2044\032\3064K\375\312\004\0045\215\361\372vq lX\234\205Z\265\300\211\313\341\335w\025Is\230\307\035\257\351\316\337C", 64 },
+ { "\377U\373\2454QB\021\242&\356&\003\375\232\177\205tY\242\033\352\222\312\271\331\315\350\313C\014\310\010\223\360*\266\031\025$\030\275\215\177*S\177\017R\261C\341F\261\363A", 56, "\345f\014\024\251\226\271\255Q\361\034s0\251\035\322U8\360\036\266\301\327\276\002W\362\210\305\253:\360\022\213n\361rW\021\353RjyW\035\206\35532\371\334\350<\245\020\375\315C\037<Gw\003M", 800, "\000\134\317g \344o\221\374\373\213\216\042[GQ%l\220\2163k\320\255\237\253\361\272\250J\220\225\322\042\220d\203\221O\327\214\256\027\023Fp$\004<\361U\317\247\235\332\203\035\332\315\026\262\312\266[", 64 },
+ { "5m\017\256L\271\334A\266.?B\177\334\202\206\014+\031\3066\211\247\272\242\314\264\035\356\350\333\134\326\301<s\242\246#.\200\275\025^\301\351\247\377h\362\366B\245\016#\327", 56, "HpKx\255\377\325\037\333\220S\006\276N\352|\264\264t\327\310\342\042!\277\276\010\307%\214\366\247E\205\267\312\007\006r\362\027\273\242\240>J\357 \362\026\222\377\327\332\015\276Z\362'\030:c\022\242\346`3WEx\304*", 800, "g#\2761Y\327J\204\256\311\322\357P\261\342\247`c\240\247\217(<\235\314&\032\000\312\331o|\345\004H\257\042,\264\212\007j\323\334189\313\000\026\257\276\204\027oK\227M\353M\313\177\265^", 64 },
+ { "\220\033\242\276\005!h\264\205\376\341\025\311\036k\222\327Z\014\236T\253&6\303\023z1\252\263\355^g\205\020\007+'\200\253\207\340\301\252\341\023\016j\301y\374i\227\320\3376", 56, "\035\321\264\204E\217A~", 900, "Vnq\035\354?\010\220\305M\311\232I\216\374\255\027rE\002\356\033\366\276\021\270\344\215,S\306\370M\332\377\320\376G6\210$\373\220\346@!\031bv\202\021%\177\364\274\203`\332@\276\233\006\245F", 64 },
+ { "\024\234\347M\376u\337\222\007\262P\325@C%\0125\337\272\317\215\377d>\211\373\241\2224e\025\352\356,\036\335qw\227\376\274\345d\362\332\335\010%\226\342\276\315\312\037\237\341", 56, "@\361\335l\273\026\232(\007\257K\251\361\351B\314", 900, "\232\213\314\327f\002\000\256\230\244\372c\004\211`*\242\352\214\000\007 \200\306\201\305z\227\222\263!\035TU_\301\332\341\221\321E\223\032\363\370\327\377\300\327$\237\312\325\235\026\372\320\014nCrB:*", 64 },
+ { "\340\017\331\257F\302X\231b)\377\022!\347l\274\246W\312\3279\367\325\223)\246\264\223\276\350x\246^\264}\324t\301-1\042\234\263\001tO\355uG\243X\225\373\0003M", 56, "\017\260\272\262\357A\271\024X\257\014g\360\256\235\265\301\302O\022\266\003\134\022", 900, ")9\026y\321\012i]E\347\224\332\210Fl\202\025\230\032\000\374\2748\272\007$\353y\264\000{\267\134\204\210\320\011\372x\343@q\254d\3541\334\020\042\010G\003\215\023\330B\33484\372[\272\273\032", 64 },
+ { "PK'7\267K\226\322\360\027\207!\2054\016\277p\240j\225\321\204\330\216-*\352V\357\226\177\214\015\312\314\240J\042R\374hh\255\031\022-\377\001^q)L[(\036 ", 56, "\305k\374\025\247\372\352\370\350v\033\271\243\311`\205+]\336R\257\210-4d\302(&H>)o", 900, ":P\335\271b\336\203-u\005\341\313\242\377\314>\015h\021\177O\351\350\320\362*;\2159\322\266CS\253\207G\000@\323\240o\267\011\321:\361\222u\015\346\343\301n\211\375\344\271\302\210\376\342\034\254\223", 64 },
+ { "\310\330\242\212\3177\277R\322C\331\374\3326\273\314(>0\320\357\042N\012\024\244`zh\202\376+\270\036\226\026\340L\251\017\200\347\341\205\2739\235\240\034\250\210R\334\320r\241", 56, "4\273\344\355\336\027\241C\220\271\006\026\270\213~\365\236\230\230\217\042\217\317\021)j/\2372(\215\007\266\305{d\370X\272\015", 900, "\242\250\134\3703\336)l\255mX\014\020-BG\206%\351B\244\231$z\260x\276\220}\020\334\310\270\243\036?\223\020\222H\201\326\203\372\252\236\246\355\010*\030\252\003&\036K\324\201\256\262\230U\2049", 64 },
+ { "&\221\233\226\351{\314\244R\254\337\341\265v\251H%\373\210W\042\367\220\2458L\214[IM\253\234\315\370\010\0253\366\205\236\244R\207\212\302\206\221\326\351\031\336[lmq\244", 56, "\360\345\305\014\367\322\302-\3147E\243\001\352\002v\326\365\203l\367\0347\375\206z\004s\003\367\201.i\350\331\315\213\236\321\223\2241&\355T\251\366\134", 900, "S})\307C\037\213\352h\346\177\2231\021DQ\000\355\245PH\325\263\376\275U\2040\335\033\333b\306n\002\365~\330<8\024\341\205*:w\305\026X\205Kcyg;\032N\037\274U9\210\264C", 64 },
+ { "\324&\3775\250\242o9}\324w$\015O\0148\311\203\332\360\210\352\006k\027\323A\035\207\21014g\021=\230C\317\201\214\035\257\2402*\371s$Y\232\2459>\271\331\023", 56, "\201\234\241T/\225\007\206\237\356Jw\252u\2178\360\372\224\005\217\335\037&)\200k\256]\177\011\013\267KB\314\367\334\305\301\344'\255\373\310jk\371\010\321\134\032\241\241\364{", 900, "\001\351\012\372\250>\310\370\301rr\023\027\025\2269\340\330\321\374I\007\210\042\253M\227\353\361\361\240\323\310l\263\351\204\015A\225A=w~\022\027\357\007\252L1Hd\253<K\203\336\255\343F\240\017\015", 64 },
+ { "\211\277\364H\333J\012\304\247;\331\324\210n0\033`\362pE\316\264\322J\267\335\334k\366\341R\365-\0253R\371\037\347\326\324\306\366[\002\365x\204s~LR\235\267\244\355", 56, "\012d\3716\364\026x8\341\317\243\337\313\376\363\211\353\002.\2776\027Y\042\027\371\317VC\306\343\007Jq=\204+Q9\311$\307\134\370\020x\032\2024\022~R:\364lp\302\205\254\335\314=\271\275", 900, "\014q_[!\315\261\274\025\234\243I\304\204\206\337\301\237\244\254\350'\2567\030\264\300\310\271\375\334>\010\214\325\225\034{\005\263\032\033\303i\253\325\021\352\037\236tv\313;)\257\337\270\004\042\340\015L0", 64 },
+ { "M\362\244pg1R\245\331\215k\033p\253J\254\362h\362\353~\310\265\210P\277q\330KR\001\303I\223\000\031\345\336L\3653\200\360\273\376\315CN\320=\224\355\345\263\255\216", 56, "\225\250\341^\2263=\270r\030\310{\014\263\222\356\031I\256\1349#\372\347[\363\026\376\231\331\3770\304\331\350%j\344\3715\325\231F_\273\361\237_\225\351\007\031\177\330t\010\206\206\003\343\307\036\310x\226=\031\355|0\255\371", 900, "\375)M\213\204\314\340(\310\013\374\254\344\220\223F\366\003q\303\220h\237\273\320\273U0\3758]\300\042X\221\347\346\012g\263{\353\353\013@\217J<\015\337\013\254\020ruo\310\247W\304g\312\322\350", 64 },
+ { "[xF\003\370\236\377\362\276Q\310l\336o\001[0\335\005=\271\226%a\012#8\346\3715s\015\2753\344\340\030\352\264\021\347\311\226\007\315\251\354,-\351\015|F\313gB", 56, "AH\260\363\312\212\224j", 1000, "W\256\352\317P\3549\250_cu0\352\016\305\025\235\306\016\2440W\317\253\011G\365\343\220\317\00560xW\014\377\230fQ\271E]\3461\354\245)\002\233\031c\317{\300\212C|\021\213\370\025W\225", 64 },
+ { "x\340\224\033,\037\317Y\242=F\270H08\371\347\210<\350\316p{\213\333\032R\372n\263Fx\000\305H\302\023yO[\254^\365\360M4\337\360\202*\206zg\355\306\005", 56, "P\256\0302\023P\212-\015\302\334l\225\376\307_", 1000, "^\260\023\024\340\263v\275\345\340z!\263S\231?\301\016:Vym\254\015\245\251\321\303\032\247~]N\023\022\372\332\317\362s\312\233z/\031kFT\220\033\317 f9\275\031\213\233\237\014K\232\032\357", 64 },
+ { "\357\254\354\376k\307\337\363jbe0\030\3037\307M\334\220\214\350\367]\234^\326,\371\306\201\251\256:1\306\010\300\346O\233\236}p\037E\031\035\360\262\021\243\260\331,-\326", 56, "@-\204\272E\361g\205\037\222\362\361\0137\363\365\010\305\265!\030EE\325", 1000, "\272\356p\267:\251\314(\371\261\3726\356\257\274\305\263\2266QE\313#)\215\010)\342\323\273wKL\300\012\010\036\241\006O9\202\266\207p\307b\301\352\234~$\335\236M\302WEqS\272\317\300\004", 64 },
+ { "\354\325X\031\235d\376>\373\207\010\011\3468\2300\273;&\343\367\336\332\212\025\355\016M\000\303i\326uW\227\3625\2214d\202]\015\261zt\212\275#}\363\347j\177\226,", 56, "\364\232I0`\373\306\273\247P^\2322\311\355G\207HQD^\3420\333\336\322\361\017!\247\306\244", 1000, "u\336*z\365\035(2x\346\345[\305\035P9tR\237Z\2155\260X\201R\367\350\362\264\276\306&82\321\363\003+\2172g\305\336\307\346\201\032\335\233\232\000-{\012\254(\256Su&O\260\223", 64 },
+ { "A$E\354\351\002<\012j\265\273q\042e\337\317L\005k\370\346\020\014\352u\357M6\276\337\334FD\342e\335\247\262z\246\335\015\336\277\014':\263\344\313\326k\203z?\304", 56, "',Rr\221\027^\012sf\321\001\027\340\303B\305\3400\317\013i\222\235E\320,\252\177\275\330\214\344\336m-'\012v\005", 1000, "\013\222\350\277#\344j\322rT\223\033\217\340]\247\235#\372\300\360\026z\004\026\267I\201\240\215\0025\320\202O\326\005g\032\221E\327\253\257$\357|\216\315Y\351\235I\015*\251\372\272|\375\374\262Zx", 64 },
+ { ":\234E\033\211\333\024\266\034[\244X\012\237+\343+\361{\177\244,\3540Re\012\337o\365Z\272e\020t\260\244I\353\267\311\005\015t;\023\035j\246G\0349\346Nd\255", 56, "\212\265\352\026e\2642BJ\250\324\003\336\042\247\320?C\302\227\327\037\317}\276\372\2069\363\025uf\373\333SS\2317\241\213b\026\215-\275\003\353\005", 1000, "]\351\274\246#\301@[\363\331j\241\371\037\334\363\323\307\200\227xT9\227\333\326\001\033jU \011\3063\232\276\015\357Z\333-\300\022\0266:\345\241\237\015\012@t{\315\355K\305\342\023\200.\374\277", 64 },
+ { "\230\354\247\310*\307w`<^i?\212\264~\317UY\213\013\303\024\357\233\016\207\312\3043\025\341\361\222+\333\375>F\026\330F\330@\220\216\314\350\231\214\253}\266\221\211\260l", 56, "#C\261\327n\205\266O\013\321\006\257\303'|\253\025\341@,hd\311\030 \317\355^\346KO\262\303\202\002j?V]\001\201\327h\020%XV\221u\227}E0\275\344\034", 1000, "\360\364\326R\272l;dB\247@\272\217\237p\337\322iONk\241F\364\225I/+\352\273\202e\306h\2742\244G\341\325\023\217(\033V\220\311\266\200\010\325\373iA\327\334\340^\203\357s\312\314\374", 64 },
+ { "\042\362a\336\307\035\016\376\022AR\316\306:g6\025\316\365\267c]l\365\360\312L\234\021\032\353e\211\213@\366\231\3673\013)U\241^\255\370>\035\351\302\3778\020\264+\000", 56, "&D\222\354\376\333t z\037{=\346z9\354\213y\340H\377M\015\354\036W\361\214=\371\201\336\017\2402\244Sg\313N\2702\362&\037\374+\371\015\255\325\263\261\303ML`\374\016\245*\272\347v", 1000, "*\257\371@pwa\351oY\230\231\034<\205\372\023\224\034\345\372\337\021!\316\271*/9\301\307\363\275\320s\230a\315\345\225\343\336\030\306s\361<DYo\222i\255\221\3436\322 ?S\026H\232\376", 64 },
+ { "\272b\240]W\237ll\355\244\233Su\240\027B\320\207\230\333Q\330\016:\012\212\232\220\255\325MKD\367\316\217\336\237\347O\307\352\251l\364\241\3603\320E]\254\305\201RB", 56, "2\314\263\253\025\002\232'\207\034\205\224\242f\375\023\201\304\3579\2566\237()K]\032\317/\321\374{D\313\020\214\240\270\234K\245N\2420\345\273\247\256\2761\365\256\323\007\206\214\013D\007\333{'L\310\226\311\222J\263\243\260", 1000, "\037\010>z\004\301\350\365<\014hb\262\210\340\230W\372\215\245N\225\374\242_\371\134+E\240\307\026\032\257Re<4\373\216\272\336|2\332\325\204\276\333\004\2112D\322{fC\025Z\346Z\205\345O", 64 },
+ { "V\227\252\273\367\350U\350`\241\303\266\211\362\001\336\202?\340[\265\201'IR/\201`\372\203\010\250\342'?k\326y\322\242\355Qt\373G\331/\207\240]\347\216\032\205n+", 56, "\341\340\034\243\201>\0261", 2000, "\371\025\042&H)`)\277\361\026\201\356\310d\000Rr\232_\365\341\020l\304Ja\225N\253w\003T\023m\362mCz\225\317\301%\017\016m\276\233*\251W&\335G\017sm\266\015\343w3\002>", 64 },
+ { "\000\270B\346\340\316\355\311\177\227\314E\361\013[_\241\226m\370\213o\014Zm\375\343n\035\327.)\221}\006\257P\0222\027\364\007\301<i\321'a\224\266\254e7\275:\017", 56, "\032\275\247\042D\011\331z\317?Y\244\207(\363D", 2000, ":\330\255S\227\134\212\322\342\322\306{\320b\332c\276a\354\227\020~(x\347\310h\302\341V\003\033\365\305\360\327\301.\224\267\327R\347\306\203\251\2065\225\364G\000\371\332\241A3yI\315\223\364S\203", 64 },
+ { "\335\367Ji:\237\005\331\300\304\012\306\273~\350\273\003\036\213:G\337J\336\207\302\270\204\213\012d\375\220\370\3761\030\273\202\000\202!V\303\246\241\255\027\335\275\022\242(\230:\034", 56, "\253\002Y\336\007,\012\036Q\250nml\274.M\033\134-\214\301-\007\270", 2000, "\346W;x\262`y\011\252\314\241\267\312\254\032>\333\231\201\336\371\010\006\207\244\373\336\001\237s\262\261Tj\030J&\214&\202\220:\2579\346\273f\305\344\362\361|\330\335\261\227\017\3607H#\260:~", 64 },
+ { "\0161\342:\237\241\016n\241C\335\323\233\376\240\230^\303\263u\336\344\012\360\306\003%\313\345\275\311\252_\016\252:\374\233\031\330\037\001@\304w\200rE\020\025g\341\270\327\314\350", 56, "\301\2278=\351>po\352?\306\024\230\005\246\345\035S\213\035\343\275y\307\370\3432\264q\376t\366", 2000, "\312\034|dHb\251\354\355\247\345\323\315x\273\234\035:}\376\322Y\376\267\306\202g\010u\324\037Q\206\023\305.\004\251Q\256^Ov\261/\233h\004#e\356\375\2403\355\303\334*l\356aj\222|", 64 },
+ { "5\221Z\313\212\034\230J\210\243(\250\274\347$kN\323\213\327m\134n\277\263\313\017:e\353\211\223f?\364\255\252\306a\272\333\241\342\373\200\007\240\260\270\177\226<\022G\347r", 56, "\301\012\025e\324\275\356\211/\024\333\010m\340\215$\313\227\353Er\2542!\356x\343\022H^\244\340iQ\315\310\205\373GP", 2000, "\005L\2216\245Z\244\305\303*1y\216\265Iv\251\357}\252\021\266 \360\357\250\2561<\360\252'\367R'}s\300\222\272\222\362\273\213\211\342\217*\331\202\363y\320\252\024\005+\027\362\204\225\333T\350", 64 },
+ { "\357e H\330\351\0252zv\002\251-\312\306\276\001o,\362\311\252#\251\333Y\224\264\350\010QK\305\252\267<\365E\344I\027\320V\015\307\262\3329\372\000\024\014\205\275\233\345", 56, "\346\346E;a\347\331\341\314\242\273<\275-\342<\231\250\306)\017\336\244\360:MN\220\017\231\220\261,\245\310}g$G\222\012\300{\225\364\3515\204", 2000, ":\310E5\210~\277\205\206yLcX3R\005\244\000\301NzZ\320\305DY\370,\357\3507P\255\3762\377\260\230\372$\214\261\230?\322\366M\256Lq\314HtP\300L\335\327\363\2175\256\375\035", 64 },
+ { "\007\213\376-\304!\347$\204\002I&\3417\027Y;\243$$ \001\007U4J\216\000\331\274\277\232\212'Ay\275\022q^\200\000\017]\320\030\353h\266u\321\372$+&\025", 56, "\243\006g!5q\241;\2612\306Y\356\231\337.k\003\374<\016\275\210\354+\265\336\224\372\004\371\022\036\214\275?f\207\215jk\262W\241U&\006<@\341\024\015I0\034\025", 2000, "P\3443\025\023\014W\177\273\201\340\036\256\201\006\034\204\354\344\237\315\026\230\345l\014 \274\271\331\011\317#r\327G\327\277\024.,\001\323\250\251\326\025\334N f\2664P\025Cf5\312#\033?C\271", 64 },
+ { "q\020\016\307\224Z^B\014_V\3456\313\214\221\342\016\374e\207u\306%K\237\276\263\307q\250\240\302~\316\361\031\204\233\2503\261\200\321P\302I\333\372\305\357e<\017R\246", 56, "\215\246*\011\312\342\002\335~z\276\261\301\255\3702\235\375>\201\246\003s97\301\217\361$]~\357\357bpKD\177\276\330Mw\323I\327\300\241\360\010%\363\021D\377\031P`o\367s\210WiN", 2000, "\367\263X\322\212v\325\010\275\376\215ME\303\356Y\010\203_n-\256\242M\346\203\275\334'dq\207h\351\337N _\215\034\021,R2\245\311c\306r]WS\341Y\253.\235h4)t\334\024i", 64 },
+ { "\3623\374\265\252\037\207\364r\177\021\313\267\213\015\3318\010\357\361U\227\224\025=\324\274\353`\315\230\325\232\303\004V@f}~S\344p(Q\312b\353\320\326\271\365y\255\020\262", 56, "\222\356\231\372'#\017\236-\027\216\245Nl\375\225\375\202\340\326,\244\254z\316\221t\222\246\314)\2123\266E.\241\257\262\337\353\300D\007^'\035\253I\366\344\274\025\247\371\373a18/\336\037r\201VG\204\012\222Oo\327", 2000, "\024#\307\016\202=\367\037\325C\340\037\311cRcb\024\335\241\363\221eM\037\377\021\301I\333zO>Z#\225\234\267>\177\223\331\002\266\352`\261\321$>bF\370\203\267;\204\267N\234\341c\306N", 64 },
+ { "\240\275Z\322\373\303\225\042$\375\346=\213\374\323s0\245L\216\317 \333\302\032\301\334\036\266tNG*ra\263\216\352\324\323u\362-3\205\275\215\333\012\243\225\344\225\253'C", 56, "\220\021w\353\360\341\226\261", 3000, "\015Y\222H\035\213\232=\031\203\016/s\025\216v\004\0029\010\303\245R\026OES1\004\265\376\027\235b\212\2678%\261\351!\025y\317f\227a\350(q\236\270{~v\015\314\3720)z\260\021q", 64 },
+ { "gG}W\3018\377\3466b\377\031\366Z\026\034\331\252\004\307\312\014+\347\211\336\012\365\371%\301\000h\324\372\327\231\254D\272x\231\350\256\224\262\363\330\2503>\3134\007\042\035", 56, "d\243\237!U\333\364}1\256\205\030*d[\337", 3000, "]\371\224\340\342\265\361\332\335j\025z;65\363\3553\360\205\210\311\212\241\375h\321\343\010\216\025_\204\324\244\240\003J\301\034Xa}\252\365\010\232\351k\371\363|\216\360O\360\217:\016\207\214\036\375q", 64 },
+ { "}\367\206\346f\344\015=\277\240>\323\225\254\033\221\006\240K\373r\332/\360\254\315\022\355\220n]6\316qtg\373jU\211\037!\215F\301p\017\276\014\002H\301\016\011f\240", 56, "\271\340\234\017\256B\004\215X\303_\374\266l\030\302{\307BX\006Z\304\206", 3000, "\014s\2215%\226\346\340D\357\201B\3052\350\376\267\376Cy*\355,\001}%g\3242\134\012\364\251\263p\033E\275\273\215\257\243\025\013\236\033:^hC\357&\237\371\277U\265!H\242\277UIp", 64 },
+ { "\004\001=O\220Z\0158\002\250\257\3670\334\230\320\032\324\353\207\026\005\235q,\224J7\350\002\327\316\134!\3016Cn6W\3303\212d\323]bF\366|\246\015\254\225\031\277", 56, "\245(~\264\367\030+\313\250>.\277')/IC\314O\012Sa\335\212\022\276\216\255 \343\341n", 3000, "\033N+$\021\002\355\346\300`\017\223\346c\340$\347\252hPO\370\352A\263\333\322\333\003\375\361m)y\003a\310\347\250\224zSE\271`\320\264\332\322\275\012\256\001\3303\365\343\2637\360m3\033\215", 64 },
+ { "wXw\343\314\327\210\242\014(tHaGv\251\302/\202\002\006\335\343T\0174\134\244!\203E\311e\351O\316\013\371\252\372\336\334!\240\367b){\315\2225\257\374?\261\366", 56, "]U\025\035Q}x\210\277\245\337\232\231\333\370\326R\002\367\301z*0,\206=\275A{\011\317\243F\034&\232BT&\374", 3000, "X\277\340\267#\363\323&\376\305SZ\000gE\345o\254\245P\202{\306\026\263\001\2752\011\237\031\345\1778\202\033\334\003\303R\234Z\370\244\217\352\247\226\0127\134>\267R\236\371\344\361(\332\221\315\371I", 64 },
+ { "`l\246Lx\0077\366bz\205m\211\202M\321\010R\2037\231\324\262`\034i4\306\020\372\232\240.P$\346\001[\035\225\324mh\023\343\344F\3255\004\256@Mvy\331", 56, "zMu\372:U\247\274pU\027\342\003e\227\367'\031;\036:\031Pq\265>,n\200\263\322\343\020z5\357\306\326\2322\251\304\262\357\236<\264\233", 3000, "j\233\005\346w\271\237\255\003\303\210G\024\326\336'U\324x\031\014P\241x\376\215UWh,\246\312n3\355\336\347\365\027\204~P\276\234\277\240(\233`<\377\134\235\306\317\322t8\312L)07\322", 64 },
+ { "h\207V\216\261y\03652<9\227\226&p\356%W\201P7Q\327\364\247F\011\000A\356\231\263$\305\364I\257[\303 \213\352\024oa\276 \340\252\311\364\305\005FD1", 56, "\335\026^\034r\361H1\305`\226r\300\236\216\325'\321\311r\214X}\234^u\326\246|f\034Q\2267[\303J\374\203\320\255BIy@\373\345\214:]EX\262\0339\253", 3000, "E\036*~\347y6)@\261\301\345]\353\273\240B\016\177_\254\357J_\004\366e#\3220\266\306\343\013k\254\230\202\023y\232\335\257PV\227\211C`\364\244f\361Z\367\324\223\321`\024G\3558F", 64 },
+ { "\344u\365\374\030\266/\336\313\036%\372I\235\031=\263S\274\1342X\346\342\034OE\2356\342\320\2411\026\240\361\037\005\026\024\331w\307\017\333\242d\231\243\231/H\242\326%X", 56, "x\372l(*&\215\213\342\307\332\300\230N\016\251\301[(j\007\037\373c\033\225P\255\264\010E%\217\252\344\376s\257\014DZ]J\302\345\212@\364\234\276Z\262>,\336\352*#\352\366\236\351\276\307", 3000, "\203\221Z\042(\206fN:\251B\354\270\033/X\270\247N.\231e\211!\232\244;<I0\325\302\367g#\372c\262\253\014w\205r\203h^\344\221Y\214\177|\350\0002\336\312\311\317\212\241|f\262", 64 },
+ { "\002\350!k\207\037\025\225S\030X\367\326e\306\367\215\310\210\007\177A_\235\230L@\240\0255\350\356\215\332\244\005\236\215\300\376\022\207\232HQ\324\331J!\315# |}J\331", 56, "\307v(*\375\230\257b\236\360\343$\250\207\217\353\346\012X\327\372\036\340\036W\006/\322\221d}=c\010Xkb[@\362\352\266\306\342)\221t=q&C\217vC\317\245\3122\301\346O ~N\301XWf\265Z2\362", 3000, "\372\310\353\350\242\014\235c\016\324!N\375\201|~\310\342J.\260#\026\300P\035\347\011\3375\312\222\333\223\340I\227\351j\200\355+\332\223\012\227\353\302\204\265\372\206\337\300\331A{\241\341\307A\010Z\304", 64 },
+ { "\370\345\2043\373\003\330\362e\024\333\365\220\301\253u\274\272+\241\346m\256S\214\034&23\274\007\223\013KJ\233\037\374\360\327\376c\326\266\012\042\221!\215\205\257\223\261\247\000I", 56, "\260 \215p\2676n\344", 4000, "v\023x\204\270\222'\026\207b\303MY\214\026\362\2718q`\013L\351$4s9\202\031\357\250\261\227Po\376\374\307C\220\247c\234\233\211q\214ZkA\345\354\0126\257F\302\275\011+\275\361\323?", 64 },
+ { " \332\231m\244ZM\200\247\272\252S*\246z\363\203\245\375\312>\346%\016\037H\177\257S\301>f\222\232i\304\324\276\301\340\315\274\215\0334\016\216\335\306\370R\321W\267\220\241", 56, "h,\323\026\342C\263L\032\201\322\314,s\342\275", 4000, "\346\360\263\27733\200,\206\350I\346\223\022\365:{cS\036QA3/K\361\015\321~C\134N\223w+F\227\020\213t\310e8\355\235CJEN\213\036,~0GA\331\301\326t\300a\234@", 64 },
+ { "L\200\264\320\356\273-!-n\350\372\134\316\366\352\011\272\313\246}\2614\134.$\212\227b\244\333M\004kU{\372\177\342\221\227\267\301\245\202\375w\032!\352\177\014u(\354\207", 56, "\361\360\352=\212)6\356\022\223U\036\002o\321\301\246\310\341\340\225\201\341\252", 4000, "O\235\310\355\252X\357\363oKwU\363\212YD@\010\370\343KV\251\304.\376K\2662\255\262C\366\246\321\332\0244)C\316\010\023\042\240j\313p\231\217&\331\371\014\015\221\325\243\311ld8\264\274", 64 },
+ { "Q\357\325\337/\371\325\311\232\367\304Z\340D!\216\222\222+\027\031\306\265\3344i\371\134\312?6\360\300\213\326\017\2600\316\022\202\266\014\012\000<\243\224\200\325\314\340t,\367g", 56, "d\330\321\026/\236J\314\253\311.\012Qo\025\042\344\240\362\324]\330\230\356\205\307%\00267\042\345", 4000, "n \363\265\2479\017\026\366M\036\375\370+A\024\3402\376\221{MY\243\270\264\265\042\304\134O\3327\016\330%wc\013j\016\226Z\317h$\257\351\260\361AM\013\207\0366X\2763\025\212Y\354\310", 64 },
+ { "\372\353S\251N*\232\367m\316PS\270aDC\335\0057\230\276\213\354\347\353K\350\024\215\321\227\300%;\023\377\352'\017Y\177C\017\024\200K\323G\037\307:\224\340^%\341", 56, "\253\254\011\237Q\270,\360\004\332\343\026\314\360\214\227\261}\240\006\300\276\374U\0244e\3052\365\307\250\276\210\263pWU\224\372", 4000, "S\263\011c\272Q\306e\3023j\203>\261\036\274\031U8*}\365\317\253\023\255\030^J\277\331J\340]\367hpR\215\345\316{\042rr\217\236\322tB\317=\215D\274\271\371\357V\2648\236\314K", 64 },
+ { "\242\270$R\263\302\200C \233E\245^\326\265B9/6\274\201\002\223\356\263X\350\354\260\002\026]$5b\042:\257\361\004\224\207\030L\277\247\314\331\032\017n\266aP\364z", 56, "\362&\253\330W'\233\343\317%>-\037\363\223=\300(\336\20214D\240a\224R\321M\013O\221h\203\333T,\302\274\261Uz\325\276\313\346\374\033", 4000, "\204\302-S\030\327`\373\222\224v\027\247`\266\267!\216\237\241l\313\264\313}'E]r`iU\302'\037\024\375\023&i\201\305\341|\3779\305\007L\035\230\355f\357\026`\031,\031\364\3145\032r", 64 },
+ { "\273\3718\272\222\2369\211\313>\0044q\202B_\272?,:\023\313o 9ps\021m\016sc\034\312\341\202\255u\002Z\013T`!1\030>\245\2315H\177\260RJ\356", 56, "\3327\347\342\334\316\323\037n<\265\305\022\026(U\273U\321\226\303\223F\302u\024\212]\022\344\334\232\264\035E\017\254\322.\334\377\333\232\005\331\367tpQ\036\377\341\305Ng[", 4000, "\326\271\016\214\230]\256\374\2779\365\367\251\315\366\330\373<\312iV\334\202\270|\323\326{\316\301K\330\322\342*/$=\222\211\255p\315\341\306\212\011:=\264\201r`Y\201\2273\0016>C\344?\236", 64 },
+ { "7\227m\350\134\343\031f7W\004y\036\335\331Ukv*\021\231\007_8t\346\034\230\310\231\026\201\021\320\254x\200wvx\314;&\230\134\0238\205\246^j\004\240\274.Y", 56, "\201\252\334L\253x\376\302\371\036\331\347?0\006\203\256\023\370\355\360\320\024\312\2233\315\307\254l\325\270<5\010\200>l,\341\305\024\352rF*\250\375\202\341{f\303w'\276\354\347\254W\367SV\243", 4000, "\007\253\010\274\354\376#>\2160\217\243{\006:\323\322\316\042\031\353\272\0423\236\335\0314\376\315\212u\360\037\323c\363i$Kj\242]\377\241\254\004\022Z\304\220\362$4\304\343(\245JI;\032:-", 64 },
+ { "MXy\263-\363Z\231\232\325\335\271\357W\351tO\264\344\224L\367\236j\344f\313z\312\221\327\355B\3645\205L\314\242\200\017\034\217L%R|TC\241\3047Cg7i", 56, "\026\030Fd\201\364\332\313\27178\215[\022(P4\342Yv|\365\021$\371\342\367\037\220\015\331:\036\345\320!\004\3219\010\023\205R\307\232g6\031`\277\376E\335\255\035\3158e\020-\220\374([\253@\340\203T\320T\222", 4000, "\027h(\353S4\375u\200\241]h\361\360\032d\321\241\301Y\036W0\342\355\214h\253D\033r\330\266U\373\260\356\3252\301\254|\030\233\371\351\271\024\205\207\301?\206\206n\341\202\266\360\324o\373|\004", 64 },
+ { "\3277m\327|\262tWVH\216\335G\001\243%\017\322\014\233\370\351e\006\320q\332\366\031N\021T\353j\237\134\371\206\300u\246\343&\020\272[p\251?\012\312\364:\317\342\021", 56, "\212\214\351\233\011\003)\246", 5000, ">\3518\336\250\245\307\302\214\245BYr\177\036,b!E\347\326U6\343\016\373\000C\325\347\367h\000\346w\310\200\042^\276\270%p\260kv\351@\243\026\204\216&q\034K\336\315\332\276\215\352@G", 64 },
+ { "\315\332\254\231\206\366c'\237\317\177\274\326\3667\2059\214\225\255\344\206\372\010;\324[\030_pcwg\211\245g\373]X\3327C\205@6x\352(\322\351@\210e\323\237A", 56, "\327\342Ar\347I\232%-B\371\343=\376*\304", 5000, "\343\315\026K\336]\324\3025\256\241%\210}\360pQkbb\030 SL\037\362\033\223\227c\342\247\004\203\277\334)\212GEoh'\327T\327zM\354.\007[E\035\030@\306\365\344!F\204Q\253", 64 },
+ { "\231\366\007m:\350\322^\246Z:\010%^\223Uc>\203\023\307\213HY\322\336{\275\262\033\203\207\336GBWB\263\346\214eR\354\273cA\342V\303\230\336\224\212\354\230\215", 56, "E\334\345\266\361\317'G\003\260Ur\377\207f%\276[Y\336\270\034\025\263", 5000, "Uz\2652\273=\0142y8\270q\025\016\351!\314\341]\224U\004m\377_\376\0246\363\3174c\334\223Fs,VZ\016\014Xt-z\242i\372\3237\034k\360\303\011\262_2\007j\327$\032J", 64 },
+ { "\315G(\317@x\334\042\352I\265i\264\330\245\234i\007\357Bs\262u\250\275\330\2619\277\004\337\256~T\351:\376\312cD\2108+r\003?\333,>\316\214V\313\315}\024", 56, "\201F\214g\330\034\200\011\016g%i:\334\340)\213\377f>\355\237\344\350*\314D?\376B\031\342", 5000, "\206k\367\203\251N\301q\007V\375J\326\344U.j3\347I{r)\377-h]\134#\275\305\0064c\330~\042\335\207\360\326\333\235e\023|\242\243\242:3\313OZ\240\237\021\344/\317\3008e\217", 64 },
+ { "\222\356l\3701\326\313:\246\207\316k\257\360\365\302 G\011\221!\244W\325\237\225\275\242z*a!\346\001\303\334\224\202P\367\220EF\271]U\265\223\375k\366&\015j?\220", 56, "m52\324\242\260GB\307\210\314=\254=\273\244\271\313;*l\0268\203<\307\315\312\345\324S8\310`u\206#\312CU", 5000, "7\31302\342em\337\264\377\024&i\030x@\331\020\317M\332H\357\243\025\033\215\373V'6C\025\235{\027k\204\234m=#\3557\313\035\313K\3265P\226?q\215\231\004\3079G\2557+\277", 64 },
+ { "\226\373\245\177k\214Mg\014\217m\351x@\230\2028\315y\262^)>\376\002\214-\3059T\005b\300\010i5\302v\211-\215\324\222'\375\321\016\035\325\013b\033\242A\305i", 56, "Ap\266 s\336[SZ9\213>d(\362r(\022g\234\230\024\255)\364\373\215\222=\320\271\344=\012`\207E5\321\372\326\006\203G\334\331\334\033", 5000, "\364\006]2x\254\337\245\262\307S\262\342\024n8\306'\316@\024W\021\023\255\275q\337\354\274\373\337\3661%8\035\210X\350\000\030\233\344\330\223\032T\302*b}\264\020\327T\021=\211\274 C\304\030", 64 },
+ { "z0G\264\233\315\322w\013\330pI\203e\364\345H\0253QO+\233K\247\030\312)X\015\361&\177\011\362w\227%w\026\006\017\303\210\356|\237c\330\217\341\317\022\203\275\355", 56, "\002N90\334\2248\025\372\377\270e\217\177~\022\236\326\204\204\241\027\372\317\341\267E\321{]\035\010K\350\307O\376\357k(\032\353Ss\331+\003\272\227U\020Q\015\3475\211", 5000, "\036gd\256\314\235\332\200\031Mj\321\212\277s\242\212y\260\274\351R\344\306\271\260\251\225X\304\375O\311tD\323\355\354\335\356\371\242\210\372\214\321D\352\275=#\033\330\002wY\037\351W\317g\241P\266", 64 },
+ { "J\315\270(J\265\215\324wO\210\3715\354\340\262\266\275\305\007xC\334a\347\356\011&\3040\220\314d\342IP\364\035\236\200\247\236\213\013\276r\036b\034'\373\253\263\025\270\247", 56, "\357L\364\021\202#\030\377a\303\301\252\345\012\036\322\030\250\334\361L\014EO\253\2407\362[|\360\207)\3719\022\363\023\200\252\036\322\026c\354\351\214\373k\351w\237\237\320\316\247MiI\232L\204\264g", 5000, "\037\300n\247=\220\010\004>\324\254\342o\310\320\305\313u\337\240\023^hu\377\250|\342_\006\017\266\366\353\251\373,\341\332\264-\253\364\265\320\212\021\021\252\035B\252\313\372\214fV\214\2120\202T\224=", 64 },
+ { ".\364\006\326rx9x\200|\254\252\003%\344un3\024v?D\274$\201\256\336\272\357\324\032\236\307\335\320\220=\272\134]\2768\256$\211\270\013\311\314,\013\222c\315\362f", 56, "\370\015s\034K{\276\233R\352\313\025\300*7\010e\0249F\020)\232T_G\037W\226\320\215\301\204\247\266!\010\3077\206,\270\371z\037\215\353\204\331\316t\220\323~\257\036\372`(\305U1\177\205-\350~cf\226\370\322", 5000, "~0-!\013\375'\213\234~\210\307c\206\216\016&\2051\003\201\000\317_\2407\223G'F\222\370-A)j\205Iuc\363H\017\316\255\236t\005\244\273\030\3047\024\376$$\030\331\003\224\332\012\347", 64 },
+ { "\265i\217\357\251$\334\0375\021h\372pW\343\302\015\004\303\330Kt\260\023\217\327\3469\225A\256\236$\246\004\361 \333\033\262%|\262\310\312\211\321\362{\270\207\020]Q\213\247", 56, "\0379\366\2507\005\313e", 6000, "\202\242\214\005`\305n\003\017\252Z\374\202\256\201R?:4\370\220H\042wiM \337\264G:c^\260%@\361\177\033\342\223\356G\2627\252,\201\341;\035\361\015\360\361U\326\212\207\236\017v\342F", 64 },
+ { "8.g=\300]\3713\257:\002\227\211\204\307h\332\007'\304\347\274\271\243|\356pR\203Dg{g\034Y\255\325\031\273\204@\212\224GL\020t\3531\303\231:\042\025\203\371", 56, "c\272\265\314\257\347\241c\3479\007\344\302m\325\240", 6000, "\242\2346\366\377T\302\300\331\244\254~O\336o*\342\215\252\261\252K\031m\356\327\3763\213?\345\020\026\035\234\230\357W\235+\215\026z\274P\256O\2303\203%%2Z\237\220&\027\267\371\203\036\276e", 64 },
+ { "\2669\264\317\377=\221E\245\027\210r\351\2560 ~2\263]S\005\224\300\206\3775\217\226\357K\027\362\320\212e\241&3w{[\344{U\221\330\242\240X\015\312\245\346\225\217", 56, "\024\226\020\220P\001}\367\223|\347h\300!\204\314\366Vo\037U6Q\360", 6000, "{\005\313E\205\0010v\224\343\220\036\002\365a\312\337A\226ws\026\003\333\342e\351\015O\234i=\355\204\271\221\306\023\366\256\375\365e\227>\013\022<\227\264\357\310[\224\305\211 \206\366\372\333e\350>", 64 },
+ { "\341\373\346\376\351}\267\354v\235\244py\242\263\367\364\240\253\027Y\240\233\346t\334r\331\211\033q\302\330[K\374\257\002h\013\007Q\233\272b\233\205zL\353\210\234h\344$\231", 56, "a<\337v>4\001\017Li!\364_\365%\224b\335\340\255}O\321\243\322\033\266\270\371\027\272\220", 6000, "\230w\354\334c\222L\007\012\314\231>\355+?!\216\005V\3243?\014\267A\3444\240\0165[+\242J-\261\214\323\371\007o\356\240\362\007\216\2034%\225V\347RF\275\341P*\034\036\200\257\351s", 64 },
+ { "\005g\321\231 \371\325C}-Q\261\261\263VDt\323\261o\206y\253\235 \272}v\262&Q#\233>*r\335{I\225w\015bt\306\226%f\027\277\350\361\373J\304\277", 56, "jv\354\235Jn\361q<\366\253.1\343\216\323\354K\253\023\2310n\237\352\272t\327hO\211U_\333\375\274\215f\231.", 6000, "(\252\210\221r<A\353\002=\2742\241Y7>\307[{\004V\233\313Ab\022\227F%\2334\231\016 _\307\000`C\012c\303\266\213t\260\203\000N\365\002\372}\217\001\233\226\327K\332\332q_l", 64 },
+ { "\232*y&!\3075^\025\241.\2739{\274\351\310\252\236;\307Hj\002\042#\311\360\251\036\200\250\245\306\012V\377%<\134\370\0221\334n\376,\373\006\375\273\327%S\331\211", 56, "\206\223\357_\031\324\205D\332\203\312\005\304^J4\042lk\323\222\215\235n\331\350 T\260{\236\344,\227\256V\316{\273\034C;\026\343L\031\207\341", 6000, "\213q\256\366\276\227\372\017\204\037#\0034M\233\177\347\250\265B\316xv\373\371\326\260z^r\334g3\244k_\321\243z\220\356\304\026Z)\300\377\323F\302\200\026\265Q\220\376=\025,\211Is\302\264", 64 },
+ { "u\243\362\207\275\261\325\345e\024R\034\212\007\252\352\324\225\024l1h\361\331_PQ\270\030aR'G\314\312e\2649O\362z\030\303\204w|\352\342\317\313^\377>*\256\263", 56, "\355\254\364\372\037\042?\221\206\351\017\326\231\365\326\035\222\214]w\246\227\016/\370\271\312\312<\266'\367H4^3F\006\034\373\353\001\233K\031\310\036\341\316\373Y\0211\201\314u", 6000, "\252u\356\0018\177ui\252x\250\037\343 1\276b\011\327/o7+\204\372)Dp\361\042\267L\375{\330\006\201@\357\261\017\315\223RD\332]\247}\231\134\022i{D,\232\227\010\364\274\355\315Z", 64 },
+ { "\224G\316\306\324\260\011\215\134\312H\020\222'\036.\211{\211c\357.\270J\272u\217\205,\2230G\273\277\214/\026!\227N\212$\211m\313\372\311F9\231\320\210s\372\264\204", 56, "\272\026n\216\027U\033R\203\262\220\250u\214\312\246S?M\204\334>\237M\277\003\035F\317y\022\006\004}\250\233\237i-\233b\011\370C\211\303X\212\003&\265\224L\177\042p\236\3318/\020\271\261?", 6000, "\134`\303\203\364\271\322$|\256\366P\007\202\226\031\220\025\360/\267\324\273\005\356D\015\207~\356\342-\215\212`\004\033R\215\312\273CWY\000\004W\015\320#\245\202\223\327\373\205V9\241\374\034\022\3159", 64 },
+ { "\340\363\363\362\356\304o\2367\377V\014\243\003;\373`%\351\020,H\302\260\232{\260\343\327\340\265\0227A\367\313\221\002\205\242\205G\232*\001\276o\305&b\030m\007\351\027@", 56, "\004\207\302\232q\277;i\254\333!w1\033h\0278\304<\022\035\353\034\010\300\267\232\317\012Pb\207\232\342O\217\005\006\217\273\210iPc\325\252/\003\244\224_!\232\315\266{\023\227E(<\261\024JI8r\210\2710\373Z", 6000, "\334\3669t\257\252\235p\023\211U\354\343\2348\367R\003\274b \134>\313Eh\271i\205\265\000I\354\275\230\017'\271\273J\301\274\233\225xm\222\260\003s'\317\364J\373j\034\014x\250\276\276\376\016", 64 },
+ { "w\240a\223(u\245\026J\256\255\306\377\243k\225\340\221i\216\216\302\262\025\341\270U\305\372j\371\305\254\374\334\3327\037\260\012\322\342t\007\214 UMsD\134\250ZS\373\016", 56, "\374\012\220Y\213\257\2431", 7000, ":\234\204\235\271=\021\351\317\3118\177\035\330\377\03744\241q\343\275mE<\376\302\361M\255X\356\275]U\202\346\364\010R\220\374\3200r\023#\236\214\370\134\340\026\244\370\204\324V\212]TL#\251", 64 },
+ { "5\251\013\363\345(\332(V5\024,\253\357jM%\002fY\334#K4I\234\274s\233\210\217C\334rz+h\036\300t\226\307\207\245\034F*\235\326\330\011\337\226;\006\244", 56, "\207\350\215\302z\042\273.\320\233\020\200\262\021\331;", 7000, "\346\262\214\322\240\221\245s\337\350\3267Ls.\2127Q\272FR\255\366\374\303\025\231\352&\244\220\337\272]\010\037\226g\001\237\235wp\226\205\337v5\263\253\034EU\272\033\234f^]\237\042M\253\321", 64 },
+ { "\343i\022\337+\261\334\250\377\356\317\356Mr\345\361%w\312-bn\326\333\244\177p\301\034\363R/o\305\337w\244\357\233\303\372a\314\3338\3169\011\232\006\317\254\010\035\345G", 56, "\374\240\034\270<\0135\004\003\342X\001\361\307C'\202)\033J\377\231\224z", 7000, "\032J\134\325\346\021T-T\311V\221M\261V\015\2547_\276\042\373X/l\322\321Z\255\371\225\271_\211?\206\316\322\300\015\366\011\366\352\277\215\271c\315\306\134\335\201\031]\353I\332&l\315\302\322U", 64 },
+ { "\361\230\217,A\213\276\331\303XzY\367o}\366v\031q\3678\303$\216\277,\217CA<\320\234\035\211\004\033H5\271a\257c\017\254\367\351\235\031KvH\304c\241r\307", 56, "\272\240\335\247\215\351!B\254\027cB\021\030\233w\330\227\344\010_L\226\271\357\036G\242\322}\012b", 7000, "\371?.\335\334\200\306\013\030\243\232\271\360\252\345\372\305p13c\314\327\363pv\232-\273\334jp\352Y)u\305\260m\252?M\272p\377\257\012\244J\020|\212\357\232\337\026\234\245Z\350U$\305l", 64 },
+ { "\332\245\264\301\004:\003\263\257\003\277\233\207\246\365u\003F3\221;\300{\371\274\012#\242S\017\236[\006\004\327r!\363I\275\365\356\016\215\025\243\341r\201\016\036\207\306kv\021", 56, "H\314\335L'\331N\267[oM\222#x\031S\3512\206K\204$\252B\204\255\363\371s\372\034\257\334\202\003o\211\320\227\005", 7000, "W\004\203\264\313o\310\202\2079\240\353\344\271\2138\034\377\350\002\226\0277#f\224s\226\274G\225\035\214Sda\326\026$k\042u+\272\004(\341\006\343(\365k\315C;gl\331\364ZS\014\022s", 64 },
+ { "#\221p\356\360\223\223'\034\026\030\236\253(\267\033HF\324\300\371\367$\013\210\275\022\252l\203\002\232ojEe\240\204\272\24594\313\241\320\221f\216\022\357\004\320p\252\214Q", 56, "\042\224\324R\270g<z?5\270x\202(\320\245\013\341\247,,Y\264\215\316\265W.\367\317`k\373\206O\341\201<\342\227\273\245\205\300\310A\265'", 7000, "\3255.;\376Z\302\003\350\276!\3163\035\014\012\332\241\277g|\316\232X}\247\242\344\3514\356n\335G\243\207\245f\277\015}\261gg\345\010\270\266\310\344k\377\367\337\014\201g\256\314\324\207s\223\202", 64 },
+ { "~R\300o\037d@.\235\022,\305^\010C\034\207+\200\227\037\015\2074\017O\026z\251(\330c\205\200!AHDI\0017\011\315\315\342~\233\021X\177\263\347\203\325\272\363", 56, "F\375T\324\220|O\214\203q.v\211\265k\356\342\335}\255\236N\031\311^9\250]\334\255+\342\250Fgw\037\343\177\203pW\002\254!kO\3749\027M\256j\0325\225", 7000, "\252\3305\010\001\336\361\236\030\244\244\322\254\204\270\202\263\134Z\042\235\267\220=1\013\300\310\022\215\204}#\007v\303\203\310\310n\322\030\243\2415O\302\006\314\362\271\222\207m\353_\332\366\300\002\221#\323\267", 64 },
+ { "\355S\325\002\033\230\364\010\366\016\351\201\0243\266\235\264f\003\251d>\373r\243\251\005Fl\020G\233='r\255y}\004\246qg\272@pa\253\332[A+\203+\227Q\350", 56, "\367&v\332\237\321\250\311\225\235/W\272S\301\016@\326\302^\213\034\276\235O\324\005ZM~\373\273\274\301k\313W$b\215\307GW\354\023\331\352\256\332\177n\337&\3119,\356\270\042ZJ\016\206\277", 7000, "\215\036n\277\322\242\306\231gd{g\313l\203\014\256\215\335\214\036p\316\327\237\000\000\364)R\222\316\234\030\017\204\344Y*#\230\033\250!Q\275\332'p\343#D4\250.\300\234\271\032\236u\372\3566", 64 },
+ { "\304\313M\022(7c\037\226\260\033\216.g\001\242\2649\367\230\314\317b\005t\341\267#\315:\336\312\261\265\220\015A\2417\002\324\021\323$d\214\257SW\351\336\027\323\367\307\333", 56, "\134]4>Dc\364\251\357\246=;\245\301\335H\001=\247\267t\345\374\013>\340|\340\371\3316q\013C+\356\2779\257\366G\206\346\213\201\254\256>\234\241=\362*\242\303L\207e\341RY\035O\013\266=\233\260\347.7K", 7000, "w\217\204\232\275\237\303G.\343`\214\304\241\322\205\340\342\251)]\306\215\021\031q\373\037U{\037\362\355\343\001A)\337,\222\014\221\231\255\203\272$\365\343\233O\231k\2148&\262u\032\204\326&\001\302", 64 },
+ { "i\344*\341C\203y\320W\001f'3\024\375o\222%\314\224\225\314}\213\200\217\314s\333\243/h/5\210\340M\241:\240\225|,W\313\260\250*?\210\347;\232\210\270\233", 56, "\355\247{\345\203\211\353\330", 8000, "\013\202O0\212\372\013\263\373\377\240\212%\277)\275*\276\021\341a\351_\313\2053\217\012*\210\334\023\232z]<\017-}\313\002pMJ\370^\261\245s\004\342\316\232\250\015F\332 \253\027\313\210%V", 64 },
+ { "\204\312\321\371\353.\037+\256\220Q\266\226\203$a|;\020\262\301\357W\242I\313\201\223\007x2\017\363=\005\212\253\0135\220\002\037\264\0072\251\256)\346\323g\005\255\340\020\241", 56, "\005\233\241\260k\013\253\216fx\234Jq\311%K", 8000, "\277d\331>\026\270R\316\032\001\263`\347\017w[\020\201\020\021hI\007n\353\256j*\303\2069\016\335b\236\376%r\250\200\212\203\004\236{\350\326L\232hvu\234\232\376\134\267h\246\312\304l\326\332", 64 },
+ { "\026+7)l@\230\345\361\206\201\314A`\274\231\370\365\300\033 \367\273\324\226.XZ\003\206!U\3673\371\205\042\324\314\331\340D\350*BoD\357\357W\233\036\243\230\006s", 56, "!\272B\012\3167\023\1341\240\341b\270\301\356\333\350|\252J\364\323\204\031", 8000, "\3419L\027\255\212\337\020P\242\036?\037_\306 m\257\243\017\042j\224\036\365[0}>\226\0314\372\236\343\331d\336\3056D\327nP\310\347\024\031\005*U\025X\004x\332x\240\032\003\004Q\250\236", 64 },
+ { "\005\327.\210\317\303\217\314\266\376Z\216\271\252V&\314\021\307bb\010\026\315\004\327\370\015\362A\277\317\307\202\014\035\315\2031\303$\341\275\241\3575n(\357\271\355z\312R\233\361", 56, "\206\306%\304F\216\250\010\260\330\263'o\266\221\373\012I7\311\357\261\273\2506~\217l\246X\372\277", 8000, "\365\200\304y\350\342\307J\247\222M\210\251c+\345\006\230\221h\226\247\373L\212\230\375\243G\224\232\021\022j\261\243\255\230\011\201.s`\325\003\032\237\305\247H},Tu\376,(\310\365\177\007\376\001\273", 64 },
+ { "\343\204\321&\260i\336\353\031\313\254j\371#\035\010\304\330\004\331\227[\206\357d\177\205\036\371\267\214\376g\254\215\321i\031\312nR\363Iml\363\323\343\320d\260\343q\375\015}", 56, "k\213\314\005\244\252\010\220A\232`*v\276#\376\206\201\344\331\363&\276s\353F|\2328\203\2217\203\020\256\2654\222\024M", 8000, " [Wz\347\360`\373\002\177\270KD\001\337\200\322\351uWf\264\331\005\276}1;\252\325\000\225\233\236\260\3426\024t\221x\226\024\134\313S\375zf\240\221\202\361\010\367\007?\242\240\372\320\003\354\322", 64 },
+ { "\273\306\042\266\301\276\015\366\257V?Za\314\002\353\015\227\312\034\250\344`_[A\371\246\276\362t\023i\361(\363@ f\024M>_\007\272.\225\026\271\005T\042\004\021~\345", 56, "\004o^\305\371\003\222\212\020\013/,\263@A\363\345\010I\373\374\244UU\206 \306M\042o3\216\375d\003H2\034\210}U\015P\221yPa\234", 8000, "\274\240\337\035\373\013\333-\251\025n\350<3\352\010\002\331\257K\307\015vW\177\033N]\020J\200\215\226\320M5\317\030\353\0259\302\344\262R]EI\252\301J\266(S\345\351]Z\206bS\011\024\251", 64 },
+ { "\303\311}s\360\326\304\311\252\003\356\334\004,T\025x\011\304l\260\357\243QW\014\330\231 F\343\177_2\323\0069\035\003\357\221(!\214\032\3234\016\216]f\374H\254\250}", 56, "\3039\277\272\024\260z\270b\325\027\026j\255\233\221\005c\234\311\217<\030\334\134n\030\314O\263\206\305\370j\025\247\337\347\357\376\216o\243\017\255\007J\037\306\375Q\032\370NA\344", 8000, " \301\0206&\260h\2467\031\331\261 \357\331@\205R<f\312\223\213#!\015tF\221^`*&\247cH\237\2449\303J*z|\306\007\360V}\256\002`$\263\331\025Z\022%(Wt\320\016", 64 },
+ { "G8e\021\244L\350ju\373R\2218\240\274\015}\246~\343gIV\177\362\0013\366l\305\216\364\225N(H\311\303\005\001\015,n)\310\024\363X}EH\007\216\232-\246", 56, "\005\366\311\342\271\275\331I]\026\307\036\245-\340\366\213J\254\3375\226\305\246\315\376\343+U\360\236\231\015E\260\256\241\354\371\204y\223\210&(4'\256D],e\213\350\302\026\224\017\235\006\025\277-\316", 8000, "\251\015\235R?\347\252\222}\207\257\225\233\314\3006\330\215\345\331\001\330<\216\263\326xw\225\274\023\216\214\352\270\363\312\276\352A\324Q\010\265\272\033\006L\0016\221\200\340O\006Y\345q2\241*7\256\220", 64 },
+ { "S\322s\224\035\253\236\346\253\303\0233\212wR\257\216e\263\353LU=v4X\365p\314\261\274\225|\014\223\022>\267\302\356\247\372T\336\000\322\034H.\276S\217\010\014\1341", 56, "\372\0219I\011a\260\233%\216\365\330Z n\326\235U\347q\022F\336\320\221\342\002\250\357\240{\355\271:\306\200\267\210M\330L?\336\013\364G=\316!:~'\321\361\233\220\2210'\247|GB&?\334M\022\334\211\031Z", 8000, "\373\274\245a\037\033!Dp]\3008qd_\324\230\200\320R\360\243[-\242_\342\365\023\021!\033\346\347\345\232\203\365\007\215r\324XG\274\322\310\214\336\334\211\245\257\355\226\201Z\323\3741\337 \237@", 64 },
+ { "\370\221a9\304\262cxsb\235\211j\215\035\007\367\226\360\207XE\335\321y\216.\373X\005\364_-H.\271\037\033\354\276\2456:#\334`\235j\363\342h\227\353\373[\254", 56, "s\321\003c\253\276\2318", 9000, "G\032f_\366\311\216\232\014\241\207J]\022\006kT\322\317Z/%J\346\333Rfv&\025y\201\257\262\327R\236\312\235\317*\212\210\245y\330\244\227\233\014m\344c\356\332\314_V\0037\347\206\012\224", 64 },
+ { "\201\277`\274\035\031Q\005\203\236\273\347\220\035\376#\340\367W\371v[[]\337s\207\265\363\035o\317Vp\344-\302o]\365\236u|Q+\313\211\231^\015\275\302\355\025\012\276", 56, "bY\344y\232N\353{\216\223\325V\377k\015\253", 9000, "\017Qz\026\032\317\311\344\330\216\374\250\300*\025\247\003\001\000\245\212\310sh:\030\243\030\222\004$\261\000\345\253,.]\360\337\035\374\242(\322G\177\345\357\274|)oyC\224\303\330\351a\344D\031\203", 64 },
+ { "\305V\021]v\311\360M+\335\017\306\216.\271\223#u\270OyKv\223\272\257W\300;\036\022\317\246&\250Vbb\253\2122\225\300\360\266j\203J\251\314g\220\206gh\222", 56, "C\377\366\0061I|\266\311\017.\363B/\353Dr\347\231\237\301\342\323\302", 9000, "?\033~\211\011\320U\277}ye\350\012H\024\340*h\203\015\374\244-\324q4mR\207n\321\3211\213\260\311\023/\215\013\374\311\026\256\304O\200\177\375\272\021\214\227\005X2\355\330\207\276n\244<\271", 64 },
+ { "r+^\375\266{\207\022v/\232\354\272P\265P\035;\264\325.m`\017o\334\333\226\344\017\375y\376E\321\002?\134\372\036$!\035\331\341\224A=\272;)\277\374\361\3567", 56, "b\254\231v\245\267\217\012\314\266g\273\016\356\241\326N\370\343\360/(\256;\321\030G\376g\2359\007", 9000, "\023\243\234BS\134|\237\3667\244\015\265\022V\002\022\000\343z9g\230\024\331\367\015\012\253\305Z\204\220\333\255\340\027\273\260we\030\037Yq'\037\000h\361\374\207\363\204\276\210\352\343\237\225v\020rv", 64 },
+ { "\027\222\301V4\241\222\007\360uI\254\010\331xKUH7\005y\274)\241#h\340\351\346z\225\325\333/\357\354k5Nh\212O\345\215\3552OT\302M\351q\307k\272\003", 56, "\251\362\353\331h\224\336\274W\373]\345#t+\011\300\316\241%\351\232I\220u\277\037\020W\205- \253=\317\304Qgk\354", 9000, ":\356\247\042\033t\204R\017-\263\025@7\201M6\035\021\002.\031h\002\011@\307\247\033L3\227\311E_\271\232\305\224\020\3421A\000H\355\276\011\321\042Y\333=2\325l\361>\315\323\267\3218^", 64 },
+ { "L\244\250\363\025\235\235\255\3260\225\365\243\352\213>\241\320\374\321\361&,\371:5\200<#]\371\042\213\300\266\026\240I\213\320\215\224\340\030$9\212T\212GM\331\004\006\345\252", 56, "xU\307)\357\312eO-\325\035\206\250\257\222x\010K(\375\343\032:\033?\351\200\004\221\322\011\024\270\367\212\001)M\247j\015\240\234\2002Y,\265", 9000, "\034\034%B\001tR\035$c\227\2621\004K\323\375N\200,Y\220@\212\372E\325\353?\343]R'~\364\305\236)\255\255\227\211\244\221\210\345\366\026\357\321\307\372\224\001\347\253\351\207\300\257\3543u\300", 64 },
+ { "\312`\215\377\377\310\2070\277u \261;l\210-j\210\314\315\213]\335)D\375\273\345\026R\361\250o#\377\303E\030\373e\226ts]\256\177V\335\234\215\237\241d$\327\222", 56, "\347\305\001\344\333E\371{1/\335\261_\226:>U\241v~\364\216\220!\242\367\340g`M&\2664=Fj+\321f>\220\2262\300~\376\201{\341\002\3340\334\353\307\320", 9000, "\027\247P\003\356C\302\177\266j\361\206\263\033\246\011\300W2n\315\227\346\242\2635\226\233P\236B#\351\027\221a\3401$\211\033\342\225\320\241\202\005\373\272\301\342\177G\335`4\233\345z0\2748\230L", 64 },
+ { "\346\322=WAC\245\211P\230\220\321`\342\223\247\231\376\006\341\300\265\266-\335@\201\362GRT\360\214\030\247\134\024r\324\3549\254\330[j*\226\234\223\210\314\024\272!\366C", 56, "Y\212p\006\011X\241\036_B\261\231\267\205P\302\2753\212|\263\307gv\235\314+y\317\217%\243\373\3167\354\213G>\360\212xj\134\362}\306#\260\337\244\177\310\365K\375\036\002\036x\025*\374\367", 9000, "^\235\352\306\347\325!\351L8\006\321\215\263\002\366\322\031\314\224\240 P\2427\010@\325\360\003\231\324\264+\357\2474\372\347(t\042)v/\331%\3149\000$9\326\276\233,5\322\017\134\324\022\205J", 64 },
+ { "u4J\310\363\243\365\026\024j\324\372\221U\200\235\246\214\236L\300I\270\240w\274#P%\003\323\213\337t\331S^$6\230\374\005yX\374\231\035\225\025?\330\015\015\313P\271", 56, "\036\017\3424\334d\301\015\373x\265\231\300u\306\301d\332\320\350^\011DI\015\033\372\027\352\004\271H%~^W\275\367.\251\206\240\012s\211\211wkK\231\203.\204\021\364,\207\023Z\225\015c5\036\232\030\217\034\027\021\036\042", 9000, "\316\333\210\302\331v\257J\252\034;\332;\344\350\374\266\332\210?7<\307\227\211\342c\016\010\026\367cS\346\370!\266\257\031\341\337\243y\004(\307\0169\301a\313\216\327_\321\261\017\241\021\276\002\240\020|", 64 },
diff --git a/tests/sys/geom/class/eli/unaligned_io.c b/tests/sys/geom/class/eli/unaligned_io.c
new file mode 100644
index 000000000000..707d15f40be4
--- /dev/null
+++ b/tests/sys/geom/class/eli/unaligned_io.c
@@ -0,0 +1,131 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 The FreeBSD Foundation
+ *
+ * This software was developed by Mark Johnston under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Idea from a test case by Andrew "RhodiumToad" Gierth in Bugzilla PR 271766.
+ */
+
+#include <sys/param.h>
+#include <sys/disk.h>
+#include <sys/mman.h>
+
+#include <crypto/cryptodev.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int
+main(int argc, char **argv)
+{
+ const char *disk;
+ char *buf1, *buf2;
+ off_t disksz;
+ size_t bufsz, iosz;
+ ssize_t n;
+ unsigned int offsets, secsz;
+ int fd;
+
+ if (argc != 2)
+ errx(1, "Usage: %s <disk>", argv[0]);
+ disk = argv[1];
+
+ fd = open(disk, O_RDWR);
+ if (fd < 0)
+ err(1, "open(%s)", disk);
+
+ if (ioctl(fd, DIOCGSECTORSIZE, &secsz) != 0)
+ err(1, "ioctl(DIOCGSECTORSIZE)");
+ if (secsz == 0)
+ errx(1, "ioctl(DIOCGSECTORSIZE) returned 0");
+ if (ioctl(fd, DIOCGMEDIASIZE, &disksz) != 0)
+ err(1, "ioctl(DIOCGMEDIASIZE)");
+ if (disksz / secsz < 2)
+ errx(1, "disk needs to be at least 2 sectors in size");
+ iosz = 2 * secsz;
+
+ bufsz = iosz + secsz;
+ buf1 = mmap(NULL, bufsz, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,
+ -1, 0);
+ if (buf1 == MAP_FAILED)
+ err(1, "mmap");
+ buf2 = mmap(NULL, bufsz, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,
+ -1, 0);
+ if (buf2 == MAP_FAILED)
+ err(1, "mmap");
+
+ arc4random_buf(buf1, bufsz);
+ n = pwrite(fd, buf1, bufsz, 0);
+ if (n < 0 || (size_t)n != bufsz)
+ err(1, "pwrite");
+
+ /*
+ * Limit the number of offsets we test with, to avoid spending too much
+ * time when the sector size is large.
+ */
+ offsets = MAX(EALG_MAX_BLOCK_LEN, HMAC_MAX_BLOCK_LEN) + 1;
+
+ /*
+ * Read test: read the first 2 sectors into buf1, then do the same with
+ * buf2, except at varying offsets into buf2. After each read, compare
+ * the buffers and make sure they're identical. This exercises corner
+ * cases in the crypto layer's buffer handling.
+ */
+ n = pread(fd, buf1, iosz, 0);
+ if (n < 0 || (size_t)n != iosz)
+ err(1, "pread");
+ for (unsigned int i = 0; i < offsets; i++) {
+ n = pread(fd, buf2 + i, iosz, 0);
+ if (n < 0 || (size_t)n != iosz)
+ err(1, "pread");
+ if (memcmp(buf1, buf2 + i, iosz) != 0)
+ errx(1, "read mismatch at offset %u/%u", i, secsz);
+ }
+
+ /*
+ * Write test. Try writing buffers at various alignments, and verify
+ * that we read back what we wrote.
+ */
+ arc4random_buf(buf1, bufsz);
+ for (unsigned int i = 0; i < offsets; i++) {
+ n = pwrite(fd, buf1 + i, iosz, 0);
+ if (n < 0 || (size_t)n != iosz)
+ err(1, "pwrite");
+ n = pread(fd, buf2, iosz, 0);
+ if (n < 0 || (size_t)n != iosz)
+ err(1, "pread");
+ if (memcmp(buf1 + i, buf2, iosz) != 0)
+ errx(1, "write mismatch at offset %u/%u", i, secsz);
+ }
+
+ return (0);
+}
diff --git a/tests/sys/geom/class/gate/Makefile b/tests/sys/geom/class/gate/Makefile
new file mode 100644
index 000000000000..5ebc1d9304e0
--- /dev/null
+++ b/tests/sys/geom/class/gate/Makefile
@@ -0,0 +1,10 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/geom/class/${.CURDIR:T}
+
+ATF_TESTS_SH+= ggate_test
+
+# Tests listen on the same port.
+TEST_METADATA.ggate_test+= is_exclusive=true
+
+.include <bsd.test.mk>
diff --git a/tests/sys/geom/class/gate/Makefile.depend b/tests/sys/geom/class/gate/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/geom/class/gate/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/geom/class/gate/ggate_test.sh b/tests/sys/geom/class/gate/ggate_test.sh
new file mode 100644
index 000000000000..4cbc5d80ae89
--- /dev/null
+++ b/tests/sys/geom/class/gate/ggate_test.sh
@@ -0,0 +1,267 @@
+PIDFILE=ggated.pid
+PLAINFILES=plainfiles
+CONF=gg.exports
+
+atf_test_case ggatec_trim cleanup
+ggatec_trim_head()
+{
+ atf_set "descr" "ggatec survives a trim"
+ atf_set "require.progs" "ggatec"
+ atf_set "require.user" "root"
+ atf_set "timeout" 60
+}
+
+ggatec_trim_body()
+{
+ load_ggate
+
+ port=33080
+ us=$(alloc_ggate_dev)
+ work=$(alloc_md)
+ atf_check -e ignore -o ignore dd if=/dev/random of=/dev/$work bs=1m count=1 conv=notrunc
+ echo $CONF >> $PLAINFILES
+ echo "localhost RW /dev/$work" > $CONF
+ atf_check ggated -p $port -F $PIDFILE $CONF
+ atf_check ggatec create -p $port -u $us localhost /dev/$work
+ ggate_dev=/dev/ggate${us}
+ wait_for_ggate_device ${ggate_dev}
+
+ # ggatec only supports read or write.
+ atf_check -s not-exit:0 -e ignore -o ignore trim -q -f ${ggate_dev}
+}
+
+ggatec_trim_cleanup()
+{
+ common_cleanup
+}
+
+
+atf_test_case ggated cleanup
+ggated_head()
+{
+ atf_set "descr" "ggated can proxy geoms"
+ atf_set "require.progs" "ggatec ggated"
+ atf_set "require.user" "root"
+ atf_set "timeout" 60
+}
+
+ggated_body()
+{
+ if [ "$(atf_config_get ci false)" = "true" ] && \
+ [ "$(uname -p)" = "i386" ]; then
+ atf_skip "https://bugs.freebsd.org/244737"
+ fi
+
+ load_ggate
+
+ port=33081
+ us=$(alloc_ggate_dev)
+ work=$(alloc_md)
+ src=$(alloc_md)
+
+ atf_check -e ignore -o ignore \
+ dd if=/dev/random of=/dev/$work bs=1m count=1 conv=notrunc
+ atf_check -e ignore -o ignore \
+ dd if=/dev/random of=/dev/$src bs=1m count=1 conv=notrunc
+
+ echo $CONF >> $PLAINFILES
+ echo "127.0.0.1 RW /dev/$work" > $CONF
+
+ atf_check ggated -p $port -F $PIDFILE $CONF
+ atf_check ggatec create -p $port -u $us 127.0.0.1 /dev/$work
+
+ ggate_dev=/dev/ggate${us}
+
+ wait_for_ggate_device ${ggate_dev}
+
+ atf_check -e ignore -o ignore \
+ dd if=/dev/${src} of=${ggate_dev} bs=1m count=1 conv=notrunc
+
+ checksum /dev/$src /dev/$work
+}
+
+ggated_cleanup()
+{
+ common_cleanup
+}
+
+atf_test_case ggatel_file cleanup
+ggatel_file_head()
+{
+ atf_set "descr" "ggatel can proxy files"
+ atf_set "require.progs" "ggatel"
+ atf_set "require.user" "root"
+ atf_set "timeout" 15
+}
+
+ggatel_file_body()
+{
+ load_ggate
+
+ us=$(alloc_ggate_dev)
+
+ echo src work >> ${PLAINFILES}
+ dd if=/dev/random of=work bs=1m count=1
+ dd if=/dev/random of=src bs=1m count=1
+
+ atf_check ggatel create -u $us work
+
+ ggate_dev=/dev/ggate${us}
+
+ wait_for_ggate_device ${ggate_dev}
+
+ atf_check -e ignore -o ignore \
+ dd if=src of=${ggate_dev} bs=1m count=1 conv=notrunc
+
+ checksum src work
+}
+
+ggatel_file_cleanup()
+{
+ common_cleanup
+}
+
+atf_test_case ggatel_md cleanup
+ggatel_md_head()
+{
+ atf_set "descr" "ggatel can proxy files"
+ atf_set "require.progs" "ggatel"
+ atf_set "require.user" "root"
+ atf_set "timeout" 15
+}
+
+ggatel_md_body()
+{
+ load_ggate
+
+ us=$(alloc_ggate_dev)
+ work=$(alloc_md)
+ src=$(alloc_md)
+
+ atf_check -e ignore -o ignore \
+ dd if=/dev/random of=$work bs=1m count=1 conv=notrunc
+ atf_check -e ignore -o ignore \
+ dd if=/dev/random of=$src bs=1m count=1 conv=notrunc
+
+ atf_check ggatel create -u $us /dev/$work
+
+ ggate_dev=/dev/ggate${us}
+
+ wait_for_ggate_device ${ggate_dev}
+
+ atf_check -e ignore -o ignore \
+ dd if=/dev/$src of=${ggate_dev} bs=1m count=1 conv=notrunc
+
+ checksum /dev/$src /dev/$work
+}
+
+ggatel_md_cleanup()
+{
+ common_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case ggated
+ atf_add_test_case ggatel_file
+ atf_add_test_case ggatel_md
+ atf_add_test_case ggatec_trim
+}
+
+alloc_ggate_dev()
+{
+ local us
+
+ us=0
+ while [ -c /dev/ggate${us} ]; do
+ : $(( us += 1 ))
+ done
+ echo ${us} > ggate.devs
+ echo ${us}
+}
+
+alloc_md()
+{
+ local md
+
+ md=$(mdconfig -a -t malloc -s 1M) || \
+ atf_fail "failed to allocate md device"
+ echo ${md} >> md.devs
+ echo ${md}
+}
+
+checksum()
+{
+ local src work
+ src=$1
+ work=$2
+
+ src_checksum=$(md5 -q $src)
+ work_checksum=$(md5 -q $work)
+
+ if [ "$work_checksum" != "$src_checksum" ]; then
+ atf_fail "work md5 checksum didn't match"
+ fi
+
+ ggate_checksum=$(md5 -q /dev/ggate${us})
+ if [ "$ggate_checksum" != "$src_checksum" ]; then
+ atf_fail "ggate md5 checksum didn't match"
+ fi
+}
+
+common_cleanup()
+{
+ if [ -f "ggate.devs" ]; then
+ while read test_ggate; do
+ ggatec destroy -f -u $test_ggate >/dev/null
+ done < ggate.devs
+ rm ggate.devs
+ fi
+
+ if [ -f "$PIDFILE" ]; then
+ pkill -F "$PIDFILE"
+ rm $PIDFILE
+ fi
+
+ if [ -f "PLAINFILES" ]; then
+ while read f; do
+ rm -f ${f}
+ done < ${PLAINFILES}
+ rm ${PLAINFILES}
+ fi
+
+ if [ -f "md.devs" ]; then
+ while read test_md; do
+ # ggatec destroy doesn't release the provider
+ # synchronously, so we may need to retry destroying it.
+ while ! mdconfig -d -u $test_md; do
+ sleep 0.1
+ done
+ done < md.devs
+ rm md.devs
+ fi
+ true
+}
+
+load_ggate()
+{
+ local class=gate
+
+ # If the geom class isn't already loaded, try loading it.
+ if ! kldstat -q -m g_${class}; then
+ if ! geom ${class} load; then
+ atf_skip "could not load module for geom class=${class}"
+ fi
+ fi
+}
+
+# Bug 204616: ggatel(8) creates /dev/ggate* asynchronously if `ggatel create`
+# isn't called with `-v`.
+wait_for_ggate_device()
+{
+ ggate_device=$1
+
+ while [ ! -c $ggate_device ]; do
+ sleep 0.5
+ done
+}
diff --git a/tests/sys/geom/class/geom_subr.sh b/tests/sys/geom/class/geom_subr.sh
new file mode 100644
index 000000000000..8e3b12d5f620
--- /dev/null
+++ b/tests/sys/geom/class/geom_subr.sh
@@ -0,0 +1,83 @@
+#!/bin/sh
+
+TEST_MDS_FILE="${TMPDIR}/test_mds.$(basename $0)"
+
+devwait()
+{
+ while :; do
+ if [ -c /dev/${class}/${name} ]; then
+ return
+ fi
+ sleep 0.2
+ done
+}
+
+attach_md()
+{
+ local _md
+ local rv=$1
+ shift
+
+ [ -c /dev/mdctl ] || atf_skip "no /dev/mdctl to create md devices"
+ _md=$(mdconfig -a "$@") || exit
+ echo $_md >> $TEST_MDS_FILE || exit
+ eval "${rv}='${_md}'"
+}
+
+detach_md()
+{
+ local test_md unit
+
+ test_md=$1
+ unit=${test_md#md}
+ mdconfig -d -u $unit || exit
+ sed -i '' "/^${test_md}$/d" $TEST_MDS_FILE || exit
+}
+
+geom_test_cleanup()
+{
+ local test_md
+
+ if [ -f "$TEST_MDS_FILE" ]; then
+ while read test_md; do
+ # The "#" tells the TAP parser this is a comment
+ echo "# Removing test memory disk: $test_md"
+ mdconfig -d -u $test_md
+ done < $TEST_MDS_FILE
+ rm -f "$TEST_MDS_FILE"
+ fi
+}
+
+geom_load_class_if_needed()
+{
+ local class=$1
+
+ # If the geom class isn't already loaded, try loading it.
+ if ! kldstat -q -m g_${class}; then
+ if ! geom ${class} load; then
+ echo "could not load module for geom class=${class}"
+ return 1
+ fi
+ fi
+ return 0
+}
+
+geom_atf_test_setup()
+{
+ if ! error_message=$(geom_load_class_if_needed $class); then
+ atf_skip "$error_message"
+ fi
+}
+
+geom_tap_test_setup()
+{
+ if ! error_message=$(geom_load_class_if_needed $class); then
+ echo "1..0 # SKIP $error_message"
+ exit 0
+ fi
+}
+
+: ${ATF_TEST=false}
+if ! $ATF_TEST; then
+ geom_tap_test_setup
+fi
diff --git a/tests/sys/geom/class/mirror/10_test.sh b/tests/sys/geom/class/mirror/10_test.sh
new file mode 100644
index 000000000000..351689d1e185
--- /dev/null
+++ b/tests/sys/geom/class/mirror/10_test.sh
@@ -0,0 +1,68 @@
+#!/bin/sh
+
+# Test handling of read errors.
+
+. $(dirname $0)/conf.sh
+
+echo 1..3
+
+set -e
+
+ddbs=2048
+regreadfp="debug.fail_point.g_mirror_regular_request_read"
+m1=$(mktemp $base.XXXXXX)
+m2=$(mktemp $base.XXXXXX)
+
+dd if=/dev/random of=$m1 bs=$ddbs count=1024 >/dev/null 2>&1
+dd if=/dev/zero of=$m2 bs=$ddbs count=1024 >/dev/null 2>&1
+
+attach_md us0 -t vnode -f $m1
+attach_md us1 -t vnode -f $m2
+
+gmirror label $name /dev/$us0
+gmirror insert $name /dev/$us1
+devwait
+syncwait
+
+tmp1=$(mktemp $base.XXXXXX)
+tmp2=$(mktemp $base.XXXXXX)
+
+EIO=5
+# gmirror should retry a failed read from the other mirror.
+sysctl ${regreadfp}="1*return(${EIO})[pid $(gmirror_worker_pid)]"
+dd if=/dev/mirror/$name of=$tmp1 iseek=256 bs=$ddbs count=1 >/dev/null 2>&1
+dd if=/dev/$us1 of=$tmp2 iseek=256 bs=$ddbs count=1 >/dev/null 2>&1
+sysctl ${regreadfp}='off'
+
+if cmp -s $tmp1 $tmp2; then
+ echo "ok 1"
+else
+ echo "not ok 1"
+fi
+
+# Make sure that one of the mirrors was marked broken.
+genid1=$(gmirror dump /dev/$us0 | awk '/^[[:space:]]*genid: /{print $2}')
+genid2=$(gmirror dump /dev/$us1 | awk '/^[[:space:]]*genid: /{print $2}')
+if [ $genid1 -eq $(($genid2 + 1)) -o $genid2 -eq $(($genid1 + 1)) ]; then
+ echo "ok 2"
+else
+ echo "not ok 2"
+fi
+
+# Force a retaste of the disconnected component.
+if [ $(gmirror status -s $name | awk '{print $3}') = $us0 ]; then
+ detach_md $us1
+ attach_md us1 -t vnode -f $m2
+else
+ detach_md $us0
+ attach_md us0 -t vnode -f $m1
+fi
+
+# Make sure that the component wasn't re-added to the gmirror.
+if [ $(gmirror status -s $name | wc -l) -eq 1 ]; then
+ echo "ok 3"
+else
+ echo "not ok 3"
+fi
+
+rm -f $m1 $m2 $tmp1 $tmp2
diff --git a/tests/sys/geom/class/mirror/11_test.sh b/tests/sys/geom/class/mirror/11_test.sh
new file mode 100644
index 000000000000..0e498ae03d15
--- /dev/null
+++ b/tests/sys/geom/class/mirror/11_test.sh
@@ -0,0 +1,83 @@
+#!/bin/sh
+
+# Test handling of read errors.
+
+. $(dirname $0)/conf.sh
+
+echo 1..4
+
+set -e
+
+ddbs=2048
+regreadfp="debug.fail_point.g_mirror_regular_request_read"
+m1=$(mktemp $base.XXXXXX)
+m2=$(mktemp $base.XXXXXX)
+
+dd if=/dev/random of=$m1 bs=$ddbs count=1024 >/dev/null 2>&1
+dd if=/dev/zero of=$m2 bs=$ddbs count=1024 >/dev/null 2>&1
+
+attach_md us0 -t vnode -f $m1
+attach_md us1 -t vnode -f $m2
+
+gmirror label $name /dev/$us0
+gmirror insert $name /dev/$us1
+devwait
+syncwait
+
+tmp1=$(mktemp $base.XXXXXX)
+tmp2=$(mktemp $base.XXXXXX)
+
+ENXIO=6
+# gmirror has special handling for ENXIO. It does not mark the failed component
+# as broken, allowing it to rejoin the mirror automatically when it appears.
+sysctl ${regreadfp}="1*return(${ENXIO})[pid $(gmirror_worker_pid)]"
+dd if=/dev/mirror/$name of=$tmp1 iseek=512 bs=$ddbs count=1 >/dev/null 2>&1
+dd if=/dev/$us1 of=$tmp2 iseek=512 bs=$ddbs count=1 >/dev/null 2>&1
+sysctl ${regreadfp}='off'
+
+if cmp -s $tmp1 $tmp2; then
+ echo "ok 1"
+else
+ echo "not ok 1"
+fi
+
+# Verify that the genids still match after ENXIO.
+genid1=$(gmirror dump /dev/$us0 | awk '/^[[:space:]]*genid: /{print $2}')
+genid2=$(gmirror dump /dev/$us1 | awk '/^[[:space:]]*genid: /{print $2}')
+if [ $genid1 -eq $genid2 ]; then
+ echo "ok 2"
+else
+ echo "not ok 2"
+fi
+
+# Trigger a syncid bump.
+dd if=/dev/zero of=/dev/mirror/$name bs=$ddbs count=1 >/dev/null 2>&1
+
+# The ENXIO+write should have caused a syncid bump.
+syncid1=$(gmirror dump /dev/$us0 | awk '/^[[:space:]]*syncid: /{print $2}')
+syncid2=$(gmirror dump /dev/$us1 | awk '/^[[:space:]]*syncid: /{print $2}')
+if [ $syncid1 -eq $(($syncid2 + 1)) -o $syncid2 -eq $(($syncid1 + 1)) ]; then
+ echo "ok 3"
+else
+ echo "not ok 3"
+fi
+
+# Force a retaste of the disconnected component.
+if [ $(gmirror status -s $name | awk '{print $3}') = $us0 ]; then
+ detach_md $us1
+ attach_md us1 -t vnode -f $m2
+else
+ detach_md $us0
+ attach_md us0 -t vnode -f $m1
+fi
+
+# Make sure that the retaste caused the mirror to automatically be re-added.
+if [ $(gmirror status -s $name | wc -l) -eq 2 ]; then
+ echo "ok 4"
+else
+ echo "not ok 4"
+fi
+
+syncwait
+
+rm -f $m1 $m2 $tmp1 $tmp2
diff --git a/tests/sys/geom/class/mirror/12_test.sh b/tests/sys/geom/class/mirror/12_test.sh
new file mode 100644
index 000000000000..4f24dd081fef
--- /dev/null
+++ b/tests/sys/geom/class/mirror/12_test.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+# Test handling of write errors.
+
+. $(dirname $0)/conf.sh
+
+echo 1..3
+
+set -e
+
+ddbs=2048
+regwritefp="debug.fail_point.g_mirror_regular_request_write"
+m1=$(mktemp $base.XXXXXX)
+m2=$(mktemp $base.XXXXXX)
+
+dd if=/dev/zero of=$m1 bs=$ddbs count=1024 >/dev/null 2>&1
+dd if=/dev/zero of=$m2 bs=$ddbs count=1024 >/dev/null 2>&1
+
+attach_md us0 -t vnode -f $m1
+attach_md us1 -t vnode -f $m2
+
+gmirror label $name /dev/$us0 /dev/$us1
+devwait
+
+tmp1=$(mktemp $base.XXXXXX)
+tmp2=$(mktemp $base.XXXXXX)
+dd if=/dev/random of=$tmp1 bs=$ddbs count=1 >/dev/null 2>&1
+
+EIO=5
+# gmirror should kick one of the mirrors out after hitting EIO.
+sysctl ${regwritefp}="1*return(${EIO})[pid $(gmirror_worker_pid)]"
+dd if=$tmp1 of=/dev/mirror/$name bs=$ddbs count=1 >/dev/null 2>&1
+dd if=/dev/mirror/$name of=$tmp2 bs=$ddbs count=1 >/dev/null 2>&1
+sysctl ${regwritefp}='off'
+
+if cmp -s $tmp1 $tmp2; then
+ echo "ok 1"
+else
+ echo "not ok 1"
+fi
+
+# Make sure that one of the mirrors was marked broken.
+genid1=$(gmirror dump /dev/$us0 | awk '/^[[:space:]]*genid: /{print $2}')
+genid2=$(gmirror dump /dev/$us1 | awk '/^[[:space:]]*genid: /{print $2}')
+if [ $genid1 -eq $(($genid2 + 1)) -o $genid2 -eq $(($genid1 + 1)) ]; then
+ echo "ok 2"
+else
+ echo "not ok 2"
+fi
+
+# Force a retaste of the disconnected component.
+if [ $(gmirror status -s $name | awk '{print $3}') = $us0 ]; then
+ detach_md $us1
+ attach_md us1 -t vnode -f $m2
+else
+ detach_md $us0
+ attach_md us0 -t vnode -f $m1
+fi
+
+# Make sure that the component wasn't re-added to the gmirror.
+if [ $(gmirror status -s $name | wc -l) -eq 1 ]; then
+ echo "ok 3"
+else
+ echo "not ok 3"
+fi
+
+rm -f $m1 $m2 $tmp1 $tmp2
diff --git a/tests/sys/geom/class/mirror/13_test.sh b/tests/sys/geom/class/mirror/13_test.sh
new file mode 100644
index 000000000000..c312a1b7a795
--- /dev/null
+++ b/tests/sys/geom/class/mirror/13_test.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+
+# Test handling of write errors.
+
+. $(dirname $0)/conf.sh
+
+echo 1..4
+
+set -e
+
+ddbs=2048
+regwritefp="debug.fail_point.g_mirror_regular_request_write"
+m1=$(mktemp $base.XXXXXX)
+m2=$(mktemp $base.XXXXXX)
+
+dd if=/dev/random of=$m1 bs=$ddbs count=1024 >/dev/null 2>&1
+dd if=/dev/zero of=$m2 bs=$ddbs count=1024 >/dev/null 2>&1
+
+attach_md us0 -t vnode -f $m1
+attach_md us1 -t vnode -f $m2
+
+gmirror label $name /dev/$us0 /dev/$us1
+devwait
+
+tmp1=$(mktemp $base.XXXXXX)
+tmp2=$(mktemp $base.XXXXXX)
+
+dd if=/dev/random of=$tmp1 bs=$ddbs count=1 >/dev/null 2>&1
+
+ENXIO=6
+# gmirror has special handling for ENXIO. It does not mark the failed component
+# as broken, allowing it to rejoin the mirror automatically when it appears.
+sysctl ${regwritefp}="1*return(${ENXIO})[pid $(gmirror_worker_pid)]"
+dd if=$tmp1 of=/dev/mirror/$name bs=$ddbs count=1 >/dev/null 2>&1
+dd if=/dev/mirror/$name of=$tmp2 bs=$ddbs count=1 >/dev/null 2>&1
+sysctl ${regwritefp}='off'
+
+if cmp -s $tmp1 $tmp2; then
+ echo "ok 1"
+else
+ echo "not ok 1"
+fi
+
+# Verify that the genids still match after ENXIO.
+genid1=$(gmirror dump /dev/$us0 | awk '/^[[:space:]]*genid: /{print $2}')
+genid2=$(gmirror dump /dev/$us1 | awk '/^[[:space:]]*genid: /{print $2}')
+if [ $genid1 -eq $genid2 ]; then
+ echo "ok 2"
+else
+ echo "not ok 2"
+fi
+
+# The ENXIO should have caused a syncid bump.
+syncid1=$(gmirror dump /dev/$us0 | awk '/^[[:space:]]*syncid: /{print $2}')
+syncid2=$(gmirror dump /dev/$us1 | awk '/^[[:space:]]*syncid: /{print $2}')
+if [ $syncid1 -eq $(($syncid2 + 1)) -o $syncid2 -eq $(($syncid1 + 1)) ]; then
+ echo "ok 3"
+else
+ echo "not ok 3"
+fi
+
+# Force a retaste of the disconnected component.
+if [ $(gmirror status -s $name | awk '{print $3}') = $us0 ]; then
+ detach_md $us1
+ attach_md us1 -t vnode -f $m2
+else
+ detach_md $us0
+ attach_md us0 -t vnode -f $m1
+fi
+
+# Make sure that the retaste caused the mirror to automatically be re-added.
+if [ $(gmirror status -s $name | wc -l) -eq 2 ]; then
+ echo "ok 4"
+else
+ echo "not ok 4"
+fi
+
+syncwait
+
+rm -f $m1 $m2 $tmp1 $tmp2
diff --git a/tests/sys/geom/class/mirror/1_test.sh b/tests/sys/geom/class/mirror/1_test.sh
new file mode 100644
index 000000000000..929aa1c6ddcf
--- /dev/null
+++ b/tests/sys/geom/class/mirror/1_test.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+. `dirname $0`/conf.sh
+
+echo "1..1"
+
+attach_md us0 -t malloc -s 1M || exit 1
+attach_md us1 -t malloc -s 2M || exit 1
+attach_md us2 -t malloc -s 3M || exit 1
+
+gmirror label $name /dev/$us0 /dev/$us1 /dev/$us2 || exit 1
+devwait
+
+# Size of created device should be 1MB - 512b.
+
+size=`diskinfo /dev/mirror/${name} | awk '{print $3}'`
+
+if [ $size -eq 1048064 ]; then
+ echo "ok 1"
+else
+ echo "not ok 1"
+fi
diff --git a/tests/sys/geom/class/mirror/2_test.sh b/tests/sys/geom/class/mirror/2_test.sh
new file mode 100644
index 000000000000..31bb426d05d7
--- /dev/null
+++ b/tests/sys/geom/class/mirror/2_test.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+. `dirname $0`/conf.sh
+
+echo "1..4"
+
+balance="round-robin"
+ddbs=2048
+nblocks1=1024
+nblocks2=`expr $nblocks1 / \( $ddbs / 512 \)`
+src=`mktemp $base.XXXXXX` || exit 1
+dst=`mktemp $base.XXXXXX` || exit 1
+
+dd if=/dev/random of=${src} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+attach_md us0 -t malloc -s `expr $nblocks1 + 1` || exit 1
+attach_md us1 -t malloc -s `expr $nblocks1 + 1` || exit 1
+attach_md us2 -t malloc -s `expr $nblocks1 + 1` || exit 1
+
+gmirror label -b $balance $name /dev/${us0} /dev/${us1} /dev/${us2} || exit 1
+devwait
+
+dd if=${src} of=/dev/mirror/${name} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+dd if=/dev/mirror/${name} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 1"
+else
+ echo "ok 1"
+fi
+dd if=/dev/${us0} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 2"
+else
+ echo "ok 2"
+fi
+dd if=/dev/${us1} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 3"
+else
+ echo "ok 3"
+fi
+
+dd if=/dev/${us2} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 4"
+else
+ echo "ok 4"
+fi
+
+rm -f ${src} ${dst}
diff --git a/tests/sys/geom/class/mirror/3_test.sh b/tests/sys/geom/class/mirror/3_test.sh
new file mode 100644
index 000000000000..dd2ca8f028fd
--- /dev/null
+++ b/tests/sys/geom/class/mirror/3_test.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+. `dirname $0`/conf.sh
+
+echo "1..5"
+
+balance="round-robin"
+ddbs=2048
+nblocks1=1024
+nblocks2=`expr $nblocks1 / \( $ddbs / 512 \)`
+src=`mktemp $base.XXXXXX` || exit 1
+dst=`mktemp $base.XXXXXX` || exit 1
+
+dd if=/dev/random of=${src} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+attach_md us0 -t malloc -s `expr $nblocks1 + 1` || exit 1
+attach_md us1 -t malloc -s `expr $nblocks1 + 1` || exit 1
+attach_md us2 -t malloc -s `expr $nblocks1 + 1` || exit 1
+
+gmirror label -b $balance $name /dev/${us0} /dev/${us1} /dev/${us2} || exit 1
+devwait
+
+dd if=${src} of=/dev/mirror/${name} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+dd if=/dev/mirror/${name} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 1"
+else
+ echo "ok 1"
+fi
+
+gmirror remove $name ${us0}
+dd if=/dev/mirror/${name} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 2"
+else
+ echo "ok 2"
+fi
+
+gmirror remove $name ${us1}
+dd if=/dev/mirror/${name} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 3"
+else
+ echo "ok 3"
+fi
+
+gmirror remove $name ${us2}
+dd if=/dev/mirror/${name} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 4"
+else
+ echo "ok 4"
+fi
+
+# mirror/${name} should be removed.
+if [ -c /dev/${name} ]; then
+ echo "not ok 5"
+else
+ echo "ok 5"
+fi
+
+rm -f ${src} ${dst}
diff --git a/tests/sys/geom/class/mirror/4_test.sh b/tests/sys/geom/class/mirror/4_test.sh
new file mode 100644
index 000000000000..a5f469a2d95e
--- /dev/null
+++ b/tests/sys/geom/class/mirror/4_test.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+
+. `dirname $0`/conf.sh
+
+echo "1..5"
+
+balance="load"
+ddbs=2048
+nblocks1=1024
+nblocks2=`expr $nblocks1 / \( $ddbs / 512 \)`
+src=`mktemp $base.XXXXXX` || exit 1
+dst=`mktemp $base.XXXXXX` || exit 1
+
+dd if=/dev/random of=${src} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+attach_md us0 -t malloc -s `expr $nblocks1 + 1` || exit 1
+attach_md us1 -t malloc -s `expr $nblocks1 + 1` || exit 1
+attach_md us2 -t malloc -s `expr $nblocks1 + 1` || exit 1
+
+gmirror label -b $balance $name /dev/${us0} /dev/${us1} /dev/${us2} || exit 1
+devwait
+
+dd if=${src} of=/dev/mirror/${name} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+dd if=/dev/mirror/${name} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 1"
+else
+ echo "ok 1"
+fi
+
+gmirror remove $name ${us0}
+dd if=/dev/mirror/${name} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 2"
+else
+ echo "ok 2"
+fi
+
+gmirror remove $name ${us1}
+dd if=/dev/mirror/${name} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 3"
+else
+ echo "ok 3"
+fi
+
+gmirror remove $name ${us2}
+dd if=/dev/mirror/${name} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 4"
+else
+ echo "ok 4"
+fi
+
+gmirror destroy $name
+
+# mirror/${name} should be removed.
+if [ -c /dev/${name} ]; then
+ echo "not ok 5"
+else
+ echo "ok 5"
+fi
+
+rm -f ${src} ${dst}
diff --git a/tests/sys/geom/class/mirror/5_test.sh b/tests/sys/geom/class/mirror/5_test.sh
new file mode 100644
index 000000000000..43062dc1feea
--- /dev/null
+++ b/tests/sys/geom/class/mirror/5_test.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+. `dirname $0`/conf.sh
+
+echo "1..5"
+
+balance="split"
+ddbs=8192
+nblocks1=1024
+nblocks2=`expr $nblocks1 / \( $ddbs / 512 \)`
+src=`mktemp $base.XXXXXX` || exit 1
+dst=`mktemp $base.XXXXXX` || exit 1
+
+dd if=/dev/random of=${src} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+attach_md us0 -t malloc -s `expr $nblocks1 + 1` || exit 1
+attach_md us1 -t malloc -s `expr $nblocks1 + 1` || exit 1
+attach_md us2 -t malloc -s `expr $nblocks1 + 1` || exit 1
+
+gmirror label -b $balance -s `expr $ddbs / 2` $name /dev/${us0} /dev/${us1} /dev/${us2} || exit 1
+devwait
+
+dd if=${src} of=/dev/mirror/${name} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+dd if=/dev/mirror/${name} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 1"
+else
+ echo "ok 1"
+fi
+
+gmirror remove $name ${us0}
+dd if=/dev/mirror/${name} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 2"
+else
+ echo "ok 2"
+fi
+
+gmirror remove $name ${us1}
+dd if=/dev/mirror/${name} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 3"
+else
+ echo "ok 3"
+fi
+
+gmirror remove $name ${us2}
+dd if=/dev/mirror/${name} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 4"
+else
+ echo "ok 4"
+fi
+
+# mirror/${name} should be removed.
+if [ -c /dev/${name} ]; then
+ echo "not ok 5"
+else
+ echo "ok 5"
+fi
+
+rm -f ${src} ${dst}
diff --git a/tests/sys/geom/class/mirror/6_test.sh b/tests/sys/geom/class/mirror/6_test.sh
new file mode 100644
index 000000000000..f37a45404642
--- /dev/null
+++ b/tests/sys/geom/class/mirror/6_test.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+. `dirname $0`/conf.sh
+
+echo "1..2"
+
+balance="split"
+ddbs=8192
+nblocks1=1024
+nblocks2=`expr $nblocks1 / \( $ddbs / 512 \)`
+src=`mktemp $base.XXXXXX` || exit 1
+dst=`mktemp $base.XXXXXX` || exit 1
+
+dd if=/dev/random of=${src} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+attach_md us0 -t malloc -s `expr $nblocks1 + 1` || exit 1
+attach_md us1 -t malloc -s `expr $nblocks1 + 1` || exit 1
+attach_md us2 -t malloc -s `expr $nblocks1 + 1` || exit 1
+
+gmirror label -b $balance -s `expr $ddbs / 2` $name /dev/${us0} /dev/${us1} || exit 1
+devwait
+
+dd if=${src} of=/dev/mirror/${name} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+dd if=/dev/zero of=/dev/${us2} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+dd if=/dev/mirror/${name} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 1"
+else
+ echo "ok 1"
+fi
+
+# Connect disk to the mirror.
+gmirror insert ${name} ${us2}
+# Wait for synchronization.
+sleep 1
+dd if=/dev/${us2} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 2"
+else
+ echo "ok 2"
+fi
+
+rm -f ${src} ${dst}
diff --git a/tests/sys/geom/class/mirror/7_test.sh b/tests/sys/geom/class/mirror/7_test.sh
new file mode 100644
index 000000000000..5f93f2fc4943
--- /dev/null
+++ b/tests/sys/geom/class/mirror/7_test.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+. `dirname $0`/conf.sh
+
+echo "1..5"
+
+balance="prefer"
+ddbs=2048
+nblocks1=1024
+nblocks2=`expr $nblocks1 / \( $ddbs / 512 \)`
+src=`mktemp $base.XXXXXX` || exit 1
+dst=`mktemp $base.XXXXXX` || exit 1
+
+dd if=/dev/random of=${src} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+attach_md us0 -t malloc -s `expr $nblocks1 + 1` || exit 1
+attach_md us1 -t malloc -s `expr $nblocks1 + 1` || exit 1
+attach_md us2 -t malloc -s `expr $nblocks1 + 1` || exit 1
+
+gmirror label -b $balance $name /dev/${us0} /dev/${us1} /dev/${us2} || exit 1
+devwait
+
+dd if=${src} of=/dev/mirror/${name} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+dd if=/dev/mirror/${name} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 1"
+else
+ echo "ok 1"
+fi
+
+gmirror remove $name ${us0}
+dd if=/dev/mirror/${name} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 2"
+else
+ echo "ok 2"
+fi
+
+gmirror remove $name ${us1}
+dd if=/dev/mirror/${name} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 3"
+else
+ echo "ok 3"
+fi
+
+gmirror remove $name ${us2}
+dd if=/dev/mirror/${name} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 4"
+else
+ echo "ok 4"
+fi
+
+# mirror/${name} should be removed.
+if [ -c /dev/${name} ]; then
+ echo "not ok 5"
+else
+ echo "ok 5"
+fi
+
+rm -f ${src} ${dst}
diff --git a/tests/sys/geom/class/mirror/8_test.sh b/tests/sys/geom/class/mirror/8_test.sh
new file mode 100644
index 000000000000..baea157a0ba8
--- /dev/null
+++ b/tests/sys/geom/class/mirror/8_test.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+# Regression test for r317712.
+
+. `dirname $0`/conf.sh
+
+if ! [ -c /dev/mdctl ]; then
+ echo "1..0 # SKIP no /dev/mdctl to create md devices"
+ exit 0
+fi
+
+echo 1..1
+
+ddbs=2048
+m1=`mktemp $base.XXXXXX` || exit 1
+m2=`mktemp $base.XXXXXX` || exit 1
+
+dd if=/dev/zero of=$m1 bs=$ddbs count=1024 >/dev/null 2>&1
+dd if=/dev/zero of=$m2 bs=$ddbs count=1024 >/dev/null 2>&1
+
+us0=$(mdconfig -t vnode -f $m1) || exit 1
+us1=$(mdconfig -t vnode -f $m2) || exit 1
+
+gmirror label $name /dev/$us0 /dev/$us1 || exit 1
+devwait
+
+# Ensure that the mirrors are marked dirty, and then disconnect them.
+# We need to have the gmirror provider open when destroying the MDs since
+# gmirror will automatically mark the mirrors clean when the provider is closed.
+exec 9>/dev/mirror/$name
+dd if=/dev/zero bs=$ddbs count=1 >&9 2>/dev/null
+mdconfig -d -u ${us0#md} -o force || exit 1
+mdconfig -d -u ${us1#md} -o force || exit 1
+exec 9>&-
+
+dd if=/dev/random of=$m1 bs=$ddbs count=1 conv=notrunc >/dev/null 2>&1
+attach_md us0 -t vnode -f $m1 || exit 1
+devwait # This will take kern.geom.mirror.timeout seconds.
+
+# Re-attach the second mirror and wait for it to synchronize.
+attach_md us1 -t vnode -f $m2 || exit 1
+syncwait
+
+# Verify the two mirrors are identical. Destroy the gmirror first so that
+# the mirror metadata is wiped; otherwise the metadata blocks will fail
+# the comparison. It would be nice to do this with a "gmirror verify"
+# command instead.
+gmirror destroy $name
+if cmp -s ${m1} ${m2}; then
+ echo "ok 1"
+else
+ echo "not ok 1"
+fi
+
+rm -f $m1 $m2
diff --git a/tests/sys/geom/class/mirror/9_test.sh b/tests/sys/geom/class/mirror/9_test.sh
new file mode 100644
index 000000000000..6bcb26860615
--- /dev/null
+++ b/tests/sys/geom/class/mirror/9_test.sh
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+# Regression test for r306743.
+
+. `dirname $0`/conf.sh
+
+echo 1..1
+
+ddbs=2048
+m1=`mktemp $base.XXXXXX` || exit 1
+m2=`mktemp $base.XXXXXX` || exit 1
+m3=`mktemp $base.XXXXXX` || exit 1
+
+dd if=/dev/zero of=$m1 bs=$ddbs count=1024 >/dev/null 2>&1
+dd if=/dev/zero of=$m2 bs=$ddbs count=1024 >/dev/null 2>&1
+dd if=/dev/zero of=$m3 bs=$ddbs count=1024 >/dev/null 2>&1
+
+attach_md us0 -t vnode -f $m1 || exit 1
+attach_md us1 -t vnode -f $m2 || exit 1
+attach_md us2 -t vnode -f $m3 || exit 1
+
+gmirror label $name /dev/$us0 /dev/$us1 || exit 1
+devwait
+
+# Break one of the mirrors by forcing a single metadata write error.
+# When dd closes the mirror provider, gmirror will attempt to mark the mirrors
+# clean, and will kick one of the mirrors out upon hitting the error.
+sysctl debug.fail_point.g_mirror_metadata_write="1*return(5)[pid $(gmirror_worker_pid)]" || exit 1
+dd if=/dev/random of=/dev/mirror/$name bs=$ddbs count=1 >/dev/null 2>&1
+sysctl debug.fail_point.g_mirror_metadata_write='off' || exit 1
+
+# Replace the broken mirror, and then stop the gmirror.
+gmirror forget $name || exit 1
+gmirror insert $name /dev/$us2 || exit 1
+syncwait
+gmirror stop $name || exit 1
+
+# Restart the gmirror on the original two mirrors. One of them is broken,
+# so we should end up with a degraded gmirror.
+gmirror activate $name /dev/$us0 /dev/$us1 || exit 1
+devwait
+dd if=/dev/random of=/dev/mirror/$name bs=$ddbs count=1 >/dev/null 2>&1
+
+# Re-add the replacement mirror and verify the two mirrors are synchronized.
+# Destroy the gmirror first so that the mirror metadata is wiped; otherwise
+# the metadata blocks will fail the comparison. It would be nice to do this
+# with a "gmirror verify" command instead.
+gmirror activate $name /dev/$us2 || exit 1
+syncwait
+gmirror destroy $name || exit 1
+if cmp -s $m1 $m3; then
+ echo "ok 1"
+else
+ echo "not ok 1"
+fi
+
+rm -f $m1 $m2 $m3
diff --git a/tests/sys/geom/class/mirror/Makefile b/tests/sys/geom/class/mirror/Makefile
new file mode 100644
index 000000000000..635288cc3d53
--- /dev/null
+++ b/tests/sys/geom/class/mirror/Makefile
@@ -0,0 +1,31 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/geom/class/${.CURDIR:T}
+
+TAP_TESTS_SH+= 1_test
+TAP_TESTS_SH+= 2_test
+TAP_TESTS_SH+= 3_test
+TAP_TESTS_SH+= 4_test
+TAP_TESTS_SH+= 5_test
+TAP_TESTS_SH+= 6_test
+TAP_TESTS_SH+= 7_test
+TAP_TESTS_SH+= 8_test
+TAP_TESTS_SH+= 9_test
+TAP_TESTS_SH+= 10_test
+TAP_TESTS_SH+= 11_test
+TAP_TESTS_SH+= 12_test
+TAP_TESTS_SH+= 13_test
+
+ATF_TESTS_SH+= component_selection
+ATF_TESTS_SH+= sync_error
+
+# Tests use a global gmirror fail point.
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= conf.sh
+
+.for t in ${TAP_TESTS_SH}
+TEST_METADATA.$t+= required_user="root"
+.endfor
+
+.include <bsd.test.mk>
diff --git a/tests/sys/geom/class/mirror/Makefile.depend b/tests/sys/geom/class/mirror/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/geom/class/mirror/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/geom/class/mirror/component_selection.sh b/tests/sys/geom/class/mirror/component_selection.sh
new file mode 100755
index 000000000000..b5c8cec78c61
--- /dev/null
+++ b/tests/sys/geom/class/mirror/component_selection.sh
@@ -0,0 +1,139 @@
+
+ATF_TEST=true
+class=mirror
+. $(atf_get_srcdir)/conf.sh
+
+atf_test_case run_latest_genid cleanup
+run_latest_genid_head()
+{
+ atf_set "descr" \
+ "Ensure that we properly select components (latest genid) during STARTING."
+ atf_set "require.user" "root"
+}
+run_latest_genid_body()
+{
+ geom_atf_test_setup
+ if ! error_message=$(geom_load_class_if_needed nop); then
+ atf_skip "$error_message"
+ fi
+
+ f1=$(mktemp ${base}.XXXXXX)
+ f2=$(mktemp ${base}.XXXXXX)
+ f3=$(mktemp ${base}.XXXXXX)
+ rnd1=$(mktemp ${base}.XXXXXX)
+ rnd2=$(mktemp ${base}.XXXXXX)
+
+ atf_check truncate -s 2M $f1
+ atf_check truncate -s 2M $f2
+ atf_check truncate -s 2M $f3
+ dd if=/dev/urandom bs=512 count=1 of="$rnd1"
+ dd if=/dev/urandom bs=512 count=1 of="$rnd2"
+
+ attach_md md1 -t vnode -f ${f1}
+ attach_md md2 -t vnode -f ${f2}
+ attach_md md3 -t vnode -f ${f3}
+
+ # Use a gnop for md1 just for consistency; it's not used for anything.
+ atf_check gnop create $md1
+ atf_check gnop create $md2
+ atf_check gnop create $md3
+ # Hardcode component names so that the non-.nop device isn't tasted
+ # instead.
+ atf_check gmirror label -h $name ${md1}.nop
+ devwait
+
+ atf_check gmirror insert -h $name ${md2}.nop
+ atf_check gmirror insert -h $name ${md3}.nop
+ syncwait
+
+ # Fail mirror 3, writing known contents to mirror 1+2 block 1
+ atf_check -s exit:0 -e empty -o empty \
+ gnop configure -w 100 ${md3}.nop
+ atf_check -s exit:0 dd if="$rnd1" bs=512 count=1 oseek=1 conv=notrunc \
+ of=/dev/mirror/$name status=none
+
+ disconnectwait nop "${md3}.nop"
+
+ # Should have two mirrors remaining after md3 was evicted
+ atf_check [ $(gmirror status -s $name | wc -l) -eq 2 ]
+ atf_check -s exit:0 -o match:"DEGRADED ${md1}.nop \(ACTIVE\)" \
+ gmirror status -s $name
+ atf_check -s exit:0 -o match:"DEGRADED ${md2}.nop \(ACTIVE\)" \
+ gmirror status -s $name
+
+ # Repeat:
+ # Fail mirror 2, writing known contents to mirror 1 block 2
+ atf_check -s exit:0 -e empty -o empty \
+ gnop configure -w 100 ${md2}.nop
+ atf_check -s exit:0 dd if="$rnd2" bs=512 count=2 oseek=1 conv=notrunc \
+ of=/dev/mirror/$name status=none
+
+ disconnectwait nop "${md2}.nop"
+
+ # Should have one mirror remaining after md2 was evicted
+ atf_check [ $(gmirror status -s $name | wc -l) -eq 1 ]
+ atf_check -s exit:0 -o match:"DEGRADED ${md1}.nop \(ACTIVE\)" \
+ gmirror status -s $name
+
+ # Stop the mirror and remove the pieces so gmirror can't see them.
+ atf_check gmirror stop $name
+ atf_check gnop destroy ${md1}.nop
+ atf_check gnop destroy ${md2}.nop
+ atf_check gnop destroy ${md3}.nop
+
+ # Rebuild; spin up "disk" with lowest genid
+ atf_check gnop create $md3
+ md3gen=$(gmirror dump /dev/${md3}.nop | grep genid | cut -d: -f2)
+ # Assert gmirror is referencing this component for now:
+ atf_check [ $(consumerrefs nop ${md3}.nop) = "r1w1e1" ]
+
+ # Adding newer genid should kick out old component
+ atf_check gnop create $md2
+ md2gen=$(gmirror dump /dev/${md2}.nop | grep genid | cut -d: -f2)
+ atf_check [ $md2gen -gt $md3gen ]
+
+ disconnectwait nop "${md3}.nop"
+
+ # Can't test this because 'status' doesn't exist until RUNNING:
+ #atf_check [ $(gmirror status -s $name | wc -l) -eq 1 ]
+ # But as a substitute, assert gmirror has dropped reference to staler
+ # component in favor of newer component:
+ atf_check [ $(consumerrefs nop ${md2}.nop) = "r1w1e1" ]
+
+ # ditto
+ atf_check gnop create $md1
+ md1gen=$(gmirror dump /dev/${md1}.nop | grep genid | cut -d: -f2)
+ atf_check [ $md1gen -gt $md2gen ]
+
+ disconnectwait nop "${md2}.nop"
+
+ # Assert gmirror has dropped reference to stale component in favor of
+ # newer component:
+ atf_check [ $(consumerrefs nop ${md1}.nop) = "r1w1e1" ]
+
+ # gmirror won't start the mirror automatically with only one component
+ # ($md0) of configured three, so this waits out the
+ # kern.geom.mirror.timeout:
+ devwait
+
+ atf_check [ $(gmirror status -s $name | wc -l) -eq 1 ]
+ atf_check -s exit:0 -o match:"DEGRADED ${md1}.nop \(ACTIVE\)" \
+ gmirror status -s $name
+}
+run_latest_genid_cleanup()
+{
+ . $(atf_get_srcdir)/conf.sh
+
+ if [ -f "$TEST_MDS_FILE" ]; then
+ while read test_md; do
+ echo "# Removing test gnop: ${test_md}.nop"
+ gnop destroy -f "${test_md}.nop" 2>/dev/null || :
+ done < "$TEST_MDS_FILE"
+ fi
+ gmirror_test_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case run_latest_genid
+}
diff --git a/tests/sys/geom/class/mirror/conf.sh b/tests/sys/geom/class/mirror/conf.sh
new file mode 100644
index 000000000000..5d3b8623dcd6
--- /dev/null
+++ b/tests/sys/geom/class/mirror/conf.sh
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+name="$(mktemp -u mirror.XXXXXX)"
+class="mirror"
+base=`basename $0`
+
+gmirror_test_cleanup()
+{
+ [ -c /dev/$class/$name ] && gmirror destroy $name
+ geom_test_cleanup
+}
+trap gmirror_test_cleanup ABRT EXIT INT TERM
+
+gmirror_worker_pid()
+{
+ pgrep -S -n "g_mirror mirror\..*"
+}
+
+syncwait()
+{
+ while $(gmirror status -s $name | grep -q SYNCHRONIZING); do
+ sleep 0.1;
+ done
+}
+
+consumerrefs()
+{
+ gclass=$1
+ geom=$2
+
+ if [ $# -ne 2 ]; then
+ echo "Bad usage consumerrefs" >&2
+ exit 1
+ fi
+
+ geom "${gclass}" list "${geom}" | \
+ grep -A5 ^Consumers | \
+ grep Mode | \
+ cut -d: -f2
+}
+
+disconnectwait()
+{
+ gclass=$1
+ geom=$2
+
+ if [ $# -ne 2 ]; then
+ echo "Bad usage disconnectwait" >&2
+ exit 1
+ fi
+
+ while [ $(consumerrefs "$gclass" "$geom") != r0w0e0 ]; do
+ sleep 0.05
+ done
+}
+
+. `dirname $0`/../geom_subr.sh
diff --git a/tests/sys/geom/class/mirror/sync_error.sh b/tests/sys/geom/class/mirror/sync_error.sh
new file mode 100644
index 000000000000..c51418151f0a
--- /dev/null
+++ b/tests/sys/geom/class/mirror/sync_error.sh
@@ -0,0 +1,108 @@
+
+ATF_TEST=true
+. $(atf_get_srcdir)/conf.sh
+
+REG_READ_FP=debug.fail_point.g_mirror_regular_request_read
+
+atf_test_case sync_read_error_2_disks cleanup
+sync_read_error_2_disks_head()
+{
+ atf_set "descr" \
+ "Ensure that we properly handle read errors during synchronization."
+ atf_set "require.user" "root"
+}
+sync_read_error_2_disks_body()
+{
+ geom_atf_test_setup
+
+ f1=$(mktemp ${base}.XXXXXX)
+ f2=$(mktemp ${base}.XXXXXX)
+
+ atf_check dd if=/dev/zero bs=1M count=32 of=$f1 status=none
+ atf_check truncate -s 32M $f2
+
+ attach_md md1 -t vnode -f ${f1}
+ attach_md md2 -t vnode -f ${f2}
+
+ atf_check gmirror label $name $md1
+ devwait
+
+ atf_check -s ignore -e empty -o not-empty sysctl ${REG_READ_FP}="1*return(5)[pid $(gmirror_worker_pid)]"
+
+ # If a read error occurs while synchronizing and the mirror contains
+ # a single active disk, gmirror has no choice but to fail the
+ # synchronization and kick the new disk out of the mirror.
+ atf_check gmirror insert $name $md2
+ sleep 0.1
+ syncwait
+ atf_check [ $(gmirror status -s $name | wc -l) -eq 1 ]
+ atf_check -s exit:0 -o match:"DEGRADED $md1 \(ACTIVE\)" \
+ gmirror status -s $name
+}
+sync_read_error_2_disks_cleanup()
+{
+ atf_check -s ignore -e ignore -o ignore sysctl ${REG_READ_FP}='off'
+ gmirror_test_cleanup
+}
+
+atf_test_case sync_read_error_3_disks cleanup
+sync_read_error_3_disks_head()
+{
+ atf_set "descr" \
+ "Ensure that we properly handle read errors during synchronization."
+ atf_set "require.user" "root"
+}
+sync_read_error_3_disks_body()
+{
+ geom_atf_test_setup
+
+ f1=$(mktemp ${base}.XXXXXX)
+ f2=$(mktemp ${base}.XXXXXX)
+ f3=$(mktemp ${base}.XXXXXX)
+
+ atf_check dd if=/dev/random bs=1M count=32 of=$f1 status=none
+ atf_check truncate -s 32M $f2
+ atf_check truncate -s 32M $f3
+
+ attach_md md1 -t vnode -f ${f1}
+ attach_md md2 -t vnode -f ${f2}
+ attach_md md3 -t vnode -f ${f3}
+
+ atf_check gmirror label $name $md1
+ devwait
+
+ atf_check gmirror insert $name $md2
+ syncwait
+
+ atf_check -s exit:0 -e empty -o not-empty sysctl ${REG_READ_FP}="1*return(5)[pid $(gmirror_worker_pid)]"
+
+ # If a read error occurs while synchronizing a new disk, and we have
+ # multiple active disks, we retry the read after an error. The disk
+ # which returned the read error is kicked out of the mirror.
+ atf_check gmirror insert $name $md3
+ syncwait
+ atf_check [ $(gmirror status -s $name | wc -l) -eq 2 ]
+ atf_check -s exit:0 -o match:"DEGRADED $md3 \(ACTIVE\)" \
+ gmirror status -s $name
+
+ # Make sure that the two active disks are identical. Destroy the
+ # mirror first so that the metadata sectors are wiped.
+ if $(gmirror status -s $name | grep -q $md1); then
+ active=$md1
+ else
+ active=$md2
+ fi
+ atf_check gmirror destroy $name
+ atf_check cmp /dev/$active /dev/$md3
+}
+sync_read_error_3_disks_cleanup()
+{
+ atf_check -s ignore -e ignore -o ignore sysctl ${REG_READ_FP}='off'
+ gmirror_test_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case sync_read_error_2_disks
+ atf_add_test_case sync_read_error_3_disks
+}
diff --git a/tests/sys/geom/class/multipath/Makefile b/tests/sys/geom/class/multipath/Makefile
new file mode 100644
index 000000000000..1246015e8fc4
--- /dev/null
+++ b/tests/sys/geom/class/multipath/Makefile
@@ -0,0 +1,11 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/geom/class/${.CURDIR:T}
+
+ATF_TESTS_SH+= failloop
+ATF_TESTS_SH+= misc
+TEST_METADATA.failloop+= is_exclusive=true
+
+${PACKAGE}FILES+= conf.sh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/geom/class/multipath/conf.sh b/tests/sys/geom/class/multipath/conf.sh
new file mode 100755
index 000000000000..2e9791bc5af1
--- /dev/null
+++ b/tests/sys/geom/class/multipath/conf.sh
@@ -0,0 +1,106 @@
+#!/bin/sh
+# Copyright (c) 2019 Axcient
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+MD_DEVS="md.devs"
+MULTIPATH_DEVS="multipath.devs"
+
+alloc_md()
+{
+ local md
+
+ md=$(mdconfig -a -t swap -s 1M) || atf_fail "mdconfig -a failed"
+ echo ${md} >> $MD_DEVS
+ echo ${md}
+}
+
+# Verify expected state.
+# check_multipath_state <active_path> <geom_state> <prov0_state> <prov1_state> [prov2_state]
+check_multipath_state()
+{
+ local want_active_path=$1
+ local want_geom_state=$2
+ local want_prov0_state=$3
+ local want_prov1_state=$4
+ local want_prov2_state=$5
+ local geom_state
+ local prov0_state
+ local prov1_state
+ local prov2_state
+
+ geom_state=`gmultipath list "$name" | awk '/^State:/ {print $2}'`
+ atf_check_equal "$want_geom_state" "$geom_state"
+ prov0_state=`gmultipath list "$name" | awk '/1. Name: md[0-9]/ {trigger=1} /State:/ && trigger == 1 {print $2; trigger=0;}'`
+ prov1_state=`gmultipath list "$name" | awk '/2. Name: md[0-9]/ {trigger=1} /State:/ && trigger == 1 {print $2; trigger=0;}'`
+ prov2_state=`gmultipath list "$name" | awk '/3. Name: md[0-9]/ {trigger=1} /State:/ && trigger == 1 {print $2; trigger=0;}'`
+ atf_check_equal "$want_active_path" "`gmultipath getactive "$name"`"
+ atf_check_equal "$want_prov0_state" $prov0_state
+ atf_check_equal "$want_prov1_state" $prov1_state
+ if [ -n "$want_prov2_state" ]; then
+ atf_check_equal "$want_prov2_state" $prov2_state
+ fi
+}
+
+common_cleanup()
+{
+ name=$(cat $MULTIPATH_DEVS)
+ if [ -n "$name" -a -c "/dev/multipath/$name" ]; then
+ gmultipath destroy "$name"
+ rm $MULTIPATH_DEVS
+ fi
+ if [ -f "$MD_DEVS" ]; then
+ while read test_md; do
+ gnop destroy -f ${test_md}.nop 2>/dev/null
+ mdconfig -d -u $test_md 2>/dev/null
+ done < $MD_DEVS
+ rm $MD_DEVS
+ fi
+ true
+}
+
+load_dtrace()
+{
+ if ! kldstat -q -m sdt; then
+ kldload sdt || atf_skip "could not load module for dtrace SDT"
+ fi
+}
+
+load_gmultipath()
+{
+ if ! kldstat -q -m g_multipath; then
+ geom multipath load || atf_skip "could not load module for geom multipath"
+ fi
+}
+
+load_gnop()
+{
+ if ! kldstat -q -m g_nop; then
+ geom nop load || atf_skip "could not load module for geom nop"
+ fi
+}
+
+mkname()
+{
+ mktemp -u mp.XXXXXX | tee $MULTIPATH_DEVS
+}
diff --git a/tests/sys/geom/class/multipath/failloop.sh b/tests/sys/geom/class/multipath/failloop.sh
new file mode 100755
index 000000000000..f966c4324a07
--- /dev/null
+++ b/tests/sys/geom/class/multipath/failloop.sh
@@ -0,0 +1,79 @@
+#!/bin/sh
+# Copyright (c) 2019 Axcient
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/conf.sh
+
+# See also https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=178473
+atf_test_case failloop cleanup
+failloop_head()
+{
+ atf_set "descr" "A persistent failure in the provider should not cause an infinite loop, nor restore any providers that were faulted by the same bio"
+ atf_set "require.user" "root"
+ atf_set "require.config" "allow_sysctl_side_effects"
+}
+failloop_body()
+{
+ sysctl -n kern.geom.notaste > kern.geom.notaste.txt
+ load_gnop
+ load_gmultipath
+ load_dtrace
+
+ md0=$(alloc_md)
+ md1=$(alloc_md)
+ name=$(mkname)
+ atf_check gnop create /dev/${md0}
+ atf_check gnop create /dev/${md1}
+ atf_check -s exit:0 gmultipath create "$name" ${md0}.nop ${md1}.nop
+ sysctl kern.geom.notaste=1
+
+ atf_check gnop configure -r 100 -w 100 ${md0}.nop
+ atf_check gnop configure -r 100 -w 100 ${md1}.nop
+ dd_status=`dtrace \
+ -o restore_count \
+ -i 'geom:multipath:config:restore {@restore = count()}' \
+ -c "dd if=/dev/zero of=/dev/multipath/"$name" bs=4096 count=1" \
+ 2>&1 | awk '/exited with status/ {print $NF}'`
+ if [ ! -f restore_count ]; then
+ atf_skip "dtrace didn't execute successfully"
+ fi
+ # The dd command should've failed ...
+ atf_check_equal 1 $dd_status
+ # and triggered 1 or 2 path restores
+ if [ `cat restore_count` -gt 2 ]; then
+ atf_fail "gmultipath restored paths too many times"
+ fi
+}
+failloop_cleanup()
+{
+ if [ -f kern.geom.notaste.txt ]; then
+ sysctl kern.geom.notaste=`cat kern.geom.notaste.txt`
+ fi
+ common_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case failloop
+}
diff --git a/tests/sys/geom/class/multipath/misc.sh b/tests/sys/geom/class/multipath/misc.sh
new file mode 100755
index 000000000000..9b27c6cf3ad5
--- /dev/null
+++ b/tests/sys/geom/class/multipath/misc.sh
@@ -0,0 +1,362 @@
+#!/bin/sh
+# Copyright (c) 2019 Axcient
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/conf.sh
+
+atf_test_case add cleanup
+add_head()
+{
+ atf_set "descr" "Add a new path"
+ atf_set "require.user" "root"
+}
+add_body()
+{
+ load_gmultipath
+ load_dtrace
+
+ md0=$(alloc_md)
+ md1=$(alloc_md)
+ md2=$(alloc_md)
+ name=$(mkname)
+ atf_check -s exit:0 gmultipath create "$name" ${md0} ${md1}
+ check_multipath_state ${md0} "OPTIMAL" "ACTIVE" "PASSIVE"
+
+ # Add a new path
+ atf_check -s exit:0 gmultipath add "$name" ${md2}
+ check_multipath_state ${md0} "OPTIMAL" "ACTIVE" "PASSIVE" "PASSIVE"
+}
+add_cleanup()
+{
+ common_cleanup
+}
+
+atf_test_case create_A cleanup
+create_A_head()
+{
+ atf_set "descr" "Create an Active/Active multipath device"
+ atf_set "require.user" "root"
+}
+create_A_body()
+{
+ load_gmultipath
+ load_dtrace
+
+ md0=$(alloc_md)
+ md1=$(alloc_md)
+ name=$(mkname)
+ atf_check -s exit:0 gmultipath create -A "$name" ${md0} ${md1}
+ check_multipath_state "${md1} ${md0}" "OPTIMAL" "ACTIVE" "ACTIVE"
+}
+create_A_cleanup()
+{
+ common_cleanup
+}
+
+atf_test_case create_R cleanup
+create_R_head()
+{
+ atf_set "descr" "Create an Active/Read multipath device"
+ atf_set "require.user" "root"
+}
+create_R_body()
+{
+ load_gmultipath
+ load_dtrace
+
+ md0=$(alloc_md)
+ md1=$(alloc_md)
+ name=$(mkname)
+ atf_check -s exit:0 gmultipath create -R "$name" ${md0} ${md1}
+ check_multipath_state ${md0} "OPTIMAL" "ACTIVE" "READ"
+}
+create_R_cleanup()
+{
+ common_cleanup
+}
+
+atf_test_case depart_and_arrive cleanup
+depart_and_arrive_head()
+{
+ atf_set "descr" "gmultipath should remove devices that disappear, and automatically reattach labeled providers that reappear"
+ atf_set "require.user" "root"
+}
+depart_and_arrive_body()
+{
+ load_gnop
+ load_gmultipath
+ md0=$(alloc_md)
+ md1=$(alloc_md)
+ name=$(mkname)
+ # We need a non-zero offset to gmultipath won't see the label when it
+ # tastes the md device. We only want the label to be visible on the
+ # gnop device.
+ offset=131072
+ atf_check gnop create -o $offset /dev/${md0}
+ atf_check gnop create -o $offset /dev/${md1}
+ atf_check -s exit:0 gmultipath label "$name" ${md0}.nop
+ # gmultipath is too smart to let us create a gmultipath device by label
+ # when the two providers aren't actually related. So we create a
+ # device by label with one provider, and then manually add the second.
+ atf_check -s exit:0 gmultipath add "$name" ${md1}.nop
+ NDEVS=`gmultipath list "$name" | grep -c 'md[0-9]*\.nop'`
+ atf_check_equal 2 $NDEVS
+
+ # Now fail the labeled provider
+ atf_check -s exit:0 gnop destroy -f ${md0}.nop
+ # It should be automatically removed from the multipath device
+ NDEVS=`gmultipath list "$name" | grep -c 'md[0-9]*\.nop'`
+ atf_check_equal 1 $NDEVS
+
+ # Now return the labeled provider
+ atf_check gnop create -o $offset /dev/${md0}
+ # It should be automatically restored to the multipath device. We
+ # don't really care which path is active.
+ NDEVS=`gmultipath list "$name" | grep -c 'md[0-9]*\.nop'`
+ atf_check_equal 2 $NDEVS
+ STATE=`gmultipath list "$name" | awk '/^State:/ {print $2}'`
+ atf_check_equal "OPTIMAL" $STATE
+}
+depart_and_arrive_cleanup()
+{
+ common_cleanup
+}
+
+
+atf_test_case fail cleanup
+fail_head()
+{
+ atf_set "descr" "Manually fail a path"
+ atf_set "require.user" "root"
+}
+fail_body()
+{
+ load_gmultipath
+ md0=$(alloc_md)
+ md1=$(alloc_md)
+ name=$(mkname)
+ atf_check -s exit:0 gmultipath create "$name" ${md0} ${md1}
+ check_multipath_state ${md0} "OPTIMAL" "ACTIVE" "PASSIVE"
+ # Manually fail the active path
+ atf_check -s exit:0 gmultipath fail "$name" ${md0}
+ check_multipath_state ${md1} "DEGRADED" "FAIL" "ACTIVE"
+}
+fail_cleanup()
+{
+ common_cleanup
+}
+
+atf_test_case fail_on_error cleanup
+fail_on_error_head()
+{
+ atf_set "descr" "An error in the provider will cause gmultipath to mark it as FAIL"
+ atf_set "require.user" "root"
+}
+fail_on_error_body()
+{
+ load_gnop
+ load_gmultipath
+ md0=$(alloc_md)
+ md1=$(alloc_md)
+ name=$(mkname)
+ atf_check gnop create /dev/${md0}
+ atf_check gnop create /dev/${md1}
+ atf_check -s exit:0 gmultipath create "$name" ${md0}.nop ${md1}.nop
+ # The first I/O to the first path should fail, causing gmultipath to
+ # fail over to the second path.
+ atf_check gnop configure -r 100 -w 100 ${md0}.nop
+ atf_check -s exit:0 -o ignore -e ignore dd if=/dev/zero of=/dev/multipath/"$name" bs=4096 count=1
+ check_multipath_state ${md1}.nop "DEGRADED" "FAIL" "ACTIVE"
+}
+fail_on_error_cleanup()
+{
+ common_cleanup
+}
+
+atf_test_case physpath cleanup
+physpath_head()
+{
+ atf_set "descr" "gmultipath should append /mp to the underlying providers' physical path"
+ atf_set "require.user" "root"
+}
+physpath_body()
+{
+ load_gnop
+ load_gmultipath
+ md0=$(alloc_md)
+ md1=$(alloc_md)
+ name=$(mkname)
+ physpath="some/physical/path"
+ # Create two providers with the same physical paths, mimicing how
+ # multipathed SAS drives appear. This is the normal way to use
+ # gmultipath. If the underlying providers' physical paths differ,
+ # then you're probably using gmultipath wrong.
+ atf_check gnop create -z $physpath /dev/${md0}
+ atf_check gnop create -z $physpath /dev/${md1}
+ atf_check -s exit:0 gmultipath create "$name" ${md0}.nop ${md1}.nop
+ gmultipath_physpath=$(diskinfo -p multipath/"$name")
+ atf_check_equal "$physpath/mp" "$gmultipath_physpath"
+}
+physpath_cleanup()
+{
+ common_cleanup
+}
+
+atf_test_case prefer cleanup
+prefer_head()
+{
+ atf_set "descr" "Manually select the preferred path"
+ atf_set "require.user" "root"
+}
+prefer_body()
+{
+ load_gmultipath
+ load_dtrace
+
+ md0=$(alloc_md)
+ md1=$(alloc_md)
+ md2=$(alloc_md)
+ name=$(mkname)
+ atf_check -s exit:0 gmultipath create "$name" ${md0} ${md1} ${md2}
+ check_multipath_state ${md0} "OPTIMAL" "ACTIVE" "PASSIVE" "PASSIVE"
+
+ # Explicitly prefer the final path
+ atf_check -s exit:0 gmultipath prefer "$name" ${md2}
+ check_multipath_state ${md2} "OPTIMAL" "PASSIVE" "PASSIVE" "ACTIVE"
+}
+prefer_cleanup()
+{
+ common_cleanup
+}
+
+atf_test_case restore cleanup
+restore_head()
+{
+ atf_set "descr" "Manually restore a failed path"
+ atf_set "require.user" "root"
+}
+restore_body()
+{
+ load_gmultipath
+ load_dtrace
+
+ md0=$(alloc_md)
+ md1=$(alloc_md)
+ name=$(mkname)
+ atf_check -s exit:0 gmultipath create "$name" ${md0} ${md1}
+
+ # Explicitly fail the first path
+ atf_check -s exit:0 gmultipath fail "$name" ${md0}
+ check_multipath_state ${md1} "DEGRADED" "FAIL" "ACTIVE"
+
+ # Explicitly restore it
+ atf_check -s exit:0 gmultipath restore "$name" ${md0}
+ check_multipath_state ${md1} "OPTIMAL" "PASSIVE" "ACTIVE"
+}
+restore_cleanup()
+{
+ common_cleanup
+}
+
+atf_test_case restore_on_error cleanup
+restore_on_error_head()
+{
+ atf_set "descr" "A failed path should be restored if an I/O error is encountered on all other active paths"
+ atf_set "require.user" "root"
+}
+restore_on_error_body()
+{
+ load_gnop
+ load_gmultipath
+ load_dtrace
+
+ md0=$(alloc_md)
+ md1=$(alloc_md)
+ name=$(mkname)
+ atf_check gnop create /dev/${md0}
+ atf_check gnop create /dev/${md1}
+ atf_check -s exit:0 gmultipath create "$name" ${md0}.nop ${md1}.nop
+ # Explicitly fail the first path
+ atf_check -s exit:0 gmultipath fail "$name" ${md0}.nop
+
+ # Setup the second path to fail on the next I/O
+ atf_check gnop configure -r 100 -w 100 ${md1}.nop
+ atf_check -s exit:0 -o ignore -e ignore \
+ dd if=/dev/zero of=/dev/multipath/"$name" bs=4096 count=1
+
+ # Now the first path should be active, and the second should be failed
+ check_multipath_state ${md0}.nop "DEGRADED" "ACTIVE" "FAIL"
+}
+restore_on_error_cleanup()
+{
+ common_cleanup
+}
+
+atf_test_case rotate cleanup
+rotate_head()
+{
+ atf_set "descr" "Manually rotate the active path"
+ atf_set "require.user" "root"
+}
+rotate_body()
+{
+ load_gmultipath
+ load_dtrace
+
+ md0=$(alloc_md)
+ md1=$(alloc_md)
+ md2=$(alloc_md)
+ name=$(mkname)
+ atf_check -s exit:0 gmultipath create "$name" ${md0} ${md1} ${md2}
+ check_multipath_state ${md0} "OPTIMAL" "ACTIVE" "PASSIVE" "PASSIVE"
+
+ # Explicitly rotate the paths
+ atf_check -s exit:0 gmultipath rotate "$name"
+ check_multipath_state ${md2} "OPTIMAL" "PASSIVE" "PASSIVE" "ACTIVE"
+ # Again
+ atf_check -s exit:0 gmultipath rotate "$name"
+ check_multipath_state ${md1} "OPTIMAL" "PASSIVE" "ACTIVE" "PASSIVE"
+ # Final rotation should restore original configuration
+ atf_check -s exit:0 gmultipath rotate "$name"
+ check_multipath_state ${md0} "OPTIMAL" "ACTIVE" "PASSIVE" "PASSIVE"
+}
+rotate_cleanup()
+{
+ common_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case add
+ atf_add_test_case create_A
+ atf_add_test_case create_R
+ atf_add_test_case depart_and_arrive
+ atf_add_test_case fail
+ atf_add_test_case fail_on_error
+ atf_add_test_case physpath
+ atf_add_test_case prefer
+ atf_add_test_case restore
+ atf_add_test_case restore_on_error
+ atf_add_test_case rotate
+}
diff --git a/tests/sys/geom/class/nop/Makefile b/tests/sys/geom/class/nop/Makefile
new file mode 100644
index 000000000000..e2cb249bcf56
--- /dev/null
+++ b/tests/sys/geom/class/nop/Makefile
@@ -0,0 +1,11 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/geom/class/${.CURDIR:T}
+
+ATF_TESTS_SH+= nop_test
+
+# Some tests make use of the "disks" property and kyua may schedule
+# them to run at the time time, which the tests do not expect.
+TEST_METADATA.nop_test+= is_exclusive="true"
+
+.include <bsd.test.mk>
diff --git a/tests/sys/geom/class/nop/Makefile.depend b/tests/sys/geom/class/nop/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/geom/class/nop/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/geom/class/nop/nop_test.sh b/tests/sys/geom/class/nop/nop_test.sh
new file mode 100644
index 000000000000..f122a1309bc0
--- /dev/null
+++ b/tests/sys/geom/class/nop/nop_test.sh
@@ -0,0 +1,280 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2016 Alan Somers
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+MD_DEVS="md.devs"
+PLAINFILES=plainfiles
+
+atf_test_case preserve_props cleanup
+preserve_props_head()
+{
+ atf_set "descr" "gnop should preserve basic GEOM properties"
+ atf_set "require.user" "root"
+ atf_set "timeout" 15
+}
+preserve_props_body()
+{
+ load_gnop
+ us=$(alloc_md)
+ atf_check gnop create /dev/${us}
+ md_secsize=$(diskinfo ${us} | cut -wf 2)
+ md_mediasize=$(diskinfo ${us} | cut -wf 3)
+ md_stripesize=$(diskinfo ${us} | cut -wf 5)
+ nop_secsize=$(diskinfo ${us}.nop | cut -wf 2)
+ nop_mediasize=$(diskinfo ${us}.nop | cut -wf 3)
+ nop_stripesize=$(diskinfo ${us}.nop | cut -wf 5)
+ atf_check_equal "$md_secsize" "$nop_secsize"
+ atf_check_equal "$md_mediasize" "$nop_mediasize"
+ atf_check_equal "$md_stripesize" "$nop_stripesize"
+}
+preserve_props_cleanup()
+{
+ common_cleanup
+}
+
+atf_test_case preserve_disk_props cleanup
+preserve_disk_props_head()
+{
+ atf_set "descr" "gnop should preserve properties for disks"
+ atf_set "require.user" "root"
+ atf_set "require.config" "disks"
+ atf_set "timeout" 15
+}
+preserve_disk_props_body()
+{
+ load_gnop
+ disks=`atf_config_get disks`
+ disk=${disks%% *}
+ if [ -z "$disk" ]; then
+ atf_skip "Must define disks (see tests(7))"
+ fi
+ atf_check gnop create ${disk}
+
+ disk_ident=$(diskinfo -s ${disk})
+ disk_physpath=$(diskinfo -p ${disk})
+ disk_descr=$(diskinfo -v ${disk} | awk '/Disk descr/ {print $1}')
+ disk_trim=$(diskinfo -v ${disk} | awk '/TRIM.UNMAP/ {print $1}')
+ disk_rotrate=$(diskinfo -v ${disk} | awk '/Rotation rate/ {print $1}')
+ disk_zonemode=$(diskinfo -v ${disk} | awk '/Zone Mode/ {print $1}')
+ nop_ident=$(diskinfo -s ${disk}.nop)
+ nop_physpath=$(diskinfo -p ${disk}.nop)
+ nop_descr=$(diskinfo -v ${disk}.nop | awk '/Disk descr/ {print $1}')
+ nop_trim=$(diskinfo -v ${disk}.nop | awk '/TRIM.UNMAP/ {print $1}')
+ nop_rotrate=$(diskinfo -v ${disk}.nop | awk '/Rotation/ {print $1}')
+ nop_zonemode=$(diskinfo -v ${disk}.nop | awk '/Zone Mode/ {print $1}')
+ atf_check_equal "$disk_ident" "$nop_ident"
+ atf_check_equal "$disk_physpath" "$nop_physpath"
+ atf_check_equal "$disk_descr" "$nop_descr"
+ atf_check_equal "$disk_trim" "$nop_trim"
+ atf_check_equal "$disk_rotrate" "$nop_rotrate"
+ atf_check_equal "$disk_zonemode" "$nop_zonemode"
+}
+preserve_disk_props_cleanup()
+{
+ disk_cleanup
+ common_cleanup
+}
+
+atf_test_case io cleanup
+io_head()
+{
+ atf_set "descr" "I/O works on gnop devices"
+ atf_set "require.user" "root"
+ atf_set "timeout" 15
+}
+io_body()
+{
+ load_gnop
+ us=$(alloc_md)
+ atf_check gnop create /dev/${us}
+
+ echo src >> $PLAINFILES
+ echo dst >> $PLAINFILES
+ dd if=/dev/random of=src bs=1m count=1 >/dev/null 2>&1
+ dd if=src of=/dev/${us}.nop bs=1m count=1 > /dev/null 2>&1
+ dd if=/dev/${us}.nop of=dst bs=1m count=1 > /dev/null 2>&1
+
+ atf_check_equal `md5 -q src` `md5 -q dst`
+}
+io_cleanup()
+{
+ common_cleanup
+}
+
+atf_test_case physpath cleanup
+physpath_head()
+{
+ atf_set "descr" "Test gnop's -z option"
+ atf_set "require.user" "root"
+ atf_set "timeout" 15
+}
+physpath_body()
+{
+ load_gnop
+ us=$(alloc_md)
+ physpath="some/physical/path"
+ atf_check gnop create -z $physpath /dev/${us}
+ gnop_physpath=$(diskinfo -p ${us}.nop)
+ atf_check_equal "$physpath" "$gnop_physpath"
+}
+physpath_cleanup()
+{
+ common_cleanup
+}
+
+atf_test_case physpath_blank cleanup
+physpath_blank_head()
+{
+ atf_set "descr" "gnop can set physical path to the empty string"
+ atf_set "require.user" "root"
+ atf_set "require.config" "disks"
+ atf_set "timeout" 15
+}
+physpath_blank_body()
+{
+ load_gnop
+ disks=`atf_config_get disks`
+ disk=${disks%% *}
+ if [ -z "$disk" ]; then
+ atf_skip "Must define disks (see tests(7))"
+ fi
+
+ atf_check gnop create -z "" ${disk}
+ gnop_physpath=$(diskinfo -p ${disk}.nop)
+ atf_check_equal "" "$gnop_physpath"
+}
+physpath_blank_cleanup()
+{
+ disk_cleanup
+ common_cleanup
+}
+
+atf_test_case size cleanup
+size_head()
+{
+ atf_set "descr" "Test gnop's -s option"
+ atf_set "require.user" "root"
+ atf_set "timeout" 15
+}
+size_body()
+{
+ load_gnop
+ us=$(alloc_md)
+ for mediasize in 65536 524288 1048576; do
+ atf_check gnop create -s ${mediasize} /dev/${us}
+ gnop_mediasize=`diskinfo /dev/${us}.nop | cut -wf 3`
+ atf_check_equal "${mediasize}" "${gnop_mediasize}"
+ atf_check gnop destroy /dev/${us}.nop
+ done
+ # We shouldn't be able to extend the provider's size
+ atf_check -s not-exit:0 -e ignore gnop create -s 2097152 /dev/${us}
+}
+size_cleanup()
+{
+ common_cleanup
+}
+
+atf_test_case stripesize cleanup
+stripesize_head()
+{
+ atf_set "descr" "Test gnop's -p and -P options"
+ atf_set "require.user" "root"
+ atf_set "timeout" 120
+}
+stripesize_body()
+{
+ load_gnop
+ us=$(alloc_md)
+ for ss in 512 1024 2048 4096 8192; do
+ for sofs in `seq 0 512 ${ss}`; do
+ [ "$sofs" -eq "$ss" ] && continue
+ atf_check gnop create -p ${ss} -P ${sofs} /dev/${us}
+ gnop_ss=`diskinfo /dev/${us}.nop | cut -wf 5`
+ gnop_sofs=`diskinfo /dev/${us}.nop | cut -wf 6`
+ atf_check_equal "${ss}" "${gnop_ss}"
+ atf_check_equal "${sofs}" "${gnop_sofs}"
+ atf_check gnop destroy /dev/${us}.nop
+ done
+ done
+}
+stripesize_cleanup()
+{
+ common_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case io
+ atf_add_test_case physpath
+ atf_add_test_case physpath_blank
+ atf_add_test_case preserve_props
+ atf_add_test_case preserve_disk_props
+ atf_add_test_case stripesize
+ atf_add_test_case size
+}
+
+alloc_md()
+{
+ local md
+
+ md=$(mdconfig -a -t swap -s 1M) || atf_fail "mdconfig -a failed"
+ echo ${md} >> $MD_DEVS
+ echo ${md}
+}
+
+common_cleanup()
+{
+ if [ -f "$MD_DEVS" ]; then
+ while read test_md; do
+ gnop destroy -f ${test_md}.nop 2>/dev/null
+ mdconfig -d -u $test_md 2>/dev/null
+ done < $MD_DEVS
+ rm $MD_DEVS
+ fi
+
+ if [ -f "$PLAINFILES" ]; then
+ while read f; do
+ rm -f ${f}
+ done < ${PLAINFILES}
+ rm ${PLAINFILES}
+ fi
+ true
+}
+
+disk_cleanup()
+{
+ disks=`atf_config_get disks`
+ disk=${disks%% *}
+ if [ -n "$disk" ]; then
+ gnop destroy -f ${disk}.nop 2>/dev/null
+ fi
+}
+
+load_gnop()
+{
+ if ! kldstat -q -m g_nop; then
+ geom nop load || atf_skip "could not load module for geom nop"
+ fi
+}
diff --git a/tests/sys/geom/class/part/Makefile b/tests/sys/geom/class/part/Makefile
new file mode 100644
index 000000000000..0d7d86224437
--- /dev/null
+++ b/tests/sys/geom/class/part/Makefile
@@ -0,0 +1,8 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/geom/class/${.CURDIR:T}
+
+# TODO: port the perl tests in tools/regression/geom_gpt
+ATF_TESTS_SH+= misc
+
+.include <bsd.test.mk>
diff --git a/tests/sys/geom/class/part/misc.sh b/tests/sys/geom/class/part/misc.sh
new file mode 100644
index 000000000000..0ca243a45a1c
--- /dev/null
+++ b/tests/sys/geom/class/part/misc.sh
@@ -0,0 +1,187 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2018 Alan Somers
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+MD_DEVS="md.devs"
+
+atf_test_case blank_physpath cleanup
+blank_physpath_head()
+{
+ atf_set "descr" "gpart shouldn't add physical paths to underlying providers that have none"
+ atf_set "require.user" "root"
+}
+blank_physpath_body()
+{
+ load_gnop
+ load_gpart
+ md=$(alloc_md)
+ atf_check -o empty -e ignore diskinfo -p ${md}
+ atf_check -s exit:0 -o ignore gpart create -s bsd ${md}
+ atf_check -s exit:0 -o ignore gpart add -t freebsd-ufs ${md}
+ atf_check -o empty -e ignore diskinfo -p ${md}a
+}
+blank_physpath_cleanup()
+{
+ common_cleanup
+}
+
+
+atf_test_case bsd_physpath cleanup
+bsd_physpath_head()
+{
+ atf_set "descr" "BSD partitions should append /X to the underlying device's physical path"
+ atf_set "require.user" "root"
+}
+bsd_physpath_body()
+{
+ load_gnop
+ load_gpart
+ md=$(alloc_md)
+ physpath="some/physical/path"
+ atf_check gnop create -z $physpath /dev/${md}
+ atf_check -s exit:0 -o ignore gpart create -s bsd ${md}.nop
+ atf_check -s exit:0 -o ignore gpart add -t freebsd-ufs ${md}.nop
+ gpart_physpath=$(diskinfo -p ${md}.nopa)
+ atf_check_equal "${physpath}/a" "$gpart_physpath"
+}
+bsd_physpath_cleanup()
+{
+ common_cleanup
+}
+
+atf_test_case gpt_physpath cleanup
+gpt_physpath_head()
+{
+ atf_set "descr" "GPT partitions should append /pX to the underlying device's physical path"
+ atf_set "require.user" "root"
+}
+gpt_physpath_body()
+{
+ load_gnop
+ load_gpart
+ md=$(alloc_md)
+ physpath="some/physical/path"
+ atf_check gnop create -z $physpath /dev/${md}
+ atf_check -s exit:0 -o ignore gpart create -s gpt ${md}.nop
+ atf_check -s exit:0 -o ignore gpart add -t efi ${md}.nop
+ gpart_physpath=$(diskinfo -p ${md}.nopp1)
+ atf_check_equal "${physpath}/p1" "$gpart_physpath"
+}
+gpt_physpath_cleanup()
+{
+ common_cleanup
+}
+
+atf_test_case mbr_physpath cleanup
+mbr_physpath_head()
+{
+ atf_set "descr" "MBR partitions should append /sX to the underlying device's physical path"
+ atf_set "require.user" "root"
+}
+mbr_physpath_body()
+{
+ load_gnop
+ load_gpart
+ md=$(alloc_md)
+ physpath="some/physical/path"
+ atf_check gnop create -z $physpath /dev/${md}
+ atf_check -s exit:0 -o ignore gpart create -s mbr ${md}.nop
+ atf_check -s exit:0 -o ignore gpart add -t freebsd ${md}.nop
+ gpart_physpath=$(diskinfo -p ${md}.nops1)
+ atf_check_equal "${physpath}/s1" "$gpart_physpath"
+}
+mbr_physpath_cleanup()
+{
+ common_cleanup
+}
+
+atf_test_case mbr_bsd_physpath cleanup
+mbr_bsd_physpath_head()
+{
+ atf_set "descr" "BSD partitions nested within MBR partitions should append /sX/Y to the underlying device's physical path"
+ atf_set "require.user" "root"
+}
+mbr_bsd_physpath_body()
+{
+ load_gnop
+ load_gpart
+ md=$(alloc_md)
+ physpath="some/physical/path"
+ atf_check gnop create -z $physpath /dev/${md}
+ atf_check -s exit:0 -o ignore gpart create -s mbr ${md}.nop
+ atf_check -s exit:0 -o ignore gpart add -t freebsd ${md}.nop
+ atf_check -s exit:0 -o ignore gpart create -s bsd ${md}.nops1
+ atf_check -s exit:0 -o ignore gpart add -t freebsd-ufs ${md}.nops1
+ gpart_physpath=$(diskinfo -p ${md}.nops1a)
+ atf_check_equal "${physpath}/s1/a" "$gpart_physpath"
+}
+mbr_bsd_physpath_cleanup()
+{
+ common_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case blank_physpath
+ atf_add_test_case bsd_physpath
+ atf_add_test_case gpt_physpath
+ atf_add_test_case mbr_physpath
+ atf_add_test_case mbr_bsd_physpath
+}
+
+alloc_md()
+{
+ local md
+
+ md=$(mdconfig -a -t swap -s 1M) || atf_fail "mdconfig -a failed"
+ echo ${md} >> $MD_DEVS
+ echo ${md}
+}
+
+common_cleanup()
+{
+ if [ -f "$MD_DEVS" ]; then
+ while read test_md; do
+ gnop destroy -f ${test_md}.nop 2>/dev/null
+ mdconfig -d -u $test_md 2>/dev/null
+ done < $MD_DEVS
+ rm $MD_DEVS
+ fi
+ true
+}
+
+load_gpart()
+{
+ if ! kldstat -q -m g_part; then
+ geom part load || atf_skip "could not load module for geom part"
+ fi
+}
+
+load_gnop()
+{
+ if ! kldstat -q -m g_nop; then
+ geom nop load || atf_skip "could not load module for geom nop"
+ fi
+}
diff --git a/tests/sys/geom/class/raid3/10_test.sh b/tests/sys/geom/class/raid3/10_test.sh
new file mode 100644
index 000000000000..ec7c47253465
--- /dev/null
+++ b/tests/sys/geom/class/raid3/10_test.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+. `dirname $0`/conf.sh
+
+echo "1..1"
+
+ddbs=2048
+nblocks1=1024
+nblocks2=`expr $nblocks1 / \( $ddbs / 512 \)`
+src=`mktemp $base.XXXXXX` || exit 1
+dst=`mktemp $base.XXXXXX` || exit 1
+
+attach_md us0 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+attach_md us1 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+attach_md us2 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+
+dd if=/dev/random of=${src} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+graid3 label -r $name /dev/${us0} /dev/${us1} /dev/${us2} || exit 1
+devwait
+
+dd if=${src} of=/dev/raid3/${name} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+dd if=/dev/raid3/${name} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 1"
+else
+ echo "ok 1"
+fi
+
+rm -f ${src} ${dst}
diff --git a/tests/sys/geom/class/raid3/11_test.sh b/tests/sys/geom/class/raid3/11_test.sh
new file mode 100644
index 000000000000..73aa1cc7f982
--- /dev/null
+++ b/tests/sys/geom/class/raid3/11_test.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+. `dirname $0`/conf.sh
+
+echo "1..1"
+
+ddbs=2048
+nblocks1=1024
+nblocks2=`expr $nblocks1 / \( $ddbs / 512 \)`
+src=`mktemp $base.XXXXXX` || exit 1
+dst=`mktemp $base.XXXXXX` || exit 1
+
+attach_md us0 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+attach_md us1 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+attach_md us2 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+
+dd if=/dev/random of=${src} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+graid3 label -w $name /dev/${us0} /dev/${us1} /dev/${us2} || exit 1
+devwait
+
+dd if=${src} of=/dev/raid3/${name} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+dd if=/dev/raid3/${name} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 1"
+else
+ echo "ok 1"
+fi
+
+rm -f ${src} ${dst}
diff --git a/tests/sys/geom/class/raid3/12_test.sh b/tests/sys/geom/class/raid3/12_test.sh
new file mode 100644
index 000000000000..4284ac616ac1
--- /dev/null
+++ b/tests/sys/geom/class/raid3/12_test.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+. `dirname $0`/conf.sh
+
+echo "1..1"
+
+nblocks1=9
+nblocks2=`expr $nblocks1 - 1`
+nblocks3=`expr $nblocks2 / 2`
+
+attach_md us0 -t malloc -s $nblocks1 || exit 1
+attach_md us1 -t malloc -s $nblocks1 || exit 1
+attach_md us2 -t malloc -s $nblocks1 || exit 1
+
+dd if=/dev/random of=/dev/${us0} count=$nblocks1 >/dev/null 2>&1
+dd if=/dev/random of=/dev/${us1} count=$nblocks1 >/dev/null 2>&1
+dd if=/dev/random of=/dev/${us2} count=$nblocks1 >/dev/null 2>&1
+
+graid3 label -w $name /dev/${us0} /dev/${us1} /dev/${us2} || exit 1
+devwait
+# Wait for synchronization.
+sleep 2
+graid3 stop $name
+# Break one component.
+dd if=/dev/random of=/dev/${us1} count=$nblocks2 >/dev/null 2>&1
+# Provoke retaste of the rest components.
+true > /dev/${us0}
+true > /dev/${us2}
+sleep 1
+
+dd if=/dev/raid3/${name} of=/dev/null bs=1k count=$nblocks3 >/dev/null 2>&1
+ec=$?
+if [ $ec -eq 0 ]; then
+ echo "not ok 1"
+else
+ echo "ok 1"
+fi
diff --git a/tests/sys/geom/class/raid3/1_test.sh b/tests/sys/geom/class/raid3/1_test.sh
new file mode 100644
index 000000000000..1df627114135
--- /dev/null
+++ b/tests/sys/geom/class/raid3/1_test.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+. `dirname $0`/conf.sh
+
+echo "1..2"
+
+attach_md us0 -t malloc -s 1M || exit 1
+attach_md us1 -t malloc -s 2M || exit 1
+attach_md us2 -t malloc -s 3M || exit 1
+
+graid3 label $name /dev/${us0} /dev/${us1} /dev/${us2} 2>/dev/null || exit 1
+devwait
+
+# Size of created device should be 2MB - 1024B.
+
+mediasize=`diskinfo /dev/raid3/${name} | awk '{print $3}'`
+if [ $mediasize -eq 2096128 ]; then
+ echo "ok 1"
+else
+ echo "not ok 1"
+fi
+sectorsize=`diskinfo /dev/raid3/${name} | awk '{print $2}'`
+if [ $sectorsize -eq 1024 ]; then
+ echo "ok 2"
+else
+ echo "not ok 2"
+fi
diff --git a/tests/sys/geom/class/raid3/2_test.sh b/tests/sys/geom/class/raid3/2_test.sh
new file mode 100644
index 000000000000..db16542f77ed
--- /dev/null
+++ b/tests/sys/geom/class/raid3/2_test.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+. `dirname $0`/conf.sh
+
+echo "1..1"
+
+ddbs=2048
+nblocks1=1024
+nblocks2=`expr $nblocks1 / \( $ddbs / 512 \)`
+src=`mktemp $base.XXXXXX` || exit 1
+dst=`mktemp $base.XXXXXX` || exit 1
+
+attach_md us0 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+attach_md us1 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+attach_md us2 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+
+dd if=/dev/random of=${src} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+graid3 label $name /dev/${us0} /dev/${us1} /dev/${us2} || exit 1
+devwait
+
+dd if=${src} of=/dev/raid3/${name} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+dd if=/dev/raid3/${name} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 1"
+else
+ echo "ok 1"
+fi
+
+rm -f ${src} ${dst}
diff --git a/tests/sys/geom/class/raid3/3_test.sh b/tests/sys/geom/class/raid3/3_test.sh
new file mode 100644
index 000000000000..64d7d5caaaf3
--- /dev/null
+++ b/tests/sys/geom/class/raid3/3_test.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+. `dirname $0`/conf.sh
+
+echo "1..1"
+
+ddbs=2048
+nblocks1=1024
+nblocks2=`expr $nblocks1 / \( $ddbs / 512 \)`
+src=`mktemp $base.XXXXXX` || exit 1
+dst=`mktemp $base.XXXXXX` || exit 1
+
+attach_md us0 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+attach_md us1 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+attach_md us2 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+
+dd if=/dev/random of=${src} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+graid3 label $name /dev/${us0} /dev/${us1} /dev/${us2} || exit 1
+devwait
+
+dd if=${src} of=/dev/raid3/${name} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+#
+# Reading without one DATA component (so with parity).
+#
+graid3 remove -n 1 $name
+dd if=/dev/raid3/${name} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 1"
+else
+ echo "ok 1"
+fi
+
+rm -f ${src} ${dst}
diff --git a/tests/sys/geom/class/raid3/4_test.sh b/tests/sys/geom/class/raid3/4_test.sh
new file mode 100644
index 000000000000..749729407368
--- /dev/null
+++ b/tests/sys/geom/class/raid3/4_test.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+. `dirname $0`/conf.sh
+
+echo "1..1"
+
+ddbs=2048
+nblocks1=1024
+nblocks2=`expr $nblocks1 / \( $ddbs / 512 \)`
+src=`mktemp $base.XXXXXX` || exit 1
+dst=`mktemp $base.XXXXXX` || exit 1
+
+attach_md us0 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+attach_md us1 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+attach_md us2 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+
+dd if=/dev/random of=${src} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+graid3 label $name /dev/${us0} /dev/${us1} /dev/${us2} || exit 1
+devwait
+
+#
+# Writing without one DATA component.
+#
+graid3 remove -n 1 $name
+dd if=${src} of=/dev/raid3/${name} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+dd if=/dev/raid3/${name} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 1"
+else
+ echo "ok 1"
+fi
+
+rm -f ${src} ${dst}
diff --git a/tests/sys/geom/class/raid3/5_test.sh b/tests/sys/geom/class/raid3/5_test.sh
new file mode 100644
index 000000000000..76d90cb60754
--- /dev/null
+++ b/tests/sys/geom/class/raid3/5_test.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+. `dirname $0`/conf.sh
+
+echo "1..1"
+
+ddbs=2048
+nblocks1=1024
+nblocks2=`expr $nblocks1 / \( $ddbs / 512 \)`
+src=`mktemp $base.XXXXXX` || exit 1
+dst=`mktemp $base.XXXXXX` || exit 1
+
+attach_md us0 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+attach_md us1 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+attach_md us2 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+
+dd if=/dev/random of=${src} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+graid3 label $name /dev/${us0} /dev/${us1} /dev/${us2} || exit 1
+devwait
+
+#
+# Writing without PARITY component.
+#
+graid3 remove -n 2 $name
+dd if=${src} of=/dev/raid3/${name} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+dd if=/dev/raid3/${name} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 1"
+else
+ echo "ok 1"
+fi
+
+rm -f ${src} ${dst}
diff --git a/tests/sys/geom/class/raid3/6_test.sh b/tests/sys/geom/class/raid3/6_test.sh
new file mode 100644
index 000000000000..4c1d3288b82b
--- /dev/null
+++ b/tests/sys/geom/class/raid3/6_test.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+. `dirname $0`/conf.sh
+
+echo "1..1"
+
+ddbs=2048
+nblocks1=1024
+nblocks2=`expr $nblocks1 / \( $ddbs / 512 \)`
+src=`mktemp $base.XXXXXX` || exit 1
+dst=`mktemp $base.XXXXXX` || exit 1
+
+attach_md us0 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+attach_md us1 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+attach_md us2 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+
+dd if=/dev/random of=${src} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+graid3 label $name /dev/${us0} /dev/${us1} /dev/${us2} || exit 1
+devwait
+
+dd if=${src} of=/dev/raid3/${name} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+#
+# Rebuild of DATA component.
+#
+graid3 remove -n 1 $name
+dd if=/dev/zero of=/dev/${us1} bs=512 count=`expr $nblocks1 + 1` >/dev/null 2>&1
+graid3 insert -n 1 $name md${us1}
+sleep 1
+
+dd if=/dev/raid3/${name} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 1"
+else
+ echo "ok 1"
+fi
+
+rm -f ${src} ${dst}
diff --git a/tests/sys/geom/class/raid3/7_test.sh b/tests/sys/geom/class/raid3/7_test.sh
new file mode 100644
index 000000000000..53933c96cd99
--- /dev/null
+++ b/tests/sys/geom/class/raid3/7_test.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+. `dirname $0`/conf.sh
+
+echo "1..1"
+
+ddbs=2048
+nblocks1=1024
+nblocks2=`expr $nblocks1 / \( $ddbs / 512 \)`
+src=`mktemp $base.XXXXXX` || exit 1
+dst=`mktemp $base.XXXXXX` || exit 1
+
+attach_md us0 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+attach_md us1 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+attach_md us2 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+
+dd if=/dev/random of=${src} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+graid3 label $name /dev/${us0} /dev/${us1} /dev/${us2} || exit 1
+devwait
+
+dd if=${src} of=/dev/raid3/${name} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+#
+# Rebuild of PARITY component.
+#
+graid3 remove -n 2 $name
+dd if=/dev/zero of=/dev/${us2} bs=512 count=`expr $nblocks1 + 1` >/dev/null 2>&1
+graid3 insert -n 2 $name md${us2}
+sleep 1
+# Remove DATA component, so PARITY component can be used while reading.
+graid3 remove -n 1 $name
+dd if=/dev/zero of=/dev/${us1} bs=512 count=`expr $nblocks1 + 1` >/dev/null 2>&1
+
+dd if=/dev/raid3/${name} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 1"
+else
+ echo "ok 1"
+fi
+
+rm -f ${src} ${dst}
diff --git a/tests/sys/geom/class/raid3/8_test.sh b/tests/sys/geom/class/raid3/8_test.sh
new file mode 100644
index 000000000000..4c31ec0d4b1c
--- /dev/null
+++ b/tests/sys/geom/class/raid3/8_test.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+. `dirname $0`/conf.sh
+
+echo "1..1"
+
+ddbs=2048
+nblocks1=1024
+nblocks2=`expr $nblocks1 / \( $ddbs / 512 \)`
+src=`mktemp $base.XXXXXX` || exit 1
+dst=`mktemp $base.XXXXXX` || exit 1
+
+attach_md us0 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+attach_md us1 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+attach_md us2 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+
+dd if=/dev/random of=${src} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+graid3 label $name /dev/${us0} /dev/${us1} /dev/${us2} || exit 1
+devwait
+
+#
+# Writing without DATA component and rebuild of DATA component.
+#
+graid3 remove -n 1 $name
+dd if=/dev/zero of=/dev/${us1} bs=512 count=`expr $nblocks1 + 1` >/dev/null 2>&1
+dd if=${src} of=/dev/raid3/${name} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+graid3 insert -n 1 $name md${us1}
+sleep 1
+
+dd if=/dev/raid3/${name} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 1"
+else
+ echo "ok 1"
+fi
+
+rm -f ${src} ${dst}
diff --git a/tests/sys/geom/class/raid3/9_test.sh b/tests/sys/geom/class/raid3/9_test.sh
new file mode 100644
index 000000000000..880b2a20c817
--- /dev/null
+++ b/tests/sys/geom/class/raid3/9_test.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+. `dirname $0`/conf.sh
+
+echo "1..1"
+
+ddbs=2048
+nblocks1=1024
+nblocks2=`expr $nblocks1 / \( $ddbs / 512 \)`
+src=`mktemp $base.XXXXXX` || exit 1
+dst=`mktemp $base.XXXXXX` || exit 1
+
+attach_md us0 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+attach_md us1 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+attach_md us2 -t malloc -s $(expr $nblocks1 + 1) || exit 1
+
+dd if=/dev/random of=${src} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+
+graid3 label $name /dev/${us0} /dev/${us1} /dev/${us2} || exit 1
+devwait
+
+#
+# Writing without PARITY component and rebuild of PARITY component.
+#
+graid3 remove -n 2 $name
+dd if=/dev/zero of=/dev/${us2} bs=512 count=`expr $nblocks1 + 1` >/dev/null 2>&1
+dd if=${src} of=/dev/raid3/${name} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+graid3 insert -n 2 $name md${us2}
+sleep 1
+# Remove DATA component, so PARITY component can be used while reading.
+graid3 remove -n 1 $name
+dd if=/dev/zero of=/dev/${us1} bs=512 count=`expr $nblocks1 + 1` >/dev/null 2>&1
+
+dd if=/dev/raid3/${name} of=${dst} bs=$ddbs count=$nblocks2 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 1"
+else
+ echo "ok 1"
+fi
+
+rm -f ${src} ${dst}
diff --git a/tests/sys/geom/class/raid3/Makefile b/tests/sys/geom/class/raid3/Makefile
new file mode 100644
index 000000000000..8f398a0f5fec
--- /dev/null
+++ b/tests/sys/geom/class/raid3/Makefile
@@ -0,0 +1,24 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/geom/class/${.CURDIR:T}
+
+TAP_TESTS_SH+= 1_test
+TAP_TESTS_SH+= 2_test
+TAP_TESTS_SH+= 3_test
+TAP_TESTS_SH+= 4_test
+TAP_TESTS_SH+= 5_test
+TAP_TESTS_SH+= 6_test
+TAP_TESTS_SH+= 7_test
+TAP_TESTS_SH+= 8_test
+TAP_TESTS_SH+= 9_test
+TAP_TESTS_SH+= 10_test
+TAP_TESTS_SH+= 11_test
+TAP_TESTS_SH+= 12_test
+
+${PACKAGE}FILES+= conf.sh
+
+.for t in ${TAP_TESTS_SH}
+TEST_METADATA.$t+= required_user="root"
+.endfor
+
+.include <bsd.test.mk>
diff --git a/tests/sys/geom/class/raid3/Makefile.depend b/tests/sys/geom/class/raid3/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/geom/class/raid3/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/geom/class/raid3/conf.sh b/tests/sys/geom/class/raid3/conf.sh
new file mode 100644
index 000000000000..c33e1c998b78
--- /dev/null
+++ b/tests/sys/geom/class/raid3/conf.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+name="$(mktemp -u graid3.XXXXXX)"
+class="raid3"
+base=`basename $0`
+
+graid3_test_cleanup()
+{
+ [ -c /dev/$class/$name ] && graid3 stop $name
+ geom_test_cleanup
+}
+trap graid3_test_cleanup ABRT EXIT INT TERM
+
+. `dirname $0`/../geom_subr.sh
diff --git a/tests/sys/geom/class/shsec/1_test.sh b/tests/sys/geom/class/shsec/1_test.sh
new file mode 100644
index 000000000000..93e1c02e7cf9
--- /dev/null
+++ b/tests/sys/geom/class/shsec/1_test.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+. `dirname $0`/conf.sh
+
+echo "1..2"
+
+attach_md us0 -t malloc -s 1M || exit 1
+attach_md us1 -t malloc -s 2M || exit 1
+attach_md us2 -t malloc -s 3M || exit 1
+
+gshsec label $name /dev/${us0} /dev/${us1} /dev/${us2} 2>/dev/null || exit 1
+devwait
+
+# Size of created device should be 1MB - 512B.
+
+mediasize=`diskinfo /dev/shsec/${name} | awk '{print $3}'`
+if [ $mediasize -eq 1048064 ]; then
+ echo "ok 1"
+else
+ echo "not ok 1"
+fi
+sectorsize=`diskinfo /dev/shsec/${name} | awk '{print $2}'`
+if [ $sectorsize -eq 512 ]; then
+ echo "ok 2"
+else
+ echo "not ok 2"
+fi
diff --git a/tests/sys/geom/class/shsec/2_test.sh b/tests/sys/geom/class/shsec/2_test.sh
new file mode 100644
index 000000000000..4f4ab3404ee0
--- /dev/null
+++ b/tests/sys/geom/class/shsec/2_test.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+. `dirname $0`/conf.sh
+
+echo "1..4"
+
+nblocks1=1024
+nblocks2=`expr $nblocks1 + 1`
+src=`mktemp $base.XXXXXX` || exit 1
+dst=`mktemp $base.XXXXXX` || exit 1
+
+dd if=/dev/random of=${src} count=$nblocks1 >/dev/null 2>&1
+
+attach_md us0 -t malloc -s $nblocks2 || exit 1
+attach_md us1 -t malloc -s $nblocks2 || exit 1
+attach_md us2 -t malloc -s $nblocks2 || exit 1
+
+gshsec label $name /dev/$us0 /dev/$us1 /dev/$us2 || exit 1
+devwait
+
+dd if=${src} of=/dev/shsec/${name} count=$nblocks1 >/dev/null 2>&1
+
+dd if=/dev/shsec/${name} of=${dst} count=$nblocks1 >/dev/null 2>&1
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 1"
+else
+ echo "ok 1"
+fi
+
+dd if=/dev/${us0} of=${dst} count=$nblocks1 >/dev/null 2>&1
+if [ `md5 -q ${src}` = `md5 -q ${dst}` ]; then
+ echo "not ok 2"
+else
+ echo "ok 2"
+fi
+
+dd if=/dev/${us1} of=${dst} count=$nblocks1 >/dev/null 2>&1
+if [ `md5 -q ${src}` = `md5 -q ${dst}` ]; then
+ echo "not ok 3"
+else
+ echo "ok 3"
+fi
+
+dd if=/dev/${us2} of=${dst} count=$nblocks1 >/dev/null 2>&1
+if [ `md5 -q ${src}` = `md5 -q ${dst}` ]; then
+ echo "not ok 4"
+else
+ echo "ok 4"
+fi
+
+rm -f ${src} ${dst}
diff --git a/tests/sys/geom/class/shsec/Makefile b/tests/sys/geom/class/shsec/Makefile
new file mode 100644
index 000000000000..500c14cca51a
--- /dev/null
+++ b/tests/sys/geom/class/shsec/Makefile
@@ -0,0 +1,14 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/geom/class/${.CURDIR:T}
+
+TAP_TESTS_SH+= 1_test
+TAP_TESTS_SH+= 2_test
+
+${PACKAGE}FILES+= conf.sh
+
+.for t in ${TAP_TESTS_SH}
+TEST_METADATA.$t+= required_user="root"
+.endfor
+
+.include <bsd.test.mk>
diff --git a/tests/sys/geom/class/shsec/Makefile.depend b/tests/sys/geom/class/shsec/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/geom/class/shsec/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/geom/class/shsec/conf.sh b/tests/sys/geom/class/shsec/conf.sh
new file mode 100644
index 000000000000..ac78d1451c8c
--- /dev/null
+++ b/tests/sys/geom/class/shsec/conf.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+name="$(mktemp -u shsec.XXXXXX)"
+class="shsec"
+base=`basename $0`
+
+shsec_test_cleanup()
+{
+ [ -c /dev/$class/$name ] && gshsec stop $name
+ geom_test_cleanup
+}
+trap shsec_test_cleanup ABRT EXIT INT TERM
+
+. `dirname $0`/../geom_subr.sh
diff --git a/tests/sys/geom/class/stripe/1_test.sh b/tests/sys/geom/class/stripe/1_test.sh
new file mode 100644
index 000000000000..a717866a0c5c
--- /dev/null
+++ b/tests/sys/geom/class/stripe/1_test.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+. `dirname $0`/conf.sh
+
+echo "1..1"
+
+attach_md us0 -t malloc -s 1M || exit 1
+attach_md us1 -t malloc -s 2M || exit 1
+attach_md us2 -t malloc -s 3M || exit 1
+
+gstripe create -s 16384 $name /dev/$us0 /dev/$us1 /dev/$us2 || exit 1
+devwait
+
+# Size of created device should be 1MB * 3.
+
+size=`diskinfo /dev/stripe/${name} | awk '{print $3}'`
+
+if [ $size -eq 3145728 ]; then
+ echo "ok 1"
+else
+ echo "not ok 1"
+fi
diff --git a/tests/sys/geom/class/stripe/2_test.sh b/tests/sys/geom/class/stripe/2_test.sh
new file mode 100644
index 000000000000..c4b1f2344629
--- /dev/null
+++ b/tests/sys/geom/class/stripe/2_test.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+. `dirname $0`/conf.sh
+
+echo "1..1"
+
+tsize=3
+src=`mktemp $base.XXXXXX` || exit 1
+dst=`mktemp $base.XXXXXX` || exit 1
+
+dd if=/dev/random of=${src} bs=1m count=$tsize >/dev/null 2>&1
+
+attach_md us0 -t malloc -s 1M || exit 1
+attach_md us1 -t malloc -s 2M || exit 1
+attach_md us2 -t malloc -s 3M || exit 1
+
+gstripe create -s 8192 $name /dev/$us0 /dev/$us1 /dev/$us2 || exit 1
+devwait
+
+dd if=${src} of=/dev/stripe/${name} bs=1m count=$tsize >/dev/null 2>&1
+dd if=/dev/stripe/${name} of=${dst} bs=1m count=$tsize >/dev/null 2>&1
+
+if [ `md5 -q ${src}` != `md5 -q ${dst}` ]; then
+ echo "not ok 1"
+else
+ echo "ok 1"
+fi
+
+rm -f ${src} ${dst}
diff --git a/tests/sys/geom/class/stripe/Makefile b/tests/sys/geom/class/stripe/Makefile
new file mode 100644
index 000000000000..500c14cca51a
--- /dev/null
+++ b/tests/sys/geom/class/stripe/Makefile
@@ -0,0 +1,14 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/geom/class/${.CURDIR:T}
+
+TAP_TESTS_SH+= 1_test
+TAP_TESTS_SH+= 2_test
+
+${PACKAGE}FILES+= conf.sh
+
+.for t in ${TAP_TESTS_SH}
+TEST_METADATA.$t+= required_user="root"
+.endfor
+
+.include <bsd.test.mk>
diff --git a/tests/sys/geom/class/stripe/Makefile.depend b/tests/sys/geom/class/stripe/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/geom/class/stripe/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/geom/class/stripe/conf.sh b/tests/sys/geom/class/stripe/conf.sh
new file mode 100644
index 000000000000..e618523ca1dc
--- /dev/null
+++ b/tests/sys/geom/class/stripe/conf.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+name="$(mktemp -u stripe.XXXXXX)"
+class="stripe"
+base=`basename $0`
+
+gstripe_test_cleanup()
+{
+ [ -c /dev/$class/$name ] && gstripe destroy $name
+ geom_test_cleanup
+}
+trap gstripe_test_cleanup ABRT EXIT INT TERM
+
+. `dirname $0`/../geom_subr.sh
diff --git a/tests/sys/geom/class/union/Makefile b/tests/sys/geom/class/union/Makefile
new file mode 100644
index 000000000000..5ab8055fd84c
--- /dev/null
+++ b/tests/sys/geom/class/union/Makefile
@@ -0,0 +1,9 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/geom/class/${.CURDIR:T}
+
+ATF_TESTS_SH+= union_test
+
+${PACKAGE}FILES+= conf.sh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/geom/class/union/conf.sh b/tests/sys/geom/class/union/conf.sh
new file mode 100644
index 000000000000..1150c7204ad3
--- /dev/null
+++ b/tests/sys/geom/class/union/conf.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+class="union"
+base=$(atf_get ident)
+
+attach_md()
+{
+ local test_md
+
+ test_md=$(mdconfig -a "$@") || atf_fail "failed to allocate md(4)"
+ echo $test_md >> $TEST_MDS_FILE || exit
+ echo $test_md
+}
+
+gunion_test_cleanup()
+{
+ if mount | grep -q "/gunionmnt"; then
+ umount gunionmnt
+ fi
+ if mount | grep -q "/uppermnt"; then
+ umount uppermnt
+ fi
+ if mount | grep -q "/lowermnt"; then
+ umount lowermnt
+ fi
+
+ if [ -e "guniondev" ]; then
+ gunion destroy "$(cat guniondev)"
+ fi
+
+ geom_test_cleanup
+}
+
+gunion_test_setup()
+{
+ geom_atf_test_setup
+}
+
+ATF_TEST=true
+. `dirname $0`/../geom_subr.sh
diff --git a/tests/sys/geom/class/union/union_test.sh b/tests/sys/geom/class/union/union_test.sh
new file mode 100644
index 000000000000..9d02cae83e8a
--- /dev/null
+++ b/tests/sys/geom/class/union/union_test.sh
@@ -0,0 +1,338 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2023 The FreeBSD Foundation
+#
+# This software was developed1 by Yan-Hao Wang <bses30074@gmail.com>
+# under sponsorship from the FreeBSD Foundation.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/conf.sh
+
+atf_test_case create cleanup
+create_head()
+{
+ atf_set "descr" "Test gunion create and destroy"
+ atf_set "require.user" "root"
+}
+create_body()
+{
+ gunion_test_setup
+
+ attach_md upperdev -s 1m
+ attach_md lowerdev -s 1m
+ newfs -U "/dev/${lowerdev}"
+
+ atf_check gunion create "$upperdev" "$lowerdev"
+ guniondev="${upperdev}-${lowerdev}.union"
+ atf_check -o inline:"/dev/${guniondev}\n" ls "/dev/${guniondev}"
+ atf_check -o ignore fsck -p -f "/dev/${guniondev}"
+
+ atf_check gunion destroy "$guniondev"
+ atf_check -s not-exit:0 -o ignore -e ignore ls "/dev/${guniondev}"
+}
+create_cleanup()
+{
+ gunion_test_cleanup
+}
+
+atf_test_case basic cleanup
+basic_head()
+{
+ atf_set "descr" "Check gunion doesn't affect lowerdev status and lowerdev can't be mounted when being in a gunion"
+ atf_set "require.user" "root"
+}
+basic_body()
+{
+ gunion_test_setup
+
+ attach_md upperdev -s 1m
+ attach_md lowerdev -s 1m
+ newfs -U "/dev/${lowerdev}"
+ mkdir lowermnt
+ mkdir gunionmnt
+
+ mount "/dev/${lowerdev}" lowermnt
+ echo "lower file" > lower_file
+ cp lower_file lowermnt/lower_file
+ sync
+ umount lowermnt
+
+ gunion create "$upperdev" "$lowerdev"
+ guniondev="${upperdev}-${lowerdev}.union"
+ atf_check -s not-exit:0 -o ignore -e ignore mount "/dev/${lowerdev}" lowermnt
+
+ mount "/dev/${guniondev}" gunionmnt
+ echo "update lower file" >> gunionmnt/lower_file
+ echo "gunion file" > gunion_file
+ cp gunion_file gunionmnt/gunion_file
+ sync
+ umount gunionmnt
+
+ gunion destroy "$guniondev"
+ mount "/dev/${lowerdev}" lowermnt
+ checksum lowermnt/lower_file lower_file
+ atf_check -s not-exit:0 -o ignore -e ignore ls lowermnt/gunion_file
+}
+basic_cleanup()
+{
+ gunion_test_cleanup
+}
+
+atf_test_case commit cleanup
+commit_head()
+{
+ atf_set "descr" "Test basic gunion commit without option"
+ atf_set "require.user" "root"
+}
+commit_body()
+{
+ gunion_test_setup
+
+ attach_md upperdev -s 1m
+ attach_md lowerdev -s 1m
+ newfs -U "/dev/${lowerdev}"
+ mkdir lowermnt
+ mkdir gunionmnt
+
+ mount "/dev/${lowerdev}" lowermnt
+ echo "lower file" > lower_file
+ cp lower_file lowermnt/lower_file
+ sync
+ umount lowermnt
+
+ gunion create "$upperdev" "$lowerdev"
+ guniondev="${upperdev}-${lowerdev}.union"
+ mount "/dev/${guniondev}" gunionmnt
+ checksum gunionmnt/lower_file lower_file
+
+ echo "update lower file" >> lower_file
+ cp -f lower_file gunionmnt/lower_file
+ echo "gunion file" > gunion_file
+ cp gunion_file gunionmnt/gunion_file
+ sync
+ umount gunionmnt
+ atf_check gunion commit "$guniondev"
+ gunion destroy "$guniondev"
+
+ atf_check -o ignore fsck -p -f "/dev/${lowerdev}"
+ mount "/dev/${lowerdev}" lowermnt
+ checksum lowermnt/lower_file lower_file
+ checksum lowermnt/gunion_file gunion_file
+}
+commit_cleanup()
+{
+ gunion_test_cleanup
+}
+
+atf_test_case offset cleanup
+offset_head()
+{
+ atf_set "descr" "Test gunion create with -o offset option"
+ atf_set "require.user" "root"
+}
+offset_body()
+{
+ gunion_test_setup
+
+ attach_md upperdev -s 1m
+ attach_md lowerdev -s 1m
+ gpart create -s GPT "/dev/${lowerdev}"
+ gpart add -t freebsd-ufs "$lowerdev"
+ newfs "/dev/${lowerdev}p1"
+ gpt_entry_1=$(gpart show "/dev/${lowerdev}")
+ mkdir gunionmnt
+
+ secsize="$(diskinfo "/dev/${lowerdev}" | awk '{print $2}')"
+ p1_start_sector="$(gpart show -p "/dev/${lowerdev}" | grep ${lowerdev}p1 | awk '{print $1}')"
+ offset_size="$((secsize * p1_start_sector))"
+
+ gunion create -o "$offset_size" "$upperdev" "$lowerdev"
+ guniondev="${upperdev}-${lowerdev}.union"
+
+ atf_check -o ignore fsck -p -f "/dev/${guniondev}"
+ atf_check mount "/dev/${guniondev}" gunionmnt
+ umount gunionmnt
+ gunion destroy "$guniondev"
+
+ gpt_entry_2=$(gpart show "/dev/${lowerdev}")
+ atf_check_equal "$gpt_entry_1" "$gpt_entry_2"
+}
+offset_cleanup()
+{
+ gunion_test_cleanup
+}
+
+atf_test_case size cleanup
+size_head()
+{
+ atf_set "descr" "Test gunion create with -s size option"
+ atf_set "require.user" "root"
+}
+size_body()
+{
+ gunion_test_setup
+
+ attach_md upperdev -s 2m
+ attach_md lowerdev -s 1m
+ newfs -U "/dev/${lowerdev}"
+
+ gunion create -s 2m "$upperdev" "$lowerdev"
+ guniondev="${upperdev}-${lowerdev}.union"
+ echo "$guniondev" > guniondev
+
+ size="$(diskinfo "/dev/$guniondev" | awk '{print $3}')"
+ atf_check_equal "2097152" "$size" # 2 MB = 2097152 bytes
+}
+size_cleanup()
+{
+ gunion_test_cleanup
+}
+
+atf_test_case secsize cleanup
+secsize_head()
+{
+ atf_set "descr" "Test gunion create with -S secsize option"
+ atf_set "require.user" "root"
+}
+secsize_body()
+{
+ gunion_test_setup
+
+ attach_md upperdev -s 1m
+ attach_md lowerdev -s 1m
+ newfs -S 512 -U "/dev/${lowerdev}"
+ lower_secsize="$(diskinfo "/dev/${lowerdev}" | awk '{print $2}')"
+ atf_check_equal "512" "$lower_secsize"
+
+ gunion create -S 1024 "$upperdev" "$lowerdev"
+ guniondev="${upperdev}-${lowerdev}.union"
+ echo "$guniondev" > guniondev
+
+ secsize="$(diskinfo "/dev/${guniondev}" | awk '{print $2}')"
+ atf_check_equal "1024" "$secsize"
+}
+secsize_cleanup()
+{
+ gunion_test_cleanup
+}
+
+atf_test_case gunionname cleanup
+gunionname_head()
+{
+ atf_set "descr" "Test gunion create with -Z gunionname option"
+ atf_set "require.user" "root"
+}
+gunionname_body()
+{
+ gunion_test_setup
+
+ attach_md upperdev -s 1m
+ attach_md lowerdev -s 1m
+ newfs -U "/dev/${lowerdev}"
+
+ gunion create -Z gunion1 "$upperdev" "$lowerdev"
+ echo "gunion1.union" > guniondev
+ atf_check -o inline:"/dev/gunion1.union\n" ls /dev/gunion1.union
+}
+gunionname_cleanup()
+{
+ gunion_test_cleanup
+}
+
+atf_test_case revert cleanup
+revert_head()
+{
+ atf_set "descr" "Test gunion revert"
+ atf_set "require.user" "root"
+}
+revert_body()
+{
+ gunion_test_setup
+
+ attach_md upperdev -s 1m
+ attach_md lowerdev -s 1m
+ newfs -U "/dev/${lowerdev}"
+ mkdir lowermnt
+ mkdir gunionmnt
+
+ mount "/dev/${lowerdev}" lowermnt
+ echo "lower file" > lower_file
+ cp lower_file lowermnt/lower_file
+ sync
+ umount lowermnt
+
+ atf_check gunion create "$upperdev" "$lowerdev"
+ guniondev="${upperdev}-${lowerdev}.union"
+ mount "/dev/${guniondev}" gunionmnt
+
+ echo "update lower file" >> gunionmnt/lower_file
+ echo "gunion file" > gunion_file
+ cp gunion_file gunionmnt/gunion_file
+ sync
+ umount gunionmnt
+ atf_check gunion revert "$guniondev"
+
+ mount "/dev/${guniondev}" gunionmnt
+ checksum gunionmnt/lower_file lower_file
+ atf_check -s not-exit:0 -o ignore -e ignore ls gunionmnt/gunion_file
+
+ umount gunionmnt
+ gunion destroy "$guniondev"
+}
+revert_cleanup()
+{
+ gunion_test_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case create
+ atf_add_test_case basic
+ atf_add_test_case commit
+ atf_add_test_case offset
+ atf_add_test_case size
+ atf_add_test_case secsize
+ atf_add_test_case gunionname
+ atf_add_test_case revert
+}
+
+checksum()
+{
+ src=$1
+ work=$2
+
+ if [ ! -e "$src" ]; then
+ atf_fail "file not exist"
+ fi
+ if [ ! -e "$work" ]; then
+ atf_fail "file not exist"
+ fi
+
+ src_checksum=$(md5 -q "$src")
+ work_checksum=$(md5 -q "$work")
+
+ if [ "$work_checksum" != "$src_checksum" ]; then
+ atf_fail "md5 checksum didn't match with ${src} and ${work}"
+ fi
+}
diff --git a/tests/sys/geom/class/uzip/1_endian_big.img.uzip.uue b/tests/sys/geom/class/uzip/1_endian_big.img.uzip.uue
new file mode 100644
index 000000000000..cc26f399a547
--- /dev/null
+++ b/tests/sys/geom/class/uzip/1_endian_big.img.uzip.uue
@@ -0,0 +1,86 @@
+#
+#
+
+begin 644 1_endian_big.img.uzip
+M(R$O8FEN+W-H"B-6,BXP($9O<FUA=`HH:VQD<W1A="`M<6T@9U]U>FEP?'QK
+M;&1L;V%D(&=E;VU?=7II<"D^)BTF)FUO=6YT7V-D.38V,"`O9&5V+V!M9&-O
+M;F9I9R`M868@)#!@+G5Z:7`@)#$*97AI="`D/PH``````````````$``````
+M0`````````*0`````````I`````````"D`````````*0`````````I``````
+M```#;P````````-O````````!#$````````$,0````````2H````````!*@`
+M```````&T@````````B4````````",4````````(Q0````````NZ````````
+M"[H````````,@0````````R!````````#($````````,@0````````R!````
+M````#($````````,@0````````R!````````#($````````,@0````````R!
+M````````#($````````,@0````````R!````````#($````````,@0``````
+M``R!````````#($````````,@0````````R!````````#($````````,@0``
+M``````R!````````#($````````,@0````````R!````````#($````````,
+M@0````````R!````````#($````````,@0````````R!````````#($`````
+M```,@0````````R!````````#($````````,@0````````R!````````#($`
+M```````,@0````````R!````````#($````````,@0````````R!````````
+M#($````````,@0````````R!````````#*UXVNW4/0Z",!3`\3:B,FA@A,W1
+MN,C@`3R$":?0R<'!Q.H)3+R0DZOQ(L3%N1:H:``3'=#E_TM>WX.TI7P4(0J!
+MB8&)H8E(U)-"*)/\M'9M9+16)JYIZ9GHF7!LOU8>_LLT6:WJ+A`GVU/G,IH\
+M^_EOEI*.EW4K5./Y8BF`!AW3YK!W\JP>Q]5/M+Q]<NY?%BTK56#SVN;^)]/$
+MR:Y;FO!L<]3L#4R+QOXK;C]Z<N9?)E;VS2FWK75^>F.SI^L&A3,9LE,`````
+M``````````````````````#PC3L712#(>-KMU#T*PD`0AN$)_FTA)*7I+,4J
+MA0?P$()7L=+U<K;B1<0;K+/)$,6LH(4_Q?O`EUG"[F9#F(BT)IJI9J:I)"T3
+M\5J*.':66@A><XG#7#/6]&U>KTEQMTT]]JD'K,_[P_`T7]SF%4^.$M=GJ1-Z
+M`?Z"[[9/P_WD.-UV*:UNK8Y>V2;VZ,.&1ZO59U]@V5Z^3/]ELK$OY]T@A.;V
+MSFH>4HO*55;2`P```````````````````````````'C'%6?Y%ZQXVNW.L0W"
+M,!"&40<J"FHH&8-%$,M0P#89@4$8@@(Q`2+\A8-DA?04[TF?BSM9NE)BM3@<
+MGY>A?'7CLTVGM*Z+?5J6>=WXN>K3-3W2+=W3+IUKC?:&UN;G]/T:9DSNFLX`
+M``````````````````#@;WP`XC\HZGC:Y=MY*`11'`?PL>[<-AN28\D5Y:I=
+M(6W:+<+F_H-"<H3P%]:1W$H16C8*191(2,Z4L$V6M"3''XZ4*]:5<K6TN<HB
+M?Q#ZOC_F-[TW]7Z?WFN:]V:&(+ZWZ$:Z#!'`92-<'/V?\F4=$Q3YVI<J-=DA
+M0E(H?(KW;Z*0%DB>RLZ?8TV;&?F%;IF_Q*5\TJ;\4<,[[E=_91Y!XUZWRJ*L
+M<$K35O_P<,OY"]L)!?G+E`S?C/_B1Y%*ICI1/<]L'Z-X7X$MCOE"'NZ_Y%?\
+MZ0[<!.H-R/<_YQC6'K*_0^3!1_9G'52K(ON+1)-^R/Z<[DX2V2_5IA0C^]EV
+MY?7(_@D?EC6R/U2P$H?LKZ`1Y\C^S,L;Z.<_[Y)-?V2_GHBM@^R7:#CV(?N-
+M2^T'D/U)$RIKR/Z6!:UM9+\:(^@&>OY[GW0C^\?6)YG(_K:2/%-DO\V4S2"R
+MO\/\7!_9?U1&LT3V6VEVG2#[!;OQ5&1_(N<R'MF?.G=D@NS/M]CA(?OGZX^A
+MW__QI4O0^Q^5XV8^R'Z'GNA>9#^;EXS,)Z+NI'7(_HMID039K[RW,(_L]Y+<
+M'R+[&Q.VKI#]Z3-NT.L?NI1>"SW_N7[5R'[.,C\$V<\=K[%#]AM'%A@@^V-3
+M3M.1_;D<6SKT^F\V"WK_V\+1-1S9+^S7O$7VVY.COLC^B#D&]/^/`=D;P\C^
+MX++F*F1_4\8Z]/<O&0.N!<A^HS"K$60_TXC'^$_Y/@#S,;!8>-KEVETK@V$8
+M!_!E8<W*2T@M,_,R)[1ERBQ'FB*)4BQ9B2'9DLCCT&J1#9,'*TN+;3BP1M,.
+M:&Q6#C2)PF;$D)48FBDFOH03_:]/</_JOE[NKIM"^=OXR5PJH`!'.MLL0?:'
+M.>8@LG]"&FQ"]K.>-")D_X+/58OL%Z<.C2#[OYY)`MF_&]+F(ON9L3W0]]^H
+M?HE']MN,!CJRGU]^487L3UP[IB'[3Z/9M\A^1_*;'=E?[XUV(OMK.`-"9'^:
+M2O&*[%^G;EY"Y_^5&GK^HW]86Y#]#7WN'62_/Y!Q@NR_OF'HD/WV$I*![-_8
+M]Y<A^U7R/>C^_][H\2#[+<QM%[*?%Z>"[O_DMP5Z_U7ML[F1_33=L`#9S\YI
+M2T+V?W8'>,C^>\%D,[*_B`P_(/MMH?PPLM\M<\XC^\<.%,A\"FM+/([L=R0P
+MO<C^+.E<'K)?:^J"WO^:.(04V:\L5$#GOY`0=2#[E[E6/;)_<=!?@>S7!#@6
+M9+_><`C]_F-4RJ'K'Z&_@_[_W-M^#KW_JSO3SB+[4\3T?F1_ZVK,"K(_(E$Z
+MH><_JBL$7?]DI5/(_J/':>C^/Q/A%B/[V:I1_G\Z[R\+%8'F>-KMPS$-`"`0
+M`+%#`#MB$<^K8&J35JLZ\\X=````````````````````\-T#NY8`HWC:[91+
+M;]0P$,=WRR(J"PFNE3C,TDJ`M%T!%Q`<H-OR$%2J!"M5W'`V3NS6CBO;(<V%
+MQY?CRI5/P5<H8R>E%2"$Q/,P/VGC9.P9C_\SWL%@L#0X/QI.!XFEP:71TK3[
+M.#.X.#H[]14_Z.9&PX_+3`2N;34-AV%`$,0/>?O5]_#X9?F?I#/\QK+2CZ_[
+M\=S/A-G]].[]5P$_]./U/WN`^U\>?YD1_@[[RKU=/GMTU)G?]..%H^\YK<R'
+M*W0'"((@"((@B/^)5;8*:P^=$+/G6VMLE;$G&[/9@V>[.YM/7S"V+1KE89,[
+M9[5F5PMG#<RELW4I(4@!V];NJZI<?Z2Y]\"K''8E#["AU4+`0UNC82Z%$Q.X
+M<?O6S6N,O9PWW$/FE-:JG"2/&,=K%60+P;X2GFVI',K6B31;*I-I`:I*ZQJ>
+MB3ML0VLPRO@6&HR=)C+K;!F])VRCCVFL$>!XD!YL'4J'KE/&+L]$PWNG)SS+
+MA&OL8G\"I@5OJS'#=&&/-QX7X$$R%3#WN':AOQ@7/"SDF)T.5&=[=8:K7=Z=
+MR<NZ2J$*5QME:P\S-`OGJ^1[F;''Z(GB@41]7UEWP#7XQKH\GE3BVCMLVU8E
+M!&6Z+0RO#N.!"BL@"A9+$&!]G3VWX(0/(H_FK$V+Y[4)M8&`9>WT\+A7#KR1
+MZEC+Y(]ZX"RF[*.Q+@KEY?%<VB6Z3=)!3FO58+%`M`*%+:#0W.`NF_@$C%\4
+M6L6\3S5)J'4IL%8I5LPFJQW6-$_;1CF-&#.V4T6E&SN&D[>NE%TD?O*>$NI%
+MRS3/<6-1!?"56NP+MXXB+_;'46$MBA"WR`7O"Y-25\'CZ7@>ER3/DNO:',B8
+M>(:^L4V2+I+[)`?V)^\[\$2'>VPS%B38V#O<&9^:*!/<1!4SVX[9#M:?9WNQ
+M_CEOQWB1M+96=B]HN!(S6$CK0I0C5AZ[8<^VT]]R4>[^RD6A?T:"(`B"(`B"
+M(`B"(`B"(`B"(`B"(`B"(`B"(`B"(`B"^/_X#/X6=T5XVNW7+0H"01B`X=EQ
+M004Q>@3!,B`8A$V#;#$+1@4/(=C4H,VD[@FT>`9ABPB;-GD$,6@0L_B#)L$@
+MIOW>)\Q/?&>FC%(JI7*N8]23=BZN-J\-DL\>[_?_0;\7F<?0//4WW^8?5)-Z
+MGJ&)/<GOR?'CEN3^2I"=2^XOM^U><O\B\J:2^[N'25IR_R`*ZY+[>ZOE5G+_
+M-:^'DOO]TF@FN7]=LT7)_8U@UY'</RZH,[]*``````````````````````#P
+M3S?_-B4S>-KMT#$!```,`B"C&]T*>_9!!!(```````````````````!X405W
+M`]:``($`````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+=````````````````````````````````````````
+`
+end
diff --git a/tests/sys/geom/class/uzip/1_endian_little.img.uzip.uue b/tests/sys/geom/class/uzip/1_endian_little.img.uzip.uue
new file mode 100644
index 000000000000..4eca8a60ad7f
--- /dev/null
+++ b/tests/sys/geom/class/uzip/1_endian_little.img.uzip.uue
@@ -0,0 +1,109 @@
+#
+#
+
+begin 644 1_endian_little.img.uzip
+M(R$O8FEN+W-H"B-6,BXP($9O<FUA=`HH:VQD<W1A="`M<6T@9U]U>FEP?'QK
+M;&1L;V%D(&=E;VU?=7II<"D^)BTF)FUO=6YT7V-D.38V,"`O9&5V+V!M9&-O
+M;F9I9R`M868@)#!@+G5Z:7`@)#$*97AI="`D/PH``````````````$``````
+M0`````````*0`````````W<````````$G@````````>&````````!ZT`````
+M```'U`````````?[````````""(````````(20````````AP````````")<`
+M```````(O@````````CE````````"0P````````),P````````E:````````
+M"8$````````)J`````````G/````````"?8````````*'0````````I$````
+M````"FL````````*D@````````JY````````"N`````````+!P````````LN
+M````````"U4````````+?`````````NC````````"\H````````+\0``````
+M``P8````````##\````````,9@````````R-````````#+0````````,VP``
+M``````T"````````#2D````````-4`````````UW````````#9X````````-
+MQ0````````WL````````#A,````````..@````````YA````````#H@`````
+M```.KP````````[6````````#OT````````/)`````````]+````````#W(`
+M```````/F0````````_`````````#^<````````0#@```````!`U````````
+M$%P````````0@P```````!"J````````$-9XVNW4L0K",!"`X8LM$@1!!T$G
+M.[JZ.W3Q#1Q\`P??H(LZ^T@^B*L/(L1+>L6L#E:0_X-KKTU"C[8Y$0``````
+M````````\"\F&G.-2F-E]X*ZK8=[*47NA8@3FZ#77MJ(-O'P"$&>(8PU'75C
+M>M!E[3J?/4SS6D^#[-I9&I]WV%Z/J8;2HEMO\V+4-A[SQFI(4YV<^9KHS?3B
+MTJ\]LW.NRO+RQW5:+=T^:_*]^T%YJ1]D"V)?D*QG?%5M^[YGWMY3[%?AY)S7
+M(L+2>F3QGN>R]QOM%H[]`0``````````@%Z]`'B8&[EXVNW9,4H#013&\?>2
+M1=:`J(6@E2G3"I86VW@#A=Q`P1ND,:ESD13!TB9-;F'K$3R`,+ZW^U8'42$$
+M-\W_!]_N;':&?0R9:4:D<60YM0PMH_@MF=7%WE@*D9>^B$ITL.=2FK@KO[RF
+M).\I'5AST+ZSBPUKQK6=HUW9K9<]:S3]>W?7\X>ZAB+2CH]^GBK>>WL2-=1=
+M5:8"=.5XIO5?^R3NN6'6+G9<9]32KK-)OG8W**_>#[(!OB](MF?\JRK6?<?*
+MF"??K]*C:FE%I//8(_M?_32;7W=SIJP/````````````=.ZVM_]YKJ=^PO;3
+MN;K\?L;W_91K85E:GBS/EGOK<&D96Z9;57J8/[REG?F[2I^/]48C````````
+M``````#8S@?5^JSR>-KMFL]/%#$4QQ<EAC1ZY_@62!2R;((7#9J871`-DI`(
+M"7JSL].9%MHI:3L,X\W_Q:OZ!_@/>3'Q3_"UL[`<](07X7V2V7;:OA_]OLY<
+M9GN]V\W/T9VN,VV^;]Q[%]M?W[X<76U7KQ-DX=)];^U!=UWP^=-<:A?GN_NO
+MBW^.OW;-^/-_B4\0!$$0!$$0!$$0!$$0!$$0!$$0!$'<#.+7YX]X/>_-OA$3
+M!'$[B/]+N=^;GQO.^G>&>',7^S_F%I@(7-MJ&,X#:440-XUEM@PK.TZ(\<'V
+M"EMF;'<T'K]\>[2_]>8]8WNB41ZVN'-6:_:H<-;`H72V+B4$*6#/VA-5E>NO
+M-/<>>)7#D>0!1EI-!.S8&@<.I7!B`!M/GSQ>9>S#8<,]9$YIK<I!LHA^O%9!
+MMA#LF?!L6^50MDZDV5*93`M055K7\$QLLI'68)3Q+33H.TUDUMDR6@_8:.K3
+M6"/`\2`]V#J4#DV'C"V-1<.G1KL\RX1K[.1D`*8%;ZL^PW3AF#<>%^!&,A4P
+M][AVHB\')SQ,9)]==51GQW6&JUW>[<G+NDJN"E<;96L/8QP6SE?)=HFQUVB)
+MXH%$?<^L.^4:?&-='G<J<>TFV[-5"4&9+H3AU7G<4&$%1,%B"0*LK[,#"T[X
+M(/(XG+5I\6%M0FT@8%D[/3S&RH$W4EUHF>Q1#YS%E'T<K(M">7DQEZ)$LT':
+MR%6M&BP6B%:@L`44FAN,LH6_@/Z+0JN8]Y5#$FI="JQ5\A6SR6J'-<U3V"BG
+M$7W&]JNH=&/[,.MUI>P\\5D_)305+=,\Q\"B"N`K-3D1;AU%GISTH\):%"&&
+MR`6?%B:EKH+'W?$\+DF6)=>U.94Q\0QMXS%)NDCNDQQX/OGT!,YT>,&V8D&"
+MC6>'.^/3(<H$-U'%S+9]MH_UY]EQK'_.VSX^2%I;*[L.#CR,&4RD=2'*$2N/
+MI^'8ML-_\J`\N\Z#0F]&@B`(@B`(@B`(@B`(@OC_^0WE#GM@>-KMP3$!````
+MPJ#U3VT,'Z````````````````````"`MP%````!>-KMP3$!````PJ#U3VT,
+M'Z````````````````````"`MP%````!>-KMP3$!````PJ#U3VT,'Z``````
+M``````````````"`MP%````!>-KMP3$!````PJ#U3VT,'Z``````````````
+M``````"`MP%````!>-KMP3$!````PJ#U3VT,'Z````````````````````"`
+MMP%````!>-KMP3$!````PJ#U3VT,'Z````````````````````"`MP%````!
+M>-KMP3$!````PJ#U3VT,'Z````````````````````"`MP%````!>-KMP3$!
+M````PJ#U3VT,'Z````````````````````"`MP%````!>-KMP3$!````PJ#U
+M3VT,'Z````````````````````"`MP%````!>-KMP3$!````PJ#U3VT,'Z``
+M``````````````````"`MP%````!>-KMP3$!````PJ#U3VT,'Z``````````
+M``````````"`MP%````!>-KMP3$!````PJ#U3VT,'Z``````````````````
+M``"`MP%````!>-KMP3$!````PJ#U3VT,'Z````````````````````"`MP%`
+M```!>-KMP3$!````PJ#U3VT,'Z````````````````````"`MP%````!>-KM
+MP3$!````PJ#U3VT,'Z````````````````````"`MP%````!>-KMP3$!````
+MPJ#U3VT,'Z````````````````````"`MP%````!>-KMP3$!````PJ#U3VT,
+M'Z````````````````````"`MP%````!>-KMP3$!````PJ#U3VT,'Z``````
+M``````````````"`MP%````!>-KMP3$!````PJ#U3VT,'Z``````````````
+M``````"`MP%````!>-KMP3$!````PJ#U3VT,'Z````````````````````"`
+MMP%````!>-KMP3$!````PJ#U3VT,'Z````````````````````"`MP%````!
+M>-KMP3$!````PJ#U3VT,'Z````````````````````"`MP%````!>-KMP3$!
+M````PJ#U3VT,'Z````````````````````"`MP%````!>-KMP3$!````PJ#U
+M3VT,'Z````````````````````"`MP%````!>-KMP3$!````PJ#U3VT,'Z``
+M``````````````````"`MP%````!>-KMP3$!````PJ#U3VT,'Z``````````
+M``````````"`MP%````!>-KMP3$!````PJ#U3VT,'Z``````````````````
+M``"`MP%````!>-KMP3$!````PJ#U3VT,'Z````````````````````"`MP%`
+M```!>-KMP3$!````PJ#U3VT,'Z````````````````````"`MP%````!>-KM
+MP3$!````PJ#U3VT,'Z````````````````````"`MP%````!>-KMP3$!````
+MPJ#U3VT,'Z````````````````````"`MP%````!>-KMP3$!````PJ#U3VT,
+M'Z````````````````````"`MP%````!>-KMP3$!````PJ#U3VT,'Z``````
+M``````````````"`MP%````!>-KMP3$!````PJ#U3VT,'Z``````````````
+M``````"`MP%````!>-KMP3$!````PJ#U3VT,'Z````````````````````"`
+MMP%````!>-KMP3$!````PJ#U3VT,'Z````````````````````"`MP%````!
+M>-KMP3$!````PJ#U3VT,'Z````````````````````"`MP%````!>-KMP3$!
+M````PJ#U3VT,'Z````````````````````"`MP%````!>-KMP3$!````PJ#U
+M3VT,'Z````````````````````"`MP%````!>-KMP3$!````PJ#U3VT,'Z``
+M``````````````````"`MP%````!>-KMP3$!````PJ#U3VT,'Z``````````
+M``````````"`MP%````!>-KMP3$!````PJ#U3VT,'Z``````````````````
+M``"`MP%````!>-KMP3$!````PJ#U3VT,'Z````````````````````"`MP%`
+M```!>-KMP3$!````PJ#U3VT,'Z````````````````````"`MP%````!>-KM
+MP3$!````PJ#U3VT,'Z````````````````````"`MP%````!>-KMP3$!````
+MPJ#U3VT,'Z````````````````````"`MP%````!>-KMP3$!````PJ#U3VT,
+M'Z````````````````````"`MP%````!>-KMP3$!````PJ#U3VT,'Z``````
+M``````````````"`MP%````!>-KMP3$!````PJ#U3VT,'Z``````````````
+M``````"`MP%````!>-KMP3$!````PJ#U3VT,'Z````````````````````"`
+MMP%````!>-KMP3$!````PJ#U3VT,'Z````````````````````"`MP%````!
+M>-KMP3$!````PJ#U3VT,'Z````````````````````"`MP%````!>-KMP3$!
+M````PJ#U3VT,'Z````````````````````"`MP%````!>-KMP3$!````PJ#U
+M3VT,'Z````````````````````"`MP%````!>-KMP3$!````PJ#U3VT,'Z``
+M``````````````````"`MP%````!>-KMP3$!````PJ#U3VT,'Z``````````
+M``````````"`MP%````!>-KMP3$!````PJ#U3VT,'Z``````````````````
+M``"`MP%````!>-KMP3$!````PJ#U3VT,'Z````````````````````"`MP%`
+M```!>-KMP3$!````PJ#U3VT,'Z````````````````````"`MP%````!>-KM
+MP3$!````PJ#U3VT,'Z````````````````````"`MP%````!>-KMT#$!```,
+M`B"C&]T*>_9!!!(```````````````````!X405W`]:``($`````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+2````````````````````````
+`
+end
diff --git a/tests/sys/geom/class/uzip/1_test.sh b/tests/sys/geom/class/uzip/1_test.sh
new file mode 100644
index 000000000000..066cacc158b4
--- /dev/null
+++ b/tests/sys/geom/class/uzip/1_test.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+testsdir=$(dirname $0)
+. $testsdir/conf.sh
+
+# Check host endianness
+ret=$(echo I | tr -d "[:space:]" | od -to2 | head -n1 | awk '{print $2}' | cut -c6)
+if [ "$ret" = "1" ]; then
+ # Little endian
+ UUE=$testsdir/1_endian_little.img.uzip.uue
+elif [ "$ret" = "0" ]; then
+ # Big endian
+ UUE=$testsdir/1_endian_big.img.uzip.uue
+else
+ echo "Couldn't detect host endianness"
+ exit 2
+fi
+
+echo "1..1"
+
+uudecode $UUE
+attach_md us0 -f $(basename $UUE .uue) || exit 1
+sleep 1
+
+mount -o ro /dev/${us0}.uzip "${mntpoint}" || exit 1
+
+#cat "${mntpoint}/etalon.txt"
+diff -I '\$FreeBSD.*\$' -u $testsdir/etalon/etalon.txt "${mntpoint}/etalon.txt"
+if [ $? -eq 0 ]; then
+ echo "ok 1"
+else
+ echo "not ok 1"
+fi
diff --git a/tests/sys/geom/class/uzip/Makefile b/tests/sys/geom/class/uzip/Makefile
new file mode 100644
index 000000000000..3ad142dcefbb
--- /dev/null
+++ b/tests/sys/geom/class/uzip/Makefile
@@ -0,0 +1,41 @@
+#
+#
+# Regression test for geom_uzip.
+#
+
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/geom/class/${.CURDIR:T}
+
+IMAGE= 1_endian_unknown_autogenerated.img
+ZIMAGE= ${IMAGE}.uzip
+UZIMAGE= ${ZIMAGE}.uue
+
+CLEANFILES+= ${IMAGE} ${UZIMAGE} ${ZIMAGE}
+
+${IMAGE}:
+ makefs -s 1048576 ${.TARGET} ${.CURDIR}/etalon
+
+${ZIMAGE}: ${IMAGE}
+ mkuzip -o ${.TARGET} ${.ALLSRC}
+
+${UZIMAGE}: ${IMAGE} ${ZIMAGE}
+ printf "#\n# $$" >${.TARGET}
+ printf "FreeBSD$$\n#\n\n" >> ${.TARGET}
+ uuencode ${ZIMAGE} ${ZIMAGE} >>${.TARGET}
+
+${PACKAGE}FILES+= conf.sh 1_endian_big.img.uzip.uue \
+ 1_endian_little.img.uzip.uue
+
+FILESGROUPS+= etalon
+etalon+= etalon/etalon.txt
+etalonDIR= ${TESTSDIR}/etalon
+etalonPACKAGE= ${PACKAGE}
+
+TAP_TESTS_SH+= 1_test
+
+.for t in ${TAP_TESTS_SH}
+TEST_METADATA.$t+= required_user="root"
+.endfor
+
+.include <bsd.test.mk>
diff --git a/tests/sys/geom/class/uzip/Makefile.depend b/tests/sys/geom/class/uzip/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/geom/class/uzip/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/geom/class/uzip/conf.sh b/tests/sys/geom/class/uzip/conf.sh
new file mode 100644
index 000000000000..96bb70806893
--- /dev/null
+++ b/tests/sys/geom/class/uzip/conf.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+class="uzip"
+base=`basename $0`
+
+uzip_test_cleanup()
+{
+ if [ -n "$mntpoint" ]; then
+ umount $mntpoint
+ rmdir $mntpoint
+ fi
+ geom_test_cleanup
+}
+trap uzip_test_cleanup ABRT EXIT INT TERM
+
+. `dirname $0`/../geom_subr.sh
+
+# NOTE: make sure $TMPDIR has been set by geom_subr.sh if unset [by kyua, etc]
+mntpoint=$(mktemp -d tmp.XXXXXX) || exit
diff --git a/tests/sys/geom/class/uzip/etalon/etalon.txt b/tests/sys/geom/class/uzip/etalon/etalon.txt
new file mode 100644
index 000000000000..64a40398c0e2
--- /dev/null
+++ b/tests/sys/geom/class/uzip/etalon/etalon.txt
@@ -0,0 +1,42 @@
+#
+#
+
+JABBERWOCKY
+
+Lewis Carroll
+(from Through the Looking-Glass and What Alice Found There, 1872)
+
+`Twas brillig, and the slithy toves
+Did gyre and gimble in the wabe:
+All mimsy were the borogoves,
+And the mome raths outgrabe.
+
+"Beware the Jabberwock, my son!
+The jaws that bite, the claws that catch!
+Beware the Jubjub bird, and shun
+The frumious Bandersnatch!"
+
+He took his vorpal sword in hand:
+Long time the manxome foe he sought --
+So rested he by the Tumtum tree,
+And stood awhile in thought.
+
+And, as in uffish thought he stood,
+The Jabberwock, with eyes of flame,
+Came whiffling through the tulgey wood,
+And burbled as it came!
+
+One, two! One, two! And through and through
+The vorpal blade went snicker-snack!
+He left it dead, and with its head
+He went galumphing back.
+
+"And, has thou slain the Jabberwock?
+Come to my arms, my beamish boy!
+O frabjous day! Callooh! Callay!'
+He chortled in his joy.
+
+`Twas brillig, and the slithy toves
+Did gyre and gimble in the wabe;
+All mimsy were the borogoves,
+And the mome raths outgrabe.
diff --git a/tests/sys/geom/class/virstor/Makefile b/tests/sys/geom/class/virstor/Makefile
new file mode 100644
index 000000000000..67242879e33f
--- /dev/null
+++ b/tests/sys/geom/class/virstor/Makefile
@@ -0,0 +1,9 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/geom/class/${.CURDIR:T}
+
+ATF_TESTS_SH+= virstor_test
+
+${PACKAGE}FILES+= conf.sh
+
+.include <bsd.test.mk>
diff --git a/tests/sys/geom/class/virstor/conf.sh b/tests/sys/geom/class/virstor/conf.sh
new file mode 100644
index 000000000000..46b0fd1308a3
--- /dev/null
+++ b/tests/sys/geom/class/virstor/conf.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+class="virstor"
+base=$(atf_get ident)
+TEST_VIRSTOR_DEVS_FILE="${TMPDIR}/test_virstor_devs.$(basename $0)"
+
+gvirstor_dev_setup()
+{
+ # Pick a random name and record it for cleanup.
+ local vdevbase="$(mktemp -u virstor.XXXXXX)" || aft_fail "mktemp"
+ echo "$vdevbase" >> "$TEST_VIRSTOR_DEVS_FILE"
+ eval "${1}='${vdevbase}'"
+}
+
+gvirstor_test_cleanup()
+{
+ local vdevbase
+ if [ -f "$TEST_VIRSTOR_DEVS_FILE" ]; then
+ while read vdevbase; do
+ if [ -c "/dev/$class/$vdevbase" ]; then
+ echo "# Destroying test virstor device:" \
+ "$vdevbase"
+ gvirstor destroy "$vdevbase"
+ fi
+ done < "$TEST_VIRSTOR_DEVS_FILE"
+ fi
+ geom_test_cleanup
+}
+
+ATF_TEST=true
+. `dirname $0`/../geom_subr.sh
diff --git a/tests/sys/geom/class/virstor/virstor_test.sh b/tests/sys/geom/class/virstor/virstor_test.sh
new file mode 100644
index 000000000000..4f2047bffe97
--- /dev/null
+++ b/tests/sys/geom/class/virstor/virstor_test.sh
@@ -0,0 +1,73 @@
+#
+# Copyright (c) 2024 Dell Inc. or its subsidiaries. All Rights Reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+. $(atf_get_srcdir)/conf.sh
+
+atf_test_case basic cleanup
+basic_head()
+{
+ atf_set "descr" "geom virstor basic functional test"
+ atf_set "require.user" "root"
+}
+basic_body()
+{
+ geom_atf_test_setup
+ # Choose a virstor device name
+ gvirstor_dev_setup name
+
+ # Create an md backing device and initialize it with junk
+ psecsize=512
+ attach_md md -t swap -S $psecsize -s 5M || atf_fail "attach_md"
+ jot -b uninitialized 0 | dd status=none of=/dev/$md 2> /dev/null
+
+ # Create a virstor device
+ vsizemb=64
+ vsize=$((vsizemb * 1024 * 1024))
+ atf_check -o ignore -e ignore \
+ gvirstor label -v -s ${vsizemb}M -m 512 $name /dev/$md
+ devwait
+ vdev="/dev/$class/$name"
+
+ ssize=$(diskinfo $vdev | awk '{print $2}')
+ atf_check_equal $psecsize $ssize
+
+ size=$(diskinfo $vdev | awk '{print $3}')
+ atf_check_equal $vsize $size
+
+ # Write the first and last sectors of the virtual address space
+ hasha=$(jot -b a 0 | head -c $ssize | sha1)
+ hashz=$(jot -b z 0 | head -c $ssize | sha1)
+ zsector=$((vsize / ssize - 1))
+ jot -b a 0 | dd status=none of=$vdev bs=$ssize count=1 conv=notrunc
+ jot -b z 0 | dd status=none of=$vdev bs=$ssize count=1 conv=notrunc \
+ seek=$zsector
+
+ # Read back and compare
+ hashx=$(dd status=none if=$vdev bs=$ssize count=1 | sha1)
+ atf_check_equal $hasha $hashx
+ hashx=$(dd status=none if=$vdev bs=$ssize count=1 skip=$zsector | sha1)
+ atf_check_equal $hashz $hashx
+
+ # Destroy, then retaste and reload
+ atf_check -o ignore gvirstor destroy $name
+ true > /dev/$md
+ devwait
+
+ # Read back and compare
+ hashx=$(dd status=none if=$vdev bs=$ssize count=1 | sha1)
+ atf_check_equal $hasha $hashx
+ hashx=$(dd status=none if=$vdev bs=$ssize count=1 skip=$zsector | sha1)
+ atf_check_equal $hashz $hashx
+}
+basic_cleanup()
+{
+ gvirstor_test_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case basic
+}
diff --git a/tests/sys/kern/Makefile b/tests/sys/kern/Makefile
new file mode 100644
index 000000000000..336e73f29835
--- /dev/null
+++ b/tests/sys/kern/Makefile
@@ -0,0 +1,147 @@
+.include <src.opts.mk>
+
+PACKAGE= tests
+
+TESTSRC= ${SRCTOP}/contrib/netbsd-tests/kernel
+.PATH: ${SRCTOP}/sys/kern
+
+TESTSDIR= ${TESTSBASE}/sys/kern
+
+ATF_TESTS_C+= basic_signal
+.if ${MACHINE_ARCH} != "i386" && ${MACHINE_ARCH} != "powerpc" && \
+ ${MACHINE_ARCH} != "powerpcspe"
+# No support for atomic_load_64 on i386 or (32-bit) powerpc
+ATF_TESTS_C+= kcov
+.endif
+ATF_TESTS_C+= kern_copyin
+ATF_TESTS_C+= kern_descrip_test
+# One test modifies the maxfiles limit, which can cause spurious test failures.
+TEST_METADATA.kern_descrip_test+= is_exclusive="true"
+ATF_TESTS_C+= exterr_test
+ATF_TESTS_C+= fdgrowtable_test
+ATF_TESTS_C+= getdirentries_test
+ATF_TESTS_C+= jail_lookup_root
+ATF_TESTS_C+= inotify_test
+ATF_TESTS_C+= kill_zombie
+.if ${MK_OPENSSL} != "no"
+ATF_TESTS_C+= ktls_test
+.endif
+ATF_TESTS_C+= ktrace_test
+ATF_TESTS_C+= listener_wakeup
+ATF_TESTS_C+= module_test
+ATF_TESTS_C+= prace
+ATF_TESTS_C+= ptrace_test
+TEST_METADATA.ptrace_test+= timeout="15"
+ATF_TESTS_C+= reaper
+ATF_TESTS_C+= sched_affinity
+ATF_TESTS_C+= shutdown_dgram
+ATF_TESTS_C+= sigaltstack
+ATF_TESTS_C+= sigwait
+ATF_TESTS_C+= socket_accept
+ATF_TESTS_C+= socket_accf
+ATF_TESTS_C+= socket_msg_trunc
+ATF_TESTS_C+= socket_msg_waitall
+ATF_TESTS_C+= socket_splice
+TEST_METADATA.sigwait+= is_exclusive="true"
+.if ${MACHINE_ARCH} != "i386" && ${MACHINE_ARCH:Mpowerpc*} == ""
+ATF_TESTS_C+= subr_physmem_test
+.endif
+PLAIN_TESTS_C+= subr_unit_test
+ATF_TESTS_C+= sysctl_kern_proc
+ATF_TESTS_C+= sys_getrandom
+ATF_TESTS_C+= tty_pts
+ATF_TESTS_C+= unix_dgram
+ATF_TESTS_C+= unix_passfd_dgram
+TEST_METADATA.unix_passfd_dgram+= is_exclusive="true"
+ATF_TESTS_C+= unix_passfd_stream
+TEST_METADATA.unix_passfd_stream+= is_exclusive="true"
+ATF_TESTS_C+= unix_seqpacket_test
+TEST_METADATA.unix_seqpacket_test+= timeout="15"
+ATF_TESTS_C+= unix_stream
+ATF_TESTS_C+= waitpid_nohang
+ATF_TESTS_C+= pdeathsig
+ATF_TESTS_C+= sigsys
+TEST_METADATA.sigsys+= is_exclusive="true"
+
+ATF_TESTS_SH+= coredump_phnum_test
+ATF_TESTS_SH+= logsigexit_test
+ATF_TESTS_SH+= jailmeta
+ATF_TESTS_SH+= sonewconn_overflow
+TEST_METADATA.sonewconn_overflow+= required_programs="python"
+TEST_METADATA.sonewconn_overflow+= required_user="root"
+TEST_METADATA.sonewconn_overflow+= is_exclusive="true"
+ATF_TESTS_SH+= sendfile_test
+ATF_TESTS_SH+= sysctl_security_jail_children
+
+${PACKAGE}FILES+= sonewconn_overflow.py
+${PACKAGE}FILESMODE_sonewconn_overflow.py=0555
+
+BINDIR= ${TESTSDIR}
+PROGS+= coredump_phnum_helper
+PROGS+= pdeathsig_helper
+PROGS+= sendfile_helper
+
+LIBADD.jail_lookup_root+= jail util
+CFLAGS.sys_getrandom+= -I${SRCTOP}/sys/contrib/zstd/lib
+LIBADD.sys_getrandom+= zstd
+LIBADD.sys_getrandom+= c
+LIBADD.sys_getrandom+= pthread
+LIBADD.ptrace_test+= pthread
+LIBADD.unix_seqpacket_test+= pthread
+LIBADD.inotify_test+= util
+LIBADD.kcov+= pthread
+CFLAGS.ktls_test+= -DOPENSSL_API_COMPAT=0x10100000L
+LIBADD.ktls_test+= crypto util
+LIBADD.listener_wakeup+= pthread
+LIBADD.shutdown_dgram+= pthread
+LIBADD.socket_msg_waitall+= pthread
+LIBADD.socket_splice+= pthread
+LIBADD.sendfile_helper+= pthread
+LIBADD.fdgrowtable_test+= util pthread kvm procstat
+LIBADD.sigwait+= rt
+LIBADD.ktrace_test+= sysdecode
+LIBADD.unix_passfd_dgram+= jail
+LIBADD.unix_passfd_stream+= jail
+LIBADD.unix_stream+= pthread
+
+NETBSD_ATF_TESTS_C+= lockf_test
+NETBSD_ATF_TESTS_C+= mqueue_test
+NETBSD_ATF_TESTS_C+= sysv_test
+
+CFLAGS.mqueue_test+= -I${SRCTOP}/tests
+LIBADD.mqueue_test+= rt
+
+LIBADD.tty_pts+= atf_c util
+
+ATF_TESTS_C+= libkern_crc32
+SRCS.libkern_crc32+= libkern_crc32.c
+.PATH: ${SRCTOP}/sys/libkern
+SRCS.libkern_crc32+= gsb_crc32.c
+CFLAGS.libkern_crc32+= -DTESTING
+.if ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "i386"
+.PATH: ${SRCTOP}/sys/libkern/x86
+SRCS.libkern_crc32+= crc32_sse42.c
+.elif ${MACHINE_CPUARCH} == "aarch64"
+.PATH: ${SRCTOP}/sys/libkern/arm64
+SRCS.libkern_crc32+= crc32c_armv8.S
+.endif
+
+CFLAGS.subr_physmem.c+= -D_WANT_FREEBSD_BITSET
+SRCS.subr_physmem_test+= subr_physmem_test.c subr_physmem.c
+
+# subr_unit.c contains functions whose prototypes lie in headers that cannot be
+# included in userland. But as far as subr_unit_test goes, they're effectively
+# static. So it's ok to disable -Wmissing-prototypes for this program.
+CFLAGS.subr_unit.c+= -Wno-missing-prototypes
+SRCS.subr_unit_test+= subr_unit.c
+
+WARNS?= 3
+
+TESTS_SUBDIRS+= acct
+TESTS_SUBDIRS+= execve
+TESTS_SUBDIRS+= pipe
+TESTS_SUBDIRS+= tty
+
+.include <netbsd-tests.test.mk>
+
+.include <bsd.test.mk>
diff --git a/tests/sys/kern/Makefile.depend b/tests/sys/kern/Makefile.depend
new file mode 100644
index 000000000000..d5fc8cec7af9
--- /dev/null
+++ b/tests/sys/kern/Makefile.depend
@@ -0,0 +1,20 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libnetbsd \
+ lib/librt \
+ lib/libthr \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/kern/Makefile.inc b/tests/sys/kern/Makefile.inc
new file mode 100644
index 000000000000..01b5f23410c8
--- /dev/null
+++ b/tests/sys/kern/Makefile.inc
@@ -0,0 +1 @@
+.include "../Makefile.inc"
diff --git a/tests/sys/kern/acct/Makefile b/tests/sys/kern/acct/Makefile
new file mode 100644
index 000000000000..779be0ae591d
--- /dev/null
+++ b/tests/sys/kern/acct/Makefile
@@ -0,0 +1,18 @@
+TESTSDIR= ${TESTSBASE}/sys/kern/acct
+
+ATF_TESTS_C= acct_test
+
+CFLAGS+= -I${.OBJDIR}
+
+CLEANFILES+= convert.c convert.c.tmp
+
+DPSRCS.acct_test= convert.c
+acct_test.o: convert.c
+
+convert.c: ${SRCTOP}/sys/kern/kern_acct.c
+ sed -n -e 's/log(/syslog(/g' \
+ -e 's/exp/exponent/g' \
+ -e '/FLOAT_CONVERSION_START/,/FLOAT_CONVERSION_END/p' ${.ALLSRC} >${.TARGET}.tmp
+ mv ${.TARGET}.tmp ${.TARGET}
+
+.include <bsd.test.mk>
diff --git a/tests/sys/kern/acct/Makefile.depend b/tests/sys/kern/acct/Makefile.depend
new file mode 100644
index 000000000000..e1523b07ad44
--- /dev/null
+++ b/tests/sys/kern/acct/Makefile.depend
@@ -0,0 +1,18 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/msun \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/kern/acct/acct_test.c b/tests/sys/kern/acct/acct_test.c
new file mode 100644
index 000000000000..f0f016b9cc26
--- /dev/null
+++ b/tests/sys/kern/acct/acct_test.c
@@ -0,0 +1,237 @@
+/*-
+ * Copyright (c) 2011 Giorgos Keramidas. All rights reserved.
+ * Copyright (c) 2007 Diomidis Spinellis. All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <float.h>
+#include <limits.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <syslog.h>
+#include <time.h>
+
+#include <atf-c.h>
+
+#define KASSERT(val, msg) assert(val)
+
+typedef u_int32_t comp_t;
+
+#define AHZ 1000000
+
+#include "convert.c"
+
+union cf {
+ comp_t c;
+ float f;
+};
+
+static void
+check_result(const char *name, float expected, union cf v)
+{
+ double eps;
+
+ eps = fabs(expected - v.f) / expected;
+ ATF_CHECK(eps <= FLT_EPSILON);
+ if (eps > FLT_EPSILON) {
+ printf("Error in %s\n", name);
+ printf("Got 0x%08x %12g\n", v.c, v.f);
+ v.f = expected;
+ printf("Expected 0x%08x %12g (%.15lg)\n", v.c, v.f, expected);
+ printf("Epsilon=%lg, rather than %g\n", eps, FLT_EPSILON);
+ }
+}
+
+/*
+ * Test case for encoding {0 sec, 0 usec} within a reasonable epsilon.
+ */
+
+ATF_TC_WITHOUT_HEAD(encode_tv_zero);
+ATF_TC_BODY(encode_tv_zero, tc)
+{
+ union cf v;
+ struct timeval tv;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ v.c = encode_timeval(tv);
+ ATF_CHECK(fabs(v.f - 0.0) < FLT_EPSILON);
+}
+
+/*
+ * Test case for encoding a random long number.
+ */
+
+ATF_TC_WITHOUT_HEAD(encode_long);
+ATF_TC_BODY(encode_long, tc)
+{
+ union cf v;
+ long l;
+
+ l = random();
+ v.c = encode_long(l);
+ check_result(atf_tc_get_ident(tc), l, v);
+}
+
+/*
+ * Test case for encoding a small number of seconds {1 sec, 0 usec}.
+ */
+
+ATF_TC_WITHOUT_HEAD(encode_tv_only_sec);
+ATF_TC_BODY(encode_tv_only_sec, tc)
+{
+ union cf v;
+ struct timeval tv;
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ v.c = encode_timeval(tv);
+ check_result(atf_tc_get_ident(tc),
+ (float)tv.tv_sec * AHZ + tv.tv_usec, v);
+}
+
+/*
+ * Test case for encoding a small number of usec {0 sec, 1 usec}.
+ */
+
+ATF_TC_WITHOUT_HEAD(encode_tv_only_usec);
+ATF_TC_BODY(encode_tv_only_usec, tc)
+{
+ union cf v;
+ struct timeval tv;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 1;
+ v.c = encode_timeval(tv);
+ check_result(atf_tc_get_ident(tc),
+ (float)tv.tv_sec * AHZ + tv.tv_usec, v);
+}
+
+/*
+ * Test case for encoding a large number of usec {1 sec, 999.999 usec}.
+ */
+
+ATF_TC_WITHOUT_HEAD(encode_tv_many_usec);
+ATF_TC_BODY(encode_tv_many_usec, tc)
+{
+ union cf v;
+ struct timeval tv;
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 999999L;
+ v.c = encode_timeval(tv);
+ check_result(atf_tc_get_ident(tc),
+ (float)tv.tv_sec * AHZ + tv.tv_usec, v);
+}
+
+/*
+ * Test case for encoding a huge number of usec {1 sec, 1.000.000 usec} that
+ * overflows the usec counter and should show up as an increase in timeval's
+ * seconds instead.
+ */
+
+ATF_TC_WITHOUT_HEAD(encode_tv_usec_overflow);
+ATF_TC_BODY(encode_tv_usec_overflow, tc)
+{
+ union cf v;
+ struct timeval tv;
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 1000000L;
+ v.c = encode_timeval(tv);
+ check_result(atf_tc_get_ident(tc),
+ (float)tv.tv_sec * AHZ + tv.tv_usec, v);
+}
+
+/*
+ * Test case for encoding a very large number of seconds, one that is very
+ * near to the limit of 32-bit signed values. With a usec value of 999.999
+ * microseconds this should result in the largest value we can represent with
+ * a timeval struct.
+ */
+
+ATF_TC_WITHOUT_HEAD(encode_tv_upper_limit);
+ATF_TC_BODY(encode_tv_upper_limit, tc)
+{
+ union cf v;
+ struct timeval tv;
+
+ tv.tv_sec = 2147483647L;
+ tv.tv_usec = 999999L;
+ v.c = encode_timeval(tv);
+ check_result(atf_tc_get_ident(tc),
+ (float)tv.tv_sec * AHZ + tv.tv_usec, v);
+}
+
+/*
+ * Test case for encoding a million random timeval objects, and checking that
+ * the conversion does not diverge too much from the expected values.
+ */
+
+ATF_TC_WITHOUT_HEAD(encode_tv_random_million);
+ATF_TC_BODY(encode_tv_random_million, tc)
+{
+ union cf v;
+ struct timeval tv;
+ long k;
+
+#ifdef __LP64__
+ atf_tc_expect_fail("the testcase violates FLT_EPSILON on 64-bit "
+ "platforms, e.g. amd64");
+#endif
+
+ ATF_REQUIRE_MSG(unsetenv("TZ") == 0, "unsetting TZ failed; errno=%d", errno);
+
+ for (k = 1; k < 1000000L; k++) {
+ tv.tv_sec = random();
+ tv.tv_usec = (random() % 1000000L);
+ v.c = encode_timeval(tv);
+ check_result(atf_tc_get_ident(tc),
+ (float)tv.tv_sec * AHZ + tv.tv_usec, v);
+ }
+}
+
+/* ---------------------------------------------------------------------
+ * Main.
+ * --------------------------------------------------------------------- */
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, encode_long);
+ ATF_TP_ADD_TC(tp, encode_tv_zero);
+ ATF_TP_ADD_TC(tp, encode_tv_only_sec);
+ ATF_TP_ADD_TC(tp, encode_tv_only_usec);
+ ATF_TP_ADD_TC(tp, encode_tv_many_usec);
+ ATF_TP_ADD_TC(tp, encode_tv_usec_overflow);
+ ATF_TP_ADD_TC(tp, encode_tv_upper_limit);
+ ATF_TP_ADD_TC(tp, encode_tv_random_million);
+
+ return atf_no_error();
+}
diff --git a/tests/sys/kern/basic_signal.c b/tests/sys/kern/basic_signal.c
new file mode 100644
index 000000000000..1dcb9ff064fc
--- /dev/null
+++ b/tests/sys/kern/basic_signal.c
@@ -0,0 +1,225 @@
+/*-
+ * Copyright (c) 2021 M. Warner Losh <imp@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <atf-c.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#if defined(__aarch64__)
+#include <machine/armreg.h>
+#define SET_TRACE_FLAG(ucp) (ucp)->uc_mcontext.mc_gpregs.gp_spsr |= PSR_SS
+#define CLR_TRACE_FLAG(ucp) (ucp)->uc_mcontext.mc_gpregs.gp_spsr &= ~PSR_SS
+#elif defined(__amd64__)
+#include <machine/psl.h>
+#define SET_TRACE_FLAG(ucp) (ucp)->uc_mcontext.mc_rflags |= PSL_T
+#define CLR_TRACE_FLAG(ucp) (ucp)->uc_mcontext.mc_rflags &= ~PSL_T
+#elif defined(__i386__)
+#include <machine/psl.h>
+#define SET_TRACE_FLAG(ucp) (ucp)->uc_mcontext.mc_eflags |= PSL_T
+#define CLR_TRACE_FLAG(ucp) (ucp)->uc_mcontext.mc_eflags &= ~PSL_T
+#endif
+
+static volatile sig_atomic_t signal_fired = 0;
+
+static void
+sig_handler(int signo, siginfo_t *info __unused, void *ucp __unused)
+{
+ signal_fired++;
+}
+
+ATF_TC(signal_test);
+
+ATF_TC_HEAD(signal_test, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "Testing delivery of a signal");
+}
+
+ATF_TC_BODY(signal_test, tc)
+{
+ /*
+ * Setup the signal handlers
+ */
+ struct sigaction sa = {
+ .sa_sigaction = sig_handler,
+ .sa_flags = SA_SIGINFO,
+ };
+ ATF_REQUIRE(sigemptyset(&sa.sa_mask) == 0);
+ ATF_REQUIRE(sigaction(SIGUSR1, &sa, NULL) == 0);
+ ATF_REQUIRE(sigaction(SIGUSR2, &sa, NULL) == 0);
+ ATF_REQUIRE(sigaction(SIGALRM, &sa, NULL) == 0);
+
+ /*
+ * Fire SIGUSR1
+ */
+ ATF_CHECK(signal_fired == 0);
+ ATF_REQUIRE(raise(SIGUSR1) == 0);
+ ATF_CHECK(signal_fired == 1);
+
+ /*
+ * Fire SIGUSR2
+ */
+ ATF_REQUIRE(raise(SIGUSR2) == 0);
+ ATF_CHECK(signal_fired == 2);
+
+ /*
+ * Fire SIGALRM after a timeout
+ */
+ ATF_REQUIRE(alarm(1) == 0);
+ ATF_REQUIRE(pause() == -1);
+ ATF_REQUIRE(errno == EINTR);
+ ATF_CHECK(signal_fired == 3);
+}
+
+/*
+ * Check setting the machine dependent single step flag works when supported.
+ */
+#ifdef SET_TRACE_FLAG
+static volatile sig_atomic_t trap_signal_fired = 0;
+
+static void
+trap_sig_handler(int signo, siginfo_t *info __unused, void *_ucp)
+{
+ ucontext_t *ucp = _ucp;
+
+ if (trap_signal_fired < 9) {
+ SET_TRACE_FLAG(ucp);
+ } else {
+ CLR_TRACE_FLAG(ucp);
+ }
+ trap_signal_fired++;
+}
+
+ATF_TC(trap_signal_test);
+
+ATF_TC_HEAD(trap_signal_test, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr",
+ "Testing signal handler setting the MD single step flag");
+}
+
+ATF_TC_BODY(trap_signal_test, tc)
+{
+ /*
+ * Setup the signal handlers
+ */
+ struct sigaction sa = {
+ .sa_sigaction = trap_sig_handler,
+ .sa_flags = SA_SIGINFO,
+ };
+ ATF_REQUIRE(sigemptyset(&sa.sa_mask) == 0);
+ ATF_REQUIRE(sigaction(SIGTRAP, &sa, NULL) == 0);
+
+ /*
+ * Fire SIGTRAP
+ */
+ ATF_CHECK(trap_signal_fired == 0);
+ ATF_REQUIRE(raise(SIGTRAP) == 0);
+ ATF_CHECK(trap_signal_fired == 10);
+}
+#endif
+
+/*
+ * Special tests for 32-bit arm. We can call thumb code (really just t32) from
+ * normal (a32) mode and vice versa. Likewise, signals can interrupt a T32
+ * context with A32 code and vice versa. Make sure these all work with a simple
+ * test that raises the signal and ensures that it executed. No other platform
+ * has these requirements. Also note: we only support thumb2, so there's no T16
+ * vs T32 issues we have to test for.
+ */
+#ifdef __arm__
+
+#define a32_isa __attribute__((target("arm")))
+#define t32_isa __attribute__((target("thumb")))
+
+static volatile sig_atomic_t t32_fired = 0;
+static volatile sig_atomic_t a32_fired = 0;
+
+a32_isa static void
+sig_a32(int signo, siginfo_t *info __unused, void *ucp __unused)
+{
+ a32_fired++;
+}
+
+t32_isa static void
+sig_t32(int signo, siginfo_t *info __unused, void *ucp __unused)
+{
+ t32_fired++;
+}
+
+
+ATF_TC(signal_test_T32_to_A32);
+
+ATF_TC_HEAD(signal_test_T32_to_A32, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "Testing delivery of a signal from T32 to A32");
+}
+
+t32_isa ATF_TC_BODY(signal_test_T32_to_A32, tc)
+{
+ /*
+ * Setup the signal handlers
+ */
+ struct sigaction sa = {
+ .sa_sigaction = sig_a32,
+ .sa_flags = SA_SIGINFO,
+ };
+ ATF_REQUIRE(sigemptyset(&sa.sa_mask) == 0);
+ ATF_REQUIRE(sigaction(SIGUSR1, &sa, NULL) == 0);
+
+ ATF_REQUIRE((((uintptr_t)sig_a32) & 1) == 0); /* Make sure compiled as not thumb */
+
+ ATF_CHECK(a32_fired == 0);
+ ATF_REQUIRE(raise(SIGUSR1) == 0);
+ ATF_CHECK(a32_fired == 1);
+}
+
+ATF_TC(signal_test_A32_to_T32);
+
+ATF_TC_HEAD(signal_test_A32_to_T32, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "Testing delivery of a signal from A32 to T32");
+}
+
+a32_isa ATF_TC_BODY(signal_test_A32_to_T32, tc)
+{
+ /*
+ * Setup the signal handlers
+ */
+ struct sigaction sa = {
+ .sa_sigaction = sig_t32,
+ .sa_flags = SA_SIGINFO,
+ };
+ ATF_REQUIRE(sigemptyset(&sa.sa_mask) == 0);
+ ATF_REQUIRE(sigaction(SIGUSR1, &sa, NULL) == 0);
+
+ ATF_REQUIRE((((uintptr_t)sig_t32) & 1) == 1); /* Make sure compiled as thumb */
+
+ ATF_CHECK(t32_fired == 0);
+ ATF_REQUIRE(raise(SIGUSR1) == 0);
+ ATF_CHECK(t32_fired == 1);
+}
+#endif
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, signal_test);
+#ifdef SET_TRACE_FLAG
+ ATF_TP_ADD_TC(tp, trap_signal_test);
+#endif
+#ifdef __arm__
+ ATF_TP_ADD_TC(tp, signal_test_T32_to_A32);
+ ATF_TP_ADD_TC(tp, signal_test_A32_to_T32);
+#endif
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/coredump_phnum_helper.c b/tests/sys/kern/coredump_phnum_helper.c
new file mode 100644
index 000000000000..d8430bb2a7fc
--- /dev/null
+++ b/tests/sys/kern/coredump_phnum_helper.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2016, Conrad Meyer
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+
+#include <err.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/*
+ * This program is intended to create a bunch of segment mappings, then dump
+ * core.
+ */
+int
+main(int argc __unused, char **argv __unused)
+{
+ void *v;
+ size_t i, pages, page_size;
+
+ page_size = getpagesize();
+ pages = UINT16_MAX + 1000;
+ v = mmap(NULL, pages * page_size, PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_PRIVATE, -1, 0);
+ if (v == NULL)
+ err(1, "mmap");
+ for (i = 0; i < pages; i += 2) {
+ /*
+ * Alternate protections to interleave RW and R PT_LOAD
+ * segments.
+ */
+ if (mprotect((char *)v + i * page_size, page_size,
+ PROT_READ) != 0)
+ err(1, "mprotect");
+ }
+
+ /* Dump core. */
+ abort();
+}
diff --git a/tests/sys/kern/coredump_phnum_test.sh b/tests/sys/kern/coredump_phnum_test.sh
new file mode 100644
index 000000000000..a1337d5ad1fb
--- /dev/null
+++ b/tests/sys/kern/coredump_phnum_test.sh
@@ -0,0 +1,114 @@
+#
+# Copyright (c) 2016 Dell EMC Isilon
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions, and the following disclaimer,
+# without modification.
+# 2. Redistributions in binary form must reproduce at minimum a disclaimer
+# substantially similar to the "NO WARRANTY" disclaimer below
+# ("Disclaimer") and any redistribution must be conditioned upon
+# including a substantially similar Disclaimer requirement for further
+# binary redistribution.
+#
+# NO WARRANTY
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGES.
+#
+
+atf_test_case coredump_phnum cleanup
+coredump_phnum_head()
+{
+ atf_set "descr" "More than 65534 segments"
+ atf_set "require.config" "allow_sysctl_side_effects"
+ atf_set "require.progs" "readelf procstat"
+ atf_set "require.user" "root"
+}
+coredump_phnum_body()
+{
+ # Set up core dumping
+ atf_check -o save:coredump_phnum_restore_state sysctl -e \
+ kern.coredump kern.corefile
+
+ ulimit -c unlimited
+ atf_check -o ignore sysctl kern.coredump=1
+ atf_check -o ignore sysctl kern.corefile=coredump_phnum_helper.core
+ atf_check -o save:cuc sysctl -n kern.compress_user_cores
+ read cuc < cuc
+
+ atf_check -s signal:sigabrt "$(atf_get_srcdir)/coredump_phnum_helper"
+
+ case "$cuc" in
+ 0)
+ unzip_status=0
+ ;;
+ 1)
+ gunzip coredump_phnum_helper.core.gz 2>unzip_stderr
+ unzip_status=$?
+ ;;
+ 2)
+ zstd -qd coredump_phnum_helper.core.zst 2>unzip_stderr
+ unzip_status=$?
+ ;;
+ *)
+ atf_skip "unsupported kern.compress_user_cores=$cuc"
+ ;;
+ esac
+
+ if [ $unzip_status -ne 0 ]; then
+ if grep -q 'No space left on device' unzip_stderr; then
+ atf_skip "file system full: $(df $PWD | tail -n 1)"
+ fi
+ atf_fail "unzip failed; status ${unzip_status}; " \
+ "stderr: $(cat unzip_stderr)"
+ fi
+
+ # Check that core looks good
+ if [ ! -f coredump_phnum_helper.core ]; then
+ atf_fail "Helper program did not dump core"
+ fi
+
+ if readelf --version | grep -q LLVM; then
+ atf_expect_fail "PR285547: llvm-objdump does not support large phdr count"
+ # See https://github.com/llvm/llvm-project/issues/132216
+ fi
+
+ # These magic numbers don't have any real significance. They are just
+ # the result of running the helper program and dumping core. The only
+ # important bit is that they're larger than 65535 (UINT16_MAX).
+ atf_check -o "match:65535 \(66[0-9]{3}\)" \
+ -x 'readelf -h coredump_phnum_helper.core | grep "Number of program headers:"'
+ atf_check -o "match:There are 66[0-9]{3} program headers" \
+ -x 'readelf -l coredump_phnum_helper.core | grep -1 "program headers"'
+ atf_check -o "match: 00000(0000000000)?1 .* 66[0-9]{3} " \
+ -x 'readelf -S coredump_phnum_helper.core | grep -A1 "^ \[ 0\] "'
+
+ atf_check -o "match:66[0-9]{3}" \
+ -x 'procstat -v coredump_phnum_helper.core | wc -l'
+}
+coredump_phnum_cleanup()
+{
+ rm -f coredump_phnum_helper.core
+ if [ -f coredump_phnum_restore_state ]; then
+ sysctl -f coredump_phnum_restore_state
+ rm -f coredump_phnum_restore_state
+ fi
+ rm -f cuc
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case coredump_phnum
+}
diff --git a/tests/sys/kern/execve/Makefile b/tests/sys/kern/execve/Makefile
new file mode 100644
index 000000000000..afa537031c39
--- /dev/null
+++ b/tests/sys/kern/execve/Makefile
@@ -0,0 +1,38 @@
+TESTSDIR= ${TESTSBASE}/sys/kern/execve
+
+BINDIR= ${TESTSDIR}
+
+MAN=
+
+ATF_TESTS_SH+= execve_test
+
+PROGS+= good_aout
+PROGS+= execve_helper
+PROGS+= execve_argc_helper
+
+LDFLAGS.goodaout+= -static
+
+CLEANFILES+= empty
+CLEANFILES+= sparse_aout
+CLEANFILES+= trunc_aout
+
+SCRIPTS+= bad_interp_len
+SCRIPTS+= dev_null_script
+SCRIPTS+= empty
+SCRIPTS+= good_script
+SCRIPTS+= non_exist_shell
+SCRIPTS+= script_arg
+SCRIPTS+= script_arg_nospace
+SCRIPTS+= sparse_aout
+SCRIPTS+= trunc_aout
+
+empty:
+ @touch $@
+
+sparse_aout:
+ @truncate -s 20480 $@
+
+trunc_aout:
+ @truncate -s 16 $@
+
+.include <bsd.test.mk>
diff --git a/tests/sys/kern/execve/Makefile.depend b/tests/sys/kern/execve/Makefile.depend
new file mode 100644
index 000000000000..d80b3a4991d1
--- /dev/null
+++ b/tests/sys/kern/execve/Makefile.depend
@@ -0,0 +1,15 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/kern/execve/bad_interp_len b/tests/sys/kern/execve/bad_interp_len
new file mode 100644
index 000000000000..f0c503ee4d40
--- /dev/null
+++ b/tests/sys/kern/execve/bad_interp_len
@@ -0,0 +1,3 @@
+#! 456789012345678 0123456789012345 789012345678 012345678901234 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890
+
+echo succeeded
diff --git a/tests/sys/kern/execve/dev_null_script b/tests/sys/kern/execve/dev_null_script
new file mode 100644
index 000000000000..d23f9901f205
--- /dev/null
+++ b/tests/sys/kern/execve/dev_null_script
@@ -0,0 +1,3 @@
+#! /dev/null
+
+echo succeeded
diff --git a/tests/sys/kern/execve/execve_argc_helper.c b/tests/sys/kern/execve/execve_argc_helper.c
new file mode 100644
index 000000000000..519c96902fdf
--- /dev/null
+++ b/tests/sys/kern/execve/execve_argc_helper.c
@@ -0,0 +1,15 @@
+/*
+ * This file is in the public domain.
+ */
+
+#include <sys/cdefs.h>
+
+#include <stdio.h>
+
+int
+main(int argc, char **argv __unused)
+{
+
+ printf("%d\n", argc);
+ return (0);
+}
diff --git a/tests/sys/kern/execve/execve_helper.c b/tests/sys/kern/execve/execve_helper.c
new file mode 100644
index 000000000000..d17c3d2a1ee4
--- /dev/null
+++ b/tests/sys/kern/execve/execve_helper.c
@@ -0,0 +1,59 @@
+/* $NetBSD: doexec.c,v 1.8 2003/07/26 19:38:48 salo Exp $ */
+
+/*
+ * Copyright (c) 1993 Christopher G. Demetriou
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the
+ * NetBSD Project. See http://www.NetBSD.org/ for
+ * information about NetBSD.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* Passing -n == null_argv */
+static char * const null_argv[] = { NULL };
+
+int
+main(int argc, char **argv)
+{
+
+ if (argc == 2) {
+ execve(argv[1], &argv[1], NULL);
+ } else if (argc == 3 && strcmp(argv[1], "-n") == 0) {
+ execve(argv[2], null_argv, NULL);
+ } else {
+ fprintf(stderr, "usage: %s [-n] <progname>\n", argv[0]);
+ exit(2);
+ }
+
+ err(1, "execve failed");
+}
diff --git a/tests/sys/kern/execve/execve_test.sh b/tests/sys/kern/execve/execve_test.sh
new file mode 100644
index 000000000000..d3650f3472cc
--- /dev/null
+++ b/tests/sys/kern/execve/execve_test.sh
@@ -0,0 +1,133 @@
+
+bad_interp_len_head()
+{
+ atf_set "descr" "Bad interpreter length"
+}
+bad_interp_len_body()
+{
+ atf_check -s exit:1 -e 'match:No such file or directory' -o empty \
+ -x "cd $(atf_get_srcdir) && ./execve_helper bad_interp_len"
+}
+
+empty_head()
+{
+ atf_set "descr" "Empty file"
+}
+empty_body()
+{
+ atf_check -s exit:1 -e 'match:Exec format error' -o empty \
+ -x "cd $(atf_get_srcdir) && ./execve_helper empty"
+}
+
+good_aout_head()
+{
+ atf_set "descr" "Good a.out"
+}
+good_aout_body()
+{
+ atf_check -s exit:0 -e empty -o 'match:succeeded' \
+ -x "cd $(atf_get_srcdir) && ./execve_helper ./good_aout"
+}
+
+good_script_head()
+{
+ atf_set "descr" "Good script"
+}
+good_script_body()
+{
+ atf_check -s exit:0 -e empty -o 'match:succeeded' \
+ -x "cd $(atf_get_srcdir) && ./execve_helper good_script"
+}
+
+non_exist_head()
+{
+ atf_set "descr" "Non-existent file"
+}
+non_exist_body()
+{
+ atf_check -s exit:1 -e 'match:No such file or directory' -o empty \
+ -x "cd $(atf_get_srcdir) && ./execve_helper non_exist"
+}
+
+non_exist_shell_head()
+{
+ atf_set "descr" "Non-existent shell"
+}
+non_exist_shell_body()
+{
+ atf_check -s exit:1 -e 'match:No such file or directory' -o empty \
+ -x "cd $(atf_get_srcdir) && ./execve_helper non_exist_shell"
+}
+
+script_arg_head()
+{
+ atf_set "descr" "-x in the shebang"
+}
+script_arg_body()
+{
+ atf_check -s exit:0 -e 'match:\+ echo succeeded' -o 'match:succeeded' \
+ -x "cd $(atf_get_srcdir) && ./execve_helper script_arg"
+}
+
+script_arg_nospace_head()
+{
+ atf_set "descr" '-x in the shebang; no space between #! and /bin/sh'
+}
+script_arg_nospace_body()
+{
+ atf_check -s exit:0 -e 'match:\+ echo succeeded' -o 'match:succeeded' \
+ -x "cd $(atf_get_srcdir) && ./execve_helper script_arg_nospace"
+}
+
+sparse_aout_head()
+{
+ atf_set "descr" 'Sparse file'
+}
+sparse_aout_body()
+{
+ atf_check -s exit:1 -e 'match:Exec format error' -o empty \
+ -x "cd $(atf_get_srcdir) && ./execve_helper sparse_aout"
+}
+
+trunc_aout_head()
+{
+ atf_set "descr" 'Truncated file'
+}
+trunc_aout_body()
+{
+ atf_check -s exit:1 -e 'match:Exec format error' -o empty \
+ -x "cd $(atf_get_srcdir) && ./execve_helper trunc_aout"
+}
+
+empty_args_head()
+{
+ atf_set "descr" "Empty argv behavior"
+}
+empty_args_body()
+{
+ atf_check -o inline:"1\n" \
+ -x "cd $(atf_get_srcdir) && ./execve_helper execve_argc_helper"
+
+ # Historically we allowed argc == 0, while execve(2) claimed we didn't.
+ # execve() should kick back an EINVAL now. We verified the helper was
+ # there/working in the check just above.
+ atf_check -s exit:1 \
+ -e match:".+Invalid argument$" \
+ -x "cd $(atf_get_srcdir) && ./execve_helper -n execve_argc_helper"
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case bad_interp_len
+ atf_add_test_case empty
+ atf_add_test_case good_aout
+ atf_add_test_case good_script
+ atf_add_test_case non_exist
+ atf_add_test_case non_exist_shell
+ atf_add_test_case script_arg
+ atf_add_test_case script_arg_nospace
+ atf_add_test_case sparse_aout
+ atf_add_test_case trunc_aout
+ atf_add_test_case empty_args
+
+}
diff --git a/tests/sys/kern/execve/good_aout.c b/tests/sys/kern/execve/good_aout.c
new file mode 100644
index 000000000000..43f2995dbaf7
--- /dev/null
+++ b/tests/sys/kern/execve/good_aout.c
@@ -0,0 +1,43 @@
+/* $NetBSD: goodaout.c,v 1.8 2003/07/26 19:38:49 salo Exp $ */
+
+/*-
+ * Copyright (c) 1993 Christopher G. Demetriou
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the
+ * NetBSD Project. See http://www.NetBSD.org/ for
+ * information about NetBSD.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main(void)
+{
+ printf("succeeded\n");
+ exit(0);
+}
diff --git a/tests/sys/kern/execve/good_script b/tests/sys/kern/execve/good_script
new file mode 100644
index 000000000000..3120668215c0
--- /dev/null
+++ b/tests/sys/kern/execve/good_script
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo succeeded
diff --git a/tests/sys/kern/execve/non_exist_shell b/tests/sys/kern/execve/non_exist_shell
new file mode 100644
index 000000000000..d00bd83e9f12
--- /dev/null
+++ b/tests/sys/kern/execve/non_exist_shell
@@ -0,0 +1,3 @@
+#! /foo/bar/baz
+
+echo foo
diff --git a/tests/sys/kern/execve/script_arg b/tests/sys/kern/execve/script_arg
new file mode 100644
index 000000000000..d0e38ae8d743
--- /dev/null
+++ b/tests/sys/kern/execve/script_arg
@@ -0,0 +1,3 @@
+#! /bin/sh -x
+
+echo succeeded
diff --git a/tests/sys/kern/execve/script_arg_nospace b/tests/sys/kern/execve/script_arg_nospace
new file mode 100644
index 000000000000..15def094282b
--- /dev/null
+++ b/tests/sys/kern/execve/script_arg_nospace
@@ -0,0 +1,3 @@
+#!/bin/sh -x
+
+echo succeeded
diff --git a/tests/sys/kern/exterr_test.c b/tests/sys/kern/exterr_test.c
new file mode 100644
index 000000000000..17c84c1f8ed4
--- /dev/null
+++ b/tests/sys/kern/exterr_test.c
@@ -0,0 +1,108 @@
+/*-
+ * Copyright (C) 2025 ConnectWise, LLC. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/exterrvar.h>
+#include <sys/mman.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <exterr.h>
+#include <stdio.h>
+
+ATF_TC(gettext_extended);
+ATF_TC_HEAD(gettext_extended, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Retrieve an extended error message");
+}
+ATF_TC_BODY(gettext_extended, tc)
+{
+ char exterr[UEXTERROR_MAXLEN];
+ int r;
+
+ /*
+ * Use an invalid call to mmap() because it supports extended error
+ * messages, requires no special resources, and does not need root.
+ */
+ ATF_CHECK_ERRNO(ENOTSUP,
+ mmap(NULL, 0, PROT_MAX(PROT_READ) | PROT_WRITE, 0, -1, 0));
+ r = uexterr_gettext(exterr, sizeof(exterr));
+ ATF_CHECK_EQ(0, r);
+ printf("Extended error: %s\n", exterr);
+ /* Note: error string may need to be updated due to kernel changes */
+ ATF_CHECK(strstr(exterr, "prot is not subset of max_prot") != 0);
+}
+
+ATF_TC(gettext_noextended);
+ATF_TC_HEAD(gettext_noextended, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Fail to retrieve an extended error message because none exists");
+}
+ATF_TC_BODY(gettext_noextended, tc)
+{
+ char exterr[UEXTERROR_MAXLEN];
+ int r;
+
+ ATF_CHECK_ERRNO(EINVAL, exterrctl(EXTERRCTL_UD, 0, NULL));
+ r = uexterr_gettext(exterr, sizeof(exterr));
+ ATF_CHECK_EQ(0, r);
+ ATF_CHECK_STREQ(exterr, "");
+}
+
+ATF_TC(gettext_noextended_after_extended);
+ATF_TC_HEAD(gettext_noextended_after_extended, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "uexterr_gettext should not return a stale extended error message");
+}
+ATF_TC_BODY(gettext_noextended_after_extended, tc)
+{
+ char exterr[UEXTERROR_MAXLEN];
+ int r;
+
+ /*
+ * First do something that will create an extended error message, but
+ * ignore it.
+ */
+ ATF_CHECK_ERRNO(ENOTSUP,
+ mmap(NULL, 0, PROT_MAX(PROT_READ) | PROT_WRITE, 0, -1, 0));
+
+ /* Then do something that won't create an extended error message */
+ ATF_CHECK_ERRNO(EINVAL, exterrctl(EXTERRCTL_UD, 0, NULL));
+
+ /* Hopefully we won't see the stale extended error message */
+ r = uexterr_gettext(exterr, sizeof(exterr));
+ ATF_CHECK_EQ(0, r);
+ ATF_CHECK_STREQ(exterr, "");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, gettext_extended);
+ ATF_TP_ADD_TC(tp, gettext_noextended);
+ ATF_TP_ADD_TC(tp, gettext_noextended_after_extended);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/fdgrowtable_test.c b/tests/sys/kern/fdgrowtable_test.c
new file mode 100644
index 000000000000..ecab72ff09aa
--- /dev/null
+++ b/tests/sys/kern/fdgrowtable_test.c
@@ -0,0 +1,286 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2020 Rob Wing
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/filedesc.h>
+#include <sys/queue.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#include <sys/wait.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* linked libraries */
+#include <kvm.h>
+#include <libutil.h>
+#include <libprocstat.h>
+#include <pthread.h>
+
+/* test-case macro */
+#define AFILE "afile"
+
+/*
+ * The following macros, struct freetable, struct fdescenttbl0
+ * and struct filedesc0 are copied from sys/kern/kern_descrip.c
+ */
+#define NDFILE 20
+#define NDSLOTSIZE sizeof(NDSLOTTYPE)
+#define NDENTRIES (NDSLOTSIZE * __CHAR_BIT)
+#define NDSLOT(x) ((x) / NDENTRIES)
+#define NDBIT(x) ((NDSLOTTYPE)1 << ((x) % NDENTRIES))
+#define NDSLOTS(x) (((x) + NDENTRIES - 1) / NDENTRIES)
+
+struct freetable {
+ struct fdescenttbl *ft_table;
+ SLIST_ENTRY(freetable) ft_next;
+};
+
+struct fdescenttbl0 {
+ int fdt_nfiles;
+ struct filedescent fdt_ofiles[NDFILE];
+};
+
+struct filedesc0 {
+ struct filedesc fd_fd;
+ SLIST_HEAD(, freetable) fd_free;
+ struct fdescenttbl0 fd_dfiles;
+ NDSLOTTYPE fd_dmap[NDSLOTS(NDFILE)];
+};
+
+static void
+openfiles(int n)
+{
+ int i, fd;
+
+ ATF_REQUIRE((fd = open(AFILE, O_CREAT, 0644)) != -1);
+ close(fd);
+ for (i = 0; i < n; i++)
+ ATF_REQUIRE((fd = open(AFILE, O_RDONLY, 0644)) != -1);
+}
+
+/*
+ * Get a count of the old file descriptor tables on the freelist.
+ */
+static int
+old_tables(kvm_t *kd, struct kinfo_proc *kp)
+{
+ struct filedesc0 fdp0;
+ struct freetable *ft, tft;
+ int counter;
+
+ counter = 0;
+
+ ATF_REQUIRE(kvm_read(kd, (unsigned long) kp->ki_fd, &fdp0, sizeof(fdp0)) > 0);
+
+ SLIST_FOREACH(ft, &fdp0.fd_free, ft_next) {
+ ATF_REQUIRE(kvm_read(kd, (unsigned long) ft, &tft, sizeof(tft)) > 0 );
+ ft = &tft;
+ counter++;
+ }
+
+ return (counter);
+}
+
+/*
+ * The returning struct kinfo_proc stores kernel addresses that will be
+ * used by kvm_read to retrieve information for the current process.
+ */
+static struct kinfo_proc *
+read_kinfo(kvm_t *kd)
+{
+ struct kinfo_proc *kp;
+ int procs_found;
+
+ ATF_REQUIRE((kp = kvm_getprocs(kd, KERN_PROC_PID, (int) getpid(), &procs_found)) != NULL);
+ ATF_REQUIRE(procs_found == 1);
+
+ return (kp);
+}
+
+/*
+ * Test a single threaded process that doesn't have a shared
+ * file descriptor table. The old tables should be freed.
+ */
+ATF_TC(free_oldtables);
+ATF_TC_HEAD(free_oldtables, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(free_oldtables, tc)
+{
+ kvm_t *kd;
+ struct kinfo_proc *kp;
+
+ ATF_REQUIRE((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) != NULL);
+ openfiles(128);
+ kp = read_kinfo(kd);
+ ATF_CHECK(old_tables(kd,kp) == 0);
+}
+
+static _Noreturn void *
+exec_thread(void *args)
+{
+ for (;;)
+ sleep(1);
+}
+
+/*
+ * Test a process with two threads that doesn't have a shared file
+ * descriptor table. The old tables should not be freed.
+ */
+ATF_TC(oldtables_shared_via_threads);
+ATF_TC_HEAD(oldtables_shared_via_threads, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(oldtables_shared_via_threads, tc)
+{
+ pid_t child;
+ kvm_t *kd;
+ struct kinfo_proc *kp;
+ pthread_t thread;
+
+ if ((child = rfork(RFPROC | RFCFDG)) > 0) {
+ pid_t wpid;
+ int status;
+
+ wpid = waitpid(child, &status, 0);
+ ATF_REQUIRE(wpid == child);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE(WEXITSTATUS(status) == EXIT_SUCCESS);
+ return;
+ }
+
+#define REQUIRE(expression) do { \
+ if (!(expression)) \
+ exit(EXIT_FAILURE); \
+ } while (0)
+
+ REQUIRE(child == 0);
+
+ REQUIRE((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) != NULL);
+ REQUIRE(pthread_create(&thread, NULL, exec_thread, NULL) == 0);
+
+ openfiles(128);
+
+ kp = read_kinfo(kd);
+ REQUIRE(kp->ki_numthreads > 1);
+ REQUIRE(old_tables(kd,kp) > 1);
+
+ REQUIRE(pthread_cancel(thread) == 0);
+ REQUIRE(pthread_join(thread, NULL) == 0);
+#undef REQUIRE
+
+ exit(EXIT_SUCCESS);
+}
+
+/*
+ * Get the reference count of a file descriptor table.
+ */
+static int
+filedesc_refcnt(kvm_t *kd, struct kinfo_proc *kp)
+{
+ struct filedesc fdp;
+
+ ATF_REQUIRE(kvm_read(kd, (unsigned long) kp->ki_fd, &fdp, sizeof(fdp)) > 0);
+
+ return (fdp.fd_refcnt);
+}
+
+/*
+ * Test a single threaded process that shares a file descriptor
+ * table with another process. The old tables should not be freed.
+ */
+ATF_TC(oldtables_shared_via_process);
+ATF_TC_HEAD(oldtables_shared_via_process, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(oldtables_shared_via_process, tc)
+{
+ kvm_t *kd;
+ struct kinfo_proc *kp;
+ int status;
+ pid_t child, wpid;
+
+ ATF_REQUIRE((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) != NULL);
+
+ /* share the file descriptor table */
+ ATF_REQUIRE((child = rfork(RFPROC)) != -1);
+
+ if (child == 0) {
+ openfiles(128);
+ raise(SIGSTOP);
+ exit(127);
+ }
+
+ /* let parent process open some files too */
+ openfiles(128);
+
+ /* get current status of child */
+ wpid = waitpid(child, &status, WUNTRACED);
+ ATF_REQUIRE(wpid == child);
+
+ /* child should be stopped */
+ ATF_REQUIRE(WIFSTOPPED(status));
+
+ /*
+ * We want to read kernel data
+ * before the child exits
+ * otherwise we'll lose a reference count
+ * to the file descriptor table
+ */
+ kp = read_kinfo(kd);
+
+ ATF_CHECK(filedesc_refcnt(kd,kp) > 1);
+ ATF_CHECK(old_tables(kd,kp) > 1);
+
+ kill(child, SIGCONT);
+
+ /* child should have exited */
+ wpid = waitpid(child, &status, 0);
+ ATF_REQUIRE(wpid == child);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE(WEXITSTATUS(status) == 127);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, free_oldtables);
+ ATF_TP_ADD_TC(tp, oldtables_shared_via_threads);
+ ATF_TP_ADD_TC(tp, oldtables_shared_via_process);
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/getdirentries_test.c b/tests/sys/kern/getdirentries_test.c
new file mode 100644
index 000000000000..e66872ffe5b6
--- /dev/null
+++ b/tests/sys/kern/getdirentries_test.c
@@ -0,0 +1,172 @@
+/*-
+ * Copyright (c) 2025 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/stat.h>
+#include <sys/mount.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdint.h>
+
+#include <atf-c.h>
+
+ATF_TC(getdirentries_ok);
+ATF_TC_HEAD(getdirentries_ok, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Successfully read a directory.");
+}
+ATF_TC_BODY(getdirentries_ok, tc)
+{
+ char dbuf[4096];
+ struct dirent *d;
+ off_t base;
+ ssize_t ret;
+ int dd, n;
+
+ ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
+ ATF_REQUIRE((dd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
+ ATF_REQUIRE((ret = getdirentries(dd, dbuf, sizeof(dbuf), &base)) > 0);
+ ATF_REQUIRE_EQ(0, getdirentries(dd, dbuf, sizeof(dbuf), &base));
+ ATF_REQUIRE_EQ(base, lseek(dd, 0, SEEK_CUR));
+ ATF_CHECK_EQ(0, close(dd));
+ for (n = 0, d = (struct dirent *)dbuf;
+ d < (struct dirent *)(dbuf + ret);
+ d = (struct dirent *)((char *)d + d->d_reclen), n++)
+ /* nothing */ ;
+ ATF_CHECK_EQ((struct dirent *)(dbuf + ret), d);
+ ATF_CHECK_EQ(2, n);
+}
+
+ATF_TC(getdirentries_ebadf);
+ATF_TC_HEAD(getdirentries_ebadf, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Attempt to read a directory "
+ "from an invalid descriptor.");
+}
+ATF_TC_BODY(getdirentries_ebadf, tc)
+{
+ char dbuf[4096];
+ off_t base;
+ int fd;
+
+ ATF_REQUIRE((fd = open("file", O_CREAT | O_WRONLY, 0644)) >= 0);
+ ATF_REQUIRE_EQ(-1, getdirentries(fd, dbuf, sizeof(dbuf), &base));
+ ATF_CHECK_EQ(EBADF, errno);
+ ATF_REQUIRE_EQ(0, close(fd));
+ ATF_REQUIRE_EQ(-1, getdirentries(fd, dbuf, sizeof(dbuf), &base));
+ ATF_CHECK_EQ(EBADF, errno);
+}
+
+ATF_TC(getdirentries_efault);
+ATF_TC_HEAD(getdirentries_efault, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Attempt to read a directory "
+ "to an invalid buffer.");
+}
+ATF_TC_BODY(getdirentries_efault, tc)
+{
+ char dbuf[4096];
+ off_t base, *basep;
+ int dd;
+
+ ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
+ ATF_REQUIRE((dd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
+ ATF_REQUIRE_EQ(-1, getdirentries(dd, NULL, sizeof(dbuf), &base));
+ ATF_CHECK_EQ(EFAULT, errno);
+ basep = NULL;
+ basep++;
+ ATF_REQUIRE_EQ(-1, getdirentries(dd, dbuf, sizeof(dbuf), basep));
+ ATF_CHECK_EQ(EFAULT, errno);
+ ATF_CHECK_EQ(0, close(dd));
+}
+
+ATF_TC(getdirentries_einval);
+ATF_TC_HEAD(getdirentries_einval, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Attempt to read a directory "
+ "with various invalid parameters.");
+}
+ATF_TC_BODY(getdirentries_einval, tc)
+{
+ struct statfs fsb;
+ char dbuf[4096];
+ off_t base;
+ ssize_t ret;
+ int dd;
+
+ ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
+ ATF_REQUIRE((dd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
+ ATF_REQUIRE_EQ(0, fstatfs(dd, &fsb));
+ /* nbytes too small */
+ ATF_REQUIRE_EQ(-1, getdirentries(dd, dbuf, 8, &base));
+ ATF_CHECK_EQ(EINVAL, errno);
+ /* nbytes too big */
+ ATF_REQUIRE_EQ(-1, getdirentries(dd, dbuf, SIZE_MAX, &base));
+ ATF_CHECK_EQ(EINVAL, errno);
+ /* invalid position */
+ ATF_REQUIRE((ret = getdirentries(dd, dbuf, sizeof(dbuf), &base)) > 0);
+ ATF_REQUIRE_EQ(0, getdirentries(dd, dbuf, sizeof(dbuf), &base));
+ ATF_REQUIRE(base > 0);
+ ATF_REQUIRE_EQ(base + 3, lseek(dd, 3, SEEK_CUR));
+ /* known to fail on ufs (FFS2) and zfs, and work on tmpfs */
+ if (strcmp(fsb.f_fstypename, "ufs") == 0 ||
+ strcmp(fsb.f_fstypename, "zfs") == 0) {
+ atf_tc_expect_fail("incorrectly returns 0 instead of EINVAL "
+ "on %s", fsb.f_fstypename);
+ }
+ ATF_REQUIRE_EQ(-1, getdirentries(dd, dbuf, sizeof(dbuf), &base));
+ ATF_CHECK_EQ(EINVAL, errno);
+ ATF_CHECK_EQ(0, close(dd));
+}
+
+ATF_TC(getdirentries_enoent);
+ATF_TC_HEAD(getdirentries_enoent, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Attempt to read a directory "
+ "after it is deleted.");
+}
+ATF_TC_BODY(getdirentries_enoent, tc)
+{
+ char dbuf[4096];
+ off_t base;
+ int dd;
+
+ ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
+ ATF_REQUIRE((dd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
+ ATF_REQUIRE_EQ(0, rmdir("dir"));
+ ATF_REQUIRE_EQ(-1, getdirentries(dd, dbuf, sizeof(dbuf), &base));
+ ATF_CHECK_EQ(ENOENT, errno);
+}
+
+ATF_TC(getdirentries_enotdir);
+ATF_TC_HEAD(getdirentries_enotdir, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Attempt to read a directory "
+ "from a descriptor not associated with a directory.");
+}
+ATF_TC_BODY(getdirentries_enotdir, tc)
+{
+ char dbuf[4096];
+ off_t base;
+ int fd;
+
+ ATF_REQUIRE((fd = open("file", O_CREAT | O_RDWR, 0644)) >= 0);
+ ATF_REQUIRE_EQ(-1, getdirentries(fd, dbuf, sizeof(dbuf), &base));
+ ATF_CHECK_EQ(ENOTDIR, errno);
+ ATF_CHECK_EQ(0, close(fd));
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, getdirentries_ok);
+ ATF_TP_ADD_TC(tp, getdirentries_ebadf);
+ ATF_TP_ADD_TC(tp, getdirentries_efault);
+ ATF_TP_ADD_TC(tp, getdirentries_einval);
+ ATF_TP_ADD_TC(tp, getdirentries_enoent);
+ ATF_TP_ADD_TC(tp, getdirentries_enotdir);
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/inotify_test.c b/tests/sys/kern/inotify_test.c
new file mode 100644
index 000000000000..713db55afc22
--- /dev/null
+++ b/tests/sys/kern/inotify_test.c
@@ -0,0 +1,864 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Klara, Inc.
+ */
+
+#include <sys/capsicum.h>
+#include <sys/filio.h>
+#include <sys/inotify.h>
+#include <sys/ioccom.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/un.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <mntopts.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+static const char *
+ev2name(int event)
+{
+ switch (event) {
+ case IN_ACCESS:
+ return ("IN_ACCESS");
+ case IN_ATTRIB:
+ return ("IN_ATTRIB");
+ case IN_CLOSE_WRITE:
+ return ("IN_CLOSE_WRITE");
+ case IN_CLOSE_NOWRITE:
+ return ("IN_CLOSE_NOWRITE");
+ case IN_CREATE:
+ return ("IN_CREATE");
+ case IN_DELETE:
+ return ("IN_DELETE");
+ case IN_DELETE_SELF:
+ return ("IN_DELETE_SELF");
+ case IN_MODIFY:
+ return ("IN_MODIFY");
+ case IN_MOVE_SELF:
+ return ("IN_MOVE_SELF");
+ case IN_MOVED_FROM:
+ return ("IN_MOVED_FROM");
+ case IN_MOVED_TO:
+ return ("IN_MOVED_TO");
+ case IN_OPEN:
+ return ("IN_OPEN");
+ default:
+ return (NULL);
+ }
+}
+
+static void
+close_checked(int fd)
+{
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+/*
+ * Make sure that no other events are pending, and close the inotify descriptor.
+ */
+static void
+close_inotify(int fd)
+{
+ int n;
+
+ ATF_REQUIRE(ioctl(fd, FIONREAD, &n) == 0);
+ ATF_REQUIRE(n == 0);
+ close_checked(fd);
+}
+
+static uint32_t
+consume_event_cookie(int ifd, int wd, unsigned int event, unsigned int flags,
+ const char *name)
+{
+ struct inotify_event *ev;
+ size_t evsz, namelen;
+ ssize_t n;
+ uint32_t cookie;
+
+ /* Only read one record. */
+ namelen = name == NULL ? 0 : strlen(name);
+ evsz = sizeof(*ev) + _IN_NAMESIZE(namelen);
+ ev = malloc(evsz);
+ ATF_REQUIRE(ev != NULL);
+
+ n = read(ifd, ev, evsz);
+ ATF_REQUIRE_MSG(n >= 0, "failed to read event %s", ev2name(event));
+ ATF_REQUIRE((size_t)n >= sizeof(*ev));
+ ATF_REQUIRE((size_t)n == sizeof(*ev) + ev->len);
+ ATF_REQUIRE((size_t)n == evsz);
+
+ ATF_REQUIRE_MSG((ev->mask & IN_ALL_EVENTS) == event,
+ "expected event %#x, got %#x", event, ev->mask);
+ ATF_REQUIRE_MSG((ev->mask & _IN_ALL_RETFLAGS) == flags,
+ "expected flags %#x, got %#x", flags, ev->mask);
+ ATF_REQUIRE_MSG(ev->wd == wd,
+ "expected wd %d, got %d", wd, ev->wd);
+ ATF_REQUIRE_MSG(name == NULL || strcmp(name, ev->name) == 0,
+ "expected name '%s', got '%s'", name, ev->name);
+ cookie = ev->cookie;
+ if ((ev->mask & (IN_MOVED_FROM | IN_MOVED_TO)) == 0)
+ ATF_REQUIRE(cookie == 0);
+ free(ev);
+ return (cookie);
+}
+
+/*
+ * Read an event from the inotify file descriptor and check that it
+ * matches the expected values.
+ */
+static void
+consume_event(int ifd, int wd, unsigned int event, unsigned int flags,
+ const char *name)
+{
+ (void)consume_event_cookie(ifd, wd, event, flags, name);
+}
+
+static int
+inotify(int flags)
+{
+ int ifd;
+
+ ifd = inotify_init1(flags);
+ ATF_REQUIRE(ifd != -1);
+ return (ifd);
+}
+
+static void
+mount_nullfs(char *dir, char *src)
+{
+ struct iovec *iov;
+ char errmsg[1024];
+ int error, iovlen;
+
+ iov = NULL;
+ iovlen = 0;
+
+ build_iovec(&iov, &iovlen, "fstype", "nullfs", (size_t)-1);
+ build_iovec(&iov, &iovlen, "fspath", dir, (size_t)-1);
+ build_iovec(&iov, &iovlen, "target", src, (size_t)-1);
+ build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
+
+ errmsg[0] = '\0';
+ error = nmount(iov, iovlen, 0);
+ ATF_REQUIRE_MSG(error == 0,
+ "mount nullfs %s %s: %s", src, dir,
+ errmsg[0] == '\0' ? strerror(errno) : errmsg);
+
+ free_iovec(&iov, &iovlen);
+}
+
+static void
+mount_tmpfs(const char *dir)
+{
+ struct iovec *iov;
+ char errmsg[1024];
+ int error, iovlen;
+
+ iov = NULL;
+ iovlen = 0;
+
+ build_iovec(&iov, &iovlen, "fstype", "tmpfs", (size_t)-1);
+ build_iovec(&iov, &iovlen, "fspath", __DECONST(char *, dir),
+ (size_t)-1);
+ build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
+
+ errmsg[0] = '\0';
+ error = nmount(iov, iovlen, 0);
+ ATF_REQUIRE_MSG(error == 0,
+ "mount tmpfs %s: %s", dir,
+ errmsg[0] == '\0' ? strerror(errno) : errmsg);
+
+ free_iovec(&iov, &iovlen);
+}
+
+static int
+watch_file(int ifd, int events, char *path)
+{
+ int fd, wd;
+
+ strncpy(path, "test.XXXXXX", PATH_MAX);
+ fd = mkstemp(path);
+ ATF_REQUIRE(fd != -1);
+ close_checked(fd);
+
+ wd = inotify_add_watch(ifd, path, events);
+ ATF_REQUIRE(wd != -1);
+
+ return (wd);
+}
+
+static int
+watch_dir(int ifd, int events, char *path)
+{
+ char *p;
+ int wd;
+
+ strlcpy(path, "test.XXXXXX", PATH_MAX);
+ p = mkdtemp(path);
+ ATF_REQUIRE(p == path);
+
+ wd = inotify_add_watch(ifd, path, events);
+ ATF_REQUIRE(wd != -1);
+
+ return (wd);
+}
+
+/*
+ * Verify that Capsicum restrictions are applied as expected.
+ */
+ATF_TC_WITHOUT_HEAD(inotify_capsicum);
+ATF_TC_BODY(inotify_capsicum, tc)
+{
+ int error, dfd, ifd, wd;
+
+ ifd = inotify(IN_NONBLOCK);
+ ATF_REQUIRE(ifd != -1);
+
+ dfd = open(".", O_RDONLY | O_DIRECTORY);
+ ATF_REQUIRE(dfd != -1);
+
+ error = mkdirat(dfd, "testdir", 0755);
+ ATF_REQUIRE(error == 0);
+
+ error = cap_enter();
+ ATF_REQUIRE(error == 0);
+
+ /*
+ * Plain inotify_add_watch() is disallowed.
+ */
+ wd = inotify_add_watch(ifd, ".", IN_DELETE_SELF);
+ ATF_REQUIRE_ERRNO(ECAPMODE, wd == -1);
+ wd = inotify_add_watch_at(ifd, dfd, "testdir", IN_DELETE_SELF);
+ ATF_REQUIRE(wd >= 0);
+
+ /*
+ * Generate a record and consume it.
+ */
+ error = unlinkat(dfd, "testdir", AT_REMOVEDIR);
+ ATF_REQUIRE(error == 0);
+ consume_event(ifd, wd, IN_DELETE_SELF, IN_ISDIR, NULL);
+ consume_event(ifd, wd, 0, IN_IGNORED, NULL);
+
+ close_checked(dfd);
+ close_inotify(ifd);
+}
+
+/*
+ * Make sure that duplicate, back-to-back events are coalesced.
+ */
+ATF_TC_WITHOUT_HEAD(inotify_coalesce);
+ATF_TC_BODY(inotify_coalesce, tc)
+{
+ char file[PATH_MAX], path[PATH_MAX];
+ int fd, fd1, ifd, n, wd;
+
+ ifd = inotify(IN_NONBLOCK);
+
+ /* Create a directory and watch it. */
+ wd = watch_dir(ifd, IN_OPEN, path);
+ /* Create a file in the directory and open it. */
+ snprintf(file, sizeof(file), "%s/file", path);
+ fd = open(file, O_RDWR | O_CREAT, 0644);
+ ATF_REQUIRE(fd != -1);
+ close_checked(fd);
+ fd = open(file, O_RDWR);
+ ATF_REQUIRE(fd != -1);
+ fd1 = open(file, O_RDONLY);
+ ATF_REQUIRE(fd1 != -1);
+ close_checked(fd1);
+ close_checked(fd);
+
+ consume_event(ifd, wd, IN_OPEN, 0, "file");
+ ATF_REQUIRE(ioctl(ifd, FIONREAD, &n) == 0);
+ ATF_REQUIRE(n == 0);
+
+ close_inotify(ifd);
+}
+
+/*
+ * Check handling of IN_MASK_CREATE.
+ */
+ATF_TC_WITHOUT_HEAD(inotify_mask_create);
+ATF_TC_BODY(inotify_mask_create, tc)
+{
+ char path[PATH_MAX];
+ int ifd, wd, wd1;
+
+ ifd = inotify(IN_NONBLOCK);
+
+ /* Create a directory and watch it. */
+ wd = watch_dir(ifd, IN_CREATE, path);
+ /* Updating the watch with IN_MASK_CREATE should result in an error. */
+ wd1 = inotify_add_watch(ifd, path, IN_MODIFY | IN_MASK_CREATE);
+ ATF_REQUIRE_ERRNO(EEXIST, wd1 == -1);
+ /* It's an error to specify IN_MASK_ADD with IN_MASK_CREATE. */
+ wd1 = inotify_add_watch(ifd, path, IN_MODIFY | IN_MASK_ADD |
+ IN_MASK_CREATE);
+ ATF_REQUIRE_ERRNO(EINVAL, wd1 == -1);
+ /* Updating the watch without IN_MASK_CREATE should work. */
+ wd1 = inotify_add_watch(ifd, path, IN_MODIFY);
+ ATF_REQUIRE(wd1 != -1);
+ ATF_REQUIRE_EQ(wd, wd1);
+
+ close_inotify(ifd);
+}
+
+/*
+ * Make sure that inotify cooperates with nullfs: if a lower vnode is the
+ * subject of an event, the upper vnode should be notified, and if the upper
+ * vnode is the subject of an event, the lower vnode should be notified.
+ */
+ATF_TC_WITH_CLEANUP(inotify_nullfs);
+ATF_TC_HEAD(inotify_nullfs, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(inotify_nullfs, tc)
+{
+ char path[PATH_MAX], *p;
+ int dfd, error, fd, ifd, mask, wd;
+
+ mask = IN_CREATE | IN_OPEN;
+
+ ifd = inotify(IN_NONBLOCK);
+
+ strlcpy(path, "./test.XXXXXX", sizeof(path));
+ p = mkdtemp(path);
+ ATF_REQUIRE(p == path);
+
+ error = mkdir("./mnt", 0755);
+ ATF_REQUIRE(error == 0);
+
+ /* Mount the testdir onto ./mnt. */
+ mount_nullfs("./mnt", path);
+
+ wd = inotify_add_watch(ifd, "./mnt", mask);
+ ATF_REQUIRE(wd != -1);
+
+ /* Create a file in the lower directory and open it. */
+ dfd = open(path, O_RDONLY | O_DIRECTORY);
+ ATF_REQUIRE(dfd != -1);
+ fd = openat(dfd, "file", O_RDWR | O_CREAT, 0644);
+ close_checked(fd);
+ close_checked(dfd);
+
+ /* We should see events via the nullfs mount. */
+ consume_event(ifd, wd, IN_OPEN, IN_ISDIR, NULL);
+ consume_event(ifd, wd, IN_CREATE, 0, "file");
+ consume_event(ifd, wd, IN_OPEN, 0, "file");
+
+ error = inotify_rm_watch(ifd, wd);
+ ATF_REQUIRE(error == 0);
+ consume_event(ifd, wd, 0, IN_IGNORED, NULL);
+
+ /* Watch the lower directory. */
+ wd = inotify_add_watch(ifd, path, mask);
+ ATF_REQUIRE(wd != -1);
+ /* ... and create a file in the upper directory and open it. */
+ dfd = open("./mnt", O_RDONLY | O_DIRECTORY);
+ ATF_REQUIRE(dfd != -1);
+ fd = openat(dfd, "file2", O_RDWR | O_CREAT, 0644);
+ ATF_REQUIRE(fd != -1);
+ close_checked(fd);
+ close_checked(dfd);
+
+ /* We should see events via the lower directory. */
+ consume_event(ifd, wd, IN_OPEN, IN_ISDIR, NULL);
+ consume_event(ifd, wd, IN_CREATE, 0, "file2");
+ consume_event(ifd, wd, IN_OPEN, 0, "file2");
+
+ close_inotify(ifd);
+}
+ATF_TC_CLEANUP(inotify_nullfs, tc)
+{
+ int error;
+
+ error = unmount("./mnt", 0);
+ if (error != 0) {
+ perror("unmount");
+ exit(1);
+ }
+}
+
+/*
+ * Make sure that exceeding max_events pending events results in an overflow
+ * event.
+ */
+ATF_TC_WITHOUT_HEAD(inotify_queue_overflow);
+ATF_TC_BODY(inotify_queue_overflow, tc)
+{
+ char path[PATH_MAX];
+ size_t size;
+ int error, dfd, ifd, max, wd;
+
+ size = sizeof(max);
+ error = sysctlbyname("vfs.inotify.max_queued_events", &max, &size, NULL,
+ 0);
+ ATF_REQUIRE(error == 0);
+
+ ifd = inotify(IN_NONBLOCK);
+
+ /* Create a directory and watch it for file creation events. */
+ wd = watch_dir(ifd, IN_CREATE, path);
+ dfd = open(path, O_DIRECTORY);
+ ATF_REQUIRE(dfd != -1);
+ /* Generate max+1 file creation events. */
+ for (int i = 0; i < max + 1; i++) {
+ char name[NAME_MAX];
+ int fd;
+
+ (void)snprintf(name, sizeof(name), "file%d", i);
+ fd = openat(dfd, name, O_CREAT | O_RDWR, 0644);
+ ATF_REQUIRE(fd != -1);
+ close_checked(fd);
+ }
+
+ /*
+ * Read our events. We should see files 0..max-1 and then an overflow
+ * event.
+ */
+ for (int i = 0; i < max; i++) {
+ char name[NAME_MAX];
+
+ (void)snprintf(name, sizeof(name), "file%d", i);
+ consume_event(ifd, wd, IN_CREATE, 0, name);
+ }
+
+ /* Look for an overflow event. */
+ consume_event(ifd, -1, 0, IN_Q_OVERFLOW, NULL);
+
+ close_checked(dfd);
+ close_inotify(ifd);
+}
+
+ATF_TC_WITHOUT_HEAD(inotify_event_access_file);
+ATF_TC_BODY(inotify_event_access_file, tc)
+{
+ char path[PATH_MAX], buf[16];
+ off_t nb;
+ ssize_t n;
+ int error, fd, fd1, ifd, s[2], wd;
+
+ ifd = inotify(IN_NONBLOCK);
+
+ wd = watch_file(ifd, IN_ACCESS, path);
+
+ fd = open(path, O_RDWR);
+ n = write(fd, "test", 4);
+ ATF_REQUIRE(n == 4);
+
+ /* A simple read(2) should generate an access. */
+ ATF_REQUIRE(lseek(fd, 0, SEEK_SET) == 0);
+ n = read(fd, buf, sizeof(buf));
+ ATF_REQUIRE(n == 4);
+ ATF_REQUIRE(memcmp(buf, "test", 4) == 0);
+ consume_event(ifd, wd, IN_ACCESS, 0, NULL);
+
+ /* copy_file_range(2) should as well. */
+ ATF_REQUIRE(lseek(fd, 0, SEEK_SET) == 0);
+ fd1 = open("sink", O_RDWR | O_CREAT, 0644);
+ ATF_REQUIRE(fd1 != -1);
+ n = copy_file_range(fd, NULL, fd1, NULL, 4, 0);
+ ATF_REQUIRE(n == 4);
+ close_checked(fd1);
+ consume_event(ifd, wd, IN_ACCESS, 0, NULL);
+
+ /* As should sendfile(2). */
+ error = socketpair(AF_UNIX, SOCK_STREAM, 0, s);
+ ATF_REQUIRE(error == 0);
+ error = sendfile(fd, s[0], 0, 4, NULL, &nb, 0);
+ ATF_REQUIRE(error == 0);
+ ATF_REQUIRE(nb == 4);
+ consume_event(ifd, wd, IN_ACCESS, 0, NULL);
+ close_checked(s[0]);
+ close_checked(s[1]);
+
+ close_checked(fd);
+
+ close_inotify(ifd);
+}
+
+ATF_TC_WITHOUT_HEAD(inotify_event_access_dir);
+ATF_TC_BODY(inotify_event_access_dir, tc)
+{
+ char root[PATH_MAX], path[PATH_MAX];
+ struct dirent *ent;
+ DIR *dir;
+ int error, ifd, wd;
+
+ ifd = inotify(IN_NONBLOCK);
+
+ wd = watch_dir(ifd, IN_ACCESS, root);
+ snprintf(path, sizeof(path), "%s/dir", root);
+ error = mkdir(path, 0755);
+ ATF_REQUIRE(error == 0);
+
+ /* Read an entry and generate an access. */
+ dir = opendir(path);
+ ATF_REQUIRE(dir != NULL);
+ ent = readdir(dir);
+ ATF_REQUIRE(ent != NULL);
+ ATF_REQUIRE(strcmp(ent->d_name, ".") == 0 ||
+ strcmp(ent->d_name, "..") == 0);
+ ATF_REQUIRE(closedir(dir) == 0);
+ consume_event(ifd, wd, IN_ACCESS, IN_ISDIR, "dir");
+
+ /*
+ * Reading the watched directory should generate an access event.
+ * This is contrary to Linux's inotify man page, which states that
+ * IN_ACCESS is only generated for accesses to objects in a watched
+ * directory.
+ */
+ dir = opendir(root);
+ ATF_REQUIRE(dir != NULL);
+ ent = readdir(dir);
+ ATF_REQUIRE(ent != NULL);
+ ATF_REQUIRE(strcmp(ent->d_name, ".") == 0 ||
+ strcmp(ent->d_name, "..") == 0);
+ ATF_REQUIRE(closedir(dir) == 0);
+ consume_event(ifd, wd, IN_ACCESS, IN_ISDIR, NULL);
+
+ close_inotify(ifd);
+}
+
+ATF_TC_WITHOUT_HEAD(inotify_event_attrib);
+ATF_TC_BODY(inotify_event_attrib, tc)
+{
+ char path[PATH_MAX];
+ int error, ifd, fd, wd;
+
+ ifd = inotify(IN_NONBLOCK);
+
+ wd = watch_file(ifd, IN_ATTRIB, path);
+
+ fd = open(path, O_RDWR);
+ ATF_REQUIRE(fd != -1);
+ error = fchmod(fd, 0600);
+ ATF_REQUIRE(error == 0);
+ consume_event(ifd, wd, IN_ATTRIB, 0, NULL);
+
+ error = fchown(fd, getuid(), getgid());
+ ATF_REQUIRE(error == 0);
+ consume_event(ifd, wd, IN_ATTRIB, 0, NULL);
+
+ close_checked(fd);
+ close_inotify(ifd);
+}
+
+ATF_TC_WITHOUT_HEAD(inotify_event_close_nowrite);
+ATF_TC_BODY(inotify_event_close_nowrite, tc)
+{
+ char file[PATH_MAX], file1[PATH_MAX], dir[PATH_MAX];
+ int ifd, fd, wd1, wd2;
+
+ ifd = inotify(IN_NONBLOCK);
+
+ wd1 = watch_dir(ifd, IN_CLOSE_NOWRITE, dir);
+ wd2 = watch_file(ifd, IN_CLOSE_NOWRITE | IN_CLOSE_WRITE, file);
+
+ fd = open(dir, O_DIRECTORY);
+ ATF_REQUIRE(fd != -1);
+ close_checked(fd);
+ consume_event(ifd, wd1, IN_CLOSE_NOWRITE, IN_ISDIR, NULL);
+
+ fd = open(file, O_RDONLY);
+ ATF_REQUIRE(fd != -1);
+ close_checked(fd);
+ consume_event(ifd, wd2, IN_CLOSE_NOWRITE, 0, NULL);
+
+ snprintf(file1, sizeof(file1), "%s/file", dir);
+ fd = open(file1, O_RDONLY | O_CREAT, 0644);
+ ATF_REQUIRE(fd != -1);
+ close_checked(fd);
+ consume_event(ifd, wd1, IN_CLOSE_NOWRITE, 0, "file");
+
+ close_inotify(ifd);
+}
+
+ATF_TC_WITHOUT_HEAD(inotify_event_close_write);
+ATF_TC_BODY(inotify_event_close_write, tc)
+{
+ char path[PATH_MAX];
+ int ifd, fd, wd;
+
+ ifd = inotify(IN_NONBLOCK);
+
+ wd = watch_file(ifd, IN_CLOSE_NOWRITE | IN_CLOSE_WRITE, path);
+
+ fd = open(path, O_RDWR);
+ ATF_REQUIRE(fd != -1);
+ close_checked(fd);
+ consume_event(ifd, wd, IN_CLOSE_WRITE, 0, NULL);
+
+ close_inotify(ifd);
+}
+
+/* Verify that various operations in a directory generate IN_CREATE events. */
+ATF_TC_WITHOUT_HEAD(inotify_event_create);
+ATF_TC_BODY(inotify_event_create, tc)
+{
+ struct sockaddr_un sun;
+ char path[PATH_MAX], path1[PATH_MAX], root[PATH_MAX];
+ ssize_t n;
+ int error, ifd, ifd1, fd, s, wd, wd1;
+ char b;
+
+ ifd = inotify(IN_NONBLOCK);
+
+ wd = watch_dir(ifd, IN_CREATE, root);
+
+ /* Regular file. */
+ snprintf(path, sizeof(path), "%s/file", root);
+ fd = open(path, O_RDWR | O_CREAT, 0644);
+ ATF_REQUIRE(fd != -1);
+ /*
+ * Make sure we get an event triggered by the fd used to create the
+ * file.
+ */
+ ifd1 = inotify(IN_NONBLOCK);
+ wd1 = inotify_add_watch(ifd1, root, IN_MODIFY);
+ b = 42;
+ n = write(fd, &b, sizeof(b));
+ ATF_REQUIRE(n == sizeof(b));
+ close_checked(fd);
+ consume_event(ifd, wd, IN_CREATE, 0, "file");
+ consume_event(ifd1, wd1, IN_MODIFY, 0, "file");
+ close_inotify(ifd1);
+
+ /* Hard link. */
+ snprintf(path1, sizeof(path1), "%s/link", root);
+ error = link(path, path1);
+ ATF_REQUIRE(error == 0);
+ consume_event(ifd, wd, IN_CREATE, 0, "link");
+
+ /* Directory. */
+ snprintf(path, sizeof(path), "%s/dir", root);
+ error = mkdir(path, 0755);
+ ATF_REQUIRE(error == 0);
+ consume_event(ifd, wd, IN_CREATE, IN_ISDIR, "dir");
+
+ /* Symbolic link. */
+ snprintf(path1, sizeof(path1), "%s/symlink", root);
+ error = symlink(path, path1);
+ ATF_REQUIRE(error == 0);
+ consume_event(ifd, wd, IN_CREATE, 0, "symlink");
+
+ /* FIFO. */
+ snprintf(path, sizeof(path), "%s/fifo", root);
+ error = mkfifo(path, 0644);
+ ATF_REQUIRE(error == 0);
+ consume_event(ifd, wd, IN_CREATE, 0, "fifo");
+
+ /* Binding a socket. */
+ s = socket(AF_UNIX, SOCK_STREAM, 0);
+ memset(&sun, 0, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ sun.sun_len = sizeof(sun);
+ snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/socket", root);
+ error = bind(s, (struct sockaddr *)&sun, sizeof(sun));
+ ATF_REQUIRE(error == 0);
+ close_checked(s);
+ consume_event(ifd, wd, IN_CREATE, 0, "socket");
+
+ close_inotify(ifd);
+}
+
+ATF_TC_WITHOUT_HEAD(inotify_event_delete);
+ATF_TC_BODY(inotify_event_delete, tc)
+{
+ char root[PATH_MAX], path[PATH_MAX], file[PATH_MAX];
+ int error, fd, ifd, wd, wd2;
+
+ ifd = inotify(IN_NONBLOCK);
+
+ wd = watch_dir(ifd, IN_DELETE | IN_DELETE_SELF, root);
+
+ snprintf(path, sizeof(path), "%s/file", root);
+ fd = open(path, O_RDWR | O_CREAT, 0644);
+ ATF_REQUIRE(fd != -1);
+ error = unlink(path);
+ ATF_REQUIRE(error == 0);
+ consume_event(ifd, wd, IN_DELETE, 0, "file");
+ close_checked(fd);
+
+ /*
+ * Make sure that renaming over a file generates a delete event when and
+ * only when that file is watched.
+ */
+ fd = open(path, O_RDWR | O_CREAT, 0644);
+ ATF_REQUIRE(fd != -1);
+ close_checked(fd);
+ wd2 = inotify_add_watch(ifd, path, IN_DELETE | IN_DELETE_SELF);
+ ATF_REQUIRE(wd2 != -1);
+ snprintf(file, sizeof(file), "%s/file2", root);
+ fd = open(file, O_RDWR | O_CREAT, 0644);
+ ATF_REQUIRE(fd != -1);
+ close_checked(fd);
+ error = rename(file, path);
+ ATF_REQUIRE(error == 0);
+ consume_event(ifd, wd2, IN_DELETE_SELF, 0, NULL);
+ consume_event(ifd, wd2, 0, IN_IGNORED, NULL);
+
+ error = unlink(path);
+ ATF_REQUIRE(error == 0);
+ consume_event(ifd, wd, IN_DELETE, 0, "file");
+ error = rmdir(root);
+ ATF_REQUIRE(error == 0);
+ consume_event(ifd, wd, IN_DELETE_SELF, IN_ISDIR, NULL);
+ consume_event(ifd, wd, 0, IN_IGNORED, NULL);
+
+ close_inotify(ifd);
+}
+
+ATF_TC_WITHOUT_HEAD(inotify_event_move);
+ATF_TC_BODY(inotify_event_move, tc)
+{
+ char dir1[PATH_MAX], dir2[PATH_MAX], path1[PATH_MAX], path2[PATH_MAX];
+ char path3[PATH_MAX];
+ int error, ifd, fd, wd1, wd2, wd3;
+ uint32_t cookie1, cookie2;
+
+ ifd = inotify(IN_NONBLOCK);
+
+ wd1 = watch_dir(ifd, IN_MOVE | IN_MOVE_SELF, dir1);
+ wd2 = watch_dir(ifd, IN_MOVE | IN_MOVE_SELF, dir2);
+
+ snprintf(path1, sizeof(path1), "%s/file", dir1);
+ fd = open(path1, O_RDWR | O_CREAT, 0644);
+ ATF_REQUIRE(fd != -1);
+ close_checked(fd);
+ snprintf(path2, sizeof(path2), "%s/file2", dir2);
+ error = rename(path1, path2);
+ ATF_REQUIRE(error == 0);
+ cookie1 = consume_event_cookie(ifd, wd1, IN_MOVED_FROM, 0, "file");
+ cookie2 = consume_event_cookie(ifd, wd2, IN_MOVED_TO, 0, "file2");
+ ATF_REQUIRE_MSG(cookie1 == cookie2,
+ "expected cookie %u, got %u", cookie1, cookie2);
+
+ snprintf(path2, sizeof(path2), "%s/dir", dir2);
+ error = rename(dir1, path2);
+ ATF_REQUIRE(error == 0);
+ consume_event(ifd, wd1, IN_MOVE_SELF, IN_ISDIR, NULL);
+ consume_event(ifd, wd2, IN_MOVED_TO, IN_ISDIR, "dir");
+
+ wd3 = watch_file(ifd, IN_MOVE_SELF, path3);
+ error = rename(path3, "foo");
+ ATF_REQUIRE(error == 0);
+ consume_event(ifd, wd3, IN_MOVE_SELF, 0, NULL);
+
+ close_inotify(ifd);
+}
+
+ATF_TC_WITHOUT_HEAD(inotify_event_open);
+ATF_TC_BODY(inotify_event_open, tc)
+{
+ char root[PATH_MAX], path[PATH_MAX];
+ int error, ifd, fd, wd;
+
+ ifd = inotify(IN_NONBLOCK);
+
+ wd = watch_dir(ifd, IN_OPEN, root);
+
+ snprintf(path, sizeof(path), "%s/file", root);
+ fd = open(path, O_RDWR | O_CREAT, 0644);
+ ATF_REQUIRE(fd != -1);
+ close_checked(fd);
+ consume_event(ifd, wd, IN_OPEN, 0, "file");
+
+ fd = open(path, O_PATH);
+ ATF_REQUIRE(fd != -1);
+ close_checked(fd);
+ consume_event(ifd, wd, IN_OPEN, 0, "file");
+
+ fd = open(root, O_DIRECTORY);
+ ATF_REQUIRE(fd != -1);
+ close_checked(fd);
+ consume_event(ifd, wd, IN_OPEN, IN_ISDIR, NULL);
+
+ snprintf(path, sizeof(path), "%s/fifo", root);
+ error = mkfifo(path, 0644);
+ ATF_REQUIRE(error == 0);
+ fd = open(path, O_RDWR);
+ ATF_REQUIRE(fd != -1);
+ close_checked(fd);
+ consume_event(ifd, wd, IN_OPEN, 0, "fifo");
+
+ close_inotify(ifd);
+}
+
+ATF_TC_WITH_CLEANUP(inotify_event_unmount);
+ATF_TC_HEAD(inotify_event_unmount, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(inotify_event_unmount, tc)
+{
+ int error, fd, ifd, wd;
+
+ ifd = inotify(IN_NONBLOCK);
+
+ error = mkdir("./root", 0755);
+ ATF_REQUIRE(error == 0);
+
+ mount_tmpfs("./root");
+
+ error = mkdir("./root/dir", 0755);
+ ATF_REQUIRE(error == 0);
+ wd = inotify_add_watch(ifd, "./root/dir", IN_OPEN);
+ ATF_REQUIRE(wd >= 0);
+
+ fd = open("./root/dir", O_RDONLY | O_DIRECTORY);
+ ATF_REQUIRE(fd != -1);
+ consume_event(ifd, wd, IN_OPEN, IN_ISDIR, NULL);
+ close_checked(fd);
+
+ /* A regular unmount should fail, as inotify holds a vnode reference. */
+ error = unmount("./root", 0);
+ ATF_REQUIRE_ERRNO(EBUSY, error == -1);
+ error = unmount("./root", MNT_FORCE);
+ ATF_REQUIRE_MSG(error == 0,
+ "unmounting ./root failed: %s", strerror(errno));
+
+ consume_event(ifd, wd, 0, IN_UNMOUNT, NULL);
+ consume_event(ifd, wd, 0, IN_IGNORED, NULL);
+
+ close_inotify(ifd);
+}
+ATF_TC_CLEANUP(inotify_event_unmount, tc)
+{
+ (void)unmount("./root", MNT_FORCE);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ /* Tests for the inotify syscalls. */
+ ATF_TP_ADD_TC(tp, inotify_capsicum);
+ ATF_TP_ADD_TC(tp, inotify_coalesce);
+ ATF_TP_ADD_TC(tp, inotify_mask_create);
+ ATF_TP_ADD_TC(tp, inotify_nullfs);
+ ATF_TP_ADD_TC(tp, inotify_queue_overflow);
+ /* Tests for the various inotify event types. */
+ ATF_TP_ADD_TC(tp, inotify_event_access_file);
+ ATF_TP_ADD_TC(tp, inotify_event_access_dir);
+ ATF_TP_ADD_TC(tp, inotify_event_attrib);
+ ATF_TP_ADD_TC(tp, inotify_event_close_nowrite);
+ ATF_TP_ADD_TC(tp, inotify_event_close_write);
+ ATF_TP_ADD_TC(tp, inotify_event_create);
+ ATF_TP_ADD_TC(tp, inotify_event_delete);
+ ATF_TP_ADD_TC(tp, inotify_event_move);
+ ATF_TP_ADD_TC(tp, inotify_event_open);
+ ATF_TP_ADD_TC(tp, inotify_event_unmount);
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/jail_lookup_root.c b/tests/sys/kern/jail_lookup_root.c
new file mode 100644
index 000000000000..34e89f4aea2b
--- /dev/null
+++ b/tests/sys/kern/jail_lookup_root.c
@@ -0,0 +1,133 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Mark Johnston <markj@FreeBSD.org>
+ */
+
+#include <sys/param.h>
+#include <sys/jail.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <jail.h>
+#include <mntopts.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <atf-c.h>
+
+static void
+mkdir_checked(const char *dir, mode_t mode)
+{
+ int error;
+
+ error = mkdir(dir, mode);
+ ATF_REQUIRE_MSG(error == 0 || errno == EEXIST,
+ "mkdir %s: %s", dir, strerror(errno));
+}
+
+static void __unused
+mount_nullfs(const char *dir, const char *target)
+{
+ struct iovec *iov;
+ char errmsg[1024];
+ int error, iovlen;
+
+ iov = NULL;
+ iovlen = 0;
+
+ build_iovec(&iov, &iovlen, __DECONST(char *, "fstype"),
+ __DECONST(char *, "nullfs"), (size_t)-1);
+ build_iovec(&iov, &iovlen, __DECONST(char *, "fspath"),
+ __DECONST(char *, target), (size_t)-1);
+ build_iovec(&iov, &iovlen, __DECONST(char *, "from"),
+ __DECONST(char *, dir), (size_t)-1);
+ build_iovec(&iov, &iovlen, __DECONST(char *, "errmsg"),
+ errmsg, sizeof(errmsg));
+
+ errmsg[0] = '\0';
+ error = nmount(iov, iovlen, 0);
+ ATF_REQUIRE_MSG(error == 0, "nmount: %s",
+ errmsg[0] != '\0' ? errmsg : strerror(errno));
+
+ free_iovec(&iov, &iovlen);
+}
+
+ATF_TC_WITH_CLEANUP(jail_root);
+ATF_TC_HEAD(jail_root, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(jail_root, tc)
+{
+ int error, fd, jid;
+
+ mkdir_checked("./root", 0755);
+ mkdir_checked("./root/a", 0755);
+ mkdir_checked("./root/b", 0755);
+ mkdir_checked("./root/a/c", 0755);
+
+ jid = jail_setv(JAIL_CREATE | JAIL_ATTACH,
+ "name", "nullfs_jail_root_test",
+ "allow.mount", "true",
+ "allow.mount.nullfs", "true",
+ "enforce_statfs", "1",
+ "path", "./root",
+ "persist", NULL,
+ NULL);
+ ATF_REQUIRE_MSG(jid >= 0, "jail_setv: %s", jail_errmsg);
+
+ mount_nullfs("/a", "/b");
+
+ error = chdir("/b/c");
+ ATF_REQUIRE(error == 0);
+
+ error = rename("/a/c", "/c");
+ ATF_REQUIRE(error == 0);
+
+ /* Descending to the jail root should be ok. */
+ error = chdir("..");
+ ATF_REQUIRE(error == 0);
+
+ /* Going beyond the root will trigger an error. */
+ error = chdir("..");
+ ATF_REQUIRE_ERRNO(ENOENT, error != 0);
+ fd = open("..", O_RDONLY | O_DIRECTORY);
+ ATF_REQUIRE_ERRNO(ENOENT, fd < 0);
+}
+ATF_TC_CLEANUP(jail_root, tc)
+{
+ struct statfs fs;
+ fsid_t fsid;
+ int error, jid;
+
+ error = statfs("./root/b", &fs);
+ if (error != 0)
+ err(1, "statfs ./b");
+ fsid = fs.f_fsid;
+ error = statfs("./root", &fs);
+ if (error != 0)
+ err(1, "statfs ./root");
+ if (fsid.val[0] != fs.f_fsid.val[0] ||
+ fsid.val[1] != fs.f_fsid.val[1]) {
+ error = unmount("./root/b", 0);
+ if (error != 0)
+ err(1, "unmount ./root/b");
+ }
+
+ jid = jail_getid("nullfs_jail_root_test");
+ if (jid >= 0) {
+ error = jail_remove(jid);
+ if (error != 0)
+ err(1, "jail_remove");
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, jail_root);
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/jailmeta.sh b/tests/sys/kern/jailmeta.sh
new file mode 100644
index 000000000000..9a63f958231f
--- /dev/null
+++ b/tests/sys/kern/jailmeta.sh
@@ -0,0 +1,588 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 SkunkWerks GmbH
+#
+# This software was developed by Igor Ostapenko <igoro@FreeBSD.org>
+# under sponsorship from SkunkWerks GmbH.
+#
+
+setup()
+{
+ # Check if we have enough buffer space for testing
+ if [ $(sysctl -n security.jail.meta_maxbufsize) -lt 128 ]; then
+ atf_skip "sysctl security.jail.meta_maxbufsize must be 128+ for testing."
+ fi
+}
+
+atf_test_case "jail_create" "cleanup"
+jail_create_head()
+{
+ atf_set descr 'Test that metadata can be set upon jail creation with jail(8)'
+ atf_set require.user root
+ atf_set execenv jail
+}
+jail_create_body()
+{
+ setup
+
+ atf_check -s not-exit:0 -e match:"not found" -o ignore \
+ jls -jj
+
+ atf_check -s exit:0 \
+ jail -c name=j persist meta="a b c" env="C B A"
+
+ atf_check -s exit:0 -o inline:"a b c\n" \
+ jls -jj meta
+ atf_check -s exit:0 -o inline:"C B A\n" \
+ jls -jj env
+}
+jail_create_cleanup()
+{
+ jail -r j
+ return 0
+}
+
+atf_test_case "jail_modify" "cleanup"
+jail_modify_head()
+{
+ atf_set descr 'Test that metadata can be modified after jail creation with jail(8)'
+ atf_set require.user root
+ atf_set execenv jail
+}
+jail_modify_body()
+{
+ setup
+
+ atf_check -s not-exit:0 -e match:"not found" -o ignore \
+ jls -jj
+
+ atf_check -s exit:0 \
+ jail -c name=j persist meta="a b c" env="CAB"
+
+ atf_check -s exit:0 -o inline:"a b c\n" \
+ jls -jj meta
+ atf_check -s exit:0 -o inline:"CAB\n" \
+ jls -jj env
+
+ atf_check -s exit:0 \
+ jail -m name=j meta="t1=A t2=B" env="CAB2"
+
+ atf_check -s exit:0 -o inline:"t1=A t2=B\n" \
+ jls -jj meta
+ atf_check -s exit:0 -o inline:"CAB2\n" \
+ jls -jj env
+}
+jail_modify_cleanup()
+{
+ jail -r j
+ return 0
+}
+
+atf_test_case "jail_add" "cleanup"
+jail_add_head()
+{
+ atf_set descr 'Test that metadata can be added to an existing jail with jail(8)'
+ atf_set require.user root
+ atf_set execenv jail
+}
+jail_add_body()
+{
+ setup
+
+ atf_check -s not-exit:0 -e match:"not found" -o ignore \
+ jls -jj
+
+ atf_check -s exit:0 \
+ jail -c name=j persist host.hostname=jail1
+
+ atf_check -s exit:0 -o inline:'""\n' \
+ jls -jj meta
+ atf_check -s exit:0 -o inline:'""\n' \
+ jls -jj env
+
+ atf_check -s exit:0 \
+ jail -m name=j meta="$(jot 3 1 3)" env="$(jot 2 11 12)"
+
+ atf_check -s exit:0 -o inline:"1\n2\n3\n" \
+ jls -jj meta
+ atf_check -s exit:0 -o inline:"11\n12\n" \
+ jls -jj env
+}
+jail_add_cleanup()
+{
+ jail -r j
+ return 0
+}
+
+atf_test_case "jail_reset" "cleanup"
+jail_reset_head()
+{
+ atf_set descr 'Test that metadata can be reset to an empty string with jail(8)'
+ atf_set require.user root
+ atf_set execenv jail
+}
+jail_reset_body()
+{
+ setup
+
+ atf_check -s not-exit:0 -e match:"not found" -o ignore \
+ jls -jj
+
+ atf_check -s exit:0 \
+ jail -c name=j persist meta="123" env="456"
+
+ atf_check -s exit:0 -o inline:"123\n" \
+ jls -jj meta
+ atf_check -s exit:0 -o inline:"456\n" \
+ jls -jj env
+
+ atf_check -s exit:0 \
+ jail -m name=j meta= env=
+
+ atf_check -s exit:0 -o inline:'""\n' \
+ jls -jj meta
+ atf_check -s exit:0 -o inline:'""\n' \
+ jls -jj env
+}
+jail_reset_cleanup()
+{
+ jail -r j
+ return 0
+}
+
+atf_test_case "jls_libxo_json" "cleanup"
+jls_libxo_json_head()
+{
+ atf_set descr 'Test that metadata can be read with jls(8) using libxo JSON'
+ atf_set require.user root
+ atf_set execenv jail
+}
+jls_libxo_json_body()
+{
+ setup
+
+ atf_check -s not-exit:0 -e match:"not found" -o ignore \
+ jls -jj
+
+ atf_check -s exit:0 \
+ jail -c name=j persist meta="a b c" env="1 2 3"
+
+ atf_check -s exit:0 -o inline:'{"__version": "2", "jail-information": {"jail": [{"name":"j","meta":"a b c"}]}}\n' \
+ jls -jj --libxo json name meta
+ atf_check -s exit:0 -o inline:'{"__version": "2", "jail-information": {"jail": [{"env":"1 2 3"}]}}\n' \
+ jls -jj --libxo json env
+}
+jls_libxo_json_cleanup()
+{
+ jail -r j
+ return 0
+}
+
+atf_test_case "flua_create" "cleanup"
+flua_create_head()
+{
+ atf_set descr 'Test that metadata can be set upon jail creation with flua'
+ atf_set require.user root
+ atf_set execenv jail
+}
+flua_create_body()
+{
+ setup
+
+ atf_check -s not-exit:0 -e match:"not found" -o ignore \
+ jls -jj
+
+ atf_check -s exit:0 \
+ /usr/libexec/flua -ljail -e 'jail.setparams("j", {["meta"]="t1 t2=v2", ["env"]="BAC", ["persist"]="true"}, jail.CREATE)'
+
+ atf_check -s exit:0 -o inline:"t1 t2=v2\n" \
+ /usr/libexec/flua -ljail -e 'jid, res = jail.getparams("j", {"meta"}); print(res["meta"])'
+ atf_check -s exit:0 -o inline:"BAC\n" \
+ /usr/libexec/flua -ljail -e 'jid, res = jail.getparams("j", {"env"}); print(res["env"])'
+}
+flua_create_cleanup()
+{
+ jail -r j
+ return 0
+}
+
+atf_test_case "flua_modify" "cleanup"
+flua_modify_head()
+{
+ atf_set descr 'Test that metadata can be changed with flua after jail creation'
+ atf_set require.user root
+ atf_set execenv jail
+}
+flua_modify_body()
+{
+ setup
+
+ atf_check -s not-exit:0 -e match:"not found" -o ignore \
+ jls -jj
+
+ atf_check -s exit:0 \
+ jail -c name=j persist meta="ABC" env="123"
+
+ atf_check -s exit:0 -o inline:"ABC\n" \
+ jls -jj meta
+ atf_check -s exit:0 -o inline:"123\n" \
+ jls -jj env
+
+ atf_check -s exit:0 \
+ /usr/libexec/flua -ljail -e 'jail.setparams("j", {["meta"]="t1 t2=v", ["env"]="4"}, jail.UPDATE)'
+
+ atf_check -s exit:0 -o inline:"t1 t2=v\n" \
+ jls -jj meta
+ atf_check -s exit:0 -o inline:"4\n" \
+ jls -jj env
+}
+flua_modify_cleanup()
+{
+ jail -r j
+ return 0
+}
+
+atf_test_case "env_readable_by_jail" "cleanup"
+env_readable_by_jail_head()
+{
+ atf_set descr 'Test that a jail can read its own env parameter via sysctl(8)'
+ atf_set require.user root
+ atf_set execenv jail
+}
+env_readable_by_jail_body()
+{
+ setup
+
+ atf_check -s not-exit:0 -e match:"not found" -o ignore \
+ jls -jj
+
+ atf_check -s exit:0 \
+ jail -c name=j persist meta="a b c" env="CBA"
+
+ atf_check -s exit:0 -o inline:"a b c\n" \
+ jls -jj meta
+ atf_check -s exit:0 -o inline:"CBA\n" \
+ jls -jj env
+
+ atf_check -s exit:0 -o inline:"CBA\n" \
+ jexec j sysctl -n security.jail.env
+}
+env_readable_by_jail_cleanup()
+{
+ jail -r j
+ return 0
+}
+
+atf_test_case "not_inheritable" "cleanup"
+not_inheritable_head()
+{
+ atf_set descr 'Test that a jail does not inherit metadata from its parent jail'
+ atf_set require.user root
+ atf_set execenv jail
+}
+not_inheritable_body()
+{
+ setup
+
+ atf_check -s not-exit:0 -e match:"not found" -o ignore \
+ jls -j parent
+
+ atf_check -s exit:0 \
+ jail -c name=parent children.max=1 persist meta="abc" env="cba"
+
+ jexec parent jail -c name=child persist
+
+ atf_check -s exit:0 -o inline:"abc\n" \
+ jls -j parent meta
+ atf_check -s exit:0 -o inline:'""\n' \
+ jls -j parent.child meta
+
+ atf_check -s exit:0 -o inline:"cba\n" \
+ jexec parent sysctl -n security.jail.env
+ atf_check -s exit:0 -o inline:"\n" \
+ jexec parent.child sysctl -n security.jail.env
+}
+not_inheritable_cleanup()
+{
+ jail -r parent.child
+ jail -r parent
+ return 0
+}
+
+atf_test_case "maxbufsize" "cleanup"
+maxbufsize_head()
+{
+ atf_set descr 'Test that metadata buffer maximum size can be changed'
+ atf_set require.user root
+ atf_set is.exclusive true
+}
+maxbufsize_body()
+{
+ setup
+
+ jn=jailmeta_maxbufsize
+
+ atf_check -s not-exit:0 -e match:"not found" -o ignore \
+ jls -j $jn
+
+ # the size counts string length and the trailing \0 char
+ origmax=$(sysctl -n security.jail.meta_maxbufsize)
+
+ # must be fine with current max
+ atf_check -s exit:0 \
+ jail -c name=$jn persist meta="$(printf %$((origmax-1))s)"
+ atf_check -s exit:0 -o inline:"${origmax}\n" \
+ jls -j $jn meta | wc -c
+ #
+ atf_check -s exit:0 \
+ jail -m name=$jn env="$(printf %$((origmax-1))s)"
+ atf_check -s exit:0 -o inline:"${origmax}\n" \
+ jls -j $jn env | wc -c
+
+ # should not allow exceeding current max
+ atf_check -s not-exit:0 -e match:"too large" \
+ jail -m name=$jn meta="$(printf %${origmax}s)"
+ #
+ atf_check -s not-exit:0 -e match:"too large" \
+ jail -m name=$jn env="$(printf %${origmax}s)"
+
+ # should allow the same size with increased max
+ newmax=$((origmax + 1))
+ sysctl security.jail.meta_maxbufsize=$newmax
+ atf_check -s exit:0 \
+ jail -m name=$jn meta="$(printf %${origmax}s)"
+ atf_check -s exit:0 -o inline:"${origmax}\n" \
+ jls -j $jn meta | wc -c
+ #
+ atf_check -s exit:0 \
+ jail -m name=$jn env="$(printf %${origmax}s)"
+ atf_check -s exit:0 -o inline:"${origmax}\n" \
+ jls -j $jn env | wc -c
+
+ # decrease back to the original max
+ sysctl security.jail.meta_maxbufsize=$origmax
+ atf_check -s not-exit:0 -e match:"too large" \
+ jail -m name=$jn meta="$(printf %${origmax}s)"
+ #
+ atf_check -s not-exit:0 -e match:"too large" \
+ jail -m name=$jn env="$(printf %${origmax}s)"
+
+ # the previously set long meta is still readable as is
+ # due to the soft limit remains higher than the hard limit
+ atf_check_equal '${newmax}' '$(sysctl -n security.jail.param.meta)'
+ atf_check_equal '${newmax}' '$(sysctl -n security.jail.param.env)'
+ atf_check -s exit:0 -o inline:"${origmax}\n" \
+ jls -j $jn meta | wc -c
+ #
+ atf_check -s exit:0 -o inline:"${origmax}\n" \
+ jls -j $jn env | wc -c
+}
+maxbufsize_cleanup()
+{
+ jail -r jailmeta_maxbufsize
+ return 0
+}
+
+atf_test_case "keyvalue" "cleanup"
+keyvalue_head()
+{
+ atf_set descr 'Test that metadata can be handled as a set of key=value\n strings using jail(8), jls(8), and flua'
+ atf_set require.user root
+ atf_set execenv jail
+}
+keyvalue_generic()
+{
+ local meta=$1
+
+ atf_check -sexit:0 -oinline:'""\n' jls -jj $meta
+
+ # Note: each sub-case depends on the results of the previous ones
+
+ # Should be able to extract a key added manually
+ atf_check -sexit:0 jail -m name=j $meta="a=1"
+ atf_check -sexit:0 -oinline:'a=1\n' jls -jj $meta
+ atf_check -sexit:0 -oinline:'1\n' jls -jj $meta.a
+ atf_check -sexit:0 jail -m name=j $meta="$(printf 'a=2\nb=3')"
+ atf_check -sexit:0 -oinline:'a=2\nb=3\n' jls -jj $meta
+ atf_check -sexit:0 -oinline:'2\n' jls -jj $meta.a
+ atf_check -sexit:0 -oinline:'3\n' jls -jj $meta.b
+
+ # Should provide nothing for a non-found key
+ atf_check -sexit:0 -oinline:'\n' jls -jj $meta.c
+
+ # Should be able to lookup multiple keys at once
+ atf_check -sexit:0 -oinline:'3 2\n' jls -jj $meta.b $meta.a
+
+ # Should be able to lookup keys and the whole buffer at once
+ atf_check -sexit:0 -oinline:'3 a=2\nb=3 2\n' jls -jj $meta.b $meta $meta.a
+
+ # Should be able to lookup a key using libxo-based JSON output
+ s='{"__version": "2", "jail-information": {"jail": [{"'$meta'.b":"3"}]}}\n'
+ atf_check -s exit:0 -o inline:"$s" jls -jj --libxo json $meta.b
+
+ # Should provide nothing for a non-found key using libxo-based JSON output
+ s='{"__version": "2", "jail-information": {"jail": [{}]}}\n'
+ atf_check -s exit:0 -o inline:"$s" jls -jj --libxo json $meta.c $meta.d
+
+ # Should be able to lookup a key using flua
+ atf_check -s exit:0 -o inline:"2\n" \
+ /usr/libexec/flua -ljail -e 'jid, res = jail.getparams("j", {"'$meta'.a"}); print(res["'$meta'.a"])'
+
+ # Should provide nil for a non-found key using flua
+ atf_check -s exit:0 -o inline:"true\n" \
+ /usr/libexec/flua -ljail -e 'jid, res = jail.getparams("j", {"'$meta'.meta"}); print(res["'$meta'.meta"] == nil)'
+
+ # Should allow resetting a buffer
+ atf_check -sexit:0 jail -m name=j $meta=
+ atf_check -sexit:0 -oinline:' "" \n' jls -jj $meta.c $meta $meta.a
+
+ # Should allow adding a new key
+ atf_check -sexit:0 jail -m name=j $meta.a=1
+ atf_check -sexit:0 -oinline:'1\n' jls -jj $meta.a
+ atf_check -sexit:0 -oinline:'a=1\n' jls -jj $meta
+
+ # Should allow adding multiple new keys at once
+ atf_check -sexit:0 jail -m name=j $meta.c=3 $meta.b=2
+ atf_check -sexit:0 -oinline:'3\n' jls -jj $meta.c
+ atf_check -sexit:0 -oinline:'2\n' jls -jj $meta.b
+ atf_check -sexit:0 -oinline:'b=2\nc=3\na=1\n' jls -jj $meta
+
+ # Should replace existing keys
+ atf_check -sexit:0 jail -m name=j $meta.a=A $meta.c=C
+ atf_check -sexit:0 -oinline:'A\n' jls -jj $meta.a
+ atf_check -sexit:0 -oinline:'C\n' jls -jj $meta.c
+ atf_check -sexit:0 -oinline:'c=C\na=A\nb=2\n' jls -jj $meta
+
+ # Should treat empty value correctly
+ atf_check -sexit:0 jail -m name=j $meta.a=
+ atf_check -sexit:0 -oinline:'""\n' jls -jj $meta.a
+ atf_check -sexit:0 -oinline:'a=\nc=C\nb=2\n' jls -jj $meta
+
+ # Should treat NULL value as a key removal
+ atf_check -sexit:0 -oinline:'2\n' jls -jj $meta.b
+ atf_check -sexit:0 jail -m name=j $meta.b
+ atf_check -sexit:0 -oinline:'\n' jls -jj $meta.b
+ atf_check -sexit:0 -oinline:'a=\nc=C\n' jls -jj $meta
+
+ # Should allow changing the whole buffer and per key at once (order matters)
+ atf_check -sexit:0 jail -m name=j $meta.a=1 $meta=ttt $meta.b=2
+ atf_check -sexit:0 -oinline:'\n' jls -jj $meta.a
+ atf_check -sexit:0 -oinline:'2\n' jls -jj $meta.b
+ atf_check -sexit:0 -oinline:'b=2\nttt\n' jls -jj $meta
+
+ # Should treat only the first equal sign as syntax
+ atf_check -sexit:0 jail -m name=j $meta.b==
+ atf_check -sexit:0 -oinline:'=\n' jls -jj $meta.b
+ atf_check -sexit:0 -oinline:'b==\nttt\n' jls -jj $meta
+
+ # Should allow adding or modifying keys using flua
+ atf_check -s exit:0 \
+ /usr/libexec/flua -ljail -e 'jail.setparams("j", {["'$meta.b'"]="ttt", ["'$meta'.c"]="C"}, jail.UPDATE)'
+ atf_check -sexit:0 -oinline:'ttt\n' jls -jj $meta.b
+ atf_check -sexit:0 -oinline:'C\n' jls -jj $meta.c
+
+ # Should allow key removal using flua
+ atf_check -s exit:0 \
+ /usr/libexec/flua -ljail -e 'jail.setparams("j", {["'$meta.c'"] = {}}, jail.UPDATE)'
+ atf_check -sexit:0 -oinline:'\n' jls -jj $meta.c
+ atf_check -s exit:0 \
+ /usr/libexec/flua -ljail -e 'jail.setparams("j", {["'$meta.b'"] = false}, jail.UPDATE)'
+ atf_check -sexit:0 -oinline:'\n' jls -jj $meta.b
+
+ # Should respectively support "jls -s" for a missing key
+ atf_check -sexit:0 -oinline:''$meta'.missing\n' jls -jj -s $meta.missing
+}
+keyvalue_body()
+{
+ setup
+
+ atf_check -s exit:0 \
+ jail -c name=j persist meta env
+
+ keyvalue_generic "meta"
+ keyvalue_generic "env"
+}
+keyvalue_cleanup()
+{
+ jail -r j
+ return 0
+}
+
+atf_test_case "keyvalue_contention" "cleanup"
+keyvalue_contention_head()
+{
+ atf_set descr 'Try to stress metadata read/write mechanism with some contention'
+ atf_set require.user root
+ atf_set execenv jail
+ atf_set timeout 30
+}
+keyvalue_stresser()
+{
+ local jailname=$1
+ local modifier=$2
+
+ while true
+ do
+ jail -m name=$jailname $modifier
+ done
+}
+keyvalue_contention_body()
+{
+ setup
+
+ atf_check -s exit:0 jail -c name=j persist meta env
+
+ keyvalue_stresser "j" "meta.a=1" &
+ apid=$!
+ keyvalue_stresser "j" "meta.b=2" &
+ bpid=$!
+ keyvalue_stresser "j" "env.c=3" &
+ cpid=$!
+ keyvalue_stresser "j" "env.d=4" &
+ dpid=$!
+
+ for it in $(jot 8)
+ do
+ jail -m name=j meta='meta=META' env='env=ENV'
+ sleep 1
+ atf_check -sexit:0 -oinline:'1\n' jls -jj meta.a
+ atf_check -sexit:0 -oinline:'2\n' jls -jj meta.b
+ atf_check -sexit:0 -oinline:'3\n' jls -jj env.c
+ atf_check -sexit:0 -oinline:'4\n' jls -jj env.d
+ atf_check -sexit:0 -oinline:'META\n' jls -jj meta.meta
+ atf_check -sexit:0 -oinline:'ENV\n' jls -jj env.env
+ done
+
+ # TODO: Think of adding a stresser on the kernel side which does
+ # osd_set() w/o allprison lock. It could test the compare
+ # and swap mechanism in jm_osd_method_set().
+
+ kill -9 $apid $bpid $cpid $dpid
+}
+keyvalue_contention_cleanup()
+{
+ jail -r j
+ return 0
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "jail_create"
+ atf_add_test_case "jail_modify"
+ atf_add_test_case "jail_add"
+ atf_add_test_case "jail_reset"
+
+ atf_add_test_case "jls_libxo_json"
+
+ atf_add_test_case "flua_create"
+ atf_add_test_case "flua_modify"
+
+ atf_add_test_case "env_readable_by_jail"
+ atf_add_test_case "not_inheritable"
+
+ atf_add_test_case "maxbufsize"
+
+ atf_add_test_case "keyvalue"
+ atf_add_test_case "keyvalue_contention"
+}
diff --git a/tests/sys/kern/kcov.c b/tests/sys/kern/kcov.c
new file mode 100644
index 000000000000..cecc0cda78ea
--- /dev/null
+++ b/tests/sys/kern/kcov.c
@@ -0,0 +1,477 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2018, 2019 Andrew Turner
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/kcov.h>
+#include <sys/mman.h>
+
+#include <machine/atomic.h>
+
+#include <fcntl.h>
+#include <pthread.h>
+#include <semaphore.h>
+
+#include <atf-c.h>
+
+static const char *modes[] = {
+ "PC tracing",
+ "comparison tracing",
+};
+
+static size_t page_size;
+
+static void
+init_page_size(void)
+{
+ page_size = getpagesize();
+}
+
+static int
+open_kcov(void)
+{
+ int fd;
+
+ fd = open("/dev/kcov", O_RDWR);
+ if (fd == -1)
+ atf_tc_skip("Failed to open /dev/kcov");
+
+ return (fd);
+}
+
+ATF_TC(kcov_bufsize);
+ATF_TC_HEAD(kcov_bufsize, tc)
+{
+ init_page_size();
+}
+
+ATF_TC_BODY(kcov_bufsize, tc)
+{
+ int fd;
+
+ fd = open_kcov();
+
+ ATF_CHECK(ioctl(fd, KIOSETBUFSIZE, 0) == -1);
+ ATF_CHECK(ioctl(fd, KIOSETBUFSIZE, 1) == -1);
+ ATF_CHECK(ioctl(fd, KIOSETBUFSIZE, 2) == 0);
+ ATF_CHECK(ioctl(fd, KIOSETBUFSIZE, 2) == -1);
+
+ close(fd);
+}
+
+ATF_TC(kcov_mmap);
+ATF_TC_HEAD(kcov_mmap, tc)
+{
+ init_page_size();
+}
+
+ATF_TC_BODY(kcov_mmap, tc)
+{
+ void *data1, *data2;
+ int fd;
+
+ fd = open_kcov();
+
+ ATF_CHECK(mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ fd, 0) == MAP_FAILED);
+
+ ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE,
+ 2 * page_size / KCOV_ENTRY_SIZE) == 0);
+
+ ATF_CHECK(mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ fd, 0) == MAP_FAILED);
+ ATF_CHECK(mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ fd, 0) == MAP_FAILED);
+ ATF_REQUIRE((data1 = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, 0)) != MAP_FAILED);
+ ATF_REQUIRE((data2 = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, 0)) != MAP_FAILED);
+
+ *(uint64_t *)data1 = 0x123456789abcdeful;
+ ATF_REQUIRE(*(uint64_t *)data2 == 0x123456789abcdefull);
+ *(uint64_t *)data2 = 0xfedcba9876543210ul;
+ ATF_REQUIRE(*(uint64_t *)data1 == 0xfedcba9876543210ull);
+
+ munmap(data1, 2 * page_size);
+ munmap(data2, 2 * page_size);
+
+ close(fd);
+}
+
+/* This shouldn't panic */
+ATF_TC(kcov_mmap_no_munmap);
+ATF_TC_HEAD(kcov_mmap_no_munmap, tc)
+{
+ init_page_size();
+}
+
+ATF_TC_BODY(kcov_mmap_no_munmap, tc)
+{
+ int fd;
+
+ fd = open_kcov();
+
+ ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0);
+
+ ATF_CHECK(mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ fd, 0) != MAP_FAILED);
+
+ close(fd);
+}
+
+ATF_TC(kcov_mmap_no_munmap_no_close);
+ATF_TC_HEAD(kcov_mmap_no_munmap_no_close, tc)
+{
+ init_page_size();
+}
+
+ATF_TC_BODY(kcov_mmap_no_munmap_no_close, tc)
+{
+ int fd;
+
+ fd = open_kcov();
+
+ ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0);
+
+ ATF_CHECK(mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ fd, 0) != MAP_FAILED);
+}
+
+static sem_t sem1, sem2;
+
+static void *
+kcov_mmap_enable_thread(void *data)
+{
+ int fd;
+
+ fd = open_kcov();
+ *(int *)data = fd;
+
+ ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0);
+ ATF_CHECK(mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ fd, 0) != MAP_FAILED);
+ ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == 0);
+
+ sem_post(&sem1);
+ sem_wait(&sem2);
+
+ return (NULL);
+}
+
+ATF_TC(kcov_mmap_enable_thread_close);
+ATF_TC_HEAD(kcov_mmap_enable_thread_close, tc)
+{
+ init_page_size();
+}
+
+ATF_TC_BODY(kcov_mmap_enable_thread_close, tc)
+{
+ pthread_t thread;
+ int fd;
+
+ sem_init(&sem1, 0, 0);
+ sem_init(&sem2, 0, 0);
+ pthread_create(&thread, NULL,
+ kcov_mmap_enable_thread, &fd);
+ sem_wait(&sem1);
+ close(fd);
+ sem_post(&sem2);
+ pthread_join(thread, NULL);
+}
+
+ATF_TC(kcov_enable);
+ATF_TC_HEAD(kcov_enable, tc)
+{
+ init_page_size();
+}
+
+ATF_TC_BODY(kcov_enable, tc)
+{
+ int fd;
+
+ fd = open_kcov();
+
+ ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == -1);
+
+ ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0);
+
+ /* We need to enable before disable */
+ ATF_CHECK(ioctl(fd, KIODISABLE, 0) == -1);
+
+ /* Check enabling works only with a valid trace method */
+ ATF_CHECK(ioctl(fd, KIOENABLE, -1) == -1);
+ ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == 0);
+ ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == -1);
+ ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_CMP) == -1);
+
+ /* Disable should only be called once */
+ ATF_CHECK(ioctl(fd, KIODISABLE, 0) == 0);
+ ATF_CHECK(ioctl(fd, KIODISABLE, 0) == -1);
+
+ /* Re-enabling should also work */
+ ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_CMP) == 0);
+ ATF_CHECK(ioctl(fd, KIODISABLE, 0) == 0);
+
+ close(fd);
+}
+
+ATF_TC(kcov_enable_no_disable);
+ATF_TC_HEAD(kcov_enable_no_disable, tc)
+{
+ init_page_size();
+}
+
+ATF_TC_BODY(kcov_enable_no_disable, tc)
+{
+ int fd;
+
+ fd = open_kcov();
+ ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0);
+ ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == 0);
+ close(fd);
+}
+
+ATF_TC(kcov_enable_no_disable_no_close);
+ATF_TC_HEAD(kcov_enable_no_disable_no_close, tc)
+{
+ init_page_size();
+}
+
+ATF_TC_BODY(kcov_enable_no_disable_no_close, tc)
+{
+ int fd;
+
+ fd = open_kcov();
+ ATF_REQUIRE(ioctl(fd, KIOSETBUFSIZE, page_size / KCOV_ENTRY_SIZE) == 0);
+ ATF_CHECK(ioctl(fd, KIOENABLE, KCOV_MODE_TRACE_PC) == 0);
+}
+
+static void *
+common_head(int *fdp)
+{
+ void *data;
+ int fd;
+
+ fd = open_kcov();
+
+ ATF_REQUIRE_MSG(ioctl(fd, KIOSETBUFSIZE,
+ page_size / KCOV_ENTRY_SIZE) == 0,
+ "Unable to set the kcov buffer size");
+
+ data = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ ATF_REQUIRE_MSG(data != MAP_FAILED, "Unable to mmap the kcov buffer");
+
+ *fdp = fd;
+ return (data);
+}
+
+static void
+common_tail(int fd, void *data)
+{
+
+ ATF_REQUIRE_MSG(munmap(data, page_size) == 0,
+ "Unable to unmap the kcov buffer");
+
+ close(fd);
+}
+
+static void
+basic_test(u_int mode)
+{
+ uint64_t *buf;
+ int fd;
+
+ buf = common_head(&fd);
+ ATF_REQUIRE_MSG(ioctl(fd, KIOENABLE, mode) == 0,
+ "Unable to enable kcov %s",
+ mode < nitems(modes) ? modes[mode] : "unknown mode");
+
+ atomic_store_64(&buf[0], 0);
+
+ sleep(0);
+ ATF_REQUIRE_MSG(atomic_load_64(&buf[0]) != 0, "No records found");
+
+ ATF_REQUIRE_MSG(ioctl(fd, KIODISABLE, 0) == 0,
+ "Unable to disable kcov");
+
+ common_tail(fd, buf);
+}
+
+ATF_TC(kcov_basic_pc);
+ATF_TC_HEAD(kcov_basic_pc, tc)
+{
+ init_page_size();
+}
+
+ATF_TC_BODY(kcov_basic_pc, tc)
+{
+ basic_test(KCOV_MODE_TRACE_PC);
+}
+
+ATF_TC(kcov_basic_cmp);
+ATF_TC_HEAD(kcov_basic_cmp, tc)
+{
+ init_page_size();
+}
+
+ATF_TC_BODY(kcov_basic_cmp, tc)
+{
+ basic_test(KCOV_MODE_TRACE_CMP);
+}
+
+static void *
+thread_test_helper(void *ptr)
+{
+ uint64_t *buf = ptr;
+
+ atomic_store_64(&buf[0], 0);
+ sleep(0);
+ ATF_REQUIRE_MSG(atomic_load_64(&buf[0]) == 0,
+ "Records changed in blocked thread");
+
+ return (NULL);
+}
+
+static void
+thread_test(u_int mode)
+{
+ pthread_t thread;
+ uint64_t *buf;
+ int fd;
+
+ buf = common_head(&fd);
+
+ ATF_REQUIRE_MSG(ioctl(fd, KIOENABLE, mode) == 0,
+ "Unable to enable kcov %s",
+ mode < nitems(modes) ? modes[mode] : "unknown mode");
+
+ pthread_create(&thread, NULL, thread_test_helper, buf);
+ pthread_join(thread, NULL);
+
+ ATF_REQUIRE_MSG(ioctl(fd, KIODISABLE, 0) == 0,
+ "Unable to disable kcov");
+
+ common_tail(fd, buf);
+}
+
+ATF_TC(kcov_thread_pc);
+ATF_TC_HEAD(kcov_thread_pc, tc)
+{
+ init_page_size();
+}
+
+ATF_TC_BODY(kcov_thread_pc, tc)
+{
+ thread_test(KCOV_MODE_TRACE_PC);
+}
+
+ATF_TC(kcov_thread_cmp);
+ATF_TC_HEAD(kcov_thread_cmp, tc)
+{
+ init_page_size();
+}
+
+ATF_TC_BODY(kcov_thread_cmp, tc)
+{
+ thread_test(KCOV_MODE_TRACE_CMP);
+}
+
+struct multi_thread_data {
+ uint64_t *buf;
+ int fd;
+ u_int mode;
+ int thread;
+};
+
+static void *
+multi_thread_test_helper(void *ptr)
+{
+ struct multi_thread_data *data = ptr;
+
+ ATF_REQUIRE_MSG(ioctl(data->fd, KIOENABLE, data->mode) == 0,
+ "Unable to enable kcov %s in thread %d",
+ data->mode < nitems(modes) ? modes[data->mode] : "unknown mode",
+ data->thread);
+
+ atomic_store_64(&data->buf[0], 0);
+ sleep(0);
+ ATF_REQUIRE_MSG(atomic_load_64(&data->buf[0]) != 0,
+ "No records found in thread %d", data->thread);
+
+ return (NULL);
+}
+
+ATF_TC(kcov_enable_multi_thread);
+ATF_TC_HEAD(kcov_enable_multi_thread, t)
+{
+ init_page_size();
+}
+
+ATF_TC_BODY(kcov_enable_multi_thread, t)
+{
+ struct multi_thread_data data;
+ pthread_t thread;
+
+ data.buf = common_head(&data.fd);
+
+ /* Run the thread to completion */
+ data.thread = 1;
+ data.mode = KCOV_MODE_TRACE_PC;
+ pthread_create(&thread, NULL, multi_thread_test_helper, &data);
+ pthread_join(thread, NULL);
+
+ /* Run it again to check enable works on the same fd */
+ data.thread = 2;
+ data.mode = KCOV_MODE_TRACE_CMP;
+ pthread_create(&thread, NULL, multi_thread_test_helper, &data);
+ pthread_join(thread, NULL);
+
+ common_tail(data.fd, data.buf);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, kcov_bufsize);
+ ATF_TP_ADD_TC(tp, kcov_mmap);
+ ATF_TP_ADD_TC(tp, kcov_mmap_no_munmap);
+ ATF_TP_ADD_TC(tp, kcov_mmap_no_munmap_no_close);
+ ATF_TP_ADD_TC(tp, kcov_enable);
+ ATF_TP_ADD_TC(tp, kcov_enable_no_disable);
+ ATF_TP_ADD_TC(tp, kcov_enable_no_disable_no_close);
+ ATF_TP_ADD_TC(tp, kcov_mmap_enable_thread_close);
+ ATF_TP_ADD_TC(tp, kcov_basic_pc);
+ ATF_TP_ADD_TC(tp, kcov_basic_cmp);
+ ATF_TP_ADD_TC(tp, kcov_thread_pc);
+ ATF_TP_ADD_TC(tp, kcov_thread_cmp);
+ ATF_TP_ADD_TC(tp, kcov_enable_multi_thread);
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/kern_copyin.c b/tests/sys/kern/kern_copyin.c
new file mode 100644
index 000000000000..e677081a5c70
--- /dev/null
+++ b/tests/sys/kern/kern_copyin.c
@@ -0,0 +1,155 @@
+/*-
+ * Copyright (c) 2015, 2020 The FreeBSD Foundation
+ *
+ * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/exec.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#include <sys/mman.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <atf-c.h>
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <machine/vmparam.h>
+
+static int scratch_file;
+
+static int
+copyin_checker(uintptr_t uaddr, size_t len)
+{
+ ssize_t ret;
+
+ ret = write(scratch_file, (const void *)uaddr, len);
+ return (ret == -1 ? errno : 0);
+}
+
+#if __SIZEOF_POINTER__ == 8
+/*
+ * A slightly more direct path to calling copyin(), but without the ability
+ * to specify a length.
+ */
+static int
+copyin_checker2(uintptr_t uaddr)
+{
+ int ret;
+
+ ret = fcntl(scratch_file, F_GETLK, (const void *)uaddr);
+ return (ret == -1 ? errno : 0);
+}
+#endif
+
+static int
+get_vm_layout(struct kinfo_vm_layout *kvm)
+{
+ size_t len;
+ int mib[4];
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_VM_LAYOUT;
+ mib[3] = getpid();
+ len = sizeof(*kvm);
+
+ return (sysctl(mib, nitems(mib), kvm, &len, NULL, 0));
+}
+
+#define FMAX ULONG_MAX
+#if __SIZEOF_POINTER__ == 8
+/* PR 257193 */
+#define ADDR_SIGNED 0x800000c000000000
+#endif
+
+ATF_TC_WITHOUT_HEAD(kern_copyin);
+ATF_TC_BODY(kern_copyin, tc)
+{
+ char template[] = "copyin.XXXXXX";
+ struct kinfo_vm_layout kvm;
+ uintptr_t maxuser;
+ long page_size;
+ void *addr;
+ int error;
+
+ addr = MAP_FAILED;
+
+ error = get_vm_layout(&kvm);
+ ATF_REQUIRE(error == 0);
+
+ page_size = sysconf(_SC_PAGESIZE);
+ ATF_REQUIRE(page_size != (long)-1);
+
+ maxuser = kvm.kvm_max_user_addr;
+ scratch_file = mkstemp(template);
+ ATF_REQUIRE(scratch_file != -1);
+ unlink(template);
+
+ /*
+ * Since the shared page address can be randomized we need to make
+ * sure that something is mapped at the top of the user address space.
+ * Otherwise reading bytes from maxuser-X will fail rendering this test
+ * useless.
+ */
+ if (kvm.kvm_shp_addr + kvm.kvm_shp_size < maxuser) {
+ addr = mmap((void *)(maxuser - page_size), page_size, PROT_READ,
+ MAP_ANON | MAP_FIXED, -1, 0);
+ ATF_REQUIRE(addr != MAP_FAILED);
+ }
+
+ ATF_CHECK(copyin_checker(0, 0) == 0);
+ ATF_CHECK(copyin_checker(maxuser - 10, 9) == 0);
+ ATF_CHECK(copyin_checker(maxuser - 10, 10) == 0);
+ ATF_CHECK(copyin_checker(maxuser - 10, 11) == EFAULT);
+ ATF_CHECK(copyin_checker(maxuser - 1, 1) == 0);
+ ATF_CHECK(copyin_checker(maxuser, 0) == 0);
+ ATF_CHECK(copyin_checker(maxuser, 1) == EFAULT);
+ ATF_CHECK(copyin_checker(maxuser, 2) == EFAULT);
+ ATF_CHECK(copyin_checker(maxuser + 1, 0) == 0);
+ ATF_CHECK(copyin_checker(maxuser + 1, 2) == EFAULT);
+ ATF_CHECK(copyin_checker(FMAX - 10, 9) == EFAULT);
+ ATF_CHECK(copyin_checker(FMAX - 10, 10) == EFAULT);
+ ATF_CHECK(copyin_checker(FMAX - 10, 11) == EFAULT);
+#if __SIZEOF_POINTER__ == 8
+ ATF_CHECK(copyin_checker(ADDR_SIGNED, 1) == EFAULT);
+ ATF_CHECK(copyin_checker2(ADDR_SIGNED) == EFAULT);
+#endif
+
+ if (addr != MAP_FAILED)
+ munmap(addr, PAGE_SIZE);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, kern_copyin);
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/kern_descrip_test.c b/tests/sys/kern/kern_descrip_test.c
new file mode 100644
index 000000000000..a315156c36de
--- /dev/null
+++ b/tests/sys/kern/kern_descrip_test.c
@@ -0,0 +1,209 @@
+/*-
+ * Copyright (c) 2014 EMC Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/limits.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+static volatile sig_atomic_t done;
+
+#define AFILE "afile"
+#define EXPANDBY 1000
+#define PARALLEL 4
+#define RENDEZVOUS "rendezvous"
+#define VALUE "value"
+
+ATF_TC_WITHOUT_HEAD(dup2__simple);
+ATF_TC_BODY(dup2__simple, tc)
+{
+ int fd1, fd2;
+ struct stat sb1, sb2;
+
+ ATF_REQUIRE((fd1 = open(AFILE, O_CREAT, 0644)) != -1);
+ fd2 = 27;
+ ATF_REQUIRE(dup2(fd1, fd2) != -1);
+ ATF_REQUIRE(fstat(fd1, &sb1) != -1);
+ ATF_REQUIRE(fstat(fd2, &sb2) != -1);
+ ATF_REQUIRE(bcmp(&sb1, &sb2, sizeof(sb1)) == 0);
+}
+
+ATF_TC(dup2__ebadf_when_2nd_arg_out_of_range);
+ATF_TC_HEAD(dup2__ebadf_when_2nd_arg_out_of_range, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Regression test for r234131");
+}
+
+ATF_TC_BODY(dup2__ebadf_when_2nd_arg_out_of_range, tc)
+{
+ int fd1, fd2, ret;
+
+ ATF_REQUIRE((fd1 = open(AFILE, O_CREAT, 0644)) != -1);
+ fd2 = INT_MAX;
+ ret = dup2(fd1, fd2);
+ ATF_CHECK_EQ(-1, ret);
+ ATF_CHECK_EQ(EBADF, errno);
+}
+
+static void
+handler(int s __unused)
+{
+ done++;
+}
+
+static void
+openfiles2(size_t n)
+{
+ size_t i;
+ int r;
+
+ errno = 0;
+ for (i = 0; i < n; i++) {
+ r = open(AFILE, O_RDONLY);
+ if (r < 0) {
+ fprintf(stderr, "open: %s\n", strerror(errno));
+ _exit(1);
+ }
+ }
+ kill(getppid(), SIGUSR1);
+
+ for (;;) {
+ if (access(RENDEZVOUS, R_OK) != 0)
+ break;
+ usleep(1000);
+ }
+ _exit(0);
+}
+
+static void
+openfiles(size_t n)
+{
+ int i, fd;
+
+ signal(SIGUSR1, handler);
+ ATF_REQUIRE((fd = open(AFILE, O_CREAT, 0644)) != -1);
+ close(fd);
+ ATF_REQUIRE((fd = open(RENDEZVOUS, O_CREAT, 0644)) != -1);
+ close(fd);
+ done = 0;
+ for (i = 0; i < PARALLEL; i++)
+ if (fork() == 0)
+ openfiles2(n / PARALLEL);
+ while (done != PARALLEL) {
+ usleep(1000);
+ ATF_REQUIRE_EQ_MSG(0, waitpid(-1, NULL, WNOHANG),
+ "a child exited unexpectedly");
+ }
+ unlink(RENDEZVOUS);
+ for (i = 0; i < PARALLEL; i++)
+ ATF_CHECK_MSG(wait(NULL) > 0, "wait: %s", strerror(errno));
+}
+
+ATF_TC_WITH_CLEANUP(kern_maxfiles__increase);
+ATF_TC_HEAD(kern_maxfiles__increase, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.config", "allow_sysctl_side_effects");
+ atf_tc_set_md_var(tc, "descr",
+ "Check kern.maxfiles expansion");
+}
+
+ATF_TC_BODY(kern_maxfiles__increase, tc)
+{
+ size_t oldlen;
+ int maxfiles, oldmaxfiles, current;
+ char buf[80];
+ struct rlimit rl;
+
+ oldlen = sizeof(maxfiles);
+ if (sysctlbyname("kern.maxfiles", &maxfiles, &oldlen, NULL, 0) == -1)
+ atf_tc_fail("getsysctlbyname(%s): %s", "kern.maxfiles",
+ strerror(errno));
+ if (sysctlbyname("kern.openfiles", &current, &oldlen, NULL, 0) == -1)
+ atf_tc_fail("getsysctlbyname(%s): %s", "kern.openfiles",
+ strerror(errno));
+
+ oldmaxfiles = maxfiles;
+
+ /* Store old kern.maxfiles in a symlink for cleanup */
+ snprintf(buf, sizeof(buf), "%d", oldmaxfiles);
+ if (symlink(buf, VALUE) == 1)
+ atf_tc_fail("symlink(%s, %s): %s", buf, VALUE,
+ strerror(errno));
+
+ maxfiles += EXPANDBY;
+ if (sysctlbyname("kern.maxfiles", NULL, 0, &maxfiles, oldlen) == -1)
+ atf_tc_fail("getsysctlbyname(%s): %s", "kern.maxfiles",
+ strerror(errno));
+
+ rl.rlim_cur = rl.rlim_max = maxfiles;
+ ATF_REQUIRE_EQ_MSG(0, setrlimit(RLIMIT_NOFILE, &rl),
+ "setrlimit(RLIMIT_NOFILE, %d): %s", maxfiles, strerror(errno));
+
+ openfiles(oldmaxfiles - current + EXPANDBY / 2);
+}
+
+ATF_TC_CLEANUP(kern_maxfiles__increase, tc)
+{
+ size_t oldlen;
+ int n, oldmaxfiles;
+ char buf[80];
+
+ if ((n = readlink(VALUE, buf, sizeof(buf))) > 0) {
+ buf[MIN((size_t)n, sizeof(buf) - 1)] = '\0';
+ if (sscanf(buf, "%d", &oldmaxfiles) == 1) {
+ oldlen = sizeof(oldmaxfiles);
+ (void) sysctlbyname("kern.maxfiles", NULL, 0,
+ &oldmaxfiles, oldlen);
+ }
+ }
+ (void)unlink(VALUE);
+ (void)unlink(AFILE);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, dup2__simple);
+ ATF_TP_ADD_TC(tp, dup2__ebadf_when_2nd_arg_out_of_range);
+ ATF_TP_ADD_TC(tp, kern_maxfiles__increase);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/kill_zombie.c b/tests/sys/kern/kill_zombie.c
new file mode 100644
index 000000000000..a62b8dbab816
--- /dev/null
+++ b/tests/sys/kern/kill_zombie.c
@@ -0,0 +1,63 @@
+/*-
+ * Copyright (c) 2018 Jilles Tjoelker
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/wait.h>
+
+#include <atf-c.h>
+#include <signal.h>
+#include <unistd.h>
+
+ATF_TC_WITHOUT_HEAD(kill_zombie);
+ATF_TC_BODY(kill_zombie, tc)
+{
+ pid_t child, pid;
+ int status, r;
+
+ child = fork();
+ ATF_REQUIRE(child != -1);
+ if (child == 0) {
+ _exit(42);
+ }
+
+ r = waitid(P_PID, child, NULL, WEXITED | WNOWAIT);
+ ATF_REQUIRE(r == 0);
+
+ r = kill(child, SIGTERM);
+ ATF_CHECK(r == 0);
+
+ status = -1;
+ pid = waitpid(child, &status, 0);
+ ATF_REQUIRE(pid == child);
+ ATF_CHECK(WIFEXITED(status) && WEXITSTATUS(status) == 42);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, kill_zombie);
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/ktls_test.c b/tests/sys/kern/ktls_test.c
new file mode 100644
index 000000000000..72497196b945
--- /dev/null
+++ b/tests/sys/kern/ktls_test.c
@@ -0,0 +1,2848 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2021 Netflix Inc.
+ * Written by: John Baldwin <jhb@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/endian.h>
+#include <sys/event.h>
+#include <sys/ktls.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <crypto/cryptodev.h>
+#include <assert.h>
+#include <err.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <netdb.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <atf-c.h>
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+
+static void
+require_ktls(void)
+{
+ size_t len;
+ bool enable;
+
+ len = sizeof(enable);
+ if (sysctlbyname("kern.ipc.tls.enable", &enable, &len, NULL, 0) == -1) {
+ if (errno == ENOENT)
+ atf_tc_skip("kernel does not support TLS offload");
+ atf_libc_error(errno, "Failed to read kern.ipc.tls.enable");
+ }
+
+ if (!enable)
+ atf_tc_skip("Kernel TLS is disabled");
+}
+
+#define ATF_REQUIRE_KTLS() require_ktls()
+
+static void
+check_tls_mode(const atf_tc_t *tc, int s, int sockopt)
+{
+ if (atf_tc_get_config_var_as_bool_wd(tc, "ktls.require_ifnet", false)) {
+ socklen_t len;
+ int mode;
+
+ len = sizeof(mode);
+ if (getsockopt(s, IPPROTO_TCP, sockopt, &mode, &len) == -1)
+ atf_libc_error(errno, "Failed to fetch TLS mode");
+
+ if (mode != TCP_TLS_MODE_IFNET)
+ atf_tc_skip("connection did not use ifnet TLS");
+ }
+
+ if (atf_tc_get_config_var_as_bool_wd(tc, "ktls.require_toe", false)) {
+ socklen_t len;
+ int mode;
+
+ len = sizeof(mode);
+ if (getsockopt(s, IPPROTO_TCP, sockopt, &mode, &len) == -1)
+ atf_libc_error(errno, "Failed to fetch TLS mode");
+
+ if (mode != TCP_TLS_MODE_TOE)
+ atf_tc_skip("connection did not use TOE TLS");
+ }
+}
+
+static void __printflike(2, 3)
+debug(const atf_tc_t *tc, const char *fmt, ...)
+{
+ if (!atf_tc_get_config_var_as_bool_wd(tc, "ktls.debug", false))
+ return;
+
+ va_list ap;
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+}
+
+static void
+debug_hexdump(const atf_tc_t *tc, const void *buf, int length,
+ const char *label)
+{
+ if (!atf_tc_get_config_var_as_bool_wd(tc, "ktls.debug", false))
+ return;
+
+ if (label != NULL)
+ printf("%s:\n", label);
+ hexdump(buf, length, NULL, 0);
+}
+
+static char
+rdigit(void)
+{
+ /* ASCII printable values between 0x20 and 0x7e */
+ return (0x20 + random() % (0x7f - 0x20));
+}
+
+static char *
+alloc_buffer(size_t len)
+{
+ char *buf;
+ size_t i;
+
+ if (len == 0)
+ return (NULL);
+ buf = malloc(len);
+ for (i = 0; i < len; i++)
+ buf[i] = rdigit();
+ return (buf);
+}
+
+static bool
+socketpair_tcp(int sv[2])
+{
+ struct pollfd pfd;
+ struct sockaddr_in sin;
+ socklen_t len;
+ int as, cs, ls;
+
+ ls = socket(PF_INET, SOCK_STREAM, 0);
+ if (ls == -1) {
+ warn("socket() for listen");
+ return (false);
+ }
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ if (bind(ls, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
+ warn("bind");
+ close(ls);
+ return (false);
+ }
+
+ if (listen(ls, 1) == -1) {
+ warn("listen");
+ close(ls);
+ return (false);
+ }
+
+ len = sizeof(sin);
+ if (getsockname(ls, (struct sockaddr *)&sin, &len) == -1) {
+ warn("getsockname");
+ close(ls);
+ return (false);
+ }
+
+ cs = socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
+ if (cs == -1) {
+ warn("socket() for connect");
+ close(ls);
+ return (false);
+ }
+
+ if (connect(cs, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
+ if (errno != EINPROGRESS) {
+ warn("connect");
+ close(ls);
+ close(cs);
+ return (false);
+ }
+ }
+
+ as = accept4(ls, NULL, NULL, SOCK_NONBLOCK);
+ if (as == -1) {
+ warn("accept4");
+ close(ls);
+ close(cs);
+ return (false);
+ }
+
+ close(ls);
+
+ pfd.fd = cs;
+ pfd.events = POLLOUT;
+ pfd.revents = 0;
+ ATF_REQUIRE_INTEQ(1, poll(&pfd, 1, INFTIM));
+ ATF_REQUIRE_INTEQ(POLLOUT, pfd.revents);
+
+ sv[0] = cs;
+ sv[1] = as;
+ return (true);
+}
+
+static bool
+echo_socket(const atf_tc_t *tc, int sv[2])
+{
+ const char *cause, *host, *port;
+ struct addrinfo hints, *ai, *tofree;
+ int error, flags, s;
+
+ host = atf_tc_get_config_var(tc, "ktls.host");
+ port = atf_tc_get_config_var_wd(tc, "ktls.port", "echo");
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ error = getaddrinfo(host, port, &hints, &tofree);
+ if (error != 0) {
+ warnx("getaddrinfo(%s:%s) failed: %s", host, port,
+ gai_strerror(error));
+ return (false);
+ }
+
+ cause = NULL;
+ for (ai = tofree; ai != NULL; ai = ai->ai_next) {
+ s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (s == -1) {
+ cause = "socket";
+ error = errno;
+ continue;
+ }
+
+ if (connect(s, ai->ai_addr, ai->ai_addrlen) == -1) {
+ cause = "connect";
+ error = errno;
+ close(s);
+ continue;
+ }
+
+ freeaddrinfo(tofree);
+
+ ATF_REQUIRE((flags = fcntl(s, F_GETFL)) != -1);
+ flags |= O_NONBLOCK;
+ ATF_REQUIRE(fcntl(s, F_SETFL, flags) != -1);
+
+ sv[0] = s;
+ sv[1] = s;
+ return (true);
+ }
+
+ warnc(error, "%s", cause);
+ freeaddrinfo(tofree);
+ return (false);
+}
+
+static bool
+open_sockets(const atf_tc_t *tc, int sv[2])
+{
+ if (atf_tc_has_config_var(tc, "ktls.host"))
+ return (echo_socket(tc, sv));
+ else
+ return (socketpair_tcp(sv));
+}
+
+static void
+close_sockets(int sv[2])
+{
+ if (sv[0] != sv[1])
+ ATF_REQUIRE(close(sv[1]) == 0);
+ ATF_REQUIRE(close(sv[0]) == 0);
+}
+
+static void
+close_sockets_ignore_errors(int sv[2])
+{
+ if (sv[0] != sv[1])
+ close(sv[1]);
+ close(sv[0]);
+}
+
+static void
+fd_set_blocking(int fd)
+{
+ int flags;
+
+ ATF_REQUIRE((flags = fcntl(fd, F_GETFL)) != -1);
+ flags &= ~O_NONBLOCK;
+ ATF_REQUIRE(fcntl(fd, F_SETFL, flags) != -1);
+}
+
+static bool
+cbc_crypt(const EVP_CIPHER *cipher, const char *key, const char *iv,
+ const char *input, char *output, size_t size, int enc)
+{
+ EVP_CIPHER_CTX *ctx;
+ int outl, total;
+
+ ctx = EVP_CIPHER_CTX_new();
+ if (ctx == NULL) {
+ warnx("EVP_CIPHER_CTX_new failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (false);
+ }
+ if (EVP_CipherInit_ex(ctx, cipher, NULL, (const u_char *)key,
+ (const u_char *)iv, enc) != 1) {
+ warnx("EVP_CipherInit_ex failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_CIPHER_CTX_free(ctx);
+ return (false);
+ }
+ EVP_CIPHER_CTX_set_padding(ctx, 0);
+ if (EVP_CipherUpdate(ctx, (u_char *)output, &outl,
+ (const u_char *)input, size) != 1) {
+ warnx("EVP_CipherUpdate failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_CIPHER_CTX_free(ctx);
+ return (false);
+ }
+ total = outl;
+ if (EVP_CipherFinal_ex(ctx, (u_char *)output + outl, &outl) != 1) {
+ warnx("EVP_CipherFinal_ex failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_CIPHER_CTX_free(ctx);
+ return (false);
+ }
+ total += outl;
+ if ((size_t)total != size) {
+ warnx("decrypt size mismatch: %zu vs %d", size, total);
+ EVP_CIPHER_CTX_free(ctx);
+ return (false);
+ }
+ EVP_CIPHER_CTX_free(ctx);
+ return (true);
+}
+
+static bool
+cbc_encrypt(const EVP_CIPHER *cipher, const char *key, const char *iv,
+ const char *input, char *output, size_t size)
+{
+ return (cbc_crypt(cipher, key, iv, input, output, size, 1));
+}
+
+static bool
+cbc_decrypt(const EVP_CIPHER *cipher, const char *key, const char *iv,
+ const char *input, char *output, size_t size)
+{
+ return (cbc_crypt(cipher, key, iv, input, output, size, 0));
+}
+
+static bool
+compute_hash(const EVP_MD *md, const void *key, size_t key_len, const void *aad,
+ size_t aad_len, const void *buffer, size_t len, void *digest,
+ u_int *digest_len)
+{
+ HMAC_CTX *ctx;
+
+ ctx = HMAC_CTX_new();
+ if (ctx == NULL) {
+ warnx("HMAC_CTX_new failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (false);
+ }
+ if (HMAC_Init_ex(ctx, key, key_len, md, NULL) != 1) {
+ warnx("HMAC_Init_ex failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ HMAC_CTX_free(ctx);
+ return (false);
+ }
+ if (HMAC_Update(ctx, aad, aad_len) != 1) {
+ warnx("HMAC_Update (aad) failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ HMAC_CTX_free(ctx);
+ return (false);
+ }
+ if (HMAC_Update(ctx, buffer, len) != 1) {
+ warnx("HMAC_Update (payload) failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ HMAC_CTX_free(ctx);
+ return (false);
+ }
+ if (HMAC_Final(ctx, digest, digest_len) != 1) {
+ warnx("HMAC_Final failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ HMAC_CTX_free(ctx);
+ return (false);
+ }
+ HMAC_CTX_free(ctx);
+ return (true);
+}
+
+static bool
+verify_hash(const EVP_MD *md, const void *key, size_t key_len, const void *aad,
+ size_t aad_len, const void *buffer, size_t len, const void *digest)
+{
+ unsigned char digest2[EVP_MAX_MD_SIZE];
+ u_int digest_len;
+
+ if (!compute_hash(md, key, key_len, aad, aad_len, buffer, len, digest2,
+ &digest_len))
+ return (false);
+ if (memcmp(digest, digest2, digest_len) != 0) {
+ warnx("HMAC mismatch");
+ return (false);
+ }
+ return (true);
+}
+
+static bool
+aead_encrypt(const EVP_CIPHER *cipher, const char *key, const char *nonce,
+ const void *aad, size_t aad_len, const char *input, char *output,
+ size_t size, char *tag, size_t tag_len)
+{
+ EVP_CIPHER_CTX *ctx;
+ int outl, total;
+
+ ctx = EVP_CIPHER_CTX_new();
+ if (ctx == NULL) {
+ warnx("EVP_CIPHER_CTX_new failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (false);
+ }
+ if (EVP_EncryptInit_ex(ctx, cipher, NULL, (const u_char *)key,
+ (const u_char *)nonce) != 1) {
+ warnx("EVP_EncryptInit_ex failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_CIPHER_CTX_free(ctx);
+ return (false);
+ }
+ EVP_CIPHER_CTX_set_padding(ctx, 0);
+ if (aad != NULL) {
+ if (EVP_EncryptUpdate(ctx, NULL, &outl, (const u_char *)aad,
+ aad_len) != 1) {
+ warnx("EVP_EncryptUpdate for AAD failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_CIPHER_CTX_free(ctx);
+ return (false);
+ }
+ }
+ if (EVP_EncryptUpdate(ctx, (u_char *)output, &outl,
+ (const u_char *)input, size) != 1) {
+ warnx("EVP_EncryptUpdate failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_CIPHER_CTX_free(ctx);
+ return (false);
+ }
+ total = outl;
+ if (EVP_EncryptFinal_ex(ctx, (u_char *)output + outl, &outl) != 1) {
+ warnx("EVP_EncryptFinal_ex failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_CIPHER_CTX_free(ctx);
+ return (false);
+ }
+ total += outl;
+ if ((size_t)total != size) {
+ warnx("encrypt size mismatch: %zu vs %d", size, total);
+ EVP_CIPHER_CTX_free(ctx);
+ return (false);
+ }
+ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tag_len, tag) !=
+ 1) {
+ warnx("EVP_CIPHER_CTX_ctrl(EVP_CTRL_AEAD_GET_TAG) failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_CIPHER_CTX_free(ctx);
+ return (false);
+ }
+ EVP_CIPHER_CTX_free(ctx);
+ return (true);
+}
+
+static bool
+aead_decrypt(const EVP_CIPHER *cipher, const char *key, const char *nonce,
+ const void *aad, size_t aad_len, const char *input, char *output,
+ size_t size, const char *tag, size_t tag_len)
+{
+ EVP_CIPHER_CTX *ctx;
+ int outl, total;
+ bool valid;
+
+ ctx = EVP_CIPHER_CTX_new();
+ if (ctx == NULL) {
+ warnx("EVP_CIPHER_CTX_new failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (false);
+ }
+ if (EVP_DecryptInit_ex(ctx, cipher, NULL, (const u_char *)key,
+ (const u_char *)nonce) != 1) {
+ warnx("EVP_DecryptInit_ex failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_CIPHER_CTX_free(ctx);
+ return (false);
+ }
+ EVP_CIPHER_CTX_set_padding(ctx, 0);
+ if (aad != NULL) {
+ if (EVP_DecryptUpdate(ctx, NULL, &outl, (const u_char *)aad,
+ aad_len) != 1) {
+ warnx("EVP_DecryptUpdate for AAD failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_CIPHER_CTX_free(ctx);
+ return (false);
+ }
+ }
+ if (EVP_DecryptUpdate(ctx, (u_char *)output, &outl,
+ (const u_char *)input, size) != 1) {
+ warnx("EVP_DecryptUpdate failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_CIPHER_CTX_free(ctx);
+ return (false);
+ }
+ total = outl;
+ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len,
+ __DECONST(char *, tag)) != 1) {
+ warnx("EVP_CIPHER_CTX_ctrl(EVP_CTRL_AEAD_SET_TAG) failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_CIPHER_CTX_free(ctx);
+ return (false);
+ }
+ valid = (EVP_DecryptFinal_ex(ctx, (u_char *)output + outl, &outl) == 1);
+ total += outl;
+ if ((size_t)total != size) {
+ warnx("decrypt size mismatch: %zu vs %d", size, total);
+ EVP_CIPHER_CTX_free(ctx);
+ return (false);
+ }
+ if (!valid)
+ warnx("tag mismatch");
+ EVP_CIPHER_CTX_free(ctx);
+ return (valid);
+}
+
+static void
+build_tls_enable(const atf_tc_t *tc, int cipher_alg, size_t cipher_key_len,
+ int auth_alg, int minor, uint64_t seqno, struct tls_enable *en)
+{
+ u_int auth_key_len, iv_len;
+
+ memset(en, 0, sizeof(*en));
+
+ switch (cipher_alg) {
+ case CRYPTO_AES_CBC:
+ if (minor == TLS_MINOR_VER_ZERO)
+ iv_len = AES_BLOCK_LEN;
+ else
+ iv_len = 0;
+ break;
+ case CRYPTO_AES_NIST_GCM_16:
+ if (minor == TLS_MINOR_VER_TWO)
+ iv_len = TLS_AEAD_GCM_LEN;
+ else
+ iv_len = TLS_1_3_GCM_IV_LEN;
+ break;
+ case CRYPTO_CHACHA20_POLY1305:
+ iv_len = TLS_CHACHA20_IV_LEN;
+ break;
+ default:
+ iv_len = 0;
+ break;
+ }
+ switch (auth_alg) {
+ case CRYPTO_SHA1_HMAC:
+ auth_key_len = SHA1_HASH_LEN;
+ break;
+ case CRYPTO_SHA2_256_HMAC:
+ auth_key_len = SHA2_256_HASH_LEN;
+ break;
+ case CRYPTO_SHA2_384_HMAC:
+ auth_key_len = SHA2_384_HASH_LEN;
+ break;
+ default:
+ auth_key_len = 0;
+ break;
+ }
+ en->cipher_key = alloc_buffer(cipher_key_len);
+ debug_hexdump(tc, en->cipher_key, cipher_key_len, "cipher key");
+ en->iv = alloc_buffer(iv_len);
+ if (iv_len != 0)
+ debug_hexdump(tc, en->iv, iv_len, "iv");
+ en->auth_key = alloc_buffer(auth_key_len);
+ if (auth_key_len != 0)
+ debug_hexdump(tc, en->auth_key, auth_key_len, "auth key");
+ en->cipher_algorithm = cipher_alg;
+ en->cipher_key_len = cipher_key_len;
+ en->iv_len = iv_len;
+ en->auth_algorithm = auth_alg;
+ en->auth_key_len = auth_key_len;
+ en->tls_vmajor = TLS_MAJOR_VER_ONE;
+ en->tls_vminor = minor;
+ be64enc(en->rec_seq, seqno);
+ debug(tc, "seqno: %ju\n", (uintmax_t)seqno);
+}
+
+static void
+free_tls_enable(struct tls_enable *en)
+{
+ free(__DECONST(void *, en->cipher_key));
+ free(__DECONST(void *, en->iv));
+ free(__DECONST(void *, en->auth_key));
+}
+
+static const EVP_CIPHER *
+tls_EVP_CIPHER(const struct tls_enable *en)
+{
+ switch (en->cipher_algorithm) {
+ case CRYPTO_AES_CBC:
+ switch (en->cipher_key_len) {
+ case 128 / 8:
+ return (EVP_aes_128_cbc());
+ case 256 / 8:
+ return (EVP_aes_256_cbc());
+ default:
+ return (NULL);
+ }
+ break;
+ case CRYPTO_AES_NIST_GCM_16:
+ switch (en->cipher_key_len) {
+ case 128 / 8:
+ return (EVP_aes_128_gcm());
+ case 256 / 8:
+ return (EVP_aes_256_gcm());
+ default:
+ return (NULL);
+ }
+ break;
+ case CRYPTO_CHACHA20_POLY1305:
+ return (EVP_chacha20_poly1305());
+ default:
+ return (NULL);
+ }
+}
+
+static const EVP_MD *
+tls_EVP_MD(const struct tls_enable *en)
+{
+ switch (en->auth_algorithm) {
+ case CRYPTO_SHA1_HMAC:
+ return (EVP_sha1());
+ case CRYPTO_SHA2_256_HMAC:
+ return (EVP_sha256());
+ case CRYPTO_SHA2_384_HMAC:
+ return (EVP_sha384());
+ default:
+ return (NULL);
+ }
+}
+
+static size_t
+tls_header_len(struct tls_enable *en)
+{
+ size_t len;
+
+ len = sizeof(struct tls_record_layer);
+ switch (en->cipher_algorithm) {
+ case CRYPTO_AES_CBC:
+ if (en->tls_vminor != TLS_MINOR_VER_ZERO)
+ len += AES_BLOCK_LEN;
+ return (len);
+ case CRYPTO_AES_NIST_GCM_16:
+ if (en->tls_vminor == TLS_MINOR_VER_TWO)
+ len += sizeof(uint64_t);
+ return (len);
+ case CRYPTO_CHACHA20_POLY1305:
+ return (len);
+ default:
+ return (0);
+ }
+}
+
+static size_t
+tls_mac_len(struct tls_enable *en)
+{
+ switch (en->cipher_algorithm) {
+ case CRYPTO_AES_CBC:
+ switch (en->auth_algorithm) {
+ case CRYPTO_SHA1_HMAC:
+ return (SHA1_HASH_LEN);
+ case CRYPTO_SHA2_256_HMAC:
+ return (SHA2_256_HASH_LEN);
+ case CRYPTO_SHA2_384_HMAC:
+ return (SHA2_384_HASH_LEN);
+ default:
+ return (0);
+ }
+ case CRYPTO_AES_NIST_GCM_16:
+ return (AES_GMAC_HASH_LEN);
+ case CRYPTO_CHACHA20_POLY1305:
+ return (POLY1305_HASH_LEN);
+ default:
+ return (0);
+ }
+}
+
+/* Includes maximum padding for MTE. */
+static size_t
+tls_trailer_len(struct tls_enable *en)
+{
+ size_t len;
+
+ len = tls_mac_len(en);
+ if (en->cipher_algorithm == CRYPTO_AES_CBC)
+ len += AES_BLOCK_LEN;
+ if (en->tls_vminor == TLS_MINOR_VER_THREE)
+ len++;
+ return (len);
+}
+
+/* Minimum valid record payload size for a given cipher suite. */
+static size_t
+tls_minimum_record_payload(struct tls_enable *en)
+{
+ size_t len;
+
+ len = tls_header_len(en);
+ if (en->cipher_algorithm == CRYPTO_AES_CBC)
+ len += roundup2(tls_mac_len(en) + 1, AES_BLOCK_LEN);
+ else
+ len += tls_mac_len(en);
+ if (en->tls_vminor == TLS_MINOR_VER_THREE)
+ len++;
+ return (len - sizeof(struct tls_record_layer));
+}
+
+/* 'len' is the length of the payload application data. */
+static void
+tls_mte_aad(struct tls_enable *en, size_t len,
+ const struct tls_record_layer *hdr, uint64_t seqno, struct tls_mac_data *ad)
+{
+ ad->seq = htobe64(seqno);
+ ad->type = hdr->tls_type;
+ ad->tls_vmajor = hdr->tls_vmajor;
+ ad->tls_vminor = hdr->tls_vminor;
+ ad->tls_length = htons(len);
+}
+
+static void
+tls_12_aead_aad(struct tls_enable *en, size_t len,
+ const struct tls_record_layer *hdr, uint64_t seqno,
+ struct tls_aead_data *ad)
+{
+ ad->seq = htobe64(seqno);
+ ad->type = hdr->tls_type;
+ ad->tls_vmajor = hdr->tls_vmajor;
+ ad->tls_vminor = hdr->tls_vminor;
+ ad->tls_length = htons(len);
+}
+
+static void
+tls_13_aad(struct tls_enable *en, const struct tls_record_layer *hdr,
+ uint64_t seqno, struct tls_aead_data_13 *ad)
+{
+ ad->type = hdr->tls_type;
+ ad->tls_vmajor = hdr->tls_vmajor;
+ ad->tls_vminor = hdr->tls_vminor;
+ ad->tls_length = hdr->tls_length;
+}
+
+static void
+tls_12_gcm_nonce(struct tls_enable *en, const struct tls_record_layer *hdr,
+ char *nonce)
+{
+ memcpy(nonce, en->iv, TLS_AEAD_GCM_LEN);
+ memcpy(nonce + TLS_AEAD_GCM_LEN, hdr + 1, sizeof(uint64_t));
+}
+
+static void
+tls_13_nonce(struct tls_enable *en, uint64_t seqno, char *nonce)
+{
+ static_assert(TLS_1_3_GCM_IV_LEN == TLS_CHACHA20_IV_LEN,
+ "TLS 1.3 nonce length mismatch");
+ memcpy(nonce, en->iv, TLS_1_3_GCM_IV_LEN);
+ *(uint64_t *)(nonce + 4) ^= htobe64(seqno);
+}
+
+/*
+ * Decrypt a TLS record 'len' bytes long at 'src' and store the result at
+ * 'dst'. If the TLS record header length doesn't match or 'dst' doesn't
+ * have sufficient room ('avail'), fail the test.
+ */
+static size_t
+decrypt_tls_aes_cbc_mte(const atf_tc_t *tc, struct tls_enable *en,
+ uint64_t seqno, const void *src, size_t len, void *dst, size_t avail,
+ uint8_t *record_type)
+{
+ const struct tls_record_layer *hdr;
+ struct tls_mac_data aad;
+ const char *iv;
+ char *buf;
+ size_t hdr_len, mac_len, payload_len;
+ int padding;
+
+ hdr = src;
+ hdr_len = tls_header_len(en);
+ mac_len = tls_mac_len(en);
+ ATF_REQUIRE_INTEQ(TLS_MAJOR_VER_ONE, hdr->tls_vmajor);
+ ATF_REQUIRE_INTEQ(en->tls_vminor, hdr->tls_vminor);
+ debug(tc, "decrypting MTE record seqno %ju:\n", (uintmax_t)seqno);
+ debug_hexdump(tc, src, len, NULL);
+
+ /* First, decrypt the outer payload into a temporary buffer. */
+ payload_len = len - hdr_len;
+ buf = malloc(payload_len);
+ if (en->tls_vminor == TLS_MINOR_VER_ZERO)
+ iv = en->iv;
+ else
+ iv = (void *)(hdr + 1);
+ debug_hexdump(tc, iv, AES_BLOCK_LEN, "iv");
+ ATF_REQUIRE(cbc_decrypt(tls_EVP_CIPHER(en), en->cipher_key, iv,
+ (const u_char *)src + hdr_len, buf, payload_len));
+ debug_hexdump(tc, buf, payload_len, "decrypted buffer");
+
+ /*
+ * Copy the last encrypted block to use as the IV for the next
+ * record for TLS 1.0.
+ */
+ if (en->tls_vminor == TLS_MINOR_VER_ZERO)
+ memcpy(__DECONST(uint8_t *, en->iv), (const u_char *)src +
+ (len - AES_BLOCK_LEN), AES_BLOCK_LEN);
+
+ /*
+ * Verify trailing padding and strip.
+ *
+ * The kernel always generates the smallest amount of padding.
+ */
+ padding = buf[payload_len - 1] + 1;
+ ATF_REQUIRE_MSG(padding > 0 && padding <= AES_BLOCK_LEN,
+ "invalid padding %d", padding);
+ ATF_REQUIRE_MSG(payload_len >= mac_len + padding,
+ "payload_len (%zu) < mac_len (%zu) + padding (%d)", payload_len,
+ mac_len, padding);
+ payload_len -= padding;
+
+ /* Verify HMAC. */
+ payload_len -= mac_len;
+ tls_mte_aad(en, payload_len, hdr, seqno, &aad);
+ debug_hexdump(tc, &aad, sizeof(aad), "aad");
+ ATF_REQUIRE(verify_hash(tls_EVP_MD(en), en->auth_key, en->auth_key_len,
+ &aad, sizeof(aad), buf, payload_len, buf + payload_len));
+
+ ATF_REQUIRE_MSG(payload_len <= avail, "payload_len (%zu) < avail (%zu)",
+ payload_len, avail);
+ memcpy(dst, buf, payload_len);
+ *record_type = hdr->tls_type;
+ return (payload_len);
+}
+
+static size_t
+decrypt_tls_12_aead(const atf_tc_t *tc, struct tls_enable *en, uint64_t seqno,
+ const void *src, size_t len, void *dst, uint8_t *record_type)
+{
+ const struct tls_record_layer *hdr;
+ struct tls_aead_data aad;
+ char nonce[12];
+ size_t hdr_len, mac_len, payload_len;
+
+ hdr = src;
+
+ hdr_len = tls_header_len(en);
+ mac_len = tls_mac_len(en);
+ payload_len = len - (hdr_len + mac_len);
+ ATF_REQUIRE_INTEQ(TLS_MAJOR_VER_ONE, hdr->tls_vmajor);
+ ATF_REQUIRE_INTEQ(TLS_MINOR_VER_TWO, hdr->tls_vminor);
+ debug(tc, "decrypting TLS 1.2 record seqno %ju:\n", (uintmax_t)seqno);
+ debug_hexdump(tc, src, len, NULL);
+
+ tls_12_aead_aad(en, payload_len, hdr, seqno, &aad);
+ debug_hexdump(tc, &aad, sizeof(aad), "aad");
+ if (en->cipher_algorithm == CRYPTO_AES_NIST_GCM_16)
+ tls_12_gcm_nonce(en, hdr, nonce);
+ else
+ tls_13_nonce(en, seqno, nonce);
+ debug_hexdump(tc, nonce, sizeof(nonce), "nonce");
+
+ ATF_REQUIRE(aead_decrypt(tls_EVP_CIPHER(en), en->cipher_key, nonce,
+ &aad, sizeof(aad), (const char *)src + hdr_len, dst, payload_len,
+ (const char *)src + hdr_len + payload_len, mac_len));
+
+ *record_type = hdr->tls_type;
+ return (payload_len);
+}
+
+static size_t
+decrypt_tls_13_aead(const atf_tc_t *tc, struct tls_enable *en, uint64_t seqno,
+ const void *src, size_t len, void *dst, uint8_t *record_type)
+{
+ const struct tls_record_layer *hdr;
+ struct tls_aead_data_13 aad;
+ char nonce[12];
+ char *buf;
+ size_t hdr_len, mac_len, payload_len;
+
+ hdr = src;
+
+ hdr_len = tls_header_len(en);
+ mac_len = tls_mac_len(en);
+ payload_len = len - (hdr_len + mac_len);
+ ATF_REQUIRE_MSG(payload_len >= 1,
+ "payload_len (%zu) too short: len %zu hdr_len %zu mac_len %zu",
+ payload_len, len, hdr_len, mac_len);
+ ATF_REQUIRE_INTEQ(TLS_RLTYPE_APP, hdr->tls_type);
+ ATF_REQUIRE_INTEQ(TLS_MAJOR_VER_ONE, hdr->tls_vmajor);
+ ATF_REQUIRE_INTEQ(TLS_MINOR_VER_TWO, hdr->tls_vminor);
+ debug(tc, "decrypting TLS 1.3 record seqno %ju:\n", (uintmax_t)seqno);
+ debug_hexdump(tc, src, len, NULL);
+
+ tls_13_aad(en, hdr, seqno, &aad);
+ debug_hexdump(tc, &aad, sizeof(aad), "aad");
+ tls_13_nonce(en, seqno, nonce);
+ debug_hexdump(tc, nonce, sizeof(nonce), "nonce");
+
+ /*
+ * Have to use a temporary buffer for the output due to the
+ * record type as the last byte of the trailer.
+ */
+ buf = malloc(payload_len);
+
+ ATF_REQUIRE(aead_decrypt(tls_EVP_CIPHER(en), en->cipher_key, nonce,
+ &aad, sizeof(aad), (const char *)src + hdr_len, buf, payload_len,
+ (const char *)src + hdr_len + payload_len, mac_len));
+ debug_hexdump(tc, buf, payload_len, "decrypted buffer");
+
+ /* Trim record type. */
+ *record_type = buf[payload_len - 1];
+ payload_len--;
+
+ memcpy(dst, buf, payload_len);
+ free(buf);
+
+ return (payload_len);
+}
+
+static size_t
+decrypt_tls_aead(const atf_tc_t *tc, struct tls_enable *en, uint64_t seqno,
+ const void *src, size_t len, void *dst, size_t avail, uint8_t *record_type)
+{
+ const struct tls_record_layer *hdr;
+ size_t payload_len;
+
+ hdr = src;
+ ATF_REQUIRE_INTEQ(len, ntohs(hdr->tls_length) + sizeof(*hdr));
+
+ payload_len = len - (tls_header_len(en) + tls_trailer_len(en));
+ ATF_REQUIRE_MSG(payload_len <= avail, "payload_len (%zu) > avail (%zu)",
+ payload_len, avail);
+
+ if (en->tls_vminor == TLS_MINOR_VER_TWO) {
+ ATF_REQUIRE_INTEQ(payload_len, decrypt_tls_12_aead(tc, en,
+ seqno, src, len, dst, record_type));
+ } else {
+ ATF_REQUIRE_INTEQ(payload_len, decrypt_tls_13_aead(tc, en,
+ seqno, src, len, dst, record_type));
+ }
+
+ return (payload_len);
+}
+
+static size_t
+decrypt_tls_record(const atf_tc_t *tc, struct tls_enable *en, uint64_t seqno,
+ const void *src, size_t len, void *dst, size_t avail, uint8_t *record_type)
+{
+ if (en->cipher_algorithm == CRYPTO_AES_CBC)
+ return (decrypt_tls_aes_cbc_mte(tc, en, seqno, src, len, dst,
+ avail, record_type));
+ else
+ return (decrypt_tls_aead(tc, en, seqno, src, len, dst, avail,
+ record_type));
+}
+
+/*
+ * Encrypt a TLS record of type 'record_type' with payload 'len' bytes
+ * long at 'src' and store the result at 'dst'. If 'dst' doesn't have
+ * sufficient room ('avail'), fail the test. 'padding' is the amount
+ * of additional padding to include beyond any amount mandated by the
+ * cipher suite.
+ */
+static size_t
+encrypt_tls_aes_cbc_mte(const atf_tc_t *tc, struct tls_enable *en,
+ uint8_t record_type, uint64_t seqno, const void *src, size_t len, void *dst,
+ size_t avail, size_t padding)
+{
+ struct tls_record_layer *hdr;
+ struct tls_mac_data aad;
+ char *buf, *iv;
+ size_t hdr_len, mac_len, record_len;
+ u_int digest_len, i;
+
+ ATF_REQUIRE_INTEQ(0, padding % 16);
+
+ hdr = dst;
+ buf = dst;
+
+ debug(tc, "encrypting MTE record seqno %ju:\n", (uintmax_t)seqno);
+ hdr_len = tls_header_len(en);
+ mac_len = tls_mac_len(en);
+ padding += (AES_BLOCK_LEN - (len + mac_len) % AES_BLOCK_LEN);
+ ATF_REQUIRE_MSG(padding > 0 && padding <= 255, "invalid padding (%zu)",
+ padding);
+
+ record_len = hdr_len + len + mac_len + padding;
+ ATF_REQUIRE_MSG(record_len <= avail, "record_len (%zu) > avail (%zu): "
+ "hdr_len %zu, len %zu, mac_len %zu, padding %zu", record_len,
+ avail, hdr_len, len, mac_len, padding);
+
+ hdr->tls_type = record_type;
+ hdr->tls_vmajor = TLS_MAJOR_VER_ONE;
+ hdr->tls_vminor = en->tls_vminor;
+ hdr->tls_length = htons(record_len - sizeof(*hdr));
+ iv = (char *)(hdr + 1);
+ for (i = 0; i < AES_BLOCK_LEN; i++)
+ iv[i] = rdigit();
+ debug_hexdump(tc, iv, AES_BLOCK_LEN, "explicit IV");
+
+ /* Copy plaintext to ciphertext region. */
+ memcpy(buf + hdr_len, src, len);
+
+ /* Compute HMAC. */
+ tls_mte_aad(en, len, hdr, seqno, &aad);
+ debug_hexdump(tc, &aad, sizeof(aad), "aad");
+ debug_hexdump(tc, src, len, "plaintext");
+ ATF_REQUIRE(compute_hash(tls_EVP_MD(en), en->auth_key, en->auth_key_len,
+ &aad, sizeof(aad), src, len, buf + hdr_len + len, &digest_len));
+ ATF_REQUIRE_INTEQ(mac_len, digest_len);
+
+ /* Store padding. */
+ for (i = 0; i < padding; i++)
+ buf[hdr_len + len + mac_len + i] = padding - 1;
+ debug_hexdump(tc, buf + hdr_len + len, mac_len + padding,
+ "MAC and padding");
+
+ /* Encrypt the record. */
+ ATF_REQUIRE(cbc_encrypt(tls_EVP_CIPHER(en), en->cipher_key, iv,
+ buf + hdr_len, buf + hdr_len, len + mac_len + padding));
+ debug_hexdump(tc, dst, record_len, "encrypted record");
+
+ return (record_len);
+}
+
+static size_t
+encrypt_tls_12_aead(const atf_tc_t *tc, struct tls_enable *en,
+ uint8_t record_type, uint64_t seqno, const void *src, size_t len, void *dst)
+{
+ struct tls_record_layer *hdr;
+ struct tls_aead_data aad;
+ char nonce[12];
+ size_t hdr_len, mac_len, record_len;
+
+ hdr = dst;
+
+ debug(tc, "encrypting TLS 1.2 record seqno %ju:\n", (uintmax_t)seqno);
+ hdr_len = tls_header_len(en);
+ mac_len = tls_mac_len(en);
+ record_len = hdr_len + len + mac_len;
+
+ hdr->tls_type = record_type;
+ hdr->tls_vmajor = TLS_MAJOR_VER_ONE;
+ hdr->tls_vminor = TLS_MINOR_VER_TWO;
+ hdr->tls_length = htons(record_len - sizeof(*hdr));
+ if (en->cipher_algorithm == CRYPTO_AES_NIST_GCM_16)
+ memcpy(hdr + 1, &seqno, sizeof(seqno));
+
+ tls_12_aead_aad(en, len, hdr, seqno, &aad);
+ debug_hexdump(tc, &aad, sizeof(aad), "aad");
+ if (en->cipher_algorithm == CRYPTO_AES_NIST_GCM_16)
+ tls_12_gcm_nonce(en, hdr, nonce);
+ else
+ tls_13_nonce(en, seqno, nonce);
+ debug_hexdump(tc, nonce, sizeof(nonce), "nonce");
+
+ debug_hexdump(tc, src, len, "plaintext");
+ ATF_REQUIRE(aead_encrypt(tls_EVP_CIPHER(en), en->cipher_key, nonce,
+ &aad, sizeof(aad), src, (char *)dst + hdr_len, len,
+ (char *)dst + hdr_len + len, mac_len));
+ debug_hexdump(tc, dst, record_len, "encrypted record");
+
+ return (record_len);
+}
+
+static size_t
+encrypt_tls_13_aead(const atf_tc_t *tc, struct tls_enable *en,
+ uint8_t record_type, uint64_t seqno, const void *src, size_t len, void *dst,
+ size_t padding)
+{
+ struct tls_record_layer *hdr;
+ struct tls_aead_data_13 aad;
+ char nonce[12];
+ char *buf;
+ size_t hdr_len, mac_len, record_len;
+
+ hdr = dst;
+
+ debug(tc, "encrypting TLS 1.3 record seqno %ju:\n", (uintmax_t)seqno);
+ hdr_len = tls_header_len(en);
+ mac_len = tls_mac_len(en);
+ record_len = hdr_len + len + 1 + padding + mac_len;
+
+ hdr->tls_type = TLS_RLTYPE_APP;
+ hdr->tls_vmajor = TLS_MAJOR_VER_ONE;
+ hdr->tls_vminor = TLS_MINOR_VER_TWO;
+ hdr->tls_length = htons(record_len - sizeof(*hdr));
+
+ tls_13_aad(en, hdr, seqno, &aad);
+ debug_hexdump(tc, &aad, sizeof(aad), "aad");
+ tls_13_nonce(en, seqno, nonce);
+ debug_hexdump(tc, nonce, sizeof(nonce), "nonce");
+
+ /*
+ * Have to use a temporary buffer for the input so that the record
+ * type can be appended.
+ */
+ buf = malloc(len + 1 + padding);
+ memcpy(buf, src, len);
+ buf[len] = record_type;
+ memset(buf + len + 1, 0, padding);
+ debug_hexdump(tc, buf, len + 1 + padding, "plaintext + type + padding");
+
+ ATF_REQUIRE(aead_encrypt(tls_EVP_CIPHER(en), en->cipher_key, nonce,
+ &aad, sizeof(aad), buf, (char *)dst + hdr_len, len + 1 + padding,
+ (char *)dst + hdr_len + len + 1 + padding, mac_len));
+ debug_hexdump(tc, dst, record_len, "encrypted record");
+
+ free(buf);
+
+ return (record_len);
+}
+
+static size_t
+encrypt_tls_aead(const atf_tc_t *tc, struct tls_enable *en,
+ uint8_t record_type, uint64_t seqno, const void *src, size_t len, void *dst,
+ size_t avail, size_t padding)
+{
+ size_t record_len;
+
+ record_len = tls_header_len(en) + len + padding + tls_trailer_len(en);
+ ATF_REQUIRE_MSG(record_len <= avail, "record_len (%zu) > avail (%zu): "
+ "header %zu len %zu padding %zu trailer %zu", record_len, avail,
+ tls_header_len(en), len, padding, tls_trailer_len(en));
+
+ if (en->tls_vminor == TLS_MINOR_VER_TWO) {
+ ATF_REQUIRE_INTEQ(0, padding);
+ ATF_REQUIRE_INTEQ(record_len, encrypt_tls_12_aead(tc, en,
+ record_type, seqno, src, len, dst));
+ } else
+ ATF_REQUIRE_INTEQ(record_len, encrypt_tls_13_aead(tc, en,
+ record_type, seqno, src, len, dst, padding));
+
+ return (record_len);
+}
+
+static size_t
+encrypt_tls_record(const atf_tc_t *tc, struct tls_enable *en,
+ uint8_t record_type, uint64_t seqno, const void *src, size_t len, void *dst,
+ size_t avail, size_t padding)
+{
+ if (en->cipher_algorithm == CRYPTO_AES_CBC)
+ return (encrypt_tls_aes_cbc_mte(tc, en, record_type, seqno, src,
+ len, dst, avail, padding));
+ else
+ return (encrypt_tls_aead(tc, en, record_type, seqno, src, len,
+ dst, avail, padding));
+}
+
+static void
+test_ktls_transmit_app_data(const atf_tc_t *tc, struct tls_enable *en,
+ uint64_t seqno, size_t len)
+{
+ struct kevent ev;
+ struct tls_record_layer *hdr;
+ char *plaintext, *decrypted, *outbuf;
+ size_t decrypted_len, outbuf_len, outbuf_cap, record_len, written;
+ ssize_t rv;
+ int kq, sockets[2];
+ uint8_t record_type;
+
+ plaintext = alloc_buffer(len);
+ debug_hexdump(tc, plaintext, len, "plaintext");
+ decrypted = malloc(len);
+ outbuf_cap = tls_header_len(en) + TLS_MAX_MSG_SIZE_V10_2 +
+ tls_trailer_len(en);
+ outbuf = malloc(outbuf_cap);
+ hdr = (struct tls_record_layer *)outbuf;
+
+ ATF_REQUIRE((kq = kqueue()) != -1);
+
+ ATF_REQUIRE_MSG(open_sockets(tc, sockets), "failed to create sockets");
+
+ ATF_REQUIRE(setsockopt(sockets[1], IPPROTO_TCP, TCP_TXTLS_ENABLE, en,
+ sizeof(*en)) == 0);
+ check_tls_mode(tc, sockets[1], TCP_TXTLS_MODE);
+
+ EV_SET(&ev, sockets[0], EVFILT_READ, EV_ADD, 0, 0, NULL);
+ ATF_REQUIRE(kevent(kq, &ev, 1, NULL, 0, NULL) == 0);
+ EV_SET(&ev, sockets[1], EVFILT_WRITE, EV_ADD, 0, 0, NULL);
+ ATF_REQUIRE(kevent(kq, &ev, 1, NULL, 0, NULL) == 0);
+
+ decrypted_len = 0;
+ outbuf_len = 0;
+ written = 0;
+
+ while (decrypted_len != len) {
+ ATF_REQUIRE(kevent(kq, NULL, 0, &ev, 1, NULL) == 1);
+
+ switch (ev.filter) {
+ case EVFILT_WRITE:
+ /* Try to write any remaining data. */
+ rv = write(ev.ident, plaintext + written,
+ len - written);
+ ATF_REQUIRE_MSG(rv > 0,
+ "failed to write to socket");
+ written += rv;
+ if (written == len) {
+ ev.flags = EV_DISABLE;
+ ATF_REQUIRE(kevent(kq, &ev, 1, NULL, 0,
+ NULL) == 0);
+ }
+ break;
+
+ case EVFILT_READ:
+ ATF_REQUIRE((ev.flags & EV_EOF) == 0);
+
+ /*
+ * Try to read data for the next TLS record
+ * into outbuf. Start by reading the header
+ * to determine how much additional data to
+ * read.
+ */
+ if (outbuf_len < sizeof(struct tls_record_layer)) {
+ rv = read(ev.ident, outbuf + outbuf_len,
+ sizeof(struct tls_record_layer) -
+ outbuf_len);
+ ATF_REQUIRE_MSG(rv > 0,
+ "failed to read from socket");
+ outbuf_len += rv;
+
+ if (outbuf_len ==
+ sizeof(struct tls_record_layer)) {
+ debug(tc, "TLS header for seqno %ju:\n",
+ (uintmax_t)seqno);
+ debug_hexdump(tc, outbuf, outbuf_len,
+ NULL);
+ }
+ }
+
+ if (outbuf_len < sizeof(struct tls_record_layer))
+ break;
+
+ record_len = sizeof(struct tls_record_layer) +
+ ntohs(hdr->tls_length);
+ debug(tc, "record_len %zu outbuf_cap %zu\n",
+ record_len, outbuf_cap);
+ ATF_REQUIRE(record_len <= outbuf_cap);
+ ATF_REQUIRE(record_len > outbuf_len);
+ rv = read(ev.ident, outbuf + outbuf_len,
+ record_len - outbuf_len);
+ if (rv == -1 && errno == EAGAIN)
+ break;
+ ATF_REQUIRE_MSG(rv > 0,
+ "failed to read from socket: %s", strerror(errno));
+
+ outbuf_len += rv;
+ if (outbuf_len == record_len) {
+ decrypted_len += decrypt_tls_record(tc, en,
+ seqno, outbuf, outbuf_len,
+ decrypted + decrypted_len,
+ len - decrypted_len, &record_type);
+ ATF_REQUIRE_INTEQ(TLS_RLTYPE_APP, record_type);
+
+ seqno++;
+ outbuf_len = 0;
+ }
+ break;
+ }
+ }
+
+ ATF_REQUIRE_MSG(written == decrypted_len,
+ "read %zu decrypted bytes, but wrote %zu", decrypted_len, written);
+
+ ATF_REQUIRE(memcmp(plaintext, decrypted, len) == 0);
+
+ free(outbuf);
+ free(decrypted);
+ free(plaintext);
+
+ close_sockets(sockets);
+ ATF_REQUIRE(close(kq) == 0);
+}
+
+static void
+ktls_send_control_message(int fd, uint8_t type, void *data, size_t len)
+{
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ char cbuf[CMSG_SPACE(sizeof(type))];
+ struct iovec iov;
+
+ memset(&msg, 0, sizeof(msg));
+
+ msg.msg_control = cbuf;
+ msg.msg_controllen = sizeof(cbuf);
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = IPPROTO_TCP;
+ cmsg->cmsg_type = TLS_SET_RECORD_TYPE;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(type));
+ *(uint8_t *)CMSG_DATA(cmsg) = type;
+
+ iov.iov_base = data;
+ iov.iov_len = len;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ ATF_REQUIRE_INTEQ((ssize_t)len, sendmsg(fd, &msg, 0));
+}
+
+static void
+test_ktls_transmit_control(const atf_tc_t *tc, struct tls_enable *en,
+ uint64_t seqno, uint8_t type, size_t len)
+{
+ struct tls_record_layer *hdr;
+ char *plaintext, *decrypted, *outbuf;
+ size_t outbuf_cap, payload_len, record_len;
+ ssize_t rv;
+ int sockets[2];
+ uint8_t record_type;
+
+ ATF_REQUIRE(len <= TLS_MAX_MSG_SIZE_V10_2);
+
+ plaintext = alloc_buffer(len);
+ decrypted = malloc(len);
+ outbuf_cap = tls_header_len(en) + len + tls_trailer_len(en);
+ outbuf = malloc(outbuf_cap);
+ hdr = (struct tls_record_layer *)outbuf;
+
+ ATF_REQUIRE_MSG(open_sockets(tc, sockets), "failed to create sockets");
+
+ ATF_REQUIRE(setsockopt(sockets[1], IPPROTO_TCP, TCP_TXTLS_ENABLE, en,
+ sizeof(*en)) == 0);
+ check_tls_mode(tc, sockets[1], TCP_TXTLS_MODE);
+
+ fd_set_blocking(sockets[0]);
+ fd_set_blocking(sockets[1]);
+
+ ktls_send_control_message(sockets[1], type, plaintext, len);
+
+ /*
+ * First read the header to determine how much additional data
+ * to read.
+ */
+ rv = read(sockets[0], outbuf, sizeof(struct tls_record_layer));
+ ATF_REQUIRE_INTEQ(sizeof(struct tls_record_layer), rv);
+ payload_len = ntohs(hdr->tls_length);
+ record_len = payload_len + sizeof(struct tls_record_layer);
+ ATF_REQUIRE_MSG(record_len <= outbuf_cap,
+ "record_len (%zu) > outbuf_cap (%zu)", record_len, outbuf_cap);
+ rv = read(sockets[0], outbuf + sizeof(struct tls_record_layer),
+ payload_len);
+ ATF_REQUIRE_INTEQ((ssize_t)payload_len, rv);
+
+ rv = decrypt_tls_record(tc, en, seqno, outbuf, record_len, decrypted,
+ len, &record_type);
+
+ ATF_REQUIRE_MSG((ssize_t)len == rv,
+ "read %zd decrypted bytes, but wrote %zu", rv, len);
+ ATF_REQUIRE_INTEQ(type, record_type);
+
+ ATF_REQUIRE(memcmp(plaintext, decrypted, len) == 0);
+
+ free(outbuf);
+ free(decrypted);
+ free(plaintext);
+
+ close_sockets(sockets);
+}
+
+static void
+test_ktls_transmit_empty_fragment(const atf_tc_t *tc, struct tls_enable *en,
+ uint64_t seqno)
+{
+ struct tls_record_layer *hdr;
+ char *outbuf;
+ size_t outbuf_cap, payload_len, record_len;
+ ssize_t rv;
+ int sockets[2];
+ uint8_t record_type;
+
+ outbuf_cap = tls_header_len(en) + tls_trailer_len(en);
+ outbuf = malloc(outbuf_cap);
+ hdr = (struct tls_record_layer *)outbuf;
+
+ ATF_REQUIRE_MSG(open_sockets(tc, sockets), "failed to create sockets");
+
+ ATF_REQUIRE(setsockopt(sockets[1], IPPROTO_TCP, TCP_TXTLS_ENABLE, en,
+ sizeof(*en)) == 0);
+ check_tls_mode(tc, sockets[1], TCP_TXTLS_MODE);
+
+ fd_set_blocking(sockets[0]);
+ fd_set_blocking(sockets[1]);
+
+ /*
+ * A write of zero bytes should send an empty fragment only for
+ * TLS 1.0, otherwise an error should be raised.
+ */
+ rv = write(sockets[1], NULL, 0);
+ if (rv == 0) {
+ ATF_REQUIRE_INTEQ(CRYPTO_AES_CBC, en->cipher_algorithm);
+ ATF_REQUIRE_INTEQ(TLS_MINOR_VER_ZERO, en->tls_vminor);
+ } else {
+ ATF_REQUIRE_INTEQ(-1, rv);
+ ATF_REQUIRE_ERRNO(EINVAL, true);
+ goto out;
+ }
+
+ /*
+ * First read the header to determine how much additional data
+ * to read.
+ */
+ rv = read(sockets[0], outbuf, sizeof(struct tls_record_layer));
+ ATF_REQUIRE_INTEQ(sizeof(struct tls_record_layer), rv);
+ payload_len = ntohs(hdr->tls_length);
+ record_len = payload_len + sizeof(struct tls_record_layer);
+ ATF_REQUIRE_MSG(record_len <= outbuf_cap,
+ "record_len (%zu) > outbuf_cap (%zu)", record_len, outbuf_cap);
+ rv = read(sockets[0], outbuf + sizeof(struct tls_record_layer),
+ payload_len);
+ ATF_REQUIRE_INTEQ((ssize_t)payload_len, rv);
+
+ rv = decrypt_tls_record(tc, en, seqno, outbuf, record_len, NULL, 0,
+ &record_type);
+
+ ATF_REQUIRE_MSG(rv == 0,
+ "read %zd decrypted bytes for an empty fragment", rv);
+ ATF_REQUIRE_INTEQ(TLS_RLTYPE_APP, record_type);
+
+out:
+ free(outbuf);
+
+ close_sockets(sockets);
+}
+
+static size_t
+ktls_receive_tls_record(struct tls_enable *en, int fd, uint8_t record_type,
+ void *data, size_t len)
+{
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ struct tls_get_record *tgr;
+ char cbuf[CMSG_SPACE(sizeof(*tgr))];
+ struct iovec iov;
+ ssize_t rv;
+
+ memset(&msg, 0, sizeof(msg));
+
+ msg.msg_control = cbuf;
+ msg.msg_controllen = sizeof(cbuf);
+
+ iov.iov_base = data;
+ iov.iov_len = len;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ ATF_REQUIRE((rv = recvmsg(fd, &msg, 0)) > 0);
+
+ ATF_REQUIRE((msg.msg_flags & (MSG_EOR | MSG_CTRUNC)) == MSG_EOR);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ ATF_REQUIRE(cmsg != NULL);
+ ATF_REQUIRE_INTEQ(IPPROTO_TCP, cmsg->cmsg_level);
+ ATF_REQUIRE_INTEQ(TLS_GET_RECORD, cmsg->cmsg_type);
+ ATF_REQUIRE_INTEQ(CMSG_LEN(sizeof(*tgr)), cmsg->cmsg_len);
+
+ tgr = (struct tls_get_record *)CMSG_DATA(cmsg);
+ ATF_REQUIRE_INTEQ(record_type, tgr->tls_type);
+ ATF_REQUIRE_INTEQ(en->tls_vmajor, tgr->tls_vmajor);
+ /* XXX: Not sure if this is what OpenSSL expects? */
+ if (en->tls_vminor == TLS_MINOR_VER_THREE)
+ ATF_REQUIRE_INTEQ(TLS_MINOR_VER_TWO, tgr->tls_vminor);
+ else
+ ATF_REQUIRE_INTEQ(en->tls_vminor, tgr->tls_vminor);
+ ATF_REQUIRE_INTEQ(htons(rv), tgr->tls_length);
+
+ return (rv);
+}
+
+static void
+test_ktls_receive_app_data(const atf_tc_t *tc, struct tls_enable *en,
+ uint64_t seqno, size_t len, size_t padding)
+{
+ struct kevent ev;
+ char *plaintext, *received, *outbuf;
+ size_t outbuf_cap, outbuf_len, outbuf_sent, received_len, todo, written;
+ ssize_t rv;
+ int kq, sockets[2];
+
+ plaintext = alloc_buffer(len);
+ received = malloc(len);
+ outbuf_cap = tls_header_len(en) + TLS_MAX_MSG_SIZE_V10_2 +
+ tls_trailer_len(en);
+ outbuf = malloc(outbuf_cap);
+
+ ATF_REQUIRE((kq = kqueue()) != -1);
+
+ ATF_REQUIRE_MSG(open_sockets(tc, sockets), "failed to create sockets");
+
+ ATF_REQUIRE(setsockopt(sockets[0], IPPROTO_TCP, TCP_RXTLS_ENABLE, en,
+ sizeof(*en)) == 0);
+ check_tls_mode(tc, sockets[0], TCP_RXTLS_MODE);
+
+ EV_SET(&ev, sockets[0], EVFILT_READ, EV_ADD, 0, 0, NULL);
+ ATF_REQUIRE(kevent(kq, &ev, 1, NULL, 0, NULL) == 0);
+ EV_SET(&ev, sockets[1], EVFILT_WRITE, EV_ADD, 0, 0, NULL);
+ ATF_REQUIRE(kevent(kq, &ev, 1, NULL, 0, NULL) == 0);
+
+ received_len = 0;
+ outbuf_len = 0;
+ written = 0;
+
+ while (received_len != len) {
+ ATF_REQUIRE(kevent(kq, NULL, 0, &ev, 1, NULL) == 1);
+
+ switch (ev.filter) {
+ case EVFILT_WRITE:
+ /*
+ * Compose the next TLS record to send.
+ */
+ if (outbuf_len == 0) {
+ ATF_REQUIRE(written < len);
+ todo = len - written;
+ if (todo > TLS_MAX_MSG_SIZE_V10_2 - padding)
+ todo = TLS_MAX_MSG_SIZE_V10_2 - padding;
+ outbuf_len = encrypt_tls_record(tc, en,
+ TLS_RLTYPE_APP, seqno, plaintext + written,
+ todo, outbuf, outbuf_cap, padding);
+ outbuf_sent = 0;
+ written += todo;
+ seqno++;
+ }
+
+ /*
+ * Try to write the remainder of the current
+ * TLS record.
+ */
+ rv = write(ev.ident, outbuf + outbuf_sent,
+ outbuf_len - outbuf_sent);
+ ATF_REQUIRE_MSG(rv > 0,
+ "failed to write to socket: %s", strerror(errno));
+ outbuf_sent += rv;
+ if (outbuf_sent == outbuf_len) {
+ outbuf_len = 0;
+ if (written == len) {
+ ev.flags = EV_DISABLE;
+ ATF_REQUIRE(kevent(kq, &ev, 1, NULL, 0,
+ NULL) == 0);
+ }
+ }
+ break;
+
+ case EVFILT_READ:
+ ATF_REQUIRE((ev.flags & EV_EOF) == 0);
+
+ rv = ktls_receive_tls_record(en, ev.ident,
+ TLS_RLTYPE_APP, received + received_len,
+ len - received_len);
+ received_len += rv;
+ break;
+ }
+ }
+
+ ATF_REQUIRE_MSG(written == received_len,
+ "read %zu decrypted bytes, but wrote %zu", received_len, written);
+
+ ATF_REQUIRE(memcmp(plaintext, received, len) == 0);
+
+ free(outbuf);
+ free(received);
+ free(plaintext);
+
+ close_sockets(sockets);
+ ATF_REQUIRE(close(kq) == 0);
+}
+
+static void
+ktls_receive_tls_error(int fd, int expected_error)
+{
+ struct msghdr msg;
+ struct tls_get_record *tgr;
+ char cbuf[CMSG_SPACE(sizeof(*tgr))];
+ char buf[64];
+ struct iovec iov;
+
+ memset(&msg, 0, sizeof(msg));
+
+ msg.msg_control = cbuf;
+ msg.msg_controllen = sizeof(cbuf);
+
+ iov.iov_base = buf;
+ iov.iov_len = sizeof(buf);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ ATF_REQUIRE(recvmsg(fd, &msg, 0) == -1);
+ if (expected_error != 0)
+ ATF_REQUIRE_ERRNO(expected_error, true);
+}
+
+static void
+test_ktls_receive_corrupted_record(const atf_tc_t *tc, struct tls_enable *en,
+ uint64_t seqno, size_t len, ssize_t offset)
+{
+ char *plaintext, *outbuf;
+ size_t outbuf_cap, outbuf_len;
+ ssize_t rv;
+ int sockets[2];
+
+ ATF_REQUIRE(len <= TLS_MAX_MSG_SIZE_V10_2);
+
+ plaintext = alloc_buffer(len);
+ outbuf_cap = tls_header_len(en) + len + tls_trailer_len(en);
+ outbuf = malloc(outbuf_cap);
+
+ ATF_REQUIRE_MSG(open_sockets(tc, sockets), "failed to create sockets");
+
+ ATF_REQUIRE(setsockopt(sockets[0], IPPROTO_TCP, TCP_RXTLS_ENABLE, en,
+ sizeof(*en)) == 0);
+ check_tls_mode(tc, sockets[0], TCP_RXTLS_MODE);
+
+ fd_set_blocking(sockets[0]);
+ fd_set_blocking(sockets[1]);
+
+ outbuf_len = encrypt_tls_record(tc, en, TLS_RLTYPE_APP, seqno,
+ plaintext, len, outbuf, outbuf_cap, 0);
+
+ /* A negative offset is an offset from the end. */
+ if (offset < 0)
+ offset += outbuf_len;
+ outbuf[offset] ^= 0x01;
+
+ rv = write(sockets[1], outbuf, outbuf_len);
+ ATF_REQUIRE_INTEQ((ssize_t)outbuf_len, rv);
+
+ ktls_receive_tls_error(sockets[0], EBADMSG);
+
+ free(outbuf);
+ free(plaintext);
+
+ close_sockets_ignore_errors(sockets);
+}
+
+static void
+test_ktls_receive_corrupted_iv(const atf_tc_t *tc, struct tls_enable *en,
+ uint64_t seqno, size_t len)
+{
+ ATF_REQUIRE(tls_header_len(en) > sizeof(struct tls_record_layer));
+
+ /* Corrupt the first byte of the explicit IV after the header. */
+ test_ktls_receive_corrupted_record(tc, en, seqno, len,
+ sizeof(struct tls_record_layer));
+}
+
+static void
+test_ktls_receive_corrupted_data(const atf_tc_t *tc, struct tls_enable *en,
+ uint64_t seqno, size_t len)
+{
+ ATF_REQUIRE(len > 0);
+
+ /* Corrupt the first ciphertext byte after the header. */
+ test_ktls_receive_corrupted_record(tc, en, seqno, len,
+ tls_header_len(en));
+}
+
+static void
+test_ktls_receive_corrupted_mac(const atf_tc_t *tc, struct tls_enable *en,
+ uint64_t seqno, size_t len)
+{
+ size_t offset;
+
+ /* Corrupt the first byte of the MAC. */
+ if (en->cipher_algorithm == CRYPTO_AES_CBC)
+ offset = tls_header_len(en) + len;
+ else
+ offset = -tls_mac_len(en);
+ test_ktls_receive_corrupted_record(tc, en, seqno, len, offset);
+}
+
+static void
+test_ktls_receive_corrupted_padding(const atf_tc_t *tc, struct tls_enable *en,
+ uint64_t seqno, size_t len)
+{
+ ATF_REQUIRE_INTEQ(CRYPTO_AES_CBC, en->cipher_algorithm);
+
+ /* Corrupt the last byte of the padding. */
+ test_ktls_receive_corrupted_record(tc, en, seqno, len, -1);
+}
+
+static void
+test_ktls_receive_truncated_record(const atf_tc_t *tc, struct tls_enable *en,
+ uint64_t seqno, size_t len)
+{
+ char *plaintext, *outbuf;
+ size_t outbuf_cap, outbuf_len;
+ ssize_t rv;
+ int sockets[2];
+
+ ATF_REQUIRE(len <= TLS_MAX_MSG_SIZE_V10_2);
+
+ plaintext = alloc_buffer(len);
+ outbuf_cap = tls_header_len(en) + len + tls_trailer_len(en);
+ outbuf = malloc(outbuf_cap);
+
+ ATF_REQUIRE_MSG(open_sockets(tc, sockets), "failed to create sockets");
+
+ ATF_REQUIRE(setsockopt(sockets[0], IPPROTO_TCP, TCP_RXTLS_ENABLE, en,
+ sizeof(*en)) == 0);
+ check_tls_mode(tc, sockets[0], TCP_RXTLS_MODE);
+
+ fd_set_blocking(sockets[0]);
+ fd_set_blocking(sockets[1]);
+
+ outbuf_len = encrypt_tls_record(tc, en, TLS_RLTYPE_APP, seqno,
+ plaintext, len, outbuf, outbuf_cap, 0);
+
+ rv = write(sockets[1], outbuf, outbuf_len / 2);
+ ATF_REQUIRE_INTEQ((ssize_t)(outbuf_len / 2), rv);
+
+ ATF_REQUIRE(shutdown(sockets[1], SHUT_WR) == 0);
+
+ ktls_receive_tls_error(sockets[0], EMSGSIZE);
+
+ free(outbuf);
+ free(plaintext);
+
+ close_sockets_ignore_errors(sockets);
+}
+
+static void
+test_ktls_receive_bad_major(const atf_tc_t *tc, struct tls_enable *en,
+ uint64_t seqno, size_t len)
+{
+ struct tls_record_layer *hdr;
+ char *plaintext, *outbuf;
+ size_t outbuf_cap, outbuf_len;
+ ssize_t rv;
+ int sockets[2];
+
+ ATF_REQUIRE(len <= TLS_MAX_MSG_SIZE_V10_2);
+
+ plaintext = alloc_buffer(len);
+ outbuf_cap = tls_header_len(en) + len + tls_trailer_len(en);
+ outbuf = malloc(outbuf_cap);
+
+ ATF_REQUIRE_MSG(open_sockets(tc, sockets), "failed to create sockets");
+
+ ATF_REQUIRE(setsockopt(sockets[0], IPPROTO_TCP, TCP_RXTLS_ENABLE, en,
+ sizeof(*en)) == 0);
+ check_tls_mode(tc, sockets[0], TCP_RXTLS_MODE);
+
+ fd_set_blocking(sockets[0]);
+ fd_set_blocking(sockets[1]);
+
+ outbuf_len = encrypt_tls_record(tc, en, TLS_RLTYPE_APP, seqno,
+ plaintext, len, outbuf, outbuf_cap, 0);
+
+ hdr = (void *)outbuf;
+ hdr->tls_vmajor++;
+
+ rv = write(sockets[1], outbuf, outbuf_len);
+ ATF_REQUIRE_INTEQ((ssize_t)outbuf_len, rv);
+
+ ktls_receive_tls_error(sockets[0], EINVAL);
+
+ free(outbuf);
+ free(plaintext);
+
+ close_sockets_ignore_errors(sockets);
+}
+
+static void
+test_ktls_receive_bad_minor(const atf_tc_t *tc, struct tls_enable *en,
+ uint64_t seqno, size_t len)
+{
+ struct tls_record_layer *hdr;
+ char *plaintext, *outbuf;
+ size_t outbuf_cap, outbuf_len;
+ ssize_t rv;
+ int sockets[2];
+
+ ATF_REQUIRE(len <= TLS_MAX_MSG_SIZE_V10_2);
+
+ plaintext = alloc_buffer(len);
+ outbuf_cap = tls_header_len(en) + len + tls_trailer_len(en);
+ outbuf = malloc(outbuf_cap);
+
+ ATF_REQUIRE_MSG(open_sockets(tc, sockets), "failed to create sockets");
+
+ ATF_REQUIRE(setsockopt(sockets[0], IPPROTO_TCP, TCP_RXTLS_ENABLE, en,
+ sizeof(*en)) == 0);
+ check_tls_mode(tc, sockets[0], TCP_RXTLS_MODE);
+
+ fd_set_blocking(sockets[0]);
+ fd_set_blocking(sockets[1]);
+
+ outbuf_len = encrypt_tls_record(tc, en, TLS_RLTYPE_APP, seqno,
+ plaintext, len, outbuf, outbuf_cap, 0);
+
+ hdr = (void *)outbuf;
+ hdr->tls_vminor++;
+
+ rv = write(sockets[1], outbuf, outbuf_len);
+ ATF_REQUIRE_INTEQ((ssize_t)outbuf_len, rv);
+
+ ktls_receive_tls_error(sockets[0], EINVAL);
+
+ free(outbuf);
+ free(plaintext);
+
+ close_sockets_ignore_errors(sockets);
+}
+
+static void
+test_ktls_receive_bad_type(const atf_tc_t *tc, struct tls_enable *en,
+ uint64_t seqno, size_t len)
+{
+ struct tls_record_layer *hdr;
+ char *plaintext, *outbuf;
+ size_t outbuf_cap, outbuf_len;
+ ssize_t rv;
+ int sockets[2];
+
+ ATF_REQUIRE(len <= TLS_MAX_MSG_SIZE_V10_2);
+ ATF_REQUIRE_INTEQ(TLS_MINOR_VER_THREE, en->tls_vminor);
+
+ plaintext = alloc_buffer(len);
+ outbuf_cap = tls_header_len(en) + len + tls_trailer_len(en);
+ outbuf = malloc(outbuf_cap);
+
+ ATF_REQUIRE_MSG(open_sockets(tc, sockets), "failed to create sockets");
+
+ ATF_REQUIRE(setsockopt(sockets[0], IPPROTO_TCP, TCP_RXTLS_ENABLE, en,
+ sizeof(*en)) == 0);
+ check_tls_mode(tc, sockets[0], TCP_RXTLS_MODE);
+
+ fd_set_blocking(sockets[0]);
+ fd_set_blocking(sockets[1]);
+
+ outbuf_len = encrypt_tls_record(tc, en, 0x21 /* Alert */, seqno,
+ plaintext, len, outbuf, outbuf_cap, 0);
+
+ hdr = (void *)outbuf;
+ hdr->tls_type = TLS_RLTYPE_APP + 1;
+
+ rv = write(sockets[1], outbuf, outbuf_len);
+ ATF_REQUIRE_INTEQ((ssize_t)outbuf_len, rv);
+
+ ktls_receive_tls_error(sockets[0], EINVAL);
+
+ free(outbuf);
+ free(plaintext);
+
+ close_sockets_ignore_errors(sockets);
+}
+
+static void
+test_ktls_receive_bad_size(const atf_tc_t *tc, struct tls_enable *en,
+ uint64_t seqno, size_t len)
+{
+ struct tls_record_layer *hdr;
+ char *outbuf;
+ size_t outbuf_len;
+ ssize_t rv;
+ int sockets[2];
+
+ outbuf_len = sizeof(*hdr) + len;
+ outbuf = calloc(1, outbuf_len);
+
+ ATF_REQUIRE_MSG(open_sockets(tc, sockets), "failed to create sockets");
+
+ ATF_REQUIRE(setsockopt(sockets[0], IPPROTO_TCP, TCP_RXTLS_ENABLE, en,
+ sizeof(*en)) == 0);
+ check_tls_mode(tc, sockets[0], TCP_RXTLS_MODE);
+
+ fd_set_blocking(sockets[0]);
+ fd_set_blocking(sockets[1]);
+
+ hdr = (void *)outbuf;
+ hdr->tls_vmajor = en->tls_vmajor;
+ if (en->tls_vminor == TLS_MINOR_VER_THREE)
+ hdr->tls_vminor = TLS_MINOR_VER_TWO;
+ else
+ hdr->tls_vminor = en->tls_vminor;
+ hdr->tls_type = TLS_RLTYPE_APP;
+ hdr->tls_length = htons(len);
+
+ rv = write(sockets[1], outbuf, outbuf_len);
+ ATF_REQUIRE_INTEQ((ssize_t)outbuf_len, rv);
+
+ /*
+ * The other end may notice the error and drop the connection
+ * before this executes resulting in shutdown() failing with
+ * either ENOTCONN or ECONNRESET. Ignore this error if it
+ * occurs.
+ */
+ if (shutdown(sockets[1], SHUT_WR) != 0) {
+ ATF_REQUIRE_MSG(errno == ENOTCONN || errno == ECONNRESET,
+ "shutdown() failed: %s", strerror(errno));
+ }
+
+ ktls_receive_tls_error(sockets[0], EMSGSIZE);
+
+ free(outbuf);
+
+ close_sockets_ignore_errors(sockets);
+}
+
+#define TLS_10_TESTS(M) \
+ M(aes128_cbc_1_0_sha1, CRYPTO_AES_CBC, 128 / 8, \
+ CRYPTO_SHA1_HMAC, TLS_MINOR_VER_ZERO) \
+ M(aes256_cbc_1_0_sha1, CRYPTO_AES_CBC, 256 / 8, \
+ CRYPTO_SHA1_HMAC, TLS_MINOR_VER_ZERO)
+
+#define TLS_13_TESTS(M) \
+ M(aes128_gcm_1_3, CRYPTO_AES_NIST_GCM_16, 128 / 8, 0, \
+ TLS_MINOR_VER_THREE) \
+ M(aes256_gcm_1_3, CRYPTO_AES_NIST_GCM_16, 256 / 8, 0, \
+ TLS_MINOR_VER_THREE) \
+ M(chacha20_poly1305_1_3, CRYPTO_CHACHA20_POLY1305, 256 / 8, 0, \
+ TLS_MINOR_VER_THREE)
+
+#define AES_CBC_NONZERO_TESTS(M) \
+ M(aes128_cbc_1_1_sha1, CRYPTO_AES_CBC, 128 / 8, \
+ CRYPTO_SHA1_HMAC, TLS_MINOR_VER_ONE) \
+ M(aes256_cbc_1_1_sha1, CRYPTO_AES_CBC, 256 / 8, \
+ CRYPTO_SHA1_HMAC, TLS_MINOR_VER_ONE) \
+ M(aes128_cbc_1_2_sha1, CRYPTO_AES_CBC, 128 / 8, \
+ CRYPTO_SHA1_HMAC, TLS_MINOR_VER_TWO) \
+ M(aes256_cbc_1_2_sha1, CRYPTO_AES_CBC, 256 / 8, \
+ CRYPTO_SHA1_HMAC, TLS_MINOR_VER_TWO) \
+ M(aes128_cbc_1_2_sha256, CRYPTO_AES_CBC, 128 / 8, \
+ CRYPTO_SHA2_256_HMAC, TLS_MINOR_VER_TWO) \
+ M(aes256_cbc_1_2_sha256, CRYPTO_AES_CBC, 256 / 8, \
+ CRYPTO_SHA2_256_HMAC, TLS_MINOR_VER_TWO) \
+ M(aes128_cbc_1_2_sha384, CRYPTO_AES_CBC, 128 / 8, \
+ CRYPTO_SHA2_384_HMAC, TLS_MINOR_VER_TWO) \
+ M(aes256_cbc_1_2_sha384, CRYPTO_AES_CBC, 256 / 8, \
+ CRYPTO_SHA2_384_HMAC, TLS_MINOR_VER_TWO) \
+
+#define AES_CBC_TESTS(M) \
+ TLS_10_TESTS(M) \
+ AES_CBC_NONZERO_TESTS(M)
+
+#define AES_GCM_12_TESTS(M) \
+ M(aes128_gcm_1_2, CRYPTO_AES_NIST_GCM_16, 128 / 8, 0, \
+ TLS_MINOR_VER_TWO) \
+ M(aes256_gcm_1_2, CRYPTO_AES_NIST_GCM_16, 256 / 8, 0, \
+ TLS_MINOR_VER_TWO)
+
+#define AES_GCM_TESTS(M) \
+ AES_GCM_12_TESTS(M) \
+ M(aes128_gcm_1_3, CRYPTO_AES_NIST_GCM_16, 128 / 8, 0, \
+ TLS_MINOR_VER_THREE) \
+ M(aes256_gcm_1_3, CRYPTO_AES_NIST_GCM_16, 256 / 8, 0, \
+ TLS_MINOR_VER_THREE)
+
+#define CHACHA20_TESTS(M) \
+ M(chacha20_poly1305_1_2, CRYPTO_CHACHA20_POLY1305, 256 / 8, 0, \
+ TLS_MINOR_VER_TWO) \
+ M(chacha20_poly1305_1_3, CRYPTO_CHACHA20_POLY1305, 256 / 8, 0, \
+ TLS_MINOR_VER_THREE)
+
+#define GEN_TRANSMIT_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, name, len) \
+ATF_TC_WITHOUT_HEAD(ktls_transmit_##cipher_name##_##name); \
+ATF_TC_BODY(ktls_transmit_##cipher_name##_##name, tc) \
+{ \
+ struct tls_enable en; \
+ uint64_t seqno; \
+ \
+ ATF_REQUIRE_KTLS(); \
+ seqno = random(); \
+ build_tls_enable(tc, cipher_alg, key_size, auth_alg, minor, \
+ seqno, &en); \
+ test_ktls_transmit_app_data(tc, &en, seqno, len); \
+ free_tls_enable(&en); \
+}
+
+#define ADD_TRANSMIT_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, name) \
+ ATF_TP_ADD_TC(tp, ktls_transmit_##cipher_name##_##name);
+
+#define GEN_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, name, type, len) \
+ATF_TC_WITHOUT_HEAD(ktls_transmit_##cipher_name##_##name); \
+ATF_TC_BODY(ktls_transmit_##cipher_name##_##name, tc) \
+{ \
+ struct tls_enable en; \
+ uint64_t seqno; \
+ \
+ ATF_REQUIRE_KTLS(); \
+ seqno = random(); \
+ build_tls_enable(tc, cipher_alg, key_size, auth_alg, minor, \
+ seqno, &en); \
+ test_ktls_transmit_control(tc, &en, seqno, type, len); \
+ free_tls_enable(&en); \
+}
+
+#define ADD_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, name) \
+ ATF_TP_ADD_TC(tp, ktls_transmit_##cipher_name##_##name);
+
+#define GEN_TRANSMIT_EMPTY_FRAGMENT_TEST(cipher_name, cipher_alg, \
+ key_size, auth_alg, minor) \
+ATF_TC_WITHOUT_HEAD(ktls_transmit_##cipher_name##_empty_fragment); \
+ATF_TC_BODY(ktls_transmit_##cipher_name##_empty_fragment, tc) \
+{ \
+ struct tls_enable en; \
+ uint64_t seqno; \
+ \
+ ATF_REQUIRE_KTLS(); \
+ seqno = random(); \
+ build_tls_enable(tc, cipher_alg, key_size, auth_alg, minor, \
+ seqno, &en); \
+ test_ktls_transmit_empty_fragment(tc, &en, seqno); \
+ free_tls_enable(&en); \
+}
+
+#define ADD_TRANSMIT_EMPTY_FRAGMENT_TEST(cipher_name, cipher_alg, \
+ key_size, auth_alg, minor) \
+ ATF_TP_ADD_TC(tp, ktls_transmit_##cipher_name##_empty_fragment);
+
+#define GEN_TRANSMIT_TESTS(cipher_name, cipher_alg, key_size, auth_alg, \
+ minor) \
+ GEN_TRANSMIT_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, short, 64) \
+ GEN_TRANSMIT_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, long, 64 * 1024) \
+ GEN_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, control, 0x21 /* Alert */, 32)
+
+#define ADD_TRANSMIT_TESTS(cipher_name, cipher_alg, key_size, auth_alg, \
+ minor) \
+ ADD_TRANSMIT_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, short) \
+ ADD_TRANSMIT_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, long) \
+ ADD_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, control)
+
+/*
+ * For each supported cipher suite, run three transmit tests:
+ *
+ * - a short test which sends 64 bytes of application data (likely as
+ * a single TLS record)
+ *
+ * - a long test which sends 64KB of application data (split across
+ * multiple TLS records)
+ *
+ * - a control test which sends a single record with a specific
+ * content type via sendmsg()
+ */
+AES_CBC_TESTS(GEN_TRANSMIT_TESTS);
+AES_GCM_TESTS(GEN_TRANSMIT_TESTS);
+CHACHA20_TESTS(GEN_TRANSMIT_TESTS);
+
+#define GEN_TRANSMIT_PADDING_TESTS(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor) \
+ GEN_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_1, 0x21 /* Alert */, 1) \
+ GEN_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_2, 0x21 /* Alert */, 2) \
+ GEN_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_3, 0x21 /* Alert */, 3) \
+ GEN_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_4, 0x21 /* Alert */, 4) \
+ GEN_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_5, 0x21 /* Alert */, 5) \
+ GEN_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_6, 0x21 /* Alert */, 6) \
+ GEN_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_7, 0x21 /* Alert */, 7) \
+ GEN_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_8, 0x21 /* Alert */, 8) \
+ GEN_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_9, 0x21 /* Alert */, 9) \
+ GEN_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_10, 0x21 /* Alert */, 10) \
+ GEN_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_11, 0x21 /* Alert */, 11) \
+ GEN_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_12, 0x21 /* Alert */, 12) \
+ GEN_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_13, 0x21 /* Alert */, 13) \
+ GEN_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_14, 0x21 /* Alert */, 14) \
+ GEN_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_15, 0x21 /* Alert */, 15) \
+ GEN_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_16, 0x21 /* Alert */, 16)
+
+#define ADD_TRANSMIT_PADDING_TESTS(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor) \
+ ADD_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_1) \
+ ADD_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_2) \
+ ADD_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_3) \
+ ADD_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_4) \
+ ADD_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_5) \
+ ADD_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_6) \
+ ADD_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_7) \
+ ADD_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_8) \
+ ADD_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_9) \
+ ADD_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_10) \
+ ADD_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_11) \
+ ADD_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_12) \
+ ADD_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_13) \
+ ADD_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_14) \
+ ADD_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_15) \
+ ADD_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_16)
+
+/*
+ * For AES-CBC MTE cipher suites using padding, add tests of messages
+ * with each possible padding size. Note that the padding_<N> tests
+ * do not necessarily test <N> bytes of padding as the padding is a
+ * function of the cipher suite's MAC length. However, cycling
+ * through all of the payload sizes from 1 to 16 should exercise all
+ * of the possible padding lengths for each suite.
+ */
+AES_CBC_TESTS(GEN_TRANSMIT_PADDING_TESTS);
+
+/*
+ * Test "empty fragments" which are TLS records with no payload that
+ * OpenSSL can send for TLS 1.0 connections.
+ */
+AES_CBC_TESTS(GEN_TRANSMIT_EMPTY_FRAGMENT_TEST);
+AES_GCM_TESTS(GEN_TRANSMIT_EMPTY_FRAGMENT_TEST);
+CHACHA20_TESTS(GEN_TRANSMIT_EMPTY_FRAGMENT_TEST);
+
+static void
+test_ktls_invalid_transmit_cipher_suite(const atf_tc_t *tc,
+ struct tls_enable *en)
+{
+ int sockets[2];
+
+ ATF_REQUIRE_MSG(open_sockets(tc, sockets), "failed to create sockets");
+
+ ATF_REQUIRE_ERRNO(EINVAL, setsockopt(sockets[1], IPPROTO_TCP,
+ TCP_TXTLS_ENABLE, en, sizeof(*en)) == -1);
+
+ close_sockets(sockets);
+}
+
+#define GEN_INVALID_TRANSMIT_TEST(name, cipher_alg, key_size, auth_alg, \
+ minor) \
+ATF_TC_WITHOUT_HEAD(ktls_transmit_invalid_##name); \
+ATF_TC_BODY(ktls_transmit_invalid_##name, tc) \
+{ \
+ struct tls_enable en; \
+ uint64_t seqno; \
+ \
+ ATF_REQUIRE_KTLS(); \
+ seqno = random(); \
+ build_tls_enable(tc, cipher_alg, key_size, auth_alg, minor, \
+ seqno, &en); \
+ test_ktls_invalid_transmit_cipher_suite(tc, &en); \
+ free_tls_enable(&en); \
+}
+
+#define ADD_INVALID_TRANSMIT_TEST(name, cipher_alg, key_size, auth_alg, \
+ minor) \
+ ATF_TP_ADD_TC(tp, ktls_transmit_invalid_##name);
+
+#define INVALID_CIPHER_SUITES(M) \
+ M(aes128_cbc_1_0_sha256, CRYPTO_AES_CBC, 128 / 8, \
+ CRYPTO_SHA2_256_HMAC, TLS_MINOR_VER_ZERO) \
+ M(aes128_cbc_1_0_sha384, CRYPTO_AES_CBC, 128 / 8, \
+ CRYPTO_SHA2_384_HMAC, TLS_MINOR_VER_ZERO) \
+ M(aes128_gcm_1_0, CRYPTO_AES_NIST_GCM_16, 128 / 8, 0, \
+ TLS_MINOR_VER_ZERO) \
+ M(chacha20_poly1305_1_0, CRYPTO_CHACHA20_POLY1305, 256 / 8, 0, \
+ TLS_MINOR_VER_ZERO) \
+ M(aes128_cbc_1_1_sha256, CRYPTO_AES_CBC, 128 / 8, \
+ CRYPTO_SHA2_256_HMAC, TLS_MINOR_VER_ONE) \
+ M(aes128_cbc_1_1_sha384, CRYPTO_AES_CBC, 128 / 8, \
+ CRYPTO_SHA2_384_HMAC, TLS_MINOR_VER_ONE) \
+ M(aes128_gcm_1_1, CRYPTO_AES_NIST_GCM_16, 128 / 8, 0, \
+ TLS_MINOR_VER_ONE) \
+ M(chacha20_poly1305_1_1, CRYPTO_CHACHA20_POLY1305, 256 / 8, 0, \
+ TLS_MINOR_VER_ONE) \
+ M(aes128_cbc_1_3_sha1, CRYPTO_AES_CBC, 128 / 8, \
+ CRYPTO_SHA1_HMAC, TLS_MINOR_VER_THREE) \
+ M(aes128_cbc_1_3_sha256, CRYPTO_AES_CBC, 128 / 8, \
+ CRYPTO_SHA2_256_HMAC, TLS_MINOR_VER_THREE) \
+ M(aes128_cbc_1_3_sha384, CRYPTO_AES_CBC, 128 / 8, \
+ CRYPTO_SHA2_384_HMAC, TLS_MINOR_VER_THREE)
+
+/*
+ * Ensure that invalid cipher suites are rejected for transmit.
+ */
+INVALID_CIPHER_SUITES(GEN_INVALID_TRANSMIT_TEST);
+
+#define GEN_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, name, len, padding) \
+ATF_TC_WITHOUT_HEAD(ktls_receive_##cipher_name##_##name); \
+ATF_TC_BODY(ktls_receive_##cipher_name##_##name, tc) \
+{ \
+ struct tls_enable en; \
+ uint64_t seqno; \
+ \
+ ATF_REQUIRE_KTLS(); \
+ seqno = random(); \
+ build_tls_enable(tc, cipher_alg, key_size, auth_alg, minor, \
+ seqno, &en); \
+ test_ktls_receive_app_data(tc, &en, seqno, len, padding); \
+ free_tls_enable(&en); \
+}
+
+#define ADD_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, name) \
+ ATF_TP_ADD_TC(tp, ktls_receive_##cipher_name##_##name);
+
+#define GEN_RECEIVE_BAD_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, len) \
+ATF_TC_WITHOUT_HEAD(ktls_receive_##cipher_name##_bad_data); \
+ATF_TC_BODY(ktls_receive_##cipher_name##_bad_data, tc) \
+{ \
+ struct tls_enable en; \
+ uint64_t seqno; \
+ \
+ ATF_REQUIRE_KTLS(); \
+ seqno = random(); \
+ build_tls_enable(tc, cipher_alg, key_size, auth_alg, minor, \
+ seqno, &en); \
+ test_ktls_receive_corrupted_data(tc, &en, seqno, len); \
+ free_tls_enable(&en); \
+}
+
+#define ADD_RECEIVE_BAD_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor) \
+ ATF_TP_ADD_TC(tp, ktls_receive_##cipher_name##_bad_data);
+
+#define GEN_RECEIVE_BAD_MAC_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, len) \
+ATF_TC_WITHOUT_HEAD(ktls_receive_##cipher_name##_bad_mac); \
+ATF_TC_BODY(ktls_receive_##cipher_name##_bad_mac, tc) \
+{ \
+ struct tls_enable en; \
+ uint64_t seqno; \
+ \
+ ATF_REQUIRE_KTLS(); \
+ seqno = random(); \
+ build_tls_enable(tc, cipher_alg, key_size, auth_alg, minor, \
+ seqno, &en); \
+ test_ktls_receive_corrupted_mac(tc, &en, seqno, len); \
+ free_tls_enable(&en); \
+}
+
+#define ADD_RECEIVE_BAD_MAC_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor) \
+ ATF_TP_ADD_TC(tp, ktls_receive_##cipher_name##_bad_mac);
+
+#define GEN_RECEIVE_TRUNCATED_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, len) \
+ATF_TC_WITHOUT_HEAD(ktls_receive_##cipher_name##_truncated_record); \
+ATF_TC_BODY(ktls_receive_##cipher_name##_truncated_record, tc) \
+{ \
+ struct tls_enable en; \
+ uint64_t seqno; \
+ \
+ ATF_REQUIRE_KTLS(); \
+ seqno = random(); \
+ build_tls_enable(tc, cipher_alg, key_size, auth_alg, minor, \
+ seqno, &en); \
+ test_ktls_receive_truncated_record(tc, &en, seqno, len); \
+ free_tls_enable(&en); \
+}
+
+#define ADD_RECEIVE_TRUNCATED_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor) \
+ ATF_TP_ADD_TC(tp, ktls_receive_##cipher_name##_truncated_record);
+
+#define GEN_RECEIVE_BAD_MAJOR_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, len) \
+ATF_TC_WITHOUT_HEAD(ktls_receive_##cipher_name##_bad_major); \
+ATF_TC_BODY(ktls_receive_##cipher_name##_bad_major, tc) \
+{ \
+ struct tls_enable en; \
+ uint64_t seqno; \
+ \
+ ATF_REQUIRE_KTLS(); \
+ seqno = random(); \
+ build_tls_enable(tc, cipher_alg, key_size, auth_alg, minor, \
+ seqno, &en); \
+ test_ktls_receive_bad_major(tc, &en, seqno, len); \
+ free_tls_enable(&en); \
+}
+
+#define ADD_RECEIVE_BAD_MAJOR_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor) \
+ ATF_TP_ADD_TC(tp, ktls_receive_##cipher_name##_bad_major);
+
+#define GEN_RECEIVE_BAD_MINOR_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, len) \
+ATF_TC_WITHOUT_HEAD(ktls_receive_##cipher_name##_bad_minor); \
+ATF_TC_BODY(ktls_receive_##cipher_name##_bad_minor, tc) \
+{ \
+ struct tls_enable en; \
+ uint64_t seqno; \
+ \
+ ATF_REQUIRE_KTLS(); \
+ seqno = random(); \
+ build_tls_enable(tc, cipher_alg, key_size, auth_alg, minor, \
+ seqno, &en); \
+ test_ktls_receive_bad_minor(tc, &en, seqno, len); \
+ free_tls_enable(&en); \
+}
+
+#define ADD_RECEIVE_BAD_MINOR_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor) \
+ ATF_TP_ADD_TC(tp, ktls_receive_##cipher_name##_bad_minor);
+
+#define GEN_RECEIVE_BAD_SIZE_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, name, len) \
+ATF_TC_WITHOUT_HEAD(ktls_receive_##cipher_name##_##name); \
+ATF_TC_BODY(ktls_receive_##cipher_name##_##name, tc) \
+{ \
+ struct tls_enable en; \
+ uint64_t seqno; \
+ \
+ ATF_REQUIRE_KTLS(); \
+ seqno = random(); \
+ build_tls_enable(tc, cipher_alg, key_size, auth_alg, minor, \
+ seqno, &en); \
+ test_ktls_receive_bad_size(tc, &en, seqno, (len)); \
+ free_tls_enable(&en); \
+}
+
+#define ADD_RECEIVE_BAD_SIZE_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, name) \
+ ATF_TP_ADD_TC(tp, ktls_receive_##cipher_name##_##name);
+
+#define GEN_RECEIVE_TESTS(cipher_name, cipher_alg, key_size, auth_alg, \
+ minor) \
+ GEN_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, short, 64, 0) \
+ GEN_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, long, 64 * 1024, 0) \
+ GEN_RECEIVE_BAD_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, 64) \
+ GEN_RECEIVE_BAD_MAC_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, 64) \
+ GEN_RECEIVE_TRUNCATED_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, 64) \
+ GEN_RECEIVE_BAD_MAJOR_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, 64) \
+ GEN_RECEIVE_BAD_MINOR_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, 64) \
+ GEN_RECEIVE_BAD_SIZE_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, small_record, \
+ tls_minimum_record_payload(&en) - 1) \
+ GEN_RECEIVE_BAD_SIZE_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, oversized_record, \
+ TLS_MAX_MSG_SIZE_V10_2 * 2)
+
+#define ADD_RECEIVE_TESTS(cipher_name, cipher_alg, key_size, auth_alg, \
+ minor) \
+ ADD_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, short) \
+ ADD_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, long) \
+ ADD_RECEIVE_BAD_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor) \
+ ADD_RECEIVE_BAD_MAC_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor) \
+ ADD_RECEIVE_TRUNCATED_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor) \
+ ADD_RECEIVE_BAD_MAJOR_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor) \
+ ADD_RECEIVE_BAD_MINOR_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor) \
+ ADD_RECEIVE_BAD_SIZE_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, small_record) \
+ ADD_RECEIVE_BAD_SIZE_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, oversized_record)
+
+/*
+ * For each supported cipher suite, run several receive tests:
+ *
+ * - a short test which sends 64 bytes of application data (likely as
+ * a single TLS record)
+ *
+ * - a long test which sends 64KB of application data (split across
+ * multiple TLS records)
+ *
+ * - a test with corrupted payload data in a single TLS record
+ *
+ * - a test with a corrupted MAC in a single TLS record
+ *
+ * - a test with a truncated TLS record
+ *
+ * - tests with invalid TLS major and minor versions
+ *
+ * - a tests with a record whose is one less than the smallest valid
+ * size
+ *
+ * - a test with an oversized TLS record
+ */
+AES_CBC_NONZERO_TESTS(GEN_RECEIVE_TESTS);
+AES_GCM_TESTS(GEN_RECEIVE_TESTS);
+CHACHA20_TESTS(GEN_RECEIVE_TESTS);
+
+#define GEN_RECEIVE_MTE_PADDING_TESTS(cipher_name, cipher_alg, \
+ key_size, auth_alg, minor) \
+ GEN_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_1, 1, 0) \
+ GEN_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_2, 2, 0) \
+ GEN_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_3, 3, 0) \
+ GEN_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_4, 4, 0) \
+ GEN_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_5, 5, 0) \
+ GEN_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_6, 6, 0) \
+ GEN_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_7, 7, 0) \
+ GEN_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_8, 8, 0) \
+ GEN_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_9, 9, 0) \
+ GEN_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_10, 10, 0) \
+ GEN_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_11, 11, 0) \
+ GEN_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_12, 12, 0) \
+ GEN_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_13, 13, 0) \
+ GEN_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_14, 14, 0) \
+ GEN_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_15, 15, 0) \
+ GEN_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_16, 16, 0) \
+ GEN_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_16_extra, 16, 16) \
+ GEN_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_32_extra, 16, 32)
+
+#define ADD_RECEIVE_MTE_PADDING_TESTS(cipher_name, cipher_alg, \
+ key_size, auth_alg, minor) \
+ ADD_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_1) \
+ ADD_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_2) \
+ ADD_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_3) \
+ ADD_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_4) \
+ ADD_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_5) \
+ ADD_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_6) \
+ ADD_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_7) \
+ ADD_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_8) \
+ ADD_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_9) \
+ ADD_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_10) \
+ ADD_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_11) \
+ ADD_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_12) \
+ ADD_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_13) \
+ ADD_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_14) \
+ ADD_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_15) \
+ ADD_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_16) \
+ ADD_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_16_extra) \
+ ADD_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, padding_32_extra)
+
+#define GEN_RECEIVE_BAD_PADDING_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, len) \
+ATF_TC_WITHOUT_HEAD(ktls_receive_##cipher_name##_bad_padding); \
+ATF_TC_BODY(ktls_receive_##cipher_name##_bad_padding, tc) \
+{ \
+ struct tls_enable en; \
+ uint64_t seqno; \
+ \
+ ATF_REQUIRE_KTLS(); \
+ seqno = random(); \
+ build_tls_enable(tc, cipher_alg, key_size, auth_alg, minor, \
+ seqno, &en); \
+ test_ktls_receive_corrupted_padding(tc, &en, seqno, len); \
+ free_tls_enable(&en); \
+}
+
+#define ADD_RECEIVE_BAD_PADDING_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor) \
+ ATF_TP_ADD_TC(tp, ktls_receive_##cipher_name##_bad_padding);
+
+#define GEN_RECEIVE_MTE_TESTS(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor) \
+ GEN_RECEIVE_MTE_PADDING_TESTS(cipher_name, cipher_alg, \
+ key_size, auth_alg, minor) \
+ GEN_RECEIVE_BAD_PADDING_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, 64) \
+ GEN_RECEIVE_BAD_SIZE_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, non_block_size, \
+ tls_minimum_record_payload(&en) + 1)
+
+#define ADD_RECEIVE_MTE_TESTS(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor) \
+ ADD_RECEIVE_MTE_PADDING_TESTS(cipher_name, cipher_alg, \
+ key_size, auth_alg, minor) \
+ ADD_RECEIVE_BAD_PADDING_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor) \
+ ADD_RECEIVE_BAD_SIZE_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, non_block_size)
+
+/*
+ * For AES-CBC MTE cipher suites using padding, add tests of messages
+ * with each possible padding size. Note that the padding_<N> tests
+ * do not necessarily test <N> bytes of padding as the padding is a
+ * function of the cipher suite's MAC length. However, cycling
+ * through all of the payload sizes from 1 to 16 should exercise all
+ * of the possible padding lengths for each suite.
+ *
+ * Two additional tests check for additional padding with an extra
+ * 16 or 32 bytes beyond the normal padding.
+ *
+ * Another test checks for corrupted padding.
+ *
+ * Another test checks for a record whose payload is not a multiple of
+ * the AES block size.
+ */
+AES_CBC_NONZERO_TESTS(GEN_RECEIVE_MTE_TESTS);
+
+#define GEN_RECEIVE_BAD_IV_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor) \
+ATF_TC_WITHOUT_HEAD(ktls_receive_##cipher_name##_bad_iv); \
+ATF_TC_BODY(ktls_receive_##cipher_name##_bad_iv, tc) \
+{ \
+ struct tls_enable en; \
+ uint64_t seqno; \
+ \
+ ATF_REQUIRE_KTLS(); \
+ seqno = random(); \
+ build_tls_enable(tc, cipher_alg, key_size, auth_alg, minor, \
+ seqno, &en); \
+ test_ktls_receive_corrupted_iv(tc, &en, seqno, 64); \
+ free_tls_enable(&en); \
+}
+
+#define ADD_RECEIVE_BAD_IV_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor) \
+ ATF_TP_ADD_TC(tp, ktls_receive_##cipher_name##_bad_iv);
+
+#define GEN_RECEIVE_EXPLICIT_IV_TESTS(cipher_name, cipher_alg, \
+ key_size, auth_alg, minor) \
+ GEN_RECEIVE_BAD_IV_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor) \
+ GEN_RECEIVE_BAD_SIZE_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, short_header, \
+ sizeof(struct tls_record_layer) + 1)
+
+#define ADD_RECEIVE_EXPLICIT_IV_TESTS(cipher_name, cipher_alg, \
+ key_size, auth_alg, minor) \
+ ADD_RECEIVE_BAD_IV_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor) \
+ ADD_RECEIVE_BAD_SIZE_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, short_header)
+
+/*
+ * For cipher suites with an explicit IV, run a receive test where the
+ * explicit IV has been corrupted. Also run a receive test that sends
+ * a short record without a complete IV.
+ */
+AES_CBC_NONZERO_TESTS(GEN_RECEIVE_EXPLICIT_IV_TESTS);
+AES_GCM_12_TESTS(GEN_RECEIVE_EXPLICIT_IV_TESTS);
+
+#define GEN_RECEIVE_BAD_TYPE_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, len) \
+ATF_TC_WITHOUT_HEAD(ktls_receive_##cipher_name##_bad_type); \
+ATF_TC_BODY(ktls_receive_##cipher_name##_bad_type, tc) \
+{ \
+ struct tls_enable en; \
+ uint64_t seqno; \
+ \
+ ATF_REQUIRE_KTLS(); \
+ seqno = random(); \
+ build_tls_enable(tc, cipher_alg, key_size, auth_alg, minor, \
+ seqno, &en); \
+ test_ktls_receive_bad_type(tc, &en, seqno, len); \
+ free_tls_enable(&en); \
+}
+
+#define ADD_RECEIVE_BAD_TYPE_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor) \
+ ATF_TP_ADD_TC(tp, ktls_receive_##cipher_name##_bad_type);
+
+#define GEN_RECEIVE_TLS13_TESTS(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor) \
+ GEN_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, short_padded, 64, 16) \
+ GEN_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, long_padded, 64 * 1024, 15) \
+ GEN_RECEIVE_BAD_TYPE_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, 64)
+
+#define ADD_RECEIVE_TLS13_TESTS(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor) \
+ ADD_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, short_padded) \
+ ADD_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, long_padded) \
+ ADD_RECEIVE_BAD_TYPE_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor)
+
+/*
+ * For TLS 1.3 cipher suites, run two additional receive tests which
+ * use add padding to each record. Also run a test that uses an
+ * invalid "outer" record type.
+ */
+TLS_13_TESTS(GEN_RECEIVE_TLS13_TESTS);
+
+static void
+test_ktls_invalid_receive_cipher_suite(const atf_tc_t *tc,
+ struct tls_enable *en)
+{
+ int sockets[2];
+
+ ATF_REQUIRE_MSG(open_sockets(tc, sockets), "failed to create sockets");
+
+ ATF_REQUIRE_ERRNO(EINVAL, setsockopt(sockets[1], IPPROTO_TCP,
+ TCP_RXTLS_ENABLE, en, sizeof(*en)) == -1);
+
+ close_sockets(sockets);
+}
+
+#define GEN_INVALID_RECEIVE_TEST(name, cipher_alg, key_size, auth_alg, \
+ minor) \
+ATF_TC_WITHOUT_HEAD(ktls_receive_invalid_##name); \
+ATF_TC_BODY(ktls_receive_invalid_##name, tc) \
+{ \
+ struct tls_enable en; \
+ uint64_t seqno; \
+ \
+ ATF_REQUIRE_KTLS(); \
+ seqno = random(); \
+ build_tls_enable(tc, cipher_alg, key_size, auth_alg, minor, \
+ seqno, &en); \
+ test_ktls_invalid_receive_cipher_suite(tc, &en); \
+ free_tls_enable(&en); \
+}
+
+#define ADD_INVALID_RECEIVE_TEST(name, cipher_alg, key_size, auth_alg, \
+ minor) \
+ ATF_TP_ADD_TC(tp, ktls_receive_invalid_##name);
+
+/*
+ * Ensure that invalid cipher suites are rejected for receive.
+ */
+INVALID_CIPHER_SUITES(GEN_INVALID_RECEIVE_TEST);
+
+static void
+test_ktls_unsupported_receive_cipher_suite(const atf_tc_t *tc,
+ struct tls_enable *en)
+{
+ int sockets[2];
+
+ ATF_REQUIRE_MSG(open_sockets(tc, sockets), "failed to create sockets");
+
+ ATF_REQUIRE_ERRNO(EPROTONOSUPPORT, setsockopt(sockets[1], IPPROTO_TCP,
+ TCP_RXTLS_ENABLE, en, sizeof(*en)) == -1);
+
+ close_sockets(sockets);
+}
+
+#define GEN_UNSUPPORTED_RECEIVE_TEST(name, cipher_alg, key_size, \
+ auth_alg, minor) \
+ATF_TC_WITHOUT_HEAD(ktls_receive_unsupported_##name); \
+ATF_TC_BODY(ktls_receive_unsupported_##name, tc) \
+{ \
+ struct tls_enable en; \
+ uint64_t seqno; \
+ \
+ ATF_REQUIRE_KTLS(); \
+ seqno = random(); \
+ build_tls_enable(tc, cipher_alg, key_size, auth_alg, minor, \
+ seqno, &en); \
+ test_ktls_unsupported_receive_cipher_suite(tc, &en); \
+ free_tls_enable(&en); \
+}
+
+#define ADD_UNSUPPORTED_RECEIVE_TEST(name, cipher_alg, key_size, \
+ auth_alg, minor) \
+ ATF_TP_ADD_TC(tp, ktls_receive_unsupported_##name);
+
+/*
+ * Ensure that valid cipher suites not supported for receive are
+ * rejected.
+ */
+TLS_10_TESTS(GEN_UNSUPPORTED_RECEIVE_TEST);
+
+/*
+ * Try to perform an invalid sendto(2) on a TXTLS-enabled socket, to exercise
+ * KTLS error handling in the socket layer.
+ */
+ATF_TC_WITHOUT_HEAD(ktls_sendto_baddst);
+ATF_TC_BODY(ktls_sendto_baddst, tc)
+{
+ char buf[32];
+ struct sockaddr_in dst;
+ struct tls_enable en;
+ ssize_t n;
+ int s;
+
+ ATF_REQUIRE_KTLS();
+
+ s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ ATF_REQUIRE(s >= 0);
+
+ build_tls_enable(tc, CRYPTO_AES_NIST_GCM_16, 128 / 8, 0,
+ TLS_MINOR_VER_THREE, (uint64_t)random(), &en);
+
+ ATF_REQUIRE(setsockopt(s, IPPROTO_TCP, TCP_TXTLS_ENABLE, &en,
+ sizeof(en)) == 0);
+
+ memset(&dst, 0, sizeof(dst));
+ dst.sin_family = AF_INET;
+ dst.sin_len = sizeof(dst);
+ dst.sin_addr.s_addr = htonl(INADDR_BROADCAST);
+ dst.sin_port = htons(12345);
+
+ memset(buf, 0, sizeof(buf));
+ n = sendto(s, buf, sizeof(buf), 0, (struct sockaddr *)&dst,
+ sizeof(dst));
+
+ /* Can't transmit to the broadcast address over TCP. */
+ ATF_REQUIRE_ERRNO(EACCES, n == -1);
+ ATF_REQUIRE(close(s) == 0);
+}
+
+/*
+ * Make sure that listen(2) returns an error for KTLS-enabled sockets, and
+ * verify that an attempt to enable KTLS on a listening socket fails.
+ */
+ATF_TC_WITHOUT_HEAD(ktls_listening_socket);
+ATF_TC_BODY(ktls_listening_socket, tc)
+{
+ struct tls_enable en;
+ struct sockaddr_in sin;
+ int s;
+
+ ATF_REQUIRE_KTLS();
+
+ s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ ATF_REQUIRE(s >= 0);
+ build_tls_enable(tc, CRYPTO_AES_NIST_GCM_16, 128 / 8, 0,
+ TLS_MINOR_VER_THREE, (uint64_t)random(), &en);
+ ATF_REQUIRE(setsockopt(s, IPPROTO_TCP, TCP_TXTLS_ENABLE, &en,
+ sizeof(en)) == 0);
+ ATF_REQUIRE_ERRNO(EINVAL, listen(s, 1) == -1);
+ ATF_REQUIRE(close(s) == 0);
+
+ s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ ATF_REQUIRE(s >= 0);
+ build_tls_enable(tc, CRYPTO_AES_NIST_GCM_16, 128 / 8, 0,
+ TLS_MINOR_VER_THREE, (uint64_t)random(), &en);
+ ATF_REQUIRE(setsockopt(s, IPPROTO_TCP, TCP_RXTLS_ENABLE, &en,
+ sizeof(en)) == 0);
+ ATF_REQUIRE_ERRNO(EINVAL, listen(s, 1) == -1);
+ ATF_REQUIRE(close(s) == 0);
+
+ s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ ATF_REQUIRE(s >= 0);
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ ATF_REQUIRE(bind(s, (struct sockaddr *)&sin, sizeof(sin)) == 0);
+ ATF_REQUIRE(listen(s, 1) == 0);
+ build_tls_enable(tc, CRYPTO_AES_NIST_GCM_16, 128 / 8, 0,
+ TLS_MINOR_VER_THREE, (uint64_t)random(), &en);
+ ATF_REQUIRE_ERRNO(ENOTCONN,
+ setsockopt(s, IPPROTO_TCP, TCP_TXTLS_ENABLE, &en, sizeof(en)) != 0);
+ ATF_REQUIRE_ERRNO(ENOTCONN,
+ setsockopt(s, IPPROTO_TCP, TCP_RXTLS_ENABLE, &en, sizeof(en)) != 0);
+ ATF_REQUIRE(close(s) == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ /* Transmit tests */
+ AES_CBC_TESTS(ADD_TRANSMIT_TESTS);
+ AES_GCM_TESTS(ADD_TRANSMIT_TESTS);
+ CHACHA20_TESTS(ADD_TRANSMIT_TESTS);
+ AES_CBC_TESTS(ADD_TRANSMIT_PADDING_TESTS);
+ AES_CBC_TESTS(ADD_TRANSMIT_EMPTY_FRAGMENT_TEST);
+ AES_GCM_TESTS(ADD_TRANSMIT_EMPTY_FRAGMENT_TEST);
+ CHACHA20_TESTS(ADD_TRANSMIT_EMPTY_FRAGMENT_TEST);
+ INVALID_CIPHER_SUITES(ADD_INVALID_TRANSMIT_TEST);
+
+ /* Receive tests */
+ TLS_10_TESTS(ADD_UNSUPPORTED_RECEIVE_TEST);
+ AES_CBC_NONZERO_TESTS(ADD_RECEIVE_TESTS);
+ AES_GCM_TESTS(ADD_RECEIVE_TESTS);
+ CHACHA20_TESTS(ADD_RECEIVE_TESTS);
+ AES_CBC_NONZERO_TESTS(ADD_RECEIVE_MTE_TESTS);
+ AES_CBC_NONZERO_TESTS(ADD_RECEIVE_EXPLICIT_IV_TESTS);
+ AES_GCM_12_TESTS(ADD_RECEIVE_EXPLICIT_IV_TESTS);
+ TLS_13_TESTS(ADD_RECEIVE_TLS13_TESTS);
+ INVALID_CIPHER_SUITES(ADD_INVALID_RECEIVE_TEST);
+
+ /* Miscellaneous */
+ ATF_TP_ADD_TC(tp, ktls_sendto_baddst);
+ ATF_TP_ADD_TC(tp, ktls_listening_socket);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/ktrace_test.c b/tests/sys/kern/ktrace_test.c
new file mode 100644
index 000000000000..785c78bedaba
--- /dev/null
+++ b/tests/sys/kern/ktrace_test.c
@@ -0,0 +1,620 @@
+/*-
+ * Copyright (c) 2015 John Baldwin <jhb@FreeBSD.org>
+ * Copyright (c) 2023 The FreeBSD Foundation
+ *
+ * This software was developed by Jake Freeland <jfree@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/capsicum.h>
+#include <sys/cpuset.h>
+#include <sys/ktrace.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/sysent.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/user.h>
+#include <sys/wait.h>
+
+#include <machine/sysarch.h>
+#include <netinet/in.h>
+
+#include <atf-c.h>
+#include <capsicum_helpers.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <signal.h>
+#include <sysdecode.h>
+
+/*
+ * A variant of ATF_REQUIRE that is suitable for use in child
+ * processes. This only works if the parent process is tripped up by
+ * the early exit and fails some requirement itself.
+ */
+#define CHILD_REQUIRE(exp) do { \
+ if (!(exp)) \
+ child_fail_require(__FILE__, __LINE__, \
+ #exp " not met\n"); \
+} while (0)
+#define CHILD_REQUIRE_EQ(actual, expected) do { \
+ __typeof__(expected) _e = expected; \
+ __typeof__(actual) _a = actual; \
+ if (_e != _a) \
+ child_fail_require(__FILE__, __LINE__, #actual \
+ " (%jd) == " #expected " (%jd) not met\n", \
+ (intmax_t)_a, (intmax_t)_e); \
+} while (0)
+
+static __dead2 void
+child_fail_require(const char *file, int line, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[1024];
+
+ /* Use write() not fprintf() to avoid possible duplicate output. */
+ snprintf(buf, sizeof(buf), "%s:%d: ", file, line);
+ write(STDERR_FILENO, buf, strlen(buf));
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ write(STDERR_FILENO, buf, strlen(buf));
+ va_end(ap);
+
+ _exit(32);
+}
+
+static void *
+xmalloc(size_t size)
+{
+ void *p;
+
+ p = malloc(size);
+ ATF_REQUIRE(p != NULL);
+ return (p);
+}
+
+/*
+ * Determine sysdecode ABI based on proc's ABI in sv_flags.
+ */
+static enum sysdecode_abi
+syscallabi(u_int sv_flags)
+{
+ switch (sv_flags & SV_ABI_MASK) {
+ case SV_ABI_FREEBSD:
+ return (SYSDECODE_ABI_FREEBSD);
+ case SV_ABI_LINUX:
+#ifdef __LP64__
+ if ((sv_flags & SV_ILP32) != 0)
+ return (SYSDECODE_ABI_LINUX32);
+#endif
+ return (SYSDECODE_ABI_LINUX);
+ }
+ return (SYSDECODE_ABI_UNKNOWN);
+}
+
+static int
+trace_child(int cpid, int facility, int status)
+{
+ int error, fd;
+
+ ATF_REQUIRE((fd = open("ktrace.out",
+ O_RDONLY | O_CREAT | O_TRUNC, 0600)) != -1);
+ ATF_REQUIRE_MSG(ktrace("ktrace.out", KTROP_SET, facility, cpid) != -1,
+ "ktrace failed: %s", strerror(errno));
+ /* Notify child that we've starting tracing. */
+ ATF_REQUIRE(kill(cpid, SIGUSR1) != -1);
+ /* Wait for child to raise violation and exit. */
+ ATF_REQUIRE(waitpid(cpid, &error, 0) != -1);
+ ATF_REQUIRE(WIFEXITED(error));
+ ATF_REQUIRE_EQ(WEXITSTATUS(error), status);
+ return (fd);
+}
+
+/*
+ * Start tracing capability violations and notify child that it can execute.
+ * Return @numv capability violations from child in @v.
+ */
+static void
+cap_trace_child(pid_t cpid, struct ktr_cap_fail *v, int numv)
+{
+ struct ktr_header header;
+ ssize_t n;
+ int fd;
+
+ fd = trace_child(cpid, KTRFAC_CAPFAIL, 0);
+
+ /* Read ktrace header and ensure violation occurred. */
+ for (int i = 0; i < numv; ++i) {
+ ATF_REQUIRE((n = read(fd, &header, sizeof(header))) != -1);
+ ATF_REQUIRE_EQ(n, sizeof(header));
+ ATF_REQUIRE_EQ(header.ktr_len, sizeof(*v));
+ ATF_REQUIRE_EQ(header.ktr_pid, cpid);
+ /* Read the capability violation. */
+ ATF_REQUIRE((n = read(fd, v + i,
+ sizeof(*v))) != -1);
+ ATF_REQUIRE_EQ(n, sizeof(*v));
+ }
+ ATF_REQUIRE(close(fd) != -1);
+}
+
+/*
+ * Test if ktrace will record an operation that is done with
+ * insufficient rights.
+ */
+ATF_TC_WITHOUT_HEAD(ktrace__cap_not_capable);
+ATF_TC_BODY(ktrace__cap_not_capable, tc)
+{
+ struct ktr_cap_fail violation;
+ cap_rights_t rights;
+ sigset_t set = { };
+ pid_t pid;
+ int error;
+
+ /* Block SIGUSR1 so child does not terminate. */
+ ATF_REQUIRE(sigaddset(&set, SIGUSR1) != -1);
+ ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) != -1);
+
+ ATF_REQUIRE((pid = fork()) != -1);
+ if (pid == 0) {
+ /* Limit fd rights to CAP_READ. */
+ cap_rights_init(&rights, CAP_READ);
+ CHILD_REQUIRE(caph_rights_limit(STDIN_FILENO, &rights) != -1);
+ CHILD_REQUIRE(caph_enter() != -1);
+ /* Wait until ktrace has started. */
+ CHILD_REQUIRE(sigwait(&set, &error) != -1);
+ CHILD_REQUIRE_EQ(error, SIGUSR1);
+ /* Write without CAP_WRITE. */
+ CHILD_REQUIRE(write(STDIN_FILENO, &pid, sizeof(pid)) == -1);
+ CHILD_REQUIRE_EQ(errno, ENOTCAPABLE);
+ exit(0);
+ }
+
+ cap_trace_child(pid, &violation, 1);
+ ATF_REQUIRE_EQ(violation.cap_type, CAPFAIL_NOTCAPABLE);
+ ATF_REQUIRE(cap_rights_is_set(&violation.cap_data.cap_needed,
+ CAP_WRITE));
+}
+
+/*
+ * Test if ktrace will record an attempt to increase rights.
+ */
+ATF_TC_WITHOUT_HEAD(ktrace__cap_increase_rights);
+ATF_TC_BODY(ktrace__cap_increase_rights, tc)
+{
+ struct ktr_cap_fail violation;
+ cap_rights_t rights;
+ sigset_t set = { };
+ pid_t pid;
+ int error;
+
+ /* Block SIGUSR1 so child does not terminate. */
+ ATF_REQUIRE(sigaddset(&set, SIGUSR1) != -1);
+ ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) != -1);
+
+ ATF_REQUIRE((pid = fork()) != -1);
+ if (pid == 0) {
+ /* Limit fd rights to CAP_READ. */
+ cap_rights_init(&rights, CAP_READ);
+ CHILD_REQUIRE(caph_rights_limit(STDIN_FILENO, &rights) != -1);
+ CHILD_REQUIRE(caph_enter() != -1);
+ /* Wait until ktrace has started. */
+ CHILD_REQUIRE(sigwait(&set, &error) != -1);
+ CHILD_REQUIRE_EQ(error, SIGUSR1);
+ /* Increase fd rights to include CAP_WRITE. */
+ cap_rights_set(&rights, CAP_WRITE);
+ CHILD_REQUIRE(caph_rights_limit(STDIN_FILENO, &rights) == -1);
+ CHILD_REQUIRE_EQ(errno, ENOTCAPABLE);
+ exit(0);
+ }
+
+ cap_trace_child(pid, &violation, 1);
+ ATF_REQUIRE_EQ(violation.cap_type, CAPFAIL_INCREASE);
+ ATF_REQUIRE(cap_rights_is_set(&violation.cap_data.cap_needed,
+ CAP_WRITE));
+}
+
+/*
+ * Test if disallowed syscalls are reported as capability violations.
+ */
+ATF_TC_WITHOUT_HEAD(ktrace__cap_syscall);
+ATF_TC_BODY(ktrace__cap_syscall, tc)
+{
+ struct kinfo_file kinf;
+ struct ktr_cap_fail violation[2];
+ sigset_t set = { };
+ pid_t pid;
+ int error;
+
+ /* Block SIGUSR1 so child does not terminate. */
+ ATF_REQUIRE(sigaddset(&set, SIGUSR1) != -1);
+ ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) != -1);
+
+ ATF_REQUIRE((pid = fork()) != -1);
+ if (pid == 0) {
+ /* Wait until ktrace has started. */
+ CHILD_REQUIRE(sigwait(&set, &error) != -1);
+ CHILD_REQUIRE_EQ(error, SIGUSR1);
+ /* chdir() is not permitted in capability mode. */
+ CHILD_REQUIRE(chdir(".") != -1);
+ kinf.kf_structsize = sizeof(struct kinfo_file);
+ /*
+ * fcntl() is permitted in capability mode,
+ * but the F_KINFO cmd is not.
+ */
+ CHILD_REQUIRE(fcntl(STDIN_FILENO, F_KINFO, &kinf) != -1);
+ exit(0);
+ }
+
+ cap_trace_child(pid, violation, 2);
+ ATF_REQUIRE_EQ(violation[0].cap_type, CAPFAIL_SYSCALL);
+ error = syscallabi(violation[0].cap_svflags);
+ ATF_REQUIRE_STREQ(sysdecode_syscallname(error, violation[0].cap_code),
+ "chdir");
+
+ ATF_REQUIRE_EQ(violation[1].cap_type, CAPFAIL_SYSCALL);
+ error = syscallabi(violation[1].cap_svflags);
+ ATF_REQUIRE_STREQ(sysdecode_syscallname(error, violation[1].cap_code),
+ "fcntl");
+ ATF_REQUIRE_EQ(violation[1].cap_data.cap_int, F_KINFO);
+}
+
+/*
+ * Test if sending a signal to another process is reported as
+ * a signal violation.
+ */
+ATF_TC_WITHOUT_HEAD(ktrace__cap_signal);
+ATF_TC_BODY(ktrace__cap_signal, tc)
+{
+ struct ktr_cap_fail violation;
+ sigset_t set = { };
+ pid_t pid;
+ int error;
+
+ /* Block SIGUSR1 so child does not terminate. */
+ ATF_REQUIRE(sigaddset(&set, SIGUSR1) != -1);
+ ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) != -1);
+
+ ATF_REQUIRE((pid = fork()) != -1);
+ if (pid == 0) {
+ /* Wait until ktrace has started. */
+ CHILD_REQUIRE(sigwait(&set, &error) != -1);
+ CHILD_REQUIRE_EQ(error, SIGUSR1);
+ /*
+ * Signals may only be sent to ourself. Sending signals
+ * to other processes is not allowed in capability mode.
+ */
+ CHILD_REQUIRE(kill(getppid(), SIGCONT) != -1);
+ exit(0);
+ }
+
+ cap_trace_child(pid, &violation, 1);
+ ATF_REQUIRE_EQ(violation.cap_type, CAPFAIL_SIGNAL);
+ error = syscallabi(violation.cap_svflags);
+ ATF_REQUIRE_STREQ(sysdecode_syscallname(error, violation.cap_code),
+ "kill");
+ ATF_REQUIRE_EQ(violation.cap_data.cap_int, SIGCONT);
+}
+
+/*
+ * Test if opening a socket with a restricted protocol is reported
+ * as a protocol violation.
+ */
+ATF_TC(ktrace__cap_proto);
+ATF_TC_HEAD(ktrace__cap_proto, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(ktrace__cap_proto, tc)
+{
+ struct ktr_cap_fail violation;
+ sigset_t set = { };
+ pid_t pid;
+ int error;
+
+ /* Block SIGUSR1 so child does not terminate. */
+ ATF_REQUIRE(sigaddset(&set, SIGUSR1) != -1);
+ ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) != -1);
+
+ ATF_REQUIRE((pid = fork()) != -1);
+ if (pid == 0) {
+ /* Wait until ktrace has started. */
+ CHILD_REQUIRE(sigwait(&set, &error) != -1);
+ CHILD_REQUIRE_EQ(error, SIGUSR1);
+ /*
+ * Certain protocols may not be used in capability mode.
+ * ICMP's raw-protocol interface is not allowed.
+ */
+ CHILD_REQUIRE(close(socket(AF_INET, SOCK_RAW,
+ IPPROTO_ICMP)) != -1);
+ exit(0);
+ }
+
+ cap_trace_child(pid, &violation, 1);
+ ATF_REQUIRE_EQ(violation.cap_type, CAPFAIL_PROTO);
+ error = syscallabi(violation.cap_svflags);
+ ATF_REQUIRE_STREQ(sysdecode_syscallname(error, violation.cap_code),
+ "socket");
+ ATF_REQUIRE_EQ(violation.cap_data.cap_int, IPPROTO_ICMP);
+}
+
+/*
+ * Test if sending data to an address using a socket is
+ * reported as a sockaddr violation.
+ */
+ATF_TC_WITHOUT_HEAD(ktrace__cap_sockaddr);
+ATF_TC_BODY(ktrace__cap_sockaddr, tc)
+{
+ struct sockaddr_in addr = { }, *saddr;
+ struct ktr_cap_fail violation;
+ sigset_t set = { };
+ pid_t pid;
+ int error, sfd;
+
+ /* Block SIGUSR1 so child does not terminate. */
+ ATF_REQUIRE(sigaddset(&set, SIGUSR1) != -1);
+ ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) != -1);
+
+ ATF_REQUIRE((sfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1);
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(5000);
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ ATF_REQUIRE(bind(sfd, (const struct sockaddr *)&addr,
+ sizeof(addr)) != -1);
+
+ ATF_REQUIRE((pid = fork()) != -1);
+ if (pid == 0) {
+ /* Wait until ktrace has started. */
+ CHILD_REQUIRE(sigwait(&set, &error) != -1);
+ CHILD_REQUIRE_EQ(error, SIGUSR1);
+ /*
+ * Sending data to an address is not permitted.
+ * In this case, sending data to @addr causes a
+ * violation.
+ */
+ CHILD_REQUIRE(sendto(sfd, NULL, 0, 0,
+ (const struct sockaddr *)&addr, sizeof(addr)) != -1);
+ exit(0);
+ }
+
+ cap_trace_child(pid, &violation, 1);
+ ATF_REQUIRE_EQ(violation.cap_type, CAPFAIL_SOCKADDR);
+ error = syscallabi(violation.cap_svflags);
+ ATF_REQUIRE_STREQ(sysdecode_syscallname(error, violation.cap_code),
+ "sendto");
+ saddr = (struct sockaddr_in *)&violation.cap_data.cap_sockaddr;
+ ATF_REQUIRE_EQ(saddr->sin_family, AF_INET);
+ ATF_REQUIRE_EQ(saddr->sin_port, htons(5000));
+ ATF_REQUIRE_EQ(saddr->sin_addr.s_addr, htonl(INADDR_LOOPBACK));
+ close(sfd);
+}
+
+/*
+ * Test if openat() with AT_FDCWD and absolute path are reported
+ * as namei violations.
+ */
+ATF_TC_WITHOUT_HEAD(ktrace__cap_namei);
+ATF_TC_BODY(ktrace__cap_namei, tc)
+{
+ struct ktr_cap_fail violation[2];
+ sigset_t set = { };
+ pid_t pid;
+ int error;
+
+ /* Block SIGUSR1 so child does not terminate. */
+ ATF_REQUIRE(sigaddset(&set, SIGUSR1) != -1);
+ ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) != -1);
+
+ ATF_REQUIRE((pid = fork()) != -1);
+ if (pid == 0) {
+ /* Wait until ktrace has started. */
+ CHILD_REQUIRE(sigwait(&set, &error) != -1);
+ CHILD_REQUIRE_EQ(error, SIGUSR1);
+ /*
+ * The AT_FDCWD file descriptor has not been opened
+ * and will be inaccessible in capability mode.
+ */
+ CHILD_REQUIRE(close(openat(AT_FDCWD, "ktrace.out",
+ O_RDONLY | O_CREAT)) != -1);
+ /*
+ * Absolute paths are inaccessible in capability mode.
+ */
+ CHILD_REQUIRE(close(openat(-1, "/", O_RDONLY)) != -1);
+ exit(0);
+ }
+
+ cap_trace_child(pid, violation, 2);
+ ATF_REQUIRE_EQ(violation[0].cap_type, CAPFAIL_NAMEI);
+ error = syscallabi(violation[0].cap_svflags);
+ ATF_REQUIRE_STREQ(sysdecode_syscallname(error, violation[0].cap_code),
+ "openat");
+ ATF_REQUIRE_STREQ(violation[0].cap_data.cap_path, "AT_FDCWD");
+
+ ATF_REQUIRE_EQ(violation[1].cap_type, CAPFAIL_NAMEI);
+ error = syscallabi(violation[1].cap_svflags);
+ ATF_REQUIRE_STREQ(sysdecode_syscallname(error, violation[1].cap_code),
+ "openat");
+ ATF_REQUIRE_STREQ(violation[1].cap_data.cap_path, "/");
+}
+
+/*
+ * Test if changing another process's cpu set is recorded as
+ * a cpuset violation.
+ */
+ATF_TC_WITHOUT_HEAD(ktrace__cap_cpuset);
+ATF_TC_BODY(ktrace__cap_cpuset, tc)
+{
+ struct ktr_cap_fail violation;
+ cpuset_t cpuset_mask = { };
+ sigset_t set = { };
+ pid_t pid;
+ int error;
+
+ /* Block SIGUSR1 so child does not terminate. */
+ ATF_REQUIRE(sigaddset(&set, SIGUSR1) != -1);
+ ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) != -1);
+
+ ATF_REQUIRE((pid = fork()) != -1);
+ if (pid == 0) {
+ /* Wait until ktrace has started. */
+ CHILD_REQUIRE(sigwait(&set, &error) != -1);
+ CHILD_REQUIRE_EQ(error, SIGUSR1);
+ /*
+ * Set cpu 0 affinity for parent process.
+ * Other process's cpu sets are restricted in capability
+ * mode, so this will raise a violation.
+ */
+ CPU_SET(0, &cpuset_mask);
+ CHILD_REQUIRE(cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID,
+ getppid(), sizeof(cpuset_mask), &cpuset_mask) != -1);
+ exit(0);
+ }
+
+ cap_trace_child(pid, &violation, 1);
+ ATF_REQUIRE_EQ(violation.cap_type, CAPFAIL_CPUSET);
+ error = syscallabi(violation.cap_svflags);
+ ATF_REQUIRE_STREQ(sysdecode_syscallname(error, violation.cap_code),
+ "cpuset_setaffinity");
+}
+
+ATF_TC_WITHOUT_HEAD(ktrace__cap_shm_open);
+ATF_TC_BODY(ktrace__cap_shm_open, tc)
+{
+ struct ktr_cap_fail violation;
+ sigset_t set = { };
+ pid_t pid;
+ int error;
+
+ /* Block SIGUSR1 so child does not terminate. */
+ ATF_REQUIRE(sigaddset(&set, SIGUSR1) != -1);
+ ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) != -1);
+
+ ATF_REQUIRE((pid = fork()) != -1);
+ if (pid == 0) {
+ /* Wait until ktrace has started. */
+ CHILD_REQUIRE(sigwait(&set, &error) != -1);
+ CHILD_REQUIRE_EQ(error, SIGUSR1);
+
+ CHILD_REQUIRE(shm_open("/ktrace_shm", O_RDWR | O_CREAT,
+ 0600) != -1);
+ CHILD_REQUIRE(shm_unlink("/ktrace_shm") != -1);
+ exit(0);
+ }
+
+ cap_trace_child(pid, &violation, 1);
+ ATF_REQUIRE_EQ(violation.cap_type, CAPFAIL_NAMEI);
+ error = syscallabi(violation.cap_svflags);
+ ATF_REQUIRE_STREQ(sysdecode_syscallname(error, violation.cap_code),
+ "shm_open2");
+ ATF_REQUIRE_STREQ(violation.cap_data.cap_path, "/ktrace_shm");
+}
+
+/*
+ * Make sure that ktrace is disabled upon exec of a setuid binary.
+ */
+ATF_TC(ktrace__setuid_exec);
+ATF_TC_HEAD(ktrace__setuid_exec, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "unprivileged");
+}
+ATF_TC_BODY(ktrace__setuid_exec, tc)
+{
+ struct ktr_header header;
+ struct ktr_syscall *syscall;
+ sigset_t set = { };
+ off_t off, off1;
+ ssize_t n;
+ pid_t pid;
+ int error, fd;
+
+ /* Block SIGUSR1 so child does not terminate. */
+ ATF_REQUIRE(sigaddset(&set, SIGUSR1) != -1);
+ ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) != -1);
+
+ ATF_REQUIRE((pid = fork()) != -1);
+ if (pid == 0) {
+ /* Wait until ktrace has started. */
+ CHILD_REQUIRE(sigwait(&set, &error) != -1);
+ CHILD_REQUIRE_EQ(error, SIGUSR1);
+
+ execve("/usr/bin/su", (char *[]){ "su", "whoami", NULL }, NULL);
+ _exit(0);
+ }
+
+ fd = trace_child(pid, KTRFAC_SYSCALL, 1);
+
+ n = read(fd, &header, sizeof(header));
+ ATF_REQUIRE(n >= 0);
+ ATF_REQUIRE_EQ((size_t)n, sizeof(header));
+ ATF_REQUIRE_EQ(header.ktr_pid, pid);
+ ATF_REQUIRE(header.ktr_len >= (int)sizeof(*syscall));
+
+ syscall = xmalloc(header.ktr_len);
+ n = read(fd, syscall, header.ktr_len);
+ ATF_REQUIRE(n >= 0);
+ ATF_REQUIRE_EQ(n, header.ktr_len);
+ if (syscall->ktr_code == SYS_sigwait) {
+ free(syscall);
+
+ /* Skip the sigwait() syscall. */
+ n = read(fd, &header, sizeof(header));
+ ATF_REQUIRE(n >= 0);
+ ATF_REQUIRE_EQ((size_t)n, sizeof(header));
+ ATF_REQUIRE_EQ(header.ktr_pid, pid);
+ ATF_REQUIRE(header.ktr_len >= (int)sizeof(*syscall));
+
+ syscall = xmalloc(header.ktr_len);
+ n = read(fd, syscall, header.ktr_len);
+ ATF_REQUIRE(n >= 0);
+ ATF_REQUIRE_EQ(n, header.ktr_len);
+ }
+ ATF_REQUIRE_EQ(syscall->ktr_code, SYS_execve);
+ free(syscall);
+
+ /* su is setuid root, so this should have been the last entry. */
+ off = lseek(fd, 0, SEEK_CUR);
+ ATF_REQUIRE(off != -1);
+ off1 = lseek(fd, 0, SEEK_END);
+ ATF_REQUIRE(off1 != -1);
+ ATF_REQUIRE_EQ(off, off1);
+
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, ktrace__cap_not_capable);
+ ATF_TP_ADD_TC(tp, ktrace__cap_increase_rights);
+ ATF_TP_ADD_TC(tp, ktrace__cap_syscall);
+ ATF_TP_ADD_TC(tp, ktrace__cap_signal);
+ ATF_TP_ADD_TC(tp, ktrace__cap_proto);
+ ATF_TP_ADD_TC(tp, ktrace__cap_sockaddr);
+ ATF_TP_ADD_TC(tp, ktrace__cap_namei);
+ ATF_TP_ADD_TC(tp, ktrace__cap_cpuset);
+ ATF_TP_ADD_TC(tp, ktrace__cap_shm_open);
+ ATF_TP_ADD_TC(tp, ktrace__setuid_exec);
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/libkern_crc32.c b/tests/sys/kern/libkern_crc32.c
new file mode 100644
index 000000000000..f386f565bb06
--- /dev/null
+++ b/tests/sys/kern/libkern_crc32.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2017 Conrad Meyer <cem@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/gsb_crc32.h>
+
+#include <stdint.h>
+
+#include <atf-c.h>
+
+#if defined(__amd64__) || defined(__i386__)
+#include <machine/cpufunc.h>
+#include <machine/specialreg.h>
+
+static bool
+have_sse42(void)
+{
+ u_int cpu_registers[4];
+
+ do_cpuid(1, cpu_registers);
+
+ return ((cpu_registers[2] & CPUID2_SSE42) != 0);
+}
+#endif
+
+static void
+check_crc32c(uint32_t expected, uint32_t crc32c, const void *buffer,
+ size_t length)
+{
+ uint32_t act;
+
+#if defined(__amd64__) || defined(__i386__)
+ if (have_sse42()) {
+ act = sse42_crc32c(crc32c, buffer, length);
+ ATF_CHECK_EQ_MSG(expected, act,
+ "sse42_crc32c expected 0x%08x, got 0x%08x", expected, act);
+ }
+#elif defined(__aarch64__)
+ act = armv8_crc32c(crc32c, buffer, length);
+ ATF_CHECK_EQ_MSG(expected, act,
+ "armv8_crc32c expected 0x%08x, got 0x%08x", expected, act);
+#endif
+ act = singletable_crc32c(crc32c, buffer, length);
+ ATF_CHECK_EQ_MSG(expected, act,
+ "singletable_crc32c expected 0x%08x, got 0x%08x", expected, act);
+ act = multitable_crc32c(crc32c, buffer, length);
+ ATF_CHECK_EQ_MSG(expected, act,
+ "multitable_crc32c expected 0x%08x, got 0x%08x", expected, act);
+}
+
+ATF_TC_WITHOUT_HEAD(crc32c_basic_correctness);
+ATF_TC_BODY(crc32c_basic_correctness, tc)
+{
+ const uint64_t inputs[] = {
+ 0xf408c634b3a9142,
+ 0x80539e8c7c352e2b,
+ 0x62e9121db6e4d649,
+ 0x899345850ed0a286,
+ 0x2302df11b4a43b15,
+ 0xe943de7b3d35d70,
+ 0xdf1ff2bf41abf56b,
+ 0x9bc138abae315de2,
+ 0x31cc82e56234f0ff,
+ 0xce63c0cd6988e847,
+ 0x3e42f6b78ee352fa,
+ 0xfa4085436078cfa6,
+ 0x53349558bf670a4b,
+ 0x2714e10e7d722c61,
+ 0xc0d3261addfc6908,
+ 0xd1567c3181d3a1bf,
+ };
+ const uint32_t results[] = {
+ 0x2ce33ede,
+ 0xc49cc573,
+ 0xb8683c96,
+ 0x6918660d,
+ 0xa904e522,
+ 0x52dbc42c,
+ 0x98863c22,
+ 0x894d5d2c,
+ 0xb003745d,
+ 0xfc496dbd,
+ 0x97d2fbb5,
+ 0x3c062ef1,
+ 0xcc2eff18,
+ 0x6a9b09f6,
+ 0x420242c1,
+ 0xfd562dc3,
+ };
+ size_t i;
+
+ ATF_REQUIRE(nitems(inputs) == nitems(results));
+
+ for (i = 0; i < nitems(inputs); i++) {
+ check_crc32c(results[i], ~0u, &inputs[i], sizeof(inputs[0]));
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(crc32c_alignment);
+ATF_TC_BODY(crc32c_alignment, tc)
+{
+ const uint64_t input = 0xf408c634b3a9142;
+ const uint32_t result = 0x2ce33ede;
+ unsigned char buf[15];
+ size_t i;
+
+ for (i = 1; i < 8; i++) {
+ memcpy(&buf[i], &input, sizeof(input));
+ check_crc32c(result, ~0u, &buf[i], sizeof(input));
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(crc32c_trailing_bytes);
+ATF_TC_BODY(crc32c_trailing_bytes, tc)
+{
+ const unsigned char input[] = {
+ 0x87, 0x54, 0x74, 0xd2, 0xb, 0x9b, 0xdd, 0xf6, 0x68, 0x37,
+ 0xd4, 0x4, 0x5e, 0xa9, 0xb3
+ };
+ const uint32_t result = 0xec638d62;
+
+ check_crc32c(result, ~0u, input, sizeof(input));
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, crc32c_basic_correctness);
+ ATF_TP_ADD_TC(tp, crc32c_alignment);
+ ATF_TP_ADD_TC(tp, crc32c_trailing_bytes);
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/listener_wakeup.c b/tests/sys/kern/listener_wakeup.c
new file mode 100644
index 000000000000..39e8596c335e
--- /dev/null
+++ b/tests/sys/kern/listener_wakeup.c
@@ -0,0 +1,293 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Gleb Smirnoff <glebius@FreeBSD.org>
+ * Copyright (c) 2018 Rozhuk Ivan <rozhuk.im@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/select.h>
+#include <sys/event.h>
+#include <poll.h>
+
+#include <stdbool.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <pthread_np.h>
+
+#include <atf-c.h>
+
+/*
+ * This test runs several scenarios when sleep(9) on a listen(2)ing socket is
+ * interrupted by shutdown(2) or by close(2). What should happen in that case
+ * is not specified, neither is documented. However, there is certain behavior
+ * that we have and this test makes sure it is preserved. The known software
+ * to rely on the behavior is FreeSWITCH telephony software (see bug 227259).
+ * There might be more. This test is based on submission with the bug, bugzilla
+ * attachment 192260.
+ */
+
+static const struct test {
+ enum {
+ SLEEP_ACCEPT = 0,
+ SLEEP_SELECT,
+ SLEEP_POLL,
+ SLEEP_KQUEUE,
+ NSLEEP
+ } sleep;
+ enum {
+ WAKEUP_SHUTDOWN,
+ WAKEUP_CLOSE,
+ } wakeup;
+ enum {
+ AFTER,
+ BEFORE,
+ } when;
+ bool nonblock;
+ int result;
+} tests[] = {
+ { SLEEP_ACCEPT, WAKEUP_SHUTDOWN, AFTER, false, ECONNABORTED },
+ { SLEEP_SELECT, WAKEUP_SHUTDOWN, AFTER, false, 0 },
+ { SLEEP_POLL, WAKEUP_SHUTDOWN, AFTER, false, 0 },
+ { SLEEP_KQUEUE, WAKEUP_SHUTDOWN, AFTER, false, 0 },
+ { SLEEP_ACCEPT, WAKEUP_CLOSE, AFTER, false, ETIMEDOUT },
+ { SLEEP_SELECT, WAKEUP_CLOSE, AFTER, false, EBADF },
+ { SLEEP_POLL, WAKEUP_CLOSE, AFTER, false, 0 },
+ { SLEEP_KQUEUE, WAKEUP_CLOSE, AFTER, false, 0 },
+ { SLEEP_ACCEPT, WAKEUP_SHUTDOWN, BEFORE, false, ECONNABORTED },
+ { SLEEP_SELECT, WAKEUP_SHUTDOWN, BEFORE, false, 0 },
+ { SLEEP_POLL, WAKEUP_SHUTDOWN, BEFORE, false, 0 },
+ { SLEEP_KQUEUE, WAKEUP_SHUTDOWN, BEFORE, false, 0 },
+ { SLEEP_SELECT, WAKEUP_SHUTDOWN, AFTER, true, 0 },
+ { SLEEP_POLL, WAKEUP_SHUTDOWN, AFTER, true, 0 },
+ { SLEEP_KQUEUE, WAKEUP_SHUTDOWN, AFTER, true, 0 },
+ { SLEEP_SELECT, WAKEUP_SHUTDOWN, BEFORE, true, 0 },
+ { SLEEP_POLL, WAKEUP_SHUTDOWN, BEFORE, true, 0 },
+ { SLEEP_KQUEUE, WAKEUP_SHUTDOWN, BEFORE, true, 0 },
+};
+
+static int
+tcp_listen(void)
+{
+ struct sockaddr_in sin = {
+ .sin_family = PF_INET,
+ .sin_len = sizeof(sin),
+ };
+ int s;
+
+ ATF_REQUIRE((s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) != -1);
+ ATF_REQUIRE(bind(s, (struct sockaddr *)&sin, sizeof(sin)) == 0);
+ ATF_REQUIRE(listen(s, -1) == 0);
+
+ return (s);
+}
+
+static int
+unix_listen(void)
+{
+ struct sockaddr_un sun = {
+ .sun_family = AF_UNIX,
+ .sun_len = sizeof(sun),
+ .sun_path = "listen-shutdown-test.sock",
+ };
+ int s;
+
+ ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) != -1);
+ (void)unlink(sun.sun_path);
+ ATF_REQUIRE(bind(s, (struct sockaddr *)&sun, sizeof(sun)) == 0);
+ ATF_REQUIRE(listen(s, -1) == 0);
+
+ return (s);
+}
+
+static const struct proto {
+ const char *name;
+ int (*listen)(void);
+} protos[] = {
+ { "PF_INET", tcp_listen },
+ { "PF_UNIX", unix_listen },
+};
+
+static int
+sleep_accept(int s)
+{
+ int rv;
+
+ rv = accept(s, NULL, NULL);
+
+ return (rv == -1 ? errno : 0);
+}
+
+static int
+sleep_select(int s)
+{
+ fd_set fds;
+ int rv;
+
+ FD_ZERO(&fds);
+ FD_SET(s, &fds);
+ rv = select(s + 1, &fds, &fds, &fds, NULL);
+
+ return (rv == -1 ? errno : 0);
+}
+
+static int
+sleep_poll(int s)
+{
+ struct pollfd fds = {
+ .fd = s,
+ .events = (POLLIN | POLLPRI | POLLRDNORM | POLLWRNORM |
+ POLLRDBAND | POLLWRBAND),
+ .revents = 0,
+ };
+ int rv;
+
+ rv = poll(&fds, 1, INFTIM);
+
+ return (rv == -1 ? errno : 0);
+}
+
+static int
+sleep_kqueue(int s)
+{
+ struct kevent kev;
+ int kq, error;
+
+ ATF_REQUIRE((kq = kqueue()) != -1);
+ EV_SET(&kev, s, EVFILT_READ, EV_ADD, 0, 0, NULL);
+ if (kevent(kq, &kev, 1, NULL, 0, NULL) == -1) {
+ error = errno;
+ } else {
+ if (kev.flags & EV_ERROR)
+ error = (int)kev.data;
+ else
+ error = 0;
+ }
+ ATF_REQUIRE(close(kq) == 0);
+
+ return (error);
+}
+
+typedef int sleep_syscall_t(int);
+static sleep_syscall_t *sleep_syscalls[NSLEEP] = {
+ [SLEEP_ACCEPT] = sleep_accept,
+ [SLEEP_SELECT] = sleep_select,
+ [SLEEP_POLL] = sleep_poll,
+ [SLEEP_KQUEUE] = sleep_kqueue,
+};
+
+struct test_ctx {
+ struct test const *test;
+ int s;
+ int result;
+};
+
+static void *
+sleep_syscall_thread(void *data) {
+ struct test_ctx *ctx = data;
+
+ ctx->result = sleep_syscalls[ctx->test->sleep](ctx->s);
+
+ return (NULL);
+}
+
+static void
+run_tests(const struct proto *pr)
+{
+ pthread_t tid;
+ struct timespec ts;
+ int error;
+
+ for (u_int i = 0; i < nitems(tests); i ++) {
+ struct test const *t = &tests[i];
+ struct test_ctx ctx = {
+ .test = t,
+ /* Note: tested syscalls don't return this. */
+ .result = ETIMEDOUT,
+ };
+
+ ctx.s = pr->listen();
+ if (t->nonblock)
+ ATF_REQUIRE(fcntl(ctx.s, F_SETFL, O_NONBLOCK) != -1);
+
+ if (t->when == AFTER) {
+ ATF_REQUIRE(pthread_create(&tid, NULL,
+ sleep_syscall_thread, &ctx) == 0);
+ usleep(100000);
+ }
+
+ switch (t->wakeup) {
+ case WAKEUP_SHUTDOWN:
+ ATF_REQUIRE(shutdown(ctx.s, SHUT_RDWR) == -1);
+ ATF_REQUIRE(errno == ENOTCONN);
+ break;
+ case WAKEUP_CLOSE:
+ ATF_REQUIRE(close(ctx.s) == 0);
+ break;
+ }
+
+ if (t->when == BEFORE) {
+ ATF_REQUIRE(pthread_create(&tid, NULL,
+ sleep_syscall_thread, &ctx) == 0);
+ usleep(100000);
+ }
+
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_sec++;
+ if ((error = pthread_timedjoin_np(tid, NULL, &ts)) != 0) {
+ ATF_REQUIRE(pthread_cancel(tid) == 0);
+ ATF_REQUIRE(error == ETIMEDOUT);
+ ATF_REQUIRE(ctx.result == ETIMEDOUT);
+ }
+
+ ATF_REQUIRE_MSG(ctx.result == t->result,
+ "proto %s sleeping syscall #%d wakeup #%d nb %d, "
+ "expected %d, got %d", pr->name, t->sleep, t->wakeup,
+ t->nonblock, t->result, ctx.result);
+
+ if (t->wakeup == WAKEUP_SHUTDOWN)
+ ATF_REQUIRE(close(ctx.s) == 0);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(all);
+ATF_TC_BODY(all, tc)
+{
+ for (u_int f = 0; f < nitems(protos); f++)
+ run_tests(&protos[f]);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, all);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/logsigexit_test.sh b/tests/sys/kern/logsigexit_test.sh
new file mode 100644
index 000000000000..f0db02613533
--- /dev/null
+++ b/tests/sys/kern/logsigexit_test.sh
@@ -0,0 +1,37 @@
+#
+# Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+atf_test_case basic
+basic_body()
+{
+
+ if ! dmesg >/dev/null 2>&1; then
+ atf_skip "No dmesg(8) access"
+ fi
+
+ # SIGABRT carefully chosen to avoid issues when run under Kyua. No
+ # matter the value of the global kern.logsigexit, these should force
+ # the messages as appropriate and we'll all be happy.
+ proccontrol -m logsigexit -s enable \
+ sh -c 'echo $$ > enabled.out; kill -ABRT $$'
+ proccontrol -m logsigexit -s disable \
+ sh -c 'echo $$ > disabled.out; kill -ABRT $$'
+
+ atf_check test -s enabled.out
+ atf_check test -s disabled.out
+
+ read enpid < enabled.out
+ read dispid < disabled.out
+
+ atf_check -o save:dmesg.out dmesg
+ atf_check grep -Eq "$enpid.+exited on signal" dmesg.out
+ atf_check -s not-exit:0 grep -Eq "$dispid.+exited on signal" dmesg.out
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case basic
+}
diff --git a/tests/sys/kern/module_test.c b/tests/sys/kern/module_test.c
new file mode 100644
index 000000000000..37cbc3b3271a
--- /dev/null
+++ b/tests/sys/kern/module_test.c
@@ -0,0 +1,197 @@
+/*-
+ * Copyright (c) 2022 University of Cambridge
+ *
+ * This software was developed by Ararat River Consulting, LLC under
+ * sponsorship from the University of Cambridge Computer Laboratory
+ * (Department of Computer Science and Technology) and Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/module.h>
+#include <errno.h>
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(modfind);
+ATF_TC_BODY(modfind, tc)
+{
+ int modid;
+
+ /* This module is present in sys/kern/subr_bus.c. */
+ modid = modfind("rootbus");
+ ATF_REQUIRE(modid > 0);
+
+ modid = modfind("nonexistent_module");
+ ATF_REQUIRE(modid == -1);
+ ATF_REQUIRE(errno == ENOENT);
+}
+
+ATF_TC_WITHOUT_HEAD(modnext);
+ATF_TC_BODY(modnext, tc)
+{
+ int modid;
+
+ /* This assumes -1 is never used as a valid module id. */
+ modid = modnext(-1);
+ ATF_REQUIRE(modid == -1);
+ ATF_REQUIRE(errno == ENOENT);
+
+ modid = modnext(0);
+ ATF_REQUIRE(modid > 0);
+
+ for (;;) {
+ modid = modnext(modid);
+ ATF_REQUIRE(modid >= 0);
+ if (modid == 0)
+ break;
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(modfnext);
+ATF_TC_BODY(modfnext, tc)
+{
+ int modid;
+
+ /* This assumes -1 is never used as a valid module id. */
+ modid = modfnext(-1);
+ ATF_REQUIRE(modid == -1);
+ ATF_REQUIRE(errno == ENOENT);
+
+ modid = modfnext(0);
+ ATF_REQUIRE(modid == -1);
+ ATF_REQUIRE(errno == ENOENT);
+
+ modid = modnext(0);
+ ATF_REQUIRE(modid > 0);
+
+ for (;;) {
+ modid = modfnext(modid);
+ ATF_REQUIRE(modid >= 0);
+ if (modid == 0)
+ break;
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(modstat);
+ATF_TC_BODY(modstat, tc)
+{
+ struct module_stat ms;
+ int modid;
+
+ ms.version = sizeof(ms);
+ ATF_REQUIRE(modstat(0, &ms) == -1);
+ ATF_REQUIRE(errno == ENOENT);
+
+ modid = modnext(0);
+ ATF_REQUIRE(modid > 0);
+
+ ATF_REQUIRE(modstat(modid, NULL) == -1);
+ ATF_REQUIRE(errno == EFAULT);
+
+ ms.version = 0;
+ ATF_REQUIRE(modstat(modid, &ms) == -1);
+ ATF_REQUIRE(errno == EINVAL);
+
+ ms.version = sizeof(ms);
+ ATF_REQUIRE(modstat(modid, &ms) == 0);
+ ATF_REQUIRE(ms.id == modid);
+ if (strnlen(ms.name, sizeof(ms.name)) < sizeof(ms.name))
+ ATF_REQUIRE(modfind(ms.name) == modid);
+}
+
+ATF_TC_WITHOUT_HEAD(modstat_v1);
+ATF_TC_BODY(modstat_v1, tc)
+{
+ struct module_stat_v1 {
+ int version;
+ char name[32];
+ int refs;
+ int id;
+ } ms;
+ int modid;
+
+ ms.version = sizeof(ms);
+ ATF_REQUIRE(modstat(0, (struct module_stat *)&ms) == -1);
+ ATF_REQUIRE(errno == ENOENT);
+
+ modid = modnext(0);
+ ATF_REQUIRE(modid > 0);
+
+ ATF_REQUIRE(modstat(modid, NULL) == -1);
+ ATF_REQUIRE(errno == EFAULT);
+
+ ms.version = 0;
+ ATF_REQUIRE(modstat(modid, (struct module_stat *)&ms) == -1);
+ ATF_REQUIRE(errno == EINVAL);
+
+ ms.version = sizeof(ms);
+ ATF_REQUIRE(modstat(modid, (struct module_stat *)&ms) == 0);
+ ATF_REQUIRE(ms.id == modid);
+ if (strnlen(ms.name, sizeof(ms.name)) < sizeof(ms.name))
+ ATF_REQUIRE(modfind(ms.name) == modid);
+}
+
+ATF_TC_WITHOUT_HEAD(modstat_v2);
+ATF_TC_BODY(modstat_v2, tc)
+{
+ struct module_stat_v2 {
+ int version;
+ char name[32];
+ int refs;
+ int id;
+ modspecific_t data;
+ } ms;
+ int modid;
+
+ ms.version = sizeof(ms);
+ ATF_REQUIRE(modstat(0, (struct module_stat *)&ms) == -1);
+ ATF_REQUIRE(errno == ENOENT);
+
+ modid = modnext(0);
+ ATF_REQUIRE(modid > 0);
+
+ ATF_REQUIRE(modstat(modid, NULL) == -1);
+ ATF_REQUIRE(errno == EFAULT);
+
+ ms.version = 0;
+ ATF_REQUIRE(modstat(modid, (struct module_stat *)&ms) == -1);
+ ATF_REQUIRE(errno == EINVAL);
+
+ ms.version = sizeof(ms);
+ ATF_REQUIRE(modstat(modid, (struct module_stat *)&ms) == 0);
+ ATF_REQUIRE(ms.id == modid);
+ if (strnlen(ms.name, sizeof(ms.name)) < sizeof(ms.name))
+ ATF_REQUIRE(modfind(ms.name) == modid);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, modfind);
+ ATF_TP_ADD_TC(tp, modnext);
+ ATF_TP_ADD_TC(tp, modfnext);
+ ATF_TP_ADD_TC(tp, modstat);
+ ATF_TP_ADD_TC(tp, modstat_v1);
+ ATF_TP_ADD_TC(tp, modstat_v2);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/pdeathsig.c b/tests/sys/kern/pdeathsig.c
new file mode 100644
index 000000000000..51032e07ff9f
--- /dev/null
+++ b/tests/sys/kern/pdeathsig.c
@@ -0,0 +1,345 @@
+/*-
+ * Copyright (c) 2018 Thomas Munro
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <assert.h>
+#include <atf-c.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/procctl.h>
+#include <sys/ptrace.h>
+#include <sys/signal.h>
+#include <sys/types.h>
+
+static void
+dummy_signal_handler(int signum)
+{
+}
+
+ATF_TC_WITHOUT_HEAD(arg_validation);
+ATF_TC_BODY(arg_validation, tc)
+{
+ int signum;
+ int rc;
+
+ /* bad signal */
+ signum = 8888;
+ rc = procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &signum);
+ ATF_CHECK_EQ(-1, rc);
+ ATF_CHECK_EQ(EINVAL, errno);
+
+ /* bad id type */
+ signum = SIGINFO;
+ rc = procctl(8888, 0, PROC_PDEATHSIG_CTL, &signum);
+ ATF_CHECK_EQ(-1, rc);
+ ATF_CHECK_EQ(EINVAL, errno);
+
+ /* bad id (pid that doesn't match mine or zero) */
+ signum = SIGINFO;
+ rc = procctl(P_PID, (((getpid() + 1) % 10) + 100),
+ PROC_PDEATHSIG_CTL, &signum);
+ ATF_CHECK_EQ(-1, rc);
+ ATF_CHECK_EQ(EINVAL, errno);
+
+ /* null pointer */
+ signum = SIGINFO;
+ rc = procctl(P_PID, 0, PROC_PDEATHSIG_CTL, NULL);
+ ATF_CHECK_EQ(-1, rc);
+ ATF_CHECK_EQ(EFAULT, errno);
+
+ /* good (pid == 0) */
+ signum = SIGINFO;
+ rc = procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &signum);
+ ATF_CHECK_EQ(0, rc);
+
+ /* good (pid == my pid) */
+ signum = SIGINFO;
+ rc = procctl(P_PID, getpid(), PROC_PDEATHSIG_CTL, &signum);
+ ATF_CHECK_EQ(0, rc);
+
+ /* check that we can read the signal number back */
+ signum = 0xdeadbeef;
+ rc = procctl(P_PID, 0, PROC_PDEATHSIG_STATUS, &signum);
+ ATF_CHECK_EQ(0, rc);
+ ATF_CHECK_EQ(SIGINFO, signum);
+}
+
+ATF_TC_WITHOUT_HEAD(fork_no_inherit);
+ATF_TC_BODY(fork_no_inherit, tc)
+{
+ int status;
+ int signum;
+ int rc;
+
+ /* request a signal on parent death in the parent */
+ signum = SIGINFO;
+ rc = procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &signum);
+
+ rc = fork();
+ ATF_REQUIRE(rc != -1);
+ if (rc == 0) {
+ /* check that we didn't inherit the setting */
+ signum = 0xdeadbeef;
+ rc = procctl(P_PID, 0, PROC_PDEATHSIG_STATUS, &signum);
+ assert(rc == 0);
+ assert(signum == 0);
+ _exit(0);
+ }
+
+ /* wait for the child to exit successfully */
+ waitpid(rc, &status, 0);
+ ATF_CHECK_EQ(0, status);
+}
+
+ATF_TC_WITHOUT_HEAD(exec_inherit);
+ATF_TC_BODY(exec_inherit, tc)
+{
+ int status;
+ int rc;
+
+ rc = fork();
+ ATF_REQUIRE(rc != -1);
+ if (rc == 0) {
+ char exec_path[1024];
+ int signum;
+
+ /* compute the path of the helper executable */
+ snprintf(exec_path, sizeof(exec_path), "%s/pdeathsig_helper",
+ atf_tc_get_config_var(tc, "srcdir"));
+
+ /* request a signal on parent death and register a handler */
+ signum = SIGINFO;
+ rc = procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &signum);
+ assert(rc == 0);
+
+ /* execute helper program: it asserts that it has the setting */
+ rc = execl(exec_path, exec_path, NULL);
+ assert(rc == 0);
+ _exit(0);
+ }
+
+ /* wait for the child to exit successfully */
+ waitpid(rc, &status, 0);
+ ATF_CHECK_EQ(0, status);
+}
+
+ATF_TC_WITHOUT_HEAD(signal_delivered);
+ATF_TC_BODY(signal_delivered, tc)
+{
+ sigset_t sigset;
+ int signum;
+ int rc;
+ int pipe_ca[2];
+ int pipe_cb[2];
+ char buffer;
+
+ rc = pipe(pipe_ca);
+ ATF_REQUIRE(rc == 0);
+ rc = pipe(pipe_cb);
+ ATF_REQUIRE(rc == 0);
+
+ rc = fork();
+ ATF_REQUIRE(rc != -1);
+ if (rc == 0) {
+ rc = fork();
+ assert(rc >= 0);
+ if (rc == 0) {
+ /* process C */
+ signum = SIGINFO;
+
+ /* block signals so we can handle them synchronously */
+ rc = sigfillset(&sigset);
+ assert(rc == 0);
+ rc = sigprocmask(SIG_SETMASK, &sigset, NULL);
+ assert(rc == 0);
+
+ /* register a dummy handler or the kernel will not queue it */
+ signal(signum, dummy_signal_handler);
+
+ /* request a signal on death of our parent B */
+ rc = procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &signum);
+ assert(rc == 0);
+
+ /* tell B that we're ready for it to exit now */
+ rc = write(pipe_cb[1], ".", 1);
+ assert(rc == 1);
+
+ /* wait for B to die and signal us... */
+ signum = 0xdeadbeef;
+ rc = sigwait(&sigset, &signum);
+ assert(rc == 0);
+ assert(signum == SIGINFO);
+
+ /* tell A the test passed */
+ rc = write(pipe_ca[1], ".", 1);
+ assert(rc == 1);
+ _exit(0);
+ }
+
+ /* process B */
+
+ /* wait for C to tell us it is ready for us to exit */
+ rc = read(pipe_cb[0], &buffer, 1);
+ assert(rc == 1);
+
+ /* now we exit so that C gets a signal */
+ _exit(0);
+ }
+ /* process A */
+
+ /* wait for C to tell us the test passed */
+ rc = read(pipe_ca[0], &buffer, 1);
+ ATF_CHECK_EQ(1, rc);
+}
+
+ATF_TC_WITHOUT_HEAD(signal_delivered_ptrace);
+ATF_TC_BODY(signal_delivered_ptrace, tc)
+{
+ sigset_t sigset;
+ int signum;
+ int rc;
+ int pipe_ca[2];
+ int pipe_db[2];
+ int pipe_cd[2];
+ char buffer;
+ int status;
+
+ rc = pipe(pipe_ca);
+ ATF_REQUIRE(rc == 0);
+ rc = pipe(pipe_db);
+ ATF_REQUIRE(rc == 0);
+ rc = pipe(pipe_cd);
+ assert(rc == 0);
+
+ rc = fork();
+ ATF_REQUIRE(rc != -1);
+ if (rc == 0) {
+ pid_t c_pid;
+
+ /* process B */
+
+ rc = fork();
+ assert(rc >= 0);
+ if (rc == 0) {
+ /* process C */
+ signum = SIGINFO;
+
+ /* block signals so we can handle them synchronously */
+ rc = sigfillset(&sigset);
+ assert(rc == 0);
+ rc = sigprocmask(SIG_SETMASK, &sigset, NULL);
+ assert(rc == 0);
+
+ /* register a dummy handler or the kernel will not queue it */
+ signal(signum, dummy_signal_handler);
+
+ /* request a signal on parent death and register a handler */
+ rc = procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &signum);
+ assert(rc == 0);
+
+ rc = write(pipe_cd[1], "x", 1);
+ assert(rc == 1);
+
+ /* wait for B to die and signal us... */
+ signum = 0xdeadbeef;
+ rc = sigwait(&sigset, &signum);
+ assert(rc == 0);
+ assert(signum == SIGINFO);
+
+ /* tell A the test passed */
+ rc = write(pipe_ca[1], ".", 1);
+ assert(rc == 1);
+ _exit(0);
+ }
+ c_pid = rc;
+
+
+ /* fork another process to ptrace C */
+ rc = fork();
+ assert(rc >= 0);
+ if (rc == 0) {
+
+ /* process D */
+ rc = ptrace(PT_ATTACH, c_pid, 0, 0);
+ assert(rc == 0);
+
+ waitpid(c_pid, &status, 0);
+ assert(WIFSTOPPED(status));
+ assert(WSTOPSIG(status) == SIGSTOP);
+
+ rc = ptrace(PT_CONTINUE, c_pid, (caddr_t) 1, 0);
+ assert(rc == 0);
+
+ rc = read(pipe_cd[0], &buffer, 1);
+ assert(rc == 1);
+
+ /* tell B that we're ready for it to exit now */
+ rc = write(pipe_db[1], ".", 1);
+ assert(rc == 1);
+
+ waitpid(c_pid, &status, 0);
+ assert(WIFSTOPPED(status));
+ assert(WSTOPSIG(status) == SIGINFO);
+
+ rc = ptrace(PT_CONTINUE, c_pid, (caddr_t) 1,
+ WSTOPSIG(status));
+ assert(rc == 0);
+
+ waitpid(c_pid, &status, 0);
+ if (!WIFEXITED(status))
+ ptrace(PT_DETACH, c_pid, 0, 0);
+
+ _exit(0);
+ }
+
+ /* wait for D to tell us it is ready for us to exit */
+ rc = read(pipe_db[0], &buffer, 1);
+ assert(rc == 1);
+
+ /* now we exit so that C gets a signal */
+ _exit(0);
+ }
+
+ /* process A */
+
+ /* wait for C to tell us the test passed */
+ rc = read(pipe_ca[0], &buffer, 1);
+ ATF_CHECK_EQ(1, rc);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, arg_validation);
+ ATF_TP_ADD_TC(tp, fork_no_inherit);
+ ATF_TP_ADD_TC(tp, exec_inherit);
+ ATF_TP_ADD_TC(tp, signal_delivered);
+ ATF_TP_ADD_TC(tp, signal_delivered_ptrace);
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/pdeathsig_helper.c b/tests/sys/kern/pdeathsig_helper.c
new file mode 100644
index 000000000000..27d286a10268
--- /dev/null
+++ b/tests/sys/kern/pdeathsig_helper.c
@@ -0,0 +1,48 @@
+/*-
+ * Copyright (c) 2018 Thomas Munro
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <assert.h>
+#include <signal.h>
+#include <sys/procctl.h>
+
+int main(int argc, char **argv)
+{
+ int signum;
+ int rc;
+
+ /*
+ * This program is executed by the pdeathsig test
+ * to check if the PROC_PDEATHSIG_CTL setting was
+ * inherited.
+ */
+ signum = 0xdeadbeef;
+ rc = procctl(P_PID, 0, PROC_PDEATHSIG_STATUS, &signum);
+ assert(rc == 0);
+ assert(signum == SIGINFO);
+
+ return 0;
+}
diff --git a/tests/sys/kern/pipe/Makefile b/tests/sys/kern/pipe/Makefile
new file mode 100644
index 000000000000..e8fa05ee8e0e
--- /dev/null
+++ b/tests/sys/kern/pipe/Makefile
@@ -0,0 +1,13 @@
+TESTSDIR= ${TESTSBASE}/sys/kern/pipe
+
+PLAIN_TESTS_C+= big_pipe_test
+PLAIN_TESTS_C+= pipe_fstat_bug_test
+PLAIN_TESTS_C+= pipe_ino_test
+ATF_TESTS_C+= pipe_kqueue_test
+PLAIN_TESTS_C+= pipe_overcommit1_test
+PLAIN_TESTS_C+= pipe_overcommit2_test
+PLAIN_TESTS_C+= pipe_reverse2_test
+PLAIN_TESTS_C+= pipe_reverse_test
+PLAIN_TESTS_C+= pipe_wraparound_test
+
+.include <bsd.test.mk>
diff --git a/tests/sys/kern/pipe/Makefile.depend b/tests/sys/kern/pipe/Makefile.depend
new file mode 100644
index 000000000000..84b8ddd67e34
--- /dev/null
+++ b/tests/sys/kern/pipe/Makefile.depend
@@ -0,0 +1,16 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/kern/pipe/big_pipe_test.c b/tests/sys/kern/pipe/big_pipe_test.c
new file mode 100644
index 000000000000..b092810a0aa5
--- /dev/null
+++ b/tests/sys/kern/pipe/big_pipe_test.c
@@ -0,0 +1,86 @@
+#include <sys/select.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define BIG_PIPE_SIZE 64*1024 /* From sys/pipe.h */
+
+/*
+ * Test for the non-blocking big pipe bug (write(2) returning
+ * EAGAIN while select(2) returns the descriptor as ready for write).
+ */
+
+static void
+write_frame(int fd, char *buf, unsigned long buflen)
+{
+ fd_set wfd;
+ int i;
+
+ while (buflen) {
+ FD_ZERO(&wfd);
+ FD_SET(fd, &wfd);
+ i = select(fd+1, NULL, &wfd, NULL, NULL);
+ if (i < 0)
+ err(1, "select failed");
+ if (i != 1) {
+ errx(1, "select returned unexpected value %d\n", i);
+ exit(1);
+ }
+ i = write(fd, buf, buflen);
+ if (i < 0) {
+ if (errno != EAGAIN)
+ warn("write failed");
+ exit(1);
+ }
+ buf += i;
+ buflen -= i;
+ }
+}
+
+int
+main(void)
+{
+ /* any value over PIPE_SIZE should do */
+ char buf[BIG_PIPE_SIZE];
+ int i, flags, fd[2];
+
+ if (pipe(fd) < 0)
+ errx(1, "pipe failed");
+
+ flags = fcntl(fd[1], F_GETFL);
+ if (flags == -1 || fcntl(fd[1], F_SETFL, flags|O_NONBLOCK) == -1) {
+ printf("fcntl failed: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ switch (fork()) {
+ case -1:
+ err(1, "fork failed: %s\n", strerror(errno));
+ break;
+ case 0:
+ close(fd[1]);
+ for (;;) {
+ /* Any small size should do */
+ i = read(fd[0], buf, 256);
+ if (i == 0)
+ break;
+ if (i < 0)
+ err(1, "read");
+ }
+ exit(0);
+ default:
+ break;
+ }
+
+ close(fd[0]);
+ memset(buf, 0, sizeof buf);
+ for (i = 0; i < 1000; i++)
+ write_frame(fd[1], buf, sizeof buf);
+
+ printf("ok\n");
+ exit(0);
+}
diff --git a/tests/sys/kern/pipe/pipe_fstat_bug_test.c b/tests/sys/kern/pipe/pipe_fstat_bug_test.c
new file mode 100644
index 000000000000..fec7797a55dc
--- /dev/null
+++ b/tests/sys/kern/pipe/pipe_fstat_bug_test.c
@@ -0,0 +1,155 @@
+/*
+Copyright (C) 2004 Michael J. Silbersack. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/event.h>
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/*
+ * The goal of this program is to see if fstat reports the correct
+ * data count for a pipe. Prior to revision 1.172 of sys_pipe.c,
+ * 0 would be returned once the pipe entered direct write mode.
+ *
+ * Linux (2.6) always returns zero, so it's not a valuable platform
+ * for comparison.
+ */
+
+int
+main(void)
+{
+ char buffer[32768], buffer2[32768], go[] = "go", go2[] = "go2";
+ int desc[2], ipc_coord[2];
+ struct kevent event, ke;
+ ssize_t error;
+ int successes = 0;
+ struct stat status;
+ pid_t new_pid;
+ int kq;
+
+ error = pipe(desc);
+ if (error == -1)
+ err(1, "Couldn't allocate data pipe");
+
+ error = pipe(ipc_coord);
+ if (error == -1)
+ err(1, "Couldn't allocate IPC coordination pipe");
+
+ new_pid = fork();
+ assert(new_pid != -1);
+
+ close(new_pid == 0 ? desc[0] : desc[1]);
+
+#define SYNC_R(i, _buf) do { \
+ int _error = errno; \
+ warnx("%d: waiting for synchronization", __LINE__); \
+ if (read(ipc_coord[i], &_buf, sizeof(_buf)) != sizeof(_buf)) \
+ err(1, "failed to synchronize (%s)", (i == 0 ? "parent" : "child")); \
+ errno = _error; \
+ } while(0)
+
+#define SYNC_W(i, _buf) do { \
+ int _error = errno; \
+ warnx("%d: sending synchronization", __LINE__); \
+ if (write(ipc_coord[i], &_buf, sizeof(_buf)) != sizeof(_buf)) \
+ err(1, "failed to synchronize (%s)", (i == 0 ? "child" : "parent")); \
+ errno = _error; \
+ } while(0)
+
+#define WRITE(s) do { \
+ ssize_t _size; \
+ if ((_size = write(desc[1], &buffer, s)) != s) \
+ warn("short write; wrote %zd, expected %d", _size, s); \
+ } while(0)
+
+ if (new_pid == 0) {
+
+ SYNC_R(0, go);
+ WRITE(145);
+ SYNC_W(0, go2);
+
+ SYNC_R(0, go);
+ WRITE(2048);
+ SYNC_W(0, go2);
+
+ SYNC_R(0, go);
+ WRITE(4096);
+ SYNC_W(0, go2);
+
+ SYNC_R(0, go);
+ WRITE(8191);
+ SYNC_W(0, go2);
+
+ SYNC_R(0, go);
+ SYNC_W(0, go2); /* XXX: why is this required? */
+ WRITE(8192);
+ SYNC_W(0, go2);
+
+ close(ipc_coord[0]);
+ close(ipc_coord[1]);
+
+ _exit(0);
+ }
+
+ kq = kqueue();
+ if (kq == -1)
+ _exit(1);
+
+ EV_SET(&ke, desc[0], EVFILT_READ, EV_ADD, 0, 0, NULL);
+
+ /* Attach event to the kqueue. */
+ if (kevent(kq, &ke, 1, NULL, 0, NULL) != 0)
+ _exit(2);
+
+ while (successes < 5) {
+ SYNC_W(1, go);
+ SYNC_R(1, go2);
+
+ /* Ensure data is available to read */
+ if (kevent(kq, NULL, 0, &event, 1, NULL) != 1)
+ _exit(3);
+
+ fstat(desc[0], &status);
+ error = read(desc[0], &buffer2, sizeof(buffer2));
+
+ if (status.st_size != error)
+ err(1, "FAILURE: stat size %jd read size %zd",
+ (intmax_t)status.st_size, error);
+ if (error > 0) {
+ printf("SUCCESS at stat size %jd read size %zd\n",
+ (intmax_t)status.st_size, error);
+ successes++;
+ }
+ }
+
+ exit(0);
+}
diff --git a/tests/sys/kern/pipe/pipe_ino_test.c b/tests/sys/kern/pipe/pipe_ino_test.c
new file mode 100644
index 000000000000..08efbc37b189
--- /dev/null
+++ b/tests/sys/kern/pipe/pipe_ino_test.c
@@ -0,0 +1,66 @@
+/*-
+ * Copyright (c) 2011 Giovanni Trematerra <giovanni.trematerra@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test conformance to stat(2) SUSv4 description:
+ * "For all other file types defined in this volume of POSIX.1-2008, the
+ * structure members st_mode, st_ino, st_dev, st_uid, st_gid, st_atim,
+ * st_ctim, and st_mtim shall have meaningful values ...".
+ * Check that st_dev and st_ino are meaningful.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <err.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int
+main(void)
+{
+ int pipefd[2];
+ struct stat st1, st2;
+
+ if (pipe(pipefd) == -1)
+ err(1, "FAIL: pipe");
+
+ if (fstat(pipefd[0], &st1) == -1)
+ err(1, "FAIL: fstat st1");
+ if (fstat(pipefd[1], &st2) == -1)
+ err(1, "FAIL: fstat st2");
+ if (st1.st_dev != st2.st_dev || st1.st_dev == 0 || st2.st_dev == 0)
+ errx(1, "FAIL: wrong dev number %ju %ju",
+ (uintmax_t)st1.st_dev, (uintmax_t)st2.st_dev);
+ if (st1.st_ino == st2.st_ino)
+ errx(1, "FAIL: inode numbers are equal: %ju",
+ (uintmax_t)st1.st_ino);
+
+ close(pipefd[0]);
+ close(pipefd[1]);
+ printf("PASS\n");
+
+ return (0);
+}
diff --git a/tests/sys/kern/pipe/pipe_kqueue_test.c b/tests/sys/kern/pipe/pipe_kqueue_test.c
new file mode 100644
index 000000000000..ee2e9ce68c7a
--- /dev/null
+++ b/tests/sys/kern/pipe/pipe_kqueue_test.c
@@ -0,0 +1,364 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2020 Jan Kokemüller
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/event.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(pipe_kqueue__write_end);
+ATF_TC_BODY(pipe_kqueue__write_end, tc)
+{
+ int p[2] = { -1, -1 };
+
+ ATF_REQUIRE(pipe2(p, O_CLOEXEC | O_NONBLOCK) == 0);
+ ATF_REQUIRE(p[0] >= 0);
+ ATF_REQUIRE(p[1] >= 0);
+
+ int kq = kqueue();
+ ATF_REQUIRE(kq >= 0);
+
+ struct kevent kev[32];
+ EV_SET(&kev[0], p[1], EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0, 0);
+
+ ATF_REQUIRE(kevent(kq, kev, 1, NULL, 0, NULL) == 0);
+
+ /* Test that EVFILT_WRITE behaves sensibly on the write end. */
+
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev),
+ &(struct timespec) { 0, 0 }) == 1);
+ ATF_REQUIRE(kev[0].ident == (uintptr_t)p[1]);
+ ATF_REQUIRE(kev[0].filter == EVFILT_WRITE);
+ ATF_REQUIRE(kev[0].flags == EV_CLEAR);
+ ATF_REQUIRE(kev[0].fflags == 0);
+ ATF_REQUIRE(kev[0].data == 16384);
+ ATF_REQUIRE(kev[0].udata == 0);
+
+ /* Filling up the pipe should make the EVFILT_WRITE disappear. */
+
+ char c = 0;
+ ssize_t r;
+ while ((r = write(p[1], &c, 1)) == 1) {
+ }
+ ATF_REQUIRE(r < 0);
+ ATF_REQUIRE(errno == EAGAIN || errno == EWOULDBLOCK);
+
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev),
+ &(struct timespec) { 0, 0 }) == 0);
+
+ /* Reading (PIPE_BUF - 1) bytes will not trigger a EVFILT_WRITE yet. */
+
+ for (int i = 0; i < PIPE_BUF - 1; ++i) {
+ ATF_REQUIRE(read(p[0], &c, 1) == 1);
+ }
+
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev),
+ &(struct timespec) { 0, 0 }) == 0);
+
+ /* Reading one additional byte triggers the EVFILT_WRITE. */
+
+ ATF_REQUIRE(read(p[0], &c, 1) == 1);
+
+ r = kevent(kq, NULL, 0, kev, nitems(kev), &(struct timespec) { 0, 0 });
+ ATF_REQUIRE(r == 1);
+ ATF_REQUIRE(kev[0].ident == (uintptr_t)p[1]);
+ ATF_REQUIRE(kev[0].filter == EVFILT_WRITE);
+ ATF_REQUIRE(kev[0].flags == EV_CLEAR);
+ ATF_REQUIRE(kev[0].fflags == 0);
+ ATF_REQUIRE(kev[0].data == PIPE_BUF);
+ ATF_REQUIRE(kev[0].udata == 0);
+
+ /*
+ * Reading another byte triggers the EVFILT_WRITE again with a changed
+ * 'data' field.
+ */
+
+ ATF_REQUIRE(read(p[0], &c, 1) == 1);
+
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev),
+ &(struct timespec) { 0, 0 }) == 1);
+ ATF_REQUIRE(kev[0].ident == (uintptr_t)p[1]);
+ ATF_REQUIRE(kev[0].filter == EVFILT_WRITE);
+ ATF_REQUIRE(kev[0].flags == EV_CLEAR);
+ ATF_REQUIRE(kev[0].fflags == 0);
+ ATF_REQUIRE(kev[0].data == PIPE_BUF + 1);
+ ATF_REQUIRE(kev[0].udata == 0);
+
+ /*
+ * Closing the read end should make a EV_EOF appear but leave the 'data'
+ * field unchanged.
+ */
+
+ ATF_REQUIRE(close(p[0]) == 0);
+
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev),
+ &(struct timespec) { 0, 0 }) == 1);
+ ATF_REQUIRE(kev[0].ident == (uintptr_t)p[1]);
+ ATF_REQUIRE(kev[0].filter == EVFILT_WRITE);
+ ATF_REQUIRE(kev[0].flags == (EV_CLEAR | EV_EOF | EV_ONESHOT));
+ ATF_REQUIRE(kev[0].fflags == 0);
+ ATF_REQUIRE(kev[0].data == PIPE_BUF + 1);
+ ATF_REQUIRE(kev[0].udata == 0);
+
+ ATF_REQUIRE(close(kq) == 0);
+ ATF_REQUIRE(close(p[1]) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(pipe_kqueue__closed_read_end);
+ATF_TC_BODY(pipe_kqueue__closed_read_end, tc)
+{
+ int p[2] = { -1, -1 };
+
+ ATF_REQUIRE(pipe2(p, O_CLOEXEC | O_NONBLOCK) == 0);
+ ATF_REQUIRE(p[0] >= 0);
+ ATF_REQUIRE(p[1] >= 0);
+
+ ATF_REQUIRE(close(p[0]) == 0);
+
+ int kq = kqueue();
+ ATF_REQUIRE(kq >= 0);
+
+ struct kevent kev[32];
+ EV_SET(&kev[0], p[1], EVFILT_READ, EV_ADD | EV_CLEAR | EV_RECEIPT, /**/
+ 0, 0, 0);
+ EV_SET(&kev[1], p[1], EVFILT_WRITE, EV_ADD | EV_CLEAR | EV_RECEIPT, /**/
+ 0, 0, 0);
+
+ /*
+ * Trying to register EVFILT_WRITE when the pipe is closed leads to an
+ * EPIPE error.
+ */
+
+ ATF_REQUIRE(kevent(kq, kev, 2, kev, 2, NULL) == 2);
+ ATF_REQUIRE((kev[0].flags & EV_ERROR) != 0);
+ ATF_REQUIRE(kev[0].data == 0);
+ ATF_REQUIRE((kev[1].flags & EV_ERROR) != 0);
+ ATF_REQUIRE(kev[1].data == EPIPE);
+
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev),
+ &(struct timespec) { 0, 0 }) == 1);
+ ATF_REQUIRE(kev[0].ident == (uintptr_t)p[1]);
+ ATF_REQUIRE(kev[0].filter == EVFILT_READ);
+ ATF_REQUIRE(kev[0].flags == (EV_EOF | EV_CLEAR | EV_RECEIPT));
+ ATF_REQUIRE(kev[0].fflags == 0);
+ ATF_REQUIRE(kev[0].data == 0);
+ ATF_REQUIRE(kev[0].udata == 0);
+
+ ATF_REQUIRE(close(kq) == 0);
+ ATF_REQUIRE(close(p[1]) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(pipe_kqueue__closed_read_end_register_before_close);
+ATF_TC_BODY(pipe_kqueue__closed_read_end_register_before_close, tc)
+{
+ int p[2] = { -1, -1 };
+
+ ATF_REQUIRE(pipe2(p, O_CLOEXEC | O_NONBLOCK) == 0);
+ ATF_REQUIRE(p[0] >= 0);
+ ATF_REQUIRE(p[1] >= 0);
+
+ int kq = kqueue();
+ ATF_REQUIRE(kq >= 0);
+
+ struct kevent kev[32];
+ EV_SET(&kev[0], p[1], EVFILT_READ, EV_ADD | EV_CLEAR | EV_RECEIPT, /**/
+ 0, 0, 0);
+ EV_SET(&kev[1], p[1], EVFILT_WRITE, EV_ADD | EV_CLEAR | EV_RECEIPT, /**/
+ 0, 0, 0);
+
+ /*
+ * Registering EVFILT_WRITE before the pipe is closed leads to a
+ * EVFILT_WRITE event with EV_EOF set.
+ */
+
+ ATF_REQUIRE(kevent(kq, kev, 2, kev, 2, NULL) == 2);
+ ATF_REQUIRE((kev[0].flags & EV_ERROR) != 0);
+ ATF_REQUIRE(kev[0].data == 0);
+ ATF_REQUIRE((kev[1].flags & EV_ERROR) != 0);
+ ATF_REQUIRE(kev[1].data == 0);
+
+ ATF_REQUIRE(close(p[0]) == 0);
+
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev),
+ &(struct timespec) { 0, 0 }) == 2);
+ {
+ ATF_REQUIRE(kev[0].ident == (uintptr_t)p[1]);
+ ATF_REQUIRE(kev[0].filter == EVFILT_WRITE);
+ ATF_REQUIRE(kev[0].flags ==
+ (EV_EOF | EV_CLEAR | EV_ONESHOT | EV_RECEIPT));
+ ATF_REQUIRE(kev[0].fflags == 0);
+ ATF_REQUIRE(kev[0].data == 16384);
+ ATF_REQUIRE(kev[0].udata == 0);
+ }
+ {
+ ATF_REQUIRE(kev[1].ident == (uintptr_t)p[1]);
+ ATF_REQUIRE(kev[1].filter == EVFILT_READ);
+ ATF_REQUIRE(kev[1].flags == (EV_EOF | EV_CLEAR | EV_RECEIPT));
+ ATF_REQUIRE(kev[1].fflags == 0);
+ ATF_REQUIRE(kev[1].data == 0);
+ ATF_REQUIRE(kev[1].udata == 0);
+ }
+
+ ATF_REQUIRE(close(kq) == 0);
+ ATF_REQUIRE(close(p[1]) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(pipe_kqueue__closed_write_end);
+ATF_TC_BODY(pipe_kqueue__closed_write_end, tc)
+{
+ struct kevent kev[32];
+ ssize_t bytes, n;
+ int kq, p[2];
+ char c;
+
+ ATF_REQUIRE(pipe2(p, O_CLOEXEC | O_NONBLOCK) == 0);
+ ATF_REQUIRE(p[0] >= 0);
+ ATF_REQUIRE(p[1] >= 0);
+
+ bytes = 0;
+ c = 0;
+ while ((n = write(p[1], &c, 1)) == 1)
+ bytes++;
+ ATF_REQUIRE(n < 0);
+ ATF_REQUIRE(errno == EAGAIN || errno == EWOULDBLOCK);
+
+ ATF_REQUIRE(close(p[1]) == 0);
+
+ kq = kqueue();
+ ATF_REQUIRE(kq >= 0);
+
+ EV_SET(&kev[0], p[0], EVFILT_READ, EV_ADD | EV_CLEAR | EV_RECEIPT,
+ 0, 0, 0);
+ EV_SET(&kev[1], p[0], EVFILT_WRITE, EV_ADD | EV_CLEAR | EV_RECEIPT,
+ 0, 0, 0);
+
+ /*
+ * Trying to register EVFILT_WRITE when the pipe is closed leads to an
+ * EPIPE error.
+ */
+
+ ATF_REQUIRE(kevent(kq, kev, 2, kev, 2, NULL) == 2);
+ ATF_REQUIRE((kev[0].flags & EV_ERROR) != 0);
+ ATF_REQUIRE(kev[0].data == 0);
+ ATF_REQUIRE((kev[1].flags & EV_ERROR) != 0);
+ ATF_REQUIRE(kev[1].data == EPIPE);
+
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev),
+ &(struct timespec) { 0, 0 }) == 1);
+ ATF_REQUIRE(kev[0].ident == (uintptr_t)p[0]);
+ ATF_REQUIRE(kev[0].filter == EVFILT_READ);
+ ATF_REQUIRE(kev[0].flags == (EV_EOF | EV_CLEAR | EV_RECEIPT));
+ ATF_REQUIRE(kev[0].fflags == 0);
+ ATF_REQUIRE(kev[0].data == bytes);
+ ATF_REQUIRE(kev[0].udata == 0);
+
+ ATF_REQUIRE(close(kq) == 0);
+ ATF_REQUIRE(close(p[0]) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(pipe_kqueue__closed_write_end_register_before_close);
+ATF_TC_BODY(pipe_kqueue__closed_write_end_register_before_close, tc)
+{
+ struct kevent kev[32];
+ ssize_t bytes, n;
+ int kq, p[2];
+ char c;
+
+ ATF_REQUIRE(pipe2(p, O_CLOEXEC | O_NONBLOCK) == 0);
+ ATF_REQUIRE(p[0] >= 0);
+ ATF_REQUIRE(p[1] >= 0);
+
+ kq = kqueue();
+ ATF_REQUIRE(kq >= 0);
+
+ EV_SET(&kev[0], p[0], EVFILT_READ, EV_ADD | EV_CLEAR | EV_RECEIPT,
+ 0, 0, 0);
+ EV_SET(&kev[1], p[0], EVFILT_WRITE, EV_ADD | EV_CLEAR | EV_RECEIPT,
+ 0, 0, 0);
+
+ /*
+ * Registering EVFILT_WRITE before the pipe is closed leads to a
+ * EVFILT_WRITE event with EV_EOF set.
+ */
+
+ ATF_REQUIRE(kevent(kq, kev, 2, kev, 2, NULL) == 2);
+ ATF_REQUIRE((kev[0].flags & EV_ERROR) != 0);
+ ATF_REQUIRE(kev[0].data == 0);
+ ATF_REQUIRE((kev[1].flags & EV_ERROR) != 0);
+ ATF_REQUIRE(kev[1].data == 0);
+
+ bytes = 0;
+ c = 0;
+ while ((n = write(p[1], &c, 1)) == 1)
+ bytes++;
+ ATF_REQUIRE(n < 0);
+ ATF_REQUIRE(errno == EAGAIN || errno == EWOULDBLOCK);
+
+ ATF_REQUIRE(close(p[1]) == 0);
+
+ ATF_REQUIRE(kevent(kq, NULL, 0, kev, nitems(kev),
+ &(struct timespec){ 0, 0 }) == 2);
+
+ ATF_REQUIRE(kev[0].ident == (uintptr_t)p[0]);
+ ATF_REQUIRE(kev[0].filter == EVFILT_WRITE);
+ ATF_REQUIRE(kev[0].flags ==
+ (EV_EOF | EV_CLEAR | EV_ONESHOT | EV_RECEIPT));
+ ATF_REQUIRE(kev[0].fflags == 0);
+ ATF_REQUIRE(kev[0].data > 0);
+ ATF_REQUIRE(kev[0].udata == 0);
+
+ ATF_REQUIRE(kev[1].ident == (uintptr_t)p[0]);
+ ATF_REQUIRE(kev[1].filter == EVFILT_READ);
+ ATF_REQUIRE(kev[1].flags == (EV_EOF | EV_CLEAR | EV_RECEIPT));
+ ATF_REQUIRE(kev[1].fflags == 0);
+ ATF_REQUIRE(kev[1].data == bytes);
+ ATF_REQUIRE(kev[1].udata == 0);
+
+ ATF_REQUIRE(close(kq) == 0);
+ ATF_REQUIRE(close(p[0]) == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, pipe_kqueue__write_end);
+ ATF_TP_ADD_TC(tp, pipe_kqueue__closed_read_end);
+ ATF_TP_ADD_TC(tp, pipe_kqueue__closed_read_end_register_before_close);
+ ATF_TP_ADD_TC(tp, pipe_kqueue__closed_write_end);
+ ATF_TP_ADD_TC(tp, pipe_kqueue__closed_write_end_register_before_close);
+
+ return atf_no_error();
+}
diff --git a/tests/sys/kern/pipe/pipe_overcommit1_test.c b/tests/sys/kern/pipe/pipe_overcommit1_test.c
new file mode 100644
index 000000000000..eac414bfdd2f
--- /dev/null
+++ b/tests/sys/kern/pipe/pipe_overcommit1_test.c
@@ -0,0 +1,50 @@
+/*-
+ * Copyright (C) 2005 Michael J. Silbersack <silby@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice(s), this list of conditions and the following disclaimer as
+ * the first lines of this file unmodified other than the possible
+ * addition of one or more copyright notices.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice(s), this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/*
+ * This program just allocates as many pipes as it can to ensure
+ * that using up all pipe memory doesn't cause a panic.
+ */
+
+int
+main(void)
+{
+ int pipes[10000];
+ unsigned int i;
+
+ for (i = 0; i < nitems(pipes); i++)
+ (void)pipe(&pipes[i]);
+ printf("PASS\n");
+
+ exit(0);
+}
diff --git a/tests/sys/kern/pipe/pipe_overcommit2_test.c b/tests/sys/kern/pipe/pipe_overcommit2_test.c
new file mode 100644
index 000000000000..d88d32c32575
--- /dev/null
+++ b/tests/sys/kern/pipe/pipe_overcommit2_test.c
@@ -0,0 +1,82 @@
+/*-
+ * Copyright (C) 2005 Michael J. Silbersack <silby@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice(s), this list of conditions and the following disclaimer as
+ * the first lines of this file unmodified other than the possible
+ * addition of one or more copyright notices.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice(s), this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/*
+ * This program tests how sys_pipe.c handles the case where there
+ * is ample memory to allocate a pipe, but the file descriptor
+ * limit for that user has been exceeded.
+ */
+
+int
+main(void)
+{
+ char template[] = "pipe.XXXXXXXXXX";
+ int lastfd, pipes[10000], returnval;
+ unsigned int i;
+
+ lastfd = -1;
+
+ if (mkstemp(template) == -1)
+ err(1, "mkstemp failed");
+
+ for (i = 0; i < nitems(pipes); i++) {
+ returnval = open(template, O_RDONLY);
+ if (returnval == -1 && (errno == ENFILE || errno == EMFILE))
+ break; /* All descriptors exhausted. */
+ else
+ lastfd = returnval;
+ }
+
+ /* First falloc failure case in sys_pipe.c:pipe() */
+ for (i = 0; i < 1000; i++) {
+ returnval = pipe(&pipes[i]);
+ }
+
+ /*
+ * Free just one FD so that the second falloc failure
+ * case will occur.
+ */
+ close(lastfd);
+
+ for (i = 0; i < 1000; i++) {
+ returnval = pipe(&pipes[i]);
+ }
+ printf("PASS\n");
+
+ unlink(template);
+
+ exit(0);
+}
diff --git a/tests/sys/kern/pipe/pipe_reverse2_test.c b/tests/sys/kern/pipe/pipe_reverse2_test.c
new file mode 100644
index 000000000000..0f7099925161
--- /dev/null
+++ b/tests/sys/kern/pipe/pipe_reverse2_test.c
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 2010 Jilles Tjoelker
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/select.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <unistd.h>
+
+/*
+ * Check that pipes can be selected for writing in the reverse direction.
+ */
+int
+main(void)
+{
+ int pip[2];
+ fd_set set;
+ int n;
+
+ if (pipe(pip) == -1)
+ err(1, "FAIL: pipe");
+
+ FD_ZERO(&set);
+ FD_SET(pip[0], &set);
+ n = select(pip[1] + 1, NULL, &set, NULL, &(struct timeval){ 0, 0 });
+ if (n != 1)
+ errx(1, "FAIL: select initial reverse direction");
+
+ n = write(pip[0], "x", 1);
+ if (n != 1)
+ err(1, "FAIL: write reverse direction");
+
+ FD_ZERO(&set);
+ FD_SET(pip[0], &set);
+ n = select(pip[1] + 1, NULL, &set, NULL, &(struct timeval){ 0, 0 });
+ if (n != 1)
+ errx(1, "FAIL: select reverse direction after write");
+
+ printf("PASS\n");
+
+ return (0);
+}
diff --git a/tests/sys/kern/pipe/pipe_reverse_test.c b/tests/sys/kern/pipe/pipe_reverse_test.c
new file mode 100644
index 000000000000..0ca481558765
--- /dev/null
+++ b/tests/sys/kern/pipe/pipe_reverse_test.c
@@ -0,0 +1,148 @@
+/*
+Copyright (C) 2004 Michael J. Silbersack. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * This program simply tests writing through the reverse direction of
+ * a pipe. Nothing too fancy, it's only needed because most pipe-using
+ * programs never touch the reverse direction (it doesn't exist on
+ * Linux.)
+ */
+
+int
+main(void)
+{
+ char buffer[65535], buffer2[65535], go[] = "go", go2[] = "go2";
+ int desc[2], ipc_coord[2];
+ size_t i;
+ ssize_t total;
+ int buggy, error;
+ pid_t new_pid;
+
+ buggy = 0;
+ total = 0;
+
+ error = pipe(desc);
+ if (error == -1)
+ err(1, "Couldn't allocate data pipe");
+
+ error = pipe(ipc_coord);
+ if (error == -1)
+ err(1, "Couldn't allocate IPC coordination pipe");
+
+ buffer[0] = 'A';
+
+ for (i = 1; i < (int)sizeof(buffer); i++) {
+ buffer[i] = buffer[i - 1] + 1;
+ if (buffer[i] > 'Z')
+ buffer[i] = 'A';
+ }
+
+ new_pid = fork();
+ assert(new_pid != -1);
+
+#define SYNC_R(i, _buf) do { \
+ int _error = errno; \
+ warnx("%d: waiting for synchronization", __LINE__); \
+ if (read(ipc_coord[i], &_buf, sizeof(_buf)) != sizeof(_buf)) \
+ err(1, "failed to synchronize (%s)", (i == 0 ? "parent" : "child")); \
+ errno = _error; \
+ } while(0)
+
+#define SYNC_W(i, _buf) do { \
+ int _error = errno; \
+ warnx("%d: sending synchronization", __LINE__); \
+ if (write(ipc_coord[i], &_buf, sizeof(_buf)) != sizeof(_buf)) \
+ err(1, "failed to synchronize (%s)", (i == 0 ? "child" : "parent")); \
+ errno = _error; \
+ } while(0)
+
+#define WRITE(s) do { \
+ ssize_t _size; \
+ if ((_size = write(desc[1], &buffer[total], s)) != s) \
+ warn("short write; wrote %zd, expected %d", _size, s); \
+ total += _size; \
+ } while(0)
+
+ if (new_pid == 0) {
+ SYNC_R(0, go);
+ for (i = 0; i < 8; i++)
+ WRITE(4096);
+
+ SYNC_W(0, go2);
+ SYNC_R(0, go);
+
+ for (i = 0; i < 2; i++)
+ WRITE(4096);
+
+ SYNC_W(0, go2);
+
+ _exit(0);
+ }
+
+ SYNC_W(1, go);
+ SYNC_R(1, go2);
+
+ error = read(desc[0], &buffer2, 8 * 4096);
+ total += error;
+ printf("Read %d bytes\n", error);
+
+ SYNC_W(1, go);
+ SYNC_R(1, go2);
+
+ error = read(desc[0], &buffer2[total], 2 * 4096);
+ total += error;
+ printf("Read %d bytes, done\n", error);
+
+ if (memcmp(buffer, buffer2, total) != 0) {
+ for (i = 0; i < (size_t)total; i++) {
+ if (buffer[i] != buffer2[i]) {
+ buggy = 1;
+ printf("Location %zu input: %hhx "
+ "output: %hhx\n",
+ i, buffer[i], buffer2[i]);
+ }
+ }
+ }
+
+ waitpid(new_pid, NULL, 0);
+
+ if ((buggy == 1) || (total != 10 * 4096))
+ errx(1, "FAILED");
+ else
+ printf("SUCCESS\n");
+
+ exit(0);
+}
diff --git a/tests/sys/kern/pipe/pipe_wraparound_test.c b/tests/sys/kern/pipe/pipe_wraparound_test.c
new file mode 100644
index 000000000000..178f0fd2d0eb
--- /dev/null
+++ b/tests/sys/kern/pipe/pipe_wraparound_test.c
@@ -0,0 +1,139 @@
+/*
+Copyright (C) 2004 Michael J. Silbersack. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * This program tests to make sure that wraparound writes and reads
+ * are working, assuming that 16K socket buffers are used. In order
+ * to really stress the pipe code with this test, kernel modifications
+ * nay be necessary.
+ */
+
+int main (void)
+{
+ char buffer[32768], buffer2[32768], go[] = "go", go2[] = "go2";
+ int desc[2], ipc_coord[2];
+ ssize_t error, total;
+ int buggy, i;
+ pid_t new_pid;
+
+ buggy = 0;
+ total = 0;
+
+ error = pipe(desc);
+ if (error == -1)
+ err(1, "Couldn't allocate data pipe");
+
+ error = pipe(ipc_coord);
+ if (error == -1)
+ err(1, "Couldn't allocate IPC coordination pipe");
+
+ buffer[0] = 'A';
+
+ for (i = 1; i < (int)sizeof(buffer); i++) {
+ buffer[i] = buffer[i - 1] + 1;
+ if (buffer[i] > 'Z')
+ buffer[i] = 'A';
+ }
+
+ new_pid = fork();
+ assert(new_pid != -1);
+
+#define SYNC_R(i, _buf) do { \
+ int _error = errno; \
+ warnx("%d: waiting for synchronization", __LINE__); \
+ if (read(ipc_coord[i], &_buf, sizeof(_buf)) != sizeof(_buf)) \
+ err(1, "failed to synchronize (%s)", (i == 0 ? "parent" : "child")); \
+ errno = _error; \
+ } while(0)
+
+#define SYNC_W(i, _buf) do { \
+ int _error = errno; \
+ warnx("%d: sending synchronization", __LINE__); \
+ if (write(ipc_coord[i], &_buf, sizeof(_buf)) != sizeof(_buf)) \
+ err(1, "failed to synchronize (%s)", (i == 0 ? "child" : "parent")); \
+ errno = _error; \
+ } while(0)
+
+#define WRITE(s) do { \
+ ssize_t _size; \
+ if ((_size = write(desc[1], &buffer[total], s)) != s) \
+ warn("short write; wrote %zd, expected %d", _size, s); \
+ total += _size; \
+ } while(0)
+
+ if (new_pid == 0) {
+ WRITE(4096);
+ WRITE(4096);
+ WRITE(4000);
+ SYNC_W(0, go2);
+
+ SYNC_R(0, go);
+ WRITE(3000);
+ WRITE(3000);
+ SYNC_W(0, go2);
+
+ _exit(0);
+ }
+
+ SYNC_R(1, go2);
+ error = read(desc[0], &buffer2, 8192);
+ total += error;
+ printf("Read %zd bytes\n", error);
+ SYNC_W(1, go);
+ SYNC_R(1, go2);
+ error = read(desc[0], &buffer2[total], 16384);
+ total += error;
+ printf("Read %zd bytes, done\n", error);
+
+ if (memcmp(buffer, buffer2, total) != 0) {
+ for (i = 0; i < total; i++) {
+ if (buffer[i] != buffer2[i]) {
+ buggy = 1;
+ printf("Location %d input: %hhx output: %hhx\n",
+ i, buffer[i], buffer2[i]);
+ }
+ }
+ }
+
+ waitpid(new_pid, NULL, 0);
+
+ if (buggy)
+ errx(1, "FAILURE");
+
+ printf("SUCCESS\n");
+
+ exit(0);
+}
diff --git a/tests/sys/kern/prace.c b/tests/sys/kern/prace.c
new file mode 100644
index 000000000000..e6aa09ec2180
--- /dev/null
+++ b/tests/sys/kern/prace.c
@@ -0,0 +1,144 @@
+/*-
+ * Copyright (c) 2024 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * These tests demonstrate a bug in ppoll() and pselect() where a blocked
+ * signal can fire after the timer runs out but before the signal mask is
+ * restored. To do this, we fork a child process which installs a SIGINT
+ * handler and repeatedly calls either ppoll() or pselect() with a 1 ms
+ * timeout, while the parent repeatedly sends SIGINT to the child at
+ * intervals that start out at 1100 us and gradually decrease to 900 us.
+ * Each SIGINT resynchronizes parent and child, and sooner or later the
+ * parent hits the sweet spot and the SIGINT arrives at just the right
+ * time to demonstrate the bug.
+ */
+
+#include <sys/select.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <errno.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+static volatile sig_atomic_t caught[NSIG];
+
+static void
+handler(int signo)
+{
+ caught[signo]++;
+}
+
+static void
+child(int rd, bool poll)
+{
+ struct timespec timeout = { .tv_nsec = 1000000 };
+ sigset_t set0, set1;
+ int ret;
+
+ /* empty mask for ppoll() / pselect() */
+ sigemptyset(&set0);
+
+ /* block SIGINT, then install a handler for it */
+ sigemptyset(&set1);
+ sigaddset(&set1, SIGINT);
+ sigprocmask(SIG_BLOCK, &set1, NULL);
+ signal(SIGINT, handler);
+
+ /* signal parent that we are ready */
+ close(rd);
+ for (;;) {
+ /* sleep for 1 ms with signals unblocked */
+ ret = poll ? ppoll(NULL, 0, &timeout, &set0) :
+ pselect(0, NULL, NULL, NULL, &timeout, &set0);
+ /*
+ * At this point, either ret == 0 (timer ran out) errno ==
+ * EINTR (a signal was received). Any other outcome is
+ * abnormal.
+ */
+ if (ret != 0 && errno != EINTR)
+ err(1, "p%s()", poll ? "poll" : "select");
+ /* if ret == 0, we should not have caught any signals */
+ if (ret == 0 && caught[SIGINT]) {
+ /*
+ * We successfully demonstrated the race. Restore
+ * the default action and re-raise SIGINT.
+ */
+ signal(SIGINT, SIG_DFL);
+ raise(SIGINT);
+ /* Not reached */
+ }
+ /* reset for next attempt */
+ caught[SIGINT] = 0;
+ }
+ /* Not reached */
+}
+
+static void
+prace(bool poll)
+{
+ int pd[2], status;
+ pid_t pid;
+
+ /* fork child process */
+ if (pipe(pd) != 0)
+ err(1, "pipe()");
+ if ((pid = fork()) < 0)
+ err(1, "fork()");
+ if (pid == 0) {
+ close(pd[0]);
+ child(pd[1], poll);
+ /* Not reached */
+ }
+ close(pd[1]);
+
+ /* wait for child to signal readiness */
+ (void)read(pd[0], &pd[0], sizeof(pd[0]));
+ close(pd[0]);
+
+ /* repeatedly attempt to signal at just the right moment */
+ for (useconds_t timeout = 1100; timeout > 900; timeout--) {
+ usleep(timeout);
+ if (kill(pid, SIGINT) != 0) {
+ if (errno != ENOENT)
+ err(1, "kill()");
+ /* ENOENT means the child has terminated */
+ break;
+ }
+ }
+
+ /* we're done, kill the child for sure */
+ (void)kill(pid, SIGKILL);
+ if (waitpid(pid, &status, 0) < 0)
+ err(1, "waitpid()");
+
+ /* assert that the child died of SIGKILL */
+ ATF_REQUIRE(WIFSIGNALED(status));
+ ATF_REQUIRE_MSG(WTERMSIG(status) == SIGKILL,
+ "child caught SIG%s", sys_signame[WTERMSIG(status)]);
+}
+
+ATF_TC_WITHOUT_HEAD(ppoll_race);
+ATF_TC_BODY(ppoll_race, tc)
+{
+ prace(true);
+}
+
+ATF_TC_WITHOUT_HEAD(pselect_race);
+ATF_TC_BODY(pselect_race, tc)
+{
+ prace(false);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, ppoll_race);
+ ATF_TP_ADD_TC(tp, pselect_race);
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/ptrace_test.c b/tests/sys/kern/ptrace_test.c
new file mode 100644
index 000000000000..d36dfe951e20
--- /dev/null
+++ b/tests/sys/kern/ptrace_test.c
@@ -0,0 +1,4665 @@
+/*-
+ * Copyright (c) 2015 John Baldwin <jhb@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/cpuset.h>
+#include <sys/elf.h>
+#include <sys/event.h>
+#include <sys/file.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <sys/procctl.h>
+#include <sys/procdesc.h>
+#include <sys/ptrace.h>
+#include <sys/procfs.h>
+#include <sys/queue.h>
+#include <sys/syscall.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <machine/cpufunc.h>
+#include <pthread.h>
+#include <sched.h>
+#include <semaphore.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <atf-c.h>
+
+/*
+ * Architectures with a user-visible breakpoint().
+ */
+#if defined(__aarch64__) || defined(__amd64__) || defined(__arm__) || \
+ defined(__i386__) || defined(__riscv)
+#define HAVE_BREAKPOINT
+#endif
+
+/*
+ * Adjust PC to skip over a breakpoint when stopped for a breakpoint trap.
+ */
+#ifdef HAVE_BREAKPOINT
+#if defined(__aarch64__)
+#define SKIP_BREAK(reg) ((reg)->elr += 4)
+#elif defined(__amd64__) || defined(__i386__)
+#define SKIP_BREAK(reg)
+#elif defined(__arm__)
+#define SKIP_BREAK(reg) ((reg)->r_pc += 4)
+#elif defined(__riscv)
+#define SKIP_BREAK(reg) ((reg)->sepc += 4)
+#endif
+#endif
+
+/*
+ * A variant of ATF_REQUIRE that is suitable for use in child
+ * processes. This only works if the parent process is tripped up by
+ * the early exit and fails some requirement itself.
+ */
+#define CHILD_REQUIRE(exp) do { \
+ if (!(exp)) \
+ child_fail_require(__FILE__, __LINE__, \
+ #exp " not met\n"); \
+} while (0)
+
+#define CHILD_REQUIRE_EQ(actual, expected) do { \
+ __typeof__(expected) _e = expected; \
+ __typeof__(actual) _a = actual; \
+ if (_e != _a) \
+ child_fail_require(__FILE__, __LINE__, #actual \
+ " (%jd) == " #expected " (%jd) not met\n", \
+ (intmax_t)_a, (intmax_t)_e); \
+} while (0)
+
+static __dead2 void
+child_fail_require(const char *file, int line, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[1024];
+
+ /* Use write() not fprintf() to avoid possible duplicate output. */
+ snprintf(buf, sizeof(buf), "%s:%d: ", file, line);
+ write(STDERR_FILENO, buf, strlen(buf));
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ write(STDERR_FILENO, buf, strlen(buf));
+ va_end(ap);
+
+ _exit(32);
+}
+
+#define REQUIRE_EQ(actual, expected) do { \
+ __typeof__(expected) _e = expected; \
+ __typeof__(actual) _a = actual; \
+ ATF_REQUIRE_MSG(_e == _a, #actual " (%jd) == " \
+ #expected " (%jd) not met", (intmax_t)_a, (intmax_t)_e); \
+} while (0)
+
+static void
+trace_me(void)
+{
+
+ /* Attach the parent process as a tracer of this process. */
+ CHILD_REQUIRE(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ /* Trigger a stop. */
+ raise(SIGSTOP);
+}
+
+static void
+attach_child(pid_t pid)
+{
+ pid_t wpid;
+ int status;
+
+ REQUIRE_EQ(ptrace(PT_ATTACH, pid, NULL, 0), 0);
+
+ wpid = waitpid(pid, &status, 0);
+ REQUIRE_EQ(wpid, pid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+}
+
+static void
+wait_for_zombie(pid_t pid)
+{
+
+ /*
+ * Wait for a process to exit. This is kind of gross, but
+ * there is not a better way.
+ *
+ * Prior to r325719, the kern.proc.pid.<pid> sysctl failed
+ * with ESRCH. After that change, a valid struct kinfo_proc
+ * is returned for zombies with ki_stat set to SZOMB.
+ */
+ for (;;) {
+ struct kinfo_proc kp;
+ size_t len;
+ int mib[4];
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PID;
+ mib[3] = pid;
+ len = sizeof(kp);
+ if (sysctl(mib, nitems(mib), &kp, &len, NULL, 0) == -1) {
+ REQUIRE_EQ(errno, ESRCH);
+ break;
+ }
+ if (kp.ki_stat == SZOMB)
+ break;
+ usleep(5000);
+ }
+}
+
+/*
+ * Verify that a parent debugger process "sees" the exit of a debugged
+ * process exactly once when attached via PT_TRACE_ME.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__parent_wait_after_trace_me);
+ATF_TC_BODY(ptrace__parent_wait_after_trace_me, tc)
+{
+ pid_t child, wpid;
+ int status;
+
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ /* Child process. */
+ trace_me();
+
+ _exit(1);
+ }
+
+ /* Parent process. */
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(child, &status, 0);
+ REQUIRE_EQ(wpid, child);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ /* Continue the child ignoring the SIGSTOP. */
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1);
+
+ /* The second wait() should report the exit status. */
+ wpid = waitpid(child, &status, 0);
+ REQUIRE_EQ(wpid, child);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 1);
+
+ /* The child should no longer exist. */
+ wpid = waitpid(child, &status, 0);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+/*
+ * Verify that a parent debugger process "sees" the exit of a debugged
+ * process exactly once when attached via PT_ATTACH.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__parent_wait_after_attach);
+ATF_TC_BODY(ptrace__parent_wait_after_attach, tc)
+{
+ pid_t child, wpid;
+ int cpipe[2], status;
+ char c;
+
+ REQUIRE_EQ(pipe(cpipe), 0);
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ /* Child process. */
+ close(cpipe[0]);
+
+ /* Wait for the parent to attach. */
+ CHILD_REQUIRE_EQ(0, read(cpipe[1], &c, sizeof(c)));
+
+ _exit(1);
+ }
+ close(cpipe[1]);
+
+ /* Parent process. */
+
+ /* Attach to the child process. */
+ attach_child(child);
+
+ /* Continue the child ignoring the SIGSTOP. */
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1);
+
+ /* Signal the child to exit. */
+ close(cpipe[0]);
+
+ /* The second wait() should report the exit status. */
+ wpid = waitpid(child, &status, 0);
+ REQUIRE_EQ(wpid, child);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 1);
+
+ /* The child should no longer exist. */
+ wpid = waitpid(child, &status, 0);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+/*
+ * Verify that a parent process "sees" the exit of a debugged process only
+ * after the debugger has seen it.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__parent_sees_exit_after_child_debugger);
+ATF_TC_BODY(ptrace__parent_sees_exit_after_child_debugger, tc)
+{
+ pid_t child, debugger, wpid;
+ int cpipe[2], dpipe[2], status;
+ char c;
+
+ REQUIRE_EQ(pipe(cpipe), 0);
+ ATF_REQUIRE((child = fork()) != -1);
+
+ if (child == 0) {
+ /* Child process. */
+ close(cpipe[0]);
+
+ /* Wait for parent to be ready. */
+ CHILD_REQUIRE_EQ(read(cpipe[1], &c, sizeof(c)),
+ (ssize_t)sizeof(c));
+
+ _exit(1);
+ }
+ close(cpipe[1]);
+
+ REQUIRE_EQ(pipe(dpipe), 0);
+ ATF_REQUIRE((debugger = fork()) != -1);
+
+ if (debugger == 0) {
+ /* Debugger process. */
+ close(dpipe[0]);
+
+ CHILD_REQUIRE(ptrace(PT_ATTACH, child, NULL, 0) != -1);
+
+ wpid = waitpid(child, &status, 0);
+ CHILD_REQUIRE_EQ(wpid, child);
+ CHILD_REQUIRE(WIFSTOPPED(status));
+ CHILD_REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ CHILD_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1);
+
+ /* Signal parent that debugger is attached. */
+ CHILD_REQUIRE_EQ(write(dpipe[1], &c, sizeof(c)),
+ (ssize_t)sizeof(c));
+
+ /* Wait for parent's failed wait. */
+ CHILD_REQUIRE_EQ(read(dpipe[1], &c, sizeof(c)), 0);
+
+ wpid = waitpid(child, &status, 0);
+ CHILD_REQUIRE_EQ(wpid, child);
+ CHILD_REQUIRE(WIFEXITED(status));
+ CHILD_REQUIRE_EQ(WEXITSTATUS(status), 1);
+
+ _exit(0);
+ }
+ close(dpipe[1]);
+
+ /* Parent process. */
+
+ /* Wait for the debugger to attach to the child. */
+ REQUIRE_EQ(read(dpipe[0], &c, sizeof(c)), (ssize_t)sizeof(c));
+
+ /* Release the child. */
+ REQUIRE_EQ(write(cpipe[0], &c, sizeof(c)), (ssize_t)sizeof(c));
+ REQUIRE_EQ(read(cpipe[0], &c, sizeof(c)), 0);
+ close(cpipe[0]);
+
+ wait_for_zombie(child);
+
+ /*
+ * This wait should return a pid of 0 to indicate no status to
+ * report. The parent should see the child as non-exited
+ * until the debugger sees the exit.
+ */
+ wpid = waitpid(child, &status, WNOHANG);
+ REQUIRE_EQ(wpid, 0);
+
+ /* Signal the debugger to wait for the child. */
+ close(dpipe[0]);
+
+ /* Wait for the debugger. */
+ wpid = waitpid(debugger, &status, 0);
+ REQUIRE_EQ(wpid, debugger);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 0);
+
+ /* The child process should now be ready. */
+ wpid = waitpid(child, &status, WNOHANG);
+ REQUIRE_EQ(wpid, child);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 1);
+}
+
+/*
+ * Verify that a parent process "sees" the exit of a debugged process
+ * only after a non-direct-child debugger has seen it. In particular,
+ * various wait() calls in the parent must avoid failing with ESRCH by
+ * checking the parent's orphan list for the debugee.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__parent_sees_exit_after_unrelated_debugger);
+ATF_TC_BODY(ptrace__parent_sees_exit_after_unrelated_debugger, tc)
+{
+ pid_t child, debugger, fpid, wpid;
+ int cpipe[2], dpipe[2], status;
+ char c;
+
+ REQUIRE_EQ(pipe(cpipe), 0);
+ ATF_REQUIRE((child = fork()) != -1);
+
+ if (child == 0) {
+ /* Child process. */
+ close(cpipe[0]);
+
+ /* Wait for parent to be ready. */
+ CHILD_REQUIRE_EQ(read(cpipe[1], &c, sizeof(c)),
+ (ssize_t)sizeof(c));
+
+ _exit(1);
+ }
+ close(cpipe[1]);
+
+ REQUIRE_EQ(pipe(dpipe), 0);
+ ATF_REQUIRE((debugger = fork()) != -1);
+
+ if (debugger == 0) {
+ /* Debugger parent. */
+
+ /*
+ * Fork again and drop the debugger parent so that the
+ * debugger is not a child of the main parent.
+ */
+ CHILD_REQUIRE((fpid = fork()) != -1);
+ if (fpid != 0)
+ _exit(2);
+
+ /* Debugger process. */
+ close(dpipe[0]);
+
+ CHILD_REQUIRE(ptrace(PT_ATTACH, child, NULL, 0) != -1);
+
+ wpid = waitpid(child, &status, 0);
+ CHILD_REQUIRE_EQ(wpid, child);
+ CHILD_REQUIRE(WIFSTOPPED(status));
+ CHILD_REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ CHILD_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1);
+
+ /* Signal parent that debugger is attached. */
+ CHILD_REQUIRE_EQ(write(dpipe[1], &c, sizeof(c)),
+ (ssize_t)sizeof(c));
+
+ /* Wait for parent's failed wait. */
+ CHILD_REQUIRE_EQ(read(dpipe[1], &c, sizeof(c)),
+ (ssize_t)sizeof(c));
+
+ wpid = waitpid(child, &status, 0);
+ CHILD_REQUIRE_EQ(wpid, child);
+ CHILD_REQUIRE(WIFEXITED(status));
+ CHILD_REQUIRE_EQ(WEXITSTATUS(status), 1);
+
+ _exit(0);
+ }
+ close(dpipe[1]);
+
+ /* Parent process. */
+
+ /* Wait for the debugger parent process to exit. */
+ wpid = waitpid(debugger, &status, 0);
+ REQUIRE_EQ(wpid, debugger);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 2);
+
+ /* A WNOHANG wait here should see the non-exited child. */
+ wpid = waitpid(child, &status, WNOHANG);
+ REQUIRE_EQ(wpid, 0);
+
+ /* Wait for the debugger to attach to the child. */
+ REQUIRE_EQ(read(dpipe[0], &c, sizeof(c)), (ssize_t)sizeof(c));
+
+ /* Release the child. */
+ REQUIRE_EQ(write(cpipe[0], &c, sizeof(c)), (ssize_t)sizeof(c));
+ REQUIRE_EQ(read(cpipe[0], &c, sizeof(c)), 0);
+ close(cpipe[0]);
+
+ wait_for_zombie(child);
+
+ /*
+ * This wait should return a pid of 0 to indicate no status to
+ * report. The parent should see the child as non-exited
+ * until the debugger sees the exit.
+ */
+ wpid = waitpid(child, &status, WNOHANG);
+ REQUIRE_EQ(wpid, 0);
+
+ /* Signal the debugger to wait for the child. */
+ REQUIRE_EQ(write(dpipe[0], &c, sizeof(c)), (ssize_t)sizeof(c));
+
+ /* Wait for the debugger. */
+ REQUIRE_EQ(read(dpipe[0], &c, sizeof(c)), 0);
+ close(dpipe[0]);
+
+ /* The child process should now be ready. */
+ wpid = waitpid(child, &status, WNOHANG);
+ REQUIRE_EQ(wpid, child);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 1);
+}
+
+/*
+ * Make sure that we can collect the exit status of an orphaned process.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__parent_exits_before_child);
+ATF_TC_BODY(ptrace__parent_exits_before_child, tc)
+{
+ ssize_t n;
+ int cpipe1[2], cpipe2[2], gcpipe[2], status;
+ pid_t child, gchild;
+
+ REQUIRE_EQ(pipe(cpipe1), 0);
+ REQUIRE_EQ(pipe(cpipe2), 0);
+ REQUIRE_EQ(pipe(gcpipe), 0);
+
+ REQUIRE_EQ(procctl(P_PID, getpid(), PROC_REAP_ACQUIRE, NULL), 0);
+
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ CHILD_REQUIRE((gchild = fork()) != -1);
+ if (gchild == 0) {
+ status = 1;
+ do {
+ n = read(gcpipe[0], &status, sizeof(status));
+ } while (n == -1 && errno == EINTR);
+ _exit(status);
+ }
+
+ CHILD_REQUIRE_EQ(write(cpipe1[1], &gchild, sizeof(gchild)),
+ (ssize_t)sizeof(gchild));
+ CHILD_REQUIRE_EQ(read(cpipe2[0], &status, sizeof(status)),
+ (ssize_t)sizeof(status));
+ _exit(status);
+ }
+
+ REQUIRE_EQ(read(cpipe1[0], &gchild, sizeof(gchild)),
+ (ssize_t)sizeof(gchild));
+
+ REQUIRE_EQ(ptrace(PT_ATTACH, gchild, NULL, 0), 0);
+
+ status = 0;
+ REQUIRE_EQ(write(cpipe2[1], &status, sizeof(status)),
+ (ssize_t)sizeof(status));
+ REQUIRE_EQ(waitpid(child, &status, 0), child);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 0);
+
+ status = 0;
+ REQUIRE_EQ(write(gcpipe[1], &status, sizeof(status)),
+ (ssize_t)sizeof(status));
+ REQUIRE_EQ(waitpid(gchild, &status, 0), gchild);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(ptrace(PT_DETACH, gchild, (caddr_t)1, 0), 0);
+ REQUIRE_EQ(waitpid(gchild, &status, 0), gchild);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 0);
+
+ REQUIRE_EQ(close(cpipe1[0]), 0);
+ REQUIRE_EQ(close(cpipe1[1]), 0);
+ REQUIRE_EQ(close(cpipe2[0]), 0);
+ REQUIRE_EQ(close(cpipe2[1]), 0);
+ REQUIRE_EQ(close(gcpipe[0]), 0);
+ REQUIRE_EQ(close(gcpipe[1]), 0);
+}
+
+/*
+ * The parent process should always act the same regardless of how the
+ * debugger is attached to it.
+ */
+static __dead2 void
+follow_fork_parent(bool use_vfork)
+{
+ pid_t fpid, wpid;
+ int status;
+
+ if (use_vfork)
+ CHILD_REQUIRE((fpid = vfork()) != -1);
+ else
+ CHILD_REQUIRE((fpid = fork()) != -1);
+
+ if (fpid == 0)
+ /* Child */
+ _exit(2);
+
+ wpid = waitpid(fpid, &status, 0);
+ CHILD_REQUIRE_EQ(wpid, fpid);
+ CHILD_REQUIRE(WIFEXITED(status));
+ CHILD_REQUIRE_EQ(WEXITSTATUS(status), 2);
+
+ _exit(1);
+}
+
+/*
+ * Helper routine for follow fork tests. This waits for two stops
+ * that report both "sides" of a fork. It returns the pid of the new
+ * child process.
+ */
+static pid_t
+handle_fork_events(pid_t parent, struct ptrace_lwpinfo *ppl)
+{
+ struct ptrace_lwpinfo pl;
+ bool fork_reported[2];
+ pid_t child, wpid;
+ int i, status;
+
+ fork_reported[0] = false;
+ fork_reported[1] = false;
+ child = -1;
+
+ /*
+ * Each process should report a fork event. The parent should
+ * report a PL_FLAG_FORKED event, and the child should report
+ * a PL_FLAG_CHILD event.
+ */
+ for (i = 0; i < 2; i++) {
+ wpid = wait(&status);
+ ATF_REQUIRE(wpid > 0);
+ ATF_REQUIRE(WIFSTOPPED(status));
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl,
+ sizeof(pl)) != -1);
+ ATF_REQUIRE((pl.pl_flags & (PL_FLAG_FORKED | PL_FLAG_CHILD)) !=
+ 0);
+ ATF_REQUIRE((pl.pl_flags & (PL_FLAG_FORKED | PL_FLAG_CHILD)) !=
+ (PL_FLAG_FORKED | PL_FLAG_CHILD));
+ if (pl.pl_flags & PL_FLAG_CHILD) {
+ ATF_REQUIRE(wpid != parent);
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+ ATF_REQUIRE(!fork_reported[1]);
+ if (child == -1)
+ child = wpid;
+ else
+ REQUIRE_EQ(child, wpid);
+ if (ppl != NULL)
+ ppl[1] = pl;
+ fork_reported[1] = true;
+ } else {
+ REQUIRE_EQ(wpid, parent);
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+ ATF_REQUIRE(!fork_reported[0]);
+ if (child == -1)
+ child = pl.pl_child_pid;
+ else
+ REQUIRE_EQ(child, pl.pl_child_pid);
+ if (ppl != NULL)
+ ppl[0] = pl;
+ fork_reported[0] = true;
+ }
+ }
+
+ return (child);
+}
+
+/*
+ * Verify that a new child process is stopped after a followed fork and
+ * that the traced parent sees the exit of the child after the debugger
+ * when both processes remain attached to the debugger.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__follow_fork_both_attached);
+ATF_TC_BODY(ptrace__follow_fork_both_attached, tc)
+{
+ pid_t children[2], fpid, wpid;
+ int status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ follow_fork_parent(false);
+ }
+
+ /* Parent process. */
+ children[0] = fpid;
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(children[0], &status, 0);
+ REQUIRE_EQ(wpid, children[0]);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1);
+
+ /* Continue the child ignoring the SIGSTOP. */
+ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1);
+
+ children[1] = handle_fork_events(children[0], NULL);
+ ATF_REQUIRE(children[1] > 0);
+
+ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1);
+ ATF_REQUIRE(ptrace(PT_CONTINUE, children[1], (caddr_t)1, 0) != -1);
+
+ /*
+ * The child can't exit until the grandchild reports status, so the
+ * grandchild should report its exit first to the debugger.
+ */
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, children[1]);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 2);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, children[0]);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 1);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+/*
+ * Verify that a new child process is stopped after a followed fork
+ * and that the traced parent sees the exit of the child when the new
+ * child process is detached after it reports its fork.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__follow_fork_child_detached);
+ATF_TC_BODY(ptrace__follow_fork_child_detached, tc)
+{
+ pid_t children[2], fpid, wpid;
+ int status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ follow_fork_parent(false);
+ }
+
+ /* Parent process. */
+ children[0] = fpid;
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(children[0], &status, 0);
+ REQUIRE_EQ(wpid, children[0]);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1);
+
+ /* Continue the child ignoring the SIGSTOP. */
+ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1);
+
+ children[1] = handle_fork_events(children[0], NULL);
+ ATF_REQUIRE(children[1] > 0);
+
+ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1);
+ ATF_REQUIRE(ptrace(PT_DETACH, children[1], (caddr_t)1, 0) != -1);
+
+ /*
+ * Should not see any status from the grandchild now, only the
+ * child.
+ */
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, children[0]);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 1);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+/*
+ * Verify that a new child process is stopped after a followed fork
+ * and that the traced parent sees the exit of the child when the
+ * traced parent is detached after the fork.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__follow_fork_parent_detached);
+ATF_TC_BODY(ptrace__follow_fork_parent_detached, tc)
+{
+ pid_t children[2], fpid, wpid;
+ int status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ follow_fork_parent(false);
+ }
+
+ /* Parent process. */
+ children[0] = fpid;
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(children[0], &status, 0);
+ REQUIRE_EQ(wpid, children[0]);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1);
+
+ /* Continue the child ignoring the SIGSTOP. */
+ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1);
+
+ children[1] = handle_fork_events(children[0], NULL);
+ ATF_REQUIRE(children[1] > 0);
+
+ ATF_REQUIRE(ptrace(PT_DETACH, children[0], (caddr_t)1, 0) != -1);
+ ATF_REQUIRE(ptrace(PT_CONTINUE, children[1], (caddr_t)1, 0) != -1);
+
+ /*
+ * The child can't exit until the grandchild reports status, so the
+ * grandchild should report its exit first to the debugger.
+ *
+ * Even though the child process is detached, it is still a
+ * child of the debugger, so it will still report it's exit
+ * after the grandchild.
+ */
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, children[1]);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 2);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, children[0]);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 1);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+static void
+attach_fork_parent(int cpipe[2])
+{
+ pid_t fpid;
+
+ close(cpipe[0]);
+
+ /* Double-fork to disassociate from the debugger. */
+ CHILD_REQUIRE((fpid = fork()) != -1);
+ if (fpid != 0)
+ _exit(3);
+
+ /* Send the pid of the disassociated child to the debugger. */
+ fpid = getpid();
+ CHILD_REQUIRE_EQ(write(cpipe[1], &fpid, sizeof(fpid)),
+ (ssize_t)sizeof(fpid));
+
+ /* Wait for the debugger to attach. */
+ CHILD_REQUIRE_EQ(read(cpipe[1], &fpid, sizeof(fpid)), 0);
+}
+
+/*
+ * Verify that a new child process is stopped after a followed fork and
+ * that the traced parent sees the exit of the child after the debugger
+ * when both processes remain attached to the debugger. In this test
+ * the parent that forks is not a direct child of the debugger.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__follow_fork_both_attached_unrelated_debugger);
+ATF_TC_BODY(ptrace__follow_fork_both_attached_unrelated_debugger, tc)
+{
+ pid_t children[2], fpid, wpid;
+ int cpipe[2], status;
+
+ REQUIRE_EQ(pipe(cpipe), 0);
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ attach_fork_parent(cpipe);
+ follow_fork_parent(false);
+ }
+
+ /* Parent process. */
+ close(cpipe[1]);
+
+ /* Wait for the direct child to exit. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 3);
+
+ /* Read the pid of the fork parent. */
+ REQUIRE_EQ(read(cpipe[0], &children[0], sizeof(children[0])),
+ (ssize_t)sizeof(children[0]));
+
+ /* Attach to the fork parent. */
+ attach_child(children[0]);
+
+ ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1);
+
+ /* Continue the fork parent ignoring the SIGSTOP. */
+ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1);
+
+ /* Signal the fork parent to continue. */
+ close(cpipe[0]);
+
+ children[1] = handle_fork_events(children[0], NULL);
+ ATF_REQUIRE(children[1] > 0);
+
+ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1);
+ ATF_REQUIRE(ptrace(PT_CONTINUE, children[1], (caddr_t)1, 0) != -1);
+
+ /*
+ * The fork parent can't exit until the child reports status,
+ * so the child should report its exit first to the debugger.
+ */
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, children[1]);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 2);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, children[0]);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 1);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+/*
+ * Verify that a new child process is stopped after a followed fork
+ * and that the traced parent sees the exit of the child when the new
+ * child process is detached after it reports its fork. In this test
+ * the parent that forks is not a direct child of the debugger.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__follow_fork_child_detached_unrelated_debugger);
+ATF_TC_BODY(ptrace__follow_fork_child_detached_unrelated_debugger, tc)
+{
+ pid_t children[2], fpid, wpid;
+ int cpipe[2], status;
+
+ REQUIRE_EQ(pipe(cpipe), 0);
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ attach_fork_parent(cpipe);
+ follow_fork_parent(false);
+ }
+
+ /* Parent process. */
+ close(cpipe[1]);
+
+ /* Wait for the direct child to exit. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 3);
+
+ /* Read the pid of the fork parent. */
+ REQUIRE_EQ(read(cpipe[0], &children[0], sizeof(children[0])),
+ (ssize_t)sizeof(children[0]));
+
+ /* Attach to the fork parent. */
+ attach_child(children[0]);
+
+ ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1);
+
+ /* Continue the fork parent ignoring the SIGSTOP. */
+ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1);
+
+ /* Signal the fork parent to continue. */
+ close(cpipe[0]);
+
+ children[1] = handle_fork_events(children[0], NULL);
+ ATF_REQUIRE(children[1] > 0);
+
+ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1);
+ ATF_REQUIRE(ptrace(PT_DETACH, children[1], (caddr_t)1, 0) != -1);
+
+ /*
+ * Should not see any status from the child now, only the fork
+ * parent.
+ */
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, children[0]);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 1);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+/*
+ * Verify that a new child process is stopped after a followed fork
+ * and that the traced parent sees the exit of the child when the
+ * traced parent is detached after the fork. In this test the parent
+ * that forks is not a direct child of the debugger.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__follow_fork_parent_detached_unrelated_debugger);
+ATF_TC_BODY(ptrace__follow_fork_parent_detached_unrelated_debugger, tc)
+{
+ pid_t children[2], fpid, wpid;
+ int cpipe[2], status;
+
+ REQUIRE_EQ(pipe(cpipe), 0);
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ attach_fork_parent(cpipe);
+ follow_fork_parent(false);
+ }
+
+ /* Parent process. */
+ close(cpipe[1]);
+
+ /* Wait for the direct child to exit. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 3);
+
+ /* Read the pid of the fork parent. */
+ REQUIRE_EQ(read(cpipe[0], &children[0], sizeof(children[0])),
+ (ssize_t)sizeof(children[0]));
+
+ /* Attach to the fork parent. */
+ attach_child(children[0]);
+
+ ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1);
+
+ /* Continue the fork parent ignoring the SIGSTOP. */
+ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1);
+
+ /* Signal the fork parent to continue. */
+ close(cpipe[0]);
+
+ children[1] = handle_fork_events(children[0], NULL);
+ ATF_REQUIRE(children[1] > 0);
+
+ ATF_REQUIRE(ptrace(PT_DETACH, children[0], (caddr_t)1, 0) != -1);
+ ATF_REQUIRE(ptrace(PT_CONTINUE, children[1], (caddr_t)1, 0) != -1);
+
+ /*
+ * Should not see any status from the fork parent now, only
+ * the child.
+ */
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, children[1]);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 2);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+/*
+ * Verify that a child process does not see an unrelated debugger as its
+ * parent but sees its original parent process.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__getppid);
+ATF_TC_BODY(ptrace__getppid, tc)
+{
+ pid_t child, debugger, ppid, wpid;
+ int cpipe[2], dpipe[2], status;
+ char c;
+
+ REQUIRE_EQ(pipe(cpipe), 0);
+ ATF_REQUIRE((child = fork()) != -1);
+
+ if (child == 0) {
+ /* Child process. */
+ close(cpipe[0]);
+
+ /* Wait for parent to be ready. */
+ CHILD_REQUIRE_EQ(read(cpipe[1], &c, sizeof(c)),
+ (ssize_t)sizeof(c));
+
+ /* Report the parent PID to the parent. */
+ ppid = getppid();
+ CHILD_REQUIRE_EQ(write(cpipe[1], &ppid, sizeof(ppid)),
+ (ssize_t)sizeof(ppid));
+
+ _exit(1);
+ }
+ close(cpipe[1]);
+
+ REQUIRE_EQ(pipe(dpipe), 0);
+ ATF_REQUIRE((debugger = fork()) != -1);
+
+ if (debugger == 0) {
+ /* Debugger process. */
+ close(dpipe[0]);
+
+ CHILD_REQUIRE(ptrace(PT_ATTACH, child, NULL, 0) != -1);
+
+ wpid = waitpid(child, &status, 0);
+ CHILD_REQUIRE_EQ(wpid, child);
+ CHILD_REQUIRE(WIFSTOPPED(status));
+ CHILD_REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ CHILD_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1);
+
+ /* Signal parent that debugger is attached. */
+ CHILD_REQUIRE_EQ(write(dpipe[1], &c, sizeof(c)),
+ (ssize_t)sizeof(c));
+
+ /* Wait for traced child to exit. */
+ wpid = waitpid(child, &status, 0);
+ CHILD_REQUIRE_EQ(wpid, child);
+ CHILD_REQUIRE(WIFEXITED(status));
+ CHILD_REQUIRE_EQ(WEXITSTATUS(status), 1);
+
+ _exit(0);
+ }
+ close(dpipe[1]);
+
+ /* Parent process. */
+
+ /* Wait for the debugger to attach to the child. */
+ REQUIRE_EQ(read(dpipe[0], &c, sizeof(c)), (ssize_t)sizeof(c));
+
+ /* Release the child. */
+ REQUIRE_EQ(write(cpipe[0], &c, sizeof(c)), (ssize_t)sizeof(c));
+
+ /* Read the parent PID from the child. */
+ REQUIRE_EQ(read(cpipe[0], &ppid, sizeof(ppid)), (ssize_t)sizeof(ppid));
+ close(cpipe[0]);
+
+ REQUIRE_EQ(ppid, getpid());
+
+ /* Wait for the debugger. */
+ wpid = waitpid(debugger, &status, 0);
+ REQUIRE_EQ(wpid, debugger);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 0);
+
+ /* The child process should now be ready. */
+ wpid = waitpid(child, &status, WNOHANG);
+ REQUIRE_EQ(wpid, child);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 1);
+}
+
+/*
+ * Verify that pl_syscall_code in struct ptrace_lwpinfo for a new
+ * child process created via fork() reports the correct value.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__new_child_pl_syscall_code_fork);
+ATF_TC_BODY(ptrace__new_child_pl_syscall_code_fork, tc)
+{
+ struct ptrace_lwpinfo pl[2];
+ pid_t children[2], fpid, wpid;
+ int status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ follow_fork_parent(false);
+ }
+
+ /* Parent process. */
+ children[0] = fpid;
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(children[0], &status, 0);
+ REQUIRE_EQ(wpid, children[0]);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1);
+
+ /* Continue the child ignoring the SIGSTOP. */
+ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1);
+
+ /* Wait for both halves of the fork event to get reported. */
+ children[1] = handle_fork_events(children[0], pl);
+ ATF_REQUIRE(children[1] > 0);
+
+ ATF_REQUIRE((pl[0].pl_flags & PL_FLAG_SCX) != 0);
+ ATF_REQUIRE((pl[1].pl_flags & PL_FLAG_SCX) != 0);
+ REQUIRE_EQ(pl[0].pl_syscall_code, (unsigned)SYS_fork);
+ REQUIRE_EQ(pl[0].pl_syscall_code, pl[1].pl_syscall_code);
+ REQUIRE_EQ(pl[0].pl_syscall_narg, pl[1].pl_syscall_narg);
+
+ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1);
+ ATF_REQUIRE(ptrace(PT_CONTINUE, children[1], (caddr_t)1, 0) != -1);
+
+ /*
+ * The child can't exit until the grandchild reports status, so the
+ * grandchild should report its exit first to the debugger.
+ */
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, children[1]);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 2);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, children[0]);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 1);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+/*
+ * Verify that pl_syscall_code in struct ptrace_lwpinfo for a new
+ * child process created via vfork() reports the correct value.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__new_child_pl_syscall_code_vfork);
+ATF_TC_BODY(ptrace__new_child_pl_syscall_code_vfork, tc)
+{
+ struct ptrace_lwpinfo pl[2];
+ pid_t children[2], fpid, wpid;
+ int status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ follow_fork_parent(true);
+ }
+
+ /* Parent process. */
+ children[0] = fpid;
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(children[0], &status, 0);
+ REQUIRE_EQ(wpid, children[0]);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1);
+
+ /* Continue the child ignoring the SIGSTOP. */
+ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1);
+
+ /* Wait for both halves of the fork event to get reported. */
+ children[1] = handle_fork_events(children[0], pl);
+ ATF_REQUIRE(children[1] > 0);
+
+ ATF_REQUIRE((pl[0].pl_flags & PL_FLAG_SCX) != 0);
+ ATF_REQUIRE((pl[1].pl_flags & PL_FLAG_SCX) != 0);
+ REQUIRE_EQ(pl[0].pl_syscall_code, (unsigned)SYS_vfork);
+ REQUIRE_EQ(pl[0].pl_syscall_code, pl[1].pl_syscall_code);
+ REQUIRE_EQ(pl[0].pl_syscall_narg, pl[1].pl_syscall_narg);
+
+ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1);
+ ATF_REQUIRE(ptrace(PT_CONTINUE, children[1], (caddr_t)1, 0) != -1);
+
+ /*
+ * The child can't exit until the grandchild reports status, so the
+ * grandchild should report its exit first to the debugger.
+ */
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, children[1]);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 2);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, children[0]);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 1);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+static void *
+simple_thread(void *arg __unused)
+{
+
+ pthread_exit(NULL);
+}
+
+static __dead2 void
+simple_thread_main(void)
+{
+ pthread_t thread;
+
+ CHILD_REQUIRE_EQ(pthread_create(&thread, NULL, simple_thread, NULL), 0);
+ CHILD_REQUIRE_EQ(pthread_join(thread, NULL), 0);
+ exit(1);
+}
+
+/*
+ * Verify that pl_syscall_code in struct ptrace_lwpinfo for a new
+ * thread reports the correct value.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__new_child_pl_syscall_code_thread);
+ATF_TC_BODY(ptrace__new_child_pl_syscall_code_thread, tc)
+{
+ struct ptrace_lwpinfo pl;
+ pid_t fpid, wpid;
+ lwpid_t mainlwp;
+ int status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ simple_thread_main();
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl,
+ sizeof(pl)) != -1);
+ mainlwp = pl.pl_lwpid;
+
+ /*
+ * Continue the child ignoring the SIGSTOP and tracing all
+ * system call exits.
+ */
+ ATF_REQUIRE(ptrace(PT_TO_SCX, fpid, (caddr_t)1, 0) != -1);
+
+ /*
+ * Wait for the new thread to arrive. pthread_create() might
+ * invoke any number of system calls. For now we just wait
+ * for the new thread to arrive and make sure it reports a
+ * valid system call code. If ptrace grows thread event
+ * reporting then this test can be made more precise.
+ */
+ for (;;) {
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl,
+ sizeof(pl)) != -1);
+ ATF_REQUIRE((pl.pl_flags & PL_FLAG_SCX) != 0);
+ ATF_REQUIRE(pl.pl_syscall_code != 0);
+ if (pl.pl_lwpid != mainlwp)
+ /* New thread seen. */
+ break;
+
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+ }
+
+ /* Wait for the child to exit. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+ for (;;) {
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ if (WIFEXITED(status))
+ break;
+
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+ }
+
+ REQUIRE_EQ(WEXITSTATUS(status), 1);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+/*
+ * Verify that the expected LWP events are reported for a child thread.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__lwp_events);
+ATF_TC_BODY(ptrace__lwp_events, tc)
+{
+ struct ptrace_lwpinfo pl;
+ pid_t fpid, wpid;
+ lwpid_t lwps[2];
+ int status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ simple_thread_main();
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl,
+ sizeof(pl)) != -1);
+ lwps[0] = pl.pl_lwpid;
+
+ REQUIRE_EQ(ptrace(PT_LWP_EVENTS, wpid, NULL, 1), 0);
+
+ /* Continue the child ignoring the SIGSTOP. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* The first event should be for the child thread's birth. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ REQUIRE_EQ((pl.pl_flags & (PL_FLAG_BORN | PL_FLAG_SCX)),
+ (PL_FLAG_BORN | PL_FLAG_SCX));
+ ATF_REQUIRE(pl.pl_lwpid != lwps[0]);
+ lwps[1] = pl.pl_lwpid;
+
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* The next event should be for the child thread's death. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ REQUIRE_EQ((pl.pl_flags & (PL_FLAG_EXITED | PL_FLAG_SCE)),
+ (PL_FLAG_EXITED | PL_FLAG_SCE));
+ REQUIRE_EQ(pl.pl_lwpid, lwps[1]);
+
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* The last event should be for the child process's exit. */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 1);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+static void *
+exec_thread(void *arg __unused)
+{
+
+ execl("/usr/bin/true", "true", NULL);
+ exit(127);
+}
+
+static __dead2 void
+exec_thread_main(void)
+{
+ pthread_t thread;
+
+ CHILD_REQUIRE_EQ(pthread_create(&thread, NULL, exec_thread, NULL), 0);
+ for (;;)
+ sleep(60);
+ exit(1);
+}
+
+/*
+ * Verify that the expected LWP events are reported for a multithreaded
+ * process that calls execve(2).
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__lwp_events_exec);
+ATF_TC_BODY(ptrace__lwp_events_exec, tc)
+{
+ struct ptrace_lwpinfo pl;
+ pid_t fpid, wpid;
+ lwpid_t lwps[2];
+ int status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ exec_thread_main();
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl,
+ sizeof(pl)) != -1);
+ lwps[0] = pl.pl_lwpid;
+
+ REQUIRE_EQ(ptrace(PT_LWP_EVENTS, wpid, NULL, 1), 0);
+
+ /* Continue the child ignoring the SIGSTOP. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* The first event should be for the child thread's birth. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ REQUIRE_EQ((pl.pl_flags & (PL_FLAG_BORN | PL_FLAG_SCX)),
+ (PL_FLAG_BORN | PL_FLAG_SCX));
+ ATF_REQUIRE(pl.pl_lwpid != lwps[0]);
+ lwps[1] = pl.pl_lwpid;
+
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /*
+ * The next event should be for the main thread's death due to
+ * single threading from execve().
+ */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ REQUIRE_EQ((pl.pl_flags & (PL_FLAG_EXITED | PL_FLAG_SCE)),
+ (PL_FLAG_EXITED));
+ REQUIRE_EQ(pl.pl_lwpid, lwps[0]);
+
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* The next event should be for the child process's exec. */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ REQUIRE_EQ((pl.pl_flags & (PL_FLAG_EXEC | PL_FLAG_SCX)),
+ (PL_FLAG_EXEC | PL_FLAG_SCX));
+ REQUIRE_EQ(pl.pl_lwpid, lwps[1]);
+
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* The last event should be for the child process's exit. */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 0);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+static void
+handler(int sig __unused)
+{
+}
+
+static void
+signal_main(void)
+{
+
+ signal(SIGINFO, handler);
+ raise(SIGINFO);
+ exit(0);
+}
+
+/*
+ * Verify that the expected ptrace event is reported for a signal.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__siginfo);
+ATF_TC_BODY(ptrace__siginfo, tc)
+{
+ struct ptrace_lwpinfo pl;
+ pid_t fpid, wpid;
+ int status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ signal_main();
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* The next event should be for the SIGINFO. */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGINFO);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ REQUIRE_EQ(pl.pl_event, PL_EVENT_SIGNAL);
+ ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI);
+ REQUIRE_EQ(pl.pl_siginfo.si_code, SI_LWP);
+ REQUIRE_EQ(pl.pl_siginfo.si_pid, wpid);
+
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* The last event should be for the child process's exit. */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 0);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+/*
+ * Verify that the expected ptrace events are reported for PTRACE_EXEC.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__ptrace_exec_disable);
+ATF_TC_BODY(ptrace__ptrace_exec_disable, tc)
+{
+ pid_t fpid, wpid;
+ int events, status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ exec_thread(NULL);
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ events = 0;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, fpid, (caddr_t)&events,
+ sizeof(events)) == 0);
+
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* Should get one event at exit. */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 0);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+ATF_TC_WITHOUT_HEAD(ptrace__ptrace_exec_enable);
+ATF_TC_BODY(ptrace__ptrace_exec_enable, tc)
+{
+ struct ptrace_lwpinfo pl;
+ pid_t fpid, wpid;
+ int events, status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ exec_thread(NULL);
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ events = PTRACE_EXEC;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, fpid, (caddr_t)&events,
+ sizeof(events)) == 0);
+
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* The next event should be for the child process's exec. */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ REQUIRE_EQ((pl.pl_flags & (PL_FLAG_EXEC | PL_FLAG_SCX)),
+ (PL_FLAG_EXEC | PL_FLAG_SCX));
+
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* The last event should be for the child process's exit. */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 0);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+ATF_TC_WITHOUT_HEAD(ptrace__event_mask);
+ATF_TC_BODY(ptrace__event_mask, tc)
+{
+ pid_t fpid, wpid;
+ int events, status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ exit(0);
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ /* PT_FOLLOW_FORK should toggle the state of PTRACE_FORK. */
+ ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, fpid, NULL, 1) != -1);
+ ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events,
+ sizeof(events)) == 0);
+ ATF_REQUIRE(events & PTRACE_FORK);
+ ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, fpid, NULL, 0) != -1);
+ ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events,
+ sizeof(events)) == 0);
+ ATF_REQUIRE(!(events & PTRACE_FORK));
+
+ /* PT_LWP_EVENTS should toggle the state of PTRACE_LWP. */
+ ATF_REQUIRE(ptrace(PT_LWP_EVENTS, fpid, NULL, 1) != -1);
+ ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events,
+ sizeof(events)) == 0);
+ ATF_REQUIRE(events & PTRACE_LWP);
+ ATF_REQUIRE(ptrace(PT_LWP_EVENTS, fpid, NULL, 0) != -1);
+ ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events,
+ sizeof(events)) == 0);
+ ATF_REQUIRE(!(events & PTRACE_LWP));
+
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* Should get one event at exit. */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 0);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+/*
+ * Verify that the expected ptrace events are reported for PTRACE_VFORK.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__ptrace_vfork);
+ATF_TC_BODY(ptrace__ptrace_vfork, tc)
+{
+ struct ptrace_lwpinfo pl;
+ pid_t fpid, wpid;
+ int events, status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ follow_fork_parent(true);
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events,
+ sizeof(events)) == 0);
+ events |= PTRACE_VFORK;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, fpid, (caddr_t)&events,
+ sizeof(events)) == 0);
+
+ /* Continue the child ignoring the SIGSTOP. */
+ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) != -1);
+
+ /* The next event should report the end of the vfork. */
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE((pl.pl_flags & PL_FLAG_VFORK_DONE) != 0);
+
+ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) != -1);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 1);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+ATF_TC_WITHOUT_HEAD(ptrace__ptrace_vfork_follow);
+ATF_TC_BODY(ptrace__ptrace_vfork_follow, tc)
+{
+ struct ptrace_lwpinfo pl[2];
+ pid_t children[2], fpid, wpid;
+ int events, status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ follow_fork_parent(true);
+ }
+
+ /* Parent process. */
+ children[0] = fpid;
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(children[0], &status, 0);
+ REQUIRE_EQ(wpid, children[0]);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, children[0], (caddr_t)&events,
+ sizeof(events)) == 0);
+ events |= PTRACE_FORK | PTRACE_VFORK;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, children[0], (caddr_t)&events,
+ sizeof(events)) == 0);
+
+ /* Continue the child ignoring the SIGSTOP. */
+ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1);
+
+ /* Wait for both halves of the fork event to get reported. */
+ children[1] = handle_fork_events(children[0], pl);
+ ATF_REQUIRE(children[1] > 0);
+
+ ATF_REQUIRE((pl[0].pl_flags & PL_FLAG_VFORKED) != 0);
+
+ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1);
+ ATF_REQUIRE(ptrace(PT_CONTINUE, children[1], (caddr_t)1, 0) != -1);
+
+ /*
+ * The child can't exit until the grandchild reports status, so the
+ * grandchild should report its exit first to the debugger.
+ */
+ wpid = waitpid(children[1], &status, 0);
+ REQUIRE_EQ(wpid, children[1]);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 2);
+
+ /*
+ * The child should report it's vfork() completion before it
+ * exits.
+ */
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, children[0]);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl[0], sizeof(pl[0])) !=
+ -1);
+ ATF_REQUIRE((pl[0].pl_flags & PL_FLAG_VFORK_DONE) != 0);
+
+ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, children[0]);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 1);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+#ifdef HAVE_BREAKPOINT
+/*
+ * Verify that no more events are reported after PT_KILL except for the
+ * process exit when stopped due to a breakpoint trap.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__PT_KILL_breakpoint);
+ATF_TC_BODY(ptrace__PT_KILL_breakpoint, tc)
+{
+ pid_t fpid, wpid;
+ int status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ breakpoint();
+ exit(1);
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ /* Continue the child ignoring the SIGSTOP. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* The second wait() should report hitting the breakpoint. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ /* Kill the child process. */
+ REQUIRE_EQ(ptrace(PT_KILL, fpid, 0, 0), 0);
+
+ /* The last wait() should report the SIGKILL. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSIGNALED(status));
+ REQUIRE_EQ(WTERMSIG(status), SIGKILL);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+#endif /* HAVE_BREAKPOINT */
+
+/*
+ * Verify that no more events are reported after PT_KILL except for the
+ * process exit when stopped inside of a system call.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__PT_KILL_system_call);
+ATF_TC_BODY(ptrace__PT_KILL_system_call, tc)
+{
+ struct ptrace_lwpinfo pl;
+ pid_t fpid, wpid;
+ int status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ getpid();
+ exit(1);
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ /* Continue the child ignoring the SIGSTOP and tracing system calls. */
+ REQUIRE_EQ(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0), 0);
+
+ /* The second wait() should report a system call entry for getpid(). */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE);
+
+ /* Kill the child process. */
+ REQUIRE_EQ(ptrace(PT_KILL, fpid, 0, 0), 0);
+
+ /* The last wait() should report the SIGKILL. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSIGNALED(status));
+ REQUIRE_EQ(WTERMSIG(status), SIGKILL);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+/*
+ * Verify that no more events are reported after PT_KILL except for the
+ * process exit when killing a multithreaded process.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__PT_KILL_threads);
+ATF_TC_BODY(ptrace__PT_KILL_threads, tc)
+{
+ struct ptrace_lwpinfo pl;
+ pid_t fpid, wpid;
+ lwpid_t main_lwp;
+ int status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ simple_thread_main();
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl,
+ sizeof(pl)) != -1);
+ main_lwp = pl.pl_lwpid;
+
+ REQUIRE_EQ(ptrace(PT_LWP_EVENTS, wpid, NULL, 1), 0);
+
+ /* Continue the child ignoring the SIGSTOP. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* The first event should be for the child thread's birth. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ REQUIRE_EQ((pl.pl_flags & (PL_FLAG_BORN | PL_FLAG_SCX)),
+ (PL_FLAG_BORN | PL_FLAG_SCX));
+ ATF_REQUIRE(pl.pl_lwpid != main_lwp);
+
+ /* Kill the child process. */
+ REQUIRE_EQ(ptrace(PT_KILL, fpid, 0, 0), 0);
+
+ /* The last wait() should report the SIGKILL. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSIGNALED(status));
+ REQUIRE_EQ(WTERMSIG(status), SIGKILL);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+static void *
+mask_usr1_thread(void *arg)
+{
+ pthread_barrier_t *pbarrier;
+ sigset_t sigmask;
+
+ pbarrier = (pthread_barrier_t*)arg;
+
+ sigemptyset(&sigmask);
+ sigaddset(&sigmask, SIGUSR1);
+ CHILD_REQUIRE_EQ(pthread_sigmask(SIG_BLOCK, &sigmask, NULL), 0);
+
+ /* Sync up with other thread after sigmask updated. */
+ pthread_barrier_wait(pbarrier);
+
+ for (;;)
+ sleep(60);
+
+ return (NULL);
+}
+
+/*
+ * Verify that the SIGKILL from PT_KILL takes priority over other signals
+ * and prevents spurious stops due to those other signals.
+ */
+ATF_TC(ptrace__PT_KILL_competing_signal);
+ATF_TC_HEAD(ptrace__PT_KILL_competing_signal, tc)
+{
+
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(ptrace__PT_KILL_competing_signal, tc)
+{
+ pid_t fpid, wpid;
+ int status;
+ cpuset_t setmask;
+ pthread_t t;
+ pthread_barrier_t barrier;
+ struct sched_param sched_param;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ /* Bind to one CPU so only one thread at a time will run. */
+ CPU_ZERO(&setmask);
+ CPU_SET(0, &setmask);
+ cpusetid_t setid;
+ CHILD_REQUIRE_EQ(cpuset(&setid), 0);
+ CHILD_REQUIRE(cpuset_setaffinity(CPU_LEVEL_CPUSET,
+ CPU_WHICH_CPUSET, setid, sizeof(setmask), &setmask) == 0);
+
+ CHILD_REQUIRE_EQ(pthread_barrier_init(&barrier, NULL, 2), 0);
+
+ CHILD_REQUIRE(pthread_create(&t, NULL, mask_usr1_thread,
+ (void*)&barrier) == 0);
+
+ /*
+ * Give the main thread higher priority. The test always
+ * assumes that, if both threads are able to run, the main
+ * thread runs first.
+ */
+ sched_param.sched_priority =
+ (sched_get_priority_max(SCHED_FIFO) +
+ sched_get_priority_min(SCHED_FIFO)) / 2;
+ CHILD_REQUIRE(pthread_setschedparam(pthread_self(),
+ SCHED_FIFO, &sched_param) == 0);
+ sched_param.sched_priority -= 1;
+ CHILD_REQUIRE(pthread_setschedparam(t, SCHED_FIFO,
+ &sched_param) == 0);
+
+ sigset_t sigmask;
+ sigemptyset(&sigmask);
+ sigaddset(&sigmask, SIGUSR2);
+ CHILD_REQUIRE_EQ(pthread_sigmask(SIG_BLOCK, &sigmask, NULL), 0);
+
+ /* Sync up with other thread after sigmask updated. */
+ pthread_barrier_wait(&barrier);
+
+ trace_me();
+
+ for (;;)
+ sleep(60);
+
+ exit(1);
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ /* Continue the child ignoring the SIGSTOP. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* Send a signal that only the second thread can handle. */
+ REQUIRE_EQ(kill(fpid, SIGUSR2), 0);
+
+ /* The second wait() should report the SIGUSR2. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGUSR2);
+
+ /* Send a signal that only the first thread can handle. */
+ REQUIRE_EQ(kill(fpid, SIGUSR1), 0);
+
+ /* Replace the SIGUSR2 with a kill. */
+ REQUIRE_EQ(ptrace(PT_KILL, fpid, 0, 0), 0);
+
+ /* The last wait() should report the SIGKILL (not the SIGUSR signal). */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSIGNALED(status));
+ REQUIRE_EQ(WTERMSIG(status), SIGKILL);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+/*
+ * Verify that the SIGKILL from PT_KILL takes priority over other stop events
+ * and prevents spurious stops caused by those events.
+ */
+ATF_TC(ptrace__PT_KILL_competing_stop);
+ATF_TC_HEAD(ptrace__PT_KILL_competing_stop, tc)
+{
+
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(ptrace__PT_KILL_competing_stop, tc)
+{
+ pid_t fpid, wpid;
+ int status;
+ cpuset_t setmask;
+ pthread_t t;
+ pthread_barrier_t barrier;
+ lwpid_t main_lwp;
+ struct ptrace_lwpinfo pl;
+ struct sched_param sched_param;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+
+ /* Bind to one CPU so only one thread at a time will run. */
+ CPU_ZERO(&setmask);
+ CPU_SET(0, &setmask);
+ cpusetid_t setid;
+ CHILD_REQUIRE_EQ(cpuset(&setid), 0);
+ CHILD_REQUIRE(cpuset_setaffinity(CPU_LEVEL_CPUSET,
+ CPU_WHICH_CPUSET, setid, sizeof(setmask), &setmask) == 0);
+
+ CHILD_REQUIRE_EQ(pthread_barrier_init(&barrier, NULL, 2), 0);
+
+ CHILD_REQUIRE(pthread_create(&t, NULL, mask_usr1_thread,
+ (void*)&barrier) == 0);
+
+ /*
+ * Give the main thread higher priority. The test always
+ * assumes that, if both threads are able to run, the main
+ * thread runs first.
+ */
+ sched_param.sched_priority =
+ (sched_get_priority_max(SCHED_FIFO) +
+ sched_get_priority_min(SCHED_FIFO)) / 2;
+ CHILD_REQUIRE(pthread_setschedparam(pthread_self(),
+ SCHED_FIFO, &sched_param) == 0);
+ sched_param.sched_priority -= 1;
+ CHILD_REQUIRE(pthread_setschedparam(t, SCHED_FIFO,
+ &sched_param) == 0);
+
+ sigset_t sigmask;
+ sigemptyset(&sigmask);
+ sigaddset(&sigmask, SIGUSR2);
+ CHILD_REQUIRE_EQ(pthread_sigmask(SIG_BLOCK, &sigmask, NULL), 0);
+
+ /* Sync up with other thread after sigmask updated. */
+ pthread_barrier_wait(&barrier);
+
+ /* Sync up with the test before doing the getpid(). */
+ raise(SIGSTOP);
+
+ getpid();
+ exit(1);
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ main_lwp = pl.pl_lwpid;
+
+ /* Continue the child ignoring the SIGSTOP and tracing system calls. */
+ REQUIRE_EQ(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0), 0);
+
+ /*
+ * Continue until child is done with setup, which is indicated with
+ * SIGSTOP. Ignore system calls in the meantime.
+ */
+ for (;;) {
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ if (WSTOPSIG(status) == SIGTRAP) {
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl,
+ sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX));
+ } else {
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+ break;
+ }
+ REQUIRE_EQ(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0), 0);
+ }
+
+ /* Proceed, allowing main thread to hit syscall entry for getpid(). */
+ REQUIRE_EQ(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0), 0);
+
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl,
+ sizeof(pl)) != -1);
+ REQUIRE_EQ(pl.pl_lwpid, main_lwp);
+ ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE);
+ /* Prevent the main thread from hitting its syscall exit for now. */
+ REQUIRE_EQ(ptrace(PT_SUSPEND, main_lwp, 0, 0), 0);
+
+ /*
+ * Proceed, allowing second thread to hit syscall exit for
+ * pthread_barrier_wait().
+ */
+ REQUIRE_EQ(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0), 0);
+
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl,
+ sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_lwpid != main_lwp);
+ ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCX);
+
+ /* Send a signal that only the second thread can handle. */
+ REQUIRE_EQ(kill(fpid, SIGUSR2), 0);
+
+ REQUIRE_EQ(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0), 0);
+
+ /* The next wait() should report the SIGUSR2. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGUSR2);
+
+ /* Allow the main thread to try to finish its system call. */
+ REQUIRE_EQ(ptrace(PT_RESUME, main_lwp, 0, 0), 0);
+
+ /*
+ * At this point, the main thread is in the middle of a system call and
+ * has been resumed. The second thread has taken a SIGUSR2 which will
+ * be replaced with a SIGKILL below. The main thread will get to run
+ * first. It should notice the kill request (even though the signal
+ * replacement occurred in the other thread) and exit accordingly. It
+ * should not stop for the system call exit event.
+ */
+
+ /* Replace the SIGUSR2 with a kill. */
+ REQUIRE_EQ(ptrace(PT_KILL, fpid, 0, 0), 0);
+
+ /* The last wait() should report the SIGKILL (not a syscall exit). */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSIGNALED(status));
+ REQUIRE_EQ(WTERMSIG(status), SIGKILL);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+static void
+sigusr1_handler(int sig)
+{
+
+ CHILD_REQUIRE_EQ(sig, SIGUSR1);
+ _exit(2);
+}
+
+/*
+ * Verify that even if the signal queue is full for a child process,
+ * a PT_KILL will kill the process.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__PT_KILL_with_signal_full_sigqueue);
+ATF_TC_BODY(ptrace__PT_KILL_with_signal_full_sigqueue, tc)
+{
+ pid_t fpid, wpid;
+ int status;
+ int max_pending_per_proc;
+ size_t len;
+ int i;
+
+ ATF_REQUIRE(signal(SIGUSR1, sigusr1_handler) != SIG_ERR);
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ exit(1);
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ len = sizeof(max_pending_per_proc);
+ ATF_REQUIRE(sysctlbyname("kern.sigqueue.max_pending_per_proc",
+ &max_pending_per_proc, &len, NULL, 0) == 0);
+
+ /* Fill the signal queue. */
+ for (i = 0; i < max_pending_per_proc; ++i)
+ REQUIRE_EQ(kill(fpid, SIGUSR1), 0);
+
+ /* Kill the child process. */
+ REQUIRE_EQ(ptrace(PT_KILL, fpid, 0, 0), 0);
+
+ /* The last wait() should report the SIGKILL. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSIGNALED(status));
+ REQUIRE_EQ(WTERMSIG(status), SIGKILL);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+/*
+ * Verify that when stopped at a system call entry, a signal can be
+ * requested with PT_CONTINUE which will be delivered once the system
+ * call is complete.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_signal_system_call_entry);
+ATF_TC_BODY(ptrace__PT_CONTINUE_with_signal_system_call_entry, tc)
+{
+ struct ptrace_lwpinfo pl;
+ pid_t fpid, wpid;
+ int status;
+
+ ATF_REQUIRE(signal(SIGUSR1, sigusr1_handler) != SIG_ERR);
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ getpid();
+ exit(1);
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ /* Continue the child ignoring the SIGSTOP and tracing system calls. */
+ REQUIRE_EQ(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0), 0);
+
+ /* The second wait() should report a system call entry for getpid(). */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE);
+
+ /* Continue the child process with a signal. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1), 0);
+
+ for (;;) {
+ /*
+ * The last wait() should report exit 2, i.e., a normal _exit
+ * from the signal handler. In the meantime, catch and proceed
+ * past any syscall stops.
+ */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) {
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX));
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+ } else {
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 2);
+ break;
+ }
+ }
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+static void
+sigusr1_counting_handler(int sig)
+{
+ static int counter = 0;
+
+ CHILD_REQUIRE_EQ(sig, SIGUSR1);
+ counter++;
+ if (counter == 2)
+ _exit(2);
+}
+
+/*
+ * Verify that, when continuing from a stop at system call entry and exit,
+ * a signal can be requested from both stops, and both will be delivered when
+ * the system call is complete.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_signal_system_call_entry_and_exit);
+ATF_TC_BODY(ptrace__PT_CONTINUE_with_signal_system_call_entry_and_exit, tc)
+{
+ struct ptrace_lwpinfo pl;
+ pid_t fpid, wpid;
+ int status;
+
+ ATF_REQUIRE(signal(SIGUSR1, sigusr1_counting_handler) != SIG_ERR);
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ getpid();
+ exit(1);
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ /* Continue the child ignoring the SIGSTOP and tracing system calls. */
+ REQUIRE_EQ(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0), 0);
+
+ /* The second wait() should report a system call entry for getpid(). */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE);
+
+ /* Continue the child process with a signal. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1), 0);
+
+ /* The third wait() should report a system call exit for getpid(). */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCX);
+
+ /* Continue the child process with a signal. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1), 0);
+
+ for (;;) {
+ /*
+ * The last wait() should report exit 2, i.e., a normal _exit
+ * from the signal handler. In the meantime, catch and proceed
+ * past any syscall stops.
+ */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) {
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX));
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+ } else {
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 2);
+ break;
+ }
+ }
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+/*
+ * Verify that even if the signal queue is full for a child process,
+ * a PT_CONTINUE with a signal will not result in loss of that signal.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_signal_full_sigqueue);
+ATF_TC_BODY(ptrace__PT_CONTINUE_with_signal_full_sigqueue, tc)
+{
+ pid_t fpid, wpid;
+ int status;
+ int max_pending_per_proc;
+ size_t len;
+ int i;
+
+ ATF_REQUIRE(signal(SIGUSR2, handler) != SIG_ERR);
+ ATF_REQUIRE(signal(SIGUSR1, sigusr1_handler) != SIG_ERR);
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ exit(1);
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ len = sizeof(max_pending_per_proc);
+ ATF_REQUIRE(sysctlbyname("kern.sigqueue.max_pending_per_proc",
+ &max_pending_per_proc, &len, NULL, 0) == 0);
+
+ /* Fill the signal queue. */
+ for (i = 0; i < max_pending_per_proc; ++i)
+ REQUIRE_EQ(kill(fpid, SIGUSR2), 0);
+
+ /* Continue with signal. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1), 0);
+
+ for (;;) {
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ if (WIFSTOPPED(status)) {
+ REQUIRE_EQ(WSTOPSIG(status), SIGUSR2);
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+ } else {
+ /*
+ * The last wait() should report normal _exit from the
+ * SIGUSR1 handler.
+ */
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 2);
+ break;
+ }
+ }
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+static sem_t sigusr1_sem;
+static int got_usr1;
+
+static void
+sigusr1_sempost_handler(int sig __unused)
+{
+
+ got_usr1++;
+ CHILD_REQUIRE_EQ(sem_post(&sigusr1_sem), 0);
+}
+
+/*
+ * Verify that even if the signal queue is full for a child process,
+ * and the signal is masked, a PT_CONTINUE with a signal will not
+ * result in loss of that signal.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_signal_masked_full_sigqueue);
+ATF_TC_BODY(ptrace__PT_CONTINUE_with_signal_masked_full_sigqueue, tc)
+{
+ struct ptrace_lwpinfo pl;
+ pid_t fpid, wpid;
+ int status, err;
+ int max_pending_per_proc;
+ size_t len;
+ int i;
+ sigset_t sigmask;
+
+ ATF_REQUIRE(signal(SIGUSR2, handler) != SIG_ERR);
+ REQUIRE_EQ(sem_init(&sigusr1_sem, 0, 0), 0);
+ ATF_REQUIRE(signal(SIGUSR1, sigusr1_sempost_handler) != SIG_ERR);
+
+ got_usr1 = 0;
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ CHILD_REQUIRE_EQ(sigemptyset(&sigmask), 0);
+ CHILD_REQUIRE_EQ(sigaddset(&sigmask, SIGUSR1), 0);
+ CHILD_REQUIRE_EQ(sigprocmask(SIG_BLOCK, &sigmask, NULL), 0);
+
+ trace_me();
+ CHILD_REQUIRE_EQ(got_usr1, 0);
+
+ /* Allow the pending SIGUSR1 in now. */
+ CHILD_REQUIRE_EQ(sigprocmask(SIG_UNBLOCK, &sigmask, NULL), 0);
+ /* Wait to receive the SIGUSR1. */
+ do {
+ err = sem_wait(&sigusr1_sem);
+ CHILD_REQUIRE(err == 0 || errno == EINTR);
+ } while (err != 0 && errno == EINTR);
+ CHILD_REQUIRE_EQ(got_usr1, 1);
+ exit(1);
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ len = sizeof(max_pending_per_proc);
+ ATF_REQUIRE(sysctlbyname("kern.sigqueue.max_pending_per_proc",
+ &max_pending_per_proc, &len, NULL, 0) == 0);
+
+ /* Fill the signal queue. */
+ for (i = 0; i < max_pending_per_proc; ++i)
+ REQUIRE_EQ(kill(fpid, SIGUSR2), 0);
+
+ /* Continue with signal. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1), 0);
+
+ /* Collect and ignore all of the SIGUSR2. */
+ for (i = 0; i < max_pending_per_proc; ++i) {
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGUSR2);
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+ }
+
+ /* Now our PT_CONTINUE'd SIGUSR1 should cause a stop after unmask. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGUSR1);
+ ATF_REQUIRE(ptrace(PT_LWPINFO, fpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGUSR1);
+
+ /* Continue the child, ignoring the SIGUSR1. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* The last wait() should report exit after receiving SIGUSR1. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 1);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+/*
+ * Verify that, after stopping due to a signal, that signal can be
+ * replaced with another signal.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_change_sig);
+ATF_TC_BODY(ptrace__PT_CONTINUE_change_sig, tc)
+{
+ struct ptrace_lwpinfo pl;
+ pid_t fpid, wpid;
+ int status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ sleep(20);
+ exit(1);
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* Send a signal without ptrace. */
+ REQUIRE_EQ(kill(fpid, SIGINT), 0);
+
+ /* The second wait() should report a SIGINT was received. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGINT);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI);
+ REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGINT);
+
+ /* Continue the child process with a different signal. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGTERM), 0);
+
+ /*
+ * The last wait() should report having died due to the new
+ * signal, SIGTERM.
+ */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSIGNALED(status));
+ REQUIRE_EQ(WTERMSIG(status), SIGTERM);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+/*
+ * Verify that a signal can be passed through to the child even when there
+ * was no true signal originally. Such cases arise when a SIGTRAP is
+ * invented for e.g, system call stops.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_sigtrap_system_call_entry);
+ATF_TC_BODY(ptrace__PT_CONTINUE_with_sigtrap_system_call_entry, tc)
+{
+ struct ptrace_lwpinfo pl;
+ struct rlimit rl;
+ pid_t fpid, wpid;
+ int status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ /* SIGTRAP expected to cause exit on syscall entry. */
+ rl.rlim_cur = rl.rlim_max = 0;
+ REQUIRE_EQ(setrlimit(RLIMIT_CORE, &rl), 0);
+ getpid();
+ exit(1);
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ /* Continue the child ignoring the SIGSTOP and tracing system calls. */
+ REQUIRE_EQ(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0), 0);
+
+ /* The second wait() should report a system call entry for getpid(). */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE);
+
+ /* Continue the child process with a SIGTRAP. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGTRAP), 0);
+
+ for (;;) {
+ /*
+ * The last wait() should report exit due to SIGTRAP. In the
+ * meantime, catch and proceed past any syscall stops.
+ */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) {
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX));
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+ } else {
+ ATF_REQUIRE(WIFSIGNALED(status));
+ REQUIRE_EQ(WTERMSIG(status), SIGTRAP);
+ break;
+ }
+ }
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+/*
+ * A mixed bag PT_CONTINUE with signal test.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_signal_mix);
+ATF_TC_BODY(ptrace__PT_CONTINUE_with_signal_mix, tc)
+{
+ struct ptrace_lwpinfo pl;
+ pid_t fpid, wpid;
+ int status;
+
+ ATF_REQUIRE(signal(SIGUSR1, sigusr1_counting_handler) != SIG_ERR);
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ getpid();
+ exit(1);
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ /* Continue the child ignoring the SIGSTOP and tracing system calls. */
+ REQUIRE_EQ(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0), 0);
+
+ /* The second wait() should report a system call entry for getpid(). */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE);
+
+ /* Continue with the first SIGUSR1. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1), 0);
+
+ /* The next wait() should report a system call exit for getpid(). */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCX);
+
+ /* Send an ABRT without ptrace. */
+ REQUIRE_EQ(kill(fpid, SIGABRT), 0);
+
+ /* Continue normally. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* The next wait() should report the SIGABRT. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGABRT);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI);
+ REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGABRT);
+
+ /* Continue, replacing the SIGABRT with another SIGUSR1. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1), 0);
+
+ for (;;) {
+ /*
+ * The last wait() should report exit 2, i.e., a normal _exit
+ * from the signal handler. In the meantime, catch and proceed
+ * past any syscall stops.
+ */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) {
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX));
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+ } else {
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 2);
+ break;
+ }
+ }
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+/*
+ * Verify a signal delivered by ptrace is noticed by kevent(2).
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_signal_kqueue);
+ATF_TC_BODY(ptrace__PT_CONTINUE_with_signal_kqueue, tc)
+{
+ pid_t fpid, wpid;
+ int status, kq, nevents;
+ struct kevent kev;
+
+ ATF_REQUIRE(signal(SIGUSR1, SIG_IGN) != SIG_ERR);
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ CHILD_REQUIRE((kq = kqueue()) > 0);
+ EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, 0);
+ CHILD_REQUIRE_EQ(kevent(kq, &kev, 1, NULL, 0, NULL), 0);
+
+ trace_me();
+
+ for (;;) {
+ nevents = kevent(kq, NULL, 0, &kev, 1, NULL);
+ if (nevents == -1 && errno == EINTR)
+ continue;
+ CHILD_REQUIRE(nevents > 0);
+ CHILD_REQUIRE_EQ(kev.filter, EVFILT_SIGNAL);
+ CHILD_REQUIRE_EQ(kev.ident, (uintptr_t)SIGUSR1);
+ break;
+ }
+
+ exit(1);
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ /* Continue with the SIGUSR1. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1), 0);
+
+ /*
+ * The last wait() should report normal exit with code 1.
+ */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 1);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+static void *
+signal_thread(void *arg)
+{
+ int err;
+ sigset_t sigmask;
+
+ pthread_barrier_t *pbarrier = (pthread_barrier_t*)arg;
+
+ /* Wait for this thread to receive a SIGUSR1. */
+ do {
+ err = sem_wait(&sigusr1_sem);
+ CHILD_REQUIRE(err == 0 || errno == EINTR);
+ } while (err != 0 && errno == EINTR);
+
+ /* Free our companion thread from the barrier. */
+ pthread_barrier_wait(pbarrier);
+
+ /*
+ * Swap ignore duties; the next SIGUSR1 should go to the
+ * other thread.
+ */
+ CHILD_REQUIRE_EQ(sigemptyset(&sigmask), 0);
+ CHILD_REQUIRE_EQ(sigaddset(&sigmask, SIGUSR1), 0);
+ CHILD_REQUIRE_EQ(pthread_sigmask(SIG_BLOCK, &sigmask, NULL), 0);
+
+ /* Sync up threads after swapping signal masks. */
+ pthread_barrier_wait(pbarrier);
+
+ /* Wait until our companion has received its SIGUSR1. */
+ pthread_barrier_wait(pbarrier);
+
+ return (NULL);
+}
+
+/*
+ * Verify that a traced process with blocked signal received the
+ * signal from kill() once unmasked.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__killed_with_sigmask);
+ATF_TC_BODY(ptrace__killed_with_sigmask, tc)
+{
+ struct ptrace_lwpinfo pl;
+ pid_t fpid, wpid;
+ int status, err;
+ sigset_t sigmask;
+
+ REQUIRE_EQ(sem_init(&sigusr1_sem, 0, 0), 0);
+ ATF_REQUIRE(signal(SIGUSR1, sigusr1_sempost_handler) != SIG_ERR);
+ got_usr1 = 0;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ CHILD_REQUIRE_EQ(sigemptyset(&sigmask), 0);
+ CHILD_REQUIRE_EQ(sigaddset(&sigmask, SIGUSR1), 0);
+ CHILD_REQUIRE_EQ(sigprocmask(SIG_BLOCK, &sigmask, NULL), 0);
+
+ trace_me();
+ CHILD_REQUIRE_EQ(got_usr1, 0);
+
+ /* Allow the pending SIGUSR1 in now. */
+ CHILD_REQUIRE_EQ(sigprocmask(SIG_UNBLOCK, &sigmask, NULL), 0);
+ /* Wait to receive a SIGUSR1. */
+ do {
+ err = sem_wait(&sigusr1_sem);
+ CHILD_REQUIRE(err == 0 || errno == EINTR);
+ } while (err != 0 && errno == EINTR);
+ CHILD_REQUIRE_EQ(got_usr1, 1);
+ exit(1);
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+ ATF_REQUIRE(ptrace(PT_LWPINFO, fpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGSTOP);
+
+ /* Send blocked SIGUSR1 which should cause a stop. */
+ REQUIRE_EQ(kill(fpid, SIGUSR1), 0);
+
+ /* Continue the child ignoring the SIGSTOP. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* The next wait() should report the kill(SIGUSR1) was received. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGUSR1);
+ ATF_REQUIRE(ptrace(PT_LWPINFO, fpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGUSR1);
+
+ /* Continue the child, allowing in the SIGUSR1. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1), 0);
+
+ /* The last wait() should report normal exit with code 1. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 1);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+/*
+ * Verify that a traced process with blocked signal received the
+ * signal from PT_CONTINUE once unmasked.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_sigmask);
+ATF_TC_BODY(ptrace__PT_CONTINUE_with_sigmask, tc)
+{
+ struct ptrace_lwpinfo pl;
+ pid_t fpid, wpid;
+ int status, err;
+ sigset_t sigmask;
+
+ REQUIRE_EQ(sem_init(&sigusr1_sem, 0, 0), 0);
+ ATF_REQUIRE(signal(SIGUSR1, sigusr1_sempost_handler) != SIG_ERR);
+ got_usr1 = 0;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ CHILD_REQUIRE_EQ(sigemptyset(&sigmask), 0);
+ CHILD_REQUIRE_EQ(sigaddset(&sigmask, SIGUSR1), 0);
+ CHILD_REQUIRE_EQ(sigprocmask(SIG_BLOCK, &sigmask, NULL), 0);
+
+ trace_me();
+ CHILD_REQUIRE_EQ(got_usr1, 0);
+
+ /* Allow the pending SIGUSR1 in now. */
+ CHILD_REQUIRE_EQ(sigprocmask(SIG_UNBLOCK, &sigmask, NULL), 0);
+ /* Wait to receive a SIGUSR1. */
+ do {
+ err = sem_wait(&sigusr1_sem);
+ CHILD_REQUIRE(err == 0 || errno == EINTR);
+ } while (err != 0 && errno == EINTR);
+
+ CHILD_REQUIRE_EQ(got_usr1, 1);
+ exit(1);
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+ ATF_REQUIRE(ptrace(PT_LWPINFO, fpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGSTOP);
+
+ /* Continue the child replacing SIGSTOP with SIGUSR1. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1), 0);
+
+ /* The next wait() should report the SIGUSR1 was received. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGUSR1);
+ ATF_REQUIRE(ptrace(PT_LWPINFO, fpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGUSR1);
+
+ /* Continue the child, ignoring the SIGUSR1. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* The last wait() should report normal exit with code 1. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 1);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+/*
+ * Verify that if ptrace stops due to a signal but continues with
+ * a different signal that the new signal is routed to a thread
+ * that can accept it, and that the thread is awakened by the signal
+ * in a timely manner.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_signal_thread_sigmask);
+ATF_TC_BODY(ptrace__PT_CONTINUE_with_signal_thread_sigmask, tc)
+{
+ pid_t fpid, wpid;
+ int status, err;
+ pthread_t t;
+ sigset_t sigmask;
+ pthread_barrier_t barrier;
+
+ REQUIRE_EQ(pthread_barrier_init(&barrier, NULL, 2), 0);
+ REQUIRE_EQ(sem_init(&sigusr1_sem, 0, 0), 0);
+ ATF_REQUIRE(signal(SIGUSR1, sigusr1_sempost_handler) != SIG_ERR);
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ CHILD_REQUIRE_EQ(pthread_create(&t, NULL, signal_thread,
+ (void *)&barrier), 0);
+
+ /* The other thread should receive the first SIGUSR1. */
+ CHILD_REQUIRE_EQ(sigemptyset(&sigmask), 0);
+ CHILD_REQUIRE_EQ(sigaddset(&sigmask, SIGUSR1), 0);
+ CHILD_REQUIRE_EQ(pthread_sigmask(SIG_BLOCK, &sigmask, NULL), 0);
+
+ trace_me();
+
+ /* Wait until other thread has received its SIGUSR1. */
+ pthread_barrier_wait(&barrier);
+
+ /*
+ * Swap ignore duties; the next SIGUSR1 should go to this
+ * thread.
+ */
+ CHILD_REQUIRE_EQ(pthread_sigmask(SIG_UNBLOCK, &sigmask, NULL),
+ 0);
+
+ /* Sync up threads after swapping signal masks. */
+ pthread_barrier_wait(&barrier);
+
+ /*
+ * Sync up with test code; we're ready for the next SIGUSR1
+ * now.
+ */
+ raise(SIGSTOP);
+
+ /* Wait for this thread to receive a SIGUSR1. */
+ do {
+ err = sem_wait(&sigusr1_sem);
+ CHILD_REQUIRE(err == 0 || errno == EINTR);
+ } while (err != 0 && errno == EINTR);
+
+ /* Free the other thread from the barrier. */
+ pthread_barrier_wait(&barrier);
+
+ CHILD_REQUIRE_EQ(pthread_join(t, NULL), 0);
+
+ exit(1);
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ /* Continue the child ignoring the SIGSTOP. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /*
+ * Send a signal without ptrace that either thread will accept (USR2,
+ * in this case).
+ */
+ REQUIRE_EQ(kill(fpid, SIGUSR2), 0);
+
+ /* The second wait() should report a SIGUSR2 was received. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGUSR2);
+
+ /* Continue the child, changing the signal to USR1. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1), 0);
+
+ /* The next wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ /* Continue the child ignoring the SIGSTOP. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ REQUIRE_EQ(kill(fpid, SIGUSR2), 0);
+
+ /* The next wait() should report a SIGUSR2 was received. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGUSR2);
+
+ /* Continue the child, changing the signal to USR1. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1), 0);
+
+ /* The last wait() should report normal exit with code 1. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 1);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+/*
+ * Verify that PT_GETREGSET returns registers and PT_SETREGSET updates them.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__PT_REGSET);
+ATF_TC_BODY(ptrace__PT_REGSET, tc)
+{
+#if defined(__aarch64__)
+ struct arm64_addr_mask addr_mask;
+#endif
+ struct prstatus prstatus;
+ struct iovec vec;
+ pid_t child, wpid;
+ int status;
+
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ trace_me();
+ exit(1);
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(child, &status, 0);
+ REQUIRE_EQ(wpid, child);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ /* Check the size is returned when vec.iov_base is NULL */
+ vec.iov_base = NULL;
+ vec.iov_len = 0;
+ ATF_REQUIRE(ptrace(PT_GETREGSET, wpid, (caddr_t)&vec, NT_PRSTATUS) !=
+ -1);
+ ATF_REQUIRE(vec.iov_len == sizeof(prstatus));
+ ATF_REQUIRE(vec.iov_base == NULL);
+
+ /* Read the registers. */
+ memset(&prstatus, 0, sizeof(prstatus));
+ vec.iov_base = &prstatus;
+ ATF_REQUIRE(ptrace(PT_GETREGSET, wpid, (caddr_t)&vec, NT_PRSTATUS) !=
+ -1);
+ ATF_REQUIRE(vec.iov_len == sizeof(prstatus));
+ ATF_REQUIRE(vec.iov_base == &prstatus);
+ ATF_REQUIRE(prstatus.pr_statussz == sizeof(prstatus));
+
+ /* Write the registers back. */
+ ATF_REQUIRE(ptrace(PT_SETREGSET, wpid, (caddr_t)&vec, NT_PRSTATUS) !=
+ -1);
+
+#if defined(__aarch64__)
+ vec.iov_base = &addr_mask;
+ vec.iov_len = sizeof(addr_mask);
+ ATF_REQUIRE(ptrace(PT_GETREGSET, wpid, (caddr_t)&vec,
+ NT_ARM_ADDR_MASK) != -1);
+ REQUIRE_EQ(addr_mask.code, addr_mask.data);
+ ATF_REQUIRE(addr_mask.code == 0 ||
+ addr_mask.code == 0xff7f000000000000UL);
+#endif
+
+ REQUIRE_EQ(ptrace(PT_CONTINUE, child, (caddr_t)1, 0), 0);
+
+ /* The second wait() should report the exit status. */
+ wpid = waitpid(child, &status, 0);
+ REQUIRE_EQ(wpid, child);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 1);
+
+ /* The child should no longer exist. */
+ wpid = waitpid(child, &status, 0);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+static void *
+raise_sigstop_thread(void *arg __unused)
+{
+
+ raise(SIGSTOP);
+ return NULL;
+}
+
+static void *
+sleep_thread(void *arg __unused)
+{
+
+ sleep(60);
+ return NULL;
+}
+
+static void
+terminate_with_pending_sigstop(bool sigstop_from_main_thread)
+{
+ pid_t fpid, wpid;
+ int status, i;
+ cpuset_t setmask;
+ cpusetid_t setid;
+ pthread_t t;
+
+ /*
+ * Become the reaper for this process tree. We need to be able to check
+ * that both child and grandchild have died.
+ */
+ REQUIRE_EQ(procctl(P_PID, getpid(), PROC_REAP_ACQUIRE, NULL), 0);
+
+ fpid = fork();
+ ATF_REQUIRE(fpid >= 0);
+ if (fpid == 0) {
+ fpid = fork();
+ CHILD_REQUIRE(fpid >= 0);
+ if (fpid == 0) {
+ trace_me();
+
+ /* Pin to CPU 0 to serialize thread execution. */
+ CPU_ZERO(&setmask);
+ CPU_SET(0, &setmask);
+ CHILD_REQUIRE_EQ(cpuset(&setid), 0);
+ CHILD_REQUIRE(cpuset_setaffinity(CPU_LEVEL_CPUSET,
+ CPU_WHICH_CPUSET, setid,
+ sizeof(setmask), &setmask) == 0);
+
+ if (sigstop_from_main_thread) {
+ /*
+ * We expect the SIGKILL sent when our parent
+ * dies to be delivered to the new thread.
+ * Raise the SIGSTOP in this thread so the
+ * threads compete.
+ */
+ CHILD_REQUIRE(pthread_create(&t, NULL,
+ sleep_thread, NULL) == 0);
+ raise(SIGSTOP);
+ } else {
+ /*
+ * We expect the SIGKILL to be delivered to
+ * this thread. After creating the new thread,
+ * just get off the CPU so the other thread can
+ * raise the SIGSTOP.
+ */
+ CHILD_REQUIRE(pthread_create(&t, NULL,
+ raise_sigstop_thread, NULL) == 0);
+ sleep(60);
+ }
+
+ exit(0);
+ }
+ /* First stop is trace_me() immediately after fork. */
+ wpid = waitpid(fpid, &status, 0);
+ CHILD_REQUIRE_EQ(wpid, fpid);
+ CHILD_REQUIRE(WIFSTOPPED(status));
+ CHILD_REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ CHILD_REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* Second stop is from the raise(SIGSTOP). */
+ wpid = waitpid(fpid, &status, 0);
+ CHILD_REQUIRE_EQ(wpid, fpid);
+ CHILD_REQUIRE(WIFSTOPPED(status));
+ CHILD_REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ /*
+ * Terminate tracing process without detaching. Our child
+ * should be killed.
+ */
+ exit(0);
+ }
+
+ /*
+ * We should get a normal exit from our immediate child and a SIGKILL
+ * exit from our grandchild. The latter case is the interesting one.
+ * Our grandchild should not have stopped due to the SIGSTOP that was
+ * left dangling when its parent died.
+ */
+ for (i = 0; i < 2; ++i) {
+ wpid = wait(&status);
+ if (wpid == fpid) {
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 0);
+ } else {
+ ATF_REQUIRE(WIFSIGNALED(status));
+ REQUIRE_EQ(WTERMSIG(status), SIGKILL);
+ }
+ }
+}
+
+/*
+ * These two tests ensure that if the tracing process exits without detaching
+ * just after the child received a SIGSTOP, the child is cleanly killed and
+ * doesn't go to sleep due to the SIGSTOP. The parent's death will send a
+ * SIGKILL to the child. If the SIGKILL and the SIGSTOP are handled by
+ * different threads, the SIGKILL must win. There are two variants of this
+ * test, designed to catch the case where the SIGKILL is delivered to the
+ * younger thread (the first test) and the case where the SIGKILL is delivered
+ * to the older thread (the second test). This behavior has changed in the
+ * past, so make no assumption.
+ */
+ATF_TC(ptrace__parent_terminate_with_pending_sigstop1);
+ATF_TC_HEAD(ptrace__parent_terminate_with_pending_sigstop1, tc)
+{
+
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(ptrace__parent_terminate_with_pending_sigstop1, tc)
+{
+
+ terminate_with_pending_sigstop(true);
+}
+
+ATF_TC(ptrace__parent_terminate_with_pending_sigstop2);
+ATF_TC_HEAD(ptrace__parent_terminate_with_pending_sigstop2, tc)
+{
+
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(ptrace__parent_terminate_with_pending_sigstop2, tc)
+{
+
+ terminate_with_pending_sigstop(false);
+}
+
+/*
+ * Verify that after ptrace() discards a SIGKILL signal, the event mask
+ * is not modified.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__event_mask_sigkill_discard);
+ATF_TC_BODY(ptrace__event_mask_sigkill_discard, tc)
+{
+ struct ptrace_lwpinfo pl;
+ pid_t fpid, wpid;
+ int status, event_mask, new_event_mask;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ raise(SIGSTOP);
+ exit(0);
+ }
+
+ /* The first wait() should report the stop from trace_me(). */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ /* Set several unobtrusive event bits. */
+ event_mask = PTRACE_EXEC | PTRACE_FORK | PTRACE_LWP;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, wpid, (caddr_t)&event_mask,
+ sizeof(event_mask)) == 0);
+
+ /* Send a SIGKILL without using ptrace. */
+ REQUIRE_EQ(kill(fpid, SIGKILL), 0);
+
+ /* Continue the child ignoring the SIGSTOP. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* The next stop should be due to the SIGKILL. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGKILL);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI);
+ REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGKILL);
+
+ /* Continue the child ignoring the SIGKILL. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* The next wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ /* Check the current event mask. It should not have changed. */
+ new_event_mask = 0;
+ ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, wpid, (caddr_t)&new_event_mask,
+ sizeof(new_event_mask)) == 0);
+ REQUIRE_EQ(event_mask, new_event_mask);
+
+ /* Continue the child to let it exit. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* The last event should be for the child process's exit. */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 0);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+static void *
+flock_thread(void *arg)
+{
+ int fd;
+
+ fd = *(int *)arg;
+ (void)flock(fd, LOCK_EX);
+ (void)flock(fd, LOCK_UN);
+ return (NULL);
+}
+
+/*
+ * Verify that PT_ATTACH will suspend threads sleeping in an SBDRY section.
+ * We rely on the fact that the lockf implementation sets SBDRY before blocking
+ * on a lock. This is a regression test for r318191.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__PT_ATTACH_with_SBDRY_thread);
+ATF_TC_BODY(ptrace__PT_ATTACH_with_SBDRY_thread, tc)
+{
+ pthread_barrier_t barrier;
+ pthread_barrierattr_t battr;
+ char tmpfile[64];
+ pid_t child, wpid;
+ int error, fd, i, status;
+
+ REQUIRE_EQ(pthread_barrierattr_init(&battr), 0);
+ ATF_REQUIRE(pthread_barrierattr_setpshared(&battr,
+ PTHREAD_PROCESS_SHARED) == 0);
+ REQUIRE_EQ(pthread_barrier_init(&barrier, &battr, 2), 0);
+
+ (void)snprintf(tmpfile, sizeof(tmpfile), "./ptrace.XXXXXX");
+ fd = mkstemp(tmpfile);
+ ATF_REQUIRE(fd >= 0);
+
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ pthread_t t[2];
+ int cfd;
+
+ error = pthread_barrier_wait(&barrier);
+ if (error != 0 && error != PTHREAD_BARRIER_SERIAL_THREAD)
+ _exit(1);
+
+ cfd = open(tmpfile, O_RDONLY);
+ if (cfd < 0)
+ _exit(1);
+
+ /*
+ * We want at least two threads blocked on the file lock since
+ * the SIGSTOP from PT_ATTACH may kick one of them out of
+ * sleep.
+ */
+ if (pthread_create(&t[0], NULL, flock_thread, &cfd) != 0)
+ _exit(1);
+ if (pthread_create(&t[1], NULL, flock_thread, &cfd) != 0)
+ _exit(1);
+ if (pthread_join(t[0], NULL) != 0)
+ _exit(1);
+ if (pthread_join(t[1], NULL) != 0)
+ _exit(1);
+ _exit(0);
+ }
+
+ REQUIRE_EQ(flock(fd, LOCK_EX), 0);
+
+ error = pthread_barrier_wait(&barrier);
+ ATF_REQUIRE(error == 0 || error == PTHREAD_BARRIER_SERIAL_THREAD);
+
+ /*
+ * Give the child some time to block. Is there a better way to do this?
+ */
+ sleep(1);
+
+ /*
+ * Attach and give the child 3 seconds to stop.
+ */
+ REQUIRE_EQ(ptrace(PT_ATTACH, child, NULL, 0), 0);
+ for (i = 0; i < 3; i++) {
+ wpid = waitpid(child, &status, WNOHANG);
+ if (wpid == child && WIFSTOPPED(status) &&
+ WSTOPSIG(status) == SIGSTOP)
+ break;
+ sleep(1);
+ }
+ ATF_REQUIRE_MSG(i < 3, "failed to stop child process after PT_ATTACH");
+
+ REQUIRE_EQ(ptrace(PT_DETACH, child, NULL, 0), 0);
+
+ REQUIRE_EQ(flock(fd, LOCK_UN), 0);
+ REQUIRE_EQ(unlink(tmpfile), 0);
+ REQUIRE_EQ(close(fd), 0);
+}
+
+static void
+sigusr1_step_handler(int sig)
+{
+
+ CHILD_REQUIRE_EQ(sig, SIGUSR1);
+ raise(SIGABRT);
+}
+
+/*
+ * Verify that PT_STEP with a signal invokes the signal before
+ * stepping the next instruction (and that the next instruction is
+ * stepped correctly).
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__PT_STEP_with_signal);
+ATF_TC_BODY(ptrace__PT_STEP_with_signal, tc)
+{
+ struct ptrace_lwpinfo pl;
+ pid_t fpid, wpid;
+ int status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ signal(SIGUSR1, sigusr1_step_handler);
+ raise(SIGABRT);
+ exit(1);
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* The next stop should report the SIGABRT in the child body. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGABRT);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI);
+ REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGABRT);
+
+ /* Step the child process inserting SIGUSR1. */
+ REQUIRE_EQ(ptrace(PT_STEP, fpid, (caddr_t)1, SIGUSR1), 0);
+
+ /* The next stop should report the SIGABRT in the signal handler. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGABRT);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI);
+ REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGABRT);
+
+ /* Continue the child process discarding the signal. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* The next stop should report a trace trap from PT_STEP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI);
+ REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGTRAP);
+ REQUIRE_EQ(pl.pl_siginfo.si_code, TRAP_TRACE);
+
+ /* Continue the child to let it exit. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* The last event should be for the child process's exit. */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 1);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+#ifdef HAVE_BREAKPOINT
+/*
+ * Verify that a SIGTRAP event with the TRAP_BRKPT code is reported
+ * for a breakpoint trap.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__breakpoint_siginfo);
+ATF_TC_BODY(ptrace__breakpoint_siginfo, tc)
+{
+ struct ptrace_lwpinfo pl;
+ pid_t fpid, wpid;
+ int status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ breakpoint();
+ exit(1);
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ /* Continue the child ignoring the SIGSTOP. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* The second wait() should report hitting the breakpoint. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE((pl.pl_flags & PL_FLAG_SI) != 0);
+ REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGTRAP);
+ REQUIRE_EQ(pl.pl_siginfo.si_code, TRAP_BRKPT);
+
+ /* Kill the child process. */
+ REQUIRE_EQ(ptrace(PT_KILL, fpid, 0, 0), 0);
+
+ /* The last wait() should report the SIGKILL. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSIGNALED(status));
+ REQUIRE_EQ(WTERMSIG(status), SIGKILL);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+#endif /* HAVE_BREAKPOINT */
+
+/*
+ * Verify that a SIGTRAP event with the TRAP_TRACE code is reported
+ * for a single-step trap from PT_STEP.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__step_siginfo);
+ATF_TC_BODY(ptrace__step_siginfo, tc)
+{
+ struct ptrace_lwpinfo pl;
+ pid_t fpid, wpid;
+ int status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ exit(1);
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ /* Step the child ignoring the SIGSTOP. */
+ REQUIRE_EQ(ptrace(PT_STEP, fpid, (caddr_t)1, 0), 0);
+
+ /* The second wait() should report a single-step trap. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE((pl.pl_flags & PL_FLAG_SI) != 0);
+ REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGTRAP);
+ REQUIRE_EQ(pl.pl_siginfo.si_code, TRAP_TRACE);
+
+ /* Continue the child process. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* The last event should be for the child process's exit. */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 1);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+#if defined(HAVE_BREAKPOINT) && defined(SKIP_BREAK)
+static void *
+continue_thread(void *arg __unused)
+{
+ breakpoint();
+ return (NULL);
+}
+
+static __dead2 void
+continue_thread_main(void)
+{
+ pthread_t threads[2];
+
+ CHILD_REQUIRE(pthread_create(&threads[0], NULL, continue_thread,
+ NULL) == 0);
+ CHILD_REQUIRE(pthread_create(&threads[1], NULL, continue_thread,
+ NULL) == 0);
+ CHILD_REQUIRE_EQ(pthread_join(threads[0], NULL), 0);
+ CHILD_REQUIRE_EQ(pthread_join(threads[1], NULL), 0);
+ exit(1);
+}
+
+/*
+ * Ensure that PT_CONTINUE clears the status of the thread that
+ * triggered the stop even if a different thread's LWP was passed to
+ * PT_CONTINUE.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_different_thread);
+ATF_TC_BODY(ptrace__PT_CONTINUE_different_thread, tc)
+{
+ struct ptrace_lwpinfo pl;
+ pid_t fpid, wpid;
+ lwpid_t lwps[2];
+ bool hit_break[2];
+ struct reg reg;
+ int i, j, status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ continue_thread_main();
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl,
+ sizeof(pl)) != -1);
+
+ REQUIRE_EQ(ptrace(PT_LWP_EVENTS, wpid, NULL, 1), 0);
+
+ /* Continue the child ignoring the SIGSTOP. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* One of the new threads should report it's birth. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ REQUIRE_EQ((pl.pl_flags & (PL_FLAG_BORN | PL_FLAG_SCX)),
+ (PL_FLAG_BORN | PL_FLAG_SCX));
+ lwps[0] = pl.pl_lwpid;
+
+ /*
+ * Suspend this thread to ensure both threads are alive before
+ * hitting the breakpoint.
+ */
+ ATF_REQUIRE(ptrace(PT_SUSPEND, lwps[0], NULL, 0) != -1);
+
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* Second thread should report it's birth. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ REQUIRE_EQ((pl.pl_flags & (PL_FLAG_BORN | PL_FLAG_SCX)),
+ (PL_FLAG_BORN | PL_FLAG_SCX));
+ ATF_REQUIRE(pl.pl_lwpid != lwps[0]);
+ lwps[1] = pl.pl_lwpid;
+
+ /* Resume both threads waiting for breakpoint events. */
+ hit_break[0] = hit_break[1] = false;
+ ATF_REQUIRE(ptrace(PT_RESUME, lwps[0], NULL, 0) != -1);
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* One thread should report a breakpoint. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE((pl.pl_flags & PL_FLAG_SI) != 0);
+ REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGTRAP);
+ REQUIRE_EQ(pl.pl_siginfo.si_code, TRAP_BRKPT);
+ if (pl.pl_lwpid == lwps[0])
+ i = 0;
+ else
+ i = 1;
+ hit_break[i] = true;
+ ATF_REQUIRE(ptrace(PT_GETREGS, pl.pl_lwpid, (caddr_t)&reg, 0) != -1);
+ SKIP_BREAK(&reg);
+ ATF_REQUIRE(ptrace(PT_SETREGS, pl.pl_lwpid, (caddr_t)&reg, 0) != -1);
+
+ /*
+ * Resume both threads but pass the other thread's LWPID to
+ * PT_CONTINUE.
+ */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, lwps[i ^ 1], (caddr_t)1, 0), 0);
+
+ /*
+ * Will now get two thread exit events and one more breakpoint
+ * event.
+ */
+ for (j = 0; j < 3; j++) {
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl,
+ sizeof(pl)) != -1);
+
+ if (pl.pl_lwpid == lwps[0])
+ i = 0;
+ else
+ i = 1;
+
+ ATF_REQUIRE_MSG(lwps[i] != 0, "event for exited thread");
+ if (pl.pl_flags & PL_FLAG_EXITED) {
+ ATF_REQUIRE_MSG(hit_break[i],
+ "exited thread did not report breakpoint");
+ lwps[i] = 0;
+ } else {
+ ATF_REQUIRE((pl.pl_flags & PL_FLAG_SI) != 0);
+ REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGTRAP);
+ REQUIRE_EQ(pl.pl_siginfo.si_code, TRAP_BRKPT);
+ ATF_REQUIRE_MSG(!hit_break[i],
+ "double breakpoint event");
+ hit_break[i] = true;
+ ATF_REQUIRE(ptrace(PT_GETREGS, pl.pl_lwpid, (caddr_t)&reg,
+ 0) != -1);
+ SKIP_BREAK(&reg);
+ ATF_REQUIRE(ptrace(PT_SETREGS, pl.pl_lwpid, (caddr_t)&reg,
+ 0) != -1);
+ }
+
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+ }
+
+ /* Both threads should have exited. */
+ REQUIRE_EQ(lwps[0], 0);
+ REQUIRE_EQ(lwps[1], 0);
+
+ /* The last event should be for the child process's exit. */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 1);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+#endif
+
+/*
+ * Verify that PT_LWPINFO doesn't return stale siginfo.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__PT_LWPINFO_stale_siginfo);
+ATF_TC_BODY(ptrace__PT_LWPINFO_stale_siginfo, tc)
+{
+ struct ptrace_lwpinfo pl;
+ pid_t fpid, wpid;
+ int events, status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ raise(SIGABRT);
+ exit(1);
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* The next stop should report the SIGABRT in the child body. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGABRT);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI);
+ REQUIRE_EQ(pl.pl_siginfo.si_signo, SIGABRT);
+
+ /*
+ * Continue the process ignoring the signal, but enabling
+ * syscall traps.
+ */
+ REQUIRE_EQ(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0), 0);
+
+ /*
+ * The next stop should report a system call entry from
+ * exit(). PL_FLAGS_SI should not be set.
+ */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE);
+ REQUIRE_EQ((pl.pl_flags & PL_FLAG_SI), 0);
+
+ /* Disable syscall tracing and continue the child to let it exit. */
+ ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events,
+ sizeof(events)) == 0);
+ events &= ~PTRACE_SYSCALL;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, fpid, (caddr_t)&events,
+ sizeof(events)) == 0);
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* The last event should be for the child process's exit. */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 1);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+/*
+ * A simple test of PT_GET_SC_ARGS and PT_GET_SC_RET.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__syscall_args);
+ATF_TC_BODY(ptrace__syscall_args, tc)
+{
+ struct ptrace_lwpinfo pl;
+ struct ptrace_sc_ret psr;
+ pid_t fpid, wpid;
+ register_t args[2];
+ int events, status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ kill(getpid(), 0);
+ /* Close a fd that should not exist. */
+ close(12345);
+ exit(1);
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ /*
+ * Continue the process ignoring the signal, but enabling
+ * syscall traps.
+ */
+ REQUIRE_EQ(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0), 0);
+
+ /*
+ * The next stop should be the syscall entry from getpid().
+ */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE);
+ REQUIRE_EQ(pl.pl_syscall_code, (unsigned)SYS_getpid);
+
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /*
+ * The next stop should be the syscall exit from getpid().
+ */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCX);
+ REQUIRE_EQ(pl.pl_syscall_code, (unsigned)SYS_getpid);
+
+ ATF_REQUIRE(ptrace(PT_GET_SC_RET, wpid, (caddr_t)&psr,
+ sizeof(psr)) != -1);
+ REQUIRE_EQ(psr.sr_error, 0);
+ REQUIRE_EQ(psr.sr_retval[0], wpid);
+
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /*
+ * The next stop should be the syscall entry from kill().
+ */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE);
+ REQUIRE_EQ(pl.pl_syscall_code, (unsigned)SYS_kill);
+ REQUIRE_EQ(pl.pl_syscall_narg, 2u);
+
+ ATF_REQUIRE(ptrace(PT_GET_SC_ARGS, wpid, (caddr_t)args,
+ sizeof(args)) != -1);
+ REQUIRE_EQ(args[0], wpid);
+ REQUIRE_EQ(args[1], 0);
+
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /*
+ * The next stop should be the syscall exit from kill().
+ */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCX);
+ REQUIRE_EQ(pl.pl_syscall_code, (unsigned)SYS_kill);
+
+ ATF_REQUIRE(ptrace(PT_GET_SC_RET, wpid, (caddr_t)&psr,
+ sizeof(psr)) != -1);
+ REQUIRE_EQ(psr.sr_error, 0);
+
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /*
+ * The next stop should be the syscall entry from close().
+ */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE);
+ REQUIRE_EQ(pl.pl_syscall_code, (unsigned)SYS_close);
+ REQUIRE_EQ(pl.pl_syscall_narg, 1u);
+
+ ATF_REQUIRE(ptrace(PT_GET_SC_ARGS, wpid, (caddr_t)args,
+ sizeof(args)) != -1);
+ REQUIRE_EQ(args[0], 12345);
+
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /*
+ * The next stop should be the syscall exit from close().
+ */
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCX);
+ REQUIRE_EQ(pl.pl_syscall_code, (unsigned)SYS_close);
+
+ ATF_REQUIRE(ptrace(PT_GET_SC_RET, wpid, (caddr_t)&psr,
+ sizeof(psr)) != -1);
+ REQUIRE_EQ(psr.sr_error, EBADF);
+
+ /* Disable syscall tracing and continue the child to let it exit. */
+ ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events,
+ sizeof(events)) == 0);
+ events &= ~PTRACE_SYSCALL;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, fpid, (caddr_t)&events,
+ sizeof(events)) == 0);
+ REQUIRE_EQ(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0), 0);
+
+ /* The last event should be for the child process's exit. */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 1);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+/*
+ * Check that syscall info is available whenever kernel has valid td_sa.
+ * Assumes that libc nanosleep(2) is the plain syscall wrapper.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__syscall_args_anywhere);
+ATF_TC_BODY(ptrace__syscall_args_anywhere, tc)
+{
+ struct timespec rqt;
+ struct ptrace_lwpinfo lwpi;
+ register_t args[8];
+ pid_t debuggee, wpid;
+ int error, status;
+
+ debuggee = fork();
+ ATF_REQUIRE(debuggee >= 0);
+ if (debuggee == 0) {
+ rqt.tv_sec = 100000;
+ rqt.tv_nsec = 0;
+ for (;;)
+ nanosleep(&rqt, NULL);
+ _exit(0);
+ }
+
+ /* Give the debuggee some time to go to sleep. */
+ sleep(2);
+ error = ptrace(PT_ATTACH, debuggee, 0, 0);
+ ATF_REQUIRE(error == 0);
+ wpid = waitpid(debuggee, &status, 0);
+ REQUIRE_EQ(wpid, debuggee);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ error = ptrace(PT_LWPINFO, debuggee, (caddr_t)&lwpi, sizeof(lwpi));
+ ATF_REQUIRE(error == 0);
+ ATF_REQUIRE(lwpi.pl_syscall_code == SYS_nanosleep);
+ ATF_REQUIRE(lwpi.pl_syscall_narg == 2);
+ error = ptrace(PT_GET_SC_ARGS, debuggee, (caddr_t)&args[0],
+ lwpi.pl_syscall_narg * sizeof(register_t));
+ ATF_REQUIRE(error == 0);
+ ATF_REQUIRE(args[0] == (register_t)&rqt);
+ ATF_REQUIRE(args[1] == 0);
+
+ error = ptrace(PT_DETACH, debuggee, 0, 0);
+ ATF_REQUIRE(error == 0);
+ kill(SIGKILL, debuggee);
+}
+
+/*
+ * Verify that when the process is traced that it isn't reparent
+ * to the init process when we close all process descriptors.
+ */
+ATF_TC(ptrace__proc_reparent);
+ATF_TC_HEAD(ptrace__proc_reparent, tc)
+{
+
+ atf_tc_set_md_var(tc, "timeout", "2");
+}
+ATF_TC_BODY(ptrace__proc_reparent, tc)
+{
+ pid_t traced, debuger, wpid;
+ int pd, status;
+
+ traced = pdfork(&pd, 0);
+ ATF_REQUIRE(traced >= 0);
+ if (traced == 0) {
+ raise(SIGSTOP);
+ exit(0);
+ }
+ ATF_REQUIRE(pd >= 0);
+
+ debuger = fork();
+ ATF_REQUIRE(debuger >= 0);
+ if (debuger == 0) {
+ /* The traced process is reparented to debuger. */
+ REQUIRE_EQ(ptrace(PT_ATTACH, traced, 0, 0), 0);
+ wpid = waitpid(traced, &status, 0);
+ REQUIRE_EQ(wpid, traced);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+ REQUIRE_EQ(close(pd), 0);
+ REQUIRE_EQ(ptrace(PT_DETACH, traced, (caddr_t)1, 0), 0);
+
+ /* We closed pd so we should not have any child. */
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+
+ exit(0);
+ }
+
+ REQUIRE_EQ(close(pd), 0);
+ wpid = waitpid(debuger, &status, 0);
+ REQUIRE_EQ(wpid, debuger);
+ REQUIRE_EQ(WEXITSTATUS(status), 0);
+
+ /* Check if we still have any child. */
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
+/*
+ * Ensure that traced processes created with pdfork(2) are visible to
+ * waitid(P_ALL).
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__procdesc_wait_child);
+ATF_TC_BODY(ptrace__procdesc_wait_child, tc)
+{
+ pid_t child, wpid;
+ int pd, status;
+
+ child = pdfork(&pd, 0);
+ ATF_REQUIRE(child >= 0);
+
+ if (child == 0) {
+ trace_me();
+ (void)raise(SIGSTOP);
+ exit(0);
+ }
+
+ wpid = waitpid(child, &status, 0);
+ REQUIRE_EQ(wpid, child);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1);
+
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, child);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1);
+
+ /*
+ * If process was created by pdfork, the return code have to
+ * be collected through process descriptor.
+ */
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+
+ ATF_REQUIRE(close(pd) != -1);
+}
+
+/*
+ * Ensure that traced processes created with pdfork(2) are not visible
+ * after returning to parent - waitid(P_ALL).
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__procdesc_reparent_wait_child);
+ATF_TC_BODY(ptrace__procdesc_reparent_wait_child, tc)
+{
+ pid_t traced, debuger, wpid;
+ int pd, status;
+
+ traced = pdfork(&pd, 0);
+ ATF_REQUIRE(traced >= 0);
+ if (traced == 0) {
+ raise(SIGSTOP);
+ exit(0);
+ }
+ ATF_REQUIRE(pd >= 0);
+
+ /* Wait until the child process has stopped before fork()ing again. */
+ REQUIRE_EQ(traced, waitpid(traced, &status, WSTOPPED));
+ debuger = fork();
+ ATF_REQUIRE(debuger >= 0);
+ if (debuger == 0) {
+ /* The traced process is reparented to debuger. */
+ REQUIRE_EQ(ptrace(PT_ATTACH, traced, 0, 0), 0);
+ wpid = waitpid(traced, &status, 0);
+ REQUIRE_EQ(wpid, traced);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ /* Allow process to die. */
+ REQUIRE_EQ(ptrace(PT_CONTINUE, traced, (caddr_t)1, 0), 0);
+ wpid = waitpid(traced, &status, 0);
+ REQUIRE_EQ(wpid, traced);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 0);
+
+ /* Reparent back to the orginal process. */
+ REQUIRE_EQ(close(pd), 0);
+ exit(0);
+ }
+
+ wpid = waitpid(debuger, &status, 0);
+ REQUIRE_EQ(wpid, debuger);
+ REQUIRE_EQ(WEXITSTATUS(status), 0);
+
+ /*
+ * We have a child but it has a process descriptori
+ * so we should not be able to collect it process.
+ */
+ wpid = wait(&status);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+
+ REQUIRE_EQ(close(pd), 0);
+}
+
+/*
+ * Try using PT_SC_REMOTE to get the PID of a traced child process.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__PT_SC_REMOTE_getpid);
+ATF_TC_BODY(ptrace__PT_SC_REMOTE_getpid, tc)
+{
+ struct ptrace_sc_remote pscr;
+ pid_t fpid, wpid;
+ int status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ exit(0);
+ }
+
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ pscr.pscr_syscall = SYS_getpid;
+ pscr.pscr_nargs = 0;
+ pscr.pscr_args = NULL;
+ ATF_REQUIRE(ptrace(PT_SC_REMOTE, fpid, (caddr_t)&pscr, sizeof(pscr)) !=
+ -1);
+ ATF_REQUIRE_MSG(pscr.pscr_ret.sr_error == 0,
+ "remote getpid failed with error %d", pscr.pscr_ret.sr_error);
+ ATF_REQUIRE_MSG(pscr.pscr_ret.sr_retval[0] == fpid,
+ "unexpected return value %jd instead of %d",
+ (intmax_t)pscr.pscr_ret.sr_retval[0], fpid);
+
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ pscr.pscr_syscall = SYS_getppid;
+ pscr.pscr_nargs = 0;
+ pscr.pscr_args = NULL;
+ ATF_REQUIRE(ptrace(PT_SC_REMOTE, fpid, (caddr_t)&pscr, sizeof(pscr)) !=
+ -1);
+ ATF_REQUIRE_MSG(pscr.pscr_ret.sr_error == 0,
+ "remote getppid failed with error %d", pscr.pscr_ret.sr_error);
+ ATF_REQUIRE_MSG(pscr.pscr_ret.sr_retval[0] == getpid(),
+ "unexpected return value %jd instead of %d",
+ (intmax_t)pscr.pscr_ret.sr_retval[0], fpid);
+
+ wpid = waitpid(fpid, &status, 0);
+ REQUIRE_EQ(wpid, fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ ATF_REQUIRE(ptrace(PT_DETACH, fpid, (caddr_t)1, 0) != -1);
+}
+
+/*
+ * Ensure that procctl(PROC_REAP_KILL) won't block forever waiting for a target
+ * process that stopped to report its status to a debugger.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__reap_kill_stopped);
+ATF_TC_BODY(ptrace__reap_kill_stopped, tc)
+{
+ struct procctl_reaper_kill prk;
+ pid_t debuggee, wpid;
+ int error, status;
+
+ REQUIRE_EQ(procctl(P_PID, getpid(), PROC_REAP_ACQUIRE, NULL), 0);
+
+ debuggee = fork();
+ ATF_REQUIRE(debuggee >= 0);
+ if (debuggee == 0) {
+ trace_me();
+ for (;;)
+ sleep(10);
+ _exit(0);
+ }
+ wpid = waitpid(debuggee, &status, 0);
+ REQUIRE_EQ(wpid, debuggee);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ /* Resume the child and ask it to stop during syscall exits. */
+ ATF_REQUIRE(ptrace(PT_TO_SCX, debuggee, (caddr_t)1, 0) != -1);
+
+ /* Give the debuggee some time to go to sleep. */
+ usleep(100000);
+
+ /*
+ * Kill the child process. procctl() may attempt to stop the target
+ * process to prevent it from adding new children to the reaper subtree,
+ * and this should not conflict with the child stopping itself for the
+ * debugger.
+ */
+ memset(&prk, 0, sizeof(prk));
+ prk.rk_sig = SIGTERM;
+ error = procctl(P_PID, getpid(), PROC_REAP_KILL, &prk);
+ REQUIRE_EQ(error, 0);
+ REQUIRE_EQ(1u, prk.rk_killed);
+ REQUIRE_EQ(-1, prk.rk_fpid);
+}
+
+struct child_res {
+ struct timespec sleep_time;
+ int nanosleep_res;
+ int nanosleep_errno;
+};
+
+static const long nsec = 1000000000L;
+static const struct timespec ten_sec = {
+ .tv_sec = 10,
+ .tv_nsec = 0,
+};
+static const struct timespec twelve_sec = {
+ .tv_sec = 12,
+ .tv_nsec = 0,
+};
+
+ATF_TC_WITHOUT_HEAD(ptrace__PT_ATTACH_no_EINTR);
+ATF_TC_BODY(ptrace__PT_ATTACH_no_EINTR, tc)
+{
+ struct child_res *shm;
+ struct timespec rqt, now, wake;
+ pid_t debuggee;
+ int status;
+
+ shm = mmap(NULL, sizeof(*shm), PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANON, -1, 0);
+ ATF_REQUIRE(shm != MAP_FAILED);
+
+ ATF_REQUIRE((debuggee = fork()) != -1);
+ if (debuggee == 0) {
+ rqt.tv_sec = 10;
+ rqt.tv_nsec = 0;
+ clock_gettime(CLOCK_MONOTONIC_PRECISE, &now);
+ errno = 0;
+ shm->nanosleep_res = nanosleep(&rqt, NULL);
+ shm->nanosleep_errno = errno;
+ clock_gettime(CLOCK_MONOTONIC_PRECISE, &wake);
+ timespecsub(&wake, &now, &shm->sleep_time);
+ _exit(0);
+ }
+
+ /* Give the debuggee some time to go to sleep. */
+ sleep(2);
+ REQUIRE_EQ(ptrace(PT_ATTACH, debuggee, 0, 0), 0);
+ REQUIRE_EQ(waitpid(debuggee, &status, 0), debuggee);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ REQUIRE_EQ(ptrace(PT_DETACH, debuggee, 0, 0), 0);
+ REQUIRE_EQ(waitpid(debuggee, &status, 0), debuggee);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 0);
+
+ ATF_REQUIRE(shm->nanosleep_res == 0);
+ ATF_REQUIRE(shm->nanosleep_errno == 0);
+ ATF_REQUIRE(timespeccmp(&shm->sleep_time, &ten_sec, >=));
+ ATF_REQUIRE(timespeccmp(&shm->sleep_time, &twelve_sec, <=));
+}
+
+ATF_TC_WITHOUT_HEAD(ptrace__PT_DETACH_continued);
+ATF_TC_BODY(ptrace__PT_DETACH_continued, tc)
+{
+ char buf[256];
+ pid_t debuggee, debugger;
+ int dpipe[2] = {-1, -1}, status;
+
+ /* Setup the debuggee's pipe, which we'll use to let it terminate. */
+ ATF_REQUIRE(pipe(dpipe) == 0);
+ ATF_REQUIRE((debuggee = fork()) != -1);
+
+ if (debuggee == 0) {
+ ssize_t readsz;
+
+ /*
+ * The debuggee will just absorb everything until the parent
+ * closes it. In the process, we expect it to get SIGSTOP'd,
+ * then ptrace(2)d and finally, it should resume after we detach
+ * and the parent will be notified.
+ */
+ close(dpipe[1]);
+ while ((readsz = read(dpipe[0], buf, sizeof(buf))) != 0) {
+ if (readsz > 0 || errno == EINTR)
+ continue;
+ _exit(1);
+ }
+
+ _exit(0);
+ }
+
+ close(dpipe[0]);
+
+ ATF_REQUIRE(kill(debuggee, SIGSTOP) == 0);
+ REQUIRE_EQ(waitpid(debuggee, &status, WUNTRACED), debuggee);
+ ATF_REQUIRE(WIFSTOPPED(status));
+
+ /* Child is stopped, enter the debugger to attach/detach. */
+ ATF_REQUIRE((debugger = fork()) != -1);
+ if (debugger == 0) {
+ REQUIRE_EQ(ptrace(PT_ATTACH, debuggee, 0, 0), 0);
+ REQUIRE_EQ(waitpid(debuggee, &status, 0), debuggee);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ REQUIRE_EQ(ptrace(PT_DETACH, debuggee, 0, 0), 0);
+ _exit(0);
+ }
+
+ REQUIRE_EQ(waitpid(debugger, &status, 0), debugger);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 0);
+
+ REQUIRE_EQ(waitpid(debuggee, &status, WCONTINUED), debuggee);
+ ATF_REQUIRE(WIFCONTINUED(status));
+
+ /*
+ * Closing the pipe will trigger the debuggee to exit now that the
+ * child has resumed following detach.
+ */
+ close(dpipe[1]);
+
+ REQUIRE_EQ(waitpid(debuggee, &status, 0), debuggee);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 0);
+
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, ptrace__parent_wait_after_trace_me);
+ ATF_TP_ADD_TC(tp, ptrace__parent_wait_after_attach);
+ ATF_TP_ADD_TC(tp, ptrace__parent_sees_exit_after_child_debugger);
+ ATF_TP_ADD_TC(tp, ptrace__parent_sees_exit_after_unrelated_debugger);
+ ATF_TP_ADD_TC(tp, ptrace__parent_exits_before_child);
+ ATF_TP_ADD_TC(tp, ptrace__follow_fork_both_attached);
+ ATF_TP_ADD_TC(tp, ptrace__follow_fork_child_detached);
+ ATF_TP_ADD_TC(tp, ptrace__follow_fork_parent_detached);
+ ATF_TP_ADD_TC(tp, ptrace__follow_fork_both_attached_unrelated_debugger);
+ ATF_TP_ADD_TC(tp,
+ ptrace__follow_fork_child_detached_unrelated_debugger);
+ ATF_TP_ADD_TC(tp,
+ ptrace__follow_fork_parent_detached_unrelated_debugger);
+ ATF_TP_ADD_TC(tp, ptrace__getppid);
+ ATF_TP_ADD_TC(tp, ptrace__new_child_pl_syscall_code_fork);
+ ATF_TP_ADD_TC(tp, ptrace__new_child_pl_syscall_code_vfork);
+ ATF_TP_ADD_TC(tp, ptrace__new_child_pl_syscall_code_thread);
+ ATF_TP_ADD_TC(tp, ptrace__lwp_events);
+ ATF_TP_ADD_TC(tp, ptrace__lwp_events_exec);
+ ATF_TP_ADD_TC(tp, ptrace__siginfo);
+ ATF_TP_ADD_TC(tp, ptrace__ptrace_exec_disable);
+ ATF_TP_ADD_TC(tp, ptrace__ptrace_exec_enable);
+ ATF_TP_ADD_TC(tp, ptrace__event_mask);
+ ATF_TP_ADD_TC(tp, ptrace__ptrace_vfork);
+ ATF_TP_ADD_TC(tp, ptrace__ptrace_vfork_follow);
+#ifdef HAVE_BREAKPOINT
+ ATF_TP_ADD_TC(tp, ptrace__PT_KILL_breakpoint);
+#endif
+ ATF_TP_ADD_TC(tp, ptrace__PT_KILL_system_call);
+ ATF_TP_ADD_TC(tp, ptrace__PT_KILL_threads);
+ ATF_TP_ADD_TC(tp, ptrace__PT_KILL_competing_signal);
+ ATF_TP_ADD_TC(tp, ptrace__PT_KILL_competing_stop);
+ ATF_TP_ADD_TC(tp, ptrace__PT_KILL_with_signal_full_sigqueue);
+ ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_system_call_entry);
+ ATF_TP_ADD_TC(tp,
+ ptrace__PT_CONTINUE_with_signal_system_call_entry_and_exit);
+ ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_full_sigqueue);
+ ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_masked_full_sigqueue);
+ ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_change_sig);
+ ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_sigtrap_system_call_entry);
+ ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_mix);
+ ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_kqueue);
+ ATF_TP_ADD_TC(tp, ptrace__killed_with_sigmask);
+ ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_sigmask);
+ ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_thread_sigmask);
+ ATF_TP_ADD_TC(tp, ptrace__PT_REGSET);
+ ATF_TP_ADD_TC(tp, ptrace__parent_terminate_with_pending_sigstop1);
+ ATF_TP_ADD_TC(tp, ptrace__parent_terminate_with_pending_sigstop2);
+ ATF_TP_ADD_TC(tp, ptrace__event_mask_sigkill_discard);
+ ATF_TP_ADD_TC(tp, ptrace__PT_ATTACH_with_SBDRY_thread);
+ ATF_TP_ADD_TC(tp, ptrace__PT_STEP_with_signal);
+#ifdef HAVE_BREAKPOINT
+ ATF_TP_ADD_TC(tp, ptrace__breakpoint_siginfo);
+#endif
+ ATF_TP_ADD_TC(tp, ptrace__step_siginfo);
+#if defined(HAVE_BREAKPOINT) && defined(SKIP_BREAK)
+ ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_different_thread);
+#endif
+ ATF_TP_ADD_TC(tp, ptrace__PT_LWPINFO_stale_siginfo);
+ ATF_TP_ADD_TC(tp, ptrace__syscall_args);
+ ATF_TP_ADD_TC(tp, ptrace__syscall_args_anywhere);
+ ATF_TP_ADD_TC(tp, ptrace__proc_reparent);
+ ATF_TP_ADD_TC(tp, ptrace__procdesc_wait_child);
+ ATF_TP_ADD_TC(tp, ptrace__procdesc_reparent_wait_child);
+ ATF_TP_ADD_TC(tp, ptrace__PT_SC_REMOTE_getpid);
+ ATF_TP_ADD_TC(tp, ptrace__reap_kill_stopped);
+ ATF_TP_ADD_TC(tp, ptrace__PT_ATTACH_no_EINTR);
+ ATF_TP_ADD_TC(tp, ptrace__PT_DETACH_continued);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/reaper.c b/tests/sys/kern/reaper.c
new file mode 100644
index 000000000000..fb5eeb9b324b
--- /dev/null
+++ b/tests/sys/kern/reaper.c
@@ -0,0 +1,792 @@
+/*-
+ * Copyright (c) 2016 Jilles Tjoelker
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/procctl.h>
+#include <sys/procdesc.h>
+#include <sys/wait.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <signal.h>
+#include <unistd.h>
+
+static void
+dummy_sighandler(int sig __unused, siginfo_t *info __unused, void *ctx __unused)
+{
+}
+
+ATF_TC_WITHOUT_HEAD(reaper_wait_child_first);
+ATF_TC_BODY(reaper_wait_child_first, tc)
+{
+ pid_t parent, child, grandchild, pid;
+ int status, r;
+ int pip[2];
+
+ /* Be paranoid. */
+ pid = waitpid(-1, NULL, WNOHANG);
+ ATF_REQUIRE(pid == -1 && errno == ECHILD);
+
+ parent = getpid();
+ r = procctl(P_PID, parent, PROC_REAP_ACQUIRE, NULL);
+ ATF_REQUIRE_EQ(0, r);
+
+ r = pipe(pip);
+ ATF_REQUIRE_EQ(0, r);
+
+ child = fork();
+ ATF_REQUIRE(child != -1);
+ if (child == 0) {
+ if (close(pip[1]) != 0)
+ _exit(100);
+ grandchild = fork();
+ if (grandchild == -1)
+ _exit(101);
+ else if (grandchild == 0) {
+ if (read(pip[0], &(uint8_t){ 0 }, 1) != 0)
+ _exit(102);
+ if (getppid() != parent)
+ _exit(103);
+ _exit(2);
+ } else
+ _exit(3);
+ }
+
+ pid = waitpid(child, &status, 0);
+ ATF_REQUIRE_EQ(child, pid);
+ r = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
+ ATF_CHECK_EQ(3, r);
+
+ r = close(pip[1]);
+ ATF_REQUIRE_EQ(0, r);
+
+ pid = waitpid(-1, &status, 0);
+ ATF_REQUIRE(pid > 0 && pid != child);
+ r = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
+ ATF_CHECK_EQ(2, r);
+
+ r = close(pip[0]);
+ ATF_REQUIRE_EQ(0, r);
+}
+
+ATF_TC_WITHOUT_HEAD(reaper_wait_grandchild_first);
+ATF_TC_BODY(reaper_wait_grandchild_first, tc)
+{
+ pid_t parent, child, grandchild, pid;
+ int status, r;
+
+ /* Be paranoid. */
+ pid = waitpid(-1, NULL, WNOHANG);
+ ATF_REQUIRE(pid == -1 && errno == ECHILD);
+
+ parent = getpid();
+ r = procctl(P_PID, parent, PROC_REAP_ACQUIRE, NULL);
+ ATF_REQUIRE_EQ(0, r);
+
+ child = fork();
+ ATF_REQUIRE(child != -1);
+ if (child == 0) {
+ grandchild = fork();
+ if (grandchild == -1)
+ _exit(101);
+ else if (grandchild == 0)
+ _exit(2);
+ else {
+ if (waitid(P_PID, grandchild, NULL,
+ WNOWAIT | WEXITED) != 0)
+ _exit(102);
+ _exit(3);
+ }
+ }
+
+ pid = waitpid(child, &status, 0);
+ ATF_REQUIRE_EQ(child, pid);
+ r = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
+ ATF_CHECK_EQ(3, r);
+
+ pid = waitpid(-1, &status, 0);
+ ATF_REQUIRE(pid > 0 && pid != child);
+ r = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
+ ATF_CHECK_EQ(2, r);
+}
+
+ATF_TC(reaper_sigchld_child_first);
+ATF_TC_HEAD(reaper_sigchld_child_first, tc)
+{
+ atf_tc_set_md_var(tc, "timeout", "2");
+}
+ATF_TC_BODY(reaper_sigchld_child_first, tc)
+{
+ struct sigaction act;
+ sigset_t mask;
+ siginfo_t info;
+ pid_t parent, child, grandchild, pid;
+ int r;
+ int pip[2];
+
+ /* Be paranoid. */
+ pid = waitpid(-1, NULL, WNOHANG);
+ ATF_REQUIRE(pid == -1 && errno == ECHILD);
+
+ act.sa_sigaction = dummy_sighandler;
+ act.sa_flags = SA_SIGINFO | SA_RESTART;
+ r = sigemptyset(&act.sa_mask);
+ ATF_REQUIRE_EQ(0, r);
+ r = sigaction(SIGCHLD, &act, NULL);
+ ATF_REQUIRE_EQ(0, r);
+
+ r = sigemptyset(&mask);
+ ATF_REQUIRE_EQ(0, r);
+ r = sigaddset(&mask, SIGCHLD);
+ ATF_REQUIRE_EQ(0, r);
+ r = sigprocmask(SIG_BLOCK, &mask, NULL);
+ ATF_REQUIRE_EQ(0, r);
+
+ parent = getpid();
+ r = procctl(P_PID, parent, PROC_REAP_ACQUIRE, NULL);
+ ATF_REQUIRE_EQ(0, r);
+
+ r = pipe(pip);
+ ATF_REQUIRE_EQ(0, r);
+
+ child = fork();
+ ATF_REQUIRE(child != -1);
+ if (child == 0) {
+ if (close(pip[1]) != 0)
+ _exit(100);
+ grandchild = fork();
+ if (grandchild == -1)
+ _exit(101);
+ else if (grandchild == 0) {
+ if (read(pip[0], &(uint8_t){ 0 }, 1) != 0)
+ _exit(102);
+ if (getppid() != parent)
+ _exit(103);
+ _exit(2);
+ } else
+ _exit(3);
+ }
+
+ r = sigwaitinfo(&mask, &info);
+ ATF_REQUIRE_EQ(SIGCHLD, r);
+ ATF_CHECK_EQ(SIGCHLD, info.si_signo);
+ ATF_CHECK_EQ(CLD_EXITED, info.si_code);
+ ATF_CHECK_EQ(3, info.si_status);
+ ATF_CHECK_EQ(child, info.si_pid);
+
+ pid = waitpid(child, NULL, 0);
+ ATF_REQUIRE_EQ(child, pid);
+
+ r = close(pip[1]);
+ ATF_REQUIRE_EQ(0, r);
+
+ r = sigwaitinfo(&mask, &info);
+ ATF_REQUIRE_EQ(SIGCHLD, r);
+ ATF_CHECK_EQ(SIGCHLD, info.si_signo);
+ ATF_CHECK_EQ(CLD_EXITED, info.si_code);
+ ATF_CHECK_EQ(2, info.si_status);
+ grandchild = info.si_pid;
+ ATF_REQUIRE(grandchild > 0);
+ ATF_REQUIRE(grandchild != parent);
+ ATF_REQUIRE(grandchild != child);
+
+ pid = waitpid(-1, NULL, 0);
+ ATF_REQUIRE_EQ(grandchild, pid);
+
+ r = close(pip[0]);
+ ATF_REQUIRE_EQ(0, r);
+}
+
+ATF_TC(reaper_sigchld_grandchild_first);
+ATF_TC_HEAD(reaper_sigchld_grandchild_first, tc)
+{
+ atf_tc_set_md_var(tc, "timeout", "2");
+}
+ATF_TC_BODY(reaper_sigchld_grandchild_first, tc)
+{
+ struct sigaction act;
+ sigset_t mask;
+ siginfo_t info;
+ pid_t parent, child, grandchild, pid;
+ int r;
+
+ /* Be paranoid. */
+ pid = waitpid(-1, NULL, WNOHANG);
+ ATF_REQUIRE(pid == -1 && errno == ECHILD);
+
+ act.sa_sigaction = dummy_sighandler;
+ act.sa_flags = SA_SIGINFO | SA_RESTART;
+ r = sigemptyset(&act.sa_mask);
+ ATF_REQUIRE_EQ(0, r);
+ r = sigaction(SIGCHLD, &act, NULL);
+ ATF_REQUIRE_EQ(0, r);
+
+ r = sigemptyset(&mask);
+ ATF_REQUIRE_EQ(0, r);
+ r = sigaddset(&mask, SIGCHLD);
+ ATF_REQUIRE_EQ(0, r);
+ r = sigprocmask(SIG_BLOCK, &mask, NULL);
+ ATF_REQUIRE_EQ(0, r);
+
+ parent = getpid();
+ r = procctl(P_PID, parent, PROC_REAP_ACQUIRE, NULL);
+ ATF_REQUIRE_EQ(0, r);
+
+ child = fork();
+ ATF_REQUIRE(child != -1);
+ if (child == 0) {
+ grandchild = fork();
+ if (grandchild == -1)
+ _exit(101);
+ else if (grandchild == 0)
+ _exit(2);
+ else {
+ if (waitid(P_PID, grandchild, NULL,
+ WNOWAIT | WEXITED) != 0)
+ _exit(102);
+ _exit(3);
+ }
+ }
+
+ pid = waitpid(child, NULL, 0);
+ ATF_REQUIRE_EQ(child, pid);
+
+ r = sigwaitinfo(&mask, &info);
+ ATF_REQUIRE_EQ(SIGCHLD, r);
+ ATF_CHECK_EQ(SIGCHLD, info.si_signo);
+ ATF_CHECK_EQ(CLD_EXITED, info.si_code);
+ ATF_CHECK_EQ(2, info.si_status);
+ grandchild = info.si_pid;
+ ATF_REQUIRE(grandchild > 0);
+ ATF_REQUIRE(grandchild != parent);
+ ATF_REQUIRE(grandchild != child);
+
+ pid = waitpid(-1, NULL, 0);
+ ATF_REQUIRE_EQ(grandchild, pid);
+}
+
+ATF_TC_WITHOUT_HEAD(reaper_status);
+ATF_TC_BODY(reaper_status, tc)
+{
+ struct procctl_reaper_status st;
+ ssize_t sr;
+ pid_t parent, child, pid;
+ int r, status;
+ int pip[2];
+
+ parent = getpid();
+ r = procctl(P_PID, parent, PROC_REAP_STATUS, &st);
+ ATF_REQUIRE_EQ(0, r);
+ ATF_CHECK_EQ(0, st.rs_flags & REAPER_STATUS_OWNED);
+ ATF_CHECK(st.rs_children > 0);
+ ATF_CHECK(st.rs_descendants > 0);
+ ATF_CHECK(st.rs_descendants >= st.rs_children);
+ ATF_CHECK(st.rs_reaper != parent);
+ ATF_CHECK(st.rs_reaper > 0);
+
+ r = procctl(P_PID, parent, PROC_REAP_ACQUIRE, NULL);
+ ATF_REQUIRE_EQ(0, r);
+
+ r = procctl(P_PID, parent, PROC_REAP_STATUS, &st);
+ ATF_REQUIRE_EQ(0, r);
+ ATF_CHECK_EQ(REAPER_STATUS_OWNED,
+ st.rs_flags & (REAPER_STATUS_OWNED | REAPER_STATUS_REALINIT));
+ ATF_CHECK_EQ(0, st.rs_children);
+ ATF_CHECK_EQ(0, st.rs_descendants);
+ ATF_CHECK(st.rs_reaper == parent);
+ ATF_CHECK_EQ(-1, st.rs_pid);
+
+ r = pipe(pip);
+ ATF_REQUIRE_EQ(0, r);
+ child = fork();
+ ATF_REQUIRE(child != -1);
+ if (child == 0) {
+ if (close(pip[0]) != 0)
+ _exit(100);
+ if (procctl(P_PID, parent, PROC_REAP_STATUS, &st) != 0)
+ _exit(101);
+ if (write(pip[1], &st, sizeof(st)) != (ssize_t)sizeof(st))
+ _exit(102);
+ if (procctl(P_PID, getpid(), PROC_REAP_STATUS, &st) != 0)
+ _exit(103);
+ if (write(pip[1], &st, sizeof(st)) != (ssize_t)sizeof(st))
+ _exit(104);
+ _exit(0);
+ }
+ r = close(pip[1]);
+ ATF_REQUIRE_EQ(0, r);
+
+ sr = read(pip[0], &st, sizeof(st));
+ ATF_REQUIRE_EQ((ssize_t)sizeof(st), sr);
+ ATF_CHECK_EQ(REAPER_STATUS_OWNED,
+ st.rs_flags & (REAPER_STATUS_OWNED | REAPER_STATUS_REALINIT));
+ ATF_CHECK_EQ(1, st.rs_children);
+ ATF_CHECK_EQ(1, st.rs_descendants);
+ ATF_CHECK(st.rs_reaper == parent);
+ ATF_CHECK_EQ(child, st.rs_pid);
+ sr = read(pip[0], &st, sizeof(st));
+ ATF_REQUIRE_EQ((ssize_t)sizeof(st), sr);
+ ATF_CHECK_EQ(0,
+ st.rs_flags & (REAPER_STATUS_OWNED | REAPER_STATUS_REALINIT));
+ ATF_CHECK_EQ(1, st.rs_children);
+ ATF_CHECK_EQ(1, st.rs_descendants);
+ ATF_CHECK(st.rs_reaper == parent);
+ ATF_CHECK_EQ(child, st.rs_pid);
+
+ r = close(pip[0]);
+ ATF_REQUIRE_EQ(0, r);
+ pid = waitpid(child, &status, 0);
+ ATF_REQUIRE_EQ(child, pid);
+ ATF_CHECK_EQ(0, status);
+
+ r = procctl(P_PID, parent, PROC_REAP_STATUS, &st);
+ ATF_REQUIRE_EQ(0, r);
+ ATF_CHECK_EQ(REAPER_STATUS_OWNED,
+ st.rs_flags & (REAPER_STATUS_OWNED | REAPER_STATUS_REALINIT));
+ ATF_CHECK_EQ(0, st.rs_children);
+ ATF_CHECK_EQ(0, st.rs_descendants);
+ ATF_CHECK(st.rs_reaper == parent);
+ ATF_CHECK_EQ(-1, st.rs_pid);
+}
+
+ATF_TC_WITHOUT_HEAD(reaper_getpids);
+ATF_TC_BODY(reaper_getpids, tc)
+{
+ struct procctl_reaper_pidinfo info[10];
+ ssize_t sr;
+ pid_t parent, child, grandchild, pid;
+ int r, status, childidx;
+ int pipa[2], pipb[2];
+
+ parent = getpid();
+ r = procctl(P_PID, parent, PROC_REAP_ACQUIRE, NULL);
+ ATF_REQUIRE_EQ(0, r);
+
+ memset(info, '\0', sizeof(info));
+ r = procctl(P_PID, parent, PROC_REAP_GETPIDS,
+ &(struct procctl_reaper_pids){
+ .rp_count = sizeof(info) / sizeof(info[0]),
+ .rp_pids = info
+ });
+ ATF_CHECK_EQ(0, r);
+ ATF_CHECK_EQ(0, info[0].pi_flags & REAPER_PIDINFO_VALID);
+
+ r = pipe(pipa);
+ ATF_REQUIRE_EQ(0, r);
+ r = pipe(pipb);
+ ATF_REQUIRE_EQ(0, r);
+ child = fork();
+ ATF_REQUIRE(child != -1);
+ if (child == 0) {
+ if (close(pipa[1]) != 0)
+ _exit(100);
+ if (close(pipb[0]) != 0)
+ _exit(100);
+ if (read(pipa[0], &(uint8_t){ 0 }, 1) != 1)
+ _exit(101);
+ grandchild = fork();
+ if (grandchild == -1)
+ _exit(102);
+ if (grandchild == 0) {
+ if (write(pipb[1], &(uint8_t){ 0 }, 1) != 1)
+ _exit(103);
+ if (read(pipa[0], &(uint8_t){ 0 }, 1) != 1)
+ _exit(104);
+ _exit(0);
+ }
+ for (;;)
+ pause();
+ }
+ r = close(pipa[0]);
+ ATF_REQUIRE_EQ(0, r);
+ r = close(pipb[1]);
+ ATF_REQUIRE_EQ(0, r);
+
+ memset(info, '\0', sizeof(info));
+ r = procctl(P_PID, parent, PROC_REAP_GETPIDS,
+ &(struct procctl_reaper_pids){
+ .rp_count = sizeof(info) / sizeof(info[0]),
+ .rp_pids = info
+ });
+ ATF_CHECK_EQ(0, r);
+ ATF_CHECK_EQ(REAPER_PIDINFO_VALID | REAPER_PIDINFO_CHILD,
+ info[0].pi_flags & (REAPER_PIDINFO_VALID | REAPER_PIDINFO_CHILD));
+ ATF_CHECK_EQ(child, info[0].pi_pid);
+ ATF_CHECK_EQ(child, info[0].pi_subtree);
+ ATF_CHECK_EQ(0, info[1].pi_flags & REAPER_PIDINFO_VALID);
+
+ sr = write(pipa[1], &(uint8_t){ 0 }, 1);
+ ATF_REQUIRE_EQ(1, sr);
+ sr = read(pipb[0], &(uint8_t){ 0 }, 1);
+ ATF_REQUIRE_EQ(1, sr);
+
+ memset(info, '\0', sizeof(info));
+ r = procctl(P_PID, parent, PROC_REAP_GETPIDS,
+ &(struct procctl_reaper_pids){
+ .rp_count = sizeof(info) / sizeof(info[0]),
+ .rp_pids = info
+ });
+ ATF_CHECK_EQ(0, r);
+ ATF_CHECK_EQ(REAPER_PIDINFO_VALID,
+ info[0].pi_flags & REAPER_PIDINFO_VALID);
+ ATF_CHECK_EQ(REAPER_PIDINFO_VALID,
+ info[1].pi_flags & REAPER_PIDINFO_VALID);
+ ATF_CHECK_EQ(0, info[2].pi_flags & REAPER_PIDINFO_VALID);
+ ATF_CHECK_EQ(child, info[0].pi_subtree);
+ ATF_CHECK_EQ(child, info[1].pi_subtree);
+ childidx = info[1].pi_pid == child ? 1 : 0;
+ ATF_CHECK_EQ(REAPER_PIDINFO_CHILD,
+ info[childidx].pi_flags & REAPER_PIDINFO_CHILD);
+ ATF_CHECK_EQ(0, info[childidx ^ 1].pi_flags & REAPER_PIDINFO_CHILD);
+ ATF_CHECK(info[childidx].pi_pid == child);
+ grandchild = info[childidx ^ 1].pi_pid;
+ ATF_CHECK(grandchild > 0);
+ ATF_CHECK(grandchild != child);
+ ATF_CHECK(grandchild != parent);
+
+ r = kill(child, SIGTERM);
+ ATF_REQUIRE_EQ(0, r);
+
+ pid = waitpid(child, &status, 0);
+ ATF_REQUIRE_EQ(child, pid);
+ ATF_CHECK(WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM);
+
+ memset(info, '\0', sizeof(info));
+ r = procctl(P_PID, parent, PROC_REAP_GETPIDS,
+ &(struct procctl_reaper_pids){
+ .rp_count = sizeof(info) / sizeof(info[0]),
+ .rp_pids = info
+ });
+ ATF_CHECK_EQ(0, r);
+ ATF_CHECK_EQ(REAPER_PIDINFO_VALID,
+ info[0].pi_flags & REAPER_PIDINFO_VALID);
+ ATF_CHECK_EQ(0, info[1].pi_flags & REAPER_PIDINFO_VALID);
+ ATF_CHECK_EQ(child, info[0].pi_subtree);
+ ATF_CHECK_EQ(REAPER_PIDINFO_CHILD,
+ info[0].pi_flags & REAPER_PIDINFO_CHILD);
+ ATF_CHECK_EQ(grandchild, info[0].pi_pid);
+
+ sr = write(pipa[1], &(uint8_t){ 0 }, 1);
+ ATF_REQUIRE_EQ(1, sr);
+
+ memset(info, '\0', sizeof(info));
+ r = procctl(P_PID, parent, PROC_REAP_GETPIDS,
+ &(struct procctl_reaper_pids){
+ .rp_count = sizeof(info) / sizeof(info[0]),
+ .rp_pids = info
+ });
+ ATF_CHECK_EQ(0, r);
+ ATF_CHECK_EQ(REAPER_PIDINFO_VALID,
+ info[0].pi_flags & REAPER_PIDINFO_VALID);
+ ATF_CHECK_EQ(0, info[1].pi_flags & REAPER_PIDINFO_VALID);
+ ATF_CHECK_EQ(child, info[0].pi_subtree);
+ ATF_CHECK_EQ(REAPER_PIDINFO_CHILD,
+ info[0].pi_flags & REAPER_PIDINFO_CHILD);
+ ATF_CHECK_EQ(grandchild, info[0].pi_pid);
+
+ pid = waitpid(grandchild, &status, 0);
+ ATF_REQUIRE_EQ(grandchild, pid);
+ ATF_CHECK_EQ(0, status);
+
+ memset(info, '\0', sizeof(info));
+ r = procctl(P_PID, parent, PROC_REAP_GETPIDS,
+ &(struct procctl_reaper_pids){
+ .rp_count = sizeof(info) / sizeof(info[0]),
+ .rp_pids = info
+ });
+ ATF_CHECK_EQ(0, r);
+ ATF_CHECK_EQ(0, info[0].pi_flags & REAPER_PIDINFO_VALID);
+
+ r = close(pipa[1]);
+ ATF_REQUIRE_EQ(0, r);
+ r = close(pipb[0]);
+ ATF_REQUIRE_EQ(0, r);
+}
+
+ATF_TC_WITHOUT_HEAD(reaper_kill_badsig);
+ATF_TC_BODY(reaper_kill_badsig, tc)
+{
+ struct procctl_reaper_kill params;
+ pid_t parent;
+ int r;
+
+ parent = getpid();
+ r = procctl(P_PID, parent, PROC_REAP_ACQUIRE, NULL);
+ ATF_REQUIRE_EQ(0, r);
+
+ params.rk_sig = -1;
+ params.rk_flags = 0;
+ r = procctl(P_PID, parent, PROC_REAP_KILL, &params);
+ ATF_CHECK(r == -1 && errno == EINVAL);
+}
+
+ATF_TC_WITHOUT_HEAD(reaper_kill_sigzero);
+ATF_TC_BODY(reaper_kill_sigzero, tc)
+{
+ struct procctl_reaper_kill params;
+ pid_t parent;
+ int r;
+
+ parent = getpid();
+ r = procctl(P_PID, parent, PROC_REAP_ACQUIRE, NULL);
+ ATF_REQUIRE_EQ(0, r);
+
+ params.rk_sig = 0;
+ params.rk_flags = 0;
+ r = procctl(P_PID, parent, PROC_REAP_KILL, &params);
+ ATF_CHECK(r == -1 && errno == EINVAL);
+}
+
+ATF_TC_WITHOUT_HEAD(reaper_kill_empty);
+ATF_TC_BODY(reaper_kill_empty, tc)
+{
+ struct procctl_reaper_kill params;
+ pid_t parent;
+ int r;
+
+ parent = getpid();
+ r = procctl(P_PID, parent, PROC_REAP_ACQUIRE, NULL);
+ ATF_REQUIRE_EQ(0, r);
+
+ params.rk_sig = SIGTERM;
+ params.rk_flags = 0;
+ params.rk_killed = 77;
+ r = procctl(P_PID, parent, PROC_REAP_KILL, &params);
+ ATF_CHECK(r == -1 && errno == ESRCH);
+ ATF_CHECK_EQ(0, params.rk_killed);
+}
+
+ATF_TC_WITHOUT_HEAD(reaper_kill_normal);
+ATF_TC_BODY(reaper_kill_normal, tc)
+{
+ struct procctl_reaper_kill params;
+ ssize_t sr;
+ pid_t parent, child, grandchild, pid;
+ int r, status;
+ int pip[2];
+
+ parent = getpid();
+ r = procctl(P_PID, parent, PROC_REAP_ACQUIRE, NULL);
+ ATF_REQUIRE_EQ(0, r);
+
+ r = pipe(pip);
+ ATF_REQUIRE_EQ(0, r);
+ child = fork();
+ ATF_REQUIRE(child != -1);
+ if (child == 0) {
+ if (close(pip[0]) != 0)
+ _exit(100);
+ grandchild = fork();
+ if (grandchild == -1)
+ _exit(101);
+ if (grandchild == 0) {
+ if (write(pip[1], &(uint8_t){ 0 }, 1) != 1)
+ _exit(102);
+ for (;;)
+ pause();
+ }
+ for (;;)
+ pause();
+ }
+ r = close(pip[1]);
+ ATF_REQUIRE_EQ(0, r);
+
+ sr = read(pip[0], &(uint8_t){ 0 }, 1);
+ ATF_REQUIRE_EQ(1, sr);
+
+ params.rk_sig = SIGTERM;
+ params.rk_flags = 0;
+ params.rk_killed = 77;
+ r = procctl(P_PID, parent, PROC_REAP_KILL, &params);
+ ATF_CHECK_EQ(0, r);
+ ATF_CHECK_EQ(2, params.rk_killed);
+
+ pid = waitpid(child, &status, 0);
+ ATF_REQUIRE_EQ(child, pid);
+ ATF_CHECK(WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM);
+
+ pid = waitpid(-1, &status, 0);
+ ATF_REQUIRE(pid > 0);
+ ATF_CHECK(pid != parent);
+ ATF_CHECK(pid != child);
+ ATF_CHECK(WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM);
+
+ r = close(pip[0]);
+ ATF_REQUIRE_EQ(0, r);
+}
+
+ATF_TC_WITHOUT_HEAD(reaper_kill_subtree);
+ATF_TC_BODY(reaper_kill_subtree, tc)
+{
+ struct procctl_reaper_kill params;
+ ssize_t sr;
+ pid_t parent, child1, child2, grandchild1, grandchild2, pid;
+ int r, status;
+ int pip[2];
+
+ parent = getpid();
+ r = procctl(P_PID, parent, PROC_REAP_ACQUIRE, NULL);
+ ATF_REQUIRE_EQ(0, r);
+
+ r = pipe(pip);
+ ATF_REQUIRE_EQ(0, r);
+ child1 = fork();
+ ATF_REQUIRE(child1 != -1);
+ if (child1 == 0) {
+ if (close(pip[0]) != 0)
+ _exit(100);
+ grandchild1 = fork();
+ if (grandchild1 == -1)
+ _exit(101);
+ if (grandchild1 == 0) {
+ if (write(pip[1], &(uint8_t){ 0 }, 1) != 1)
+ _exit(102);
+ for (;;)
+ pause();
+ }
+ for (;;)
+ pause();
+ }
+ child2 = fork();
+ ATF_REQUIRE(child2 != -1);
+ if (child2 == 0) {
+ if (close(pip[0]) != 0)
+ _exit(100);
+ grandchild2 = fork();
+ if (grandchild2 == -1)
+ _exit(101);
+ if (grandchild2 == 0) {
+ if (write(pip[1], &(uint8_t){ 0 }, 1) != 1)
+ _exit(102);
+ for (;;)
+ pause();
+ }
+ for (;;)
+ pause();
+ }
+ r = close(pip[1]);
+ ATF_REQUIRE_EQ(0, r);
+
+ sr = read(pip[0], &(uint8_t){ 0 }, 1);
+ ATF_REQUIRE_EQ(1, sr);
+ sr = read(pip[0], &(uint8_t){ 0 }, 1);
+ ATF_REQUIRE_EQ(1, sr);
+
+ params.rk_sig = SIGUSR1;
+ params.rk_flags = REAPER_KILL_SUBTREE;
+ params.rk_subtree = child1;
+ params.rk_killed = 77;
+ r = procctl(P_PID, parent, PROC_REAP_KILL, &params);
+ ATF_REQUIRE_EQ(0, r);
+ ATF_REQUIRE_EQ(2, params.rk_killed);
+ ATF_CHECK_EQ(-1, params.rk_fpid);
+
+ pid = waitpid(child1, &status, 0);
+ ATF_REQUIRE_EQ(child1, pid);
+ ATF_CHECK(WIFSIGNALED(status) && WTERMSIG(status) == SIGUSR1);
+
+ pid = waitpid(-1, &status, 0);
+ ATF_REQUIRE(pid > 0);
+ ATF_CHECK(pid != parent);
+ ATF_CHECK(pid != child1);
+ ATF_CHECK(pid != child2);
+ ATF_CHECK(WIFSIGNALED(status) && WTERMSIG(status) == SIGUSR1);
+
+ params.rk_sig = SIGUSR2;
+ params.rk_flags = REAPER_KILL_SUBTREE;
+ params.rk_subtree = child2;
+ params.rk_killed = 77;
+ r = procctl(P_PID, parent, PROC_REAP_KILL, &params);
+ ATF_REQUIRE_EQ(0, r);
+ ATF_REQUIRE_EQ(2, params.rk_killed);
+ ATF_CHECK_EQ(-1, params.rk_fpid);
+
+ pid = waitpid(child2, &status, 0);
+ ATF_REQUIRE_EQ(child2, pid);
+ ATF_CHECK(WIFSIGNALED(status) && WTERMSIG(status) == SIGUSR2);
+
+ pid = waitpid(-1, &status, 0);
+ ATF_REQUIRE(pid > 0);
+ ATF_CHECK(pid != parent);
+ ATF_CHECK(pid != child1);
+ ATF_CHECK(pid != child2);
+ ATF_CHECK(WIFSIGNALED(status) && WTERMSIG(status) == SIGUSR2);
+
+ r = close(pip[0]);
+ ATF_REQUIRE_EQ(0, r);
+}
+
+ATF_TC_WITHOUT_HEAD(reaper_pdfork);
+ATF_TC_BODY(reaper_pdfork, tc)
+{
+ struct procctl_reaper_status st;
+ pid_t child, grandchild, parent, pid;
+ int pd, r, status;
+
+ parent = getpid();
+ r = procctl(P_PID, parent, PROC_REAP_ACQUIRE, NULL);
+ ATF_REQUIRE_EQ(r, 0);
+
+ child = pdfork(&pd, 0);
+ ATF_REQUIRE(child != -1);
+ if (child == 0) {
+ grandchild = pdfork(&pd, 0);
+ if (grandchild == -1)
+ _exit(1);
+ if (grandchild == 0)
+ pause();
+ _exit(0);
+ }
+ pid = waitpid(child, &status, 0);
+ ATF_REQUIRE_EQ(pid, child);
+ r = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
+ ATF_REQUIRE_EQ(r, 0);
+
+ r = procctl(P_PID, parent, PROC_REAP_STATUS, &st);
+ ATF_REQUIRE_EQ(r, 0);
+ ATF_CHECK((st.rs_flags & REAPER_STATUS_OWNED) != 0);
+ ATF_CHECK(st.rs_reaper == parent);
+ ATF_CHECK(st.rs_children == 1);
+ ATF_CHECK(st.rs_descendants == 1);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, reaper_wait_child_first);
+ ATF_TP_ADD_TC(tp, reaper_wait_grandchild_first);
+ ATF_TP_ADD_TC(tp, reaper_sigchld_child_first);
+ ATF_TP_ADD_TC(tp, reaper_sigchld_grandchild_first);
+ ATF_TP_ADD_TC(tp, reaper_status);
+ ATF_TP_ADD_TC(tp, reaper_getpids);
+ ATF_TP_ADD_TC(tp, reaper_kill_badsig);
+ ATF_TP_ADD_TC(tp, reaper_kill_sigzero);
+ ATF_TP_ADD_TC(tp, reaper_kill_empty);
+ ATF_TP_ADD_TC(tp, reaper_kill_normal);
+ ATF_TP_ADD_TC(tp, reaper_kill_subtree);
+ ATF_TP_ADD_TC(tp, reaper_pdfork);
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/sched_affinity.c b/tests/sys/kern/sched_affinity.c
new file mode 100644
index 000000000000..9af6076c1649
--- /dev/null
+++ b/tests/sys/kern/sched_affinity.c
@@ -0,0 +1,275 @@
+/*-
+ * Copyright (c) 2022 Dmitry Chagin <dchagin@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/types.h>
+#include <sys/stdint.h>
+#include <sys/sysctl.h>
+
+#include <errno.h>
+#include <sched.h>
+
+#include <atf-c.h>
+
+static uint32_t maxcpuid;
+static uint32_t maxcpus;
+static uint32_t cpus;
+
+static uint32_t
+support_getcpus(void)
+{
+ uint32_t val;
+ size_t sz = sizeof(val);
+
+ ATF_REQUIRE(sysctlbyname("kern.smp.cpus", &val, &sz, NULL, 0) == 0);
+ return (val);
+}
+
+static uint32_t
+support_getmaxcpus(void)
+{
+ uint32_t val;
+ size_t sz = sizeof(val);
+
+ ATF_REQUIRE(sysctlbyname("kern.smp.maxcpus", &val, &sz, NULL, 0) == 0);
+ return (val);
+}
+
+static uint32_t
+support_getmaxcpuid(void)
+{
+ cpuset_t *set;
+ int setsize, rv;
+ uint32_t i, id;
+
+ for (i = 1; i < maxcpus; i++) {
+ setsize = CPU_ALLOC_SIZE(i);
+ set = CPU_ALLOC(i);
+ ATF_REQUIRE(set != NULL);
+ CPU_ZERO_S(setsize, set);
+ rv = cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID,
+ -1, setsize, set);
+ if (rv == 0) {
+ id = __BIT_FLS(i, set) - 1;
+ CPU_FREE(set);
+ break;
+ }
+ CPU_FREE(set);
+ }
+ ATF_REQUIRE(rv == 0);
+ return (id);
+}
+
+ATF_TC_WITHOUT_HEAD(test_setinvalidcpu);
+ATF_TC_BODY(test_setinvalidcpu, tc)
+{
+ size_t cpusetsize;
+ cpuset_t *set;
+ int cpu;
+
+ cpu = maxcpuid > 1 ? maxcpuid - 1 : 0;
+
+ cpusetsize = CPU_ALLOC_SIZE(maxcpuid + 1);
+ set = CPU_ALLOC(maxcpuid + 1);
+ ATF_REQUIRE(set != NULL);
+ CPU_ZERO_S(cpusetsize, set);
+ CPU_SET_S(maxcpuid + 1, cpusetsize, set);
+ CPU_SET_S(cpu, cpusetsize, set);
+ ATF_REQUIRE(sched_setaffinity(0, cpusetsize, set) == 0);
+ CPU_FREE(set);
+
+ cpusetsize = CPU_ALLOC_SIZE(maxcpus + 1);
+ set = CPU_ALLOC(maxcpus + 1);
+ ATF_REQUIRE(set != NULL);
+ CPU_ZERO_S(cpusetsize, set);
+ CPU_SET_S(maxcpuid + 1, cpusetsize, set);
+ CPU_SET_S(cpu, cpusetsize, set);
+ ATF_REQUIRE(cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID,
+ -1, cpusetsize, set) == -1);
+ ATF_REQUIRE_EQ(errno, EINVAL);
+ CPU_FREE(set);
+}
+
+ATF_TC_WITHOUT_HEAD(test_setvalidcpu);
+ATF_TC_BODY(test_setvalidcpu, tc)
+{
+ size_t cpusetsize;
+ cpuset_t *set;
+ int cpu;
+
+ ATF_REQUIRE(maxcpuid < maxcpus);
+ cpu = maxcpuid > 1 ? maxcpuid - 1 : 0;
+
+ cpusetsize = CPU_ALLOC_SIZE(maxcpus + 1);
+ set = CPU_ALLOC(maxcpus + 1);
+ ATF_REQUIRE(set != NULL);
+ CPU_ZERO_S(cpusetsize, set);
+ CPU_SET_S(cpu, cpusetsize, set);
+ ATF_REQUIRE(cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID,
+ -1, cpusetsize, set) == 0);
+ ATF_REQUIRE_EQ(cpu, sched_getcpu());
+ CPU_FREE(set);
+}
+
+ATF_TC_WITHOUT_HEAD(test_setzeroset1);
+ATF_TC_BODY(test_setzeroset1, tc)
+{
+ size_t cpusetsize;
+ cpuset_t *set;
+
+ cpusetsize = CPU_ALLOC_SIZE(maxcpuid + 1);
+ set = CPU_ALLOC(maxcpuid + 1);
+ ATF_REQUIRE(set != NULL);
+ CPU_ZERO_S(cpusetsize, set);
+ ATF_REQUIRE(sched_setaffinity(0, cpusetsize, set) == -1);
+ ATF_REQUIRE_EQ(errno, EINVAL);
+ CPU_FREE(set);
+}
+
+ATF_TC_WITHOUT_HEAD(test_setzeroset2);
+ATF_TC_BODY(test_setzeroset2, tc)
+{
+ size_t cpusetsize;
+ cpuset_t *set;
+
+ cpusetsize = CPU_ALLOC_SIZE(maxcpuid + 1);
+ set = CPU_ALLOC(maxcpuid + 1);
+ ATF_REQUIRE(set != NULL);
+ CPU_ZERO_S(cpusetsize, set);
+ ATF_REQUIRE(cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID,
+ -1, cpusetsize, set) == -1);
+ ATF_REQUIRE_EQ(errno, EDEADLK);
+ CPU_FREE(set);
+}
+
+ATF_TC_WITHOUT_HEAD(test_setmaxsetsize);
+ATF_TC_BODY(test_setmaxsetsize, tc)
+{
+ size_t cpusetsize;
+ cpuset_t *set;
+
+ cpusetsize = CPU_ALLOC_SIZE(maxcpus * 2);
+ set = CPU_ALLOC(maxcpus * 2);
+ ATF_REQUIRE(set != NULL);
+ CPU_ZERO_S(cpusetsize, set);
+ ATF_REQUIRE(CPU_COUNT_S(cpusetsize, set) == 0);
+ CPU_SET_S(0, cpusetsize, set);
+ ATF_REQUIRE(CPU_COUNT_S(cpusetsize, set) == 1);
+ ATF_REQUIRE(cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID,
+ -1, cpusetsize, set) == 0);
+
+ CPU_ZERO_S(cpusetsize, set);
+ CPU_SET_S(maxcpuid, cpusetsize, set);
+ ATF_REQUIRE(cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID,
+ -1, cpusetsize, set) == 0);
+
+ CPU_ZERO_S(cpusetsize, set);
+ CPU_SET_S(maxcpuid + 1, cpusetsize, set);
+ ATF_REQUIRE(cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID,
+ -1, cpusetsize, set) == -1);
+ ATF_REQUIRE_EQ(errno, EINVAL);
+ CPU_FREE(set);
+}
+
+ATF_TC_WITHOUT_HEAD(test_setminsetsize);
+ATF_TC_BODY(test_setminsetsize, tc)
+{
+ size_t cpusetsize = 1;
+ int8_t set;
+
+ if (cpus <= 8)
+ return;
+
+ set = 1;
+ ATF_REQUIRE(cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID,
+ -1, cpusetsize, (const cpuset_t *)&set) == 0);
+ set = 0;
+ ATF_REQUIRE(cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID,
+ -1, cpusetsize, (const cpuset_t *)&set) == -1);
+ ATF_REQUIRE_EQ(errno, EDEADLK);
+}
+
+ATF_TC_WITHOUT_HEAD(test_getminsetsize);
+ATF_TC_BODY(test_getminsetsize, tc)
+{
+ size_t cpusetsize = 1;
+ int8_t set = 0;
+
+ if (cpus < 9)
+ return;
+ ATF_REQUIRE(cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID,
+ -1, cpusetsize, (cpuset_t *)&set) == -1);
+ ATF_REQUIRE_EQ(errno, ERANGE);
+}
+
+ATF_TC_WITHOUT_HEAD(test_getsetsize);
+ATF_TC_BODY(test_getsetsize, tc)
+{
+ size_t cpusetsize;
+ cpuset_t *set;
+
+ cpusetsize = CPU_ALLOC_SIZE(maxcpuid + 1);
+ set = CPU_ALLOC(maxcpuid + 1);
+ ATF_REQUIRE(set != NULL);
+ CPU_ZERO_S(cpusetsize, set);
+ ATF_REQUIRE(cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID,
+ -1, cpusetsize, set) == 0);
+ CPU_FREE(set);
+}
+
+ATF_TC_WITHOUT_HEAD(test_holes);
+ATF_TC_BODY(test_holes, tc)
+{
+ cpuset_t *set;
+ int cpusetsize;
+
+ cpusetsize = CPU_ALLOC_SIZE(maxcpus * 2);
+ set = CPU_ALLOC(maxcpus * 2);
+ ATF_REQUIRE(set != NULL);
+ CPU_ZERO_S(cpusetsize, set);
+ ATF_REQUIRE(CPU_COUNT_S(cpusetsize, set) == 0);
+ CPU_SET_S(maxcpuid, cpusetsize, set);
+ ATF_REQUIRE(CPU_COUNT_S(cpusetsize, set) == 1);
+ ATF_REQUIRE(cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID,
+ -1, cpusetsize, set) == 0);
+
+ CPU_ZERO_S(cpusetsize, set);
+ ATF_REQUIRE(CPU_COUNT_S(cpusetsize, set) == 0);
+ CPU_SET_S(maxcpuid + 1, cpusetsize, set);
+ ATF_REQUIRE(CPU_COUNT_S(cpusetsize, set) == 1);
+ ATF_REQUIRE(cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID,
+ -1, cpusetsize, set) == -1);
+ ATF_REQUIRE_EQ(errno, EINVAL);
+
+ ATF_REQUIRE(CPU_COUNT_S(cpusetsize, set) == 1);
+ ATF_REQUIRE(cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID,
+ -1, cpusetsize, set) == 0);
+ ATF_REQUIRE(CPU_ISSET_S(maxcpuid + 1, cpusetsize, set) == false);
+ ATF_REQUIRE(CPU_ISSET_S(maxcpuid, cpusetsize, set) == true);
+ ATF_REQUIRE_EQ(maxcpuid, (uint32_t)sched_getcpu());
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ cpus = support_getcpus();
+ maxcpus = support_getmaxcpus();
+ maxcpuid = support_getmaxcpuid();
+
+ ATF_TP_ADD_TC(tp, test_setinvalidcpu);
+ ATF_TP_ADD_TC(tp, test_setvalidcpu);
+ ATF_TP_ADD_TC(tp, test_setzeroset1);
+ ATF_TP_ADD_TC(tp, test_setzeroset2);
+
+ ATF_TP_ADD_TC(tp, test_setminsetsize);
+ ATF_TP_ADD_TC(tp, test_setmaxsetsize);
+
+ ATF_TP_ADD_TC(tp, test_getminsetsize);
+ ATF_TP_ADD_TC(tp, test_getsetsize);
+
+ ATF_TP_ADD_TC(tp, test_holes);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/sendfile_helper.c b/tests/sys/kern/sendfile_helper.c
new file mode 100644
index 000000000000..6365531e312c
--- /dev/null
+++ b/tests/sys/kern/sendfile_helper.c
@@ -0,0 +1,177 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2020 Netflix, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/uio.h>
+
+#include <netinet/in.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static char buf[1024*1024];
+ssize_t readlen;
+static volatile bool read_done = false;
+
+static int
+tcp_socketpair(int *sv)
+{
+ struct sockaddr_in sin = {
+ .sin_len = sizeof(struct sockaddr_in),
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
+ };
+ int flags;
+ int ls;
+
+ ls = socket(PF_INET, SOCK_STREAM, 0);
+ if (ls < 0)
+ err(1, "socket ls");
+
+ if (setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, &(socklen_t){1},
+ sizeof(int)) < 0)
+ err(1, "SO_REUSEADDR");
+
+ if (bind(ls, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+ err(1, "bind ls");
+
+ if (getsockname(ls, (struct sockaddr *)&sin,
+ &(socklen_t){ sizeof(sin) }) < 0)
+ err(1, "getsockname");
+
+ if (listen(ls, 5) < 0)
+ err(1, "listen ls");
+
+ sv[0] = socket(PF_INET, SOCK_STREAM, 0);
+ if (sv[0] < 0)
+ err(1, "socket cs");
+
+ flags = fcntl(sv[0], F_GETFL);
+ flags |= O_NONBLOCK;
+ if (fcntl(sv[0], F_SETFL, flags) == -1)
+ err(1, "fcntl +O_NONBLOCK");
+
+ if (connect(sv[0], (void *)&sin, sizeof(sin)) == -1 &&
+ errno != EINPROGRESS)
+ err(1, "connect cs");
+
+ sv[1] = accept(ls, NULL, 0);
+ if (sv[1] < 0)
+ err(1, "accept ls");
+
+ flags &= ~O_NONBLOCK;
+ if (fcntl(sv[0], F_SETFL, flags) == -1)
+ err(1, "fcntl -O_NONBLOCK");
+
+ close(ls);
+
+ return (0);
+}
+
+static void *
+receiver(void *arg)
+{
+ int s = *(int *)arg;
+ ssize_t rv;
+
+ do {
+ rv = read(s, buf, sizeof(buf));
+ if (rv == -1)
+ err(2, "read receiver");
+ if (rv == 0)
+ break;
+ readlen -= rv;
+ } while (readlen != 0);
+
+ read_done = true;
+
+ return NULL;
+}
+
+static void
+usage(void)
+{
+ errx(1, "usage: %s [-u] <file> <start> <len> <flags>", getprogname());
+}
+
+int
+main(int argc, char **argv)
+{
+ pthread_t pt;
+ off_t start;
+ int ch, fd, ss[2], flags, error;
+ bool pf_unix = false;
+
+ while ((ch = getopt(argc, argv, "u")) != -1)
+ switch (ch) {
+ case 'u':
+ pf_unix = true;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 4)
+ usage();
+
+ start = strtoull(argv[1], NULL, 0);
+ readlen = strtoull(argv[2], NULL, 0);
+ flags = strtoul(argv[3], NULL, 0);
+
+ fd = open(argv[0], O_RDONLY);
+ if (fd < 0)
+ err(1, "open");
+
+ if (pf_unix) {
+ if (socketpair(PF_LOCAL, SOCK_STREAM, 0, ss) != 0)
+ err(1, "socketpair");
+ } else
+ tcp_socketpair(ss);
+
+ error = pthread_create(&pt, NULL, receiver, &ss[1]);
+ if (error)
+ errc(1, error, "pthread_create");
+
+ if (sendfile(fd, ss[0], start, readlen, NULL, NULL, flags) < 0)
+ err(3, "sendfile");
+
+ while (!read_done)
+ usleep(1000);
+
+ exit(0);
+}
diff --git a/tests/sys/kern/sendfile_test.sh b/tests/sys/kern/sendfile_test.sh
new file mode 100755
index 000000000000..7e549eec610a
--- /dev/null
+++ b/tests/sys/kern/sendfile_test.sh
@@ -0,0 +1,192 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+#
+# These tests exercise a few basic cases for the sendfile() syscall:
+# - successful operation.
+# - sendfile() starts an async disk read but that async I/O fails.
+# - sendfile() fails to read an indirect block and thus cannot
+# even start an async I/O.
+#
+# In all cases we request some read ahead in addition to
+# the data to be sent to the socket.
+#
+
+MD_DEVS="md.devs"
+MNT=mnt
+FILE=$MNT/file
+HELPER="$(atf_get_srcdir)/sendfile_helper"
+BSIZE=4096
+
+atf_test_case io_success cleanup
+io_success_head()
+{
+ atf_set "descr" "sendfile where all disk I/O succeeds"
+ atf_set "require.user" "root"
+ atf_set "timeout" 15
+}
+io_success_body()
+{
+ if [ "$(atf_config_get qemu false)" = "true" ]; then
+ atf_skip "Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25"
+ fi
+
+ alloc_md md
+ common_body_setup $md
+
+ atf_check $HELPER $FILE 0 0x10000 0x10000
+}
+io_success_cleanup()
+{
+ common_cleanup
+}
+
+atf_test_case io_fail_sync cleanup
+io_fail_sync_head()
+{
+ atf_set "descr" "sendfile where we fail to start async I/O"
+ atf_set "require.user" "root"
+ atf_set "timeout" 15
+}
+io_fail_sync_body()
+{
+ if [ "$(atf_config_get qemu false)" = "true" ]; then
+ atf_skip "Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25"
+ fi
+
+ alloc_md md
+ common_body_setup $md
+
+ atf_check gnop configure -r 100 -e 5 ${md}.nop
+ atf_check -s exit:3 -e ignore $HELPER $FILE $((12 * $BSIZE)) $BSIZE 0x10000
+}
+io_fail_sync_cleanup()
+{
+ common_cleanup
+}
+
+atf_test_case io_fail_async cleanup
+io_fail_async_head()
+{
+ atf_set "descr" "sendfile where an async I/O fails"
+ atf_set "require.user" "root"
+ atf_set "timeout" 15
+}
+io_fail_async_body()
+{
+ if [ "$(atf_config_get qemu false)" = "true" ]; then
+ atf_skip "Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25"
+ fi
+
+ alloc_md md
+ common_body_setup $md
+
+ atf_check gnop configure -r 100 -e 5 ${md}.nop
+ atf_check -s exit:2 -e ignore $HELPER $FILE 0 $BSIZE 0x10000
+}
+io_fail_async_cleanup()
+{
+ common_cleanup
+}
+
+atf_test_case unix_success cleanup
+unix_success_head()
+{
+ atf_set "descr" "sendfile via unix(4) where all disk I/O succeeds"
+ atf_set "require.user" "root"
+ atf_set "timeout" 15
+}
+unix_success_body()
+{
+ if [ "$(atf_config_get qemu false)" = "true" ]; then
+ atf_skip "Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25"
+ fi
+
+ alloc_md md
+ common_body_setup $md
+
+ atf_check $HELPER -u $FILE 0 0x10000 0x10000
+}
+unix_success_cleanup()
+{
+ common_cleanup
+}
+
+
+atf_init_test_cases()
+{
+ atf_add_test_case io_success
+ atf_add_test_case io_fail_sync
+ atf_add_test_case io_fail_async
+ atf_add_test_case unix_success
+}
+
+alloc_md()
+{
+ local _md
+
+ [ -c /dev/mdctl ] || atf_skip "no /dev/mdctl to create md devices"
+ _md=$(mdconfig -a -t swap -s 256M) || atf_fail "mdconfig -a failed"
+ echo ${_md} >> $MD_DEVS
+ eval "${1}='${_md}'"
+}
+
+common_body_setup()
+{
+ us=$1
+
+ atf_check mkdir $MNT
+ atf_check -o ignore -e ignore newfs -b $BSIZE -U -j /dev/${us}
+ atf_check mount /dev/${us} $MNT
+ atf_check -e ignore dd if=/dev/zero of=$FILE bs=1m count=1
+ atf_check umount $MNT
+
+ load_gnop
+ atf_check gnop create /dev/${us}
+ atf_check mount /dev/${us}.nop $MNT
+ atf_check -o ignore ls -l $MNT/file
+}
+
+common_cleanup()
+{
+ umount -f $MNT
+ if [ -f "$MD_DEVS" ]; then
+ while read test_md; do
+ gnop destroy -f ${test_md}.nop 2>/dev/null
+ mdconfig -d -u $test_md 2>/dev/null
+ done < $MD_DEVS
+ rm $MD_DEVS
+ fi
+
+ true
+}
+
+load_gnop()
+{
+ if ! kldstat -q -m g_nop; then
+ geom nop load || atf_skip "could not load module for geom nop"
+ fi
+}
diff --git a/tests/sys/kern/shutdown_dgram.c b/tests/sys/kern/shutdown_dgram.c
new file mode 100644
index 000000000000..c46c2091a5a0
--- /dev/null
+++ b/tests/sys/kern/shutdown_dgram.c
@@ -0,0 +1,111 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Gleb Smirnoff <glebius@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <assert.h>
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <netinet/in.h>
+
+#include <atf-c.h>
+
+/*
+ * shutdown(2) on SOCK_DGRAM shall return ENOTCONN per POSIX. However, there
+ * is historical behavior of the shutdown(2) also unblocking any ongoing
+ * recv(2) syscall on the socket. It is known that some programs rely on this
+ * behavior, but exact list of programs isn't known. Neither we know if the
+ * "feature" is required on PF_UNIX sockets or on PF_INET/INET6 sockets or
+ * on both kinds. Feel free to improve this comment if you know any details.
+ *
+ * List of relevant commits, bug reports and reviews:
+ * 63649db04205
+ * https://reviews.freebsd.org/D10351
+ * b114aa79596c (regresses)
+ * https://reviews.freebsd.org/D3039 (regresses)
+ * kern/84761 c5cff17017f9 aada5cccd878
+ */
+
+
+static void *
+blocking_thread(void *arg)
+{
+ int *s = arg;
+ char buf[1];
+ int error, rv;
+
+ rv = recv(*s, buf, sizeof(buf), 0);
+ error = (rv == -1) ? errno : 0;
+
+ return ((void *)(uintptr_t)error);
+}
+
+static void
+shutdown_thread(int s)
+{
+ pthread_t t;
+ int rv;
+
+ ATF_REQUIRE(pthread_create(&t, NULL, blocking_thread, &s) == 0);
+ usleep(1000);
+ ATF_REQUIRE(shutdown(s, SHUT_RD) == -1);
+ ATF_REQUIRE(errno == ENOTCONN);
+ ATF_REQUIRE(pthread_join(t, (void *)&rv) == 0);
+ ATF_REQUIRE(rv == 0);
+ close(s);
+}
+
+ATF_TC_WITHOUT_HEAD(unblock);
+ATF_TC_BODY(unblock, tc)
+{
+ static const struct sockaddr_un sun = {
+ .sun_family = AF_LOCAL,
+ .sun_len = sizeof(sun),
+ .sun_path = "shutdown-dgram-test-sock",
+ };
+ int s;
+
+ ATF_REQUIRE((s = socket(PF_UNIX, SOCK_DGRAM, 0)) >= 0);
+ ATF_REQUIRE(bind(s, (struct sockaddr *)&sun, sizeof(sun)) == 0);
+ shutdown_thread(s);
+
+ static const struct sockaddr_in sin = {
+ .sin_family = AF_INET,
+ .sin_len = sizeof(sin),
+ };
+ ATF_REQUIRE((s = socket(PF_INET, SOCK_DGRAM, 0)) >= 0);
+ ATF_REQUIRE(bind(s, (struct sockaddr *)&sin, sizeof(sin)) == 0);
+ shutdown_thread(s);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, unblock);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/sigaltstack.c b/tests/sys/kern/sigaltstack.c
new file mode 100644
index 000000000000..36d9542e5548
--- /dev/null
+++ b/tests/sys/kern/sigaltstack.c
@@ -0,0 +1,115 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2018 Eric van Gyzen
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <atf-c.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+/*
+ * TODO add tests for:
+ *
+ * - all error cases
+ * - SS_DISABLE
+ * - no effect without SA_ONSTACK
+ */
+
+static sig_atomic_t disabled_correct = 1;
+static sig_atomic_t level1_correct = -1;
+static sig_atomic_t level2_correct = -1;
+
+static void
+sig_handler(int signo, siginfo_t *info __unused, void *ucp)
+{
+ ucontext_t *uc = ucp;
+
+ // The alternate signal stack is enabled.
+ disabled_correct = disabled_correct &&
+ (uc->uc_stack.ss_flags & SS_DISABLE) == 0;
+ if (signo == SIGUSR1) {
+ // The thread was NOT running on the alternate signal
+ // stack when this signal arrived.
+ level1_correct = (uc->uc_stack.ss_flags & SS_ONSTACK) == 0;
+ raise(SIGUSR2);
+ } else {
+ // The thread WAS running on the alternate signal
+ // stack when this signal arrived.
+ level2_correct = (uc->uc_stack.ss_flags & SS_ONSTACK) != 0;
+ }
+}
+
+ATF_TC(ss_onstack);
+
+ATF_TC_HEAD(ss_onstack, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "Test reporting of SS_ONSTACK");
+}
+
+ATF_TC_BODY(ss_onstack, tc)
+{
+ stack_t ss = {
+ .ss_size = SIGSTKSZ,
+ };
+ stack_t oss = {
+ .ss_size = 0,
+ };
+
+ ss.ss_sp = malloc(ss.ss_size);
+ ATF_REQUIRE(ss.ss_sp != NULL);
+ ATF_REQUIRE(sigaltstack(&ss, &oss) == 0);
+
+ // There should be no signal stack currently configured.
+ ATF_CHECK(oss.ss_sp == NULL);
+ ATF_CHECK(oss.ss_size == 0);
+ ATF_CHECK((oss.ss_flags & SS_DISABLE) != 0);
+ ATF_CHECK((oss.ss_flags & SS_ONSTACK) == 0);
+
+ struct sigaction sa = {
+ .sa_sigaction = sig_handler,
+ .sa_flags = SA_ONSTACK | SA_SIGINFO,
+ };
+ ATF_REQUIRE(sigemptyset(&sa.sa_mask) == 0);
+ ATF_REQUIRE(sigaction(SIGUSR1, &sa, NULL) == 0);
+ ATF_REQUIRE(sigaction(SIGUSR2, &sa, NULL) == 0);
+ ATF_REQUIRE(raise(SIGUSR1) == 0);
+
+ ATF_CHECK(level1_correct != -1);
+ ATF_CHECK(level1_correct == 1);
+ ATF_CHECK(level2_correct != -1);
+ ATF_CHECK(level2_correct == 1);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, ss_onstack);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/sigsys.c b/tests/sys/kern/sigsys.c
new file mode 100644
index 000000000000..d135ed777498
--- /dev/null
+++ b/tests/sys/kern/sigsys.c
@@ -0,0 +1,142 @@
+/*-
+ * Copyright (c) 2023 The FreeBSD Foundation
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * This software were developed by Konstantin Belousov <kib@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ */
+
+#include <sys/param.h>
+#include <sys/syscall.h>
+#include <sys/sysctl.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdatomic.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+static sig_atomic_t sigsys_cnt;
+
+#define SAVEDVALUE "savedsignosys"
+
+static void
+sigsys_handler(int signo, siginfo_t *si, void *ucp)
+{
+ sigsys_cnt++;
+}
+
+static void
+sigsys_test(int knob)
+{
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = sigsys_handler;
+ sa.sa_flags = SA_SIGINFO;
+ ATF_REQUIRE(sigaction(SIGSYS, &sa, NULL) == 0);
+
+ ATF_REQUIRE(syscall(273) == -1); /* reserved */
+ ATF_CHECK_ERRNO(ENOSYS, true);
+ atomic_signal_fence(memory_order_seq_cst);
+ ATF_CHECK_EQ(1 * knob, sigsys_cnt);
+
+ ATF_REQUIRE(syscall(440) == -1); /* SYS_kse_switchin */
+ ATF_CHECK_ERRNO(ENOSYS, true);
+ atomic_signal_fence(memory_order_seq_cst);
+ ATF_CHECK_EQ(2 * knob, sigsys_cnt);
+
+ /* Hope this is enough for say next two months */
+ ATF_REQUIRE(syscall(3000000) == -1);
+ ATF_CHECK_ERRNO(ENOSYS, true);
+ atomic_signal_fence(memory_order_seq_cst);
+ ATF_CHECK_EQ(3 * knob, sigsys_cnt);
+
+ ATF_REQUIRE(syscall(SYS_afs3_syscall) == -1);
+ ATF_CHECK_ERRNO(ENOSYS, true);
+ atomic_signal_fence(memory_order_seq_cst);
+ ATF_CHECK_EQ(4 * knob, sigsys_cnt);
+}
+
+static void
+sysctlset(const char *name, int val)
+{
+ size_t oldlen = sizeof(int);
+ int oldval;
+ char buf[80];
+
+ ATF_REQUIRE(sysctlbyname(name, &oldval, &oldlen, NULL, 0) == 0);
+
+ /* Store old %name in a symlink for cleanup */
+ snprintf(buf, sizeof(buf), "%d", oldval);
+ ATF_REQUIRE(symlink(buf, SAVEDVALUE) == 0);
+
+ ATF_REQUIRE(sysctlbyname(name, NULL, NULL, &val, sizeof(val)) == 0);
+}
+
+static void
+sysctlcleanup(const char *name)
+{
+ size_t oldlen;
+ int n, oldval;
+ char buf[80];
+
+ if ((n = readlink(SAVEDVALUE, buf, sizeof(buf))) > 0) {
+ buf[MIN((size_t)n, sizeof(buf) - 1)] = '\0';
+ if (sscanf(buf, "%d", &oldval) == 1) {
+ oldlen = sizeof(oldval);
+ (void)sysctlbyname(name, NULL, 0,
+ &oldval, oldlen);
+ }
+ }
+ (void)unlink(SAVEDVALUE);
+}
+
+ATF_TC_WITH_CLEANUP(sigsys_test_on);
+ATF_TC_HEAD(sigsys_test_on, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.config", "allow_sysctl_side_effects");
+ atf_tc_set_md_var(tc, "descr",
+ "Testing delivery of SIGSYS on invalid syscalls");
+}
+
+ATF_TC_BODY(sigsys_test_on, tc)
+{
+ sysctlset("kern.signosys", 1);
+ sigsys_test(1);
+}
+
+ATF_TC_CLEANUP(sigsys_test_on, tc)
+{
+ sysctlcleanup("kern.signosys");
+}
+
+ATF_TC_WITH_CLEANUP(sigsys_test_off);
+ATF_TC_HEAD(sigsys_test_off, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.config", "allow_sysctl_side_effects");
+ atf_tc_set_md_var(tc, "descr",
+ "Testing SIGSYS silence on invalid syscalls");
+}
+
+ATF_TC_BODY(sigsys_test_off, tc)
+{
+ sysctlset("kern.signosys", 0);
+ sigsys_test(0);
+}
+
+ATF_TC_CLEANUP(sigsys_test_off, tc)
+{
+ sysctlcleanup("kern.signosys");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, sigsys_test_on);
+ ATF_TP_ADD_TC(tp, sigsys_test_off);
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/sigwait.c b/tests/sys/kern/sigwait.c
new file mode 100644
index 000000000000..b971985de02f
--- /dev/null
+++ b/tests/sys/kern/sigwait.c
@@ -0,0 +1,583 @@
+/*-
+ * Copyright (c) 2022 Dmitry Chagin <dchagin@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+#include <sys/cdefs.h>
+#include <sys/limits.h>
+#include <sys/time.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+
+static inline struct timespec
+make_timespec(time_t s, long int ns)
+{
+ struct timespec rts;
+
+ rts.tv_sec = s;
+ rts.tv_nsec = ns;
+ return (rts);
+}
+
+static void
+dummy_sig_handler(int sig)
+{
+
+}
+
+static void
+dummy_sigchld(int signo, siginfo_t *info, void *ctx)
+{
+
+}
+
+static void
+support_signal(int sig, sig_t handler)
+{
+
+ ATF_REQUIRE(signal(sig, handler) != SIG_ERR);
+}
+
+static void
+support_sysctlset(const char *name, int32_t val)
+{
+
+ ATF_REQUIRE(sysctlbyname(name, NULL, NULL, &val, sizeof(val)) == 0);
+}
+
+static timer_t
+support_create_timer(uint64_t sec, long int nsec, bool repeat,
+ bool callback)
+{
+ struct sigevent ev = {
+ .sigev_notify = SIGEV_SIGNAL,
+ .sigev_signo = SIGALRM
+ };
+ struct itimerspec its =
+ {
+ { .tv_sec = repeat ? sec : 0, .tv_nsec = repeat ? nsec : 0 },
+ { .tv_sec = sec, .tv_nsec = nsec }
+ };
+ struct sigaction sa;
+ timer_t timerid;
+
+ if (callback) {
+ sa.sa_handler = dummy_sig_handler;
+ sigemptyset (&sa.sa_mask);
+ sa.sa_flags = 0;
+ ATF_REQUIRE(sigaction(SIGALRM, &sa, NULL) == 0);
+ }
+ ATF_REQUIRE(timer_create(CLOCK_REALTIME, &ev, &timerid) == 0);
+ ATF_REQUIRE(timer_settime(timerid, 0, &its, NULL) == 0);
+ return (timerid);
+}
+
+static void
+support_delete_timer(timer_t timer)
+{
+
+ ATF_REQUIRE(timer_delete(timer) == 0);
+ support_signal(SIGALRM, SIG_DFL);
+}
+
+static pid_t
+support_create_sig_proc(int sig, int count, unsigned int usec)
+{
+ pid_t pid, cpid;
+
+ pid = getpid();
+ ATF_REQUIRE(pid > 0);
+ cpid = fork();
+ ATF_REQUIRE(cpid >= 0);
+
+ if (cpid == 0) {
+ while (count-- > 0) {
+ usleep(usec);
+ if (kill(pid, sig) == -1)
+ break;
+ }
+ exit(0);
+ }
+ return (cpid);
+}
+
+#define TIMESPEC_HZ 1000000000
+
+static void
+test_sigtimedwait_timeout_eagain(time_t sec, bool zero_tmo)
+{
+ struct timespec ts, timeout, now;
+ sigset_t ss;
+ int rv;
+
+ ATF_REQUIRE(clock_gettime(CLOCK_REALTIME, &ts) == 0);
+
+ timeout = make_timespec(sec, zero_tmo ? 0 : TIMESPEC_HZ/2);
+ timespecadd(&ts, &timeout, &ts);
+
+ sigemptyset(&ss);
+ sigaddset(&ss, SIGUSR1);
+ rv = sigtimedwait(&ss, NULL, &timeout);
+ ATF_REQUIRE_EQ_MSG(-1, rv,
+ "sigtimedwait () should fail: rv %d, errno %d", rv, errno);
+ ATF_REQUIRE_EQ_MSG(EAGAIN, errno,
+ "sigtimedwait() should fail with EAGAIN: rv %d, errno %d",
+ rv, errno);
+ /* now >= ts */
+ ATF_REQUIRE(clock_gettime(CLOCK_REALTIME, &now) == 0);
+ ATF_REQUIRE_MSG(timespeccmp(&now, &ts, >=) == true,
+ "timespeccmp: now { %jd.%ld } < ts { %jd.%ld }",
+ (intmax_t)now.tv_sec, now.tv_nsec,
+ (intmax_t)ts.tv_sec, ts.tv_nsec);
+}
+
+ATF_TC(test_sigtimedwait_timeout_eagain0);
+ATF_TC_HEAD(test_sigtimedwait_timeout_eagain0, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "Check if sigtimedwait exits immediately");
+}
+
+ATF_TC_BODY(test_sigtimedwait_timeout_eagain0, tc)
+{
+
+ test_sigtimedwait_timeout_eagain(0, true);
+}
+
+ATF_TC(test_sigtimedwait_timeout_eagain1);
+ATF_TC_HEAD(test_sigtimedwait_timeout_eagain1, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "Check if sigtimedwait exits immediately");
+}
+
+ATF_TC_BODY(test_sigtimedwait_timeout_eagain1, tc)
+{
+
+ test_sigtimedwait_timeout_eagain(-1, true);
+}
+
+ATF_TC(test_sigtimedwait_timeout_eagain2);
+ATF_TC_HEAD(test_sigtimedwait_timeout_eagain2, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "Check if sigtimedwait exits immediately");
+}
+
+ATF_TC_BODY(test_sigtimedwait_timeout_eagain2, tc)
+{
+
+ test_sigtimedwait_timeout_eagain(-1, false);
+}
+
+ATF_TC(test_sigtimedwait_timeout_eagain3);
+ATF_TC_HEAD(test_sigtimedwait_timeout_eagain3, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "Check if sigtimedwait exits after specified timeout");
+}
+
+ATF_TC_BODY(test_sigtimedwait_timeout_eagain3, tc)
+{
+
+ test_sigtimedwait_timeout_eagain(0, false);
+}
+
+ATF_TC(test_sigtimedwait_large_timeout_eintr);
+ATF_TC_HEAD(test_sigtimedwait_large_timeout_eintr, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "Check if sigtimedwait exits with EINTR");
+}
+
+ATF_TC_BODY(test_sigtimedwait_large_timeout_eintr, tc)
+{
+ struct timespec ts;
+ timer_t timerid;
+ sigset_t ss;
+ int rv;
+
+ ts = make_timespec(LONG_MAX, 0);
+ timerid = support_create_timer(0, 100000000, false, true);
+
+ sigemptyset(&ss);
+ sigaddset(&ss, SIGUSR1);
+ rv = sigtimedwait(&ss, NULL, &ts);
+ ATF_REQUIRE_EQ_MSG(-1, rv,
+ "sigtimedwait () should fail: rv %d, errno %d", rv, errno);
+ ATF_REQUIRE_EQ_MSG(EINTR, errno,
+ "sigtimedwait() should fail with EINTR: rv %d, errno %d",
+ rv, errno);
+ support_delete_timer(timerid);
+}
+
+ATF_TC(test_sigtimedwait_infinity);
+ATF_TC_HEAD(test_sigtimedwait_infinity, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "Check if sigtimedwait exits with EINTR");
+}
+
+ATF_TC_BODY(test_sigtimedwait_infinity, tc)
+{
+ timer_t timerid;
+ sigset_t ss;
+ int rv;
+
+ timerid = support_create_timer(0, 100000000, false, true);
+
+ sigemptyset(&ss);
+ sigaddset(&ss, SIGUSR1);
+ rv = sigtimedwait(&ss, NULL, NULL);
+ ATF_REQUIRE_EQ_MSG(-1, rv,
+ "sigtimedwait () should fail: rv %d, errno %d", rv, errno);
+ ATF_REQUIRE_EQ_MSG(EINTR, errno,
+ "sigtimedwait() should fail with EINTR: rv %d, errno %d",
+ rv, errno);
+ support_delete_timer(timerid);
+}
+
+ATF_TC(test_sigtimedwait_einval);
+ATF_TC_HEAD(test_sigtimedwait_einval, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "Check if sigtimedwait exits with EINVAL");
+}
+
+ATF_TC_BODY(test_sigtimedwait_einval, tc)
+{
+ struct timespec ts;
+ timer_t timerid;
+ sigset_t ss;
+ int rv;
+
+ ts = make_timespec(0, -1);
+ timerid = support_create_timer(0, 100000000, false, true);
+
+ sigemptyset(&ss);
+ sigaddset(&ss, SIGUSR1);
+ rv = sigtimedwait(&ss, NULL, &ts);
+ ATF_REQUIRE_EQ_MSG(-1, rv,
+ "sigtimedwait () should fail: rv %d, errno %d", rv, errno);
+ ATF_REQUIRE_EQ_MSG(EINVAL, errno,
+ "sigtimedwait() should fail with EINVAL: rv %d, errno %d",
+ rv, errno);
+ support_delete_timer(timerid);
+}
+
+ATF_TC(test_sigwait_eintr);
+ATF_TC_HEAD(test_sigwait_eintr, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "Check if sigwait exits with EINTR");
+}
+
+ATF_TC_BODY(test_sigwait_eintr, tc)
+{
+ timer_t timerid;
+ sigset_t ss;
+ int rv, sig, pid;
+
+ support_signal(SIGUSR1, dummy_sig_handler);
+
+ pid = support_create_sig_proc(SIGUSR1, 1, 400000);
+ timerid = support_create_timer(0, 200000, false, true);
+
+ sigemptyset(&ss);
+ sigaddset(&ss, SIGUSR1);
+ rv = sigwait(&ss, &sig);
+ ATF_REQUIRE_EQ_MSG(0, rv,
+ "sigwait() should not fail: rv %d, errno %d", rv, errno);
+ ATF_REQUIRE_EQ_MSG(SIGUSR1, sig,
+ "sigwait() should return SIGUSR1: rv %d, sig %d", rv, sig);
+ ATF_REQUIRE(waitid(P_PID, pid, NULL, WEXITED) == 0);
+ support_delete_timer(timerid);
+}
+
+ATF_TC(test_sigwaitinfo_eintr);
+ATF_TC_HEAD(test_sigwaitinfo_eintr, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "Check if sigwaitinfo exits with EINTR");
+}
+
+ATF_TC_BODY(test_sigwaitinfo_eintr, tc)
+{
+ timer_t timerid;
+ sigset_t ss;
+ int rv;
+
+ timerid = support_create_timer(0, 100000000, false, true);
+
+ sigemptyset(&ss);
+ sigaddset(&ss, SIGUSR1);
+ rv = sigwaitinfo(&ss, NULL);
+ ATF_REQUIRE_EQ_MSG(-1, rv,
+ "sigwaitinfo() should fail, rv %d != -1", rv);
+ ATF_REQUIRE_EQ_MSG(EINTR, errno,
+ "sigwaitinfo() should fail errno %d != EINTR", errno);
+ support_delete_timer(timerid);
+}
+
+/*
+ * Test kern.sig_discard_ign knob (default true).
+ * See commit bc387624
+ */
+static void
+test_sig_discard_ign(bool ignore)
+{
+ struct timespec ts;
+ sigset_t mask;
+ pid_t pid;
+ int rv;
+
+ support_signal(SIGUSR2, SIG_IGN);
+
+ if (ignore)
+ support_sysctlset("kern.sig_discard_ign", 1);
+ else
+ support_sysctlset("kern.sig_discard_ign", 0);
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR2);
+ ATF_REQUIRE(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
+
+ pid = support_create_sig_proc(SIGUSR2, 1, 100000);
+
+ ts = make_timespec(1, 0);
+ rv = sigtimedwait(&mask, NULL, &ts);
+ if (ignore == true) {
+ ATF_REQUIRE_EQ_MSG(-1, rv,
+ "sigtimedwait() ign=on should fail, rv %d != -1", rv);
+ ATF_REQUIRE_EQ_MSG(EAGAIN, errno,
+ "sigtimedwait() ign=on should fail with EAGAIN errno %d",
+ errno);
+ } else
+ ATF_REQUIRE_EQ_MSG(SIGUSR2, rv,
+ "sigtimedwait() ign=off should return SIGUSR2, rv %d errno %d",
+ rv, errno);
+ ATF_REQUIRE(waitid(P_PID, pid, NULL, WEXITED) == 0);
+}
+
+static void
+support_check_siginfo(int code, int status, pid_t pid,
+ siginfo_t *si, int sig)
+{
+
+ ATF_REQUIRE_EQ_MSG(sig, si->si_signo,
+ "check_siginfo: si_signo %d != sig %d", si->si_signo, sig);
+ ATF_REQUIRE_EQ_MSG(code, si->si_code,
+ "check_siginfo: si_code %d != code %d", si->si_code, code);
+ ATF_REQUIRE_EQ_MSG(status, si->si_status,
+ "check_siginfo: si_status %d != status %d", si->si_status, status);
+ ATF_REQUIRE_EQ_MSG(pid, si->si_pid,
+ "check_siginfo: si_pid %d != pid %d", si->si_pid, pid);
+}
+
+static void
+support_check_sigchld(sigset_t *set, int code, int status, pid_t pid,
+ bool dequeue)
+{
+ siginfo_t si;
+ int sig, kpid;
+
+ if (dequeue == true)
+ kpid = support_create_sig_proc(SIGUSR2, 1, 1000000);
+
+ sig = sigwaitinfo(set, &si);
+ if (dequeue == true) {
+ ATF_REQUIRE_EQ_MSG(-1, sig,
+ "sigwaitinfo() should fail, sig %d != -1", sig);
+ ATF_REQUIRE_EQ_MSG(EINTR, errno,
+ "sigwaitinfo() should fail errno %d != EINTR", errno);
+ } else
+ ATF_REQUIRE_EQ_MSG(SIGCHLD, sig,
+ "sigwaitinfo() %d != SIGCHLD", sig);
+ if (dequeue == false)
+ support_check_siginfo(code, status, pid, &si, SIGCHLD);
+ if (dequeue == true)
+ ATF_REQUIRE(waitid(P_PID, kpid, &si, WEXITED) == 0);
+}
+
+static void
+test_child(void)
+{
+
+ raise(SIGSTOP);
+ while (1)
+ pause();
+}
+
+/*
+ * Test kern.wait_dequeue_sigchld knob.
+ */
+static void
+test_wait_dequeue_sigchld(bool dequeue)
+{
+ struct sigaction sa;
+ siginfo_t si;
+ sigset_t set;
+ pid_t pid;
+
+ sa.sa_flags = SA_SIGINFO | SA_RESTART;
+ sa.sa_sigaction = dummy_sigchld;
+ sigemptyset(&sa.sa_mask);
+ ATF_REQUIRE(sigaction(SIGCHLD, &sa, NULL) == 0);
+
+ support_signal(SIGUSR2, dummy_sig_handler);
+
+ sigemptyset(&set);
+ sigaddset(&set, SIGCHLD);
+ ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) == 0);
+
+ if (dequeue)
+ support_sysctlset("kern.wait_dequeue_sigchld", 1);
+ else
+ support_sysctlset("kern.wait_dequeue_sigchld", 0);
+
+ pid = fork();
+ ATF_REQUIRE(pid >= 0);
+ if (pid == 0) {
+ test_child();
+ exit(0);
+ }
+
+ bzero(&si, sizeof(si));
+ ATF_REQUIRE(waitid(P_PID, pid, &si, WSTOPPED) == 0);
+
+ support_check_siginfo(CLD_STOPPED, SIGSTOP, pid, &si, SIGCHLD);
+ support_check_sigchld(&set, CLD_STOPPED, SIGSTOP, pid, dequeue);
+
+ ATF_REQUIRE(kill(pid, SIGCONT) == 0);
+
+ bzero(&si, sizeof(si));
+ ATF_REQUIRE(waitid(P_PID, pid, &si, WCONTINUED) == 0);
+
+ support_check_siginfo(CLD_CONTINUED, SIGCONT, pid, &si, SIGCHLD);
+ support_check_sigchld(&set, CLD_CONTINUED, SIGCONT, pid, dequeue);
+
+ ATF_REQUIRE(kill(pid, SIGKILL) == 0);
+
+ bzero(&si, sizeof(si));
+ ATF_REQUIRE(waitid(P_PID, pid, &si, WEXITED) == 0);
+
+ support_check_siginfo(CLD_KILLED, SIGKILL, pid, &si, SIGCHLD);
+}
+
+ATF_TC_WITH_CLEANUP(test_sig_discard_ign_true);
+ATF_TC_HEAD(test_sig_discard_ign_true, tc)
+{
+
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "descr", "Test kern.sig_discard_ign on");
+}
+
+ATF_TC_BODY(test_sig_discard_ign_true, tc)
+{
+
+ test_sig_discard_ign(true);
+}
+
+ATF_TC_CLEANUP(test_sig_discard_ign_true, tc)
+{
+
+ support_sysctlset("kern.sig_discard_ign", 1);
+}
+
+ATF_TC_WITH_CLEANUP(test_sig_discard_ign_false);
+ATF_TC_HEAD(test_sig_discard_ign_false, tc)
+{
+
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "descr", "Test kern.sig_discard_ign off");
+}
+
+ATF_TC_BODY(test_sig_discard_ign_false, tc)
+{
+
+ test_sig_discard_ign(false);
+}
+
+ATF_TC_CLEANUP(test_sig_discard_ign_false, tc)
+{
+
+ support_sysctlset("kern.sig_discard_ign", 1);
+}
+
+ATF_TC_WITH_CLEANUP(test_wait_dequeue_sigchld_true);
+ATF_TC_HEAD(test_wait_dequeue_sigchld_true, tc)
+{
+
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "descr", "Test kern.wait_dequeue_sigchld on");
+}
+
+ATF_TC_BODY(test_wait_dequeue_sigchld_true, tc)
+{
+
+ test_wait_dequeue_sigchld(true);
+}
+
+ATF_TC_CLEANUP(test_wait_dequeue_sigchld_true, tc)
+{
+
+ support_sysctlset("kern.wait_dequeue_sigchld", 1);
+}
+
+ATF_TC_WITH_CLEANUP(test_wait_dequeue_sigchld_false);
+ATF_TC_HEAD(test_wait_dequeue_sigchld_false, tc)
+{
+
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "descr", "Test kern.wait_dequeue_sigchld off");
+}
+
+ATF_TC_BODY(test_wait_dequeue_sigchld_false, tc)
+{
+
+ test_wait_dequeue_sigchld(false);
+}
+
+ATF_TC_CLEANUP(test_wait_dequeue_sigchld_false, tc)
+{
+
+ support_sysctlset("kern.wait_dequeue_sigchld", 1);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, test_sigtimedwait_timeout_eagain0);
+ ATF_TP_ADD_TC(tp, test_sigtimedwait_timeout_eagain1);
+ ATF_TP_ADD_TC(tp, test_sigtimedwait_timeout_eagain2);
+ ATF_TP_ADD_TC(tp, test_sigtimedwait_timeout_eagain3);
+
+ ATF_TP_ADD_TC(tp, test_sigtimedwait_large_timeout_eintr);
+ ATF_TP_ADD_TC(tp, test_sigtimedwait_infinity);
+
+ ATF_TP_ADD_TC(tp, test_sigtimedwait_einval);
+
+ ATF_TP_ADD_TC(tp, test_sigwait_eintr);
+ ATF_TP_ADD_TC(tp, test_sigwaitinfo_eintr);
+
+ ATF_TP_ADD_TC(tp, test_sig_discard_ign_true);
+ ATF_TP_ADD_TC(tp, test_sig_discard_ign_false);
+
+ ATF_TP_ADD_TC(tp, test_wait_dequeue_sigchld_true);
+ ATF_TP_ADD_TC(tp, test_wait_dequeue_sigchld_false);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/socket_accept.c b/tests/sys/kern/socket_accept.c
new file mode 100644
index 000000000000..24c3210900d6
--- /dev/null
+++ b/tests/sys/kern/socket_accept.c
@@ -0,0 +1,127 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Gleb Smirnoff <glebius@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+static int
+tcp4_listensock(struct sockaddr_in *sin)
+{
+ int l;
+
+ ATF_REQUIRE((l = socket(PF_INET, SOCK_STREAM, 0)) > 0);
+ ATF_REQUIRE(setsockopt(l, SOL_SOCKET, SO_REUSEADDR, &(socklen_t){1},
+ sizeof(int)) == 0);
+ *sin = (struct sockaddr_in){
+ .sin_len = sizeof(sin),
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
+ };
+ ATF_REQUIRE(bind(l, (struct sockaddr *)sin, sizeof(*sin)) == 0);
+ ATF_REQUIRE(getsockname(l, (struct sockaddr *)sin,
+ &(socklen_t){ sizeof(*sin) }) == 0);
+ ATF_REQUIRE(listen(l, -1) == 0);
+
+ return (l);
+}
+
+static int
+tcp4_clientsock(struct sockaddr_in *sin)
+{
+ int s;
+
+ ATF_REQUIRE((s = socket(PF_INET, SOCK_STREAM, 0)) > 0);
+ ATF_REQUIRE(connect(s, (struct sockaddr *)sin, sizeof(*sin)) == 0);
+
+ return (s);
+}
+
+ATF_TC_WITHOUT_HEAD(tcp4_zerolen);
+ATF_TC_BODY(tcp4_zerolen, tc)
+{
+ static char canary[sizeof(struct sockaddr_in)] =
+ { [0 ... sizeof(struct sockaddr_in) - 1] = 0xa };
+ struct sockaddr_in sin, ret;
+ socklen_t salen;
+ int l;
+
+ l = tcp4_listensock(&sin);
+ (void )tcp4_clientsock(&sin);
+
+ memcpy(&ret, &canary, sizeof(ret));
+ salen = 0;
+ ATF_REQUIRE(accept(l, (struct sockaddr *)&ret, &salen) > 0);
+ ATF_REQUIRE(memcmp(&ret, &canary, sizeof(ret)) == 0);
+ ATF_REQUIRE(salen == sizeof(struct sockaddr_in));
+ /* Note: Linux will block for connection here, we fail immediately. */
+ ATF_REQUIRE(accept(l, (struct sockaddr *)&ret, NULL) == -1);
+ ATF_REQUIRE(errno == EFAULT);
+}
+
+ATF_TC_WITHOUT_HEAD(tcp4);
+ATF_TC_BODY(tcp4, tc)
+{
+ struct sockaddr_in sin, ret;
+ socklen_t salen;
+ int l, s;
+
+ l = tcp4_listensock(&sin);
+ s = tcp4_clientsock(&sin);
+
+ salen = sizeof(struct sockaddr_in) + 2;
+ ATF_REQUIRE(accept(l, (struct sockaddr *)&ret, &salen) > 0);
+ ATF_REQUIRE(salen == sizeof(struct sockaddr_in));
+ ATF_REQUIRE(getsockname(s, (struct sockaddr *)&sin,
+ &(socklen_t){ sizeof(sin) }) == 0);
+ ATF_REQUIRE(memcmp(&ret, &sin, sizeof(sin)) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(tcp4_noaddr);
+ATF_TC_BODY(tcp4_noaddr, tc)
+{
+ struct sockaddr_in sin;
+ int l;
+
+ l = tcp4_listensock(&sin);
+ (void )tcp4_clientsock(&sin);
+
+ ATF_REQUIRE(accept(l, NULL, NULL) > 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, tcp4);
+ ATF_TP_ADD_TC(tp, tcp4_noaddr);
+ ATF_TP_ADD_TC(tp, tcp4_zerolen);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/socket_accf.c b/tests/sys/kern/socket_accf.c
new file mode 100644
index 000000000000..ae6522397cf7
--- /dev/null
+++ b/tests/sys/kern/socket_accf.c
@@ -0,0 +1,251 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2022-2024 Gleb Smirnoff <glebius@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+#include <atf-c.h>
+
+static int
+listensock(struct sockaddr_in *sin)
+{
+ int l;
+
+ ATF_REQUIRE((l = socket(PF_INET, SOCK_STREAM, 0)) > 0);
+ ATF_REQUIRE(fcntl(l, F_SETFL, O_NONBLOCK) != -1);
+ ATF_REQUIRE(setsockopt(l, SOL_SOCKET, SO_REUSEADDR, &(socklen_t){1},
+ sizeof(int)) == 0);
+ *sin = (struct sockaddr_in){
+ .sin_len = sizeof(sin),
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
+ };
+ ATF_REQUIRE(bind(l, (struct sockaddr *)sin, sizeof(*sin)) == 0);
+ ATF_REQUIRE(getsockname(l, (struct sockaddr *)sin,
+ &(socklen_t){ sizeof(*sin) }) == 0);
+ ATF_REQUIRE(listen(l, -1) == 0);
+
+ return (l);
+}
+
+static int
+clientsock(struct sockaddr_in *sin)
+{
+ int s;
+
+ ATF_REQUIRE((s = socket(PF_INET, SOCK_STREAM, 0)) > 0);
+ ATF_REQUIRE(connect(s, (struct sockaddr *)sin, sizeof(*sin)) == 0);
+
+ return (s);
+}
+
+static void
+accfon(int l, struct accept_filter_arg *af)
+{
+
+ if (setsockopt(l, SOL_SOCKET, SO_ACCEPTFILTER, af, sizeof(*af)) != 0) {
+ if (errno == ENOENT)
+ atf_tc_skip("Accept filter %s not loaded in kernel",
+ af->af_name);
+ else
+ atf_tc_fail("setsockopt(SO_ACCEPTFILTER): %s",
+ strerror(errno));
+ }
+}
+
+/*
+ * XXX: return from send(2) on a localhost connection doesn't guarantee that
+ * netisr has fully processed and delivered the data to the remote local
+ * socket. Sleep a fraction of second to "guarantee" that it did.
+ */
+static ssize_t
+usend(int s, const void *msg, size_t len)
+{
+ ssize_t rv;
+
+ rv = send(s, msg, len, 0);
+ usleep(100000);
+ return (rv);
+}
+
+ATF_TC_WITHOUT_HEAD(data);
+ATF_TC_BODY(data, tc)
+{
+ struct accept_filter_arg afa = {
+ .af_name = "dataready"
+ };
+ struct sockaddr_in sin;
+ int l, s, a;
+
+ l = listensock(&sin);
+ accfon(l, &afa);
+ s = clientsock(&sin);
+ ATF_REQUIRE(accept(l, NULL, 0) == -1);
+ ATF_REQUIRE(errno == EAGAIN);
+ ATF_REQUIRE(usend(s, "foo", sizeof("foo")) == sizeof("foo"));
+ ATF_REQUIRE((a = accept(l, NULL, 0)) > 0);
+}
+
+ATF_TC_WITHOUT_HEAD(http);
+ATF_TC_BODY(http, tc)
+{
+ struct accept_filter_arg afa = {
+ .af_name = "httpready"
+ };
+ struct sockaddr_in sin;
+ int l, s, a;
+
+ l = listensock(&sin);
+ accfon(l, &afa);
+ s = clientsock(&sin);
+
+ /* 1) No data. */
+ ATF_REQUIRE(accept(l, NULL, 0) == -1);
+ ATF_REQUIRE(errno == EAGAIN);
+
+ /* 2) Data, that doesn't look like HTTP. */
+ ATF_REQUIRE(usend(s, "foo", sizeof("foo")) == sizeof("foo"));
+ ATF_REQUIRE((a = accept(l, NULL, 0)) > 0);
+
+ close(s);
+ close(a);
+
+#define CHUNK1 "GET / "
+#define CHUNK2 "HTTP/1.0\r\n\n"
+#define LEN(c) (sizeof(c) - 1)
+
+ /* 3) Partial HTTP. */
+ s = clientsock(&sin);
+ ATF_REQUIRE(usend(s, CHUNK1, LEN(CHUNK1)) == LEN(CHUNK1));
+ ATF_REQUIRE(accept(l, NULL, 0) == -1);
+ ATF_REQUIRE(errno == EAGAIN);
+
+ /* 4) Complete HTTP. */
+ ATF_REQUIRE(usend(s, CHUNK2, LEN(CHUNK2)) == LEN(CHUNK2));
+ ATF_REQUIRE((a = accept(l, NULL, 0)) > 0);
+}
+
+ATF_TC_WITHOUT_HEAD(tls);
+ATF_TC_BODY(tls, tc)
+{
+ struct accept_filter_arg afa = {
+ .af_name = "tlsready"
+ };
+ struct sockaddr_in sin;
+ int l, s, a;
+
+ l = listensock(&sin);
+ accfon(l, &afa);
+ s = clientsock(&sin);
+
+ /* 1) No data. */
+ ATF_REQUIRE(accept(l, NULL, 0) == -1);
+ ATF_REQUIRE(errno == EAGAIN);
+
+ /* 2) Less than 5 bytes. */
+ ATF_REQUIRE(usend(s, "foo", sizeof("foo")) == sizeof("foo"));
+ ATF_REQUIRE(errno == EAGAIN);
+
+ /* 3) Something that doesn't look like TLS handshake. */
+ ATF_REQUIRE(usend(s, "bar", sizeof("bar")) == sizeof("bar"));
+ ATF_REQUIRE((a = accept(l, NULL, 0)) > 0);
+
+ close(s);
+ close(a);
+
+ /* 4) Partial TLS record. */
+ s = clientsock(&sin);
+ struct {
+ uint8_t type;
+ uint16_t version;
+ uint16_t length;
+ } __attribute__((__packed__)) header = {
+ .type = 0x16,
+ .length = htons((uint16_t)(arc4random() % 16384)),
+ };
+ _Static_assert(sizeof(header) == 5, "");
+ ATF_REQUIRE(usend(s, &header, sizeof(header)) == sizeof(header));
+ ssize_t sent = 0;
+ do {
+ size_t len;
+ char *buf;
+
+ ATF_REQUIRE(accept(l, NULL, 0) == -1);
+ ATF_REQUIRE(errno == EAGAIN);
+
+ len = arc4random() % 1024;
+ buf = alloca(len);
+ ATF_REQUIRE(usend(s, buf, len) == (ssize_t)len);
+ sent += len;
+ } while (sent < ntohs(header.length));
+ /* TLS header with bytes >= declared length. */
+ ATF_REQUIRE((a = accept(l, NULL, 0)) > 0);
+}
+
+/* Check changing to a different filter. */
+ATF_TC_WITHOUT_HEAD(change);
+ATF_TC_BODY(change, tc)
+{
+ struct accept_filter_arg dfa = {
+ .af_name = "dataready"
+ };
+ struct accept_filter_arg hfa = {
+ .af_name = "httpready"
+ };
+ struct sockaddr_in sin;
+ int n, l;
+
+ l = listensock(&sin);
+ accfon(l, &dfa);
+
+ /* Refuse to change filter without explicit removal of the old one. */
+ ATF_REQUIRE(setsockopt(l, SOL_SOCKET, SO_ACCEPTFILTER, &hfa,
+ sizeof(hfa)) != 0 && errno == EBUSY);
+
+ /* But allow after clearing. */
+ ATF_REQUIRE(setsockopt(l, SOL_SOCKET, SO_ACCEPTFILTER, NULL, 0) == 0);
+ ATF_REQUIRE(setsockopt(l, SOL_SOCKET, SO_ACCEPTFILTER, &hfa,
+ sizeof(hfa)) == 0);
+
+ /* Must be listening socket. */
+ ATF_REQUIRE((n = socket(PF_INET, SOCK_STREAM, 0)) > 0);
+ ATF_REQUIRE(setsockopt(n, SOL_SOCKET, SO_ACCEPTFILTER, &dfa,
+ sizeof(dfa)) != 0 && errno == EINVAL);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, data);
+ ATF_TP_ADD_TC(tp, http);
+ ATF_TP_ADD_TC(tp, tls);
+ ATF_TP_ADD_TC(tp, change);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/socket_msg_trunc.c b/tests/sys/kern/socket_msg_trunc.c
new file mode 100644
index 000000000000..bf1f0bf8ff42
--- /dev/null
+++ b/tests/sys/kern/socket_msg_trunc.c
@@ -0,0 +1,218 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2022 Alexander V. Chernikov
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/ktrace.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <netinet/in.h>
+
+#include <fcntl.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <atf-c.h>
+
+static void
+check_recvmsg(int cs, int ss, struct sockaddr *sa, const size_t sizes[],
+ size_t nsizes)
+{
+ char buf[9000];
+
+ memset(buf, 0xFF, sizeof(buf));
+ for (size_t i = 0; i < nsizes; i++) {
+ ssize_t rc;
+ size_t sz = sizes[i];
+ char tbuf[1];
+
+ ATF_REQUIRE(sz <= sizeof(buf));
+
+ rc = sendto(cs, buf, sz, 0, sa, sa->sa_len);
+ ATF_REQUIRE_MSG(rc != -1, "sendto failed: %s", strerror(errno));
+ ATF_REQUIRE((size_t)rc == sz);
+
+ rc = recv(ss, NULL, 0, MSG_PEEK | MSG_TRUNC);
+ ATF_REQUIRE_MSG(rc >= 0, "recv failed: %s", strerror(errno));
+ ATF_REQUIRE((size_t)rc == sz);
+
+ rc = recv(ss, tbuf, sizeof(tbuf), MSG_PEEK | MSG_TRUNC);
+ ATF_REQUIRE_MSG(rc >= 0, "recv failed: %s", strerror(errno));
+ ATF_REQUIRE((size_t)rc == sz);
+
+ rc = recv(ss, tbuf, sizeof(tbuf), MSG_TRUNC);
+ ATF_REQUIRE_MSG(rc >= 0, "recv failed: %s", strerror(errno));
+ ATF_REQUIRE((size_t)rc == sz);
+ }
+
+ ATF_REQUIRE(close(cs) == 0);
+ ATF_REQUIRE(close(ss) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(recv_trunc_afinet_udp);
+ATF_TC_BODY(recv_trunc_afinet_udp, tc)
+{
+ struct sockaddr_in sin;
+ struct sockaddr *sa;
+ int ss, cs, rc;
+
+ ss = socket(PF_INET, SOCK_DGRAM, 0);
+ ATF_REQUIRE(ss >= 0);
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof(sin);
+ sin.sin_port = htons(6666);
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sa = (struct sockaddr *)&sin;
+ rc = bind(ss, sa, sa->sa_len);
+ ATF_REQUIRE_MSG(rc == 0, "bind failed: %s", strerror(errno));
+
+ cs = socket(PF_INET, SOCK_DGRAM, 0);
+ ATF_REQUIRE(cs >= 0);
+
+ size_t sizes[] = {80, 255, 256, 1024, 4096, 9000};
+ check_recvmsg(cs, ss, sa, sizes, nitems(sizes));
+}
+
+ATF_TC_WITHOUT_HEAD(recv_trunc_afinet6_udp);
+ATF_TC_BODY(recv_trunc_afinet6_udp, tc)
+{
+ struct sockaddr_in6 sin6;
+ struct sockaddr *sa;
+ int cs, ss, rc;
+
+ ss = socket(PF_INET6, SOCK_DGRAM, 0);
+ ATF_REQUIRE(ss >= 0);
+
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_len = sizeof(sin6);
+ sin6.sin6_port = htons(6666);
+ const struct in6_addr in6loopback = IN6ADDR_LOOPBACK_INIT;
+ sin6.sin6_addr = in6loopback;
+ sa = (struct sockaddr *)&sin6;
+ rc = bind(ss, sa, sa->sa_len);
+ ATF_REQUIRE_MSG(rc == 0, "bind failed: %s", strerror(errno));
+
+ cs = socket(PF_INET6, SOCK_DGRAM, 0);
+ ATF_REQUIRE(cs >= 0);
+
+ size_t sizes[] = {80, 255, 256, 1024, 4096, 9000};
+ check_recvmsg(cs, ss, sa, sizes, nitems(sizes));
+}
+
+ATF_TC_WITHOUT_HEAD(recv_trunc_afunix_dgram);
+ATF_TC_BODY(recv_trunc_afunix_dgram, tc)
+{
+ struct sockaddr_un sun;
+ struct sockaddr *sa;
+ int ss, cs, rc;
+
+ ss = socket(PF_UNIX, SOCK_DGRAM, 0);
+ ATF_REQUIRE(ss >= 0);
+
+ bzero(&sun, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ strlcpy(sun.sun_path, "test_check_recvmsg_socket", sizeof(sun.sun_path));
+ sun.sun_len = sizeof(sun);
+ sa = (struct sockaddr *)&sun;
+ rc = bind(ss, sa, sa->sa_len);
+ ATF_REQUIRE_MSG(rc == 0, "bind failed: %s", strerror(errno));
+
+ cs = socket(PF_UNIX, SOCK_DGRAM, 0);
+ ATF_REQUIRE(cs >= 0);
+
+ size_t sizes[] = {80, 255, 256, 1024, 2000};
+ check_recvmsg(cs, ss, sa, sizes, nitems(sizes));
+}
+
+/*
+ * Exercise the case where ktrace was used to dump a truncated buffer.
+ */
+ATF_TC_WITHOUT_HEAD(recvmsg_trunc_ktrace_uio);
+ATF_TC_BODY(recvmsg_trunc_ktrace_uio, tc)
+{
+ struct ktr_header ktr;
+ struct msghdr msg;
+ struct iovec iov;
+ const char *tracepath;
+ char buf[128];
+ ssize_t nbytes;
+ int error, fd, sd[2];
+
+ tracepath = "ktrace";
+
+ error = socketpair(AF_UNIX, SOCK_DGRAM, 0, sd);
+ ATF_REQUIRE(error == 0);
+
+ memset(buf, 0, sizeof(buf));
+ nbytes = send(sd[0], buf, sizeof(buf), 0);
+ ATF_REQUIRE_MSG(nbytes >= 0, "send failed: %s", strerror(errno));
+ ATF_REQUIRE((size_t)nbytes == sizeof(buf));
+
+ fd = open(tracepath, O_RDWR | O_CREAT | O_TRUNC, 0644);
+ ATF_REQUIRE_MSG(fd >= 0, "open failed: %s", strerror(errno));
+ error = ktrace(tracepath, KTROP_SET, KTRFAC_GENIO, getpid());
+ ATF_REQUIRE_MSG(error == 0,
+ "ktrace(SET) failed: %s", strerror(errno));
+
+ iov.iov_base = buf;
+ iov.iov_len = sizeof(buf) - 1; /* truncate */
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ nbytes = recvmsg(sd[1], &msg, MSG_TRUNC);
+ ATF_REQUIRE_MSG(nbytes >= 0, "recvmsg failed: %s", strerror(errno));
+ ATF_REQUIRE((size_t)nbytes == sizeof(buf));
+ ATF_REQUIRE((msg.msg_flags & MSG_TRUNC) != 0);
+
+ error = ktrace(tracepath, KTROP_CLEARFILE, 0, getpid());
+ ATF_REQUIRE_MSG(error == 0,
+ "ktrace(CLEARFILE) failed: %s", strerror(errno));
+
+ nbytes = read(fd, &ktr, sizeof(ktr));
+ ATF_REQUIRE_MSG(nbytes >= 0, "read failed: %s", strerror(errno));
+ ATF_REQUIRE((size_t)nbytes == sizeof(ktr));
+ ATF_REQUIRE((ktr.ktr_type & ~KTR_TYPE) == KTR_GENIO);
+
+ ATF_REQUIRE(close(fd) == 0);
+ ATF_REQUIRE(close(sd[0]) == 0);
+ ATF_REQUIRE(close(sd[1]) == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, recv_trunc_afinet_udp);
+ ATF_TP_ADD_TC(tp, recv_trunc_afinet6_udp);
+ ATF_TP_ADD_TC(tp, recv_trunc_afunix_dgram);
+ ATF_TP_ADD_TC(tp, recvmsg_trunc_ktrace_uio);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/socket_msg_waitall.c b/tests/sys/kern/socket_msg_waitall.c
new file mode 100644
index 000000000000..4ad26b7419ba
--- /dev/null
+++ b/tests/sys/kern/socket_msg_waitall.c
@@ -0,0 +1,182 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 The FreeBSD Foundation
+ *
+ * This software was developed by Mark Johnston under sponsorship from
+ * the FreeBSD Foundation.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+struct close_test_params {
+ struct sockaddr_storage sa;
+ size_t msglen;
+ int count;
+ int af, type, proto;
+};
+
+static void *
+close_test_client(void *arg)
+{
+ struct close_test_params *p = arg;
+ char *buf;
+ size_t buflen;
+
+ buflen = p->msglen + 1;
+ buf = malloc(buflen);
+ ATF_REQUIRE(buf != NULL);
+
+ while (p->count-- > 0) {
+ ssize_t n;
+ int error, s;
+
+ s = socket(p->af, p->type, p->proto);
+ ATF_REQUIRE_MSG(s >= 0, "socket: %s", strerror(errno));
+
+ error = connect(s, (struct sockaddr *)&p->sa, p->sa.ss_len);
+ ATF_REQUIRE_MSG(error == 0, "connect: %s", strerror(errno));
+
+ n = recv(s, buf, buflen, MSG_WAITALL);
+ ATF_REQUIRE_MSG(n == (ssize_t)p->msglen,
+ "recv: got %zd expected %zd", n, (ssize_t)p->msglen);
+
+ ATF_REQUIRE(close(s) == 0);
+ }
+ free(buf);
+
+ return (NULL);
+}
+
+static void
+close_test(struct sockaddr *sa, unsigned int count, int af, int type, int proto)
+{
+ struct close_test_params p;
+ const char *msg;
+ pthread_t t;
+ size_t msglen;
+ int error, s;
+
+ s = socket(af, type, proto);
+ ATF_REQUIRE_MSG(s >= 0, "socket %s", strerror(errno));
+
+ ATF_REQUIRE_MSG(bind(s, sa, sa->sa_len) == 0,
+ "bind: %s", strerror(errno));
+ ATF_REQUIRE_MSG(listen(s, 1) == 0,
+ "listen: %s", strerror(errno));
+ ATF_REQUIRE_MSG(getsockname(s, sa, &(socklen_t){ sa->sa_len }) == 0,
+ "getsockname: %s", strerror(errno));
+
+ msg = "hello bonjour";
+ msglen = strlen(msg) + 1;
+ p = (struct close_test_params){
+ .count = count,
+ .msglen = msglen,
+ .af = af,
+ .type = type,
+ .proto = proto,
+ };
+ memcpy(&p.sa, sa, sa->sa_len);
+ error = pthread_create(&t, NULL, close_test_client, &p);
+ ATF_REQUIRE_MSG(error == 0, "pthread_create: %s", strerror(error));
+
+ while (count-- > 0) {
+ ssize_t n;
+ int cs;
+
+ cs = accept(s, NULL, NULL);
+ ATF_REQUIRE_MSG(cs >= 0, "accept: %s", strerror(errno));
+
+ n = send(cs, msg, msglen, 0);
+ ATF_REQUIRE_MSG(n == (ssize_t)msglen,
+ "send: %s", strerror(errno));
+
+ ATF_REQUIRE(close(cs) == 0);
+ }
+
+ ATF_REQUIRE(close(s) == 0);
+ ATF_REQUIRE(pthread_join(t, NULL) == 0);
+}
+
+/*
+ * Make sure that closing a connection kicks a MSG_WAITALL recv() out of the
+ * syscall. See bugzilla PR 212716.
+ */
+ATF_TC(close_tcp);
+ATF_TC_HEAD(close_tcp, tc)
+{
+ atf_tc_set_md_var(tc, "timeout", "10");
+}
+ATF_TC_BODY(close_tcp, tc)
+{
+ struct sockaddr_in sin;
+
+ sin = (struct sockaddr_in){
+ .sin_len = sizeof(sin),
+ .sin_family = AF_INET,
+ .sin_addr = { htonl(INADDR_LOOPBACK) },
+ .sin_port = htons(0),
+ };
+ close_test((struct sockaddr *)&sin, 1000, AF_INET, SOCK_STREAM,
+ IPPROTO_TCP);
+}
+
+/* A variant of the above test for UNIX domain stream sockets. */
+ATF_TC(close_unix_stream);
+ATF_TC_HEAD(close_unix_stream, tc)
+{
+ atf_tc_set_md_var(tc, "timeout", "10");
+}
+ATF_TC_BODY(close_unix_stream, tc)
+{
+ struct sockaddr_un sun;
+
+ sun = (struct sockaddr_un){
+ .sun_len = sizeof(sun),
+ .sun_family = AF_UNIX,
+ .sun_path = "socket_msg_waitall_unix",
+ };
+ close_test((struct sockaddr *)&sun, 1000, AF_UNIX, SOCK_STREAM, 0);
+ ATF_REQUIRE_MSG(unlink(sun.sun_path) == 0,
+ "unlink: %s", strerror(errno));
+}
+
+/* A variant of the above test for UNIX domain seqpacket sockets. */
+ATF_TC(close_unix_seqpacket);
+ATF_TC_HEAD(close_unix_seqpacket, tc)
+{
+ atf_tc_set_md_var(tc, "timeout", "10");
+}
+ATF_TC_BODY(close_unix_seqpacket, tc)
+{
+ struct sockaddr_un sun;
+
+ sun = (struct sockaddr_un){
+ .sun_len = sizeof(sun),
+ .sun_family = AF_UNIX,
+ .sun_path = "socket_msg_waitall_unix",
+ };
+ close_test((struct sockaddr *)&sun, 1000, AF_UNIX, SOCK_SEQPACKET, 0);
+ ATF_REQUIRE_MSG(unlink(sun.sun_path) == 0,
+ "unlink: %s", strerror(errno));
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, close_tcp);
+ ATF_TP_ADD_TC(tp, close_unix_stream);
+ ATF_TP_ADD_TC(tp, close_unix_seqpacket);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/socket_splice.c b/tests/sys/kern/socket_splice.c
new file mode 100644
index 000000000000..dfd4cb4f5957
--- /dev/null
+++ b/tests/sys/kern/socket_splice.c
@@ -0,0 +1,979 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Stormshield
+ */
+
+#include <sys/capsicum.h>
+#include <sys/event.h>
+#include <sys/filio.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#include <errno.h>
+#include <poll.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <atf-c.h>
+
+static void
+checked_close(int fd)
+{
+ int error;
+
+ error = close(fd);
+ ATF_REQUIRE_MSG(error == 0, "close failed: %s", strerror(errno));
+}
+
+static int
+fionread(int fd)
+{
+ int data, error;
+
+ data = 0;
+ error = ioctl(fd, FIONREAD, &data);
+ ATF_REQUIRE_MSG(error == 0, "ioctl failed: %s", strerror(errno));
+ ATF_REQUIRE(data >= 0);
+ return (data);
+}
+
+static void
+noblocking(int fd)
+{
+ int flags, error;
+
+ flags = fcntl(fd, F_GETFL);
+ ATF_REQUIRE_MSG(flags != -1, "fcntl failed: %s", strerror(errno));
+ flags |= O_NONBLOCK;
+ error = fcntl(fd, F_SETFL, flags);
+ ATF_REQUIRE_MSG(error == 0, "fcntl failed: %s", strerror(errno));
+}
+
+/*
+ * Create a pair of connected TCP sockets, returned via the "out" array.
+ */
+static void
+tcp_socketpair(int out[2], int domain)
+{
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ struct sockaddr *sinp;
+ int error, sd[2];
+
+ sd[0] = socket(domain, SOCK_STREAM, 0);
+ ATF_REQUIRE_MSG(sd[0] >= 0, "socket failed: %s", strerror(errno));
+ sd[1] = socket(domain, SOCK_STREAM, 0);
+ ATF_REQUIRE_MSG(sd[1] >= 0, "socket failed: %s", strerror(errno));
+
+ error = setsockopt(sd[0], IPPROTO_TCP, TCP_NODELAY, &(int){ 1 },
+ sizeof(int));
+ ATF_REQUIRE_MSG(error == 0, "setsockopt failed: %s", strerror(errno));
+ error = setsockopt(sd[1], IPPROTO_TCP, TCP_NODELAY, &(int){ 1 },
+ sizeof(int));
+ ATF_REQUIRE_MSG(error == 0, "setsockopt failed: %s", strerror(errno));
+
+ if (domain == PF_INET) {
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof(sin);
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sin.sin_port = htons(0);
+ sinp = (struct sockaddr *)&sin;
+ } else {
+ ATF_REQUIRE(domain == PF_INET6);
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_len = sizeof(sin6);
+ sin6.sin6_addr = in6addr_loopback;
+ sin6.sin6_port = htons(0);
+ sinp = (struct sockaddr *)&sin6;
+ }
+
+ error = bind(sd[0], sinp, sinp->sa_len);
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+ error = listen(sd[0], 1);
+ ATF_REQUIRE_MSG(error == 0, "listen failed: %s", strerror(errno));
+
+ error = getsockname(sd[0], sinp, &(socklen_t){ sinp->sa_len });
+ ATF_REQUIRE_MSG(error == 0, "getsockname failed: %s", strerror(errno));
+
+ error = connect(sd[1], sinp, sinp->sa_len);
+ ATF_REQUIRE_MSG(error == 0, "connect failed: %s", strerror(errno));
+ out[0] = accept(sd[0], NULL, NULL);
+ ATF_REQUIRE_MSG(out[0] >= 0, "accept failed: %s", strerror(errno));
+ checked_close(sd[0]);
+ out[1] = sd[1];
+}
+
+static void
+tcp4_socketpair(int out[2])
+{
+ tcp_socketpair(out, PF_INET);
+}
+
+static void
+tcp6_socketpair(int out[2])
+{
+ tcp_socketpair(out, PF_INET6);
+}
+
+static off_t
+nspliced(int sd)
+{
+ off_t n;
+ socklen_t len;
+ int error;
+
+ len = sizeof(n);
+ error = getsockopt(sd, SOL_SOCKET, SO_SPLICE, &n, &len);
+ ATF_REQUIRE_MSG(error == 0, "getsockopt failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(len == sizeof(n), "unexpected length: %d", len);
+ return (n);
+}
+
+/*
+ * Use a macro so that ATF_REQUIRE_MSG prints a useful line number.
+ */
+#define check_nspliced(sd, n) do { \
+ off_t sofar; \
+ \
+ sofar = nspliced(sd); \
+ ATF_REQUIRE_MSG(sofar == (off_t)n, "spliced %jd bytes, expected %jd", \
+ (intmax_t)sofar, (intmax_t)n); \
+} while (0)
+
+static void
+splice_init(struct splice *sp, int fd, off_t max, struct timeval *tv)
+{
+ memset(sp, 0, sizeof(*sp));
+ sp->sp_fd = fd;
+ sp->sp_max = max;
+ if (tv != NULL)
+ sp->sp_idle = *tv;
+ else
+ sp->sp_idle.tv_sec = sp->sp_idle.tv_usec = 0;
+}
+
+static void
+unsplice(int fd)
+{
+ struct splice sp;
+ int error;
+
+ splice_init(&sp, -1, 0, NULL);
+ error = setsockopt(fd, SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp));
+ ATF_REQUIRE_MSG(error == 0, "setsockopt failed: %s", strerror(errno));
+}
+
+static void
+unsplice_pair(int fd1, int fd2)
+{
+ unsplice(fd1);
+ unsplice(fd2);
+}
+
+static void
+splice_pair(int fd1, int fd2, off_t max, struct timeval *tv)
+{
+ struct splice sp;
+ int error;
+
+ splice_init(&sp, fd1, max, tv);
+ error = setsockopt(fd2, SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp));
+ ATF_REQUIRE_MSG(error == 0, "setsockopt failed: %s", strerror(errno));
+
+ splice_init(&sp, fd2, max, tv);
+ error = setsockopt(fd1, SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp));
+ ATF_REQUIRE_MSG(error == 0, "setsockopt failed: %s", strerror(errno));
+}
+
+/*
+ * A structure representing a spliced pair of connections. left[1] is
+ * bidirectionally spliced with right[0].
+ */
+struct splice_conn {
+ int left[2];
+ int right[2];
+};
+
+/*
+ * Initialize a splice connection with the given maximum number of bytes to
+ * splice and the given idle timeout. For now we're forced to use TCP socket,
+ * but at some point it would be nice (and simpler) to use pairs of PF_LOCAL
+ * sockets.
+ */
+static void
+splice_conn_init_limits(struct splice_conn *sc, off_t max, struct timeval *tv)
+{
+ memset(sc, 0, sizeof(*sc));
+ tcp4_socketpair(sc->left);
+ tcp4_socketpair(sc->right);
+ splice_pair(sc->left[1], sc->right[0], max, tv);
+}
+
+static void
+splice_conn_init(struct splice_conn *sc)
+{
+ splice_conn_init_limits(sc, 0, NULL);
+}
+
+static void
+splice_conn_check_empty(struct splice_conn *sc)
+{
+ int data;
+
+ data = fionread(sc->left[0]);
+ ATF_REQUIRE_MSG(data == 0, "unexpected data on left[0]: %d", data);
+ data = fionread(sc->left[1]);
+ ATF_REQUIRE_MSG(data == 0, "unexpected data on left[1]: %d", data);
+ data = fionread(sc->right[0]);
+ ATF_REQUIRE_MSG(data == 0, "unexpected data on right[0]: %d", data);
+ data = fionread(sc->right[1]);
+ ATF_REQUIRE_MSG(data == 0, "unexpected data on right[1]: %d", data);
+}
+
+static void
+splice_conn_fini(struct splice_conn *sc)
+{
+ checked_close(sc->left[0]);
+ checked_close(sc->left[1]);
+ checked_close(sc->right[0]);
+ checked_close(sc->right[1]);
+}
+
+static void
+splice_conn_noblocking(struct splice_conn *sc)
+{
+ noblocking(sc->left[0]);
+ noblocking(sc->left[1]);
+ noblocking(sc->right[0]);
+ noblocking(sc->right[1]);
+}
+
+/* Pass a byte through a pair of spliced connections. */
+ATF_TC_WITHOUT_HEAD(splice_basic);
+ATF_TC_BODY(splice_basic, tc)
+{
+ struct splice_conn sc;
+ ssize_t n;
+ char c;
+
+ splice_conn_init(&sc);
+
+ check_nspliced(sc.left[1], 0);
+ check_nspliced(sc.right[0], 0);
+
+ /* Left-to-right. */
+ c = 'M';
+ n = write(sc.left[0], &c, 1);
+ ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno));
+ n = read(sc.right[1], &c, 1);
+ ATF_REQUIRE_MSG(n == 1, "read failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(c == 'M', "unexpected character: %c", c);
+ check_nspliced(sc.left[1], 1);
+ check_nspliced(sc.right[0], 0);
+
+ /* Right-to-left. */
+ c = 'J';
+ n = write(sc.right[1], &c, 1);
+ ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno));
+ n = read(sc.left[0], &c, 1);
+ ATF_REQUIRE_MSG(n == 1, "read failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(c == 'J', "unexpected character: %c", c);
+ check_nspliced(sc.left[1], 1);
+ check_nspliced(sc.right[0], 1);
+
+ /* Unsplice and verify that the byte counts haven't changed. */
+ unsplice(sc.left[1]);
+ unsplice(sc.right[0]);
+ check_nspliced(sc.left[1], 1);
+ check_nspliced(sc.right[0], 1);
+
+ splice_conn_fini(&sc);
+}
+
+static void
+remove_rights(int fd, const cap_rights_t *toremove)
+{
+ cap_rights_t rights;
+ int error;
+
+ error = cap_rights_get(fd, &rights);
+ ATF_REQUIRE_MSG(error == 0, "cap_rights_get failed: %s",
+ strerror(errno));
+ cap_rights_remove(&rights, toremove);
+ error = cap_rights_limit(fd, &rights);
+ ATF_REQUIRE_MSG(error == 0, "cap_rights_limit failed: %s",
+ strerror(errno));
+}
+
+/*
+ * Verify that splicing fails when the socket is missing the necessary rights.
+ */
+ATF_TC_WITHOUT_HEAD(splice_capsicum);
+ATF_TC_BODY(splice_capsicum, tc)
+{
+ struct splice sp;
+ cap_rights_t rights;
+ off_t n;
+ int error, left[2], right[2];
+
+ tcp4_socketpair(left);
+ tcp4_socketpair(right);
+
+ /*
+ * Make sure that we can't splice a socket that's missing recv rights.
+ */
+ remove_rights(left[1], cap_rights_init(&rights, CAP_RECV));
+ splice_init(&sp, right[0], 0, NULL);
+ error = setsockopt(left[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp));
+ ATF_REQUIRE_ERRNO(ENOTCAPABLE, error == -1);
+
+ /* Make sure we can still splice left[1] in the other direction. */
+ splice_init(&sp, left[1], 0, NULL);
+ error = setsockopt(right[0], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp));
+ ATF_REQUIRE_MSG(error == 0, "setsockopt failed: %s", strerror(errno));
+ splice_init(&sp, -1, 0, NULL);
+ error = setsockopt(right[0], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp));
+ ATF_REQUIRE_MSG(error == 0, "setsockopt failed: %s", strerror(errno));
+
+ /*
+ * Now remove send rights from left[1] and verify that splicing is no
+ * longer possible.
+ */
+ remove_rights(left[1], cap_rights_init(&rights, CAP_SEND));
+ splice_init(&sp, left[1], 0, NULL);
+ error = setsockopt(right[0], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp));
+ ATF_REQUIRE_ERRNO(ENOTCAPABLE, error == -1);
+
+ /*
+ * It's still ok to query the SO_SPLICE state though.
+ */
+ n = -1;
+ error = getsockopt(left[1], SOL_SOCKET, SO_SPLICE, &n,
+ &(socklen_t){ sizeof(n) });
+ ATF_REQUIRE_MSG(error == 0, "getsockopt failed: %s", strerror(errno));
+ ATF_REQUIRE(n == 0);
+
+ /*
+ * Make sure that we can unsplice a spliced pair without any rights
+ * other than CAP_SETSOCKOPT.
+ */
+ splice_pair(left[0], right[1], 0, NULL);
+ error = cap_rights_limit(left[0],
+ cap_rights_init(&rights, CAP_SETSOCKOPT));
+ ATF_REQUIRE_MSG(error == 0, "cap_rights_limit failed: %s",
+ strerror(errno));
+ unsplice(left[0]);
+
+ checked_close(left[0]);
+ checked_close(left[1]);
+ checked_close(right[0]);
+ checked_close(right[1]);
+}
+
+/*
+ * Check various error cases in splice configuration.
+ */
+ATF_TC_WITHOUT_HEAD(splice_error);
+ATF_TC_BODY(splice_error, tc)
+{
+ struct splice_conn sc;
+ struct splice sp;
+ char path[PATH_MAX];
+ int error, fd, sd, usd[2];
+
+ memset(&sc, 0, sizeof(sc));
+ tcp4_socketpair(sc.left);
+ tcp4_socketpair(sc.right);
+
+ /* A negative byte limit is invalid. */
+ splice_init(&sp, sc.right[0], -3, NULL);
+ error = setsockopt(sc.left[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp));
+ ATF_REQUIRE_ERRNO(EINVAL, error == -1);
+
+ /* Can't unsplice a never-spliced socket. */
+ splice_init(&sp, -1, 0, NULL);
+ error = setsockopt(sc.left[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp));
+ ATF_REQUIRE_ERRNO(ENOTCONN, error == -1);
+
+ /* Can't double-unsplice a socket. */
+ splice_init(&sp, sc.right[0], 0, NULL);
+ error = setsockopt(sc.left[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp));
+ ATF_REQUIRE_MSG(error == 0, "setsockopt failed: %s", strerror(errno));
+ unsplice(sc.left[1]);
+ splice_init(&sp, -1, 0, NULL);
+ error = setsockopt(sc.left[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp));
+ ATF_REQUIRE_ERRNO(ENOTCONN, error == -1);
+
+ /* Can't splice a spliced socket */
+ splice_init(&sp, sc.right[0], 0, NULL);
+ error = setsockopt(sc.left[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp));
+ ATF_REQUIRE_MSG(error == 0, "setsockopt failed: %s", strerror(errno));
+ splice_init(&sp, sc.right[1], 0, NULL);
+ error = setsockopt(sc.left[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp));
+ ATF_REQUIRE_ERRNO(EBUSY, error == -1);
+ splice_init(&sp, sc.right[0], 0, NULL);
+ error = setsockopt(sc.left[0], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp));
+ ATF_REQUIRE_ERRNO(EBUSY, error == -1);
+ splice_init(&sp, -1, 0, NULL);
+ error = setsockopt(sc.left[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp));
+
+ /* Can't splice to a non-socket. */
+ snprintf(path, sizeof(path), "/tmp/splice_error.XXXXXX");
+ fd = mkstemp(path);
+ ATF_REQUIRE_MSG(fd >= 0, "mkstemp failed: %s", strerror(errno));
+ splice_init(&sp, fd, 0, NULL);
+ error = setsockopt(sc.left[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp));
+ ATF_REQUIRE_ERRNO(ENOTSOCK, error == -1);
+
+ /* Can't splice to an invalid fd. */
+ checked_close(fd);
+ splice_init(&sp, fd, 0, NULL);
+ error = setsockopt(sc.left[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp));
+ ATF_REQUIRE_ERRNO(EBADF, error == -1);
+
+ /* Can't splice a unix stream socket. */
+ error = socketpair(AF_UNIX, SOCK_STREAM, 0, usd);
+ ATF_REQUIRE_MSG(error == 0, "socketpair failed: %s", strerror(errno));
+ splice_init(&sp, usd[0], 0, NULL);
+ error = setsockopt(sc.left[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp));
+ ATF_REQUIRE_ERRNO(EPROTONOSUPPORT, error == -1);
+ error = setsockopt(usd[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp));
+ ATF_REQUIRE_ERRNO(EPROTONOSUPPORT, error == -1);
+ checked_close(usd[0]);
+ checked_close(usd[1]);
+
+ /* Can't splice an unconnected TCP socket. */
+ sd = socket(PF_INET, SOCK_STREAM, 0);
+ ATF_REQUIRE_MSG(sd >= 0, "socket failed: %s", strerror(errno));
+ splice_init(&sp, sd, 0, NULL);
+ error = setsockopt(sc.left[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp));
+ ATF_REQUIRE_ERRNO(ENOTCONN, error == -1);
+ splice_init(&sp, sc.right[0], 0, NULL);
+ error = setsockopt(sd, SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp));
+ ATF_REQUIRE_ERRNO(ENOTCONN, error == -1);
+
+ splice_conn_fini(&sc);
+}
+
+/*
+ * Make sure that kevent() doesn't report read I/O events on spliced sockets.
+ */
+ATF_TC_WITHOUT_HEAD(splice_kevent);
+ATF_TC_BODY(splice_kevent, tc)
+{
+ struct splice_conn sc;
+ struct kevent kev;
+ struct timespec ts;
+ ssize_t n;
+ int error, nev, kq;
+ uint8_t b;
+
+ splice_conn_init(&sc);
+
+ kq = kqueue();
+ ATF_REQUIRE_MSG(kq >= 0, "kqueue failed: %s", strerror(errno));
+
+ EV_SET(&kev, sc.left[1], EVFILT_READ, EV_ADD, 0, 0, NULL);
+ error = kevent(kq, &kev, 1, NULL, 0, NULL);
+ ATF_REQUIRE_MSG(error == 0, "kevent failed: %s", strerror(errno));
+
+ memset(&ts, 0, sizeof(ts));
+ nev = kevent(kq, NULL, 0, &kev, 1, &ts);
+ ATF_REQUIRE_MSG(nev >= 0, "kevent failed: %s", strerror(errno));
+ ATF_REQUIRE(nev == 0);
+
+ b = 'M';
+ n = write(sc.left[0], &b, 1);
+ ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno));
+ n = read(sc.right[1], &b, 1);
+ ATF_REQUIRE_MSG(n == 1, "read failed: %s", strerror(errno));
+ ATF_REQUIRE(b == 'M');
+
+ nev = kevent(kq, NULL, 0, &kev, 1, &ts);
+ ATF_REQUIRE_MSG(nev >= 0, "kevent failed: %s", strerror(errno));
+ ATF_REQUIRE(nev == 0);
+
+ b = 'J';
+ n = write(sc.right[1], &b, 1);
+ ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno));
+ n = read(sc.left[0], &b, 1);
+ ATF_REQUIRE_MSG(n == 1, "read failed: %s", strerror(errno));
+ ATF_REQUIRE(b == 'J');
+
+ splice_conn_fini(&sc);
+ checked_close(kq);
+}
+
+/*
+ * Verify that a splice byte limit is applied.
+ */
+ATF_TC_WITHOUT_HEAD(splice_limit_bytes);
+ATF_TC_BODY(splice_limit_bytes, tc)
+{
+ struct splice_conn sc;
+ ssize_t n;
+ uint8_t b, buf[128];
+
+ splice_conn_init_limits(&sc, sizeof(buf) + 1, NULL);
+
+ memset(buf, 'A', sizeof(buf));
+ for (size_t total = sizeof(buf); total > 0; total -= n) {
+ n = write(sc.left[0], buf, total);
+ ATF_REQUIRE_MSG(n > 0, "write failed: %s", strerror(errno));
+ }
+ for (size_t total = sizeof(buf); total > 0; total -= n) {
+ n = read(sc.right[1], buf, sizeof(buf));
+ ATF_REQUIRE_MSG(n > 0, "read failed: %s", strerror(errno));
+ }
+
+ check_nspliced(sc.left[1], sizeof(buf));
+ check_nspliced(sc.right[0], 0);
+
+ /* Trigger an unsplice by writing the last byte. */
+ b = 'B';
+ n = write(sc.left[0], &b, 1);
+ ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno));
+ n = read(sc.right[1], &b, 1);
+ ATF_REQUIRE_MSG(n == 1, "read failed: %s", strerror(errno));
+ ATF_REQUIRE(b == 'B');
+
+ /*
+ * The next byte should appear on the other side of the connection
+ * rather than the splice.
+ */
+ b = 'C';
+ n = write(sc.left[0], &b, 1);
+ ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno));
+ n = read(sc.left[1], &b, 1);
+ ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno));
+ ATF_REQUIRE(b == 'C');
+
+ splice_conn_check_empty(&sc);
+
+ splice_conn_fini(&sc);
+}
+
+/*
+ * Verify that a splice timeout limit is applied.
+ */
+ATF_TC_WITHOUT_HEAD(splice_limit_timeout);
+ATF_TC_BODY(splice_limit_timeout, tc)
+{
+ struct splice_conn sc;
+ ssize_t n;
+ int error;
+ uint8_t b, buf[128];
+
+ splice_conn_init_limits(&sc, 0,
+ &(struct timeval){ .tv_sec = 0, .tv_usec = 500000 /* 500ms */ });
+
+ /* Write some data through the splice. */
+ memset(buf, 'A', sizeof(buf));
+ for (size_t total = sizeof(buf); total > 0; total -= n) {
+ n = write(sc.left[0], buf, total);
+ ATF_REQUIRE_MSG(n > 0, "write failed: %s", strerror(errno));
+ }
+ for (size_t total = sizeof(buf); total > 0; total -= n) {
+ n = read(sc.right[1], buf, sizeof(buf));
+ ATF_REQUIRE_MSG(n > 0, "read failed: %s", strerror(errno));
+ }
+
+ check_nspliced(sc.left[1], sizeof(buf));
+ check_nspliced(sc.right[0], 0);
+
+ /* Wait for the splice to time out. */
+ error = usleep(550000);
+ ATF_REQUIRE_MSG(error == 0, "usleep failed: %s", strerror(errno));
+
+ /*
+ * The next byte should appear on the other side of the connection
+ * rather than the splice.
+ */
+ b = 'C';
+ n = write(sc.left[0], &b, 1);
+ ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno));
+ n = read(sc.left[1], &b, 1);
+ ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno));
+ ATF_REQUIRE(b == 'C');
+
+ splice_conn_fini(&sc);
+}
+
+/*
+ * Make sure that listen() fails on spliced sockets, and that SO_SPLICE can't be
+ * used with listening sockets.
+ */
+ATF_TC_WITHOUT_HEAD(splice_listen);
+ATF_TC_BODY(splice_listen, tc)
+{
+ struct splice sp;
+ struct splice_conn sc;
+ int error, sd[3];
+
+ /*
+ * These should fail regardless since the sockets are connected, but it
+ * doesn't hurt to check.
+ */
+ splice_conn_init(&sc);
+ error = listen(sc.left[1], 1);
+ ATF_REQUIRE_ERRNO(EINVAL, error == -1);
+ error = listen(sc.right[0], 1);
+ ATF_REQUIRE_ERRNO(EINVAL, error == -1);
+ splice_conn_fini(&sc);
+
+ tcp4_socketpair(sd);
+ sd[2] = socket(PF_INET, SOCK_STREAM, 0);
+ ATF_REQUIRE_MSG(sd[2] >= 0, "socket failed: %s", strerror(errno));
+ error = listen(sd[2], 1);
+ ATF_REQUIRE_MSG(error == 0, "listen failed: %s", strerror(errno));
+
+ /*
+ * Make sure a listening socket can't be spliced in either direction.
+ */
+ splice_init(&sp, sd[2], 0, NULL);
+ error = setsockopt(sd[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp));
+ ATF_REQUIRE_ERRNO(EINVAL, error == -1);
+ splice_init(&sp, sd[1], 0, NULL);
+ error = setsockopt(sd[2], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp));
+ ATF_REQUIRE_ERRNO(EINVAL, error == -1);
+
+ /*
+ * Make sure we can't try to unsplice a listening socket.
+ */
+ splice_init(&sp, -1, 0, NULL);
+ error = setsockopt(sd[2], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp));
+ ATF_REQUIRE_ERRNO(EINVAL, error == -1);
+
+ checked_close(sd[0]);
+ checked_close(sd[1]);
+ checked_close(sd[2]);
+}
+
+static void
+sigalarm(int sig __unused)
+{
+}
+
+/*
+ * Our SO_SPLICE implementation doesn't do anything to prevent loops. We should
+ * however make sure that they are interruptible.
+ */
+ATF_TC_WITHOUT_HEAD(splice_loop);
+ATF_TC_BODY(splice_loop, tc)
+{
+ ssize_t n;
+ int sd[2], status;
+ pid_t child;
+ char c;
+
+ tcp_socketpair(sd, PF_INET);
+ splice_pair(sd[0], sd[1], 0, NULL);
+
+ /*
+ * Let the child process trigger an infinite loop. It should still be
+ * possible to kill the child with a signal, causing the connection to
+ * be dropped and ending the loop.
+ */
+ child = fork();
+ ATF_REQUIRE_MSG(child >= 0, "fork failed: %s", strerror(errno));
+ if (child == 0) {
+ alarm(2);
+ c = 42;
+ n = write(sd[0], &c, 1);
+ if (n != 1)
+ _exit(2);
+ c = 24;
+ n = write(sd[1], &c, 1);
+ if (n != 1)
+ _exit(3);
+
+ for (;;) {
+ /* Wait for SIGALARM. */
+ sleep(100);
+ }
+
+ _exit(0);
+ } else {
+ checked_close(sd[0]);
+ checked_close(sd[1]);
+
+ child = waitpid(child, &status, 0);
+ ATF_REQUIRE_MSG(child >= 0,
+ "waitpid failed: %s", strerror(errno));
+ ATF_REQUIRE(WIFSIGNALED(status));
+ ATF_REQUIRE(WTERMSIG(status) == SIGALRM);
+ }
+}
+
+/*
+ * Simple I/O test.
+ */
+ATF_TC_WITHOUT_HEAD(splice_nonblock);
+ATF_TC_BODY(splice_nonblock, tc)
+{
+ struct splice_conn sc;
+ char buf[200];
+ size_t sofar;
+ ssize_t n;
+
+ splice_conn_init(&sc);
+ splice_conn_noblocking(&sc);
+
+ memset(buf, 'A', sizeof(buf));
+ for (sofar = 0;;) {
+ n = write(sc.left[0], buf, sizeof(buf));
+ if (n < 0) {
+ ATF_REQUIRE_ERRNO(EAGAIN, n == -1);
+ break;
+ }
+ sofar += n;
+ }
+
+ while (sofar > 0) {
+ n = read(sc.right[1], buf, sizeof(buf));
+ if (n < 0) {
+ ATF_REQUIRE_ERRNO(EAGAIN, n == -1);
+ usleep(100);
+ } else {
+ for (size_t i = 0; i < (size_t)n; i++)
+ ATF_REQUIRE(buf[i] == 'A');
+ sofar -= n;
+ }
+ }
+
+ splice_conn_fini(&sc);
+}
+
+ATF_TC_WITHOUT_HEAD(splice_resplice);
+ATF_TC_BODY(splice_resplice, tc)
+{
+ struct splice_conn sc;
+ ssize_t n;
+ char c;
+
+ splice_conn_init(&sc);
+
+ /* Left-to-right. */
+ c = 'M';
+ n = write(sc.left[0], &c, 1);
+ ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno));
+ n = read(sc.right[1], &c, 1);
+ ATF_REQUIRE_MSG(n == 1, "read failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(c == 'M', "unexpected character: %c", c);
+ check_nspliced(sc.left[1], 1);
+ check_nspliced(sc.right[0], 0);
+
+ /* Right-to-left. */
+ c = 'J';
+ n = write(sc.right[1], &c, 1);
+ ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno));
+ n = read(sc.left[0], &c, 1);
+ ATF_REQUIRE_MSG(n == 1, "read failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(c == 'J', "unexpected character: %c", c);
+ check_nspliced(sc.left[1], 1);
+ check_nspliced(sc.right[0], 1);
+
+ /* Unsplice and verify that the byte counts haven't changed. */
+ unsplice(sc.left[1]);
+ unsplice(sc.right[0]);
+ check_nspliced(sc.left[1], 1);
+ check_nspliced(sc.right[0], 1);
+
+ /* Splice again, check that byte counts are reset. */
+ splice_pair(sc.left[1], sc.right[0], 0, NULL);
+ check_nspliced(sc.left[1], 0);
+ check_nspliced(sc.right[0], 0);
+
+ /* Left-to-right. */
+ c = 'M';
+ n = write(sc.left[0], &c, 1);
+ ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno));
+ n = read(sc.right[1], &c, 1);
+ ATF_REQUIRE_MSG(n == 1, "read failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(c == 'M', "unexpected character: %c", c);
+ check_nspliced(sc.left[1], 1);
+ check_nspliced(sc.right[0], 0);
+
+ /* Right-to-left. */
+ c = 'J';
+ n = write(sc.right[1], &c, 1);
+ ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno));
+ n = read(sc.left[0], &c, 1);
+ ATF_REQUIRE_MSG(n == 1, "read failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(c == 'J', "unexpected character: %c", c);
+ check_nspliced(sc.left[1], 1);
+ check_nspliced(sc.right[0], 1);
+
+ splice_conn_fini(&sc);
+}
+
+struct xfer_args {
+ pthread_barrier_t *barrier;
+ uint32_t bytes;
+ int fd;
+};
+
+static void *
+xfer(void *arg)
+{
+ struct xfer_args *xfer;
+ uint8_t *buf;
+ size_t sz;
+ ssize_t n;
+ uint32_t resid;
+ int error;
+
+ xfer = arg;
+
+ error = fcntl(xfer->fd, F_SETFL, O_NONBLOCK);
+ ATF_REQUIRE_MSG(error == 0, "fcntl failed: %s", strerror(errno));
+
+ sz = MIN(xfer->bytes, 1024 * 1024);
+ buf = malloc(sz);
+ ATF_REQUIRE(buf != NULL);
+ arc4random_buf(buf, sz);
+
+ pthread_barrier_wait(xfer->barrier);
+
+ for (resid = xfer->bytes; xfer->bytes > 0 || resid > 0;) {
+ n = write(xfer->fd, buf, MIN(sz, xfer->bytes));
+ if (n < 0) {
+ ATF_REQUIRE_ERRNO(EAGAIN, n == -1);
+ usleep(1000);
+ } else {
+ ATF_REQUIRE(xfer->bytes >= (size_t)n);
+ xfer->bytes -= n;
+ }
+
+ n = read(xfer->fd, buf, sz);
+ if (n < 0) {
+ ATF_REQUIRE_ERRNO(EAGAIN, n == -1);
+ usleep(1000);
+ } else {
+ ATF_REQUIRE(resid >= (size_t)n);
+ resid -= n;
+ }
+ }
+
+ free(buf);
+ return (NULL);
+}
+
+/*
+ * Use two threads to transfer data between two spliced connections.
+ */
+ATF_TC_WITHOUT_HEAD(splice_throughput);
+ATF_TC_BODY(splice_throughput, tc)
+{
+ struct xfer_args xfers[2];
+ pthread_t thread[2];
+ pthread_barrier_t barrier;
+ struct splice_conn sc;
+ uint32_t bytes;
+ int error;
+
+ /* Transfer an amount between 1B and 1GB. */
+ bytes = arc4random_uniform(1024 * 1024 * 1024) + 1;
+ splice_conn_init(&sc);
+
+ error = pthread_barrier_init(&barrier, NULL, 2);
+ ATF_REQUIRE(error == 0);
+ xfers[0] = (struct xfer_args){
+ .barrier = &barrier,
+ .bytes = bytes,
+ .fd = sc.left[0]
+ };
+ xfers[1] = (struct xfer_args){
+ .barrier = &barrier,
+ .bytes = bytes,
+ .fd = sc.right[1]
+ };
+
+ error = pthread_create(&thread[0], NULL, xfer, &xfers[0]);
+ ATF_REQUIRE_MSG(error == 0,
+ "pthread_create failed: %s", strerror(errno));
+ error = pthread_create(&thread[1], NULL, xfer, &xfers[1]);
+ ATF_REQUIRE_MSG(error == 0,
+ "pthread_create failed: %s", strerror(errno));
+
+ error = pthread_join(thread[0], NULL);
+ ATF_REQUIRE_MSG(error == 0,
+ "pthread_join failed: %s", strerror(errno));
+ error = pthread_join(thread[1], NULL);
+ ATF_REQUIRE_MSG(error == 0,
+ "pthread_join failed: %s", strerror(errno));
+
+ error = pthread_barrier_destroy(&barrier);
+ ATF_REQUIRE(error == 0);
+ splice_conn_fini(&sc);
+}
+
+/*
+ * Make sure it's possible to splice v4 and v6 sockets together.
+ */
+ATF_TC_WITHOUT_HEAD(splice_v4v6);
+ATF_TC_BODY(splice_v4v6, tc)
+{
+ struct splice sp;
+ ssize_t n;
+ int sd4[2], sd6[2];
+ int error;
+ uint8_t b;
+
+ tcp4_socketpair(sd4);
+ tcp6_socketpair(sd6);
+
+ splice_init(&sp, sd6[0], 0, NULL);
+ error = setsockopt(sd4[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp));
+ ATF_REQUIRE_MSG(error == 0, "setsockopt failed: %s", strerror(errno));
+
+ splice_init(&sp, sd4[1], 0, NULL);
+ error = setsockopt(sd6[0], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp));
+ ATF_REQUIRE_MSG(error == 0, "setsockopt failed: %s", strerror(errno));
+
+ b = 'M';
+ n = write(sd4[0], &b, 1);
+ ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno));
+ n = read(sd6[1], &b, 1);
+ ATF_REQUIRE_MSG(n == 1, "read failed: %s", strerror(errno));
+ ATF_REQUIRE(b == 'M');
+
+ b = 'J';
+ n = write(sd6[1], &b, 1);
+ ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno));
+ n = read(sd4[0], &b, 1);
+ ATF_REQUIRE_MSG(n == 1, "read failed: %s", strerror(errno));
+ ATF_REQUIRE(b == 'J');
+
+ checked_close(sd4[0]);
+ checked_close(sd4[1]);
+ checked_close(sd6[0]);
+ checked_close(sd6[1]);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, splice_basic);
+ ATF_TP_ADD_TC(tp, splice_capsicum);
+ ATF_TP_ADD_TC(tp, splice_error);
+ ATF_TP_ADD_TC(tp, splice_kevent);
+ ATF_TP_ADD_TC(tp, splice_limit_bytes);
+ ATF_TP_ADD_TC(tp, splice_limit_timeout);
+ ATF_TP_ADD_TC(tp, splice_listen);
+ ATF_TP_ADD_TC(tp, splice_loop);
+ ATF_TP_ADD_TC(tp, splice_nonblock);
+ ATF_TP_ADD_TC(tp, splice_resplice);
+ ATF_TP_ADD_TC(tp, splice_throughput);
+ ATF_TP_ADD_TC(tp, splice_v4v6);
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/sonewconn_overflow.py b/tests/sys/kern/sonewconn_overflow.py
new file mode 100644
index 000000000000..bb4c570392fc
--- /dev/null
+++ b/tests/sys/kern/sonewconn_overflow.py
@@ -0,0 +1,157 @@
+#!/usr/bin/env python
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+import socket
+import os
+import sys
+from subprocess import check_output
+from time import sleep
+
+V4HOST = '127.0.0.1'
+V6HOST = '::1'
+TCPPORT = 65432
+UNIXSOCK = '/tmp/testsock'
+TYPE = socket.SOCK_STREAM
+
+class GenericTest(object):
+ def __init__(self):
+ raise NotImplementedError("Subclass must override the __init__ method")
+ def setup(self, af, addr):
+ self.sockets = []
+ self.ls = None
+ self.ls = socket.socket(af, TYPE)
+ self.ls.bind(addr)
+ self.ls.listen(2)
+ self.af = af
+ self.addr = addr
+ def doTest(self, cnt):
+ rv = 0
+ for i in range(0, cnt):
+ try:
+ s = socket.socket(self.af, TYPE)
+ s.connect(self.addr)
+ except:
+ continue
+ self.sockets.append(s)
+ rv += 1
+ return rv
+ def __del__(self):
+ for s in self.sockets:
+ s.close()
+ if self.ls is not None:
+ self.ls.close()
+
+class IPv4Test(GenericTest):
+ def __init__(self):
+ super(IPv4Test, self).setup(socket.AF_INET, (V4HOST, TCPPORT))
+
+class IPv6Test(GenericTest):
+ def __init__(self):
+ super(IPv6Test, self).setup(socket.AF_INET6, (V6HOST, TCPPORT))
+
+class UnixTest(GenericTest):
+ def __init__(self):
+ super(UnixTest, self).setup(socket.AF_UNIX, UNIXSOCK)
+ def __del__(self):
+ super(UnixTest, self).__del__()
+ os.remove(UNIXSOCK)
+
+class LogChecker():
+ def __init__(self):
+ # Clear the dmesg buffer to prevent rotating causes issues
+ os.system('/sbin/dmesg -c > /dev/null')
+ # Figure out how big the dmesg buffer is.
+ self.dmesgOff = len(check_output("/sbin/dmesg"))
+
+ def checkForMsg(self, expected):
+ newOff = self.dmesgOff
+ for i in range(0, 3):
+ dmesg = check_output("/sbin/dmesg")
+ newOff = len(dmesg)
+ if newOff >= self.dmesgOff:
+ dmesg = dmesg[self.dmesgOff:]
+ for line in dmesg.splitlines():
+ try:
+ if str(line).find(expected) >= 0:
+ self.dmesgOff = newOff
+ return True
+ except:
+ pass
+ sleep(0.5)
+ self.dmesgOff = newOff
+ return False
+
+def main():
+ ip4 = IPv4Test()
+ ip6 = IPv6Test()
+ lcl = UnixTest()
+ lc = LogChecker()
+ failure = False
+
+ STDLOGMSG = "Listen queue overflow: 4 already in queue awaiting acceptance (1 occurrences)"
+
+ V4LOGMSG = "(%s:%d (proto 6)): %s" % (V4HOST, TCPPORT, STDLOGMSG)
+ ip4.doTest(5)
+ if not lc.checkForMsg(V4LOGMSG):
+ failure = True
+ sys.stderr.write("IPv4 log message not seen\n")
+ else:
+ ip4.doTest(1)
+ if lc.checkForMsg(V4LOGMSG):
+ failure = True
+ sys.stderr.write("Subsequent IPv4 log message not suppressed\n")
+
+ V6LOGMSG = "([%s]:%d (proto 6)): %s" % (V6HOST, TCPPORT, STDLOGMSG)
+ ip6.doTest(5)
+ if not lc.checkForMsg(V6LOGMSG):
+ failure = True
+ sys.stderr.write("IPv6 log message not seen\n")
+ else:
+ ip6.doTest(1)
+ if lc.checkForMsg(V6LOGMSG):
+ failure = True
+ sys.stderr.write("Subsequent IPv6 log message not suppressed\n")
+
+ UNIXLOGMSG = "(local:%s): %s" % (UNIXSOCK, STDLOGMSG)
+ lcl.doTest(5)
+ if not lc.checkForMsg(UNIXLOGMSG):
+ failure = True
+ sys.stderr.write("Unix socket log message not seen\n")
+ else:
+ lcl.doTest(1)
+ if lc.checkForMsg(UNIXLOGMSG):
+ failure = True
+ sys.stderr.write("Subsequent Unix socket log message not suppressed\n")
+
+ if failure:
+ sys.exit(1)
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/kern/sonewconn_overflow.sh b/tests/sys/kern/sonewconn_overflow.sh
new file mode 100755
index 000000000000..2f23a79f8f59
--- /dev/null
+++ b/tests/sys/kern/sonewconn_overflow.sh
@@ -0,0 +1,42 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+atf_test_case sonewconn_overflow_01
+sonewconn_overflow_01_head()
+{
+ atf_set descr "Check the operation of the sonewconn() listen queue overflow messages"
+}
+
+sonewconn_overflow_01_body()
+{
+ atf_check -s exit:0 $(atf_get_srcdir)/sonewconn_overflow.py
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case sonewconn_overflow_01
+}
diff --git a/tests/sys/kern/subr_physmem_test.c b/tests/sys/kern/subr_physmem_test.c
new file mode 100644
index 000000000000..b810a2016473
--- /dev/null
+++ b/tests/sys/kern/subr_physmem_test.c
@@ -0,0 +1,137 @@
+/*-
+ * Copyright (c) 2021 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Andrew Turner under
+ * sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/physmem.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(hwregion);
+ATF_TC_BODY(hwregion, tc)
+{
+ vm_paddr_t avail[4];
+ size_t len;
+
+ physmem_hardware_region(2 * PAGE_SIZE, PAGE_SIZE);
+
+ len = physmem_avail(avail, 4);
+ ATF_CHECK_EQ(len, 2);
+ ATF_CHECK_EQ(avail[0], 2 * PAGE_SIZE);
+ ATF_CHECK_EQ(avail[1], 3 * PAGE_SIZE);
+
+ /* Add an overlap */
+ physmem_hardware_region(2 * PAGE_SIZE, 2 * PAGE_SIZE);
+ len = physmem_avail(avail, 4);
+ ATF_CHECK_EQ(len, 2);
+ ATF_CHECK_EQ(avail[0], 2 * PAGE_SIZE);
+ ATF_CHECK_EQ(avail[1], 4 * PAGE_SIZE);
+
+ /* Add a touching region */
+ physmem_hardware_region(4 * PAGE_SIZE, PAGE_SIZE);
+ len = physmem_avail(avail, 4);
+ ATF_CHECK_EQ(len, 2);
+ ATF_CHECK_EQ(avail[0], 2 * PAGE_SIZE);
+ ATF_CHECK_EQ(avail[1], 5 * PAGE_SIZE);
+
+ /* Add a partial overlap */
+ physmem_hardware_region(4 * PAGE_SIZE, 2 * PAGE_SIZE);
+ len = physmem_avail(avail, 4);
+ ATF_CHECK_EQ(len, 2);
+ ATF_CHECK_EQ(avail[0], 2 * PAGE_SIZE);
+ ATF_CHECK_EQ(avail[1], 6 * PAGE_SIZE);
+
+ /* Add a non-page aligned section */
+ physmem_hardware_region(6 * PAGE_SIZE, PAGE_SIZE / 2);
+ len = physmem_avail(avail, 4);
+ ATF_CHECK_EQ(len, 2);
+ ATF_CHECK_EQ(avail[0], 2 * PAGE_SIZE);
+ ATF_CHECK_EQ(avail[1], 6 * PAGE_SIZE);
+
+ /* Add the remaining part of the page */
+ physmem_hardware_region(6 * PAGE_SIZE + PAGE_SIZE / 2, PAGE_SIZE / 2);
+ len = physmem_avail(avail, 4);
+ ATF_CHECK_EQ(len, 2);
+ ATF_CHECK_EQ(avail[0], 2 * PAGE_SIZE);
+ ATF_CHECK_EQ(avail[1], 7 * PAGE_SIZE);
+}
+
+ATF_TC_WITHOUT_HEAD(hwregion_exclude);
+ATF_TC_BODY(hwregion_exclude, tc)
+{
+ vm_paddr_t avail[6];
+ size_t len;
+
+ physmem_hardware_region(2 * PAGE_SIZE, 5 * PAGE_SIZE);
+ physmem_exclude_region(4 * PAGE_SIZE, PAGE_SIZE, EXFLAG_NOALLOC);
+
+ len = physmem_avail(avail, 6);
+ ATF_CHECK_EQ(len, 4);
+ ATF_CHECK_EQ(avail[0], 2 * PAGE_SIZE);
+ ATF_CHECK_EQ(avail[1], 4 * PAGE_SIZE);
+ ATF_CHECK_EQ(avail[2], 5 * PAGE_SIZE);
+ ATF_CHECK_EQ(avail[3], 7 * PAGE_SIZE);
+
+ /* Check mis-aligned/sized excluded regions work */
+ physmem_exclude_region(4 * PAGE_SIZE - 1, PAGE_SIZE + 2,
+ EXFLAG_NOALLOC);
+ len = physmem_avail(avail, 6);
+ ATF_CHECK_EQ(len, 4);
+ ATF_CHECK_EQ(avail[0], 2 * PAGE_SIZE);
+ ATF_CHECK_EQ(avail[1], 3 * PAGE_SIZE);
+ ATF_CHECK_EQ(avail[2], 6 * PAGE_SIZE);
+ ATF_CHECK_EQ(avail[3], 7 * PAGE_SIZE);
+}
+
+ATF_TC_WITHOUT_HEAD(hwregion_unordered);
+ATF_TC_BODY(hwregion_unordered, tc)
+{
+ vm_paddr_t avail[4];
+ size_t len;
+
+ /* Add a partial page */
+ physmem_hardware_region(PAGE_SIZE, PAGE_SIZE / 2);
+ /* Add a full page not touching the previous */
+ physmem_hardware_region( 2 * PAGE_SIZE, PAGE_SIZE);
+ /* Add the remainder of the first page */
+ physmem_hardware_region(PAGE_SIZE + PAGE_SIZE / 2, PAGE_SIZE / 2);
+
+ len = physmem_avail(avail, 4);
+ ATF_CHECK_EQ(len, 2);
+ ATF_CHECK_EQ(avail[0], PAGE_SIZE);
+ ATF_CHECK_EQ(avail[1], 3 * PAGE_SIZE);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, hwregion);
+ ATF_TP_ADD_TC(tp, hwregion_exclude);
+ ATF_TP_ADD_TC(tp, hwregion_unordered);
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/sys_getrandom.c b/tests/sys/kern/sys_getrandom.c
new file mode 100644
index 000000000000..6b6553bbdaac
--- /dev/null
+++ b/tests/sys/kern/sys_getrandom.c
@@ -0,0 +1,127 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2018 Conrad Meyer <cem@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/random.h>
+#include <errno.h>
+
+#include <atf-c.h>
+
+#include <zstd.h>
+
+static const unsigned valid_flags[] = { 0, GRND_NONBLOCK, GRND_RANDOM,
+ GRND_NONBLOCK | GRND_RANDOM };
+
+ATF_TC_WITHOUT_HEAD(getrandom_randomness);
+ATF_TC_BODY(getrandom_randomness, tc)
+{
+ char randomb[4096], compressed[5000];
+ ssize_t ret;
+ size_t i, j, c;
+ unsigned mode;
+
+ for (i = 0; i < nitems(valid_flags); i++) {
+ mode = valid_flags[i];
+
+ /* Get new random data, filling randomb. */
+
+ memset(randomb, 0, sizeof(randomb));
+
+ for (j = 0; j < sizeof(randomb);) {
+ ret = getrandom(&randomb[j], sizeof(randomb) - j, mode);
+ if (ret < 0 && (mode & GRND_NONBLOCK) != 0 &&
+ errno == EAGAIN)
+ continue;
+
+ ATF_REQUIRE_MSG(ret >= 0, "other error: %d", errno);
+ ATF_REQUIRE_MSG(ret > 0, "bogus zero return");
+
+ j += (size_t)ret;
+ }
+
+ /* Perform compressibility test */
+ c = ZSTD_compress(compressed, sizeof(compressed), randomb,
+ sizeof(randomb), ZSTD_maxCLevel());
+ ATF_REQUIRE_MSG(!ZSTD_isError(c), "zstd compress: %s",
+ ZSTD_getErrorName(c));
+
+ /*
+ * If the output is very compressible, it's probably not random
+ */
+ ATF_REQUIRE_MSG(c > (sizeof(randomb) * 4 / 5),
+ "purportedly random data was compressible: %zu/%zu or %f%%",
+ c, sizeof(randomb), (double)c / (double)sizeof(randomb));
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(getrandom_fault);
+ATF_TC_BODY(getrandom_fault, tc)
+{
+ ssize_t ret;
+
+ ret = getrandom(NULL, 1, 0);
+ ATF_REQUIRE_EQ(ret, -1);
+ ATF_REQUIRE_EQ(errno, EFAULT);
+}
+
+ATF_TC_WITHOUT_HEAD(getrandom_count);
+ATF_TC_BODY(getrandom_count, tc)
+{
+ char buf[4096], reference[4096];
+ ssize_t ret;
+
+ /* getrandom(2) does not modify buf past the requested length */
+ _Static_assert(sizeof(reference) == sizeof(buf), "must match");
+ memset(reference, 0x7C, sizeof(reference));
+
+ memset(buf, 0x7C, sizeof(buf));
+ ret = getrandom(buf, 1, 0);
+ ATF_REQUIRE_EQ(ret, 1);
+ ATF_REQUIRE_EQ(memcmp(&buf[1], reference, sizeof(reference) - 1), 0);
+
+ memset(buf, 0x7C, sizeof(buf));
+ ATF_REQUIRE_EQ(getrandom(buf, 15, 0), 15);
+ ATF_REQUIRE_EQ(memcmp(&buf[15], reference, sizeof(reference) - 15), 0);
+
+ memset(buf, 0x7C, sizeof(buf));
+ ATF_REQUIRE_EQ(getrandom(buf, 255, 0), 255);
+ ATF_REQUIRE_EQ(memcmp(&buf[255], reference, sizeof(reference) - 255), 0);
+
+ memset(buf, 0x7C, sizeof(buf));
+ ATF_REQUIRE_EQ(getrandom(buf, 4095, 0), 4095);
+ ATF_REQUIRE_EQ(memcmp(&buf[4095], reference, sizeof(reference) - 4095), 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, getrandom_count);
+ ATF_TP_ADD_TC(tp, getrandom_fault);
+ ATF_TP_ADD_TC(tp, getrandom_randomness);
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/sysctl_kern_proc.c b/tests/sys/kern/sysctl_kern_proc.c
new file mode 100644
index 000000000000..99983f69014b
--- /dev/null
+++ b/tests/sys/kern/sysctl_kern_proc.c
@@ -0,0 +1,199 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2021 The FreeBSD Foundation
+ *
+ * This software was developed by Mark Johnston under sponsorship from
+ * the FreeBSD Foundation.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#include <sys/wait.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+/*
+ * These tests exercise the KERN_PROC_* sysctls.
+ */
+
+/*
+ * Loop through all valid PIDs and try to fetch info for each one.
+ */
+static void
+sysctl_kern_proc_all(int cmd)
+{
+ int mib[4], pid_max;
+ void *buf;
+ size_t sz;
+
+ sz = sizeof(pid_max);
+ ATF_REQUIRE(sysctlbyname("kern.pid_max", &pid_max, &sz, NULL, 0) == 0);
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = cmd;
+ for (int i = 1; i <= pid_max; i++) {
+ mib[3] = i;
+
+ if (sysctl(mib, 4, NULL, &sz, NULL, 0) == 0) {
+ buf = malloc(sz);
+ ATF_REQUIRE(buf != NULL);
+ (void)sysctl(mib, 4, buf, &sz, NULL, 0);
+ free(buf);
+ }
+ }
+
+ mib[3] = -1;
+ ATF_REQUIRE_ERRNO(ESRCH, sysctl(mib, 4, NULL, &sz, NULL, 0) != 0);
+}
+
+/*
+ * Validate behaviour of the KERN_PROC_CWD sysctl.
+ */
+ATF_TC_WITHOUT_HEAD(sysctl_kern_proc_cwd);
+ATF_TC_BODY(sysctl_kern_proc_cwd, tc)
+{
+ struct kinfo_file kfile;
+ char cwd[PATH_MAX];
+ int cmd, mib[4];
+ size_t sz;
+ pid_t child;
+ int status;
+
+ cmd = KERN_PROC_CWD;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = cmd;
+ mib[3] = getpid();
+
+ /* Try querying the kernel for the output buffer size. */
+ sz = 0;
+ ATF_REQUIRE(sysctl(mib, 4, NULL, &sz, NULL, 0) == 0);
+ ATF_REQUIRE(sz <= sizeof(kfile));
+
+ sz = sizeof(kfile);
+ memset(&kfile, 0, sz);
+ ATF_REQUIRE(sysctl(mib, 4, &kfile, &sz, NULL, 0) == 0);
+ ATF_REQUIRE(sz <= sizeof(kfile));
+ ATF_REQUIRE(sz == (u_int)kfile.kf_structsize);
+
+ /* Make sure that we get the same result from getcwd(2). */
+ ATF_REQUIRE(getcwd(cwd, sizeof(cwd)) == cwd);
+ ATF_REQUIRE(strcmp(cwd, kfile.kf_path) == 0);
+
+ /* Spot-check some of the kinfo fields. */
+ ATF_REQUIRE(kfile.kf_type == KF_TYPE_VNODE);
+ ATF_REQUIRE(kfile.kf_fd == KF_FD_TYPE_CWD);
+ ATF_REQUIRE(S_ISDIR(kfile.kf_un.kf_file.kf_file_mode));
+ ATF_REQUIRE((kfile.kf_status & KF_ATTR_VALID) != 0);
+
+ /*
+ * Verify that a child process can get our CWD info, and that it
+ * matches the info we got above.
+ */
+ child = fork();
+ ATF_REQUIRE(child != -1);
+ if (child == 0) {
+ struct kinfo_file pkfile;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_CWD;
+ mib[3] = getppid();
+
+ sz = sizeof(pkfile);
+ memset(&pkfile, 0, sz);
+ if (sysctl(mib, 4, &pkfile, &sz, NULL, 0) != 0)
+ _exit(1);
+ if (memcmp(&kfile, &pkfile, sizeof(kfile)) != 0)
+ _exit(2);
+ _exit(0);
+ }
+ ATF_REQUIRE(waitpid(child, &status, 0) == child);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE(WEXITSTATUS(status) == 0);
+
+ /*
+ * Truncate the output buffer ever so slightly and make sure that we get
+ * an error.
+ */
+ sz--;
+ ATF_REQUIRE_ERRNO(ENOMEM, sysctl(mib, 4, &kfile, &sz, NULL, 0) != 0);
+
+ sysctl_kern_proc_all(cmd);
+}
+
+/*
+ * Validate behaviour of the KERN_PROC_FILEDESC sysctl.
+ */
+ATF_TC_WITHOUT_HEAD(sysctl_kern_proc_filedesc);
+ATF_TC_BODY(sysctl_kern_proc_filedesc, tc)
+{
+ int cmd, fd, mib[4];
+ struct kinfo_file *kfile;
+ char *buf, tmp[16];
+ size_t sz, sz1;
+
+ cmd = KERN_PROC_FILEDESC;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = cmd;
+ mib[3] = getpid();
+
+ sz = 0;
+ ATF_REQUIRE(sysctl(mib, 4, NULL, &sz, NULL, 0) == 0);
+ ATF_REQUIRE(sz >= __offsetof(struct kinfo_file, kf_structsize) +
+ sizeof(kfile->kf_structsize));
+
+ buf = malloc(sz);
+ ATF_REQUIRE(buf != NULL);
+
+ ATF_REQUIRE(sysctl(mib, 4, buf, &sz, NULL, 0) == 0);
+
+ /* Walk over the list of returned files. */
+ for (sz1 = 0; sz1 < sz; sz1 += kfile->kf_structsize) {
+ kfile = (void *)(buf + sz1);
+
+ ATF_REQUIRE((unsigned int)kfile->kf_structsize <= sz);
+ ATF_REQUIRE((unsigned int)kfile->kf_structsize + sz1 <= sz);
+
+ ATF_REQUIRE((kfile->kf_status & KF_ATTR_VALID) != 0);
+ }
+ /* We shouldn't have any trailing bytes. */
+ ATF_REQUIRE(sz1 == sz);
+
+ /*
+ * Open a file. This increases the size of the output buffer, so an
+ * attempt to re-fetch the records without increasing the buffer size
+ * should fail with ENOMEM.
+ */
+ snprintf(tmp, sizeof(tmp), "tmp.XXXXXX");
+ fd = mkstemp(tmp);
+ ATF_REQUIRE(fd >= 0);
+ ATF_REQUIRE_ERRNO(ENOMEM, sysctl(mib, 4, buf, &sz, NULL, 0) != 0);
+
+ ATF_REQUIRE(unlink(tmp) == 0);
+ ATF_REQUIRE(close(fd) == 0);
+
+ free(buf);
+
+ sysctl_kern_proc_all(cmd);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, sysctl_kern_proc_cwd);
+ ATF_TP_ADD_TC(tp, sysctl_kern_proc_filedesc);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/sysctl_security_jail_children.sh b/tests/sys/kern/sysctl_security_jail_children.sh
new file mode 100644
index 000000000000..ac990e57ff74
--- /dev/null
+++ b/tests/sys/kern/sysctl_security_jail_children.sh
@@ -0,0 +1,77 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 Igor Ostapenko <pm@igoro.pro>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+atf_test_case "max_cur" "cleanup"
+max_cur_head()
+{
+ atf_set descr 'Test maximum and current number of child jails'
+ atf_set require.user root
+ atf_set execenv jail
+}
+max_cur_body()
+{
+ origin_max=$(sysctl -n security.jail.children.max)
+ origin_cur=$(sysctl -n security.jail.children.cur)
+
+ # Magic numbers reasoning:
+ # 3 stands for:
+ # - the test creates three jails: childfree, maxallowed, maxallowed.family
+ # 6 stands for:
+ # - maxallowed.family wants to set children.max=4
+ # - it means that its parent (maxallowed) should have at least children.max=5
+ # - it makes the origin (parent of maxallowed) provide children.max=6 minimum
+ #
+ test $origin_cur -le $origin_max || atf_fail "Abnormal cur=$origin_cur > max=$origin_max."
+ test $((origin_max - origin_cur)) -ge 3 || atf_skip "Not enough child jails are allowed for the test."
+ test $origin_max -ge 6 || atf_skip "Not high enough children.max limit for the test."
+
+ jail -c name=childfree persist
+ atf_check_equal "$((origin_cur + 1))" "$(sysctl -n security.jail.children.cur)"
+ atf_check_equal "0" "$(jexec childfree sysctl -n security.jail.children.max)"
+ atf_check_equal "0" "$(jexec childfree sysctl -n security.jail.children.cur)"
+
+ jail -c name=maxallowed children.max=$((origin_max - 1)) persist
+ atf_check_equal "$((origin_cur + 2))" "$(sysctl -n security.jail.children.cur)"
+ atf_check_equal "$((origin_max - 1))" "$(jexec maxallowed sysctl -n security.jail.children.max)"
+ atf_check_equal "0" "$(jexec maxallowed sysctl -n security.jail.children.cur)"
+
+ jexec maxallowed jail -c name=family children.max=4 persist
+ atf_check_equal "$((origin_cur + 3))" "$(sysctl -n security.jail.children.cur)"
+ atf_check_equal "1" "$(jexec maxallowed sysctl -n security.jail.children.cur)"
+ atf_check_equal "4" "$(jexec maxallowed.family sysctl -n security.jail.children.max)"
+ atf_check_equal "0" "$(jexec maxallowed.family sysctl -n security.jail.children.cur)"
+}
+max_cur_cleanup()
+{
+ jail -r maxallowed
+ jail -r childfree
+ return 0
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "max_cur"
+}
diff --git a/tests/sys/kern/tty/Makefile b/tests/sys/kern/tty/Makefile
new file mode 100644
index 000000000000..8628ab79875f
--- /dev/null
+++ b/tests/sys/kern/tty/Makefile
@@ -0,0 +1,15 @@
+TESTSDIR= ${TESTSBASE}/sys/kern/tty
+BINDIR= ${TESTSDIR}
+
+PLAIN_TESTS_PORCH+= test_canon
+PLAIN_TESTS_PORCH+= test_canon_fullbuf
+PLAIN_TESTS_PORCH+= test_ncanon
+PLAIN_TESTS_PORCH+= test_recanon
+ATF_TESTS_C+= test_sti
+
+PROGS+= fionread
+PROGS+= readsz
+
+LIBADD.test_sti= util
+
+.include <bsd.test.mk>
diff --git a/tests/sys/kern/tty/fionread.c b/tests/sys/kern/tty/fionread.c
new file mode 100644
index 000000000000..929d613f883b
--- /dev/null
+++ b/tests/sys/kern/tty/fionread.c
@@ -0,0 +1,21 @@
+/*-
+ * Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/ioctl.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int
+main(void)
+{
+ int nb;
+
+ assert(ioctl(STDIN_FILENO, FIONREAD, &nb) == 0);
+ printf("%d", nb);
+ return (0);
+}
diff --git a/tests/sys/kern/tty/readsz.c b/tests/sys/kern/tty/readsz.c
new file mode 100644
index 000000000000..95dafa02472f
--- /dev/null
+++ b/tests/sys/kern/tty/readsz.c
@@ -0,0 +1,130 @@
+/*-
+ * Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "usage: %s [-b bytes | -c lines | -e] [-s buffer-size]\n",
+ getprogname());
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *buf;
+ const char *errstr;
+ size_t bufsz = 0, reps;
+ ssize_t ret;
+ enum { MODE_BYTES, MODE_COUNT, MODE_EOF } mode;
+ int ch;
+
+ /*
+ * -b specifies number of bytes.
+ * -c specifies number of read() calls.
+ * -e specifies eof (default)
+ * -s to pass a buffer size
+ *
+ * Reading N lines is the same as -c with a high buffer size.
+ */
+ mode = MODE_EOF;
+ while ((ch = getopt(argc, argv, "b:c:es:")) != -1) {
+ switch (ch) {
+ case 'b':
+ mode = MODE_BYTES;
+ reps = strtonum(optarg, 0, SSIZE_MAX, &errstr);
+ if (errstr != NULL)
+ errx(1, "strtonum: %s", errstr);
+ break;
+ case 'c':
+ mode = MODE_COUNT;
+ reps = strtonum(optarg, 1, SSIZE_MAX, &errstr);
+ if (errstr != NULL)
+ errx(1, "strtonum: %s", errstr);
+ break;
+ case 'e':
+ mode = MODE_EOF;
+ break;
+ case 's':
+ bufsz = strtonum(optarg, 1, SSIZE_MAX, &errstr);
+ if (errstr != NULL)
+ errx(1, "strtonum: %s", errstr);
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (bufsz == 0) {
+ if (mode == MODE_BYTES)
+ bufsz = reps;
+ else
+ bufsz = LINE_MAX;
+ }
+
+ buf = malloc(bufsz);
+ if (buf == NULL)
+ err(1, "malloc");
+
+ for (;;) {
+ size_t readsz;
+
+ /*
+ * Be careful not to over-read if we're in byte-mode. In every other
+ * mode, we'll read as much as we can.
+ */
+ if (mode == MODE_BYTES)
+ readsz = MIN(bufsz, reps);
+ else
+ readsz = bufsz;
+
+ ret = read(STDIN_FILENO, buf, readsz);
+ if (ret == -1 && errno == EINTR)
+ continue;
+ if (ret == -1)
+ err(1, "read");
+ if (ret == 0) {
+ if (mode == MODE_EOF)
+ return (0);
+ errx(1, "premature EOF");
+ }
+
+ /* Write out what we've got */
+ write(STDOUT_FILENO, buf, ret);
+
+ /*
+ * Bail out if we've hit our metric (byte mode / count mode).
+ */
+ switch (mode) {
+ case MODE_BYTES:
+ reps -= ret;
+ if (reps == 0)
+ return (0);
+ break;
+ case MODE_COUNT:
+ reps--;
+ if (reps == 0)
+ return (0);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return (0);
+}
diff --git a/tests/sys/kern/tty/test_canon.orch b/tests/sys/kern/tty/test_canon.orch
new file mode 100644
index 000000000000..28018edfdcd6
--- /dev/null
+++ b/tests/sys/kern/tty/test_canon.orch
@@ -0,0 +1,102 @@
+#!/usr/bin/env -S porch -f
+--
+-- Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+
+timeout(3)
+
+spawn("cat")
+
+write "Complete\r"
+match "Complete\r"
+
+write "Basic\rIncomplete"
+match "Basic\r"
+
+-- We shouldn't see any of the "Incomplete" line
+fail(function()
+end)
+
+match "Incomp" {
+ callback = function()
+ exit(1)
+ end
+}
+
+fail(nil)
+
+-- Pushing a ^D along should force a flush of the tty, cat(1) will write the
+-- result without a trailing newline.
+write " line^D"
+match "Incomplete line$"
+
+-- Erase!
+write "Dog^H^D"
+match "Do$"
+
+-- More erase!
+write "Cat Dog^W^D"
+match "Cat $"
+
+write "^D"
+eof()
+
+local function fionread_test(str, expected)
+ spawn("fionread")
+
+ write(str)
+ match(expected)
+end
+
+-- Incomplete line
+fionread_test("Hello", "0")
+-- VEOF does not count
+fionread_test("Hello^D", "5")
+-- VEOF still doesn't count, even if the next line is an extra VEOF later
+fionread_test("Hello^D^D", "5")
+-- read(2) definitely won't return the second incomplete line
+fionread_test("Hello^Dther", "5")
+-- read(2) also won't return a second complete line at once
+fionread_test("Hello^Dthere^D", "5")
+-- Finally, send a VEOF to terminate a blank line and signal EOF in read(2)
+fionread_test("^D", "0")
+
+-- \r will instead show up in the input stream to the application, so we must
+-- make sure those are counted where VEOF generally wouldn't be.
+fionread_test("Hello\r", "6")
+fionread_test("Hello\rther", "6")
+fionread_test("Hello\rthere\r", "6")
+fionread_test("\r", "1")
+
+local function readsz_test(str, arg, expected)
+ spawn("readsz", table.unpack(arg))
+
+ if type(str) == "table" then
+ assert(#str == 2)
+ write(str[1])
+ release()
+
+ -- Give readsz a chance to consume the partial input before we send more
+ -- along.
+ sleep(1)
+ write(str[2])
+ else
+ write(str)
+ end
+ match(expected)
+end
+
+readsz_test("partial", {"-b", 3}, "^$")
+readsz_test("partial^D", {"-b", 3}, "^par$")
+readsz_test("partial^D", {"-c", 1}, "^partial$")
+for s = 1, #"partial" do
+ readsz_test("partial^D", {"-s", s}, "^partial$")
+end
+-- Send part of the line, release and pause, then finish it.
+readsz_test({"par", "tial^D"}, {"-c", 1}, "^partial$")
+-- line is incomplete, so we'll just see the "partial" even if we want two
+readsz_test("partial^Dline", {"-c", 2}, "^partial$")
+readsz_test("partial^Dline^D", {"-c", 1}, "^partial$")
+readsz_test("partial^Dline^D", {"-c", 2}, "^partialline$")
diff --git a/tests/sys/kern/tty/test_canon_fullbuf.orch b/tests/sys/kern/tty/test_canon_fullbuf.orch
new file mode 100644
index 000000000000..1833703e4f45
--- /dev/null
+++ b/tests/sys/kern/tty/test_canon_fullbuf.orch
@@ -0,0 +1,23 @@
+#!/usr/bin/env -S porch -f
+--
+-- Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+
+timeout(3)
+
+local TTYINQ_DATASIZE = 128
+local scream = string.rep("A", TTYINQ_DATASIZE - 1)
+
+spawn("cat")
+
+-- Fill up a whole block with screaming + VEOF
+write(scream .. "^D")
+match(scream .. "$")
+
+scream = scream .. "A"
+
+-- Now fill up the next block, but spill the VEOF over to a third block.
+write(scream .. "^D")
+match(scream .. "$")
diff --git a/tests/sys/kern/tty/test_ncanon.orch b/tests/sys/kern/tty/test_ncanon.orch
new file mode 100644
index 000000000000..14a34d82fa9a
--- /dev/null
+++ b/tests/sys/kern/tty/test_ncanon.orch
@@ -0,0 +1,39 @@
+#!/usr/bin/env -S porch -f
+--
+-- Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+
+timeout(3)
+
+local function spawn_one(...)
+ spawn(...)
+
+ stty("lflag", 0, tty.lflag.ICANON)
+end
+
+-- We can send one byte...
+spawn_one("readsz", "-c", 1)
+write "H"
+match "^H$"
+
+-- or many.
+spawn_one("readsz", "-c", 1)
+write "Hello"
+match "^Hello$"
+
+-- VEOF is a normal character here, passed through as-is.
+spawn_one("readsz", "-c", 1)
+write "Hello^D"
+match "^Hello\x04$"
+spawn_one("readsz", "-c", 1)
+write "^D"
+match "^\x04$"
+
+-- Confirm that FIONREAD agrees that VEOF will be returned, even if it was sent
+-- while the tty was still in canonical mode.
+spawn("fionread")
+write "^D"
+stty("lflag", 0, tty.lflag.ICANON)
+match "^1$"
diff --git a/tests/sys/kern/tty/test_recanon.orch b/tests/sys/kern/tty/test_recanon.orch
new file mode 100644
index 000000000000..e3943495ca5d
--- /dev/null
+++ b/tests/sys/kern/tty/test_recanon.orch
@@ -0,0 +1,90 @@
+#!/usr/bin/env -S porch -f
+--
+-- Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+
+timeout(3)
+
+local TTYINQ_DATASIZE = 128
+local scream = string.rep("A", TTYINQ_DATASIZE - 1)
+
+local function ncanon()
+ stty("lflag", nil, tty.lflag.ICANON)
+end
+
+local function canon()
+ stty("lflag", tty.lflag.ICANON)
+end
+
+spawn("readsz", "-e")
+ncanon()
+
+-- Fill up a whole block with screaming + VEOF; when it gets recanonicalized,
+-- the next line should be pointing to the beginning of the next block.
+write(scream .. "^D")
+
+canon()
+match(scream .. "$")
+
+-- The same as above, but spilling VEOF over to the next block.
+spawn("readsz", "-e")
+ncanon()
+
+write(scream .. "A^D")
+
+canon()
+match(scream .. "A$")
+
+-- We'll do it again, except with one character spilled over to the next block
+-- before we recanonicalize. We should then have the scream, followed by a
+-- partial line containing the spill over.
+spawn("cat")
+ncanon()
+
+write(scream .. "^DZ")
+
+canon()
+match(scream .. "$")
+
+-- Sending "B^D" should give us "ZB" to make sure that we didn't lose anything
+-- at the beginning of the next block.
+
+write("B^D")
+match("^ZB$")
+
+-- Next we'll VEOF at the beginning.
+spawn("readsz", "-e")
+ncanon()
+
+write("^D")
+match("^$")
+
+-- Finally, we'll trigger recanonicalization with an empty buffer. This one is
+-- just about avoiding a panic.
+spawn("true")
+
+ncanon()
+canon()
+release()
+eof()
+
+spawn("readsz", "-c", "1")
+
+write("Test^Dfoo")
+ncanon()
+
+match("^Test\x04foo$")
+
+-- Finally, swap VEOF out with ^F; before recent changes, we would remain
+-- canonicalized at Test^D and the kernel would block on it unless a short
+-- buffer was used since VEOF would not appear within the canonicalized bit.
+spawn("readsz", "-c", 1)
+
+write("Test^DLine^F")
+stty("cc", {
+ VEOF = "^F"
+})
+
+match("^Test\x04Line$")
diff --git a/tests/sys/kern/tty/test_sti.c b/tests/sys/kern/tty/test_sti.c
new file mode 100644
index 000000000000..f792001b4e3f
--- /dev/null
+++ b/tests/sys/kern/tty/test_sti.c
@@ -0,0 +1,337 @@
+/*-
+ * Copyright (c) 2025 Kyle Evans <kevans@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <termios.h>
+
+#include <atf-c.h>
+#include <libutil.h>
+
+enum stierr {
+ STIERR_CONFIG_FETCH,
+ STIERR_CONFIG,
+ STIERR_INJECT,
+ STIERR_READFAIL,
+ STIERR_BADTEXT,
+ STIERR_DATAFOUND,
+ STIERR_ROTTY,
+ STIERR_WOTTY,
+ STIERR_WOOK,
+ STIERR_BADERR,
+
+ STIERR_MAXERR
+};
+
+static const struct stierr_map {
+ enum stierr stierr;
+ const char *msg;
+} stierr_map[] = {
+ { STIERR_CONFIG_FETCH, "Failed to fetch ctty configuration" },
+ { STIERR_CONFIG, "Failed to configure ctty in the child" },
+ { STIERR_INJECT, "Failed to inject characters via TIOCSTI" },
+ { STIERR_READFAIL, "Failed to read(2) from stdin" },
+ { STIERR_BADTEXT, "read(2) data did not match injected data" },
+ { STIERR_DATAFOUND, "read(2) data when we did not expected to" },
+ { STIERR_ROTTY, "Failed to open tty r/o" },
+ { STIERR_WOTTY, "Failed to open tty w/o" },
+ { STIERR_WOOK, "TIOCSTI on w/o tty succeeded" },
+ { STIERR_BADERR, "Received wrong error from failed TIOCSTI" },
+};
+_Static_assert(nitems(stierr_map) == STIERR_MAXERR,
+ "Failed to describe all errors");
+
+/*
+ * Inject each character of the input string into the TTY. The caller can
+ * assume that errno is preserved on return.
+ */
+static ssize_t
+inject(int fileno, const char *str)
+{
+ size_t nb = 0;
+
+ for (const char *walker = str; *walker != '\0'; walker++) {
+ if (ioctl(fileno, TIOCSTI, walker) != 0)
+ return (-1);
+ nb++;
+ }
+
+ return (nb);
+}
+
+/*
+ * Forks off a new process, stashes the parent's handle for the pty in *termfd
+ * and returns the pid. 0 for the child, >0 for the parent, as usual.
+ *
+ * Most tests fork so that we can do them while unprivileged, which we can only
+ * do if we're operating on our ctty (and we don't want to touch the tty of
+ * whatever may be running the tests).
+ */
+static int
+init_pty(int *termfd, bool canon)
+{
+ int pid;
+
+ pid = forkpty(termfd, NULL, NULL, NULL);
+ ATF_REQUIRE(pid != -1);
+
+ if (pid == 0) {
+ struct termios term;
+
+ /*
+ * Child reconfigures tty to disable echo and put it into raw
+ * mode if requested.
+ */
+ if (tcgetattr(STDIN_FILENO, &term) == -1)
+ _exit(STIERR_CONFIG_FETCH);
+ term.c_lflag &= ~ECHO;
+ if (!canon)
+ term.c_lflag &= ~ICANON;
+ if (tcsetattr(STDIN_FILENO, TCSANOW, &term) == -1)
+ _exit(STIERR_CONFIG);
+ }
+
+ return (pid);
+}
+
+static void
+finalize_child(pid_t pid, int signo)
+{
+ int status, wpid;
+
+ while ((wpid = waitpid(pid, &status, 0)) != pid) {
+ if (wpid != -1)
+ continue;
+ ATF_REQUIRE_EQ_MSG(EINTR, errno,
+ "waitpid: %s", strerror(errno));
+ }
+
+ /*
+ * Some tests will signal the child for whatever reason, and we're
+ * expecting it to terminate it. For those cases, it's OK to just see
+ * that termination. For all other cases, we expect a graceful exit
+ * with an exit status that reflects a cause that we have an error
+ * mapped for.
+ */
+ if (signo >= 0) {
+ ATF_REQUIRE(WIFSIGNALED(status));
+ ATF_REQUIRE_EQ(signo, WTERMSIG(status));
+ } else {
+ ATF_REQUIRE(WIFEXITED(status));
+ if (WEXITSTATUS(status) != 0) {
+ int err = WEXITSTATUS(status);
+
+ for (size_t i = 0; i < nitems(stierr_map); i++) {
+ const struct stierr_map *map = &stierr_map[i];
+
+ if ((int)map->stierr == err) {
+ atf_tc_fail("%s", map->msg);
+ __assert_unreachable();
+ }
+ }
+ }
+ }
+}
+
+ATF_TC(basic);
+ATF_TC_HEAD(basic, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test for basic functionality of TIOCSTI");
+ atf_tc_set_md_var(tc, "require.user", "unprivileged");
+}
+ATF_TC_BODY(basic, tc)
+{
+ int pid, term;
+
+ /*
+ * We don't canonicalize on this test because we can assume that the
+ * injected data will be available after TIOCSTI returns. This is all
+ * within a single thread for the basic test, so we simplify our lives
+ * slightly in raw mode.
+ */
+ pid = init_pty(&term, false);
+ if (pid == 0) {
+ static const char sending[] = "Text";
+ char readbuf[32];
+ ssize_t injected, readsz;
+
+ injected = inject(STDIN_FILENO, sending);
+ if (injected != sizeof(sending) - 1)
+ _exit(STIERR_INJECT);
+
+ readsz = read(STDIN_FILENO, readbuf, sizeof(readbuf));
+
+ if (readsz < 0 || readsz != injected)
+ _exit(STIERR_READFAIL);
+ if (memcmp(readbuf, sending, readsz) != 0)
+ _exit(STIERR_BADTEXT);
+
+ _exit(0);
+ }
+
+ finalize_child(pid, -1);
+}
+
+ATF_TC(root);
+ATF_TC_HEAD(root, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test that root can inject into another TTY");
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(root, tc)
+{
+ static const char sending[] = "Text\r";
+ ssize_t injected;
+ int pid, term;
+
+ /*
+ * We leave canonicalization enabled for this one so that the read(2)
+ * below hangs until we have all of the data available, rather than
+ * having to signal OOB that it's safe to read.
+ */
+ pid = init_pty(&term, true);
+ if (pid == 0) {
+ char readbuf[32];
+ ssize_t readsz;
+
+ readsz = read(STDIN_FILENO, readbuf, sizeof(readbuf));
+ if (readsz < 0 || readsz != sizeof(sending) - 1)
+ _exit(STIERR_READFAIL);
+
+ /*
+ * Here we ignore the trailing \r, because it won't have
+ * surfaced in our read(2).
+ */
+ if (memcmp(readbuf, sending, readsz - 1) != 0)
+ _exit(STIERR_BADTEXT);
+
+ _exit(0);
+ }
+
+ injected = inject(term, sending);
+ ATF_REQUIRE_EQ_MSG(sizeof(sending) - 1, injected,
+ "Injected %zu characters, expected %zu", injected,
+ sizeof(sending) - 1);
+
+ finalize_child(pid, -1);
+}
+
+ATF_TC(unprivileged_fail_noctty);
+ATF_TC_HEAD(unprivileged_fail_noctty, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test that unprivileged cannot inject into non-controlling TTY");
+ atf_tc_set_md_var(tc, "require.user", "unprivileged");
+}
+ATF_TC_BODY(unprivileged_fail_noctty, tc)
+{
+ const char sending[] = "Text";
+ ssize_t injected;
+ int pid, serrno, term;
+
+ pid = init_pty(&term, false);
+ if (pid == 0) {
+ char readbuf[32];
+ ssize_t readsz;
+
+ /*
+ * This should hang until we get terminated by the parent.
+ */
+ readsz = read(STDIN_FILENO, readbuf, sizeof(readbuf));
+ if (readsz > 0)
+ _exit(STIERR_DATAFOUND);
+
+ _exit(0);
+ }
+
+ /* Should fail. */
+ injected = inject(term, sending);
+ serrno = errno;
+
+ /* Done with the child, just kill it now to avoid problems later. */
+ kill(pid, SIGINT);
+ finalize_child(pid, SIGINT);
+
+ ATF_REQUIRE_EQ_MSG(-1, (ssize_t)injected,
+ "TIOCSTI into non-ctty succeeded");
+ ATF_REQUIRE_EQ(EACCES, serrno);
+}
+
+ATF_TC(unprivileged_fail_noread);
+ATF_TC_HEAD(unprivileged_fail_noread, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test that unprivileged cannot inject into TTY not opened for read");
+ atf_tc_set_md_var(tc, "require.user", "unprivileged");
+}
+ATF_TC_BODY(unprivileged_fail_noread, tc)
+{
+ int pid, term;
+
+ /*
+ * Canonicalization actually doesn't matter for this one, we'll trust
+ * that the failure means we didn't inject anything.
+ */
+ pid = init_pty(&term, true);
+ if (pid == 0) {
+ static const char sending[] = "Text";
+ ssize_t injected;
+ int rotty, wotty;
+
+ /*
+ * We open the tty both r/o and w/o to ensure we got the device
+ * name right; one of these will pass, one of these will fail.
+ */
+ wotty = openat(STDIN_FILENO, "", O_EMPTY_PATH | O_WRONLY);
+ if (wotty == -1)
+ _exit(STIERR_WOTTY);
+ rotty = openat(STDIN_FILENO, "", O_EMPTY_PATH | O_RDONLY);
+ if (rotty == -1)
+ _exit(STIERR_ROTTY);
+
+ /*
+ * This injection is expected to fail with EPERM, because it may
+ * be our controlling tty but it is not open for reading.
+ */
+ injected = inject(wotty, sending);
+ if (injected != -1)
+ _exit(STIERR_WOOK);
+ if (errno != EPERM)
+ _exit(STIERR_BADERR);
+
+ /*
+ * Demonstrate that it does succeed on the other fd we opened,
+ * which is r/o.
+ */
+ injected = inject(rotty, sending);
+ if (injected != sizeof(sending) - 1)
+ _exit(STIERR_INJECT);
+
+ _exit(0);
+ }
+
+ finalize_child(pid, -1);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, basic);
+ ATF_TP_ADD_TC(tp, root);
+ ATF_TP_ADD_TC(tp, unprivileged_fail_noctty);
+ ATF_TP_ADD_TC(tp, unprivileged_fail_noread);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/tty_pts.c b/tests/sys/kern/tty_pts.c
new file mode 100644
index 000000000000..241cb085a5a7
--- /dev/null
+++ b/tests/sys/kern/tty_pts.c
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <termios.h>
+
+#include <atf-c.h>
+#include <libutil.h>
+
+/* Just a little more concise. */
+#define newpty(masterp, slavep) openpty((masterp), (slavep), NULL, NULL, NULL)
+
+ATF_TC_WITHOUT_HEAD(fionread);
+ATF_TC_BODY(fionread, tc)
+{
+ char rbuf[32];
+ char buf[] = "Hello";
+ int master, slave;
+ int bytes;
+
+ ATF_REQUIRE_EQ(0, newpty(&master, &slave));
+
+ /* Should be empty to begin with. */
+ ATF_REQUIRE_EQ(0, ioctl(master, FIONREAD, &bytes));
+ ATF_REQUIRE_EQ(0, bytes);
+
+ ATF_REQUIRE_EQ(sizeof(buf) - 1, write(slave, buf, sizeof(buf) - 1));
+ ATF_REQUIRE_EQ(0, ioctl(master, FIONREAD, &bytes));
+ ATF_REQUIRE_EQ(sizeof(buf) - 1, bytes);
+
+ /* Drain what we have available, should result in 0 bytes again. */
+ ATF_REQUIRE_EQ(sizeof(buf) - 1, read(master, rbuf, sizeof(rbuf)));
+ ATF_REQUIRE_EQ(0, ioctl(master, FIONREAD, &bytes));
+ ATF_REQUIRE_EQ(0, bytes);
+
+ /*
+ * Write once more, then close the slave side with data still in the
+ * buffer.
+ */
+ ATF_REQUIRE_EQ(sizeof(buf) - 1, write(slave, buf, sizeof(buf) - 1));
+ ATF_REQUIRE_EQ(0, ioctl(master, FIONREAD, &bytes));
+ ATF_REQUIRE_EQ(sizeof(buf) - 1, bytes);
+
+ ATF_REQUIRE_EQ(0, close(slave));
+
+ /*
+ * The tty's output queue is discarded upon close, so we shouldn't have
+ * anything else to read().
+ */
+ ATF_REQUIRE_EQ(0, ioctl(master, FIONREAD, &bytes));
+ ATF_REQUIRE_EQ(0, bytes);
+ ATF_REQUIRE_EQ(0, read(master, rbuf, sizeof(rbuf)));
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, fionread);
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/unix_dgram.c b/tests/sys/kern/unix_dgram.c
new file mode 100644
index 000000000000..7464cdf197cd
--- /dev/null
+++ b/tests/sys/kern/unix_dgram.c
@@ -0,0 +1,441 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2022 Gleb Smirnoff <glebius@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/event.h>
+#include <sys/ioctl.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/un.h>
+
+#include <aio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <atf-c.h>
+
+static struct itimerval itv = {
+ .it_interval = { 0, 0 },
+ .it_value = { 1, 0 }, /* one second */
+};
+static sig_atomic_t timer_done = 0;
+static void
+sigalarm(int sig __unused)
+{
+
+ timer_done = 1;
+}
+
+static struct sigaction sigact = {
+ .sa_handler = sigalarm,
+};
+
+static struct sockaddr_un sun = {
+ .sun_family = AF_LOCAL,
+ .sun_len = sizeof(sun),
+ .sun_path = "unix_dgram_listener",
+};
+
+/*
+ * Fill socket to a state when next send(len) would fail.
+ *
+ * Note that every datagram is prepended with sender address,
+ * size of struct sockaddr.
+ */
+static void
+fill(int fd, void *buf, ssize_t len)
+{
+ unsigned long recvspace;
+ size_t llen = sizeof(unsigned long);
+ ssize_t sent;
+
+ ATF_REQUIRE(sysctlbyname("net.local.dgram.recvspace", &recvspace,
+ &llen, NULL, 0) == 0);
+ for (sent = 0;
+ sent + len + sizeof(struct sockaddr) < recvspace;
+ sent += len + sizeof(struct sockaddr))
+ ATF_REQUIRE(send(fd, buf, len, 0) == len);
+}
+
+ATF_TC_WITHOUT_HEAD(basic);
+ATF_TC_BODY(basic, tc)
+{
+ struct msghdr msg;
+ struct iovec iov[1];
+ unsigned long maxdgram;
+ size_t llen = sizeof(unsigned long);
+ int fd[2];
+ char *buf;
+
+ /* Allocate and initialize:
+ * - fd[0] to send, fd[1] to receive
+ * - buf[maxdgram] for data
+ */
+ ATF_REQUIRE(sysctlbyname("net.local.dgram.maxdgram", &maxdgram,
+ &llen, NULL, 0) == 0);
+ ATF_REQUIRE(socketpair(PF_UNIX, SOCK_DGRAM, 0, fd) != -1);
+ buf = malloc(maxdgram + 1);
+ ATF_REQUIRE(buf);
+ msg = (struct msghdr ){
+ .msg_iov = iov,
+ .msg_iovlen = 1,
+ };
+ iov[0] = (struct iovec ){
+ .iov_base = buf,
+ };
+
+ /* Fail to send > maxdgram. */
+ ATF_REQUIRE(send(fd[0], buf, maxdgram + 1, 0) == -1);
+ ATF_REQUIRE(errno == EMSGSIZE);
+
+ /* Send maxdgram. */
+ ATF_REQUIRE(send(fd[0], buf, maxdgram, 0) == (ssize_t)maxdgram);
+
+ /* Exercise MSG_PEEK, full and truncated.. */
+ ATF_REQUIRE(recv(fd[1], buf, maxdgram, MSG_PEEK) == (ssize_t)maxdgram);
+ iov[0].iov_len = 42;
+ ATF_REQUIRE(recvmsg(fd[1], &msg, MSG_PEEK) == 42);
+ ATF_REQUIRE(msg.msg_flags == (MSG_PEEK | MSG_TRUNC));
+
+ /* Receive maxdgram. */
+ iov[0].iov_len = maxdgram;
+ ATF_REQUIRE(recvmsg(fd[1], &msg, 0) == (ssize_t)maxdgram);
+ ATF_REQUIRE(msg.msg_flags == 0);
+
+ /* Receive truncated message. */
+ ATF_REQUIRE(send(fd[0], buf, maxdgram, 0) == (ssize_t)maxdgram);
+ iov[0].iov_len = maxdgram / 2;
+ ATF_REQUIRE(recvmsg(fd[1], &msg, 0) == (ssize_t)maxdgram / 2);
+ ATF_REQUIRE(msg.msg_flags == MSG_TRUNC);
+
+ /* Empty: block. */
+ ATF_REQUIRE(sigaction(SIGALRM, &sigact, NULL) == 0);
+ ATF_REQUIRE(timer_done == 0);
+ ATF_REQUIRE(setitimer(ITIMER_REAL, &itv, NULL) == 0);
+ ATF_REQUIRE(recv(fd[1], buf, maxdgram, 0) == -1);
+ ATF_REQUIRE(errno == EINTR);
+ ATF_REQUIRE(timer_done == 1);
+
+ /* Don't block with MSG_DONTWAIT. */
+ ATF_REQUIRE(recv(fd[1], buf, maxdgram, MSG_DONTWAIT) == -1);
+ ATF_REQUIRE(errno == EAGAIN);
+
+ /* Don't block with O_NONBLOCK. */
+ ATF_REQUIRE(fcntl(fd[1], F_SETFL, O_NONBLOCK) != -1);
+ ATF_REQUIRE(recv(fd[1], buf, maxdgram, 0) == -1);
+ ATF_REQUIRE(errno == EAGAIN);
+
+ /* Fail with ENOBUFS on full socket. */
+ fill(fd[0], buf, maxdgram);
+ ATF_REQUIRE(send(fd[0], buf, maxdgram, 0) == -1);
+ ATF_REQUIRE(errno == ENOBUFS);
+
+ /*
+ * Fail with ENOBUFS with O_NONBLOCK set, too. See 71e70c25c00
+ * for explanation why this behavior needs to be preserved.
+ */
+ ATF_REQUIRE(fcntl(fd[0], F_SETFL, O_NONBLOCK) != -1);
+ ATF_REQUIRE(send(fd[0], buf, maxdgram, 0) == -1);
+ ATF_REQUIRE(errno == ENOBUFS);
+
+ /* Remote side closed -> ECONNRESET. */
+ close(fd[1]);
+ ATF_REQUIRE(send(fd[0], buf, maxdgram, 0) == -1);
+ ATF_REQUIRE(errno == ECONNRESET);
+}
+
+ATF_TC_WITHOUT_HEAD(one2many);
+ATF_TC_BODY(one2many, tc)
+{
+ int one, many[3], two;
+#define BUFSIZE 1024
+ char buf[BUFSIZE], goodboy[BUFSIZE], flooder[BUFSIZE], notconn[BUFSIZE];
+
+ /* Establish one to many connection. */
+ ATF_REQUIRE((one = socket(PF_UNIX, SOCK_DGRAM, 0)) > 0);
+ ATF_REQUIRE(bind(one, (struct sockaddr *)&sun, sizeof(sun)) == 0);
+ /* listen(2) shall fail. */
+ ATF_REQUIRE(listen(one, -1) != 0);
+ for (int i = 0; i < 3; i++) {
+ ATF_REQUIRE((many[i] = socket(PF_UNIX, SOCK_DGRAM, 0)) > 0);
+ ATF_REQUIRE(connect(many[i], (struct sockaddr *)&sun,
+ sizeof(sun)) == 0);
+ }
+
+ /* accept() on UNIX/DGRAM is invalid. */
+ ATF_REQUIRE(accept(one, NULL, NULL) == -1);
+ ATF_REQUIRE(errno == EINVAL);
+
+ /*
+ * Connecting a bound socket to self: a strange, useless, but
+ * historically existing edge case that is not explicitly described
+ * in SuS, neither is forbidden there. Works on FreeBSD and Linux.
+ */
+ ATF_REQUIRE(connect(one, (struct sockaddr *)&sun, sizeof(sun)) == 0);
+ ATF_REQUIRE(send(one, buf, 42, 0) == 42);
+ ATF_REQUIRE(recv(one, buf, sizeof(buf), 0) == 42);
+
+ /*
+ * Interaction between concurrent senders. New feature in FreeBSD 14.
+ *
+ * One sender can not fill the receive side. Other senders can
+ * continue operation. Senders who don't fill their buffers are
+ * prioritized over flooders. Connected senders are prioritized over
+ * unconnected.
+ *
+ * Disconnecting a sender that has queued data optionally preserves
+ * the data. Allow the data to migrate to peers buffer only if the
+ * latter is empty. Otherwise discard it, to prevent against
+ * connect-fill-close attack.
+ */
+#define FLOODER 13 /* for connected flooder on many[0] */
+#define GOODBOY 42 /* for a good boy on many[1] */
+#define NOTCONN 66 /* for sendto(2) via two */
+ goodboy[0] = GOODBOY;
+ flooder[0] = FLOODER;
+ notconn[0] = NOTCONN;
+
+ /* Connected priority over sendto(2). */
+ ATF_REQUIRE((two = socket(PF_UNIX, SOCK_DGRAM, 0)) > 0);
+ ATF_REQUIRE(sendto(two, notconn, BUFSIZE, 0, (struct sockaddr *)&sun,
+ sizeof(sun)) == BUFSIZE);
+ ATF_REQUIRE(send(many[1], goodboy, BUFSIZE, 0) == BUFSIZE);
+ ATF_REQUIRE(recv(one, buf, sizeof(buf), 0) == sizeof(buf));
+ ATF_REQUIRE(buf[0] == GOODBOY); /* message from good boy comes first */
+ ATF_REQUIRE(recv(one, buf, sizeof(buf), 0) == sizeof(buf));
+ ATF_REQUIRE(buf[0] == NOTCONN); /* only then message from sendto(2) */
+
+ /* Casual sender priority over a flooder. */
+ fill(many[0], flooder, sizeof(flooder));
+ ATF_REQUIRE(send(many[0], flooder, BUFSIZE, 0) == -1);
+ ATF_REQUIRE(errno == ENOBUFS);
+ ATF_REQUIRE(send(many[1], goodboy, BUFSIZE, 0) == BUFSIZE);
+ ATF_REQUIRE(recv(one, buf, sizeof(buf), 0) == sizeof(buf));
+ ATF_REQUIRE(buf[0] == GOODBOY); /* message from good boy comes first */
+ ATF_REQUIRE(recv(one, buf, sizeof(buf), 0) == sizeof(buf));
+ ATF_REQUIRE(buf[0] == FLOODER); /* only then message from flooder */
+
+ /* Once seen, a message can't be deprioritized by any other message. */
+ ATF_REQUIRE(recv(one, buf, sizeof(buf), MSG_PEEK) == sizeof(buf));
+ ATF_REQUIRE(buf[0] == FLOODER); /* message from the flooder seen */
+ ATF_REQUIRE(send(many[1], goodboy, BUFSIZE, 0) == BUFSIZE);
+ ATF_REQUIRE(recv(one, buf, sizeof(buf), MSG_PEEK) == sizeof(buf));
+ ATF_REQUIRE(buf[0] == FLOODER); /* should be the same message */
+ ATF_REQUIRE(recv(one, buf, sizeof(buf), 0) == sizeof(buf));
+ ATF_REQUIRE(buf[0] == FLOODER); /* now we read it out... */
+ ATF_REQUIRE(recv(one, buf, sizeof(buf), 0) == sizeof(buf));
+ ATF_REQUIRE(buf[0] == GOODBOY); /* ... and next one is the good boy */
+
+ /* Disconnect in presence of data from not connected. */
+ ATF_REQUIRE(sendto(two, notconn, BUFSIZE, 0, (struct sockaddr *)&sun,
+ sizeof(sun)) == BUFSIZE);
+ close(many[0]);
+ ATF_REQUIRE(recv(one, buf, sizeof(buf), 0) == sizeof(buf));
+ ATF_REQUIRE(buf[0] == NOTCONN); /* message from sendto() */
+ ATF_REQUIRE(recv(one, buf, sizeof(buf), MSG_DONTWAIT) == -1);
+ ATF_REQUIRE(errno == EAGAIN); /* data from many[0] discarded */
+
+ /* Disconnect in absence of data from not connected. */
+ ATF_REQUIRE(send(many[1], goodboy, BUFSIZE, 0) == BUFSIZE);
+ close(many[1]);
+ ATF_REQUIRE(recv(one, buf, sizeof(buf), 0) == sizeof(buf));
+ ATF_REQUIRE(buf[0] == GOODBOY); /* message from many[1] preserved */
+
+ /* Check that nothing leaks on close(2). */
+ ATF_REQUIRE(send(many[2], buf, 42, 0) == 42);
+ ATF_REQUIRE(send(many[2], buf, 42, 0) == 42);
+ ATF_REQUIRE(recv(one, buf, sizeof(buf), MSG_PEEK) == 42);
+ ATF_REQUIRE(sendto(two, notconn, 42, 0, (struct sockaddr *)&sun,
+ sizeof(sun)) == 42);
+ close(one);
+}
+
+/*
+ * Check that various mechanism report socket as readable and having
+ * 42 bytes of data.
+ */
+static void
+test42(int fd)
+{
+
+ /* ioctl(FIONREAD) */
+ int data;
+
+ ATF_REQUIRE(ioctl(fd, FIONREAD, &data) != -1);
+ ATF_REQUIRE(data == 42);
+
+ /* select(2) */
+ fd_set rfds;
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+ ATF_REQUIRE(select(fd + 1, &rfds, NULL, NULL, NULL) == 1);
+ ATF_REQUIRE(FD_ISSET(fd, &rfds));
+
+ /* kevent(2) */
+ struct kevent ev;
+ int kq;
+
+ ATF_REQUIRE((kq = kqueue()) != -1);
+ EV_SET(&ev, fd, EVFILT_READ, EV_ADD, NOTE_LOWAT, 41, NULL);
+ ATF_REQUIRE(kevent(kq, &ev, 1, NULL, 0, NULL) == 0);
+ ATF_REQUIRE(kevent(kq, NULL, 0, &ev, 1, NULL) == 1);
+ ATF_REQUIRE(ev.filter == EVFILT_READ);
+ ATF_REQUIRE(ev.data == 42);
+
+ /* aio(4) */
+ char buf[50];
+ struct aiocb aio = {
+ .aio_nbytes = 50,
+ .aio_fildes = fd,
+ .aio_buf = buf,
+ }, *aiop;
+
+ ATF_REQUIRE(aio_read(&aio) == 0);
+ ATF_REQUIRE(aio_waitcomplete(&aiop, NULL) == 42);
+ ATF_REQUIRE(aiop == &aio);
+}
+
+/*
+ * Send data and control in connected & unconnected mode and check that
+ * various event mechanisms see the data, but don't count control bytes.
+ */
+ATF_TC_WITHOUT_HEAD(event);
+ATF_TC_BODY(event, tc)
+{
+ int fd[2];
+ char buf[50];
+ struct iovec iov = {
+ .iov_base = buf,
+ .iov_len = 42,
+ };
+ struct cmsghdr cmsg = {
+ .cmsg_len = CMSG_LEN(0),
+ .cmsg_level = SOL_SOCKET,
+ .cmsg_type = SCM_TIMESTAMP,
+ };
+ struct msghdr msghdr = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = &cmsg,
+ .msg_controllen = CMSG_LEN(0),
+ };
+
+ /* Connected socket */
+ ATF_REQUIRE(socketpair(PF_UNIX, SOCK_DGRAM, 0, fd) != -1);
+ ATF_REQUIRE(sendmsg(fd[0], &msghdr, 0) == 42);
+ test42(fd[1]);
+ close(fd[0]);
+ close(fd[1]);
+
+ /* Not-connected send */
+ ATF_REQUIRE((fd[0] = socket(PF_UNIX, SOCK_DGRAM, 0)) > 0);
+ ATF_REQUIRE((fd[1] = socket(PF_UNIX, SOCK_DGRAM, 0)) > 0);
+ ATF_REQUIRE(bind(fd[0], (struct sockaddr *)&sun, sizeof(sun)) == 0);
+ ATF_REQUIRE(sendto(fd[1], buf, 42, 0, (struct sockaddr *)&sun,
+ sizeof(sun)) == 42);
+ test42(fd[0]);
+}
+
+ATF_TC_WITHOUT_HEAD(selfgetpeername);
+ATF_TC_BODY(selfgetpeername, tc)
+{
+ struct sockaddr_un sun;
+ const char *name;
+ socklen_t len;
+ int sd;
+
+ name = "selfgetpeername";
+
+ sd = socket(PF_UNIX, SOCK_DGRAM, 0);
+ ATF_REQUIRE(sd != -1);
+
+ memset(&sun, 0, sizeof(sun));
+ sun.sun_len = sizeof(sun);
+ sun.sun_family = AF_UNIX;
+ snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", name);
+ ATF_REQUIRE(bind(sd, (struct sockaddr *)&sun, sizeof(sun)) == 0);
+ ATF_REQUIRE(connect(sd, (struct sockaddr *)&sun, sizeof(sun)) == 0);
+
+ len = sizeof(sun);
+ ATF_REQUIRE(getpeername(sd, (struct sockaddr *)&sun, &len) == 0);
+ ATF_REQUIRE(strcmp(sun.sun_path, name) == 0);
+
+ ATF_REQUIRE(close(sd) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(fchmod);
+ATF_TC_BODY(fchmod, tc)
+{
+ struct stat sb;
+ struct sockaddr_un sun;
+ int error, sd;
+
+ memset(&sun, 0, sizeof(sun));
+ sun.sun_len = sizeof(sun);
+ sun.sun_family = AF_UNIX;
+ strlcpy(sun.sun_path, "sock", sizeof(sun.sun_path));
+
+ sd = socket(PF_UNIX, SOCK_DGRAM, 0);
+ ATF_REQUIRE(sd != -1);
+
+ error = fchmod(sd, 0600 | S_ISUID);
+ ATF_REQUIRE_ERRNO(EINVAL, error == -1);
+
+ umask(0022);
+ error = fchmod(sd, 0766);
+ ATF_REQUIRE(error == 0);
+
+ error = bind(sd, (struct sockaddr *)&sun, sizeof(sun));
+ ATF_REQUIRE(error == 0);
+
+ error = stat(sun.sun_path, &sb);
+ ATF_REQUIRE(error == 0);
+ ATF_REQUIRE_MSG((sb.st_mode & 0777) == 0744,
+ "sb.st_mode = %o", sb.st_mode);
+
+ error = fchmod(sd, 0666);
+ ATF_REQUIRE_ERRNO(EINVAL, error == -1);
+
+ ATF_REQUIRE(close(sd) == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, basic);
+ ATF_TP_ADD_TC(tp, one2many);
+ ATF_TP_ADD_TC(tp, event);
+ ATF_TP_ADD_TC(tp, selfgetpeername);
+ ATF_TP_ADD_TC(tp, fchmod);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/unix_passfd_dgram.c b/tests/sys/kern/unix_passfd_dgram.c
new file mode 100644
index 000000000000..d61b761d0f72
--- /dev/null
+++ b/tests/sys/kern/unix_passfd_dgram.c
@@ -0,0 +1,2 @@
+#define TEST_PROTO SOCK_DGRAM
+#include "unix_passfd_test.c"
diff --git a/tests/sys/kern/unix_passfd_stream.c b/tests/sys/kern/unix_passfd_stream.c
new file mode 100644
index 000000000000..e3612bf8da7e
--- /dev/null
+++ b/tests/sys/kern/unix_passfd_stream.c
@@ -0,0 +1,2 @@
+#define TEST_PROTO SOCK_STREAM
+#include "unix_passfd_test.c"
diff --git a/tests/sys/kern/unix_passfd_test.c b/tests/sys/kern/unix_passfd_test.c
new file mode 100644
index 000000000000..7dc4541ad402
--- /dev/null
+++ b/tests/sys/kern/unix_passfd_test.c
@@ -0,0 +1,1216 @@
+/*-
+ * Copyright (c) 2005 Robert N. M. Watson
+ * Copyright (c) 2015 Mark Johnston
+ * Copyright (c) 2022 Gleb Smirnoff <glebius@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/jail.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <jail.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#if !defined(TEST_PROTO)
+#error Need TEST_PROTO defined to SOCK_STREAM or SOCK_DGRAM
+#endif
+
+/*
+ * UNIX domain sockets allow file descriptors to be passed via "ancillary
+ * data", or control messages. This regression test is intended to exercise
+ * this facility, both performing some basic tests that it operates, and also
+ * causing some kernel edge cases to execute, such as garbage collection when
+ * there are cyclic file descriptor references. Right now we test only with
+ * stream sockets, but ideally we'd also test with datagram sockets.
+ */
+
+static void
+domainsocketpair(int *fdp)
+{
+
+ ATF_REQUIRE_MSG(socketpair(PF_UNIX, TEST_PROTO, 0, fdp) != -1,
+ "socketpair(PF_UNIX, %u) failed: %s", TEST_PROTO, strerror(errno));
+}
+
+static void
+closesocketpair(int *fdp)
+{
+
+ close(fdp[0]);
+ close(fdp[1]);
+}
+
+static void
+devnull(int *fdp)
+{
+ int fd;
+
+ fd = open("/dev/null", O_RDONLY);
+ ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
+ *fdp = fd;
+}
+
+static void
+tempfile(int *fdp)
+{
+ char path[PATH_MAX];
+ int fd;
+
+ snprintf(path, PATH_MAX, "%s/unix_passfd.XXXXXXXXXXXXXXX",
+ getenv("TMPDIR") == NULL ? "/tmp" : getenv("TMPDIR"));
+ fd = mkstemp(path);
+ ATF_REQUIRE_MSG(fd != -1, "mkstemp(%s) failed", path);
+ (void)unlink(path);
+ *fdp = fd;
+}
+
+static void
+dofstat(int fd, struct stat *sb)
+{
+
+ ATF_REQUIRE_MSG(fstat(fd, sb) == 0,
+ "fstat failed: %s", strerror(errno));
+}
+
+static int
+getnfds(void)
+{
+ size_t len;
+ int mib[4], n, rc;
+
+ len = sizeof(n);
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_NFDS;
+ mib[3] = 0;
+
+ rc = sysctl(mib, 4, &n, &len, NULL, 0);
+ ATF_REQUIRE_MSG(rc != -1, "sysctl(KERN_PROC_NFDS) failed");
+ return (n);
+}
+
+static int
+openfiles(void)
+{
+ int files;
+ size_t len = sizeof(files);
+
+ ATF_REQUIRE(sysctlbyname("kern.openfiles", &files, &len, NULL, 0) == 0);
+
+ return (files);
+}
+
+static void
+putfds(char *buf, int fd, int nfds)
+{
+ struct cmsghdr *cm;
+ int *fdp, i;
+
+ cm = (struct cmsghdr *)buf;
+ cm->cmsg_len = CMSG_LEN(nfds * sizeof(int));
+ cm->cmsg_level = SOL_SOCKET;
+ cm->cmsg_type = SCM_RIGHTS;
+ for (fdp = (int *)CMSG_DATA(cm), i = 0; i < nfds; i++)
+ *fdp++ = fd;
+}
+
+static void
+samefile(struct stat *sb1, struct stat *sb2)
+{
+
+ ATF_REQUIRE_MSG(sb1->st_dev == sb2->st_dev, "different device");
+ ATF_REQUIRE_MSG(sb1->st_ino == sb2->st_ino, "different inode");
+}
+
+static ssize_t
+sendfd_payload(int sockfd, int send_fd, void *payload, size_t paylen)
+{
+ struct iovec iovec;
+ char message[CMSG_SPACE(sizeof(int))];
+ struct msghdr msghdr;
+
+ bzero(&msghdr, sizeof(msghdr));
+ bzero(&message, sizeof(message));
+
+ msghdr.msg_control = message;
+ msghdr.msg_controllen = sizeof(message);
+
+ iovec.iov_base = payload;
+ iovec.iov_len = paylen;
+
+ msghdr.msg_iov = &iovec;
+ msghdr.msg_iovlen = 1;
+
+ putfds(message, send_fd, 1);
+ return (sendmsg(sockfd, &msghdr, 0));
+}
+
+static void
+sendfd(int sockfd, int send_fd)
+{
+ ssize_t len;
+ char ch;
+
+ ch = 0;
+ len = sendfd_payload(sockfd, send_fd, &ch, sizeof(ch));
+ ATF_REQUIRE_MSG(len == sizeof(ch),
+ "sendmsg: %zd bytes sent; expected %zu; %s", len, sizeof(ch),
+ strerror(errno));
+}
+
+static bool
+localcreds(int sockfd)
+{
+ socklen_t sz;
+ int rc, val;
+
+ sz = sizeof(val);
+ rc = getsockopt(sockfd, 0, LOCAL_CREDS, &val, &sz);
+ ATF_REQUIRE_MSG(rc != -1, "getsockopt(LOCAL_CREDS) failed: %s",
+ strerror(errno));
+ return (val != 0);
+}
+
+static ssize_t
+recvfd_payload(int sockfd, int *recv_fd, void *buf, size_t buflen,
+ size_t cmsgsz, int recvmsg_flags)
+{
+ struct cmsghdr *cmsghdr;
+ struct msghdr msghdr;
+ struct iovec iovec;
+ char *message;
+ ssize_t len;
+ bool foundcreds;
+
+ bzero(&msghdr, sizeof(msghdr));
+ message = malloc(cmsgsz);
+ ATF_REQUIRE(message != NULL);
+
+ msghdr.msg_control = message;
+ msghdr.msg_controllen = cmsgsz;
+
+ iovec.iov_base = buf;
+ iovec.iov_len = buflen;
+
+ msghdr.msg_iov = &iovec;
+ msghdr.msg_iovlen = 1;
+
+ len = recvmsg(sockfd, &msghdr, recvmsg_flags);
+ ATF_REQUIRE_MSG(len != -1, "recvmsg failed: %s", strerror(errno));
+
+ cmsghdr = CMSG_FIRSTHDR(&msghdr);
+ ATF_REQUIRE_MSG(cmsghdr != NULL,
+ "recvmsg: did not receive control message");
+ foundcreds = false;
+ *recv_fd = -1;
+ for (; cmsghdr != NULL; cmsghdr = CMSG_NXTHDR(&msghdr, cmsghdr)) {
+ if (cmsghdr->cmsg_level == SOL_SOCKET &&
+ cmsghdr->cmsg_type == SCM_RIGHTS &&
+ cmsghdr->cmsg_len == CMSG_LEN(sizeof(int))) {
+ memcpy(recv_fd, CMSG_DATA(cmsghdr), sizeof(int));
+ ATF_REQUIRE(*recv_fd != -1);
+ } else if (cmsghdr->cmsg_level == SOL_SOCKET &&
+ cmsghdr->cmsg_type == SCM_CREDS)
+ foundcreds = true;
+ }
+ ATF_REQUIRE_MSG(*recv_fd != -1,
+ "recvmsg: did not receive single-fd message");
+ ATF_REQUIRE_MSG(!localcreds(sockfd) || foundcreds,
+ "recvmsg: expected credentials were not received");
+ ATF_REQUIRE_MSG((msghdr.msg_flags & MSG_TRUNC) == 0,
+ "recvmsg: MSG_TRUNC is set while buffer is sufficient");
+
+ return (len);
+}
+
+static void
+recvfd(int sockfd, int *recv_fd, int flags)
+{
+ ssize_t len;
+ char ch = 0;
+
+ len = recvfd_payload(sockfd, recv_fd, &ch, sizeof(ch),
+ CMSG_SPACE(sizeof(int)), flags);
+ ATF_REQUIRE_MSG((size_t)len == sizeof(ch),
+ "recvmsg: %zd bytes received; expected %zd", len, sizeof(ch));
+}
+
+#if TEST_PROTO == SOCK_STREAM
+#define LOCAL_SENDSPACE_SYSCTL "net.local.stream.sendspace"
+#define LOCAL_RECVSPACE_SYSCTL "net.local.stream.recvspace"
+#elif TEST_PROTO == SOCK_DGRAM
+#define LOCAL_SENDSPACE_SYSCTL "net.local.dgram.maxdgram"
+#define LOCAL_RECVSPACE_SYSCTL "net.local.dgram.recvspace"
+#endif
+
+static u_long
+getsendspace(void)
+{
+ u_long sendspace;
+
+ ATF_REQUIRE_MSG(sysctlbyname(LOCAL_SENDSPACE_SYSCTL, &sendspace,
+ &(size_t){sizeof(u_long)}, NULL, 0) != -1,
+ "sysctl %s failed: %s", LOCAL_SENDSPACE_SYSCTL, strerror(errno));
+
+ return (sendspace);
+}
+
+static u_long
+getrecvspace(void)
+{
+ u_long recvspace;
+
+ ATF_REQUIRE_MSG(sysctlbyname(LOCAL_RECVSPACE_SYSCTL, &recvspace,
+ &(size_t){sizeof(u_long)}, NULL, 0) != -1,
+ "sysctl %s failed: %s", LOCAL_RECVSPACE_SYSCTL, strerror(errno));
+
+ return (recvspace);
+}
+
+/*
+ * Fill socket to a state when next max sized send would fail with EAGAIN.
+ */
+static void
+fill(int fd)
+{
+ u_long sendspace;
+ void *buf;
+
+ sendspace = getsendspace();
+ ATF_REQUIRE((buf = malloc(sendspace)) != NULL);
+
+ ATF_REQUIRE_MSG(fcntl(fd, F_SETFL, O_NONBLOCK) != -1,
+ "fcntl(O_NONBLOCK) failed: %s", strerror(errno));
+
+#if TEST_PROTO == SOCK_STREAM
+ do {} while (send(fd, buf, sendspace, 0) == (ssize_t)sendspace);
+#elif TEST_PROTO == SOCK_DGRAM
+ u_long recvspace = getrecvspace();
+
+ for (ssize_t sent = 0;
+ sent + sendspace + sizeof(struct sockaddr) < recvspace;
+ sent += sendspace + sizeof(struct sockaddr))
+ ATF_REQUIRE(send(fd, buf, sendspace, 0) == (ssize_t)sendspace);
+#endif
+ free(buf);
+}
+
+/*
+ * Put a temporary file into a UNIX domain socket, then take it out and make
+ * sure it's the same file. First time around, don't close the reference
+ * after sending.
+ */
+ATF_TC_WITHOUT_HEAD(simple_send_fd);
+ATF_TC_BODY(simple_send_fd, tc)
+{
+ struct stat getfd_stat, putfd_stat;
+ int fd[2], getfd, putfd;
+
+ domainsocketpair(fd);
+ tempfile(&putfd);
+ dofstat(putfd, &putfd_stat);
+ sendfd(fd[0], putfd);
+ recvfd(fd[1], &getfd, 0);
+ dofstat(getfd, &getfd_stat);
+ samefile(&putfd_stat, &getfd_stat);
+ close(putfd);
+ close(getfd);
+ closesocketpair(fd);
+}
+
+/*
+ * Like simple_send_fd but also sets MSG_CMSG_CLOEXEC and checks that the
+ * received file descriptor has the FD_CLOEXEC flag set.
+ */
+ATF_TC_WITHOUT_HEAD(simple_send_fd_msg_cmsg_cloexec);
+ATF_TC_BODY(simple_send_fd_msg_cmsg_cloexec, tc)
+{
+ struct stat getfd_stat, putfd_stat;
+ int fd[2], getfd, putfd;
+
+ domainsocketpair(fd);
+ tempfile(&putfd);
+ dofstat(putfd, &putfd_stat);
+ sendfd(fd[0], putfd);
+ recvfd(fd[1], &getfd, MSG_CMSG_CLOEXEC);
+ dofstat(getfd, &getfd_stat);
+ samefile(&putfd_stat, &getfd_stat);
+ ATF_REQUIRE_EQ_MSG(fcntl(getfd, F_GETFD) & FD_CLOEXEC, FD_CLOEXEC,
+ "FD_CLOEXEC not set on the received file descriptor");
+ close(putfd);
+ close(getfd);
+ closesocketpair(fd);
+}
+
+/*
+ * Like simple_send_fd but also sets MSG_CMSG_CLOFORK and checks that the
+ * received file descriptor has the FD_CLOFORK flag set.
+ */
+ATF_TC_WITHOUT_HEAD(simple_send_fd_msg_cmsg_clofork);
+ATF_TC_BODY(simple_send_fd_msg_cmsg_clofork, tc)
+{
+ struct stat getfd_stat, putfd_stat;
+ int fd[2], getfd, putfd;
+
+ domainsocketpair(fd);
+ tempfile(&putfd);
+ dofstat(putfd, &putfd_stat);
+ sendfd(fd[0], putfd);
+ recvfd(fd[1], &getfd, MSG_CMSG_CLOFORK);
+ dofstat(getfd, &getfd_stat);
+ samefile(&putfd_stat, &getfd_stat);
+ ATF_REQUIRE_EQ_MSG(fcntl(getfd, F_GETFD) & FD_CLOFORK, FD_CLOFORK,
+ "FD_CLOFORK not set on the received file descriptor");
+ close(putfd);
+ close(getfd);
+ closesocketpair(fd);
+}
+
+/*
+ * Same as simple_send_fd, only close the file reference after sending, so that
+ * the only reference is the descriptor in the UNIX domain socket buffer.
+ */
+ATF_TC_WITHOUT_HEAD(send_and_close);
+ATF_TC_BODY(send_and_close, tc)
+{
+ struct stat getfd_stat, putfd_stat;
+ int fd[2], getfd, putfd;
+
+ domainsocketpair(fd);
+ tempfile(&putfd);
+ dofstat(putfd, &putfd_stat);
+ sendfd(fd[0], putfd);
+ close(putfd);
+ recvfd(fd[1], &getfd, 0);
+ dofstat(getfd, &getfd_stat);
+ samefile(&putfd_stat, &getfd_stat);
+ close(getfd);
+ closesocketpair(fd);
+}
+
+/*
+ * Put a temporary file into a UNIX domain socket, then close both endpoints
+ * causing garbage collection to kick off.
+ */
+ATF_TC_WITHOUT_HEAD(send_and_cancel);
+ATF_TC_BODY(send_and_cancel, tc)
+{
+ int fd[2], putfd;
+
+ domainsocketpair(fd);
+ tempfile(&putfd);
+ sendfd(fd[0], putfd);
+ close(putfd);
+ closesocketpair(fd);
+}
+
+/*
+ * Send file then shutdown receive side to exercise unp_dispose() call
+ * via soshutdown(). Check that shutdown(SHUT_RD) would gc the file
+ * reference sitting in the receive buffer. There is no good way of
+ * checking that except using global open file count.
+ */
+ATF_TC_WITHOUT_HEAD(send_and_shutdown);
+ATF_TC_BODY(send_and_shutdown, tc)
+{
+ int fd[2], putfd, nfiles;
+
+ domainsocketpair(fd);
+ tempfile(&putfd);
+ sendfd(fd[0], putfd);
+ nfiles = openfiles();
+ close(putfd);
+ ATF_REQUIRE(openfiles() == nfiles);
+ shutdown(fd[1], SHUT_RD);
+ ATF_REQUIRE(openfiles() == nfiles - 1);
+ closesocketpair(fd);
+}
+
+/*
+ * Send maximum possible SCM_RIGHTS message.
+ * Internally the file descriptors are converted from integers to pointers
+ * and stored in a single mbuf cluster. Check that we can not send too much
+ * and that we can successfully send maximum possible amount. Check that we
+ * can not exploit getrlimit(3).
+ */
+#define MAXFDS ((MCLBYTES - _ALIGN(sizeof(struct cmsghdr)))/sizeof(void *))
+ATF_TC_WITHOUT_HEAD(send_a_lot);
+ATF_TC_BODY(send_a_lot, tc)
+{
+ struct msghdr msghdr;
+ struct iovec iov;
+ struct rlimit rlim;
+ int fd[2], nfds;
+ char *cmsg, ch;
+
+ domainsocketpair(fd);
+ cmsg = malloc(CMSG_SPACE((MAXFDS + 1) * sizeof(int)));
+ ATF_REQUIRE(cmsg != NULL);
+ iov.iov_base = &ch;
+ iov.iov_len = sizeof(ch);
+ msghdr = (struct msghdr ){
+ .msg_control = cmsg,
+ .msg_controllen = CMSG_LEN((MAXFDS + 1) * sizeof(int)),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+
+ /* Sending too much fails. */
+ putfds(cmsg, fd[0], MAXFDS + 1);
+ ATF_REQUIRE(sendmsg(fd[0], &msghdr, 0) == -1);
+ ATF_REQUIRE(errno == EMSGSIZE);
+
+ /* Sending just the right amount works and everything is received. */
+ putfds(cmsg, fd[0], MAXFDS);
+ msghdr.msg_controllen = CMSG_LEN(MAXFDS * sizeof(int));
+ ATF_REQUIRE(sendmsg(fd[0], &msghdr, 0) == 1);
+ nfds = getnfds();
+ ATF_REQUIRE(recvmsg(fd[1], &msghdr, 0) == 1);
+ ATF_REQUIRE(getnfds() == (int)(nfds + MAXFDS));
+
+ /* Limit our process open files... */
+ ATF_REQUIRE(getrlimit(RLIMIT_NOFILE, &rlim) == 0);
+ nfds = rlim.rlim_cur = getnfds();
+ ATF_REQUIRE(setrlimit(RLIMIT_NOFILE, &rlim) == 0);
+
+ /* ... and try to receive a single descriptor. */
+ putfds(cmsg, fd[0], 1);
+ msghdr.msg_controllen = CMSG_LEN(sizeof(int));
+ ATF_REQUIRE(sendmsg(fd[0], &msghdr, 0) == 1);
+ ATF_REQUIRE(recvmsg(fd[1], &msghdr, 0) == -1);
+ /* Such attempt shall fail with EMFILE. */
+ ATF_REQUIRE(errno == EMFILE);
+ ATF_REQUIRE(getnfds() == nfds);
+#if TEST_PROTO == SOCK_STREAM
+ /*
+ * For the SOCK_STREAM the above attempt shall free the control in
+ * the kernel, so that socket isn't left in a stuck state. Next read
+ * shall bring us the normal data only. The stream data shall not
+ * miss a byte.
+ */
+ ATF_REQUIRE(recvmsg(fd[1], &msghdr, 0) == 1);
+ ATF_REQUIRE(msghdr.msg_controllen == 0);
+#elif TEST_PROTO == SOCK_DGRAM
+ /*
+ * For SOCK_DGRAM there are two options for the previously failed
+ * syscall: strip the control leaving datagram in the socket or
+ * drop the whole datagram. Our implementation drops the whole
+ * datagram.
+ */
+ ATF_REQUIRE(recvmsg(fd[1], &msghdr, MSG_DONTWAIT) == -1);
+ ATF_REQUIRE(errno == EAGAIN);
+#endif
+}
+
+/*
+ * Exersize condition when SCM_RIGHTS is successfully internalized, but
+ * message delivery fails due to receive buffer overflow. Check that no
+ * file descriptors are leaked.
+ */
+ATF_TC_WITHOUT_HEAD(send_overflow);
+ATF_TC_BODY(send_overflow, tc)
+{
+ void *buf;
+ ssize_t len;
+ int fd[2], putfd, nfiles;
+ int sendspace;
+
+ sendspace = (int)getsendspace();
+ ATF_REQUIRE((buf = malloc(sendspace)) != NULL);
+
+ domainsocketpair(fd);
+ fill(fd[0]);
+ nfiles = openfiles();
+ tempfile(&putfd);
+ len = sendfd_payload(fd[0], putfd, buf, sendspace);
+#if TEST_PROTO == SOCK_STREAM
+ ATF_REQUIRE_MSG(len == -1 && errno == EAGAIN,
+ "sendmsg: %zd bytes sent, errno %d", len, errno);
+#elif TEST_PROTO == SOCK_DGRAM
+ ATF_REQUIRE_MSG(len == -1 && errno == ENOBUFS,
+ "sendmsg: %zd bytes sent, errno %d", len, errno);
+#endif
+ close(putfd);
+ ATF_REQUIRE(nfiles == openfiles());
+ closesocketpair(fd);
+}
+
+/*
+ * Make sure that we do not receive descriptors with MSG_PEEK.
+ */
+ATF_TC_WITHOUT_HEAD(peek);
+ATF_TC_BODY(peek, tc)
+{
+ int fd[2], getfd, putfd, nfds;
+
+ domainsocketpair(fd);
+ tempfile(&putfd);
+ nfds = getnfds();
+ sendfd(fd[0], putfd);
+ ATF_REQUIRE(getnfds() == nfds);
+
+ /* First make MSG_PEEK recvmsg(2)... */
+ char cbuf[CMSG_SPACE(sizeof(int))];
+ char buf[1];
+ struct iovec iov = {
+ .iov_base = buf,
+ .iov_len = sizeof(buf)
+ };
+ struct msghdr msghdr = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = cbuf,
+ .msg_controllen = sizeof(cbuf),
+ };
+ ATF_REQUIRE(1 == recvmsg(fd[1], &msghdr, MSG_PEEK));
+ for (struct cmsghdr *cmsghdr = CMSG_FIRSTHDR(&msghdr);
+ cmsghdr != NULL; cmsghdr = CMSG_NXTHDR(&msghdr, cmsghdr)) {
+ /* Usually this is some garbage. */
+ printf("level %d type %d len %u\n",
+ cmsghdr->cmsg_level, cmsghdr->cmsg_type, cmsghdr->cmsg_len);
+ }
+
+ /* ... and make sure we did not receive any descriptors! */
+ ATF_REQUIRE(getnfds() == nfds);
+
+ /* Now really receive a descriptor. */
+ recvfd(fd[1], &getfd, 0);
+ ATF_REQUIRE(getnfds() == nfds + 1);
+ close(putfd);
+ close(getfd);
+ closesocketpair(fd);
+}
+
+/*
+ * Send two files. Then receive them. Make sure they are returned in the
+ * right order, and both get there.
+ */
+ATF_TC_WITHOUT_HEAD(two_files);
+ATF_TC_BODY(two_files, tc)
+{
+ struct stat getfd_1_stat, getfd_2_stat, putfd_1_stat, putfd_2_stat;
+ int fd[2], getfd_1, getfd_2, putfd_1, putfd_2;
+
+ domainsocketpair(fd);
+ tempfile(&putfd_1);
+ tempfile(&putfd_2);
+ dofstat(putfd_1, &putfd_1_stat);
+ dofstat(putfd_2, &putfd_2_stat);
+ sendfd(fd[0], putfd_1);
+ sendfd(fd[0], putfd_2);
+ close(putfd_1);
+ close(putfd_2);
+ recvfd(fd[1], &getfd_1, 0);
+ recvfd(fd[1], &getfd_2, 0);
+ dofstat(getfd_1, &getfd_1_stat);
+ dofstat(getfd_2, &getfd_2_stat);
+ samefile(&putfd_1_stat, &getfd_1_stat);
+ samefile(&putfd_2_stat, &getfd_2_stat);
+ close(getfd_1);
+ close(getfd_2);
+ closesocketpair(fd);
+}
+
+/*
+ * Big bundling test. Send an endpoint of the UNIX domain socket over itself,
+ * closing the door behind it.
+ */
+ATF_TC_WITHOUT_HEAD(bundle);
+ATF_TC_BODY(bundle, tc)
+{
+ int fd[2], getfd;
+
+ domainsocketpair(fd);
+
+ sendfd(fd[0], fd[0]);
+ close(fd[0]);
+ recvfd(fd[1], &getfd, 0);
+ close(getfd);
+ close(fd[1]);
+}
+
+/*
+ * Big bundling test part two: Send an endpoint of the UNIX domain socket over
+ * itself, close the door behind it, and never remove it from the other end.
+ */
+ATF_TC_WITHOUT_HEAD(bundle_cancel);
+ATF_TC_BODY(bundle_cancel, tc)
+{
+ int fd[2];
+
+ domainsocketpair(fd);
+ sendfd(fd[0], fd[0]);
+ sendfd(fd[1], fd[0]);
+ closesocketpair(fd);
+}
+
+/*
+ * Test for PR 151758: Send an character device over the UNIX domain socket
+ * and then close both sockets to orphan the device.
+ */
+ATF_TC_WITHOUT_HEAD(devfs_orphan);
+ATF_TC_BODY(devfs_orphan, tc)
+{
+ int fd[2], putfd;
+
+ domainsocketpair(fd);
+ devnull(&putfd);
+ sendfd(fd[0], putfd);
+ close(putfd);
+ closesocketpair(fd);
+}
+
+/*
+ * Test for PR 181741. Receiver sets LOCAL_CREDS, and kernel prepends a
+ * control message to the data. Sender sends large payload using a non-blocking
+ * socket. Payload + SCM_RIGHTS + LOCAL_CREDS hit socket buffer limit, and
+ * receiver receives truncated data.
+ */
+ATF_TC_WITHOUT_HEAD(rights_creds_payload);
+ATF_TC_BODY(rights_creds_payload, tc)
+{
+ const int on = 1;
+ u_long sendspace;
+ ssize_t len, rlen;
+ void *buf;
+ int fd[2], getfd, putfd, rc;
+
+ sendspace = getsendspace();
+ buf = calloc(1, sendspace);
+ ATF_REQUIRE(buf != NULL);
+
+ domainsocketpair(fd);
+ tempfile(&putfd);
+
+ rc = fcntl(fd[0], F_SETFL, O_NONBLOCK);
+ ATF_REQUIRE_MSG(rc != -1, "fcntl(O_NONBLOCK) failed: %s",
+ strerror(errno));
+ rc = setsockopt(fd[1], 0, LOCAL_CREDS, &on, sizeof(on));
+ ATF_REQUIRE_MSG(rc != -1, "setsockopt(LOCAL_CREDS) failed: %s",
+ strerror(errno));
+
+ len = sendfd_payload(fd[0], putfd, buf, sendspace);
+ ATF_REQUIRE_MSG(len != -1 , "sendmsg failed: %s", strerror(errno));
+#if TEST_PROTO == SOCK_STREAM
+ ATF_REQUIRE_MSG((size_t)len < sendspace,
+ "sendmsg: %zd bytes sent, expected < %lu", len, sendspace);
+#endif
+#if TEST_PROTO == SOCK_DGRAM
+ /*
+ * sendmsg(2) can't truncate datagrams, only recvmsg(2) can. There are
+ * two options for the kernel here: either accept the datagram with
+ * slight overcommit of the socket buffer space or return ENOBUFS for a
+ * datagram that is smaller or equal to the socket buffer space. Our
+ * implementation does overcommit. Explanation is simple: from our
+ * side we see space available, we have no idea that remote side has
+ * LOCAL_CREDS set. From our side we expect sendmsg(2) to succeed.
+ */
+ ATF_REQUIRE_MSG((size_t)len == sendspace,
+ "sendmsg: %zd bytes sent, expected %lu", len, sendspace);
+#endif
+ rlen = recvfd_payload(fd[1], &getfd, buf, len,
+ CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) + CMSG_SPACE(sizeof(int)), 0);
+ ATF_REQUIRE_MSG(rlen == len,
+ "recvmsg: %zd bytes received; expected %zd", rlen, len);
+
+ close(putfd);
+ close(getfd);
+ closesocketpair(fd);
+}
+
+static void
+send_cmsg(int sockfd, void *cmsg, size_t cmsgsz)
+{
+ struct iovec iov;
+ struct msghdr msghdr;
+ ssize_t len;
+ char ch;
+
+ ch = 0;
+ bzero(&msghdr, sizeof(msghdr));
+
+ iov.iov_base = &ch;
+ iov.iov_len = sizeof(ch);
+ msghdr.msg_control = cmsg;
+ msghdr.msg_controllen = cmsgsz;
+ msghdr.msg_iov = &iov;
+ msghdr.msg_iovlen = 1;
+
+ len = sendmsg(sockfd, &msghdr, 0);
+ ATF_REQUIRE_MSG(len != -1,
+ "sendmsg failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(len == sizeof(ch),
+ "sendmsg: %zd bytes sent; expected %zu", len, sizeof(ch));
+}
+
+static void
+recv_cmsg(int sockfd, char *cmsg, size_t cmsgsz, int flags)
+{
+ struct iovec iov;
+ struct msghdr msghdr;
+ ssize_t len;
+ char ch;
+
+ ch = 0;
+ bzero(&msghdr, sizeof(msghdr));
+
+ iov.iov_base = &ch;
+ iov.iov_len = sizeof(ch);
+ msghdr.msg_control = cmsg;
+ msghdr.msg_controllen = cmsgsz;
+ msghdr.msg_iov = &iov;
+ msghdr.msg_iovlen = 1;
+
+ len = recvmsg(sockfd, &msghdr, 0);
+ ATF_REQUIRE_MSG(len != -1,
+ "recvmsg failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(len == sizeof(ch),
+ "recvmsg: %zd bytes received; expected %zu", len, sizeof(ch));
+ ATF_REQUIRE_MSG((msghdr.msg_flags & flags) == flags,
+ "recvmsg: got flags %#x; expected %#x", msghdr.msg_flags, flags);
+}
+
+/*
+ * Test for PR 131876. Receiver uses a control message buffer that is too
+ * small for the incoming SCM_RIGHTS message, so the message is truncated.
+ * The kernel must not leak the copied right into the receiver's namespace.
+ */
+ATF_TC_WITHOUT_HEAD(truncated_rights);
+ATF_TC_BODY(truncated_rights, tc)
+{
+ char *message;
+ int fd[2], nfds, putfd, rc;
+
+ domainsocketpair(fd);
+ devnull(&putfd);
+ nfds = getnfds();
+
+ /*
+ * Case 1: Send a single descriptor and truncate the message.
+ */
+ message = malloc(CMSG_SPACE(sizeof(int)));
+ ATF_REQUIRE(message != NULL);
+ putfds(message, putfd, 1);
+ send_cmsg(fd[0], message, CMSG_LEN(sizeof(int)));
+ recv_cmsg(fd[1], message, CMSG_LEN(0), MSG_CTRUNC);
+ ATF_REQUIRE(getnfds() == nfds);
+ free(message);
+
+ /*
+ * Case 2a: Send two descriptors in separate messages, and truncate at
+ * the boundary between the two messages. We should still
+ * receive the first message.
+ */
+ message = malloc(CMSG_SPACE(sizeof(int)) * 2);
+ ATF_REQUIRE(message != NULL);
+ putfds(message, putfd, 1);
+ putfds(message + CMSG_SPACE(sizeof(int)), putfd, 1);
+ send_cmsg(fd[0], message, CMSG_SPACE(sizeof(int)) * 2);
+ recv_cmsg(fd[1], message, CMSG_SPACE(sizeof(int)), MSG_CTRUNC);
+ rc = close(*(int *)CMSG_DATA(message));
+ ATF_REQUIRE_MSG(rc == 0, "close failed: %s", strerror(errno));
+ ATF_REQUIRE(getnfds() == nfds);
+ free(message);
+
+ /*
+ * Case 2b: Send two descriptors in separate messages, and truncate
+ * before the end of the first message.
+ */
+ message = malloc(CMSG_SPACE(sizeof(int)) * 2);
+ ATF_REQUIRE(message != NULL);
+ putfds(message, putfd, 1);
+ putfds(message + CMSG_SPACE(sizeof(int)), putfd, 1);
+ send_cmsg(fd[0], message, CMSG_SPACE(sizeof(int)) * 2);
+ recv_cmsg(fd[1], message, CMSG_SPACE(0), MSG_CTRUNC);
+ ATF_REQUIRE(getnfds() == nfds);
+ free(message);
+
+ /*
+ * Case 2c: Send two descriptors in separate messages, and truncate
+ * after the end of the first message. We should still
+ * receive the first message.
+ */
+ message = malloc(CMSG_SPACE(sizeof(int)) * 2);
+ ATF_REQUIRE(message != NULL);
+ putfds(message, putfd, 1);
+ putfds(message + CMSG_SPACE(sizeof(int)), putfd, 1);
+ send_cmsg(fd[0], message, CMSG_SPACE(sizeof(int)) * 2);
+ recv_cmsg(fd[1], message, CMSG_SPACE(sizeof(int)) + CMSG_SPACE(0),
+ MSG_CTRUNC);
+ rc = close(*(int *)CMSG_DATA(message));
+ ATF_REQUIRE_MSG(rc == 0, "close failed: %s", strerror(errno));
+ ATF_REQUIRE(getnfds() == nfds);
+ free(message);
+
+ /*
+ * Case 3: Send three descriptors in the same message, and leave space
+ * only for the first when receiving the message.
+ */
+ message = malloc(CMSG_SPACE(sizeof(int) * 3));
+ ATF_REQUIRE(message != NULL);
+ putfds(message, putfd, 3);
+ send_cmsg(fd[0], message, CMSG_SPACE(sizeof(int) * 3));
+ recv_cmsg(fd[1], message, CMSG_SPACE(sizeof(int)), MSG_CTRUNC);
+ ATF_REQUIRE(getnfds() == nfds);
+ free(message);
+
+ close(putfd);
+ closesocketpair(fd);
+}
+
+/*
+ * Ensure that an attempt to copy a SCM_RIGHTS message to the recipient
+ * fails. In this case the kernel must dispose of the externalized rights
+ * rather than leaking them into the recipient's file descriptor table.
+ */
+ATF_TC_WITHOUT_HEAD(copyout_rights_error);
+ATF_TC_BODY(copyout_rights_error, tc)
+{
+ struct iovec iovec;
+ struct msghdr msghdr;
+ char buf[16];
+ ssize_t len;
+ int fd[2], error, nfds, putfd;
+
+ memset(buf, 0, sizeof(buf));
+ domainsocketpair(fd);
+ devnull(&putfd);
+ nfds = getnfds();
+
+ len = sendfd_payload(fd[0], putfd, buf, sizeof(buf));
+ ATF_REQUIRE_MSG(len != -1, "sendmsg failed: %s", strerror(errno));
+
+ bzero(&msghdr, sizeof(msghdr));
+
+ iovec.iov_base = buf;
+ iovec.iov_len = sizeof(buf);
+ msghdr.msg_control = (char *)-1; /* trigger EFAULT */
+ msghdr.msg_controllen = CMSG_SPACE(sizeof(int));
+ msghdr.msg_iov = &iovec;
+ msghdr.msg_iovlen = 1;
+
+ len = recvmsg(fd[1], &msghdr, 0);
+ error = errno;
+ ATF_REQUIRE_MSG(len == -1, "recvmsg succeeded: %zd", len);
+ ATF_REQUIRE_MSG(errno == EFAULT, "expected EFAULT, got %d (%s)",
+ error, strerror(errno));
+
+ /* Verify that no FDs were leaked. */
+ ATF_REQUIRE(getnfds() == nfds);
+
+ close(putfd);
+ closesocketpair(fd);
+}
+
+/*
+ * Verify that we can handle empty rights messages.
+ */
+ATF_TC_WITHOUT_HEAD(empty_rights_message);
+ATF_TC_BODY(empty_rights_message, tc)
+{
+ struct iovec iov;
+ struct msghdr msghdr;
+ struct cmsghdr cmsg;
+ char *cm, message[CMSG_SPACE(0) + CMSG_SPACE(sizeof(int))];
+ ssize_t len;
+ int error, fd[2], putfd;
+
+ domainsocketpair(fd);
+ devnull(&putfd);
+
+ memset(&msghdr, 0, sizeof(msghdr));
+ iov.iov_base = NULL;
+ iov.iov_len = 0;
+ msghdr.msg_iov = &iov;
+ msghdr.msg_iovlen = 1;
+
+ /*
+ * Try sending incorrect empty message. On 64-bit platforms, where
+ * CMSG_SPACE(0) > sizeof(struct cmsghdr), this will exercise
+ * an edge case.
+ */
+ cmsg = (struct cmsghdr ){
+ .cmsg_len = sizeof(struct cmsghdr), /* not CMSG_LEN(0)! */
+ .cmsg_level = SOL_SOCKET,
+ .cmsg_type = SCM_RIGHTS,
+ };
+ msghdr.msg_control = &cmsg;
+ msghdr.msg_controllen = CMSG_SPACE(0);
+
+ len = sendmsg(fd[0], &msghdr, 0);
+ if (CMSG_LEN(0) != sizeof(struct cmsghdr))
+ ATF_REQUIRE(len == -1 && errno == EINVAL);
+ else
+ ATF_REQUIRE(len == 0);
+
+ /*
+ * Try sending an empty message followed by a non-empty message.
+ */
+ cm = message;
+ putfds(cm, -1, 0);
+ cm += CMSG_SPACE(0);
+ putfds(cm, putfd, 1);
+ msghdr.msg_control = message;
+ msghdr.msg_controllen = sizeof(message);
+
+ len = sendmsg(fd[0], &msghdr, 0);
+ ATF_REQUIRE_MSG(len == 0, "sendmsg failed: %s", strerror(errno));
+
+ /* Only the non-empty message should be received. */
+ len = recvmsg(fd[1], &msghdr, 0);
+ ATF_REQUIRE_MSG(len == 0, "recvmsg failed: %s", strerror(errno));
+ ATF_REQUIRE(msghdr.msg_controllen == CMSG_SPACE(sizeof(int)));
+ error = close(*(int *)CMSG_DATA(msghdr.msg_control));
+ ATF_REQUIRE_MSG(error == 0, "close failed: %s", strerror(errno));
+
+ /*
+ * Now try sending with the non-empty message before the empty message.
+ */
+ cm = message;
+ putfds(cm, putfd, 1);
+ cm += CMSG_SPACE(sizeof(int));
+ putfds(cm, -1, 0);
+
+ memset(&msghdr, 0, sizeof(msghdr));
+ iov.iov_base = NULL;
+ iov.iov_len = 0;
+ msghdr.msg_control = message;
+ msghdr.msg_controllen = CMSG_SPACE(sizeof(int));
+ msghdr.msg_iov = &iov;
+ msghdr.msg_iovlen = 1;
+
+ len = sendmsg(fd[0], &msghdr, 0);
+ ATF_REQUIRE_MSG(len == 0, "sendmsg failed: %s", strerror(errno));
+
+ /* Only the non-empty message should be received. */
+ len = recvmsg(fd[1], &msghdr, 0);
+ ATF_REQUIRE_MSG(len == 0, "recvmsg failed: %s", strerror(errno));
+ ATF_REQUIRE(msghdr.msg_controllen == CMSG_SPACE(sizeof(int)));
+ error = close(*(int *)CMSG_DATA(msghdr.msg_control));
+ ATF_REQUIRE_MSG(error == 0, "close failed: %s", strerror(errno));
+
+ (void)close(putfd);
+}
+
+/*
+ * Check that sending control creates records in a stream socket, making it
+ * behave like a seqpacket socket. If we stack several control+data writes
+ * on a stream socket, we won't be able to read them all at once, even if we
+ * provide a buffer large enough to receive all at once.
+ *
+ * XXXGL: adding MSG_WAITALL to the recvmsg() flags will make this test stuck.
+ */
+ATF_TC_WITHOUT_HEAD(control_creates_records);
+ATF_TC_BODY(control_creates_records, tc)
+{
+ int fd[2], putfd, getfd;
+ char buf[2];
+ ssize_t rlen;
+
+ domainsocketpair(fd);
+ tempfile(&putfd);
+
+ for (int i = 1; i <= 2; i++)
+ ATF_REQUIRE(sendfd_payload(fd[0], putfd, buf, 1) == 1);
+ ATF_REQUIRE(close(putfd) == 0);
+ for (int i = 1; i <= 2; i++) {
+ rlen = recvfd_payload(fd[1], &getfd, buf, 2,
+ CMSG_SPACE(sizeof(int)) * 2, 0);
+ ATF_REQUIRE_MSG(rlen == 1,
+ "recvmsg: %zd bytes received; expected 1", rlen);
+ ATF_REQUIRE(close(getfd) == 0);
+ }
+ closesocketpair(fd);
+}
+
+ATF_TC_WITH_CLEANUP(cross_jail_dirfd);
+ATF_TC_HEAD(cross_jail_dirfd, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(cross_jail_dirfd, tc)
+{
+ int error, sock[2], jid1, jid2, status;
+ pid_t pid1, pid2;
+
+ domainsocketpair(sock);
+
+ error = mkdir("./a", 0755);
+ ATF_REQUIRE(error == 0);
+ error = mkdir("./b", 0755);
+ ATF_REQUIRE(error == 0);
+ error = mkdir("./c", 0755);
+ ATF_REQUIRE(error == 0);
+ error = mkdir("./a/c", 0755);
+ ATF_REQUIRE(error == 0);
+
+ jid1 = jail_setv(JAIL_CREATE,
+ "name", "passfd_test_cross_jail_dirfd1",
+ "path", "./a",
+ "persist", NULL,
+ NULL);
+ ATF_REQUIRE_MSG(jid1 >= 0, "jail_setv: %s", jail_errmsg);
+
+ jid2 = jail_setv(JAIL_CREATE,
+ "name", "passfd_test_cross_jail_dirfd2",
+ "path", "./b",
+ "persist", NULL,
+ NULL);
+ ATF_REQUIRE_MSG(jid2 >= 0, "jail_setv: %s", jail_errmsg);
+
+ pid1 = fork();
+ ATF_REQUIRE(pid1 >= 0);
+ if (pid1 == 0) {
+ ssize_t len;
+ int dfd, error;
+ char ch;
+
+ error = jail_attach(jid1);
+ if (error != 0)
+ err(1, "jail_attach");
+
+ dfd = open(".", O_RDONLY | O_DIRECTORY);
+ if (dfd < 0)
+ err(1, "open(\".\") in jail %d", jid1);
+
+ ch = 0;
+ len = sendfd_payload(sock[0], dfd, &ch, sizeof(ch));
+ if (len == -1)
+ err(1, "sendmsg");
+
+ _exit(0);
+ }
+
+ pid2 = fork();
+ ATF_REQUIRE(pid2 >= 0);
+ if (pid2 == 0) {
+ ssize_t len;
+ int dfd, dfd2, error, fd;
+ char ch;
+
+ error = jail_attach(jid2);
+ if (error != 0)
+ err(1, "jail_attach");
+
+ /* Get a directory from outside the jail root. */
+ len = recvfd_payload(sock[1], &dfd, &ch, sizeof(ch),
+ CMSG_SPACE(sizeof(int)), 0);
+ if (len == -1)
+ err(1, "recvmsg");
+
+ if ((fcntl(dfd, F_GETFD) & FD_RESOLVE_BENEATH) == 0)
+ errx(1, "dfd does not have FD_RESOLVE_BENEATH set");
+
+ /* Make sure we can't chdir. */
+ error = fchdir(dfd);
+ if (error == 0)
+ errx(1, "fchdir succeeded");
+ if (errno != ENOTCAPABLE)
+ err(1, "fchdir");
+
+ /* Make sure a dotdot access fails. */
+ fd = openat(dfd, "../c", O_RDONLY | O_DIRECTORY);
+ if (fd >= 0)
+ errx(1, "openat(\"../c\") succeeded");
+ if (errno != ENOTCAPABLE)
+ err(1, "openat");
+
+ /* Accesses within the sender's jail root are ok. */
+ fd = openat(dfd, "c", O_RDONLY | O_DIRECTORY);
+ if (fd < 0)
+ err(1, "openat(\"c\")");
+
+ dfd2 = openat(dfd, "", O_EMPTY_PATH | O_RDONLY | O_DIRECTORY);
+ if (dfd2 < 0)
+ err(1, "openat(\"\")");
+ if ((fcntl(dfd2, F_GETFD) & FD_RESOLVE_BENEATH) == 0)
+ errx(1, "dfd2 does not have FD_RESOLVE_BENEATH set");
+
+ _exit(0);
+ }
+
+ error = waitpid(pid1, &status, 0);
+ ATF_REQUIRE(error != -1);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE(WEXITSTATUS(status) == 0);
+ error = waitpid(pid2, &status, 0);
+ ATF_REQUIRE(error != -1);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE(WEXITSTATUS(status) == 0);
+
+ closesocketpair(sock);
+}
+ATF_TC_CLEANUP(cross_jail_dirfd, tc)
+{
+ int jid;
+
+ jid = jail_getid("passfd_test_cross_jail_dirfd1");
+ if (jid >= 0 && jail_remove(jid) != 0)
+ err(1, "jail_remove");
+ jid = jail_getid("passfd_test_cross_jail_dirfd2");
+ if (jid >= 0 && jail_remove(jid) != 0)
+ err(1, "jail_remove");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, simple_send_fd);
+ ATF_TP_ADD_TC(tp, simple_send_fd_msg_cmsg_cloexec);
+ ATF_TP_ADD_TC(tp, simple_send_fd_msg_cmsg_clofork);
+ ATF_TP_ADD_TC(tp, send_and_close);
+ ATF_TP_ADD_TC(tp, send_and_cancel);
+ ATF_TP_ADD_TC(tp, send_and_shutdown);
+ ATF_TP_ADD_TC(tp, send_a_lot);
+ ATF_TP_ADD_TC(tp, send_overflow);
+ ATF_TP_ADD_TC(tp, peek);
+ ATF_TP_ADD_TC(tp, two_files);
+ ATF_TP_ADD_TC(tp, bundle);
+ ATF_TP_ADD_TC(tp, bundle_cancel);
+ ATF_TP_ADD_TC(tp, devfs_orphan);
+ ATF_TP_ADD_TC(tp, rights_creds_payload);
+ ATF_TP_ADD_TC(tp, truncated_rights);
+ ATF_TP_ADD_TC(tp, copyout_rights_error);
+ ATF_TP_ADD_TC(tp, empty_rights_message);
+ ATF_TP_ADD_TC(tp, control_creates_records);
+ ATF_TP_ADD_TC(tp, cross_jail_dirfd);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/kern/unix_seqpacket_test.c b/tests/sys/kern/unix_seqpacket_test.c
new file mode 100644
index 000000000000..b9a6be015241
--- /dev/null
+++ b/tests/sys/kern/unix_seqpacket_test.c
@@ -0,0 +1,1375 @@
+/*-
+ *
+ * Copyright (c) 2024 Gleb Smirnoff <glebius@FreeBSD.org>
+ * Copyright (c) 2014 Spectra Logic Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/un.h>
+
+#include <stdio.h>
+
+#include <atf-c.h>
+
+/*
+ * Helper functions
+ */
+
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+
+static void
+do_socketpair(int *sv)
+{
+ int s;
+
+ s = socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sv);
+ ATF_REQUIRE_EQ(0, s);
+ ATF_REQUIRE(sv[0] >= 0);
+ ATF_REQUIRE(sv[1] >= 0);
+ ATF_REQUIRE(sv[0] != sv[1]);
+}
+
+static void
+do_socketpair_nonblocking(int *sv)
+{
+ int s;
+
+ s = socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sv);
+ ATF_REQUIRE_EQ(0, s);
+ ATF_REQUIRE(sv[0] >= 0);
+ ATF_REQUIRE(sv[1] >= 0);
+ ATF_REQUIRE(sv[0] != sv[1]);
+ ATF_REQUIRE(-1 != fcntl(sv[0], F_SETFL, O_NONBLOCK));
+ ATF_REQUIRE(-1 != fcntl(sv[1], F_SETFL, O_NONBLOCK));
+}
+
+/*
+ * Returns a bound and listening socket.
+ * @return const char* The path to the socket
+ */
+static const struct sockaddr_un *
+mk_listening_socket(int *sv)
+{
+ /* ATF's isolation mechanisms will guarantee uniqueness of this file */
+ static const struct sockaddr_un sun = {
+ .sun_family = AF_LOCAL,
+ .sun_len = sizeof(sun),
+ .sun_path = "sock",
+ };
+ int s, r, l;
+
+ s = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
+ ATF_REQUIRE(s >= 0);
+
+ r = bind(s, (struct sockaddr *)&sun, sizeof(sun));
+ l = listen(s, -1);
+ ATF_CHECK_EQ(0, r);
+ ATF_CHECK_EQ(0, l);
+
+ if (sv != NULL)
+ *sv = s;
+
+ return (&sun);
+}
+
+/*
+ * Returns a pair of sockets made the hard way: bind, listen, connect & accept
+ * @return const char* The path to the socket
+ */
+static const struct sockaddr_un *
+mk_pair_of_sockets(int *sv)
+{
+ const struct sockaddr_un *sun;
+ int s, s2, err, s1;
+
+ sun = mk_listening_socket(&s);
+
+ /* Create the other socket */
+ s2 = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
+ ATF_REQUIRE(s2 >= 0);
+ err = connect(s2, (struct sockaddr *)sun, sizeof(*sun));
+ if (err != 0) {
+ perror("connect");
+ atf_tc_fail("connect(2) failed");
+ }
+
+ /* Accept it */
+ s1 = accept(s, NULL, NULL);
+ if (s1 == -1) {
+ perror("accept");
+ atf_tc_fail("accept(2) failed");
+ }
+
+ sv[0] = s1;
+ sv[1] = s2;
+
+ close(s);
+
+ return (sun);
+}
+
+static volatile sig_atomic_t got_sigpipe = 0;
+static void
+shutdown_send_sigpipe_handler(int __unused x)
+{
+ got_sigpipe = 1;
+}
+
+/*
+ * Parameterized test function bodies
+ */
+static void
+test_eagain(int sndbufsize, int rcvbufsize)
+{
+ int i;
+ int sv[2];
+ const size_t totalsize = (sndbufsize + rcvbufsize) * 2;
+ const size_t pktsize = MIN(sndbufsize, rcvbufsize) / 4;
+ const int numpkts = totalsize / pktsize;
+ char sndbuf[pktsize];
+ ssize_t ssize;
+
+ /* setup the socket pair */
+ do_socketpair_nonblocking(sv);
+ /* Setup the buffers */
+ ATF_REQUIRE_EQ(0, setsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &sndbufsize,
+ sizeof(sndbufsize)));
+ ATF_REQUIRE_EQ(0, setsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rcvbufsize,
+ sizeof(rcvbufsize)));
+
+ bzero(sndbuf, pktsize);
+ /* Send data until we get EAGAIN */
+ for(i=0; i < numpkts; i++) {
+ ssize = send(sv[0], sndbuf, pktsize, MSG_EOR);
+ if (ssize == -1) {
+ if (errno == EAGAIN) {
+ close(sv[0]);
+ close(sv[1]);
+ atf_tc_pass();
+ }
+ else {
+ perror("send");
+ atf_tc_fail("send returned < 0 but not EAGAIN");
+ }
+ }
+ }
+ atf_tc_fail("Never got EAGAIN");
+}
+
+static void
+test_sendrecv_symmetric_buffers(int bufsize, int blocking) {
+ int s;
+ int sv[2];
+ const ssize_t pktsize = bufsize / 2;
+ char sndbuf[pktsize];
+ char recv_buf[pktsize];
+ ssize_t ssize, rsize;
+
+ /* setup the socket pair */
+ if (blocking)
+ do_socketpair(sv);
+ else
+ do_socketpair_nonblocking(sv);
+
+ /* Setup the buffers */
+ s = setsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize));
+ ATF_REQUIRE_EQ(0, s);
+ s = setsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize));
+ ATF_REQUIRE_EQ(0, s);
+
+ /* Fill the send buffer */
+ bzero(sndbuf, pktsize);
+
+ /* send and receive the packet */
+ ssize = send(sv[0], sndbuf, pktsize, MSG_EOR);
+ if (ssize < 0) {
+ perror("send");
+ atf_tc_fail("send returned < 0");
+ }
+ ATF_CHECK_EQ_MSG(pktsize, ssize, "expected %zd=send(...) but got %zd",
+ pktsize, ssize);
+
+ rsize = recv(sv[1], recv_buf, pktsize, MSG_WAITALL);
+ if (rsize < 0) {
+ perror("recv");
+ atf_tc_fail("recv returned < 0");
+ }
+ ATF_CHECK_EQ_MSG(pktsize, rsize, "expected %zd=send(...) but got %zd",
+ pktsize, rsize);
+ close(sv[0]);
+ close(sv[1]);
+}
+
+static void
+test_pipe_simulator(int sndbufsize, int rcvbufsize)
+{
+ int num_sent, num_received;
+ int sv[2];
+ const ssize_t pktsize = MIN(sndbufsize, rcvbufsize) / 4;
+ int numpkts;
+ char sndbuf[pktsize];
+ char rcvbuf[pktsize];
+ char comparebuf[pktsize];
+ ssize_t ssize, rsize;
+ bool currently_sending = true;
+
+ /* setup the socket pair */
+ do_socketpair_nonblocking(sv);
+ /* Setup the buffers */
+ ATF_REQUIRE_EQ(0, setsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &sndbufsize,
+ sizeof(sndbufsize)));
+ ATF_REQUIRE_EQ(0, setsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rcvbufsize,
+ sizeof(rcvbufsize)));
+
+ /* Send a total amount of data comfortably greater than the buffers */
+ numpkts = MAX(sndbufsize, rcvbufsize) * 8 / pktsize;
+ for (num_sent=0, num_received=0;
+ num_sent < numpkts || num_received < numpkts; ) {
+ if (currently_sending && num_sent < numpkts) {
+ /* The simulated sending process */
+ /* fill the buffer */
+ memset(sndbuf, num_sent, pktsize);
+ ssize = send(sv[0], sndbuf, pktsize, MSG_EOR);
+ if (ssize < 0) {
+ if (errno == EAGAIN)
+ currently_sending = false;
+ else {
+ perror("send");
+ atf_tc_fail("send failed");
+ }
+ } else {
+ ATF_CHECK_EQ_MSG(pktsize, ssize,
+ "expected %zd=send(...) but got %zd",
+ pktsize, ssize);
+ num_sent++;
+ }
+ } else {
+ /* The simulated receiving process */
+ rsize = recv(sv[1], rcvbuf, pktsize, MSG_WAITALL);
+ if (rsize < 0) {
+ if (errno == EAGAIN) {
+ currently_sending = true;
+ ATF_REQUIRE_MSG(num_sent < numpkts,
+ "Packets were lost!");
+ }
+ else {
+ perror("recv");
+ atf_tc_fail("recv failed");
+ }
+ } else {
+ ATF_CHECK_EQ_MSG(pktsize, rsize,
+ "expected %zd=recv(...) but got %zd",
+ pktsize, rsize);
+ memset(comparebuf, num_received, pktsize);
+ ATF_CHECK_EQ_MSG(0, memcmp(comparebuf, rcvbuf,
+ pktsize),
+ "Received data miscompare");
+ num_received++;
+ }
+ }
+ }
+ close(sv[0]);
+ close(sv[1]);
+}
+
+typedef struct {
+ ssize_t pktsize;
+ int numpkts;
+ int so;
+} test_pipe_thread_data_t;
+
+static void*
+test_pipe_writer(void* args)
+{
+ test_pipe_thread_data_t* td = args;
+ char sndbuf[td->pktsize];
+ ssize_t ssize;
+ int i;
+
+ for(i=0; i < td->numpkts; i++) {
+ memset(sndbuf, i, td->pktsize);
+ ssize = send(td->so, sndbuf, td->pktsize, MSG_EOR);
+ if (ssize < 0) {
+ perror("send");
+ atf_tc_fail("send returned < 0");
+ }
+ ATF_CHECK_EQ_MSG(td->pktsize, ssize,
+ "expected %zd=send(...) but got %zd",
+ td->pktsize, ssize);
+ }
+ return (0);
+}
+
+static void*
+test_pipe_reader(void* args)
+{
+ test_pipe_thread_data_t* td = args;
+ char rcvbuf[td->pktsize];
+ char comparebuf[td->pktsize];
+ ssize_t rsize;
+ int i, d;
+
+ for(i=0; i < td->numpkts; i++) {
+ memset(comparebuf, i, td->pktsize);
+ rsize = recv(td->so, rcvbuf, td->pktsize, MSG_WAITALL);
+ if (rsize < 0) {
+ perror("recv");
+ atf_tc_fail("recv returned < 0");
+ }
+ ATF_CHECK_EQ_MSG(td->pktsize, rsize,
+ "expected %zd=send(...) but got %zd",
+ td->pktsize, rsize);
+ d = memcmp(comparebuf, rcvbuf, td->pktsize);
+ ATF_CHECK_EQ_MSG(0, d,
+ "Received data miscompare on packet %d", i);
+ }
+ return (0);
+}
+
+
+static void
+test_pipe(int sndbufsize, int rcvbufsize)
+{
+ test_pipe_thread_data_t writer_data, reader_data;
+ pthread_t writer, reader;
+ int sv[2];
+ const size_t pktsize = MIN(sndbufsize, rcvbufsize) / 4;
+ int numpkts;
+
+ /* setup the socket pair */
+ do_socketpair(sv);
+ /* Setup the buffers */
+ ATF_REQUIRE_EQ(0, setsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &sndbufsize,
+ sizeof(sndbufsize)));
+ ATF_REQUIRE_EQ(0, setsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rcvbufsize,
+ sizeof(rcvbufsize)));
+
+ /* Send a total amount of data comfortably greater than the buffers */
+ numpkts = MAX(sndbufsize, rcvbufsize) * 8 / pktsize;
+
+ /* Start the child threads */
+ writer_data.pktsize = pktsize;
+ writer_data.numpkts = numpkts;
+ writer_data.so = sv[0];
+ reader_data.pktsize = pktsize;
+ reader_data.numpkts = numpkts;
+ reader_data.so = sv[1];
+ ATF_REQUIRE_EQ(0, pthread_create(&writer, NULL, test_pipe_writer,
+ (void*)&writer_data));
+ /*
+ * Give the writer time to start writing, and hopefully block, before
+ * starting the reader. This increases the likelihood of the test case
+ * failing due to PR kern/185812
+ */
+ usleep(1000);
+ ATF_REQUIRE_EQ(0, pthread_create(&reader, NULL, test_pipe_reader,
+ (void*)&reader_data));
+
+ /* Join the children */
+ ATF_REQUIRE_EQ(0, pthread_join(writer, NULL));
+ ATF_REQUIRE_EQ(0, pthread_join(reader, NULL));
+ close(sv[0]);
+ close(sv[1]);
+}
+
+
+/*
+ * Test Cases
+ */
+
+/* Create a SEQPACKET socket */
+ATF_TC_WITHOUT_HEAD(create_socket);
+ATF_TC_BODY(create_socket, tc)
+{
+ int s;
+
+ s = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
+ ATF_REQUIRE(s >= 0);
+ close(s);
+}
+
+/* Create SEQPACKET sockets using socketpair(2) */
+ATF_TC_WITHOUT_HEAD(create_socketpair);
+ATF_TC_BODY(create_socketpair, tc)
+{
+ int sv[2];
+ int s;
+
+ s = socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sv);
+ ATF_CHECK_EQ(0, s);
+ ATF_CHECK(sv[0] >= 0);
+ ATF_CHECK(sv[1] >= 0);
+ ATF_CHECK(sv[0] != sv[1]);
+ close(sv[0]);
+ close(sv[1]);
+}
+
+/* Call listen(2) without first calling bind(2). It should fail */
+ATF_TC_WITHOUT_HEAD(listen_unbound);
+ATF_TC_BODY(listen_unbound, tc)
+{
+ int s, r;
+
+ s = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
+ ATF_REQUIRE(s > 0);
+ r = listen(s, -1);
+ /* expect listen to fail since we haven't called bind(2) */
+ ATF_CHECK(r != 0);
+ close(s);
+}
+
+/* Bind the socket to a file */
+ATF_TC_WITHOUT_HEAD(bind);
+ATF_TC_BODY(bind, tc)
+{
+ struct sockaddr_un sun;
+ /* ATF's isolation mechanisms will guarantee uniqueness of this file */
+ const char *path = "sock";
+ int s, r;
+
+ s = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
+ ATF_REQUIRE(s >= 0);
+
+ bzero(&sun, sizeof(sun));
+ sun.sun_family = AF_LOCAL;
+ sun.sun_len = sizeof(sun);
+ strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
+ r = bind(s, (struct sockaddr *)&sun, sizeof(sun));
+ ATF_CHECK_EQ(0, r);
+ close(s);
+}
+
+/* listen(2) a socket that is already bound(2) should succeed */
+ATF_TC_WITHOUT_HEAD(listen_bound);
+ATF_TC_BODY(listen_bound, tc)
+{
+ int s;
+
+ (void)mk_listening_socket(&s);
+ close(s);
+}
+
+/* connect(2) can make a connection */
+ATF_TC_WITHOUT_HEAD(connect);
+ATF_TC_BODY(connect, tc)
+{
+ const struct sockaddr_un *sun;
+ int s, err, s2;
+
+ sun = mk_listening_socket(&s);
+
+ /* Create the other socket */
+ s2 = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
+ ATF_REQUIRE(s2 >= 0);
+ err = connect(s2, (struct sockaddr *)sun, sizeof(*sun));
+ if (err != 0) {
+ perror("connect");
+ atf_tc_fail("connect(2) failed");
+ }
+ close(s);
+ close(s2);
+}
+
+/*
+ * An undocumented feature that we probably want to preserve: sending to
+ * a socket that isn't yet accepted lands data on the socket. It can be
+ * read after accept(2).
+ */
+ATF_TC_WITHOUT_HEAD(send_before_accept);
+ATF_TC_BODY(send_before_accept, tc)
+{
+ const char buf[] = "hello";
+ char repl[sizeof(buf)];
+ const struct sockaddr_un *sun;
+ int l, s, a;
+
+ sun = mk_listening_socket(&l);
+
+ ATF_REQUIRE((s = socket(PF_LOCAL, SOCK_SEQPACKET, 0)) > 0);
+ ATF_REQUIRE(connect(s, (struct sockaddr *)sun, sizeof(*sun)) == 0);
+ ATF_REQUIRE(send(s, &buf, sizeof(buf), 0) == sizeof(buf));
+ ATF_REQUIRE((a = accept(l, NULL, NULL)) != 1);
+ ATF_REQUIRE(recv(a, &repl, sizeof(repl), 0) == sizeof(buf));
+ ATF_REQUIRE(strcmp(buf, repl) == 0);
+ close(l);
+ close(s);
+ close(a);
+}
+
+/*
+ * Test that close(2) of the peer ends in EPIPE when we try to send(2).
+ * Test both normal case as well as a peer that was not accept(2)-ed.
+ */
+static bool sigpipe_received = false;
+static void
+sigpipe_handler(int signo __unused)
+{
+ sigpipe_received = true;
+}
+
+ATF_TC_WITHOUT_HEAD(send_to_closed);
+ATF_TC_BODY(send_to_closed, tc)
+{
+ struct sigaction sa = {
+ .sa_handler = sigpipe_handler,
+ };
+ const struct sockaddr_un *sun;
+ int l, s, a;
+
+ ATF_REQUIRE(sigemptyset(&sa.sa_mask) == 0);
+ ATF_REQUIRE(sigaction(SIGPIPE, &sa, NULL) == 0);
+
+ sun = mk_listening_socket(&l);
+
+ ATF_REQUIRE((s = socket(PF_LOCAL, SOCK_SEQPACKET, 0)) > 0);
+ ATF_REQUIRE(connect(s, (struct sockaddr *)sun, sizeof(*sun)) == 0);
+ ATF_REQUIRE((a = accept(l, NULL, NULL)) != 1);
+ close(a);
+ ATF_REQUIRE(send(s, &s, sizeof(s), 0) == -1);
+ ATF_REQUIRE(errno == EPIPE);
+ ATF_REQUIRE(sigpipe_received == true);
+ close(s);
+
+ ATF_REQUIRE((s = socket(PF_LOCAL, SOCK_SEQPACKET, 0)) > 0);
+ ATF_REQUIRE(connect(s, (struct sockaddr *)sun, sizeof(*sun)) == 0);
+ close(l);
+ sigpipe_received = false;
+ ATF_REQUIRE(send(s, &s, sizeof(s), 0) == -1);
+ ATF_REQUIRE(errno == EPIPE);
+ ATF_REQUIRE(sigpipe_received == true);
+ close(s);
+
+ sa.sa_handler = SIG_DFL;
+ ATF_REQUIRE(sigaction(SIGPIPE, &sa, NULL) == 0);
+}
+
+/* Implied connect is unix/dgram only feature. Fails on stream or seqpacket. */
+ATF_TC_WITHOUT_HEAD(implied_connect);
+ATF_TC_BODY(implied_connect, tc)
+{
+ const struct sockaddr_un *sun;
+ int l, s;
+
+ sun = mk_listening_socket(&l);
+
+ ATF_REQUIRE((s = socket(PF_LOCAL, SOCK_SEQPACKET, 0)) > 0);
+ ATF_REQUIRE(sendto(s, &s, sizeof(s), 0, (struct sockaddr *)sun,
+ sizeof(*sun)) == -1);
+ ATF_REQUIRE(errno == ENOTCONN);
+ close(l);
+ close(s);
+}
+
+/* accept(2) can receive a connection */
+ATF_TC_WITHOUT_HEAD(accept);
+ATF_TC_BODY(accept, tc)
+{
+ int sv[2];
+
+ mk_pair_of_sockets(sv);
+ close(sv[0]);
+ close(sv[1]);
+}
+
+
+/* Set O_NONBLOCK on the socket */
+ATF_TC_WITHOUT_HEAD(fcntl_nonblock);
+ATF_TC_BODY(fcntl_nonblock, tc)
+{
+ int s;
+
+ s = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
+ ATF_REQUIRE(s >= 0);
+ if (fcntl(s, F_SETFL, O_NONBLOCK) == -1) {
+ perror("fcntl");
+ atf_tc_fail("fcntl failed");
+ }
+ close(s);
+}
+
+/* Resize the send and receive buffers */
+ATF_TC_WITHOUT_HEAD(resize_buffers);
+ATF_TC_BODY(resize_buffers, tc)
+{
+ int s;
+ int sndbuf = 12345;
+ int rcvbuf = 23456;
+ int xs, xr;
+ socklen_t sl = sizeof(xs);
+
+ s = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
+ ATF_REQUIRE(s >= 0);
+
+ printf(" Socket Buffer Sizes\n");
+ printf(" | SNDBUF | RCVBUF |\n");
+ ATF_CHECK_EQ(0, getsockopt(s, SOL_SOCKET, SO_SNDBUF, &xs, &sl));
+ ATF_CHECK_EQ(0, getsockopt(s, SOL_SOCKET, SO_RCVBUF, &xr, &sl));
+ printf("Default | %7d | %7d |\n", xs, xr);
+
+ if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) != 0){
+ perror("setsockopt");
+ atf_tc_fail("setsockopt(SO_SNDBUF) failed");
+ }
+ ATF_CHECK_EQ(0, getsockopt(s, SOL_SOCKET, SO_SNDBUF, &xs, &sl));
+ ATF_CHECK_EQ(0, getsockopt(s, SOL_SOCKET, SO_RCVBUF, &xr, &sl));
+ printf("After changing SNDBUF | %7d | %7d |\n", xs, xr);
+
+ if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)) != 0){
+ perror("setsockopt");
+ atf_tc_fail("setsockopt(SO_RCVBUF) failed");
+ }
+ ATF_CHECK_EQ(0, getsockopt(s, SOL_SOCKET, SO_SNDBUF, &xs, &sl));
+ ATF_CHECK_EQ(0, getsockopt(s, SOL_SOCKET, SO_RCVBUF, &xr, &sl));
+ printf("After changing RCVBUF | %7d | %7d |\n", xs, xr);
+ close(s);
+}
+
+/*
+ * Resize the send and receive buffers of a connected socketpair
+ * Print some useful debugging info too
+ */
+ATF_TC_WITHOUT_HEAD(resize_connected_buffers);
+ATF_TC_BODY(resize_connected_buffers, tc)
+{
+ int sv[2];
+ int sndbuf = 12345;
+ int rcvbuf = 23456;
+ int err;
+ int ls, lr, rs, rr;
+ socklen_t sl = sizeof(ls);
+
+ /* setup the socket pair */
+ do_socketpair(sv);
+
+ printf(" Socket Buffer Sizes\n");
+ printf(" | Left Socket | Right Socket |\n");
+ printf(" | SNDBUF | RCVBUF | SNDBUF | RCVBUF |\n");
+ ATF_CHECK_EQ(0, getsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &ls, &sl));
+ ATF_CHECK_EQ(0, getsockopt(sv[0], SOL_SOCKET, SO_RCVBUF, &lr, &sl));
+ ATF_CHECK_EQ(0, getsockopt(sv[1], SOL_SOCKET, SO_SNDBUF, &rs, &sl));
+ ATF_CHECK_EQ(0, getsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rr, &sl));
+ printf("Default | %7d | %7d | %7d | %7d |\n",
+ ls, lr, rs, rr);
+
+ /* Update one side's send buffer */
+ err = setsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));
+ if (err != 0){
+ perror("setsockopt");
+ atf_tc_fail("setsockopt(SO_SNDBUF) failed");
+ }
+
+ ATF_CHECK_EQ(0, getsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &ls, &sl));
+ ATF_CHECK_EQ(0, getsockopt(sv[0], SOL_SOCKET, SO_RCVBUF, &lr, &sl));
+ ATF_CHECK_EQ(0, getsockopt(sv[1], SOL_SOCKET, SO_SNDBUF, &rs, &sl));
+ ATF_CHECK_EQ(0, getsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rr, &sl));
+ printf("After changing Left's SNDBUF | %7d | %7d | %7d | %7d |\n",
+ ls, lr, rs, rr);
+
+ /* Update the same side's receive buffer */
+ err = setsockopt(sv[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
+ if (err != 0){
+ perror("setsockopt");
+ atf_tc_fail("setsockopt(SO_RCVBUF) failed");
+ }
+
+ ATF_CHECK_EQ(0, getsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &ls, &sl));
+ ATF_CHECK_EQ(0, getsockopt(sv[0], SOL_SOCKET, SO_RCVBUF, &lr, &sl));
+ ATF_CHECK_EQ(0, getsockopt(sv[1], SOL_SOCKET, SO_SNDBUF, &rs, &sl));
+ ATF_CHECK_EQ(0, getsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rr, &sl));
+ printf("After changing Left's RCVBUF | %7d | %7d | %7d | %7d |\n",
+ ls, lr, rs, rr);
+ close(sv[0]);
+ close(sv[1]);
+}
+
+
+/* send(2) and recv(2) a single short record */
+ATF_TC_WITHOUT_HEAD(send_recv);
+ATF_TC_BODY(send_recv, tc)
+{
+ int sv[2];
+ const int bufsize = 64;
+ const char *data = "data";
+ char recv_buf[bufsize];
+ ssize_t datalen;
+ ssize_t ssize, rsize;
+
+ /* setup the socket pair */
+ do_socketpair(sv);
+
+ /* send and receive a small packet */
+ datalen = strlen(data) + 1; /* +1 for the null */
+ ssize = send(sv[0], data, datalen, MSG_EOR);
+ if (ssize < 0) {
+ perror("send");
+ atf_tc_fail("send returned < 0");
+ }
+ ATF_CHECK_EQ_MSG(datalen, ssize, "expected %zd=send(...) but got %zd",
+ datalen, ssize);
+
+ rsize = recv(sv[1], recv_buf, bufsize, MSG_WAITALL);
+ ATF_CHECK_EQ(datalen, rsize);
+ close(sv[0]);
+ close(sv[1]);
+}
+
+/* sendto(2) and recvfrom(2) a single short record
+ * According to The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004
+ * Edition, sendto(2) is exactly the same as send(2) on a connection-mode socket
+ *
+ * According to the same spec, not all protocols are required to provide the
+ * source addres in recvfrom(2).
+ */
+ATF_TC_WITHOUT_HEAD(sendto_recvfrom);
+ATF_TC_BODY(sendto_recvfrom, tc)
+{
+#ifdef TEST_SEQ_PACKET_SOURCE_ADDRESS
+ const sockaddr_un *sun;
+#endif
+ struct sockaddr_storage from;
+ int sv[2];
+ const int bufsize = 64;
+ const char *data = "data";
+ char recv_buf[bufsize];
+ ssize_t datalen;
+ ssize_t ssize, rsize;
+ socklen_t fromlen;
+
+ /* setup the socket pair */
+#ifdef TEST_SEQ_PACKET_SOURCE_ADDRESS
+ sun =
+#endif
+ mk_pair_of_sockets(sv);
+
+ /* send and receive a small packet */
+ datalen = strlen(data) + 1; /* +1 for the null */
+ ssize = sendto(sv[0], data, datalen, MSG_EOR, NULL, 0);
+ if (ssize < 0) {
+ perror("send");
+ atf_tc_fail("send returned < 0");
+ }
+ ATF_CHECK_EQ_MSG(datalen, ssize, "expected %zd=send(...) but got %zd",
+ datalen, ssize);
+
+ fromlen = sizeof(from);
+ rsize = recvfrom(sv[1], recv_buf, bufsize, MSG_WAITALL,
+ (struct sockaddr*)&from, &fromlen);
+ if (ssize < 0) {
+ perror("recvfrom");
+ atf_tc_fail("recvfrom returned < 0");
+ }
+ ATF_CHECK_EQ(datalen, rsize);
+
+#ifdef TEST_SEQ_PACKET_SOURCE_ADDRESS
+ /*
+ * FreeBSD does not currently provide the source address for SEQ_PACKET
+ * AF_UNIX sockets, and POSIX does not require it, so these two checks
+ * are disabled. If FreeBSD gains that feature in the future, then
+ * these checks may be reenabled
+ */
+ ATF_CHECK_EQ(PF_LOCAL, from.ss_family);
+ ATF_CHECK_STREQ(sun->sun_path, ((struct sockaddr_un*)&from)->sun_path);
+#endif
+ close(sv[0]);
+ close(sv[1]);
+}
+
+/*
+ * send(2) and recv(2) a single short record with sockets created the
+ * traditional way, involving bind, listen, connect, and accept
+ */
+ATF_TC_WITHOUT_HEAD(send_recv_with_connect);
+ATF_TC_BODY(send_recv_with_connect, tc)
+{
+ int sv[2];
+ const int bufsize = 64;
+ const char *data = "data";
+ char recv_buf[bufsize];
+ ssize_t datalen;
+ ssize_t ssize, rsize;
+
+ mk_pair_of_sockets(sv);
+
+ /* send and receive a small packet */
+ datalen = strlen(data) + 1; /* +1 for the null */
+ ssize = send(sv[0], data, datalen, MSG_EOR);
+ if (ssize < 0) {
+ perror("send");
+ atf_tc_fail("send returned < 0");
+ }
+ ATF_CHECK_EQ_MSG(datalen, ssize, "expected %zd=send(...) but got %zd",
+ datalen, ssize);
+
+ rsize = recv(sv[1], recv_buf, bufsize, MSG_WAITALL);
+ ATF_CHECK_EQ(datalen, rsize);
+ close(sv[0]);
+ close(sv[1]);
+}
+
+/* send(2) should fail on a shutdown socket */
+ATF_TC_WITHOUT_HEAD(shutdown_send);
+ATF_TC_BODY(shutdown_send, tc)
+{
+ const struct sockaddr_un *sun;
+ const char *data = "data";
+ ssize_t datalen, ssize;
+ int s, err, s2;
+
+ sun = mk_listening_socket(&s);
+
+ /* Create the other socket */
+ s2 = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
+ ATF_REQUIRE(s2 >= 0);
+ err = connect(s2, (struct sockaddr *)sun, sizeof(*sun));
+ if (err != 0) {
+ perror("connect");
+ atf_tc_fail("connect(2) failed");
+ }
+
+ ATF_CHECK_EQ(0, shutdown(s2, SHUT_RDWR));
+ datalen = strlen(data) + 1; /* +1 for the null */
+ /* USE MSG_NOSIGNAL so we don't get SIGPIPE */
+ ssize = send(s2, data, datalen, MSG_EOR | MSG_NOSIGNAL);
+ ATF_CHECK_EQ(EPIPE, errno);
+ ATF_CHECK_EQ(-1, ssize);
+ close(s);
+ close(s2);
+}
+
+/* send(2) should cause SIGPIPE on a shutdown socket */
+ATF_TC_WITHOUT_HEAD(shutdown_send_sigpipe);
+ATF_TC_BODY(shutdown_send_sigpipe, tc)
+{
+ const struct sockaddr_un *sun;
+ const char *data = "data";
+ ssize_t datalen;
+ int s, err, s2;
+
+ sun = mk_listening_socket(&s);
+
+ /* Create the other socket */
+ s2 = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
+ ATF_REQUIRE(s2 >= 0);
+ err = connect(s2, (struct sockaddr *)sun, sizeof(*sun));
+ if (err != 0) {
+ perror("connect");
+ atf_tc_fail("connect(2) failed");
+ }
+
+ ATF_CHECK_EQ(0, shutdown(s2, SHUT_RDWR));
+ ATF_REQUIRE(SIG_ERR != signal(SIGPIPE, shutdown_send_sigpipe_handler));
+ datalen = strlen(data) + 1; /* +1 for the null */
+ (void)send(s2, data, datalen, MSG_EOR);
+ ATF_CHECK_EQ(1, got_sigpipe);
+ close(s);
+ close(s2);
+}
+
+/*
+ * https://syzkaller.appspot.com/bug?id=ac94349a29f2efc40e9274239e4ca9b2c473a4e7
+ */
+ATF_TC_WITHOUT_HEAD(shutdown_o_async);
+ATF_TC_BODY(shutdown_o_async, tc)
+{
+ int sv[2];
+
+ do_socketpair(sv);
+
+ ATF_CHECK_EQ(0, fcntl(sv[0], F_SETFL, O_ASYNC));
+ ATF_CHECK_EQ(0, shutdown(sv[0], SHUT_WR));
+ close(sv[0]);
+ close(sv[1]);
+}
+
+/*
+ * If peer had done SHUT_WR on their side, our recv(2) shouldn't block.
+ */
+ATF_TC_WITHOUT_HEAD(shutdown_recv);
+ATF_TC_BODY(shutdown_recv, tc)
+{
+ char buf[10];
+ int sv[2];
+
+ do_socketpair(sv);
+ ATF_CHECK_EQ(0, shutdown(sv[0], SHUT_WR));
+ ATF_CHECK_EQ(0, recv(sv[1], buf, sizeof(buf), 0));
+ close(sv[0]);
+ close(sv[1]);
+}
+
+/* nonblocking send(2) and recv(2) a single short record */
+ATF_TC_WITHOUT_HEAD(send_recv_nonblocking);
+ATF_TC_BODY(send_recv_nonblocking, tc)
+{
+ int sv[2];
+ const int bufsize = 64;
+ const char *data = "data";
+ char recv_buf[bufsize];
+ ssize_t datalen;
+ ssize_t ssize, rsize;
+
+ /* setup the socket pair */
+ do_socketpair_nonblocking(sv);
+
+ /* Verify that there is nothing to receive */
+ rsize = recv(sv[1], recv_buf, bufsize, MSG_WAITALL);
+ ATF_CHECK_EQ(EAGAIN, errno);
+ ATF_CHECK_EQ(-1, rsize);
+
+ /* send and receive a small packet */
+ datalen = strlen(data) + 1; /* +1 for the null */
+ ssize = send(sv[0], data, datalen, MSG_EOR);
+ if (ssize < 0) {
+ perror("send");
+ atf_tc_fail("send returned < 0");
+ }
+ ATF_CHECK_EQ_MSG(datalen, ssize, "expected %zd=send(...) but got %zd",
+ datalen, ssize);
+
+ rsize = recv(sv[1], recv_buf, bufsize, MSG_WAITALL);
+ ATF_CHECK_EQ(datalen, rsize);
+ close(sv[0]);
+ close(sv[1]);
+}
+
+/*
+ * We should get EAGAIN if we try to send a message larger than the socket
+ * buffer, with nonblocking sockets. Test with several different sockbuf sizes
+ */
+ATF_TC_WITHOUT_HEAD(eagain_8k_8k);
+ATF_TC_BODY(eagain_8k_8k, tc)
+{
+ test_eagain(8192, 8192);
+}
+ATF_TC_WITHOUT_HEAD(eagain_8k_128k);
+ATF_TC_BODY(eagain_8k_128k, tc)
+{
+ test_eagain(8192, 131072);
+}
+ATF_TC_WITHOUT_HEAD(eagain_128k_8k);
+ATF_TC_BODY(eagain_128k_8k, tc)
+{
+ test_eagain(131072, 8192);
+}
+ATF_TC_WITHOUT_HEAD(eagain_128k_128k);
+ATF_TC_BODY(eagain_128k_128k, tc)
+{
+ test_eagain(131072, 131072);
+}
+
+
+/*
+ * nonblocking send(2) and recv(2) of several records, which should collectively
+ * fill up the send buffer but not the receive buffer
+ */
+ATF_TC_WITHOUT_HEAD(rcvbuf_oversized);
+ATF_TC_BODY(rcvbuf_oversized, tc)
+{
+ int i;
+ int sv[2];
+ const ssize_t pktsize = 1024;
+ const int sndbufsize = 8192;
+ const int rcvbufsize = 131072;
+ const size_t geometric_mean_bufsize = 32768;
+ const int numpkts = geometric_mean_bufsize / pktsize;
+ char sndbuf[pktsize];
+ char recv_buf[pktsize];
+ ssize_t ssize, rsize;
+
+ /* setup the socket pair */
+ do_socketpair_nonblocking(sv);
+ ATF_REQUIRE_EQ(0, setsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &sndbufsize,
+ sizeof(sndbufsize)));
+ ATF_REQUIRE_EQ(0, setsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rcvbufsize,
+ sizeof(rcvbufsize)));
+
+ /*
+ * Send and receive packets that are collectively greater than the send
+ * buffer, but less than the receive buffer
+ */
+ for (i=0; i < numpkts; i++) {
+ /* Fill the buffer */
+ memset(sndbuf, i, pktsize);
+
+ /* send the packet */
+ ssize = send(sv[0], sndbuf, pktsize, MSG_EOR);
+ if (ssize < 0) {
+ perror("send");
+ atf_tc_fail("send returned < 0");
+ }
+ ATF_CHECK_EQ_MSG(pktsize, ssize,
+ "expected %zd=send(...) but got %zd", pktsize, ssize);
+
+ /* Receive it */
+
+ rsize = recv(sv[1], recv_buf, pktsize, MSG_WAITALL);
+ if (rsize < 0) {
+ perror("recv");
+ atf_tc_fail("recv returned < 0");
+ }
+ ATF_CHECK_EQ_MSG(pktsize, rsize,
+ "expected %zd=send(...) but got %zd", pktsize, rsize);
+
+ /* Verify the contents */
+ ATF_CHECK_EQ_MSG(0, memcmp(sndbuf, recv_buf, pktsize),
+ "Received data miscompare");
+ }
+
+ /* Trying to receive again should return EAGAIN */
+ rsize = recv(sv[1], recv_buf, pktsize, MSG_WAITALL);
+ ATF_CHECK_EQ(EAGAIN, errno);
+ ATF_CHECK_EQ(-1, rsize);
+ close(sv[0]);
+ close(sv[1]);
+}
+
+/*
+ * Simulate the behavior of a blocking pipe. The sender will send until his
+ * buffer fills up, then we'll simulate a scheduler switch that will allow the
+ * receiver to read until his buffer empties. Repeat the process until the
+ * transfer is complete.
+ * Repeat the test with multiple send and receive buffer sizes
+ */
+ATF_TC_WITHOUT_HEAD(pipe_simulator_8k_8k);
+ATF_TC_BODY(pipe_simulator_8k_8k, tc)
+{
+ test_pipe_simulator(8192, 8192);
+}
+
+ATF_TC_WITHOUT_HEAD(pipe_simulator_8k_128k);
+ATF_TC_BODY(pipe_simulator_8k_128k, tc)
+{
+ test_pipe_simulator(8192, 131072);
+}
+
+ATF_TC_WITHOUT_HEAD(pipe_simulator_128k_8k);
+ATF_TC_BODY(pipe_simulator_128k_8k, tc)
+{
+ test_pipe_simulator(131072, 8192);
+}
+
+ATF_TC_WITHOUT_HEAD(pipe_simulator_128k_128k);
+ATF_TC_BODY(pipe_simulator_128k_128k, tc)
+{
+ test_pipe_simulator(131072, 131072);
+}
+
+/*
+ * Test blocking I/O by passing data between two threads. The total amount of
+ * data will be >> buffer size to force blocking. Repeat the test with multiple
+ * send and receive buffer sizes
+ */
+ATF_TC_WITHOUT_HEAD(pipe_8k_8k);
+ATF_TC_BODY(pipe_8k_8k, tc)
+{
+ test_pipe(8192, 8192);
+}
+
+ATF_TC_WITHOUT_HEAD(pipe_8k_128k);
+ATF_TC_BODY(pipe_8k_128k, tc)
+{
+ test_pipe(8192, 131072);
+}
+
+ATF_TC_WITHOUT_HEAD(pipe_128k_8k);
+ATF_TC_BODY(pipe_128k_8k, tc)
+{
+ test_pipe(131072, 8192);
+}
+
+ATF_TC_WITHOUT_HEAD(pipe_128k_128k);
+ATF_TC_BODY(pipe_128k_128k, tc)
+{
+ test_pipe(131072, 131072);
+}
+
+
+/*
+ * Test single-packet I/O with and without blocking, with symmetric buffers of
+ * various sizes
+ */
+ATF_TC_WITHOUT_HEAD(sendrecv_8k);
+ATF_TC_BODY(sendrecv_8k, tc)
+{
+ test_sendrecv_symmetric_buffers(8 * 1024, true);
+}
+ATF_TC_WITHOUT_HEAD(sendrecv_16k);
+ATF_TC_BODY(sendrecv_16k, tc)
+{
+ test_sendrecv_symmetric_buffers(16 * 1024, true);
+}
+ATF_TC_WITHOUT_HEAD(sendrecv_32k);
+ATF_TC_BODY(sendrecv_32k, tc)
+{
+ test_sendrecv_symmetric_buffers(32 * 1024, true);
+}
+ATF_TC_WITHOUT_HEAD(sendrecv_64k);
+ATF_TC_BODY(sendrecv_64k, tc)
+{
+ test_sendrecv_symmetric_buffers(64 * 1024, true);
+}
+ATF_TC_WITHOUT_HEAD(sendrecv_128k);
+ATF_TC_BODY(sendrecv_128k, tc)
+{
+ test_sendrecv_symmetric_buffers(128 * 1024, true);
+}
+ATF_TC_WITHOUT_HEAD(sendrecv_8k_nonblocking);
+ATF_TC_BODY(sendrecv_8k_nonblocking, tc)
+{
+ test_sendrecv_symmetric_buffers(8 * 1024, false);
+}
+ATF_TC_WITHOUT_HEAD(sendrecv_16k_nonblocking);
+ATF_TC_BODY(sendrecv_16k_nonblocking, tc)
+{
+ test_sendrecv_symmetric_buffers(16 * 1024, false);
+}
+ATF_TC_WITHOUT_HEAD(sendrecv_32k_nonblocking);
+ATF_TC_BODY(sendrecv_32k_nonblocking, tc)
+{
+ test_sendrecv_symmetric_buffers(32 * 1024, false);
+}
+ATF_TC_WITHOUT_HEAD(sendrecv_64k_nonblocking);
+ATF_TC_BODY(sendrecv_64k_nonblocking, tc)
+{
+ test_sendrecv_symmetric_buffers(64 * 1024, false);
+}
+ATF_TC_WITHOUT_HEAD(sendrecv_128k_nonblocking);
+ATF_TC_BODY(sendrecv_128k_nonblocking, tc)
+{
+ test_sendrecv_symmetric_buffers(128 * 1024, false);
+}
+
+ATF_TC(random_eor_and_waitall);
+ATF_TC_HEAD(random_eor_and_waitall, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test random sized send/recv with "
+ "randomly placed MSG_EOR and randomly applied MSG_WAITALL on "
+ "PF_UNIX/SOCK_SEQPACKET");
+}
+
+struct random_eor_params {
+ u_long recvspace;
+ char *sendbuf;
+ size_t *records;
+ u_int nrecords;
+ int sock;
+ u_short seed[6];
+};
+
+#define RANDOM_TESTSIZE ((size_t)100 * 1024 * 1024)
+/* Below defines are factor of recvspace. */
+#define RANDOM_MAXRECORD 10
+#define RANDOM_SENDSIZE 2
+#define RANDOM_RECVSIZE 4
+
+static void *
+sending_thread(void *arg)
+{
+ struct random_eor_params *params = arg;
+ size_t off = 0;
+ int eor = 0;
+
+ while (off < RANDOM_TESTSIZE) {
+ ssize_t len;
+ int flags;
+
+ len = nrand48(&params->seed[3]) %
+ (RANDOM_SENDSIZE * params->recvspace);
+ if (off + len >= params->records[eor]) {
+ len = params->records[eor] - off;
+ flags = MSG_EOR;
+ eor++;
+ } else
+ flags = 0;
+ ATF_REQUIRE(send(params->sock, &params->sendbuf[off], len,
+ flags) == len);
+ off += len;
+#ifdef DEBUG
+ printf("send %zd%s\n", off, flags ? " EOR" : "");
+#endif
+ }
+
+ return (NULL);
+}
+
+ATF_TC_BODY(random_eor_and_waitall, tc)
+{
+ struct random_eor_params params;
+ void *recvbuf;
+ pthread_t t;
+ size_t off;
+ int fd[2], eor;
+
+ arc4random_buf(params.seed, sizeof(params.seed));
+ printf("Using seed:");
+ for (u_int i = 0; i < (u_int)sizeof(params.seed)/sizeof(u_short); i++)
+ printf(" 0x%.4x,", params.seed[i]);
+ printf("\n");
+
+ ATF_REQUIRE((params.sendbuf = malloc(RANDOM_TESTSIZE)) != NULL);
+ for (u_int i = 0; i < RANDOM_TESTSIZE / (u_int )sizeof(long); i++)
+ ((long *)params.sendbuf)[i] = nrand48(&params.seed[0]);
+
+ ATF_REQUIRE(sysctlbyname("net.local.seqpacket.recvspace",
+ &params.recvspace, &(size_t){sizeof(u_long)}, NULL, 0) != -1);
+ ATF_REQUIRE((recvbuf =
+ malloc(RANDOM_RECVSIZE * params.recvspace)) != NULL);
+
+ params.nrecords = 2 * RANDOM_TESTSIZE /
+ (RANDOM_MAXRECORD * params.recvspace);
+
+ ATF_REQUIRE((params.records =
+ malloc(params.nrecords * sizeof(size_t *))) != NULL);
+ off = 0;
+ for (u_int i = 0; i < params.nrecords; i++) {
+ off += 1 + nrand48(&params.seed[0]) %
+ (RANDOM_MAXRECORD * params.recvspace);
+ if (off > RANDOM_TESTSIZE) {
+ params.nrecords = i;
+ break;
+ }
+ params.records[i] = off;
+ }
+ params.records[params.nrecords - 1] = RANDOM_TESTSIZE;
+
+ ATF_REQUIRE(socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, fd) == 0);
+ params.sock = fd[0];
+ ATF_REQUIRE(pthread_create(&t, NULL, sending_thread, &params) == 0);
+
+ off = 0;
+ eor = 0;
+ while (off < RANDOM_TESTSIZE) {
+ struct iovec iov = {
+ .iov_base = recvbuf,
+ .iov_len = nrand48(&params.seed[0]) %
+ (RANDOM_RECVSIZE * params.recvspace)
+ };
+ struct msghdr hdr = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ size_t len;
+ int waitall = iov.iov_len & 0x1 ? MSG_WAITALL : 0;
+
+ len = recvmsg(fd[1], &hdr, waitall);
+ if (waitall && !(hdr.msg_flags & MSG_EOR))
+ ATF_CHECK_EQ_MSG(len, iov.iov_len,
+ "recvmsg(MSG_WAITALL): %zd, expected %zd",
+ len, iov.iov_len);
+ if (off + len == params.records[eor]) {
+ ATF_REQUIRE_MSG(hdr.msg_flags & MSG_EOR,
+ "recvmsg(): expected EOR @ %zd", off + len);
+ eor++;
+ } else {
+ ATF_REQUIRE_MSG(off + len < params.records[eor],
+ "recvmsg() past EOR: %zd, expected %zd",
+ off + len, params.records[eor]);
+ ATF_REQUIRE_MSG(!(hdr.msg_flags & MSG_EOR),
+ "recvmsg() spurious EOR at %zd, expected %zd",
+ off + len, params.records[eor]);
+ }
+ ATF_REQUIRE_MSG(0 == memcmp(params.sendbuf + off, recvbuf, len),
+ "data corruption past %zd", off);
+ off += len;
+#ifdef DEBUG
+ printf("recv %zd%s %zd/%zd%s\n", off,
+ (hdr.msg_flags & MSG_EOR) ? " EOR" : "",
+ len, iov.iov_len,
+ waitall ? " WAITALL" : "");
+#endif
+ }
+
+ ATF_REQUIRE(pthread_join(t, NULL) == 0);
+ free(params.sendbuf);
+ free(recvbuf);
+ free(params.records);
+}
+
+/*
+ * Main.
+ */
+
+ATF_TP_ADD_TCS(tp)
+{
+ /* Basic creation and connection tests */
+ ATF_TP_ADD_TC(tp, create_socket);
+ ATF_TP_ADD_TC(tp, create_socketpair);
+ ATF_TP_ADD_TC(tp, listen_unbound);
+ ATF_TP_ADD_TC(tp, bind);
+ ATF_TP_ADD_TC(tp, listen_bound);
+ ATF_TP_ADD_TC(tp, connect);
+ ATF_TP_ADD_TC(tp, accept);
+ ATF_TP_ADD_TC(tp, fcntl_nonblock);
+ ATF_TP_ADD_TC(tp, resize_buffers);
+ ATF_TP_ADD_TC(tp, resize_connected_buffers);
+
+ /* Unthreaded I/O tests */
+ ATF_TP_ADD_TC(tp, send_recv);
+ ATF_TP_ADD_TC(tp, send_recv_nonblocking);
+ ATF_TP_ADD_TC(tp, send_recv_with_connect);
+ ATF_TP_ADD_TC(tp, sendto_recvfrom);
+ ATF_TP_ADD_TC(tp, send_before_accept);
+ ATF_TP_ADD_TC(tp, send_to_closed);
+ ATF_TP_ADD_TC(tp, implied_connect);
+ ATF_TP_ADD_TC(tp, shutdown_send);
+ ATF_TP_ADD_TC(tp, shutdown_send_sigpipe);
+ ATF_TP_ADD_TC(tp, shutdown_o_async);
+ ATF_TP_ADD_TC(tp, shutdown_recv);
+ ATF_TP_ADD_TC(tp, eagain_8k_8k);
+ ATF_TP_ADD_TC(tp, eagain_8k_128k);
+ ATF_TP_ADD_TC(tp, eagain_128k_8k);
+ ATF_TP_ADD_TC(tp, eagain_128k_128k);
+ ATF_TP_ADD_TC(tp, sendrecv_8k);
+ ATF_TP_ADD_TC(tp, sendrecv_16k);
+ ATF_TP_ADD_TC(tp, sendrecv_32k);
+ ATF_TP_ADD_TC(tp, sendrecv_64k);
+ ATF_TP_ADD_TC(tp, sendrecv_128k);
+ ATF_TP_ADD_TC(tp, sendrecv_8k_nonblocking);
+ ATF_TP_ADD_TC(tp, sendrecv_16k_nonblocking);
+ ATF_TP_ADD_TC(tp, sendrecv_32k_nonblocking);
+ ATF_TP_ADD_TC(tp, sendrecv_64k_nonblocking);
+ ATF_TP_ADD_TC(tp, sendrecv_128k_nonblocking);
+ ATF_TP_ADD_TC(tp, rcvbuf_oversized);
+ ATF_TP_ADD_TC(tp, pipe_simulator_8k_8k);
+ ATF_TP_ADD_TC(tp, pipe_simulator_8k_128k);
+ ATF_TP_ADD_TC(tp, pipe_simulator_128k_8k);
+ ATF_TP_ADD_TC(tp, pipe_simulator_128k_128k);
+
+ /* Threaded I/O tests with blocking sockets */
+ ATF_TP_ADD_TC(tp, pipe_8k_8k);
+ ATF_TP_ADD_TC(tp, pipe_8k_128k);
+ ATF_TP_ADD_TC(tp, pipe_128k_8k);
+ ATF_TP_ADD_TC(tp, pipe_128k_128k);
+ ATF_TP_ADD_TC(tp, random_eor_and_waitall);
+
+ return atf_no_error();
+}
diff --git a/tests/sys/kern/unix_stream.c b/tests/sys/kern/unix_stream.c
new file mode 100644
index 000000000000..bb811f78f620
--- /dev/null
+++ b/tests/sys/kern/unix_stream.c
@@ -0,0 +1,487 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2018 Alan Somers
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/socket.h>
+#include <sys/event.h>
+#include <sys/select.h>
+#include <sys/sysctl.h>
+#include <sys/un.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <poll.h>
+#include <pthread.h>
+#include <pthread_np.h>
+
+#include <atf-c.h>
+
+static void
+do_socketpair(int *sv)
+{
+ int s;
+
+ s = socketpair(PF_LOCAL, SOCK_STREAM, 0, sv);
+ ATF_REQUIRE_EQ(0, s);
+ ATF_REQUIRE(sv[0] >= 0);
+ ATF_REQUIRE(sv[1] >= 0);
+ ATF_REQUIRE(sv[0] != sv[1]);
+}
+
+static u_long
+getsendspace(void)
+{
+ u_long sendspace;
+
+ ATF_REQUIRE_MSG(sysctlbyname("net.local.stream.sendspace", &sendspace,
+ &(size_t){sizeof(u_long)}, NULL, 0) != -1,
+ "sysctl net.local.stream.sendspace failed: %s", strerror(errno));
+
+ return (sendspace);
+}
+
+/* getpeereid(3) should work with stream sockets created via socketpair(2) */
+ATF_TC_WITHOUT_HEAD(getpeereid);
+ATF_TC_BODY(getpeereid, tc)
+{
+ int sv[2];
+ uid_t real_euid, euid;
+ gid_t real_egid, egid;
+
+ real_euid = geteuid();
+ real_egid = getegid();
+
+ do_socketpair(sv);
+
+ ATF_REQUIRE_EQ(0, getpeereid(sv[0], &euid, &egid));
+ ATF_CHECK_EQ(real_euid, euid);
+ ATF_CHECK_EQ(real_egid, egid);
+
+ ATF_REQUIRE_EQ(0, getpeereid(sv[1], &euid, &egid));
+ ATF_CHECK_EQ(real_euid, euid);
+ ATF_CHECK_EQ(real_egid, egid);
+
+ close(sv[0]);
+ close(sv[1]);
+}
+
+/* Sending zero bytes should succeed (once regressed in aba79b0f4a3f). */
+ATF_TC_WITHOUT_HEAD(send_0);
+ATF_TC_BODY(send_0, tc)
+{
+ int sv[2];
+
+ do_socketpair(sv);
+ ATF_REQUIRE(send(sv[0], sv, 0, 0) == 0);
+ close(sv[0]);
+ close(sv[1]);
+}
+
+struct check_ctx;
+typedef void check_func_t(struct check_ctx *);
+struct check_ctx {
+ check_func_t *method;
+ int sv[2];
+ bool timeout;
+ union {
+ enum { SELECT_RD, SELECT_WR } select_what;
+ short poll_events;
+ short kev_filter;
+ };
+ int nfds;
+ union {
+ short poll_revents;
+ unsigned short kev_flags;
+ };
+};
+
+static void
+check_select(struct check_ctx *ctx)
+{
+ fd_set fds;
+ int nfds;
+
+ FD_ZERO(&fds);
+ FD_SET(ctx->sv[0], &fds);
+ nfds = select(ctx->sv[0] + 1,
+ ctx->select_what == SELECT_RD ? &fds : NULL,
+ ctx->select_what == SELECT_WR ? &fds : NULL,
+ NULL,
+ ctx->timeout ? &(struct timeval){.tv_usec = 1000} : NULL);
+ ATF_REQUIRE_MSG(nfds == ctx->nfds,
+ "select() returns %d errno %d", nfds, errno);
+}
+
+static void
+check_poll(struct check_ctx *ctx)
+{
+ struct pollfd pfd[1];
+ int nfds;
+
+ pfd[0] = (struct pollfd){
+ .fd = ctx->sv[0],
+ .events = ctx->poll_events,
+ };
+ nfds = poll(pfd, 1, ctx->timeout ? 1 : INFTIM);
+ ATF_REQUIRE_MSG(nfds == ctx->nfds,
+ "poll() returns %d errno %d", nfds, errno);
+ ATF_REQUIRE((pfd[0].revents & ctx->poll_revents) == ctx->poll_revents);
+}
+
+static void
+check_kevent(struct check_ctx *ctx)
+{
+ struct kevent kev;
+ int nfds, kq;
+
+ ATF_REQUIRE(kq = kqueue());
+ EV_SET(&kev, ctx->sv[0], ctx->kev_filter, EV_ADD, 0, 0, NULL);
+ nfds = kevent(kq, &kev, 1, NULL, 0, NULL);
+ ATF_REQUIRE_MSG(nfds == 0,
+ "kevent() returns %d errno %d", nfds, errno);
+ nfds = kevent(kq, NULL, 0, &kev, 1, ctx->timeout ?
+ &(struct timespec){.tv_nsec = 1000000} : NULL);
+ ATF_REQUIRE_MSG(nfds == ctx->nfds,
+ "kevent() returns %d errno %d", nfds, errno);
+ ATF_REQUIRE(kev.ident == (uintptr_t)ctx->sv[0] &&
+ kev.filter == ctx->kev_filter &&
+ (kev.flags & ctx->kev_flags) == ctx->kev_flags);
+ close(kq);
+}
+
+static void
+full_socketpair(int *sv)
+{
+ void *buf;
+ u_long sendspace;
+
+ sendspace = getsendspace();
+ ATF_REQUIRE((buf = malloc(sendspace)) != NULL);
+ do_socketpair(sv);
+ ATF_REQUIRE(fcntl(sv[0], F_SETFL, O_NONBLOCK) != -1);
+ do {} while (send(sv[0], buf, sendspace, 0) == (ssize_t)sendspace);
+ ATF_REQUIRE(errno == EAGAIN);
+ ATF_REQUIRE(fcntl(sv[0], F_SETFL, 0) != -1);
+ free(buf);
+}
+
+static void *
+pthread_wrap(void *arg)
+{
+ struct check_ctx *ctx = arg;
+
+ ctx->method(ctx);
+
+ return (NULL);
+}
+
+/*
+ * Launch a thread that would block in event mech and return it.
+ */
+static pthread_t
+pthread_create_blocked(struct check_ctx *ctx)
+{
+ pthread_t thr;
+
+ ctx->timeout = false;
+ ctx->nfds = 1;
+ ATF_REQUIRE(pthread_create(&thr, NULL, pthread_wrap, ctx) == 0);
+
+ /* Sleep a bit to make sure that thread is put to sleep. */
+ usleep(10000);
+ ATF_REQUIRE(pthread_peekjoin_np(thr, NULL) == EBUSY);
+
+ return (thr);
+}
+
+static void
+full_writability_check(struct check_ctx *ctx)
+{
+ pthread_t thr;
+ void *buf;
+ u_long space;
+
+ space = getsendspace() / 2;
+ ATF_REQUIRE((buf = malloc(space)) != NULL);
+
+ /* First check with timeout, expecting 0 fds returned. */
+ ctx->timeout = true;
+ ctx->nfds = 0;
+ ctx->method(ctx);
+
+ thr = pthread_create_blocked(ctx);
+
+ /* Read some data and re-check, the fd is expected to be returned. */
+ ATF_REQUIRE(read(ctx->sv[1], buf, space) == (ssize_t)space);
+
+ /* Now check that thread was successfully woken up and exited. */
+ ATF_REQUIRE(pthread_join(thr, NULL) == 0);
+
+ /* Extra check repeating what joined thread already did. */
+ ctx->method(ctx);
+
+ close(ctx->sv[0]);
+ close(ctx->sv[1]);
+ free(buf);
+}
+
+/*
+ * Make sure that a full socket is not reported as writable by event APIs.
+ */
+ATF_TC_WITHOUT_HEAD(full_writability_select);
+ATF_TC_BODY(full_writability_select, tc)
+{
+ struct check_ctx ctx = {
+ .method = check_select,
+ .select_what = SELECT_WR,
+ };
+
+ full_socketpair(ctx.sv);
+ full_writability_check(&ctx);
+ close(ctx.sv[0]);
+ close(ctx.sv[1]);
+}
+
+ATF_TC_WITHOUT_HEAD(full_writability_poll);
+ATF_TC_BODY(full_writability_poll, tc)
+{
+ struct check_ctx ctx = {
+ .method = check_poll,
+ .poll_events = POLLOUT | POLLWRNORM,
+ };
+
+ full_socketpair(ctx.sv);
+ full_writability_check(&ctx);
+ close(ctx.sv[0]);
+ close(ctx.sv[1]);
+}
+
+ATF_TC_WITHOUT_HEAD(full_writability_kevent);
+ATF_TC_BODY(full_writability_kevent, tc)
+{
+ struct check_ctx ctx = {
+ .method = check_kevent,
+ .kev_filter = EVFILT_WRITE,
+ };
+
+ full_socketpair(ctx.sv);
+ full_writability_check(&ctx);
+ close(ctx.sv[0]);
+ close(ctx.sv[1]);
+}
+
+ATF_TC_WITHOUT_HEAD(connected_writability);
+ATF_TC_BODY(connected_writability, tc)
+{
+ struct check_ctx ctx = {
+ .timeout = true,
+ .nfds = 1,
+ };
+
+ do_socketpair(ctx.sv);
+
+ ctx.select_what = SELECT_WR;
+ check_select(&ctx);
+ ctx.poll_events = POLLOUT | POLLWRNORM;
+ check_poll(&ctx);
+ ctx.kev_filter = EVFILT_WRITE;
+ check_kevent(&ctx);
+
+ close(ctx.sv[0]);
+ close(ctx.sv[1]);
+}
+
+ATF_TC_WITHOUT_HEAD(unconnected_writability);
+ATF_TC_BODY(unconnected_writability, tc)
+{
+ struct check_ctx ctx = {
+ .timeout = true,
+ .nfds = 0,
+ };
+
+ ATF_REQUIRE((ctx.sv[0] = socket(PF_LOCAL, SOCK_STREAM, 0)) > 0);
+
+ ctx.select_what = SELECT_WR;
+ check_select(&ctx);
+ ctx.poll_events = POLLOUT | POLLWRNORM;
+ check_poll(&ctx);
+ ctx.kev_filter = EVFILT_WRITE;
+ check_kevent(&ctx);
+
+ close(ctx.sv[0]);
+}
+
+ATF_TC_WITHOUT_HEAD(peerclosed_writability);
+ATF_TC_BODY(peerclosed_writability, tc)
+{
+ struct check_ctx ctx = {
+ .timeout = false,
+ .nfds = 1,
+ };
+
+ do_socketpair(ctx.sv);
+ close(ctx.sv[1]);
+
+ ctx.select_what = SELECT_WR;
+ check_select(&ctx);
+ ctx.poll_events = POLLOUT | POLLWRNORM;
+ check_poll(&ctx);
+ ctx.kev_filter = EVFILT_WRITE;
+ ctx.kev_flags = EV_EOF;
+ check_kevent(&ctx);
+
+ close(ctx.sv[0]);
+}
+
+ATF_TC_WITHOUT_HEAD(peershutdown_writability);
+ATF_TC_BODY(peershutdown_writability, tc)
+{
+ struct check_ctx ctx = {
+ .timeout = false,
+ .nfds = 1,
+ };
+
+ do_socketpair(ctx.sv);
+ shutdown(ctx.sv[1], SHUT_RD);
+
+ ctx.select_what = SELECT_WR;
+ check_select(&ctx);
+ ctx.poll_events = POLLOUT | POLLWRNORM;
+ check_poll(&ctx);
+ /*
+ * XXXGL: historically unix(4) sockets were not reporting peer's
+ * shutdown(SHUT_RD) as our EV_EOF. The kevent(2) manual page says
+ * "filter will set EV_EOF when the reader disconnects", which is hard
+ * to interpret unambigously. For now leave the historic behavior,
+ * but we may want to change that in uipc_usrreq.c:uipc_filt_sowrite(),
+ * and then this test will also expect EV_EOF in returned flags.
+ */
+ ctx.kev_filter = EVFILT_WRITE;
+ check_kevent(&ctx);
+
+ close(ctx.sv[0]);
+ close(ctx.sv[1]);
+}
+
+ATF_TC_WITHOUT_HEAD(peershutdown_readability);
+ATF_TC_BODY(peershutdown_readability, tc)
+{
+ struct check_ctx ctx = {
+ .timeout = false,
+ .nfds = 1,
+ };
+ ssize_t readsz;
+ char c;
+
+ do_socketpair(ctx.sv);
+ shutdown(ctx.sv[1], SHUT_WR);
+
+ /*
+ * The other side should flag as readable in select(2) to allow it to
+ * read(2) and observe EOF. Ensure that both poll(2) and select(2)
+ * are consistent here.
+ */
+ ctx.select_what = SELECT_RD;
+ check_select(&ctx);
+ ctx.poll_events = POLLIN | POLLRDNORM;
+ check_poll(&ctx);
+
+ /*
+ * Also check that read doesn't block.
+ */
+ readsz = read(ctx.sv[0], &c, sizeof(c));
+ ATF_REQUIRE_INTEQ(0, readsz);
+
+ close(ctx.sv[0]);
+ close(ctx.sv[1]);
+}
+
+static void
+peershutdown_wakeup(struct check_ctx *ctx)
+{
+ pthread_t thr;
+
+ ctx->timeout = false;
+ ctx->nfds = 1;
+
+ do_socketpair(ctx->sv);
+ thr = pthread_create_blocked(ctx);
+ shutdown(ctx->sv[1], SHUT_WR);
+ ATF_REQUIRE(pthread_join(thr, NULL) == 0);
+
+ close(ctx->sv[0]);
+ close(ctx->sv[1]);
+}
+
+ATF_TC_WITHOUT_HEAD(peershutdown_wakeup_select);
+ATF_TC_BODY(peershutdown_wakeup_select, tc)
+{
+ peershutdown_wakeup(&(struct check_ctx){
+ .method = check_select,
+ .select_what = SELECT_RD,
+ });
+}
+
+ATF_TC_WITHOUT_HEAD(peershutdown_wakeup_poll);
+ATF_TC_BODY(peershutdown_wakeup_poll, tc)
+{
+ peershutdown_wakeup(&(struct check_ctx){
+ .method = check_poll,
+ .poll_events = POLLIN | POLLRDNORM | POLLRDHUP,
+ .poll_revents = POLLRDHUP,
+ });
+}
+
+ATF_TC_WITHOUT_HEAD(peershutdown_wakeup_kevent);
+ATF_TC_BODY(peershutdown_wakeup_kevent, tc)
+{
+ peershutdown_wakeup(&(struct check_ctx){
+ .method = check_kevent,
+ .kev_filter = EVFILT_READ,
+ .kev_flags = EV_EOF,
+ });
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, getpeereid);
+ ATF_TP_ADD_TC(tp, send_0);
+ ATF_TP_ADD_TC(tp, connected_writability);
+ ATF_TP_ADD_TC(tp, unconnected_writability);
+ ATF_TP_ADD_TC(tp, full_writability_select);
+ ATF_TP_ADD_TC(tp, full_writability_poll);
+ ATF_TP_ADD_TC(tp, full_writability_kevent);
+ ATF_TP_ADD_TC(tp, peerclosed_writability);
+ ATF_TP_ADD_TC(tp, peershutdown_writability);
+ ATF_TP_ADD_TC(tp, peershutdown_readability);
+ ATF_TP_ADD_TC(tp, peershutdown_wakeup_select);
+ ATF_TP_ADD_TC(tp, peershutdown_wakeup_poll);
+ ATF_TP_ADD_TC(tp, peershutdown_wakeup_kevent);
+
+ return atf_no_error();
+}
diff --git a/tests/sys/kern/waitpid_nohang.c b/tests/sys/kern/waitpid_nohang.c
new file mode 100644
index 000000000000..279eda740f78
--- /dev/null
+++ b/tests/sys/kern/waitpid_nohang.c
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 2016 Jilles Tjoelker
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/wait.h>
+
+#include <atf-c.h>
+#include <signal.h>
+#include <unistd.h>
+
+ATF_TC_WITHOUT_HEAD(waitpid_nohang);
+ATF_TC_BODY(waitpid_nohang, tc)
+{
+ pid_t child, pid;
+ int status, r;
+
+ child = fork();
+ ATF_REQUIRE(child != -1);
+ if (child == 0) {
+ sleep(10);
+ _exit(1);
+ }
+
+ status = 42;
+ pid = waitpid(child, &status, WNOHANG);
+ ATF_REQUIRE(pid == 0);
+ ATF_CHECK(status == 42);
+
+ r = kill(child, SIGTERM);
+ ATF_REQUIRE(r == 0);
+ r = waitid(P_PID, child, NULL, WEXITED | WNOWAIT);
+ ATF_REQUIRE(r == 0);
+
+ status = -1;
+ pid = waitpid(child, &status, WNOHANG);
+ ATF_REQUIRE(pid == child);
+ ATF_CHECK(WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, waitpid_nohang);
+ return (atf_no_error());
+}
diff --git a/tests/sys/kqueue/Makefile b/tests/sys/kqueue/Makefile
new file mode 100644
index 000000000000..37f4f323904d
--- /dev/null
+++ b/tests/sys/kqueue/Makefile
@@ -0,0 +1,21 @@
+TESTSRC= ${SRCTOP}/contrib/netbsd-tests/kernel/kqueue
+
+TESTSDIR= ${TESTSBASE}/sys/kqueue
+BINDIR= ${TESTSDIR}
+
+ATF_TESTS_C+= kqueue_fork \
+ kqueue_peek_signal
+
+NETBSD_ATF_TESTS_C= proc1_test
+# XXX: fails `ke.fflags & NOTE_TRACKERR` invariant
+#NETBSD_ATF_TESTS_C+= proc2_test
+NETBSD_ATF_TESTS_C+= proc3_test
+NETBSD_ATF_TESTS_C+= sig_test
+NETBSD_ATF_TESTS_C+= vnode_test
+
+WARNS?= 3
+
+TESTS_SUBDIRS+= libkqueue
+
+.include <netbsd-tests.test.mk>
+.include <bsd.test.mk>
diff --git a/tests/sys/kqueue/Makefile.depend b/tests/sys/kqueue/Makefile.depend
new file mode 100644
index 000000000000..e89a5c52c82a
--- /dev/null
+++ b/tests/sys/kqueue/Makefile.depend
@@ -0,0 +1,18 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libnetbsd \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/kqueue/Makefile.inc b/tests/sys/kqueue/Makefile.inc
new file mode 100644
index 000000000000..01b5f23410c8
--- /dev/null
+++ b/tests/sys/kqueue/Makefile.inc
@@ -0,0 +1 @@
+.include "../Makefile.inc"
diff --git a/tests/sys/kqueue/kqueue_fork.c b/tests/sys/kqueue/kqueue_fork.c
new file mode 100644
index 000000000000..e4c0412c1980
--- /dev/null
+++ b/tests/sys/kqueue/kqueue_fork.c
@@ -0,0 +1,89 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Andreas Bock <andreas.bock@virtual-arts-software.de>
+ * Copyright (c) 2023 Mark Johnston <markj@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/event.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+/*
+ * A regression test for bugzilla 275286.
+ */
+ATF_TC_WITHOUT_HEAD(shared_table_filt_sig);
+ATF_TC_BODY(shared_table_filt_sig, tc)
+{
+ struct sigaction sa;
+ pid_t pid;
+ int error, status;
+
+ sa.sa_handler = SIG_IGN;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ error = sigaction(SIGINT, &sa, NULL);
+ ATF_REQUIRE(error == 0);
+
+ pid = rfork(RFPROC);
+ ATF_REQUIRE(pid != -1);
+ if (pid == 0) {
+ struct kevent ev;
+ int kq;
+
+ kq = kqueue();
+ if (kq < 0)
+ err(1, "kqueue");
+ EV_SET(&ev, SIGINT, EVFILT_SIGNAL, EV_ADD | EV_ENABLE, 0, 0,
+ NULL);
+ if (kevent(kq, &ev, 1, NULL, 0, NULL) < 0)
+ err(2, "kevent");
+ if (kevent(kq, NULL, 0, &ev, 1, NULL) < 0)
+ err(3, "kevent");
+ _exit(0);
+ }
+
+ /* Wait for the child to block in kevent(). */
+ usleep(100000);
+
+ error = kill(pid, SIGINT);
+ ATF_REQUIRE(error == 0);
+
+ error = waitpid(pid, &status, 0);
+ ATF_REQUIRE(error != -1);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, shared_table_filt_sig);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/kqueue/kqueue_peek_signal.c b/tests/sys/kqueue/kqueue_peek_signal.c
new file mode 100644
index 000000000000..16dbce68a57c
--- /dev/null
+++ b/tests/sys/kqueue/kqueue_peek_signal.c
@@ -0,0 +1,106 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2021 Jan Kokemüller
+ * Copyright 2021 Alex Richardson <arichardson@FreeBSD.org>
+ *
+ * This work was supported by Innovate UK project 105694, "Digital Security by
+ * Design (DSbD) Technology Platform Prototype".
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+#include <sys/event.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <signal.h>
+
+ATF_TC_WITHOUT_HEAD(main);
+
+ATF_TC_BODY(main, tc)
+{
+ int rv;
+
+ sigset_t set;
+ rv = sigemptyset(&set);
+ ATF_REQUIRE_EQ(0, rv);
+ rv = sigaddset(&set, SIGUSR1);
+ ATF_REQUIRE_EQ(0, rv);
+ rv = sigprocmask(SIG_BLOCK, &set, NULL);
+ ATF_REQUIRE_EQ(0, rv);
+
+ int skq = kqueue();
+ ATF_REQUIRE(skq >= 0);
+
+ struct kevent kev;
+ EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, 0);
+ rv = kevent(skq, &kev, 1, NULL, 0, NULL);
+ ATF_REQUIRE_EQ(0, rv);
+
+ int kq = kqueue();
+ ATF_REQUIRE(kq >= 0);
+
+ EV_SET(&kev, skq, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, 0);
+ rv = kevent(kq, &kev, 1, NULL, 0, NULL);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /*
+ * It was previously not guaranteed that sending a signal to self would
+ * be immediately visible in the nested kqueue activation with a zero
+ * timeout. As of https://reviews.freebsd.org/D31858, the kqueue task
+ * queue will be processed in this case, so we are guaranteed to see the
+ * SIGUSR1 here even with a zero timeout. We run the code below in a
+ * loop to make it more likely that older kernels without the fix fail
+ * this test.
+ */
+ for (int i = 0; i < 100; i++) {
+ rv = kill(getpid(), SIGUSR1);
+ ATF_REQUIRE_EQ(0, rv);
+
+ rv = kevent(kq, NULL, 0, &kev, 1, &(struct timespec) { 0, 0 });
+ ATF_REQUIRE_EQ_MSG(1, rv,
+ "Unexpected result %d from kevent() after %d iterations",
+ rv, i);
+ rv = kevent(kq, NULL, 0, &kev, 1, &(struct timespec) { 0, 0 });
+ ATF_REQUIRE_EQ(0, rv);
+
+ rv = kevent(skq, NULL, 0, &kev, 1, &(struct timespec) { 0, 0 });
+ ATF_REQUIRE_EQ(1, rv);
+ rv = kevent(skq, NULL, 0, &kev, 1, &(struct timespec) { 0, 0 });
+ ATF_REQUIRE_EQ(0, rv);
+
+ siginfo_t siginfo;
+ rv = sigtimedwait(&set, &siginfo, &(struct timespec) { 0, 0 });
+ ATF_REQUIRE_EQ(SIGUSR1, rv);
+
+ rv = sigtimedwait(&set, &siginfo, &(struct timespec) { 0, 0 });
+ ATF_REQUIRE_ERRNO(EAGAIN, rv < 0);
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, main);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/kqueue/libkqueue/Makefile b/tests/sys/kqueue/libkqueue/Makefile
new file mode 100644
index 000000000000..31eee2bd7c40
--- /dev/null
+++ b/tests/sys/kqueue/libkqueue/Makefile
@@ -0,0 +1,16 @@
+TESTSDIR= ${TESTSBASE}/sys/kqueue/libkqueue
+BINDIR= ${TESTSDIR}
+
+# libkqueue and test suite by Mark Heily <mark@heily.com>
+PLAIN_TESTS_C= kqueue_test
+
+SRCS.kqueue_test= \
+ main.c \
+ read.c \
+ timer.c \
+ vnode.c \
+ proc.c \
+ signal.c \
+ user.c
+
+.include <bsd.test.mk>
diff --git a/tests/sys/kqueue/libkqueue/Makefile.depend b/tests/sys/kqueue/libkqueue/Makefile.depend
new file mode 100644
index 000000000000..84b8ddd67e34
--- /dev/null
+++ b/tests/sys/kqueue/libkqueue/Makefile.depend
@@ -0,0 +1,16 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/kqueue/libkqueue/common.h b/tests/sys/kqueue/libkqueue/common.h
new file mode 100644
index 000000000000..9a39d10dc71b
--- /dev/null
+++ b/tests/sys/kqueue/libkqueue/common.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2009 Mark Heily <mark@heily.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _COMMON_H
+#define _COMMON_H
+
+#include "config.h" /* Needed for HAVE_* defines */
+
+#if HAVE_ERR_H
+# include <err.h>
+#else
+# define err(rc,msg,...) do { perror(msg); exit(rc); } while (0)
+# define errx(rc,msg,...) do { puts(msg); exit(rc); } while (0)
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <sys/event.h>
+
+extern int vnode_fd;
+extern int kqfd;
+
+char * kevent_to_str(struct kevent *);
+struct kevent * kevent_get(int);
+struct kevent * kevent_get_timeout(int, int);
+
+#define kevent_cmp(a,b) _kevent_cmp(a,b, __FILE__, __LINE__)
+void _kevent_cmp(struct kevent *expected, struct kevent *got, const char *file, int line);
+
+void
+kevent_add(int kqfd, struct kevent *kev,
+ uintptr_t ident,
+ short filter,
+ u_short flags,
+ u_int fflags,
+ intptr_t data,
+ void *udata);
+
+/* DEPRECATED: */
+#define KEV_CMP(kev,_ident,_filter,_flags) do { \
+ if (kev.ident != (_ident) || \
+ kev.filter != (_filter) || \
+ kev.flags != (_flags)) \
+ err(1, "kevent mismatch: got [%d,%d,%d] but expecting [%d,%d,%d]", \
+ (int)_ident, (int)_filter, (int)_flags,\
+ (int)kev.ident, kev.filter, kev.flags);\
+} while (0);
+
+/* Checks if any events are pending, which is an error. */
+#define test_no_kevents() _test_no_kevents(__FILE__, __LINE__)
+void _test_no_kevents(const char *, int);
+void test_no_kevents_quietly(void);
+
+void test_begin(const char *);
+void success(void);
+
+void test_evfilt_read(void);
+void test_evfilt_signal(void);
+void test_evfilt_vnode(void);
+void test_evfilt_timer(void);
+void test_evfilt_proc(void);
+#if HAVE_EVFILT_USER
+void test_evfilt_user(void);
+#endif
+
+#endif /* _COMMON_H */
diff --git a/tests/sys/kqueue/libkqueue/config.h b/tests/sys/kqueue/libkqueue/config.h
new file mode 100644
index 000000000000..e15fb73e55ef
--- /dev/null
+++ b/tests/sys/kqueue/libkqueue/config.h
@@ -0,0 +1,13 @@
+
+#define HAVE_ERR_H 1
+#define HAVE_SYS_EVENT_H 1
+#define HAVE_EV_DISPATCH 1
+#define HAVE_EV_RECEIPT 1
+#undef HAVE_NOTE_TRUNCATE
+#define HAVE_EVFILT_TIMER 1
+#define HAVE_EVFILT_USER 1
+#define WITH_NATIVE_KQUEUE_BUGS 0
+#define PROGRAM "libkqueue-test"
+#define VERSION "0.1"
+#define TARGET "freebsd"
+#define CFLAGS "-g -O0 -Wall -Werror"
diff --git a/tests/sys/kqueue/libkqueue/main.c b/tests/sys/kqueue/libkqueue/main.c
new file mode 100644
index 000000000000..32f059336a5c
--- /dev/null
+++ b/tests/sys/kqueue/libkqueue/main.c
@@ -0,0 +1,368 @@
+/*
+ * Copyright (c) 2009 Mark Heily <mark@heily.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include "config.h"
+#include "common.h"
+
+int kqfd;
+static char *cur_test_id = NULL;
+static int testnum = 1;
+
+/* Checks if any events are pending, which is an error. */
+void
+_test_no_kevents(const char *file, int line)
+{
+ int nfds;
+ struct timespec timeo;
+ struct kevent kev;
+ char *kev_str;
+
+ puts("confirming that there are no events pending");
+ memset(&timeo, 0, sizeof(timeo));
+ nfds = kevent(kqfd, NULL, 0, &kev, 1, &timeo);
+ if (nfds != 0) {
+ printf("\n[%s:%d]: Unexpected event:", file, line);
+ kev_str = kevent_to_str(&kev);
+ puts(kev_str);
+ free(kev_str);
+ errx(1, "%d event(s) pending, but none expected:", nfds);
+ }
+}
+
+/* Checks if any events are pending, which is an error. Do not print
+ * out anything unless events are found.
+*/
+void
+test_no_kevents_quietly(void)
+{
+ int nfds;
+ struct timespec timeo;
+ struct kevent kev;
+ char *kev_str;
+
+ memset(&timeo, 0, sizeof(timeo));
+ nfds = kevent(kqfd, NULL, 0, &kev, 1, &timeo);
+ if (nfds != 0) {
+ puts("\nUnexpected event:");
+ kev_str = kevent_to_str(&kev);
+ puts(kev_str);
+ free(kev_str);
+ errx(1, "%d event(s) pending, but none expected:", nfds);
+ }
+}
+
+/* Retrieve a single kevent */
+struct kevent *
+kevent_get(int fd)
+{
+ int nfds;
+ struct kevent *kev;
+
+ if ((kev = calloc(1, sizeof(*kev))) == NULL)
+ err(1, "out of memory");
+
+ nfds = kevent(fd, NULL, 0, kev, 1, NULL);
+ if (nfds < 1)
+ err(1, "kevent(2)");
+
+ return (kev);
+}
+
+/* Retrieve a single kevent, specifying a maximum time to wait for it. */
+struct kevent *
+kevent_get_timeout(int fd, int seconds)
+{
+ int nfds;
+ struct kevent *kev;
+ struct timespec timeout = {seconds, 0};
+
+ if ((kev = calloc(1, sizeof(*kev))) == NULL)
+ err(1, "out of memory");
+
+ nfds = kevent(fd, NULL, 0, kev, 1, &timeout);
+ if (nfds < 0) {
+ err(1, "kevent(2)");
+ } else if (nfds == 0) {
+ free(kev);
+ kev = NULL;
+ }
+
+ return (kev);
+}
+
+static char *
+kevent_fflags_dump(struct kevent *kev)
+{
+ char *buf;
+
+#define KEVFFL_DUMP(attrib) \
+ if (kev->fflags & attrib) \
+ strncat(buf, #attrib" ", 64);
+
+ if ((buf = calloc(1, 1024)) == NULL)
+ abort();
+
+ /* Not every filter has meaningful fflags */
+ if (kev->filter == EVFILT_PROC) {
+ snprintf(buf, 1024, "fflags = %x (", kev->fflags);
+ KEVFFL_DUMP(NOTE_EXIT);
+ KEVFFL_DUMP(NOTE_FORK);
+ KEVFFL_DUMP(NOTE_EXEC);
+ KEVFFL_DUMP(NOTE_CHILD);
+ KEVFFL_DUMP(NOTE_TRACKERR);
+ KEVFFL_DUMP(NOTE_TRACK);
+ buf[strlen(buf) - 1] = ')';
+ } else if (kev->filter == EVFILT_PROCDESC) {
+ snprintf(buf, 1024, "fflags = %x (", kev->fflags);
+ KEVFFL_DUMP(NOTE_EXIT);
+ KEVFFL_DUMP(NOTE_FORK);
+ KEVFFL_DUMP(NOTE_EXEC);
+ buf[strlen(buf) - 1] = ')';
+ } else if (kev->filter == EVFILT_VNODE) {
+ snprintf(buf, 1024, "fflags = %x (", kev->fflags);
+ KEVFFL_DUMP(NOTE_DELETE);
+ KEVFFL_DUMP(NOTE_WRITE);
+ KEVFFL_DUMP(NOTE_EXTEND);
+#if HAVE_NOTE_TRUNCATE
+ KEVFFL_DUMP(NOTE_TRUNCATE);
+#endif
+ KEVFFL_DUMP(NOTE_ATTRIB);
+ KEVFFL_DUMP(NOTE_LINK);
+ KEVFFL_DUMP(NOTE_RENAME);
+#if HAVE_NOTE_REVOKE
+ KEVFFL_DUMP(NOTE_REVOKE);
+#endif
+ buf[strlen(buf) - 1] = ')';
+ } else {
+ snprintf(buf, 1024, "fflags = %x", kev->fflags);
+ }
+
+ return (buf);
+}
+
+static char *
+kevent_flags_dump(struct kevent *kev)
+{
+ char *buf;
+
+#define KEVFL_DUMP(attrib) \
+ if (kev->flags & attrib) \
+ strncat(buf, #attrib" ", 64);
+
+ if ((buf = calloc(1, 1024)) == NULL)
+ abort();
+
+ snprintf(buf, 1024, "flags = %d (", kev->flags);
+ KEVFL_DUMP(EV_ADD);
+ KEVFL_DUMP(EV_ENABLE);
+ KEVFL_DUMP(EV_DISABLE);
+ KEVFL_DUMP(EV_DELETE);
+ KEVFL_DUMP(EV_ONESHOT);
+ KEVFL_DUMP(EV_CLEAR);
+ KEVFL_DUMP(EV_EOF);
+ KEVFL_DUMP(EV_ERROR);
+#if HAVE_EV_DISPATCH
+ KEVFL_DUMP(EV_DISPATCH);
+#endif
+#if HAVE_EV_RECEIPT
+ KEVFL_DUMP(EV_RECEIPT);
+#endif
+ buf[strlen(buf) - 1] = ')';
+
+ return (buf);
+}
+
+/* Copied from ../kevent.c kevent_dump() and improved */
+char *
+kevent_to_str(struct kevent *kev)
+{
+ char buf[512];
+ char *flags_str = kevent_flags_dump(kev);
+ char *fflags_str = kevent_fflags_dump(kev);
+
+ snprintf(&buf[0], sizeof(buf),
+ "[ident=%ju, filter=%d, %s, %s, data=%jd, udata=%p, "
+ "ext=[%jx %jx %jx %jx]",
+ (uintmax_t) kev->ident,
+ kev->filter,
+ flags_str,
+ fflags_str,
+ (uintmax_t)kev->data,
+ kev->udata,
+ (uintmax_t)kev->ext[0],
+ (uintmax_t)kev->ext[1],
+ (uintmax_t)kev->ext[2],
+ (uintmax_t)kev->ext[3]);
+
+ free(flags_str);
+ free(fflags_str);
+
+ return (strdup(buf));
+}
+
+void
+kevent_add(int fd, struct kevent *kev,
+ uintptr_t ident,
+ short filter,
+ u_short flags,
+ u_int fflags,
+ intptr_t data,
+ void *udata)
+{
+ char *kev_str;
+
+ EV_SET(kev, ident, filter, flags, fflags, data, udata);
+ if (kevent(fd, kev, 1, NULL, 0, NULL) < 0) {
+ kev_str = kevent_to_str(kev);
+ printf("Unable to add the following kevent:\n%s\n",
+ kev_str);
+ free(kev_str);
+ err(1, "kevent(): %s", strerror(errno));
+ }
+}
+
+void
+_kevent_cmp(struct kevent *k1, struct kevent *k2, const char *file, int line)
+{
+ char *kev1_str;
+ char *kev2_str;
+
+/* XXX-
+ Workaround for inconsistent implementation of kevent(2)
+ */
+#ifdef __FreeBSD__
+ if (k1->flags & EV_ADD)
+ k2->flags |= EV_ADD;
+#endif
+ if (k1->ident != k2->ident || k1->filter != k2->filter ||
+ k1->flags != k2->flags || k1->fflags != k2->fflags ||
+ k1->data != k2->data || k1->udata != k2->udata ||
+ k1->ext[0] != k2->ext[0] || k1->ext[1] != k2->ext[1] ||
+ k1->ext[0] != k2->ext[2] || k1->ext[0] != k2->ext[3]) {
+ kev1_str = kevent_to_str(k1);
+ kev2_str = kevent_to_str(k2);
+ printf("[%s:%d]: kevent_cmp: mismatch:\n %s !=\n %s\n",
+ file, line, kev1_str, kev2_str);
+ free(kev1_str);
+ free(kev2_str);
+ abort();
+ }
+}
+
+void
+test_begin(const char *func)
+{
+ if (cur_test_id)
+ free(cur_test_id);
+ cur_test_id = strdup(func);
+ if (!cur_test_id)
+ err(1, "strdup failed");
+
+ printf("\n\nTest %d: %s\n", testnum++, func);
+}
+
+void
+success(void)
+{
+ printf("%-70s %s\n", cur_test_id, "passed");
+ free(cur_test_id);
+ cur_test_id = NULL;
+}
+
+static void
+test_kqueue(void)
+{
+ test_begin("kqueue()");
+ if ((kqfd = kqueue()) < 0)
+ err(1, "kqueue()");
+ test_no_kevents();
+ success();
+}
+
+static void
+test_kqueue_close(void)
+{
+ test_begin("close(kq)");
+ if (close(kqfd) < 0)
+ err(1, "close()");
+ success();
+}
+
+int
+main(int argc, char **argv)
+{
+ int test_proc = 1;
+ int test_socket = 1;
+ int test_signal = 1;
+ int test_vnode = 1;
+ int test_timer = 1;
+#ifdef __FreeBSD__
+ int test_user = 1;
+#else
+ /* XXX-FIXME temporary */
+ int test_user = 0;
+#endif
+
+ while (argc) {
+ if (strcmp(argv[0], "--no-proc") == 0)
+ test_proc = 0;
+ if (strcmp(argv[0], "--no-socket") == 0)
+ test_socket = 0;
+ if (strcmp(argv[0], "--no-timer") == 0)
+ test_timer = 0;
+ if (strcmp(argv[0], "--no-signal") == 0)
+ test_signal = 0;
+ if (strcmp(argv[0], "--no-vnode") == 0)
+ test_vnode = 0;
+ if (strcmp(argv[0], "--no-user") == 0)
+ test_user = 0;
+ argv++;
+ argc--;
+ }
+
+ /*
+ * Some tests fork. If output is fully buffered,
+ * the children inherit some buffered data and flush
+ * it when they exit, causing some data to be printed twice.
+ * Use line buffering to avoid this problem.
+ */
+ setlinebuf(stdout);
+ setlinebuf(stderr);
+
+ test_kqueue();
+ test_kqueue_close();
+
+ if (test_socket)
+ test_evfilt_read();
+ if (test_signal)
+ test_evfilt_signal();
+ if (test_vnode)
+ test_evfilt_vnode();
+#if HAVE_EVFILT_USER
+ if (test_user)
+ test_evfilt_user();
+#endif
+ if (test_timer)
+ test_evfilt_timer();
+ if (test_proc)
+ test_evfilt_proc();
+
+ printf("\n---\n"
+ "+OK All %d tests completed.\n", testnum - 1);
+ return (0);
+}
diff --git a/tests/sys/kqueue/libkqueue/proc.c b/tests/sys/kqueue/libkqueue/proc.c
new file mode 100644
index 000000000000..85d306aae5ef
--- /dev/null
+++ b/tests/sys/kqueue/libkqueue/proc.c
@@ -0,0 +1,423 @@
+/*
+ * Copyright (c) 2009 Mark Heily <mark@heily.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/stat.h>
+
+#include <err.h>
+
+#include "config.h"
+#include "common.h"
+
+static int sigusr1_caught = 0;
+
+
+static void
+sig_handler(__unused int signum)
+{
+ sigusr1_caught = 1;
+}
+
+static void
+add_and_delete(void)
+{
+ struct kevent kev;
+ pid_t pid;
+
+ /* Create a child that waits to be killed and then exits */
+ pid = fork();
+ if (pid == 0) {
+ struct stat s;
+ if (fstat(kqfd, &s) != -1)
+ errx(1, "kqueue inherited across fork! (%s() at %s:%d)",
+ __func__, __FILE__, __LINE__);
+
+ pause();
+ exit(2);
+ }
+ printf(" -- child created (pid %d)\n", (int) pid);
+
+ test_begin("kevent(EVFILT_PROC, EV_ADD)");
+
+ test_no_kevents();
+ kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD, 0, 0, NULL);
+ test_no_kevents();
+
+ success();
+
+ test_begin("kevent(EVFILT_PROC, EV_DELETE)");
+
+ sleep(1);
+ test_no_kevents();
+ kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_DELETE, 0, 0, NULL);
+ if (kill(pid, SIGKILL) < 0)
+ err(1, "kill");
+ sleep(1);
+ test_no_kevents();
+
+ success();
+
+}
+
+static void
+proc_track(int sleep_time)
+{
+ char test_id[64];
+ struct kevent kev;
+ pid_t pid;
+ int pipe_fd[2];
+ ssize_t result;
+
+ snprintf(test_id, sizeof(test_id),
+ "kevent(EVFILT_PROC, NOTE_TRACK); sleep %d", sleep_time);
+ test_begin(test_id);
+ test_no_kevents();
+
+ if (pipe(pipe_fd)) {
+ err(1, "pipe (parent) failed! (%s() at %s:%d)",
+ __func__, __FILE__, __LINE__);
+ }
+
+ /* Create a child to track. */
+ pid = fork();
+ if (pid == 0) { /* Child */
+ pid_t grandchild = -1;
+
+ /*
+ * Give the parent a chance to start tracking us.
+ */
+ result = read(pipe_fd[1], test_id, 1);
+ if (result != 1) {
+ err(1, "read from pipe in child failed! (ret %zd) (%s() at %s:%d)",
+ result, __func__, __FILE__, __LINE__);
+ }
+
+ /*
+ * Spawn a grandchild that will immediately exit. If the kernel has bug
+ * 180385, the parent will see a kevent with both NOTE_CHILD and
+ * NOTE_EXIT. If that bug is fixed, it will see two separate kevents
+ * for those notes. Note that this triggers the conditions for
+ * detecting the bug quite reliably on a 1 CPU system (or if the test
+ * process is restricted to a single CPU), but may not trigger it on a
+ * multi-CPU system.
+ */
+ grandchild = fork();
+ if (grandchild == 0) { /* Grandchild */
+ if (sleep_time) sleep(sleep_time);
+ exit(1);
+ } else if (grandchild == -1) { /* Error */
+ err(1, "fork (grandchild) failed! (%s() at %s:%d)",
+ __func__, __FILE__, __LINE__);
+ } else { /* Child (Grandchild Parent) */
+ printf(" -- grandchild created (pid %d)\n", (int) grandchild);
+ }
+ if (sleep_time) sleep(sleep_time);
+ exit(0);
+ } else if (pid == -1) { /* Error */
+ err(1, "fork (child) failed! (%s() at %s:%d)",
+ __func__, __FILE__, __LINE__);
+ }
+
+ printf(" -- child created (pid %d)\n", (int) pid);
+
+ kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD | EV_ENABLE,
+ NOTE_TRACK | NOTE_EXEC | NOTE_EXIT | NOTE_FORK,
+ 0, NULL);
+
+ printf(" -- tracking child (pid %d)\n", (int) pid);
+
+ /* Now that we're tracking the child, tell it to proceed. */
+ result = write(pipe_fd[0], test_id, 1);
+ if (result != 1) {
+ err(1, "write to pipe in parent failed! (ret %zd) (%s() at %s:%d)",
+ result, __func__, __FILE__, __LINE__);
+ }
+
+ /*
+ * Several events should be received:
+ * - NOTE_FORK (from child)
+ * - NOTE_CHILD (from grandchild)
+ * - NOTE_EXIT (from grandchild)
+ * - NOTE_EXIT (from child)
+ *
+ * The NOTE_FORK and NOTE_EXIT from the child could be combined into a
+ * single event, but the NOTE_CHILD and NOTE_EXIT from the grandchild must
+ * not be combined.
+ *
+ * The loop continues until no events are received within a 5 second
+ * period, at which point it is assumed that no more will be coming. The
+ * loop is deliberately designed to attempt to get events even after all
+ * the expected ones are received in case some spurious events are
+ * generated as well as the expected ones.
+ */
+ {
+ int child_exit = 0;
+ int child_fork = 0;
+ int gchild_exit = 0;
+ int gchild_note = 0;
+ pid_t gchild_pid = -1;
+ int done = 0;
+ char *kev_str;
+
+ while (!done)
+ {
+ int handled = 0;
+ struct kevent *kevp;
+
+ kevp = kevent_get_timeout(kqfd, 5);
+ if (kevp == NULL) {
+ done = 1;
+ } else {
+ kev_str = kevent_to_str(kevp);
+ printf(" -- Received kevent: %s\n", kev_str);
+ free(kev_str);
+
+ if ((kevp->fflags & NOTE_CHILD) && (kevp->fflags & NOTE_EXIT)) {
+ errx(1, "NOTE_CHILD and NOTE_EXIT in same kevent: %s", kevent_to_str(kevp));
+ }
+
+ if (kevp->fflags & NOTE_CHILD) {
+ if (kevp->data == pid) {
+ if (!gchild_note) {
+ ++gchild_note;
+ gchild_pid = kevp->ident;
+ ++handled;
+ } else {
+ errx(1, "Spurious NOTE_CHILD: %s", kevent_to_str(kevp));
+ }
+ }
+ }
+
+ if (kevp->fflags & NOTE_EXIT) {
+ if ((kevp->ident == (uintptr_t)pid) && (!child_exit)) {
+ ++child_exit;
+ ++handled;
+ } else if ((kevp->ident == (uintptr_t)gchild_pid) && (!gchild_exit)) {
+ ++gchild_exit;
+ ++handled;
+ } else {
+ errx(1, "Spurious NOTE_EXIT: %s", kevent_to_str(kevp));
+ }
+ }
+
+ if (kevp->fflags & NOTE_FORK) {
+ if ((kevp->ident == (uintptr_t)pid) && (!child_fork)) {
+ ++child_fork;
+ ++handled;
+ } else {
+ errx(1, "Spurious NOTE_FORK: %s", kevent_to_str(kevp));
+ }
+ }
+
+ if (!handled) {
+ errx(1, "Spurious kevent: %s", kevent_to_str(kevp));
+ }
+
+ free(kevp);
+ }
+ }
+
+ /* Make sure all expected events were received. */
+ if (child_exit && child_fork && gchild_exit && gchild_note) {
+ printf(" -- Received all expected events.\n");
+ } else {
+ errx(1, "Did not receive all expected events.");
+ }
+ }
+
+ success();
+}
+
+#ifdef TODO
+static void
+event_trigger(void)
+{
+ struct kevent kev;
+ pid_t pid;
+
+ test_begin("kevent(EVFILT_PROC, wait)");
+
+ /* Create a child that waits to be killed and then exits */
+ pid = fork();
+ if (pid == 0) {
+ pause();
+ printf(" -- child caught signal, exiting\n");
+ exit(2);
+ }
+ printf(" -- child created (pid %d)\n", (int) pid);
+
+ test_no_kevents();
+ kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD, 0, 0, NULL);
+
+ /* Cause the child to exit, then retrieve the event */
+ printf(" -- killing process %d\n", (int) pid);
+ if (kill(pid, SIGUSR1) < 0)
+ err(1, "kill");
+ kevent_cmp(&kev, kevent_get(kqfd));
+ test_no_kevents();
+
+ success();
+}
+
+static void
+test_kevent_signal_disable(void)
+{
+ const char *test_id = "kevent(EVFILT_SIGNAL, EV_DISABLE)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DISABLE, 0, 0, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Block SIGUSR1, then send it to ourselves */
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR1);
+ if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
+ err(1, "sigprocmask");
+ if (kill(getpid(), SIGKILL) < 0)
+ err(1, "kill");
+
+ test_no_kevents();
+
+ success();
+}
+
+void
+test_kevent_signal_enable(void)
+{
+ const char *test_id = "kevent(EVFILT_SIGNAL, EV_ENABLE)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ENABLE, 0, 0, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Block SIGUSR1, then send it to ourselves */
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR1);
+ if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
+ err(1, "sigprocmask");
+ if (kill(getpid(), SIGUSR1) < 0)
+ err(1, "kill");
+
+ kev.flags = EV_ADD | EV_CLEAR;
+#if LIBKQUEUE
+ kev.data = 1; /* WORKAROUND */
+#else
+ kev.data = 2; // one extra time from test_kevent_signal_disable()
+#endif
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ /* Delete the watch */
+ kev.flags = EV_DELETE;
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ success();
+}
+
+void
+test_kevent_signal_del(void)
+{
+ const char *test_id = "kevent(EVFILT_SIGNAL, EV_DELETE)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ /* Delete the kevent */
+ EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DELETE, 0, 0, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Block SIGUSR1, then send it to ourselves */
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR1);
+ if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
+ err(1, "sigprocmask");
+ if (kill(getpid(), SIGUSR1) < 0)
+ err(1, "kill");
+
+ test_no_kevents();
+ success();
+}
+
+void
+test_kevent_signal_oneshot(void)
+{
+ const char *test_id = "kevent(EVFILT_SIGNAL, EV_ONESHOT)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD | EV_ONESHOT, 0, 0, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Block SIGUSR1, then send it to ourselves */
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR1);
+ if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
+ err(1, "sigprocmask");
+ if (kill(getpid(), SIGUSR1) < 0)
+ err(1, "kill");
+
+ kev.flags |= EV_CLEAR;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ /* Send another one and make sure we get no events */
+ if (kill(getpid(), SIGUSR1) < 0)
+ err(1, "kill");
+ test_no_kevents();
+
+ success();
+}
+#endif
+
+void
+test_evfilt_proc(void)
+{
+ kqfd = kqueue();
+
+ signal(SIGUSR1, sig_handler);
+
+ add_and_delete();
+ proc_track(0); /* Run without sleeping before children exit. */
+ proc_track(1); /* Sleep a bit in the children before exiting. */
+
+#if TODO
+ event_trigger();
+#endif
+
+ signal(SIGUSR1, SIG_DFL);
+
+#if TODO
+ test_kevent_signal_add();
+ test_kevent_signal_del();
+ test_kevent_signal_get();
+ test_kevent_signal_disable();
+ test_kevent_signal_enable();
+ test_kevent_signal_oneshot();
+#endif
+ close(kqfd);
+}
diff --git a/tests/sys/kqueue/libkqueue/read.c b/tests/sys/kqueue/libkqueue/read.c
new file mode 100644
index 000000000000..e39bcf92375f
--- /dev/null
+++ b/tests/sys/kqueue/libkqueue/read.c
@@ -0,0 +1,323 @@
+/*
+ * Copyright (c) 2009 Mark Heily <mark@heily.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "common.h"
+
+static int sockfd[2];
+
+static void
+kevent_socket_drain(void)
+{
+ char buf[1];
+
+ /* Drain the read buffer, then make sure there are no more events. */
+ puts("draining the read buffer");
+ if (read(sockfd[0], &buf[0], 1) < 1)
+ err(1, "read(2)");
+}
+
+static void
+kevent_socket_fill(void)
+{
+ puts("filling the read buffer");
+ if (write(sockfd[1], ".", 1) < 1)
+ err(1, "write(2)");
+}
+
+
+static void
+test_kevent_socket_add(void)
+{
+ const char *test_id = "kevent(EVFILT_READ, EV_ADD)";
+ struct kevent kev;
+
+ test_begin(test_id);
+ EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD, 0, 0, &sockfd[0]);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ success();
+}
+
+static void
+test_kevent_socket_get(void)
+{
+ const char *test_id = "kevent(EVFILT_READ) wait";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD, 0, 0, &sockfd[0]);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ kevent_socket_fill();
+
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ kevent_socket_drain();
+ test_no_kevents();
+
+ kev.flags = EV_DELETE;
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ success();
+}
+
+static void
+test_kevent_socket_clear(void)
+{
+ const char *test_id = "kevent(EVFILT_READ, EV_CLEAR)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, &sockfd[0]);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ kevent_socket_fill();
+ kevent_socket_fill();
+
+ kev.data = 2;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ /* We filled twice, but drain once. Edge-triggered would not generate
+ additional events.
+ */
+ kevent_socket_drain();
+ test_no_kevents();
+
+ kevent_socket_drain();
+ EV_SET(&kev, sockfd[0], EVFILT_READ, EV_DELETE, 0, 0, &sockfd[0]);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ success();
+}
+
+static void
+test_kevent_socket_disable_and_enable(void)
+{
+ const char *test_id = "kevent(EVFILT_READ, EV_DISABLE)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ /*
+ * Write to the socket before adding the event. This way we can verify that
+ * enabling a triggered kevent causes the event to be returned immediately.
+ */
+ kevent_socket_fill();
+
+ /* Add a disabled event. */
+ EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD | EV_DISABLE, 0, 0, &sockfd[0]);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ test_no_kevents();
+
+ /* Re-enable the knote, then see if an event is generated */
+ kev.flags = EV_ENABLE;
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+ kev.flags = EV_ADD;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ kevent_socket_drain();
+
+ kev.flags = EV_DELETE;
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ success();
+}
+
+static void
+test_kevent_socket_del(void)
+{
+ const char *test_id = "kevent(EVFILT_READ, EV_DELETE)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ EV_SET(&kev, sockfd[0], EVFILT_READ, EV_DELETE, 0, 0, &sockfd[0]);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ kevent_socket_fill();
+ test_no_kevents();
+ kevent_socket_drain();
+
+ success();
+}
+
+static void
+test_kevent_socket_oneshot(void)
+{
+ const char *test_id = "kevent(EVFILT_READ, EV_ONESHOT)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ /* Re-add the watch and make sure no events are pending */
+ puts("-- re-adding knote");
+ EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, &sockfd[0]);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+ test_no_kevents();
+
+ puts("-- getting one event");
+ kevent_socket_fill();
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ puts("-- checking knote disabled");
+ test_no_kevents();
+
+ /* Try to delete the knote, it should already be deleted */
+ EV_SET(&kev, sockfd[0], EVFILT_READ, EV_DELETE, 0, 0, &sockfd[0]);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) == 0)
+ err(1, "%s", test_id);
+
+ kevent_socket_drain();
+
+ success();
+}
+
+
+#if HAVE_EV_DISPATCH
+static void
+test_kevent_socket_dispatch(void)
+{
+ const char *test_id = "kevent(EVFILT_READ, EV_DISPATCH)";
+
+ test_begin(test_id);
+
+ struct kevent kev;
+
+ /* Re-add the watch and make sure no events are pending */
+ puts("-- re-adding knote");
+ EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD | EV_DISPATCH, 0, 0, &sockfd[0]);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+ test_no_kevents();
+
+ /* The event will occur only once, even though EV_CLEAR is not
+ specified. */
+ kevent_socket_fill();
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+ test_no_kevents();
+
+ /* Since the knote is disabled, the EV_DELETE operation succeeds. */
+ EV_SET(&kev, sockfd[0], EVFILT_READ, EV_DELETE, 0, 0, &sockfd[0]);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ kevent_socket_drain();
+
+ success();
+}
+#endif /* HAVE_EV_DISPATCH */
+
+#if BROKEN
+static void
+test_kevent_socket_lowat(void)
+{
+ const char *test_id = "kevent(EVFILT_READ, NOTE_LOWAT)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ /* Re-add the watch and make sure no events are pending */
+ puts("-- re-adding knote, setting low watermark to 2 bytes");
+ EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD | EV_ONESHOT, NOTE_LOWAT, 2, &sockfd[0]);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+ test_no_kevents();
+
+ puts("-- checking that one byte does not trigger an event..");
+ kevent_socket_fill();
+ test_no_kevents();
+
+ puts("-- checking that two bytes triggers an event..");
+ kevent_socket_fill();
+ if (kevent(kqfd, NULL, 0, &kev, 1, NULL) != 1)
+ err(1, "%s", test_id);
+ KEV_CMP(kev, sockfd[0], EVFILT_READ, 0);
+ test_no_kevents();
+
+ kevent_socket_drain();
+ kevent_socket_drain();
+
+ success();
+}
+#endif
+
+static void
+test_kevent_socket_eof(void)
+{
+ const char *test_id = "kevent(EVFILT_READ, EV_EOF)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ /* Re-add the watch and make sure no events are pending */
+ EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD, 0, 0, &sockfd[0]);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+ test_no_kevents();
+
+ if (close(sockfd[1]) < 0)
+ err(1, "close(2)");
+
+ kev.flags |= EV_EOF;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ /* Delete the watch */
+ EV_SET(&kev, sockfd[0], EVFILT_READ, EV_DELETE, 0, 0, &sockfd[0]);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ success();
+}
+
+void
+test_evfilt_read(void)
+{
+ /* Create a connected pair of full-duplex sockets for testing socket events */
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd) < 0)
+ abort();
+
+ kqfd = kqueue();
+ test_kevent_socket_add();
+ test_kevent_socket_del();
+ test_kevent_socket_get();
+ test_kevent_socket_disable_and_enable();
+ test_kevent_socket_oneshot();
+ test_kevent_socket_clear();
+#if HAVE_EV_DISPATCH
+ test_kevent_socket_dispatch();
+#endif
+ test_kevent_socket_eof();
+ close(kqfd);
+}
diff --git a/tests/sys/kqueue/libkqueue/signal.c b/tests/sys/kqueue/libkqueue/signal.c
new file mode 100644
index 000000000000..e081e0a44a83
--- /dev/null
+++ b/tests/sys/kqueue/libkqueue/signal.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2009 Mark Heily <mark@heily.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "common.h"
+
+
+static void
+test_kevent_signal_add(void)
+{
+ const char *test_id = "kevent(EVFILT_SIGNAL, EV_ADD)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ success();
+}
+
+static void
+test_kevent_signal_get(void)
+{
+ const char *test_id = "kevent(EVFILT_SIGNAL, wait)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Block SIGUSR1, then send it to ourselves */
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR1);
+ if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
+ err(1, "sigprocmask");
+ if (kill(getpid(), SIGUSR1) < 0)
+ err(1, "kill");
+
+ kev.flags |= EV_CLEAR;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ success();
+}
+
+static void
+test_kevent_signal_disable(void)
+{
+ const char *test_id = "kevent(EVFILT_SIGNAL, EV_DISABLE)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DISABLE, 0, 0, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Block SIGUSR1, then send it to ourselves */
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR1);
+ if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
+ err(1, "sigprocmask");
+ if (kill(getpid(), SIGUSR1) < 0)
+ err(1, "kill");
+
+ test_no_kevents();
+
+ success();
+}
+
+static void
+test_kevent_signal_enable(void)
+{
+ const char *test_id = "kevent(EVFILT_SIGNAL, EV_ENABLE)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ENABLE, 0, 0, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Block SIGUSR1, then send it to ourselves */
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR1);
+ if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
+ err(1, "sigprocmask");
+ if (kill(getpid(), SIGUSR1) < 0)
+ err(1, "kill");
+
+ kev.flags = EV_ADD | EV_CLEAR;
+#if LIBKQUEUE
+ kev.data = 1; /* WORKAROUND */
+#else
+ kev.data = 2; // one extra time from test_kevent_signal_disable()
+#endif
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ /* Delete the watch */
+ kev.flags = EV_DELETE;
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ success();
+}
+
+static void
+test_kevent_signal_del(void)
+{
+ const char *test_id = "kevent(EVFILT_SIGNAL, EV_DELETE)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ /* Delete the kevent */
+ EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DELETE, 0, 0, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Block SIGUSR1, then send it to ourselves */
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR1);
+ if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
+ err(1, "sigprocmask");
+ if (kill(getpid(), SIGUSR1) < 0)
+ err(1, "kill");
+
+ test_no_kevents();
+ success();
+}
+
+static void
+test_kevent_signal_oneshot(void)
+{
+ const char *test_id = "kevent(EVFILT_SIGNAL, EV_ONESHOT)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD | EV_ONESHOT, 0, 0, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Block SIGUSR1, then send it to ourselves */
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR1);
+ if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
+ err(1, "sigprocmask");
+ if (kill(getpid(), SIGUSR1) < 0)
+ err(1, "kill");
+
+ kev.flags |= EV_CLEAR;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ /* Send another one and make sure we get no events */
+ if (kill(getpid(), SIGUSR1) < 0)
+ err(1, "kill");
+ test_no_kevents();
+
+ success();
+}
+
+void
+test_evfilt_signal(void)
+{
+ kqfd = kqueue();
+ test_kevent_signal_add();
+ test_kevent_signal_del();
+ test_kevent_signal_get();
+ test_kevent_signal_disable();
+ test_kevent_signal_enable();
+ test_kevent_signal_oneshot();
+ close(kqfd);
+}
diff --git a/tests/sys/kqueue/libkqueue/timer.c b/tests/sys/kqueue/libkqueue/timer.c
new file mode 100644
index 000000000000..5116aea98b83
--- /dev/null
+++ b/tests/sys/kqueue/libkqueue/timer.c
@@ -0,0 +1,764 @@
+/*
+ * Copyright (c) 2009 Mark Heily <mark@heily.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "common.h"
+#include <sys/time.h>
+
+#define MILLION 1000000
+#define THOUSAND 1000
+#define SEC_TO_MS(t) ((t) * THOUSAND) /* Convert seconds to milliseconds. */
+#define SEC_TO_US(t) ((t) * MILLION) /* Convert seconds to microseconds. */
+#define MS_TO_US(t) ((t) * THOUSAND) /* Convert milliseconds to microseconds. */
+#define US_TO_NS(t) ((t) * THOUSAND) /* Convert microseconds to nanoseconds. */
+
+
+/* Get the current time with microsecond precision. Used for
+ * sub-second timing to make some timer tests run faster.
+ */
+static uint64_t
+now(void)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ /* Promote potentially 32-bit time_t to uint64_t before conversion. */
+ return SEC_TO_US((uint64_t)tv.tv_sec) + tv.tv_usec;
+}
+
+/* Sleep for a given number of milliseconds. The timeout is assumed to
+ * be less than 1 second.
+ */
+static void
+mssleep(int t)
+{
+ struct timespec stime = {
+ .tv_sec = 0,
+ .tv_nsec = US_TO_NS(MS_TO_US(t)),
+ };
+
+ nanosleep(&stime, NULL);
+}
+
+/* Sleep for a given number of microseconds. The timeout is assumed to
+ * be less than 1 second.
+ */
+static void
+ussleep(int t)
+{
+ struct timespec stime = {
+ .tv_sec = 0,
+ .tv_nsec = US_TO_NS(t),
+ };
+
+ nanosleep(&stime, NULL);
+}
+
+static void
+test_kevent_timer_add(void)
+{
+ const char *test_id = "kevent(EVFILT_TIMER, EV_ADD)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ EV_SET(&kev, 1, EVFILT_TIMER, EV_ADD, 0, 1000, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ success();
+}
+
+static void
+test_kevent_timer_del(void)
+{
+ const char *test_id = "kevent(EVFILT_TIMER, EV_DELETE)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ EV_SET(&kev, 1, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ test_no_kevents();
+
+ success();
+}
+
+static void
+test_kevent_timer_get(void)
+{
+ const char *test_id = "kevent(EVFILT_TIMER, wait)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ EV_SET(&kev, 1, EVFILT_TIMER, EV_ADD, 0, 1000, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ kev.flags |= EV_CLEAR;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ EV_SET(&kev, 1, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ success();
+}
+
+static void
+test_oneshot(void)
+{
+ const char *test_id = "kevent(EVFILT_TIMER, EV_ONESHOT)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, 500,NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Retrieve the event */
+ kev.flags = EV_ADD | EV_CLEAR | EV_ONESHOT;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ /* Check if the event occurs again */
+ sleep(3);
+ test_no_kevents();
+
+
+ success();
+}
+
+static void
+test_periodic(void)
+{
+ const char *test_id = "kevent(EVFILT_TIMER, periodic)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, 0, 1000,NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Retrieve the event */
+ kev.flags = EV_ADD | EV_CLEAR;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ /* Check if the event occurs again */
+ sleep(1);
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ /* Delete the event */
+ kev.flags = EV_DELETE;
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ success();
+}
+
+static void
+test_periodic_modify(void)
+{
+ const char *test_id = "kevent(EVFILT_TIMER, periodic_modify)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, 0, 1000, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Retrieve the event */
+ kev.flags = EV_ADD | EV_CLEAR;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ /* Check if the event occurs again */
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, 0, 495, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ kev.flags = EV_ADD | EV_CLEAR;
+ sleep(1);
+ kev.data = 2; /* Should have fired twice */
+
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ /* Delete the event */
+ kev.flags = EV_DELETE;
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ success();
+}
+
+#if WITH_NATIVE_KQUEUE_BUGS
+static void
+test_periodic_to_oneshot(void)
+{
+ const char *test_id = "kevent(EVFILT_TIMER, period_to_oneshot)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, 0, 1000, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Retrieve the event */
+ kev.flags = EV_ADD | EV_CLEAR;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ /* Check if the event occurs again */
+ sleep(1);
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ /* Switch to oneshot */
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, 500, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+ kev.flags = EV_ADD | EV_CLEAR | EV_ONESHOT;
+
+ sleep(1);
+ kev.data = 1; /* Should have fired once */
+
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ success();
+}
+#endif
+
+static void
+test_disable_and_enable(void)
+{
+ const char *test_id = "kevent(EVFILT_TIMER, EV_DISABLE and EV_ENABLE)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ /* Add the watch and immediately disable it */
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, 2000,NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+ kev.flags = EV_DISABLE;
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+ test_no_kevents();
+
+ /* Re-enable and check again */
+ kev.flags = EV_ENABLE;
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ kev.flags = EV_ADD | EV_CLEAR | EV_ONESHOT;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ success();
+}
+
+static void
+test_abstime(void)
+{
+ const char *test_id = "kevent(EVFILT_TIMER, EV_ONESHOT, NOTE_ABSTIME)";
+ struct kevent kev;
+ uint64_t end, start, stop;
+ const int timeout_sec = 3;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ start = now();
+ end = start + SEC_TO_US(timeout_sec);
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
+ NOTE_ABSTIME | NOTE_USECONDS, end, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Retrieve the event */
+ kev.flags = EV_ADD | EV_ONESHOT;
+ kev.data = 1;
+ kev.fflags = 0;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ stop = now();
+ if (stop < end)
+ err(1, "too early %jd %jd", (intmax_t)stop, (intmax_t)end);
+ /* Check if the event occurs again */
+ sleep(3);
+ test_no_kevents();
+
+ success();
+}
+
+static void
+test_abstime_epoch(void)
+{
+ const char *test_id = "kevent(EVFILT_TIMER (EPOCH), NOTE_ABSTIME)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, NOTE_ABSTIME, 0,
+ NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Retrieve the event */
+ kev.flags = EV_ADD;
+ kev.data = 1;
+ kev.fflags = 0;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ /* Delete the event */
+ kev.flags = EV_DELETE;
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ success();
+}
+
+static void
+test_abstime_preboot(void)
+{
+ const char *test_id = "kevent(EVFILT_TIMER (PREBOOT), EV_ONESHOT, NOTE_ABSTIME)";
+ struct kevent kev;
+ struct timespec btp;
+ uint64_t end, start, stop;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ /*
+ * We'll expire it at just before system boot (roughly) with the hope that
+ * we'll get an ~immediate expiration, just as we do for any value specified
+ * between system boot and now.
+ */
+ start = now();
+ if (clock_gettime(CLOCK_BOOTTIME, &btp) != 0)
+ err(1, "%s", test_id);
+
+ end = start - SEC_TO_US(btp.tv_sec + 1);
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
+ NOTE_ABSTIME | NOTE_USECONDS, end, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Retrieve the event */
+ kev.flags = EV_ADD | EV_ONESHOT;
+ kev.data = 1;
+ kev.fflags = 0;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ stop = now();
+ if (stop < end)
+ err(1, "too early %jd %jd", (intmax_t)stop, (intmax_t)end);
+ /* Check if the event occurs again */
+ sleep(3);
+ test_no_kevents();
+
+ success();
+}
+
+static void
+test_abstime_postboot(void)
+{
+ const char *test_id = "kevent(EVFILT_TIMER (POSTBOOT), EV_ONESHOT, NOTE_ABSTIME)";
+ struct kevent kev;
+ uint64_t end, start, stop;
+ const int timeout_sec = 1;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ /*
+ * Set a timer for 1 second ago, it should fire immediately rather than
+ * being rejected.
+ */
+ start = now();
+ end = start - SEC_TO_US(timeout_sec);
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
+ NOTE_ABSTIME | NOTE_USECONDS, end, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Retrieve the event */
+ kev.flags = EV_ADD | EV_ONESHOT;
+ kev.data = 1;
+ kev.fflags = 0;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ stop = now();
+ if (stop < end)
+ err(1, "too early %jd %jd", (intmax_t)stop, (intmax_t)end);
+ /* Check if the event occurs again */
+ sleep(3);
+ test_no_kevents();
+
+ success();
+}
+
+static void
+test_update(void)
+{
+ const char *test_id = "kevent(EVFILT_TIMER (UPDATE), EV_ADD | EV_ONESHOT)";
+ struct kevent kev;
+ long elapsed;
+ uint64_t start;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ /* First set the timer to 1 second */
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
+ NOTE_USECONDS, SEC_TO_US(1), (void *)1);
+ start = now();
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Now reduce the timer to 1 ms */
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
+ NOTE_USECONDS, MS_TO_US(1), (void *)2);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Wait for the event */
+ kev.flags |= EV_CLEAR;
+ kev.fflags &= ~NOTE_USECONDS;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+ elapsed = now() - start;
+
+ /* Check that the timer expired after at least 1 ms, but less than
+ * 1 second. This check is to make sure that the original 1 second
+ * timeout was not used.
+ */
+ printf("timer expired after %ld us\n", elapsed);
+ if (elapsed < MS_TO_US(1))
+ errx(1, "early timer expiration: %ld us", elapsed);
+ if (elapsed > SEC_TO_US(1))
+ errx(1, "late timer expiration: %ld us", elapsed);
+
+ success();
+}
+
+static void
+test_update_equal(void)
+{
+ const char *test_id = "kevent(EVFILT_TIMER (UPDATE=), EV_ADD | EV_ONESHOT)";
+ struct kevent kev;
+ long elapsed;
+ uint64_t start;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ /* First set the timer to 1 ms */
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
+ NOTE_USECONDS, MS_TO_US(1), NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Sleep for a significant fraction of the timeout. */
+ ussleep(600);
+
+ /* Now re-add the timer with the same parameters */
+ start = now();
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Wait for the event */
+ kev.flags |= EV_CLEAR;
+ kev.fflags &= ~NOTE_USECONDS;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+ elapsed = now() - start;
+
+ /* Check that the timer expired after at least 1 ms. This check is
+ * to make sure that the timer re-started and that the event is
+ * not from the original add of the timer.
+ */
+ printf("timer expired after %ld us\n", elapsed);
+ if (elapsed < MS_TO_US(1))
+ errx(1, "early timer expiration: %ld us", elapsed);
+
+ success();
+}
+
+static void
+test_update_expired(void)
+{
+ const char *test_id = "kevent(EVFILT_TIMER (UPDATE EXP), EV_ADD | EV_ONESHOT)";
+ struct kevent kev;
+ long elapsed;
+ uint64_t start;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ /* Set the timer to 1ms */
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
+ NOTE_USECONDS, MS_TO_US(1), NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Wait for 2 ms to give the timer plenty of time to expire. */
+ mssleep(2);
+
+ /* Now re-add the timer */
+ start = now();
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Wait for the event */
+ kev.flags |= EV_CLEAR;
+ kev.fflags &= ~NOTE_USECONDS;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+ elapsed = now() - start;
+
+ /* Check that the timer expired after at least 1 ms. This check
+ * is to make sure that the timer re-started and that the event is
+ * not from the original add (and expiration) of the timer.
+ */
+ printf("timer expired after %ld us\n", elapsed);
+ if (elapsed < MS_TO_US(1))
+ errx(1, "early timer expiration: %ld us", elapsed);
+
+ /* Make sure the re-added timer does not fire. In other words,
+ * test that the event received above was the only event from the
+ * add and re-add of the timer.
+ */
+ mssleep(2);
+ test_no_kevents();
+
+ success();
+}
+
+static void
+test_update_periodic(void)
+{
+ const char *test_id = "kevent(EVFILT_TIMER (UPDATE), periodic)";
+ struct kevent kev;
+ long elapsed;
+ uint64_t start, stop;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, 0, SEC_TO_MS(1), NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Retrieve the event */
+ kev.flags = EV_ADD | EV_CLEAR;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ /* Check if the event occurs again */
+ sleep(1);
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ /* Re-add with new timeout. */
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, 0, SEC_TO_MS(2), NULL);
+ start = now();
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Retrieve the event */
+ kev.flags = EV_ADD | EV_CLEAR;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ stop = now();
+ elapsed = stop - start;
+
+ /* Check that the timer expired after at least 2 ms.
+ */
+ printf("timer expired after %ld us\n", elapsed);
+ if (elapsed < MS_TO_US(2))
+ errx(1, "early timer expiration: %ld us", elapsed);
+
+ /* Delete the event */
+ kev.flags = EV_DELETE;
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ success();
+}
+
+static void
+test_update_timing(void)
+{
+#define MIN_SLEEP 500
+#define MAX_SLEEP 1500
+ const char *test_id = "kevent(EVFILT_TIMER (UPDATE TIMING), EV_ADD | EV_ONESHOT)";
+ struct kevent kev;
+ int iteration;
+ int sleeptime;
+ long elapsed;
+ uint64_t start, stop;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ /* Re-try the update tests with a variety of delays between the
+ * original timer activation and the update of the timer. The goal
+ * is to show that in all cases the only timer event that is
+ * received is from the update and not the original timer add.
+ */
+ for (sleeptime = MIN_SLEEP, iteration = 1;
+ sleeptime < MAX_SLEEP;
+ ++sleeptime, ++iteration) {
+
+ /* First set the timer to 1 ms */
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
+ NOTE_USECONDS, MS_TO_US(1), NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Delay; the delay ranges from less than to greater than the
+ * timer period.
+ */
+ ussleep(sleeptime);
+
+ /* Now re-add the timer with the same parameters */
+ start = now();
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Wait for the event */
+ kev.flags |= EV_CLEAR;
+ kev.fflags &= ~NOTE_USECONDS;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+ stop = now();
+ elapsed = stop - start;
+
+ /* Check that the timer expired after at least 1 ms. This
+ * check is to make sure that the timer re-started and that
+ * the event is not from the original add of the timer.
+ */
+ if (elapsed < MS_TO_US(1))
+ errx(1, "early timer expiration: %ld us", elapsed);
+
+ /* Make sure the re-added timer does not fire. In other words,
+ * test that the event received above was the only event from
+ * the add and re-add of the timer.
+ */
+ mssleep(2);
+ test_no_kevents_quietly();
+ }
+
+ success();
+}
+
+static void
+test_dispatch(void)
+{
+ const char *test_id = "kevent(EVFILT_TIMER, EV_ADD | EV_DISPATCH)";
+ struct kevent kev;
+
+ test_no_kevents();
+
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_DISPATCH, 0, 200, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Get one event */
+ kev.flags = EV_ADD | EV_CLEAR | EV_DISPATCH;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ /* Confirm that the knote is disabled due to EV_DISPATCH */
+ usleep(500000);
+ test_no_kevents();
+
+ /* Enable the knote and make sure no events are pending */
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ENABLE | EV_DISPATCH, 0, 200, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+ test_no_kevents();
+
+ /* Get the next event */
+ usleep(1100000); /* 1100 ms */
+ kev.flags = EV_ADD | EV_CLEAR | EV_DISPATCH;
+ kev.data = 5;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ /* Remove the knote and ensure the event no longer fires */
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+ usleep(500000); /* 500ms */
+ test_no_kevents();
+
+ success();
+}
+
+void
+test_evfilt_timer(void)
+{
+ kqfd = kqueue();
+ test_kevent_timer_add();
+ test_kevent_timer_del();
+ test_kevent_timer_get();
+ test_oneshot();
+ test_periodic();
+ test_periodic_modify();
+#if WITH_NATIVE_KQUEUE_BUGS
+ test_periodic_to_oneshot();
+#endif
+ test_abstime();
+ test_abstime_epoch();
+ test_abstime_preboot();
+ test_abstime_postboot();
+ test_update();
+ test_update_equal();
+ test_update_expired();
+ test_update_timing();
+ test_update_periodic();
+ test_disable_and_enable();
+ test_dispatch();
+ close(kqfd);
+}
diff --git a/tests/sys/kqueue/libkqueue/user.c b/tests/sys/kqueue/libkqueue/user.c
new file mode 100644
index 000000000000..5f3d1c9f94bd
--- /dev/null
+++ b/tests/sys/kqueue/libkqueue/user.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2009 Mark Heily <mark@heily.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "common.h"
+
+
+static void
+add_and_delete(void)
+{
+ const char *test_id = "kevent(EVFILT_USER, EV_ADD and EV_DELETE)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ADD, 0, 0, NULL);
+ test_no_kevents();
+
+ kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_DELETE, 0, 0, NULL);
+ test_no_kevents();
+
+ success();
+}
+
+static void
+event_wait(void)
+{
+ const char *test_id = "kevent(EVFILT_USER, wait)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ /* Add the event, and then trigger it */
+ kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, NULL);
+ kevent_add(kqfd, &kev, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL);
+
+ kev.fflags &= ~NOTE_FFCTRLMASK;
+ kev.fflags &= ~NOTE_TRIGGER;
+ kev.flags = EV_CLEAR;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ test_no_kevents();
+
+ success();
+}
+
+static void
+event_wait_keepudata(void)
+{
+ const char *test_id = "kevent(EVFILT_USER, wait w/ EV_KEEPUDATA)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, &kev);
+ kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_KEEPUDATA, NOTE_TRIGGER, 0,
+ NULL);
+
+ kev.fflags &= ~NOTE_FFCTRLMASK;
+ kev.fflags &= ~NOTE_TRIGGER;
+ kev.flags = EV_CLEAR;
+ kev.udata = &kev;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ test_no_kevents();
+
+ success();
+}
+
+
+static void
+disable_and_enable(void)
+{
+ const char *test_id = "kevent(EVFILT_USER, EV_DISABLE and EV_ENABLE)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ADD, 0, 0, NULL);
+ kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_DISABLE, 0, 0, NULL);
+
+ /* Trigger the event, but since it is disabled, nothing will happen. */
+ kevent_add(kqfd, &kev, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL);
+ test_no_kevents();
+
+ kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ENABLE, 0, 0, NULL);
+ kevent_add(kqfd, &kev, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL);
+
+ kev.flags = EV_CLEAR;
+ kev.fflags &= ~NOTE_FFCTRLMASK;
+ kev.fflags &= ~NOTE_TRIGGER;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ success();
+}
+
+static void
+disable_and_enable_keepudata(void)
+{
+ const char *test_id =
+ "kevent(EVFILT_USER, EV_DISABLE and EV_ENABLE w/ EV_KEEPUDATA)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ADD, 0, 0, &kev);
+ kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_DISABLE | EV_KEEPUDATA, 0, 0,
+ NULL);
+
+ /* Trigger the event, but since it is disabled, nothing will happen. */
+ kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_KEEPUDATA, NOTE_TRIGGER, 0, NULL);
+ test_no_kevents();
+
+ kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ENABLE | EV_KEEPUDATA, 0, 0,
+ NULL);
+ kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_KEEPUDATA, NOTE_TRIGGER, 0, NULL);
+
+ kev.flags = EV_CLEAR;
+ kev.fflags &= ~NOTE_FFCTRLMASK;
+ kev.fflags &= ~NOTE_TRIGGER;
+ kev.udata = &kev;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ success();
+}
+
+static void
+oneshot(void)
+{
+ const char *test_id = "kevent(EVFILT_USER, EV_ONESHOT)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ kevent_add(kqfd, &kev, 2, EVFILT_USER, EV_ADD | EV_ONESHOT, 0, 0, NULL);
+
+ puts(" -- event 1");
+ kevent_add(kqfd, &kev, 2, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL);
+
+ kev.flags = EV_ONESHOT;
+ kev.fflags &= ~NOTE_FFCTRLMASK;
+ kev.fflags &= ~NOTE_TRIGGER;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ test_no_kevents();
+
+ success();
+}
+
+void
+test_evfilt_user(void)
+{
+ kqfd = kqueue();
+
+ add_and_delete();
+ event_wait();
+ event_wait_keepudata();
+ disable_and_enable();
+ disable_and_enable_keepudata();
+ oneshot();
+ /* TODO: try different fflags operations */
+
+ close(kqfd);
+}
diff --git a/tests/sys/kqueue/libkqueue/vnode.c b/tests/sys/kqueue/libkqueue/vnode.c
new file mode 100644
index 000000000000..2c214f50d08e
--- /dev/null
+++ b/tests/sys/kqueue/libkqueue/vnode.c
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 2009 Mark Heily <mark@heily.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "common.h"
+
+int vnode_fd;
+
+static void
+test_kevent_vnode_add(void)
+{
+ const char *test_id = "kevent(EVFILT_VNODE, EV_ADD)";
+ const char *testfile = "./kqueue-test.tmp";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ system("touch ./kqueue-test.tmp");
+ vnode_fd = open(testfile, O_RDONLY);
+ if (vnode_fd < 0)
+ err(1, "open of %s", testfile);
+ else
+ printf("vnode_fd = %d\n", vnode_fd);
+
+ EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD,
+ NOTE_WRITE | NOTE_ATTRIB | NOTE_RENAME | NOTE_DELETE, 0, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ success();
+}
+
+static void
+test_kevent_vnode_note_delete(void)
+{
+ const char *test_id = "kevent(EVFILT_VNODE, NOTE_DELETE)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_DELETE, 0, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ if (unlink("./kqueue-test.tmp") < 0)
+ err(1, "unlink");
+
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ success();
+}
+
+static void
+test_kevent_vnode_note_delete_fifo(void)
+{
+ const char *test_id = "kevent(EVFILT_VNODE, NOTE_DELETE, FIFO)";
+ const char *fifo_path = "./kqueue-fifo.tmp";
+ struct kevent kev;
+ int fd;
+ pid_t pid;
+
+ test_begin(test_id);
+
+ if (mkfifo(fifo_path, 0600) != 0)
+ err(1, "mkfifo");
+
+ pid = fork();
+ if (pid == -1)
+ err(1, "fork");
+
+ if (pid == 0) {
+ char buf[4];
+
+ fd = open(fifo_path, O_RDONLY);
+ if (fd == -1)
+ _exit(1);
+
+ while (read(fd, buf, sizeof(buf)) != 0) {
+ }
+
+ _exit(0);
+ }
+
+ sleep(1);
+ if (waitpid(pid, NULL, WNOHANG) == pid) {
+ unlink(fifo_path);
+ err(1, "open");
+ }
+
+ fd = open(fifo_path, O_WRONLY);
+ if (fd < 0) {
+ unlink(fifo_path);
+ err(1, "open");
+ }
+
+ EV_SET(&kev, fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_DELETE, 0, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) {
+ unlink(fifo_path);
+ err(1, "%s", test_id);
+ }
+
+ if (unlink(fifo_path) < 0)
+ err(1, "unlink");
+
+ kevent_cmp(&kev, kevent_get(kqfd));
+ close(fd);
+
+ success();
+}
+
+static void
+test_kevent_vnode_note_write(void)
+{
+ const char *test_id = "kevent(EVFILT_VNODE, NOTE_WRITE)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_WRITE, 0, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ if (system("echo hello >> ./kqueue-test.tmp") < 0)
+ err(1, "system");
+
+ /* BSD kqueue adds NOTE_EXTEND even though it was not requested */
+ /* BSD kqueue removes EV_ENABLE */
+ kev.flags &= ~EV_ENABLE; // XXX-FIXME compatibility issue
+ kev.fflags |= NOTE_EXTEND; // XXX-FIXME compatibility issue
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ success();
+}
+
+static void
+test_kevent_vnode_note_attrib(void)
+{
+ const char *test_id = "kevent(EVFILT_VNODE, NOTE_ATTRIB)";
+ struct kevent kev;
+ int nfds;
+
+ test_begin(test_id);
+
+ EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_ATTRIB, 0, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ if (system("touch ./kqueue-test.tmp") < 0)
+ err(1, "system");
+
+ nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL);
+ if (nfds < 1)
+ err(1, "%s", test_id);
+ if (kev.ident != (uintptr_t)vnode_fd ||
+ kev.filter != EVFILT_VNODE ||
+ kev.fflags != NOTE_ATTRIB)
+ err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)",
+ test_id, (unsigned int)kev.ident, kev.filter, kev.flags);
+
+ success();
+}
+
+static void
+test_kevent_vnode_note_rename(void)
+{
+ const char *test_id = "kevent(EVFILT_VNODE, NOTE_RENAME)";
+ struct kevent kev;
+ int nfds;
+
+ test_begin(test_id);
+
+ EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_RENAME, 0, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ if (system("mv ./kqueue-test.tmp ./kqueue-test2.tmp") < 0)
+ err(1, "system");
+
+ nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL);
+ if (nfds < 1)
+ err(1, "%s", test_id);
+ if (kev.ident != (uintptr_t)vnode_fd ||
+ kev.filter != EVFILT_VNODE ||
+ kev.fflags != NOTE_RENAME)
+ err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)",
+ test_id, (unsigned int)kev.ident, kev.filter, kev.flags);
+
+ if (system("mv ./kqueue-test2.tmp ./kqueue-test.tmp") < 0)
+ err(1, "system");
+
+ success();
+}
+
+static void
+test_kevent_vnode_del(void)
+{
+ const char *test_id = "kevent(EVFILT_VNODE, EV_DELETE)";
+ struct kevent kev;
+
+ test_begin(test_id);
+
+ EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_DELETE, 0, 0, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ success();
+}
+
+static void
+test_kevent_vnode_disable_and_enable(void)
+{
+ const char *test_id = "kevent(EVFILT_VNODE, EV_DISABLE and EV_ENABLE)";
+ struct kevent kev;
+ int nfds;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ /* Add the watch and immediately disable it */
+ EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_ATTRIB, 0, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+ kev.flags = EV_DISABLE;
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Confirm that the watch is disabled */
+ if (system("touch ./kqueue-test.tmp") < 0)
+ err(1, "system");
+ test_no_kevents();
+
+ /* Re-enable and check again */
+ kev.flags = EV_ENABLE;
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+ if (system("touch ./kqueue-test.tmp") < 0)
+ err(1, "system");
+ nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL);
+ if (nfds < 1)
+ err(1, "%s", test_id);
+ if (kev.ident != (uintptr_t)vnode_fd ||
+ kev.filter != EVFILT_VNODE ||
+ kev.fflags != NOTE_ATTRIB)
+ err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)",
+ test_id, (unsigned int)kev.ident, kev.filter, kev.flags);
+
+ success();
+}
+
+#if HAVE_EV_DISPATCH
+static void
+test_kevent_vnode_dispatch(void)
+{
+ const char *test_id = "kevent(EVFILT_VNODE, EV_DISPATCH)";
+ struct kevent kev;
+ int nfds;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_DISPATCH, NOTE_ATTRIB, 0, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ if (system("touch ./kqueue-test.tmp") < 0)
+ err(1, "system");
+
+ nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL);
+ if (nfds < 1)
+ err(1, "%s", test_id);
+ if (kev.ident != (uintptr_t)vnode_fd ||
+ kev.filter != EVFILT_VNODE ||
+ kev.fflags != NOTE_ATTRIB)
+ err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)",
+ test_id, (unsigned int)kev.ident, kev.filter, kev.flags);
+
+ /* Confirm that the watch is disabled automatically */
+ puts("-- checking that watch is disabled");
+ if (system("touch ./kqueue-test.tmp") < 0)
+ err(1, "system");
+ test_no_kevents();
+
+ /* Delete the watch */
+ EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_DELETE, NOTE_ATTRIB, 0, NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "remove watch failed: %s", test_id);
+
+ success();
+}
+#endif /* HAVE_EV_DISPATCH */
+
+void
+test_evfilt_vnode(void)
+{
+ kqfd = kqueue();
+ test_kevent_vnode_add();
+ test_kevent_vnode_del();
+ test_kevent_vnode_disable_and_enable();
+#if HAVE_EV_DISPATCH
+ test_kevent_vnode_dispatch();
+#endif
+ test_kevent_vnode_note_write();
+ test_kevent_vnode_note_attrib();
+ test_kevent_vnode_note_rename();
+ test_kevent_vnode_note_delete();
+ test_kevent_vnode_note_delete_fifo();
+ close(kqfd);
+}
diff --git a/tests/sys/mac/Makefile b/tests/sys/mac/Makefile
new file mode 100644
index 000000000000..3447d00122f5
--- /dev/null
+++ b/tests/sys/mac/Makefile
@@ -0,0 +1,7 @@
+TESTSDIR= ${TESTSBASE}/sys/mac
+
+TESTS_SUBDIRS+= bsdextended
+TESTS_SUBDIRS+= ipacl
+TESTS_SUBDIRS+= portacl
+
+.include <bsd.test.mk>
diff --git a/tests/sys/mac/Makefile.depend b/tests/sys/mac/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/mac/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/mac/Makefile.inc b/tests/sys/mac/Makefile.inc
new file mode 100644
index 000000000000..01b5f23410c8
--- /dev/null
+++ b/tests/sys/mac/Makefile.inc
@@ -0,0 +1 @@
+.include "../Makefile.inc"
diff --git a/tests/sys/mac/bsdextended/Makefile b/tests/sys/mac/bsdextended/Makefile
new file mode 100644
index 000000000000..cc3a3f8ea534
--- /dev/null
+++ b/tests/sys/mac/bsdextended/Makefile
@@ -0,0 +1,14 @@
+TESTSDIR= ${TESTSBASE}/sys/mac/bsdextended
+
+ATF_TESTS_SH+= matches_test
+TAP_TESTS_C+= ugidfw_test
+
+LIBADD.ugidfw_test+= ugidfw
+
+TEST_METADATA.ugidfw_test+= required_user="root"
+# Each test case of matches_test reuses the same ruleset number, so they cannot
+# be run simultaneously
+TEST_METADATA.matches_test+= is_exclusive=true
+TEST_METADATA+= required_kmods="mac_bsdextended"
+
+.include <bsd.test.mk>
diff --git a/tests/sys/mac/bsdextended/Makefile.depend b/tests/sys/mac/bsdextended/Makefile.depend
new file mode 100644
index 000000000000..6bb431578174
--- /dev/null
+++ b/tests/sys/mac/bsdextended/Makefile.depend
@@ -0,0 +1,17 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libugidfw \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/mac/bsdextended/matches_test.sh b/tests/sys/mac/bsdextended/matches_test.sh
new file mode 100644
index 000000000000..41fa04f221e3
--- /dev/null
+++ b/tests/sys/mac/bsdextended/matches_test.sh
@@ -0,0 +1,396 @@
+#!/bin/sh
+#
+#
+
+uidrange="60000:100000"
+gidrange="60000:100000"
+uidinrange="nobody"
+uidoutrange="daemon"
+gidinrange="nobody" # We expect $uidinrange in this group
+gidoutrange="daemon" # We expect $uidinrange in this group
+
+
+check_ko()
+{
+ if [ $(sysctl -n security.mac.bsdextended.enabled) = "0" ]; then
+ # The kernel module is loaded but disabled. Enable it for the
+ # duration of the test.
+ touch enabled_bsdextended
+ sysctl security.mac.bsdextended.enabled=1
+ fi
+}
+
+setup()
+{
+ check_ko
+ mkdir mnt
+ [ -c /dev/mdctl ] || atf_skip "no /dev/mdctl to create md devices"
+ mdmfs -s 25m md mnt \
+ || atf_fail "failed to mount md device"
+ chmod a+rwx mnt
+ md_device=$(mount -p | grep "$PWD/mnt" | awk '{ gsub(/^\/dev\//, "", $1); print $1 }')
+ if [ -z "$md_device" ]; then
+ atf_fail "md device not properly attached to the system"
+ fi
+ echo $md_device > md_device
+
+ ugidfw remove 1
+
+ cat > mnt/test-script.sh <<'EOF'
+#!/bin/sh
+: > $1
+EOF
+ if [ $? -ne 0 ]; then
+ atf_fail "failed to create test script"
+ fi
+
+ file1=mnt/test-$uidinrange
+ file2=mnt/test-$uidoutrange
+ command1="sh mnt/test-script.sh $file1"
+ command2="sh mnt/test-script.sh $file2"
+
+ # $uidinrange file
+ atf_check -s exit:0 su -m $uidinrange -c "$command1"
+
+ chown "$uidinrange":"$gidinrange" $file1
+ chmod a+w $file1
+
+ # $uidoutrange file
+ if ! $command2; then
+ atf_fail $desc
+ fi
+
+ chown "$uidoutrange":"$gidoutrange" $file2
+ chmod a+w $file2
+}
+
+cleanup()
+{
+ ugidfw remove 1
+
+ umount -f mnt
+ if [ -f md_device ]; then
+ mdconfig -d -u $( cat md_device )
+ fi
+ if [ -f enabled_bsdextended ]; then
+ sysctl security.mac.bsdextended.enabled=0
+ fi
+}
+
+atf_test_case no_rules cleanup
+no_rules_head()
+{
+ atf_set "require.user" "root"
+}
+no_rules_body()
+{
+ setup
+
+ # no rules $uidinrange
+ atf_check -s exit:0 su -fm $uidinrange -c "$command1"
+
+ # no rules $uidoutrange
+ atf_check -s exit:0 su -fm $uidoutrange -c "$command1"
+}
+no_rules_cleanup()
+{
+ cleanup
+}
+
+atf_test_case subject_match_on_uid cleanup
+subject_match_on_uid_head()
+{
+ atf_set "require.user" "root"
+}
+subject_match_on_uid_body()
+{
+ setup
+
+ atf_check -s exit:0 ugidfw set 1 subject uid $uidrange object mode rasx
+ # subject uid in range
+ atf_check -s not-exit:0 -e match:"Permission denied" \
+ su -fm $uidinrange -c "$command1"
+
+ # subject uid out range
+ atf_check -s exit:0 su -fm $uidoutrange -c "$command1"
+
+}
+subject_match_on_uid_cleanup()
+{
+ cleanup
+}
+
+atf_test_case subject_match_on_gid cleanup
+subject_match_on_gid_head()
+{
+ atf_set "require.user" "root"
+}
+subject_match_on_gid_body()
+{
+ setup
+
+ atf_check -s exit:0 ugidfw set 1 subject gid $gidrange object mode rasx
+
+ # subject gid in range
+ atf_check -s not-exit:0 -e match:"Permission denied" \
+ su -fm $uidinrange -c "$command1"
+
+ # subject gid out range
+ atf_check -s exit:0 su -fm $uidoutrange -c "$command1"
+}
+subject_match_on_gid_cleanup()
+{
+ cleanup
+}
+
+atf_test_case subject_match_on_jail cleanup
+subject_match_on_jail_head()
+{
+ atf_set "require.progs" "jail"
+ atf_set "require.user" "root"
+}
+subject_match_on_jail_body()
+{
+ setup
+
+ atf_expect_fail "this testcase fails (see bug # 205481)"
+ # subject matching jailid
+ jailid=`jail -i / localhost 127.0.0.1 /usr/sbin/daemon -f /bin/sh -c "(sleep 5; touch mnt/test-jail) &"`
+ atf_check -s exit:0 ugidfw set 1 subject jailid $jailid object mode rasx
+ sleep 10
+
+ if [ -f mnt/test-jail ]; then
+ atf_fail "$desc"
+ fi
+
+ rm -f mnt/test-jail
+ # subject nonmatching jailid
+ jailid=`jail -i / localhost 127.0.0.1 /usr/sbin/daemon -f /bin/sh -c "(sleep 5; touch mnt/test-jail) &"`
+ sleep 10
+ if ! [ -f mnt/test-jail ]; then
+ atf_fail $desc
+ fi
+}
+subject_match_on_jail_cleanup()
+{
+ cleanup
+}
+
+atf_test_case object_uid cleanup
+object_uid_head()
+{
+ atf_set "require.user" "root"
+}
+object_uid_body()
+{
+ setup
+
+ atf_check -s exit:0 ugidfw set 1 subject object uid $uidrange mode rasx
+
+ # object uid in range
+ atf_check -s not-exit:0 -e match:"Permission denied" \
+ su -fm $uidinrange -c "$command1"
+
+ # object uid out range
+ atf_check -s exit:0 su -fm $uidinrange -c "$command2"
+ atf_check -s exit:0 ugidfw set 1 subject object uid $uidrange mode rasx
+
+ # object uid in range (different subject)
+ atf_check -s not-exit:0 -e match:"Permission denied" \
+ su -fm $uidoutrange -c "$command1"
+
+ # object uid out range (different subject)
+ atf_check -s exit:0 su -fm $uidoutrange -c "$command2"
+
+}
+object_uid_cleanup()
+{
+ cleanup
+}
+
+atf_test_case object_gid cleanup
+object_gid_head()
+{
+ atf_set "require.user" "root"
+}
+object_gid_body()
+{
+ setup
+
+ atf_check -s exit:0 ugidfw set 1 subject object gid $uidrange mode rasx
+
+ # object gid in range
+ atf_check -s not-exit:0 -e match:"Permission denied" \
+ su -fm $uidinrange -c "$command1"
+
+ # object gid out range
+ atf_check -s exit:0 su -fm $uidinrange -c "$command2"
+ # object gid in range (different subject)
+ atf_check -s not-exit:0 -e match:"Permission denied" \
+ su -fm $uidoutrange -c "$command1"
+
+ # object gid out range (different subject)
+ atf_check -s exit:0 su -fm $uidoutrange -c "$command2"
+}
+object_gid_cleanup()
+{
+ cleanup
+}
+
+atf_test_case object_filesys cleanup
+object_filesys_head()
+{
+ atf_set "require.user" "root"
+}
+object_filesys_body()
+{
+ setup
+
+ atf_check -s exit:0 ugidfw set 1 subject uid $uidrange object filesys / mode rasx
+ # object out of filesys
+ atf_check -s exit:0 su -fm $uidinrange -c "$command1"
+
+ atf_check -s exit:0 ugidfw set 1 subject uid $uidrange object filesys mnt mode rasx
+ # object in filesys
+ atf_check -s not-exit:0 -e match:"Permission denied" \
+ su -fm $uidinrange -c "$command1"
+}
+object_filesys_cleanup()
+{
+ cleanup
+}
+
+atf_test_case object_suid cleanup
+object_suid_head()
+{
+ atf_set "require.user" "root"
+}
+object_suid_body()
+{
+ setup
+
+ atf_check -s exit:0 ugidfw set 1 subject uid $uidrange object suid mode rasx
+ # object notsuid
+ atf_check -s exit:0 su -fm $uidinrange -c "$command1"
+
+ chmod u+s $file1
+ # object suid
+ atf_check -s not-exit:0 -e match:"Permission denied" \
+ su -fm $uidinrange -c "$command1"
+ chmod u-s $file1
+
+}
+object_suid_cleanup()
+{
+ cleanup
+}
+
+atf_test_case object_sgid cleanup
+object_sgid_head()
+{
+ atf_set "require.user" "root"
+}
+object_sgid_body()
+{
+ setup
+
+ atf_check -s exit:0 ugidfw set 1 subject uid $uidrange object sgid mode rasx
+ # object notsgid
+ atf_check -s exit:0 su -fm $uidinrange -c "$command1"
+
+ chmod g+s $file1
+ # object sgid
+ atf_check -s not-exit:0 -e match:"Permission denied" \
+ su -fm $uidinrange -c "$command1"
+ chmod g-s $file1
+}
+object_sgid_cleanup()
+{
+ cleanup
+}
+
+atf_test_case object_uid_matches_subject cleanup
+object_uid_matches_subject_head()
+{
+ atf_set "require.user" "root"
+}
+object_uid_matches_subject_body()
+{
+ setup
+
+ atf_check -s exit:0 ugidfw set 1 subject uid $uidrange object uid_of_subject mode rasx
+
+ # object uid notmatches subject
+ atf_check -s exit:0 su -fm $uidinrange -c "$command2"
+
+ # object uid matches subject
+ atf_check -s not-exit:0 -e match:"Permission denied" \
+ su -fm $uidinrange -c "$command1"
+}
+object_uid_matches_subject_cleanup()
+{
+ cleanup
+}
+
+atf_test_case object_gid_matches_subject cleanup
+object_gid_matches_subject_head()
+{
+ atf_set "require.user" "root"
+}
+object_gid_matches_subject_body()
+{
+ setup
+
+ atf_check -s exit:0 ugidfw set 1 subject uid $uidrange object gid_of_subject mode rasx
+
+ # object gid notmatches subject
+ atf_check -s exit:0 su -fm $uidinrange -c "$command2"
+
+ # object gid matches subject
+ atf_check -s not-exit:0 -e match:"Permission denied" \
+ su -fm $uidinrange -c "$command1"
+
+}
+object_gid_matches_subject_cleanup()
+{
+ cleanup
+}
+
+atf_test_case object_type cleanup
+object_type_head()
+{
+ atf_set "require.user" "root"
+}
+object_type_body()
+{
+ setup
+
+ # object not type
+ atf_check -s exit:0 ugidfw set 1 subject uid $uidrange object type dbclsp mode rasx
+ atf_check -s exit:0 su -fm $uidinrange -c "$command1"
+
+ # object type
+ atf_check -s exit:0 ugidfw set 1 subject uid $uidrange object type r mode rasx
+ atf_check -s not-exit:0 -e match:"Permission denied" \
+ su -fm $uidinrange -c "$command1"
+}
+object_type_cleanup()
+{
+ cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case no_rules
+ atf_add_test_case subject_match_on_uid
+ atf_add_test_case subject_match_on_gid
+ atf_add_test_case subject_match_on_jail
+ atf_add_test_case object_uid
+ atf_add_test_case object_gid
+ atf_add_test_case object_filesys
+ atf_add_test_case object_suid
+ atf_add_test_case object_sgid
+ atf_add_test_case object_uid_matches_subject
+ atf_add_test_case object_gid_matches_subject
+ atf_add_test_case object_type
+}
diff --git a/tests/sys/mac/bsdextended/ugidfw_test.c b/tests/sys/mac/bsdextended/ugidfw_test.c
new file mode 100644
index 000000000000..032ba33adb21
--- /dev/null
+++ b/tests/sys/mac/bsdextended/ugidfw_test.c
@@ -0,0 +1,251 @@
+/*-
+ * Copyright (c) 2005 McAfee, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/mac.h>
+#include <sys/mount.h>
+
+#include <security/mac_bsdextended/mac_bsdextended.h>
+
+#include <err.h>
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ugidfw.h>
+#include <unistd.h>
+
+/*
+ * Starting point for a regression test for mac_bsdextended(4) and the
+ * supporting libugidfw(3).
+ */
+
+/*
+ * This section of the regression test passes some test cases through the
+ * rule<->string routines to confirm they work approximately as desired.
+ */
+
+/*
+ * List of users and groups we must check exists before we can begin, since
+ * they are used in the string test rules. We use users and groups that will
+ * always exist in a default install used for regression testing.
+ */
+static const char *test_users[] = {
+ "root",
+ "daemon",
+ "operator",
+ "bin",
+};
+
+static const char *test_groups[] = {
+ "wheel",
+ "daemon",
+ "operator",
+ "bin",
+};
+
+static int test_num;
+
+/*
+ * List of test strings that must go in (and come out) of libugidfw intact.
+ */
+static const char *test_strings[] = {
+ /* Variations on subject and object uids. */
+ "subject uid root object uid root mode n",
+ "subject uid root object uid daemon mode n",
+ "subject uid daemon object uid root mode n",
+ "subject uid daemon object uid daemon mode n",
+ /* Variations on mode. */
+ "subject uid root object uid root mode a",
+ "subject uid root object uid root mode r",
+ "subject uid root object uid root mode s",
+ "subject uid root object uid root mode w",
+ "subject uid root object uid root mode x",
+ "subject uid root object uid root mode arswx",
+ /* Variations on subject and object gids. */
+ "subject gid wheel object gid wheel mode n",
+ "subject gid wheel object gid daemon mode n",
+ "subject gid daemon object gid wheel mode n",
+ "subject gid daemon object gid daemon mode n",
+ /* Subject uids and subject gids. */
+ "subject uid bin gid daemon object uid operator gid wheel mode n",
+ /* Not */
+ "subject not uid operator object uid bin mode n",
+ "subject uid bin object not uid operator mode n",
+ "subject not uid daemon object not uid operator mode n",
+ /* Ranges */
+ "subject uid root:operator object gid wheel:bin mode n",
+ /* Jail ID */
+ "subject jailid 1 object uid root mode n",
+ /* Filesys */
+ "subject uid root object filesys / mode n",
+ "subject uid root object filesys /dev mode n",
+ /* S/UGID */
+ "subject not uid root object sgid mode n",
+ "subject not uid root object sgid mode n",
+ /* Matching uid/gid */
+ "subject not uid root:operator object not uid_of_subject mode n",
+ "subject not gid wheel:bin object not gid_of_subject mode n",
+ /* Object types */
+ "subject uid root object type a mode a",
+ "subject uid root object type r mode a",
+ "subject uid root object type d mode a",
+ "subject uid root object type b mode a",
+ "subject uid root object type c mode a",
+ "subject uid root object type l mode a",
+ "subject uid root object type s mode a",
+ "subject uid root object type rbc mode a",
+ "subject uid root object type dls mode a",
+ /* Empty rules always match */
+ "subject object mode a",
+ /* Partial negations */
+ "subject ! uid root object mode n",
+ "subject ! gid wheel object mode n",
+ "subject ! jailid 2 object mode n",
+ "subject object ! uid root mode n",
+ "subject object ! gid wheel mode n",
+ "subject object ! filesys / mode n",
+ "subject object ! suid mode n",
+ "subject object ! sgid mode n",
+ "subject object ! uid_of_subject mode n",
+ "subject object ! gid_of_subject mode n",
+ "subject object ! type d mode n",
+ /* All out nonsense */
+ "subject uid root ! gid wheel:bin ! jailid 1 "
+ "object ! uid root:daemon gid daemon filesys / suid sgid uid_of_subject gid_of_subject ! type r "
+ "mode rsx",
+};
+
+static void
+test_libugidfw_strings(void)
+{
+ struct mac_bsdextended_rule rule;
+ char errorstr[256];
+ char rulestr[256];
+ size_t i;
+ int error;
+
+ for (i = 0; i < nitems(test_users); i++, test_num++) {
+ if (getpwnam(test_users[i]) == NULL)
+ printf("not ok %d # test_libugidfw_strings: getpwnam(%s) "
+ "failed: %s\n", test_num, test_users[i], strerror(errno));
+ else
+ printf("ok %d\n", test_num);
+ }
+
+ for (i = 0; i < nitems(test_groups); i++, test_num++) {
+ if (getgrnam(test_groups[i]) == NULL)
+ printf("not ok %d # test_libugidfw_strings: getgrnam(%s) "
+ "failed: %s\n", test_num, test_groups[i], strerror(errno));
+ else
+ printf("ok %d\n", test_num);
+ }
+
+ for (i = 0; i < nitems(test_strings); i++) {
+ error = bsde_parse_rule_string(test_strings[i], &rule,
+ sizeof(errorstr), errorstr);
+ if (error == -1)
+ printf("not ok %d # bsde_parse_rule_string: '%s' (%zu) "
+ "failed: %s\n", test_num, test_strings[i], i, errorstr);
+ else
+ printf("ok %d\n", test_num);
+ test_num++;
+
+ error = bsde_rule_to_string(&rule, rulestr, sizeof(rulestr));
+ if (error < 0)
+ printf("not ok %d # bsde_rule_to_string: rule for '%s' "
+ "returned %d\n", test_num, test_strings[i], error);
+ else
+ printf("ok %d\n", test_num);
+ test_num++;
+
+ if (strcmp(test_strings[i], rulestr) != 0)
+ printf("not ok %d # test_libugidfw: '%s' in, '%s' "
+ "out\n", test_num, test_strings[i], rulestr);
+ else
+ printf("ok %d\n", test_num);
+ test_num++;
+ }
+}
+
+int
+main(void)
+{
+ char errorstr[256];
+ int count, slots;
+
+ test_num = 1;
+
+ /* Print an error if a non-root user attemps to run the tests. */
+ if (getuid() != 0) {
+ printf("1..0 # SKIP you must be root\n");
+ return (0);
+ }
+
+ switch (mac_is_present("bsdextended")) {
+ case -1:
+ printf("1..0 # SKIP mac_is_present failed: %s\n",
+ strerror(errno));
+ return (0);
+ case 1:
+ break;
+ case 0:
+ default:
+ printf("1..0 # SKIP mac_bsdextended not loaded\n");
+ return (0);
+ }
+
+ printf("1..%zu\n", nitems(test_users) + nitems(test_groups) +
+ 3 * nitems(test_strings) + 2);
+
+ test_libugidfw_strings();
+
+ /*
+ * Some simple up-front checks to see if we're able to query the
+ * policy for basic state. We want the rule count to be 0 before
+ * starting, but "slots" is a property of prior runs and so we ignore
+ * the return value.
+ */
+ count = bsde_get_rule_count(sizeof(errorstr), errorstr);
+ if (count == -1)
+ printf("not ok %d # bsde_get_rule_count: %s\n", test_num,
+ errorstr);
+ else
+ printf("ok %d\n", test_num);
+
+ test_num++;
+
+ slots = bsde_get_rule_slots(sizeof(errorstr), errorstr);
+ if (slots == -1)
+ printf("not ok %d # bsde_get_rule_slots: %s\n", test_num,
+ errorstr);
+ else
+ printf("ok %d\n", test_num);
+
+ return (0);
+}
diff --git a/tests/sys/mac/ipacl/Makefile b/tests/sys/mac/ipacl/Makefile
new file mode 100644
index 000000000000..e083f6c1a69c
--- /dev/null
+++ b/tests/sys/mac/ipacl/Makefile
@@ -0,0 +1,9 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/mac/ipacl
+
+ATF_TESTS_SH+= ipacl_test
+
+${PACKAGE}FILES+= utils.subr
+
+.include <bsd.test.mk>
diff --git a/tests/sys/mac/ipacl/ipacl_test.sh b/tests/sys/mac/ipacl/ipacl_test.sh
new file mode 100644
index 000000000000..0de1b414857b
--- /dev/null
+++ b/tests/sys/mac/ipacl/ipacl_test.sh
@@ -0,0 +1,281 @@
+#-
+# Copyright (c) 2019, 2023 Shivank Garg <shivank@FreeBSD.org>
+#
+# This code was developed as a Google Summer of Code 2019 project
+# under the guidance of Bjoern A. Zeeb.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "ipacl_v4" "cleanup"
+
+ipacl_v4_head()
+{
+ atf_set descr 'basic test for ipacl on IPv4 addresses'
+ atf_set require.user root
+}
+
+ipacl_v4_body()
+{
+ ipacl_test_init
+
+ epairA=$(vnet_mkepair)
+ epairB=$(vnet_mkepair)
+ epairC=$(vnet_mkepair)
+
+ vnet_mkjail A ${epairA}b
+ vnet_mkjail B ${epairB}b ${epairC}b
+
+ jidA=$(jls -j A -s jid | grep -o -E '[0-9]+')
+ jidB=$(jls -j B -s jid | grep -o -E '[0-9]+')
+
+ # The ipacl policy module is not enforced for IPv4.
+ sysctl security.mac.ipacl.ipv4=0
+
+ atf_check -s exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b 192.0.2.2/24 up
+ atf_check -s exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b 203.0.113.254/24 up
+
+ # The ipacl policy module is enforced for IPv4 and prevent all
+ # jails from setting their IPv4 address.
+ sysctl security.mac.ipacl.ipv4=1
+ sysctl security.mac.ipacl.rules=
+
+ atf_check -s not-exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b 192.0.2.2/24 up
+ atf_check -s not-exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b 203.0.113.254/24 up
+
+ rule="${jidA},1,${epairA}b,AF_INET,192.0.2.42/-1@"
+ rule="${rule}${jidB},1,${epairB}b,AF_INET,198.51.100.12/-1@"
+ rule="${rule}${jidB},1,,AF_INET,203.0.113.1/24@"
+ rule="${rule}${jidB},0,,AF_INET,203.0.113.9/-1"
+ sysctl security.mac.ipacl.rules="${rule}"
+
+ # Verify if it allows jail to set only certain IPv4 address.
+ atf_check -s exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b 192.0.2.42/24 up
+ atf_check -s not-exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b 192.0.2.43/24 up
+ atf_check -s exit:0 -e ignore \
+ jexec B ifconfig ${epairB}b 198.51.100.12/24 up
+ atf_check -s not-exit:0 -e ignore \
+ jexec B ifconfig ${epairC}b 198.51.100.12/24 up
+
+ # Verify if the module allow jail to set any address in subnet.
+ atf_check -s exit:0 -e ignore \
+ jexec B ifconfig ${epairB}b 203.0.113.19/24 up
+ atf_check -s exit:0 -e ignore \
+ jexec B ifconfig ${epairB}b 203.0.113.241/24 up
+ atf_check -s not-exit:0 -e ignore \
+ jexec B ifconfig ${epairB}b 198.18.0.1/24 up
+ atf_check -s not-exit:0 -e ignore \
+ jexec B ifconfig ${epairB}b 203.0.113.9/24 up
+
+ # Check wildcard for interfaces.
+ atf_check -s exit:0 -e ignore \
+ jexec B ifconfig ${epairC}b 203.0.113.20/24 up
+ atf_check -s exit:0 -e ignore \
+ jexec B ifconfig ${epairC}b 203.0.113.242/24 up
+ atf_check -s not-exit:0 -e ignore \
+ jexec B ifconfig ${epairC}b 198.18.0.1/24 up
+ atf_check -s not-exit:0 -e ignore \
+ jexec B ifconfig ${epairC}b 203.0.113.9/24 up
+
+ rule="${jidA},1,,AF_INET,198.18.0.0/15@"
+ rule="${rule}${jidA},0,,AF_INET,198.18.23.0/24@"
+ rule="${rule}${jidA},1,,AF_INET,198.18.23.1/-1@"
+ rule="${rule}${jidA},1,,AF_INET,198.51.100.0/24@"
+ rule="${rule}${jidA},0,,AF_INET,198.51.100.100/-1"
+ sysctl security.mac.ipacl.rules="${rule}"
+
+ # Tests from Benchamarking and Documentation(TEST-NET-3).
+ atf_check -s exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b 198.18.0.1/24 up
+ atf_check -s not-exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b 198.18.23.2/24 up
+ atf_check -s exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b 198.18.23.1/22 up
+ atf_check -s not-exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b 198.18.23.3/24 up
+
+ atf_check -s exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b 198.51.100.001/24 up
+ atf_check -s exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b 198.51.100.254/24 up
+ atf_check -s not-exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b 198.51.100.100/24 up
+ atf_check -s not-exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b 203.0.113.1/24 up
+
+ # Reset rules OID.
+ sysctl security.mac.ipacl.rules=
+}
+
+ipacl_v4_cleanup()
+{
+ ipacl_test_cleanup
+}
+
+atf_test_case "ipacl_v6" "cleanup"
+
+ipacl_v6_head()
+{
+ atf_set descr 'basic test for ipacl on IPv6 addresses'
+ atf_set require.user root
+}
+
+ipacl_v6_body()
+{
+ ipacl_test_init
+
+ epairA=$(vnet_mkepair)
+ epairB=$(vnet_mkepair)
+ epairC=$(vnet_mkepair)
+
+ vnet_mkjail A ${epairA}b
+ vnet_mkjail B ${epairB}b ${epairC}b
+
+ jidA=$(jls -j A -s jid | grep -o -E '[0-9]+')
+ jidB=$(jls -j B -s jid | grep -o -E '[0-9]+')
+
+ # The ipacl policy module is not enforced for IPv6.
+ sysctl security.mac.ipacl.ipv6=0
+
+ atf_check -s exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b inet6 2001:2::abcd/48 up
+ atf_check -s exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b inet6 2001:2::5ea:11/48 up
+
+ # The ipacl policy module is enforced for IPv6 and prevent all
+ # jails from setting their IPv6 address.
+ sysctl security.mac.ipacl.ipv6=1
+ sysctl security.mac.ipacl.rules=
+
+ atf_check -s not-exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b inet6 2001:2::abcd/48 up
+ atf_check -s not-exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b inet6 2001:2::5ea:11/48 up
+
+ rule="${jidA},1,${epairA}b,AF_INET6,2001:db8::1111/-1@"
+ rule="${rule}${jidB},1,${epairB}b,AF_INET6,2001:2::1234:1234/-1@"
+ rule="${rule}${jidB},1,,AF_INET6,fe80::/32@"
+ rule="${rule}${jidB},0,,AF_INET6,fe80::abcd/-1"
+ sysctl security.mac.ipacl.rules="${rule}"
+
+ # Verify if it allows jail to set only certain IPv6 address.
+ atf_check -s exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b inet6 2001:db8::1111/64 up
+ atf_check -s not-exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b inet6 2001:db8::1112/64 up
+ atf_check -s exit:0 -e ignore \
+ jexec B ifconfig ${epairB}b inet6 2001:2::1234:1234/48 up
+ atf_check -s not-exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b inet6 2001:2::1234:1234/48 up
+
+ # Verify if the module allow jail set any address in subnet.
+ atf_check -s exit:0 -e ignore \
+ jexec B ifconfig ${epairB}b inet6 FE80::1101:1221/15 up
+ atf_check -s exit:0 -e ignore \
+ jexec B ifconfig ${epairB}b inet6 FE80::abab/15 up
+ atf_check -s exit:0 -e ignore \
+ jexec B ifconfig ${epairB}b inet6 FE80::1/64 up
+ atf_check -s not-exit:0 -e ignore \
+ jexec B ifconfig ${epairB}b inet6 FE80::abcd/15 up
+
+ # Check wildcard for interfaces.
+ atf_check -s exit:0 -e ignore \
+ jexec B ifconfig ${epairC}b inet6 FE80::1101:1221/15 up
+ atf_check -s exit:0 -e ignore \
+ jexec B ifconfig ${epairC}b inet6 FE80::abab/32 up
+ atf_check -s not-exit:0 -e ignore \
+ jexec B ifconfig ${epairC}b inet6 FE81::1/64 up
+ atf_check -s not-exit:0 -e ignore \
+ jexec B ifconfig ${epairC}b inet6 FE80::abcd/32 up
+
+ rule="${jidB},1,,AF_INET6,2001:2::/48@"
+ rule="${rule}${jidB},1,,AF_INET6,2001:3::/32"
+ sysctl security.mac.ipacl.rules="${rule}"
+
+ # Tests when subnet is allowed.
+ atf_check -s not-exit:0 -e ignore \
+ jexec B ifconfig ${epairC}b inet6 2001:2:0001::1/64 up
+ atf_check -s not-exit:0 -e ignore \
+ jexec B ifconfig ${epairC}b inet6 2001:2:1000::1/32 up
+ atf_check -s exit:0 -e ignore \
+ jexec B ifconfig ${epairC}b inet6 2001:3:0001::1/64 up
+ atf_check -s not-exit:0 -e ignore \
+ jexec B ifconfig ${epairC}b inet6 2001:4::1/64 up
+
+ # More tests of ULA address space.
+ rule="${jidA},1,,AF_INET6,fc00::/7@"
+ rule="${rule}${jidA},0,,AF_INET6,fc00::1111:2200/120@"
+ rule="${rule}${jidA},1,,AF_INET6,fc00::1111:2299/-1@"
+ rule="${rule}${jidA},1,,AF_INET6,2001:db8::/32@"
+ rule="${rule}${jidA},0,,AF_INET6,2001:db8::abcd/-1"
+ sysctl security.mac.ipacl.rules="${rule}"
+
+ atf_check -s exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b inet6 fc00::0000:1234/48 up
+ atf_check -s exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b inet6 fc00::0000:1234/48 up
+ atf_check -s not-exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b inet6 f800::2222:2200/48 up
+ atf_check -s not-exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b inet6 f800::2222:22ff/48 up
+
+ atf_check -s exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b inet6 fc00::1111:2111/64 up
+ atf_check -s not-exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b inet6 fc00::1111:2211/64 up
+ atf_check -s not-exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b inet6 fc00::1111:22aa/48 up
+ atf_check -s exit:0 -e ignore \
+ jexec A ifconfig ${epairA}b inet6 fc00::1111:2299/48 up
+
+ # More tests from IPv6 documentation range.
+ atf_check -s exit:0 -e ignore jexec A ifconfig \
+ ${epairA}b inet6 2001:db8:abcd:bcde:cdef:def1:ef12:f123/32 up
+ atf_check -s exit:0 -e ignore jexec A ifconfig \
+ ${epairA}b inet6 2001:db8:1111:2222:3333:4444:5555:6666/32 up
+ atf_check -s not-exit:0 -e ignore jexec A ifconfig \
+ ${epairA}b inet6 2001:ab9:1111:2222:3333:4444:5555:6666/32 up
+ atf_check -s not-exit:0 -e ignore jexec A ifconfig \
+ ${epairA}b inet6 2001:db8::abcd/32 up
+
+ # Reset rules OID.
+ sysctl security.mac.ipacl.rules=
+}
+
+ipacl_v6_cleanup()
+{
+ ipacl_test_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "ipacl_v4"
+ atf_add_test_case "ipacl_v6"
+}
diff --git a/tests/sys/mac/ipacl/utils.subr b/tests/sys/mac/ipacl/utils.subr
new file mode 100644
index 000000000000..1d80414bafea
--- /dev/null
+++ b/tests/sys/mac/ipacl/utils.subr
@@ -0,0 +1,17 @@
+# Utility functions for mac_ipacl tests
+
+. $(atf_get_srcdir)/../../common/vnet.subr
+
+ipacl_test_init()
+{
+ vnet_init
+
+ if ! kldstat -q -m mac_ipacl; then
+ atf_skip "mac_ipacl is not loaded"
+ fi
+}
+
+ipacl_test_cleanup()
+{
+ vnet_cleanup
+}
diff --git a/tests/sys/mac/portacl/LICENSE b/tests/sys/mac/portacl/LICENSE
new file mode 100644
index 000000000000..5f14b0583e6d
--- /dev/null
+++ b/tests/sys/mac/portacl/LICENSE
@@ -0,0 +1,26 @@
+
+License for all mac_portacl regression tests:
+
+Copyright (c) 2009 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/tests/sys/mac/portacl/Makefile b/tests/sys/mac/portacl/Makefile
new file mode 100644
index 000000000000..856a85d331d5
--- /dev/null
+++ b/tests/sys/mac/portacl/Makefile
@@ -0,0 +1,16 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/mac/portacl
+
+${PACKAGE}FILES+= misc.sh
+
+TAP_TESTS_SH+= nobody_test
+TAP_TESTS_SH+= root_test
+
+.for t in ${TAP_TESTS_SH}
+TEST_METADATA.$t+= required_user="root"
+TEST_METADATA.$t+= timeout="450"
+TEST_METADATA.$t+= is_exclusive="true"
+.endfor
+
+.include <bsd.test.mk>
diff --git a/tests/sys/mac/portacl/Makefile.depend b/tests/sys/mac/portacl/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/mac/portacl/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/mac/portacl/misc.sh b/tests/sys/mac/portacl/misc.sh
new file mode 100644
index 000000000000..a1b729c87777
--- /dev/null
+++ b/tests/sys/mac/portacl/misc.sh
@@ -0,0 +1,106 @@
+#!/bin/sh
+
+sysctl security.mac.portacl >/dev/null 2>&1
+if [ $? -ne 0 ]; then
+ echo "1..0 # SKIP MAC_PORTACL is unavailable."
+ exit 0
+fi
+if [ $(id -u) -ne 0 ]; then
+ echo "1..0 # SKIP testcases must be run as root"
+ exit 0
+fi
+
+ntest=1
+
+check_bind() {
+ local host idtype name proto port udpflag
+
+ host="127.0.0.1"
+ timeout=1
+
+ idtype=${1}
+ name=${2}
+ proto=${3}
+ port=${4}
+
+ [ "${proto}" = "udp" ] && udpflag="-u"
+
+ out=$(
+ case "${idtype}" in
+ uid|gid)
+ ( echo -n | su -m ${name} -c "nc ${udpflag} -l -w ${timeout} $host $port" 2>&1 ) &
+ ;;
+ jail)
+ kill $$
+ ;;
+ *)
+ kill $$
+ esac
+ sleep 0.3
+ echo | nc ${udpflag} -w ${timeout} $host $port >/dev/null 2>&1
+ wait
+ )
+ case "${out}" in
+ "nc: Permission denied"*|"nc: Operation not permitted"*)
+ echo fl
+ ;;
+ "")
+ echo ok
+ ;;
+ *)
+ echo ${out}
+ ;;
+ esac
+}
+
+bind_test() {
+ local expect_without_rule expect_with_rule idtype name proto port
+
+ expect_without_rule=${1}
+ expect_with_rule=${2}
+ idtype=${3}
+ name=${4}
+ proto=${5}
+ port=${6}
+
+ sysctl security.mac.portacl.rules= >/dev/null
+ out=$(check_bind ${idtype} ${name} ${proto} ${port})
+ if [ "${out}" = "${expect_without_rule}" ]; then
+ echo "ok ${ntest}"
+ elif [ "${out}" = "ok" -o "${out}" = "fl" ]; then
+ echo "not ok ${ntest} # '${out}' != '${expect_without_rule}'"
+ else
+ echo "not ok ${ntest} # unexpected output: '${out}'"
+ fi
+ : $(( ntest += 1 ))
+
+ if [ "${idtype}" = "uid" ]; then
+ idstr=$(id -u ${name})
+ elif [ "${idtype}" = "gid" ]; then
+ idstr=$(id -g ${name})
+ else
+ idstr=${name}
+ fi
+ sysctl security.mac.portacl.rules=${idtype}:${idstr}:${proto}:${port} >/dev/null
+ out=$(check_bind ${idtype} ${name} ${proto} ${port})
+ if [ "${out}" = "${expect_with_rule}" ]; then
+ echo "ok ${ntest}"
+ elif [ "${out}" = "ok" -o "${out}" = "fl" ]; then
+ echo "not ok ${ntest} # '${out}' != '${expect_with_rule}'"
+ else
+ echo "not ok ${ntest} # unexpected output: '${out}'"
+ fi
+ : $(( ntest += 1 ))
+
+ sysctl security.mac.portacl.rules= >/dev/null
+}
+
+reserved_high=$(sysctl -n net.inet.ip.portrange.reservedhigh)
+suser_exempt=$(sysctl -n security.mac.portacl.suser_exempt)
+port_high=$(sysctl -n security.mac.portacl.port_high)
+
+restore_settings() {
+ sysctl -n net.inet.ip.portrange.reservedhigh=${reserved_high} >/dev/null
+ sysctl -n security.mac.portacl.suser_exempt=${suser_exempt} >/dev/null
+ sysctl -n security.mac.portacl.port_high=${port_high} >/dev/null
+}
diff --git a/tests/sys/mac/portacl/nobody_test.sh b/tests/sys/mac/portacl/nobody_test.sh
new file mode 100644
index 000000000000..7e64f68113ea
--- /dev/null
+++ b/tests/sys/mac/portacl/nobody_test.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+
+dir=`dirname $0`
+. ${dir}/misc.sh
+
+echo "1..64"
+
+# security.mac.portacl.suser_exempt value doesn't affect unprivileged users
+# behaviour.
+# mac_portacl has no impact on ports <= net.inet.ip.portrange.reservedhigh.
+
+trap restore_settings EXIT INT TERM
+
+sysctl security.mac.portacl.suser_exempt=1 >/dev/null
+sysctl net.inet.ip.portrange.reservedhigh=78 >/dev/null
+
+bind_test fl fl uid nobody tcp 77
+bind_test ok ok uid nobody tcp 7777
+bind_test fl fl uid nobody udp 77
+bind_test ok ok uid nobody udp 7777
+
+bind_test fl fl gid nobody tcp 77
+bind_test ok ok gid nobody tcp 7777
+bind_test fl fl gid nobody udp 77
+bind_test ok ok gid nobody udp 7777
+
+sysctl security.mac.portacl.suser_exempt=0 >/dev/null
+
+bind_test fl fl uid nobody tcp 77
+bind_test ok ok uid nobody tcp 7777
+bind_test fl fl uid nobody udp 77
+bind_test ok ok uid nobody udp 7777
+
+bind_test fl fl gid nobody tcp 77
+bind_test ok ok gid nobody tcp 7777
+bind_test fl fl gid nobody udp 77
+bind_test ok ok gid nobody udp 7777
+
+# Verify if security.mac.portacl.port_high works.
+
+sysctl security.mac.portacl.port_high=7778 >/dev/null
+
+bind_test fl fl uid nobody tcp 77
+bind_test fl ok uid nobody tcp 7777
+bind_test fl fl uid nobody udp 77
+bind_test fl ok uid nobody udp 7777
+
+bind_test fl fl gid nobody tcp 77
+bind_test fl ok gid nobody tcp 7777
+bind_test fl fl gid nobody udp 77
+bind_test fl ok gid nobody udp 7777
+
+# Verify if mac_portacl rules work.
+
+sysctl net.inet.ip.portrange.reservedhigh=76 >/dev/null
+sysctl security.mac.portacl.port_high=7776 >/dev/null
+
+bind_test fl ok uid nobody tcp 77
+bind_test ok ok uid nobody tcp 7777
+bind_test fl ok uid nobody udp 77
+bind_test ok ok uid nobody udp 7777
+
+bind_test fl ok gid nobody tcp 77
+bind_test ok ok gid nobody tcp 7777
+bind_test fl ok gid nobody udp 77
+bind_test ok ok gid nobody udp 7777
diff --git a/tests/sys/mac/portacl/root_test.sh b/tests/sys/mac/portacl/root_test.sh
new file mode 100644
index 000000000000..daa5b147b4fa
--- /dev/null
+++ b/tests/sys/mac/portacl/root_test.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+dir=`dirname $0`
+. ${dir}/misc.sh
+
+echo "1..48"
+
+# Verify if security.mac.portacl.suser_exempt=1 really exempts super-user.
+
+trap restore_settings EXIT INT TERM
+
+sysctl security.mac.portacl.suser_exempt=1 >/dev/null
+
+bind_test ok ok uid root tcp 77
+bind_test ok ok uid root tcp 7777
+bind_test ok ok uid root udp 77
+bind_test ok ok uid root udp 7777
+
+bind_test ok ok gid root tcp 77
+bind_test ok ok gid root tcp 7777
+bind_test ok ok gid root udp 77
+bind_test ok ok gid root udp 7777
+
+# Verify if security.mac.portacl.suser_exempt=0 really doesn't exempt super-user.
+
+sysctl security.mac.portacl.suser_exempt=0 >/dev/null
+
+bind_test fl ok uid root tcp 77
+bind_test ok ok uid root tcp 7777
+bind_test fl ok uid root udp 77
+bind_test ok ok uid root udp 7777
+
+bind_test fl ok gid root tcp 77
+bind_test ok ok gid root tcp 7777
+bind_test fl ok gid root udp 77
+bind_test ok ok gid root udp 7777
+
+# Verify if security.mac.portacl.port_high works for super-user.
+
+sysctl security.mac.portacl.port_high=7778 >/dev/null
+
+bind_test fl ok uid root tcp 77
+bind_test fl ok uid root tcp 7777
+bind_test fl ok uid root udp 77
+bind_test fl ok uid root udp 7777
+
+bind_test fl ok gid root tcp 77
+bind_test fl ok gid root tcp 7777
+bind_test fl ok gid root udp 77
+bind_test fl ok gid root udp 7777
diff --git a/tests/sys/mqueue/Makefile b/tests/sys/mqueue/Makefile
new file mode 100644
index 000000000000..743c82ecc954
--- /dev/null
+++ b/tests/sys/mqueue/Makefile
@@ -0,0 +1,17 @@
+TESTSDIR= ${TESTSBASE}/sys/mqueue
+
+ATF_TESTS_SH= mqueue_test
+
+BINDIR= ${TESTSDIR}
+
+CFLAGS+= -I${SRCTOP}/tests
+
+PROGS+= mqtest1
+PROGS+= mqtest2
+PROGS+= mqtest3
+PROGS+= mqtest4
+PROGS+= mqtest5
+
+LIBADD+= rt
+
+.include <bsd.test.mk>
diff --git a/tests/sys/mqueue/Makefile.depend b/tests/sys/mqueue/Makefile.depend
new file mode 100644
index 000000000000..fd5dbd04cba7
--- /dev/null
+++ b/tests/sys/mqueue/Makefile.depend
@@ -0,0 +1,19 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/librt \
+ lib/libthr \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/mqueue/mqtest1.c b/tests/sys/mqueue/mqtest1.c
new file mode 100644
index 000000000000..78acde1122ce
--- /dev/null
+++ b/tests/sys/mqueue/mqtest1.c
@@ -0,0 +1,56 @@
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <mqueue.h>
+#include <signal.h>
+#include <stdio.h>
+
+#include "freebsd_test_suite/macros.h"
+
+#define MQNAME "/mytstqueue1"
+
+int
+main(void)
+{
+ struct mq_attr attr, attr2;
+ struct sigevent sigev;
+ mqd_t mq;
+ int status;
+
+ PLAIN_REQUIRE_KERNEL_MODULE("mqueuefs", 0);
+
+ attr.mq_maxmsg = 2;
+ attr.mq_msgsize = 100;
+ mq = mq_open(MQNAME, O_CREAT | O_RDWR | O_EXCL, 0666, &attr);
+ if (mq == (mqd_t)-1)
+ err(1, "mq_open");
+ status = mq_unlink(MQNAME);
+ if (status)
+ err(1, "mq_unlink");
+ status = mq_getattr(mq, &attr2);
+ if (status)
+ err(1, "mq_getattr");
+ if (attr.mq_maxmsg != attr2.mq_maxmsg)
+ err(1, "mq_maxmsg changed");
+ if (attr.mq_msgsize != attr2.mq_msgsize)
+ err(1, "mq_msgsize changed");
+
+ sigev.sigev_notify = SIGEV_SIGNAL;
+ sigev.sigev_signo = SIGRTMIN;
+ status = mq_notify(mq, &sigev);
+ if (status)
+ err(1, "mq_notify");
+ status = mq_notify(mq, &sigev);
+ if (status == 0)
+ err(1, "mq_notify 2");
+ else if (errno != EBUSY)
+ err(1, "mq_notify 3");
+ status = mq_notify(mq, NULL);
+ if (status)
+ err(1, "mq_notify 4");
+ status = mq_close(mq);
+ if (status)
+ err(1, "mq_close");
+ return (0);
+}
diff --git a/tests/sys/mqueue/mqtest2.c b/tests/sys/mqueue/mqtest2.c
new file mode 100644
index 000000000000..08d4d9a8003a
--- /dev/null
+++ b/tests/sys/mqueue/mqtest2.c
@@ -0,0 +1,100 @@
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <err.h>
+#include <fcntl.h>
+#include <mqueue.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "freebsd_test_suite/macros.h"
+
+#define MQNAME "/mytstqueue2"
+#define LOOPS 1000
+#define PRIO 10
+
+static void
+alarmhandler(int sig __unused)
+{
+ write(1, "timeout\n", 8);
+ _exit(1);
+}
+
+int
+main(void)
+{
+ struct mq_attr attr;
+ mqd_t mq;
+ int status;
+ pid_t pid;
+
+ PLAIN_REQUIRE_KERNEL_MODULE("mqueuefs", 0);
+
+ mq_unlink(MQNAME);
+
+ attr.mq_maxmsg = 5;
+ attr.mq_msgsize = 128;
+ mq = mq_open(MQNAME, O_CREAT | O_RDWR | O_EXCL, 0666, &attr);
+ if (mq == (mqd_t)-1)
+ err(1, "mq_open");
+ status = mq_getattr(mq, &attr);
+ if (status)
+ err(1, "mq_getattr");
+ pid = fork();
+ if (pid == 0) { /* child */
+ char *buf;
+ int j, i;
+ unsigned int prio;
+
+ mq_close(mq);
+
+ signal(SIGALRM, alarmhandler);
+
+ mq = mq_open(MQNAME, O_RDWR);
+ if (mq == (mqd_t)-1)
+ err(1, "child: mq_open");
+ buf = malloc(attr.mq_msgsize);
+ for (j = 0; j < LOOPS; ++j) {
+ alarm(3);
+ status = mq_receive(mq, buf, attr.mq_msgsize, &prio);
+ if (status == -1)
+ err(2, "child: mq_receive");
+ for (i = 0; i < attr.mq_msgsize; ++i)
+ if (buf[i] != i)
+ err(3, "child: message data corrupted");
+ if (prio != PRIO)
+ err(4, "child: priority is incorrect: %d",
+ prio);
+ }
+ alarm(0);
+ free(buf);
+ mq_close(mq);
+ return (0);
+ } else if (pid == -1) {
+ err(1, "fork()");
+ } else {
+ char *buf;
+ int i, j;
+
+ signal(SIGALRM, alarmhandler);
+ buf = malloc(attr.mq_msgsize);
+ for (j = 0; j < LOOPS; ++j) {
+ for (i = 0; i < attr.mq_msgsize; ++i)
+ buf[i] = i;
+ alarm(3);
+ status = mq_send(mq, buf, attr.mq_msgsize, PRIO);
+ if (status)
+ err(1, "mq_send");
+ }
+ alarm(3);
+ wait(&status);
+ alarm(0);
+ }
+ status = mq_close(mq);
+ if (status)
+ err(1, "mq_close");
+ mq_unlink(MQNAME);
+ return (0);
+}
diff --git a/tests/sys/mqueue/mqtest3.c b/tests/sys/mqueue/mqtest3.c
new file mode 100644
index 000000000000..65b8f4fcc2b9
--- /dev/null
+++ b/tests/sys/mqueue/mqtest3.c
@@ -0,0 +1,117 @@
+
+#include <sys/types.h>
+#include <sys/select.h>
+#include <sys/wait.h>
+#include <err.h>
+#include <fcntl.h>
+#include <mqueue.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "freebsd_test_suite/macros.h"
+
+#define MQNAME "/mytstqueue3"
+#define LOOPS 1000
+#define PRIO 10
+
+static void
+sighandler(int sig __unused)
+{
+ write(1, "timeout\n", 8);
+ _exit(1);
+}
+
+int
+main(void)
+{
+ fd_set set;
+ struct mq_attr attr;
+ int status;
+ mqd_t mq;
+ pid_t pid;
+
+ PLAIN_REQUIRE_KERNEL_MODULE("mqueuefs", 0);
+
+ mq_unlink(MQNAME);
+
+ attr.mq_maxmsg = 5;
+ attr.mq_msgsize = 128;
+ mq = mq_open(MQNAME, O_CREAT | O_RDWR | O_EXCL, 0666, &attr);
+ if (mq == (mqd_t)-1)
+ err(1, "mq_open()");
+ status = mq_getattr(mq, &attr);
+ if (status)
+ err(1, "mq_getattr()");
+
+ pid = fork();
+ if (pid == 0) { /* child */
+ char *buf;
+ int j, i;
+ unsigned int prio;
+
+ mq_close(mq);
+
+ signal(SIGALRM, sighandler);
+
+ mq = mq_open(MQNAME, O_RDWR);
+ if (mq == (mqd_t)-1)
+ err(1, "child process: mq_open");
+ buf = malloc(attr.mq_msgsize);
+ for (j = 0; j < LOOPS; ++j) {
+ FD_ZERO(&set);
+ FD_SET(mq_getfd_np(mq), &set);
+ alarm(3);
+ status = select(mq_getfd_np(mq) + 1, &set, NULL,
+ NULL, NULL);
+ if (status != 1)
+ err(1, "child process: select()");
+ status = mq_receive(mq, buf, attr.mq_msgsize, &prio);
+ if (status == -1)
+ err(2, "child process: mq_receive");
+ for (i = 0; i < attr.mq_msgsize; ++i)
+ if (buf[i] != i)
+ err(3, "message data corrupted");
+ if (prio != PRIO)
+ err(4, "priority is incorrect: %d", prio);
+ }
+ alarm(0);
+ free(buf);
+ mq_close(mq);
+ return (0);
+ } else if (pid == -1) {
+ err(1, "fork()");
+ } else {
+ char *buf;
+ int i, j;
+
+ signal(SIGALRM, sighandler);
+ buf = malloc(attr.mq_msgsize);
+ for (j = 0; j < LOOPS; ++j) {
+ for (i = 0; i < attr.mq_msgsize; ++i) {
+ buf[i] = i;
+ }
+ alarm(3);
+ FD_ZERO(&set);
+ FD_SET(mq_getfd_np(mq), &set);
+ status = select(mq_getfd_np(mq) + 1, NULL, &set,
+ NULL, NULL);
+ if (status != 1)
+ err(1, "select()");
+ status = mq_send(mq, buf, attr.mq_msgsize, PRIO);
+ if (status) {
+ kill(pid, SIGKILL);
+ err(2, "mq_send()");
+ }
+ }
+ alarm(3);
+ wait(&status);
+ alarm(0);
+ }
+ status = mq_close(mq);
+ if (status)
+ err(1, "mq_close");
+ mq_unlink(MQNAME);
+ return (0);
+}
diff --git a/tests/sys/mqueue/mqtest4.c b/tests/sys/mqueue/mqtest4.c
new file mode 100644
index 000000000000..99841c670b5c
--- /dev/null
+++ b/tests/sys/mqueue/mqtest4.c
@@ -0,0 +1,119 @@
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/select.h>
+#include <sys/wait.h>
+#include <err.h>
+#include <fcntl.h>
+#include <mqueue.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "freebsd_test_suite/macros.h"
+
+#define MQNAME "/mytstqueue4"
+#define LOOPS 1000
+#define PRIO 10
+
+static void
+sighandler(int sig __unused)
+{
+ write(1, "timeout\n", 8);
+ _exit(1);
+}
+
+int
+main(void)
+{
+ struct kevent kev;
+ struct mq_attr attr;
+ mqd_t mq;
+ int kq, status;
+ pid_t pid;
+
+ PLAIN_REQUIRE_KERNEL_MODULE("mqueuefs", 0);
+
+ mq_unlink(MQNAME);
+
+ attr.mq_maxmsg = 5;
+ attr.mq_msgsize = 128;
+ mq = mq_open(MQNAME, O_CREAT | O_RDWR | O_EXCL, 0666, &attr);
+ if (mq == (mqd_t) -1)
+ err(1, "mq_open()");
+ status = mq_getattr(mq, &attr);
+ if (status)
+ err(1, "mq_getattr()");
+ pid = fork();
+ if (pid == 0) { /* child */
+ char *buf;
+ int j, i;
+ unsigned int prio;
+
+ mq_close(mq);
+ kq = kqueue();
+ mq = mq_open(MQNAME, O_RDWR);
+ if (mq == (mqd_t)-1)
+ err(1, "child: mq_open");
+ EV_SET(&kev, mq_getfd_np(mq), EVFILT_READ, EV_ADD, 0, 0, 0);
+ status = kevent(kq, &kev, 1, NULL, 0, NULL);
+ if (status == -1)
+ err(1, "child: kevent");
+ buf = malloc(attr.mq_msgsize);
+ for (j = 0; j < LOOPS; ++j) {
+ alarm(3);
+ status = kevent(kq, NULL, 0, &kev, 1, NULL);
+ if (status != 1)
+ err(1, "child: kevent 2");
+ status = mq_receive(mq, buf, attr.mq_msgsize, &prio);
+ if (status == -1)
+ err(2, "child: mq_receive");
+ for (i = 0; i < attr.mq_msgsize; ++i)
+ if (buf[i] != i)
+ err(3, "child: message data corrupted");
+ if (prio != PRIO)
+ err(4, "child: priority is incorrect: %d",
+ prio);
+ }
+ alarm(0);
+ free(buf);
+ mq_close(mq);
+ return (0);
+ } else if (pid == -1) {
+ err(1, "fork()");
+ } else {
+ char *buf;
+ int i, j;
+
+ signal(SIGALRM, sighandler);
+ kq = kqueue();
+ EV_SET(&kev, mq_getfd_np(mq), EVFILT_WRITE, EV_ADD, 0, 0, 0);
+ status = kevent(kq, &kev, 1, NULL, 0, NULL);
+ if (status == -1)
+ err(1, "kevent");
+ buf = malloc(attr.mq_msgsize);
+ for (j = 0; j < LOOPS; ++j) {
+ for (i = 0; i < attr.mq_msgsize; ++i) {
+ buf[i] = i;
+ }
+ alarm(3);
+ status = kevent(kq, NULL, 0, &kev, 1, NULL);
+ if (status != 1)
+ err(1, "child: kevent 2");
+ status = mq_send(mq, buf, attr.mq_msgsize, PRIO);
+ if (status) {
+ err(2, "mq_send()");
+ }
+ }
+ free(buf);
+ alarm(3);
+ wait(&status);
+ alarm(0);
+ }
+ status = mq_close(mq);
+ if (status)
+ err(1, "mq_close");
+ mq_unlink(MQNAME);
+ return (0);
+}
diff --git a/tests/sys/mqueue/mqtest5.c b/tests/sys/mqueue/mqtest5.c
new file mode 100644
index 000000000000..f48ef1121289
--- /dev/null
+++ b/tests/sys/mqueue/mqtest5.c
@@ -0,0 +1,127 @@
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/select.h>
+#include <sys/wait.h>
+#include <err.h>
+#include <fcntl.h>
+#include <mqueue.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "freebsd_test_suite/macros.h"
+
+#define MQNAME "/mytstqueue5"
+#define LOOPS 1000
+#define PRIO 10
+
+static void
+sighandler(int sig __unused)
+{
+ write(1, "timeout\n", 8);
+ _exit(1);
+}
+
+int
+main(void)
+{
+ int status;
+ struct mq_attr attr;
+ struct sigaction sa;
+ sigset_t set;
+ siginfo_t info;
+ mqd_t mq;
+ pid_t pid;
+
+ PLAIN_REQUIRE_KERNEL_MODULE("mqueuefs", 0);
+
+ mq_unlink(MQNAME);
+
+ sigemptyset(&set);
+ sigaddset(&set, SIGRTMIN);
+ sigprocmask(SIG_BLOCK, &set, NULL);
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_SIGINFO;
+ sa.sa_sigaction = (void *) SIG_DFL;
+ sigaction(SIGRTMIN, &sa, NULL);
+
+ attr.mq_maxmsg = 5;
+ attr.mq_msgsize = 128;
+ mq = mq_open(MQNAME, O_CREAT | O_RDWR | O_EXCL, 0666, &attr);
+ if (mq == (mqd_t)-1)
+ err(1, "mq_open()");
+ status = mq_getattr(mq, &attr);
+ if (status)
+ err(1, "mq_getattr()");
+ pid = fork();
+ if (pid == 0) { /* child */
+ int prio, j, i;
+ char *buf;
+ struct sigevent sigev;
+
+ signal(SIGALRM, sighandler);
+
+ sigev.sigev_notify = SIGEV_SIGNAL;
+ sigev.sigev_signo = SIGRTMIN;
+ sigev.sigev_value.sival_int = 2;
+
+ mq_close(mq);
+ mq = mq_open(MQNAME, O_RDWR | O_NONBLOCK);
+ if (mq == (mqd_t)-1)
+ err(1, "child: mq_open");
+ buf = malloc(attr.mq_msgsize);
+ for (j = 0; j < LOOPS; ++j) {
+ alarm(3);
+ status = mq_notify(mq, &sigev);
+ if (status)
+ err(1, "child: mq_notify");
+ status = sigwaitinfo(&set, &info);
+ if (status == -1)
+ err(1, "child: sigwaitinfo");
+ if (info.si_value.sival_int != 2)
+ err(1, "child: sival_int");
+ status = mq_receive(mq, buf, attr.mq_msgsize, &prio);
+ if (status == -1)
+ err(2, "child: mq_receive");
+ for (i = 0; i < attr.mq_msgsize; ++i)
+ if (buf[i] != i)
+ err(3, "child: message data corrupted");
+ if (prio != PRIO)
+ err(4, "child: priority is incorrect: %d",
+ prio);
+ }
+ alarm(0);
+ free(buf);
+ mq_close(mq);
+ return (0);
+ } else if (pid == -1) {
+ err(1, "fork()");
+ } else {
+ char *buf;
+ int i, j;
+
+ signal(SIGALRM, sighandler);
+ buf = malloc(attr.mq_msgsize);
+ for (j = 0; j < LOOPS; ++j) {
+ for (i = 0; i < attr.mq_msgsize; ++i) {
+ buf[i] = i;
+ }
+ alarm(3);
+ status = mq_send(mq, buf, attr.mq_msgsize, PRIO);
+ if (status) {
+ kill(pid, SIGKILL);
+ err(2, "mq_send()");
+ }
+ }
+ alarm(3);
+ wait(&status);
+ alarm(0);
+ }
+ status = mq_close(mq);
+ if (status)
+ err(1, "mq_close");
+ mq_unlink(MQNAME);
+ return (0);
+}
diff --git a/tests/sys/mqueue/mqueue_test.sh b/tests/sys/mqueue/mqueue_test.sh
new file mode 100644
index 000000000000..8b3f45159ad9
--- /dev/null
+++ b/tests/sys/mqueue/mqueue_test.sh
@@ -0,0 +1,80 @@
+#
+# Copyright (c) 2015 EMC / Isilon Storage Division
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+mqtest1_head()
+{
+ :
+}
+mqtest1_body()
+{
+ atf_check -s exit:0 -x $(atf_get_srcdir)/mqtest1
+}
+
+mqtest2_head()
+{
+ :
+}
+mqtest2_body()
+{
+ atf_check -s exit:0 -x $(atf_get_srcdir)/mqtest2
+}
+
+mqtest3_head()
+{
+ :
+}
+mqtest3_body()
+{
+ atf_check -s exit:0 -x $(atf_get_srcdir)/mqtest3
+}
+
+mqtest4_head()
+{
+ :
+}
+mqtest4_body()
+{
+ atf_check -s exit:0 -x $(atf_get_srcdir)/mqtest4
+}
+
+mqtest5_head()
+{
+ :
+}
+mqtest5_body()
+{
+ atf_check -s exit:0 -x $(atf_get_srcdir)/mqtest5
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case mqtest1
+ atf_add_test_case mqtest2
+ #atf_add_test_case mqtest3
+ #atf_add_test_case mqtest4
+ atf_add_test_case mqtest5
+}
diff --git a/tests/sys/net/Makefile b/tests/sys/net/Makefile
new file mode 100644
index 000000000000..bc8f9c5e9c80
--- /dev/null
+++ b/tests/sys/net/Makefile
@@ -0,0 +1,47 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/net
+BINDIR= ${TESTSDIR}
+
+ATF_TESTS_C+= if_epair
+ATF_TESTS_SH+= if_epair_test
+ATF_TESTS_SH+= if_bridge_test
+TEST_METADATA.if_bridge_test+= required_programs="python"
+TEST_METADATA.if_bridge_test+= execenv="jail"
+TEST_METADATA.if_bridge_test+= execenv_jail_params="vnet allow.raw_sockets"
+ATF_TESTS_SH+= if_clone_test
+ATF_TESTS_SH+= if_gif
+ATF_TESTS_SH+= if_lagg_test
+ATF_TESTS_SH+= if_stf
+ATF_TESTS_SH+= if_tun_test
+ATF_TESTS_SH+= if_vlan
+ATF_TESTS_SH+= if_wg
+
+TESTS_SUBDIRS+= bpf
+TESTS_SUBDIRS+= if_ovpn
+TESTS_SUBDIRS+= routing
+
+# The netmap bridge application is used by if_wg tests.
+.PATH: ${SRCTOP}/tools/tools/netmap
+PROGS+= bridge
+LIBADD.bridge+= netmap
+
+# The tests are written to be run in parallel, but doing so leads to random
+# panics. I think it's because the kernel's list of interfaces isn't properly
+# locked.
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= \
+ dhclient_pcp.conf \
+ pcp.py \
+ stp.py
+
+${PACKAGE}FILESMODE_pcp.py= 0555
+${PACKAGE}FILESMODE_stp.py= 0555
+
+MAN=
+PROGS+= randsleep
+
+CFLAGS+= -I${.CURDIR:H:H}
+
+.include <bsd.test.mk>
diff --git a/tests/sys/net/bpf/Makefile b/tests/sys/net/bpf/Makefile
new file mode 100644
index 000000000000..9c8a25b15d16
--- /dev/null
+++ b/tests/sys/net/bpf/Makefile
@@ -0,0 +1,15 @@
+.include <src.opts.mk>
+
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/net/bpf
+BINDIR= ${TESTSDIR}
+
+LIBADD+= nv
+
+PROGS= bpf_multi_read
+LIBADD.bpf_multi_read+= pcap
+
+ATF_TESTS_SH= bpf
+
+.include <bsd.test.mk>
diff --git a/tests/sys/net/bpf/bpf.sh b/tests/sys/net/bpf/bpf.sh
new file mode 100644
index 000000000000..2830c4862de9
--- /dev/null
+++ b/tests/sys/net/bpf/bpf.sh
@@ -0,0 +1,67 @@
+##
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2025 Rubicon Communications, LLC ("Netgate")
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/../../common/vnet.subr
+
+atf_test_case "multi_read" "cleanup"
+multi_read_head()
+{
+ atf_set descr 'Test multiple readers on /dev/bpf'
+ atf_set require.user root
+}
+
+multi_read_body()
+{
+ vnet_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a inet 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b inet 192.0.2.2/24 up
+
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.2
+
+ # Start a multi-thread (or multi-process) read on bpf
+ $(atf_get_srcdir)/bpf_multi_read ${epair}a &
+
+ # Generate traffic
+ ping -f 192.0.2.2 >/dev/null 2>&1 &
+
+ # Now let this run for 10 seconds
+ sleep 10
+}
+
+multi_read_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "multi_read"
+}
diff --git a/tests/sys/net/bpf/bpf_multi_read.c b/tests/sys/net/bpf/bpf_multi_read.c
new file mode 100644
index 000000000000..3a8edd76d623
--- /dev/null
+++ b/tests/sys/net/bpf/bpf_multi_read.c
@@ -0,0 +1,76 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Rubicon Communications, LLC (Netgate)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <pcap.h>
+#include <unistd.h>
+
+static void
+callback(u_char *arg __unused, const struct pcap_pkthdr *hdr __unused,
+ const unsigned char *bytes __unused)
+{
+}
+
+int
+main(int argc, const char **argv)
+{
+ pcap_t *pcap;
+ const char *interface;
+ char errbuf[PCAP_ERRBUF_SIZE] = { 0 };
+ int ret;
+
+ if (argc != 2)
+ err(1, "Usage: %s <interface>\n", argv[0]);
+
+ interface = argv[1];
+
+ pcap = pcap_create(interface, errbuf);
+ if (! pcap)
+ perror("Failed to pcap interface");
+
+ ret = pcap_set_snaplen(pcap, 86);
+ if (ret != 0)
+ perror("Failed to set snaplen");
+
+ ret = pcap_set_timeout(pcap, 100);
+ if (ret != 0)
+ perror("Failed to set timeout");
+
+ ret = pcap_activate(pcap);
+ if (ret != 0)
+ perror("Failed to activate");
+
+ /* So we have two readers on one /dev/bpf fd */
+ fork();
+
+ printf("Interface open\n");
+ pcap_loop(pcap, 0, callback, NULL);
+
+ return (0);
+}
diff --git a/tests/sys/net/dhclient_pcp.conf b/tests/sys/net/dhclient_pcp.conf
new file mode 100644
index 000000000000..fbd86e5bd0f8
--- /dev/null
+++ b/tests/sys/net/dhclient_pcp.conf
@@ -0,0 +1 @@
+vlan-pcp 6;
diff --git a/tests/sys/net/if_bridge_test.sh b/tests/sys/net/if_bridge_test.sh
new file mode 100755
index 000000000000..c0c085f22273
--- /dev/null
+++ b/tests/sys/net/if_bridge_test.sh
@@ -0,0 +1,1274 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 The FreeBSD Foundation
+#
+# This software was developed by Kristof Provost under sponsorship
+# from the FreeBSD Foundation.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+atf_test_case "bridge_transmit_ipv4_unicast" "cleanup"
+bridge_transmit_ipv4_unicast_head()
+{
+ atf_set descr 'bridge_transmit_ipv4_unicast bridging test'
+ atf_set require.user root
+}
+
+bridge_transmit_ipv4_unicast_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ epair_alcatraz=$(vnet_mkepair)
+ epair_singsing=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair_alcatraz}b
+ vnet_mkjail singsing ${epair_singsing}b
+
+ jexec alcatraz ifconfig ${epair_alcatraz}b 192.0.2.1/24 up
+ jexec singsing ifconfig ${epair_singsing}b 192.0.2.2/24 up
+
+ bridge=$(vnet_mkbridge)
+
+ ifconfig ${bridge} up
+ ifconfig ${epair_alcatraz}a up
+ ifconfig ${epair_singsing}a up
+ ifconfig ${bridge} addm ${epair_alcatraz}a
+ ifconfig ${bridge} addm ${epair_singsing}a
+
+ atf_check -s exit:0 -o ignore jexec alcatraz ping -c 3 -t 1 192.0.2.2
+ atf_check -s exit:0 -o ignore jexec singsing ping -c 3 -t 1 192.0.2.1
+}
+
+bridge_transmit_ipv4_unicast_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "stp" "cleanup"
+stp_head()
+{
+ atf_set descr 'Spanning tree test'
+ atf_set require.user root
+}
+
+stp_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+ bridge_a=$(vnet_mkbridge)
+ bridge_b=$(vnet_mkbridge)
+
+ vnet_mkjail a ${bridge_a} ${epair_one}a ${epair_two}a
+ vnet_mkjail b ${bridge_b} ${epair_one}b ${epair_two}b
+
+ jexec a ifconfig ${epair_one}a up
+ jexec a ifconfig ${epair_two}a up
+ jexec a ifconfig ${bridge_a} addm ${epair_one}a
+ jexec a ifconfig ${bridge_a} addm ${epair_two}a
+
+ jexec b ifconfig ${epair_one}b up
+ jexec b ifconfig ${epair_two}b up
+ jexec b ifconfig ${bridge_b} addm ${epair_one}b
+ jexec b ifconfig ${bridge_b} addm ${epair_two}b
+
+ jexec a ifconfig ${bridge_a} 192.0.2.1/24
+
+ # Enable spanning tree
+ jexec a ifconfig ${bridge_a} stp ${epair_one}a
+ jexec a ifconfig ${bridge_a} stp ${epair_two}a
+ jexec b ifconfig ${bridge_b} stp ${epair_one}b
+ jexec b ifconfig ${bridge_b} stp ${epair_two}b
+
+ jexec b ifconfig ${bridge_b} up
+ jexec a ifconfig ${bridge_a} up
+
+ # Give STP time to do its thing
+ sleep 5
+
+ a_discard=$(jexec a ifconfig ${bridge_a} | grep discarding)
+ b_discard=$(jexec b ifconfig ${bridge_b} | grep discarding)
+
+ if [ -z "${a_discard}" ] && [ -z "${b_discard}" ]
+ then
+ atf_fail "STP failed to detect bridging loop"
+ fi
+
+ # We must also have at least some forwarding interfaces
+ a_forwarding=$(jexec a ifconfig ${bridge_a} | grep forwarding)
+ b_forwarding=$(jexec b ifconfig ${bridge_b} | grep forwarding)
+
+ if [ -z "${a_forwarding}" ] && [ -z "${b_forwarding}" ]
+ then
+ atf_fail "STP failed to detect bridging loop"
+ fi
+}
+
+stp_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "stp_vlan" "cleanup"
+stp_vlan_head()
+{
+ atf_set descr 'Spanning tree on VLAN test'
+ atf_set require.user root
+}
+
+stp_vlan_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+ bridge_a=$(vnet_mkbridge)
+ bridge_b=$(vnet_mkbridge)
+
+ vnet_mkjail a ${bridge_a} ${epair_one}a ${epair_two}a
+ vnet_mkjail b ${bridge_b} ${epair_one}b ${epair_two}b
+
+ jexec a ifconfig ${epair_one}a up
+ jexec a ifconfig ${epair_two}a up
+ vlan_a_one=$(jexec a ifconfig vlan create vlandev ${epair_one}a vlan 42)
+ vlan_a_two=$(jexec a ifconfig vlan create vlandev ${epair_two}a vlan 42)
+ jexec a ifconfig ${vlan_a_one} up
+ jexec a ifconfig ${vlan_a_two} up
+ jexec a ifconfig ${bridge_a} addm ${vlan_a_one}
+ jexec a ifconfig ${bridge_a} addm ${vlan_a_two}
+
+ jexec b ifconfig ${epair_one}b up
+ jexec b ifconfig ${epair_two}b up
+ vlan_b_one=$(jexec b ifconfig vlan create vlandev ${epair_one}b vlan 42)
+ vlan_b_two=$(jexec b ifconfig vlan create vlandev ${epair_two}b vlan 42)
+ jexec b ifconfig ${vlan_b_one} up
+ jexec b ifconfig ${vlan_b_two} up
+ jexec b ifconfig ${bridge_b} addm ${vlan_b_one}
+ jexec b ifconfig ${bridge_b} addm ${vlan_b_two}
+
+ jexec a ifconfig ${bridge_a} 192.0.2.1/24
+
+ # Enable spanning tree
+ jexec a ifconfig ${bridge_a} stp ${vlan_a_one}
+ jexec a ifconfig ${bridge_a} stp ${vlan_a_two}
+ jexec b ifconfig ${bridge_b} stp ${vlan_b_one}
+ jexec b ifconfig ${bridge_b} stp ${vlan_b_two}
+
+ jexec b ifconfig ${bridge_b} up
+ jexec a ifconfig ${bridge_a} up
+
+ # Give STP time to do its thing
+ sleep 5
+
+ a_discard=$(jexec a ifconfig ${bridge_a} | grep discarding)
+ b_discard=$(jexec b ifconfig ${bridge_b} | grep discarding)
+
+ if [ -z "${a_discard}" ] && [ -z "${b_discard}" ]
+ then
+ atf_fail "STP failed to detect bridging loop"
+ fi
+
+ # We must also have at least some forwarding interfaces
+ a_forwarding=$(jexec a ifconfig ${bridge_a} | grep forwarding)
+ b_forwarding=$(jexec b ifconfig ${bridge_b} | grep forwarding)
+
+ if [ -z "${a_forwarding}" ] && [ -z "${b_forwarding}" ]
+ then
+ atf_fail "STP failed to detect bridging loop"
+ fi
+}
+
+stp_vlan_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "static" "cleanup"
+static_head()
+{
+ atf_set descr 'Bridge static address test'
+ atf_set require.user root
+}
+
+static_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ epair=$(vnet_mkepair)
+ bridge=$(vnet_mkbridge)
+
+ vnet_mkjail one ${bridge} ${epair}a
+
+ ifconfig ${epair}b up
+
+ jexec one ifconfig ${bridge} up
+ jexec one ifconfig ${epair}a up
+ jexec one ifconfig ${bridge} addm ${epair}a
+
+ # Wrong interface
+ atf_check -s exit:1 -o ignore -e ignore \
+ jexec one ifconfig ${bridge} static ${epair}b 00:01:02:03:04:05
+
+ # Bad address format
+ atf_check -s exit:1 -o ignore -e ignore \
+ jexec one ifconfig ${bridge} static ${epair}a 00:01:02:03:04
+
+ # Correct add
+ atf_check -s exit:0 -o ignore \
+ jexec one ifconfig ${bridge} static ${epair}a 00:01:02:03:04:05
+
+ # List addresses
+ atf_check -s exit:0 -o ignore \
+ jexec one ifconfig ${bridge} addr
+
+ # Delete with bad address format
+ atf_check -s exit:1 -o ignore -e ignore \
+ jexec one ifconfig ${bridge} deladdr 00:01:02:03:04
+
+ # Delete with unlisted address
+ atf_check -s exit:1 -o ignore -e ignore \
+ jexec one ifconfig ${bridge} deladdr 00:01:02:03:04:06
+
+ # Correct delete
+ atf_check -s exit:0 -o ignore \
+ jexec one ifconfig ${bridge} deladdr 00:01:02:03:04:05
+}
+
+static_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "span" "cleanup"
+span_head()
+{
+ atf_set descr 'Bridge span test'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+span_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ epair=$(vnet_mkepair)
+ epair_span=$(vnet_mkepair)
+ bridge=$(vnet_mkbridge)
+
+ vnet_mkjail one ${bridge} ${epair}a ${epair_span}a
+
+ ifconfig ${epair}b up
+ ifconfig ${epair_span}b up
+
+ jexec one ifconfig ${bridge} up
+ jexec one ifconfig ${epair}a up
+ jexec one ifconfig ${epair_span}a up
+ jexec one ifconfig ${bridge} addm ${epair}a
+
+ jexec one ifconfig ${bridge} span ${epair_span}a
+ jexec one ifconfig ${bridge} 192.0.2.1/24
+
+ # Send some traffic through the span
+ jexec one ping -c 1 -t 1 192.0.2.2
+
+ # Check that we see the traffic on the span interface
+ atf_check -s exit:0 \
+ $(atf_get_srcdir)/../netpfil/common/pft_ping.py \
+ --sendif ${epair}b \
+ --to 192.0.2.2 \
+ --recvif ${epair_span}b
+
+ jexec one ifconfig ${bridge} -span ${epair_span}a
+
+ # And no more traffic after we remove the span
+ atf_check -s exit:1 \
+ $(atf_get_srcdir)/../netpfil/common/pft_ping.py \
+ --sendif ${epair}b \
+ --to 192.0.2.2 \
+ --recvif ${epair_span}b
+}
+
+span_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "delete_with_members" "cleanup"
+delete_with_members_head()
+{
+ atf_set descr 'Delete a bridge which still has member interfaces'
+ atf_set require.user root
+}
+
+delete_with_members_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ bridge=$(vnet_mkbridge)
+ epair=$(vnet_mkepair)
+
+ ifconfig ${bridge} 192.0.2.1/24 up
+ ifconfig ${epair}a up
+ ifconfig ${bridge} addm ${epair}a
+
+ ifconfig ${bridge} destroy
+}
+
+delete_with_members_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "mac_conflict" "cleanup"
+mac_conflict_head()
+{
+ atf_set descr 'Ensure that bridges in different jails get different mac addresses'
+ atf_set require.user root
+}
+
+mac_conflict_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ epair=$(vnet_mkepair)
+
+ # Ensure the bridge module is loaded so jails can use it.
+ tmpbridge=$(vnet_mkbridge)
+
+ vnet_mkjail bridge_mac_conflict_one ${epair}a
+ vnet_mkjail bridge_mac_conflict_two ${epair}b
+
+ jexec bridge_mac_conflict_one ifconfig bridge create
+ jexec bridge_mac_conflict_one ifconfig bridge0 192.0.2.1/24 up \
+ addm ${epair}a
+ jexec bridge_mac_conflict_one ifconfig ${epair}a up
+
+ jexec bridge_mac_conflict_two ifconfig bridge create
+ jexec bridge_mac_conflict_two ifconfig bridge0 192.0.2.2/24 up \
+ addm ${epair}b
+ jexec bridge_mac_conflict_two ifconfig ${epair}b up
+
+ atf_check -s exit:0 -o ignore \
+ jexec bridge_mac_conflict_one ping -c 3 192.0.2.2
+}
+
+mac_conflict_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "inherit_mac" "cleanup"
+inherit_mac_head()
+{
+ atf_set descr 'Bridge inherit_mac test, #216510'
+ atf_set require.user root
+}
+
+inherit_mac_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ bridge=$(vnet_mkbridge)
+ epair=$(vnet_mkepair)
+ vnet_mkjail one ${bridge} ${epair}a
+
+ jexec one sysctl net.link.bridge.inherit_mac=1
+
+ # Attempt to provoke the panic described in #216510
+ jexec one ifconfig ${bridge} 192.0.0.1/24 up
+ jexec one ifconfig ${bridge} addm ${epair}a
+}
+
+inherit_mac_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "stp_validation" "cleanup"
+stp_validation_head()
+{
+ atf_set descr 'Check STP validation'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+stp_validation_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+ bridge=$(vnet_mkbridge)
+
+ ifconfig ${bridge} up
+ ifconfig ${bridge} addm ${epair_one}a addm ${epair_two}a
+ ifconfig ${bridge} stp ${epair_one}a stp ${epair_two}a
+
+ ifconfig ${epair_one}a up
+ ifconfig ${epair_one}b up
+ ifconfig ${epair_two}a up
+ ifconfig ${epair_two}b up
+
+ # Wait until the interfaces are no longer discarding
+ while ifconfig ${bridge} | grep 'state discarding' >/dev/null
+ do
+ sleep 1
+ done
+
+ # Now inject invalid STP BPDUs on epair_one and see if they're repeated
+ # on epair_two
+ atf_check -s exit:0 \
+ $(atf_get_srcdir)/stp.py \
+ --sendif ${epair_one}b \
+ --recvif ${epair_two}b
+}
+
+stp_validation_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "gif" "cleanup"
+gif_head()
+{
+ atf_set descr 'gif as a bridge member'
+ atf_set require.user root
+}
+
+gif_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail one ${epair}a
+ vnet_mkjail two ${epair}b
+
+ jexec one sysctl net.link.gif.max_nesting=2
+ jexec two sysctl net.link.gif.max_nesting=2
+
+ jexec one ifconfig ${epair}a 192.0.2.1/24 up
+ jexec two ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Tunnel
+ gif_one=$(jexec one ifconfig gif create)
+ gif_two=$(jexec two ifconfig gif create)
+
+ jexec one ifconfig ${gif_one} tunnel 192.0.2.1 192.0.2.2
+ jexec one ifconfig ${gif_one} up
+ jexec two ifconfig ${gif_two} tunnel 192.0.2.2 192.0.2.1
+ jexec two ifconfig ${gif_two} up
+
+ bridge_one=$(jexec one ifconfig bridge create)
+ bridge_two=$(jexec two ifconfig bridge create)
+ jexec one ifconfig ${bridge_one} 198.51.100.1/24 up
+ jexec one ifconfig ${bridge_one} addm ${gif_one}
+ jexec two ifconfig ${bridge_two} 198.51.100.2/24 up
+ jexec two ifconfig ${bridge_two} addm ${gif_two}
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ jexec one ping -c 1 192.0.2.2
+
+ # Test tunnel
+ atf_check -s exit:0 -o ignore \
+ jexec one ping -c 1 198.51.100.2
+ atf_check -s exit:0 -o ignore \
+ jexec one ping -c 1 -s 1200 198.51.100.2
+ atf_check -s exit:0 -o ignore \
+ jexec one ping -c 1 -s 2000 198.51.100.2
+
+ # Higher MTU on the tunnel than on the underlying interface
+ jexec one ifconfig ${epair}a mtu 1000
+ jexec two ifconfig ${epair}b mtu 1000
+
+ atf_check -s exit:0 -o ignore \
+ jexec one ping -c 1 -s 1200 198.51.100.2
+ atf_check -s exit:0 -o ignore \
+ jexec one ping -c 1 -s 2000 198.51.100.2
+}
+
+gif_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "mtu" "cleanup"
+mtu_head()
+{
+ atf_set descr 'Bridge MTU changes'
+ atf_set require.user root
+}
+
+get_mtu()
+{
+ intf=$1
+
+ ifconfig ${intf} | awk '$5 == "mtu" { print $6 }'
+}
+
+check_mtu()
+{
+ intf=$1
+ expected=$2
+
+ mtu=$(get_mtu $intf)
+ if [ "$mtu" -ne "$expected" ];
+ then
+ atf_fail "Expected MTU of $expected on $intf but found $mtu"
+ fi
+}
+
+mtu_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ epair=$(vnet_mkepair)
+ gif=$(ifconfig gif create)
+ echo ${gif} >> created_interfaces.lst
+ bridge=$(vnet_mkbridge)
+
+ atf_check -s exit:0 \
+ ifconfig ${bridge} addm ${epair}a
+
+ ifconfig ${gif} mtu 1500
+ atf_check -s exit:0 \
+ ifconfig ${bridge} addm ${gif}
+
+ # Changing MTU changes it for all member interfaces
+ atf_check -s exit:0 \
+ ifconfig ${bridge} mtu 2000
+
+ check_mtu ${bridge} 2000
+ check_mtu ${gif} 2000
+ check_mtu ${epair}a 2000
+
+ # Rejected MTUs mean none of the MTUs change
+ atf_check -s exit:1 -e ignore \
+ ifconfig ${bridge} mtu 9000
+
+ check_mtu ${bridge} 2000
+ check_mtu ${gif} 2000
+ check_mtu ${epair}a 2000
+
+ # We're not allowed to change the MTU of a member interface
+ atf_check -s exit:1 -e ignore \
+ ifconfig ${epair}a mtu 1900
+ check_mtu ${epair}a 2000
+
+ # Test adding an interface with a different MTU
+ new_epair=$(vnet_mkepair)
+ check_mtu ${new_epair}a 1500
+ atf_check -s exit:0 -e ignore \
+ ifconfig ${bridge} addm ${new_epair}a
+
+ check_mtu ${bridge} 2000
+ check_mtu ${gif} 2000
+ check_mtu ${epair}a 2000
+ check_mtu ${new_epair}a 2000
+}
+
+mtu_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "vlan" "cleanup"
+vlan_head()
+{
+ atf_set descr 'Ensure the bridge takes vlan ID into account, PR#270559'
+ atf_set require.user root
+}
+
+vlan_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ vid=1
+
+ epaira=$(vnet_mkepair)
+ epairb=$(vnet_mkepair)
+
+ br=$(vnet_mkbridge)
+
+ vnet_mkjail one ${epaira}b
+ vnet_mkjail two ${epairb}b
+
+ ifconfig ${br} up
+ ifconfig ${epaira}a up
+ ifconfig ${epairb}a up
+ ifconfig ${br} addm ${epaira}a addm ${epairb}a
+
+ jexec one ifconfig ${epaira}b up
+ jexec one ifconfig ${epaira}b.${vid} create
+
+ jexec two ifconfig ${epairb}b up
+ jexec two ifconfig ${epairb}b.${vid} create
+
+ # Create a MAC address conflict between an untagged and tagged interface
+ jexec two ifconfig ${epairb}b.${vid} ether 02:05:6e:06:28:1a
+ jexec one ifconfig ${epaira}b ether 02:05:6e:06:28:1a
+ jexec one ifconfig ${epaira}b.${vid} ether 02:05:6e:06:28:1b
+
+ # Add ip address, will also populate $br's fowarding table, by ARP announcement
+ jexec one ifconfig ${epaira}b.${vid} 192.0.2.1/24 up
+ jexec two ifconfig ${epairb}b.${vid} 192.0.2.2/24 up
+
+ sleep 0.5
+
+ ifconfig ${br}
+ jexec one ifconfig
+ jexec two ifconfig
+ ifconfig ${br} addr
+
+ atf_check -s exit:0 -o ignore \
+ jexec one ping -c 1 -t 1 192.0.2.2
+
+ # This will trigger a mac flap (by ARP announcement)
+ jexec one ifconfig ${epaira}b 192.0.2.1/24 up
+
+ sleep 0.5
+
+ ifconfig ${br} addr
+
+ atf_check -s exit:0 -o ignore \
+ jexec one ping -c 1 -t 1 192.0.2.2
+}
+
+vlan_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "many_bridge_members" "cleanup"
+many_bridge_members_head()
+{
+ atf_set descr 'many_bridge_members ifconfig test'
+ atf_set require.user root
+}
+
+many_bridge_members_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ bridge=$(vnet_mkbridge)
+ ifcount=256
+ for _ in $(seq 1 $ifcount); do
+ epair=$(vnet_mkepair)
+ ifconfig "${bridge}" addm "${epair}"a
+ done
+
+ atf_check -s exit:0 -o inline:"$ifcount\n" \
+ sh -c "ifconfig ${bridge} | grep member: | wc -l | xargs"
+}
+
+many_bridge_members_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "member_ifaddrs_enabled" "cleanup"
+member_ifaddrs_enabled_head()
+{
+ atf_set descr 'bridge with member_ifaddrs=1'
+ atf_set require.user root
+}
+
+member_ifaddrs_enabled_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ ep=$(vnet_mkepair)
+ ifconfig ${ep}a inet 192.0.2.1/24 up
+
+ vnet_mkjail one ${ep}b
+ jexec one sysctl net.link.bridge.member_ifaddrs=1
+ jexec one ifconfig ${ep}b inet 192.0.2.2/24 up
+ jexec one ifconfig bridge0 create addm ${ep}b
+
+ atf_check -s exit:0 -o ignore ping -c3 -t1 192.0.2.2
+}
+
+member_ifaddrs_enabled_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "member_ifaddrs_disabled" "cleanup"
+member_ifaddrs_disabled_head()
+{
+ atf_set descr 'bridge with member_ifaddrs=0'
+ atf_set require.user root
+}
+
+member_ifaddrs_disabled_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ vnet_mkjail one
+ jexec one sysctl net.link.bridge.member_ifaddrs=0
+
+ bridge=$(jexec one ifconfig bridge create)
+
+ # adding an interface with an IPv4 address
+ ep=$(jexec one ifconfig epair create)
+ jexec one ifconfig ${ep} 192.0.2.1/32
+ atf_check -s exit:1 -e ignore jexec one ifconfig ${bridge} addm ${ep}
+
+ # adding an interface with an IPv6 address
+ ep=$(jexec one ifconfig epair create)
+ jexec one ifconfig ${ep} inet6 2001:db8::1/128
+ atf_check -s exit:1 -e ignore jexec one ifconfig ${bridge} addm ${ep}
+
+ # adding an interface with an IPv6 link-local address
+ ep=$(jexec one ifconfig epair create)
+ jexec one ifconfig ${ep} inet6 -ifdisabled auto_linklocal up
+ atf_check -s exit:1 -e ignore jexec one ifconfig ${bridge} addm ${ep}
+
+ # adding an IPv4 address to a member
+ ep=$(jexec one ifconfig epair create)
+ jexec one ifconfig ${bridge} addm ${ep}
+ atf_check -s exit:1 -e ignore jexec one ifconfig ${ep} inet 192.0.2.2/32
+
+ # adding an IPv6 address to a member
+ ep=$(jexec one ifconfig epair create)
+ jexec one ifconfig ${bridge} addm ${ep}
+ atf_check -s exit:1 -e ignore jexec one ifconfig ${ep} inet6 2001:db8::1/128
+}
+
+member_ifaddrs_disabled_cleanup()
+{
+ vnet_cleanup
+}
+
+#
+# Test kern/287150: when member_ifaddrs=0, and a physical interface which is in
+# a bridge also has a vlan(4) on it, tagged packets are not correctly passed to
+# vlan(4).
+atf_test_case "member_ifaddrs_vlan" "cleanup"
+member_ifaddrs_vlan_head()
+{
+ atf_set descr 'kern/287150: vlan and bridge on the same interface'
+ atf_set require.user root
+}
+
+member_ifaddrs_vlan_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ epone=$(vnet_mkepair)
+ eptwo=$(vnet_mkepair)
+
+ # The first jail has an epair with an IP address on vlan 20.
+ vnet_mkjail one ${epone}a
+ atf_check -s exit:0 jexec one ifconfig ${epone}a up
+ atf_check -s exit:0 jexec one \
+ ifconfig ${epone}a.20 create inet 192.0.2.1/24 up
+
+ # The second jail has an epair with an IP address on vlan 20,
+ # which is also in a bridge.
+ vnet_mkjail two ${epone}b
+
+ jexec two ifconfig
+ atf_check -s exit:0 -o save:bridge jexec two ifconfig bridge create
+ bridge=$(cat bridge)
+ atf_check -s exit:0 jexec two ifconfig ${bridge} addm ${epone}b up
+
+ atf_check -s exit:0 -o ignore jexec two \
+ sysctl net.link.bridge.member_ifaddrs=0
+ atf_check -s exit:0 jexec two ifconfig ${epone}b up
+ atf_check -s exit:0 jexec two \
+ ifconfig ${epone}b.20 create inet 192.0.2.2/24 up
+
+ # Make sure the two jails can communicate over the vlan.
+ atf_check -s exit:0 -o ignore jexec one ping -c 3 -t 1 192.0.2.2
+ atf_check -s exit:0 -o ignore jexec two ping -c 3 -t 1 192.0.2.1
+}
+
+member_ifaddrs_vlan_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "vlan_pvid" "cleanup"
+vlan_pvid_head()
+{
+ atf_set descr 'bridge with two ports with pvid set'
+ atf_set require.user root
+}
+
+vlan_pvid_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ epone=$(vnet_mkepair)
+ eptwo=$(vnet_mkepair)
+
+ vnet_mkjail one ${epone}b
+ vnet_mkjail two ${eptwo}b
+
+ jexec one ifconfig ${epone}b 192.0.2.1/24 up
+ jexec two ifconfig ${eptwo}b 192.0.2.2/24 up
+
+ bridge=$(vnet_mkbridge)
+
+ ifconfig ${bridge} up
+ ifconfig ${epone}a up
+ ifconfig ${eptwo}a up
+ ifconfig ${bridge} addm ${epone}a untagged ${epone}a 20
+ ifconfig ${bridge} addm ${eptwo}a untagged ${eptwo}a 20
+
+ # With VLAN filtering enabled, traffic should be passed.
+ atf_check -s exit:0 -o ignore jexec one ping -c 3 -t 1 192.0.2.2
+ atf_check -s exit:0 -o ignore jexec two ping -c 3 -t 1 192.0.2.1
+
+ # Removed the untagged VLAN on one port; traffic should not be passed.
+ ifconfig ${bridge} -untagged ${epone}a
+ atf_check -s exit:2 -o ignore jexec one ping -c 3 -t 1 192.0.2.2
+ atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1
+}
+
+vlan_pvid_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "vlan_pvid_filtered" "cleanup"
+vlan_pvid_filtered_head()
+{
+ atf_set descr 'bridge with two ports with different pvids'
+ atf_set require.user root
+}
+
+vlan_pvid_filtered_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ epone=$(vnet_mkepair)
+ eptwo=$(vnet_mkepair)
+
+ vnet_mkjail one ${epone}b
+ vnet_mkjail two ${eptwo}b
+
+ jexec one ifconfig ${epone}b 192.0.2.1/24 up
+ jexec two ifconfig ${eptwo}b 192.0.2.2/24 up
+
+ bridge=$(vnet_mkbridge)
+
+ ifconfig ${bridge} up
+ ifconfig ${epone}a up
+ ifconfig ${eptwo}a up
+ ifconfig ${bridge} addm ${epone}a untagged ${epone}a 20
+ ifconfig ${bridge} addm ${eptwo}a untagged ${eptwo}a 30
+
+ atf_check -s exit:2 -o ignore jexec one ping -c 3 -t 1 192.0.2.2
+ atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1
+}
+
+vlan_pvid_filtered_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "vlan_pvid_tagged" "cleanup"
+vlan_pvid_tagged_head()
+{
+ atf_set descr 'bridge pvid with tagged frames for pvid'
+ atf_set require.user root
+}
+
+vlan_pvid_tagged_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ epone=$(vnet_mkepair)
+ eptwo=$(vnet_mkepair)
+
+ vnet_mkjail one ${epone}b
+ vnet_mkjail two ${eptwo}b
+
+ # Create two tagged interfaces on the appropriate VLANs
+ jexec one ifconfig ${epone}b up
+ jexec one ifconfig ${epone}b.20 create 192.0.2.1/24 up
+ jexec two ifconfig ${eptwo}b up
+ jexec two ifconfig ${eptwo}b.20 create 192.0.2.2/24 up
+
+ bridge=$(vnet_mkbridge)
+
+ ifconfig ${bridge} up
+ ifconfig ${epone}a up
+ ifconfig ${eptwo}a up
+ ifconfig ${bridge} addm ${epone}a untagged ${epone}a 20
+ ifconfig ${bridge} addm ${eptwo}a untagged ${eptwo}a 20
+
+ # Tagged frames should not be passed.
+ atf_check -s exit:2 -o ignore jexec one ping -c 3 -t 1 192.0.2.2
+ atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1
+}
+
+vlan_pvid_tagged_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "vlan_pvid_1q" "cleanup"
+vlan_pvid_1q_head()
+{
+ atf_set descr '802.1q tag addition and removal'
+ atf_set require.user root
+}
+
+vlan_pvid_1q_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ epone=$(vnet_mkepair)
+ eptwo=$(vnet_mkepair)
+
+ vnet_mkjail one ${epone}b
+ vnet_mkjail two ${eptwo}b
+
+ # Set up one jail with an access port, and the other with a trunk port.
+ # This forces the bridge to add and remove .1q tags to bridge the
+ # traffic.
+
+ jexec one ifconfig ${epone}b 192.0.2.1/24 up
+ jexec two ifconfig ${eptwo}b up
+ jexec two ifconfig ${eptwo}b.20 create 192.0.2.2/24 up
+
+ bridge=$(vnet_mkbridge)
+
+ ifconfig ${bridge} addm ${epone}a untagged ${epone}a 20
+ ifconfig ${bridge} addm ${eptwo}a
+
+ ifconfig ${bridge} up
+ ifconfig ${epone}a up
+ ifconfig ${eptwo}a up
+
+ atf_check -s exit:0 -o ignore jexec one ping -c 3 -t 1 192.0.2.2
+ atf_check -s exit:0 -o ignore jexec two ping -c 3 -t 1 192.0.2.1
+}
+
+vlan_pvid_1q_cleanup()
+{
+ vnet_cleanup
+}
+
+#
+# Test vlan filtering.
+#
+atf_test_case "vlan_filtering" "cleanup"
+vlan_filtering_head()
+{
+ atf_set descr 'tagged traffic with filtering'
+ atf_set require.user root
+}
+
+vlan_filtering_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ epone=$(vnet_mkepair)
+ eptwo=$(vnet_mkepair)
+
+ vnet_mkjail one ${epone}b
+ vnet_mkjail two ${eptwo}b
+
+ jexec one ifconfig ${epone}b up
+ jexec one ifconfig ${epone}b.20 create 192.0.2.1/24 up
+ jexec two ifconfig ${eptwo}b up
+ jexec two ifconfig ${eptwo}b.20 create 192.0.2.2/24 up
+
+ bridge=$(vnet_mkbridge)
+
+ ifconfig ${bridge} up
+ ifconfig ${epone}a up
+ ifconfig ${eptwo}a up
+ ifconfig ${bridge} addm ${epone}a vlanfilter ${epone}a
+ ifconfig ${bridge} addm ${eptwo}a vlanfilter ${eptwo}a
+
+ # Right now there are no VLANs on the access list, so everything
+ # should be blocked.
+ atf_check -s exit:2 -o ignore jexec one ping -c 3 -t 1 192.0.2.2
+ atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1
+
+ # Set the untagged vlan on both ports to 20 and make sure traffic is
+ # still blocked. We intentionally do not pass tagged traffic for the
+ # untagged vlan.
+ atf_check -s exit:0 ifconfig ${bridge} untagged ${epone}a 20
+ atf_check -s exit:0 ifconfig ${bridge} untagged ${eptwo}a 20
+
+ atf_check -s exit:2 -o ignore jexec one ping -c 3 -t 1 192.0.2.2
+ atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1
+
+ atf_check -s exit:0 ifconfig ${bridge} -untagged ${epone}a
+ atf_check -s exit:0 ifconfig ${bridge} -untagged ${eptwo}a
+
+ # Add VLANs 10-30 to the access list; now access should be allowed.
+ ifconfig ${bridge} +tagged ${epone}a 10-30
+ ifconfig ${bridge} +tagged ${eptwo}a 10-30
+ atf_check -s exit:0 -o ignore jexec one ping -c 3 -t 1 192.0.2.2
+ atf_check -s exit:0 -o ignore jexec two ping -c 3 -t 1 192.0.2.1
+
+ # Remove vlan 20 from the access list, now access should be blocked
+ # again.
+ ifconfig ${bridge} -tagged ${epone}a 20
+ ifconfig ${bridge} -tagged ${eptwo}a 20
+ atf_check -s exit:2 -o ignore jexec one ping -c 3 -t 1 192.0.2.2
+ atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1
+}
+
+vlan_filtering_cleanup()
+{
+ vnet_cleanup
+}
+
+#
+# Test the ifconfig 'tagged' option.
+#
+atf_test_case "vlan_ifconfig_tagged" "cleanup"
+vlan_ifconfig_tagged_head()
+{
+ atf_set descr 'test the ifconfig tagged option'
+ atf_set require.user root
+}
+
+vlan_ifconfig_tagged_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ ep=$(vnet_mkepair)
+ bridge=$(vnet_mkbridge)
+
+ ifconfig ${bridge} addm ${ep}a vlanfilter ${ep}a up
+ ifconfig ${ep}a up
+
+ # To start with, no vlans should be configured.
+ atf_check -s exit:0 -o not-match:"tagged" ifconfig ${bridge}
+
+ # Add vlans 100-149.
+ atf_check -s exit:0 ifconfig ${bridge} tagged ${ep}a 100-149
+ atf_check -s exit:0 -o match:"tagged 100-149" ifconfig ${bridge}
+
+ # Replace the vlan list with 139-199.
+ atf_check -s exit:0 ifconfig ${bridge} tagged ${ep}a 139-199
+ atf_check -s exit:0 -o match:"tagged 139-199" ifconfig ${bridge}
+
+ # Add vlans 100-170.
+ atf_check -s exit:0 ifconfig ${bridge} +tagged ${ep}a 100-170
+ atf_check -s exit:0 -o match:"tagged 100-199" ifconfig ${bridge}
+
+ # Remove vlans 104, 105, and 150-159
+ atf_check -s exit:0 ifconfig ${bridge} -tagged ${ep}a 104,105,150-159
+ atf_check -s exit:0 -o match:"tagged 100-103,106-149,160-199" \
+ ifconfig ${bridge}
+
+ # Remove the entire vlan list.
+ atf_check -s exit:0 ifconfig ${bridge} tagged ${ep}a none
+ atf_check -s exit:0 -o not-match:"tagged" ifconfig ${bridge}
+
+ # Test some invalid vlans sets.
+ for bad_vlan in -1 0 4096 4097 foo 0-10 4000-5000 foo-40 40-foo; do
+ atf_check -s exit:1 -e ignore \
+ ifconfig ${bridge} tagged "$bad_vlan"
+ done
+}
+
+vlan_ifconfig_tagged_cleanup()
+{
+ vnet_cleanup
+}
+
+#
+# Test a vlan(4) "SVI" interface on top of a bridge.
+#
+atf_test_case "vlan_svi" "cleanup"
+vlan_svi_head()
+{
+ atf_set descr 'vlan bridge with an SVI'
+ atf_set require.user root
+}
+
+vlan_svi_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ epone=$(vnet_mkepair)
+
+ vnet_mkjail one ${epone}b
+
+ jexec one ifconfig ${epone}b up
+ jexec one ifconfig ${epone}b.20 create 192.0.2.1/24 up
+
+ bridge=$(vnet_mkbridge)
+
+ ifconfig ${bridge} up
+ ifconfig ${epone}a up
+ ifconfig ${bridge} addm ${epone}a tagged ${epone}a 20
+
+ svi=$(vnet_mkvlan)
+ ifconfig ${svi} vlan 20 vlandev ${bridge}
+ ifconfig ${svi} inet 192.0.2.2/24 up
+
+ atf_check -s exit:0 -o ignore ping -c 3 -t 1 192.0.2.1
+}
+
+vlan_svi_cleanup()
+{
+ vnet_cleanup
+}
+
+#
+# Test QinQ (802.1ad).
+#
+atf_test_case "vlan_qinq" "cleanup"
+vlan_qinq_head()
+{
+ atf_set descr 'vlan filtering with QinQ traffic'
+ atf_set require.user root
+}
+
+vlan_qinq_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ epone=$(vnet_mkepair)
+ eptwo=$(vnet_mkepair)
+
+ vnet_mkjail one ${epone}b
+ vnet_mkjail two ${eptwo}b
+
+ # Create a QinQ trunk between the two jails. The outer (provider) tag
+ # is 5, and the inner tag is 10.
+
+ jexec one ifconfig ${epone}b up
+ jexec one ifconfig ${epone}b.5 create vlanproto 802.1ad up
+ jexec one ifconfig ${epone}b.5.10 create inet 192.0.2.1/24 up
+
+ jexec two ifconfig ${eptwo}b up
+ jexec two ifconfig ${eptwo}b.5 create vlanproto 802.1ad up
+ jexec two ifconfig ${eptwo}b.5.10 create inet 192.0.2.2/24 up
+
+ bridge=$(vnet_mkbridge)
+
+ ifconfig ${bridge} up
+ ifconfig ${epone}a up
+ ifconfig ${eptwo}a up
+ ifconfig ${bridge} addm ${epone}a vlanfilter ${epone}a
+ ifconfig ${bridge} addm ${eptwo}a vlanfilter ${eptwo}a
+
+ # Right now there are no VLANs on the access list, so everything
+ # should be blocked.
+ atf_check -s exit:2 -o ignore jexec one ping -c 3 -t 1 192.0.2.2
+ atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1
+
+ # Add the provider tag to the access list; now traffic should be passed.
+ ifconfig ${bridge} +tagged ${epone}a 5
+ ifconfig ${bridge} +tagged ${eptwo}a 5
+ atf_check -s exit:0 -o ignore jexec one ping -c 3 -t 1 192.0.2.2
+ atf_check -s exit:0 -o ignore jexec two ping -c 3 -t 1 192.0.2.1
+}
+
+vlan_qinq_cleanup()
+{
+ vnet_cleanup
+}
+
+# Adding a bridge SVI to a bridge should not be allowed.
+atf_test_case "bridge_svi_in_bridge" "cleanup"
+bridge_svi_in_bridge_head()
+{
+ atf_set descr 'adding a bridge SVI to a bridge is not allowed (1)'
+ atf_set require.user root
+}
+
+bridge_svi_in_bridge_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ bridge=$(vnet_mkbridge)
+ atf_check -s exit:0 ifconfig ${bridge}.1 create
+ atf_check -s exit:1 -e ignore ifconfig ${bridge} addm ${bridge}.1
+}
+
+bridge_svi_in_bridge_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "bridge_transmit_ipv4_unicast"
+ atf_add_test_case "stp"
+ atf_add_test_case "stp_vlan"
+ atf_add_test_case "static"
+ atf_add_test_case "span"
+ atf_add_test_case "inherit_mac"
+ atf_add_test_case "delete_with_members"
+ atf_add_test_case "mac_conflict"
+ atf_add_test_case "stp_validation"
+ atf_add_test_case "gif"
+ atf_add_test_case "mtu"
+ atf_add_test_case "vlan"
+ atf_add_test_case "many_bridge_members"
+ atf_add_test_case "member_ifaddrs_enabled"
+ atf_add_test_case "member_ifaddrs_disabled"
+ atf_add_test_case "member_ifaddrs_vlan"
+ atf_add_test_case "vlan_pvid"
+ atf_add_test_case "vlan_pvid_1q"
+ atf_add_test_case "vlan_pvid_filtered"
+ atf_add_test_case "vlan_pvid_tagged"
+ atf_add_test_case "vlan_filtering"
+ atf_add_test_case "vlan_ifconfig_tagged"
+ atf_add_test_case "vlan_svi"
+ atf_add_test_case "vlan_qinq"
+ atf_add_test_case "bridge_svi_in_bridge"
+}
diff --git a/tests/sys/net/if_clone_test.sh b/tests/sys/net/if_clone_test.sh
new file mode 100755
index 000000000000..864ff86a7d44
--- /dev/null
+++ b/tests/sys/net/if_clone_test.sh
@@ -0,0 +1,574 @@
+#
+# Copyright (c) 2014 Spectra Logic Corporation
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions, and the following disclaimer,
+# without modification.
+# 2. Redistributions in binary form must reproduce at minimum a disclaimer
+# substantially similar to the "NO WARRANTY" disclaimer below
+# ("Disclaimer") and any redistribution must be conditioned upon
+# including a substantially similar Disclaimer requirement for further
+# binary redistribution.
+#
+# NO WARRANTY
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGES.
+#
+# Authors: Alan Somers (Spectra Logic Corporation)
+#
+
+# Outline:
+# For each cloned interface type, do three tests
+# 1) Create and destroy it
+# 2) Create, up, and destroy it
+# 3) Create, disable IPv6 auto address assignment, up, and destroy it
+
+TESTLEN=10 # seconds
+
+atf_test_case epair_stress cleanup
+epair_stress_head()
+{
+ atf_set "descr" "Simultaneously create and destroy an epair(4)"
+ atf_set "require.user" "root"
+}
+epair_stress_body()
+{
+ do_stress "epair"
+}
+epair_stress_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case epair_up_stress cleanup
+epair_up_stress_head()
+{
+ atf_set "descr" "Simultaneously up and detroy an epair(4)"
+ atf_set "require.user" "root"
+}
+epair_up_stress_body()
+{
+ do_up_stress "epair" "" ""
+}
+epair_up_stress_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case epair_destroy_race cleanup
+epair_destroy_race_head()
+{
+ atf_set "descr" "Race if_detach() and if_vmove()"
+ atf_set "require.user" "root"
+}
+epair_destroy_race_body()
+{
+ for i in `seq 1 10`
+ do
+ epair_a=$(ifconfig epair create)
+ echo $epair_a >> devices_to_cleanup
+ epair_b=${epair_a%a}b
+
+ jail -c vnet name="epair_destroy" nopersist path=/ \
+ host.hostname="epair_destroy" vnet.interface="$epair_b" \
+ command=sh -c "ifconfig $epair_b 192.0.2.1/24; sleep 0.1"&
+ pid=$!
+ ifconfig "$epair_a" destroy
+ wait $pid
+ done
+ true
+}
+epair_destroy_race_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case epair_ipv6_up_stress cleanup
+epair_ipv6_up_stress_head()
+{
+ atf_set "descr" "Simultaneously up and destroy an epair(4) with IPv6"
+ atf_set "require.user" "root"
+}
+epair_ipv6_up_stress_body()
+{
+ do_up_stress "epair" "6" ""
+}
+epair_ipv6_up_stress_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case faith_stress cleanup
+faith_stress_head()
+{
+ atf_set "descr" "Simultaneously create and destroy a faith(4)"
+ atf_set "require.user" "root"
+}
+faith_stress_body()
+{
+ do_stress "faith"
+}
+faith_stress_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case faith_up_stress cleanup
+faith_up_stress_head()
+{
+ atf_set "descr" "Simultaneously up and destroy a faith(4)"
+ atf_set "require.user" "root"
+}
+faith_up_stress_body()
+{
+ do_up_stress "faith" "" ""
+}
+faith_up_stress_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case faith_ipv6_up_stress cleanup
+faith_ipv6_up_stress_head()
+{
+ atf_set "descr" "Simultaneously up and destroy a faith(4) with IPv6"
+ atf_set "require.user" "root"
+}
+faith_ipv6_up_stress_body()
+{
+ do_up_stress "faith" "6" ""
+}
+faith_ipv6_up_stress_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case gif_stress cleanup
+gif_stress_head()
+{
+ atf_set "descr" "Simultaneously create and destroy a gif(4)"
+ atf_set "require.user" "root"
+}
+gif_stress_body()
+{
+ do_stress "gif"
+}
+gif_stress_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case gif_up_stress cleanup
+gif_up_stress_head()
+{
+ atf_set "descr" "Simultaneously up and destroy a gif(4)"
+ atf_set "require.user" "root"
+}
+gif_up_stress_body()
+{
+ do_up_stress "gif" "" "p2p"
+}
+gif_up_stress_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case gif_ipv6_up_stress cleanup
+gif_ipv6_up_stress_head()
+{
+ atf_set "descr" "Simultaneously up and destroy a gif(4) with IPv6"
+ atf_set "require.user" "root"
+}
+gif_ipv6_up_stress_body()
+{
+ do_up_stress "gif" "6" "p2p"
+}
+gif_ipv6_up_stress_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case lo_stress cleanup
+lo_stress_head()
+{
+ atf_set "descr" "Simultaneously create and destroy an lo(4)"
+ atf_set "require.user" "root"
+}
+lo_stress_body()
+{
+ do_stress "lo"
+}
+lo_stress_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case lo_up_stress cleanup
+lo_up_stress_head()
+{
+ atf_set "descr" "Simultaneously up and destroy an lo(4)"
+ atf_set "require.user" "root"
+}
+lo_up_stress_body()
+{
+ do_up_stress "lo" "" ""
+}
+lo_up_stress_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case lo_ipv6_up_stress cleanup
+lo_ipv6_up_stress_head()
+{
+ atf_set "descr" "Simultaneously up and destroy an lo(4) with IPv6"
+ atf_set "require.user" "root"
+}
+lo_ipv6_up_stress_body()
+{
+ do_up_stress "lo" "6" ""
+}
+lo_ipv6_up_stress_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case tap_stress cleanup
+tap_stress_head()
+{
+ atf_set "descr" "Simultaneously create and destroy a tap(4)"
+ atf_set "require.user" "root"
+}
+tap_stress_body()
+{
+ do_stress "tap"
+}
+tap_stress_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case tap_up_stress cleanup
+tap_up_stress_head()
+{
+ atf_set "descr" "Simultaneously up and destroy a tap(4)"
+ atf_set "require.user" "root"
+}
+tap_up_stress_body()
+{
+ do_up_stress "tap" "" ""
+}
+tap_up_stress_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case tap_ipv6_up_stress cleanup
+tap_ipv6_up_stress_head()
+{
+ atf_set "descr" "Simultaneously up and destroy a tap(4) with IPv6"
+ atf_set "require.user" "root"
+}
+tap_ipv6_up_stress_body()
+{
+ do_up_stress "tap" "6" ""
+}
+tap_ipv6_up_stress_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case tun_stress cleanup
+tun_stress_head()
+{
+ atf_set "descr" "Simultaneously create and destroy a tun(4)"
+ atf_set "require.user" "root"
+}
+tun_stress_body()
+{
+ do_stress "tun"
+}
+tun_stress_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case tun_up_stress cleanup
+tun_up_stress_head()
+{
+ atf_set "descr" "Simultaneously up and destroy a tun(4)"
+ atf_set "require.user" "root"
+}
+tun_up_stress_body()
+{
+ do_up_stress "tun" "" "p2p"
+}
+tun_up_stress_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case tun_ipv6_up_stress cleanup
+tun_ipv6_up_stress_head()
+{
+ atf_set "descr" "Simultaneously up and destroy a tun(4) with IPv6"
+ atf_set "require.user" "root"
+}
+tun_ipv6_up_stress_body()
+{
+ do_up_stress "tun" "6" "p2p"
+}
+tun_ipv6_up_stress_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case vlan_stress cleanup
+vlan_stress_head()
+{
+ atf_set "descr" "Simultaneously create and destroy a vlan(4)"
+ atf_set "require.user" "root"
+}
+vlan_stress_body()
+{
+ do_stress "vlan"
+}
+vlan_stress_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case vlan_up_stress cleanup
+vlan_up_stress_head()
+{
+ atf_set "descr" "Simultaneously up and destroy a vlan(4)"
+ atf_set "require.user" "root"
+}
+vlan_up_stress_body()
+{
+ do_up_stress "vlan" "" ""
+}
+vlan_up_stress_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case vlan_ipv6_up_stress cleanup
+vlan_ipv6_up_stress_head()
+{
+ atf_set "descr" "Simultaneously up and destroy a vlan(4) with IPv6"
+ atf_set "require.user" "root"
+}
+vlan_ipv6_up_stress_body()
+{
+ do_up_stress "vlan" "6" ""
+}
+vlan_ipv6_up_stress_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case vmnet_stress cleanup
+vmnet_stress_head()
+{
+ atf_set "descr" "Simultaneously create and destroy a vmnet(4)"
+ atf_set "require.user" "root"
+}
+vmnet_stress_body()
+{
+ do_stress "vmnet"
+}
+vmnet_stress_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case vmnet_up_stress cleanup
+vmnet_up_stress_head()
+{
+ atf_set "descr" "Simultaneously up and destroy a vmnet(4)"
+ atf_set "require.user" "root"
+}
+vmnet_up_stress_body()
+{
+ do_up_stress "vmnet" "" ""
+}
+vmnet_up_stress_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case vmnet_ipv6_up_stress cleanup
+vmnet_ipv6_up_stress_head()
+{
+ atf_set "descr" "Simultaneously up and destroy a vmnet(4) with IPv6"
+ atf_set "require.user" "root"
+}
+vmnet_ipv6_up_stress_body()
+{
+ do_up_stress "vmnet" "6" ""
+}
+vmnet_ipv6_up_stress_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case epair_ipv6_up_stress
+ atf_add_test_case epair_stress
+ atf_add_test_case epair_up_stress
+ atf_add_test_case epair_destroy_race
+ atf_add_test_case faith_ipv6_up_stress
+ atf_add_test_case faith_stress
+ atf_add_test_case faith_up_stress
+ atf_add_test_case gif_ipv6_up_stress
+ atf_add_test_case gif_stress
+ atf_add_test_case gif_up_stress
+ # Don't test lagg; it has its own test program
+ atf_add_test_case lo_ipv6_up_stress
+ atf_add_test_case lo_stress
+ atf_add_test_case lo_up_stress
+ atf_add_test_case tap_ipv6_up_stress
+ atf_add_test_case tap_stress
+ atf_add_test_case tap_up_stress
+ atf_add_test_case tun_ipv6_up_stress
+ atf_add_test_case tun_stress
+ atf_add_test_case tun_up_stress
+ atf_add_test_case vlan_ipv6_up_stress
+ atf_add_test_case vlan_stress
+ atf_add_test_case vlan_up_stress
+ atf_add_test_case vmnet_ipv6_up_stress
+ atf_add_test_case vmnet_stress
+ atf_add_test_case vmnet_up_stress
+}
+
+do_stress()
+{
+ local IFACE CREATOR_PID DESTROYER_PID
+
+ IFACE=`get_iface $1`
+
+ # First thread: create the interface
+ while true; do
+ ifconfig ${IFACE%a} create 2>/dev/null && \
+ echo -n . >> creator_count.txt
+ done &
+ CREATOR_PID=$!
+
+ # Second thread: destroy the lagg
+ while true; do
+ ifconfig $IFACE destroy 2>/dev/null && \
+ echo -n . >> destroyer_count.txt
+ done &
+ DESTROYER_PID=$!
+
+ sleep ${TESTLEN}
+ kill $CREATOR_PID
+ kill $DESTROYER_PID
+ echo "Created ${IFACE%a} `stat -f %z creator_count.txt` times."
+ echo "Destroyed it `stat -f %z destroyer_count.txt` times."
+}
+
+# Implement the up stress tests
+# Parameters
+# $1 Interface class, etc "lo" or "tap"
+# $2 "6" to enable IPv6 auto address assignment, anything else otherwise
+# $3 p2p for point to point interfaces, anything else for normal interfaces
+do_up_stress()
+{
+ local ADDR DSTADDR MASK MEAN_SLEEP_SECONDS MAX_SLEEP_USECS \
+ IFACE IPV6 P2P SRCDIR LOOP_PID ipv6_cmd up_cmd
+
+ # Configure the interface to use an RFC5737 nonrouteable addresses
+ ADDR="192.0.2.2"
+ DSTADDR="192.0.2.3"
+ MASK="24"
+ # ifconfig takes about 10ms to run. To increase race coverage,
+ # randomly delay the two commands relative to each other by 5ms either
+ # way.
+ MEAN_SLEEP_SECONDS=.005
+ MAX_SLEEP_USECS=10000
+
+ IFACE=`get_iface $1`
+ IPV6=$2
+ P2P=$3
+
+ SRCDIR=$( atf_get_srcdir )
+ if [ "$IPV6" = 6 ]; then
+ ipv6_cmd="true"
+ else
+ ipv6_cmd="ifconfig $IFACE inet6 ifdisabled"
+ fi
+ if [ "$P2P" = "p2p" ]; then
+ up_cmd="ifconfig $IFACE up ${ADDR}/${MASK} ${DSTADDR}"
+ else
+ up_cmd="ifconfig $IFACE up ${ADDR}/${MASK}"
+ fi
+ while true; do
+ eval "$ipv6_cmd"
+ { sleep ${MEAN_SLEEP_SECONDS} && \
+ eval "$up_cmd" 2> /dev/null &&
+ echo -n . >> up_count.txt ; } &
+ { ${SRCDIR}/randsleep ${MAX_SLEEP_USECS} && \
+ ifconfig $IFACE destroy &&
+ echo -n . >> destroy_count.txt ; } &
+ wait
+ ifconfig ${IFACE%a} create
+ done &
+ LOOP_PID=$!
+
+ sleep ${TESTLEN}
+ kill $LOOP_PID
+ echo "Upped ${IFACE} `stat -f %z up_count.txt` times."
+ echo "Destroyed it `stat -f %z destroy_count.txt` times."
+}
+
+# Creates a new cloned interface, registers it for cleanup, and echoes it
+# params: $1 Interface class name (tap, gif, etc)
+get_iface()
+{
+ local CLASS DEV N
+
+ CLASS=$1
+ N=0
+ while ! ifconfig ${CLASS}${N} create > /dev/null 2>&1; do
+ if [ "$N" -ge 8 ]; then
+ atf_skip "Could not create a ${CLASS} interface"
+ else
+ N=$(($N + 1))
+ fi
+ done
+ if [ ${CLASS} = "epair" ]; then
+ DEV=${CLASS}${N}a
+ else
+ DEV=${CLASS}${N}
+ fi
+ # Record the device so we can clean it up later
+ echo ${DEV} >> "devices_to_cleanup"
+ echo ${DEV}
+}
+
+
+cleanup_ifaces()
+{
+ local DEV
+
+ for DEV in `cat "devices_to_cleanup"`; do
+ ifconfig ${DEV} destroy
+ done
+ true
+}
diff --git a/tests/sys/net/if_epair.c b/tests/sys/net/if_epair.c
new file mode 100644
index 000000000000..0817b298d427
--- /dev/null
+++ b/tests/sys/net/if_epair.c
@@ -0,0 +1,77 @@
+/*-
+ * Copyright (c) 2020 Kristof Provost <kp@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <net/if.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <strings.h>
+
+#include <atf-c.h>
+#include "freebsd_test_suite/macros.h"
+
+ATF_TC(params);
+ATF_TC_HEAD(params, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(params, tc)
+{
+ struct ifreq ifr;
+ int s;
+
+ kldload("if_epair");
+ ATF_REQUIRE_KERNEL_MODULE("if_epair");
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0)
+ atf_tc_fail("Failed to create socket");
+
+ bzero(&ifr, sizeof(ifr));
+ ifr.ifr_data = (caddr_t)-1;
+ (void) strlcpy(ifr.ifr_name, "epair", sizeof(ifr.ifr_name));
+
+ if (ioctl(s, SIOCIFCREATE2, &ifr) < 0)
+ atf_tc_fail("Failed to create interface");
+
+ if (ioctl(s, SIOCIFDESTROY, &ifr) < 0)
+ atf_tc_fail("Failed to destroy interface");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, params);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/net/if_epair_test.sh b/tests/sys/net/if_epair_test.sh
new file mode 100644
index 000000000000..e1d0c0266b2d
--- /dev/null
+++ b/tests/sys/net/if_epair_test.sh
@@ -0,0 +1,65 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2023 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+atf_test_case "pcp" "cleanup"
+pcp_head()
+{
+ atf_set descr 'Test PCP over if_epair. PR#270736'
+ atf_set require.user root
+}
+
+pcp_body()
+{
+ vnet_init
+
+ j="if_epair_test_pcp"
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail ${j}one ${epair}a
+ vnet_mkjail ${j}two ${epair}b
+
+ jexec ${j}one ifconfig ${epair}a 192.0.2.1/24 pcp 3 up
+ jexec ${j}two ifconfig ${epair}b 192.0.2.2/24 up
+
+ atf_check -s exit:0 -o ignore \
+ jexec ${j}one ping -c 1 192.0.2.2
+
+ # Now set a different PCP. This used to lead to double tagging and failed pin.
+ atf_check -s exit:0 -o ignore \
+ jexec ${j}one ping -C5 -c 1 192.0.2.2
+}
+
+pcp_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "pcp"
+}
diff --git a/tests/sys/net/if_gif.sh b/tests/sys/net/if_gif.sh
new file mode 100644
index 000000000000..16b0b1a6fca0
--- /dev/null
+++ b/tests/sys/net/if_gif.sh
@@ -0,0 +1,76 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2021 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+atf_test_case "basic" "cleanup"
+basic_head()
+{
+ atf_set descr 'Basic gif(4) test'
+ atf_set require.user root
+}
+
+basic_body()
+{
+ vnet_init
+ if ! kldstat -q -m if_gif; then
+ atf_skip "This test requires if_gif"
+ fi
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail one ${epair}a
+ jexec one ifconfig ${epair}a 192.0.2.1/24 up
+ gone=$(jexec one ifconfig gif create)
+ jexec one ifconfig $gone tunnel 192.0.2.1 192.0.2.2
+ jexec one ifconfig $gone inet 198.51.100.1/24 198.51.100.2 up
+
+ vnet_mkjail two ${epair}b
+ jexec two ifconfig ${epair}b 192.0.2.2/24 up
+ gtwo=$(jexec two ifconfig gif create)
+ jexec two ifconfig $gtwo tunnel 192.0.2.2 192.0.2.1
+ jexec two ifconfig $gtwo inet 198.51.100.2/24 198.51.100.1 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ jexec one ping -c 1 192.0.2.2
+
+ # Tunnel test
+ atf_check -s exit:0 -o ignore \
+ jexec one ping -c 1 198.51.100.2
+ atf_check -s exit:0 -o ignore \
+ jexec two ping -c 1 198.51.100.1
+}
+
+basic_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "basic"
+}
diff --git a/tests/sys/net/if_lagg_test.sh b/tests/sys/net/if_lagg_test.sh
new file mode 100755
index 000000000000..e2b998599991
--- /dev/null
+++ b/tests/sys/net/if_lagg_test.sh
@@ -0,0 +1,463 @@
+#
+# Copyright (c) 2014 Spectra Logic Corporation
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions, and the following disclaimer,
+# without modification.
+# 2. Redistributions in binary form must reproduce at minimum a disclaimer
+# substantially similar to the "NO WARRANTY" disclaimer below
+# ("Disclaimer") and any redistribution must be conditioned upon
+# including a substantially similar Disclaimer requirement for further
+# binary redistribution.
+#
+# NO WARRANTY
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGES.
+#
+# Authors: Alan Somers (Spectra Logic Corporation)
+#
+
+atf_test_case create cleanup
+create_head()
+{
+ atf_set "descr" "Create a lagg and assign an address"
+ atf_set "require.user" "root"
+}
+create_body()
+{
+ local TAP0 TAP1 LAGG MAC
+
+ # Configure the lagg interface to use an RFC5737 nonrouteable addresses
+ ADDR="192.0.2.2"
+ MASK="24"
+
+ TAP0=`get_tap`
+ TAP1=`get_tap`
+ LAGG=`get_lagg`
+
+ # Create the lagg
+ ifconfig $TAP0 up
+ ifconfig $TAP1 up
+ atf_check ifconfig $LAGG up laggport $TAP0 laggport $TAP1 \
+ ${ADDR}/${MASK}
+ atf_check -o match:"inet ${ADDR}" ifconfig $LAGG
+ atf_check -o match:"laggport: ${TAP0}" ifconfig $LAGG
+ atf_check -o match:"laggport: ${TAP1}" ifconfig $LAGG
+
+ # Check that all members have the same MAC
+ MAC=`ifconfig $LAGG | awk '/ether/ {print $2}'`
+ atf_check -o match:"ether ${MAC}" ifconfig $TAP0
+ atf_check -o match:"ether ${MAC}" ifconfig $TAP1
+
+ # Check that no members have an IPv6 link-local address. IPv6
+ # link-local addresses should never be merged in any way to prevent
+ # scope violation.
+ atf_check -o not-match:"inet6 fe80:" ifconfig $TAP0
+ atf_check -o not-match:"inet6 fe80:" ifconfig $TAP1
+}
+create_cleanup()
+{
+ cleanup_tap_and_lagg
+}
+
+atf_test_case status_stress cleanup
+status_stress_head()
+{
+ atf_set "descr" "Simultaneously query a lagg while also creating or destroying it."
+ atf_set "require.user" "root"
+}
+status_stress_body()
+{
+ local TAP0 TAP1 LAGG MAC
+
+ # Configure the lagg interface to use an RFC5737 nonrouteable addresses
+ ADDR="192.0.2.2"
+ MASK="24"
+
+ TAP0=`get_tap`
+ TAP1=`get_tap`
+ TAP2=`get_tap`
+ TAP3=`get_tap`
+ LAGG=`get_lagg`
+
+ # Up the lagg's children
+ ifconfig $TAP0 inet6 ifdisabled up
+ ifconfig $TAP1 inet6 ifdisabled up
+ ifconfig $TAP2 inet6 ifdisabled up
+ ifconfig $TAP3 inet6 ifdisabled up
+
+ # First thread: create and destroy the lagg
+ while true; do
+ ifconfig $LAGG destroy 2>&1
+ ifconfig $LAGG create 2>/dev/null
+ ifconfig $LAGG inet6 ifdisabled
+ ifconfig $LAGG up laggport $TAP0 laggport $TAP1 laggport $TAP2\
+ laggport $TAP3 ${ADDR}/${MASK} 2>/dev/null
+ echo -n . >> creator_count.txt
+ done &
+ CREATOR_PID=$!
+
+ # Second thread: Query the lagg's status
+ while true; do
+ ifconfig -am 2> /dev/null > /dev/null
+ echo -n . >> querier_count.txt
+ done &
+ QUERIER_PID=$!
+
+ sleep 60
+ kill $CREATOR_PID
+ kill $QUERIER_PID
+ echo "Created the lagg `stat -f %z creator_count.txt` times."
+ echo "Queried its status `stat -f %z querier_count.txt` times"
+}
+status_stress_cleanup()
+{
+ cleanup_tap_and_lagg
+}
+
+atf_test_case create_destroy_stress cleanup
+create_destroy_stress_head()
+{
+ atf_set "descr" "Simultaneously create and destroy a lagg"
+ atf_set "require.user" "root"
+}
+create_destroy_stress_body()
+{
+ local TAP0 TAP1 LAGG MAC
+
+ TAP0=`get_tap`
+ TAP1=`get_tap`
+ TAP2=`get_tap`
+ TAP3=`get_tap`
+ LAGG=`get_lagg`
+
+ # Up the lagg's children
+ ifconfig $TAP0 inet6 ifdisabled up
+ ifconfig $TAP1 inet6 ifdisabled up
+ ifconfig $TAP2 inet6 ifdisabled up
+ ifconfig $TAP3 inet6 ifdisabled up
+
+ # First thread: create the lagg
+ while true; do
+ ifconfig $LAGG create 2>/dev/null && \
+ echo -n . >> creator_count.txt
+ done &
+ CREATOR_PID=$!
+
+ # Second thread: destroy the lagg
+ while true; do
+ ifconfig $LAGG destroy 2>/dev/null && \
+ echo -n . >> destroyer_count.txt
+ done &
+ DESTROYER_PID=$!
+
+ sleep 60
+ kill $CREATOR_PID
+ kill $DESTROYER_PID
+ echo "Created the lagg `stat -f %z creator_count.txt` times."
+ echo "Destroyed it `stat -f %z destroyer_count.txt` times."
+}
+create_destroy_stress_cleanup()
+{
+ cleanup_tap_and_lagg
+}
+
+# This test regresses a panic that is particular to LACP. If the child's link
+# state changes while the lagg is being destroyed, lacp_linkstate can
+# use-after-free. The problem is compounded by two factors:
+# 1) In SpectraBSD, downing the parent will also down the child
+# 2) The cxgbe driver will show the link state as "no carrier" as soon as you
+# down the interface.
+# TeamTrack: P2_30328
+atf_test_case lacp_linkstate_destroy_stress cleanup
+lacp_linkstate_destroy_stress_head()
+{
+ atf_set "descr" "Simultaneously destroy an LACP lagg and change its childrens link states"
+ atf_set "require.user" "root"
+}
+lacp_linkstate_destroy_stress_body()
+{
+ local TAP0 TAP1 LAGG MAC SRCDIR
+
+ # Configure the lagg interface to use an RFC5737 nonrouteable addresses
+ ADDR="192.0.2.2"
+ MASK="24"
+ # ifconfig takes about 10ms to run. To increase race coverage,
+ # randomly delay the two commands relative to each other by 5ms either
+ # way.
+ MEAN_SLEEP_SECONDS=.005
+ MAX_SLEEP_USECS=10000
+
+ TAP0=`get_tap`
+ TAP1=`get_tap`
+ LAGG=`get_lagg`
+
+ # Up the lagg's children
+ ifconfig $TAP0 inet6 ifdisabled up
+ ifconfig $TAP1 inet6 ifdisabled up
+
+ SRCDIR=$( atf_get_srcdir )
+ while true; do
+ ifconfig $LAGG inet6 ifdisabled
+ # We must open the tap devices to change their link states
+ cat /dev/$TAP0 > /dev/null &
+ CAT0_PID=$!
+ cat /dev/$TAP1 > /dev/null &
+ CAT1_PID=$!
+ ifconfig $LAGG up laggport $TAP0 laggport $TAP1 \
+ ${ADDR}/${MASK} 2> /dev/null &&
+ { sleep ${MEAN_SLEEP_SECONDS} && \
+ kill $CAT0_PID &&
+ kill $CAT1_PID &&
+ echo -n . >> linkstate_count.txt ; } &
+ { ${SRCDIR}/randsleep ${MAX_SLEEP_USECS} && \
+ ifconfig $LAGG destroy &&
+ echo -n . >> destroy_count.txt ; } &
+ wait
+ ifconfig $LAGG create
+ done &
+ LOOP_PID=$!
+
+ sleep 60
+ kill $LOOP_PID
+ echo "Disconnected the children `stat -f %z linkstate_count.txt` times."
+ echo "Destroyed the lagg `stat -f %z destroy_count.txt` times."
+}
+lacp_linkstate_destroy_stress_cleanup()
+{
+ cleanup_tap_and_lagg
+}
+
+atf_test_case up_destroy_stress cleanup
+up_destroy_stress_head()
+{
+ atf_set "descr" "Simultaneously up and destroy a lagg"
+ atf_set "require.user" "root"
+}
+up_destroy_stress_body()
+{
+ local TAP0 TAP1 LAGG MAC SRCDIR
+
+ # Configure the lagg interface to use an RFC5737 nonrouteable addresses
+ ADDR="192.0.2.2"
+ MASK="24"
+ # ifconfig takes about 10ms to run. To increase race coverage,
+ # randomly delay the two commands relative to each other by 5ms either
+ # way.
+ MEAN_SLEEP_SECONDS=.005
+ MAX_SLEEP_USECS=10000
+
+ TAP0=`get_tap`
+ TAP1=`get_tap`
+ TAP2=`get_tap`
+ TAP3=`get_tap`
+ LAGG=`get_lagg`
+
+ # Up the lagg's children
+ ifconfig $TAP0 inet6 ifdisabled up
+ ifconfig $TAP1 inet6 ifdisabled up
+ ifconfig $TAP2 inet6 ifdisabled up
+ ifconfig $TAP3 inet6 ifdisabled up
+
+ SRCDIR=$( atf_get_srcdir )
+ while true; do
+ ifconfig $LAGG inet6 ifdisabled
+ { sleep ${MEAN_SLEEP_SECONDS} && \
+ ifconfig $LAGG up laggport $TAP0 laggport $TAP1 \
+ laggport $TAP2 laggport $TAP3 \
+ ${ADDR}/${MASK} 2> /dev/null &&
+ echo -n . >> up_count.txt ; } &
+ { ${SRCDIR}/randsleep ${MAX_SLEEP_USECS} && \
+ ifconfig $LAGG destroy &&
+ echo -n . >> destroy_count.txt ; } &
+ wait
+ ifconfig $LAGG create
+ done &
+ LOOP_PID=$!
+
+ sleep 60
+ kill $LOOP_PID
+ echo "Upped the lagg `stat -f %z up_count.txt` times."
+ echo "Destroyed it `stat -f %z destroy_count.txt` times."
+}
+up_destroy_stress_cleanup()
+{
+ cleanup_tap_and_lagg
+}
+
+atf_test_case set_ether cleanup
+set_ether_head()
+{
+ atf_set "descr" "Set a lagg's ethernet address"
+ atf_set "require.user" "root"
+}
+set_ether_body()
+{
+ local TAP0 TAP1 LAGG MAC
+
+ # Configure the lagg interface to use an RFC5737 nonrouteable addresses
+ ADDR="192.0.2.2"
+ MASK="24"
+ MAC="00:11:22:33:44:55"
+
+ TAP0=`get_tap`
+ TAP1=`get_tap`
+ LAGG=`get_lagg`
+
+ # Create the lagg
+ ifconfig $TAP0 up
+ ifconfig $TAP1 up
+ atf_check ifconfig $LAGG up laggport $TAP0 laggport $TAP1 \
+ ${ADDR}/${MASK}
+
+ # Change the lagg's ethernet address
+ atf_check ifconfig $LAGG ether ${MAC}
+
+ # Check that all members have the same MAC
+ atf_check -o match:"ether ${MAC}" ifconfig $LAGG
+ atf_check -o match:"ether ${MAC}" ifconfig $TAP0
+ atf_check -o match:"ether ${MAC}" ifconfig $TAP1
+}
+set_ether_cleanup()
+{
+ cleanup_tap_and_lagg
+}
+
+atf_test_case updown cleanup
+updown_head()
+{
+ atf_set "descr" "upping or downing a lagg ups or downs its children"
+ atf_set "require.user" "root"
+}
+updown_body()
+{
+ local TAP0 TAP1 LAGG MAC
+
+ # Configure the lagg interface to use an RFC5737 nonrouteable addresses
+ ADDR="192.0.2.2"
+ MASK="24"
+ MAC="00:11:22:33:44:55"
+
+ TAP0=`get_tap`
+ TAP1=`get_tap`
+ LAGG=`get_lagg`
+
+ # Create the lagg
+ ifconfig $TAP0 up
+ ifconfig $TAP1 up
+ atf_check ifconfig $LAGG up laggport $TAP0 laggport $TAP1 \
+ ${ADDR}/${MASK}
+
+ # Down the lagg
+ ifconfig $LAGG down
+ atf_check -o not-match:"flags=.*\<UP\>" ifconfig $LAGG
+ atf_check -o not-match:"flags=.*\<UP\>" ifconfig $TAP0
+ atf_check -o not-match:"flags=.*\<UP\>" ifconfig $TAP1
+ # Up the lagg again
+ ifconfig $LAGG up
+ atf_check -o match:"flags=.*\<UP\>" ifconfig $LAGG
+ atf_check -o match:"flags=.*\<UP\>" ifconfig $TAP0
+ atf_check -o match:"flags=.*\<UP\>" ifconfig $TAP1
+
+ # Check that no members have acquired an IPv6 link-local address by
+ # virtue of being upped. IPv6 link-local addresses should never be
+ # merged in any way to prevent scope violation.
+ atf_check -o not-match:"inet6 fe80:" ifconfig $TAP0
+ atf_check -o not-match:"inet6 fe80:" ifconfig $TAP1
+}
+updown_cleanup()
+{
+ cleanup_tap_and_lagg
+}
+
+# Check for lock-order reversals. For best results, this test should be run
+# last.
+atf_test_case witness
+witness_head()
+{
+ atf_set "descr" "Check witness(4) for lock-order reversals in if_lagg"
+}
+witness_body()
+{
+ if [ "$(atf_config_get ci false)" = "true" ]; then
+ atf_skip "https://bugs.freebsd.org/244163 and https://bugs.freebsd.org/251726"
+ fi
+ if [ `sysctl -n debug.witness.watch` -ne 1 ]; then
+ atf_skip "witness(4) is not enabled"
+ fi
+ if `sysctl -n debug.witness.badstacks | grep -q 'at lagg_'`; then
+ sysctl debug.witness.badstacks
+ atf_fail "Lock-order reversals involving if_lagg.c detected"
+ fi
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case create
+ atf_add_test_case create_destroy_stress
+ atf_add_test_case lacp_linkstate_destroy_stress
+ atf_add_test_case set_ether
+ atf_add_test_case status_stress
+ atf_add_test_case up_destroy_stress
+ atf_add_test_case updown
+ # For best results, keep the witness test last
+ atf_add_test_case witness
+}
+
+
+# Creates a new tap(4) interface, registers it for cleanup, and echoes it
+get_tap()
+{
+ local TAPN=0
+ while ! ifconfig tap${TAPN} create > /dev/null 2>&1; do
+ if [ "$TAPN" -ge 8 ]; then
+ atf_skip "Could not create a tap(4) interface"
+ else
+ TAPN=$(($TAPN + 1))
+ fi
+ done
+ local TAPD=tap${TAPN}
+ # Record the TAP device so we can clean it up later
+ echo ${TAPD} >> "devices_to_cleanup"
+ echo ${TAPD}
+}
+
+# Creates a new lagg(4) interface, registers it for cleanup, and echoes it
+get_lagg()
+{
+ local LAGGN=0
+ while ! ifconfig lagg${LAGGN} create > /dev/null 2>&1; do
+ if [ "$LAGGN" -ge 8 ]; then
+ atf_skip "Could not create a lagg(4) interface"
+ else
+ LAGGN=$(($LAGGN + 1))
+ fi
+ done
+ local LAGGD=lagg${LAGGN}
+ # Record the lagg device so we can clean it up later
+ echo ${LAGGD} >> "devices_to_cleanup"
+ echo ${LAGGD}
+}
+
+cleanup_tap_and_lagg()
+{
+ local DEV
+
+ for DEV in `cat "devices_to_cleanup"`; do
+ ifconfig ${DEV} destroy
+ done
+ true
+}
diff --git a/tests/sys/net/if_ovpn/Makefile b/tests/sys/net/if_ovpn/Makefile
new file mode 100644
index 000000000000..85746226e122
--- /dev/null
+++ b/tests/sys/net/if_ovpn/Makefile
@@ -0,0 +1,30 @@
+.include <src.opts.mk>
+
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/net/if_ovpn
+
+.if ${MK_PF} != "no"
+ATF_TESTS_SH+= if_ovpn
+TEST_METADATA.if_ovpn+= execenv="jail"
+TEST_METADATA.if_ovpn+= execenv_jail_params="vnet allow.raw_sockets"
+.endif
+ATF_TESTS_C+= if_ovpn_c
+
+LIBADD+= nv
+
+TESTS_SUBDIRS+= ccd
+
+${PACKAGE}FILES+= \
+ ca.crt \
+ client.crt \
+ client.key \
+ client2.crt \
+ client2.key \
+ dh.pem \
+ server.crt \
+ server.key \
+ user.pass \
+ utils.subr
+
+.include <bsd.test.mk>
diff --git a/tests/sys/net/if_ovpn/ca.crt b/tests/sys/net/if_ovpn/ca.crt
new file mode 100644
index 000000000000..4bdde726d12b
--- /dev/null
+++ b/tests/sys/net/if_ovpn/ca.crt
@@ -0,0 +1,33 @@
+-----BEGIN CERTIFICATE-----
+MIIFrzCCA5egAwIBAgIUFByUexsc+WCQtaEQZCg+nrJaFDowDQYJKoZIhvcNAQEL
+BQAwZjELMAkGA1UEBhMCS0cxCzAJBgNVBAgMAk5BMRAwDgYDVQQHDAdCSVNIS0VL
+MRUwEwYDVQQKDAxPcGVuVlBOLVRFU1QxITAfBgkqhkiG9w0BCQEWEm1lQG15aG9z
+dC5teWRvbWFpbjAgFw0yMjA0MjcxNDM1NTZaGA8yMTIyMDQwMzE0MzU1NlowZjEL
+MAkGA1UEBhMCS0cxCzAJBgNVBAgMAk5BMRAwDgYDVQQHDAdCSVNIS0VLMRUwEwYD
+VQQKDAxPcGVuVlBOLVRFU1QxITAfBgkqhkiG9w0BCQEWEm1lQG15aG9zdC5teWRv
+bWFpbjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJnak7swgeJta01H
+4pNQte3S0bdEGRvyhkzY8HIxe/pdz371fSL40iBQ29PVZseMVOsQIrIolhma4qTy
+tMyU9VEfJRlBj8hdlbIpb8FDsvMOFyC+7+GxHdkAQlFV5fUj7JydnxiY+L87W7WE
+VaTy9ZRtuIXhvkVZYTHzvVwXwL5e5EdcJKHsdam49ty4rs6CE7J06GYnwmLk/nDw
+vZZhHTB5V+vNeurRoxKyYvUXePTCqh6cGh2as061JxU1j1vj3cTNVlPymO85Etl1
+t4uEHjXyLVK2+OCcauQGn6QT02xqFLRsbk42gWOksFj50NDQVtp71JIMAmwzz2yi
++cqz1qqdyOlfLeP2oow1jYa3Hhht5Es90m6YkeF/073eLW/nuVsoD57PZ1rnClX+
+9Zs24LAN/vnXR1gK/nAZsv9CNtRdWt9yTSmq/oGZj4kWU154XxRLfcDa9gj5X+g9
+Z4w6YaJtsa8XaOelPWnBg/JXLrdE731DwyDRqlSPYgwzU6g3CgjRV3/cJs/RaxWB
+Au1Tdpd2T5KbeLivYJnhNApE0J4CxXrudfglAaZV8tG0SK8F07Sd0uS4Sa83I1Bh
+IjAFcjBcMxv1gWNZN903V0etmCkIwONlqTSWKNYGV+9EQ1moHDDXRUK14He95AQ7
+ZNadjsqZuDGl925HW4wjk2HLa42FAgMBAAGjUzBRMB0GA1UdDgQWBBSIBxjB/i4J
+Ln3guRuDqVTeCbEWfjAfBgNVHSMEGDAWgBSIBxjB/i4JLn3guRuDqVTeCbEWfjAP
+BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQAMrtwU2YfLGp2QWH33
+qH4dZh92iaSApEDwIggBj+wrc/6LbbS+Mm6UTOj9SYNcDZpj/ia4m1OXV7dC1adZ
+wGk94qBknwgxPkxS0f3HLlvYDnSWCOTP5CJPfdQWPRX0WQO8l5s+tIigLaioS76N
+C/g+I7ytPYbCN7sZRgQJr+vGxOxaBD8GrSq6/brTSUMCHUjE/ylFZ9ykBtmXTVIb
+u9WsnWTyA7h5Nzhkh0VvN0o/EhlgRpUdT1661QlIvWsyfd6sxrtLum4h88DUkjw9
+qlMDTnkhWUfyPg8kS99dLodnxp1QeW0ISeWpAucJuOvu3ode/N3lOrxq88OrZQNJ
+upQsdUxLEU6DzQlvBd2s4d8Ghvk3l65u688cE6dXIcNPEp78wy/IXkVvTRTstpuA
+Ep9ZNwrEvaPQDxBJ6a2sPKwXst1NZZgmPQG2ZbpQfCQtJ0zYZWpI/LytiC/05joi
+/aGh01GN7nODt9U7rtZtCQjjmIlK7fuBJLL9yQXcpzT5sItdQSkn9QuCBlnlxMqx
+felbaNPxTLJVqCilqlx/xaybDljduKLvJouR+l/UjrXz+n02lzxPQ3FIr8/vJlVf
+EFbSmkzS3C/O5gXUxHTq44z6LbnosjyiPEB2J7n5kvsA8HTynZU4GCrHa5LLG1eg
+1odsgCIYiAwNBCbPtWWykUHXhA==
+-----END CERTIFICATE-----
diff --git a/tests/sys/net/if_ovpn/ca.key b/tests/sys/net/if_ovpn/ca.key
new file mode 100644
index 000000000000..79537a84c6a0
--- /dev/null
+++ b/tests/sys/net/if_ovpn/ca.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKQIBAAKCAgEAmdqTuzCB4m1rTUfik1C17dLRt0QZG/KGTNjwcjF7+l3PfvV9
+IvjSIFDb09Vmx4xU6xAisiiWGZripPK0zJT1UR8lGUGPyF2VsilvwUOy8w4XIL7v
+4bEd2QBCUVXl9SPsnJ2fGJj4vztbtYRVpPL1lG24heG+RVlhMfO9XBfAvl7kR1wk
+oex1qbj23LiuzoITsnToZifCYuT+cPC9lmEdMHlX68166tGjErJi9Rd49MKqHpwa
+HZqzTrUnFTWPW+PdxM1WU/KY7zkS2XW3i4QeNfItUrb44Jxq5AafpBPTbGoUtGxu
+TjaBY6SwWPnQ0NBW2nvUkgwCbDPPbKL5yrPWqp3I6V8t4/aijDWNhrceGG3kSz3S
+bpiR4X/Tvd4tb+e5WygPns9nWucKVf71mzbgsA3++ddHWAr+cBmy/0I21F1a33JN
+Kar+gZmPiRZTXnhfFEt9wNr2CPlf6D1njDphom2xrxdo56U9acGD8lcut0TvfUPD
+INGqVI9iDDNTqDcKCNFXf9wmz9FrFYEC7VN2l3ZPkpt4uK9gmeE0CkTQngLFeu51
++CUBplXy0bRIrwXTtJ3S5LhJrzcjUGEiMAVyMFwzG/WBY1k33TdXR62YKQjA42Wp
+NJYo1gZX70RDWagcMNdFQrXgd73kBDtk1p2Oypm4MaX3bkdbjCOTYctrjYUCAwEA
+AQKCAgBCSLw6+nQK5E9FVUIVa8Flu90kUs9qjfs0YoZ8/DrIq9/4d7U4+vA0ggGj
+ax5DvH7dYu8/yGKal3Mr03D3bvmdwIhQMEHM9hzHTDjcj9kqBBkMv5ZDqvYMBqOL
+vTE8gwSumO3xP/5zDRS+yEvJq+8Hypmj/JTn9dD9H2Cmq4kbu3AoGleh92jd/VVF
+1L9jMwFfciSp1llWGAhTCjTz89UKjEzHx5g8UKMsK/ScWUeAU9nNJD3QIVU9BKjY
+52FjGnHyFvEpa6xd7D3luGSiVAJcBYFnUHM6+cjHgt8GZpBA9hcDgG+pfKwUDOD5
+BG/ldszkhlMXtNraWRv1VuW9YeuKnTwagxNLuqYOe2PyFq9sYcpHP8pSspKlun6M
+JnTZUrb8E6DdteW9ITEmtTH2jYIR0COgRYg0xFVbPjS1D2JH94H1pXeDGw8UgdGX
+H1QhrQqtBafqweWdPEoGLG7dhZqSCptuYzA/o+53oFo/B0EPmG0G5oAx1d0VJrVR
+6+bEfX5+3eWnAnTw138WAczXlVIvQoD+r7TJY+smD2RCYAJChI/3ZPiQkhj/WOMV
+ueELkapracgnsGxcT+UyqV1351sNkje7+DwKUsnP/vJZxfV0LGPIMx9ykYEwKpKh
+EvDjusV7Y1FOYqJDkl0veIG532L4ndwu5SZCBdcTzmSUlQLgoQKCAQEAxx29a5SU
+YupoLbEPw3gWoGFCbcDQ9EJ3N/s3g4T34QeBWzpwDhx2NZsNbLAn9LXYUTRAd8gr
++3Isu5WY71Kctk8Q134eBB0Gsnu51j1LUeorgUlKgQ3nCbADti0sKRv8jSaGyTXi
+r42qUOo9n+gQUdNVdKPNvy8gkcp2A1RfqxedZVZ5UcC0xVdoF661sFkLBGZXx6HP
+y8HK3qk0W3KkVMpbj3TH/URaA7bEYuY9J2StV4ywP8oM+MdRvR5067niI11kuQ6B
+JO+vQcbyWgnP5o0gWuh6WlwwXkXxazPRMwq3XuBbBxjs6VirSdrWHWW+oLacpFcD
+Y/eyRW5caN+fSQKCAQEAxc6ZCoxwFPV3lf82lMKaXgMMM/9Z1R0L6+yqUmqZafbC
+kxib68Z7a2jY1FgAy5903M/MOv+sL/OON7xWbc6+fsyn2XoOvQwdrdst2olEmUi1
+UuxVHTg1gNNP9MW0YAHJVernCYyQc0BI+aVKL55IpFBudiiPe8Y4qXBy5cwynJ7w
+TQACy4jv/DOdcib9JWEzE6RB2Q7ClbIaVztnYFOI4bPSabsiJnAQPKI18ka7k0CR
+0mVYUxIOJ2N84+XV1wZ2MeVfUzlwzHmx7+vPdkCiwdKrbtpjoyv4DGCxvIAudGLm
+0TcFVOK9oCil7qLLzbgFPyYBZX7gxfxSJvcgFMkwXQKCAQEAwLucuiFbcFOM+41D
+wOTgoeUCs9HLcGNVmq5kEb4HYZ4uK+vowv/xu/mViPsJ8eiCtjdpn4f2arEdc4Ve
+P2krn5vwpWXCECE4dlMkkqdJ3MRZ0A7tOvYGCG6DaTdYY4JfdxEvrlumTF9H2IKj
+m8C46zswoHJdmQ047WWXzalB4Q4+n/SQAf4R/GKaszG9VDEcZOnbVbTeuk+e2t/V
+eh8BycEF11omqpQavTWP6lsKHrNoxjG7+ELPQ49LI0/zxKhsp+aitC3B+8q1TWoQ
+8+5Detpn0xbsN8K8XsQ85pOFj250CDYKZlhOGaBmTFqynkn5tv6LqNdAxObhfCtS
+74BlYQKCAQByzuS44KY1I/vSzZxKX2Dla/NrQqxLK16+AlEhIMoGXLi2U7Q79qmv
+v90J8kIT7WsQtnMdU0QHWN+UrfWkKjkas4JAkb14ME4RmINWshFkvnSvuof0O6mi
+KgPgV9fHWYIYIg0S18kHe6pfa3ZRiRc0d5KFdilBd91vStsFUa2WhhGHP5hftg1E
+Xljl5odLaM0Se2XUq+J4rDTpqIrpt9Jc3dgkkf7SPHzQFH4nLrK0VufMLBJFtNcO
+OYpFZCLneNKlRzI6xb4YkBGc5Us2oXFV+gaSgqMOE/kWhhDjDaro1naNu9eWWzwg
+dzdH+Kk9r68r5c0tsaSYhUjRYOH37oXpAoIBAQDAqjYwd64AKFnppbt5eAJtdJtd
+BatS6DfNxYl6jmF0E9+ZUIALdmyxB/Y8Grng5kpd8VQHW5LDzX+ABrrD99+hSPDe
+quq6S6JSvXfrGa5EGMkC6zkPFvppjYj4u+VVqn0sRKPLAa3tmXLECJ+lORX4LVhe
+rG2/AmvG5YVqYBTgbBi+cYXToHXUp1D/qQLXN+8PvCvvWVCzfVYr25TsD+5UEbvc
+TQA9WtwHy/xjzLx11IrI9xlDBPzfGQVStnroP6MkMe3ACc0pg/17ktY5MiFnzLpK
+o58qMXWZMrYyDvlo9PBOYLFXL41yJMhZeJjP80Kk/L1EnDiTDHuU8qFltbD3
+-----END RSA PRIVATE KEY-----
diff --git a/tests/sys/net/if_ovpn/ccd/Makefile b/tests/sys/net/if_ovpn/ccd/Makefile
new file mode 100644
index 000000000000..2d3fefa1f321
--- /dev/null
+++ b/tests/sys/net/if_ovpn/ccd/Makefile
@@ -0,0 +1,8 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/net/if_ovpn/ccd
+
+${PACKAGE}FILES+= \
+ Test-Client2
+
+.include <bsd.test.mk>
diff --git a/tests/sys/net/if_ovpn/ccd/Test-Client2 b/tests/sys/net/if_ovpn/ccd/Test-Client2
new file mode 100644
index 000000000000..b378ad0d4394
--- /dev/null
+++ b/tests/sys/net/if_ovpn/ccd/Test-Client2
@@ -0,0 +1,2 @@
+iroute 203.0.113.0 255.255.255.0
+ifconfig-push 198.51.100.3 255.255.255.0
diff --git a/tests/sys/net/if_ovpn/client.crt b/tests/sys/net/if_ovpn/client.crt
new file mode 100644
index 000000000000..92ba8ad1ba4b
--- /dev/null
+++ b/tests/sys/net/if_ovpn/client.crt
@@ -0,0 +1,123 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1048686 (0x10006e)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=KG, ST=NA, L=BISHKEK, O=OpenVPN-TEST/emailAddress=me@myhost.mydomain
+ Validity
+ Not Before: Apr 27 15:00:37 2022 GMT
+ Not After : Apr 3 15:00:37 2122 GMT
+ Subject: C=KG, ST=NA, O=OpenVPN-TEST, CN=Test-Client/emailAddress=me@myhost.mydomain
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (4096 bit)
+ Modulus:
+ 00:f0:86:38:95:a6:e0:76:52:eb:25:57:8b:47:2a:
+ e1:17:db:9b:84:64:33:0c:70:91:74:ae:f5:39:cc:
+ a7:9e:37:3c:d2:32:df:cd:57:8e:21:00:c9:43:b6:
+ b3:d3:a8:fc:15:84:c4:15:86:d8:d6:69:5b:d4:9b:
+ 19:c9:8e:71:9f:1d:12:72:89:e3:7b:db:80:6e:dc:
+ 69:fd:98:eb:22:33:ac:59:4a:57:bd:6d:48:60:fd:
+ 89:8c:94:fd:64:24:cf:98:ea:31:c0:20:50:38:2e:
+ c6:f3:67:54:c1:ea:70:13:a4:34:fd:38:59:9c:64:
+ bf:11:f1:ed:01:46:08:31:c8:de:32:13:47:38:81:
+ 84:4d:f6:00:d3:8c:ee:6f:71:a1:5a:b1:34:60:95:
+ 25:67:7f:4c:d5:86:09:0b:dc:75:a1:e5:aa:05:74:
+ 0f:e8:f1:b1:2a:63:be:53:cb:d8:a3:9f:f1:1a:c6:
+ fb:c8:c5:ec:6c:34:86:13:c7:e4:83:d2:11:66:2f:
+ ee:8e:19:8e:e5:da:0c:59:09:b3:c6:35:aa:7e:88:
+ 15:eb:53:29:cd:f6:a5:c4:d2:af:72:28:b0:a8:f5:
+ a4:38:5b:ab:9f:e0:db:f1:b9:e4:ca:d0:e8:c7:dd:
+ 95:81:c9:75:e2:23:74:30:59:b0:ca:74:b1:fe:86:
+ 0d:7c:5a:f3:5d:bb:42:75:7d:48:51:d7:6a:ee:93:
+ d2:e4:30:2a:5c:65:56:f4:5e:74:97:e1:7e:ae:2c:
+ f7:da:95:12:e0:1a:dd:f5:07:c0:4b:85:90:45:d1:
+ b0:61:ec:90:ab:20:c3:55:78:6c:da:bb:48:4f:33:
+ 61:04:4f:8d:1a:e4:57:8a:cb:e1:ea:db:8f:f3:9f:
+ d4:98:5f:27:dd:20:9e:76:35:54:75:ab:ef:74:6b:
+ 77:93:02:e9:79:a4:0b:83:a4:ff:fd:3d:bd:a5:e3:
+ 96:b8:78:13:5a:91:7d:bd:a2:90:54:9d:07:87:fd:
+ 62:e2:d9:01:9c:50:8b:d4:7c:a4:28:f6:31:2b:9a:
+ f1:6f:6f:85:71:7e:71:b2:bc:6d:97:e7:fc:8c:5e:
+ 97:85:c1:6a:61:10:c1:e5:b4:db:52:db:20:e3:42:
+ 8f:fa:48:4c:27:87:0f:05:0d:6d:93:4e:2f:a1:36:
+ 58:16:73:9f:61:68:d5:cb:67:1b:5d:41:c2:e6:6f:
+ e6:ca:e1:f3:b6:92:c1:48:72:3f:a9:84:3b:1f:9b:
+ 3d:73:85:46:f2:f7:dc:5e:de:e9:18:47:24:f4:7d:
+ 46:e1:0e:2e:5a:4a:9a:4e:f1:e5:7c:71:d0:7b:9e:
+ 62:3d:43:a3:62:9e:55:0a:77:a6:98:31:b7:a1:11:
+ b3:58:35
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Comment:
+ OpenSSL Generated Certificate
+ X509v3 Subject Key Identifier:
+ FC:B5:C3:D3:A0:B5:6C:8F:3D:52:4F:07:03:08:0A:BD:9D:A8:94:3A
+ X509v3 Authority Key Identifier:
+ keyid:88:07:18:C1:FE:2E:09:2E:7D:E0:B9:1B:83:A9:54:DE:09:B1:16:7E
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 5d:26:45:db:9f:d6:c7:86:a0:22:85:f2:fd:4a:41:3b:3a:2f:
+ 81:1b:93:38:e7:0e:81:bf:bc:5f:47:95:94:92:a6:12:47:78:
+ 74:68:65:fd:e0:99:b2:08:d5:2b:fa:aa:05:13:88:7f:00:e8:
+ cb:17:b0:04:4d:d1:6e:80:4b:11:1b:71:45:b9:61:c2:66:14:
+ e6:86:d9:13:a0:7d:63:14:fe:41:7c:86:42:c8:53:0e:04:da:
+ 1b:28:cc:a4:e8:ff:f4:b0:73:4b:c0:a3:d7:be:7c:2c:2a:e5:
+ aa:3e:8b:ce:07:8b:b2:62:a4:7d:f0:b3:75:39:02:10:f0:a8:
+ b9:d8:1f:70:4d:d1:b0:68:46:43:02:bc:8b:15:e6:df:5d:c3:
+ ae:e3:89:80:48:64:35:9b:0b:2b:d6:75:38:96:0d:6c:f1:cb:
+ 03:91:ec:75:58:3b:fb:f7:78:cf:38:58:9b:a6:04:48:fc:aa:
+ c0:fa:a3:9c:da:c3:26:e0:82:9a:0e:0e:2b:2f:50:00:56:7f:
+ d5:ab:87:61:dd:bb:34:23:af:38:5f:ea:40:72:cf:46:38:31:
+ 8c:a3:68:1c:a1:84:62:03:05:7e:92:46:1b:0f:e2:a3:47:d3:
+ a2:c5:f9:e8:7b:d1:0a:20:63:d6:ca:01:05:7f:3f:4c:4f:d5:
+ 6c:51:e8:ee:82:35:37:9b:1e:e8:76:6d:05:50:88:43:cc:8c:
+ 20:81:09:a9:76:57:97:7b:bc:38:14:d5:3e:38:b1:a5:7e:51:
+ b2:67:9b:50:05:00:1b:24:90:cc:57:e1:b1:27:3e:50:09:0b:
+ bc:9c:0e:b3:d1:08:80:30:d6:28:85:6c:4d:9f:d2:ea:96:de:
+ 6f:0d:25:0c:03:94:65:4e:88:aa:d8:81:78:49:44:09:4d:85:
+ c8:db:8c:57:be:6d:49:97:2b:a5:28:97:e3:99:ea:f1:b7:46:
+ 2e:a6:dc:85:1c:d6:66:6e:dd:a9:db:d6:d3:34:71:95:0a:6d:
+ bb:47:b5:18:b5:7e:95:92:9b:53:f9:9b:a3:6c:09:2c:e2:d0:
+ d3:9a:4e:31:21:0b:18:b6:b4:fc:65:8a:a2:e5:b9:c8:f5:4a:
+ 92:3f:4e:de:db:e5:3a:bf:22:4e:39:b6:ae:09:d1:1b:84:f5:
+ e6:53:6d:c2:8e:24:26:58:58:80:aa:8d:dd:54:21:9e:7b:c1:
+ 01:f8:94:cc:a6:c0:32:9a:4a:0c:b4:f5:b8:e7:b0:5c:c2:18:
+ 57:1e:49:93:72:c9:01:91:b7:ea:a1:0b:fa:f0:33:0d:ff:55:
+ b6:fb:07:30:85:47:ab:cd:05:4e:cc:a2:49:91:0b:7d:b7:a4:
+ bb:43:ea:bb:f9:95:bb:e9
+-----BEGIN CERTIFICATE-----
+MIIFyjCCA7KgAwIBAgIDEABuMA0GCSqGSIb3DQEBCwUAMGYxCzAJBgNVBAYTAktH
+MQswCQYDVQQIDAJOQTEQMA4GA1UEBwwHQklTSEtFSzEVMBMGA1UECgwMT3BlblZQ
+Ti1URVNUMSEwHwYJKoZIhvcNAQkBFhJtZUBteWhvc3QubXlkb21haW4wIBcNMjIw
+NDI3MTUwMDM3WhgPMjEyMjA0MDMxNTAwMzdaMGoxCzAJBgNVBAYTAktHMQswCQYD
+VQQIDAJOQTEVMBMGA1UECgwMT3BlblZQTi1URVNUMRQwEgYDVQQDDAtUZXN0LUNs
+aWVudDEhMB8GCSqGSIb3DQEJARYSbWVAbXlob3N0Lm15ZG9tYWluMIICIjANBgkq
+hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA8IY4labgdlLrJVeLRyrhF9ubhGQzDHCR
+dK71Ocynnjc80jLfzVeOIQDJQ7az06j8FYTEFYbY1mlb1JsZyY5xnx0Sconje9uA
+btxp/ZjrIjOsWUpXvW1IYP2JjJT9ZCTPmOoxwCBQOC7G82dUwepwE6Q0/ThZnGS/
+EfHtAUYIMcjeMhNHOIGETfYA04zub3GhWrE0YJUlZ39M1YYJC9x1oeWqBXQP6PGx
+KmO+U8vYo5/xGsb7yMXsbDSGE8fkg9IRZi/ujhmO5doMWQmzxjWqfogV61Mpzfal
+xNKvciiwqPWkOFurn+Db8bnkytDox92Vgcl14iN0MFmwynSx/oYNfFrzXbtCdX1I
+Uddq7pPS5DAqXGVW9F50l+F+riz32pUS4Brd9QfAS4WQRdGwYeyQqyDDVXhs2rtI
+TzNhBE+NGuRXisvh6tuP85/UmF8n3SCedjVUdavvdGt3kwLpeaQLg6T//T29peOW
+uHgTWpF9vaKQVJ0Hh/1i4tkBnFCL1HykKPYxK5rxb2+FcX5xsrxtl+f8jF6XhcFq
+YRDB5bTbUtsg40KP+khMJ4cPBQ1tk04voTZYFnOfYWjVy2cbXUHC5m/myuHztpLB
+SHI/qYQ7H5s9c4VG8vfcXt7pGEck9H1G4Q4uWkqaTvHlfHHQe55iPUOjYp5VCnem
+mDG3oRGzWDUCAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3Bl
+blNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFPy1w9OgtWyPPVJP
+BwMICr2dqJQ6MB8GA1UdIwQYMBaAFIgHGMH+LgkufeC5G4OpVN4JsRZ+MA0GCSqG
+SIb3DQEBCwUAA4ICAQBdJkXbn9bHhqAihfL9SkE7Oi+BG5M45w6Bv7xfR5WUkqYS
+R3h0aGX94JmyCNUr+qoFE4h/AOjLF7AETdFugEsRG3FFuWHCZhTmhtkToH1jFP5B
+fIZCyFMOBNobKMyk6P/0sHNLwKPXvnwsKuWqPovOB4uyYqR98LN1OQIQ8Ki52B9w
+TdGwaEZDAryLFebfXcOu44mASGQ1mwsr1nU4lg1s8csDkex1WDv793jPOFibpgRI
+/KrA+qOc2sMm4IKaDg4rL1AAVn/Vq4dh3bs0I684X+pAcs9GODGMo2gcoYRiAwV+
+kkYbD+KjR9Oixfnoe9EKIGPWygEFfz9MT9VsUejugjU3mx7odm0FUIhDzIwggQmp
+dleXe7w4FNU+OLGlflGyZ5tQBQAbJJDMV+GxJz5QCQu8nA6z0QiAMNYohWxNn9Lq
+lt5vDSUMA5RlToiq2IF4SUQJTYXI24xXvm1JlyulKJfjmerxt0YuptyFHNZmbt2p
+29bTNHGVCm27R7UYtX6VkptT+ZujbAks4tDTmk4xIQsYtrT8ZYqi5bnI9UqSP07e
+2+U6vyJOObauCdEbhPXmU23CjiQmWFiAqo3dVCGee8EB+JTMpsAymkoMtPW457Bc
+whhXHkmTcskBkbfqoQv68DMN/1W2+wcwhUerzQVOzKJJkQt9t6S7Q+q7+ZW76Q==
+-----END CERTIFICATE-----
diff --git a/tests/sys/net/if_ovpn/client.key b/tests/sys/net/if_ovpn/client.key
new file mode 100644
index 000000000000..7ad255b52556
--- /dev/null
+++ b/tests/sys/net/if_ovpn/client.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEA8IY4labgdlLrJVeLRyrhF9ubhGQzDHCRdK71Ocynnjc80jLf
+zVeOIQDJQ7az06j8FYTEFYbY1mlb1JsZyY5xnx0Sconje9uAbtxp/ZjrIjOsWUpX
+vW1IYP2JjJT9ZCTPmOoxwCBQOC7G82dUwepwE6Q0/ThZnGS/EfHtAUYIMcjeMhNH
+OIGETfYA04zub3GhWrE0YJUlZ39M1YYJC9x1oeWqBXQP6PGxKmO+U8vYo5/xGsb7
+yMXsbDSGE8fkg9IRZi/ujhmO5doMWQmzxjWqfogV61MpzfalxNKvciiwqPWkOFur
+n+Db8bnkytDox92Vgcl14iN0MFmwynSx/oYNfFrzXbtCdX1IUddq7pPS5DAqXGVW
+9F50l+F+riz32pUS4Brd9QfAS4WQRdGwYeyQqyDDVXhs2rtITzNhBE+NGuRXisvh
+6tuP85/UmF8n3SCedjVUdavvdGt3kwLpeaQLg6T//T29peOWuHgTWpF9vaKQVJ0H
+h/1i4tkBnFCL1HykKPYxK5rxb2+FcX5xsrxtl+f8jF6XhcFqYRDB5bTbUtsg40KP
++khMJ4cPBQ1tk04voTZYFnOfYWjVy2cbXUHC5m/myuHztpLBSHI/qYQ7H5s9c4VG
+8vfcXt7pGEck9H1G4Q4uWkqaTvHlfHHQe55iPUOjYp5VCnemmDG3oRGzWDUCAwEA
+AQKCAgEArHhyhs0c03vt5d76nlOfCM6Om8aF3HuzsanrakDYSNlvIYMdfE82OXAo
+4gdWt4XLDVsgiBcj0cvG75MwUJl13BSqr7s0hhIF7Hjc/93xbZsEERsAA3MjnXjw
+cwA7Gt5ShmIYvp3tJ/xS6SLFYi/LoinzXUhU6ZJMeH+z5V/kbF6PBfVQ8rHcv1KR
+kSDTsNIYU8IRvtfz9F0SKWJthjXVm/vliPeKmQ0Gb1EKn2fitqHv77WTwoo6V/Tp
+17FUqTmvBEmGlBq7nxJWHFqasJy23viSTyZZKbmdcJ9q8z8+Pkm2Mjt5u7Evxgv4
+hX58DSVVGbXuc/PcUvddkC9RmyNg8tEd/HN8e3E0rtHnyCRU7E06zHJ06mxoKgst
+e1L4RXdAJFL0QzT4fpNfbTt4obAhOuq0GxpUoFdXSWOrCYvL9CRXTEtohs5aS6l2
+zG9/lQ0JpT8S6ASLP8v3v83Mw37ffjBLMzGsKUbZcCQCoUiezGuR5nyeWvYoaBGf
+9f68zYICzgJTZDDYV5VE170TjCq1eEIEi/X/HZr2l99BckOwhZp1jGrGCZcH/nqJ
+jERAGFDtXjcWVWNUGlqhIb4RZ1VoyHqGcu5RfprlQhOJn5IJUQE7bFWq6/Z9sJa5
+0pD7kMZwrOPAPskWs/zTjIJ1FFKAwW68tPxbCr2Rh/dIIhktd9kCggEBAPio1deG
+WlgZoV1tbpjSNKPZHArRG03j1aXHOKoMjC1P2jiUW9/gJ1OMV19HCJtrkdDbRlFl
+qkjoRNW/+B0cuHwiemVogVdObmt2QU2+xsccCM8iu1UDTN3Q6WhtV6ejFLa4/p9K
+3ZxLnj1zgTEw/ZZDXI9vaxINtDEa3cakrqtStIJSXyWaViB/+rBCYi+7H+9fMVsk
+N956SQR4mocdOP7LpHMKjYLb2SlUrajXV+5fg43nEucIQGRRJ/zddI3RtlcLYr4Y
+areyRZ9GwH4qrS2QJJMy8UQAcrr6JPT6w5vRob45B23uMNgrp8XOmdH+nXuHth0W
+M7z/zECRHe1J9BcCggEBAPef6A1C8AZFxpcWZ9jwZ7dsRdOzWf/65TtLWFqZVHGa
+JbD+ytHDVmOviRBhBM+nGnIRUdPYns1wCpFKeCPlmqgIhZVJH9Qyj7g03QVAZwCD
+6FLa0sewlvK5+wkC++6C7JhO4qhYNEk9W8P2ck12PlzeFEpGWSTiNLwwDEgahvmS
+0yghqJRugojnebySTMdNqaqDzo1U7l6YWhVdlTF0GFdG4Yab/Ikz1KIgaY5VpKtA
+b+mtguGH1Yh1n3x8Fw6FyRasSfIhJmsXZRe+RGx7vwoEnMjVbYBAVfjijkmDcbS5
+4O0zI5eZzBXfIMM6EwClig9OZEEYcx+llDWoX41PqZMCggEAb2PCl4+5/OlOXfnd
+p1vS9OsXIslVf+jmFiNOgO6qBMpWqS3ckkdploWxxh6d/nGLmpH/yArQ42QZId+j
+F/d7tTAEwFS2TBP4Zu9MhbVGen9WeuPGI2kdD+i8Bmmk8JWfe9MXTOhOqes98a1C
+XHTjxGJcnmx8/FNjOvQcERZIoLql3hNkSAYBOwHZnQe/0D31KlfsVjW9SU5iUzxr
+jMdMdudmvZomlk5B07/5Iz+ERmZHGlQ/JXuOzOGGFkJmKfmdwxR4oUty0uNrSNR6
++onHljeSCtaxOZMx0gyobY2//pdD62DEsTwYaV31BCluwqFajrHWpOUDPFEigHIB
+hACy9QKCAQB2JYiNU0O8amxPSDRyMHn77R//2xH07ZuTx+Y3C/NbZIXZRig1HzNH
+ysfl1bR68yrOA+972V4jfPK90b8yuWkqBS7fRI14LEugQzC1Qb4jY8xkQ93PwzSy
+SQQ6j37ulO8X2IOSeMsxqqHvBNYSmXk1zAv4SEpeK8OninFBsc52o5Q2EKEjePq1
+IWRXEaKqcSajodHaYwx8e8p3aTg26UJ32eze0ewS9nTcigRzEe/Iea0r3EqXGr1K
+J3zZ40cI+dIxDDEX4rM242mrg2+YJw7GU98Of66IQ6oBXu8uqhWFei6UXhL8UTgr
+s1MpcrsAUvtlRCzXVjgPgGwPke9NOBYbAoIBAALzEo+wH3NNIiqD1DfNTAGHuEC3
+SRkXVe2sQEGfXN8l5N5ujvhCUqhkVzni4t/7FqHDuGQW/FMoO9xpAfRzi2+09Ymg
+JLbMDIuhoOqnFD8Kn7yW4TKnAtaPIjxYARf6ODAJkFQL0t6r3psgZH4oMYAeBNCz
+DJES+ED5tj6q3nBPYR+4H9CAxBJd4Bmvpv5N9Sg1W7VByqSlM9HMxtbkWEV0WshL
+Zvm2PXBsCEPDp1SQcF9Vxuf9YMx3etVfTZ82gZXOwUF+MCBA0EjqtqMznT4gd7tH
+RQZRjuIyd4gRq6PcvW7hPvfE7FL9wC3CHCsD1JZ++TRChYe4HRbEY/Oz62k=
+-----END RSA PRIVATE KEY-----
diff --git a/tests/sys/net/if_ovpn/client2.crt b/tests/sys/net/if_ovpn/client2.crt
new file mode 100644
index 000000000000..83aec7eedaa0
--- /dev/null
+++ b/tests/sys/net/if_ovpn/client2.crt
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIFbTCCA1UCFC6I/36G1ZhmNxvabxL+BppMd38jMA0GCSqGSIb3DQEBCwUAMGYx
+CzAJBgNVBAYTAktHMQswCQYDVQQIDAJOQTEQMA4GA1UEBwwHQklTSEtFSzEVMBMG
+A1UECgwMT3BlblZQTi1URVNUMSEwHwYJKoZIhvcNAQkBFhJtZUBteWhvc3QubXlk
+b21haW4wIBcNMjIwNjE1MTIwNzQzWhgPMjEyMjA1MjIxMjA3NDNaMH4xCzAJBgNV
+BAYTAktHMQswCQYDVQQIDAJOQTEQMA4GA1UEBwwHQklTSEtFSzEVMBMGA1UECgwM
+T3BlblZQTi1URVNUMRUwEwYDVQQDDAxUZXN0LUNsaWVudDIxIjAgBgkqhkiG9w0B
+CQEWE21lMkBteWhvc3QubXlkb21haW4wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
+ggIKAoICAQDteW+ZsfahA+NJHgTycmGejCIw/jwbVpaFlwYLBe39OsDK44XUjVn1
+i8k4Vce9F1UcGeY9scyLZ797Ify5Sm59ejVkm2EriuA/jQeNpr8A0HxjcmEcn/G5
+5cM/zZYj7f9Bfj+XVgHG0zHVfD9PItwEUHKNp3hVr/86FwbnHKpcQK/QjYlDOFZB
+wiIxDUSpaMLT7eFUqLOem1ZmnBd0qT3GPjBJsbpzzK+LZd9V0brvIc8XCnoUGs2V
+wzsg8oRCpVpQsKUNrW3mid9lCJQvRAm6j0/14nZHm3sP5BroOTOzcLKiWuYMwizs
+QkkEYP0G9ZtipbIhAdnDB4FgjF+9arH3IXw3UZxXNPguA2UasuqcCwiwyp2aPNAf
+G0sIv3rvOGyTp0QfhrsQW0/xcJxfYlMONHft9kvuhC9ITKaH1ei8iQuFhm2QZCrO
+f/jEf8d6nckpM3GAp/WIze49HZgdVfAIGV3+DcF2u/gwBjKsRe9W4KN5GxLQEx0x
+gWLJN34O340N/Sy+NX82KP/kO/Zb3N1rKVmDIZx49ZJy1eN/Kt7pl0+AqifZzneu
+pLl9nziwe0csUtCQbIJHZQQon6vwDQVR3VuGwMra/sayxZDY5IOwueEm62/cJhoQ
+rxGknCM99WPhJau3S0gBV1nsH7M37AQxyHhC7q3ambdpEqzUDzf3XwIDAQABMA0G
+CSqGSIb3DQEBCwUAA4ICAQBtV12w72Yflc0bIJ3IsnQ1om820Fx8/0Ndr9GD8vov
+XXupazyuQmfRBpB0qcVR0tStxJrf8S19WRiLFM2UJexT4H8A3Rp788IESYo5JytV
+kAvTtJ+LE74EIRXt9M3II5vFaGiFRyozN7Vdr8mUJO5sXNJaZPQkOsAta652J2JV
+Qy5rOgAUEylUWZMVKkmSAdU4LGVgJC86XA9eQGtqtbXj09v3YW/EPsobCi0YbFYS
+5WgGCunqw7zT4Ko8KP+horaV/bQWZKnKIb3e5xDh9Zkm48RBRU4pYZ0VoOSp1xAy
+qzn/818NVPfhKWSXxLFBVWgsIzLO825vH5WEaQNgg+vfq2/AZcfl6UNGn5dufkAk
+73t5dNq46H2Z6t02dfOQ7U4tduCUPbWmPXD/kjFqryQ4GXNR8TMKLf6GZRKD5nOt
+KRfrkPL4tbsWL8WY9c5KQRC/vaLXETuuavDMVp0AFwTz846tB2njjyTc5jFcTgfY
+X8PgUw/miJszbQd6Z9HTDTTH0osv+VNXE5MCYPWe3QaobBJGRjaPJyO5OA/SXZa+
++9XCXyEBdVvckHpc4yHK9ATlCeiouDi45lzlnXpvuQz6VXwB8v4JKB/qqFlrzO2E
+09yAyw3qPH43TBbgvJwtpD+g6k9VvE7ojHS4fl2epyQAm/orT6RLLHMHEkaYqRCU
+2A==
+-----END CERTIFICATE-----
diff --git a/tests/sys/net/if_ovpn/client2.key b/tests/sys/net/if_ovpn/client2.key
new file mode 100644
index 000000000000..7e5c6857de1c
--- /dev/null
+++ b/tests/sys/net/if_ovpn/client2.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEA7XlvmbH2oQPjSR4E8nJhnowiMP48G1aWhZcGCwXt/TrAyuOF
+1I1Z9YvJOFXHvRdVHBnmPbHMi2e/eyH8uUpufXo1ZJthK4rgP40Hjaa/ANB8Y3Jh
+HJ/xueXDP82WI+3/QX4/l1YBxtMx1Xw/TyLcBFByjad4Va//OhcG5xyqXECv0I2J
+QzhWQcIiMQ1EqWjC0+3hVKiznptWZpwXdKk9xj4wSbG6c8yvi2XfVdG67yHPFwp6
+FBrNlcM7IPKEQqVaULClDa1t5onfZQiUL0QJuo9P9eJ2R5t7D+Qa6Dkzs3Cyolrm
+DMIs7EJJBGD9BvWbYqWyIQHZwweBYIxfvWqx9yF8N1GcVzT4LgNlGrLqnAsIsMqd
+mjzQHxtLCL967zhsk6dEH4a7EFtP8XCcX2JTDjR37fZL7oQvSEymh9XovIkLhYZt
+kGQqzn/4xH/Hep3JKTNxgKf1iM3uPR2YHVXwCBld/g3Bdrv4MAYyrEXvVuCjeRsS
+0BMdMYFiyTd+Dt+NDf0svjV/Nij/5Dv2W9zdaylZgyGcePWSctXjfyre6ZdPgKon
+2c53rqS5fZ84sHtHLFLQkGyCR2UEKJ+r8A0FUd1bhsDK2v7GssWQ2OSDsLnhJutv
+3CYaEK8RpJwjPfVj4SWrt0tIAVdZ7B+zN+wEMch4Qu6t2pm3aRKs1A83918CAwEA
+AQKCAgAGjSMXCmHTb1gF3F4mkiE/Tn5i+6CM4IamiNQR2cgHBGftMPmwM3YX4BNd
+CoDIJqyiadSAPzd1YRdXPkjKk9MYgxaV//NeUCZ/mlRrA/6g9x93XuBu+bqhdkU8
+rV9G/nncRK9cbXL/GTR2v0a/2CZZuB5w6f3X31MbNydpmNDaWq5/AmiXAibfCYwH
+7mXGhq1ZS2a7/yt1ZLOtgQDkpwadQXnzjoOmTi9JmTXgGDkf/77G0/MqOtMRHqGy
+9v3PGOC0+SqUhgRSJ9uR3fq4kxfxnaKHFghNUWzDs3dKkMlsWd+Tuw49q92xZuK8
+zDAu0PfIcOnJH1PynXJkR3scrqTaLuXQab2PeEZYZYABBsKuq+Vik9+MUUVjz8RT
+VveYoBFYGGLZrCUC5/RUKzOcBWhHxQnRiODm2zrhun0Sfs7HDeii3r4yNwB0Hibi
+rIbgMXnxSNp1bYRPp8rECgAEGGhQBJ90D7bZq1H4AU6dKYCnbgxYZopZN2/nsjZN
+HGANyJkeDTUVc6VhP6vMQo1B4jSC9n4wykmInfN/+3k8Yd/IPzRJY1WWmjSgzEyv
+s1dam+dSN5woq4bl7sbEVrlJaWv/8/Oa1/xypJl4DKLP8g4sTbsa6Ak3JW7BGXyi
+V2PfzPMVBq7k4BHAqRJjNTShQfqq/Gsstje+X1bs7pBoQMAGgQKCAQEA/pZffQgp
+Odg87PusKGvVbGsLfgEo1sJoM/b6+BZs3HgMSoWTl7k4ph+d9zFYG8NcUau3RLbV
+5v5IytKN5WQVzNhUjAxvCZLTu/6m06rtUs2qOCi6GZK5IZaY7Qxho25xAN2VZdEt
+bjae4qmaHl6t4anBuVqdMLhzPIQ6gQYXZNXFo3DxlPBCz/Chn6kkq8r2yMobmoov
+ny9ai4Exm8JVnwzFv3NWr/iQB232w05Fr0NIWnok/z31q+FFQ8izJsX8rv0+s1zv
+pS0kP9rs0GDBxfA034+vNPGM++i+o09igJmtqlV67fB4vHEq2BZm2EkgsPBqjIY+
+1MeNZvMH8/FBAwKCAQEA7srBPRQCHEigHkjKd9igTr/YGDQ0HVD1m2pE0SvuBHSB
+dB1n1AH6HqRqMhYuxxXCH72wpej06fjKo/rqqhub4H3XlEgTBmSQfDBe42WDDGEN
+T7XDKVNaa27i8s2ztUfCkumoNR6IbhcvQlCmhwZVW1NsNkk5bY/pA3Qs6vntMT5F
+MILJIChPhIWkQpmdNvaJeVE0fIw2J1yXTZwX4TZUrf2MhystD1BAdyNQe8QxstJQ
+3WG1GYFH25X8onQ1uCvhpe9xdJv9U1qY/D5V3gf63Dy/wsvm50LGf1/cVxkRthSu
+s2tBCtiQImgmJsk2FpK3vAnzX0Ik9gcKd/8P6ENrdQKCAQAOx/JBUyD5n8lhxPbo
+3eHlSo2/Qhf56A2evr8xejPV1Q55oSnBjFpyorFMMcw4yG3qu/qG/cqLf8YAKJte
+byIo44J9IxerSaALcSyEa48d2J0CZ7LuWytufMziLm7Yy0e6UiMjZzKpDHjLFifB
+jaOwz2dU+KLZukvOfqra5Nyk2RiBdcRA7nYiloj7uRlM9BrB66IQpec/6cLrCJQ1
+w+Guu1Ib3Hly/A54r/S8wCWhmFlyD1dojlNeKFUaK2PjY2lZS5DBXyr2vxk0r+RB
+8OwvLtQTCseUXlXeJlQzLR+98a44jn/1opmP704af6p28j/4pey5ve2V8wQNrxyO
+GDq7AoIBAEs+kpOXeW7GJ8ZDM6F+Hk2SQBqoYH+YYjw9yT+MMy0uNRiMp4nzsYf0
+UQ5FVSognhH4aPBurrYHUntHdqhxmLWtkb/E0lHiYHDxoQTQmPHOpy4l3UBpZoWR
+5GuUC/ukiBhZDkrmuyDNp3OjDEZh5YWojOGyQylV/pu7AOhuJqKst4qou42phh0B
+K5hc5WBLYVhcEUjpuaq/j2HCPPgXcal9yslQ/prjs9yWwSau1OY/RYHs5u8JgMYd
+xgS+z6qgETODduHCwZmBY9GgJtiW9SJu9hIAxFq8/OVoJHtBiAYzEDWzJ0SupwRg
+gx0XrDaCtujGzeyHYDQyVccoFTAgBn0CggEBAICbfBKaQyt9xTXazTIgDF+KED6u
+E0AVCnAUHT7qkMa0y+LlcOAuCoZrr8yIYU7VjRxUKIuYyUSQ5SRPhL9P2HBhPNFe
+yTVT5IC2Lrqh+UTiwacUA/USCUY4XmshXZS0eg8/ZEGpjHMa3gGEVhtVmM40zmLt
+XJWrYAahYNCjMW2lVLPSr/m6UDoo1lDO9Xi1Usls2de1cMA+jVAMEO0F+k8PmZ3a
+5/2fkGm1+gFevICOzvrzYVtLJaLGfUGVrxsPYC7t0T5o8AEduaGAcpwD/snTdJwg
+zLyEZJ/G0v0DOyadQoBSKTdcgrI4XgyUkktFGLAlTND2tkbQdtsdNC6LR1k=
+-----END RSA PRIVATE KEY-----
diff --git a/tests/sys/net/if_ovpn/dh.pem b/tests/sys/net/if_ovpn/dh.pem
new file mode 100644
index 000000000000..8eda59aa139e
--- /dev/null
+++ b/tests/sys/net/if_ovpn/dh.pem
@@ -0,0 +1,8 @@
+-----BEGIN DH PARAMETERS-----
+MIIBCAKCAQEArdnA32xujHPlPI+jPffHSoMUZ+b5gRz1H1Lw9//Gugm5TAsRiYrB
+t2BDSsMKvAjyqN+i5SJv4TOk98kRRKB27iPvyXmiL945VaDQl/UehCySjYlGFUjW
+9nuo+JwQxeSbw0TLiSYoYJZQ8X1CxPl9mgJl277O4cW1Gc8I/bWa+ipU/4K5wv3h
+GI8nt+6A0jN3M/KebotMP101G4k0l0qsY4oRMTmP+z3oAP0qU9NZ1jiuMFVzRlNp
+5FdYF7ctrH+tBF+QmyT4SRKSED4wE4oX6gp420NaBhIEQifIj75wlMDtxQlpkN+x
+QkjsEbPlaPKHGQ4uupssChVUi8IM2yq5EwIBAg==
+-----END DH PARAMETERS-----
diff --git a/tests/sys/net/if_ovpn/if_ovpn.sh b/tests/sys/net/if_ovpn/if_ovpn.sh
new file mode 100644
index 000000000000..c42344da1a3b
--- /dev/null
+++ b/tests/sys/net/if_ovpn/if_ovpn.sh
@@ -0,0 +1,1426 @@
+##
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2022 Rubicon Communications, LLC ("Netgate")
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+. $(atf_get_srcdir)/../../netpfil/pf/utils.subr
+
+atf_test_case "4in4" "cleanup"
+4in4_head()
+{
+ atf_set descr 'IPv4 in IPv4 tunnel'
+ atf_set require.user root
+ atf_set require.progs openvpn
+}
+
+4in4_body()
+{
+ ovpn_init
+
+ l=$(vnet_mkepair)
+
+ vnet_mkjail a ${l}a
+ jexec a ifconfig ${l}a 192.0.2.1/24 up
+ vnet_mkjail b ${l}b
+ jexec b ifconfig ${l}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2
+
+ ovpn_start a "
+ dev ovpn0
+ dev-type tun
+ proto udp4
+
+ cipher AES-256-GCM
+ auth SHA256
+
+ local 192.0.2.1
+ server 198.51.100.0 255.255.255.0
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/server.crt
+ key $(atf_get_srcdir)/server.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ mode server
+ script-security 2
+ auth-user-pass-verify /usr/bin/true via-env
+ topology subnet
+
+ keepalive 100 600
+ "
+ ovpn_start b "
+ dev tun0
+ dev-type tun
+
+ client
+
+ remote 192.0.2.1
+ auth-user-pass $(atf_get_srcdir)/user.pass
+
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/client.crt
+ key $(atf_get_srcdir)/client.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ keepalive 100 600
+ "
+
+ # Give the tunnel time to come up
+ sleep 10
+
+ atf_check -s exit:0 -o ignore jexec b ping -c 1 198.51.100.1
+
+ echo 'foo' | jexec b nc -u -w 2 192.0.2.1 1194
+ atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1
+
+ # Test routing loop protection
+ jexec b route add 192.0.2.1 198.51.100.1
+ atf_check -s exit:2 -o ignore jexec b ping -t 1 -c 1 198.51.100.1
+}
+
+4in4_cleanup()
+{
+ ovpn_cleanup
+}
+
+atf_test_case "bz283426" "cleanup"
+bz283426_head()
+{
+ atf_set descr 'FreeBSD Bugzilla 283426'
+ atf_set require.user root
+ atf_set require.progs openvpn python3
+}
+
+bz283426_body()
+{
+ ovpn_init
+
+ l=$(vnet_mkepair)
+
+ vnet_mkjail a ${l}a
+ jexec a ifconfig ${l}a 192.0.2.1/24 up
+ vnet_mkjail b ${l}b
+ jexec b ifconfig ${l}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2
+
+ ovpn_start a "
+ dev ovpn0
+ dev-type tun
+ proto udp4
+
+ cipher AES-256-GCM
+ auth SHA256
+
+ bind 0.0.0.0:1194
+ server 198.51.100.0 255.255.255.0
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/server.crt
+ key $(atf_get_srcdir)/server.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ mode server
+ script-security 2
+ auth-user-pass-verify /usr/bin/true via-env
+ topology subnet
+
+ keepalive 100 600
+ "
+ ovpn_start b "
+ dev tun0
+ dev-type tun
+
+ client
+
+ remote 192.0.2.1
+ auth-user-pass $(atf_get_srcdir)/user.pass
+
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/client.crt
+ key $(atf_get_srcdir)/client.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ keepalive 100 600
+ "
+
+ # Give the tunnel time to come up
+ sleep 10
+
+ atf_check -s exit:0 -o ignore jexec b ping -c 1 198.51.100.1
+
+ # Send a broadcast packet in the outer link.
+ echo "import socket as sk
+s = sk.socket(sk.AF_INET, sk.SOCK_DGRAM)
+s.setsockopt(sk.SOL_SOCKET, sk.SO_BROADCAST, 1)
+s.sendto(b'x' * 1000, ('192.0.2.255', 1194))" | jexec b python3
+
+ atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1
+}
+
+bz283426_cleanup()
+{
+ ovpn_cleanup
+}
+
+atf_test_case "4mapped" "cleanup"
+4mapped_head()
+{
+ atf_set descr 'IPv4 mapped addresses'
+ atf_set require.user root
+ atf_set require.progs openvpn
+}
+
+4mapped_body()
+{
+ ovpn_init
+
+ l=$(vnet_mkepair)
+
+ vnet_mkjail a ${l}a
+ jexec a ifconfig ${l}a 192.0.2.1/24 up
+ vnet_mkjail b ${l}b
+ jexec b ifconfig ${l}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2
+
+ #jexec a ifconfig ${l}a
+
+ ovpn_start a "
+ dev ovpn0
+ dev-type tun
+
+ cipher AES-256-GCM
+ auth SHA256
+
+ server 198.51.100.0 255.255.255.0
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/server.crt
+ key $(atf_get_srcdir)/server.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ mode server
+ script-security 2
+ auth-user-pass-verify /usr/bin/true via-env
+ topology subnet
+
+ keepalive 100 600
+ "
+ ovpn_start b "
+ dev tun0
+ dev-type tun
+
+ client
+
+ remote 192.0.2.1
+ auth-user-pass $(atf_get_srcdir)/user.pass
+
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/client.crt
+ key $(atf_get_srcdir)/client.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ keepalive 100 600
+ "
+
+ # Give the tunnel time to come up
+ sleep 10
+
+ atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1
+}
+
+4mapped_cleanup()
+{
+ ovpn_cleanup
+}
+
+atf_test_case "6in4" "cleanup"
+6in4_head()
+{
+ atf_set descr 'IPv6 in IPv4 tunnel'
+ atf_set require.user root
+ atf_set require.progs openvpn
+}
+
+6in4_body()
+{
+ ovpn_init
+
+ l=$(vnet_mkepair)
+
+ vnet_mkjail a ${l}a
+ jexec a ifconfig ${l}a 192.0.2.1/24 up
+ vnet_mkjail b ${l}b
+ jexec b ifconfig ${l}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2
+
+ ovpn_start a "
+ dev ovpn0
+ dev-type tun
+ proto udp
+
+ cipher AES-256-GCM
+ auth SHA256
+
+ local 192.0.2.1
+ server-ipv6 2001:db8:1::/64
+
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/server.crt
+ key $(atf_get_srcdir)/server.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ mode server
+ script-security 2
+ auth-user-pass-verify /usr/bin/true via-env
+ topology subnet
+
+ keepalive 100 600
+ "
+ ovpn_start b "
+ dev tun0
+ dev-type tun
+
+ client
+
+ remote 192.0.2.1
+ auth-user-pass $(atf_get_srcdir)/user.pass
+
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/client.crt
+ key $(atf_get_srcdir)/client.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ keepalive 100 600
+ "
+
+ # Give the tunnel time to come up
+ sleep 10
+
+ atf_check -s exit:0 -o ignore jexec b ping6 -c 3 2001:db8:1::1
+}
+
+6in4_cleanup()
+{
+ ovpn_cleanup
+}
+
+atf_test_case "4in6" "cleanup"
+4in6_head()
+{
+ atf_set descr 'IPv4 in IPv6 tunnel'
+ atf_set require.user root
+ atf_set require.progs openvpn
+}
+
+4in6_body()
+{
+ ovpn_init
+
+ l=$(vnet_mkepair)
+
+ vnet_mkjail a ${l}a
+ jexec a ifconfig ${l}a inet6 2001:db8::1/64 up no_dad
+ vnet_mkjail b ${l}b
+ jexec b ifconfig ${l}b inet6 2001:db8::2/64 up no_dad
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore jexec a ping6 -c 1 2001:db8::2
+
+ ovpn_start a "
+ dev ovpn0
+ dev-type tun
+ proto udp6
+
+ cipher AES-256-GCM
+ auth SHA256
+
+ local 2001:db8::1
+ server 198.51.100.0 255.255.255.0
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/server.crt
+ key $(atf_get_srcdir)/server.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ mode server
+ script-security 2
+ auth-user-pass-verify /usr/bin/true via-env
+ topology subnet
+
+ keepalive 100 600
+ "
+ ovpn_start b "
+ dev tun0
+ dev-type tun
+
+ client
+
+ remote 2001:db8::1
+ auth-user-pass $(atf_get_srcdir)/user.pass
+
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/client.crt
+ key $(atf_get_srcdir)/client.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ keepalive 100 600
+ "
+
+ dd if=/dev/random of=test.img bs=1024 count=1024
+ cat test.img | jexec a nc -N -l 1234 &
+
+ # Give the tunnel time to come up
+ sleep 10
+
+ atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1
+
+ # MTU sweep
+ for i in `seq 1000 1500`
+ do
+ atf_check -s exit:0 -o ignore jexec b \
+ ping -c 1 -s $i 198.51.100.1
+ done
+
+ rcvmd5=$(jexec b nc -N -w 3 198.51.100.1 1234 | md5)
+ md5=$(md5 test.img)
+
+ if [ $md5 != $rcvmd5 ];
+ then
+ atf_fail "Transmit corruption!"
+ fi
+}
+
+4in6_cleanup()
+{
+ ovpn_cleanup
+}
+
+atf_test_case "6in6" "cleanup"
+6in6_head()
+{
+ atf_set descr 'IPv6 in IPv6 tunnel'
+ atf_set require.user root
+ atf_set require.progs openvpn
+}
+
+6in6_body()
+{
+ ovpn_init
+
+ l=$(vnet_mkepair)
+
+ vnet_mkjail a ${l}a
+ jexec a ifconfig ${l}a inet6 2001:db8::1/64 up no_dad
+ vnet_mkjail b ${l}b
+ jexec b ifconfig ${l}b inet6 2001:db8::2/64 up no_dad
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore jexec a ping6 -c 1 2001:db8::2
+
+ ovpn_start a "
+ dev ovpn0
+ dev-type tun
+ proto udp6
+
+ cipher AES-256-GCM
+ auth SHA256
+
+ local 2001:db8::1
+ server-ipv6 2001:db8:1::/64
+
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/server.crt
+ key $(atf_get_srcdir)/server.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ mode server
+ script-security 2
+ auth-user-pass-verify /usr/bin/true via-env
+ topology subnet
+
+ keepalive 100 600
+ "
+ ovpn_start b "
+ dev tun0
+ dev-type tun
+
+ client
+
+ remote 2001:db8::1
+ auth-user-pass $(atf_get_srcdir)/user.pass
+
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/client.crt
+ key $(atf_get_srcdir)/client.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ keepalive 100 600
+ "
+
+ # Give the tunnel time to come up
+ sleep 10
+
+ atf_check -s exit:0 -o ignore jexec b ping6 -c 3 2001:db8:1::1
+ atf_check -s exit:0 -o ignore jexec b ping6 -c 3 -z 16 2001:db8:1::1
+
+ # Test routing loop protection
+ jexec b route add -6 2001:db8::1 2001:db8:1::1
+ atf_check -s exit:2 -o ignore jexec b ping6 -t 1 -c 3 2001:db8:1::1
+}
+
+6in6_cleanup()
+{
+ ovpn_cleanup
+}
+
+atf_test_case "timeout_client" "cleanup"
+timeout_client_head()
+{
+ atf_set descr 'IPv4 in IPv4 tunnel'
+ atf_set require.user root
+ atf_set require.progs openvpn
+}
+
+timeout_client_body()
+{
+ ovpn_init
+
+ l=$(vnet_mkepair)
+
+ vnet_mkjail a ${l}a
+ jexec a ifconfig ${l}a 192.0.2.1/24 up
+ jexec a ifconfig lo0 127.0.0.1/8 up
+ vnet_mkjail b ${l}b
+ jexec b ifconfig ${l}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2
+
+ ovpn_start a "
+ dev ovpn0
+ dev-type tun
+ proto udp4
+
+ cipher AES-256-GCM
+ auth SHA256
+
+ local 192.0.2.1
+ server 198.51.100.0 255.255.255.0
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/server.crt
+ key $(atf_get_srcdir)/server.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ mode server
+ script-security 2
+ auth-user-pass-verify /usr/bin/true via-env
+ topology subnet
+
+ keepalive 2 10
+
+ management 192.0.2.1 1234
+ "
+ ovpn_start b "
+ dev tun0
+ dev-type tun
+
+ client
+
+ remote 192.0.2.1
+ auth-user-pass $(atf_get_srcdir)/user.pass
+
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/client.crt
+ key $(atf_get_srcdir)/client.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ keepalive 2 10
+ "
+
+ # Give the tunnel time to come up
+ sleep 10
+
+ atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1
+
+ # Kill the client
+ jexec b killall openvpn
+
+ # Now wait for the server to notice
+ sleep 15
+
+ while echo "status" | jexec a nc -N 192.0.2.1 1234 | grep 192.0.2.2; do
+ echo "Client disconnect not discovered"
+ sleep 1
+ done
+}
+
+timeout_client_cleanup()
+{
+ ovpn_cleanup
+}
+
+atf_test_case "explicit_exit" "cleanup"
+explicit_exit_head()
+{
+ atf_set descr 'Test explicit exit notification'
+ atf_set require.user root
+ atf_set require.progs openvpn
+}
+
+explicit_exit_body()
+{
+ ovpn_init
+
+ l=$(vnet_mkepair)
+
+ vnet_mkjail a ${l}a
+ jexec a ifconfig ${l}a 192.0.2.1/24 up
+ jexec a ifconfig lo0 127.0.0.1/8 up
+ vnet_mkjail b ${l}b
+ jexec b ifconfig ${l}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2
+
+ ovpn_start a "
+ dev ovpn0
+ dev-type tun
+ proto udp4
+
+ cipher AES-256-GCM
+ auth SHA256
+
+ local 192.0.2.1
+ server 198.51.100.0 255.255.255.0
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/server.crt
+ key $(atf_get_srcdir)/server.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ mode server
+ script-security 2
+ auth-user-pass-verify /usr/bin/true via-env
+ topology subnet
+
+ management 192.0.2.1 1234
+ "
+ ovpn_start b "
+ dev tun0
+ dev-type tun
+
+ client
+
+ remote 192.0.2.1
+ auth-user-pass $(atf_get_srcdir)/user.pass
+
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/client.crt
+ key $(atf_get_srcdir)/client.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ explicit-exit-notify
+ "
+
+ # Give the tunnel time to come up
+ sleep 10
+
+ atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1
+
+ if ! echo "status" | jexec a nc -N 192.0.2.1 1234 | grep 192.0.2.2; then
+ atf_fail "Client not found in status list!"
+ fi
+
+ # Kill the client
+ jexec b killall openvpn
+
+ while echo "status" | jexec a nc -N 192.0.2.1 1234 | grep 192.0.2.2; do
+ jexec a ps auxf
+ echo "Client disconnect not discovered"
+ sleep 1
+ done
+}
+
+explicit_exit_cleanup()
+{
+ ovpn_cleanup
+}
+
+atf_test_case "multi_client" "cleanup"
+multi_client_head()
+{
+ atf_set descr 'Multiple simultaneous clients'
+ atf_set require.user root
+ atf_set require.progs openvpn
+}
+
+multi_client_body()
+{
+ ovpn_init
+ vnet_init_bridge
+
+ bridge=$(vnet_mkbridge)
+ srv=$(vnet_mkepair)
+ one=$(vnet_mkepair)
+ two=$(vnet_mkepair)
+
+ ifconfig ${bridge} up
+
+ ifconfig ${srv}a up
+ ifconfig ${bridge} addm ${srv}a
+ ifconfig ${one}a up
+ ifconfig ${bridge} addm ${one}a
+ ifconfig ${two}a up
+ ifconfig ${bridge} addm ${two}a
+
+ vnet_mkjail srv ${srv}b
+ jexec srv ifconfig ${srv}b 192.0.2.1/24 up
+ vnet_mkjail one ${one}b
+ jexec one ifconfig ${one}b 192.0.2.2/24 up
+ vnet_mkjail two ${two}b
+ jexec two ifconfig ${two}b 192.0.2.3/24 up
+ jexec two ifconfig lo0 127.0.0.1/8 up
+ jexec two ifconfig lo0 inet alias 203.0.113.1/24
+
+ # Sanity checks
+ atf_check -s exit:0 -o ignore jexec one ping -c 1 192.0.2.1
+ atf_check -s exit:0 -o ignore jexec two ping -c 1 192.0.2.1
+
+ jexec srv sysctl net.inet.ip.forwarding=1
+
+ ovpn_start srv "
+ dev ovpn0
+ dev-type tun
+ proto udp4
+
+ cipher AES-256-GCM
+ auth SHA256
+
+ local 192.0.2.1
+ server 198.51.100.0 255.255.255.0
+
+ push \"route 203.0.113.0 255.255.255.0 198.51.100.1\"
+
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/server.crt
+ key $(atf_get_srcdir)/server.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ mode server
+ duplicate-cn
+ script-security 2
+ auth-user-pass-verify /usr/bin/true via-env
+ topology subnet
+
+ keepalive 100 600
+
+ client-config-dir $(atf_get_srcdir)/ccd
+ "
+ ovpn_start one "
+ dev tun0
+ dev-type tun
+
+ client
+
+ remote 192.0.2.1
+ auth-user-pass $(atf_get_srcdir)/user.pass
+
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/client.crt
+ key $(atf_get_srcdir)/client.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ keepalive 100 600
+ "
+ ovpn_start two "
+ dev tun0
+ dev-type tun
+
+ client
+
+ remote 192.0.2.1
+ auth-user-pass $(atf_get_srcdir)/user.pass
+
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/client2.crt
+ key $(atf_get_srcdir)/client2.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ keepalive 100 600
+ "
+
+ # Give the tunnel time to come up
+ sleep 10
+
+ atf_check -s exit:0 -o ignore jexec one ping -c 3 198.51.100.1
+ atf_check -s exit:0 -o ignore jexec two ping -c 3 198.51.100.1
+
+ # Client-to-client communication
+ atf_check -s exit:0 -o ignore jexec one ping -c 3 198.51.100.3
+ atf_check -s exit:0 -o ignore jexec two ping -c 3 198.51.100.2
+
+ # iroute test
+ atf_check -s exit:0 -o ignore jexec one ping -c 3 203.0.113.1
+}
+
+multi_client_cleanup()
+{
+ ovpn_cleanup
+}
+
+atf_test_case "route_to" "cleanup"
+route_to_head()
+{
+ atf_set descr "Test pf's route-to with OpenVPN tunnels"
+ atf_set require.user root
+ atf_set require.progs openvpn
+}
+
+route_to_body()
+{
+ pft_init
+ ovpn_init
+
+ l=$(vnet_mkepair)
+ n=$(vnet_mkepair)
+
+ vnet_mkjail a ${l}a
+ jexec a ifconfig ${l}a 192.0.2.1/24 up
+ vnet_mkjail b ${l}b ${n}a
+ jexec b ifconfig ${l}b 192.0.2.2/24 up
+ jexec b ifconfig ${n}a up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2
+
+ ovpn_start a "
+ dev ovpn0
+ dev-type tun
+ proto udp4
+
+ cipher AES-256-GCM
+ auth SHA256
+
+ local 192.0.2.1
+ server 198.51.100.0 255.255.255.0
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/server.crt
+ key $(atf_get_srcdir)/server.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ mode server
+ script-security 2
+ auth-user-pass-verify /usr/bin/true via-env
+ topology subnet
+
+ keepalive 100 600
+ "
+ ovpn_start b "
+ dev tun0
+ dev-type tun
+
+ client
+
+ remote 192.0.2.1
+ auth-user-pass $(atf_get_srcdir)/user.pass
+
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/client.crt
+ key $(atf_get_srcdir)/client.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ keepalive 100 600
+ "
+
+ # Give the tunnel time to come up
+ sleep 10
+ jexec a ifconfig ovpn0 inet alias 198.51.100.254/24
+
+ # Check the tunnel
+ atf_check -s exit:0 -o ignore jexec b ping -c 1 -S 198.51.100.2 198.51.100.1
+ atf_check -s exit:0 -o ignore jexec b ping -c 1 -S 198.51.100.2 198.51.100.254
+
+ # Break our route to .254 so that we need a route-to to make things work.
+ jexec b ifconfig ${n}a 203.0.113.1/24 up
+ jexec b route add 198.51.100.254 -interface ${n}a
+
+ # Make sure it's broken.
+ atf_check -s exit:2 -o ignore jexec b ping -c 1 -S 198.51.100.2 198.51.100.254
+
+ jexec b pfctl -e
+ pft_set_rules b \
+ "pass out route-to (tun0 198.51.100.1) proto icmp from 198.51.100.2 "
+ atf_check -s exit:0 -o ignore jexec b ping -c 3 -S 198.51.100.2 198.51.100.254
+}
+
+route_to_cleanup()
+{
+ ovpn_cleanup
+ pft_cleanup
+}
+
+atf_test_case "ra" "cleanup"
+ra_head()
+{
+ atf_set descr 'Remote access with multiple clients'
+ atf_set require.user root
+ atf_set require.progs openvpn
+}
+
+ra_body()
+{
+ ovpn_init
+ vnet_init_bridge
+
+ bridge=$(vnet_mkbridge)
+ srv=$(vnet_mkepair)
+ lan=$(vnet_mkepair)
+ one=$(vnet_mkepair)
+ two=$(vnet_mkepair)
+
+ ifconfig ${bridge} up
+
+ ifconfig ${srv}a up
+ ifconfig ${bridge} addm ${srv}a
+ ifconfig ${one}a up
+ ifconfig ${bridge} addm ${one}a
+ ifconfig ${two}a up
+ ifconfig ${bridge} addm ${two}a
+
+ vnet_mkjail srv ${srv}b ${lan}a
+ jexec srv ifconfig lo0 inet 127.0.0.1/8 up
+ jexec srv ifconfig ${srv}b 192.0.2.1/24 up
+ jexec srv ifconfig ${lan}a 203.0.113.1/24 up
+ vnet_mkjail lan ${lan}b
+ jexec lan ifconfig lo0 inet 127.0.0.1/8 up
+ jexec lan ifconfig ${lan}b 203.0.113.2/24 up
+ jexec lan route add default 203.0.113.1
+ vnet_mkjail one ${one}b
+ jexec one ifconfig lo0 inet 127.0.0.1/8 up
+ jexec one ifconfig ${one}b 192.0.2.2/24 up
+ vnet_mkjail two ${two}b
+ jexec two ifconfig lo0 inet 127.0.0.1/8 up
+ jexec two ifconfig ${two}b 192.0.2.3/24 up
+
+ # Sanity checks
+ atf_check -s exit:0 -o ignore jexec one ping -c 1 192.0.2.1
+ atf_check -s exit:0 -o ignore jexec two ping -c 1 192.0.2.1
+ atf_check -s exit:0 -o ignore jexec srv ping -c 1 203.0.113.2
+
+ jexec srv sysctl net.inet.ip.forwarding=1
+
+ ovpn_start srv "
+ dev ovpn0
+ dev-type tun
+ proto udp4
+
+ cipher AES-256-GCM
+ auth SHA256
+
+ local 192.0.2.1
+ server 198.51.100.0 255.255.255.0
+
+ push \"route 203.0.113.0 255.255.255.0\"
+
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/server.crt
+ key $(atf_get_srcdir)/server.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ mode server
+ duplicate-cn
+ script-security 2
+ auth-user-pass-verify /usr/bin/true via-env
+ topology subnet
+
+ keepalive 100 600
+ "
+ ovpn_start one "
+ dev tun0
+ dev-type tun
+
+ client
+
+ remote 192.0.2.1
+ auth-user-pass $(atf_get_srcdir)/user.pass
+
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/client.crt
+ key $(atf_get_srcdir)/client.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ keepalive 100 600
+ "
+ sleep 2
+ ovpn_start two "
+ dev tun0
+ dev-type tun
+
+ client
+
+ remote 192.0.2.1
+ auth-user-pass $(atf_get_srcdir)/user.pass
+
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/client2.crt
+ key $(atf_get_srcdir)/client2.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ keepalive 100 600
+ "
+
+ # Give the tunnel time to come up
+ sleep 10
+
+ atf_check -s exit:0 -o ignore jexec one ping -c 1 198.51.100.1
+ atf_check -s exit:0 -o ignore jexec two ping -c 1 198.51.100.1
+
+ # Client-to-client communication
+ atf_check -s exit:0 -o ignore jexec one ping -c 1 198.51.100.3
+ atf_check -s exit:0 -o ignore jexec one ping -c 1 198.51.100.2
+ atf_check -s exit:0 -o ignore jexec two ping -c 1 198.51.100.2
+ atf_check -s exit:0 -o ignore jexec two ping -c 1 198.51.100.3
+
+ # RA test
+ atf_check -s exit:0 -o ignore jexec one ping -c 1 203.0.113.1
+ atf_check -s exit:0 -o ignore jexec two ping -c 1 203.0.113.1
+
+ atf_check -s exit:0 -o ignore jexec srv ping -c 1 -S 203.0.113.1 198.51.100.2
+ atf_check -s exit:0 -o ignore jexec srv ping -c 1 -S 203.0.113.1 198.51.100.3
+
+ atf_check -s exit:0 -o ignore jexec one ping -c 1 203.0.113.2
+ atf_check -s exit:0 -o ignore jexec two ping -c 1 203.0.113.2
+
+ atf_check -s exit:0 -o ignore jexec lan ping -c 1 198.51.100.1
+ atf_check -s exit:0 -o ignore jexec lan ping -c 1 198.51.100.2
+ atf_check -s exit:0 -o ignore jexec lan ping -c 1 198.51.100.3
+ atf_check -s exit:2 -o ignore jexec lan ping -c 1 198.51.100.4
+}
+
+ra_cleanup()
+{
+ ovpn_cleanup
+}
+
+ovpn_algo_body()
+{
+ algo=$1
+
+ ovpn_init
+
+ l=$(vnet_mkepair)
+
+ vnet_mkjail a ${l}a
+ jexec a ifconfig ${l}a 192.0.2.1/24 up
+ vnet_mkjail b ${l}b
+ jexec b ifconfig ${l}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2
+
+ ovpn_start a "
+ dev ovpn0
+ dev-type tun
+ proto udp4
+
+ cipher ${algo}
+ data-ciphers ${algo}
+ auth SHA256
+
+ local 192.0.2.1
+ server 198.51.100.0 255.255.255.0
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/server.crt
+ key $(atf_get_srcdir)/server.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ mode server
+ script-security 2
+ auth-user-pass-verify /usr/bin/true via-env
+ topology subnet
+
+ keepalive 100 600
+ "
+ ovpn_start b "
+ dev tun0
+ dev-type tun
+
+ client
+
+ cipher ${algo}
+ data-ciphers ${algo}
+
+ remote 192.0.2.1
+ auth-user-pass $(atf_get_srcdir)/user.pass
+
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/client.crt
+ key $(atf_get_srcdir)/client.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ keepalive 100 600
+ "
+
+ # Give the tunnel time to come up
+ sleep 10
+
+ atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1
+}
+
+atf_test_case "chacha" "cleanup"
+chacha_head()
+{
+ atf_set descr 'Test DCO with the chacha algorithm'
+ atf_set require.user root
+ atf_set require.progs openvpn
+}
+
+chacha_body()
+{
+ ovpn_algo_body CHACHA20-POLY1305
+}
+
+chacha_cleanup()
+{
+ ovpn_cleanup
+}
+
+atf_test_case "gcm_128" "cleanup"
+gcm_128_head()
+{
+ atf_set descr 'Test DCO with AES-128-GCM'
+ atf_set require.user root
+ atf_set require.progs openvpn
+}
+
+gcm_128_body()
+{
+ ovpn_algo_body AES-128-GCM
+}
+
+gcm_128_cleanup()
+{
+ ovpn_cleanup
+}
+
+atf_test_case "destroy_unused" "cleanup"
+destroy_unused_head()
+{
+ atf_set descr 'Destroy an if_ovpn interface before it is used'
+ atf_set require.user root
+}
+
+destroy_unused_body()
+{
+ ovpn_init
+
+ intf=$(ifconfig ovpn create)
+ atf_check -s exit:0 \
+ ifconfig ${intf} destroy
+}
+
+destroy_unused_cleanup()
+{
+ ovpn_cleanup
+}
+
+atf_test_case "multihome4" "cleanup"
+multihome4_head()
+{
+ atf_set descr 'Test multihome IPv4 with OpenVPN'
+ atf_set require.user root
+ atf_set require.progs openvpn
+}
+
+multihome4_body()
+{
+ pft_init
+ ovpn_init
+
+ l=$(vnet_mkepair)
+
+ vnet_mkjail a ${l}a
+ atf_check jexec a ifconfig ${l}a inet 192.0.2.1/24
+ atf_check jexec a ifconfig ${l}a alias 192.0.2.2/24
+ vnet_mkjail b ${l}b
+ atf_check jexec b ifconfig ${l}b inet 192.0.2.3/24
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore jexec b ping -c 1 192.0.2.1
+ atf_check -s exit:0 -o ignore jexec b ping -c 1 192.0.2.2
+
+ ovpn_start a "
+ dev ovpn0
+ dev-type tun
+ proto udp4
+
+ cipher AES-256-GCM
+ auth SHA256
+
+ multihome
+ server 198.51.100.0 255.255.255.0
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/server.crt
+ key $(atf_get_srcdir)/server.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ mode server
+ script-security 2
+ auth-user-pass-verify /usr/bin/true via-env
+ topology subnet
+
+ keepalive 100 600
+ "
+ ovpn_start b "
+ dev tun0
+ dev-type tun
+
+ client
+
+ remote 192.0.2.2
+ auth-user-pass $(atf_get_srcdir)/user.pass
+
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/client.crt
+ key $(atf_get_srcdir)/client.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ keepalive 100 600
+ "
+
+ # Block packets from the primary address, openvpn should only use the
+ # configured remote address.
+ jexec b pfctl -e
+ pft_set_rules b \
+ "block in quick from 192.0.2.1 to any" \
+ "pass all"
+
+ # Give the tunnel time to come up
+ sleep 10
+
+ atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1
+}
+
+multihome4_cleanup()
+{
+ ovpn_cleanup
+ pft_cleanup
+}
+
+multihome6_head()
+{
+ atf_set descr 'Test multihome IPv6 with OpenVPN'
+ atf_set require.user root
+ atf_set require.progs openvpn
+}
+
+multihome6_body()
+{
+ ovpn_init
+
+ l=$(vnet_mkepair)
+
+ vnet_mkjail a ${l}a
+ atf_check jexec a ifconfig ${l}a inet6 2001:db8::1/64 no_dad
+ atf_check jexec a ifconfig ${l}a inet6 alias 2001:db8::2/64 no_dad
+ vnet_mkjail b ${l}b
+ atf_check jexec b ifconfig ${l}b inet6 2001:db8::3/64 no_dad
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore jexec b ping6 -c 1 2001:db8::1
+ atf_check -s exit:0 -o ignore jexec b ping6 -c 1 2001:db8::2
+
+ ovpn_start a "
+ dev ovpn0
+ dev-type tun
+ proto udp6
+
+ cipher AES-256-GCM
+ auth SHA256
+
+ multihome
+ server-ipv6 2001:db8:1::/64
+
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/server.crt
+ key $(atf_get_srcdir)/server.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ mode server
+ script-security 2
+ auth-user-pass-verify /usr/bin/true via-env
+ topology subnet
+
+ keepalive 100 600
+ "
+ ovpn_start b "
+ dev tun0
+ dev-type tun
+
+ client
+
+ remote 2001:db8::2
+ auth-user-pass $(atf_get_srcdir)/user.pass
+
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/client.crt
+ key $(atf_get_srcdir)/client.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ keepalive 100 600
+ "
+
+ # Block packets from the primary address, openvpn should only use the
+ # configured remote address.
+ jexec b pfctl -e
+ pft_set_rules b \
+ "block in quick from 2001:db8::1 to any" \
+ "pass all"
+
+ # Give the tunnel time to come up
+ sleep 10
+
+ atf_check -s exit:0 -o ignore jexec b ping6 -c 3 2001:db8:1::1
+ atf_check -s exit:0 -o ignore jexec b ping6 -c 3 -z 16 2001:db8:1::1
+}
+
+multihome6_cleanup()
+{
+ ovpn_cleanup
+}
+
+atf_test_case "float" "cleanup"
+float_head()
+{
+ atf_set descr 'Test peer float notification'
+ atf_set require.user root
+}
+
+float_body()
+{
+ ovpn_init
+
+ l=$(vnet_mkepair)
+
+ vnet_mkjail a ${l}a
+ jexec a ifconfig ${l}a 192.0.2.1/24 up
+ jexec a ifconfig lo0 127.0.0.1/8 up
+ vnet_mkjail b ${l}b
+ jexec b ifconfig ${l}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2
+
+ ovpn_start a "
+ dev ovpn0
+ dev-type tun
+ proto udp4
+
+ cipher AES-256-GCM
+ auth SHA256
+
+ local 192.0.2.1
+ server 198.51.100.0 255.255.255.0
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/server.crt
+ key $(atf_get_srcdir)/server.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ mode server
+ script-security 2
+ auth-user-pass-verify /usr/bin/true via-env
+ topology subnet
+
+ keepalive 2 10
+
+ management 192.0.2.1 1234
+ "
+ ovpn_start b "
+ dev tun0
+ dev-type tun
+
+ client
+
+ remote 192.0.2.1
+ auth-user-pass $(atf_get_srcdir)/user.pass
+
+ ca $(atf_get_srcdir)/ca.crt
+ cert $(atf_get_srcdir)/client.crt
+ key $(atf_get_srcdir)/client.key
+ dh $(atf_get_srcdir)/dh.pem
+
+ keepalive 2 10
+ "
+
+ # Give the tunnel time to come up
+ sleep 10
+
+ atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1
+
+ # We expect the client on 192.0.2.2
+ if ! echo "status" | jexec a nc -N 192.0.2.1 1234 | grep 192.0.2.2; then
+ atf_fail "Client not found in status list!"
+ fi
+
+ # Now change the client IP
+ jexec b ifconfig ${l}b 192.0.2.3/24 up
+
+ # And wait for keepalives to trigger the float notification
+ sleep 5
+
+ # So the client now has the new address in userspace
+ if ! echo "status" | jexec a nc -N 192.0.2.1 1234 | grep 192.0.2.3; then
+ atf_fail "Client not found in status list!"
+ fi
+}
+
+float_cleanup()
+{
+ ovpn_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "4in4"
+ atf_add_test_case "bz283426"
+ atf_add_test_case "4mapped"
+ atf_add_test_case "6in4"
+ atf_add_test_case "6in6"
+ atf_add_test_case "4in6"
+ atf_add_test_case "timeout_client"
+ atf_add_test_case "explicit_exit"
+ atf_add_test_case "multi_client"
+ atf_add_test_case "route_to"
+ atf_add_test_case "ra"
+ atf_add_test_case "chacha"
+ atf_add_test_case "gcm_128"
+ atf_add_test_case "destroy_unused"
+ atf_add_test_case "multihome4"
+ atf_add_test_case "multihome6"
+ atf_add_test_case "float"
+}
diff --git a/tests/sys/net/if_ovpn/if_ovpn_c.c b/tests/sys/net/if_ovpn/if_ovpn_c.c
new file mode 100644
index 000000000000..fa8a9a07fa35
--- /dev/null
+++ b/tests/sys/net/if_ovpn/if_ovpn_c.c
@@ -0,0 +1,135 @@
+//#include <sys/param.h>
+#include <stdio.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/linker.h>
+#include <sys/ioctl.h>
+#include <sys/nv.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+
+#include <atf-c.h>
+
+#define OVPN_NEW_PEER _IO ('D', 1)
+
+static nvlist_t *
+fake_sockaddr(void)
+{
+ uint32_t addr = htonl(INADDR_LOOPBACK);
+ nvlist_t *nvl;
+
+ nvl = nvlist_create(0);
+
+ nvlist_add_number(nvl, "af", AF_INET);
+ nvlist_add_binary(nvl, "address", &addr, 4);
+ nvlist_add_number(nvl, "port", 1024);
+
+ return (nvl);
+}
+
+static char ovpn_ifname[IFNAMSIZ];
+static int ovpn_fd;
+
+static int
+create_interface(int fd)
+{
+ int ret;
+ struct ifreq ifr;
+
+ bzero(&ifr, sizeof(ifr));
+
+ /* Create ovpnx first, then rename it. */
+ snprintf(ifr.ifr_name, IFNAMSIZ, "ovpn");
+ ret = ioctl(fd, SIOCIFCREATE2, &ifr);
+ if (ret)
+ return (ret);
+
+ snprintf(ovpn_ifname, IFNAMSIZ, "%s", ifr.ifr_name);
+ printf("Created %s\n", ovpn_ifname);
+
+ return (0);
+}
+
+static void
+destroy_interface(int fd)
+{
+ int ret;
+ struct ifreq ifr;
+
+ if (ovpn_ifname[0] == 0)
+ return;
+
+ printf("Destroy %s\n", ovpn_ifname);
+
+ bzero(&ifr, sizeof(ifr));
+ snprintf(ifr.ifr_name, IFNAMSIZ, "%s", ovpn_ifname);
+
+ ret = ioctl(fd, SIOCIFDESTROY, &ifr);
+ if (ret)
+ atf_tc_fail("Failed to destroy interface");
+
+ ovpn_ifname[0] = 0;
+}
+
+ATF_TC_WITH_CLEANUP(tcp);
+ATF_TC_HEAD(tcp, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(tcp, tc)
+{
+ struct ifdrv drv;
+ struct sockaddr_in sock_in;
+ int ret;
+ nvlist_t *nvl;
+
+ /* Ensure the module is loaded. */
+ if (kldfind("if_ovpn") == -1 && errno == ENOENT)
+ atf_tc_skip("if_ovpn not loaded");
+
+ ovpn_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
+
+ /* Kick off a connect so there's a local address set, which we need for
+ * ovpn_new_peer() to get to the critical point. */
+ bzero(&sock_in, sizeof(sock_in));
+ sock_in.sin_family = AF_INET;
+ sock_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sock_in.sin_port = htons(1024);
+ connect(ovpn_fd, (struct sockaddr *)&sock_in, sizeof(sock_in));
+
+ ret = create_interface(ovpn_fd);
+ if (ret)
+ atf_tc_fail("Failed to create interface");
+
+ nvl = nvlist_create(0);
+
+ nvlist_add_number(nvl, "peerid", 0);
+ nvlist_add_number(nvl, "fd", ovpn_fd);
+ nvlist_add_nvlist(nvl, "remote", fake_sockaddr());
+
+ bzero(&drv, sizeof(drv));
+ snprintf(drv.ifd_name, IFNAMSIZ, "%s", ovpn_ifname);
+ drv.ifd_cmd = OVPN_NEW_PEER;
+ drv.ifd_data = nvlist_pack(nvl, &drv.ifd_len);
+
+ ret = ioctl(ovpn_fd, SIOCSDRVSPEC, &drv);
+ ATF_CHECK_EQ(ret, -1);
+ ATF_CHECK_EQ(errno, EPROTOTYPE);
+}
+
+ATF_TC_CLEANUP(tcp, tc)
+{
+ destroy_interface(ovpn_fd);
+ close(ovpn_fd);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, tcp);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/net/if_ovpn/server.crt b/tests/sys/net/if_ovpn/server.crt
new file mode 100644
index 000000000000..e4166fa2e0ae
--- /dev/null
+++ b/tests/sys/net/if_ovpn/server.crt
@@ -0,0 +1,123 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1048687 (0x10006f)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=KG, ST=NA, L=BISHKEK, O=OpenVPN-TEST/emailAddress=me@myhost.mydomain
+ Validity
+ Not Before: Apr 27 15:01:41 2022 GMT
+ Not After : Apr 3 15:01:41 2122 GMT
+ Subject: C=KG, ST=NA, O=OpenVPN-TEST, CN=Test-Server/emailAddress=me@myhost.mydomain
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (4096 bit)
+ Modulus:
+ 00:b1:4c:f3:d9:6e:39:ac:d7:8f:78:e9:37:dd:ae:
+ 73:f4:d6:77:84:42:3e:2a:76:76:6f:71:6a:b4:45:
+ a9:e3:84:0e:ee:3d:18:20:47:9d:fb:d1:ca:bb:d7:
+ cd:d5:e3:b8:3d:1b:9e:c1:f5:26:72:4a:bb:fe:4e:
+ ec:49:06:c0:ff:21:f4:b5:5c:72:fc:c7:3e:27:86:
+ 03:65:e2:d8:f7:5c:c7:23:16:82:ab:ee:81:7d:44:
+ 41:a0:34:06:14:19:08:7a:47:69:5e:b6:aa:6f:74:
+ 08:4f:13:ca:1d:b1:d8:2e:3a:a7:41:ec:e0:3e:b4:
+ 54:b2:7c:2e:dd:ee:f5:07:92:ed:f2:64:62:2f:7a:
+ c2:8e:f0:50:2d:f6:2b:1c:9d:1d:db:25:04:1e:b5:
+ 0d:18:c8:a4:b6:1e:cc:05:a1:10:74:e2:4c:98:32:
+ 44:6c:95:94:18:a0:64:0d:32:6b:84:f9:25:4d:04:
+ 0d:39:73:23:cf:b0:5a:ab:c0:ff:ec:c1:6b:ed:fa:
+ 9b:26:d0:45:d5:0e:75:72:d6:2f:36:26:fe:2d:bc:
+ 50:e0:a0:14:d4:34:e0:10:cf:aa:6a:46:79:7d:dc:
+ 30:e7:6c:c0:44:3e:fc:20:dd:e1:05:b2:a2:2f:aa:
+ 06:76:dd:33:44:19:8e:5c:54:50:d0:2b:a8:03:06:
+ ec:31:1c:48:1b:39:51:52:0f:44:b0:90:d5:29:c0:
+ b9:ae:e2:74:af:e2:08:c7:b2:e5:4e:71:f0:88:33:
+ 97:16:92:69:0b:48:8c:25:7c:8e:20:7c:8a:0f:32:
+ e6:15:90:02:33:d6:00:4f:1d:c9:7e:ef:5c:af:5f:
+ b4:f9:c5:8b:7b:c8:47:34:4d:85:80:f2:a9:3c:e0:
+ 53:d0:b7:15:59:67:0e:1b:17:6d:9b:ed:a8:14:e3:
+ 90:9d:6e:3a:83:ae:6f:0c:c6:58:2f:e6:41:f2:67:
+ b5:7c:86:97:98:55:59:14:a0:0a:f4:5f:2e:8d:ae:
+ e2:d9:68:a9:34:b1:c3:0f:44:04:9e:81:a5:45:8b:
+ 2c:82:a9:6c:ea:e7:a8:dd:27:8b:0e:0c:8c:14:35:
+ c8:86:01:1e:69:43:93:cb:57:c8:9d:43:d9:8e:22:
+ de:f8:27:e4:44:1c:3b:58:47:10:13:5f:ca:85:8b:
+ a6:ee:5a:27:17:13:9e:72:c5:a6:d1:60:e3:ec:69:
+ f0:2f:d0:d8:25:a1:80:c3:03:6b:5d:32:21:26:31:
+ 48:b7:ce:d2:32:2c:e9:1e:34:eb:21:e7:8e:a9:26:
+ 86:e6:a1:ba:0f:4a:8a:05:eb:4b:02:7a:65:e4:d3:
+ 39:da:10:3a:e6:0e:6f:22:ca:a0:2f:b7:7b:1e:65:
+ 05:73:d7
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Comment:
+ OpenSSL Generated Certificate
+ X509v3 Subject Key Identifier:
+ 4E:69:D9:D4:F1:8C:C7:F0:7D:02:79:DD:00:AB:5C:0B:61:42:F5:E2
+ X509v3 Authority Key Identifier:
+ keyid:88:07:18:C1:FE:2E:09:2E:7D:E0:B9:1B:83:A9:54:DE:09:B1:16:7E
+
+ Signature Algorithm: sha256WithRSAEncryption
+ 10:40:c5:a6:b4:4f:10:e1:ff:d4:fd:68:6d:b1:f4:02:7e:18:
+ 03:99:d2:fa:ce:24:b9:10:c3:d7:18:48:e6:9c:4c:b2:45:39:
+ a8:bc:7b:1f:66:bf:1a:bc:d5:22:f6:bc:61:e3:87:4d:d4:c8:
+ dc:ea:ee:5f:ad:95:94:e0:17:ff:7f:d5:b6:bd:a7:5f:2b:9d:
+ a4:5b:65:58:9f:83:c6:91:6f:d9:d9:1b:2e:e8:19:d8:d7:35:
+ ef:07:5d:1b:cd:89:2b:b3:d1:0c:bd:41:99:fc:54:fe:44:03:
+ a6:25:06:3a:e3:f5:3e:a1:9e:de:6b:7c:8e:dc:71:32:a9:2b:
+ 48:06:b7:72:f3:e4:38:fd:88:c0:62:48:d3:48:81:30:9e:ac:
+ 3f:d9:c6:40:92:98:39:7f:ec:bb:b8:8d:25:a0:c0:ed:c3:be:
+ 3c:df:54:42:3c:5d:2d:48:f5:35:b1:e9:b5:2f:0b:53:f1:fa:
+ 30:56:da:7d:2e:46:7d:7e:27:59:e5:ab:19:7b:be:9c:77:df:
+ 5d:6d:94:d7:3d:3b:45:09:0d:4e:a6:3e:2d:6d:95:76:78:af:
+ 03:d9:62:5d:64:14:f6:9b:36:a1:e4:6c:07:07:7f:ae:31:e6:
+ 69:6b:56:e6:42:6b:f6:de:24:ae:12:6a:58:13:31:9c:1a:87:
+ 01:a5:57:57:1f:0a:9e:16:85:30:c9:95:46:d1:05:70:df:39:
+ 80:fb:75:b1:44:43:e3:ba:8e:ef:c0:4d:db:9d:53:6c:32:e0:
+ 69:c8:74:b3:24:51:db:f9:7b:fb:0e:bc:61:0e:f3:56:31:2e:
+ 29:51:ed:dc:93:14:13:d0:6b:ab:88:d4:ae:e7:41:c2:da:7b:
+ 73:ec:d1:b9:49:07:85:73:e3:75:ed:ea:e8:48:09:01:45:52:
+ 58:05:37:bf:f8:5e:74:61:5f:1e:b2:db:1f:49:62:d1:ab:8f:
+ e6:d4:3e:69:1d:da:0b:93:88:3e:1c:2e:f4:03:32:9e:75:df:
+ af:65:0d:1c:cf:fd:35:36:f7:a5:93:01:11:69:2a:7b:76:8a:
+ 52:bb:e3:e9:b0:dc:e8:5b:78:65:3d:e9:36:84:8f:03:b3:ed:
+ 43:88:1f:97:71:ce:c6:c1:9e:e2:5d:51:b5:1a:bd:c6:fe:f1:
+ e2:c3:6c:95:6d:08:06:5b:b1:13:4b:57:36:07:c4:81:ea:d8:
+ f7:e4:2a:27:ce:75:1d:fd:ca:e4:82:a3:d1:0b:75:a3:77:8e:
+ c0:2b:34:87:2b:84:17:8c:23:e0:6d:fb:18:01:06:56:4b:f5:
+ 39:bc:3e:59:3d:ed:68:17:c0:12:66:56:0e:34:64:95:b2:84:
+ 99:de:71:94:2a:ca:a6:70
+-----BEGIN CERTIFICATE-----
+MIIFyjCCA7KgAwIBAgIDEABvMA0GCSqGSIb3DQEBCwUAMGYxCzAJBgNVBAYTAktH
+MQswCQYDVQQIDAJOQTEQMA4GA1UEBwwHQklTSEtFSzEVMBMGA1UECgwMT3BlblZQ
+Ti1URVNUMSEwHwYJKoZIhvcNAQkBFhJtZUBteWhvc3QubXlkb21haW4wIBcNMjIw
+NDI3MTUwMTQxWhgPMjEyMjA0MDMxNTAxNDFaMGoxCzAJBgNVBAYTAktHMQswCQYD
+VQQIDAJOQTEVMBMGA1UECgwMT3BlblZQTi1URVNUMRQwEgYDVQQDDAtUZXN0LVNl
+cnZlcjEhMB8GCSqGSIb3DQEJARYSbWVAbXlob3N0Lm15ZG9tYWluMIICIjANBgkq
+hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsUzz2W45rNePeOk33a5z9NZ3hEI+KnZ2
+b3FqtEWp44QO7j0YIEed+9HKu9fN1eO4PRuewfUmckq7/k7sSQbA/yH0tVxy/Mc+
+J4YDZeLY91zHIxaCq+6BfURBoDQGFBkIekdpXraqb3QITxPKHbHYLjqnQezgPrRU
+snwu3e71B5Lt8mRiL3rCjvBQLfYrHJ0d2yUEHrUNGMikth7MBaEQdOJMmDJEbJWU
+GKBkDTJrhPklTQQNOXMjz7Baq8D/7MFr7fqbJtBF1Q51ctYvNib+LbxQ4KAU1DTg
+EM+qakZ5fdww52zARD78IN3hBbKiL6oGdt0zRBmOXFRQ0CuoAwbsMRxIGzlRUg9E
+sJDVKcC5ruJ0r+IIx7LlTnHwiDOXFpJpC0iMJXyOIHyKDzLmFZACM9YATx3Jfu9c
+r1+0+cWLe8hHNE2FgPKpPOBT0LcVWWcOGxdtm+2oFOOQnW46g65vDMZYL+ZB8me1
+fIaXmFVZFKAK9F8uja7i2WipNLHDD0QEnoGlRYssgqls6ueo3SeLDgyMFDXIhgEe
+aUOTy1fInUPZjiLe+CfkRBw7WEcQE1/KhYum7lonFxOecsWm0WDj7GnwL9DYJaGA
+wwNrXTIhJjFIt87SMizpHjTrIeeOqSaG5qG6D0qKBetLAnpl5NM52hA65g5vIsqg
+L7d7HmUFc9cCAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3Bl
+blNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFE5p2dTxjMfwfQJ5
+3QCrXAthQvXiMB8GA1UdIwQYMBaAFIgHGMH+LgkufeC5G4OpVN4JsRZ+MA0GCSqG
+SIb3DQEBCwUAA4ICAQAQQMWmtE8Q4f/U/WhtsfQCfhgDmdL6ziS5EMPXGEjmnEyy
+RTmovHsfZr8avNUi9rxh44dN1Mjc6u5frZWU4Bf/f9W2vadfK52kW2VYn4PGkW/Z
+2Rsu6BnY1zXvB10bzYkrs9EMvUGZ/FT+RAOmJQY64/U+oZ7ea3yO3HEyqStIBrdy
+8+Q4/YjAYkjTSIEwnqw/2cZAkpg5f+y7uI0loMDtw74831RCPF0tSPU1sem1LwtT
+8fowVtp9LkZ9fidZ5asZe76cd99dbZTXPTtFCQ1Opj4tbZV2eK8D2WJdZBT2mzah
+5GwHB3+uMeZpa1bmQmv23iSuEmpYEzGcGocBpVdXHwqeFoUwyZVG0QVw3zmA+3Wx
+REPjuo7vwE3bnVNsMuBpyHSzJFHb+Xv7DrxhDvNWMS4pUe3ckxQT0GuriNSu50HC
+2ntz7NG5SQeFc+N17eroSAkBRVJYBTe/+F50YV8estsfSWLRq4/m1D5pHdoLk4g+
+HC70AzKedd+vZQ0cz/01NvelkwERaSp7dopSu+PpsNzoW3hlPek2hI8Ds+1DiB+X
+cc7GwZ7iXVG1Gr3G/vHiw2yVbQgGW7ETS1c2B8SB6tj35ConznUd/crkgqPRC3Wj
+d47AKzSHK4QXjCPgbfsYAQZWS/U5vD5ZPe1oF8ASZlYONGSVsoSZ3nGUKsqmcA==
+-----END CERTIFICATE-----
diff --git a/tests/sys/net/if_ovpn/server.key b/tests/sys/net/if_ovpn/server.key
new file mode 100644
index 000000000000..f35d6fcd4563
--- /dev/null
+++ b/tests/sys/net/if_ovpn/server.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEAsUzz2W45rNePeOk33a5z9NZ3hEI+KnZ2b3FqtEWp44QO7j0Y
+IEed+9HKu9fN1eO4PRuewfUmckq7/k7sSQbA/yH0tVxy/Mc+J4YDZeLY91zHIxaC
+q+6BfURBoDQGFBkIekdpXraqb3QITxPKHbHYLjqnQezgPrRUsnwu3e71B5Lt8mRi
+L3rCjvBQLfYrHJ0d2yUEHrUNGMikth7MBaEQdOJMmDJEbJWUGKBkDTJrhPklTQQN
+OXMjz7Baq8D/7MFr7fqbJtBF1Q51ctYvNib+LbxQ4KAU1DTgEM+qakZ5fdww52zA
+RD78IN3hBbKiL6oGdt0zRBmOXFRQ0CuoAwbsMRxIGzlRUg9EsJDVKcC5ruJ0r+II
+x7LlTnHwiDOXFpJpC0iMJXyOIHyKDzLmFZACM9YATx3Jfu9cr1+0+cWLe8hHNE2F
+gPKpPOBT0LcVWWcOGxdtm+2oFOOQnW46g65vDMZYL+ZB8me1fIaXmFVZFKAK9F8u
+ja7i2WipNLHDD0QEnoGlRYssgqls6ueo3SeLDgyMFDXIhgEeaUOTy1fInUPZjiLe
++CfkRBw7WEcQE1/KhYum7lonFxOecsWm0WDj7GnwL9DYJaGAwwNrXTIhJjFIt87S
+MizpHjTrIeeOqSaG5qG6D0qKBetLAnpl5NM52hA65g5vIsqgL7d7HmUFc9cCAwEA
+AQKCAgB3Me/B3juB+o0m0UtQikbEdCZ3UP1wGKH4u/vrY+YZ4Z8UBRiiIuOP9vNf
+o25q/CPRWS863+/P6HRIPJDXa2X2X8Ke5p8bV2tusMa6CW6ppcLu0ORaaAa/y1J/
+PFpVypqLclThatZcBVrMptY7bmOSeLYXOQNsxFkogRoU89/hDqNPULM9jj8cT2zn
+6VYEb5Ax0snZRwid/83T7hJlOmnQ2o55x1l+0nR0tedtg9cK12B/TVkCpWiO6NWc
+IC0t4r8Hh3Ik/uHjoUvOPzYQJti8sJyC1rwKCd4VDzdXKTfmKFDsVI1RlDJ2ehQc
+e4JTnu+nm4AqqS+u3LRTrvXNyyqfnkIeW4M4OQLkWf9t2JMI8S4wzeqRuwPR2l64
+8iwILZk1ppdXnwfMco8/OPjNOWX446863mXI1vdtWlXPGximQ47BKcJCdvNvZwEl
+BWKK0arr/xDMykwPImxCW6hxCo9Ba7KhzKuoQM3byF+EhmlG3wx+8xKAZAmOfVo/
+YOTexuotj+JRU3LAZsOBU2L/mvFYFPPwG7JldVn7tyt+Lh1wRqsr0t+oEomImfDz
+c7csQWV+HJXUpsFhwxUU2foUJlVx5n/60tZs/TLQojXepK8w+vLwmzzMW/Q5MWcI
+DWk8VkOOCgAP/ID17/eVIfoaGbMGRtsSKFJUgiSu55u8ptloSQKCAQEA5gYOK43j
+nHAz/GHDr5rHp6Rn3fPuLR6WpKDNO0suQa/y3hUzGJH4LfywhXApCkGW973NnCgn
+HZI/Sh0kxZ2MtxaEW0We9woypTFDBvH+0ItVFJK/ZzYRD/lT+X5Wrx5nJAq95KCP
+hRjdbk/BACczA64vvmUCNzpFhupAWvomQcS8vLuV2VrJnriX/zYXgXcHL8yB8RfC
+d+2rSjZxOJ2PwUKBwsePNqgaSrS7zgcI3dfQkE77MSR3eN8NmOaJY7nCmhvBldAH
+cflzP0+DDOrpZmGjot/y9fso3IDuiVtB92BdBHJuFwwhQNIZXaPSv6UAoITvKF6Q
+nsUrqxxL6am2cwKCAQEAxVKuvF+4ipOQpk0zvLyy5m2zytFgLMrv5PfPvh99pntt
+odltCLx2DzWJEaSsVXv9iaRyy+9/SnoSjNDIbTT1Pg1Wp7Aq0J5/Xyl6s9GQzDNK
+DuOxrcVeSSOBKp/r7T+xQUq8HD7C2k1jN6kqcHDgO1mW/W5sblVIW+BEWOX5xeQ2
+5BwJteXN7VtNZn8/PMDqMqqc6MXiONd9AFvxosIgYDaR23F4pwninvjjQEgW+q2G
+hvwpZxClkDDpx3n1En87XYdLt8p89I9fH22DIC/Un4LYqR3+5ppx3T0zzRofntmh
+JKrIudcFpWcSdi27G5NolPQQ2fb0No0lCbwMZAAQDQKCAQEAleFWNF0E9XdK+GV/
+i5nQBFUk9MOv6yhmQikg8UTAhD6wgrLPk2/xhY2EO75kj3FDfHPpWJn1OtiDcrhg
+sH9DJD2AyrQnq5Kyg18A7LKcNajELF6eZxMctQriA8ylkP+/dwWkzCcuvSwBhJJl
+EMN6AyjppSbN9cx7ZziV7HHYobweut+D+Zeljk17hOjrEgnL3gJknQK9TUXI+ddV
+mO1ZsTSztoYvtA5+6zSutsVwqpSoKo+8Lz4ytsioZHu7BAcTXTU+w25Em6hNxu/5
+VV5v7K0sYcGI32zjKCK+yzNyXU0l7vLc9xmJRWJg8tn/Ra6vJOjZqLVNiJazKJCM
+illyLwKCAQBn4wgsFRlLnDVj2PGMRKzLtKYb+e/wpUd3/SBasKmupP0rYRWOq+pc
+R4tKxrAUsZrihLoLtKQHyg1KJgHfvSoA6XTeBFoGS+wzZds8IPFjEP3EqQw6uNbT
+GuY+UsQbvJTOE1LGbCSaWnQKMf4uBL+Jf7mG5EQiMrRN6t0REMNX9LcRkdFq+vpY
+JOGzPPtGOSsUUc8anlRkKM+fCMlHL31sKk7QggVLrGCr4c2DYnD2ubVCDDCgGpuQ
+NrBeXU8x1dqjez/aG7l96J3kJfwLTiNbd8AqCajSMC4SlM5ZBY/wShQVAfV8IkDO
+vF1z6s+/zPQauATHPMWGkvkVDvRXEdFhAoIBAFcbdqOHQq7OTEfgOQZsu5j2GPh2
+xmxHwggE5UZtaRRixOI1ThopGCBlXJoj6NvyXcG9ALdU48hWK7iGLLjL7KDV7l4Y
+OOC/bEUsOOiQt0zkL9KFEICJJN6cB9DWWXbpN9VKUO9qoZ6ms/NPM+Re60LDD+Pe
+eW7G68LBS24+z532LgpzJ/y5vldVSM0YjSEioD8o3Ns4T8sB42J2r3AtKjMhz0NA
+fwBFnVTZLXQgO9S6aHQepwAfkTNe7YY4lSJsY7b1urWRww16PQLbhwgO7tS566fM
+iHlPeEBnR8FiXjcFrIQCb6/h2DCJdjVZwNT0g6ng23JIPnLAT1KMnpOBv0E=
+-----END RSA PRIVATE KEY-----
diff --git a/tests/sys/net/if_ovpn/user.pass b/tests/sys/net/if_ovpn/user.pass
new file mode 100644
index 000000000000..59d468ee39af
--- /dev/null
+++ b/tests/sys/net/if_ovpn/user.pass
@@ -0,0 +1,2 @@
+username
+password
diff --git a/tests/sys/net/if_ovpn/utils.subr b/tests/sys/net/if_ovpn/utils.subr
new file mode 100644
index 000000000000..0da35119b2bf
--- /dev/null
+++ b/tests/sys/net/if_ovpn/utils.subr
@@ -0,0 +1,73 @@
+##
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2022 Rubicon Communications, LLC ("Netgate")
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/../../common/vnet.subr
+
+ovpn_init()
+{
+ vnet_init
+
+ if ! kldstat -q -m if_ovpn; then
+ atf_skip "This test requires if_ovpn"
+ fi
+
+ has_dco=$(openvpn --version 2>&1 | grep '\[DCO\]')
+ if [ -z "$has_dco" ]; then
+ atf_skip "openvpn binary does not support DCO"
+ fi
+}
+
+ovpn_cleanup()
+{
+ for jail in `cat ovpn_jails.lst | sort -u`
+ do
+ cat ovpn_${jail}.log| sed s/^/\[${jail}\]\ /
+ done
+
+ vnet_cleanup
+}
+
+ovpn_start()
+{
+ jail=$1
+ cfg=$2
+
+ echo ${jail} >> ovpn_jails.lst
+
+ dir=$(pwd)
+
+ echo "Start" >> ovpn_${jail}.log
+ echo "=====" >> ovpn_${jail}.log
+
+ echo "$cfg" > ovpn_${jail}.ovpn
+
+ echo "Jail $jail:"
+ echo "==========="
+ cat ovpn_${jail}.ovpn
+
+ jexec $jail sh -c "cd ${dir} &&
+ openvpn --config ovpn_${jail}.ovpn >> ovpn_${jail}.log &"
+}
diff --git a/tests/sys/net/if_stf.sh b/tests/sys/net/if_stf.sh
new file mode 100644
index 000000000000..71b00f308014
--- /dev/null
+++ b/tests/sys/net/if_stf.sh
@@ -0,0 +1,203 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2021 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+atf_test_case "6to4" "cleanup"
+6to4_head()
+{
+ atf_set descr 'Test 6to4'
+ atf_set require.user root
+}
+
+6to4_body()
+{
+ vnet_init
+ if ! kldstat -q -m if_stf; then
+ atf_skip "This test requires if_stf"
+ fi
+ if ! kldstat -q -m if_gif; then
+ atf_skip "This test requires if_gif"
+ fi
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail relay ${epair}a
+ jexec relay ifconfig lo0 inet6 2001:db8::1/64 up
+ jexec relay ifconfig ${epair}a 192.0.2.1/24 up
+ # Simple gif to terminate 6to4
+ gif=$(jexec relay ifconfig gif create)
+ jexec relay ifconfig $gif inet6 2002:c000:0201::1/64 up
+ jexec relay ifconfig $gif tunnel 192.0.2.1 192.0.2.2
+ jexec relay route -6 add default -interface $gif
+
+ vnet_mkjail client ${epair}b
+ jexec client ifconfig lo0 up
+ jexec client ifconfig ${epair}b 192.0.2.2/24 up
+ stf=$(jexec client ifconfig stf create)
+ jexec client ifconfig $stf inet6 2002:c000:0202::1/32 up
+ jexec client route -6 add default 2002:c000:0201::1
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ jexec client ping -c 1 192.0.2.1
+ # 6to4 direct
+ atf_check -s exit:0 -o ignore \
+ jexec client ping6 -c 1 2002:c000:0201::1
+
+ # "Wider internet"
+ atf_check -s exit:0 -o ignore \
+ jexec client ping6 -c 1 2001:db8::1
+}
+
+6to4_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "6rd" "cleanup"
+6rd_head()
+{
+ atf_set descr '6RD test'
+ atf_set require.user root
+}
+
+6rd_body()
+{
+ vnet_init
+
+ if ! kldstat -q -m if_stf; then
+ atf_skip "This test requires if_stf"
+ fi
+ if ! kldstat -q -m if_gif; then
+ atf_skip "This test requires if_gif"
+ fi
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail br ${epair}a
+ jexec br ifconfig ${epair}a 192.0.2.1/24 up
+
+ # Simple gif to terminate the 6RD tunnel
+ gif=$(jexec br ifconfig gif create)
+ jexec br ifconfig lo0 inet6 2001:db9::1/64 up
+ jexec br ifconfig $gif inet6 2001:db8::/64 up
+ jexec br ifconfig $gif tunnel 192.0.2.1 192.0.2.2
+ jexec br route -6 add default -interface $gif
+
+ vnet_mkjail client ${epair}b
+ jexec client ifconfig lo0 up
+ jexec client ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ jexec client ping -c 1 192.0.2.1
+
+ stf=$(jexec client ifconfig stf create)
+
+ jexec client ifconfig $stf stfv4br 192.0.2.1
+ jexec client ifconfig $stf stfv4net 192.0.2.2/32
+ jexec client ifconfig $stf inet6 2001:db8:c000:0202::1/32 up
+ jexec client route -6 add default -interface $stf
+
+ atf_check -s exit:0 -o ignore \
+ jexec client ping6 -c 1 2001:db9::1
+}
+
+6rd_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "6rd_peer" "cleanup"
+6rd_peer_head()
+{
+ atf_set descr '6RD peer test'
+ atf_set require.user root
+}
+
+6rd_peer_body()
+{
+ vnet_init
+
+ if ! kldstat -q -m if_stf; then
+ atf_skip "This test requires if_stf"
+ fi
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail one ${epair}a
+ jexec one ifconfig lo0 up
+ jexec one ifconfig ${epair}a 192.0.2.1/24 up
+ stf_one=$(jexec one ifconfig stf create)
+ jexec one ifconfig $stf_one stfv4br 192.0.2.3
+ jexec one ifconfig $stf_one stfv4net 192.0.2.1/32
+ jexec one ifconfig $stf_one inet6 2001:db8:c000:0201::1/32 up
+ jexec one route -6 add default -interface $stf_one
+
+ vnet_mkjail two ${epair}b
+ jexec two ifconfig lo0 up
+ jexec two ifconfig ${epair}b 192.0.2.2/24 up
+ stf_two=$(jexec two ifconfig stf create)
+ jexec two ifconfig $stf_two stfv4br 192.0.2.3
+ jexec two ifconfig $stf_two stfv4net 192.0.2.2/32
+ jexec two ifconfig $stf_two inet6 2001:db8:c000:0202::1/32 up
+ jexec two route -6 add default -interface $stf_two
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ jexec one ping -c 1 192.0.2.2
+
+ # Test 6rd
+ atf_check -s exit:0 -o ignore \
+ jexec one ping6 -c 1 2001:db8:c000:0202::1
+ atf_check -s exit:0 -o ignore \
+ jexec two ping6 -c 1 2001:db8:c000:0201::1
+
+ # Shorter prefixes, for both v4 and v6
+ jexec one ifconfig $stf_one inet6 2001:db8:c000:0201::1 delete
+ jexec one ifconfig $stf_one inet6 2001:0201::1/16
+ jexec one ifconfig $stf_one stfv4net 192.0.2.1/16
+ jexec two ifconfig $stf_two inet6 2001:db8:c000:0202::1 delete
+ jexec two ifconfig $stf_two inet6 2001:0202::1/16
+ jexec two ifconfig $stf_two stfv4net 192.0.2.2/16
+
+ atf_check -s exit:0 -o ignore \
+ jexec one ping6 -c 1 2001:0202::1
+ atf_check -s exit:0 -o ignore \
+ jexec two ping6 -c 1 2001:0201::1
+}
+
+6rd_peer_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "6to4"
+ atf_add_test_case "6rd"
+ atf_add_test_case "6rd_peer"
+}
diff --git a/tests/sys/net/if_tun_test.sh b/tests/sys/net/if_tun_test.sh
new file mode 100755
index 000000000000..a4ffe66e04ce
--- /dev/null
+++ b/tests/sys/net/if_tun_test.sh
@@ -0,0 +1,63 @@
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+atf_test_case "235704" "cleanup"
+235704_head()
+{
+ atf_set descr "Test PR #235704"
+ atf_set require.user root
+}
+235704_body()
+{
+ vnet_init
+ vnet_mkjail one
+
+ tun=$(jexec one ifconfig tun create)
+ jexec one ifconfig ${tun} name foo
+ atf_check -s exit:0 jexec one ifconfig foo destroy
+}
+235704_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "basic" "cleanup"
+basic_head()
+{
+ atf_set descr "Test if_tun using nc"
+ atf_set require.user root
+}
+basic_body()
+{
+ vnet_init
+
+ epair=$(vnet_mkepair)
+
+ tun_duke=$(ifconfig tun create)
+ tun_bass=$(ifconfig tun create)
+
+ vnet_mkjail duke ${epair}a ${tun_duke}
+ vnet_mkjail bass ${epair}b ${tun_bass}
+
+ jexec duke ifconfig ${epair}a inet 10.0.0.1/24 up
+ jexec bass ifconfig ${epair}b inet 10.0.0.2/24 up
+
+ jexec duke nc -u -l --tun /dev/${tun_duke} 10.0.0.1 2600 &
+ jexec bass nc -u --tun /dev/${tun_bass} 10.0.0.1 2600 &
+
+ jexec duke ifconfig ${tun_duke} inet 10.100.0.1/24 10.100.0.2 up
+ jexec bass ifconfig ${tun_bass} inet 10.100.0.2/24 10.100.0.1 up
+
+ atf_check -s exit:0 -o ignore \
+ jexec bass ping -c 1 10.100.0.1
+}
+basic_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "235704"
+ atf_add_test_case "basic"
+}
diff --git a/tests/sys/net/if_vlan.sh b/tests/sys/net/if_vlan.sh
new file mode 100755
index 000000000000..8122203337e2
--- /dev/null
+++ b/tests/sys/net/if_vlan.sh
@@ -0,0 +1,373 @@
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+atf_test_case "basic" "cleanup"
+basic_head()
+{
+ atf_set descr 'Basic VLAN test'
+ atf_set require.user root
+}
+
+basic_body()
+{
+ vnet_init
+
+ epair_vlan=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair_vlan}a
+ vnet_mkjail singsing ${epair_vlan}b
+
+ vlan0=$(jexec alcatraz ifconfig vlan create vlandev ${epair_vlan}a \
+ vlan 42)
+ jexec alcatraz ifconfig ${epair_vlan}a up
+ jexec alcatraz ifconfig ${vlan0} 10.0.0.1/24 up
+
+ vlan1=$(jexec singsing ifconfig vlan create)
+
+ # Test associating the physical interface
+ atf_check -s exit:0 \
+ jexec singsing ifconfig ${vlan1} vlandev ${epair_vlan}b vlan 42
+
+ jexec singsing ifconfig ${epair_vlan}b up
+ jexec singsing ifconfig ${vlan1} 10.0.0.2/24 up
+
+ atf_check -s exit:0 -o ignore jexec singsing ping -c 1 10.0.0.1
+
+ # Test changing the vlan ID
+ atf_check -s exit:0 \
+ jexec singsing ifconfig ${vlan1} vlandev ${epair_vlan}b vlan 43
+ atf_check -s exit:2 -o ignore jexec singsing ping -c 1 10.0.0.1
+
+ # And change back
+ # Test changing the vlan ID
+ atf_check -s exit:0 \
+ jexec singsing ifconfig ${vlan1} vlan 42 vlandev ${epair_vlan}b
+ atf_check -s exit:0 -o ignore jexec singsing ping -c 1 10.0.0.1
+}
+
+basic_cleanup()
+{
+ vnet_cleanup
+}
+
+# Simple Q-in-Q (802.1Q over 802.1ad)
+
+atf_test_case "qinq_simple" "cleanup"
+qinq_simple_head()
+{
+ atf_set descr 'Simple Q-in-Q test (802.1Q over 802.1ad)'
+ atf_set require.user root
+}
+
+qinq_simple_body()
+{
+ vnet_init
+
+ epair_qinq=$(vnet_mkepair)
+
+ vnet_mkjail jqinq0 ${epair_qinq}a
+ vnet_mkjail jqinq1 ${epair_qinq}b
+
+ vlan5a=$(jexec jqinq0 ifconfig vlan create \
+ vlandev ${epair_qinq}a vlan 5 vlanproto 802.1ad)
+ vlan42a=$(jexec jqinq0 ifconfig vlan create \
+ vlandev ${vlan5a} vlan 42 vlanproto 802.1q)
+ jexec jqinq0 ifconfig ${epair_qinq}a up
+ jexec jqinq0 ifconfig ${vlan5a} up
+ jexec jqinq0 ifconfig ${vlan42a} 10.5.42.1/24 up
+
+ vlan5b=$(jexec jqinq1 ifconfig vlan create \
+ vlandev ${epair_qinq}b vlan 5 vlanproto 802.1ad)
+ vlan42b=$(jexec jqinq1 ifconfig vlan create \
+ vlandev ${vlan5b} vlan 42 vlanproto 802.1q)
+ jexec jqinq1 ifconfig ${epair_qinq}b up
+ jexec jqinq1 ifconfig ${vlan5b} up
+ jexec jqinq1 ifconfig ${vlan42b} 10.5.42.2/24 up
+
+ atf_check -s exit:0 -o ignore jexec jqinq1 ping -c 1 10.5.42.1
+}
+
+qinq_simple_cleanup()
+{
+ vnet_cleanup
+}
+
+# Deep Q-in-Q (802.1Q over 802.1ad over 802.1ad)
+
+atf_test_case "qinq_deep" "cleanup"
+qinq_deep_head()
+{
+ atf_set descr 'Deep Q-in-Q test (802.1Q over 802.1ad over 802.1ad)'
+ atf_set require.user root
+}
+
+qinq_deep_body()
+{
+ vnet_init
+
+ epair_qinq=$(vnet_mkepair)
+
+ vnet_mkjail jqinq2 ${epair_qinq}a
+ vnet_mkjail jqinq3 ${epair_qinq}b
+
+ vlan5a=$(jexec jqinq2 ifconfig vlan create \
+ vlandev ${epair_qinq}a vlan 5 vlanproto 802.1ad)
+ vlan6a=$(jexec jqinq2 ifconfig vlan create \
+ vlandev ${vlan5a} vlan 6 vlanproto 802.1ad)
+ vlan42a=$(jexec jqinq2 ifconfig vlan create \
+ vlandev ${vlan6a} vlan 42 vlanproto 802.1q)
+ jexec jqinq2 ifconfig ${epair_qinq}a up
+ jexec jqinq2 ifconfig ${vlan5a} up
+ jexec jqinq2 ifconfig ${vlan6a} up
+ jexec jqinq2 ifconfig ${vlan42a} 10.6.42.1/24 up
+
+ vlan5b=$(jexec jqinq3 ifconfig vlan create \
+ vlandev ${epair_qinq}b vlan 5 vlanproto 802.1ad)
+ vlan6b=$(jexec jqinq3 ifconfig vlan create \
+ vlandev ${vlan5b} vlan 6 vlanproto 802.1ad)
+ vlan42b=$(jexec jqinq3 ifconfig vlan create \
+ vlandev ${vlan6b} vlan 42 vlanproto 802.1q)
+ jexec jqinq3 ifconfig ${epair_qinq}b up
+ jexec jqinq3 ifconfig ${vlan5b} up
+ jexec jqinq3 ifconfig ${vlan6b} up
+ jexec jqinq3 ifconfig ${vlan42b} 10.6.42.2/24 up
+
+ atf_check -s exit:0 -o ignore jexec jqinq3 ping -c 1 10.6.42.1
+}
+
+qinq_deep_cleanup()
+{
+ vnet_cleanup
+}
+
+# Legacy Q-in-Q (802.1Q over 802.1Q)
+
+atf_test_case "qinq_legacy" "cleanup"
+qinq_legacy_head()
+{
+ atf_set descr 'Legacy Q-in-Q test (802.1Q over 802.1Q)'
+ atf_set require.user root
+}
+
+qinq_legacy_body()
+{
+ vnet_init
+
+ epair_qinq=$(vnet_mkepair)
+
+ vnet_mkjail jqinq4 ${epair_qinq}a
+ vnet_mkjail jqinq5 ${epair_qinq}b
+
+ vlan5a=$(jexec jqinq4 ifconfig vlan create \
+ vlandev ${epair_qinq}a vlan 5)
+ vlan42a=$(jexec jqinq4 ifconfig vlan create \
+ vlandev ${vlan5a} vlan 42)
+ jexec jqinq4 ifconfig ${epair_qinq}a up
+ jexec jqinq4 ifconfig ${vlan5a} up
+ jexec jqinq4 ifconfig ${vlan42a} 10.5.42.1/24 up
+
+ vlan5b=$(jexec jqinq5 ifconfig vlan create \
+ vlandev ${epair_qinq}b vlan 5)
+ vlan42b=$(jexec jqinq5 ifconfig vlan create \
+ vlandev ${vlan5b} vlan 42)
+ jexec jqinq5 ifconfig ${epair_qinq}b up
+ jexec jqinq5 ifconfig ${vlan5b} up
+ jexec jqinq5 ifconfig ${vlan42b} 10.5.42.2/24 up
+
+ atf_check -s exit:0 -o ignore jexec jqinq5 ping -c 1 10.5.42.1
+}
+
+qinq_legacy_cleanup()
+{
+ vnet_cleanup
+}
+
+# Simple Q-in-Q with dot notation
+
+atf_test_case "qinq_dot" "cleanup"
+qinq_dot_head()
+{
+ atf_set descr 'Simple Q-in-Q test with dot notation'
+ atf_set require.user root
+}
+
+qinq_dot_body()
+{
+ vnet_init
+
+ epair_qinq=$(vnet_mkepair)
+
+ vnet_mkjail jqinq6 ${epair_qinq}a
+ vnet_mkjail jqinq7 ${epair_qinq}b
+
+ jexec jqinq6 ifconfig vlan5 create \
+ vlandev ${epair_qinq}a vlan 5 vlanproto 802.1ad
+ jexec jqinq6 ifconfig vlan5.42 create \
+ vlanproto 802.1q
+ jexec jqinq6 ifconfig ${epair_qinq}a up
+ jexec jqinq6 ifconfig vlan5 up
+ jexec jqinq6 ifconfig vlan5.42 10.5.42.1/24 up
+
+ vlan5b=$(jexec jqinq7 ifconfig vlan create \
+ vlandev ${epair_qinq}b vlan 5 vlanproto 802.1ad)
+ vlan42b=$(jexec jqinq7 ifconfig vlan create \
+ vlandev ${vlan5b} vlan 42 vlanproto 802.1q)
+ jexec jqinq7 ifconfig ${epair_qinq}b up
+ jexec jqinq7 ifconfig ${vlan5b} up
+ jexec jqinq7 ifconfig ${vlan42b} 10.5.42.2/24 up
+
+ atf_check -s exit:0 -o ignore jexec jqinq7 ping -c 1 10.5.42.1
+}
+
+qinq_dot_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "qinq_setflags" "cleanup"
+qinq_setflags_head()
+{
+ atf_set descr 'Test setting flags on a QinQ device'
+ atf_set require.user root
+}
+
+qinq_setflags_body()
+{
+ vnet_init
+
+ epair=$(vnet_mkepair)
+
+ ifconfig ${epair}a up
+ vlan1=$(ifconfig vlan create)
+ ifconfig $vlan1 vlan 1 vlandev ${epair}a
+ vlan2=$(ifconfig vlan create)
+ ifconfig $vlan2 vlan 2 vlandev $vlan1
+
+ # This panics, incorrect locking
+ ifconfig $vlan2 promisc
+}
+
+qinq_setflags_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "bpf_pcp" "cleanup"
+bpf_pcp_head()
+{
+ atf_set descr 'Set VLAN PCP through BPF'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+bpf_pcp_body()
+{
+ vnet_init
+
+ epair=$(vnet_mkepair)
+
+ ifconfig ${epair}a up
+
+ vnet_mkjail alcatraz ${epair}b
+ vlan=$(jexec alcatraz ifconfig vlan create)
+ jexec alcatraz ifconfig ${vlan} vlan 42 vlandev ${epair}b
+ jexec alcatraz ifconfig ${vlan} up
+ jexec alcatraz ifconfig ${epair}b up
+
+ jexec alcatraz sysctl net.link.vlan.mtag_pcp=1
+
+ jexec alcatraz dhclient ${vlan} &
+ atf_check -s exit:1 -o ignore -e ignore $(atf_get_srcdir)/pcp.py \
+ --expect-pcp 6 \
+ --recvif ${epair}a
+
+ jexec alcatraz killall dhclient
+ sleep 1
+
+ jexec alcatraz dhclient -c $(atf_get_srcdir)/dhclient_pcp.conf ${vlan} &
+ atf_check -s exit:0 -o ignore -e ignore $(atf_get_srcdir)/pcp.py \
+ --expect-pcp 6 \
+ --recvif ${epair}a
+}
+
+bpf_pcp_cleanup()
+{
+ sysctl net.link.vlan.mtag_pcp=0
+ jexec alcatraz killall dhclient
+ vnet_cleanup
+}
+
+atf_test_case "conflict_id" "cleanup"
+conflict_id_head()
+{
+ atf_set descr 'Test conflicting VLAN IDs, PR #279195'
+ atf_set require.user root
+}
+
+conflict_id_body()
+{
+ vnet_init
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair}b
+ vlan_a=$(jexec alcatraz ifconfig vlan create)
+ vlan_b=$(jexec alcatraz ifconfig vlan create)
+
+ jexec alcatraz ifconfig ${vlan_a} vlan 100 vlandev ${epair}b
+ jexec alcatraz ifconfig ${vlan_b} vlan 101 vlandev ${epair}b
+
+ atf_check -s exit:1 -o ignore -e ignore \
+ jexec alcatraz ifconfig ${vlan_a} vlan 101
+
+ atf_check -s exit:0 -o match:"vlan: 100" \
+ jexec alcatraz ifconfig ${vlan_a}
+
+ atf_check -s exit:0 -o ignore -e ignore \
+ jexec alcatraz ifconfig ${vlan_a} vlan 100
+}
+
+conflict_id_cleanup()
+{
+ vnet_cleanup
+
+}
+
+# If a vlan interface is in a bridge, changing the vlandev to refer to
+# a bridge should not be allowed.
+atf_test_case "bridge_vlandev" "cleanup"
+bridge_vlandev_head()
+{
+ atf_set descr 'transforming a bridge member vlan into an SVI is not allowed'
+ atf_set require.user root
+}
+
+bridge_vlandev_body()
+{
+ vnet_init
+ vnet_init_bridge
+
+ bridge=$(vnet_mkbridge)
+ vlan=$(vnet_mkvlan)
+
+ atf_check -s exit:0 ifconfig ${bridge} addm ${vlan}
+ atf_check -s exit:1 -e ignore ifconfig ${vlan} vlan 1 vlandev ${bridge}
+}
+
+bridge_vlandev_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "basic"
+ atf_add_test_case "qinq_simple"
+ atf_add_test_case "qinq_deep"
+ atf_add_test_case "qinq_legacy"
+ atf_add_test_case "qinq_dot"
+ atf_add_test_case "qinq_setflags"
+ atf_add_test_case "bpf_pcp"
+ atf_add_test_case "conflict_id"
+ atf_add_test_case "bridge_vlandev"
+}
diff --git a/tests/sys/net/if_wg.sh b/tests/sys/net/if_wg.sh
new file mode 100644
index 000000000000..1f51d86c8efa
--- /dev/null
+++ b/tests/sys/net/if_wg.sh
@@ -0,0 +1,633 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2021 The FreeBSD Foundation
+#
+# This software was developed by Mark Johnston under sponsorship
+# from the FreeBSD Foundation.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+atf_test_case "wg_basic" "cleanup"
+wg_basic_head()
+{
+ atf_set descr 'Create a wg(4) tunnel over an epair and pass traffic between jails'
+ atf_set require.user root
+ atf_set require.kmods if_wg
+}
+
+wg_basic_body()
+{
+ local epair pri1 pri2 pub1 pub2 wg1 wg2
+ local endpoint1 endpoint2 tunnel1 tunnel2
+
+ pri1=$(wg genkey)
+ pri2=$(wg genkey)
+
+ endpoint1=192.168.2.1
+ endpoint2=192.168.2.2
+ tunnel1=169.254.0.1
+ tunnel2=169.254.0.2
+
+ epair=$(vnet_mkepair)
+
+ vnet_init
+
+ vnet_mkjail wgtest1 ${epair}a
+ vnet_mkjail wgtest2 ${epair}b
+
+ jexec wgtest1 ifconfig ${epair}a ${endpoint1}/24 up
+ jexec wgtest2 ifconfig ${epair}b ${endpoint2}/24 up
+
+ wg1=$(jexec wgtest1 ifconfig wg create)
+ echo "$pri1" | jexec wgtest1 wg set $wg1 listen-port 12345 \
+ private-key /dev/stdin
+ pub1=$(jexec wgtest1 wg show $wg1 public-key)
+ wg2=$(jexec wgtest2 ifconfig wg create)
+ echo "$pri2" | jexec wgtest2 wg set $wg2 listen-port 12345 \
+ private-key /dev/stdin
+ pub2=$(jexec wgtest2 wg show $wg2 public-key)
+
+ atf_check -s exit:0 -o ignore \
+ jexec wgtest1 wg set $wg1 peer "$pub2" \
+ endpoint ${endpoint2}:12345 allowed-ips ${tunnel2}/32
+ atf_check -s exit:0 \
+ jexec wgtest1 ifconfig $wg1 inet ${tunnel1}/24 up
+
+ atf_check -s exit:0 -o ignore \
+ jexec wgtest2 wg set $wg2 peer "$pub1" \
+ endpoint ${endpoint1}:12345 allowed-ips ${tunnel1}/32
+ atf_check -s exit:0 \
+ jexec wgtest2 ifconfig $wg2 inet ${tunnel2}/24 up
+
+ # Generous timeout since the handshake takes some time.
+ atf_check -s exit:0 -o ignore jexec wgtest1 ping -c 1 -t 5 $tunnel2
+ atf_check -s exit:0 -o ignore jexec wgtest2 ping -c 1 $tunnel1
+}
+
+wg_basic_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "wg_basic_crossaf" "cleanup"
+wg_basic_crossaf_head()
+{
+ atf_set descr 'Create a wg(4) tunnel and pass IPv4 traffic over an IPv6 nexthop'
+ atf_set require.user root
+}
+
+wg_basic_crossaf_body()
+{
+ local epair pri1 pri2 pub1 pub2 wg1 wg2
+ local endpoint1 endpoint2 tunnel1 tunnel2
+ local testnet testlocal testremote
+
+ kldload -n if_wg || atf_skip "This test requires if_wg and could not load it"
+
+ pri1=$(wg genkey)
+ pri2=$(wg genkey)
+
+ endpoint1=192.168.2.1
+ endpoint2=192.168.2.2
+ tunnel1=2001:db8:1::1
+ tunnel2=2001:db8:1::2
+
+ testnet=192.168.3.0/24
+ testlocal=192.168.3.1
+ testremote=192.168.3.2
+
+ epair=$(vnet_mkepair)
+
+ vnet_init
+
+ vnet_mkjail wgtest1 ${epair}a
+ vnet_mkjail wgtest2 ${epair}b
+
+ jexec wgtest1 ifconfig ${epair}a ${endpoint1}/24 up
+ jexec wgtest2 ifconfig ${epair}b ${endpoint2}/24 up
+
+ wg1=$(jexec wgtest1 ifconfig wg create)
+ echo "$pri1" | jexec wgtest1 wg set $wg1 listen-port 12345 \
+ private-key /dev/stdin
+ pub1=$(jexec wgtest1 wg show $wg1 public-key)
+ wg2=$(jexec wgtest2 ifconfig wg create)
+ echo "$pri2" | jexec wgtest2 wg set $wg2 listen-port 12345 \
+ private-key /dev/stdin
+ pub2=$(jexec wgtest2 wg show $wg2 public-key)
+
+ atf_check -s exit:0 -o ignore \
+ jexec wgtest1 wg set $wg1 peer "$pub2" \
+ endpoint ${endpoint2}:12345 allowed-ips ${tunnel2}/128,${testnet}
+ atf_check -s exit:0 \
+ jexec wgtest1 ifconfig $wg1 inet6 ${tunnel1}/64 up
+
+ atf_check -s exit:0 -o ignore \
+ jexec wgtest2 wg set $wg2 peer "$pub1" \
+ endpoint ${endpoint1}:12345 allowed-ips ${tunnel1}/128,${testnet}
+ atf_check -s exit:0 \
+ jexec wgtest2 ifconfig $wg2 inet6 ${tunnel2}/64 up
+
+ atf_check -s exit:0 jexec wgtest1 ifconfig $wg1 inet ${testlocal}/32
+ atf_check -s exit:0 jexec wgtest2 ifconfig $wg2 inet ${testremote}/32
+
+ # Generous timeout since the handshake takes some time.
+ atf_check -s exit:0 -o ignore jexec wgtest1 ping -c 1 -t 5 "$tunnel2"
+
+ # Setup our IPv6 endpoint and routing
+ atf_check -s exit:0 -o ignore \
+ jexec wgtest1 route add -inet ${testnet} -inet6 "$tunnel2"
+ atf_check -s exit:0 -o ignore \
+ jexec wgtest2 route add -inet ${testnet} -inet6 "$tunnel1"
+ # Now ping an address on the other side
+ atf_check -s exit:0 -o ignore jexec wgtest1 ping -c 1 -t 3 ${testremote}
+}
+
+wg_basic_crossaf_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "wg_basic_netmap" "cleanup"
+wg_basic_netmap_head()
+{
+ atf_set descr 'Create a wg(4) tunnel over an epair and pass traffic between jails with netmap'
+ atf_set require.user root
+ atf_set require.kmods if_wg netmap
+}
+
+wg_basic_netmap_body()
+{
+ local epair pri1 pri2 pub1 pub2 wg1 wg2
+ local endpoint1 endpoint2 tunnel1 tunnel2 tunnel3 tunnel4
+ local pid status
+
+ pri1=$(wg genkey)
+ pri2=$(wg genkey)
+
+ endpoint1=192.168.2.1
+ endpoint2=192.168.2.2
+ tunnel1=192.168.3.1
+ tunnel2=192.168.3.2
+ tunnel3=192.168.3.3
+ tunnel4=192.168.3.4
+
+ epair=$(vnet_mkepair)
+
+ vnet_init
+
+ vnet_mkjail wgtest1 ${epair}a
+ vnet_mkjail wgtest2 ${epair}b
+
+ jexec wgtest1 ifconfig ${epair}a ${endpoint1}/24 up
+ jexec wgtest2 ifconfig ${epair}b ${endpoint2}/24 up
+
+ wg1=$(jexec wgtest1 ifconfig wg create)
+ echo "$pri1" | jexec wgtest1 wg set $wg1 listen-port 12345 \
+ private-key /dev/stdin
+ pub1=$(jexec wgtest1 wg show $wg1 public-key)
+ wg2=$(jexec wgtest2 ifconfig wg create)
+ echo "$pri2" | jexec wgtest2 wg set $wg2 listen-port 12345 \
+ private-key /dev/stdin
+ pub2=$(jexec wgtest2 wg show $wg2 public-key)
+
+ atf_check -s exit:0 -o ignore \
+ jexec wgtest1 wg set $wg1 peer "$pub2" \
+ endpoint ${endpoint2}:12345 allowed-ips ${tunnel2}/32,${tunnel4}/32
+ atf_check -s exit:0 \
+ jexec wgtest1 ifconfig $wg1 inet ${tunnel1}/24 up
+
+ atf_check -s exit:0 -o ignore \
+ jexec wgtest2 wg set $wg2 peer "$pub1" \
+ endpoint ${endpoint1}:12345 allowed-ips ${tunnel1}/32,${tunnel3}/32
+ atf_check -s exit:0 \
+ jexec wgtest2 ifconfig $wg2 inet ${tunnel2}/24 up
+
+ atf_check -s exit:0 -o ignore \
+ jexec wgtest1 sysctl net.inet.ip.forwarding=1
+ atf_check -s exit:0 -o ignore \
+ jexec wgtest2 sysctl net.inet.ip.forwarding=1
+
+ jexec wgtest1 $(atf_get_srcdir)/bridge -w 0 -i netmap:wg0 -i netmap:wg0^ &
+ pid=$!
+
+ # Generous timeout since the handshake takes some time.
+ atf_check -s exit:0 -o ignore jexec wgtest1 ping -c 1 -t 5 $tunnel2
+ atf_check -s exit:0 -o ignore jexec wgtest2 ping -c 1 $tunnel1
+
+ # Verify that we cannot ping non-existent tunnel addresses. In general
+ # the remote side should respond with an ICMP message.
+ atf_check -s exit:2 -o ignore jexec wgtest1 ping -c 1 -t 2 $tunnel4
+ atf_check -s exit:2 -o ignore jexec wgtest2 ping -c 1 -t 2 $tunnel3
+
+ # Make sure that the bridge is still functional.
+ atf_check -s exit:0 -o ignore jexec wgtest1 ping -c 1 $tunnel2
+ atf_check -s exit:0 -o ignore jexec wgtest2 ping -c 1 $tunnel1
+
+ atf_check -s exit:0 kill -TERM $pid
+ wait $pid
+ status=$?
+
+ # Make sure that SIGTERM was received and handled.
+ atf_check_equal $status 143
+}
+
+wg_basic_netmap_cleanup()
+{
+ vnet_cleanup
+}
+
+# The kernel is expected to silently ignore any attempt to add a peer with a
+# public key identical to the host's.
+atf_test_case "wg_key_peerdev_shared" "cleanup"
+wg_key_peerdev_shared_head()
+{
+ atf_set descr 'Create a wg(4) interface with a shared pubkey between device and a peer'
+ atf_set require.user root
+ atf_set require.kmods if_wg
+}
+
+wg_key_peerdev_shared_body()
+{
+ local epair pri1 pub1 wg1
+ local endpoint1 tunnel1
+
+ pri1=$(wg genkey)
+
+ endpoint1=192.168.2.1
+ tunnel1=169.254.0.1
+
+ vnet_mkjail wgtest1
+
+ wg1=$(jexec wgtest1 ifconfig wg create)
+ echo "$pri1" | jexec wgtest1 wg set $wg1 listen-port 12345 \
+ private-key /dev/stdin
+ pub1=$(jexec wgtest1 wg show $wg1 public-key)
+
+ atf_check -s exit:0 \
+ jexec wgtest1 wg set ${wg1} peer "${pub1}" \
+ allowed-ips "${tunnel1}/32"
+
+ atf_check -o empty jexec wgtest1 wg show ${wg1} peers
+}
+
+wg_key_peerdev_shared_cleanup()
+{
+ vnet_cleanup
+}
+
+# When a wg(8) interface has a private key reassigned that corresponds to the
+# public key already on a peer, the kernel is expected to deconfigure the peer
+# to resolve the conflict.
+atf_test_case "wg_key_peerdev_makeshared" "cleanup"
+wg_key_peerdev_makeshared_head()
+{
+ atf_set descr 'Create a wg(4) interface and assign peer key to device'
+ atf_set require.progs wg
+}
+
+wg_key_peerdev_makeshared_body()
+{
+ local epair pri1 pub1 pri2 wg1 wg2
+ local endpoint1 tunnel1
+
+ pri1=$(wg genkey)
+ pri2=$(wg genkey)
+
+ endpoint1=192.168.2.1
+ tunnel1=169.254.0.1
+
+ vnet_mkjail wgtest1
+
+ wg1=$(jexec wgtest1 ifconfig wg create)
+ echo "$pri1" | jexec wgtest1 wg set $wg1 listen-port 12345 \
+ private-key /dev/stdin
+ pub1=$(jexec wgtest1 wg show $wg1 public-key)
+ wg2=$(jexec wgtest1 ifconfig wg create)
+ echo "$pri2" | jexec wgtest1 wg set $wg2 listen-port 12345 \
+ private-key /dev/stdin
+
+ atf_check -s exit:0 -o ignore \
+ jexec wgtest1 wg set ${wg2} peer "${pub1}" \
+ allowed-ips "${tunnel1}/32"
+
+ atf_check -o not-empty jexec wgtest1 wg show ${wg2} peers
+
+ jexec wgtest1 sh -c "echo '${pri1}' > pri1"
+
+ atf_check -s exit:0 \
+ jexec wgtest1 wg set ${wg2} private-key pri1
+
+ atf_check -o empty jexec wgtest1 wg show ${wg2} peers
+}
+
+wg_key_peerdev_makeshared_cleanup()
+{
+ vnet_cleanup
+}
+
+# The kernel is expected to create the wg socket in the jail context that the
+# wg interface was created in, even if the interface is moved to a different
+# vnet.
+atf_test_case "wg_vnet_parent_routing" "cleanup"
+wg_vnet_parent_routing_head()
+{
+ atf_set descr 'Create a wg(4) tunnel without epairs and pass traffic between jails'
+ atf_set require.user root
+ atf_set require.kmods if_wg
+}
+
+wg_vnet_parent_routing_body()
+{
+ local pri1 pri2 pub1 pub2 wg1 wg2
+ local tunnel1 tunnel2
+
+ pri1=$(wg genkey)
+ pri2=$(wg genkey)
+
+ tunnel1=169.254.0.1
+ tunnel2=169.254.0.2
+
+ vnet_init
+
+ wg1=$(ifconfig wg create)
+ wg2=$(ifconfig wg create)
+
+ vnet_mkjail wgtest1 ${wg1}
+ vnet_mkjail wgtest2 ${wg2}
+
+ echo "$pri1" | jexec wgtest1 wg set $wg1 listen-port 12345 \
+ private-key /dev/stdin
+ pub1=$(jexec wgtest1 wg show $wg1 public-key)
+ echo "$pri2" | jexec wgtest2 wg set $wg2 listen-port 12346 \
+ private-key /dev/stdin
+ pub2=$(jexec wgtest2 wg show $wg2 public-key)
+
+ atf_check -s exit:0 -o ignore \
+ jexec wgtest1 wg set $wg1 peer "$pub2" \
+ endpoint 127.0.0.1:12346 allowed-ips ${tunnel2}/32
+ atf_check -s exit:0 \
+ jexec wgtest1 ifconfig $wg1 inet ${tunnel1}/24 up
+
+ atf_check -s exit:0 -o ignore \
+ jexec wgtest2 wg set $wg2 peer "$pub1" \
+ endpoint 127.0.0.1:12345 allowed-ips ${tunnel1}/32
+ atf_check -s exit:0 \
+ jexec wgtest2 ifconfig $wg2 inet ${tunnel2}/24 up
+
+ # Sanity check ICMP counters; should clearly be nothing on these new
+ # jails. We'll check them as we go to ensure that the ICMP packets
+ # generated really are being handled by the jails' vnets.
+ atf_check -o not-match:"histogram" jexec wgtest1 netstat -s -p icmp
+ atf_check -o not-match:"histogram" jexec wgtest2 netstat -s -p icmp
+
+ # Generous timeout since the handshake takes some time.
+ atf_check -s exit:0 -o ignore jexec wgtest1 ping -c 1 -t 5 $tunnel2
+ atf_check -o match:"echo reply: 1" jexec wgtest1 netstat -s -p icmp
+ atf_check -o match:"echo: 1" jexec wgtest2 netstat -s -p icmp
+
+ atf_check -s exit:0 -o ignore jexec wgtest2 ping -c 1 $tunnel1
+ atf_check -o match:"echo reply: 1" jexec wgtest2 netstat -s -p icmp
+ atf_check -o match:"echo: 1" jexec wgtest1 netstat -s -p icmp
+}
+
+wg_vnet_parent_routing_cleanup()
+{
+ vnet_cleanup
+}
+
+# The kernel should now allow removing a single allowed-ip without having to
+# replace the whole list. We can't really test the atomicity of it all that
+# easily, but we'll trust that it worked right if just that addr/mask is gone.
+atf_test_case "wg_allowedip_incremental" "cleanup"
+wg_allowedip_incremental_head()
+{
+ atf_set descr "Add/remove allowed-ips from a peer with the +/- incremental syntax"
+ atf_set require.user root
+}
+
+wg_allowedip_incremental_body()
+{
+ local pri1 pri2 pub1 pub2 wg1
+ local tunnel1 tunnel2 tunnel3
+
+ kldload -n if_wg || atf_skip "This test requires if_wg and could not load it"
+
+ pri1=$(wg genkey)
+ pri2=$(wg genkey)
+ pub2=$(echo "$pri2" | wg pubkey)
+
+ tunnel1=169.254.0.1
+ tunnel2=169.254.0.2
+ tunnel3=169.254.0.3
+
+ vnet_mkjail wgtest1
+
+ wg1=$(jexec wgtest1 ifconfig wg create)
+ echo "$pri1" | jexec wgtest1 wg set $wg1 private-key /dev/stdin
+ pub1=$(jexec wgtest1 wg show $wg1 public-key)
+
+ atf_check -s exit:0 \
+ jexec wgtest1 wg set $wg1 peer $pub2 \
+ allowed-ips "${tunnel1}/32,${tunnel2}/32"
+
+ atf_check -o save:wg.allowed jexec wgtest1 wg show $wg1 allowed-ips
+ atf_check grep -q "${tunnel1}/32" wg.allowed
+ atf_check grep -q "${tunnel2}/32" wg.allowed
+
+ atf_check -s exit:0 \
+ jexec wgtest1 wg set $wg1 peer $pub2 \
+ allowed-ips "-${tunnel2}/32"
+
+ atf_check -o save:wg-2.allowed jexec wgtest1 wg show $wg1 allowed-ips
+ atf_check grep -q "${tunnel1}/32" wg-2.allowed
+ atf_check -s not-exit:0 grep -q "${tunnel2}/32" wg-2.allowed
+
+ atf_check -s exit:0 \
+ jexec wgtest1 wg set $wg1 peer $pub2 \
+ allowed-ips "+${tunnel2}/32"
+
+ atf_check -o save:wg-3.allowed jexec wgtest1 wg show $wg1 allowed-ips
+ atf_check grep -q "${tunnel1}/32" wg-3.allowed
+ atf_check grep -q "${tunnel2}/32" wg-3.allowed
+
+ # Now attempt to add the address yet again to confirm that it's not
+ # harmful.
+ atf_check -s exit:0 \
+ jexec wgtest1 wg set $wg1 peer $pub2 \
+ allowed-ips "+${tunnel2}/32"
+
+ atf_check -o save:wg-4.allowed -x \
+ "jexec wgtest1 wg show $wg1 allowed-ips | cut -f2 | tr ' ' '\n'"
+ atf_check -o match:"2 wg-4.allowed$" wc -l wg-4.allowed
+
+ # Finally, let's try removing an address that we never had at all and
+ # confirm that we still have our two addresses.
+ atf_check -s exit:0 \
+ jexec wgtest1 wg set $wg1 peer $pub2 \
+ allowed-ips "-${tunnel3}/32"
+
+ atf_check -o save:wg-5.allowed -x \
+ "jexec wgtest1 wg show $wg1 allowed-ips | cut -f2 | tr ' ' '\n'"
+ atf_check cmp -s wg-4.allowed wg-5.allowed
+}
+
+wg_allowedip_incremental_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "wg_allowedip_incremental_inet6" "cleanup"
+wg_allowedip_incremental_inet6_head()
+{
+ atf_set descr "Add/remove IPv6 allowed-ips from a peer with the +/- incremental syntax"
+ atf_set require.user root
+}
+
+wg_allowedip_incremental_inet6_body()
+{
+ local pri1 pri2 pub1 pub2 wg1
+ local tunnel1 tunnel2
+
+ kldload -n if_wg || atf_skip "This test requires if_wg and could not load it"
+
+ pri1=$(wg genkey)
+ pri2=$(wg genkey)
+ pub2=$(echo "$pri2" | wg pubkey)
+
+ tunnel1=2001:db8:1::1
+ tunnel2=2001:db8:1::2
+
+ vnet_mkjail wgtest1
+
+ wg1=$(jexec wgtest1 ifconfig wg create)
+ echo "$pri1" | jexec wgtest1 wg set $wg1 private-key /dev/stdin
+ pub1=$(jexec wgtest1 wg show $wg1 public-key)
+
+ atf_check -s exit:0 \
+ jexec wgtest1 wg set $wg1 peer $pub2 \
+ allowed-ips "${tunnel1}/128"
+ atf_check -o save:wg.allowed jexec wgtest1 wg show $wg1 allowed-ips
+ atf_check grep -q "${tunnel1}/128" wg.allowed
+
+ atf_check -s exit:0 \
+ jexec wgtest1 wg set $wg1 peer $pub2 \
+ allowed-ips "+${tunnel2}/128"
+ atf_check -o save:wg-2.allowed jexec wgtest1 wg show $wg1 allowed-ips
+ atf_check grep -q "${tunnel1}/128" wg-2.allowed
+ atf_check grep -q "${tunnel2}/128" wg-2.allowed
+
+ atf_check -s exit:0 \
+ jexec wgtest1 wg set $wg1 peer $pub2 \
+ allowed-ips "-${tunnel1}/128"
+ atf_check -o save:wg-3.allowed jexec wgtest1 wg show $wg1 allowed-ips
+ atf_check -s not-exit:0 grep -q "${tunnel1}/128" wg-3.allowed
+ atf_check grep -q "${tunnel2}/128" wg-3.allowed
+}
+
+wg_allowedip_incremental_inet6_cleanup()
+{
+ vnet_cleanup
+}
+
+
+atf_test_case "wg_allowedip_incremental_stealing" "cleanup"
+wg_allowedip_incremental_stealing_head()
+{
+ atf_set descr "Add/remove allowed-ips from a peer with the +/- incremental syntax to steal"
+ atf_set require.user root
+}
+
+wg_allowedip_incremental_stealing_body()
+{
+ local pri1 pri2 pri3 pub1 pub2 pub3 wg1
+ local regex2 regex3
+ local tunnel1 tunnel2
+
+ kldload -n if_wg || atf_skip "This test requires if_wg and could not load it"
+
+ pri1=$(wg genkey)
+ pri2=$(wg genkey)
+ pri3=$(wg genkey)
+ pub2=$(echo "$pri2" | wg pubkey)
+ pub3=$(echo "$pri3" | wg pubkey)
+
+ regex2=$(echo "$pub2" | sed -e 's/[+]/[+]/g')
+ regex3=$(echo "$pub3" | sed -e 's/[+]/[+]/g')
+
+ tunnel1=169.254.0.1
+ tunnel2=169.254.0.2
+ tunnel3=169.254.0.3
+
+ vnet_mkjail wgtest1
+
+ wg1=$(jexec wgtest1 ifconfig wg create)
+ echo "$pri1" | jexec wgtest1 wg set $wg1 private-key /dev/stdin
+ pub1=$(jexec wgtest1 wg show $wg1 public-key)
+
+ atf_check -s exit:0 \
+ jexec wgtest1 wg set $wg1 peer $pub2 \
+ allowed-ips "${tunnel1}/32,${tunnel2}/32"
+
+ atf_check -s exit:0 \
+ jexec wgtest1 wg set $wg1 peer $pub3 \
+ allowed-ips "${tunnel3}/32"
+
+ # First, confirm that the negative syntax doesn't do anything because
+ # we have the wrong peer.
+ atf_check -s exit:0 \
+ jexec wgtest1 wg set $wg1 peer $pub2 \
+ allowed-ips "-${tunnel3}/32"
+
+ atf_check -o save:wg.allowed jexec wgtest1 wg show $wg1 allowed-ips
+ atf_check grep -Eq "^${regex3}.+${tunnel3}/32" wg.allowed
+
+ # Next, steal it with an incremental move and check that it moved.
+ atf_check -s exit:0 \
+ jexec wgtest1 wg set $wg1 peer $pub2 \
+ allowed-ips "+${tunnel3}/32"
+
+ atf_check -o save:wg-2.allowed jexec wgtest1 wg show $wg1 allowed-ips
+
+ atf_check grep -Eq "^${regex2}.+${tunnel3}/32" wg-2.allowed
+ atf_check grep -Evq "^${regex3}.+${tunnel3}/32" wg-2.allowed
+}
+
+wg_allowedip_incremental_stealing_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "wg_basic"
+ atf_add_test_case "wg_basic_crossaf"
+ atf_add_test_case "wg_basic_netmap"
+ atf_add_test_case "wg_key_peerdev_shared"
+ atf_add_test_case "wg_key_peerdev_makeshared"
+ atf_add_test_case "wg_vnet_parent_routing"
+ atf_add_test_case "wg_allowedip_incremental"
+ atf_add_test_case "wg_allowedip_incremental_inet6"
+ atf_add_test_case "wg_allowedip_incremental_stealing"
+}
diff --git a/tests/sys/net/pcp.py b/tests/sys/net/pcp.py
new file mode 100644
index 000000000000..c0b6d4efc3b0
--- /dev/null
+++ b/tests/sys/net/pcp.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python3
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2021 Rubicon Communications, LLC (Netgate).
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import sys
+import os
+curdir = os.path.dirname(os.path.realpath(__file__))
+netpfil_common = curdir + "/../netpfil/common"
+sys.path.append(netpfil_common)
+from sniffer import Sniffer
+
+def check_pcp(args, packet):
+ vlan = packet.getlayer(sp.Dot1Q)
+
+ if vlan is None:
+ return False
+
+ if not packet.getlayer(sp.BOOTP):
+ return False
+
+ if vlan.prio == int(args.expect_pcp[0]):
+ return True
+
+ return False
+
+def main():
+ parser = argparse.ArgumentParser("pcp.py",
+ description="PCP test tool")
+ parser.add_argument('--recvif', nargs=1,
+ required=True,
+ help='The interface where to look for packets to check')
+ parser.add_argument('--expect-pcp', nargs=1,
+ help='The expected PCP value on VLAN packets')
+
+ args = parser.parse_args()
+
+ sniffer = Sniffer(args, check_pcp, args.recvif[0], timeout=20)
+
+ sniffer.join()
+
+ if sniffer.correctPackets:
+ sys.exit(0)
+
+ sys.exit(1)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/net/randsleep.c b/tests/sys/net/randsleep.c
new file mode 100644
index 000000000000..55f1fff45617
--- /dev/null
+++ b/tests/sys/net/randsleep.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2014 Spectra Logic Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * Authors: Alan Somers (Spectra Logic Corporation)
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define RANDOM_MAX ((1U<<31) - 1)
+
+int main(int argc, char** argv){
+ useconds_t max_usecs, usecs;
+ double frac;
+
+ if (argc != 2) {
+ printf("Usage: randsleep <max_microseconds>\n");
+ exit(2);
+ }
+
+ errno = 0;
+ max_usecs = (useconds_t)strtol(argv[1], NULL, 0);
+ if (errno != 0) {
+ perror("strtol");
+ exit(1);
+ }
+ srandomdev();
+ frac = (double)random() / (double)RANDOM_MAX;
+ usecs = (useconds_t)((double)max_usecs * frac);
+ usleep(usecs);
+
+ return (0);
+}
diff --git a/tests/sys/net/routing/Makefile b/tests/sys/net/routing/Makefile
new file mode 100644
index 000000000000..c725d23f15d1
--- /dev/null
+++ b/tests/sys/net/routing/Makefile
@@ -0,0 +1,21 @@
+PACKAGE= tests
+WARNS?= 1
+
+TESTSDIR= ${TESTSBASE}/sys/net/routing
+
+ATF_TESTS_C += test_rtsock_l3
+ATF_TESTS_C += test_rtsock_lladdr
+ATF_TESTS_C += test_rtsock_ops
+ATF_TESTS_PYTEST += test_routing_l3.py
+ATF_TESTS_PYTEST += test_rtsock_multipath.py
+
+${PACKAGE}FILES+= generic_cleanup.sh
+${PACKAGE}FILESMODE_generic_cleanup.sh=0555
+
+# Most of the tests operates on a common IPv4/IPv6 prefix,
+# so running them in parallel will lead to weird results.
+TEST_METADATA+= is_exclusive=true
+
+CFLAGS+= -I${.CURDIR:H:H:H}
+
+.include <bsd.test.mk>
diff --git a/tests/sys/net/routing/generic_cleanup.sh b/tests/sys/net/routing/generic_cleanup.sh
new file mode 100755
index 000000000000..4c9ded969d1f
--- /dev/null
+++ b/tests/sys/net/routing/generic_cleanup.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Alexander V. Chernikov
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+
+srcdir=`dirname $0`
+. ${srcdir}/../../common/vnet.subr
+
+vnet_cleanup
+
diff --git a/tests/sys/net/routing/params.h b/tests/sys/net/routing/params.h
new file mode 100644
index 000000000000..f66678bceca9
--- /dev/null
+++ b/tests/sys/net/routing/params.h
@@ -0,0 +1,36 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Alexander V. Chernikov
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _NET_ROUTING_PARAMS_H_
+#define _NET_ROUTING_PARAMS_H_
+
+/* files to store state */
+#define JAILS_FNAME "created_jails.lst"
+#define IFACES_FNAME "created_interfaces.lst"
+
+#endif
+
diff --git a/tests/sys/net/routing/rtsock_common.h b/tests/sys/net/routing/rtsock_common.h
new file mode 100644
index 000000000000..eed4a348ad74
--- /dev/null
+++ b/tests/sys/net/routing/rtsock_common.h
@@ -0,0 +1,882 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Alexander V. Chernikov
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _NET_ROUTING_RTSOCK_COMMON_H_
+#define _NET_ROUTING_RTSOCK_COMMON_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <ctype.h>
+#include <poll.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/jail.h>
+#include <sys/linker.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+
+#include <arpa/inet.h>
+#include <net/ethernet.h>
+
+#include <netinet/in.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/nd6.h>
+
+#include <ifaddrs.h>
+
+#include <errno.h>
+#include <err.h>
+#include <sysexits.h>
+
+#include <atf-c.h>
+#include "freebsd_test_suite/macros.h"
+
+#include "rtsock_print.h"
+#include "params.h"
+
+void rtsock_update_rtm_len(struct rt_msghdr *rtm);
+void rtsock_validate_message(char *buffer, ssize_t len);
+void rtsock_add_rtm_sa(struct rt_msghdr *rtm, int addr_type, struct sockaddr *sa);
+
+void file_append_line(char *fname, char *text);
+
+static int _rtm_seq = 42;
+
+
+/*
+ * Checks if the interface cloner module is present for @name.
+ */
+static int
+_check_cloner(char *name)
+{
+ struct if_clonereq ifcr;
+ char *cp, *buf;
+ int idx;
+ int s;
+ int found = 0;
+
+ s = socket(AF_LOCAL, SOCK_DGRAM, 0);
+ if (s == -1)
+ err(1, "socket(AF_LOCAL,SOCK_DGRAM)");
+
+ memset(&ifcr, 0, sizeof(ifcr));
+
+ if (ioctl(s, SIOCIFGCLONERS, &ifcr) < 0)
+ err(1, "SIOCIFGCLONERS for count");
+
+ buf = malloc(ifcr.ifcr_total * IFNAMSIZ);
+ if (buf == NULL)
+ err(1, "unable to allocate cloner name buffer");
+
+ ifcr.ifcr_count = ifcr.ifcr_total;
+ ifcr.ifcr_buffer = buf;
+
+ if (ioctl(s, SIOCIFGCLONERS, &ifcr) < 0)
+ err(1, "SIOCIFGCLONERS for names");
+
+ /*
+ * In case some disappeared in the mean time, clamp it down.
+ */
+ if (ifcr.ifcr_count > ifcr.ifcr_total)
+ ifcr.ifcr_count = ifcr.ifcr_total;
+
+ for (cp = buf, idx = 0; idx < ifcr.ifcr_count; idx++, cp += IFNAMSIZ) {
+ if (!strcmp(cp, name)) {
+ found = 1;
+ break;
+ }
+ }
+
+ free(buf);
+ close(s);
+
+ return (found);
+}
+
+static char *
+iface_create(char *ifname_orig)
+{
+ struct ifreq ifr;
+ int s;
+ char prefix[IFNAMSIZ], ifname[IFNAMSIZ], *result;
+
+ char *src, *dst;
+ for (src = ifname_orig, dst = prefix; *src && isalpha(*src); src++)
+ *dst++ = *src;
+ *dst = '\0';
+
+ memset(&ifr, 0, sizeof(struct ifreq));
+
+ s = socket(AF_LOCAL, SOCK_DGRAM, 0);
+ strlcpy(ifr.ifr_name, ifname_orig, sizeof(ifr.ifr_name));
+
+ RLOG("creating iface %s %s", prefix, ifr.ifr_name);
+ if (ioctl(s, SIOCIFCREATE2, &ifr) < 0)
+ err(1, "SIOCIFCREATE2");
+
+ strlcpy(ifname, ifr.ifr_name, IFNAMSIZ);
+ RLOG("created interface %s", ifname);
+
+ result = strdup(ifname);
+
+ file_append_line(IFACES_FNAME, ifname);
+ if (strstr(ifname, "epair") == ifname) {
+ /* call returned epairXXXa, need to add epairXXXb */
+ ifname[strlen(ifname) - 1] = 'b';
+ file_append_line(IFACES_FNAME, ifname);
+ }
+
+ return (result);
+}
+
+static int
+iface_destroy(char *ifname)
+{
+ struct ifreq ifr;
+ int s;
+
+ s = socket(AF_LOCAL, SOCK_DGRAM, 0);
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+
+ RLOG("destroying interface %s", ifname);
+ if (ioctl(s, SIOCIFDESTROY, &ifr) < 0)
+ return (0);
+
+ return (1);
+}
+
+/*
+ * Open tunneling device such as tuntap and returns fd.
+ */
+int
+iface_open(char *ifname)
+{
+ char path[256];
+
+ snprintf(path, sizeof(path), "/dev/%s", ifname);
+
+ RLOG("opening interface %s", ifname);
+ int fd = open(path, O_RDWR|O_EXCL);
+ if (fd == -1) {
+ RLOG_ERRNO("unable to open interface %s", ifname);
+ return (-1);
+ }
+
+ return (fd);
+}
+
+/*
+ * Sets primary IPv4 addr.
+ * Returns 0 on success.
+ */
+static inline int
+iface_setup_addr(char *ifname, char *addr, int plen)
+{
+ char cmd[512];
+ char *af;
+
+ if (strchr(addr, ':'))
+ af = "inet6";
+ else
+ af = "inet";
+ RLOG("setting af_%s %s/%d on %s", af, addr, plen, ifname);
+ snprintf(cmd, sizeof(cmd), "/sbin/ifconfig %s %s %s/%d", ifname,
+ af, addr, plen);
+
+ return system(cmd);
+}
+
+/*
+ * Removes primary IPv4 prefix.
+ * Returns 0 on success.
+ */
+static inline int
+iface_delete_addr(char *ifname, char *addr)
+{
+ char cmd[512];
+
+ if (strchr(addr, ':')) {
+ RLOG("removing IPv6 %s from %s", addr, ifname);
+ snprintf(cmd, sizeof(cmd), "/sbin/ifconfig %s inet6 %s delete", ifname, addr);
+ } else {
+ RLOG("removing IPv4 %s from %s", addr, ifname);
+ snprintf(cmd, sizeof(cmd), "/sbin/ifconfig %s -alias %s", ifname, addr);
+ }
+
+ return system(cmd);
+}
+
+int
+iface_turn_up(char *ifname)
+{
+ struct ifreq ifr;
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ RLOG_ERRNO("socket");
+ return (-1);
+ }
+ memset(&ifr, 0, sizeof(struct ifreq));
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+ RLOG_ERRNO("ioctl(SIOCGIFFLAGS)");
+ return (-1);
+ }
+ /* Update flags */
+ if ((ifr.ifr_flags & IFF_UP) == 0) {
+ ifr.ifr_flags |= IFF_UP;
+ if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) {
+ RLOG_ERRNO("ioctl(SIOSGIFFLAGS)");
+ return (-1);
+ }
+ RLOG("turned interface %s up", ifname);
+ }
+
+ return (0);
+}
+
+/*
+ * Removes ND6_IFF_IFDISABLED from IPv6 interface flags.
+ * Returns 0 on success.
+ */
+int
+iface_enable_ipv6(char *ifname)
+{
+ struct in6_ndireq nd;
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ err(1, "socket");
+ }
+ memset(&nd, 0, sizeof(nd));
+ strlcpy(nd.ifname, ifname, sizeof(nd.ifname));
+ if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) {
+ RLOG_ERRNO("ioctl(SIOCGIFINFO_IN6)");
+ return (-1);
+ }
+ /* Update flags */
+ if ((nd.ndi.flags & ND6_IFF_IFDISABLED) != 0) {
+ nd.ndi.flags &= ~ND6_IFF_IFDISABLED;
+ if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&nd) < 0) {
+ RLOG_ERRNO("ioctl(SIOCSIFINFO_IN6)");
+ return (-1);
+ }
+ RLOG("enabled IPv6 for %s", ifname);
+ }
+
+ return (0);
+}
+
+void
+file_append_line(char *fname, char *text)
+{
+ FILE *f;
+
+ f = fopen(fname, "a");
+ fputs(text, f);
+ fputs("\n", f);
+ fclose(f);
+}
+
+static int
+vnet_wait_interface(char *vnet_name, char *ifname)
+{
+ char buf[512], cmd[512], *line, *token;
+ FILE *fp;
+ int i;
+
+ snprintf(cmd, sizeof(cmd), "/usr/sbin/jexec %s /sbin/ifconfig -l", vnet_name);
+ for (int i = 0; i < 50; i++) {
+ fp = popen(cmd, "r");
+ line = fgets(buf, sizeof(buf), fp);
+ /* cut last\n */
+ if (line[0])
+ line[strlen(line)-1] = '\0';
+ while ((token = strsep(&line, " ")) != NULL) {
+ if (strcmp(token, ifname) == 0)
+ return (1);
+ }
+
+ /* sleep 100ms */
+ usleep(1000 * 100);
+ }
+
+ return (0);
+}
+
+void
+vnet_switch(char *vnet_name, char **ifnames, int count)
+{
+ char buf[512], cmd[512], *line;
+ FILE *fp;
+ int jid, len, ret;
+
+ RLOG("switching to vnet %s with interface(s) %s", vnet_name, ifnames[0]);
+ len = snprintf(cmd, sizeof(cmd),
+ "/usr/sbin/jail -i -c name=%s persist vnet", vnet_name);
+ for (int i = 0; i < count && len < sizeof(cmd); i++) {
+ len += snprintf(&cmd[len], sizeof(cmd) - len,
+ " vnet.interface=%s", ifnames[i]);
+ }
+ RLOG("jail cmd: \"%s\"\n", cmd);
+
+ fp = popen(cmd, "r");
+ if (fp == NULL)
+ atf_tc_fail("jail creation failed");
+ line = fgets(buf, sizeof(buf), fp);
+ if (line == NULL)
+ atf_tc_fail("empty output from jail(8)");
+ jid = strtol(line, NULL, 10);
+ if (jid <= 0) {
+ atf_tc_fail("invalid jail output: %s", line);
+ }
+
+ RLOG("created jail jid=%d", jid);
+ file_append_line(JAILS_FNAME, vnet_name);
+
+ /* Wait while interface appearsh inside vnet */
+ for (int i = 0; i < count; i++) {
+ if (vnet_wait_interface(vnet_name, ifnames[i]))
+ continue;
+ atf_tc_fail("unable to move interface %s to jail %s",
+ ifnames[i], vnet_name);
+ }
+
+ if (jail_attach(jid) == -1) {
+ RLOG_ERRNO("jail %s attach failed: ret=%d", vnet_name, errno);
+ atf_tc_fail("jail attach failed");
+ }
+
+ RLOG("attached to the jail");
+}
+
+void
+vnet_switch_one(char *vnet_name, char *ifname)
+{
+ char *ifnames[1];
+
+ ifnames[0] = ifname;
+ vnet_switch(vnet_name, ifnames, 1);
+}
+
+
+#define SA_F_IGNORE_IFNAME 0x01
+#define SA_F_IGNORE_IFTYPE 0x02
+#define SA_F_IGNORE_MEMCMP 0x04
+int
+sa_equal_msg_flags(const struct sockaddr *a, const struct sockaddr *b, char *msg, size_t sz, int flags)
+{
+ char a_s[64], b_s[64];
+ const struct sockaddr_in *a4, *b4;
+ const struct sockaddr_in6 *a6, *b6;
+ const struct sockaddr_dl *al, *bl;
+
+ if (a == NULL) {
+ snprintf(msg, sz, "first sa is NULL");
+ return 0;
+ }
+ if (b == NULL) {
+ snprintf(msg, sz, "second sa is NULL");
+ return 0;
+ }
+
+ if (a->sa_family != b->sa_family) {
+ snprintf(msg, sz, "family: %d vs %d", a->sa_family, b->sa_family);
+ return 0;
+ }
+ if (a->sa_len != b->sa_len) {
+ snprintf(msg, sz, "len: %d vs %d", a->sa_len, b->sa_len);
+ return 0;
+ }
+
+ switch (a->sa_family) {
+ case AF_INET:
+ a4 = (const struct sockaddr_in *)a;
+ b4 = (const struct sockaddr_in *)b;
+ if (a4->sin_addr.s_addr != b4->sin_addr.s_addr) {
+ inet_ntop(AF_INET, &a4->sin_addr, a_s, sizeof(a_s));
+ inet_ntop(AF_INET, &b4->sin_addr, b_s, sizeof(b_s));
+ snprintf(msg, sz, "addr diff: %s vs %s", a_s, b_s);
+ return 0;
+ }
+ if (a4->sin_port != b4->sin_port) {
+ snprintf(msg, sz, "port diff: %d vs %d",
+ ntohs(a4->sin_port), ntohs(b4->sin_port));
+ //return 0;
+ }
+ const uint32_t *a32, *b32;
+ a32 = (const uint32_t *)a4->sin_zero;
+ b32 = (const uint32_t *)b4->sin_zero;
+ if ((*a32 != *b32) || (*(a32 + 1) != *(b32 + 1))) {
+ snprintf(msg, sz, "zero diff: 0x%08X%08X vs 0x%08X%08X",
+ ntohl(*a32), ntohl(*(a32 + 1)),
+ ntohl(*b32), ntohl(*(b32 + 1)));
+ return 0;
+ }
+ return 1;
+ case AF_INET6:
+ a6 = (const struct sockaddr_in6 *)a;
+ b6 = (const struct sockaddr_in6 *)b;
+ if (!IN6_ARE_ADDR_EQUAL(&a6->sin6_addr, &b6->sin6_addr)) {
+ inet_ntop(AF_INET6, &a6->sin6_addr, a_s, sizeof(a_s));
+ inet_ntop(AF_INET6, &b6->sin6_addr, b_s, sizeof(b_s));
+ snprintf(msg, sz, "addr diff: %s vs %s", a_s, b_s);
+ return 0;
+ }
+ if (a6->sin6_scope_id != b6->sin6_scope_id) {
+ snprintf(msg, sz, "scope diff: %u vs %u", a6->sin6_scope_id, b6->sin6_scope_id);
+ return 0;
+ }
+ break;
+ case AF_LINK:
+ al = (const struct sockaddr_dl *)a;
+ bl = (const struct sockaddr_dl *)b;
+
+ if (al->sdl_index != bl->sdl_index) {
+ snprintf(msg, sz, "sdl_index diff: %u vs %u", al->sdl_index, bl->sdl_index);
+ return 0;
+ }
+
+ if ((al->sdl_alen != bl->sdl_alen) || (memcmp(LLADDR(al), LLADDR(bl), al->sdl_alen) != 0)) {
+ char abuf[64], bbuf[64];
+ sa_print_hd(abuf, sizeof(abuf), LLADDR(al), al->sdl_alen);
+ sa_print_hd(bbuf, sizeof(bbuf), LLADDR(bl), bl->sdl_alen);
+ snprintf(msg, sz, "sdl_alen diff: {%s} (%d) vs {%s} (%d)",
+ abuf, al->sdl_alen, bbuf, bl->sdl_alen);
+ return 0;
+ }
+
+ if (((flags & SA_F_IGNORE_IFTYPE) == 0) && (al->sdl_type != bl->sdl_type)) {
+ snprintf(msg, sz, "sdl_type diff: %u vs %u", al->sdl_type, bl->sdl_type);
+ return 0;
+ }
+
+ if (((flags & SA_F_IGNORE_IFNAME) == 0) && ((al->sdl_nlen != bl->sdl_nlen) ||
+ (memcmp(al->sdl_data, bl->sdl_data, al->sdl_nlen) != 0))) {
+ char abuf[64], bbuf[64];
+ memcpy(abuf, al->sdl_data, al->sdl_nlen);
+ abuf[al->sdl_nlen] = '\0';
+ memcpy(bbuf, bl->sdl_data, bl->sdl_nlen);
+ abuf[bl->sdl_nlen] = '\0';
+ snprintf(msg, sz, "sdl_nlen diff: {%s} (%d) vs {%s} (%d)",
+ abuf, al->sdl_nlen, bbuf, bl->sdl_nlen);
+ return 0;
+ }
+
+ if (flags & SA_F_IGNORE_MEMCMP)
+ return 1;
+ break;
+ }
+
+ if (memcmp(a, b, a->sa_len)) {
+ int i;
+ for (i = 0; i < a->sa_len; i++)
+ if (((const char *)a)[i] != ((const char *)b)[i])
+ break;
+
+ sa_print(a, 1);
+ sa_print(b, 1);
+
+ snprintf(msg, sz, "overall memcmp() reports diff for af %d offset %d",
+ a->sa_family, i);
+ return 0;
+ }
+ return 1;
+}
+
+int
+sa_equal_msg(const struct sockaddr *a, const struct sockaddr *b, char *msg, size_t sz)
+{
+
+ return sa_equal_msg_flags(a, b, msg, sz, 0);
+}
+
+void
+sa_fill_mask4(struct sockaddr_in *sin, int plen)
+{
+
+ memset(sin, 0, sizeof(struct sockaddr_in));
+ sin->sin_family = AF_INET;
+ sin->sin_len = sizeof(struct sockaddr_in);
+ sin->sin_addr.s_addr = htonl(plen ? ~((1 << (32 - plen)) - 1) : 0);
+}
+
+void
+sa_fill_mask6(struct sockaddr_in6 *sin6, uint8_t mask)
+{
+ uint32_t *cp;
+
+ memset(sin6, 0, sizeof(struct sockaddr_in6));
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+
+ for (cp = (uint32_t *)&sin6->sin6_addr; mask >= 32; mask -= 32)
+ *cp++ = 0xFFFFFFFF;
+ if (mask > 0)
+ *cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0);
+}
+
+/* 52:54:00:14:e3:10 */
+#define ETHER_MAC_MAX_LENGTH 17
+
+int
+sa_convert_str_to_sa(const char *_addr, struct sockaddr *sa)
+{
+ int error;
+
+ int af = AF_UNSPEC;
+
+ char *addr = strdup(_addr);
+ int retcode = 0;
+
+ /* classify AF by str */
+ if (strchr(addr, ':')) {
+ /* inet6 or ether */
+ char *k;
+ int delim_cnt = 0;
+ for (k = addr; *k; k++)
+ if (*k == ':')
+ delim_cnt++;
+ af = AF_INET6;
+
+ if (delim_cnt == 5) {
+ k = strchr(addr, '%');
+ if (k != NULL && (k - addr) <= ETHER_MAC_MAX_LENGTH)
+ af = AF_LINK;
+ }
+ } else if (strchr(addr, '.'))
+ af = AF_INET;
+
+ /* */
+ char *delimiter;
+ int ifindex = 0;
+ char *ifname = NULL;
+ if ((delimiter = strchr(addr, '%')) != NULL) {
+ *delimiter = '\0';
+ ifname = delimiter + 1;
+ ifindex = if_nametoindex(ifname);
+ if (ifindex == 0)
+ RLOG("unable to find ifindex for '%s'", ifname);
+ else
+ RLOG("if %s mapped to %d", ifname, ifindex);
+ }
+
+ if (af == AF_INET6) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+ memset(sin6, 0, sizeof(struct sockaddr_in6));
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+ sin6->sin6_scope_id = ifindex;
+ error = inet_pton(AF_INET6, addr, &sin6->sin6_addr);
+ if (error != 1)
+ RLOG_ERRNO("inet_ntop() failed: ret=%d", error);
+ else
+ retcode = 1;
+ } else if (af == AF_INET) {
+ struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+ memset(sin, 0, sizeof(struct sockaddr_in));
+ sin->sin_family = AF_INET;
+ sin->sin_len = sizeof(struct sockaddr_in);
+ error = inet_pton(AF_INET, addr, &sin->sin_addr);
+ if (error != 1)
+ RLOG("inet_ntop() failed: ret=%d", error);
+ else
+ retcode = 1;
+ } else if (af == AF_LINK) {
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;
+ memset(sdl, 0, sizeof(struct sockaddr_dl));
+ sdl->sdl_family = AF_LINK;
+ sdl->sdl_len = sizeof(struct sockaddr_dl);
+ sdl->sdl_index = ifindex;
+ sdl->sdl_alen = 6;
+ struct ether_addr *ea = (struct ether_addr *)LLADDR(sdl);
+ if (ether_aton_r(addr, ea) == NULL)
+ RLOG("ether_aton() failed");
+ else
+ retcode = 1;
+ }
+
+ return (retcode);
+}
+
+
+int
+rtsock_setup_socket()
+{
+ int fd;
+ int af = AF_UNSPEC; /* 0 to capture messages from all AFs */
+ fd = socket(PF_ROUTE, SOCK_RAW, af);
+
+ ATF_REQUIRE_MSG(fd != -1, "rtsock open failed: %s", strerror(errno));
+
+ /* Listen for our messages */
+ int on = 1;
+ if (setsockopt(fd, SOL_SOCKET,SO_USELOOPBACK, &on, sizeof(on)) < 0)
+ RLOG_ERRNO("setsockopt failed");
+
+ return (fd);
+}
+
+ssize_t
+rtsock_send_rtm(int fd, struct rt_msghdr *rtm)
+{
+ int my_errno;
+ ssize_t len;
+
+ rtsock_update_rtm_len(rtm);
+
+ len = write(fd, rtm, rtm->rtm_msglen);
+ my_errno = errno;
+ RTSOCK_ATF_REQUIRE_MSG(rtm, len == rtm->rtm_msglen,
+ "rtsock write failed: want %d got %zd (%s)",
+ rtm->rtm_msglen, len, strerror(my_errno));
+
+ return (len);
+}
+
+struct rt_msghdr *
+rtsock_read_rtm(int fd, char *buffer, size_t buflen)
+{
+ ssize_t len;
+ struct pollfd pfd;
+ int poll_delay = 5 * 1000; /* 5 seconds */
+
+ /* Check for the data available to read first */
+ memset(&pfd, 0, sizeof(pfd));
+ pfd.fd = fd;
+ pfd.events = POLLIN;
+
+ if (poll(&pfd, 1, poll_delay) == 0)
+ ATF_REQUIRE_MSG(1 == 0, "rtsock read timed out (%d seconds passed)",
+ poll_delay / 1000);
+
+ len = read(fd, buffer, buflen);
+ int my_errno = errno;
+ ATF_REQUIRE_MSG(len > 0, "rtsock read failed: %s", strerror(my_errno));
+
+ rtsock_validate_message(buffer, len);
+ return ((struct rt_msghdr *)buffer);
+}
+
+struct rt_msghdr *
+rtsock_read_rtm_reply(int fd, char *buffer, size_t buflen, int seq)
+{
+ struct rt_msghdr *rtm;
+ int found = 0;
+
+ while (true) {
+ rtm = rtsock_read_rtm(fd, buffer, buflen);
+ if (rtm->rtm_pid == getpid() && rtm->rtm_seq == seq)
+ found = 1;
+ if (found)
+ RLOG("--- MATCHED RTSOCK MESSAGE ---");
+ else
+ RLOG("--- SKIPPED RTSOCK MESSAGE ---");
+ rtsock_print_rtm(rtm);
+ if (found)
+ return (rtm);
+ }
+
+ /* NOTREACHED */
+}
+
+void
+rtsock_prepare_route_message_base(struct rt_msghdr *rtm, int cmd)
+{
+
+ memset(rtm, 0, sizeof(struct rt_msghdr));
+ rtm->rtm_type = cmd;
+ rtm->rtm_version = RTM_VERSION;
+ rtm->rtm_seq = _rtm_seq++;
+}
+
+void
+rtsock_prepare_route_message(struct rt_msghdr *rtm, int cmd, struct sockaddr *dst,
+ struct sockaddr *mask, struct sockaddr *gw)
+{
+
+ rtsock_prepare_route_message_base(rtm, cmd);
+ if (dst != NULL)
+ rtsock_add_rtm_sa(rtm, RTA_DST, dst);
+
+ if (gw != NULL) {
+ rtsock_add_rtm_sa(rtm, RTA_GATEWAY, gw);
+ rtm->rtm_flags |= RTF_GATEWAY;
+ }
+
+ if (mask != NULL)
+ rtsock_add_rtm_sa(rtm, RTA_NETMASK, mask);
+}
+
+void
+rtsock_add_rtm_sa(struct rt_msghdr *rtm, int addr_type, struct sockaddr *sa)
+{
+ char *ptr = (char *)(rtm + 1);
+ for (int i = 0; i < RTAX_MAX; i++) {
+ if (rtm->rtm_addrs & (1 << i)) {
+ /* add */
+ ptr += ALIGN(((struct sockaddr *)ptr)->sa_len);
+ }
+ }
+
+ rtm->rtm_addrs |= addr_type;
+ memcpy(ptr, sa, sa->sa_len);
+}
+
+struct sockaddr *
+rtsock_find_rtm_sa(struct rt_msghdr *rtm, int addr_type)
+{
+ char *ptr = (char *)(rtm + 1);
+ for (int i = 0; i < RTAX_MAX; i++) {
+ if (rtm->rtm_addrs & (1 << i)) {
+ if (addr_type == (1 << i))
+ return ((struct sockaddr *)ptr);
+ /* add */
+ ptr += ALIGN(((struct sockaddr *)ptr)->sa_len);
+ }
+ }
+
+ return (NULL);
+}
+
+size_t
+rtsock_calc_rtm_len(struct rt_msghdr *rtm)
+{
+ size_t len = sizeof(struct rt_msghdr);
+
+ char *ptr = (char *)(rtm + 1);
+ for (int i = 0; i < RTAX_MAX; i++) {
+ if (rtm->rtm_addrs & (1 << i)) {
+ /* add */
+ int sa_len = ALIGN(((struct sockaddr *)ptr)->sa_len);
+ len += sa_len;
+ ptr += sa_len;
+ }
+ }
+
+ return len;
+}
+
+void
+rtsock_update_rtm_len(struct rt_msghdr *rtm)
+{
+
+ rtm->rtm_msglen = rtsock_calc_rtm_len(rtm);
+}
+
+static void
+_validate_message_sockaddrs(char *buffer, int rtm_len, size_t offset, int rtm_addrs)
+{
+ struct sockaddr *sa;
+ size_t parsed_len = offset;
+
+ /* Offset denotes initial header size */
+ sa = (struct sockaddr *)(buffer + offset);
+
+ for (int i = 0; i < RTAX_MAX; i++) {
+ if ((rtm_addrs & (1 << i)) == 0)
+ continue;
+ parsed_len += SA_SIZE(sa);
+ RTSOCK_ATF_REQUIRE_MSG((struct rt_msghdr *)buffer, parsed_len <= rtm_len,
+ "SA %d: len %d exceeds msg size %d", i, (int)sa->sa_len, rtm_len);
+ if (sa->sa_family == AF_LINK) {
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;
+ int data_len = sdl->sdl_nlen + sdl->sdl_alen;
+ data_len += offsetof(struct sockaddr_dl, sdl_data);
+
+ RTSOCK_ATF_REQUIRE_MSG((struct rt_msghdr *)buffer,
+ data_len <= rtm_len,
+ "AF_LINK data size exceeds total len: %u vs %u, nlen=%d alen=%d",
+ data_len, rtm_len, sdl->sdl_nlen, sdl->sdl_alen);
+ }
+ sa = (struct sockaddr *)((char *)sa + SA_SIZE(sa));
+ }
+}
+
+/*
+ * Raises error if base syntax checks fails.
+ */
+void
+rtsock_validate_message(char *buffer, ssize_t len)
+{
+ struct rt_msghdr *rtm;
+
+ ATF_REQUIRE_MSG(len > 0, "read() return %zd, error: %s", len, strerror(errno));
+
+ rtm = (struct rt_msghdr *)buffer;
+ ATF_REQUIRE_MSG(rtm->rtm_version == RTM_VERSION, "unknown RTM_VERSION: expected %d got %d",
+ RTM_VERSION, rtm->rtm_version);
+ ATF_REQUIRE_MSG(rtm->rtm_msglen <= len, "wrong message length: expected %d got %d",
+ (int)len, (int)rtm->rtm_msglen);
+
+ switch (rtm->rtm_type) {
+ case RTM_GET:
+ case RTM_ADD:
+ case RTM_DELETE:
+ case RTM_CHANGE:
+ _validate_message_sockaddrs(buffer, rtm->rtm_msglen,
+ sizeof(struct rt_msghdr), rtm->rtm_addrs);
+ break;
+ case RTM_DELADDR:
+ case RTM_NEWADDR:
+ _validate_message_sockaddrs(buffer, rtm->rtm_msglen,
+ sizeof(struct ifa_msghdr), ((struct ifa_msghdr *)buffer)->ifam_addrs);
+ break;
+ }
+}
+
+void
+rtsock_validate_pid_ours(struct rt_msghdr *rtm)
+{
+ RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_pid == getpid(), "expected pid %d, got %d",
+ getpid(), rtm->rtm_pid);
+}
+
+void
+rtsock_validate_pid_user(struct rt_msghdr *rtm)
+{
+ RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_pid > 0, "expected non-zero pid, got %d",
+ rtm->rtm_pid);
+}
+
+void
+rtsock_validate_pid_kernel(struct rt_msghdr *rtm)
+{
+ RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_pid == 0, "expected zero pid, got %d",
+ rtm->rtm_pid);
+}
+
+#endif
diff --git a/tests/sys/net/routing/rtsock_config.h b/tests/sys/net/routing/rtsock_config.h
new file mode 100644
index 000000000000..345fc13f167c
--- /dev/null
+++ b/tests/sys/net/routing/rtsock_config.h
@@ -0,0 +1,175 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Alexander V. Chernikov
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _NET_ROUTING_RTSOCK_CONFIG_H_
+#define _NET_ROUTING_RTSOCK_CONFIG_H_
+
+#include "params.h"
+
+struct rtsock_config_options {
+ int num_interfaces; /* number of interfaces to create */
+};
+
+struct rtsock_test_config {
+ int ifindex;
+ char net4_str[INET_ADDRSTRLEN];
+ char addr4_str[INET_ADDRSTRLEN];
+ char net6_str[INET6_ADDRSTRLEN];
+ char addr6_str[INET6_ADDRSTRLEN];
+ struct sockaddr_in net4;
+ struct sockaddr_in mask4;
+ struct sockaddr_in addr4;
+ struct sockaddr_in6 net6;
+ struct sockaddr_in6 mask6;
+ struct sockaddr_in6 addr6;
+ int plen4;
+ int plen6;
+ char *remote_lladdr;
+ char *ifname;
+ char **ifnames;
+ bool autocreated_interface;
+ int rtsock_fd;
+ int num_interfaces;
+};
+
+struct rtsock_test_config *
+config_setup(const atf_tc_t *tc, struct rtsock_config_options *co)
+{
+ struct rtsock_config_options default_co;
+ struct rtsock_test_config *c;
+ char buf[64], *s;
+ const char *key;
+ int mask;
+
+ if (co == NULL) {
+ bzero(&default_co, sizeof(default_co));
+ co = &default_co;
+ co->num_interfaces = 1;
+ }
+
+ c = calloc(1, sizeof(struct rtsock_test_config));
+ c->rtsock_fd = -1;
+
+ key = atf_tc_get_config_var_wd(tc, "rtsock.v4prefix", "192.0.2.0/24");
+ strlcpy(buf, key, sizeof(buf));
+ if ((s = strchr(buf, '/')) == NULL)
+ return (NULL);
+ *s++ = '\0';
+ mask = strtol(s, NULL, 10);
+ if (mask < 0 || mask > 32)
+ return (NULL);
+ c->plen4 = mask;
+ inet_pton(AF_INET, buf, &c->net4.sin_addr);
+
+ c->net4.sin_len = sizeof(struct sockaddr_in);
+ c->net4.sin_family = AF_INET;
+ c->addr4.sin_len = sizeof(struct sockaddr_in);
+ c->addr4.sin_family = AF_INET;
+
+ sa_fill_mask4(&c->mask4, c->plen4);
+
+ /* Fill in interface IPv4 address. Assume the first address in net */
+ c->addr4.sin_addr.s_addr = htonl(ntohl(c->net4.sin_addr.s_addr) + 1);
+ inet_ntop(AF_INET, &c->net4.sin_addr, c->net4_str, INET_ADDRSTRLEN);
+ inet_ntop(AF_INET, &c->addr4.sin_addr, c->addr4_str, INET_ADDRSTRLEN);
+
+ key = atf_tc_get_config_var_wd(tc, "rtsock.v6prefix", "2001:DB8::/32");
+ strlcpy(buf, key, sizeof(buf));
+ if ((s = strchr(buf, '/')) == NULL)
+ return (NULL);
+ *s++ = '\0';
+ mask = strtol(s, NULL, 10);
+ if (mask < 0 || mask > 128)
+ return (NULL);
+ c->plen6 = mask;
+
+ inet_pton(AF_INET6, buf, &c->net6.sin6_addr);
+
+ c->net6.sin6_len = sizeof(struct sockaddr_in6);
+ c->net6.sin6_family = AF_INET6;
+ c->addr6.sin6_len = sizeof(struct sockaddr_in6);
+ c->addr6.sin6_family = AF_INET6;
+
+ sa_fill_mask6(&c->mask6, c->plen6);
+
+ /* Fill in interface IPv6 address. Assume the first address in net */
+ memcpy(&c->addr6.sin6_addr, &c->net6.sin6_addr, sizeof(struct in6_addr));
+#define _s6_addr32 __u6_addr.__u6_addr32
+ c->addr6.sin6_addr._s6_addr32[3] = htonl(ntohl(c->net6.sin6_addr._s6_addr32[3]) + 1);
+#undef _s6_addr32
+ inet_ntop(AF_INET6, &c->net6.sin6_addr, c->net6_str, INET6_ADDRSTRLEN);
+ inet_ntop(AF_INET6, &c->addr6.sin6_addr, c->addr6_str, INET6_ADDRSTRLEN);
+
+ ATF_CHECK_ERRNO(0, true);
+
+ if (co->num_interfaces > 0) {
+ /* Try loading if_epair and if that fails skip the test. */
+ kldload("if_epair");
+ ATF_REQUIRE_KERNEL_MODULE("if_epair");
+ /* Clear errno for the following tests. */
+ errno = 0;
+
+ c->ifnames = calloc(co->num_interfaces, sizeof(char *));
+ for (int i = 0; i < co->num_interfaces; i++)
+ c->ifnames[i] = iface_create("epair");
+
+ c->ifname = c->ifnames[0];
+ c->ifindex = if_nametoindex(c->ifname);
+ ATF_REQUIRE_MSG(c->ifindex != 0, "interface %s not found",
+ c->ifname);
+ }
+ c->num_interfaces = co->num_interfaces;
+
+ c->remote_lladdr = strdup(atf_tc_get_config_var_wd(tc,
+ "rtsock.remote_lladdr", "00:00:5E:00:53:42"));
+
+ return (c);
+}
+
+void
+config_generic_cleanup(const atf_tc_t *tc)
+{
+ const char *srcdir = atf_tc_get_config_var(tc, "srcdir");
+ char cmd[512];
+ int ret;
+
+ snprintf(cmd, sizeof(cmd), "%s/generic_cleanup.sh", srcdir);
+ ret = system(cmd);
+ if (ret != 0)
+ RLOG("'%s' failed, error %d", cmd, ret);
+}
+
+void
+config_describe_root_test(atf_tc_t *tc, char *test_descr)
+{
+
+ atf_tc_set_md_var(tc, "descr", test_descr);
+ // Adding/deleting prefix requires root privileges
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+#endif
diff --git a/tests/sys/net/routing/rtsock_print.h b/tests/sys/net/routing/rtsock_print.h
new file mode 100644
index 000000000000..61d70dc55670
--- /dev/null
+++ b/tests/sys/net/routing/rtsock_print.h
@@ -0,0 +1,412 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Alexander V. Chernikov
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _NET_ROUTING_RTSOCK_PRINT_H_
+#define _NET_ROUTING_RTSOCK_PRINT_H_
+
+
+#define RLOG(_fmt, ...) printf("%s: " _fmt "\n", __func__, ##__VA_ARGS__)
+#define RLOG_ERRNO(_fmt, ...) do { \
+ printf("%s: " _fmt, __func__, ##__VA_ARGS__); \
+ printf(": %s\n", strerror(errno)); \
+} while(0)
+
+#define RTSOCK_ATF_REQUIRE_MSG(_rtm, _cond, _fmt, ...) do { \
+ if (!(_cond)) { \
+ printf("-- CONDITION FAILED, rtm dump --\n\n");\
+ rtsock_print_message(_rtm); \
+ rtsock_print_table(AF_INET); \
+ rtsock_print_table(AF_INET6); \
+ printf("===================================\n");\
+ } \
+ ATF_REQUIRE_MSG(_cond, _fmt, ##__VA_ARGS__); \
+} while (0);
+
+#define RTSOCKHD_ATF_REQUIRE_MSG(_rtm, _cond, _fmt, ...) do { \
+ if (!(_cond)) { \
+ printf("-- CONDITION FAILED, rtm hexdump--\n\n");\
+ rtsock_print_message_hd(_rtm); \
+ } \
+ ATF_REQUIRE_MSG(_cond, _fmt, ##__VA_ARGS__); \
+} while (0);
+
+
+/* from route.c */
+static const char *const msgtypes[] = {
+ "",
+ "RTM_ADD",
+ "RTM_DELETE",
+ "RTM_CHANGE",
+ "RTM_GET",
+ "RTM_LOSING",
+ "RTM_REDIRECT",
+ "RTM_MISS",
+ "RTM_LOCK",
+ "RTM_OLDADD",
+ "RTM_OLDDEL",
+ "RTM_RESOLVE",
+ "RTM_NEWADDR",
+ "RTM_DELADDR",
+ "RTM_IFINFO",
+ "RTM_NEWMADDR",
+ "RTM_DELMADDR",
+ "RTM_IFANNOUNCE",
+ "RTM_IEEE80211",
+};
+
+static const char metricnames[] =
+ "\011weight\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire"
+ "\1mtu";
+static const char routeflags[] =
+ "\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE"
+ "\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE"
+ "\017PROTO2\020PROTO1\021PRCLONING\022WASCLONED\023PROTO3"
+ "\024FIXEDMTU\025PINNED\026LOCAL\027BROADCAST\030MULTICAST\035STICKY";
+static const char ifnetflags[] =
+ "\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6b6\7RUNNING\010NOARP"
+ "\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1"
+ "\017LINK2\020MULTICAST";
+static const char addrnames[] =
+ "\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD";
+
+static int
+_printb(char *buf, size_t bufsize, int b, const char *str)
+{
+ int i;
+ int gotsome = 0;
+
+ char *pbuf = buf;
+
+ if (b == 0) {
+ *pbuf = '\0';
+ return (0);
+ }
+ while ((i = *str++) != 0) {
+ if (b & (1 << (i-1))) {
+ if (gotsome == 0)
+ i = '<';
+ else
+ i = ',';
+ *pbuf++ = i;
+ gotsome = 1;
+ for (; (i = *str) > 32; str++)
+ *pbuf++ = i;
+ } else
+ while (*str > 32)
+ str++;
+ }
+ if (gotsome)
+ *pbuf++ = '>';
+ *pbuf = '\0';
+
+ return (int)(pbuf - buf);
+}
+
+const char *
+rtsock_print_cmdtype(int cmd)
+{
+
+ return (msgtypes[cmd]);
+}
+
+char *
+rtsock_print_rtm_flags(char *buf, int buflen, int rtm_flags)
+{
+
+ _printb(buf, buflen, rtm_flags, routeflags);
+ return (buf);
+}
+
+
+#define _PRINTX(fmt, ...) do { \
+ one_len = snprintf(ptr, rem_len, fmt, __VA_ARGS__); \
+ ptr += one_len; \
+ rem_len -= one_len; \
+} while(0)
+
+
+void
+sa_print_hd(char *buf, int buflen, const char *data, int len)
+{
+ char *ptr;
+ int one_len, rem_len;
+
+ ptr = buf;
+ rem_len = buflen;
+
+ const char *last_char = NULL;
+ unsigned char v;
+ int repeat_count = 0;
+ for (int i = 0; i < len; i++) {
+ if (last_char && *last_char == data[i] && data[i] == 0x00) {
+ repeat_count++;
+ continue;
+ }
+
+ if (repeat_count > 1) {
+ _PRINTX("{%d}", repeat_count);
+ repeat_count = 0;
+ }
+
+ v = ((const unsigned char *)data)[i];
+ if (last_char == NULL)
+ _PRINTX("x%02X", v);
+ else
+ _PRINTX(", x%02X", v);
+
+ last_char = &data[i];
+ repeat_count = 1;
+ }
+
+ if (repeat_count > 1)
+ snprintf(ptr, rem_len, "{%d}", repeat_count);
+}
+
+#undef _PRINTX
+
+void
+sa_print(const struct sockaddr *sa, int include_hexdump)
+{
+ char hdbuf[512], abuf[64];
+ char ifbuf[128];
+ const struct sockaddr_dl *sdl;
+ const struct sockaddr_in6 *sin6;
+ const struct sockaddr_in *sin;
+ int i;
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ sin = (struct sockaddr_in *)sa;
+ inet_ntop(AF_INET, &sin->sin_addr, abuf, sizeof(abuf));
+ printf(" af=inet len=%d addr=%s", sa->sa_len, abuf);
+ break;
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *)sa;
+ inet_ntop(AF_INET6, &sin6->sin6_addr, abuf, sizeof(abuf));
+ int scope_id = sin6->sin6_scope_id;
+ printf(" af=inet6 len=%d addr=%s", sa->sa_len, abuf);
+ if (scope_id != 0) {
+ memset(ifbuf, 0, sizeof(ifbuf));
+ if_indextoname(scope_id, ifbuf);
+ printf(" scope_id=%d if_name=%s", scope_id, ifbuf);
+ }
+ break;
+ case AF_LINK:
+ sdl = (const struct sockaddr_dl *)sa;
+ int sdl_index = sdl->sdl_index;
+ if (sdl_index != 0) {
+ memset(ifbuf, 0, sizeof(ifbuf));
+ if_indextoname(sdl_index, ifbuf);
+ printf(" af=link len=%d sdl_index=%d if_name=%s", sdl->sdl_len, sdl_index, ifbuf);
+ }
+ if (sdl->sdl_nlen) {
+ char _ifname[IFNAMSIZ];
+ memcpy(_ifname, sdl->sdl_data, sdl->sdl_nlen);
+ _ifname[sdl->sdl_nlen] = '\0';
+ printf(" name=%s", _ifname);
+ }
+ if (sdl->sdl_alen) {
+ printf(" addr=");
+ const char *lladdr = LLADDR(sdl);
+ for (int i = 0; i < sdl->sdl_alen; i++) {
+ if (i + 1 < sdl->sdl_alen)
+ printf("%02X:", ((const unsigned char *)lladdr)[i]);
+ else
+ printf("%02X", ((const unsigned char *)lladdr)[i]);
+ }
+ }
+ break;
+ default:
+ printf(" af=%d len=%d", sa->sa_family, sa->sa_len);
+ }
+
+ if (include_hexdump) {
+ sa_print_hd(hdbuf, sizeof(hdbuf), ((char *)sa), sa->sa_len);
+ printf(" hd={%s}", hdbuf);
+ }
+ printf("\n");
+}
+
+/*
+got message of size 240 on Mon Dec 16 09:23:31 2019
+RTM_ADD: Add Route: len 240, pid: 25534, seq 2, errno 0, flags:<HOST,DONE,LLINFO,STATIC>
+locks: inits:
+sockaddrs: <DST,GATEWAY>
+*/
+
+void
+rtsock_print_rtm(struct rt_msghdr *rtm)
+{
+ struct timeval tv;
+ struct tm tm_res;
+ char buf[64];
+
+ gettimeofday(&tv, NULL);
+ localtime_r(&tv.tv_sec, &tm_res);
+ strftime(buf, sizeof(buf), "%F %T", &tm_res);
+ printf("Got message of size %hu on %s\n", rtm->rtm_msglen, buf);
+
+ char flags_buf[256];
+ rtsock_print_rtm_flags(flags_buf, sizeof(flags_buf), rtm->rtm_flags);
+
+ printf("%s: len %hu, pid: %d, seq %d, errno %d, flags: %s\n", msgtypes[rtm->rtm_type],
+ rtm->rtm_msglen, rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno, flags_buf);
+
+ if (rtm->rtm_inits > 0) {
+ _printb(flags_buf, sizeof(flags_buf), rtm->rtm_inits, metricnames);
+ printf("metrics: %s\n", flags_buf);
+ if (rtm->rtm_inits & RTV_MTU)
+ printf("mtu: %lu\n", rtm->rtm_rmx.rmx_mtu);
+ if (rtm->rtm_inits & RTV_EXPIRE) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ printf("expire: %d (%lu raw)\n",
+ (int)(rtm->rtm_rmx.rmx_expire - tv.tv_sec), rtm->rtm_rmx.rmx_expire);
+ }
+ }
+
+ _printb(flags_buf, sizeof(flags_buf), rtm->rtm_addrs, addrnames);
+ printf("sockaddrs: 0x%X %s\n", rtm->rtm_addrs, flags_buf);
+
+ char *ptr = (char *)(rtm + 1);
+ for (int i = 0; i < RTAX_MAX; i++) {
+ if (rtm->rtm_addrs & (1 << i)) {
+ struct sockaddr *sa = (struct sockaddr *)ptr;
+ sa_print(sa, 1);
+
+ /* add */
+ ptr += ALIGN(((struct sockaddr *)ptr)->sa_len);
+ }
+ }
+
+ printf("\n");
+
+}
+
+void
+rtsock_print_ifa(struct ifa_msghdr *ifam)
+{
+ struct timeval tv;
+ struct tm tm_res;
+ char buf[64];
+
+ gettimeofday(&tv, NULL);
+ localtime_r(&tv.tv_sec, &tm_res);
+ strftime(buf, sizeof(buf), "%F %T", &tm_res);
+ printf("Got message of size %hu on %s\n", ifam->ifam_msglen, buf);
+
+ char flags_buf[256];
+ _printb(flags_buf, sizeof(flags_buf), ifam->ifam_flags, routeflags);
+
+ printf("%s: len %hu, ifindex: %d, flags: %s\n", msgtypes[ifam->ifam_type],
+ ifam->ifam_msglen, ifam->ifam_index, flags_buf);
+
+ _printb(flags_buf, sizeof(flags_buf), ifam->ifam_addrs, addrnames);
+ printf("sockaddrs: 0x%X %s\n", ifam->ifam_addrs, flags_buf);
+
+ char *ptr = (char *)(ifam + 1);
+ for (int i = 0; i < RTAX_MAX; i++) {
+ if (ifam->ifam_addrs & (1 << i)) {
+ struct sockaddr *sa = (struct sockaddr *)ptr;
+ sa_print(sa, 1);
+
+ /* add */
+ ptr += ALIGN(((struct sockaddr *)ptr)->sa_len);
+ }
+ }
+
+ printf("\n");
+
+}
+
+void
+rtsock_print_message_hd(struct rt_msghdr *rtm)
+{
+ struct timeval tv;
+ struct tm tm_res;
+ char buf[64];
+ char dumpbuf[2048];
+
+ gettimeofday(&tv, NULL);
+ localtime_r(&tv.tv_sec, &tm_res);
+ strftime(buf, sizeof(buf), "%F %T", &tm_res);
+ printf("Got message type %s of size %hu on %s\n",
+ rtsock_print_cmdtype(rtm->rtm_type),
+ rtm->rtm_msglen, buf);
+
+ sa_print_hd(dumpbuf, sizeof(dumpbuf), (char *)rtm, rtm->rtm_msglen);
+ printf(" %s\n", dumpbuf);
+}
+
+void
+rtsock_print_message(struct rt_msghdr *rtm)
+{
+
+ switch (rtm->rtm_type) {
+ case RTM_GET:
+ case RTM_ADD:
+ case RTM_DELETE:
+ case RTM_CHANGE:
+ rtsock_print_rtm(rtm);
+ break;
+ case RTM_DELADDR:
+ case RTM_NEWADDR:
+ rtsock_print_ifa((struct ifa_msghdr *)rtm);
+ break;
+ default:
+ printf("unknown rt message type %X\n", rtm->rtm_type);
+ }
+}
+
+static void
+print_command(char *cmd)
+{
+ char line[1024];
+
+ FILE *fp = popen(cmd, "r");
+ if (fp != NULL) {
+ while (fgets(line, sizeof(line), fp) != NULL)
+ printf("%s", line);
+ pclose(fp);
+ }
+}
+
+void
+rtsock_print_table(int family)
+{
+ char cmdbuf[128];
+ char *key = (family == AF_INET) ? "4" : "6";
+
+ snprintf(cmdbuf, sizeof(cmdbuf), "/usr/bin/netstat -%srnW", key);
+ printf("==== %s ===\n", cmdbuf);
+ print_command(cmdbuf);
+ snprintf(cmdbuf, sizeof(cmdbuf), "/usr/bin/netstat -%sonW", key);
+ printf("==== %s ===\n", cmdbuf);
+ print_command(cmdbuf);
+}
+
+#endif
diff --git a/tests/sys/net/routing/test_routing_l3.py b/tests/sys/net/routing/test_routing_l3.py
new file mode 100755
index 000000000000..3a3822293424
--- /dev/null
+++ b/tests/sys/net/routing/test_routing_l3.py
@@ -0,0 +1,120 @@
+import ipaddress
+import socket
+
+import pytest
+from atf_python.sys.net.rtsock import RtConst
+from atf_python.sys.net.rtsock import Rtsock
+from atf_python.sys.net.rtsock import RtsockRtMessage
+from atf_python.sys.net.rtsock import SaHelper
+from atf_python.sys.net.tools import ToolsHelper
+from atf_python.sys.net.vnet import SingleVnetTestTemplate
+from atf_python.sys.net.vnet import VnetTestTemplate
+
+
+class TestIfOps(VnetTestTemplate):
+ TOPOLOGY = {
+ "vnet1": {"ifaces": ["if1", "if2"]},
+ "if1": {"prefixes4": [], "prefixes6": []},
+ "if2": {"prefixes4": [], "prefixes6": []},
+ }
+
+ @pytest.mark.parametrize("family", ["inet", "inet6"])
+ @pytest.mark.require_user("root")
+ def test_change_prefix_route(self, family):
+ """Tests that prefix route changes to the new one upon addr deletion"""
+ vnet = self.vnet_map["vnet1"]
+ first_iface = vnet.iface_alias_map["if1"]
+ second_iface = vnet.iface_alias_map["if2"]
+ if family == "inet":
+ first_addr = ipaddress.ip_interface("192.0.2.1/24")
+ second_addr = ipaddress.ip_interface("192.0.2.2/24")
+ else:
+ first_addr = ipaddress.ip_interface("2001:db8::1/64")
+ second_addr = ipaddress.ip_interface("2001:db8::2/64")
+
+ first_iface.setup_addr(str(first_addr))
+ second_iface.setup_addr(str(second_addr))
+
+ # At this time prefix should be pointing to the first interface
+ routes = ToolsHelper.get_routes(family)
+ px = [r for r in routes if r["destination"] == str(first_addr.network)][0]
+ assert px["interface-name"] == first_iface.name
+
+ # Now delete address from the first interface and verify switchover
+ first_iface.delete_addr(first_addr.ip)
+
+ routes = ToolsHelper.get_routes(family)
+ px = [r for r in routes if r["destination"] == str(first_addr.network)][0]
+ assert px["interface-name"] == second_iface.name
+
+ @pytest.mark.parametrize(
+ "family",
+ [
+ "inet",
+ pytest.param("inet6", marks=pytest.mark.xfail(reason="currently fails")),
+ ],
+ )
+ @pytest.mark.require_user("root")
+ def test_change_prefix_route_same_iface(self, family):
+ """Tests that prefix route changes to the new ifa upon addr deletion"""
+ vnet = self.vnet_map["vnet1"]
+ first_iface = vnet.iface_alias_map["if1"]
+
+ if family == "inet":
+ first_addr = ipaddress.ip_interface("192.0.2.1/24")
+ second_addr = ipaddress.ip_interface("192.0.2.2/24")
+ else:
+ first_addr = ipaddress.ip_interface("2001:db8::1/64")
+ second_addr = ipaddress.ip_interface("2001:db8::2/64")
+
+ first_iface.setup_addr(str(first_addr))
+ first_iface.setup_addr(str(second_addr))
+
+ # At this time prefix should be pointing to the first interface
+ routes = ToolsHelper.get_routes(family)
+ px = [r for r in routes if r["destination"] == str(first_addr.network)][0]
+ assert px["interface-name"] == first_iface.name
+
+ # Now delete address from the first interface and verify switchover
+ first_iface.delete_addr(str(first_addr.ip))
+
+ routes = ToolsHelper.get_routes(family)
+ px = [r for r in routes if r["destination"] == str(first_addr.network)][0]
+ nhop_kidx = px["nhop"]
+ assert px["interface-name"] == first_iface.name
+ nhops = ToolsHelper.get_nhops(family)
+ nh = [nh for nh in nhops if nh["index"] == nhop_kidx][0]
+ assert nh["ifa"] == str(second_addr.ip)
+
+
+class TestRouteCornerCase1(SingleVnetTestTemplate):
+ @pytest.mark.parametrize("family", ["inet", "inet6"])
+ @pytest.mark.require_user("root")
+ def test_add_direct_route_p2p_wo_ifa(self, family):
+
+ tun_ifname = ToolsHelper.get_output("/sbin/ifconfig tun create").rstrip()
+ tun_ifindex = socket.if_nametoindex(tun_ifname)
+ assert tun_ifindex > 0
+ rtsock = Rtsock()
+
+ if family == "inet":
+ prefix = "172.16.0.0/12"
+ else:
+ prefix = "2a02:6b8::/64"
+ IFT_ETHER = 0x06
+ gw_link = SaHelper.link_sa(ifindex=tun_ifindex, iftype=IFT_ETHER)
+
+ msg = rtsock.new_rtm_add(prefix, gw_link)
+ msg.add_link_attr(RtConst.RTA_IFP, tun_ifindex)
+ rtsock.write_message(msg)
+
+ data = rtsock.read_data(msg.rtm_seq)
+ msg_in = RtsockRtMessage.from_bytes(data)
+ msg_in.print_in_message()
+
+ desired_sa = {
+ RtConst.RTA_DST: msg.get_sa(RtConst.RTA_DST),
+ RtConst.RTA_NETMASK: msg.get_sa(RtConst.RTA_NETMASK),
+ }
+
+ msg_in.verify(msg.rtm_type, desired_sa)
diff --git a/tests/sys/net/routing/test_rtsock_l3.c b/tests/sys/net/routing/test_rtsock_l3.c
new file mode 100644
index 000000000000..cdfc63d604bf
--- /dev/null
+++ b/tests/sys/net/routing/test_rtsock_l3.c
@@ -0,0 +1,1421 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Alexander V. Chernikov
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "rtsock_common.h"
+#include "rtsock_config.h"
+#include "sys/types.h"
+#include <sys/time.h>
+#include <sys/ioctl.h>
+
+#include "net/bpf.h"
+
+static void
+jump_vnet(struct rtsock_test_config *c, const atf_tc_t *tc)
+{
+ char vnet_name[512];
+
+ snprintf(vnet_name, sizeof(vnet_name), "vt-%s", atf_tc_get_ident(tc));
+ RLOG("jumping to %s", vnet_name);
+
+ vnet_switch(vnet_name, c->ifnames, c->num_interfaces);
+
+ /* Update ifindex cache */
+ c->ifindex = if_nametoindex(c->ifname);
+}
+
+static inline struct rtsock_test_config *
+presetup_ipv6_iface(const atf_tc_t *tc)
+{
+ struct rtsock_test_config *c;
+ int ret;
+
+ c = config_setup(tc, NULL);
+
+ jump_vnet(c, tc);
+
+ ret = iface_turn_up(c->ifname);
+ ATF_REQUIRE_MSG(ret == 0, "Unable to turn up %s", c->ifname);
+
+ ret = iface_enable_ipv6(c->ifname);
+ ATF_REQUIRE_MSG(ret == 0, "Unable to enable IPv6 on %s", c->ifname);
+ ATF_REQUIRE_ERRNO(0, true);
+
+ return (c);
+}
+
+static inline struct rtsock_test_config *
+presetup_ipv6(const atf_tc_t *tc)
+{
+ struct rtsock_test_config *c;
+ int ret;
+
+ c = presetup_ipv6_iface(tc);
+
+ ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6);
+
+ c->rtsock_fd = rtsock_setup_socket();
+ ATF_REQUIRE_ERRNO(0, true);
+
+ return (c);
+}
+
+static inline struct rtsock_test_config *
+presetup_ipv4_iface(const atf_tc_t *tc)
+{
+ struct rtsock_test_config *c;
+ int ret;
+
+ c = config_setup(tc, NULL);
+ ATF_REQUIRE(c != NULL);
+
+ jump_vnet(c, tc);
+
+ ret = iface_turn_up(c->ifname);
+ ATF_REQUIRE_MSG(ret == 0, "Unable to turn up %s", c->ifname);
+ ATF_REQUIRE_ERRNO(0, true);
+
+ return (c);
+}
+
+static inline struct rtsock_test_config *
+presetup_ipv4(const atf_tc_t *tc)
+{
+ struct rtsock_test_config *c;
+ int ret;
+
+ c = presetup_ipv4_iface(tc);
+
+ /* assumes ifconfig doing IFF_UP */
+ ret = iface_setup_addr(c->ifname, c->addr4_str, c->plen4);
+ ATF_REQUIRE_MSG(ret == 0, "ifconfig failed");
+
+ c->rtsock_fd = rtsock_setup_socket();
+ ATF_REQUIRE_ERRNO(0, true);
+
+ return (c);
+}
+
+
+static void
+prepare_v4_network(struct rtsock_test_config *c, struct sockaddr_in *dst,
+ struct sockaddr_in *mask, struct sockaddr_in *gw)
+{
+ /* Create IPv4 subnetwork with smaller prefix */
+ sa_fill_mask4(mask, c->plen4 + 1);
+ *dst = c->net4;
+ /* Calculate GW as last-net-address - 1 */
+ *gw = c->net4;
+ gw->sin_addr.s_addr = htonl((ntohl(c->net4.sin_addr.s_addr) | ~ntohl(c->mask4.sin_addr.s_addr)) - 1);
+ sa_print((struct sockaddr *)dst, 0);
+ sa_print((struct sockaddr *)mask, 0);
+ sa_print((struct sockaddr *)gw, 0);
+}
+
+static void
+prepare_v6_network(struct rtsock_test_config *c, struct sockaddr_in6 *dst,
+ struct sockaddr_in6 *mask, struct sockaddr_in6 *gw)
+{
+ /* Create IPv6 subnetwork with smaller prefix */
+ sa_fill_mask6(mask, c->plen6 + 1);
+ *dst = c->net6;
+ /* Calculate GW as last-net-address - 1 */
+ *gw = c->net6;
+#define _s6_addr32 __u6_addr.__u6_addr32
+ gw->sin6_addr._s6_addr32[0] = htonl((ntohl(gw->sin6_addr._s6_addr32[0]) | ~ntohl(c->mask6.sin6_addr._s6_addr32[0])));
+ gw->sin6_addr._s6_addr32[1] = htonl((ntohl(gw->sin6_addr._s6_addr32[1]) | ~ntohl(c->mask6.sin6_addr._s6_addr32[1])));
+ gw->sin6_addr._s6_addr32[2] = htonl((ntohl(gw->sin6_addr._s6_addr32[2]) | ~ntohl(c->mask6.sin6_addr._s6_addr32[2])));
+ gw->sin6_addr._s6_addr32[3] = htonl((ntohl(gw->sin6_addr._s6_addr32[3]) | ~ntohl(c->mask6.sin6_addr._s6_addr32[3])) - 1);
+#undef _s6_addr32
+ sa_print((struct sockaddr *)dst, 0);
+ sa_print((struct sockaddr *)mask, 0);
+ sa_print((struct sockaddr *)gw, 0);
+}
+
+static void
+prepare_route_message(struct rt_msghdr *rtm, int cmd, struct sockaddr *dst,
+ struct sockaddr *mask, struct sockaddr *gw)
+{
+
+ rtsock_prepare_route_message(rtm, cmd, dst, mask, gw);
+
+ if (cmd == RTM_ADD || cmd == RTM_CHANGE)
+ rtm->rtm_flags |= RTF_STATIC;
+}
+
+static void
+verify_route_message(struct rt_msghdr *rtm, int cmd, struct sockaddr *dst,
+ struct sockaddr *mask, struct sockaddr *gw)
+{
+ char msg[512];
+ struct sockaddr *sa;
+ int ret;
+
+ RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_type == cmd,
+ "expected %s message, got %d (%s)", rtsock_print_cmdtype(cmd),
+ rtm->rtm_type, rtsock_print_cmdtype(rtm->rtm_type));
+ RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_errno == 0,
+ "got got errno %d as message reply", rtm->rtm_errno);
+ RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->_rtm_spare1 == 0,
+ "expected rtm_spare==0, got %d", rtm->_rtm_spare1);
+
+ /* kernel MAY return more sockaddrs, including RTA_IFP / RTA_IFA, so verify the needed ones */
+ if (dst != NULL) {
+ sa = rtsock_find_rtm_sa(rtm, RTA_DST);
+ RTSOCK_ATF_REQUIRE_MSG(rtm, sa != NULL, "DST is not set");
+ ret = sa_equal_msg(sa, dst, msg, sizeof(msg));
+ RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "DST sa diff: %s", msg);
+ }
+
+ if (mask != NULL) {
+ sa = rtsock_find_rtm_sa(rtm, RTA_NETMASK);
+ RTSOCK_ATF_REQUIRE_MSG(rtm, sa != NULL, "NETMASK is not set");
+ ret = sa_equal_msg(sa, mask, msg, sizeof(msg));
+ ret = 1;
+ RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "NETMASK sa diff: %s", msg);
+ }
+
+ if (gw != NULL) {
+ sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY);
+ RTSOCK_ATF_REQUIRE_MSG(rtm, sa != NULL, "GATEWAY is not set");
+ ret = sa_equal_msg(sa, gw, msg, sizeof(msg));
+ RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "GATEWAY sa diff: %s", msg);
+ }
+}
+
+static void
+verify_route_message_extra(struct rt_msghdr *rtm, int ifindex, int rtm_flags)
+{
+ RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_index == ifindex,
+ "expected ifindex %d, got %d", ifindex, rtm->rtm_index);
+
+ if (rtm->rtm_flags != rtm_flags) {
+ char got_flags[64], expected_flags[64];
+ rtsock_print_rtm_flags(got_flags, sizeof(got_flags),
+ rtm->rtm_flags);
+ rtsock_print_rtm_flags(expected_flags, sizeof(expected_flags),
+ rtm_flags);
+
+ RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_flags == rtm_flags,
+ "expected flags: 0x%X %s, got 0x%X %s",
+ rtm_flags, expected_flags,
+ rtm->rtm_flags, got_flags);
+ }
+}
+
+static void
+verify_link_gateway(struct rt_msghdr *rtm, int ifindex)
+{
+ struct sockaddr *sa;
+ struct sockaddr_dl *sdl;
+
+ sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY);
+ RTSOCK_ATF_REQUIRE_MSG(rtm, sa != NULL, "GATEWAY is not set");
+ RTSOCK_ATF_REQUIRE_MSG(rtm, sa->sa_family == AF_LINK, "GW sa family is %d", sa->sa_family);
+ sdl = (struct sockaddr_dl *)sa;
+ RTSOCK_ATF_REQUIRE_MSG(rtm, sdl->sdl_index == ifindex, "GW ifindex is %d", sdl->sdl_index);
+}
+
+/* TESTS */
+
+#define DECLARE_TEST_VARS \
+ char buffer[2048]; \
+ struct rtsock_test_config *c; \
+ struct rt_msghdr *rtm = (struct rt_msghdr *)buffer; \
+ struct sockaddr *sa; \
+ int ret; \
+ \
+
+#define DESCRIBE_ROOT_TEST(_msg) config_describe_root_test(tc, _msg)
+#define CLEANUP_AFTER_TEST config_generic_cleanup(tc)
+
+#define RTM_DECLARE_ROOT_TEST(_name, _descr) \
+ATF_TC_WITH_CLEANUP(_name); \
+ATF_TC_HEAD(_name, tc) \
+{ \
+ DESCRIBE_ROOT_TEST(_descr); \
+} \
+ATF_TC_CLEANUP(_name, tc) \
+{ \
+ CLEANUP_AFTER_TEST; \
+}
+
+ATF_TC_WITH_CLEANUP(rtm_get_v4_exact_success);
+ATF_TC_HEAD(rtm_get_v4_exact_success, tc)
+{
+ DESCRIBE_ROOT_TEST("Tests RTM_GET with exact prefix lookup on an interface prefix");
+}
+
+ATF_TC_BODY(rtm_get_v4_exact_success, tc)
+{
+ DECLARE_TEST_VARS;
+
+ c = presetup_ipv4(tc);
+
+ prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&c->net4,
+ (struct sockaddr *)&c->mask4, NULL);
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+
+ /*
+ * RTM_GET: Report Metrics: len 240, pid: 45072, seq 42, errno 0, flags: <UP,DONE,PINNED>
+ * sockaddrs: 0x7 <DST,GATEWAY,NETMASK>
+ * af=inet len=16 addr=192.0.2.0 hd={10, 02, 00{2}, C0, 00, 02, 00{9}}
+ * af=link len=54 sdl_index=3 if_name=tap4242 hd={36, 12, 03, 00, 06, 00{49}}
+ * af=inet len=16 addr=255.255.255.0 hd={10, 02, FF{5}, 00{9}}
+ */
+
+ verify_route_message(rtm, RTM_GET, (struct sockaddr *)&c->net4,
+ (struct sockaddr *)&c->mask4, NULL);
+
+ verify_route_message_extra(rtm, c->ifindex, RTF_UP | RTF_DONE | RTF_PINNED);
+
+ /* Explicitly verify gateway for the interface route */
+ verify_link_gateway(rtm, c->ifindex);
+ sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY);
+ RTSOCK_ATF_REQUIRE_MSG(rtm, sa != NULL, "GATEWAY is not set");
+ RTSOCK_ATF_REQUIRE_MSG(rtm, sa->sa_family == AF_LINK, "GW sa family is %d", sa->sa_family);
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;
+ RTSOCK_ATF_REQUIRE_MSG(rtm, sdl->sdl_index == c->ifindex, "GW ifindex is %d", sdl->sdl_index);
+}
+
+ATF_TC_CLEANUP(rtm_get_v4_exact_success, tc)
+{
+ CLEANUP_AFTER_TEST;
+}
+
+ATF_TC_WITH_CLEANUP(rtm_get_v4_lpm_success);
+ATF_TC_HEAD(rtm_get_v4_lpm_success, tc)
+{
+ DESCRIBE_ROOT_TEST("Tests RTM_GET with address lookup on an existing prefix");
+}
+
+ATF_TC_BODY(rtm_get_v4_lpm_success, tc)
+{
+ DECLARE_TEST_VARS;
+
+ c = presetup_ipv4(tc);
+
+ prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&c->net4, NULL, NULL);
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+
+ /*
+ * RTM_GET: Report Metrics: len 312, pid: 67074, seq 1, errno 0, flags:<UP,DONE,PINNED>
+ * locks: inits:
+ * sockaddrs: <DST,GATEWAY,NETMASK,IFP,IFA>
+ * 10.0.0.0 link#1 255.255.255.0 vtnet0:52.54.0.42.f.ef 10.0.0.157
+ */
+
+ verify_route_message(rtm, RTM_GET, (struct sockaddr *)&c->net4,
+ (struct sockaddr *)&c->mask4, NULL);
+
+ verify_route_message_extra(rtm, c->ifindex, RTF_UP | RTF_DONE | RTF_PINNED);
+}
+
+ATF_TC_CLEANUP(rtm_get_v4_lpm_success, tc)
+{
+ CLEANUP_AFTER_TEST;
+}
+
+
+ATF_TC_WITH_CLEANUP(rtm_get_v4_empty_dst_failure);
+ATF_TC_HEAD(rtm_get_v4_empty_dst_failure, tc)
+{
+
+ DESCRIBE_ROOT_TEST("Tests RTM_GET with empty DST addr");
+}
+
+ATF_TC_BODY(rtm_get_v4_empty_dst_failure, tc)
+{
+ DECLARE_TEST_VARS;
+ struct rtsock_config_options co;
+
+ bzero(&co, sizeof(co));
+ co.num_interfaces = 0;
+
+ c = config_setup(tc,&co);
+ c->rtsock_fd = rtsock_setup_socket();
+
+ rtsock_prepare_route_message(rtm, RTM_GET, NULL,
+ (struct sockaddr *)&c->mask4, NULL);
+ rtsock_update_rtm_len(rtm);
+
+ ATF_CHECK_ERRNO(EINVAL, write(c->rtsock_fd, rtm, rtm->rtm_msglen) == -1);
+}
+
+ATF_TC_CLEANUP(rtm_get_v4_empty_dst_failure, tc)
+{
+ CLEANUP_AFTER_TEST;
+}
+
+ATF_TC_WITH_CLEANUP(rtm_get_v4_hostbits_success);
+ATF_TC_HEAD(rtm_get_v4_hostbits_success, tc)
+{
+ DESCRIBE_ROOT_TEST("Tests RTM_GET with prefix with some hosts-bits set");
+}
+
+ATF_TC_BODY(rtm_get_v4_hostbits_success, tc)
+{
+ DECLARE_TEST_VARS;
+
+ c = presetup_ipv4(tc);
+
+ /* Q the same prefix */
+ rtsock_prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&c->addr4,
+ (struct sockaddr *)&c->mask4, NULL);
+ rtsock_update_rtm_len(rtm);
+
+ ATF_REQUIRE_ERRNO(0, true);
+ ATF_CHECK_ERRNO(0, write(c->rtsock_fd, rtm, rtm->rtm_msglen) > 0);
+}
+
+ATF_TC_CLEANUP(rtm_get_v4_hostbits_success, tc)
+{
+ CLEANUP_AFTER_TEST;
+}
+
+ATF_TC_WITH_CLEANUP(rtm_add_v4_gw_direct_success);
+ATF_TC_HEAD(rtm_add_v4_gw_direct_success, tc)
+{
+ DESCRIBE_ROOT_TEST("Tests IPv4 route addition with directly-reachable GW specified by IP");
+}
+
+ATF_TC_BODY(rtm_add_v4_gw_direct_success, tc)
+{
+ DECLARE_TEST_VARS;
+
+ c = presetup_ipv4(tc);
+
+ /* Create IPv4 subnetwork with smaller prefix */
+ struct sockaddr_in mask4;
+ struct sockaddr_in net4;
+ struct sockaddr_in gw4;
+ prepare_v4_network(c, &net4, &mask4, &gw4);
+
+ prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4,
+ (struct sockaddr *)&mask4, (struct sockaddr *)&gw4);
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+
+ /*
+ * RTM_ADD: Add Route: len 200, pid: 46068, seq 42, errno 0, flags:<GATEWAY,DONE,STATIC>
+ * locks: inits:
+ * sockaddrs: <DST,GATEWAY,NETMASK>
+ * 192.0.2.0 192.0.2.254 255.255.255.128
+ */
+
+ verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4,
+ (struct sockaddr *)&mask4, (struct sockaddr *)&gw4);
+ verify_route_message_extra(rtm, c->ifindex,
+ RTF_UP | RTF_DONE | RTF_GATEWAY | RTF_STATIC);
+}
+
+ATF_TC_CLEANUP(rtm_add_v4_gw_direct_success, tc)
+{
+ CLEANUP_AFTER_TEST;
+}
+
+RTM_DECLARE_ROOT_TEST(rtm_add_v4_no_rtf_host_success,
+ "Tests success with netmask sa and RTF_HOST inconsistency");
+
+ATF_TC_BODY(rtm_add_v4_no_rtf_host_success, tc)
+{
+ DECLARE_TEST_VARS;
+
+ c = presetup_ipv4(tc);
+
+ /* Create IPv4 subnetwork with smaller prefix */
+ struct sockaddr_in mask4;
+ struct sockaddr_in net4;
+ struct sockaddr_in gw4;
+ prepare_v4_network(c, &net4, &mask4, &gw4);
+
+ prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4,
+ NULL, (struct sockaddr *)&gw4);
+ rtsock_update_rtm_len(rtm);
+
+ /* RTF_HOST is NOT specified, while netmask is empty */
+ ATF_REQUIRE_ERRNO(0, true);
+ ATF_CHECK_ERRNO(0, write(c->rtsock_fd, rtm, rtm->rtm_msglen) > 0);
+}
+
+ATF_TC_WITH_CLEANUP(rtm_del_v4_prefix_nogw_success);
+ATF_TC_HEAD(rtm_del_v4_prefix_nogw_success, tc)
+{
+ DESCRIBE_ROOT_TEST("Tests IPv4 route removal without specifying gateway");
+}
+
+ATF_TC_BODY(rtm_del_v4_prefix_nogw_success, tc)
+{
+ DECLARE_TEST_VARS;
+
+ c = presetup_ipv4(tc);
+
+ /* Create IPv4 subnetwork with smaller prefix */
+ struct sockaddr_in mask4;
+ struct sockaddr_in net4;
+ struct sockaddr_in gw4;
+ prepare_v4_network(c, &net4, &mask4, &gw4);
+
+ prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4,
+ (struct sockaddr *)&mask4, (struct sockaddr *)&gw4);
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+
+ /* Route has been added successfully, try to delete it */
+ prepare_route_message(rtm, RTM_DELETE, (struct sockaddr *)&net4,
+ (struct sockaddr *)&mask4, NULL);
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+
+ /*
+ * RTM_DELETE: Delete Route: len 200, pid: 46417, seq 43, errno 0, flags: <GATEWAY,DONE,STATIC>
+ * sockaddrs: 0x7 <DST,GATEWAY,NETMASK>
+ * af=inet len=16 addr=192.0.2.0 hd={10, 02, 00{2}, C0, 00, 02, 00{9}}
+ * af=inet len=16 addr=192.0.2.254 hd={10, 02, 00{2}, C0, 00, 02, FE, 00{8}}
+ * af=inet len=16 addr=255.255.255.128 hd={10, 02, FF{5}, 80, 00{8}}
+ */
+ verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&net4,
+ (struct sockaddr *)&mask4, (struct sockaddr *)&gw4);
+
+ verify_route_message_extra(rtm, c->ifindex, RTF_DONE | RTF_GATEWAY | RTF_STATIC);
+}
+
+ATF_TC_CLEANUP(rtm_del_v4_prefix_nogw_success, tc)
+{
+ CLEANUP_AFTER_TEST;
+}
+
+RTM_DECLARE_ROOT_TEST(rtm_change_v4_gw_success,
+ "Tests IPv4 gateway change");
+
+ATF_TC_BODY(rtm_change_v4_gw_success, tc)
+{
+ DECLARE_TEST_VARS;
+ struct rtsock_config_options co;
+
+ bzero(&co, sizeof(co));
+ co.num_interfaces = 2;
+
+ c = config_setup(tc, &co);
+ jump_vnet(c, tc);
+
+ ret = iface_turn_up(c->ifnames[0]);
+ ATF_REQUIRE_MSG(ret == 0, "Unable to turn up %s", c->ifnames[0]);
+ ret = iface_turn_up(c->ifnames[1]);
+ ATF_REQUIRE_MSG(ret == 0, "Unable to turn up %s", c->ifnames[1]);
+
+ ret = iface_setup_addr(c->ifnames[0], c->addr4_str, c->plen4);
+ ATF_REQUIRE_MSG(ret == 0, "ifconfig failed");
+
+ /* Use 198.51.100.0/24 "TEST-NET-2" for the second interface */
+ ret = iface_setup_addr(c->ifnames[1], "198.51.100.1", 24);
+ ATF_REQUIRE_MSG(ret == 0, "ifconfig failed");
+
+ c->rtsock_fd = rtsock_setup_socket();
+
+ /* Create IPv4 subnetwork with smaller prefix */
+ struct sockaddr_in mask4;
+ struct sockaddr_in net4;
+ struct sockaddr_in gw4;
+ prepare_v4_network(c, &net4, &mask4, &gw4);
+
+ prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4,
+ (struct sockaddr *)&mask4, (struct sockaddr *)&gw4);
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+
+ verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4,
+ (struct sockaddr *)&mask4, (struct sockaddr *)&gw4);
+
+ /* Change gateway to the one on desiding on the other interface */
+ inet_pton(AF_INET, "198.51.100.2", &gw4.sin_addr.s_addr);
+ prepare_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net4,
+ (struct sockaddr *)&mask4, (struct sockaddr *)&gw4);
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+
+ verify_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net4,
+ (struct sockaddr *)&mask4, (struct sockaddr *)&gw4);
+
+ verify_route_message_extra(rtm, if_nametoindex(c->ifnames[1]),
+ RTF_UP | RTF_DONE | RTF_GATEWAY | RTF_STATIC);
+
+ /* Verify the change has actually taken place */
+ prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&net4,
+ (struct sockaddr *)&mask4, NULL);
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+
+ /*
+ * RTM_GET: len 200, pid: 3894, seq 44, errno 0, flags: <UP,GATEWAY,DONE,STATIC>
+ * sockaddrs: 0x7 <DST,GATEWAY,NETMASK>
+ * af=inet len=16 addr=192.0.2.0 hd={x10, x02, x00{2}, xC0, x00, x02, x00{9}}
+ * af=inet len=16 addr=198.51.100.2 hd={x10, x02, x00{2}, xC6, x33, x64, x02, x00{8}}
+ * af=inet len=16 addr=255.255.255.128 hd={x10, x02, xFF, xFF, xFF, xFF, xFF, x80, x00{8}}
+ */
+
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+ verify_route_message_extra(rtm, if_nametoindex(c->ifnames[1]),
+ RTF_UP | RTF_DONE | RTF_GATEWAY | RTF_STATIC);
+
+}
+
+RTM_DECLARE_ROOT_TEST(rtm_change_v4_mtu_success,
+ "Tests IPv4 path mtu change");
+
+ATF_TC_BODY(rtm_change_v4_mtu_success, tc)
+{
+ DECLARE_TEST_VARS;
+
+ unsigned long test_mtu = 1442;
+
+ c = presetup_ipv4(tc);
+
+ /* Create IPv4 subnetwork with smaller prefix */
+ struct sockaddr_in mask4;
+ struct sockaddr_in net4;
+ struct sockaddr_in gw4;
+ prepare_v4_network(c, &net4, &mask4, &gw4);
+
+ prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4,
+ (struct sockaddr *)&mask4, (struct sockaddr *)&gw4);
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+
+ /* Change MTU */
+ prepare_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net4,
+ (struct sockaddr *)&mask4, NULL);
+ rtm->rtm_inits |= RTV_MTU;
+ rtm->rtm_rmx.rmx_mtu = test_mtu;
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+
+ verify_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net4,
+ (struct sockaddr *)&mask4, NULL);
+
+ RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_rmx.rmx_mtu == test_mtu,
+ "expected mtu: %lu, got %lu", test_mtu, rtm->rtm_rmx.rmx_mtu);
+
+ /* Verify the change has actually taken place */
+ prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&net4,
+ (struct sockaddr *)&mask4, NULL);
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+
+ RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_rmx.rmx_mtu == test_mtu,
+ "expected mtu: %lu, got %lu", test_mtu, rtm->rtm_rmx.rmx_mtu);
+}
+
+RTM_DECLARE_ROOT_TEST(rtm_change_v4_flags_success,
+ "Tests IPv4 path flags change");
+
+ATF_TC_BODY(rtm_change_v4_flags_success, tc)
+{
+ DECLARE_TEST_VARS;
+
+ uint32_t test_flags = RTF_PROTO1 | RTF_PROTO2 | RTF_PROTO3 | RTF_STATIC;
+ uint32_t desired_flags;
+
+ c = presetup_ipv4(tc);
+
+ /* Create IPv4 subnetwork with smaller prefix */
+ struct sockaddr_in mask4;
+ struct sockaddr_in net4;
+ struct sockaddr_in gw4;
+ prepare_v4_network(c, &net4, &mask4, &gw4);
+
+ prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4,
+ (struct sockaddr *)&mask4, (struct sockaddr *)&gw4);
+
+ /* Set test flags during route addition */
+ desired_flags = RTF_UP | RTF_DONE | RTF_GATEWAY | test_flags;
+ rtm->rtm_flags |= test_flags;
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+
+ /* Change flags */
+ prepare_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net4,
+ (struct sockaddr *)&mask4, NULL);
+ rtm->rtm_flags &= ~test_flags;
+ desired_flags &= ~test_flags;
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+
+ /* Verify updated flags */
+ verify_route_message_extra(rtm, c->ifindex, desired_flags | RTF_DONE);
+
+ /* Verify the change has actually taken place */
+ prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&net4,
+ (struct sockaddr *)&mask4, NULL);
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+
+ verify_route_message_extra(rtm, c->ifindex, desired_flags | RTF_DONE);
+}
+
+
+ATF_TC_WITH_CLEANUP(rtm_add_v6_gu_gw_gu_direct_success);
+ATF_TC_HEAD(rtm_add_v6_gu_gw_gu_direct_success, tc)
+{
+ DESCRIBE_ROOT_TEST("Tests IPv6 global unicast prefix addition with directly-reachable GU GW");
+}
+
+ATF_TC_BODY(rtm_add_v6_gu_gw_gu_direct_success, tc)
+{
+ DECLARE_TEST_VARS;
+
+ c = presetup_ipv6(tc);
+
+ /* Create IPv6 subnetwork with smaller prefix */
+ struct sockaddr_in6 mask6;
+ struct sockaddr_in6 net6;
+ struct sockaddr_in6 gw6;
+ prepare_v6_network(c, &net6, &mask6, &gw6);
+
+ prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net6,
+ (struct sockaddr *)&mask6, (struct sockaddr *)&gw6);
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+
+ /*
+ * RTM_ADD: Add Route: len 200, pid: 46068, seq 42, errno 0, flags:<GATEWAY,DONE,STATIC>
+ * locks: inits:
+ * sockaddrs: <DST,GATEWAY,NETMASK>
+ * 192.0.2.0 192.0.2.254 255.255.255.128
+ */
+
+ verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&net6,
+ (struct sockaddr *)&mask6, (struct sockaddr *)&gw6);
+
+ verify_route_message_extra(rtm, c->ifindex,
+ RTF_UP | RTF_DONE | RTF_GATEWAY | RTF_STATIC);
+}
+
+ATF_TC_CLEANUP(rtm_add_v6_gu_gw_gu_direct_success, tc)
+{
+ CLEANUP_AFTER_TEST;
+}
+
+ATF_TC_WITH_CLEANUP(rtm_del_v6_gu_prefix_nogw_success);
+ATF_TC_HEAD(rtm_del_v6_gu_prefix_nogw_success, tc)
+{
+
+ DESCRIBE_ROOT_TEST("Tests IPv6 global unicast prefix removal without specifying gateway");
+}
+
+ATF_TC_BODY(rtm_del_v6_gu_prefix_nogw_success, tc)
+{
+ DECLARE_TEST_VARS;
+
+ c = presetup_ipv6(tc);
+
+ /* Create IPv6 subnetwork with smaller prefix */
+ struct sockaddr_in6 mask6;
+ struct sockaddr_in6 net6;
+ struct sockaddr_in6 gw6;
+ prepare_v6_network(c, &net6, &mask6, &gw6);
+
+ prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net6,
+ (struct sockaddr *)&mask6, (struct sockaddr *)&gw6);
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+
+ /* Route has been added successfully, try to delete it */
+ prepare_route_message(rtm, RTM_DELETE, (struct sockaddr *)&net6,
+ (struct sockaddr *)&mask6, NULL);
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+
+ /*
+ * RTM_DELETE: Delete Route: len 200, pid: 46417, seq 43, errno 0, flags: <GATEWAY,DONE,STATIC>
+ * sockaddrs: 0x7 <DST,GATEWAY,NETMASK>
+ * af=inet len=16 addr=192.0.2.0 hd={10, 02, 00{2}, C0, 00, 02, 00{9}}
+ * af=inet len=16 addr=192.0.2.254 hd={10, 02, 00{2}, C0, 00, 02, FE, 00{8}}
+ * af=inet len=16 addr=255.255.255.128 hd={10, 02, FF{5}, 80, 00{8}}
+ */
+
+ verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&net6,
+ (struct sockaddr *)&mask6, (struct sockaddr *)&gw6);
+ verify_route_message_extra(rtm, c->ifindex, RTF_DONE | RTF_GATEWAY | RTF_STATIC);
+}
+
+ATF_TC_CLEANUP(rtm_del_v6_gu_prefix_nogw_success, tc)
+{
+ CLEANUP_AFTER_TEST;
+}
+
+RTM_DECLARE_ROOT_TEST(rtm_change_v6_gw_success,
+ "Tests IPv6 gateway change");
+
+ATF_TC_BODY(rtm_change_v6_gw_success, tc)
+{
+ DECLARE_TEST_VARS;
+ struct rtsock_config_options co;
+
+ bzero(&co, sizeof(co));
+ co.num_interfaces = 2;
+
+ c = config_setup(tc, &co);
+ jump_vnet(c, tc);
+
+ ret = iface_turn_up(c->ifnames[0]);
+ ATF_REQUIRE_MSG(ret == 0, "Unable to turn up %s", c->ifnames[0]);
+ ret = iface_turn_up(c->ifnames[1]);
+ ATF_REQUIRE_MSG(ret == 0, "Unable to turn up %s", c->ifnames[1]);
+
+ ret = iface_enable_ipv6(c->ifnames[0]);
+ ATF_REQUIRE_MSG(ret == 0, "Unable to enable IPv6 on %s", c->ifnames[0]);
+ ret = iface_enable_ipv6(c->ifnames[1]);
+ ATF_REQUIRE_MSG(ret == 0, "Unable to enable IPv6 on %s", c->ifnames[1]);
+
+ ret = iface_setup_addr(c->ifnames[0], c->addr6_str, c->plen6);
+ ATF_REQUIRE_MSG(ret == 0, "ifconfig failed");
+
+ ret = iface_setup_addr(c->ifnames[1], "2001:DB8:4242::1", 64);
+ ATF_REQUIRE_MSG(ret == 0, "ifconfig failed");
+
+ c->rtsock_fd = rtsock_setup_socket();
+
+ /* Create IPv6 subnetwork with smaller prefix */
+ struct sockaddr_in6 mask6;
+ struct sockaddr_in6 net6;
+ struct sockaddr_in6 gw6;
+ prepare_v6_network(c, &net6, &mask6, &gw6);
+
+ prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net6,
+ (struct sockaddr *)&mask6, (struct sockaddr *)&gw6);
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+
+ verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&net6,
+ (struct sockaddr *)&mask6, (struct sockaddr *)&gw6);
+
+ /* Change gateway to the one on residing on the other interface */
+ inet_pton(AF_INET6, "2001:DB8:4242::4242", &gw6.sin6_addr);
+ prepare_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net6,
+ (struct sockaddr *)&mask6, (struct sockaddr *)&gw6);
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+
+ verify_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net6,
+ (struct sockaddr *)&mask6, (struct sockaddr *)&gw6);
+
+ verify_route_message_extra(rtm, if_nametoindex(c->ifnames[1]),
+ RTF_UP | RTF_DONE | RTF_GATEWAY | RTF_STATIC);
+
+ /* Verify the change has actually taken place */
+ prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&net6,
+ (struct sockaddr *)&mask6, NULL);
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+
+ /*
+ * RTM_GET: len 248, pid: 2268, seq 44, errno 0, flags: <UP,GATEWAY,DONE,STATIC>
+ * sockaddrs: 0x7 <DST,GATEWAY,NETMASK>
+ * af=inet6 len=28 addr=2001:db8:: hd={x1C, x1C, x00{6}, x20, x01, x0D, xB8, x00{16}}
+ * af=inet6 len=28 addr=2001:db8:4242::4242 hd={x1C, x1C, x00{6}, x20, x01, x0D, xB8, x42, x42, x00{8}, x42, x42, x00{4}}
+ * af=inet6 len=28 addr=ffff:ffff:8000:: hd={x1C, x1C, xFF, xFF, xFF, xFF, xFF, xFF, xFF, xFF, xFF, xFF, x80, x00{15}}
+ */
+
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+ verify_route_message_extra(rtm, if_nametoindex(c->ifnames[1]),
+ RTF_UP | RTF_DONE | RTF_GATEWAY | RTF_STATIC);
+}
+
+RTM_DECLARE_ROOT_TEST(rtm_change_v6_mtu_success,
+ "Tests IPv6 path mtu change");
+
+ATF_TC_BODY(rtm_change_v6_mtu_success, tc)
+{
+ DECLARE_TEST_VARS;
+
+ unsigned long test_mtu = 1442;
+
+ c = presetup_ipv6(tc);
+
+ /* Create IPv6 subnetwork with smaller prefix */
+ struct sockaddr_in6 mask6;
+ struct sockaddr_in6 net6;
+ struct sockaddr_in6 gw6;
+ prepare_v6_network(c, &net6, &mask6, &gw6);
+
+ prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net6,
+ (struct sockaddr *)&mask6, (struct sockaddr *)&gw6);
+
+ /* Send route add */
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+
+ /* Change MTU */
+ prepare_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net6,
+ (struct sockaddr *)&mask6, NULL);
+ rtm->rtm_inits |= RTV_MTU;
+ rtm->rtm_rmx.rmx_mtu = test_mtu;
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+
+ verify_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net6,
+ (struct sockaddr *)&mask6, NULL);
+
+ RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_rmx.rmx_mtu == test_mtu,
+ "expected mtu: %lu, got %lu", test_mtu, rtm->rtm_rmx.rmx_mtu);
+
+ /* Verify the change has actually taken place */
+ prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&net6,
+ (struct sockaddr *)&mask6, NULL);
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+
+ RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_rmx.rmx_mtu == test_mtu,
+ "expected mtu: %lu, got %lu", test_mtu, rtm->rtm_rmx.rmx_mtu);
+}
+
+RTM_DECLARE_ROOT_TEST(rtm_change_v6_flags_success,
+ "Tests IPv6 path flags change");
+
+ATF_TC_BODY(rtm_change_v6_flags_success, tc)
+{
+ DECLARE_TEST_VARS;
+
+ uint32_t test_flags = RTF_PROTO1 | RTF_PROTO2 | RTF_PROTO3 | RTF_STATIC;
+ uint32_t desired_flags;
+
+ c = presetup_ipv6(tc);
+
+ /* Create IPv6 subnetwork with smaller prefix */
+ struct sockaddr_in6 mask6;
+ struct sockaddr_in6 net6;
+ struct sockaddr_in6 gw6;
+ prepare_v6_network(c, &net6, &mask6, &gw6);
+
+ prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net6,
+ (struct sockaddr *)&mask6, (struct sockaddr *)&gw6);
+
+ /* Set test flags during route addition */
+ desired_flags = RTF_UP | RTF_DONE | RTF_GATEWAY | test_flags;
+ rtm->rtm_flags |= test_flags;
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+
+ /* Change flags */
+ prepare_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net6,
+ (struct sockaddr *)&mask6, NULL);
+ rtm->rtm_flags &= ~test_flags;
+ desired_flags &= ~test_flags;
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+
+ /* Verify updated flags */
+ verify_route_message_extra(rtm, c->ifindex, desired_flags | RTF_DONE);
+
+ /* Verify the change has actually taken place */
+ prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&net6,
+ (struct sockaddr *)&mask6, NULL);
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+
+ verify_route_message_extra(rtm, c->ifindex, desired_flags | RTF_DONE);
+}
+
+ATF_TC_WITH_CLEANUP(rtm_add_v4_temporal1_success);
+ATF_TC_HEAD(rtm_add_v4_temporal1_success, tc)
+{
+ DESCRIBE_ROOT_TEST("Tests IPv4 route expiration with expire time set");
+}
+
+ATF_TC_BODY(rtm_add_v4_temporal1_success, tc)
+{
+ DECLARE_TEST_VARS;
+
+ c = presetup_ipv4(tc);
+
+ /* Create IPv4 subnetwork with smaller prefix */
+ struct sockaddr_in mask4;
+ struct sockaddr_in net4;
+ struct sockaddr_in gw4;
+ prepare_v4_network(c, &net4, &mask4, &gw4);
+
+ prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4,
+ (struct sockaddr *)&mask4, (struct sockaddr *)&gw4);
+
+ /* Set expire time to now */
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ rtm->rtm_rmx.rmx_expire = tv.tv_sec - 1;
+ rtm->rtm_inits |= RTV_EXPIRE;
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+ ATF_REQUIRE_MSG(rtm != NULL, "unable to get rtsock reply for RTM_ADD");
+ RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_inits & RTV_EXPIRE, "RTV_EXPIRE not set");
+
+ /* The next should be route deletion */
+ rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer));
+
+ verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&net4,
+ (struct sockaddr *)&mask4, (struct sockaddr *)&gw4);
+
+ verify_route_message_extra(rtm, c->ifindex,
+ RTF_DONE | RTF_GATEWAY | RTF_STATIC);
+}
+
+ATF_TC_CLEANUP(rtm_add_v4_temporal1_success, tc)
+{
+ CLEANUP_AFTER_TEST;
+}
+
+ATF_TC_WITH_CLEANUP(rtm_add_v6_temporal1_success);
+ATF_TC_HEAD(rtm_add_v6_temporal1_success, tc)
+{
+ DESCRIBE_ROOT_TEST("Tests IPv6 global unicast prefix addition with directly-reachable GU GW");
+}
+
+ATF_TC_BODY(rtm_add_v6_temporal1_success, tc)
+{
+ DECLARE_TEST_VARS;
+
+ c = presetup_ipv6(tc);
+
+ /* Create IPv6 subnetwork with smaller prefix */
+ struct sockaddr_in6 mask6;
+ struct sockaddr_in6 net6;
+ struct sockaddr_in6 gw6;
+ prepare_v6_network(c, &net6, &mask6, &gw6);
+
+ prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net6,
+ (struct sockaddr *)&mask6, (struct sockaddr *)&gw6);
+
+ /* Set expire time to now */
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ rtm->rtm_rmx.rmx_expire = tv.tv_sec - 1;
+ rtm->rtm_inits |= RTV_EXPIRE;
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+ ATF_REQUIRE_MSG(rtm != NULL, "unable to get rtsock reply for RTM_ADD");
+ RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_inits & RTV_EXPIRE, "RTV_EXPIRE not set");
+
+ /* The next should be route deletion */
+ rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer));
+
+ verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&net6,
+ (struct sockaddr *)&mask6, (struct sockaddr *)&gw6);
+
+ verify_route_message_extra(rtm, c->ifindex,
+ RTF_DONE | RTF_GATEWAY | RTF_STATIC);
+}
+
+ATF_TC_CLEANUP(rtm_add_v6_temporal1_success, tc)
+{
+ CLEANUP_AFTER_TEST;
+}
+
+/* Interface address messages tests */
+
+RTM_DECLARE_ROOT_TEST(rtm_add_v6_gu_ifa_hostroute_success,
+ "Tests validness for /128 host route announce after ifaddr assignment");
+
+ATF_TC_BODY(rtm_add_v6_gu_ifa_hostroute_success, tc)
+{
+ DECLARE_TEST_VARS;
+
+ c = presetup_ipv6_iface(tc);
+
+ c->rtsock_fd = rtsock_setup_socket();
+
+ ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6);
+
+ /*
+ * There will be multiple.
+ * RTM_ADD without llinfo.
+ */
+
+ while (true) {
+ rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer));
+ if ((rtm->rtm_type == RTM_ADD) && ((rtm->rtm_flags & RTF_LLINFO) == 0))
+ break;
+ }
+ /* This should be a message for the host route */
+
+ verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&c->addr6, NULL, NULL);
+ rtsock_validate_pid_kernel(rtm);
+ /* No netmask should be set */
+ RTSOCK_ATF_REQUIRE_MSG(rtm, rtsock_find_rtm_sa(rtm, RTA_NETMASK) == NULL, "netmask is set");
+
+ /* gateway should be link sdl with ifindex of an address interface */
+ verify_link_gateway(rtm, c->ifindex);
+
+ int expected_rt_flags = RTF_UP | RTF_HOST | RTF_DONE | RTF_STATIC | RTF_PINNED;
+ verify_route_message_extra(rtm, if_nametoindex("lo0"), expected_rt_flags);
+}
+
+RTM_DECLARE_ROOT_TEST(rtm_add_v6_gu_ifa_prefixroute_success,
+ "Tests validness for the prefix route announce after ifaddr assignment");
+
+ATF_TC_BODY(rtm_add_v6_gu_ifa_prefixroute_success, tc)
+{
+ DECLARE_TEST_VARS;
+
+ c = presetup_ipv6_iface(tc);
+
+ c->rtsock_fd = rtsock_setup_socket();
+
+ ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6);
+
+ /*
+ * Multiple RTM_ADD messages will be generated:
+ * 1) lladdr mapping (RTF_LLDATA)
+ * 2) host route (one w/o netmask)
+ * 3) prefix route
+ */
+
+ while (true) {
+ rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer));
+ /* Find RTM_ADD with netmask - this should skip both host route and LLADDR */
+ if ((rtm->rtm_type == RTM_ADD) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK)))
+ break;
+ }
+
+ /* This should be a message for the prefix route */
+ verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&c->net6,
+ (struct sockaddr *)&c->mask6, NULL);
+
+ /* gateway should be link sdl with ifindex of an address interface */
+ verify_link_gateway(rtm, c->ifindex);
+
+ int expected_rt_flags = RTF_UP | RTF_DONE | RTF_PINNED;
+ verify_route_message_extra(rtm, c->ifindex, expected_rt_flags);
+}
+
+RTM_DECLARE_ROOT_TEST(rtm_add_v6_gu_ifa_ordered_success,
+ "Tests ordering of the messages for IPv6 global unicast ifaddr assignment");
+
+ATF_TC_BODY(rtm_add_v6_gu_ifa_ordered_success, tc)
+{
+ DECLARE_TEST_VARS;
+
+ c = presetup_ipv6_iface(tc);
+
+ c->rtsock_fd = rtsock_setup_socket();
+
+ ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6);
+
+ int count = 0, tries = 0;
+
+ enum msgtype {
+ MSG_IFADDR,
+ MSG_HOSTROUTE,
+ MSG_PREFIXROUTE,
+ MSG_MAX,
+ };
+
+ int msg_array[MSG_MAX];
+
+ bzero(msg_array, sizeof(msg_array));
+
+ while (count < 3 && tries < 20) {
+ rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer));
+ tries++;
+ /* Classify */
+ if (rtm->rtm_type == RTM_NEWADDR) {
+ RLOG("MSG_IFADDR: %d", count);
+ msg_array[MSG_IFADDR] = count++;
+ continue;
+ }
+
+ /* Find RTM_ADD with netmask - this should skip both host route and LLADDR */
+ if ((rtm->rtm_type == RTM_ADD) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK))) {
+ RLOG("MSG_PREFIXROUTE: %d", count);
+ msg_array[MSG_PREFIXROUTE] = count++;
+ continue;
+ }
+
+ if ((rtm->rtm_type == RTM_ADD) && ((rtm->rtm_flags & RTF_LLDATA) == 0)) {
+ RLOG("MSG_HOSTROUTE: %d", count);
+ msg_array[MSG_HOSTROUTE] = count++;
+ continue;
+ }
+
+ RLOG("skipping msg type %s, try: %d", rtsock_print_cmdtype(rtm->rtm_type),
+ tries);
+ }
+
+ /* TODO: verify multicast */
+ ATF_REQUIRE_MSG(count == 3, "Received only %d/3 messages", count);
+ ATF_REQUIRE_MSG(msg_array[MSG_IFADDR] == 0, "ifaddr message is not the first");
+}
+
+RTM_DECLARE_ROOT_TEST(rtm_del_v6_gu_ifa_hostroute_success,
+ "Tests validness for /128 host route removal after ifaddr removal");
+
+ATF_TC_BODY(rtm_del_v6_gu_ifa_hostroute_success, tc)
+{
+ DECLARE_TEST_VARS;
+
+ c = presetup_ipv6_iface(tc);
+
+ ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6);
+
+ c->rtsock_fd = rtsock_setup_socket();
+
+ ret = iface_delete_addr(c->ifname, c->addr6_str);
+
+ while (true) {
+ rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer));
+ if ((rtm->rtm_type == RTM_DELETE) &&
+ ((rtm->rtm_flags & RTF_LLINFO) == 0) &&
+ rtsock_find_rtm_sa(rtm, RTA_NETMASK) == NULL)
+ break;
+ }
+ /* This should be a message for the host route */
+
+ verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&c->addr6, NULL, NULL);
+ rtsock_validate_pid_kernel(rtm);
+ /* No netmask should be set */
+ RTSOCK_ATF_REQUIRE_MSG(rtm, rtsock_find_rtm_sa(rtm, RTA_NETMASK) == NULL, "netmask is set");
+
+ /* gateway should be link sdl with ifindex of an address interface */
+ verify_link_gateway(rtm, c->ifindex);
+
+ /* XXX: consider passing ifindex in rtm_index as done in RTM_ADD. */
+ int expected_rt_flags = RTF_HOST | RTF_DONE | RTF_STATIC | RTF_PINNED;
+ RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_flags == expected_rt_flags,
+ "expected rtm flags: 0x%X, got 0x%X", expected_rt_flags, rtm->rtm_flags);
+}
+
+RTM_DECLARE_ROOT_TEST(rtm_del_v6_gu_ifa_prefixroute_success,
+ "Tests validness for the prefix route removal after ifaddr assignment");
+
+ATF_TC_BODY(rtm_del_v6_gu_ifa_prefixroute_success, tc)
+{
+ DECLARE_TEST_VARS;
+
+ c = presetup_ipv6_iface(tc);
+
+ ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6);
+
+ c->rtsock_fd = rtsock_setup_socket();
+
+ ret = iface_delete_addr(c->ifname, c->addr6_str);
+
+ while (true) {
+ rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer));
+ /* Find RTM_DELETE with netmask - this should skip both host route and LLADDR */
+ if ((rtm->rtm_type == RTM_DELETE) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK)))
+ break;
+ }
+
+ /* This should be a message for the prefix route */
+ verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&c->net6,
+ (struct sockaddr *)&c->mask6, NULL);
+
+ /* gateway should be link sdl with ifindex of an address interface */
+ verify_link_gateway(rtm, c->ifindex);
+
+ int expected_rt_flags = RTF_DONE | RTF_PINNED;
+ verify_route_message_extra(rtm, c->ifindex, expected_rt_flags);
+}
+
+RTM_DECLARE_ROOT_TEST(rtm_add_v4_gu_ifa_prefixroute_success,
+ "Tests validness for the prefix route announce after ifaddr assignment");
+
+ATF_TC_BODY(rtm_add_v4_gu_ifa_prefixroute_success, tc)
+{
+ DECLARE_TEST_VARS;
+
+ c = presetup_ipv4_iface(tc);
+
+ c->rtsock_fd = rtsock_setup_socket();
+
+ ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6);
+
+ /*
+ * Multiple RTM_ADD messages will be generated:
+ * 1) lladdr mapping (RTF_LLDATA)
+ * 3) prefix route
+ */
+
+ while (true) {
+ rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer));
+ /* Find RTM_ADD with netmask - this should skip both host route and LLADDR */
+ if ((rtm->rtm_type == RTM_ADD) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK)))
+ break;
+ }
+
+ /* This should be a message for the prefix route */
+ verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&c->net4,
+ (struct sockaddr *)&c->mask4, NULL);
+
+ /* gateway should be link sdl with ifindex of an address interface */
+ verify_link_gateway(rtm, c->ifindex);
+
+ int expected_rt_flags = RTF_UP | RTF_DONE | RTF_PINNED;
+ verify_route_message_extra(rtm, c->ifindex, expected_rt_flags);
+}
+
+RTM_DECLARE_ROOT_TEST(rtm_add_v4_gu_ifa_ordered_success,
+ "Tests ordering of the messages for IPv4 unicast ifaddr assignment");
+
+ATF_TC_BODY(rtm_add_v4_gu_ifa_ordered_success, tc)
+{
+ DECLARE_TEST_VARS;
+
+ c = presetup_ipv4_iface(tc);
+
+ c->rtsock_fd = rtsock_setup_socket();
+
+ ret = iface_setup_addr(c->ifname, c->addr4_str, c->plen4);
+
+ int count = 0, tries = 0;
+
+ enum msgtype {
+ MSG_IFADDR,
+ MSG_PREFIXROUTE,
+ MSG_MAX,
+ };
+
+ int msg_array[MSG_MAX];
+
+ bzero(msg_array, sizeof(msg_array));
+
+ while (count < 2 && tries < 20) {
+ rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer));
+ tries++;
+ /* Classify */
+ if (rtm->rtm_type == RTM_NEWADDR) {
+ RLOG("MSG_IFADDR: %d", count);
+ msg_array[MSG_IFADDR] = count++;
+ continue;
+ }
+
+ /* Find RTM_ADD with netmask - this should skip both host route and LLADDR */
+ if ((rtm->rtm_type == RTM_ADD) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK))) {
+ RLOG("MSG_PREFIXROUTE: %d", count);
+ msg_array[MSG_PREFIXROUTE] = count++;
+ continue;
+ }
+
+ RLOG("skipping msg type %s, try: %d", rtsock_print_cmdtype(rtm->rtm_type),
+ tries);
+ }
+
+ /* TODO: verify multicast */
+ ATF_REQUIRE_MSG(count == 2, "Received only %d/2 messages", count);
+ ATF_REQUIRE_MSG(msg_array[MSG_IFADDR] == 0, "ifaddr message is not the first");
+}
+
+RTM_DECLARE_ROOT_TEST(rtm_del_v4_gu_ifa_prefixroute_success,
+ "Tests validness for the prefix route removal after ifaddr assignment");
+
+ATF_TC_BODY(rtm_del_v4_gu_ifa_prefixroute_success, tc)
+{
+ DECLARE_TEST_VARS;
+
+ c = presetup_ipv4_iface(tc);
+
+
+ ret = iface_setup_addr(c->ifname, c->addr4_str, c->plen4);
+
+ c->rtsock_fd = rtsock_setup_socket();
+
+ ret = iface_delete_addr(c->ifname, c->addr4_str);
+
+ while (true) {
+ rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer));
+ /* Find RTM_ADD with netmask - this should skip both host route and LLADDR */
+ if ((rtm->rtm_type == RTM_DELETE) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK)))
+ break;
+ }
+
+ /* This should be a message for the prefix route */
+ verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&c->net4,
+ (struct sockaddr *)&c->mask4, NULL);
+
+ /* gateway should be link sdl with ifindex of an address interface */
+ verify_link_gateway(rtm, c->ifindex);
+
+ int expected_rt_flags = RTF_DONE | RTF_PINNED;
+ verify_route_message_extra(rtm, c->ifindex, expected_rt_flags);
+}
+
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, rtm_get_v4_exact_success);
+ ATF_TP_ADD_TC(tp, rtm_get_v4_lpm_success);
+ ATF_TP_ADD_TC(tp, rtm_get_v4_hostbits_success);
+ ATF_TP_ADD_TC(tp, rtm_get_v4_empty_dst_failure);
+ ATF_TP_ADD_TC(tp, rtm_add_v4_no_rtf_host_success);
+ ATF_TP_ADD_TC(tp, rtm_add_v4_gw_direct_success);
+ ATF_TP_ADD_TC(tp, rtm_del_v4_prefix_nogw_success);
+ ATF_TP_ADD_TC(tp, rtm_add_v6_gu_gw_gu_direct_success);
+ ATF_TP_ADD_TC(tp, rtm_del_v6_gu_prefix_nogw_success);
+ ATF_TP_ADD_TC(tp, rtm_change_v4_gw_success);
+ ATF_TP_ADD_TC(tp, rtm_change_v4_mtu_success);
+ ATF_TP_ADD_TC(tp, rtm_change_v4_flags_success);
+ ATF_TP_ADD_TC(tp, rtm_change_v6_gw_success);
+ ATF_TP_ADD_TC(tp, rtm_change_v6_mtu_success);
+ ATF_TP_ADD_TC(tp, rtm_change_v6_flags_success);
+ /* ifaddr tests */
+ ATF_TP_ADD_TC(tp, rtm_add_v6_gu_ifa_hostroute_success);
+ ATF_TP_ADD_TC(tp, rtm_add_v6_gu_ifa_prefixroute_success);
+ ATF_TP_ADD_TC(tp, rtm_add_v6_gu_ifa_ordered_success);
+ ATF_TP_ADD_TC(tp, rtm_del_v6_gu_ifa_hostroute_success);
+ ATF_TP_ADD_TC(tp, rtm_del_v6_gu_ifa_prefixroute_success);
+ ATF_TP_ADD_TC(tp, rtm_add_v4_gu_ifa_ordered_success);
+ ATF_TP_ADD_TC(tp, rtm_del_v4_gu_ifa_prefixroute_success);
+ /* temporal routes */
+ ATF_TP_ADD_TC(tp, rtm_add_v4_temporal1_success);
+ ATF_TP_ADD_TC(tp, rtm_add_v6_temporal1_success);
+
+ return (atf_no_error());
+}
+
diff --git a/tests/sys/net/routing/test_rtsock_lladdr.c b/tests/sys/net/routing/test_rtsock_lladdr.c
new file mode 100644
index 000000000000..072a4e31f7ce
--- /dev/null
+++ b/tests/sys/net/routing/test_rtsock_lladdr.c
@@ -0,0 +1,416 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Alexander V. Chernikov
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "rtsock_common.h"
+#include "rtsock_config.h"
+
+static void
+jump_vnet(struct rtsock_test_config *c, const atf_tc_t *tc)
+{
+ char vnet_name[512];
+
+ snprintf(vnet_name, sizeof(vnet_name), "vt-%s", atf_tc_get_ident(tc));
+ RLOG("jumping to %s", vnet_name);
+
+ vnet_switch_one(vnet_name, c->ifname);
+
+ /* Update ifindex cache */
+ c->ifindex = if_nametoindex(c->ifname);
+}
+
+static inline struct rtsock_test_config *
+presetup_ipv6(const atf_tc_t *tc)
+{
+ struct rtsock_test_config *c;
+ int ret;
+
+ c = config_setup(tc, NULL);
+
+ jump_vnet(c, tc);
+
+ ret = iface_turn_up(c->ifname);
+ ATF_REQUIRE_MSG(ret == 0, "Unable to turn up %s", c->ifname);
+ ret = iface_enable_ipv6(c->ifname);
+ ATF_REQUIRE_MSG(ret == 0, "Unable to enable IPv6 on %s", c->ifname);
+
+ c->rtsock_fd = rtsock_setup_socket();
+
+ return (c);
+}
+
+static inline struct rtsock_test_config *
+presetup_ipv4(const atf_tc_t *tc)
+{
+ struct rtsock_test_config *c;
+ int ret;
+
+ c = config_setup(tc, NULL);
+
+ jump_vnet(c, tc);
+
+ /* assumes ifconfig doing IFF_UP */
+ ret = iface_setup_addr(c->ifname, c->addr4_str, c->plen4);
+ ATF_REQUIRE_MSG(ret == 0, "ifconfig failed");
+
+ c->rtsock_fd = rtsock_setup_socket();
+
+ return (c);
+}
+
+static void
+prepare_route_message(struct rt_msghdr *rtm, int cmd, struct sockaddr *dst,
+ struct sockaddr *gw)
+{
+
+ rtsock_prepare_route_message(rtm, cmd, dst, NULL, gw);
+
+ rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA);
+}
+
+/* TESTS */
+#define DECLARE_TEST_VARS \
+ char buffer[2048], msg[512]; \
+ ssize_t len; \
+ int ret; \
+ struct rtsock_test_config *c; \
+ struct rt_msghdr *rtm = (struct rt_msghdr *)buffer; \
+ struct sockaddr *sa; \
+ \
+
+#define DECLARE_CLEANUP_VARS \
+ struct rtsock_test_config *c = config_setup(tc); \
+ \
+
+#define DESCRIBE_ROOT_TEST(_msg) config_describe_root_test(tc, _msg)
+#define CLEANUP_AFTER_TEST config_generic_cleanup(tc)
+
+#define RTM_DECLARE_ROOT_TEST(_name, _descr) \
+ATF_TC_WITH_CLEANUP(_name); \
+ATF_TC_HEAD(_name, tc) \
+{ \
+ DESCRIBE_ROOT_TEST(_descr); \
+} \
+ATF_TC_CLEANUP(_name, tc) \
+{ \
+ CLEANUP_AFTER_TEST; \
+}
+
+RTM_DECLARE_ROOT_TEST(rtm_add_v6_ll_lle_success, "Tests addition of link-local IPv6 ND entry");
+ATF_TC_BODY(rtm_add_v6_ll_lle_success, tc)
+{
+ DECLARE_TEST_VARS;
+
+ c = presetup_ipv6(tc);
+
+ char str_buf[128];
+ struct sockaddr_in6 sin6;
+ /* Interface here is optional. XXX: verify kernel side. */
+ char *v6addr = "fe80::4242:4242";
+ snprintf(str_buf, sizeof(str_buf), "%s%%%s", v6addr, c->ifname);
+ sa_convert_str_to_sa(str_buf, (struct sockaddr *)&sin6);
+
+ struct sockaddr_dl ether;
+ snprintf(str_buf, sizeof(str_buf), "%s%%%s", c->remote_lladdr, c->ifname);
+ sa_convert_str_to_sa(str_buf, (struct sockaddr *)&ether);
+
+ prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&sin6, (struct sockaddr *)&ether);
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+
+ /*
+ * Got message of size 240 on 2019-12-17 15:06:51
+ * RTM_ADD: Add Route: len 240, pid: 0, seq 0, errno 0, flags: <UP,HOST,DONE,LLINFO>
+ * sockaddrs: 0x3 <DST,GATEWAY>
+ * af=inet6 len=28 addr=fe80::4242:4242 scope_id=3 if_name=tap4242
+ * af=link len=54 sdl_index=3 if_name=tap4242 addr=52:54:00:14:E3:10
+ */
+
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+
+ sa = rtsock_find_rtm_sa(rtm, RTA_DST);
+ ret = sa_equal_msg(sa, (struct sockaddr *)&sin6, msg, sizeof(msg));
+ RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "DST sa diff: %s", msg);
+
+ sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY);
+ int sa_flags = SA_F_IGNORE_IFNAME | SA_F_IGNORE_IFTYPE | SA_F_IGNORE_MEMCMP;
+ ret = sa_equal_msg_flags(sa, (struct sockaddr *)&ether, msg, sizeof(msg), sa_flags);
+ RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "GATEWAY sa diff: %s", msg);
+
+#if 0
+ /* Disable the check until https://reviews.freebsd.org/D22003 merge */
+ /* Some additional checks to verify kernel has filled in interface data */
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;
+ RTSOCK_ATF_REQUIRE_MSG(rtm, sdl->sdl_type > 0, "sdl_type not set");
+#endif
+}
+
+RTM_DECLARE_ROOT_TEST(rtm_add_v6_gu_lle_success, "Tests addition of global IPv6 ND entry");
+ATF_TC_BODY(rtm_add_v6_gu_lle_success, tc)
+{
+ DECLARE_TEST_VARS;
+
+ c = presetup_ipv6(tc);
+
+ char str_buf[128];
+
+ struct sockaddr_in6 sin6;
+ sin6 = c->net6;
+#define _s6_addr32 __u6_addr.__u6_addr32
+ sin6.sin6_addr._s6_addr32[3] = htonl(0x42424242);
+#undef _s6_addr32
+
+ struct sockaddr_dl ether;
+ snprintf(str_buf, sizeof(str_buf), "%s%%%s", c->remote_lladdr, c->ifname);
+ sa_convert_str_to_sa(str_buf, (struct sockaddr *)&ether);
+
+ prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&sin6, (struct sockaddr *)&ether);
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+
+ /*
+ * Got message of size 240 on 2019-12-17 14:56:43
+ * RTM_ADD: Add Route: len 240, pid: 0, seq 0, errno 0, flags: <UP,HOST,DONE,LLINFO>
+ * sockaddrs: 0x3 <DST,GATEWAY>
+ * af=inet6 len=28 addr=2001:db8::4242:4242
+ * af=link len=54 sdl_index=3 if_name=tap4242 addr=52:54:00:14:E3:10
+ */
+
+ /* XXX: where is uRPF?! this should fail */
+
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+
+ sa = rtsock_find_rtm_sa(rtm, RTA_DST);
+ ret = sa_equal_msg(sa, (struct sockaddr *)&sin6, msg, sizeof(msg));
+ RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "DST sa diff: %s", msg);
+
+ sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY);
+ int sa_flags = SA_F_IGNORE_IFNAME | SA_F_IGNORE_IFTYPE | SA_F_IGNORE_MEMCMP;
+ ret = sa_equal_msg_flags(sa, (struct sockaddr *)&ether, msg, sizeof(msg), sa_flags);
+ RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "GATEWAY sa diff: %s", msg);
+
+#if 0
+ /* Disable the check until https://reviews.freebsd.org/D22003 merge */
+ /* Some additional checks to verify kernel has filled in interface data */
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;
+ RTSOCK_ATF_REQUIRE_MSG(rtm, sdl->sdl_type > 0, "sdl_type not set");
+#endif
+}
+
+RTM_DECLARE_ROOT_TEST(rtm_add_v4_gu_lle_success, "Tests addition of IPv4 ARP entry");
+ATF_TC_BODY(rtm_add_v4_gu_lle_success, tc)
+{
+ DECLARE_TEST_VARS;
+
+ c = presetup_ipv4(tc);
+
+ char str_buf[128];
+
+ struct sockaddr_in sin;
+ sin = c->addr4;
+ /* Use the next IPv4 address after self */
+ sin.sin_addr.s_addr = htonl(ntohl(sin.sin_addr.s_addr) + 1);
+
+ struct sockaddr_dl ether;
+ snprintf(str_buf, sizeof(str_buf), "%s%%%s", c->remote_lladdr, c->ifname);
+ sa_convert_str_to_sa(str_buf, (struct sockaddr *)&ether);
+
+ prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&sin, (struct sockaddr *)&ether);
+
+ len = rtsock_send_rtm(c->rtsock_fd, rtm);
+
+ /*
+ * RTM_ADD: Add Route: len 224, pid: 43131, seq 42, errno 0, flags: <HOST,DONE,LLINFO,STATIC>
+ * sockaddrs: 0x3 <DST,GATEWAY>
+ * af=inet len=16 addr=192.0.2.2
+ * af=link len=54 sdl_index=3 if_name=tap4242 addr=52:54:00:14:E3:10
+ */
+
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+
+ sa = rtsock_find_rtm_sa(rtm, RTA_DST);
+ ret = sa_equal_msg(sa, (struct sockaddr *)&sin, msg, sizeof(msg));
+ RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "DST sa diff: %s", msg);
+
+ sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY);
+ int sa_flags = SA_F_IGNORE_IFNAME | SA_F_IGNORE_IFTYPE | SA_F_IGNORE_MEMCMP;
+ ret = sa_equal_msg_flags(sa, (struct sockaddr *)&ether, msg, sizeof(msg), sa_flags);
+ RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "GATEWAY sa diff: %s", msg);
+
+ /*
+ * TODO: Currently kernel code does not set sdl_type, contrary to IPv6.
+ */
+}
+
+RTM_DECLARE_ROOT_TEST(rtm_del_v6_ll_lle_success, "Tests removal of link-local IPv6 ND entry");
+ATF_TC_BODY(rtm_del_v6_ll_lle_success, tc)
+{
+ DECLARE_TEST_VARS;
+
+ c = presetup_ipv6(tc);
+
+ char str_buf[128];
+
+ struct sockaddr_in6 sin6;
+ /* Interface here is optional. XXX: verify kernel side. */
+ char *v6addr = "fe80::4242:4242";
+ snprintf(str_buf, sizeof(str_buf), "%s%%%s", v6addr, c->ifname);
+ sa_convert_str_to_sa(str_buf, (struct sockaddr *)&sin6);
+
+ struct sockaddr_dl ether;
+ snprintf(str_buf, sizeof(str_buf), "%s%%%s", c->remote_lladdr, c->ifname);
+ sa_convert_str_to_sa(str_buf, (struct sockaddr *)&ether);
+
+ prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&sin6, (struct sockaddr *)&ether);
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+
+ /* Successfully added an entry, let's try to remove it. */
+ prepare_route_message(rtm, RTM_DELETE, (struct sockaddr *)&sin6, (struct sockaddr *)&ether);
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+
+ RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_type == RTM_DELETE, "rtm_type is not delete");
+
+ sa = rtsock_find_rtm_sa(rtm, RTA_DST);
+ ret = sa_equal_msg(sa, (struct sockaddr *)&sin6, msg, sizeof(msg));
+ RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "DST sa diff: %s", msg);
+
+ sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY);
+ int sa_flags = SA_F_IGNORE_IFNAME | SA_F_IGNORE_IFTYPE | SA_F_IGNORE_MEMCMP;
+ ret = sa_equal_msg_flags(sa, (struct sockaddr *)&ether, msg, sizeof(msg), sa_flags);
+ RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "GATEWAY sa diff: %s", msg);
+
+ /*
+ * TODO: Currently kernel code does not set sdl_type on delete.
+ */
+}
+
+RTM_DECLARE_ROOT_TEST(rtm_del_v6_gu_lle_success, "Tests removal of global IPv6 ND entry");
+ATF_TC_BODY(rtm_del_v6_gu_lle_success, tc)
+{
+ DECLARE_TEST_VARS;
+
+ c = presetup_ipv6(tc);
+
+ char str_buf[128];
+
+ struct sockaddr_in6 sin6;
+ sin6 = c->net6;
+#define _s6_addr32 __u6_addr.__u6_addr32
+ sin6.sin6_addr._s6_addr32[3] = htonl(0x42424242);
+#undef _s6_addr32
+
+ struct sockaddr_dl ether;
+ snprintf(str_buf, sizeof(str_buf), "%s%%%s", c->remote_lladdr, c->ifname);
+ sa_convert_str_to_sa(str_buf, (struct sockaddr *)&ether);
+
+ prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&sin6, (struct sockaddr *)&ether);
+
+ len = rtsock_send_rtm(c->rtsock_fd, rtm);
+
+ /* Successfully added an entry, let's try to remove it. */
+ prepare_route_message(rtm, RTM_DELETE, (struct sockaddr *)&sin6, (struct sockaddr *)&ether);
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+
+ RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_type == RTM_DELETE, "rtm_type is not delete");
+
+ sa = rtsock_find_rtm_sa(rtm, RTA_DST);
+ ret = sa_equal_msg(sa, (struct sockaddr *)&sin6, msg, sizeof(msg));
+ RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "DST sa diff: %s", msg);
+
+ sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY);
+ int sa_flags = SA_F_IGNORE_IFNAME | SA_F_IGNORE_IFTYPE | SA_F_IGNORE_MEMCMP;
+ ret = sa_equal_msg_flags(sa, (struct sockaddr *)&ether, msg, sizeof(msg), sa_flags);
+ RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "GATEWAY sa diff: %s", msg);
+
+ /*
+ * TODO: Currently kernel code does not set sdl_type on delete.
+ */
+}
+
+RTM_DECLARE_ROOT_TEST(rtm_del_v4_gu_lle_success, "Tests removal of IPv4 ARP entry");
+ATF_TC_BODY(rtm_del_v4_gu_lle_success, tc)
+{
+ DECLARE_TEST_VARS;
+
+ c = presetup_ipv4(tc);
+
+ char str_buf[128];
+
+ struct sockaddr_in sin;
+ sin = c->addr4;
+ /* Use the next IPv4 address after self */
+ sin.sin_addr.s_addr = htonl(ntohl(sin.sin_addr.s_addr) + 1);
+
+ struct sockaddr_dl ether;
+ snprintf(str_buf, sizeof(str_buf), "%s%%%s", c->remote_lladdr, c->ifname);
+ sa_convert_str_to_sa(str_buf, (struct sockaddr *)&ether);
+
+ prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&sin, (struct sockaddr *)&ether);
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+
+ /* We successfully added an entry, let's try to remove it. */
+ prepare_route_message(rtm, RTM_DELETE, (struct sockaddr *)&sin, (struct sockaddr *)&ether);
+
+ rtsock_send_rtm(c->rtsock_fd, rtm);
+
+ rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq);
+
+ RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_type == RTM_DELETE, "rtm_type is not delete");
+
+ sa = rtsock_find_rtm_sa(rtm, RTA_DST);
+ ret = sa_equal_msg(sa, (struct sockaddr *)&sin, msg, sizeof(msg));
+ RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "DST sa diff: %s", msg);
+
+ sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY);
+ int sa_flags = SA_F_IGNORE_IFNAME | SA_F_IGNORE_IFTYPE | SA_F_IGNORE_MEMCMP;
+ ret = sa_equal_msg_flags(sa, (struct sockaddr *)&ether, msg, sizeof(msg), sa_flags);
+ RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "GATEWAY sa diff: %s", msg);
+
+ /*
+ * TODO: Currently kernel code does not set sdl_type, contrary to IPv6.
+ */
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, rtm_add_v6_ll_lle_success);
+ ATF_TP_ADD_TC(tp, rtm_add_v6_gu_lle_success);
+ ATF_TP_ADD_TC(tp, rtm_add_v4_gu_lle_success);
+ ATF_TP_ADD_TC(tp, rtm_del_v6_ll_lle_success);
+ ATF_TP_ADD_TC(tp, rtm_del_v6_gu_lle_success);
+ ATF_TP_ADD_TC(tp, rtm_del_v4_gu_lle_success);
+
+ return (atf_no_error());
+}
+
+
diff --git a/tests/sys/net/routing/test_rtsock_multipath.py b/tests/sys/net/routing/test_rtsock_multipath.py
new file mode 100755
index 000000000000..f4734d2f65bb
--- /dev/null
+++ b/tests/sys/net/routing/test_rtsock_multipath.py
@@ -0,0 +1,271 @@
+import pytest
+from atf_python.sys.net.rtsock import RtConst
+from atf_python.sys.net.rtsock import Rtsock
+from atf_python.sys.net.rtsock import RtsockRtMessage
+from atf_python.sys.net.tools import ToolsHelper
+from atf_python.sys.net.vnet import SingleVnetTestTemplate
+
+
+class TestRtmMultipath(SingleVnetTestTemplate):
+ def setup_method(self, method):
+ method_name = method.__name__
+ if "multipath4" in method_name:
+ self.IPV4_PREFIXES = ["192.0.2.1/24"]
+ self.PREFIX = "128.66.0.0/24"
+ elif "multipath6" in method_name:
+ self.IPV6_PREFIXES = ["2001:db8::1/64"]
+ self.PREFIX = "2001:db8:0:ddbb::/64"
+ super().setup_method(method)
+ self.rtsock = Rtsock()
+
+ def get_prefix_routes(self):
+ family = "inet6" if ":" in self.PREFIX else "inet"
+ routes = ToolsHelper.get_routes(family)
+ return [r for r in routes if r["destination"] == self.PREFIX]
+
+ @pytest.mark.parametrize(
+ "gws",
+ [
+ pytest.param(["+.10=2", "+.5=3"], id="transition_multi"),
+ pytest.param(["+.10=2", "+.5=3", "-.10=2"], id="transition_single1"),
+ pytest.param(["+.10=2", "+.5=3", "-.5=3"], id="transition_single2"),
+ pytest.param(
+ ["+.10", "+.11", "+.50", "+.13", "+.145", "+.72"], id="correctness1"
+ ),
+ pytest.param(
+ ["+.10", "+.11", "+.50", "-.50", "+.145", "+.72"], id="correctness2"
+ ),
+ pytest.param(["+.10=1", "+.5=2"], id="weight1"),
+ pytest.param(["+.10=2", "+.5=7"], id="weight2"),
+ pytest.param(["+.10=13", "+.5=21"], id="weight3_max"),
+ pytest.param(["+.10=2", "+.5=3", "~.5=4"], id="change_new_weight1"),
+ pytest.param(["+.10=2", "+.5=3", "~.10=3"], id="change_new_weight2"),
+ pytest.param(
+ ["+.10=2", "+.5=3", "+.7=4", "~.10=3"], id="change_new_weight3"
+ ),
+ pytest.param(["+.10=2", "+.5=3", "~.5=3"], id="change_same_weight1"),
+ pytest.param(
+ ["+.10=2", "+.5=3", "+.7=4", "~.5=3"], id="change_same_weight2"
+ ),
+ ],
+ )
+ @pytest.mark.require_user("root")
+ def test_rtm_multipath4(self, gws):
+ """Tests RTM_ADD with IPv4 dest transitioning to multipath"""
+ self._test_rtm_multipath(gws, "192.0.2")
+
+ @pytest.mark.parametrize(
+ "gws",
+ [
+ pytest.param(["+:10=2", "+:5=3"], id="transition_multi"),
+ pytest.param(["+:10=2", "+:5=3", "-:10=2"], id="transition_single1"),
+ pytest.param(["+:10=2", "+:5=3", "-:5=3"], id="transition_single2"),
+ pytest.param(
+ ["+:10", "+:11", "+:50", "+:13", "+:145", "+:72"], id="correctness1"
+ ),
+ pytest.param(
+ ["+:10", "+:11", "+:50", "-:50", "+:145", "+:72"], id="correctness2"
+ ),
+ pytest.param(["+:10=1", "+:5=2"], id="weight1"),
+ pytest.param(["+:10=2", "+:5=7"], id="weight2"),
+ pytest.param(["+:10=13", "+:5=21"], id="weight3_max"),
+ pytest.param(["+:10=13", "+:5=21"], id="weight3_max"),
+ pytest.param(["+:10=2", "+:5=3", "~:5=4"], id="change_new_weight1"),
+ pytest.param(["+:10=2", "+:5=3", "~:10=3"], id="change_new_weight2"),
+ pytest.param(
+ ["+:10=2", "+:5=3", "+:7=4", "~:10=3"], id="change_new_weight3"
+ ),
+ pytest.param(["+:10=2", "+:5=3", "~:5=3"], id="change_same_weight1"),
+ pytest.param(
+ ["+:10=2", "+:5=3", "+:7=4", "~:5=3"], id="change_same_weight2"
+ ),
+ ],
+ )
+ @pytest.mark.require_user("root")
+ def test_rtm_multipath6(self, gws):
+ """Tests RTM_ADD with IPv6 dest transitioning to multipath"""
+ self._test_rtm_multipath(gws, "2001:db8:")
+
+ def _test_rtm_multipath(self, gws, gw_prefix: str):
+ desired_map = {}
+ for gw_act in gws:
+ # GW format: <+-~>GW[=weight]
+ if "=" in gw_act:
+ arr = gw_act[1:].split("=")
+ weight = int(arr[1])
+ gw = gw_prefix + arr[0]
+ else:
+ weight = None
+ gw = gw_prefix + gw_act[1:]
+ if gw_act[0] == "+":
+ msg = self.rtsock.new_rtm_add(self.PREFIX, gw)
+ desired_map[gw] = self.rtsock.get_weight(weight)
+ elif gw_act[0] == "-":
+ msg = self.rtsock.new_rtm_del(self.PREFIX, gw)
+ del desired_map[gw]
+ else:
+ msg = self.rtsock.new_rtm_change(self.PREFIX, gw)
+ desired_map[gw] = self.rtsock.get_weight(weight)
+
+ msg.rtm_flags = RtConst.RTF_GATEWAY
+ if weight:
+ msg.rtm_inits |= RtConst.RTV_WEIGHT
+ msg.rtm_rmx.rmx_weight = weight
+ # Prepare SAs to check for
+ desired_sa = {
+ RtConst.RTA_DST: msg.get_sa(RtConst.RTA_DST),
+ RtConst.RTA_NETMASK: msg.get_sa(RtConst.RTA_NETMASK),
+ RtConst.RTA_GATEWAY: msg.get_sa(RtConst.RTA_GATEWAY),
+ }
+ self.rtsock.write_message(msg)
+
+ data = self.rtsock.read_data(msg.rtm_seq)
+ msg_in = RtsockRtMessage.from_bytes(data)
+ msg_in.print_in_message()
+ msg_in.verify(msg.rtm_type, desired_sa)
+ assert msg_in.rtm_rmx.rmx_weight == self.rtsock.get_weight(weight)
+
+ routes = self.get_prefix_routes()
+ derived_map = {r["gateway"]: r["weight"] for r in routes}
+ assert derived_map == desired_map
+
+ @pytest.mark.require_user("root")
+ def test_rtm_multipath4_add_same_eexist(self):
+ """Tests adding same IPv4 gw to the multipath group (EEXIST)"""
+ gws = ["192.0.2.10", "192.0.2.11", "192.0.2.11"]
+ self._test_rtm_multipath_add_same_eexist(gws)
+
+ @pytest.mark.require_user("root")
+ def test_rtm_multipath6_add_same_eexist(self):
+ """Tests adding same IPv4 gw to the multipath group (EEXIST)"""
+ gws = ["2001:db8::10", "2001:db8::11", "2001:db8::11"]
+ self._test_rtm_multipath_add_same_eexist(gws)
+
+ def _test_rtm_multipath_add_same_eexist(self, gws):
+ for idx, gw in enumerate(gws):
+ msg = self.rtsock.new_rtm_add(self.PREFIX, gw)
+ msg.rtm_flags = RtConst.RTF_GATEWAY
+ try:
+ self.rtsock.write_message(msg)
+ except FileExistsError as e:
+ if idx != 2:
+ raise
+ print("Succcessfully raised {}".format(e))
+
+ @pytest.mark.require_user("root")
+ def test_rtm_multipath4_del_unknown_esrch(self):
+ """Tests deleting non-existing dest from the multipath group (ESRCH)"""
+ gws = ["192.0.2.10", "192.0.2.11"]
+ self._test_rtm_multipath_del_unknown_esrch(gws, "192.0.2.7")
+
+ @pytest.mark.require_user("root")
+ def test_rtm_multipath6_del_unknown_esrch(self):
+ """Tests deleting non-existing dest from the multipath group (ESRCH)"""
+ gws = ["2001:db8::10", "2001:db8::11"]
+ self._test_rtm_multipath_del_unknown_esrch(gws, "2001:db8::7")
+
+ @pytest.mark.require_user("root")
+ def _test_rtm_multipath_del_unknown_esrch(self, gws, target_gw):
+ for gw in gws:
+ msg = self.rtsock.new_rtm_add(self.PREFIX, gw)
+ msg.rtm_flags = RtConst.RTF_GATEWAY
+ self.rtsock.write_message(msg)
+ msg = self.rtsock.new_rtm_del(self.PREFIX, target_gw)
+ msg.rtm_flags = RtConst.RTF_GATEWAY
+ try:
+ self.rtsock.write_message(msg)
+ except ProcessLookupError as e:
+ print("Succcessfully raised {}".format(e))
+
+ @pytest.mark.require_user("root")
+ def test_rtm_multipath4_change_unknown_esrch(self):
+ """Tests changing non-existing dest in the multipath group (ESRCH)"""
+ gws = ["192.0.2.10", "192.0.2.11"]
+ self._test_rtm_multipath_change_unknown_esrch(gws, "192.0.2.7")
+
+ @pytest.mark.require_user("root")
+ def test_rtm_multipath6_change_unknown_esrch(self):
+ """Tests changing non-existing dest in the multipath group (ESRCH)"""
+ gws = ["2001:db8::10", "2001:db8::11"]
+ self._test_rtm_multipath_change_unknown_esrch(gws, "2001:db8::7")
+
+ @pytest.mark.require_user("root")
+ def _test_rtm_multipath_change_unknown_esrch(self, gws, target_gw):
+ for gw in gws:
+ msg = self.rtsock.new_rtm_add(self.PREFIX, gw)
+ msg.rtm_flags = RtConst.RTF_GATEWAY
+ self.rtsock.write_message(msg)
+ msg = self.rtsock.new_rtm_change(self.PREFIX, target_gw)
+ msg.rtm_flags = RtConst.RTF_GATEWAY
+ try:
+ self.rtsock.write_message(msg)
+ except ProcessLookupError as e:
+ print("Succcessfully raised {}".format(e))
+
+ @pytest.mark.require_user("root")
+ def test_rtm_multipath4_add_zero_weight(self):
+ """Tests RTM_ADD with dest transitioning to multipath"""
+
+ desired_map = {}
+ for gw in ["192.0.2.10", "192.0.2.11", "192.0.2.13"]:
+ msg = self.rtsock.new_rtm_add(self.PREFIX, gw)
+ msg.rtm_flags = RtConst.RTF_GATEWAY
+ msg.rtm_rmx.rmx_weight = 0
+ msg.rtm_inits |= RtConst.RTV_WEIGHT
+ self.rtsock.write_message(msg)
+ desired_map[gw] = self.rtsock.get_weight(0)
+
+ routes = self.get_prefix_routes()
+ derived_map = {r["gateway"]: r["weight"] for r in routes}
+ assert derived_map == desired_map
+
+ @pytest.mark.require_user("root")
+ def test_rtm_multipath4_getroute(self):
+ """Tests RTM_GET with exact prefix lookup on the multipath group"""
+ gws = ["192.0.2.10", "192.0.2.11", "192.0.2.13"]
+ return self._test_rtm_multipath_getroute(gws)
+
+ @pytest.mark.require_user("root")
+ def test_rtm_multipath6_getroute(self):
+ """Tests RTM_GET with exact prefix lookup on the multipath group"""
+ gws = ["2001:db8::10", "2001:db8::11", "2001:db8::13"]
+ return self._test_rtm_multipath_getroute(gws)
+
+ def _test_rtm_multipath_getroute(self, gws):
+ valid_gws = []
+ for gw in gws:
+ msg = self.rtsock.new_rtm_add(self.PREFIX, gw)
+ msg.rtm_flags = RtConst.RTF_GATEWAY
+ self.rtsock.write_message(msg)
+
+ desired_sa = {
+ RtConst.RTA_DST: msg.get_sa(RtConst.RTA_DST),
+ RtConst.RTA_NETMASK: msg.get_sa(RtConst.RTA_NETMASK),
+ }
+ valid_gws.append(msg.get_sa(RtConst.RTA_GATEWAY))
+
+ msg_get = RtsockRtMessage(
+ RtConst.RTM_GET,
+ self.rtsock.get_seq(),
+ msg.get_sa(RtConst.RTA_DST),
+ msg.get_sa(RtConst.RTA_NETMASK),
+ )
+ self.rtsock.write_message(msg_get)
+
+ data = self.rtsock.read_data(msg_get.rtm_seq)
+ msg_in = RtsockRtMessage.from_bytes(data)
+ msg_in.print_in_message()
+ msg_in.verify(RtConst.RTM_GET, desired_sa)
+
+ # Additionally, check that the gateway is among the valid
+ # gateways
+ gw_found = False
+ gw_in = msg_in.get_sa(RtConst.RTA_GATEWAY)
+ for valid_gw in valid_gws:
+ try:
+ assert valid_gw == gw_in
+ gw_found = True
+ break
+ except AssertionError:
+ pass
+ assert gw_found is True
diff --git a/tests/sys/net/routing/test_rtsock_ops.c b/tests/sys/net/routing/test_rtsock_ops.c
new file mode 100644
index 000000000000..ab4be93cc1af
--- /dev/null
+++ b/tests/sys/net/routing/test_rtsock_ops.c
@@ -0,0 +1,53 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Alexander V. Chernikov
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/socket.h>
+#include <net/route.h>
+
+#include <atf-c.h>
+
+ATF_TC(socket_rtsock_openclose);
+ATF_TC_HEAD(socket_rtsock_openclose, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "test successful open/close");
+}
+
+ATF_TC_BODY(socket_rtsock_openclose, tc)
+{
+ int s = socket(PF_ROUTE, SOCK_RAW, 0);
+ ATF_CHECK(s >= 0);
+ ATF_CHECK_ERRNO(0, close(s) == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, socket_rtsock_openclose);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/net/stp.py b/tests/sys/net/stp.py
new file mode 100644
index 000000000000..dc6634fb7279
--- /dev/null
+++ b/tests/sys/net/stp.py
@@ -0,0 +1,114 @@
+#!/usr/bin/env python3
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2021 Kristof Provost <kp@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import sys
+import os
+curdir = os.path.dirname(os.path.realpath(__file__))
+netpfil_common = curdir + "/../netpfil/common"
+sys.path.append(netpfil_common)
+from sniffer import Sniffer
+
+def check_stp(args, packet):
+ stp = packet.getlayer(sp.STP)
+ if stp is None:
+ return False
+
+ if stp.rootmac != "00:0c:29:01:01:01":
+ return False
+
+ # Ensure we don't get confused by valid STP packets generated by if_bridge
+ if (stp.maxage >= 6 and stp.maxage <= 40) and \
+ (stp.hellotime >= 1 and stp.hellotime <= 2) and \
+ (stp.fwddelay >= 4 and stp.fwddelay <= 30):
+ return False
+
+ print("This packet should have been dropped")
+ print(packet.show())
+ return True
+
+def invalid_stp(send_if):
+ llc = sp.Ether(src="00:0c:29:0b:91:0a", dst="01:80:C2:00:00:00") \
+ / sp.LLC()
+
+ # Bad maxage
+ stp = llc / sp.STP(proto=0, rootid=32768, rootmac="00:0c:29:01:01:01", \
+ bridgeid=32768, bridgemac="00:0c:29:01:01:01", \
+ portid=0x8007, maxage=41, hellotime=2, fwddelay=30)
+ sp.sendp(stp, iface=send_if, verbose=False)
+ stp = llc / sp.STP(proto=0, rootid=32768, rootmac="00:0c:29:01:01:01", \
+ bridgeid=32768, bridgemac="00:0c:29:01:01:01", \
+ portid=0x8007, maxage=5, hellotime=2, fwddelay=30)
+ sp.sendp(stp, iface=send_if, verbose=False)
+
+ # Bad hellotime
+ stp = llc / sp.STP(proto=0, rootid=32768, rootmac="00:0c:29:01:01:01", \
+ bridgeid=32768, bridgemac="00:0c:29:01:01:01", \
+ portid=0x8007, maxage=40, hellotime=3, fwddelay=30)
+ sp.sendp(stp, iface=send_if, verbose=False)
+ stp = llc / sp.STP(proto=0, rootid=32768, rootmac="00:0c:29:01:01:01", \
+ bridgeid=32768, bridgemac="00:0c:29:01:01:01", \
+ portid=0x8007, maxage=40, hellotime=1, fwddelay=30)
+ sp.sendp(stp, iface=send_if, verbose=False)
+
+ # Bad fwddelay
+ stp = llc / sp.STP(proto=0, rootid=32768, rootmac="00:0c:29:01:01:01", \
+ bridgeid=32768, bridgemac="00:0c:29:01:01:01", \
+ portid=0x8007, maxage=40, hellotime=2, fwddelay=31)
+ sp.sendp(stp, iface=send_if, verbose=False)
+ stp = llc / sp.STP(proto=0, rootid=32768, rootmac="00:0c:29:01:01:01", \
+ bridgeid=32768, bridgemac="00:0c:29:01:01:01", \
+ portid=0x8007, maxage=40, hellotime=2, fwddelay=3)
+ sp.sendp(stp, iface=send_if, verbose=False)
+
+def main():
+ parser = argparse.ArgumentParser("stp.py",
+ description="STP test tool")
+ parser.add_argument('--sendif', nargs=1,
+ required=True,
+ help='The interface through which the packet(s) will be sent')
+ parser.add_argument('--recvif', nargs=1,
+ help='The interface on which to expect the ICMP echo request')
+
+ args = parser.parse_args()
+
+ sniffer = Sniffer(args, check_stp, args.recvif[0])
+
+ invalid_stp(args.sendif[0])
+
+ sniffer.join()
+
+ # The 'correct' packet is a corrupt STP packet, so it shouldn't turn up.
+ if sniffer.correctPackets:
+ sys.exit(1)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netgraph/Makefile b/tests/sys/netgraph/Makefile
new file mode 100644
index 000000000000..f0eb4928ec7f
--- /dev/null
+++ b/tests/sys/netgraph/Makefile
@@ -0,0 +1,28 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/netgraph
+BINDIR= ${TESTSDIR}
+
+TAP_TESTS_SH+= ng_macfilter_test
+
+# Serialize tests since some share netgraph node names.
+TEST_METADATA+= is_exclusive=true
+TEST_METADATA+= required_user="root"
+TEST_METADATA.ng_macfilter_test+= required_programs="perl"
+
+ATF_TESTS_C+= basic \
+ bridge \
+ hub \
+ ksocket \
+ socket \
+ vlan_rotate \
+
+SRCS.basic= basic.c util.c
+SRCS.bridge= bridge.c util.c
+SRCS.hub= hub.c util.c
+SRCS.socket= socket.c
+SRCS.vlan_rotate=vlan_rotate.c util.c
+
+LIBADD+= netgraph
+
+.include <bsd.test.mk>
diff --git a/tests/sys/netgraph/basic.c b/tests/sys/netgraph/basic.c
new file mode 100644
index 000000000000..573422add694
--- /dev/null
+++ b/tests/sys/netgraph/basic.c
@@ -0,0 +1,191 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright 2021 Lutz Donnerhacke
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <atf-c.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include "util.h"
+
+ATF_TC(send_recv);
+ATF_TC_HEAD(send_recv, conf)
+{
+ atf_tc_set_md_var(conf, "require.user", "root");
+}
+
+ATF_TC_BODY(send_recv, dummy)
+{
+ char msg[] = "test";
+ ng_counter_t r;
+
+ ng_init();
+ ng_connect(".", "a", ".", "b");
+ ng_register_data("b", get_data0);
+ ng_send_data("a", msg, sizeof(msg));
+
+ ng_counter_clear(r);
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 1);
+}
+
+ATF_TC(node);
+ATF_TC_HEAD(node, conf)
+{
+ atf_tc_set_md_var(conf, "require.user", "root");
+}
+
+ATF_TC_BODY(node, dummy)
+{
+ char msg[] = "test";
+ ng_counter_t r;
+
+ ng_init();
+ ng_mkpeer(".", "a", "hub", "a");
+ ng_name("a", "test hub");
+
+ ng_errors(PASS);
+ ng_name("a", "test hub");
+ ng_errors(FAIL);
+ if (errno == EADDRINUSE)
+ atf_tc_expect_fail("PR241954");
+ ATF_CHECK_ERRNO(0, 1);
+ atf_tc_expect_pass();
+
+ ng_connect(".", "b", "test hub:", "b");
+ ng_connect(".", "c", "test hub:", "c");
+ ng_register_data("a", get_data0);
+ ng_register_data("b", get_data1);
+ ng_register_data("c", get_data2);
+
+ ng_counter_clear(r);
+ ng_send_data("a", msg, sizeof(msg));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1);
+
+ ng_rmhook(".", "b");
+ ng_counter_clear(r);
+ ng_send_data("a", msg, sizeof(msg));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 1);
+
+ ng_shutdown("test hub:");
+}
+
+ATF_TC(message);
+ATF_TC_HEAD(message, conf)
+{
+ atf_tc_set_md_var(conf, "require.user", "root");
+}
+
+ATF_TC_BODY(message, dummy)
+{
+ ng_init();
+ ng_mkpeer(".", "a", "hub", "a");
+ ng_name("a", "test hub");
+
+ ng_send_msg("test hub:", "setpersistent");
+ ng_rmhook(".", "a");
+
+ ng_shutdown("test hub:");
+}
+
+ATF_TC(same_name);
+ATF_TC_HEAD(same_name, conf)
+{
+ atf_tc_set_md_var(conf, "require.user", "root");
+}
+
+ATF_TC_BODY(same_name, dummy)
+{
+ ng_init();
+ ng_mkpeer(".", "a", "hub", "a");
+ ng_name("a", "test");
+
+ ng_errors(PASS);
+ ng_connect(".", "a", ".", "b");
+ ATF_CHECK_ERRNO(EEXIST, 1);
+ ng_connect(".", "b", ".", "b");
+ ATF_CHECK_ERRNO(EEXIST, 1);
+ ng_name(".", "test");
+ ATF_CHECK_ERRNO(EADDRINUSE, 1);
+
+ ng_errors(FAIL);
+ ng_shutdown("test:");
+}
+
+ATF_TC(queuelimit);
+ATF_TC_HEAD(queuelimit, conf)
+{
+ atf_tc_set_md_var(conf, "require.user", "root");
+}
+
+ATF_TC_BODY(queuelimit, dummy)
+{
+ ng_counter_t r;
+ int i;
+ char msg[] = "test";
+ const int MAX = 1000;
+
+ ng_init();
+ ng_connect(".", "a", ".", "b");
+ ng_register_data("b", get_data0);
+
+ ng_errors(PASS);
+ for (i = 0; i < MAX; i++)
+ {
+ ng_send_data("a", msg, sizeof(msg));
+ if (errno != 0)
+ break;
+ /* no ng_handle_events -> messages stall */
+ }
+ ng_errors(FAIL);
+
+ ng_counter_clear(r);
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] > 100);
+ ATF_CHECK(r[0] == i);
+ atf_tc_expect_fail("Queue full (%d)", i);
+ ATF_CHECK(r[0] == MAX);
+ atf_tc_expect_pass();
+}
+
+ATF_TP_ADD_TCS(basic)
+{
+ ATF_TP_ADD_TC(basic, send_recv);
+ ATF_TP_ADD_TC(basic, node);
+ ATF_TP_ADD_TC(basic, message);
+ ATF_TP_ADD_TC(basic, same_name);
+ ATF_TP_ADD_TC(basic, queuelimit);
+
+ return atf_no_error();
+}
diff --git a/tests/sys/netgraph/bridge.c b/tests/sys/netgraph/bridge.c
new file mode 100644
index 000000000000..3e3c0f804278
--- /dev/null
+++ b/tests/sys/netgraph/bridge.c
@@ -0,0 +1,632 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright 2021 Lutz Donnerhacke
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <atf-c.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+
+#include "util.h"
+#include <netgraph/ng_bridge.h>
+
+static void get_tablesize(char const *source, struct ng_mesg *msg, void *ctx);
+struct gettable
+{
+ u_int32_t tok;
+ int cnt;
+};
+
+struct frame4
+{
+ struct ether_header eh;
+ struct ip ip;
+ char data[64];
+};
+struct frame6
+{
+ struct ether_header eh;
+ struct ip6_hdr ip;
+ char data[64];
+};
+
+static struct frame4 msg4 = {
+ .ip.ip_v = 4,
+ .ip.ip_hl = 5,
+ .ip.ip_ttl = 1,
+ .ip.ip_p = 254,
+ .ip.ip_src = {htonl(0x0a00dead)},
+ .ip.ip_dst = {htonl(0x0a00beef)},
+ .ip.ip_len = 32,
+ .eh.ether_type = ETHERTYPE_IP,
+ .eh.ether_shost = {2, 4, 6},
+ .eh.ether_dhost = {2, 4, 6},
+};
+
+
+ATF_TC(basic);
+ATF_TC_HEAD(basic, conf)
+{
+ atf_tc_set_md_var(conf, "require.user", "root");
+}
+
+ATF_TC_BODY(basic, dummy)
+{
+ ng_counter_t r;
+ struct gettable rm;
+
+ ng_init();
+ ng_errors(PASS);
+ ng_shutdown("bridge:");
+ ng_errors(FAIL);
+
+ ng_mkpeer(".", "a", "bridge", "link0");
+ ng_name("a", "bridge");
+ ng_connect(".", "b", "bridge:", "link1");
+ ng_connect(".", "c", "bridge:", "link2");
+
+ /* do not bounce back */
+ ng_register_data("a", get_data0);
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[5] = 1;
+ ng_send_data("a", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 0);
+
+ /* send to others */
+ ng_register_data("b", get_data1);
+ ng_register_data("c", get_data2);
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[5] = 1;
+ ng_send_data("a", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1);
+
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[5] = 2;
+ ng_send_data("b", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 1);
+
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[5] = 3;
+ ng_send_data("c", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 1 && r[1] == 1 && r[2] == 0);
+
+ /* send to learned unicast */
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[5] = 1;
+ msg4.eh.ether_dhost[5] = 3;
+ ng_send_data("a", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 1);
+
+ /* inspect mac table */
+ ng_register_msg(get_tablesize);
+ rm.tok = ng_send_msg("bridge:", "gettable");
+ rm.cnt = 0;
+ ng_handle_events(50, &rm);
+ ATF_CHECK(rm.cnt == 3);
+
+ /* remove a link */
+ ng_rmhook(".", "b");
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[5] = 1;
+ msg4.eh.ether_dhost[5] = 0;
+ ng_send_data("a", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 1);
+
+ /* inspect mac table */
+ ng_register_msg(get_tablesize);
+ rm.tok = ng_send_msg("bridge:", "gettable");
+ rm.cnt = 0;
+ ng_handle_events(50, &rm);
+ ATF_CHECK(rm.cnt == 2);
+
+ ng_shutdown("bridge:");
+}
+
+ATF_TC(persistence);
+ATF_TC_HEAD(persistence, conf)
+{
+ atf_tc_set_md_var(conf, "require.user", "root");
+}
+
+ATF_TC_BODY(persistence, dummy)
+{
+ ng_init();
+ ng_errors(PASS);
+ ng_shutdown("bridge:");
+ ng_errors(FAIL);
+
+ ng_mkpeer(".", "a", "bridge", "link0");
+ ng_name("a", "bridge");
+
+ ng_send_msg("bridge:", "setpersistent");
+ ng_rmhook(".", "a");
+
+ ng_shutdown("bridge:");
+}
+
+ATF_TC(loop);
+ATF_TC_HEAD(loop, conf)
+{
+ atf_tc_set_md_var(conf, "require.user", "root");
+}
+
+ATF_TC_BODY(loop, dummy)
+{
+ ng_counter_t r;
+ int i;
+
+ ng_init();
+ ng_errors(PASS);
+ ng_shutdown("bridge1:");
+ ng_shutdown("bridge2:");
+ ng_errors(FAIL);
+
+ ng_mkpeer(".", "a", "bridge", "link0");
+ ng_name("a", "bridge1");
+ ng_mkpeer(".", "b", "bridge", "link1");
+ ng_name("b", "bridge2");
+
+ ng_register_data("a", get_data0);
+ ng_register_data("b", get_data1);
+
+ /*-
+ * Open loop
+ *
+ * /-- bridge1
+ * . < |
+ * \-- bridge2
+ */
+ ng_connect("bridge1:", "link11", "bridge2:", "link11");
+
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[5] = 1;
+ ng_send_data("a", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 0 && r[1] == 1);
+
+ /*-
+ * Closed loop, DANGEROUS!
+ *
+ * /-- bridge1 -\
+ * . < | |
+ * \-- bridge2 -/
+ */
+ ng_connect("bridge1:", "link12", "bridge2:", "link12");
+
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[5] = 1;
+ ng_errors(PASS);
+ ng_send_data("a", &msg4, sizeof(msg4));
+ ATF_CHECK_ERRNO(ELOOP, errno != 0); /* loop might be detected */
+ ng_errors(FAIL);
+ for (i = 0; i < 10; i++) /* don't run forever */
+ if (!ng_handle_event(50, &r))
+ break;
+ ATF_CHECK(r[0] == 0 && r[1] == 1);
+
+ ng_shutdown("bridge1:");
+ ng_shutdown("bridge2:");
+}
+
+ATF_TC(many_unicasts);
+ATF_TC_HEAD(many_unicasts, conf)
+{
+ atf_tc_set_md_var(conf, "require.user", "root");
+}
+
+ATF_TC_BODY(many_unicasts, dummy)
+{
+ ng_counter_t r;
+ int i;
+ const int HOOKS = 1000;
+ struct gettable rm;
+
+ ng_init();
+ ng_errors(PASS);
+ ng_shutdown("bridge:");
+ ng_errors(FAIL);
+
+ ng_mkpeer(".", "a", "bridge", "link0");
+ ng_name("a", "bridge");
+ ng_register_data("a", get_data0);
+
+ /* learn MAC */
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[3] = 0xff;
+ ng_send_data("a", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 0);
+
+ /* use learned MAC as destination */
+ msg4.eh.ether_shost[3] = 0;
+ msg4.eh.ether_dhost[3] = 0xff;
+
+ /* now send */
+ ng_counter_clear(r);
+ for (i = 1; i <= HOOKS; i++)
+ {
+ char hook[20];
+
+ snprintf(hook, sizeof(hook), "link%d", i);
+ ng_connect(".", hook, "bridge:", hook);
+ ng_register_data(hook, get_data2);
+
+ msg4.eh.ether_shost[4] = i >> 8;
+ msg4.eh.ether_shost[5] = i & 0xff;
+ ng_errors(PASS);
+ ng_send_data(hook, &msg4, sizeof(msg4));
+ ng_errors(FAIL);
+ if (errno != 0)
+ break;
+ ng_handle_events(50, &r);
+ }
+ ATF_CHECK(r[0] == HOOKS && r[2] == 0);
+
+ /* inspect mac table */
+ ng_register_msg(get_tablesize);
+ rm.cnt = 0;
+ ng_errors(PASS);
+ rm.tok = ng_send_msg("bridge:", "gettable");
+ ng_errors(FAIL);
+ if (rm.tok == (u_int32_t)-1)
+ {
+ ATF_CHECK_ERRNO(ENOBUFS, 1);
+ atf_tc_expect_fail("response too large");
+ }
+ ng_handle_events(50, &rm);
+ ATF_CHECK(rm.cnt == HOOKS + 1);
+ atf_tc_expect_pass();
+
+ ng_shutdown("bridge:");
+}
+
+ATF_TC(many_broadcasts);
+ATF_TC_HEAD(many_broadcasts, conf)
+{
+ atf_tc_set_md_var(conf, "require.user", "root");
+}
+
+ATF_TC_BODY(many_broadcasts, dummy)
+{
+ ng_counter_t r;
+ int i;
+ const int HOOKS = 1000;
+
+ ng_init();
+ ng_errors(PASS);
+ ng_shutdown("bridge:");
+ ng_errors(FAIL);
+
+ ng_mkpeer(".", "a", "bridge", "link0");
+ ng_name("a", "bridge");
+ ng_register_data("a", get_data0);
+
+ /* learn MAC */
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[3] = 0xff;
+ ng_send_data("a", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 0);
+
+ /* use broadcast MAC */
+ msg4.eh.ether_shost[3] = 0;
+ memset(msg4.eh.ether_dhost, 0xff, sizeof(msg4.eh.ether_dhost));
+
+ /* now send */
+ ng_counter_clear(r);
+ for (i = 1; i <= HOOKS; i++)
+ {
+ char hook[20];
+
+ snprintf(hook, sizeof(hook), "link%d", i);
+ ng_connect(".", hook, "bridge:", hook);
+ ng_register_data(hook, get_data3);
+
+ msg4.eh.ether_shost[4] = i >> 8;
+ msg4.eh.ether_shost[5] = i & 0xff;
+ ng_errors(PASS);
+ ng_send_data(hook, &msg4, sizeof(msg4));
+ ng_errors(FAIL);
+ if (errno != 0)
+ break;
+ ng_handle_events(50, &r);
+ }
+ ATF_CHECK(r[0] > 100 && r[3] > 100);
+ if (i < HOOKS)
+ atf_tc_expect_fail("netgraph queue full (%d)", i);
+ ATF_CHECK(r[0] == HOOKS);
+ atf_tc_expect_pass();
+
+ ng_shutdown("bridge:");
+}
+
+ATF_TC(uplink_private);
+ATF_TC_HEAD(uplink_private, conf)
+{
+ atf_tc_set_md_var(conf, "require.user", "root");
+}
+
+ATF_TC_BODY(uplink_private, dummy)
+{
+ ng_counter_t r;
+ struct gettable rm;
+
+ ng_init();
+ ng_errors(PASS);
+ ng_shutdown("bridge:");
+
+ ng_mkpeer(".", "u1", "bridge", "uplink1");
+ if (errno > 0)
+ atf_tc_skip("uplinks are not supported.");
+ ng_errors(FAIL);
+ ng_name("u1", "bridge");
+ ng_register_data("u1", get_data1);
+ ng_connect(".", "u2", "bridge:", "uplink2");
+ ng_register_data("u2", get_data2);
+ ng_connect(".", "l0", "bridge:", "link0");
+ ng_register_data("l0", get_data0);
+ ng_connect(".", "l3", "bridge:", "link3");
+ ng_register_data("l3", get_data3);
+
+ /* unknown unicast 0 from uplink1 */
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[5] = 1;
+ ng_send_data("u1", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 1 && r[3] == 0);
+
+ /* unknown unicast 2 from link0 */
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[5] = 0;
+ msg4.eh.ether_dhost[5] = 2;
+ ng_send_data("l0", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 0);
+
+ /* known unicast 0 from uplink2 */
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[5] = 2;
+ msg4.eh.ether_dhost[5] = 0;
+ ng_send_data("u2", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 0 && r[3] == 0);
+
+ /* known unicast 0 from link3 */
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[5] = 3;
+ msg4.eh.ether_dhost[5] = 0;
+ ng_send_data("l3", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 0 && r[3] == 0);
+
+ /* (un)known unicast 2 from uplink1 */
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[5] = 1;
+ msg4.eh.ether_dhost[5] = 2;
+ ng_send_data("u1", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 1 && r[3] == 0);
+
+ /* (un)known unicast 2 from link0 */
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[5] = 0;
+ ng_send_data("l0", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 0);
+
+ /* unknown multicast 2 from uplink1 */
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[5] = 1;
+ msg4.eh.ether_dhost[0] = 0xff;
+ ng_send_data("u1", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 1 && r[3] == 1);
+
+ /* unknown multicast 2 from link0 */
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[5] = 0;
+ ng_send_data("l0", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 1);
+
+ /* broadcast from uplink1 */
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[5] = 1;
+ memset(msg4.eh.ether_dhost, 0xff, sizeof(msg4.eh.ether_dhost));
+ ng_send_data("u1", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 1 && r[3] == 1);
+
+ /* broadcast from link0 */
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[5] = 0;
+ ng_send_data("l0", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 1);
+
+ /* inspect mac table */
+ ng_register_msg(get_tablesize);
+ rm.tok = ng_send_msg("bridge:", "gettable");
+ rm.cnt = 0;
+ ng_handle_events(50, &rm);
+ ATF_CHECK(rm.cnt == 2);
+
+ ng_shutdown("bridge:");
+}
+
+ATF_TC(uplink_classic);
+ATF_TC_HEAD(uplink_classic, conf)
+{
+ atf_tc_set_md_var(conf, "require.user", "root");
+}
+
+ATF_TC_BODY(uplink_classic, dummy)
+{
+ ng_counter_t r;
+ struct gettable rm;
+
+ ng_init();
+ ng_errors(PASS);
+ ng_shutdown("bridge:");
+
+ ng_mkpeer(".", "l0", "bridge", "link0");
+ if (errno > 0)
+ atf_tc_skip("uplinks are not supported.");
+ ng_errors(FAIL);
+ ng_name("l0", "bridge");
+ ng_register_data("l0", get_data0);
+ ng_connect(".", "u1", "bridge:", "uplink1");
+ ng_register_data("u1", get_data1);
+ ng_connect(".", "u2", "bridge:", "uplink2");
+ ng_register_data("u2", get_data2);
+ ng_connect(".", "l3", "bridge:", "link3");
+ ng_register_data("l3", get_data3);
+
+ /* unknown unicast 0 from uplink1 */
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[5] = 1;
+ ng_send_data("u1", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 1 && r[3] == 1);
+
+ /* unknown unicast 2 from link0 */
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[5] = 0;
+ msg4.eh.ether_dhost[5] = 2;
+ ng_send_data("l0", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 1);
+
+ /* known unicast 0 from uplink2 */
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[5] = 2;
+ msg4.eh.ether_dhost[5] = 0;
+ ng_send_data("u2", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 0 && r[3] == 0);
+
+ /* known unicast 0 from link3 */
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[5] = 3;
+ msg4.eh.ether_dhost[5] = 0;
+ ng_send_data("l3", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 0 && r[3] == 0);
+
+ /* (un)known unicast 2 from uplink1 */
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[5] = 1;
+ msg4.eh.ether_dhost[5] = 2;
+ ng_send_data("u1", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 1 && r[3] == 1);
+
+ /* (un)known unicast 2 from link0 */
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[5] = 0;
+ ng_send_data("l0", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 1);
+
+ /* unknown multicast 2 from uplink1 */
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[5] = 1;
+ msg4.eh.ether_dhost[0] = 0xff;
+ ng_send_data("u1", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 1 && r[3] == 1);
+
+ /* unknown multicast 2 from link0 */
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[5] = 0;
+ ng_send_data("l0", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 1);
+
+ /* broadcast from uplink1 */
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[5] = 1;
+ memset(msg4.eh.ether_dhost, 0xff, sizeof(msg4.eh.ether_dhost));
+ ng_send_data("u1", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 1 && r[3] == 1);
+
+ /* broadcast from link0 */
+ ng_counter_clear(r);
+ msg4.eh.ether_shost[5] = 0;
+ ng_send_data("l0", &msg4, sizeof(msg4));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 1 && r[3] == 1);
+
+ /* inspect mac table */
+ ng_register_msg(get_tablesize);
+ rm.tok = ng_send_msg("bridge:", "gettable");
+ rm.cnt = 0;
+ ng_handle_events(50, &rm);
+ ATF_CHECK(rm.cnt == 2);
+
+ ng_shutdown("bridge:");
+}
+
+ATF_TP_ADD_TCS(bridge)
+{
+ ATF_TP_ADD_TC(bridge, basic);
+ ATF_TP_ADD_TC(bridge, loop);
+ ATF_TP_ADD_TC(bridge, persistence);
+ ATF_TP_ADD_TC(bridge, many_unicasts);
+ ATF_TP_ADD_TC(bridge, many_broadcasts);
+ ATF_TP_ADD_TC(bridge, uplink_private);
+ ATF_TP_ADD_TC(bridge, uplink_classic);
+
+ return atf_no_error();
+}
+
+static void
+get_tablesize(char const *source, struct ng_mesg *msg, void *ctx)
+{
+ struct gettable *rm = ctx;
+ struct ng_bridge_host_ary *gt = (void *)msg->data;
+
+ fprintf(stderr, "Response from %s to query %d\n", source, msg->header.token);
+ if (rm->tok == msg->header.token)
+ rm->cnt = gt->numHosts;
+}
diff --git a/tests/sys/netgraph/hub.c b/tests/sys/netgraph/hub.c
new file mode 100644
index 000000000000..08d083ad9605
--- /dev/null
+++ b/tests/sys/netgraph/hub.c
@@ -0,0 +1,231 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright 2021 Lutz Donnerhacke
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <atf-c.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include "util.h"
+
+ATF_TC(basic);
+ATF_TC_HEAD(basic, conf)
+{
+ atf_tc_set_md_var(conf, "require.user", "root");
+}
+
+ATF_TC_BODY(basic, dummy)
+{
+ char msg[] = "test";
+ ng_counter_t r;
+
+ ng_errors(PASS);
+ ng_shutdown("hub:");
+ ng_errors(FAIL);
+
+ ng_init();
+ ng_mkpeer(".", "a", "hub", "a");
+ ng_name("a", "hub");
+ ng_connect(".", "b", "hub:", "b");
+ ng_connect(".", "c", "hub:", "c");
+
+ /* do not bounce back */
+ ng_register_data("a", get_data0);
+ ng_counter_clear(r);
+ ng_send_data("a", msg, sizeof(msg));
+ ng_handle_events(50, r);
+ ATF_CHECK(r[0] == 0);
+
+ /* send to others */
+ ng_register_data("b", get_data0);
+ ng_register_data("c", get_data0);
+ ng_counter_clear(r);
+ ng_send_data("a", msg, sizeof(msg));
+ ng_handle_events(50, r);
+ ATF_CHECK(r[0] == 2);
+
+ ng_counter_clear(r);
+ ng_send_data("b", msg, sizeof(msg));
+ ng_handle_events(50, r);
+ ATF_CHECK(r[0] == 2);
+
+ ng_counter_clear(r);
+ ng_send_data("c", msg, sizeof(msg));
+ ng_handle_events(50, r);
+ ATF_CHECK(r[0] == 2);
+
+ /* remove a link */
+ ng_rmhook(".", "b");
+ ng_counter_clear(r);
+ ng_send_data("a", msg, sizeof(msg));
+ ng_handle_events(50, r);
+ ATF_CHECK(r[0] == 1);
+
+ ng_shutdown("hub:");
+}
+
+ATF_TC(persistence);
+ATF_TC_HEAD(persistence, conf)
+{
+ atf_tc_set_md_var(conf, "require.user", "root");
+}
+
+ATF_TC_BODY(persistence, dummy)
+{
+ ng_errors(PASS);
+ ng_shutdown("hub:");
+ ng_errors(FAIL);
+
+ ng_init();
+ ng_mkpeer(".", "a", "hub", "a");
+ ng_name("a", "hub");
+
+ ng_send_msg("hub:", "setpersistent");
+ ng_rmhook(".", "a");
+
+ ng_shutdown("hub:");
+}
+
+ATF_TC(loop);
+ATF_TC_HEAD(loop, conf)
+{
+ atf_tc_set_md_var(conf, "require.user", "root");
+}
+
+ATF_TC_BODY(loop, dummy)
+{
+ ng_counter_t r;
+ int i;
+ char msg[] = "LOOP Alert!";
+
+ ng_errors(PASS);
+ ng_shutdown("hub1:");
+ ng_shutdown("hub2:");
+ ng_errors(FAIL);
+
+ ng_init();
+ ng_mkpeer(".", "a", "hub", "a");
+ ng_name("a", "hub1");
+ ng_mkpeer(".", "b", "hub", "b");
+ ng_name("b", "hub2");
+
+ ng_register_data("a", get_data0);
+ ng_register_data("b", get_data0);
+
+ /*-
+ * Open loop
+ *
+ * /-- hub1
+ * . < |
+ * \-- hub2
+ */
+ ng_connect("hub1:", "xc1", "hub2:", "xc1");
+
+ ng_counter_clear(r);
+ ng_send_data("a", msg, sizeof(msg));
+ ng_handle_events(50, r);
+ ATF_CHECK(r[0] == 1);
+
+ /*-
+ * Closed loop, DANGEROUS!
+ *
+ * /-- hub1 -\
+ * . < | |
+ * \-- hub2 -/
+ */
+ ng_connect("hub1:", "xc2", "hub2:", "xc2");
+
+ ng_counter_clear(r);
+ ng_send_data("a", msg, sizeof(msg));
+ for (i = 0; i < 10; i++) /* don't run forever */
+ if (!ng_handle_event(50, r))
+ break;
+ ATF_CHECK(r[0] > 7);
+
+ ng_shutdown("hub1:");
+ ng_shutdown("hub2:");
+}
+
+ATF_TC(many_hooks);
+ATF_TC_HEAD(many_hooks, conf)
+{
+ atf_tc_set_md_var(conf, "require.user", "root");
+}
+
+ATF_TC_BODY(many_hooks, dummy)
+{
+ ng_counter_t r;
+ int i;
+ char msg[] = "test";
+ const int HOOKS = 1000;
+
+ ng_errors(PASS);
+ ng_shutdown("hub:");
+ ng_errors(FAIL);
+
+ ng_init();
+ ng_mkpeer(".", "a", "hub", "a");
+ ng_name("a", "hub");
+
+ ng_register_data("a", get_data0);
+ ng_counter_clear(r);
+ for (i = 0; i < HOOKS; i++)
+ {
+ char hook[20];
+
+ snprintf(hook, sizeof(hook), "hook%d", i);
+ ng_connect(".", hook, "hub:", hook);
+ ng_errors(PASS);
+ ng_send_data(hook, msg, sizeof(msg));
+ ng_errors(FAIL);
+ if (errno != 0)
+ break;
+ ng_handle_events(50, r);
+ }
+ ATF_CHECK(r[0] > 100);
+ atf_tc_expect_fail("Implementation limitation (%d)", i);
+ ATF_CHECK(r[0] == HOOKS);
+ atf_tc_expect_pass();
+
+ ng_shutdown("hub:");
+}
+
+
+ATF_TP_ADD_TCS(hub)
+{
+ ATF_TP_ADD_TC(hub, basic);
+ ATF_TP_ADD_TC(hub, loop);
+ ATF_TP_ADD_TC(hub, persistence);
+ ATF_TP_ADD_TC(hub, many_hooks);
+
+ return atf_no_error();
+}
diff --git a/tests/sys/netgraph/ksocket.c b/tests/sys/netgraph/ksocket.c
new file mode 100644
index 000000000000..a60c17bd337f
--- /dev/null
+++ b/tests/sys/netgraph/ksocket.c
@@ -0,0 +1,180 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Gleb Smirnoff <glebius@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netgraph.h>
+#include <netgraph/ng_socket.h>
+#include <netgraph/ng_ksocket.h>
+
+#include <errno.h>
+
+#include <atf-c.h>
+
+static void
+hellocheck(int wr, int rd)
+{
+ char sbuf[] = "Hello, peer!", rbuf[sizeof(sbuf)];
+
+ ATF_REQUIRE(send(wr, sbuf, sizeof(sbuf), 0) == sizeof(sbuf));
+ ATF_REQUIRE(recv(rd, rbuf, sizeof(rbuf), 0) == sizeof(sbuf));
+ ATF_REQUIRE(strcmp(sbuf, rbuf) == 0);
+}
+
+#define OURHOOK "ks"
+
+ATF_TC_WITHOUT_HEAD(udp_connect);
+ATF_TC_BODY(udp_connect, tc)
+{
+ struct ngm_mkpeer mkp = {
+ .type = NG_KSOCKET_NODE_TYPE,
+ .ourhook = OURHOOK,
+ .peerhook = "inet/dgram/udp",
+ };
+ struct sockaddr_in sin = {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
+ .sin_len = sizeof(sin),
+ };
+ socklen_t slen = sizeof(sin);
+ int cs, ds, us;
+
+ ATF_REQUIRE((us = socket(PF_INET, SOCK_DGRAM, 0)) > 0);
+ ATF_REQUIRE(bind(us, (struct sockaddr *)&sin, sizeof(sin)) == 0);
+ ATF_REQUIRE(getsockname(us, (struct sockaddr *)&sin, &slen) == 0);
+
+ ATF_REQUIRE(NgMkSockNode(NULL, &cs, &ds) == 0);
+ ATF_REQUIRE(NgSendMsg(cs, ".", NGM_GENERIC_COOKIE, NGM_MKPEER, &mkp,
+ sizeof(mkp)) >= 0);
+ ATF_REQUIRE(NgSendMsg(cs, ".:" OURHOOK, NGM_KSOCKET_COOKIE,
+ NGM_KSOCKET_CONNECT, &sin, sizeof(sin)) >= 0);
+
+ hellocheck(ds, us);
+}
+
+ATF_TC_WITHOUT_HEAD(udp_bind);
+ATF_TC_BODY(udp_bind, tc)
+{
+ struct ngm_mkpeer mkp = {
+ .type = NG_KSOCKET_NODE_TYPE,
+ .ourhook = OURHOOK,
+ .peerhook = "inet/dgram/udp",
+ };
+ struct sockaddr_in sin = {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
+ .sin_len = sizeof(sin),
+ };
+ struct ng_mesg *rep;
+ int cs, ds, us;
+
+ ATF_REQUIRE(NgMkSockNode(NULL, &cs, &ds) == 0);
+ ATF_REQUIRE(NgSendMsg(cs, ".", NGM_GENERIC_COOKIE, NGM_MKPEER, &mkp,
+ sizeof(mkp)) >= 0);
+ ATF_REQUIRE(NgSendMsg(cs, ".:" OURHOOK, NGM_KSOCKET_COOKIE,
+ NGM_KSOCKET_BIND, &sin, sizeof(sin)) >= 0);
+ ATF_REQUIRE(NgSendMsg(cs, ".:" OURHOOK, NGM_KSOCKET_COOKIE,
+ NGM_KSOCKET_GETNAME, NULL, 0) >= 0);
+ ATF_REQUIRE(NgAllocRecvMsg(cs, &rep, NULL) == sizeof(struct ng_mesg) +
+ sizeof(struct sockaddr_in));
+
+ ATF_REQUIRE((us = socket(PF_INET, SOCK_DGRAM, 0)) > 0);
+ ATF_REQUIRE(connect(us, (struct sockaddr *)rep->data,
+ sizeof(struct sockaddr_in)) == 0);
+
+ hellocheck(us, ds);
+}
+
+ATF_TC_WITHOUT_HEAD(udp6_connect);
+ATF_TC_BODY(udp6_connect, tc)
+{
+ struct ngm_mkpeer mkp = {
+ .type = NG_KSOCKET_NODE_TYPE,
+ .ourhook = OURHOOK,
+ .peerhook = "inet6/dgram/udp6",
+ };
+ struct sockaddr_in6 sin6 = {
+ .sin6_family = AF_INET6,
+ };
+ socklen_t slen = sizeof(sin6);
+ int cs, ds, us;
+
+ ATF_REQUIRE((us = socket(PF_INET6, SOCK_DGRAM, 0)) > 0);
+ ATF_REQUIRE(bind(us, (struct sockaddr *)&sin6, sizeof(sin6)) == 0);
+ ATF_REQUIRE(getsockname(us, (struct sockaddr *)&sin6, &slen) == 0);
+
+ ATF_REQUIRE(NgMkSockNode(NULL, &cs, &ds) == 0);
+ ATF_REQUIRE(NgSendMsg(cs, ".", NGM_GENERIC_COOKIE, NGM_MKPEER, &mkp,
+ sizeof(mkp)) >= 0);
+ ATF_REQUIRE(NgSendMsg(cs, ".:" OURHOOK, NGM_KSOCKET_COOKIE,
+ NGM_KSOCKET_CONNECT, &sin6, sizeof(sin6)) >= 0);
+
+ hellocheck(ds, us);
+}
+
+
+ATF_TC_WITHOUT_HEAD(udp6_bind);
+ATF_TC_BODY(udp6_bind, tc)
+{
+ struct ngm_mkpeer mkp = {
+ .type = NG_KSOCKET_NODE_TYPE,
+ .ourhook = OURHOOK,
+ .peerhook = "inet6/dgram/udp6",
+ };
+ struct sockaddr_in6 sin6 = {
+ .sin6_family = AF_INET6,
+ .sin6_len = sizeof(sin6),
+ };
+ struct ng_mesg *rep;
+ int cs, ds, us;
+
+ ATF_REQUIRE(NgMkSockNode(NULL, &cs, &ds) == 0);
+ ATF_REQUIRE(NgSendMsg(cs, ".", NGM_GENERIC_COOKIE, NGM_MKPEER, &mkp,
+ sizeof(mkp)) >= 0);
+ ATF_REQUIRE(NgSendMsg(cs, ".:" OURHOOK, NGM_KSOCKET_COOKIE,
+ NGM_KSOCKET_BIND, &sin6, sizeof(sin6)) >= 0);
+ ATF_REQUIRE(NgSendMsg(cs, ".:" OURHOOK, NGM_KSOCKET_COOKIE,
+ NGM_KSOCKET_GETNAME, NULL, 0) >= 0);
+ ATF_REQUIRE(NgAllocRecvMsg(cs, &rep, NULL) == sizeof(struct ng_mesg) +
+ sizeof(struct sockaddr_in6));
+
+ ATF_REQUIRE((us = socket(PF_INET6, SOCK_DGRAM, 0)) > 0);
+ ATF_REQUIRE(connect(us, (struct sockaddr *)rep->data,
+ sizeof(struct sockaddr_in6)) == 0);
+
+ hellocheck(us, ds);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, udp_connect);
+ ATF_TP_ADD_TC(tp, udp_bind);
+ ATF_TP_ADD_TC(tp, udp6_connect);
+ ATF_TP_ADD_TC(tp, udp6_bind);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/netgraph/ng_macfilter_test.sh b/tests/sys/netgraph/ng_macfilter_test.sh
new file mode 100755
index 000000000000..32342bb81e4c
--- /dev/null
+++ b/tests/sys/netgraph/ng_macfilter_test.sh
@@ -0,0 +1,443 @@
+#!/bin/sh
+#
+# Copyright (c) 2018-2020 Retina b.v.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+set -e
+
+progname="$(basename $0 .sh)"
+entries_lst="/tmp/$progname.entries.lst"
+entries2_lst="/tmp/$progname.entries2.lst"
+
+HOOKS=3
+HOOKSADD=42
+ITERATIONS=7
+SUBITERATIONS=71
+
+find_iface () {
+ # Figure out the first ethernet interface
+ ifconfig -u -l ether | awk '{print $1}'
+}
+
+loaded_modules=''
+load_modules () {
+ for kmod in "$@"; do
+ if ! kldstat -q -m $kmod; then
+ test_comment "Loading $kmod..."
+ kldload $kmod
+ loaded_modules="$loaded_modules $kmod"
+ fi
+ done
+}
+unload_modules () {
+ for kmod in $loaded_modules; do
+ # These cannot be unloaded
+ test $kmod = 'ng_ether' -o $kmod = 'ng_socket' \
+ && continue
+
+ test_comment "Unloading $kmod..."
+ kldunload $kmod
+ done
+ loaded_modules=''
+}
+
+configure_nodes () {
+ ngctl mkpeer $eth: macfilter lower ether # Connect the lower hook of the ether instance $eth to the ether hook of a new macfilter instance
+ ngctl name $eth:lower MF # Give the macfilter instance a name
+ ngctl mkpeer $eth: one2many upper one # Connect the upper hook of the ether instance $eth to the one hook of a new one2many instance
+ ngctl name $eth:upper O2M # Give the one2many instance a name
+ ngctl msg O2M: setconfig "{ xmitAlg=3 failAlg=1 enabledLinks=[ 1 1 ] }" # XMIT_FAILOVER -> send replies always out many0
+
+ ngctl connect MF: O2M: default many0 # Connect macfilter:default to the many0 hook of a one2many instance
+ for i in $(seq 1 1 $HOOKS); do
+ ngctl connect MF: O2M: out$i many$i
+ done
+}
+
+deconfigure_nodes () {
+ ngctl shutdown MF:
+ ngctl shutdown O2M:
+}
+
+cleanup () {
+ test_title "Cleaning up"
+
+ deconfigure_nodes
+ unload_modules
+
+ rm -f $entries_lst $entries2_lst
+}
+
+TSTNR=0
+TSTFAILS=0
+TSTSUCCS=0
+
+_test_next () { TSTNR=$((TSTNR + 1)); }
+_test_succ () { TSTSUCCS=$((TSTSUCCS + 1)); }
+_test_fail () { TSTFAILS=$((TSTFAILS + 1)); }
+
+test_cnt () { echo "1..${1:-$TSTNR}"; }
+test_title () {
+ local msg="$1"
+
+ printf '### %s ' "$msg"
+ printf '#%.0s' $(seq $((80 - ${#msg} - 5)))
+ printf "\n"
+}
+test_comment () { echo "# $1"; }
+test_bailout () { echo "Bail out!${1+:- $1}"; exit 1; }
+test_bail_on_fail () { test $TSTFAILS -eq 0 || test_bailout $1; }
+test_ok () {
+ local msg="$1"
+
+ _test_next
+ _test_succ
+ echo "ok $TSTNR - $msg"
+
+ return 0
+}
+test_not_ok () {
+ local msg="$1"
+
+ _test_next
+ _test_fail
+ echo "not ok $TSTNR - $msg"
+
+ return 1
+}
+test_eq () {
+ local v1="$1" v2="$2" msg="$3"
+
+ if [ "$v1" = "$v2" ]; then
+ test_ok "$v1 $msg"
+ else
+ test_not_ok "$v1 vs $v2 $msg"
+ fi
+}
+test_ne () {
+ local v1="$1" v2="$2" msg="$3"
+
+ if [ "$v1" != "$v2" ]; then
+ test_ok "$v1 $msg"
+ else
+ test_not_ok "$v1 vs $v2 $msg"
+ fi
+}
+test_lt () {
+ local v1=$1 v2=$2 msg="$3"
+
+ if [ "$v1" -lt "$v2" ]; then
+ test_ok "$v1 $msg"
+ else
+ test_not_ok "$v1 >= $v2 $msg"
+ fi
+}
+test_le () {
+ local v1=$1 v2=$2 msg="$3"
+
+ if [ "$v1" -le "$v2" ]; then
+ test_ok "$v1 $msg"
+ else
+ test_not_ok "$v1 >= $v2 $msg"
+ fi
+}
+test_gt () {
+ local v1=$1 v2=$2 msg="$3"
+
+ if [ "$v1" -gt "$v2" ]; then
+ test_ok "$v1 $msg"
+ else
+ test_not_ok "$v1 <= $v2 $msg"
+ fi
+}
+test_ge () {
+ local v1=$1 v2=$2 msg="$3"
+
+ if [ "$v1" -ge "$v2" ]; then
+ test_ok "$v1 $msg"
+ else
+ test_not_ok "$v1 <= $v2 $msg"
+ fi
+}
+test_failure () {
+ msg=$1
+ shift
+ if ! "$@"; then
+ test_ok "$msg - \"$@\" failed as expected"
+ else
+ test_not_ok "$msg - expected \"$@\" to fail but succeeded"
+ fi
+}
+test_success () {
+ msg=$1
+ shift
+ if ! "$@"; then
+ test_not_ok "$msg - \"$@\" failed unexpectedly"
+ else
+ test_ok "$msg - \"$@\" succeeded"
+ fi
+}
+
+gethooks () {
+ ngctl msg MF: 'gethooks' \
+ | perl -ne '$h{$1}=1 while s/hookname="(.*?)"//; sub END {print join(":", sort keys %h)."\n"}'
+}
+
+countmacs () {
+ local hookname=${1:-'[^"]*'}
+
+ ngctl msg MF: 'gethooks' \
+ | perl -ne 'sub BEGIN {$c=0} $c += $1 while s/hookname="'$hookname'" hookid=\d+ maccnt=(\d+)//; sub END {print "$c\n"}'
+}
+randomedge () {
+ local edge="out$(seq 0 1 $HOOKS | sort -R | head -1)"
+ test $edge = 'out0' \
+ && echo default \
+ || echo $edge
+}
+genmac () {
+ echo "00:00:00:00:$(printf "%02x" $1):$(printf "%02x" $2)"
+}
+
+
+
+################################################################################
+### Start ######################################################################
+################################################################################
+
+test_title "Setting up system..."
+load_modules netgraph ng_socket ng_ether ng_macfilter ng_one2many
+eth=$(find_iface)
+if [ -z "$eth" ]; then
+ echo "1..0 # SKIP could not find a valid interface"
+ echo "Available interfaces:"
+ ifconfig
+ exit 1
+fi
+test_comment "Using $eth..."
+
+
+test_title "Configuring netgraph nodes..."
+configure_nodes
+
+trap 'exit 99' 1 2 3 13 14 15
+trap 'cleanup' EXIT
+
+created_hooks=$(gethooks)
+rc=0
+
+# Update this number when adding new tests
+test_cnt 46
+
+
+################################################################################
+### Tests ######################################################################
+################################################################################
+
+################################################################################
+test_title "Test: Duplicate default hook"
+test_failure "duplicate connect of default hook" ngctl connect MF: O2M: default many99
+
+################################################################################
+test_title "Test: Add and remove hooks"
+test_success "connect MF:xxx1 to O2M:many$((HOOKS + 1))" ngctl connect MF: O2M: xxx1 many$((HOOKS + 1))
+test_success "connect MF:xxx2 to O2M:many$((HOOKS + 2))" ngctl connect MF: O2M: xxx2 many$((HOOKS + 2))
+test_success "connect MF:xxx3 to O2M:many$((HOOKS + 3))" ngctl connect MF: O2M: xxx3 many$((HOOKS + 3))
+hooks=$(gethooks)
+test_eq $created_hooks:xxx1:xxx2:xxx3 $hooks 'hooks after adding xxx1-3'
+
+test_success "rmhook MF:xxx$i" ngctl rmhook MF: xxx1
+hooks=$(gethooks)
+test_eq $created_hooks:xxx2:xxx3 $hooks 'hooks after removing xxx1'
+test_success "rmhook MF:xxx$i" ngctl rmhook MF: xxx2
+hooks=$(gethooks)
+test_eq $created_hooks:xxx3 $hooks 'hooks after removing xxx2'
+test_success "rmhook MF:xxx$i" ngctl rmhook MF: xxx3
+hooks=$(gethooks)
+test_eq $created_hooks $hooks 'hooks after removing xxx3'
+
+test_bail_on_fail
+
+################################################################################
+test_title "Test: Add many hooks"
+added_hooks=""
+for i in $(seq 10 1 $HOOKSADD); do
+ added_hooks="$added_hooks:xxx$i"
+ ngctl connect MF: O2M: xxx$i many$((HOOKS + i))
+done
+hooks=$(gethooks)
+test_eq $created_hooks$added_hooks $hooks 'hooks after adding many hooks'
+
+for h in $(echo $added_hooks | perl -ne 'chomp; %h=map { $_=>1 } split /:/; print "$_\n" for grep {$_} keys %h'); do
+ ngctl rmhook MF: $h
+done
+hooks=$(gethooks)
+test_eq $created_hooks $hooks 'hooks after adding many hooks'
+
+test_bail_on_fail
+
+
+################################################################################
+test_title "Test: Adding many MACs..."
+I=1
+for i in $(seq $ITERATIONS | sort -R); do
+ test_comment "Iteration $I/$ITERATIONS..."
+ for j in $(seq 0 1 $SUBITERATIONS); do
+ test $i = 2 && edge='out2' || edge='out1'
+ ether=$(genmac $j $i)
+
+ ngctl msg MF: 'direct' "{ hookname=\"$edge\" ether=$ether }"
+ done
+ I=$((I + 1))
+done
+
+n=$(countmacs out1)
+n2=$(( ( ITERATIONS - 1 ) * ( SUBITERATIONS + 1 ) ))
+test_eq $n $n2 'MACs in table for out1'
+n=$(countmacs out2)
+n2=$(( 1 * ( SUBITERATIONS + 1 ) ))
+test_eq $n $n2 'MACs in table for out2'
+n=$(countmacs out3)
+n2=0
+test_eq $n $n2 'MACs in table for out3'
+
+test_bail_on_fail
+
+
+################################################################################
+test_title "Test: Changing hooks for MACs..."
+for i in $(seq $ITERATIONS); do
+ edge='out3'
+ ether=$(genmac 0 $i)
+
+ ngctl msg MF: 'direct' "{ hookname=\"$edge\" ether=$ether }"
+done
+
+n=$(countmacs out1)
+n2=$(( ( ITERATIONS - 1 ) * ( SUBITERATIONS + 1 - 1 ) ))
+test_eq $n $n2 'MACs in table for out1'
+n=$(countmacs out2)
+n2=$(( 1 * ( SUBITERATIONS + 1 - 1 ) ))
+test_eq $n $n2 'MACs in table for out2'
+n=$(countmacs out3)
+n2=$ITERATIONS
+test_eq $n $n2 'MACs in table for out3'
+
+test_bail_on_fail
+
+
+################################################################################
+test_title "Test: Removing all MACs one by one..."
+I=1
+for i in $(seq $ITERATIONS | sort -R); do
+ test_comment "Iteration $I/$ITERATIONS..."
+ for j in $(seq 0 1 $SUBITERATIONS | sort -R); do
+ edge="default"
+ ether=$(genmac $j $i)
+
+ ngctl msg MF: 'direct' "{ hookname=\"$edge\" ether=$ether }"
+ done
+ I=$(($I + 1))
+done
+n=$(countmacs)
+n2=0
+test_eq $n $n2 'MACs in table'
+
+test_bail_on_fail
+
+
+################################################################################
+test_title "Test: Randomly adding MACs on random hooks..."
+rm -f $entries_lst
+for i in $(seq $ITERATIONS); do
+ test_comment "Iteration $i/$ITERATIONS..."
+ for j in $(seq 0 1 $SUBITERATIONS | sort -R); do
+ edge=$(randomedge)
+ ether=$(genmac $j $i)
+
+ ngctl msg MF: 'direct' "{ hookname=\"$edge\" ether=$ether }"
+
+ echo $ether $edge >> $entries_lst
+ done
+
+ n=$(countmacs)
+done
+
+n=$(countmacs out1)
+n2=$(grep -c ' out1' $entries_lst)
+test_eq $n $n2 'MACs in table for out1'
+n=$(countmacs out2)
+n2=$(grep -c ' out2' $entries_lst)
+test_eq $n $n2 'MACs in table for out2'
+n=$(countmacs out3)
+n2=$(grep -c ' out3' $entries_lst)
+test_eq $n $n2 'MACs in table for out3'
+
+test_bail_on_fail
+
+
+################################################################################
+test_title "Test: Randomly changing MAC assignments..."
+rm -f $entries2_lst
+for i in $(seq $ITERATIONS); do
+ test_comment "Iteration $i/$ITERATIONS..."
+ cat $entries_lst | while read ether edge; do
+ edge2=$(randomedge)
+
+ ngctl msg MF: 'direct' "{ hookname=\"$edge2\" ether=$ether }"
+
+ echo $ether $edge2 >> $entries2_lst
+ done
+
+ n=$(countmacs out1)
+ n2=$(grep -c ' out1' $entries2_lst)
+ test_eq $n $n2 'MACs in table for out1'
+ n=$(countmacs out2)
+ n2=$(grep -c ' out2' $entries2_lst)
+ test_eq $n $n2 'MACs in table for out2'
+ n=$(countmacs out3)
+ n2=$(grep -c ' out3' $entries2_lst)
+ test_eq $n $n2 'MACs in table for out3'
+
+ test_bail_on_fail
+
+ mv $entries2_lst $entries_lst
+done
+
+
+################################################################################
+test_title "Test: Resetting macfilter..."
+test_success "**** reset failed" ngctl msg MF: reset
+test_eq $(countmacs) 0 'MACs in table'
+
+test_bail_on_fail
+
+
+################################################################################
+
+exit 0
diff --git a/tests/sys/netgraph/socket.c b/tests/sys/netgraph/socket.c
new file mode 100644
index 000000000000..1e5d68e1d21f
--- /dev/null
+++ b/tests/sys/netgraph/socket.c
@@ -0,0 +1,63 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Gleb Smirnoff <glebius@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/socket.h>
+#include <netgraph.h>
+#include <netgraph/ng_socket.h>
+
+#include <errno.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(getsockname);
+ATF_TC_BODY(getsockname, tc)
+{
+ struct sockaddr_ng sg;
+ socklen_t len = sizeof(struct sockaddr_ng);
+#define NAME "0123456789012345678901234567891" /* 31 chars */
+ char name[NG_NODESIZ] = NAME;
+ int cs;
+
+ /* Unnamed node returns its ID as name. */
+ ATF_REQUIRE(NgMkSockNode(NULL, &cs, NULL) == 0);
+ ATF_REQUIRE(getsockname(cs, (struct sockaddr *)&sg, &len) == 0);
+ ATF_REQUIRE(strspn(sg.sg_data, "[0123456789abcdef]") >= 3 &&
+ sg.sg_data[strspn(sg.sg_data, "[0123456789abcdef]")] == '\0');
+ close(cs);
+
+ /* Named node. */
+ ATF_REQUIRE(NgMkSockNode(name, &cs, NULL) == 0);
+ ATF_REQUIRE(getsockname(cs, (struct sockaddr *)&sg, &len) == 0);
+ ATF_REQUIRE(strcmp(sg.sg_data, NAME) == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, getsockname);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/netgraph/util.c b/tests/sys/netgraph/util.c
new file mode 100644
index 000000000000..b25e63dfa76d
--- /dev/null
+++ b/tests/sys/netgraph/util.c
@@ -0,0 +1,277 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright 2021 Lutz Donnerhacke
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <atf-c.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/select.h>
+#include <sys/queue.h>
+
+#include "util.h"
+
+
+static int cs = -1, ds = -1;
+static ng_error_t error_handling = FAIL;
+
+#define CHECK(r, x) do { \
+ if (!(x)) { \
+ if (error_handling == PASS) \
+ return r; \
+ atf_tc_fail_requirement(file, line, "%s (%s)", \
+ #x " not met", strerror(errno));\
+ } \
+} while(0)
+
+struct data_handler
+{
+ char const *hook;
+ ng_data_handler_t handler;
+ SLIST_ENTRY(data_handler) next;
+};
+static SLIST_HEAD(, data_handler) data_head = SLIST_HEAD_INITIALIZER(data_head);
+static ng_msg_handler_t msg_handler = NULL;
+
+static void handle_data(void *ctx);
+static void handle_msg(void *ctx);
+
+void
+_ng_connect(char const *path1, char const *hook1,
+ char const *path2, char const *hook2,
+ char const *file, size_t line)
+{
+ struct ngm_connect c;
+
+ strncpy(c.ourhook, hook1, sizeof(c.ourhook));
+ strncpy(c.peerhook, hook2, sizeof(c.peerhook));
+ strncpy(c.path, path2, sizeof(c.path));
+
+ CHECK(, -1 != NgSendMsg(cs, path1,
+ NGM_GENERIC_COOKIE, NGM_CONNECT,
+ &c, sizeof(c)));
+}
+
+void
+_ng_mkpeer(char const *path1, char const *hook1,
+ char const *type, char const *hook2,
+ char const *file, size_t line)
+{
+ struct ngm_mkpeer p;
+
+ strncpy(p.ourhook, hook1, sizeof(p.ourhook));
+ strncpy(p.peerhook, hook2, sizeof(p.peerhook));
+ strncpy(p.type, type, sizeof(p.type));
+
+ CHECK(, -1 != NgSendMsg(cs, path1,
+ NGM_GENERIC_COOKIE, NGM_MKPEER,
+ &p, sizeof(p)));
+}
+
+void
+_ng_rmhook(char const *path, char const *hook,
+ char const *file, size_t line)
+{
+ struct ngm_rmhook h;
+
+ strncpy(h.ourhook, hook, sizeof(h.ourhook));
+
+ CHECK(, -1 != NgSendMsg(cs, path,
+ NGM_GENERIC_COOKIE, NGM_RMHOOK,
+ &h, sizeof(h)));
+}
+
+void
+_ng_name(char const *path, char const *name,
+ char const *file, size_t line)
+{
+ struct ngm_name n;
+
+ strncpy(n.name, name, sizeof(n.name));
+
+ CHECK(, -1 != NgSendMsg(cs, path,
+ NGM_GENERIC_COOKIE, NGM_NAME,
+ &n, sizeof(n)));
+}
+
+void
+_ng_shutdown(char const *path,
+ char const *file, size_t line)
+{
+ CHECK(, -1 != NgSendMsg(cs, path,
+ NGM_GENERIC_COOKIE, NGM_SHUTDOWN,
+ NULL, 0));
+}
+
+void
+ng_register_data(char const *hook, ng_data_handler_t proc)
+{
+ struct data_handler *p;
+
+ ATF_REQUIRE(NULL != (p = calloc(1, sizeof(struct data_handler))));
+ ATF_REQUIRE(NULL != (p->hook = strdup(hook)));
+ ATF_REQUIRE(NULL != (p->handler = proc));
+ SLIST_INSERT_HEAD(&data_head, p, next);
+}
+
+void
+_ng_send_data(char const *hook,
+ void const *data, size_t len,
+ char const *file, size_t line)
+{
+ CHECK(, -1 != NgSendData(ds, hook, data, len));
+}
+
+void
+ng_register_msg(ng_msg_handler_t proc)
+{
+ msg_handler = proc;
+}
+
+static void
+handle_msg(void *ctx)
+{
+ struct ng_mesg *m;
+ char path[NG_PATHSIZ];
+
+ ATF_REQUIRE(-1 != NgAllocRecvMsg(cs, &m, path));
+
+ if (msg_handler != NULL)
+ (*msg_handler) (path, m, ctx);
+
+ free(m);
+}
+
+static void
+handle_data(void *ctx)
+{
+ char hook[NG_HOOKSIZ];
+ struct data_handler *hnd;
+ u_char *data;
+ int len;
+
+ ATF_REQUIRE(0 < (len = NgAllocRecvData(ds, &data, hook)));
+ SLIST_FOREACH(hnd, &data_head, next)
+ {
+ if (0 == strcmp(hnd->hook, hook))
+ break;
+ }
+
+ if (hnd != NULL)
+ (*(hnd->handler)) (data, len, ctx);
+
+ free(data);
+}
+
+int
+ng_handle_event(unsigned int ms, void *context)
+{
+ fd_set fds;
+ int maxfd = (ds < cs) ? cs : ds;
+ struct timeval timeout = {0, ms * 1000lu};
+
+ FD_ZERO(&fds);
+ FD_SET(cs, &fds);
+ FD_SET(ds, &fds);
+retry:
+ switch (select(maxfd + 1, &fds, NULL, NULL, &timeout))
+ {
+ case -1:
+ ATF_REQUIRE_ERRNO(EINTR, 1);
+ goto retry;
+ case 0: /* timeout */
+ return 0;
+ default: /* something to do */
+ if (FD_ISSET(cs, &fds))
+ handle_msg(context);
+ if (FD_ISSET(ds, &fds))
+ handle_data(context);
+ return 1;
+ }
+}
+
+void
+ng_handle_events(unsigned int ms, void *context)
+{
+ while (ng_handle_event(ms, context))
+ ;
+}
+
+int
+_ng_send_msg(char const *path, char const *msg,
+ char const *file, size_t line)
+{
+ int res;
+
+ CHECK(-1, -1 != (res = NgSendAsciiMsg(cs, path, "%s", msg)));
+ return (res);
+}
+
+ng_error_t
+ng_errors(ng_error_t n)
+{
+ ng_error_t o = error_handling;
+
+ error_handling = n;
+ return (o);
+}
+
+void
+_ng_init(char const *file, size_t line)
+{
+ if (cs >= 0) /* prevent reinit */
+ return;
+
+ CHECK(, 0 == NgMkSockNode(NULL, &cs, &ds));
+ NgSetDebug(3);
+}
+
+#define GD(x) void \
+get_data##x(void *data, size_t len, void *ctx) {\
+ int *cnt = ctx; \
+ \
+ (void)data; \
+ (void)len; \
+ cnt[x]++; \
+}
+
+GD(0)
+GD(1)
+GD(2)
+GD(3)
+GD(4)
+GD(5)
+GD(6)
+GD(7)
+GD(8)
+GD(9)
diff --git a/tests/sys/netgraph/util.h b/tests/sys/netgraph/util.h
new file mode 100644
index 000000000000..44c9f3a360a7
--- /dev/null
+++ b/tests/sys/netgraph/util.h
@@ -0,0 +1,114 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright 2021 Lutz Donnerhacke
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <netgraph.h>
+
+void
+_ng_connect(char const *path1, char const *hook1,
+ char const *path2, char const *hook2,
+ char const *file, size_t line);
+#define ng_connect(p1,h1,p2,h2) \
+ _ng_connect(p1,h1,p2,h2,__FILE__,__LINE__)
+
+void
+_ng_mkpeer(char const *path1, char const *hook1,
+ char const *type, char const *hook2,
+ char const *file, size_t line);
+#define ng_mkpeer(p1,h1,t,h2) \
+ _ng_mkpeer(p1,h1,t,h2,__FILE__,__LINE__)
+
+void
+_ng_shutdown(char const *path,
+ char const *file, size_t line);
+#define ng_shutdown(p) \
+ _ng_shutdown(p,__FILE__,__LINE__)
+
+void
+_ng_rmhook(char const *path, char const *hook,
+ char const *file, size_t line);
+#define ng_rmhook(p,h) \
+ _ng_rmhook(p,h,__FILE__,__LINE__)
+
+void
+_ng_name(char const *path, char const *name,
+ char const *file, size_t line);
+#define ng_name(p,n) \
+ _ng_name(p,n,__FILE__,__LINE__)
+
+
+typedef void (*ng_data_handler_t)(void *, size_t, void *ctx);
+void ng_register_data(char const *hook, ng_data_handler_t proc);
+void
+_ng_send_data(char const *hook, void const *, size_t,
+ char const *file, size_t line);
+#define ng_send_data(h,d,l) \
+ _ng_send_data(h,d,l,__FILE__,__LINE__)
+
+typedef void (*ng_msg_handler_t)(char const *, struct ng_mesg *, void *);
+void ng_register_msg(ng_msg_handler_t proc);
+int
+_ng_send_msg(char const *path, char const *msg,
+ char const *file, size_t line);
+#define ng_send_msg(p,m) \
+ _ng_send_msg(p,m,__FILE__,__LINE__)
+
+int ng_handle_event(unsigned int ms, void *ctx);
+void ng_handle_events(unsigned int ms, void *ctx);
+
+typedef enum
+{
+ FAIL, PASS
+} ng_error_t;
+ng_error_t ng_errors(ng_error_t);
+
+void _ng_init(char const *file, size_t line);
+#define ng_init() \
+ _ng_init(__FILE__,__LINE__)
+
+/* Helper function to count received data */
+
+typedef int ng_counter_t[10];
+#define ng_counter_clear(x)\
+ bzero((x), sizeof(x))
+
+void get_data0(void *data, size_t len, void *ctx);
+void get_data1(void *data, size_t len, void *ctx);
+void get_data2(void *data, size_t len, void *ctx);
+void get_data3(void *data, size_t len, void *ctx);
+void get_data4(void *data, size_t len, void *ctx);
+void get_data5(void *data, size_t len, void *ctx);
+void get_data6(void *data, size_t len, void *ctx);
+void get_data7(void *data, size_t len, void *ctx);
+void get_data8(void *data, size_t len, void *ctx);
+void get_data9(void *data, size_t len, void *ctx);
diff --git a/tests/sys/netgraph/vlan_rotate.c b/tests/sys/netgraph/vlan_rotate.c
new file mode 100644
index 000000000000..8df3ab981a7c
--- /dev/null
+++ b/tests/sys/netgraph/vlan_rotate.c
@@ -0,0 +1,335 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright 2021 Lutz Donnerhacke
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <atf-c.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <net/ethernet.h>
+#include <netinet/in.h>
+
+#include "util.h"
+#include <netgraph/ng_bridge.h>
+
+struct vlan
+{
+ uint16_t proto;
+ uint16_t tag;
+} __packed;
+
+struct frame
+{
+ u_char dst[ETHER_ADDR_LEN];
+ u_char src[ETHER_ADDR_LEN];
+ struct vlan vlan[10];
+} __packed;
+
+static struct frame msg = {
+ .src = {2, 4, 6, 1, 3, 5},
+ .dst = {2, 4, 6, 1, 3, 7},
+ .vlan[0] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(1, 0, 0))},
+ .vlan[1] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(2, 0, 0))},
+ .vlan[2] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(3, 0, 0))},
+ .vlan[3] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(4, 0, 0))},
+ .vlan[4] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(5, 0, 0))},
+ .vlan[5] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(6, 0, 0))},
+ .vlan[6] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(7, 0, 0))},
+ .vlan[7] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(8, 0, 0))},
+ .vlan[8] = {htons(ETHERTYPE_VLAN), htons(EVL_MAKETAG(9, 0, 0))},
+ .vlan[9] = {0}
+};
+
+static void _basic(int);
+static void get_vlan(void *data, size_t len, void *ctx);
+
+static void
+get_vlan(void *data, size_t len, void *ctx)
+{
+ int *v = ctx, i;
+ struct frame *f = data;
+
+ (void)len;
+ for (i = 0; i < 10; i++)
+ v[i] = EVL_VLANOFTAG(ntohs(f->vlan[i].tag));
+}
+
+static void
+_basic(int direction)
+{
+ int r[10];
+ int i, rot, len;
+
+ ng_init();
+ ng_errors(PASS);
+ ng_shutdown("vr:");
+ ng_errors(FAIL);
+
+ ng_mkpeer(".", "a", "vlan_rotate", direction > 0 ? "original" : "ordered");
+ ng_name("a", "vr");
+ ng_connect(".", "b", "vr:", direction > 0 ? "ordered" : "original");
+ ng_register_data("b", get_vlan);
+
+ for (len = 9; len > 0; len--)
+ {
+ /* reduce the number of vlans */
+ msg.vlan[len].proto = htons(ETHERTYPE_IP);
+
+ for (rot = -len + 1; rot < len; rot++)
+ {
+ char cmd[40];
+
+ /* set rotation offset */
+ snprintf(cmd, sizeof(cmd), "setconf { min=0 max=9 rot=%d }", rot);
+ ng_send_msg("vr:", cmd);
+
+ ng_send_data("a", &msg, sizeof(msg));
+ ng_handle_events(50, &r);
+
+ /* check rotation */
+ for (i = 0; i < len; i++)
+ {
+ int expect = (2 * len + i - direction * rot) % len + 1;
+ int vlan = r[i];
+
+ ATF_CHECK_MSG(vlan == expect,
+ "len=%d rot=%d i=%d -> vlan=%d, expect=%d",
+ len, rot, i, r[i], expect);
+ }
+ }
+ }
+
+ ng_shutdown("vr:");
+}
+
+ATF_TC(basic);
+ATF_TC_HEAD(basic, conf)
+{
+ atf_tc_set_md_var(conf, "require.user", "root");
+}
+
+ATF_TC_BODY(basic, dummy)
+{
+ _basic(1);
+}
+
+ATF_TC(reverse);
+ATF_TC_HEAD(reverse, conf)
+{
+ atf_tc_set_md_var(conf, "require.user", "root");
+}
+
+ATF_TC_BODY(reverse, dummy)
+{
+ _basic(-1);
+}
+
+static void _ethertype(int);
+static void get_ethertype(void *data, size_t len, void *ctx);
+
+static void
+get_ethertype(void *data, size_t len, void *ctx)
+{
+ int *v = ctx, i;
+ struct frame *f = data;
+
+ (void)len;
+ for (i = 0; i < 10; i++)
+ v[i] = ntohs(f->vlan[i].proto);
+}
+
+static void
+_ethertype(int direction)
+{
+ int r[10];
+ int i, rounds = 20;
+
+ ng_init();
+ ng_errors(PASS);
+ ng_shutdown("vr:");
+ ng_errors(FAIL);
+
+ ng_mkpeer(".", "a", "vlan_rotate", direction > 0 ? "original" : "ordered");
+ ng_name("a", "vr");
+ ng_connect(".", "b", "vr:", direction > 0 ? "ordered" : "original");
+ ng_register_data("b", get_ethertype);
+
+ while (rounds-- > 0)
+ {
+ char cmd[40];
+ int len = 9;
+ int rot = rand() % (2 * len - 1) - len + 1;
+ int vlan[10];
+
+ for (i = 0; i < len; i++)
+ {
+ switch (rand() % 3)
+ {
+ default:
+ msg.vlan[i].proto = htons(ETHERTYPE_VLAN);
+ break;
+ case 1:
+ msg.vlan[i].proto = htons(ETHERTYPE_QINQ);
+ break;
+ case 2:
+ msg.vlan[i].proto = htons(ETHERTYPE_8021Q9100);
+ break;
+ }
+ }
+ msg.vlan[i].proto = htons(ETHERTYPE_IP);
+
+ for (i = 0; i < len; i++)
+ vlan[i] = msg.vlan[i].proto;
+
+ snprintf(cmd, sizeof(cmd), "setconf { min=0 max=9 rot=%d }", rot);
+ ng_send_msg("vr:", cmd);
+
+ bzero(r, sizeof(r));
+ ng_send_data("a", &msg, sizeof(msg));
+ ng_handle_events(50, &r);
+
+ /* check rotation */
+ for (i = 0; i < len; i++)
+ {
+ int expect = (2 * len + i - direction * rot) % len;
+
+ ATF_CHECK_MSG(r[i] == ntohs(vlan[expect]),
+ "len=%d rot=%d i=%d -> vlan=%04x, expect(%d)=%04x",
+ len, rot, i, ntohs(r[i]), expect, vlan[expect]);
+ }
+ }
+
+ ng_shutdown("vr:");
+}
+
+ATF_TC(ethertype);
+ATF_TC_HEAD(ethertype, conf)
+{
+ atf_tc_set_md_var(conf, "require.user", "root");
+}
+
+ATF_TC_BODY(ethertype, dummy)
+{
+ _ethertype(1);
+}
+
+ATF_TC(typeether);
+ATF_TC_HEAD(typeether, conf)
+{
+ atf_tc_set_md_var(conf, "require.user", "root");
+}
+
+ATF_TC_BODY(typeether, dummy)
+{
+ _ethertype(-1);
+}
+
+ATF_TC(minmax);
+ATF_TC_HEAD(minmax, conf)
+{
+ atf_tc_set_md_var(conf, "require.user", "root");
+}
+
+ATF_TC_BODY(minmax, dummy)
+{
+ ng_counter_t r;
+ int len;
+
+ ng_init();
+ ng_errors(PASS);
+ ng_shutdown("vr:");
+ ng_errors(FAIL);
+
+ ng_mkpeer(".", "a", "vlan_rotate", "original");
+ ng_name("a", "vr");
+ ng_connect(".", "b", "vr:", "ordered");
+ ng_connect(".", "c", "vr:", "excessive");
+ ng_connect(".", "d", "vr:", "incomplete");
+ ng_register_data("a", get_data0);
+ ng_register_data("b", get_data1);
+ ng_register_data("c", get_data2);
+ ng_register_data("d", get_data3);
+
+ ng_send_msg("vr:", "setconf { min=3 max=7 rot=0 }");
+ for (len = 9; len > 0; len--)
+ {
+ /* reduce the number of vlans */
+ msg.vlan[len].proto = htons(ETHERTYPE_IP);
+
+ ng_counter_clear(r);
+ ng_send_data("a", &msg, sizeof(msg));
+ ng_handle_events(50, &r);
+ if (len < 3)
+ ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 0 && r[3] == 1);
+ else if (len > 7)
+ ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 1 && r[3] == 0);
+ else
+ ATF_CHECK(r[0] == 0 && r[1] == 1 && r[2] == 0 && r[3] == 0);
+
+ ng_counter_clear(r);
+ ng_send_data("b", &msg, sizeof(msg));
+ ng_handle_events(50, &r);
+ if (len < 3)
+ ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 0 && r[3] == 1);
+ else if (len > 7)
+ ATF_CHECK(r[0] == 0 && r[1] == 0 && r[2] == 1 && r[3] == 0);
+ else
+ ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 0 && r[3] == 0);
+
+ ng_counter_clear(r);
+ ng_send_data("c", &msg, sizeof(msg));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 0 && r[3] == 0);
+
+ ng_counter_clear(r);
+ ng_send_data("d", &msg, sizeof(msg));
+ ng_handle_events(50, &r);
+ ATF_CHECK(r[0] == 1 && r[1] == 0 && r[2] == 0 && r[3] == 0);
+ }
+
+ ng_shutdown("vr:");
+}
+
+ATF_TP_ADD_TCS(vlan_rotate)
+{
+ /* Use "dd if=/dev/random bs=2 count=1 | od -x" to reproduce */
+ srand(0xb93b);
+
+ ATF_TP_ADD_TC(vlan_rotate, basic);
+ ATF_TP_ADD_TC(vlan_rotate, ethertype);
+ ATF_TP_ADD_TC(vlan_rotate, reverse);
+ ATF_TP_ADD_TC(vlan_rotate, typeether);
+ ATF_TP_ADD_TC(vlan_rotate, minmax);
+
+ return atf_no_error();
+}
diff --git a/tests/sys/netinet/Makefile b/tests/sys/netinet/Makefile
new file mode 100644
index 000000000000..cc525bf24480
--- /dev/null
+++ b/tests/sys/netinet/Makefile
@@ -0,0 +1,59 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/netinet
+BINDIR= ${TESTSDIR}
+
+TESTS_SUBDIRS+= libalias
+
+ATF_TESTS_C= broadcast \
+ fibs_multibind_test \
+ ip_reass_test \
+ ip6_v4mapped_test \
+ so_reuseport_lb_test \
+ socket_afinet \
+ tcp_connect_port_test \
+ tcp_implied_connect \
+ tcp_md5_getsockopt \
+ udp_bindings \
+ udp_io
+
+ATF_TESTS_SH= arp \
+ carp \
+ divert \
+ fibs \
+ fibs_test \
+ forward \
+ lpm \
+ multicast \
+ output \
+ redirect
+
+ATF_TESTS_PYTEST+= carp.py
+ATF_TESTS_PYTEST+= igmp.py
+
+LIBADD.so_reuseport_lb_test= pthread
+LIBADD.udp_bindings= pthread
+
+# Some of the arp tests look for log messages in the dmesg buffer, so run them
+# serially to avoid problems with interleaved output.
+TEST_METADATA.arp+= is_exclusive="true"
+TEST_METADATA.divert+= required_programs="python" \
+ execenv="jail" \
+ execenv_jail_params="vnet allow.raw_sockets"
+TEST_METADATA.fibs_test+= execenv="jail" \
+ execenv_jail_params="vnet allow.raw_sockets"
+TEST_METADATA.forward+= required_programs="python" \
+ execenv="jail" \
+ execenv_jail_params="vnet allow.raw_sockets"
+TEST_METADATA.output+= required_programs="python"
+TEST_METADATA.redirect+= required_programs="python"
+
+PROGS= udp_dontroute tcp_user_cookie sendto-IP_MULTICAST_IF
+
+${PACKAGE}FILES+= redirect.py
+
+${PACKAGE}FILESMODE_redirect.py=0555
+
+MAN=
+
+.include <bsd.test.mk>
diff --git a/tests/sys/netinet/Makefile.depend b/tests/sys/netinet/Makefile.depend
new file mode 100644
index 000000000000..ec18d8c500c6
--- /dev/null
+++ b/tests/sys/netinet/Makefile.depend
@@ -0,0 +1,17 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/arpa \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/netinet/arp.sh b/tests/sys/netinet/arp.sh
new file mode 100755
index 000000000000..df5dbc50ffa1
--- /dev/null
+++ b/tests/sys/netinet/arp.sh
@@ -0,0 +1,273 @@
+#!/usr/bin/env atf-sh
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2021 Alexander V. Chernikov
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+atf_test_case "arp_add_success" "cleanup"
+arp_add_success_head() {
+ atf_set descr 'Test static arp record addition'
+ atf_set require.user root
+}
+
+arp_add_success_body() {
+
+ vnet_init
+
+ jname="v4t-arp_add_success"
+
+ epair0=$(vnet_mkepair)
+
+ vnet_mkjail ${jname} ${epair0}a
+
+ jexec ${jname} ifconfig ${epair0}a inet 198.51.100.1/24
+
+ atf_check jexec ${jname} arp -s 198.51.100.2 90:10:00:01:02:03
+
+ atf_check -o match:"\? \(198.51.100.2\) at 90:10:00:01:02:03 on ${epair0}a permanent" jexec ${jname} arp -ni ${epair0}a 198.51.100.2
+}
+
+arp_add_success_cleanup() {
+ vnet_cleanup
+}
+
+
+atf_test_case "arp_del_success" "cleanup"
+arp_del_success_head() {
+ atf_set descr 'Test arp record deletion'
+ atf_set require.user root
+}
+
+arp_del_success_body() {
+
+ vnet_init
+
+ jname="v4t-arp_del_success"
+
+ epair0=$(vnet_mkepair)
+
+ vnet_mkjail ${jname} ${epair0}a
+
+ jexec ${jname} ifconfig ${epair0}a inet 198.51.100.1/24
+
+ jexec ${jname} ping -c1 -t1 198.51.100.2
+
+ atf_check -o match:"198.51.100.2 \(198.51.100.2\) deleted" jexec ${jname} arp -nd 198.51.100.2
+ atf_check -s exit:1 -o match:"198.51.100.2 \(198.51.100.2\) -- no entry" jexec ${jname} arp -n 198.51.100.2
+}
+
+arp_del_success_cleanup() {
+ vnet_cleanup
+}
+
+atf_test_case "pending_delete_if" "cleanup"
+pending_delete_if_head() {
+ atf_set descr 'Test having pending link layer lookups on interface delete'
+ atf_set require.user root
+}
+
+pending_delete_if_body() {
+ vnet_init
+
+ jname="arp_pending_delete_if"
+ epair=$(vnet_mkepair)
+
+ ifconfig ${epair}b up
+
+ vnet_mkjail ${jname} ${epair}a
+ jexec ${jname} ifconfig ${epair}a 198.51.100.1/24
+ for i in `seq 2 200`
+ do
+ jexec ${jname} ping 198.51.100.${i} &
+ done
+
+ # Give the ping processes time to send their ARP requests
+ sleep 1
+
+ jexec ${jname} arp -an
+ jexec ${jname} killall ping
+
+ # Delete the interface. Test failure panics the machine.
+ ifconfig ${epair}b destroy
+}
+
+pending_delete_if_cleanup() {
+ vnet_cleanup
+}
+
+
+atf_test_case "arp_lookup_host" "cleanup"
+arp_lookup_host_head() {
+ atf_set descr 'Test looking up a specific host'
+ atf_set require.user root
+}
+
+arp_lookup_host_body() {
+
+ vnet_init
+
+ jname="v4t-arp_lookup_host"
+
+ epair0=$(vnet_mkepair)
+
+ vnet_mkjail ${jname}a ${epair0}a
+ vnet_mkjail ${jname}b ${epair0}b
+
+ ipa=198.51.100.1
+ ipb=198.51.100.2
+
+ atf_check -o ignore \
+ ifconfig -j ${jname}a ${epair0}a inet ${ipa}/24
+ atf_check -o ignore \
+ ifconfig -j ${jname}b ${epair0}b inet ${ipb}/24
+
+ # get jail b's MAC address
+ eth="$(ifconfig -j ${jname}b ${epair0}b |
+ sed -nE "s/^\tether ([0-9a-f:]*)$/\1/p")"
+
+ # no entry yet
+ atf_check -s exit:1 -o match:"\(${ipb}\) -- no entry" \
+ jexec ${jname}a arp -n ${ipb}
+
+ # now ping jail b from jail a
+ atf_check -o ignore \
+ jexec ${jname}a ping -c1 ${ipb}
+
+ # should be populated
+ atf_check -o match:"\(${ipb}\) at $eth on ${epair0}a" \
+ jexec ${jname}a arp -n ${ipb}
+
+}
+
+arp_lookup_host_cleanup() {
+ vnet_cleanup
+}
+
+
+atf_test_case "static" "cleanup"
+static_head() {
+ atf_set descr 'Test arp -s/-S works'
+ atf_set require.user root
+}
+
+static_body() {
+
+ vnet_init
+
+ jname="v4t-arp_static_host"
+
+ epair0=$(vnet_mkepair)
+
+ vnet_mkjail ${jname}a ${epair0}a
+ vnet_mkjail ${jname}b ${epair0}b
+
+ ipa=198.51.100.1
+ ipb=198.51.100.2
+ ipb_re=$(echo ${ipb} | sed 's/\./\\./g')
+ max_age=$(sysctl -n net.link.ether.inet.max_age)
+ max_age="(${max_age}|$((${max_age} - 1)))"
+
+ atf_check ifconfig -j ${jname}a ${epair0}a inet ${ipa}/24
+ eth="$(ifconfig -j ${jname}b ${epair0}b |
+ sed -nE "s/^\tether ([0-9a-f:]*)$/\1/p")"
+
+ # Expected outputs
+ permanent=\
+"? (${ipb}) at 00:00:00:00:00:00 on ${epair0}a permanent [ethernet]\n"
+ temporary_re=\
+"\? \(${ipb_re}\) at ${eth} on ${epair0}a expires in ${max_age} seconds \[ethernet\]"
+ deleted=\
+"${ipb} (${ipb}) deleted\n"
+
+ # first check -s
+ atf_check jexec ${jname}a arp -s ${ipb} 0:0:0:0:0:0
+ # the jail B ifconfig will send gratuitous ARP that will trigger A
+ atf_check ifconfig -j ${jname}b ${epair0}b inet ${ipb}/24
+ atf_check -o "inline:${permanent}" jexec ${jname}a arp -n ${ipb}
+ if [ $(sysctl -n net.link.ether.inet.log_arp_permanent_modify) -ne 0 ];
+ then
+ msg=$(dmesg | tail -n 1)
+ atf_check_equal "${msg}" \
+"arp: ${eth} attempts to modify permanent entry for ${ipb} on ${epair0}a"
+ fi
+
+ # then check -S
+ atf_check -o "inline:${deleted}" jexec ${jname}a arp -nd ${ipb}
+ atf_check -o ignore jexec ${jname}b ping -c1 ${ipa}
+ atf_check -o "match:${temporary_re}" jexec ${jname}a arp -n ${ipb}
+ # Note: this doesn't fail, tracked all the way down to FreeBSD 8
+ # atf_check -s not-exit:0 jexec ${jname}a arp -s ${ipb} 0:0:0:0:0:0
+ atf_check -o "inline:${deleted}" \
+ jexec ${jname}a arp -S ${ipb} 0:0:0:0:0:0
+ atf_check -o "inline:${permanent}" jexec ${jname}a arp -n ${ipb}
+}
+
+static_cleanup() {
+ vnet_cleanup
+}
+
+atf_test_case "garp" "cleanup"
+garp_head() {
+ atf_set descr 'Basic gratuitous arp test'
+ atf_set require.user root
+}
+
+garp_body() {
+ vnet_init
+
+ j="v4t-garp"
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail ${j} ${epair}a
+ atf_check -s exit:0 -o ignore \
+ jexec ${j} sysctl net.link.ether.inet.garp_rexmit_count=3
+ jexec ${j} ifconfig ${epair}a inet 192.0.2.1/24 up
+
+ # Allow some time for the timer to actually fire
+ sleep 5
+}
+
+garp_cleanup() {
+ vnet_cleanup
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case "arp_add_success"
+ atf_add_test_case "arp_del_success"
+ atf_add_test_case "pending_delete_if"
+ atf_add_test_case "arp_lookup_host"
+ atf_add_test_case "static"
+ atf_add_test_case "garp"
+}
+
+# end
+
diff --git a/tests/sys/netinet/broadcast.c b/tests/sys/netinet/broadcast.c
new file mode 100644
index 000000000000..e7850d513663
--- /dev/null
+++ b/tests/sys/netinet/broadcast.c
@@ -0,0 +1,196 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Gleb Smirnoff <glebius@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <ifaddrs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+static const char buf[] = "Hello";
+
+/* Create a UDP socket with SO_BROADCAST set. */
+static int
+bcastsock(void)
+{
+ int s;
+
+ ATF_REQUIRE((s = socket(PF_INET, SOCK_DGRAM, 0)) > 0);
+ ATF_REQUIRE(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &(int){1},
+ sizeof(int)) == 0);
+ return (s);
+}
+
+/* Send on socket 's' with address 'to', confirm receive on 'r'. */
+static void
+bcastecho(int s, struct sockaddr_in *to, int r)
+{
+ char rbuf[sizeof(buf)];
+
+ printf("Sending to %s\n", inet_ntoa(to->sin_addr));
+ ATF_REQUIRE_MSG(sendto(s, buf, sizeof(buf), 0, (struct sockaddr *)to,
+ sizeof(*to)) == sizeof(buf), "sending of broadcast failed: %d",
+ errno);
+ ATF_REQUIRE(recv(r, rbuf, sizeof(rbuf), 0) == sizeof(rbuf));
+ ATF_REQUIRE_MSG(memcmp(buf, rbuf, sizeof(buf)) == 0,
+ "failed to receive own broadcast");
+}
+
+/* Find a first broadcast capable interface and copy its broadcast address. */
+static void
+firstbcast(struct in_addr *out)
+{
+ struct ifaddrs *ifa0, *ifa;
+ struct sockaddr_in sin;
+
+ ATF_REQUIRE(getifaddrs(&ifa0) == 0);
+ for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next)
+ if (ifa->ifa_addr->sa_family == AF_INET &&
+ (ifa->ifa_flags & IFF_BROADCAST))
+ break;
+ if (ifa == NULL) {
+ freeifaddrs(ifa0);
+ atf_tc_skip("No broadcast address found");
+ }
+ memcpy(&sin, ifa->ifa_broadaddr, sizeof(struct sockaddr_in));
+ *out = sin.sin_addr;
+ freeifaddrs(ifa0);
+}
+
+/* Application sends to INADDR_BROADCAST, and this goes on the wire. */
+ATF_TC(INADDR_BROADCAST);
+ATF_TC_HEAD(INADDR_BROADCAST, tc)
+{
+ atf_tc_set_md_var(tc, "require.config", "allow_network_access");
+}
+ATF_TC_BODY(INADDR_BROADCAST, tc)
+{
+ struct sockaddr_in sin = {
+ .sin_family = AF_INET,
+ .sin_len = sizeof(struct sockaddr_in),
+ };
+ socklen_t slen = sizeof(sin);
+ int l, s;
+
+ l = bcastsock();
+ ATF_REQUIRE(bind(l, (struct sockaddr *)&sin, sizeof(sin)) == 0);
+ ATF_REQUIRE(getsockname(l, (struct sockaddr *)&sin, &slen) == 0);
+ sin.sin_addr.s_addr = htonl(INADDR_BROADCAST);
+
+ s = bcastsock();
+ bcastecho(s, &sin, l);
+
+ close(s);
+ close(l);
+}
+
+/*
+ * Application sends on broadcast address of an interface, INADDR_BROADCAST
+ * goes on the wire of the selected interface.
+ */
+ATF_TC_WITHOUT_HEAD(IP_ONESBCAST);
+ATF_TC_BODY(IP_ONESBCAST, tc)
+{
+ struct ifaddrs *ifa0, *ifa;
+ struct sockaddr_in sin = {
+ .sin_family = AF_INET,
+ .sin_len = sizeof(struct sockaddr_in),
+ };
+ socklen_t slen = sizeof(sin);
+ int s, l;
+ in_port_t port;
+ bool skip = true;
+
+ s = bcastsock();
+ ATF_REQUIRE(setsockopt(s, IPPROTO_IP, IP_ONESBCAST, &(int){1},
+ sizeof(int)) == 0);
+
+ l = bcastsock();
+ ATF_REQUIRE(bind(l, (struct sockaddr *)&sin, sizeof(sin)) == 0);
+ ATF_REQUIRE(getsockname(l, (struct sockaddr *)&sin, &slen) == 0);
+ port = sin.sin_port;
+
+ ATF_REQUIRE(getifaddrs(&ifa0) == 0);
+ for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr->sa_family != AF_INET)
+ continue;
+ if (!(ifa->ifa_flags & IFF_BROADCAST))
+ continue;
+ skip = false;
+ memcpy(&sin, ifa->ifa_broadaddr, sizeof(struct sockaddr_in));
+ sin.sin_port = port;
+ bcastecho(s, &sin, l);
+ }
+ freeifaddrs(ifa0);
+ close(s);
+ close(l);
+ if (skip)
+ atf_tc_skip("No broadcast address found");
+}
+
+/*
+ * Application sends on broadcast address of an interface, and this is what
+ * goes out the wire.
+ */
+ATF_TC_WITHOUT_HEAD(local_broadcast);
+ATF_TC_BODY(local_broadcast, tc)
+{
+ struct sockaddr_in sin = {
+ .sin_family = AF_INET,
+ .sin_len = sizeof(struct sockaddr_in),
+ };
+ socklen_t slen = sizeof(sin);
+ int s, l;
+
+ s = bcastsock();
+ l = bcastsock();
+ ATF_REQUIRE(bind(l, (struct sockaddr *)&sin, sizeof(sin)) == 0);
+ ATF_REQUIRE(getsockname(l, (struct sockaddr *)&sin, &slen) == 0);
+ firstbcast(&sin.sin_addr);
+
+ bcastecho(s, &sin, l);
+
+ close(s);
+ close(l);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, INADDR_BROADCAST);
+ ATF_TP_ADD_TC(tp, IP_ONESBCAST);
+ ATF_TP_ADD_TC(tp, local_broadcast);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/netinet/carp.py b/tests/sys/netinet/carp.py
new file mode 100644
index 000000000000..e35c9470d035
--- /dev/null
+++ b/tests/sys/netinet/carp.py
@@ -0,0 +1,69 @@
+import pytest
+from atf_python.sys.net.tools import ToolsHelper
+from atf_python.sys.net.vnet import VnetTestTemplate
+
+sc = None
+
+
+def filter_f(x):
+ ip = x.getlayer(sc.IP)
+ if not ip:
+ return False
+
+ return ip.proto == 112
+
+
+class TestCarp(VnetTestTemplate):
+ REQUIRED_MODULES = ["carp"]
+ TOPOLOGY = {
+ "vnet1": {"ifaces": ["if1"]},
+ "if1": {"prefixes4": [("192.0.2.1/24", "192.0.2.2/24")]},
+ }
+
+ def setup_method(self, method):
+ global sc
+ if sc is None:
+ import scapy.all as _sc
+
+ sc = _sc
+ super().setup_method(method)
+
+ @classmethod
+ def check_carp_src_mac(self, pkts):
+ for p in pkts:
+ if not filter_f(p):
+ continue
+
+ print("Packet src mac {}".format(p.src))
+
+ if p.src != "00:00:5e:00:01:01":
+ raise
+
+ @pytest.mark.require_progs(["scapy"])
+ def test_source_mac(self):
+ "Test carp packets source address"
+
+ if1 = self.vnet.iface_alias_map["if1"]
+
+ ToolsHelper.print_output(
+ "ifconfig {} add vhid 1 192.0.2.203/24".format(if1.name)
+ )
+
+ carp_pkts = sc.sniff(iface=if1.name, stop_filter=filter_f, timeout=5)
+
+ self.check_carp_src_mac(carp_pkts)
+
+ @pytest.mark.require_progs(["scapy"])
+ def test_source_mac_vrrp(self):
+ "Test VRRP packets source address"
+
+ if1 = self.vnet.iface_alias_map["if1"]
+
+ ToolsHelper.print_output(
+ "ifconfig {} add vhid 1 carpver 3 192.0.2.203/24".format(if1.name)
+ )
+
+ carp_pkts = sc.sniff(iface=if1.name, stop_filter=filter_f, timeout=5)
+
+ self.check_carp_src_mac(carp_pkts)
+
diff --git a/tests/sys/netinet/carp.sh b/tests/sys/netinet/carp.sh
new file mode 100755
index 000000000000..2aae2854826e
--- /dev/null
+++ b/tests/sys/netinet/carp.sh
@@ -0,0 +1,590 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Kristof Provost <kp@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+is_master()
+{
+ jail=$1
+ itf=$2
+
+ jexec ${jail} ifconfig ${itf} | grep -E '(carp|vrrp)' | grep MASTER
+}
+
+wait_for_carp()
+{
+ jail1=$1
+ itf1=$2
+ jail2=$3
+ itf2=$4
+
+ while [ -z "$(is_master ${jail1} ${itf1})" ] &&
+ [ -z "$(is_master ${jail2} ${itf2})" ]; do
+ sleep 1
+ done
+
+ if [ -n "$(is_master ${jail1} ${itf1})" ] &&
+ [ -n "$(is_master ${jail2} ${itf2})" ]; then
+ atf_fail "Both jails are master"
+ fi
+}
+
+carp_init()
+{
+ if ! kldstat -q -m carp; then
+ atf_skip "This test requires carp"
+ fi
+
+ vnet_init
+}
+
+atf_test_case "basic_v4" "cleanup"
+basic_v4_head()
+{
+ atf_set descr 'Basic CARP test (IPv4)'
+ atf_set require.user root
+}
+
+basic_v4_body()
+{
+ carp_init
+ vnet_init_bridge
+
+ bridge=$(vnet_mkbridge)
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+
+ vnet_mkjail carp_basic_v4_one ${bridge} ${epair_one}a ${epair_two}a
+ vnet_mkjail carp_basic_v4_two ${epair_one}b
+ vnet_mkjail carp_basic_v4_three ${epair_two}b
+
+ jexec carp_basic_v4_one ifconfig ${bridge} 192.0.2.4/29 up
+ jexec carp_basic_v4_one ifconfig ${bridge} addm ${epair_one}a \
+ addm ${epair_two}a
+ jexec carp_basic_v4_one ifconfig ${epair_one}a up
+ jexec carp_basic_v4_one ifconfig ${epair_two}a up
+
+ jexec carp_basic_v4_two ifconfig ${epair_one}b 192.0.2.202/29 up
+ jexec carp_basic_v4_two ifconfig ${epair_one}b add vhid 1 192.0.2.1/29
+
+ jexec carp_basic_v4_three ifconfig ${epair_two}b 192.0.2.203/29 up
+ jexec carp_basic_v4_three ifconfig ${epair_two}b add vhid 1 \
+ 192.0.2.1/29
+
+ wait_for_carp carp_basic_v4_two ${epair_one}b \
+ carp_basic_v4_three ${epair_two}b
+
+ atf_check -s exit:0 -o ignore jexec carp_basic_v4_one \
+ ping -c 3 192.0.2.1
+}
+
+basic_v4_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "vrrp_v4" "cleanup"
+vrrp_v4_head()
+{
+ atf_set descr 'Basic VRRP test (IPv4)'
+ atf_set require.user root
+}
+
+vrrp_v4_body()
+{
+ carp_init
+ vnet_init_bridge
+
+ j=vrrp_basic_v4
+
+ bridge=$(vnet_mkbridge)
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+
+ vnet_mkjail ${j}_one ${bridge} ${epair_one}a ${epair_two}a
+ vnet_mkjail ${j}_two ${epair_one}b
+ vnet_mkjail ${j}_three ${epair_two}b
+
+ jexec ${j}_one ifconfig ${bridge} 192.0.2.4/29 up
+ jexec ${j}_one ifconfig ${bridge} addm ${epair_one}a \
+ addm ${epair_two}a
+ jexec ${j}_one ifconfig ${epair_one}a up
+ jexec ${j}_one ifconfig ${epair_two}a up
+
+ jexec ${j}_two ifconfig ${epair_one}b 192.0.2.202/29 up
+ jexec ${j}_two ifconfig ${epair_one}b add vhid 1 carpver 3 192.0.2.1/29
+
+ jexec ${j}_three ifconfig ${epair_two}b 192.0.2.203/29 up
+ jexec ${j}_three ifconfig ${epair_two}b add vhid 1 carpver 3 \
+ 192.0.2.1/29
+
+ wait_for_carp ${j}_two ${epair_one}b \
+ ${j}_three ${epair_two}b
+
+ atf_check -s exit:0 -o ignore jexec ${j}_one \
+ ping -c 3 192.0.2.1
+}
+
+vrrp_v4_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "unicast_v4" "cleanup"
+unicast_v4_head()
+{
+ atf_set descr 'Unicast CARP test (IPv4)'
+ atf_set require.user root
+}
+
+unicast_v4_body()
+{
+ carp_init
+
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+
+ vnet_mkjail carp_uni_v4_one ${epair_one}a ${epair_two}a
+ vnet_mkjail carp_uni_v4_two ${epair_one}b
+ vnet_mkjail carp_uni_v4_three ${epair_two}b
+
+ jexec carp_uni_v4_one sysctl net.inet.ip.forwarding=1
+ jexec carp_uni_v4_one ifconfig ${epair_one}a inet 198.51.100.1/25
+ jexec carp_uni_v4_one ifconfig ${epair_two}a inet 198.51.100.129/25
+
+ jexec carp_uni_v4_two sysctl net.inet.ip.forwarding=1
+ jexec carp_uni_v4_two ifconfig ${epair_one}b 198.51.100.2/25 up
+ jexec carp_uni_v4_two route add 198.51.100.224 198.51.100.1
+ # A peer address x.x.x.224 to catch PR 284872
+ jexec carp_uni_v4_two ifconfig ${epair_one}b add vhid 1 \
+ peer 198.51.100.224 192.0.2.1/32
+
+ jexec carp_uni_v4_three sysctl net.inet.ip.forwarding=1
+ jexec carp_uni_v4_three ifconfig ${epair_two}b 198.51.100.224/25 up
+ jexec carp_uni_v4_three route add 198.51.100.2 198.51.100.129
+ jexec carp_uni_v4_three ifconfig ${epair_two}b add vhid 1 \
+ peer 198.51.100.2 192.0.2.1/32
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore jexec carp_uni_v4_two \
+ ping -c 1 198.51.100.224
+
+ wait_for_carp carp_uni_v4_two ${epair_one}b \
+ carp_uni_v4_three ${epair_two}b
+
+ # Setup RIPv2 route daemon
+ jexec carp_uni_v4_two routed -s -Pripv2
+ jexec carp_uni_v4_three routed -s -Pripv2
+ jexec carp_uni_v4_one routed -Pripv2
+
+ # XXX Wait for route propagation
+ sleep 3
+
+ atf_check -s exit:0 -o ignore jexec carp_uni_v4_one \
+ ping -c 3 192.0.2.1
+
+ # Check that we remain in unicast when tweaking settings
+ atf_check -s exit:0 -o ignore \
+ jexec carp_uni_v4_two ifconfig ${epair_one}b vhid 1 advskew 2
+ atf_check -s exit:0 -o match:"peer 198.51.100.224" \
+ jexec carp_uni_v4_two ifconfig ${epair_one}b
+}
+
+unicast_v4_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "basic_v6" "cleanup"
+basic_v6_head()
+{
+ atf_set descr 'Basic CARP test (IPv6)'
+ atf_set require.user root
+}
+
+basic_v6_body()
+{
+ carp_init
+ vnet_init_bridge
+
+ bridge=$(vnet_mkbridge)
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+
+ vnet_mkjail carp_basic_v6_one ${bridge} ${epair_one}a ${epair_two}a
+ vnet_mkjail carp_basic_v6_two ${epair_one}b
+ vnet_mkjail carp_basic_v6_three ${epair_two}b
+
+ jexec carp_basic_v6_one ifconfig ${bridge} inet6 2001:db8::0:4/64 up \
+ no_dad
+ jexec carp_basic_v6_one ifconfig ${bridge} addm ${epair_one}a \
+ addm ${epair_two}a
+ jexec carp_basic_v6_one ifconfig ${epair_one}a up
+ jexec carp_basic_v6_one ifconfig ${epair_two}a up
+
+ jexec carp_basic_v6_two ifconfig ${epair_one}b inet6 \
+ 2001:db8::1:2/64 up no_dad
+ jexec carp_basic_v6_two ifconfig ${epair_one}b inet6 add vhid 1 \
+ 2001:db8::0:1/64
+
+ jexec carp_basic_v6_three ifconfig ${epair_two}b inet6 2001:db8::1:3/64 up no_dad
+ jexec carp_basic_v6_three ifconfig ${epair_two}b inet6 add vhid 1 \
+ 2001:db8::0:1/64
+
+ wait_for_carp carp_basic_v6_two ${epair_one}b \
+ carp_basic_v6_three ${epair_two}b
+
+ atf_check -s exit:0 -o ignore jexec carp_basic_v6_one \
+ ping -6 -c 3 2001:db8::0:1
+}
+
+basic_v6_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "vrrp_v6" "cleanup"
+vrrp_v6_head()
+{
+ atf_set descr 'Basic VRRP test (IPv6)'
+ atf_set require.user root
+}
+
+vrrp_v6_body()
+{
+ carp_init
+ vnet_init_bridge
+
+ j=carp_basic_v6
+
+ bridge=$(vnet_mkbridge)
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+
+ vnet_mkjail ${j}_one ${bridge} ${epair_one}a ${epair_two}a
+ vnet_mkjail ${j}_two ${epair_one}b
+ vnet_mkjail ${j}_three ${epair_two}b
+
+ jexec ${j}_one ifconfig ${bridge} inet6 2001:db8::0:4/64 up \
+ no_dad
+ jexec ${j}_one ifconfig ${bridge} addm ${epair_one}a \
+ addm ${epair_two}a
+ jexec ${j}_one ifconfig ${epair_one}a up
+ jexec ${j}_one ifconfig ${epair_two}a up
+
+ jexec ${j}_two ifconfig ${epair_one}b inet6 \
+ 2001:db8::1:2/64 up no_dad
+ jexec ${j}_two ifconfig ${epair_one}b inet6 add vhid 1 carpver 3 \
+ 2001:db8::0:1/64
+
+ jexec ${j}_three ifconfig ${epair_two}b inet6 2001:db8::1:3/64 up no_dad
+ jexec ${j}_three ifconfig ${epair_two}b inet6 add vhid 1 carpver 3 \
+ 2001:db8::0:1/64
+
+ wait_for_carp ${j}_two ${epair_one}b \
+ ${j}_three ${epair_two}b
+
+ atf_check -s exit:0 -o ignore jexec ${j}_one \
+ ping -6 -c 3 2001:db8::0:1
+}
+
+vrrp_v6_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "unicast_v6" "cleanup"
+unicast_v6_head()
+{
+ atf_set descr 'Unicast CARP test (IPv6)'
+ atf_set require.user root
+}
+
+unicast_v6_body()
+{
+ carp_init
+ vnet_init_bridge
+
+ bridge=$(vnet_mkbridge)
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+
+ vnet_mkjail carp_uni_v6_one ${bridge} ${epair_one}a ${epair_two}a
+ vnet_mkjail carp_uni_v6_two ${epair_one}b
+ vnet_mkjail carp_uni_v6_three ${epair_two}b
+
+ jexec carp_uni_v6_one sysctl net.inet6.ip6.forwarding=1
+ jexec carp_uni_v6_one ifconfig ${bridge} addm ${epair_one}a \
+ addm ${epair_two}a
+ jexec carp_uni_v6_one ifconfig ${epair_one}a up
+ jexec carp_uni_v6_one ifconfig ${epair_two}a up
+ jexec carp_uni_v6_one ifconfig ${bridge} inet6 2001:db8::0:4/64 up \
+ no_dad
+ jexec carp_uni_v6_one ifconfig ${bridge} inet6 alias 2001:db8:1::1/64 \
+ no_dad up
+ jexec carp_uni_v6_one ifconfig ${bridge} inet6 alias 2001:db8:2::1/64 \
+ no_dad up
+
+ jexec carp_uni_v6_two ifconfig ${epair_one}b inet6 2001:db8:1::2/64 \
+ no_dad up
+ jexec carp_uni_v6_two route -6 add default 2001:db8:1::1
+ jexec carp_uni_v6_two ifconfig ${epair_one}b inet6 add vhid 1 \
+ peer6 2001:db8:2::2 \
+ 2001:db8::0:1/64
+
+ jexec carp_uni_v6_three ifconfig ${epair_two}b inet6 2001:db8:2::2/64 \
+ no_dad up
+ jexec carp_uni_v6_three route -6 add default 2001:db8:2::1
+ jexec carp_uni_v6_three ifconfig ${epair_two}b inet6 add vhid 1 \
+ peer6 2001:db8:1::2 \
+ 2001:db8::0:1/64
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore jexec carp_uni_v6_two \
+ ping -6 -c 1 2001:db8:2::2
+
+ wait_for_carp carp_uni_v6_two ${epair_one}b \
+ carp_uni_v6_three ${epair_two}b
+
+ atf_check -s exit:0 -o ignore jexec carp_uni_v6_one \
+ ping -6 -c 3 2001:db8::0:1
+}
+
+unicast_v6_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "unicast_ll_v6" "cleanup"
+unicast_ll_v6_head()
+{
+ atf_set descr 'Unicast CARP test (IPv6, link-local)'
+ atf_set require.user root
+}
+
+unicast_ll_v6_body()
+{
+ carp_init
+ vnet_init_bridge
+
+ j=carp_uni_ll_v6
+
+ bridge=$(vnet_mkbridge)
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+
+ vnet_mkjail ${j}_one ${bridge} ${epair_one}a ${epair_two}a
+ vnet_mkjail ${j}_two ${epair_one}b
+ vnet_mkjail ${j}_three ${epair_two}b
+
+ jexec ${j}_one ifconfig ${bridge} addm ${epair_one}a \
+ addm ${epair_two}a
+ jexec ${j}_one ifconfig ${epair_one}a up
+ jexec ${j}_one ifconfig ${epair_two}a up
+ jexec ${j}_one ifconfig ${bridge} inet6 2001:db8::0:4/64 up \
+ no_dad
+ jexec ${j}_one ifconfig ${bridge} inet6 alias 2001:db8:1::1/64 \
+ no_dad up
+
+ jexec ${j}_two ifconfig ${epair_one}b inet6 2001:db8:1::2/64 \
+ no_dad up
+ jexec ${j}_three ifconfig ${epair_two}b inet6 2001:db8:1::3/64 \
+ no_dad up
+
+ ll_one=$(jexec ${j}_two ifconfig ${epair_one}b | awk "/ .*%${epair_one}b.* / { print \$2 }" | cut -d % -f 1)
+ ll_two=$(jexec ${j}_three ifconfig ${epair_two}b | awk "/ .*%${epair_two}b.* / { print \$2 }" | cut -d % -f 1)
+
+ jexec ${j}_two ifconfig ${epair_one}b inet6 add vhid 1 \
+ peer6 ${ll_two} \
+ 2001:db8::0:1/64
+ jexec ${j}_three ifconfig ${epair_two}b inet6 add vhid 1 \
+ peer6 ${ll_one} \
+ 2001:db8::0:1/64
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore jexec ${j}_two \
+ ping -6 -c 1 2001:db8:1::3
+
+ wait_for_carp ${j}_two ${epair_one}b \
+ ${j}_three ${epair_two}b
+
+ atf_check -s exit:0 -o ignore jexec ${j}_one \
+ ping -6 -c 3 2001:db8::0:1
+}
+
+unicast_ll_v6_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "negative_demotion" "cleanup"
+negative_demotion_head()
+{
+ atf_set descr 'Test PR #259528'
+ atf_set require.user root
+}
+
+negative_demotion_body()
+{
+ carp_init
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail one ${epair}a
+ jexec one sysctl net.inet.carp.preempt=1
+ jexec one ifconfig ${epair}a 192.0.2.1/24 up
+ jexec one ifconfig ${epair}a add vhid 1 192.0.2.254/24 \
+ advskew 0 pass foobar
+
+ vnet_mkjail two ${epair}b
+ jexec two sysctl net.inet.carp.preempt=1
+ jexec two ifconfig ${epair}b 192.0.2.2/24 up
+ jexec two ifconfig ${epair}b add vhid 1 192.0.2.254/24 \
+ advskew 100 pass foobar
+
+ # Allow things to settle
+ wait_for_carp one ${epair}a two ${epair}b
+
+ if is_master one ${epair}a && is_master two ${epair}b
+ then
+ atf_fail "Two masters!"
+ fi
+
+ jexec one sysctl net.inet.carp.demotion=-1
+ sleep 3
+
+ if is_master one ${epair}a && is_master two ${epair}b
+ then
+ atf_fail "Two masters!"
+ fi
+}
+
+negative_demotion_cleanup()
+{
+ vnet_cleanup
+}
+
+
+
+atf_test_case "nd6_ns_source_mac" "cleanup"
+nd6_ns_source_mac_head()
+{
+ atf_set descr 'CARP ndp neighbor solicitation MAC source test (IPv6)'
+ atf_set require.user root
+}
+
+nd6_ns_source_mac_body()
+{
+ carp_init
+ vnet_init_bridge
+
+ bridge=$(vnet_mkbridge)
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+
+ vnet_mkjail carp_ndp_v6_bridge ${bridge} ${epair_one}a ${epair_two}a
+ vnet_mkjail carp_ndp_v6_master ${epair_one}b
+ vnet_mkjail carp_ndp_v6_slave ${epair_two}b
+
+ jexec carp_ndp_v6_bridge ifconfig ${bridge} inet6 2001:db8::0:4/64 up \
+ no_dad
+ jexec carp_ndp_v6_bridge ifconfig ${bridge} addm ${epair_one}a \
+ addm ${epair_two}a
+ jexec carp_ndp_v6_bridge ifconfig ${epair_one}a up
+ jexec carp_ndp_v6_bridge ifconfig ${epair_two}a up
+
+ jexec carp_ndp_v6_master ifconfig ${epair_one}b inet6 \
+ 2001:db8::1:2/64 up no_dad
+ jexec carp_ndp_v6_master ifconfig ${epair_one}b inet6 add vhid 1 \
+ advskew 0 2001:db8::0:1/64
+
+ jexec carp_ndp_v6_slave ifconfig ${epair_two}b inet6 \
+ 2001:db8::1:3/64 up no_dad
+ jexec carp_ndp_v6_slave ifconfig ${epair_two}b inet6 add vhid 1 \
+ advskew 100 2001:db8::0:1/64
+
+ wait_for_carp carp_ndp_v6_master ${epair_one}b \
+ carp_ndp_v6_slave ${epair_two}b
+
+ # carp_ndp_v6_master is MASTER
+
+ # trigger a NS from the virtual IP from the BACKUP
+ atf_check -s exit:2 -o ignore jexec carp_ndp_v6_slave \
+ ping -6 -c 3 -S 2001:db8::0:1 2001:db8::0:4
+
+ # trigger a NS from the virtual IP from the MASTER,
+ # this ping should work
+ atf_check -s exit:0 -o ignore jexec carp_ndp_v6_master \
+ ping -6 -c 3 -S 2001:db8::0:1 2001:db8::0:4
+
+ # ndp entry should be for the virtual mac
+ atf_check -o match:'2001:db8::1 +00:00:5e:00:01:01' \
+ jexec carp_ndp_v6_bridge ndp -an
+}
+
+nd6_ns_source_mac_cleanup()
+{
+ vnet_cleanup
+}
+
+
+atf_test_case "switch" "cleanup"
+switch_head()
+{
+ atf_set descr 'Switch between master and backup'
+ atf_set require.user root
+}
+
+switch_body()
+{
+ carp_init
+
+ epair=$(vnet_mkepair)
+
+ ifconfig ${epair}a up
+ ifconfig ${epair}a vhid 1 advskew 100 192.0.2.1/24
+ ifconfig ${epair}a vhid 1 state backup
+ ifconfig ${epair}a vhid 1 state master
+}
+
+switch_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "basic_v4"
+ atf_add_test_case "vrrp_v4"
+ atf_add_test_case "unicast_v4"
+ atf_add_test_case "basic_v6"
+ atf_add_test_case "vrrp_v6"
+ atf_add_test_case "unicast_v6"
+ atf_add_test_case "unicast_ll_v6"
+ atf_add_test_case "negative_demotion"
+ atf_add_test_case "nd6_ns_source_mac"
+ atf_add_test_case "switch"
+}
diff --git a/tests/sys/netinet/divert.sh b/tests/sys/netinet/divert.sh
new file mode 100755
index 000000000000..d50620d94a09
--- /dev/null
+++ b/tests/sys/netinet/divert.sh
@@ -0,0 +1,158 @@
+#!/usr/bin/env atf-sh
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Alexander V. Chernikov
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+load_divert_module() {
+ kldstat -q -m ipdivert
+ if [ $? -ne 0 ]; then
+ atf_skip "ipdivert module is not loaded"
+ fi
+}
+
+atf_test_case "ipdivert_ip_output_remote_success" "cleanup"
+ipdivert_ip_output_remote_success_head() {
+
+ atf_set descr 'Test diverting IPv4 packet to remote destination'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+ipdivert_ip_output_remote_success_body() {
+
+ if [ "$(atf_config_get ci false)" = "true" ] && \
+ [ "$(uname -p)" = "i386" ]; then
+ atf_skip "https://bugs.freebsd.org/245764"
+ fi
+
+ ids=65530
+ id=`printf "%x" ${ids}`
+ if [ $$ -gt 65535 ]; then
+ xl=`printf "%x" $(($$ - 65535))`
+ yl="1"
+ else
+ xl=`printf "%x" $$`
+ yl=""
+ fi
+
+ vnet_init
+ load_divert_module
+
+ ip4a="192.0.2.5"
+ ip4b="192.0.2.6"
+
+ script_name="../common/divert.py"
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a up
+ ifconfig ${epair}a inet ${ip4a}/30
+
+ jname="v4t-${id}-${yl}-${xl}"
+ vnet_mkjail ${jname} ${epair}b
+ jexec ${jname} ifconfig ${epair}b up
+ jexec ${jname} ifconfig ${epair}b inet ${ip4b}/30
+
+ atf_check -s exit:0 $(atf_get_srcdir)/${script_name} \
+ --dip ${ip4b} --test_name ipdivert_ip_output_remote_success
+
+ count=`jexec ${jname} netstat -s -p icmp | grep 'Input histogram:' -A8 | grep -c 'echo: '`
+ # Verify redirect got installed
+ atf_check_equal "1" "${count}"
+}
+
+ipdivert_ip_output_remote_success_cleanup() {
+
+ vnet_cleanup
+}
+
+atf_test_case "ipdivert_ip_input_local_success" "cleanup"
+ipdivert_ip_input_local_success_head() {
+
+ atf_set descr 'Test diverting IPv4 packet to remote destination'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+ipdivert_ip_input_local_success_body() {
+
+ if [ "$(atf_config_get ci false)" = "true" ] && \
+ [ "$(uname -p)" = "i386" ]; then
+ atf_skip "https://bugs.freebsd.org/245764"
+ fi
+
+ ids=65529
+ id=`printf "%x" ${ids}`
+ if [ $$ -gt 65535 ]; then
+ xl=`printf "%x" $(($$ - 65535))`
+ yl="1"
+ else
+ xl=`printf "%x" $$`
+ yl=""
+ fi
+
+ vnet_init
+ load_divert_module
+
+ ip4a="192.0.2.5"
+ ip4b="192.0.2.6"
+
+ script_name="../common/divert.py"
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a up
+ ifconfig ${epair}a inet ${ip4a}/30
+
+ jname="v4t-${id}-${yl}-${xl}"
+ vnet_mkjail ${jname} ${epair}b
+ jexec ${jname} ifconfig ${epair}b up
+ jexec ${jname} ifconfig ${epair}b inet ${ip4b}/30
+
+ atf_check -s exit:0 jexec ${jname} $(atf_get_srcdir)/${script_name} \
+ --sip ${ip4a} --dip ${ip4b} \
+ --test_name ipdivert_ip_input_local_success
+
+ count=`jexec ${jname} netstat -s -p icmp | grep 'Input histogram:' -A8 | grep -c 'echo: '`
+ # Verify redirect got installed
+ atf_check_equal "1" "${count}"
+}
+
+ipdivert_ip_input_local_success_cleanup() {
+
+ vnet_cleanup
+}
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case "ipdivert_ip_output_remote_success"
+ atf_add_test_case "ipdivert_ip_input_local_success"
+}
+
+# end
+
diff --git a/tests/sys/netinet/fibs.sh b/tests/sys/netinet/fibs.sh
new file mode 100755
index 000000000000..bd514b6f8a17
--- /dev/null
+++ b/tests/sys/netinet/fibs.sh
@@ -0,0 +1,72 @@
+#!/usr/bin/env atf-sh
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Alexander V. Chernikov
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+atf_test_case "fibs_ifroutes1_success" "cleanup"
+fibs_ifroutes1_success_head()
+{
+
+ atf_set descr 'Test IPv4 routes gets populated in the correct fib'
+ atf_set require.user root
+}
+
+fibs_ifroutes1_success_body()
+{
+
+ vnet_init
+
+ net_dst="192.168.0."
+ jname="v6t-fibs_ifroutes1_success"
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail ${jname}a ${epair}a
+
+ jexec ${jname}a sysctl net.fibs=2
+
+ jexec ${jname}a ifconfig ${epair}a fib 1
+ jexec ${jname}a ifconfig ${epair}a inet ${net_dst}1/24
+ jexec ${jname}a ifconfig ${epair}a up
+
+ atf_check -s exit:0 -o ignore jexec ${jname}a setfib 1 route -4n get ${net_dst}0/24
+ atf_check -o match:"interface: lo0" jexec ${jname}a setfib 1 route -4n get ${net_dst}1
+ atf_check -o match:"destination: ${net_dst}1" jexec ${jname}a setfib 1 route -4n get ${net_dst}1
+}
+
+fibs_ifroutes1_success_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "fibs_ifroutes1_success"
+}
+
+
diff --git a/tests/sys/netinet/fibs_multibind_test.c b/tests/sys/netinet/fibs_multibind_test.c
new file mode 100644
index 000000000000..61ebf83c56ef
--- /dev/null
+++ b/tests/sys/netinet/fibs_multibind_test.c
@@ -0,0 +1,755 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024-2025 Stormshield
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+#define MAKETEST_TCP(name) \
+ATF_TC_WITHOUT_HEAD(name ## _tcp); \
+ATF_TC_BODY(name ## _tcp, tc) \
+{ \
+ name(PF_INET, SOCK_STREAM, tc); \
+} \
+ATF_TC_WITHOUT_HEAD(name ## _tcp6); \
+ATF_TC_BODY(name ## _tcp6, tc) \
+{ \
+ name(PF_INET6, SOCK_STREAM, tc); \
+}
+#define MAKETEST_UDP(name) \
+ATF_TC_WITHOUT_HEAD(name ## _udp); \
+ATF_TC_BODY(name ## _udp, tc) \
+{ \
+ name(PF_INET, SOCK_DGRAM, tc); \
+} \
+ATF_TC_WITHOUT_HEAD(name ## _udp6); \
+ATF_TC_BODY(name ## _udp6, tc) \
+{ \
+ name(PF_INET6, SOCK_DGRAM, tc); \
+}
+#define MAKETEST_RAW(name) \
+ATF_TC(name ## _raw); \
+ATF_TC_HEAD(name ## _raw, tc) \
+{ \
+ atf_tc_set_md_var(tc, "require.user", \
+ "root"); \
+} \
+ATF_TC_BODY(name ## _raw, tc) \
+{ \
+ name(PF_INET, SOCK_RAW, tc); \
+} \
+ATF_TC(name ## _raw6); \
+ATF_TC_HEAD(name ## _raw6, tc) \
+{ \
+ atf_tc_set_md_var(tc, "require.user", \
+ "root"); \
+} \
+ATF_TC_BODY(name ## _raw6, tc) \
+{ \
+ name(PF_INET6, SOCK_RAW, tc); \
+}
+
+#define MAKETEST(name) \
+ MAKETEST_TCP(name) \
+ MAKETEST_UDP(name)
+
+#define LISTTEST_TCP(name) \
+ ATF_TP_ADD_TC(tp, name ## _tcp); \
+ ATF_TP_ADD_TC(tp, name ## _tcp6);
+#define LISTTEST_UDP(name) \
+ ATF_TP_ADD_TC(tp, name ## _udp); \
+ ATF_TP_ADD_TC(tp, name ## _udp6);
+#define LISTTEST_RAW(name) \
+ ATF_TP_ADD_TC(tp, name ## _raw); \
+ ATF_TP_ADD_TC(tp, name ## _raw6);
+#define LISTTEST(name) \
+ LISTTEST_TCP(name) \
+ LISTTEST_UDP(name)
+
+static void
+checked_close(int s)
+{
+ int error;
+
+ error = close(s);
+ ATF_REQUIRE_MSG(error == 0, "close failed: %s", strerror(errno));
+}
+
+static int
+mksockp(int domain, int type, int fib, int proto)
+{
+ int error, s;
+
+ s = socket(domain, type, proto);
+ ATF_REQUIRE(s != -1);
+ error = setsockopt(s, SOL_SOCKET, SO_SETFIB, &fib, sizeof(fib));
+ ATF_REQUIRE_MSG(error == 0, "setsockopt failed: %s", strerror(errno));
+
+ return (s);
+}
+
+static int
+mksock(int domain, int type, int fib)
+{
+ return (mksockp(domain, type, fib, 0));
+}
+
+static void
+require_fibs_multibind(int socktype, int minfibs)
+{
+ const char *sysctl;
+ size_t sz;
+ int error, fibs, multibind;
+
+ fibs = 0;
+ sz = sizeof(fibs);
+ error = sysctlbyname("net.fibs", &fibs, &sz, NULL, 0);
+ ATF_REQUIRE_MSG(error == 0, "sysctlbyname failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(fibs >= 1, "strange FIB count %d", fibs);
+ if (fibs == 1)
+ atf_tc_skip("multiple FIBs not enabled");
+ if (fibs < minfibs)
+ atf_tc_skip("not enough FIBs, need %d", minfibs);
+
+ switch (socktype) {
+ case SOCK_STREAM:
+ sysctl = "net.inet.tcp.bind_all_fibs";
+ break;
+ case SOCK_DGRAM:
+ sysctl = "net.inet.udp.bind_all_fibs";
+ break;
+ case SOCK_RAW:
+ sysctl = "net.inet.raw.bind_all_fibs";
+ break;
+ default:
+ atf_tc_fail("unknown socket type %d", socktype);
+ break;
+ }
+
+ multibind = -1;
+ sz = sizeof(multibind);
+ error = sysctlbyname(sysctl, &multibind, &sz, NULL, 0);
+ ATF_REQUIRE_MSG(error == 0, "sysctlbyname failed: %s", strerror(errno));
+ if (multibind != 0)
+ atf_tc_skip("FIB multibind not configured (%s)", sysctl);
+}
+
+/*
+ * Make sure that different users can't bind to the same port from different
+ * FIBs.
+ */
+static void
+multibind_different_user(int domain, int type, const atf_tc_t *tc)
+{
+ struct sockaddr_storage ss;
+ struct passwd *passwd;
+ const char *user;
+ socklen_t sslen;
+ int error, s[2];
+
+ if (geteuid() != 0)
+ atf_tc_skip("need root privileges");
+ if (!atf_tc_has_config_var(tc, "unprivileged_user"))
+ atf_tc_skip("unprivileged_user not set");
+
+ ATF_REQUIRE(domain == PF_INET || domain == PF_INET6);
+ sslen = domain == PF_INET ? sizeof(struct sockaddr_in) :
+ sizeof(struct sockaddr_in6);
+
+ require_fibs_multibind(type, 2);
+
+ s[0] = mksock(domain, type, 0);
+
+ memset(&ss, 0, sizeof(ss));
+ ss.ss_family = domain;
+ ss.ss_len = sslen;
+ error = bind(s[0], (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+
+ error = getsockname(s[0], (struct sockaddr *)&ss, &sslen);
+ ATF_REQUIRE_MSG(error == 0, "getsockname failed: %s", strerror(errno));
+
+ /*
+ * Create a second socket in a different FIB, and bind it to the same
+ * address/port tuple. This should succeed if done as the same user as
+ * the first socket, and should fail otherwise.
+ */
+ s[1] = mksock(domain, type, 1);
+ error = bind(s[1], (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(close(s[1]) == 0, "close failed: %s", strerror(errno));
+
+ user = atf_tc_get_config_var(tc, "unprivileged_user");
+ passwd = getpwnam(user);
+ ATF_REQUIRE(passwd != NULL);
+ error = seteuid(passwd->pw_uid);
+ ATF_REQUIRE_MSG(error == 0, "seteuid failed: %s", strerror(errno));
+
+ /* Repeat the bind as a different user. */
+ s[1] = mksock(domain, type, 1);
+ error = bind(s[1], (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_ERRNO(EADDRINUSE, error == -1);
+ ATF_REQUIRE_MSG(close(s[1]) == 0, "close failed: %s", strerror(errno));
+}
+MAKETEST(multibind_different_user);
+
+/*
+ * Verify that a listening socket only accepts connections originating from the
+ * same FIB.
+ */
+static void
+per_fib_listening_socket(int domain, int type, const atf_tc_t *tc __unused)
+{
+ struct sockaddr_storage ss;
+ socklen_t sslen;
+ int cs1, cs2, error, fib1, fib2, ls1, ls2, ns;
+
+ ATF_REQUIRE(type == SOCK_STREAM);
+ ATF_REQUIRE(domain == PF_INET || domain == PF_INET6);
+ require_fibs_multibind(type, 2);
+
+ fib1 = 0;
+ fib2 = 1;
+
+ ls1 = mksock(domain, type, fib1);
+ ls2 = mksock(domain, type, fib2);
+
+ sslen = domain == PF_INET ? sizeof(struct sockaddr_in) :
+ sizeof(struct sockaddr_in6);
+
+ memset(&ss, 0, sizeof(ss));
+ ss.ss_family = domain;
+ ss.ss_len = sslen;
+ error = bind(ls1, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+
+ error = getsockname(ls1, (struct sockaddr *)&ss, &sslen);
+ ATF_REQUIRE_MSG(error == 0, "getsockname failed: %s", strerror(errno));
+
+ error = listen(ls1, 5);
+ ATF_REQUIRE_MSG(error == 0, "listen failed: %s", strerror(errno));
+
+ cs1 = mksock(domain, type, fib1);
+ cs2 = mksock(domain, type, fib2);
+
+ /*
+ * Make sure we can connect from the same FIB.
+ */
+ error = connect(cs1, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "connect failed: %s", strerror(errno));
+ ns = accept(ls1, NULL, NULL);
+ ATF_REQUIRE_MSG(ns != -1, "accept failed: %s", strerror(errno));
+ checked_close(ns);
+ checked_close(cs1);
+ cs1 = mksock(domain, type, fib1);
+
+ /*
+ * ... but not from a different FIB.
+ */
+ error = connect(cs2, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == -1, "connect succeeded unexpectedly");
+ ATF_REQUIRE_MSG(errno == ECONNREFUSED, "unexpected error %d", errno);
+ checked_close(cs2);
+ cs2 = mksock(domain, type, fib2);
+
+ /*
+ * ... but if there are multiple listening sockets, we always connect to
+ * the same FIB.
+ */
+ error = bind(ls2, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+ error = listen(ls2, 5);
+ ATF_REQUIRE_MSG(error == 0, "listen failed: %s", strerror(errno));
+
+ for (int i = 0; i < 10; i++) {
+ error = connect(cs1, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "connect failed: %s",
+ strerror(errno));
+ ns = accept(ls1, NULL, NULL);
+ ATF_REQUIRE_MSG(ns != -1, "accept failed: %s", strerror(errno));
+
+ checked_close(ns);
+ checked_close(cs1);
+ cs1 = mksock(domain, type, fib1);
+ }
+ for (int i = 0; i < 10; i++) {
+ error = connect(cs2, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "connect failed: %s",
+ strerror(errno));
+ ns = accept(ls2, NULL, NULL);
+ ATF_REQUIRE_MSG(ns != -1, "accept failed: %s", strerror(errno));
+
+ checked_close(ns);
+ checked_close(cs2);
+ cs2 = mksock(domain, type, fib2);
+ }
+
+ /*
+ * ... and if we close one of the listening sockets, we're back to only
+ * being able to connect from the same FIB.
+ */
+ checked_close(ls1);
+ error = connect(cs1, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == -1, "connect succeeded unexpectedly");
+ ATF_REQUIRE_MSG(errno == ECONNREFUSED, "unexpected error %d", errno);
+ checked_close(cs1);
+
+ error = connect(cs2, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "connect failed: %s", strerror(errno));
+ ns = accept(ls2, NULL, NULL);
+ ATF_REQUIRE_MSG(ns != -1, "accept failed: %s", strerror(errno));
+ checked_close(ns);
+ checked_close(cs2);
+ checked_close(ls2);
+}
+MAKETEST_TCP(per_fib_listening_socket);
+
+/*
+ * Verify that a bound datagram socket only accepts data from the same FIB.
+ */
+static void
+per_fib_dgram_socket(int domain, int type, const atf_tc_t *tc __unused)
+{
+ struct sockaddr_storage ss;
+ struct sockaddr_in6 *sin6p;
+ socklen_t sslen;
+ ssize_t n;
+ int error, cs1, cs2, fib1, fib2, ss1, ss2;
+ char b;
+
+ ATF_REQUIRE(type == SOCK_DGRAM);
+ ATF_REQUIRE(domain == PF_INET || domain == PF_INET6);
+ require_fibs_multibind(type, 2);
+
+ fib1 = 0;
+ fib2 = 1;
+
+ cs1 = mksock(domain, type, fib1);
+ cs2 = mksock(domain, type, fib2);
+
+ ss1 = mksock(domain, type, fib1);
+ ss2 = mksock(domain, type, fib2);
+
+ sslen = domain == PF_INET ? sizeof(struct sockaddr_in) :
+ sizeof(struct sockaddr_in6);
+
+ memset(&ss, 0, sizeof(ss));
+ ss.ss_family = domain;
+ ss.ss_len = sslen;
+ error = bind(ss1, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+
+ error = getsockname(ss1, (struct sockaddr *)&ss, &sslen);
+ ATF_REQUIRE_MSG(error == 0, "getsockname failed: %s", strerror(errno));
+
+ if (domain == PF_INET6) {
+ sin6p = (struct sockaddr_in6 *)&ss;
+ sin6p->sin6_addr = in6addr_loopback;
+ }
+
+ /* If we send a byte from cs1, it should be recieved by ss1. */
+ b = 42;
+ n = sendto(cs1, &b, sizeof(b), 0, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(n == 1, "sendto failed: %s", strerror(errno));
+ n = recv(ss1, &b, sizeof(b), 0);
+ ATF_REQUIRE(n == 1);
+ ATF_REQUIRE(b == 42);
+
+ /* If we send a byte from cs2, it should not be received by ss1. */
+ b = 42;
+ n = sendto(cs2, &b, sizeof(b), 0, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(n == 1, "sendto failed: %s", strerror(errno));
+ usleep(10000);
+ n = recv(ss1, &b, sizeof(b), MSG_DONTWAIT);
+ ATF_REQUIRE_ERRNO(EWOULDBLOCK, n == -1);
+
+ error = bind(ss2, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+
+ /* Repeat now that ss2 is bound. */
+ b = 42;
+ n = sendto(cs1, &b, sizeof(b), 0, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(n == 1, "sendto failed: %s", strerror(errno));
+ n = recv(ss1, &b, sizeof(b), 0);
+ ATF_REQUIRE(n == 1);
+ ATF_REQUIRE(b == 42);
+
+ b = 42;
+ n = sendto(cs2, &b, sizeof(b), 0, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(n == 1, "sendto failed: %s", strerror(errno));
+ n = recv(ss2, &b, sizeof(b), 0);
+ ATF_REQUIRE(n == 1);
+ ATF_REQUIRE(b == 42);
+
+ checked_close(ss1);
+ checked_close(ss2);
+ checked_close(cs1);
+ checked_close(cs2);
+}
+MAKETEST_UDP(per_fib_dgram_socket);
+
+static size_t
+ping(int s, const struct sockaddr *sa, socklen_t salen)
+{
+ struct {
+ struct icmphdr icmp;
+ char data[64];
+ } icmp;
+ ssize_t n;
+
+ memset(&icmp, 0, sizeof(icmp));
+ icmp.icmp.icmp_type = ICMP_ECHO;
+ icmp.icmp.icmp_code = 0;
+ icmp.icmp.icmp_cksum = htons((unsigned short)~(ICMP_ECHO << 8));
+ n = sendto(s, &icmp, sizeof(icmp), 0, sa, salen);
+ ATF_REQUIRE_MSG(n == (ssize_t)sizeof(icmp), "sendto failed: %s",
+ strerror(errno));
+
+ return (sizeof(icmp) + sizeof(struct ip));
+}
+
+static size_t
+ping6(int s, const struct sockaddr *sa, socklen_t salen)
+{
+ struct {
+ struct icmp6_hdr icmp6;
+ char data[64];
+ } icmp6;
+ ssize_t n;
+
+ memset(&icmp6, 0, sizeof(icmp6));
+ icmp6.icmp6.icmp6_type = ICMP6_ECHO_REQUEST;
+ icmp6.icmp6.icmp6_code = 0;
+ icmp6.icmp6.icmp6_cksum =
+ htons((unsigned short)~(ICMP6_ECHO_REQUEST << 8));
+ n = sendto(s, &icmp6, sizeof(icmp6), 0, sa, salen);
+ ATF_REQUIRE_MSG(n == (ssize_t)sizeof(icmp6), "sendto failed: %s",
+ strerror(errno));
+
+ return (sizeof(icmp6));
+}
+
+static void
+per_fib_raw_socket(int domain, int type, const atf_tc_t *tc __unused)
+{
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ ssize_t n;
+ size_t sz;
+ int error, cs, s[2], proto;
+ uint8_t b[256];
+
+ ATF_REQUIRE(type == SOCK_RAW);
+ ATF_REQUIRE(domain == PF_INET || domain == PF_INET6);
+ require_fibs_multibind(type, 2);
+
+ proto = domain == PF_INET ? IPPROTO_ICMP : IPPROTO_ICMPV6;
+ s[0] = mksockp(domain, type, 0, proto);
+ s[1] = mksockp(domain, type, 1, proto);
+
+ if (domain == PF_INET) {
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = domain;
+ sin.sin_len = sizeof(sin);
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ error = bind(s[0], (struct sockaddr *)&sin, sizeof(sin));
+ } else /* if (domain == PF_INET6) */ {
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = domain;
+ sin6.sin6_len = sizeof(sin6);
+ sin6.sin6_addr = in6addr_loopback;
+ error = bind(s[0], (struct sockaddr *)&sin6, sizeof(sin6));
+ }
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+
+ for (int i = 0; i < 2; i++) {
+ cs = mksockp(domain, type, i, proto);
+ if (domain == PF_INET) {
+ sz = ping(cs, (struct sockaddr *)&sin, sizeof(sin));
+ } else /* if (domain == PF_INET6) */ {
+ sz = ping6(cs, (struct sockaddr *)&sin6, sizeof(sin6));
+ }
+ n = recv(s[i], b, sizeof(b), 0);
+ ATF_REQUIRE_MSG(n > 0, "recv failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(n == (ssize_t)sz,
+ "short packet received: %zd", n);
+
+ if (domain == PF_INET6) {
+ /* Get the echo reply as well. */
+ n = recv(s[i], b, sizeof(b), 0);
+ ATF_REQUIRE_MSG(n > 0,
+ "recv failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(n == (ssize_t)sz,
+ "short packet received: %zd", n);
+ }
+
+ /* Make sure that the other socket didn't receive anything. */
+ n = recv(s[1 - i], b, sizeof(b), MSG_DONTWAIT);
+ printf("n = %zd i = %d\n", n, i);
+ ATF_REQUIRE_ERRNO(EWOULDBLOCK, n == -1);
+
+ checked_close(cs);
+ }
+
+ checked_close(s[0]);
+ checked_close(s[1]);
+}
+MAKETEST_RAW(per_fib_raw_socket);
+
+/*
+ * Create a pair of load-balancing listening socket groups, one in each FIB, and
+ * make sure that connections to the group are only load-balanced within the
+ * same FIB.
+ */
+static void
+multibind_lbgroup_stream(int domain, int type, const atf_tc_t *tc __unused)
+{
+ struct sockaddr_storage ss;
+ socklen_t sslen;
+ int error, as, cs, s[3];
+
+ ATF_REQUIRE(type == SOCK_STREAM);
+ ATF_REQUIRE(domain == PF_INET || domain == PF_INET6);
+ require_fibs_multibind(type, 2);
+
+ s[0] = mksock(domain, type, 0);
+ ATF_REQUIRE(setsockopt(s[0], SOL_SOCKET, SO_REUSEPORT_LB, &(int){1},
+ sizeof(int)) == 0);
+ ATF_REQUIRE(fcntl(s[0], F_SETFL, O_NONBLOCK) == 0);
+ s[1] = mksock(domain, type, 0);
+ ATF_REQUIRE(setsockopt(s[1], SOL_SOCKET, SO_REUSEPORT_LB, &(int){1},
+ sizeof(int)) == 0);
+ ATF_REQUIRE(fcntl(s[1], F_SETFL, O_NONBLOCK) == 0);
+ s[2] = mksock(domain, type, 1);
+ ATF_REQUIRE(setsockopt(s[2], SOL_SOCKET, SO_REUSEPORT_LB, &(int){1},
+ sizeof(int)) == 0);
+
+ sslen = domain == PF_INET ? sizeof(struct sockaddr_in) :
+ sizeof(struct sockaddr_in6);
+ memset(&ss, 0, sizeof(ss));
+ ss.ss_family = domain;
+ ss.ss_len = sslen;
+ error = bind(s[0], (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+ error = listen(s[0], 5);
+ ATF_REQUIRE_MSG(error == 0, "listen failed: %s", strerror(errno));
+ error = getsockname(s[0], (struct sockaddr *)&ss, &sslen);
+ ATF_REQUIRE_MSG(error == 0, "getsockname failed: %s", strerror(errno));
+
+ error = bind(s[1], (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+ error = listen(s[1], 5);
+ ATF_REQUIRE_MSG(error == 0, "listen failed: %s", strerror(errno));
+
+ error = bind(s[2], (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+ error = listen(s[2], 5);
+ ATF_REQUIRE_MSG(error == 0, "listen failed: %s", strerror(errno));
+
+ /*
+ * Initiate connections from FIB 0, make sure they go to s[0] or s[1].
+ */
+ for (int count = 0; count < 100; count++) {
+ cs = mksock(domain, type, 0);
+ error = connect(cs, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "connect failed: %s",
+ strerror(errno));
+
+ do {
+ as = accept(s[0], NULL, NULL);
+ if (as == -1) {
+ ATF_REQUIRE_MSG(errno == EWOULDBLOCK,
+ "accept failed: %s", strerror(errno));
+ as = accept(s[1], NULL, NULL);
+ if (as == -1) {
+ ATF_REQUIRE_MSG(errno == EWOULDBLOCK,
+ "accept failed: %s",
+ strerror(errno));
+ }
+ }
+ } while (as == -1);
+ checked_close(as);
+ checked_close(cs);
+ }
+
+ /*
+ * Initiate connections from FIB 1, make sure they go to s[2].
+ */
+ for (int count = 0; count < 100; count++) {
+ cs = mksock(domain, type, 1);
+ error = connect(cs, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "connect failed: %s",
+ strerror(errno));
+
+ as = accept(s[2], NULL, NULL);
+ ATF_REQUIRE_MSG(as != -1, "accept failed: %s", strerror(errno));
+ checked_close(as);
+ checked_close(cs);
+ }
+
+ checked_close(s[0]);
+ checked_close(s[1]);
+ checked_close(s[2]);
+}
+MAKETEST_TCP(multibind_lbgroup_stream);
+
+static void
+multibind_lbgroup_dgram(int domain, int type, const atf_tc_t *tc __unused)
+{
+ struct sockaddr_storage ss;
+ struct sockaddr_in6 *sin6p;
+ socklen_t sslen;
+ ssize_t n;
+ int error, cs, s[3];
+ char b;
+
+ ATF_REQUIRE(type == SOCK_DGRAM);
+ ATF_REQUIRE(domain == PF_INET || domain == PF_INET6);
+ require_fibs_multibind(type, 2);
+
+ s[0] = mksock(domain, type, 0);
+ ATF_REQUIRE(setsockopt(s[0], SOL_SOCKET, SO_REUSEPORT_LB, &(int){1},
+ sizeof(int)) == 0);
+ s[1] = mksock(domain, type, 0);
+ ATF_REQUIRE(setsockopt(s[1], SOL_SOCKET, SO_REUSEPORT_LB, &(int){1},
+ sizeof(int)) == 0);
+ s[2] = mksock(domain, type, 1);
+ ATF_REQUIRE(setsockopt(s[2], SOL_SOCKET, SO_REUSEPORT_LB, &(int){1},
+ sizeof(int)) == 0);
+
+ sslen = domain == PF_INET ? sizeof(struct sockaddr_in) :
+ sizeof(struct sockaddr_in6);
+ memset(&ss, 0, sizeof(ss));
+ ss.ss_family = domain;
+ ss.ss_len = sslen;
+ error = bind(s[0], (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+ error = getsockname(s[0], (struct sockaddr *)&ss, &sslen);
+ ATF_REQUIRE_MSG(error == 0, "getsockname failed: %s", strerror(errno));
+
+ error = bind(s[1], (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+ error = bind(s[2], (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+
+ if (domain == PF_INET6) {
+ sin6p = (struct sockaddr_in6 *)&ss;
+ sin6p->sin6_addr = in6addr_loopback;
+ }
+
+ /*
+ * Send a packet from FIB 0, make sure it goes to s[0] or s[1].
+ */
+ cs = mksock(domain, type, 0);
+ for (int count = 0; count < 100; count++) {
+ int bytes, rs;
+
+ b = 42;
+ n = sendto(cs, &b, sizeof(b), 0, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(n == 1, "sendto failed: %s", strerror(errno));
+ usleep(1000);
+
+ error = ioctl(s[0], FIONREAD, &bytes);
+ ATF_REQUIRE_MSG(error == 0, "ioctl failed: %s",
+ strerror(errno));
+ if (bytes == 0) {
+ error = ioctl(s[1], FIONREAD, &bytes);
+ ATF_REQUIRE_MSG(error == 0, "ioctl failed: %s",
+ strerror(errno));
+ rs = s[1];
+ } else {
+ rs = s[0];
+ }
+ n = recv(rs, &b, sizeof(b), 0);
+ ATF_REQUIRE(n == 1);
+ ATF_REQUIRE(b == 42);
+ ATF_REQUIRE(bytes == 1);
+ }
+ checked_close(cs);
+
+ /*
+ * Send a packet from FIB 1, make sure it goes to s[2].
+ */
+ cs = mksock(domain, type, 1);
+ for (int count = 0; count < 100; count++) {
+ b = 42;
+ n = sendto(cs, &b, sizeof(b), 0, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(n == 1, "sendto failed: %s", strerror(errno));
+ usleep(1000);
+
+ n = recv(s[2], &b, sizeof(b), 0);
+ ATF_REQUIRE(n == 1);
+ ATF_REQUIRE(b == 42);
+ }
+ checked_close(cs);
+
+ checked_close(s[0]);
+ checked_close(s[1]);
+ checked_close(s[2]);
+}
+MAKETEST_UDP(multibind_lbgroup_dgram);
+
+/*
+ * Make sure that we can't change the FIB of a bound socket.
+ */
+static void
+no_setfib_after_bind(int domain, int type, const atf_tc_t *tc __unused)
+{
+ struct sockaddr_storage ss;
+ socklen_t sslen;
+ int error, s;
+
+ ATF_REQUIRE(domain == PF_INET || domain == PF_INET6);
+ require_fibs_multibind(type, 2);
+
+ s = mksock(domain, type, 0);
+
+ sslen = domain == PF_INET ? sizeof(struct sockaddr_in) :
+ sizeof(struct sockaddr_in6);
+ memset(&ss, 0, sizeof(ss));
+ ss.ss_family = domain;
+ ss.ss_len = sslen;
+ error = bind(s, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+
+ error = setsockopt(s, SOL_SOCKET, SO_SETFIB, &(int){1}, sizeof(int));
+ ATF_REQUIRE_ERRNO(EISCONN, error == -1);
+
+ /* It's ok to set the FIB number to its current value. */
+ error = setsockopt(s, SOL_SOCKET, SO_SETFIB, &(int){0}, sizeof(int));
+ ATF_REQUIRE_MSG(error == 0, "setsockopt failed: %s", strerror(errno));
+
+ checked_close(s);
+}
+MAKETEST(no_setfib_after_bind);
+
+ATF_TP_ADD_TCS(tp)
+{
+ LISTTEST(multibind_different_user);
+ LISTTEST_TCP(per_fib_listening_socket);
+ LISTTEST_UDP(per_fib_dgram_socket);
+ LISTTEST_RAW(per_fib_raw_socket);
+ LISTTEST_TCP(multibind_lbgroup_stream);
+ LISTTEST_UDP(multibind_lbgroup_dgram);
+ LISTTEST(no_setfib_after_bind);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/netinet/fibs_test.sh b/tests/sys/netinet/fibs_test.sh
new file mode 100644
index 000000000000..2d0b63f8e30a
--- /dev/null
+++ b/tests/sys/netinet/fibs_test.sh
@@ -0,0 +1,830 @@
+#
+# Copyright (c) 2014 Spectra Logic Corporation
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions, and the following disclaimer,
+# without modification.
+# 2. Redistributions in binary form must reproduce at minimum a disclaimer
+# substantially similar to the "NO WARRANTY" disclaimer below
+# ("Disclaimer") and any redistribution must be conditioned upon
+# including a substantially similar Disclaimer requirement for further
+# binary redistribution.
+#
+# NO WARRANTY
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGES.
+#
+# Authors: Alan Somers (Spectra Logic Corporation)
+#
+
+# arpresolve should check the interface fib for routes to a target when
+# creating an ARP table entry. This is a regression for kern/167947, where
+# arpresolve only checked the default route.
+#
+# Outline:
+# Create two connected epair(4) interfaces
+# Use nping (from security/nmap) to send an ICMP echo request from one
+# interface to the other, spoofing the source IP. The source IP must be
+# spoofed, or else it will already have an entry in the arp table.
+# Check whether an arp entry exists for the spoofed IP
+atf_test_case arpresolve_checks_interface_fib cleanup
+arpresolve_checks_interface_fib_head()
+{
+ atf_set "descr" "arpresolve should check the interface fib, not the default fib, for routes"
+ atf_set "require.user" "root"
+ atf_set "require.progs" "nping"
+}
+arpresolve_checks_interface_fib_body()
+{
+ # Configure the TAP interfaces to use a RFC5737 nonrouteable addresses
+ # and a non-default fib
+ ADDR0="192.0.2.2"
+ ADDR1="192.0.2.3"
+ SUBNET="192.0.2.0"
+ # Due to bug TBD (regressed by multiple_fibs_on_same_subnet) we need
+ # diffferent subnet masks, or FIB1 won't have a subnet route.
+ MASK0="24"
+ MASK1="25"
+ # Spoof a MAC that is reserved per RFC7042
+ SPOOF_ADDR="192.0.2.4"
+ SPOOF_MAC="00:00:5E:00:53:00"
+
+ # Check system configuration
+ if [ 0 != `sysctl -n net.add_addr_allfibs` ]; then
+ atf_skip "This test requires net.add_addr_allfibs=0"
+ fi
+ get_fibs 2
+
+ # Configure epair interfaces
+ get_epair
+ setup_iface "$EPAIRA" "$FIB0" inet ${ADDR0} ${MASK0}
+ setup_iface "$EPAIRB" "$FIB1" inet ${ADDR1} ${MASK1}
+
+ # Send an ICMP echo request with a spoofed source IP
+ setfib "$FIB0" nping -c 1 -e ${EPAIRA} -S ${SPOOF_ADDR} \
+ --source-mac ${SPOOF_MAC} --icmp --icmp-type "echo-request" \
+ --icmp-code 0 --icmp-id 0xdead --icmp-seq 1 --data 0xbeef \
+ ${ADDR1}
+ # For informational and debugging purposes only, look for the
+ # characteristic error message
+ dmesg | grep "llinfo.*${SPOOF_ADDR}"
+ # Check that the ARP entry exists
+ atf_check -o match:"${SPOOF_ADDR}.*expires" setfib "$FIB1" arp ${SPOOF_ADDR}
+}
+arpresolve_checks_interface_fib_cleanup()
+{
+ cleanup_ifaces
+}
+
+
+# Regression test for kern/187549
+atf_test_case loopback_and_network_routes_on_nondefault_fib cleanup
+loopback_and_network_routes_on_nondefault_fib_head()
+{
+ atf_set "descr" "When creating and deleting loopback IPv4 routes, use the interface's fib"
+ atf_set "require.user" "root"
+}
+
+loopback_and_network_routes_on_nondefault_fib_body()
+{
+ # Configure the TAP interface to use an RFC5737 nonrouteable address
+ # and a non-default fib
+ ADDR="192.0.2.2"
+ SUBNET="192.0.2.0"
+ MASK="24"
+
+ # Check system configuration
+ if [ 0 != `sysctl -n net.add_addr_allfibs` ]; then
+ atf_skip "This test requires net.add_addr_allfibs=0"
+ fi
+ get_fibs 1
+
+ # Configure a TAP interface
+ setup_tap ${FIB0} inet ${ADDR} ${MASK}
+
+ # Check whether the host route exists in only the correct FIB
+ setfib ${FIB0} netstat -rn -f inet | grep -q "^${ADDR}.*UHS.*lo0"
+ if [ 0 -ne $? ]; then
+ setfib ${FIB0} netstat -rn -f inet
+ atf_fail "Host route did not appear in the correct FIB"
+ fi
+ setfib 0 netstat -rn -f inet | grep -q "^${ADDR}.*UHS.*lo0"
+ if [ 0 -eq $? ]; then
+ setfib 0 netstat -rn -f inet
+ atf_fail "Host route appeared in the wrong FIB"
+ fi
+
+ # Check whether the network route exists in only the correct FIB
+ setfib ${FIB0} netstat -rn -f inet | \
+ grep -q "^${SUBNET}/${MASK}.*${TAPD}"
+ if [ 0 -ne $? ]; then
+ setfib ${FIB0} netstat -rn -f inet
+ atf_fail "Network route did not appear in the correct FIB"
+ fi
+ setfib 0 netstat -rn -f inet | \
+ grep -q "^${SUBNET}/${MASK}.*${TAPD}"
+ if [ 0 -eq $? ]; then
+ setfib 0 netstat -rn -f inet
+ atf_fail "Network route appeared in the wrong FIB"
+ fi
+}
+
+loopback_and_network_routes_on_nondefault_fib_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case loopback_and_network_routes_on_nondefault_fib_inet6 cleanup
+loopback_and_network_routes_on_nondefault_fib_inet6_head()
+{
+ atf_set "descr" "When creating and deleting loopback IPv6 routes, use the interface's fib"
+ atf_set "require.user" "root"
+}
+
+loopback_and_network_routes_on_nondefault_fib_inet6_body()
+{
+ # Configure the TAP interface to use a nonrouteable RFC3849
+ # address and a non-default fib
+ ADDR="2001:db8::2"
+ SUBNET="2001:db8::"
+ MASK="64"
+
+ # Check system configuration
+ if [ 0 != `sysctl -n net.add_addr_allfibs` ]; then
+ atf_skip "This test requires net.add_addr_allfibs=0"
+ fi
+ get_fibs 1
+
+ # Configure a TAP interface
+ setup_tap ${FIB0} inet6 ${ADDR} ${MASK}
+
+ # Check whether the host route exists in only the correct FIB
+ setfib ${FIB0} netstat -rn -f inet6 | grep -q "^${ADDR}.*UHS.*lo0"
+ if [ 0 -ne $? ]; then
+ setfib ${FIB0} netstat -rn -f inet6
+ atf_fail "Host route did not appear in the correct FIB"
+ fi
+ setfib 0 netstat -rn -f inet6 | grep -q "^${ADDR}.*UHS.*lo0"
+ if [ 0 -eq $? ]; then
+ setfib 0 netstat -rn -f inet6
+ atf_fail "Host route appeared in the wrong FIB"
+ fi
+
+ # Check whether the network route exists in only the correct FIB
+ setfib ${FIB0} netstat -rn -f inet6 | \
+ grep -q "^${SUBNET}/${MASK}.*${TAPD}"
+ if [ 0 -ne $? ]; then
+ setfib ${FIB0} netstat -rn -f inet6
+ atf_fail "Network route did not appear in the correct FIB"
+ fi
+ setfib 0 netstat -rn -f inet6 | \
+ grep -q "^${SUBNET}/${MASK}.*${TAPD}"
+ if [ 0 -eq $? ]; then
+ setfib 0 netstat -rn -f inet6
+ atf_fail "Network route appeared in the wrong FIB"
+ fi
+}
+
+loopback_and_network_routes_on_nondefault_fib_inet6_cleanup()
+{
+ cleanup_ifaces
+}
+
+
+# Regression test for kern/187552
+atf_test_case default_route_with_multiple_fibs_on_same_subnet cleanup
+default_route_with_multiple_fibs_on_same_subnet_head()
+{
+ atf_set "descr" "Multiple interfaces on the same subnet but with different fibs can both have default IPv4 routes"
+ atf_set "require.user" "root"
+}
+
+default_route_with_multiple_fibs_on_same_subnet_body()
+{
+ # Configure the TAP interfaces to use a RFC5737 nonrouteable addresses
+ # and a non-default fib
+ ADDR0="192.0.2.2"
+ ADDR1="192.0.2.3"
+ GATEWAY="192.0.2.1"
+ SUBNET="192.0.2.0"
+ MASK="24"
+
+ # Check system configuration
+ if [ 0 != `sysctl -n net.add_addr_allfibs` ]; then
+ atf_skip "This test requires net.add_addr_allfibs=0"
+ fi
+ get_fibs 2
+
+ # Configure TAP interfaces
+ setup_tap "$FIB0" inet ${ADDR0} ${MASK}
+ TAP0=$TAP
+ setup_tap "$FIB1" inet ${ADDR1} ${MASK}
+ TAP1=$TAP
+
+ # Attempt to add default routes
+ setfib ${FIB0} route add default ${GATEWAY}
+ setfib ${FIB1} route add default ${GATEWAY}
+
+ # Verify that the default route exists for both fibs, with their
+ # respective interfaces.
+ atf_check -o match:"^default.*${TAP0}$" \
+ setfib ${FIB0} netstat -rn -f inet
+ atf_check -o match:"^default.*${TAP1}$" \
+ setfib ${FIB1} netstat -rn -f inet
+}
+
+default_route_with_multiple_fibs_on_same_subnet_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case default_route_with_multiple_fibs_on_same_subnet_inet6 cleanup
+default_route_with_multiple_fibs_on_same_subnet_inet6_head()
+{
+ atf_set "descr" "Multiple interfaces on the same subnet but with different fibs can both have default IPv6 routes"
+ atf_set "require.user" "root"
+}
+
+default_route_with_multiple_fibs_on_same_subnet_inet6_body()
+{
+ # Configure the TAP interfaces to use nonrouteable RFC3849
+ # addresses and non-default FIBs
+ ADDR0="2001:db8::2"
+ ADDR1="2001:db8::3"
+ GATEWAY="2001:db8::1"
+ SUBNET="2001:db8::"
+ MASK="64"
+
+ # Check system configuration
+ if [ 0 != `sysctl -n net.add_addr_allfibs` ]; then
+ atf_skip "This test requires net.add_addr_allfibs=0"
+ fi
+ get_fibs 2
+
+ # Configure TAP interfaces
+ setup_tap "$FIB0" inet6 ${ADDR0} ${MASK}
+ TAP0=$TAP
+ setup_tap "$FIB1" inet6 ${ADDR1} ${MASK}
+ TAP1=$TAP
+
+ # Attempt to add default routes
+ setfib ${FIB0} route -6 add default ${GATEWAY}
+ setfib ${FIB1} route -6 add default ${GATEWAY}
+
+ # Verify that the default route exists for both fibs, with their
+ # respective interfaces.
+ atf_check -o match:"^default.*${TAP0}$" \
+ setfib ${FIB0} netstat -rn -f inet6
+ atf_check -o match:"^default.*${TAP1}$" \
+ setfib ${FIB1} netstat -rn -f inet6
+}
+
+default_route_with_multiple_fibs_on_same_subnet_inet6_cleanup()
+{
+ cleanup_ifaces
+}
+
+
+# Regression test for PR kern/189089
+# Create two tap interfaces and assign them both the same IP address but with
+# different netmasks, and both on the default FIB. Then remove one's IP
+# address. Hopefully the machine won't panic.
+atf_test_case same_ip_multiple_ifaces_fib0 cleanup
+same_ip_multiple_ifaces_fib0_head()
+{
+ atf_set "descr" "Can remove an IPv4 alias from an interface when the same IPv4 is also assigned to another interface."
+ atf_set "require.user" "root"
+}
+same_ip_multiple_ifaces_fib0_body()
+{
+ ADDR="192.0.2.2"
+ MASK0="24"
+ MASK1="32"
+
+ # Unlike most of the tests in this file, this is applicable regardless
+ # of net.add_addr_allfibs
+
+ # Setup the interfaces, then remove one alias. It should not panic.
+ setup_tap 0 inet ${ADDR} ${MASK0}
+ TAP0=${TAP}
+ # After commit 361a8395f0b0e6f254fd138798232529679d99f6 it became
+ # an error to assign the same interface address twice.
+ atf_expect_fail "The test results in an ifconfig error and thus spuriously fails"
+ setup_tap 0 inet ${ADDR} ${MASK1}
+ TAP1=${TAP}
+ ifconfig ${TAP1} -alias ${ADDR}
+
+ # Do it again, in the opposite order. It should not panic.
+ setup_tap 0 inet ${ADDR} ${MASK0}
+ TAP0=${TAP}
+ setup_tap 0 inet ${ADDR} ${MASK1}
+ TAP1=${TAP}
+ ifconfig ${TAP0} -alias ${ADDR}
+}
+same_ip_multiple_ifaces_fib0_cleanup()
+{
+ cleanup_ifaces
+}
+
+# Regression test for PR kern/189088
+# Test that removing an IP address works even if the same IP is assigned to a
+# different interface, on a different FIB. Tests the same code that whose
+# panic was regressed by same_ip_multiple_ifaces_fib0.
+# Create two tap interfaces and assign them both the same IP address but with
+# different netmasks, and on different FIBs. Then remove one's IP
+# address. Hopefully the machine won't panic. Also, the IP's hostroute should
+# dissappear from the correct fib.
+atf_test_case same_ip_multiple_ifaces cleanup
+same_ip_multiple_ifaces_head()
+{
+ atf_set "descr" "Can remove an IPv4 alias from an interface when the same address is also assigned to another interface, on non-default FIBs."
+ atf_set "require.user" "root"
+}
+same_ip_multiple_ifaces_body()
+{
+ ADDR="192.0.2.2"
+ MASK0="24"
+ MASK1="32"
+
+ # Unlike most of the tests in this file, this is applicable regardless
+ # of net.add_addr_allfibs
+ get_fibs 4
+
+ # Setup the interfaces, then remove one alias. It should not panic.
+ setup_tap ${FIB0} inet ${ADDR} ${MASK0}
+ TAP0=${TAP}
+ setup_tap ${FIB1} inet ${ADDR} ${MASK1}
+ TAP1=${TAP}
+ ifconfig ${TAP1} -alias ${ADDR}
+ atf_check -o not-match:"^${ADDR}[[:space:]]" \
+ setfib ${FIB1} netstat -rn -f inet
+
+ # Do it again, in the opposite order. It should not panic.
+ setup_tap ${FIB2} inet ${ADDR} ${MASK0}
+ TAP0=${TAP}
+ setup_tap ${FIB3} inet ${ADDR} ${MASK1}
+ TAP1=${TAP}
+ ifconfig ${TAP0} -alias ${ADDR}
+ atf_check -o not-match:"^${ADDR}[[:space:]]" \
+ setfib ${FIB2} netstat -rn -f inet
+}
+same_ip_multiple_ifaces_cleanup()
+{
+ # Due to PR kern/189088, we must destroy the interfaces in LIFO order
+ # in order for the routes to be correctly cleaned up.
+ for TAPD in `tail -r "ifaces_to_cleanup"`; do
+ echo ifconfig ${TAPD} destroy
+ ifconfig ${TAPD} destroy
+ done
+}
+
+atf_test_case same_ip_multiple_ifaces_inet6 cleanup
+same_ip_multiple_ifaces_inet6_head()
+{
+ atf_set "descr" "Can remove an IPv6 alias from an interface when the same address is also assigned to another interface, on non-default FIBs."
+ atf_set "require.user" "root"
+}
+same_ip_multiple_ifaces_inet6_body()
+{
+ ADDR="2001:db8::2"
+ MASK0="64"
+ MASK1="128"
+
+ # Unlike most of the tests in this file, this is applicable regardless
+ # of net.add_addr_allfibs
+ get_fibs 2
+
+ # Setup the interfaces, then remove one alias. It should not panic.
+ setup_tap ${FIB0} inet6 ${ADDR} ${MASK0}
+ TAP0=${TAP}
+ setup_tap ${FIB1} inet6 ${ADDR} ${MASK1}
+ TAP1=${TAP}
+ atf_check -s exit:0 ifconfig ${TAP1} inet6 ${ADDR} -alias
+ atf_check -o not-match:"^${ADDR}[[:space:]]" \
+ setfib ${FIB1} netstat -rn -f inet6
+ ifconfig ${TAP1} destroy
+ ifconfig ${TAP0} destroy
+
+ # Do it again, in the opposite order. It should not panic.
+ setup_tap ${FIB0} inet6 ${ADDR} ${MASK0}
+ TAP0=${TAP}
+ setup_tap ${FIB1} inet6 ${ADDR} ${MASK1}
+ TAP1=${TAP}
+ atf_check -s exit:0 ifconfig ${TAP0} inet6 ${ADDR} -alias
+ atf_check -o not-match:"^${ADDR}[[:space:]]" \
+ setfib ${FIB0} netstat -rn -f inet6
+}
+same_ip_multiple_ifaces_inet6_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case slaac_on_nondefault_fib6 cleanup
+slaac_on_nondefault_fib6_head()
+{
+ atf_set "descr" "SLAAC correctly installs routes on non-default FIBs"
+ atf_set "require.user" "root"
+ atf_set "require.config" "allow_sysctl_side_effects"
+}
+slaac_on_nondefault_fib6_body()
+{
+ # Configure the epair interfaces to use nonrouteable RFC3849
+ # addresses and non-default FIBs
+ PREFIX="2001:db8:$(printf "%x" `jot -r 1 0 65535`):$(printf "%x" `jot -r 1 0 65535`)"
+ ADDR="$PREFIX::2"
+ GATEWAY="$PREFIX::1"
+ SUBNET="$PREFIX:"
+ MASK="64"
+
+ # Check system configuration
+ if [ 0 != `sysctl -n net.add_addr_allfibs` ]; then
+ atf_skip "This test requires net.add_addr_allfibs=0"
+ fi
+ get_fibs 2
+
+ sysctl -n "net.inet6.ip6.rfc6204w3" >> "rfc6204w3.state"
+ sysctl -n "net.inet6.ip6.forwarding" >> "forwarding.state"
+ # Enable forwarding so the kernel will send RAs
+ sysctl net.inet6.ip6.forwarding=1
+ # Enable RFC6204W3 mode so the kernel will enable default router
+ # selection while also forwarding packets
+ sysctl net.inet6.ip6.rfc6204w3=1
+
+ # Configure epair interfaces
+ get_epair
+ setup_iface "$EPAIRA" "$FIB0" inet6 ${ADDR} ${MASK}
+ echo setfib $FIB1 ifconfig "$EPAIRB" inet6 -ifdisabled accept_rtadv fib $FIB1 up
+ setfib $FIB1 ifconfig "$EPAIRB" inet6 -ifdisabled accept_rtadv fib $FIB1 up
+ rtadvd -p rtadvd.pid -C rtadvd.sock -c /dev/null "$EPAIRA"
+ rtsol "$EPAIRB"
+
+ # Check SLAAC address
+ atf_check -o match:"inet6 ${SUBNET}.*prefixlen ${MASK}.*autoconf" \
+ ifconfig "$EPAIRB"
+ # Check local route
+ atf_check -o match:"${SUBNET}.*\<UHS\>.*lo0" \
+ netstat -rnf inet6 -F $FIB1
+ # Check subnet route
+ atf_check -o match:"${SUBNET}:/${MASK}.*\<U\>.*$EPAIRB" \
+ netstat -rnf inet6 -F $FIB1
+ # Check default route
+ atf_check -o match:"default.*\<UG\>.*$EPAIRB" \
+ netstat -rnf inet6 -F $FIB1
+
+ # Check that none of the above routes appeared on other routes
+ for fib in $( seq 0 $(($(sysctl -n net.fibs) - 1))); do
+ if [ "$fib" = "$FIB1" -o "$fib" = "$FIB0" ]; then
+ continue
+ fi
+ atf_check -o not-match:"${SUBNET}.*\<UHS\>.*lo0" \
+ netstat -rnf inet6 -F $fib
+ atf_check -o not-match:"${SUBNET}:/${MASK}.*\<U\>.*$EPAIRB" \
+ netstat -rnf inet6 -F $fib
+ atf_check -o not-match:"default.*\<UG\>.*$EPAIRB" \
+ netstat -rnf inet6 -F $fib
+ done
+}
+slaac_on_nondefault_fib6_cleanup()
+{
+ if [ -f "rtadvd.pid" ]; then
+ # rtadvd can take a long time to shutdown. Use SIGKILL to kill
+ # it right away. The downside to using SIGKILL is that it
+ # won't send final RAs to all interfaces, but we don't care
+ # because we're about to destroy its interface anyway.
+ pkill -kill -F rtadvd.pid
+ rm -f rtadvd.pid
+ fi
+ cleanup_ifaces
+ if [ -f "forwarding.state" ] ; then
+ sysctl "net.inet6.ip6.forwarding"=`cat "forwarding.state"`
+ rm "forwarding.state"
+ fi
+ if [ -f "rfc6204w3.state" ] ; then
+ sysctl "net.inet6.ip6.rfc6204w3"=`cat "rfc6204w3.state"`
+ rm "rfc6204w3.state"
+ fi
+}
+
+# Regression test for kern/187550
+atf_test_case subnet_route_with_multiple_fibs_on_same_subnet cleanup
+subnet_route_with_multiple_fibs_on_same_subnet_head()
+{
+ atf_set "descr" "Multiple FIBs can have IPv4 subnet routes for the same subnet"
+ atf_set "require.user" "root"
+}
+
+subnet_route_with_multiple_fibs_on_same_subnet_body()
+{
+ # Configure the TAP interfaces to use a RFC5737 nonrouteable addresses
+ # and a non-default fib
+ ADDR0="192.0.2.2"
+ ADDR1="192.0.2.3"
+ SUBNET="192.0.2.0"
+ MASK="24"
+
+ # Check system configuration
+ if [ 0 != `sysctl -n net.add_addr_allfibs` ]; then
+ atf_skip "This test requires net.add_addr_allfibs=0"
+ fi
+ get_fibs 2
+
+ # Configure TAP interfaces
+ setup_tap "$FIB0" inet ${ADDR0} ${MASK}
+ setup_tap "$FIB1" inet ${ADDR1} ${MASK}
+
+ # Check that a subnet route exists on both fibs
+ atf_check -o ignore setfib "$FIB0" route get $ADDR1
+ atf_check -o ignore setfib "$FIB1" route get $ADDR0
+}
+
+subnet_route_with_multiple_fibs_on_same_subnet_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case subnet_route_with_multiple_fibs_on_same_subnet_inet6 cleanup
+subnet_route_with_multiple_fibs_on_same_subnet_inet6_head()
+{
+ atf_set "descr" "Multiple FIBs can have IPv6 subnet routes for the same subnet"
+ atf_set "require.user" "root"
+}
+
+subnet_route_with_multiple_fibs_on_same_subnet_inet6_body()
+{
+ # Configure the TAP interfaces to use a RFC3849 nonrouteable addresses
+ # and a non-default fib
+ ADDR0="2001:db8::2"
+ ADDR1="2001:db8::3"
+ SUBNET="2001:db8::"
+ MASK="64"
+
+ # Check system configuration
+ if [ 0 != `sysctl -n net.add_addr_allfibs` ]; then
+ atf_skip "This test requires net.add_addr_allfibs=0"
+ fi
+ get_fibs 2
+
+ # Configure TAP interfaces
+ setup_tap "$FIB0" inet6 ${ADDR0} ${MASK}
+ setup_tap "$FIB1" inet6 ${ADDR1} ${MASK}
+
+ # Check that a subnet route exists on both fibs
+ atf_check -o ignore setfib "$FIB0" route -6 get $ADDR1
+ atf_check -o ignore setfib "$FIB1" route -6 get $ADDR0
+}
+
+subnet_route_with_multiple_fibs_on_same_subnet_inet6_cleanup()
+{
+ cleanup_ifaces
+}
+
+# Test that source address selection works correctly for UDP packets with
+# SO_DONTROUTE set that are sent on non-default FIBs.
+# This bug was discovered with "setfib 1 netperf -t UDP_STREAM -H some_host"
+# Regression test for kern/187553
+#
+# The root cause was that ifa_ifwithnet() did not have a fib argument. It
+# would return an address from an interface on any FIB that had a subnet route
+# for the destination. If more than one were available, it would choose the
+# most specific. This is most easily tested by creating a FIB without a
+# default route, then trying to send a UDP packet with SO_DONTROUTE set to an
+# address which is not routable on that FIB. Absent the fix for this bug,
+# in_pcbladdr would choose an interface on any FIB with a default route. With
+# the fix, you will get EUNREACH or ENETUNREACH.
+atf_test_case udp_dontroute cleanup
+udp_dontroute_head()
+{
+ atf_set "descr" "Source address selection for UDP packets with SO_DONTROUTE on non-default FIBs works"
+ atf_set "require.user" "root"
+}
+
+udp_dontroute_body()
+{
+ # Configure the TAP interface to use an RFC5737 nonrouteable address
+ # and a non-default fib
+ ADDR0="192.0.2.2"
+ ADDR1="192.0.2.3"
+ SUBNET="192.0.2.0"
+ MASK="24"
+ # Use a different IP on the same subnet as the target
+ TARGET="192.0.2.100"
+ SRCDIR=`atf_get_srcdir`
+
+ # Check system configuration
+ if [ 0 != `sysctl -n net.add_addr_allfibs` ]; then
+ atf_skip "This test requires net.add_addr_allfibs=0"
+ fi
+ get_fibs 2
+
+ # Configure the TAP interfaces
+ setup_tap ${FIB0} inet ${ADDR0} ${MASK}
+ TARGET_TAP=${TAP}
+ setup_tap ${FIB1} inet ${ADDR1} ${MASK}
+
+ # Send a UDP packet with SO_DONTROUTE. In the failure case, it will
+ # return ENETUNREACH, or send the packet to the wrong tap
+ atf_check -o ignore setfib ${FIB0} \
+ ${SRCDIR}/udp_dontroute ${TARGET} /dev/${TARGET_TAP}
+ cleanup_ifaces
+
+ # Repeat, but this time target the other tap
+ setup_tap ${FIB0} inet ${ADDR0} ${MASK}
+ setup_tap ${FIB1} inet ${ADDR1} ${MASK}
+ TARGET_TAP=${TAP}
+
+ atf_check -o ignore setfib ${FIB1} \
+ ${SRCDIR}/udp_dontroute ${TARGET} /dev/${TARGET_TAP}
+}
+
+udp_dontroute_cleanup()
+{
+ cleanup_ifaces
+}
+
+atf_test_case udp_dontroute6 cleanup
+udp_dontroute6_head()
+{
+ atf_set "descr" "Source address selection for UDP IPv6 packets with SO_DONTROUTE on non-default FIBs works"
+ atf_set "require.user" "root"
+}
+
+udp_dontroute6_body()
+{
+ if [ "$(atf_config_get ci false)" = "true" ]; then
+ atf_skip "https://bugs.freebsd.org/244172"
+ fi
+ # Configure the TAP interface to use an RFC3849 nonrouteable address
+ # and a non-default fib
+ ADDR0="2001:db8::2"
+ ADDR1="2001:db8::3"
+ SUBNET="2001:db8::"
+ MASK="64"
+ # Use a different IP on the same subnet as the target
+ TARGET="2001:db8::100"
+ SRCDIR=`atf_get_srcdir`
+
+ # Check system configuration
+ if [ 0 != `sysctl -n net.add_addr_allfibs` ]; then
+ atf_skip "This test requires net.add_addr_allfibs=0"
+ fi
+ get_fibs 2
+
+ # Configure the TAP interfaces. Use no_dad so the addresses will be
+ # ready right away and won't be marked as tentative until DAD
+ # completes.
+ setup_tap ${FIB0} inet6 ${ADDR0} ${MASK} no_dad
+ TARGET_TAP=${TAP}
+ setup_tap ${FIB1} inet6 ${ADDR1} ${MASK} no_dad
+
+ # Send a UDP packet with SO_DONTROUTE. In the failure case, it will
+ # return ENETUNREACH, or send the packet to the wrong tap
+ atf_check -o ignore setfib ${FIB0} \
+ ${SRCDIR}/udp_dontroute -6 ${TARGET} /dev/${TARGET_TAP}
+ cleanup_ifaces
+
+ # Repeat, but this time target the other tap
+ setup_tap ${FIB0} inet6 ${ADDR0} ${MASK} no_dad
+ setup_tap ${FIB1} inet6 ${ADDR1} ${MASK} no_dad
+ TARGET_TAP=${TAP}
+
+ atf_check -o ignore setfib ${FIB1} \
+ ${SRCDIR}/udp_dontroute -6 ${TARGET} /dev/${TARGET_TAP}
+}
+
+udp_dontroute6_cleanup()
+{
+ cleanup_ifaces
+}
+
+
+atf_init_test_cases()
+{
+ atf_add_test_case arpresolve_checks_interface_fib
+ atf_add_test_case loopback_and_network_routes_on_nondefault_fib
+ atf_add_test_case loopback_and_network_routes_on_nondefault_fib_inet6
+ atf_add_test_case default_route_with_multiple_fibs_on_same_subnet
+ atf_add_test_case default_route_with_multiple_fibs_on_same_subnet_inet6
+ atf_add_test_case same_ip_multiple_ifaces_fib0
+ atf_add_test_case same_ip_multiple_ifaces
+ atf_add_test_case same_ip_multiple_ifaces_inet6
+ atf_add_test_case slaac_on_nondefault_fib6
+ atf_add_test_case subnet_route_with_multiple_fibs_on_same_subnet
+ atf_add_test_case subnet_route_with_multiple_fibs_on_same_subnet_inet6
+ atf_add_test_case udp_dontroute
+ atf_add_test_case udp_dontroute6
+}
+
+# Looks up one or more fibs from the configuration data and validates them.
+# Returns the results in the env varilables FIB0, FIB1, etc.
+
+# parameter numfibs The number of fibs to lookup
+get_fibs()
+{
+ NUMFIBS=$1
+ net_fibs=`sysctl -n net.fibs`
+ if [ $net_fibs -lt $(($NUMFIBS + 1)) ]; then
+ atf_check -o ignore sysctl net.fibs=$(($NUMFIBS + 1))
+ net_fibs=`sysctl -n net.fibs`
+ fi
+ i=0
+ while [ $i -lt "$NUMFIBS" ]; do
+ eval FIB${i}=$(($i + 1))
+ i=$(( $i + 1 ))
+ done
+}
+
+# Creates a new pair of connected epair(4) interface, registers them for
+# cleanup, and returns their namen via the environment variables EPAIRA and
+# EPAIRB
+get_epair()
+{
+ local EPAIRD
+
+ if (which pfctl && pfctl -s info | grep -q 'Status: Enabled') ||
+ [ `sysctl -n net.inet.ip.fw.enable` = "1" ] ||
+ (which ipf && ipf -V); then
+ atf_skip "firewalls interfere with this test"
+ fi
+
+ if EPAIRD=`ifconfig epair create`; then
+ # Record the epair device so we can clean it up later
+ echo ${EPAIRD} >> "ifaces_to_cleanup"
+ EPAIRA=${EPAIRD}
+ EPAIRB=${EPAIRD%a}b
+ else
+ atf_skip "Could not create epair(4) interfaces"
+ fi
+}
+
+# Creates a new tap(4) interface, registers it for cleanup, and returns the
+# name via the environment variable TAP
+get_tap()
+{
+ local TAPD
+
+ if TAPD=`ifconfig tap create`; then
+ # Record the TAP device so we can clean it up later
+ echo ${TAPD} >> "ifaces_to_cleanup"
+ TAP=${TAPD}
+ else
+ atf_skip "Could not create a tap(4) interface"
+ fi
+}
+
+# Configure an ethernet interface
+# parameters:
+# Interface name
+# fib
+# Protocol (inet or inet6)
+# IP address
+# Netmask in number of bits (eg 24 or 8)
+# Extra flags
+# Return: None
+setup_iface()
+{
+ local IFACE=$1
+ local FIB=$2
+ local PROTO=$3
+ local ADDR=$4
+ local MASK=$5
+ local FLAGS=$6
+ atf_check setfib ${FIB} ifconfig $IFACE ${PROTO} ${ADDR}/${MASK} fib $FIB $FLAGS
+}
+
+# Create a tap(4) interface, configure it, and register it for cleanup.
+# parameters:
+# fib
+# Protocol (inet or inet6)
+# IP address
+# Netmask in number of bits (eg 24 or 8)
+# Extra flags
+# Return: the tap interface name as the env variable TAP
+setup_tap()
+{
+ get_tap
+ setup_iface "$TAP" "$@"
+}
+
+cleanup_ifaces()
+{
+ if [ -f ifaces_to_cleanup ]; then
+ for iface in $(cat ifaces_to_cleanup); do
+ echo ifconfig "${iface}" destroy
+ ifconfig "${iface}" destroy 2>/dev/null || true
+ done
+ rm -f ifaces_to_cleanup
+ fi
+}
diff --git a/tests/sys/netinet/forward.sh b/tests/sys/netinet/forward.sh
new file mode 100755
index 000000000000..be69e91b6137
--- /dev/null
+++ b/tests/sys/netinet/forward.sh
@@ -0,0 +1,325 @@
+#!/usr/bin/env atf-sh
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Alexander V. Chernikov
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+atf_test_case "fwd_ip_icmp_iface_fast_success" "cleanup"
+fwd_ip_icmp_iface_fast_success_head() {
+
+ atf_set descr 'Test valid IPv4 on-stick fastforwarding to iface'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+fwd_ip_icmp_iface_fast_success_body() {
+
+ vnet_init
+
+ ip4a="192.0.2.1"
+ ip4b="192.0.2.2"
+ plen=29
+ src_ip="192.0.2.3"
+
+ script_name="../common/sender.py"
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a up
+ ifconfig ${epair}a inet ${ip4a}/${plen}
+
+ jname="v4t-fwd_ip_icmp_iface_fast_success"
+ vnet_mkjail ${jname} ${epair}b
+ jexec ${jname} ifconfig ${epair}b up
+ jexec ${jname} ifconfig ${epair}b inet ${ip4b}/${plen}
+
+ # Get router ip/mac
+ jail_ip=${ip4b}
+ jail_mac=`jexec ${jname} ifconfig ${epair}b ether | awk '$1~/ether/{print$2}'`
+
+ our_mac=`ifconfig ${epair}a ether | awk '$1~/ether/{print$2}'`
+
+ jexec ${jname} sysctl net.inet.ip.forwarding=1
+ # As we're doing router-on-the-stick, turn sending IP redirects off:
+ jexec ${jname} sysctl net.inet.ip.redirect=0
+
+ # echo "LOCAL: ${local_ip} ${local_mac}"
+ # echo "REMOTE: ${remote_rtr_ip} ${remote_rtr_mac}"
+
+ atf_check -s exit:0 $(atf_get_srcdir)/${script_name} \
+ --test_name fwd_ip_icmp_fast \
+ --smac ${our_mac} --dmac ${jail_mac} \
+ --sip ${src_ip} --dip ${ip4a} \
+ --iface ${epair}a
+
+ # check counters are valid
+ atf_check -o match:'1 packet forwarded \(1 packet fast forwarded\)' jexec ${jname} netstat -sp ip
+}
+
+fwd_ip_icmp_iface_fast_success_cleanup() {
+
+ vnet_cleanup
+}
+
+atf_test_case "fwd_ip_icmp_gw_fast_success" "cleanup"
+fwd_ip_icmp_gw_fast_success_head() {
+
+ atf_set descr 'Test valid IPv4 on-stick fastforwarding to gw'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+fwd_ip_icmp_gw_fast_success_body() {
+
+ vnet_init
+
+ ip4a="192.0.2.1"
+ ip4b="192.0.2.2"
+ plen=29
+ src_ip="192.0.2.3"
+ dst_ip="192.0.2.4"
+
+ script_name="../common/sender.py"
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a up
+ ifconfig ${epair}a inet ${ip4a}/${plen}
+
+ jname="v4t-fwd_ip_icmp_gw_fast_success"
+ vnet_mkjail ${jname} ${epair}b
+ jexec ${jname} ifconfig ${epair}b up
+ jexec ${jname} ifconfig ${epair}b inet ${ip4b}/${plen}
+
+ # Get router ip/mac
+ jail_ip=${ip4b}
+ jail_mac=`jexec ${jname} ifconfig ${epair}b ether | awk '$1~/ether/{print$2}'`
+
+ our_mac=`ifconfig ${epair}a ether | awk '$1~/ether/{print$2}'`
+
+ jexec ${jname} sysctl net.inet.ip.forwarding=1
+ # As we're doing router-on-the-stick, turn sending IP redirects off:
+ jexec ${jname} sysctl net.inet.ip.redirect=0
+
+ # Add host route
+ jexec ${jname} route -4 add -host ${dst_ip} ${ip4a}
+
+ # echo "LOCAL: ${local_ip} ${local_mac}"
+ # echo "REMOTE: ${remote_rtr_ip} ${remote_rtr_mac}"
+
+ atf_check -s exit:0 $(atf_get_srcdir)/${script_name} \
+ --test_name fwd_ip_icmp_fast \
+ --smac ${our_mac} --dmac ${jail_mac} \
+ --sip ${src_ip} --dip ${dst_ip} \
+ --iface ${epair}a
+
+ # check counters are valid
+ atf_check -o match:'1 packet forwarded \(1 packet fast forwarded\)' jexec ${jname} netstat -sp ip
+}
+
+fwd_ip_icmp_gw_fast_success_cleanup() {
+
+ vnet_cleanup
+}
+
+atf_test_case "fwd_ip_icmp_iface_slow_success" "cleanup"
+fwd_ip_icmp_iface_slow_success_head() {
+
+ atf_set descr 'Test valid IPv4 on-stick "slow" forwarding to iface'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+fwd_ip_icmp_iface_slow_success_body() {
+
+ vnet_init
+
+ ip4a="192.0.2.1"
+ ip4b="192.0.2.2"
+ plen=29
+ src_ip="192.0.2.3"
+
+ script_name="../common/sender.py"
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a up
+ ifconfig ${epair}a inet ${ip4a}/${plen}
+
+ jname="v4t-fwd_ip_icmp_iface_slow_success"
+ vnet_mkjail ${jname} ${epair}b
+ jexec ${jname} ifconfig ${epair}b up
+ jexec ${jname} ifconfig ${epair}b inet ${ip4b}/${plen}
+
+ # Get router ip/mac
+ jail_ip=${ip4b}
+ jail_mac=`jexec ${jname} ifconfig ${epair}b ether | awk '$1~/ether/{print$2}'`
+
+ our_mac=`ifconfig ${epair}a ether | awk '$1~/ether/{print$2}'`
+
+ jexec ${jname} sysctl net.inet.ip.forwarding=1
+ # As we're doing router-on-the-stick, turn sending IP redirects off:
+ jexec ${jname} sysctl net.inet.ip.redirect=0
+
+ # Generate packet with options to force slow-path
+ atf_check -s exit:0 $(atf_get_srcdir)/${script_name} \
+ --test_name fwd_ip_icmp_slow \
+ --smac ${our_mac} --dmac ${jail_mac} \
+ --sip ${src_ip} --dip ${ip4a} \
+ --iface ${epair}a
+
+ # check counters are valid
+ atf_check -o match:'1 packet forwarded \(0 packets fast forwarded\)' jexec ${jname} netstat -sp ip
+}
+
+fwd_ip_icmp_iface_slow_success_cleanup() {
+
+ vnet_cleanup
+}
+
+atf_test_case "fwd_ip_icmp_gw_slow_success" "cleanup"
+fwd_ip_icmp_gw_slow_success_head() {
+
+ atf_set descr 'Test valid IPv4 on-stick "slow" forwarding to gw'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+fwd_ip_icmp_gw_slow_success_body() {
+
+ vnet_init
+
+ ip4a="192.0.2.1"
+ ip4b="192.0.2.2"
+ plen=29
+ src_ip="192.0.2.3"
+ dst_ip="192.0.2.4"
+
+ script_name="../common/sender.py"
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a up
+ ifconfig ${epair}a inet ${ip4a}/${plen}
+
+ jname="v4t-fwd_ip_icmp_gw_slow_success"
+ vnet_mkjail ${jname} ${epair}b
+ jexec ${jname} ifconfig ${epair}b up
+ jexec ${jname} ifconfig ${epair}b inet ${ip4b}/${plen}
+
+ # Get router ip/mac
+ jail_ip=${ip4b}
+ jail_mac=`jexec ${jname} ifconfig ${epair}b ether | awk '$1~/ether/{print$2}'`
+
+ our_mac=`ifconfig ${epair}a ether | awk '$1~/ether/{print$2}'`
+
+ jexec ${jname} sysctl net.inet.ip.forwarding=1
+ # As we're doing router-on-the-stick, turn sending IP redirects off:
+ jexec ${jname} sysctl net.inet.ip.redirect=0
+
+ # Add host route
+ jexec ${jname} route -4 add -host ${dst_ip} ${ip4a}
+
+ # echo "LOCAL: ${local_ip} ${local_mac}"
+ # echo "REMOTE: ${remote_rtr_ip} ${remote_rtr_mac}"
+
+ atf_check -s exit:0 $(atf_get_srcdir)/${script_name} \
+ --test_name fwd_ip_icmp_fast \
+ --smac ${our_mac} --dmac ${jail_mac} \
+ --sip ${src_ip} --dip ${dst_ip} \
+ --iface ${epair}a
+
+ # check counters are valid
+ atf_check -o match:'1 packet forwarded \(1 packet fast forwarded\)' jexec ${jname} netstat -sp ip
+}
+
+fwd_ip_icmp_gw_slow_success_cleanup() {
+
+ vnet_cleanup
+}
+
+atf_test_case "fwd_ip_blackhole" "cleanup"
+fwd_ip_blackhole_head() {
+
+ atf_set descr 'Test blackhole routes'
+ atf_set require.user root
+}
+
+fwd_ip_blackhole_body() {
+ jname="v4t-fwd_ip_blackhole"
+
+ vnet_init
+
+ epair=$(vnet_mkepair)
+ epair_out=$(vnet_mkepair)
+
+ ifconfig ${epair}a 192.0.2.2/24 up
+
+ vnet_mkjail ${jname} ${epair}b ${epair_out}b
+ jexec ${jname} ifconfig lo0 127.0.0.1/8 up
+ jexec ${jname} ifconfig ${epair}b 192.0.2.1/24 up
+ jexec ${jname} ifconfig ${epair_out}b 198.51.100.1/24 up
+ jexec ${jname} sysctl net.inet.ip.forwarding=1
+
+ route add default 192.0.2.1
+
+ atf_check -s exit:2 -o ignore \
+ ping -c 1 -t 1 198.51.100.2
+ atf_check -s exit:0 -o match:"0 packets not forwardable" \
+ jexec ${jname} netstat -s -p ip
+
+ # Create blackhole route
+ jexec ${jname} /sbin/route add 198.51.100.2 -blackhole -fib 0
+ jexec ${jname} netstat -rn
+
+ # Include an IP option to ensure slow path
+ atf_check -s exit:2 -o ignore \
+ ping -c 1 -t 1 -R 198.51.100.2
+ atf_check -s exit:0 -o match:"1 packet not forwardable" \
+ jexec ${jname} netstat -s -p ip
+
+ # Now try via the fast path
+ atf_check -s exit:2 -o ignore \
+ ping -c 1 -t 1 198.51.100.2
+ atf_check -s exit:0 -o match:"2 packets not forwardable" \
+ jexec ${jname} netstat -s -p ip
+}
+
+fwd_ip_blackhole_cleanup() {
+
+ vnet_cleanup
+}
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case "fwd_ip_icmp_iface_fast_success"
+ atf_add_test_case "fwd_ip_icmp_gw_fast_success"
+ atf_add_test_case "fwd_ip_icmp_iface_slow_success"
+ atf_add_test_case "fwd_ip_icmp_gw_slow_success"
+ atf_add_test_case "fwd_ip_blackhole"
+}
+
+# end
+
diff --git a/tests/sys/netinet/igmp.py b/tests/sys/netinet/igmp.py
new file mode 100644
index 000000000000..feb9b8b571d5
--- /dev/null
+++ b/tests/sys/netinet/igmp.py
@@ -0,0 +1,157 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2023 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+import pytest
+from atf_python.sys.net.tools import ToolsHelper
+from atf_python.sys.net.vnet import VnetTestTemplate
+import os
+import socket
+import struct
+import sys
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+curdir = os.path.dirname(os.path.realpath(__file__))
+netpfil_common = curdir + "/../netpfil/common"
+sys.path.append(netpfil_common)
+
+sc = None
+sp = None
+
+def check_igmpv3(args, pkt):
+ igmp = pkt.getlayer(sc.igmpv3.IGMPv3)
+ if igmp is None:
+ return False
+
+ igmpmr = pkt.getlayer(sc.igmpv3.IGMPv3mr)
+ if igmpmr is None:
+ return False
+
+ for r in igmpmr.records:
+ if r.maddr != args["group"]:
+ return False
+ if args["type"] == "join":
+ if r.rtype != 4:
+ return False
+ elif args["type"] == "leave":
+ if r.rtype != 3:
+ return False
+ r.show()
+
+ return True
+
+def check_igmpv2(args, pkt):
+ pkt.show()
+
+ igmp = pkt.getlayer(sc.igmp.IGMP)
+ if igmp is None:
+ return False
+
+ if igmp.gaddr != args["group"]:
+ return False
+
+ if args["type"] == "join":
+ if igmp.type != 0x16:
+ return False
+ if args["type"] == "leave":
+ if igmp.type != 0x17:
+ return False
+
+ return True
+
+class TestIGMP(VnetTestTemplate):
+ REQUIRED_MODULES = []
+ TOPOLOGY = {
+ "vnet1": { "ifaces": [ "if1" ] },
+ "if1": { "prefixes4": [ ("192.0.2.1/24", "192.0.2.2/24" ) ] },
+ }
+
+ def setup_method(self, method):
+ global sc
+ if sc is None:
+ import scapy.contrib as _sc
+ import scapy.contrib.igmp
+ import scapy.contrib.igmpv3
+ import scapy.all as _sp
+ sc = _sc
+ sp = _sp
+ super().setup_method(method)
+
+ @pytest.mark.require_progs(["scapy"])
+ def test_igmp3_join_leave(self):
+ "Test that we send the expected join/leave IGMPv3 messages"
+
+ if1 = self.vnet.iface_alias_map["if1"]
+
+ # Start a background sniff
+ from sniffer import Sniffer
+ expected_pkt = { "type": "join", "group": "230.0.0.1" }
+ sniffer = Sniffer(expected_pkt, check_igmpv3, if1.name, timeout=10)
+
+ # Now join a multicast group, and see if we're getting the igmp packet we expect
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+ mreq = struct.pack("4sl", socket.inet_aton('230.0.0.1'), socket.INADDR_ANY)
+ s.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
+
+ # Wait for the sniffer to see the join packet
+ sniffer.join()
+ assert(sniffer.correctPackets > 0)
+
+ # Now leave, check for the packet
+ expected_pkt = { "type": "leave", "group": "230.0.0.1" }
+ sniffer = Sniffer(expected_pkt, check_igmpv3, if1.name)
+
+ s.close()
+ sniffer.join()
+ assert(sniffer.correctPackets > 0)
+
+ @pytest.mark.require_progs(["scapy"])
+ def test_igmp2_join_leave(self):
+ "Test that we send the expected join/leave IGMPv2 messages"
+ ToolsHelper.print_output("/sbin/sysctl net.inet.igmp.default_version=2")
+
+ if1 = self.vnet.iface_alias_map["if1"]
+
+ # Start a background sniff
+ from sniffer import Sniffer
+ expected_pkt = { "type": "join", "group": "230.0.0.1" }
+ sniffer = Sniffer(expected_pkt, check_igmpv2, if1.name, timeout=10)
+
+ # Now join a multicast group, and see if we're getting the igmp packet we expect
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+ mreq = struct.pack("4sl", socket.inet_aton('230.0.0.1'), socket.INADDR_ANY)
+ s.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
+
+ # Wait for the sniffer to see the join packet
+ sniffer.join()
+ assert(sniffer.correctPackets > 0)
+
+ # Now leave, check for the packet
+ expected_pkt = { "type": "leave", "group": "230.0.0.1" }
+ sniffer = Sniffer(expected_pkt, check_igmpv2, if1.name)
+
+ s.close()
+ sniffer.join()
+ assert(sniffer.correctPackets > 0)
diff --git a/tests/sys/netinet/ip6_v4mapped_test.c b/tests/sys/netinet/ip6_v4mapped_test.c
new file mode 100644
index 000000000000..fa23192f56e4
--- /dev/null
+++ b/tests/sys/netinet/ip6_v4mapped_test.c
@@ -0,0 +1,398 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2022 Michael J. Karels.
+ * Copyright (c) 2020 Netflix, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This test is derived from tcp_connect_port_test.c.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+
+#include <netinet/in.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#define SYSCTLBAKFILE "tmp.net.inet.ip.portrange.values"
+
+#define PORT_FIRST 10000 /* normal default */
+#define PORT_LAST 10003
+#define LOOPS 10 /* 5 should be enough */
+
+struct portrange {
+ int first;
+ int last;
+};
+
+/*
+ * Set first and last ports in the ipport range. Save the old values
+ * of the sysctls so they can be restored later.
+ */
+static void
+set_portrange(void)
+{
+ int error, fd, first_new, last_new;
+ struct portrange save_ports;
+ size_t sysctlsz;
+
+ /*
+ * Pre-emptively unlink our restoration file, so we will do no
+ * restoration on error.
+ */
+ unlink(SYSCTLBAKFILE);
+
+ /*
+ * Set the net.inet.ip.portrange.{first,last} sysctls. Save the
+ * old values so we can restore them.
+ */
+ first_new = PORT_FIRST;
+ sysctlsz = sizeof(save_ports.first);
+ error = sysctlbyname("net.inet.ip.portrange.first", &save_ports.first,
+ &sysctlsz, &first_new, sizeof(first_new));
+ if (error) {
+ warn("sysctlbyname(\"net.inet.ip.portrange.first\") "
+ "failed");
+ atf_tc_skip("Unable to set sysctl");
+ }
+ if (sysctlsz != sizeof(save_ports.first)) {
+ fprintf(stderr, "Error: unexpected sysctl value size "
+ "(expected %zu, actual %zu)\n", sizeof(save_ports.first),
+ sysctlsz);
+ goto restore_sysctl;
+ }
+
+ last_new = PORT_LAST;
+ sysctlsz = sizeof(save_ports.last);
+ error = sysctlbyname("net.inet.ip.portrange.last", &save_ports.last,
+ &sysctlsz, &last_new, sizeof(last_new));
+ if (error) {
+ warn("sysctlbyname(\"net.inet.ip.portrange.last\") "
+ "failed");
+ atf_tc_skip("Unable to set sysctl");
+ }
+ if (sysctlsz != sizeof(save_ports.last)) {
+ fprintf(stderr, "Error: unexpected sysctl value size "
+ "(expected %zu, actual %zu)\n", sizeof(save_ports.last),
+ sysctlsz);
+ goto restore_sysctl;
+ }
+
+ /* Open the backup file, write the contents, and close it. */
+ fd = open(SYSCTLBAKFILE, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL,
+ S_IRUSR|S_IWUSR);
+ if (fd < 0) {
+ warn("error opening sysctl backup file");
+ goto restore_sysctl;
+ }
+ error = write(fd, &save_ports, sizeof(save_ports));
+ if (error < 0) {
+ warn("error writing saved value to sysctl backup file");
+ goto cleanup_and_restore;
+ }
+ if (error != (int)sizeof(save_ports)) {
+ fprintf(stderr,
+ "Error writing saved value to sysctl backup file: "
+ "(expected %zu, actual %d)\n", sizeof(save_ports), error);
+ goto cleanup_and_restore;
+ }
+ error = close(fd);
+ if (error) {
+ warn("error closing sysctl backup file");
+cleanup_and_restore:
+ (void)close(fd);
+ (void)unlink(SYSCTLBAKFILE);
+restore_sysctl:
+ sysctlsz = sizeof(save_ports.first);
+ (void)sysctlbyname("net.inet.ip.portrange.first", NULL,
+ NULL, &save_ports.first, sysctlsz);
+ sysctlsz = sizeof(save_ports.last);
+ (void)sysctlbyname("net.inet.ip.portrange.last", NULL,
+ NULL, &save_ports.last, sysctlsz);
+ atf_tc_skip("Error setting sysctl");
+ }
+}
+
+/*
+ * Restore the sysctl values from the backup file and delete the backup file.
+ */
+static void
+restore_portrange(void)
+{
+ int error, fd;
+ struct portrange save_ports;
+
+ /* Open the backup file, read the contents, close it, and delete it. */
+ fd = open(SYSCTLBAKFILE, O_RDONLY);
+ if (fd < 0) {
+ warn("error opening sysctl backup file");
+ return;
+ }
+ error = read(fd, &save_ports, sizeof(save_ports));
+ if (error < 0) {
+ warn("error reading saved values from sysctl backup file");
+ return;
+ }
+ if (error != (int)sizeof(save_ports)) {
+ fprintf(stderr,
+ "Error reading saved values from sysctl backup file: "
+ "(expected %zu, actual %d)\n", sizeof(save_ports), error);
+ return;
+ }
+ error = close(fd);
+ if (error)
+ warn("error closing sysctl backup file");
+ error = unlink(SYSCTLBAKFILE);
+ if (error)
+ warn("error removing sysctl backup file");
+
+ /* Restore the saved sysctl values. */
+ error = sysctlbyname("net.inet.ip.portrange.first", NULL, NULL,
+ &save_ports.first, sizeof(save_ports.first));
+ if (error)
+ warn("sysctlbyname(\"net.inet.ip.portrange.first\") "
+ "failed while restoring value");
+ error = sysctlbyname("net.inet.ip.portrange.last", NULL, NULL,
+ &save_ports.last, sizeof(save_ports.last));
+ if (error)
+ warn("sysctlbyname(\"net.inet.ip.portrange.last\") "
+ "failed while restoring value");
+}
+
+ATF_TC_WITH_CLEANUP(tcp_v4mapped_bind);
+ATF_TC_HEAD(tcp_v4mapped_bind, tc)
+{
+ /* root is only required for sysctls (setup and cleanup). */
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.config", "allow_sysctl_side_effects");
+ atf_tc_set_md_var(tc, "descr",
+ "Check local port assignment with bind and mapped V4 addresses");
+}
+/*
+ * Create a listening IPv4 socket, then connect to it repeatedly using a
+ * bound IPv6 socket using a v4 mapped address. With a small port range,
+ * this should fail on a bind() call with EADDRNOTAVAIL. However, in
+ * previous systems, the bind() would succeed, binding a duplicate port,
+ * and then the connect would fail with EADDRINUSE. Make sure we get
+ * the right error.
+ */
+ATF_TC_BODY(tcp_v4mapped_bind, tc)
+{
+ union {
+ struct sockaddr saddr;
+ struct sockaddr_in saddr4;
+ struct sockaddr_in6 saddr6;
+ } su_clnt, su_srvr, su_mapped;
+ struct addrinfo ai_hint, *aip;
+ socklen_t salen;
+ int csock, error, i, lsock, off = 0;
+ bool got_bind_error = false;
+
+ /*
+ * Set the net.inet.ip.portrange.{first,last} sysctls to use a small
+ * range, allowing us to generate port exhaustion quickly.
+ */
+ set_portrange();
+
+ /* Setup the listen socket. */
+ lsock = socket(PF_INET, SOCK_STREAM, 0);
+ ATF_REQUIRE_MSG(lsock >= 0, "socket() for listen socket failed: %s",
+ strerror(errno));
+
+ memset(&su_srvr.saddr4, 0, sizeof(su_srvr.saddr4));
+ su_srvr.saddr4.sin_family = AF_INET;
+ error = bind(lsock, &su_srvr.saddr, sizeof(su_srvr.saddr4));
+ ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
+ error = listen(lsock, LOOPS + 1);
+ ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno));
+
+ /* Get the address of the listen socket. */
+ salen = sizeof(su_srvr);
+ error = getsockname(lsock, &su_srvr.saddr, &salen);
+ ATF_REQUIRE_MSG(error == 0,
+ "getsockname() for listen socket failed: %s",
+ strerror(errno));
+ ATF_REQUIRE_MSG(salen == sizeof(struct sockaddr_in),
+ "unexpected sockaddr size");
+ ATF_REQUIRE_MSG(su_srvr.saddr.sa_len == sizeof(struct sockaddr_in),
+ "unexpected sa_len size");
+
+ /* Set up destination address for client sockets. */
+ memset(&ai_hint, 0, sizeof(ai_hint));
+ ai_hint.ai_family = AF_INET6;
+ ai_hint.ai_flags = AI_NUMERICHOST | AI_V4MAPPED;
+ error = getaddrinfo("127.0.0.1", NULL, &ai_hint, &aip);
+ ATF_REQUIRE_MSG(error == 0, "getaddrinfo: %s", gai_strerror(error));
+ memcpy(&su_mapped.saddr6, aip->ai_addr, sizeof(su_mapped.saddr6));
+ su_mapped.saddr6.sin6_port = su_srvr.saddr4.sin_port;
+ freeaddrinfo(aip);
+
+ /* Set up address to bind for client sockets (unspecified). */
+ memset(&su_clnt.saddr6, 0, sizeof(su_clnt.saddr6));
+ su_clnt.saddr6.sin6_family = AF_INET6;
+
+ /* Open connections in a loop. */
+ for (i = 0; i < LOOPS; i++) {
+ csock = socket(PF_INET6, SOCK_STREAM, 0);
+ ATF_REQUIRE_MSG(csock >= 0,
+ "socket() for client socket %d failed: %s",
+ i, strerror(errno));
+ error = setsockopt(csock, IPPROTO_IPV6, IPV6_V6ONLY, &off,
+ sizeof(off));
+ ATF_REQUIRE_MSG(error == 0,
+ "setsockopt(IPV6_ONLY = 0) failed: %s", strerror(errno));
+
+ /*
+ * A bind would not be necessary for operation, but
+ * provokes the error.
+ */
+ error = bind(csock, &su_clnt.saddr, sizeof(su_clnt.saddr6));
+ if (error != 0) {
+ if (errno == EADDRNOTAVAIL) { /* Success, expected */
+ got_bind_error = true;
+ break;
+ }
+ ATF_REQUIRE_MSG(error == 0,
+ "client bind %d failed: %s", i, strerror(errno));
+ }
+
+ error = connect(csock, &su_mapped.saddr, su_mapped.saddr.sa_len);
+ if (error != 0 && errno == EADDRINUSE) {
+ /* This is the specific error we were looking for. */
+ atf_tc_fail("client connect %d failed, "
+ " client had duplicate port: %s",
+ i, strerror(errno));
+ }
+ ATF_REQUIRE_MSG(error == 0,
+ "connect() for client socket %d failed: %s",
+ i, strerror(errno));
+
+ /*
+ * We don't accept the new socket from the server socket
+ * or close the client socket, as we want the ports to
+ * remain busy. The range is small enough that this is
+ * not a problem.
+ */
+ }
+ ATF_REQUIRE_MSG(i >= 1, "No successful connections");
+ ATF_REQUIRE_MSG(got_bind_error == true, "No expected bind error");
+
+ ATF_REQUIRE(close(lsock) == 0);
+}
+ATF_TC_CLEANUP(tcp_v4mapped_bind, tc)
+{
+ restore_portrange();
+}
+
+ATF_TC(udp_v4mapped_sendto);
+ATF_TC_HEAD(udp_v4mapped_sendto, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Validate sendto() with a v4-mapped address and a v6-only socket");
+}
+ATF_TC_BODY(udp_v4mapped_sendto, tc)
+{
+ struct addrinfo ai_hint, *aip;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ ssize_t n;
+ socklen_t salen;
+ int error, ls, s, zero;
+ short port;
+ char ch;
+
+ ls = socket(PF_INET, SOCK_DGRAM, 0);
+ ATF_REQUIRE(ls >= 0);
+
+ memset(&ai_hint, 0, sizeof(ai_hint));
+ ai_hint.ai_family = AF_INET;
+ ai_hint.ai_flags = AI_NUMERICHOST;
+ error = getaddrinfo("127.0.0.1", NULL, &ai_hint, &aip);
+ ATF_REQUIRE_MSG(error == 0, "getaddrinfo: %s", gai_strerror(error));
+ memcpy(&sin, aip->ai_addr, sizeof(sin));
+
+ error = bind(ls, (struct sockaddr *)&sin, sizeof(sin));
+ ATF_REQUIRE_MSG(error == 0, "bind: %s", strerror(errno));
+ salen = sizeof(sin);
+ error = getsockname(ls, (struct sockaddr *)&sin, &salen);
+ ATF_REQUIRE_MSG(error == 0,
+ "getsockname() for listen socket failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(salen == sizeof(struct sockaddr_in),
+ "unexpected sockaddr size");
+ port = sin.sin_port;
+
+ s = socket(PF_INET6, SOCK_DGRAM, 0);
+ ATF_REQUIRE(s >= 0);
+
+ memset(&ai_hint, 0, sizeof(ai_hint));
+ ai_hint.ai_family = AF_INET6;
+ ai_hint.ai_flags = AI_NUMERICHOST | AI_V4MAPPED;
+ error = getaddrinfo("127.0.0.1", NULL, &ai_hint, &aip);
+ ATF_REQUIRE_MSG(error == 0, "getaddrinfo: %s", gai_strerror(error));
+ memcpy(&sin6, aip->ai_addr, sizeof(sin6));
+ sin6.sin6_port = port;
+ freeaddrinfo(aip);
+
+ ch = 0x42;
+ n = sendto(s, &ch, 1, 0, (struct sockaddr *)&sin6, sizeof(sin6));
+ ATF_REQUIRE_ERRNO(EINVAL, n == -1);
+
+ zero = 0;
+ error = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero));
+ ATF_REQUIRE_MSG(error == 0,
+ "setsockopt(IPV6_V6ONLY) failed: %s", strerror(errno));
+
+ ch = 0x42;
+ n = sendto(s, &ch, 1, 0, (struct sockaddr *)&sin6, sizeof(sin6));
+ ATF_REQUIRE_MSG(n == 1, "sendto() failed: %s", strerror(errno));
+
+ ch = 0;
+ n = recv(ls, &ch, 1, 0);
+ ATF_REQUIRE_MSG(n == 1, "recv() failed: %s", strerror(errno));
+ ATF_REQUIRE(ch == 0x42);
+
+ ATF_REQUIRE(close(s) == 0);
+ ATF_REQUIRE(close(ls) == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, tcp_v4mapped_bind);
+ ATF_TP_ADD_TC(tp, udp_v4mapped_sendto);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/netinet/ip_reass_test.c b/tests/sys/netinet/ip_reass_test.c
new file mode 100644
index 000000000000..538815bd7a2c
--- /dev/null
+++ b/tests/sys/netinet/ip_reass_test.c
@@ -0,0 +1,384 @@
+/*-
+ * Copyright (c) 2018 The FreeBSD Foundation
+ *
+ * This software was developed by Mark Johnston under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ifaddrs.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+struct lopacket {
+ u_int family;
+ struct ip hdr;
+ char payload[];
+};
+
+static void
+update_cksum(struct ip *ip)
+{
+ size_t i;
+ uint32_t cksum;
+ uint8_t *cksump;
+ uint16_t tmp;
+
+ ip->ip_sum = 0;
+ cksump = (char *)ip;
+ for (cksum = 0, i = 0; i < sizeof(*ip) / sizeof(uint16_t); i++) {
+ tmp = *cksump++;
+ tmp = tmp << 8 | *cksump++;
+ cksum += ntohs(tmp);
+ }
+ cksum = (cksum >> 16) + (cksum & 0xffff);
+ cksum = ~(cksum + (cksum >> 16));
+ ip->ip_sum = htons((uint16_t)cksum);
+}
+
+static struct lopacket *
+alloc_lopacket(in_addr_t dstaddr, size_t payloadlen)
+{
+ struct ip *ip;
+ struct lopacket *packet;
+ size_t pktlen;
+
+ pktlen = sizeof(*packet) + payloadlen;
+ packet = malloc(pktlen);
+ ATF_REQUIRE(packet != NULL);
+
+ memset(packet, 0, pktlen);
+ packet->family = AF_INET;
+
+ ip = &packet->hdr;
+ ip->ip_hl = sizeof(struct ip) >> 2;
+ ip->ip_v = 4;
+ ip->ip_tos = 0;
+ ip->ip_len = htons(sizeof(*ip) + payloadlen);
+ ip->ip_id = 0;
+ ip->ip_off = 0;
+ ip->ip_ttl = 1;
+ ip->ip_p = IPPROTO_IP;
+ ip->ip_sum = 0;
+ ip->ip_src.s_addr = dstaddr;
+ ip->ip_dst.s_addr = dstaddr;
+ update_cksum(ip);
+
+ return (packet);
+}
+
+static void
+free_lopacket(struct lopacket *packet)
+{
+
+ free(packet);
+}
+
+static void
+write_lopacket(int bpffd, struct lopacket *packet)
+{
+ struct timespec ts;
+ ssize_t n;
+ size_t len;
+
+ len = sizeof(packet->family) + ntohs(packet->hdr.ip_len);
+ n = write(bpffd, packet, len);
+ ATF_REQUIRE_MSG(n >= 0, "packet write failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG((size_t)n == len, "wrote %zd bytes instead of %zu",
+ n, len);
+
+ /*
+ * Loopback packets are dispatched asynchronously, give netisr some
+ * time.
+ */
+ ts.tv_sec = 0;
+ ts.tv_nsec = 5000000; /* 5ms */
+ (void)nanosleep(&ts, NULL);
+}
+
+static int
+open_lobpf(in_addr_t *addrp)
+{
+ struct ifreq ifr;
+ struct ifaddrs *ifa, *ifap;
+ int error, fd;
+
+ fd = open("/dev/bpf0", O_RDWR);
+ if (fd < 0 && errno == ENOENT)
+ atf_tc_skip("no BPF device available");
+ ATF_REQUIRE_MSG(fd >= 0, "open(/dev/bpf0): %s", strerror(errno));
+
+ error = getifaddrs(&ifap);
+ ATF_REQUIRE(error == 0);
+ for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next)
+ if ((ifa->ifa_flags & IFF_LOOPBACK) != 0 &&
+ ifa->ifa_addr->sa_family == AF_INET)
+ break;
+ if (ifa == NULL)
+ atf_tc_skip("no loopback address found");
+
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifa->ifa_name, IFNAMSIZ);
+ error = ioctl(fd, BIOCSETIF, &ifr);
+ ATF_REQUIRE_MSG(error == 0, "ioctl(BIOCSETIF): %s", strerror(errno));
+
+ *addrp = ((struct sockaddr_in *)(void *)ifa->ifa_addr)->sin_addr.s_addr;
+
+ freeifaddrs(ifap);
+
+ return (fd);
+}
+
+static void
+get_ipstat(struct ipstat *stat)
+{
+ size_t len;
+ int error;
+
+ memset(stat, 0, sizeof(*stat));
+ len = sizeof(*stat);
+ error = sysctlbyname("net.inet.ip.stats", stat, &len, NULL, 0);
+ ATF_REQUIRE_MSG(error == 0, "sysctl(net.inet.ip.stats) failed: %s",
+ strerror(errno));
+ ATF_REQUIRE(len == sizeof(*stat));
+}
+
+#define CHECK_IP_COUNTER(oldp, newp, counter) \
+ ATF_REQUIRE_MSG((oldp)->ips_ ## counter < (newp)->ips_ ## counter, \
+ "ips_" #counter " wasn't incremented (%ju vs. %ju)", \
+ (uintmax_t)old.ips_ ## counter, (uintmax_t)new.ips_## counter);
+
+/*
+ * Make sure a fragment with MF set doesn't come after the last fragment of a
+ * packet. Make sure that multiple fragments with MF clear have the same offset
+ * and length.
+ */
+ATF_TC(ip_reass__multiple_last_fragments);
+ATF_TC_HEAD(ip_reass__multiple_last_fragments, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(ip_reass__multiple_last_fragments, tc)
+{
+ struct ipstat old, new;
+ struct ip *ip;
+ struct lopacket *packet1, *packet2, *packet3, *packet4;
+ in_addr_t addr;
+ int error, fd;
+ uint16_t ipid;
+
+ fd = open_lobpf(&addr);
+ ipid = arc4random_uniform(UINT16_MAX + 1);
+
+ packet1 = alloc_lopacket(addr, 16);
+ ip = &packet1->hdr;
+ ip->ip_id = ipid;
+ ip->ip_off = htons(0x10);
+ update_cksum(ip);
+
+ packet2 = alloc_lopacket(addr, 16);
+ ip = &packet2->hdr;
+ ip->ip_id = ipid;
+ ip->ip_off = htons(0x20);
+ update_cksum(ip);
+
+ packet3 = alloc_lopacket(addr, 16);
+ ip = &packet3->hdr;
+ ip->ip_id = ipid;
+ ip->ip_off = htons(0x8);
+ update_cksum(ip);
+
+ packet4 = alloc_lopacket(addr, 32);
+ ip = &packet4->hdr;
+ ip->ip_id = ipid;
+ ip->ip_off = htons(0x10);
+ update_cksum(ip);
+
+ write_lopacket(fd, packet1);
+
+ /* packet2 comes after packet1. */
+ get_ipstat(&old);
+ write_lopacket(fd, packet2);
+ get_ipstat(&new);
+ CHECK_IP_COUNTER(&old, &new, fragdropped);
+
+ /* packet2 comes after packet1 and has MF set. */
+ packet2->hdr.ip_off = htons(IP_MF | 0x20);
+ update_cksum(&packet2->hdr);
+ get_ipstat(&old);
+ write_lopacket(fd, packet2);
+ get_ipstat(&new);
+ CHECK_IP_COUNTER(&old, &new, fragdropped);
+
+ /* packet3 comes before packet1 but overlaps. */
+ get_ipstat(&old);
+ write_lopacket(fd, packet3);
+ get_ipstat(&new);
+ CHECK_IP_COUNTER(&old, &new, fragdropped);
+
+ /* packet4 has the same offset as packet1 but is longer. */
+ get_ipstat(&old);
+ write_lopacket(fd, packet4);
+ get_ipstat(&new);
+ CHECK_IP_COUNTER(&old, &new, fragdropped);
+
+ error = close(fd);
+ ATF_REQUIRE(error == 0);
+ free_lopacket(packet1);
+ free_lopacket(packet2);
+ free_lopacket(packet3);
+ free_lopacket(packet4);
+}
+
+/*
+ * Make sure that we reject zero-length fragments.
+ */
+ATF_TC(ip_reass__zero_length_fragment);
+ATF_TC_HEAD(ip_reass__zero_length_fragment, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(ip_reass__zero_length_fragment, tc)
+{
+ struct ipstat old, new;
+ struct ip *ip;
+ struct lopacket *packet1, *packet2;
+ in_addr_t addr;
+ int error, fd;
+ uint16_t ipid;
+
+ fd = open_lobpf(&addr);
+ ipid = arc4random_uniform(UINT16_MAX + 1);
+
+ /*
+ * Create two packets, one with MF set, one without.
+ */
+ packet1 = alloc_lopacket(addr, 0);
+ ip = &packet1->hdr;
+ ip->ip_id = ipid;
+ ip->ip_off = htons(IP_MF | 0x10);
+ update_cksum(ip);
+
+ packet2 = alloc_lopacket(addr, 0);
+ ip = &packet2->hdr;
+ ip->ip_id = ~ipid;
+ ip->ip_off = htons(0x10);
+ update_cksum(ip);
+
+ get_ipstat(&old);
+ write_lopacket(fd, packet1);
+ get_ipstat(&new);
+ CHECK_IP_COUNTER(&old, &new, toosmall);
+ CHECK_IP_COUNTER(&old, &new, fragdropped);
+
+ get_ipstat(&old);
+ write_lopacket(fd, packet2);
+ get_ipstat(&new);
+ CHECK_IP_COUNTER(&old, &new, toosmall);
+ CHECK_IP_COUNTER(&old, &new, fragdropped);
+
+ error = close(fd);
+ ATF_REQUIRE(error == 0);
+ free_lopacket(packet1);
+ free_lopacket(packet2);
+}
+
+ATF_TC(ip_reass__large_fragment);
+ATF_TC_HEAD(ip_reass__large_fragment, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(ip_reass__large_fragment, tc)
+{
+ struct ipstat old, new;
+ struct ip *ip;
+ struct lopacket *packet1, *packet2;
+ in_addr_t addr;
+ int error, fd;
+ uint16_t ipid;
+
+ fd = open_lobpf(&addr);
+ ipid = arc4random_uniform(UINT16_MAX + 1);
+
+ /*
+ * Create two packets, one with MF set, one without.
+ *
+ * 16 + (0x1fff << 3) > IP_MAXPACKET, so these should fail the check.
+ */
+ packet1 = alloc_lopacket(addr, 16);
+ ip = &packet1->hdr;
+ ip->ip_id = ipid;
+ ip->ip_off = htons(IP_MF | 0x1fff);
+ update_cksum(ip);
+
+ packet2 = alloc_lopacket(addr, 16);
+ ip = &packet2->hdr;
+ ip->ip_id = ipid;
+ ip->ip_off = htons(0x1fff);
+ update_cksum(ip);
+
+ get_ipstat(&old);
+ write_lopacket(fd, packet1);
+ get_ipstat(&new);
+ CHECK_IP_COUNTER(&old, &new, toolong);
+ CHECK_IP_COUNTER(&old, &new, fragdropped);
+
+ get_ipstat(&old);
+ write_lopacket(fd, packet2);
+ get_ipstat(&new);
+ CHECK_IP_COUNTER(&old, &new, toolong);
+ CHECK_IP_COUNTER(&old, &new, fragdropped);
+
+ error = close(fd);
+ ATF_REQUIRE(error == 0);
+ free_lopacket(packet1);
+ free_lopacket(packet2);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, ip_reass__multiple_last_fragments);
+ ATF_TP_ADD_TC(tp, ip_reass__zero_length_fragment);
+ ATF_TP_ADD_TC(tp, ip_reass__large_fragment);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/netinet/libalias/1_instance.c b/tests/sys/netinet/libalias/1_instance.c
new file mode 100644
index 000000000000..842acb41bb90
--- /dev/null
+++ b/tests/sys/netinet/libalias/1_instance.c
@@ -0,0 +1,119 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright 2021 Lutz Donnerhacke
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <atf-c.h>
+#include <alias.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "util.h"
+
+ATF_TC(2_destroynull);
+ATF_TC_HEAD(2_destroynull, env)
+{
+ atf_tc_set_md_var(env, "descr", "Destroy the NULL instance");
+}
+ATF_TC_BODY(2_destroynull, dummy)
+{
+ atf_tc_expect_death("Code expects valid pointer.");
+ LibAliasUninit(NULL);
+}
+
+ATF_TC(1_singleinit);
+ATF_TC_HEAD(1_singleinit, env)
+{
+ atf_tc_set_md_var(env, "descr", "Create an instance");
+}
+ATF_TC_BODY(1_singleinit, dummy)
+{
+ struct libalias *la;
+
+ la = LibAliasInit(NULL);
+ ATF_CHECK_MSG(la != NULL, "Creating an instance failed.");
+ LibAliasUninit(la);
+}
+
+ATF_TC(3_multiinit);
+ATF_TC_HEAD(3_multiinit, env)
+{
+ atf_tc_set_md_var(env, "descr", "Recreate an instance multiple times");
+}
+ATF_TC_BODY(3_multiinit, dummy)
+{
+ struct libalias *la;
+ int i;
+
+ la = LibAliasInit(NULL);
+ for(i = 1; i < 30; i++) {
+ struct libalias *lo = la;
+
+ la = LibAliasInit(la);
+ ATF_CHECK_MSG(la == lo, "Recreating moved the instance around: %d", i);
+ }
+ LibAliasUninit(la);
+}
+
+ATF_TC(4_multiinstance);
+ATF_TC_HEAD(4_multiinstance, env)
+{
+ atf_tc_set_md_var(env, "descr", "Create and destoy multiple instances.");
+}
+ATF_TC_BODY(4_multiinstance, dummy)
+{
+ struct libalias *la[300];
+ int const num_instances = sizeof(la) / sizeof(*la);
+ int i;
+
+ for (i = 0; i < num_instances; i++) {
+ la[i] = LibAliasInit(NULL);
+ ATF_CHECK_MSG(la[i] != NULL, "Creating instance %d failed.", i);
+ }
+
+ qsort(la, num_instances, sizeof(*la), randcmp);
+
+ for (i = 0; i < num_instances; i++)
+ LibAliasUninit(la[i]);
+}
+
+ATF_TP_ADD_TCS(instance)
+{
+ /* Use "dd if=/dev/random bs=2 count=1 | od -x" to reproduce */
+ srand(0x5ac4);
+
+ ATF_TP_ADD_TC(instance, 2_destroynull);
+ ATF_TP_ADD_TC(instance, 1_singleinit);
+ ATF_TP_ADD_TC(instance, 3_multiinit);
+ ATF_TP_ADD_TC(instance, 4_multiinstance);
+
+ return atf_no_error();
+}
diff --git a/tests/sys/netinet/libalias/2_natout.c b/tests/sys/netinet/libalias/2_natout.c
new file mode 100644
index 000000000000..24ca06d11bf4
--- /dev/null
+++ b/tests/sys/netinet/libalias/2_natout.c
@@ -0,0 +1,547 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright 2021 Lutz Donnerhacke
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <atf-c.h>
+#include <alias.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "util.h"
+
+ATF_TC_WITHOUT_HEAD(1_simplemasq);
+ATF_TC_BODY(1_simplemasq, dummy)
+{
+ struct libalias *la = LibAliasInit(NULL);
+ struct ip *pip;
+
+ ATF_REQUIRE(la != NULL);
+ LibAliasSetAddress(la, masq);
+ LibAliasSetMode(la, 0, ~0);
+
+ pip = ip_packet(254, 64);
+ NAT_CHECK(pip, prv1, ext, masq);
+ NAT_CHECK(pip, prv2, ext, masq);
+ NAT_CHECK(pip, prv3, ext, masq);
+ NAT_CHECK(pip, cgn, ext, masq);
+ NAT_CHECK(pip, pub, ext, masq);
+
+ free(pip);
+ LibAliasUninit(la);
+}
+
+ATF_TC_WITHOUT_HEAD(2_unregistered);
+ATF_TC_BODY(2_unregistered, dummy)
+{
+ struct libalias *la = LibAliasInit(NULL);
+ struct ip *pip;
+
+ ATF_REQUIRE(la != NULL);
+ LibAliasSetAddress(la, masq);
+ LibAliasSetMode(la, PKT_ALIAS_UNREGISTERED_ONLY, ~0);
+
+ pip = ip_packet(254, 64);
+ NAT_CHECK(pip, prv1, ext, masq);
+ NAT_CHECK(pip, prv2, ext, masq);
+ NAT_CHECK(pip, prv3, ext, masq);
+ NAT_CHECK(pip, cgn, ext, cgn);
+ NAT_CHECK(pip, pub, ext, pub);
+
+ /*
+ * State is only for new connections
+ * Because they are now active,
+ * the mode setting should be ignored
+ */
+ LibAliasSetMode(la, 0, PKT_ALIAS_UNREGISTERED_ONLY);
+ NAT_CHECK(pip, prv1, ext, masq);
+ NAT_CHECK(pip, prv2, ext, masq);
+ NAT_CHECK(pip, prv3, ext, masq);
+ NAT_CHECK(pip, cgn, ext, cgn);
+ NAT_CHECK(pip, pub, ext, pub);
+
+ free(pip);
+ LibAliasUninit(la);
+}
+
+ATF_TC_WITHOUT_HEAD(3_cgn);
+ATF_TC_BODY(3_cgn, dummy)
+{
+ struct libalias *la = LibAliasInit(NULL);
+ struct ip *pip;
+
+ ATF_REQUIRE(la != NULL);
+ LibAliasSetAddress(la, masq);
+ LibAliasSetMode(la, PKT_ALIAS_UNREGISTERED_CGN, ~0);
+
+ pip = ip_packet(254, 64);
+ NAT_CHECK(pip, prv1, ext, masq);
+ NAT_CHECK(pip, prv2, ext, masq);
+ NAT_CHECK(pip, prv3, ext, masq);
+ NAT_CHECK(pip, cgn, ext, masq);
+ NAT_CHECK(pip, pub, ext, pub);
+
+ /*
+ * State is only for new connections
+ * Because they are now active,
+ * the mode setting should be ignored
+ */
+ LibAliasSetMode(la, 0, PKT_ALIAS_UNREGISTERED_CGN);
+ NAT_CHECK(pip, prv1, ext, masq);
+ NAT_CHECK(pip, prv2, ext, masq);
+ NAT_CHECK(pip, prv3, ext, masq);
+ NAT_CHECK(pip, cgn, ext, masq);
+ NAT_CHECK(pip, pub, ext, pub);
+
+ free(pip);
+ LibAliasUninit(la);
+}
+
+ATF_TC_WITHOUT_HEAD(4_udp);
+ATF_TC_BODY(4_udp, dummy)
+{
+ struct libalias *la = LibAliasInit(NULL);
+ struct ip *po, *pi;
+ struct udphdr *ui, *uo;
+ uint16_t sport = 0x1234;
+ uint16_t dport = 0x5678;
+ uint16_t aport;
+
+ ATF_REQUIRE(la != NULL);
+ LibAliasSetAddress(la, masq);
+ LibAliasSetMode(la, 0, ~0);
+
+ /* Query from prv1 */
+ po = ip_packet(0, 64);
+ UDP_NAT_CHECK(po, uo, prv1, sport, ext, dport, masq);
+ aport = ntohs(uo->uh_sport);
+ /* should use a different external port */
+ ATF_CHECK(aport != sport);
+
+ /* Response */
+ pi = ip_packet(0, 64);
+ UDP_UNNAT_CHECK(pi, ui, ext, dport, masq, aport, prv1, sport);
+
+ /* Query from different source with same ports */
+ UDP_NAT_CHECK(po, uo, prv2, sport, ext, dport, masq);
+ /* should use a different external port */
+ ATF_CHECK(uo->uh_sport != htons(aport));
+
+ /* Response to prv2 */
+ ui->uh_dport = uo->uh_sport;
+ UDP_UNNAT_CHECK(pi, ui, ext, dport, masq, htons(uo->uh_sport), prv2, sport);
+
+ /* Response to prv1 again */
+ UDP_UNNAT_CHECK(pi, ui, ext, dport, masq, aport, prv1, sport);
+
+ free(pi);
+ free(po);
+ LibAliasUninit(la);
+}
+
+ATF_TC_WITHOUT_HEAD(5_sameport);
+ATF_TC_BODY(5_sameport, dummy)
+{
+ struct libalias *la = LibAliasInit(NULL);
+ struct ip *p;
+ struct udphdr *u;
+ uint16_t sport = 0x1234;
+ uint16_t dport = 0x5678;
+ uint16_t aport;
+
+ ATF_REQUIRE(la != NULL);
+ LibAliasSetAddress(la, masq);
+ LibAliasSetMode(la, PKT_ALIAS_SAME_PORTS, ~0);
+
+ /* Query from prv1 */
+ p = ip_packet(0, 64);
+ UDP_NAT_CHECK(p, u, prv1, sport, ext, dport, masq);
+ aport = ntohs(u->uh_sport);
+ /* should use the same external port */
+ ATF_CHECK(aport == sport);
+
+ /* Query from different source with same ports */
+ UDP_NAT_CHECK(p, u, prv2, sport, ext, dport, masq);
+ /* should use a different external port */
+ ATF_CHECK(u->uh_sport != htons(aport));
+
+ free(p);
+ LibAliasUninit(la);
+}
+
+ATF_TC_WITHOUT_HEAD(6_cleartable);
+ATF_TC_BODY(6_cleartable, dummy)
+{
+ struct libalias *la = LibAliasInit(NULL);
+ struct ip *po, *pi;
+ struct udphdr *ui __unused, *uo;
+ uint16_t sport = 0x1234;
+ uint16_t dport = 0x5678;
+ uint16_t aport;
+
+ ATF_REQUIRE(la != NULL);
+ LibAliasSetAddress(la, masq);
+ LibAliasSetMode(la, PKT_ALIAS_RESET_ON_ADDR_CHANGE, ~0);
+ LibAliasSetMode(la, PKT_ALIAS_SAME_PORTS, PKT_ALIAS_SAME_PORTS);
+ LibAliasSetMode(la, PKT_ALIAS_DENY_INCOMING, PKT_ALIAS_DENY_INCOMING);
+
+ /* Query from prv1 */
+ po = ip_packet(0, 64);
+ UDP_NAT_CHECK(po, uo, prv1, sport, ext, dport, masq);
+ aport = ntohs(uo->uh_sport);
+ /* should use the same external port */
+ ATF_CHECK(aport == sport);
+
+ /* Response */
+ pi = ip_packet(0, 64);
+ UDP_UNNAT_CHECK(po, uo, ext, dport, masq, aport, prv1, sport);
+
+ /* clear table by keeping the address */
+ LibAliasSetAddress(la, ext);
+ LibAliasSetAddress(la, masq);
+
+ /* Response to prv1 again -> DENY_INCOMING */
+ UDP_UNNAT_FAIL(pi, ui, ext, dport, masq, aport);
+
+ /* Query from different source with same ports */
+ UDP_NAT_CHECK(po, uo, prv2, sport, ext, dport, masq);
+ /* should use the same external port, because it's free */
+ ATF_CHECK(uo->uh_sport == htons(aport));
+
+ /* Response to prv2 */
+ UDP_UNNAT_CHECK(po, uo, ext, dport, masq, htons(uo->uh_sport), prv2, sport);
+
+ free(pi);
+ free(po);
+ LibAliasUninit(la);
+}
+
+ATF_TC_WITHOUT_HEAD(7_stress);
+ATF_TC_BODY(7_stress, dummy)
+{
+ struct libalias *la = LibAliasInit(NULL);
+ struct ip *p;
+ struct udphdr *u;
+ struct {
+ struct in_addr src, dst;
+ uint16_t sport, dport, aport;
+ } *batch;
+ size_t const batch_size = 1200;
+ size_t const rounds = 25;
+ size_t i, j;
+
+ ATF_REQUIRE(la != NULL);
+ LibAliasSetAddress(la, masq);
+
+ p = ip_packet(0, 64);
+
+ batch = calloc(batch_size, sizeof(*batch));
+ ATF_REQUIRE(batch != NULL);
+ for (j = 0; j < rounds; j++) {
+ for (i = 0; i < batch_size; i++) {
+ struct in_addr s, d;
+ switch (i&3) {
+ case 0: s = prv1; d = ext; break;
+ case 1: s = prv2; d = pub; break;
+ case 2: s = prv3; d = ext; break;
+ case 3: s = cgn; d = pub; break;
+ }
+ s.s_addr &= htonl(0xffff0000);
+ d.s_addr &= htonl(0xffff0000);
+ batch[i].src.s_addr = s.s_addr | htonl(rand_range(0, 0xffff));
+ batch[i].dst.s_addr = d.s_addr | htonl(rand_range(0, 0xffff));
+ batch[i].sport = rand_range(1000, 60000);
+ batch[i].dport = rand_range(1000, 60000);
+ }
+
+ for (i = 0; i < batch_size; i++) {
+ UDP_NAT_CHECK(p, u,
+ batch[i].src, batch[i].sport,
+ batch[i].dst, batch[i].dport,
+ masq);
+ batch[i].aport = htons(u->uh_sport);
+ }
+
+ qsort(batch, batch_size, sizeof(*batch), randcmp);
+
+ for (i = 0; i < batch_size; i++) {
+ UDP_UNNAT_CHECK(p, u,
+ batch[i].dst, batch[i].dport,
+ masq, batch[i].aport,
+ batch[i].src, batch[i].sport);
+ }
+ }
+
+ free(batch);
+ free(p);
+ LibAliasUninit(la);
+}
+
+ATF_TC_WITHOUT_HEAD(8_portrange);
+ATF_TC_BODY(8_portrange, dummy)
+{
+ struct libalias *la = LibAliasInit(NULL);
+ struct ip *po;
+ struct udphdr *uo;
+ uint16_t sport = 0x1234;
+ uint16_t dport = 0x5678;
+ uint16_t aport;
+
+ ATF_REQUIRE(la != NULL);
+ LibAliasSetAddress(la, masq);
+ LibAliasSetMode(la, 0, ~0);
+ po = ip_packet(0, 64);
+
+ LibAliasSetAliasPortRange(la, 0, 0); /* reinit like ipfw */
+ UDP_NAT_CHECK(po, uo, prv1, sport, ext, dport, masq);
+ aport = ntohs(uo->uh_sport);
+ ATF_CHECK(aport >= 0x8000);
+
+ /* Different larger range */
+ LibAliasSetAliasPortRange(la, 2000, 3000);
+ dport++;
+ UDP_NAT_CHECK(po, uo, prv1, sport, ext, dport, masq);
+ aport = ntohs(uo->uh_sport);
+ ATF_CHECK(aport >= 2000 && aport < 3000);
+
+ /* Different small range (contains two ports) */
+ LibAliasSetAliasPortRange(la, 4000, 4001);
+ dport++;
+ UDP_NAT_CHECK(po, uo, prv1, sport, ext, dport, masq);
+ aport = ntohs(uo->uh_sport);
+ ATF_CHECK(aport >= 4000 && aport <= 4001);
+
+ sport++;
+ UDP_NAT_CHECK(po, uo, prv1, sport, ext, dport, masq);
+ aport = ntohs(uo->uh_sport);
+ ATF_CHECK(aport >= 4000 && aport <= 4001);
+
+ /* Third port not available in the range */
+ sport++;
+ UDP_NAT_FAIL(po, uo, prv1, sport, ext, dport);
+
+ /* Back to normal */
+ LibAliasSetAliasPortRange(la, 0, 0);
+ dport++;
+ UDP_NAT_CHECK(po, uo, prv1, sport, ext, dport, masq);
+ aport = ntohs(uo->uh_sport);
+ ATF_CHECK(aport >= 0x8000);
+
+ free(po);
+ LibAliasUninit(la);
+}
+
+ATF_TC_WITHOUT_HEAD(9_udp_eim_mapping);
+ATF_TC_BODY(9_udp_eim_mapping, dummy)
+{
+ struct libalias *la = LibAliasInit(NULL);
+ struct ip *po, *po2, *po3;
+ struct udphdr *uo, *uo2, *uo3;
+ uint16_t sport = 0x1234;
+ uint16_t dport = 0x5678;
+ uint16_t dport2 = 0x6789;
+ uint16_t aport, aport2, aport3;
+
+ ATF_REQUIRE(la != NULL);
+ LibAliasSetAddress(la, masq);
+ LibAliasSetMode(la, PKT_ALIAS_UDP_EIM, ~0);
+
+ po = ip_packet(0, 64);
+ UDP_NAT_CHECK(po, uo, prv1, sport, ext, dport, masq);
+ aport = ntohs(uo->uh_sport);
+
+ /* Change of dst port shouldn't change alias port */
+ po2 = ip_packet(0, 64);
+ UDP_NAT_CHECK(po2, uo2, prv1, sport, ext, dport2, masq);
+ aport2 = ntohs(uo2->uh_sport);
+ ATF_CHECK_EQ_MSG(aport, aport2,
+ "NAT uses address- and port-dependent mapping (%uh -> %uh)",
+ aport, aport2);
+
+ /* Change of dst address shouldn't change alias port */
+ po3 = ip_packet(0, 64);
+ UDP_NAT_CHECK(po3, uo3, prv1, sport, pub, dport, masq);
+ aport3 = ntohs(uo3->uh_sport);
+ ATF_CHECK_EQ_MSG(aport, aport3, "NAT uses address-dependent mapping");
+
+ free(po);
+ free(po2);
+ free(po3);
+ LibAliasUninit(la);
+}
+
+ATF_TC_WITHOUT_HEAD(10_udp_eim_out_in);
+ATF_TC_BODY(10_udp_eim_out_in, dummy)
+{
+ struct libalias *la = LibAliasInit(NULL);
+ struct ip *po, *po2, *po3;
+ struct udphdr *uo, *uo2, *uo3;
+ uint16_t sport = 0x1234;
+ uint16_t dport = 0x5678;
+ uint16_t dport2 = 0x6789;
+ uint16_t aport;
+
+ ATF_REQUIRE(la != NULL);
+ LibAliasSetAddress(la, masq);
+ LibAliasSetMode(la, PKT_ALIAS_UDP_EIM, ~0);
+
+ po = ip_packet(0, 64);
+ UDP_NAT_CHECK(po, uo, prv1, sport, pub, dport, masq);
+ aport = ntohs(uo->uh_sport);
+
+ /* Accepts inbound packets from different port */
+ po2 = ip_packet(0, 64);
+ UDP_UNNAT_CHECK(po2, uo2, pub, dport2, masq, aport, prv1, sport);
+
+ /* Accepts inbound packets from differerent host and port */
+ po3 = ip_packet(0, 64);
+ UDP_UNNAT_CHECK(po3, uo3, pub2, dport2, masq, aport, prv1, sport);
+
+ free(po);
+ free(po2);
+ free(po3);
+ LibAliasUninit(la);
+}
+
+ATF_TC_WITHOUT_HEAD(11_udp_eim_with_deny_incoming);
+ATF_TC_BODY(11_udp_eim_with_deny_incoming, dummy)
+{
+ struct libalias *la = LibAliasInit(NULL);
+ struct ip *po, *po2, *po3, *po4;
+ struct udphdr *uo;
+ uint16_t sport = 0x1234;
+ uint16_t dport = 0x5678;
+ uint16_t dport2 = 0x6789;
+ uint16_t aport;
+ int ret;
+
+ ATF_REQUIRE(la != NULL);
+ LibAliasSetAddress(la, masq);
+ LibAliasSetMode(la,
+ PKT_ALIAS_UDP_EIM | PKT_ALIAS_DENY_INCOMING,
+ ~0);
+
+ po = ip_packet(0, 64);
+ UDP_NAT_CHECK(po, uo, prv1, sport, pub, dport, masq);
+ aport = ntohs(uo->uh_sport);
+
+ po2 = ip_packet(0, 64);
+ po2->ip_src = pub;
+ po2->ip_dst = masq;
+ set_udp(po2, dport, aport);
+ ret = LibAliasIn(la, po2, 64);
+ ATF_CHECK_EQ_MSG(PKT_ALIAS_OK, ret,
+ "LibAliasIn failed with error %d\n", ret);
+
+ po3 = ip_packet(0, 64);
+ po3->ip_src = pub;
+ po3->ip_dst = masq;
+ set_udp(po3, dport2, aport);
+ ret = LibAliasIn(la, po3, 64);
+ ATF_CHECK_EQ_MSG(PKT_ALIAS_IGNORED, ret,
+ "incoming packet from different port not ignored "
+ "with PKT_ALIAS_DENY_INCOMING");
+
+ po4 = ip_packet(0, 64);
+ po4->ip_src = pub2;
+ po4->ip_dst = masq;
+ set_udp(po4, dport2, aport);
+ ret = LibAliasIn(la, po4, 64);
+ ATF_CHECK_EQ_MSG(PKT_ALIAS_IGNORED, ret,
+ "incoming packet from different address and port not ignored "
+ "with PKT_ALIAS_DENY_INCOMING");
+
+ free(po);
+ free(po2);
+ free(po3);
+ free(po4);
+ LibAliasUninit(la);
+}
+
+ATF_TC_WITHOUT_HEAD(12_udp_eim_hairpinning);
+ATF_TC_BODY(12_udp_eim_hairpinning, dummy)
+{
+ struct libalias *la = LibAliasInit(NULL);
+ struct ip *po, *po2, *po3;
+ struct udphdr *uo, *uo2, *uo3;
+ uint16_t sport1 = 0x1234;
+ uint16_t sport2 = 0x2345;
+ uint16_t dport = 0x5678;
+ uint16_t extport1, extport2;
+
+ ATF_REQUIRE(la != NULL);
+ LibAliasSetAddress(la, masq);
+ LibAliasSetMode(la, PKT_ALIAS_UDP_EIM, ~0);
+
+ /* prv1 sends out somewhere (eg. a STUN server) */
+ po = ip_packet(0, 64);
+ UDP_NAT_CHECK(po, uo, prv1, sport1, pub, dport, masq);
+ extport1 = ntohs(uo->uh_sport);
+
+ /* prv2, behind the same NAT as prv1, also sends out somewhere */
+ po2 = ip_packet(0, 64);
+ UDP_NAT_CHECK(po2, uo2, prv2, sport2, pub, dport, masq);
+ extport2 = ntohs(uo2->uh_sport);
+
+ /* hairpin: prv1 sends to prv2's external NAT mapping
+ * (unaware it could address it internally instead).
+ */
+ po3 = ip_packet(0, 64);
+ UDP_NAT_CHECK(po3, uo3, prv1, sport1, masq, extport2, masq);
+ UDP_UNNAT_CHECK(po3, uo3, masq, extport1, masq, extport2,
+ prv2, sport2);
+
+ free(po);
+ free(po2);
+ free(po3);
+ LibAliasUninit(la);
+}
+
+ATF_TP_ADD_TCS(natout)
+{
+ /* Use "dd if=/dev/random bs=2 count=1 | od -x" to reproduce */
+ srand(0x0b61);
+
+ ATF_TP_ADD_TC(natout, 1_simplemasq);
+ ATF_TP_ADD_TC(natout, 2_unregistered);
+ ATF_TP_ADD_TC(natout, 3_cgn);
+ ATF_TP_ADD_TC(natout, 4_udp);
+ ATF_TP_ADD_TC(natout, 5_sameport);
+ ATF_TP_ADD_TC(natout, 6_cleartable);
+ ATF_TP_ADD_TC(natout, 7_stress);
+ ATF_TP_ADD_TC(natout, 8_portrange);
+ ATF_TP_ADD_TC(natout, 9_udp_eim_mapping);
+ ATF_TP_ADD_TC(natout, 10_udp_eim_out_in);
+ ATF_TP_ADD_TC(natout, 11_udp_eim_with_deny_incoming);
+ ATF_TP_ADD_TC(natout, 12_udp_eim_hairpinning);
+
+ return atf_no_error();
+}
diff --git a/tests/sys/netinet/libalias/3_natin.c b/tests/sys/netinet/libalias/3_natin.c
new file mode 100644
index 000000000000..3bc088ce3da9
--- /dev/null
+++ b/tests/sys/netinet/libalias/3_natin.c
@@ -0,0 +1,381 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright 2021 Lutz Donnerhacke
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <atf-c.h>
+#include <alias.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "util.h"
+
+ATF_TC_WITHOUT_HEAD(1_portforward);
+ATF_TC_BODY(1_portforward, dummy)
+{
+ struct libalias *la = LibAliasInit(NULL);
+ struct alias_link *pf1, *pf2, *pf3, *pf4;
+ struct ip *p;
+ struct udphdr *u;
+
+ ATF_REQUIRE(la != NULL);
+ LibAliasSetAddress(la, masq);
+ LibAliasSetMode(la, PKT_ALIAS_RESET_ON_ADDR_CHANGE, ~0);
+ LibAliasSetMode(la, PKT_ALIAS_DENY_INCOMING, PKT_ALIAS_DENY_INCOMING);
+
+ /*
+ * Fully specified
+ */
+ pf1 = LibAliasRedirectPort(la, prv1, ntohs(0x1234), ext, ntohs(0x5678), masq, ntohs(0xabcd), IPPROTO_UDP);
+ ATF_REQUIRE(pf1 != NULL);
+
+ p = ip_packet(0, 64);
+ UDP_UNNAT_CHECK(p, u, ext, 0x5678, masq, 0xabcd, prv1, 0x1234);
+ /* try again */
+ UDP_UNNAT_CHECK(p, u, ext, 0x5678, masq, 0xabcd, prv1, 0x1234);
+ /* different source */
+ UDP_UNNAT_FAIL(p, u, pub, 0x5678, masq, 0xabcd);
+ UDP_UNNAT_FAIL(p, u, ext, 0xdead, masq, 0xabcd);
+
+ /* clear table by keeping the address */
+ LibAliasSetAddress(la, ext);
+ LibAliasSetAddress(la, masq);
+
+ /* delete and try again */
+ LibAliasRedirectDelete(la, pf1);
+ UDP_UNNAT_FAIL(p, u, ext, 0x5678, masq, 0xabcd);
+
+ /*
+ * Any external port
+ */
+ pf2 = LibAliasRedirectPort(la, prv2, ntohs(0x1234), ext, ntohs(0), masq, ntohs(0xabcd), IPPROTO_UDP);
+ ATF_REQUIRE(pf2 != NULL);
+
+ UDP_UNNAT_CHECK(p, u, ext, 0x5678, masq, 0xabcd, prv2, 0x1234);
+ /* try again */
+ UDP_UNNAT_CHECK(p, u, ext, 0x5678, masq, 0xabcd, prv2, 0x1234);
+ /* different source */
+ UDP_UNNAT_FAIL(p, u, pub, 0x5678, masq, 0xabcd);
+ UDP_UNNAT_CHECK(p, u, ext, 0xdead, masq, 0xabcd, prv2, 0x1234);
+
+ /* clear table by keeping the address */
+ LibAliasSetAddress(la, ext);
+ LibAliasSetAddress(la, masq);
+
+ /* delete and try again */
+ LibAliasRedirectDelete(la, pf2);
+ UDP_UNNAT_FAIL(p, u, ext, 0x5678, masq, 0xabcd);
+
+ /*
+ * Any external host
+ */
+ pf3 = LibAliasRedirectPort(la, prv3, ntohs(0x1234), ANY_ADDR, ntohs(0x5678), masq, ntohs(0xabcd), IPPROTO_UDP);
+ ATF_REQUIRE(pf3 != NULL);
+
+ UDP_UNNAT_CHECK(p, u, ext, 0x5678, masq, 0xabcd, prv3, 0x1234);
+ /* try again */
+ UDP_UNNAT_CHECK(p, u, ext, 0x5678, masq, 0xabcd, prv3, 0x1234);
+ /* different source */
+ UDP_UNNAT_CHECK(p, u, pub, 0x5678, masq, 0xabcd, prv3, 0x1234);
+ UDP_UNNAT_FAIL(p, u, ext, 0xdead, masq, 0xabcd);
+
+ /* clear table by keeping the address */
+ LibAliasSetAddress(la, ext);
+ LibAliasSetAddress(la, masq);
+
+ /* delete and try again */
+ LibAliasRedirectDelete(la, pf3);
+ UDP_UNNAT_FAIL(p, u, ext, 0x5678, masq, 0xabcd);
+
+ /*
+ * Any external host, any port
+ */
+ pf4 = LibAliasRedirectPort(la, cgn, ntohs(0x1234), ANY_ADDR, ntohs(0), masq, ntohs(0xabcd), IPPROTO_UDP);
+ ATF_REQUIRE(pf4 != NULL);
+
+ UDP_UNNAT_CHECK(p, u, ext, 0x5678, masq, 0xabcd, cgn, 0x1234);
+ /* try again */
+ UDP_UNNAT_CHECK(p, u, ext, 0x5678, masq, 0xabcd, cgn, 0x1234);
+ /* different source */
+ UDP_UNNAT_CHECK(p, u, pub, 0x5678, masq, 0xabcd, cgn, 0x1234);
+ UDP_UNNAT_CHECK(p, u, ext, 0xdead, masq, 0xabcd, cgn, 0x1234);
+
+ /* clear table by keeping the address */
+ LibAliasSetAddress(la, ext);
+ LibAliasSetAddress(la, masq);
+
+ /* delete and try again */
+ LibAliasRedirectDelete(la, pf4);
+ UDP_UNNAT_FAIL(p, u, ext, 0x5678, masq, 0xabcd);
+
+ free(p);
+ LibAliasUninit(la);
+}
+
+ATF_TC_WITHOUT_HEAD(2_portoverlap);
+ATF_TC_BODY(2_portoverlap, dummy)
+{
+ struct libalias *la = LibAliasInit(NULL);
+ struct alias_link *pf1, *pf2, *pf3, *pf4;
+ struct ip *p;
+ struct udphdr *u;
+
+ ATF_REQUIRE(la != NULL);
+ LibAliasSetAddress(la, masq);
+ LibAliasSetMode(la, PKT_ALIAS_RESET_ON_ADDR_CHANGE, ~0);
+ LibAliasSetMode(la, PKT_ALIAS_DENY_INCOMING, PKT_ALIAS_DENY_INCOMING);
+
+ /*
+ * Fully specified
+ */
+ pf1 = LibAliasRedirectPort(la, prv2, ntohs(0x1234), ext, ntohs(0x5678), masq, ntohs(0xabcd), IPPROTO_UDP);
+ ATF_REQUIRE(pf1 != NULL);
+
+ p = ip_packet(0, 64);
+ UDP_UNNAT_CHECK(p, u, ext, 0x5678, masq, 0xabcd, prv2, 0x1234);
+
+ /* clear table by keeping the address */
+ LibAliasSetAddress(la, ext);
+ LibAliasSetAddress(la, masq);
+
+ /*
+ * Fully specified (override)
+ */
+ pf1 = LibAliasRedirectPort(la, prv1, ntohs(0x1234), ext, ntohs(0x5678), masq, ntohs(0xabcd), IPPROTO_UDP);
+ ATF_REQUIRE(pf1 != NULL);
+
+ UDP_UNNAT_CHECK(p, u, ext, 0x5678, masq, 0xabcd, prv1, 0x1234);
+
+ /* clear table by keeping the address */
+ LibAliasSetAddress(la, ext);
+ LibAliasSetAddress(la, masq);
+
+ /*
+ * Any external port
+ */
+ pf2 = LibAliasRedirectPort(la, prv2, ntohs(0x1234), ext, ntohs(0), masq, ntohs(0xabcd), IPPROTO_UDP);
+ ATF_REQUIRE(pf2 != NULL);
+
+ UDP_UNNAT_CHECK(p, u, ext, 0x5679, masq, 0xabcd, prv2, 0x1234);
+ /* more specific rule wins */
+ UDP_UNNAT_CHECK(p, u, ext, 0x5678, masq, 0xabcd, prv1, 0x1234);
+
+ /* clear table by keeping the address */
+ LibAliasSetAddress(la, ext);
+ LibAliasSetAddress(la, masq);
+
+ /*
+ * Any external host
+ */
+ pf3 = LibAliasRedirectPort(la, prv3, ntohs(0x1234), ANY_ADDR, ntohs(0x5678), masq, ntohs(0xabcd), IPPROTO_UDP);
+ ATF_REQUIRE(pf3 != NULL);
+
+ UDP_UNNAT_CHECK(p, u, pub, 0x5678, masq, 0xabcd, prv3, 0x1234);
+ /* more specific rule wins */
+ UDP_UNNAT_CHECK(p, u, ext, 0x5679, masq, 0xabcd, prv2, 0x1234);
+ UDP_UNNAT_CHECK(p, u, ext, 0x5678, masq, 0xabcd, prv1, 0x1234);
+
+ /* clear table by keeping the address */
+ LibAliasSetAddress(la, ext);
+ LibAliasSetAddress(la, masq);
+
+ /*
+ * Any external host, any port
+ */
+ pf4 = LibAliasRedirectPort(la, cgn, ntohs(0x1234), ANY_ADDR, ntohs(0), masq, ntohs(0xabcd), IPPROTO_UDP);
+ ATF_REQUIRE(pf4 != NULL);
+
+ UDP_UNNAT_CHECK(p, u, prv1, 0x5679, masq, 0xabcd, cgn, 0x1234);
+ /* more specific rule wins */
+ UDP_UNNAT_CHECK(p, u, pub, 0x5678, masq, 0xabcd, prv3, 0x1234);
+ UDP_UNNAT_CHECK(p, u, ext, 0x5679, masq, 0xabcd, prv2, 0x1234);
+ UDP_UNNAT_CHECK(p, u, ext, 0x5678, masq, 0xabcd, prv1, 0x1234);
+
+ free(p);
+ LibAliasUninit(la);
+}
+
+ATF_TC_WITHOUT_HEAD(3_redirectany);
+ATF_TC_BODY(3_redirectany, dummy)
+{
+ struct libalias *la = LibAliasInit(NULL);
+ struct alias_link *pf;
+ struct ip *p;
+ struct udphdr *u;
+
+ ATF_REQUIRE(la != NULL);
+ LibAliasSetMode(la, PKT_ALIAS_DENY_INCOMING, ~0);
+ p = ip_packet(0, 64);
+
+ pf = LibAliasRedirectPort(la, prv1, ntohs(0x1234), ANY_ADDR, 0, ANY_ADDR, ntohs(0xabcd), IPPROTO_UDP);
+ ATF_REQUIRE(pf != NULL);
+
+ LibAliasSetAddress(la, masq);
+ UDP_UNNAT_CHECK(p, u, ext, 0x5678, masq, 0xabcd, prv1, 0x1234);
+ UDP_UNNAT_FAIL(p, u, pub, 0x5678, pub, 0xabcd);
+
+ LibAliasSetAddress(la, pub);
+ UDP_UNNAT_CHECK(p, u, pub, 0x5679, pub, 0xabcd, prv1, 0x1234);
+ UDP_UNNAT_FAIL(p, u, ext, 0x5679, masq, 0xabcd);
+
+ free(p);
+ LibAliasUninit(la);
+}
+
+ATF_TC_WITHOUT_HEAD(4_redirectaddr);
+ATF_TC_BODY(4_redirectaddr, dummy)
+{
+ struct libalias *la = LibAliasInit(NULL);
+ struct alias_link *pf1, *pf2;
+ struct ip *p;
+
+ ATF_REQUIRE(la != NULL);
+ LibAliasSetAddress(la, masq);
+ pf1 = LibAliasRedirectAddr(la, prv1, pub);
+ ATF_REQUIRE(pf1 != NULL);
+
+ p = ip_packet(254, 64);
+ UNNAT_CHECK(p, ext, pub, prv1);
+ UNNAT_CHECK(p, ext, masq, masq);
+
+ pf2 = LibAliasRedirectAddr(la, prv2, pub);
+ ATF_REQUIRE(pf2 != NULL);
+ UNNAT_CHECK(p, ext, pub, prv1);
+ p->ip_p = 253; /* new flows */
+ UNNAT_CHECK(p, ext, pub, prv2);
+ UNNAT_CHECK(p, ext, masq, masq);
+
+ p->ip_p = 252; /* new flows */
+ NAT_CHECK(p, prv1, ext, pub);
+ NAT_CHECK(p, prv2, ext, pub);
+ NAT_CHECK(p, prv3, ext, masq);
+
+ LibAliasSetMode(la, PKT_ALIAS_DENY_INCOMING, ~0);
+ p->ip_p = 251; /* new flows */
+ UNNAT_FAIL(p, ext, pub);
+ UNNAT_FAIL(p, ext, masq);
+
+ /* unhide older version */
+ LibAliasRedirectDelete(la, pf2);
+ LibAliasSetMode(la, 0, ~0);
+ p->ip_p = 250; /* new flows */
+ UNNAT_CHECK(p, ext, pub, prv1);
+
+ p->ip_p = 249; /* new flows */
+ NAT_CHECK(p, prv1, ext, pub);
+ NAT_CHECK(p, prv2, ext, masq);
+ NAT_CHECK(p, prv3, ext, masq);
+
+ free(p);
+ LibAliasUninit(la);
+}
+
+ATF_TC_WITHOUT_HEAD(5_lsnat);
+ATF_TC_BODY(5_lsnat, dummy)
+{
+ struct libalias *la = LibAliasInit(NULL);
+ struct alias_link *pf;
+ struct ip *p;
+ struct udphdr *u;
+
+ ATF_REQUIRE(la != NULL);
+ LibAliasSetMode(la, 0, ~0);
+ p = ip_packet(0, 64);
+
+ pf = LibAliasRedirectPort(la, cgn, ntohs(0xdead), ANY_ADDR, 0, masq, ntohs(0xabcd), IPPROTO_UDP);
+ ATF_REQUIRE(pf != NULL);
+
+ ATF_REQUIRE(0 == LibAliasAddServer(la, pf, prv1, ntohs(0x1234)));
+ ATF_REQUIRE(0 == LibAliasAddServer(la, pf, prv2, ntohs(0x2345)));
+ ATF_REQUIRE(0 == LibAliasAddServer(la, pf, prv3, ntohs(0x3456)));
+
+ UDP_UNNAT_CHECK(p, u, ext, 0x5678, masq, 0xabcd, prv3, 0x3456);
+ UDP_UNNAT_CHECK(p, u, ext, 0x5679, masq, 0xabcd, prv2, 0x2345);
+ UDP_UNNAT_CHECK(p, u, ext, 0x567a, masq, 0xabcd, prv1, 0x1234);
+ UDP_UNNAT_CHECK(p, u, ext, 0x567b, masq, 0xabcd, prv3, 0x3456);
+ UDP_UNNAT_CHECK(p, u, ext, 0x567c, masq, 0xabcd, prv2, 0x2345);
+ UDP_UNNAT_CHECK(p, u, ext, 0x567d, masq, 0xabcd, prv1, 0x1234);
+
+ free(p);
+ LibAliasUninit(la);
+}
+
+ATF_TC_WITHOUT_HEAD(6_oneshot);
+ATF_TC_BODY(6_oneshot, dummy)
+{
+ struct libalias *la = LibAliasInit(NULL);
+ struct alias_link *pf;
+ struct ip *p;
+ struct udphdr *u;
+
+ ATF_REQUIRE(la != NULL);
+ LibAliasSetMode(la, 0, ~0);
+ LibAliasSetMode(la, PKT_ALIAS_RESET_ON_ADDR_CHANGE, ~0);
+ LibAliasSetMode(la, PKT_ALIAS_DENY_INCOMING, PKT_ALIAS_DENY_INCOMING);
+
+ pf = LibAliasRedirectPort(la, prv1, ntohs(0x1234), ANY_ADDR, 0, masq, ntohs(0xabcd), IPPROTO_UDP);
+ ATF_REQUIRE(pf != NULL);
+ /* only for fully specified links */
+ ATF_CHECK(-1 == LibAliasRedirectDynamic(la, pf));
+ LibAliasRedirectDelete(la, pf);
+
+ pf = LibAliasRedirectPort(la, prv1, ntohs(0x1234), ext, ntohs(0x5678), masq, ntohs(0xabcd), IPPROTO_UDP);
+ ATF_REQUIRE(pf != NULL);
+ ATF_CHECK(0 == LibAliasRedirectDynamic(la, pf));
+
+ p = ip_packet(0, 64);
+ UDP_UNNAT_CHECK(p, u, ext, 0x5678, masq, 0xabcd, prv1, 0x1234);
+
+ /* clear table by keeping the address */
+ LibAliasSetAddress(la, ext);
+ LibAliasSetAddress(la, masq);
+
+ /* does not work anymore */
+ UDP_UNNAT_FAIL(p, u, ext, 0x5678, masq, 0xabcd);
+
+ free(p);
+ LibAliasUninit(la);
+}
+
+ATF_TP_ADD_TCS(natin)
+{
+ /* Use "dd if=/dev/random bs=2 count=1 | od -x" to reproduce */
+ srand(0xe859);
+
+ ATF_TP_ADD_TC(natin, 1_portforward);
+ ATF_TP_ADD_TC(natin, 2_portoverlap);
+ ATF_TP_ADD_TC(natin, 3_redirectany);
+ ATF_TP_ADD_TC(natin, 4_redirectaddr);
+ ATF_TP_ADD_TC(natin, 5_lsnat);
+ ATF_TP_ADD_TC(natin, 6_oneshot);
+
+ return atf_no_error();
+}
diff --git a/tests/sys/netinet/libalias/Makefile b/tests/sys/netinet/libalias/Makefile
new file mode 100644
index 000000000000..43bef996fae7
--- /dev/null
+++ b/tests/sys/netinet/libalias/Makefile
@@ -0,0 +1,31 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/netinet/libalias
+BINDIR= ${TESTSDIR}
+
+ATF_TESTS_C+= 1_instance \
+ 2_natout \
+ 3_natin \
+
+PROGS+= perf
+
+LIBADD+= alias
+
+SRCS.1_instance=1_instance.c util.c
+SRCS.2_natout= 2_natout.c util.c
+SRCS.3_natin= 3_natin.c util.c
+SRCS.perf= perf.c util.c
+
+.include <bsd.test.mk>
+
+#
+# Testing during development
+#
+test: all
+ cd ${.OBJDIR}; kyua test
+
+report:
+ cd ${.OBJDIR}; kyua report
+
+report-v:
+ cd ${.OBJDIR}; kyua report --verbose
diff --git a/tests/sys/netinet/libalias/perf.c b/tests/sys/netinet/libalias/perf.c
new file mode 100644
index 000000000000..07e73612a6dd
--- /dev/null
+++ b/tests/sys/netinet/libalias/perf.c
@@ -0,0 +1,308 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright 2021 Lutz Donnerhacke
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <sys/time.h>
+#include "util.h"
+#include <alias.h>
+
+static void usage(void) __dead2;
+
+#define timevalcmp(tv, uv, cmp) \
+ (((tv).tv_sec == (uv).tv_sec) \
+ ? ((tv).tv_usec cmp (uv).tv_usec) \
+ : ((tv).tv_sec cmp (uv).tv_sec))
+
+#define timevaldiff(n, o) (float) \
+ (((n).tv_sec - (o).tv_sec)*1000000l + \
+ ((n).tv_usec - (o).tv_usec))
+
+#define check_timeout() do { \
+ if (check_timeout_cnt++ > 1000) { \
+ check_timeout_cnt = 0; \
+ gettimeofday(&now, NULL); \
+ if (timevalcmp(now, timeout, >=)) \
+ goto out; \
+ } } while(0)
+
+static void
+usage(void) {
+ printf("Usage: perf [max_seconds [batch_size [random_size [attack_size [redir_size]]]]]\n");
+ exit(1);
+}
+
+int main(int argc, char ** argv)
+{
+ struct libalias *la;
+ struct timeval timeout, now, start;
+ struct ip *p;
+ struct udphdr *u;
+ struct {
+ struct in_addr src, dst;
+ uint16_t sport, dport, aport;
+ } *batch;
+ struct {
+ unsigned long ok, fail;
+ } nat, usenat, unnat, random, attack;
+ int i, round, check_timeout_cnt = 0;
+ int max_seconds = 90, batch_size = 2000,
+ random_size = 1000, attack_size = 1000,
+ redir_size = 2000;
+
+ if (argc >= 2) {
+ char * end;
+
+ max_seconds = strtol(argv[1], &end, 10);
+ if (max_seconds < 2 || end[0] != '\0')
+ usage();
+ }
+ if (argc > 2 && (batch_size = atoi(argv[2])) < 0) usage();
+ if (argc > 3 && (random_size = atoi(argv[3])) < 0) usage();
+ if (argc > 4 && (attack_size = atoi(argv[4])) < 0) usage();
+ if (argc > 5 && (redir_size = atoi(argv[5])) < 0) usage();
+
+ printf("Running perfomance test with parameters:\n");
+ printf(" Maximum Runtime (max_seconds) = %d\n", max_seconds);
+ printf(" Amount of valid connections (batch_size) = %d\n", batch_size);
+ printf(" Amount of random, incoming packets (batch_size) = %d\n", random_size);
+ printf(" Repeat count of a random, incoming packet (attack_size) = %d\n", attack_size);
+ printf(" Amount of open port forwardings (redir_size) = %d\n", redir_size);
+ printf("\n");
+
+ if (NULL == (la = LibAliasInit(NULL))) {
+ perror("LibAliasInit");
+ return -1;
+ }
+
+ bzero(&nat, sizeof(nat));
+ bzero(&usenat, sizeof(usenat));
+ bzero(&unnat, sizeof(unnat));
+ bzero(&random, sizeof(random));
+ bzero(&attack, sizeof(attack));
+
+ LibAliasSetAddress(la, masq);
+ LibAliasSetMode(la, PKT_ALIAS_SAME_PORTS | PKT_ALIAS_DENY_INCOMING, ~0);
+
+ prv1.s_addr &= htonl(0xffff0000);
+ ext.s_addr &= htonl(0xffff0000);
+
+ for (i = 0; i < redir_size; i++) {
+ int aport = htons(rand_range(1000, 2000));
+ int sport = htons(rand_range(1000, 2000));
+
+ prv2.s_addr &= htonl(0xffff0000);
+ prv2.s_addr |= rand_range(0, 0xffff);
+ LibAliasRedirectPort(la, prv2, sport, ANY_ADDR, 0, masq, aport, IPPROTO_UDP);
+ }
+
+ p = ip_packet(0, 64);
+ u = set_udp(p, 0, 0);
+
+ if (NULL == (batch = calloc(batch_size, sizeof(*batch)))) {
+ perror("calloc(batch)");
+ return -1;
+ }
+
+ gettimeofday(&timeout, NULL);
+ timeout.tv_sec += max_seconds;
+
+ printf("RND SECOND newNAT RANDOM ATTACK useNAT\n");
+ for (round = 0; ; round++) {
+ int res, cnt;
+
+ printf("%3d ", round+1);
+
+ gettimeofday(&start, NULL);
+ printf("%6.1f ", max_seconds - timevaldiff(timeout, start)/1000000.0f);
+ for (cnt = i = 0; i < batch_size; i++, cnt++) {
+ batch[i].src.s_addr = prv1.s_addr | htonl(rand_range(0, 0xffff));
+ batch[i].dst.s_addr = ext.s_addr | htonl(rand_range(0, 0xffff));
+ batch[i].sport = rand_range(1000, 60000);
+ batch[i].dport = rand_range(1000, 60000);
+
+ p->ip_src = batch[i].src;
+ p->ip_dst = batch[i].dst;
+ u = set_udp(p, batch[i].sport, batch[i].dport);
+
+ res = LibAliasOut(la, p, 64);
+ batch[i].aport = htons(u->uh_sport);
+
+ if (res == PKT_ALIAS_OK &&
+ u->uh_dport == htons(batch[i].dport) &&
+ addr_eq(p->ip_dst, batch[i].dst) &&
+ addr_eq(p->ip_src, masq))
+ nat.ok++;
+ else
+ nat.fail++;
+
+ check_timeout();
+ }
+ gettimeofday(&now, NULL);
+ if (cnt > 0)
+ printf("%6.2f ", timevaldiff(now, start) / cnt);
+ else
+ printf("------ ");
+
+ start = now;
+ for (cnt = i = 0; i < random_size; i++, cnt++) {
+ p->ip_src.s_addr = ext.s_addr & htonl(0xfff00000);
+ p->ip_src.s_addr |= htonl(rand_range(0, 0xffff));
+ p->ip_dst = masq;
+ u = set_udp(p, rand_range(1, 0xffff), rand_range(1, 0xffff));
+
+ res = LibAliasIn(la, p, 64);
+
+ if (res == PKT_ALIAS_OK)
+ random.ok++;
+ else
+ random.fail++;
+
+ check_timeout();
+ }
+ gettimeofday(&now, NULL);
+ if (cnt > 0)
+ printf("%6.2f ", timevaldiff(now, start) / cnt);
+ else
+ printf("------ ");
+
+ start = now;
+ p->ip_src.s_addr = ext.s_addr & htonl(0xfff00000);
+ p->ip_src.s_addr |= htonl(rand_range(0, 0xffff));
+ p->ip_dst = masq;
+ u = set_udp(p, rand_range(1, 0xffff), rand_range(1, 0xffff));
+ for (cnt = i = 0; i < attack_size; i++, cnt++) {
+ res = LibAliasIn(la, p, 64);
+
+ if (res == PKT_ALIAS_OK)
+ attack.ok++;
+ else
+ attack.fail++;
+
+ check_timeout();
+ }
+ gettimeofday(&now, NULL);
+ if (cnt > 0)
+ printf("%6.2f ", timevaldiff(now, start) / cnt);
+ else
+ printf("------ ");
+
+ qsort(batch, batch_size, sizeof(*batch), randcmp);
+
+ gettimeofday(&start, NULL);
+ for (cnt = i = 0; i < batch_size; i++) {
+ int j;
+
+ /* random communication length */
+ for(j = rand_range(1, 150); j-- > 0; cnt++) {
+ int k;
+
+ /* a random flow out of rolling window */
+ k = rand_range(i, i + 25);
+ if (k >= batch_size)
+ k = i;
+
+ /* 10% outgoing, 90% incoming */
+ if (rand_range(0, 100) > 10) {
+ p->ip_src = batch[k].dst;
+ p->ip_dst = masq;
+ u = set_udp(p, batch[k].dport, batch[k].aport);
+
+ res = LibAliasIn(la, p, 64);
+ if (res == PKT_ALIAS_OK &&
+ u->uh_sport == htons(batch[k].dport) &&
+ u->uh_dport == htons(batch[k].sport) &&
+ addr_eq(p->ip_dst, batch[k].src) &&
+ addr_eq(p->ip_src, batch[k].dst))
+ unnat.ok++;
+ else
+ unnat.fail++;
+ } else {
+ p->ip_src = batch[k].src;
+ p->ip_dst = batch[k].dst;
+ u = set_udp(p, batch[k].sport, batch[k].dport);
+
+ res = LibAliasOut(la, p, 64);
+ if (res == PKT_ALIAS_OK &&
+ u->uh_sport == htons(batch[k].aport) &&
+ u->uh_dport == htons(batch[k].dport) &&
+ addr_eq(p->ip_dst, batch[k].dst) &&
+ addr_eq(p->ip_src, masq))
+ usenat.ok++;
+ else
+ usenat.fail++;
+ }
+ check_timeout();
+ }
+ }
+ gettimeofday(&now, NULL);
+ if (cnt > 0)
+ printf("%6.2f ", timevaldiff(now, start) / cnt);
+ else
+ printf("------ ");
+
+ printf("\n");
+ }
+out:
+ printf("\n\n");
+ free(batch);
+ free(p);
+
+ printf("Results\n");
+ printf(" Rounds : %9u\n", round);
+ printf("newNAT ok : %9lu\n", nat.ok);
+ printf("newNAT fail: %9lu\n", nat.fail);
+ printf("useNAT ok : %9lu (out)\n", usenat.ok);
+ printf("useNAT fail: %9lu (out)\n", usenat.fail);
+ printf("useNAT ok : %9lu (in)\n", unnat.ok);
+ printf("useNAT fail: %9lu (in)\n", unnat.fail);
+ printf("RANDOM ok : %9lu\n", random.ok);
+ printf("RANDOM fail: %9lu\n", random.fail);
+ printf("ATTACK ok : %9lu\n", attack.ok);
+ printf("ATTACK fail: %9lu\n", attack.fail);
+ printf(" ---------\n");
+ printf(" Total: %9lu\n",
+ nat.ok + nat.fail +
+ unnat.ok + unnat.fail +
+ usenat.ok + usenat.fail +
+ random.ok + random.fail +
+ attack.ok + attack.fail);
+
+ gettimeofday(&start, NULL);
+ printf("\n Cleanup : ");
+ LibAliasUninit(la);
+ gettimeofday(&now, NULL);
+ printf("%.2fs\n", timevaldiff(now, start)/1000000l);
+ return (0);
+}
diff --git a/tests/sys/netinet/libalias/util.c b/tests/sys/netinet/libalias/util.c
new file mode 100644
index 000000000000..8ceb8355c8ff
--- /dev/null
+++ b/tests/sys/netinet/libalias/util.c
@@ -0,0 +1,123 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright 2021 Lutz Donnerhacke
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <netinet/in.h>
+
+#include "util.h"
+
+/* common ip ranges */
+struct in_addr masq = { htonl(0x01020304) };
+struct in_addr pub = { htonl(0x0102dead) };
+struct in_addr pub2 = { htonl(0x0102beef) };
+struct in_addr prv1 = { htonl(0x0a00dead) };
+struct in_addr prv2 = { htonl(0xac10dead) };
+struct in_addr prv3 = { htonl(0xc0a8dead) };
+struct in_addr cgn = { htonl(0x6440dead) };
+struct in_addr ext = { htonl(0x12345678) };
+struct in_addr ANY_ADDR = { 0 };
+
+#define REQUIRE(x) do { \
+ if (!(x)) { \
+ fprintf(stderr, "Failed in %s %s:%d.\n",\
+ __FUNCTION__, __FILE__, __LINE__); \
+ exit(-1); \
+ } \
+} while(0)
+
+int
+randcmp(const void *a, const void *b)
+{
+ int res, r = rand();
+
+ (void)a;
+ (void)b;
+ res = (r/4 < RAND_MAX/9) ? 1
+ : (r/5 < RAND_MAX/9) ? 0
+ : -1;
+ return (res);
+}
+
+void
+hexdump(void *p, size_t len)
+{
+ size_t i;
+ unsigned char *c = p;
+
+ for (i = 0; i < len; i++) {
+ printf(" %02x", c[i]);
+ switch (i & 0xf) {
+ case 0xf: printf("\n"); break;
+ case 0x7: printf(" "); break;
+ default: break;
+ }
+ }
+ if ((i & 0xf) != 0x0)
+ printf("\n");
+}
+
+struct ip *
+ip_packet(u_char protocol, size_t len)
+{
+ struct ip * p;
+
+ REQUIRE(len >= 64 && len <= IP_MAXPACKET);
+
+ p = calloc(1, len);
+ REQUIRE(p != NULL);
+
+ p->ip_v = IPVERSION;
+ p->ip_hl = sizeof(*p)/4;
+ p->ip_len = htons(len);
+ p->ip_ttl = IPDEFTTL;
+ p->ip_p = protocol;
+ REQUIRE(p->ip_hl == 5);
+
+ return (p);
+}
+
+struct udphdr *
+set_udp(struct ip *p, u_short sport, u_short dport) {
+ int hlen = p->ip_hl << 2;
+ struct udphdr *u = (struct udphdr *)((uintptr_t)p + hlen);
+ int payload = ntohs(p->ip_len) - hlen;
+
+ REQUIRE(payload >= (int)sizeof(*u));
+ p->ip_p = IPPROTO_UDP;
+ u->uh_sport = htons(sport);
+ u->uh_dport = htons(dport);
+ u->uh_ulen = htons(payload);
+ return (u);
+}
diff --git a/tests/sys/netinet/libalias/util.h b/tests/sys/netinet/libalias/util.h
new file mode 100644
index 000000000000..f58a1ad26248
--- /dev/null
+++ b/tests/sys/netinet/libalias/util.h
@@ -0,0 +1,137 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright 2021 Lutz Donnerhacke
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/types.h>
+
+#include <arpa/inet.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+
+#ifndef _UTIL_H
+#define _UTIL_H
+
+/* common ip ranges */
+extern struct in_addr masq, pub, pub2, prv1, prv2, prv3, cgn, ext, ANY_ADDR;
+
+int randcmp(const void *a, const void *b);
+void hexdump(void *p, size_t len);
+struct ip * ip_packet(u_char protocol, size_t len);
+struct udphdr * set_udp(struct ip *p, u_short sport, u_short dport);
+
+static inline int
+addr_eq(struct in_addr a, struct in_addr b)
+{
+ return a.s_addr == b.s_addr;
+}
+
+#define a2h(a) ntohl(a.s_addr)
+
+static inline int
+rand_range(int min, int max)
+{
+ return min + rand()%(max - min);
+}
+
+#define NAT_CHECK(pip, src, dst, msq) do { \
+ int res; \
+ int len = ntohs(pip->ip_len); \
+ pip->ip_src = src; \
+ pip->ip_dst = dst; \
+ res = LibAliasOut(la, pip, len); \
+ ATF_CHECK_MSG(res == PKT_ALIAS_OK, \
+ ">%d< not met PKT_ALIAS_OK", res); \
+ ATF_CHECK(addr_eq(msq, pip->ip_src)); \
+ ATF_CHECK(addr_eq(dst, pip->ip_dst)); \
+} while(0)
+
+#define NAT_FAIL(pip, src, dst) do { \
+ int res; \
+ int len = ntohs(pip->ip_len); \
+ pip->ip_src = src; \
+ pip->ip_dst = dst; \
+ res = LibAliasOut(la, pip, len); \
+ ATF_CHECK_MSG(res != PKT_ALIAS_OK, \
+ ">%d< not met !PKT_ALIAS_OK", res); \
+ ATF_CHECK(addr_eq(src, pip->ip_src)); \
+ ATF_CHECK(addr_eq(dst, pip->ip_dst)); \
+} while(0)
+
+#define UNNAT_CHECK(pip, src, dst, rel) do { \
+ int res; \
+ int len = ntohs(pip->ip_len); \
+ pip->ip_src = src; \
+ pip->ip_dst = dst; \
+ res = LibAliasIn(la, pip, len); \
+ ATF_CHECK_MSG(res == PKT_ALIAS_OK, \
+ ">%d< not met PKT_ALIAS_OK", res); \
+ ATF_CHECK(addr_eq(src, pip->ip_src)); \
+ ATF_CHECK(addr_eq(rel, pip->ip_dst)); \
+} while(0)
+
+#define UNNAT_FAIL(pip, src, dst) do { \
+ int res; \
+ int len = ntohs(pip->ip_len); \
+ pip->ip_src = src; \
+ pip->ip_dst = dst; \
+ res = LibAliasIn(la, pip, len); \
+ ATF_CHECK_MSG(res != PKT_ALIAS_OK, \
+ ">%d< not met !PKT_ALIAS_OK", res); \
+ ATF_CHECK(addr_eq(src, pip->ip_src)); \
+ ATF_CHECK(addr_eq(dst, pip->ip_dst)); \
+} while(0)
+
+#define UDP_NAT_CHECK(p, u, si, sp, di, dp, mi) do { \
+ u = set_udp(p, (sp), (dp)); \
+ NAT_CHECK(p, (si), (di), (mi)); \
+ ATF_CHECK(u->uh_dport == htons(dp)); \
+} while(0)
+
+#define UDP_NAT_FAIL(p, u, si, sp, di, dp) do { \
+ u = set_udp(p, (sp), (dp)); \
+ NAT_FAIL(p, (si), (di)); \
+} while(0)
+
+#define UDP_UNNAT_CHECK(p, u, si, sp, mi, mp, di, dp) \
+do { \
+ u = set_udp(p, (sp), (mp)); \
+ UNNAT_CHECK(p, (si), (mi), (di)); \
+ ATF_CHECK(u->uh_sport == htons(sp)); \
+ ATF_CHECK(u->uh_dport == htons(dp)); \
+} while(0)
+
+#define UDP_UNNAT_FAIL(p, u, si, sp, mi, mp) do { \
+ u = set_udp(p, (sp), (mp)); \
+ UNNAT_FAIL(p, (si), (mi)); \
+} while(0)
+
+#endif /* _UTIL_H */
diff --git a/tests/sys/netinet/lpm.sh b/tests/sys/netinet/lpm.sh
new file mode 100755
index 000000000000..1cc377f91d9b
--- /dev/null
+++ b/tests/sys/netinet/lpm.sh
@@ -0,0 +1,178 @@
+#!/usr/bin/env atf-sh
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Alexander V. Chernikov
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+setup_networking()
+{
+ jname="$1"
+ lo_dst="$2"
+ epair0="$3"
+ epair1="$4"
+
+ vnet_mkjail ${jname}a ${epair0}a ${epair1}a
+ # Setup transit IPv4 networks
+ jexec ${jname}a ifconfig ${epair0}a up
+ jexec ${jname}a ifconfig ${epair0}a inet 203.0.113.1/30
+ jexec ${jname}a ifconfig ${epair1}a up
+ jexec ${jname}a ifconfig ${epair1}a inet 203.0.113.5/30
+
+ vnet_mkjail ${jname}b ${epair0}b ${epair1}b ${lo_dst}
+ jexec ${jname}b ifconfig ${epair0}b up
+ jexec ${jname}b ifconfig ${epair0}b inet 203.0.113.2/30
+ jexec ${jname}b ifconfig ${epair1}b up
+ jexec ${jname}b ifconfig ${epair1}b inet 203.0.113.6/30
+ jexec ${jname}b ifconfig ${lo_dst} up
+
+}
+
+atf_test_case "lpm_test1_success" "cleanup"
+lpm_test1_success_head()
+{
+
+ atf_set descr 'Test IPv4 LPM for /30 and /31'
+ atf_set require.user root
+}
+
+lpm_test1_success_body()
+{
+
+ vnet_init
+
+ jname="v4t-lpm_test1_success"
+
+ lo_dst=$(vnet_mkloopback)
+ epair0=$(vnet_mkepair)
+ epair1=$(vnet_mkepair)
+
+ setup_networking ${jname} ${lo_dst} ${epair0} ${epair1}
+
+ jexec ${jname}b ifconfig ${lo_dst} inet 198.51.100.0/32
+ jexec ${jname}b ifconfig ${lo_dst} alias 198.51.100.2/32
+
+ # Add routes
+ # A -> towards B via epair0a
+ jexec ${jname}a route add -4 -net 198.51.100.0/30 203.0.113.2
+ # A -> towards B via epair1a
+ jexec ${jname}a route add -4 -net 198.51.100.0/31 203.0.113.6
+
+ count=20
+ valid_message="${count} packets transmitted, ${count} packets received"
+
+ # Check that 198.51.100.0 goes via epair1
+ atf_check -o match:"${valid_message}" jexec ${jname}a ping -f -nc${count} 198.51.100.0
+ pkt_0=`jexec ${jname}a netstat -Wf link -I ${epair0}a | head | awk '$1!~/^Name/{print$8}'`
+ pkt_1=`jexec ${jname}a netstat -Wf link -I ${epair1}a | head | awk '$1!~/^Name/{print$8}'`
+ if [ ${pkt_1} -le ${count} ]; then
+ echo "LPM failure: 1: ${pkt_0} 2: ${pkt_1} (should be ${count})"
+ exit 1
+ fi
+
+ # Check that 198.51.100.2 goes via epair0
+ atf_check -o match:"${valid_message}" jexec ${jname}a ping -f -nc${count} 198.51.100.2
+ pkt_0=`jexec ${jname}a netstat -Wf link -I ${epair0}a | head | awk '$1!~/^Name/{print$8}'`
+ pkt_1=`jexec ${jname}a netstat -Wf link -I ${epair1}a | head | awk '$1!~/^Name/{print$8}'`
+ if [ ${pkt_0} -le ${count} ]; then
+ echo "LPM failure: 1: ${pkt_0} (should be ${count}) 2: ${pkt_1}"
+ exit 1
+ fi
+
+ echo "RAW BALANCING: 1: ${pkt_0} 2: ${pkt_1}"
+}
+
+lpm_test1_success_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "lpm_test2_success" "cleanup"
+lpm_test2_success_head()
+{
+
+ atf_set descr 'Test IPv4 LPM for the host routes'
+ atf_set require.user root
+}
+
+lpm_test2_success_body()
+{
+
+ vnet_init
+
+ jname="v4t-lpm_test2_success"
+
+ lo_dst=$(vnet_mkloopback)
+ epair0=$(vnet_mkepair)
+ epair1=$(vnet_mkepair)
+
+ setup_networking ${jname} ${lo_dst} ${epair0} ${epair1}
+
+ jexec ${jname}b ifconfig ${lo_dst} inet 198.51.100.0/32
+ jexec ${jname}b ifconfig ${lo_dst} alias 198.51.100.1/32
+
+ # Add routes
+ # A -> towards B via epair0a
+ jexec ${jname}a route add -4 -host 198.51.100.0 203.0.113.2
+ # A -> towards B via epair1a
+ jexec ${jname}a route add -4 -host 198.51.100.1 203.0.113.6
+
+ count=20
+ valid_message="${count} packets transmitted, ${count} packets received"
+
+ # Check that 198.51.100.0 goes via epair0
+ atf_check -o match:"${valid_message}" jexec ${jname}a ping -f -nc${count} 198.51.100.0
+ pkt_0=`jexec ${jname}a netstat -Wf link -I ${epair0}a | head | awk '$1!~/^Name/{print$8}'`
+ pkt_1=`jexec ${jname}a netstat -Wf link -I ${epair1}a | head | awk '$1!~/^Name/{print$8}'`
+ if [ ${pkt_0} -le ${count} ]; then
+ echo "LPM failure: 1: ${pkt_0} (should be ${count}) 2: ${pkt_1}"
+ exit 1
+ fi
+
+ # Check that 198.51.100.1 goes via epair1
+ atf_check -o match:"${valid_message}" jexec ${jname}a ping -f -nc${count} 198.51.100.1
+ pkt_0=`jexec ${jname}a netstat -Wf link -I ${epair0}a | head | awk '$1!~/^Name/{print$8}'`
+ pkt_1=`jexec ${jname}a netstat -Wf link -I ${epair1}a | head | awk '$1!~/^Name/{print$8}'`
+ if [ ${pkt_1} -le ${count} ]; then
+ echo "LPM failure: 1: ${pkt_0} 2: ${pkt_1} (should be ${count})"
+ exit 1
+ fi
+
+ echo "RAW BALANCING: 1: ${pkt_0} 2: ${pkt_1}"
+}
+
+lpm_test2_success_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "lpm_test1_success"
+ atf_add_test_case "lpm_test2_success"
+}
+
diff --git a/tests/sys/netinet/multicast.sh b/tests/sys/netinet/multicast.sh
new file mode 100644
index 000000000000..eb2b962dac70
--- /dev/null
+++ b/tests/sys/netinet/multicast.sh
@@ -0,0 +1,61 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2025 Gleb Smirnoff <glebius@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+# See regression fixed in baad45c9c12028964acd0b58096f3aaa0fb22859
+atf_test_case "IP_MULTICAST_IF" "cleanup"
+IP_MULTICAST_IF_head()
+{
+ atf_set descr \
+ 'sendto() for IP_MULTICAST_IF socket does not do routing lookup'
+ atf_set require.user root
+
+}
+
+IP_MULTICAST_IF_body()
+{
+ local epair mjail
+
+ vnet_init
+ # The test doesn't use our half of epair
+ epair=$(vnet_mkepair)
+ vnet_mkjail mjail ${epair}a
+ jexec mjail ifconfig ${epair}a up
+ jexec mjail ifconfig ${epair}a 192.0.2.1/24
+ atf_check -s exit:0 -o empty \
+ jexec mjail $(atf_get_srcdir)/sendto-IP_MULTICAST_IF 192.0.2.1
+}
+
+IP_MULTICAST_IF_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "IP_MULTICAST_IF"
+}
diff --git a/tests/sys/netinet/output.sh b/tests/sys/netinet/output.sh
new file mode 100755
index 000000000000..23d427605878
--- /dev/null
+++ b/tests/sys/netinet/output.sh
@@ -0,0 +1,593 @@
+#!/usr/bin/env atf-sh
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Alexander V. Chernikov
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+atf_test_case "output_tcp_setup_success" "cleanup"
+output_tcp_setup_success_head()
+{
+
+ atf_set descr 'Test valid IPv4 TCP output'
+ atf_set require.user root
+}
+
+output_tcp_setup_success_body()
+{
+
+ vnet_init
+
+ net_src="192.0.2."
+ net_dst="192.0.2."
+ ip_src="${net_src}1"
+ ip_dst="${net_dst}2"
+ plen=24
+ text="testtesttst"
+ port=4242
+
+ script_name=`dirname $0`/../common/net_receiver.py
+ script_name=`realpath ${script_name}`
+ jname="v4t-output_tcp_setup_success"
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail ${jname}a ${epair}a
+ jexec ${jname}a ifconfig ${epair}a up
+ jexec ${jname}a ifconfig ${epair}a inet ${ip_src}/${plen}
+
+ vnet_mkjail ${jname}b ${epair}b
+ jexec ${jname}b ifconfig ${epair}b up
+
+ jexec ${jname}b ifconfig ${epair}b inet ${ip_dst}/${plen}
+
+ # run listener
+ args="--family inet --ports ${port} --match_str ${text}"
+ echo jexec ${jname}b ${script_name} ${args}
+ jexec ${jname}b ${script_name} --test_name "test_listen_tcp" ${args} &
+ cmd_pid=$!
+
+ # wait for the script init
+ counter=0
+ while [ `jexec ${jname}b sockstat -4qlp ${port} | wc -l` != "1" ]; do
+ sleep 0.01
+ counter=$((counter+1))
+ if [ ${counter} -ge 50 ]; then break; fi
+ done
+ if [ `jexec ${jname}b sockstat -4qlp ${port} | wc -l` != "1" ]; then
+ echo "App setup failed"
+ exit 1
+ fi
+
+ # run sender
+ echo -n "${text}" | jexec ${jname}a nc -N ${ip_dst} ${port}
+ exit_code=$?
+ if [ $exit_code -ne 0 ]; then atf_fail "sender exit code $exit_code" ; fi
+
+ wait ${cmd_pid}
+ exit_code=$?
+ if [ $exit_code -ne 0 ]; then atf_fail "receiver exit code $exit_code" ; fi
+}
+
+output_tcp_setup_success_cleanup()
+{
+ vnet_cleanup
+}
+
+
+atf_test_case "output_udp_setup_success" "cleanup"
+output_udp_setup_success_head()
+{
+
+ atf_set descr 'Test valid IPv4 UDP output'
+ atf_set require.user root
+}
+
+output_udp_setup_success_body()
+{
+
+ vnet_init
+
+ net_src="192.0.2."
+ net_dst="192.0.2."
+ ip_src="${net_src}1"
+ ip_dst="${net_dst}2"
+ plen=24
+ text="testtesttst"
+ port=4242
+
+ script_name=`dirname $0`/../common/net_receiver.py
+ script_name=`realpath ${script_name}`
+ jname="v4t-output_udp_setup_success"
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail ${jname}a ${epair}a
+ jexec ${jname}a ifconfig ${epair}a up
+ jexec ${jname}a ifconfig ${epair}a inet ${ip_src}/${plen}
+
+ vnet_mkjail ${jname}b ${epair}b
+ jexec ${jname}b ifconfig ${epair}b up
+ jexec ${jname}b ifconfig ${epair}b inet ${ip_dst}/${plen}
+
+ # run listener
+ args="--family inet --ports ${port} --match_str ${text}"
+ echo jexec ${jname}b ${script_name} ${args}
+ jexec ${jname}b ${script_name} --test_name "test_listen_udp" ${args} &
+ cmd_pid=$!
+
+ # wait for the script init
+ counter=0
+ while [ `jexec ${jname}b sockstat -4qlp ${port} | wc -l` != "1" ]; do
+ sleep 0.1
+ counterc=$((counter+1))
+ if [ ${counter} -ge 50 ]; then break; fi
+ done
+ if [ `jexec ${jname}b sockstat -4qlp ${port} | wc -l` != "1" ]; then
+ echo "App setup failed"
+ exit 1
+ fi
+
+ # run sender
+ # TODO: switch from nc to some alternative to avoid 1-second delay
+ echo -n "${text}" | jexec ${jname}a nc -uNw1 ${ip_dst} ${port}
+ exit_code=$?
+ if [ $exit_code -ne 0 ]; then atf_fail "sender exit code $exit_code" ; fi
+
+ wait ${cmd_pid}
+ exit_code=$?
+ if [ $exit_code -ne 0 ]; then atf_fail "receiver exit code $exit_code" ; fi
+}
+
+output_udp_setup_success_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "output_raw_success" "cleanup"
+output_raw_success_head()
+{
+
+ atf_set descr 'Test valid IPv4 raw output'
+ atf_set require.user root
+}
+
+output_raw_success_body()
+{
+
+ vnet_init
+
+ net_src="192.0.2."
+ net_dst="192.0.2."
+ ip_src="${net_src}1"
+ ip_dst="${net_dst}2"
+ plen=24
+
+ script_name=`dirname $0`/../common/net_receiver.py
+ script_name=`realpath ${script_name}`
+ jname="v4t-output_raw_success"
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail ${jname}a ${epair}a
+ jexec ${jname}a ifconfig ${epair}a up
+ jexec ${jname}a ifconfig ${epair}a inet ${ip_src}/${plen}
+
+ vnet_mkjail ${jname}b ${epair}b
+ jexec ${jname}b ifconfig ${epair}b up
+
+ jexec ${jname}b ifconfig ${epair}b inet ${ip_dst}/${plen}
+
+ atf_check -o match:'1 packets transmitted, 1 packets received' jexec ${jname}a ping -nc1 ${ip_dst}
+}
+
+output_raw_success_cleanup()
+{
+ vnet_cleanup
+}
+
+# Multipath tests are done the following way:
+# epair0
+# jailA lo < > lo jailB
+# epair1
+# jailA has 2 routes towards /24 prefix on jailB loopback, via 2 epairs
+# jailB has 1 route towards /24 prefix on jailA loopback, via epair0
+#
+# jailA initiates connections/sends packets towards IPs on jailB loopback.
+# Script then compares amount of packets sent via epair0 and epair1
+
+mpath_check()
+{
+ if [ `sysctl -iW net.route.multipath | wc -l` != "1" ]; then
+ atf_skip "This test requires ROUTE_MPATH enabled"
+ fi
+}
+
+mpath_enable()
+{
+ jexec $1 sysctl net.route.multipath=1
+ if [ $? != 0 ]; then
+ atf_fail "Setting multipath in jail $1 failed".
+ fi
+}
+
+atf_test_case "output_tcp_flowid_mpath_success" "cleanup"
+output_tcp_flowid_mpath_success_head()
+{
+
+ atf_set descr 'Test valid IPv4 TCP output flowid generation'
+ atf_set require.user root
+}
+
+output_tcp_flowid_mpath_success_body()
+{
+ vnet_init
+ mpath_check
+
+ net_src="192.0.2."
+ net_dst="198.51.100."
+ ip_src="${net_src}1"
+ ip_dst="${net_dst}1"
+ plen=24
+ text="testtesttst"
+
+ script_name=`dirname $0`/../common/net_receiver.py
+ script_name=`realpath ${script_name}`
+ jname="v4t-output_tcp_flowid_mpath_success"
+
+ epair0=$(vnet_mkepair)
+ epair1=$(vnet_mkepair)
+ lo_src=$(vnet_mkloopback)
+ lo_dst=$(vnet_mkloopback)
+
+ vnet_mkjail ${jname}a ${epair0}a ${epair1}a ${lo_src}
+ mpath_enable ${jname}a
+ # Setup transit IPv4 networks
+ jexec ${jname}a ifconfig ${epair0}a up
+ jexec ${jname}a ifconfig ${epair0}a inet 203.0.113.1/30
+ jexec ${jname}a ifconfig ${epair1}a up
+ jexec ${jname}a ifconfig ${epair1}a inet 203.0.113.5/30
+ jexec ${jname}a ifconfig ${lo_src} up
+
+ vnet_mkjail ${jname}b ${epair0}b ${epair1}b ${lo_dst}
+ jexec ${jname}b ifconfig ${epair0}b up
+ jexec ${jname}b ifconfig ${epair0}b inet 203.0.113.2/30
+ jexec ${jname}b ifconfig ${epair1}b up
+ jexec ${jname}b ifconfig ${epair1}b inet 203.0.113.6/30
+ jexec ${jname}b ifconfig ${lo_dst} up
+
+ # DST ips/ports to test
+ ips="4 29 48 53 55 61 71 80 84 87 90 91 119 131 137 153 154 158 162 169 169 171 176 187 197 228 233 235 236 237 245 251"
+ ports="53540 49743 43067 9131 16734 5150 14379 40292 20634 51302 3387 24387 9282 14275 42103 26881 42461 29520 45714 11096"
+
+ jexec ${jname}a ifconfig ${lo_src} inet ${ip_src}/32
+
+ jexec ${jname}b ifconfig ${lo_dst} inet ${ip_dst}/32
+ for i in ${ips}; do
+ jexec ${jname}b ifconfig ${lo_dst} alias ${net_dst}${i}/32
+ done
+
+ # Add routes
+ # A -> towards B via epair0a
+ jexec ${jname}a route add -4 -net ${net_dst}0/${plen} 203.0.113.2
+ # A -> towards B via epair1a
+ jexec ${jname}a route add -4 -net ${net_dst}0/${plen} 203.0.113.6
+
+ # B towards A via epair0b
+ jexec ${jname}b route add -4 -net ${net_src}0/${plen} 203.0.113.1
+
+ # Base setup verification
+ atf_check -o match:'1 packets transmitted, 1 packets received' jexec ${jname}a ping -nc1 ${ip_dst}
+
+ # run listener
+ num_ports=`echo ${ports} | wc -w`
+ num_ips=`echo ${ips} | wc -w`
+ count_examples=$((num_ports*num_ips))
+ listener_ports=`echo ${ports} | tr ' ' '\n' | sort -n | tr '\n' ',' | sed -e 's?,$??'`
+ args="--family inet --ports ${listener_ports} --count ${count_examples} --match_str ${text}"
+ echo jexec ${jname}b ${script_name} ${args}
+ jexec ${jname}b ${script_name} --test_name "test_listen_tcp" ${args} &
+ cmd_pid=$!
+
+ # wait for the app init
+ counter=0
+ init=0
+ while [ ${counter} -le 50 ]; do
+ _ports=`jexec ${jname}b sockstat -4ql | awk "\\\$3 == ${cmd_pid} {print \\\$6}"|awk -F: "{print \\\$2}" | sort -n | tr '\n' ','`
+ if [ "${_ports}" = "${listener_ports}," ]; then
+ init=1
+ break;
+ fi
+ done
+ if [ ${init} -eq 0 ]; then
+ jexec ${jname}b sockstat -6ql | awk "\$3 == ${cmd_pid}"
+ echo "App setup failed"
+ exit 1
+ fi
+ echo "App setup done"
+
+ # run sender
+ for _ip in ${ips}; do
+ ip="${net_dst}${_ip}"
+ for port in ${ports}; do
+ echo -n "${text}" | jexec ${jname}a nc -nN ${ip} ${port}
+ exit_code=$?
+ if [ $exit_code -ne 0 ]; then atf_fail "sender exit code $exit_code" ; fi
+ done
+ done
+
+ wait ${cmd_pid}
+ exit_code=$?
+ if [ $exit_code -ne 0 ]; then atf_fail "receiver exit code $exit_code" ; fi
+
+ pkt_0=`jexec ${jname}a netstat -Wf link -I ${epair0}a | head | awk '$1!~/^Name/{print$8}'`
+ pkt_1=`jexec ${jname}a netstat -Wf link -I ${epair1}a | head | awk '$1!~/^Name/{print$8}'`
+ if [ ${pkt_0} -le 10 ]; then
+ atf_fail "Balancing failure: 1: ${pkt_0} 2: ${pkt_1}"
+ fi
+ if [ ${pkt_1} -le 10 ]; then
+ atf_fail "Balancing failure: 1: ${pkt_0} 2: ${pkt_1}"
+ exit 1
+ fi
+ echo "TCP Balancing: 1: ${pkt_0} 2: ${pkt_1}"
+}
+
+output_tcp_flowid_mpath_success_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "output_udp_flowid_mpath_success" "cleanup"
+output_udp_flowid_mpath_success_head()
+{
+
+ atf_set descr 'Test valid IPv4 UDP output flowid generation'
+ atf_set require.user root
+}
+
+output_udp_flowid_mpath_success_body()
+{
+
+ vnet_init
+ mpath_check
+
+ # Note this test will spawn around ~100 nc processes
+
+ net_src="192.0.2."
+ net_dst="198.51.100."
+ ip_src="${net_src}1"
+ ip_dst="${net_dst}1"
+ plen=24
+ text="testtesttst"
+
+ script_name=`dirname $0`/../common/net_receiver.py
+ script_name=`realpath ${script_name}`
+ jname="v4t-output_udp_flowid_mpath_success"
+
+ epair0=$(vnet_mkepair)
+ epair1=$(vnet_mkepair)
+ lo_src=$(vnet_mkloopback)
+ lo_dst=$(vnet_mkloopback)
+
+ vnet_mkjail ${jname}a ${epair0}a ${epair1}a ${lo_src}
+ mpath_enable ${jname}a
+ # Setup transit IPv4 networks
+ jexec ${jname}a ifconfig ${epair0}a up
+ jexec ${jname}a ifconfig ${epair0}a inet 203.0.113.1/30
+ jexec ${jname}a ifconfig ${epair1}a up
+ jexec ${jname}a ifconfig ${epair1}a inet 203.0.113.5/30
+ jexec ${jname}a ifconfig ${lo_src} up
+
+ vnet_mkjail ${jname}b ${epair0}b ${epair1}b ${lo_dst}
+ jexec ${jname}b ifconfig ${epair0}b up
+ jexec ${jname}b ifconfig ${epair0}b inet 203.0.113.2/30
+ jexec ${jname}b ifconfig ${epair1}b up
+ jexec ${jname}b ifconfig ${epair1}b inet 203.0.113.6/30
+ jexec ${jname}b ifconfig ${lo_dst} up
+
+ # DST ips/ports to test
+ ips="4 29 48 53 55 61 71 80 84 87 90 91 119 131 137 153 154 158 162 169 169 171 176 187 197 228 233 235 236 237 245 251"
+ ports="53540 49743 43067 9131 16734 5150 14379 40292 20634 51302 3387 24387 9282 14275 42103 26881 42461 29520 45714 11096"
+
+ jexec ${jname}a ifconfig ${lo_src} inet ${ip_src}/32
+
+ jexec ${jname}b ifconfig ${lo_dst} inet ${ip_dst}/32
+ for i in ${ips}; do
+ jexec ${jname}b ifconfig ${lo_dst} alias ${net_dst}${i}/32
+ done
+
+ # Add routes
+ # A -> towards B via epair0a
+ jexec ${jname}a route add -4 -net ${net_dst}0/${plen} 203.0.113.2
+ # A -> towards B via epair1a
+ jexec ${jname}a route add -4 -net ${net_dst}0/${plen} 203.0.113.6
+
+ # B towards A via epair0b
+ jexec ${jname}b route add -4 -net ${net_src}0/${plen} 203.0.113.1
+
+ # Base setup verification
+ atf_check -o match:'1 packets transmitted, 1 packets received' jexec ${jname}a ping -nc1 ${ip_dst}
+
+ # run listener
+ num_ports=`echo ${ports} | wc -w`
+ num_ips=`echo ${ips} | wc -w`
+ count_examples=$((num_ports*num_ips))
+ listener_ports=`echo ${ports} | tr ' ' '\n' | sort -n | tr '\n' ',' | sed -e 's?,$??'`
+ args="--family inet --ports ${listener_ports} --count ${count_examples} --match_str ${text}"
+ echo jexec ${jname}b ${script_name} ${args}
+ jexec ${jname}b ${script_name} --test_name "test_listen_udp" ${args} &
+ cmd_pid=$!
+
+ # wait for the app init
+ counter=0
+ init=0
+ while [ ${counter} -le 50 ]; do
+ _ports=`jexec ${jname}b sockstat -4ql | awk "\\\$3 == ${cmd_pid} {print \\\$6}"|awk -F: "{print \\\$2}" | sort -n | tr '\n' ','`
+ if [ "${_ports}" = "${listener_ports}," ]; then
+ init=1
+ break;
+ fi
+ done
+ if [ ${init} -eq 0 ]; then
+ jexec ${jname}b sockstat -4ql | awk "\$3 == ${cmd_pid}"
+ echo "App setup failed"
+ exit 1
+ fi
+ echo "App setup done"
+
+ # run sender
+ for _ip in ${ips}; do
+ ip="${net_dst}${_ip}"
+ for port in ${ports}; do
+ # XXX: switch to something that allows immediate exit
+ echo -n "${text}" | jexec ${jname}a nc -nuNw1 ${ip} ${port} &
+ sleep 0.01
+ done
+ done
+
+ wait ${cmd_pid}
+ exit_code=$?
+ if [ $exit_code -ne 0 ]; then atf_fail "receiver exit code $exit_code" ; fi
+
+ pkt_0=`jexec ${jname}a netstat -Wf link -I ${epair0}a | head | awk '$1!~/^Name/{print$8}'`
+ pkt_1=`jexec ${jname}a netstat -Wf link -I ${epair1}a | head | awk '$1!~/^Name/{print$8}'`
+ if [ ${pkt_0} -le 10 ]; then
+ atf_fail "Balancing failure: 1: ${pkt_0} 2: ${pkt_1}"
+ fi
+ if [ ${pkt_1} -le 10 ]; then
+ atf_fail "Balancing failure: 1: ${pkt_0} 2: ${pkt_1}"
+ fi
+ echo "UDP BALANCING: 1: ${pkt_0} 2: ${pkt_1}"
+}
+
+output_udp_flowid_mpath_success_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "output_raw_flowid_mpath_success" "cleanup"
+output_raw_flowid_mpath_success_head()
+{
+
+ atf_set descr 'Test valid IPv4 raw output flowid generation'
+ atf_set require.user root
+}
+
+output_raw_flowid_mpath_success_body()
+{
+
+ vnet_init
+ mpath_check
+
+ net_src="192.0.2."
+ net_dst="198.51.100."
+ ip_src="${net_src}1"
+ ip_dst="${net_dst}1"
+ plen=24
+ text="testtesttst"
+
+ jname="v4t-output_raw_flowid_mpath_success"
+
+ epair0=$(vnet_mkepair)
+ epair1=$(vnet_mkepair)
+ lo_src=$(vnet_mkloopback)
+ lo_dst=$(vnet_mkloopback)
+
+ vnet_mkjail ${jname}a ${epair0}a ${epair1}a ${lo_src}
+ mpath_enable ${jname}a
+ # Setup transit IPv4 networks
+ jexec ${jname}a ifconfig ${epair0}a up
+ jexec ${jname}a ifconfig ${epair0}a inet 203.0.113.1/30
+ jexec ${jname}a ifconfig ${epair1}a up
+ jexec ${jname}a ifconfig ${epair1}a inet 203.0.113.5/30
+ jexec ${jname}a ifconfig ${lo_src} up
+
+ vnet_mkjail ${jname}b ${epair0}b ${epair1}b ${lo_dst}
+ jexec ${jname}b ifconfig ${epair0}b up
+ jexec ${jname}b ifconfig ${epair0}b inet 203.0.113.2/30
+ jexec ${jname}b ifconfig ${epair1}b up
+ jexec ${jname}b ifconfig ${epair1}b inet 203.0.113.6/30
+ jexec ${jname}b ifconfig ${lo_dst} up
+
+ # DST ips/ports to test
+ ips="4 29 48 53 55 61 71 80 84 87 90 91 119 131 137 153 154 158 162 169 169 171 176 187 197 228 233 235 236 237 245 251"
+
+ jexec ${jname}a ifconfig ${lo_src} inet ${ip_src}/32
+
+ jexec ${jname}b ifconfig ${lo_dst} inet ${ip_dst}/32
+ for i in ${ips}; do
+ jexec ${jname}b ifconfig ${lo_dst} alias ${net_dst}${i}/32
+ done
+
+ # Add routes
+ # A -> towards B via epair0a
+ jexec ${jname}a route add -4 -net ${net_dst}0/${plen} 203.0.113.2
+ # A -> towards B via epair1a
+ jexec ${jname}a route add -4 -net ${net_dst}0/${plen} 203.0.113.6
+
+ # B towards A via epair0b
+ jexec ${jname}b route add -4 -net ${net_src}0/${plen} 203.0.113.1
+
+ # Base setup verification
+ atf_check -o match:'1 packets transmitted, 1 packets received' jexec ${jname}a ping -nc1 ${ip_dst}
+
+ # run sender
+ valid_message='1 packets transmitted, 1 packets received'
+ for _ip in ${ips}; do
+ ip="${net_dst}${_ip}"
+ atf_check -o match:"${valid_message}" jexec ${jname}a ping -nc1 ${ip}
+ done
+
+ pkt_0=`jexec ${jname}a netstat -Wf link -I ${epair0}a | head | awk '$1!~/^Name/{print$8}'`
+ pkt_1=`jexec ${jname}a netstat -Wf link -I ${epair1}a | head | awk '$1!~/^Name/{print$8}'`
+
+ jexec ${jname}a netstat -bWf link -I ${epair0}a
+ jexec ${jname}a netstat -bWf link -I ${epair1}a
+ if [ ${pkt_0} -le 10 ]; then
+ atf_fail "Balancing failure: 1: ${pkt_0} 2: ${pkt_1}"
+ fi
+ if [ ${pkt_1} -le 10 ]; then
+ atf_fail "Balancing failure: 1: ${pkt_0} 2: ${pkt_1}"
+ fi
+ echo "RAW BALANCING: 1: ${pkt_0} 2: ${pkt_1}"
+}
+
+output_raw_flowid_mpath_success_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "output_tcp_setup_success"
+ atf_add_test_case "output_udp_setup_success"
+ atf_add_test_case "output_raw_success"
+ atf_add_test_case "output_tcp_flowid_mpath_success"
+ atf_add_test_case "output_udp_flowid_mpath_success"
+ atf_add_test_case "output_raw_flowid_mpath_success"
+}
+
diff --git a/tests/sys/netinet/redirect.py b/tests/sys/netinet/redirect.py
new file mode 100755
index 000000000000..2085d72fde2b
--- /dev/null
+++ b/tests/sys/netinet/redirect.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+# -
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Alexander V. Chernikov
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sc
+import socket
+import sys
+import fcntl
+import struct
+
+
+def parse_args():
+ parser = argparse.ArgumentParser(description='ICMP redirect generator')
+ parser.add_argument('--smac', type=str, required=True,
+ help='eth source mac')
+ parser.add_argument('--dmac', type=str, required=True,
+ help='eth dest mac')
+ parser.add_argument('--sip', type=str, required=True,
+ help='remote router source ip')
+ parser.add_argument('--dip', type=str, required=True,
+ help='local router ip')
+ parser.add_argument('--iface', type=str, required=True,
+ help='ifname to send packet to')
+ parser.add_argument('--route', type=str, required=True,
+ help='destination IP to redirect')
+ parser.add_argument('--gw', type=str, required=True,
+ help='redirect GW')
+ return parser.parse_args()
+
+
+def construct_icmp_redirect(smac, dmac, sip, dip, route_dst, route_gw):
+ e = sc.Ether(src=smac, dst=dmac)
+ l3 = sc.IP(src=sip, dst=dip)
+ icmp = sc.ICMP(type=5, code=1, gw=route_gw)
+ orig_ip = sc.IP(src=sip, dst=route_dst)
+ return e / l3 / icmp / orig_ip / sc.UDP()
+
+
+def send_packet(pkt, iface, feedback=False):
+ if feedback:
+ # Make kernel receive the packet as well
+ BIOCFEEDBACK = 0x8004427c
+ socket = sc.conf.L2socket(iface=args.iface)
+ fcntl.ioctl(socket.ins, BIOCFEEDBACK, struct.pack('I', 1))
+ sc.sendp(pkt, socket=socket, verbose=True)
+ else:
+ sc.sendp(pkt, iface=iface, verbose=False)
+
+
+def main():
+ args = parse_args()
+ pkt = construct_icmp_redirect(args.smac, args.dmac, args.sip, args.dip,
+ args.route, args.gw)
+ send_packet(pkt, args.iface)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netinet/redirect.sh b/tests/sys/netinet/redirect.sh
new file mode 100755
index 000000000000..ad5b562da57a
--- /dev/null
+++ b/tests/sys/netinet/redirect.sh
@@ -0,0 +1,110 @@
+#!/usr/bin/env atf-sh
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Alexander V. Chernikov
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+atf_test_case "valid_redirect" "cleanup"
+valid_redirect_head() {
+
+ atf_set descr 'Test valid IPv4 redirect'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+valid_redirect_body() {
+
+ ids=65533
+ id=`printf "%x" ${ids}`
+ if [ $$ -gt 65535 ]; then
+ xl=`printf "%x" $(($$ - 65535))`
+ yl="1"
+ else
+ xl=`printf "%x" $$`
+ yl=""
+ fi
+
+ vnet_init
+
+ ip4a="192.0.2.1"
+ ip4b="192.0.2.2"
+
+ net4="198.51.100.0/24"
+ dst_addr4="198.51.100.42"
+
+ # remote_rtr
+ remote_rtr_ip="192.0.2.3"
+ remote_rtr_mac="00:00:5E:00:53:42"
+
+ new_rtr_ip="192.0.2.4"
+
+ script_name="redirect.py"
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a up
+ ifconfig ${epair}a inet ${ip4a}/24
+
+ jname="v4t-${id}-${yl}-${xl}"
+ vnet_mkjail ${jname} ${epair}b
+ jexec ${jname} ifconfig ${epair}b up
+ jexec ${jname} ifconfig ${epair}b inet ${ip4b}/24
+
+ # Setup static entry for the remote router
+ jexec ${jname} arp -s ${remote_rtr_ip} ${remote_rtr_mac}
+ # setup prefix reachable via router
+ jexec ${jname} route add -4 -net ${net4} ${remote_rtr_ip}
+
+ local_ip=${ip4b}
+ local_mac=`jexec ${jname} ifconfig ${epair}b ether | awk '$1~/ether/{print$2}'`
+
+ # echo "LOCAL: ${local_ip} ${local_mac}"
+ # echo "REMOTE: ${remote_rtr_ip} ${remote_rtr_mac}"
+
+ atf_check -s exit:0 $(atf_get_srcdir)/${script_name} \
+ --smac ${remote_rtr_mac} --dmac ${local_mac} \
+ --sip ${remote_rtr_ip} --dip ${local_ip} \
+ --route ${dst_addr4} --gw ${new_rtr_ip} \
+ --iface ${epair}a
+
+ atf_check -o match:"destination: ${dst_addr4}\$" jexec ${jname} route -n get -4 ${dst_addr4}
+ atf_check -o match:'flags: <UP,GATEWAY,HOST,DYNAMIC,DONE>' jexec ${jname} route -n get -4 ${dst_addr4}
+}
+
+valid_redirect_cleanup() {
+
+ vnet_cleanup
+}
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case "valid_redirect"
+}
+
+# end
+
diff --git a/tests/sys/netinet/sendto-IP_MULTICAST_IF.c b/tests/sys/netinet/sendto-IP_MULTICAST_IF.c
new file mode 100644
index 000000000000..d478e4da0b3b
--- /dev/null
+++ b/tests/sys/netinet/sendto-IP_MULTICAST_IF.c
@@ -0,0 +1,63 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Gleb Smirnoff <glebius@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <assert.h>
+#include <err.h>
+
+int
+main(int argc, char *argv[])
+{
+ struct sockaddr_in sin = {
+ .sin_family = AF_INET,
+ .sin_len = sizeof(struct sockaddr_in),
+ };
+ struct in_addr in;
+ int s, rv;
+
+ if (argc < 2)
+ errx(1, "Usage: %s IPv4-address", argv[0]);
+
+ if (inet_pton(AF_INET, argv[1], &in) != 1)
+ err(1, "inet_pton(%s) failed", argv[1]);
+
+ assert((s = socket(PF_INET, SOCK_DGRAM, 0)) > 0);
+ assert(bind(s, (struct sockaddr *)&sin, sizeof(sin)) == 0);
+ assert(setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &in, sizeof(in))
+ == 0);
+ /* RFC 6676 */
+ assert(inet_pton(AF_INET, "233.252.0.1", &sin.sin_addr) == 1);
+ sin.sin_port = htons(6676);
+ rv = sendto(s, &sin, sizeof(sin), 0,
+ (struct sockaddr *)&sin, sizeof(sin));
+ if (rv != sizeof(sin))
+ err(1, "sendto failed");
+
+ return (0);
+}
diff --git a/tests/sys/netinet/so_reuseport_lb_test.c b/tests/sys/netinet/so_reuseport_lb_test.c
new file mode 100644
index 000000000000..fa9d6e425884
--- /dev/null
+++ b/tests/sys/netinet/so_reuseport_lb_test.c
@@ -0,0 +1,566 @@
+/*-
+ * Copyright (c) 2018 The FreeBSD Foundation
+ *
+ * This software was developed by Mark Johnston under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/event.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#include <err.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdatomic.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+/*
+ * Given an array of non-blocking listening sockets configured in a LB group
+ * for "addr", try connecting to "addr" in a loop and verify that connections
+ * are roughly balanced across the sockets.
+ */
+static void
+lb_simple_accept_loop(int domain, const struct sockaddr *addr, int sds[],
+ size_t nsds, int nconns)
+{
+ size_t i;
+ int *acceptcnt;
+ int csd, error, excnt, sd;
+ const struct linger lopt = { 1, 0 };
+
+ /*
+ * We expect each listening socket to accept roughly nconns/nsds
+ * connections, but allow for some error.
+ */
+ excnt = nconns / nsds / 8;
+ acceptcnt = calloc(nsds, sizeof(*acceptcnt));
+ ATF_REQUIRE_MSG(acceptcnt != NULL, "calloc() failed: %s",
+ strerror(errno));
+
+ while (nconns-- > 0) {
+ sd = socket(domain, SOCK_STREAM, 0);
+ ATF_REQUIRE_MSG(sd >= 0, "socket() failed: %s",
+ strerror(errno));
+
+ error = connect(sd, addr, addr->sa_len);
+ ATF_REQUIRE_MSG(error == 0, "connect() failed: %s",
+ strerror(errno));
+
+ error = setsockopt(sd, SOL_SOCKET, SO_LINGER, &lopt, sizeof(lopt));
+ ATF_REQUIRE_MSG(error == 0, "Setting linger failed: %s",
+ strerror(errno));
+
+ /*
+ * Poll the listening sockets.
+ */
+ do {
+ for (i = 0; i < nsds; i++) {
+ csd = accept(sds[i], NULL, NULL);
+ if (csd < 0) {
+ ATF_REQUIRE_MSG(errno == EWOULDBLOCK ||
+ errno == EAGAIN,
+ "accept() failed: %s",
+ strerror(errno));
+ continue;
+ }
+
+ error = close(csd);
+ ATF_REQUIRE_MSG(error == 0,
+ "close() failed: %s", strerror(errno));
+
+ acceptcnt[i]++;
+ break;
+ }
+ } while (i == nsds);
+
+ error = close(sd);
+ ATF_REQUIRE_MSG(error == 0, "close() failed: %s",
+ strerror(errno));
+ }
+
+ for (i = 0; i < nsds; i++)
+ ATF_REQUIRE_MSG(acceptcnt[i] > excnt, "uneven balancing");
+}
+
+static int
+lb_listen_socket(int domain, int flags)
+{
+ int one;
+ int error, sd;
+
+ sd = socket(domain, SOCK_STREAM | flags, 0);
+ ATF_REQUIRE_MSG(sd >= 0, "socket() failed: %s", strerror(errno));
+
+ one = 1;
+ error = setsockopt(sd, SOL_SOCKET, SO_REUSEPORT_LB, &one, sizeof(one));
+ ATF_REQUIRE_MSG(error == 0, "setsockopt(SO_REUSEPORT_LB) failed: %s",
+ strerror(errno));
+
+ return (sd);
+}
+
+ATF_TC_WITHOUT_HEAD(basic_ipv4);
+ATF_TC_BODY(basic_ipv4, tc)
+{
+ struct sockaddr_in addr;
+ socklen_t slen;
+ size_t i;
+ const int nconns = 16384;
+ int error, sds[16];
+ uint16_t port;
+
+ sds[0] = lb_listen_socket(PF_INET, SOCK_NONBLOCK);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_len = sizeof(addr);
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(0);
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ error = bind(sds[0], (const struct sockaddr *)&addr, sizeof(addr));
+ ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
+ error = listen(sds[0], 1);
+ ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno));
+
+ slen = sizeof(addr);
+ error = getsockname(sds[0], (struct sockaddr *)&addr, &slen);
+ ATF_REQUIRE_MSG(error == 0, "getsockname() failed: %s",
+ strerror(errno));
+ ATF_REQUIRE_MSG(slen == sizeof(addr), "sockaddr size changed");
+ port = addr.sin_port;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_len = sizeof(addr);
+ addr.sin_family = AF_INET;
+ addr.sin_port = port;
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ for (i = 1; i < nitems(sds); i++) {
+ sds[i] = lb_listen_socket(PF_INET, SOCK_NONBLOCK);
+
+ error = bind(sds[i], (const struct sockaddr *)&addr,
+ sizeof(addr));
+ ATF_REQUIRE_MSG(error == 0, "bind() failed: %s",
+ strerror(errno));
+ error = listen(sds[i], 1);
+ ATF_REQUIRE_MSG(error == 0, "listen() failed: %s",
+ strerror(errno));
+ }
+
+ lb_simple_accept_loop(PF_INET, (struct sockaddr *)&addr, sds,
+ nitems(sds), nconns);
+ for (i = 0; i < nitems(sds); i++) {
+ error = close(sds[i]);
+ ATF_REQUIRE_MSG(error == 0, "close() failed: %s",
+ strerror(errno));
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(basic_ipv6);
+ATF_TC_BODY(basic_ipv6, tc)
+{
+ const struct in6_addr loopback6 = IN6ADDR_LOOPBACK_INIT;
+ struct sockaddr_in6 addr;
+ socklen_t slen;
+ size_t i;
+ const int nconns = 16384;
+ int error, sds[16];
+ uint16_t port;
+
+ sds[0] = lb_listen_socket(PF_INET6, SOCK_NONBLOCK);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin6_len = sizeof(addr);
+ addr.sin6_family = AF_INET6;
+ addr.sin6_port = htons(0);
+ addr.sin6_addr = loopback6;
+ error = bind(sds[0], (const struct sockaddr *)&addr, sizeof(addr));
+ ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
+ error = listen(sds[0], 1);
+ ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno));
+
+ slen = sizeof(addr);
+ error = getsockname(sds[0], (struct sockaddr *)&addr, &slen);
+ ATF_REQUIRE_MSG(error == 0, "getsockname() failed: %s",
+ strerror(errno));
+ ATF_REQUIRE_MSG(slen == sizeof(addr), "sockaddr size changed");
+ port = addr.sin6_port;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin6_len = sizeof(addr);
+ addr.sin6_family = AF_INET6;
+ addr.sin6_port = port;
+ addr.sin6_addr = loopback6;
+ for (i = 1; i < nitems(sds); i++) {
+ sds[i] = lb_listen_socket(PF_INET6, SOCK_NONBLOCK);
+
+ error = bind(sds[i], (const struct sockaddr *)&addr,
+ sizeof(addr));
+ ATF_REQUIRE_MSG(error == 0, "bind() failed: %s",
+ strerror(errno));
+ error = listen(sds[i], 1);
+ ATF_REQUIRE_MSG(error == 0, "listen() failed: %s",
+ strerror(errno));
+ }
+
+ lb_simple_accept_loop(PF_INET6, (struct sockaddr *)&addr, sds,
+ nitems(sds), nconns);
+ for (i = 0; i < nitems(sds); i++) {
+ error = close(sds[i]);
+ ATF_REQUIRE_MSG(error == 0, "close() failed: %s",
+ strerror(errno));
+ }
+}
+
+struct concurrent_add_softc {
+ struct sockaddr_storage ss;
+ int socks[128];
+ int kq;
+};
+
+static void *
+listener(void *arg)
+{
+ for (struct concurrent_add_softc *sc = arg;;) {
+ struct kevent kev;
+ ssize_t n;
+ int error, count, cs, s;
+ uint8_t b;
+
+ count = kevent(sc->kq, NULL, 0, &kev, 1, NULL);
+ ATF_REQUIRE_MSG(count == 1,
+ "kevent() failed: %s", strerror(errno));
+
+ s = (int)kev.ident;
+ cs = accept(s, NULL, NULL);
+ ATF_REQUIRE_MSG(cs >= 0,
+ "accept() failed: %s", strerror(errno));
+
+ b = 'M';
+ n = write(cs, &b, sizeof(b));
+ ATF_REQUIRE_MSG(n >= 0, "write() failed: %s", strerror(errno));
+ ATF_REQUIRE(n == 1);
+
+ error = close(cs);
+ ATF_REQUIRE_MSG(error == 0 || errno == ECONNRESET,
+ "close() failed: %s", strerror(errno));
+ }
+}
+
+static void *
+connector(void *arg)
+{
+ for (struct concurrent_add_softc *sc = arg;;) {
+ ssize_t n;
+ int error, s;
+ uint8_t b;
+
+ s = socket(sc->ss.ss_family, SOCK_STREAM, 0);
+ ATF_REQUIRE_MSG(s >= 0, "socket() failed: %s", strerror(errno));
+
+ error = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (int[]){1},
+ sizeof(int));
+
+ error = connect(s, (struct sockaddr *)&sc->ss, sc->ss.ss_len);
+ ATF_REQUIRE_MSG(error == 0, "connect() failed: %s",
+ strerror(errno));
+
+ n = read(s, &b, sizeof(b));
+ ATF_REQUIRE_MSG(n >= 0, "read() failed: %s",
+ strerror(errno));
+ ATF_REQUIRE(n == 1);
+ ATF_REQUIRE(b == 'M');
+ error = close(s);
+ ATF_REQUIRE_MSG(error == 0,
+ "close() failed: %s", strerror(errno));
+ }
+}
+
+/*
+ * Run three threads. One accepts connections from listening sockets on a
+ * kqueue, while the other makes connections. The third thread slowly adds
+ * sockets to the LB group. This is meant to help flush out race conditions.
+ */
+ATF_TC_WITHOUT_HEAD(concurrent_add);
+ATF_TC_BODY(concurrent_add, tc)
+{
+ struct concurrent_add_softc sc;
+ struct sockaddr_in *sin;
+ pthread_t threads[4];
+ int error;
+
+ sc.kq = kqueue();
+ ATF_REQUIRE_MSG(sc.kq >= 0, "kqueue() failed: %s", strerror(errno));
+
+ error = pthread_create(&threads[0], NULL, listener, &sc);
+ ATF_REQUIRE_MSG(error == 0, "pthread_create() failed: %s",
+ strerror(error));
+
+ sin = (struct sockaddr_in *)&sc.ss;
+ memset(sin, 0, sizeof(*sin));
+ sin->sin_len = sizeof(*sin);
+ sin->sin_family = AF_INET;
+ sin->sin_port = htons(0);
+ sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ for (size_t i = 0; i < nitems(sc.socks); i++) {
+ struct kevent kev;
+ int s;
+
+ sc.socks[i] = s = socket(AF_INET, SOCK_STREAM, 0);
+ ATF_REQUIRE_MSG(s >= 0, "socket() failed: %s", strerror(errno));
+
+ error = setsockopt(s, SOL_SOCKET, SO_REUSEPORT_LB, (int[]){1},
+ sizeof(int));
+ ATF_REQUIRE_MSG(error == 0,
+ "setsockopt(SO_REUSEPORT_LB) failed: %s", strerror(errno));
+
+ error = bind(s, (struct sockaddr *)sin, sizeof(*sin));
+ ATF_REQUIRE_MSG(error == 0, "bind() failed: %s",
+ strerror(errno));
+
+ error = listen(s, 5);
+ ATF_REQUIRE_MSG(error == 0, "listen() failed: %s",
+ strerror(errno));
+
+ EV_SET(&kev, s, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0);
+ error = kevent(sc.kq, &kev, 1, NULL, 0, NULL);
+ ATF_REQUIRE_MSG(error == 0, "kevent() failed: %s",
+ strerror(errno));
+
+ if (i == 0) {
+ socklen_t slen = sizeof(sc.ss);
+
+ error = getsockname(sc.socks[i],
+ (struct sockaddr *)&sc.ss, &slen);
+ ATF_REQUIRE_MSG(error == 0, "getsockname() failed: %s",
+ strerror(errno));
+ ATF_REQUIRE(sc.ss.ss_family == AF_INET);
+
+ for (size_t j = 1; j < nitems(threads); j++) {
+ error = pthread_create(&threads[j], NULL,
+ connector, &sc);
+ ATF_REQUIRE_MSG(error == 0,
+ "pthread_create() failed: %s",
+ strerror(error));
+ }
+ }
+
+ usleep(20000);
+ }
+}
+
+/*
+ * Try calling listen(2) twice on a socket with SO_REUSEPORT_LB set.
+ */
+ATF_TC_WITHOUT_HEAD(double_listen_ipv4);
+ATF_TC_BODY(double_listen_ipv4, tc)
+{
+ struct sockaddr_in sin;
+ int error, s;
+
+ s = lb_listen_socket(PF_INET, 0);
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(0);
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ error = bind(s, (struct sockaddr *)&sin, sizeof(sin));
+ ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
+
+ error = listen(s, 1);
+ ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno));
+ error = listen(s, 2);
+ ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno));
+
+ error = close(s);
+ ATF_REQUIRE_MSG(error == 0, "close() failed: %s", strerror(errno));
+}
+
+/*
+ * Try calling listen(2) twice on a socket with SO_REUSEPORT_LB set.
+ */
+ATF_TC_WITHOUT_HEAD(double_listen_ipv6);
+ATF_TC_BODY(double_listen_ipv6, tc)
+{
+ struct sockaddr_in6 sin6;
+ int error, s;
+
+ s = lb_listen_socket(PF_INET6, 0);
+
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_len = sizeof(sin6);
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(0);
+ sin6.sin6_addr = in6addr_loopback;
+ error = bind(s, (struct sockaddr *)&sin6, sizeof(sin6));
+ ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
+
+ error = listen(s, 1);
+ ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno));
+ error = listen(s, 2);
+ ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno));
+
+ error = close(s);
+ ATF_REQUIRE_MSG(error == 0, "close() failed: %s", strerror(errno));
+}
+
+/*
+ * Try binding many sockets to the same lbgroup without calling listen(2) on
+ * them.
+ */
+ATF_TC_WITHOUT_HEAD(bind_without_listen);
+ATF_TC_BODY(bind_without_listen, tc)
+{
+ const int nsockets = 100;
+ struct sockaddr_in sin;
+ socklen_t socklen;
+ int error, s, s2[nsockets];
+
+ s = lb_listen_socket(PF_INET, 0);
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(0);
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ error = bind(s, (struct sockaddr *)&sin, sizeof(sin));
+ ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
+
+ socklen = sizeof(sin);
+ error = getsockname(s, (struct sockaddr *)&sin, &socklen);
+ ATF_REQUIRE_MSG(error == 0, "getsockname() failed: %s",
+ strerror(errno));
+
+ for (int i = 0; i < nsockets; i++) {
+ s2[i] = lb_listen_socket(PF_INET, 0);
+ error = bind(s2[i], (struct sockaddr *)&sin, sizeof(sin));
+ ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
+ }
+ for (int i = 0; i < nsockets; i++) {
+ error = listen(s2[i], 1);
+ ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno));
+ }
+ for (int i = 0; i < nsockets; i++) {
+ error = close(s2[i]);
+ ATF_REQUIRE_MSG(error == 0, "close() failed: %s", strerror(errno));
+ }
+
+ error = close(s);
+ ATF_REQUIRE_MSG(error == 0, "close() failed: %s", strerror(errno));
+}
+
+/*
+ * Check that SO_REUSEPORT_LB doesn't mess with connect(2).
+ * Two sockets:
+ * 1) auxiliary peer socket 'p', where we connect to
+ * 2) test socket 's', that sets SO_REUSEPORT_LB and then connect(2)s to 'p'
+ */
+ATF_TC_WITHOUT_HEAD(connect_not_bound);
+ATF_TC_BODY(connect_not_bound, tc)
+{
+ struct sockaddr_in sin = {
+ .sin_family = AF_INET,
+ .sin_len = sizeof(sin),
+ .sin_addr = { htonl(INADDR_LOOPBACK) },
+ };
+ socklen_t slen = sizeof(struct sockaddr_in);
+ int p, s, rv;
+
+ ATF_REQUIRE((p = socket(PF_INET, SOCK_STREAM, 0)) > 0);
+ ATF_REQUIRE(bind(p, (struct sockaddr *)&sin, sizeof(sin)) == 0);
+ ATF_REQUIRE(listen(p, 1) == 0);
+ ATF_REQUIRE(getsockname(p, (struct sockaddr *)&sin, &slen) == 0);
+
+ s = lb_listen_socket(PF_INET, 0);
+ rv = connect(s, (struct sockaddr *)&sin, sizeof(sin));
+ ATF_REQUIRE_MSG(rv == -1 && errno == EOPNOTSUPP,
+ "Expected EOPNOTSUPP on connect(2) not met. Got %d, errno %d",
+ rv, errno);
+ rv = sendto(s, "test", 4, 0, (struct sockaddr *)&sin,
+ sizeof(sin));
+ ATF_REQUIRE_MSG(rv == -1 && errno == EOPNOTSUPP,
+ "Expected EOPNOTSUPP on sendto(2) not met. Got %d, errno %d",
+ rv, errno);
+
+ close(p);
+ close(s);
+}
+
+/*
+ * Same as above, but we also bind(2) between setsockopt(2) of SO_REUSEPORT_LB
+ * and the connect(2).
+ */
+ATF_TC_WITHOUT_HEAD(connect_bound);
+ATF_TC_BODY(connect_bound, tc)
+{
+ struct sockaddr_in sin = {
+ .sin_family = AF_INET,
+ .sin_len = sizeof(sin),
+ .sin_addr = { htonl(INADDR_LOOPBACK) },
+ };
+ socklen_t slen = sizeof(struct sockaddr_in);
+ int p, s, rv;
+
+ ATF_REQUIRE((p = socket(PF_INET, SOCK_STREAM, 0)) > 0);
+ ATF_REQUIRE(bind(p, (struct sockaddr *)&sin, sizeof(sin)) == 0);
+ ATF_REQUIRE(listen(p, 1) == 0);
+
+ s = lb_listen_socket(PF_INET, 0);
+ ATF_REQUIRE(bind(s, (struct sockaddr *)&sin, sizeof(sin)) == 0);
+ ATF_REQUIRE(getsockname(p, (struct sockaddr *)&sin, &slen) == 0);
+ rv = connect(s, (struct sockaddr *)&sin, sizeof(sin));
+ ATF_REQUIRE_MSG(rv == -1 && errno == EOPNOTSUPP,
+ "Expected EOPNOTSUPP on connect(2) not met. Got %d, errno %d",
+ rv, errno);
+ rv = sendto(s, "test", 4, 0, (struct sockaddr *)&sin,
+ sizeof(sin));
+ ATF_REQUIRE_MSG(rv == -1 && errno == EOPNOTSUPP,
+ "Expected EOPNOTSUPP on sendto(2) not met. Got %d, errno %d",
+ rv, errno);
+
+ close(p);
+ close(s);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, basic_ipv4);
+ ATF_TP_ADD_TC(tp, basic_ipv6);
+ ATF_TP_ADD_TC(tp, concurrent_add);
+ ATF_TP_ADD_TC(tp, double_listen_ipv4);
+ ATF_TP_ADD_TC(tp, double_listen_ipv6);
+ ATF_TP_ADD_TC(tp, bind_without_listen);
+ ATF_TP_ADD_TC(tp, connect_not_bound);
+ ATF_TP_ADD_TC(tp, connect_bound);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/netinet/socket_afinet.c b/tests/sys/netinet/socket_afinet.c
new file mode 100644
index 000000000000..9c718fc5a901
--- /dev/null
+++ b/tests/sys/netinet/socket_afinet.c
@@ -0,0 +1,599 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Bjoern A. Zeeb
+ * Copyright (c) 2024 Stormshield
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <poll.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(socket_afinet);
+ATF_TC_BODY(socket_afinet, tc)
+{
+ int sd;
+
+ sd = socket(PF_INET, SOCK_DGRAM, 0);
+ ATF_CHECK(sd >= 0);
+
+ close(sd);
+}
+
+ATF_TC_WITHOUT_HEAD(socket_afinet_bind_zero);
+ATF_TC_BODY(socket_afinet_bind_zero, tc)
+{
+ int sd, rc;
+ struct sockaddr_in sin;
+
+ if (atf_tc_get_config_var_as_bool_wd(tc, "ci", false))
+ atf_tc_skip("doesn't work when mac_portacl(4) loaded (https://bugs.freebsd.org/238781)");
+
+ sd = socket(PF_INET, SOCK_DGRAM, 0);
+ ATF_CHECK(sd >= 0);
+
+ bzero(&sin, sizeof(sin));
+ /*
+ * For AF_INET we do not check the family in in_pcbbind_setup(9),
+ * sa_len gets set from the syscall argument in getsockaddr(9),
+ * so we bind to 0:0.
+ */
+ rc = bind(sd, (struct sockaddr *)&sin, sizeof(sin));
+ ATF_CHECK_EQ(0, rc);
+
+ close(sd);
+}
+
+ATF_TC_WITHOUT_HEAD(socket_afinet_bind_ok);
+ATF_TC_BODY(socket_afinet_bind_ok, tc)
+{
+ int sd, rc;
+ struct sockaddr_in sin;
+
+ sd = socket(PF_INET, SOCK_DGRAM, 0);
+ ATF_CHECK(sd >= 0);
+
+ bzero(&sin, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof(sin);
+ sin.sin_port = htons(0);
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ rc = bind(sd, (struct sockaddr *)&sin, sizeof(sin));
+ ATF_CHECK_EQ(0, rc);
+
+ close(sd);
+}
+
+ATF_TC_WITHOUT_HEAD(socket_afinet_poll_no_rdhup);
+ATF_TC_BODY(socket_afinet_poll_no_rdhup, tc)
+{
+ int ss, ss2, cs, rc;
+ struct sockaddr_in sin;
+ socklen_t slen;
+ struct pollfd pfd;
+ int one = 1;
+
+ /* Verify that we don't expose POLLRDHUP if not requested. */
+
+ /* Server setup. */
+ ss = socket(PF_INET, SOCK_STREAM, 0);
+ ATF_CHECK(ss >= 0);
+ rc = setsockopt(ss, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
+ ATF_CHECK_EQ(0, rc);
+ bzero(&sin, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof(sin);
+ sin.sin_port = htons(0);
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ rc = bind(ss, (struct sockaddr *)&sin, sizeof(sin));
+ ATF_CHECK_EQ(0, rc);
+ rc = listen(ss, 1);
+ ATF_CHECK_EQ(0, rc);
+ slen = sizeof(sin);
+ rc = getsockname(ss, (struct sockaddr *)&sin, &slen);
+ ATF_CHECK_EQ(0, rc);
+
+ /* Client connects, server accepts. */
+ cs = socket(PF_INET, SOCK_STREAM, 0);
+ ATF_CHECK(cs >= 0);
+ rc = connect(cs, (struct sockaddr *)&sin, sizeof(sin));
+ ATF_CHECK_EQ(0, rc);
+ ss2 = accept(ss, NULL, NULL);
+ ATF_CHECK(ss2 >= 0);
+
+ /* Server can write, sees only POLLOUT. */
+ pfd.fd = ss2;
+ pfd.events = POLLIN | POLLOUT;
+ rc = poll(&pfd, 1, 0);
+ ATF_CHECK_EQ(1, rc);
+ ATF_CHECK_EQ(POLLOUT, pfd.revents);
+
+ /* Client closes socket! */
+ rc = close(cs);
+ ATF_CHECK_EQ(0, rc);
+
+ /*
+ * Server now sees POLLIN, but not POLLRDHUP because we didn't ask.
+ * Need non-zero timeout to wait for the FIN to arrive and trigger the
+ * socket to become readable.
+ */
+ pfd.fd = ss2;
+ pfd.events = POLLIN;
+ rc = poll(&pfd, 1, 60000);
+ ATF_CHECK_EQ(1, rc);
+ ATF_CHECK_EQ(POLLIN, pfd.revents);
+
+ close(ss2);
+ close(ss);
+}
+
+ATF_TC_WITHOUT_HEAD(socket_afinet_poll_rdhup);
+ATF_TC_BODY(socket_afinet_poll_rdhup, tc)
+{
+ int ss, ss2, cs, rc;
+ struct sockaddr_in sin;
+ socklen_t slen;
+ struct pollfd pfd;
+ char buffer;
+ int one = 1;
+
+ /* Verify that server sees POLLRDHUP if it asks for it. */
+
+ /* Server setup. */
+ ss = socket(PF_INET, SOCK_STREAM, 0);
+ ATF_CHECK(ss >= 0);
+ rc = setsockopt(ss, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
+ ATF_CHECK_EQ(0, rc);
+ bzero(&sin, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof(sin);
+ sin.sin_port = htons(0);
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ rc = bind(ss, (struct sockaddr *)&sin, sizeof(sin));
+ ATF_CHECK_EQ(0, rc);
+ rc = listen(ss, 1);
+ ATF_CHECK_EQ(0, rc);
+ slen = sizeof(sin);
+ rc = getsockname(ss, (struct sockaddr *)&sin, &slen);
+ ATF_CHECK_EQ(0, rc);
+
+ /* Client connects, server accepts. */
+ cs = socket(PF_INET, SOCK_STREAM, 0);
+ ATF_CHECK(cs >= 0);
+ rc = connect(cs, (struct sockaddr *)&sin, sizeof(sin));
+ ATF_CHECK_EQ(0, rc);
+ ss2 = accept(ss, NULL, NULL);
+ ATF_CHECK(ss2 >= 0);
+
+ /* Server can write, so sees POLLOUT. */
+ pfd.fd = ss2;
+ pfd.events = POLLIN | POLLOUT | POLLRDHUP;
+ rc = poll(&pfd, 1, 0);
+ ATF_CHECK_EQ(1, rc);
+ ATF_CHECK_EQ(POLLOUT, pfd.revents);
+
+ /* Client writes two bytes, server reads only one of them. */
+ rc = write(cs, "xx", 2);
+ ATF_CHECK_EQ(2, rc);
+ rc = read(ss2, &buffer, 1);
+ ATF_CHECK_EQ(1, rc);
+
+ /* Server can read, so sees POLLIN. */
+ pfd.fd = ss2;
+ pfd.events = POLLIN | POLLOUT | POLLRDHUP;
+ rc = poll(&pfd, 1, 0);
+ ATF_CHECK_EQ(1, rc);
+ ATF_CHECK_EQ(POLLIN | POLLOUT, pfd.revents);
+
+ /* Client closes socket! */
+ rc = close(cs);
+ ATF_CHECK_EQ(0, rc);
+
+ /*
+ * Server sees Linux-style POLLRDHUP. Note that this is the case even
+ * though one byte of data remains unread.
+ *
+ * This races against the delivery of FIN caused by the close() above.
+ * Sometimes (more likely when run under truss or if another system
+ * call is added in between) it hits the path where sopoll_generic()
+ * immediately sees SBS_CANTRCVMORE, and sometimes it sleeps with flag
+ * SB_SEL so that it's woken up almost immediately and runs again,
+ * which is why we need a non-zero timeout here.
+ */
+ pfd.fd = ss2;
+ pfd.events = POLLRDHUP;
+ rc = poll(&pfd, 1, 60000);
+ ATF_CHECK_EQ(1, rc);
+ ATF_CHECK_EQ(POLLRDHUP, pfd.revents);
+
+ close(ss2);
+ close(ss);
+}
+
+ATF_TC_WITHOUT_HEAD(socket_afinet_stream_reconnect);
+ATF_TC_BODY(socket_afinet_stream_reconnect, tc)
+{
+ struct sockaddr_in sin;
+ socklen_t slen;
+ int ss, cs, rc;
+
+ /*
+ * Make sure that an attempt to connect(2) a connected or disconnected
+ * stream socket fails with EISCONN.
+ */
+
+ /* Server setup. */
+ ss = socket(PF_INET, SOCK_STREAM, 0);
+ ATF_CHECK(ss >= 0);
+ bzero(&sin, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof(sin);
+ sin.sin_port = htons(0);
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ rc = bind(ss, (struct sockaddr *)&sin, sizeof(sin));
+ ATF_CHECK_EQ(0, rc);
+ rc = listen(ss, 1);
+ ATF_CHECK_EQ(0, rc);
+ slen = sizeof(sin);
+ rc = getsockname(ss, (struct sockaddr *)&sin, &slen);
+ ATF_CHECK_EQ(0, rc);
+
+ /* Client connects, shuts down. */
+ cs = socket(PF_INET, SOCK_STREAM, 0);
+ ATF_CHECK(cs >= 0);
+ rc = connect(cs, (struct sockaddr *)&sin, sizeof(sin));
+ ATF_CHECK_EQ(0, rc);
+ rc = shutdown(cs, SHUT_RDWR);
+ ATF_CHECK_EQ(0, rc);
+
+ /* A subsequent connect(2) fails with EISCONN. */
+ rc = connect(cs, (struct sockaddr *)&sin, sizeof(sin));
+ ATF_CHECK_EQ(-1, rc);
+ ATF_CHECK_EQ(errno, EISCONN);
+
+ rc = close(cs);
+ ATF_CHECK_EQ(0, rc);
+ rc = close(ss);
+ ATF_CHECK_EQ(0, rc);
+}
+
+/*
+ * Make sure that unprivileged users can't set the IP_BINDANY or IPV6_BINDANY
+ * socket options.
+ */
+ATF_TC(socket_afinet_bindany);
+ATF_TC_HEAD(socket_afinet_bindany, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "unprivileged");
+}
+ATF_TC_BODY(socket_afinet_bindany, tc)
+{
+ int s;
+
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ ATF_REQUIRE(s >= 0);
+ ATF_REQUIRE_ERRNO(EPERM,
+ setsockopt(s, IPPROTO_IP, IP_BINDANY, &(int){1}, sizeof(int)) ==
+ -1);
+ ATF_REQUIRE(close(s) == 0);
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ ATF_REQUIRE(s >= 0);
+ ATF_REQUIRE_ERRNO(EPERM,
+ setsockopt(s, IPPROTO_IP, IP_BINDANY, &(int){1}, sizeof(int)) ==
+ -1);
+ ATF_REQUIRE(close(s) == 0);
+
+ s = socket(AF_INET6, SOCK_STREAM, 0);
+ ATF_REQUIRE(s >= 0);
+ ATF_REQUIRE_ERRNO(EPERM,
+ setsockopt(s, IPPROTO_IPV6, IPV6_BINDANY, &(int){1}, sizeof(int)) ==
+ -1);
+ ATF_REQUIRE(close(s) == 0);
+
+ s = socket(AF_INET6, SOCK_DGRAM, 0);
+ ATF_REQUIRE(s >= 0);
+ ATF_REQUIRE_ERRNO(EPERM,
+ setsockopt(s, IPPROTO_IPV6, IPV6_BINDANY, &(int){1}, sizeof(int)) ==
+ -1);
+ ATF_REQUIRE(close(s) == 0);
+}
+
+/*
+ * Bind a socket to the specified address, optionally dropping privileges and
+ * setting one of the SO_REUSE* options first.
+ *
+ * Returns true if the bind succeeded, and false if it failed with EADDRINUSE.
+ */
+static bool
+child_bind(const atf_tc_t *tc, int type, struct sockaddr *sa, int opt,
+ bool unpriv)
+{
+ const char *user;
+ pid_t child;
+
+ if (unpriv) {
+ if (!atf_tc_has_config_var(tc, "unprivileged_user"))
+ atf_tc_skip("unprivileged_user not set");
+ user = atf_tc_get_config_var(tc, "unprivileged_user");
+ } else {
+ user = NULL;
+ }
+
+ child = fork();
+ ATF_REQUIRE(child != -1);
+ if (child == 0) {
+ int s;
+
+ if (user != NULL) {
+ struct passwd *passwd;
+
+ passwd = getpwnam(user);
+ if (seteuid(passwd->pw_uid) != 0)
+ _exit(1);
+ }
+
+ s = socket(sa->sa_family, type, 0);
+ if (s < 0)
+ _exit(2);
+ if (bind(s, sa, sa->sa_len) == 0)
+ _exit(3);
+ if (errno != EADDRINUSE)
+ _exit(4);
+ if (opt != 0) {
+ if (setsockopt(s, SOL_SOCKET, opt, &(int){1},
+ sizeof(int)) != 0)
+ _exit(5);
+ }
+ if (bind(s, sa, sa->sa_len) == 0)
+ _exit(6);
+ if (errno != EADDRINUSE)
+ _exit(7);
+ _exit(0);
+ } else {
+ int status;
+
+ ATF_REQUIRE_EQ(waitpid(child, &status, 0), child);
+ ATF_REQUIRE(WIFEXITED(status));
+ status = WEXITSTATUS(status);
+ ATF_REQUIRE_MSG(status == 0 || status == 6,
+ "child exited with %d", status);
+ return (status == 6);
+ }
+}
+
+static bool
+child_bind_priv(const atf_tc_t *tc, int type, struct sockaddr *sa, int opt)
+{
+ return (child_bind(tc, type, sa, opt, false));
+}
+
+static bool
+child_bind_unpriv(const atf_tc_t *tc, int type, struct sockaddr *sa, int opt)
+{
+ return (child_bind(tc, type, sa, opt, true));
+}
+
+static int
+bind_socket(int domain, int type, int opt, bool unspec, struct sockaddr *sa)
+{
+ socklen_t slen;
+ int s;
+
+ s = socket(domain, type, 0);
+ ATF_REQUIRE(s >= 0);
+
+ if (domain == AF_INET) {
+ struct sockaddr_in sin;
+
+ bzero(&sin, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof(sin);
+ sin.sin_addr.s_addr = htonl(unspec ?
+ INADDR_ANY : INADDR_LOOPBACK);
+ sin.sin_port = htons(0);
+ ATF_REQUIRE(bind(s, (struct sockaddr *)&sin, sizeof(sin)) == 0);
+
+ slen = sizeof(sin);
+ } else /* if (domain == AF_INET6) */ {
+ struct sockaddr_in6 sin6;
+
+ bzero(&sin6, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_len = sizeof(sin6);
+ sin6.sin6_addr = unspec ? in6addr_any : in6addr_loopback;
+ sin6.sin6_port = htons(0);
+ ATF_REQUIRE(bind(s, (struct sockaddr *)&sin6, sizeof(sin6)) == 0);
+
+ slen = sizeof(sin6);
+ }
+
+ if (opt != 0) {
+ ATF_REQUIRE(setsockopt(s, SOL_SOCKET, opt, &(int){1},
+ sizeof(int)) == 0);
+ }
+
+ ATF_REQUIRE(getsockname(s, sa, &slen) == 0);
+
+ return (s);
+}
+
+static void
+multibind_test(const atf_tc_t *tc, int domain, int type)
+{
+ struct sockaddr_storage ss;
+ int opts[4] = { 0, SO_REUSEADDR, SO_REUSEPORT, SO_REUSEPORT_LB };
+ int s;
+ bool flags[2] = { false, true };
+ bool res;
+
+ for (size_t flagi = 0; flagi < nitems(flags); flagi++) {
+ for (size_t opti = 0; opti < nitems(opts); opti++) {
+ s = bind_socket(domain, type, opts[opti], flags[flagi],
+ (struct sockaddr *)&ss);
+ for (size_t optj = 0; optj < nitems(opts); optj++) {
+ int opt;
+
+ opt = opts[optj];
+ res = child_bind_priv(tc, type,
+ (struct sockaddr *)&ss, opt);
+ /*
+ * Multi-binding is only allowed when both
+ * sockets have SO_REUSEPORT or SO_REUSEPORT_LB
+ * set.
+ */
+ if (opts[opti] != 0 &&
+ opts[opti] != SO_REUSEADDR && opti == optj)
+ ATF_REQUIRE(res);
+ else
+ ATF_REQUIRE(!res);
+
+ res = child_bind_unpriv(tc, type,
+ (struct sockaddr *)&ss, opt);
+ /*
+ * Multi-binding is only allowed when both
+ * sockets have the same owner.
+ */
+ ATF_REQUIRE(!res);
+ }
+ ATF_REQUIRE(close(s) == 0);
+ }
+ }
+}
+
+/*
+ * Try to bind two sockets to the same address/port tuple. Under some
+ * conditions this is permitted.
+ */
+ATF_TC(socket_afinet_multibind);
+ATF_TC_HEAD(socket_afinet_multibind, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.config", "unprivileged_user");
+}
+ATF_TC_BODY(socket_afinet_multibind, tc)
+{
+ multibind_test(tc, AF_INET, SOCK_STREAM);
+ multibind_test(tc, AF_INET, SOCK_DGRAM);
+ multibind_test(tc, AF_INET6, SOCK_STREAM);
+ multibind_test(tc, AF_INET6, SOCK_DGRAM);
+}
+
+static void
+bind_connected_port_test(const atf_tc_t *tc, int domain)
+{
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ struct sockaddr *sinp;
+ int error, sd[3], tmp;
+ bool res;
+
+ /*
+ * Create a connected socket pair.
+ */
+ sd[0] = socket(domain, SOCK_STREAM, 0);
+ ATF_REQUIRE_MSG(sd[0] >= 0, "socket failed: %s", strerror(errno));
+ sd[1] = socket(domain, SOCK_STREAM, 0);
+ ATF_REQUIRE_MSG(sd[1] >= 0, "socket failed: %s", strerror(errno));
+ if (domain == PF_INET) {
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof(sin);
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ sin.sin_port = htons(0);
+ sinp = (struct sockaddr *)&sin;
+ } else {
+ ATF_REQUIRE(domain == PF_INET6);
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_len = sizeof(sin6);
+ sin6.sin6_addr = in6addr_any;
+ sin6.sin6_port = htons(0);
+ sinp = (struct sockaddr *)&sin6;
+ }
+
+ error = bind(sd[0], sinp, sinp->sa_len);
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+ error = listen(sd[0], 1);
+ ATF_REQUIRE_MSG(error == 0, "listen failed: %s", strerror(errno));
+
+ error = getsockname(sd[0], sinp, &(socklen_t){ sinp->sa_len });
+ ATF_REQUIRE_MSG(error == 0, "getsockname failed: %s", strerror(errno));
+ if (domain == PF_INET)
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ error = connect(sd[1], sinp, sinp->sa_len);
+ ATF_REQUIRE_MSG(error == 0, "connect failed: %s", strerror(errno));
+ tmp = accept(sd[0], NULL, NULL);
+ ATF_REQUIRE_MSG(tmp >= 0, "accept failed: %s", strerror(errno));
+ ATF_REQUIRE(close(sd[0]) == 0);
+ sd[0] = tmp;
+
+ /* bind() should succeed even from an unprivileged user. */
+ res = child_bind(tc, SOCK_STREAM, sinp, 0, false);
+ ATF_REQUIRE(!res);
+ res = child_bind(tc, SOCK_STREAM, sinp, 0, true);
+ ATF_REQUIRE(!res);
+}
+
+/*
+ * Normally bind() prevents port stealing by a different user, even when
+ * SO_REUSE* are specified. However, if the port is bound by a connected
+ * socket, then it's fair game.
+ */
+ATF_TC(socket_afinet_bind_connected_port);
+ATF_TC_HEAD(socket_afinet_bind_connected_port, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.config", "unprivileged_user");
+}
+ATF_TC_BODY(socket_afinet_bind_connected_port, tc)
+{
+ bind_connected_port_test(tc, AF_INET);
+ bind_connected_port_test(tc, AF_INET6);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, socket_afinet);
+ ATF_TP_ADD_TC(tp, socket_afinet_bind_zero);
+ ATF_TP_ADD_TC(tp, socket_afinet_bind_ok);
+ ATF_TP_ADD_TC(tp, socket_afinet_poll_no_rdhup);
+ ATF_TP_ADD_TC(tp, socket_afinet_poll_rdhup);
+ ATF_TP_ADD_TC(tp, socket_afinet_stream_reconnect);
+ ATF_TP_ADD_TC(tp, socket_afinet_bindany);
+ ATF_TP_ADD_TC(tp, socket_afinet_multibind);
+ ATF_TP_ADD_TC(tp, socket_afinet_bind_connected_port);
+
+ return atf_no_error();
+}
diff --git a/tests/sys/netinet/tcp_connect_port_test.c b/tests/sys/netinet/tcp_connect_port_test.c
new file mode 100644
index 000000000000..ad087afb162d
--- /dev/null
+++ b/tests/sys/netinet/tcp_connect_port_test.c
@@ -0,0 +1,331 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2020 Netflix, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+
+#include <netinet/in.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#define SYSCTLBAKFILE "tmp.net.inet.ip.portrange.randomized"
+
+/*
+ * Check if port allocation is randomized. If so, update it. Save the old
+ * value of the sysctl so it can be updated later.
+ */
+static void
+disable_random_ports(void)
+{
+ int error, fd, random_new, random_save;
+ size_t sysctlsz;
+
+ /*
+ * Pre-emptively unlink our restoration file, so we will do no
+ * restoration on error.
+ */
+ unlink(SYSCTLBAKFILE);
+
+ /*
+ * Disable the net.inet.ip.portrange.randomized sysctl. Save the
+ * old value so we can restore it, if necessary.
+ */
+ random_new = 0;
+ sysctlsz = sizeof(random_save);
+ error = sysctlbyname("net.inet.ip.portrange.randomized", &random_save,
+ &sysctlsz, &random_new, sizeof(random_new));
+ if (error) {
+ warn("sysctlbyname(\"net.inet.ip.portrange.randomized\") "
+ "failed");
+ atf_tc_skip("Unable to set sysctl");
+ }
+ if (sysctlsz != sizeof(random_save)) {
+ fprintf(stderr, "Error: unexpected sysctl value size "
+ "(expected %zu, actual %zu)\n", sizeof(random_save),
+ sysctlsz);
+ goto restore_sysctl;
+ }
+
+ /* Open the backup file, write the contents, and close it. */
+ fd = open(SYSCTLBAKFILE, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL,
+ S_IRUSR|S_IWUSR);
+ if (fd < 0) {
+ warn("error opening sysctl backup file");
+ goto restore_sysctl;
+ }
+ error = write(fd, &random_save, sizeof(random_save));
+ if (error < 0) {
+ warn("error writing saved value to sysctl backup file");
+ goto cleanup_and_restore;
+ }
+ if (error != (int)sizeof(random_save)) {
+ fprintf(stderr,
+ "Error writing saved value to sysctl backup file: "
+ "(expected %zu, actual %d)\n", sizeof(random_save), error);
+ goto cleanup_and_restore;
+ }
+ error = close(fd);
+ if (error) {
+ warn("error closing sysctl backup file");
+cleanup_and_restore:
+ (void)close(fd);
+ (void)unlink(SYSCTLBAKFILE);
+restore_sysctl:
+ (void)sysctlbyname("net.inet.ip.portrange.randomized", NULL,
+ NULL, &random_save, sysctlsz);
+ atf_tc_skip("Error setting sysctl");
+ }
+}
+
+/*
+ * Restore the sysctl value from the backup file and delete the backup file.
+ */
+static void
+restore_random_ports(void)
+{
+ int error, fd, random_save;
+
+ /* Open the backup file, read the contents, close it, and delete it. */
+ fd = open(SYSCTLBAKFILE, O_RDONLY);
+ if (fd < 0) {
+ warn("error opening sysctl backup file");
+ return;
+ }
+ error = read(fd, &random_save, sizeof(random_save));
+ if (error < 0) {
+ warn("error reading saved value from sysctl backup file");
+ return;
+ }
+ if (error != (int)sizeof(random_save)) {
+ fprintf(stderr,
+ "Error reading saved value from sysctl backup file: "
+ "(expected %zu, actual %d)\n", sizeof(random_save), error);
+ return;
+ }
+ error = close(fd);
+ if (error)
+ warn("error closing sysctl backup file");
+ error = unlink(SYSCTLBAKFILE);
+ if (error)
+ warn("error removing sysctl backup file");
+
+ /* Restore the saved sysctl value. */
+ error = sysctlbyname("net.inet.ip.portrange.randomized", NULL, NULL,
+ &random_save, sizeof(random_save));
+ if (error)
+ warn("sysctlbyname(\"net.inet.ip.portrange.randomized\") "
+ "failed while restoring value");
+}
+
+/*
+ * Given a domain and sockaddr, open a listening socket with automatic port
+ * selection. Then, try to connect 64K times. Ensure the connected socket never
+ * uses an overlapping port.
+ */
+static void
+connect_loop(int domain, const struct sockaddr *addr)
+{
+ union {
+ struct sockaddr saddr;
+ struct sockaddr_in saddr4;
+ struct sockaddr_in6 saddr6;
+ } su_clnt, su_srvr;
+ socklen_t salen;
+ int asock, csock, error, i, lsock;
+ const struct linger lopt = { 1, 0 };
+
+ /*
+ * Disable the net.inet.ip.portrange.randomized sysctl. Assuming an
+ * otherwise idle system, this makes the kernel try all possible
+ * ports sequentially and makes it more likely it will try the
+ * port on which we have a listening socket.
+ */
+ disable_random_ports();
+
+ /* Setup the listen socket. */
+ lsock = socket(domain, SOCK_STREAM, 0);
+ ATF_REQUIRE_MSG(lsock >= 0, "socket() for listen socket failed: %s",
+ strerror(errno));
+ error = bind(lsock, addr, addr->sa_len);
+ ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
+ error = listen(lsock, 1);
+ ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno));
+
+ /*
+ * Get the address of the listen socket, which will be the destination
+ * address for our connection attempts.
+ */
+ salen = sizeof(su_srvr);
+ error = getsockname(lsock, &su_srvr.saddr, &salen);
+ ATF_REQUIRE_MSG(error == 0,
+ "getsockname() for listen socket failed: %s",
+ strerror(errno));
+ ATF_REQUIRE_MSG(salen == (domain == PF_INET ?
+ sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)),
+ "unexpected sockaddr size");
+ ATF_REQUIRE_MSG(su_srvr.saddr.sa_len == (domain == PF_INET ?
+ sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)),
+ "unexpected sa_len size");
+
+ /* Open 64K connections in a loop. */
+ for (i = 0; i < 65536; i++) {
+ csock = socket(domain, SOCK_STREAM, 0);
+ ATF_REQUIRE_MSG(csock >= 0,
+ "socket() for client socket %d failed: %s",
+ i, strerror(errno));
+
+ error = connect(csock, &su_srvr.saddr, su_srvr.saddr.sa_len);
+ ATF_REQUIRE_MSG(error == 0,
+ "connect() for client socket %d failed: %s",
+ i, strerror(errno));
+
+ error = setsockopt(csock, SOL_SOCKET, SO_LINGER, &lopt,
+ sizeof(lopt));
+ ATF_REQUIRE_MSG(error == 0,
+ "Setting linger for client socket %d failed: %s",
+ i, strerror(errno));
+
+ /* Ascertain the client socket address. */
+ salen = sizeof(su_clnt);
+ error = getsockname(csock, &su_clnt.saddr, &salen);
+ ATF_REQUIRE_MSG(error == 0,
+ "getsockname() for client socket %d failed: %s",
+ i, strerror(errno));
+ ATF_REQUIRE_MSG(salen == (domain == PF_INET ?
+ sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)),
+ "unexpected sockaddr size for client socket %d", i);
+
+ /* Ensure the ports do not match. */
+ switch (domain) {
+ case PF_INET:
+ ATF_REQUIRE_MSG(su_clnt.saddr4.sin_port !=
+ su_srvr.saddr4.sin_port,
+ "client socket %d using the same port as server",
+ i);
+ break;
+ case PF_INET6:
+ ATF_REQUIRE_MSG(su_clnt.saddr6.sin6_port !=
+ su_srvr.saddr6.sin6_port,
+ "client socket %d using the same port as server",
+ i);
+ break;
+ }
+
+ /* Accept the socket and close both ends. */
+ asock = accept(lsock, NULL, NULL);
+ ATF_REQUIRE_MSG(asock >= 0,
+ "accept() failed for client socket %d: %s",
+ i, strerror(errno));
+
+ error = close(asock);
+ ATF_REQUIRE_MSG(error == 0,
+ "close() failed for accepted socket %d: %s",
+ i, strerror(errno));
+
+ error = close(csock);
+ ATF_REQUIRE_MSG(error == 0,
+ "close() failed for client socket %d: %s",
+ i, strerror(errno));
+ }
+}
+
+ATF_TC_WITH_CLEANUP(basic_ipv4);
+ATF_TC_HEAD(basic_ipv4, tc)
+{
+
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.config", "allow_sysctl_side_effects");
+ atf_tc_set_md_var(tc, "descr",
+ "Check automatic local port assignment during TCP connect calls");
+}
+
+ATF_TC_BODY(basic_ipv4, tc)
+{
+ struct sockaddr_in saddr4;
+
+ memset(&saddr4, 0, sizeof(saddr4));
+ saddr4.sin_len = sizeof(saddr4);
+ saddr4.sin_family = AF_INET;
+ saddr4.sin_port = htons(0);
+ saddr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ connect_loop(PF_INET, (const struct sockaddr *)&saddr4);
+}
+
+ATF_TC_CLEANUP(basic_ipv4, tc)
+{
+
+ restore_random_ports();
+}
+
+ATF_TC_WITH_CLEANUP(basic_ipv6);
+ATF_TC_HEAD(basic_ipv6, tc)
+{
+
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.config", "allow_sysctl_side_effects");
+ atf_tc_set_md_var(tc, "descr",
+ "Check automatic local port assignment during TCP connect calls");
+}
+
+ATF_TC_BODY(basic_ipv6, tc)
+{
+ struct sockaddr_in6 saddr6;
+
+ memset(&saddr6, 0, sizeof(saddr6));
+ saddr6.sin6_len = sizeof(saddr6);
+ saddr6.sin6_family = AF_INET6;
+ saddr6.sin6_port = htons(0);
+ saddr6.sin6_addr = in6addr_loopback;
+
+ connect_loop(PF_INET6, (const struct sockaddr *)&saddr6);
+}
+
+ATF_TC_CLEANUP(basic_ipv6, tc)
+{
+
+ restore_random_ports();
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, basic_ipv4);
+ ATF_TP_ADD_TC(tp, basic_ipv6);
+
+ return (atf_no_error());
+}
+
diff --git a/tests/sys/netinet/tcp_implied_connect.c b/tests/sys/netinet/tcp_implied_connect.c
new file mode 100644
index 000000000000..d03d6be4fb92
--- /dev/null
+++ b/tests/sys/netinet/tcp_implied_connect.c
@@ -0,0 +1,80 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Gleb Smirnoff <glebius@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(tcp_implied_connect);
+ATF_TC_BODY(tcp_implied_connect, tc)
+{
+ struct sockaddr_in sin = {
+ .sin_family = AF_INET,
+ .sin_len = sizeof(sin),
+ };
+ const char buf[] = "hello";
+ char repl[sizeof(buf)];
+ socklen_t len;
+ int s, c, a;
+
+ ATF_REQUIRE(s = socket(PF_INET, SOCK_STREAM, 0));
+ ATF_REQUIRE(c = socket(PF_INET, SOCK_STREAM, 0));
+
+ ATF_REQUIRE(bind(s, (struct sockaddr *)&sin, sizeof(sin)) == 0);
+ len = sizeof(sin);
+ ATF_REQUIRE(getsockname(s, (struct sockaddr *)&sin, &len) == 0);
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ ATF_REQUIRE(listen(s, -1) == 0);
+#if 0
+ /*
+ * The disabled code is that you would normally do.
+ */
+ ATF_REQUIRE(connect(c, (struct sockaddr *)&sin, sizeof(sin)) == 0);
+ ATF_REQUIRE(send(c, &buf, sizeof(buf), 0) == sizeof(buf));
+#else
+ /*
+ * And this is implied connect.
+ */
+ ATF_REQUIRE(sendto(c, &buf, sizeof(buf), 0, (struct sockaddr *)&sin,
+ sizeof(sin)) == sizeof(buf));
+#endif
+
+ ATF_REQUIRE((a = accept(s, NULL, NULL)) != 1);
+ ATF_REQUIRE(recv(a, &repl, sizeof(repl), 0) == sizeof(buf));
+ ATF_REQUIRE(strcmp(buf, repl) == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, tcp_implied_connect);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/netinet/tcp_md5_getsockopt.c b/tests/sys/netinet/tcp_md5_getsockopt.c
new file mode 100644
index 000000000000..deaa4170caea
--- /dev/null
+++ b/tests/sys/netinet/tcp_md5_getsockopt.c
@@ -0,0 +1,135 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2022, Klara Inc.
+ * Copyright (c) 2022, Claudio Jeker <claudio@openbsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <err.h>
+
+#include <atf-c.h>
+
+void test_tcp_md5_getsockopt(int);
+
+void
+test_tcp_md5_getsockopt(int v6)
+{
+ if (kldfind("tcpmd5.ko") == -1)
+ atf_tc_skip("Test requires the tcpmd5 kernel module to be loaded");
+
+ struct sockaddr_in *s;
+ struct sockaddr_in6 s6 = { 0 };
+ struct sockaddr_in s4 = { 0 };
+ socklen_t len;
+ int csock, ssock, opt;
+ int pf;
+
+ if (v6) {
+ pf = PF_INET6;
+ len = sizeof(s6);
+
+ s6.sin6_family = pf;
+ s6.sin6_len = sizeof(s6);
+ s6.sin6_addr = in6addr_loopback;
+ s6.sin6_port = 0;
+
+ s = (struct sockaddr_in *)&s6;
+ } else {
+ pf = PF_INET;
+ len = sizeof(s4);
+
+ s4.sin_family = pf;
+ s4.sin_len = sizeof(s4);
+ s4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ s4.sin_port = 0;
+
+ s = &s4;
+ }
+
+ if ((ssock = socket(pf, SOCK_STREAM, 0)) == -1)
+ atf_tc_fail("creating server socket");
+
+ fcntl(ssock, F_SETFL, O_NONBLOCK);
+
+ if ((bind(ssock, (struct sockaddr *)s, len) == -1))
+ atf_tc_fail("binding to localhost");
+
+ getsockname(ssock, (struct sockaddr *)s, &len);
+
+ listen(ssock, 1);
+
+ if ((csock = socket(pf, SOCK_STREAM, 0)) == -1)
+ atf_tc_fail("creating client socket");
+
+ if (connect(csock, (struct sockaddr *)s, len) == -1)
+ atf_tc_fail("connecting to server instance");
+
+ if (getsockopt(csock, IPPROTO_TCP, TCP_MD5SIG, &opt, &len) == -1)
+ atf_tc_fail("getsockopt");
+
+ close(csock);
+ close(ssock);
+
+ atf_tc_pass();
+}
+
+ATF_TC(tcp_md5_getsockopt_v4);
+ATF_TC_HEAD(tcp_md5_getsockopt_v4, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test getsockopt for TCP MD5 SIG (IPv4)");
+}
+
+ATF_TC_BODY(tcp_md5_getsockopt_v4, tc)
+{
+ test_tcp_md5_getsockopt(0);
+}
+
+ATF_TC(tcp_md5_getsockopt_v6);
+ATF_TC_HEAD(tcp_md5_getsockopt_v6, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test getsockopt for TCP MD5 SIG (IPv6)");
+}
+
+ATF_TC_BODY(tcp_md5_getsockopt_v6, tc)
+{
+ test_tcp_md5_getsockopt(1);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, tcp_md5_getsockopt_v4);
+ ATF_TP_ADD_TC(tp, tcp_md5_getsockopt_v6);
+
+ return atf_no_error();
+}
diff --git a/tests/sys/netinet/tcp_user_cookie.c b/tests/sys/netinet/tcp_user_cookie.c
new file mode 100644
index 000000000000..51df4860f6bd
--- /dev/null
+++ b/tests/sys/netinet/tcp_user_cookie.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2016 Limelight Networks
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * Authors: George Neville-Neil
+ */
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <err.h>
+#include <sysexits.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define buflen 80
+
+/*
+ * Setup a TCP server listening on a port for connections, all of
+ * which subseuqently have their user cookie set.
+ */
+int
+main(int argc, char **argv)
+{
+ struct sockaddr_in srv;
+ int sock, accepted, port, cookie;
+ int ret;
+ char recvbuf[buflen];
+
+ if (argc != 3) {
+ fprintf(stderr, "Usage: %s port cookie\n", argv[0]);
+ exit(2);
+ }
+
+ port = atoi(argv[1]);
+ cookie = atoi(argv[2]);
+
+ sock = socket(PF_INET, SOCK_STREAM, 0);
+ if (sock < 0)
+ err(EXIT_FAILURE, "socket");
+
+ memset(&srv, 0, sizeof(srv));
+ srv.sin_len = sizeof(srv);
+ srv.sin_family = AF_INET;
+ srv.sin_port = htons(port);
+ srv.sin_addr.s_addr = INADDR_ANY;
+
+ if (bind(sock, (struct sockaddr *)&srv, srv.sin_len) < 0)
+ err(EX_OSERR, "failed to bind to port %d", port);
+
+ if (listen(sock, 5) < 0)
+ err(EX_OSERR, "failed to listen on socket");
+
+ ret = setsockopt(sock, SOL_SOCKET, SO_USER_COOKIE, &cookie, sizeof(cookie));
+ if (ret < 0)
+ err(EX_OSERR, "setsockopt(SO_USER_COOKIE)");
+
+ while (1) {
+
+ accepted = accept(sock, NULL, 0);
+
+ if (accepted < 0)
+ err(EX_OSERR, "accept failed");
+
+ ret = setsockopt(accepted, SOL_SOCKET, SO_USER_COOKIE,
+ &cookie, sizeof(cookie));
+ if (ret < 0)
+ err(EX_OSERR, "setsockopt(SO_USER_COOKIE)");
+
+ ret = read(accepted, &recvbuf, buflen);
+
+ if (ret < 0)
+ warn("failed read");
+
+ close(accepted);
+ }
+
+ return (0);
+}
diff --git a/tests/sys/netinet/udp_bindings.c b/tests/sys/netinet/udp_bindings.c
new file mode 100644
index 000000000000..b05967d4b080
--- /dev/null
+++ b/tests/sys/netinet/udp_bindings.c
@@ -0,0 +1,249 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Gleb Smirnoff <glebius@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/jail.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <ifaddrs.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+static const char buf[] = "Hello";
+
+static void
+sendtolocalhost(int s)
+{
+ struct sockaddr_in dst = {
+ .sin_family = AF_INET,
+ .sin_len = sizeof(struct sockaddr_in),
+ .sin_addr = { htonl(INADDR_LOOPBACK) },
+ .sin_port = htons(1638),
+ };
+
+ ATF_REQUIRE(sendto(s, buf, sizeof(buf), 0, (struct sockaddr *)&dst,
+ sizeof(dst)) == sizeof(buf));
+}
+
+/*
+ * Echo back to the sender its own address in payload.
+ */
+static void *
+echo(void *arg)
+{
+ int s = *(int *)arg;
+ struct sockaddr_in sin;
+ socklen_t slen = sizeof(sin);
+ char rbuf[sizeof(buf)];
+
+ ATF_REQUIRE(recvfrom(s, &rbuf, sizeof(rbuf), 0, (struct sockaddr *)&sin,
+ &slen) == sizeof(rbuf));
+ printf("Echo to %s:%u\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
+ ATF_REQUIRE(sendto(s, &sin, sizeof(sin), 0, (struct sockaddr *)&sin,
+ sizeof(sin)) == sizeof(sin));
+ return (NULL);
+}
+
+/*
+ * Cycle through local addresses (normally there should be at least two
+ * different IPv4 ones), and communicate to the echo server checking both
+ * IP_SENDSRCADDR and IP_RECVDSTADDR. Use same cmsg buffer for both send
+ * and receive operation, this is a suggested in manual, given that
+ * IP_RECVDSTADDR == IP_SENDSRCADDR.
+ * At the setup phase check that IP_SENDSRCADDR doesn't work on unbound socket.
+ */
+ATF_TC_WITHOUT_HEAD(IP_SENDSRCADDR);
+ATF_TC_BODY(IP_SENDSRCADDR, tc)
+{
+ struct sockaddr_in srv = {
+ .sin_family = AF_INET,
+ .sin_len = sizeof(struct sockaddr_in),
+ }, dst;
+ char cbuf[CMSG_SPACE(sizeof(struct in_addr))];
+ struct iovec iov = {
+ .iov_base = __DECONST(char *, buf),
+ .iov_len = sizeof(buf),
+ };
+ struct iovec riov = {
+ .iov_base = &dst,
+ .iov_len = sizeof(dst),
+ };
+ struct msghdr msg = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_name = &srv,
+ .msg_namelen = sizeof(srv),
+ .msg_control = cbuf,
+ .msg_controllen = sizeof(cbuf),
+ };
+ struct msghdr rmsg = {
+ .msg_iov = &riov,
+ .msg_iovlen = 1,
+ .msg_control = cbuf,
+ .msg_controllen = sizeof(cbuf),
+ };
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
+ *cmsg = (struct cmsghdr) {
+ .cmsg_level = IPPROTO_IP,
+ .cmsg_type = IP_SENDSRCADDR,
+ .cmsg_len = CMSG_LEN(sizeof(struct in_addr)),
+ };
+ socklen_t slen = sizeof(struct sockaddr_in);
+ struct ifaddrs *ifa0, *ifa;
+ pthread_t tid;
+ int s, e;
+
+ /* First check that IP_SENDSRCADDR doesn't work on an unbound socket. */
+ ATF_REQUIRE((s = socket(PF_INET, SOCK_DGRAM, 0)) > 0);
+ ATF_REQUIRE_MSG(sendmsg(s, &msg, 0) == -1 && errno == EINVAL,
+ "sendmsg(.cmsg_type = IP_SENDSRCADDR), errno %d", errno);
+
+ /* Bind to random ports both sender and echo server. */
+ ATF_REQUIRE(bind(s, (struct sockaddr *)&srv, sizeof(srv)) == 0);
+ ATF_REQUIRE((e = socket(PF_INET, SOCK_DGRAM, 0)) > 0);
+ ATF_REQUIRE(bind(e, (struct sockaddr *)&srv, sizeof(srv)) == 0);
+ ATF_REQUIRE(getsockname(e, (struct sockaddr *)&srv, &slen) == 0);
+ srv.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ ATF_REQUIRE(getifaddrs(&ifa0) == 0);
+ for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) {
+ struct sockaddr_in src;
+ struct in_addr vrf;
+
+ if (ifa->ifa_addr->sa_family != AF_INET)
+ continue;
+ memcpy(&src, ifa->ifa_addr, sizeof(src));
+ printf("Sending from %s\n", inet_ntoa(src.sin_addr));
+ ATF_REQUIRE(pthread_create(&tid, NULL, echo, &e) == 0);
+ memcpy(CMSG_DATA(cmsg), &src.sin_addr, sizeof(src.sin_addr));
+ ATF_REQUIRE(sendmsg(s, &msg, 0) == sizeof(buf));
+ ATF_REQUIRE(recvmsg(s, &rmsg, 0) == sizeof(struct sockaddr_in));
+ memcpy(&vrf, CMSG_DATA(cmsg), sizeof(vrf));
+ ATF_REQUIRE_MSG(dst.sin_addr.s_addr == src.sin_addr.s_addr,
+ "Sent from %s, but echo server reports %s",
+ inet_ntoa(src.sin_addr), inet_ntoa(dst.sin_addr));
+ ATF_REQUIRE_MSG(vrf.s_addr == src.sin_addr.s_addr,
+ "Sent from %s, but IP_RECVDSTADDR reports %s",
+ inet_ntoa(src.sin_addr), inet_ntoa(vrf));
+ ATF_REQUIRE(pthread_join(tid, NULL) == 0);
+ }
+
+ freeifaddrs(ifa0);
+ close(s);
+ close(e);
+}
+
+/*
+ * Check gethostname(2) on a newborn socket, and then on an unconnected, but
+ * used socket. The first shall return all-zeroes, and second one should
+ * return us our assigned port.
+ */
+ATF_TC_WITHOUT_HEAD(gethostname);
+ATF_TC_BODY(gethostname, tc)
+{
+ struct sockaddr_in sin;
+ socklen_t slen = sizeof(sin);
+ int s;
+
+ ATF_REQUIRE((s = socket(PF_INET, SOCK_DGRAM, 0)) > 0);
+ ATF_REQUIRE(getsockname(s, (struct sockaddr *)&sin, &slen) == 0);
+ ATF_REQUIRE_MSG(sin.sin_addr.s_addr == INADDR_ANY && sin.sin_port == 0,
+ "newborn socket name %s:%u", inet_ntoa(sin.sin_addr),
+ ntohs(sin.sin_port));
+ sendtolocalhost(s);
+ ATF_REQUIRE(getsockname(s, (struct sockaddr *)&sin, &slen) == 0);
+ ATF_REQUIRE_MSG(sin.sin_addr.s_addr == INADDR_ANY && sin.sin_port != 0,
+ "used unconnected socket name %s:%u", inet_ntoa(sin.sin_addr),
+ ntohs(sin.sin_port));
+ close(s);
+}
+
+ATF_TC_WITHOUT_HEAD(gethostname_jailed);
+ATF_TC_BODY(gethostname_jailed, tc)
+{
+ struct in_addr laddr = { htonl(INADDR_LOOPBACK) };
+ struct jail jconf = {
+ .version = JAIL_API_VERSION,
+ .path = __DECONST(char *, "/"),
+ .hostname = __DECONST(char *,"test"),
+ .ip4s = 1,
+ .ip4 = &laddr,
+ };
+ struct sockaddr_in sin;
+ socklen_t slen = sizeof(sin);
+ int s;
+
+ ATF_REQUIRE(jail(&jconf) > 0);
+ ATF_REQUIRE((s = socket(PF_INET, SOCK_DGRAM, 0)) > 0);
+ sendtolocalhost(s);
+ ATF_REQUIRE(getsockname(s, (struct sockaddr *)&sin, &slen) == 0);
+ ATF_REQUIRE_MSG(sin.sin_addr.s_addr == laddr.s_addr &&
+ sin.sin_port != 0,
+ "jailed unconnected socket name %s:%u", inet_ntoa(sin.sin_addr),
+ ntohs(sin.sin_port));
+ close(s);
+}
+
+/*
+ * See bug 274009.
+ */
+ATF_TC_WITHOUT_HEAD(v4mapped);
+ATF_TC_BODY(v4mapped, tc)
+{
+ struct sockaddr_in6 sa6 = {
+ .sin6_family = AF_INET6,
+ .sin6_len = sizeof(struct sockaddr_in6),
+ .sin6_port = htons(1),
+ };
+ int s;
+
+ ATF_REQUIRE((s = socket(PF_INET6, SOCK_DGRAM, 0)) > 0);
+ ATF_REQUIRE(setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &(int){0},
+ sizeof(int)) == 0);
+ ATF_REQUIRE(inet_pton(AF_INET6, "::ffff:127.0.0.1", &(sa6.sin6_addr))
+ == 1);
+ ATF_REQUIRE(sendto(s, buf, sizeof(buf), 0, (struct sockaddr *)&sa6,
+ sizeof(sa6)) == sizeof(buf));
+ close(s);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, v4mapped);
+ ATF_TP_ADD_TC(tp, gethostname);
+ ATF_TP_ADD_TC(tp, gethostname_jailed);
+ ATF_TP_ADD_TC(tp, IP_SENDSRCADDR);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/netinet/udp_dontroute.c b/tests/sys/netinet/udp_dontroute.c
new file mode 100644
index 000000000000..ba6d4ec6236c
--- /dev/null
+++ b/tests/sys/netinet/udp_dontroute.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2014 Spectra Logic Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * Authors: Alan Somers (Spectra Logic Corporation)
+ */
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * Sends a single UDP packet to the provided address, with SO_DONTROUTE set
+ * I couldn't find a way to do this with builtin utilities like nc(1)
+ */
+int
+main(int argc, char **argv)
+{
+ struct sockaddr_storage dst;
+ int s, t;
+ int opt;
+ int ret;
+ ssize_t len;
+ const char* sendbuf = "Hello, World!";
+ const size_t buflen = 80;
+ char recvbuf[buflen];
+ bool v6 = false;
+ const char *addr, *tapdev;
+ const uint16_t port = 46120;
+
+ bzero(&dst, sizeof(dst));
+ if (argc < 3 || argc > 4) {
+ fprintf(stderr, "Usage: %s [-6] ip_address tapdev\n", argv[0]);
+ exit(2);
+ }
+
+ if (strcmp("-6", argv[1]) == 0) {
+ v6 = true;
+ addr = argv[2];
+ tapdev = argv[3];
+ } else {
+ addr = argv[1];
+ tapdev = argv[2];
+ }
+
+ t = open(tapdev, O_RDWR | O_NONBLOCK);
+ if (t < 0)
+ err(EXIT_FAILURE, "open");
+
+ if (v6)
+ s = socket(PF_INET6, SOCK_DGRAM, 0);
+ else
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0)
+ err(EXIT_FAILURE, "socket");
+ opt = 1;
+
+ ret = setsockopt(s, SOL_SOCKET, SO_DONTROUTE, &opt, sizeof(opt));
+ if (ret == -1)
+ err(EXIT_FAILURE, "setsockopt(SO_DONTROUTE)");
+
+ if (v6) {
+ struct sockaddr_in6 *dst6 = ((struct sockaddr_in6*)&dst);
+
+ dst.ss_len = sizeof(struct sockaddr_in6);
+ dst.ss_family = AF_INET6;
+ dst6->sin6_port = htons(port);
+ ret = inet_pton(AF_INET6, addr, &dst6->sin6_addr);
+ } else {
+ struct sockaddr_in *dst4 = ((struct sockaddr_in*)&dst);
+
+ dst.ss_len = sizeof(struct sockaddr_in);
+ dst.ss_family = AF_INET;
+ dst4->sin_port = htons(port);
+ ret = inet_pton(AF_INET, addr, &dst4->sin_addr);
+ }
+ if (ret != 1)
+ err(EXIT_FAILURE, "inet_pton returned %d", ret);
+
+ ret = sendto(s, sendbuf, strlen(sendbuf), 0, (struct sockaddr*)&dst,
+ dst.ss_len);
+ if (ret == -1)
+ err(EXIT_FAILURE, "sendto");
+
+ /* Verify that the packet went to the desired tap device */
+
+ len = read(t, recvbuf, buflen);
+ if (len == 0)
+ errx(EXIT_FAILURE, "read returned EOF");
+ else if (len < 0 && errno == EAGAIN)
+ errx(EXIT_FAILURE, "Did not receive any packets");
+ else if (len < 0)
+ err(EXIT_FAILURE, "read");
+
+ /*
+ * If read returned anything at all, consider it a success. The packet
+ * should be an Ethernet frame containing an ARP request for
+ * ip_address. We won't bother to decode it
+ */
+ return (0);
+}
diff --git a/tests/sys/netinet/udp_io.c b/tests/sys/netinet/udp_io.c
new file mode 100644
index 000000000000..04f9bf56ed02
--- /dev/null
+++ b/tests/sys/netinet/udp_io.c
@@ -0,0 +1,140 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Gleb Smirnoff <glebius@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+/*
+ * Create a pair of UDP sockets. The first one is bound to a local
+ * address and the second one is connected to it.
+ */
+static void
+udp_socketpair(int *s)
+{
+ struct sockaddr_in sin = {
+ .sin_family = AF_INET,
+ .sin_len = sizeof(sin),
+ };
+ socklen_t slen = sizeof(sin);
+ int b, c;
+
+ ATF_REQUIRE((b = socket(PF_INET, SOCK_DGRAM, 0)) > 0);
+ ATF_REQUIRE((c = socket(PF_INET, SOCK_DGRAM, 0)) > 0);
+ ATF_REQUIRE(bind(b, (struct sockaddr *)&sin, sizeof(sin)) == 0);
+ ATF_REQUIRE(getsockname(b, (struct sockaddr *)&sin, &slen) == 0);
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ ATF_REQUIRE(connect(c, (struct sockaddr *)&sin, sizeof(sin)) == 0);
+
+ s[0] = b;
+ s[1] = c;
+}
+
+/*
+ * Check MSG_TRUNC.
+ */
+ATF_TC_WITHOUT_HEAD(trunc);
+ATF_TC_BODY(trunc, tc)
+{
+ char sbuf[] = "Hello, peer!", rbuf[sizeof(sbuf)];
+ struct iovec iov = {
+ .iov_base = sbuf,
+ .iov_len = sizeof(sbuf),
+ };
+ struct msghdr msg = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ int s[2];
+ u_int n;
+
+ udp_socketpair(s);
+
+ ATF_REQUIRE(sendmsg(s[1], &msg, 0) == sizeof(sbuf));
+ n = (arc4random() % (sizeof(sbuf) - 1)) + 1;
+ iov.iov_base = rbuf;
+ iov.iov_len = n;
+ ATF_REQUIRE(recvmsg(s[0], &msg, 0) == (ssize_t)n);
+ ATF_REQUIRE(msg.msg_flags == MSG_TRUNC);
+ ATF_REQUIRE(strncmp(sbuf, rbuf, n) == 0);
+ iov.iov_len = sizeof(rbuf);
+ ATF_REQUIRE(recvmsg(s[0], &msg, MSG_DONTWAIT) == -1);
+ ATF_REQUIRE(errno == EAGAIN);
+
+ close(s[0]);
+ close(s[1]);
+}
+
+/*
+ * Check MSG_PEEK.
+ */
+ATF_TC_WITHOUT_HEAD(peek);
+ATF_TC_BODY(peek, tc)
+{
+ char sbuf[] = "Hello, peer!", rbuf[sizeof(sbuf)];
+ struct iovec iov = {
+ .iov_base = sbuf,
+ .iov_len = sizeof(sbuf),
+ };
+ struct msghdr msg = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ int s[2];
+ u_int n;
+
+ udp_socketpair(s);
+
+ ATF_REQUIRE(sendmsg(s[1], &msg, 0) == sizeof(sbuf));
+ iov.iov_base = rbuf;
+ for (int i = 0; i < 10; i++) {
+ n = (arc4random() % sizeof(sbuf)) + 1;
+ iov.iov_len = n;
+ ATF_REQUIRE(recvmsg(s[0], &msg, MSG_PEEK) == (ssize_t)n);
+ if (n < sizeof(sbuf))
+ ATF_REQUIRE(msg.msg_flags == (MSG_PEEK | MSG_TRUNC));
+ else
+ ATF_REQUIRE(msg.msg_flags == MSG_PEEK);
+ ATF_REQUIRE(strncmp(sbuf, rbuf, n) == 0);
+ }
+
+ close(s[0]);
+ close(s[1]);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, trunc);
+ ATF_TP_ADD_TC(tp, peek);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/netinet6/Makefile b/tests/sys/netinet6/Makefile
new file mode 100644
index 000000000000..26f1a18a8d32
--- /dev/null
+++ b/tests/sys/netinet6/Makefile
@@ -0,0 +1,54 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/netinet6
+FILESDIR= ${TESTSDIR}
+
+ATF_TESTS_PYTEST= test_ip6_output.py
+ATF_TESTS_SH= exthdr \
+ mld \
+ scapyi386 \
+ redirect \
+ divert \
+ forward6 \
+ output6 \
+ lpm6 \
+ fibs6 \
+ ndp \
+ proxy_ndp \
+ addr6
+
+TEST_METADATA.divert+= execenv="jail" \
+ execenv_jail_params="vnet allow.raw_sockets"
+TEST_METADATA.exthdr+= execenv="jail" \
+ execenv_jail_params="vnet allow.raw_sockets"
+TEST_METADATA.forward6+= execenv="jail" \
+ execenv_jail_params="vnet allow.raw_sockets"
+TEST_METADATA.ndp+= execenv="jail" \
+ execenv_jail_params="vnet allow.raw_sockets"
+TEST_METADATA.output6+= execenv="jail" \
+ execenv_jail_params="vnet allow.raw_sockets" \
+ required_programs="python"
+TEST_METADATA.proxy_ndp+= execenv="jail" \
+ execenv_jail_params="vnet allow.raw_sockets"
+TEST_METADATA.redirect+= execenv="jail" \
+ execenv_jail_params="vnet allow.raw_sockets"
+TEST_METADATA.scapyi386+= execenv="jail" \
+ execenv_jail_params="vnet allow.raw_sockets"
+TEST_METADATA.addr6+= execenv="jail" \
+ execenv_jail_params="vnet allow.raw_sockets"
+
+${PACKAGE}FILES+= exthdr.py \
+ mld.py \
+ scapyi386.py \
+ ra.py \
+ redirect.py
+
+${PACKAGE}FILESMODE_exthdr.py= 0555
+${PACKAGE}FILESMODE_mld.py= 0555
+${PACKAGE}FILESMODE_scapyi386.py=0555
+${PACKAGE}FILESMODE_ra.py=0555
+${PACKAGE}FILESMODE_redirect.py=0555
+
+TESTS_SUBDIRS+= frag6
+
+.include <bsd.test.mk>
diff --git a/tests/sys/netinet6/addr6.sh b/tests/sys/netinet6/addr6.sh
new file mode 100755
index 000000000000..6fd66d5aa0c7
--- /dev/null
+++ b/tests/sys/netinet6/addr6.sh
@@ -0,0 +1,70 @@
+#!/usr/bin/env atf-sh
+#-
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2025 Lexi Winter.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+atf_test_case "addr6_invalid_addr" "cleanup"
+addr6_invalid_addr_head()
+{
+ atf_set descr "adding an invalid IPv6 address returns an error"
+ atf_set require.user root
+}
+
+addr6_invalid_addr_body()
+{
+ vnet_init
+
+ ep=$(vnet_mkepair)
+ atf_check -s exit:0 ifconfig ${ep}a inet6 2001:db8::1/128
+ atf_check -s exit:1 -e ignore ifconfig ${ep}a inet6 2001:db8::1/127 alias
+}
+
+addr6_invalid_addr_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "anycast_raw_addr" "cleanup"
+anycast_raw_addr_head()
+{
+ atf_set descr "a raw socket can bind to an anycast address"
+ atf_set require.user root
+}
+
+anycast_raw_addr_body()
+{
+ # lo0 needs to be up in the test jail for this test to work
+ ifconfig lo0 up
+
+ netif=$(ifconfig lo create)
+ echo $netif >netif
+ atf_check -s exit:0 ifconfig $netif inet6 2001:db8::1/128 up
+ atf_check -s exit:0 ifconfig $netif inet6 2001:db8::2/128 anycast
+ atf_check -s exit:0 -o ignore ping -c1 -S 2001:db8::2 2001:db8::1
+}
+
+anycast_raw_addr_cleanup()
+{
+ ifconfig $(cat netif) destroy
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "addr6_invalid_addr"
+ atf_add_test_case "anycast_raw_addr"
+}
diff --git a/tests/sys/netinet6/divert.sh b/tests/sys/netinet6/divert.sh
new file mode 100755
index 000000000000..e2dc3e26d97e
--- /dev/null
+++ b/tests/sys/netinet6/divert.sh
@@ -0,0 +1,101 @@
+#!/usr/bin/env atf-sh
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Alexander V. Chernikov
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+load_divert_module() {
+ kldstat -q -m ipdivert
+ if [ $? -ne 0 ]; then
+ atf_skip "ipdivert module is not loaded"
+ fi
+}
+
+atf_test_case "ipdivert_ip6_output_remote_success" "cleanup"
+ipdivert_ip6_output_remote_success_head() {
+
+ atf_set descr 'Test valid IPv6 redirect'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+ipdivert_ip6_output_remote_success_body() {
+ ids=65530
+ id=`printf "%x" ${ids}`
+ if [ $$ -gt 65535 ]; then
+ xl=`printf "%x" $(($$ - 65535))`
+ yl="1"
+ else
+ xl=`printf "%x" $$`
+ yl=""
+ fi
+
+ vnet_init
+ load_divert_module
+
+ ip6a="2001:db8:6666:0000:${yl}:${id}:1:${xl}"
+ ip6b="2001:db8:6666:0000:${yl}:${id}:2:${xl}"
+
+ script_name="../common/divert.py"
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a up
+ ifconfig ${epair}a inet6 ${ip6a}/64
+
+ jname="v6t-${id}-${yl}-${xl}"
+ vnet_mkjail ${jname} ${epair}b
+ jexec ${jname} ifconfig ${epair}b up
+ jexec ${jname} ifconfig ${epair}b inet6 ${ip6b}/64
+
+ # wait for DAD to complete
+ sleep 2
+
+ # echo "LOCAL: ${local_ll_ip} ${local_ll_mac}"
+ # echo "REMOTE: ${remote_rtr_ll_ip} ${remote_rtr_mac}"
+
+ atf_check -s exit:0 $(atf_get_srcdir)/${script_name} \
+ --dip ${ip6b} --test_name ipdivert_ip6_output_remote_success
+
+ count=`jexec ${jname} netstat -s -p icmp6 | grep 'Input histogram:' -A1 | grep -c 'echo:'`
+ # Verify redirect got installed
+ atf_check_equal "1" "${count}"
+}
+
+ipdivert_ip6_output_remote_success_cleanup() {
+
+ vnet_cleanup
+}
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case "ipdivert_ip6_output_remote_success"
+}
+
+# end
+
diff --git a/tests/sys/netinet6/exthdr.py b/tests/sys/netinet6/exthdr.py
new file mode 100644
index 000000000000..f4927a19e43a
--- /dev/null
+++ b/tests/sys/netinet6/exthdr.py
@@ -0,0 +1,274 @@
+#!/usr/bin/env python
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import socket
+import sys
+import frag6.sniffer as Sniffer
+from time import sleep
+
+def check_icmp6_error_dst_unreach_noport(args, packet):
+ ip6 = packet.getlayer(sp.IPv6)
+ if not ip6:
+ return False
+ oip6 = sp.IPv6(src=args.src[0], dst=args.to[0])
+ if ip6.dst != oip6.src:
+ return False
+ icmp6 = packet.getlayer(sp.ICMPv6DestUnreach)
+ if not icmp6:
+ return False
+ # ICMP6_DST_UNREACH_NOPORT 4
+ if icmp6.code != 4:
+ return False
+ # Should we check the payload as well?
+ # We are running in a very isolated environment and nothing else
+ # should trigger an ICMPv6 Dest Unreach / Port Unreach so leave it.
+ #icmp6.display()
+ return True
+
+def check_icmp6_error_paramprob_header(args, packet):
+ ip6 = packet.getlayer(sp.IPv6)
+ if not ip6:
+ return False
+ oip6 = sp.IPv6(src=args.src[0], dst=args.to[0])
+ if ip6.dst != oip6.src:
+ return False
+ icmp6 = packet.getlayer(sp.ICMPv6ParamProblem)
+ if not icmp6:
+ return False
+ # ICMP6_PARAMPROB_HEADER 0
+ if icmp6.code != 0:
+ return False
+ # Should we check the payload as well?
+ # We are running in a very isolated environment and nothing else
+ # should trigger an ICMPv6 Param Prob so leave it.
+ #icmp6.display()
+ return True
+
+def check_tcp_rst(args, packet):
+ ip6 = packet.getlayer(sp.IPv6)
+ if not ip6:
+ return False
+ oip6 = sp.IPv6(src=args.src[0], dst=args.to[0])
+ if ip6.dst != oip6.src:
+ return False
+ tcp = packet.getlayer(sp.TCP)
+ if not tcp:
+ return False
+ # Is TCP RST?
+ if tcp.flags & 0x04:
+ #tcp.display()
+ return True
+ return False
+
+def addExt(ext, h):
+ if h is None:
+ return ext
+ if ext is None:
+ ext = h
+ else:
+ ext = ext / h
+ return ext
+
+def getExtHdrs(args):
+ ext = None
+
+ # XXX-TODO Try to put them in an order which could make sense
+ # in real life packets and according to the RFCs.
+ if args.hbh:
+ hbh = sp.IPv6ExtHdrHopByHop(options = \
+ sp.PadN(optdata="\x00\x00\x00\x00\x00\x00"))
+ ext = addExt(ext, hbh)
+
+ if args.rh:
+ rh = sp.IPv6ExtHdrRouting(type = 0)
+ ext = addExt(ext, rh)
+
+ if args.frag6:
+ frag6 = sp.IPv6ExtHdrFragment(offset=0, m=0, id=0x1234)
+ ext = addExt(ext, frag6)
+
+ if args.esp:
+ # XXX TODO
+ esp = None
+ ext = addExt(ext, esp)
+
+ if args.ah:
+ # XXX TODO
+ ah = None
+ ext = addExt(ext, ah)
+
+ if args.dest:
+ dest = sp.IPv6ExtHdrDestOpt(options = \
+ sp.PadN(optdata="\x00\x00\x00\x00\x00\x00"))
+ ext = addExt(ext, dest)
+
+ if args.mobi:
+ # XXX TODO
+ mobi = None
+ ext = addExt(ext, mobi)
+
+ if args.hip:
+ # XXX TODO
+ hip = None
+ ext = addExt(ext, hip)
+
+ if args.shim6:
+ # XXX TODO
+ shim6 = None
+ ext = addExt(ext, shim6)
+
+ if args.proto253:
+ # XXX TODO
+ tft = None
+ ext = addExt(ext, tft)
+
+ if args.proto254:
+ # XXX TODO
+ tff = None
+ ext = addExt(ext, tff)
+
+ if args.hbhbad:
+ hbhbad = sp.IPv6ExtHdrHopByHop(options = \
+ sp.PadN(optdata="\x00\x00\x00\x00\x00\x00"))
+ ext = addExt(ext, hbhbad)
+
+ return ext
+
+def main():
+ parser = argparse.ArgumentParser("exthdr.py",
+ description="IPv6 extension header test tool")
+ parser.add_argument('--sendif', nargs=1,
+ required=True,
+ help='The interface through which the packet will be sent')
+ parser.add_argument('--recvif', nargs=1,
+ required=True,
+ help='The interface on which to check for the packet')
+ parser.add_argument('--src', nargs=1,
+ required=True,
+ help='The source IP address')
+ parser.add_argument('--to', nargs=1,
+ required=True,
+ help='The destination IP address')
+ parser.add_argument('--debug',
+ required=False, action='store_true',
+ help='Enable test debugging')
+ # Extension Headers
+ # See https://www.iana.org/assignments/ipv6-parameters/ipv6-parameters.xhtml
+ parser.add_argument('--hbh',
+ required=False, action='store_true',
+ help='Add IPv6 Hop-by-Hop Option')
+ parser.add_argument('--hbhbad',
+ required=False, action='store_true',
+ help='Add IPv6 Hop-by-Hop Option at an invalid position')
+ parser.add_argument('--rh',
+ required=False, action='store_true',
+ help='Add Routing Header for IPv6')
+ parser.add_argument('--frag6',
+ required=False, action='store_true',
+ help='Add Fragment Header for IPv6')
+ parser.add_argument('--esp',
+ required=False, action='store_true',
+ help='Add Encapsulating Security Payload')
+ parser.add_argument('--ah',
+ required=False, action='store_true',
+ help='Add Authentication Header')
+ parser.add_argument('--dest',
+ required=False, action='store_true',
+ help='Add Destination Options for IPv6')
+ parser.add_argument('--mobi',
+ required=False, action='store_true',
+ help='Add Mobility Header')
+ parser.add_argument('--hip',
+ required=False, action='store_true',
+ help='Add Host Identity Protocol')
+ parser.add_argument('--shim6',
+ required=False, action='store_true',
+ help='Add Shim6 Protocol')
+ parser.add_argument('--proto253',
+ required=False, action='store_true',
+ help='Use for experimentation and testing (253)')
+ parser.add_argument('--proto254',
+ required=False, action='store_true',
+ help='Use for experimentation and testing (254)')
+
+ args = parser.parse_args()
+
+ if args.hbhbad:
+ ok = 0
+ else:
+ ok = 1
+
+ ########################################################################
+ #
+ # Send IPv6 packets with one or more extension headers (combinations
+ # mmight not always make sense depending what user tells us).
+ # We are trying to cover the basic loop and passing mbufs on
+ # and making sure m_pullup() works.
+ # Try for at least UDP and TCP upper layer payloads.
+ #
+ # Expectations: no panics
+ # We are not testing for any other outcome here.
+ #
+ data = "6" * 88
+ udp = sp.UDP(dport=3456, sport=6543) / data
+ tcp = sp.TCP(dport=4567, sport=7654)
+ ip6 = sp.Ether() / sp.IPv6(src=args.src[0], dst=args.to[0])
+ for ulp in [ udp, tcp ]:
+ ext = getExtHdrs(args)
+ if ext is not None:
+ pkt = ip6 / ext / ulp
+ else:
+ pkt = ip6 / ulp
+ if args.debug :
+ pkt.display()
+ if not ok:
+ sc = check_icmp6_error_paramprob_header;
+ elif ulp == udp:
+ sc = check_icmp6_error_dst_unreach_noport;
+ elif ulp == tcp:
+ sc = check_tcp_rst;
+ else:
+ sys.exit(2)
+ # Start sniffing on recvif
+ sniffer = Sniffer.Sniffer(args, sc)
+ sp.sendp(pkt, iface=args.sendif[0], verbose=False)
+ sleep(0.10)
+ sniffer.setEnd()
+ sniffer.join()
+ if not sniffer.foundCorrectPacket:
+ sys.exit(not ok)
+
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netinet6/exthdr.sh b/tests/sys/netinet6/exthdr.sh
new file mode 100755
index 000000000000..3d866d85ea83
--- /dev/null
+++ b/tests/sys/netinet6/exthdr.sh
@@ -0,0 +1,125 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+atf_test_case "exthdr" "cleanup"
+exthdr_head() {
+
+ atf_set descr 'Test IPv6 extension header code paths'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+exthdr_body() {
+
+ ids=65533
+ id=`printf "%x" ${ids}`
+ if [ $$ -gt 65535 ]; then
+ xl=`printf "%x" $(($$ - 65535))`
+ yl="1"
+ else
+ xl=`printf "%x" $$`
+ yl=""
+ fi
+
+ vnet_init
+
+ ip6a="2001:db8:6666:0000:${yl}:${id}:1:${xl}"
+ ip6b="2001:db8:6666:0000:${yl}:${id}:2:${xl}"
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a up
+ ifconfig ${epair}a inet6 ${ip6a}/64
+
+ jname="v6t-${id}-${yl}-${xl}"
+ vnet_mkjail ${jname} ${epair}b
+ jexec ${jname} ifconfig ${epair}b up
+ jexec ${jname} ifconfig ${epair}b inet6 ${ip6b}/64
+
+ # Let IPv6 ND do its thing.
+ #ping6 -q -c 1 ff02::1%${epair}a
+ #ping6 -q -c 1 ${ip6b}
+ sleep 3
+
+ # Clear statistics.
+ jexec ${jname} netstat -z -s > /dev/null
+
+ # Run extension header tests.
+ pyname=$(atf_get ident)
+ pyname=${pyname%*_[0-9]}
+
+ atf_check -o ignore -s exit:0 ping -6 -c 3 -q -o ${ip6b}
+
+ atf_check -s exit:0 $(atf_get_srcdir)/${pyname}.py \
+ --sendif ${epair}a --recvif ${epair}a \
+ --src ${ip6a} --to ${ip6b}
+
+ atf_check -s exit:0 $(atf_get_srcdir)/${pyname}.py \
+ --sendif ${epair}a --recvif ${epair}a \
+ --src ${ip6a} --to ${ip6b} \
+ --hbh
+
+ atf_check -s exit:0 $(atf_get_srcdir)/${pyname}.py \
+ --sendif ${epair}a --recvif ${epair}a \
+ --src ${ip6a} --to ${ip6b} \
+ --rh
+
+ atf_check -s exit:0 $(atf_get_srcdir)/${pyname}.py \
+ --sendif ${epair}a --recvif ${epair}a \
+ --src ${ip6a} --to ${ip6b} \
+ --frag6
+
+ atf_check -s exit:0 $(atf_get_srcdir)/${pyname}.py \
+ --sendif ${epair}a --recvif ${epair}a \
+ --src ${ip6a} --to ${ip6b} \
+ --dest
+
+ atf_check -s exit:0 $(atf_get_srcdir)/${pyname}.py \
+ --sendif ${epair}a --recvif ${epair}a \
+ --src ${ip6a} --to ${ip6b} \
+ --hbh --dest
+
+ atf_check -s exit:1 $(atf_get_srcdir)/${pyname}.py \
+ --sendif ${epair}a --recvif ${epair}a \
+ --src ${ip6a} --to ${ip6b} \
+ --dest --hbhbad
+
+}
+
+exthdr_cleanup() {
+
+ vnet_cleanup
+}
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case "exthdr"
+}
+
+# end
diff --git a/tests/sys/netinet6/fibs6.sh b/tests/sys/netinet6/fibs6.sh
new file mode 100755
index 000000000000..1a6ba79c09fb
--- /dev/null
+++ b/tests/sys/netinet6/fibs6.sh
@@ -0,0 +1,91 @@
+#!/usr/bin/env atf-sh
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Alexander V. Chernikov
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+atf_test_case "fibs6_ifroutes1_success" "cleanup"
+fibs6_ifroutes1_success_head()
+{
+
+ atf_set descr 'Test IPv6 routes gets populated in the correct fib'
+ atf_set require.user root
+}
+
+fibs6_ifroutes1_success_body()
+{
+
+ vnet_init
+
+ net_dst="2001:db8::"
+ jname="v6t-fibs6_ifroutes1_success"
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail ${jname}a ${epair}a
+
+ jexec ${jname}a sysctl net.fibs=2
+
+ jexec ${jname}a ifconfig ${epair}a fib 1
+ jexec ${jname}a ifconfig ${epair}a inet6 ${net_dst}1/64
+ jexec ${jname}a ifconfig ${epair}a up
+
+
+ # wait for DAD to complete
+ while [ `jexec ${jname}a ifconfig | grep inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+ # wait for lo0 to come up
+ while [ `jexec ${jname}a ifconfig -l | grep -c lo0` = "0" ]; do
+ sleep 0.1
+ done
+ jexec ${jname}a setfib 1 netstat -rn
+ jexec ${jname}a ifconfig
+
+ lladdr=`jexec ${jname}a ifconfig ${epair}a inet6 | awk '$1~/inet6/ && $2~/^fe80/{print$2}'`
+
+ jexec ${jname}a setfib 1 route -6n get ${net_dst}/64
+ jexec ${jname}a setfib 1 route -6n get ${net_dst}1/128
+ jexec ${jname}a setfib 1 route -6n get fe80::%${epair}a/64
+ jexec ${jname}a setfib 1 route -6n get ${lladdr}
+
+ atf_check -s exit:0 -o ignore jexec ${jname}a setfib 1 route -6n get ${net_dst}/64
+ atf_check -s exit:0 -o ignore jexec ${jname}a setfib 1 route -6n get ${net_dst}1/128
+ atf_check -s exit:0 -o ignore jexec ${jname}a setfib 1 route -6n get fe80::%${epair}a/64
+ atf_check -s exit:0 -o ignore jexec ${jname}a setfib 1 route -6n get ${lladdr}
+}
+
+fibs6_ifroutes1_success_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "fibs6_ifroutes1_success"
+}
+
diff --git a/tests/sys/netinet6/forward6.sh b/tests/sys/netinet6/forward6.sh
new file mode 100755
index 000000000000..e4b027bf281a
--- /dev/null
+++ b/tests/sys/netinet6/forward6.sh
@@ -0,0 +1,535 @@
+#!/usr/bin/env atf-sh
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Alexander V. Chernikov
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+atf_test_case "fwd_ip6_gu_icmp_iface_fast_success" "cleanup"
+fwd_ip6_gu_icmp_iface_fast_success_head() {
+
+ atf_set descr 'Test valid IPv6 global unicast fast-forwarding to interface'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+fwd_ip6_gu_icmp_iface_fast_success_body() {
+
+ ids=65529
+ id=`printf "%x" ${ids}`
+ if [ $$ -gt 65535 ]; then
+ xl=`printf "%x" $(($$ - 65535))`
+ yl="1"
+ else
+ xl=`printf "%x" $$`
+ yl=""
+ fi
+
+ vnet_init
+
+ ip6a="2001:db8:6666:0000:${yl}:${id}:1:${xl}"
+ ip6b="2001:db8:6666:0000:${yl}:${id}:2:${xl}"
+ plen=96
+
+ src_ip="2001:db8:6666:0000:${yl}:${id}:3:${xl}"
+
+ script_name="../common/sender.py"
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a up
+ ifconfig ${epair}a inet6 ${ip6a}/${plen}
+
+ jname="v6t-${id}-${yl}-${xl}"
+ vnet_mkjail ${jname} ${epair}b
+ jexec ${jname} ifconfig ${epair}b up
+ jexec ${jname} ifconfig ${epair}b inet6 ${ip6b}/${plen}
+
+ jail_mac=`jexec ${jname} ifconfig ${epair}b ether | awk '$1~/ether/{print$2}'`
+
+ our_mac=`ifconfig ${epair}a ether | awk '$1~/ether/{print$2}'`
+
+ # wait for DAD to complete
+ while [ `jexec ${jname} ifconfig ${epair}b inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+ while [ `ifconfig ${epair}a inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+
+ jexec ${jname} sysctl net.inet6.ip6.forwarding=1
+ # As we're doing router-on-the-stick, turn sending IP redirects off:
+ jexec ${jname} sysctl net.inet6.ip6.redirect=0
+
+ atf_check -s exit:0 $(atf_get_srcdir)/${script_name} \
+ --test_name fwd_ip6_icmp \
+ --smac ${our_mac} --dmac ${jail_mac} \
+ --sip ${src_ip} --dip ${ip6a} \
+ --iface ${epair}a
+
+ # check counters are valid
+ atf_check -o match:'1 packet forwarded' jexec ${jname} netstat -sp ip6
+}
+
+fwd_ip6_gu_icmp_iface_fast_success_cleanup() {
+
+ vnet_cleanup
+}
+
+atf_test_case "fwd_ip6_gu_icmp_gw_gu_fast_success" "cleanup"
+fwd_ip6_gu_icmp_gw_gu_fast_success_head() {
+
+ atf_set descr 'Test valid IPv6 global unicast fast-forwarding to GU gw'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+fwd_ip6_gu_icmp_gw_gu_fast_success_body() {
+
+ ids=65528
+ id=`printf "%x" ${ids}`
+ if [ $$ -gt 65535 ]; then
+ xl=`printf "%x" $(($$ - 65535))`
+ yl="1"
+ else
+ xl=`printf "%x" $$`
+ yl=""
+ fi
+
+ vnet_init
+
+ ip6a="2001:db8:6666:0000:${yl}:${id}:1:${xl}"
+ ip6b="2001:db8:6666:0000:${yl}:${id}:2:${xl}"
+ plen=96
+
+ src_ip="2001:db8:6666:0000:${yl}:${id}:3:${xl}"
+ dst_ip="2001:db8:6666:0000:${yl}:${id}:4:${xl}"
+
+ script_name="../common/sender.py"
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a up
+ ifconfig ${epair}a inet6 ${ip6a}/${plen}
+
+ jname="v6t-${id}-${yl}-${xl}"
+ vnet_mkjail ${jname} ${epair}b
+ jexec ${jname} ifconfig ${epair}b up
+ jexec ${jname} ifconfig ${epair}b inet6 ${ip6b}/${plen}
+
+ jail_mac=`jexec ${jname} ifconfig ${epair}b ether | awk '$1~/ether/{print$2}'`
+
+ our_mac=`ifconfig ${epair}a ether | awk '$1~/ether/{print$2}'`
+
+ # wait for DAD to complete
+ while [ `jexec ${jname} ifconfig ${epair}b inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+ while [ `ifconfig ${epair}a inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+
+ # Add static route back to us
+ jexec ${jname} route add -6 -host ${dst_ip} ${ip6a}
+
+ jexec ${jname} sysctl net.inet6.ip6.forwarding=1
+ # As we're doing router-on-the-stick, turn sending IP redirects off:
+ jexec ${jname} sysctl net.inet6.ip6.redirect=0
+
+ atf_check -s exit:0 $(atf_get_srcdir)/${script_name} \
+ --test_name fwd_ip6_icmp \
+ --smac ${our_mac} --dmac ${jail_mac} \
+ --sip ${src_ip} --dip ${dst_ip} \
+ --iface ${epair}a
+
+ # check counters are valid
+ atf_check -o match:'1 packet forwarded' jexec ${jname} netstat -sp ip6
+}
+
+fwd_ip6_gu_icmp_gw_gu_fast_success_cleanup() {
+
+ vnet_cleanup
+}
+
+atf_test_case "fwd_ip6_gu_icmp_gw_ll_fast_success" "cleanup"
+fwd_ip6_gu_icmp_gw_ll_fast_success_head() {
+
+ atf_set descr 'Test valid IPv6 global unicast fast-forwarding to LL gw'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+fwd_ip6_gu_icmp_gw_ll_fast_success_body() {
+
+ ids=65527
+ id=`printf "%x" ${ids}`
+ if [ $$ -gt 65535 ]; then
+ xl=`printf "%x" $(($$ - 65535))`
+ yl="1"
+ else
+ xl=`printf "%x" $$`
+ yl=""
+ fi
+
+ vnet_init
+
+ ip6a="2001:db8:6666:0000:${yl}:${id}:1:${xl}"
+ ip6b="2001:db8:6666:0000:${yl}:${id}:2:${xl}"
+ plen=96
+
+ src_ip="2001:db8:6666:0000:${yl}:${id}:3:${xl}"
+ dst_ip="2001:db8:6666:0000:${yl}:${id}:4:${xl}"
+
+ script_name="../common/sender.py"
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a up
+ ifconfig ${epair}a inet6 ${ip6a}/${plen}
+
+ jname="v6t-${id}-${yl}-${xl}"
+ vnet_mkjail ${jname} ${epair}b
+ jexec ${jname} ifconfig ${epair}b up
+ jexec ${jname} ifconfig ${epair}b inet6 ${ip6b}/${plen}
+
+ jail_mac=`jexec ${jname} ifconfig ${epair}b ether | awk '$1~/ether/{print$2}'`
+
+ our_mac=`ifconfig ${epair}a ether | awk '$1~/ether/{print$2}'`
+ our_ll_ip=`ifconfig ${epair}a inet6 | awk '$1~/inet6/&& $2~/^fe80:/{print$2}' | awk -F% '{print$1}'`
+
+ # wait for DAD to complete
+ while [ `jexec ${jname} ifconfig ${epair}b inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+ while [ `ifconfig ${epair}a inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+
+ # Add static route back to us
+ atf_check -s exit:0 -o ignore jexec ${jname} route add -6 -host ${dst_ip} ${our_ll_ip}%${epair}b
+
+ jexec ${jname} sysctl net.inet6.ip6.forwarding=1
+ # As we're doing router-on-the-stick, turn sending IP redirects off:
+ jexec ${jname} sysctl net.inet6.ip6.redirect=0
+
+ atf_check -s exit:0 $(atf_get_srcdir)/${script_name} \
+ --test_name fwd_ip6_icmp \
+ --smac ${our_mac} --dmac ${jail_mac} \
+ --sip ${src_ip} --dip ${dst_ip} \
+ --iface ${epair}a
+
+ # check counters are valid
+ atf_check -o match:'1 packet forwarded' jexec ${jname} netstat -sp ip6
+}
+
+fwd_ip6_gu_icmp_gw_ll_fast_success_cleanup() {
+
+ vnet_cleanup
+}
+
+atf_test_case "fwd_ip6_gu_icmp_iface_slow_success" "cleanup"
+fwd_ip6_gu_icmp_iface_slow_success_head() {
+
+ atf_set descr 'Test valid IPv6 global unicast fast-forwarding to interface'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+fwd_ip6_gu_icmp_iface_slow_success_body() {
+
+ ids=65526
+ id=`printf "%x" ${ids}`
+ if [ $$ -gt 65535 ]; then
+ xl=`printf "%x" $(($$ - 65535))`
+ yl="1"
+ else
+ xl=`printf "%x" $$`
+ yl=""
+ fi
+
+ vnet_init
+
+ ip6a="2001:db8:6666:0000:${yl}:${id}:1:${xl}"
+ ip6b="2001:db8:6666:0000:${yl}:${id}:2:${xl}"
+ plen=96
+
+ src_ip="2001:db8:6666:0000:${yl}:${id}:3:${xl}"
+
+ script_name="../common/sender.py"
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a up
+ ifconfig ${epair}a inet6 ${ip6a}/${plen}
+
+ jname="v6t-${id}-${yl}-${xl}"
+ vnet_mkjail ${jname} ${epair}b
+ jexec ${jname} ifconfig ${epair}b up
+ jexec ${jname} ifconfig ${epair}b inet6 ${ip6b}/${plen}
+
+ jail_mac=`jexec ${jname} ifconfig ${epair}b ether | awk '$1~/ether/{print$2}'`
+
+ our_mac=`ifconfig ${epair}a ether | awk '$1~/ether/{print$2}'`
+
+ # wait for DAD to complete
+ while [ `jexec ${jname} ifconfig ${epair}b inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+ while [ `ifconfig ${epair}a inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+
+ jexec ${jname} sysctl net.inet6.ip6.forwarding=1
+ # Do not turn off route redirects to ensure slow path is on
+
+ atf_check -s exit:0 $(atf_get_srcdir)/${script_name} \
+ --test_name fwd_ip6_icmp \
+ --smac ${our_mac} --dmac ${jail_mac} \
+ --sip ${src_ip} --dip ${ip6a} \
+ --iface ${epair}a
+
+ # check counters are valid
+ atf_check -o match:'1 packet forwarded' jexec ${jname} netstat -sp ip6
+}
+
+fwd_ip6_gu_icmp_iface_slow_success_cleanup() {
+
+ vnet_cleanup
+}
+
+atf_test_case "fwd_ip6_gu_icmp_gw_gu_slow_success" "cleanup"
+fwd_ip6_gu_icmp_gw_gu_slow_success_head() {
+
+ atf_set descr 'Test valid IPv6 global unicast fast-forwarding to GU gw'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+fwd_ip6_gu_icmp_gw_gu_slow_success_body() {
+
+ ids=65525
+ id=`printf "%x" ${ids}`
+ if [ $$ -gt 65535 ]; then
+ xl=`printf "%x" $(($$ - 65535))`
+ yl="1"
+ else
+ xl=`printf "%x" $$`
+ yl=""
+ fi
+
+ vnet_init
+
+ ip6a="2001:db8:6666:0000:${yl}:${id}:1:${xl}"
+ ip6b="2001:db8:6666:0000:${yl}:${id}:2:${xl}"
+ plen=96
+
+ src_ip="2001:db8:6666:0000:${yl}:${id}:3:${xl}"
+ dst_ip="2001:db8:6666:0000:${yl}:${id}:4:${xl}"
+
+ script_name="../common/sender.py"
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a up
+ ifconfig ${epair}a inet6 ${ip6a}/${plen}
+
+ jname="v6t-${id}-${yl}-${xl}"
+ vnet_mkjail ${jname} ${epair}b
+ jexec ${jname} ifconfig ${epair}b up
+ jexec ${jname} ifconfig ${epair}b inet6 ${ip6b}/${plen}
+
+ jail_mac=`jexec ${jname} ifconfig ${epair}b ether | awk '$1~/ether/{print$2}'`
+
+ our_mac=`ifconfig ${epair}a ether | awk '$1~/ether/{print$2}'`
+
+ # wait for DAD to complete
+ while [ `jexec ${jname} ifconfig ${epair}b inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+ while [ `ifconfig ${epair}a inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+
+ # Add static route back to us
+ jexec ${jname} route add -6 -host ${dst_ip} ${ip6a}
+
+ jexec ${jname} sysctl net.inet6.ip6.forwarding=1
+ # Do not turn off route redirects to ensure slow path is on
+
+ atf_check -s exit:0 \
+ $(atf_get_srcdir)/${script_name} \
+ --test_name fwd_ip6_icmp \
+ --smac ${our_mac} --dmac ${jail_mac} \
+ --sip ${src_ip} --dip ${dst_ip} \
+ --iface ${epair}a
+ jexec ${jname} netstat -sp ip6
+
+ # check counters are valid
+ atf_check -o match:'1 packet forwarded' jexec ${jname} netstat -sp ip6
+}
+
+fwd_ip6_gu_icmp_gw_gu_slow_success_cleanup() {
+
+ vnet_cleanup
+}
+
+atf_test_case "fwd_ip6_gu_icmp_gw_ll_slow_success" "cleanup"
+fwd_ip6_gu_icmp_gw_ll_slow_success_head() {
+
+ atf_set descr 'Test valid IPv6 global unicast fast-forwarding to LL gw'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+fwd_ip6_gu_icmp_gw_ll_slow_success_body() {
+
+ ids=65524
+ id=`printf "%x" ${ids}`
+ if [ $$ -gt 65535 ]; then
+ xl=`printf "%x" $(($$ - 65535))`
+ yl="1"
+ else
+ xl=`printf "%x" $$`
+ yl=""
+ fi
+
+ vnet_init
+
+ ip6a="2001:db8:6666:0000:${yl}:${id}:1:${xl}"
+ ip6b="2001:db8:6666:0000:${yl}:${id}:2:${xl}"
+ plen=96
+
+ src_ip="2001:db8:6666:0000:${yl}:${id}:3:${xl}"
+ dst_ip="2001:db8:6666:0000:${yl}:${id}:4:${xl}"
+
+ script_name="../common/sender.py"
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a up
+ ifconfig ${epair}a inet6 ${ip6a}/${plen}
+
+ jname="v6t-${id}-${yl}-${xl}"
+ vnet_mkjail ${jname} ${epair}b
+ jexec ${jname} ifconfig ${epair}b up
+ jexec ${jname} ifconfig ${epair}b inet6 ${ip6b}/${plen}
+
+ jail_mac=`jexec ${jname} ifconfig ${epair}b ether | awk '$1~/ether/{print$2}'`
+
+ our_mac=`ifconfig ${epair}a ether | awk '$1~/ether/{print$2}'`
+ our_ll_ip=`ifconfig ${epair}a inet6 | awk '$1~/inet6/&& $2~/^fe80:/{print$2}' | awk -F% '{print$1}'`
+
+ # wait for DAD to complete
+ while [ `jexec ${jname} ifconfig ${epair}b inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+ while [ `ifconfig ${epair}a inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+
+ # Add static route back to us
+ atf_check -s exit:0 -o ignore jexec ${jname} route add -6 -host ${dst_ip} ${our_ll_ip}%${epair}b
+
+ jexec ${jname} sysctl net.inet6.ip6.forwarding=1
+ # Do not turn off route redirects to ensure slow path is on
+
+ atf_check -s exit:0 $(atf_get_srcdir)/${script_name} \
+ --test_name fwd_ip6_icmp \
+ --smac ${our_mac} --dmac ${jail_mac} \
+ --sip ${src_ip} --dip ${dst_ip} \
+ --iface ${epair}a
+
+ # check counters are valid
+ atf_check -o match:'1 packet forwarded' jexec ${jname} netstat -sp ip6
+}
+
+fwd_ip6_gu_icmp_gw_ll_slow_success_cleanup() {
+
+ vnet_cleanup
+}
+
+atf_test_case "fwd_ip6_blackhole" "cleanup"
+fwd_ip6_blackhole_head() {
+
+ atf_set descr 'Test blackhole routing'
+ atf_set require.user root
+}
+
+fwd_ip6_blackhole_body() {
+ jname="v6t-fwd_ip6_blackhole"
+
+ vnet_init
+
+ epair=$(vnet_mkepair)
+ epair_out=$(vnet_mkepair)
+
+ ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad
+
+ vnet_mkjail ${jname} ${epair}b ${epair_out}b
+ jexec ${jname} ifconfig lo0 inet6 ::1/128 up no_dad
+ jexec ${jname} ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad
+ jexec ${jname} ifconfig ${epair_out}b inet6 2001:db8:1::1/64 up no_dad
+ jexec ${jname} sysctl net.inet6.ip6.forwarding=1
+
+ route -6 add default 2001:db8::1
+
+ atf_check -s exit:2 -o ignore \
+ ping6 -c 1 -t 1 2001:db8:1::2
+ atf_check -s exit:0 -o match:"0 packets not forwardable" \
+ jexec ${jname} netstat -s -p ip6
+
+ # Create blackhole route
+ jexec ${jname} route -6 add 2001:db8:1::2 -blackhole
+
+ # Force slow path
+ jexec ${jname} sysctl net.inet6.ip6.redirect=1
+ atf_check -s exit:2 -o ignore \
+ ping6 -c 1 -t 1 2001:db8:1::2
+ atf_check -s exit:0 -o match:"1 packet not forwardable" \
+ jexec ${jname} netstat -s -p ip6
+
+ # Now try the fast path
+ jexec ${jname} sysctl net.inet6.ip6.redirect=0
+ atf_check -s exit:2 -o ignore \
+ ping6 -c 1 -t 1 2001:db8:1::2
+ atf_check -s exit:0 -o match:"2 packets not forwardable" \
+ jexec ${jname} netstat -s -p ip6
+}
+
+fwd_ip6_blackhole_cleanup() {
+
+ vnet_cleanup
+}
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case "fwd_ip6_gu_icmp_iface_fast_success"
+ atf_add_test_case "fwd_ip6_gu_icmp_gw_gu_fast_success"
+ atf_add_test_case "fwd_ip6_gu_icmp_gw_ll_fast_success"
+ atf_add_test_case "fwd_ip6_gu_icmp_iface_slow_success"
+ atf_add_test_case "fwd_ip6_gu_icmp_gw_gu_slow_success"
+ atf_add_test_case "fwd_ip6_gu_icmp_gw_ll_slow_success"
+ atf_add_test_case "fwd_ip6_blackhole"
+}
+
+# end
+
diff --git a/tests/sys/netinet6/frag6/Makefile b/tests/sys/netinet6/frag6/Makefile
new file mode 100644
index 000000000000..3fca0522e533
--- /dev/null
+++ b/tests/sys/netinet6/frag6/Makefile
@@ -0,0 +1,79 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/netinet6/frag6
+FILESDIR= ${TESTSDIR}
+
+# We split these up so they can run in parallel.
+# Seems kyua is not running the test cases from one file in parallel.
+# Otherwise we could cat the files together into one shell file.
+ATF_TESTS_SH= \
+ frag6_01 \
+ frag6_02 \
+ frag6_03 \
+ frag6_04 \
+ frag6_05 \
+ frag6_06 \
+ frag6_07 \
+ frag6_08 \
+ frag6_09 \
+ frag6_10 \
+ frag6_11 \
+ frag6_12 \
+ frag6_13 \
+ frag6_14 \
+ frag6_15 \
+ frag6_16 \
+ frag6_17 \
+ frag6_18 \
+ frag6_19 \
+ frag6_20
+
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= frag6.subr
+${PACKAGE}FILES+= sniffer.py
+${PACKAGE}FILES+= frag6_01.py
+${PACKAGE}FILES+= frag6_02.py
+${PACKAGE}FILES+= frag6_03.py
+${PACKAGE}FILES+= frag6_04.py
+${PACKAGE}FILES+= frag6_05.py
+${PACKAGE}FILES+= frag6_06.py
+${PACKAGE}FILES+= frag6_07.py
+${PACKAGE}FILES+= frag6_08.py
+${PACKAGE}FILES+= frag6_09.py
+${PACKAGE}FILES+= frag6_10.py
+${PACKAGE}FILES+= frag6_11.py
+${PACKAGE}FILES+= frag6_12.py
+${PACKAGE}FILES+= frag6_13.py
+${PACKAGE}FILES+= frag6_14.py
+${PACKAGE}FILES+= frag6_15.py
+${PACKAGE}FILES+= frag6_16.py
+${PACKAGE}FILES+= frag6_17.py
+${PACKAGE}FILES+= frag6_18.py
+${PACKAGE}FILES+= frag6_19.py
+${PACKAGE}FILES+= frag6_20.py
+
+${PACKAGE}FILESMODE_frag6.subr= 0444
+${PACKAGE}FILESMODE_sniffer.py= 0555
+${PACKAGE}FILESMODE_frag6_01.py= 0555
+${PACKAGE}FILESMODE_frag6_02.py= 0555
+${PACKAGE}FILESMODE_frag6_03.py= 0555
+${PACKAGE}FILESMODE_frag6_04.py= 0555
+${PACKAGE}FILESMODE_frag6_05.py= 0555
+${PACKAGE}FILESMODE_frag6_06.py= 0555
+${PACKAGE}FILESMODE_frag6_07.py= 0555
+${PACKAGE}FILESMODE_frag6_08.py= 0555
+${PACKAGE}FILESMODE_frag6_09.py= 0555
+${PACKAGE}FILESMODE_frag6_10.py= 0555
+${PACKAGE}FILESMODE_frag6_11.py= 0555
+${PACKAGE}FILESMODE_frag6_12.py= 0555
+${PACKAGE}FILESMODE_frag6_13.py= 0555
+${PACKAGE}FILESMODE_frag6_14.py= 0555
+${PACKAGE}FILESMODE_frag6_15.py= 0555
+${PACKAGE}FILESMODE_frag6_16.py= 0555
+${PACKAGE}FILESMODE_frag6_17.py= 0555
+${PACKAGE}FILESMODE_frag6_18.py= 0555
+${PACKAGE}FILESMODE_frag6_19.py= 0555
+${PACKAGE}FILESMODE_frag6_20.py= 0555
+
+.include <bsd.test.mk>
diff --git a/tests/sys/netinet6/frag6/frag6.subr b/tests/sys/netinet6/frag6/frag6.subr
new file mode 100644
index 000000000000..238c9619c398
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6.subr
@@ -0,0 +1,125 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/../../common/vnet.subr
+
+frag6_head()
+{
+ atf_set descr 'Test IPv6 fragmentation code'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+frag6_body()
+{
+ ids=${1:="65533"}
+ shift
+ id=`printf "%x" ${ids}`
+ if [ $$ -gt 65535 ]; then
+ xl=`printf "%x" $(($$ - 65535))`
+ yl="1"
+ else
+ xl=`printf "%x" $$`
+ yl=""
+ fi
+
+ vnet_init
+
+ ip6a="2001:db8:6666:6666:${yl}:${id}:1:${xl}"
+ ip6b="2001:db8:6666:6666:${yl}:${id}:2:${xl}"
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a up
+ ifconfig ${epair}a inet6 ${ip6a}/64
+
+ jname="v6t-${id}-${yl}-${xl}"
+ vnet_mkjail ${jname} ${epair}b
+ jexec ${jname} sysctl net.inet6.ip6.dad_count=0
+ jexec ${jname} ifconfig ${epair}b up
+ jexec ${jname} ifconfig ${epair}b inet6 ${ip6b}/64
+
+ # Set max fragment reassembly time to 2 seconds
+ jexec ${jname} sysctl net.inet6.ip6.fraglifetime_ms=2000
+
+ # Let IPv6 ND do its thing.
+ while [ `ifconfig ${epair}a inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+
+ # We need to try to make sure all expiry happened, otherwise there might
+ # be global fragments queued. (This still does not rule out that there
+ # are no other fragments queued anywhere else in the system).
+ i=0
+ while test $i -lt 60; do
+ nf=`sysctl -n net.inet6.ip6.frag6_nfrags`
+ case ${nf} in
+ 0) break ;;
+ esac
+ sleep 1
+ i=$((i + 1))
+ done
+ case ${nf} in
+ 0) ;;
+ *) atf_fail "Global frag6_nfrags count is not zero but ${nf}" ;;
+ esac
+
+ pretestf=$2
+ case "${pretestf}" in
+ "") ;;
+ [A-Za-z0-9_]*)
+ eval ${pretestf} "${jname}" "${epair}b"
+ ;;
+ esac
+
+ # Clear statistics.
+ jexec ${jname} netstat -z -s > /dev/null
+
+ # Run fragment tests.
+ pyname=$(atf_get ident)
+ pyname=${pyname%*_[0-9]}
+ atf_check -s exit:0 $(atf_get_srcdir)/${pyname}.py \
+ --sendif ${epair}a \
+ --recvif ${epair}a \
+ --src ${ip6a} \
+ --to ${ip6b}
+
+ checkf=$1
+ case "${checkf}" in
+ "") ;;
+ [A-Za-z0-9_]*)
+ eval ${checkf} "${jname}" "${epair}b"
+ ;;
+ esac
+}
+
+frag6_cleanup()
+{
+
+ vnet_cleanup
+}
+
+# end
diff --git a/tests/sys/netinet6/frag6/frag6_01.py b/tests/sys/netinet6/frag6/frag6_01.py
new file mode 100644
index 000000000000..f51a63a01ed6
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_01.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import socket
+import sys
+from sniffer import Sniffer
+from time import sleep
+
+def check_icmp6_error(args, packet):
+ ip6 = packet.getlayer(sp.IPv6)
+ if not ip6:
+ return False
+ oip6 = sp.IPv6(src=args.src[0], dst=args.to[0])
+ if ip6.dst != oip6.src:
+ return False
+ icmp6 = packet.getlayer(sp.ICMPv6ParamProblem)
+ if not icmp6:
+ return False
+ # ICMP6_PARAMPROB_HEADER 0
+ if icmp6.code != 0:
+ return False
+ # Should we check the payload as well?
+ # We are running in a very isolated environment and nothing else
+ # should trigger an ICMPv6 Param Prob so leave it.
+ #icmp6.display()
+ return True
+
+def main():
+ parser = argparse.ArgumentParser("frag6.py",
+ description="IPv6 fragementation test tool")
+ parser.add_argument('--sendif', nargs=1,
+ required=True,
+ help='The interface through which the packet will be sent')
+ parser.add_argument('--recvif', nargs=1,
+ required=True,
+ help='The interface on which to check for the packet')
+ parser.add_argument('--src', nargs=1,
+ required=True,
+ help='The source IP address')
+ parser.add_argument('--to', nargs=1,
+ required=True,
+ help='The destination IP address')
+ parser.add_argument('--debug',
+ required=False, action='store_true',
+ help='Enable test debugging')
+
+ args = parser.parse_args()
+
+
+ # Start sniffing on recvif
+ sniffer = Sniffer(args, check_icmp6_error)
+
+
+ ########################################################################
+ #
+ # A single start fragment with zero length IPv6 header (jumbo).
+ # Make sure we do hit the Fragment case, which is tricky as the
+ # jumbogram needs to be > 64k.
+ #
+ # A: Jumbo-Fragment not allowed.
+ # R: ICMPv6 param problem.
+ #
+ #data = "6" * (65536 - 2 - 6 - 8 - 8)
+ data = "6" * 65512
+ ip6f01 = sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0], plen=0) / \
+ sp.IPv6ExtHdrHopByHop(options=sp.Jumbo(jumboplen=65536)) / \
+ sp.IPv6ExtHdrFragment(offset=0, m=1, id=6) / \
+ sp.UDP(dport=3456, sport=6543) / \
+ data
+ if args.debug :
+ ip6f01.display()
+ sp.sendp(ip6f01, iface=args.sendif[0], verbose=False)
+
+ # We should only need to sleep 0.10 but it seems scapy
+ # takes time for this one.
+ sleep(3)
+ sniffer.setEnd()
+ sniffer.join()
+ if not sniffer.foundCorrectPacket:
+ sys.exit(1)
+
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netinet6/frag6/frag6_01.sh b/tests/sys/netinet6/frag6/frag6_01.sh
new file mode 100755
index 000000000000..906a6b8059bf
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_01.sh
@@ -0,0 +1,232 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/frag6.subr
+
+atf_test_case "frag6_01" "cleanup"
+frag6_01_head() {
+ frag6_head 1
+}
+
+frag6_01_check_stats() {
+
+ local jname ifname
+ jname=$1
+ ifname=$2
+
+ case "${jname}" in
+ "") echo "ERROR: jname is empty"; return ;;
+ esac
+ case "${ifname}" in
+ "") echo "ERROR: ifname is empty"; return ;;
+ esac
+
+ # Defaults are: IPV6_FRAGTTL 120 slowtimo ticks.
+ # pfslowtimo() is run at hz/2. So this takes 60s.
+ # This is awefully long for a test case.
+ # The Python script has to wait for this already to get the ICMPv6
+ # hence we do not sleep here anymore.
+
+ nf=`jexec ${jname} sysctl -n net.inet6.ip6.frag6_nfragpackets`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "VNET frag6_nfragpackets not 0 but: ${nf}" ;;
+ esac
+ nf=`sysctl -n net.inet6.ip6.frag6_nfrags`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "Global frag6_nfrags not 0 but: ${nf}" ;;
+ esac
+
+ #
+ # Check selection of global UDP stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-datagrams>0</received-datagrams>
+ <dropped-incomplete-headers>0</dropped-incomplete-headers>
+ <dropped-bad-data-length>0</dropped-bad-data-length>
+ <dropped-bad-checksum>0</dropped-bad-checksum>
+ <dropped-no-checksum>0</dropped-no-checksum>
+ <dropped-no-socket>0</dropped-no-socket>
+ <dropped-broadcast-multicast>0</dropped-broadcast-multicast>
+ <dropped-full-socket-buffer>0</dropped-full-socket-buffer>
+ <not-for-hashed-pcb>0</not-for-hashed-pcb>
+EOF
+ count=`jexec ${jname} netstat -s -p udp --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 9) ;;
+ *) jexec ${jname} netstat -s -p udp --libxo xml,pretty
+ atf_fail "Global UDP statistics do not match: ${count} != 9" ;;
+ esac
+
+ #
+ # Check selection of global IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-below-minimum-size>0</dropped-below-minimum-size>
+ <dropped-short-packets>0</dropped-short-packets>
+ <dropped-bad-options>1</dropped-bad-options>
+ <dropped-bad-version>0</dropped-bad-version>
+ <received-fragments>0</received-fragments>
+ <dropped-fragment>0</dropped-fragment>
+ <dropped-fragment-after-timeout>0</dropped-fragment-after-timeout>
+ <dropped-fragments-overflow>0</dropped-fragments-overflow>
+ <atomic-fragments>0</atomic-fragments>
+ <reassembled-packets>0</reassembled-packets>
+ <forwarded-packets>0</forwarded-packets>
+ <packets-not-forwardable>0</packets-not-forwardable>
+ <sent-redirects>0</sent-redirects>
+ <send-packets-fabricated-header>0</send-packets-fabricated-header>
+ <discard-no-mbufs>0</discard-no-mbufs>
+ <discard-no-route>0</discard-no-route>
+ <sent-fragments>0</sent-fragments>
+ <fragments-created>0</fragments-created>
+ <discard-cannot-fragment>0</discard-cannot-fragment>
+ <discard-scope-violations>0</discard-scope-violations>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 20) ;;
+ *) jexec ${jname} netstat -s -p ip6 --libxo xml,pretty
+ atf_fail "Global IPv6 statistics do not match: ${count} != 20" ;;
+ esac
+
+ #
+ # Check selection of global ICMPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <icmp6-calls>1</icmp6-calls>
+ <no-route>0</no-route>
+ <admin-prohibited>0</admin-prohibited>
+ <beyond-scope>0</beyond-scope>
+ <address-unreachable>0</address-unreachable>
+ <port-unreachable>0</port-unreachable>
+ <packet-too-big>0</packet-too-big>
+ <time-exceed-transmit>0</time-exceed-transmit>
+ <time-exceed-reassembly>0</time-exceed-reassembly>
+ <bad-header>1</bad-header>
+ <bad-next-header>0</bad-next-header>
+ <bad-option>0</bad-option>
+ <redirects>0</redirects>
+ <unknown>0</unknown>
+ <reflect>0</reflect>
+ <too-many-nd-options>0</too-many-nd-options>
+ <bad-nd-options>0</bad-nd-options>
+ <bad-neighbor-solicitation>0</bad-neighbor-solicitation>
+ <bad-neighbor-advertisement>0</bad-neighbor-advertisement>
+ <bad-router-solicitation>0</bad-router-solicitation>
+ <bad-router-advertisement>0</bad-router-advertisement>
+ <bad-redirect>0</bad-redirect>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 22) ;;
+ *) jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty
+ atf_fail "Global ICMPv6 statistics do not match: ${count} != 22" ;;
+ esac
+
+ #
+ # Check selection of interface IPv6 stats.
+ # XXX-BZ TODO FIXME reassembly-failed should be 1?
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-invalid-header>0</dropped-invalid-header>
+ <dropped-mtu-exceeded>0</dropped-mtu-exceeded>
+ <dropped-no-route>0</dropped-no-route>
+ <dropped-invalid-destination>0</dropped-invalid-destination>
+ <dropped-unknown-protocol>0</dropped-unknown-protocol>
+ <dropped-truncated>0</dropped-truncated>
+ <sent-forwarded>0</sent-forwarded>
+ <discard-packets>0</discard-packets>
+ <discard-fragments>0</discard-fragments>
+ <fragments-failed>0</fragments-failed>
+ <fragments-created>0</fragments-created>
+ <reassembly-required>0</reassembly-required>
+ <reassembled-packets>0</reassembled-packets>
+ <reassembly-failed>0</reassembly-failed>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 14) ;;
+ *) jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface IPv6 statistics do not match: ${count} != 14" ;;
+ esac
+
+ #
+ # Check selection of interface ICMPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-errors>0</received-errors>
+ <received-destination-unreachable>0</received-destination-unreachable>
+ <received-admin-prohibited>0</received-admin-prohibited>
+ <received-time-exceeded>0</received-time-exceeded>
+ <received-bad-parameter>0</received-bad-parameter>
+ <received-packet-too-big>0</received-packet-too-big>
+ <received-echo-requests>0</received-echo-requests>
+ <received-echo-replies>0</received-echo-replies>
+ <received-router-solicitation>0</received-router-solicitation>
+ <received-router-advertisement>0</received-router-advertisement>
+ <sent-errors>1</sent-errors>
+ <sent-destination-unreachable>0</sent-destination-unreachable>
+ <sent-admin-prohibited>0</sent-admin-prohibited>
+ <sent-time-exceeded>0</sent-time-exceeded>
+ <sent-bad-parameter>1</sent-bad-parameter>
+ <sent-packet-too-big>0</sent-packet-too-big>
+ <sent-echo-requests>0</sent-echo-requests>
+ <sent-echo-replies>0</sent-echo-replies>
+ <sent-router-solicitation>0</sent-router-solicitation>
+ <sent-router-advertisement>0</sent-router-advertisement>
+ <sent-redirects>0</sent-redirects>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 21) ;;
+ *) jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface ICMPv6 statistics do not match: ${count} != 21" ;;
+ esac
+}
+
+frag6_01_body() {
+
+ atf_skip "Sending IPv6 Jumbograms needs 1 kernel changes and BPF fixes"
+
+ frag6_body 1 frag6_01_check_stats
+}
+
+frag6_01_cleanup() {
+ frag6_cleanup 1
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "frag6_01"
+}
diff --git a/tests/sys/netinet6/frag6/frag6_02.py b/tests/sys/netinet6/frag6/frag6_02.py
new file mode 100644
index 000000000000..24fa3cbd10c5
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_02.py
@@ -0,0 +1,110 @@
+#!/usr/bin/env python
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import socket
+import sys
+from sniffer import Sniffer
+from time import sleep
+
+def check_icmp6_error(args, packet):
+ ip6 = packet.getlayer(sp.IPv6)
+ if not ip6:
+ return False
+ oip6 = sp.IPv6(src=args.src[0], dst=args.to[0])
+ if ip6.dst != oip6.src:
+ return False
+ icmp6 = packet.getlayer(sp.ICMPv6ParamProblem)
+ if not icmp6:
+ return False
+ # ICMP6_PARAMPROB_HEADER 0
+ if icmp6.code != 0:
+ return False
+ # Should we check the payload as well?
+ # We are running in a very isolated environment and nothing else
+ # should trigger an ICMPv6 Param Prob so leave it.
+ #icmp6.display()
+ return True
+
+def main():
+ parser = argparse.ArgumentParser("frag6.py",
+ description="IPv6 fragementation test tool")
+ parser.add_argument('--sendif', nargs=1,
+ required=True,
+ help='The interface through which the packet will be sent')
+ parser.add_argument('--recvif', nargs=1,
+ required=True,
+ help='The interface on which to check for the packet')
+ parser.add_argument('--src', nargs=1,
+ required=True,
+ help='The source IP address')
+ parser.add_argument('--to', nargs=1,
+ required=True,
+ help='The destination IP address')
+ parser.add_argument('--debug',
+ required=False, action='store_true',
+ help='Enable test debugging')
+
+ args = parser.parse_args()
+
+
+ # Start sniffing on recvif
+ sniffer = Sniffer(args, check_icmp6_error)
+
+
+ ########################################################################
+ #
+ # A single start fragment with payload length not % 8.
+ #
+ # A: Error handling in code.
+ # R: ICMPv6 param problem.
+ #
+ data = "6" * 1287
+ ip6f01 = sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrFragment(offset=0, m=1, id=5) / \
+ sp.UDP(dport=3456, sport=6543) / \
+ data
+ if args.debug :
+ ip6f01.display()
+ sp.sendp(ip6f01, iface=args.sendif[0], verbose=False)
+
+ sleep(0.10)
+ sniffer.setEnd()
+ sniffer.join()
+ if not sniffer.foundCorrectPacket:
+ sys.exit(1)
+
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netinet6/frag6/frag6_02.sh b/tests/sys/netinet6/frag6/frag6_02.sh
new file mode 100755
index 000000000000..b15aa1cf4937
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_02.sh
@@ -0,0 +1,229 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/frag6.subr
+
+atf_test_case "frag6_02" "cleanup"
+frag6_02_head() {
+ frag6_head 2
+}
+
+frag6_02_check_stats() {
+
+ local jname ifname
+ jname=$1
+ ifname=$2
+
+ case "${jname}" in
+ "") echo "ERROR: jname is empty"; return ;;
+ esac
+ case "${ifname}" in
+ "") echo "ERROR: ifname is empty"; return ;;
+ esac
+
+ # Defaults are: IPV6_FRAGTTL 120 slowtimo ticks.
+ # pfslowtimo() is run at hz/2. So this takes 60s.
+ # This is awefully long for a test case.
+ # The Python script has to wait for this already to get the ICMPv6
+ # hence we do not sleep here anymore.
+
+ nf=`jexec ${jname} sysctl -n net.inet6.ip6.frag6_nfragpackets`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "VNET frag6_nfragpackets not 0 but: ${nf}" ;;
+ esac
+ nf=`sysctl -n net.inet6.ip6.frag6_nfrags`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "Global frag6_nfrags not 0 but: ${nf}" ;;
+ esac
+
+ #
+ # Check selection of global UDP stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-datagrams>0</received-datagrams>
+ <dropped-incomplete-headers>0</dropped-incomplete-headers>
+ <dropped-bad-data-length>0</dropped-bad-data-length>
+ <dropped-bad-checksum>0</dropped-bad-checksum>
+ <dropped-no-checksum>0</dropped-no-checksum>
+ <dropped-no-socket>0</dropped-no-socket>
+ <dropped-broadcast-multicast>0</dropped-broadcast-multicast>
+ <dropped-full-socket-buffer>0</dropped-full-socket-buffer>
+ <not-for-hashed-pcb>0</not-for-hashed-pcb>
+EOF
+ count=`jexec ${jname} netstat -s -p udp --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 9) ;;
+ *) jexec ${jname} netstat -s -p udp --libxo xml,pretty
+ atf_fail "Global UDP statistics do not match: ${count} != 9" ;;
+ esac
+
+ #
+ # Check selection of global IPv6 stats.
+ # XXX-TODO no global stats for this case?
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-below-minimum-size>0</dropped-below-minimum-size>
+ <dropped-short-packets>0</dropped-short-packets>
+ <dropped-bad-options>0</dropped-bad-options>
+ <dropped-bad-version>0</dropped-bad-version>
+ <received-fragments>0</received-fragments>
+ <dropped-fragment>0</dropped-fragment>
+ <dropped-fragment-after-timeout>0</dropped-fragment-after-timeout>
+ <dropped-fragments-overflow>0</dropped-fragments-overflow>
+ <atomic-fragments>0</atomic-fragments>
+ <reassembled-packets>0</reassembled-packets>
+ <forwarded-packets>0</forwarded-packets>
+ <packets-not-forwardable>0</packets-not-forwardable>
+ <sent-redirects>0</sent-redirects>
+ <send-packets-fabricated-header>0</send-packets-fabricated-header>
+ <discard-no-mbufs>0</discard-no-mbufs>
+ <discard-no-route>0</discard-no-route>
+ <sent-fragments>0</sent-fragments>
+ <fragments-created>0</fragments-created>
+ <discard-cannot-fragment>0</discard-cannot-fragment>
+ <discard-scope-violations>0</discard-scope-violations>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 20) ;;
+ *) jexec ${jname} netstat -s -p ip6 --libxo xml,pretty
+ atf_fail "Global IPv6 statistics do not match: ${count} != 20" ;;
+ esac
+
+ #
+ # Check selection of global ICMPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <icmp6-calls>1</icmp6-calls>
+ <no-route>0</no-route>
+ <admin-prohibited>0</admin-prohibited>
+ <beyond-scope>0</beyond-scope>
+ <address-unreachable>0</address-unreachable>
+ <port-unreachable>0</port-unreachable>
+ <packet-too-big>0</packet-too-big>
+ <time-exceed-transmit>0</time-exceed-transmit>
+ <time-exceed-reassembly>0</time-exceed-reassembly>
+ <bad-header>1</bad-header>
+ <bad-next-header>0</bad-next-header>
+ <bad-option>0</bad-option>
+ <redirects>0</redirects>
+ <unknown>0</unknown>
+ <reflect>0</reflect>
+ <too-many-nd-options>0</too-many-nd-options>
+ <bad-nd-options>0</bad-nd-options>
+ <bad-neighbor-solicitation>0</bad-neighbor-solicitation>
+ <bad-neighbor-advertisement>0</bad-neighbor-advertisement>
+ <bad-router-solicitation>0</bad-router-solicitation>
+ <bad-router-advertisement>0</bad-router-advertisement>
+ <bad-redirect>0</bad-redirect>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 22) ;;
+ *) jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty
+ atf_fail "Global ICMPv6 statistics do not match: ${count} != 22" ;;
+ esac
+
+ #
+ # Check selection of interface IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-invalid-header>0</dropped-invalid-header>
+ <dropped-mtu-exceeded>0</dropped-mtu-exceeded>
+ <dropped-no-route>0</dropped-no-route>
+ <dropped-invalid-destination>0</dropped-invalid-destination>
+ <dropped-unknown-protocol>0</dropped-unknown-protocol>
+ <dropped-truncated>0</dropped-truncated>
+ <sent-forwarded>0</sent-forwarded>
+ <discard-packets>0</discard-packets>
+ <discard-fragments>0</discard-fragments>
+ <fragments-failed>0</fragments-failed>
+ <fragments-created>0</fragments-created>
+ <reassembly-required>0</reassembly-required>
+ <reassembled-packets>0</reassembled-packets>
+ <reassembly-failed>1</reassembly-failed>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 14) ;;
+ *) jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface IPv6 statistics do not match: ${count} != 14" ;;
+ esac
+
+ #
+ # Check selection of interface ICMPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-errors>0</received-errors>
+ <received-destination-unreachable>0</received-destination-unreachable>
+ <received-admin-prohibited>0</received-admin-prohibited>
+ <received-time-exceeded>0</received-time-exceeded>
+ <received-bad-parameter>0</received-bad-parameter>
+ <received-packet-too-big>0</received-packet-too-big>
+ <received-echo-requests>0</received-echo-requests>
+ <received-echo-replies>0</received-echo-replies>
+ <received-router-solicitation>0</received-router-solicitation>
+ <received-router-advertisement>0</received-router-advertisement>
+ <sent-errors>1</sent-errors>
+ <sent-destination-unreachable>0</sent-destination-unreachable>
+ <sent-admin-prohibited>0</sent-admin-prohibited>
+ <sent-time-exceeded>0</sent-time-exceeded>
+ <sent-bad-parameter>1</sent-bad-parameter>
+ <sent-packet-too-big>0</sent-packet-too-big>
+ <sent-echo-requests>0</sent-echo-requests>
+ <sent-echo-replies>0</sent-echo-replies>
+ <sent-router-solicitation>0</sent-router-solicitation>
+ <sent-router-advertisement>0</sent-router-advertisement>
+ <sent-redirects>0</sent-redirects>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 21) ;;
+ *) jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface ICMPv6 statistics do not match: ${count} != 21" ;;
+ esac
+}
+
+frag6_02_body() {
+ frag6_body 2 frag6_02_check_stats
+}
+
+frag6_02_cleanup() {
+ frag6_cleanup 2
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "frag6_02"
+}
diff --git a/tests/sys/netinet6/frag6/frag6_03.py b/tests/sys/netinet6/frag6/frag6_03.py
new file mode 100644
index 000000000000..e0299e95d1db
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_03.py
@@ -0,0 +1,135 @@
+#!/usr/bin/env python
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import socket
+import sys
+from sniffer import Sniffer
+from time import sleep
+
+def check_icmp6_error(args, packet):
+ ip6 = packet.getlayer(sp.IPv6)
+ if not ip6:
+ return False
+ oip6 = sp.IPv6(src=args.src[0], dst=args.to[0])
+ if ip6.dst != oip6.src:
+ return False
+ icmp6 = packet.getlayer(sp.ICMPv6DestUnreach)
+ if not icmp6:
+ return False
+ # ICMP6_DST_UNREACH_NOPORT 4
+ if icmp6.code != 4:
+ return False
+ # Should we check the payload as well?
+ # We are running in a very isolated environment and nothing else
+ # should trigger an ICMPv6 Dest Unreach / Port Unreach so leave it.
+ #icmp6.display()
+ return True
+
+def main():
+ parser = argparse.ArgumentParser("frag6.py",
+ description="IPv6 fragementation test tool")
+ parser.add_argument('--sendif', nargs=1,
+ required=True,
+ help='The interface through which the packet will be sent')
+ parser.add_argument('--recvif', nargs=1,
+ required=True,
+ help='The interface on which to check for the packet')
+ parser.add_argument('--src', nargs=1,
+ required=True,
+ help='The source IP address')
+ parser.add_argument('--to', nargs=1,
+ required=True,
+ help='The destination IP address')
+ parser.add_argument('--debug',
+ required=False, action='store_true',
+ help='Enable test debugging')
+
+ args = parser.parse_args()
+
+
+ # Start sniffing on recvif
+ sniffer = Sniffer(args, check_icmp6_error)
+
+
+ ########################################################################
+ #
+ # Atomic fragment.
+ #
+ # A: Nothing listening on UDP port.
+ # R: ICMPv6 dst unreach, unreach port.
+ #
+ ip6f01 = sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrFragment(offset=0, m=0, id=3) / \
+ sp.UDP(dport=3456, sport=6543)
+ if args.debug :
+ ip6f01.display()
+ sp.sendp(ip6f01, iface=args.sendif[0], verbose=False)
+
+ sleep(0.10)
+ sniffer.setEnd()
+ sniffer.join()
+ if not sniffer.foundCorrectPacket:
+ sys.exit(1)
+
+
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ##
+ #
+ # Atomic fragment with extension header.
+ #
+ # A: Nothing listening on UDP port.
+ # R: ICMPv6 dst unreach, unreach port.
+ #
+ # Start sniffing on recvif
+ sniffer = Sniffer(args, check_icmp6_error)
+
+ ip6f01 = sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrDestOpt(options = \
+ sp.PadN(optdata="\x00\x00\x00\x00\x00\x00")) / \
+ sp.IPv6ExtHdrFragment(offset=0, m=0, id=0x3001) / \
+ sp.UDP(dport=3456, sport=6543)
+ if args.debug :
+ ip6f01.display()
+ sp.sendp(ip6f01, iface=args.sendif[0], verbose=False)
+
+ sleep(0.10)
+ sniffer.setEnd()
+ sniffer.join()
+ if not sniffer.foundCorrectPacket:
+ sys.exit(1)
+
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netinet6/frag6/frag6_03.sh b/tests/sys/netinet6/frag6/frag6_03.sh
new file mode 100755
index 000000000000..8420831b02ae
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_03.sh
@@ -0,0 +1,230 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/frag6.subr
+
+atf_test_case "frag6_03" "cleanup"
+frag6_03_head() {
+ frag6_head 3
+}
+
+frag6_03_check_stats() {
+
+ local jname ifname
+ jname=$1
+ ifname=$2
+
+ case "${jname}" in
+ "") echo "ERROR: jname is empty"; return ;;
+ esac
+ case "${ifname}" in
+ "") echo "ERROR: ifname is empty"; return ;;
+ esac
+
+ # Defaults are: IPV6_FRAGTTL 120 slowtimo ticks.
+ # pfslowtimo() is run at hz/2. So this takes 60s.
+ # This is awefully long for a test case.
+ # The Python script has to wait for this already to get the ICMPv6
+ # hence we do not sleep here anymore.
+
+ nf=`jexec ${jname} sysctl -n net.inet6.ip6.frag6_nfragpackets`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "VNET frag6_nfragpackets not 0 but: ${nf}" ;;
+ esac
+ nf=`sysctl -n net.inet6.ip6.frag6_nfrags`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "Global frag6_nfrags not 0 but: ${nf}" ;;
+ esac
+
+ #
+ # Check selection of global UDP stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-datagrams>2</received-datagrams>
+ <dropped-incomplete-headers>0</dropped-incomplete-headers>
+ <dropped-bad-data-length>0</dropped-bad-data-length>
+ <dropped-bad-checksum>0</dropped-bad-checksum>
+ <dropped-no-checksum>0</dropped-no-checksum>
+ <dropped-no-socket>2</dropped-no-socket>
+ <dropped-broadcast-multicast>0</dropped-broadcast-multicast>
+ <dropped-full-socket-buffer>0</dropped-full-socket-buffer>
+ <not-for-hashed-pcb>0</not-for-hashed-pcb>
+EOF
+ count=`jexec ${jname} netstat -s -p udp --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 9) ;;
+ *) jexec ${jname} netstat -s -p udp --libxo xml,pretty
+ atf_fail "Global UDP statistics do not match: ${count} != 9" ;;
+ esac
+
+
+ #
+ # Check selection of global IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-below-minimum-size>0</dropped-below-minimum-size>
+ <dropped-short-packets>0</dropped-short-packets>
+ <dropped-bad-options>0</dropped-bad-options>
+ <dropped-bad-version>0</dropped-bad-version>
+ <received-fragments>2</received-fragments>
+ <dropped-fragment>0</dropped-fragment>
+ <dropped-fragment-after-timeout>0</dropped-fragment-after-timeout>
+ <dropped-fragments-overflow>0</dropped-fragments-overflow>
+ <atomic-fragments>2</atomic-fragments>
+ <reassembled-packets>0</reassembled-packets>
+ <forwarded-packets>0</forwarded-packets>
+ <packets-not-forwardable>0</packets-not-forwardable>
+ <sent-redirects>0</sent-redirects>
+ <send-packets-fabricated-header>0</send-packets-fabricated-header>
+ <discard-no-mbufs>0</discard-no-mbufs>
+ <discard-no-route>0</discard-no-route>
+ <sent-fragments>0</sent-fragments>
+ <fragments-created>0</fragments-created>
+ <discard-cannot-fragment>0</discard-cannot-fragment>
+ <discard-scope-violations>0</discard-scope-violations>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 20) ;;
+ *) jexec ${jname} netstat -s -p ip6 --libxo xml,pretty
+ atf_fail "Global IPv6 statistics do not match: ${count} != 20" ;;
+ esac
+
+ #
+ # Check selection of global ICMPv6 stats.
+ # XXX-TODO check output histogram (just too hard to parse [no multi-line-grep])
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <icmp6-calls>2</icmp6-calls>
+ <no-route>0</no-route>
+ <admin-prohibited>0</admin-prohibited>
+ <beyond-scope>0</beyond-scope>
+ <address-unreachable>0</address-unreachable>
+ <port-unreachable>2</port-unreachable>
+ <packet-too-big>0</packet-too-big>
+ <time-exceed-transmit>0</time-exceed-transmit>
+ <time-exceed-reassembly>0</time-exceed-reassembly>
+ <bad-header>0</bad-header>
+ <bad-next-header>0</bad-next-header>
+ <bad-option>0</bad-option>
+ <redirects>0</redirects>
+ <unknown>0</unknown>
+ <reflect>0</reflect>
+ <too-many-nd-options>0</too-many-nd-options>
+ <bad-nd-options>0</bad-nd-options>
+ <bad-neighbor-solicitation>0</bad-neighbor-solicitation>
+ <bad-neighbor-advertisement>0</bad-neighbor-advertisement>
+ <bad-router-solicitation>0</bad-router-solicitation>
+ <bad-router-advertisement>0</bad-router-advertisement>
+ <bad-redirect>0</bad-redirect>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 22) ;;
+ *) jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty
+ atf_fail "Global ICMPv6 statistics do not match: ${count} != 22" ;;
+ esac
+
+ #
+ # Check selection of interface IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-invalid-header>0</dropped-invalid-header>
+ <dropped-mtu-exceeded>0</dropped-mtu-exceeded>
+ <dropped-no-route>0</dropped-no-route>
+ <dropped-invalid-destination>0</dropped-invalid-destination>
+ <dropped-unknown-protocol>0</dropped-unknown-protocol>
+ <dropped-truncated>0</dropped-truncated>
+ <sent-forwarded>0</sent-forwarded>
+ <discard-packets>0</discard-packets>
+ <discard-fragments>0</discard-fragments>
+ <fragments-failed>0</fragments-failed>
+ <fragments-created>0</fragments-created>
+ <reassembly-required>2</reassembly-required>
+ <reassembled-packets>2</reassembled-packets>
+ <reassembly-failed>0</reassembly-failed>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 14) ;;
+ *) jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface IPv6 statistics do not match: ${count} != 14" ;;
+ esac
+
+ #
+ # Check selection of interface ICMPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-errors>0</received-errors>
+ <received-destination-unreachable>0</received-destination-unreachable>
+ <received-admin-prohibited>0</received-admin-prohibited>
+ <received-time-exceeded>0</received-time-exceeded>
+ <received-bad-parameter>0</received-bad-parameter>
+ <received-packet-too-big>0</received-packet-too-big>
+ <received-echo-requests>0</received-echo-requests>
+ <received-echo-replies>0</received-echo-replies>
+ <received-router-solicitation>0</received-router-solicitation>
+ <received-router-advertisement>0</received-router-advertisement>
+ <sent-errors>2</sent-errors>
+ <sent-destination-unreachable>2</sent-destination-unreachable>
+ <sent-admin-prohibited>0</sent-admin-prohibited>
+ <sent-time-exceeded>0</sent-time-exceeded>
+ <sent-bad-parameter>0</sent-bad-parameter>
+ <sent-packet-too-big>0</sent-packet-too-big>
+ <sent-echo-requests>0</sent-echo-requests>
+ <sent-echo-replies>0</sent-echo-replies>
+ <sent-router-solicitation>0</sent-router-solicitation>
+ <sent-router-advertisement>0</sent-router-advertisement>
+ <sent-redirects>0</sent-redirects>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 21) ;;
+ *) jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface ICMPv6 statistics do not match: ${count} != 21" ;;
+ esac
+}
+
+frag6_03_body() {
+ frag6_body 3 frag6_03_check_stats
+}
+
+frag6_03_cleanup() {
+ frag6_cleanup 3
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "frag6_03"
+}
diff --git a/tests/sys/netinet6/frag6/frag6_04.py b/tests/sys/netinet6/frag6/frag6_04.py
new file mode 100644
index 000000000000..83f60c8aa92a
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_04.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import socket
+import sys
+from sniffer import Sniffer
+from time import sleep
+
+def check_icmp6_error(args, packet):
+ ip6 = packet.getlayer(sp.IPv6)
+ if not ip6:
+ return False
+ oip6 = sp.IPv6(src=args.src[0], dst=args.to[0])
+ if ip6.dst != oip6.src:
+ return False
+ icmp6 = packet.getlayer(sp.ICMPv6ParamProblem)
+ if not icmp6:
+ return False
+ # ICMP6_PARAMPROB_HEADER 0
+ if icmp6.code != 0:
+ return False
+ # Should we check the payload as well?
+ # We are running in a very isolated environment and nothing else
+ # should trigger an ICMPv6 Param Prob so leave it.
+ #icmp6.display()
+ return True
+
+def main():
+ parser = argparse.ArgumentParser("frag6.py",
+ description="IPv6 fragementation test tool")
+ parser.add_argument('--sendif', nargs=1,
+ required=True,
+ help='The interface through which the packet will be sent')
+ parser.add_argument('--recvif', nargs=1,
+ required=True,
+ help='The interface on which to check for the packet')
+ parser.add_argument('--src', nargs=1,
+ required=True,
+ help='The source IP address')
+ parser.add_argument('--to', nargs=1,
+ required=True,
+ help='The destination IP address')
+ parser.add_argument('--debug',
+ required=False, action='store_true',
+ help='Enable test debugging')
+
+ args = parser.parse_args()
+
+
+ # Start sniffing on recvif
+ sniffer = Sniffer(args, check_icmp6_error)
+
+
+ ########################################################################
+ #
+ # 0-byte first fragment.
+ #
+ # A: 0-byte fragment payload not allowed. Discarded.
+ # R: ICMPv6 param prob, paramprob header.
+ #
+ ip6f01 = sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrFragment(offset=0, m=1, id=4)
+ if args.debug :
+ ip6f01.display()
+ sp.sendp(ip6f01, iface=args.sendif[0], verbose=False)
+
+ sleep(0.10)
+ sniffer.setEnd()
+ sniffer.join()
+ if not sniffer.foundCorrectPacket:
+ sys.exit(1)
+
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netinet6/frag6/frag6_04.sh b/tests/sys/netinet6/frag6/frag6_04.sh
new file mode 100755
index 000000000000..eee551dba549
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_04.sh
@@ -0,0 +1,230 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/frag6.subr
+
+atf_test_case "frag6_04" "cleanup"
+frag6_04_head() {
+ frag6_head 4
+}
+
+frag6_04_check_stats() {
+
+ local jname ifname
+ jname=$1
+ ifname=$2
+
+ case "${jname}" in
+ "") echo "ERROR: jname is empty"; return ;;
+ esac
+ case "${ifname}" in
+ "") echo "ERROR: ifname is empty"; return ;;
+ esac
+
+ # Defaults are: IPV6_FRAGTTL 120 slowtimo ticks.
+ # pfslowtimo() is run at hz/2. So this takes 60s.
+ # This is awefully long for a test case.
+ # The Python script has to wait for this already to get the ICMPv6
+ # hence we do not sleep here anymore.
+
+ nf=`jexec ${jname} sysctl -n net.inet6.ip6.frag6_nfragpackets`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "VNET frag6_nfragpackets not 0 but: ${nf}" ;;
+ esac
+ nf=`sysctl -n net.inet6.ip6.frag6_nfrags`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "Global frag6_nfrags not 0 but: ${nf}" ;;
+ esac
+
+ #
+ # Check selection of global UDP stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-datagrams>0</received-datagrams>
+ <dropped-incomplete-headers>0</dropped-incomplete-headers>
+ <dropped-bad-data-length>0</dropped-bad-data-length>
+ <dropped-bad-checksum>0</dropped-bad-checksum>
+ <dropped-no-checksum>0</dropped-no-checksum>
+ <dropped-no-socket>0</dropped-no-socket>
+ <dropped-broadcast-multicast>0</dropped-broadcast-multicast>
+ <dropped-full-socket-buffer>0</dropped-full-socket-buffer>
+ <not-for-hashed-pcb>0</not-for-hashed-pcb>
+EOF
+ count=`jexec ${jname} netstat -s -p udp --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 9) ;;
+ *) jexec ${jname} netstat -s -p udp --libxo xml,pretty
+ atf_fail "Global UDP statistics do not match: ${count} != 9" ;;
+ esac
+
+
+ #
+ # Check selection of global IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-below-minimum-size>0</dropped-below-minimum-size>
+ <dropped-short-packets>0</dropped-short-packets>
+ <dropped-bad-options>0</dropped-bad-options>
+ <dropped-bad-version>0</dropped-bad-version>
+ <received-fragments>1</received-fragments>
+ <dropped-fragment>1</dropped-fragment>
+ <dropped-fragment-after-timeout>0</dropped-fragment-after-timeout>
+ <dropped-fragments-overflow>0</dropped-fragments-overflow>
+ <atomic-fragments>0</atomic-fragments>
+ <reassembled-packets>0</reassembled-packets>
+ <forwarded-packets>0</forwarded-packets>
+ <packets-not-forwardable>0</packets-not-forwardable>
+ <sent-redirects>0</sent-redirects>
+ <send-packets-fabricated-header>0</send-packets-fabricated-header>
+ <discard-no-mbufs>0</discard-no-mbufs>
+ <discard-no-route>0</discard-no-route>
+ <sent-fragments>0</sent-fragments>
+ <fragments-created>0</fragments-created>
+ <discard-cannot-fragment>0</discard-cannot-fragment>
+ <discard-scope-violations>0</discard-scope-violations>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 20) ;;
+ *) jexec ${jname} netstat -s -p ip6 --libxo xml,pretty
+ atf_fail "Global IPv6 statistics do not match: ${count} != 20" ;;
+ esac
+
+ #
+ # Check selection of global ICMPv6 stats.
+ # XXX-TODO check output histogram (just too hard to parse [no multi-line-grep])
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <icmp6-calls>1</icmp6-calls>
+ <no-route>0</no-route>
+ <admin-prohibited>0</admin-prohibited>
+ <beyond-scope>0</beyond-scope>
+ <address-unreachable>0</address-unreachable>
+ <port-unreachable>0</port-unreachable>
+ <packet-too-big>0</packet-too-big>
+ <time-exceed-transmit>0</time-exceed-transmit>
+ <time-exceed-reassembly>0</time-exceed-reassembly>
+ <bad-header>1</bad-header>
+ <bad-next-header>0</bad-next-header>
+ <bad-option>0</bad-option>
+ <redirects>0</redirects>
+ <unknown>0</unknown>
+ <reflect>0</reflect>
+ <too-many-nd-options>0</too-many-nd-options>
+ <bad-nd-options>0</bad-nd-options>
+ <bad-neighbor-solicitation>0</bad-neighbor-solicitation>
+ <bad-neighbor-advertisement>0</bad-neighbor-advertisement>
+ <bad-router-solicitation>0</bad-router-solicitation>
+ <bad-router-advertisement>0</bad-router-advertisement>
+ <bad-redirect>0</bad-redirect>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 22) ;;
+ *) jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty
+ atf_fail "Global ICMPv6 statistics do not match: ${count} != 22" ;;
+ esac
+
+ #
+ # Check selection of interface IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-invalid-header>0</dropped-invalid-header>
+ <dropped-mtu-exceeded>0</dropped-mtu-exceeded>
+ <dropped-no-route>0</dropped-no-route>
+ <dropped-invalid-destination>0</dropped-invalid-destination>
+ <dropped-unknown-protocol>0</dropped-unknown-protocol>
+ <dropped-truncated>0</dropped-truncated>
+ <sent-forwarded>0</sent-forwarded>
+ <discard-packets>0</discard-packets>
+ <discard-fragments>0</discard-fragments>
+ <fragments-failed>0</fragments-failed>
+ <fragments-created>0</fragments-created>
+ <reassembly-required>1</reassembly-required>
+ <reassembled-packets>0</reassembled-packets>
+ <reassembly-failed>1</reassembly-failed>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 14) ;;
+ *) jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface IPv6 statistics do not match: ${count} != 14" ;;
+ esac
+
+ #
+ # Check selection of interface ICMPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-errors>0</received-errors>
+ <received-destination-unreachable>0</received-destination-unreachable>
+ <received-admin-prohibited>0</received-admin-prohibited>
+ <received-time-exceeded>0</received-time-exceeded>
+ <received-bad-parameter>0</received-bad-parameter>
+ <received-packet-too-big>0</received-packet-too-big>
+ <received-echo-requests>0</received-echo-requests>
+ <received-echo-replies>0</received-echo-replies>
+ <received-router-solicitation>0</received-router-solicitation>
+ <received-router-advertisement>0</received-router-advertisement>
+ <sent-errors>1</sent-errors>
+ <sent-destination-unreachable>0</sent-destination-unreachable>
+ <sent-admin-prohibited>0</sent-admin-prohibited>
+ <sent-time-exceeded>0</sent-time-exceeded>
+ <sent-bad-parameter>1</sent-bad-parameter>
+ <sent-packet-too-big>0</sent-packet-too-big>
+ <sent-echo-requests>0</sent-echo-requests>
+ <sent-echo-replies>0</sent-echo-replies>
+ <sent-router-solicitation>0</sent-router-solicitation>
+ <sent-router-advertisement>0</sent-router-advertisement>
+ <sent-redirects>0</sent-redirects>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 21) ;;
+ *) jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface ICMPv6 statistics do not match: ${count} != 21" ;;
+ esac
+}
+
+frag6_04_body() {
+ frag6_body 4 frag6_04_check_stats
+}
+
+frag6_04_cleanup() {
+ frag6_cleanup 4
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "frag6_04"
+}
diff --git a/tests/sys/netinet6/frag6/frag6_05.py b/tests/sys/netinet6/frag6/frag6_05.py
new file mode 100644
index 000000000000..4837f6bf4504
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_05.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import socket
+import sys
+from time import sleep
+
+def main():
+ parser = argparse.ArgumentParser("frag6.py",
+ description="IPv6 fragementation test tool")
+ parser.add_argument('--sendif', nargs=1,
+ required=True,
+ help='The interface through which the packet will be sent')
+ parser.add_argument('--recvif', nargs=1,
+ required=True,
+ help='The interface on which to check for the packet')
+ parser.add_argument('--src', nargs=1,
+ required=True,
+ help='The source IP address')
+ parser.add_argument('--to', nargs=1,
+ required=True,
+ help='The destination IP address')
+ parser.add_argument('--debug',
+ required=False, action='store_true',
+ help='Enable test debugging')
+
+ args = parser.parse_args()
+
+
+ ########################################################################
+ #
+ # Sysctl set to accept (no|maximum 10) fragments.
+ #
+ # A: Discarded.
+ # R: Silence (statistics only) or ICMPv6 timeout expiry.
+ #
+ data = "6" * 1280
+ bfid = 0x5001
+ for i in range(20):
+ fid = bfid + i
+ ip6f01 = sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrFragment(offset=0, m=1, id=fid) / \
+ sp.UDP(dport=3456, sport=6543) / \
+ data
+ if args.debug :
+ ip6f01.display()
+ sp.sendp(ip6f01, iface=args.sendif[0], verbose=False)
+
+ # Wait for possible expiry to happen.
+ sleep(3)
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netinet6/frag6/frag6_05.sh b/tests/sys/netinet6/frag6/frag6_05.sh
new file mode 100755
index 000000000000..ba65c70c8d35
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_05.sh
@@ -0,0 +1,474 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/frag6.subr
+
+frag6_05_check_stats_0() {
+
+ local jname ifname
+ jname=$1
+ ifname=$2
+
+ case "${jname}" in
+ "") echo "ERROR: jname is empty"; return ;;
+ esac
+ case "${ifname}" in
+ "") echo "ERROR: ifname is empty"; return ;;
+ esac
+
+ # Defaults are: IPV6_FRAGTTL 120 slowtimo ticks.
+ # pfslowtimo() is run at hz/2. So this takes 60s.
+ # This is awefully long for a test case.
+ # The Python script has to wait for this already to get the ICMPv6
+ # hence we do not sleep here anymore.
+
+ nf=`jexec ${jname} sysctl -n net.inet6.ip6.frag6_nfragpackets`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "VNET frag6_nfragpackets not 0 but: ${nf}" ;;
+ esac
+ nf=`sysctl -n net.inet6.ip6.frag6_nfrags`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "Global frag6_nfrags not 0 but: ${nf}" ;;
+ esac
+
+ #
+ # Check that the sysctl is set to what we expect.
+ #
+ sn=`sysctl -n net.inet6.ip6.maxfrags`
+ case "${sn}" in
+ 0) ;;
+ *) atf_fail "Sysctl net.inet6.ip6.maxfrags is ${sn} and not 0" ;;
+ esac
+
+ #
+ # Check selection of global UDP stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-datagrams>0</received-datagrams>
+ <dropped-incomplete-headers>0</dropped-incomplete-headers>
+ <dropped-bad-data-length>0</dropped-bad-data-length>
+ <dropped-bad-checksum>0</dropped-bad-checksum>
+ <dropped-no-checksum>0</dropped-no-checksum>
+ <dropped-no-socket>0</dropped-no-socket>
+ <dropped-broadcast-multicast>0</dropped-broadcast-multicast>
+ <dropped-full-socket-buffer>0</dropped-full-socket-buffer>
+ <not-for-hashed-pcb>0</not-for-hashed-pcb>
+EOF
+ count=`jexec ${jname} netstat -s -p udp --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 9) ;;
+ *) jexec ${jname} netstat -s -p udp --libxo xml,pretty
+ atf_fail "Global UDP statistics do not match: ${count} != 9" ;;
+ esac
+
+
+ #
+ # Check selection of global IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-below-minimum-size>0</dropped-below-minimum-size>
+ <dropped-short-packets>0</dropped-short-packets>
+ <dropped-bad-options>0</dropped-bad-options>
+ <dropped-bad-version>0</dropped-bad-version>
+ <received-fragments>20</received-fragments>
+ <dropped-fragment>20</dropped-fragment>
+ <dropped-fragment-after-timeout>0</dropped-fragment-after-timeout>
+ <dropped-fragments-overflow>0</dropped-fragments-overflow>
+ <atomic-fragments>0</atomic-fragments>
+ <reassembled-packets>0</reassembled-packets>
+ <forwarded-packets>0</forwarded-packets>
+ <packets-not-forwardable>0</packets-not-forwardable>
+ <sent-redirects>0</sent-redirects>
+ <send-packets-fabricated-header>0</send-packets-fabricated-header>
+ <discard-no-mbufs>0</discard-no-mbufs>
+ <discard-no-route>0</discard-no-route>
+ <sent-fragments>0</sent-fragments>
+ <fragments-created>0</fragments-created>
+ <discard-cannot-fragment>0</discard-cannot-fragment>
+ <discard-scope-violations>0</discard-scope-violations>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 20) ;;
+ *) jexec ${jname} netstat -s -p ip6 --libxo xml,pretty
+ atf_fail "Global IPv6 statistics do not match: ${count} != 20" ;;
+ esac
+
+ #
+ # Check selection of global ICMPv6 stats.
+ # XXX-TODO check output histogram (just too hard to parse [no multi-line-grep])
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <icmp6-calls>0</icmp6-calls>
+ <no-route>0</no-route>
+ <admin-prohibited>0</admin-prohibited>
+ <beyond-scope>0</beyond-scope>
+ <address-unreachable>0</address-unreachable>
+ <port-unreachable>0</port-unreachable>
+ <packet-too-big>0</packet-too-big>
+ <time-exceed-transmit>0</time-exceed-transmit>
+ <time-exceed-reassembly>0</time-exceed-reassembly>
+ <bad-header>0</bad-header>
+ <bad-next-header>0</bad-next-header>
+ <bad-option>0</bad-option>
+ <redirects>0</redirects>
+ <unknown>0</unknown>
+ <reflect>0</reflect>
+ <too-many-nd-options>0</too-many-nd-options>
+ <bad-nd-options>0</bad-nd-options>
+ <bad-neighbor-solicitation>0</bad-neighbor-solicitation>
+ <bad-neighbor-advertisement>0</bad-neighbor-advertisement>
+ <bad-router-solicitation>0</bad-router-solicitation>
+ <bad-router-advertisement>0</bad-router-advertisement>
+ <bad-redirect>0</bad-redirect>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 22) ;;
+ *) jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty
+ atf_fail "Global ICMPv6 statistics do not match: ${count} != 22" ;;
+ esac
+
+ #
+ # Check selection of interface IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-invalid-header>0</dropped-invalid-header>
+ <dropped-mtu-exceeded>0</dropped-mtu-exceeded>
+ <dropped-no-route>0</dropped-no-route>
+ <dropped-invalid-destination>0</dropped-invalid-destination>
+ <dropped-unknown-protocol>0</dropped-unknown-protocol>
+ <dropped-truncated>0</dropped-truncated>
+ <sent-forwarded>0</sent-forwarded>
+ <discard-packets>0</discard-packets>
+ <discard-fragments>0</discard-fragments>
+ <fragments-failed>0</fragments-failed>
+ <fragments-created>0</fragments-created>
+ <reassembly-required>20</reassembly-required>
+ <reassembled-packets>0</reassembled-packets>
+ <reassembly-failed>20</reassembly-failed>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 14) ;;
+ *) jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface IPv6 statistics do not match: ${count} != 14" ;;
+ esac
+
+ #
+ # Check selection of interface ICMPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-errors>0</received-errors>
+ <received-destination-unreachable>0</received-destination-unreachable>
+ <received-admin-prohibited>0</received-admin-prohibited>
+ <received-time-exceeded>0</received-time-exceeded>
+ <received-bad-parameter>0</received-bad-parameter>
+ <received-packet-too-big>0</received-packet-too-big>
+ <received-echo-requests>0</received-echo-requests>
+ <received-echo-replies>0</received-echo-replies>
+ <received-router-solicitation>0</received-router-solicitation>
+ <received-router-advertisement>0</received-router-advertisement>
+ <sent-errors>0</sent-errors>
+ <sent-destination-unreachable>0</sent-destination-unreachable>
+ <sent-admin-prohibited>0</sent-admin-prohibited>
+ <sent-time-exceeded>0</sent-time-exceeded>
+ <sent-bad-parameter>0</sent-bad-parameter>
+ <sent-packet-too-big>0</sent-packet-too-big>
+ <sent-echo-requests>0</sent-echo-requests>
+ <sent-echo-replies>0</sent-echo-replies>
+ <sent-router-solicitation>0</sent-router-solicitation>
+ <sent-router-advertisement>0</sent-router-advertisement>
+ <sent-redirects>0</sent-redirects>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 21) ;;
+ *) jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface ICMPv6 statistics do not match: ${count} != 21" ;;
+ esac
+}
+
+frag6_05_check_stats_1() {
+
+ local jname ifname
+ jname=$1
+ ifname=$2
+
+ case "${jname}" in
+ "") echo "ERROR: jname is empty"; return ;;
+ esac
+ case "${ifname}" in
+ "") echo "ERROR: ifname is empty"; return ;;
+ esac
+
+ # Defaults are: IPV6_FRAGTTL 120 slowtimo ticks.
+ # pfslowtimo() is run at hz/2. So this takes 60s.
+ # This is awefully long for a test case.
+ # The Python script has to wait for this already to get the ICMPv6
+ # hence we do not sleep here anymore.
+
+ nf=`jexec ${jname} sysctl -n net.inet6.ip6.frag6_nfragpackets`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "VNET frag6_nfragpackets not 0 but: ${nf}" ;;
+ esac
+ nf=`sysctl -n net.inet6.ip6.frag6_nfrags`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "Global frag6_nfrags not 0 but: ${nf}" ;;
+ esac
+
+ #
+ # Check that the sysctl is set to what we expect.
+ #
+ sn=`sysctl -n net.inet6.ip6.maxfrags`
+ case "${sn}" in
+ 10) ;;
+ *) atf_fail "Sysctl net.inet6.ip6.maxfrags is ${sn} and not 10" ;;
+ esac
+
+ #
+ # Check selection of global UDP stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-datagrams>0</received-datagrams>
+ <dropped-incomplete-headers>0</dropped-incomplete-headers>
+ <dropped-bad-data-length>0</dropped-bad-data-length>
+ <dropped-bad-checksum>0</dropped-bad-checksum>
+ <dropped-no-checksum>0</dropped-no-checksum>
+ <dropped-no-socket>0</dropped-no-socket>
+ <dropped-broadcast-multicast>0</dropped-broadcast-multicast>
+ <dropped-full-socket-buffer>0</dropped-full-socket-buffer>
+ <not-for-hashed-pcb>0</not-for-hashed-pcb>
+EOF
+ count=`jexec ${jname} netstat -s -p udp --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 9) ;;
+ *) jexec ${jname} netstat -s -p udp --libxo xml,pretty
+ atf_fail "Global UDP statistics do not match: ${count} != 9" ;;
+ esac
+
+
+ #
+ # Check selection of global IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-below-minimum-size>0</dropped-below-minimum-size>
+ <dropped-short-packets>0</dropped-short-packets>
+ <dropped-bad-options>0</dropped-bad-options>
+ <dropped-bad-version>0</dropped-bad-version>
+ <received-fragments>20</received-fragments>
+ <dropped-fragment>10</dropped-fragment>
+ <dropped-fragment-after-timeout>10</dropped-fragment-after-timeout>
+ <dropped-fragments-overflow>0</dropped-fragments-overflow>
+ <atomic-fragments>0</atomic-fragments>
+ <reassembled-packets>0</reassembled-packets>
+ <forwarded-packets>0</forwarded-packets>
+ <packets-not-forwardable>0</packets-not-forwardable>
+ <sent-redirects>0</sent-redirects>
+ <send-packets-fabricated-header>0</send-packets-fabricated-header>
+ <discard-no-mbufs>0</discard-no-mbufs>
+ <discard-no-route>0</discard-no-route>
+ <sent-fragments>0</sent-fragments>
+ <fragments-created>0</fragments-created>
+ <discard-cannot-fragment>0</discard-cannot-fragment>
+ <discard-scope-violations>0</discard-scope-violations>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 20) ;;
+ *) jexec ${jname} netstat -s -p ip6 --libxo xml,pretty
+ atf_fail "Global IPv6 statistics do not match: ${count} != 20" ;;
+ esac
+
+ #
+ # Check selection of global ICMPv6 stats.
+ # XXX-TODO check output histogram (just too hard to parse [no multi-line-grep])
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <icmp6-calls>10</icmp6-calls>
+ <no-route>0</no-route>
+ <admin-prohibited>0</admin-prohibited>
+ <beyond-scope>0</beyond-scope>
+ <address-unreachable>0</address-unreachable>
+ <port-unreachable>0</port-unreachable>
+ <packet-too-big>0</packet-too-big>
+ <time-exceed-transmit>0</time-exceed-transmit>
+ <time-exceed-reassembly>10</time-exceed-reassembly>
+ <bad-header>0</bad-header>
+ <bad-next-header>0</bad-next-header>
+ <bad-option>0</bad-option>
+ <redirects>0</redirects>
+ <unknown>0</unknown>
+ <reflect>0</reflect>
+ <too-many-nd-options>0</too-many-nd-options>
+ <bad-nd-options>0</bad-nd-options>
+ <bad-neighbor-solicitation>0</bad-neighbor-solicitation>
+ <bad-neighbor-advertisement>0</bad-neighbor-advertisement>
+ <bad-router-solicitation>0</bad-router-solicitation>
+ <bad-router-advertisement>0</bad-router-advertisement>
+ <bad-redirect>0</bad-redirect>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 22) ;;
+ *) jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty
+ atf_fail "Global ICMPv6 statistics do not match: ${count} != 22" ;;
+ esac
+
+ #
+ # Check selection of interface IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-invalid-header>0</dropped-invalid-header>
+ <dropped-mtu-exceeded>0</dropped-mtu-exceeded>
+ <dropped-no-route>0</dropped-no-route>
+ <dropped-invalid-destination>0</dropped-invalid-destination>
+ <dropped-unknown-protocol>0</dropped-unknown-protocol>
+ <dropped-truncated>0</dropped-truncated>
+ <sent-forwarded>0</sent-forwarded>
+ <discard-packets>0</discard-packets>
+ <discard-fragments>0</discard-fragments>
+ <fragments-failed>0</fragments-failed>
+ <fragments-created>0</fragments-created>
+ <reassembly-required>20</reassembly-required>
+ <reassembled-packets>0</reassembled-packets>
+ <reassembly-failed>10</reassembly-failed>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 14) ;;
+ *) jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface IPv6 statistics do not match: ${count} != 14" ;;
+ esac
+
+ #
+ # Check selection of interface ICMPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-errors>0</received-errors>
+ <received-destination-unreachable>0</received-destination-unreachable>
+ <received-admin-prohibited>0</received-admin-prohibited>
+ <received-time-exceeded>0</received-time-exceeded>
+ <received-bad-parameter>0</received-bad-parameter>
+ <received-packet-too-big>0</received-packet-too-big>
+ <received-echo-requests>0</received-echo-requests>
+ <received-echo-replies>0</received-echo-replies>
+ <received-router-solicitation>0</received-router-solicitation>
+ <received-router-advertisement>0</received-router-advertisement>
+ <sent-errors>10</sent-errors>
+ <sent-destination-unreachable>0</sent-destination-unreachable>
+ <sent-admin-prohibited>0</sent-admin-prohibited>
+ <sent-time-exceeded>10</sent-time-exceeded>
+ <sent-bad-parameter>0</sent-bad-parameter>
+ <sent-packet-too-big>0</sent-packet-too-big>
+ <sent-echo-requests>0</sent-echo-requests>
+ <sent-echo-replies>0</sent-echo-replies>
+ <sent-router-solicitation>0</sent-router-solicitation>
+ <sent-router-advertisement>0</sent-router-advertisement>
+ <sent-redirects>0</sent-redirects>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 21) ;;
+ *) jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface ICMPv6 statistics do not match: ${count} != 21" ;;
+ esac
+}
+
+atf_test_case "frag6_05_0" "cleanup"
+frag6_05_0_head() {
+ frag6_head 5_0
+}
+
+frag6_05_0_body() {
+
+ # Save current sysctl value.
+ ov=`sysctl -n net.inet6.ip6.maxfrags`
+ echo "${ov}" > ${HOME}/sysctl-$(atf_get ident).txt
+
+ # Never accept fragments.
+ sysctl net.inet6.ip6.maxfrags=0
+
+ frag6_body 5 frag6_05_check_stats_0
+}
+
+frag6_05_0_cleanup() {
+ frag6_cleanup 5_0
+
+ # Restore sysctl back to default.
+ ov=`cat ${HOME}/sysctl-$(atf_get ident).txt`
+ rm -f ${HOME}/sysctl-$(atf_get ident).txt
+ sysctl net.inet6.ip6.maxfrags=${ov}
+}
+
+
+atf_test_case "frag6_05_1" "cleanup"
+frag6_05_1_head() {
+ frag6_head 5_1
+}
+
+frag6_05_1_body() {
+
+ # Save current sysctl value.
+ ov=`sysctl -n net.inet6.ip6.maxfrags`
+ echo "${ov}" > ${HOME}/sysctl-$(atf_get ident).txt
+
+ # Maximum of 10 global system-wide fragments.
+ sysctl net.inet6.ip6.maxfrags=10
+
+ frag6_body 5 frag6_05_check_stats_1
+}
+
+frag6_05_1_cleanup() {
+ frag6_cleanup 5_1
+
+ # Restore sysctl back to default.
+ ov=`cat ${HOME}/sysctl-$(atf_get ident).txt`
+ rm -f ${HOME}/sysctl-$(atf_get ident).txt
+ sysctl net.inet6.ip6.maxfrags=${ov}
+}
+
+
+atf_init_test_cases()
+{
+ atf_add_test_case "frag6_05_0"
+ atf_add_test_case "frag6_05_1"
+}
diff --git a/tests/sys/netinet6/frag6/frag6_06.py b/tests/sys/netinet6/frag6/frag6_06.py
new file mode 100644
index 000000000000..dc185256a6d4
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_06.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import socket
+import sys
+from time import sleep
+
+def main():
+ parser = argparse.ArgumentParser("frag6.py",
+ description="IPv6 fragementation test tool")
+ parser.add_argument('--sendif', nargs=1,
+ required=True,
+ help='The interface through which the packet will be sent')
+ parser.add_argument('--recvif', nargs=1,
+ required=True,
+ help='The interface on which to check for the packet')
+ parser.add_argument('--src', nargs=1,
+ required=True,
+ help='The source IP address')
+ parser.add_argument('--to', nargs=1,
+ required=True,
+ help='The destination IP address')
+ parser.add_argument('--debug',
+ required=False, action='store_true',
+ help='Enable test debugging')
+
+ args = parser.parse_args()
+
+
+ ########################################################################
+ #
+ # Sysctl set to accept (no|maximum 10) fragments on a reassembly queue.
+ #
+ # A: Discarded.
+ # R: Silence (statistics only) or ICMPv6 timeout expiry.
+ #
+ data = "66666666"
+ for i in range(20):
+ foffset=(i * (int)(16 / 8))
+ ip6f01 = sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrFragment(offset=foffset, m=1, id=6) / \
+ sp.UDP(dport=3456, sport=6543) / \
+ data
+ if args.debug :
+ ip6f01.display()
+ sp.sendp(ip6f01, iface=args.sendif[0], verbose=False)
+
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netinet6/frag6/frag6_06.sh b/tests/sys/netinet6/frag6/frag6_06.sh
new file mode 100755
index 000000000000..a12da99a13bf
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_06.sh
@@ -0,0 +1,262 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/frag6.subr
+
+frag6_06_pre_test_0() {
+
+ local jname ifname
+ jname=$1
+
+ case "${jname}" in
+ "") echo "ERROR: jname is empty"; return ;;
+ esac
+
+ # Never accept fragments.
+ jexec ${jname} sysctl net.inet6.ip6.maxfragbucketsize=0
+}
+
+
+frag6_06_check_stats_0() {
+
+ local jname ifname
+ jname=$1
+ ifname=$2
+
+ case "${jname}" in
+ "") echo "ERROR: jname is empty"; return ;;
+ esac
+ case "${ifname}" in
+ "") echo "ERROR: ifname is empty"; return ;;
+ esac
+
+ # Defaults are: IPV6_FRAGTTL 120 slowtimo ticks.
+ # pfslowtimo() is run at hz/2. So this takes 60s.
+ # This is awefully long for a test case.
+ # The Python script has to wait for this already to get the ICMPv6
+ # hence we do not sleep here anymore.
+
+ nf=`jexec ${jname} sysctl -n net.inet6.ip6.frag6_nfragpackets`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "VNET frag6_nfragpackets not 0 but: ${nf}" ;;
+ esac
+ nf=`sysctl -n net.inet6.ip6.frag6_nfrags`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "Global frag6_nfrags not 0 but: ${nf}" ;;
+ esac
+
+ #
+ # Check that the sysctl is set to what we expect.
+ #
+ sn=`jexec ${jname} sysctl -n net.inet6.ip6.maxfragbucketsize`
+ case "${sn}" in
+ 0) ;;
+ *) atf_fail "Sysctl net.inet6.ip6.maxfragbucketsize is ${sn} and not 0" ;;
+ esac
+
+ #
+ # Check selection of global UDP stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-datagrams>0</received-datagrams>
+ <dropped-incomplete-headers>0</dropped-incomplete-headers>
+ <dropped-bad-data-length>0</dropped-bad-data-length>
+ <dropped-bad-checksum>0</dropped-bad-checksum>
+ <dropped-no-checksum>0</dropped-no-checksum>
+ <dropped-no-socket>0</dropped-no-socket>
+ <dropped-broadcast-multicast>0</dropped-broadcast-multicast>
+ <dropped-full-socket-buffer>0</dropped-full-socket-buffer>
+ <not-for-hashed-pcb>0</not-for-hashed-pcb>
+EOF
+ count=`jexec ${jname} netstat -s -p udp --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 9) ;;
+ *) jexec ${jname} netstat -s -p udp --libxo xml,pretty
+ atf_fail "Global UDP statistics do not match: ${count} != 9" ;;
+ esac
+
+
+ #
+ # Check selection of global IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-below-minimum-size>0</dropped-below-minimum-size>
+ <dropped-short-packets>0</dropped-short-packets>
+ <dropped-bad-options>0</dropped-bad-options>
+ <dropped-bad-version>0</dropped-bad-version>
+ <received-fragments>20</received-fragments>
+ <dropped-fragment>20</dropped-fragment>
+ <dropped-fragment-after-timeout>0</dropped-fragment-after-timeout>
+ <dropped-fragments-overflow>0</dropped-fragments-overflow>
+ <atomic-fragments>0</atomic-fragments>
+ <reassembled-packets>0</reassembled-packets>
+ <forwarded-packets>0</forwarded-packets>
+ <packets-not-forwardable>0</packets-not-forwardable>
+ <sent-redirects>0</sent-redirects>
+ <send-packets-fabricated-header>0</send-packets-fabricated-header>
+ <discard-no-mbufs>0</discard-no-mbufs>
+ <discard-no-route>0</discard-no-route>
+ <sent-fragments>0</sent-fragments>
+ <fragments-created>0</fragments-created>
+ <discard-cannot-fragment>0</discard-cannot-fragment>
+ <discard-scope-violations>0</discard-scope-violations>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 20) ;;
+ *) jexec ${jname} netstat -s -p ip6 --libxo xml,pretty
+ atf_fail "Global IPv6 statistics do not match: ${count} != 20" ;;
+ esac
+
+ #
+ # Check selection of global ICMPv6 stats.
+ # XXX-TODO check output histogram (just too hard to parse [no multi-line-grep])
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <icmp6-calls>0</icmp6-calls>
+ <no-route>0</no-route>
+ <admin-prohibited>0</admin-prohibited>
+ <beyond-scope>0</beyond-scope>
+ <address-unreachable>0</address-unreachable>
+ <port-unreachable>0</port-unreachable>
+ <packet-too-big>0</packet-too-big>
+ <time-exceed-transmit>0</time-exceed-transmit>
+ <time-exceed-reassembly>0</time-exceed-reassembly>
+ <bad-header>0</bad-header>
+ <bad-next-header>0</bad-next-header>
+ <bad-option>0</bad-option>
+ <redirects>0</redirects>
+ <unknown>0</unknown>
+ <reflect>0</reflect>
+ <too-many-nd-options>0</too-many-nd-options>
+ <bad-nd-options>0</bad-nd-options>
+ <bad-neighbor-solicitation>0</bad-neighbor-solicitation>
+ <bad-neighbor-advertisement>0</bad-neighbor-advertisement>
+ <bad-router-solicitation>0</bad-router-solicitation>
+ <bad-router-advertisement>0</bad-router-advertisement>
+ <bad-redirect>0</bad-redirect>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 22) ;;
+ *) jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty
+ atf_fail "Global ICMPv6 statistics do not match: ${count} != 22" ;;
+ esac
+
+ #
+ # Check selection of interface IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-invalid-header>0</dropped-invalid-header>
+ <dropped-mtu-exceeded>0</dropped-mtu-exceeded>
+ <dropped-no-route>0</dropped-no-route>
+ <dropped-invalid-destination>0</dropped-invalid-destination>
+ <dropped-unknown-protocol>0</dropped-unknown-protocol>
+ <dropped-truncated>0</dropped-truncated>
+ <sent-forwarded>0</sent-forwarded>
+ <discard-packets>0</discard-packets>
+ <discard-fragments>0</discard-fragments>
+ <fragments-failed>0</fragments-failed>
+ <fragments-created>0</fragments-created>
+ <reassembly-required>20</reassembly-required>
+ <reassembled-packets>0</reassembled-packets>
+ <reassembly-failed>20</reassembly-failed>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 14) ;;
+ *) jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface IPv6 statistics do not match: ${count} != 14" ;;
+ esac
+
+ #
+ # Check selection of interface ICMPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-errors>0</received-errors>
+ <received-destination-unreachable>0</received-destination-unreachable>
+ <received-admin-prohibited>0</received-admin-prohibited>
+ <received-time-exceeded>0</received-time-exceeded>
+ <received-bad-parameter>0</received-bad-parameter>
+ <received-packet-too-big>0</received-packet-too-big>
+ <received-echo-requests>0</received-echo-requests>
+ <received-echo-replies>0</received-echo-replies>
+ <received-router-solicitation>0</received-router-solicitation>
+ <received-router-advertisement>0</received-router-advertisement>
+ <sent-errors>0</sent-errors>
+ <sent-destination-unreachable>0</sent-destination-unreachable>
+ <sent-admin-prohibited>0</sent-admin-prohibited>
+ <sent-time-exceeded>0</sent-time-exceeded>
+ <sent-bad-parameter>0</sent-bad-parameter>
+ <sent-packet-too-big>0</sent-packet-too-big>
+ <sent-echo-requests>0</sent-echo-requests>
+ <sent-echo-replies>0</sent-echo-replies>
+ <sent-router-solicitation>0</sent-router-solicitation>
+ <sent-router-advertisement>0</sent-router-advertisement>
+ <sent-redirects>0</sent-redirects>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 21) ;;
+ *) jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface ICMPv6 statistics do not match: ${count} != 21" ;;
+ esac
+}
+
+atf_test_case "frag6_06_0" "cleanup"
+frag6_06_0_head() {
+ frag6_head 6_0
+}
+
+frag6_06_0_body() {
+ frag6_body 6 frag6_06_check_stats_0 frag6_06_pre_test_0
+}
+
+frag6_06_0_cleanup() {
+ frag6_cleanup 6_0
+
+ # No need to restore the sysctl back to default as the jail is gone.
+}
+
+
+#atf_test_case "frag6_06_1" "cleanup"
+# There is no point in testing a != 0 value for net.inet6.ip6.maxfragbucketsize.
+# We would have to be able to generate hash collisions to end up in the same
+# bucket (or re-compile a kernel with only 1 bucket).
+
+
+atf_init_test_cases()
+{
+ atf_add_test_case "frag6_06_0"
+}
diff --git a/tests/sys/netinet6/frag6/frag6_07.py b/tests/sys/netinet6/frag6/frag6_07.py
new file mode 100644
index 000000000000..cd2d5096b27f
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_07.py
@@ -0,0 +1,179 @@
+#!/usr/bin/env python
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import socket
+import sys
+from sniffer import Sniffer
+from time import sleep
+
+def check_icmp6_error(args, packet):
+ ip6 = packet.getlayer(sp.IPv6)
+ if not ip6:
+ return False
+ oip6 = sp.IPv6(src=args.src[0], dst=args.to[0])
+ if ip6.dst != oip6.src:
+ return False
+ icmp6 = packet.getlayer(sp.ICMPv6ParamProblem)
+ if not icmp6:
+ return False
+ # ICMP6_PARAMPROB_HEADER 0
+ if icmp6.code != 0:
+ return False
+ # Should we check the payload as well?
+ # We are running in a very isolated environment and nothing else
+ # should trigger an ICMPv6 Param Prob so leave it.
+ #icmp6.display()
+ return True
+
+def check_icmp6_error_2(args, packet):
+ ip6 = packet.getlayer(sp.IPv6)
+ if not ip6:
+ return False
+ oip6 = sp.IPv6(src=args.src[0], dst=args.to[0])
+ if ip6.dst != oip6.src:
+ return False
+ icmp6 = packet.getlayer(sp.ICMPv6TimeExceeded)
+ if not icmp6:
+ return False
+ # ICMP6_TIME_EXCEED_REASSEMBLY 1
+ if icmp6.code != 1:
+ return False
+ # Should we check the payload as well?
+ # We are running in a very isolated environment and nothing else
+ # should trigger an ICMPv6 Time Exceeded / Frag reassembly so leave it.
+ #icmp6.display()
+ return True
+
+def main():
+ parser = argparse.ArgumentParser("frag6.py",
+ description="IPv6 fragementation test tool")
+ parser.add_argument('--sendif', nargs=1,
+ required=True,
+ help='The interface through which the packet will be sent')
+ parser.add_argument('--recvif', nargs=1,
+ required=True,
+ help='The interface on which to check for the packet')
+ parser.add_argument('--src', nargs=1,
+ required=True,
+ help='The source IP address')
+ parser.add_argument('--to', nargs=1,
+ required=True,
+ help='The destination IP address')
+ parser.add_argument('--debug',
+ required=False, action='store_true',
+ help='Enable test debugging')
+
+ args = parser.parse_args()
+
+
+ # Start sniffing on recvif
+ sniffer = Sniffer(args, check_icmp6_error)
+ sniffer2 = Sniffer(args, check_icmp6_error_2)
+
+
+ ########################################################################
+ #
+ # Two fragments with payload and offset set to add up to >64k.
+ #
+ # Make a first fragment arrive and a second to explode everything.
+ #
+ # A: Reassembly failure.
+ # R: ICMPv6 param prob, param header.
+ # R: ICMPv6 timeout (1st frag, off=0)
+ #
+ data = "6" * 1280
+ ip6f01 = \
+ sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrFragment(offset=0, m=1, id=7) / \
+ sp.UDP(dport=3456, sport=6543) / \
+ data
+ ip6f02 = \
+ sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrFragment(offset=0x1fff, m=1, id=7) / \
+ sp.UDP(dport=3456, sport=6543) / \
+ data
+ if args.debug :
+ ip6f01.display()
+ ip6f02.display()
+ sp.sendp(ip6f01, iface=args.sendif[0], verbose=False)
+ sp.sendp(ip6f02, iface=args.sendif[0], verbose=False)
+
+ sleep(1.00)
+ sniffer.setEnd()
+ sniffer.join()
+ if not sniffer.foundCorrectPacket:
+ sys.exit(1)
+
+
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ##
+ #
+ # A fragment with payload and offset set to add up to >64k.
+ #
+ # Try again with the first packet to make things explode.
+ #
+ # A: Reassembly failure.
+ # R: ICMPv6 param prob, param header.
+ #
+
+ # Start sniffing on recvif
+ sniffer = Sniffer(args, check_icmp6_error)
+
+ ip6f01 = \
+ sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrFragment(offset=0x1fff, m=1, id=0x7001) / \
+ sp.UDP(dport=3456, sport=6543) / \
+ data
+ if args.debug :
+ ip6f01.display()
+ sp.sendp(ip6f01, iface=args.sendif[0], verbose=False)
+
+ sleep(0.10)
+ sniffer.setEnd()
+ sniffer.join()
+ if not sniffer.foundCorrectPacket:
+ sys.exit(1)
+
+ # Wait for expiry from first test run.
+ sleep(3)
+ sniffer2.setEnd()
+ sniffer2.join()
+ if not sniffer2.foundCorrectPacket:
+ sys.exit(1)
+
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netinet6/frag6/frag6_07.sh b/tests/sys/netinet6/frag6/frag6_07.sh
new file mode 100755
index 000000000000..09c82102b9b0
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_07.sh
@@ -0,0 +1,234 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/frag6.subr
+
+frag6_07_check_stats() {
+
+ local jname ifname
+ jname=$1
+ ifname=$2
+
+ case "${jname}" in
+ "") echo "ERROR: jname is empty"; return ;;
+ esac
+ case "${ifname}" in
+ "") echo "ERROR: ifname is empty"; return ;;
+ esac
+
+ # Defaults are: IPV6_FRAGTTL 120 slowtimo ticks.
+ # pfslowtimo() is run at hz/2. So this takes 60s.
+ # This is awefully long for a test case.
+ # The Python script has to wait for this already to get the ICMPv6
+ # hence we do not sleep here anymore.
+
+ nf=`jexec ${jname} sysctl -n net.inet6.ip6.frag6_nfragpackets`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "VNET frag6_nfragpackets not 0 but: ${nf}" ;;
+ esac
+ nf=`sysctl -n net.inet6.ip6.frag6_nfrags`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "Global frag6_nfrags not 0 but: ${nf}" ;;
+ esac
+
+ #
+ # Check selection of global UDP stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-datagrams>0</received-datagrams>
+ <dropped-incomplete-headers>0</dropped-incomplete-headers>
+ <dropped-bad-data-length>0</dropped-bad-data-length>
+ <dropped-bad-checksum>0</dropped-bad-checksum>
+ <dropped-no-checksum>0</dropped-no-checksum>
+ <dropped-no-socket>0</dropped-no-socket>
+ <dropped-broadcast-multicast>0</dropped-broadcast-multicast>
+ <dropped-full-socket-buffer>0</dropped-full-socket-buffer>
+ <not-for-hashed-pcb>0</not-for-hashed-pcb>
+EOF
+ count=`jexec ${jname} netstat -s -p udp --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 9) ;;
+ *) jexec ${jname} netstat -s -p udp --libxo xml,pretty
+ atf_fail "Global UDP statistics do not match: ${count} != 9" ;;
+ esac
+
+
+ #
+ # Check selection of global IPv6 stats.
+ # XXX-BZ Only ICMPv6 errors and no proper stats!
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-below-minimum-size>0</dropped-below-minimum-size>
+ <dropped-short-packets>0</dropped-short-packets>
+ <dropped-bad-options>0</dropped-bad-options>
+ <dropped-bad-version>0</dropped-bad-version>
+ <received-fragments>3</received-fragments>
+ <dropped-fragment>0</dropped-fragment>
+ <dropped-fragment-after-timeout>1</dropped-fragment-after-timeout>
+ <dropped-fragments-overflow>0</dropped-fragments-overflow>
+ <atomic-fragments>0</atomic-fragments>
+ <reassembled-packets>0</reassembled-packets>
+ <forwarded-packets>0</forwarded-packets>
+ <packets-not-forwardable>0</packets-not-forwardable>
+ <sent-redirects>0</sent-redirects>
+ <send-packets-fabricated-header>0</send-packets-fabricated-header>
+ <discard-no-mbufs>0</discard-no-mbufs>
+ <discard-no-route>0</discard-no-route>
+ <sent-fragments>0</sent-fragments>
+ <fragments-created>0</fragments-created>
+ <discard-cannot-fragment>0</discard-cannot-fragment>
+ <discard-scope-violations>0</discard-scope-violations>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 20) ;;
+ *) jexec ${jname} netstat -s -p ip6 --libxo xml,pretty
+ atf_fail "Global IPv6 statistics do not match: ${count} != 20" ;;
+ esac
+
+ #
+ # Check selection of global ICMPv6 stats.
+ # XXX-TODO check output histogram (just too hard to parse [no multi-line-grep])
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <icmp6-calls>3</icmp6-calls>
+ <no-route>0</no-route>
+ <admin-prohibited>0</admin-prohibited>
+ <beyond-scope>0</beyond-scope>
+ <address-unreachable>0</address-unreachable>
+ <port-unreachable>0</port-unreachable>
+ <packet-too-big>0</packet-too-big>
+ <time-exceed-transmit>0</time-exceed-transmit>
+ <time-exceed-reassembly>1</time-exceed-reassembly>
+ <bad-header>2</bad-header>
+ <bad-next-header>0</bad-next-header>
+ <bad-option>0</bad-option>
+ <redirects>0</redirects>
+ <unknown>0</unknown>
+ <reflect>0</reflect>
+ <too-many-nd-options>0</too-many-nd-options>
+ <bad-nd-options>0</bad-nd-options>
+ <bad-neighbor-solicitation>0</bad-neighbor-solicitation>
+ <bad-neighbor-advertisement>0</bad-neighbor-advertisement>
+ <bad-router-solicitation>0</bad-router-solicitation>
+ <bad-router-advertisement>0</bad-router-advertisement>
+ <bad-redirect>0</bad-redirect>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 22) ;;
+ *) jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty
+ atf_fail "Global ICMPv6 statistics do not match: ${count} != 22" ;;
+ esac
+
+ #
+ # Check selection of interface IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-invalid-header>0</dropped-invalid-header>
+ <dropped-mtu-exceeded>0</dropped-mtu-exceeded>
+ <dropped-no-route>0</dropped-no-route>
+ <dropped-invalid-destination>0</dropped-invalid-destination>
+ <dropped-unknown-protocol>0</dropped-unknown-protocol>
+ <dropped-truncated>0</dropped-truncated>
+ <sent-forwarded>0</sent-forwarded>
+ <discard-packets>0</discard-packets>
+ <discard-fragments>0</discard-fragments>
+ <fragments-failed>0</fragments-failed>
+ <fragments-created>0</fragments-created>
+ <reassembly-required>3</reassembly-required>
+ <reassembled-packets>0</reassembled-packets>
+ <reassembly-failed>0</reassembly-failed>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 14) ;;
+ *) jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface IPv6 statistics do not match: ${count} != 14" ;;
+ esac
+
+ #
+ # Check selection of interface ICMPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-errors>0</received-errors>
+ <received-destination-unreachable>0</received-destination-unreachable>
+ <received-admin-prohibited>0</received-admin-prohibited>
+ <received-time-exceeded>0</received-time-exceeded>
+ <received-bad-parameter>0</received-bad-parameter>
+ <received-packet-too-big>0</received-packet-too-big>
+ <received-echo-requests>0</received-echo-requests>
+ <received-echo-replies>0</received-echo-replies>
+ <received-router-solicitation>0</received-router-solicitation>
+ <received-router-advertisement>0</received-router-advertisement>
+ <sent-errors>3</sent-errors>
+ <sent-destination-unreachable>0</sent-destination-unreachable>
+ <sent-admin-prohibited>0</sent-admin-prohibited>
+ <sent-time-exceeded>1</sent-time-exceeded>
+ <sent-bad-parameter>2</sent-bad-parameter>
+ <sent-packet-too-big>0</sent-packet-too-big>
+ <sent-echo-requests>0</sent-echo-requests>
+ <sent-echo-replies>0</sent-echo-replies>
+ <sent-router-solicitation>0</sent-router-solicitation>
+ <sent-router-advertisement>0</sent-router-advertisement>
+ <sent-redirects>0</sent-redirects>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 21) ;;
+ *) jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface ICMPv6 statistics do not match: ${count} != 21" ;;
+ esac
+}
+
+atf_test_case "frag6_07" "cleanup"
+frag6_07_head() {
+ frag6_head 7
+}
+
+frag6_07_body() {
+ if [ "$(atf_config_get ci false)" = "true" ]; then
+ atf_skip "https://bugs.freebsd.org/244170"
+ fi
+ frag6_body 7 frag6_07_check_stats
+}
+
+frag6_07_cleanup() {
+ frag6_cleanup 7
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "frag6_07"
+}
diff --git a/tests/sys/netinet6/frag6/frag6_08.py b/tests/sys/netinet6/frag6/frag6_08.py
new file mode 100644
index 000000000000..b0f8bf122d14
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_08.py
@@ -0,0 +1,153 @@
+#!/usr/bin/env python
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import socket
+import sys
+from sniffer import Sniffer
+from time import sleep
+
+def check_icmp6_error(args, packet):
+ ip6 = packet.getlayer(sp.IPv6)
+ if not ip6:
+ return False
+ oip6 = sp.IPv6(src=args.src[0], dst=args.to[0])
+ if ip6.dst != oip6.src:
+ return False
+ icmp6 = packet.getlayer(sp.ICMPv6ParamProblem)
+ if not icmp6:
+ return False
+ # ICMP6_PARAMPROB_HEADER 0
+ if icmp6.code != 0:
+ return False
+ # Should we check the payload as well?
+ # We are running in a very isolated environment and nothing else
+ # should trigger an ICMPv6 Param Prob so leave it.
+ #icmp6.display()
+ return True
+
+def check_icmp6_error_2(args, packet):
+ ip6 = packet.getlayer(sp.IPv6)
+ if not ip6:
+ return False
+ oip6 = sp.IPv6(src=args.src[0], dst=args.to[0])
+ if ip6.dst != oip6.src:
+ return False
+ icmp6 = packet.getlayer(sp.ICMPv6TimeExceeded)
+ if not icmp6:
+ return False
+ # ICMP6_TIME_EXCEED_REASSEMBLY 1
+ if icmp6.code != 1:
+ return False
+ # Should we check the payload as well?
+ # We are running in a very isolated environment and nothing else
+ # should trigger an ICMPv6 Time Exceeded / Frag reassembly so leave it.
+ #icmp6.display()
+ return True
+
+
+def main():
+ parser = argparse.ArgumentParser("frag6.py",
+ description="IPv6 fragementation test tool")
+ parser.add_argument('--sendif', nargs=1,
+ required=True,
+ help='The interface through which the packet will be sent')
+ parser.add_argument('--recvif', nargs=1,
+ required=True,
+ help='The interface on which to check for the packet')
+ parser.add_argument('--src', nargs=1,
+ required=True,
+ help='The source IP address')
+ parser.add_argument('--to', nargs=1,
+ required=True,
+ help='The destination IP address')
+ parser.add_argument('--debug',
+ required=False, action='store_true',
+ help='Enable test debugging')
+
+ args = parser.parse_args()
+
+
+ # Start sniffing on recvif
+ sniffer = Sniffer(args, check_icmp6_error)
+ sniffer2 = Sniffer(args, check_icmp6_error_2)
+
+
+ ########################################################################
+ #
+ # A fragment with payload and offset set to add up to >64k when
+ # another frag with offset=0 arrives and has an unfrag part.
+ # This is us checking for all fragments queued already when the
+ # one with off=0 arrives. Note: unless the off=0 has its own problem
+ # it will be queued and off!=0 ones might be expunged with param prob.
+ #
+ # A: Reassembly failure, timeout after
+ # R: ICMPv6 param prob, param header (1st frag)
+ # R: ICMPv6 time exceeded (2nd frag, as off=0)
+ #
+ data = "6" * 15
+ ip6f01 = \
+ sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrFragment(offset=0x1ffc, m=0, id=8) / \
+ sp.UDP(dport=3456, sport=6543) / \
+ data
+ data = "6" * 8
+ ip6f02 = \
+ sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrDestOpt(options = \
+ sp.PadN(optdata="\x00\x00\x00\x00\x00\x00")) / \
+ sp.IPv6ExtHdrFragment(offset=0, m=1, id=8) / \
+ sp.UDP(dport=3456, sport=6543) / \
+ data
+ if args.debug :
+ ip6f01.display()
+ ip6f02.display()
+ sp.sendp(ip6f01, iface=args.sendif[0], verbose=False)
+ sp.sendp(ip6f02, iface=args.sendif[0], verbose=False)
+
+ sleep(1.00)
+ sniffer.setEnd()
+ sniffer.join()
+ if not sniffer.foundCorrectPacket:
+ sys.exit(1)
+ sleep(3)
+ sniffer2.setEnd()
+ sniffer2.join()
+ if not sniffer2.foundCorrectPacket:
+ sys.exit(1)
+
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netinet6/frag6/frag6_08.sh b/tests/sys/netinet6/frag6/frag6_08.sh
new file mode 100755
index 000000000000..f3f8924d5d0f
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_08.sh
@@ -0,0 +1,231 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/frag6.subr
+
+frag6_08_check_stats() {
+
+ local jname ifname
+ jname=$1
+ ifname=$2
+
+ case "${jname}" in
+ "") echo "ERROR: jname is empty"; return ;;
+ esac
+ case "${ifname}" in
+ "") echo "ERROR: ifname is empty"; return ;;
+ esac
+
+ # Defaults are: IPV6_FRAGTTL 120 slowtimo ticks.
+ # pfslowtimo() is run at hz/2. So this takes 60s.
+ # This is awefully long for a test case.
+ # The Python script has to wait for this already to get the ICMPv6
+ # hence we do not sleep here anymore.
+
+ nf=`jexec ${jname} sysctl -n net.inet6.ip6.frag6_nfragpackets`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "VNET frag6_nfragpackets not 0 but: ${nf}" ;;
+ esac
+ nf=`sysctl -n net.inet6.ip6.frag6_nfrags`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "Global frag6_nfrags not 0 but: ${nf}" ;;
+ esac
+
+ #
+ # Check selection of global UDP stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-datagrams>0</received-datagrams>
+ <dropped-incomplete-headers>0</dropped-incomplete-headers>
+ <dropped-bad-data-length>0</dropped-bad-data-length>
+ <dropped-bad-checksum>0</dropped-bad-checksum>
+ <dropped-no-checksum>0</dropped-no-checksum>
+ <dropped-no-socket>0</dropped-no-socket>
+ <dropped-broadcast-multicast>0</dropped-broadcast-multicast>
+ <dropped-full-socket-buffer>0</dropped-full-socket-buffer>
+ <not-for-hashed-pcb>0</not-for-hashed-pcb>
+EOF
+ count=`jexec ${jname} netstat -s -p udp --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 9) ;;
+ *) jexec ${jname} netstat -s -p udp --libxo xml,pretty
+ atf_fail "Global UDP statistics do not match: ${count} != 9" ;;
+ esac
+
+
+ #
+ # Check selection of global IPv6 stats.
+ # XXX-BZ Only ICMPv6 errors and no proper stats!
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-below-minimum-size>0</dropped-below-minimum-size>
+ <dropped-short-packets>0</dropped-short-packets>
+ <dropped-bad-options>0</dropped-bad-options>
+ <dropped-bad-version>0</dropped-bad-version>
+ <received-fragments>2</received-fragments>
+ <dropped-fragment>0</dropped-fragment>
+ <dropped-fragment-after-timeout>1</dropped-fragment-after-timeout>
+ <dropped-fragments-overflow>0</dropped-fragments-overflow>
+ <atomic-fragments>0</atomic-fragments>
+ <reassembled-packets>0</reassembled-packets>
+ <forwarded-packets>0</forwarded-packets>
+ <packets-not-forwardable>0</packets-not-forwardable>
+ <sent-redirects>0</sent-redirects>
+ <send-packets-fabricated-header>0</send-packets-fabricated-header>
+ <discard-no-mbufs>0</discard-no-mbufs>
+ <discard-no-route>0</discard-no-route>
+ <sent-fragments>0</sent-fragments>
+ <fragments-created>0</fragments-created>
+ <discard-cannot-fragment>0</discard-cannot-fragment>
+ <discard-scope-violations>0</discard-scope-violations>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 20) ;;
+ *) jexec ${jname} netstat -s -p ip6 --libxo xml,pretty
+ atf_fail "Global IPv6 statistics do not match: ${count} != 20" ;;
+ esac
+
+ #
+ # Check selection of global ICMPv6 stats.
+ # XXX-TODO check output histogram (just too hard to parse [no multi-line-grep])
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <icmp6-calls>2</icmp6-calls>
+ <no-route>0</no-route>
+ <admin-prohibited>0</admin-prohibited>
+ <beyond-scope>0</beyond-scope>
+ <address-unreachable>0</address-unreachable>
+ <port-unreachable>0</port-unreachable>
+ <packet-too-big>0</packet-too-big>
+ <time-exceed-transmit>0</time-exceed-transmit>
+ <time-exceed-reassembly>1</time-exceed-reassembly>
+ <bad-header>1</bad-header>
+ <bad-next-header>0</bad-next-header>
+ <bad-option>0</bad-option>
+ <redirects>0</redirects>
+ <unknown>0</unknown>
+ <reflect>0</reflect>
+ <too-many-nd-options>0</too-many-nd-options>
+ <bad-nd-options>0</bad-nd-options>
+ <bad-neighbor-solicitation>0</bad-neighbor-solicitation>
+ <bad-neighbor-advertisement>0</bad-neighbor-advertisement>
+ <bad-router-solicitation>0</bad-router-solicitation>
+ <bad-router-advertisement>0</bad-router-advertisement>
+ <bad-redirect>0</bad-redirect>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 22) ;;
+ *) jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty
+ atf_fail "Global ICMPv6 statistics do not match: ${count} != 22" ;;
+ esac
+
+ #
+ # Check selection of interface IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-invalid-header>0</dropped-invalid-header>
+ <dropped-mtu-exceeded>0</dropped-mtu-exceeded>
+ <dropped-no-route>0</dropped-no-route>
+ <dropped-invalid-destination>0</dropped-invalid-destination>
+ <dropped-unknown-protocol>0</dropped-unknown-protocol>
+ <dropped-truncated>0</dropped-truncated>
+ <sent-forwarded>0</sent-forwarded>
+ <discard-packets>0</discard-packets>
+ <discard-fragments>0</discard-fragments>
+ <fragments-failed>0</fragments-failed>
+ <fragments-created>0</fragments-created>
+ <reassembly-required>2</reassembly-required>
+ <reassembled-packets>0</reassembled-packets>
+ <reassembly-failed>0</reassembly-failed>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 14) ;;
+ *) jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface IPv6 statistics do not match: ${count} != 14" ;;
+ esac
+
+ #
+ # Check selection of interface ICMPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-errors>0</received-errors>
+ <received-destination-unreachable>0</received-destination-unreachable>
+ <received-admin-prohibited>0</received-admin-prohibited>
+ <received-time-exceeded>0</received-time-exceeded>
+ <received-bad-parameter>0</received-bad-parameter>
+ <received-packet-too-big>0</received-packet-too-big>
+ <received-echo-requests>0</received-echo-requests>
+ <received-echo-replies>0</received-echo-replies>
+ <received-router-solicitation>0</received-router-solicitation>
+ <received-router-advertisement>0</received-router-advertisement>
+ <sent-errors>2</sent-errors>
+ <sent-destination-unreachable>0</sent-destination-unreachable>
+ <sent-admin-prohibited>0</sent-admin-prohibited>
+ <sent-time-exceeded>1</sent-time-exceeded>
+ <sent-bad-parameter>1</sent-bad-parameter>
+ <sent-packet-too-big>0</sent-packet-too-big>
+ <sent-echo-requests>0</sent-echo-requests>
+ <sent-echo-replies>0</sent-echo-replies>
+ <sent-router-solicitation>0</sent-router-solicitation>
+ <sent-router-advertisement>0</sent-router-advertisement>
+ <sent-redirects>0</sent-redirects>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 21) ;;
+ *) jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface ICMPv6 statistics do not match: ${count} != 21" ;;
+ esac
+}
+
+atf_test_case "frag6_08" "cleanup"
+frag6_08_head() {
+ frag6_head 8
+}
+
+frag6_08_body() {
+ frag6_body 8 frag6_08_check_stats
+}
+
+frag6_08_cleanup() {
+ frag6_cleanup 8
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "frag6_08"
+}
diff --git a/tests/sys/netinet6/frag6/frag6_09.py b/tests/sys/netinet6/frag6/frag6_09.py
new file mode 100644
index 000000000000..a7cea6648ded
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_09.py
@@ -0,0 +1,110 @@
+#!/usr/bin/env python
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import socket
+import sys
+from sniffer import Sniffer
+from time import sleep
+
+def check_icmp6_error(args, packet):
+ ip6 = packet.getlayer(sp.IPv6)
+ if not ip6:
+ return False
+ oip6 = sp.IPv6(src=args.src[0], dst=args.to[0])
+ if ip6.dst != oip6.src:
+ return False
+ icmp6 = packet.getlayer(sp.ICMPv6TimeExceeded)
+ if not icmp6:
+ return False
+ # ICMP6_TIME_EXCEED_REASSEMBLY 1
+ if icmp6.code != 1:
+ return False
+ # Should we check the payload as well?
+ # We are running in a very isolated environment and nothing else
+ # should trigger an ICMPv6 Time Exceeded / Frag reassembly so leave it.
+ #icmp6.display()
+ return True
+
+
+def main():
+ parser = argparse.ArgumentParser("frag6.py",
+ description="IPv6 fragementation test tool")
+ parser.add_argument('--sendif', nargs=1,
+ required=True,
+ help='The interface through which the packet will be sent')
+ parser.add_argument('--recvif', nargs=1,
+ required=True,
+ help='The interface on which to check for the packet')
+ parser.add_argument('--src', nargs=1,
+ required=True,
+ help='The source IP address')
+ parser.add_argument('--to', nargs=1,
+ required=True,
+ help='The destination IP address')
+ parser.add_argument('--debug',
+ required=False, action='store_true',
+ help='Enable test debugging')
+
+ args = parser.parse_args()
+
+
+ # Start sniffing on recvif
+ sniffer = Sniffer(args, check_icmp6_error)
+
+
+ ########################################################################
+ #
+ # A single start fragment.
+ #
+ # A: Waiting for more data.
+ # R: Timeout / Expiry.
+ #
+ ip6f01 = sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrFragment(offset=0, m=1, id=2) / \
+ sp.UDP(dport=3456, sport=6543)
+ if args.debug :
+ ip6f01.display()
+ sp.sendp(ip6f01, iface=args.sendif[0], verbose=False)
+
+ # Wait for ICMPv6 error generation on timeout.
+ sleep(3)
+ sniffer.setEnd()
+ sniffer.join()
+ if not sniffer.foundCorrectPacket:
+ sys.exit(1)
+
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netinet6/frag6/frag6_09.sh b/tests/sys/netinet6/frag6/frag6_09.sh
new file mode 100755
index 000000000000..922ec85f14f6
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_09.sh
@@ -0,0 +1,228 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/frag6.subr
+
+atf_test_case "frag6_09" "cleanup"
+frag6_09_head() {
+ frag6_head 9
+}
+
+frag6_09_check_stats() {
+
+ local jname ifname
+ jname=$1
+ ifname=$2
+
+ case "${jname}" in
+ "") echo "ERROR: jname is empty"; return ;;
+ esac
+ case "${ifname}" in
+ "") echo "ERROR: ifname is empty"; return ;;
+ esac
+
+ # Defaults are: IPV6_FRAGTTL 120 slowtimo ticks.
+ # pfslowtimo() is run at hz/2. So this takes 60s.
+ # This is awefully long for a test case.
+ # The Python script has to wait for this already to get the ICMPv6
+ # hence we do not sleep here anymore.
+
+ nf=`jexec ${jname} sysctl -n net.inet6.ip6.frag6_nfragpackets`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "VNET frag6_nfragpackets not 0 but: ${nf}" ;;
+ esac
+ nf=`sysctl -n net.inet6.ip6.frag6_nfrags`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "Global frag6_nfrags not 0 but: ${nf}" ;;
+ esac
+
+ #
+ # Check selection of global UDP stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-datagrams>0</received-datagrams>
+ <dropped-incomplete-headers>0</dropped-incomplete-headers>
+ <dropped-bad-data-length>0</dropped-bad-data-length>
+ <dropped-bad-checksum>0</dropped-bad-checksum>
+ <dropped-no-checksum>0</dropped-no-checksum>
+ <dropped-no-socket>0</dropped-no-socket>
+ <dropped-broadcast-multicast>0</dropped-broadcast-multicast>
+ <dropped-full-socket-buffer>0</dropped-full-socket-buffer>
+ <not-for-hashed-pcb>0</not-for-hashed-pcb>
+EOF
+ count=`jexec ${jname} netstat -s -p udp --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 9) ;;
+ *) jexec ${jname} netstat -s -p udp --libxo xml,pretty
+ atf_fail "Global UDP statistics do not match: ${count} != 9" ;;
+ esac
+
+ #
+ # Check selection of global IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-below-minimum-size>0</dropped-below-minimum-size>
+ <dropped-short-packets>0</dropped-short-packets>
+ <dropped-bad-options>0</dropped-bad-options>
+ <dropped-bad-version>0</dropped-bad-version>
+ <received-fragments>1</received-fragments>
+ <dropped-fragment>0</dropped-fragment>
+ <dropped-fragment-after-timeout>1</dropped-fragment-after-timeout>
+ <dropped-fragments-overflow>0</dropped-fragments-overflow>
+ <atomic-fragments>0</atomic-fragments>
+ <reassembled-packets>0</reassembled-packets>
+ <forwarded-packets>0</forwarded-packets>
+ <packets-not-forwardable>0</packets-not-forwardable>
+ <sent-redirects>0</sent-redirects>
+ <send-packets-fabricated-header>0</send-packets-fabricated-header>
+ <discard-no-mbufs>0</discard-no-mbufs>
+ <discard-no-route>0</discard-no-route>
+ <sent-fragments>0</sent-fragments>
+ <fragments-created>0</fragments-created>
+ <discard-cannot-fragment>0</discard-cannot-fragment>
+ <discard-scope-violations>0</discard-scope-violations>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 20) ;;
+ *) jexec ${jname} netstat -s -p ip6 --libxo xml,pretty
+ atf_fail "Global IPv6 statistics do not match: ${count} != 20" ;;
+ esac
+
+ #
+ # Check selection of global ICMPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <icmp6-calls>1</icmp6-calls>
+ <no-route>0</no-route>
+ <admin-prohibited>0</admin-prohibited>
+ <beyond-scope>0</beyond-scope>
+ <address-unreachable>0</address-unreachable>
+ <port-unreachable>0</port-unreachable>
+ <packet-too-big>0</packet-too-big>
+ <time-exceed-transmit>0</time-exceed-transmit>
+ <time-exceed-reassembly>1</time-exceed-reassembly>
+ <bad-header>0</bad-header>
+ <bad-next-header>0</bad-next-header>
+ <bad-option>0</bad-option>
+ <redirects>0</redirects>
+ <unknown>0</unknown>
+ <reflect>0</reflect>
+ <too-many-nd-options>0</too-many-nd-options>
+ <bad-nd-options>0</bad-nd-options>
+ <bad-neighbor-solicitation>0</bad-neighbor-solicitation>
+ <bad-neighbor-advertisement>0</bad-neighbor-advertisement>
+ <bad-router-solicitation>0</bad-router-solicitation>
+ <bad-router-advertisement>0</bad-router-advertisement>
+ <bad-redirect>0</bad-redirect>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 22) ;;
+ *) jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty
+ atf_fail "Global ICMPv6 statistics do not match: ${count} != 22" ;;
+ esac
+
+ #
+ # Check selection of interface IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-invalid-header>0</dropped-invalid-header>
+ <dropped-mtu-exceeded>0</dropped-mtu-exceeded>
+ <dropped-no-route>0</dropped-no-route>
+ <dropped-invalid-destination>0</dropped-invalid-destination>
+ <dropped-unknown-protocol>0</dropped-unknown-protocol>
+ <dropped-truncated>0</dropped-truncated>
+ <sent-forwarded>0</sent-forwarded>
+ <discard-packets>0</discard-packets>
+ <discard-fragments>0</discard-fragments>
+ <fragments-failed>0</fragments-failed>
+ <fragments-created>0</fragments-created>
+ <reassembly-required>1</reassembly-required>
+ <reassembled-packets>0</reassembled-packets>
+ <reassembly-failed>0</reassembly-failed>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 14) ;;
+ *) jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface IPv6 statistics do not match: ${count} != 14" ;;
+ esac
+
+ #
+ # Check selection of interface ICMPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-errors>0</received-errors>
+ <received-destination-unreachable>0</received-destination-unreachable>
+ <received-admin-prohibited>0</received-admin-prohibited>
+ <received-time-exceeded>0</received-time-exceeded>
+ <received-bad-parameter>0</received-bad-parameter>
+ <received-packet-too-big>0</received-packet-too-big>
+ <received-echo-requests>0</received-echo-requests>
+ <received-echo-replies>0</received-echo-replies>
+ <received-router-solicitation>0</received-router-solicitation>
+ <received-router-advertisement>0</received-router-advertisement>
+ <sent-errors>1</sent-errors>
+ <sent-destination-unreachable>0</sent-destination-unreachable>
+ <sent-admin-prohibited>0</sent-admin-prohibited>
+ <sent-time-exceeded>1</sent-time-exceeded>
+ <sent-bad-parameter>0</sent-bad-parameter>
+ <sent-packet-too-big>0</sent-packet-too-big>
+ <sent-echo-requests>0</sent-echo-requests>
+ <sent-echo-replies>0</sent-echo-replies>
+ <sent-router-solicitation>0</sent-router-solicitation>
+ <sent-router-advertisement>0</sent-router-advertisement>
+ <sent-redirects>0</sent-redirects>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 21) ;;
+ *) jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface ICMPv6 statistics do not match: ${count} != 21" ;;
+ esac
+}
+
+frag6_09_body() {
+ frag6_body 9 frag6_09_check_stats
+}
+
+frag6_09_cleanup() {
+ frag6_cleanup 9
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "frag6_09"
+}
diff --git a/tests/sys/netinet6/frag6/frag6_10.py b/tests/sys/netinet6/frag6/frag6_10.py
new file mode 100644
index 000000000000..afdfdb6415a6
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_10.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import socket
+import sys
+from time import sleep
+
+def main():
+ parser = argparse.ArgumentParser("frag6.py",
+ description="IPv6 fragementation test tool")
+ parser.add_argument('--sendif', nargs=1,
+ required=True,
+ help='The interface through which the packet will be sent')
+ parser.add_argument('--recvif', nargs=1,
+ required=True,
+ help='The interface on which to check for the packet')
+ parser.add_argument('--src', nargs=1,
+ required=True,
+ help='The source IP address')
+ parser.add_argument('--to', nargs=1,
+ required=True,
+ help='The destination IP address')
+ parser.add_argument('--debug',
+ required=False, action='store_true',
+ help='Enable test debugging')
+
+ args = parser.parse_args()
+
+
+ ########################################################################
+ #
+ # A single middle fragment.
+ #
+ # A: Waiting for more data.
+ # R: Timeout / Expiry.
+ #
+ ip6f01 = sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrFragment(offset=161, m=1, id=7) / \
+ sp.UDP(dport=3456, sport=6543)
+ if args.debug :
+ ip6f01.display()
+ sp.sendp(ip6f01, iface=args.sendif[0], verbose=False)
+
+ # We do not generate ICMPv6 for non-off=0-segments.
+ # Wait for expiry.
+ sleep(3)
+
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netinet6/frag6/frag6_10.sh b/tests/sys/netinet6/frag6/frag6_10.sh
new file mode 100755
index 000000000000..11a3a3a9e1c2
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_10.sh
@@ -0,0 +1,230 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/frag6.subr
+
+atf_test_case "frag6_10" "cleanup"
+frag6_10_head() {
+ frag6_head 10
+}
+
+frag6_10_check_stats() {
+
+ local jname ifname
+ jname=$1
+ ifname=$2
+
+ case "${jname}" in
+ "") echo "ERROR: jname is empty"; return ;;
+ esac
+ case "${ifname}" in
+ "") echo "ERROR: ifname is empty"; return ;;
+ esac
+
+ # Defaults are: IPV6_FRAGTTL 120 slowtimo ticks.
+ # pfslowtimo() is run at hz/2. So this takes 60s.
+ # This is awefully long for a test case.
+ # The Python script has to wait for this already to get the ICMPv6
+ # hence we do not sleep here anymore.
+
+ nf=`jexec ${jname} sysctl -n net.inet6.ip6.frag6_nfragpackets`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "VNET frag6_nfragpackets not 0 but: ${nf}" ;;
+ esac
+ nf=`sysctl -n net.inet6.ip6.frag6_nfrags`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "Global frag6_nfrags not 0 but: ${nf}" ;;
+ esac
+
+ #
+ # Check selection of global UDP stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-datagrams>0</received-datagrams>
+ <dropped-incomplete-headers>0</dropped-incomplete-headers>
+ <dropped-bad-data-length>0</dropped-bad-data-length>
+ <dropped-bad-checksum>0</dropped-bad-checksum>
+ <dropped-no-checksum>0</dropped-no-checksum>
+ <dropped-no-socket>0</dropped-no-socket>
+ <dropped-broadcast-multicast>0</dropped-broadcast-multicast>
+ <dropped-full-socket-buffer>0</dropped-full-socket-buffer>
+ <not-for-hashed-pcb>0</not-for-hashed-pcb>
+EOF
+ count=`jexec ${jname} netstat -s -p udp --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 9) ;;
+ *) jexec ${jname} netstat -s -p udp --libxo xml,pretty
+ atf_fail "Global UDP statistics do not match: ${count} != 9" ;;
+ esac
+
+ #
+ # Check selection of global IPv6 stats.
+ # We do not sent a timeout ICMPv6 for this one
+ # as it is not an off=0 segment.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-below-minimum-size>0</dropped-below-minimum-size>
+ <dropped-short-packets>0</dropped-short-packets>
+ <dropped-bad-options>0</dropped-bad-options>
+ <dropped-bad-version>0</dropped-bad-version>
+ <received-fragments>1</received-fragments>
+ <dropped-fragment>0</dropped-fragment>
+ <dropped-fragment-after-timeout>1</dropped-fragment-after-timeout>
+ <dropped-fragments-overflow>0</dropped-fragments-overflow>
+ <atomic-fragments>0</atomic-fragments>
+ <reassembled-packets>0</reassembled-packets>
+ <forwarded-packets>0</forwarded-packets>
+ <packets-not-forwardable>0</packets-not-forwardable>
+ <sent-redirects>0</sent-redirects>
+ <send-packets-fabricated-header>0</send-packets-fabricated-header>
+ <discard-no-mbufs>0</discard-no-mbufs>
+ <discard-no-route>0</discard-no-route>
+ <sent-fragments>0</sent-fragments>
+ <fragments-created>0</fragments-created>
+ <discard-cannot-fragment>0</discard-cannot-fragment>
+ <discard-scope-violations>0</discard-scope-violations>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 20) ;;
+ *) jexec ${jname} netstat -s -p ip6 --libxo xml,pretty
+ atf_fail "Global IPv6 statistics do not match: ${count} != 20" ;;
+ esac
+
+ #
+ # Check selection of global ICMPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <icmp6-calls>0</icmp6-calls>
+ <no-route>0</no-route>
+ <admin-prohibited>0</admin-prohibited>
+ <beyond-scope>0</beyond-scope>
+ <address-unreachable>0</address-unreachable>
+ <port-unreachable>0</port-unreachable>
+ <packet-too-big>0</packet-too-big>
+ <time-exceed-transmit>0</time-exceed-transmit>
+ <time-exceed-reassembly>0</time-exceed-reassembly>
+ <bad-header>0</bad-header>
+ <bad-next-header>0</bad-next-header>
+ <bad-option>0</bad-option>
+ <redirects>0</redirects>
+ <unknown>0</unknown>
+ <reflect>0</reflect>
+ <too-many-nd-options>0</too-many-nd-options>
+ <bad-nd-options>0</bad-nd-options>
+ <bad-neighbor-solicitation>0</bad-neighbor-solicitation>
+ <bad-neighbor-advertisement>0</bad-neighbor-advertisement>
+ <bad-router-solicitation>0</bad-router-solicitation>
+ <bad-router-advertisement>0</bad-router-advertisement>
+ <bad-redirect>0</bad-redirect>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 22) ;;
+ *) jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty
+ atf_fail "Global ICMPv6 statistics do not match: ${count} != 22" ;;
+ esac
+
+ #
+ # Check selection of interface IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-invalid-header>0</dropped-invalid-header>
+ <dropped-mtu-exceeded>0</dropped-mtu-exceeded>
+ <dropped-no-route>0</dropped-no-route>
+ <dropped-invalid-destination>0</dropped-invalid-destination>
+ <dropped-unknown-protocol>0</dropped-unknown-protocol>
+ <dropped-truncated>0</dropped-truncated>
+ <sent-forwarded>0</sent-forwarded>
+ <discard-packets>0</discard-packets>
+ <discard-fragments>0</discard-fragments>
+ <fragments-failed>0</fragments-failed>
+ <fragments-created>0</fragments-created>
+ <reassembly-required>1</reassembly-required>
+ <reassembled-packets>0</reassembled-packets>
+ <reassembly-failed>0</reassembly-failed>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 14) ;;
+ *) jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface IPv6 statistics do not match: ${count} != 14" ;;
+ esac
+
+ #
+ # Check selection of interface ICMPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-errors>0</received-errors>
+ <received-destination-unreachable>0</received-destination-unreachable>
+ <received-admin-prohibited>0</received-admin-prohibited>
+ <received-time-exceeded>0</received-time-exceeded>
+ <received-bad-parameter>0</received-bad-parameter>
+ <received-packet-too-big>0</received-packet-too-big>
+ <received-echo-requests>0</received-echo-requests>
+ <received-echo-replies>0</received-echo-replies>
+ <received-router-solicitation>0</received-router-solicitation>
+ <received-router-advertisement>0</received-router-advertisement>
+ <sent-errors>0</sent-errors>
+ <sent-destination-unreachable>0</sent-destination-unreachable>
+ <sent-admin-prohibited>0</sent-admin-prohibited>
+ <sent-time-exceeded>0</sent-time-exceeded>
+ <sent-bad-parameter>0</sent-bad-parameter>
+ <sent-packet-too-big>0</sent-packet-too-big>
+ <sent-echo-requests>0</sent-echo-requests>
+ <sent-echo-replies>0</sent-echo-replies>
+ <sent-router-solicitation>0</sent-router-solicitation>
+ <sent-router-advertisement>0</sent-router-advertisement>
+ <sent-redirects>0</sent-redirects>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 21) ;;
+ *) jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface ICMPv6 statistics do not match: ${count} != 21" ;;
+ esac
+}
+
+frag6_10_body() {
+ frag6_body 10 frag6_10_check_stats
+}
+
+frag6_10_cleanup() {
+ frag6_cleanup 10
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "frag6_10"
+}
diff --git a/tests/sys/netinet6/frag6/frag6_11.py b/tests/sys/netinet6/frag6/frag6_11.py
new file mode 100644
index 000000000000..ef90145d4bb8
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_11.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import socket
+import sys
+from time import sleep
+
+def main():
+ parser = argparse.ArgumentParser("frag6.py",
+ description="IPv6 fragementation test tool")
+ parser.add_argument('--sendif', nargs=1,
+ required=True,
+ help='The interface through which the packet will be sent')
+ parser.add_argument('--recvif', nargs=1,
+ required=True,
+ help='The interface on which to check for the packet')
+ parser.add_argument('--src', nargs=1,
+ required=True,
+ help='The source IP address')
+ parser.add_argument('--to', nargs=1,
+ required=True,
+ help='The destination IP address')
+ parser.add_argument('--debug',
+ required=False, action='store_true',
+ help='Enable test debugging')
+
+ args = parser.parse_args()
+
+
+ ########################################################################
+ #
+ # A single last fragment.
+ #
+ # A: Waiting for more data.
+ # R: Timeout / Expiry.
+ #
+ ip6f01 = sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrFragment(offset=321, m=0, id=8) / \
+ sp.UDP(dport=3456, sport=6543)
+ if args.debug :
+ ip6f01.display()
+ sp.sendp(ip6f01, iface=args.sendif[0], verbose=False)
+
+ # Wait for expiration to happen. We will not see an ICMPv6 as there
+ # is no frag with offset=0.
+ sleep(3)
+
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netinet6/frag6/frag6_11.sh b/tests/sys/netinet6/frag6/frag6_11.sh
new file mode 100755
index 000000000000..c75f8c55006b
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_11.sh
@@ -0,0 +1,229 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/frag6.subr
+
+atf_test_case "frag6_11" "cleanup"
+frag6_11_head() {
+ frag6_head 11
+}
+
+frag6_11_check_stats() {
+
+ local jname ifname
+ jname=$1
+ ifname=$2
+
+ case "${jname}" in
+ "") echo "ERROR: jname is empty"; return ;;
+ esac
+ case "${ifname}" in
+ "") echo "ERROR: ifname is empty"; return ;;
+ esac
+
+ # Defaults are: IPV6_FRAGTTL 120 slowtimo ticks.
+ # pfslowtimo() is run at hz/2. So this takes 60s.
+ # This is awefully long for a test case.
+ # The Python script has to wait for this already to get the ICMPv6
+ # hence we do not sleep here anymore.
+
+ nf=`jexec ${jname} sysctl -n net.inet6.ip6.frag6_nfragpackets`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "VNET frag6_nfragpackets not 0 but: ${nf}" ;;
+ esac
+ nf=`sysctl -n net.inet6.ip6.frag6_nfrags`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "Global frag6_nfrags not 0 but: ${nf}" ;;
+ esac
+
+ #
+ # Check selection of global UDP stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-datagrams>0</received-datagrams>
+ <dropped-incomplete-headers>0</dropped-incomplete-headers>
+ <dropped-bad-data-length>0</dropped-bad-data-length>
+ <dropped-bad-checksum>0</dropped-bad-checksum>
+ <dropped-no-checksum>0</dropped-no-checksum>
+ <dropped-no-socket>0</dropped-no-socket>
+ <dropped-broadcast-multicast>0</dropped-broadcast-multicast>
+ <dropped-full-socket-buffer>0</dropped-full-socket-buffer>
+ <not-for-hashed-pcb>0</not-for-hashed-pcb>
+EOF
+ count=`jexec ${jname} netstat -s -p udp --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 9) ;;
+ *) jexec ${jname} netstat -s -p udp --libxo xml,pretty
+ atf_fail "Global UDP statistics do not match: ${count} != 9" ;;
+ esac
+
+ #
+ # Check selection of global IPv6 stats.
+ # XXX-TODO We do not seem to sent a timeout ICMPv6 for this one?
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-below-minimum-size>0</dropped-below-minimum-size>
+ <dropped-short-packets>0</dropped-short-packets>
+ <dropped-bad-options>0</dropped-bad-options>
+ <dropped-bad-version>0</dropped-bad-version>
+ <received-fragments>1</received-fragments>
+ <dropped-fragment>0</dropped-fragment>
+ <dropped-fragment-after-timeout>1</dropped-fragment-after-timeout>
+ <dropped-fragments-overflow>0</dropped-fragments-overflow>
+ <atomic-fragments>0</atomic-fragments>
+ <reassembled-packets>0</reassembled-packets>
+ <forwarded-packets>0</forwarded-packets>
+ <packets-not-forwardable>0</packets-not-forwardable>
+ <sent-redirects>0</sent-redirects>
+ <send-packets-fabricated-header>0</send-packets-fabricated-header>
+ <discard-no-mbufs>0</discard-no-mbufs>
+ <discard-no-route>0</discard-no-route>
+ <sent-fragments>0</sent-fragments>
+ <fragments-created>0</fragments-created>
+ <discard-cannot-fragment>0</discard-cannot-fragment>
+ <discard-scope-violations>0</discard-scope-violations>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 20) ;;
+ *) jexec ${jname} netstat -s -p ip6 --libxo xml,pretty
+ atf_fail "Global IPv6 statistics do not match: ${count} != 20" ;;
+ esac
+
+ #
+ # Check selection of global ICMPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <icmp6-calls>0</icmp6-calls>
+ <no-route>0</no-route>
+ <admin-prohibited>0</admin-prohibited>
+ <beyond-scope>0</beyond-scope>
+ <address-unreachable>0</address-unreachable>
+ <port-unreachable>0</port-unreachable>
+ <packet-too-big>0</packet-too-big>
+ <time-exceed-transmit>0</time-exceed-transmit>
+ <time-exceed-reassembly>0</time-exceed-reassembly>
+ <bad-header>0</bad-header>
+ <bad-next-header>0</bad-next-header>
+ <bad-option>0</bad-option>
+ <redirects>0</redirects>
+ <unknown>0</unknown>
+ <reflect>0</reflect>
+ <too-many-nd-options>0</too-many-nd-options>
+ <bad-nd-options>0</bad-nd-options>
+ <bad-neighbor-solicitation>0</bad-neighbor-solicitation>
+ <bad-neighbor-advertisement>0</bad-neighbor-advertisement>
+ <bad-router-solicitation>0</bad-router-solicitation>
+ <bad-router-advertisement>0</bad-router-advertisement>
+ <bad-redirect>0</bad-redirect>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 22) ;;
+ *) jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty
+ atf_fail "Global ICMPv6 statistics do not match: ${count} != 22" ;;
+ esac
+
+ #
+ # Check selection of interface IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-invalid-header>0</dropped-invalid-header>
+ <dropped-mtu-exceeded>0</dropped-mtu-exceeded>
+ <dropped-no-route>0</dropped-no-route>
+ <dropped-invalid-destination>0</dropped-invalid-destination>
+ <dropped-unknown-protocol>0</dropped-unknown-protocol>
+ <dropped-truncated>0</dropped-truncated>
+ <sent-forwarded>0</sent-forwarded>
+ <discard-packets>0</discard-packets>
+ <discard-fragments>0</discard-fragments>
+ <fragments-failed>0</fragments-failed>
+ <fragments-created>0</fragments-created>
+ <reassembly-required>1</reassembly-required>
+ <reassembled-packets>0</reassembled-packets>
+ <reassembly-failed>0</reassembly-failed>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 14) ;;
+ *) jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface IPv6 statistics do not match: ${count} != 14" ;;
+ esac
+
+ #
+ # Check selection of interface ICMPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-errors>0</received-errors>
+ <received-destination-unreachable>0</received-destination-unreachable>
+ <received-admin-prohibited>0</received-admin-prohibited>
+ <received-time-exceeded>0</received-time-exceeded>
+ <received-bad-parameter>0</received-bad-parameter>
+ <received-packet-too-big>0</received-packet-too-big>
+ <received-echo-requests>0</received-echo-requests>
+ <received-echo-replies>0</received-echo-replies>
+ <received-router-solicitation>0</received-router-solicitation>
+ <received-router-advertisement>0</received-router-advertisement>
+ <sent-errors>0</sent-errors>
+ <sent-destination-unreachable>0</sent-destination-unreachable>
+ <sent-admin-prohibited>0</sent-admin-prohibited>
+ <sent-time-exceeded>0</sent-time-exceeded>
+ <sent-bad-parameter>0</sent-bad-parameter>
+ <sent-packet-too-big>0</sent-packet-too-big>
+ <sent-echo-requests>0</sent-echo-requests>
+ <sent-echo-replies>0</sent-echo-replies>
+ <sent-router-solicitation>0</sent-router-solicitation>
+ <sent-router-advertisement>0</sent-router-advertisement>
+ <sent-redirects>0</sent-redirects>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 21) ;;
+ *) jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface ICMPv6 statistics do not match: ${count} != 21" ;;
+ esac
+}
+
+frag6_11_body() {
+ frag6_body 11 frag6_11_check_stats
+}
+
+frag6_11_cleanup() {
+ frag6_cleanup 11
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "frag6_11"
+}
diff --git a/tests/sys/netinet6/frag6/frag6_12.py b/tests/sys/netinet6/frag6/frag6_12.py
new file mode 100644
index 000000000000..c8c974bad287
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_12.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import socket
+import sys
+from sniffer import Sniffer
+from time import sleep
+
+def check_icmp6_error(args, packet):
+ ip6 = packet.getlayer(sp.IPv6)
+ if not ip6:
+ return False
+ oip6 = sp.IPv6(src=args.src[0], dst=args.to[0])
+ if ip6.dst != oip6.src:
+ return False
+ icmp6 = packet.getlayer(sp.ICMPv6TimeExceeded)
+ if not icmp6:
+ return False
+ # ICMP6_TIME_EXCEED_REASSEMBLY 1
+ if icmp6.code != 1:
+ return False
+ # Should we check the payload as well?
+ # We are running in a very isolated environment and nothing else
+ # should trigger an ICMPv6 Time Exceeded / Frag reassembly so leave it.
+ #icmp6.display()
+ return True
+
+
+def main():
+ parser = argparse.ArgumentParser("frag6.py",
+ description="IPv6 fragementation test tool")
+ parser.add_argument('--sendif', nargs=1,
+ required=True,
+ help='The interface through which the packet will be sent')
+ parser.add_argument('--recvif', nargs=1,
+ required=True,
+ help='The interface on which to check for the packet')
+ parser.add_argument('--src', nargs=1,
+ required=True,
+ help='The source IP address')
+ parser.add_argument('--to', nargs=1,
+ required=True,
+ help='The destination IP address')
+ parser.add_argument('--debug',
+ required=False, action='store_true',
+ help='Enable test debugging')
+
+ args = parser.parse_args()
+
+
+ # Start sniffing on recvif
+ sniffer = Sniffer(args, check_icmp6_error)
+
+
+ ########################################################################
+ #
+ # A single start fragment with payload.
+ #
+ # A: Waiting for more data.
+ # R: Timeout / Expiry.
+ #
+ data = "6" * 1280
+ ip6f01 = sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrFragment(offset=0, m=1, id=3) / \
+ sp.UDP(dport=3456, sport=6543) / \
+ data
+ if args.debug :
+ ip6f01.display()
+ sp.sendp(ip6f01, iface=args.sendif[0], verbose=False)
+
+ # Wait for ICMPv6 error generation on timeout.
+ sleep(3)
+ sniffer.setEnd()
+ sniffer.join()
+ if not sniffer.foundCorrectPacket:
+ sys.exit(1)
+
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netinet6/frag6/frag6_12.sh b/tests/sys/netinet6/frag6/frag6_12.sh
new file mode 100755
index 000000000000..2d57e413d568
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_12.sh
@@ -0,0 +1,227 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/frag6.subr
+
+atf_test_case "frag6_12" "cleanup"
+frag6_12_head() {
+ frag6_head 12
+}
+
+frag6_12_check_stats() {
+
+ local jname ifname
+ jname=$1
+ ifname=$2
+
+ case "${jname}" in
+ "") echo "ERROR: jname is empty"; return ;;
+ esac
+ case "${ifname}" in
+ "") echo "ERROR: ifname is empty"; return ;;
+ esac
+
+ # Defaults are: IPV6_FRAGTTL 120 slowtimo ticks.
+ # pfslowtimo() is run at hz/2. So this takes 60s.
+ # This is awefully long for a test case.
+ # The Python script has to wait for this already to get the ICMPv6
+ # hence we do not sleep here anymore.
+
+ nf=`jexec ${jname} sysctl -n net.inet6.ip6.frag6_nfragpackets`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "VNET frag6_nfragpackets not 0 but: ${nf}" ;;
+ esac
+ nf=`sysctl -n net.inet6.ip6.frag6_nfrags`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "Global frag6_nfrags not 0 but: ${nf}" ;;
+ esac
+
+ #
+ # Check selection of global UDP stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-datagrams>0</received-datagrams>
+ <dropped-incomplete-headers>0</dropped-incomplete-headers>
+ <dropped-bad-data-length>0</dropped-bad-data-length>
+ <dropped-bad-checksum>0</dropped-bad-checksum>
+ <dropped-no-checksum>0</dropped-no-checksum>
+ <dropped-no-socket>0</dropped-no-socket>
+ <dropped-broadcast-multicast>0</dropped-broadcast-multicast>
+ <dropped-full-socket-buffer>0</dropped-full-socket-buffer>
+ <not-for-hashed-pcb>0</not-for-hashed-pcb>
+EOF
+ count=`jexec ${jname} netstat -s -p udp --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 9) ;;
+ *) jexec ${jname} netstat -s -p udp --libxo xml,pretty
+ atf_fail "Global UDP statistics do not match: ${count} != 9" ;;
+ esac
+
+ #
+ # Check selection of global IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-below-minimum-size>0</dropped-below-minimum-size>
+ <dropped-short-packets>0</dropped-short-packets>
+ <dropped-bad-options>0</dropped-bad-options>
+ <dropped-bad-version>0</dropped-bad-version>
+ <received-fragments>1</received-fragments>
+ <dropped-fragment>0</dropped-fragment>
+ <dropped-fragment-after-timeout>1</dropped-fragment-after-timeout>
+ <dropped-fragments-overflow>0</dropped-fragments-overflow>
+ <atomic-fragments>0</atomic-fragments>
+ <reassembled-packets>0</reassembled-packets>
+ <forwarded-packets>0</forwarded-packets>
+ <packets-not-forwardable>0</packets-not-forwardable>
+ <sent-redirects>0</sent-redirects>
+ <send-packets-fabricated-header>0</send-packets-fabricated-header>
+ <discard-no-mbufs>0</discard-no-mbufs>
+ <discard-no-route>0</discard-no-route>
+ <sent-fragments>0</sent-fragments>
+ <fragments-created>0</fragments-created>
+ <discard-cannot-fragment>0</discard-cannot-fragment>
+ <discard-scope-violations>0</discard-scope-violations>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 20) ;;
+ *) jexec ${jname} netstat -s -p ip6 --libxo xml,pretty
+ atf_fail "Global IPv6 statistics do not match: ${count} != 20" ;;
+ esac
+
+ #
+ # Check selection of global ICMPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <icmp6-calls>1</icmp6-calls>
+ <no-route>0</no-route>
+ <admin-prohibited>0</admin-prohibited>
+ <beyond-scope>0</beyond-scope>
+ <address-unreachable>0</address-unreachable>
+ <port-unreachable>0</port-unreachable>
+ <packet-too-big>0</packet-too-big>
+ <time-exceed-transmit>0</time-exceed-transmit>
+ <time-exceed-reassembly>1</time-exceed-reassembly>
+ <bad-header>0</bad-header>
+ <bad-next-header>0</bad-next-header>
+ <bad-option>0</bad-option>
+ <redirects>0</redirects>
+ <unknown>0</unknown>
+ <reflect>0</reflect>
+ <too-many-nd-options>0</too-many-nd-options>
+ <bad-nd-options>0</bad-nd-options>
+ <bad-neighbor-solicitation>0</bad-neighbor-solicitation>
+ <bad-neighbor-advertisement>0</bad-neighbor-advertisement>
+ <bad-router-solicitation>0</bad-router-solicitation>
+ <bad-router-advertisement>0</bad-router-advertisement>
+ <bad-redirect>0</bad-redirect>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 22) ;;
+ *) jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty
+ atf_fail "Global ICMPv6 statistics do not match: ${count} != 22" ;;
+ esac
+
+ #
+ # Check selection of interface IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-invalid-header>0</dropped-invalid-header>
+ <dropped-mtu-exceeded>0</dropped-mtu-exceeded>
+ <dropped-no-route>0</dropped-no-route>
+ <dropped-invalid-destination>0</dropped-invalid-destination>
+ <dropped-unknown-protocol>0</dropped-unknown-protocol>
+ <dropped-truncated>0</dropped-truncated>
+ <sent-forwarded>0</sent-forwarded>
+ <discard-packets>0</discard-packets>
+ <discard-fragments>0</discard-fragments>
+ <fragments-failed>0</fragments-failed>
+ <fragments-created>0</fragments-created>
+ <reassembly-required>1</reassembly-required>
+ <reassembled-packets>0</reassembled-packets>
+ <reassembly-failed>0</reassembly-failed>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 14) ;;
+ *) jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface IPv6 statistics do not match: ${count} != 14" ;;
+ esac
+
+ #
+ # Check selection of interface ICMPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-errors>0</received-errors>
+ <received-destination-unreachable>0</received-destination-unreachable>
+ <received-admin-prohibited>0</received-admin-prohibited>
+ <received-time-exceeded>0</received-time-exceeded>
+ <received-bad-parameter>0</received-bad-parameter>
+ <received-packet-too-big>0</received-packet-too-big>
+ <received-echo-requests>0</received-echo-requests>
+ <received-echo-replies>0</received-echo-replies>
+ <received-router-solicitation>0</received-router-solicitation>
+ <received-router-advertisement>0</received-router-advertisement>
+ <sent-errors>1</sent-errors>
+ <sent-destination-unreachable>0</sent-destination-unreachable>
+ <sent-admin-prohibited>0</sent-admin-prohibited>
+ <sent-time-exceeded>1</sent-time-exceeded>
+ <sent-bad-parameter>0</sent-bad-parameter>
+ <sent-packet-too-big>0</sent-packet-too-big>
+ <sent-echo-requests>0</sent-echo-requests>
+ <sent-echo-replies>0</sent-echo-replies>
+ <sent-router-solicitation>0</sent-router-solicitation>
+ <sent-router-advertisement>0</sent-router-advertisement>
+ <sent-redirects>0</sent-redirects>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 21) ;;
+ *) jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface ICMPv6 statistics do not match: ${count} != 21" ;;
+ esac
+}
+frag6_12_body() {
+ frag6_body 12 frag6_12_check_stats
+}
+
+frag6_12_cleanup() {
+ frag6_cleanup 12
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "frag6_12"
+}
diff --git a/tests/sys/netinet6/frag6/frag6_13.py b/tests/sys/netinet6/frag6/frag6_13.py
new file mode 100644
index 000000000000..dd93ff4002fe
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_13.py
@@ -0,0 +1,123 @@
+#!/usr/bin/env python
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import socket
+import sys
+from time import sleep
+
+def main():
+ parser = argparse.ArgumentParser("frag6.py",
+ description="IPv6 fragementation test tool")
+ parser.add_argument('--sendif', nargs=1,
+ required=True,
+ help='The interface through which the packet will be sent')
+ parser.add_argument('--recvif', nargs=1,
+ required=True,
+ help='The interface on which to check for the packet')
+ parser.add_argument('--src', nargs=1,
+ required=True,
+ help='The source IP address')
+ parser.add_argument('--to', nargs=1,
+ required=True,
+ help='The destination IP address')
+ parser.add_argument('--debug',
+ required=False, action='store_true',
+ help='Enable test debugging')
+
+ args = parser.parse_args()
+
+
+ ########################################################################
+ #
+ # Two fragments with different ECN (Traffic Clas) bits to trigger
+ # error cases.
+ #
+ # A: Reassembly failure.
+ # R: ip6f02 dropped / Timeout (not waiting for).
+ #
+ data = "6" * 8
+ # IPTOS_ECN_NOTECT
+ ip6f01 = \
+ sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0], tc=0x00) / \
+ sp.IPv6ExtHdrFragment(offset=0, m=1, id=13) / \
+ sp.UDP(dport=3456, sport=6543) / \
+ data
+ # IPTOS_ECN_CE
+ ip6f02 = \
+ sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0], tc=0x03) / \
+ sp.IPv6ExtHdrFragment(offset=16, m=0, id=13) / \
+ sp.UDP(dport=3456, sport=6543) / \
+ data
+ if args.debug :
+ ip6f01.display()
+ ip6f02.display()
+ sp.sendp(ip6f01, iface=args.sendif[0], verbose=False)
+ sp.sendp(ip6f02, iface=args.sendif[0], verbose=False)
+
+
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ##
+ #
+ # Two fragments with different ECN (Traffic Clas) bits to trigger
+ # error cases.
+ #
+ # A: Reassembly failure.
+ # R: ip6f02 dropped / Timeout (not waiting for).
+ #
+ # IPTOS_ECN_ECT1
+ ip6f01 = \
+ sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0], tc=0x01) / \
+ sp.IPv6ExtHdrFragment(offset=0, m=1, id=0x1301) / \
+ sp.UDP(dport=3456, sport=6543) / \
+ data
+ # IPTOS_ECN_NOTECT
+ ip6f02 = \
+ sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0], tc=0x00) / \
+ sp.IPv6ExtHdrFragment(offset=16, m=0, id=0x1301) / \
+ sp.UDP(dport=3456, sport=6543) / \
+ data
+ if args.debug :
+ ip6f01.display()
+ ip6f02.display()
+ sp.sendp(ip6f01, iface=args.sendif[0], verbose=False)
+ sp.sendp(ip6f02, iface=args.sendif[0], verbose=False)
+
+ # Wait for expiry.
+ sleep(3)
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netinet6/frag6/frag6_13.sh b/tests/sys/netinet6/frag6/frag6_13.sh
new file mode 100755
index 000000000000..586310f22a4f
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_13.sh
@@ -0,0 +1,227 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/frag6.subr
+
+atf_test_case "frag6_13" "cleanup"
+frag6_13_head() {
+ frag6_head 13
+}
+
+frag6_13_check_stats() {
+
+ local jname ifname
+ jname=$1
+ ifname=$2
+
+ case "${jname}" in
+ "") echo "ERROR: jname is empty"; return ;;
+ esac
+ case "${ifname}" in
+ "") echo "ERROR: ifname is empty"; return ;;
+ esac
+
+ # Defaults are: IPV6_FRAGTTL 120 slowtimo ticks.
+ # pfslowtimo() is run at hz/2. So this takes 60s.
+ # This is awefully long for a test case.
+ # The Python script has to wait for this already to get the ICMPv6
+ # hence we do not sleep here anymore.
+
+ nf=`jexec ${jname} sysctl -n net.inet6.ip6.frag6_nfragpackets`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "VNET frag6_nfragpackets not 0 but: ${nf}" ;;
+ esac
+ nf=`sysctl -n net.inet6.ip6.frag6_nfrags`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "Global frag6_nfrags not 0 but: ${nf}" ;;
+ esac
+
+ #
+ # Check selection of global UDP stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-datagrams>0</received-datagrams>
+ <dropped-incomplete-headers>0</dropped-incomplete-headers>
+ <dropped-bad-data-length>0</dropped-bad-data-length>
+ <dropped-bad-checksum>0</dropped-bad-checksum>
+ <dropped-no-checksum>0</dropped-no-checksum>
+ <dropped-no-socket>0</dropped-no-socket>
+ <dropped-broadcast-multicast>0</dropped-broadcast-multicast>
+ <dropped-full-socket-buffer>0</dropped-full-socket-buffer>
+ <not-for-hashed-pcb>0</not-for-hashed-pcb>
+EOF
+ count=`jexec ${jname} netstat -s -p udp --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 9) ;;
+ *) jexec ${jname} netstat -s -p udp --libxo xml,pretty
+ atf_fail "Global UDP statistics do not match: ${count} != 9" ;;
+ esac
+
+ #
+ # Check selection of global IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-below-minimum-size>0</dropped-below-minimum-size>
+ <dropped-short-packets>0</dropped-short-packets>
+ <dropped-bad-options>0</dropped-bad-options>
+ <dropped-bad-version>0</dropped-bad-version>
+ <received-fragments>4</received-fragments>
+ <dropped-fragment>2</dropped-fragment>
+ <dropped-fragment-after-timeout>2</dropped-fragment-after-timeout>
+ <dropped-fragments-overflow>0</dropped-fragments-overflow>
+ <atomic-fragments>0</atomic-fragments>
+ <reassembled-packets>0</reassembled-packets>
+ <forwarded-packets>0</forwarded-packets>
+ <packets-not-forwardable>0</packets-not-forwardable>
+ <sent-redirects>0</sent-redirects>
+ <send-packets-fabricated-header>0</send-packets-fabricated-header>
+ <discard-no-mbufs>0</discard-no-mbufs>
+ <discard-no-route>0</discard-no-route>
+ <sent-fragments>0</sent-fragments>
+ <fragments-created>0</fragments-created>
+ <discard-cannot-fragment>0</discard-cannot-fragment>
+ <discard-scope-violations>0</discard-scope-violations>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 20) ;;
+ *) jexec ${jname} netstat -s -p ip6 --libxo xml,pretty
+ atf_fail "Global IPv6 statistics do not match: ${count} != 20" ;;
+ esac
+
+ #
+ # Check selection of global ICMPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <icmp6-calls>2</icmp6-calls>
+ <no-route>0</no-route>
+ <admin-prohibited>0</admin-prohibited>
+ <beyond-scope>0</beyond-scope>
+ <address-unreachable>0</address-unreachable>
+ <port-unreachable>0</port-unreachable>
+ <packet-too-big>0</packet-too-big>
+ <time-exceed-transmit>0</time-exceed-transmit>
+ <time-exceed-reassembly>2</time-exceed-reassembly>
+ <bad-header>0</bad-header>
+ <bad-next-header>0</bad-next-header>
+ <bad-option>0</bad-option>
+ <redirects>0</redirects>
+ <unknown>0</unknown>
+ <reflect>0</reflect>
+ <too-many-nd-options>0</too-many-nd-options>
+ <bad-nd-options>0</bad-nd-options>
+ <bad-neighbor-solicitation>0</bad-neighbor-solicitation>
+ <bad-neighbor-advertisement>0</bad-neighbor-advertisement>
+ <bad-router-solicitation>0</bad-router-solicitation>
+ <bad-router-advertisement>0</bad-router-advertisement>
+ <bad-redirect>0</bad-redirect>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 22) ;;
+ *) jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty
+ atf_fail "Global ICMPv6 statistics do not match: ${count} != 22" ;;
+ esac
+
+ #
+ # Check selection of interface IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-invalid-header>0</dropped-invalid-header>
+ <dropped-mtu-exceeded>0</dropped-mtu-exceeded>
+ <dropped-no-route>0</dropped-no-route>
+ <dropped-invalid-destination>0</dropped-invalid-destination>
+ <dropped-unknown-protocol>0</dropped-unknown-protocol>
+ <dropped-truncated>0</dropped-truncated>
+ <sent-forwarded>0</sent-forwarded>
+ <discard-packets>0</discard-packets>
+ <discard-fragments>0</discard-fragments>
+ <fragments-failed>0</fragments-failed>
+ <fragments-created>0</fragments-created>
+ <reassembly-required>4</reassembly-required>
+ <reassembled-packets>0</reassembled-packets>
+ <reassembly-failed>2</reassembly-failed>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 14) ;;
+ *) jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface IPv6 statistics do not match: ${count} != 14" ;;
+ esac
+
+ #
+ # Check selection of interface ICMPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-errors>0</received-errors>
+ <received-destination-unreachable>0</received-destination-unreachable>
+ <received-admin-prohibited>0</received-admin-prohibited>
+ <received-time-exceeded>0</received-time-exceeded>
+ <received-bad-parameter>0</received-bad-parameter>
+ <received-packet-too-big>0</received-packet-too-big>
+ <received-echo-requests>0</received-echo-requests>
+ <received-echo-replies>0</received-echo-replies>
+ <received-router-solicitation>0</received-router-solicitation>
+ <received-router-advertisement>0</received-router-advertisement>
+ <sent-errors>2</sent-errors>
+ <sent-destination-unreachable>0</sent-destination-unreachable>
+ <sent-admin-prohibited>0</sent-admin-prohibited>
+ <sent-time-exceeded>2</sent-time-exceeded>
+ <sent-bad-parameter>0</sent-bad-parameter>
+ <sent-packet-too-big>0</sent-packet-too-big>
+ <sent-echo-requests>0</sent-echo-requests>
+ <sent-echo-replies>0</sent-echo-replies>
+ <sent-router-solicitation>0</sent-router-solicitation>
+ <sent-router-advertisement>0</sent-router-advertisement>
+ <sent-redirects>0</sent-redirects>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 21) ;;
+ *) jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface ICMPv6 statistics do not match: ${count} != 21" ;;
+ esac
+}
+frag6_13_body() {
+ frag6_body 13 frag6_13_check_stats
+}
+
+frag6_13_cleanup() {
+ frag6_cleanup 13
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "frag6_13"
+}
diff --git a/tests/sys/netinet6/frag6/frag6_14.py b/tests/sys/netinet6/frag6/frag6_14.py
new file mode 100644
index 000000000000..4ffb0ea976ab
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_14.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import socket
+import sys
+from time import sleep
+
+def main():
+ parser = argparse.ArgumentParser("frag6.py",
+ description="IPv6 fragementation test tool")
+ parser.add_argument('--sendif', nargs=1,
+ required=True,
+ help='The interface through which the packet will be sent')
+ parser.add_argument('--recvif', nargs=1,
+ required=True,
+ help='The interface on which to check for the packet')
+ parser.add_argument('--src', nargs=1,
+ required=True,
+ help='The source IP address')
+ parser.add_argument('--to', nargs=1,
+ required=True,
+ help='The destination IP address')
+ parser.add_argument('--debug',
+ required=False, action='store_true',
+ help='Enable test debugging')
+
+ args = parser.parse_args()
+
+
+ ########################################################################
+ #
+ # Send multiple fragments, with an exact overlap in a middle one,
+ # not finishing the full packet (and ignoring the content anyway).
+ #
+ # A: Reassembly failure.
+ # R: dup dropped silently / Timeout (not waiting for).
+ #
+ data = "6" * 8
+ ip6f01 = \
+ sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrFragment(offset=0, m=1, id=14) / \
+ sp.UDP(dport=3456, sport=6543) / \
+ data
+ ip6f02 = \
+ sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrFragment(offset=0x1000, m=0, id=14) / \
+ sp.UDP(dport=3456, sport=6543) / \
+ data
+ ip6f03 = \
+ sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrFragment(offset=16, m=1, id=14) / \
+ sp.UDP(dport=3456, sport=6543) / \
+ data
+ ip6f04 = \
+ sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrFragment(offset=16, m=1, id=14) / \
+ sp.UDP(dport=3456, sport=6543) / \
+ data
+ if args.debug :
+ ip6f01.display()
+ ip6f02.display()
+ ip6f03.display()
+ ip6f04.display()
+ sp.sendp(ip6f01, iface=args.sendif[0], verbose=False)
+ sp.sendp(ip6f02, iface=args.sendif[0], verbose=False)
+ sp.sendp(ip6f03, iface=args.sendif[0], verbose=False)
+ sp.sendp(ip6f04, iface=args.sendif[0], verbose=False)
+
+
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ##
+ #
+ # Send multiple fragments, with a partial overlap on the first one
+ # not finishing the full packet (and ignoring the content anyway).
+ # The second packet needs to be the first one in the fragment chain
+ # in order to trigger the 2nd case to test.
+ #
+ # A: Reassembly failure.
+ # R: dup dropped silently / Timeout (not waiting for).
+ #
+ data = "6" * 8
+ ip6f01 = \
+ sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrFragment(offset=10, m=1, id=0x1401) / \
+ sp.UDP(dport=3456, sport=6543) / \
+ data
+ ip6f02 = \
+ sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrFragment(offset=9, m=0, id=0x1401) / \
+ sp.UDP(dport=3456, sport=6543) / \
+ data
+ if args.debug :
+ ip6f01.display()
+ ip6f02.display()
+ sp.sendp(ip6f01, iface=args.sendif[0], verbose=False)
+ sp.sendp(ip6f02, iface=args.sendif[0], verbose=False)
+
+ # Wait for expiry.
+ sleep(3)
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netinet6/frag6/frag6_14.sh b/tests/sys/netinet6/frag6/frag6_14.sh
new file mode 100755
index 000000000000..ff6d0081187d
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_14.sh
@@ -0,0 +1,227 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/frag6.subr
+
+atf_test_case "frag6_14" "cleanup"
+frag6_14_head() {
+ frag6_head 14
+}
+
+frag6_14_check_stats() {
+
+ local jname ifname
+ jname=$1
+ ifname=$2
+
+ case "${jname}" in
+ "") echo "ERROR: jname is empty"; return ;;
+ esac
+ case "${ifname}" in
+ "") echo "ERROR: ifname is empty"; return ;;
+ esac
+
+ # Defaults are: IPV6_FRAGTTL 120 slowtimo ticks.
+ # pfslowtimo() is run at hz/2. So this takes 60s.
+ # This is awefully long for a test case.
+ # The Python script has to wait for this already to get the ICMPv6
+ # hence we do not sleep here anymore.
+
+ nf=`jexec ${jname} sysctl -n net.inet6.ip6.frag6_nfragpackets`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "VNET frag6_nfragpackets not 0 but: ${nf}" ;;
+ esac
+ nf=`sysctl -n net.inet6.ip6.frag6_nfrags`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "Global frag6_nfrags not 0 but: ${nf}" ;;
+ esac
+
+ #
+ # Check selection of global UDP stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-datagrams>0</received-datagrams>
+ <dropped-incomplete-headers>0</dropped-incomplete-headers>
+ <dropped-bad-data-length>0</dropped-bad-data-length>
+ <dropped-bad-checksum>0</dropped-bad-checksum>
+ <dropped-no-checksum>0</dropped-no-checksum>
+ <dropped-no-socket>0</dropped-no-socket>
+ <dropped-broadcast-multicast>0</dropped-broadcast-multicast>
+ <dropped-full-socket-buffer>0</dropped-full-socket-buffer>
+ <not-for-hashed-pcb>0</not-for-hashed-pcb>
+EOF
+ count=`jexec ${jname} netstat -s -p udp --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 9) ;;
+ *) jexec ${jname} netstat -s -p udp --libxo xml,pretty
+ atf_fail "Global UDP statistics do not match: ${count} != 9" ;;
+ esac
+
+ #
+ # Check selection of global IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-below-minimum-size>0</dropped-below-minimum-size>
+ <dropped-short-packets>0</dropped-short-packets>
+ <dropped-bad-options>0</dropped-bad-options>
+ <dropped-bad-version>0</dropped-bad-version>
+ <received-fragments>6</received-fragments>
+ <dropped-fragment>2</dropped-fragment>
+ <dropped-fragment-after-timeout>3</dropped-fragment-after-timeout>
+ <dropped-fragments-overflow>0</dropped-fragments-overflow>
+ <atomic-fragments>0</atomic-fragments>
+ <reassembled-packets>0</reassembled-packets>
+ <forwarded-packets>0</forwarded-packets>
+ <packets-not-forwardable>0</packets-not-forwardable>
+ <sent-redirects>0</sent-redirects>
+ <send-packets-fabricated-header>0</send-packets-fabricated-header>
+ <discard-no-mbufs>0</discard-no-mbufs>
+ <discard-no-route>0</discard-no-route>
+ <sent-fragments>0</sent-fragments>
+ <fragments-created>0</fragments-created>
+ <discard-cannot-fragment>0</discard-cannot-fragment>
+ <discard-scope-violations>0</discard-scope-violations>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 20) ;;
+ *) jexec ${jname} netstat -s -p ip6 --libxo xml,pretty
+ atf_fail "Global IPv6 statistics do not match: ${count} != 20" ;;
+ esac
+
+ #
+ # Check selection of global ICMPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <icmp6-calls>1</icmp6-calls>
+ <no-route>0</no-route>
+ <admin-prohibited>0</admin-prohibited>
+ <beyond-scope>0</beyond-scope>
+ <address-unreachable>0</address-unreachable>
+ <port-unreachable>0</port-unreachable>
+ <packet-too-big>0</packet-too-big>
+ <time-exceed-transmit>0</time-exceed-transmit>
+ <time-exceed-reassembly>1</time-exceed-reassembly>
+ <bad-header>0</bad-header>
+ <bad-next-header>0</bad-next-header>
+ <bad-option>0</bad-option>
+ <redirects>0</redirects>
+ <unknown>0</unknown>
+ <reflect>0</reflect>
+ <too-many-nd-options>0</too-many-nd-options>
+ <bad-nd-options>0</bad-nd-options>
+ <bad-neighbor-solicitation>0</bad-neighbor-solicitation>
+ <bad-neighbor-advertisement>0</bad-neighbor-advertisement>
+ <bad-router-solicitation>0</bad-router-solicitation>
+ <bad-router-advertisement>0</bad-router-advertisement>
+ <bad-redirect>0</bad-redirect>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 22) ;;
+ *) jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty
+ atf_fail "Global ICMPv6 statistics do not match: ${count} != 22" ;;
+ esac
+
+ #
+ # Check selection of interface IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-invalid-header>0</dropped-invalid-header>
+ <dropped-mtu-exceeded>0</dropped-mtu-exceeded>
+ <dropped-no-route>0</dropped-no-route>
+ <dropped-invalid-destination>0</dropped-invalid-destination>
+ <dropped-unknown-protocol>0</dropped-unknown-protocol>
+ <dropped-truncated>0</dropped-truncated>
+ <sent-forwarded>0</sent-forwarded>
+ <discard-packets>0</discard-packets>
+ <discard-fragments>0</discard-fragments>
+ <fragments-failed>0</fragments-failed>
+ <fragments-created>0</fragments-created>
+ <reassembly-required>6</reassembly-required>
+ <reassembled-packets>0</reassembled-packets>
+ <reassembly-failed>2</reassembly-failed>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 14) ;;
+ *) jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface IPv6 statistics do not match: ${count} != 14" ;;
+ esac
+
+ #
+ # Check selection of interface ICMPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-errors>0</received-errors>
+ <received-destination-unreachable>0</received-destination-unreachable>
+ <received-admin-prohibited>0</received-admin-prohibited>
+ <received-time-exceeded>0</received-time-exceeded>
+ <received-bad-parameter>0</received-bad-parameter>
+ <received-packet-too-big>0</received-packet-too-big>
+ <received-echo-requests>0</received-echo-requests>
+ <received-echo-replies>0</received-echo-replies>
+ <received-router-solicitation>0</received-router-solicitation>
+ <received-router-advertisement>0</received-router-advertisement>
+ <sent-errors>1</sent-errors>
+ <sent-destination-unreachable>0</sent-destination-unreachable>
+ <sent-admin-prohibited>0</sent-admin-prohibited>
+ <sent-time-exceeded>1</sent-time-exceeded>
+ <sent-bad-parameter>0</sent-bad-parameter>
+ <sent-packet-too-big>0</sent-packet-too-big>
+ <sent-echo-requests>0</sent-echo-requests>
+ <sent-echo-replies>0</sent-echo-replies>
+ <sent-router-solicitation>0</sent-router-solicitation>
+ <sent-router-advertisement>0</sent-router-advertisement>
+ <sent-redirects>0</sent-redirects>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 21) ;;
+ *) jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface ICMPv6 statistics do not match: ${count} != 21" ;;
+ esac
+}
+frag6_14_body() {
+ frag6_body 14 frag6_14_check_stats
+}
+
+frag6_14_cleanup() {
+ frag6_cleanup 14
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "frag6_14"
+}
diff --git a/tests/sys/netinet6/frag6/frag6_15.py b/tests/sys/netinet6/frag6/frag6_15.py
new file mode 100644
index 000000000000..6b27122a33d5
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_15.py
@@ -0,0 +1,106 @@
+#!/usr/bin/env python
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import socket
+import sys
+from time import sleep
+
+def main():
+ parser = argparse.ArgumentParser("frag6.py",
+ description="IPv6 fragementation test tool")
+ parser.add_argument('--sendif', nargs=1,
+ required=True,
+ help='The interface through which the packet will be sent')
+ parser.add_argument('--recvif', nargs=1,
+ required=True,
+ help='The interface on which to check for the packet')
+ parser.add_argument('--src', nargs=1,
+ required=True,
+ help='The source IP address')
+ parser.add_argument('--to', nargs=1,
+ required=True,
+ help='The destination IP address')
+ parser.add_argument('--debug',
+ required=False, action='store_true',
+ help='Enable test debugging')
+
+ args = parser.parse_args()
+
+
+ ########################################################################
+ #
+ # Sysctl set to accept maximum 3 segments on a fragmented packet.
+ # The 4th packet will flush the entire q6.
+ #
+ # A: 4 Discarded.
+ # R: Silence (statistics only) no ICMPv6 as we skip the off=0 segment.
+ #
+ data = "66666666"
+ for i in range(4):
+ foffset=16 + (i * (0x100 + (int)(16 / 8)))
+ ip6f01 = sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrFragment(offset=foffset, m=1, id=15) / \
+ sp.UDP(dport=3456, sport=6543) / \
+ data
+ if args.debug :
+ ip6f01.display()
+ sp.sendp(ip6f01, iface=args.sendif[0], verbose=False)
+
+
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ##
+ #
+ # Sysctl set to accept maximum 3 segments on a fragmented packet.
+ # The 4th packet will flush the entire q6.
+ # This time we play proper offset/length games on the packets in order
+ # to trigger the 2nd test case, with the last packet still having m=1.
+ #
+ # A: 4 Discarded.
+ # R: ICMPv6 timeout expired.
+ #
+ data = "66666666"
+ for i in range(4):
+ foffset=(i * (int)(16 / 8))
+ ip6f01 = sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrFragment(offset=foffset, m=1, id=0x1501) / \
+ sp.UDP(dport=3456, sport=6543) / \
+ data
+ if args.debug :
+ ip6f01.display()
+ sp.sendp(ip6f01, iface=args.sendif[0], verbose=False)
+
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netinet6/frag6/frag6_15.sh b/tests/sys/netinet6/frag6/frag6_15.sh
new file mode 100755
index 000000000000..9efe671d0c54
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_15.sh
@@ -0,0 +1,256 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/frag6.subr
+
+frag6_15_pre_test() {
+
+ local jname ifname
+ jname=$1
+
+ case "${jname}" in
+ "") echo "ERROR: jname is empty"; return ;;
+ esac
+
+ # Accept 3 fragments per fragmented packet.
+ jexec ${jname} sysctl net.inet6.ip6.maxfragsperpacket=3
+}
+
+
+frag6_15_check_stats() {
+
+ local jname ifname
+ jname=$1
+ ifname=$2
+
+ case "${jname}" in
+ "") echo "ERROR: jname is empty"; return ;;
+ esac
+ case "${ifname}" in
+ "") echo "ERROR: ifname is empty"; return ;;
+ esac
+
+ # Defaults are: IPV6_FRAGTTL 120 slowtimo ticks.
+ # pfslowtimo() is run at hz/2. So this takes 60s.
+ # This is awefully long for a test case.
+ # The Python script has to wait for this already to get the ICMPv6
+ # hence we do not sleep here anymore.
+
+ nf=`jexec ${jname} sysctl -n net.inet6.ip6.frag6_nfragpackets`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "VNET frag6_nfragpackets not 0 but: ${nf}" ;;
+ esac
+ nf=`sysctl -n net.inet6.ip6.frag6_nfrags`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "Global frag6_nfrags not 0 but: ${nf}" ;;
+ esac
+
+ #
+ # Check that the sysctl is set to what we expect.
+ #
+ sn=`jexec ${jname} sysctl -n net.inet6.ip6.maxfragsperpacket`
+ case "${sn}" in
+ 3) ;;
+ *) atf_fail "Sysctl net.inet6.ip6.maxfragsperpacket is ${sn} and not 3" ;;
+ esac
+
+ #
+ # Check selection of global UDP stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-datagrams>0</received-datagrams>
+ <dropped-incomplete-headers>0</dropped-incomplete-headers>
+ <dropped-bad-data-length>0</dropped-bad-data-length>
+ <dropped-bad-checksum>0</dropped-bad-checksum>
+ <dropped-no-checksum>0</dropped-no-checksum>
+ <dropped-no-socket>0</dropped-no-socket>
+ <dropped-broadcast-multicast>0</dropped-broadcast-multicast>
+ <dropped-full-socket-buffer>0</dropped-full-socket-buffer>
+ <not-for-hashed-pcb>0</not-for-hashed-pcb>
+EOF
+ count=`jexec ${jname} netstat -s -p udp --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 9) ;;
+ *) jexec ${jname} netstat -s -p udp --libxo xml,pretty
+ atf_fail "Global UDP statistics do not match: ${count} != 9" ;;
+ esac
+
+
+ #
+ # Check selection of global IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-below-minimum-size>0</dropped-below-minimum-size>
+ <dropped-short-packets>0</dropped-short-packets>
+ <dropped-bad-options>0</dropped-bad-options>
+ <dropped-bad-version>0</dropped-bad-version>
+ <received-fragments>8</received-fragments>
+ <dropped-fragment>8</dropped-fragment>
+ <dropped-fragment-after-timeout>0</dropped-fragment-after-timeout>
+ <dropped-fragments-overflow>0</dropped-fragments-overflow>
+ <atomic-fragments>0</atomic-fragments>
+ <reassembled-packets>0</reassembled-packets>
+ <forwarded-packets>0</forwarded-packets>
+ <packets-not-forwardable>0</packets-not-forwardable>
+ <sent-redirects>0</sent-redirects>
+ <send-packets-fabricated-header>0</send-packets-fabricated-header>
+ <discard-no-mbufs>0</discard-no-mbufs>
+ <discard-no-route>0</discard-no-route>
+ <sent-fragments>0</sent-fragments>
+ <fragments-created>0</fragments-created>
+ <discard-cannot-fragment>0</discard-cannot-fragment>
+ <discard-scope-violations>0</discard-scope-violations>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 20) ;;
+ *) jexec ${jname} netstat -s -p ip6 --libxo xml,pretty
+ atf_fail "Global IPv6 statistics do not match: ${count} != 20" ;;
+ esac
+
+ #
+ # Check selection of global ICMPv6 stats.
+ # XXX-TODO check output histogram (just too hard to parse [no multi-line-grep])
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <icmp6-calls>1</icmp6-calls>
+ <no-route>0</no-route>
+ <admin-prohibited>0</admin-prohibited>
+ <beyond-scope>0</beyond-scope>
+ <address-unreachable>0</address-unreachable>
+ <port-unreachable>0</port-unreachable>
+ <packet-too-big>0</packet-too-big>
+ <time-exceed-transmit>0</time-exceed-transmit>
+ <time-exceed-reassembly>1</time-exceed-reassembly>
+ <bad-header>0</bad-header>
+ <bad-next-header>0</bad-next-header>
+ <bad-option>0</bad-option>
+ <redirects>0</redirects>
+ <unknown>0</unknown>
+ <reflect>0</reflect>
+ <too-many-nd-options>0</too-many-nd-options>
+ <bad-nd-options>0</bad-nd-options>
+ <bad-neighbor-solicitation>0</bad-neighbor-solicitation>
+ <bad-neighbor-advertisement>0</bad-neighbor-advertisement>
+ <bad-router-solicitation>0</bad-router-solicitation>
+ <bad-router-advertisement>0</bad-router-advertisement>
+ <bad-redirect>0</bad-redirect>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 22) ;;
+ *) jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty
+ atf_fail "Global ICMPv6 statistics do not match: ${count} != 22" ;;
+ esac
+
+ #
+ # Check selection of interface IPv6 stats.
+ # XXX-BZ no reassembly failed stats.#
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-invalid-header>0</dropped-invalid-header>
+ <dropped-mtu-exceeded>0</dropped-mtu-exceeded>
+ <dropped-no-route>0</dropped-no-route>
+ <dropped-invalid-destination>0</dropped-invalid-destination>
+ <dropped-unknown-protocol>0</dropped-unknown-protocol>
+ <dropped-truncated>0</dropped-truncated>
+ <sent-forwarded>0</sent-forwarded>
+ <discard-packets>0</discard-packets>
+ <discard-fragments>0</discard-fragments>
+ <fragments-failed>0</fragments-failed>
+ <fragments-created>0</fragments-created>
+ <reassembly-required>8</reassembly-required>
+ <reassembled-packets>0</reassembled-packets>
+ <reassembly-failed>0</reassembly-failed>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 14) ;;
+ *) jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface IPv6 statistics do not match: ${count} != 14" ;;
+ esac
+
+ #
+ # Check selection of interface ICMPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-errors>0</received-errors>
+ <received-destination-unreachable>0</received-destination-unreachable>
+ <received-admin-prohibited>0</received-admin-prohibited>
+ <received-time-exceeded>0</received-time-exceeded>
+ <received-bad-parameter>0</received-bad-parameter>
+ <received-packet-too-big>0</received-packet-too-big>
+ <received-echo-requests>0</received-echo-requests>
+ <received-echo-replies>0</received-echo-replies>
+ <received-router-solicitation>0</received-router-solicitation>
+ <received-router-advertisement>0</received-router-advertisement>
+ <sent-errors>1</sent-errors>
+ <sent-destination-unreachable>0</sent-destination-unreachable>
+ <sent-admin-prohibited>0</sent-admin-prohibited>
+ <sent-time-exceeded>1</sent-time-exceeded>
+ <sent-bad-parameter>0</sent-bad-parameter>
+ <sent-packet-too-big>0</sent-packet-too-big>
+ <sent-echo-requests>0</sent-echo-requests>
+ <sent-echo-replies>0</sent-echo-replies>
+ <sent-router-solicitation>0</sent-router-solicitation>
+ <sent-router-advertisement>0</sent-router-advertisement>
+ <sent-redirects>0</sent-redirects>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 21) ;;
+ *) jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface ICMPv6 statistics do not match: ${count} != 21" ;;
+ esac
+}
+
+atf_test_case "frag6_15" "cleanup"
+frag6_15_head() {
+ frag6_head 15
+}
+
+frag6_15_body() {
+ frag6_body 15 frag6_15_check_stats frag6_15_pre_test
+}
+
+frag6_15_cleanup() {
+ frag6_cleanup 15
+
+ # No need to restore the sysctl back to default as the jail is gone.
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "frag6_15"
+}
diff --git a/tests/sys/netinet6/frag6/frag6_16.py b/tests/sys/netinet6/frag6/frag6_16.py
new file mode 100644
index 000000000000..1225bbf1042f
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_16.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import socket
+import sys
+from sniffer import Sniffer
+from time import sleep
+
+def check_icmp6_error(args, packet):
+ ip6 = packet.getlayer(sp.IPv6)
+ if not ip6:
+ return False
+ oip6 = sp.IPv6(src=args.src[0], dst=args.to[0])
+ if ip6.dst != oip6.src:
+ return False
+ icmp6 = packet.getlayer(sp.ICMPv6DestUnreach)
+ if not icmp6:
+ return False
+ # ICMP6_DST_UNREACH_NOPORT 4
+ if icmp6.code != 4:
+ return False
+ # Should we check the payload as well?
+ # We are running in a very isolated environment and nothing else
+ # should trigger an ICMPv6 Dest Unreach / Port Unreach so leave it.
+ #icmp6.display()
+ return True
+
+def main():
+ parser = argparse.ArgumentParser("frag6.py",
+ description="IPv6 fragementation test tool")
+ parser.add_argument('--sendif', nargs=1,
+ required=True,
+ help='The interface through which the packet will be sent')
+ parser.add_argument('--recvif', nargs=1,
+ required=True,
+ help='The interface on which to check for the packet')
+ parser.add_argument('--src', nargs=1,
+ required=True,
+ help='The source IP address')
+ parser.add_argument('--to', nargs=1,
+ required=True,
+ help='The destination IP address')
+ parser.add_argument('--debug',
+ required=False, action='store_true',
+ help='Enable test debugging')
+
+ args = parser.parse_args()
+
+
+ # Start sniffing on recvif
+ sniffer = Sniffer(args, check_icmp6_error)
+
+
+ ########################################################################
+ #
+ # A single fragmented packet with upper layer data in multiple segments
+ # to pass fragmentation.
+ # We need to do a bit of a dance to get the UDP checksum right.
+ #
+ # A: 1 reassembled packet
+ # R: Statistics and ICMPv6 error (non-fragmentation) as no port open.
+ #
+ data = "6" * 1280
+ dataall = data * 30
+ ip6f01 = \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.UDP(dport=3456, sport=6543) / \
+ dataall
+ ip6fd = sp.IPv6(sp.raw(ip6f01))
+
+ ip6f01 = sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrFragment(offset=0, m=1, id=16) / \
+ sp.UDP(dport=3456, sport=6543, len=ip6fd.len, chksum=ip6fd.chksum) / \
+ data
+ if args.debug :
+ ip6f01.display()
+ sp.sendp(ip6f01, iface=args.sendif[0], verbose=False)
+
+ foffset=(int)(1288/8)
+ mbit=1
+ for i in range(1,30):
+ if i == 29:
+ mbit=0
+ ip6f0n = sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrFragment(offset=foffset, m=mbit, id=16, nh=socket.IPPROTO_UDP) / \
+ data
+ if args.debug :
+ ip6f0n.display()
+ sp.sendp(ip6f0n, iface=args.sendif[0], verbose=False)
+ foffset += (int)(1280/8)
+
+ sleep(0.10)
+ sniffer.setEnd()
+ sniffer.join()
+ if not sniffer.foundCorrectPacket:
+ sys.exit(1)
+
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netinet6/frag6/frag6_16.sh b/tests/sys/netinet6/frag6/frag6_16.sh
new file mode 100755
index 000000000000..8e5e3cc8866d
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_16.sh
@@ -0,0 +1,232 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/frag6.subr
+
+
+frag6_16_check_stats() {
+
+ local jname ifname
+ jname=$1
+ ifname=$2
+
+ case "${jname}" in
+ "") echo "ERROR: jname is empty"; return ;;
+ esac
+ case "${ifname}" in
+ "") echo "ERROR: ifname is empty"; return ;;
+ esac
+
+ # Defaults are: IPV6_FRAGTTL 120 slowtimo ticks.
+ # pfslowtimo() is run at hz/2. So this takes 60s.
+ # This is awefully long for a test case.
+ # The Python script has to wait for this already to get the ICMPv6
+ # hence we do not sleep here anymore.
+
+ nf=`jexec ${jname} sysctl -n net.inet6.ip6.frag6_nfragpackets`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "VNET frag6_nfragpackets not 0 but: ${nf}" ;;
+ esac
+ nf=`sysctl -n net.inet6.ip6.frag6_nfrags`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "Global frag6_nfrags not 0 but: ${nf}" ;;
+ esac
+
+ #
+ # Check selection of global UDP stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-datagrams>1</received-datagrams>
+ <dropped-incomplete-headers>0</dropped-incomplete-headers>
+ <dropped-bad-data-length>0</dropped-bad-data-length>
+ <dropped-bad-checksum>0</dropped-bad-checksum>
+ <dropped-no-checksum>0</dropped-no-checksum>
+ <dropped-no-socket>1</dropped-no-socket>
+ <dropped-broadcast-multicast>0</dropped-broadcast-multicast>
+ <dropped-full-socket-buffer>0</dropped-full-socket-buffer>
+ <not-for-hashed-pcb>0</not-for-hashed-pcb>
+EOF
+ count=`jexec ${jname} netstat -s -p udp --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 9) ;;
+ *) jexec ${jname} netstat -s -p udp --libxo xml,pretty
+ atf_fail "Global UDP statistics do not match: ${count} != 9" ;;
+ esac
+
+
+ #
+ # Check selection of global IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-below-minimum-size>0</dropped-below-minimum-size>
+ <dropped-short-packets>0</dropped-short-packets>
+ <dropped-bad-options>0</dropped-bad-options>
+ <dropped-bad-version>0</dropped-bad-version>
+ <received-fragments>30</received-fragments>
+ <dropped-fragment>0</dropped-fragment>
+ <dropped-fragment-after-timeout>0</dropped-fragment-after-timeout>
+ <dropped-fragments-overflow>0</dropped-fragments-overflow>
+ <atomic-fragments>0</atomic-fragments>
+ <reassembled-packets>1</reassembled-packets>
+ <forwarded-packets>0</forwarded-packets>
+ <packets-not-forwardable>0</packets-not-forwardable>
+ <sent-redirects>0</sent-redirects>
+ <send-packets-fabricated-header>0</send-packets-fabricated-header>
+ <discard-no-mbufs>0</discard-no-mbufs>
+ <discard-no-route>0</discard-no-route>
+ <sent-fragments>0</sent-fragments>
+ <fragments-created>0</fragments-created>
+ <discard-cannot-fragment>0</discard-cannot-fragment>
+ <discard-scope-violations>0</discard-scope-violations>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 20) ;;
+ *) jexec ${jname} netstat -s -p ip6 --libxo xml,pretty
+ atf_fail "Global IPv6 statistics do not match: ${count} != 20" ;;
+ esac
+
+ #
+ # Check selection of global ICMPv6 stats.
+ # XXX-TODO check output histogram (just too hard to parse [no multi-line-grep])
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <icmp6-calls>1</icmp6-calls>
+ <no-route>0</no-route>
+ <admin-prohibited>0</admin-prohibited>
+ <beyond-scope>0</beyond-scope>
+ <address-unreachable>0</address-unreachable>
+ <port-unreachable>1</port-unreachable>
+ <packet-too-big>0</packet-too-big>
+ <time-exceed-transmit>0</time-exceed-transmit>
+ <time-exceed-reassembly>0</time-exceed-reassembly>
+ <bad-header>0</bad-header>
+ <bad-next-header>0</bad-next-header>
+ <bad-option>0</bad-option>
+ <redirects>0</redirects>
+ <unknown>0</unknown>
+ <reflect>0</reflect>
+ <too-many-nd-options>0</too-many-nd-options>
+ <bad-nd-options>0</bad-nd-options>
+ <bad-neighbor-solicitation>0</bad-neighbor-solicitation>
+ <bad-neighbor-advertisement>0</bad-neighbor-advertisement>
+ <bad-router-solicitation>0</bad-router-solicitation>
+ <bad-router-advertisement>0</bad-router-advertisement>
+ <bad-redirect>0</bad-redirect>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 22) ;;
+ *) jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty
+ atf_fail "Global ICMPv6 statistics do not match: ${count} != 22" ;;
+ esac
+
+ #
+ # Check selection of interface IPv6 stats.
+ # XXX-BZ no reassembly failed stats.#
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-invalid-header>0</dropped-invalid-header>
+ <dropped-mtu-exceeded>0</dropped-mtu-exceeded>
+ <dropped-no-route>0</dropped-no-route>
+ <dropped-invalid-destination>0</dropped-invalid-destination>
+ <dropped-unknown-protocol>0</dropped-unknown-protocol>
+ <dropped-truncated>0</dropped-truncated>
+ <sent-forwarded>0</sent-forwarded>
+ <discard-packets>0</discard-packets>
+ <discard-fragments>0</discard-fragments>
+ <fragments-failed>0</fragments-failed>
+ <fragments-created>0</fragments-created>
+ <reassembly-required>30</reassembly-required>
+ <reassembled-packets>1</reassembled-packets>
+ <reassembly-failed>0</reassembly-failed>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 14) ;;
+ *) jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface IPv6 statistics do not match: ${count} != 14" ;;
+ esac
+
+ #
+ # Check selection of interface ICMPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-errors>0</received-errors>
+ <received-destination-unreachable>0</received-destination-unreachable>
+ <received-admin-prohibited>0</received-admin-prohibited>
+ <received-time-exceeded>0</received-time-exceeded>
+ <received-bad-parameter>0</received-bad-parameter>
+ <received-packet-too-big>0</received-packet-too-big>
+ <received-echo-requests>0</received-echo-requests>
+ <received-echo-replies>0</received-echo-replies>
+ <received-router-solicitation>0</received-router-solicitation>
+ <received-router-advertisement>0</received-router-advertisement>
+ <sent-errors>1</sent-errors>
+ <sent-destination-unreachable>1</sent-destination-unreachable>
+ <sent-admin-prohibited>0</sent-admin-prohibited>
+ <sent-time-exceeded>0</sent-time-exceeded>
+ <sent-bad-parameter>0</sent-bad-parameter>
+ <sent-packet-too-big>0</sent-packet-too-big>
+ <sent-echo-requests>0</sent-echo-requests>
+ <sent-echo-replies>0</sent-echo-replies>
+ <sent-router-solicitation>0</sent-router-solicitation>
+ <sent-router-advertisement>0</sent-router-advertisement>
+ <sent-redirects>0</sent-redirects>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 21) ;;
+ *) jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface ICMPv6 statistics do not match: ${count} != 21" ;;
+ esac
+}
+
+atf_test_case "frag6_16" "cleanup"
+frag6_16_head() {
+ frag6_head 16
+}
+
+frag6_16_body() {
+ frag6_body 16 frag6_16_check_stats
+}
+
+frag6_16_cleanup() {
+ frag6_cleanup 16
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "frag6_16"
+}
diff --git a/tests/sys/netinet6/frag6/frag6_17.py b/tests/sys/netinet6/frag6/frag6_17.py
new file mode 100644
index 000000000000..3228f9186c68
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_17.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+import argparse
+import random as random
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import socket
+import sys
+
+def main():
+ parser = argparse.ArgumentParser("frag6.py",
+ description="IPv6 fragementation test tool")
+ parser.add_argument('--sendif', nargs=1,
+ required=True,
+ help='The interface through which the packet will be sent')
+ parser.add_argument('--recvif', nargs=1,
+ required=True,
+ help='The interface on which to check for the packet')
+ parser.add_argument('--src', nargs=1,
+ required=True,
+ help='The source IP address')
+ parser.add_argument('--to', nargs=1,
+ required=True,
+ help='The destination IP address')
+ parser.add_argument('--debug',
+ required=False, action='store_true',
+ help='Enable test debugging')
+
+ args = parser.parse_args()
+
+
+ ########################################################################
+ #
+ # Send a sample of pseudo-random fragments into the system in order
+ # to test vnet teardown.
+ #
+ # A: Cleaned up and freed
+ # R: No panic (ignoring everything else)
+ #
+
+ random.seed()
+ packets = [];
+
+ for i in range(0,127):
+ fid=random.randint(0,0xffff)
+ foffset=random.randint(0,0xffff)
+ fm=random.randint(0,1)
+ fsrc=sp.RandIP6()
+ ip6f01 = sp.Ether() / \
+ sp.IPv6(src=fsrc, dst=args.to[0]) / \
+ sp.IPv6ExtHdrFragment(offset=foffset, m=fm, id=fid) / \
+ sp.UDP(dport=3456, sport=6543)
+ if args.debug:
+ ip6f01.display()
+ packets.append(ip6f01)
+
+ for p in packets:
+ sp.sendp(p, iface=args.sendif[0], verbose=False)
+
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netinet6/frag6/frag6_17.sh b/tests/sys/netinet6/frag6/frag6_17.sh
new file mode 100755
index 000000000000..eb6a7e44a2a7
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_17.sh
@@ -0,0 +1,46 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/frag6.subr
+
+atf_test_case "frag6_17" "cleanup"
+frag6_17_head() {
+ frag6_head 17
+}
+
+frag6_17_body() {
+ frag6_body 17
+}
+
+frag6_17_cleanup() {
+ frag6_cleanup 17
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "frag6_17"
+}
diff --git a/tests/sys/netinet6/frag6/frag6_18.py b/tests/sys/netinet6/frag6/frag6_18.py
new file mode 100644
index 000000000000..3228f9186c68
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_18.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+import argparse
+import random as random
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import socket
+import sys
+
+def main():
+ parser = argparse.ArgumentParser("frag6.py",
+ description="IPv6 fragementation test tool")
+ parser.add_argument('--sendif', nargs=1,
+ required=True,
+ help='The interface through which the packet will be sent')
+ parser.add_argument('--recvif', nargs=1,
+ required=True,
+ help='The interface on which to check for the packet')
+ parser.add_argument('--src', nargs=1,
+ required=True,
+ help='The source IP address')
+ parser.add_argument('--to', nargs=1,
+ required=True,
+ help='The destination IP address')
+ parser.add_argument('--debug',
+ required=False, action='store_true',
+ help='Enable test debugging')
+
+ args = parser.parse_args()
+
+
+ ########################################################################
+ #
+ # Send a sample of pseudo-random fragments into the system in order
+ # to test vnet teardown.
+ #
+ # A: Cleaned up and freed
+ # R: No panic (ignoring everything else)
+ #
+
+ random.seed()
+ packets = [];
+
+ for i in range(0,127):
+ fid=random.randint(0,0xffff)
+ foffset=random.randint(0,0xffff)
+ fm=random.randint(0,1)
+ fsrc=sp.RandIP6()
+ ip6f01 = sp.Ether() / \
+ sp.IPv6(src=fsrc, dst=args.to[0]) / \
+ sp.IPv6ExtHdrFragment(offset=foffset, m=fm, id=fid) / \
+ sp.UDP(dport=3456, sport=6543)
+ if args.debug:
+ ip6f01.display()
+ packets.append(ip6f01)
+
+ for p in packets:
+ sp.sendp(p, iface=args.sendif[0], verbose=False)
+
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netinet6/frag6/frag6_18.sh b/tests/sys/netinet6/frag6/frag6_18.sh
new file mode 100755
index 000000000000..bfaa85fe9f8b
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_18.sh
@@ -0,0 +1,63 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/frag6.subr
+
+frag6_18_delif() {
+
+ local jname ifname
+ jname=$1
+ ifname=$2
+
+ case "${jname}" in
+ "") echo "ERROR: jname is empty"; return ;;
+ esac
+ case "${ifname}" in
+ "") echo "ERROR: ifname is empty"; return ;;
+ esac
+
+ # Invalidate the ifp from the packets in the reassembly queue.
+ ifconfig ${ifname} -vnet ${jname}
+}
+
+atf_test_case "frag6_18" "cleanup"
+frag6_18_head() {
+ frag6_head 18
+}
+
+frag6_18_body() {
+ frag6_body 18 frag6_18_delif
+}
+
+frag6_18_cleanup() {
+ frag6_cleanup 18
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "frag6_18"
+}
diff --git a/tests/sys/netinet6/frag6/frag6_19.py b/tests/sys/netinet6/frag6/frag6_19.py
new file mode 100644
index 000000000000..3848744ea30f
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_19.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import socket
+import sys
+
+def main():
+ parser = argparse.ArgumentParser("frag6.py",
+ description="IPv6 fragementation test tool")
+ parser.add_argument('--sendif', nargs=1,
+ required=True,
+ help='The interface through which the packet will be sent')
+ parser.add_argument('--recvif', nargs=1,
+ required=True,
+ help='The interface on which to check for the packet')
+ parser.add_argument('--src', nargs=1,
+ required=True,
+ help='The source IP address')
+ parser.add_argument('--to', nargs=1,
+ required=True,
+ help='The destination IP address')
+ parser.add_argument('--debug',
+ required=False, action='store_true',
+ help='Enable test debugging')
+
+ args = parser.parse_args()
+
+
+ ########################################################################
+ #
+ # Send a sample of sequeneced ID fragments into the system in order
+ # to test bucket distribution.
+ #
+ # A: No overflow at V_ip6_maxfragsperpacket == 64.
+ # R: Stats only, timeout and no ICMPv6 (all ignored).
+ #
+ packets = [];
+ data = "66666666"
+ for i in range(0,127):
+ ip6f01 = sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrFragment(offset=1, m=1, id=i) / \
+ data
+ if args.debug:
+ ip6f01.display()
+ packets.append(ip6f01)
+
+ for p in packets:
+ sp.sendp(p, iface=args.sendif[0], verbose=False)
+
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netinet6/frag6/frag6_19.sh b/tests/sys/netinet6/frag6/frag6_19.sh
new file mode 100755
index 000000000000..187876c4c2bc
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_19.sh
@@ -0,0 +1,223 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/frag6.subr
+
+frag6_19_check_stats() {
+
+ local jname ifname
+ jname=$1
+ ifname=$2
+
+ case "${jname}" in
+ "") echo "ERROR: jname is empty"; return ;;
+ esac
+ case "${ifname}" in
+ "") echo "ERROR: ifname is empty"; return ;;
+ esac
+
+ # Defaults are: IPV6_FRAGTTL 120 slowtimo ticks.
+ # pfslowtimo() is run at hz/2. So this takes 60s.
+ # This is awefully long for a test case.
+ # The Python script has to wait for this already to get the ICMPv6
+ # hence we do not sleep here anymore.
+
+ #
+ # Check selection of global UDP stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-datagrams>0</received-datagrams>
+ <dropped-incomplete-headers>0</dropped-incomplete-headers>
+ <dropped-bad-data-length>0</dropped-bad-data-length>
+ <dropped-bad-checksum>0</dropped-bad-checksum>
+ <dropped-no-checksum>0</dropped-no-checksum>
+ <dropped-no-socket>0</dropped-no-socket>
+ <dropped-broadcast-multicast>0</dropped-broadcast-multicast>
+ <dropped-full-socket-buffer>0</dropped-full-socket-buffer>
+ <not-for-hashed-pcb>0</not-for-hashed-pcb>
+EOF
+ count=`jexec ${jname} netstat -s -p udp --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 9) ;;
+ *) jexec ${jname} netstat -s -p udp --libxo xml,pretty
+ atf_fail "Global UDP statistics do not match: ${count} != 9" ;;
+ esac
+
+
+ #
+ # Check selection of global IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-below-minimum-size>0</dropped-below-minimum-size>
+ <dropped-short-packets>0</dropped-short-packets>
+ <dropped-bad-options>0</dropped-bad-options>
+ <dropped-bad-version>0</dropped-bad-version>
+ <received-fragments>127</received-fragments>
+ <dropped-fragment>0</dropped-fragment>
+ <dropped-fragment-after-timeout>0</dropped-fragment-after-timeout>
+ <dropped-fragments-overflow>0</dropped-fragments-overflow>
+ <atomic-fragments>0</atomic-fragments>
+ <reassembled-packets>0</reassembled-packets>
+ <forwarded-packets>0</forwarded-packets>
+ <packets-not-forwardable>0</packets-not-forwardable>
+ <sent-redirects>0</sent-redirects>
+ <send-packets-fabricated-header>0</send-packets-fabricated-header>
+ <discard-no-mbufs>0</discard-no-mbufs>
+ <discard-no-route>0</discard-no-route>
+ <sent-fragments>0</sent-fragments>
+ <fragments-created>0</fragments-created>
+ <discard-cannot-fragment>0</discard-cannot-fragment>
+ <discard-scope-violations>0</discard-scope-violations>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 20) ;;
+ *) jexec ${jname} netstat -s -p ip6 --libxo xml,pretty
+ atf_fail "Global IPv6 statistics do not match: ${count} != 20" ;;
+ esac
+
+ #
+ # Check selection of global ICMPv6 stats.
+ # XXX-TODO check output histogram (just too hard to parse [no multi-line-grep])
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <icmp6-calls>0</icmp6-calls>
+ <no-route>0</no-route>
+ <admin-prohibited>0</admin-prohibited>
+ <beyond-scope>0</beyond-scope>
+ <address-unreachable>0</address-unreachable>
+ <port-unreachable>0</port-unreachable>
+ <packet-too-big>0</packet-too-big>
+ <time-exceed-transmit>0</time-exceed-transmit>
+ <time-exceed-reassembly>0</time-exceed-reassembly>
+ <bad-header>0</bad-header>
+ <bad-next-header>0</bad-next-header>
+ <bad-option>0</bad-option>
+ <redirects>0</redirects>
+ <unknown>0</unknown>
+ <reflect>0</reflect>
+ <too-many-nd-options>0</too-many-nd-options>
+ <bad-nd-options>0</bad-nd-options>
+ <bad-neighbor-solicitation>0</bad-neighbor-solicitation>
+ <bad-neighbor-advertisement>0</bad-neighbor-advertisement>
+ <bad-router-solicitation>0</bad-router-solicitation>
+ <bad-router-advertisement>0</bad-router-advertisement>
+ <bad-redirect>0</bad-redirect>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 22) ;;
+ *) jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty
+ atf_fail "Global ICMPv6 statistics do not match: ${count} != 22" ;;
+ esac
+
+ #
+ # Check selection of interface IPv6 stats.
+ # XXX-BZ no reassembly failed stats.#
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-invalid-header>0</dropped-invalid-header>
+ <dropped-mtu-exceeded>0</dropped-mtu-exceeded>
+ <dropped-no-route>0</dropped-no-route>
+ <dropped-invalid-destination>0</dropped-invalid-destination>
+ <dropped-unknown-protocol>0</dropped-unknown-protocol>
+ <dropped-truncated>0</dropped-truncated>
+ <sent-forwarded>0</sent-forwarded>
+ <discard-packets>0</discard-packets>
+ <discard-fragments>0</discard-fragments>
+ <fragments-failed>0</fragments-failed>
+ <fragments-created>0</fragments-created>
+ <reassembly-required>127</reassembly-required>
+ <reassembled-packets>0</reassembled-packets>
+ <reassembly-failed>0</reassembly-failed>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 14) ;;
+ *) jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface IPv6 statistics do not match: ${count} != 14" ;;
+ esac
+
+ #
+ # Check selection of interface ICMPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-errors>0</received-errors>
+ <received-destination-unreachable>0</received-destination-unreachable>
+ <received-admin-prohibited>0</received-admin-prohibited>
+ <received-time-exceeded>0</received-time-exceeded>
+ <received-bad-parameter>0</received-bad-parameter>
+ <received-packet-too-big>0</received-packet-too-big>
+ <received-echo-requests>0</received-echo-requests>
+ <received-echo-replies>0</received-echo-replies>
+ <received-router-solicitation>0</received-router-solicitation>
+ <received-router-advertisement>0</received-router-advertisement>
+ <sent-errors>0</sent-errors>
+ <sent-destination-unreachable>0</sent-destination-unreachable>
+ <sent-admin-prohibited>0</sent-admin-prohibited>
+ <sent-time-exceeded>0</sent-time-exceeded>
+ <sent-bad-parameter>0</sent-bad-parameter>
+ <sent-packet-too-big>0</sent-packet-too-big>
+ <sent-echo-requests>0</sent-echo-requests>
+ <sent-echo-replies>0</sent-echo-replies>
+ <sent-router-solicitation>0</sent-router-solicitation>
+ <sent-router-advertisement>0</sent-router-advertisement>
+ <sent-redirects>0</sent-redirects>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 21) ;;
+ *) jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface ICMPv6 statistics do not match: ${count} != 21" ;;
+ esac
+}
+
+atf_test_case "frag6_19" "cleanup"
+frag6_19_head() {
+ frag6_head 19
+}
+
+frag6_19_body() {
+ if [ "$(atf_config_get ci false)" = "true" ]; then
+ atf_skip "https://bugs.freebsd.org/274941"
+ fi
+ frag6_body 19 frag6_19_check_stats
+}
+
+frag6_19_cleanup() {
+ frag6_cleanup 19
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "frag6_19"
+}
diff --git a/tests/sys/netinet6/frag6/frag6_20.py b/tests/sys/netinet6/frag6/frag6_20.py
new file mode 100755
index 000000000000..37b091dca9aa
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_20.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import socket
+import sys
+from sniffer import Sniffer
+from time import sleep
+
+def check_icmp6_error(args, packet):
+ ip6 = packet.getlayer(sp.IPv6)
+ if not ip6:
+ return False
+ oip6 = sp.IPv6(src=args.src[0], dst=args.to[0])
+ if ip6.dst != oip6.src:
+ return False
+ icmp6 = packet.getlayer(sp.ICMPv6TimeExceeded)
+ if not icmp6:
+ return False
+ # ICMP6_TIME_EXCEED_REASSEMBLY 1
+ if icmp6.code != 1:
+ return False
+ # Should we check the payload as well?
+ # We are running in a very isolated environment and nothing else
+ # should trigger an ICMPv6 Time Exceeded / Frag reassembly so leave it.
+ #icmp6.display()
+ return True
+
+
+def main():
+ parser = argparse.ArgumentParser("frag6.py",
+ description="IPv6 fragementation test tool")
+ parser.add_argument('--sendif', nargs=1,
+ required=True,
+ help='The interface through which the packet will be sent')
+ parser.add_argument('--recvif', nargs=1,
+ required=True,
+ help='The interface on which to check for the packet')
+ parser.add_argument('--src', nargs=1,
+ required=True,
+ help='The source IP address')
+ parser.add_argument('--to', nargs=1,
+ required=True,
+ help='The destination IP address')
+ parser.add_argument('--debug',
+ required=False, action='store_true',
+ help='Enable test debugging')
+
+ args = parser.parse_args()
+
+
+ # Start sniffing on recvif
+ sniffer = Sniffer(args, check_icmp6_error)
+
+
+ ########################################################################
+ #
+ # Send a proper first fragment (off=0) and a second fragment which
+ # just fits the 64k. The re-send the first fragment with an extra
+ # unfragmentable part making the 64k to exceed the limit.
+ # This is to make sure we don't allow to update meta-data for a
+ # 1st fragmented packet should a second arrive but given the
+ # fragmentable part is an exact duplicate only that fragment
+ # will be silently discarded.
+ #
+ # A: Reassembly failure, timeout after
+ # R: ICMPv6 time exceeded / statistics for the duplicate
+ #
+ data = "6" * 8
+ ip6f00 = \
+ sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrFragment(offset=0, m=1, id=20) / \
+ sp.UDP(dport=3456, sport=6543) / \
+ data
+ data = "6" * 15
+ ip6f01 = \
+ sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrFragment(offset=0x1ffc, m=0, id=20) / \
+ sp.UDP(dport=3456, sport=6543) / \
+ data
+ data = "6" * 8
+ ip6f02 = \
+ sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.IPv6ExtHdrDestOpt(options = \
+ sp.PadN(optdata="\x00\x00\x00\x00\x00\x00")) / \
+ sp.IPv6ExtHdrFragment(offset=0, m=1, id=20) / \
+ sp.UDP(dport=3456, sport=6543) / \
+ data
+ if args.debug :
+ ip6f00.display()
+ ip6f01.display()
+ ip6f02.display()
+ sp.sendp(ip6f00, iface=args.sendif[0], verbose=False)
+ sp.sendp(ip6f01, iface=args.sendif[0], verbose=False)
+ sp.sendp(ip6f02, iface=args.sendif[0], verbose=False)
+
+ sleep(3)
+ sniffer.setEnd()
+ sniffer.join()
+ if not sniffer.foundCorrectPacket:
+ sys.exit(1)
+
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netinet6/frag6/frag6_20.sh b/tests/sys/netinet6/frag6/frag6_20.sh
new file mode 100755
index 000000000000..ee4741ddc0bb
--- /dev/null
+++ b/tests/sys/netinet6/frag6/frag6_20.sh
@@ -0,0 +1,230 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/frag6.subr
+
+frag6_20_check_stats() {
+
+ local jname ifname
+ jname=$1
+ ifname=$2
+
+ case "${jname}" in
+ "") echo "ERROR: jname is empty"; return ;;
+ esac
+ case "${ifname}" in
+ "") echo "ERROR: ifname is empty"; return ;;
+ esac
+
+ # Defaults are: IPV6_FRAGTTL 120 slowtimo ticks.
+ # pfslowtimo() is run at hz/2. So this takes 60s.
+ # This is awefully long for a test case.
+ # The Python script has to wait for this already to get the ICMPv6
+ # hence we do not sleep here anymore.
+
+ nf=`jexec ${jname} sysctl -n net.inet6.ip6.frag6_nfragpackets`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "VNET frag6_nfragpackets not 0 but: ${nf}" ;;
+ esac
+ nf=`sysctl -n net.inet6.ip6.frag6_nfrags`
+ case ${nf} in
+ 0) break ;;
+ *) atf_fail "Global frag6_nfrags not 0 but: ${nf}" ;;
+ esac
+
+ #
+ # Check selection of global UDP stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-datagrams>0</received-datagrams>
+ <dropped-incomplete-headers>0</dropped-incomplete-headers>
+ <dropped-bad-data-length>0</dropped-bad-data-length>
+ <dropped-bad-checksum>0</dropped-bad-checksum>
+ <dropped-no-checksum>0</dropped-no-checksum>
+ <dropped-no-socket>0</dropped-no-socket>
+ <dropped-broadcast-multicast>0</dropped-broadcast-multicast>
+ <dropped-full-socket-buffer>0</dropped-full-socket-buffer>
+ <not-for-hashed-pcb>0</not-for-hashed-pcb>
+EOF
+ count=`jexec ${jname} netstat -s -p udp --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 9) ;;
+ *) jexec ${jname} netstat -s -p udp --libxo xml,pretty
+ atf_fail "Global UDP statistics do not match: ${count} != 9" ;;
+ esac
+
+
+ #
+ # Check selection of global IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-below-minimum-size>0</dropped-below-minimum-size>
+ <dropped-short-packets>0</dropped-short-packets>
+ <dropped-bad-options>0</dropped-bad-options>
+ <dropped-bad-version>0</dropped-bad-version>
+ <received-fragments>3</received-fragments>
+ <dropped-fragment>1</dropped-fragment>
+ <dropped-fragment-after-timeout>2</dropped-fragment-after-timeout>
+ <dropped-fragments-overflow>0</dropped-fragments-overflow>
+ <atomic-fragments>0</atomic-fragments>
+ <reassembled-packets>0</reassembled-packets>
+ <forwarded-packets>0</forwarded-packets>
+ <packets-not-forwardable>0</packets-not-forwardable>
+ <sent-redirects>0</sent-redirects>
+ <send-packets-fabricated-header>0</send-packets-fabricated-header>
+ <discard-no-mbufs>0</discard-no-mbufs>
+ <discard-no-route>0</discard-no-route>
+ <sent-fragments>0</sent-fragments>
+ <fragments-created>0</fragments-created>
+ <discard-cannot-fragment>0</discard-cannot-fragment>
+ <discard-scope-violations>0</discard-scope-violations>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 20) ;;
+ *) jexec ${jname} netstat -s -p ip6 --libxo xml,pretty
+ atf_fail "Global IPv6 statistics do not match: ${count} != 20" ;;
+ esac
+
+ #
+ # Check selection of global ICMPv6 stats.
+ # XXX-TODO check output histogram (just too hard to parse [no multi-line-grep])
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <icmp6-calls>1</icmp6-calls>
+ <no-route>0</no-route>
+ <admin-prohibited>0</admin-prohibited>
+ <beyond-scope>0</beyond-scope>
+ <address-unreachable>0</address-unreachable>
+ <port-unreachable>0</port-unreachable>
+ <packet-too-big>0</packet-too-big>
+ <time-exceed-transmit>0</time-exceed-transmit>
+ <time-exceed-reassembly>1</time-exceed-reassembly>
+ <bad-header>0</bad-header>
+ <bad-next-header>0</bad-next-header>
+ <bad-option>0</bad-option>
+ <redirects>0</redirects>
+ <unknown>0</unknown>
+ <reflect>0</reflect>
+ <too-many-nd-options>0</too-many-nd-options>
+ <bad-nd-options>0</bad-nd-options>
+ <bad-neighbor-solicitation>0</bad-neighbor-solicitation>
+ <bad-neighbor-advertisement>0</bad-neighbor-advertisement>
+ <bad-router-solicitation>0</bad-router-solicitation>
+ <bad-router-advertisement>0</bad-router-advertisement>
+ <bad-redirect>0</bad-redirect>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 22) ;;
+ *) jexec ${jname} netstat -s -p icmp6 --libxo xml,pretty
+ atf_fail "Global ICMPv6 statistics do not match: ${count} != 22" ;;
+ esac
+
+ #
+ # Check selection of interface IPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <dropped-invalid-header>0</dropped-invalid-header>
+ <dropped-mtu-exceeded>0</dropped-mtu-exceeded>
+ <dropped-no-route>0</dropped-no-route>
+ <dropped-invalid-destination>0</dropped-invalid-destination>
+ <dropped-unknown-protocol>0</dropped-unknown-protocol>
+ <dropped-truncated>0</dropped-truncated>
+ <sent-forwarded>0</sent-forwarded>
+ <discard-packets>0</discard-packets>
+ <discard-fragments>0</discard-fragments>
+ <fragments-failed>0</fragments-failed>
+ <fragments-created>0</fragments-created>
+ <reassembly-required>3</reassembly-required>
+ <reassembled-packets>0</reassembled-packets>
+ <reassembly-failed>1</reassembly-failed>
+EOF
+ count=`jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 14) ;;
+ *) jexec ${jname} netstat -s -p ip6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface IPv6 statistics do not match: ${count} != 14" ;;
+ esac
+
+ #
+ # Check selection of interface ICMPv6 stats.
+ #
+ cat <<EOF > ${HOME}/filter-${jname}.txt
+ <received-errors>0</received-errors>
+ <received-destination-unreachable>0</received-destination-unreachable>
+ <received-admin-prohibited>0</received-admin-prohibited>
+ <received-time-exceeded>0</received-time-exceeded>
+ <received-bad-parameter>0</received-bad-parameter>
+ <received-packet-too-big>0</received-packet-too-big>
+ <received-echo-requests>0</received-echo-requests>
+ <received-echo-replies>0</received-echo-replies>
+ <received-router-solicitation>0</received-router-solicitation>
+ <received-router-advertisement>0</received-router-advertisement>
+ <sent-errors>1</sent-errors>
+ <sent-destination-unreachable>0</sent-destination-unreachable>
+ <sent-admin-prohibited>0</sent-admin-prohibited>
+ <sent-time-exceeded>1</sent-time-exceeded>
+ <sent-bad-parameter>0</sent-bad-parameter>
+ <sent-packet-too-big>0</sent-packet-too-big>
+ <sent-echo-requests>0</sent-echo-requests>
+ <sent-echo-replies>0</sent-echo-replies>
+ <sent-router-solicitation>0</sent-router-solicitation>
+ <sent-router-advertisement>0</sent-router-advertisement>
+ <sent-redirects>0</sent-redirects>
+EOF
+ count=`jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty | grep -E -x -c -f ${HOME}/filter-${jname}.txt`
+ rm -f ${HOME}/filter-${jname}.txt
+ case ${count} in
+ 21) ;;
+ *) jexec ${jname} netstat -s -p icmp6 -I ${ifname} --libxo xml,pretty
+ atf_fail "Interface ICMPv6 statistics do not match: ${count} != 21" ;;
+ esac
+}
+
+atf_test_case "frag6_20" "cleanup"
+frag6_20_head() {
+ frag6_head 20
+}
+
+frag6_20_body() {
+ frag6_body 20 frag6_20_check_stats
+}
+
+frag6_20_cleanup() {
+ frag6_cleanup 20
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "frag6_20"
+}
diff --git a/tests/sys/netinet6/frag6/sniffer.py b/tests/sys/netinet6/frag6/sniffer.py
new file mode 100644
index 000000000000..4904214cf8ab
--- /dev/null
+++ b/tests/sys/netinet6/frag6/sniffer.py
@@ -0,0 +1,41 @@
+
+import threading
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+
+class Sniffer(threading.Thread):
+ def __init__(self, args, check_function):
+ threading.Thread.__init__(self)
+
+ self._args = args
+ self._recvif = args.recvif[0]
+ self._check_function = check_function
+ self.foundCorrectPacket = False
+ self._endme = False
+
+ self.start()
+
+ def _checkPacket(self, packet):
+ ret = self._check_function(self._args, packet)
+ if ret:
+ self.foundCorrectPacket = True
+ return ret
+
+ def setEnd(self):
+ self._endme = True
+
+ def stopFilter(self, pkt):
+ if pkt is not None:
+ self._checkPacket(pkt)
+ if self.foundCorrectPacket or self._endme:
+ return True
+ else:
+ return False
+
+ def run(self):
+ while True:
+ self.packets = sp.sniff(iface=self._recvif, store=False,
+ stop_filter=self.stopFilter, timeout=90)
+ if self.stopFilter(None):
+ break
diff --git a/tests/sys/netinet6/lpm6.sh b/tests/sys/netinet6/lpm6.sh
new file mode 100755
index 000000000000..b1042eb43dff
--- /dev/null
+++ b/tests/sys/netinet6/lpm6.sh
@@ -0,0 +1,198 @@
+#!/usr/bin/env atf-sh
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Alexander V. Chernikov
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+setup_networking()
+{
+ jname="$1"
+ lo_dst="$2"
+ epair0="$3"
+ epair1="$4"
+
+ vnet_mkjail ${jname}a ${epair0}a ${epair1}a ${lo_src}
+ # enable link-local IPv6
+ jexec ${jname}a ndp -i ${epair0}a -- -disabled
+ jexec ${jname}a ifconfig ${epair0}a up
+ jexec ${jname}a ndp -i ${epair1}a -- -disabled
+ jexec ${jname}a ifconfig ${epair1}a up
+ jexec ${jname}a ifconfig ${lo_src} up
+
+ vnet_mkjail ${jname}b ${epair0}b ${epair1}b ${lo_dst}
+ jexec ${jname}b ndp -i ${epair0}b -- -disabled
+ jexec ${jname}b ifconfig ${epair0}b up
+ jexec ${jname}b ndp -i ${epair1}b -- -disabled
+ jexec ${jname}b ifconfig ${epair1}b up
+ jexec ${jname}b ifconfig ${lo_dst} up
+
+ # wait for DAD to complete
+ while [ `jexec ${jname}b ifconfig | grep inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+ while [ `jexec ${jname}a ifconfig | grep inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+}
+
+
+atf_test_case "lpm6_test1_success" "cleanup"
+lpm6_test1_success_head()
+{
+
+ atf_set descr 'Test IPv6 LPM for the host routes'
+ atf_set require.user root
+}
+
+lpm6_test1_success_body()
+{
+
+ vnet_init
+
+ net_dst="2001:db8:"
+
+ jname="v6t-lpm6_test1_success"
+
+ epair0=$(vnet_mkepair)
+ epair1=$(vnet_mkepair)
+ lo_dst=$(vnet_mkloopback)
+
+ setup_networking ${jname} ${lo_dst} ${epair0} ${epair1}
+
+ jexec ${jname}b ifconfig ${lo_dst} inet6 ${net_dst}:2:0/128
+ jexec ${jname}b ifconfig ${lo_dst} inet6 ${net_dst}:2:1/128
+
+ # Add routes
+ # A -> towards B via epair0a LL
+ ll=`jexec ${jname}b ifconfig ${epair0}b inet6 | awk '$2~/^fe80:/{print$2}' | awk -F% '{print$1}'`
+ jexec ${jname}a route add -6 -host ${net_dst}:2:0 ${ll}%${epair0}a
+ # A -> towards B via epair1a LL
+ ll=`jexec ${jname}b ifconfig ${epair1}b inet6 | awk '$2~/^fe80:/{print$2}' | awk -F% '{print$1}'`
+ jexec ${jname}a route add -6 -host ${net_dst}:2:1 ${ll}%${epair1}a
+
+ count=20
+ valid_message="${count} packets transmitted, ${count} packets received"
+
+ # Check that ${net_dst}:2:0 goes via epair0
+ atf_check -o match:"${valid_message}" jexec ${jname}a ping -6 -f -nc${count} ${net_dst}:2:0
+ pkt_0=`jexec ${jname}a netstat -Wf link -I ${epair0}a | head | awk '$1!~/^Name/{print$8}'`
+ pkt_1=`jexec ${jname}a netstat -Wf link -I ${epair1}a | head | awk '$1!~/^Name/{print$8}'`
+ if [ ${pkt_0} -le ${count} ]; then
+ echo "LPM failure: 1: ${pkt_0} (should be ${count}) 2: ${pkt_1}"
+ exit 1
+ fi
+
+ # Check that ${net_dst}:2:1 goes via epair1
+ atf_check -o match:"${valid_message}" jexec ${jname}a ping -6 -f -nc${count} ${net_dst}:2:1
+ pkt_0=`jexec ${jname}a netstat -Wf link -I ${epair0}a | head | awk '$1!~/^Name/{print$8}'`
+ pkt_1=`jexec ${jname}a netstat -Wf link -I ${epair1}a | head | awk '$1!~/^Name/{print$8}'`
+ if [ ${pkt_1} -le ${count} ]; then
+ echo "LPM failure: 1: ${pkt_0} 2: ${pkt_1} (should be ${count})"
+ exit 1
+ fi
+
+ echo "RAW BALANCING: 1: ${pkt_0} 2: ${pkt_1}"
+}
+
+lpm6_test1_success_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "lpm6_test2_success" "cleanup"
+lpm6_test2_success_head()
+{
+
+ atf_set descr 'Test IPv6 LPM for /126 and /127'
+ atf_set require.user root
+}
+
+lpm6_test2_success_body()
+{
+
+ vnet_init
+
+ net_dst="2001:db8:"
+
+ jname="v6t-lpm6_test2_success"
+
+ epair0=$(vnet_mkepair)
+ epair1=$(vnet_mkepair)
+ lo_dst=$(vnet_mkloopback)
+
+ setup_networking ${jname} ${lo_dst} ${epair0} ${epair1}
+
+ jexec ${jname}b ifconfig ${lo_dst} inet6 ${net_dst}:2:0/128
+ jexec ${jname}b ifconfig ${lo_dst} inet6 ${net_dst}:2:2/128
+
+ # Add routes
+ # A -> towards B via epair0a LL
+ ll=`jexec ${jname}b ifconfig ${epair0}b inet6 | awk '$2~/^fe80:/{print$2}' | awk -F% '{print$1}'`
+ jexec ${jname}a route add -6 -net ${net_dst}:2:0/126 ${ll}%${epair0}a
+ # A -> towards B via epair1a LL
+ ll=`jexec ${jname}b ifconfig ${epair1}b inet6 | awk '$2~/^fe80:/{print$2}' | awk -F% '{print$1}'`
+ jexec ${jname}a route add -6 -net ${net_dst}:2:0/127 ${ll}%${epair1}a
+
+ count=20
+ valid_message="${count} packets transmitted, ${count} packets received"
+
+ # Check that ${net_dst}:2:0 goes via epair1
+ atf_check -o match:"${valid_message}" jexec ${jname}a ping -6 -f -nc${count} ${net_dst}:2:0
+ pkt_0=`jexec ${jname}a netstat -Wf link -I ${epair0}a | head | awk '$1!~/^Name/{print$8}'`
+ pkt_1=`jexec ${jname}a netstat -Wf link -I ${epair1}a | head | awk '$1!~/^Name/{print$8}'`
+ if [ ${pkt_1} -le ${count} ]; then
+ echo "LPM failure: 1: ${pkt_0} 2: ${pkt_1} (should be ${count})"
+ exit 1
+ fi
+
+ # Check that ${net_dst}:2:2 goes via epair0
+ atf_check -o match:"${valid_message}" jexec ${jname}a ping -6 -f -nc${count} ${net_dst}:2:2
+ pkt_0=`jexec ${jname}a netstat -Wf link -I ${epair0}a | head | awk '$1!~/^Name/{print$8}'`
+ pkt_1=`jexec ${jname}a netstat -Wf link -I ${epair1}a | head | awk '$1!~/^Name/{print$8}'`
+ if [ ${pkt_0} -le ${count} ]; then
+ echo "LPM failure: 1: ${pkt_0} (should be ${count}) 2: ${pkt_1}"
+ exit 1
+ fi
+
+ echo "RAW BALANCING: 1: ${pkt_0} 2: ${pkt_1}"
+}
+
+lpm6_test2_success_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "lpm6_test1_success"
+ atf_add_test_case "lpm6_test2_success"
+}
+
+# end
+
+
diff --git a/tests/sys/netinet6/mld.py b/tests/sys/netinet6/mld.py
new file mode 100644
index 000000000000..17d75b4c26c9
--- /dev/null
+++ b/tests/sys/netinet6/mld.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import socket
+import sys
+import binascii
+
+def main():
+ parser = argparse.ArgumentParser("scapyi386.py",
+ description="IPv6 Ethernet Dest MAC test")
+ parser.add_argument('--sendif', nargs=1,
+ required=True,
+ help='The interface through which the packet will be sent')
+ parser.add_argument('--recvif', nargs=1,
+ required=True,
+ help='The interface on which to check for the packet')
+ parser.add_argument('--src', nargs=1,
+ required=True,
+ help='The source IP address')
+ parser.add_argument('--to', nargs=1,
+ required=True,
+ help='The destination IP address')
+ parser.add_argument('--debug',
+ required=False, action='store_true',
+ help='Enable test debugging')
+ parser.add_argument('--mldraw01',
+ required=False, action='store_true',
+ help='Multicast Listener Query Raw01')
+
+ args = parser.parse_args()
+
+ pkt = None
+ if args.mldraw01:
+ pkt = sp.Ether() / \
+ sp.IPv6(dst="ff02::1", hlim=1, nh=0) / \
+ sp.IPv6ExtHdrHopByHop(options = sp.RouterAlert(value=0)) / \
+ sp.ICMPv6MLQuery()
+ if pkt is None:
+ sys.exit(1)
+ if args.debug:
+ pkt.display()
+ sp.sendp(pkt, iface=args.sendif[0], verbose=False)
+
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netinet6/mld.sh b/tests/sys/netinet6/mld.sh
new file mode 100755
index 000000000000..d98624daedf5
--- /dev/null
+++ b/tests/sys/netinet6/mld.sh
@@ -0,0 +1,128 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+atf_test_case "mldraw01" "cleanup"
+mldraw01_head() {
+
+ atf_set descr 'Test for correct Ethernet Destination MAC address'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+mldraw01_body() {
+
+ ids=65533
+ id=`printf "%x" ${ids}`
+ if [ $$ -gt 65535 ]; then
+ xl=`printf "%x" $(($$ - 65535))`
+ yl="1"
+ else
+ xl=`printf "%x" $$`
+ yl=""
+ fi
+
+ vnet_init
+
+ ip6a="2001:db8:6666:0000:${yl}:${id}:1:${xl}"
+ ip6b="2001:db8:6666:0000:${yl}:${id}:2:${xl}"
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a up
+ ifconfig ${epair}a inet6 ${ip6a}/64
+
+ jname="v6t-${id}-${yl}-${xl}"
+ vnet_mkjail ${jname} ${epair}b
+ jexec ${jname} ifconfig ${epair}b up
+ jexec ${jname} ifconfig ${epair}b inet6 ${ip6b}/64
+
+ # Let IPv6 ND do its thing.
+ #ping6 -q -c 1 ff02::1%${epair}a
+ #ping6 -q -c 1 ${ip6b}
+ sleep 3
+
+ pyname=$(atf_get ident)
+
+ atf_check -s exit:0 $(atf_get_srcdir)/mld.py \
+ --sendif ${epair}a --recvif ${epair}a \
+ --src ${ip6a} --to ${ip6b} \
+ --${pyname}
+}
+
+mldraw01_cleanup() {
+
+ vnet_cleanup
+}
+
+atf_test_case "pr233683" "cleanup"
+pr233683_head() {
+
+ atf_set descr 'Test for PR233683'
+ atf_set require.user root
+}
+
+pr233683_body() {
+ j="mld:pr233683"
+
+ vnet_init
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail ${j}a ${epair}a
+ jexec ${j}a ifconfig ${epair}a inet6 2001:db8::1/64 up
+ sleep 5
+
+ jexec ${j}a ifconfig ${epair}a inet6 2001:db8::1/64
+
+ vnet_mkjail ${j}b ${epair}b
+ jexec ${j}b ifconfig ${epair}b inet6 2001:db8::2/64 up
+
+ # Allow DAD to run
+ sleep 5
+
+ # Debug output. If the bug is present we'd expect to not see a
+ # membership for ff02::1:ff00:1
+ jexec ${j}a ifmcstat -i ${epair}a
+ jexec ${j}a ifconfig ${epair}a
+
+ atf_check -s exit:0 -o ignore \
+ jexec ${j}b ping -6 -c 1 2001:db8::1
+}
+
+pr233683_cleanup() {
+
+ vnet_cleanup
+}
+atf_init_test_cases()
+{
+
+ atf_add_test_case "mldraw01"
+ atf_add_test_case "pr233683"
+}
+
+# end
diff --git a/tests/sys/netinet6/ndp.sh b/tests/sys/netinet6/ndp.sh
new file mode 100755
index 000000000000..bac9764ee3c9
--- /dev/null
+++ b/tests/sys/netinet6/ndp.sh
@@ -0,0 +1,196 @@
+#!/usr/bin/env atf-sh
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2021 Alexander V. Chernikov
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+atf_test_case "ndp_add_gu_success" "cleanup"
+ndp_add_gu_success_head() {
+ atf_set descr 'Test static ndp record addition'
+ atf_set require.user root
+}
+
+ndp_add_gu_success_body() {
+ local epair0 jname
+
+ vnet_init
+
+ jname="v6t-ndp_add_success"
+
+ epair0=$(vnet_mkepair)
+
+ vnet_mkjail ${jname} ${epair0}a
+ jexec ${jname} ndp -i ${epair0}a -- -disabled
+ jexec ${jname} ifconfig ${epair0}a up
+
+ jexec ${jname} ifconfig ${epair0}a inet6 2001:db8::1/64
+
+ # wait for DAD to complete
+ while [ `jexec ${jname} ifconfig | grep inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+
+ atf_check jexec ${jname} ndp -s 2001:db8::2 90:10:00:01:02:03
+
+ t=`jexec ${jname} ndp -an | grep 2001:db8::2 | awk '{print $1, $2, $3, $4}'`
+ if [ "${t}" != "2001:db8::2 90:10:00:01:02:03 ${epair0}a permanent" ]; then
+ atf_fail "Wrong output: ${t}"
+ fi
+ echo "T='${t}'"
+}
+
+ndp_add_gu_success_cleanup() {
+ vnet_cleanup
+}
+
+atf_test_case "ndp_del_gu_success" "cleanup"
+ndp_del_gu_success_head() {
+ atf_set descr 'Test ndp record deletion'
+ atf_set require.user root
+}
+
+ndp_del_gu_success_body() {
+ local epair0 jname
+
+ vnet_init
+
+ jname="v6t-ndp_del_gu_success"
+
+ epair0=$(vnet_mkepair)
+
+ vnet_mkjail ${jname} ${epair0}a
+
+ jexec ${jname} ndp -i ${epair0}a -- -disabled
+ jexec ${jname} ifconfig ${epair0}a up
+
+ jexec ${jname} ifconfig ${epair0}a inet6 2001:db8::1/64
+
+ # wait for DAD to complete
+ while [ `jexec ${jname} ifconfig | grep inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+
+ jexec ${jname} ping -c1 -t1 2001:db8::2
+
+ atf_check -o match:"2001:db8::2 \(2001:db8::2\) deleted" jexec ${jname} ndp -nd 2001:db8::2
+}
+
+ndp_del_gu_success_cleanup() {
+ vnet_cleanup
+}
+
+ndp_if_up()
+{
+ local ifname=$1
+ local jname=$2
+
+ if [ -n "$jname" ]; then
+ jname="jexec ${jname}"
+ fi
+ atf_check ${jname} ifconfig ${ifname} up
+ atf_check ${jname} ifconfig ${ifname} inet6 -ifdisabled
+ while ${jname} ifconfig ${ifname} inet6 | grep tentative; do
+ sleep 0.1
+ done
+}
+
+ndp_if_lladdr()
+{
+ local ifname=$1
+ local jname=$2
+
+ if [ -n "$jname" ]; then
+ jname="jexec ${jname}"
+ fi
+ ${jname} ifconfig ${ifname} inet6 | \
+ awk '/inet6 fe80:/{split($2, addr, "%"); print addr[1]}'
+}
+
+atf_test_case "ndp_slaac_default_route" "cleanup"
+ndp_slaac_default_route_head() {
+ atf_set descr 'Test default route installation via SLAAC'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+ndp_slaac_default_route_body() {
+ local epair0 jname lladdr
+
+ vnet_init
+
+ jname="v6t-ndp_slaac_default_route"
+
+ epair0=$(vnet_mkepair)
+
+ vnet_mkjail ${jname} ${epair0}a
+
+ ndp_if_up ${epair0}a ${jname}
+ ndp_if_up ${epair0}b
+ atf_check jexec ${jname} ifconfig ${epair0}a inet6 accept_rtadv
+
+ # Send an RA advertising a prefix.
+ atf_check -e ignore python3 $(atf_get_srcdir)/ra.py \
+ --sendif ${epair0}b \
+ --dst $(ndp_if_lladdr ${epair0}a ${jname}) \
+ --src $(ndp_if_lladdr ${epair0}b) \
+ --prefix "2001:db8:ffff:1000::" --prefixlen 64
+
+ # Wait for a default router to appear.
+ while [ -z "$(jexec ${jname} ndp -r)" ]; do
+ sleep 0.1
+ done
+ atf_check -o match:"^default[[:space:]]+fe80:" \
+ jexec ${jname} netstat -rn -6
+
+ # Get rid of the default route.
+ jexec ${jname} route -6 flush
+ atf_check -o not-match:"^default[[:space:]]+fe80:" \
+ jexec ${jname} netstat -rn -6
+
+ # Send another RA, make sure that the default route is installed again.
+ atf_check -e ignore python3 $(atf_get_srcdir)/ra.py \
+ --sendif ${epair0}b \
+ --dst $(ndp_if_lladdr ${epair0}a ${jname}) \
+ --src $(ndp_if_lladdr ${epair0}b) \
+ --prefix "2001:db8:ffff:1000::" --prefixlen 64
+ while [ -z "$(jexec ${jname} ndp -r)" ]; do
+ sleep 0.1
+ done
+ atf_check -o match:"^default[[:space:]]+fe80:" \
+ jexec ${jname} netstat -rn -6
+}
+
+ndp_slaac_default_route_cleanup() {
+ vnet_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "ndp_add_gu_success"
+ atf_add_test_case "ndp_del_gu_success"
+ atf_add_test_case "ndp_slaac_default_route"
+}
diff --git a/tests/sys/netinet6/output6.sh b/tests/sys/netinet6/output6.sh
new file mode 100755
index 000000000000..5811e0e5eacc
--- /dev/null
+++ b/tests/sys/netinet6/output6.sh
@@ -0,0 +1,663 @@
+#!/usr/bin/env atf-sh
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Alexander V. Chernikov
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+atf_test_case "output6_tcp_setup_success" "cleanup"
+output6_tcp_setup_success_head()
+{
+
+ atf_set descr 'Test valid IPv6 TCP output'
+ atf_set require.user root
+}
+
+output6_tcp_setup_success_body()
+{
+
+ vnet_init
+
+ net_src="2001:db8:0:0:1::"
+ net_dst="2001:db8:0:0:1::"
+ ip_src="${net_src}1"
+ ip_dst=${net_dst}4242
+ plen=64
+ text="testtesttst"
+ port=4242
+
+ script_name=`dirname $0`/../common/net_receiver.py
+ script_name=`realpath ${script_name}`
+ jname="v6t-output6_tcp_setup_success"
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail ${jname}a ${epair}a
+ jexec ${jname}a ifconfig ${epair}a up
+ jexec ${jname}a ifconfig ${epair}a inet6 ${ip_src}/${plen}
+
+ vnet_mkjail ${jname}b ${epair}b
+ jexec ${jname}b ifconfig ${epair}b up
+
+ jexec ${jname}b ifconfig ${epair}b inet6 ${ip_dst}/${plen}
+
+ # wait for DAD to complete
+ while [ `jexec ${jname}b ifconfig ${epair}b inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+ while [ `jexec ${jname}a ifconfig ${epair}a inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+
+ # run listener
+ args="--family inet6 --ports ${port} --match_str ${text}"
+ echo jexec ${jname}b ${script_name} ${args}
+ jexec ${jname}b ${script_name} --test_name "test_listen_tcp" ${args} &
+ cmd_pid=$!
+
+ # wait for the script init
+ counter=0
+ while [ `jexec ${jname}b sockstat -6qlp ${port} | wc -l` != "1" ]; do
+ sleep 0.01
+ counter=$((counter+1))
+ if [ ${counter} -ge 50 ]; then break; fi
+ done
+ if [ `jexec ${jname}b sockstat -6qlp ${port} | wc -l` != "1" ]; then
+ echo "App setup failed"
+ exit 1
+ fi
+
+ # run sender
+ echo -n "${text}" | jexec ${jname}a nc -N ${ip_dst} ${port}
+ exit_code=$?
+ if [ $exit_code -ne 0 ]; then atf_fail "sender exit code $exit_code" ; fi
+
+ wait ${cmd_pid}
+ exit_code=$?
+ if [ $exit_code -ne 0 ]; then atf_fail "receiver exit code $exit_code" ; fi
+}
+
+output6_tcp_setup_success_cleanup()
+{
+ vnet_cleanup
+}
+
+
+atf_test_case "output6_udp_setup_success" "cleanup"
+output6_udp_setup_success_head()
+{
+
+ atf_set descr 'Test valid IPv6 UDP output'
+ atf_set require.user root
+}
+
+output6_udp_setup_success_body()
+{
+
+ vnet_init
+
+ net_src="2001:db8:0:0:1::"
+ net_dst="2001:db8:0:0:1::"
+ ip_src="${net_src}1"
+ ip_dst=${net_dst}4242
+ plen=64
+ text="testtesttst"
+ port=4242
+
+ script_name=`dirname $0`/../common/net_receiver.py
+ script_name=`realpath ${script_name}`
+ jname="v6t-output6_udp_setup_success"
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail ${jname}a ${epair}a
+ jexec ${jname}a ifconfig ${epair}a up
+ jexec ${jname}a ifconfig ${epair}a inet6 ${ip_src}/${plen}
+
+ vnet_mkjail ${jname}b ${epair}b
+ jexec ${jname}b ifconfig ${epair}b up
+ jexec ${jname}b ifconfig ${epair}b inet6 ${ip_dst}/${plen}
+
+ # wait for DAD to complete
+ while [ `jexec ${jname}b ifconfig ${epair}b inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+ while [ `jexec ${jname}a ifconfig ${epair}a inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+
+ # run listener
+ args="--family inet6 --ports ${port} --match_str ${text}"
+ echo jexec ${jname}b ${script_name} ${args}
+ jexec ${jname}b ${script_name} --test_name "test_listen_udp" ${args} &
+ cmd_pid=$!
+
+ # wait for the script init
+ counter=0
+ while [ `jexec ${jname}b sockstat -6qlp ${port} | wc -l` != "1" ]; do
+ sleep 0.1
+ counterc=$((counter+1))
+ if [ ${counter} -ge 50 ]; then break; fi
+ done
+ if [ `jexec ${jname}b sockstat -6qlp ${port} | wc -l` != "1" ]; then
+ echo "App setup failed"
+ exit 1
+ fi
+
+ # run sender
+ # TODO: switch from nc to some alternative to avoid 1-second delay
+ echo -n "${text}" | jexec ${jname}a nc -uNw1 ${ip_dst} ${port}
+ exit_code=$?
+ if [ $exit_code -ne 0 ]; then atf_fail "sender exit code $exit_code" ; fi
+
+ wait ${cmd_pid}
+ exit_code=$?
+ if [ $exit_code -ne 0 ]; then atf_fail "receiver exit code $exit_code" ; fi
+}
+
+output6_udp_setup_success_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "output6_raw_success" "cleanup"
+output6_raw_success_head()
+{
+
+ atf_set descr 'Test valid IPv6 raw output'
+ atf_set require.user root
+}
+
+output6_raw_success_body()
+{
+
+ vnet_init
+
+ net_src="2001:db8:0:0:1::"
+ net_dst="2001:db8:0:0:1::"
+ ip_src="${net_src}1"
+ ip_dst=${net_dst}4242
+ plen=64
+
+ script_name=`dirname $0`/../common/net_receiver.py
+ script_name=`realpath ${script_name}`
+ jname="v6t-output6_raw_success"
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail ${jname}a ${epair}a
+ jexec ${jname}a ifconfig ${epair}a up
+ jexec ${jname}a ifconfig ${epair}a inet6 ${ip_src}/${plen}
+
+ vnet_mkjail ${jname}b ${epair}b
+ jexec ${jname}b ifconfig ${epair}b up
+
+ jexec ${jname}b ifconfig ${epair}b inet6 ${ip_dst}/${plen}
+
+ # wait for DAD to complete
+ while [ `jexec ${jname}b ifconfig ${epair}b inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+ while [ `jexec ${jname}a ifconfig ${epair}a inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+
+ atf_check -o match:'1 packets transmitted, 1 packets received' jexec ${jname}a ping -6 -nc1 ${ip_dst}
+}
+
+output6_raw_success_cleanup()
+{
+ vnet_cleanup
+}
+
+# Multipath tests are done the following way:
+# epair0/LL
+# jailA lo/GU < > lo/GU jailB
+# epair1/LL
+# jailA has 2 routes towards /64 prefix on jailB loopback, via 2 epairs
+# jailB has 1 route towards /64 prefix on jailA loopback, via epair0
+#
+# jailA initiates connections/sends packets towards IPs on jailB loopback.
+# Script then compares amount of packets sent via epair0 and epair1
+
+mpath_check()
+{
+ if [ `sysctl -iW net.route.multipath | wc -l` != "1" ]; then
+ atf_skip "This test requires ROUTE_MPATH enabled"
+ fi
+}
+
+mpath_enable()
+{
+ jexec $1 sysctl net.route.multipath=1
+ if [ $? != 0 ]; then
+ atf_fail "Setting multipath in jail $1 failed".
+ fi
+}
+
+
+atf_test_case "output6_tcp_flowid_mpath_success" "cleanup"
+output6_tcp_flowid_mpath_success_head()
+{
+
+ atf_set descr 'Test valid IPv6 TCP output flowid generation'
+ atf_set require.user root
+}
+
+output6_tcp_flowid_mpath_success_body()
+{
+ vnet_init
+ mpath_check
+
+ net_src="2001:db8:0:1"
+ net_dst="2001:db8:0:2"
+ ip_src="${net_src}::1"
+ ip_dst="${net_dst}::1"
+ plen=64
+ text="testtesttst"
+
+ script_name=`dirname $0`/../common/net_receiver.py
+ script_name=`realpath ${script_name}`
+ jname="v6t-output6_tcp_flowid_mpath_success"
+
+ epair0=$(vnet_mkepair)
+ epair1=$(vnet_mkepair)
+ lo_src=$(vnet_mkloopback)
+ lo_dst=$(vnet_mkloopback)
+
+ vnet_mkjail ${jname}a ${epair0}a ${epair1}a ${lo_src}
+ mpath_enable ${jname}a
+ jls -N
+ # enable link-local IPv6
+ jexec ${jname}a ndp -i ${epair0}a -- -disabled
+ jexec ${jname}a ifconfig ${epair0}a up
+ jexec ${jname}a ndp -i ${epair1}a -- -disabled
+ jexec ${jname}a ifconfig ${epair1}a up
+ jexec ${jname}a ifconfig ${lo_src} up
+
+ vnet_mkjail ${jname}b ${epair0}b ${epair1}b ${lo_dst}
+ jls -N
+ jexec ${jname}b ndp -i ${epair0}b -- -disabled
+ jexec ${jname}b ifconfig ${epair0}b up
+ jexec ${jname}b ndp -i ${epair1}b -- -disabled
+ jexec ${jname}b ifconfig ${epair1}b up
+ jexec ${jname}b ifconfig ${lo_dst} up
+
+ # DST ips/ports to test
+ ips="d3:c4:eb:40 2b:ff:dd:52 b1:d4:44:0e 41:2c:4d:43 66:4a:b4:be 8b:da:ac:f7 ca:d1:c4:f0 b1:31:da:d7 0c:ac:45:7a 44:9c:ce:71"
+ ports="53540 49743 43067 9131 16734 5150 14379 40292 20634 51302 3387 24387 9282 14275 42103 26881 42461 29520 45714 11096"
+
+ jexec ${jname}a ifconfig ${lo_src} inet6 ${ip_src}/128
+
+ jexec ${jname}b ifconfig ${lo_dst} inet6 ${ip_dst}/128
+ for i in ${ips}; do
+ jexec ${jname}b ifconfig ${lo_dst} inet6 ${net_dst}:${i}/128
+ done
+
+ # wait for DAD to complete
+ while [ `jexec ${jname}b ifconfig | grep inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+ while [ `jexec ${jname}a ifconfig | grep inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+
+ # Add routes
+ # A -> towards B via epair0a LL
+ ll=`jexec ${jname}b ifconfig ${epair0}b inet6 | awk '$2~/^fe80:/{print$2}' | awk -F% '{print$1}'`
+ jexec ${jname}a route add -6 -net ${net_dst}::/${plen} ${ll}%${epair0}a
+ # A -> towards B via epair1a LL
+ ll=`jexec ${jname}b ifconfig ${epair1}b inet6 | awk '$2~/^fe80:/{print$2}' | awk -F% '{print$1}'`
+ jexec ${jname}a route add -6 -net ${net_dst}::/${plen} ${ll}%${epair1}a
+
+ # B towards A via epair0b LL
+ ll=`jexec ${jname}a ifconfig ${epair1}a inet6 | awk '$2~/^fe80:/{print$2}' | awk -F% '{print$1}'`
+ jexec ${jname}b route add -6 -net ${net_src}::/${plen} ${ll}%${epair1}b
+
+ # Base setup verification
+ atf_check -o match:'1 packets transmitted, 1 packets received' jexec ${jname}a ping -6 -c1 ${ip_dst}
+
+ # run listener
+ num_ports=`echo ${ports} | wc -w`
+ num_ips=`echo ${ips} | wc -w`
+ count_examples=$((num_ports*num_ips))
+ listener_ports=`echo ${ports} | tr ' ' '\n' | sort -n | tr '\n' ',' | sed -e 's?,$??'`
+ args="--family inet6 --ports ${listener_ports} --count ${count_examples} --match_str ${text}"
+ echo jexec ${jname}b ${script_name} ${args}
+ jexec ${jname}b ${script_name} --test_name "test_listen_tcp" ${args} &
+ cmd_pid=$!
+
+ # wait for the app init
+ counter=0
+ init=0
+ while [ ${counter} -le 50 ]; do
+ _ports=`jexec ${jname}b sockstat -6ql | awk "\\\$3 == ${cmd_pid} {print \\\$6}"|awk -F: "{print \\\$2}" | sort -n | tr '\n' ','`
+ if [ "${_ports}" = "${listener_ports}," ]; then
+ init=1
+ break;
+ fi
+ done
+ if [ ${init} -eq 0 ]; then
+ jexec ${jname}b sockstat -6ql | awk "\$3 == ${cmd_pid}"
+ echo "App setup failed"
+ exit 1
+ fi
+ echo "App setup done"
+
+ # run sender
+ for _ip in ${ips}; do
+ ip="${net_dst}:${_ip}"
+ for port in ${ports}; do
+ echo -n "${text}" | jexec ${jname}a nc -nN ${ip} ${port}
+ exit_code=$?
+ if [ $exit_code -ne 0 ]; then atf_fail "sender exit code $exit_code" ; fi
+ done
+ done
+
+ wait ${cmd_pid}
+ exit_code=$?
+ if [ $exit_code -ne 0 ]; then atf_fail "receiver exit code $exit_code" ; fi
+
+ pkt_0=`jexec ${jname}a netstat -Wf link -I ${epair0}a | head | awk '$1!~/^Name/{print$8}'`
+ pkt_1=`jexec ${jname}a netstat -Wf link -I ${epair1}a | head | awk '$1!~/^Name/{print$8}'`
+ if [ ${pkt_0} -le 10 ]; then
+ atf_fail "Balancing failure: 1: ${pkt_0} 2: ${pkt_1}"
+ fi
+ if [ ${pkt_1} -le 10 ]; then
+ atf_fail "Balancing failure: 1: ${pkt_0} 2: ${pkt_1}"
+ fi
+ echo "TCP Balancing: 1: ${pkt_0} 2: ${pkt_1}"
+}
+
+output6_tcp_flowid_mpath_success_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "output6_udp_flowid_mpath_success" "cleanup"
+output6_udp_flowid_mpath_success_head()
+{
+
+ atf_set descr 'Test valid IPv6 UDP output flowid generation'
+ atf_set require.user root
+}
+
+output6_udp_flowid_mpath_success_body()
+{
+
+ vnet_init
+ mpath_check
+
+ # Note this test will spawn around ~100 nc processes
+
+ net_src="2001:db8:0:1"
+ net_dst="2001:db8:0:2"
+ ip_src="${net_src}::1"
+ ip_dst="${net_dst}::1"
+ plen=64
+ text="testtesttst"
+
+ script_name=`dirname $0`/../common/net_receiver.py
+ script_name=`realpath ${script_name}`
+ jname="v6t-output6_udp_flowid_mpath_success"
+
+ epair0=$(vnet_mkepair)
+ epair1=$(vnet_mkepair)
+ lo_src=$(vnet_mkloopback)
+ lo_dst=$(vnet_mkloopback)
+
+ vnet_mkjail ${jname}a ${epair0}a ${epair1}a ${lo_src}
+ mpath_enable ${jname}a
+ jls -N
+ # enable link-local IPv6
+ jexec ${jname}a ndp -i ${epair0}a -- -disabled
+ jexec ${jname}a ifconfig ${epair0}a up
+ jexec ${jname}a ndp -i ${epair1}a -- -disabled
+ jexec ${jname}a ifconfig ${epair1}a up
+ jexec ${jname}a ifconfig ${lo_src} up
+
+ vnet_mkjail ${jname}b ${epair0}b ${epair1}b ${lo_dst}
+ jls -N
+ jexec ${jname}b ndp -i ${epair0}b -- -disabled
+ jexec ${jname}b ifconfig ${epair0}b up
+ jexec ${jname}b ndp -i ${epair1}b -- -disabled
+ jexec ${jname}b ifconfig ${epair1}b up
+ jexec ${jname}b ifconfig ${lo_dst} up
+
+ # DST ips/ports to test
+ ips="d3:c4:eb:40 2b:ff:dd:52 b1:d4:44:0e 41:2c:4d:43 66:4a:b4:be 8b:da:ac:f7 ca:d1:c4:f0 b1:31:da:d7 0c:ac:45:7a 44:9c:ce:71"
+ ports="53540 49743 43067 9131 16734 5150 14379 40292 20634 51302 3387 24387 9282 14275 42103 26881 42461 29520 45714 11096"
+
+ jexec ${jname}a ifconfig ${lo_src} inet6 ${ip_src}/128
+
+ jexec ${jname}b ifconfig ${lo_dst} inet6 ${ip_dst}/128
+ for i in ${ips}; do
+ jexec ${jname}b ifconfig ${lo_dst} inet6 ${net_dst}:${i}/128
+ done
+
+
+ # wait for DAD to complete
+ while [ `jexec ${jname}b ifconfig | grep inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+ while [ `jexec ${jname}a ifconfig | grep inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+
+ # Add routes
+ # A -> towards B via epair0a LL
+ ll=`jexec ${jname}b ifconfig ${epair0}b inet6 | awk '$2~/^fe80:/{print$2}' | awk -F% '{print$1}'`
+ jexec ${jname}a route add -6 -net ${net_dst}::/${plen} ${ll}%${epair0}a
+ # A -> towards B via epair1a LL
+ ll=`jexec ${jname}b ifconfig ${epair1}b inet6 | awk '$2~/^fe80:/{print$2}' | awk -F% '{print$1}'`
+ jexec ${jname}a route add -6 -net ${net_dst}::/${plen} ${ll}%${epair1}a
+
+ # B towards A via epair0b LL
+ ll=`jexec ${jname}a ifconfig ${epair1}a inet6 | awk '$2~/^fe80:/{print$2}' | awk -F% '{print$1}'`
+ jexec ${jname}b route add -6 -net ${net_src}::/${plen} ${ll}%${epair1}b
+
+ # Base setup verification
+ atf_check -o match:'1 packets transmitted, 1 packets received' jexec ${jname}a ping -6 -c1 ${ip_dst}
+
+ # run listener
+ num_ports=`echo ${ports} | wc -w`
+ num_ips=`echo ${ips} | wc -w`
+ count_examples=$((num_ports*num_ips))
+ listener_ports=`echo ${ports} | tr ' ' '\n' | sort -n | tr '\n' ',' | sed -e 's?,$??'`
+ args="--family inet6 --ports ${listener_ports} --count ${count_examples} --match_str ${text}"
+ echo jexec ${jname}b ${script_name} ${args}
+ jexec ${jname}b ${script_name} --test_name "test_listen_udp" ${args} &
+ cmd_pid=$!
+
+ # wait for the app init
+ counter=0
+ init=0
+ while [ ${counter} -le 50 ]; do
+ _ports=`jexec ${jname}b sockstat -6ql | awk "\\\$3 == ${cmd_pid} {print \\\$6}"|awk -F: "{print \\\$2}" | sort -n | tr '\n' ','`
+ if [ "${_ports}" = "${listener_ports}," ]; then
+ init=1
+ break;
+ fi
+ done
+ if [ ${init} -eq 0 ]; then
+ jexec ${jname}b sockstat -6ql | awk "\$3 == ${cmd_pid}"
+ echo "App setup failed"
+ exit 1
+ fi
+ echo "App setup done"
+
+ # run sender
+ for _ip in ${ips}; do
+ ip="${net_dst}:${_ip}"
+ for port in ${ports}; do
+ # XXX: switch to something that allows immediate exit
+ echo -n "${text}" | jexec ${jname}a nc -nuNw1 ${ip} ${port} &
+ sleep 0.01
+ done
+ done
+
+ wait ${cmd_pid}
+ exit_code=$?
+ if [ $exit_code -ne 0 ]; then atf_fail "receiver exit code $exit_code" ; fi
+
+ pkt_0=`jexec ${jname}a netstat -Wf link -I ${epair0}a | head | awk '$1!~/^Name/{print$8}'`
+ pkt_1=`jexec ${jname}a netstat -Wf link -I ${epair1}a | head | awk '$1!~/^Name/{print$8}'`
+ if [ ${pkt_0} -le 10 ]; then
+ atf_fail "Balancing failure: 1: ${pkt_0} 2: ${pkt_1}"
+ fi
+ if [ ${pkt_1} -le 10 ]; then
+ atf_fail "Balancing failure: 1: ${pkt_0} 2: ${pkt_1}"
+ fi
+ echo "UDP BALANCING: 1: ${pkt_0} 2: ${pkt_1}"
+}
+
+output6_udp_flowid_mpath_success_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_test_case "output6_raw_flowid_mpath_success" "cleanup"
+output6_raw_flowid_mpath_success_head()
+{
+
+ atf_set descr 'Test valid IPv6 raw output flowid generation'
+ atf_set require.user root
+}
+
+output6_raw_flowid_mpath_success_body()
+{
+
+ vnet_init
+ mpath_check
+
+ net_src="2001:db8:0:1"
+ net_dst="2001:db8:0:2"
+ ip_src="${net_src}::1"
+ ip_dst="${net_dst}::1"
+ plen=64
+ text="testtesttst"
+
+ jname="v6t-output6_raw_flowid_mpath_success"
+
+ epair0=$(vnet_mkepair)
+ epair1=$(vnet_mkepair)
+ lo_src=$(vnet_mkloopback)
+ lo_dst=$(vnet_mkloopback)
+
+ vnet_mkjail ${jname}a ${epair0}a ${epair1}a ${lo_src}
+ mpath_enable ${jname}a
+ jls -N
+ # enable link-local IPv6
+ jexec ${jname}a ndp -i ${epair0}a -- -disabled
+ jexec ${jname}a ifconfig ${epair0}a up
+ jexec ${jname}a ndp -i ${epair1}a -- -disabled
+ jexec ${jname}a ifconfig ${epair1}a up
+ jexec ${jname}a ifconfig ${lo_src} up
+
+ vnet_mkjail ${jname}b ${epair0}b ${epair1}b ${lo_dst}
+ jls -N
+ jexec ${jname}b ndp -i ${epair0}b -- -disabled
+ jexec ${jname}b ifconfig ${epair0}b up
+ jexec ${jname}b ndp -i ${epair1}b -- -disabled
+ jexec ${jname}b ifconfig ${epair1}b up
+ jexec ${jname}b ifconfig ${lo_dst} up
+
+ # DST ips to test
+ ips="9d:33:f2:7f 48:06:9a:0b cf:96:d5:78 76:da:8e:28 d4:66:38:1e ec:43:da:7c bb:f8:93:2f d3:c4:eb:40"
+ ips="${ips} c7:31:0e:ae 8d:ed:35:2e c0:e0:22:31 82:1c:4e:28 c1:00:60:3e 6a:4f:3b:6c 8e:a7:e9:57 2b:ff:dd:52"
+ ips="${ips} 88:44:79:5d 80:62:83:11 c8:e4:17:a6 e7:2a:45:d7 5a:92:0e:04 70:fc:6a:ee ce:24:4c:68 41:2c:4d:43"
+ ips="${ips} 57:2b:5e:a7 f9:e0:69:c6 cb:b9:e6:ed 28:88:5d:fa d6:19:ac:1d dc:de:37:d8 fe:39:55:c7 b1:31:da:d7"
+
+ jexec ${jname}a ifconfig ${lo_src} inet6 ${ip_src}/128
+
+ jexec ${jname}b ifconfig ${lo_dst} inet6 ${ip_dst}/128
+ for i in ${ips}; do
+ jexec ${jname}b ifconfig ${lo_dst} inet6 ${net_dst}:${i}/128
+ done
+
+ # wait for DAD to complete
+ while [ `jexec ${jname}b ifconfig | grep inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+ while [ `jexec ${jname}a ifconfig | grep inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+
+ # Add routes
+ # A -> towards B via epair0a LL
+ ll=`jexec ${jname}b ifconfig ${epair0}b inet6 | awk '$2~/^fe80:/{print$2}' | awk -F% '{print$1}'`
+ jexec ${jname}a route add -6 -net ${net_dst}::/${plen} ${ll}%${epair0}a
+ # A -> towards B via epair1a LL
+ ll=`jexec ${jname}b ifconfig ${epair1}b inet6 | awk '$2~/^fe80:/{print$2}' | awk -F% '{print$1}'`
+ jexec ${jname}a route add -6 -net ${net_dst}::/${plen} ${ll}%${epair1}a
+
+ # B towards A via epair0b LL
+ ll=`jexec ${jname}a ifconfig ${epair1}a inet6 | awk '$2~/^fe80:/{print$2}' | awk -F% '{print$1}'`
+ jexec ${jname}b route add -6 -net ${net_src}::/${plen} ${ll}%${epair1}b
+
+ # Base setup verification
+ atf_check -o match:'1 packets transmitted, 1 packets received' jexec ${jname}a ping -6 -nc1 ${ip_dst}
+
+ # run sender
+ valid_message='1 packets transmitted, 1 packets received'
+ for _ip in ${ips}; do
+ ip="${net_dst}:${_ip}"
+ atf_check -o match:"${valid_message}" jexec ${jname}a ping -6 -nc1 ${ip}
+ done
+
+ pkt_0=`jexec ${jname}a netstat -Wf link -I ${epair0}a | head | awk '$1!~/^Name/{print$8}'`
+ pkt_1=`jexec ${jname}a netstat -Wf link -I ${epair1}a | head | awk '$1!~/^Name/{print$8}'`
+
+ jexec ${jname}a netstat -bWf link -I ${epair0}a
+ jexec ${jname}a netstat -bWf link -I ${epair1}a
+ if [ ${pkt_0} -le 10 ]; then
+ atf_fail "Balancing failure: 1: ${pkt_0} 2: ${pkt_1}"
+ fi
+ if [ ${pkt_1} -le 10 ]; then
+ atf_fail "Balancing failure: 1: ${pkt_0} 2: ${pkt_1}"
+ fi
+ echo "RAW BALANCING: 1: ${pkt_0} 2: ${pkt_1}"
+}
+
+output6_raw_flowid_mpath_success_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "output6_tcp_setup_success"
+ atf_add_test_case "output6_udp_setup_success"
+ atf_add_test_case "output6_raw_success"
+ atf_add_test_case "output6_tcp_flowid_mpath_success"
+ atf_add_test_case "output6_udp_flowid_mpath_success"
+ atf_add_test_case "output6_raw_flowid_mpath_success"
+}
+
+# end
+
+
diff --git a/tests/sys/netinet6/proxy_ndp.sh b/tests/sys/netinet6/proxy_ndp.sh
new file mode 100755
index 000000000000..9dfcb6a42e0f
--- /dev/null
+++ b/tests/sys/netinet6/proxy_ndp.sh
@@ -0,0 +1,221 @@
+#!/usr/bin/env atf-sh
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2022 KUROSAWA Takahiro <takahiro.kurosawa@gmail.com>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+atf_test_case "pndp_add_gu_success" "cleanup"
+pndp_add_gu_success_head() {
+ atf_set descr 'Test proxy ndp record addition'
+ atf_set require.user root
+}
+
+pndp_add_gu_success_body() {
+
+ vnet_init
+
+ jname="v6t-pndp_add_success"
+
+ epair0=$(vnet_mkepair)
+
+ vnet_mkjail ${jname} ${epair0}a
+ jexec ${jname} ndp -i ${epair0}a -- -disabled
+ jexec ${jname} ifconfig ${epair0}a up
+
+ jexec ${jname} ifconfig ${epair0}a inet6 2001:db8::1/64
+ proxy_mac=`jexec ${jname} ifconfig ${epair0}a ether | awk '$1~/ether/{print$2}'`
+
+ # wait for DAD to complete
+ while [ `jexec ${jname} ifconfig | grep inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+
+ atf_check jexec ${jname} ndp -s 2001:db8::2 ${proxy_mac} proxy
+ while [ `jexec ${jname} ifmcstat | grep -c undefined` != "0" ]; do
+ sleep 0.1
+ done
+
+ # checking the output of ndp -an is covered by ndp.sh.
+ # we check the output of ifmcstat output here.
+ t=`jexec ${jname} ifmcstat -i ${epair0}a -f inet6 | grep -A1 'group ff02::1:ff00:2'`
+ atf_check -o match:'mcast-macaddr 33:33:ff:00:00:02' echo $t
+}
+
+pndp_add_gu_success_cleanup() {
+ vnet_cleanup
+}
+
+atf_test_case "pndp_del_gu_success" "cleanup"
+pndp_del_gu_success_head() {
+ atf_set descr 'Test proxy ndp record deletion'
+ atf_set require.user root
+}
+
+pndp_del_gu_success_body() {
+
+ vnet_init
+
+ jname="v6t-pndp_del_gu_success"
+
+ epair0=$(vnet_mkepair)
+
+ vnet_mkjail ${jname} ${epair0}a
+
+ jexec ${jname} ndp -i ${epair0}a -- -disabled
+ jexec ${jname} ifconfig ${epair0}a up
+
+ jexec ${jname} ifconfig ${epair0}a inet6 2001:db8::1/64
+ proxy_mac=`jexec ${jname} ifconfig ${epair0}a ether | awk '$1~/ether/{print$2}'`
+
+ # wait for DAD to complete
+ while [ `jexec ${jname} ifconfig | grep inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+
+ atf_check jexec ${jname} ndp -s 2001:db8::2 ${proxy_mac} proxy
+ while [ `jexec ${jname} ifmcstat | grep -c undefined` != "0" ]; do
+ sleep 0.1
+ done
+ jexec ${jname} ping -c1 -t1 2001:db8::2
+
+ atf_check -o match:"2001:db8::2 \(2001:db8::2\) deleted" jexec ${jname} ndp -nd 2001:db8::2
+ while [ `jexec ${jname} ifmcstat | grep -c undefined` != "0" ]; do
+ sleep 0.1
+ done
+ atf_check \
+ -o not-match:'group ff02::1:ff00:2' \
+ -o not-match:'mcast-macaddr 33:33:ff:00:00:02' \
+ jexec ${jname} ifmcstat -i ${epair0}a -f inet6
+}
+
+pndp_del_gu_success_cleanup() {
+ vnet_cleanup
+}
+
+atf_test_case "pndp_ifdestroy_success" "cleanup"
+pndp_ifdetroy_success_head() {
+ atf_set descr 'Test interface destruction with proxy ndp'
+ atf_set require.user root
+}
+
+pndp_ifdestroy_success_body() {
+
+ vnet_init
+
+ jname="v6t-pndp_ifdestroy_success"
+
+ epair0=$(vnet_mkepair)
+
+ vnet_mkjail ${jname} ${epair0}a
+
+ jexec ${jname} ndp -i ${epair0}a -- -disabled
+ jexec ${jname} ifconfig ${epair0}a up
+
+ jexec ${jname} ifconfig ${epair0}a inet6 2001:db8::1/64
+ proxy_mac=`jexec ${jname} ifconfig ${epair0}a ether | awk '$1~/ether/{print$2}'`
+
+ # wait for DAD to complete
+ while [ `jexec ${jname} ifconfig | grep inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+
+ atf_check jexec ${jname} ndp -s 2001:db8::2 ${proxy_mac} proxy
+ while [ `jexec ${jname} ifmcstat | grep -c undefined` != "0" ]; do
+ sleep 0.1
+ done
+
+ atf_check jexec ${jname} ifconfig ${epair0}a destroy
+}
+
+pndp_ifdestroy_success_cleanup() {
+ vnet_cleanup
+}
+
+atf_test_case "pndp_neighbor_advert" "cleanup"
+pndp_neighbor_advert_head() {
+ atf_set descr 'Test Neighbor Advertisement for proxy ndp'
+ atf_set require.user root
+}
+
+pndp_neighbor_advert_body() {
+
+ vnet_init
+
+ jname_a="v6t-pndp_neighbor_advert_a" # NA sender (w/proxy ndp entry)
+ jname_b="v6t-pndp_neighbor_advert_b" # NA receiver (checker)
+ proxy_addr="2001:db8::aaaa"
+
+ epair0=$(vnet_mkepair)
+
+ vnet_mkjail ${jname_a} ${epair0}a
+ jexec ${jname_a} ndp -i ${epair0}a -- -disabled
+ jexec ${jname_a} ifconfig ${epair0}a up
+ jexec ${jname_a} ifconfig ${epair0}a inet6 2001:db8::1/64
+ proxy_mac=`jexec ${jname_a} ifconfig ${epair0}a ether | awk '$1~/ether/{print$2}'`
+ # wait for DAD to complete
+ while [ `jexec ${jname_a} ifconfig | grep inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+ atf_check jexec ${jname_a} ndp -s ${proxy_addr} ${proxy_mac} proxy
+ while [ `jexec ${jname_a} ifmcstat | grep -c undefined` != "0" ]; do
+ sleep 0.1
+ done
+
+ vnet_mkjail ${jname_b} ${epair0}b
+ jexec ${jname_b} ndp -i ${epair0}b -- -disabled
+ jexec ${jname_b} ifconfig ${epair0}b up
+ jexec ${jname_b} ifconfig ${epair0}b inet6 2001:db8::2/64
+ # wait for DAD to complete
+ while [ `jexec ${jname_b} ifconfig | grep inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+
+ jexec ${jname_b} ndp -nc
+ # jname_b sends a NS before ICMPv6 Echo Request for the proxy address.
+ # jname_a responds with a NA resolving the proxy address.
+ # Then there must be a NDP entry of the proxy address in jname_b.
+ jexec ${jname_b} ping -c1 -t1 ${proxy_addr}
+ atf_check -o match:"${proxy_addr} +${proxy_mac} +${epair0}b" \
+ jexec ${jname_b} ndp -an
+}
+
+pndp_neighbor_advert_cleanup() {
+ vnet_cleanup
+}
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case "pndp_add_gu_success"
+ atf_add_test_case "pndp_del_gu_success"
+ atf_add_test_case "pndp_ifdestroy_success"
+ atf_add_test_case "pndp_neighbor_advert"
+}
+
+# end
+
diff --git a/tests/sys/netinet6/ra.py b/tests/sys/netinet6/ra.py
new file mode 100644
index 000000000000..44814418da48
--- /dev/null
+++ b/tests/sys/netinet6/ra.py
@@ -0,0 +1,38 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 Klara, Inc.
+#
+
+import argparse
+import scapy.all as sp
+import sys
+
+#
+# Emit a router advertisement with the specified prefix.
+#
+def main():
+ parser = argparse.ArgumentParser("ra.py",
+ description="Emits Router Advertisement packets")
+ parser.add_argument('--sendif', nargs=1, required=True,
+ help='The interface through which the packet will be sent')
+ parser.add_argument('--src', nargs=1, required=True,
+ help='The source IP address')
+ parser.add_argument('--dst', nargs=1, required=True,
+ help='The destination IP address')
+ parser.add_argument('--prefix', nargs=1, required=True,
+ help='The prefix to be advertised')
+ parser.add_argument('--prefixlen', nargs=1, required=True, type=int,
+ help='The prefix length to be advertised')
+
+ args = parser.parse_args()
+ pkt = sp.Ether() / \
+ sp.IPv6(src=args.src, dst=args.dst) / \
+ sp.ICMPv6ND_RA(chlim=64) / \
+ sp.ICMPv6NDOptPrefixInfo(prefix=args.prefix, prefixlen=args.prefixlen)
+
+ sp.sendp(pkt, iface=args.sendif[0], verbose=False)
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netinet6/redirect.py b/tests/sys/netinet6/redirect.py
new file mode 100644
index 000000000000..bdaf32405210
--- /dev/null
+++ b/tests/sys/netinet6/redirect.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python
+# -
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Alexander V. Chernikov
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sc
+import socket
+import sys
+import fcntl
+import struct
+
+
+def parse_args():
+ parser = argparse.ArgumentParser(description='ICMPv6 redirect generator')
+ parser.add_argument('--smac', type=str, required=True,
+ help='eth source mac')
+ parser.add_argument('--dmac', type=str, required=True,
+ help='eth dest mac')
+ parser.add_argument('--sip', type=str, required=True,
+ help='remote router ll source ip')
+ parser.add_argument('--dip', type=str, required=True,
+ help='local router ip')
+ parser.add_argument('--iface', type=str, required=True,
+ help='ifname to send packet to')
+ parser.add_argument('--route', type=str, required=True,
+ help='destination IP to redirect')
+ parser.add_argument('--gw', type=str, required=True,
+ help='redirect GW')
+ return parser.parse_args()
+
+
+def construct_icmp6_redirect(smac, dmac, sip, dip, route_dst, route_gw):
+ e = sc.Ether(src=smac, dst=dmac)
+ l3 = sc.IPv6(src=sip, dst=dip)
+ icmp6 = sc.ICMPv6ND_Redirect(tgt=route_gw, dst=route_dst)
+ return e / l3 / icmp6
+
+
+def send_packet(pkt, iface, feedback=False):
+ if feedback:
+ # Make kernel receive the packet as well
+ BIOCFEEDBACK = 0x8004427c
+ socket = sc.conf.L2socket(iface=args.iface)
+ fcntl.ioctl(socket.ins, BIOCFEEDBACK, struct.pack('I', 1))
+ sc.sendp(pkt, socket=socket, verbose=True)
+ else:
+ sc.sendp(pkt, iface=iface, verbose=False)
+
+
+def main():
+ args = parse_args()
+ pkt = construct_icmp6_redirect(args.smac, args.dmac, args.sip, args.dip,
+ args.route, args.gw)
+ send_packet(pkt, args.iface)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netinet6/redirect.sh b/tests/sys/netinet6/redirect.sh
new file mode 100644
index 000000000000..40874f8c9b6d
--- /dev/null
+++ b/tests/sys/netinet6/redirect.sh
@@ -0,0 +1,121 @@
+#!/usr/bin/env atf-sh
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Alexander V. Chernikov
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+atf_test_case "valid_redirect" "cleanup"
+valid_redirect_head() {
+
+ atf_set descr 'Test valid IPv6 redirect'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+valid_redirect_body() {
+
+ ids=65533
+ id=`printf "%x" ${ids}`
+ if [ $$ -gt 65535 ]; then
+ xl=`printf "%x" $(($$ - 65535))`
+ yl="1"
+ else
+ xl=`printf "%x" $$`
+ yl=""
+ fi
+
+ vnet_init
+
+ ip6a="2001:db8:6666:0000:${yl}:${id}:1:${xl}"
+ ip6b="2001:db8:6666:0000:${yl}:${id}:2:${xl}"
+
+ net6="2001:db8:6667::/64"
+ dst_addr6=`echo ${net6} | awk -F/ '{printf"%s4242", $1}'`
+ new_rtr_ll_ip="fe80::5555"
+
+ # remote_rtr
+ remote_rtr_ll_ip="fe80::4242"
+ remote_rtr_mac="00:00:5E:00:53:42"
+
+ script_name="redirect.py"
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a up
+ ifconfig ${epair}a inet6 ${ip6a}/64
+
+ jname="v6t-${id}-${yl}-${xl}"
+ vnet_mkjail ${jname} ${epair}b
+ jexec ${jname} ifconfig ${epair}b up
+ jexec ${jname} ifconfig ${epair}b inet6 ${ip6b}/64
+
+ # Setup static entry for the remote router
+ jexec ${jname} ndp -s ${remote_rtr_ll_ip}%${epair}b ${remote_rtr_mac}
+ # setup prefix reachable via router
+ jexec ${jname} route add -6 -net ${net6} ${remote_rtr_ll_ip}%${epair}b
+
+ local_ll_ip=`jexec ${jname} ifconfig ${epair}b inet6 | awk '$1 ~ /inet6/&&$2~/^fe80/ {print$2}'|awk -F% '{print$1}'`
+ local_ll_mac=`jexec ${jname} ifconfig ${epair}b ether | awk '$1~/ether/{print$2}'`
+
+ # wait for DAD to complete
+ while [ `ifconfig ${epair}a inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+ while [ `jexec ${jname} ifconfig ${epair}b inet6 | grep -c tentative` != "0" ]; do
+ sleep 0.1
+ done
+
+ # enable ND debugging in the target jail to ease catching errors
+ jexec ${jname} sysctl net.inet6.icmp6.nd6_debug=1
+
+ # echo "LOCAL: ${local_ll_ip} ${local_ll_mac}"
+ # echo "REMOTE: ${remote_rtr_ll_ip} ${remote_rtr_mac}"
+
+ atf_check -s exit:0 $(atf_get_srcdir)/${script_name} \
+ --smac ${remote_rtr_mac} --dmac ${local_ll_mac} \
+ --sip ${remote_rtr_ll_ip} --dip ${local_ll_ip} \
+ --route ${dst_addr6} --gw ${new_rtr_ll_ip} \
+ --iface ${epair}a
+
+ # Verify redirect got installed
+ atf_check -o match:"destination: ${dst_addr6}\$" jexec ${jname} route -n get -6 ${dst_addr6}
+ atf_check -o match:'flags: <UP,GATEWAY,HOST,DYNAMIC,DONE>' jexec ${jname} route -n get -6 ${dst_addr6}
+}
+
+valid_redirect_cleanup() {
+
+ vnet_cleanup
+}
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case "valid_redirect"
+}
+
+# end
+
diff --git a/tests/sys/netinet6/scapyi386.py b/tests/sys/netinet6/scapyi386.py
new file mode 100644
index 000000000000..01d71b3a5146
--- /dev/null
+++ b/tests/sys/netinet6/scapyi386.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import socket
+import sys
+
+def main():
+ parser = argparse.ArgumentParser("scapyi386.py",
+ description="IPv6 Ethernet Dest MAC test")
+ parser.add_argument('--sendif', nargs=1,
+ required=True,
+ help='The interface through which the packet will be sent')
+ parser.add_argument('--recvif', nargs=1,
+ required=True,
+ help='The interface on which to check for the packet')
+ parser.add_argument('--src', nargs=1,
+ required=True,
+ help='The source IP address')
+ parser.add_argument('--to', nargs=1,
+ required=True,
+ help='The destination IP address')
+ parser.add_argument('--debug',
+ required=False, action='store_true',
+ help='Enable test debugging')
+
+ args = parser.parse_args()
+
+ ########################################################################
+ #
+ # A test case to check that IPv6 packets are sent with a proper
+ # (resolved) Ethernet Destination MAC address instead of the BCAST one.
+ # This was needed as test cases did not work properly on i386 due to a
+ # scapy BPF parsing bug. (See PR 239380 and duplicates).
+ #
+ bcmac = sp.Ether(dst="ff:ff:ff:ff:ff:ff").dst
+ data = "6" * 88
+ pkt = sp.Ether() / \
+ sp.IPv6(src=args.src[0], dst=args.to[0]) / \
+ sp.UDP(dport=3456, sport=6543) / \
+ data
+ sp.sendp(pkt, iface=args.sendif[0], verbose=False)
+
+ eth = pkt.getlayer(sp.Ether)
+ if eth is None:
+ print("No Ether in packet")
+ pkt.display()
+ sys.exit(1)
+ if eth.dst == bcmac:
+ print("Broadcast dMAC on packet")
+ eth.display()
+ sys.exit(1)
+
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netinet6/scapyi386.sh b/tests/sys/netinet6/scapyi386.sh
new file mode 100755
index 000000000000..2d91f25dd01e
--- /dev/null
+++ b/tests/sys/netinet6/scapyi386.sh
@@ -0,0 +1,88 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Netflix, Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/../common/vnet.subr
+
+atf_test_case "scapyi386" "cleanup"
+scapyi386_head() {
+
+ atf_set descr 'Test for correct Ethernet Destination MAC address'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+scapyi386_body() {
+
+ ids=65533
+ id=`printf "%x" ${ids}`
+ if [ $$ -gt 65535 ]; then
+ xl=`printf "%x" $(($$ - 65535))`
+ yl="1"
+ else
+ xl=`printf "%x" $$`
+ yl=""
+ fi
+
+ vnet_init
+
+ ip6a="2001:db8:6666:0000:${yl}:${id}:1:${xl}"
+ ip6b="2001:db8:6666:0000:${yl}:${id}:2:${xl}"
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a up
+ ifconfig ${epair}a inet6 ${ip6a}/64
+
+ jname="v6t-${id}-${yl}-${xl}"
+ vnet_mkjail ${jname} ${epair}b
+ jexec ${jname} ifconfig ${epair}b up
+ jexec ${jname} ifconfig ${epair}b inet6 ${ip6b}/64
+
+ # Let IPv6 ND do its thing.
+ #ping6 -q -c 1 ff02::1%${epair}a
+ #ping6 -q -c 1 ${ip6b}
+ sleep 3
+
+ pyname=$(atf_get ident)
+ pyname=${pyname%*_[0-9]}
+
+ atf_check -s exit:0 $(atf_get_srcdir)/${pyname}.py \
+ --sendif ${epair}a --recvif ${epair}a \
+ --src ${ip6a} --to ${ip6b}
+}
+
+scapyi386_cleanup() {
+
+ vnet_cleanup
+}
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case "scapyi386"
+}
+
+# end
diff --git a/tests/sys/netinet6/test_ip6_output.py b/tests/sys/netinet6/test_ip6_output.py
new file mode 100644
index 000000000000..fc821606a726
--- /dev/null
+++ b/tests/sys/netinet6/test_ip6_output.py
@@ -0,0 +1,540 @@
+import errno
+import ipaddress
+import socket
+import struct
+import time
+from ctypes import c_byte
+from ctypes import c_uint
+from ctypes import Structure
+
+import pytest
+from atf_python.sys.net.rtsock import SaHelper
+from atf_python.sys.net.tools import ToolsHelper
+from atf_python.sys.net.vnet import run_cmd
+from atf_python.sys.net.vnet import SingleVnetTestTemplate
+from atf_python.sys.net.vnet import VnetTestTemplate
+
+
+class In6Pktinfo(Structure):
+ _fields_ = [
+ ("ipi6_addr", c_byte * 16),
+ ("ipi6_ifindex", c_uint),
+ ]
+
+
+class VerboseSocketServer:
+ def __init__(self, ip: str, port: int, ifname: str = None):
+ self.ip = ip
+ self.port = port
+
+ s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+ s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_RECVPKTINFO, 1)
+ addr = ipaddress.ip_address(ip)
+ if addr.is_link_local and ifname:
+ ifindex = socket.if_nametoindex(ifname)
+ addr_tuple = (ip, port, 0, ifindex)
+ elif addr.is_multicast and ifname:
+ ifindex = socket.if_nametoindex(ifname)
+ mreq = socket.inet_pton(socket.AF_INET6, ip) + struct.pack("I", ifindex)
+ s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)
+ print("## JOINED group {} % {}".format(ip, ifname))
+ addr_tuple = ("::", port, 0, ifindex)
+ else:
+ addr_tuple = (ip, port, 0, 0)
+ print("## Listening on [{}]:{}".format(addr_tuple[0], port))
+ s.bind(addr_tuple)
+ self.socket = s
+
+ def recv(self):
+ # data = self.socket.recv(4096)
+ # print("RX: " + data)
+ data, ancdata, msg_flags, address = self.socket.recvmsg(4096, 128)
+ # Assume ancdata has just 1 item
+ info = In6Pktinfo.from_buffer_copy(ancdata[0][2])
+ dst_ip = socket.inet_ntop(socket.AF_INET6, info.ipi6_addr)
+ dst_iface = socket.if_indextoname(info.ipi6_ifindex)
+
+ tx_obj = {
+ "data": data,
+ "src_ip": address[0],
+ "dst_ip": dst_ip,
+ "dst_iface": dst_iface,
+ }
+ return tx_obj
+
+
+class BaseTestIP6Ouput(VnetTestTemplate):
+ TOPOLOGY = {
+ "vnet1": {"ifaces": ["if1", "if2", "if3"]},
+ "vnet2": {"ifaces": ["if1", "if2", "if3"]},
+ "if1": {"prefixes6": [("2001:db8:a::1/64", "2001:db8:a::2/64")]},
+ "if2": {"prefixes6": [("2001:db8:b::1/64", "2001:db8:b::2/64")]},
+ "if3": {"prefixes6": [("2001:db8:c::1/64", "2001:db8:c::2/64")]},
+ }
+ DEFAULT_PORT = 45365
+
+ def _vnet2_handler(self, vnet, ip: str, os_ifname: str = None):
+ """Generic listener that sends first received packet with metadata
+ back to the sender via pipw
+ """
+ ll_data = ToolsHelper.get_linklocals()
+ # Start listener
+ ss = VerboseSocketServer(ip, self.DEFAULT_PORT, os_ifname)
+ vnet.pipe.send(ll_data)
+
+ tx_obj = ss.recv()
+ tx_obj["dst_iface_alias"] = vnet.iface_map[tx_obj["dst_iface"]].alias
+ vnet.pipe.send(tx_obj)
+
+
+class TestIP6Output(BaseTestIP6Ouput):
+ def vnet2_handler(self, vnet):
+ ip = str(vnet.iface_alias_map["if2"].first_ipv6.ip)
+ self._vnet2_handler(vnet, ip, None)
+
+ @pytest.mark.require_user("root")
+ def test_output6_base(self):
+ """Tests simple UDP output"""
+ second_vnet = self.vnet_map["vnet2"]
+
+ # Pick target on if2 vnet2's end
+ ifaddr = ipaddress.ip_interface(self.TOPOLOGY["if2"]["prefixes6"][0][1])
+ ip = str(ifaddr.ip)
+
+ s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+ data = bytes("AAAA", "utf-8")
+ print("## TX packet to {},{}".format(ip, self.DEFAULT_PORT))
+
+ # Wait for the child to become ready
+ self.wait_object(second_vnet.pipe)
+ s.sendto(data, (ip, self.DEFAULT_PORT))
+
+ # Wait for the received object
+ rx_obj = self.wait_object(second_vnet.pipe)
+ assert rx_obj["dst_ip"] == ip
+ assert rx_obj["dst_iface_alias"] == "if2"
+
+ @pytest.mark.require_user("root")
+ def test_output6_nhop(self):
+ """Tests UDP output with custom nhop set"""
+ second_vnet = self.vnet_map["vnet2"]
+
+ # Pick target on if2 vnet2's end
+ ifaddr = ipaddress.ip_interface(self.TOPOLOGY["if2"]["prefixes6"][0][1])
+ ip_dst = str(ifaddr.ip)
+ # Pick nexthop on if1
+ ifaddr = ipaddress.ip_interface(self.TOPOLOGY["if1"]["prefixes6"][0][1])
+ ip_next = str(ifaddr.ip)
+ sin6_next = SaHelper.ip6_sa(ip_next, 0)
+
+ s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, 0)
+ s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_NEXTHOP, sin6_next)
+
+ # Wait for the child to become ready
+ self.wait_object(second_vnet.pipe)
+ data = bytes("AAAA", "utf-8")
+ s.sendto(data, (ip_dst, self.DEFAULT_PORT))
+
+ # Wait for the received object
+ rx_obj = self.wait_object(second_vnet.pipe)
+ assert rx_obj["dst_ip"] == ip_dst
+ assert rx_obj["dst_iface_alias"] == "if1"
+
+ @pytest.mark.parametrize(
+ "params",
+ [
+ # esrc: src-ip, if: src-interface, esrc: expected-src,
+ # eif: expected-rx-interface
+ pytest.param({"esrc": "2001:db8:b::1", "eif": "if2"}, id="empty"),
+ pytest.param(
+ {"src": "2001:db8:c::1", "esrc": "2001:db8:c::1", "eif": "if2"},
+ id="iponly1",
+ ),
+ pytest.param(
+ {
+ "src": "2001:db8:c::1",
+ "if": "if3",
+ "ex": errno.EHOSTUNREACH,
+ },
+ id="ipandif",
+ ),
+ pytest.param(
+ {
+ "src": "2001:db8:c::aaaa",
+ "ex": errno.EADDRNOTAVAIL,
+ },
+ id="nolocalip",
+ ),
+ pytest.param(
+ {"if": "if2", "src": "2001:db8:b::1", "eif": "if2"}, id="ifsame"
+ ),
+ ],
+ )
+ @pytest.mark.require_user("root")
+ def test_output6_pktinfo(self, params):
+ """Tests simple UDP output"""
+ second_vnet = self.vnet_map["vnet2"]
+ vnet = self.vnet
+
+ # Pick target on if2 vnet2's end
+ ifaddr = ipaddress.ip_interface(self.TOPOLOGY["if2"]["prefixes6"][0][1])
+ dst_ip = str(ifaddr.ip)
+
+ src_ip = params.get("src", "")
+ src_ifname = params.get("if", "")
+ expected_ip = params.get("esrc", "")
+ expected_ifname = params.get("eif", "")
+ errno = params.get("ex", 0)
+
+ pktinfo = In6Pktinfo()
+ if src_ip:
+ for i, b in enumerate(socket.inet_pton(socket.AF_INET6, src_ip)):
+ pktinfo.ipi6_addr[i] = b
+ if src_ifname:
+ os_ifname = vnet.iface_alias_map[src_ifname].name
+ pktinfo.ipi6_ifindex = socket.if_nametoindex(os_ifname)
+
+ # Wait for the child to become ready
+ self.wait_object(second_vnet.pipe)
+ data = bytes("AAAA", "utf-8")
+
+ s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, 0)
+ try:
+ s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_PKTINFO, bytes(pktinfo))
+ aux = (socket.IPPROTO_IPV6, socket.IPV6_PKTINFO, bytes(pktinfo))
+ s.sendto(data, (dst_ip, self.DEFAULT_PORT))
+ except OSError as e:
+ if not errno:
+ raise
+ assert e.errno == errno
+ print("Correctly raised {}".format(e))
+ return
+
+ # Wait for the received object
+ rx_obj = self.wait_object(second_vnet.pipe)
+
+ assert rx_obj["dst_ip"] == dst_ip
+ if expected_ip:
+ assert rx_obj["src_ip"] == expected_ip
+ if expected_ifname:
+ assert rx_obj["dst_iface_alias"] == expected_ifname
+
+
+class TestIP6OutputLL(BaseTestIP6Ouput):
+ def vnet2_handler(self, vnet):
+ """Generic listener that sends first received packet with metadata
+ back to the sender via pipw
+ """
+ os_ifname = vnet.iface_alias_map["if2"].name
+ ll_data = ToolsHelper.get_linklocals()
+ ll_ip, _ = ll_data[os_ifname][0]
+ self._vnet2_handler(vnet, ll_ip, os_ifname)
+
+ @pytest.mark.require_user("root")
+ def test_output6_linklocal(self):
+ """Tests simple UDP output"""
+ second_vnet = self.vnet_map["vnet2"]
+
+ # Wait for the child to become ready
+ ll_data = self.wait_object(second_vnet.pipe)
+
+ # Pick LL address on if2 vnet2's end
+ ip, _ = ll_data[second_vnet.iface_alias_map["if2"].name][0]
+ # Get local interface scope
+ os_ifname = self.vnet.iface_alias_map["if2"].name
+ scopeid = socket.if_nametoindex(os_ifname)
+
+ s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+ data = bytes("AAAA", "utf-8")
+ target = (ip, self.DEFAULT_PORT, 0, scopeid)
+ print("## TX packet to {}%{},{}".format(ip, scopeid, target[1]))
+
+ s.sendto(data, target)
+
+ # Wait for the received object
+ rx_obj = self.wait_object(second_vnet.pipe)
+ assert rx_obj["dst_ip"] == ip
+ assert rx_obj["dst_iface_alias"] == "if2"
+
+
+class TestIP6OutputNhopLL(BaseTestIP6Ouput):
+ def vnet2_handler(self, vnet):
+ """Generic listener that sends first received packet with metadata
+ back to the sender via pipw
+ """
+ ip = str(vnet.iface_alias_map["if2"].first_ipv6.ip)
+ self._vnet2_handler(vnet, ip, None)
+
+ @pytest.mark.require_user("root")
+ def test_output6_nhop_linklocal(self):
+ """Tests UDP output with custom link-local nhop set"""
+ second_vnet = self.vnet_map["vnet2"]
+
+ # Wait for the child to become ready
+ ll_data = self.wait_object(second_vnet.pipe)
+
+ # Pick target on if2 vnet2's end
+ ifaddr = ipaddress.ip_interface(self.TOPOLOGY["if2"]["prefixes6"][0][1])
+ ip_dst = str(ifaddr.ip)
+ # Pick nexthop on if1
+ ip_next, _ = ll_data[second_vnet.iface_alias_map["if1"].name][0]
+ # Get local interfaces
+ os_ifname = self.vnet.iface_alias_map["if1"].name
+ scopeid = socket.if_nametoindex(os_ifname)
+ sin6_next = SaHelper.ip6_sa(ip_next, scopeid)
+
+ s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, 0)
+ s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_NEXTHOP, sin6_next)
+
+ data = bytes("AAAA", "utf-8")
+ s.sendto(data, (ip_dst, self.DEFAULT_PORT))
+
+ # Wait for the received object
+ rx_obj = self.wait_object(second_vnet.pipe)
+ assert rx_obj["dst_ip"] == ip_dst
+ assert rx_obj["dst_iface_alias"] == "if1"
+
+
+class TestIP6OutputScope(BaseTestIP6Ouput):
+ def vnet2_handler(self, vnet):
+ """Generic listener that sends first received packet with metadata
+ back to the sender via pipw
+ """
+ bind_ip, bind_ifp = self.wait_object(vnet.pipe)
+ if bind_ip is None:
+ os_ifname = vnet.iface_alias_map[bind_ifp].name
+ ll_data = ToolsHelper.get_linklocals()
+ bind_ip, _ = ll_data[os_ifname][0]
+ if bind_ifp is not None:
+ bind_ifp = vnet.iface_alias_map[bind_ifp].name
+ print("## BIND {}%{}".format(bind_ip, bind_ifp))
+ self._vnet2_handler(vnet, bind_ip, bind_ifp)
+
+ @pytest.mark.parametrize(
+ "params",
+ [
+ # sif/dif: source/destination interface (for link-local addr)
+ # sip/dip: source/destination ip (for non-LL addr)
+ # ex: OSError errno that sendto() must raise
+ pytest.param({"sif": "if2", "dif": "if2"}, id="same"),
+ pytest.param(
+ {
+ "sif": "if1",
+ "dif": "if2",
+ "ex": errno.EHOSTUNREACH,
+ },
+ id="ll_differentif1",
+ ),
+ pytest.param(
+ {
+ "sif": "if1",
+ "dip": "2001:db8:b::2",
+ "ex": errno.EHOSTUNREACH,
+ },
+ id="ll_differentif2",
+ ),
+ pytest.param(
+ {
+ "sip": "2001:db8:a::1",
+ "dif": "if2",
+ },
+ id="gu_to_ll",
+ ),
+ ],
+ )
+ @pytest.mark.require_user("root")
+ def test_output6_linklocal_scope(self, params):
+ """Tests simple UDP output"""
+ second_vnet = self.vnet_map["vnet2"]
+
+ src_ifp = params.get("sif")
+ src_ip = params.get("sip")
+ dst_ifp = params.get("dif")
+ dst_ip = params.get("dip")
+ errno = params.get("ex", 0)
+
+ # Sent ifp/IP to bind on
+ second_vnet = self.vnet_map["vnet2"]
+ second_vnet.pipe.send((dst_ip, dst_ifp))
+
+ # Wait for the child to become ready
+ ll_data = self.wait_object(second_vnet.pipe)
+
+ if dst_ip is None:
+ # Pick LL address on dst_ifp vnet2's end
+ dst_ip, _ = ll_data[second_vnet.iface_alias_map[dst_ifp].name][0]
+ # Get local interface scope
+ os_ifname = self.vnet.iface_alias_map[dst_ifp].name
+ scopeid = socket.if_nametoindex(os_ifname)
+ target = (dst_ip, self.DEFAULT_PORT, 0, scopeid)
+ else:
+ target = (dst_ip, self.DEFAULT_PORT, 0, 0)
+
+ # Bind
+ if src_ip is None:
+ ll_data = ToolsHelper.get_linklocals()
+ os_ifname = self.vnet.iface_alias_map[src_ifp].name
+ src_ip, _ = ll_data[os_ifname][0]
+ scopeid = socket.if_nametoindex(os_ifname)
+ src = (src_ip, self.DEFAULT_PORT, 0, scopeid)
+ else:
+ src = (src_ip, self.DEFAULT_PORT, 0, 0)
+
+ s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+ s.bind(src)
+ data = bytes("AAAA", "utf-8")
+ print("## TX packet {} -> {}".format(src, target))
+
+ try:
+ s.sendto(data, target)
+ except OSError as e:
+ if not errno:
+ raise
+ assert e.errno == errno
+ print("Correctly raised {}".format(e))
+ return
+
+ # Wait for the received object
+ rx_obj = self.wait_object(second_vnet.pipe)
+ assert rx_obj["dst_ip"] == dst_ip
+ assert rx_obj["src_ip"] == src_ip
+ # assert rx_obj["dst_iface_alias"] == "if2"
+
+
+class TestIP6OutputMulticast(BaseTestIP6Ouput):
+ def vnet2_handler(self, vnet):
+ group = self.wait_object(vnet.pipe)
+ os_ifname = vnet.iface_alias_map["if2"].name
+ self._vnet2_handler(vnet, group, os_ifname)
+
+ @pytest.mark.parametrize("group_scope", ["ff02", "ff05", "ff08", "ff0e"])
+ @pytest.mark.require_user("root")
+ def test_output6_multicast(self, group_scope):
+ """Tests simple UDP output"""
+ second_vnet = self.vnet_map["vnet2"]
+
+ group = "{}::3456".format(group_scope)
+ second_vnet.pipe.send(group)
+
+ # Pick target on if2 vnet2's end
+ ip = group
+ os_ifname = self.vnet.iface_alias_map["if2"].name
+ ifindex = socket.if_nametoindex(os_ifname)
+ optval = struct.pack("I", ifindex)
+
+ s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+ s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, optval)
+
+ data = bytes("AAAA", "utf-8")
+
+ # Wait for the child to become ready
+ self.wait_object(second_vnet.pipe)
+
+ print("## TX packet to {},{}".format(ip, self.DEFAULT_PORT))
+ s.sendto(data, (ip, self.DEFAULT_PORT))
+
+ # Wait for the received object
+ rx_obj = self.wait_object(second_vnet.pipe)
+ assert rx_obj["dst_ip"] == ip
+ assert rx_obj["dst_iface_alias"] == "if2"
+
+
+class TestIP6OutputLoopback(SingleVnetTestTemplate):
+ IPV6_PREFIXES = ["2001:db8:a::1/64"]
+ DEFAULT_PORT = 45365
+
+ @pytest.mark.parametrize(
+ "source_validation",
+ [
+ pytest.param(0, id="no_sav"),
+ pytest.param(1, id="sav"),
+ ],
+ )
+ @pytest.mark.parametrize("scope", ["gu", "ll", "lo"])
+ def test_output6_self_tcp(self, scope, source_validation):
+ """Tests IPv6 TCP connection to the local IPv6 address"""
+
+ ToolsHelper.set_sysctl(
+ "net.inet6.ip6.source_address_validation", source_validation
+ )
+
+ if scope == "gu":
+ ip = "2001:db8:a::1"
+ addr_tuple = (ip, self.DEFAULT_PORT)
+ elif scope == "ll":
+ os_ifname = self.vnet.iface_alias_map["if1"].name
+ ifindex = socket.if_nametoindex(os_ifname)
+ ll_data = ToolsHelper.get_linklocals()
+ ip, _ = ll_data[os_ifname][0]
+ addr_tuple = (ip, self.DEFAULT_PORT, 0, ifindex)
+ elif scope == "lo":
+ ip = "::1"
+ ToolsHelper.get_output("route add -6 ::1/128 -iface lo0")
+ ifindex = socket.if_nametoindex("lo0")
+ addr_tuple = (ip, self.DEFAULT_PORT)
+ else:
+ assert 0 == 1
+ print("address: {}".format(addr_tuple))
+
+ start = time.perf_counter()
+ ss = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, socket.IPPROTO_TCP)
+ ss.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_RECVPKTINFO, 1)
+ ss.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
+ ss.bind(addr_tuple)
+ ss.listen()
+ s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, socket.IPPROTO_TCP)
+ s.settimeout(2.0)
+ s.connect(addr_tuple)
+ conn, from_addr = ss.accept()
+ duration = time.perf_counter() - start
+
+ assert from_addr[0] == ip
+ assert duration < 1.0
+
+ @pytest.mark.parametrize(
+ "source_validation",
+ [
+ pytest.param(0, id="no_sav"),
+ pytest.param(1, id="sav"),
+ ],
+ )
+ @pytest.mark.parametrize("scope", ["gu", "ll", "lo"])
+ def test_output6_self_udp(self, scope, source_validation):
+ """Tests IPv6 UDP connection to the local IPv6 address"""
+
+ ToolsHelper.set_sysctl(
+ "net.inet6.ip6.source_address_validation", source_validation
+ )
+
+ if scope == "gu":
+ ip = "2001:db8:a::1"
+ addr_tuple = (ip, self.DEFAULT_PORT)
+ elif scope == "ll":
+ os_ifname = self.vnet.iface_alias_map["if1"].name
+ ifindex = socket.if_nametoindex(os_ifname)
+ ll_data = ToolsHelper.get_linklocals()
+ ip, _ = ll_data[os_ifname][0]
+ addr_tuple = (ip, self.DEFAULT_PORT, 0, ifindex)
+ elif scope == "lo":
+ ip = "::1"
+ ToolsHelper.get_output("route add -6 ::1/128 -iface lo0")
+ ifindex = socket.if_nametoindex("lo0")
+ addr_tuple = (ip, self.DEFAULT_PORT)
+ else:
+ assert 0 == 1
+ print("address: {}".format(addr_tuple))
+
+ start = time.perf_counter()
+ ss = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, socket.IPPROTO_TCP)
+ ss.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_RECVPKTINFO, 1)
+ ss.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
+ ss.bind(addr_tuple)
+ ss.listen()
+ s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, socket.IPPROTO_TCP)
+ s.settimeout(2.0)
+ s.connect(addr_tuple)
+ conn, from_addr = ss.accept()
+ duration = time.perf_counter() - start
+
+ assert from_addr[0] == ip
+ assert duration < 1.0
diff --git a/tests/sys/netipsec/Makefile b/tests/sys/netipsec/Makefile
new file mode 100644
index 000000000000..bb10297e0cc3
--- /dev/null
+++ b/tests/sys/netipsec/Makefile
@@ -0,0 +1,5 @@
+TESTSDIR= ${TESTSBASE}/sys/netipsec
+
+TESTS_SUBDIRS+= tunnel
+
+.include <bsd.test.mk>
diff --git a/tests/sys/netipsec/tunnel/Makefile b/tests/sys/netipsec/tunnel/Makefile
new file mode 100644
index 000000000000..c6060a790cc3
--- /dev/null
+++ b/tests/sys/netipsec/tunnel/Makefile
@@ -0,0 +1,21 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/netipsec/tunnel
+
+ATF_TESTS_SH+= empty \
+ aes_cbc_128_hmac_sha1 \
+ aes_cbc_256_hmac_sha2_256 \
+ aes_gcm_128 \
+ aes_gcm_256 \
+ aesni_aes_cbc_128_hmac_sha1 \
+ aesni_aes_cbc_256_hmac_sha2_256 \
+ aesni_aes_gcm_128 \
+ aesni_aes_gcm_256 \
+ chacha20_poly1305
+
+# Each test uses the same names for its jails, so they must be run serially.
+TEST_METADATA+= is_exclusive=true
+
+${PACKAGE}FILES+= utils.subr
+
+.include <bsd.test.mk>
diff --git a/tests/sys/netipsec/tunnel/aes_cbc_128_hmac_sha1.sh b/tests/sys/netipsec/tunnel/aes_cbc_128_hmac_sha1.sh
new file mode 100755
index 000000000000..317296dec893
--- /dev/null
+++ b/tests/sys/netipsec/tunnel/aes_cbc_128_hmac_sha1.sh
@@ -0,0 +1,48 @@
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "v4" "cleanup"
+v4_head()
+{
+ atf_set descr 'IPSec inet4 tunnel using aes-cbc-128-hmac-sha1'
+ atf_set require.user root
+}
+
+v4_body()
+{
+ # Unload AESNI module if loaded
+ kldstat -q -n aesni && kldunload aesni
+
+ ist_test 4 rijndael-cbc "1234567890123456" hmac-sha1 "12345678901234567890"
+}
+
+v4_cleanup()
+{
+ ist_cleanup
+}
+
+atf_test_case "v6" "cleanup"
+v6_head()
+{
+ atf_set descr 'IPSec inet6 tunnel using aes-cbc-128-hmac-sha1'
+ atf_set require.user root
+}
+
+v6_body()
+{
+ # Unload AESNI module if loaded
+ kldstat -q -n aesni && kldunload aesni
+
+ ist_test 6 rijndael-cbc "1234567890123456" hmac-sha1 "12345678901234567890"
+}
+
+v6_cleanup()
+{
+ ist_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "v4"
+ atf_add_test_case "v6"
+}
diff --git a/tests/sys/netipsec/tunnel/aes_cbc_256_hmac_sha2_256.sh b/tests/sys/netipsec/tunnel/aes_cbc_256_hmac_sha2_256.sh
new file mode 100755
index 000000000000..4aefc8ec042d
--- /dev/null
+++ b/tests/sys/netipsec/tunnel/aes_cbc_256_hmac_sha2_256.sh
@@ -0,0 +1,48 @@
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "v4" "cleanup"
+v4_head()
+{
+ atf_set descr 'IPSec inet4 tunnel using aes-cbc-256-hmac-sha2-256'
+ atf_set require.user root
+}
+
+v4_body()
+{
+ # load AESNI module if not already
+ kldstat -q -n aesni || kldload aesni
+
+ ist_test 4 rijndael-cbc "12345678901234567890123456789012" hmac-sha2-256 "12345678901234567890123456789012"
+}
+
+v4_cleanup()
+{
+ ist_cleanup
+}
+
+atf_test_case "v6" "cleanup"
+v6_head()
+{
+ atf_set descr 'IPSec inet6 tunnel using aes-cbc-256-hmac-sha2-256'
+ atf_set require.user root
+}
+
+v6_body()
+{
+ # load AESNI module if not already
+ kldstat -q -n aesni || kldload aesni
+
+ ist_test 6 rijndael-cbc "12345678901234567890123456789012" hmac-sha2-256 "12345678901234567890123456789012"
+}
+
+v6_cleanup()
+{
+ ist_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "v4"
+ atf_add_test_case "v6"
+}
diff --git a/tests/sys/netipsec/tunnel/aes_gcm_128.sh b/tests/sys/netipsec/tunnel/aes_gcm_128.sh
new file mode 100755
index 000000000000..092a9d596089
--- /dev/null
+++ b/tests/sys/netipsec/tunnel/aes_gcm_128.sh
@@ -0,0 +1,48 @@
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "v4" "cleanup"
+v4_head()
+{
+ atf_set descr 'IPSec inet4 tunnel using aes-gcm-128'
+ atf_set require.user root
+}
+
+v4_body()
+{
+ # Unload AESNI module if loaded
+ kldstat -q -n aesni && kldunload aesni
+
+ ist_test 4 aes-gcm-16 "12345678901234567890"
+}
+
+v4_cleanup()
+{
+ ist_cleanup
+}
+
+atf_test_case "v6" "cleanup"
+v6_head()
+{
+ atf_set descr 'IPSec inet6 tunnel using aes-gcm-128'
+ atf_set require.user root
+}
+
+v6_body()
+{
+ # Unload AESNI module if loaded
+ kldstat -q -n aesni && kldunload aesni
+
+ ist_test 6 aes-gcm-16 "12345678901234567890"
+}
+
+v6_cleanup()
+{
+ ist_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "v4"
+ atf_add_test_case "v6"
+}
diff --git a/tests/sys/netipsec/tunnel/aes_gcm_256.sh b/tests/sys/netipsec/tunnel/aes_gcm_256.sh
new file mode 100755
index 000000000000..7fdee7133cdf
--- /dev/null
+++ b/tests/sys/netipsec/tunnel/aes_gcm_256.sh
@@ -0,0 +1,48 @@
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "v4" "cleanup"
+v4_head()
+{
+ atf_set descr 'IPSec inet4 tunnel using aes-gcm-256'
+ atf_set require.user root
+}
+
+v4_body()
+{
+ # Unload AESNI module if loaded
+ kldstat -q -n aesni && kldunload aesni
+
+ ist_test 4 aes-gcm-16 "123456789012345678901234567890123456"
+}
+
+v4_cleanup()
+{
+ ist_cleanup
+}
+
+atf_test_case "v6" "cleanup"
+v6_head()
+{
+ atf_set descr 'IPSec inet6 tunnel using aes-gcm-256'
+ atf_set require.user root
+}
+
+v6_body()
+{
+ # Unload AESNI module if loaded
+ kldstat -q -n aesni && kldunload aesni
+
+ ist_test 6 aes-gcm-16 "123456789012345678901234567890123456"
+}
+
+v6_cleanup()
+{
+ ist_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "v4"
+ atf_add_test_case "v6"
+}
diff --git a/tests/sys/netipsec/tunnel/aesni_aes_cbc_128_hmac_sha1.sh b/tests/sys/netipsec/tunnel/aesni_aes_cbc_128_hmac_sha1.sh
new file mode 100755
index 000000000000..2f8fa234efe3
--- /dev/null
+++ b/tests/sys/netipsec/tunnel/aesni_aes_cbc_128_hmac_sha1.sh
@@ -0,0 +1,48 @@
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "v4" "cleanup"
+v4_head()
+{
+ atf_set descr 'IPSec inet4 tunnel using aes-cbc-128-hmac-sha1 and AESNI'
+ atf_set require.user root
+}
+
+v4_body()
+{
+ # load AESNI module if not already
+ kldstat -q -n aesni || kldload aesni
+
+ ist_test 4 rijndael-cbc "1234567890123456" hmac-sha1 "12345678901234567890"
+}
+
+v4_cleanup()
+{
+ ist_cleanup
+}
+
+atf_test_case "v6" "cleanup"
+v6_head()
+{
+ atf_set descr 'IPSec inet6 tunnel using aes-cbc-128-hmac-sha1 and AESNI'
+ atf_set require.user root
+}
+
+v6_body()
+{
+ # load AESNI module if not already
+ kldstat -q -n aesni || kldload aesni
+
+ ist_test 6 rijndael-cbc "1234567890123456" hmac-sha1 "12345678901234567890"
+}
+
+v6_cleanup()
+{
+ ist_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "v4"
+ atf_add_test_case "v6"
+}
diff --git a/tests/sys/netipsec/tunnel/aesni_aes_cbc_256_hmac_sha2_256.sh b/tests/sys/netipsec/tunnel/aesni_aes_cbc_256_hmac_sha2_256.sh
new file mode 100755
index 000000000000..c5a75e08d03b
--- /dev/null
+++ b/tests/sys/netipsec/tunnel/aesni_aes_cbc_256_hmac_sha2_256.sh
@@ -0,0 +1,48 @@
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "v4" "cleanup"
+v4_head()
+{
+ atf_set descr 'IPSec inet4 tunnel using aes-cbc-256-hmac-sha2-256 and AESNI'
+ atf_set require.user root
+}
+
+v4_body()
+{
+ # Unload AESNI module if loaded
+ kldstat -q -n aesni && kldunload aesni
+
+ ist_test 4 rijndael-cbc "12345678901234567890123456789012" hmac-sha2-256 "12345678901234567890123456789012"
+}
+
+v4_cleanup()
+{
+ ist_cleanup
+}
+
+atf_test_case "v6" "cleanup"
+v6_head()
+{
+ atf_set descr 'IPSec inet6 tunnel using aes-cbc-256-hmac-sha2-256 and AESNI'
+ atf_set require.user root
+}
+
+v6_body()
+{
+ # Unload AESNI module if loaded
+ kldstat -q -n aesni && kldunload aesni
+
+ ist_test 6 rijndael-cbc "12345678901234567890123456789012" hmac-sha2-256 "12345678901234567890123456789012"
+}
+
+v6_cleanup()
+{
+ ist_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "v4"
+ atf_add_test_case "v6"
+}
diff --git a/tests/sys/netipsec/tunnel/aesni_aes_gcm_128.sh b/tests/sys/netipsec/tunnel/aesni_aes_gcm_128.sh
new file mode 100755
index 000000000000..05961fff53ba
--- /dev/null
+++ b/tests/sys/netipsec/tunnel/aesni_aes_gcm_128.sh
@@ -0,0 +1,48 @@
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "v4" "cleanup"
+v4_head()
+{
+ atf_set descr 'IPSec inet4 tunnel using aes-gcm-128 and AESNI'
+ atf_set require.user root
+}
+
+v4_body()
+{
+ # load AESNI module if not already
+ kldstat -q -n aesni || kldload aesni
+
+ ist_test 4 aes-gcm-16 "12345678901234567890"
+}
+
+v4_cleanup()
+{
+ ist_cleanup
+}
+
+atf_test_case "v6" "cleanup"
+v6_head()
+{
+ atf_set descr 'IPSec inet6 tunnel using aes-gcm-128 and AESNI'
+ atf_set require.user root
+}
+
+v6_body()
+{
+ # load AESNI module if not already
+ kldstat -q -n aesni || kldload aesni
+
+ ist_test 6 aes-gcm-16 "12345678901234567890"
+}
+
+v6_cleanup()
+{
+ ist_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "v4"
+ atf_add_test_case "v6"
+}
diff --git a/tests/sys/netipsec/tunnel/aesni_aes_gcm_256.sh b/tests/sys/netipsec/tunnel/aesni_aes_gcm_256.sh
new file mode 100755
index 000000000000..3fda16a923ab
--- /dev/null
+++ b/tests/sys/netipsec/tunnel/aesni_aes_gcm_256.sh
@@ -0,0 +1,48 @@
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "v4" "cleanup"
+v4_head()
+{
+ atf_set descr 'IPSec inet4 tunnel using aes-gcm-256 and AESNI'
+ atf_set require.user root
+}
+
+v4_body()
+{
+ # load AESNI module if not already
+ kldstat -q -n aesni || kldload aesni
+
+ ist_test 4 aes-gcm-16 "123456789012345678901234567890123456"
+}
+
+v4_cleanup()
+{
+ ist_cleanup
+}
+
+atf_test_case "v6" "cleanup"
+v6_head()
+{
+ atf_set descr 'IPSec inet6 tunnel using aes-gcm-256 and AESNI'
+ atf_set require.user root
+}
+
+v6_body()
+{
+ # load AESNI module if not already
+ kldstat -q -n aesni || kldload aesni
+
+ ist_test 6 aes-gcm-16 "123456789012345678901234567890123456"
+}
+
+v6_cleanup()
+{
+ ist_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "v4"
+ atf_add_test_case "v6"
+}
diff --git a/tests/sys/netipsec/tunnel/chacha20_poly1305.sh b/tests/sys/netipsec/tunnel/chacha20_poly1305.sh
new file mode 100755
index 000000000000..be13e985a6f6
--- /dev/null
+++ b/tests/sys/netipsec/tunnel/chacha20_poly1305.sh
@@ -0,0 +1,42 @@
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "v4" "cleanup"
+v4_head()
+{
+ atf_set descr 'IPSec inet4 tunnel using chacha20-poly1305'
+ atf_set require.user root
+}
+
+v4_body()
+{
+ ist_test 4 chacha20-poly1305 "123456789012345678901234567890123456"
+}
+
+v4_cleanup()
+{
+ ist_cleanup
+}
+
+atf_test_case "v6" "cleanup"
+v6_head()
+{
+ atf_set descr 'IPSec inet6 tunnel using chacha20-poly1305'
+ atf_set require.user root
+}
+
+v6_body()
+{
+ ist_test 6 chacha20-poly1305 "123456789012345678901234567890123456"
+}
+
+v6_cleanup()
+{
+ ist_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "v4"
+ atf_add_test_case "v6"
+}
diff --git a/tests/sys/netipsec/tunnel/empty.sh b/tests/sys/netipsec/tunnel/empty.sh
new file mode 100755
index 000000000000..56480d21f4ec
--- /dev/null
+++ b/tests/sys/netipsec/tunnel/empty.sh
@@ -0,0 +1,43 @@
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "v4" "cleanup"
+v4_head()
+{
+ atf_set descr 'IPSec inet4 tunnel using NULL encryption'
+ atf_set require.user root
+}
+
+v4_body()
+{
+ # Can't use filename "null" for this script: PR 223564
+ ist_test 4 null "1234"
+}
+
+v4_cleanup()
+{
+ ist_cleanup
+}
+
+atf_test_case "v6" "cleanup"
+v6_head()
+{
+ atf_set descr 'IPSec inet6 tunnel using NULL encryption'
+ atf_set require.user root
+}
+
+v6_body()
+{
+ ist_test 6 null "5678"
+}
+
+v6_cleanup()
+{
+ ist_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "v4"
+ atf_add_test_case "v6"
+}
diff --git a/tests/sys/netipsec/tunnel/utils.subr b/tests/sys/netipsec/tunnel/utils.subr
new file mode 100644
index 000000000000..7ea3c6e2b432
--- /dev/null
+++ b/tests/sys/netipsec/tunnel/utils.subr
@@ -0,0 +1,132 @@
+# Utility functions (mainly from pf tests, should be merged one day)
+##
+
+: ${TMPDIR=/tmp}
+
+. $(atf_get_srcdir)/../../common/vnet.subr
+
+ist_init()
+{
+ if ! sysctl -q kern.features.ipsec >/dev/null ; then
+ atf_skip "This test requires ipsec"
+ fi
+
+ vnet_init
+}
+
+ist_labsetup ()
+{
+ epair_LAN_A=$(vnet_mkepair)
+ ifconfig ${epair_LAN_A}a up
+ epair_PUB_A=$(vnet_mkepair)
+ ifconfig ${epair_PUB_A}a up
+ epair_LAN_B=$(vnet_mkepair)
+ ifconfig ${epair_LAN_B}a up
+ epair_PUB_B=$(vnet_mkepair)
+ ifconfig ${epair_PUB_B}a up
+
+ vnet_mkjail hostA ${epair_LAN_A}a
+ vnet_mkjail ipsecA ${epair_LAN_A}b ${epair_PUB_A}a
+ vnet_mkjail router ${epair_PUB_A}b ${epair_PUB_B}b
+ vnet_mkjail ipsecB ${epair_LAN_B}b ${epair_PUB_B}a
+ vnet_mkjail hostB ${epair_LAN_B}a
+}
+
+ist_v4_setup ()
+{
+ jexec hostA ifconfig ${epair_LAN_A}a 192.0.2.1/30 up
+ jexec ipsecA ifconfig ${epair_LAN_A}b 192.0.2.2/30 up
+ jexec ipsecA ifconfig ${epair_PUB_A}a 198.51.100.2/30 up
+ jexec router ifconfig ${epair_PUB_A}b 198.51.100.1/30 up
+ jexec router ifconfig ${epair_PUB_B}b 198.51.100.5/30 up
+ jexec ipsecB ifconfig ${epair_PUB_B}a 198.51.100.6/30 up
+ jexec ipsecB ifconfig ${epair_LAN_B}b 203.0.113.2/30 up
+ jexec hostB ifconfig ${epair_LAN_B}a 203.0.113.1/30 up
+ jexec ipsecA sysctl net.inet.ip.forwarding=1
+ jexec router sysctl net.inet.ip.forwarding=1
+ jexec ipsecB sysctl net.inet.ip.forwarding=1
+ jexec hostA route add default 192.0.2.2
+ jexec ipsecA route add default 198.51.100.1
+ jexec ipsecB route add default 198.51.100.5
+ jexec hostB route add default 203.0.113.2
+}
+
+ist_v6_setup ()
+{
+ jexec hostA ifconfig ${epair_LAN_A}a inet6 2001:db8:1::1/64 up no_dad
+ jexec ipsecA ifconfig ${epair_LAN_A}b inet6 2001:db8:1::2/64 up no_dad
+ jexec ipsecA ifconfig ${epair_PUB_A}a inet6 2001:db8:23::2/64 up no_dad
+ jexec router ifconfig ${epair_PUB_A}b inet6 2001:db8:23::3/64 up no_dad
+ jexec router ifconfig ${epair_PUB_B}b inet6 2001:db8:34::3/64 up no_dad
+ jexec ipsecB ifconfig ${epair_PUB_B}a inet6 2001:db8:34::2/64 up no_dad
+ jexec ipsecB ifconfig ${epair_LAN_B}b inet6 2001:db8:45::2/64 up no_dad
+ jexec hostB ifconfig ${epair_LAN_B}a inet6 2001:db8:45::1/64 up no_dad
+ jexec ipsecA sysctl net.inet6.ip6.forwarding=1
+ jexec router sysctl net.inet6.ip6.forwarding=1
+ jexec ipsecB sysctl net.inet6.ip6.forwarding=1
+ jexec hostA route -6 add default 2001:db8:1::2
+ jexec ipsecA route -6 add default 2001:db8:23::3
+ jexec ipsecB route -6 add default 2001:db8:34::3
+ jexec hostB route -6 add default 2001:db8:45::2
+}
+
+ist_setkey()
+{
+ jname=$1
+ dir=$2
+ afnet=$3
+ enc_algo=$4
+ enc_key=$5
+ auth_algo=$6
+ auth_key=$7
+
+ # Load
+ (
+ printf "#arguments debug: ${jname} ${afnet} ${dir} ${enc_algo} "
+ printf "${enc_key} ${auth_algo} ${auth_key}\n"
+ printf "flush;\n"
+ printf "spdflush;\n"
+ if [ ${afnet} -eq 4 ]; then
+ SRC_LAN="192.0.2.0/24"
+ DST_LAN="203.0.113.0/24"
+ SRC_GW="198.51.100.2"
+ DST_GW="198.51.100.6"
+ else
+ SRC_LAN="2001:db8:1::/64"
+ DST_LAN="2001:db8:45::/64"
+ SRC_GW="2001:db8:23::2"
+ DST_GW="2001:db8:34::2"
+ fi
+ printf "spdadd ${SRC_LAN} ${DST_LAN} any -P "
+ [ ${dir} = "out" ] && printf "out" || printf "in"
+ printf " ipsec esp/tunnel/${SRC_GW}-${DST_GW}/require;\n"
+ printf "spdadd ${DST_LAN} ${SRC_LAN} any -P "
+ [ ${dir} = "out" ] && printf "in" || printf "out"
+ printf " ipsec esp/tunnel/${DST_GW}-${SRC_GW}/require;\n"
+ printf "add ${SRC_GW} ${DST_GW} esp 0x1000 -E ${enc_algo} \"${enc_key}\""
+ [ -n "${auth_algo}" ] && printf " -A ${auth_algo} \"${auth_key}\";\n" || printf ";\n"
+ printf "add ${DST_GW} ${SRC_GW} esp 0x1001 -E ${enc_algo} \"${enc_key}\""
+ [ -n "$auth_algo" ] && printf " -A ${auth_algo} \"${auth_key}\";\n" || printf ";\n"
+ ) > ${TMPDIR}/ipsec.${jname}.conf
+}
+
+ist_test()
+{
+ ist_init
+ ist_labsetup
+ [ $1 -eq 4 ] && ist_v4_setup || ist_v6_setup
+ ist_setkey ipsecA out $@
+ atf_check -s exit:0 -o ignore jexec ipsecA setkey -f ${TMPDIR}/ipsec.ipsecA.conf
+ ist_setkey ipsecB in $@
+ atf_check -s exit:0 -o ignore jexec ipsecB setkey -f ${TMPDIR}/ipsec.ipsecB.conf
+ # Check ipsec tunnel
+ if [ $1 -eq 4 ]; then
+ atf_check -s exit:0 -o ignore jexec hostA ping -c 1 203.0.113.1
+ else
+ atf_check -s exit:0 -o ignore jexec hostA ping -6 -c 1 2001:db8:45::1
+ fi
+}
+ist_cleanup()
+{
+ vnet_cleanup
+}
diff --git a/tests/sys/netlink/Makefile b/tests/sys/netlink/Makefile
new file mode 100644
index 000000000000..c07ef8663867
--- /dev/null
+++ b/tests/sys/netlink/Makefile
@@ -0,0 +1,17 @@
+PACKAGE= tests
+WARNS?= 1
+
+TESTSDIR= ${TESTSBASE}/sys/netlink
+
+ATF_TESTS_C+= netlink_socket
+ATF_TESTS_C+= test_snl test_snl_generic
+ATF_TESTS_PYTEST += test_nl_core.py
+ATF_TESTS_PYTEST += test_rtnl_iface.py
+ATF_TESTS_PYTEST += test_rtnl_ifaddr.py
+ATF_TESTS_PYTEST += test_rtnl_neigh.py
+ATF_TESTS_PYTEST += test_rtnl_route.py
+ATF_TESTS_PYTEST += test_netlink_message_writer.py
+
+CFLAGS+= -I${.CURDIR:H:H:H}
+
+.include <bsd.test.mk>
diff --git a/tests/sys/netlink/netlink_socket.c b/tests/sys/netlink/netlink_socket.c
new file mode 100644
index 000000000000..6dcc894b6695
--- /dev/null
+++ b/tests/sys/netlink/netlink_socket.c
@@ -0,0 +1,341 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Gleb Smirnoff <glebius@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/module.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <netlink/netlink.h>
+#include <netlink/netlink_route.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <atf-c.h>
+
+static struct itimerval itv = {
+ .it_interval = { 0, 0 },
+ .it_value = { 1, 0 }, /* one second */
+};
+static sig_atomic_t timer_done = 0;
+static void
+sigalarm(int sig __unused)
+{
+
+ timer_done = 1;
+}
+
+static struct sigaction sigact = {
+ .sa_handler = sigalarm,
+};
+
+static struct nlmsghdr hdr = (struct nlmsghdr) {
+ .nlmsg_type = RTM_GETLINK,
+ .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
+ .nlmsg_len = sizeof(struct nlmsghdr),
+};
+
+#define BUFLEN 1000
+
+static int
+fullsocket(void)
+{
+ char buf[BUFLEN];
+ socklen_t slen = sizeof(int);
+ int fd, sendspace, recvspace, sendavail, recvavail, rsize;
+ u_int cnt = 0;
+
+ ATF_REQUIRE((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1);
+ ATF_REQUIRE(getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sendspace,
+ &slen) == 0);
+ ATF_REQUIRE(getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &recvspace,
+ &slen) == 0);
+
+ /* Check the expected size of reply on a single RTM_GETLINK. */
+ ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == sizeof(hdr));
+ ATF_REQUIRE(recv(fd, buf, sizeof(hdr), MSG_WAITALL | MSG_PEEK) ==
+ sizeof(hdr));
+ ATF_REQUIRE(ioctl(fd, FIONREAD, &rsize) != -1);
+
+
+ /*
+ * Flood the socket with requests, without reading out the replies.
+ * While we are flooding, the kernel tries to process the requests.
+ * Kernel takes off requests from the send buffer and puts replies
+ * on receive buffer. Once the receive buffer is full it stops working
+ * on queue in the send buffer. At this point we must get a solid
+ * failure. However, if we flood faster than kernel taskqueue runs,
+ * we may get intermittent failures.
+ */
+ do {
+ ssize_t rv;
+
+ rv = send(fd, &hdr, sizeof(hdr), MSG_DONTWAIT);
+ if (__predict_true(rv == sizeof(hdr)))
+ cnt++;
+ else {
+ ATF_REQUIRE(errno == EAGAIN);
+ ATF_REQUIRE(sizeof(hdr) * cnt > sendspace);
+ }
+ ATF_REQUIRE(ioctl(fd, FIONREAD, &recvavail) != -1);
+ ATF_REQUIRE(ioctl(fd, FIONWRITE, &sendavail) != -1);
+ } while (recvavail <= recvspace - rsize ||
+ sendavail <= sendspace - sizeof(hdr));
+
+ return (fd);
+}
+
+ATF_TC_WITHOUT_HEAD(overflow);
+ATF_TC_BODY(overflow, tc)
+{
+ char buf[BUFLEN];
+ int fd;
+
+ fd = fullsocket();
+
+ /* Both buffers full: block. */
+ timer_done = 0;
+ ATF_REQUIRE(sigaction(SIGALRM, &sigact, NULL) == 0);
+ ATF_REQUIRE(setitimer(ITIMER_REAL, &itv, NULL) == 0);
+ ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == -1);
+ ATF_REQUIRE(errno == EINTR);
+ ATF_REQUIRE(timer_done == 1);
+
+ /*
+ * Now, reading something from the receive buffer should wake up the
+ * taskqueue and send buffer should start getting drained.
+ */
+ ATF_REQUIRE(recv(fd, buf, BUFLEN, 0) > sizeof(hdr));
+ timer_done = 0;
+ ATF_REQUIRE(setitimer(ITIMER_REAL, &itv, NULL) == 0);
+ ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == sizeof(hdr));
+ ATF_REQUIRE(timer_done == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(peek);
+ATF_TC_BODY(peek, tc)
+{
+ char *buf;
+ ssize_t ss, ss1;
+ int fd;
+
+ ATF_REQUIRE((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1);
+
+ ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == sizeof(hdr));
+ ss = recv(fd, buf, 0, MSG_WAITALL | MSG_PEEK | MSG_TRUNC);
+ ATF_REQUIRE((buf = malloc(ss)) != NULL);
+ ATF_REQUIRE(recv(fd, buf, ss, MSG_WAITALL) == ss);
+}
+
+struct nl_control {
+ struct nlattr nla;
+ uint32_t val;
+};
+
+static void
+cmsg_check(struct msghdr *msg)
+{
+ static pid_t pid = 0;
+ struct cmsghdr *cmsg;
+ struct nl_control *nlc;
+
+ ATF_REQUIRE((cmsg = CMSG_FIRSTHDR(msg)) != NULL);
+ ATF_REQUIRE(cmsg->cmsg_level == SOL_NETLINK);
+ ATF_REQUIRE(cmsg->cmsg_type == NETLINK_MSG_INFO);
+ nlc = (struct nl_control *)CMSG_DATA(cmsg);
+ ATF_REQUIRE(nlc[0].nla.nla_type == NLMSGINFO_ATTR_PROCESS_ID);
+ if (pid == 0)
+ pid = getpid();
+ ATF_REQUIRE(nlc[0].val == pid);
+ ATF_REQUIRE(nlc[1].nla.nla_type == NLMSGINFO_ATTR_PORT_ID);
+ /* XXX need another test to test port id */
+ ATF_REQUIRE(nlc[1].val == 0);
+ ATF_REQUIRE(CMSG_NXTHDR(msg, cmsg) == NULL);
+ ATF_REQUIRE((msg->msg_flags & MSG_CTRUNC) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(sizes);
+ATF_TC_BODY(sizes, tc)
+{
+#define NLMSG_LARGE 2048 /* XXX: match kernel nl_buf */
+ char buf[NLMSG_LARGE * 10];
+ char cbuf[CMSG_SPACE(sizeof(struct nl_control) * 2)];
+ struct iovec iov;
+ struct msghdr msg = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = cbuf,
+ .msg_controllen = sizeof(cbuf),
+ };
+ ssize_t ss;
+ int fd, size, rsize;
+
+ fd = fullsocket();
+
+ /*
+ * Set NETLINK_MSG_INFO, so that later cmsg_check will check that any
+ * read is accompanied with control data.
+ */
+ ATF_REQUIRE(setsockopt(fd, SOL_NETLINK, NETLINK_MSG_INFO,
+ &(int){1}, sizeof(int)) == 0);
+
+ iov = (struct iovec ){
+ .iov_base = &hdr,
+ .iov_len = sizeof(hdr),
+ };
+ /* Obtain size of the first message in the socket. */
+ ss = recvmsg(fd, &msg, MSG_WAITALL | MSG_PEEK | MSG_TRUNC);
+ ATF_REQUIRE(ss == hdr.nlmsg_len);
+ /* And overall amount of data in the socket. */
+ ATF_REQUIRE(ioctl(fd, FIONREAD, &rsize) != -1);
+ cmsg_check(&msg);
+
+ /* Zero-sized read should not affect state of the socket buffer. */
+ ATF_REQUIRE(recv(fd, buf, 0, 0) == 0);
+ ATF_REQUIRE(ioctl(fd, FIONREAD, &size) != -1);
+ ATF_REQUIRE(size == rsize);
+
+ /*
+ * Undersized read should lose a message. This isn't exactly
+ * pronounced in the Netlink RFC, but it always says that Netlink
+ * socket is an analog of the BSD routing socket, and this is how
+ * a route(4) socket deals with undersized read.
+ */
+ iov = (struct iovec ){
+ .iov_base = buf,
+ .iov_len = sizeof(hdr),
+ };
+ ATF_REQUIRE(recvmsg(fd, &msg, 0) == sizeof(hdr));
+ ATF_REQUIRE(msg.msg_flags & MSG_TRUNC);
+ ATF_REQUIRE(hdr.nlmsg_len > sizeof(hdr));
+ size = rsize - hdr.nlmsg_len;
+ ATF_REQUIRE(ioctl(fd, FIONREAD, &rsize) != -1);
+ ATF_REQUIRE(size == rsize);
+ cmsg_check(&msg);
+
+ /*
+ * Large read should span several nl_bufs, seeing no boundaries.
+ */
+ iov = (struct iovec ){
+ .iov_base = buf,
+ .iov_len = sizeof(buf) < rsize ? sizeof(buf) : rsize,
+ };
+ ss = recvmsg(fd, &msg, 0);
+ ATF_REQUIRE(ss > NLMSG_LARGE * 9 || ss == rsize);
+ cmsg_check(&msg);
+}
+
+static struct nlattr *
+nla_RTA_DST(struct nlattr *start, ssize_t len)
+{
+ struct nlattr *nla;
+
+ for (nla = start; (char *)nla < (char *)start + len;
+ nla = (struct nlattr *)((char *)nla + NLA_ALIGN(nla->nla_len))) {
+ if (nla->nla_type == RTA_DST)
+ return (nla);
+ }
+
+ return (NULL);
+}
+/*
+ * Check that NETLINK_ADD_MEMBERSHIP subscribes us. Add & delete a temporary
+ * route and check if announcements came in.
+ */
+ATF_TC_WITHOUT_HEAD(membership);
+ATF_TC_BODY(membership, tc)
+{
+ struct {
+ struct nlmsghdr hdr;
+ struct rtmsg rtm;
+ struct nlattr rta_dst;
+ struct in_addr dst;
+ struct nlattr rta_oif;
+ uint32_t oif;
+ } reply, msg = {
+ .hdr.nlmsg_type = RTM_NEWROUTE,
+ .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL,
+ .hdr.nlmsg_len = sizeof(msg),
+ .rtm.rtm_family = AF_INET,
+ .rtm.rtm_protocol = RTPROT_STATIC,
+ .rtm.rtm_type = RTN_UNICAST,
+ .rtm.rtm_dst_len = 32,
+ .rta_dst.nla_type = RTA_DST,
+ .rta_dst.nla_len = sizeof(struct in_addr) +
+ sizeof(struct nlattr),
+ .dst.s_addr = inet_addr("127.0.0.127"),
+ .rta_oif.nla_type = RTA_OIF,
+ .rta_oif.nla_len = sizeof(uint32_t) + sizeof(struct nlattr),
+ .oif = 1,
+ };
+ struct nlattr *nla;
+ int fd;
+
+ ATF_REQUIRE((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1);
+ ATF_REQUIRE(setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
+ &(int){RTNLGRP_IPV4_ROUTE}, sizeof(int)) == 0);
+
+ ATF_REQUIRE(send(fd, &msg, sizeof(msg), 0) == sizeof(msg));
+ ATF_REQUIRE(recv(fd, &reply, sizeof(reply), 0) == sizeof(reply));
+ ATF_REQUIRE(reply.hdr.nlmsg_type == msg.hdr.nlmsg_type);
+ ATF_REQUIRE(reply.rtm.rtm_type == msg.rtm.rtm_type);
+ ATF_REQUIRE(reply.rtm.rtm_dst_len == msg.rtm.rtm_dst_len);
+ ATF_REQUIRE(nla = nla_RTA_DST(&reply.rta_dst, sizeof(reply)));
+ ATF_REQUIRE(memcmp(&msg.dst, (char *)nla + sizeof(struct nlattr),
+ sizeof(struct in_addr)) == 0);
+
+ msg.hdr.nlmsg_type = RTM_DELROUTE;
+ msg.hdr.nlmsg_len -= sizeof(struct nlattr) + sizeof(uint32_t);
+ ATF_REQUIRE(send(fd, &msg, msg.hdr.nlmsg_len, 0) == msg.hdr.nlmsg_len);
+ ATF_REQUIRE(recv(fd, &reply, sizeof(reply), 0) == sizeof(reply));
+ ATF_REQUIRE(reply.hdr.nlmsg_type == msg.hdr.nlmsg_type);
+ ATF_REQUIRE(reply.rtm.rtm_type == msg.rtm.rtm_type);
+ ATF_REQUIRE(reply.rtm.rtm_dst_len == msg.rtm.rtm_dst_len);
+ ATF_REQUIRE(nla = nla_RTA_DST(&reply.rta_dst, sizeof(reply)));
+ ATF_REQUIRE(memcmp(&msg.dst, (char *)nla + sizeof(struct nlattr),
+ sizeof(struct in_addr)) == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ if (modfind("netlink") == -1)
+ atf_tc_skip("netlink module not loaded");
+
+ ATF_TP_ADD_TC(tp, overflow);
+ ATF_TP_ADD_TC(tp, peek);
+ ATF_TP_ADD_TC(tp, sizes);
+ ATF_TP_ADD_TC(tp, membership);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/netlink/test_netlink_message_writer.py b/tests/sys/netlink/test_netlink_message_writer.py
new file mode 100644
index 000000000000..5f854b14ca45
--- /dev/null
+++ b/tests/sys/netlink/test_netlink_message_writer.py
@@ -0,0 +1,39 @@
+import mmap
+import pytest
+
+from atf_python.ktest import BaseKernelTest
+from atf_python.sys.netlink.attrs import NlAttrU32
+
+M_NOWAIT = 1
+M_WAITOK = 2
+
+NLMSG_SMALL = 128
+NLMSG_LARGE = 2048
+
+class TestNetlinkMessageWriter(BaseKernelTest):
+ KTEST_MODULE_NAME = "ktest_netlink_message_writer"
+
+ @pytest.mark.parametrize(
+ "malloc_flags",
+ [
+ pytest.param(M_NOWAIT, id="NOWAIT"),
+ pytest.param(M_WAITOK, id="WAITOK"),
+ ],
+ )
+ @pytest.mark.parametrize(
+ "sz",
+ [
+ pytest.param([NLMSG_SMALL, NLMSG_SMALL], id="NLMSG_SMALL"),
+ pytest.param([NLMSG_LARGE, NLMSG_LARGE], id="NLMSG_LARGE"),
+ pytest.param([NLMSG_LARGE + 256, NLMSG_LARGE + 256], id="NLMSG_LARGE+256"),
+ ],
+ )
+ def test_nlbuf_writer_allocation(self, sz, malloc_flags):
+ """override to parametrize"""
+
+ test_meta = [
+ NlAttrU32(1, sz[0]), # size
+ NlAttrU32(2, sz[1]), # expected_avail
+ NlAttrU32(3, malloc_flags),
+ ]
+ self.runtest(test_meta)
diff --git a/tests/sys/netlink/test_nl_core.py b/tests/sys/netlink/test_nl_core.py
new file mode 100644
index 000000000000..5b4306cbb932
--- /dev/null
+++ b/tests/sys/netlink/test_nl_core.py
@@ -0,0 +1,33 @@
+import errno
+import socket
+
+import pytest
+from atf_python.sys.net.vnet import SingleVnetTestTemplate
+from atf_python.sys.netlink.netlink import NetlinkTestTemplate
+from atf_python.sys.netlink.utils import NlConst
+
+
+class TestNlCore(NetlinkTestTemplate, SingleVnetTestTemplate):
+ @pytest.mark.parametrize(
+ "params",
+ [
+ pytest.param({"type": socket.SOCK_RAW}, id="SOCK_RAW"),
+ pytest.param({"type": socket.SOCK_DGRAM}, id="SOCK_DGRAM"),
+ ],
+ )
+ def test_socket_type(self, params):
+ s = socket.socket(NlConst.AF_NETLINK, params["type"], NlConst.NETLINK_ROUTE)
+ s.close()
+
+ @pytest.mark.parametrize(
+ "params",
+ [
+ pytest.param({"type": socket.SOCK_STREAM}, id="SOCK_STREAM"),
+ pytest.param({"type": socket.SOCK_RDM}, id="SOCK_RDM"),
+ pytest.param({"type": socket.SOCK_SEQPACKET}, id="SOCK_SEQPACKET"),
+ ],
+ )
+ def test_socket_type_unsup(self, params):
+ with pytest.raises(OSError) as exc_info:
+ socket.socket(NlConst.AF_NETLINK, params["type"], NlConst.NETLINK_ROUTE)
+ assert exc_info.value.errno == errno.EPROTOTYPE
diff --git a/tests/sys/netlink/test_rtnl_iface.py b/tests/sys/netlink/test_rtnl_iface.py
new file mode 100644
index 000000000000..41cb4d16de94
--- /dev/null
+++ b/tests/sys/netlink/test_rtnl_iface.py
@@ -0,0 +1,355 @@
+import errno
+import socket
+
+import pytest
+from atf_python.sys.netlink.netlink_route import IflattrType
+from atf_python.sys.netlink.netlink_route import IflinkInfo
+from atf_python.sys.netlink.netlink_route import IfLinkInfoDataVlan
+from atf_python.sys.netlink.netlink_route import NetlinkIflaMessage
+from atf_python.sys.netlink.netlink import NetlinkTestTemplate
+from atf_python.sys.netlink.attrs import NlAttrNested
+from atf_python.sys.netlink.attrs import NlAttrStr
+from atf_python.sys.netlink.attrs import NlAttrStrn
+from atf_python.sys.netlink.attrs import NlAttrU16
+from atf_python.sys.netlink.attrs import NlAttrU32
+from atf_python.sys.netlink.utils import NlConst
+from atf_python.sys.netlink.base_headers import NlmBaseFlags
+from atf_python.sys.netlink.base_headers import NlmNewFlags
+from atf_python.sys.netlink.base_headers import NlMsgType
+from atf_python.sys.netlink.netlink_route import NlRtMsgType
+from atf_python.sys.netlink.netlink_route import rtnl_ifla_attrs
+from atf_python.sys.net.vnet import SingleVnetTestTemplate
+from atf_python.sys.net.tools import ToolsHelper
+
+
+class TestRtNlIface(NetlinkTestTemplate, SingleVnetTestTemplate):
+ def setup_method(self, method):
+ super().setup_method(method)
+ self.setup_netlink(NlConst.NETLINK_ROUTE)
+
+ def get_interface_byname(self, ifname):
+ msg = NetlinkIflaMessage(self.helper, NlRtMsgType.RTM_GETLINK.value)
+ msg.nl_hdr.nlmsg_flags = (
+ NlmBaseFlags.NLM_F_ACK.value | NlmBaseFlags.NLM_F_REQUEST.value
+ )
+ msg.add_nla(NlAttrStr(IflattrType.IFLA_IFNAME, ifname))
+ self.write_message(msg)
+ while True:
+ rx_msg = self.read_message()
+ if msg.nl_hdr.nlmsg_seq == rx_msg.nl_hdr.nlmsg_seq:
+ if rx_msg.is_type(NlMsgType.NLMSG_ERROR):
+ if rx_msg.error_code != 0:
+ raise ValueError("unable to get interface {}".format(ifname))
+ elif rx_msg.is_type(NlRtMsgType.RTM_NEWLINK):
+ return rx_msg
+ else:
+ raise ValueError("bad message")
+
+ def test_get_iface_byname_error(self):
+ """Tests error on fetching non-existing interface name"""
+ msg = NetlinkIflaMessage(self.helper, NlRtMsgType.RTM_GETLINK.value)
+ msg.nl_hdr.nlmsg_flags = (
+ NlmBaseFlags.NLM_F_ACK.value | NlmBaseFlags.NLM_F_REQUEST.value
+ )
+ msg.add_nla(NlAttrStr(IflattrType.IFLA_IFNAME, "lo10"))
+
+ rx_msg = self.get_reply(msg)
+ assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
+ assert rx_msg.error_code == errno.ENODEV
+
+ def test_get_iface_byindex_error(self):
+ """Tests error on fetching non-existing interface index"""
+ msg = NetlinkIflaMessage(self.helper, NlRtMsgType.RTM_GETLINK.value)
+ msg.nl_hdr.nlmsg_flags = (
+ NlmBaseFlags.NLM_F_ACK.value | NlmBaseFlags.NLM_F_REQUEST.value
+ )
+ msg.base_hdr.ifi_index = 2147483647
+
+ rx_msg = self.get_reply(msg)
+ assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
+ assert rx_msg.error_code == errno.ENODEV
+
+ @pytest.mark.require_user("root")
+ def test_create_iface_plain(self):
+ """Tests loopback creation w/o any parameters"""
+ flags = NlmNewFlags.NLM_F_EXCL.value | NlmNewFlags.NLM_F_CREATE.value
+ msg = NetlinkIflaMessage(self.helper, NlRtMsgType.RTM_NEWLINK.value)
+ msg.nl_hdr.nlmsg_flags = (
+ flags | NlmBaseFlags.NLM_F_ACK.value | NlmBaseFlags.NLM_F_REQUEST.value
+ )
+ msg.add_nla(NlAttrStr(IflattrType.IFLA_IFNAME, "lo10"))
+ msg.add_nla(
+ NlAttrNested(
+ IflattrType.IFLA_LINKINFO,
+ [
+ NlAttrStrn(IflinkInfo.IFLA_INFO_KIND, "lo"),
+ ],
+ )
+ )
+
+ rx_msg = self.get_reply(msg)
+ assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
+ assert rx_msg.error_code == 0
+
+ self.get_interface_byname("lo10")
+
+ @pytest.mark.require_user("root")
+ def test_create_iface_plain_retvals(self):
+ """Tests loopback creation w/o any parameters"""
+ flags = NlmNewFlags.NLM_F_EXCL.value | NlmNewFlags.NLM_F_CREATE.value
+ msg = NetlinkIflaMessage(self.helper, NlRtMsgType.RTM_NEWLINK.value)
+ msg.nl_hdr.nlmsg_flags = (
+ flags | NlmBaseFlags.NLM_F_ACK.value | NlmBaseFlags.NLM_F_REQUEST.value
+ )
+ msg.add_nla(NlAttrStr(IflattrType.IFLA_IFNAME, "lo10"))
+ msg.add_nla(
+ NlAttrNested(
+ IflattrType.IFLA_LINKINFO,
+ [
+ NlAttrStrn(IflinkInfo.IFLA_INFO_KIND, "lo"),
+ ],
+ )
+ )
+
+ rx_msg = self.get_reply(msg)
+ assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
+ assert rx_msg.error_code == 0
+ assert rx_msg.cookie is not None
+ nla_list, _ = rx_msg.parse_attrs(bytes(rx_msg.cookie)[4:], rtnl_ifla_attrs)
+ nla_map = {n.nla_type: n for n in nla_list}
+ assert IflattrType.IFLA_IFNAME.value in nla_map
+ assert nla_map[IflattrType.IFLA_IFNAME.value].text == "lo10"
+ assert IflattrType.IFLA_NEW_IFINDEX.value in nla_map
+ assert nla_map[IflattrType.IFLA_NEW_IFINDEX.value].u32 > 0
+
+ lo_msg = self.get_interface_byname("lo10")
+ assert (
+ lo_msg.base_hdr.ifi_index == nla_map[IflattrType.IFLA_NEW_IFINDEX.value].u32
+ )
+
+ @pytest.mark.require_user("root")
+ def test_create_iface_attrs(self):
+ """Tests interface creation with additional properties"""
+ flags = NlmNewFlags.NLM_F_EXCL.value | NlmNewFlags.NLM_F_CREATE.value
+ msg = NetlinkIflaMessage(self.helper, NlRtMsgType.RTM_NEWLINK.value)
+ msg.nl_hdr.nlmsg_flags = (
+ flags | NlmBaseFlags.NLM_F_ACK.value | NlmBaseFlags.NLM_F_REQUEST.value
+ )
+ msg.add_nla(NlAttrStr(IflattrType.IFLA_IFNAME, "lo10"))
+ msg.add_nla(
+ NlAttrNested(
+ IflattrType.IFLA_LINKINFO,
+ [
+ NlAttrStrn(IflinkInfo.IFLA_INFO_KIND, "lo"),
+ ],
+ )
+ )
+
+ # Custom attributes
+ msg.add_nla(NlAttrStr(IflattrType.IFLA_IFALIAS, "test description"))
+ msg.add_nla(NlAttrU32(IflattrType.IFLA_MTU, 1024))
+
+ rx_msg = self.get_reply(msg)
+ assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
+ assert rx_msg.error_code == 0
+
+ iface_msg = self.get_interface_byname("lo10")
+ assert iface_msg.get_nla(IflattrType.IFLA_IFALIAS).text == "test description"
+ assert iface_msg.get_nla(IflattrType.IFLA_MTU).u32 == 1024
+
+ @pytest.mark.require_user("root")
+ def test_modify_iface_attrs(self):
+ """Tests interface modifications"""
+ flags = NlmNewFlags.NLM_F_EXCL.value | NlmNewFlags.NLM_F_CREATE.value
+ msg = NetlinkIflaMessage(self.helper, NlRtMsgType.RTM_NEWLINK.value)
+ msg.nl_hdr.nlmsg_flags = (
+ flags | NlmBaseFlags.NLM_F_ACK.value | NlmBaseFlags.NLM_F_REQUEST.value
+ )
+ msg.add_nla(NlAttrStr(IflattrType.IFLA_IFNAME, "lo10"))
+ msg.add_nla(
+ NlAttrNested(
+ IflattrType.IFLA_LINKINFO,
+ [
+ NlAttrStrn(IflinkInfo.IFLA_INFO_KIND, "lo"),
+ ],
+ )
+ )
+
+ rx_msg = self.get_reply(msg)
+ assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
+ assert rx_msg.error_code == 0
+
+ msg = NetlinkIflaMessage(self.helper, NlRtMsgType.RTM_NEWLINK.value)
+ msg.nl_hdr.nlmsg_flags = (
+ NlmBaseFlags.NLM_F_ACK.value | NlmBaseFlags.NLM_F_REQUEST.value
+ )
+ msg.add_nla(NlAttrStr(IflattrType.IFLA_IFNAME, "lo10"))
+
+ # Custom attributes
+ msg.add_nla(NlAttrStr(IflattrType.IFLA_IFALIAS, "test description"))
+ msg.add_nla(NlAttrU32(IflattrType.IFLA_MTU, 1024))
+
+ rx_msg = self.get_reply(msg)
+ assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
+ assert rx_msg.error_code == 0
+
+ iface_msg = self.get_interface_byname("lo10")
+ assert iface_msg.get_nla(IflattrType.IFLA_IFALIAS).text == "test description"
+ assert iface_msg.get_nla(IflattrType.IFLA_MTU).u32 == 1024
+
+ @pytest.mark.require_user("root")
+ def test_delete_iface(self):
+ """Tests interface modifications"""
+ flags = NlmNewFlags.NLM_F_EXCL.value | NlmNewFlags.NLM_F_CREATE.value
+ msg = NetlinkIflaMessage(self.helper, NlRtMsgType.RTM_NEWLINK.value)
+ msg.nl_hdr.nlmsg_flags = (
+ flags | NlmBaseFlags.NLM_F_ACK.value | NlmBaseFlags.NLM_F_REQUEST.value
+ )
+ msg.add_nla(NlAttrStr(IflattrType.IFLA_IFNAME, "lo10"))
+ msg.add_nla(
+ NlAttrNested(
+ IflattrType.IFLA_LINKINFO,
+ [
+ NlAttrStrn(IflinkInfo.IFLA_INFO_KIND, "lo"),
+ ],
+ )
+ )
+
+ rx_msg = self.get_reply(msg)
+ assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
+ assert rx_msg.error_code == 0
+
+ iface_msg = self.get_interface_byname("lo10")
+ iface_idx = iface_msg.base_hdr.ifi_index
+
+ msg = NetlinkIflaMessage(self.helper, NlRtMsgType.RTM_DELLINK.value)
+ msg.nl_hdr.nlmsg_flags = (
+ NlmBaseFlags.NLM_F_ACK.value | NlmBaseFlags.NLM_F_REQUEST.value
+ )
+ msg.base_hdr.ifi_index = iface_idx
+ # msg.add_nla(NlAttrStr(IflattrType.IFLA_IFNAME, "lo10"))
+
+ rx_msg = self.get_reply(msg)
+ assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
+ assert rx_msg.error_code == 0
+
+ msg = NetlinkIflaMessage(self.helper, NlRtMsgType.RTM_GETLINK.value)
+ msg.nl_hdr.nlmsg_flags = (
+ NlmBaseFlags.NLM_F_ACK.value | NlmBaseFlags.NLM_F_REQUEST.value
+ )
+ msg.base_hdr.ifi_index = 2147483647
+
+ rx_msg = self.get_reply(msg)
+ assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
+ assert rx_msg.error_code == errno.ENODEV
+
+ @pytest.mark.require_user("root")
+ def test_dump_ifaces_many(self):
+ """Tests if interface dummp is not missing interfaces"""
+
+ ifmap = {}
+ ifmap[socket.if_nametoindex("lo0")] = "lo0"
+
+ for i in range(40):
+ ifname = "lo{}".format(i + 1)
+ flags = NlmNewFlags.NLM_F_EXCL.value | NlmNewFlags.NLM_F_CREATE.value
+ msg = NetlinkIflaMessage(self.helper, NlRtMsgType.RTM_NEWLINK.value)
+ msg.nl_hdr.nlmsg_flags = (
+ flags | NlmBaseFlags.NLM_F_ACK.value | NlmBaseFlags.NLM_F_REQUEST.value
+ )
+ msg.add_nla(NlAttrStr(IflattrType.IFLA_IFNAME, ifname))
+ msg.add_nla(
+ NlAttrNested(
+ IflattrType.IFLA_LINKINFO,
+ [
+ NlAttrStrn(IflinkInfo.IFLA_INFO_KIND, "lo"),
+ ],
+ )
+ )
+
+ rx_msg = self.get_reply(msg)
+ assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
+ nla_list, _ = rx_msg.parse_attrs(bytes(rx_msg.cookie)[4:], rtnl_ifla_attrs)
+ nla_map = {n.nla_type: n for n in nla_list}
+ assert nla_map[IflattrType.IFLA_IFNAME.value].text == ifname
+ ifindex = nla_map[IflattrType.IFLA_NEW_IFINDEX.value].u32
+ assert ifindex > 0
+ assert ifindex not in ifmap
+ ifmap[ifindex] = ifname
+
+ # Dump all interfaces and check if the output matches ifmap
+ kernel_ifmap = {}
+ msg = NetlinkIflaMessage(self.helper, NlRtMsgType.RTM_GETLINK.value)
+ msg.nl_hdr.nlmsg_flags = (
+ NlmBaseFlags.NLM_F_ACK.value | NlmBaseFlags.NLM_F_REQUEST.value
+ )
+ self.write_message(msg)
+ while True:
+ rx_msg = self.read_message()
+ if msg.nl_hdr.nlmsg_seq != rx_msg.nl_hdr.nlmsg_seq:
+ raise ValueError(
+ "unexpected seq {}".format(rx_msg.nl_hdr.nlmsg_seq)
+ )
+ if rx_msg.is_type(NlMsgType.NLMSG_ERROR):
+ raise ValueError("unexpected message {}".format(rx_msg))
+ if rx_msg.is_type(NlMsgType.NLMSG_DONE):
+ break
+ if not rx_msg.is_type(NlRtMsgType.RTM_NEWLINK):
+ raise ValueError("unexpected message {}".format(rx_msg))
+
+ ifindex = rx_msg.base_hdr.ifi_index
+ assert ifindex == rx_msg.base_hdr.ifi_index
+ ifname = rx_msg.get_nla(IflattrType.IFLA_IFNAME).text
+ if ifname.startswith("lo"):
+ kernel_ifmap[ifindex] = ifname
+ assert kernel_ifmap == ifmap
+
+ #
+ # *
+ # * {len=76, type=RTM_NEWLINK, flags=NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL|NLM_F_CREATE, seq=1662892737, pid=0},
+ # * {ifi_family=AF_UNSPEC, ifi_type=ARPHRD_NETROM, ifi_index=0, ifi_flags=0, ifi_change=0},
+ # * {{nla_len=8, nla_type=IFLA_LINK}, 2},
+ # * {{nla_len=12, nla_type=IFLA_IFNAME}, "xvlan22"},
+ # * {{nla_len=24, nla_type=IFLA_LINKINFO},
+ # * {{nla_len=8, nla_type=IFLA_INFO_KIND}, "vlan"...},
+ # * {{nla_len=12, nla_type=IFLA_INFO_DATA}, "\x06\x00\x01\x00\x16\x00\x00\x00"}
+ # */
+ @pytest.mark.require_user("root")
+ def test_create_vlan_plain(self):
+ """Creates 802.1Q VLAN interface in vlanXX and ifX fashion"""
+ self.require_module("if_vlan")
+ os_ifname = self.vnet.iface_alias_map["if1"].name
+ ifindex = socket.if_nametoindex(os_ifname)
+
+ flags = NlmNewFlags.NLM_F_EXCL.value | NlmNewFlags.NLM_F_CREATE.value
+ msg = NetlinkIflaMessage(self.helper, NlRtMsgType.RTM_NEWLINK.value)
+ msg.nl_hdr.nlmsg_flags = (
+ flags | NlmBaseFlags.NLM_F_ACK.value | NlmBaseFlags.NLM_F_REQUEST.value
+ )
+ msg.base_hdr.ifi_index = ifindex
+
+ msg.add_nla(NlAttrU32(IflattrType.IFLA_LINK, ifindex))
+ msg.add_nla(NlAttrStr(IflattrType.IFLA_IFNAME, "vlan22"))
+
+ msg.add_nla(
+ NlAttrNested(
+ IflattrType.IFLA_LINKINFO,
+ [
+ NlAttrStrn(IflinkInfo.IFLA_INFO_KIND, "vlan"),
+ NlAttrNested(
+ IflinkInfo.IFLA_INFO_DATA,
+ [
+ NlAttrU16(IfLinkInfoDataVlan.IFLA_VLAN_ID, 22),
+ ],
+ ),
+ ],
+ )
+ )
+
+ rx_msg = self.get_reply(msg)
+ assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
+ assert rx_msg.error_code == 0
+
+ ToolsHelper.print_net_debug()
+ self.get_interface_byname("vlan22")
+ # ToolsHelper.print_net_debug()
diff --git a/tests/sys/netlink/test_rtnl_ifaddr.py b/tests/sys/netlink/test_rtnl_ifaddr.py
new file mode 100644
index 000000000000..768bf38153ff
--- /dev/null
+++ b/tests/sys/netlink/test_rtnl_ifaddr.py
@@ -0,0 +1,762 @@
+import ipaddress
+import socket
+import struct
+
+import pytest
+from atf_python.sys.net.vnet import SingleVnetTestTemplate
+from atf_python.sys.netlink.attrs import NlAttr
+from atf_python.sys.netlink.attrs import NlAttrIp
+from atf_python.sys.netlink.attrs import NlAttrNested
+from atf_python.sys.netlink.attrs import NlAttrU32
+from atf_python.sys.netlink.base_headers import NlmBaseFlags
+from atf_python.sys.netlink.base_headers import NlmNewFlags
+from atf_python.sys.netlink.base_headers import Nlmsghdr
+from atf_python.sys.netlink.message import NlMsgType
+from atf_python.sys.netlink.netlink import NetlinkTestTemplate
+from atf_python.sys.netlink.netlink import Nlsock
+from atf_python.sys.netlink.netlink_generic import CarpAttrType
+from atf_python.sys.netlink.netlink_generic import CarpGenMessage
+from atf_python.sys.netlink.netlink_generic import CarpMsgType
+from atf_python.sys.netlink.netlink_route import IfaAttrType
+from atf_python.sys.netlink.netlink_route import IfaCacheInfo
+from atf_python.sys.netlink.netlink_route import IfafAttrType
+from atf_python.sys.netlink.netlink_route import IfafFlags6
+from atf_python.sys.netlink.netlink_route import IfaFlags
+from atf_python.sys.netlink.netlink_route import NetlinkIfaMessage
+from atf_python.sys.netlink.netlink_route import NlRtMsgType
+from atf_python.sys.netlink.netlink_route import RtScope
+from atf_python.sys.netlink.utils import enum_or_int
+from atf_python.sys.netlink.utils import NlConst
+
+
+class TestRtNlIfaddrList(NetlinkTestTemplate, SingleVnetTestTemplate):
+ def setup_method(self, method):
+ method_name = method.__name__
+ if "4" in method_name:
+ if "nofilter" in method_name:
+ self.IPV4_PREFIXES = ["192.0.2.1/24", "169.254.169.254/16"]
+ else:
+ self.IPV4_PREFIXES = ["192.0.2.1/24"]
+ if "6" in method_name:
+ self.IPV6_PREFIXES = ["2001:db8::1/64"]
+ super().setup_method(method)
+ self.setup_netlink(NlConst.NETLINK_ROUTE)
+
+ def test_46_nofilter(self):
+ """Tests that listing outputs both IPv4/IPv6 and interfaces"""
+ msg = NetlinkIfaMessage(self.helper, NlRtMsgType.RTM_GETADDR.value)
+ msg.set_request()
+ self.write_message(msg)
+
+ ret = []
+ for rx_msg in self.read_msg_list(msg.nl_hdr.nlmsg_seq, NlRtMsgType.RTM_NEWADDR):
+ ifname = socket.if_indextoname(rx_msg.base_hdr.ifa_index)
+ family = rx_msg.base_hdr.ifa_family
+ scope = rx_msg.base_hdr.ifa_scope
+ ret.append((ifname, family, scope))
+
+ ifname = "lo0"
+ assert len([r for r in ret if r[0] == ifname and r[1] == socket.AF_INET and r[2] == RtScope.RT_SCOPE_HOST.value]) == 1
+ assert len([r for r in ret if r[0] == ifname and r[1] == socket.AF_INET6 and r[2] == RtScope.RT_SCOPE_HOST.value]) == 1
+ assert len([r for r in ret if r[0] == ifname and r[1] == socket.AF_INET6 and r[2] == RtScope.RT_SCOPE_LINK.value]) == 1
+ assert len([r for r in ret if r[0] == ifname]) == 3
+
+ ifname = self.vnet.iface_alias_map["if1"].name
+ assert len([r for r in ret if r[0] == ifname and r[1] == socket.AF_INET and r[2] == RtScope.RT_SCOPE_LINK.value]) == 1
+ assert len([r for r in ret if r[0] == ifname and r[1] == socket.AF_INET and r[2] == RtScope.RT_SCOPE_UNIVERSE.value]) == 1
+ assert len([r for r in ret if r[0] == ifname and r[1] == socket.AF_INET6 and r[2] == RtScope.RT_SCOPE_LINK.value]) == 1
+ assert len([r for r in ret if r[0] == ifname and r[1] == socket.AF_INET6 and r[2] == RtScope.RT_SCOPE_UNIVERSE.value]) == 1
+ assert len([r for r in ret if r[0] == ifname]) == 4
+
+ def test_46_filter_iface(self):
+ """Tests that listing outputs both IPv4/IPv6 for the specific interface"""
+ epair_ifname = self.vnet.iface_alias_map["if1"].name
+
+ msg = NetlinkIfaMessage(self.helper, NlRtMsgType.RTM_GETADDR.value)
+ msg.set_request()
+ msg.base_hdr.ifa_index = socket.if_nametoindex(epair_ifname)
+ self.write_message(msg)
+
+ ret = []
+ for rx_msg in self.read_msg_list(msg.nl_hdr.nlmsg_seq, NlRtMsgType.RTM_NEWADDR):
+ ifname = socket.if_indextoname(rx_msg.base_hdr.ifa_index)
+ family = rx_msg.base_hdr.ifa_family
+ ret.append((ifname, family, rx_msg))
+
+ ifname = epair_ifname
+ assert len([r for r in ret if r[0] == ifname and r[1] == socket.AF_INET]) == 1
+ assert len([r for r in ret if r[0] == ifname and r[1] == socket.AF_INET6]) == 2
+ assert len(ret) == 3
+
+ def test_46_filter_family_compat(self):
+ """Tests that family filtering works with the stripped header"""
+
+ hdr = Nlmsghdr(
+ nlmsg_len=17,
+ nlmsg_type=NlRtMsgType.RTM_GETADDR.value,
+ nlmsg_flags=NlmBaseFlags.NLM_F_ACK.value | NlmBaseFlags.NLM_F_REQUEST.value,
+ nlmsg_seq=self.helper.get_seq(),
+ )
+ data = bytes(hdr) + struct.pack("@B", socket.AF_INET)
+ self.nlsock.write_data(data)
+
+ ret = []
+ for rx_msg in self.read_msg_list(hdr.nlmsg_seq, NlRtMsgType.RTM_NEWADDR):
+ ifname = socket.if_indextoname(rx_msg.base_hdr.ifa_index)
+ family = rx_msg.base_hdr.ifa_family
+ ret.append((ifname, family, rx_msg))
+ assert len(ret) == 2
+
+ def filter_iface_family(self, family, num_items):
+ """Tests that listing outputs IPv4 for the specific interface"""
+ epair_ifname = self.vnet.iface_alias_map["if1"].name
+
+ msg = NetlinkIfaMessage(self.helper, NlRtMsgType.RTM_GETADDR.value)
+ msg.set_request()
+ msg.base_hdr.ifa_family = family
+ msg.base_hdr.ifa_index = socket.if_nametoindex(epair_ifname)
+ self.write_message(msg)
+
+ ret = []
+ for rx_msg in self.read_msg_list(msg.nl_hdr.nlmsg_seq, NlRtMsgType.RTM_NEWADDR):
+ assert family == rx_msg.base_hdr.ifa_family
+ assert epair_ifname == socket.if_indextoname(rx_msg.base_hdr.ifa_index)
+ ret.append(rx_msg)
+ assert len(ret) == num_items
+ return ret
+
+ def test_4_broadcast(self):
+ """Tests header/attr output for listing IPv4 ifas on broadcast iface"""
+ ret = self.filter_iface_family(socket.AF_INET, 1)
+ # Should be 192.0.2.1/24
+ msg = ret[0]
+ # Family and ifindex has been checked already
+ assert msg.base_hdr.ifa_prefixlen == 24
+ # Ignore IFA_FLAGS for now
+ assert msg.base_hdr.ifa_scope == RtScope.RT_SCOPE_UNIVERSE.value
+
+ assert msg.get_nla(IfaAttrType.IFA_ADDRESS).addr == "192.0.2.1"
+ assert msg.get_nla(IfaAttrType.IFA_LOCAL).addr == "192.0.2.1"
+ assert msg.get_nla(IfaAttrType.IFA_BROADCAST).addr == "192.0.2.255"
+
+ epair_ifname = self.vnet.iface_alias_map["if1"].name
+ assert msg.get_nla(IfaAttrType.IFA_LABEL).text == epair_ifname
+
+ def test_6_broadcast(self):
+ """Tests header/attr output for listing IPv6 ifas on broadcast iface"""
+ ret = self.filter_iface_family(socket.AF_INET6, 2)
+ # Should be 192.0.2.1/24
+ if ret[0].base_hdr.ifa_scope == RtScope.RT_SCOPE_UNIVERSE.value:
+ (gmsg, lmsg) = ret
+ else:
+ (lmsg, gmsg) = ret
+ # Start with global ( 2001:db8::1/64 )
+ msg = gmsg
+ # Family and ifindex has been checked already
+ assert msg.base_hdr.ifa_prefixlen == 64
+ # Ignore IFA_FLAGS for now
+ assert msg.base_hdr.ifa_scope == RtScope.RT_SCOPE_UNIVERSE.value
+
+ assert msg.get_nla(IfaAttrType.IFA_ADDRESS).addr == "2001:db8::1"
+ assert msg.get_nla(IfaAttrType.IFA_LOCAL) is None
+ assert msg.get_nla(IfaAttrType.IFA_BROADCAST) is None
+
+ epair_ifname = self.vnet.iface_alias_map["if1"].name
+ assert msg.get_nla(IfaAttrType.IFA_LABEL).text == epair_ifname
+
+ # Local: fe80::/64
+ msg = lmsg
+ assert msg.base_hdr.ifa_prefixlen == 64
+ # Ignore IFA_FLAGS for now
+ assert msg.base_hdr.ifa_scope == RtScope.RT_SCOPE_LINK.value
+
+ addr = ipaddress.ip_address(msg.get_nla(IfaAttrType.IFA_ADDRESS).addr)
+ assert addr.is_link_local
+ # Verify that ifindex is not emmbedded
+ assert struct.unpack("!H", addr.packed[2:4])[0] == 0
+ assert msg.get_nla(IfaAttrType.IFA_LOCAL) is None
+ assert msg.get_nla(IfaAttrType.IFA_BROADCAST) is None
+
+ epair_ifname = self.vnet.iface_alias_map["if1"].name
+ assert msg.get_nla(IfaAttrType.IFA_LABEL).text == epair_ifname
+
+
+class RtnlIfaOps(NetlinkTestTemplate, SingleVnetTestTemplate):
+ def setup_method(self, method):
+ super().setup_method(method)
+ self.setup_netlink(NlConst.NETLINK_ROUTE)
+
+ def send_check_success(self, msg):
+ rx_msg = self.get_reply(msg)
+ assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
+ assert rx_msg.error_code == 0
+
+ @staticmethod
+ def get_family_from_ip(ip):
+ if ip.version == 4:
+ return socket.AF_INET
+ return socket.AF_INET6
+
+ def create_msg(self, ifa):
+ iface = self.vnet.iface_alias_map["if1"]
+
+ msg = NetlinkIfaMessage(self.helper, NlRtMsgType.RTM_NEWADDR.value)
+ msg.set_request()
+ msg.nl_hdr.nlmsg_flags |= (
+ NlmNewFlags.NLM_F_EXCL.value | NlmNewFlags.NLM_F_CREATE.value
+ )
+ msg.base_hdr.ifa_family = self.get_family_from_ip(ifa.ip)
+ msg.base_hdr.ifa_index = iface.ifindex
+ msg.base_hdr.ifa_prefixlen = ifa.network.prefixlen
+ return msg
+
+ def get_ifa_list(self, ifindex=0, family=0):
+ msg = NetlinkIfaMessage(self.helper, NlRtMsgType.RTM_GETADDR.value)
+ msg.set_request()
+ msg.base_hdr.ifa_family = family
+ msg.base_hdr.ifa_index = ifindex
+ self.write_message(msg)
+ return self.read_msg_list(msg.nl_hdr.nlmsg_seq, NlRtMsgType.RTM_NEWADDR)
+
+ def find_msg_by_ifa(self, msg_list, ip):
+ for msg in msg_list:
+ if msg.get_nla(IfaAttrType.IFA_ADDRESS).addr == str(ip):
+ return msg
+ return None
+
+ def setup_dummy_carp(self, ifindex: int, vhid: int):
+ self.require_module("carp")
+
+ nlsock = Nlsock(NlConst.NETLINK_GENERIC, self.helper)
+ family_id = nlsock.get_genl_family_id("carp")
+
+ msg = CarpGenMessage(self.helper, family_id, CarpMsgType.CARP_NL_CMD_SET)
+ msg.set_request()
+ msg.add_nla(NlAttrU32(CarpAttrType.CARP_NL_VHID, vhid))
+ msg.add_nla(NlAttrU32(CarpAttrType.CARP_NL_IFINDEX, ifindex))
+ rx_msg = nlsock.get_reply(msg)
+
+ assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
+ assert rx_msg.error_code == 0
+
+
+class TestRtNlIfaddrOpsBroadcast(RtnlIfaOps):
+ def test_add_4(self):
+ """Tests IPv4 address addition to the standard broadcast interface"""
+ ifa = ipaddress.ip_interface("192.0.2.1/24")
+ ifa_brd = ifa.network.broadcast_address
+ iface = self.vnet.iface_alias_map["if1"]
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_BROADCAST, str(ifa_brd)))
+
+ self.send_check_success(msg)
+
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ assert len(lst) == 1
+ rx_msg = lst[0]
+
+ assert rx_msg.base_hdr.ifa_prefixlen == ifa.network.prefixlen
+ assert rx_msg.base_hdr.ifa_scope == RtScope.RT_SCOPE_UNIVERSE.value
+
+ assert rx_msg.get_nla(IfaAttrType.IFA_ADDRESS).addr == str(ifa.ip)
+ assert rx_msg.get_nla(IfaAttrType.IFA_LOCAL).addr == str(ifa.ip)
+ assert rx_msg.get_nla(IfaAttrType.IFA_BROADCAST).addr == str(ifa_brd)
+
+ @pytest.mark.parametrize(
+ "brd",
+ [
+ pytest.param((32, True, "192.0.2.1"), id="auto_32"),
+ pytest.param((31, True, "255.255.255.255"), id="auto_31"),
+ pytest.param((30, True, "192.0.2.3"), id="auto_30"),
+ pytest.param((30, False, "192.0.2.2"), id="custom_30"),
+ pytest.param((24, False, "192.0.2.7"), id="custom_24"),
+ ],
+ )
+ def test_add_4_brd(self, brd):
+ """Tests proper broadcast setup when adding IPv4 ifa"""
+ plen, auto_brd, ifa_brd_str = brd
+ ifa = ipaddress.ip_interface("192.0.2.1/{}".format(plen))
+ iface = self.vnet.iface_alias_map["if1"]
+ ifa_brd = ipaddress.ip_address(ifa_brd_str)
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+ if not auto_brd:
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_BROADCAST, str(ifa_brd)))
+
+ self.send_check_success(msg)
+
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ assert len(lst) == 1
+ rx_msg = lst[0]
+
+ assert rx_msg.base_hdr.ifa_prefixlen == ifa.network.prefixlen
+ assert rx_msg.base_hdr.ifa_scope == RtScope.RT_SCOPE_UNIVERSE.value
+
+ assert rx_msg.get_nla(IfaAttrType.IFA_ADDRESS).addr == str(ifa.ip)
+ assert rx_msg.get_nla(IfaAttrType.IFA_LOCAL).addr == str(ifa.ip)
+ assert rx_msg.get_nla(IfaAttrType.IFA_BROADCAST).addr == str(ifa_brd)
+
+ def test_add_6(self):
+ ifa = ipaddress.ip_interface("2001:db8::1/64")
+ iface = self.vnet.iface_alias_map["if1"]
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+
+ self.send_check_success(msg)
+
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ assert len(lst) == 2
+ rx_msg_gu = self.find_msg_by_ifa(lst, ifa.ip)
+ assert rx_msg_gu is not None
+
+ assert rx_msg_gu.base_hdr.ifa_prefixlen == ifa.network.prefixlen
+ assert rx_msg_gu.base_hdr.ifa_scope == RtScope.RT_SCOPE_UNIVERSE.value
+ assert rx_msg_gu.get_nla(IfaAttrType.IFA_ADDRESS).addr == str(ifa.ip)
+
+ def test_add_4_carp(self):
+ ifa = ipaddress.ip_interface("192.0.2.1/24")
+ ifa_brd = ifa.network.broadcast_address
+ iface = self.vnet.iface_alias_map["if1"]
+ vhid = 77
+
+ self.setup_dummy_carp(iface.ifindex, vhid)
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_BROADCAST, str(ifa_brd)))
+ attrs_bsd = [NlAttrU32(IfafAttrType.IFAF_VHID, vhid)]
+ msg.add_nla(NlAttrNested(IfaAttrType.IFA_FREEBSD, attrs_bsd))
+
+ self.send_check_success(msg)
+
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ assert len(lst) == 1
+ rx_msg = lst[0]
+
+ assert rx_msg.base_hdr.ifa_prefixlen == ifa.network.prefixlen
+ assert rx_msg.base_hdr.ifa_scope == RtScope.RT_SCOPE_UNIVERSE.value
+
+ assert rx_msg.get_nla(IfaAttrType.IFA_ADDRESS).addr == str(ifa.ip)
+ assert rx_msg.get_nla(IfaAttrType.IFA_LOCAL).addr == str(ifa.ip)
+ assert rx_msg.get_nla(IfaAttrType.IFA_BROADCAST).addr == str(ifa_brd)
+ ifa_bsd = rx_msg.get_nla(IfaAttrType.IFA_FREEBSD)
+ assert ifa_bsd.get_nla(IfafAttrType.IFAF_VHID).u32 == vhid
+
+ def test_add_6_carp(self):
+ ifa = ipaddress.ip_interface("2001:db8::1/64")
+ iface = self.vnet.iface_alias_map["if1"]
+ vhid = 77
+
+ self.setup_dummy_carp(iface.ifindex, vhid)
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+ attrs_bsd = [NlAttrU32(IfafAttrType.IFAF_VHID, vhid)]
+ msg.add_nla(NlAttrNested(IfaAttrType.IFA_FREEBSD, attrs_bsd))
+
+ self.send_check_success(msg)
+
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ assert len(lst) == 2
+ rx_msg_gu = self.find_msg_by_ifa(lst, ifa.ip)
+ assert rx_msg_gu is not None
+
+ assert rx_msg_gu.base_hdr.ifa_prefixlen == ifa.network.prefixlen
+ assert rx_msg_gu.base_hdr.ifa_scope == RtScope.RT_SCOPE_UNIVERSE.value
+ assert rx_msg_gu.get_nla(IfaAttrType.IFA_ADDRESS).addr == str(ifa.ip)
+ ifa_bsd = rx_msg_gu.get_nla(IfaAttrType.IFA_FREEBSD)
+ assert ifa_bsd.get_nla(IfafAttrType.IFAF_VHID).u32 == vhid
+
+ def test_add_6_lifetime(self):
+ ifa = ipaddress.ip_interface("2001:db8::1/64")
+ iface = self.vnet.iface_alias_map["if1"]
+ pref_time = 43200
+ valid_time = 86400
+
+ ci = IfaCacheInfo(ifa_prefered=pref_time, ifa_valid=valid_time)
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+ msg.add_nla(NlAttr(IfaAttrType.IFA_CACHEINFO, bytes(ci)))
+
+ self.send_check_success(msg)
+
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ assert len(lst) == 2
+ rx_msg = self.find_msg_by_ifa(lst, ifa.ip)
+ assert rx_msg is not None
+
+ ci = rx_msg.get_nla(IfaAttrType.IFA_CACHEINFO).ci
+ assert pref_time - 5 <= ci.ifa_prefered <= pref_time
+ assert valid_time - 5 <= ci.ifa_valid <= valid_time
+ assert ci.cstamp > 0
+ assert ci.tstamp > 0
+ assert ci.tstamp >= ci.cstamp
+
+ @pytest.mark.parametrize(
+ "flags_str",
+ [
+ "autoconf",
+ "deprecated",
+ "autoconf,deprecated",
+ "prefer_source",
+ ],
+ )
+ def test_add_6_flags(self, flags_str):
+ ifa = ipaddress.ip_interface("2001:db8::1/64")
+ iface = self.vnet.iface_alias_map["if1"]
+
+ flags_map = {
+ "autoconf": {"nl": 0, "f": IfafFlags6.IN6_IFF_AUTOCONF},
+ "deprecated": {
+ "nl": IfaFlags.IFA_F_DEPRECATED,
+ "f": IfafFlags6.IN6_IFF_DEPRECATED,
+ },
+ "prefer_source": {"nl": 0, "f": IfafFlags6.IN6_IFF_PREFER_SOURCE},
+ }
+ nl_flags = 0
+ f_flags = 0
+
+ for flag_str in flags_str.split(","):
+ d = flags_map.get(flag_str, {})
+ nl_flags |= enum_or_int(d.get("nl", 0))
+ f_flags |= enum_or_int(d.get("f", 0))
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+ msg.add_nla(NlAttrU32(IfaAttrType.IFA_FLAGS, nl_flags))
+ attrs_bsd = [NlAttrU32(IfafAttrType.IFAF_FLAGS, f_flags)]
+ msg.add_nla(NlAttrNested(IfaAttrType.IFA_FREEBSD, attrs_bsd))
+
+ self.send_check_success(msg)
+
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ assert len(lst) == 2
+ rx_msg = self.find_msg_by_ifa(lst, ifa.ip)
+ assert rx_msg is not None
+
+ assert rx_msg.get_nla(IfaAttrType.IFA_FLAGS).u32 == nl_flags
+ ifa_bsd = rx_msg.get_nla(IfaAttrType.IFA_FREEBSD)
+ assert ifa_bsd.get_nla(IfafAttrType.IFAF_FLAGS).u32 == f_flags
+
+ def test_add_4_empty_message(self):
+ """Tests correct failure w/ empty message"""
+ msg = NetlinkIfaMessage(self.helper, NlRtMsgType.RTM_NEWADDR.value)
+ msg.set_request()
+ msg.nl_hdr.nlmsg_flags |= (
+ NlmNewFlags.NLM_F_EXCL.value | NlmNewFlags.NLM_F_CREATE.value
+ )
+
+ rx_msg = self.get_reply(msg)
+ assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
+ assert rx_msg.error_code != 0
+
+ def test_add_4_empty_ifindex(self):
+ """Tests correct failure w/ empty ifindex"""
+ ifa = ipaddress.ip_interface("192.0.2.1/24")
+ ifa_brd = ifa.network.broadcast_address
+
+ msg = self.create_msg(ifa)
+ msg.base_hdr.ifa_index = 0
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_BROADCAST, str(ifa_brd)))
+
+ rx_msg = self.get_reply(msg)
+ assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
+ assert rx_msg.error_code != 0
+
+ def test_add_4_empty_addr(self):
+ """Tests correct failure w/ empty address"""
+ ifa = ipaddress.ip_interface("192.0.2.1/24")
+ ifa_brd = ifa.network.broadcast_address
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_BROADCAST, str(ifa_brd)))
+
+ rx_msg = self.get_reply(msg)
+ assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
+ assert rx_msg.error_code != 0
+
+ @pytest.mark.parametrize(
+ "ifa_str",
+ [
+ pytest.param("192.0.2.1/32", id="ipv4_host"),
+ pytest.param("192.0.2.1/24", id="ipv4_prefix"),
+ pytest.param("2001:db8::1/64", id="ipv6_gu_prefix"),
+ pytest.param("2001:db8::1/128", id="ipv6_gu_host"),
+ ],
+ )
+ @pytest.mark.parametrize(
+ "tlv",
+ [
+ pytest.param("local", id="ifa_local"),
+ pytest.param("address", id="ifa_address"),
+ ],
+ )
+ def test_del(self, tlv, ifa_str):
+ """Tests address deletion from the standard broadcast interface"""
+ ifa = ipaddress.ip_interface(ifa_str)
+ ifa_brd = ifa.network.broadcast_address
+ iface = self.vnet.iface_alias_map["if1"]
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_BROADCAST, str(ifa_brd)))
+
+ self.send_check_success(msg)
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ rx_msg = self.find_msg_by_ifa(lst, ifa.ip)
+ assert rx_msg is not None
+
+ msg = NetlinkIfaMessage(self.helper, NlRtMsgType.RTM_DELADDR.value)
+ msg.set_request()
+ msg.base_hdr.ifa_family = self.get_family_from_ip(ifa.ip)
+ msg.base_hdr.ifa_index = iface.ifindex
+ msg.base_hdr.ifa_prefixlen = ifa.network.prefixlen
+
+ if tlv == "local":
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+ if tlv == "address":
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_ADDRESS, str(ifa.ip)))
+
+ self.send_check_success(msg)
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ rx_msg = self.find_msg_by_ifa(lst, ifa.ip)
+ assert rx_msg is None
+
+
+class TestRtNlIfaddrOpsP2p(RtnlIfaOps):
+ IFTYPE = "gif"
+
+ @pytest.mark.parametrize(
+ "ifa_pair",
+ [
+ pytest.param(["192.0.2.1/24", "192.0.2.2"], id="dst_inside_24"),
+ pytest.param(["192.0.2.1/30", "192.0.2.2"], id="dst_inside_30"),
+ pytest.param(["192.0.2.1/31", "192.0.2.2"], id="dst_inside_31"),
+ pytest.param(["192.0.2.1/32", "192.0.2.2"], id="dst_outside_32"),
+ pytest.param(["192.0.2.1/30", "192.0.2.100"], id="dst_outside_30"),
+ ],
+ )
+ def test_add_4(self, ifa_pair):
+ """Tests IPv4 address addition to the p2p interface"""
+ ifa = ipaddress.ip_interface(ifa_pair[0])
+ peer_ip = ipaddress.ip_address(ifa_pair[1])
+ iface = self.vnet.iface_alias_map["if1"]
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_ADDRESS, str(peer_ip)))
+
+ self.send_check_success(msg)
+
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ assert len(lst) == 1
+ rx_msg = lst[0]
+
+ assert rx_msg.base_hdr.ifa_prefixlen == ifa.network.prefixlen
+ assert rx_msg.base_hdr.ifa_scope == RtScope.RT_SCOPE_UNIVERSE.value
+
+ assert rx_msg.get_nla(IfaAttrType.IFA_LOCAL).addr == str(ifa.ip)
+ assert rx_msg.get_nla(IfaAttrType.IFA_ADDRESS).addr == str(peer_ip)
+
+ @pytest.mark.parametrize(
+ "ifa_pair",
+ [
+ pytest.param(
+ ["2001:db8::1/64", "2001:db8::2"],
+ id="dst_inside_64",
+ marks=pytest.mark.xfail(reason="currently fails"),
+ ),
+ pytest.param(
+ ["2001:db8::1/127", "2001:db8::2"],
+ id="dst_inside_127",
+ marks=pytest.mark.xfail(reason="currently fails"),
+ ),
+ pytest.param(["2001:db8::1/128", "2001:db8::2"], id="dst_outside_128"),
+ pytest.param(
+ ["2001:db8::1/64", "2001:db8:2::2"],
+ id="dst_outside_64",
+ marks=pytest.mark.xfail(reason="currently fails"),
+ ),
+ ],
+ )
+ def test_add_6(self, ifa_pair):
+ """Tests IPv6 address addition to the p2p interface"""
+ ifa = ipaddress.ip_interface(ifa_pair[0])
+ peer_ip = ipaddress.ip_address(ifa_pair[1])
+ iface = self.vnet.iface_alias_map["if1"]
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_ADDRESS, str(peer_ip)))
+
+ self.send_check_success(msg)
+
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ assert len(lst) == 2
+ rx_msg_gu = self.find_msg_by_ifa(lst, peer_ip)
+ assert rx_msg_gu is not None
+
+ assert rx_msg_gu.base_hdr.ifa_prefixlen == ifa.network.prefixlen
+ assert rx_msg_gu.base_hdr.ifa_scope == RtScope.RT_SCOPE_UNIVERSE.value
+ assert rx_msg_gu.get_nla(IfaAttrType.IFA_LOCAL).addr == str(ifa.ip)
+ assert rx_msg_gu.get_nla(IfaAttrType.IFA_ADDRESS).addr == str(peer_ip)
+
+ @pytest.mark.parametrize(
+ "ifa_pair",
+ [
+ pytest.param(["192.0.2.1/30", "192.0.2.2"], id="ipv4_dst_inside_30"),
+ pytest.param(["192.0.2.1/32", "192.0.2.2"], id="ipv4_dst_outside_32"),
+ pytest.param(["2001:db8::1/128", "2001:db8::2"], id="ip6_dst_outside_128"),
+ ],
+ )
+ @pytest.mark.parametrize(
+ "tlv_pair",
+ [
+ pytest.param(["a", ""], id="ifa_addr=addr"),
+ pytest.param(["", "a"], id="ifa_local=addr"),
+ pytest.param(["a", "a"], id="ifa_addr=addr,ifa_local=addr"),
+ ],
+ )
+ def test_del(self, tlv_pair, ifa_pair):
+ """Tests address deletion from the P2P interface"""
+ ifa = ipaddress.ip_interface(ifa_pair[0])
+ peer_ip = ipaddress.ip_address(ifa_pair[1])
+ iface = self.vnet.iface_alias_map["if1"]
+ ifa_addr_str, ifa_local_str = tlv_pair
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_ADDRESS, str(peer_ip)))
+
+ self.send_check_success(msg)
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ rx_msg = self.find_msg_by_ifa(lst, peer_ip)
+ assert rx_msg is not None
+
+ msg = NetlinkIfaMessage(self.helper, NlRtMsgType.RTM_DELADDR.value)
+ msg.set_request()
+ msg.base_hdr.ifa_family = self.get_family_from_ip(ifa.ip)
+ msg.base_hdr.ifa_index = iface.ifindex
+ msg.base_hdr.ifa_prefixlen = ifa.network.prefixlen
+
+ if "a" in ifa_addr_str:
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_ADDRESS, str(ifa.ip)))
+ if "p" in ifa_addr_str:
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_ADDRESS, str(peer_ip)))
+ if "a" in ifa_local_str:
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+ if "p" in ifa_local_str:
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(peer_ip)))
+
+ self.send_check_success(msg)
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ rx_msg = self.find_msg_by_ifa(lst, ifa.ip)
+ assert rx_msg is None
+
+
+class TestRtNlAddIfaddrLo(RtnlIfaOps):
+ IFTYPE = "lo"
+
+ @pytest.mark.parametrize(
+ "ifa_str",
+ [
+ pytest.param("192.0.2.1/24", id="prefix"),
+ pytest.param("192.0.2.1/32", id="host"),
+ ],
+ )
+ def test_add_4(self, ifa_str):
+ """Tests IPv4 address addition to the loopback interface"""
+ ifa = ipaddress.ip_interface(ifa_str)
+ iface = self.vnet.iface_alias_map["if1"]
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+
+ self.send_check_success(msg)
+
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ assert len(lst) == 1
+ rx_msg = lst[0]
+
+ assert rx_msg.base_hdr.ifa_prefixlen == ifa.network.prefixlen
+ assert rx_msg.base_hdr.ifa_scope == RtScope.RT_SCOPE_UNIVERSE.value
+
+ assert rx_msg.get_nla(IfaAttrType.IFA_ADDRESS).addr == str(ifa.ip)
+ assert rx_msg.get_nla(IfaAttrType.IFA_LOCAL).addr == str(ifa.ip)
+
+ @pytest.mark.parametrize(
+ "ifa_str",
+ [
+ pytest.param("2001:db8::1/64", id="gu_prefix"),
+ pytest.param("2001:db8::1/128", id="gu_host"),
+ ],
+ )
+ def test_add_6(self, ifa_str):
+ """Tests IPv6 address addition to the loopback interface"""
+ ifa = ipaddress.ip_interface(ifa_str)
+ iface = self.vnet.iface_alias_map["if1"]
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+
+ self.send_check_success(msg)
+
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ assert len(lst) == 2 # link-local should be auto-created as well
+ rx_msg = self.find_msg_by_ifa(lst, ifa.ip)
+ assert rx_msg is not None
+
+ assert rx_msg.base_hdr.ifa_prefixlen == ifa.network.prefixlen
+ assert rx_msg.base_hdr.ifa_scope == RtScope.RT_SCOPE_UNIVERSE.value
+ assert rx_msg.get_nla(IfaAttrType.IFA_ADDRESS).addr == str(ifa.ip)
+
+ @pytest.mark.parametrize(
+ "ifa_str",
+ [
+ pytest.param("192.0.2.1/32", id="ipv4_host"),
+ pytest.param("192.0.2.1/24", id="ipv4_prefix"),
+ pytest.param("2001:db8::1/64", id="ipv6_gu_prefix"),
+ pytest.param("2001:db8::1/128", id="ipv6_gu_host"),
+ ],
+ )
+ @pytest.mark.parametrize(
+ "tlv",
+ [
+ pytest.param("local", id="ifa_local"),
+ pytest.param("address", id="ifa_address"),
+ ],
+ )
+ def test_del(self, tlv, ifa_str):
+ """Tests address deletion from the loopback interface"""
+ ifa = ipaddress.ip_interface(ifa_str)
+ iface = self.vnet.iface_alias_map["if1"]
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+
+ self.send_check_success(msg)
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ rx_msg = self.find_msg_by_ifa(lst, ifa.ip)
+ assert rx_msg is not None
+
+ msg = NetlinkIfaMessage(self.helper, NlRtMsgType.RTM_DELADDR.value)
+ msg.set_request()
+ msg.base_hdr.ifa_family = self.get_family_from_ip(ifa.ip)
+ msg.base_hdr.ifa_index = iface.ifindex
+ msg.base_hdr.ifa_prefixlen = ifa.network.prefixlen
+
+ if tlv == "local":
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+ if tlv == "address":
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_ADDRESS, str(ifa.ip)))
+
+ self.send_check_success(msg)
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ rx_msg = self.find_msg_by_ifa(lst, ifa.ip)
+ assert rx_msg is None
diff --git a/tests/sys/netlink/test_rtnl_neigh.py b/tests/sys/netlink/test_rtnl_neigh.py
new file mode 100644
index 000000000000..aab682d1d8a4
--- /dev/null
+++ b/tests/sys/netlink/test_rtnl_neigh.py
@@ -0,0 +1,53 @@
+import socket
+
+import pytest
+from atf_python.sys.net.vnet import SingleVnetTestTemplate
+from atf_python.sys.netlink.netlink import NetlinkTestTemplate
+from atf_python.sys.netlink.netlink_route import NdAttrType
+from atf_python.sys.netlink.netlink_route import NetlinkNdMessage
+from atf_python.sys.netlink.netlink_route import NlRtMsgType
+from atf_python.sys.netlink.utils import NlConst
+
+
+class TestRtNlNeigh(NetlinkTestTemplate, SingleVnetTestTemplate):
+ def setup_method(self, method):
+ method_name = method.__name__
+ if "4" in method_name:
+ self.IPV4_PREFIXES = ["192.0.2.1/24"]
+ if "6" in method_name:
+ self.IPV6_PREFIXES = ["2001:db8::1/64"]
+ super().setup_method(method)
+ self.setup_netlink(NlConst.NETLINK_ROUTE)
+
+ def filter_iface(self, family, num_items):
+ epair_ifname = self.vnet.iface_alias_map["if1"].name
+ epair_ifindex = socket.if_nametoindex(epair_ifname)
+
+ msg = NetlinkNdMessage(self.helper, NlRtMsgType.RTM_GETNEIGH)
+ msg.set_request()
+ msg.base_hdr.ndm_family = family
+ msg.base_hdr.ndm_ifindex = epair_ifindex
+ self.write_message(msg)
+
+ ret = []
+ for rx_msg in self.read_msg_list(
+ msg.nl_hdr.nlmsg_seq, NlRtMsgType.RTM_NEWNEIGH
+ ):
+ ifname = socket.if_indextoname(rx_msg.base_hdr.ndm_ifindex)
+ family = rx_msg.base_hdr.ndm_family
+ assert ifname == epair_ifname
+ assert family == family
+ assert rx_msg.get_nla(NdAttrType.NDA_DST) is not None
+ assert rx_msg.get_nla(NdAttrType.NDA_LLADDR) is not None
+ ret.append(rx_msg)
+ assert len(ret) == num_items
+
+ @pytest.mark.timeout(5)
+ def test_6_filter_iface(self):
+ """Tests that listing outputs all nd6 records"""
+ return self.filter_iface(socket.AF_INET6, 2)
+
+ @pytest.mark.timeout(5)
+ def test_4_filter_iface(self):
+ """Tests that listing outputs all arp records"""
+ return self.filter_iface(socket.AF_INET, 1)
diff --git a/tests/sys/netlink/test_rtnl_route.py b/tests/sys/netlink/test_rtnl_route.py
new file mode 100644
index 000000000000..370c8a74a2de
--- /dev/null
+++ b/tests/sys/netlink/test_rtnl_route.py
@@ -0,0 +1,142 @@
+import ipaddress
+import socket
+
+import pytest
+from atf_python.sys.net.tools import ToolsHelper
+from atf_python.sys.net.vnet import IfaceFactory
+from atf_python.sys.net.vnet import SingleVnetTestTemplate
+from atf_python.sys.netlink.attrs import NlAttrIp
+from atf_python.sys.netlink.attrs import NlAttrU32
+from atf_python.sys.netlink.base_headers import NlmBaseFlags
+from atf_python.sys.netlink.base_headers import NlmGetFlags
+from atf_python.sys.netlink.base_headers import NlmNewFlags
+from atf_python.sys.netlink.base_headers import NlMsgType
+from atf_python.sys.netlink.netlink import NetlinkTestTemplate
+from atf_python.sys.netlink.netlink_route import NetlinkRtMessage
+from atf_python.sys.netlink.netlink_route import NlRtMsgType
+from atf_python.sys.netlink.netlink_route import RtattrType
+from atf_python.sys.netlink.utils import NlConst
+
+
+class TestRtNlRoute(NetlinkTestTemplate, SingleVnetTestTemplate):
+ IPV6_PREFIXES = ["2001:db8::1/64"]
+
+ def setup_method(self, method):
+ super().setup_method(method)
+ self.setup_netlink(NlConst.NETLINK_ROUTE)
+
+ @pytest.mark.timeout(5)
+ def test_add_route6_ll_gw(self):
+ epair_ifname = self.vnet.iface_alias_map["if1"].name
+ epair_ifindex = socket.if_nametoindex(epair_ifname)
+
+ msg = NetlinkRtMessage(self.helper, NlRtMsgType.RTM_NEWROUTE)
+ msg.set_request()
+ msg.add_nlflags([NlmNewFlags.NLM_F_CREATE])
+ msg.base_hdr.rtm_family = socket.AF_INET6
+ msg.base_hdr.rtm_dst_len = 64
+ msg.add_nla(NlAttrIp(RtattrType.RTA_DST, "2001:db8:2::"))
+ msg.add_nla(NlAttrIp(RtattrType.RTA_GATEWAY, "fe80::1"))
+ msg.add_nla(NlAttrU32(RtattrType.RTA_OIF, epair_ifindex))
+
+ rx_msg = self.get_reply(msg)
+ assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
+ assert rx_msg.error_code == 0
+
+ ToolsHelper.print_net_debug()
+ ToolsHelper.print_output("netstat -6onW")
+
+ @pytest.mark.timeout(5)
+ def test_add_route6_ll_if_gw(self):
+ self.require_module("if_tun")
+ tun_ifname = IfaceFactory().create_iface("", "tun")[0].name
+ tun_ifindex = socket.if_nametoindex(tun_ifname)
+
+ msg = NetlinkRtMessage(self.helper, NlRtMsgType.RTM_NEWROUTE)
+ msg.set_request()
+ msg.add_nlflags([NlmNewFlags.NLM_F_CREATE])
+ msg.base_hdr.rtm_family = socket.AF_INET6
+ msg.base_hdr.rtm_dst_len = 64
+ msg.add_nla(NlAttrIp(RtattrType.RTA_DST, "2001:db8:2::"))
+ msg.add_nla(NlAttrU32(RtattrType.RTA_OIF, tun_ifindex))
+
+ rx_msg = self.get_reply(msg)
+ assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
+ assert rx_msg.error_code == 0
+
+ ToolsHelper.print_net_debug()
+ ToolsHelper.print_output("netstat -6onW")
+
+ @pytest.mark.timeout(5)
+ def test_add_route4_ll_if_gw(self):
+ self.require_module("if_tun")
+ tun_ifname = IfaceFactory().create_iface("", "tun")[0].name
+ tun_ifindex = socket.if_nametoindex(tun_ifname)
+
+ msg = NetlinkRtMessage(self.helper, NlRtMsgType.RTM_NEWROUTE)
+ msg.set_request()
+ msg.add_nlflags([NlmNewFlags.NLM_F_CREATE])
+ msg.base_hdr.rtm_family = socket.AF_INET
+ msg.base_hdr.rtm_dst_len = 32
+ msg.add_nla(NlAttrIp(RtattrType.RTA_DST, "192.0.2.1"))
+ msg.add_nla(NlAttrU32(RtattrType.RTA_OIF, tun_ifindex))
+
+ rx_msg = self.get_reply(msg)
+ assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
+ assert rx_msg.error_code == 0
+
+ ToolsHelper.print_net_debug()
+ ToolsHelper.print_output("netstat -4onW")
+
+ @pytest.mark.timeout(20)
+ def test_buffer_override(self):
+ msg_flags = (
+ NlmBaseFlags.NLM_F_ACK.value
+ | NlmBaseFlags.NLM_F_REQUEST.value
+ | NlmNewFlags.NLM_F_CREATE.value
+ )
+
+ num_routes = 1000
+ base_address = bytearray(ipaddress.ip_address("2001:db8:ffff::").packed)
+ for i in range(num_routes):
+ base_address[7] = i % 256
+ base_address[6] = i // 256
+ prefix_address = ipaddress.IPv6Address(bytes(base_address))
+
+ msg = NetlinkRtMessage(self.helper, NlRtMsgType.RTM_NEWROUTE.value)
+ msg.nl_hdr.nlmsg_flags = msg_flags
+ msg.base_hdr.rtm_family = socket.AF_INET6
+ msg.base_hdr.rtm_dst_len = 65
+ msg.add_nla(NlAttrIp(RtattrType.RTA_DST, str(prefix_address)))
+ msg.add_nla(NlAttrIp(RtattrType.RTA_GATEWAY, "2001:db8::2"))
+
+ self.write_message(msg, silent=True)
+ rx_msg = self.read_message(silent=True)
+ assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
+ assert msg.nl_hdr.nlmsg_seq == rx_msg.nl_hdr.nlmsg_seq
+ assert rx_msg.error_code == 0
+ # Now, dump
+ msg = NetlinkRtMessage(self.helper, NlRtMsgType.RTM_GETROUTE.value)
+ msg.nl_hdr.nlmsg_flags = (
+ NlmBaseFlags.NLM_F_ACK.value
+ | NlmBaseFlags.NLM_F_REQUEST.value
+ | NlmGetFlags.NLM_F_ROOT.value
+ | NlmGetFlags.NLM_F_MATCH.value
+ )
+ msg.base_hdr.rtm_family = socket.AF_INET6
+ self.write_message(msg)
+ num_received = 0
+ while True:
+ rx_msg = self.read_message(silent=True)
+ if msg.nl_hdr.nlmsg_seq == rx_msg.nl_hdr.nlmsg_seq:
+ if rx_msg.is_type(NlMsgType.NLMSG_ERROR):
+ if rx_msg.error_code != 0:
+ raise ValueError(
+ "unable to dump routes: error {}".format(rx_msg.error_code)
+ )
+ if rx_msg.is_type(NlMsgType.NLMSG_DONE):
+ break
+ if rx_msg.is_type(NlRtMsgType.RTM_NEWROUTE):
+ if rx_msg.base_hdr.rtm_dst_len == 65:
+ num_received += 1
+ assert num_routes == num_received
diff --git a/tests/sys/netlink/test_snl.c b/tests/sys/netlink/test_snl.c
new file mode 100644
index 000000000000..040414a96e2c
--- /dev/null
+++ b/tests/sys/netlink/test_snl.c
@@ -0,0 +1,251 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <sys/param.h>
+#include <sys/module.h>
+
+#include <netlink/netlink.h>
+#include <netlink/netlink_route.h>
+#include "netlink/netlink_snl.h"
+#include "netlink/netlink_snl_route.h"
+#include "netlink/netlink_snl_route_parsers.h"
+
+#include <atf-c.h>
+
+static const struct snl_hdr_parser *snl_all_core_parsers[] = {
+ &snl_errmsg_parser, &snl_donemsg_parser,
+ &_nla_bit_parser, &_nla_bitset_parser,
+};
+
+static const struct snl_hdr_parser *snl_all_route_parsers[] = {
+ &_metrics_mp_nh_parser, &_mpath_nh_parser, &_metrics_parser, &snl_rtm_route_parser,
+ &_link_fbsd_parser, &snl_rtm_link_parser, &snl_rtm_link_parser_simple,
+ &_neigh_fbsd_parser, &snl_rtm_neigh_parser,
+ &_addr_fbsd_parser, &snl_rtm_addr_parser, &_nh_fbsd_parser, &snl_nhmsg_parser,
+};
+
+static void
+require_netlink(void)
+{
+ if (modfind("netlink") == -1)
+ atf_tc_skip("netlink module not loaded");
+}
+
+ATF_TC(snl_verify_core_parsers);
+ATF_TC_HEAD(snl_verify_core_parsers, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests snl(3) core nlmsg parsers are correct");
+}
+
+ATF_TC_BODY(snl_verify_core_parsers, tc)
+{
+ SNL_VERIFY_PARSERS(snl_all_core_parsers);
+
+}
+
+ATF_TC(snl_verify_route_parsers);
+ATF_TC_HEAD(snl_verify_route_parsers, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests snl(3) route parsers are correct");
+}
+
+ATF_TC_BODY(snl_verify_route_parsers, tc)
+{
+ SNL_VERIFY_PARSERS(snl_all_route_parsers);
+
+}
+
+ATF_TC(snl_parse_errmsg_capped);
+ATF_TC_HEAD(snl_parse_errmsg_capped, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests snl(3) correctly parsing capped errors");
+}
+
+ATF_TC_BODY(snl_parse_errmsg_capped, tc)
+{
+ struct snl_state ss;
+ struct snl_writer nw;
+
+ require_netlink();
+
+ if (!snl_init(&ss, NETLINK_ROUTE))
+ atf_tc_fail("snl_init() failed");
+
+ atf_tc_skip("does not work");
+
+ int optval = 1;
+ ATF_CHECK(setsockopt(ss.fd, SOL_NETLINK, NETLINK_CAP_ACK, &optval, sizeof(optval)) == 0);
+
+ snl_init_writer(&ss, &nw);
+
+ struct nlmsghdr *hdr = snl_create_msg_request(&nw, 255);
+ ATF_CHECK(hdr != NULL);
+ ATF_CHECK(snl_reserve_msg_object(&nw, struct ifinfomsg) != NULL);
+ snl_add_msg_attr_string(&nw, 143, "some random string");
+ ATF_CHECK(snl_finalize_msg(&nw) != NULL);
+
+ ATF_CHECK(snl_send_message(&ss, hdr));
+
+ struct nlmsghdr *rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq);
+ ATF_CHECK(rx_hdr != NULL);
+ ATF_CHECK(rx_hdr->nlmsg_type == NLMSG_ERROR);
+
+ struct snl_errmsg_data e = {};
+ ATF_CHECK(rx_hdr->nlmsg_len == sizeof(struct nlmsghdr) + sizeof(struct nlmsgerr));
+ ATF_CHECK(snl_parse_errmsg(&ss, rx_hdr, &e));
+ ATF_CHECK(e.error != 0);
+ ATF_CHECK(!memcmp(hdr, e.orig_hdr, sizeof(struct nlmsghdr)));
+}
+
+ATF_TC(snl_parse_errmsg_capped_extack);
+ATF_TC_HEAD(snl_parse_errmsg_capped_extack, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests snl(3) correctly parsing capped errors with extack");
+}
+
+ATF_TC_BODY(snl_parse_errmsg_capped_extack, tc)
+{
+ struct snl_state ss;
+ struct snl_writer nw;
+
+ require_netlink();
+
+ if (!snl_init(&ss, NETLINK_ROUTE))
+ atf_tc_fail("snl_init() failed");
+
+ int optval = 1;
+ ATF_CHECK(setsockopt(ss.fd, SOL_NETLINK, NETLINK_CAP_ACK, &optval, sizeof(optval)) == 0);
+ optval = 1;
+ ATF_CHECK(setsockopt(ss.fd, SOL_NETLINK, NETLINK_EXT_ACK, &optval, sizeof(optval)) == 0);
+
+ snl_init_writer(&ss, &nw);
+
+ struct nlmsghdr *hdr = snl_create_msg_request(&nw, 255);
+ ATF_CHECK(hdr != NULL);
+ ATF_CHECK(snl_reserve_msg_object(&nw, struct ifinfomsg) != NULL);
+ snl_add_msg_attr_string(&nw, 143, "some random string");
+ ATF_CHECK(snl_finalize_msg(&nw) != NULL);
+
+ ATF_CHECK(snl_send_message(&ss, hdr));
+
+ struct nlmsghdr *rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq);
+ ATF_CHECK(rx_hdr != NULL);
+ ATF_CHECK(rx_hdr->nlmsg_type == NLMSG_ERROR);
+
+ struct snl_errmsg_data e = {};
+ ATF_CHECK(snl_parse_errmsg(&ss, rx_hdr, &e));
+ ATF_CHECK(e.error != 0);
+ ATF_CHECK(!memcmp(hdr, e.orig_hdr, sizeof(struct nlmsghdr)));
+
+ ATF_CHECK(e.error_str != NULL);
+}
+
+ATF_TC(snl_parse_errmsg_uncapped_extack);
+ATF_TC_HEAD(snl_parse_errmsg_uncapped_extack, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests snl(3) correctly parsing errors with extack");
+}
+
+ATF_TC_BODY(snl_parse_errmsg_uncapped_extack, tc)
+{
+ struct snl_state ss;
+ struct snl_writer nw;
+
+ require_netlink();
+
+ ATF_CHECK(snl_init(&ss, NETLINK_ROUTE));
+
+ int optval = 1;
+ ATF_CHECK(setsockopt(ss.fd, SOL_NETLINK, NETLINK_EXT_ACK, &optval, sizeof(optval)) == 0);
+
+ snl_init_writer(&ss, &nw);
+
+ struct nlmsghdr *hdr = snl_create_msg_request(&nw, 255);
+ ATF_CHECK(hdr != NULL);
+ ATF_CHECK(snl_reserve_msg_object(&nw, struct ifinfomsg) != NULL);
+ snl_add_msg_attr_string(&nw, 143, "some random string");
+ ATF_CHECK(snl_finalize_msg(&nw) != NULL);
+
+ ATF_CHECK(snl_send_message(&ss, hdr));
+
+ struct nlmsghdr *rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq);
+ ATF_CHECK(rx_hdr != NULL);
+ ATF_CHECK(rx_hdr->nlmsg_type == NLMSG_ERROR);
+
+ struct snl_errmsg_data e = {};
+ ATF_CHECK(snl_parse_errmsg(&ss, rx_hdr, &e));
+ ATF_CHECK(e.error != 0);
+ ATF_CHECK(!memcmp(hdr, e.orig_hdr, hdr->nlmsg_len));
+
+ ATF_CHECK(e.error_str != NULL);
+}
+
+ATF_TC(snl_list_ifaces);
+ATF_TC_HEAD(snl_list_ifaces, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests snl(3) listing interfaces");
+}
+
+struct nl_parsed_link {
+ uint32_t ifi_index;
+ uint32_t ifla_mtu;
+ char *ifla_ifname;
+};
+
+#define _IN(_field) offsetof(struct ifinfomsg, _field)
+#define _OUT(_field) offsetof(struct nl_parsed_link, _field)
+static struct snl_attr_parser ap_link[] = {
+ { .type = IFLA_IFNAME, .off = _OUT(ifla_ifname), .cb = snl_attr_get_string },
+ { .type = IFLA_MTU, .off = _OUT(ifla_mtu), .cb = snl_attr_get_uint32 },
+};
+static struct snl_field_parser fp_link[] = {
+ {.off_in = _IN(ifi_index), .off_out = _OUT(ifi_index), .cb = snl_field_get_uint32 },
+};
+#undef _IN
+#undef _OUT
+SNL_DECLARE_PARSER(link_parser, struct ifinfomsg, fp_link, ap_link);
+
+
+ATF_TC_BODY(snl_list_ifaces, tc)
+{
+ struct snl_state ss;
+ struct snl_writer nw;
+
+ require_netlink();
+
+ if (!snl_init(&ss, NETLINK_ROUTE))
+ atf_tc_fail("snl_init() failed");
+
+ snl_init_writer(&ss, &nw);
+
+ struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK);
+ ATF_CHECK(hdr != NULL);
+ ATF_CHECK(snl_reserve_msg_object(&nw, struct ifinfomsg) != NULL);
+ ATF_CHECK(snl_finalize_msg(&nw) != NULL);
+ uint32_t seq_id = hdr->nlmsg_seq;
+
+ ATF_CHECK(snl_send_message(&ss, hdr));
+
+ struct snl_errmsg_data e = {};
+ int count = 0;
+
+ while ((hdr = snl_read_reply_multi(&ss, seq_id, &e)) != NULL) {
+ count++;
+ }
+ ATF_REQUIRE(e.error == 0);
+
+ ATF_REQUIRE_MSG(count > 0, "Empty interface list");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, snl_verify_core_parsers);
+ ATF_TP_ADD_TC(tp, snl_verify_route_parsers);
+ ATF_TP_ADD_TC(tp, snl_parse_errmsg_capped);
+ ATF_TP_ADD_TC(tp, snl_parse_errmsg_capped_extack);
+ ATF_TP_ADD_TC(tp, snl_parse_errmsg_uncapped_extack);
+ ATF_TP_ADD_TC(tp, snl_list_ifaces);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/netlink/test_snl_generic.c b/tests/sys/netlink/test_snl_generic.c
new file mode 100644
index 000000000000..c63b1380f2ad
--- /dev/null
+++ b/tests/sys/netlink/test_snl_generic.c
@@ -0,0 +1,120 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <sys/param.h>
+#include <sys/module.h>
+
+#include <netlink/netlink.h>
+#include "netlink/netlink_snl.h"
+#include "netlink/netlink_snl_generic.h"
+
+#include <atf-c.h>
+
+static const struct snl_hdr_parser *snl_all_genl_parsers[] = {
+ &_genl_ctrl_getfam_parser, &_genl_ctrl_mc_parser,
+};
+
+static void
+require_netlink(void)
+{
+ if (modfind("netlink") == -1)
+ atf_tc_skip("netlink module not loaded");
+}
+
+ATF_TC(snl_verify_genl_parsers);
+ATF_TC_HEAD(snl_verify_genl_parsers, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests snl(3) generic parsers are correct");
+}
+
+ATF_TC_BODY(snl_verify_genl_parsers, tc)
+{
+ SNL_VERIFY_PARSERS(snl_all_genl_parsers);
+
+}
+
+ATF_TC(test_snl_get_genl_family_success);
+ATF_TC_HEAD(test_snl_get_genl_family_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests successfull resolution of the 'nlctrl' family");
+}
+
+ATF_TC_BODY(test_snl_get_genl_family_success, tc)
+{
+ struct snl_state ss;
+
+ require_netlink();
+
+ if (!snl_init(&ss, NETLINK_GENERIC))
+ atf_tc_fail("snl_init() failed");
+
+ ATF_CHECK_EQ(snl_get_genl_family(&ss, "nlctrl"), GENL_ID_CTRL);
+}
+
+ATF_TC(test_snl_get_genl_family_failure);
+ATF_TC_HEAD(test_snl_get_genl_family_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests unsuccessfull resolution of 'no-such-family' family");
+}
+
+ATF_TC_BODY(test_snl_get_genl_family_failure, tc)
+{
+ struct snl_state ss;
+
+ require_netlink();
+
+ if (!snl_init(&ss, NETLINK_GENERIC))
+ atf_tc_fail("snl_init() failed");
+
+ ATF_CHECK_EQ(snl_get_genl_family(&ss, "no-such-family"), 0);
+}
+
+ATF_TC(test_snl_get_genl_family_groups);
+ATF_TC_HEAD(test_snl_get_genl_family_groups, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests getting 'nlctrl' groups");
+}
+
+ATF_TC_BODY(test_snl_get_genl_family_groups, tc)
+{
+ struct snl_state ss;
+ struct snl_writer nw;
+ struct nlmsghdr *hdr;
+
+ require_netlink();
+
+ if (!snl_init(&ss, NETLINK_GENERIC))
+ atf_tc_fail("snl_init() failed");
+
+ snl_init_writer(&ss, &nw);
+ hdr = snl_create_genl_msg_request(&nw, GENL_ID_CTRL, CTRL_CMD_GETFAMILY);
+ snl_add_msg_attr_string(&nw, CTRL_ATTR_FAMILY_NAME, "nlctrl");
+ hdr = snl_finalize_msg(&nw);
+ snl_send_message(&ss, hdr);
+
+ hdr = snl_read_reply(&ss, hdr->nlmsg_seq);
+ ATF_CHECK(hdr != NULL);
+ ATF_CHECK(hdr->nlmsg_type != NLMSG_ERROR);
+
+ struct _getfamily_attrs attrs = {};
+
+ ATF_CHECK(snl_parse_nlmsg(&ss, hdr, &_genl_ctrl_getfam_parser, &attrs));
+ ATF_CHECK_EQ(attrs.mcast_groups.num_groups, 1);
+
+ struct _snl_genl_ctrl_mcast_group *group = attrs.mcast_groups.groups[0];
+
+ ATF_CHECK(group->mcast_grp_id > 0);
+ ATF_CHECK(!strcmp(group->mcast_grp_name, "notify"));
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, snl_verify_genl_parsers);
+ ATF_TP_ADD_TC(tp, test_snl_get_genl_family_success);
+ ATF_TP_ADD_TC(tp, test_snl_get_genl_family_failure);
+ ATF_TP_ADD_TC(tp, test_snl_get_genl_family_groups);
+
+ return (atf_no_error());
+}
+
diff --git a/tests/sys/netmap/Makefile b/tests/sys/netmap/Makefile
new file mode 100644
index 000000000000..ee00d0421620
--- /dev/null
+++ b/tests/sys/netmap/Makefile
@@ -0,0 +1,13 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/netmap
+TEST_METADATA+= required_user="root"
+TEST_METADATA+= is_exclusive=true
+
+PLAIN_TESTS_C+= ctrl-api-test
+
+CFLAGS+= -I${SRCTOP}/tests
+LIBADD+= pthread
+LIBADD+= netmap
+
+.include <bsd.test.mk>
diff --git a/tests/sys/netmap/ctrl-api-test.c b/tests/sys/netmap/ctrl-api-test.c
new file mode 100644
index 000000000000..6b45dbb1cfea
--- /dev/null
+++ b/tests/sys/netmap/ctrl-api-test.c
@@ -0,0 +1,2335 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2018 Vincenzo Maffione
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This program contains a suite of unit tests for the netmap control device.
+ *
+ * On FreeBSD, you can run these tests with Kyua once installed in the system:
+ * # kyua test -k /usr/tests/sys/netmap/Kyuafile
+ *
+ * On Linux, you can run them directly:
+ * # ./ctrl-api-test
+ */
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libnetmap.h>
+#include <net/if.h>
+#include <net/netmap.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stddef.h>
+
+#ifdef __FreeBSD__
+#include "freebsd_test_suite/macros.h"
+
+static int
+eventfd(int x __unused, int y __unused)
+{
+ errno = ENODEV;
+ return -1;
+}
+#else /* __linux__ */
+#include <sys/eventfd.h>
+#endif
+
+#define NM_IFNAMSZ 64
+
+static int
+exec_command(int argc, const char *const argv[])
+{
+ pid_t child_pid;
+ pid_t wret;
+ int child_status;
+ int i;
+
+ printf("Executing command: ");
+ for (i = 0; i < argc - 1; i++) {
+ if (!argv[i]) {
+ /* Invalid argument. */
+ return -1;
+ }
+ if (i > 0) {
+ putchar(' ');
+ }
+ printf("%s", argv[i]);
+ }
+ putchar('\n');
+
+ child_pid = fork();
+ if (child_pid == 0) {
+ char **av;
+ int fds[3];
+
+ /* Child process. Redirect stdin, stdout
+ * and stderr. */
+ for (i = 0; i < 3; i++) {
+ close(i);
+ fds[i] = open("/dev/null", O_RDONLY);
+ if (fds[i] < 0) {
+ for (i--; i >= 0; i--) {
+ close(fds[i]);
+ }
+ return -1;
+ }
+ }
+
+ /* Make a copy of the arguments, passing them to execvp. */
+ av = calloc(argc, sizeof(av[0]));
+ if (!av) {
+ exit(EXIT_FAILURE);
+ }
+ for (i = 0; i < argc - 1; i++) {
+ av[i] = strdup(argv[i]);
+ if (!av[i]) {
+ exit(EXIT_FAILURE);
+ }
+ }
+ execvp(av[0], av);
+ perror("execvp()");
+ exit(EXIT_FAILURE);
+ }
+
+ wret = waitpid(child_pid, &child_status, 0);
+ if (wret < 0) {
+ fprintf(stderr, "waitpid() failed: %s\n", strerror(errno));
+ return wret;
+ }
+ if (WIFEXITED(child_status)) {
+ return WEXITSTATUS(child_status);
+ }
+
+ return -1;
+}
+
+
+#define THRET_SUCCESS ((void *)128)
+#define THRET_FAILURE ((void *)0)
+
+struct TestContext {
+ char ifname[NM_IFNAMSZ];
+ char ifname_ext[NM_IFNAMSZ];
+ char bdgname[NM_IFNAMSZ];
+ uint32_t nr_tx_slots; /* slots in tx rings */
+ uint32_t nr_rx_slots; /* slots in rx rings */
+ uint16_t nr_tx_rings; /* number of tx rings */
+ uint16_t nr_rx_rings; /* number of rx rings */
+ uint16_t nr_host_tx_rings; /* number of host tx rings */
+ uint16_t nr_host_rx_rings; /* number of host rx rings */
+ uint16_t nr_mem_id; /* id of the memory allocator */
+ uint16_t nr_ringid; /* ring(s) we care about */
+ uint32_t nr_mode; /* specify NR_REG_* modes */
+ uint32_t nr_extra_bufs; /* number of requested extra buffers */
+ uint64_t nr_flags; /* additional flags (see below) */
+ uint32_t nr_hdr_len; /* for PORT_HDR_SET and PORT_HDR_GET */
+ uint32_t nr_first_cpu_id; /* vale polling */
+ uint32_t nr_num_polling_cpus; /* vale polling */
+ uint32_t sync_kloop_mode; /* sync-kloop */
+ int fd; /* netmap file descriptor */
+
+ void *csb; /* CSB entries (atok and ktoa) */
+ struct nmreq_option *nr_opt; /* list of options */
+ sem_t *sem; /* for thread synchronization */
+
+ struct nmctx *nmctx;
+ const char *ifparse;
+ struct nmport_d *nmport; /* nmport descriptor from libnetmap */
+};
+
+static struct TestContext ctx_;
+
+typedef int (*testfunc_t)(struct TestContext *ctx);
+
+static void
+nmreq_hdr_init(struct nmreq_header *hdr, const char *ifname)
+{
+ memset(hdr, 0, sizeof(*hdr));
+ hdr->nr_version = NETMAP_API;
+ assert(strlen(ifname) < NM_IFNAMSZ);
+ strncpy(hdr->nr_name, ifname, sizeof(hdr->nr_name));
+}
+
+/* Single NETMAP_REQ_PORT_INFO_GET. */
+static int
+port_info_get(struct TestContext *ctx)
+{
+ struct nmreq_port_info_get req;
+ struct nmreq_header hdr;
+ int success;
+ int ret;
+
+ printf("Testing NETMAP_REQ_PORT_INFO_GET on '%s'\n", ctx->ifname_ext);
+
+ nmreq_hdr_init(&hdr, ctx->ifname_ext);
+ hdr.nr_reqtype = NETMAP_REQ_PORT_INFO_GET;
+ hdr.nr_body = (uintptr_t)&req;
+ memset(&req, 0, sizeof(req));
+ req.nr_mem_id = ctx->nr_mem_id;
+ ret = ioctl(ctx->fd, NIOCCTRL, &hdr);
+ if (ret != 0) {
+ perror("ioctl(/dev/netmap, NIOCCTRL, PORT_INFO_GET)");
+ return ret;
+ }
+ printf("nr_memsize %llu\n", (unsigned long long)req.nr_memsize);
+ printf("nr_tx_slots %u\n", req.nr_tx_slots);
+ printf("nr_rx_slots %u\n", req.nr_rx_slots);
+ printf("nr_tx_rings %u\n", req.nr_tx_rings);
+ printf("nr_rx_rings %u\n", req.nr_rx_rings);
+ printf("nr_mem_id %u\n", req.nr_mem_id);
+
+ success = req.nr_memsize && req.nr_tx_slots && req.nr_rx_slots &&
+ req.nr_tx_rings && req.nr_rx_rings && req.nr_tx_rings;
+ if (!success) {
+ return -1;
+ }
+
+ /* Write back results to the context structure. */
+ ctx->nr_tx_slots = req.nr_tx_slots;
+ ctx->nr_rx_slots = req.nr_rx_slots;
+ ctx->nr_tx_rings = req.nr_tx_rings;
+ ctx->nr_rx_rings = req.nr_rx_rings;
+ ctx->nr_mem_id = req.nr_mem_id;
+
+ return 0;
+}
+
+/* Single NETMAP_REQ_REGISTER, no use. */
+static int
+port_register(struct TestContext *ctx)
+{
+ struct nmreq_register req;
+ struct nmreq_header hdr;
+ int success;
+ int ret;
+
+ printf("Testing NETMAP_REQ_REGISTER(mode=%d,ringid=%d,"
+ "flags=0x%llx) on '%s'\n",
+ ctx->nr_mode, ctx->nr_ringid, (unsigned long long)ctx->nr_flags,
+ ctx->ifname_ext);
+
+ nmreq_hdr_init(&hdr, ctx->ifname_ext);
+ hdr.nr_reqtype = NETMAP_REQ_REGISTER;
+ hdr.nr_body = (uintptr_t)&req;
+ hdr.nr_options = (uintptr_t)ctx->nr_opt;
+ memset(&req, 0, sizeof(req));
+ req.nr_mem_id = ctx->nr_mem_id;
+ req.nr_mode = ctx->nr_mode;
+ req.nr_ringid = ctx->nr_ringid;
+ req.nr_flags = ctx->nr_flags;
+ req.nr_tx_slots = ctx->nr_tx_slots;
+ req.nr_rx_slots = ctx->nr_rx_slots;
+ req.nr_tx_rings = ctx->nr_tx_rings;
+ req.nr_host_tx_rings = ctx->nr_host_tx_rings;
+ req.nr_host_rx_rings = ctx->nr_host_rx_rings;
+ req.nr_rx_rings = ctx->nr_rx_rings;
+ req.nr_extra_bufs = ctx->nr_extra_bufs;
+ ret = ioctl(ctx->fd, NIOCCTRL, &hdr);
+ if (ret != 0) {
+ perror("ioctl(/dev/netmap, NIOCCTRL, REGISTER)");
+ return ret;
+ }
+ printf("nr_offset 0x%llx\n", (unsigned long long)req.nr_offset);
+ printf("nr_memsize %llu\n", (unsigned long long)req.nr_memsize);
+ printf("nr_tx_slots %u\n", req.nr_tx_slots);
+ printf("nr_rx_slots %u\n", req.nr_rx_slots);
+ printf("nr_tx_rings %u\n", req.nr_tx_rings);
+ printf("nr_rx_rings %u\n", req.nr_rx_rings);
+ printf("nr_host_tx_rings %u\n", req.nr_host_tx_rings);
+ printf("nr_host_rx_rings %u\n", req.nr_host_rx_rings);
+ printf("nr_mem_id %u\n", req.nr_mem_id);
+ printf("nr_extra_bufs %u\n", req.nr_extra_bufs);
+
+ success = req.nr_memsize && (ctx->nr_mode == req.nr_mode) &&
+ (ctx->nr_ringid == req.nr_ringid) &&
+ (ctx->nr_flags == req.nr_flags) &&
+ ((!ctx->nr_tx_slots && req.nr_tx_slots) ||
+ (ctx->nr_tx_slots == req.nr_tx_slots)) &&
+ ((!ctx->nr_rx_slots && req.nr_rx_slots) ||
+ (ctx->nr_rx_slots == req.nr_rx_slots)) &&
+ ((!ctx->nr_tx_rings && req.nr_tx_rings) ||
+ (ctx->nr_tx_rings == req.nr_tx_rings)) &&
+ ((!ctx->nr_rx_rings && req.nr_rx_rings) ||
+ (ctx->nr_rx_rings == req.nr_rx_rings)) &&
+ ((!ctx->nr_host_tx_rings && req.nr_host_tx_rings) ||
+ (ctx->nr_host_tx_rings == req.nr_host_tx_rings)) &&
+ ((!ctx->nr_host_rx_rings && req.nr_host_rx_rings) ||
+ (ctx->nr_host_rx_rings == req.nr_host_rx_rings)) &&
+ ((!ctx->nr_mem_id && req.nr_mem_id) ||
+ (ctx->nr_mem_id == req.nr_mem_id)) &&
+ (ctx->nr_extra_bufs == req.nr_extra_bufs);
+ if (!success) {
+ return -1;
+ }
+
+ /* Write back results to the context structure.*/
+ ctx->nr_tx_slots = req.nr_tx_slots;
+ ctx->nr_rx_slots = req.nr_rx_slots;
+ ctx->nr_tx_rings = req.nr_tx_rings;
+ ctx->nr_rx_rings = req.nr_rx_rings;
+ ctx->nr_host_tx_rings = req.nr_host_tx_rings;
+ ctx->nr_host_rx_rings = req.nr_host_rx_rings;
+ ctx->nr_mem_id = req.nr_mem_id;
+ ctx->nr_extra_bufs = req.nr_extra_bufs;
+
+ return 0;
+}
+
+static int
+niocregif(struct TestContext *ctx, int netmap_api)
+{
+ struct nmreq req;
+ int success;
+ int ret;
+
+ printf("Testing legacy NIOCREGIF on '%s'\n", ctx->ifname_ext);
+
+ memset(&req, 0, sizeof(req));
+ memcpy(req.nr_name, ctx->ifname_ext, sizeof(req.nr_name));
+ req.nr_name[sizeof(req.nr_name) - 1] = '\0';
+ req.nr_version = netmap_api;
+ req.nr_ringid = ctx->nr_ringid;
+ req.nr_flags = ctx->nr_mode | ctx->nr_flags;
+ req.nr_tx_slots = ctx->nr_tx_slots;
+ req.nr_rx_slots = ctx->nr_rx_slots;
+ req.nr_tx_rings = ctx->nr_tx_rings;
+ req.nr_rx_rings = ctx->nr_rx_rings;
+ req.nr_arg2 = ctx->nr_mem_id;
+ req.nr_arg3 = ctx->nr_extra_bufs;
+
+ ret = ioctl(ctx->fd, NIOCREGIF, &req);
+ if (ret != 0) {
+ perror("ioctl(/dev/netmap, NIOCREGIF)");
+ return ret;
+ }
+
+ printf("nr_offset 0x%x\n", req.nr_offset);
+ printf("nr_memsize %u\n", req.nr_memsize);
+ printf("nr_tx_slots %u\n", req.nr_tx_slots);
+ printf("nr_rx_slots %u\n", req.nr_rx_slots);
+ printf("nr_tx_rings %u\n", req.nr_tx_rings);
+ printf("nr_rx_rings %u\n", req.nr_rx_rings);
+ printf("nr_version %d\n", req.nr_version);
+ printf("nr_ringid %x\n", req.nr_ringid);
+ printf("nr_flags %x\n", req.nr_flags);
+ printf("nr_arg2 %u\n", req.nr_arg2);
+ printf("nr_arg3 %u\n", req.nr_arg3);
+
+ success = req.nr_memsize &&
+ (ctx->nr_ringid == req.nr_ringid) &&
+ ((ctx->nr_mode | ctx->nr_flags) == req.nr_flags) &&
+ ((!ctx->nr_tx_slots && req.nr_tx_slots) ||
+ (ctx->nr_tx_slots == req.nr_tx_slots)) &&
+ ((!ctx->nr_rx_slots && req.nr_rx_slots) ||
+ (ctx->nr_rx_slots == req.nr_rx_slots)) &&
+ ((!ctx->nr_tx_rings && req.nr_tx_rings) ||
+ (ctx->nr_tx_rings == req.nr_tx_rings)) &&
+ ((!ctx->nr_rx_rings && req.nr_rx_rings) ||
+ (ctx->nr_rx_rings == req.nr_rx_rings)) &&
+ ((!ctx->nr_mem_id && req.nr_arg2) ||
+ (ctx->nr_mem_id == req.nr_arg2)) &&
+ (ctx->nr_extra_bufs == req.nr_arg3);
+ if (!success) {
+ return -1;
+ }
+
+ /* Write back results to the context structure.*/
+ ctx->nr_tx_slots = req.nr_tx_slots;
+ ctx->nr_rx_slots = req.nr_rx_slots;
+ ctx->nr_tx_rings = req.nr_tx_rings;
+ ctx->nr_rx_rings = req.nr_rx_rings;
+ ctx->nr_mem_id = req.nr_arg2;
+ ctx->nr_extra_bufs = req.nr_arg3;
+
+ return ret;
+}
+
+/* The 11 ABI is the one right before the introduction of the new NIOCCTRL
+ * ABI. The 11 ABI is useful to perform tests with legacy applications
+ * (which use the 11 ABI) and new kernel (which uses 12, or higher).
+ * However, version 14 introduced a change in the layout of struct netmap_if,
+ * so that binary backward compatibility to 11 is not supported anymore.
+ */
+#define NETMAP_API_NIOCREGIF 14
+
+static int
+legacy_regif_default(struct TestContext *ctx)
+{
+ return niocregif(ctx, NETMAP_API_NIOCREGIF);
+}
+
+static int
+legacy_regif_all_nic(struct TestContext *ctx)
+{
+ ctx->nr_mode = NR_REG_ALL_NIC;
+ return niocregif(ctx, NETMAP_API);
+}
+
+static int
+legacy_regif_12(struct TestContext *ctx)
+{
+ ctx->nr_mode = NR_REG_ALL_NIC;
+ return niocregif(ctx, NETMAP_API_NIOCREGIF+1);
+}
+
+static int
+legacy_regif_sw(struct TestContext *ctx)
+{
+ ctx->nr_mode = NR_REG_SW;
+ return niocregif(ctx, NETMAP_API_NIOCREGIF);
+}
+
+static int
+legacy_regif_future(struct TestContext *ctx)
+{
+ ctx->nr_mode = NR_REG_NIC_SW;
+ /* Test forward compatibility for the legacy ABI. This means
+ * using an older kernel (with ABI 12 or higher) and a newer
+ * application (with ABI greater than NETMAP_API). */
+ return niocregif(ctx, NETMAP_API+2);
+}
+
+static int
+legacy_regif_extra_bufs(struct TestContext *ctx)
+{
+ ctx->nr_mode = NR_REG_ALL_NIC;
+ ctx->nr_extra_bufs = 20; /* arbitrary number of extra bufs */
+ return niocregif(ctx, NETMAP_API_NIOCREGIF);
+}
+
+static int
+legacy_regif_extra_bufs_pipe(struct TestContext *ctx)
+{
+ strncat(ctx->ifname_ext, "{pipeexbuf", sizeof(ctx->ifname_ext));
+ ctx->nr_mode = NR_REG_ALL_NIC;
+ ctx->nr_extra_bufs = 58; /* arbitrary number of extra bufs */
+
+ return niocregif(ctx, NETMAP_API_NIOCREGIF);
+}
+
+static int
+legacy_regif_extra_bufs_pipe_vale(struct TestContext *ctx)
+{
+ strncpy(ctx->ifname_ext, "valeX1:Y4", sizeof(ctx->ifname_ext));
+ return legacy_regif_extra_bufs_pipe(ctx);
+}
+
+/* Only valid after a successful port_register(). */
+static int
+num_registered_rings(struct TestContext *ctx)
+{
+ if (ctx->nr_flags & NR_TX_RINGS_ONLY) {
+ return ctx->nr_tx_rings;
+ }
+ if (ctx->nr_flags & NR_RX_RINGS_ONLY) {
+ return ctx->nr_rx_rings;
+ }
+
+ return ctx->nr_tx_rings + ctx->nr_rx_rings;
+}
+
+static int
+port_register_hwall_host(struct TestContext *ctx)
+{
+ ctx->nr_mode = NR_REG_NIC_SW;
+ return port_register(ctx);
+}
+
+static int
+port_register_hostall(struct TestContext *ctx)
+{
+ ctx->nr_mode = NR_REG_SW;
+ return port_register(ctx);
+}
+
+static int
+port_register_hwall(struct TestContext *ctx)
+{
+ ctx->nr_mode = NR_REG_ALL_NIC;
+ return port_register(ctx);
+}
+
+static int
+port_register_single_hw_pair(struct TestContext *ctx)
+{
+ ctx->nr_mode = NR_REG_ONE_NIC;
+ ctx->nr_ringid = 0;
+ return port_register(ctx);
+}
+
+static int
+port_register_single_host_pair(struct TestContext *ctx)
+{
+ ctx->nr_mode = NR_REG_ONE_SW;
+ ctx->nr_host_tx_rings = 2;
+ ctx->nr_host_rx_rings = 2;
+ ctx->nr_ringid = 1;
+ return port_register(ctx);
+}
+
+static int
+port_register_hostall_many(struct TestContext *ctx)
+{
+ ctx->nr_mode = NR_REG_SW;
+ ctx->nr_host_tx_rings = 5;
+ ctx->nr_host_rx_rings = 4;
+ return port_register(ctx);
+}
+
+static int
+port_register_hwall_tx(struct TestContext *ctx)
+{
+ ctx->nr_mode = NR_REG_ALL_NIC;
+ ctx->nr_flags |= NR_TX_RINGS_ONLY;
+ return port_register(ctx);
+}
+
+static int
+port_register_hwall_rx(struct TestContext *ctx)
+{
+ ctx->nr_mode = NR_REG_ALL_NIC;
+ ctx->nr_flags |= NR_RX_RINGS_ONLY;
+ return port_register(ctx);
+}
+
+
+static int
+vale_mkname(char *vpname, struct TestContext *ctx)
+{
+ if (snprintf(vpname, NM_IFNAMSZ, "%s:%s", ctx->bdgname, ctx->ifname_ext) >= NM_IFNAMSZ) {
+ fprintf(stderr, "%s:%s too long (max %d chars)\n", ctx->bdgname, ctx->ifname_ext,
+ NM_IFNAMSZ - 1);
+ return -1;
+ }
+ return 0;
+}
+
+
+/* NETMAP_REQ_VALE_ATTACH */
+static int
+vale_attach(struct TestContext *ctx)
+{
+ struct nmreq_vale_attach req;
+ struct nmreq_header hdr;
+ char vpname[NM_IFNAMSZ];
+ int ret;
+
+ if (vale_mkname(vpname, ctx) < 0)
+ return -1;
+
+ printf("Testing NETMAP_REQ_VALE_ATTACH on '%s'\n", vpname);
+ nmreq_hdr_init(&hdr, vpname);
+ hdr.nr_reqtype = NETMAP_REQ_VALE_ATTACH;
+ hdr.nr_body = (uintptr_t)&req;
+ memset(&req, 0, sizeof(req));
+ req.reg.nr_mem_id = ctx->nr_mem_id;
+ if (ctx->nr_mode == 0) {
+ ctx->nr_mode = NR_REG_ALL_NIC; /* default */
+ }
+ req.reg.nr_mode = ctx->nr_mode;
+ ret = ioctl(ctx->fd, NIOCCTRL, &hdr);
+ if (ret != 0) {
+ perror("ioctl(/dev/netmap, NIOCCTRL, VALE_ATTACH)");
+ return ret;
+ }
+ printf("nr_mem_id %u\n", req.reg.nr_mem_id);
+
+ return ((!ctx->nr_mem_id && req.reg.nr_mem_id > 1) ||
+ (ctx->nr_mem_id == req.reg.nr_mem_id)) &&
+ (ctx->nr_flags == req.reg.nr_flags)
+ ? 0
+ : -1;
+}
+
+/* NETMAP_REQ_VALE_DETACH */
+static int
+vale_detach(struct TestContext *ctx)
+{
+ struct nmreq_header hdr;
+ struct nmreq_vale_detach req;
+ char vpname[NM_IFNAMSZ];
+ int ret;
+
+ if (vale_mkname(vpname, ctx) < 0)
+ return -1;
+
+ printf("Testing NETMAP_REQ_VALE_DETACH on '%s'\n", vpname);
+ nmreq_hdr_init(&hdr, vpname);
+ hdr.nr_reqtype = NETMAP_REQ_VALE_DETACH;
+ hdr.nr_body = (uintptr_t)&req;
+ ret = ioctl(ctx->fd, NIOCCTRL, &hdr);
+ if (ret != 0) {
+ perror("ioctl(/dev/netmap, NIOCCTRL, VALE_DETACH)");
+ return ret;
+ }
+
+ return 0;
+}
+
+/* First NETMAP_REQ_VALE_ATTACH, then NETMAP_REQ_VALE_DETACH. */
+static int
+vale_attach_detach(struct TestContext *ctx)
+{
+ int ret;
+
+ if ((ret = vale_attach(ctx)) != 0) {
+ return ret;
+ }
+
+ return vale_detach(ctx);
+}
+
+static int
+vale_attach_detach_host_rings(struct TestContext *ctx)
+{
+ ctx->nr_mode = NR_REG_NIC_SW;
+ return vale_attach_detach(ctx);
+}
+
+/* First NETMAP_REQ_PORT_HDR_SET and the NETMAP_REQ_PORT_HDR_GET
+ * to check that we get the same value. */
+static int
+port_hdr_set_and_get(struct TestContext *ctx)
+{
+ struct nmreq_port_hdr req;
+ struct nmreq_header hdr;
+ int ret;
+
+ printf("Testing NETMAP_REQ_PORT_HDR_SET on '%s'\n", ctx->ifname_ext);
+
+ nmreq_hdr_init(&hdr, ctx->ifname_ext);
+ hdr.nr_reqtype = NETMAP_REQ_PORT_HDR_SET;
+ hdr.nr_body = (uintptr_t)&req;
+ memset(&req, 0, sizeof(req));
+ req.nr_hdr_len = ctx->nr_hdr_len;
+ ret = ioctl(ctx->fd, NIOCCTRL, &hdr);
+ if (ret != 0) {
+ perror("ioctl(/dev/netmap, NIOCCTRL, PORT_HDR_SET)");
+ return ret;
+ }
+
+ if (req.nr_hdr_len != ctx->nr_hdr_len) {
+ return -1;
+ }
+
+ printf("Testing NETMAP_REQ_PORT_HDR_GET on '%s'\n", ctx->ifname_ext);
+ hdr.nr_reqtype = NETMAP_REQ_PORT_HDR_GET;
+ req.nr_hdr_len = 0;
+ ret = ioctl(ctx->fd, NIOCCTRL, &hdr);
+ if (ret != 0) {
+ perror("ioctl(/dev/netmap, NIOCCTRL, PORT_HDR_SET)");
+ return ret;
+ }
+ printf("nr_hdr_len %u\n", req.nr_hdr_len);
+
+ return (req.nr_hdr_len == ctx->nr_hdr_len) ? 0 : -1;
+}
+
+/*
+ * Possible lengths for the VirtIO network header, as specified by
+ * the standard:
+ * http://docs.oasis-open.org/virtio/virtio/v1.0/cs04/virtio-v1.0-cs04.html
+ */
+#define VIRTIO_NET_HDR_LEN 10
+#define VIRTIO_NET_HDR_LEN_WITH_MERGEABLE_RXBUFS 12
+
+static int
+vale_ephemeral_port_hdr_manipulation(struct TestContext *ctx)
+{
+ int ret;
+
+ strncpy(ctx->ifname_ext, "vale:eph0", sizeof(ctx->ifname_ext));
+ ctx->nr_mode = NR_REG_ALL_NIC;
+ if ((ret = port_register(ctx))) {
+ return ret;
+ }
+ /* Try to set and get all the acceptable values. */
+ ctx->nr_hdr_len = VIRTIO_NET_HDR_LEN_WITH_MERGEABLE_RXBUFS;
+ if ((ret = port_hdr_set_and_get(ctx))) {
+ return ret;
+ }
+ ctx->nr_hdr_len = 0;
+ if ((ret = port_hdr_set_and_get(ctx))) {
+ return ret;
+ }
+ ctx->nr_hdr_len = VIRTIO_NET_HDR_LEN;
+ if ((ret = port_hdr_set_and_get(ctx))) {
+ return ret;
+ }
+ return 0;
+}
+
+static int
+vale_persistent_port(struct TestContext *ctx)
+{
+ struct nmreq_vale_newif req;
+ struct nmreq_header hdr;
+ int result;
+ int ret;
+
+ strncpy(ctx->ifname_ext, "per4", sizeof(ctx->ifname_ext));
+
+ printf("Testing NETMAP_REQ_VALE_NEWIF on '%s'\n", ctx->ifname_ext);
+
+ nmreq_hdr_init(&hdr, ctx->ifname_ext);
+ hdr.nr_reqtype = NETMAP_REQ_VALE_NEWIF;
+ hdr.nr_body = (uintptr_t)&req;
+ memset(&req, 0, sizeof(req));
+ req.nr_mem_id = ctx->nr_mem_id;
+ req.nr_tx_slots = ctx->nr_tx_slots;
+ req.nr_rx_slots = ctx->nr_rx_slots;
+ req.nr_tx_rings = ctx->nr_tx_rings;
+ req.nr_rx_rings = ctx->nr_rx_rings;
+ ret = ioctl(ctx->fd, NIOCCTRL, &hdr);
+ if (ret != 0) {
+ perror("ioctl(/dev/netmap, NIOCCTRL, VALE_NEWIF)");
+ return ret;
+ }
+
+ /* Attach the persistent VALE port to a switch and then detach. */
+ result = vale_attach_detach(ctx);
+
+ printf("Testing NETMAP_REQ_VALE_DELIF on '%s'\n", ctx->ifname_ext);
+ hdr.nr_reqtype = NETMAP_REQ_VALE_DELIF;
+ hdr.nr_body = (uintptr_t)NULL;
+ ret = ioctl(ctx->fd, NIOCCTRL, &hdr);
+ if (ret != 0) {
+ perror("ioctl(/dev/netmap, NIOCCTRL, VALE_NEWIF)");
+ if (result == 0) {
+ result = ret;
+ }
+ }
+
+ return result;
+}
+
+/* Single NETMAP_REQ_POOLS_INFO_GET. */
+static int
+pools_info_get(struct TestContext *ctx)
+{
+ struct nmreq_pools_info req;
+ struct nmreq_header hdr;
+ int ret;
+
+ printf("Testing NETMAP_REQ_POOLS_INFO_GET on '%s'\n", ctx->ifname_ext);
+
+ nmreq_hdr_init(&hdr, ctx->ifname_ext);
+ hdr.nr_reqtype = NETMAP_REQ_POOLS_INFO_GET;
+ hdr.nr_body = (uintptr_t)&req;
+ memset(&req, 0, sizeof(req));
+ req.nr_mem_id = ctx->nr_mem_id;
+ ret = ioctl(ctx->fd, NIOCCTRL, &hdr);
+ if (ret != 0) {
+ perror("ioctl(/dev/netmap, NIOCCTRL, POOLS_INFO_GET)");
+ return ret;
+ }
+ printf("nr_memsize %llu\n", (unsigned long long)req.nr_memsize);
+ printf("nr_mem_id %u\n", req.nr_mem_id);
+ printf("nr_if_pool_offset 0x%llx\n",
+ (unsigned long long)req.nr_if_pool_offset);
+ printf("nr_if_pool_objtotal %u\n", req.nr_if_pool_objtotal);
+ printf("nr_if_pool_objsize %u\n", req.nr_if_pool_objsize);
+ printf("nr_ring_pool_offset 0x%llx\n",
+ (unsigned long long)req.nr_if_pool_offset);
+ printf("nr_ring_pool_objtotal %u\n", req.nr_ring_pool_objtotal);
+ printf("nr_ring_pool_objsize %u\n", req.nr_ring_pool_objsize);
+ printf("nr_buf_pool_offset 0x%llx\n",
+ (unsigned long long)req.nr_buf_pool_offset);
+ printf("nr_buf_pool_objtotal %u\n", req.nr_buf_pool_objtotal);
+ printf("nr_buf_pool_objsize %u\n", req.nr_buf_pool_objsize);
+
+ return req.nr_memsize && req.nr_if_pool_objtotal &&
+ req.nr_if_pool_objsize &&
+ req.nr_ring_pool_objtotal &&
+ req.nr_ring_pool_objsize &&
+ req.nr_buf_pool_objtotal &&
+ req.nr_buf_pool_objsize
+ ? 0
+ : -1;
+}
+
+static int
+pools_info_get_and_register(struct TestContext *ctx)
+{
+ int ret;
+
+ /* Check that we can get pools info before we register
+ * a netmap interface. */
+ ret = pools_info_get(ctx);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ctx->nr_mode = NR_REG_ONE_NIC;
+ ret = port_register(ctx);
+ if (ret != 0) {
+ return ret;
+ }
+ ctx->nr_mem_id = 1;
+
+ /* Check that we can get pools info also after we register. */
+ return pools_info_get(ctx);
+}
+
+static int
+pools_info_get_empty_ifname(struct TestContext *ctx)
+{
+ strncpy(ctx->ifname_ext, "", sizeof(ctx->ifname_ext));
+ return pools_info_get(ctx) != 0 ? 0 : -1;
+}
+
+static int
+pipe_master(struct TestContext *ctx)
+{
+ strncat(ctx->ifname_ext, "{pipeid1", sizeof(ctx->ifname_ext));
+ ctx->nr_mode = NR_REG_NIC_SW;
+
+ if (port_register(ctx) == 0) {
+ printf("pipes should not accept NR_REG_NIC_SW\n");
+ return -1;
+ }
+ ctx->nr_mode = NR_REG_ALL_NIC;
+
+ return port_register(ctx);
+}
+
+static int
+pipe_slave(struct TestContext *ctx)
+{
+ strncat(ctx->ifname_ext, "}pipeid2", sizeof(ctx->ifname_ext));
+ ctx->nr_mode = NR_REG_ALL_NIC;
+
+ return port_register(ctx);
+}
+
+/* Test PORT_INFO_GET and POOLS_INFO_GET on a pipe. This is useful to test the
+ * registration request used internally by netmap. */
+static int
+pipe_port_info_get(struct TestContext *ctx)
+{
+ strncat(ctx->ifname_ext, "}pipeid3", sizeof(ctx->ifname_ext));
+
+ return port_info_get(ctx);
+}
+
+static int
+pipe_pools_info_get(struct TestContext *ctx)
+{
+ strncat(ctx->ifname_ext, "{xid", sizeof(ctx->ifname_ext));
+
+ return pools_info_get(ctx);
+}
+
+/* NETMAP_REQ_VALE_POLLING_ENABLE */
+static int
+vale_polling_enable(struct TestContext *ctx)
+{
+ struct nmreq_vale_polling req;
+ struct nmreq_header hdr;
+ char vpname[NM_IFNAMSZ];
+ int ret;
+
+ if (vale_mkname(vpname, ctx) < 0)
+ return -1;
+
+ printf("Testing NETMAP_REQ_VALE_POLLING_ENABLE on '%s'\n", vpname);
+
+ nmreq_hdr_init(&hdr, vpname);
+ hdr.nr_reqtype = NETMAP_REQ_VALE_POLLING_ENABLE;
+ hdr.nr_body = (uintptr_t)&req;
+ memset(&req, 0, sizeof(req));
+ req.nr_mode = ctx->nr_mode;
+ req.nr_first_cpu_id = ctx->nr_first_cpu_id;
+ req.nr_num_polling_cpus = ctx->nr_num_polling_cpus;
+ ret = ioctl(ctx->fd, NIOCCTRL, &hdr);
+ if (ret != 0) {
+ perror("ioctl(/dev/netmap, NIOCCTRL, VALE_POLLING_ENABLE)");
+ return ret;
+ }
+
+ return (req.nr_mode == ctx->nr_mode &&
+ req.nr_first_cpu_id == ctx->nr_first_cpu_id &&
+ req.nr_num_polling_cpus == ctx->nr_num_polling_cpus)
+ ? 0
+ : -1;
+}
+
+/* NETMAP_REQ_VALE_POLLING_DISABLE */
+static int
+vale_polling_disable(struct TestContext *ctx)
+{
+ struct nmreq_vale_polling req;
+ struct nmreq_header hdr;
+ char vpname[NM_IFNAMSZ];
+ int ret;
+
+ if (vale_mkname(vpname, ctx) < 0)
+ return -1;
+
+ printf("Testing NETMAP_REQ_VALE_POLLING_DISABLE on '%s'\n", vpname);
+
+ nmreq_hdr_init(&hdr, vpname);
+ hdr.nr_reqtype = NETMAP_REQ_VALE_POLLING_DISABLE;
+ hdr.nr_body = (uintptr_t)&req;
+ memset(&req, 0, sizeof(req));
+ ret = ioctl(ctx->fd, NIOCCTRL, &hdr);
+ if (ret != 0) {
+ perror("ioctl(/dev/netmap, NIOCCTRL, VALE_POLLING_DISABLE)");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int
+vale_polling_enable_disable(struct TestContext *ctx)
+{
+ int ret = 0;
+
+ if ((ret = vale_attach(ctx)) != 0) {
+ return ret;
+ }
+
+ ctx->nr_mode = NETMAP_POLLING_MODE_SINGLE_CPU;
+ ctx->nr_num_polling_cpus = 1;
+ ctx->nr_first_cpu_id = 0;
+ if ((ret = vale_polling_enable(ctx))) {
+ vale_detach(ctx);
+#ifdef __FreeBSD__
+ /* NETMAP_REQ_VALE_POLLING_DISABLE is disabled on FreeBSD,
+ * because it is currently broken. We are happy to see that
+ * it fails. */
+ return 0;
+#else
+ return ret;
+#endif
+ }
+
+ if ((ret = vale_polling_disable(ctx))) {
+ vale_detach(ctx);
+ return ret;
+ }
+
+ return vale_detach(ctx);
+}
+
+static void
+push_option(struct nmreq_option *opt, struct TestContext *ctx)
+{
+ opt->nro_next = (uintptr_t)ctx->nr_opt;
+ ctx->nr_opt = opt;
+}
+
+static void
+clear_options(struct TestContext *ctx)
+{
+ ctx->nr_opt = NULL;
+}
+
+static int
+checkoption(struct nmreq_option *opt, struct nmreq_option *exp)
+{
+ if (opt->nro_next != exp->nro_next) {
+ printf("nro_next %p expected %p\n",
+ (void *)(uintptr_t)opt->nro_next,
+ (void *)(uintptr_t)exp->nro_next);
+ return -1;
+ }
+ if (opt->nro_reqtype != exp->nro_reqtype) {
+ printf("nro_reqtype %u expected %u\n", opt->nro_reqtype,
+ exp->nro_reqtype);
+ return -1;
+ }
+ if (opt->nro_status != exp->nro_status) {
+ printf("nro_status %u expected %u\n", opt->nro_status,
+ exp->nro_status);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+unsupported_option(struct TestContext *ctx)
+{
+ struct nmreq_option opt, save;
+
+ printf("Testing unsupported option on %s\n", ctx->ifname_ext);
+
+ memset(&opt, 0, sizeof(opt));
+ opt.nro_reqtype = 1234;
+ push_option(&opt, ctx);
+ save = opt;
+
+ if (port_register_hwall(ctx) >= 0)
+ return -1;
+
+ clear_options(ctx);
+ save.nro_status = EOPNOTSUPP;
+ return checkoption(&opt, &save);
+}
+
+static int
+infinite_options(struct TestContext *ctx)
+{
+ struct nmreq_option opt;
+
+ printf("Testing infinite list of options on %s (invalid options)\n", ctx->ifname_ext);
+
+ memset(&opt, 0, sizeof(opt));
+ opt.nro_reqtype = NETMAP_REQ_OPT_MAX + 1;
+ push_option(&opt, ctx);
+ opt.nro_next = (uintptr_t)&opt;
+ if (port_register_hwall(ctx) >= 0)
+ return -1;
+ clear_options(ctx);
+ return (errno == EMSGSIZE ? 0 : -1);
+}
+
+static int
+infinite_options2(struct TestContext *ctx)
+{
+ struct nmreq_option opt;
+
+ printf("Testing infinite list of options on %s (valid options)\n", ctx->ifname_ext);
+
+ memset(&opt, 0, sizeof(opt));
+ opt.nro_reqtype = NETMAP_REQ_OPT_OFFSETS;
+ push_option(&opt, ctx);
+ opt.nro_next = (uintptr_t)&opt;
+ if (port_register_hwall(ctx) >= 0)
+ return -1;
+ clear_options(ctx);
+ return (errno == EINVAL ? 0 : -1);
+}
+
+#ifdef CONFIG_NETMAP_EXTMEM
+int
+change_param(const char *pname, unsigned long newv, unsigned long *poldv)
+{
+#ifdef __linux__
+ char param[256] = "/sys/module/netmap/parameters/";
+ unsigned long oldv;
+ FILE *f;
+
+ strncat(param, pname, sizeof(param) - 1);
+
+ f = fopen(param, "r+");
+ if (f == NULL) {
+ perror(param);
+ return -1;
+ }
+ if (fscanf(f, "%ld", &oldv) != 1) {
+ perror(param);
+ fclose(f);
+ return -1;
+ }
+ if (poldv)
+ *poldv = oldv;
+ rewind(f);
+ if (fprintf(f, "%ld\n", newv) < 0) {
+ perror(param);
+ fclose(f);
+ return -1;
+ }
+ fclose(f);
+ printf("change_param: %s: %ld -> %ld\n", pname, oldv, newv);
+#endif /* __linux__ */
+ return 0;
+}
+
+static int
+push_extmem_option(struct TestContext *ctx, const struct nmreq_pools_info *pi,
+ struct nmreq_opt_extmem *e)
+{
+ void *addr;
+
+ addr = mmap(NULL, pi->nr_memsize, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+ if (addr == MAP_FAILED) {
+ perror("mmap");
+ return -1;
+ }
+
+ memset(e, 0, sizeof(*e));
+ e->nro_opt.nro_reqtype = NETMAP_REQ_OPT_EXTMEM;
+ e->nro_info = *pi;
+ e->nro_usrptr = (uintptr_t)addr;
+
+ push_option(&e->nro_opt, ctx);
+
+ return 0;
+}
+
+static int
+pop_extmem_option(struct TestContext *ctx, struct nmreq_opt_extmem *exp)
+{
+ struct nmreq_opt_extmem *e;
+ int ret;
+
+ e = (struct nmreq_opt_extmem *)(uintptr_t)ctx->nr_opt;
+ ctx->nr_opt = (struct nmreq_option *)(uintptr_t)ctx->nr_opt->nro_next;
+
+ if ((ret = checkoption(&e->nro_opt, &exp->nro_opt))) {
+ return ret;
+ }
+
+ if (e->nro_usrptr != exp->nro_usrptr) {
+ printf("usrptr %" PRIu64 " expected %" PRIu64 "\n",
+ e->nro_usrptr, exp->nro_usrptr);
+ return -1;
+ }
+ if (e->nro_info.nr_memsize != exp->nro_info.nr_memsize) {
+ printf("memsize %" PRIu64 " expected %" PRIu64 "\n",
+ e->nro_info.nr_memsize, exp->nro_info.nr_memsize);
+ return -1;
+ }
+
+ if ((ret = munmap((void *)(uintptr_t)e->nro_usrptr,
+ e->nro_info.nr_memsize)))
+ return ret;
+
+ return 0;
+}
+
+static int
+_extmem_option(struct TestContext *ctx,
+ const struct nmreq_pools_info *pi)
+{
+ struct nmreq_opt_extmem e, save;
+ int ret;
+
+ if ((ret = push_extmem_option(ctx, pi, &e)) < 0)
+ return ret;
+
+ save = e;
+
+ strncpy(ctx->ifname_ext, "vale0:0", sizeof(ctx->ifname_ext));
+ ctx->nr_tx_slots = 16;
+ ctx->nr_rx_slots = 16;
+
+ if ((ret = port_register_hwall(ctx)))
+ return ret;
+
+ ret = pop_extmem_option(ctx, &save);
+
+ return ret;
+}
+
+static size_t
+pools_info_min_memsize(const struct nmreq_pools_info *pi)
+{
+ size_t tot = 0;
+
+ tot += pi->nr_if_pool_objtotal * pi->nr_if_pool_objsize;
+ tot += pi->nr_ring_pool_objtotal * pi->nr_ring_pool_objsize;
+ tot += pi->nr_buf_pool_objtotal * pi->nr_buf_pool_objsize;
+
+ return tot;
+}
+
+/*
+ * Fill the specification of a netmap memory allocator to be
+ * used with the 'struct nmreq_opt_extmem' option. Arbitrary
+ * values are used for the parameters, but with enough netmap
+ * rings, netmap ifs, and buffers to support a VALE port.
+ */
+static void
+pools_info_fill(struct nmreq_pools_info *pi)
+{
+ pi->nr_if_pool_objtotal = 2;
+ pi->nr_if_pool_objsize = 1024;
+ pi->nr_ring_pool_objtotal = 64;
+ pi->nr_ring_pool_objsize = 512;
+ pi->nr_buf_pool_objtotal = 4096;
+ pi->nr_buf_pool_objsize = 2048;
+ pi->nr_memsize = pools_info_min_memsize(pi);
+}
+
+static int
+extmem_option(struct TestContext *ctx)
+{
+ struct nmreq_pools_info pools_info;
+
+ pools_info_fill(&pools_info);
+
+ printf("Testing extmem option on vale0:0\n");
+ return _extmem_option(ctx, &pools_info);
+}
+
+static int
+bad_extmem_option(struct TestContext *ctx)
+{
+ struct nmreq_pools_info pools_info;
+
+ printf("Testing bad extmem option on vale0:0\n");
+
+ pools_info_fill(&pools_info);
+ /* Request a large ring size, to make sure that the kernel
+ * rejects our request. */
+ pools_info.nr_ring_pool_objsize = (1 << 20);
+
+ return _extmem_option(ctx, &pools_info) < 0 ? 0 : -1;
+}
+
+static int
+duplicate_extmem_options(struct TestContext *ctx)
+{
+ struct nmreq_opt_extmem e1, save1, e2, save2;
+ struct nmreq_pools_info pools_info;
+ int ret;
+
+ printf("Testing duplicate extmem option on vale0:0\n");
+
+ pools_info_fill(&pools_info);
+
+ if ((ret = push_extmem_option(ctx, &pools_info, &e1)) < 0)
+ return ret;
+
+ if ((ret = push_extmem_option(ctx, &pools_info, &e2)) < 0) {
+ clear_options(ctx);
+ return ret;
+ }
+
+ save1 = e1;
+ save2 = e2;
+
+ strncpy(ctx->ifname_ext, "vale0:0", sizeof(ctx->ifname_ext));
+ ctx->nr_tx_slots = 16;
+ ctx->nr_rx_slots = 16;
+
+ ret = port_register_hwall(ctx);
+ if (ret >= 0) {
+ printf("duplicate option not detected\n");
+ return -1;
+ }
+
+ save2.nro_opt.nro_status = EINVAL;
+ if ((ret = pop_extmem_option(ctx, &save2)))
+ return ret;
+
+ save1.nro_opt.nro_status = EINVAL;
+ if ((ret = pop_extmem_option(ctx, &save1)))
+ return ret;
+
+ return 0;
+}
+#endif /* CONFIG_NETMAP_EXTMEM */
+
+static int
+push_csb_option(struct TestContext *ctx, struct nmreq_opt_csb *opt)
+{
+ size_t csb_size;
+ int num_entries;
+ int ret;
+
+ ctx->nr_flags |= NR_EXCLUSIVE;
+
+ /* Get port info in order to use num_registered_rings(). */
+ ret = port_info_get(ctx);
+ if (ret != 0) {
+ return ret;
+ }
+ num_entries = num_registered_rings(ctx);
+
+ csb_size = (sizeof(struct nm_csb_atok) + sizeof(struct nm_csb_ktoa)) *
+ num_entries;
+ assert(csb_size > 0);
+ if (ctx->csb) {
+ free(ctx->csb);
+ }
+ ret = posix_memalign(&ctx->csb, sizeof(struct nm_csb_atok), csb_size);
+ if (ret != 0) {
+ printf("Failed to allocate CSB memory\n");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(opt, 0, sizeof(*opt));
+ opt->nro_opt.nro_reqtype = NETMAP_REQ_OPT_CSB;
+ opt->csb_atok = (uintptr_t)ctx->csb;
+ opt->csb_ktoa = (uintptr_t)(((uint8_t *)ctx->csb) +
+ sizeof(struct nm_csb_atok) * num_entries);
+
+ printf("Pushing option NETMAP_REQ_OPT_CSB\n");
+ push_option(&opt->nro_opt, ctx);
+
+ return 0;
+}
+
+static int
+csb_mode(struct TestContext *ctx)
+{
+ struct nmreq_opt_csb opt;
+ int ret;
+
+ ret = push_csb_option(ctx, &opt);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = port_register_hwall(ctx);
+ clear_options(ctx);
+
+ return ret;
+}
+
+static int
+csb_mode_invalid_memory(struct TestContext *ctx)
+{
+ struct nmreq_opt_csb opt;
+ int ret;
+
+ memset(&opt, 0, sizeof(opt));
+ opt.nro_opt.nro_reqtype = NETMAP_REQ_OPT_CSB;
+ opt.csb_atok = (uintptr_t)0x10;
+ opt.csb_ktoa = (uintptr_t)0x800;
+ push_option(&opt.nro_opt, ctx);
+
+ ctx->nr_flags = NR_EXCLUSIVE;
+ ret = port_register_hwall(ctx);
+ clear_options(ctx);
+
+ return (ret < 0) ? 0 : -1;
+}
+
+static int
+sync_kloop_stop(struct TestContext *ctx)
+{
+ struct nmreq_header hdr;
+ int ret;
+
+ printf("Testing NETMAP_REQ_SYNC_KLOOP_STOP on '%s'\n", ctx->ifname_ext);
+
+ nmreq_hdr_init(&hdr, ctx->ifname_ext);
+ hdr.nr_reqtype = NETMAP_REQ_SYNC_KLOOP_STOP;
+ ret = ioctl(ctx->fd, NIOCCTRL, &hdr);
+ if (ret != 0) {
+ perror("ioctl(/dev/netmap, NIOCCTRL, SYNC_KLOOP_STOP)");
+ }
+
+ return ret;
+}
+
+static void *
+sync_kloop_worker(void *opaque)
+{
+ struct TestContext *ctx = opaque;
+ struct nmreq_sync_kloop_start req;
+ struct nmreq_header hdr;
+ int ret;
+
+ printf("Testing NETMAP_REQ_SYNC_KLOOP_START on '%s'\n", ctx->ifname_ext);
+
+ nmreq_hdr_init(&hdr, ctx->ifname_ext);
+ hdr.nr_reqtype = NETMAP_REQ_SYNC_KLOOP_START;
+ hdr.nr_body = (uintptr_t)&req;
+ hdr.nr_options = (uintptr_t)ctx->nr_opt;
+ memset(&req, 0, sizeof(req));
+ req.sleep_us = 500;
+ ret = ioctl(ctx->fd, NIOCCTRL, &hdr);
+ if (ret != 0) {
+ perror("ioctl(/dev/netmap, NIOCCTRL, SYNC_KLOOP_START)");
+ }
+
+ if (ctx->sem) {
+ sem_post(ctx->sem);
+ }
+
+ pthread_exit(ret ? (void *)THRET_FAILURE : (void *)THRET_SUCCESS);
+}
+
+static int
+sync_kloop_start_stop(struct TestContext *ctx)
+{
+ pthread_t th;
+ void *thret = THRET_FAILURE;
+ int ret;
+
+ ret = pthread_create(&th, NULL, sync_kloop_worker, ctx);
+ if (ret != 0) {
+ printf("pthread_create(kloop): %s\n", strerror(ret));
+ return -1;
+ }
+
+ ret = sync_kloop_stop(ctx);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = pthread_join(th, &thret);
+ if (ret != 0) {
+ printf("pthread_join(kloop): %s\n", strerror(ret));
+ }
+
+ return thret == THRET_SUCCESS ? 0 : -1;
+}
+
+static int
+sync_kloop(struct TestContext *ctx)
+{
+ int ret;
+
+ ret = csb_mode(ctx);
+ if (ret != 0) {
+ return ret;
+ }
+
+ return sync_kloop_start_stop(ctx);
+}
+
+static int
+sync_kloop_eventfds(struct TestContext *ctx)
+{
+ struct nmreq_opt_sync_kloop_eventfds *evopt = NULL;
+ struct nmreq_opt_sync_kloop_mode modeopt;
+ struct nmreq_option evsave;
+ int num_entries;
+ size_t opt_size;
+ int ret, i;
+
+ memset(&modeopt, 0, sizeof(modeopt));
+ modeopt.nro_opt.nro_reqtype = NETMAP_REQ_OPT_SYNC_KLOOP_MODE;
+ modeopt.mode = ctx->sync_kloop_mode;
+ push_option(&modeopt.nro_opt, ctx);
+
+ num_entries = num_registered_rings(ctx);
+ opt_size = sizeof(*evopt) + num_entries * sizeof(evopt->eventfds[0]);
+ evopt = calloc(1, opt_size);
+ evopt->nro_opt.nro_next = 0;
+ evopt->nro_opt.nro_reqtype = NETMAP_REQ_OPT_SYNC_KLOOP_EVENTFDS;
+ evopt->nro_opt.nro_status = 0;
+ evopt->nro_opt.nro_size = opt_size;
+ for (i = 0; i < num_entries; i++) {
+ int efd = eventfd(0, 0);
+
+ evopt->eventfds[i].ioeventfd = efd;
+ efd = eventfd(0, 0);
+ evopt->eventfds[i].irqfd = efd;
+ }
+
+ push_option(&evopt->nro_opt, ctx);
+ evsave = evopt->nro_opt;
+
+ ret = sync_kloop_start_stop(ctx);
+ if (ret != 0) {
+ free(evopt);
+ clear_options(ctx);
+ return ret;
+ }
+#ifdef __linux__
+ evsave.nro_status = 0;
+#else /* !__linux__ */
+ evsave.nro_status = EOPNOTSUPP;
+#endif /* !__linux__ */
+
+ ret = checkoption(&evopt->nro_opt, &evsave);
+ free(evopt);
+ clear_options(ctx);
+
+ return ret;
+}
+
+static int
+sync_kloop_eventfds_all_mode(struct TestContext *ctx,
+ uint32_t sync_kloop_mode)
+{
+ int ret;
+
+ ret = csb_mode(ctx);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ctx->sync_kloop_mode = sync_kloop_mode;
+
+ return sync_kloop_eventfds(ctx);
+}
+
+static int
+sync_kloop_eventfds_all(struct TestContext *ctx)
+{
+ return sync_kloop_eventfds_all_mode(ctx, 0);
+}
+
+static int
+sync_kloop_eventfds_all_tx(struct TestContext *ctx)
+{
+ struct nmreq_opt_csb opt;
+ int ret;
+
+ ret = push_csb_option(ctx, &opt);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = port_register_hwall_tx(ctx);
+ if (ret != 0) {
+ return ret;
+ }
+ clear_options(ctx);
+
+ return sync_kloop_eventfds(ctx);
+}
+
+static int
+sync_kloop_eventfds_all_direct(struct TestContext *ctx)
+{
+ return sync_kloop_eventfds_all_mode(ctx,
+ NM_OPT_SYNC_KLOOP_DIRECT_TX | NM_OPT_SYNC_KLOOP_DIRECT_RX);
+}
+
+static int
+sync_kloop_eventfds_all_direct_tx(struct TestContext *ctx)
+{
+ return sync_kloop_eventfds_all_mode(ctx,
+ NM_OPT_SYNC_KLOOP_DIRECT_TX);
+}
+
+static int
+sync_kloop_eventfds_all_direct_rx(struct TestContext *ctx)
+{
+ return sync_kloop_eventfds_all_mode(ctx,
+ NM_OPT_SYNC_KLOOP_DIRECT_RX);
+}
+
+static int
+sync_kloop_nocsb(struct TestContext *ctx)
+{
+ int ret;
+
+ ret = port_register_hwall(ctx);
+ if (ret != 0) {
+ return ret;
+ }
+
+ /* Sync kloop must fail because we did not use
+ * NETMAP_REQ_CSB_ENABLE. */
+ return sync_kloop_start_stop(ctx) != 0 ? 0 : -1;
+}
+
+static int
+csb_enable(struct TestContext *ctx)
+{
+ struct nmreq_option saveopt;
+ struct nmreq_opt_csb opt;
+ struct nmreq_header hdr;
+ int ret;
+
+ ret = push_csb_option(ctx, &opt);
+ if (ret != 0) {
+ return ret;
+ }
+ saveopt = opt.nro_opt;
+ saveopt.nro_status = 0;
+
+ nmreq_hdr_init(&hdr, ctx->ifname_ext);
+ hdr.nr_reqtype = NETMAP_REQ_CSB_ENABLE;
+ hdr.nr_options = (uintptr_t)ctx->nr_opt;
+ hdr.nr_body = (uintptr_t)NULL;
+
+ printf("Testing NETMAP_REQ_CSB_ENABLE on '%s'\n", ctx->ifname_ext);
+
+ ret = ioctl(ctx->fd, NIOCCTRL, &hdr);
+ if (ret != 0) {
+ perror("ioctl(/dev/netmap, NIOCCTRL, CSB_ENABLE)");
+ return ret;
+ }
+
+ ret = checkoption(&opt.nro_opt, &saveopt);
+ clear_options(ctx);
+
+ return ret;
+}
+
+static int
+sync_kloop_csb_enable(struct TestContext *ctx)
+{
+ int ret;
+
+ ctx->nr_flags |= NR_EXCLUSIVE;
+ ret = port_register_hwall(ctx);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = csb_enable(ctx);
+ if (ret != 0) {
+ return ret;
+ }
+
+ return sync_kloop_start_stop(ctx);
+}
+
+#if 0
+static int
+sync_kloop_conflict(struct TestContext *ctx)
+{
+ struct nmreq_opt_csb opt;
+ pthread_t th1, th2;
+ void *thret1 = THRET_FAILURE, *thret2 = THRET_FAILURE;
+ struct timespec to;
+ sem_t sem;
+ int err = 0;
+ int ret;
+
+ ret = push_csb_option(ctx, &opt);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = port_register_hwall(ctx);
+ if (ret != 0) {
+ return ret;
+ }
+ clear_options(ctx);
+
+ ret = sem_init(&sem, 0, 0);
+ if (ret != 0) {
+ printf("sem_init() failed: %s\n", strerror(ret));
+ return ret;
+ }
+ ctx->sem = &sem;
+
+ ret = pthread_create(&th1, NULL, sync_kloop_worker, ctx);
+ err |= ret;
+ if (ret != 0) {
+ printf("pthread_create(kloop1): %s\n", strerror(ret));
+ }
+
+ ret = pthread_create(&th2, NULL, sync_kloop_worker, ctx);
+ err |= ret;
+ if (ret != 0) {
+ printf("pthread_create(kloop2): %s\n", strerror(ret));
+ }
+
+ /* Wait for one of the two threads to fail to start the kloop, to
+ * avoid a race condition where th1 starts the loop and stops,
+ * and after that th2 starts the loop successfully. */
+ /*
+ * XXX: This doesn't fully close the race. th2 might fail to
+ * start executing since th1 can enter the kernel and hog the
+ * CPU on a single-CPU system until the semaphore timeout
+ * awakens this thread and it calls sync_kloop_stop. Once th1
+ * exits the kernel, th2 can finally run and will then loop
+ * forever in the ioctl handler.
+ */
+ clock_gettime(CLOCK_REALTIME, &to);
+ to.tv_sec += 2;
+ ret = sem_timedwait(&sem, &to);
+ err |= ret;
+ if (ret != 0) {
+ printf("sem_timedwait() failed: %s\n", strerror(errno));
+ }
+
+ err |= sync_kloop_stop(ctx);
+
+ ret = pthread_join(th1, &thret1);
+ err |= ret;
+ if (ret != 0) {
+ printf("pthread_join(kloop1): %s\n", strerror(ret));
+ }
+
+ ret = pthread_join(th2, &thret2);
+ err |= ret;
+ if (ret != 0) {
+ printf("pthread_join(kloop2): %s %d\n", strerror(ret), ret);
+ }
+
+ sem_destroy(&sem);
+ ctx->sem = NULL;
+ if (err) {
+ return err;
+ }
+
+ /* Check that one of the two failed, while the other one succeeded. */
+ return ((thret1 == THRET_SUCCESS && thret2 == THRET_FAILURE) ||
+ (thret1 == THRET_FAILURE && thret2 == THRET_SUCCESS))
+ ? 0
+ : -1;
+}
+#endif
+
+static int
+sync_kloop_eventfds_mismatch(struct TestContext *ctx)
+{
+ struct nmreq_opt_csb opt;
+ int ret;
+
+ ret = push_csb_option(ctx, &opt);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = port_register_hwall_rx(ctx);
+ if (ret != 0) {
+ return ret;
+ }
+ clear_options(ctx);
+
+ /* Deceive num_registered_rings() to trigger a failure of
+ * sync_kloop_eventfds(). The latter will think that all the
+ * rings were registered, and allocate the wrong number of
+ * eventfds. */
+ ctx->nr_flags &= ~NR_RX_RINGS_ONLY;
+
+ return (sync_kloop_eventfds(ctx) != 0) ? 0 : -1;
+}
+
+static int
+null_port(struct TestContext *ctx)
+{
+ int ret;
+
+ ctx->nr_mem_id = 1;
+ ctx->nr_mode = NR_REG_NULL;
+ ctx->nr_tx_rings = 10;
+ ctx->nr_rx_rings = 5;
+ ctx->nr_tx_slots = 256;
+ ctx->nr_rx_slots = 100;
+ ret = port_register(ctx);
+ if (ret != 0) {
+ return ret;
+ }
+ return 0;
+}
+
+static int
+null_port_all_zero(struct TestContext *ctx)
+{
+ int ret;
+
+ ctx->nr_mem_id = 1;
+ ctx->nr_mode = NR_REG_NULL;
+ ctx->nr_tx_rings = 0;
+ ctx->nr_rx_rings = 0;
+ ctx->nr_tx_slots = 0;
+ ctx->nr_rx_slots = 0;
+ ret = port_register(ctx);
+ if (ret != 0) {
+ return ret;
+ }
+ return 0;
+}
+
+static int
+null_port_sync(struct TestContext *ctx)
+{
+ int ret;
+
+ ctx->nr_mem_id = 1;
+ ctx->nr_mode = NR_REG_NULL;
+ ctx->nr_tx_rings = 10;
+ ctx->nr_rx_rings = 5;
+ ctx->nr_tx_slots = 256;
+ ctx->nr_rx_slots = 100;
+ ret = port_register(ctx);
+ if (ret != 0) {
+ return ret;
+ }
+ ret = ioctl(ctx->fd, NIOCTXSYNC, 0);
+ if (ret != 0) {
+ return ret;
+ }
+ return 0;
+}
+
+struct nmreq_parse_test {
+ const char *ifname;
+ const char *exp_port;
+ const char *exp_suff;
+ int exp_error;
+ uint32_t exp_mode;
+ uint16_t exp_ringid;
+ uint64_t exp_flags;
+};
+
+static struct nmreq_parse_test nmreq_parse_tests[] = {
+ /* port spec is the input. The expected results are as follows:
+ * - port: what should go into hdr.nr_name
+ * - suff: the trailing part of the input after parsing (NULL means equal to port spec)
+ * - err: the expected return value, interpreted as follows
+ * err > 0 => nmreq_header_parse should fail with the given error
+ * err < 0 => nrmeq_header_parse should succeed, but nmreq_register_decode should
+ * fail with error |err|
+ * err = 0 => should succeed
+ * - mode, ringid flags: what should go into the corresponding nr_* fields in the
+ * nmreq_register struct in case of success
+ */
+
+ /*port spec*/ /*port*/ /*suff*/ /*err*/ /*mode*/ /*ringid*/ /*flags*/
+ { "netmap:eth0", "eth0", "", 0, NR_REG_ALL_NIC, 0, 0 },
+ { "netmap:eth0-1", "eth0", "", 0, NR_REG_ONE_NIC, 1, 0 },
+ { "netmap:eth0-", "eth0", "-", -EINVAL,0, 0, 0 },
+ { "netmap:eth0/x", "eth0", "", 0, NR_REG_ALL_NIC, 0, NR_EXCLUSIVE },
+ { "netmap:eth0/z", "eth0", "", 0, NR_REG_ALL_NIC, 0, NR_ZCOPY_MON },
+ { "netmap:eth0/r", "eth0", "", 0, NR_REG_ALL_NIC, 0, NR_MONITOR_RX },
+ { "netmap:eth0/t", "eth0", "", 0, NR_REG_ALL_NIC, 0, NR_MONITOR_TX },
+ { "netmap:eth0-2/Tx", "eth0", "", 0, NR_REG_ONE_NIC, 2, NR_TX_RINGS_ONLY|NR_EXCLUSIVE },
+ { "netmap:eth0*", "eth0", "", 0, NR_REG_NIC_SW, 0, 0 },
+ { "netmap:eth0^", "eth0", "", 0, NR_REG_SW, 0, 0 },
+ { "netmap:eth0@2", "eth0", "", 0, NR_REG_ALL_NIC, 0, 0 },
+ { "netmap:eth0@2/R", "eth0", "", 0, NR_REG_ALL_NIC, 0, NR_RX_RINGS_ONLY },
+ { "netmap:eth0@netmap:lo/R", "eth0", "@netmap:lo/R", 0, NR_REG_ALL_NIC, 0, 0 },
+ { "netmap:eth0/R@xxx", "eth0", "@xxx", 0, NR_REG_ALL_NIC, 0, NR_RX_RINGS_ONLY },
+ { "netmap:eth0@2/R@2", "eth0", "", 0, NR_REG_ALL_NIC, 0, NR_RX_RINGS_ONLY },
+ { "netmap:eth0@2/R@3", "eth0", "@2/R@3", -EINVAL,0, 0, 0 },
+ { "netmap:eth0@", "eth0", "@", -EINVAL,0, 0, 0 },
+ { "netmap:", "", NULL, EINVAL, 0, 0, 0 },
+ { "netmap:^", "", NULL, EINVAL, 0, 0, 0 },
+ { "netmap:{", "", NULL, EINVAL, 0, 0, 0 },
+ { "eth0", NULL, NULL, EINVAL, 0, 0, 0 },
+ { "vale0:0", "vale0:0", "", 0, NR_REG_ALL_NIC, 0, 0 },
+ { "vale:0", "vale:0", "", 0, NR_REG_ALL_NIC, 0, 0 },
+ { "valeXXX:YYY", "valeXXX:YYY", "", 0, NR_REG_ALL_NIC, 0, 0 },
+ { "valeXXX:YYY-4", "valeXXX:YYY", "", 0, NR_REG_ONE_NIC, 4, 0 },
+ { "netmapXXX:eth0", NULL, NULL, EINVAL, 0, 0, 0 },
+ { "netmap:14", "14", "", 0, NR_REG_ALL_NIC, 0, 0 },
+ { "netmap:pipe{0", "pipe{0", "", 0, NR_REG_ALL_NIC, 0, 0 },
+ { "netmap:pipe{in", "pipe{in", "", 0, NR_REG_ALL_NIC, 0, 0 },
+ { "netmap:pipe{in-7", "pipe{in", "", 0, NR_REG_ONE_NIC, 7, 0 },
+ { "vale0:0{0", "vale0:0{0", "", 0, NR_REG_ALL_NIC, 0, 0 },
+ { "netmap:pipe{1}2", NULL, NULL, EINVAL, 0, 0, 0 },
+ { "vale0:0@opt", "vale0:0", "@opt", 0, NR_REG_ALL_NIC, 0, 0 },
+ { "vale0:0/Tx@opt", "vale0:0", "@opt", 0, NR_REG_ALL_NIC, 0, NR_TX_RINGS_ONLY|NR_EXCLUSIVE },
+ { "vale0:0-3@opt", "vale0:0", "@opt", 0, NR_REG_ONE_NIC, 3, 0 },
+ { "vale0:0@", "vale0:0", "@", -EINVAL,0, 0, 0 },
+ { "", NULL, NULL, EINVAL, 0, 0, 0 },
+ { NULL, NULL, NULL, 0, 0, 0, 0 },
+};
+
+static void
+randomize(void *dst, size_t n)
+{
+ size_t i;
+ char *dst_ = dst;
+
+ for (i = 0; i < n; i++)
+ dst_[i] = (char)random();
+}
+
+static int
+nmreq_hdr_parsing(struct TestContext *ctx,
+ struct nmreq_parse_test *t,
+ struct nmreq_header *hdr)
+{
+ const char *save;
+ struct nmreq_header orig_hdr;
+
+ save = ctx->ifparse = t->ifname;
+ orig_hdr = *hdr;
+
+ printf("nmreq_header: \"%s\"\n", ctx->ifparse);
+ if (nmreq_header_decode(&ctx->ifparse, hdr, ctx->nmctx) < 0) {
+ if (t->exp_error > 0) {
+ if (errno != t->exp_error) {
+ printf("!!! got errno=%d, want %d\n",
+ errno, t->exp_error);
+ return -1;
+ }
+ if (ctx->ifparse != save) {
+ printf("!!! parse error, but first arg changed\n");
+ return -1;
+ }
+ if (memcmp(&orig_hdr, hdr, sizeof(*hdr))) {
+ printf("!!! parse error, but header changed\n");
+ return -1;
+ }
+ return 0;
+ }
+ printf ("!!! nmreq_header_decode was expected to succeed, but it failed with error %d\n", errno);
+ return -1;
+ }
+ if (t->exp_error > 0) {
+ printf("!!! nmreq_header_decode returns 0, but error %d was expected\n", t->exp_error);
+ return -1;
+ }
+ if (strcmp(t->exp_port, hdr->nr_name) != 0) {
+ printf("!!! got '%s', want '%s'\n", hdr->nr_name, t->exp_port);
+ return -1;
+ }
+ if (hdr->nr_reqtype != orig_hdr.nr_reqtype ||
+ hdr->nr_options != orig_hdr.nr_options ||
+ hdr->nr_body != orig_hdr.nr_body) {
+ printf("!!! some fields of the nmreq_header where changed unexpectedly\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int
+nmreq_reg_parsing(struct TestContext *ctx,
+ struct nmreq_parse_test *t,
+ struct nmreq_register *reg)
+{
+ const char *save;
+ struct nmreq_register orig_reg;
+
+
+ save = ctx->ifparse;
+ orig_reg = *reg;
+
+ printf("nmreq_register: \"%s\"\n", ctx->ifparse);
+ if (nmreq_register_decode(&ctx->ifparse, reg, ctx->nmctx) < 0) {
+ if (t->exp_error < 0) {
+ if (errno != -t->exp_error) {
+ printf("!!! got errno=%d, want %d\n",
+ errno, -t->exp_error);
+ return -1;
+ }
+ if (ctx->ifparse != save) {
+ printf("!!! parse error, but first arg changed\n");
+ return -1;
+ }
+ if (memcmp(&orig_reg, reg, sizeof(*reg))) {
+ printf("!!! parse error, but nmreq_register changed\n");
+ return -1;
+ }
+ return 0;
+ }
+ printf ("!!! parse failed but it should have succeeded\n");
+ return -1;
+ }
+ if (t->exp_error < 0) {
+ printf("!!! nmreq_register_decode returns 0, but error %d was expected\n", -t->exp_error);
+ return -1;
+ }
+ if (reg->nr_mode != t->exp_mode) {
+ printf("!!! got nr_mode '%d', want '%d'\n", reg->nr_mode, t->exp_mode);
+ return -1;
+ }
+ if (reg->nr_ringid != t->exp_ringid) {
+ printf("!!! got nr_ringid '%d', want '%d'\n", reg->nr_ringid, t->exp_ringid);
+ return -1;
+ }
+ if (reg->nr_flags != t->exp_flags) {
+ printf("!!! got nm_flags '%llx', want '%llx\n", (unsigned long long)reg->nr_flags,
+ (unsigned long long)t->exp_flags);
+ return -1;
+ }
+ if (reg->nr_offset != orig_reg.nr_offset ||
+ reg->nr_memsize != orig_reg.nr_memsize ||
+ reg->nr_tx_slots != orig_reg.nr_tx_slots ||
+ reg->nr_rx_slots != orig_reg.nr_rx_slots ||
+ reg->nr_tx_rings != orig_reg.nr_tx_rings ||
+ reg->nr_rx_rings != orig_reg.nr_rx_rings ||
+ reg->nr_extra_bufs != orig_reg.nr_extra_bufs)
+ {
+ printf("!!! some fields of the nmreq_register where changed unexpectedly\n");
+ return -1;
+ }
+ return 0;
+}
+
+static void
+nmctx_parsing_error(struct nmctx *ctx, const char *msg)
+{
+ (void)ctx;
+ printf(" got message: %s\n", msg);
+}
+
+static int
+nmreq_parsing(struct TestContext *ctx)
+{
+ struct nmreq_parse_test *t;
+ struct nmreq_header hdr;
+ struct nmreq_register reg;
+ struct nmctx test_nmctx, *nmctx;
+ int ret = 0;
+
+ nmctx = nmctx_get();
+ if (nmctx == NULL) {
+ printf("Failed to acquire nmctx: %s", strerror(errno));
+ return -1;
+ }
+ test_nmctx = *nmctx;
+ test_nmctx.error = nmctx_parsing_error;
+ ctx->nmctx = &test_nmctx;
+ for (t = nmreq_parse_tests; t->ifname != NULL; t++) {
+ const char *exp_suff = t->exp_suff != NULL ?
+ t->exp_suff : t->ifname;
+
+ randomize(&hdr, sizeof(hdr));
+ randomize(&reg, sizeof(reg));
+ reg.nr_mem_id = 0;
+ if (nmreq_hdr_parsing(ctx, t, &hdr) < 0) {
+ ret = -1;
+ } else if (t->exp_error <= 0 && nmreq_reg_parsing(ctx, t, &reg) < 0) {
+ ret = -1;
+ }
+ if (strcmp(ctx->ifparse, exp_suff) != 0) {
+ printf("!!! string suffix after parse is '%s', but it should be '%s'\n",
+ ctx->ifparse, exp_suff);
+ ret = -1;
+ }
+ }
+ ctx->nmctx = NULL;
+ return ret;
+}
+
+static int
+binarycomp(struct TestContext *ctx)
+{
+#define ckroff(f, o) do {\
+ if (offsetof(struct netmap_ring, f) != (o)) {\
+ printf("offset of netmap_ring.%s is %zd, but it should be %d",\
+ #f, offsetof(struct netmap_ring, f), (o));\
+ return -1;\
+ }\
+} while (0)
+
+ (void)ctx;
+
+ ckroff(buf_ofs, 0);
+ ckroff(num_slots, 8);
+ ckroff(nr_buf_size, 12);
+ ckroff(ringid, 16);
+ ckroff(dir, 18);
+ ckroff(head, 20);
+ ckroff(cur, 24);
+ ckroff(tail, 28);
+ ckroff(flags, 32);
+ ckroff(ts, 40);
+ ckroff(offset_mask, 56);
+ ckroff(buf_align, 64);
+ ckroff(sem, 128);
+ ckroff(slot, 256);
+
+ return 0;
+}
+
+static void
+usage(const char *prog)
+{
+ printf("%s -i IFNAME\n"
+ "[-j TEST_NUM1[-[TEST_NUM2]] | -[TEST_NUM_2]]\n"
+ "[-l (list test cases)]\n",
+ prog);
+}
+
+struct mytest {
+ testfunc_t test;
+ const char *name;
+};
+
+#define decltest(f) \
+ { \
+ .test = f, .name = #f \
+ }
+
+static struct mytest tests[] = {
+ decltest(port_info_get),
+ decltest(port_register_hwall_host),
+ decltest(port_register_hwall),
+ decltest(port_register_hostall),
+ decltest(port_register_single_hw_pair),
+ decltest(port_register_single_host_pair),
+ decltest(port_register_hostall_many),
+ decltest(vale_attach_detach),
+ decltest(vale_attach_detach_host_rings),
+ decltest(vale_ephemeral_port_hdr_manipulation),
+ decltest(vale_persistent_port),
+ decltest(pools_info_get_and_register),
+ decltest(pools_info_get_empty_ifname),
+ decltest(pipe_master),
+ decltest(pipe_slave),
+ decltest(pipe_port_info_get),
+ decltest(pipe_pools_info_get),
+ decltest(vale_polling_enable_disable),
+ decltest(unsupported_option),
+ decltest(infinite_options),
+ decltest(infinite_options2),
+#ifdef CONFIG_NETMAP_EXTMEM
+ decltest(extmem_option),
+ decltest(bad_extmem_option),
+ decltest(duplicate_extmem_options),
+#endif /* CONFIG_NETMAP_EXTMEM */
+ decltest(csb_mode),
+ decltest(csb_mode_invalid_memory),
+ decltest(sync_kloop),
+ decltest(sync_kloop_eventfds_all),
+ decltest(sync_kloop_eventfds_all_tx),
+ decltest(sync_kloop_eventfds_all_direct),
+ decltest(sync_kloop_eventfds_all_direct_tx),
+ decltest(sync_kloop_eventfds_all_direct_rx),
+ decltest(sync_kloop_nocsb),
+ decltest(sync_kloop_csb_enable),
+#if 0
+ decltest(sync_kloop_conflict),
+#endif
+ decltest(sync_kloop_eventfds_mismatch),
+ decltest(null_port),
+ decltest(null_port_all_zero),
+ decltest(null_port_sync),
+ decltest(legacy_regif_default),
+ decltest(legacy_regif_all_nic),
+ decltest(legacy_regif_12),
+ decltest(legacy_regif_sw),
+ decltest(legacy_regif_future),
+ decltest(legacy_regif_extra_bufs),
+ decltest(legacy_regif_extra_bufs_pipe),
+ decltest(legacy_regif_extra_bufs_pipe_vale),
+ decltest(nmreq_parsing),
+ decltest(binarycomp),
+};
+
+static void
+context_cleanup(struct TestContext *ctx)
+{
+ if (ctx->csb) {
+ free(ctx->csb);
+ ctx->csb = NULL;
+ }
+
+ close(ctx->fd);
+ ctx->fd = -1;
+}
+
+static int
+parse_interval(const char *arg, int *j, int *k)
+{
+ const char *scan = arg;
+ char *rest;
+
+ *j = 0;
+ *k = -1;
+ if (*scan == '-') {
+ scan++;
+ goto get_k;
+ }
+ if (!isdigit(*scan))
+ goto err;
+ *k = strtol(scan, &rest, 10);
+ *j = *k - 1;
+ scan = rest;
+ if (*scan == '-') {
+ *k = -1;
+ scan++;
+ }
+get_k:
+ if (*scan == '\0')
+ return 0;
+ if (!isdigit(*scan))
+ goto err;
+ *k = strtol(scan, &rest, 10);
+ scan = rest;
+ if (!(*scan == '\0'))
+ goto err;
+
+ return 0;
+
+err:
+ fprintf(stderr, "syntax error in '%s', must be num[-[num]] or -[num]\n", arg);
+ return -1;
+}
+
+#define ARGV_APPEND(_av, _ac, _x)\
+ do {\
+ assert((int)(_ac) < (int)(sizeof(_av)/sizeof((_av)[0])));\
+ (_av)[(_ac)++] = _x;\
+ } while (0)
+
+static void
+tap_cleanup(int signo)
+{
+ const char *av[8];
+ int ac = 0;
+
+ (void)signo;
+#ifdef __FreeBSD__
+ ARGV_APPEND(av, ac, "ifconfig");
+ ARGV_APPEND(av, ac, ctx_.ifname);
+ ARGV_APPEND(av, ac, "destroy");
+#else
+ ARGV_APPEND(av, ac, "ip");
+ ARGV_APPEND(av, ac, "link");
+ ARGV_APPEND(av, ac, "del");
+ ARGV_APPEND(av, ac, ctx_.ifname);
+#endif
+ ARGV_APPEND(av, ac, NULL);
+ if (exec_command(ac, av)) {
+ printf("Failed to destroy tap interface\n");
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ int create_tap = 1;
+ int num_tests;
+ int ret = 0;
+ int j = 0;
+ int k = -1;
+ int list = 0;
+ int opt;
+ int i;
+
+#ifdef __FreeBSD__
+ PLAIN_REQUIRE_KERNEL_MODULE("if_tap", 0);
+ PLAIN_REQUIRE_KERNEL_MODULE("netmap", 0);
+#endif
+
+ memset(&ctx_, 0, sizeof(ctx_));
+
+ {
+ struct timespec t;
+ int idx;
+
+ clock_gettime(CLOCK_REALTIME, &t);
+ srand((unsigned int)t.tv_nsec);
+ idx = rand() % 8000 + 100;
+ snprintf(ctx_.ifname, sizeof(ctx_.ifname), "tap%d", idx);
+ idx = rand() % 800 + 100;
+ snprintf(ctx_.bdgname, sizeof(ctx_.bdgname), "vale%d", idx);
+ }
+
+ while ((opt = getopt(argc, argv, "hi:j:l")) != -1) {
+ switch (opt) {
+ case 'h':
+ usage(argv[0]);
+ return 0;
+
+ case 'i':
+ strncpy(ctx_.ifname, optarg, sizeof(ctx_.ifname) - 1);
+ create_tap = 0;
+ break;
+
+ case 'j':
+ if (parse_interval(optarg, &j, &k) < 0) {
+ usage(argv[0]);
+ return -1;
+ }
+ break;
+
+ case 'l':
+ list = 1;
+ create_tap = 0;
+ break;
+
+ default:
+ printf(" Unrecognized option %c\n", opt);
+ usage(argv[0]);
+ return -1;
+ }
+ }
+
+ num_tests = sizeof(tests) / sizeof(tests[0]);
+
+ if (j < 0 || j >= num_tests || k > num_tests) {
+ fprintf(stderr, "Test interval %d-%d out of range (%d-%d)\n",
+ j + 1, k, 1, num_tests + 1);
+ return -1;
+ }
+
+ if (k < 0)
+ k = num_tests;
+
+ if (list) {
+ printf("Available tests:\n");
+ for (i = 0; i < num_tests; i++) {
+ printf("#%03d: %s\n", i + 1, tests[i].name);
+ }
+ return 0;
+ }
+
+ if (create_tap) {
+ struct sigaction sa;
+ const char *av[8];
+ int ac = 0;
+#ifdef __FreeBSD__
+ ARGV_APPEND(av, ac, "ifconfig");
+ ARGV_APPEND(av, ac, ctx_.ifname);
+ ARGV_APPEND(av, ac, "create");
+ ARGV_APPEND(av, ac, "up");
+#else
+ ARGV_APPEND(av, ac, "ip");
+ ARGV_APPEND(av, ac, "tuntap");
+ ARGV_APPEND(av, ac, "add");
+ ARGV_APPEND(av, ac, "mode");
+ ARGV_APPEND(av, ac, "tap");
+ ARGV_APPEND(av, ac, "name");
+ ARGV_APPEND(av, ac, ctx_.ifname);
+#endif
+ ARGV_APPEND(av, ac, NULL);
+ if (exec_command(ac, av)) {
+ printf("Failed to create tap interface\n");
+ return -1;
+ }
+
+ sa.sa_handler = tap_cleanup;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ ret = sigaction(SIGINT, &sa, NULL);
+ if (ret) {
+ perror("sigaction(SIGINT)");
+ goto out;
+ }
+ ret = sigaction(SIGTERM, &sa, NULL);
+ if (ret) {
+ perror("sigaction(SIGTERM)");
+ goto out;
+ }
+ }
+
+ for (i = j; i < k; i++) {
+ struct TestContext ctxcopy;
+ int fd;
+ printf("==> Start of Test #%d [%s]\n", i + 1, tests[i].name);
+ fd = open("/dev/netmap", O_RDWR);
+ if (fd < 0) {
+ perror("open(/dev/netmap)");
+ ret = fd;
+ goto out;
+ }
+ memcpy(&ctxcopy, &ctx_, sizeof(ctxcopy));
+ ctxcopy.fd = fd;
+ memcpy(ctxcopy.ifname_ext, ctxcopy.ifname,
+ sizeof(ctxcopy.ifname));
+ ret = tests[i].test(&ctxcopy);
+ if (ret != 0) {
+ printf("Test #%d [%s] failed\n", i + 1, tests[i].name);
+ goto out;
+ }
+ printf("==> Test #%d [%s] successful\n", i + 1, tests[i].name);
+ context_cleanup(&ctxcopy);
+ }
+out:
+ tap_cleanup(0);
+
+ return ret;
+}
diff --git a/tests/sys/netpfil/Makefile b/tests/sys/netpfil/Makefile
new file mode 100644
index 000000000000..b449902aabc2
--- /dev/null
+++ b/tests/sys/netpfil/Makefile
@@ -0,0 +1,14 @@
+.include <src.opts.mk>
+
+TESTSDIR= ${TESTSBASE}/sys/netpfil
+TESTS_SUBDIRS+= common
+
+.if ${MK_PF} != "no"
+TESTS_SUBDIRS+= pf
+.endif
+
+.if ${MK_IPFW} != "no"
+TESTS_SUBDIRS+= ipfw
+.endif
+
+.include <bsd.test.mk>
diff --git a/tests/sys/netpfil/Makefile.depend b/tests/sys/netpfil/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/netpfil/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/netpfil/common/Makefile b/tests/sys/netpfil/common/Makefile
new file mode 100644
index 000000000000..0938bd9d9c7e
--- /dev/null
+++ b/tests/sys/netpfil/common/Makefile
@@ -0,0 +1,36 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/netpfil/common
+BINDIR= ${TESTSDIR}
+
+
+ATF_TESTS_SH+= \
+ dummynet \
+ pass_block \
+ nat \
+ rdr \
+ tos \
+ fragments \
+ forward
+
+# Allow tests to run in parallel in their own jails
+TEST_METADATA+= execenv="jail"
+TEST_METADATA+= execenv_jail_params="vnet allow.raw_sockets"
+
+PROGS= divapp
+
+${PACKAGE}FILES+= \
+ utils.subr \
+ runner.subr \
+ pft_icmp_check.py \
+ pft_ping.py \
+ pft_rst.py \
+ pft_synflood.py \
+ sniffer.py
+
+${PACKAGE}FILESMODE_pft_icmp_check.py= 0555
+${PACKAGE}FILESMODE_pft_ping.py= 0555
+${PACKAGE}FILESMODE_pft_rst.py= 0555
+${PACKAGE}FILESMODE_pft_synflood.py= 0555
+
+.include <bsd.test.mk>
diff --git a/tests/sys/netpfil/common/divapp.c b/tests/sys/netpfil/common/divapp.c
new file mode 100644
index 000000000000..d0f4b345b14c
--- /dev/null
+++ b/tests/sys/netpfil/common/divapp.c
@@ -0,0 +1,146 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Igor Ostapenko <pm@igoro.pro>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Used by divert(4) related tests */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <err.h>
+#include <sysexits.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+
+
+struct context {
+ unsigned short divert_port;
+ bool divert_back;
+
+ int fd;
+ struct sockaddr_in sin;
+ socklen_t sin_len;
+ char pkt[IP_MAXPACKET];
+ ssize_t pkt_n;
+};
+
+static void
+init(struct context *c)
+{
+ c->fd = socket(PF_DIVERT, SOCK_RAW, 0);
+ if (c->fd == -1)
+ errx(EX_OSERR, "init: Cannot create divert socket.");
+
+ memset(&c->sin, 0, sizeof(c->sin));
+ c->sin.sin_family = AF_INET;
+ c->sin.sin_port = htons(c->divert_port);
+ c->sin.sin_addr.s_addr = INADDR_ANY;
+ c->sin_len = sizeof(struct sockaddr_in);
+
+ if (bind(c->fd, (struct sockaddr *) &c->sin, c->sin_len) != 0)
+ errx(EX_OSERR, "init: Cannot bind divert socket.");
+}
+
+static ssize_t
+recv_pkt(struct context *c)
+{
+ fd_set readfds;
+ struct timeval timeout;
+ int s;
+
+ FD_ZERO(&readfds);
+ FD_SET(c->fd, &readfds);
+ timeout.tv_sec = 3;
+ timeout.tv_usec = 0;
+
+ s = select(c->fd + 1, &readfds, 0, 0, &timeout);
+ if (s == -1)
+ errx(EX_IOERR, "recv_pkt: select() errors.");
+ if (s != 1) /* timeout */
+ return (-1);
+
+ c->pkt_n = recvfrom(c->fd, c->pkt, sizeof(c->pkt), 0,
+ (struct sockaddr *) &c->sin, &c->sin_len);
+ if (c->pkt_n == -1)
+ errx(EX_IOERR, "recv_pkt: recvfrom() errors.");
+
+ return (c->pkt_n);
+}
+
+static void
+send_pkt(struct context *c)
+{
+ ssize_t n;
+
+ n = sendto(c->fd, c->pkt, c->pkt_n, 0,
+ (struct sockaddr *) &c->sin, c->sin_len);
+ if (n == -1)
+ err(EX_IOERR, "send_pkt: sendto() errors");
+ if (n != c->pkt_n)
+ errx(EX_IOERR, "send_pkt: sendto() sent %zd of %zd bytes.",
+ n, c->pkt_n);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct context c;
+ int npkt;
+
+ if (argc < 2)
+ errx(EX_USAGE,
+ "Usage: %s <divert-port> [divert-back]", argv[0]);
+
+ memset(&c, 0, sizeof(struct context));
+
+ c.divert_port = (unsigned short) strtol(argv[1], NULL, 10);
+ if (c.divert_port == 0)
+ errx(EX_USAGE, "divert port is not defined.");
+
+ if (argc >= 3 && strcmp(argv[2], "divert-back") == 0)
+ c.divert_back = true;
+
+
+ init(&c);
+
+ npkt = 0;
+ while (recv_pkt(&c) > 0) {
+ if (c.divert_back)
+ send_pkt(&c);
+ npkt++;
+ if (npkt >= 10)
+ break;
+ }
+
+ if (npkt != 1)
+ errx(EXIT_FAILURE, "%d: npkt=%d.", c.divert_port, npkt);
+
+ return (EXIT_SUCCESS);
+}
diff --git a/tests/sys/netpfil/common/dummynet.sh b/tests/sys/netpfil/common/dummynet.sh
new file mode 100644
index 000000000000..66736fbecdb7
--- /dev/null
+++ b/tests/sys/netpfil/common/dummynet.sh
@@ -0,0 +1,641 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2021 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+. $(atf_get_srcdir)/runner.subr
+
+interface_removal_head()
+{
+ atf_set descr 'Test removing interfaces with dummynet delayed traffic'
+ atf_set require.user root
+}
+
+interface_removal_body()
+{
+ fw=$1
+ firewall_init $fw
+ dummynet_init $fw
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}b
+
+ ifconfig ${epair}a 192.0.2.1/24 up
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -i .1 -c 3 -s 1200 192.0.2.2
+
+ jexec alcatraz dnctl pipe 1 config delay 1500
+
+ firewall_config alcatraz ${fw} \
+ "ipfw" \
+ "ipfw add 1000 pipe 1 ip from any to any" \
+ "pf" \
+ "pass on ${epair}b dnpipe 1"
+
+ # single ping succeeds just fine
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ # Send traffic that'll still be pending when we remove the interface
+ ping -c 5 -s 1200 192.0.2.2 &
+ sleep 1 # Give ping the chance to start.
+
+ # Remove the interface, but keep the jail around for a bit
+ ifconfig ${epair}a destroy
+
+ sleep 3
+}
+
+interface_removal_cleanup()
+{
+ firewall_cleanup $1
+}
+
+pipe_head()
+{
+ atf_set descr 'Basic pipe test'
+ atf_set require.user root
+}
+
+pipe_body()
+{
+ fw=$1
+ firewall_init $fw
+ dummynet_init $fw
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}b
+
+ ifconfig ${epair}a 192.0.2.1/24 up
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -i .1 -c 3 -s 1200 192.0.2.2
+
+ jexec alcatraz dnctl pipe 1 config bw 30Byte/s
+
+ firewall_config alcatraz ${fw} \
+ "ipfw" \
+ "ipfw add 1000 pipe 1 ip from any to any" \
+ "pf" \
+ "pass on ${epair}b dnpipe 1"
+
+ # single ping succeeds just fine
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ # Saturate the link
+ ping -i .1 -c 5 -s 1200 192.0.2.2
+
+ # We should now be hitting the limits and get this packet dropped.
+ atf_check -s exit:2 -o ignore ping -c 1 -s 1200 192.0.2.2
+}
+
+pipe_cleanup()
+{
+ firewall_cleanup $1
+}
+
+pipe_v6_head()
+{
+ atf_set descr 'Basic IPv6 pipe test'
+ atf_set require.user root
+}
+
+pipe_v6_body()
+{
+ fw=$1
+ firewall_init $fw
+ dummynet_init $fw
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}b
+
+ ifconfig ${epair}a inet6 2001:db8:42::1/64 up no_dad
+ jexec alcatraz ifconfig ${epair}b inet6 2001:db8:42::2/64 up no_dad
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping6 -i .1 -c 3 -s 1200 2001:db8:42::2
+
+ jexec alcatraz dnctl pipe 1 config bw 100Byte/s
+
+ firewall_config alcatraz ${fw} \
+ "ipfw" \
+ "ipfw add 1000 pipe 1 ip6 from any to any" \
+ "pf" \
+ "pass on ${epair}b dnpipe 1"
+
+ # Single ping succeeds
+ atf_check -s exit:0 -o ignore ping6 -c 1 2001:db8:42::2
+
+ # Saturate the link
+ ping6 -i .1 -c 5 -s 1200 2001:db8:42::2
+
+ # We should now be hitting the limit and get this packet dropped.
+ atf_check -s exit:2 -o ignore ping6 -c 1 -s 1200 2001:db8:42::2
+}
+
+pipe_v6_cleanup()
+{
+ firewall_cleanup $1
+}
+
+codel_head()
+{
+ atf_set descr 'FQ_CODEL basic test'
+ atf_set require.user root
+}
+
+codel_body()
+{
+ fw=$1
+ firewall_init $fw
+ dummynet_init $fw
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}b
+
+ ifconfig ${epair}a 192.0.2.1/24 up
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -i .1 -c 3 -s 1200 192.0.2.2
+
+ jexec alcatraz dnctl pipe 1 config bw 10Mb queue 100 droptail
+ jexec alcatraz dnctl sched 1 config pipe 1 type fq_codel target 0ms interval 0ms quantum 1514 limit 10240 flows 1024 ecn
+ jexec alcatraz dnctl queue 1 config pipe 1 droptail
+
+ firewall_config alcatraz ${fw} \
+ "ipfw" \
+ "ipfw add 1000 queue 1 ip from any to any" \
+ "pf" \
+ "pass dnqueue 1"
+
+ # single ping succeeds just fine
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+}
+
+codel_cleanup()
+{
+ firewall_cleanup $1
+}
+
+wf2q_heap_head()
+{
+ atf_set descr 'Test WF2Q+, attempting to provoke use-after-free'
+ atf_set require.user root
+}
+
+wf2q_heap_body()
+{
+ fw=$1
+ firewall_init $fw
+ dummynet_init $fw
+
+ j=dummynet_wf2q_heap_${fw}_
+
+ epair=$(vnet_mkepair)
+ epair_other=$(vnet_mkepair)
+ vnet_mkjail ${j}a ${epair}a
+ vnet_mkjail ${j}b ${epair}b ${epair_other}b
+
+ jexec ${j}a ifconfig ${epair}a up mtu 9000
+ va=$(jexec ${j}a ifconfig vlan create vlan 42 vlandev ${epair}a)
+ jexec ${j}a ifconfig ${va} 192.0.2.1/24 up #mtu 8000
+
+ jexec ${j}b ifconfig ${epair}b up mtu 9000
+ vb=$(jexec ${j}b ifconfig vlan create vlan 42 vlandev ${epair}b)
+ jexec ${j}b ifconfig ${vb} 192.0.2.2/24 up #mtu 8000
+ jexec ${j}b ifconfig ${epair_other}b up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ jexec ${j}b ping -c 1 192.0.2.1
+
+ jexec ${j}b dnctl pipe 1 config bw 10Mb queue 100 delay 500 droptail
+ jexec ${j}b dnctl sched 1 config pipe 1 type wf2q+
+ jexec ${j}b dnctl queue 1 config pipe 1 droptail
+
+ firewall_config ${j}b ${fw} \
+ "pf" \
+ "pass dnqueue 1"
+
+ jexec ${j}a ping -f 192.0.2.2 &
+ sleep 1
+
+ jexec ${j}b ifconfig ${vb} destroy
+
+ sleep 2
+}
+
+wf2q_heap_cleanup()
+{
+ firewall_cleanup $1
+}
+
+queue_head()
+{
+ atf_set descr 'Basic queue test'
+ atf_set require.user root
+}
+
+queue_body()
+{
+ fw=$1
+
+ firewall_init $fw
+ dummynet_init $fw
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}b
+
+ ifconfig ${epair}a 192.0.2.1/24 up
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+ jexec alcatraz /usr/sbin/inetd -p ${PWD}/inetd-alcatraz.pid \
+ $(atf_get_srcdir)/../pf/echo_inetd.conf
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -i .1 -c 3 -s 1200 192.0.2.2
+ reply=$(echo "foo" | nc -N 192.0.2.2 7)
+ if [ "$reply" != "foo" ];
+ then
+ atf_fail "Echo sanity check failed"
+ fi
+
+ jexec alcatraz dnctl pipe 1 config bw 1MByte/s
+ jexec alcatraz dnctl sched 1 config pipe 1 type wf2q+
+ jexec alcatraz dnctl queue 100 config sched 1 weight 99 mask all
+ jexec alcatraz dnctl queue 200 config sched 1 weight 1 mask all
+
+ firewall_config alcatraz ${fw} \
+ "ipfw" \
+ "ipfw add 1000 queue 100 tcp from 192.0.2.2 to any out" \
+ "ipfw add 1001 queue 200 icmp from 192.0.2.2 to any out" \
+ "ipfw add 1002 allow ip from any to any" \
+ "pf" \
+ "pass in proto tcp dnqueue (0, 100)" \
+ "pass in proto icmp dnqueue (0, 200)"
+
+ # Single ping succeeds
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ # Unsaturated TCP succeeds
+ reply=$(echo "foo" | nc -w 5 -N 192.0.2.2 7)
+ if [ "$reply" != "foo" ];
+ then
+ atf_fail "Unsaturated echo failed"
+ fi
+
+ # Saturate the link
+ ping -f -s 1300 192.0.2.2 &
+
+ # Allow this to fill the queue
+ sleep 1
+
+ # TCP should still just pass
+ fails=0
+ for i in `seq 1 5`
+ do
+ result=$(dd if=/dev/zero bs=1024 count=2000 | timeout 3 nc -w 5 -N 192.0.2.2 7 | wc -c)
+ if [ $result -ne 2048000 ];
+ then
+ echo "Failed to prioritise TCP traffic. Got only $result bytes"
+ fails=$(( ${fails} + 1 ))
+ fi
+ done
+ if [ ${fails} -gt 2 ];
+ then
+ atf_fail "We failed prioritisation ${fails} times"
+ fi
+
+ # This will fail if we reverse the pola^W priority
+ firewall_config alcatraz ${fw} \
+ "ipfw" \
+ "ipfw add 1000 queue 200 tcp from 192.0.2.2 to any out" \
+ "ipfw add 1001 queue 100 icmp from 192.0.2.2 to any out" \
+ "ipfw add 1002 allow ip from any to any" \
+ "pf" \
+ "pass in proto tcp dnqueue (0, 200)" \
+ "pass in proto icmp dnqueue (0, 100)"
+
+ jexec alcatraz ping -f -s 1300 192.0.2.1 &
+ sleep 1
+
+ fails=0
+ for i in `seq 1 5`
+ do
+ result=$(dd if=/dev/zero bs=1024 count=2000 | timeout 3 nc -w 5 -N 192.0.2.2 7 | wc -c)
+ if [ $result -ne 2048000 ];
+ then
+ echo "Failed to prioritise TCP traffic. Got only $result bytes"
+ fails=$(( ${fails} + 1 ))
+ fi
+ done
+ if [ ${fails} -lt 3 ];
+ then
+ atf_fail "We failed reversed prioritisation only ${fails} times."
+ fi
+}
+
+queue_cleanup()
+{
+ firewall_cleanup $1
+}
+
+queue_v6_head()
+{
+ atf_set descr 'Basic queue test'
+ atf_set require.user root
+}
+
+queue_v6_body()
+{
+ fw=$1
+ firewall_init $fw
+ dummynet_init $fw
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}b
+
+ ifconfig ${epair}a inet6 2001:db8:42::1/64 no_dad up
+ jexec alcatraz ifconfig ${epair}b inet6 2001:db8:42::2 no_dad up
+ jexec alcatraz /usr/sbin/inetd -p ${PWD}/inetd-alcatraz.pid \
+ $(atf_get_srcdir)/../pf/echo_inetd.conf
+ jexec alcatraz sysctl net.inet6.icmp6.errppslimit=0
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping6 -i .1 -c 3 -s 1200 2001:db8:42::2
+ reply=$(echo "foo" | nc -N 2001:db8:42::2 7)
+ if [ "$reply" != "foo" ];
+ then
+ atf_fail "Echo sanity check failed"
+ fi
+
+ jexec alcatraz dnctl pipe 1 config bw 1MByte/s
+ jexec alcatraz dnctl sched 1 config pipe 1 type wf2q+
+ jexec alcatraz dnctl queue 100 config sched 1 weight 99 mask all
+ jexec alcatraz dnctl queue 200 config sched 1 weight 1 mask all
+
+ firewall_config alcatraz ${fw} \
+ "ipfw" \
+ "ipfw add 1001 queue 100 tcp from 2001:db8:42::2 to any out" \
+ "ipfw add 1000 queue 200 ipv6-icmp from 2001:db8:42::2 to any out" \
+ "ipfw add 1002 allow ip6 from any to any" \
+ "pf" \
+ "pass in proto tcp dnqueue (0, 100)" \
+ "pass in proto icmp6 dnqueue (0, 200)"
+
+ # Single ping succeeds
+ atf_check -s exit:0 -o ignore ping6 -c 1 2001:db8:42::2
+
+ # Unsaturated TCP succeeds
+ reply=$(echo "foo" | nc -w 5 -N 2001:db8:42::2 7)
+ if [ "$reply" != "foo" ];
+ then
+ atf_fail "Unsaturated echo failed"
+ fi
+
+ # Saturate the link
+ ping6 -f -s 1200 2001:db8:42::2 &
+
+ # Allow this to fill the queue
+ sleep 1
+
+ # TCP should still just pass
+ fails=0
+ for i in `seq 1 5`
+ do
+ result=$(dd if=/dev/zero bs=1024 count=1000 | timeout 3 nc -w 5 -N 2001:db8:42::2 7 | wc -c)
+ if [ $result -ne 1024000 ];
+ then
+ echo "Failed to prioritise TCP traffic. Got only $result bytes"
+ fails=$(( ${fails} + 1 ))
+ fi
+ done
+ if [ ${fails} -gt 2 ];
+ then
+ atf_fail "We failed prioritisation ${fails} times"
+ fi
+
+ # What happens if we prioritise ICMP over TCP?
+ firewall_config alcatraz ${fw} \
+ "ipfw" \
+ "ipfw add 1001 queue 200 tcp from 2001:db8:42::2 to any out" \
+ "ipfw add 1000 queue 100 ipv6-icmp from 2001:db8:42::2 to any out" \
+ "ipfw add 1002 allow ip6 from any to any" \
+ "pf" \
+ "pass in proto tcp dnqueue (0, 200)" \
+ "pass in proto icmp6 dnqueue (0, 100)"
+
+ fails=0
+ for i in `seq 1 5`
+ do
+ result=$(dd if=/dev/zero bs=1024 count=1000 | timeout 3 nc -w 5 -N 2001:db8:42::2 7 | wc -c)
+ if [ $result -ne 1024000 ];
+ then
+ echo "Failed to prioritise TCP traffic. Got only $result bytes"
+ fails=$(( ${fails} + 1 ))
+ fi
+ done
+ if [ ${fails} -lt 3 ];
+ then
+ atf_fail "We failed reversed prioritisation only ${fails} times."
+ fi
+}
+
+queue_v6_cleanup()
+{
+ firewall_cleanup $1
+}
+
+nat_head()
+{
+ atf_set descr 'Basic dummynet + NAT test'
+ atf_set require.user root
+}
+
+nat_body()
+{
+ fw=$1
+ firewall_init $fw
+ dummynet_init $fw
+ nat_init $fw
+
+ epair=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+
+ ifconfig ${epair}a 192.0.2.2/24 up
+ route add -net 198.51.100.0/24 192.0.2.1
+
+ vnet_mkjail gw ${epair}b ${epair_two}a
+ jexec gw ifconfig ${epair}b 192.0.2.1/24 up
+ jexec gw ifconfig ${epair_two}a 198.51.100.1/24 up
+ jexec gw sysctl net.inet.ip.forwarding=1
+
+ vnet_mkjail srv ${epair_two}b
+ jexec srv ifconfig ${epair_two}b 198.51.100.2/24 up
+
+ jexec gw dnctl pipe 1 config bw 300Byte/s
+
+ firewall_config gw $fw \
+ "pf" \
+ "nat on ${epair_two}a inet from 192.0.2.0/24 to any -> (${epair_two}a)" \
+ "pass dnpipe 1"
+
+ # We've deliberately not set a route to 192.0.2.0/24 on srv, so the
+ # only way it can respond to this is if NAT is applied correctly.
+ atf_check -s exit:0 -o ignore ping -c 1 198.51.100.2
+}
+
+nat_cleanup()
+{
+ firewall_cleanup $1
+}
+
+pls_basic_head()
+{
+ atf_set descr 'Basic dummynet packet loss rate test'
+ atf_set require.user root
+}
+
+pls_basic_body()
+{
+ fw=$1
+ firewall_init $fw
+ dummynet_init $fw
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}b
+
+ ifconfig ${epair}a 192.0.2.1/24 up
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+
+ firewall_config alcatraz ${fw} \
+ "ipfw" \
+ "ipfw add 65432 ip from any to any" \
+ "pf" \
+ "pass on ${epair}b"
+
+ # Sanity check
+ atf_check -s exit:0 -o match:'100 packets transmitted, 100 packets received' ping -i .1 -c 100 192.0.2.2
+
+ jexec alcatraz dnctl pipe 1 config plr 0.1
+
+ firewall_config alcatraz ${fw} \
+ "ipfw" \
+ "ipfw add 1000 pipe 1 ip from 192.0.2.1 to 192.0.2.2" \
+ "pf" \
+ "pass on ${epair}b dnpipe 1"
+
+ # check if the expected number of pings
+ # are dropped (84 - 96 responses).
+ # repeat up to 6 times if the initial
+ # checks fail
+ atf_check -s exit:0 -o match:'100 packets transmitted, (8[4-9]|9[0-6]) packets received' -r 20:10 ping -i 0.010 -c 100 192.0.2.2
+}
+
+pls_basic_cleanup()
+{
+ firewall_cleanup $1
+}
+
+pls_gilbert_head()
+{
+ atf_set descr 'dummynet Gilbert-Elliott packet loss model test'
+ atf_set require.user root
+}
+
+pls_gilbert_body()
+{
+ fw=$1
+ firewall_init $fw
+ dummynet_init $fw
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}b
+
+ ifconfig ${epair}a 192.0.2.1/24 up
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+
+ firewall_config alcatraz ${fw} \
+ "ipfw" \
+ "ipfw add 65432 ip from any to any" \
+ "pf" \
+ "pass on ${epair}b"
+
+ # Sanity check
+ atf_check -s exit:0 -o match:'100 packets transmitted, 100 packets received' ping -i .1 -c 100 192.0.2.2
+
+ jexec alcatraz dnctl pipe 1 config plr 0.01,0.1,0.8,0.2
+
+ firewall_config alcatraz ${fw} \
+ "ipfw" \
+ "ipfw add 1000 pipe 1 ip from 192.0.2.1 to 192.0.2.2" \
+ "pf" \
+ "pass on ${epair}b dnpipe 1"
+
+ # check if the expected number of pings
+ # are dropped (70 - 85 responses).
+ # repeat up to 6 times if the initial
+ # checks fail
+ atf_check -s exit:0 -o match:'100 packets transmitted, (7[0-9]|8[0-5]) packets received' -r 20:10 ping -i 0.010 -c 100 192.0.2.2
+}
+
+pls_gilbert_cleanup()
+{
+ firewall_cleanup $1
+}
+
+
+
+setup_tests \
+ interface_removal \
+ ipfw \
+ pf \
+ pipe \
+ ipfw \
+ pf \
+ pipe_v6 \
+ ipfw \
+ pf \
+ codel \
+ ipfw \
+ pf \
+ wf2q_heap \
+ pf \
+ queue \
+ ipfw \
+ pf \
+ queue_v6 \
+ ipfw \
+ pf \
+ nat \
+ pf \
+ pls_basic \
+ ipfw \
+ pf \
+ pls_gilbert \
+ ipfw \
+ pf
diff --git a/tests/sys/netpfil/common/forward.sh b/tests/sys/netpfil/common/forward.sh
new file mode 100644
index 000000000000..fa1f97aa0390
--- /dev/null
+++ b/tests/sys/netpfil/common/forward.sh
@@ -0,0 +1,100 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Ahsan Barkati
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+. $(atf_get_srcdir)/utils.subr
+. $(atf_get_srcdir)/runner.subr
+
+v4_head()
+{
+ atf_set descr 'Basic forwarding test'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+v4_body()
+{
+ firewall=$1
+ firewall_init $firewall
+
+ epair_send=$(vnet_mkepair)
+ ifconfig ${epair_send}a 192.0.2.1/24 up
+
+ epair_recv=$(vnet_mkepair)
+ ifconfig ${epair_recv}a up
+
+ vnet_mkjail iron ${epair_send}b ${epair_recv}b
+ jexec iron ifconfig ${epair_send}b 192.0.2.2/24 up
+ jexec iron ifconfig ${epair_recv}b 198.51.100.2/24 up
+ jexec iron sysctl net.inet.ip.forwarding=1
+ jexec iron arp -s 198.51.100.3 00:01:02:03:04:05
+ route add -net 198.51.100.0/24 192.0.2.2
+
+
+ atf_check -s exit:0 $(atf_get_srcdir)/pft_ping.py \
+ --sendif ${epair_send}a \
+ --to 198.51.100.3 \
+ --recvif ${epair_recv}a
+
+ firewall_config "iron" ${firewall} \
+ "pf" \
+ "block in" \
+ "ipfw" \
+ "ipfw -q add 100 deny all from any to any in" \
+ "ipf" \
+ "block in all" \
+
+ atf_check -s exit:1 $(atf_get_srcdir)/pft_ping.py \
+ --sendif ${epair_send}a \
+ --to 198.51.100.3 \
+ --recvif ${epair_recv}a
+
+ firewall_config "iron" ${firewall} \
+ "pf" \
+ "block out" \
+ "ipfw" \
+ "ipfw -q add 100 deny all from any to any out" \
+ "ipf" \
+ "block out all" \
+
+ atf_check -s exit:1 $(atf_get_srcdir)/pft_ping.py \
+ --sendif ${epair_send}a \
+ --to 198.51.100.3 \
+ --recv ${epair_recv}a
+}
+
+v4_cleanup()
+{
+ firewall=$1
+ firewall_cleanup $firewall
+}
+
+setup_tests \
+ v4 \
+ pf \
+ ipfw \
+ ipf
diff --git a/tests/sys/netpfil/common/fragments.sh b/tests/sys/netpfil/common/fragments.sh
new file mode 100644
index 000000000000..1f8540d3449c
--- /dev/null
+++ b/tests/sys/netpfil/common/fragments.sh
@@ -0,0 +1,79 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Ahsan Barkati
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+. $(atf_get_srcdir)/utils.subr
+. $(atf_get_srcdir)/runner.subr
+
+fragments_head()
+{
+ atf_set descr 'Too many fragments test'
+ atf_set require.user root
+}
+
+fragments_body()
+{
+ firewall=$1
+ firewall_init $firewall
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}b inet 192.0.2.1/24 up
+
+ vnet_mkjail iron ${epair}a
+ jexec iron ifconfig ${epair}a 192.0.2.2/24 up
+
+ ifconfig ${epair}b mtu 200
+ jexec iron ifconfig ${epair}a mtu 200
+
+ firewall_config "iron" ${firewall} \
+ "pf" \
+ "scrub all fragment reassemble" \
+ "ipfw" \
+ "ipfw -q add 100 reass all from any to any in" \
+ "ipf" \
+ "pass in all with frags"
+
+ jexec iron sysctl net.inet.ip.maxfragsperpacket=1024
+
+ atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.2
+ atf_check -s exit:0 -o ignore ping -c 1 -s 800 192.0.2.2
+
+ # Too many fragments should fail
+ atf_check -s exit:2 -o ignore ping -c 1 -s 20000 192.0.2.2
+}
+
+fragments_cleanup()
+{
+ firewall=$1
+ firewall_cleanup $firewall
+}
+
+setup_tests \
+ "fragments" \
+ "pf" \
+ "ipfw" \
+ "ipf"
diff --git a/tests/sys/netpfil/common/nat.sh b/tests/sys/netpfil/common/nat.sh
new file mode 100644
index 000000000000..023b0742ec6b
--- /dev/null
+++ b/tests/sys/netpfil/common/nat.sh
@@ -0,0 +1,252 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Ahsan Barkati
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+. $(atf_get_srcdir)/utils.subr
+. $(atf_get_srcdir)/runner.subr
+
+basic_head()
+{
+ atf_set descr 'Basic IPv4 NAT test'
+ atf_set require.user root
+}
+
+basic_body()
+{
+ firewall=$1
+ firewall_init $firewall
+ nat_init $firewall
+
+ epair_host_nat=$(vnet_mkepair)
+ epair_client1_nat=$(vnet_mkepair)
+ epair_client2_nat=$(vnet_mkepair)
+
+ vnet_mkjail nat ${epair_host_nat}b ${epair_client1_nat}a ${epair_client2_nat}a
+ vnet_mkjail client1 ${epair_client1_nat}b
+ vnet_mkjail client2 ${epair_client2_nat}b
+
+ ifconfig ${epair_host_nat}a 198.51.100.2/24 up
+ jexec nat ifconfig ${epair_host_nat}b 198.51.100.1/24 up
+
+ jexec nat ifconfig ${epair_client1_nat}a 192.0.2.1/24 up
+ jexec client1 ifconfig ${epair_client1_nat}b 192.0.2.2/24 up
+
+ jexec nat ifconfig ${epair_client2_nat}a 192.0.3.1/24 up
+ jexec client2 ifconfig ${epair_client2_nat}b 192.0.3.2/24 up
+
+ jexec nat sysctl net.inet.ip.forwarding=1
+
+ jexec client1 route add -net 198.51.100.0/24 192.0.2.1
+ jexec client2 route add -net 198.51.100.0/24 192.0.3.1
+
+ # ping fails without NAT configuration
+ atf_check -s exit:2 -o ignore jexec client1 ping -t 1 -c 1 198.51.100.2
+ atf_check -s exit:2 -o ignore jexec client2 ping -t 1 -c 1 198.51.100.2
+
+ firewall_config nat ${firewall} \
+ "pf" \
+ "nat pass on ${epair_host_nat}b inet from any to any -> (${epair_host_nat}b)" \
+ "ipfw" \
+ "ipfw -q nat 123 config if ${epair_host_nat}b" \
+ "ipfw -q add 1000 nat 123 all from any to any" \
+ "ipfnat" \
+ "map ${epair_host_nat}b 192.0.3.0/24 -> 0/32" \
+ "map ${epair_host_nat}b 192.0.2.0/24 -> 0/32" \
+
+
+ # ping is successful now
+ atf_check -s exit:0 -o ignore jexec client1 ping -t 1 -c 1 198.51.100.2
+ atf_check -s exit:0 -o ignore jexec client2 ping -t 1 -c 1 198.51.100.2
+
+}
+
+basic_cleanup()
+{
+ firewall=$1
+ firewall_cleanup $firewall
+}
+
+userspace_nat_head()
+{
+ atf_set descr 'Nat test for ipfw using userspace natd'
+ atf_set require.user root
+}
+userspace_nat_body()
+{
+ firewall=$1
+ firewall_init $firewall
+
+ if ! kldstat -q -m ipdivert; then
+ atf_skip "This test requires ipdivert module loaded"
+ fi
+
+ epair_host_nat=$(vnet_mkepair)
+ epair_client1_nat=$(vnet_mkepair)
+ epair_client2_nat=$(vnet_mkepair)
+
+ vnet_mkjail nat ${epair_host_nat}b ${epair_client1_nat}a ${epair_client2_nat}a
+ vnet_mkjail client1 ${epair_client1_nat}b
+ vnet_mkjail client2 ${epair_client2_nat}b
+
+ ifconfig ${epair_host_nat}a 198.51.100.2/24 up
+ jexec nat ifconfig ${epair_host_nat}b 198.51.100.1/24 up
+
+ jexec nat ifconfig ${epair_client1_nat}a 192.0.2.1/24 up
+ jexec client1 ifconfig ${epair_client1_nat}b 192.0.2.2/24 up
+
+ jexec nat ifconfig ${epair_client2_nat}a 192.0.3.1/24 up
+ jexec client2 ifconfig ${epair_client2_nat}b 192.0.3.2/24 up
+
+ jexec nat sysctl net.inet.ip.forwarding=1
+
+ jexec client1 route add -net 198.51.100.0/24 192.0.2.1
+ jexec client2 route add -net 198.51.100.0/24 192.0.3.1
+ # Test the userspace NAT of ipfw
+ # ping fails without NAT configuration
+ atf_check -s exit:2 -o ignore jexec client1 ping -t 1 -c 1 198.51.100.2
+ atf_check -s exit:2 -o ignore jexec client2 ping -t 1 -c 1 198.51.100.2
+
+ firewall_config nat ${firewall} \
+ "ipfw" \
+ "natd -interface ${epair_host_nat}b" \
+ "ipfw -q add divert natd all from any to any via ${epair_host_nat}b" \
+
+ # ping is successful now
+ atf_check -s exit:0 -o ignore jexec client1 ping -t 1 -c 1 198.51.100.2
+ atf_check -s exit:0 -o ignore jexec client2 ping -t 1 -c 1 198.51.100.2
+}
+
+userspace_nat_cleanup()
+{
+ firewall=$1
+ firewall_cleanup $firewall
+}
+
+common_cgn() {
+ firewall=$1
+ portalias=$2
+ firewall_init $firewall
+ nat_init $firewall
+
+ epair_host_nat=$(vnet_mkepair)
+ epair_client1_nat=$(vnet_mkepair)
+ epair_client2_nat=$(vnet_mkepair)
+
+ vnet_mkjail nat ${epair_host_nat}b ${epair_client1_nat}a ${epair_client2_nat}a
+ vnet_mkjail client1 ${epair_client1_nat}b
+ vnet_mkjail client2 ${epair_client2_nat}b
+
+ ifconfig ${epair_host_nat}a 198.51.100.2/24 up
+ jexec nat ifconfig ${epair_host_nat}b 198.51.100.1/24 up
+
+ jexec nat ifconfig ${epair_client1_nat}a 100.64.0.1/24 up
+ jexec client1 ifconfig ${epair_client1_nat}b 100.64.0.2/24 up
+
+ jexec nat ifconfig ${epair_client2_nat}a 100.64.1.1/24 up
+ jexec client2 ifconfig ${epair_client2_nat}b 100.64.1.2/24 up
+
+ jexec nat sysctl net.inet.ip.forwarding=1
+
+ jexec client1 route add -net 198.51.100.0/24 100.64.0.1
+ jexec client2 route add -net 198.51.100.0/24 100.64.1.1
+
+ # ping fails without NAT configuration
+ atf_check -s exit:2 -o ignore jexec client1 ping -t 1 -c 1 198.51.100.2
+ atf_check -s exit:2 -o ignore jexec client2 ping -t 1 -c 1 198.51.100.2
+
+ if [[ $portalias ]]; then
+ firewall_config nat $firewall \
+ "ipfw" \
+ "ipfw -q nat 123 config if ${epair_host_nat}b unreg_cgn port_alias 2000-2999" \
+ "ipfw -q nat 456 config if ${epair_host_nat}b unreg_cgn port_alias 3000-3999" \
+ "ipfw -q add 1000 nat 123 all from any to 198.51.100.2 2000-2999 in via ${epair_host_nat}b" \
+ "ipfw -q add 2000 nat 456 all from any to 198.51.100.2 3000-3999 in via ${epair_host_nat}b" \
+ "ipfw -q add 3000 nat 123 all from 100.64.0.2 to any out via ${epair_host_nat}b" \
+ "ipfw -q add 4000 nat 456 all from 100.64.1.2 to any out via ${epair_host_nat}b"
+ else
+ firewall_config nat $firewall \
+ "ipfw" \
+ "ipfw -q nat 123 config if ${epair_host_nat}b unreg_cgn" \
+ "ipfw -q add 1000 nat 123 all from any to any"
+ fi
+
+ # ping is successful now
+ atf_check -s exit:0 -o ignore jexec client1 ping -t 1 -c 1 198.51.100.2
+ atf_check -s exit:0 -o ignore jexec client2 ping -t 1 -c 1 198.51.100.2
+
+ # if portalias, test a tcp server/client with nc
+ if [[ $portalias ]]; then
+ for inst in 1 2; do
+ daemon nc -p 198.51.100.2 7
+ atf_check -s exit:0 -o ignore jexec client$inst sh -c "echo | nc -N 198.51.100.2 7"
+ done
+ fi
+}
+
+cgn_head()
+{
+ atf_set descr 'IPv4 CGN (RFC 6598) test'
+ atf_set require.user root
+}
+
+cgn_body()
+{
+ common_cgn $1 false
+}
+
+cgn_cleanup()
+{
+ firewall_cleanup ipfw
+}
+
+portalias_head()
+{
+ atf_set descr 'IPv4 CGN (RFC 6598) port aliasing test'
+ atf_set require.user root
+}
+
+portalias_body()
+{
+ common_cgn $1 true
+}
+
+portalias_cleanup()
+{
+ firewall_cleanup ipfw
+}
+
+setup_tests \
+ basic \
+ pf \
+ ipfw \
+ ipfnat \
+ userspace_nat \
+ ipfw \
+ cgn \
+ ipfw \
+ portalias \
+ ipfw
diff --git a/tests/sys/netpfil/common/pass_block.sh b/tests/sys/netpfil/common/pass_block.sh
new file mode 100644
index 000000000000..82eb8882d4a0
--- /dev/null
+++ b/tests/sys/netpfil/common/pass_block.sh
@@ -0,0 +1,128 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Ahsan Barkati
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+. $(atf_get_srcdir)/utils.subr
+. $(atf_get_srcdir)/runner.subr
+
+v4_head()
+{
+ atf_set require.user root
+}
+
+v4_body()
+{
+ firewall=$1
+ firewall_init $firewall
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a 192.0.2.1/24 up
+ vnet_mkjail iron ${epair}b
+ jexec iron ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Block All
+ firewall_config "iron" ${firewall} \
+ "pf" \
+ "block in" \
+ "ipfw" \
+ "ipfw -q add 100 deny all from any to any" \
+ "ipf" \
+ "block in all"
+
+ atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.2
+
+ # Pass All
+ firewall_config "iron" ${firewall} \
+ "pf" \
+ "pass in" \
+ "ipfw" \
+ "ipfw -q add 100 allow all from any to any" \
+ "ipf" \
+ "pass in all"
+
+ atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.2
+}
+
+v4_cleanup()
+{
+ firewall=$1
+ firewall_cleanup $firewall
+}
+
+v6_head()
+{
+ atf_set require.user root
+}
+
+v6_body()
+{
+ firewall=$1
+ firewall_init $firewall
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a inet6 fd7a:803f:cc4b::1/64 up no_dad
+
+ vnet_mkjail iron ${epair}b
+ jexec iron ifconfig ${epair}b inet6 fd7a:803f:cc4b::2/64 up no_dad
+
+ # Block All
+ firewall_config "iron" ${firewall} \
+ "pf" \
+ "block in" \
+ "ipfw" \
+ "ipfw -q add 100 deny all from any to any" \
+ "ipf" \
+ "block in all"
+
+ atf_check -s exit:2 -o ignore ping -6 -c 1 -W 1 fd7a:803f:cc4b::2
+
+ # Pass All
+ firewall_config "iron" ${firewall} \
+ "pf" \
+ "pass in" \
+ "ipfw" \
+ "ipfw -q add 100 allow all from any to any" \
+ "ipf" \
+ "pass in all"
+
+ atf_check -s exit:0 -o ignore ping -6 -c 1 -W 1 fd7a:803f:cc4b::2
+}
+
+v6_cleanup()
+{
+ firewall=$1
+ firewall_cleanup $firewall
+}
+
+setup_tests "v4" \
+ "pf" \
+ "ipfw" \
+ "ipf" \
+ "v6" \
+ "pf" \
+ "ipfw" \
+ "ipf"
diff --git a/tests/sys/netpfil/common/pft_icmp_check.py b/tests/sys/netpfil/common/pft_icmp_check.py
new file mode 100644
index 000000000000..070465a198f7
--- /dev/null
+++ b/tests/sys/netpfil/common/pft_icmp_check.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python3
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2021 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import random
+import scapy.all as sp
+import socket
+import sys
+from sniffer import Sniffer
+
+PAYLOAD_MAGIC = bytes.fromhex('42c0ffee')
+
+def ping(send_if, dst_ip, args):
+ ether = sp.Ether()
+ ip = sp.IP(dst=dst_ip, src=args.fromaddr[0])
+ icmp = sp.ICMP(type='echo-request')
+ raw = sp.raw(PAYLOAD_MAGIC * 250) # We want 1000 bytes payload, -ish
+
+ ip.flags = 2 # Don't fragment
+ icmp.seq = random.randint(0, 65535)
+ args.icmp_seq = icmp.seq
+
+ req = ether / ip / icmp / raw
+ sp.sendp(req, iface=send_if, verbose=False)
+
+def check_icmp_too_big(args, packet):
+ """
+ Verify that this is an ICMP packet too big error, and that the IP addresses
+ in the payload packet match expectations.
+ """
+ icmp = packet.getlayer(sp.ICMP)
+ if not icmp:
+ return False
+
+ if not icmp.type == 3:
+ return False
+ ip = packet.getlayer(sp.IPerror)
+ if not ip:
+ return False
+
+ if ip.src != args.fromaddr[0]:
+ print("Incorrect src addr %s" % ip.src)
+ return False
+ if ip.dst != args.to[0]:
+ print("Incorrect dst addr %s" % ip.dst)
+ return False
+
+ icmp2 = packet.getlayer(sp.ICMPerror)
+ if not icmp2:
+ print("IPerror doesn't contain ICMP")
+ return False
+ if icmp2.seq != args.icmp_seq:
+ print("Incorrect icmp seq %d != %d" % (icmp2.seq, args.icmp_seq))
+ return False
+ return True
+
+def main():
+ parser = argparse.ArgumentParser("pft_icmp_check.py",
+ description="ICMP error validation tool")
+ parser.add_argument('--to', nargs=1, required=True,
+ help='The destination IP address')
+ parser.add_argument('--fromaddr', nargs=1, required=True,
+ help='The source IP address')
+ parser.add_argument('--sendif', nargs=1, required=True,
+ help='The interface through which the packet(s) will be sent')
+ parser.add_argument('--recvif', nargs=1,
+ help='The interface on which to expect the ICMP error')
+
+ args = parser.parse_args()
+ sniffer = None
+ if not args.recvif is None:
+ sniffer = Sniffer(args, check_icmp_too_big, args.recvif[0])
+
+ ping(args.sendif[0], args.to[0], args)
+
+ if sniffer:
+ sniffer.join()
+
+ if sniffer.correctPackets:
+ sys.exit(0)
+ else:
+ sys.exit(1)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netpfil/common/pft_ping.py b/tests/sys/netpfil/common/pft_ping.py
new file mode 100644
index 000000000000..a2a1d9c7f4ec
--- /dev/null
+++ b/tests/sys/netpfil/common/pft_ping.py
@@ -0,0 +1,758 @@
+#!/usr/bin/env python3
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2017 Kristof Provost <kp@FreeBSD.org>
+# Copyright (c) 2023 Kajetan Staszkiewicz <vegeta@tuxpowered.net>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import math
+import scapy.all as sp
+import sys
+import socket
+
+from copy import copy
+from sniffer import Sniffer
+
+logging.basicConfig(format='%(message)s')
+LOGGER = logging.getLogger(__name__)
+
+PAYLOAD_MAGIC = bytes.fromhex('42c0ffee')
+
+def build_payload(l):
+ pl = len(PAYLOAD_MAGIC)
+ ret = PAYLOAD_MAGIC * math.floor(l/pl)
+ ret += PAYLOAD_MAGIC[0:(l % pl)]
+ return ret
+
+
+def clean_params(params):
+ # Prepare a copy of safe copy of params
+ ret = copy(params)
+ ret.pop('src_address')
+ ret.pop('dst_address')
+ ret.pop('flags')
+ return ret
+
+def prepare_ipv6(send_params):
+ src_address = send_params.get('src_address')
+ dst_address = send_params.get('dst_address')
+ hlim = send_params.get('hlim')
+ tc = send_params.get('tc')
+ ip6 = sp.IPv6(dst=dst_address)
+ if src_address:
+ ip6.src = src_address
+ if hlim:
+ ip6.hlim = hlim
+ if tc:
+ ip6.tc = tc
+ return ip6
+
+
+def prepare_ipv4(send_params):
+ src_address = send_params.get('src_address')
+ dst_address = send_params.get('dst_address')
+ flags = send_params.get('flags')
+ tos = send_params.get('tc')
+ ttl = send_params.get('hlim')
+ opt = send_params.get('nop')
+ options = ''
+ if opt:
+ options='\x00'
+ ip = sp.IP(dst=dst_address, options=options)
+ if src_address:
+ ip.src = src_address
+ if flags:
+ ip.flags = flags
+ if tos:
+ ip.tos = tos
+ if ttl:
+ ip.ttl = ttl
+ return ip
+
+
+def send_icmp_ping(send_params):
+ send_length = send_params['length']
+ send_frag_length = send_params['frag_length']
+ packets = []
+ ether = sp.Ether()
+ if ':' in send_params['dst_address']:
+ ip6 = prepare_ipv6(send_params)
+ icmp = sp.ICMPv6EchoRequest(data=sp.raw(build_payload(send_length)))
+ if send_frag_length:
+ for packet in sp.fragment6(ip6 / icmp, fragSize=send_frag_length):
+ packets.append(ether / packet)
+ else:
+ packets.append(ether / ip6 / icmp)
+
+ else:
+ ip = prepare_ipv4(send_params)
+ icmp = sp.ICMP(type='echo-request')
+ raw = sp.raw(build_payload(send_length))
+ if send_frag_length:
+ for packet in sp.fragment(ip / icmp / raw, fragsize=send_frag_length):
+ packets.append(ether / packet)
+ else:
+ packets.append(ether / ip / icmp / raw)
+ for packet in packets:
+ sp.sendp(packet, iface=send_params['sendif'], verbose=False)
+
+
+def send_tcp_syn(send_params):
+ tcpopt_unaligned = send_params.get('tcpopt_unaligned')
+ seq = send_params.get('seq')
+ mss = send_params.get('mss')
+ ether = sp.Ether()
+ opts=[('Timestamp', (1, 1)), ('MSS', mss if mss else 1280)]
+ if tcpopt_unaligned:
+ opts = [('NOP', 0 )] + opts
+ if ':' in send_params['dst_address']:
+ ip = prepare_ipv6(send_params)
+ else:
+ ip = prepare_ipv4(send_params)
+ tcp = sp.TCP(
+ sport=send_params.get('sport'), dport=send_params.get('dport'),
+ flags='S', options=opts, seq=seq,
+ )
+ req = ether / ip / tcp
+ sp.sendp(req, iface=send_params['sendif'], verbose=False)
+
+
+def send_udp(send_params):
+ LOGGER.debug(f'Sending UDP ping')
+ packets = []
+ send_length = send_params['length']
+ send_frag_length = send_params['frag_length']
+ ether = sp.Ether()
+ if ':' in send_params['dst_address']:
+ ip6 = prepare_ipv6(send_params)
+ udp = sp.UDP(
+ sport=send_params.get('sport'), dport=send_params.get('dport'),
+ )
+ raw = sp.Raw(load=build_payload(send_length))
+ if send_frag_length:
+ for packet in sp.fragment6(ip6 / udp / raw, fragSize=send_frag_length):
+ packets.append(ether / packet)
+ else:
+ packets.append(ether / ip6 / udp / raw)
+ else:
+ ip = prepare_ipv4(send_params)
+ udp = sp.UDP(
+ sport=send_params.get('sport'), dport=send_params.get('dport'),
+ )
+ raw = sp.Raw(load=build_payload(send_length))
+ if send_frag_length:
+ for packet in sp.fragment(ip / udp / raw, fragsize=send_frag_length):
+ packets.append(ether / packet)
+ else:
+ packets.append(ether / ip / udp / raw)
+
+ for packet in packets:
+ sp.sendp(packet, iface=send_params['sendif'], verbose=False)
+
+
+def send_ping(ping_type, send_params):
+ if ping_type == 'icmp':
+ send_icmp_ping(send_params)
+ elif (
+ ping_type == 'tcpsyn' or
+ ping_type == 'tcp3way'
+ ):
+ send_tcp_syn(send_params)
+ elif ping_type == 'udp':
+ send_udp(send_params)
+ else:
+ raise Exception('Unsupported ping type')
+
+
+def check_ipv4(expect_params, packet):
+ src_address = expect_params.get('src_address')
+ dst_address = expect_params.get('dst_address')
+ flags = expect_params.get('flags')
+ tos = expect_params.get('tc')
+ ttl = expect_params.get('hlim')
+ ip = packet.getlayer(sp.IP)
+ LOGGER.debug(f'Packet: {ip}')
+ if not ip:
+ LOGGER.debug('Packet is not IPv4!')
+ return False
+ if src_address and ip.src != src_address:
+ LOGGER.debug(f'Wrong IPv4 source {ip.src}, expected {src_address}')
+ return False
+ if dst_address and ip.dst != dst_address:
+ LOGGER.debug(f'Wrong IPv4 destination {ip.dst}, expected {dst_address}')
+ return False
+ if flags and ip.flags != flags:
+ LOGGER.debug(f'Wrong IP flags value {ip.flags}, expected {flags}')
+ return False
+ if tos and ip.tos != tos:
+ LOGGER.debug(f'Wrong ToS value {ip.tos}, expected {tos}')
+ return False
+ if ttl and ip.ttl != ttl:
+ LOGGER.debug(f'Wrong TTL value {ip.ttl}, expected {ttl}')
+ return False
+ return True
+
+
+def check_ipv6(expect_params, packet):
+ src_address = expect_params.get('src_address')
+ dst_address = expect_params.get('dst_address')
+ flags = expect_params.get('flags')
+ hlim = expect_params.get('hlim')
+ tc = expect_params.get('tc')
+ ip6 = packet.getlayer(sp.IPv6)
+ if not ip6:
+ LOGGER.debug('Packet is not IPv6!')
+ return False
+ if src_address and socket.inet_pton(socket.AF_INET6, ip6.src) != \
+ socket.inet_pton(socket.AF_INET6, src_address):
+ LOGGER.debug(f'Wrong IPv6 source {ip6.src}, expected {src_address}')
+ return False
+ if dst_address and socket.inet_pton(socket.AF_INET6, ip6.dst) != \
+ socket.inet_pton(socket.AF_INET6, dst_address):
+ LOGGER.debug(f'Wrong IPv6 destination {ip6.dst}, expected {dst_address}')
+ return False
+ # IPv6 has no IP-level checksum.
+ if flags:
+ raise Exception("There's no fragmentation flags in IPv6")
+ if hlim and ip6.hlim != hlim:
+ LOGGER.debug(f'Wrong Hop Limit value {ip6.hlim}, expected {hlim}')
+ return False
+ if tc and ip6.tc != tc:
+ LOGGER.debug(f'Wrong TC value {ip6.tc}, expected {tc}')
+ return False
+ return True
+
+
+def check_ping_4(expect_params, packet):
+ expect_length = expect_params['length']
+ if not check_ipv4(expect_params, packet):
+ return False
+ icmp = packet.getlayer(sp.ICMP)
+ if not icmp:
+ LOGGER.debug('Packet is not IPv4 ICMP!')
+ return False
+ raw = packet.getlayer(sp.Raw)
+ if not raw:
+ LOGGER.debug('Packet contains no payload!')
+ return False
+ if raw.load != build_payload(expect_length):
+ LOGGER.debug('Payload magic does not match!')
+ return False
+ return True
+
+
+def check_ping_request_4(expect_params, packet):
+ if not check_ping_4(expect_params, packet):
+ return False
+ icmp = packet.getlayer(sp.ICMP)
+ if sp.icmptypes[icmp.type] != 'echo-request':
+ LOGGER.debug('Packet is not IPv4 ICMP Echo Request!')
+ return False
+ return True
+
+
+def check_ping_reply_4(expect_params, packet):
+ if not check_ping_4(expect_params, packet):
+ return False
+ icmp = packet.getlayer(sp.ICMP)
+ if sp.icmptypes[icmp.type] != 'echo-reply':
+ LOGGER.debug('Packet is not IPv4 ICMP Echo Reply!')
+ return False
+ return True
+
+
+def check_ping_request_6(expect_params, packet):
+ expect_length = expect_params['length']
+ if not check_ipv6(expect_params, packet):
+ return False
+ icmp = packet.getlayer(sp.ICMPv6EchoRequest)
+ if not icmp:
+ LOGGER.debug('Packet is not IPv6 ICMP Echo Request!')
+ return False
+ if icmp.data != build_payload(expect_length):
+ LOGGER.debug('Payload magic does not match!')
+ return False
+ return True
+
+
+def check_ping_reply_6(expect_params, packet):
+ expect_length = expect_params['length']
+ if not check_ipv6(expect_params, packet):
+ return False
+ icmp = packet.getlayer(sp.ICMPv6EchoReply)
+ if not icmp:
+ LOGGER.debug('Packet is not IPv6 ICMP Echo Reply!')
+ return False
+ if icmp.data != build_payload(expect_length):
+ LOGGER.debug('Payload magic does not match!')
+ return False
+ return True
+
+
+def check_ping_request(args, packet):
+ src_address = args['expect_params'].get('src_address')
+ dst_address = args['expect_params'].get('dst_address')
+ if not (src_address or dst_address):
+ raise Exception('Source or destination address must be given to match the ping request!')
+ if (
+ (src_address and ':' in src_address) or
+ (dst_address and ':' in dst_address)
+ ):
+ return check_ping_request_6(args['expect_params'], packet)
+ else:
+ return check_ping_request_4(args['expect_params'], packet)
+
+
+def check_ping_reply(args, packet):
+ src_address = args['expect_params'].get('src_address')
+ dst_address = args['expect_params'].get('dst_address')
+ if not (src_address or dst_address):
+ raise Exception('Source or destination address must be given to match the ping reply!')
+ if (
+ (src_address and ':' in src_address) or
+ (dst_address and ':' in dst_address)
+ ):
+ return check_ping_reply_6(args['expect_params'], packet)
+ else:
+ return check_ping_reply_4(args['expect_params'], packet)
+
+
+def check_tcp(expect_params, packet):
+ tcp_flags = expect_params.get('tcp_flags')
+ mss = expect_params.get('mss')
+ seq = expect_params.get('seq')
+ tcp = packet.getlayer(sp.TCP)
+ if not tcp:
+ LOGGER.debug('Packet is not TCP!')
+ return False
+ chksum = tcp.chksum
+ tcp.chksum = None
+ newpacket = sp.Ether(sp.raw(packet[sp.Ether]))
+ new_chksum = newpacket[sp.TCP].chksum
+ if new_chksum and chksum != new_chksum:
+ LOGGER.debug(f'Wrong TCP checksum {chksum}, expected {new_chksum}!')
+ return False
+ if tcp_flags and tcp.flags != tcp_flags:
+ LOGGER.debug(f'Wrong TCP flags {tcp.flags}, expected {tcp_flags}!')
+ return False
+ if seq:
+ if tcp_flags == 'S':
+ tcp_seq = tcp.seq
+ elif tcp_flags == 'SA':
+ tcp_seq = tcp.ack - 1
+ if seq != tcp_seq:
+ LOGGER.debug(f'Wrong TCP Sequence Number {tcp_seq}, expected {seq}')
+ return False
+ if mss:
+ for option in tcp.options:
+ if option[0] == 'MSS':
+ if option[1] != mss:
+ LOGGER.debug(f'Wrong TCP MSS {option[1]}, expected {mss}')
+ return False
+ return True
+
+
+def check_udp(expect_params, packet):
+ expect_length = expect_params['length']
+ udp = packet.getlayer(sp.UDP)
+ if not udp:
+ LOGGER.debug('Packet is not UDP!')
+ return False
+ raw = packet.getlayer(sp.Raw)
+ if not raw:
+ LOGGER.debug('Packet contains no payload!')
+ return False
+ if raw.load != build_payload(expect_length):
+ LOGGER.debug(f'Payload magic does not match len {len(raw.load)} vs {expect_length}!')
+ return False
+ orig_chksum = udp.chksum
+ udp.chksum = None
+ newpacket = sp.Ether(sp.raw(packet[sp.Ether]))
+ new_chksum = newpacket[sp.UDP].chksum
+ if new_chksum and orig_chksum != new_chksum:
+ LOGGER.debug(f'Wrong UDP checksum {orig_chksum}, expected {new_chksum}!')
+ return False
+
+ return True
+
+
+def check_tcp_syn_request_4(expect_params, packet):
+ if not check_ipv4(expect_params, packet):
+ return False
+ if not check_tcp(expect_params | {'tcp_flags': 'S'}, packet):
+ return False
+ return True
+
+
+def check_tcp_syn_reply_4(send_params, expect_params, packet):
+ if not check_ipv4(expect_params, packet):
+ return False
+ if not check_tcp(expect_params | {'tcp_flags': 'SA'}, packet):
+ return False
+ return True
+
+
+def check_tcp_3way_4(args, packet):
+ send_params = args['send_params']
+
+ expect_params_sa = clean_params(args['expect_params'])
+ expect_params_sa['src_address'] = send_params['dst_address']
+ expect_params_sa['dst_address'] = send_params['src_address']
+
+ # Sniff incoming SYN+ACK packet
+ if (
+ check_ipv4(expect_params_sa, packet) and
+ check_tcp(expect_params_sa | {'tcp_flags': 'SA'}, packet)
+ ):
+ ether = sp.Ether()
+ ip_sa = packet.getlayer(sp.IP)
+ tcp_sa = packet.getlayer(sp.TCP)
+ reply_params = clean_params(send_params)
+ reply_params['src_address'] = ip_sa.dst
+ reply_params['dst_address'] = ip_sa.src
+ ip_a = prepare_ipv4(reply_params)
+ tcp_a = sp.TCP(
+ sport=tcp_sa.dport, dport=tcp_sa.sport, flags='A',
+ seq=tcp_sa.ack, ack=tcp_sa.seq + 1,
+ )
+ req = ether / ip_a / tcp_a
+ sp.sendp(req, iface=send_params['sendif'], verbose=False)
+ return True
+
+ return False
+
+
+def check_udp_request_4(expect_params, packet):
+ if not check_ipv4(expect_params, packet):
+ return False
+ if not check_udp(expect_params, packet):
+ return False
+ return True
+
+
+def check_tcp_syn_request_6(expect_params, packet):
+ if not check_ipv6(expect_params, packet):
+ return False
+ if not check_tcp(expect_params | {'tcp_flags': 'S'}, packet):
+ return False
+ return True
+
+
+def check_tcp_syn_reply_6(expect_params, packet):
+ if not check_ipv6(expect_params, packet):
+ return False
+ if not check_tcp(expect_params | {'tcp_flags': 'SA'}, packet):
+ return False
+ return True
+
+
+def check_tcp_3way_6(args, packet):
+ send_params = args['send_params']
+
+ expect_params_sa = clean_params(args['expect_params'])
+ expect_params_sa['src_address'] = send_params['dst_address']
+ expect_params_sa['dst_address'] = send_params['src_address']
+
+ # Sniff incoming SYN+ACK packet
+ if (
+ check_ipv6(expect_params_sa, packet) and
+ check_tcp(expect_params_sa | {'tcp_flags': 'SA'}, packet)
+ ):
+ ether = sp.Ether()
+ ip6_sa = packet.getlayer(sp.IPv6)
+ tcp_sa = packet.getlayer(sp.TCP)
+ reply_params = clean_params(send_params)
+ reply_params['src_address'] = ip6_sa.dst
+ reply_params['dst_address'] = ip6_sa.src
+ ip_a = prepare_ipv6(reply_params)
+ tcp_a = sp.TCP(
+ sport=tcp_sa.dport, dport=tcp_sa.sport, flags='A',
+ seq=tcp_sa.ack, ack=tcp_sa.seq + 1,
+ )
+ req = ether / ip_a / tcp_a
+ sp.sendp(req, iface=send_params['sendif'], verbose=False)
+ return True
+
+ return False
+
+
+def check_udp_request_6(expect_params, packet):
+ if not check_ipv6(expect_params, packet):
+ return False
+ if not check_udp(expect_params, packet):
+ return False
+ return True
+
+def check_tcp_syn_request(args, packet):
+ expect_params = args['expect_params']
+ src_address = expect_params.get('src_address')
+ dst_address = expect_params.get('dst_address')
+ if not (src_address or dst_address):
+ raise Exception('Source or destination address must be given to match the tcp syn request!')
+ if (
+ (src_address and ':' in src_address) or
+ (dst_address and ':' in dst_address)
+ ):
+ return check_tcp_syn_request_6(expect_params, packet)
+ else:
+ return check_tcp_syn_request_4(expect_params, packet)
+
+
+def check_tcp_syn_reply(args, packet):
+ expect_params = args['expect_params']
+ src_address = expect_params.get('src_address')
+ dst_address = expect_params.get('dst_address')
+ if not (src_address or dst_address):
+ raise Exception('Source or destination address must be given to match the tcp syn reply!')
+ if (
+ (src_address and ':' in src_address) or
+ (dst_address and ':' in dst_address)
+ ):
+ return check_tcp_syn_reply_6(expect_params, packet)
+ else:
+ return check_tcp_syn_reply_4(expect_params, packet)
+
+def check_tcp_3way(args, packet):
+ expect_params = args['expect_params']
+ src_address = expect_params.get('src_address')
+ dst_address = expect_params.get('dst_address')
+ if not (src_address or dst_address):
+ raise Exception('Source or destination address must be given to match the tcp syn reply!')
+ if (
+ (src_address and ':' in src_address) or
+ (dst_address and ':' in dst_address)
+ ):
+ return check_tcp_3way_6(args, packet)
+ else:
+ return check_tcp_3way_4(args, packet)
+
+
+def check_udp_request(args, packet):
+ expect_params = args['expect_params']
+ src_address = expect_params.get('src_address')
+ dst_address = expect_params.get('dst_address')
+ if not (src_address or dst_address):
+ raise Exception('Source or destination address must be given to match the tcp syn request!')
+ if (
+ (src_address and ':' in src_address) or
+ (dst_address and ':' in dst_address)
+ ):
+ return check_udp_request_6(expect_params, packet)
+ else:
+ return check_udp_request_4(expect_params, packet)
+
+
+def setup_sniffer(
+ recvif, ping_type, sniff_type, expect_params, defrag, send_params,
+):
+ if ping_type == 'icmp' and sniff_type == 'request':
+ checkfn = check_ping_request
+ elif ping_type == 'icmp' and sniff_type == 'reply':
+ checkfn = check_ping_reply
+ elif ping_type == 'tcpsyn' and sniff_type == 'request':
+ checkfn = check_tcp_syn_request
+ elif ping_type == 'tcpsyn' and sniff_type == 'reply':
+ checkfn = check_tcp_syn_reply
+ elif ping_type == 'tcp3way' and sniff_type == 'reply':
+ checkfn = check_tcp_3way
+ elif ping_type == 'udp' and sniff_type == 'request':
+ checkfn = check_udp_request
+ else:
+ raise Exception('Unspported ping and sniff type combination')
+
+ return Sniffer(
+ {'send_params': send_params, 'expect_params': expect_params},
+ checkfn, recvif, defrag=defrag,
+ )
+
+
+def parse_args():
+ parser = argparse.ArgumentParser("pft_ping.py",
+ description="Ping test tool")
+
+ # Parameters of sent ping request
+ parser.add_argument('--sendif', required=True,
+ help='The interface through which the packet(s) will be sent')
+ parser.add_argument('--to', required=True,
+ help='The destination IP address for the ping request')
+ parser.add_argument('--ping-type',
+ choices=('icmp', 'tcpsyn', 'tcp3way', 'udp'),
+ help='Type of ping: ICMP (default) or TCP SYN or 3-way TCP handshake',
+ default='icmp')
+ parser.add_argument('--fromaddr',
+ help='The source IP address for the ping request')
+
+ # Where to look for packets to analyze.
+ # The '+' format is ugly as it mixes positional with optional syntax.
+ # But we have no positional parameters so I guess it's fine to use it.
+ parser.add_argument('--recvif', nargs='+',
+ help='The interfaces on which to expect the ping request')
+ parser.add_argument('--replyif', nargs='+',
+ help='The interfaces which to expect the ping response')
+
+ # Packet settings
+ parser_send = parser.add_argument_group('Values set in transmitted packets')
+ parser_send.add_argument('--send-flags', type=str,
+ help='IPv4 fragmentation flags')
+ parser_send.add_argument('--send-frag-length', type=int,
+ help='Force IP fragmentation with given fragment length')
+ parser_send.add_argument('--send-hlim', type=int,
+ help='IPv6 Hop Limit or IPv4 Time To Live')
+ parser_send.add_argument('--send-mss', type=int,
+ help='TCP Maximum Segment Size')
+ parser_send.add_argument('--send-seq', type=int,
+ help='TCP sequence number')
+ parser_send.add_argument('--send-sport', type=int,
+ help='TCP source port')
+ parser_send.add_argument('--send-dport', type=int, default=9,
+ help='TCP destination port')
+ parser_send.add_argument('--send-length', type=int, default=len(PAYLOAD_MAGIC),
+ help='ICMP Echo Request payload size')
+ parser_send.add_argument('--send-tc', type=int,
+ help='IPv6 Traffic Class or IPv4 DiffServ / ToS')
+ parser_send.add_argument('--send-tcpopt-unaligned', action='store_true',
+ help='Include unaligned TCP options')
+ parser_send.add_argument('--send-nop', action='store_true',
+ help='Include a NOP IPv4 option')
+
+ # Expectations
+ parser_expect = parser.add_argument_group('Values expected in sniffed packets')
+ parser_expect.add_argument('--expect-flags', type=str,
+ help='IPv4 fragmentation flags')
+ parser_expect.add_argument('--expect-hlim', type=int,
+ help='IPv6 Hop Limit or IPv4 Time To Live')
+ parser_expect.add_argument('--expect-mss', type=int,
+ help='TCP Maximum Segment Size')
+ parser_send.add_argument('--expect-seq', type=int,
+ help='TCP sequence number')
+ parser_expect.add_argument('--expect-tc', type=int,
+ help='IPv6 Traffic Class or IPv4 DiffServ / ToS')
+
+ parser.add_argument('-v', '--verbose', action='store_true',
+ help=('Enable verbose logging. Apart of potentially useful information '
+ 'you might see warnings from parsing packets like NDP or other '
+ 'packets not related to the test being run. Use only when '
+ 'developing because real tests expect empty stderr and stdout.'))
+
+ return parser.parse_args()
+
+
+def main():
+ args = parse_args()
+
+ if args.verbose:
+ LOGGER.setLevel(logging.DEBUG)
+
+ # Split parameters into send and expect parameters. Parameters might be
+ # missing from the command line, always fill the dictionaries with None.
+ send_params = {}
+ expect_params = {}
+ for param_name in (
+ 'flags', 'hlim', 'length', 'mss', 'seq', 'tc', 'frag_length',
+ 'sport', 'dport',
+ ):
+ param_arg = vars(args).get(f'send_{param_name}')
+ send_params[param_name] = param_arg if param_arg else None
+ param_arg = vars(args).get(f'expect_{param_name}')
+ expect_params[param_name] = param_arg if param_arg else None
+
+ expect_params['length'] = send_params['length']
+ send_params['tcpopt_unaligned'] = args.send_tcpopt_unaligned
+ send_params['nop'] = args.send_nop
+ send_params['src_address'] = args.fromaddr if args.fromaddr else None
+ send_params['dst_address'] = args.to
+ send_params['sendif'] = args.sendif
+
+ # We may not have a default route. Tell scapy where to start looking for routes
+ sp.conf.iface6 = args.sendif
+
+ # Configuration sanity checking.
+ if not (args.replyif or args.recvif):
+ raise Exception('With no reply or recv interface specified no traffic '
+ 'can be sniffed and verified!'
+ )
+
+ sniffers = []
+
+ if send_params['frag_length']:
+ if (
+ (send_params['src_address'] and ':' in send_params['src_address']) or
+ (send_params['dst_address'] and ':' in send_params['dst_address'])
+ ):
+ defrag = 'IPv6'
+ else:
+ defrag = 'IPv4'
+ else:
+ defrag = False
+
+ if args.recvif:
+ sniffer_params = copy(expect_params)
+ sniffer_params['src_address'] = None
+ sniffer_params['dst_address'] = args.to
+ for iface in args.recvif:
+ LOGGER.debug(f'Installing receive sniffer on {iface}')
+ sniffers.append(
+ setup_sniffer(iface, args.ping_type, 'request',
+ sniffer_params, defrag, send_params,
+ ))
+
+ if args.replyif:
+ sniffer_params = copy(expect_params)
+ sniffer_params['src_address'] = args.to
+ sniffer_params['dst_address'] = None
+ for iface in args.replyif:
+ LOGGER.debug(f'Installing reply sniffer on {iface}')
+ sniffers.append(
+ setup_sniffer(iface, args.ping_type, 'reply',
+ sniffer_params, defrag, send_params,
+ ))
+
+ LOGGER.debug(f'Installed {len(sniffers)} sniffers')
+
+ send_ping(args.ping_type, send_params)
+
+ err = 0
+ sniffer_num = 0
+ for sniffer in sniffers:
+ sniffer.join()
+ if sniffer.correctPackets == 1:
+ LOGGER.debug(f'Expected ping has been sniffed on {sniffer._recvif}.')
+ else:
+ # Set a bit in err for each failed sniffer.
+ err |= 1<<sniffer_num
+ if sniffer.correctPackets > 1:
+ LOGGER.debug(f'Duplicated ping has been sniffed on {sniffer._recvif}!')
+ else:
+ LOGGER.debug(f'Expected ping has not been sniffed on {sniffer._recvif}!')
+ sniffer_num += 1
+
+ return err
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/tests/sys/netpfil/common/pft_rst.py b/tests/sys/netpfil/common/pft_rst.py
new file mode 100644
index 000000000000..28d951e8a047
--- /dev/null
+++ b/tests/sys/netpfil/common/pft_rst.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python3
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2023 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import math
+import scapy.all as sp
+import sys
+
+def send_rst(src_ip, src_port, dst_ip, dst_port):
+ sp.send(sp.IP(src=src_ip, dst=dst_ip) /
+ sp.TCP(sport=src_port, dport=dst_port, seq=1, flags="R"))
+
+send_rst(sys.argv[1], int(sys.argv[2]), sys.argv[3], int(sys.argv[4]))
diff --git a/tests/sys/netpfil/common/pft_synflood.py b/tests/sys/netpfil/common/pft_synflood.py
new file mode 100644
index 000000000000..f73caa1b6aa6
--- /dev/null
+++ b/tests/sys/netpfil/common/pft_synflood.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python3
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2021 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+
+def syn_flood(args):
+ s = sp.conf.L2socket(iface=args.sendif[0])
+
+ # Set a src mac, to avoid doing lookups which really slow us down.
+ ether = sp.Ether(src='01:02:03:04:05')
+ if args.ip6:
+ ip = sp.IPv6(dst=args.to[0])
+ else:
+ ip = sp.IP(dst=args.to[0])
+ for i in range(int(args.count[0])):
+ tcp = sp.TCP(flags='S', sport=1+i, dport=22, seq=500+i)
+ pkt = ether / ip / tcp
+ s.send(pkt)
+
+def main():
+ parser = argparse.ArgumentParser("pft_synflood.py",
+ description="SYN flooding tool")
+ parser.add_argument('--ip6',
+ action='store_true',
+ help='Use IPv6 rather than IPv4')
+ parser.add_argument('--sendif', nargs=1,
+ required=True,
+ help='The interface through which the packet(s) will be sent')
+ parser.add_argument('--to', nargs=1,
+ required=True,
+ help='The destination IP address for the SYN packets')
+ parser.add_argument('--count', nargs=1,
+ required=True,
+ help='The number of packets to send')
+
+ args = parser.parse_args()
+
+ syn_flood(args)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netpfil/common/rdr.sh b/tests/sys/netpfil/common/rdr.sh
new file mode 100644
index 000000000000..0d6f27694c8c
--- /dev/null
+++ b/tests/sys/netpfil/common/rdr.sh
@@ -0,0 +1,137 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2018 Kristof Provost <kp@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+. $(atf_get_srcdir)/runner.subr
+
+basic_head()
+{
+ atf_set descr 'Basic IPv4 NAT test'
+ atf_set require.user root
+}
+
+basic_body()
+{
+ firewall=$1
+ firewall_init $firewall
+ nat_init $firewall
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair}b
+
+ ifconfig ${epair}a 192.0.2.2/24 up
+ route add -net 198.51.100.0/24 192.0.2.1
+
+ jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up
+ jexec alcatraz sysctl net.inet.ip.forwarding=1
+
+ # Enable redirect filter rule
+ firewall_config alcatraz ${firewall} \
+ "pf" \
+ "rdr pass on ${epair}b proto tcp from any to 198.51.100.0/24 port 1234 -> 192.0.2.1 port 4321" \
+ "ipfnat" \
+ "rdr ${epair}b from any to 198.51.100.0/24 port = 1234 -> 192.0.2.1 port 4321 tcp"
+
+
+ echo "foo" | jexec alcatraz nc -N -l 4321 &
+ sleep 1
+
+ result=$(nc -N -w 3 198.51.100.2 1234)
+ if [ "$result" != "foo" ]; then
+ atf_fail "Redirect failed"
+ fi
+}
+
+basic_cleanup()
+{
+ firewall=$1
+ firewall_cleanup $firewall
+}
+
+local_redirect_head()
+{
+ atf_set descr 'Redirect local traffic test'
+ atf_set require.user root
+}
+
+local_redirect_body()
+{
+ firewall=$1
+ firewall_init $firewall
+ nat_init $firewall
+ vnet_init_bridge
+
+ bridge=$(vnet_mkbridge)
+ ifconfig ${bridge} 192.0.2.1/24 up
+
+ epair1=$(vnet_mkepair)
+ epair2=$(vnet_mkepair)
+
+ vnet_mkjail first ${epair1}b
+ ifconfig ${epair1}a up
+ ifconfig ${bridge} addm ${epair1}a
+ jexec first ifconfig ${epair1}b 192.0.2.2/24 up
+ jexec first ifconfig lo0 127.0.0.1/8 up
+
+ vnet_mkjail second ${epair2}b
+ ifconfig ${epair2}a up
+ ifconfig ${bridge} addm ${epair2}a
+ jexec second ifconfig ${epair2}b 192.0.2.3/24 up
+ jexec second ifconfig lo0 127.0.0.1/8 up
+ jexec second sysctl net.inet.ip.forwarding=1
+
+ # Enable redirect filter rule
+ firewall_config second ${firewall} \
+ "pf" \
+ "rdr pass proto tcp from any to 192.0.2.3/24 port 1234 -> 192.0.2.2 port 4321" \
+ "ipfnat" \
+ "rdr '*' from any to 192.0.2.3/24 port = 1234 -> 192.0.2.2 port 4321 tcp"
+
+ echo "foo" | jexec first nc -N -l 4321 &
+ sleep 1
+
+ # Verify that second can use its rule to redirect local connections to first
+ result=$(jexec second nc -N -w 3 192.0.2.3 1234)
+ if [ "$result" != "foo" ]; then
+ atf_fail "Redirect failed"
+ fi
+}
+
+local_redirect_cleanup()
+{
+ firewall=$1
+ firewall_cleanup $firewall
+}
+
+setup_tests \
+ basic \
+ pf \
+ ipfnat \
+ local_redirect \
+ pf \
+ ipfnat
+
diff --git a/tests/sys/netpfil/common/runner.subr b/tests/sys/netpfil/common/runner.subr
new file mode 100644
index 000000000000..d95e60919251
--- /dev/null
+++ b/tests/sys/netpfil/common/runner.subr
@@ -0,0 +1,66 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Ahsan Barkati
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+. $(atf_get_srcdir)/utils.subr
+
+setup_tests()
+{
+ tests=""
+ while [ $# -gt 0 ]; do
+ if [ $(is_firewall $1) -eq 1 ]; then
+ fw=$1
+ shift
+ if [ -z "${testcase}" ]; then
+ echo "no testcase passed to setup_test"
+ return
+ fi
+ atf_test_case "${fw}_${testcase}" "cleanup"
+ eval "${fw}_${testcase}_head(){ ${testcase}_head; }"
+ eval "${fw}_${testcase}_body(){ ${testcase}_body $fw; }"
+ eval "${fw}_${testcase}_cleanup(){ ${testcase}_cleanup $fw; }"
+ tests="$tests ${fw}_${testcase}"
+ else
+ testcase=$1
+ shift
+ fi
+ done
+ init_testcases "$tests"
+}
+
+
+init_testcases()
+{
+ args="$@"
+ atf_init_test_cases()
+ {
+ for testcase in $args;
+ do
+ atf_add_test_case "$testcase"
+ done
+ }
+} \ No newline at end of file
diff --git a/tests/sys/netpfil/common/sniffer.py b/tests/sys/netpfil/common/sniffer.py
new file mode 100644
index 000000000000..583b27d34ca6
--- /dev/null
+++ b/tests/sys/netpfil/common/sniffer.py
@@ -0,0 +1,75 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2017 Kristof Provost <kp@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+import threading
+import scapy.all as sp
+import sys
+
+class Sniffer(threading.Thread):
+ def __init__(self, args, check_function, recvif, timeout=3, defrag=False):
+ threading.Thread.__init__(self)
+
+ self._sem = threading.Semaphore(0)
+ self._args = args
+ self._timeout = timeout
+ self._recvif = recvif
+ self._check_function = check_function
+ self._defrag = defrag
+ self.correctPackets = 0
+
+ self.start()
+ if not self._sem.acquire(timeout=30):
+ raise Exception("Failed to start sniffer")
+
+ def _checkPacket(self, packet):
+ ret = self._check_function(self._args, packet)
+ if ret:
+ self.correctPackets += 1
+ return ret
+
+ def _startedCb(self):
+ self._sem.release()
+
+ def run(self):
+ self.packets = []
+ # With fragment reassembly we can't stop the sniffer after catching
+ # the good packets, as those have not been reassembled. We must
+ # wait for sniffer to finish and check returned packets instead.
+ if self._defrag == 'IPv4':
+ self.packets = sp.sniff(session=sp.IPSession, iface=self._recvif,
+ timeout=self._timeout, started_callback=self._startedCb)
+ for p in self.packets:
+ self._checkPacket(p)
+ elif self._defrag == 'IPv6':
+ self.packets = sp.sniff(session=sp.DefaultSession, iface=self._recvif,
+ timeout=self._timeout, started_callback=self._startedCb)
+ for p in sp.defragment6(self.packets):
+ self._checkPacket(p)
+ else:
+ self.packets = sp.sniff(iface=self._recvif,
+ stop_filter=self._checkPacket, timeout=self._timeout,
+ started_callback=self._startedCb)
diff --git a/tests/sys/netpfil/common/tos.sh b/tests/sys/netpfil/common/tos.sh
new file mode 100644
index 000000000000..3b689d7f67d0
--- /dev/null
+++ b/tests/sys/netpfil/common/tos.sh
@@ -0,0 +1,117 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Ahsan Barkati
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+. $(atf_get_srcdir)/utils.subr
+. $(atf_get_srcdir)/runner.subr
+
+tos_head()
+{
+ atf_set descr 'set-tos test'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+tos_body()
+{
+ firewall=$1
+ firewall_init $firewall
+
+ epair_send=$(vnet_mkepair)
+ ifconfig ${epair_send}a 192.0.2.1/24 up
+
+ epair_recv=$(vnet_mkepair)
+ ifconfig ${epair_recv}a up
+
+ vnet_mkjail iron ${epair_send}b ${epair_recv}b
+ jexec iron ifconfig ${epair_send}b 192.0.2.2/24 up
+ jexec iron ifconfig ${epair_recv}b 198.51.100.2/24 up
+ jexec iron sysctl net.inet.ip.forwarding=1
+ jexec iron arp -s 198.51.100.3 00:01:02:03:04:05
+ route add -net 198.51.100.0/24 192.0.2.2
+
+ # Check if the firewall is able to set the ToS bits
+ firewall_config "iron" ${firewall} \
+ "pf" \
+ "scrub out proto icmp set-tos 36" \
+ "ipfw" \
+ "ipfw -q add 100 setdscp 9 ip from any to any"
+ # dscp is set to 9 because last two bits are for
+ # EN and hence tos would be 36
+
+ atf_check -s exit:0 $(atf_get_srcdir)/pft_ping.py \
+ --sendif ${epair_send}a \
+ --to 198.51.100.3 \
+ --recvif ${epair_recv}a \
+ --expect-tc 36
+
+ # Check if the firewall is able to set the ToS bits
+ # and persists the EN bits (if already set)
+ firewall_config "iron" ${firewall} \
+ "pf" \
+ "scrub out proto icmp set-tos 36" \
+ "ipfw" \
+ "ipfw -q add 100 setdscp 9 ip from any to any"
+
+ atf_check -s exit:0 $(atf_get_srcdir)/pft_ping.py \
+ --sendif ${epair_send}a \
+ --to 198.51.100.3 \
+ --recvif ${epair_recv}a \
+ --send-tc 3 \
+ --expect-tc 39
+
+ # Check if the firewall is able to filter the
+ # packets based on the ToS value
+ firewall_config "iron" ${firewall} \
+ "pf" \
+ "block all tos 36" \
+ "ipfw" \
+ "ipfw -q add 100 deny all from any to any dscp 9"
+
+ atf_check -s exit:1 $(atf_get_srcdir)/pft_ping.py \
+ --sendif ${epair_send}a \
+ --to 198.51.100.3 \
+ --recvif ${epair_recv}a \
+ --send-tc 36
+
+ atf_check -s exit:0 $(atf_get_srcdir)/pft_ping.py \
+ --sendif ${epair_send}a \
+ --to 198.51.100.3 \
+ --recvif ${epair_recv}a \
+ --send-tc 32
+}
+
+tos_cleanup()
+{
+ firewall=$1
+ firewall_cleanup $firewall
+}
+
+setup_tests \
+ "tos" \
+ "pf" \
+ "ipfw"
diff --git a/tests/sys/netpfil/common/utils.subr b/tests/sys/netpfil/common/utils.subr
new file mode 100644
index 000000000000..528cf6642508
--- /dev/null
+++ b/tests/sys/netpfil/common/utils.subr
@@ -0,0 +1,143 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Ahsan Barkati
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+. $(atf_get_srcdir)/../../common/vnet.subr
+
+firewall_config()
+{
+ jname=$1
+ shift
+ fw=$1
+ shift
+
+ while [ $# -gt 0 ]; do
+ if [ $(is_firewall "$1") -eq 1 ]; then
+ current_fw="$1"
+ shift
+ filename=${current_fw}.rule
+ cwd=$(pwd)
+ if [ -f ${current_fw}.rule ]; then
+ rm ${current_fw}.rule
+ fi
+ fi
+ rule=$1
+ echo $rule >> $filename
+ shift
+ done
+
+ if [ ${fw} == "ipfw" ]; then
+ jexec ${jname} ipfw -q -f flush
+ jexec ${jname} /bin/sh $cwd/ipfw.rule
+ elif [ ${fw} == "pf" ]; then
+ jexec ${jname} sysctl net.pf.filter_local=1
+ jexec ${jname} pfctl -e
+ jexec ${jname} pfctl -F all
+ jexec ${jname} pfctl -f $cwd/pf.rule
+ elif [ ${fw} == "ipf" ]; then
+ jexec ${jname} ipf -E
+ jexec ${jname} ipf -Fa -f $cwd/ipf.rule
+ elif [ ${fw} == "ipfnat" ]; then
+ jexec ${jname} service ipfilter start
+ jexec ${jname} ipnat -CF -f $cwd/ipfnat.rule
+ jexec ${jname} pfilctl link -o ipfilter:default-ip4 inet-local
+ jexec ${jname} pfilctl link -o ipfilter:default-ip6 inet6-local
+ else
+ atf_fail "$fw is not a valid firewall to configure"
+ fi
+}
+
+firewall_cleanup()
+{
+ firewall=$1
+ echo "Cleaning $firewall"
+ vnet_cleanup
+}
+
+firewall_init()
+{
+ firewall=$1
+ vnet_init
+
+ if [ ${firewall} == "ipfw" ]; then
+ if ! kldstat -q -m ipfw; then
+ atf_skip "This test requires ipfw"
+ fi
+ elif [ ${firewall} == "pf" ]; then
+ if [ ! -c /dev/pf ]; then
+ atf_skip "This test requires pf"
+ fi
+ elif [ ${firewall} == "ipf" ]; then
+ if ! kldstat -q -m ipfilter; then
+ atf_skip "This test requires ipf"
+ fi
+ elif [ ${firewall} == "ipfnat" ]; then
+ if ! kldstat -q -m ipfilter; then
+ atf_skip "This test requires ipf"
+ fi
+ else
+ atf_fail "$fw is not a valid firewall to initialize"
+ fi
+
+}
+
+dummynet_init()
+{
+ firewall=$1
+
+ if ! kldstat -q -m dummynet; then
+ atf_skip "This test requires dummynet"
+ fi
+
+ case $firewall in
+ ipfw|pf)
+ # Nothing. This is okay.
+ ;;
+ *)
+ atf_skip "${firewall} does not support dummynet"
+ ;;
+ esac
+}
+
+nat_init()
+{
+ firewall=$1
+ if [ ${firewall} == "ipfw" ]; then
+ if ! kldstat -q -m ipfw_nat; then
+ atf_skip "This test requires ipfw_nat"
+ fi
+ fi
+}
+
+is_firewall()
+{
+ if [ "$1" = "pf" -o "$1" = "ipfw" -o "$1" = "ipf" -o "$1" = "ipfnat" ]; then
+ echo 1
+ else
+ echo 0
+ fi
+}
diff --git a/tests/sys/netpfil/ipfw/Makefile b/tests/sys/netpfil/ipfw/Makefile
new file mode 100644
index 000000000000..d4dbdb00f251
--- /dev/null
+++ b/tests/sys/netpfil/ipfw/Makefile
@@ -0,0 +1,14 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/netpfil/ipfw
+
+ATF_TESTS_SH+= fwd \
+ divert
+
+${PACKAGE}FILES+= fwd_inetd.conf
+
+# Allow tests to run in parallel in their own jails
+TEST_METADATA+= execenv="jail"
+TEST_METADATA+= execenv_jail_params="vnet allow.raw_sockets"
+
+.include <bsd.test.mk>
diff --git a/tests/sys/netpfil/ipfw/divert.sh b/tests/sys/netpfil/ipfw/divert.sh
new file mode 100644
index 000000000000..62db3f8fce98
--- /dev/null
+++ b/tests/sys/netpfil/ipfw/divert.sh
@@ -0,0 +1,281 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2023 Igor Ostapenko <pm@igoro.pro>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+#
+# ipfw divert action test cases
+#
+# -----------| |-- |----| ----| |-----------
+# ( ) inbound |ipfw| ) -> |host| -> ( ) |ipfw| outbound )
+# -----------| | |-- |----| ----| | |-----------
+# | |
+# \|/ \|/
+# |------| |------|
+# |divapp| |divapp|
+# |------| |------|
+#
+# The basic cases:
+# - inbound > diverted | divapp terminated
+# - inbound > diverted > inbound | host terminated
+# - inbound > diverted > outbound | network terminated
+# - outbound > diverted | divapp terminated
+# - outbound > diverted > outbound | network terminated
+# - outbound > diverted > inbound | e.g. host terminated
+#
+# When a packet is diverted, forwarded, and possibly diverted again:
+# - inbound > diverted > inbound > forwarded
+# > outbound | network terminated
+# - inbound > diverted > inbound > forwarded
+# > outbound > diverted > outbound | network terminated
+#
+# Test case naming legend:
+# in - inbound
+# div - diverted
+# out - outbound
+# fwd - forwarded
+#
+
+. $(atf_get_srcdir)/../common/utils.subr
+
+divert_init()
+{
+ if ! kldstat -q -m ipdivert; then
+ atf_skip "This test requires ipdivert"
+ fi
+}
+
+atf_test_case "in_div" "cleanup"
+in_div_head()
+{
+ atf_set descr 'Test inbound > diverted | divapp terminated'
+ atf_set require.user root
+}
+in_div_body()
+{
+ firewall_init "ipfw"
+ divert_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail div ${epair}b
+ ifconfig ${epair}a 192.0.2.1/24 up
+ jexec div ifconfig ${epair}b 192.0.2.2/24 up
+ jexec div ipfw add 65534 allow all from any to any
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c3 192.0.2.2
+
+ jexec div ipfw add 100 divert 2000 icmp from any to any in icmptypes 8
+
+ jexec div $(atf_get_srcdir)/../common/divapp 2000 &
+ divapp_pid=$!
+ # Wait for the divapp to be ready
+ sleep 1
+
+ # divapp is expected to "eat" the packet
+ atf_check -s not-exit:0 -o ignore ping -c1 -t1 192.0.2.2
+
+ wait $divapp_pid
+}
+in_div_cleanup()
+{
+ firewall_cleanup "ipfw"
+}
+
+atf_test_case "in_div_in" "cleanup"
+in_div_in_head()
+{
+ atf_set descr 'Test inbound > diverted > inbound | host terminated'
+ atf_set require.user root
+}
+in_div_in_body()
+{
+ firewall_init "ipfw"
+ divert_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail div ${epair}b
+ ifconfig ${epair}a 192.0.2.1/24 up
+ jexec div ifconfig ${epair}b 192.0.2.2/24 up
+ jexec div ipfw add 65534 allow all from any to any
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c3 192.0.2.2
+
+ jexec div ipfw add 100 divert 2000 icmp from any to any in icmptypes 8
+
+ jexec div $(atf_get_srcdir)/../common/divapp 2000 divert-back &
+ divapp_pid=$!
+ # Wait for the divapp to be ready
+ sleep 1
+
+ # divapp is NOT expected to "eat" the packet
+ atf_check -s exit:0 -o ignore ping -c1 192.0.2.2
+
+ wait $divapp_pid
+}
+in_div_in_cleanup()
+{
+ firewall_cleanup "ipfw"
+}
+
+atf_test_case "out_div" "cleanup"
+out_div_head()
+{
+ atf_set descr 'Test outbound > diverted | divapp terminated'
+ atf_set require.user root
+}
+out_div_body()
+{
+ firewall_init "ipfw"
+ divert_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail div ${epair}b
+ ifconfig ${epair}a 192.0.2.1/24 up
+ jexec div ifconfig ${epair}b 192.0.2.2/24 up
+ jexec div ipfw add 65534 allow all from any to any
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c3 192.0.2.2
+
+ jexec div ipfw add 100 divert 2000 icmp from any to any out icmptypes 0
+
+ jexec div $(atf_get_srcdir)/../common/divapp 2000 &
+ divapp_pid=$!
+ # Wait for the divapp to be ready
+ sleep 1
+
+ # divapp is expected to "eat" the packet
+ atf_check -s not-exit:0 -o ignore ping -c1 -t1 192.0.2.2
+
+ wait $divapp_pid
+}
+out_div_cleanup()
+{
+ firewall_cleanup "ipfw"
+}
+
+atf_test_case "out_div_out" "cleanup"
+out_div_out_head()
+{
+ atf_set descr 'Test outbound > diverted > outbound | network terminated'
+ atf_set require.user root
+}
+out_div_out_body()
+{
+ firewall_init "ipfw"
+ divert_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail div ${epair}b
+ ifconfig ${epair}a 192.0.2.1/24 up
+ jexec div ifconfig ${epair}b 192.0.2.2/24 up
+ jexec div ipfw add 65534 allow all from any to any
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c3 192.0.2.2
+
+ jexec div ipfw add 100 divert 2000 icmp from any to any out icmptypes 0
+
+ jexec div $(atf_get_srcdir)/../common/divapp 2000 divert-back &
+ divapp_pid=$!
+ # Wait for the divapp to be ready
+ sleep 1
+
+ # divapp is NOT expected to "eat" the packet
+ atf_check -s exit:0 -o ignore ping -c1 192.0.2.2
+
+ wait $divapp_pid
+}
+out_div_out_cleanup()
+{
+ firewall_cleanup "ipfw"
+}
+
+atf_test_case "in_div_in_fwd_out_div_out" "cleanup"
+in_div_in_fwd_out_div_out_head()
+{
+ atf_set descr 'Test inbound > diverted > inbound > forwarded > outbound > diverted > outbound | network terminated'
+ atf_set require.user root
+}
+in_div_in_fwd_out_div_out_body()
+{
+ firewall_init "ipfw"
+ divert_init
+
+ # host <a--epair0--b> router <a--epair1--b> site
+ epair0=$(vnet_mkepair)
+ epair1=$(vnet_mkepair)
+
+ vnet_mkjail router ${epair0}b ${epair1}a
+ ifconfig ${epair0}a 192.0.2.1/24 up
+ jexec router sysctl net.inet.ip.forwarding=1
+ jexec router ifconfig ${epair0}b 192.0.2.2/24 up
+ jexec router ifconfig ${epair1}a 198.51.100.1/24 up
+ jexec router ipfw add 65534 allow all from any to any
+
+ vnet_mkjail site ${epair1}b
+ jexec site ifconfig ${epair1}b 198.51.100.2/24 up
+ jexec site ipfw add 65534 allow all from any to any
+ jexec site route add default 198.51.100.1
+
+ route add -net 198.51.100.0/24 192.0.2.2
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c3 192.0.2.2
+
+ # Should be routed without diversion
+ atf_check -s exit:0 -o ignore ping -c3 198.51.100.2
+
+ jexec router ipfw add 100 divert 2001 icmp from any to any in icmptypes 8
+ jexec router ipfw add 200 divert 2002 icmp from any to any out icmptypes 8
+
+ jexec router $(atf_get_srcdir)/../common/divapp 2001 divert-back &
+ indivapp_pid=$!
+ jexec router $(atf_get_srcdir)/../common/divapp 2002 divert-back &
+ outdivapp_pid=$!
+ # Wait for the divappS to be ready
+ sleep 1
+
+ # Both divappS are NOT expected to "eat" the packet
+ atf_check -s exit:0 -o ignore ping -c1 198.51.100.2
+
+ wait $indivapp_pid && wait $outdivapp_pid
+}
+in_div_in_fwd_out_div_out_cleanup()
+{
+ firewall_cleanup "ipfw"
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "in_div"
+ atf_add_test_case "in_div_in"
+
+ atf_add_test_case "out_div"
+ atf_add_test_case "out_div_out"
+
+ atf_add_test_case "in_div_in_fwd_out_div_out"
+}
diff --git a/tests/sys/netpfil/ipfw/fwd.sh b/tests/sys/netpfil/ipfw/fwd.sh
new file mode 100755
index 000000000000..9f7a6d48e12f
--- /dev/null
+++ b/tests/sys/netpfil/ipfw/fwd.sh
@@ -0,0 +1,70 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2022 Gleb Smirnoff <glebius@FreeBSD.org>
+# Copyright (c) 2022 Pavel Polyakov <bsd@kobyla.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/../common/utils.subr
+
+atf_test_case "local" "cleanup"
+local_head()
+{
+ atf_set descr 'Test that fwd 127.0.0.1,port works'
+ atf_set require.user root
+}
+
+local_body()
+{
+ firewall_init "ipfw"
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}b
+
+ ifconfig ${epair}a 192.0.2.0/31 up
+ route add 192.0.2.3/32 192.0.2.1
+
+ jexec alcatraz ifconfig lo0 127.0.0.1/8 up
+ jexec alcatraz ifconfig ${epair}b 192.0.2.1/31 up
+ jexec alcatraz route add default 192.0.2.0
+ jexec alcatraz /usr/sbin/inetd -p /dev/null $(atf_get_srcdir)/fwd_inetd.conf
+
+ firewall_config alcatraz ipfw ipfw \
+ "ipfw add 10 fwd 127.0.0.1,82 tcp from any to any dst-port 80 in via ${epair}b" \
+ "ipfw add 20 allow all from any to any"
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -i .1 -c 3 -s 1200 192.0.2.1
+
+ reply=$(nc -nN 192.0.2.3 80 < /dev/null)
+ atf_check [ "${reply}" = "GOOD 82" ]
+}
+
+local_cleanup()
+{
+ firewall_cleanup $1
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "local"
+}
diff --git a/tests/sys/netpfil/ipfw/fwd_inetd.conf b/tests/sys/netpfil/ipfw/fwd_inetd.conf
new file mode 100644
index 000000000000..0acafbc4cc2e
--- /dev/null
+++ b/tests/sys/netpfil/ipfw/fwd_inetd.conf
@@ -0,0 +1,6 @@
+#
+# Set up the correct listener and a listener that matches the original
+# destination port in the test, to check the bug fixed by aab8c844b91.
+#
+http stream tcp nowait nobody /bin/echo echo BAD 80
+xfer stream tcp nowait nobody /bin/echo echo GOOD 82
diff --git a/tests/sys/netpfil/pf/CVE-2019-5597.py b/tests/sys/netpfil/pf/CVE-2019-5597.py
new file mode 100644
index 000000000000..646791819e53
--- /dev/null
+++ b/tests/sys/netpfil/pf/CVE-2019-5597.py
@@ -0,0 +1,63 @@
+#!/usr/bin/env python3
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Synacktiv
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import random
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import sys
+
+UDP_PROTO = 17
+AH_PROTO = 51
+FRAG_PROTO = 44
+
+def main():
+ intf = sys.argv[1]
+ ipv6_src = sys.argv[2]
+ ipv6_dst = sys.argv[3]
+
+ ipv6_main = sp.IPv6(dst=ipv6_dst, src=ipv6_src)
+
+ padding = 8
+ fid = random.randint(0,100000)
+ frag_0 = sp.IPv6ExtHdrFragment(id=fid, nh=UDP_PROTO, m=1, offset=0)
+ foff_1 = (int)(padding/8)
+ frag_1 = sp.IPv6ExtHdrFragment(id=fid, nh=UDP_PROTO, m=0, offset=foff_1)
+
+ pkt1_opts = sp.AH(nh=AH_PROTO, payloadlen=200) \
+ / sp.Raw('XXXX' * 199) \
+ / sp.AH(nh=FRAG_PROTO, payloadlen=1) \
+ / frag_1
+
+ pkt0 = sp.Ether() / ipv6_main / frag_0 / sp.Raw('A' * padding)
+ pkt1 = sp.Ether() / ipv6_main / pkt1_opts / sp.Raw('B' * padding)
+
+ sp.sendp(pkt0, iface=intf, verbose=False)
+ sp.sendp(pkt1, iface=intf, verbose=False)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netpfil/pf/CVE-2019-5598.py b/tests/sys/netpfil/pf/CVE-2019-5598.py
new file mode 100644
index 000000000000..74a1270f369f
--- /dev/null
+++ b/tests/sys/netpfil/pf/CVE-2019-5598.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python3
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Kristof Provost <kp@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import sys
+from sniffer import Sniffer
+
+def check_icmp_error(args, packet):
+ ip = packet.getlayer(sp.IP)
+ if not ip:
+ return False
+ if ip.dst != args.to[0]:
+ return False
+
+ icmp = packet.getlayer(sp.ICMP)
+ if not icmp:
+ return False
+ if icmp.type != 3 or icmp.code != 3:
+ return False
+
+ return True
+
+def main():
+ parser = argparse.ArgumentParser("CVE-2019-icmp.py",
+ description="CVE-2019-icmp test tool")
+ parser.add_argument('--sendif', nargs=1,
+ required=True,
+ help='The interface through which the packet will be sent')
+ parser.add_argument('--recvif', nargs=1,
+ required=True,
+ help='The interface on which to check for the packet')
+ parser.add_argument('--src', nargs=1,
+ required=True,
+ help='The source IP address')
+ parser.add_argument('--to', nargs=1,
+ required=True,
+ help='The destination IP address')
+
+ args = parser.parse_args()
+
+ # Send the allowed packet to establish state
+ udp = sp.Ether() / \
+ sp.IP(src=args.src[0], dst=args.to[0]) / \
+ sp.UDP(dport=53, sport=1234)
+ sp.sendp(udp, iface=args.sendif[0], verbose=False)
+
+ # Start sniffing on recvif
+ sniffer = Sniffer(args, check_icmp_error, args.recvif[0])
+
+ # Send the bad error packet
+ icmp_reachable = sp.Ether() / \
+ sp.IP(src=args.src[0], dst=args.to[0]) / \
+ sp.ICMP(type=3, code=3) / \
+ sp.IP(src="4.3.2.1", dst="1.2.3.4") / \
+ sp.UDP(dport=53, sport=1234)
+ sp.sendp(icmp_reachable, iface=args.sendif[0], verbose=False)
+
+ sniffer.join()
+ if sniffer.correctPackets:
+ sys.exit(1)
+
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netpfil/pf/Makefile b/tests/sys/netpfil/pf/Makefile
new file mode 100644
index 000000000000..404d5adfb07a
--- /dev/null
+++ b/tests/sys/netpfil/pf/Makefile
@@ -0,0 +1,105 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/netpfil/pf
+TESTS_SUBDIRS+= ioctl
+
+ATF_TESTS_SH+= altq \
+ anchor \
+ debug \
+ divert-to \
+ dup \
+ ether \
+ forward \
+ fragmentation_compat \
+ fragmentation_pass \
+ fragmentation_no_reassembly \
+ get_state \
+ icmp \
+ icmp6 \
+ if_enc \
+ limits \
+ loginterface \
+ killstate \
+ macro \
+ match \
+ max_pkt_rate \
+ max_pkt_size \
+ max_states \
+ mbuf \
+ modulate \
+ names \
+ nat \
+ nat64 \
+ pass_block \
+ pflog \
+ pflow \
+ pfsync \
+ prio \
+ proxy \
+ rdr \
+ ridentifier \
+ route_to \
+ rtable \
+ rules_counter \
+ scrub_compat \
+ scrub_pass \
+ sctp \
+ set_skip \
+ set_tos \
+ snmp \
+ src_track \
+ status \
+ syncookie \
+ synproxy \
+ table \
+ tcp \
+ tos
+
+ATF_TESTS_PYTEST+= frag6.py
+ATF_TESTS_PYTEST+= header.py
+ATF_TESTS_PYTEST+= icmp.py
+ATF_TESTS_PYTEST+= igmp.py
+ATF_TESTS_PYTEST+= mld.py
+ATF_TESTS_PYTEST+= nat64.py
+ATF_TESTS_PYTEST+= nat66.py
+ATF_TESTS_PYTEST+= return.py
+ATF_TESTS_PYTEST+= sctp.py
+ATF_TESTS_PYTEST+= tcp.py
+
+# Allow tests to run in parallel in their own jails
+TEST_METADATA+= execenv="jail"
+TEST_METADATA+= execenv_jail_params="vnet allow.raw_sockets"
+
+${PACKAGE}FILES+= \
+ bsnmpd.conf \
+ CVE-2019-5597.py \
+ CVE-2019-5598.py \
+ daytime_inetd.conf \
+ echo_inetd.conf \
+ fragcommon.py \
+ frag-overindex.py \
+ frag-overlimit.py \
+ frag-overreplace.py \
+ frag-overhole.py \
+ frag-adjhole.py \
+ pfsync_defer.py \
+ pft_ether.py \
+ pft_read_ipfix.py \
+ rdr-srcport.py \
+ utils.subr \
+ utils.py
+
+${PACKAGE}FILESMODE_bsnmpd.conf= 0555
+${PACKAGE}FILESMODE_CVE-2019-5597.py= 0555
+${PACKAGE}FILESMODE_CVE-2019-5598.py= 0555
+${PACKAGE}FILESMODE_fragcommon.py= 0555
+${PACKAGE}FILESMODE_frag-overindex.py= 0555
+${PACKAGE}FILESMODE_frag-overlimit.py= 0555
+${PACKAGE}FILESMODE_frag-overreplace.py= 0555
+${PACKAGE}FILESMODE_frag-overhole.py= 0555
+${PACKAGE}FILESMODE_frag-adjhole.py= 0555
+${PACKAGE}FILESMODE_pfsync_defer.py= 0555
+${PACKAGE}FILESMODE_pft_ether.py= 0555
+${PACKAGE}FILESMODE_pft_read_ipfix.py= 0555
+
+.include <bsd.test.mk>
diff --git a/tests/sys/netpfil/pf/Makefile.depend b/tests/sys/netpfil/pf/Makefile.depend
new file mode 100644
index 000000000000..993ab0638f4a
--- /dev/null
+++ b/tests/sys/netpfil/pf/Makefile.depend
@@ -0,0 +1,11 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/netpfil/pf/altq.sh b/tests/sys/netpfil/pf/altq.sh
new file mode 100644
index 000000000000..416a55777849
--- /dev/null
+++ b/tests/sys/netpfil/pf/altq.sh
@@ -0,0 +1,346 @@
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "hfsc" "cleanup"
+hfsc_head()
+{
+ atf_set descr 'Basic HFSC test'
+ atf_set require.user root
+}
+
+hfsc_body()
+{
+ altq_init
+ is_altq_supported hfsc
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail altq_hfsc ${epair}b
+
+ ifconfig ${epair}a 192.0.2.1/24 up
+ jexec altq_hfsc ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -i .1 -c 3 -s 1200 192.0.2.2
+
+ jexec altq_hfsc pfctl -e
+ pft_set_rules altq_hfsc \
+ "altq on ${epair}b bandwidth 100b hfsc queue { default }" \
+ "queue default hfsc(default linkshare 80b)" \
+ "pass proto icmp "
+
+ # single ping succeeds just fine
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ # "Saturate the link"
+ ping -i .1 -c 5 -s 1200 192.0.2.2
+
+ # We should now be hitting the limits and get this packet dropped.
+ atf_check -s exit:2 -o ignore ping -c 1 -s 1200 192.0.2.2
+}
+
+hfsc_cleanup()
+{
+ altq_cleanup
+}
+
+atf_test_case "match" "cleanup"
+match_head()
+{
+ atf_set descr 'Basic match keyword test'
+ atf_set require.user root
+}
+
+match_body()
+{
+ altq_init
+ is_altq_supported hfsc
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail altq_match ${epair}b
+
+ ifconfig ${epair}a 192.0.2.1/24 up
+ jexec altq_match ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -i .1 -c 3 -s 1200 192.0.2.2
+
+ jexec altq_match pfctl -e
+ pft_set_rules altq_match \
+ "altq on ${epair}b bandwidth 100000000b hfsc queue { default, slow }" \
+ "queue default hfsc(default linkshare 80000000b)" \
+ "queue slow hfsc(linkshare 80b upperlimit 80b)" \
+ "match proto icmp queue slow" \
+ "pass"
+
+ # single ping succeeds just fine
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ # "Saturate the link"
+ ping -i .1 -c 5 -s 1200 192.0.2.2
+
+ # We should now be hitting the limits and get this packet dropped.
+ atf_check -s exit:2 -o ignore ping -c 1 -s 1200 192.0.2.2
+}
+
+match_cleanup()
+{
+ altq_cleanup
+}
+
+atf_test_case "cbq_vlan" "cleanup"
+cbq_vlan_head()
+{
+ atf_set descr 'CBQ over VLAN test'
+ atf_set require.user root
+}
+
+cbq_vlan_body()
+{
+ altq_init
+ is_altq_supported cbq
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail altq_cbq_vlan ${epair}b
+
+ vlan=$(vnet_mkvlan)
+ ifconfig ${vlan} vlan 42 vlandev ${epair}a
+ ifconfig ${vlan} 192.0.2.1/24 up
+ ifconfig ${epair}a up
+
+ vlanj=$(jexec altq_cbq_vlan ifconfig vlan create)
+ echo ${vlanj} >> created_interfaces.lst
+
+ jexec altq_cbq_vlan ifconfig ${epair}b up
+ jexec altq_cbq_vlan ifconfig ${vlanj} vlan 42 vlandev ${epair}b
+ jexec altq_cbq_vlan ifconfig ${vlanj} 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -i .1 -c 3 -s 1200 192.0.2.2
+
+ jexec altq_cbq_vlan pfctl -e
+ pft_set_rules altq_cbq_vlan \
+ "altq on ${vlanj} bandwidth 14000b cbq queue { default }" \
+ "queue default bandwidth 14000b cbq(default) { slow } " \
+ "queue slow bandwidth 6000b cbq(borrow)" \
+ "match proto icmp queue slow" \
+ "match proto tcp queue default" \
+ "pass"
+
+ # single ping succeeds just fine
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ # "Saturate the link"
+ ping -i .01 -c 50 -s 1200 192.0.2.2
+
+ # We should now be hitting the limits and get these packet dropped.
+ rcv=$(ping -i .1 -c 5 -s 1200 192.0.2.2 | tr "," "\n" | awk '/packets received/ { print $1; }')
+ echo "Received $rcv packets"
+ if [ ${rcv} -gt 1 ]
+ then
+ atf_fail "Received ${rcv} packets in a saturated link"
+ fi
+}
+
+cbq_vlan_cleanup()
+{
+ altq_cleanup
+}
+
+atf_test_case "codel_bridge" "cleanup"
+codel_bridge_head()
+{
+ atf_set descr 'codel over if_bridge test'
+ atf_set require.user root
+}
+
+codel_bridge_body()
+{
+ altq_init
+ is_altq_supported codel
+ vnet_init_bridge
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a 192.0.2.1/24 up
+
+ vnet_mkjail altq_codel_bridge ${epair}b
+
+ bridge=$(jexec altq_codel_bridge ifconfig bridge create)
+ jexec altq_codel_bridge ifconfig ${bridge} addm ${epair}b
+ jexec altq_codel_bridge ifconfig ${epair}b up
+ jexec altq_codel_bridge ifconfig ${bridge} 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -i .1 -c 3 -s 1200 192.0.2.2
+
+ jexec altq_codel_bridge pfctl -e
+ pft_set_rules altq_codel_bridge \
+ "altq on ${bridge} bandwidth 1000b codelq queue { slow }" \
+ "match queue slow" \
+ "pass"
+
+ # "Saturate the link"
+ ping -i .1 -c 5 -s 1200 192.0.2.2
+
+ # We should now be hitting the limits and get these packet dropped.
+ rcv=$(ping -i .1 -c 5 -s 1200 192.0.2.2 | tr "," "\n" | awk '/packets received/ { print $1; }')
+ echo "Received $rcv packets"
+ if [ ${rcv} -gt 1 ]
+ then
+ atf_fail "Received ${rcv} packets in a saturated link"
+ fi
+}
+
+codel_bridge_cleanup()
+{
+ altq_cleanup
+}
+
+atf_test_case "prioritise" "cleanup"
+prioritise_head()
+{
+ atf_set descr "Test prioritising one type of traffic over the other"
+ atf_set require.user root
+}
+
+prioritise_body()
+{
+ altq_init
+ is_altq_supported cbq
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail altq_prioritise ${epair}b
+
+ ifconfig ${epair}a 192.0.2.1/24 up
+ jexec altq_prioritise ifconfig ${epair}b 192.0.2.2/24 up
+
+ jexec altq_prioritise /usr/sbin/inetd -p ${PWD}/inetd-altq.pid \
+ $(atf_get_srcdir)/../pf/echo_inetd.conf
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -i .1 -c 3 -s 1200 192.0.2.2
+
+ jexec altq_prioritise pfctl -e
+ pft_set_rules altq_prioritise \
+ "altq on ${epair}b bandwidth 6000b cbq queue { default, slow }" \
+ "queue default priority 7 cbq(default)" \
+ "queue slow priority 1 cbq" \
+ "match proto icmp queue slow" \
+ "match proto tcp queue default" \
+ "pass"
+
+ # single ping succeeds just fine
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ # Unsaturated TCP succeeds
+ reply=$(echo "foo" | nc -w 5 -N 192.0.2.2 7)
+ if [ "$reply" != "foo" ];
+ then
+ atf_fail "Unsaturated echo failed"
+ fi
+
+ # "Saturate the link"
+ ping -i .01 -c 50 -s 1200 192.0.2.2
+
+ # We should now be hitting the limits and get these packet dropped.
+ rcv=$(ping -i .1 -c 5 -s 1200 192.0.2.2 | tr "," "\n" | awk '/packets received/ { print $1; }')
+ echo "Received $rcv packets"
+ if [ ${rcv} -gt 1 ]
+ then
+ atf_fail "Received ${rcv} packets in a saturated link"
+ fi
+
+ # TCP should still pass
+ for i in `seq 1 10`
+ do
+ reply=$(echo "foo_${i}" | nc -w 5 -N 192.0.2.2 7)
+ if [ "$reply" != "foo_${i}" ];
+ then
+ atf_fail "Prioritised echo failed ${i}"
+ fi
+
+ done
+
+ # Now reverse priority
+ pft_set_rules altq_prioritise \
+ "altq on ${epair}b bandwidth 6000b cbq queue { default, slow }" \
+ "queue default priority 7 cbq(default)" \
+ "queue slow priority 1 cbq" \
+ "match proto tcp queue slow" \
+ "match proto icmp queue default" \
+ "pass"
+
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+ ping -i .01 -c 50 -s 1200 192.0.2.2
+ for i in `seq 1 10`
+ do
+ reply=$(echo "foo_${i}" | nc -w 5 -N 192.0.2.2 7)
+ if [ "$reply" == "foo_${i}" ];
+ then
+ atf_fail "Unexpected echo success"
+ fi
+
+ done
+}
+
+prioritise_cleanup()
+{
+ altq_cleanup
+}
+
+atf_test_case "codel_vlan" "cleanup"
+codel_vlan_head()
+{
+ atf_set descr 'Test double-pass through ALTQ with codel'
+ atf_set require.user root
+}
+
+codel_vlan_body()
+{
+ altq_init
+ is_altq_supported priq
+ is_altq_supported codel
+
+ j=altq_vlan_codel
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail ${j}a ${epair}a
+ va=$(jexec ${j}a ifconfig vlan create)
+ jexec ${j}a ifconfig ${epair}a up
+ jexec ${j}a ifconfig ${va} vlan 42 vlandev ${epair}a up
+ jexec ${j}a ifconfig ${va} inet 192.0.2.1/24
+
+ vnet_mkjail ${j}b ${epair}b
+ vb=$(jexec ${j}b ifconfig vlan create)
+ jexec ${j}b ifconfig ${epair}b up
+ jexec ${j}b ifconfig ${vb} vlan 42 vlandev ${epair}b up
+ jexec ${j}b ifconfig ${vb} inet 192.0.2.2/24
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ jexec ${j}a ping -c 1 192.0.2.2
+
+ jexec ${j}a pfctl -e
+ pft_set_rules ${j}a \
+ "altq on ${epair}a priq bandwidth 10Mb queue { slow }" \
+ "queue slow priority 6 qlimit 50 priq ( default codel )" \
+ "altq on ${va} priq bandwidth 10Mb queue { vslow }" \
+ "queue vslow priority 6 qlimit 50 priq ( default codel )" \
+ "pass queue (slow)"
+
+ atf_check -s exit:0 -o ignore \
+ jexec ${j}a ping -c 1 192.0.2.2
+}
+
+codel_vlan_cleanup()
+{
+ altq_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "hfsc"
+ atf_add_test_case "match"
+ atf_add_test_case "cbq_vlan"
+ atf_add_test_case "codel_bridge"
+ atf_add_test_case "prioritise"
+ atf_add_test_case "codel_vlan"
+}
diff --git a/tests/sys/netpfil/pf/anchor.sh b/tests/sys/netpfil/pf/anchor.sh
new file mode 100644
index 000000000000..64ca84b34c3d
--- /dev/null
+++ b/tests/sys/netpfil/pf/anchor.sh
@@ -0,0 +1,510 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2018 Kristof Provost <kp@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "pr183198" "cleanup"
+pr183198_head()
+{
+ atf_set descr 'Test tables referenced by rules in anchors'
+ atf_set require.user root
+}
+
+pr183198_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz pfctl -e
+
+ # Forward with pf enabled
+ pft_set_rules alcatraz \
+ "table <test> { 10.0.0.1, 10.0.0.2, 10.0.0.3 }" \
+ "block in" \
+ "anchor \"epair\" on ${epair}b { \n\
+ pass in from <test> \n\
+ }"
+
+ atf_check -s exit:0 -o ignore jexec alcatraz pfctl -sr -a '*'
+ atf_check -s exit:0 -o ignore jexec alcatraz pfctl -t test -T show
+}
+
+pr183198_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "pr279225" "cleanup"
+pr279225_head()
+{
+ atf_set descr "Test that we can retrieve longer anchor names, PR 279225"
+ atf_set require.user root
+}
+
+pr279225_body()
+{
+ pft_init
+
+ vnet_mkjail alcatraz
+
+ pft_set_rules alcatraz \
+ "nat-anchor \"appjail-nat/jail/*\" all" \
+ "rdr-anchor \"appjail-rdr/*\" all" \
+ "anchor \"appjail/jail/*\" all"
+
+ atf_check -s exit:0 -o match:"nat-anchor \"appjail-nat/jail/\*\" all \{" \
+ jexec alcatraz pfctl -sn -a "*"
+ atf_check -s exit:0 -o match:"rdr-anchor \"appjail-rdr/\*\" all \{" \
+ jexec alcatraz pfctl -sn -a "*"
+ atf_check -s exit:0 -o match:"anchor \"appjail/jail/\*\" all \{" \
+ jexec alcatraz pfctl -sr -a "*"
+}
+
+pr279225_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nested_anchor" "cleanup"
+nested_anchor_head()
+{
+ atf_set descr 'Test setting and retrieving nested anchors'
+ atf_set require.user root
+}
+
+nested_anchor_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}a
+
+ pft_set_rules alcatraz \
+ "anchor \"foo\" { \n\
+ anchor \"bar\" { \n\
+ pass on ${epair}a \n\
+ } \n\
+ }"
+
+ atf_check -s exit:0 -o inline:"anchor \"foo\" all {
+ anchor \"bar\" all {
+ pass on ${epair}a all flags S/SA keep state
+ }
+}
+" jexec alcatraz pfctl -sr -a "*"
+}
+
+nested_anchor_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "wildcard" "cleanup"
+wildcard_head()
+{
+ atf_set descr 'Test wildcard anchors for functionality'
+ atf_set require.user root
+}
+
+wildcard_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}a
+
+ ifconfig ${epair}b 192.0.2.2/24 up
+ jexec alcatraz ifconfig ${epair}a 192.0.2.1/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "block" \
+ "anchor \"foo/*\""
+
+ atf_check -s exit:2 -o ignore ping -c 1 192.0.2.1
+
+ echo "pass" | jexec alcatraz pfctl -g -f - -a "foo/bar"
+
+ jexec alcatraz pfctl -sr -a "*"
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+}
+
+wildcard_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nested_label" "cleanup"
+nested_label_head()
+{
+ atf_set descr "Test recursive listing of labels"
+ atf_set require.user root
+}
+
+nested_label_body()
+{
+ pft_init
+
+ vnet_mkjail alcatraz
+
+ pft_set_rules alcatraz \
+ "anchor \"foo\" { \n\
+ pass in quick proto icmp label \"passicmp\"\n\
+ anchor \"bar\" { \n\
+ pass in proto tcp label \"passtcp\"\n\
+ } \n\
+ }" \
+ "pass quick from any to any label \"anytoany\""
+
+ atf_check -s exit:0 \
+ -o inline:"passicmp 0 0 0 0 0 0 0 0
+passtcp 0 0 0 0 0 0 0 0
+anytoany 0 0 0 0 0 0 0 0
+" jexec alcatraz pfctl -sl -a*
+}
+
+nested_label_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "quick" "cleanup"
+quick_head()
+{
+ atf_set descr "Test handling of quick on anchors"
+ atf_set require.user root
+}
+
+quick_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}a
+
+ ifconfig ${epair}b 192.0.2.2/24 up
+ jexec alcatraz ifconfig ${epair}a 192.0.2.1/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "anchor quick {\n\
+ pass\n\
+ }" \
+ "block"
+
+ # We can still ping because the anchor is 'quick'
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+ jexec alcatraz pfctl -sr -v
+ jexec alcatraz pfctl -ss -v
+}
+
+quick_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "quick_nested" "cleanup"
+quick_nested_head()
+{
+ atf_set descr 'Verify that a nested anchor does not clear quick'
+ atf_set require.user root
+}
+
+quick_nested_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}a
+
+ ifconfig ${epair}b 192.0.2.2/24 up
+ jexec alcatraz ifconfig ${epair}a 192.0.2.1/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "anchor quick {\n\
+ pass\n\
+ anchor {\n\
+ block proto tcp\n\
+ }\n\
+ }" \
+ "block"
+ ping -c 1 192.0.2.1
+
+ jexec alcatraz pfctl -sr -v
+ jexec alcatraz pfctl -ss -v
+
+ # We can still ping because the anchor is 'quick'
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+ jexec alcatraz pfctl -sr -v
+ jexec alcatraz pfctl -ss -v
+}
+
+quick_nested_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "counter" "cleanup"
+counter_head()
+{
+ atf_set descr 'Test counters on anchors'
+ atf_set require.user root
+}
+
+counter_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}a
+
+ ifconfig ${epair}b 192.0.2.2/24 up
+ jexec alcatraz ifconfig ${epair}a 192.0.2.1/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "anchor \"foo\" {\n\
+ pass\n\
+ }"
+
+ # Generate traffic
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+ atf_check -s exit:0 -e ignore \
+ -o match:'[ Evaluations: 1 Packets: 2 Bytes: 168 States: 1 ]' \
+ jexec alcatraz pfctl -sr -vv
+}
+
+counter_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nat" "cleanup"
+nat_head()
+{
+ atf_set descr 'Test nested nat anchors'
+ atf_set require.user root
+}
+
+nat_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}a
+
+ ifconfig ${epair}b 192.0.2.2/24 up
+ jexec alcatraz ifconfig ${epair}a 192.0.2.1/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "nat-anchor \"foo/*\"" \
+ "pass"
+
+ echo "nat log on ${epair}a inet from 192.0.2.0/24 to any port = 53 -> 192.0.2.1" \
+ | jexec alcatraz pfctl -a "foo/bar" -g -f -
+ echo "rdr on ${epair}a proto tcp to port echo -> 127.0.0.1 port echo" \
+ | jexec alcatraz pfctl -a "foo/baz" -g -f -
+
+ jexec alcatraz pfctl -sn -a "*"
+ jexec alcatraz pfctl -sn -a "foo/bar"
+ jexec alcatraz pfctl -sn -a "foo/baz"
+
+ atf_check -s exit:0 -o match:"nat log on ${epair}a inet from 192.0.2.0/24 to any port = domain -> 192.0.2.1" \
+ jexec alcatraz pfctl -sn -a "*"
+ atf_check -s exit:0 -o match:"rdr on ${epair}a inet proto tcp from any to any port = echo -> 127.0.0.1 port 7" \
+ jexec alcatraz pfctl -sn -a "*"
+}
+
+nat_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "include" "cleanup"
+include_head()
+{
+ atf_set descr 'Test including inside anchors'
+ atf_set require.user root
+}
+
+include_body()
+{
+ pft_init
+
+ wd=`pwd`
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}a
+
+ ifconfig ${epair}b 192.0.2.2/24 up
+ jexec alcatraz ifconfig ${epair}a 192.0.2.1/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+
+ echo "pass" > ${wd}/extra.conf
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "block" \
+ "anchor \"foo\" {\n\
+ include \"${wd}/extra.conf\"\n\
+ }"
+
+ jexec alcatraz pfctl -sr
+
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+}
+
+include_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "quick" "cleanup"
+quick_head()
+{
+ atf_set descr 'Test quick on anchors'
+ atf_set require.user root
+}
+
+quick_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}a
+
+ ifconfig ${epair}b 192.0.2.2/24 up
+ jexec alcatraz ifconfig ${epair}a 192.0.2.1/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "anchor quick {\n\
+ pass\n\
+ }" \
+ "block"
+
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+ jexec alcatraz pfctl -sr -vv -a "*"
+}
+
+quick_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "recursive_flush" "cleanup"
+recursive_flush_head()
+{
+ atf_set descr 'Test recursive flushing of rules'
+ atf_set require.user root
+}
+
+recursive_flush_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}a
+
+ ifconfig ${epair}b 192.0.2.2/24 up
+ jexec alcatraz ifconfig ${epair}a 192.0.2.1/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "block" \
+ "anchor \"foo\" {\n\
+ pass\n\
+ }"
+
+ # We can ping thanks to the pass rule in foo
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+
+ # Only reset the main rules. I.e. not a recursive flush
+ pft_set_rules alcatraz \
+ "block" \
+ "anchor \"foo\""
+
+ # "foo" still has the pass rule, so this works
+ jexec alcatraz pfctl -a "*" -sr
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+
+ # Now do a recursive flush
+ atf_check -s exit:0 -e ignore -o ignore \
+ jexec alcatraz pfctl -a "*" -Fr
+ pft_set_rules alcatraz \
+ "block" \
+ "anchor \"foo\""
+
+ # So this fails
+ jexec alcatraz pfctl -a "*" -sr
+ atf_check -s exit:2 -o ignore ping -c 1 192.0.2.1
+}
+
+recursive_flush_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "pr183198"
+ atf_add_test_case "pr279225"
+ atf_add_test_case "nested_anchor"
+ atf_add_test_case "wildcard"
+ atf_add_test_case "nested_label"
+ atf_add_test_case "quick"
+ atf_add_test_case "quick_nested"
+ atf_add_test_case "counter"
+ atf_add_test_case "nat"
+ atf_add_test_case "include"
+ atf_add_test_case "quick"
+ atf_add_test_case "recursive_flush"
+}
diff --git a/tests/sys/netpfil/pf/bsnmpd.conf b/tests/sys/netpfil/pf/bsnmpd.conf
new file mode 100644
index 000000000000..27abdda6cbd3
--- /dev/null
+++ b/tests/sys/netpfil/pf/bsnmpd.conf
@@ -0,0 +1,47 @@
+location := "A galaxy far, far away"
+contact := "skywalker@Tatooine"
+system := 1
+
+read := "public"
+write := "geheim"
+trap := "mytrap"
+
+NoAuthProtocol := 1.3.6.1.6.3.10.1.1.1
+HMACMD5AuthProtocol := 1.3.6.1.6.3.10.1.1.2
+HMACSHAAuthProtocol := 1.3.6.1.6.3.10.1.1.3
+NoPrivProtocol := 1.3.6.1.6.3.10.1.2.1
+DESPrivProtocol := 1.3.6.1.6.3.10.1.2.2
+AesCfb128Protocol := 1.3.6.1.6.3.10.1.2.4
+
+securityModelAny := 0
+securityModelSNMPv1 := 1
+securityModelSNMPv2c := 2
+securityModelUSM := 3
+
+MPmodelSNMPv1 := 0
+MPmodelSNMPv2c := 1
+MPmodelSNMPv3 := 3
+
+noAuthNoPriv := 1
+authNoPriv := 2
+authPriv := 3
+
+%snmpd
+begemotSnmpdDebugDumpPdus = 2
+begemotSnmpdDebugSyslogPri = 7
+
+begemotSnmpdCommunityString.0.1 = $(read)
+begemotSnmpdCommunityDisable = 1
+
+begemotSnmpdTransInetStatus.1.4.0.0.0.0.161.1 = 4
+begemotSnmpdTransInetStatus.2.16.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.161.1 = 4
+
+begemotSnmpdLocalPortStatus."/var/run/snmpd.sock" = 1
+begemotSnmpdLocalPortType."/var/run/snmpd.sock" = 4
+
+sysContact = $(contact)
+sysLocation = $(location)
+sysObjectId = 1.3.6.1.4.1.12325.1.1.2.1.$(system)
+
+begemotSnmpdModulePath."mibII" = "/usr/lib/snmp_mibII.so"
+begemotSnmpdModulePath."pf" = "/usr/lib/snmp_pf.so"
diff --git a/tests/sys/netpfil/pf/daytime_inetd.conf b/tests/sys/netpfil/pf/daytime_inetd.conf
new file mode 100644
index 000000000000..0cfb250bb340
--- /dev/null
+++ b/tests/sys/netpfil/pf/daytime_inetd.conf
@@ -0,0 +1,30 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2021 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+daytime stream tcp nowait root internal
+daytime stream tcp6 nowait root internal
+daytime dgram udp wait root internal
+daytime dgram udp6 wait root internal
diff --git a/tests/sys/netpfil/pf/debug.sh b/tests/sys/netpfil/pf/debug.sh
new file mode 100644
index 000000000000..404d37ab8932
--- /dev/null
+++ b/tests/sys/netpfil/pf/debug.sh
@@ -0,0 +1,106 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "basic" "cleanup"
+basic_head()
+{
+ atf_set descr 'Test setting and retrieving debug level'
+ atf_set require.user root
+}
+
+basic_body()
+{
+ pft_init
+
+ vnet_mkjail debug
+ atf_check -s exit:0 -e ignore \
+ jexec debug pfctl -x loud
+
+ atf_check -s exit:0 -o match:'Debug: Loud' \
+ jexec debug pfctl -si
+}
+
+basic_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "reset" "cleanup"
+reset_head()
+{
+ atf_set descr 'Test resetting debug level'
+ atf_set require.user root
+}
+
+reset_body()
+{
+ pft_init
+
+ vnet_mkjail debug
+
+ # Default is Urgent
+ atf_check -s exit:0 -o match:'Debug: Urgent' \
+ jexec debug pfctl -sa
+ state_limit=$(jexec debug pfctl -sa | grep 'states.*hard limit' | awk '{ print $4; }')
+
+ # Change defaults
+ pft_set_rules debug \
+ "set limit states 42"
+ atf_check -s exit:0 -e ignore \
+ jexec debug pfctl -x loud
+
+ atf_check -s exit:0 -o match:'Debug: Loud' \
+ jexec debug pfctl -sa
+ new_state_limit=$(jexec debug pfctl -sa | grep 'states.*hard limit' | awk '{ print $4; }')
+ if [ $state_limit -eq $new_state_limit ]; then
+ jexec debug pfctl -sa
+ atf_fail "Failed to change state limit"
+ fi
+
+ # Reset
+ atf_check -s exit:0 -o ignore -e ignore \
+ jexec debug pfctl -FR
+ atf_check -s exit:0 -o match:'Debug: Urgent' \
+ jexec debug pfctl -sa
+ new_state_limit=$(jexec debug pfctl -sa | grep 'states.*hard limit' | awk '{ print $4; }')
+ if [ $state_limit -ne $new_state_limit ]; then
+ jexec debug pfctl -sa
+ atf_fail "Failed to reset state limit"
+ fi
+}
+
+reset_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "basic"
+ atf_add_test_case "reset"
+}
diff --git a/tests/sys/netpfil/pf/divert-to.sh b/tests/sys/netpfil/pf/divert-to.sh
new file mode 100644
index 000000000000..ae44cd5d51af
--- /dev/null
+++ b/tests/sys/netpfil/pf/divert-to.sh
@@ -0,0 +1,386 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2023 Igor Ostapenko <pm@igoro.pro>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+#
+# pf divert-to action test cases
+#
+# -----------| |-- |----| ----| |-----------
+# ( ) inbound |pf_check_in| ) -> |host| -> ( ) |pf_check_out| outbound )
+# -----------| | |-- |----| ----| | |-----------
+# | |
+# \|/ \|/
+# |------| |------|
+# |divapp| |divapp|
+# |------| |------|
+#
+# The basic cases:
+# - inbound > diverted | divapp terminated
+# - inbound > diverted > inbound | host terminated
+# - inbound > diverted > outbound | network terminated
+# - outbound > diverted | divapp terminated
+# - outbound > diverted > outbound | network terminated
+# - outbound > diverted > inbound | e.g. host terminated
+#
+# When a packet is diverted, forwarded, and possibly diverted again:
+# - inbound > diverted > inbound > forwarded
+# > outbound | network terminated
+# - inbound > diverted > inbound > forwarded
+# > outbound > diverted > outbound | network terminated
+#
+# Test case naming legend:
+# in - inbound
+# div - diverted
+# out - outbound
+# fwd - forwarded
+# dn - delayed by dummynet
+#
+
+. $(atf_get_srcdir)/utils.subr
+
+divert_init()
+{
+ if ! kldstat -q -m ipdivert; then
+ atf_skip "This test requires ipdivert"
+ fi
+}
+
+dummynet_init()
+{
+ if ! kldstat -q -m dummynet; then
+ atf_skip "This test requires dummynet"
+ fi
+}
+
+atf_test_case "in_div" "cleanup"
+in_div_head()
+{
+ atf_set descr 'Test inbound > diverted | divapp terminated'
+ atf_set require.user root
+}
+in_div_body()
+{
+ pft_init
+ divert_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail div ${epair}b
+ ifconfig ${epair}a 192.0.2.1/24 up
+ jexec div ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c3 192.0.2.2
+
+ jexec div pfctl -e
+ pft_set_rules div \
+ "pass all" \
+ "pass in inet proto icmp icmp-type echoreq divert-to 127.0.0.1 port 2000"
+
+ jexec div $(atf_get_srcdir)/../common/divapp 2000 &
+ divapp_pid=$!
+ # Wait for the divapp to be ready
+ sleep 1
+
+ # divapp is expected to "eat" the packet
+ atf_check -s not-exit:0 -o ignore ping -c1 -t1 192.0.2.2
+
+ wait $divapp_pid
+}
+in_div_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "in_div_in" "cleanup"
+in_div_in_head()
+{
+ atf_set descr 'Test inbound > diverted > inbound | host terminated'
+ atf_set require.user root
+}
+in_div_in_body()
+{
+ pft_init
+ divert_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail div ${epair}b
+ ifconfig ${epair}a 192.0.2.1/24 up
+ jexec div ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c3 192.0.2.2
+
+ jexec div pfctl -e
+ pft_set_rules div \
+ "pass all" \
+ "pass in inet proto icmp icmp-type echoreq divert-to 127.0.0.1 port 2000 no state"
+
+ jexec div $(atf_get_srcdir)/../common/divapp 2000 divert-back &
+ divapp_pid=$!
+ # Wait for the divapp to be ready
+ sleep 1
+
+ # divapp is NOT expected to "eat" the packet
+ atf_check -s exit:0 -o ignore ping -c1 192.0.2.2
+
+ wait $divapp_pid
+}
+in_div_in_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "out_div" "cleanup"
+out_div_head()
+{
+ atf_set descr 'Test outbound > diverted | divapp terminated'
+ atf_set require.user root
+}
+out_div_body()
+{
+ pft_init
+ divert_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail div ${epair}b
+ ifconfig ${epair}a 192.0.2.1/24 up
+ jexec div ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c3 192.0.2.2
+
+ jexec div pfctl -e
+ pft_set_rules div \
+ "pass all" \
+ "pass in inet proto icmp icmp-type echoreq no state" \
+ "pass out inet proto icmp icmp-type echorep divert-to 127.0.0.1 port 2000 no state"
+
+ jexec div $(atf_get_srcdir)/../common/divapp 2000 &
+ divapp_pid=$!
+ # Wait for the divapp to be ready
+ sleep 1
+
+ # divapp is expected to "eat" the packet
+ atf_check -s not-exit:0 -o ignore ping -c1 -t1 192.0.2.2
+
+ wait $divapp_pid
+}
+out_div_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "out_div_out" "cleanup"
+out_div_out_head()
+{
+ atf_set descr 'Test outbound > diverted > outbound | network terminated'
+ atf_set require.user root
+}
+out_div_out_body()
+{
+ pft_init
+ divert_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail div ${epair}b
+ ifconfig ${epair}a 192.0.2.1/24 up
+ jexec div ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c3 192.0.2.2
+
+ jexec div pfctl -e
+ pft_set_rules div \
+ "pass all" \
+ "pass in inet proto icmp icmp-type echoreq no state" \
+ "pass out inet proto icmp icmp-type echorep divert-to 127.0.0.1 port 2000 no state"
+
+ jexec div $(atf_get_srcdir)/../common/divapp 2000 divert-back &
+ divapp_pid=$!
+ # Wait for the divapp to be ready
+ sleep 1
+
+ # divapp is NOT expected to "eat" the packet
+ atf_check -s exit:0 -o ignore ping -c1 192.0.2.2
+
+ wait $divapp_pid
+}
+out_div_out_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "in_div_in_fwd_out_div_out" "cleanup"
+in_div_in_fwd_out_div_out_head()
+{
+ atf_set descr 'Test inbound > diverted > inbound > forwarded > outbound > diverted > outbound | network terminated'
+ atf_set require.user root
+}
+in_div_in_fwd_out_div_out_body()
+{
+ pft_init
+ divert_init
+
+ # host <a--epair0--b> router <a--epair1--b> site
+ epair0=$(vnet_mkepair)
+ epair1=$(vnet_mkepair)
+
+ vnet_mkjail router ${epair0}b ${epair1}a
+ ifconfig ${epair0}a 192.0.2.1/24 up
+ jexec router sysctl net.inet.ip.forwarding=1
+ jexec router ifconfig ${epair0}b 192.0.2.2/24 up
+ jexec router ifconfig ${epair1}a 198.51.100.1/24 up
+
+ vnet_mkjail site ${epair1}b
+ jexec site ifconfig ${epair1}b 198.51.100.2/24 up
+ jexec site route add default 198.51.100.1
+
+ route add -net 198.51.100.0/24 192.0.2.2
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c3 192.0.2.2
+
+ # Should be routed without pf
+ atf_check -s exit:0 -o ignore ping -c3 198.51.100.2
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "pass all" \
+ "pass in inet proto icmp icmp-type echoreq divert-to 127.0.0.1 port 2001 no state" \
+ "pass out inet proto icmp icmp-type echoreq divert-to 127.0.0.1 port 2002 no state"
+
+ jexec router $(atf_get_srcdir)/../common/divapp 2001 divert-back &
+ indivapp_pid=$!
+ jexec router $(atf_get_srcdir)/../common/divapp 2002 divert-back &
+ outdivapp_pid=$!
+ # Wait for the divappS to be ready
+ sleep 1
+
+ # Both divappS are NOT expected to "eat" the packet
+ atf_check -s exit:0 -o ignore ping -c1 198.51.100.2
+
+ wait $indivapp_pid && wait $outdivapp_pid
+}
+in_div_in_fwd_out_div_out_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "in_dn_in_div_in_out_div_out_dn_out" "cleanup"
+in_dn_in_div_in_out_div_out_dn_out_head()
+{
+ atf_set descr 'Test inbound > delayed+diverted > outbound > diverted+delayed > outbound | network terminated'
+ atf_set require.user root
+}
+in_dn_in_div_in_out_div_out_dn_out_body()
+{
+ pft_init
+ divert_init
+ dummynet_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}b
+ ifconfig ${epair}a 192.0.2.1/24 up
+ ifconfig ${epair}a ether 02:00:00:00:00:01
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c3 192.0.2.2
+
+ # a) ping should time out due to very narrow dummynet pipes {
+
+ jexec alcatraz dnctl pipe 1001 config bw 1Byte/s
+ jexec alcatraz dnctl pipe 1002 config bw 1Byte/s
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "ether pass in from 02:00:00:00:00:01 l3 all dnpipe 1001" \
+ "ether pass out to 02:00:00:00:00:01 l3 all dnpipe 1002 " \
+ "pass all" \
+ "pass in inet proto icmp icmp-type echoreq divert-to 127.0.0.1 port 1001 no state" \
+ "pass out inet proto icmp icmp-type echorep divert-to 127.0.0.1 port 1002 no state"
+
+ jexec alcatraz $(atf_get_srcdir)/../common/divapp 1001 divert-back &
+ indivapp_pid=$!
+ jexec alcatraz $(atf_get_srcdir)/../common/divapp 1002 divert-back &
+ outdivapp_pid=$!
+ # Wait for the divappS to be ready
+ sleep 1
+
+ atf_check -s not-exit:0 -o ignore ping -c1 -s56 -t1 192.0.2.2
+
+ wait $indivapp_pid
+ atf_check_not_equal 0 $?
+ wait $outdivapp_pid
+ atf_check_not_equal 0 $?
+
+ # }
+
+ # b) ping should NOT time out due to wide enough dummynet pipes {
+
+ jexec alcatraz dnctl pipe 2001 config bw 100KByte/s
+ jexec alcatraz dnctl pipe 2002 config bw 100KByte/s
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "ether pass in from 02:00:00:00:00:01 l3 all dnpipe 2001" \
+ "ether pass out to 02:00:00:00:00:01 l3 all dnpipe 2002 " \
+ "pass all" \
+ "pass in inet proto icmp icmp-type echoreq divert-to 127.0.0.1 port 2001 no state" \
+ "pass out inet proto icmp icmp-type echorep divert-to 127.0.0.1 port 2002 no state"
+
+ jexec alcatraz $(atf_get_srcdir)/../common/divapp 2001 divert-back &
+ indivapp_pid=$!
+ jexec alcatraz $(atf_get_srcdir)/../common/divapp 2002 divert-back &
+ outdivapp_pid=$!
+ # Wait for the divappS to be ready
+ sleep 1
+
+ atf_check -s exit:0 -o ignore ping -c1 -s56 -t1 192.0.2.2
+
+ wait $indivapp_pid
+ atf_check_equal 0 $?
+ wait $outdivapp_pid
+ atf_check_equal 0 $?
+
+ # }
+}
+in_dn_in_div_in_out_div_out_dn_out_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "in_div"
+ atf_add_test_case "in_div_in"
+
+ atf_add_test_case "out_div"
+ atf_add_test_case "out_div_out"
+
+ atf_add_test_case "in_div_in_fwd_out_div_out"
+
+ atf_add_test_case "in_dn_in_div_in_out_div_out_dn_out"
+}
diff --git a/tests/sys/netpfil/pf/dup.sh b/tests/sys/netpfil/pf/dup.sh
new file mode 100644
index 000000000000..64a08083bca0
--- /dev/null
+++ b/tests/sys/netpfil/pf/dup.sh
@@ -0,0 +1,80 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2021 Kristof Provost <kp@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+common_dir=$(atf_get_srcdir)/../common
+
+atf_test_case "dup_to" "cleanup"
+dup_to_head()
+{
+ atf_set descr 'dup-to test'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+dup_to_body()
+{
+ pft_init
+
+ epair_send=$(vnet_mkepair)
+ ifconfig ${epair_send}a 192.0.2.1/24 up
+
+ epair_recv=$(vnet_mkepair)
+ ifconfig ${epair_recv}a up
+
+ epair_dupto=$(vnet_mkepair)
+ ifconfig ${epair_dupto}a up
+
+ vnet_mkjail alcatraz ${epair_send}b ${epair_recv}b ${epair_dupto}b
+ jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up
+ jexec alcatraz ifconfig ${epair_recv}b 198.51.100.2/24 up
+ jexec alcatraz ifconfig ${epair_dupto}b 203.0.113.2/24 up
+ jexec alcatraz sysctl net.inet.ip.forwarding=1
+ jexec alcatraz arp -s 198.51.100.3 00:01:02:03:04:05
+ jexec alcatraz arp -s 203.0.113.3 01:02:03:04:05:06
+ route add -net 198.51.100.0/24 192.0.2.2
+
+ jexec alcatraz pfctl -e
+
+ pft_set_rules alcatraz "pass out on { ${epair_recv}b } \
+ dup-to (${epair_dupto}b 203.0.113.3)"
+
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_send}a \
+ --to 198.51.100.3 \
+ --recv ${epair_recv}a ${epair_dupto}a
+}
+
+dup_to_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "dup_to"
+}
diff --git a/tests/sys/netpfil/pf/echo_inetd.conf b/tests/sys/netpfil/pf/echo_inetd.conf
new file mode 100644
index 000000000000..d259dbf4ffa4
--- /dev/null
+++ b/tests/sys/netpfil/pf/echo_inetd.conf
@@ -0,0 +1,30 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Kristof Provost <kp@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+echo stream tcp nowait root internal
+echo stream tcp6 nowait root internal
+echo dgram udp wait root internal
+echo dgram udp6 wait root internal
diff --git a/tests/sys/netpfil/pf/ether.sh b/tests/sys/netpfil/pf/ether.sh
new file mode 100644
index 000000000000..f0fdce50a7d3
--- /dev/null
+++ b/tests/sys/netpfil/pf/ether.sh
@@ -0,0 +1,746 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright © 2021. Rubicon Communications, LLC (Netgate). All Rights Reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+common_dir=$(atf_get_srcdir)/../common
+
+atf_test_case "mac" "cleanup"
+mac_head()
+{
+ atf_set descr 'Test MAC address filtering'
+ atf_set require.user root
+}
+
+mac_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ epair_a_mac=$(ifconfig ${epair}a ether | awk '/ether/ { print $2; }')
+
+ ifconfig ${epair}a 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+
+ pft_set_rules alcatraz \
+ "ether block from ${epair_a_mac}"
+
+ atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.2
+
+ # Now enable. Ping should fail.
+ jexec alcatraz pfctl -e
+
+ atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.2
+
+ # Should still fail for 'to'
+ pft_set_rules alcatraz \
+ "ether block to ${epair_a_mac}"
+ atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.2
+
+ # Succeeds if we block a different MAC address
+ pft_set_rules alcatraz \
+ "ether block to 00:01:02:03:04:05"
+ atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.2
+
+ # Should still fail for 'to', even if it's in a list
+ pft_set_rules alcatraz \
+ "ether block to { ${epair_a_mac}, 00:01:02:0:04:05 }"
+ atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.2
+
+ # Now try this with an interface specified
+ pft_set_rules alcatraz \
+ "ether block on ${epair}b from ${epair_a_mac}"
+ atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.2
+
+ # Wrong interface should not match
+ pft_set_rules alcatraz \
+ "ether block on ${epair}a from ${epair_a_mac}"
+ atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.2
+
+ # Test negation
+ pft_set_rules alcatraz \
+ "ether block in on ${epair}b from ! ${epair_a_mac}"
+ atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.2
+
+ pft_set_rules alcatraz \
+ "ether block out on ${epair}b to ! ${epair_a_mac}"
+ atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.2
+
+ # Block everything not us
+ pft_set_rules alcatraz \
+ "ether block out on ${epair}b to { ! ${epair_a_mac} }"
+ atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.2
+
+ # Block us now
+ pft_set_rules alcatraz \
+ "ether block out on ${epair}b to { ! 00:01:02:03:04:05 }"
+ atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.2
+
+ # Block with a masked address
+ pft_set_rules alcatraz \
+ "ether block out on ${epair}b to { ! 00:01:02:03:00:00/32 }"
+ jexec alcatraz pfctl -se
+ atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.2
+
+ epair_prefix=$(echo $epair_a_mac | cut -c-8)
+ pft_set_rules alcatraz \
+ "ether block out on ${epair}b to { ${epair_prefix}:00:00:00/24 }"
+ atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.2
+
+ pft_set_rules alcatraz \
+ "ether block out on ${epair}b to { ${epair_prefix}:00:00:00&ff:ff:ff:00:00:00 }"
+ atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.2
+
+ # Check '-F ethernet' works
+ jexec alcatraz pfctl -F ethernet
+ atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.2
+}
+
+mac_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "proto" "cleanup"
+proto_head()
+{
+ atf_set descr 'Test EtherType filtering'
+ atf_set require.user root
+}
+
+proto_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ epair_a_mac=$(ifconfig ${epair}a ether | awk '/ether/ { print $2; }')
+
+ ifconfig ${epair}a 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+
+ pft_set_rules alcatraz \
+ "ether block proto 0x0810"
+ jexec alcatraz pfctl -e
+
+ atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.2
+
+ # Block IP
+ pft_set_rules alcatraz \
+ "ether block proto 0x0800"
+ atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.2
+
+ # Block ARP
+ pft_set_rules alcatraz \
+ "ether block proto 0x0806"
+ arp -d 192.0.2.2
+ atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.2
+}
+
+proto_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "direction" "cleanup"
+direction_head()
+{
+ atf_set descr 'Test directionality of ether rules'
+ atf_set require.user root
+ atf_set require.progs jq
+}
+
+direction_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ epair_a_mac=$(ifconfig ${epair}a ether | awk '/ether/ { print $2; }')
+ epair_b_mac=$(ifconfig ${epair}b ether | awk '/ether/ { print $2; }')
+
+ ifconfig ${epair}a 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+
+ pft_set_rules alcatraz \
+ "ether block in proto 0x0806"
+ jexec alcatraz pfctl -e
+
+ arp -d 192.0.2.2
+ jexec alcatraz arp -d 192.0.2.1
+
+ # We don't allow the jail to receive ARP requests, so if we try to ping
+ # from host to jail the host can't resolve the MAC address
+ ping -c 1 -t 1 192.0.2.2
+
+ mac=$(arp -an --libxo json \
+ | jq '."arp"."arp-cache"[] |
+ select(."ip-address"=="192.0.2.2")."mac-address"')
+ atf_check_not_equal "$mac" "$epair_b_mac"
+
+ # Clear ARP table again
+ arp -d 192.0.2.2
+ jexec alcatraz arp -d 192.0.2.1
+
+ # However, we allow outbound ARP, so the host will learn our MAC if the
+ # jail tries to ping
+ jexec alcatraz ping -c 1 -t 1 192.0.2.1
+
+ mac=$(arp -an --libxo json \
+ | jq '."arp"."arp-cache"[] |
+ select(."ip-address"=="192.0.2.2")."mac-address"')
+ atf_check_equal "$mac" "$epair_b_mac"
+
+ # Now do the same, but with outbound ARP blocking
+ pft_set_rules alcatraz \
+ "ether block out proto 0x0806"
+
+ # Clear ARP table again
+ arp -d 192.0.2.2
+ jexec alcatraz arp -d 192.0.2.1
+
+ # The jail can't send ARP requests to us, so we'll never learn our MAC
+ # address
+ jexec alcatraz ping -c 1 -t 1 192.0.2.1
+
+ mac=$(jexec alcatraz arp -an --libxo json \
+ | jq '."arp"."arp-cache"[] |
+ select(."ip-address"=="192.0.2.1")."mac-address"')
+ atf_check_not_equal "$mac" "$epair_a_mac"
+}
+
+direction_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "captive" "cleanup"
+captive_head()
+{
+ atf_set descr 'Test a basic captive portal-like setup'
+ atf_set require.user root
+}
+
+captive_body()
+{
+ # Host is client, jail 'gw' is the captive portal gateway, jail 'srv'
+ # is a random (web)server. We use the echo protocol rather than http
+ # for the test, because that's easier.
+ pft_init
+
+ epair_gw=$(vnet_mkepair)
+ epair_srv=$(vnet_mkepair)
+ epair_gw_a_mac=$(ifconfig ${epair_gw}a ether | awk '/ether/ { print $2; }')
+
+ vnet_mkjail gw ${epair_gw}b ${epair_srv}a
+ vnet_mkjail srv ${epair_srv}b
+
+ ifconfig ${epair_gw}a 192.0.2.2/24 up
+ route add -net 198.51.100.0/24 192.0.2.1
+ jexec gw ifconfig ${epair_gw}b 192.0.2.1/24 up
+ jexec gw ifconfig lo0 127.0.0.1/8 up
+ jexec gw sysctl net.inet.ip.forwarding=1
+
+ jexec gw ifconfig ${epair_srv}a 198.51.100.1/24 up
+ jexec srv ifconfig ${epair_srv}b 198.51.100.2/24 up
+ jexec srv route add -net 192.0.2.0/24 198.51.100.1
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 -t 1 198.51.100.2
+
+ pft_set_rules gw \
+ "ether pass quick proto 0x0806" \
+ "ether pass tag captive" \
+ "rdr on ${epair_gw}b proto tcp to port echo tagged captive -> 127.0.0.1 port echo"
+ jexec gw pfctl -e
+
+ # ICMP should still work, because we don't redirect it.
+ atf_check -s exit:0 -o ignore ping -c 1 -t 1 198.51.100.2
+
+ # Run the echo server only on the gw, so we know we've redirectly
+ # correctly if we get an echo message.
+ jexec gw /usr/sbin/inetd -p ${PWD}/echo_inetd.pid $(atf_get_srcdir)/echo_inetd.conf
+
+ # Confirm that we're getting redirected
+ atf_check -s exit:0 -o match:"^foo$" -x "echo foo | nc -N 198.51.100.2 7"
+
+ jexec gw killall inetd
+
+ # Now pretend we've authenticated, so add the client's MAC address
+ pft_set_rules gw \
+ "ether pass quick proto 0x0806" \
+ "ether pass quick from ${epair_gw_a_mac}" \
+ "ether pass tag captive" \
+ "rdr on ${epair_gw}b proto tcp to port echo tagged captive -> 127.0.0.1 port echo"
+
+ # No redirect, so failure.
+ atf_check -s exit:1 -x "echo foo | nc -N 198.51.100.2 7"
+
+ # Start a server in srv
+ jexec srv /usr/sbin/inetd -p ${PWD}/echo_inetd.pid $(atf_get_srcdir)/echo_inetd.conf
+
+ # And now we can talk to that one.
+ atf_check -s exit:0 -o match:"^foo$" -x "echo foo | nc -N 198.51.100.2 7"
+}
+
+captive_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "captive_long" "cleanup"
+captive_long_head()
+{
+ atf_set descr 'More complex captive portal setup'
+ atf_set require.user root
+}
+
+captive_long_body()
+{
+ # Host is client, jail 'gw' is the captive portal gateway, jail 'srv'
+ # is a random (web)server. We use the echo protocol rather than http
+ # for the test, because that's easier.
+ dummynet_init
+
+ epair_gw=$(vnet_mkepair)
+ epair_srv=$(vnet_mkepair)
+ epair_gw_a_mac=$(ifconfig ${epair_gw}a ether | awk '/ether/ { print $2; }')
+
+ vnet_mkjail gw ${epair_gw}b ${epair_srv}a
+ vnet_mkjail srv ${epair_srv}b
+
+ ifconfig ${epair_gw}a 192.0.2.2/24 up
+ route add -net 198.51.100.0/24 192.0.2.1
+ jexec gw ifconfig ${epair_gw}b 192.0.2.1/24 up
+ jexec gw ifconfig lo0 127.0.0.1/8 up
+ jexec gw sysctl net.inet.ip.forwarding=1
+
+ jexec gw ifconfig ${epair_srv}a 198.51.100.1/24 up
+ jexec srv ifconfig ${epair_srv}b 198.51.100.2/24 up
+ jexec srv route add -net 192.0.2.0/24 198.51.100.1
+
+ jexec gw dnctl pipe 1 config bw 300KByte/s
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 -t 1 198.51.100.2
+
+ pft_set_rules gw \
+ "ether anchor \"captiveportal\" on { ${epair_gw}b } {" \
+ "ether pass quick proto { 0x0806, 0x8035, 0x888e, 0x88c7, 0x8863, 0x8864 }" \
+ "ether pass tag \"captive\"" \
+ "}" \
+ "rdr on ${epair_gw}b proto tcp to port daytime tagged captive -> 127.0.0.1 port echo"
+ jexec gw pfctl -e
+
+ # ICMP should still work, because we don't redirect it.
+ atf_check -s exit:0 -o ignore ping -c 1 -t 1 198.51.100.2
+
+ jexec gw /usr/sbin/inetd -p ${PWD}/gw.pid $(atf_get_srcdir)/echo_inetd.conf
+ jexec srv /usr/sbin/inetd -p ${PWD}/srv.pid $(atf_get_srcdir)/daytime_inetd.conf
+
+ echo foo | nc -N 198.51.100.2 13
+
+ # Confirm that we're getting redirected
+ atf_check -s exit:0 -o match:"^foo$" -x "echo foo | nc -N 198.51.100.2 13"
+
+ # Now update the rules to allow our client to pass without redirect
+ pft_set_rules gw \
+ "ether anchor \"captiveportal\" on { ${epair_gw}b } {" \
+ "ether pass quick proto { 0x0806, 0x8035, 0x888e, 0x88c7, 0x8863, 0x8864 }" \
+ "ether pass quick from { ${epair_gw_a_mac} } dnpipe 1" \
+ "ether pass tag \"captive\"" \
+ "}" \
+ "rdr on ${epair_gw}b proto tcp to port daytime tagged captive -> 127.0.0.1 port echo"
+
+ # We're not being redirected and get datime information now
+ atf_check -s exit:0 -o match:"^(Mon|Tue|Wed|Thu|Fri|Sat|Sun)" -x "echo foo | nc -N 198.51.100.2 13"
+
+ jexec gw killall inetd
+ jexec srv killall inetd
+}
+
+captive_long_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "dummynet" "cleanup"
+dummynet_head()
+{
+ atf_set descr 'Test dummynet for L2 traffic'
+ atf_set require.user root
+}
+
+dummynet_body()
+{
+ pft_init
+
+ if ! kldstat -q -m dummynet; then
+ atf_skip "This test requires dummynet"
+ fi
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}b
+
+ ifconfig ${epair}a 192.0.2.1/24 up
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -i .1 -c 3 -s 1200 192.0.2.2
+
+ jexec alcatraz dnctl pipe 1 config bw 300Byte/s
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "ether pass in dnpipe 1"
+
+ # Ensure things don't break if non-IP(v4/v6) traffic hits dummynet
+ arp -d 192.0.2.2
+
+ # single ping succeeds just fine
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ # Saturate the link
+ ping -i .1 -c 5 -s 1200 192.0.2.2
+
+ # We should now be hitting the limits and get this packet dropped.
+ atf_check -s exit:2 -o ignore ping -c 1 -t 1 -s 1200 192.0.2.2
+
+ # We can now also dummynet outbound traffic!
+ pft_set_rules alcatraz \
+ "ether pass out dnpipe 1"
+
+ # We should still be hitting the limits and get this packet dropped.
+ atf_check -s exit:2 -o ignore ping -c 1 -t 1 -s 1200 192.0.2.2
+}
+
+dummynet_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "anchor" "cleanup"
+anchor_head()
+{
+ atf_set descr 'Test ether anchors'
+ atf_set require.user root
+}
+
+anchor_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ epair_a_mac=$(ifconfig ${epair}a ether | awk '/ether/ { print $2; }')
+
+ vnet_mkjail alcatraz ${epair}b
+
+ ifconfig ${epair}a 192.0.2.1/24 up
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "ether anchor \"foo\" in on lo0 {" \
+ "ether block" \
+ "}"
+
+ # That only filters on lo0, so we should still be able to pass traffic
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ pft_set_rules alcatraz \
+ "ether block in" \
+ "ether anchor \"foo\" in on ${epair}b {" \
+ "ether pass" \
+ "}"
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ pft_set_rules alcatraz \
+ "ether pass" \
+ "ether anchor \"bar\" in on ${epair}b {" \
+ "ether block" \
+ "}"
+ atf_check -s exit:2 -o ignore ping -c 1 -t 2 192.0.2.2
+
+ pft_set_rules alcatraz \
+ "ether block in" \
+ "ether anchor \"baz\" on ${epair}b {" \
+ "ether pass in from 01:02:03:04:05:06" \
+ "}" \
+ "ether pass in from ${epair_a_mac}"
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ atf_check -s exit:0 -o match:'baz' jexec alcatraz pfctl -sA
+}
+
+anchor_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "ip" "cleanup"
+ip_head()
+{
+ atf_set descr 'Test filtering based on IP source/destination'
+ atf_set require.user root
+}
+
+ip_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair}b
+
+ ifconfig ${epair}a 192.0.2.1/24 up
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "ether pass" \
+ "ether block in l3 from 192.0.2.1"
+
+ atf_check -s exit:2 -o ignore ping -c 1 192.0.2.2
+
+ # Change IP address and we can ping again
+ ifconfig ${epair}a 192.0.2.3/24 up
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ # Test the 'to' keyword too
+ pft_set_rules alcatraz \
+ "ether pass" \
+ "ether block out l3 to 192.0.2.3"
+ atf_check -s exit:2 -o ignore ping -c 1 192.0.2.2
+
+ # Test table
+ pft_set_rules alcatraz \
+ "table <tbl> { 192.0.2.3 }" \
+ "ether pass" \
+ "ether block out l3 to <tbl>"
+ atf_check -s exit:2 -o ignore ping -c 1 192.0.2.2
+}
+
+ip_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "tag" "cleanup"
+tag_head()
+{
+ atf_set descr 'Test setting tags'
+ atf_set require.user root
+}
+
+tag_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair}b
+ ifconfig ${epair}a 192.0.2.1/24 up
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "ether pass in tag foo" \
+ "block in tagged foo"
+
+ atf_check -s exit:2 -o ignore ping -c 1 192.0.2.2
+
+ pft_set_rules alcatraz \
+ "ether pass in tag bar" \
+ "block in tagged foo"
+
+ # Still passes when tagged differently
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+}
+
+tag_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "match_tag" "cleanup"
+match_tag_head()
+{
+ atf_set descr 'Test matching tags'
+ atf_set require.user root
+}
+
+match_tag_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair}b
+
+ ifconfig ${epair}a 192.0.2.1/24 up
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "ether block out tagged foo" \
+ "pass in proto icmp tag foo"
+
+ atf_check -s exit:2 -o ignore ping -c 1 192.0.2.2
+
+ pft_set_rules alcatraz \
+ "ether block out tagged bar" \
+ "pass in proto icmp tag foo"
+
+ # Still passes when tagged differently
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+}
+
+match_tag_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "short_pkt" "cleanup"
+short_pkt_head()
+{
+ atf_set descr 'Test overly short Ethernet packets'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+short_pkt_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "ether pass in" \
+ "ether pass out" \
+ "ether pass in l3 from 192.0.2.1"
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ jexec alcatraz pfctl -se -v
+
+ # Try sending ever shorter ping requests
+ # BPF won't let us send anything shorter than an Ethernet header, but
+ # that's good enough for this test
+ $(atf_get_srcdir)/pft_ether.py \
+ --sendif ${epair}a \
+ --to 192.0.2.2 \
+ --len 14-64
+}
+
+short_pkt_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "bridge_to" "cleanup"
+bridge_to_head()
+{
+ atf_set descr 'Test bridge-to keyword'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+bridge_to_body()
+{
+ pft_init
+
+ epair_in=$(vnet_mkepair)
+ epair_out=$(vnet_mkepair)
+
+ ifconfig ${epair_in}a 192.0.2.1/24 up
+ ifconfig ${epair_out}a up
+
+ vnet_mkjail alcatraz ${epair_in}b ${epair_out}b
+ jexec alcatraz ifconfig ${epair_in}b 192.0.2.2/24 up
+ jexec alcatraz ifconfig ${epair_out}b up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+ atf_check -s exit:1 -o ignore \
+ ${common_dir}/pft_ping.py \
+ --sendif ${epair_in}a \
+ --to 192.0.2.2 \
+ --recvif ${epair_out}a
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "ether pass in on ${epair_in}b bridge-to ${epair_out}b"
+
+ # Now the packets go out epair_out rather than be processed locally
+ atf_check -s exit:2 -o ignore ping -c 1 192.0.2.2
+ atf_check -s exit:0 -o ignore \
+ ${common_dir}/pft_ping.py \
+ --sendif ${epair_in}a \
+ --to 192.0.2.2 \
+ --recvif ${epair_out}a
+}
+
+bridge_to_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "mac"
+ atf_add_test_case "proto"
+ atf_add_test_case "direction"
+ atf_add_test_case "captive"
+ atf_add_test_case "captive_long"
+ atf_add_test_case "dummynet"
+ atf_add_test_case "anchor"
+ atf_add_test_case "ip"
+ atf_add_test_case "tag"
+ atf_add_test_case "match_tag"
+ atf_add_test_case "short_pkt"
+ atf_add_test_case "bridge_to"
+}
diff --git a/tests/sys/netpfil/pf/forward.sh b/tests/sys/netpfil/pf/forward.sh
new file mode 100644
index 000000000000..e9539bc9d278
--- /dev/null
+++ b/tests/sys/netpfil/pf/forward.sh
@@ -0,0 +1,170 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2017 Kristof Provost <kp@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+common_dir=$(atf_get_srcdir)/../common
+
+atf_test_case "v4" "cleanup"
+v4_head()
+{
+ atf_set descr 'Basic forwarding test'
+ atf_set require.user root
+
+ # We need scapy to be installed for out test scripts to work
+ atf_set require.progs python3 scapy
+}
+
+v4_body()
+{
+ pft_init
+
+ epair_send=$(vnet_mkepair)
+ ifconfig ${epair_send}a 192.0.2.1/24 up
+
+ epair_recv=$(vnet_mkepair)
+ ifconfig ${epair_recv}a up
+
+ vnet_mkjail alcatraz ${epair_send}b ${epair_recv}b
+ jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up
+ jexec alcatraz ifconfig ${epair_recv}b 198.51.100.2/24 up
+ jexec alcatraz sysctl net.inet.ip.forwarding=1
+ jexec alcatraz arp -s 198.51.100.3 00:01:02:03:04:05
+ route add -net 198.51.100.0/24 192.0.2.2
+
+ # Sanity check, can we forward ICMP echo requests without pf?
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_send}a \
+ --to 198.51.100.3 \
+ --recvif ${epair_recv}a
+
+ jexec alcatraz pfctl -e
+
+ # Forward with pf enabled
+ pft_set_rules alcatraz "block in"
+ atf_check -s exit:1 ${common_dir}/pft_ping.py \
+ --sendif ${epair_send}a \
+ --to 198.51.100.3 \
+ --recvif ${epair_recv}a
+
+ pft_set_rules alcatraz "block out"
+ atf_check -s exit:1 ${common_dir}/pft_ping.py \
+ --sendif ${epair_send}a \
+ --to 198.51.100.3 \
+ --recv ${epair_recv}a
+
+ # Allow ICMP
+ pft_set_rules alcatraz "block in" "pass in proto icmp"
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_send}a \
+ --to 198.51.100.3 \
+ --recvif ${epair_recv}a
+}
+
+v4_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "v6" "cleanup"
+v6_head()
+{
+ atf_set descr 'Basic IPv6 forwarding test'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+v6_body()
+{
+ pft_init
+
+ epair_send=$(vnet_mkepair)
+ epair_recv=$(vnet_mkepair)
+
+ ifconfig ${epair_send}a inet6 2001:db8:42::1/64 up no_dad -ifdisabled
+ ifconfig ${epair_recv}a up
+
+ vnet_mkjail alcatraz ${epair_send}b ${epair_recv}b
+
+ jexec alcatraz ifconfig ${epair_send}b inet6 2001:db8:42::2/64 up no_dad
+ jexec alcatraz ifconfig ${epair_recv}b inet6 2001:db8:43::2/64 up no_dad
+ jexec alcatraz sysctl net.inet6.ip6.forwarding=1
+ jexec alcatraz ndp -s 2001:db8:43::3 00:01:02:03:04:05
+ route add -6 2001:db8:43::/64 2001:db8:42::2
+
+ # Sanity check, can we forward ICMP echo requests without pf?
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_send}a \
+ --to 2001:db8:43::3 \
+ --recvif ${epair_recv}a
+
+ jexec alcatraz pfctl -e
+
+ # Block incoming echo request packets
+ pft_set_rules alcatraz \
+ "block in inet6 proto icmp6 icmp6-type echoreq"
+ atf_check -s exit:1 ${common_dir}/pft_ping.py \
+ --sendif ${epair_send}a \
+ --to 2001:db8:43::3 \
+ --recvif ${epair_recv}a
+
+ # Block outgoing echo request packets
+ pft_set_rules alcatraz \
+ "block out inet6 proto icmp6 icmp6-type echoreq"
+ atf_check -s exit:1 -e ignore ${common_dir}/pft_ping.py \
+ --sendif ${epair_send}a \
+ --to 2001:db8:43::3 \
+ --recvif ${epair_recv}a
+
+ # Allow ICMPv6 but nothing else
+ pft_set_rules alcatraz \
+ "block out" \
+ "pass out inet6 proto icmp6"
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_send}a \
+ --to 2001:db8:43::3 \
+ --recvif ${epair_recv}a
+
+ # Allowing ICMPv4 does not allow ICMPv6
+ pft_set_rules alcatraz \
+ "block out inet6 proto icmp6 icmp6-type echoreq" \
+ "pass in proto icmp"
+ atf_check -s exit:1 ${common_dir}/pft_ping.py \
+ --sendif ${epair_send}a \
+ --to 2001:db8:43::3 \
+ --recvif ${epair_recv}a
+}
+
+v6_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "v4"
+ atf_add_test_case "v6"
+}
diff --git a/tests/sys/netpfil/pf/frag-adjhole.py b/tests/sys/netpfil/pf/frag-adjhole.py
new file mode 100644
index 000000000000..99caf66617dd
--- /dev/null
+++ b/tests/sys/netpfil/pf/frag-adjhole.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2025 Alexander Bluhm <bluhm@openbsd.org>
+
+from fragcommon import *
+
+# |--------|
+# |--------|
+# |-------|
+# |----|
+
+def send(src, dst, send_if, recv_if):
+ pid = os.getpid()
+ eid = pid & 0xffff
+ payload = b"ABCDEFGHIJKLMNOP" * 2
+ packet = sp.IP(src=src, dst=dst)/ \
+ sp.ICMP(type='echo-request', id=eid) / payload
+ frag = []
+ fid = pid & 0xffff
+ frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+ flags='MF') / bytes(packet)[20:36])
+ frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+ frag=2, flags='MF') / bytes(packet)[36:52])
+ frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+ frag=1, flags='MF') / bytes(packet)[28:44])
+ frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+ frag=4) / bytes(packet)[52:60])
+ eth=[]
+ for f in frag:
+ eth.append(sp.Ether()/f)
+ if os.fork() == 0:
+ time.sleep(1)
+ sp.sendp(eth, iface=send_if)
+ os._exit(0)
+
+ ans = sp.sniff(iface=recv_if, timeout=3, filter=
+ "ip and src " + dst + " and dst " + src + " and icmp")
+ for a in ans:
+ if a and a.type == sp.ETH_P_IP and \
+ a.payload.proto == 1 and \
+ a.payload.frag == 0 and a.payload.flags == 0 and \
+ sp.icmptypes[a.payload.payload.type] == 'echo-reply':
+ id = a.payload.payload.id
+ print("id=%#x" % (id))
+ if id != eid:
+ print("WRONG ECHO REPLY ID")
+ exit(2)
+ data = a.payload.payload.payload.load
+ print("payload=%s" % (data))
+ if data == payload:
+ exit(0)
+ print("PAYLOAD!=%s" % (payload))
+ exit(1)
+ print("NO ECHO REPLY")
+ exit(2)
+
+if __name__ == '__main__':
+ main(send)
diff --git a/tests/sys/netpfil/pf/frag-overhole.py b/tests/sys/netpfil/pf/frag-overhole.py
new file mode 100644
index 000000000000..91697b6b83c6
--- /dev/null
+++ b/tests/sys/netpfil/pf/frag-overhole.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2025 Alexander Bluhm <bluhm@openbsd.org>
+
+from fragcommon import *
+
+# index boundary 4096 |
+# |--------------|
+# ....
+# |--------------|
+# |----------|
+# |XXXX----------|
+# |XXXX----|
+# |---|
+
+# this should trigger "frag tail overlap %d" and "frag head overlap %d"
+def send(src, dst, send_if, recv_if):
+ pid = os.getpid()
+ eid = pid & 0xffff
+ payload = b"ABCDEFGHIJKLMNOP"
+ dummy = b"01234567"
+ fragsize = 1024
+ boundary = 4096
+ fragnum = int(boundary / fragsize)
+ packet = sp.IP(src=src, dst=dst)/ \
+ sp.ICMP(type='echo-request', id=eid)/ \
+ ((int((boundary + fragsize) / len(payload)) + 1) * payload)
+ packet_length = len(packet)
+ frag = []
+ fid = pid & 0xffff
+ for i in range(fragnum-1):
+ frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+ frag=(i * fragsize)>>3, flags='MF')/
+ bytes(packet)[20 + i * fragsize:20 + (i + 1) * fragsize])
+ frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+ frag=(boundary - fragsize) >> 3, flags='MF')/
+ bytes(packet)[20 + boundary - fragsize:20 + boundary - len(dummy)])
+ frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+ frag=(boundary - len(dummy)) >> 3, flags='MF')/
+ (dummy+bytes(packet)[20 + boundary:20 + boundary + fragsize]))
+ frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+ frag=(boundary - 8 - len(dummy)) >> 3, flags='MF')/
+ (dummy+bytes(packet)[20 + boundary - 8:20 + boundary]))
+ frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+ frag=(boundary + fragsize) >> 3)/bytes(packet)[20 + boundary + fragsize:])
+ eth=[]
+ for f in frag:
+ eth.append(sp.Ether() / f)
+
+ if os.fork() == 0:
+ time.sleep(1)
+ for e in eth:
+ sp.sendp(e, iface=send_if)
+ time.sleep(0.001)
+ os._exit(0)
+
+ ans = sp.sniff(iface=recv_if, timeout=3, filter=
+ "ip and src " + dst + " and dst " + src + " and icmp")
+ for a in ans:
+ if a and a.type == sp.ETH_P_IP and \
+ a.payload.proto == 1 and \
+ a.payload.frag == 0 and \
+ sp.icmptypes[a.payload.payload.type] == 'echo-reply':
+ id = a.payload.payload.id
+ print("id=%#x" % (id))
+ if id != eid:
+ print("WRONG ECHO REPLY ID")
+ exit(2)
+ if a and a.type == sp.ETH_P_IP and \
+ a.payload.proto == 1 and \
+ a.payload.frag > 0 and \
+ a.payload.flags == '':
+ length = (a.payload.frag << 3) + a.payload.len
+ print("len=%d" % (length))
+ if length != packet_length:
+ print("WRONG ECHO REPLY LENGTH")
+ exit(1)
+ exit(0)
+ print("NO ECHO REPLY")
+ exit(1)
+
+if __name__ == '__main__':
+ main(send)
diff --git a/tests/sys/netpfil/pf/frag-overindex.py b/tests/sys/netpfil/pf/frag-overindex.py
new file mode 100644
index 000000000000..594eb9efe39d
--- /dev/null
+++ b/tests/sys/netpfil/pf/frag-overindex.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python3
+#
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2012-2021 Alexander Bluhm <bluhm@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+from fragcommon import *
+
+# index boundary 4096 |
+# |--------------|
+# ....
+# |--------------|
+# |XXXX-----|
+# |--------------|
+#
+# this should trigger "frag index %d, new %d" log in kernel
+
+def send(src, dst, send_if, recv_if):
+ pid = os.getpid()
+ eid = pid & 0xffff
+ payload = b"ABCDEFGHIJKLMNOP"
+ dummy = b"01234567"
+ fragsize = 64
+ boundary = 4096
+ fragnum = int(boundary / fragsize)
+ packet = sp.IP(src=src, dst=dst)/ \
+ sp.ICMP(type='echo-request', id=eid)/ \
+ (int((boundary + 8) / len(payload)) * payload)
+ frag = []
+ fid = pid & 0xffff
+ for i in range(fragnum - 1):
+ frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+ frag=(i * fragsize) >> 3, flags='MF') /
+ bytes(packet)[20 + i * fragsize:20 + (i + 1) * fragsize])
+ frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+ frag=(boundary - 8) >> 3) /
+ (dummy + bytes(packet)[20 + boundary:20 + boundary + 8]))
+ frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+ frag=(boundary - fragsize) >> 3, flags='MF') /
+ bytes(packet)[20 + boundary - fragsize:20 + boundary])
+ eth = []
+ for f in frag:
+ eth.append(sp.Ether() / f)
+
+ if os.fork() == 0:
+ time.sleep(1)
+ for e in eth:
+ sp.sendp(e, iface=send_if)
+ time.sleep(0.001)
+ os._exit(0)
+
+ ans = sp.sniff(iface=recv_if, timeout=5)
+ print(ans)
+ for a in ans:
+ a.show()
+ if a and a.type == sp.ETH_P_IP and \
+ a.payload.proto == 1 and \
+ a.payload.frag == 0 and \
+ sp.icmptypes[a.payload.payload.type] == 'echo-reply':
+ id = a.payload.payload.id
+ print("id=%#x" % (id))
+ if id != eid:
+ print("WRONG ECHO REPLY ID")
+ sys.exit(2)
+ sys.exit(0)
+ print("NO ECHO REPLY")
+ exit(1)
+
+if __name__ == '__main__':
+ main(send)
diff --git a/tests/sys/netpfil/pf/frag-overlimit.py b/tests/sys/netpfil/pf/frag-overlimit.py
new file mode 100644
index 000000000000..e25ebf5b0dcd
--- /dev/null
+++ b/tests/sys/netpfil/pf/frag-overlimit.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python3
+#
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2012-2021 Alexander Bluhm <bluhm@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+from fragcommon import *
+from itertools import chain
+
+# index boundary 4096 |
+# |--------------|
+# ....
+# |--------------|
+# |--------------|
+# ....----|
+# |XXXX-----|
+# |--------------|
+
+# this should trigger "fragment requeue limit exceeded" log in kernel
+
+def send(src, dst, send_if, recv_if):
+ pid = os.getpid()
+ eid = pid & 0xffff
+ payload = b"ABCDEFGHIJKLMNOP"
+ dummy = b"01234567"
+ fragsize = 64
+ boundary = 4096
+ fragnum= int(boundary / fragsize)
+ packet = sp.IP(src=src, dst=dst)/ \
+ sp.ICMP(type='echo-request', id=eid)/ \
+ (int((boundary + boundary) / len(payload)) * payload)
+ frag = []
+ fid = pid & 0xffff
+ for i in chain(range(fragnum - 1), range(fragnum, fragnum + fragnum - 1)):
+ frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+ frag=(i * fragsize) >> 3, flags='MF') /
+ bytes(packet)[20 + i * fragsize:20 + (i + 1) * fragsize])
+ frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+ frag=(boundary + boundary - fragsize) >> 3) /
+ bytes(packet)[20 + boundary + boundary - fragsize:])
+ frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+ frag=(boundary - 8) >> 3, flags='MF')/
+ (dummy + bytes(packet)[20 + boundary:20 + boundary + 8]))
+ frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+ frag=(boundary - fragsize) >> 3, flags='MF') /
+ bytes(packet)[20 + boundary - fragsize:20 + boundary])
+ eth = []
+ for f in frag:
+ eth.append(sp.Ether() / f)
+
+ if os.fork() == 0:
+ time.sleep(1)
+ for e in eth:
+ sp.sendp(e, iface=send_if)
+ time.sleep(0.001)
+ os._exit(0)
+
+ ans = sp.sniff(iface=recv_if, timeout=10, filter=
+ "ip and src " + dst + " and dst " + src + " and icmp")
+ for a in ans:
+ if a and a.type == ETH_P_IP and \
+ a.payload.proto == 1 and \
+ a.payload.frag == 0 and \
+ sp.icmptypes[a.payload.payload.type] == 'echo-reply':
+ id = a.payload.payload.id
+ print("id=%#x" % (id))
+ if id != eid:
+ print("WRONG ECHO REPLY ID")
+ sys.exit(2)
+ print("ECHO REPLY")
+ sys.exit(1)
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main(send)
diff --git a/tests/sys/netpfil/pf/frag-overreplace.py b/tests/sys/netpfil/pf/frag-overreplace.py
new file mode 100644
index 000000000000..ff9184243a1d
--- /dev/null
+++ b/tests/sys/netpfil/pf/frag-overreplace.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python3
+#
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2012-2021 Alexander Bluhm <bluhm@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+from fragcommon import *
+
+# index boundary 4096 |
+# |--------------|
+# ....
+# |--------------|
+# |XXXX-----|
+# |--------------|
+# |--------------|
+
+# this should trigger "frag tail overlap %d" and "frag head overlap %d"
+
+def send(src, dst, send_if, recv_if):
+ pid = os.getpid()
+ eid = pid & 0xffff
+ payload = b"ABCDEFGHIJKLMNOP"
+ dummy = b"01234567"
+ fragsize = 1024
+ boundary = 4096
+ fragnum = int(boundary / fragsize)
+ packet = sp.IP(src=src, dst=dst)/ \
+ sp.ICMP(type='echo-request', id=eid)/ \
+ (int((boundary + fragsize) / len(payload)) * payload)
+ frag = []
+ fid = pid & 0xffff
+
+ for i in range(fragnum - 1):
+ frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+ frag=(i * fragsize) >> 3, flags='MF') /
+ bytes(packet)[20 + i * fragsize:20 + (i + 1) * fragsize])
+ frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+ frag=(boundary - 8) >> 3, flags='MF') /
+ (dummy + bytes(packet)[20 + boundary:20 + boundary + 8]))
+ frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+ frag=(boundary - fragsize) >> 3, flags='MF') /
+ bytes(packet)[20 + boundary - fragsize:20 + boundary])
+ frag.append(sp.IP(src=src, dst=dst, proto=1, id=fid,
+ frag=(boundary) >> 3)/bytes(packet)[20 + boundary:])
+
+ eth=[]
+ for f in frag:
+ eth.append(sp.Ether() / f)
+
+ if os.fork() == 0:
+ time.sleep(1)
+ for e in eth:
+ sp.sendp(e, iface=send_if)
+ time.sleep(0.001)
+ os._exit(0)
+
+ ans = sp.sniff(iface=recv_if, timeout=3, filter="")
+ for a in ans:
+ if a and a.type == sp.ETH_P_IP and \
+ a.payload.proto == 1 and \
+ a.payload.frag == 0 and \
+ sp.icmptypes[a.payload.payload.type] == 'echo-reply':
+ id=a.payload.payload.id
+ if id != eid:
+ print("WRONG ECHO REPLY ID")
+ sys.exit(2)
+ sys.exit(0)
+ print("NO ECHO REPLY")
+ sys.exit(1)
+
+if __name__ == '__main__':
+ main(send)
diff --git a/tests/sys/netpfil/pf/frag6.py b/tests/sys/netpfil/pf/frag6.py
new file mode 100644
index 000000000000..26ae7af7c90c
--- /dev/null
+++ b/tests/sys/netpfil/pf/frag6.py
@@ -0,0 +1,276 @@
+import pytest
+import logging
+import random
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+from utils import DelayedSend
+from atf_python.sys.net.tools import ToolsHelper
+from atf_python.sys.net.vnet import VnetTestTemplate
+
+class TestFrag6(VnetTestTemplate):
+ REQUIRED_MODULES = ["pf", "dummymbuf"]
+ TOPOLOGY = {
+ "vnet1": {"ifaces": ["if1"]},
+ "vnet2": {"ifaces": ["if1"]},
+ "if1": {"prefixes6": [("2001:db8::1/64", "2001:db8::2/64")]},
+ }
+
+ def vnet2_handler(self, vnet):
+ ifname = vnet.iface_alias_map["if1"].name
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.pf_rules([
+ "scrub fragment reassemble min-ttl 10",
+ "pass",
+ "block in inet6 proto icmp6 icmp6-type echoreq",
+ ])
+ ToolsHelper.print_output("/sbin/pfilctl link -i dummymbuf:inet6 inet6")
+ ToolsHelper.print_output("/sbin/sysctl net.dummymbuf.rules=\"inet6 in %s enlarge 3000;\"" % ifname)
+
+ def check_ping_reply(self, packet):
+ print(packet)
+ return False
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_dup_frag_hdr(self):
+ "Test packets with duplicate fragment headers"
+ srv_vnet = self.vnet_map["vnet2"]
+
+ # Import in the correct vnet, so at to not confuse Scapy
+ import scapy.all as sp
+
+ packet = sp.IPv6(src="2001:db8::1", dst="2001:db8::2") \
+ / sp.IPv6ExtHdrFragment(offset = 0, m = 0) \
+ / sp.IPv6ExtHdrFragment(offset = 0, m = 0) \
+ / sp.ICMPv6EchoRequest(data=sp.raw(bytes.fromhex('f00f') * 128))
+
+ # Delay the send so the sniffer is running when we transmit.
+ s = DelayedSend(packet)
+
+ packets = sp.sniff(iface=self.vnet.iface_alias_map["if1"].name,
+ timeout=3)
+ for p in packets:
+ assert not p.getlayer(sp.ICMPv6EchoReply)
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_overlong(self):
+ "Test overly long fragmented packet"
+
+ # Import in the correct vnet, so at to not confuse Scapy
+ import scapy.all as sp
+
+ curr = 0
+ pkts = []
+
+ frag_id = random.randint(0,0xffffffff)
+ gran = 1200
+
+ i = 0
+ while curr <= 65535:
+ ipv61 = sp.IPv6(src="2001:db8::1", dst="2001:db8::2")
+ more = True
+ g = gran
+ if curr + gran > 65535:
+ more = False
+ g = 65530 - curr
+ if i == 0:
+ pkt = ipv61 / sp.IPv6ExtHdrHopByHop(options=[sp.PadN(optlen=2), sp.Pad1()]) / \
+ sp.IPv6ExtHdrFragment(id = frag_id, offset = curr // 8, m = more) / bytes([i] * g)
+ else:
+ pkt = ipv61 / sp.IPv6ExtHdrFragment(id = frag_id, offset = curr // 8, m = more) / bytes([i] * g)
+ pkts.append(pkt)
+ curr += gran
+ i += 1
+
+ sp.send(pkts, inter = 0.1)
+
+class TestFrag6HopHyHop(VnetTestTemplate):
+ REQUIRED_MODULES = ["pf"]
+ TOPOLOGY = {
+ "vnet1": {"ifaces": ["if1", "if2"]},
+ "vnet2": {"ifaces": ["if1", "if2"]},
+ "if1": {"prefixes6": [("2001:db8::1/64", "2001:db8::2/64")]},
+ "if2": {"prefixes6": [("2001:db8:666::1/64", "2001:db8:1::2/64")]},
+ }
+
+ def vnet2_handler(self, vnet):
+ ifname = vnet.iface_alias_map["if1"].name
+ ToolsHelper.print_output("/sbin/sysctl net.inet6.ip6.forwarding=1")
+ ToolsHelper.print_output("/usr/sbin/ndp -s 2001:db8:1::1 00:01:02:03:04:05")
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.print_output("/sbin/pfctl -x loud")
+ ToolsHelper.pf_rules([
+ "scrub fragment reassemble min-ttl 10",
+ "pass allow-opts",
+ ])
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_hop_by_hop(self):
+ "Verify that we reject non-first hop-by-hop headers"
+ if1 = self.vnet.iface_alias_map["if1"].name
+ if2 = self.vnet.iface_alias_map["if2"].name
+ ToolsHelper.print_output("/sbin/route add -6 default 2001:db8::2")
+ ToolsHelper.print_output("/sbin/ping6 -c 1 2001:db8:1::2")
+
+ # Import in the correct vnet, so at to not confuse Scapy
+ import scapy.all as sp
+
+ # A hop-by-hop header is accepted if it's the first header
+ pkt = sp.IPv6(src="2001:db8::1", dst="2001:db8:1::1") \
+ / sp.IPv6ExtHdrHopByHop() \
+ / sp.ICMPv6EchoRequest(data=sp.raw(bytes.fromhex('f0') * 30))
+ pkt.show()
+
+ # Delay the send so the sniffer is running when we transmit.
+ s = DelayedSend(pkt)
+
+ replies = sp.sniff(iface=if2, timeout=3)
+ found = False
+ for p in replies:
+ p.show()
+ ip6 = p.getlayer(sp.IPv6)
+ hbh = p.getlayer(sp.IPv6ExtHdrHopByHop)
+ icmp6 = p.getlayer(sp.ICMPv6EchoRequest)
+
+ if not ip6 or not icmp6:
+ continue
+ assert ip6.src == "2001:db8::1"
+ assert ip6.dst == "2001:db8:1::1"
+ assert hbh
+ assert icmp6
+ found = True
+ assert found
+
+ # A hop-by-hop header causes the packet to be dropped if it's not the
+ # first extension header
+ pkt = sp.IPv6(src="2001:db8::1", dst="2001:db8:1::1") \
+ / sp.IPv6ExtHdrFragment(offset=0, m=0) \
+ / sp.IPv6ExtHdrHopByHop() \
+ / sp.ICMPv6EchoRequest(data=sp.raw(bytes.fromhex('f0') * 30))
+ pkt2 = sp.IPv6(src="2001:db8::1", dst="2001:db8:1::1") \
+ / sp.ICMPv6EchoRequest(data=sp.raw(bytes.fromhex('f0') * 30))
+
+ # Delay the send so the sniffer is running when we transmit.
+ ToolsHelper.print_output("/sbin/ping6 -c 1 2001:db8:1::2")
+
+ s = DelayedSend([ pkt2, pkt ])
+ replies = sp.sniff(iface=if2, timeout=10)
+ found = False
+ for p in replies:
+ # Expect to find the packet without the hop-by-hop header, not the
+ # one with
+ p.show()
+ ip6 = p.getlayer(sp.IPv6)
+ hbh = p.getlayer(sp.IPv6ExtHdrHopByHop)
+ icmp6 = p.getlayer(sp.ICMPv6EchoRequest)
+
+ if not ip6 or not icmp6:
+ continue
+ assert ip6.src == "2001:db8::1"
+ assert ip6.dst == "2001:db8:1::1"
+ assert not hbh
+ assert icmp6
+ found = True
+ assert found
+
+class TestFrag6_Overlap(VnetTestTemplate):
+ REQUIRED_MODULES = ["pf"]
+ TOPOLOGY = {
+ "vnet1": {"ifaces": ["if1"]},
+ "vnet2": {"ifaces": ["if1"]},
+ "if1": {"prefixes6": [("2001:db8::1/64", "2001:db8::2/64")]},
+ }
+
+ def vnet2_handler(self, vnet):
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.print_output("/sbin/pfctl -x loud")
+ ToolsHelper.pf_rules([
+ "scrub fragment reassemble",
+ "pass",
+ ])
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_overlap(self):
+ "Ensure we discard packets with overlapping fragments"
+
+ # Import in the correct vnet, so at to not confuse Scapy
+ import scapy.all as sp
+
+ packet = sp.IPv6(src="2001:db8::1", dst="2001:db8::2") \
+ / sp.ICMPv6EchoRequest(data=sp.raw(bytes.fromhex('f00f') * 90))
+ frags = sp.fragment6(packet, 128)
+ assert len(frags) == 3
+
+ f = frags[0].getlayer(sp.IPv6ExtHdrFragment)
+ # Fragment with overlap
+ overlap = sp.IPv6(src="2001:db8::1", dst="2001:db8::2") \
+ / sp.IPv6ExtHdrFragment(offset = 4, m = 1, id = f.id, nh = f.nh) \
+ / sp.raw(bytes.fromhex('f00f') * 4)
+ frags = [ frags[0], frags[1], overlap, frags[2] ]
+
+ # Delay the send so the sniffer is running when we transmit.
+ s = DelayedSend(frags)
+
+ packets = sp.sniff(iface=self.vnet.iface_alias_map["if1"].name,
+ timeout=3)
+ for p in packets:
+ p.show()
+ assert not p.getlayer(sp.ICMPv6EchoReply)
+
+class TestFrag6_RouteTo(VnetTestTemplate):
+ REQUIRED_MODULES = ["pf"]
+ TOPOLOGY = {
+ "vnet1": {"ifaces": ["if1"]},
+ "vnet2": {"ifaces": ["if1", "if2"]},
+ "vnet3": {"ifaces": ["if2"]},
+ "if1": {"prefixes6": [("2001:db8::1/64", "2001:db8::2/64")]},
+ "if2": {"prefixes6": [("2001:db8:1::1/64", "2001:db8:1::2/64")]},
+ }
+
+ def vnet2_handler(self, vnet):
+ if2name = vnet.iface_alias_map["if2"].name
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.print_output("/sbin/pfctl -x loud")
+ ToolsHelper.pf_rules([
+ "scrub fragment reassemble",
+ "pass in route-to (%s 2001:db8:1::2) from 2001:db8::1 to 2001:db8:666::1" % if2name,
+ ])
+
+ ToolsHelper.print_output("/sbin/ifconfig %s mtu 1300" % if2name)
+ ToolsHelper.print_output("/sbin/sysctl net.inet6.ip6.forwarding=1")
+
+ def vnet3_handler(self, vnet):
+ pass
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_too_big(self):
+ ToolsHelper.print_output("/sbin/route add -6 default 2001:db8::2")
+
+ # Import in the correct vnet, so at to not confuse Scapy
+ import scapy.all as sp
+
+ pkt = sp.IPv6(dst="2001:db8:666::1") \
+ / sp.ICMPv6EchoRequest(data=sp.raw(bytes.fromhex('f0') * 3000))
+ frags = sp.fragment6(pkt, 1320)
+
+ reply = sp.sr1(frags, timeout=3)
+ if reply:
+ reply.show()
+
+ assert reply
+
+ ip6 = reply.getlayer(sp.IPv6)
+ icmp6 = reply.getlayer(sp.ICMPv6PacketTooBig)
+ err_ip6 = reply.getlayer(sp.IPerror6)
+
+ assert ip6
+ assert ip6.src == "2001:db8::2"
+ assert ip6.dst == "2001:db8::1"
+ assert icmp6
+ assert icmp6.mtu == 1300
+ assert err_ip6
+ assert err_ip6.src == "2001:db8::1"
+ assert err_ip6.dst == "2001:db8:666::1"
diff --git a/tests/sys/netpfil/pf/fragcommon.py b/tests/sys/netpfil/pf/fragcommon.py
new file mode 100644
index 000000000000..1ca3129e6057
--- /dev/null
+++ b/tests/sys/netpfil/pf/fragcommon.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python3
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2021 Rubicon Communications, LLC (Netgate). All Rights Reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import argparse
+import os
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import sys
+import time
+
+def main(send):
+ parser = argparse.ArgumentParser("frag-overindex.py",
+ description="Fragmentation test tool")
+ parser.add_argument('--to', nargs=1,
+ required=True,
+ help='The address to send the fragmented packets to')
+ parser.add_argument('--fromaddr', nargs=1,
+ required=True,
+ help='The source address for the generated packets')
+ parser.add_argument('--sendif', nargs=1,
+ required=True,
+ help='The interface through which the packet(s) will be sent')
+ parser.add_argument('--recvif', nargs=1,
+ required=True,
+ help='The interface to expect the reply on')
+
+ args = parser.parse_args()
+
+ send(args.fromaddr[0], args.to[0], args.sendif[0], args.recvif[0])
diff --git a/tests/sys/netpfil/pf/fragmentation_compat.sh b/tests/sys/netpfil/pf/fragmentation_compat.sh
new file mode 100644
index 000000000000..1f4550ebd69e
--- /dev/null
+++ b/tests/sys/netpfil/pf/fragmentation_compat.sh
@@ -0,0 +1,338 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2017 Kristof Provost <kp@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "too_many_fragments" "cleanup"
+
+too_many_fragments_head()
+{
+ atf_set descr 'IPv4 fragment limitation test'
+ atf_set require.user root
+}
+
+too_many_fragments_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}a
+
+ ifconfig ${epair}b inet 192.0.2.1/24 up
+ jexec alcatraz ifconfig ${epair}a 192.0.2.2/24 up
+
+ ifconfig ${epair}b mtu 200
+ jexec alcatraz ifconfig ${epair}a mtu 200
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "scrub all fragment reassemble"
+
+ # So we know pf is limiting things
+ jexec alcatraz sysctl net.inet.ip.maxfragsperpacket=1024
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ # We can ping with < 64 fragments
+ atf_check -s exit:0 -o ignore ping -c 1 -s 800 192.0.2.2
+
+ # Too many fragments should fail
+ atf_check -s exit:2 -o ignore ping -c 1 -s 20000 192.0.2.2
+}
+
+too_many_fragments_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "v6" "cleanup"
+v6_head()
+{
+ atf_set descr 'IPv6 fragmentation test'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+v6_body()
+{
+ pft_init
+
+ epair_send=$(vnet_mkepair)
+ epair_link=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair_send}b ${epair_link}a
+ vnet_mkjail singsing ${epair_link}b
+
+ ifconfig ${epair_send}a inet6 2001:db8:42::1/64 no_dad up
+
+ jexec alcatraz ifconfig ${epair_send}b inet6 2001:db8:42::2/64 no_dad up
+ jexec alcatraz ifconfig ${epair_link}a inet6 2001:db8:43::2/64 no_dad up
+ jexec alcatraz sysctl net.inet6.ip6.forwarding=1
+
+ jexec singsing ifconfig ${epair_link}b inet6 2001:db8:43::3/64 no_dad up
+ jexec singsing route add -6 2001:db8:42::/64 2001:db8:43::2
+ route add -6 2001:db8:43::/64 2001:db8:42::2
+
+ jexec alcatraz ifconfig ${epair_send}b inet6 -ifdisabled
+ jexec alcatraz ifconfig ${epair_link}a inet6 -ifdisabled
+ jexec singsing ifconfig ${epair_link}b inet6 -ifdisabled
+ ifconfig ${epair_send}a inet6 -ifdisabled
+
+ ifconfig ${epair_send}a
+ jexec alcatraz ifconfig ${epair_send}b
+ lladdr=$(jexec alcatraz ifconfig ${epair_send}b | awk '/ scopeid / { print($2); }' | cut -f 1 -d %)
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "scrub fragment reassemble" \
+ "block in" \
+ "pass in inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in inet6 proto icmp6 icmp6-type { echoreq, echorep }" \
+ "set skip on lo"
+
+ # Host test
+ atf_check -s exit:0 -o ignore \
+ ping -6 -c 1 2001:db8:42::2
+
+ atf_check -s exit:0 -o ignore \
+ ping -6 -c 1 -s 4500 2001:db8:42::2
+
+ atf_check -s exit:0 -o ignore\
+ ping -6 -c 1 -b 70000 -s 65000 2001:db8:42::2
+
+ # Force an NDP lookup
+ ping -6 -c 1 ${lladdr}%${epair_send}a
+
+ atf_check -s exit:0 -o ignore\
+ ping -6 -c 1 -b 70000 -s 65000 ${lladdr}%${epair_send}a
+
+ # Forwarding test
+ atf_check -s exit:0 -o ignore \
+ ping -6 -c 1 2001:db8:43::3
+
+ atf_check -s exit:0 -o ignore \
+ ping -6 -c 1 -s 4500 2001:db8:43::3
+
+ atf_check -s exit:0 -o ignore\
+ ping -6 -c 1 -b 70000 -s 65000 2001:db8:43::3
+
+ $(atf_get_srcdir)/CVE-2019-5597.py \
+ ${epair_send}a \
+ 2001:db8:42::1 \
+ 2001:db8:43::3
+}
+
+v6_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "mtu_diff" "cleanup"
+mtu_diff_head()
+{
+ atf_set descr 'Test reassembly across different MTUs, PR #255432'
+ atf_set require.user root
+}
+
+mtu_diff_body()
+{
+ pft_init
+
+ epair_small=$(vnet_mkepair)
+ epair_large=$(vnet_mkepair)
+
+ vnet_mkjail first ${epair_small}b ${epair_large}a
+ vnet_mkjail second ${epair_large}b
+
+ ifconfig ${epair_small}a 192.0.2.1/25 up
+ jexec first ifconfig ${epair_small}b 192.0.2.2/25 up
+
+ jexec first sysctl net.inet.ip.forwarding=1
+ jexec first ifconfig ${epair_large}a 192.0.2.130/25 up
+ jexec first ifconfig ${epair_large}a mtu 9000
+ jexec second ifconfig ${epair_large}b 192.0.2.131/25 up
+ jexec second ifconfig ${epair_large}b mtu 9000
+ jexec second route add default 192.0.2.130
+
+ route add 192.0.2.128/25 192.0.2.2
+
+ jexec first pfctl -e
+ pft_set_rules first \
+ "scrub all fragment reassemble"
+
+ # Sanity checks
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.130
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.131
+
+ # Large packet that'll get reassembled and sent out in one on the large
+ # epair
+ atf_check -s exit:0 -o ignore ping -c 1 -s 8000 192.0.2.131
+}
+
+mtu_diff_cleanup()
+{
+ pft_cleanup
+}
+
+frag_common()
+{
+ name=$1
+
+ pft_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}a
+
+ ifconfig ${epair}b inet 192.0.2.1/24 up
+ jexec alcatraz ifconfig ${epair}a 192.0.2.2/24 up
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "scrub all fragment reassemble"
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ atf_check -s exit:0 -o ignore $(atf_get_srcdir)/frag-${1}.py \
+ --to 192.0.2.2 \
+ --fromaddr 192.0.2.1 \
+ --sendif ${epair}b \
+ --recvif ${epair}b
+}
+
+atf_test_case "overreplace" "cleanup"
+overreplace_head()
+{
+ atf_set descr 'ping fragment that overlaps fragment at index boundary and replace it'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+overreplace_body()
+{
+ frag_common overreplace
+}
+
+overreplace_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "overindex" "cleanup"
+overindex_head()
+{
+ atf_set descr 'ping fragment that overlaps the first fragment at index boundary'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+overindex_body()
+{
+ frag_common overindex
+}
+
+overindex_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "overlimit" "cleanup"
+overlimit_head()
+{
+ atf_set descr 'ping fragment at index boundary that cannot be requeued'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+overlimit_body()
+{
+ frag_common overlimit
+}
+
+overlimit_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "reassemble" "cleanup"
+reassemble_head()
+{
+ atf_set descr 'Test reassembly'
+ atf_set require.user root
+}
+
+reassemble_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}a
+
+ ifconfig ${epair}b inet 192.0.2.1/24 up
+ jexec alcatraz ifconfig ${epair}a 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "scrub in" \
+ "pass out" \
+ "block in" \
+ "pass in inet proto icmp all icmp-type echoreq"
+
+ # Both single packet & fragmented pass when we scrub
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+ atf_check -s exit:0 -o ignore ping -c 1 -s 2000 192.0.2.2
+
+ pft_set_rules alcatraz \
+ "scrub in fragment no reassemble" \
+ "pass out" \
+ "block in" \
+ "pass in inet proto icmp all icmp-type echoreq"
+
+ # And the fragmented ping doesn't pass if we do not reassemble
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+ atf_check -s exit:2 -o ignore ping -c 1 -s 2000 192.0.2.2
+}
+
+reassemble_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "too_many_fragments"
+ atf_add_test_case "v6"
+ atf_add_test_case "mtu_diff"
+ atf_add_test_case "overreplace"
+ atf_add_test_case "overindex"
+ atf_add_test_case "overlimit"
+ atf_add_test_case "reassemble"
+}
diff --git a/tests/sys/netpfil/pf/fragmentation_no_reassembly.sh b/tests/sys/netpfil/pf/fragmentation_no_reassembly.sh
new file mode 100644
index 000000000000..7cab89f5debb
--- /dev/null
+++ b/tests/sys/netpfil/pf/fragmentation_no_reassembly.sh
@@ -0,0 +1,130 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2017 Kristof Provost <kp@FreeBSD.org>
+# Copyright (c) 2023 Kajetan Staszkiewicz <vegeta@tuxpowered.net>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "match_full_v4" "cleanup"
+match_full_v4_head()
+{
+ atf_set descr 'Matching non-fragmented IPv4 packets'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+match_full_v4_body()
+{
+ setup_router_dummy_ipv4
+
+ # Sanity check.
+ ping_dummy_check_request exit:0 --ping-type=icmp
+
+ # Only non-fragmented packets are passed
+ jexec router pfctl -e
+ pft_set_rules router \
+ "pass out" \
+ "block in" \
+ "pass in inet proto icmp all icmp-type echoreq"
+ ping_dummy_check_request exit:0 --ping-type=icmp
+ ping_dummy_check_request exit:1 --ping-type=icmp --send-length=2000 --send-frag-length 1000
+}
+
+match_full_v4_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_test_case "match_fragment_v4" "cleanup"
+match_fragment_v4_head()
+{
+ atf_set descr 'Matching fragmented IPv4 packets'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+match_fragment_v4_body()
+{
+ setup_router_dummy_ipv4
+
+ # Sanity check.
+ ping_dummy_check_request exit:0 --ping-type=icmp
+
+ # Only fragmented packets are passed
+ pft_set_rules router \
+ "pass out" \
+ "block in" \
+ "pass in inet proto icmp fragment"
+ ping_dummy_check_request exit:1 --ping-type=icmp
+ ping_dummy_check_request exit:0 --ping-type=icmp --send-length=2000 --send-frag-length 1000
+}
+
+match_fragment_v4_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_test_case "compat_override_v4" "cleanup"
+compat_override_v4_head()
+{
+ atf_set descr 'Scrub rules override "set reassemble" for IPv4'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+compat_override_v4_body()
+{
+ setup_router_dummy_ipv4
+
+ # Sanity check.
+ ping_dummy_check_request exit:0 --ping-type=icmp
+
+ # The same as match_fragment_v4 but with "set reassemble yes" which
+ # is ignored because of presence of scrub rules.
+ # Only fragmented packets are passed.
+ pft_set_rules router \
+ "set reassemble yes" \
+ "no scrub" \
+ "pass out" \
+ "block in" \
+ "pass in inet proto icmp fragment"
+ ping_dummy_check_request exit:1 --ping-type=icmp
+ ping_dummy_check_request exit:0 --ping-type=icmp --send-length=2000 --send-frag-length 1000
+}
+
+compat_override_v4_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_init_test_cases()
+{
+ atf_add_test_case "match_full_v4"
+ atf_add_test_case "match_fragment_v4"
+ atf_add_test_case "compat_override_v4"
+}
diff --git a/tests/sys/netpfil/pf/fragmentation_pass.sh b/tests/sys/netpfil/pf/fragmentation_pass.sh
new file mode 100644
index 000000000000..5deaba18301d
--- /dev/null
+++ b/tests/sys/netpfil/pf/fragmentation_pass.sh
@@ -0,0 +1,668 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2017 Kristof Provost <kp@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+common_dir=$(atf_get_srcdir)/../common
+
+atf_test_case "too_many_fragments" "cleanup"
+
+too_many_fragments_head()
+{
+ atf_set descr 'IPv4 fragment limitation test'
+ atf_set require.user root
+}
+
+too_many_fragments_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}a
+
+ ifconfig ${epair}b inet 192.0.2.1/24 up
+ jexec alcatraz ifconfig ${epair}a 192.0.2.2/24 up
+
+ ifconfig ${epair}b mtu 200
+ jexec alcatraz ifconfig ${epair}a mtu 200
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "set reassemble yes" \
+ "pass keep state"
+
+ # So we know pf is limiting things
+ jexec alcatraz sysctl net.inet.ip.maxfragsperpacket=1024
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ # We can ping with < 64 fragments
+ atf_check -s exit:0 -o ignore ping -c 1 -s 800 192.0.2.2
+
+ # Too many fragments should fail
+ atf_check -s exit:2 -o ignore ping -c 1 -s 20000 192.0.2.2
+}
+
+too_many_fragments_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "v6" "cleanup"
+v6_head()
+{
+ atf_set descr 'IPv6 fragmentation test'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+v6_body()
+{
+ pft_init
+
+ epair_send=$(vnet_mkepair)
+ epair_link=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair_send}b ${epair_link}a
+ vnet_mkjail singsing ${epair_link}b
+
+ ifconfig ${epair_send}a inet6 2001:db8:42::1/64 no_dad up
+
+ jexec alcatraz ifconfig ${epair_send}b inet6 2001:db8:42::2/64 no_dad up
+ jexec alcatraz ifconfig ${epair_link}a inet6 2001:db8:43::2/64 no_dad up
+ jexec alcatraz sysctl net.inet6.ip6.forwarding=1
+
+ jexec singsing ifconfig ${epair_link}b inet6 2001:db8:43::3/64 no_dad up
+ jexec singsing route add -6 2001:db8:42::/64 2001:db8:43::2
+ route add -6 2001:db8:43::/64 2001:db8:42::2
+
+ jexec alcatraz ifconfig ${epair_send}b inet6 -ifdisabled
+ jexec alcatraz ifconfig ${epair_link}a inet6 -ifdisabled
+ jexec singsing ifconfig ${epair_link}b inet6 -ifdisabled
+ ifconfig ${epair_send}a inet6 -ifdisabled
+
+ ifconfig ${epair_send}a
+ jexec alcatraz ifconfig ${epair_send}b
+ lladdr=$(jexec alcatraz ifconfig ${epair_send}b | awk '/ scopeid / { print($2); }' | cut -f 1 -d %)
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "set reassemble yes" \
+ "pass keep state" \
+ "block in" \
+ "pass in inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in inet6 proto icmp6 icmp6-type { echoreq, echorep }" \
+ "set skip on lo"
+
+ # Host test
+ atf_check -s exit:0 -o ignore \
+ ping -6 -c 1 2001:db8:42::2
+
+ atf_check -s exit:0 -o ignore \
+ ping -6 -c 1 -s 4500 2001:db8:42::2
+
+ atf_check -s exit:0 -o ignore\
+ ping -6 -c 1 -b 70000 -s 65000 2001:db8:42::2
+
+ # Force an NDP lookup
+ ping -6 -c 1 ${lladdr}%${epair_send}a
+
+ atf_check -s exit:0 -o ignore\
+ ping -6 -c 1 -b 70000 -s 65000 ${lladdr}%${epair_send}a
+
+ # Forwarding test
+ atf_check -s exit:0 -o ignore \
+ ping -6 -c 1 2001:db8:43::3
+
+ atf_check -s exit:0 -o ignore \
+ ping -6 -c 1 -s 4500 2001:db8:43::3
+
+ atf_check -s exit:0 -o ignore\
+ ping -6 -c 1 -b 70000 -s 65000 2001:db8:43::3
+
+ $(atf_get_srcdir)/CVE-2019-5597.py \
+ ${epair_send}a \
+ 2001:db8:42::1 \
+ 2001:db8:43::3
+}
+
+v6_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "v6_route_to" "cleanup"
+v6_route_to_head()
+{
+ atf_set descr 'Test IPv6 reassembly combined with route-to'
+ atf_set require.user root
+}
+
+v6_route_to_body()
+{
+ pft_init
+
+ epair_send=$(vnet_mkepair)
+ epair_link=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair_send}b ${epair_link}a
+ vnet_mkjail singsing ${epair_link}b
+
+ ifconfig ${epair_send}a inet6 2001:db8:42::1/64 no_dad up
+
+ jexec alcatraz ifconfig ${epair_send}b inet6 2001:db8:42::2/64 no_dad up
+ jexec alcatraz ifconfig ${epair_link}a inet6 2001:db8:43::2/64 no_dad up
+ jexec alcatraz sysctl net.inet6.ip6.forwarding=1
+
+ jexec singsing ifconfig ${epair_link}b inet6 2001:db8:43::3/64 no_dad up
+ jexec singsing route add -6 2001:db8:42::/64 2001:db8:43::2
+ route add -6 2001:db8:43::/64 2001:db8:42::2
+
+ jexec alcatraz ifconfig ${epair_send}b inet6 -ifdisabled
+ jexec alcatraz ifconfig ${epair_link}a inet6 -ifdisabled
+ jexec singsing ifconfig ${epair_link}b inet6 -ifdisabled
+ ifconfig ${epair_send}a inet6 -ifdisabled
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "set reassemble yes" \
+ "pass" \
+ "pass in route-to (${epair_link}a 2001:db8:43::3) inet6 proto icmp6 from any to 2001:db8:43::3 keep state"
+
+ # Forwarding test
+ atf_check -s exit:0 -o ignore \
+ ping -6 -c 1 2001:db8:43::3
+
+ atf_check -s exit:0 -o ignore \
+ ping -6 -c 1 -s 4500 2001:db8:43::3
+
+ atf_check -s exit:0 -o ignore\
+ ping -6 -c 1 -b 70000 -s 65000 2001:db8:43::3
+
+ # Now test this without fragmentation
+ pft_set_rules alcatraz \
+ "set reassemble no" \
+ "pass" \
+ "pass in route-to (${epair_link}a 2001:db8:43::3) inet6 proto icmp6 from any to 2001:db8:43::3 keep state"
+
+ atf_check -s exit:0 -o ignore \
+ ping -6 -c 1 2001:db8:43::3
+
+ atf_check -s exit:0 -o ignore \
+ ping -6 -c 1 -s 4500 2001:db8:43::3
+
+ atf_check -s exit:0 -o ignore\
+ ping -6 -c 1 -b 70000 -s 65000 2001:db8:43::3
+}
+
+v6_route_to_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "mtu_diff" "cleanup"
+mtu_diff_head()
+{
+ atf_set descr 'Test reassembly across different MTUs, PR #255432'
+ atf_set require.user root
+}
+
+mtu_diff_body()
+{
+ pft_init
+
+ epair_small=$(vnet_mkepair)
+ epair_large=$(vnet_mkepair)
+
+ vnet_mkjail first ${epair_small}b ${epair_large}a
+ vnet_mkjail second ${epair_large}b
+
+ ifconfig ${epair_small}a 192.0.2.1/25 up
+ jexec first ifconfig ${epair_small}b 192.0.2.2/25 up
+
+ jexec first sysctl net.inet.ip.forwarding=1
+ jexec first ifconfig ${epair_large}a 192.0.2.130/25 up
+ jexec first ifconfig ${epair_large}a mtu 9000
+ jexec second ifconfig ${epair_large}b 192.0.2.131/25 up
+ jexec second ifconfig ${epair_large}b mtu 9000
+ jexec second route add default 192.0.2.130
+
+ route add 192.0.2.128/25 192.0.2.2
+
+ jexec first pfctl -e
+ pft_set_rules first \
+ "set reassemble yes" \
+ "pass keep state"
+
+ # Sanity checks
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.130
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.131
+
+ # Large packet that'll get reassembled and sent out in one on the large
+ # epair
+ atf_check -s exit:0 -o ignore ping -c 1 -s 8000 192.0.2.131
+}
+
+mtu_diff_cleanup()
+{
+ pft_cleanup
+}
+
+frag_common()
+{
+ name=$1
+
+ pft_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}a
+
+ ifconfig ${epair}b inet 192.0.2.1/24 up
+ jexec alcatraz ifconfig ${epair}a 192.0.2.2/24 up
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "set reassemble yes" \
+ "pass keep state"
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ atf_check -s exit:0 -o ignore $(atf_get_srcdir)/frag-${1}.py \
+ --to 192.0.2.2 \
+ --fromaddr 192.0.2.1 \
+ --sendif ${epair}b \
+ --recvif ${epair}b
+}
+
+atf_test_case "overreplace" "cleanup"
+overreplace_head()
+{
+ atf_set descr 'ping fragment that overlaps fragment at index boundary and replace it'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+overreplace_body()
+{
+ frag_common overreplace
+}
+
+overreplace_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "overindex" "cleanup"
+overindex_head()
+{
+ atf_set descr 'ping fragment that overlaps the first fragment at index boundary'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+overindex_body()
+{
+ frag_common overindex
+}
+
+overindex_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "overlimit" "cleanup"
+overlimit_head()
+{
+ atf_set descr 'ping fragment at index boundary that cannot be requeued'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+overlimit_body()
+{
+ frag_common overlimit
+}
+
+overlimit_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "overhole" "cleanup"
+overhole_head()
+{
+ atf_set descr 'ping fragment at index boundary which modifies pf hole counter'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+overhole_body()
+{
+ frag_common overhole
+}
+
+overhole_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "adjhole" "cleanup"
+adjhole_head()
+{
+ atf_set descr 'overlapping ping fragments which modifies pf hole counter'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+adjhole_body()
+{
+ frag_common adjhole
+}
+
+adjhole_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "reassemble" "cleanup"
+reassemble_head()
+{
+ atf_set descr 'Test reassembly'
+ atf_set require.user root
+}
+
+reassemble_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}a
+
+ ifconfig ${epair}b inet 192.0.2.1/24 up
+ jexec alcatraz ifconfig ${epair}a 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "pass out" \
+ "block in" \
+ "pass in inet proto icmp all icmp-type echoreq"
+
+ # Single fragment passes
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ # But a fragmented ping does not
+ atf_check -s exit:2 -o ignore ping -c 1 -s 2000 192.0.2.2
+
+ pft_set_rules alcatraz \
+ "set reassemble yes" \
+ "pass out" \
+ "block in" \
+ "pass in inet proto icmp all icmp-type echoreq"
+
+ # Both single packet & fragmented pass when we scrub
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+ atf_check -s exit:0 -o ignore ping -c 1 -s 2000 192.0.2.2
+}
+
+reassemble_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "no_df" "cleanup"
+no_df_head()
+{
+ atf_set descr 'Test removing of DF flag'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+no_df_body()
+{
+ setup_router_server_ipv4
+
+ # Tester can send long packets which will get fragmented by the router.
+ # Replies from server will come in fragments which might get
+ # reassembled resulting in a long reply packet sent back to tester.
+ ifconfig ${epair_tester}a mtu 9000
+ jexec router ifconfig ${epair_tester}b mtu 9000
+ jexec router ifconfig ${epair_server}a mtu 1500
+ jexec server ifconfig ${epair_server}b mtu 1500
+
+ # Sanity check.
+ ping_server_check_reply exit:0 --ping-type=icmp
+
+ # Enable packet reassembly with clearing of the no-df flag.
+ pft_set_rules router \
+ "scrub all fragment reassemble no-df" \
+ "block" \
+ "pass inet proto icmp all icmp-type echoreq"
+ # Ping with non-fragmentable packets.
+ # pf will strip the DF flag resulting in fragmentation and packets
+ # getting properly forwarded.
+ ping_server_check_reply exit:0 --ping-type=icmp --send-length=2000 --send-flags DF
+}
+
+no_df_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "reassemble_slowpath" "cleanup"
+reassemble_slowpath_head()
+{
+ atf_set descr 'Test reassembly on the slow path'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+reassemble_slowpath_body()
+{
+ if ! sysctl -q kern.features.ipsec >/dev/null ; then
+ atf_skip "This test requires ipsec"
+ fi
+
+ setup_router_server_ipv4
+
+ # Now define an ipsec policy so we end up taking the slow path.
+ # We don't actually need the traffic to go through ipsec, we just don't
+ # want to go through ip_tryforward().
+ echo "flush;
+ spdflush;
+ spdadd 203.0.113.1/32 203.0.113.2/32 any -P out ipsec esp/transport//require;
+ add 203.0.113.1 203.0.113.2 esp 0x1001 -E aes-gcm-16 \"12345678901234567890\";" \
+ | jexec router setkey -c
+
+ # Sanity check.
+ ping_server_check_reply exit:0 --ping-type=icmp
+
+ # Enable packet reassembly with clearing of the no-df flag.
+ pft_set_rules router \
+ "scrub in on ${epair_tester}b fragment no reassemble" \
+ "scrub on ${epair_server}a fragment reassemble" \
+ "pass"
+
+ # Ensure that the packet makes it through the slow path
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 -s 2000 198.51.100.2
+}
+
+reassemble_slowpath_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "dummynet" "cleanup"
+dummynet_head()
+{
+ atf_set descr 'dummynet + reassembly test'
+ atf_set require.user root
+}
+
+dummynet_body()
+{
+ pft_init
+ dummynet_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}a
+
+ ifconfig ${epair}b inet 192.0.2.1/24 up
+ jexec alcatraz ifconfig ${epair}a 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ jexec alcatraz dnctl pipe 1 config bw 600Byte/s
+ jexec alcatraz dnctl pipe 2 config bw 700Byte/s
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "set reassemble yes" \
+ "block" \
+ "pass inet proto icmp all icmp-type echoreq dnpipe (1, 2)"
+
+ atf_check -s exit:0 -o ignore ping -s 2000 -c 1 192.0.2.2
+}
+
+dummynet_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "dummynet_nat" "cleanup"
+dummynet_nat_head()
+{
+ atf_set descr 'Test dummynet on NATed fragmented traffic'
+ atf_set require.user root
+}
+
+dummynet_nat_body()
+{
+ pft_init
+ dummynet_init
+
+ epair_one=$(vnet_mkepair)
+ ifconfig ${epair_one}a 192.0.2.1/24 up
+
+ epair_two=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair_one}b ${epair_two}a
+ jexec alcatraz ifconfig ${epair_one}b 192.0.2.2/24 up
+ jexec alcatraz ifconfig ${epair_two}a 198.51.100.1/24 up
+ jexec alcatraz sysctl net.inet.ip.forwarding=1
+
+ vnet_mkjail singsing ${epair_two}b
+ jexec singsing ifconfig ${epair_two}b 198.51.100.2/24 up
+ jexec singsing route add default 198.51.100.1
+
+ route add 198.51.100.0/24 192.0.2.2
+
+ jexec alcatraz dnctl pipe 1 config bw 1600Byte/s
+ jexec alcatraz dnctl pipe 2 config bw 1700Byte/s
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "set reassemble yes" \
+ "nat on ${epair_two}a from 192.0.2.0/24 -> (${epair_two}a)" \
+ "block in" \
+ "pass in inet proto icmp all icmp-type echoreq dnpipe (1, 2)"
+
+ atf_check -s exit:0 -o ignore ping -c 1 198.51.100.2
+ atf_check -s exit:0 -o ignore ping -c 1 -s 2000 198.51.100.2
+}
+
+dummynet_nat_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "dummynet_fragmented" "cleanup"
+dummynet_fragmented_head()
+{
+ atf_set descr 'Test dummynet on NATed fragmented traffic'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+dummynet_fragmented_body()
+{
+ pft_init
+ dummynet_init
+
+ # No test for IPv6. IPv6 fragment reassembly can't be disabled.
+ setup_router_dummy_ipv4
+
+ jexec router dnctl pipe 1 config delay 1
+
+ pft_set_rules router \
+ "set reassemble no" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in on ${epair_tester}b inet proto udp dnpipe (1, 1)" \
+ "pass out on ${epair_server}a inet proto udp" \
+
+ ping_dummy_check_request exit:0 --ping-type=udp --send-length=10000 --send-frag-length=1280
+
+ rules=$(mktemp) || exit 1
+ jexec router pfctl -qvsr | normalize_pfctl_s > $rules
+
+ # Count that fragmented packets have hit the rule only once and that
+ # they have not created states. There is no stateful firewall support
+ # for fragmented packets.
+ grep -qE 'pass in on epair0b inet proto udp all keep state dnpipe\(1, 1\) .* Packets: 8 Bytes: 10168 States: 0 ' $rules ||
+ atf_fail "Fragmented packets not counted correctly"
+}
+
+dummynet_fragmented_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "too_many_fragments"
+ atf_add_test_case "v6"
+ atf_add_test_case "v6_route_to"
+ atf_add_test_case "mtu_diff"
+ atf_add_test_case "overreplace"
+ atf_add_test_case "overindex"
+ atf_add_test_case "overlimit"
+ atf_add_test_case "overhole"
+ atf_add_test_case "adjhole"
+ atf_add_test_case "reassemble"
+ atf_add_test_case "no_df"
+ atf_add_test_case "reassemble_slowpath"
+ atf_add_test_case "dummynet"
+ atf_add_test_case "dummynet_nat"
+ atf_add_test_case "dummynet_fragmented"
+}
diff --git a/tests/sys/netpfil/pf/get_state.sh b/tests/sys/netpfil/pf/get_state.sh
new file mode 100644
index 000000000000..eb2bc854c800
--- /dev/null
+++ b/tests/sys/netpfil/pf/get_state.sh
@@ -0,0 +1,80 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2021 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+common_dir=$(atf_get_srcdir)/../common
+
+atf_test_case "many" "cleanup"
+many_head()
+{
+ atf_set descr 'Test retrieving many states'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+many_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+ jexec alcatraz pfctl -e
+
+ pft_set_rules alcatraz "set timeout tcp.closed 60000" \
+ "pass in proto icmp" \
+ "pass in proto tcp"
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 -W 1 192.0.2.2
+
+ # Now syn flood to create many states
+ ${common_dir}/pft_synflood.py \
+ --sendif ${epair}a \
+ --to 192.0.2.2 \
+ --count 20000
+
+ count=$(time jexec alcatraz pfctl -ss | wc -l 2>time.txt)
+ echo "Found $count states in `cat time.txt`"
+ if [ $count -lt 20000 ];
+ then
+ atf_fail "Fail to retrieve states"
+ fi
+}
+
+many_cleanup()
+{
+ rm -f time.txt
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "many"
+}
diff --git a/tests/sys/netpfil/pf/header.py b/tests/sys/netpfil/pf/header.py
new file mode 100644
index 000000000000..a5e36bc85d14
--- /dev/null
+++ b/tests/sys/netpfil/pf/header.py
@@ -0,0 +1,216 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2025 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import pytest
+import re
+from utils import DelayedSend
+from atf_python.sys.net.tools import ToolsHelper
+from atf_python.sys.net.vnet import VnetTestTemplate
+
+class TestHeader(VnetTestTemplate):
+ REQUIRED_MODULES = [ "pf" ]
+ TOPOLOGY = {
+ "vnet1": {"ifaces": ["if1", "if2"]},
+ "vnet2": {"ifaces": ["if1", "if2"]},
+ "if1": {"prefixes4": [("192.0.2.2/24", "192.0.2.1/24")]},
+ "if2": {"prefixes4": [("198.51.100.1/24", "198.51.100.2/24")]},
+ }
+
+ def vnet2_handler(self, vnet):
+ ToolsHelper.print_output("/sbin/sysctl net.inet.ip.forwarding=1")
+ ToolsHelper.print_output("/usr/sbin/arp -s 198.51.100.3 00:01:02:03:04:05")
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.print_output("/sbin/pfctl -x loud")
+ ToolsHelper.pf_rules([
+ "pass",
+ ])
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_too_many(self):
+ "Verify that we drop packets with silly numbers of headers."
+
+ sendif = self.vnet.iface_alias_map["if1"]
+ recvif = self.vnet.iface_alias_map["if2"].name
+ gw_mac = sendif.epairb.ether
+
+ ToolsHelper.print_output("/sbin/route add default 192.0.2.1")
+
+ # Import in the correct vnet, so at to not confuse Scapy
+ import scapy.all as sp
+
+ # Sanity check, ensure we get replies to normal ping
+ pkt = sp.Ether(dst=gw_mac) \
+ / sp.IP(dst="198.51.100.3") \
+ / sp.ICMP(type='echo-request')
+ s = DelayedSend(pkt, sendif.name)
+ reply = sp.sniff(iface=recvif, timeout=3)
+ print(reply)
+
+ found = False
+ for r in reply:
+ r.show()
+
+ icmp = r.getlayer(sp.ICMP)
+ if not icmp:
+ continue
+ assert icmp.type == 8 # 'echo-request'
+ found = True
+ assert found
+
+ # Up to 19 AH headers will pass
+ pkt = sp.Ether(dst=gw_mac) \
+ / sp.IP(dst="198.51.100.3")
+ for i in range(0, 18):
+ pkt = pkt / sp.AH(nh=51, payloadlen=1)
+ pkt = pkt / sp.AH(nh=1, payloadlen=1) / sp.ICMP(type='echo-request')
+
+ s = DelayedSend(pkt, sendif.name)
+ reply = sp.sniff(iface=recvif, timeout=3)
+ print(reply)
+ found = False
+ for r in reply:
+ r.show()
+
+ ah = r.getlayer(sp.AH)
+ if not ah:
+ continue
+ found = True
+ assert found
+
+ # But more will get dropped
+ pkt = sp.Ether(dst=gw_mac) \
+ / sp.IP(dst="198.51.100.3")
+ for i in range(0, 19):
+ pkt = pkt / sp.AH(nh=51, payloadlen=1)
+ pkt = pkt / sp.AH(nh=1, payloadlen=1) / sp.ICMP(type='echo-request')
+
+ s = DelayedSend(pkt, sendif.name)
+ reply = sp.sniff(iface=recvif, timeout=3)
+ print(reply)
+
+ found = False
+ for r in reply:
+ r.show()
+
+ ah = r.getlayer(sp.AH)
+ if not ah:
+ continue
+ found = True
+ assert not found
+
+class TestHeader6(VnetTestTemplate):
+ REQUIRED_MODULES = [ "pf" ]
+ SKIP_MODULES = [ "ipfilter" ]
+ TOPOLOGY = {
+ "vnet1": {"ifaces": ["if1", "if2"]},
+ "vnet2": {"ifaces": ["if1", "if2"]},
+ "if1": {"prefixes6": [("2001:db8::2/64", "2001:db8::1/64")]},
+ "if2": {"prefixes6": [("2001:db8:1::2/64", "2001:db8:1::1/64")]},
+ }
+
+ def vnet2_handler(self, vnet):
+ ToolsHelper.print_output("/sbin/sysctl net.inet6.ip6.forwarding=1")
+ ToolsHelper.print_output("/usr/sbin/ndp -s 2001:db8:1::3 00:01:02:03:04:05")
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.print_output("/sbin/pfctl -x loud")
+ ToolsHelper.pf_rules([
+ "pass",
+ ])
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_too_many(self):
+ "Verify that we drop packets with silly numbers of headers."
+ ToolsHelper.print_output("/sbin/ifconfig")
+
+ sendif = self.vnet.iface_alias_map["if1"]
+ recvif = self.vnet.iface_alias_map["if2"].name
+ our_mac = sendif.ether
+ gw_mac = sendif.epairb.ether
+
+ ToolsHelper.print_output("/sbin/route -6 add default 2001:db8::1")
+
+ # Import in the correct vnet, so at to not confuse Scapy
+ import scapy.all as sp
+
+ # Sanity check, ensure we get replies to normal ping
+ pkt = sp.Ether(src=our_mac, dst=gw_mac) \
+ / sp.IPv6(src="2001:db8::2", dst="2001:db8:1::3") \
+ / sp.ICMPv6EchoRequest()
+ s = DelayedSend(pkt, sendif.name)
+ reply = sp.sniff(iface=recvif, timeout=3)
+ print(reply)
+
+ found = False
+ for r in reply:
+ r.show()
+
+ icmp = r.getlayer(sp.ICMPv6EchoRequest)
+ if not icmp:
+ continue
+ found = True
+ assert found
+
+ # Up to 19 AH headers will pass
+ pkt = sp.Ether(src=our_mac, dst=gw_mac) \
+ / sp.IPv6(src="2001:db8::2", dst="2001:db8:1::3")
+ for i in range(0, 18):
+ pkt = pkt / sp.AH(nh=51, payloadlen=1)
+ pkt = pkt / sp.AH(nh=58, payloadlen=1) / sp.ICMPv6EchoRequest()
+ s = DelayedSend(pkt, sendif.name)
+ reply = sp.sniff(iface=recvif, timeout=3)
+ print(reply)
+
+ found = False
+ for r in reply:
+ r.show()
+
+ ah = r.getlayer(sp.AH)
+ if not ah:
+ continue
+ found = True
+ assert found
+
+ # But more will get dropped
+ pkt = sp.Ether(src=our_mac, dst=gw_mac) \
+ / sp.IPv6(src="2001:db8::2", dst="2001:db8:1::3")
+ for i in range(0, 19):
+ pkt = pkt / sp.AH(nh=51, payloadlen=1)
+ pkt = pkt / sp.AH(nh=58, payloadlen=1) / sp.ICMPv6EchoRequest()
+ s = DelayedSend(pkt, sendif.name)
+ reply = sp.sniff(iface=recvif, timeout=3)
+ print(reply)
+
+ found = False
+ for r in reply:
+ r.show()
+
+ ah = r.getlayer(sp.AH)
+ if not ah:
+ continue
+ found = True
+ assert not found
diff --git a/tests/sys/netpfil/pf/icmp.py b/tests/sys/netpfil/pf/icmp.py
new file mode 100644
index 000000000000..c5e945d60e99
--- /dev/null
+++ b/tests/sys/netpfil/pf/icmp.py
@@ -0,0 +1,269 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import pytest
+import subprocess
+import re
+from atf_python.sys.net.tools import ToolsHelper
+from atf_python.sys.net.vnet import VnetTestTemplate
+
+def check(cmd):
+ ps = subprocess.Popen(cmd, shell=True)
+ ret = ps.wait()
+ if ret != 0:
+ raise Exception("Command %s returned %d" % (cmd, ret))
+
+class TestICMP(VnetTestTemplate):
+ REQUIRED_MODULES = [ "pf" ]
+ TOPOLOGY = {
+ "vnet1": {"ifaces": ["if1"]},
+ "vnet2": {"ifaces": ["if1", "if2"]},
+ "vnet3": {"ifaces": ["if2"]},
+ "if1": {"prefixes4": [("192.0.2.2/24", "192.0.2.1/24")]},
+ "if2": {"prefixes4": [("198.51.100.1/24", "198.51.100.2/24")]},
+ }
+
+ def vnet2_handler(self, vnet):
+ ifname = vnet.iface_alias_map["if1"].name
+ if2name = vnet.iface_alias_map["if2"].name
+
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.pf_rules([
+ "set reassemble yes",
+ "set state-policy if-bound",
+ "block",
+ "pass inet proto icmp icmp-type echoreq",
+ ])
+
+ ToolsHelper.print_output("/sbin/sysctl net.inet.ip.forwarding=1")
+ ToolsHelper.print_output("/sbin/pfctl -x loud")
+
+ ToolsHelper.print_output("/sbin/ifconfig %s mtu 1492" % if2name)
+
+ def vnet3_handler(self, vnet):
+ # Import in the correct vnet, so at to not confuse Scapy
+ import scapy.all as sp
+
+ ifname = vnet.iface_alias_map["if2"].name
+ ToolsHelper.print_output("/sbin/route add default 198.51.100.1")
+ ToolsHelper.print_output("/sbin/ifconfig %s inet alias 198.51.100.3/24" % ifname)
+ ToolsHelper.print_output("/sbin/ifconfig %s mtu 1492" % ifname)
+
+ def checkfn(packet):
+ icmp = packet.getlayer(sp.ICMP)
+ if not icmp:
+ return False
+
+ if icmp.type != 3:
+ return False
+
+ packet.show()
+ return True
+
+ sp.sniff(iface=ifname, stop_filter=checkfn)
+ vnet.pipe.send("Got ICMP destination unreachable packet")
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_inner_match(self):
+ vnet = self.vnet_map["vnet1"]
+ dst_vnet = self.vnet_map["vnet3"]
+ sendif = vnet.iface_alias_map["if1"]
+
+ our_mac = sendif.ether
+ dst_mac = sendif.epairb.ether
+
+ # Import in the correct vnet, so at to not confuse Scapy
+ import scapy.all as sp
+
+ ToolsHelper.print_output("/sbin/route add default 192.0.2.1")
+
+ # Sanity check
+ check("/sbin/ping -c 1 192.0.2.1")
+ check("/sbin/ping -c 1 198.51.100.1")
+ check("/sbin/ping -c 2 198.51.100.3")
+
+ # Establish a state
+ pkt = sp.Ether(src=our_mac, dst=dst_mac) \
+ / sp.IP(src="192.0.2.2", dst="198.51.100.2") \
+ / sp.ICMP(type='echo-request') \
+ / "PAYLOAD"
+ sp.sendp(pkt, sendif.name, verbose=False)
+
+ # Now try to pass an ICMP error message piggy-backing on that state, but
+ # use a different source address
+ pkt = sp.Ether(src=our_mac, dst=dst_mac) \
+ / sp.IP(src="192.0.2.2", dst="198.51.100.3") \
+ / sp.ICMP(type='dest-unreach') \
+ / sp.IP(src="198.51.100.2", dst="192.0.2.2") \
+ / sp.ICMP(type='echo-reply')
+ sp.sendp(pkt, sendif.name, verbose=False)
+
+ try:
+ rcvd = self.wait_object(dst_vnet.pipe, timeout=1)
+ if rcvd:
+ raise Exception(rcvd)
+ except TimeoutError as e:
+ # We expect the timeout here. It means we didn't get the destination
+ # unreachable packet in vnet3
+ pass
+
+ def check_icmp_echo(self, sp, payload_size):
+ packet = sp.IP(dst="198.51.100.2", flags="DF") \
+ / sp.ICMP(type='echo-request') \
+ / sp.raw(bytes.fromhex('f0') * payload_size)
+
+ p = sp.sr1(packet, timeout=3)
+ p.show()
+
+ ip = p.getlayer(sp.IP)
+ icmp = p.getlayer(sp.ICMP)
+ assert ip
+ assert icmp
+
+ if payload_size > 1464:
+ # Expect ICMP destination unreachable, fragmentation needed
+ assert ip.src == "192.0.2.1"
+ assert ip.dst == "192.0.2.2"
+ assert icmp.type == 3 # dest-unreach
+ assert icmp.code == 4
+ assert icmp.nexthopmtu == 1492
+ else:
+ # Expect echo reply
+ assert ip.src == "198.51.100.2"
+ assert ip.dst == "192.0.2.2"
+ assert icmp.type == 0 # "echo-reply"
+ assert icmp.code == 0
+
+ return
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_fragmentation_needed(self):
+ ToolsHelper.print_output("/sbin/route add default 192.0.2.1")
+
+ ToolsHelper.print_output("/sbin/ping -c 1 198.51.100.2")
+ ToolsHelper.print_output("/sbin/ping -c 1 -D -s 1472 198.51.100.2")
+
+ # Import in the correct vnet, so at to not confuse Scapy
+ import scapy.all as sp
+
+ self.check_icmp_echo(sp, 128)
+ self.check_icmp_echo(sp, 1464)
+ self.check_icmp_echo(sp, 1468)
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_truncated_opts(self):
+ ToolsHelper.print_output("/sbin/route add default 192.0.2.1")
+
+ # Import in the correct vnet, so at to not confuse Scapy
+ import scapy.all as sp
+
+ packet = sp.IP(dst="198.51.100.2", flags="DF") \
+ / sp.ICMP(type='dest-unreach', length=108) \
+ / sp.IP(src="198.51.100.2", dst="192.0.2.2", len=1000, \
+ ihl=(120 >> 2), options=[ \
+ sp.IPOption_Security(length=100)])
+ packet.show()
+ sp.sr1(packet, timeout=3)
+
+class TestICMP_NAT(VnetTestTemplate):
+ REQUIRED_MODULES = [ "pf" ]
+ TOPOLOGY = {
+ "vnet1": {"ifaces": ["if1"]},
+ "vnet2": {"ifaces": ["if1", "if2"]},
+ "vnet3": {"ifaces": ["if2"]},
+ "if1": {"prefixes4": [("192.0.2.2/24", "192.0.2.1/24")]},
+ "if2": {"prefixes4": [("198.51.100.1/24", "198.51.100.2/24")]},
+ }
+
+ def vnet2_handler(self, vnet):
+ ifname = vnet.iface_alias_map["if1"].name
+ if2name = vnet.iface_alias_map["if2"].name
+
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.pf_rules([
+ "set reassemble yes",
+ "set state-policy if-bound",
+ "nat on %s inet from 192.0.2.0/24 to any -> (%s)" % (if2name, if2name),
+ "block",
+ "pass inet proto icmp icmp-type echoreq",
+ ])
+
+ ToolsHelper.print_output("/sbin/sysctl net.inet.ip.forwarding=1")
+ ToolsHelper.print_output("/sbin/pfctl -x loud")
+
+ def vnet3_handler(self, vnet):
+ ifname = vnet.iface_alias_map["if2"].name
+ ToolsHelper.print_output("/sbin/ifconfig %s inet alias 198.51.100.3/24" % ifname)
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_id_conflict(self):
+ """
+ Test ICMP echo requests with the same ID from different clients.
+ Windows does this, and it can confuse pf.
+ """
+ ifname = self.vnet.iface_alias_map["if1"].name
+ ToolsHelper.print_output("/sbin/route add default 192.0.2.1")
+ ToolsHelper.print_output("/sbin/ifconfig %s inet alias 192.0.2.3/24" % ifname)
+
+ ToolsHelper.print_output("/sbin/ping -c 1 192.0.2.1")
+ ToolsHelper.print_output("/sbin/ping -c 1 198.51.100.1")
+ ToolsHelper.print_output("/sbin/ping -c 1 198.51.100.2")
+ ToolsHelper.print_output("/sbin/ping -c 1 198.51.100.3")
+
+ # Import in the correct vnet, so at to not confuse Scapy
+ import scapy.all as sp
+
+ # Address one
+ packet = sp.IP(src="192.0.2.2", dst="198.51.100.2", flags="DF") \
+ / sp.ICMP(type='echo-request', id=42) \
+ / sp.raw(bytes.fromhex('f0') * 16)
+
+ p = sp.sr1(packet, timeout=3)
+ p.show()
+ ip = p.getlayer(sp.IP)
+ icmp = p.getlayer(sp.ICMP)
+ assert ip
+ assert icmp
+ assert ip.dst == "192.0.2.2"
+ assert icmp.id == 42
+
+ # Address one
+ packet = sp.IP(src="192.0.2.3", dst="198.51.100.2", flags="DF") \
+ / sp.ICMP(type='echo-request', id=42) \
+ / sp.raw(bytes.fromhex('f0') * 16)
+
+ p = sp.sr1(packet, timeout=3)
+ p.show()
+ ip = p.getlayer(sp.IP)
+ icmp = p.getlayer(sp.ICMP)
+ assert ip
+ assert icmp
+ assert ip.dst == "192.0.2.3"
+ assert icmp.id == 42
diff --git a/tests/sys/netpfil/pf/icmp.sh b/tests/sys/netpfil/pf/icmp.sh
new file mode 100644
index 000000000000..279e3c7a29d5
--- /dev/null
+++ b/tests/sys/netpfil/pf/icmp.sh
@@ -0,0 +1,144 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2019 Kristof Provost <kp@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+common_dir=$(atf_get_srcdir)/../common
+
+atf_test_case "cve_2019_5598" "cleanup"
+cve_2019_5598_head()
+{
+ atf_set descr 'Test CVE-2019-5598'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+cve_2019_5598_body()
+{
+ pft_init
+
+ epair_in=$(vnet_mkepair)
+ epair_out=$(vnet_mkepair)
+ ifconfig ${epair_in}a 192.0.2.1/24 up
+ ifconfig ${epair_out}a up
+
+ vnet_mkjail alcatraz ${epair_in}b ${epair_out}b
+ jexec alcatraz ifconfig ${epair_in}b 192.0.2.2/24 up
+ jexec alcatraz ifconfig ${epair_out}b 198.51.100.2/24 up
+ jexec alcatraz sysctl net.inet.ip.forwarding=1
+ jexec alcatraz arp -s 198.51.100.3 00:01:02:03:04:05
+ jexec alcatraz route add default 198.51.100.3
+ route add -net 198.51.100.0/24 192.0.2.2
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz "block all" \
+ "pass in proto udp to 198.51.100.3 port 53" \
+ "pass out proto udp to 198.51.100.3 port 53"
+
+ atf_check -s exit:0 env PYTHONPATH=${common_dir} \
+ $(atf_get_srcdir)/CVE-2019-5598.py \
+ --sendif ${epair_in}a \
+ --recvif ${epair_out}a \
+ --src 192.0.2.1 \
+ --to 198.51.100.3
+}
+
+cve_2019_5598_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "ttl_exceeded" "cleanup"
+ttl_exceeded_head()
+{
+ atf_set descr 'Test that we correctly translate TTL exceeded back'
+ atf_set require.user root
+}
+
+ttl_exceeded_body()
+{
+ pft_init
+
+ epair_srv=$(vnet_mkepair)
+ epair_int=$(vnet_mkepair)
+ epair_cl=$(vnet_mkepair)
+
+ vnet_mkjail srv ${epair_srv}a
+ jexec srv ifconfig ${epair_srv}a 192.0.2.1/24 up
+ jexec srv route add default 192.0.2.2
+
+ vnet_mkjail int ${epair_srv}b ${epair_int}a
+ jexec int sysctl net.inet.ip.forwarding=1
+ jexec int ifconfig ${epair_srv}b 192.0.2.2/24 up
+ jexec int ifconfig ${epair_int}a 203.0.113.2/24 up
+
+ vnet_mkjail nat ${epair_int}b ${epair_cl}b
+ jexec nat ifconfig ${epair_int}b 203.0.113.1/24 up
+ jexec nat ifconfig ${epair_cl}b 198.51.100.2/24 up
+ jexec nat sysctl net.inet.ip.forwarding=1
+ jexec nat route add default 203.0.113.2
+
+ vnet_mkjail cl ${epair_cl}a
+ jexec cl ifconfig ${epair_cl}a 198.51.100.1/24 up
+ jexec cl route add default 198.51.100.2
+
+ jexec nat pfctl -e
+ pft_set_rules nat \
+ "nat on ${epair_int}b from 198.51.100.0/24 -> (${epair_int}b)" \
+ "block" \
+ "pass inet proto udp" \
+ "pass inet proto icmp icmp-type { echoreq }"
+
+ # Sanity checks
+ atf_check -s exit:0 -o ignore \
+ jexec cl ping -c 1 198.51.100.2
+ atf_check -s exit:0 -o ignore \
+ jexec cl ping -c 1 203.0.113.1
+ atf_check -s exit:0 -o ignore \
+ jexec cl ping -c 1 203.0.113.2
+ atf_check -s exit:0 -o ignore \
+ jexec cl ping -c 1 192.0.2.1
+
+ echo "UDP"
+ atf_check -s exit:0 -e ignore -o match:".*203.0.113.2.*" \
+ jexec cl traceroute 192.0.2.1
+ jexec nat pfctl -Fs
+
+ echo "ICMP"
+ atf_check -s exit:0 -e ignore -o match:".*203.0.113.2.*" \
+ jexec cl traceroute -I 192.0.2.1
+}
+
+ttl_exceeded_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "cve_2019_5598"
+ atf_add_test_case "ttl_exceeded"
+}
diff --git a/tests/sys/netpfil/pf/icmp6.sh b/tests/sys/netpfil/pf/icmp6.sh
new file mode 100644
index 000000000000..c55af906e3a6
--- /dev/null
+++ b/tests/sys/netpfil/pf/icmp6.sh
@@ -0,0 +1,204 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+common_dir=$(atf_get_srcdir)/../common
+
+atf_test_case "zero_id" "cleanup"
+zero_id_head()
+{
+ atf_set descr 'Test ICMPv6 echo with ID 0 keep being blocked'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+zero_id_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 2001:db8::1
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "set block-policy drop" \
+ "antispoof quick for { egress ${epair}b }" \
+ "block all" \
+ "pass out" \
+ "pass in quick inet6 proto IPV6-ICMP icmp6-type 135" \
+ "pass in quick inet6 proto IPV6-ICMP icmp6-type 136" \
+ "pass out quick inet6 proto IPV6 from self to any"
+
+ # Now we can't ping
+ atf_check -s exit:2 -o ignore \
+ ping -c 1 2001:db8::1
+
+ # Force neighbour discovery
+ ndp -d 2001:db8::1
+
+ # Verify that we don't confuse echo request with ID 0 for neighbour discovery
+ atf_check -s exit:1 -o ignore \
+ ${common_dir}/pft_ping.py \
+ --sendif ${epair}a \
+ --to 2001:db8::1 \
+ --replyif ${epair}a
+
+ jexec alcatraz pfctl -ss -vv
+ jexec alcatraz pfctl -sr -vv
+}
+
+zero_id_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "ttl_exceeded" "cleanup"
+ttl_exceeded_head()
+{
+ atf_set descr 'Test that we correctly translate TTL exceeded back'
+ atf_set require.user root
+}
+
+ttl_exceeded_body()
+{
+ pft_init
+
+ epair_srv=$(vnet_mkepair)
+ epair_int=$(vnet_mkepair)
+ epair_cl=$(vnet_mkepair)
+
+ vnet_mkjail srv ${epair_srv}a
+ jexec srv ifconfig ${epair_srv}a inet6 2001:db8:1::1/64 no_dad up
+ jexec srv route add -6 default 2001:db8:1::2
+
+ vnet_mkjail int ${epair_srv}b ${epair_int}a
+ jexec int sysctl net.inet6.ip6.forwarding=1
+ jexec int ifconfig ${epair_srv}b inet6 2001:db8:1::2/64 no_dad up
+ jexec int ifconfig ${epair_int}a inet6 2001:db8:2::2/64 no_dad up
+
+ vnet_mkjail nat ${epair_int}b ${epair_cl}b
+ jexec nat ifconfig ${epair_int}b inet6 2001:db8:2::1 no_dad up
+ jexec nat ifconfig ${epair_cl}b inet6 2001:db8:3::2/64 no_dad up
+ jexec nat sysctl net.inet6.ip6.forwarding=1
+ jexec nat route add -6 default 2001:db8:2::2
+
+ vnet_mkjail cl ${epair_cl}a
+ jexec cl ifconfig ${epair_cl}a inet6 2001:db8:3::1/64 no_dad up
+ jexec cl route add -6 default 2001:db8:3::2
+
+ jexec nat pfctl -e
+ pft_set_rules nat \
+ "nat on ${epair_int}b from 2001:db8:3::/64 -> (${epair_int}b:0)" \
+ "block" \
+ "pass inet6 proto udp" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv, echoreq }"
+
+ # Sanity checks
+ atf_check -s exit:0 -o ignore \
+ jexec cl ping -c 1 2001:db8:3::2
+ atf_check -s exit:0 -o ignore \
+ jexec cl ping -c 1 2001:db8:2::1
+ atf_check -s exit:0 -o ignore \
+ jexec cl ping -c 1 2001:db8:2::2
+ atf_check -s exit:0 -o ignore \
+ jexec cl ping -c 1 2001:db8:1::1
+
+ echo "UDP"
+ atf_check -s exit:0 -e ignore -o match:".*2001:db8:2::2.*" \
+ jexec cl traceroute6 2001:db8:1::1
+ jexec nat pfctl -Fs
+
+ echo "ICMP"
+ atf_check -s exit:0 -e ignore -o match:".*2001:db8:2::2.*" \
+ jexec cl traceroute6 -I 2001:db8:1::1
+}
+
+ttl_exceeded_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "repeat" "cleanup"
+repeat_head()
+{
+ atf_set descr 'Ensure that repeated NDs work'
+ atf_set require.user root
+ atf_set require.progs ndisc6
+}
+
+repeat_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 2001:db8::1
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "block all" \
+ "pass quick inet6 proto ipv6-icmp all icmp6-type neighbrsol keep state (if-bound) ridentifier 1000000107"
+
+ jexec alcatraz pfctl -x loud
+ ndisc6 -m -n -r 1 2001:db8::1 ${epair}a
+ jexec alcatraz pfctl -ss -vv
+
+ atf_check -s exit:0 -o ignore \
+ ndisc6 -m -n -r 1 2001:db8::1 ${epair}a
+ jexec alcatraz pfctl -ss -vv
+ atf_check -s exit:0 -o ignore \
+ ndisc6 -m -n -r 1 2001:db8::1 ${epair}a
+ jexec alcatraz pfctl -ss -vv
+ atf_check -s exit:0 -o ignore \
+ ndisc6 -m -n -r 1 2001:db8::1 ${epair}a
+ jexec alcatraz pfctl -ss -vv
+}
+
+repeat_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "zero_id"
+ atf_add_test_case "ttl_exceeded"
+ atf_add_test_case "repeat"
+}
diff --git a/tests/sys/netpfil/pf/if_enc.sh b/tests/sys/netpfil/pf/if_enc.sh
new file mode 100644
index 000000000000..40090b175470
--- /dev/null
+++ b/tests/sys/netpfil/pf/if_enc.sh
@@ -0,0 +1,178 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 Igor Ostapenko <pm@igoro.pro>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+#
+# The following network is used as a base for testing.
+#
+#
+# ${awan}b |----------| ${bwan}b
+# 2.0.0.1 | host wan | 3.0.0.1
+# .---->| Internet |<----.
+# A WAN | |----------| | B WAN
+# | |
+# Office A side | | Office B side
+# | ${awan}a ${bwan}a |
+# v 2.0.0.22 3.0.0.33 v
+# ${alan}b |----------| |----------| ${blan}b
+# 1.0.0.1 | host agw | | host bgw | 4.0.0.1
+# .----------->| gateway | < IPsec > | gateway |<-----------.
+# | A LAN |----------| tunnel |----------| B LAN |
+# | |
+# | |
+# | ${alan}a ${blan}a |
+# v 1.0.0.11 4.0.0.44 v
+# |----------| |----------|
+# | host a | | host b |
+# | client | | client |
+# |----------| |----------|
+#
+#
+# There is routing between office A clients and office B ones. The traffic is
+# encrypted, i.e. host wan should see IPsec flow (ESP packets).
+#
+
+ipsec_init()
+{
+ if ! sysctl -q kern.features.ipsec >/dev/null ; then
+ atf_skip "This test requires ipsec"
+ fi
+}
+
+if_enc_init()
+{
+ ipsec_init
+ if ! kldstat -q -m if_enc; then
+ atf_skip "This test requires if_enc"
+ fi
+}
+
+build_test_network()
+{
+ alan=$(vnet_mkepair)
+ awan=$(vnet_mkepair)
+ bwan=$(vnet_mkepair)
+ blan=$(vnet_mkepair)
+
+ # host a
+ vnet_mkjail a ${alan}a
+ jexec a ifconfig ${alan}a 1.0.0.11/24 up
+ jexec a route add default 1.0.0.1
+
+ # host agw
+ vnet_mkjail agw ${alan}b ${awan}a
+ jexec agw ifconfig ${alan}b 1.0.0.1/24 up
+ jexec agw ifconfig ${awan}a 2.0.0.22/24 up
+ jexec agw route add default 2.0.0.1
+ jexec agw sysctl net.inet.ip.forwarding=1
+
+ # host wan
+ vnet_mkjail wan ${awan}b ${bwan}b
+ jexec wan ifconfig ${awan}b 2.0.0.1/24 up
+ jexec wan ifconfig ${bwan}b 3.0.0.1/24 up
+ jexec wan sysctl net.inet.ip.forwarding=1
+
+ # host bgw
+ vnet_mkjail bgw ${bwan}a ${blan}b
+ jexec bgw ifconfig ${bwan}a 3.0.0.33/24 up
+ jexec bgw ifconfig ${blan}b 4.0.0.1/24 up
+ jexec bgw route add default 3.0.0.1
+ jexec bgw sysctl net.inet.ip.forwarding=1
+
+ # host b
+ vnet_mkjail b ${blan}a
+ jexec b ifconfig ${blan}a 4.0.0.44/24 up
+ jexec b route add default 4.0.0.1
+
+ # Office A VPN setup
+ echo '
+ spdadd 1.0.0.0/24 4.0.0.0/24 any -P out ipsec esp/tunnel/2.0.0.22-3.0.0.33/require;
+ spdadd 4.0.0.0/24 1.0.0.0/24 any -P in ipsec esp/tunnel/3.0.0.33-2.0.0.22/require;
+ add 2.0.0.22 3.0.0.33 esp 0x203 -E aes-gcm-16 "123456789012345678901234567890123456";
+ add 3.0.0.33 2.0.0.22 esp 0x302 -E aes-gcm-16 "123456789012345678901234567890123456";
+ ' | jexec agw setkey -c
+
+ # Office B VPN setup
+ echo '
+ spdadd 4.0.0.0/24 1.0.0.0/24 any -P out ipsec esp/tunnel/3.0.0.33-2.0.0.22/require;
+ spdadd 1.0.0.0/24 4.0.0.0/24 any -P in ipsec esp/tunnel/2.0.0.22-3.0.0.33/require;
+ add 2.0.0.22 3.0.0.33 esp 0x203 -E aes-gcm-16 "123456789012345678901234567890123456";
+ add 3.0.0.33 2.0.0.22 esp 0x302 -E aes-gcm-16 "123456789012345678901234567890123456";
+ ' | jexec bgw setkey -c
+}
+
+atf_test_case "ip4_pfil_in_after_stripping" "cleanup"
+ip4_pfil_in_after_stripping_head()
+{
+ atf_set descr 'Test that pf pulls up mbuf if m_len==0 after stripping the outer header'
+ atf_set require.user root
+ atf_set require.progs nc
+}
+ip4_pfil_in_after_stripping_body()
+{
+ pft_init
+ if_enc_init
+
+ build_test_network
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore jexec a ping -c3 4.0.0.44
+
+ # Configure port forwarding on host bgw
+ jexec bgw ifconfig enc0 up
+ jexec bgw sysctl net.inet.ipsec.filtertunnel=0
+ jexec bgw sysctl net.enc.in.ipsec_filter_mask=2 # after stripping
+ jexec bgw sysctl net.enc.out.ipsec_filter_mask=1 # before outer header
+ jexec bgw pfctl -e
+ pft_set_rules bgw \
+ "rdr on enc0 proto tcp to 4.0.0.1 port 666 -> 4.0.0.44" \
+ "pass"
+
+ # Prepare the catcher on host b
+ echo "unexpected" > ./receiver
+ jexec b nc -n4l -N 666 > ./receiver &
+ nc_pid=$!
+ sleep 1
+
+ # Poke it from host a to host bgw
+ spell="Ak Ohum Oktay Weez Barsoom."
+ echo $spell | jexec a nc -w3 4.0.0.1 666
+
+ # Expect it to hit host b instead
+ sleep 1 # let the catcher finish
+ jexec b kill -KILL $nc_pid # in a fail case the catcher may listen forever
+ atf_check_equal "$spell" "$(cat ./receiver)"
+}
+ip4_pfil_in_after_stripping_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "ip4_pfil_in_after_stripping"
+}
diff --git a/tests/sys/netpfil/pf/igmp.py b/tests/sys/netpfil/pf/igmp.py
new file mode 100644
index 000000000000..b339a2825082
--- /dev/null
+++ b/tests/sys/netpfil/pf/igmp.py
@@ -0,0 +1,95 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2025 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import pytest
+from utils import DelayedSend
+from atf_python.sys.net.tools import ToolsHelper
+from atf_python.sys.net.vnet import VnetTestTemplate
+
+class TestIGMP(VnetTestTemplate):
+ REQUIRED_MODULES = [ "pf" ]
+ TOPOLOGY = {
+ "vnet1": {"ifaces": ["if1"]},
+ "vnet2": {"ifaces": ["if1"]},
+ "if1": {"prefixes4": [("192.0.2.2/24", "192.0.2.1/24")]},
+ }
+
+ def vnet2_handler(self, vnet):
+ ifname = vnet.iface_alias_map["if1"].name
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.pf_rules([
+ "pass",
+ ])
+ ToolsHelper.print_output("/sbin/pfctl -x loud")
+ ToolsHelper.print_output("echo \"j 230.0.0.1 %s\ns 3600\nq\" | /usr/sbin/mtest" % ifname)
+
+ def find_igmp_reply(self, pkt, ifname):
+ pkt.show()
+ s = DelayedSend(pkt)
+
+ found = False
+ packets = self.sp.sniff(iface=ifname, timeout=5)
+ for r in packets:
+ r.show()
+ igmp = r.getlayer(self.sc.igmp.IGMP)
+ if not igmp:
+ continue
+ igmp.show()
+ if not igmp.gaddr == "230.0.0.1":
+ continue
+ found = True
+ return found
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_ip_opts(self):
+ """Verify that we allow IGMP packets with IP options"""
+ ifname = self.vnet.iface_alias_map["if1"].name
+
+ # Import in the correct vnet, so at to not confuse Scapy
+ import scapy.all as sp
+ import scapy.contrib as sc
+ import scapy.contrib.igmp
+ self.sp = sp
+ self.sc = sc
+
+ # We allow IGMP packets with the router alert option
+ pkt = sp.IP(dst="224.0.0.1%%%s" % ifname, ttl=1,
+ options=[sp.IPOption_Router_Alert()]) \
+ / sc.igmp.IGMP(type=0x11, mrcode=1)
+ assert self.find_igmp_reply(pkt, ifname)
+
+ # But not with other options
+ pkt = sp.IP(dst="224.0.0.1%%%s" % ifname, ttl=1,
+ options=[sp.IPOption_NOP()]) \
+ / sc.igmp.IGMP(type=0x11, mrcode=1)
+ assert not self.find_igmp_reply(pkt, ifname)
+
+ # Or with the wrong TTL
+ pkt = sp.IP(dst="224.0.0.1%%%s" % ifname, ttl=2,
+ options=[sp.IPOption_Router_Alert()]) \
+ / sc.igmp.IGMP(type=0x11, mrcode=1)
+ assert not self.find_igmp_reply(pkt, ifname)
diff --git a/tests/sys/netpfil/pf/ioctl/Makefile b/tests/sys/netpfil/pf/ioctl/Makefile
new file mode 100644
index 000000000000..6bcf48432d30
--- /dev/null
+++ b/tests/sys/netpfil/pf/ioctl/Makefile
@@ -0,0 +1,8 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/netpfil/pf/ioctl
+
+ATF_TESTS_C += \
+ validation
+
+.include <bsd.test.mk>
diff --git a/tests/sys/netpfil/pf/ioctl/validation.c b/tests/sys/netpfil/pf/ioctl/validation.c
new file mode 100644
index 000000000000..18fafe11c6ab
--- /dev/null
+++ b/tests/sys/netpfil/pf/ioctl/validation.c
@@ -0,0 +1,958 @@
+/*-
+ * Copyright (c) 2018 Kristof Provost <kp@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/module.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/pfvar.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#include <atf-c.h>
+
+static int dev;
+
+#define COMMON_HEAD() \
+ if (modfind("pf") == -1) \
+ atf_tc_skip("pf not loaded"); \
+ dev = open("/dev/pf", O_RDWR); \
+ if (dev == -1) \
+ atf_tc_skip("Failed to open /dev/pf");
+
+#define COMMON_CLEANUP() \
+ close(dev);
+
+static void
+common_init_tbl(struct pfr_table *tbl)
+{
+ bzero(tbl, sizeof(struct pfr_table));
+ strcpy(tbl->pfrt_anchor, "anchor");
+ strcpy(tbl->pfrt_name, "name");
+ tbl->pfrt_flags = 0;
+ tbl->pfrt_fback = 0;
+}
+
+ATF_TC_WITH_CLEANUP(addtables);
+ATF_TC_HEAD(addtables, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(addtables, tc)
+{
+ struct pfioc_table io;
+ struct pfr_table tbl;
+ struct pfr_table tbls[4];
+ int flags;
+
+ COMMON_HEAD();
+
+ flags = 0;
+
+ bzero(&io, sizeof(io));
+ io.pfrio_flags = flags;
+ io.pfrio_buffer = &tbl;
+ io.pfrio_esize = sizeof(tbl);
+
+ /* Negative size */
+ io.pfrio_size = -1;
+ if (ioctl(dev, DIOCRADDTABLES, &io) == 0)
+ atf_tc_fail("Request with size -1 succeeded");
+
+ /* Overly large size */
+ io.pfrio_size = 1 << 24;
+ if (ioctl(dev, DIOCRADDTABLES, &io) == 0)
+ atf_tc_fail("Request with size 1 << 24 succeeded");
+
+ /* NULL buffer */
+ io.pfrio_size = 1;
+ io.pfrio_buffer = NULL;
+ if (ioctl(dev, DIOCRADDTABLES, &io) == 0)
+ atf_tc_fail("Request with NULL buffer succeeded");
+
+ /* This can provoke a memory leak, see r331225. */
+ io.pfrio_size = 4;
+ for (int i = 0; i < io.pfrio_size; i++)
+ common_init_tbl(&tbls[i]);
+
+ io.pfrio_buffer = &tbls;
+ ioctl(dev, DIOCRADDTABLES, &io);
+}
+
+ATF_TC_CLEANUP(addtables, tc)
+{
+ COMMON_CLEANUP();
+}
+
+ATF_TC_WITH_CLEANUP(deltables);
+ATF_TC_HEAD(deltables, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(deltables, tc)
+{
+ struct pfioc_table io;
+ struct pfr_table tbl;
+ int flags;
+
+ COMMON_HEAD();
+
+ flags = 0;
+
+ bzero(&io, sizeof(io));
+ io.pfrio_flags = flags;
+ io.pfrio_buffer = &tbl;
+ io.pfrio_esize = sizeof(tbl);
+
+ /* Negative size */
+ io.pfrio_size = -1;
+ if (ioctl(dev, DIOCRDELTABLES, &io) == 0)
+ atf_tc_fail("Request with size -1 succeeded");
+
+ /* Overly large size */
+ io.pfrio_size = 1 << 24;
+ if (ioctl(dev, DIOCRDELTABLES, &io) == 0)
+ atf_tc_fail("Request with size 1 << 24 succeeded");
+
+ /* NULL buffer */
+ io.pfrio_size = 1;
+ io.pfrio_buffer = NULL;
+ if (ioctl(dev, DIOCRDELTABLES, &io) == 0)
+ atf_tc_fail("Request with NULL buffer succeeded");
+}
+
+ATF_TC_CLEANUP(deltables, tc)
+{
+ COMMON_CLEANUP();
+}
+
+ATF_TC_WITH_CLEANUP(gettables);
+ATF_TC_HEAD(gettables, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(gettables, tc)
+{
+ struct pfioc_table io;
+ struct pfr_table tbl;
+ int flags;
+
+ COMMON_HEAD();
+
+ flags = 0;
+
+ bzero(&io, sizeof(io));
+ io.pfrio_flags = flags;
+ io.pfrio_buffer = &tbl;
+ io.pfrio_esize = sizeof(tbl);
+
+ /* Negative size. This will succeed, because the kernel will not copy
+ * tables than it has. */
+ io.pfrio_size = -1;
+ if (ioctl(dev, DIOCRGETTABLES, &io) != 0)
+ atf_tc_fail("Request with size -1 failed");
+
+ /* Overly large size. See above. */
+ io.pfrio_size = 1 << 24;
+ if (ioctl(dev, DIOCRGETTABLES, &io) != 0)
+ atf_tc_fail("Request with size 1 << 24 failed");
+}
+
+ATF_TC_CLEANUP(gettables, tc)
+{
+ COMMON_CLEANUP();
+}
+
+ATF_TC_WITH_CLEANUP(gettstats);
+ATF_TC_HEAD(gettstats, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(gettstats, tc)
+{
+ struct pfioc_table io;
+ struct pfr_tstats stats;
+ int flags;
+
+ COMMON_HEAD();
+
+ flags = 0;
+
+ bzero(&io, sizeof(io));
+ io.pfrio_flags = flags;
+ io.pfrio_buffer = &stats;
+ io.pfrio_esize = sizeof(stats);
+
+ /* Negative size. This will succeed, because the kernel will not copy
+ * tables than it has. */
+ io.pfrio_size = -1;
+ if (ioctl(dev, DIOCRGETTSTATS, &io) != 0)
+ atf_tc_fail("Request with size -1 failed");
+
+ /* Overly large size. See above. */
+ io.pfrio_size = 1 << 24;
+ if (ioctl(dev, DIOCRGETTSTATS, &io) != 0)
+ atf_tc_fail("Request with size 1 << 24 failed");
+}
+
+ATF_TC_CLEANUP(gettstats, tc)
+{
+ COMMON_CLEANUP();
+}
+
+ATF_TC_WITH_CLEANUP(clrtstats);
+ATF_TC_HEAD(clrtstats, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(clrtstats, tc)
+{
+ struct pfioc_table io;
+ struct pfr_table tbl;
+ int flags;
+
+ COMMON_HEAD();
+
+ flags = 0;
+
+ common_init_tbl(&tbl);
+
+ bzero(&io, sizeof(io));
+ io.pfrio_flags = flags;
+ io.pfrio_buffer = &tbl;
+ io.pfrio_esize = sizeof(tbl);
+
+ /* Negative size. This will succeed, because the kernel will not copy
+ * tables than it has. */
+ io.pfrio_size = -1;
+ if (ioctl(dev, DIOCRCLRTSTATS, &io) != 0)
+ atf_tc_fail("Request with size -1 failed ");
+
+ /* Overly large size. See above. */
+ io.pfrio_size = 1 << 24;
+ if (ioctl(dev, DIOCRCLRTSTATS, &io) != 0)
+ atf_tc_fail("Request with size 1 << 24 failed");
+
+ io.pfrio_size = sizeof(tbl);
+ io.pfrio_buffer = NULL;
+ if (ioctl(dev, DIOCRCLRTSTATS, &io) == 0)
+ atf_tc_fail("Request with NULL buffer succeeded");
+}
+
+ATF_TC_CLEANUP(clrtstats, tc)
+{
+ COMMON_CLEANUP();
+}
+
+ATF_TC_WITH_CLEANUP(settflags);
+ATF_TC_HEAD(settflags, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(settflags, tc)
+{
+ struct pfioc_table io;
+ struct pfr_table tbl;
+ int flags;
+
+ COMMON_HEAD();
+
+ flags = 0;
+
+ common_init_tbl(&tbl);
+
+ bzero(&io, sizeof(io));
+ io.pfrio_flags = flags;
+ io.pfrio_buffer = &tbl;
+ io.pfrio_esize = sizeof(tbl);
+
+ /* Negative size. This will succeed, because the kernel will not copy
+ * tables than it has. */
+ io.pfrio_size = -1;
+ if (ioctl(dev, DIOCRSETTFLAGS, &io) != 0)
+ atf_tc_fail("Request with size -1 failed");
+
+ /* Overly large size. See above. */
+ io.pfrio_size = 1 << 28;
+ if (ioctl(dev, DIOCRSETTFLAGS, &io) != 0)
+ atf_tc_fail("Request with size 1 << 24 failed");
+
+ /* NULL buffer */
+ io.pfrio_buffer = NULL;
+ if (ioctl(dev, DIOCRSETTFLAGS, &io) != -1)
+ atf_tc_fail("Request with NULL buffer succeeded");
+}
+
+ATF_TC_CLEANUP(settflags, tc)
+{
+ COMMON_CLEANUP();
+}
+
+ATF_TC_WITH_CLEANUP(addaddrs);
+ATF_TC_HEAD(addaddrs, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(addaddrs, tc)
+{
+ struct pfioc_table io;
+ struct pfr_addr addr;
+
+ COMMON_HEAD();
+
+ bzero(&addr, sizeof(addr));
+ bzero(&io, sizeof(io));
+ io.pfrio_flags = 0;
+ io.pfrio_buffer = &addr;
+ io.pfrio_esize = sizeof(addr);
+
+ /* Negative size. */
+ io.pfrio_size = -1;
+ if (ioctl(dev, DIOCRADDADDRS, &io) == 0)
+ atf_tc_fail("Request with size -1 succeeded");
+
+ /* Overly large size. */
+ io.pfrio_size = 1 << 28;
+ if (ioctl(dev, DIOCRADDADDRS, &io) == 0)
+ atf_tc_fail("Reuqest with size 1 << 28 failed");
+}
+
+ATF_TC_CLEANUP(addaddrs, tc)
+{
+ COMMON_CLEANUP();
+}
+
+ATF_TC_WITH_CLEANUP(deladdrs);
+ATF_TC_HEAD(deladdrs, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(deladdrs, tc)
+{
+ struct pfioc_table io;
+ struct pfr_addr addr;
+
+ COMMON_HEAD();
+
+ bzero(&addr, sizeof(addr));
+ bzero(&io, sizeof(io));
+ io.pfrio_flags = 0;
+ io.pfrio_buffer = &addr;
+ io.pfrio_esize = sizeof(addr);
+
+ /* Negative size. */
+ io.pfrio_size = -1;
+ if (ioctl(dev, DIOCRDELADDRS, &io) == 0)
+ atf_tc_fail("Request with size -1 succeeded");
+
+ /* Overly large size. */
+ io.pfrio_size = 1 << 28;
+ if (ioctl(dev, DIOCRDELADDRS, &io) == 0)
+ atf_tc_fail("Reuqest with size 1 << 28 failed");
+}
+
+ATF_TC_CLEANUP(deladdrs, tc)
+{
+ COMMON_CLEANUP();
+}
+
+ATF_TC_WITH_CLEANUP(setaddrs);
+ATF_TC_HEAD(setaddrs, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(setaddrs, tc)
+{
+ struct pfioc_table io;
+ struct pfr_addr addr;
+
+ COMMON_HEAD();
+
+ bzero(&addr, sizeof(addr));
+ bzero(&io, sizeof(io));
+ io.pfrio_flags = 0;
+ io.pfrio_buffer = &addr;
+ io.pfrio_esize = sizeof(addr);
+
+ /* Negative size. */
+ io.pfrio_size = -1;
+ if (ioctl(dev, DIOCRSETADDRS, &io) == 0)
+ atf_tc_fail("Request with size -1 succeeded");
+
+ /* Overly large size. */
+ io.pfrio_size = 1 << 28;
+ if (ioctl(dev, DIOCRSETADDRS, &io) == 0)
+ atf_tc_fail("Reuqest with size 1 << 28 failed");
+}
+
+ATF_TC_CLEANUP(setaddrs, tc)
+{
+ COMMON_CLEANUP();
+}
+
+ATF_TC_WITH_CLEANUP(getaddrs);
+ATF_TC_HEAD(getaddrs, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(getaddrs, tc)
+{
+ struct pfioc_table io;
+ struct pfr_addr addr;
+
+ COMMON_HEAD();
+
+ bzero(&addr, sizeof(addr));
+ bzero(&io, sizeof(io));
+ io.pfrio_flags = 0;
+ io.pfrio_buffer = &addr;
+ io.pfrio_esize = sizeof(addr);
+
+ common_init_tbl(&io.pfrio_table);
+
+ /* Negative size. */
+ io.pfrio_size = -1;
+ if (ioctl(dev, DIOCRGETADDRS, &io) == 0)
+ atf_tc_fail("Request with size -1 succeeded");
+
+ /* Overly large size. */
+ io.pfrio_size = 1 << 24;
+ if (ioctl(dev, DIOCRGETADDRS, &io) == 0)
+ atf_tc_fail("Request with size 1 << 24 failed");
+}
+
+ATF_TC_CLEANUP(getaddrs, tc)
+{
+ COMMON_CLEANUP();
+}
+
+ATF_TC_WITH_CLEANUP(getastats);
+ATF_TC_HEAD(getastats, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(getastats, tc)
+{
+ struct pfioc_table io;
+ struct pfr_astats astats;
+
+ COMMON_HEAD();
+
+ bzero(&astats, sizeof(astats));
+ bzero(&io, sizeof(io));
+ io.pfrio_flags = 0;
+ io.pfrio_buffer = &astats;
+ io.pfrio_esize = sizeof(astats);
+
+ common_init_tbl(&io.pfrio_table);
+
+ /* Negative size. */
+ io.pfrio_size = -1;
+ if (ioctl(dev, DIOCRGETASTATS, &io) == 0)
+ atf_tc_fail("Request with size -1 succeeded");
+
+ /* Overly large size. */
+ io.pfrio_size = 1 << 24;
+ if (ioctl(dev, DIOCRGETASTATS, &io) == 0)
+ atf_tc_fail("Request with size 1 << 24 failed");
+}
+
+ATF_TC_CLEANUP(getastats, tc)
+{
+ COMMON_CLEANUP();
+}
+
+ATF_TC_WITH_CLEANUP(clrastats);
+ATF_TC_HEAD(clrastats, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(clrastats, tc)
+{
+ struct pfioc_table io;
+ struct pfr_addr addr;
+
+ COMMON_HEAD();
+
+ bzero(&addr, sizeof(addr));
+ bzero(&io, sizeof(io));
+ io.pfrio_flags = 0;
+ io.pfrio_buffer = &addr;
+ io.pfrio_esize = sizeof(addr);
+
+ common_init_tbl(&io.pfrio_table);
+
+ /* Negative size. */
+ io.pfrio_size = -1;
+ if (ioctl(dev, DIOCRCLRASTATS, &io) == 0)
+ atf_tc_fail("Request with size -1 succeeded");
+
+ /* Overly large size. */
+ io.pfrio_size = 1 << 24;
+ if (ioctl(dev, DIOCRCLRASTATS, &io) == 0)
+ atf_tc_fail("Request with size 1 << 24 failed");
+}
+
+ATF_TC_CLEANUP(clrastats, tc)
+{
+ COMMON_CLEANUP();
+}
+
+ATF_TC_WITH_CLEANUP(tstaddrs);
+ATF_TC_HEAD(tstaddrs, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(tstaddrs, tc)
+{
+ struct pfioc_table io;
+ struct pfr_addr addr;
+
+ COMMON_HEAD();
+
+ bzero(&addr, sizeof(addr));
+ bzero(&io, sizeof(io));
+ io.pfrio_flags = 0;
+ io.pfrio_buffer = &addr;
+ io.pfrio_esize = sizeof(addr);
+
+ common_init_tbl(&io.pfrio_table);
+
+ /* Negative size. */
+ io.pfrio_size = -1;
+ if (ioctl(dev, DIOCRTSTADDRS, &io) == 0)
+ atf_tc_fail("Request with size -1 succeeded");
+
+ /* Overly large size. */
+ io.pfrio_size = 1 << 24;
+ if (ioctl(dev, DIOCRTSTADDRS, &io) == 0)
+ atf_tc_fail("Request with size 1 << 24 failed");
+}
+
+ATF_TC_CLEANUP(tstaddrs, tc)
+{
+ COMMON_CLEANUP();
+}
+
+ATF_TC_WITH_CLEANUP(inadefine);
+ATF_TC_HEAD(inadefine, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(inadefine, tc)
+{
+ struct pfioc_table io;
+ struct pfr_addr addr;
+
+ COMMON_HEAD();
+
+ bzero(&addr, sizeof(addr));
+ bzero(&io, sizeof(io));
+ io.pfrio_flags = 0;
+ io.pfrio_buffer = &addr;
+ io.pfrio_esize = sizeof(addr);
+
+ common_init_tbl(&io.pfrio_table);
+
+ /* Negative size. */
+ io.pfrio_size = -1;
+ if (ioctl(dev, DIOCRINADEFINE, &io) == 0)
+ atf_tc_fail("Request with size -1 succeeded");
+
+ /* Overly large size. */
+ io.pfrio_size = 1 << 24;
+ if (ioctl(dev, DIOCRINADEFINE, &io) == 0)
+ atf_tc_fail("Request with size 1 << 24 failed");
+}
+
+ATF_TC_CLEANUP(inadefine, tc)
+{
+ COMMON_CLEANUP();
+}
+
+ATF_TC_WITH_CLEANUP(igetifaces);
+ATF_TC_HEAD(igetifaces, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(igetifaces, tc)
+{
+ struct pfioc_iface io;
+ struct pfi_kif kif;
+
+ COMMON_HEAD();
+
+ bzero(&io, sizeof(io));
+ io.pfiio_flags = 0;
+ io.pfiio_buffer = &kif;
+ io.pfiio_esize = sizeof(kif);
+
+ /* Negative size */
+ io.pfiio_size = -1;
+ if (ioctl(dev, DIOCIGETIFACES, &io) == 0)
+ atf_tc_fail("request with size -1 succeeded");
+
+ /* Overflow size */
+ io.pfiio_size = 1 << 31;
+ if (ioctl(dev, DIOCIGETIFACES, &io) == 0)
+ atf_tc_fail("request with size 1 << 31 succeeded");
+}
+
+ATF_TC_CLEANUP(igetifaces, tc)
+{
+ COMMON_CLEANUP();
+}
+
+ATF_TC_WITH_CLEANUP(cxbegin);
+ATF_TC_HEAD(cxbegin, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(cxbegin, tc)
+{
+ struct pfioc_trans io;
+ struct pfioc_trans_e ioe;
+
+ COMMON_HEAD();
+
+ bzero(&io, sizeof(io));
+ io.esize = sizeof(ioe);
+ io.array = &ioe;
+
+ /* Negative size */
+ io.size = -1;
+ if (ioctl(dev, DIOCXBEGIN, &io) == 0)
+ atf_tc_fail("request with size -1 succeeded");
+
+ /* Overflow size */
+ io.size = 1 << 30;
+ if (ioctl(dev, DIOCXBEGIN, &io) == 0)
+ atf_tc_fail("request with size 1 << 30 succeeded");
+
+ /* NULL buffer */
+ io.size = 1;
+ io.array = NULL;
+ if (ioctl(dev, DIOCXBEGIN, &io) == 0)
+ atf_tc_fail("request with size -1 succeeded");
+}
+
+ATF_TC_CLEANUP(cxbegin, tc)
+{
+ COMMON_CLEANUP();
+}
+
+ATF_TC_WITH_CLEANUP(cxrollback);
+ATF_TC_HEAD(cxrollback, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(cxrollback, tc)
+{
+ struct pfioc_trans io;
+ struct pfioc_trans_e ioe;
+
+ COMMON_HEAD();
+
+ bzero(&io, sizeof(io));
+ io.esize = sizeof(ioe);
+ io.array = &ioe;
+
+ /* Negative size */
+ io.size = -1;
+ if (ioctl(dev, DIOCXROLLBACK, &io) == 0)
+ atf_tc_fail("request with size -1 succeeded");
+
+ /* Overflow size */
+ io.size = 1 << 30;
+ if (ioctl(dev, DIOCXROLLBACK, &io) == 0)
+ atf_tc_fail("request with size 1 << 30 succeeded");
+
+ /* NULL buffer */
+ io.size = 1;
+ io.array = NULL;
+ if (ioctl(dev, DIOCXROLLBACK, &io) == 0)
+ atf_tc_fail("request with size -1 succeeded");
+}
+
+ATF_TC_CLEANUP(cxrollback, tc)
+{
+ COMMON_CLEANUP();
+}
+
+ATF_TC_WITH_CLEANUP(commit);
+ATF_TC_HEAD(commit, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(commit, tc)
+{
+ struct pfioc_trans io;
+ struct pfioc_trans_e ioe;
+
+ COMMON_HEAD();
+
+ bzero(&io, sizeof(io));
+ io.esize = sizeof(ioe);
+ io.array = &ioe;
+
+ /* Negative size */
+ io.size = -1;
+ if (ioctl(dev, DIOCXCOMMIT, &io) == 0)
+ atf_tc_fail("request with size -1 succeeded");
+
+ /* Overflow size */
+ io.size = 1 << 30;
+ if (ioctl(dev, DIOCXCOMMIT, &io) == 0)
+ atf_tc_fail("request with size 1 << 30 succeeded");
+
+ /* NULL buffer */
+ io.size = 1;
+ io.array = NULL;
+ if (ioctl(dev, DIOCXCOMMIT, &io) == 0)
+ atf_tc_fail("request with size -1 succeeded");
+}
+
+ATF_TC_CLEANUP(commit, tc)
+{
+ COMMON_CLEANUP();
+}
+
+ATF_TC_WITH_CLEANUP(getsrcnodes);
+ATF_TC_HEAD(getsrcnodes, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(getsrcnodes, tc)
+{
+ struct pfioc_src_nodes psn;
+
+ COMMON_HEAD();
+
+ bzero(&psn, sizeof(psn));
+
+ psn.psn_len = -1;
+ if (ioctl(dev, DIOCGETSRCNODES, &psn) != 0)
+ atf_tc_fail("request with size -1 failed");
+
+ psn.psn_len = 1 << 30;
+ if (ioctl(dev, DIOCGETSRCNODES, &psn) != 0)
+ atf_tc_fail("request with size << 30 failed");
+
+ psn.psn_len = 1 << 31;
+ if (ioctl(dev, DIOCGETSRCNODES, &psn) != 0)
+ atf_tc_fail("request with size << 30 failed");
+}
+
+ATF_TC_CLEANUP(getsrcnodes, tc)
+{
+ COMMON_CLEANUP();
+}
+
+ATF_TC_WITH_CLEANUP(tag);
+ATF_TC_HEAD(tag, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(tag, tc)
+{
+ struct pfioc_rule rule;
+
+ COMMON_HEAD();
+
+ memset(&rule, 0x42, sizeof(rule));
+
+ rule.ticket = 0;
+ rule.pool_ticket = 0;
+ rule.anchor[0] = 0;
+
+ rule.rule.return_icmp = 0;
+ bzero(&rule.rule.src, sizeof(rule.rule.src));
+ bzero(&rule.rule.dst, sizeof(rule.rule.dst));
+
+ rule.rule.ifname[0] = 0;
+ rule.rule.action = 0;
+ rule.rule.rtableid = 0;
+
+ rule.rule.tagname[0] = 0;
+
+ for (int i = 0; i < 10; i++)
+ ioctl(dev, DIOCADDRULE, &rule);
+}
+
+ATF_TC_CLEANUP(tag, tc)
+{
+ COMMON_CLEANUP();
+}
+
+ATF_TC_WITH_CLEANUP(rpool_mtx);
+ATF_TC_HEAD(rpool_mtx, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(rpool_mtx, tc)
+{
+ struct pfioc_rule rule;
+
+ COMMON_HEAD();
+
+ memset(&rule, 0, sizeof(rule));
+
+ rule.ticket = 0;
+ rule.pool_ticket = 0;
+ rule.anchor[0] = 0;
+
+ rule.rule.return_icmp = 0;
+ bzero(&rule.rule.src, sizeof(rule.rule.src));
+ bzero(&rule.rule.dst, sizeof(rule.rule.dst));
+
+ rule.rule.ifname[0] = 0;
+ rule.rule.action = 0;
+ rule.rule.rtableid = 0;
+
+ rule.rule.tagname[0] = 0;
+ rule.rule.action = 42;
+
+ ioctl(dev, DIOCADDRULE, &rule);
+}
+
+ATF_TC_CLEANUP(rpool_mtx, tc)
+{
+ COMMON_CLEANUP();
+}
+
+ATF_TC_WITH_CLEANUP(rpool_mtx2);
+ATF_TC_HEAD(rpool_mtx2, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(rpool_mtx2, tc)
+{
+ struct pfioc_rule rule;
+
+ COMMON_HEAD();
+
+ memset(&rule, 0, sizeof(rule));
+
+ rule.pool_ticket = 1000000;
+ rule.action = PF_CHANGE_ADD_HEAD;
+ rule.rule.af = AF_INET;
+
+ ioctl(dev, DIOCCHANGERULE, &rule);
+}
+
+ATF_TC_CLEANUP(rpool_mtx2, tc)
+{
+ COMMON_CLEANUP();
+}
+
+ATF_TC_WITH_CLEANUP(natlook);
+ATF_TC_HEAD(natlook, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(natlook, tc)
+{
+ struct pfioc_natlook nl = { 0 };
+
+ COMMON_HEAD();
+
+ nl.af = AF_INET;
+ nl.proto = IPPROTO_ICMP;
+ nl.saddr.v4.s_addr = 0x01020304;
+ nl.daddr.v4.s_addr = 0x05060708;
+
+ /* Invalid direction */
+ nl.direction = 42;
+
+ ATF_CHECK_ERRNO(EINVAL, ioctl(dev, DIOCNATLOOK, &nl) == -1);
+
+ /* Invalid af */
+ nl.direction = PF_IN;
+ nl.af = 99;
+
+ ATF_CHECK_ERRNO(EAFNOSUPPORT, ioctl(dev, DIOCNATLOOK, &nl) == -1);
+}
+
+ATF_TC_CLEANUP(natlook, tc)
+{
+ COMMON_CLEANUP();
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, addtables);
+ ATF_TP_ADD_TC(tp, deltables);
+ ATF_TP_ADD_TC(tp, gettables);
+ ATF_TP_ADD_TC(tp, getastats);
+ ATF_TP_ADD_TC(tp, gettstats);
+ ATF_TP_ADD_TC(tp, clrtstats);
+ ATF_TP_ADD_TC(tp, settflags);
+ ATF_TP_ADD_TC(tp, addaddrs);
+ ATF_TP_ADD_TC(tp, deladdrs);
+ ATF_TP_ADD_TC(tp, setaddrs);
+ ATF_TP_ADD_TC(tp, getaddrs);
+ ATF_TP_ADD_TC(tp, clrastats);
+ ATF_TP_ADD_TC(tp, tstaddrs);
+ ATF_TP_ADD_TC(tp, inadefine);
+ ATF_TP_ADD_TC(tp, igetifaces);
+ ATF_TP_ADD_TC(tp, cxbegin);
+ ATF_TP_ADD_TC(tp, cxrollback);
+ ATF_TP_ADD_TC(tp, commit);
+ ATF_TP_ADD_TC(tp, getsrcnodes);
+ ATF_TP_ADD_TC(tp, tag);
+ ATF_TP_ADD_TC(tp, rpool_mtx);
+ ATF_TP_ADD_TC(tp, rpool_mtx2);
+ ATF_TP_ADD_TC(tp, natlook);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/netpfil/pf/killstate.sh b/tests/sys/netpfil/pf/killstate.sh
new file mode 100644
index 000000000000..0d98db822535
--- /dev/null
+++ b/tests/sys/netpfil/pf/killstate.sh
@@ -0,0 +1,710 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2021 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+common_dir=$(atf_get_srcdir)/../common
+
+find_state()
+{
+ jail=${1:-alcatraz}
+ ip=${2:-192.0.2.2}
+
+ jexec ${jail} pfctl -ss | grep icmp | grep ${ip}
+}
+
+find_state_v6()
+{
+ jexec alcatraz pfctl -ss | grep icmp | grep 2001:db8::2
+}
+
+
+atf_test_case "v4" "cleanup"
+v4_head()
+{
+ atf_set descr 'Test killing states by IPv4 address'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+v4_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+ jexec alcatraz pfctl -e
+
+ pft_set_rules alcatraz "block all" \
+ "pass in proto icmp" \
+ "set skip on lo"
+
+ # Sanity check & establish state
+ atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \
+ --sendif ${epair}a \
+ --to 192.0.2.2 \
+ --replyif ${epair}a
+
+ # Change rules to now deny the ICMP traffic
+ pft_set_rules noflush alcatraz "block all"
+ if ! find_state;
+ then
+ atf_fail "Setting new rules removed the state."
+ fi
+
+ # Killing with the wrong IP doesn't affect our state
+ jexec alcatraz pfctl -k 192.0.2.3
+ if ! find_state;
+ then
+ atf_fail "Killing with the wrong IP removed our state."
+ fi
+
+ # Killing with one correct address and one incorrect doesn't kill the state
+ jexec alcatraz pfctl -k 192.0.2.1 -k 192.0.2.3
+ if ! find_state;
+ then
+ atf_fail "Killing with one wrong IP removed our state."
+ fi
+
+ # Killing with correct address does remove the state
+ jexec alcatraz pfctl -k 192.0.2.1
+ if find_state;
+ then
+ atf_fail "Killing with the correct IP did not remove our state."
+ fi
+}
+
+v4_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "v6" "cleanup"
+v6_head()
+{
+ atf_set descr 'Test killing states by IPv6 address'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+v6_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a inet6 2001:db8::1/64 up no_dad
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b inet6 2001:db8::2/64 up no_dad
+ jexec alcatraz pfctl -e
+
+ pft_set_rules alcatraz "block all" \
+ "pass in proto icmp6" \
+ "set skip on lo"
+
+ # Sanity check & establish state
+ atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \
+ --sendif ${epair}a \
+ --to 2001:db8::2 \
+ --replyif ${epair}a
+
+ # Change rules to now deny the ICMP traffic
+ pft_set_rules noflush alcatraz "block all"
+ if ! find_state_v6;
+ then
+ atf_fail "Setting new rules removed the state."
+ fi
+
+ # Killing with the wrong IP doesn't affect our state
+ jexec alcatraz pfctl -k 2001:db8::3
+ if ! find_state_v6;
+ then
+ atf_fail "Killing with the wrong IP removed our state."
+ fi
+
+ # Killing with one correct address and one incorrect doesn't kill the state
+ jexec alcatraz pfctl -k 2001:db8::1 -k 2001:db8::3
+ if ! find_state_v6;
+ then
+ atf_fail "Killing with one wrong IP removed our state."
+ fi
+
+ # Killing with correct address does remove the state
+ jexec alcatraz pfctl -k 2001:db8::1
+ if find_state_v6;
+ then
+ atf_fail "Killing with the correct IP did not remove our state."
+ fi
+}
+
+v6_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "label" "cleanup"
+label_head()
+{
+ atf_set descr 'Test killing states by label'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+label_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+ jexec alcatraz pfctl -e
+
+ pft_set_rules alcatraz "block all" \
+ "pass in proto tcp label bar" \
+ "pass in proto icmp label foo" \
+ "set skip on lo"
+
+ # Sanity check & establish state
+ atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \
+ --sendif ${epair}a \
+ --to 192.0.2.2 \
+ --replyif ${epair}a
+
+ # Change rules to now deny the ICMP traffic
+ pft_set_rules noflush alcatraz "block all"
+ if ! find_state;
+ then
+ atf_fail "Setting new rules removed the state."
+ fi
+
+ # Killing a label on a different rules keeps the state
+ jexec alcatraz pfctl -k label -k bar
+ if ! find_state;
+ then
+ atf_fail "Killing a different label removed the state."
+ fi
+
+ # Killing a non-existing label keeps the state
+ jexec alcatraz pfctl -k label -k baz
+ if ! find_state;
+ then
+ atf_fail "Killing a non-existing label removed the state."
+ fi
+
+ # Killing the correct label kills the state
+ jexec alcatraz pfctl -k label -k foo
+ if find_state;
+ then
+ atf_fail "Killing the state did not remove it."
+ fi
+}
+
+label_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "multilabel" "cleanup"
+multilabel_head()
+{
+ atf_set descr 'Test killing states with multiple labels by label'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+multilabel_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+ jexec alcatraz pfctl -e
+
+ pft_set_rules alcatraz "block all" \
+ "pass in proto icmp label foo label bar" \
+ "set skip on lo"
+
+ # Sanity check & establish state
+ atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \
+ --sendif ${epair}a \
+ --to 192.0.2.2 \
+ --replyif ${epair}a
+
+ # Change rules to now deny the ICMP traffic
+ pft_set_rules noflush alcatraz "block all"
+ if ! find_state;
+ then
+ atf_fail "Setting new rules removed the state."
+ fi
+
+ # Killing a label on a different rules keeps the state
+ jexec alcatraz pfctl -k label -k baz
+ if ! find_state;
+ then
+ atf_fail "Killing a different label removed the state."
+ fi
+
+ # Killing the state with the last label works
+ jexec alcatraz pfctl -k label -k bar
+ if find_state;
+ then
+ atf_fail "Killing with the last label did not remove the state."
+ fi
+
+ pft_set_rules alcatraz "block all" \
+ "pass in proto icmp label foo label bar" \
+ "set skip on lo"
+
+ # Reestablish state
+ atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \
+ --sendif ${epair}a \
+ --to 192.0.2.2 \
+ --replyif ${epair}a
+
+ # Change rules to now deny the ICMP traffic
+ pft_set_rules noflush alcatraz "block all"
+ if ! find_state;
+ then
+ atf_fail "Setting new rules removed the state."
+ fi
+
+ # Killing with the first label works too
+ jexec alcatraz pfctl -k label -k foo
+ if find_state;
+ then
+ atf_fail "Killing with the first label did not remove the state."
+ fi
+}
+
+multilabel_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "gateway" "cleanup"
+gateway_head()
+{
+ atf_set descr 'Test killing states by route-to/reply-to address'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+gateway_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+ jexec alcatraz pfctl -e
+
+ pft_set_rules alcatraz "block all" \
+ "pass in reply-to (${epair}b 192.0.2.1) proto icmp" \
+ "set skip on lo"
+
+ # Sanity check & establish state
+ # Note: use pft_ping so we always use the same ID, so pf considers all
+ # echo requests part of the same flow.
+ atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \
+ --sendif ${epair}a \
+ --to 192.0.2.2 \
+ --replyif ${epair}a
+
+ # Change rules to now deny the ICMP traffic
+ pft_set_rules noflush alcatraz "block all"
+ if ! find_state;
+ then
+ atf_fail "Setting new rules removed the state."
+ fi
+
+ # Killing with a different gateway does not affect our state
+ jexec alcatraz pfctl -k gateway -k 192.0.2.2
+ if ! find_state;
+ then
+ atf_fail "Killing with a different gateway removed the state."
+ fi
+
+ # Killing states with the relevant gateway does terminate our state
+ jexec alcatraz pfctl -k gateway -k 192.0.2.1
+ if find_state;
+ then
+ atf_fail "Killing with the gateway did not remove the state."
+ fi
+}
+
+gateway_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "match" "cleanup"
+match_head()
+{
+ atf_set descr 'Test killing matching states'
+ atf_set require.user root
+}
+
+wait_for_state()
+{
+ jail=$1
+ addr=$2
+
+ while ! jexec $jail pfctl -s s | grep $addr >/dev/null;
+ do
+ sleep .1
+ done
+}
+
+match_body()
+{
+ pft_init
+
+ epair_one=$(vnet_mkepair)
+ ifconfig ${epair_one}a 192.0.2.1/24 up
+
+ epair_two=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair_one}b ${epair_two}a
+ jexec alcatraz ifconfig ${epair_one}b 192.0.2.2/24 up
+ jexec alcatraz ifconfig ${epair_two}a 198.51.100.1/24 up
+ jexec alcatraz sysctl net.inet.ip.forwarding=1
+ jexec alcatraz pfctl -e
+
+ vnet_mkjail singsing ${epair_two}b
+ jexec singsing ifconfig ${epair_two}b 198.51.100.2/24 up
+ jexec singsing route add default 198.51.100.1
+ jexec singsing /usr/sbin/inetd -p ${PWD}/inetd-echo.pid \
+ $(atf_get_srcdir)/echo_inetd.conf
+
+ route add 198.51.100.0/24 192.0.2.2
+
+ pft_set_rules alcatraz \
+ "nat on ${epair_two}a from 192.0.2.0/24 -> (${epair_two}a)" \
+ "pass all"
+
+ nc 198.51.100.2 7 &
+ wait_for_state alcatraz 192.0.2.1
+
+ # Expect two states
+ states=$(jexec alcatraz pfctl -s s | grep 192.0.2.1 | wc -l)
+ if [ $states -ne 2 ] ;
+ then
+ atf_fail "Expected two states, found $states"
+ fi
+
+ # If we don't kill the matching NAT state one should be left
+ jexec alcatraz pfctl -k 192.0.2.1
+ states=$(jexec alcatraz pfctl -s s | grep 192.0.2.1 | wc -l)
+ if [ $states -ne 1 ] ;
+ then
+ atf_fail "Expected one states, found $states"
+ fi
+
+ # Flush
+ jexec alcatraz pfctl -F states
+
+ nc 198.51.100.2 7 &
+ wait_for_state alcatraz 192.0.2.1
+
+ # Kill matching states, expect all of them to be gone
+ jexec alcatraz pfctl -M -k 192.0.2.1
+ states=$(jexec alcatraz pfctl -s s | grep 192.0.2.1 | wc -l)
+ if [ $states -ne 0 ] ;
+ then
+ atf_fail "Expected zero states, found $states"
+ fi
+}
+
+match_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "interface" "cleanup"
+interface_head()
+{
+ atf_set descr 'Test killing states based on interface'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+interface_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+ jexec alcatraz pfctl -e
+
+ pft_set_rules alcatraz "block all" \
+ "pass in proto icmp" \
+ "set skip on lo"
+
+ # Sanity check & establish state
+ atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \
+ --sendif ${epair}a \
+ --to 192.0.2.2 \
+ --replyif ${epair}a
+
+ # Change rules to now deny the ICMP traffic
+ pft_set_rules noflush alcatraz "block all"
+ if ! find_state;
+ then
+ atf_fail "Setting new rules removed the state."
+ fi
+
+ # Flushing states on a different interface doesn't affect our state
+ jexec alcatraz pfctl -i ${epair}a -Fs
+ if ! find_state;
+ then
+ atf_fail "Flushing on a different interface removed the state."
+ fi
+
+ # Flushing on the correct interface does (even with floating states)
+ jexec alcatraz pfctl -i ${epair}b -Fs
+ if find_state;
+ then
+ atf_fail "Flushing on a the interface did not remove the state."
+ fi
+}
+
+interface_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "id" "cleanup"
+id_head()
+{
+ atf_set descr 'Test killing states by id'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+id_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+ jexec alcatraz pfctl -e
+
+ pft_set_rules alcatraz "block all" \
+ "pass in proto tcp" \
+ "pass in proto icmp" \
+ "set skip on lo"
+
+ # Sanity check & establish state
+ atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \
+ --sendif ${epair}a \
+ --to 192.0.2.2 \
+ --replyif ${epair}a
+
+ # Change rules to now deny the ICMP traffic
+ pft_set_rules noflush alcatraz "block all"
+ if ! find_state;
+ then
+ atf_fail "Setting new rules removed the state."
+ fi
+
+ # Get the state ID
+ id=$(jexec alcatraz pfctl -ss -vvv | grep -A 3 icmp |
+ grep -A 3 192.0.2.2 | awk '/id:/ { printf("%s/%s", $2, $4); }')
+
+ # Kill the wrong ID
+ jexec alcatraz pfctl -k id -k 1
+ if ! find_state;
+ then
+ atf_fail "Killing a different ID removed the state."
+ fi
+
+ # Kill the correct ID
+ jexec alcatraz pfctl -k id -k ${id}
+ if find_state;
+ then
+ atf_fail "Killing the state did not remove it."
+ fi
+}
+
+id_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "key" "cleanup"
+key_head()
+{
+ atf_set descr 'Test killing states by their key'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+key_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+ jexec alcatraz pfctl -e
+
+ pft_set_rules alcatraz \
+ "block all" \
+ "pass in proto tcp" \
+ "pass in proto icmp"
+
+ # Sanity check & establish state
+ atf_check -s exit:0 -o ignore ${common_dir}/pft_ping.py \
+ --sendif ${epair}a \
+ --to 192.0.2.2 \
+ --replyif ${epair}a
+
+ # Get the state key
+ key=$(jexec alcatraz pfctl -ss -vvv | awk '/icmp/ { print($2 " " $3 " " $4 " " $5); }')
+ bad_key=$(echo ${key} | sed 's/icmp/tcp/')
+
+ # Kill the wrong key
+ atf_check -s exit:0 -e "match:killed 0 states" \
+ jexec alcatraz pfctl -k key -k "${bad_key}"
+ if ! find_state;
+ then
+ atf_fail "Killing a different ID removed the state."
+ fi
+
+ # Kill the correct key
+ atf_check -s exit:0 -e "match:killed 1 states" \
+ jexec alcatraz pfctl -k key -k "${key}"
+ if find_state;
+ then
+ atf_fail "Killing the state did not remove it."
+ fi
+}
+
+key_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nat" "cleanup"
+nat_head()
+{
+ atf_set descr 'Test killing states by their NAT-ed IP address'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+nat_body()
+{
+ pft_init
+ j="killstate:nat"
+
+ epair_c=$(vnet_mkepair)
+ epair_srv=$(vnet_mkepair)
+
+ vnet_mkjail ${j}c ${epair_c}a
+ ifconfig -j ${j}c ${epair_c}a inet 192.0.2.2/24 up
+ jexec ${j}c route add default 192.0.2.1
+
+ vnet_mkjail ${j}srv ${epair_srv}a
+ ifconfig -j ${j}srv ${epair_srv}a inet 198.51.100.2/24 up
+
+ vnet_mkjail ${j}r ${epair_c}b ${epair_srv}b
+ ifconfig -j ${j}r ${epair_c}b inet 192.0.2.1/24 up
+ ifconfig -j ${j}r ${epair_srv}b inet 198.51.100.1/24 up
+ jexec ${j}r sysctl net.inet.ip.forwarding=1
+
+ jexec ${j}r pfctl -e
+ pft_set_rules ${j}r \
+ "nat on ${epair_srv}b inet from 192.0.2.0/24 to any -> (${epair_srv}b)"
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ jexec ${j}c ping -c 1 192.0.2.1
+ atf_check -s exit:0 -o ignore \
+ jexec ${j}srv ping -c 1 198.51.100.1
+ atf_check -s exit:0 -o ignore \
+ jexec ${j}c ping -c 1 198.51.100.2
+
+ # Establish state
+ # Note: use pft_ping so we always use the same ID, so pf considers all
+ # echo requests part of the same flow.
+ atf_check -s exit:0 -o ignore jexec ${j}c ${common_dir}/pft_ping.py \
+ --sendif ${epair_c}a \
+ --to 198.51.100.1 \
+ --replyif ${epair_c}a
+
+ # There's NAT here, so the source IP will be 198.51.100.1
+ if ! find_state ${j}r 198.51.100.1;
+ then
+ atf_fail "Expected state not found"
+ fi
+
+ # By NAT-ed address?
+ jexec ${j}r pfctl -k nat -k 192.0.2.2
+
+ if find_state ${j}r 198.51.100.1;
+ then
+ jexec ${j}r pfctl -ss -v
+ atf_fail "Failed to remove state"
+ fi
+}
+
+nat_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "v4"
+ atf_add_test_case "v6"
+ atf_add_test_case "label"
+ atf_add_test_case "multilabel"
+ atf_add_test_case "gateway"
+ atf_add_test_case "match"
+ atf_add_test_case "interface"
+ atf_add_test_case "id"
+ atf_add_test_case "key"
+ atf_add_test_case "nat"
+}
diff --git a/tests/sys/netpfil/pf/limits.sh b/tests/sys/netpfil/pf/limits.sh
new file mode 100644
index 000000000000..69f0b6af2ccf
--- /dev/null
+++ b/tests/sys/netpfil/pf/limits.sh
@@ -0,0 +1,119 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 Kristof Provost <kp@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "basic" "cleanup"
+basic_head()
+{
+ atf_set descr 'Test setting and retrieving limits'
+ atf_set require.user root
+}
+
+basic_body()
+{
+ pft_init
+
+ vnet_mkjail alcatraz
+
+ pft_set_rules alcatraz \
+ "set limit states 200" \
+ "set limit frags 100" \
+ "set limit src-nodes 50" \
+ "set limit table-entries 25"
+
+ atf_check -s exit:0 -o match:'states.*200' \
+ jexec alcatraz pfctl -sm
+ atf_check -s exit:0 -o match:'frags.*100' \
+ jexec alcatraz pfctl -sm
+ atf_check -s exit:0 -o match:'src-nodes.*50' \
+ jexec alcatraz pfctl -sm
+ atf_check -s exit:0 -o match:'table-entries.*25' \
+ jexec alcatraz pfctl -sm
+}
+
+basic_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "zero" "cleanup"
+zero_head()
+{
+ atf_set descr 'Test changing a limit from zero on an in-use zone'
+ atf_set require.user root
+}
+
+zero_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}b 192.0.2.2/24 up
+
+ vnet_mkjail alcatraz ${epair}a
+ jexec alcatraz ifconfig ${epair}a 192.0.2.1/24 up
+
+ atf_check -s exit:0 -o ignore \
+ ping -c 3 192.0.2.1
+
+ jexec alcatraz pfctl -e
+ # Set no limit
+ pft_set_rules noflush alcatraz \
+ "set limit states 0" \
+ "pass"
+
+ # Check that we really report no limit
+ atf_check -s exit:0 -o 'match:states hard limit 0' \
+ jexec alcatraz pfctl -sa
+
+ # Create a state
+ atf_check -s exit:0 -o ignore \
+ ping -c 3 192.0.2.1
+
+ # Limit states
+ pft_set_rules noflush alcatraz \
+ "set limit states 1000" \
+ "pass"
+
+ # And create a new state
+ atf_check -s exit:0 -o ignore \
+ ping -c 3 192.0.2.1
+
+ atf_check -s exit:0 -o 'match:states hard limit 1000' \
+ jexec alcatraz pfctl -sa
+}
+
+zero_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "basic"
+ atf_add_test_case "zero"
+}
diff --git a/tests/sys/netpfil/pf/loginterface.sh b/tests/sys/netpfil/pf/loginterface.sh
new file mode 100644
index 000000000000..6decb69fe63d
--- /dev/null
+++ b/tests/sys/netpfil/pf/loginterface.sh
@@ -0,0 +1,87 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+
+atf_test_case "basic" "cleanup"
+basic_head()
+{
+ atf_set descr 'Basic loginterface test'
+ atf_set require.user root
+}
+
+basic_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+
+ ifconfig ${epair}a 192.0.2.2/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+
+ # No interface stats until we configure a loginterface
+ atf_check -o not-match:"Interface Stats for" \
+ jexec alcatraz pfctl -s info
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "set loginterface ${epair}b" \
+ "pass"
+
+ # We do get Interface Stats listed when we've configured a loginterface
+ atf_check -o match:"Interface Stats for ${epair}b" \
+ jexec alcatraz pfctl -s info
+
+ # And after we've sent traffic there's non-zero counters
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+
+ atf_check -o match:"Interface Stats for ${epair}b" \
+ jexec alcatraz pfctl -s info
+ atf_check -o match:"Passed 1" \
+ jexec alcatraz pfctl -s info
+
+ # And no interface stats once we remove the loginterface
+ pft_set_rules alcatraz \
+ "pass"
+ atf_check -o not-match:"Interface Stats for ${epair}b" \
+ jexec alcatraz pfctl -s info
+}
+
+basic_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "basic"
+}
diff --git a/tests/sys/netpfil/pf/macro.sh b/tests/sys/netpfil/pf/macro.sh
new file mode 100644
index 000000000000..442677c9f946
--- /dev/null
+++ b/tests/sys/netpfil/pf/macro.sh
@@ -0,0 +1,40 @@
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "nr" "cleanup"
+nr_head()
+{
+ atf_set descr 'Test $nr expansion'
+ atf_set require.user root
+}
+
+nr_body()
+{
+ # Ensure that when the optimiser collapses rules the macro expansion
+ # has the correct rule number
+ pft_init
+
+ vnet_mkjail alcatraz
+ jexec alcatraz ifconfig lo0 inet 127.0.0.1/8
+ jexec alcatraz ifconfig lo0 inet 127.0.0.2/32 alias
+
+ pft_set_rules alcatraz \
+ "pass quick on lo from lo:network to lo:network" \
+ "block quick all label \"ruleNo:\$nr\""
+
+ no=$(jexec alcatraz pfctl -sr -vv | awk '/ruleNo/ { gsub("@", "", $1); print $1; }')
+ ruleno=$(jexec alcatraz pfctl -sr -vv | awk '/ruleNo/ { gsub(/"ruleNo:/, "", $7); gsub(/"/, "", $7); print $7; }')
+ if [ "${no}" -ne "${ruleno}" ];
+ then
+ atf_fail "Expected ruleNo $no != $ruleno"
+ fi
+}
+
+nr_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "nr"
+}
diff --git a/tests/sys/netpfil/pf/match.sh b/tests/sys/netpfil/pf/match.sh
new file mode 100644
index 000000000000..58c1e021310a
--- /dev/null
+++ b/tests/sys/netpfil/pf/match.sh
@@ -0,0 +1,185 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2021 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+common_dir=$(atf_get_srcdir)/../common
+
+atf_test_case "dummynet" "cleanup"
+dummynet_head()
+{
+ atf_set descr 'Test dummynet with match keyword'
+ atf_set require.user root
+}
+
+dummynet_body()
+{
+ dummynet_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}b
+
+ ifconfig ${epair}a 192.0.2.1/24 up
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -i .1 -c 3 -s 1200 192.0.2.2
+
+ jexec alcatraz dnctl pipe 1 config bw 30Byte/s
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "match in dnpipe 1" \
+ "pass"
+
+ # single ping succeeds just fine
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ # Saturate the link
+ ping -i .1 -c 5 -s 1200 192.0.2.2
+
+ # We should now be hitting the limits and get this packet dropped.
+ atf_check -s exit:2 -o ignore ping -c 1 -s 1200 192.0.2.2
+}
+
+dummynet_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "quick" "cleanup"
+quick_head()
+{
+ atf_set descr 'Test quick on match rules'
+ atf_set require.user root
+}
+
+quick_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}b
+
+ ifconfig ${epair}a 192.0.2.1/24 up
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.2
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "pass" \
+ "match in quick proto icmp" \
+ "block"
+
+ # 'match quick' should retain the previous pass/block state
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.2
+
+ pft_set_rules alcatraz \
+ "block" \
+ "match in quick proto icmp" \
+ "pass"
+
+ atf_check -s exit:2 -o ignore \
+ ping -c 1 192.0.2.2
+}
+
+quick_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "allow_opts" "cleanup"
+allow_opts_head()
+{
+ atf_set descr 'Test allowing IP options via match'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+allow_opts_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+
+ ifconfig ${epair}b 192.0.2.2/24 up
+
+ vnet_mkjail alcatraz ${epair}a
+ jexec alcatraz ifconfig ${epair}a 192.0.2.1/24 up
+
+ jexec alcatraz pfctl -e
+ jexec alcatraz pfctl -x loud
+ pft_set_rules alcatraz \
+ "match proto icmp allow-opts" \
+ "pass"
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.1
+
+ atf_check -s exit:0 -o ignore \
+ ${common_dir}/pft_ping.py \
+ --sendif ${epair}b \
+ --to 192.0.2.1 \
+ --send-nop \
+ --replyif ${epair}b
+
+ # This doesn't work without 'allow-opts'
+ pft_set_rules alcatraz \
+ "match proto icmp" \
+ "pass"
+ atf_check -s exit:1 -o ignore \
+ ${common_dir}/pft_ping.py \
+ --sendif ${epair}b \
+ --to 192.0.2.1 \
+ --send-nop \
+ --replyif ${epair}b
+
+ # Setting it on a pass rule still works.
+ pft_set_rules alcatraz \
+ "pass allow-opts"
+ atf_check -s exit:0 -o ignore \
+ ${common_dir}/pft_ping.py \
+ --sendif ${epair}b \
+ --to 192.0.2.1 \
+ --send-nop \
+ --replyif ${epair}b
+}
+
+allow_opts_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "dummynet"
+ atf_add_test_case "quick"
+ atf_add_test_case "allow_opts"
+}
diff --git a/tests/sys/netpfil/pf/max_pkt_rate.sh b/tests/sys/netpfil/pf/max_pkt_rate.sh
new file mode 100644
index 000000000000..bdd140eb60dd
--- /dev/null
+++ b/tests/sys/netpfil/pf/max_pkt_rate.sh
@@ -0,0 +1,121 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2025 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+common_setup()
+{
+ epair=$(vnet_mkepair)
+
+ ifconfig ${epair}a inet 192.0.2.2/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b inet 192.0.2.1/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.1
+
+ jexec alcatraz pfctl -e
+}
+
+common_test()
+{
+ # One ping will pass
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.1
+
+ # As will a second
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.1
+
+ # But the third should fail
+ atf_check -s exit:2 -o ignore \
+ ping -c 1 192.0.2.1
+
+ # But three seconds later we can ping again
+ sleep 3
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.1
+}
+
+atf_test_case "basic" "cleanup"
+basic_head()
+{
+ atf_set descr 'Basic maximum packet rate test'
+ atf_set require.user root
+}
+
+basic_body()
+{
+ pft_init
+
+ common_setup
+
+ pft_set_rules alcatraz \
+ "block" \
+ "pass in proto icmp max-pkt-rate 2/2"
+
+ common_test
+}
+
+basic_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "anchor" "cleanup"
+anchor_head()
+{
+ atf_set descr 'maximum packet rate on anchor'
+ atf_set require.user root
+}
+
+anchor_body()
+{
+ pft_init
+
+ common_setup
+
+ pft_set_rules alcatraz \
+ "block" \
+ "anchor \"foo\" proto icmp max-pkt-rate 2/2 {\n \
+ pass \n \
+ }"
+
+ common_test
+}
+
+anchor_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "basic"
+ atf_add_test_case "anchor"
+}
diff --git a/tests/sys/netpfil/pf/max_pkt_size.sh b/tests/sys/netpfil/pf/max_pkt_size.sh
new file mode 100644
index 000000000000..030d642303fc
--- /dev/null
+++ b/tests/sys/netpfil/pf/max_pkt_size.sh
@@ -0,0 +1,122 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2025 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+common_setup()
+{
+ epair=$(vnet_mkepair)
+
+ ifconfig ${epair}b 192.0.2.2/24 up
+
+ vnet_mkjail alcatraz ${epair}a
+ jexec alcatraz ifconfig ${epair}a 192.0.2.1/24 up
+
+ jexec alcatraz pfctl -e
+}
+
+common_test()
+{
+ # Small packets pass
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.1
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 -s 100 192.0.2.1
+
+ # Larger packets do not
+ atf_check -s exit:2 -o ignore \
+ ping -c 3 -s 101 192.0.2.1
+ atf_check -s exit:2 -o ignore \
+ ping -c 3 -s 128 192.0.2.1
+}
+
+atf_test_case "basic" "cleanup"
+basic_head()
+{
+ atf_set descr 'Basic max-pkt-size test'
+ atf_set require.user root
+}
+
+basic_body()
+{
+ pft_init
+
+ common_setup
+
+ pft_set_rules alcatraz \
+ "pass max-pkt-size 128"
+
+ common_test
+
+ # We can enforce this on fragmented packets too
+ pft_set_rules alcatraz \
+ "pass max-pkt-size 2000"
+
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 -s 1400 192.0.2.1
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 -s 1972 192.0.2.1
+ atf_check -s exit:2 -o ignore \
+ ping -c 1 -s 1973 192.0.2.1
+ atf_check -s exit:2 -o ignore \
+ ping -c 3 -s 3000 192.0.2.1
+}
+
+basic_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "match" "cleanup"
+match_head()
+{
+ atf_set descr 'max-pkt-size on match rules'
+ atf_set require.user root
+}
+
+match_body()
+{
+ pft_init
+
+ common_setup
+
+ pft_set_rules alcatraz \
+ "match in max-pkt-size 128" \
+ "pass"
+
+ common_test
+}
+
+match_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "basic"
+ atf_add_test_case "match"
+}
diff --git a/tests/sys/netpfil/pf/max_states.sh b/tests/sys/netpfil/pf/max_states.sh
new file mode 100755
index 000000000000..1bf6814f9283
--- /dev/null
+++ b/tests/sys/netpfil/pf/max_states.sh
@@ -0,0 +1,62 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 Kajetan Staszkiewicz <vegeta@tuxpowered.net>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+
+max_states_head()
+{
+ atf_set descr 'Max states per rule'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+max_states_body()
+{
+ setup_router_dummy_ipv6
+
+ pft_set_rules router \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in on ${epair_tester}b inet6 proto tcp keep state (max 3)" \
+ "pass out on ${epair_server}a inet6 proto tcp keep state"
+
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-sport=4201
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-sport=4202
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-sport=4203
+ ping_dummy_check_request exit:1 --ping-type=tcpsyn --send-sport=4204
+}
+
+max_states_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_init_test_cases()
+{
+ atf_add_test_case "max_states"
+}
diff --git a/tests/sys/netpfil/pf/mbuf.sh b/tests/sys/netpfil/pf/mbuf.sh
new file mode 100644
index 000000000000..e3f138bb73b9
--- /dev/null
+++ b/tests/sys/netpfil/pf/mbuf.sh
@@ -0,0 +1,242 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 Igor Ostapenko <pm@igoro.pro>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+dummymbuf_init()
+{
+ if ! kldstat -q -m dummymbuf; then
+ atf_skip "This test requires dummymbuf"
+ fi
+}
+
+atf_test_case "inet_in_mbuf_len" "cleanup"
+inet_in_mbuf_len_head()
+{
+ atf_set descr 'Test that pf can handle inbound with the first mbuf with m_len < sizeof(struct ip)'
+ atf_set require.user root
+}
+inet_in_mbuf_len_body()
+{
+ pft_init
+ dummymbuf_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a 192.0.2.1/24 up
+
+ # Set up a simple jail with one interface
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c1 192.0.2.2
+
+ # Should be denied
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "block"
+ atf_check -s not-exit:0 -o ignore ping -c1 -t1 192.0.2.2
+
+ # Should be allowed by from/to addresses
+ pft_set_rules alcatraz \
+ "block" \
+ "pass in from 192.0.2.1 to 192.0.2.2"
+ atf_check -s exit:0 -o ignore ping -c1 192.0.2.2
+
+ # Should still work for m_len=0
+ jexec alcatraz pfilctl link -i dummymbuf:inet inet
+ jexec alcatraz sysctl net.dummymbuf.rules="inet in ${epair}b pull-head 0;"
+ atf_check_equal "0" "$(jexec alcatraz sysctl -n net.dummymbuf.hits)"
+ atf_check -s exit:0 -o ignore ping -c1 192.0.2.2
+ atf_check_equal "1" "$(jexec alcatraz sysctl -n net.dummymbuf.hits)"
+
+ # m_len=1
+ jexec alcatraz sysctl net.dummymbuf.rules="inet in ${epair}b pull-head 1;"
+ jexec alcatraz sysctl net.dummymbuf.hits=0
+ atf_check -s exit:0 -o ignore ping -c1 192.0.2.2
+ atf_check_equal "1" "$(jexec alcatraz sysctl -n net.dummymbuf.hits)"
+
+ # m_len=19
+ # provided IPv4 basic header is 20 bytes long, it should impact the dst addr
+ jexec alcatraz sysctl net.dummymbuf.rules="inet in ${epair}b pull-head 19;"
+ jexec alcatraz sysctl net.dummymbuf.hits=0
+ atf_check -s exit:0 -o ignore ping -c1 192.0.2.2
+ atf_check_equal "1" "$(jexec alcatraz sysctl -n net.dummymbuf.hits)"
+}
+inet_in_mbuf_len_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "inet6_in_mbuf_len" "cleanup"
+inet6_in_mbuf_len_head()
+{
+ atf_set descr 'Test that pf can handle inbound with the first mbuf with m_len < sizeof(struct ip6_hdr)'
+ atf_set require.user root
+}
+inet6_in_mbuf_len_body()
+{
+ pft_init
+ dummymbuf_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a inet6 2001:db8::1/64 up no_dad
+
+ # Ensure we don't unintentionally send MLD packets to alcatraz
+ pfctl -e
+ echo "block
+ pass out inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv, echoreq, echorep }
+ " | pfctl -g -f -
+
+ # Set up a simple jail with one interface
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b inet6 2001:db8::2/64 up no_dad
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c1 2001:db8::2
+
+ # Should be denied
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "block" \
+ "pass quick inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }"
+ atf_check -s not-exit:0 -o ignore ping -c1 -t1 2001:db8::2
+
+ # Avoid redundant ICMPv6 packets to avoid false positives during
+ # counting of net.dummymbuf.hits.
+ ndp -i ${epair}a -- -nud
+ jexec alcatraz ndp -i ${epair}b -- -nud
+
+ # Should be allowed by from/to addresses
+ pft_set_rules alcatraz \
+ "block" \
+ "pass quick inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in inet6 from 2001:db8::1 to 2001:db8::2"
+ atf_check -s exit:0 -o ignore ping -c1 2001:db8::2
+
+ # Should still work for m_len=0
+ jexec alcatraz pfilctl link -i dummymbuf:inet6 inet6
+ jexec alcatraz sysctl net.dummymbuf.rules="inet6 in ${epair}b pull-head 0;"
+ atf_check_equal "0" "$(jexec alcatraz sysctl -n net.dummymbuf.hits)"
+ atf_check -s exit:0 -o ignore ping -c1 2001:db8::2
+ atf_check_equal "1" "$(jexec alcatraz sysctl -n net.dummymbuf.hits)"
+
+ # m_len=1
+ jexec alcatraz sysctl net.dummymbuf.rules="inet6 in ${epair}b pull-head 1;"
+ jexec alcatraz sysctl net.dummymbuf.hits=0
+ atf_check -s exit:0 -o ignore ping -c1 2001:db8::2
+ atf_check_equal "1" "$(jexec alcatraz sysctl -n net.dummymbuf.hits)"
+
+ # m_len=39
+ # provided IPv6 basic header is 40 bytes long, it should impact the dst addr
+ jexec alcatraz sysctl net.dummymbuf.rules="inet6 in ${epair}b pull-head 39;"
+ jexec alcatraz sysctl net.dummymbuf.hits=0
+ atf_check -s exit:0 -o ignore ping -c1 2001:db8::2
+ atf_check_equal "1" "$(jexec alcatraz sysctl -n net.dummymbuf.hits)"
+}
+inet6_in_mbuf_len_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "ethernet_in_mbuf_len" "cleanup"
+ethernet_in_mbuf_len_head()
+{
+ atf_set descr 'Test that pf can handle inbound with the first mbuf with m_len < sizeof(struct ether_header)'
+ atf_set require.user root
+}
+ethernet_in_mbuf_len_body()
+{
+ pft_init
+ dummymbuf_init
+
+ epair=$(vnet_mkepair)
+ epair_a_mac=$(ifconfig ${epair}a ether | awk '/ether/ { print $2; }')
+ ifconfig ${epair}a 192.0.2.1/24 up
+
+ # Set up a simple jail with one interface
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+ epair_b_mac=$(jexec alcatraz ifconfig ${epair}b ether | awk '/ether/ { print $2; }')
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c1 192.0.2.2
+
+ # Should be denied
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "ether block" \
+ "pass"
+ atf_check -s not-exit:0 -o ignore ping -c1 -t1 192.0.2.2
+
+ # Should be allowed by from/to addresses
+ echo $epair_a_mac
+ echo $epair_b_mac
+ pft_set_rules alcatraz \
+ "ether block" \
+ "ether pass in from ${epair_a_mac} to ${epair_b_mac}" \
+ "ether pass out from ${epair_b_mac} to ${epair_a_mac}" \
+ "pass"
+ atf_check -s exit:0 -o ignore ping -c1 192.0.2.2
+
+ # Should still work for m_len=0
+ jexec alcatraz pfilctl link -i dummymbuf:ethernet ethernet
+ jexec alcatraz sysctl net.dummymbuf.rules="ethernet in ${epair}b pull-head 0;"
+ atf_check_equal "0" "$(jexec alcatraz sysctl -n net.dummymbuf.hits)"
+ atf_check -s exit:0 -o ignore ping -c1 192.0.2.2
+ atf_check_equal "1" "$(jexec alcatraz sysctl -n net.dummymbuf.hits)"
+
+ # m_len=1
+ jexec alcatraz sysctl net.dummymbuf.rules="ethernet in ${epair}b pull-head 1;"
+ jexec alcatraz sysctl net.dummymbuf.hits=0
+ atf_check -s exit:0 -o ignore ping -c1 192.0.2.2
+ atf_check_equal "1" "$(jexec alcatraz sysctl -n net.dummymbuf.hits)"
+
+ # m_len=11
+ # for the simplest L2 Ethernet frame it should impact src field
+ jexec alcatraz sysctl net.dummymbuf.rules="ethernet in ${epair}b pull-head 11;"
+ jexec alcatraz sysctl net.dummymbuf.hits=0
+ atf_check -s exit:0 -o ignore ping -c1 192.0.2.2
+ atf_check_equal "1" "$(jexec alcatraz sysctl -n net.dummymbuf.hits)"
+
+ # m_len=13
+ # provided L2 Ethernet simplest header is 14 bytes long, it should impact ethertype field
+ jexec alcatraz sysctl net.dummymbuf.rules="ethernet in ${epair}b pull-head 13;"
+ jexec alcatraz sysctl net.dummymbuf.hits=0
+ atf_check -s exit:0 -o ignore ping -c1 192.0.2.2
+ atf_check_equal "1" "$(jexec alcatraz sysctl -n net.dummymbuf.hits)"
+}
+ethernet_in_mbuf_len_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "inet_in_mbuf_len"
+ atf_add_test_case "inet6_in_mbuf_len"
+ atf_add_test_case "ethernet_in_mbuf_len"
+}
diff --git a/tests/sys/netpfil/pf/mld.py b/tests/sys/netpfil/pf/mld.py
new file mode 100644
index 000000000000..d118a34c8a7d
--- /dev/null
+++ b/tests/sys/netpfil/pf/mld.py
@@ -0,0 +1,95 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2025 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import pytest
+from utils import DelayedSend
+from atf_python.sys.net.tools import ToolsHelper
+from atf_python.sys.net.vnet import VnetTestTemplate
+
+class TestMLD(VnetTestTemplate):
+ REQUIRED_MODULES = [ "pf" ]
+ TOPOLOGY = {
+ "vnet1": {"ifaces": ["if1"]},
+ "vnet2": {"ifaces": ["if1"]},
+ "if1": {"prefixes6": [("2001:db8::2/64", "2001:db8::1/64")]},
+ }
+
+ def vnet2_handler(self, vnet):
+ ifname = vnet.iface_alias_map["if1"].name
+ #ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.pf_rules([
+ "pass",
+ ])
+ ToolsHelper.print_output("/sbin/pfctl -x loud")
+ #ToolsHelper.print_output("echo \"j 230.0.0.1 %s\ns 3600\nq\" | /usr/sbin/mtest" % ifname)
+
+ def find_mld_reply(self, pkt, ifname):
+ pkt.show()
+ s = DelayedSend(pkt)
+
+ found = False
+ packets = self.sp.sniff(iface=ifname, timeout=5)
+ for r in packets:
+ r.show()
+ mld = r.getlayer(self.sp.ICMPv6MLReport2)
+ if not mld:
+ continue
+ mld.show()
+ found = True
+ return found
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_router_alert(self):
+ """Verify that we allow MLD packets with router alert extension header"""
+ ifname = self.vnet.iface_alias_map["if1"].name
+ #ToolsHelper.print_output("/sbin/ifconfig %s inet6 -ifdisable" % ifname)
+ ToolsHelper.print_output("/sbin/ifconfig")
+
+ # Import in the correct vnet, so at to not confuse Scapy
+ import scapy.all as sp
+ import scapy.contrib as sc
+ import scapy.contrib.igmp
+ self.sp = sp
+ self.sc = sc
+
+ # A correct MLD query gets a reply
+ pkt = sp.IPv6(src="fe80::1%%%s" % ifname, dst="ff02::1", hlim=1) \
+ / sp.RouterAlert(value=0) \
+ / sp.ICMPv6MLQuery2()
+ assert self.find_mld_reply(pkt, ifname)
+
+ # The wrong extension header does not
+ pkt = sp.IPv6(src="fe80::1%%%s" % ifname, dst="ff02::1", hlim=1) \
+ / sp.IPv6ExtHdrRouting() \
+ / sp.ICMPv6MLQuery2()
+ assert not self.find_mld_reply(pkt, ifname)
+
+ # Neither does an incorrect hop limit
+ pkt = sp.IPv6(src="fe80::1%%%s" % ifname, dst="ff02::1", hlim=2) \
+ / sp.RouterAlert(value=0) \
+ / sp.ICMPv6MLQuery2()
+ assert not self.find_mld_reply(pkt, ifname)
diff --git a/tests/sys/netpfil/pf/modulate.sh b/tests/sys/netpfil/pf/modulate.sh
new file mode 100644
index 000000000000..1abe22cff391
--- /dev/null
+++ b/tests/sys/netpfil/pf/modulate.sh
@@ -0,0 +1,79 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2023 Kajetan Staszkiewicz <vegetga@tuxpowered.net>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "modulate_v4" "cleanup"
+modulate_v4_head()
+{
+ atf_set descr 'IPv4 TCP sequence number modulation'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+modulate_v4_body()
+{
+ setup_router_dummy_ipv4
+
+ pft_set_rules router \
+ "pass in on ${epair_tester}b modulate state"
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-seq 42 # Sanity check
+ ping_dummy_check_request exit:1 --ping-type=tcpsyn --send-seq 42 --expect-seq 42
+}
+
+modulate_v4_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "modulate_v6" "cleanup"
+modulate_v6_head()
+{
+ atf_set descr 'IPv6 TCP sequence number modulation'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+modulate_v6_body()
+{
+ setup_router_dummy_ipv6
+
+ pft_set_rules router \
+ "pass in on ${epair_tester}b modulate state"
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-seq 42 # Sanity check
+ ping_dummy_check_request exit:1 --ping-type=tcpsyn --send-seq 42 --expect-seq 42
+}
+
+modulate_v6_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "modulate_v4"
+ atf_add_test_case "modulate_v6"
+}
diff --git a/tests/sys/netpfil/pf/names.sh b/tests/sys/netpfil/pf/names.sh
new file mode 100644
index 000000000000..e47b0917cfec
--- /dev/null
+++ b/tests/sys/netpfil/pf/names.sh
@@ -0,0 +1,102 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2018 Kristof Provost <kp@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "names" "cleanup"
+names_head()
+{
+ atf_set descr 'Test overlapping names'
+ atf_set require.user root
+}
+
+names_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair}b
+ ifconfig ${epair}a name foo
+ jexec alcatraz ifconfig ${epair}b name foo
+
+ jail -r alcatraz
+ ifconfig foo destroy
+}
+
+names_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "group" "cleanup"
+group_head()
+{
+ atf_set descr 'Test group cleanup, PR257218'
+ atf_set require.user root
+}
+
+group_body()
+{
+ pft_init
+
+ vnet_mkjail alcatraz
+
+ if [ -n "$(jexec alcatraz pfctl -sI | grep '^epair$')" ];
+ then
+ atf_fail "Unexpected epair group"
+ fi
+
+ epair=$(vnet_mkepair)
+ if [ -n "$(jexec alcatraz pfctl -sI | grep '^epair$')" ];
+ then
+ atf_fail "Unexpected epair group"
+ fi
+
+ ifconfig ${epair}b vnet alcatraz
+ if [ -z "$(jexec alcatraz pfctl -sI | grep '^epair$')" ];
+ then
+ atf_fail "Failed to find epair group"
+ fi
+
+ ifconfig ${epair}a destroy
+
+ if [ -n "$(jexec alcatraz pfctl -sI | grep '^epair$')" ];
+ then
+ atf_fail "Unexpected epair group"
+ fi
+}
+
+group_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "names"
+ atf_add_test_case "group"
+}
diff --git a/tests/sys/netpfil/pf/nat.sh b/tests/sys/netpfil/pf/nat.sh
new file mode 100644
index 000000000000..16c981f97399
--- /dev/null
+++ b/tests/sys/netpfil/pf/nat.sh
@@ -0,0 +1,830 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2018 Kristof Provost <kp@FreeBSD.org>
+# Copyright (c) 2025 Kajetan Staszkiewicz <ks@FreeBSD.org>
+# Copyright (c) 2021 KUROSAWA Takahiro <takahiro.kurosawa@gmail.com>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "exhaust" "cleanup"
+exhaust_head()
+{
+ atf_set descr 'Test exhausting the NAT pool'
+ atf_set require.user root
+}
+
+exhaust_body()
+{
+ pft_init
+
+ epair_nat=$(vnet_mkepair)
+ epair_echo=$(vnet_mkepair)
+
+ vnet_mkjail nat ${epair_nat}b ${epair_echo}a
+ vnet_mkjail echo ${epair_echo}b
+
+ ifconfig ${epair_nat}a 192.0.2.2/24 up
+ route add -net 198.51.100.0/24 192.0.2.1
+
+ jexec nat ifconfig ${epair_nat}b 192.0.2.1/24 up
+ jexec nat ifconfig ${epair_echo}a 198.51.100.1/24 up
+ jexec nat sysctl net.inet.ip.forwarding=1
+
+ jexec echo ifconfig ${epair_echo}b 198.51.100.2/24 up
+ jexec echo /usr/sbin/inetd -p ${PWD}/inetd-echo.pid $(atf_get_srcdir)/echo_inetd.conf
+
+ # Enable pf!
+ jexec nat pfctl -e
+ pft_set_rules nat \
+ "nat pass on ${epair_echo}a inet from 192.0.2.0/24 to any -> (${epair_echo}a) port 30000:30001 sticky-address"
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 3 198.51.100.2
+
+ atf_check -s exit:0 -o match:foo* echo "foo" | nc -N 198.51.100.2 7
+ atf_check -s exit:0 -o match:foo* echo "foo" | nc -N 198.51.100.2 7
+
+ # This one will fail, but that's expected
+ echo "foo" | nc -N 198.51.100.2 7 &
+
+ sleep 1
+
+ # If the kernel is stuck in pf_get_sport() this will not succeed either.
+ timeout 2 jexec nat pfctl -sa
+ if [ $? -eq 124 ]; then
+ # Timed out
+ atf_fail "pfctl timeout"
+ fi
+}
+
+exhaust_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nested_anchor" "cleanup"
+nested_anchor_head()
+{
+ atf_set descr 'Test setting and retrieving nested nat anchors'
+ atf_set require.user root
+}
+
+nested_anchor_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail nat ${epair}a
+
+ pft_set_rules nat \
+ "nat-anchor \"foo\""
+
+ echo "nat-anchor \"bar\"" | jexec nat pfctl -g -a foo -f -
+ echo "nat on ${epair}a from any to any -> (${epair}a)" | jexec nat pfctl -g -a "foo/bar" -f -
+
+ atf_check -s exit:0 -o inline:"nat-anchor \"foo\" all {
+ nat-anchor \"bar\" all {
+ nat on ${epair}a all -> (${epair}a) round-robin
+ }
+}
+" jexec nat pfctl -sn -a "*"
+
+}
+
+endpoint_independent_setup()
+{
+ pft_init
+ filter="udp and dst port 1234" # only capture udp pings
+
+ epair_client=$(vnet_mkepair)
+ epair_nat=$(vnet_mkepair)
+ epair_server1=$(vnet_mkepair)
+ epair_server2=$(vnet_mkepair)
+ bridge=$(vnet_mkbridge)
+
+ vnet_mkjail nat ${epair_client}b ${epair_nat}a
+ vnet_mkjail client ${epair_client}a
+ vnet_mkjail server1 ${epair_server1}a
+ vnet_mkjail server2 ${epair_server2}a
+
+ ifconfig ${epair_server1}b up
+ ifconfig ${epair_server2}b up
+ ifconfig ${epair_nat}b up
+ ifconfig ${bridge} \
+ addm ${epair_server1}b \
+ addm ${epair_server2}b \
+ addm ${epair_nat}b \
+ up
+
+ jexec nat ifconfig ${epair_client}b 192.0.2.1/24 up
+ jexec nat ifconfig ${epair_nat}a 198.51.100.42/24 up
+ jexec nat sysctl net.inet.ip.forwarding=1
+
+ jexec client ifconfig ${epair_client}a 192.0.2.2/24 up
+ jexec client route add default 192.0.2.1
+
+ jexec server1 ifconfig ${epair_server1}a 198.51.100.32/24 up
+ jexec server2 ifconfig ${epair_server2}a 198.51.100.22/24 up
+}
+
+endpoint_independent_common()
+{
+ # Enable pf!
+ jexec nat pfctl -e
+
+ # validate non-endpoint independent nat rule behaviour
+ pft_set_rules nat "${1}"
+
+ jexec server1 tcpdump -i ${epair_server1}a -w ${PWD}/server1.pcap \
+ --immediate-mode $filter &
+ server1tcppid="$!"
+ jexec server2 tcpdump -i ${epair_server2}a -w ${PWD}/server2.pcap \
+ --immediate-mode $filter &
+ server2tcppid="$!"
+
+ # send out multiple packets
+ for i in $(seq 1 10); do
+ echo "ping" | jexec client nc -u 198.51.100.32 1234 -p 4242 -w 0
+ echo "ping" | jexec client nc -u 198.51.100.22 1234 -p 4242 -w 0
+ done
+
+ kill $server1tcppid
+ kill $server2tcppid
+
+ tuple_server1=$(tcpdump -r ${PWD}/server1.pcap | awk '{addr=$3} END {print addr}')
+ tuple_server2=$(tcpdump -r ${PWD}/server2.pcap | awk '{addr=$3} END {print addr}')
+
+ if [ -z $tuple_server1 ]
+ then
+ atf_fail "server1 did not receive connection from client (default)"
+ fi
+
+ if [ -z $tuple_server2 ]
+ then
+ atf_fail "server2 did not receive connection from client (default)"
+ fi
+
+ if [ "$tuple_server1" = "$tuple_server2" ]
+ then
+ echo "server1 tcpdump: $tuple_server1"
+ echo "server2 tcpdump: $tuple_server2"
+ atf_fail "Received same IP:port on server1 and server2 (default)"
+ fi
+
+ # validate endpoint independent nat rule behaviour
+ pft_set_rules nat "${2}"
+
+ jexec server1 tcpdump -i ${epair_server1}a -w ${PWD}/server1.pcap \
+ --immediate-mode $filter &
+ server1tcppid="$!"
+ jexec server2 tcpdump -i ${epair_server2}a -w ${PWD}/server2.pcap \
+ --immediate-mode $filter &
+ server2tcppid="$!"
+
+ # send out multiple packets, sometimes one fails to go through
+ for i in $(seq 1 10); do
+ echo "ping" | jexec client nc -u 198.51.100.32 1234 -p 4242 -w 0
+ echo "ping" | jexec client nc -u 198.51.100.22 1234 -p 4242 -w 0
+ done
+
+ kill $server1tcppid
+ kill $server2tcppid
+
+ tuple_server1=$(tcpdump -r ${PWD}/server1.pcap | awk '{addr=$3} END {print addr}')
+ tuple_server2=$(tcpdump -r ${PWD}/server2.pcap | awk '{addr=$3} END {print addr}')
+
+ if [ -z $tuple_server1 ]
+ then
+ atf_fail "server1 did not receive connection from client (endpoint-independent)"
+ fi
+
+ if [ -z $tuple_server2 ]
+ then
+ atf_fail "server2 did not receive connection from client (endpoint-independent)"
+ fi
+
+ if [ ! "$tuple_server1" = "$tuple_server2" ]
+ then
+ echo "server1 tcpdump: $tuple_server1"
+ echo "server2 tcpdump: $tuple_server2"
+ atf_fail "Received different IP:port on server1 than server2 (endpoint-independent)"
+ fi
+}
+
+atf_test_case "endpoint_independent_compat" "cleanup"
+endpoint_independent_compat_head()
+{
+ atf_set descr 'Test that a client behind NAT gets the same external IP:port for different servers'
+ atf_set require.user root
+}
+
+endpoint_independent_compat_body()
+{
+ endpoint_independent_setup # Sets ${epair_…} variables
+
+ endpoint_independent_common \
+ "nat on ${epair_nat}a inet from ! (${epair_nat}a) to any -> (${epair_nat}a)" \
+ "nat on ${epair_nat}a inet from ! (${epair_nat}a) to any -> (${epair_nat}a) endpoint-independent"
+}
+
+endpoint_independent_compat_cleanup()
+{
+ pft_cleanup
+ rm -f server1.out
+ rm -f server2.out
+}
+
+atf_test_case "endpoint_independent_pass" "cleanup"
+endpoint_independent_pass_head()
+{
+ atf_set descr 'Test that a client behind NAT gets the same external IP:port for different servers'
+ atf_set require.user root
+}
+
+endpoint_independent_pass_body()
+{
+ endpoint_independent_setup # Sets ${epair_…} variables
+
+ endpoint_independent_common \
+ "pass out on ${epair_nat}a inet from ! (${epair_nat}a) to any nat-to (${epair_nat}a) keep state" \
+ "pass out on ${epair_nat}a inet from ! (${epair_nat}a) to any nat-to (${epair_nat}a) endpoint-independent keep state"
+
+}
+
+endpoint_independent_pass_cleanup()
+{
+ pft_cleanup
+ rm -f server1.out
+ rm -f server2.out
+}
+
+nested_anchor_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nat6_nolinklocal" "cleanup"
+nat6_nolinklocal_head()
+{
+ atf_set descr 'Ensure we do not use link-local addresses'
+ atf_set require.user root
+}
+
+nat6_nolinklocal_body()
+{
+ pft_init
+
+ epair_nat=$(vnet_mkepair)
+ epair_echo=$(vnet_mkepair)
+
+ vnet_mkjail nat ${epair_nat}b ${epair_echo}a
+ vnet_mkjail echo ${epair_echo}b
+
+ ifconfig ${epair_nat}a inet6 2001:db8::2/64 no_dad up
+ route add -6 -net 2001:db8:1::/64 2001:db8::1
+
+ jexec nat ifconfig ${epair_nat}b inet6 2001:db8::1/64 no_dad up
+ jexec nat ifconfig ${epair_echo}a inet6 2001:db8:1::1/64 no_dad up
+ jexec nat sysctl net.inet6.ip6.forwarding=1
+
+ jexec echo ifconfig ${epair_echo}b inet6 2001:db8:1::2/64 no_dad up
+ # Ensure we can't reply to link-local pings
+ jexec echo pfctl -e
+ pft_set_rules echo \
+ "pass" \
+ "block in inet6 proto icmp6 from fe80::/10 to any icmp6-type echoreq"
+
+ jexec nat pfctl -e
+ pft_set_rules nat \
+ "nat pass on ${epair_echo}a inet6 from 2001:db8::/64 to any -> (${epair_echo}a)" \
+ "pass"
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ ping -6 -c 1 2001:db8::1
+ for i in `seq 0 10`
+ do
+ atf_check -s exit:0 -o ignore \
+ ping -6 -c 1 2001:db8:1::2
+ done
+}
+
+nat6_nolinklocal_cleanup()
+{
+ pft_cleanup
+}
+
+empty_table_common()
+{
+ option=$1
+
+ pft_init
+
+ epair_wan=$(vnet_mkepair)
+ epair_lan=$(vnet_mkepair)
+
+ vnet_mkjail srv ${epair_wan}a
+ jexec srv ifconfig ${epair_wan}a 192.0.2.2/24 up
+
+ vnet_mkjail rtr ${epair_wan}b ${epair_lan}a
+ jexec rtr ifconfig ${epair_wan}b 192.0.2.1/24 up
+ jexec rtr ifconfig ${epair_lan}a 198.51.100.1/24 up
+ jexec rtr sysctl net.inet.ip.forwarding=1
+
+ ifconfig ${epair_lan}b 198.51.100.2/24 up
+ route add default 198.51.100.1
+
+ jexec rtr pfctl -e
+ pft_set_rules rtr \
+ "table <empty>" \
+ "nat on ${epair_wan}b inet from 198.51.100.0/24 -> <empty> ${option}" \
+ "pass"
+
+ # Sanity checks
+ atf_check -s exit:0 -o ignore \
+ jexec rtr ping -c 1 192.0.2.2
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 198.51.100.1
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.1
+
+ # Provoke divide by zero
+ ping -c 1 192.0.2.2
+ true
+}
+
+atf_test_case "empty_table_source_hash" "cleanup"
+empty_table_source_hash_head()
+{
+ atf_set descr 'Test source-hash on an emtpy table'
+ atf_set require.user root
+}
+
+empty_table_source_hash_body()
+{
+ empty_table_common "source-hash"
+}
+
+empty_table_source_hash_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "empty_table_random" "cleanup"
+empty_table_random_head()
+{
+ atf_set descr 'Test random on an emtpy table'
+ atf_set require.user root
+}
+
+empty_table_random_body()
+{
+ empty_table_common "random"
+}
+
+empty_table_random_cleanup()
+{
+ pft_cleanup
+}
+
+no_addrs_common()
+{
+ option=$1
+
+ pft_init
+
+ epair_wan=$(vnet_mkepair)
+ epair_lan=$(vnet_mkepair)
+
+ vnet_mkjail srv ${epair_wan}a
+ jexec srv ifconfig ${epair_wan}a 192.0.2.2/24 up
+
+ vnet_mkjail rtr ${epair_wan}b ${epair_lan}a
+ jexec rtr route add -net 192.0.2.0/24 -iface ${epair_wan}b
+ jexec rtr ifconfig ${epair_lan}a 198.51.100.1/24 up
+ jexec rtr sysctl net.inet.ip.forwarding=1
+
+ ifconfig ${epair_lan}b 198.51.100.2/24 up
+ route add default 198.51.100.1
+
+ jexec rtr pfctl -e
+ pft_set_rules rtr \
+ "nat on ${epair_wan}b inet from 198.51.100.0/24 -> (${epair_wan}b) ${option}" \
+ "pass"
+
+ # Provoke divide by zero
+ ping -c 1 192.0.2.2
+ true
+}
+
+atf_test_case "no_addrs_source_hash" "cleanup"
+no_addrs_source_hash_head()
+{
+ atf_set descr 'Test source-hash on an interface with no addresses'
+ atf_set require.user root
+}
+
+no_addrs_source_hash_body()
+{
+ no_addrs_common "source-hash"
+}
+
+no_addrs_source_hash_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "no_addrs_random" "cleanup"
+no_addrs_random_head()
+{
+ atf_set descr 'Test random on an interface with no addresses'
+ atf_set require.user root
+}
+
+no_addrs_random_body()
+{
+ no_addrs_common "random"
+}
+
+no_addrs_random_cleanup()
+{
+ pft_cleanup
+}
+
+nat_pass_head()
+{
+ atf_set descr 'IPv4 NAT on pass rule'
+ atf_set require.user root
+ atf_set require.progs scapy
+}
+
+nat_pass_body()
+{
+ setup_router_server_ipv4
+ # Delete the route back to make sure that the traffic has been NAT-ed
+ jexec server route del -net ${net_tester} ${net_server_host_router}
+
+ pft_set_rules router \
+ "block" \
+ "pass in on ${epair_tester}b inet proto tcp keep state" \
+ "pass out on ${epair_server}a inet proto tcp nat-to ${epair_server}a keep state"
+
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4201
+
+ jexec router pfctl -qvvsr
+ jexec router pfctl -qvvss
+ jexec router ifconfig
+ jexec router netstat -rn
+}
+
+nat_pass_cleanup()
+{
+ pft_cleanup
+}
+
+nat_match_head()
+{
+ atf_set descr 'IPv4 NAT on match rule'
+ atf_set require.user root
+ atf_set require.progs scapy
+}
+
+nat_match_body()
+{
+ setup_router_server_ipv4
+ # Delete the route back to make sure that the traffic has been NAT-ed
+ jexec server route del -net ${net_tester} ${net_server_host_router}
+
+ # NAT is applied during ruleset evaluation:
+ # rules after "match" match on NAT-ed address
+ pft_set_rules router \
+ "block" \
+ "pass in on ${epair_tester}b inet proto tcp keep state" \
+ "match out on ${epair_server}a inet proto tcp nat-to ${epair_server}a" \
+ "pass out on ${epair_server}a inet proto tcp from ${epair_server}a keep state"
+
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4201
+
+ jexec router pfctl -qvvsr
+ jexec router pfctl -qvvss
+ jexec router ifconfig
+ jexec router netstat -rn
+}
+
+nat_match_cleanup()
+{
+ pft_cleanup
+}
+
+map_e_common()
+{
+ NC_TRY_COUNT=12
+
+ pft_init
+
+ epair_map_e=$(vnet_mkepair)
+ epair_echo=$(vnet_mkepair)
+
+ vnet_mkjail map_e ${epair_map_e}b ${epair_echo}a
+ vnet_mkjail echo ${epair_echo}b
+
+ ifconfig ${epair_map_e}a 192.0.2.2/24 up
+ route add -net 198.51.100.0/24 192.0.2.1
+
+ jexec map_e ifconfig ${epair_map_e}b 192.0.2.1/24 up
+ jexec map_e ifconfig ${epair_echo}a 198.51.100.1/24 up
+ jexec map_e sysctl net.inet.ip.forwarding=1
+
+ jexec echo ifconfig ${epair_echo}b 198.51.100.2/24 up
+ jexec echo /usr/sbin/inetd -p ${PWD}/inetd-echo.pid $(atf_get_srcdir)/echo_inetd.conf
+
+ # Enable pf!
+ jexec map_e pfctl -e
+}
+
+atf_test_case "map_e_compat" "cleanup"
+map_e_compat_head()
+{
+ atf_set descr 'map-e-portset test'
+ atf_set require.user root
+}
+
+map_e_compat_body()
+{
+ map_e_common
+
+ pft_set_rules map_e \
+ "nat pass on ${epair_echo}a inet from 192.0.2.0/24 to any -> (${epair_echo}a) map-e-portset 2/12/0x342"
+
+ # Only allow specified ports.
+ jexec echo pfctl -e
+ pft_set_rules echo "block return all" \
+ "pass in on ${epair_echo}b inet proto tcp from 198.51.100.1 port 19720:19723 to (${epair_echo}b) port 7" \
+ "pass in on ${epair_echo}b inet proto tcp from 198.51.100.1 port 36104:36107 to (${epair_echo}b) port 7" \
+ "pass in on ${epair_echo}b inet proto tcp from 198.51.100.1 port 52488:52491 to (${epair_echo}b) port 7" \
+ "set skip on lo"
+
+ i=0
+ while [ ${i} -lt ${NC_TRY_COUNT} ]
+ do
+ echo "foo ${i}" | timeout 2 nc -N 198.51.100.2 7
+ if [ $? -ne 0 ]; then
+ atf_fail "nc failed (${i})"
+ fi
+ i=$((${i}+1))
+ done
+}
+
+map_e_compat_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_test_case "map_e_pass" "cleanup"
+map_e_pass_head()
+{
+ atf_set descr 'map-e-portset test'
+ atf_set require.user root
+}
+
+map_e_pass_body()
+{
+ map_e_common
+
+ pft_set_rules map_e \
+ "pass out on ${epair_echo}a inet from 192.0.2.0/24 to any nat-to (${epair_echo}a) map-e-portset 2/12/0x342 keep state"
+
+ jexec map_e pfctl -qvvsr
+
+ # Only allow specified ports.
+ jexec echo pfctl -e
+ pft_set_rules echo "block return all" \
+ "pass in on ${epair_echo}b inet proto tcp from 198.51.100.1 port 19720:19723 to (${epair_echo}b) port 7" \
+ "pass in on ${epair_echo}b inet proto tcp from 198.51.100.1 port 36104:36107 to (${epair_echo}b) port 7" \
+ "pass in on ${epair_echo}b inet proto tcp from 198.51.100.1 port 52488:52491 to (${epair_echo}b) port 7" \
+ "set skip on lo"
+
+ i=0
+ while [ ${i} -lt ${NC_TRY_COUNT} ]
+ do
+ echo "foo ${i}" | timeout 2 nc -N 198.51.100.2 7
+ if [ $? -ne 0 ]; then
+ atf_fail "nc failed (${i})"
+ fi
+ i=$((${i}+1))
+ done
+}
+
+map_e_pass_cleanup()
+{
+ pft_cleanup
+}
+
+binat_compat_head()
+{
+ atf_set descr 'IPv4 BINAT with nat ruleset'
+ atf_set require.user root
+ atf_set require.progs scapy
+}
+
+binat_compat_body()
+{
+ setup_router_server_ipv4
+ # Delete the route back to make sure that the traffic has been NAT-ed
+ jexec server route del -net ${net_tester} ${net_server_host_router}
+
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "set ruleset-optimization none" \
+ "binat on ${epair_server}a inet proto tcp from ${net_tester_host_tester} to any tag sometag -> ${epair_server}a" \
+ "block" \
+ "pass in on ${epair_tester}b inet proto tcp !tagged sometag keep state" \
+ "pass out on ${epair_server}a inet proto tcp tagged sometag keep state" \
+ "pass in on ${epair_server}a inet proto tcp tagged sometag keep state" \
+ "pass out on ${epair_tester}b inet proto tcp tagged sometag keep state"
+
+ # Test the outbound NAT part of BINAT.
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4201
+
+ states=$(mktemp) || exit 1
+ jexec router pfctl -qvss | normalize_pfctl_s > $states
+
+ for state_regexp in \
+ "${epair_tester}b tcp ${net_server_host_server}:9 <- ${net_tester_host_tester}:4201 .* 3:2 pkts,.* rule 1" \
+ "${epair_server}a tcp ${net_server_host_router}:4201 \(${net_tester_host_tester}:4201\) -> ${net_server_host_server}:9 .* 3:2 pkts,.* rule 2" \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+
+ # Test the inbound RDR part of BINAT.
+ # The "tester" becomes "server" and vice versa.
+ inetd_conf=$(mktemp)
+ echo "discard stream tcp nowait root internal" > $inetd_conf
+ inetd -p ${PWD}/inetd_tester.pid $inetd_conf
+
+ atf_check -s exit:0 \
+ jexec server ${common_dir}/pft_ping.py \
+ --ping-type=tcp3way --send-sport=4202 \
+ --sendif ${epair_server}b \
+ --to ${net_server_host_router} \
+ --replyif ${epair_server}b
+
+ states=$(mktemp) || exit 1
+ jexec router pfctl -qvss | normalize_pfctl_s > $states
+
+ for state_regexp in \
+ "${epair_server}a tcp ${net_tester_host_tester}:9 \(${net_server_host_router}:9\) <- ${net_server_host_server}:4202 .* 3:2 pkts,.* rule 3" \
+ "${epair_tester}b tcp ${net_server_host_server}:4202 -> ${net_tester_host_tester}:9 .* 3:2 pkts,.* rule 4" \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+binat_compat_cleanup()
+{
+ pft_cleanup
+ kill $(cat ${PWD}/inetd_tester.pid)
+}
+
+binat_match_head()
+{
+ atf_set descr 'IPv4 BINAT with nat ruleset'
+ atf_set require.user root
+ atf_set require.progs scapy
+}
+
+binat_match_body()
+{
+ setup_router_server_ipv4
+ # Delete the route back to make sure that the traffic has been NAT-ed
+ jexec server route del -net ${net_tester} ${net_server_host_router}
+
+ # The "binat-to" rule expands to 2 rules so the ""pass" rules start at 3!
+ pft_set_rules router \
+ "set state-policy if-bound" \
+ "set ruleset-optimization none" \
+ "block" \
+ "match on ${epair_server}a inet proto tcp from ${net_tester_host_tester} to any tag sometag binat-to ${epair_server}a" \
+ "pass in on ${epair_tester}b inet proto tcp !tagged sometag keep state" \
+ "pass out on ${epair_server}a inet proto tcp tagged sometag keep state" \
+ "pass in on ${epair_server}a inet proto tcp tagged sometag keep state" \
+ "pass out on ${epair_tester}b inet proto tcp tagged sometag keep state"
+
+ # Test the outbound NAT part of BINAT.
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4201
+
+ states=$(mktemp) || exit 1
+ jexec router pfctl -qvss | normalize_pfctl_s > $states
+
+ for state_regexp in \
+ "${epair_tester}b tcp ${net_server_host_server}:9 <- ${net_tester_host_tester}:4201 .* 3:2 pkts,.* rule 3" \
+ "${epair_server}a tcp ${net_server_host_router}:4201 \(${net_tester_host_tester}:4201\) -> ${net_server_host_server}:9 .* 3:2 pkts,.* rule 4" \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+
+ # Test the inbound RDR part of BINAT.
+ # The "tester" becomes "server" and vice versa.
+ inetd_conf=$(mktemp)
+ echo "discard stream tcp nowait root internal" > $inetd_conf
+ inetd -p ${PWD}/inetd_tester.pid $inetd_conf
+
+ atf_check -s exit:0 \
+ jexec server ${common_dir}/pft_ping.py \
+ --ping-type=tcp3way --send-sport=4202 \
+ --sendif ${epair_server}b \
+ --to ${net_server_host_router} \
+ --replyif ${epair_server}b
+
+ states=$(mktemp) || exit 1
+ jexec router pfctl -qvss | normalize_pfctl_s > $states
+
+ for state_regexp in \
+ "${epair_server}a tcp ${net_tester_host_tester}:9 \(${net_server_host_router}:9\) <- ${net_server_host_server}:4202 .* 3:2 pkts,.* rule 5" \
+ "${epair_tester}b tcp ${net_server_host_server}:4202 -> ${net_tester_host_tester}:9 .* 3:2 pkts,.* rule 6" \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+binat_match_cleanup()
+{
+ pft_cleanup
+ kill $(cat ${PWD}/inetd_tester.pid)
+}
+
+atf_test_case "empty_pool" "cleanup"
+empty_pool_head()
+{
+ atf_set descr 'NAT with empty pool'
+ atf_set require.user root
+}
+
+empty_pool_body()
+{
+ pft_init
+ setup_router_server_ipv6
+
+
+ pft_set_rules router \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in on ${epair_tester}b" \
+ "pass out on ${epair_server}a inet6 from any to ${net_server_host_server} nat-to <nonexistent>" \
+
+ # pf_map_addr_sn() won't be able to pick a target address, because
+ # the table used in redireciton pool is empty. Packet will not be
+ # forwarded, error counter will be increased.
+ ping_server_check_reply exit:1
+ # Ignore warnings about not-loaded ALTQ
+ atf_check -o "match:map-failed +1 +" -x "jexec router pfctl -qvvsi 2> /dev/null"
+}
+
+empty_pool_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "exhaust"
+ atf_add_test_case "nested_anchor"
+ atf_add_test_case "endpoint_independent_compat"
+ atf_add_test_case "endpoint_independent_pass"
+ atf_add_test_case "nat6_nolinklocal"
+ atf_add_test_case "empty_table_source_hash"
+ atf_add_test_case "no_addrs_source_hash"
+ atf_add_test_case "empty_table_random"
+ atf_add_test_case "no_addrs_random"
+ atf_add_test_case "map_e_compat"
+ atf_add_test_case "map_e_pass"
+ atf_add_test_case "nat_pass"
+ atf_add_test_case "nat_match"
+ atf_add_test_case "binat_compat"
+ atf_add_test_case "binat_match"
+ atf_add_test_case "empty_pool"
+}
diff --git a/tests/sys/netpfil/pf/nat64.py b/tests/sys/netpfil/pf/nat64.py
new file mode 100644
index 000000000000..a5890fc4a161
--- /dev/null
+++ b/tests/sys/netpfil/pf/nat64.py
@@ -0,0 +1,328 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import pytest
+import selectors
+import socket
+import sys
+from utils import DelayedSend
+from atf_python.sys.net.tools import ToolsHelper
+from atf_python.sys.net.vnet import VnetTestTemplate
+
+class TestNAT64(VnetTestTemplate):
+ REQUIRED_MODULES = [ "pf", "pflog" ]
+ TOPOLOGY = {
+ "vnet1": {"ifaces": ["if1"]},
+ "vnet2": {"ifaces": ["if1", "if2"]},
+ "vnet3": {"ifaces": ["if2", "if3"]},
+ "vnet4": {"ifaces": ["if3"]},
+ "if1": {"prefixes6": [("2001:db8::2/64", "2001:db8::1/64")]},
+ "if2": {"prefixes4": [("192.0.2.1/24", "192.0.2.2/24")]},
+ "if3": {"prefixes4": [("198.51.100.1/24", "198.51.100.2/24")]}
+ }
+
+ def vnet4_handler(self, vnet):
+ ToolsHelper.print_output("/sbin/route add default 198.51.100.1")
+
+ def vnet3_handler(self, vnet):
+ ToolsHelper.print_output("/sbin/sysctl net.inet.ip.forwarding=1")
+ ToolsHelper.print_output("/sbin/sysctl net.inet.ip.ttl=62")
+ ToolsHelper.print_output("/sbin/sysctl net.inet.udp.checksum=0")
+
+ sel = selectors.DefaultSelector()
+ t = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ t.bind(("0.0.0.0", 1234))
+ t.setblocking(False)
+ t.listen()
+ sel.register(t, selectors.EVENT_READ, data=None)
+
+ u = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ u.bind(("0.0.0.0", 4444))
+ u.setblocking(False)
+ sel.register(u, selectors.EVENT_READ, data="UDP")
+
+ while True:
+ events = sel.select(timeout=20)
+ for key, mask in events:
+ sock = key.fileobj
+ if key.data is None:
+ conn, addr = sock.accept()
+ print(f"Accepted connection from {addr}")
+ data = types.SimpleNamespace(addr=addr, inb=b"", outb=b"")
+ events = selectors.EVENT_READ | selectors.EVENT_WRITE
+ sel.register(conn, events, data=data)
+ elif key.data == "UDP":
+ recv_data, addr = sock.recvfrom(1024)
+ print(f"Received UDP {recv_data} from {addr}")
+ sock.sendto(b"foo", addr)
+ else:
+ if mask & selectors.EVENT_READ:
+ recv_data = sock.recv(1024)
+ print(f"Received TCP {recv_data}")
+ sock.send(b"foo")
+ else:
+ print("Unknown event?")
+ t.close()
+ u.close()
+ return
+
+ def vnet2_handler(self, vnet):
+ ifname = vnet.iface_alias_map["if1"].name
+
+ ToolsHelper.print_output("/sbin/sysctl net.inet6.ip6.forwarding=1")
+ ToolsHelper.print_output("/sbin/route add default 192.0.2.2")
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.pf_rules([
+ "pass inet6 proto icmp6",
+ "pass in on %s inet6 af-to inet from 192.0.2.1" % ifname])
+
+ vnet.pipe.send(socket.if_nametoindex("pflog0"))
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_tcp_rst(self):
+ ToolsHelper.print_output("/sbin/route -6 add default 2001:db8::1")
+
+ import scapy.all as sp
+
+ # Send a SYN
+ packet = sp.IPv6(dst="64:ff9b::192.0.2.2") \
+ / sp.TCP(dport=1222, flags="S")
+
+ # Get a reply
+ reply = sp.sr1(packet)
+
+ # We expect to get a RST here.
+ tcp = reply.getlayer(sp.TCP)
+ assert tcp
+ assert "R" in tcp.flags
+
+ # Now try to SYN to an open port
+ packet = sp.IPv6(dst="64:ff9b::192.0.2.2") \
+ / sp.TCP(dport=1234, flags="S")
+ reply = sp.sr1(packet)
+
+ tcp = reply.getlayer(sp.TCP)
+ assert tcp
+
+ # We don't get RST
+ assert "R" not in tcp.flags
+
+ # We do get SYN|ACK
+ assert "S" in tcp.flags
+ assert "A" in tcp.flags
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_udp_port_closed(self):
+ ToolsHelper.print_output("/sbin/route -6 add default 2001:db8::1")
+
+ import scapy.all as sp
+
+ packet = sp.IPv6(dst="64:ff9b::192.0.2.2") \
+ / sp.UDP(dport=1222) / sp.Raw("bar")
+ reply = sp.sr1(packet, timeout=3)
+ print(reply.show())
+
+ # We expect an ICMPv6 error, not a UDP reply
+ assert not reply.getlayer(sp.UDP)
+ icmp = reply.getlayer(sp.ICMPv6DestUnreach)
+ assert icmp
+ assert icmp.type == 1
+ assert icmp.code == 4
+ udp = reply.getlayer(sp.UDPerror)
+ assert udp
+ assert udp.dport == 1222
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_address_unreachable(self):
+ ToolsHelper.print_output("/sbin/route -6 add default 2001:db8::1")
+
+ import scapy.all as sp
+
+ packet = sp.IPv6(dst="64:ff9b::203.0.113.2") \
+ / sp.UDP(dport=1222) / sp.Raw("bar")
+ reply = sp.sr1(packet, timeout=3)
+ print(reply.show())
+
+ # We expect an ICMPv6 error, not a UDP reply
+ assert not reply.getlayer(sp.UDP)
+ icmp = reply.getlayer(sp.ICMPv6DestUnreach)
+ assert icmp
+ assert icmp.type == 1
+ assert icmp.code == 0
+ udp = reply.getlayer(sp.UDPerror)
+ assert udp
+ assert udp.dport == 1222
+
+ # Check the hop limit
+ ip6 = reply.getlayer(sp.IPv6)
+ assert ip6.hlim == 61
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_udp_checksum(self):
+ ToolsHelper.print_output("/sbin/route -6 add default 2001:db8::1")
+
+ import scapy.all as sp
+
+ # Send an outbound UDP packet to establish state
+ packet = sp.IPv6(dst="64:ff9b::192.0.2.2") \
+ / sp.UDP(sport=3333, dport=4444) / sp.Raw("foo")
+
+ # Get a reply
+ # We'll send the reply without UDP checksum on the IPv4 side
+ # but that's not valid for IPv6, so expect pf to update the checksum.
+ reply = sp.sr1(packet, timeout=5)
+
+ udp = reply.getlayer(sp.UDP)
+ assert udp
+ assert udp.chksum != 0
+
+ def common_test_source_addr(self, packet):
+ vnet = self.vnet_map["vnet1"]
+ sendif = vnet.iface_alias_map["if1"].name
+
+ import scapy.all as sp
+
+ print("Outbound:\n")
+ packet.show()
+
+ s = DelayedSend(packet)
+
+ # We expect an ICMPv6 error here, where we'll verify the source address of
+ # the outer packet
+ packets = sp.sniff(iface=sendif, timeout=5)
+
+ for reply in packets:
+ print("Reply:\n")
+ reply.show()
+ icmp = reply.getlayer(sp.ICMPv6TimeExceeded)
+ if not icmp:
+ continue
+
+ ip = reply.getlayer(sp.IPv6)
+ assert icmp
+ assert ip.src == "64:ff9b::c000:202"
+ return reply
+
+ # If we don't find the packet we expect to see
+ assert False
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_source_addr_tcp(self):
+ ToolsHelper.print_output("/sbin/route -6 add default 2001:db8::1")
+ import scapy.all as sp
+
+ packet = sp.IPv6(dst="64:ff9b::198.51.100.2", hlim=2) \
+ / sp.TCP(sport=1111, dport=2222, flags="S")
+ self.common_test_source_addr(packet)
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_source_addr_udp(self):
+ ToolsHelper.print_output("/sbin/route -6 add default 2001:db8::1")
+ import scapy.all as sp
+
+ packet = sp.IPv6(dst="64:ff9b::198.51.100.2", hlim=2) \
+ / sp.UDP(sport=1111, dport=2222) / sp.Raw("foo")
+ self.common_test_source_addr(packet)
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_source_addr_sctp(self):
+ ToolsHelper.print_output("/sbin/route -6 add default 2001:db8::1")
+ import scapy.all as sp
+
+ packet = sp.IPv6(dst="64:ff9b::198.51.100.2", hlim=2) \
+ / sp.SCTP(sport=1111, dport=2222) \
+ / sp.SCTPChunkInit(init_tag=1, n_in_streams=1, n_out_streams=1, a_rwnd=1500)
+ self.common_test_source_addr(packet)
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_source_addr_icmp(self):
+ ToolsHelper.print_output("/sbin/route -6 add default 2001:db8::1")
+ import scapy.all as sp
+
+ packet = sp.IPv6(dst="64:ff9b::198.51.100.2", hlim=2) \
+ / sp.ICMPv6EchoRequest() / sp.Raw("foo")
+ reply = self.common_test_source_addr(packet)
+ icmp = reply.getlayer(sp.ICMPv6EchoRequest)
+ assert icmp
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_bad_len(self):
+ """
+ PR 288224: we can panic if the IPv6 plen is longer than the packet length.
+ """
+ ToolsHelper.print_output("/sbin/route -6 add default 2001:db8::1")
+ import scapy.all as sp
+
+ packet = sp.IPv6(dst="64:ff9b::198.51.100.2", hlim=2, plen=512) \
+ / sp.ICMPv6EchoRequest() / sp.Raw("foo")
+ reply = sp.sr1(packet, timeout=3)
+ # We don't expect a reply to a corrupted packet
+ assert not reply
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_noip6(self):
+ """
+ PR 288263: link-local target address in icmp6 ADVERT can cause NULL deref
+ """
+ ifname = self.vnet.iface_alias_map["if1"].name
+ gw_mac = self.vnet.iface_alias_map["if1"].epairb.ether
+ scopeid = self.wait_object(self.vnet_map["vnet2"].pipe)
+ ToolsHelper.print_output("/sbin/route -6 add default 2001:db8::1")
+
+ import scapy.all as sp
+
+ pkt = sp.Ether(dst=gw_mac) \
+ / sp.IPv6(dst="64:ff9b::203.0.113.2") \
+ / sp.ICMPv6ND_NA(tgt="FFA2:%x:2821:125F:1D27:B3B2:3F6F:C43C" % scopeid)
+ pkt.show()
+ sp.hexdump(pkt)
+ s = DelayedSend(pkt, sendif=ifname)
+
+ packets = sp.sniff(iface=ifname, timeout=5)
+ for r in packets:
+ r.show()
+
+ # Try scope id that likely doesn't have an interface at all
+ pkt = sp.Ether(dst=gw_mac) \
+ / sp.IPv6(dst="64:ff9b::203.0.113.2") \
+ / sp.ICMPv6ND_NA(tgt="FFA2:%x:2821:125F:1D27:B3B2:3F6F:C43C" % 255)
+ pkt.show()
+ sp.hexdump(pkt)
+ s = DelayedSend(pkt, sendif=ifname)
+
+ packets = sp.sniff(iface=ifname, timeout=5)
+ for r in packets:
+ r.show()
diff --git a/tests/sys/netpfil/pf/nat64.sh b/tests/sys/netpfil/pf/nat64.sh
new file mode 100644
index 000000000000..0bba1470c4c5
--- /dev/null
+++ b/tests/sys/netpfil/pf/nat64.sh
@@ -0,0 +1,1056 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+nat64_setup_base()
+{
+ pft_init
+
+ epair_link=$(vnet_mkepair)
+ epair=$(vnet_mkepair)
+
+ ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad
+ route -6 add default 2001:db8::1
+
+ vnet_mkjail rtr ${epair}b ${epair_link}a
+ jexec rtr ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad
+ jexec rtr ifconfig ${epair_link}a 192.0.2.1/24 up
+
+ vnet_mkjail dst ${epair_link}b
+ jexec dst ifconfig ${epair_link}b 192.0.2.2/24 up
+ jexec dst route add default 192.0.2.1
+
+ # Sanity checks
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 2001:db8::1
+ atf_check -s exit:0 -o ignore \
+ jexec dst ping -c 1 192.0.2.1
+
+ jexec rtr pfctl -e
+}
+
+nat64_setup_in()
+{
+ nat64_setup_base
+ pft_set_rules rtr \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "pass in on ${epair}b inet6 from any to 64:ff9b::/96 af-to inet from (${epair_link}a)"
+}
+
+nat64_setup_out()
+{
+ nat64_setup_base
+ jexec rtr sysctl net.inet6.ip6.forwarding=1
+ # AF translation happens post-routing, traffic must be directed
+ # towards the outbound interface using routes for the original AF.
+ # jexec rtr ifconfig ${epair_link}a inet6 2001:db8:2::1/64 up no_dad
+ jexec rtr route add -inet6 64:ff9b::/96 -iface ${epair_link}a;
+ pft_set_rules rtr \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "pass quick inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in quick on ${epair}b from any to 64:ff9b::/96" \
+ "pass out quick on ${epair_link}a from any to 64:ff9b::/96 af-to inet from (${epair_link}a)" \
+ "block"
+}
+
+atf_test_case "icmp_echo_in" "cleanup"
+icmp_echo_in_head()
+{
+ atf_set descr 'Basic NAT64 ICMP echo test on inbound interface'
+ atf_set require.user root
+}
+
+icmp_echo_in_body()
+{
+ nat64_setup_in
+
+ # One ping
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 64:ff9b::192.0.2.2
+
+ # Make sure packets make it even when state is established
+ atf_check -s exit:0 \
+ -o match:'5 packets transmitted, 5 packets received, 0.0% packet loss' \
+ ping6 -c 5 64:ff9b::192.0.2.2
+}
+
+icmp_echo_in_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "icmp_echo_out" "cleanup"
+icmp_echo_out_head()
+{
+ atf_set descr 'Basic NAT64 ICMP echo test on outbound interface'
+ atf_set require.user root
+}
+
+icmp_echo_out_body()
+{
+ nat64_setup_out
+
+ # One ping
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 64:ff9b::192.0.2.2
+
+ # Make sure packets make it even when state is established
+ atf_check -s exit:0 \
+ -o match:'5 packets transmitted, 5 packets received, 0.0% packet loss' \
+ ping6 -c 5 64:ff9b::192.0.2.2
+}
+
+icmp_echo_out_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "fragmentation_in" "cleanup"
+fragmentation_in_head()
+{
+ atf_set descr 'Test fragmented packets on inbound interface'
+ atf_set require.user root
+}
+
+fragmentation_in_body()
+{
+ nat64_setup_in
+
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 -s 1280 64:ff9b::192.0.2.2
+
+ atf_check -s exit:0 \
+ -o match:'3 packets transmitted, 3 packets received, 0.0% packet loss' \
+ ping6 -c 3 -s 2000 64:ff9b::192.0.2.2
+ atf_check -s exit:0 \
+ -o match:'3 packets transmitted, 3 packets received, 0.0% packet loss' \
+ ping6 -c 3 -s 10000 -b 20000 64:ff9b::192.0.2.2
+}
+
+fragmentation_in_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "fragmentation_out" "cleanup"
+fragmentation_out_head()
+{
+ atf_set descr 'Test fragmented packets on outbound interface'
+ atf_set require.user root
+}
+
+fragmentation_out_body()
+{
+ nat64_setup_out
+
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 -s 1280 64:ff9b::192.0.2.2
+
+ atf_check -s exit:0 \
+ -o match:'3 packets transmitted, 3 packets received, 0.0% packet loss' \
+ ping6 -c 3 -s 2000 64:ff9b::192.0.2.2
+ atf_check -s exit:0 \
+ -o match:'3 packets transmitted, 3 packets received, 0.0% packet loss' \
+ ping6 -c 3 -s 10000 -b 20000 64:ff9b::192.0.2.2
+}
+
+fragmentation_out_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "tcp_in" "cleanup"
+tcp_in_head()
+{
+ atf_set descr 'TCP NAT64 test on inbound interface'
+ atf_set require.user root
+}
+
+tcp_in_body()
+{
+ nat64_setup_in
+
+ echo "foo" | jexec dst nc -l 1234 &
+
+ # Sanity check & delay for nc startup
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 64:ff9b::192.0.2.2
+
+ rcv=$(nc -w 3 -6 64:ff9b::c000:202 1234)
+ if [ "${rcv}" != "foo" ];
+ then
+ echo "rcv=${rcv}"
+ atf_fail "Failed to connect to TCP server"
+ fi
+}
+
+tcp_in_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "tcp_out" "cleanup"
+tcp_out_head()
+{
+ atf_set descr 'TCP NAT64 test on outbound interface'
+ atf_set require.user root
+}
+
+tcp_out_body()
+{
+ nat64_setup_out
+
+ echo "foo" | jexec dst nc -l 1234 &
+
+ # Sanity check & delay for nc startup
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 64:ff9b::192.0.2.2
+
+ rcv=$(nc -w 3 -6 64:ff9b::c000:202 1234)
+ if [ "${rcv}" != "foo" ];
+ then
+ echo "rcv=${rcv}"
+ atf_fail "Failed to connect to TCP server"
+ fi
+}
+
+tcp_out_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "udp_in" "cleanup"
+udp_in_head()
+{
+ atf_set descr 'UDP NAT64 test on inbound interface'
+ atf_set require.user root
+}
+
+udp_in_body()
+{
+ nat64_setup_in
+
+ echo "foo" | jexec dst nc -u -l 1234 &
+
+ # Sanity check & delay for nc startup
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 64:ff9b::192.0.2.2
+
+ rcv=$(echo bar | nc -w 3 -6 -u 64:ff9b::c000:202 1234)
+ if [ "${rcv}" != "foo" ];
+ then
+ echo "rcv=${rcv}"
+ atf_fail "Failed to connect to UDP server"
+ fi
+}
+
+udp_in_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "udp_out" "cleanup"
+udp_out_head()
+{
+ atf_set descr 'UDP NAT64 test on outbound interface'
+ atf_set require.user root
+}
+
+udp_out_body()
+{
+ nat64_setup_out
+
+ echo "foo" | jexec dst nc -u -l 1234 &
+
+ # Sanity check & delay for nc startup
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 64:ff9b::192.0.2.2
+
+ rcv=$(echo bar | nc -w 3 -6 -u 64:ff9b::c000:202 1234)
+ if [ "${rcv}" != "foo" ];
+ then
+ echo "rcv=${rcv}"
+ atf_fail "Failed to connect to UDP server"
+ fi
+}
+
+udp_out_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "sctp_in" "cleanup"
+sctp_in_head()
+{
+ atf_set descr 'SCTP NAT64 test on inbound interface'
+ atf_set require.user root
+}
+
+sctp_in_body()
+{
+ nat64_setup_in
+ if ! kldstat -q -m sctp; then
+ atf_skip "This test requires SCTP"
+ fi
+
+ echo "foo" | jexec dst nc --sctp -N -l 1234 &
+
+ # Sanity check & delay for nc startup
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 64:ff9b::192.0.2.2
+
+ rcv=$(echo bar | nc --sctp -w 3 -6 64:ff9b::c000:202 1234)
+ if [ "${rcv}" != "foo" ];
+ then
+ echo "rcv=${rcv}"
+ atf_fail "Failed to connect to SCTP server"
+ fi
+}
+
+sctp_in_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "sctp_out" "cleanup"
+sctp_out_head()
+{
+ atf_set descr 'SCTP NAT64 test on outbound interface'
+ atf_set require.user root
+}
+
+sctp_out_body()
+{
+ nat64_setup_out
+ if ! kldstat -q -m sctp; then
+ atf_skip "This test requires SCTP"
+ fi
+
+ echo "foo" | jexec dst nc --sctp -N -l 1234 &
+
+ # Sanity check & delay for nc startup
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 64:ff9b::192.0.2.2
+
+ rcv=$(echo bar | nc --sctp -w 3 -6 64:ff9b::c000:202 1234)
+ if [ "${rcv}" != "foo" ];
+ then
+ echo "rcv=${rcv}"
+ atf_fail "Failed to connect to SCTP server"
+ fi
+}
+
+sctp_out_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "tos" "cleanup"
+tos_head()
+{
+ atf_set descr 'ToS translation test'
+ atf_set require.user root
+}
+
+tos_body()
+{
+ nat64_setup_in
+
+ # Ensure we can distinguish ToS on the destination
+ jexec dst pfctl -e
+ pft_set_rules dst \
+ "pass" \
+ "block in inet tos 8"
+
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 -z 4 64:ff9b::192.0.2.2
+ atf_check -s exit:2 -o ignore \
+ ping6 -c 1 -z 8 64:ff9b::192.0.2.2
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 -z 16 64:ff9b::192.0.2.2
+
+ jexec dst pfctl -sr -vv
+}
+
+tos_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "no_v4" "cleanup"
+no_v4_head()
+{
+ atf_set descr 'Test error handling when there is no IPv4 address to translate to'
+ atf_set require.user root
+}
+
+no_v4_body()
+{
+ pft_init
+
+ epair_link=$(vnet_mkepair)
+ epair=$(vnet_mkepair)
+
+ ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad
+ route -6 add default 2001:db8::1
+
+ vnet_mkjail rtr ${epair}b ${epair_link}a
+ jexec rtr ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad
+
+ vnet_mkjail dst ${epair_link}b
+ jexec dst ifconfig ${epair_link}b 192.0.2.2/24 up
+ jexec dst route add default 192.0.2.1
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 2001:db8::1
+
+ jexec rtr pfctl -e
+ pft_set_rules rtr \
+ "pass in on ${epair}b inet6 from any to 64:ff9b::/96 af-to inet from (${epair_link}a)"
+
+ atf_check -s exit:2 -o ignore \
+ ping6 -c 3 64:ff9b::192.0.2.2
+}
+
+no_v4_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "range" "cleanup"
+range_head()
+{
+ atf_set descr 'Test using an address range for the IPv4 side'
+ atf_set require.user root
+}
+
+range_body()
+{
+ pft_init
+
+ epair_link=$(vnet_mkepair)
+ epair=$(vnet_mkepair)
+
+ ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad
+ route -6 add default 2001:db8::1
+
+ vnet_mkjail rtr ${epair}b ${epair_link}a
+ jexec rtr ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad
+ jexec rtr ifconfig ${epair_link}a 192.0.2.2/24 up
+ jexec rtr ifconfig ${epair_link}a inet alias 192.0.2.3/24 up
+
+ vnet_mkjail dst ${epair_link}b
+ jexec dst ifconfig ${epair_link}b 192.0.2.254/24 up
+ jexec dst route add default 192.0.2.2
+
+ # Sanity checks
+ atf_check -s exit:0 -o ignore \
+ jexec rtr ping -c 1 192.0.2.254
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 2001:db8::1
+ atf_check -s exit:0 -o ignore \
+ jexec dst ping -c 1 192.0.2.2
+ atf_check -s exit:0 -o ignore \
+ jexec dst ping -c 1 192.0.2.3
+
+ jexec rtr pfctl -e
+ pft_set_rules rtr \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "pass in on ${epair}b inet6 from any to 64:ff9b::/96 af-to inet from 192.0.2.2/31 round-robin"
+
+ # Use pf to count sources
+ jexec dst pfctl -e
+ pft_set_rules dst \
+ "pass"
+
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 64:ff9b::192.0.2.254
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 64:ff9b::192.0.2.254
+
+ # Verify on dst that we saw different source addresses
+ atf_check -s exit:0 -o match:".*192.0.2.2.*" \
+ jexec dst pfctl -ss
+ atf_check -s exit:0 -o match:".*192.0.2.3.*" \
+ jexec dst pfctl -ss
+}
+
+range_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "pool" "cleanup"
+pool_head()
+{
+ atf_set descr 'Use a pool of IPv4 addresses'
+ atf_set require.user root
+}
+
+pool_body()
+{
+ pft_init
+
+ epair_link=$(vnet_mkepair)
+ epair=$(vnet_mkepair)
+
+ ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad
+ route -6 add default 2001:db8::1
+
+ vnet_mkjail rtr ${epair}b ${epair_link}a
+ jexec rtr ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad
+ jexec rtr ifconfig ${epair_link}a 192.0.2.1/24 up
+ jexec rtr ifconfig ${epair_link}a inet alias 192.0.2.3/24 up
+ jexec rtr ifconfig ${epair_link}a inet alias 192.0.2.4/24 up
+
+ vnet_mkjail dst ${epair_link}b
+ jexec dst ifconfig ${epair_link}b 192.0.2.2/24 up
+ jexec dst route add default 192.0.2.1
+
+ # Sanity checks
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 2001:db8::1
+ atf_check -s exit:0 -o ignore \
+ jexec dst ping -c 1 192.0.2.1
+
+ jexec rtr pfctl -e
+ pft_set_rules rtr \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "pass in on ${epair}b inet6 from any to 64:ff9b::/96 af-to inet from { 192.0.2.1, 192.0.2.3, 192.0.2.4 } round-robin"
+
+ # Use pf to count sources
+ jexec dst pfctl -e
+ pft_set_rules dst \
+ "pass"
+
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 64:ff9b::192.0.2.2
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 64:ff9b::192.0.2.2
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 64:ff9b::192.0.2.2
+
+ # Verify on dst that we saw different source addresses
+ atf_check -s exit:0 -o match:".*192.0.2.1.*" \
+ jexec dst pfctl -ss
+ atf_check -s exit:0 -o match:".*192.0.2.3.*" \
+ jexec dst pfctl -ss
+ atf_check -s exit:0 -o match:".*192.0.2.4.*" \
+ jexec dst pfctl -ss
+}
+
+pool_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_test_case "table"
+table_head()
+{
+ atf_set descr 'Check table restrictions'
+ atf_set require.user root
+}
+
+table_body()
+{
+ pft_init
+
+ # Round-robin and random are allowed
+ echo "pass in on epair inet6 from any to 64:ff9b::/96 af-to inet from <wanaddr> round-robin" | \
+ atf_check -s exit:0 \
+ pfctl -f -
+ echo "pass in on epair inet6 from any to 64:ff9b::/96 af-to inet from <wanaddr> random" | \
+ atf_check -s exit:0 \
+ pfctl -f -
+
+ # bitmask is not
+ echo "pass in on epair inet6 from any to 64:ff9b::/96 af-to inet from <wanaddr> bitmask" | \
+ atf_check -s exit:1 \
+ -e match:"tables are not supported by pool type" \
+ pfctl -f -
+}
+
+table_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "table_range" "cleanup"
+table_range_head()
+{
+ atf_set descr 'Test using an address range within a table for the IPv4 side'
+ atf_set require.user root
+}
+
+table_range_body()
+{
+ pft_init
+
+ epair_link=$(vnet_mkepair)
+ epair=$(vnet_mkepair)
+
+ ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad
+ route -6 add default 2001:db8::1
+
+ vnet_mkjail rtr ${epair}b ${epair_link}a
+ jexec rtr ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad
+ jexec rtr ifconfig ${epair_link}a 192.0.2.2/24 up
+ jexec rtr ifconfig ${epair_link}a inet alias 192.0.2.3/24 up
+
+ vnet_mkjail dst ${epair_link}b
+ jexec dst ifconfig ${epair_link}b 192.0.2.254/24 up
+ jexec dst route add default 192.0.2.2
+
+ # Sanity checks
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 2001:db8::1
+ atf_check -s exit:0 -o ignore \
+ jexec dst ping -c 1 192.0.2.2
+
+ jexec rtr pfctl -e
+ pft_set_rules rtr \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "table <wanaddrs> { 192.0.2.2/31 }" \
+ "pass in on ${epair}b inet6 from any to 64:ff9b::/96 af-to inet from <wanaddrs> round-robin"
+
+ # Use pf to count sources
+ jexec dst pfctl -e
+ pft_set_rules dst \
+ "pass"
+
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 64:ff9b::192.0.2.254
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 64:ff9b::192.0.2.254
+
+ # Verify on dst that we saw different source addresses
+ atf_check -s exit:0 -o match:".*192.0.2.2.*" \
+ jexec dst pfctl -ss
+ atf_check -s exit:0 -o match:".*192.0.2.3.*" \
+ jexec dst pfctl -ss
+}
+
+table_range_cleanup()
+{
+ pft_cleanup
+}
+
+table_common_body()
+{
+ pool_type=$1
+
+ pft_init
+
+ epair_link=$(vnet_mkepair)
+ epair=$(vnet_mkepair)
+
+ ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad
+ route -6 add default 2001:db8::1
+
+ vnet_mkjail rtr ${epair}b ${epair_link}a
+ jexec rtr ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad
+ jexec rtr ifconfig ${epair_link}a 192.0.2.1/24 up
+ jexec rtr ifconfig ${epair_link}a inet alias 192.0.2.3/24 up
+ jexec rtr ifconfig ${epair_link}a inet alias 192.0.2.4/24 up
+
+ vnet_mkjail dst ${epair_link}b
+ jexec dst ifconfig ${epair_link}b 192.0.2.2/24 up
+ jexec dst route add default 192.0.2.1
+
+ # Sanity checks
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 2001:db8::1
+ atf_check -s exit:0 -o ignore \
+ jexec dst ping -c 1 192.0.2.1
+
+ jexec rtr pfctl -e
+ pft_set_rules rtr \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "table <wanaddrs> { 192.0.2.1, 192.0.2.3, 192.0.2.4 }" \
+ "pass in on ${epair}b inet6 from any to 64:ff9b::/96 af-to inet from <wanaddrs> ${pool_type}"
+
+ # Use pf to count sources
+ jexec dst pfctl -e
+ pft_set_rules dst \
+ "pass"
+
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 64:ff9b::192.0.2.2
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 64:ff9b::192.0.2.2
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 64:ff9b::192.0.2.2
+
+ # XXX We can't reasonably check pool type random because it's random. It may end
+ # up choosing the same source IP for all three connections.
+ if [ "${pool_type}" == "round-robin" ];
+ then
+ # Verify on dst that we saw different source addresses
+ atf_check -s exit:0 -o match:".*192.0.2.1.*" \
+ jexec dst pfctl -ss
+ atf_check -s exit:0 -o match:".*192.0.2.3.*" \
+ jexec dst pfctl -ss
+ atf_check -s exit:0 -o match:".*192.0.2.4.*" \
+ jexec dst pfctl -ss
+ fi
+}
+
+atf_test_case "table_round_robin" "cleanup"
+table_round_robin_head()
+{
+ atf_set descr 'Use a table of IPv4 addresses in round-robin mode'
+ atf_set require.user root
+}
+
+table_round_robin_body()
+{
+ table_common_body round-robin
+}
+
+table_round_robin_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "table_random" "cleanup"
+table_random_head()
+{
+ atf_set descr 'Use a table of IPv4 addresses in random mode'
+ atf_set require.user root
+}
+
+table_random_body()
+{
+ table_common_body random
+}
+
+table_random_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "dummynet" "cleanup"
+dummynet_head()
+{
+ atf_set descr 'Test dummynet on af-to rules'
+ atf_set require.user root
+}
+
+dummynet_body()
+{
+ pft_init
+ dummynet_init
+
+ epair_link=$(vnet_mkepair)
+ epair=$(vnet_mkepair)
+
+ ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad
+ route -6 add default 2001:db8::1
+
+ vnet_mkjail rtr ${epair}b ${epair_link}a
+ jexec rtr ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad
+ jexec rtr ifconfig ${epair_link}a 192.0.2.1/24 up
+
+ vnet_mkjail dst ${epair_link}b
+ jexec dst ifconfig ${epair_link}b 192.0.2.2/24 up
+ jexec dst route add default 192.0.2.1
+
+ # Sanity checks
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 2001:db8::1
+ atf_check -s exit:0 -o ignore \
+ jexec dst ping -c 1 192.0.2.1
+
+ jexec rtr pfctl -e
+ jexec rtr dnctl pipe 1 config delay 600
+ pft_set_rules rtr \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "pass in on ${epair}b inet6 from any to 64:ff9b::/96 dnpipe 1 af-to inet from (${epair_link}a)"
+
+ # The ping request will pass, but take 1.2 seconds (.6 in, .6 out)
+ # So this works:
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 -t 2 64:ff9b::192.0.2.2
+
+ # But this times out:
+ atf_check -s exit:2 -o ignore \
+ ping6 -c 1 -t 1 64:ff9b::192.0.2.2
+}
+
+dummynet_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "gateway6" "cleanup"
+gateway6_head()
+{
+ atf_set descr 'NAT64 with a routing hop on the v6 side'
+ atf_set require.user root
+}
+
+gateway6_body()
+{
+ pft_init
+
+ epair_lan_link=$(vnet_mkepair)
+ epair_link=$(vnet_mkepair)
+ epair=$(vnet_mkepair)
+
+ ifconfig ${epair}a inet6 2001:db8:1::2/64 up no_dad
+ route -6 add default 2001:db8:1::1
+
+ vnet_mkjail lan_rtr ${epair}b ${epair_lan_link}a
+ jexec lan_rtr ifconfig ${epair}b inet6 2001:db8:1::1/64 up no_dad
+ jexec lan_rtr ifconfig ${epair_lan_link}a inet6 2001:db8::2/64 up no_dad
+ jexec lan_rtr route -6 add default 2001:db8::1
+ jexec lan_rtr sysctl net.inet6.ip6.forwarding=1
+
+ vnet_mkjail rtr ${epair_lan_link}b ${epair_link}a
+ jexec rtr ifconfig ${epair_lan_link}b inet6 2001:db8::1/64 up no_dad
+ jexec rtr ifconfig ${epair_link}a 192.0.2.1/24 up
+ jexec rtr route -6 add default 2001:db8::2
+
+ vnet_mkjail dst ${epair_link}b
+ jexec dst ifconfig ${epair_link}b 192.0.2.2/24 up
+ jexec dst route add default 192.0.2.1
+
+ # Sanity checks
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 2001:db8:1::1
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 2001:db8::1
+ atf_check -s exit:0 -o ignore \
+ jexec dst ping -c 1 192.0.2.1
+
+ jexec rtr pfctl -e
+ pft_set_rules rtr \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "pass in on ${epair_lan_link}b inet6 from any to 64:ff9b::/96 af-to inet from (${epair_link}a)"
+
+ # One ping
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 64:ff9b::192.0.2.2
+
+ # Make sure packets make it even when state is established
+ atf_check -s exit:0 \
+ -o match:'5 packets transmitted, 5 packets received, 0.0% packet loss' \
+ ping6 -c 5 64:ff9b::192.0.2.2
+}
+
+gateway6_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "route_to" "cleanup"
+route_to_head()
+{
+ atf_set descr 'Test route-to on af-to rules'
+ atf_set require.user root
+}
+
+route_to_body()
+{
+ pft_init
+
+ epair_link=$(vnet_mkepair)
+ epair_null=$(vnet_mkepair)
+ epair=$(vnet_mkepair)
+
+ ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad
+ route -6 add default 2001:db8::1
+
+ vnet_mkjail rtr ${epair}b ${epair_link}a ${epair_null}a
+ jexec rtr ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad
+ jexec rtr ifconfig ${epair_null}a 192.0.2.3/24 up
+ jexec rtr ifconfig ${epair_link}a 192.0.2.1/24 up
+
+ vnet_mkjail dst ${epair_link}b
+ jexec dst ifconfig ${epair_link}b 192.0.2.2/24 up
+ jexec dst route add default 192.0.2.1
+
+ # Sanity checks
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 2001:db8::1
+
+ jexec rtr pfctl -e
+ pft_set_rules rtr \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "pass in on ${epair}b route-to (${epair_link}a 192.0.2.2) inet6 from any to 64:ff9b::/96 af-to inet from (${epair_link}a)"
+
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 3 64:ff9b::192.0.2.2
+}
+
+route_to_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "reply_to" "cleanup"
+reply_to_head()
+{
+ atf_set descr 'Test reply-to on af-to rules'
+ atf_set require.user root
+}
+
+reply_to_body()
+{
+ pft_init
+
+ epair_link=$(vnet_mkepair)
+ epair=$(vnet_mkepair)
+
+ ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad
+ route -6 add default 2001:db8::1
+
+ vnet_mkjail rtr ${epair}b ${epair_link}a
+ jexec rtr ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad
+ jexec rtr ifconfig ${epair_link}a 192.0.2.1/24 up
+
+ vnet_mkjail dst ${epair_link}b
+ jexec dst ifconfig ${epair_link}b 192.0.2.2/24 up
+ jexec dst route add default 192.0.2.1
+
+ # Sanity checks
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 2001:db8::1
+
+ jexec rtr pfctl -e
+ pft_set_rules rtr \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "pass in on ${epair}b reply-to (${epair}b 2001:db8::2) inet6 from any to 64:ff9b::/96 af-to inet from 192.0.2.1"
+
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 3 64:ff9b::192.0.2.2
+}
+
+reply_to_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "v6_gateway" "cleanup"
+v6_gateway_head()
+{
+ atf_set descr 'nat64 when the IPv4 gateway is given by an IPv6 address'
+ atf_set require.user root
+}
+
+v6_gateway_body()
+{
+ pft_init
+
+ epair_wan_two=$(vnet_mkepair)
+ epair_wan_one=$(vnet_mkepair)
+ epair_lan=$(vnet_mkepair)
+
+ ifconfig ${epair_lan}a inet6 2001:db8::2/64 up no_dad
+ route -6 add default 2001:db8::1
+
+ vnet_mkjail rtr ${epair_lan}b ${epair_wan_one}a
+ jexec rtr ifconfig ${epair_lan}b inet6 2001:db8::1/64 up no_dad
+ jexec rtr ifconfig ${epair_wan_one}a 192.0.2.1/24 up
+ jexec rtr ifconfig ${epair_wan_one}a inet6 -ifdisabled
+ jexec rtr route add default -inet6 fe80::1%${epair_wan_one}a
+ #jexec rtr route add default 192.0.2.2
+
+ vnet_mkjail wan_one ${epair_wan_one}b ${epair_wan_two}a
+ jexec wan_one ifconfig ${epair_wan_one}b 192.0.2.2/24 up
+ jexec wan_one ifconfig ${epair_wan_one}b inet6 fe80::1/64
+ jexec wan_one ifconfig ${epair_wan_two}a 198.51.100.2/24 up
+ jexec wan_one route add default 192.0.2.1
+ jexec wan_one sysctl net.inet.ip.forwarding=1
+
+ vnet_mkjail wan_two ${epair_wan_two}b
+ jexec wan_two ifconfig ${epair_wan_two}b 198.51.100.1/24 up
+ jexec wan_two route add default 198.51.100.2
+
+ # Sanity checks
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 2001:db8::1
+ atf_check -s exit:0 -o ignore \
+ jexec rtr ping -c 1 192.0.2.2
+ atf_check -s exit:0 -o ignore \
+ jexec rtr ping -c 1 198.51.100.1
+
+ jexec rtr pfctl -e
+ pft_set_rules rtr \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "pass in on ${epair_lan}b inet6 from any to 64:ff9b::/96 af-to inet from (${epair_wan_one}a)"
+
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 3 64:ff9b::192.0.2.2
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 3 64:ff9b::198.51.100.1
+}
+
+v6_gateway_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "icmp_echo_in"
+ atf_add_test_case "icmp_echo_out"
+ atf_add_test_case "fragmentation_in"
+ atf_add_test_case "fragmentation_out"
+ atf_add_test_case "tcp_in"
+ atf_add_test_case "tcp_out"
+ atf_add_test_case "udp_in"
+ atf_add_test_case "udp_out"
+ atf_add_test_case "sctp_in"
+ atf_add_test_case "sctp_out"
+ atf_add_test_case "tos"
+ atf_add_test_case "no_v4"
+ atf_add_test_case "range"
+ atf_add_test_case "pool"
+ atf_add_test_case "table"
+ atf_add_test_case "table_range"
+ atf_add_test_case "table_round_robin"
+ atf_add_test_case "table_random"
+ atf_add_test_case "dummynet"
+ atf_add_test_case "gateway6"
+ atf_add_test_case "route_to"
+ atf_add_test_case "reply_to"
+ atf_add_test_case "v6_gateway"
+}
diff --git a/tests/sys/netpfil/pf/nat66.py b/tests/sys/netpfil/pf/nat66.py
new file mode 100644
index 000000000000..16b4ef3dd02b
--- /dev/null
+++ b/tests/sys/netpfil/pf/nat66.py
@@ -0,0 +1,186 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import ctypes
+import ipaddress
+import pytest
+import re
+import socket
+from utils import DelayedSend
+from atf_python.sys.net.tools import ToolsHelper
+from atf_python.sys.net.vnet import VnetTestTemplate
+
+class TestNAT66(VnetTestTemplate):
+ REQUIRED_MODULES = [ "pf" ]
+ TOPOLOGY = {
+ "vnet1": {"ifaces": ["if1"]},
+ "vnet2": {"ifaces": ["if1", "if2"]},
+ "vnet3": {"ifaces": ["if2"]},
+ "if1": {"prefixes6": [("2001:db8::2/64", "2001:db8::1/64")]},
+ "if2": {"prefixes6": [("2001:db8:1::1/64", "2001:db8:1::2/64")]},
+ }
+
+ def vnet2_handler(self, vnet):
+ ifname = vnet.iface_alias_map["if1"].name
+ ToolsHelper.print_output("/sbin/ifconfig %s mtu 9000" % ifname)
+ outifname = vnet.iface_alias_map["if2"].name
+
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.pf_rules([
+ "set reassemble yes",
+ "binat inet6 from 2001:db8::/64 to 2001:db8:1::/64 -> 2001:db8:42::/64",
+ "binat inet6 from 2001:db8:1::/64 to 2001:db8:42::/64 -> 2001:db8::/64",
+ "pass inet6 proto icmp6",
+ "pass in route-to ( %s 2001:db8:1::2 ) inet6 from 2001:db8::3 to 2001:db8:1::/64" % outifname])
+
+ ToolsHelper.print_output("/sbin/sysctl net.inet6.ip6.forwarding=1")
+
+ def vnet3_handler(self, vnet):
+ ToolsHelper.print_output("/sbin/route add -6 2001:db8:42::/64 2001:db8:1::1")
+
+ def check_icmp_too_big(self, sp, payload_size, frag_size=None, src="2001:db8::2"):
+ packet = sp.IPv6(src=src, dst="2001:db8:1::2") \
+ / sp.ICMPv6EchoRequest(data=sp.raw(bytes.fromhex('f0') * payload_size))
+
+ if frag_size is not None:
+ packet = sp.fragment6(packet, frag_size)
+
+ # Delay the send so the sniffer is running when we transmit.
+ s = DelayedSend(packet)
+
+ packets = sp.sniff(iface=self.vnet.iface_alias_map["if1"].name,
+ timeout=3)
+ found=False
+ for p in packets:
+ # We can't get a reply to this
+ assert not p.getlayer(sp.ICMPv6EchoReply)
+
+ if not p.getlayer(sp.ICMPv6PacketTooBig):
+ continue
+
+ ip6 = p.getlayer(sp.IPv6)
+ icmp6 = p.getlayer(sp.ICMPv6PacketTooBig)
+
+ # Error is from the router vnet
+ assert ip6.src == "2001:db8::1"
+ assert ip6.dst == src
+
+ # And the relevant MTU is 1500
+ assert icmp6.mtu == 1500
+
+ # The icmp6 error contains our original IPv6 packet
+ err = icmp6.getlayer(sp.IPerror6)
+ assert err.src == src
+ assert err.dst == "2001:db8:1::2"
+ assert err.nh == 58
+
+ found = True
+
+ assert found
+
+ def check_icmp_echo(self, sp, payload_size, src="2001:db8::2"):
+ packet = sp.IPv6(src=src, dst="2001:db8:1::2") \
+ / sp.ICMPv6EchoRequest(data=sp.raw(bytes.fromhex('f0') * payload_size))
+
+ # Delay the send so the sniffer is running when we transmit.
+ s = DelayedSend(packet)
+
+ packets = sp.sniff(iface=self.vnet.iface_alias_map["if1"].name,
+ timeout=3)
+ found=False
+ for p in packets:
+ if not p.getlayer(sp.ICMPv6EchoReply):
+ continue
+
+ ip6 = p.getlayer(sp.IPv6)
+ icmp6 = p.getlayer(sp.ICMPv6EchoReply)
+
+ # Error is from the router vnet
+ assert ip6.src == "2001:db8:1::2"
+ assert ip6.dst == src
+
+ found = True
+
+ assert found
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_npt_icmp(self):
+ cl_vnet = self.vnet_map["vnet1"]
+ ifname = cl_vnet.iface_alias_map["if1"].name
+ ToolsHelper.print_output("/sbin/ifconfig %s mtu 9000" % ifname)
+
+ ToolsHelper.print_output("/sbin/route add -6 2001:db8:1::/64 2001:db8::1")
+
+ # For unclear reasons vnet3 doesn't respond to the first ping.
+ # Just send two for now.
+ ToolsHelper.print_output("/sbin/ping -6 -c 1 2001:db8:1::2")
+ ToolsHelper.print_output("/sbin/ping -6 -c 1 2001:db8:1::2")
+
+ # Import in the correct vnet, so at to not confuse Scapy
+ import scapy.all as sp
+
+ # A ping that easily passes without fragmentation
+ self.check_icmp_echo(sp, 128)
+
+ # Send a ping that just barely doesn't need to be fragmented
+ self.check_icmp_echo(sp, 1452)
+
+ # Send a ping that just barely needs to be fragmented
+ self.check_icmp_too_big(sp, 1453)
+
+ # A ping that arrives fragmented
+ self.check_icmp_too_big(sp, 12000, 5000)
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_npt_route_to_icmp(self):
+ cl_vnet = self.vnet_map["vnet1"]
+ ifname = cl_vnet.iface_alias_map["if1"].name
+ ToolsHelper.print_output("/sbin/ifconfig %s mtu 9000" % ifname)
+ ToolsHelper.print_output("/sbin/ifconfig %s inet6 alias 2001:db8::3/64" % ifname)
+
+ ToolsHelper.print_output("/sbin/route add -6 2001:db8:1::/64 2001:db8::1")
+
+ # For unclear reasons vnet3 doesn't respond to the first ping.
+ # Just send two for now.
+ ToolsHelper.print_output("/sbin/ping -6 -c 1 -S 2001:db8::3 2001:db8:1::2")
+ ToolsHelper.print_output("/sbin/ping -6 -c 1 -S 2001:db8::3 2001:db8:1::2")
+
+ # Import in the correct vnet, so at to not confuse Scapy
+ import scapy.all as sp
+
+ # A ping that easily passes without fragmentation
+ self.check_icmp_echo(sp, 128, src="2001:db8::3")
+
+ # Send a ping that just barely doesn't need to be fragmented
+ self.check_icmp_echo(sp, 1452, src="2001:db8::3")
+
+ # Send a ping that just barely needs to be fragmented
+ self.check_icmp_too_big(sp, 1453, src="2001:db8::3")
+
+ # A ping that arrives fragmented
+ self.check_icmp_too_big(sp, 12000, 5000, src="2001:db8::3")
diff --git a/tests/sys/netpfil/pf/pass_block.sh b/tests/sys/netpfil/pf/pass_block.sh
new file mode 100644
index 000000000000..e955068d014b
--- /dev/null
+++ b/tests/sys/netpfil/pf/pass_block.sh
@@ -0,0 +1,465 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2018 Kristof Provost <kp@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+common_dir=$(atf_get_srcdir)/../common
+
+atf_test_case "enable_disable" "cleanup"
+enable_disable_head()
+{
+ atf_set descr 'Test enable/disable'
+ atf_set require.user root
+}
+
+enable_disable_body()
+{
+ pft_init
+
+ j="pass_block:enable_disable"
+
+ vnet_mkjail ${j}
+
+ # Disable when disabled fails
+ atf_check -s exit:1 -e ignore \
+ jexec ${j} pfctl -d
+
+ # Enable succeeds
+ atf_check -s exit:0 -e ignore \
+ jexec ${j} pfctl -e
+
+ # Enable when enabled fails
+ atf_check -s exit:1 -e ignore \
+ jexec ${j} pfctl -e
+
+ # Disable succeeds
+ atf_check -s exit:0 -e ignore \
+ jexec ${j} pfctl -d
+}
+
+enable_disable_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "v4" "cleanup"
+v4_head()
+{
+ atf_set descr 'Basic pass/block test for IPv4'
+ atf_set require.user root
+}
+
+v4_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a 192.0.2.1/24 up
+
+ # Set up a simple jail with one interface
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Trivial ping to the jail, without pf
+ atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.2
+
+ # pf without policy will let us ping
+ jexec alcatraz pfctl -e
+ atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.2
+
+ # Block everything
+ pft_set_rules alcatraz "block in"
+ atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.2
+
+ # Block everything but ICMP
+ pft_set_rules alcatraz "block in" "pass in proto icmp"
+ atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.2
+}
+
+v4_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "v6" "cleanup"
+v6_head()
+{
+ atf_set descr 'Basic pass/block test for IPv6'
+ atf_set require.user root
+}
+
+v6_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a inet6 2001:db8:42::1/64 up no_dad
+
+ # Set up a simple jail with one interface
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b inet6 2001:db8:42::2/64 up no_dad
+
+ # Trivial ping to the jail, without pf
+ atf_check -s exit:0 -o ignore ping -6 -c 1 -W 1 2001:db8:42::2
+
+ # pf without policy will let us ping
+ jexec alcatraz pfctl -e
+ atf_check -s exit:0 -o ignore ping -6 -c 1 -W 1 2001:db8:42::2
+
+ # Block everything
+ pft_set_rules alcatraz "block in"
+ atf_check -s exit:2 -o ignore ping -6 -c 1 -W 1 2001:db8:42::2
+
+ # Block everything but ICMP
+ pft_set_rules alcatraz "block in" "pass in proto icmp6"
+ atf_check -s exit:0 -o ignore ping -6 -c 1 -W 1 2001:db8:42::2
+
+ # Allowing ICMPv4 does not allow ICMPv6
+ pft_set_rules alcatraz "block in" "pass in proto icmp"
+ atf_check -s exit:2 -o ignore ping -6 -c 1 -W 1 2001:db8:42::2
+}
+
+v6_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "noalias" "cleanup"
+noalias_head()
+{
+ atf_set descr 'Test the :0 noalias option'
+ atf_set require.user root
+}
+
+noalias_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a inet6 2001:db8:42::1/64 up no_dad
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b inet6 2001:db8:42::2/64 up no_dad
+
+ linklocaladdr=$(jexec alcatraz ifconfig ${epair}b inet6 \
+ | grep %${epair}b \
+ | awk '{ print $2; }' \
+ | cut -d % -f 1)
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -6 -c 3 -W 1 2001:db8:42::2
+ atf_check -s exit:0 -o ignore ping -6 -c 3 -W 1 ${linklocaladdr}%${epair}a
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz "block out inet6 from (${epair}b:0) to any"
+
+ atf_check -s exit:2 -o ignore ping -6 -c 3 -W 1 2001:db8:42::2
+
+ # We should still be able to ping the link-local address
+ atf_check -s exit:0 -o ignore ping -6 -c 3 -W 1 ${linklocaladdr}%${epair}a
+
+ pft_set_rules alcatraz "block out inet6 from (${epair}b) to any"
+
+ # We cannot ping to the link-local address
+ atf_check -s exit:2 -o ignore ping -6 -c 3 -W 1 ${linklocaladdr}%${epair}a
+}
+
+noalias_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nested_inline" "cleanup"
+nested_inline_head()
+{
+ atf_set descr "Test nested inline anchors, PR196314"
+ atf_set require.user root
+}
+
+nested_inline_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a inet 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "block in" \
+ "anchor \"an1\" {" \
+ "pass in quick proto tcp to port time" \
+ "anchor \"an2\" {" \
+ "pass in quick proto icmp" \
+ "}" \
+ "}"
+
+ atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.2
+}
+
+nested_inline_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "urpf" "cleanup"
+urpf_head()
+{
+ atf_set descr "Test unicast reverse path forwarding check"
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+urpf_body()
+{
+ pft_init
+
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair_one}b ${epair_two}b
+
+ ifconfig ${epair_one}a 192.0.2.2/24 up
+ ifconfig ${epair_two}a 198.51.100.2/24 up
+
+ jexec alcatraz ifconfig ${epair_one}b 192.0.2.1/24 up
+ jexec alcatraz ifconfig ${epair_two}b 198.51.100.1/24 up
+ jexec alcatraz sysctl net.inet.ip.forwarding=1
+
+ # Sanity checks
+ atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.1
+ atf_check -s exit:0 -o ignore ping -c 1 -t 1 198.51.100.1
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_one}a \
+ --to 192.0.2.1 \
+ --fromaddr 198.51.100.2 \
+ --replyif ${epair_two}a
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_two}a \
+ --to 198.51.100.1 \
+ --fromaddr 192.0.2.2 \
+ --replyif ${epair_one}a
+
+ pft_set_rules alcatraz \
+ "block quick from urpf-failed" \
+ "set skip on lo"
+ jexec alcatraz pfctl -e
+
+ # Correct source still works
+ atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.1
+ atf_check -s exit:0 -o ignore ping -c 1 -t 1 198.51.100.1
+
+ # Unexpected source interface is blocked
+ atf_check -s exit:1 ${common_dir}/pft_ping.py \
+ --sendif ${epair_one}a \
+ --to 192.0.2.1 \
+ --fromaddr 198.51.100.2 \
+ --replyif ${epair_two}a
+ atf_check -s exit:1 ${common_dir}/pft_ping.py \
+ --sendif ${epair_two}a \
+ --to 198.51.100.1 \
+ --fromaddr 192.0.2.2 \
+ --replyif ${epair_one}a
+}
+
+urpf_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "received_on" "cleanup"
+received_on_head()
+{
+ atf_set descr 'Test received-on filtering'
+ atf_set require.user root
+}
+
+received_on_body()
+{
+ pft_init
+
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+ epair_route=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair_one}b ${epair_two}b ${epair_route}a
+ vnet_mkjail srv ${epair_route}b
+
+ ifconfig ${epair_one}a 192.0.2.2/24 up
+ ifconfig ${epair_two}a 198.51.100.2/24 up
+ route add 203.0.113.2 192.0.2.1
+ route add 203.0.113.3 198.51.100.1
+
+ jexec alcatraz ifconfig ${epair_one}b 192.0.2.1/24 up
+ jexec alcatraz ifconfig ${epair_two}b 198.51.100.1/24 up
+ jexec alcatraz ifconfig ${epair_route}a 203.0.113.1/24 up
+ jexec alcatraz sysctl net.inet.ip.forwarding=1
+
+ jexec srv ifconfig ${epair_route}b 203.0.113.2/24 up
+ jexec srv ifconfig ${epair_route}b inet alias 203.0.113.3/24 up
+ jexec srv route add default 203.0.113.1
+
+ # Sanity checks
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.1
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 198.51.100.1
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 203.0.113.2
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 203.0.113.3
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "block in" \
+ "pass received-on ${epair_one}b"
+
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.1
+ atf_check -s exit:2 -o ignore \
+ ping -c 1 198.51.100.1
+
+ # And ensure we can check the received-on interface after routing
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 203.0.113.2
+ atf_check -s exit:2 -o ignore \
+ ping -c 1 203.0.113.3
+
+ # Now try this with a group instead
+ jexec alcatraz ifconfig ${epair_one}b group test
+ pft_set_rules alcatraz \
+ "block in" \
+ "pass received-on test"
+
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.1
+ atf_check -s exit:2 -o ignore \
+ ping -c 1 198.51.100.1
+
+ # And ensure we can check the received-on interface after routing
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 203.0.113.2
+ atf_check -s exit:2 -o ignore \
+ ping -c 1 203.0.113.3
+
+ # Test '! received-on'
+ pft_set_rules alcatraz \
+ "pass in" \
+ "block ! received-on ${epair_one}b"
+
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.1
+ atf_check -s exit:2 -o ignore \
+ ping -c 1 198.51.100.1
+}
+
+received_on_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "optimize_any" "cleanup"
+optimize_any_head()
+{
+ atf_set descr 'Test known optimizer bug'
+ atf_set require.user root
+}
+
+optimize_any_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair}a
+
+ ifconfig ${epair}b 192.0.2.2/24 up
+
+ jexec alcatraz ifconfig ${epair}a 192.0.2.1/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.1
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "block" \
+ "pass in inet from { any, 192.0.2.3 }"
+
+ atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.1
+}
+
+optimize_any_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "any_if" "cleanup"
+any_if_head()
+{
+ atf_set descr 'Test the any interface keyword'
+ atf_set require.user root
+}
+
+any_if_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}b 192.0.2.2/24 up
+
+ vnet_mkjail alcatraz ${epair}a
+ jexec alcatraz ifconfig ${epair}a 192.0.2.1/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.1
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "block" \
+ "pass in on any"
+
+ atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.1
+}
+
+any_if_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "enable_disable"
+ atf_add_test_case "v4"
+ atf_add_test_case "v6"
+ atf_add_test_case "noalias"
+ atf_add_test_case "nested_inline"
+ atf_add_test_case "urpf"
+ atf_add_test_case "received_on"
+ atf_add_test_case "optimize_any"
+ atf_add_test_case "any_if"
+}
diff --git a/tests/sys/netpfil/pf/pflog.sh b/tests/sys/netpfil/pf/pflog.sh
new file mode 100644
index 000000000000..a34ec893a75c
--- /dev/null
+++ b/tests/sys/netpfil/pf/pflog.sh
@@ -0,0 +1,406 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2023 Rubicon Communications, LLC (Netgate)
+# Copyright (c) 2024 Deciso B.V.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+common_dir=$(atf_get_srcdir)/../common
+
+atf_test_case "malformed" "cleanup"
+malformed_head()
+{
+ atf_set descr 'Test that we do not log malformed packets as passing'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+malformed_body()
+{
+ pflog_init
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail srv ${epair}b
+ jexec srv ifconfig ${epair}b 192.0.2.1/24 up
+
+ vnet_mkjail cl ${epair}a
+ jexec cl ifconfig ${epair}a 192.0.2.2/24 up
+
+ jexec cl pfctl -e
+ jexec cl ifconfig pflog0 up
+ pft_set_rules cl \
+ "pass log keep state"
+
+ # Not required, but the 'pf: dropping packet with ip options' kernel log can
+ # help when debugging the test.
+ jexec cl pfctl -x loud
+
+ jexec cl tcpdump -n -e -ttt --immediate-mode -l -U -i pflog0 >> pflog.txt &
+ sleep 1 # Wait for tcpdump to start
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ jexec srv ping -c 1 192.0.2.2
+
+ jexec srv ${common_dir}/pft_ping.py \
+ --sendif ${epair}b \
+ --to 192.0.2.2 \
+ --send-nop \
+ --recvif ${epair}b
+
+ atf_check -o match:".*rule 0/8\(ip-option\): block in on ${epair}a: 192.0.2.1 > 192.0.2.2: ICMP echo request.*" \
+ cat pflog.txt
+}
+
+malformed_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "matches" "cleanup"
+matches_head()
+{
+ atf_set descr 'Test the pflog matches keyword'
+ atf_set require.user root
+}
+
+matches_body()
+{
+ pflog_init
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair}a
+ jexec alcatraz ifconfig ${epair}a 192.0.2.1/24 up
+
+ ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.1
+
+ jexec alcatraz pfctl -e
+ jexec alcatraz ifconfig pflog0 up
+ pft_set_rules alcatraz \
+ "match log(matches) inet proto icmp" \
+ "match log(matches) inet from 192.0.2.2" \
+ "pass"
+
+ jexec alcatraz tcpdump -n -e -ttt --immediate-mode -l -U -i pflog0 >> ${PWD}/pflog.txt &
+ sleep 1 # Wait for tcpdump to start
+
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.1
+
+ echo "Rules"
+ jexec alcatraz pfctl -sr -vv
+ echo "States"
+ jexec alcatraz pfctl -ss -vv
+ echo "Log"
+ cat ${PWD}/pflog.txt
+
+ atf_check -o match:".*rule 0/0\(match\): match in on ${epair}a: 192.0.2.2 > 192.0.2.1: ICMP echo request.*" \
+ cat pflog.txt
+ atf_check -o match:".*rule 1/0\(match\): match in on ${epair}a: 192.0.2.2 > 192.0.2.1: ICMP echo request.*" \
+ cat pflog.txt
+}
+
+matches_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "matches_logif" "cleanup"
+matches_logif_head()
+{
+ atf_set descr 'Test log(matches, to pflogX)'
+ atf_set require.user root
+}
+
+matches_logif_body()
+{
+ pflog_init
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair}a
+ jexec alcatraz ifconfig ${epair}a 192.0.2.1/24 up
+
+ ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.1
+
+ jexec alcatraz pfctl -e
+ jexec alcatraz ifconfig pflog0 up
+ jexec alcatraz ifconfig pflog1 create
+ jexec alcatraz ifconfig pflog1 up
+ pft_set_rules alcatraz \
+ "match log(matches, to pflog1) inet proto icmp" \
+ "match log inet from 192.0.2.2" \
+ "pass log(to pflog0)"
+
+ jexec alcatraz tcpdump -n -e -ttt --immediate-mode -l -U -i pflog1 >> ${PWD}/pflog1.txt &
+ jexec alcatraz tcpdump -n -e -ttt --immediate-mode -l -U -i pflog0 >> ${PWD}/pflog0.txt &
+ sleep 1 # Wait for tcpdump to start
+
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.1
+
+ echo "Rules"
+ jexec alcatraz pfctl -sr -vv
+ echo "States"
+ jexec alcatraz pfctl -ss -vv
+ echo "Log 0"
+ cat ${PWD}/pflog0.txt
+ echo "Log 1"
+ cat ${PWD}/pflog1.txt
+
+ atf_check -o match:".*rule 0/0\(match\): match in on ${epair}a: 192.0.2.2 > 192.0.2.1: ICMP echo request.*" \
+ cat pflog1.txt
+ atf_check -o match:".*rule 1/0\(match\): match in on ${epair}a: 192.0.2.2 > 192.0.2.1: ICMP echo request.*" \
+ cat pflog1.txt
+}
+
+matches_logif_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "state_max" "cleanup"
+state_max_head()
+{
+ atf_set descr 'Ensure that drops due to state limits are logged'
+ atf_set require.user root
+}
+
+state_max_body()
+{
+ pflog_init
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair}a
+ jexec alcatraz ifconfig ${epair}a 192.0.2.1/24 up
+
+ ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.1
+
+ jexec alcatraz pfctl -e
+ jexec alcatraz ifconfig pflog0 up
+ pft_set_rules alcatraz "pass log inet keep state (max 1)"
+
+ jexec alcatraz tcpdump -n -e -ttt --immediate-mode -l -U -i pflog0 >> ${PWD}/pflog.txt &
+ sleep 1 # Wait for tcpdump to start
+
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.1
+
+ atf_check -s exit:2 -o ignore \
+ ping -c 1 192.0.2.1
+
+ echo "Rules"
+ jexec alcatraz pfctl -sr -vv
+ echo "States"
+ jexec alcatraz pfctl -ss -vv
+ echo "Log"
+ cat ${PWD}/pflog.txt
+
+ # First ping passes.
+ atf_check -o match:".*rule 0/0\(match\): pass in on ${epair}a: 192.0.2.2 > 192.0.2.1: ICMP echo request.*" \
+ cat pflog.txt
+
+ # Second ping is blocked due to the state limit.
+ atf_check -o match:".*rule 0/12\(state-limit\): block in on ${epair}a: 192.0.2.2 > 192.0.2.1: ICMP echo request.*" \
+ cat pflog.txt
+
+ # At most three lines should be written: one for the first ping, and
+ # two for the second: one for the initial pass through the ruleset, and
+ # then a drop because of the state limit. Ideally only the drop would
+ # be logged; if this is fixed, the count will be 2 instead of 3.
+ atf_check -o match:3 grep -c . pflog.txt
+
+ # If the rule doesn't specify logging, we shouldn't log drops
+ # due to state limits.
+ pft_set_rules alcatraz "pass inet keep state (max 1)"
+
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.1
+
+ atf_check -s exit:2 -o ignore \
+ ping -c 1 192.0.2.1
+
+ atf_check -o match:3 grep -c . pflog.txt
+}
+
+state_max_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "unspecified_v4" "cleanup"
+unspecified_v4_head()
+{
+ atf_set descr 'Ensure that packets to the unspecified address are visible to pfil hooks'
+ atf_set require.user root
+}
+
+unspecified_v4_body()
+{
+ pflog_init
+
+ vnet_mkjail alcatraz
+ jexec alcatraz ifconfig lo0 inet 127.0.0.1
+ jexec alcatraz route add default 127.0.0.1
+
+ jexec alcatraz pfctl -e
+ jexec alcatraz ifconfig pflog0 up
+ pft_set_rules alcatraz "block log on lo0 to 0.0.0.0"
+
+ jexec alcatraz tcpdump -n -e -ttt --immediate-mode -l -U -i pflog0 >> pflog.txt &
+ sleep 1 # Wait for tcpdump to start
+
+ atf_check -s not-exit:0 -o ignore -e ignore \
+ jexec alcatraz ping -S 127.0.0.1 -c 1 0.0.0.0
+
+ atf_check -o match:".*: block out on lo0: 127.0.0.1 > 0.0.0.0: ICMP echo request,.*" \
+ cat pflog.txt
+}
+
+unspecified_v4_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "unspecified_v6" "cleanup"
+unspecified_v6_head()
+{
+ atf_set descr 'Ensure that packets to the unspecified address are visible to pfil hooks'
+ atf_set require.user root
+}
+
+unspecified_v6_body()
+{
+ pflog_init
+
+ vnet_mkjail alcatraz
+ jexec alcatraz ifconfig lo0 up
+ jexec alcatraz route -6 add ::0 ::1
+
+ jexec alcatraz pfctl -e
+ jexec alcatraz ifconfig pflog0 up
+ pft_set_rules alcatraz "block log on lo0 to ::0"
+
+ jexec alcatraz tcpdump -n -e -ttt --immediate-mode -l -U -i pflog0 >> pflog.txt &
+ sleep 1 # Wait for tcpdump to start
+
+ atf_check -s not-exit:0 -o ignore -e ignore \
+ jexec alcatraz ping -6 -S ::1 -c 1 ::0
+
+ cat pflog.txt
+ atf_check -o match:".*: block out on lo0: ::1 > ::: ICMP6, echo request,.*" \
+ cat pflog.txt
+}
+
+unspecified_v6_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "rdr_action" "cleanup"
+rdr_head()
+{
+ atf_set descr 'Ensure that NAT rule actions are logged correctly'
+ atf_set require.user root
+}
+
+rdr_action_body()
+{
+ pflog_init
+
+ j="pflog:rdr_action"
+ epair_c=$(vnet_mkepair)
+ epair_srv=$(vnet_mkepair)
+
+ vnet_mkjail ${j}srv ${epair_srv}a
+ vnet_mkjail ${j}gw ${epair_srv}b ${epair_c}a
+ vnet_mkjail ${j}c ${epair_c}b
+
+ jexec ${j}srv ifconfig ${epair_srv}a 198.51.100.1/24 up
+ # No default route in srv jail, to ensure we're NAT-ing
+ jexec ${j}gw ifconfig ${epair_srv}b 198.51.100.2/24 up
+ jexec ${j}gw ifconfig ${epair_c}a 192.0.2.1/24 up
+ jexec ${j}gw sysctl net.inet.ip.forwarding=1
+ jexec ${j}c ifconfig ${epair_c}b 192.0.2.2/24 up
+ jexec ${j}c route add default 192.0.2.1
+
+ jexec ${j}gw pfctl -e
+ jexec ${j}gw ifconfig pflog0 up
+ pft_set_rules ${j}gw \
+ "rdr log on ${epair_srv}b proto tcp from 198.51.100.0/24 to any port 1234 -> 192.0.2.2 port 1234" \
+ "block quick inet6" \
+ "pass in log"
+
+ jexec ${j}gw tcpdump -n -e -ttt --immediate-mode -l -U -i pflog0 >> ${PWD}/pflog.txt &
+ sleep 1 # Wait for tcpdump to start
+
+ # send a SYN to catch in the log
+ jexec ${j}srv nc -N -w 0 198.51.100.2 1234
+
+ echo "Log"
+ cat ${PWD}/pflog.txt
+
+ # log line generated for rdr hit (pre-NAT)
+ atf_check -o match:".*.*rule 0/0\(match\): rdr in on ${epair_srv}b: 198.51.100.1.[0-9]* > 198.51.100.2.1234: Flags \[S\].*" \
+ cat pflog.txt
+
+ # log line generated for pass hit (post-NAT)
+ atf_check -o match:".*.*rule 1/0\(match\): pass in on ${epair_srv}b: 198.51.100.1.[0-9]* > 192.0.2.2.1234: Flags \[S\].*" \
+ cat pflog.txt
+
+ # only two log lines shall be written
+ atf_check -o match:2 grep -c . pflog.txt
+}
+
+rdr_action_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "malformed"
+ atf_add_test_case "matches"
+ atf_add_test_case "matches_logif"
+ atf_add_test_case "state_max"
+ atf_add_test_case "unspecified_v4"
+ atf_add_test_case "unspecified_v6"
+ atf_add_test_case "rdr_action"
+}
diff --git a/tests/sys/netpfil/pf/pflow.sh b/tests/sys/netpfil/pf/pflow.sh
new file mode 100644
index 000000000000..1122096d2e31
--- /dev/null
+++ b/tests/sys/netpfil/pf/pflow.sh
@@ -0,0 +1,349 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2023 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "basic" "cleanup"
+basic_head()
+{
+ atf_set descr 'Basic pflow test'
+ atf_set require.user root
+}
+
+basic_body()
+{
+ pflow_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a 192.0.2.2/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+
+ pflow=$(jexec alcatraz pflowctl -c)
+
+ # Reject invalid flow destinations
+ atf_check -s exit:1 -e ignore \
+ jexec alcatraz pflowctl -s ${pflow} dst 256.0.0.1:4000
+ atf_check -s exit:1 -e ignore \
+ jexec alcatraz pflowctl -s ${pflow} dst 192.0.0.2:400000
+
+ # A valid destination is accepted
+ atf_check -s exit:0 \
+ jexec alcatraz pflowctl -s ${pflow} dst 192.0.2.2:4000
+
+ # Reject invalid version numbers
+ atf_check -s exit:1 -e ignore \
+ jexec alcatraz pflowctl -s ${pflow} proto 9
+
+ # Valid version passes
+ atf_check -s exit:0 \
+ jexec alcatraz pflowctl -s ${pflow} proto 5
+ atf_check -s exit:0 \
+ jexec alcatraz pflowctl -s ${pflow} proto 10
+
+ # We can change the observation domain
+ atf_check -s exit:0 \
+ jexec alcatraz pflowctl -s ${pflow} domain 13
+ atf_check -s exit:0 -o match:".*domain 13.*" \
+ jexec alcatraz pflowctl -l
+}
+
+basic_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "state_defaults" "cleanup"
+state_defaults_head()
+{
+ atf_set descr 'Test set state-defaults pflow'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+state_defaults_body()
+{
+ pflow_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a 192.0.2.2/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "pass"
+
+ pflow=$(jexec alcatraz pflowctl -c)
+ jexec alcatraz pflowctl -s ${pflow} dst 192.0.2.2:2055
+
+ # No flow data is generated because no states are marked for it.
+ ping -c 1 192.0.2.1
+ # Flush states to force pflow creation
+ jexec alcatraz pfctl -Fstates
+
+ atf_check -o match:"No data" \
+ $(atf_get_srcdir)/pft_read_ipfix.py --recvif ${epair}a --port 2055
+
+ # Expect pflow output with state-defaults pflow
+ pft_set_rules alcatraz \
+ "set state-defaults pflow" \
+ "pass"
+
+ ping -c 1 192.0.2.1
+
+ # We default to version 5
+ atf_check -o match:".*v=5.*" \
+ $(atf_get_srcdir)/pft_read_ipfix.py --recvif ${epair}a --port 2055
+
+ # Switch to version 10
+ jexec alcatraz pflowctl -s ${pflow} proto 10
+
+ ping -c 1 192.0.2.1
+
+ atf_check -o match:".*v=10.*" \
+ $(atf_get_srcdir)/pft_read_ipfix.py --recvif ${epair}a --port 2055
+}
+
+state_defaults_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "v6" "cleanup"
+v6_head()
+{
+ atf_set descr 'Test pflow over IPv6'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+v6_body()
+{
+ pflow_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ ping -6 -c 1 2001:db8::1
+
+ pflow=$(jexec alcatraz pflowctl -c )
+ # Note proto 10, because there's no IPv6 information in v5
+ jexec alcatraz pflowctl -s ${pflow} dst [2001:db8::2]:2055 proto 10
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "set state-defaults pflow" \
+ "pass"
+
+ ping -6 -c 1 2001:db8::1
+
+ atf_check -o match:".*v=10,IPv6,proto=58,src=2001:db8::2,dst=2001:db8::1.*" \
+ $(atf_get_srcdir)/pft_read_ipfix.py --recvif ${epair}a --port 2055
+}
+
+v6_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nat" "cleanup"
+nat_head()
+{
+ atf_set descr 'Test pflow export for NAT44'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+nat_body()
+{
+ pflow_init
+
+ epair=$(vnet_mkepair)
+ epair_srv=$(vnet_mkepair)
+
+ vnet_mkjail srv ${epair_srv}a
+ vnet_mkjail rtr ${epair_srv}b ${epair}a
+
+ ifconfig ${epair}b 192.0.2.2/24 up
+
+ jexec srv ifconfig ${epair_srv}a 198.51.100.2/24 up
+ jexec srv route add default 198.51.100.1
+
+ jexec rtr ifconfig ${epair_srv}b 198.51.100.1/24 up
+ jexec rtr ifconfig ${epair}a 192.0.2.1/24 up
+ jexec rtr sysctl net.inet.ip.forwarding=1
+
+ route add -net 198.51.100.0/24 192.0.2.1
+
+ jexec rtr pfctl -e
+ pft_set_rules rtr \
+ "set state-defaults pflow" \
+ "nat pass on ${epair_srv}b inet from 192.0.2.0/24 to any -> (${epair_srv}b)" \
+ "pass"
+
+ pflow=$(jexec rtr pflowctl -c)
+ jexec rtr pflowctl -s ${pflow} dst 192.0.2.2:2055 proto 10
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 198.51.100.2
+
+ atf_check -o match:".*v=10,NAT=4,proto=1,src=192.0.2.2-198.51.100.1.*" \
+ $(atf_get_srcdir)/pft_read_ipfix.py --recvif ${epair}b --port 2055
+}
+
+nat_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "rule" "cleanup"
+rule_head()
+{
+ atf_set descr 'Test per-rule pflow option'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+rule_body()
+{
+ pflow_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a 192.0.2.2/24 up
+ ifconfig ${epair}a alias 192.0.2.3/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "pass in from 192.0.2.2 keep state (pflow)" \
+ "pass in from 192.0.2.3 keep state"
+
+ pflow=$(jexec alcatraz pflowctl -c)
+ jexec alcatraz pflowctl -s ${pflow} dst 192.0.2.2:2055
+
+ # No flow is generated if we ping from 192.0.2.3
+ ping -c 1 -S 192.0.2.3 192.0.2.1
+
+ atf_check -o match:"No data" \
+ $(atf_get_srcdir)/pft_read_ipfix.py --recvif ${epair}a --port 2055
+
+ # But there is one if we ping from 192.0.2.2
+ ping -c 1 -S 192.0.2.2 192.0.2.1
+
+ atf_check -o match:".*v=5.*" \
+ $(atf_get_srcdir)/pft_read_ipfix.py --recvif ${epair}a --port 2055
+}
+
+rule_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "max_entries" "cleanup"
+max_entries_head()
+{
+ atf_set descr 'Test that we can only create X pflow senders'
+ atf_set require.user root
+}
+
+max_entries_body()
+{
+ pflow_init
+
+ vnet_mkjail alcatraz
+
+ for i in `seq 1 128`
+ do
+ atf_check -s exit:0 -o ignore \
+ jexec alcatraz pflowctl -c
+ done
+
+ # We cannot create the 129th pflow sender
+ atf_check -s exit:1 -o ignore -e ignore \
+ jexec alcatraz pflowctl -c
+
+ jexec alcatraz pflowctl -l
+}
+
+max_entries_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "obs_dom" "cleanup"
+obs_dom_head()
+{
+ atf_set descr 'Test configuring observation domain values'
+ atf_set require.user root
+}
+
+obs_dom_body()
+{
+ pflow_init
+
+ vnet_mkjail alcatraz
+
+ pflow=$(jexec alcatraz pflowctl -c)
+ jexec alcatraz pflowctl -s ${pflow} domain 2300000000
+ atf_check -o match:".*domain 2300000000.*" -s exit:0 \
+ jexec alcatraz pflowctl -l
+}
+
+obs_dom_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "basic"
+ atf_add_test_case "state_defaults"
+ atf_add_test_case "v6"
+ atf_add_test_case "nat"
+ atf_add_test_case "rule"
+ atf_add_test_case "max_entries"
+ atf_add_test_case "obs_dom"
+}
diff --git a/tests/sys/netpfil/pf/pfsync.sh b/tests/sys/netpfil/pf/pfsync.sh
new file mode 100644
index 000000000000..3be4a3024393
--- /dev/null
+++ b/tests/sys/netpfil/pf/pfsync.sh
@@ -0,0 +1,1227 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2018 Orange Business Services
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+common_dir=$(atf_get_srcdir)/../common
+
+atf_test_case "basic" "cleanup"
+basic_head()
+{
+ atf_set descr 'Basic pfsync test'
+ atf_set require.user root
+}
+
+basic_body()
+{
+ common_body
+}
+
+common_body()
+{
+ defer=$1
+ pfsynct_init
+
+ epair_sync=$(vnet_mkepair)
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+
+ vnet_mkjail one ${epair_one}a ${epair_sync}a
+ vnet_mkjail two ${epair_two}a ${epair_sync}b
+
+ # pfsync interface
+ jexec one ifconfig ${epair_sync}a 192.0.2.1/24 up
+ jexec one ifconfig ${epair_one}a 198.51.100.1/24 up
+ jexec one ifconfig pfsync0 \
+ syncdev ${epair_sync}a \
+ maxupd 1 \
+ $defer \
+ up
+ jexec two ifconfig ${epair_two}a 198.51.100.2/24 up
+ jexec two ifconfig ${epair_sync}b 192.0.2.2/24 up
+ jexec two ifconfig pfsync0 \
+ syncdev ${epair_sync}b \
+ maxupd 1 \
+ $defer \
+ up
+
+ # Enable pf!
+ jexec one pfctl -e
+ pft_set_rules one \
+ "set skip on ${epair_sync}a" \
+ "pass out keep state"
+ jexec two pfctl -e
+ pft_set_rules two \
+ "set skip on ${epair_sync}b" \
+ "pass out keep state"
+
+ hostid_one=$(jexec one pfctl -si -v | awk '/Hostid:/ { gsub(/0x/, "", $2); printf($2); }')
+
+ ifconfig ${epair_one}b 198.51.100.254/24 up
+
+ ping -c 1 -S 198.51.100.254 198.51.100.1
+
+ # Give pfsync time to do its thing
+ sleep 2
+
+ if ! jexec two pfctl -s states | grep icmp | grep 198.51.100.1 | \
+ grep 198.51.100.254 ; then
+ atf_fail "state not found on synced host"
+ fi
+
+ if ! jexec two pfctl -sc | grep ""${hostid_one}"";
+ then
+ jexec two pfctl -sc
+ atf_fail "HostID for host one not found on two"
+ fi
+}
+
+basic_cleanup()
+{
+ pfsynct_cleanup
+}
+
+atf_test_case "basic_defer" "cleanup"
+basic_defer_head()
+{
+ atf_set descr 'Basic defer mode pfsync test'
+ atf_set require.user root
+}
+
+basic_defer_body()
+{
+ common_body defer
+}
+
+basic_defer_cleanup()
+{
+ pfsynct_cleanup
+}
+
+atf_test_case "defer" "cleanup"
+defer_head()
+{
+ atf_set descr 'Defer mode pfsync test'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+defer_body()
+{
+ pfsynct_init
+
+ epair_sync=$(vnet_mkepair)
+ epair_in=$(vnet_mkepair)
+ epair_out=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair_sync}a ${epair_in}a ${epair_out}a
+
+ jexec alcatraz ifconfig ${epair_sync}a 192.0.2.1/24 up
+ jexec alcatraz ifconfig ${epair_out}a 198.51.100.1/24 up
+ jexec alcatraz ifconfig ${epair_in}a 203.0.113.1/24 up
+ jexec alcatraz arp -s 203.0.113.2 00:01:02:03:04:05
+ jexec alcatraz sysctl net.inet.ip.forwarding=1
+
+ # Set a long defer delay
+ jexec alcatraz sysctl net.pfsync.defer_delay=2500
+
+ jexec alcatraz ifconfig pfsync0 \
+ syncdev ${epair_sync}a \
+ maxupd 1 \
+ defer \
+ up
+
+ ifconfig ${epair_sync}b 192.0.2.2/24 up
+ ifconfig ${epair_out}b 198.51.100.2/24 up
+ ifconfig ${epair_in}b up
+ route add -net 203.0.113.0/24 198.51.100.1
+
+ # Enable pf
+ jexec alcatraz sysctl net.pf.filter_local=0
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "set skip on ${epair_sync}a" \
+ "pass keep state"
+
+ atf_check -s exit:0 env PYTHONPATH=${common_dir} \
+ $(atf_get_srcdir)/pfsync_defer.py \
+ --syncdev ${epair_sync}b \
+ --indev ${epair_in}b \
+ --outdev ${epair_out}b
+
+ # Now disable defer mode and expect failure.
+ jexec alcatraz ifconfig pfsync0 -defer
+
+ # Flush state
+ pft_set_rules alcatraz \
+ "set skip on ${epair_sync}a" \
+ "pass keep state"
+
+ atf_check -s exit:3 env PYTHONPATH=${common_dir} \
+ $(atf_get_srcdir)/pfsync_defer.py \
+ --syncdev ${epair_sync}b \
+ --indev ${epair_in}b \
+ --outdev ${epair_out}b
+}
+
+defer_cleanup()
+{
+ pfsynct_cleanup
+}
+
+atf_test_case "bulk" "cleanup"
+bulk_head()
+{
+ atf_set descr 'Test bulk updates'
+ atf_set require.user root
+}
+
+bulk_body()
+{
+ pfsynct_init
+
+ epair_sync=$(vnet_mkepair)
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+
+ vnet_mkjail one ${epair_one}a ${epair_sync}a
+ vnet_mkjail two ${epair_two}a ${epair_sync}b
+
+ # pfsync interface
+ jexec one ifconfig ${epair_sync}a 192.0.2.1/24 up
+ jexec one ifconfig ${epair_one}a 198.51.100.1/24 up
+ jexec one ifconfig pfsync0 \
+ syncdev ${epair_sync}a \
+ maxupd 1\
+ up
+ jexec two ifconfig ${epair_two}a 198.51.100.2/24 up
+ jexec two ifconfig ${epair_sync}b 192.0.2.2/24 up
+
+ # Enable pf
+ jexec one pfctl -e
+ pft_set_rules one \
+ "set skip on ${epair_sync}a" \
+ "pass keep state"
+ jexec two pfctl -e
+ pft_set_rules two \
+ "set skip on ${epair_sync}b" \
+ "pass keep state"
+
+ ifconfig ${epair_one}b 198.51.100.254/24 up
+
+ # Create state prior to setting up pfsync
+ ping -c 1 -S 198.51.100.254 198.51.100.1
+
+ # Wait before setting up pfsync on two, so we don't accidentally catch
+ # the update anyway.
+ sleep 1
+
+ # Now set up pfsync in jail two
+ jexec two ifconfig pfsync0 \
+ syncdev ${epair_sync}b \
+ up
+
+ # Give pfsync time to do its thing
+ sleep 2
+
+ jexec two pfctl -s states
+ if ! jexec two pfctl -s states | grep icmp | grep 198.51.100.1 | \
+ grep 198.51.100.2 ; then
+ atf_fail "state not found on synced host"
+ fi
+}
+
+bulk_cleanup()
+{
+ pfsynct_cleanup
+}
+
+atf_test_case "pbr" "cleanup"
+pbr_head()
+{
+ atf_set descr 'route_to and reply_to directives test'
+ atf_set require.user root
+ atf_set timeout '600'
+}
+
+pbr_body()
+{
+ pbr_common_body
+}
+
+pbr_cleanup()
+{
+ pbr_common_cleanup
+}
+
+atf_test_case "pfsync_pbr" "cleanup"
+pfsync_pbr_head()
+{
+ atf_set descr 'route_to and reply_to directives pfsync test'
+ atf_set require.user root
+ atf_set timeout '600'
+}
+
+pfsync_pbr_body()
+{
+ pbr_common_body backup_promotion
+}
+
+pfsync_pbr_cleanup()
+{
+ pbr_common_cleanup
+}
+
+pbr_common_body()
+{
+ # + builds bellow topology and initiate a single ping session
+ # from client to server.
+ # + gw* forward traffic through pbr not fib lookups.
+ # + if backup_promotion arg is given, a carp failover event occurs
+ # during the ping session on both gateways.
+ # ┌──────┐
+ # │client│
+ # └───┬──┘
+ # │
+ # ┌───┴───┐
+ # │bridge0│
+ # └┬─────┬┘
+ # │ │
+ # ┌────────────────┴─┐ ┌─┴────────────────┐
+ # │gw_route_to_master├─┤gw_route_to_backup│
+ # └────────────────┬─┘ └─┬────────────────┘
+ # │ │
+ # ┌┴─────┴┐
+ # │bridge1│
+ # └┬─────┬┘
+ # │ │
+ # ┌────────────────┴─┐ ┌─┴────────────────┐
+ # │gw_reply_to_master├─┤gw_reply_to_backup│
+ # └────────────────┬─┘ └─┬────────────────┘
+ # │ │
+ # ┌┴─────┴┐
+ # │bridge2│
+ # └───┬───┘
+ # │
+ # ┌───┴──┐
+ # │server│
+ # └──────┘
+
+ if ! kldstat -q -m carp
+ then
+ atf_skip "This test requires carp"
+ fi
+ pfsynct_init
+ vnet_init_bridge
+
+ bridge0=$(vnet_mkbridge)
+ bridge1=$(vnet_mkbridge)
+ bridge2=$(vnet_mkbridge)
+
+ epair_sync_gw_route_to=$(vnet_mkepair)
+ epair_sync_gw_reply_to=$(vnet_mkepair)
+ epair_client_bridge0=$(vnet_mkepair)
+
+ epair_gw_route_to_master_bridge0=$(vnet_mkepair)
+ epair_gw_route_to_backup_bridge0=$(vnet_mkepair)
+ epair_gw_route_to_master_bridge1=$(vnet_mkepair)
+ epair_gw_route_to_backup_bridge1=$(vnet_mkepair)
+
+ epair_gw_reply_to_master_bridge1=$(vnet_mkepair)
+ epair_gw_reply_to_backup_bridge1=$(vnet_mkepair)
+ epair_gw_reply_to_master_bridge2=$(vnet_mkepair)
+ epair_gw_reply_to_backup_bridge2=$(vnet_mkepair)
+
+ epair_server_bridge2=$(vnet_mkepair)
+
+ ifconfig ${bridge0} up
+ ifconfig ${epair_client_bridge0}b up
+ ifconfig ${epair_gw_route_to_master_bridge0}b up
+ ifconfig ${epair_gw_route_to_backup_bridge0}b up
+ ifconfig ${bridge0} \
+ addm ${epair_client_bridge0}b \
+ addm ${epair_gw_route_to_master_bridge0}b \
+ addm ${epair_gw_route_to_backup_bridge0}b
+
+ ifconfig ${bridge1} up
+ ifconfig ${epair_gw_route_to_master_bridge1}b up
+ ifconfig ${epair_gw_route_to_backup_bridge1}b up
+ ifconfig ${epair_gw_reply_to_master_bridge1}b up
+ ifconfig ${epair_gw_reply_to_backup_bridge1}b up
+ ifconfig ${bridge1} \
+ addm ${epair_gw_route_to_master_bridge1}b \
+ addm ${epair_gw_route_to_backup_bridge1}b \
+ addm ${epair_gw_reply_to_master_bridge1}b \
+ addm ${epair_gw_reply_to_backup_bridge1}b
+
+ ifconfig ${bridge2} up
+ ifconfig ${epair_gw_reply_to_master_bridge2}b up
+ ifconfig ${epair_gw_reply_to_backup_bridge2}b up
+ ifconfig ${epair_server_bridge2}b up
+ ifconfig ${bridge2} \
+ addm ${epair_gw_reply_to_master_bridge2}b \
+ addm ${epair_gw_reply_to_backup_bridge2}b \
+ addm ${epair_server_bridge2}b
+
+ vnet_mkjail client ${epair_client_bridge0}a
+ jexec client hostname client
+ vnet_mkjail gw_route_to_master \
+ ${epair_gw_route_to_master_bridge0}a \
+ ${epair_gw_route_to_master_bridge1}a \
+ ${epair_sync_gw_route_to}a
+ jexec gw_route_to_master hostname gw_route_to_master
+ vnet_mkjail gw_route_to_backup \
+ ${epair_gw_route_to_backup_bridge0}a \
+ ${epair_gw_route_to_backup_bridge1}a \
+ ${epair_sync_gw_route_to}b
+ jexec gw_route_to_backup hostname gw_route_to_backup
+ vnet_mkjail gw_reply_to_master \
+ ${epair_gw_reply_to_master_bridge1}a \
+ ${epair_gw_reply_to_master_bridge2}a \
+ ${epair_sync_gw_reply_to}a
+ jexec gw_reply_to_master hostname gw_reply_to_master
+ vnet_mkjail gw_reply_to_backup \
+ ${epair_gw_reply_to_backup_bridge1}a \
+ ${epair_gw_reply_to_backup_bridge2}a \
+ ${epair_sync_gw_reply_to}b
+ jexec gw_reply_to_backup hostname gw_reply_to_backup
+ vnet_mkjail server ${epair_server_bridge2}a
+ jexec server hostname server
+
+ jexec client ifconfig ${epair_client_bridge0}a inet 198.18.0.1/24 up
+ jexec client route add 198.18.2.0/24 198.18.0.10
+
+ jexec gw_route_to_master ifconfig ${epair_sync_gw_route_to}a \
+ inet 198.19.10.1/24 up
+ jexec gw_route_to_master ifconfig ${epair_gw_route_to_master_bridge0}a \
+ inet 198.18.0.8/24 up
+ jexec gw_route_to_master ifconfig ${epair_gw_route_to_master_bridge0}a \
+ alias 198.18.0.10/32 vhid 10 pass 3WjvVVw7 advskew 50
+ jexec gw_route_to_master ifconfig ${epair_gw_route_to_master_bridge1}a \
+ inet 198.18.1.8/24 up
+ jexec gw_route_to_master ifconfig ${epair_gw_route_to_master_bridge1}a \
+ alias 198.18.1.10/32 vhid 11 pass 3WjvVVw7 advskew 50
+ jexec gw_route_to_master sysctl net.inet.ip.forwarding=1
+ jexec gw_route_to_master sysctl net.inet.carp.preempt=1
+
+ vnet_ifrename_jail gw_route_to_master ${epair_sync_gw_route_to}a if_pfsync
+ vnet_ifrename_jail gw_route_to_master ${epair_gw_route_to_master_bridge0}a if_br0
+ vnet_ifrename_jail gw_route_to_master ${epair_gw_route_to_master_bridge1}a if_br1
+
+ jexec gw_route_to_master ifconfig pfsync0 \
+ syncpeer 198.19.10.2 \
+ syncdev if_pfsync \
+ maxupd 1 \
+ up
+ pft_set_rules gw_route_to_master \
+ "keep_state = 'tag auth_packet keep state'" \
+ "set timeout { icmp.first 120, icmp.error 60 }" \
+ "block log all" \
+ "pass quick on if_pfsync proto pfsync keep state (no-sync)" \
+ "pass quick on { if_br0 if_br1 } proto carp keep state (no-sync)" \
+ "block drop in quick to 224.0.0.18/32" \
+ "pass out quick tagged auth_packet keep state" \
+ "pass in quick log on if_br0 route-to (if_br1 198.18.1.20) proto { icmp udp tcp } from 198.18.0.0/24 to 198.18.2.0/24 \$keep_state"
+ jexec gw_route_to_master pfctl -e
+
+ jexec gw_route_to_backup ifconfig ${epair_sync_gw_route_to}b \
+ inet 198.19.10.2/24 up
+ jexec gw_route_to_backup ifconfig ${epair_gw_route_to_backup_bridge0}a \
+ inet 198.18.0.9/24 up
+ jexec gw_route_to_backup ifconfig ${epair_gw_route_to_backup_bridge0}a \
+ alias 198.18.0.10/32 vhid 10 pass 3WjvVVw7 advskew 100
+ jexec gw_route_to_backup ifconfig ${epair_gw_route_to_backup_bridge1}a \
+ inet 198.18.1.9/24 up
+ jexec gw_route_to_backup ifconfig ${epair_gw_route_to_backup_bridge1}a \
+ alias 198.18.1.10/32 vhid 11 pass 3WjvVVw7 advskew 100
+ jexec gw_route_to_backup sysctl net.inet.ip.forwarding=1
+ jexec gw_route_to_backup sysctl net.inet.carp.preempt=1
+
+ vnet_ifrename_jail gw_route_to_backup ${epair_sync_gw_route_to}b if_pfsync
+ vnet_ifrename_jail gw_route_to_backup ${epair_gw_route_to_backup_bridge0}a if_br0
+ vnet_ifrename_jail gw_route_to_backup ${epair_gw_route_to_backup_bridge1}a if_br1
+
+ jexec gw_route_to_backup ifconfig pfsync0 \
+ syncpeer 198.19.10.1 \
+ syncdev if_pfsync \
+ up
+ pft_set_rules gw_route_to_backup \
+ "keep_state = 'tag auth_packet keep state'" \
+ "set timeout { icmp.first 120, icmp.error 60 }" \
+ "block log all" \
+ "pass quick on if_pfsync proto pfsync keep state (no-sync)" \
+ "pass quick on { if_br0 if_br1 } proto carp keep state (no-sync)" \
+ "block drop in quick to 224.0.0.18/32" \
+ "pass out quick tagged auth_packet keep state" \
+ "pass in quick log on if_br0 route-to (if_br1 198.18.1.20) proto { icmp udp tcp } from 198.18.0.0/24 to 198.18.2.0/24 \$keep_state"
+ jexec gw_route_to_backup pfctl -e
+
+ jexec gw_reply_to_master ifconfig ${epair_sync_gw_reply_to}a \
+ inet 198.19.20.1/24 up
+ jexec gw_reply_to_master ifconfig ${epair_gw_reply_to_master_bridge1}a \
+ inet 198.18.1.18/24 up
+ jexec gw_reply_to_master ifconfig ${epair_gw_reply_to_master_bridge1}a \
+ alias 198.18.1.20/32 vhid 21 pass 3WjvVVw7 advskew 50
+ jexec gw_reply_to_master ifconfig ${epair_gw_reply_to_master_bridge2}a \
+ inet 198.18.2.18/24 up
+ jexec gw_reply_to_master ifconfig ${epair_gw_reply_to_master_bridge2}a \
+ alias 198.18.2.20/32 vhid 22 pass 3WjvVVw7 advskew 50
+ jexec gw_reply_to_master sysctl net.inet.ip.forwarding=1
+ jexec gw_reply_to_master sysctl net.inet.carp.preempt=1
+
+ vnet_ifrename_jail gw_reply_to_master ${epair_sync_gw_reply_to}a if_pfsync
+ vnet_ifrename_jail gw_reply_to_master ${epair_gw_reply_to_master_bridge1}a if_br1
+ vnet_ifrename_jail gw_reply_to_master ${epair_gw_reply_to_master_bridge2}a if_br2
+
+ jexec gw_reply_to_master ifconfig pfsync0 \
+ syncpeer 198.19.20.2 \
+ syncdev if_pfsync \
+ maxupd 1 \
+ up
+ pft_set_rules gw_reply_to_master \
+ "set timeout { icmp.first 120, icmp.error 60 }" \
+ "block log all" \
+ "pass quick on if_pfsync proto pfsync keep state (no-sync)" \
+ "pass quick on { if_br1 if_br2 } proto carp keep state (no-sync)" \
+ "block drop in quick to 224.0.0.18/32" \
+ "pass out quick on if_br2 reply-to (if_br1 198.18.1.10) tagged auth_packet_reply_to keep state" \
+ "pass in quick log on if_br1 proto { icmp udp tcp } from 198.18.0.0/24 to 198.18.2.0/24 tag auth_packet_reply_to keep state"
+ jexec gw_reply_to_master pfctl -e
+
+ jexec gw_reply_to_backup ifconfig ${epair_sync_gw_reply_to}b \
+ inet 198.19.20.2/24 up
+ jexec gw_reply_to_backup ifconfig ${epair_gw_reply_to_backup_bridge1}a \
+ inet 198.18.1.19/24 up
+ jexec gw_reply_to_backup ifconfig ${epair_gw_reply_to_backup_bridge1}a \
+ alias 198.18.1.20/32 vhid 21 pass 3WjvVVw7 advskew 100
+ jexec gw_reply_to_backup ifconfig ${epair_gw_reply_to_backup_bridge2}a \
+ inet 198.18.2.19/24 up
+ jexec gw_reply_to_backup ifconfig ${epair_gw_reply_to_backup_bridge2}a \
+ alias 198.18.2.20/32 vhid 22 pass 3WjvVVw7 advskew 100
+ jexec gw_reply_to_backup sysctl net.inet.ip.forwarding=1
+ jexec gw_reply_to_backup sysctl net.inet.carp.preempt=1
+
+ vnet_ifrename_jail gw_reply_to_backup ${epair_sync_gw_reply_to}b if_pfsync
+ vnet_ifrename_jail gw_reply_to_backup ${epair_gw_reply_to_backup_bridge1}a if_br1
+ vnet_ifrename_jail gw_reply_to_backup ${epair_gw_reply_to_backup_bridge2}a if_br2
+
+ jexec gw_reply_to_backup ifconfig pfsync0 \
+ syncpeer 198.19.20.1 \
+ syncdev if_pfsync \
+ up
+ pft_set_rules gw_reply_to_backup \
+ "set timeout { icmp.first 120, icmp.error 60 }" \
+ "block log all" \
+ "pass quick on if_pfsync proto pfsync keep state (no-sync)" \
+ "pass quick on { if_br1 if_br2 } proto carp keep state (no-sync)" \
+ "block drop in quick to 224.0.0.18/32" \
+ "pass out quick on if_br2 reply-to (if_br1 198.18.1.10) tagged auth_packet_reply_to keep state" \
+ "pass in quick log on if_br1 proto { icmp udp tcp } from 198.18.0.0/24 to 198.18.2.0/24 tag auth_packet_reply_to keep state"
+ jexec gw_reply_to_backup pfctl -e
+
+ jexec server ifconfig ${epair_server_bridge2}a inet 198.18.2.1/24 up
+ jexec server route add 198.18.0.0/24 198.18.2.20
+
+ # Waiting for platform to settle
+ while ! jexec gw_route_to_backup ifconfig | grep 'carp: BACKUP'
+ do
+ sleep 1
+ done
+ while ! jexec gw_reply_to_backup ifconfig | grep 'carp: BACKUP'
+ do
+ sleep 1
+ done
+ while ! jexec client ping -c 10 198.18.2.1 | grep ', 0.0% packet loss'
+ do
+ sleep 1
+ done
+
+ # Checking cluster members pf.conf checksums match
+ gw_route_to_master_checksum=$(jexec gw_route_to_master pfctl -si -v | grep 'Checksum:' | cut -d ' ' -f 2)
+ gw_route_to_backup_checksum=$(jexec gw_route_to_backup pfctl -si -v | grep 'Checksum:' | cut -d ' ' -f 2)
+ gw_reply_to_master_checksum=$(jexec gw_reply_to_master pfctl -si -v | grep 'Checksum:' | cut -d ' ' -f 2)
+ gw_reply_to_backup_checksum=$(jexec gw_reply_to_backup pfctl -si -v | grep 'Checksum:' | cut -d ' ' -f 2)
+ if [ "$gw_route_to_master_checksum" != "$gw_route_to_backup_checksum" ]
+ then
+ atf_fail "gw_route_to cluster members pf.conf do not match each others"
+ fi
+ if [ "$gw_reply_to_master_checksum" != "$gw_reply_to_backup_checksum" ]
+ then
+ atf_fail "gw_reply_to cluster members pf.conf do not match each others"
+ fi
+
+ # Creating state entries
+ (jexec client ping -c 10 198.18.2.1 >ping.stdout) &
+
+ if [ "$1" = "backup_promotion" ]
+ then
+ sleep 1
+ jexec gw_route_to_backup ifconfig if_br0 vhid 10 advskew 0
+ jexec gw_route_to_backup ifconfig if_br1 vhid 11 advskew 0
+ jexec gw_reply_to_backup ifconfig if_br1 vhid 21 advskew 0
+ jexec gw_reply_to_backup ifconfig if_br2 vhid 22 advskew 0
+ fi
+ while ! grep -q -e 'packet loss' ping.stdout
+ do
+ sleep 1
+ done
+
+ atf_check -s exit:0 -e ignore -o ignore grep ', 0.0% packet loss' ping.stdout
+}
+
+pbr_common_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "ipsec" "cleanup"
+ipsec_head()
+{
+ atf_set descr 'Transport pfsync over IPSec'
+ atf_set require.user root
+}
+
+ipsec_body()
+{
+ if ! sysctl -q kern.features.ipsec >/dev/null ; then
+ atf_skip "This test requires ipsec"
+ fi
+
+ # Run the common test, to set up pfsync
+ common_body
+
+ # But we want unicast pfsync
+ jexec one ifconfig pfsync0 syncpeer 192.0.2.2
+ jexec two ifconfig pfsync0 syncpeer 192.0.2.1
+
+ # Flush existing states
+ jexec one pfctl -Fs
+ jexec two pfctl -Fs
+
+ # Now define an ipsec policy to run over the epair_sync interfaces
+ echo "flush;
+ spdflush;
+ spdadd 192.0.2.1/32 192.0.2.2/32 any -P out ipsec esp/transport//require;
+ spdadd 192.0.2.2/32 192.0.2.1/32 any -P in ipsec esp/transport//require;
+ add 192.0.2.1 192.0.2.2 esp 0x1000 -E aes-gcm-16 \"12345678901234567890\";
+ add 192.0.2.2 192.0.2.1 esp 0x1001 -E aes-gcm-16 \"12345678901234567890\";" \
+ | jexec one setkey -c
+
+ echo "flush;
+ spdflush;
+ spdadd 192.0.2.2/32 192.0.2.1/32 any -P out ipsec esp/transport//require;
+ spdadd 192.0.2.1/32 192.0.2.2/32 any -P in ipsec esp/transport//require;
+ add 192.0.2.1 192.0.2.2 esp 0x1000 -E aes-gcm-16 \"12345678901234567891\";
+ add 192.0.2.2 192.0.2.1 esp 0x1001 -E aes-gcm-16 \"12345678901234567891\";" \
+ | jexec two setkey -c
+
+ # We've set incompatible keys, so pfsync will be broken.
+ ping -c 1 -S 198.51.100.254 198.51.100.1
+
+ # Give pfsync time to do its thing
+ sleep 2
+
+ if jexec two pfctl -s states | grep icmp | grep 198.51.100.1 | \
+ grep 198.51.100.2 ; then
+ atf_fail "state synced although IPSec should have prevented it"
+ fi
+
+ # Flush existing states
+ jexec one pfctl -Fs
+ jexec two pfctl -Fs
+
+ # Fix the IPSec key to match
+ echo "flush;
+ spdflush;
+ spdadd 192.0.2.2/32 192.0.2.1/32 any -P out ipsec esp/transport//require;
+ spdadd 192.0.2.1/32 192.0.2.2/32 any -P in ipsec esp/transport//require;
+ add 192.0.2.1 192.0.2.2 esp 0x1000 -E aes-gcm-16 \"12345678901234567890\";
+ add 192.0.2.2 192.0.2.1 esp 0x1001 -E aes-gcm-16 \"12345678901234567890\";" \
+ | jexec two setkey -c
+
+ ping -c 1 -S 198.51.100.254 198.51.100.1
+
+ # Give pfsync time to do its thing
+ sleep 2
+
+ if ! jexec two pfctl -s states | grep icmp | grep 198.51.100.1 | \
+ grep 198.51.100.2 ; then
+ atf_fail "state not found on synced host"
+ fi
+}
+
+ipsec_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "timeout" "cleanup"
+timeout_head()
+{
+ atf_set descr 'Trigger pfsync_timeout()'
+ atf_set require.user root
+}
+
+timeout_body()
+{
+ pft_init
+
+ vnet_mkjail one
+
+ jexec one ifconfig lo0 127.0.0.1/8 up
+ jexec one ifconfig lo0 inet6 ::1/128 up
+
+ pft_set_rules one \
+ "pass all"
+ jexec one pfctl -e
+ jexec one ifconfig pfsync0 defer up
+
+ jexec one ping -c 1 ::1
+ jexec one ping -c 1 127.0.0.1
+
+ # Give pfsync_timeout() time to fire (a callout on a 1 second delay)
+ sleep 2
+}
+
+timeout_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "basic_ipv6_unicast" "cleanup"
+basic_ipv6_unicast_head()
+{
+ atf_set descr 'Basic pfsync test (IPv6)'
+ atf_set require.user root
+}
+
+basic_ipv6_unicast_body()
+{
+ pfsynct_init
+
+ epair_sync=$(vnet_mkepair)
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+
+ vnet_mkjail one ${epair_one}a ${epair_sync}a
+ vnet_mkjail two ${epair_two}a ${epair_sync}b
+
+ # pfsync interface
+ jexec one ifconfig ${epair_sync}a inet6 fd2c::1/64 no_dad up
+ jexec one ifconfig ${epair_one}a inet6 fd2b::1/64 no_dad up
+ jexec one ifconfig pfsync0 \
+ syncdev ${epair_sync}a \
+ syncpeer fd2c::2 \
+ maxupd 1 \
+ up
+ jexec two ifconfig ${epair_two}a inet6 fd2b::2/64 no_dad up
+ jexec two ifconfig ${epair_sync}b inet6 fd2c::2/64 no_dad up
+ jexec two ifconfig pfsync0 \
+ syncdev ${epair_sync}b \
+ syncpeer fd2c::1 \
+ maxupd 1 \
+ up
+
+ # Enable pf!
+ jexec one pfctl -e
+ pft_set_rules one \
+ "block on ${epair_sync}a inet" \
+ "pass out keep state"
+ jexec two pfctl -e
+ pft_set_rules two \
+ "block on ${epair_sync}b inet" \
+ "pass out keep state"
+
+ ifconfig ${epair_one}b inet6 fd2b::f0/64 no_dad up
+
+ ping6 -c 1 -S fd2b::f0 fd2b::1
+
+ # Give pfsync time to do its thing
+ sleep 2
+
+ if ! jexec two pfctl -s states | grep icmp | grep fd2b::1 | \
+ grep fd2b::f0 ; then
+ atf_fail "state not found on synced host"
+ fi
+}
+
+basic_ipv6_unicast_cleanup()
+{
+ pfsynct_cleanup
+}
+
+atf_test_case "basic_ipv6" "cleanup"
+basic_ipv6_head()
+{
+ atf_set descr 'Basic pfsync test (IPv6)'
+ atf_set require.user root
+}
+
+basic_ipv6_body()
+{
+ pfsynct_init
+
+ epair_sync=$(vnet_mkepair)
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+
+ vnet_mkjail one ${epair_one}a ${epair_sync}a
+ vnet_mkjail two ${epair_two}a ${epair_sync}b
+
+ # pfsync interface
+ jexec one ifconfig ${epair_sync}a inet6 fd2c::1/64 no_dad up
+ jexec one ifconfig ${epair_one}a inet6 fd2b::1/64 no_dad up
+ jexec one ifconfig pfsync0 \
+ syncdev ${epair_sync}a \
+ syncpeer ff12::f0 \
+ maxupd 1 \
+ up
+ jexec two ifconfig ${epair_two}a inet6 fd2b::2/64 no_dad up
+ jexec two ifconfig ${epair_sync}b inet6 fd2c::2/64 no_dad up
+ jexec two ifconfig pfsync0 \
+ syncdev ${epair_sync}b \
+ syncpeer ff12::f0 \
+ maxupd 1 \
+ up
+
+ # Enable pf!
+ jexec one pfctl -e
+ pft_set_rules one \
+ "block on ${epair_sync}a inet" \
+ "pass out keep state"
+ jexec two pfctl -e
+ pft_set_rules two \
+ "block on ${epair_sync}b inet" \
+ "pass out keep state"
+
+ ifconfig ${epair_one}b inet6 fd2b::f0/64 no_dad up
+
+ ping6 -c 1 -S fd2b::f0 fd2b::1
+
+ # Give pfsync time to do its thing
+ sleep 2
+
+ if ! jexec two pfctl -s states | grep icmp | grep fd2b::1 | \
+ grep fd2b::f0 ; then
+ atf_fail "state not found on synced host"
+ fi
+}
+
+basic_ipv6_cleanup()
+{
+ pfsynct_cleanup
+}
+
+atf_test_case "rtable" "cleanup"
+rtable_head()
+{
+ atf_set descr 'Test handling of invalid rtableid'
+ atf_set require.user root
+}
+
+rtable_body()
+{
+ pfsynct_init
+
+ epair_sync=$(vnet_mkepair)
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+
+ vnet_mkjail one ${epair_one}a ${epair_sync}a
+ vnet_mkjail two ${epair_two}a ${epair_sync}b
+
+ # pfsync interface
+ jexec one ifconfig ${epair_sync}a 192.0.2.1/24 up
+ jexec one ifconfig ${epair_one}a 198.51.100.1/24 up
+ jexec one ifconfig pfsync0 \
+ syncdev ${epair_sync}a \
+ maxupd 1 \
+ up
+ jexec two ifconfig ${epair_two}a 198.51.100.1/24 up
+ jexec two ifconfig ${epair_sync}b 192.0.2.2/24 up
+ jexec two ifconfig pfsync0 \
+ syncdev ${epair_sync}b \
+ maxupd 1 \
+ up
+
+ # Make life easy, give ${epair_two}a the same mac addrss as ${epair_one}a
+ mac=$(jexec one ifconfig ${epair_one}a | awk '/ether/ { print($2); }')
+ jexec two ifconfig ${epair_two}a ether ${mac}
+
+ # Enable pf!
+ jexec one /sbin/sysctl net.fibs=8
+ jexec one pfctl -e
+ pft_set_rules one \
+ "set skip on ${epair_sync}a" \
+ "pass rtable 3 keep state"
+ # No extra fibs in two
+ jexec two pfctl -e
+ pft_set_rules two \
+ "set skip on ${epair_sync}b" \
+ "pass keep state"
+
+ ifconfig ${epair_one}b 198.51.100.254/24 up
+ ifconfig ${epair_two}b 198.51.100.253/24 up
+
+ # Create a new state
+ env PYTHONPATH=${common_dir} \
+ ${common_dir}/pft_ping.py \
+ --sendif ${epair_one}b \
+ --fromaddr 198.51.100.254 \
+ --to 198.51.100.1 \
+ --recvif ${epair_one}b
+
+ # Now
+ jexec one pfctl -ss -vv
+ sleep 2
+
+ # Now try to use that state on jail two
+ env PYTHONPATH=${common_dir} \
+ ${common_dir}/pft_ping.py \
+ --sendif ${epair_two}b \
+ --fromaddr 198.51.100.254 \
+ --to 198.51.100.1 \
+ --recvif ${epair_two}b
+
+ echo one
+ jexec one pfctl -ss -vv
+ jexec one pfctl -sr -vv
+ echo two
+ jexec two pfctl -ss -vv
+ jexec two pfctl -sr -vv
+}
+
+rtable_cleanup()
+{
+ pfsynct_cleanup
+}
+
+route_to_common_head()
+{
+ pfsync_version=$1
+ shift
+
+ pfsynct_init
+
+ epair_sync=$(vnet_mkepair)
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+ epair_out_one=$(vnet_mkepair)
+ epair_out_two=$(vnet_mkepair)
+
+ vnet_mkjail one ${epair_one}a ${epair_sync}a ${epair_out_one}a
+ vnet_mkjail two ${epair_two}a ${epair_sync}b ${epair_out_two}a
+
+ # pfsync interface
+ jexec one ifconfig ${epair_sync}a 192.0.2.1/24 up
+ jexec one ifconfig ${epair_one}a 198.51.100.1/24 up
+ jexec one ifconfig ${epair_out_one}a 203.0.113.1/24 up
+ jexec one ifconfig ${epair_out_one}a name outif
+ jexec one sysctl net.inet.ip.forwarding=1
+ jexec one arp -s 203.0.113.254 00:01:02:03:04:05
+ jexec one ifconfig pfsync0 \
+ syncdev ${epair_sync}a \
+ maxupd 1 \
+ version $pfsync_version \
+ up
+
+ jexec two ifconfig ${epair_sync}b 192.0.2.2/24 up
+ jexec two ifconfig ${epair_two}a 198.51.100.2/24 up
+ jexec two ifconfig ${epair_out_two}a 203.0.113.2/24 up
+ jexec two ifconfig ${epair_out_two}a name outif
+ jexec two sysctl net.inet.ip.forwarding=1
+ jexec two arp -s 203.0.113.254 00:01:02:03:04:05
+ jexec two ifconfig pfsync0 \
+ syncdev ${epair_sync}b \
+ maxupd 1 \
+ version $pfsync_version \
+ up
+
+ ifconfig ${epair_one}b 198.51.100.254/24 up
+ ifconfig ${epair_two}b 198.51.100.253/24 up
+ route add -net 203.0.113.0/24 198.51.100.1
+ ifconfig ${epair_two}b up
+ ifconfig ${epair_out_one}b up
+ ifconfig ${epair_out_two}b up
+}
+
+route_to_common_tail()
+{
+ atf_check -s exit:0 env PYTHONPATH=${common_dir} \
+ ${common_dir}/pft_ping.py \
+ --sendif ${epair_one}b \
+ --fromaddr 198.51.100.254 \
+ --to 203.0.113.254 \
+ --recvif ${epair_out_one}b
+
+ # Allow time for sync
+ sleep 2
+
+ states_one=$(mktemp)
+ states_two=$(mktemp)
+ jexec one pfctl -qvvss | normalize_pfctl_s > $states_one
+ jexec two pfctl -qvvss | normalize_pfctl_s > $states_two
+}
+
+atf_test_case "route_to_1301_body" "cleanup"
+route_to_1301_head()
+{
+ atf_set descr 'Test route-to with pfsync version 13.1'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+route_to_1301_body()
+{
+ route_to_common_head 1301
+
+ jexec one pfctl -e
+ pft_set_rules one \
+ "set skip on ${epair_sync}a" \
+ "pass out route-to (outif 203.0.113.254)"
+
+ jexec two pfctl -e
+ pft_set_rules two \
+ "set skip on ${epair_sync}b" \
+ "pass out route-to (outif 203.0.113.254)"
+
+ route_to_common_tail
+
+ # Sanity check
+ grep -qE 'all icmp 198.51.100.254 -> 203.0.113.254:8 .*, rule 0 .* route-to: 203.0.113.254@outif origif: outif' $states_one ||
+ atf_fail "State missing on router one"
+
+ # With identical ruleset the routing information is recovered from the matching rule.
+ grep -qE 'all icmp 198.51.100.254 -> 203.0.113.254:8 .*, rule 0 .* route-to: 203.0.113.254@outif' $states_two ||
+ atf_fail "State missing on router two"
+
+ true
+}
+
+route_to_1301_cleanup()
+{
+ pfsynct_cleanup
+}
+
+atf_test_case "route_to_1301_bad_ruleset" "cleanup"
+route_to_1301_bad_ruleset_head()
+{
+ atf_set descr 'Test route-to with pfsync version 13.1 and incompatible ruleset'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+route_to_1301_bad_ruleset_body()
+{
+ route_to_common_head 1301
+
+ jexec one pfctl -e
+ pft_set_rules one \
+ "set skip on ${epair_sync}a" \
+ "pass out route-to (outif 203.0.113.254)"
+
+ jexec two pfctl -e
+ pft_set_rules two \
+ "set debug loud" \
+ "set skip on ${epair_sync}b" \
+ "pass out route-to (outif 203.0.113.254)" \
+ "pass out proto tcp"
+
+ atf_check -s exit:0 env PYTHONPATH=${common_dir} \
+ ${common_dir}/pft_ping.py \
+ --sendif ${epair_one}b \
+ --fromaddr 198.51.100.254 \
+ --to 203.0.113.254 \
+ --recvif ${epair_out_one}b
+
+ route_to_common_tail
+
+ # Sanity check
+ grep -qE 'all icmp 198.51.100.254 -> 203.0.113.254:8 .*, rule 0 .* route-to: 203.0.113.254@outif origif: outif' $states_one ||
+ atf_fail "State missing on router one"
+
+ # Different ruleset on each router means the routing information recovery
+ # from rule is impossible. The state is not synced.
+ grep -qE 'all icmp 198.51.100.254 -> 203.0.113.254:8 .*' $states_two &&
+ atf_fail "State present on router two"
+
+ true
+}
+
+route_to_1301_bad_ruleset_cleanup()
+{
+ pfsynct_cleanup
+}
+
+atf_test_case "route_to_1301_bad_rpool" "cleanup"
+route_to_1301_bad_rpool_head()
+{
+ atf_set descr 'Test route-to with pfsync version 13.1 and different interface'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+route_to_1301_bad_rpool_body()
+{
+ route_to_common_head 1301
+
+ jexec one pfctl -e
+ pft_set_rules one \
+ "set skip on ${epair_sync}a" \
+ "pass out route-to { (outif 203.0.113.254) (outif 203.0.113.254) }"
+
+ jexec two pfctl -e
+ pft_set_rules two \
+ "set skip on ${epair_sync}b" \
+ "pass out route-to { (outif 203.0.113.254) (outif 203.0.113.254) }"
+
+ atf_check -s exit:0 env PYTHONPATH=${common_dir} \
+ ${common_dir}/pft_ping.py \
+ --sendif ${epair_one}b \
+ --fromaddr 198.51.100.254 \
+ --to 203.0.113.254 \
+ --recvif ${epair_out_one}b
+
+ route_to_common_tail
+
+ # Sanity check
+ grep -qE 'all icmp 198.51.100.254 -> 203.0.113.254:8 .*, rule 0 .* route-to: 203.0.113.254@outif origif: outif' $states_one ||
+ atf_fail "State missing on router one"
+
+ # The ruleset is identical but since the redirection pool contains multiple interfaces
+ # pfsync will not attempt to recover the routing information from the rule.
+ grep -qE 'all icmp 198.51.100.254 -> 203.0.113.254:8 .*' $states_two &&
+ atf_fail "State present on router two"
+
+ true
+}
+
+route_to_1301_bad_rpool_cleanup()
+{
+ pfsynct_cleanup
+}
+
+atf_test_case "route_to_1400_bad_ruleset" "cleanup"
+route_to_1400_bad_ruleset_head()
+{
+ atf_set descr 'Test route-to with pfsync version 14.0'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+route_to_1400_bad_ruleset_body()
+{
+ route_to_common_head 1400
+
+ jexec one pfctl -e
+ pft_set_rules one \
+ "set skip on ${epair_sync}a" \
+ "pass out route-to (outif 203.0.113.254)"
+
+ jexec two pfctl -e
+ pft_set_rules two \
+ "set skip on ${epair_sync}b"
+
+ route_to_common_tail
+
+ # Sanity check
+ grep -qE 'all icmp 198.51.100.254 -> 203.0.113.254:8 .*, rule 0 .* route-to: 203.0.113.254@outif origif: outif' $states_one ||
+ atf_fail "State missing on router one"
+
+ # Even with a different ruleset FreeBSD 14 syncs the state just fine.
+ # There's no recovery involved, the pfsync packet contains the routing information.
+ grep -qE 'all icmp 198.51.100.254 -> 203.0.113.254:8 .* route-to: 203.0.113.254@outif' $states_two ||
+ atf_fail "State missing on router two"
+
+ true
+}
+
+route_to_1400_bad_ruleset_cleanup()
+{
+ pfsynct_cleanup
+}
+
+atf_test_case "route_to_1400_bad_ifname" "cleanup"
+route_to_1400_bad_ifname_head()
+{
+ atf_set descr 'Test route-to with pfsync version 14.0'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+route_to_1400_bad_ifname_body()
+{
+ route_to_common_head 1400
+
+ jexec one pfctl -e
+ pft_set_rules one \
+ "set skip on ${epair_sync}a" \
+ "pass out route-to (outif 203.0.113.254)"
+
+ jexec two pfctl -e
+ jexec two ifconfig outif name outif_new
+ pft_set_rules two \
+ "set skip on ${epair_sync}b" \
+ "pass out route-to (outif_new 203.0.113.254)"
+
+ route_to_common_tail
+
+ # Sanity check
+ grep -qE 'all icmp 198.51.100.254 -> 203.0.113.254:8 .*, rule 0 .* route-to: 203.0.113.254@outif origif: outif' $states_one ||
+ atf_fail "State missing on router one"
+
+ # Since FreeBSD 14 never attempts recovery of missing routing information
+ # a state synced to a router with a different interface name is dropped.
+ grep -qE 'all icmp 198.51.100.254 -> 203.0.113.254:8 .*' $states_two &&
+ atf_fail "State present on router two"
+
+ true
+}
+
+route_to_1400_bad_ifname_cleanup()
+{
+ pfsynct_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "basic"
+ atf_add_test_case "basic_defer"
+ atf_add_test_case "defer"
+ atf_add_test_case "bulk"
+ atf_add_test_case "pbr"
+ atf_add_test_case "pfsync_pbr"
+ atf_add_test_case "ipsec"
+ atf_add_test_case "timeout"
+ atf_add_test_case "basic_ipv6_unicast"
+ atf_add_test_case "basic_ipv6"
+ atf_add_test_case "rtable"
+ atf_add_test_case "route_to_1301"
+ atf_add_test_case "route_to_1301_bad_ruleset"
+ atf_add_test_case "route_to_1301_bad_rpool"
+ atf_add_test_case "route_to_1400_bad_ruleset"
+ atf_add_test_case "route_to_1400_bad_ifname"
+}
diff --git a/tests/sys/netpfil/pf/pfsync_defer.py b/tests/sys/netpfil/pf/pfsync_defer.py
new file mode 100644
index 000000000000..0a258c8f27b3
--- /dev/null
+++ b/tests/sys/netpfil/pf/pfsync_defer.py
@@ -0,0 +1,130 @@
+#!/usr/bin/env python3
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2021 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import socket
+import sys
+import time
+from sniffer import Sniffer
+
+got_pfsync = False
+got_ping = False
+sent_ping = False
+
+def check_pfsync(args, packet):
+ global got_pfsync
+ global got_ping
+
+ ip = packet.getlayer(sp.IP)
+ if not ip:
+ return False
+
+ if ip.proto != 240:
+ return False
+
+ # Only look at the first packet
+ if got_pfsync:
+ return False
+
+ got_pfsync = time.time()
+
+ return False
+
+def check_reply(args, packet):
+ global got_pfsync
+ global got_ping
+
+ if not packet.getlayer(sp.ICMP):
+ return False
+
+ # Only look at the first packet
+ if got_ping:
+ return False
+
+ got_ping = time.time()
+
+ return False
+
+def ping(intf):
+ global sent_ping
+
+ ether = sp.Ether()
+ ip = sp.IP(dst="203.0.113.2", src="198.51.100.2")
+ icmp = sp.ICMP(type='echo-request')
+ raw = sp.raw(bytes.fromhex('00010203'))
+
+ req = ether / ip / icmp / raw
+ sp.sendp(req, iface=intf, verbose=False)
+ sent_ping = time.time()
+
+def main():
+ global got_pfsync
+ global got_ping
+ global sent_ping
+
+ parser = argparse.ArgumentParser("pfsync_defer.py",
+ description="pfsync defer mode test")
+ parser.add_argument('--syncdev', nargs=1,
+ required=True,
+ help='The pfsync interface')
+ parser.add_argument('--outdev', nargs=1,
+ required=True,
+ help='The interface we will send packets on')
+ parser.add_argument('--indev', nargs=1,
+ required=True,
+ help='The interface we will receive packets on')
+
+ args = parser.parse_args()
+
+ syncmon = Sniffer(args, check_pfsync, args.syncdev[0])
+ datamon = Sniffer(args, check_reply, args.indev[0])
+
+ # Send traffic on datadev, which should create state and produce a pfsync message
+ ping(args.outdev[0])
+
+ syncmon.join()
+ datamon.join()
+
+ if not got_pfsync:
+ sys.exit(1)
+
+ if not got_ping:
+ sys.exit(2)
+
+ # Deferred packets are delayed around 2.5s (unless the pfsync peer, which
+ # we don't have here, acks their state update earlier)
+ # Expect at least a second of delay, to be somewhat robust against
+ # scheduling-induced jitter.
+ if (sent_ping + 1) > got_ping:
+ sys.exit(3)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netpfil/pf/pft_ether.py b/tests/sys/netpfil/pf/pft_ether.py
new file mode 100644
index 000000000000..4efb974f7897
--- /dev/null
+++ b/tests/sys/netpfil/pf/pft_ether.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright © 2022. Rubicon Communications, LLC (Netgate). All Rights Reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+import socket
+import sys
+
+PAYLOAD_MAGIC = bytes.fromhex('42c0ffee')
+
+def ping(send_if, dst_ip, length):
+ ether = sp.Ether()
+ ip = sp.IP(dst=dst_ip)
+ icmp = sp.ICMP(type='echo-request')
+ raw = sp.raw(PAYLOAD_MAGIC)
+
+ req = ether / ip / icmp / raw
+ req = req.build()[0:length]
+
+ sp.sendp(req, iface=send_if, verbose=False)
+
+def main():
+ parser = argparse.ArgumentParser("pft_ether.py",
+ description="Ethernet test tool")
+ parser.add_argument('--sendif', nargs=1,
+ required=True,
+ help='The interface through which the packet(s) will be sent')
+ parser.add_argument('--to', nargs=1,
+ required=True,
+ help='The destination IP address for the ICMP echo request')
+ parser.add_argument('--len', nargs=1,
+ required=True,
+ help='The length of the packet')
+
+ args = parser.parse_args()
+
+ if '-' in args.len[0]:
+ s=args.len[0].split('-')
+ for i in range(int(s[0]), int(s[1]) + 1):
+ ping(args.sendif[0], args.to[0], i)
+ else:
+ ping(args.sendif[0], args.to[0], int(args.len[0]))
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netpfil/pf/pft_read_ipfix.py b/tests/sys/netpfil/pf/pft_read_ipfix.py
new file mode 100644
index 000000000000..02ef2ca5ab06
--- /dev/null
+++ b/tests/sys/netpfil/pf/pft_read_ipfix.py
@@ -0,0 +1,109 @@
+#!/usr/bin/env python3
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright © 2023. Rubicon Communications, LLC (Netgate). All Rights Reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+import argparse
+import logging
+logging.getLogger("scapy").setLevel(logging.CRITICAL)
+import scapy.all as sp
+
+def parse_ipfix(p):
+ if not sp.NetflowDataflowsetV9 in p:
+ # A template?
+ return
+
+ c = p
+ while sp.NetflowDataflowsetV9 in c:
+ datafl = c[sp.NetflowDataflowsetV9]
+ if datafl.templateID == 258:
+ # NAT
+ for r in datafl.records:
+ print("v=10,NAT=%d,proto=%s,src=%s-%s,dst=%s-%s" %
+ (int.from_bytes(r.natEvent, "big"), r.PROTOCOL,
+ r.IPV4_SRC_ADDR, r.postNATSourceIPv4Address,
+ r.IPV4_DST_ADDR, r.postNATDestinationIPv4Address))
+ elif datafl.templateID == 257:
+ # IPv6
+ for r in datafl.records:
+ print("v=10,IPv6,proto=%s,src=%s,dst=%s" %
+ (r.PROTOCOL, r.IPV6_SRC_ADDR, r.IPV6_DST_ADDR))
+ elif datafl.templateID == 256:
+ # IPv4
+ for r in datafl.records:
+ print("v=10,proto=%s,src=%s,dst=%s" %
+ (r.PROTOCOL, r.IPV4_SRC_ADDR, r.IPV4_DST_ADDR))
+
+ c = datafl.payload
+
+def receive(recvif, recvport):
+ pkts = sp.sniff(iface=recvif, timeout=65, filter="udp port 2055")
+
+ if len(pkts) == 0:
+ print("No data")
+ return
+
+ pkts = sp.ipfix_defragment(pkts)
+
+ for pkt in pkts:
+ udp = pkt.getlayer(sp.UDP)
+ if not udp:
+ continue
+
+ if udp.dport != recvport:
+ continue
+
+ if not sp.NetflowHeader in pkt:
+ continue
+
+ hdr = pkt.getlayer(sp.NetflowHeader)
+ if hdr.version == 5:
+ v5hdr = pkt.getlayer(sp.NetflowHeaderV5)
+ out=""
+ for i in range(1, v5hdr.count + 1):
+ r = pkt.getlayer(sp.NetflowRecordV5, nb=i)
+ print("v=%d,proto=%d,src=%s,dst=%s,srcport=%d,dstport=%d" %
+ (hdr.version, r.prot, r.src, r.dst, r.srcport, r.dstport))
+ elif hdr.version == 10:
+ parse_ipfix(pkt)
+
+def main():
+ parser = argparse.ArgumentParser("pft_read_ipfix.py",
+ description="IPFix test tool")
+ parser.add_argument('--recvif', nargs=1,
+ required=True,
+ help='The interface on which to look for packets')
+ parser.add_argument('--port', nargs=1,
+ required=True,
+ help='The port number')
+
+ args = parser.parse_args()
+
+ receive(args.recvif[0], int(args.port[0]))
+
+if __name__ == '__main__':
+ main()
+
diff --git a/tests/sys/netpfil/pf/prio.sh b/tests/sys/netpfil/pf/prio.sh
new file mode 100644
index 000000000000..c86f77cd25e0
--- /dev/null
+++ b/tests/sys/netpfil/pf/prio.sh
@@ -0,0 +1,72 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2022 Kristof Provost <kp@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "set_prio" "cleanup"
+set_prio_head()
+{
+ atf_set descr 'Test setting VLAN PCP'
+ atf_set require.user root
+}
+
+set_prio_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a.42 create
+ ifconfig ${epair}a up
+ ifconfig ${epair}a.42 192.0.2.1/24 up
+ ifconfig ${epair}a.42 vlandev ${epair}a vlan 42
+ echo ${epair}a.42 >> created_interfaces.lst
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b.42 create
+ jexec alcatraz ifconfig ${epair}b up
+ jexec alcatraz ifconfig ${epair}b.42 192.0.2.2/24 up
+ jexec alcatraz ifconfig ${epair}b.42 vlandev ${epair}b vlan 42
+
+ jexec alcatraz sysctl net.link.vlan.mtag_pcp=1
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "pass out set prio 4"
+
+ jexec alcatraz ping 192.0.2.1 &
+
+ atf_check -e ignore -o match:'.*vlan 42, p 4.*' tcpdump -n -i ${epair}a -e -c 4
+}
+
+set_prio_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "set_prio"
+}
diff --git a/tests/sys/netpfil/pf/proxy.sh b/tests/sys/netpfil/pf/proxy.sh
new file mode 100644
index 000000000000..78ce25930c04
--- /dev/null
+++ b/tests/sys/netpfil/pf/proxy.sh
@@ -0,0 +1,94 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2021 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+common_dir=$(atf_get_srcdir)/../common
+
+atf_test_case "ftp" "cleanup"
+ftp_head()
+{
+ atf_set descr 'Test ftp-proxy'
+ atf_set require.user root
+ atf_set require.progs twistd
+}
+
+ftp_body()
+{
+ pft_init
+
+ epair_client=$(vnet_mkepair)
+ epair_link=$(vnet_mkepair)
+
+ ifconfig ${epair_client}a 192.0.2.2/24 up
+ route add -net 198.51.100.0/24 192.0.2.1
+
+ vnet_mkjail fwd ${epair_client}b ${epair_link}a
+ jexec fwd ifconfig ${epair_client}b 192.0.2.1/24 up
+ jexec fwd ifconfig ${epair_link}a 198.51.100.1/24 up
+ jexec fwd ifconfig lo0 127.0.0.1/8 up
+ jexec fwd sysctl net.inet.ip.forwarding=1
+
+ vnet_mkjail srv ${epair_link}b
+ jexec srv ifconfig ${epair_link}b 198.51.100.2/24 up
+ jexec srv route add default 198.51.100.1
+
+ # Start FTP server in srv
+ jexec srv twistd --logfile=/dev/null ftp -r `pwd` -p 21
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 198.51.100.2
+
+ jexec fwd pfctl -e
+ pft_set_rules fwd \
+ "nat on ${epair_link}a inet from 192.0.2.0/24 to any -> (${epair_link}a)" \
+ "nat-anchor \"ftp-proxy/*\"" \
+ "rdr-anchor \"ftp-proxy/*\"" \
+ "rdr pass on ${epair_client}b proto tcp from 192.0.2.0/24 to any port 21 -> 127.0.0.1 port 8021" \
+ "anchor \"ftp-proxy/*\"" \
+ "pass out proto tcp from 127.0.0.1 to any port 21"
+ jexec fwd /usr/sbin/ftp-proxy
+
+ # Create a dummy file to download
+ echo 'foo' > remote.txt
+ echo -e 'epsv\nget remote.txt local.txt' | ftp -a 198.51.100.2
+
+ # Compare the downloaded file to the original
+ if ! diff -q local.txt remote.txt;
+ then
+ atf_fail 'Failed to retrieve file'
+ fi
+}
+
+ftp_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "ftp"
+}
diff --git a/tests/sys/netpfil/pf/rdr-srcport.py b/tests/sys/netpfil/pf/rdr-srcport.py
new file mode 100644
index 000000000000..633580582711
--- /dev/null
+++ b/tests/sys/netpfil/pf/rdr-srcport.py
@@ -0,0 +1,20 @@
+#
+# A helper script which accepts TCP connections and writes the remote port
+# number to the stream.
+#
+
+import socket
+
+def main():
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ s.bind(('0.0.0.0', 8888))
+ s.listen(5)
+
+ while True:
+ cs, addr = s.accept()
+ cs.sendall(str(addr[1]).encode())
+ cs.close()
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/sys/netpfil/pf/rdr.sh b/tests/sys/netpfil/pf/rdr.sh
new file mode 100644
index 000000000000..f7c920bbfa8f
--- /dev/null
+++ b/tests/sys/netpfil/pf/rdr.sh
@@ -0,0 +1,290 @@
+#
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright © 2023 Tom Jones <thj@freebsd.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+#
+# Test that rdr works for TCP with IPv6.
+#
+# a <-----> b <-----> c
+#
+# Test configures three jails (a, b and c) and connects them together with b as
+# a router between a and c.
+#
+# TCP traffic to b on port 80 is redirected to c on port 8000
+#
+# Test for incorrect checksums after the rewrite by looking at a packet capture (see bug 210860)
+#
+tcp_v6_setup()
+{
+ pft_init
+
+ j="rdr:tcp_v6"
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+
+ echo $epair_one
+ echo $epair_two
+
+ vnet_mkjail ${j}a ${epair_one}b
+ vnet_mkjail ${j}b ${epair_one}a ${epair_two}a
+ vnet_mkjail ${j}c ${epair_two}b
+
+ # configure addresses for b
+ jexec ${j}b ifconfig lo0 up
+ jexec ${j}b ifconfig ${epair_one}a inet6 2001:db8:a::1/64 up no_dad
+ jexec ${j}b ifconfig ${epair_two}a inet6 2001:db8:b::1/64 up no_dad
+
+ # configure addresses for a
+ jexec ${j}a ifconfig lo0 up
+ jexec ${j}a ifconfig ${epair_one}b inet6 2001:db8:a::2/64 up no_dad
+
+ # configure addresses for c
+ jexec ${j}c ifconfig lo0 up
+ jexec ${j}c ifconfig ${epair_two}b inet6 2001:db8:b::2/64 up no_dad
+
+ # enable forwarding in the b jail
+ jexec ${j}b sysctl net.inet6.ip6.forwarding=1
+
+ # add routes so a and c can find each other
+ jexec ${j}a route add -inet6 2001:db8:b::0/64 2001:db8:a::1
+ jexec ${j}c route add -inet6 2001:db8:a::0/64 2001:db8:b::1
+
+ jexec ${j}b pfctl -e
+}
+
+tcp_v6_common()
+{
+ pft_set_rules ${j}b "${1}"
+
+ # Check that a can reach c over the router
+ atf_check -s exit:0 -o ignore \
+ jexec ${j}a ping -6 -c 1 2001:db8:b::2
+
+ # capture packets on c so we can look for incorrect checksums
+ jexec ${j}c tcpdump --immediate-mode -w ${PWD}/${j}.pcap tcp and port 8000 &
+ tcpdumppid=$!
+
+ # start a web server and give it a second to start
+ jexec ${j}c python3 -m http.server &
+ sleep 1
+
+ # http directly from a to c -> a ---> b
+ atf_check -s exit:0 -o ignore \
+ jexec ${j}a fetch -T 1 -o /dev/null -q "http://[2001:db8:b::2]:8000"
+
+ # http from a to b with a redirect -> a ---> b
+ atf_check -s exit:0 -o ignore \
+ jexec ${j}a fetch -T 1 -o /dev/null -q "http://[2001:db8:a::1]:80"
+
+ # ask tcpdump to stop so we can check the packet capture
+ jexec ${j}c kill -s SIGINT $tcpdumppid
+
+ # Check for 'incorrect' in packet capture, this should tell us if
+ # checksums are bad with rdr rules
+ count=$(jexec ${j}c tcpdump -vvvv -r ${PWD}/${j}.pcap | grep incorrect | wc -l)
+ atf_check_equal " 0" "$count"
+}
+
+atf_test_case "tcp_v6_compat" "cleanup"
+tcp_v6_compat_head()
+{
+ atf_set descr 'TCP rdr with IPv6 with NAT rules'
+ atf_set require.user root
+ atf_set require.progs python3
+}
+
+tcp_v6_compat_body()
+{
+ tcp_v6_setup # Sets ${epair_…} variables
+ tcp_v6_common \
+ "rdr on ${epair_one}a proto tcp from any to any port 80 -> 2001:db8:b::2 port 8000"
+}
+
+tcp_v6_compat_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "tcp_v6_pass" "cleanup"
+tcp_v6_pass_head()
+{
+ atf_set descr 'TCP rdr with IPv6 with pass/match rules'
+ atf_set require.user root
+ atf_set require.progs python3
+}
+
+tcp_v6_pass_body()
+{
+ tcp_v6_setup # Sets ${epair_…} variables
+ tcp_v6_common \
+ "pass in on ${epair_one}a proto tcp from any to any port 80 rdr-to 2001:db8:b::2 port 8000"
+}
+
+tcp_v6_pass_cleanup()
+{
+ pft_cleanup
+}
+
+#
+# Test that rdr works for multiple TCP with same srcip and srcport.
+#
+# Four jails, a, b, c, d, are used:
+# - jail d runs a server on port 8888,
+# - jail a makes connections to the server, routed through jails b and c,
+# - jail b uses NAT to rewrite source addresses and ports to the same 2-tuple,
+# avoiding the need to use SO_REUSEADDR in jail a,
+# - jail c uses a redirect rule to map the destination address to the same
+# address and port, resulting in a NAT state conflict.
+#
+# In this case, the rdr rule should also rewrite the source port (again) to
+# resolve the state conflict.
+#
+srcport_setup()
+{
+ pft_init
+
+ j="rdr:srcport"
+ epair1=$(vnet_mkepair)
+ epair2=$(vnet_mkepair)
+ epair3=$(vnet_mkepair)
+
+ echo $epair_one
+ echo $epair_two
+
+ vnet_mkjail ${j}a ${epair1}a
+ vnet_mkjail ${j}b ${epair1}b ${epair2}a
+ vnet_mkjail ${j}c ${epair2}b ${epair3}a
+ vnet_mkjail ${j}d ${epair3}b
+
+ # configure addresses for a
+ jexec ${j}a ifconfig lo0 up
+ jexec ${j}a ifconfig ${epair1}a inet 198.51.100.50/24 up
+ jexec ${j}a ifconfig ${epair1}a inet alias 198.51.100.51/24
+ jexec ${j}a ifconfig ${epair1}a inet alias 198.51.100.52/24
+
+ # configure addresses for b
+ jexec ${j}b ifconfig lo0 up
+ jexec ${j}b ifconfig ${epair1}b inet 198.51.100.1/24 up
+ jexec ${j}b ifconfig ${epair2}a inet 198.51.101.2/24 up
+
+ # configure addresses for c
+ jexec ${j}c ifconfig lo0 up
+ jexec ${j}c ifconfig ${epair2}b inet 198.51.101.3/24 up
+ jexec ${j}c ifconfig ${epair2}b inet alias 198.51.101.4/24
+ jexec ${j}c ifconfig ${epair2}b inet alias 198.51.101.5/24
+ jexec ${j}c ifconfig ${epair3}a inet 203.0.113.1/24 up
+
+ # configure addresses for d
+ jexec ${j}d ifconfig lo0 up
+ jexec ${j}d ifconfig ${epair3}b inet 203.0.113.50/24 up
+
+ jexec ${j}b sysctl net.inet.ip.forwarding=1
+ jexec ${j}c sysctl net.inet.ip.forwarding=1
+ jexec ${j}b pfctl -e
+ jexec ${j}c pfctl -e
+}
+
+srcport_common()
+{
+ pft_set_rules ${j}b \
+ "set debug misc" \
+ "${1}"
+
+ pft_set_rules ${j}c \
+ "set debug misc" \
+ "${2}"
+
+ jexec ${j}a route add default 198.51.100.1
+ jexec ${j}c route add 198.51.100.0/24 198.51.101.2
+ jexec ${j}d route add 198.51.101.0/24 203.0.113.1
+
+ jexec ${j}d python3 $(atf_get_srcdir)/rdr-srcport.py &
+ sleep 1
+
+ echo a | jexec ${j}a nc -w 3 -s 198.51.100.50 -p 1234 198.51.101.3 7777 > port1
+
+ jexec ${j}a nc -s 198.51.100.51 -p 1234 198.51.101.4 7777 > port2 &
+ jexec ${j}a nc -s 198.51.100.52 -p 1234 198.51.101.5 7777 > port3 &
+ sleep 1
+
+ atf_check -o inline:"1234" cat port1
+ atf_check -o match:"[0-9]+" -o not-inline:"1234" cat port2
+ atf_check -o match:"[0-9]+" -o not-inline:"1234" cat port3
+}
+
+atf_test_case "srcport_compat" "cleanup"
+srcport_compat_head()
+{
+ atf_set descr 'TCP rdr srcport modulation with NAT rules'
+ atf_set require.user root
+ atf_set require.progs python3
+ atf_set timeout 9999
+}
+
+srcport_compat_body()
+{
+ srcport_setup # Sets ${epair_…} variables
+ srcport_common \
+ "nat on ${epair2}a inet from 198.51.100.0/24 to any -> ${epair2}a static-port" \
+ "rdr on ${epair2}b proto tcp from any to ${epair2}b port 7777 -> 203.0.113.50 port 8888"
+}
+
+srcport_compat_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "srcport_pass" "cleanup"
+srcport_pass_head()
+{
+ atf_set descr 'TCP rdr srcport modulation with pass/match rules'
+ atf_set require.user root
+ atf_set require.progs python3
+ atf_set timeout 9999
+}
+
+srcport_pass_body()
+{
+ srcport_setup # Sets ${epair_…} variables
+ srcport_common \
+ "pass out on ${epair2}a inet from 198.51.100.0/24 to any nat-to ${epair2}a static-port" \
+ "pass in on ${epair2}b proto tcp from any to ${epair2}b port 7777 rdr-to 203.0.113.50 port 8888"
+}
+
+srcport_pass_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "tcp_v6_compat"
+ atf_add_test_case "tcp_v6_pass"
+ atf_add_test_case "srcport_compat"
+ atf_add_test_case "srcport_pass"
+}
diff --git a/tests/sys/netpfil/pf/return.py b/tests/sys/netpfil/pf/return.py
new file mode 100644
index 000000000000..753012860764
--- /dev/null
+++ b/tests/sys/netpfil/pf/return.py
@@ -0,0 +1,153 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2025 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+import pytest
+import subprocess
+import re
+from atf_python.sys.net.tools import ToolsHelper
+from atf_python.sys.net.vnet import VnetTestTemplate
+
+def check(cmd):
+ ps = subprocess.Popen(cmd, shell=True)
+ ret = ps.wait()
+ if ret != 0:
+ raise Exception("Command %s returned %d" % (cmd, ret))
+
+class TestReturn(VnetTestTemplate):
+ REQUIRED_MODULES = [ "pf" ]
+ TOPOLOGY = {
+ "vnet1": {"ifaces": ["if1"]},
+ "vnet2": {"ifaces": ["if1", "if2"]},
+ "vnet3": {"ifaces": ["if2"]},
+ "if1": {"prefixes4": [("192.0.2.2/24", "192.0.2.1/24")]},
+ "if2": {"prefixes4": [("198.51.100.1/24", "198.51.100.2/24")]},
+ }
+
+ def vnet2_handler(self, vnet):
+ ifname = vnet.iface_alias_map["if1"].name
+ if2name = vnet.iface_alias_map["if2"].name
+
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.pf_rules([
+ "nat on %s inet from 192.0.2.0/24 to any -> (%s)" % (ifname, ifname),
+ "block return",
+ "pass inet proto icmp icmp-type echoreq",
+ ])
+
+ ToolsHelper.print_output("/sbin/sysctl net.inet.ip.forwarding=1")
+ ToolsHelper.print_output("/sbin/pfctl -x loud")
+
+ def vnet3_handler(self, vnet):
+ ToolsHelper.print_output("/sbin/route add default 198.51.100.1")
+
+ def common_setup(self):
+ ToolsHelper.print_output("/sbin/route add default 192.0.2.1")
+
+ # Sanity check
+ check("/sbin/ping -c 1 192.0.2.1")
+ check("/sbin/ping -c 1 198.51.100.1")
+ check("/sbin/ping -c 2 198.51.100.2")
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_tcp(self):
+ self.common_setup()
+
+ # Import in the correct vnet, so at to not confuse Scapy
+ import scapy.all as sp
+
+ # Send a TCP SYN, expect a RST
+ pkt = sp.IP(src="192.0.2.2", dst="198.51.100.2") \
+ / sp.TCP(sport=4321, dport=1234, flags="S")
+ print(pkt)
+ reply = sp.sr1(pkt, timeout=3, verbose=False)
+ print(reply)
+
+ ip = reply.getlayer(sp.IP)
+ tcp = reply.getlayer(sp.TCP)
+ assert ip
+ assert ip.src == "198.51.100.2"
+ assert ip.dst == "192.0.2.2"
+ assert tcp
+ assert tcp.sport == 1234
+ assert tcp.dport == 4321
+ assert "R" in tcp.flags
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_udp(self):
+ self.common_setup()
+
+ # Import in the correct vnet, so at to not confuse Scapy
+ import scapy.all as sp
+
+ # Send a UDP packet, expect ICMP error
+ pkt = sp.IP(dst="198.51.100.2") \
+ / sp.UDP(sport=4321, dport=1234)
+ print(pkt)
+ reply = sp.sr1(pkt, timeout=3, verbose=False)
+ print(reply)
+ ip = reply.getlayer(sp.IP)
+ icmp = reply.getlayer(sp.ICMP)
+ udp = reply.getlayer(sp.UDPerror)
+
+ assert ip
+ assert ip.src == "192.0.2.1"
+ assert ip.dst == "192.0.2.2"
+ assert icmp
+ assert icmp.type == 3
+ assert icmp.code == 3
+ assert udp
+ assert udp.sport == 4321
+ assert udp.dport == 1234
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_sctp(self):
+ self.common_setup()
+
+ # Import in the correct vnet, so at to not confuse Scapy
+ import scapy.all as sp
+
+ # Send an SCTP init, expect an SCTP abort
+ pkt = sp.IP(dst="198.51.100.2") \
+ / sp.SCTP(sport=1111, dport=2222) \
+ / sp.SCTPChunkInit(init_tag=1, n_in_streams=1, n_out_streams=1, a_rwnd=1500)
+ print(pkt)
+ reply = sp.sr1(pkt, timeout=3, verbose=False)
+ print(reply)
+ ip = reply.getlayer(sp.IP)
+ sctp = reply.getlayer(sp.SCTP)
+ abort = reply.getlayer(sp.SCTPChunkAbort)
+ print(sctp)
+
+ assert ip
+ assert ip.src == "198.51.100.2"
+ assert ip.dst == "192.0.2.2"
+ assert sctp
+ assert sctp.sport == 2222
+ assert sctp.dport == 1111
+ assert(abort)
diff --git a/tests/sys/netpfil/pf/ridentifier.sh b/tests/sys/netpfil/pf/ridentifier.sh
new file mode 100644
index 000000000000..8d83bcfb8213
--- /dev/null
+++ b/tests/sys/netpfil/pf/ridentifier.sh
@@ -0,0 +1,105 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2021 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "basic" "cleanup"
+basic_head()
+{
+ atf_set descr 'Test ridentifier keyword'
+ atf_set require.user root
+}
+
+basic_body()
+{
+ pft_init
+ pflog_init
+
+ epair=$(vnet_mkepair)
+
+ ifconfig ${epair}a 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig lo0 up
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+ jexec alcatraz /usr/sbin/inetd -p ${PWD}/inetd-alcatraz.pid $(atf_get_srcdir)/echo_inetd.conf
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ jexec alcatraz pfctl -e
+ jexec alcatraz ifconfig pflog0 up
+ pft_set_rules alcatraz \
+ "pass in log" \
+ "pass in log proto tcp ridentifier 1234"
+
+ jexec alcatraz tcpdump --immediate-mode -n -e -i pflog0 > ${PWD}/tcpdump.log &
+ sleep 1
+
+ echo "test" | nc -N 192.0.2.2 7
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ sleep 1
+ jexec alcatraz killall tcpdump
+
+ # Make sure we spotted the ridentifier
+ atf_check -s exit:0 -o ignore \
+ grep 'rule 1/0.*ridentifier 1234' ${PWD}/tcpdump.log
+ # But not on the !TCP traffic
+ atf_check -s exit:1 -o ignore \
+ grep 'rule 0/0.*ridentifier' ${PWD}/tcpdump.log
+
+ # Now try with antispoof rules
+ pft_set_rules alcatraz \
+ "pass in log" \
+ "antispoof log for ${epair}b ridentifier 4321"
+
+ jexec alcatraz tcpdump --immediate-mode -n -e -i pflog0 > ${PWD}/tcpdump.log &
+ sleep 1
+
+ # Without explicit rules for lo0 we're going to drop packets to ourself
+ atf_check -s exit:2 -o ignore -e ignore \
+ jexec alcatraz ping -c 1 -t 1 192.0.2.2
+
+ sleep 1
+ jexec alcatraz killall tcpdump
+
+ cat ${PWD}/tcpdump.log
+
+ # Make sure we spotted the ridentifier
+ atf_check -s exit:0 -o ignore \
+ grep 'rule 2/0.*ridentifier 4321' ${PWD}/tcpdump.log
+}
+
+basic_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "basic"
+}
diff --git a/tests/sys/netpfil/pf/route_to.sh b/tests/sys/netpfil/pf/route_to.sh
new file mode 100644
index 000000000000..fd1653cce311
--- /dev/null
+++ b/tests/sys/netpfil/pf/route_to.sh
@@ -0,0 +1,997 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2018 Kristof Provost <kp@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+common_dir=$(atf_get_srcdir)/../common
+
+atf_test_case "v4" "cleanup"
+v4_head()
+{
+ atf_set descr 'Basic route-to test'
+ atf_set require.user root
+}
+
+v4_body()
+{
+ pft_init
+
+ epair_send=$(vnet_mkepair)
+ ifconfig ${epair_send}a 192.0.2.1/24 up
+ epair_route=$(vnet_mkepair)
+ ifconfig ${epair_route}a 203.0.113.1/24 up
+
+ vnet_mkjail alcatraz ${epair_send}b ${epair_route}b
+ jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up
+ jexec alcatraz ifconfig ${epair_route}b 203.0.113.2/24 up
+ jexec alcatraz route add -net 198.51.100.0/24 192.0.2.1
+ jexec alcatraz pfctl -e
+
+ # Attempt to provoke PR 228782
+ pft_set_rules alcatraz "block all" "pass user 2" \
+ "pass out route-to (${epair_route}b 203.0.113.1) from 192.0.2.2 to 198.51.100.1 no state"
+ jexec alcatraz nc -w 3 -s 192.0.2.2 198.51.100.1 22
+
+ # atf wants us to not return an error, but our netcat will fail
+ true
+}
+
+v4_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "v6" "cleanup"
+v6_head()
+{
+ atf_set descr 'Basic route-to test (IPv6)'
+ atf_set require.user root
+}
+
+v6_body()
+{
+ pft_init
+
+ epair_send=$(vnet_mkepair)
+ ifconfig ${epair_send}a inet6 2001:db8:42::1/64 up no_dad -ifdisabled
+ epair_route=$(vnet_mkepair)
+ ifconfig ${epair_route}a inet6 2001:db8:43::1/64 up no_dad -ifdisabled
+
+ vnet_mkjail alcatraz ${epair_send}b ${epair_route}b
+ jexec alcatraz ifconfig ${epair_send}b inet6 2001:db8:42::2/64 up no_dad
+ jexec alcatraz ifconfig ${epair_route}b inet6 2001:db8:43::2/64 up no_dad
+ jexec alcatraz route add -6 2001:db8:666::/64 2001:db8:42::2
+ jexec alcatraz pfctl -e
+
+ # Attempt to provoke PR 228782
+ pft_set_rules alcatraz "block all" "pass user 2" \
+ "pass out route-to (${epair_route}b 2001:db8:43::1) from 2001:db8:42::2 to 2001:db8:666::1 no state"
+ jexec alcatraz nc -6 -w 3 -s 2001:db8:42::2 2001:db8:666::1 22
+
+ # atf wants us to not return an error, but our netcat will fail
+ true
+}
+
+v6_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "multiwan" "cleanup"
+multiwan_head()
+{
+ atf_set descr 'Multi-WAN redirection / reply-to test'
+ atf_set require.user root
+}
+
+multiwan_body()
+{
+ pft_init
+
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+ epair_cl_one=$(vnet_mkepair)
+ epair_cl_two=$(vnet_mkepair)
+
+ vnet_mkjail srv ${epair_one}b ${epair_two}b
+ vnet_mkjail wan_one ${epair_one}a ${epair_cl_one}b
+ vnet_mkjail wan_two ${epair_two}a ${epair_cl_two}b
+ vnet_mkjail client ${epair_cl_one}a ${epair_cl_two}a
+
+ jexec client ifconfig ${epair_cl_one}a 203.0.113.1/25
+ jexec wan_one ifconfig ${epair_cl_one}b 203.0.113.2/25
+ jexec wan_one ifconfig ${epair_one}a 192.0.2.1/24 up
+ jexec wan_one sysctl net.inet.ip.forwarding=1
+ jexec srv ifconfig ${epair_one}b 192.0.2.2/24 up
+ jexec client route add 192.0.2.0/24 203.0.113.2
+
+ jexec client ifconfig ${epair_cl_two}a 203.0.113.128/25
+ jexec wan_two ifconfig ${epair_cl_two}b 203.0.113.129/25
+ jexec wan_two ifconfig ${epair_two}a 198.51.100.1/24 up
+ jexec wan_two sysctl net.inet.ip.forwarding=1
+ jexec srv ifconfig ${epair_two}b 198.51.100.2/24 up
+ jexec client route add 198.51.100.0/24 203.0.113.129
+
+ jexec srv ifconfig lo0 127.0.0.1/8 up
+ jexec srv route add default 192.0.2.1
+ jexec srv sysctl net.inet.ip.forwarding=1
+
+ # Run echo server in srv jail
+ jexec srv /usr/sbin/inetd -p ${PWD}/multiwan.pid $(atf_get_srcdir)/echo_inetd.conf
+
+ jexec srv pfctl -e
+ pft_set_rules srv \
+ "nat on ${epair_one}b inet from 127.0.0.0/8 to any -> (${epair_one}b)" \
+ "nat on ${epair_two}b inet from 127.0.0.0/8 to any -> (${epair_two}b)" \
+ "rdr on ${epair_one}b inet proto tcp from any to 192.0.2.2 port 7 -> 127.0.0.1 port 7" \
+ "rdr on ${epair_two}b inet proto tcp from any to 198.51.100.2 port 7 -> 127.0.0.1 port 7" \
+ "block in" \
+ "block out" \
+ "pass in quick on ${epair_one}b reply-to (${epair_one}b 192.0.2.1) inet proto tcp from any to 127.0.0.1 port 7" \
+ "pass in quick on ${epair_two}b reply-to (${epair_two}b 198.51.100.1) inet proto tcp from any to 127.0.0.1 port 7"
+
+ # These will always succeed, because we don't change interface to route
+ # correctly here.
+ result=$(echo "one" | jexec wan_one nc -N -w 3 192.0.2.2 7)
+ if [ "${result}" != "one" ]; then
+ atf_fail "Redirect on one failed"
+ fi
+ result=$(echo "two" | jexec wan_two nc -N -w 3 198.51.100.2 7)
+ if [ "${result}" != "two" ]; then
+ atf_fail "Redirect on two failed"
+ fi
+
+ result=$(echo "one" | jexec client nc -N -w 3 192.0.2.2 7)
+ if [ "${result}" != "one" ]; then
+ atf_fail "Redirect from client on one failed"
+ fi
+
+ # This should trigger the issue fixed in 829a69db855b48ff7e8242b95e193a0783c489d9
+ result=$(echo "two" | jexec client nc -N -w 3 198.51.100.2 7)
+ if [ "${result}" != "two" ]; then
+ atf_fail "Redirect from client on two failed"
+ fi
+}
+
+multiwan_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "multiwanlocal" "cleanup"
+multiwanlocal_head()
+{
+ atf_set descr 'Multi-WAN local origin source-based redirection / route-to test'
+ atf_set require.user root
+}
+
+multiwanlocal_body()
+{
+ pft_init
+
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+ epair_cl_one=$(vnet_mkepair)
+ epair_cl_two=$(vnet_mkepair)
+
+ vnet_mkjail srv1 ${epair_one}b
+ vnet_mkjail srv2 ${epair_two}b
+ vnet_mkjail wan_one ${epair_one}a ${epair_cl_one}b
+ vnet_mkjail wan_two ${epair_two}a ${epair_cl_two}b
+ vnet_mkjail client ${epair_cl_one}a ${epair_cl_two}a
+
+ jexec client ifconfig ${epair_cl_one}a 203.0.113.1/25
+ jexec wan_one ifconfig ${epair_cl_one}b 203.0.113.2/25
+ jexec wan_one ifconfig ${epair_one}a 192.0.2.1/24 up
+ jexec wan_one sysctl net.inet.ip.forwarding=1
+ jexec srv1 ifconfig ${epair_one}b 192.0.2.2/24 up
+
+ jexec client ifconfig ${epair_cl_two}a 203.0.113.128/25
+ jexec wan_two ifconfig ${epair_cl_two}b 203.0.113.129/25
+ jexec wan_two ifconfig ${epair_two}a 198.51.100.1/24 up
+ jexec wan_two sysctl net.inet.ip.forwarding=1
+ jexec srv2 ifconfig ${epair_two}b 198.51.100.2/24 up
+
+ jexec client route add default 203.0.113.2
+ jexec srv1 route add default 192.0.2.1
+ jexec srv2 route add default 198.51.100.1
+
+ # Run data source in srv1 and srv2
+ jexec srv1 sh -c 'dd if=/dev/zero bs=1024 count=100 | nc -l 7 -w 2 -N &'
+ jexec srv2 sh -c 'dd if=/dev/zero bs=1024 count=100 | nc -l 7 -w 2 -N &'
+
+ jexec client pfctl -e
+ pft_set_rules client \
+ "block in" \
+ "block out" \
+ "pass out quick route-to (${epair_cl_two}a 203.0.113.129) inet proto tcp from 203.0.113.128 to any port 7" \
+ "pass out on ${epair_cl_one}a inet proto tcp from any to any port 7" \
+ "set skip on lo"
+
+ # This should work
+ result=$(jexec client nc -N -w 1 192.0.2.2 7 | wc -c)
+ if [ ${result} -ne 102400 ]; then
+ jexec client pfctl -ss
+ atf_fail "Redirect from client on one failed: ${result}"
+ fi
+
+ # This should trigger the issue
+ result=$(jexec client nc -N -w 1 -s 203.0.113.128 198.51.100.2 7 | wc -c)
+ jexec client pfctl -ss
+ if [ ${result} -ne 102400 ]; then
+ atf_fail "Redirect from client on two failed: ${result}"
+ fi
+}
+
+multiwanlocal_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "icmp_nat" "cleanup"
+icmp_nat_head()
+{
+ atf_set descr 'Test that ICMP packets are correct for route-to + NAT'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+icmp_nat_body()
+{
+ pft_init
+
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+ epair_three=$(vnet_mkepair)
+
+ vnet_mkjail gw ${epair_one}b ${epair_two}a ${epair_three}a
+ vnet_mkjail srv ${epair_two}b
+ vnet_mkjail srv2 ${epair_three}b
+
+ ifconfig ${epair_one}a 192.0.2.2/24 up
+ route add -net 198.51.100.0/24 192.0.2.1
+ jexec gw sysctl net.inet.ip.forwarding=1
+ jexec gw ifconfig ${epair_one}b 192.0.2.1/24 up
+ jexec gw ifconfig ${epair_two}a 198.51.100.1/24 up
+ jexec gw ifconfig ${epair_three}a 203.0.113.1/24 up mtu 500
+ jexec srv ifconfig ${epair_two}b 198.51.100.2/24 up
+ jexec srv route add default 198.51.100.1
+ jexec srv2 ifconfig ${epair_three}b 203.0.113.2/24 up mtu 500
+ jexec srv2 route add default 203.0.113.1
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 198.51.100.2
+
+ jexec gw pfctl -e
+ pft_set_rules gw \
+ "nat on ${epair_two}a inet from 192.0.2.0/24 to any -> (${epair_two}a)" \
+ "nat on ${epair_three}a inet from 192.0.2.0/24 to any -> (${epair_three}a)" \
+ "pass out route-to (${epair_three}a 203.0.113.2) proto icmp icmp-type echoreq"
+
+ # Now ensure that we get an ICMP error with the correct IP addresses in it.
+ atf_check -s exit:0 ${common_dir}/pft_icmp_check.py \
+ --to 198.51.100.2 \
+ --fromaddr 192.0.2.2 \
+ --recvif ${epair_one}a \
+ --sendif ${epair_one}a
+
+ # ping reports the ICMP error, so check of that too.
+ atf_check -s exit:2 -o match:'frag needed and DF set' \
+ ping -D -c 1 -s 1000 198.51.100.2
+}
+
+icmp_nat_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "dummynet" "cleanup"
+dummynet_head()
+{
+ atf_set descr 'Test that dummynet applies to route-to packets'
+ atf_set require.user root
+}
+
+dummynet_body()
+{
+ dummynet_init
+
+ epair_srv=$(vnet_mkepair)
+ epair_gw=$(vnet_mkepair)
+
+ vnet_mkjail srv ${epair_srv}a
+ jexec srv ifconfig ${epair_srv}a 192.0.2.1/24 up
+ jexec srv route add default 192.0.2.2
+
+ vnet_mkjail gw ${epair_srv}b ${epair_gw}a
+ jexec gw ifconfig ${epair_srv}b 192.0.2.2/24 up
+ jexec gw ifconfig ${epair_gw}a 198.51.100.1/24 up
+ jexec gw sysctl net.inet.ip.forwarding=1
+
+ ifconfig ${epair_gw}b 198.51.100.2/24 up
+ route add -net 192.0.2.0/24 198.51.100.1
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.1
+
+ jexec gw dnctl pipe 1 config delay 1200
+ pft_set_rules gw \
+ "pass out route-to (${epair_srv}b 192.0.2.1) to 192.0.2.1 dnpipe 1"
+ jexec gw pfctl -e
+
+ # The ping request will pass, but take 1.2 seconds
+ # So this works:
+ atf_check -s exit:0 -o ignore ping -c 1 -t 2 192.0.2.1
+ # But this times out:
+ atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.1
+
+ # return path dummynet
+ pft_set_rules gw \
+ "pass out route-to (${epair_srv}b 192.0.2.1) to 192.0.2.1 dnpipe (0, 1)"
+
+ # The ping request will pass, but take 1.2 seconds
+ # So this works:
+ atf_check -s exit:0 -o ignore ping -c 1 -t 2 192.0.2.1
+ # But this times out:
+ atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.1
+}
+
+dummynet_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "dummynet_in" "cleanup"
+dummynet_in_head()
+{
+ atf_set descr 'Thest that dummynet works as expected on pass in route-to packets'
+ atf_set require.user root
+}
+
+dummynet_in_body()
+{
+ dummynet_init
+
+ epair_srv=$(vnet_mkepair)
+ epair_gw=$(vnet_mkepair)
+
+ vnet_mkjail srv ${epair_srv}a
+ jexec srv ifconfig ${epair_srv}a 192.0.2.1/24 up
+ jexec srv route add default 192.0.2.2
+
+ vnet_mkjail gw ${epair_srv}b ${epair_gw}a
+ jexec gw ifconfig ${epair_srv}b 192.0.2.2/24 up
+ jexec gw ifconfig ${epair_gw}a 198.51.100.1/24 up
+ jexec gw sysctl net.inet.ip.forwarding=1
+
+ ifconfig ${epair_gw}b 198.51.100.2/24 up
+ route add -net 192.0.2.0/24 198.51.100.1
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.1
+
+ jexec gw dnctl pipe 1 config delay 1200
+ pft_set_rules gw \
+ "pass in route-to (${epair_srv}b 192.0.2.1) to 192.0.2.1 dnpipe 1"
+ jexec gw pfctl -e
+
+ # The ping request will pass, but take 1.2 seconds
+ # So this works:
+ echo "Expect 1.2 s"
+ ping -c 1 192.0.2.1
+ atf_check -s exit:0 -o ignore ping -c 1 -t 2 192.0.2.1
+ # But this times out:
+ atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.1
+
+ # return path dummynet
+ pft_set_rules gw \
+ "pass in route-to (${epair_srv}b 192.0.2.1) to 192.0.2.1 dnpipe (0, 1)"
+
+ # The ping request will pass, but take 1.2 seconds
+ # So this works:
+ echo "Expect 1.2 s"
+ ping -c 1 192.0.2.1
+ atf_check -s exit:0 -o ignore ping -c 1 -t 2 192.0.2.1
+ # But this times out:
+ atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.1
+}
+
+dummynet_in_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "ifbound" "cleanup"
+ifbound_head()
+{
+ atf_set descr 'Test that route-to states bind the expected interface'
+ atf_set require.user root
+}
+
+ifbound_body()
+{
+ pft_init
+
+ j="route_to:ifbound"
+
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+ ifconfig ${epair_one}b up
+
+ vnet_mkjail ${j}2 ${epair_two}b
+ jexec ${j}2 ifconfig ${epair_two}b inet 198.51.100.2/24 up
+ jexec ${j}2 ifconfig ${epair_two}b inet alias 203.0.113.1/24
+ jexec ${j}2 route add default 198.51.100.1
+
+ vnet_mkjail $j ${epair_one}a ${epair_two}a
+ jexec $j ifconfig ${epair_one}a 192.0.2.1/24 up
+ jexec $j ifconfig ${epair_two}a 198.51.100.1/24 up
+ jexec $j route add default 192.0.2.2
+
+ jexec $j pfctl -e
+ pft_set_rules $j \
+ "set state-policy if-bound" \
+ "block" \
+ "pass out route-to (${epair_two}a 198.51.100.2)"
+
+ atf_check -s exit:0 -o ignore \
+ jexec $j ping -c 3 203.0.113.1
+}
+
+ifbound_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "ifbound_v6" "cleanup"
+ifbound_v6_head()
+{
+ atf_set descr 'Test that route-to states for IPv6 bind the expected interface'
+ atf_set require.user root
+}
+
+ifbound_v6_body()
+{
+ pft_init
+
+ j="route_to:ifbound_v6"
+
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+ ifconfig ${epair_one}b up
+
+ vnet_mkjail ${j}2 ${epair_two}b
+ jexec ${j}2 ifconfig ${epair_two}b inet6 2001:db8:1::2/64 up no_dad
+ jexec ${j}2 ifconfig ${epair_two}b inet6 alias 2001:db8:2::1/64 no_dad
+ jexec ${j}2 route -6 add default 2001:db8:1::1
+
+ vnet_mkjail $j ${epair_one}a ${epair_two}a
+ jexec $j ifconfig ${epair_one}a inet6 2001:db8::1/64 up no_dad
+ jexec $j ifconfig ${epair_two}a inet6 2001:db8:1::1/64 up no_dad
+ jexec $j route -6 add default 2001:db8::2
+
+ jexec $j ping6 -c 3 2001:db8:1::2
+
+ jexec $j pfctl -e
+ pft_set_rules $j \
+ "set state-policy if-bound" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass out route-to (${epair_two}a 2001:db8:1::2)"
+
+ atf_check -s exit:0 -o ignore \
+ jexec $j ping6 -c 3 2001:db8:2::1
+}
+
+ifbound_v6_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "ifbound_reply_to" "cleanup"
+ifbound_reply_to_head()
+{
+ atf_set descr 'Test that reply-to states bind to the expected interface'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+ifbound_reply_to_body()
+{
+ pft_init
+
+ j="route_to:ifbound_reply_to"
+
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+ ifconfig ${epair_one}b inet 192.0.2.2/24 up
+ ifconfig ${epair_two}b up
+
+ vnet_mkjail $j ${epair_one}a ${epair_two}a
+ jexec $j ifconfig ${epair_one}a 192.0.2.1/24 up
+ jexec $j ifconfig ${epair_two}a 198.51.100.1/24 up
+ jexec $j route add default 198.51.100.254
+
+ jexec $j pfctl -e
+ pft_set_rules $j \
+ "set state-policy if-bound" \
+ "block" \
+ "pass in on ${epair_one}a reply-to (${epair_one}a 192.0.2.2) inet from any to 192.0.2.0/24 keep state"
+
+ atf_check -s exit:0 -o ignore \
+ ping -c 3 192.0.2.1
+
+ atf_check -s exit:0 \
+ ${common_dir}/pft_ping.py \
+ --to 192.0.2.1 \
+ --from 203.0.113.2 \
+ --sendif ${epair_one}b \
+ --replyif ${epair_one}b
+
+ # pft_ping uses the same ID every time, so this will look like more traffic in the same state
+ atf_check -s exit:0 \
+ ${common_dir}/pft_ping.py \
+ --to 192.0.2.1 \
+ --from 203.0.113.2 \
+ --sendif ${epair_one}b \
+ --replyif ${epair_one}b
+
+ jexec $j pfctl -ss -vv
+}
+
+ifbound_reply_to_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "ifbound_reply_to_v6" "cleanup"
+ifbound_reply_to_v6_head()
+{
+ atf_set descr 'Test that reply-to states bind to the expected interface for IPv6'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+ifbound_reply_to_v6_body()
+{
+ pft_init
+
+ j="route_to:ifbound_reply_to_v6"
+
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+
+ vnet_mkjail ${j}s ${epair_one}b ${epair_two}b
+ jexec ${j}s ifconfig ${epair_one}b inet6 2001:db8::2/64 up no_dad
+ jexec ${j}s ifconfig ${epair_two}b up
+ #jexec ${j}s route -6 add default 2001:db8::1
+
+ vnet_mkjail $j ${epair_one}a ${epair_two}a
+ jexec $j ifconfig ${epair_one}a inet6 2001:db8::1/64 up no_dad
+ jexec $j ifconfig ${epair_two}a inet6 2001:db8:1::1/64 up no_dad
+ jexec $j route -6 add default 2001:db8:1::254
+
+ jexec $j pfctl -e
+ pft_set_rules $j \
+ "set state-policy if-bound" \
+ "block" \
+ "pass quick inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in on ${epair_one}a reply-to (${epair_one}a 2001:db8::2) inet6 from any to 2001:db8::/64 keep state"
+
+ atf_check -s exit:0 -o ignore \
+ jexec ${j}s ping6 -c 3 2001:db8::1
+
+ atf_check -s exit:0 \
+ jexec ${j}s ${common_dir}/pft_ping.py \
+ --to 2001:db8::1 \
+ --from 2001:db8:2::2 \
+ --sendif ${epair_one}b \
+ --replyif ${epair_one}b
+
+ # pft_ping uses the same ID every time, so this will look like more traffic in the same state
+ atf_check -s exit:0 \
+ jexec ${j}s ${common_dir}/pft_ping.py \
+ --to 2001:db8::1 \
+ --from 2001:db8:2::2 \
+ --sendif ${epair_one}b \
+ --replyif ${epair_one}b
+
+ jexec $j pfctl -ss -vv
+}
+
+ifbound_reply_to_v6_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "ifbound_reply_to_rdr_dummynet" "cleanup"
+ifbound_reply_to_rdr_dummynet_head()
+{
+ atf_set descr 'Test that reply-to states bind to the expected non-default-route interface after rdr and dummynet'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+ifbound_reply_to_rdr_dummynet_body()
+{
+ dummynet_init
+
+ j="route_to:ifbound_reply_to_rdr_dummynet"
+
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+ ifconfig ${epair_one}b inet 192.0.2.2/24 up
+ ifconfig ${epair_two}b up
+
+ vnet_mkjail $j ${epair_one}a ${epair_two}a
+ jexec $j ifconfig lo0 inet 127.0.0.1/8 up
+ jexec $j ifconfig ${epair_one}a 192.0.2.1/24 up
+ jexec $j ifconfig ${epair_two}a 198.51.100.1/24 up
+ jexec $j route add default 198.51.100.254
+
+ jexec $j pfctl -e
+ jexec $j dnctl pipe 1 config delay 1
+ pft_set_rules $j \
+ "set state-policy if-bound" \
+ "rdr on ${epair_one}a proto icmp from any to 192.0.2.1 -> 127.0.0.1" \
+ "rdr on ${epair_two}a proto icmp from any to 198.51.100.1 -> 127.0.0.1" \
+ "match in on ${epair_one}a inet all dnpipe (1, 1)" \
+ "pass in on ${epair_one}a reply-to (${epair_one}a 192.0.2.2) inet from any to 127.0.0.1 keep state"
+
+ atf_check -s exit:0 -o ignore \
+ ping -c 3 192.0.2.1
+
+ atf_check -s exit:0 \
+ ${common_dir}/pft_ping.py \
+ --to 192.0.2.1 \
+ --from 203.0.113.2 \
+ --sendif ${epair_one}b \
+ --replyif ${epair_one}b
+
+ # pft_ping uses the same ID every time, so this will look like more traffic in the same state
+ atf_check -s exit:0 \
+ ${common_dir}/pft_ping.py \
+ --to 192.0.2.1 \
+ --from 203.0.113.2 \
+ --sendif ${epair_one}b \
+ --replyif ${epair_one}b
+
+ jexec $j pfctl -sr -vv
+ jexec $j pfctl -ss -vv
+}
+
+ifbound_reply_to_rdr_dummynet_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "dummynet_frag" "cleanup"
+dummynet_frag_head()
+{
+ atf_set descr 'Test fragmentation with route-to and dummynet'
+ atf_set require.user root
+}
+
+dummynet_frag_body()
+{
+ pft_init
+ dummynet_init
+
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+
+ ifconfig ${epair_one}a 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair_one}b ${epair_two}a
+ jexec alcatraz ifconfig ${epair_one}b 192.0.2.2/24 up
+ jexec alcatraz ifconfig ${epair_two}a 198.51.100.1/24 up
+ jexec alcatraz sysctl net.inet.ip.forwarding=1
+
+ vnet_mkjail singsing ${epair_two}b
+ jexec singsing ifconfig ${epair_two}b 198.51.100.2/24 up
+ jexec singsing route add default 198.51.100.1
+
+ route add 198.51.100.0/24 192.0.2.2
+
+ jexec alcatraz dnctl pipe 1 config bw 1000Byte/s burst 4500
+ jexec alcatraz dnctl pipe 2 config
+ # This second pipe ensures that the pf_test(PF_OUT) call in pf_route() doesn't
+ # delay packets in dummynet (by inheriting pipe 1 from the input rule).
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "set reassemble yes" \
+ "pass in route-to (${epair_two}a 198.51.100.2) inet proto icmp all icmp-type echoreq dnpipe 1" \
+ "pass out dnpipe 2"
+
+
+ atf_check -s exit:0 -o ignore ping -c 1 198.51.100.2
+ atf_check -s exit:0 -o ignore ping -c 1 -s 4000 198.51.100.2
+}
+
+dummynet_frag_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "dummynet_double" "cleanup"
+dummynet_double_head()
+{
+ atf_set descr 'Ensure dummynet is not applied multiple times'
+ atf_set require.user root
+}
+
+dummynet_double_body()
+{
+ pft_init
+ dummynet_init
+
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+
+ ifconfig ${epair_one}a 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair_one}b ${epair_two}a
+ jexec alcatraz ifconfig ${epair_one}b 192.0.2.2/24 up
+ jexec alcatraz ifconfig ${epair_two}a 198.51.100.1/24 up
+ jexec alcatraz sysctl net.inet.ip.forwarding=1
+
+ vnet_mkjail singsing ${epair_two}b
+ jexec singsing ifconfig ${epair_two}b 198.51.100.2/24 up
+ jexec singsing route add default 198.51.100.1
+
+ route add 198.51.100.0/24 192.0.2.2
+
+ jexec alcatraz dnctl pipe 1 config delay 800
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "set reassemble yes" \
+ "nat on ${epair_two}a from 192.0.2.0/24 -> (${epair_two}a)" \
+ "pass in route-to (${epair_two}a 198.51.100.2) inet proto icmp all icmp-type echoreq dnpipe (1, 1)" \
+ "pass out route-to (${epair_two}a 198.51.100.2) inet proto icmp all icmp-type echoreq"
+
+ ping -c 1 198.51.100.2
+ jexec alcatraz pfctl -sr -vv
+ jexec alcatraz pfctl -ss -vv
+
+ # We expect to be delayed 1.6 seconds, so timeout of two seconds passes, but
+ # timeout of 1 does not.
+ atf_check -s exit:0 -o ignore ping -t 2 -c 1 198.51.100.2
+ atf_check -s exit:2 -o ignore ping -t 1 -c 1 198.51.100.2
+}
+
+dummynet_double_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "sticky" "cleanup"
+sticky_head()
+{
+ atf_set descr 'Set and retrieve a rule with sticky-address'
+ atf_set require.user root
+}
+
+sticky_body()
+{
+ pft_init
+
+ vnet_mkjail alcatraz
+
+ pft_set_rules alcatraz \
+ "pass in quick log on n_test_h_rtr route-to (n_srv_h_rtr <change_dst>) sticky-address from any to <dst> keep state"
+
+ jexec alcatraz pfctl -qvvsr
+}
+
+sticky_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "ttl" "cleanup"
+ttl_head()
+{
+ atf_set descr 'Ensure we decrement TTL on route-to'
+ atf_set require.user root
+}
+
+ttl_body()
+{
+ pft_init
+
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+ ifconfig ${epair_one}b 192.0.2.2/24 up
+ route add default 192.0.2.1
+
+ vnet_mkjail alcatraz ${epair_one}a ${epair_two}a
+ jexec alcatraz ifconfig ${epair_one}a 192.0.2.1/24 up
+ jexec alcatraz ifconfig ${epair_two}a 198.51.100.1/24 up
+ jexec alcatraz sysctl net.inet.ip.forwarding=1
+
+ vnet_mkjail singsing ${epair_two}b
+ jexec singsing ifconfig ${epair_two}b 198.51.100.2/24 up
+ jexec singsing route add default 198.51.100.1
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ ping -c 3 198.51.100.2
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "pass out" \
+ "pass in route-to (${epair_two}a 198.51.100.2)"
+
+ atf_check -s exit:0 -o ignore \
+ ping -c 3 198.51.100.2
+
+ atf_check -s exit:2 -o ignore \
+ ping -m 1 -c 3 198.51.100.2
+}
+
+ttl_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_test_case "empty_pool" "cleanup"
+empty_pool_head()
+{
+ atf_set descr 'Route-to with empty pool'
+ atf_set require.user root
+}
+
+empty_pool_body()
+{
+ pft_init
+ setup_router_server_ipv6
+
+
+ pft_set_rules router \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in on ${epair_tester}b route-to (${epair_server}a <nonexistent>) inet6 from any to ${net_server_host_server}" \
+ "pass out on ${epair_server}a"
+
+ # pf_map_addr_sn() won't be able to pick a target address, because
+ # the table used in redireciton pool is empty. Packet will not be
+ # forwarded, error counter will be increased.
+ ping_server_check_reply exit:1
+ # Ignore warnings about not-loaded ALTQ
+ atf_check -o "match:map-failed +1 +" -x "jexec router pfctl -qvvsi 2> /dev/null"
+}
+
+empty_pool_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_test_case "table_loop" "cleanup"
+
+table_loop_head()
+{
+ atf_set descr 'Check that iterating over tables poperly loops'
+ atf_set require.user root
+}
+
+table_loop_body()
+{
+ setup_router_server_nat64
+
+ # Clients will connect from another network behind the router.
+ # This allows for using multiple source addresses.
+ jexec router route add -6 ${net_clients_6}::/${net_clients_6_mask} ${net_tester_6_host_tester}
+ jexec router route add ${net_clients_4}.0/${net_clients_4_mask} ${net_tester_4_host_tester}
+
+ # The servers are reachable over additional IP addresses for
+ # testing of tables and subnets. The addresses are noncontinougnus
+ # for pf_map_addr() counter tests.
+ for i in 0 1 4 5; do
+ a1=$((24 + i))
+ jexec server1 ifconfig ${epair_server1}b inet ${net_server1_4}.${a1}/32 alias
+ jexec server1 ifconfig ${epair_server1}b inet6 ${net_server1_6}::42:${i}/128 alias
+ a2=$((40 + i))
+ jexec server2 ifconfig ${epair_server2}b inet ${net_server2_4}.${a2}/32 alias
+ jexec server2 ifconfig ${epair_server2}b inet6 ${net_server2_6}::42:${i}/128 alias
+ done
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set debug loud" \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "table <rt_targets_1> { ${net_server1_6}::42:4/127 ${net_server1_6}::42:0/127 }" \
+ "table <rt_targets_2> { ${net_server2_6}::42:4/127 }" \
+ "pass in on ${epair_tester}b \
+ route-to { \
+ (${epair_server1}a <rt_targets_1>) \
+ (${epair_server2}a <rt_targets_2_empty>) \
+ (${epair_server2}a <rt_targets_2>) \
+ } \
+ inet6 proto tcp \
+ keep state"
+
+ # Both hosts of the pool are tables. Each table gets iterated over once,
+ # then the pool iterates to the next host, which is also iterated,
+ # then the pool loops back to the 1st host. If an empty table is found,
+ # it is skipped. Unless that's the only table, that is tested by
+ # the "empty_pool" test.
+ for port in $(seq 1 7); do
+ port=$((4200 + port))
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a --replyif ${epair_tester}a \
+ --fromaddr ${net_clients_6}::1 --to ${host_server_6} \
+ --ping-type=tcp3way --send-sport=${port}
+ done
+
+ states=$(mktemp) || exit 1
+ jexec router pfctl -qvvss | normalize_pfctl_s > $states
+ cat $states
+
+ for state_regexp in \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4201\] .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4202\] .* route-to: ${net_server1_6}::42:1@${epair_server1}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4203\] .* route-to: ${net_server1_6}::42:4@${epair_server1}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4204\] .* route-to: ${net_server1_6}::42:5@${epair_server1}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4205\] .* route-to: ${net_server2_6}::42:4@${epair_server2}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4206\] .* route-to: ${net_server2_6}::42:5@${epair_server2}a" \
+ "${epair_tester}b tcp ${host_server_6}\[9\] <- ${net_clients_6}::1\[4207\] .* route-to: ${net_server1_6}::42:0@${epair_server1}a" \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+table_loop_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_init_test_cases()
+{
+ atf_add_test_case "v4"
+ atf_add_test_case "v6"
+ atf_add_test_case "multiwan"
+ atf_add_test_case "multiwanlocal"
+ atf_add_test_case "icmp_nat"
+ atf_add_test_case "dummynet"
+ atf_add_test_case "dummynet_in"
+ atf_add_test_case "ifbound"
+ atf_add_test_case "ifbound_v6"
+ atf_add_test_case "ifbound_reply_to"
+ atf_add_test_case "ifbound_reply_to_v6"
+ atf_add_test_case "ifbound_reply_to_rdr_dummynet"
+ atf_add_test_case "dummynet_frag"
+ atf_add_test_case "dummynet_double"
+ atf_add_test_case "sticky"
+ atf_add_test_case "ttl"
+ atf_add_test_case "empty_pool"
+ atf_add_test_case "table_loop"
+}
diff --git a/tests/sys/netpfil/pf/rtable.sh b/tests/sys/netpfil/pf/rtable.sh
new file mode 100644
index 000000000000..bb2cada57049
--- /dev/null
+++ b/tests/sys/netpfil/pf/rtable.sh
@@ -0,0 +1,130 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2023 Kajetan Staszkiewicz <vegetga@tuxpowered.net>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "forward_v4" "cleanup"
+forward_v4_head()
+{
+ atf_set descr 'Test IPv4 forwarding with rtable'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+forward_v4_body()
+{
+ setup_router_server_ipv4
+
+ # Sanity check
+ ping_server_check_reply exit:0
+
+ jexec router sysctl net.fibs=2
+ jexec router ifconfig ${epair_server}a fib 1
+ jexec router route del -net ${net_server}
+ jexec router route add -fib 1 -net ${net_server} -iface ${epair_server}a
+
+ # Sanity check
+ ping_server_check_reply exit:1
+
+ # This rule is not enough.
+ # Echo requests will be properly forwarded but replies can't be routed back.
+ pft_set_rules router \
+ "pass in on ${epair_tester}b inet proto icmp all icmp-type echoreq rtable 1"
+ ping_server_check_reply exit:1
+
+ # Allow replies coming back to the tester properly via stateful filtering post-routing.
+ pft_set_rules router \
+ "pass in on ${epair_tester}b inet proto icmp all icmp-type echoreq rtable 1" \
+ "pass out on ${epair_server}a inet proto icmp all icmp-type echoreq rtable 0"
+ ping_server_check_reply exit:0
+
+ # Allow replies coming back to the tester properly via provding extra routes in rtable 1
+ pft_set_rules router \
+ "pass in on ${epair_tester}b inet proto icmp all icmp-type echoreq rtable 1"
+ jexec router route add -fib 1 -net ${net_tester} -iface ${epair_tester}b
+ ping_server_check_reply exit:0
+}
+
+forward_v4_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "forward_v6" "cleanup"
+forward_v6_head()
+{
+ atf_set descr 'Test IPv6 forwarding with rtable'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+forward_v6_body()
+{
+ setup_router_server_ipv6
+
+ # Sanity check
+ ping_server_check_reply exit:0
+
+ jexec router sysctl net.fibs=2
+ jexec router ifconfig ${epair_server}a fib 1
+ jexec router route del -6 ${net_server}
+ jexec router route add -fib 1 -6 ${net_server} -iface ${epair_server}a
+
+ # Sanity check
+ ping_server_check_reply exit:1
+
+ # This rule is not enough.
+ # Echo requests will be properly forwarded but replies can't be routed back.
+ pft_set_rules router \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in on ${epair_tester}b inet6 proto icmp6 icmp6-type echoreq"
+ ping_server_check_reply exit:1
+
+ # Allow replies coming back to the tester properly via stateful filtering post-routing.
+ pft_set_rules router \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in on ${epair_tester}b inet6 proto icmp6 icmp6-type echoreq rtable 1" \
+ "pass out on ${epair_server}a inet6 proto icmp6 icmp6-type echoreq rtable 0"
+ ping_server_check_reply exit:0
+
+ # Allow replies coming back to the tester properly via provding extra routes in rtable 1
+ pft_set_rules router \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in on ${epair_tester}b inet6 proto icmp6 icmp6-type echoreq rtable 1"
+ jexec router route add -fib 1 -6 ${net_tester} -iface ${epair_tester}b
+ ping_server_check_reply exit:0
+}
+
+forward_v6_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "forward_v4"
+ atf_add_test_case "forward_v6"
+}
diff --git a/tests/sys/netpfil/pf/rules_counter.sh b/tests/sys/netpfil/pf/rules_counter.sh
new file mode 100644
index 000000000000..98f96a7adca1
--- /dev/null
+++ b/tests/sys/netpfil/pf/rules_counter.sh
@@ -0,0 +1,209 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2021 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "get_clear" "cleanup"
+get_clear_head()
+{
+ atf_set descr 'Test clearing rules counters on get rules'
+ atf_set require.user root
+}
+
+get_clear_body()
+{
+ pft_init
+
+ epair_send=$(vnet_mkepair)
+ ifconfig ${epair_send}a 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair_send}b
+ jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up
+ jexec alcatraz pfctl -e
+
+ pft_set_rules alcatraz \
+ "pass all"
+
+ # Ensure the rule matched packets, so we can verify non-zero counters
+ atf_check -s exit:0 -o ignore ping -c 3 192.0.2.2
+
+ # Expect non-zero counters
+ atf_check -s exit:0 -e ignore \
+ -o match:'Evaluations: [1-9][0-9]*[[:space:]]*Packets: [1-9][0-9]*[[:space:]]*Bytes: [1-9][0-9]*[[:space:]]*' \
+ jexec alcatraz pfctl -s r -v
+
+ # We should still see non-zero because we didn't clear on the last
+ # pfctl, but are going to clear now
+ atf_check -s exit:0 -e ignore \
+ -o match:'Evaluations: [1-9][0-9]*[[:space:]]*Packets: [1-9][0-9]*[[:space:]]*Bytes: [1-9][0-9]*[[:space:]]*' \
+ jexec alcatraz pfctl -s r -v -z
+
+ # Expect zero counters
+ atf_check -s exit:0 -e ignore \
+ -o match:'Evaluations: 0[[:space:]]*Packets: 0*[[:space:]]*Bytes: 0*[[:space:]]*' \
+ jexec alcatraz pfctl -s r -v
+}
+
+get_clear_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "keepcounters" "cleanup"
+keepcounters_head()
+{
+ atf_set descr 'Test keepcounter functionality'
+ atf_set require.user root
+}
+
+keepcounters_body()
+{
+ pft_init
+
+ epair_send=$(vnet_mkepair)
+ ifconfig ${epair_send}a 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair_send}b
+ jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up
+ jexec alcatraz pfctl -e
+
+ pft_set_rules alcatraz \
+ "pass all"
+
+ # Expect zero counters
+ atf_check -s exit:0 -e ignore \
+ -o match:'Evaluations: 0[[:space:]]*Packets: 0*[[:space:]]*Bytes: 0*[[:space:]]*' \
+ jexec alcatraz pfctl -s r -v
+
+ # Ensure the rule matched packets, so we can verify non-zero counters
+ atf_check -s exit:0 -o ignore ping -c 3 192.0.2.2
+
+ # Expect non-zero counters
+ atf_check -s exit:0 -e ignore \
+ -o match:'Evaluations: [1-9][0-9]*[[:space:]]*Packets: [1-9][0-9]*[[:space:]]*Bytes: [1-9][0-9]*[[:space:]]*' \
+ jexec alcatraz pfctl -s r -v
+
+ # As we set the (same) rules again we'd expect the counters to return
+ # to zero
+ pft_set_rules noflush alcatraz \
+ "pass all"
+
+ atf_check -s exit:0 -e ignore \
+ -o match:'Evaluations: 0[[:space:]]*Packets: 0*[[:space:]]*Bytes: 0*[[:space:]]*' \
+ jexec alcatraz pfctl -s r -v
+
+ # Increment rule counters
+ atf_check -s exit:0 -o ignore ping -c 3 192.0.2.2
+
+ # Now set new rules with 'keepcounters' set, so we'd expect nonzero
+ # counters
+ pft_set_rules noflush alcatraz \
+ "set keepcounters" \
+ "pass all"
+
+ atf_check -s exit:0 -e ignore \
+ -o match:'Evaluations: [1-9][0-9]*[[:space:]]*Packets: [1-9][0-9]*[[:space:]]*Bytes: [1-9][0-9]*[[:space:]]*' \
+ jexec alcatraz pfctl -s r -v
+
+ # However, if we set a different rule it should return to zero
+ pft_set_rules noflush alcatraz \
+ "set keepcounters" \
+ "pass inet all"
+
+ atf_check -s exit:0 -e ignore \
+ -o match:'Evaluations: 0[[:space:]]*Packets: 0*[[:space:]]*Bytes: 0*[[:space:]]*' \
+ jexec alcatraz pfctl -s r -v
+
+ # If we generate traffic and don't set keepcounters we also see zero
+ # counts when setting new rules
+ atf_check -s exit:0 -o ignore ping -c 3 192.0.2.2
+ pft_set_rules noflush alcatraz \
+ "pass inet all"
+
+ atf_check -s exit:0 -e ignore \
+ -o match:'Evaluations: 0[[:space:]]*Packets: 0*[[:space:]]*Bytes: 0*[[:space:]]*' \
+ jexec alcatraz pfctl -s r -v
+}
+
+atf_test_case "4G" "cleanup"
+4G_head()
+{
+ atf_set descr 'Test keepcounter for values above 32 bits'
+ atf_set require.user root
+}
+
+4G_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+ jexec alcatraz nc -l 1234 >/dev/null &
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "pass all"
+
+ # Now pass more than 4GB of data
+ dd if=/dev/zero bs=1k count=4M | nc -N 192.0.2.2 1234
+
+ bytes=$(jexec alcatraz pfctl -s r -v | awk '/Bytes:/ { print $7; }')
+ if [ $bytes -lt 4000000000 ];
+ then
+ atf_fail "Expected to see > 4GB"
+ fi
+
+ # Set new rules, keeping counters
+ pft_set_rules noflush alcatraz \
+ "set keepcounters" \
+ "pass all"
+
+ bytes=$(jexec alcatraz pfctl -s r -v | awk '/Bytes:/ { print $7; }')
+ if [ $bytes -lt 4000000000 ];
+ then
+ atf_fail "Expected to see > 4GB after rule reload"
+ fi
+}
+
+4G_cleanup()
+{
+ pft_cleanup
+}
+
+keepcounters_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "get_clear"
+ atf_add_test_case "keepcounters"
+ atf_add_test_case "4G"
+}
diff --git a/tests/sys/netpfil/pf/scrub.sh b/tests/sys/netpfil/pf/scrub.sh
new file mode 100644
index 000000000000..6a5b748bed7b
--- /dev/null
+++ b/tests/sys/netpfil/pf/scrub.sh
@@ -0,0 +1,220 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Kristof Provost <kp@FreeBSD.org>
+# Copyright (c) 2023 Kajetan Staszkiewicz <vegeta@tuxpowered.net>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "max_mss_v4" "cleanup"
+max_mss_v4_head()
+{
+ atf_set descr 'Test IPv4 scrub "mss" rule'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+max_mss_v4_body()
+{
+ setup_router_dummy_ipv4
+ pft_set_rules router "scrub on ${epair_tester}b max-mss 1300"
+ # Check aligned
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-mss=1400 --expect-mss=1300
+ # And unaligned
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-mss=1400 --expect-mss=1300 \
+ --send-tcpopt-unaligned
+}
+
+max_mss_v4_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_test_case "max_mss_v6" "cleanup"
+max_mss_v6_head()
+{
+ atf_set descr 'Test IPv6 scrub "mss" rule'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+max_mss_v6_body()
+{
+ setup_router_dummy_ipv6
+ pft_set_rules router "scrub on ${epair_tester}b max-mss 1300"
+ # Check aligned
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-mss=1400 --expect-mss=1300
+ # And unaligned
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-mss=1400 --expect-mss=1300 \
+ --send-tcpopt-unaligned
+}
+
+max_mss_v6_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_test_case "set_tos_v4" "cleanup"
+set_tos_v4_head()
+{
+ atf_set descr 'Test IPv4 scub "set-tos" rule'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+set_tos_v4_body()
+{
+ setup_router_dummy_ipv4
+ pft_set_rules router "scrub on ${epair_tester}b set-tos 0x42"
+ ping_dummy_check_request exit:0 --send-tc=0 --expect-tc=66
+}
+
+set_tos_v4_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_test_case "set_tos_v6" "cleanup"
+set_tos_v6_head()
+{
+ atf_set descr 'Test IPv6 scub "set-tos" rule'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+set_tos_v6_body()
+{
+ setup_router_dummy_ipv6
+ pft_set_rules router "scrub on ${epair_tester}b set-tos 0x42"
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-tc=0 --expect-tc=66
+}
+
+set_tos_v6_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_test_case "min_ttl_v4" "cleanup"
+min_ttl_v4_head()
+{
+ atf_set descr 'Test IPv4 scub "min-ttl" rule'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+min_ttl_v4_body()
+{
+ setup_router_dummy_ipv4
+ pft_set_rules router "scrub on ${epair_tester}b min-ttl 50"
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-hlim=40 --expect-hlim=49
+}
+
+min_ttl_v4_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_test_case "min_ttl_v6" "cleanup"
+min_ttl_v6_head()
+{
+ atf_set descr 'Test IPv6 scub "min-ttl" rule'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+min_ttl_v6_body()
+{
+ setup_router_dummy_ipv6
+ pft_set_rules router "scrub on ${epair_tester}b min-ttl 50"
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-hlim=40 --expect-hlim=49
+}
+
+min_ttl_v6_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_test_case "no_scrub_v4" "cleanup"
+no_scrub_v4_head()
+{
+ atf_set descr 'Test IPv4 "no scrub" rule'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+no_scrub_v4_body()
+{
+ setup_router_dummy_ipv4
+ pft_set_rules router\
+ "no scrub on ${epair_tester}b to ${net_server_host_server}"
+ "scrub on ${epair_tester}b set-tos 0x42"
+ ping_dummy_check_request exit:0 --send-tc=0 --expect-tc=0
+}
+
+no_scrub_v4_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_test_case "no_scrub_v6" "cleanup"
+no_scrub_v6_head()
+{
+ atf_set descr 'Test IPv6 "no scrub" rule'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+no_scrub_v6_body()
+{
+ setup_router_dummy_ipv6
+ pft_set_rules router \
+ "no scrub on ${epair_tester}b to ${net_server_host_server}"
+ "scrub on ${epair_tester}b set-tos 0x42"
+ ping_dummy_check_request exit:0 --send-tc=0 --expect-tc=0
+}
+
+no_scrub_v6_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_init_test_cases()
+{
+ atf_add_test_case "max_mss_v4"
+ atf_add_test_case "max_mss_v6"
+ atf_add_test_case "set_tos_v4"
+ atf_add_test_case "set_tos_v6"
+ atf_add_test_case "min_ttl_v4"
+ atf_add_test_case "min_ttl_v6"
+ atf_add_test_case "no_scrub_v4"
+ atf_add_test_case "no_scrub_v6"
+}
diff --git a/tests/sys/netpfil/pf/scrub_compat.sh b/tests/sys/netpfil/pf/scrub_compat.sh
new file mode 100644
index 000000000000..6e1499309869
--- /dev/null
+++ b/tests/sys/netpfil/pf/scrub_compat.sh
@@ -0,0 +1,221 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Kristof Provost <kp@FreeBSD.org>
+# Copyright (c) 2023 Kajetan Staszkiewicz <vegeta@tuxpowered.net>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+common_dir=$(atf_get_srcdir)/../common
+
+atf_test_case "max_mss_v4" "cleanup"
+max_mss_v4_head()
+{
+ atf_set descr 'Test IPv4 scrub "mss" rule'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+max_mss_v4_body()
+{
+ setup_router_dummy_ipv4
+ pft_set_rules router "scrub on ${epair_tester}b max-mss 1300"
+ # Check aligned
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-mss=1400 --expect-mss=1300
+ # And unaligned
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-mss=1400 --expect-mss=1300 \
+ --send-tcpopt-unaligned
+}
+
+max_mss_v4_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_test_case "max_mss_v6" "cleanup"
+max_mss_v6_head()
+{
+ atf_set descr 'Test IPv6 scrub "mss" rule'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+max_mss_v6_body()
+{
+ setup_router_dummy_ipv6
+ pft_set_rules router "scrub on ${epair_tester}b max-mss 1300"
+ # Check aligned
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-mss=1400 --expect-mss=1300
+ # And unaligned
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-mss=1400 --expect-mss=1300 \
+ --send-tcpopt-unaligned
+}
+
+max_mss_v6_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_test_case "set_tos_v4" "cleanup"
+set_tos_v4_head()
+{
+ atf_set descr 'Test IPv4 scub "set-tos" rule'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+set_tos_v4_body()
+{
+ setup_router_dummy_ipv4
+ pft_set_rules router "scrub on ${epair_tester}b set-tos 0x42"
+ ping_dummy_check_request exit:0 --send-tc=0 --expect-tc=66
+}
+
+set_tos_v4_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_test_case "set_tos_v6" "cleanup"
+set_tos_v6_head()
+{
+ atf_set descr 'Test IPv6 scub "set-tos" rule'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+set_tos_v6_body()
+{
+ setup_router_dummy_ipv6
+ pft_set_rules router "scrub on ${epair_tester}b set-tos 0x42"
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-tc=0 --expect-tc=66
+}
+
+set_tos_v6_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_test_case "min_ttl_v4" "cleanup"
+min_ttl_v4_head()
+{
+ atf_set descr 'Test IPv4 scub "min-ttl" rule'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+min_ttl_v4_body()
+{
+ setup_router_dummy_ipv4
+ pft_set_rules router "scrub on ${epair_tester}b min-ttl 50"
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-hlim=40 --expect-hlim=49
+}
+
+min_ttl_v4_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_test_case "min_ttl_v6" "cleanup"
+min_ttl_v6_head()
+{
+ atf_set descr 'Test IPv6 scub "min-ttl" rule'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+min_ttl_v6_body()
+{
+ setup_router_dummy_ipv6
+ pft_set_rules router "scrub on ${epair_tester}b min-ttl 50"
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-hlim=40 --expect-hlim=49
+}
+
+min_ttl_v6_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_test_case "no_scrub_v4" "cleanup"
+no_scrub_v4_head()
+{
+ atf_set descr 'Test IPv4 "no scrub" rule'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+no_scrub_v4_body()
+{
+ setup_router_dummy_ipv4
+ pft_set_rules router\
+ "no scrub on ${epair_tester}b to ${net_server_host_server}"
+ "scrub on ${epair_tester}b set-tos 0x42"
+ ping_dummy_check_request exit:0 --send-tc=0 --expect-tc=0
+}
+
+no_scrub_v4_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_test_case "no_scrub_v6" "cleanup"
+no_scrub_v6_head()
+{
+ atf_set descr 'Test IPv6 "no scrub" rule'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+no_scrub_v6_body()
+{
+ setup_router_dummy_ipv6
+ pft_set_rules router \
+ "no scrub on ${epair_tester}b to ${net_server_host_server}"
+ "scrub on ${epair_tester}b set-tos 0x42"
+ ping_dummy_check_request exit:0 --send-tc=0 --expect-tc=0
+}
+
+no_scrub_v6_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_init_test_cases()
+{
+ atf_add_test_case "max_mss_v4"
+ atf_add_test_case "max_mss_v6"
+ atf_add_test_case "set_tos_v4"
+ atf_add_test_case "set_tos_v6"
+ atf_add_test_case "min_ttl_v4"
+ atf_add_test_case "min_ttl_v6"
+ atf_add_test_case "no_scrub_v4"
+ atf_add_test_case "no_scrub_v6"
+}
diff --git a/tests/sys/netpfil/pf/scrub_pass.sh b/tests/sys/netpfil/pf/scrub_pass.sh
new file mode 100644
index 000000000000..8ba599144757
--- /dev/null
+++ b/tests/sys/netpfil/pf/scrub_pass.sh
@@ -0,0 +1,173 @@
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Kristof Provost <kp@FreeBSD.org>
+# Copyright (c) 2023 Kajetan Staszkiewicz <vegeta@tuxpowered.net>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+common_dir=$(atf_get_srcdir)/../common
+
+atf_test_case "max_mss_v4" "cleanup"
+max_mss_v4_head()
+{
+ atf_set descr 'Test IPv4 pass "mss" rule'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+max_mss_v4_body()
+{
+ setup_router_dummy_ipv4
+ pft_set_rules router "pass on ${epair_tester}b scrub ( max-mss 1300 )"
+ # Check aligned
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-mss=1400 --expect-mss=1300
+ # And unaligned
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-mss=1400 --expect-mss=1300 \
+ --send-tcpopt-unaligned
+}
+
+max_mss_v4_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_test_case "max_mss_v6" "cleanup"
+max_mss_v6_head()
+{
+ atf_set descr 'Test IPv6 pass "mss" rule'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+max_mss_v6_body()
+{
+ setup_router_dummy_ipv6
+ pft_set_rules router "pass on ${epair_tester}b scrub ( max-mss 1300 )"
+ # Check aligned
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-mss=1400 --expect-mss=1300
+ # And unaligned
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-mss=1400 --expect-mss=1300 \
+ --send-tcpopt-unaligned
+}
+
+max_mss_v6_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_test_case "set_tos_v4" "cleanup"
+set_tos_v4_head()
+{
+ atf_set descr 'Test IPv4 pass "set-tos" rule'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+set_tos_v4_body()
+{
+ setup_router_dummy_ipv4
+ pft_set_rules router "pass on ${epair_tester}b set ( tos 0x42 )"
+ ping_dummy_check_request exit:0 --send-tc=66 --expect-tc=66
+}
+
+set_tos_v4_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_test_case "set_tos_v6" "cleanup"
+set_tos_v6_head()
+{
+ atf_set descr 'Test IPv6 pass "set-tos" rule'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+set_tos_v6_body()
+{
+ setup_router_dummy_ipv4
+ pft_set_rules router "pass on ${epair_tester}b set ( tos 0x42 )"
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-tc=66 --expect-tc=66
+}
+
+set_tos_v6_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_test_case "min_ttl_v4" "cleanup"
+min_ttl_v4_head()
+{
+ atf_set descr 'Test IPv4 pass "min-ttl" rule'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+min_ttl_v4_body()
+{
+ setup_router_dummy_ipv4
+ pft_set_rules router "pass on ${epair_tester}b scrub ( min-ttl 50 )"
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-hlim=40 --expect-hlim=49
+}
+
+min_ttl_v4_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_test_case "min_ttl_v6" "cleanup"
+min_ttl_v6_head()
+{
+ atf_set descr 'Test IPv6 pass "min-ttl" rule'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+min_ttl_v6_body()
+{
+ setup_router_dummy_ipv6
+ pft_set_rules router "pass on ${epair_tester}b scrub ( min-ttl 50 )"
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-hlim=40 --expect-hlim=49
+}
+
+min_ttl_v6_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_init_test_cases()
+{
+ atf_add_test_case "max_mss_v4"
+ atf_add_test_case "max_mss_v6"
+ atf_add_test_case "set_tos_v4"
+ atf_add_test_case "set_tos_v6"
+ atf_add_test_case "min_ttl_v4"
+ atf_add_test_case "min_ttl_v6"
+}
diff --git a/tests/sys/netpfil/pf/sctp.py b/tests/sys/netpfil/pf/sctp.py
new file mode 100644
index 000000000000..da42ce527195
--- /dev/null
+++ b/tests/sys/netpfil/pf/sctp.py
@@ -0,0 +1,713 @@
+import pytest
+import ctypes
+import socket
+import ipaddress
+import re
+from atf_python.sys.net.tools import ToolsHelper
+from atf_python.sys.net.vnet import VnetTestTemplate
+
+import time
+
+SCTP_UNORDERED = 0x0400
+
+SCTP_NODELAY = 0x00000004
+SCTP_SET_PEER_PRIMARY_ADDR = 0x00000006
+SCTP_PRIMARY_ADDR = 0x00000007
+
+SCTP_BINDX_ADD_ADDR = 0x00008001
+SCTP_BINDX_REM_ADDR = 0x00008002
+
+class sockaddr_in(ctypes.Structure):
+ _fields_ = [
+ ('sin_len', ctypes.c_uint8),
+ ('sin_family', ctypes.c_uint8),
+ ('sin_port', ctypes.c_uint16),
+ ('sin_addr', ctypes.c_uint32),
+ ('sin_zero', ctypes.c_int8 * 8)
+ ]
+
+class sockaddr_in6(ctypes.Structure):
+ _fields_ = [
+ ('sin6_len', ctypes.c_uint8),
+ ('sin6_family', ctypes.c_uint8),
+ ('sin6_port', ctypes.c_uint16),
+ ('sin6_flowinfo', ctypes.c_uint32),
+ ('sin6_addr', ctypes.c_uint8 * 16),
+ ('sin6_scope_id', ctypes.c_uint32)
+ ]
+
+class sockaddr_storage(ctypes.Union):
+ _fields_ = [
+ ("v4", sockaddr_in),
+ ("v6", sockaddr_in6)
+ ]
+
+class sctp_sndrcvinfo(ctypes.Structure):
+ _fields_ = [
+ ('sinfo_stream', ctypes.c_uint16),
+ ('sinfo_ssn', ctypes.c_uint16),
+ ('sinfo_flags', ctypes.c_uint16),
+ ('sinfo_ppid', ctypes.c_uint32),
+ ('sinfo_context', ctypes.c_uint32),
+ ('sinfo_timetolive', ctypes.c_uint32),
+ ('sinfo_tsn', ctypes.c_uint32),
+ ('sinfo_cumtsn', ctypes.c_uint32),
+ ('sinfo_assoc_id', ctypes.c_uint32),
+ ]
+
+class sctp_setprim(ctypes.Structure):
+ _fields_ = [
+ ('ssp_addr', sockaddr_storage),
+ ('ssp_pad', ctypes.c_int8 * (128 - 16)),
+ ('ssp_assoc_id', ctypes.c_uint32),
+ ('ssp_padding', ctypes.c_uint32)
+ ]
+
+def to_sockaddr(ip, port):
+ ip = ipaddress.ip_address(ip)
+
+ if ip.version == 4:
+ addr = sockaddr_in()
+ addr.sin_len = ctypes.sizeof(addr)
+ addr.sin_family = socket.AF_INET
+ addr.sin_port = socket.htons(port)
+ addr.sin_addr = socket.htonl(int.from_bytes(ip.packed, byteorder='big'))
+ else:
+ assert ip.version == 6
+
+ addr = sockaddr_in6()
+ addr.sin6_len = ctypes.sizeof(addr)
+ addr.sin6_family = socket.AF_INET6
+ addr.sin6_port = socket.htons(port)
+ for i in range(0, 16):
+ addr.sin6_addr[i] = ip.packed[i]
+
+ return addr
+
+class SCTPServer:
+ def __init__(self, family, port=1234):
+ self._libc = ctypes.CDLL("libc.so.7", use_errno=True)
+
+ self._listen_fd = self._libc.socket(family, socket.SOCK_STREAM, socket.IPPROTO_SCTP)
+ if self._listen_fd == -1:
+ raise Exception("Failed to create socket")
+
+ if family == socket.AF_INET:
+ srvaddr = sockaddr_in()
+ srvaddr.sin_len = ctypes.sizeof(srvaddr)
+ srvaddr.sin_family = socket.AF_INET
+ srvaddr.sin_port = socket.htons(port)
+ srvaddr.sin_addr = socket.INADDR_ANY
+ else:
+ srvaddr = sockaddr_in6()
+ srvaddr.sin6_len = ctypes.sizeof(srvaddr)
+ srvaddr.sin6_family = family
+ srvaddr.sin6_port = socket.htons(port)
+ # Leave sin_addr empty, because ANY is zero
+
+ ret = self._libc.bind(self._listen_fd, ctypes.pointer(srvaddr),
+ ctypes.sizeof(srvaddr))
+ if ret == -1:
+ raise Exception("Failed to bind: %d" % ctypes.get_errno())
+
+ ret = self._libc.listen(self._listen_fd, 2)
+ if ret == -1:
+ raise Exception("Failed to listen")
+
+ def _to_string(self, buf):
+ return ''.join([chr(int.from_bytes(i, byteorder='big')) for i in buf]).rstrip('\x00')
+
+ def accept(self, vnet):
+ fd = self._libc.accept(self._listen_fd, 0, 0)
+ if fd < 0:
+ raise Exception("Failed to accept")
+
+ print("SCTPServer: connection opened")
+ while True:
+ rcvinfo = sctp_sndrcvinfo()
+ flags = ctypes.c_int()
+ buf = ctypes.create_string_buffer(128)
+
+ # Receive a single message, and inform the other vnet about it.
+ ret = self._libc.sctp_recvmsg(fd, ctypes.cast(buf, ctypes.c_void_p), 128,
+ 0, 0, ctypes.pointer(rcvinfo), ctypes.pointer(flags))
+ if ret < 0:
+ print("SCTPServer: connection closed")
+ return
+ if ret == 0:
+ continue
+
+ rcvd = {}
+ rcvd['ppid'] = socket.ntohl(rcvinfo.sinfo_ppid)
+ rcvd['data'] = self._to_string(buf)
+ rcvd['len'] = ret
+ print(rcvd)
+ vnet.pipe.send(rcvd)
+
+class SCTPClient:
+ def __init__(self, ip, port=1234, fromaddr=None):
+ self._libc = ctypes.CDLL("libc.so.7", use_errno=True)
+
+ if ipaddress.ip_address(ip).version == 4:
+ family = socket.AF_INET
+ else:
+ family = socket.AF_INET6
+
+ self._fd = self._libc.socket(family, socket.SOCK_STREAM,
+ socket.IPPROTO_SCTP)
+ if self._fd == -1:
+ raise Exception("Failed to open socket")
+
+ if fromaddr is not None:
+ addr = to_sockaddr(fromaddr, 0)
+
+ ret = self._libc.bind(self._fd, ctypes.pointer(addr), ctypes.sizeof(addr))
+ if ret != 0:
+ print("bind() => %d", ctypes.get_errno())
+ raise
+
+ addr = to_sockaddr(ip, port)
+ ret = self._libc.connect(self._fd, ctypes.pointer(addr), ctypes.sizeof(addr))
+ if ret == -1:
+ raise Exception("Failed to connect")
+
+ # Enable NODELAY, because otherwise the sending host may wait for SACK
+ # on a data chunk we've removed
+ enable = ctypes.c_int(1)
+ ret = self._libc.setsockopt(self._fd, socket.IPPROTO_SCTP,
+ SCTP_NODELAY, ctypes.pointer(enable), 4)
+
+ def newpeer(self, addr):
+ print("newpeer(%s)" % (addr))
+
+ setp = sctp_setprim()
+ a = to_sockaddr(addr, 0)
+ if type(a) is sockaddr_in:
+ setp.ssp_addr.v4 = a
+ else:
+ assert type(a) is sockaddr_in6
+ setp.ssp_addr.v6 = a
+
+ ret = self._libc.setsockopt(self._fd, socket.IPPROTO_SCTP,
+ SCTP_PRIMARY_ADDR, ctypes.pointer(setp), ctypes.sizeof(setp))
+ if ret != 0:
+ print("errno %d" % ctypes.get_errno())
+ raise Exception(ctypes.get_errno())
+
+ def newprimary(self, addr):
+ print("newprimary(%s)" % (addr))
+
+ # Strictly speaking needs to be struct sctp_setpeerprim, but that's
+ # identical to sctp_setprim
+ setp = sctp_setprim()
+ a = to_sockaddr(addr, 0)
+ if type(a) is sockaddr_in:
+ setp.ssp_addr.v4 = a
+ else:
+ assert type(a) is sockaddr_in6
+ setp.ssp_addr.v6 = a
+
+ ret = self._libc.setsockopt(self._fd, socket.IPPROTO_SCTP,
+ SCTP_SET_PEER_PRIMARY_ADDR, ctypes.pointer(setp), ctypes.sizeof(setp))
+ if ret != 0:
+ print("errno %d" % ctypes.get_errno())
+ raise
+
+ def bindx(self, addr, add):
+ print("bindx(%s, %s)" % (addr, add))
+
+ addr = to_sockaddr(addr, 0)
+
+ if add:
+ flag = SCTP_BINDX_ADD_ADDR
+ else:
+ flag = SCTP_BINDX_REM_ADDR
+ ret = self._libc.sctp_bindx(self._fd, ctypes.pointer(addr), 1, flag)
+ if ret != 0:
+ print("sctp_bindx() errno %d" % ctypes.get_errno())
+ raise
+
+ def send(self, buf, ppid, ordered=False):
+ flags = 0
+
+ if not ordered:
+ flags = SCTP_UNORDERED
+
+ ppid = socket.htonl(ppid)
+ ret = self._libc.sctp_sendmsg(self._fd, ctypes.c_char_p(buf), len(buf),
+ ctypes.c_void_p(0), 0, ppid, flags, 0, 0, 0)
+ if ret < 0:
+ raise Exception("Failed to send message")
+
+ def close(self):
+ self._libc.close(self._fd)
+ self._fd = -1
+
+class TestSCTP(VnetTestTemplate):
+ REQUIRED_MODULES = ["sctp", "pf"]
+ TOPOLOGY = {
+ "vnet1": {"ifaces": ["if1"]},
+ "vnet2": {"ifaces": ["if1"]},
+ "if1": {"prefixes4": [("192.0.2.1/24", "192.0.2.2/24")]},
+ }
+
+ def vnet2_handler(self, vnet):
+ # Give ourself a second IP address, for multihome testing
+ ifname = vnet.iface_alias_map["if1"].name
+ ToolsHelper.print_output("/sbin/ifconfig %s inet alias 192.0.2.3/24" % ifname)
+
+ # Start an SCTP server process, pipe the ppid + data back to the other vnet?
+ srv = SCTPServer(socket.AF_INET, port=1234)
+ while True:
+ srv.accept(vnet)
+
+ @pytest.mark.require_user("root")
+ def test_multihome(self):
+ srv_vnet = self.vnet_map["vnet2"]
+
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.pf_rules([
+ "block proto sctp",
+ "pass inet proto sctp to 192.0.2.0/24",
+ "pass on lo"])
+
+ # Sanity check, we can communicate with the primary address.
+ client = SCTPClient("192.0.2.3", 1234)
+ client.send(b"hello", 0)
+ rcvd = self.wait_object(srv_vnet.pipe)
+ print(rcvd)
+ assert rcvd['ppid'] == 0
+ assert rcvd['data'] == "hello"
+
+ try:
+ client.newpeer("192.0.2.2")
+ client.send(b"world", 0)
+ rcvd = self.wait_object(srv_vnet.pipe)
+ print(rcvd)
+ assert rcvd['ppid'] == 0
+ assert rcvd['data'] == "world"
+ finally:
+ # Debug output
+ ToolsHelper.print_output("/sbin/pfctl -ss")
+ ToolsHelper.print_output("/sbin/pfctl -sr -vv")
+
+ # Check that we have a state for 192.0.2.3 and 192.0.2.2 to 192.0.2.1
+ states = ToolsHelper.get_output("/sbin/pfctl -ss")
+ assert re.search(r"all sctp 192.0.2.1:.*192.0.2.3:1234", states)
+ assert re.search(r"all sctp 192.0.2.1:.*192.0.2.2:1234", states)
+
+ @pytest.mark.require_user("root")
+ def test_multihome_asconf(self):
+ srv_vnet = self.vnet_map["vnet2"]
+
+ # Assign a second IP to ourselves
+ ToolsHelper.print_output("/sbin/ifconfig %s inet alias 192.0.2.10/24"
+ % self.vnet.iface_alias_map["if1"].name)
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.pf_rules([
+ "block proto sctp",
+ "pass on lo",
+ "pass inet proto sctp from 192.0.2.0/24"])
+
+ # Sanity check, we can communicate with the primary address.
+ client = SCTPClient("192.0.2.3", 1234, "192.0.2.1")
+ client.send(b"hello", 0)
+ rcvd = self.wait_object(srv_vnet.pipe)
+ print(rcvd)
+ assert rcvd['ppid'] == 0
+ assert rcvd['data'] == "hello"
+
+ # Now add our second address to the connection
+ client.bindx("192.0.2.10", True)
+
+ # We can still communicate
+ client.send(b"world", 0)
+ rcvd = self.wait_object(srv_vnet.pipe)
+ print(rcvd)
+ assert rcvd['ppid'] == 0
+ assert rcvd['data'] == "world"
+
+ # Now change to a different peer address
+ try:
+ client.newprimary("192.0.2.10")
+ client.send(b"!", 0)
+ rcvd = self.wait_object(srv_vnet.pipe, 5)
+ print(rcvd)
+ assert rcvd['ppid'] == 0
+ assert rcvd['data'] == "!"
+ finally:
+ # Debug output
+ ToolsHelper.print_output("/sbin/pfctl -ss -vv")
+
+ # Ensure we have the states we'd expect
+ states = ToolsHelper.get_output("/sbin/pfctl -ss")
+ assert re.search(r"all sctp 192.0.2.1:.*192.0.2.3:1234", states)
+ assert re.search(r"all sctp 192.0.2.10:.*192.0.2.3:1234", states)
+
+ # Now remove 192.0.2.1 as an address
+ client.bindx("192.0.2.1", False)
+
+ # We can still communicate
+ try:
+ client.send(b"More data", 0)
+ rcvd = self.wait_object(srv_vnet.pipe, 5)
+ print(rcvd)
+ assert rcvd['ppid'] == 0
+ assert rcvd['data'] =="More data"
+ finally:
+ # Debug output
+ ToolsHelper.print_output("/sbin/pfctl -ss -vv")
+
+ # Verify that state is closing
+ states = ToolsHelper.get_output("/sbin/pfctl -ss")
+ assert re.search(r"all sctp 192.0.2.1:.*192.0.2.3:1234.*SHUTDOWN", states)
+
+
+ @pytest.mark.require_user("root")
+ def test_permutation_if_bound(self):
+ # Test that we generate all permutations of src/dst addresses.
+ # Assign two addresses to each end, and check for the expected states
+ srv_vnet = self.vnet_map["vnet2"]
+
+ ifname = self.vnet_map["vnet1"].iface_alias_map["if1"].name
+ ToolsHelper.print_output("/sbin/ifconfig %s inet alias 192.0.2.4/24" % ifname)
+
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.pf_rules([
+ "set state-policy if-bound",
+ "block proto sctp",
+ "pass on lo",
+ "pass inet proto sctp to 192.0.2.0/24"])
+
+ # Sanity check, we can communicate with the primary address.
+ client = SCTPClient("192.0.2.3", 1234)
+ client.send(b"hello", 0)
+ rcvd = self.wait_object(srv_vnet.pipe)
+ print(rcvd)
+ assert rcvd['ppid'] == 0
+ assert rcvd['data'] == "hello"
+
+ # Check that we have a state for 192.0.2.3 and 192.0.2.2 to 192.0.2.1, but also to 192.0.2.4
+ states = ToolsHelper.get_output("/sbin/pfctl -ss")
+ print(states)
+ assert re.search(r"epair.*sctp 192.0.2.1:.*192.0.2.3:1234", states)
+ assert re.search(r"epair.*sctp 192.0.2.1:.*192.0.2.2:1234", states)
+ assert re.search(r"epair.*sctp 192.0.2.4:.*192.0.2.3:1234", states)
+ assert re.search(r"epair.*sctp 192.0.2.4:.*192.0.2.2:1234", states)
+
+ @pytest.mark.require_user("root")
+ def test_permutation_floating(self):
+ # Test that we generate all permutations of src/dst addresses.
+ # Assign two addresses to each end, and check for the expected states
+ srv_vnet = self.vnet_map["vnet2"]
+
+ ifname = self.vnet_map["vnet1"].iface_alias_map["if1"].name
+ ToolsHelper.print_output("/sbin/ifconfig %s inet alias 192.0.2.4/24" % ifname)
+
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.pf_rules([
+ "block proto sctp",
+ "pass on lo",
+ "pass inet proto sctp to 192.0.2.0/24"])
+
+ # Sanity check, we can communicate with the primary address.
+ client = SCTPClient("192.0.2.3", 1234)
+ client.send(b"hello", 0)
+ rcvd = self.wait_object(srv_vnet.pipe)
+ print(rcvd)
+ assert rcvd['ppid'] == 0
+ assert rcvd['data'] == "hello"
+
+ # Check that we have a state for 192.0.2.3 and 192.0.2.2 to 192.0.2.1, but also to 192.0.2.4
+ states = ToolsHelper.get_output("/sbin/pfctl -ss")
+ print(states)
+ assert re.search(r"all sctp 192.0.2.1:.*192.0.2.3:1234", states)
+ assert re.search(r"all sctp 192.0.2.1:.*192.0.2.2:1234", states)
+ assert re.search(r"all sctp 192.0.2.4:.*192.0.2.3:1234", states)
+ assert re.search(r"all sctp 192.0.2.4:.*192.0.2.2:1234", states)
+
+ @pytest.mark.require_user("root")
+ def test_limit_addresses(self):
+ srv_vnet = self.vnet_map["vnet2"]
+
+ ifname = self.vnet_map["vnet1"].iface_alias_map["if1"].name
+ for i in range(0, 16):
+ ToolsHelper.print_output("/sbin/ifconfig %s inet alias 192.0.2.%d/24" % (ifname, 4 + i))
+
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.pf_rules([
+ "block proto sctp",
+ "pass on lo",
+ "pass inet proto sctp to 192.0.2.0/24"])
+
+ # Set up a connection, which will try to create states for all addresses
+ # we have assigned
+ client = SCTPClient("192.0.2.3", 1234)
+ client.send(b"hello", 0)
+ rcvd = self.wait_object(srv_vnet.pipe)
+ print(rcvd)
+ assert rcvd['ppid'] == 0
+ assert rcvd['data'] == "hello"
+
+ # But the number should be limited to 9 (original + 8 extra)
+ states = ToolsHelper.get_output("/sbin/pfctl -ss | grep 192.0.2.2")
+ print(states)
+ assert(states.count('\n') <= 9)
+
+ @pytest.mark.require_user("root")
+ def test_disallow_related(self):
+ srv_vnet = self.vnet_map["vnet2"]
+
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.pf_rules([
+ "block proto sctp",
+ "pass inet proto sctp to 192.0.2.3",
+ "pass on lo"])
+
+ # Sanity check, we can communicate with the primary address.
+ client = SCTPClient("192.0.2.3", 1234)
+ client.send(b"hello", 0)
+ rcvd = self.wait_object(srv_vnet.pipe)
+ print(rcvd)
+ assert rcvd['ppid'] == 0
+ assert rcvd['data'] == "hello"
+
+ # This shouldn't work
+ success=False
+ try:
+ client.newpeer("192.0.2.2")
+ client.send(b"world", 0)
+ rcvd = self.wait_object(srv_vnet.pipe)
+ print(rcvd)
+ assert rcvd['ppid'] == 0
+ assert rcvd['data'] == "world"
+ success=True
+ except:
+ success=False
+ assert not success
+
+ # Check that we have a state for 192.0.2.3, but not 192.0.2.2 to 192.0.2.1
+ states = ToolsHelper.get_output("/sbin/pfctl -ss")
+ assert re.search(r"all sctp 192.0.2.1:.*192.0.2.3:1234", states)
+ assert not re.search(r"all sctp 192.0.2.1:.*192.0.2.2:1234", states)
+
+ @pytest.mark.require_user("root")
+ def test_allow_related(self):
+ srv_vnet = self.vnet_map["vnet2"]
+
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.pf_rules([
+ "set state-policy if-bound",
+ "block proto sctp",
+ "pass inet proto sctp to 192.0.2.3 keep state (allow-related)",
+ "pass on lo"])
+
+ # Sanity check, we can communicate with the primary address.
+ client = SCTPClient("192.0.2.3", 1234)
+ client.send(b"hello", 0)
+ rcvd = self.wait_object(srv_vnet.pipe)
+ print(rcvd)
+ assert rcvd['ppid'] == 0
+ assert rcvd['data'] == "hello"
+
+ success=False
+ try:
+ client.newpeer("192.0.2.2")
+ client.send(b"world", 0)
+ rcvd = self.wait_object(srv_vnet.pipe)
+ print(rcvd)
+ assert rcvd['ppid'] == 0
+ assert rcvd['data'] == "world"
+ success=True
+ finally:
+ # Debug output
+ ToolsHelper.print_output("/sbin/pfctl -ss")
+ ToolsHelper.print_output("/sbin/pfctl -sr -vv")
+ assert success
+
+ # Check that we have a state for 192.0.2.3 and 192.0.2.2 to 192.0.2.1
+ states = ToolsHelper.get_output("/sbin/pfctl -ss")
+ assert re.search(r"epair.*sctp 192.0.2.1:.*192.0.2.3:1234", states)
+ assert re.search(r"epair.*sctp 192.0.2.1:.*192.0.2.2:1234", states)
+
+class TestSCTPv6(VnetTestTemplate):
+ REQUIRED_MODULES = ["sctp", "pf"]
+ TOPOLOGY = {
+ "vnet1": {"ifaces": ["if1"]},
+ "vnet2": {"ifaces": ["if1"]},
+ "if1": {"prefixes6": [("2001:db8::1/64", "2001:db8::2/64")]},
+ }
+
+ def vnet2_handler(self, vnet):
+ # Give ourself a second IP address, for multihome testing
+ ifname = vnet.iface_alias_map["if1"].name
+ ToolsHelper.print_output("/sbin/ifconfig %s inet6 alias 2001:db8::3/64" % ifname)
+
+ # Start an SCTP server process, pipe the ppid + data back to the other vnet?
+ srv = SCTPServer(socket.AF_INET6, port=1234)
+ while True:
+ srv.accept(vnet)
+
+ @pytest.mark.require_user("root")
+ def test_multihome(self):
+ srv_vnet = self.vnet_map["vnet2"]
+
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.pf_rules([
+ "block proto sctp",
+ "pass on lo",
+ "pass inet6 proto sctp to 2001:db8::0/64"])
+
+ # Sanity check, we can communicate with the primary address.
+ client = SCTPClient("2001:db8::3", 1234)
+ client.send(b"hello", 0)
+ rcvd = self.wait_object(srv_vnet.pipe)
+ print(rcvd)
+ assert rcvd['ppid'] == 0
+ assert rcvd['data'] == "hello"
+
+ # Now change to a different peer address
+ try:
+ client.newpeer("2001:db8::2")
+ client.send(b"world", 0)
+ rcvd = self.wait_object(srv_vnet.pipe)
+ print(rcvd)
+ assert rcvd['ppid'] == 0
+ assert rcvd['data'] == "world"
+ finally:
+ # Debug output
+ ToolsHelper.print_output("/sbin/pfctl -ss -vv")
+
+ # Check that we have the expected states
+ states = ToolsHelper.get_output("/sbin/pfctl -ss")
+ assert re.search(r"all sctp 2001:db8::1\[.*2001:db8::3\[1234\]", states)
+ assert re.search(r"all sctp 2001:db8::1\[.*2001:db8::2\[1234\]", states)
+
+ @pytest.mark.require_user("root")
+ def test_multihome_asconf(self):
+ srv_vnet = self.vnet_map["vnet2"]
+
+ # Assign a second IP to ourselves
+ ToolsHelper.print_output("/sbin/ifconfig %s inet6 alias 2001:db8::10/64"
+ % self.vnet.iface_alias_map["if1"].name)
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.pf_rules([
+ "block proto sctp",
+ "pass on lo",
+ "pass inet6 proto sctp from 2001:db8::/64"])
+
+ # Sanity check, we can communicate with the primary address.
+ client = SCTPClient("2001:db8::3", 1234, "2001:db8::1")
+ client.send(b"hello", 0)
+ rcvd = self.wait_object(srv_vnet.pipe)
+ print(rcvd)
+ assert rcvd['ppid'] == 0
+ assert rcvd['data'] == "hello"
+
+ # Now add our second address to the connection
+ client.bindx("2001:db8::10", True)
+
+ # We can still communicate
+ client.send(b"world", 0)
+ rcvd = self.wait_object(srv_vnet.pipe)
+ print(rcvd)
+ assert rcvd['ppid'] == 0
+ assert rcvd['data'] == "world"
+
+ # Now change to a different peer address
+ try:
+ client.newprimary("2001:db8::10")
+ client.send(b"!", 0)
+ rcvd = self.wait_object(srv_vnet.pipe, 5)
+ print(rcvd)
+ assert rcvd['ppid'] == 0
+ assert rcvd['data'] == "!"
+ finally:
+ # Debug output
+ ToolsHelper.print_output("/sbin/pfctl -ss -vv")
+
+ # Check that we have the expected states
+ states = ToolsHelper.get_output("/sbin/pfctl -ss")
+ assert re.search(r"all sctp 2001:db8::1\[.*2001:db8::3\[1234\]", states)
+ assert re.search(r"all sctp 2001:db8::10\[.*2001:db8::3\[1234\]", states)
+
+ # Now remove 2001:db8::1 as an address
+ client.bindx("2001:db8::1", False)
+
+ # Wecan still communicate
+ try:
+ client.send(b"More data", 0)
+ rcvd = self.wait_object(srv_vnet.pipe, 5)
+ print(rcvd)
+ assert rcvd['ppid'] == 0
+ assert rcvd['data'] == "More data"
+ finally:
+ # Debug output
+ ToolsHelper.print_output("/sbin/pfctl -ss -vv")
+
+ # Verify that the state is closing
+ states = ToolsHelper.get_output("/sbin/pfctl -ss")
+ assert re.search(r"all sctp 2001:db8::1\[.*2001:db8::3\[1234\].*SHUTDOWN", states)
+
+ @pytest.mark.require_user("root")
+ def test_permutation(self):
+ # Test that we generate all permutations of src/dst addresses.
+ # Assign two addresses to each end, and check for the expected states
+ srv_vnet = self.vnet_map["vnet2"]
+
+ ifname = self.vnet_map["vnet1"].iface_alias_map["if1"].name
+ ToolsHelper.print_output("/sbin/ifconfig %s inet6 alias 2001:db8::4/64" % ifname)
+
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.pf_rules([
+ "set state-policy if-bound",
+ "block proto sctp",
+ "pass on lo",
+ "pass inet6 proto sctp to 2001:db8::0/64"])
+
+ # Sanity check, we can communicate with the primary address.
+ client = SCTPClient("2001:db8::3", 1234)
+ client.send(b"hello", 0)
+ rcvd = self.wait_object(srv_vnet.pipe)
+ print(rcvd)
+ assert rcvd['ppid'] == 0
+ assert rcvd['data'] == "hello"
+
+ # Check that we have a state for 2001:db8::3 and 2001:db8::2 to 2001:db8::1, but also to 2001:db8::4
+ states = ToolsHelper.get_output("/sbin/pfctl -ss")
+ print(states)
+ assert re.search(r"epair.*sctp 2001:db8::1\[.*2001:db8::2\[1234\]", states)
+ assert re.search(r"epair.*sctp 2001:db8::1\[.*2001:db8::3\[1234\]", states)
+ assert re.search(r"epair.*sctp 2001:db8::4\[.*2001:db8::2\[1234\]", states)
+ assert re.search(r"epair.*sctp 2001:db8::4\[.*2001:db8::3\[1234\]", states)
+
+ @pytest.mark.require_user("root")
+ def test_permutation_floating(self):
+ # Test that we generate all permutations of src/dst addresses.
+ # Assign two addresses to each end, and check for the expected states
+ srv_vnet = self.vnet_map["vnet2"]
+
+ ifname = self.vnet_map["vnet1"].iface_alias_map["if1"].name
+ ToolsHelper.print_output("/sbin/ifconfig %s inet6 alias 2001:db8::4/64" % ifname)
+
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.pf_rules([
+ "block proto sctp",
+ "pass on lo",
+ "pass inet6 proto sctp to 2001:db8::0/64"])
+
+ # Sanity check, we can communicate with the primary address.
+ client = SCTPClient("2001:db8::3", 1234)
+ client.send(b"hello", 0)
+ rcvd = self.wait_object(srv_vnet.pipe)
+ print(rcvd)
+ assert rcvd['ppid'] == 0
+ assert rcvd['data'] == "hello"
+
+ # Check that we have a state for 2001:db8::3 and 2001:db8::2 to 2001:db8::1, but also to 2001:db8::4
+ states = ToolsHelper.get_output("/sbin/pfctl -ss")
+ print(states)
+ assert re.search(r"all sctp 2001:db8::1\[.*2001:db8::2\[1234\]", states)
+ assert re.search(r"all sctp 2001:db8::1\[.*2001:db8::3\[1234\]", states)
+ assert re.search(r"all sctp 2001:db8::4\[.*2001:db8::2\[1234\]", states)
+ assert re.search(r"all sctp 2001:db8::4\[.*2001:db8::3\[1234\]", states)
diff --git a/tests/sys/netpfil/pf/sctp.sh b/tests/sys/netpfil/pf/sctp.sh
new file mode 100644
index 000000000000..57dcdad1d866
--- /dev/null
+++ b/tests/sys/netpfil/pf/sctp.sh
@@ -0,0 +1,846 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+#
+# Copyright © 2023 Orange Business Services
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+sctp_init()
+{
+ pft_init
+ if ! kldstat -q -m sctp; then
+ atf_skip "This test requires SCTP"
+ fi
+}
+
+atf_test_case "basic_v4" "cleanup"
+basic_v4_head()
+{
+ atf_set descr 'Basic SCTP connection over IPv4 passthrough'
+ atf_set require.user root
+}
+
+basic_v4_body()
+{
+ sctp_init
+
+ j="sctp:basic_v4"
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail ${j}a ${epair}a
+ vnet_mkjail ${j}b ${epair}b
+
+ jexec ${j}a ifconfig ${epair}a 192.0.2.1/24 up
+ jexec ${j}b ifconfig ${epair}b 192.0.2.2/24 up
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ jexec ${j}a ping -c 1 192.0.2.2
+
+ jexec ${j}a pfctl -e
+ pft_set_rules ${j}a \
+ "block" \
+ "pass in proto sctp to port 1234"
+
+ echo "foo" | jexec ${j}a nc --sctp -N -l 1234 &
+
+ # Wait for the server to start
+ sleep 1
+
+ out=$(jexec ${j}b nc --sctp -N -w 3 192.0.2.1 1234)
+ if [ "$out" != "foo" ]; then
+ atf_fail "SCTP connection failed"
+ fi
+
+ # Now with scrub rules present, so normalization is done
+ pft_set_rules ${j}a \
+ "scrub on ${j}a" \
+ "block" \
+ "pass in proto sctp to port 1234"
+
+ echo "foo" | jexec ${j}a nc --sctp -N -l 1234 &
+ sleep 1
+
+ out=$(jexec ${j}b nc --sctp -N -w 3 192.0.2.1 1234)
+ if [ "$out" != "foo" ]; then
+ atf_fail "SCTP connection failed"
+ fi
+
+ # Now fail with a blocked port
+ echo "foo" | jexec ${j}a nc --sctp -N -l 1235 &
+ sleep 1
+
+ out=$(jexec ${j}b nc --sctp -N -w 3 192.0.2.1 1235)
+ if [ "$out" == "foo" ]; then
+ atf_fail "SCTP port block failed"
+ fi
+
+ # Now fail with a blocked port but passing source port
+ out=$(jexec ${j}b nc --sctp -N -w 3 -p 1234 192.0.2.1 1235)
+ if [ "$out" == "foo" ]; then
+ atf_fail "SCTP port block failed"
+ fi
+}
+
+basic_v4_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "basic_v6" "cleanup"
+basic_v6_head()
+{
+ atf_set descr 'Basic SCTP connection over IPv6'
+ atf_set require.user root
+}
+
+basic_v6_body()
+{
+ sctp_init
+
+ j="sctp:basic_v6"
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail ${j}a ${epair}a
+ vnet_mkjail ${j}b ${epair}b
+
+ jexec ${j}a ifconfig ${epair}a inet6 2001:db8::a/64 up no_dad
+ jexec ${j}b ifconfig ${epair}b inet6 2001:db8::b/64 up no_dad
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ jexec ${j}a ping -6 -c 1 2001:db8::b
+
+ jexec ${j}a pfctl -e
+ pft_set_rules ${j}a \
+ "block proto sctp" \
+ "pass in proto sctp to port 1234"
+
+ echo "foo" | jexec ${j}a nc -6 --sctp -N -l 1234 &
+
+ # Wait for the server to start
+ sleep 1
+
+ out=$(jexec ${j}b nc --sctp -N -w 3 2001:db8::a 1234)
+ if [ "$out" != "foo" ]; then
+ atf_fail "SCTP connection failed"
+ fi
+
+ # Now with scrub rules present, so normalization is done
+ pft_set_rules ${j}a \
+ "scrub on ${j}a" \
+ "block proto sctp" \
+ "pass in proto sctp to port 1234"
+
+ echo "foo" | jexec ${j}a nc -6 --sctp -N -l 1234 &
+ sleep 1
+
+ out=$(jexec ${j}b nc --sctp -N -w 3 2001:db8::a 1234)
+ if [ "$out" != "foo" ]; then
+ atf_fail "SCTP connection failed"
+ fi
+
+ # Now fail with a blocked port
+ echo "foo" | jexec ${j}a nc -6 --sctp -N -l 1235 &
+ sleep 1
+
+ out=$(jexec ${j}b nc --sctp -N -w 3 2001:db8::a 1235)
+ if [ "$out" == "foo" ]; then
+ atf_fail "SCTP port block failed"
+ fi
+
+ # Now fail with a blocked port but passing source port
+ out=$(jexec ${j}b nc --sctp -N -w 3 -p 1234 2001:db8::a 1235)
+ if [ "$out" == "foo" ]; then
+ atf_fail "SCTP port block failed"
+ fi
+}
+
+basic_v6_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "reuse" "cleanup"
+reuse_head()
+{
+ atf_set descr 'Test handling dumb clients that reuse source ports'
+ atf_set require.user root
+}
+
+reuse_body()
+{
+ sctp_init
+
+ j="sctp:reuse"
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail ${j}a ${epair}a
+ vnet_mkjail ${j}b ${epair}b
+
+ jexec ${j}a ifconfig ${epair}a 192.0.2.1/24 up
+ jexec ${j}b ifconfig ${epair}b 192.0.2.2/24 up
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ jexec ${j}a ping -c 1 192.0.2.2
+
+ jexec ${j}a pfctl -e
+ pft_set_rules ${j}a \
+ "block" \
+ "pass in proto sctp to port 1234"
+
+ echo "foo" | jexec ${j}a nc --sctp -N -l 1234 &
+
+ # Wait for the server to start
+ sleep 1
+
+ out=$(jexec ${j}b nc --sctp -N -w 3 -p 1234 192.0.2.1 1234)
+ if [ "$out" != "foo" ]; then
+ atf_fail "SCTP connection failed"
+ fi
+
+ # Now do the same thing again, with the same port numbers
+ jexec ${j}a pfctl -ss -v
+
+ echo "foo" | jexec ${j}a nc --sctp -N -l 1234 &
+
+ # Wait for the server to start
+ sleep 1
+
+ out=$(jexec ${j}b nc --sctp -N -w 3 -p 1234 192.0.2.1 1234)
+ if [ "$out" != "foo" ]; then
+ atf_fail "SCTP connection failed"
+ fi
+ jexec ${j}a pfctl -ss -v
+}
+
+reuse_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "abort_v4" "cleanup"
+abort_v4_head()
+{
+ atf_set descr 'Test sending ABORT messages'
+ atf_set require.user root
+}
+
+abort_v4_body()
+{
+ sctp_init
+
+ j="sctp:abort_v4"
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail ${j}a ${epair}a
+ vnet_mkjail ${j}b ${epair}b
+
+ jexec ${j}a ifconfig ${epair}a 192.0.2.1/24 up
+ jexec ${j}b ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ jexec ${j}a ping -c 1 192.0.2.2
+
+ jexec ${j}a pfctl -e
+ pft_set_rules ${j}a \
+ "block return in proto sctp to port 1234"
+
+ echo "foo" | jexec ${j}a nc --sctp -N -l 1234 &
+
+ # Wait for the server to start
+ sleep 1
+
+ # If we get the abort we'll exit immediately, if we don't timeout will
+ # stop nc.
+ out=$(jexec ${j}b timeout 3 nc --sctp -N 192.0.2.1 1234)
+ if [ $? -eq 124 ]; then
+ atf_fail 'Abort not received'
+ fi
+ if [ "$out" == "foo" ]; then
+ atf_fail "block failed entirely"
+ fi
+
+ # Without 'return' we will time out.
+ pft_set_rules ${j}a \
+ "block in proto sctp to port 1234"
+
+ out=$(jexec ${j}b timeout 3 nc --sctp -N 192.0.2.1 1234)
+ if [ $? -ne 124 ]; then
+ atf_fail 'Abort sent anyway?'
+ fi
+}
+
+abort_v4_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "abort_v6" "cleanup"
+abort_v6_head()
+{
+ atf_set descr 'Test sending ABORT messages over IPv6'
+ atf_set require.user root
+}
+
+abort_v6_body()
+{
+ sctp_init
+
+ j="sctp:abort_v6"
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail ${j}a ${epair}a
+ vnet_mkjail ${j}b ${epair}b
+
+ jexec ${j}a ifconfig ${epair}a inet6 2001:db8::a/64 no_dad
+ jexec ${j}b ifconfig ${epair}b inet6 2001:db8::b/64 no_dad
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ jexec ${j}a ping -6 -c 1 2001:db8::b
+
+ jexec ${j}a pfctl -e
+ pft_set_rules ${j}a \
+ "block return in proto sctp to port 1234"
+
+ echo "foo" | jexec ${j}a nc -6 --sctp -N -l 1234 &
+
+ # Wait for the server to start
+ sleep 1
+
+ # If we get the abort we'll exit immediately, if we don't timeout will
+ # stop nc.
+ out=$(jexec ${j}b timeout 3 nc --sctp -N 2001:db8::a 1234)
+ if [ $? -eq 124 ]; then
+ atf_fail 'Abort not received'
+ fi
+ if [ "$out" == "foo" ]; then
+ atf_fail "block failed entirely"
+ fi
+
+ # Without 'return' we will time out.
+ pft_set_rules ${j}a \
+ "block in proto sctp to port 1234"
+
+ out=$(jexec ${j}b timeout 3 nc --sctp -N 2001:db8::a 1234)
+ if [ $? -ne 124 ]; then
+ atf_fail 'Abort sent anyway?'
+ fi
+}
+
+abort_v6_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nat_v4" "cleanup"
+nat_v4_head()
+{
+ atf_set descr 'Test NAT-ing SCTP over IPv4'
+ atf_set require.user root
+}
+
+nat_v4_body()
+{
+ sctp_init
+
+ j="sctp:nat_v4"
+ epair_c=$(vnet_mkepair)
+ epair_srv=$(vnet_mkepair)
+
+ vnet_mkjail ${j}srv ${epair_srv}a
+ vnet_mkjail ${j}gw ${epair_srv}b ${epair_c}a
+ vnet_mkjail ${j}c ${epair_c}b
+
+ jexec ${j}srv ifconfig ${epair_srv}a 198.51.100.1/24 up
+ # No default route in srv jail, to ensure we're NAT-ing
+ jexec ${j}gw ifconfig ${epair_srv}b 198.51.100.2/24 up
+ jexec ${j}gw ifconfig ${epair_c}a 192.0.2.1/24 up
+ jexec ${j}gw sysctl net.inet.ip.forwarding=1
+ jexec ${j}c ifconfig ${epair_c}b 192.0.2.2/24 up
+ jexec ${j}c route add default 192.0.2.1
+
+ jexec ${j}gw pfctl -e
+ pft_set_rules ${j}gw \
+ "nat on ${epair_srv}b from 192.0.2.0/24 -> (${epair_srv}b)" \
+ "pass"
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ jexec ${j}c ping -c 1 198.51.100.1
+
+ echo "foo" | jexec ${j}srv nc --sctp -N -l 1234 &
+
+ # Wait for the server to start
+ sleep 1
+
+ out=$(jexec ${j}c nc --sctp -N -w 3 198.51.100.1 1234)
+ if [ "$out" != "foo" ]; then
+ atf_fail "SCTP connection failed"
+ fi
+}
+
+nat_v4_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nat_v6" "cleanup"
+nat_v6_head()
+{
+ atf_set descr 'Test NAT-ing SCTP over IPv6'
+ atf_set require.user root
+}
+
+nat_v6_body()
+{
+ sctp_init
+
+ j="sctp:nat_v6"
+ epair_c=$(vnet_mkepair)
+ epair_srv=$(vnet_mkepair)
+
+ vnet_mkjail ${j}srv ${epair_srv}a
+ vnet_mkjail ${j}gw ${epair_srv}b ${epair_c}a
+ vnet_mkjail ${j}c ${epair_c}b
+
+ jexec ${j}srv ifconfig ${epair_srv}a inet6 2001:db8::1/64 up no_dad
+ # No default route in srv jail, to ensure we're NAT-ing
+ jexec ${j}gw ifconfig ${epair_srv}b inet6 2001:db8::2/64 up no_dad
+ jexec ${j}gw ifconfig ${epair_c}a inet6 2001:db8:1::1/64 up no_dad
+ jexec ${j}gw sysctl net.inet6.ip6.forwarding=1
+ jexec ${j}c ifconfig ${epair_c}b inet6 2001:db8:1::2/64 up no_dad
+ jexec ${j}c route add -6 default 2001:db8:1::1
+
+ jexec ${j}gw pfctl -e
+ pft_set_rules ${j}gw \
+ "nat on ${epair_srv}b from 2001:db8:1::/64 -> (${epair_srv}b)" \
+ "pass"
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ jexec ${j}c ping -6 -c 1 2001:db8::1
+
+ echo "foo" | jexec ${j}srv nc -6 --sctp -N -l 1234 &
+
+ # Wait for the server to start
+ sleep 1
+
+ out=$(jexec ${j}c nc --sctp -N -w 3 2001:db8::1 1234)
+ if [ "$out" != "foo" ]; then
+ atf_fail "SCTP connection failed"
+ fi
+}
+
+nat_v6_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "rdr_v4" "cleanup"
+rdr_v4_head()
+{
+ atf_set descr 'Test rdr SCTP over IPv4'
+ atf_set require.user root
+}
+
+rdr_v4_body()
+{
+ sctp_init
+
+ j="sctp:rdr_v4"
+ epair_c=$(vnet_mkepair)
+ epair_srv=$(vnet_mkepair)
+
+ vnet_mkjail ${j}srv ${epair_srv}a
+ vnet_mkjail ${j}gw ${epair_srv}b ${epair_c}a
+ vnet_mkjail ${j}c ${epair_c}b
+
+ jexec ${j}srv ifconfig ${epair_srv}a 198.51.100.1/24 up
+ # No default route in srv jail, to ensure we're NAT-ing
+ jexec ${j}gw ifconfig ${epair_srv}b 198.51.100.2/24 up
+ jexec ${j}gw ifconfig ${epair_c}a 192.0.2.1/24 up
+ jexec ${j}gw sysctl net.inet.ip.forwarding=1
+ jexec ${j}c ifconfig ${epair_c}b 192.0.2.2/24 up
+ jexec ${j}c route add default 192.0.2.1
+
+ jexec ${j}gw pfctl -e
+ pft_set_rules ${j}gw \
+ "rdr pass on ${epair_srv}b proto sctp from 198.51.100.0/24 to any port 1234 -> 192.0.2.2 port 1234" \
+ "pass"
+
+ echo "foo" | jexec ${j}c nc --sctp -N -l 1234 &
+
+ # Wait for the server to start
+ sleep 1
+
+ out=$(jexec ${j}srv nc --sctp -N -w 3 198.51.100.2 1234)
+ if [ "$out" != "foo" ]; then
+ atf_fail "SCTP connection failed"
+ fi
+
+ # Despite configuring port changes pf will not do so.
+ echo "bar" | jexec ${j}c nc --sctp -N -l 1234 &
+
+ pft_set_rules ${j}gw \
+ "rdr pass on ${epair_srv}b proto sctp from 198.51.100.0/24 to any port 1234 -> 192.0.2.2 port 4321" \
+ "pass"
+
+ # This will fail
+ out=$(jexec ${j}srv nc --sctp -N -w 3 198.51.100.2 4321)
+ if [ "$out" == "bar" ]; then
+ atf_fail "Port was unexpectedly changed."
+ fi
+
+ # This succeeds
+ out=$(jexec ${j}srv nc --sctp -N -w 3 198.51.100.2 1234)
+ if [ "$out" != "bar" ]; then
+ atf_fail "Port was unexpectedly changed."
+ fi
+}
+
+rdr_v4_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "pfsync" "cleanup"
+pfsync_head()
+{
+ atf_set descr 'Test pfsync-ing SCTP connections'
+ atf_set require.user root
+}
+
+pfsync_body()
+{
+ # + Builds bellow topology and initiate an SCTP connection
+ # from client to server.
+ # + Tests that the connection remains open when we fail over from
+ # router one to router two.
+ #
+ # ┌──────┐
+ # │client│
+ # └───┬──┘
+ # │
+ # ┌───┴───┐
+ # │bridge0│
+ # └┬─────┬┘
+ # │ │
+ # ┌────────────────┴─┐ ┌─┴────────────────┐
+ # │ one ├─┤ two │
+ # └────────────────┬─┘ └─┬────────────────┘
+ # │ │
+ # ┌┴─────┴┐
+ # │bridge1│
+ # └───┬───┘
+ # │
+ # ┌───┴──┐
+ # │server│
+ # └──────┘
+
+ sctp_init
+ pfsynct_init
+ vnet_init_bridge
+ if ! kldstat -q -m carp
+ then
+ atf_skip "This test requires carp"
+ fi
+
+ j="sctp:pfsync"
+
+ tmp=`pwd`
+
+ bridge0=$(vnet_mkbridge)
+ bridge1=$(vnet_mkbridge)
+
+ epair_c=$(vnet_mkepair)
+ epair_one0=$(vnet_mkepair)
+ epair_two0=$(vnet_mkepair)
+ epair_sync=$(vnet_mkepair)
+ epair_one1=$(vnet_mkepair)
+ epair_two1=$(vnet_mkepair)
+ epair_srv=$(vnet_mkepair)
+
+ ifconfig ${bridge0} addm ${epair_c}a addm ${epair_one0}a addm ${epair_two0}a
+ ifconfig ${epair_one0}a up
+ ifconfig ${epair_two0}a up
+ ifconfig ${epair_c}a up
+ ifconfig ${bridge0} up
+
+ ifconfig ${bridge1} addm ${epair_srv}a addm ${epair_one1}a addm ${epair_two1}a
+ ifconfig ${epair_one1}a up
+ ifconfig ${epair_two1}a up
+ ifconfig ${epair_srv}a up
+ ifconfig ${bridge1} up
+
+ vnet_mkjail ${j}c ${epair_c}b
+ jexec ${j}c ifconfig ${epair_c}b 192.0.2.2/24 up
+ jexec ${j}c route add default 192.0.2.1
+
+ vnet_mkjail ${j}one ${epair_one0}b ${epair_one1}b ${epair_sync}a
+ jexec ${j}one ifconfig ${epair_one0}b 192.0.2.3/24 up
+ jexec ${j}one ifconfig ${epair_one0}b \
+ alias 192.0.2.1/32 vhid 1 pass 1234
+ jexec ${j}one ifconfig ${epair_one1}b 198.51.100.3/24 up
+ jexec ${j}one ifconfig ${epair_one1}b \
+ alias 198.51.100.2/32 vhid 2 pass 4321
+ jexec ${j}one ifconfig ${epair_sync}a 203.0.113.1/24 up
+ jexec ${j}one ifconfig pfsync0 \
+ syncdev ${epair_sync}a \
+ maxupd 1 \
+ up
+ jexec ${j}one sysctl net.inet.ip.forwarding=1
+
+ vnet_mkjail ${j}two ${epair_two0}b ${epair_two1}b ${epair_sync}b
+ jexec ${j}two ifconfig ${epair_two0}b 192.0.2.4/24 up
+ jexec ${j}two ifconfig ${epair_two0}b \
+ alias 192.0.2.1/32 vhid 1 pass 1234
+ jexec ${j}two ifconfig ${epair_two1}b 198.51.100.4/24 up
+ jexec ${j}two ifconfig ${epair_two1}b \
+ alias 198.51.100.2/32 vhid 2 pass 4321
+ jexec ${j}two ifconfig ${epair_sync}b 203.0.113.2/24 up
+ jexec ${j}two ifconfig pfsync0 \
+ syncdev ${epair_sync}b \
+ maxupd 1 \
+ up
+ jexec ${j}two sysctl net.inet.ip.forwarding=1
+
+ vnet_mkjail ${j}srv ${epair_srv}b
+ jexec ${j}srv ifconfig ${epair_srv}b 198.51.100.1/24 up
+ jexec ${j}srv route add default 198.51.100.2
+
+ # Demote two, to avoid dealing with asymmetric routing
+ jexec ${j}two sysctl net.inet.carp.demotion=50
+
+ jexec ${j}one pfctl -e
+ pft_set_rules ${j}one \
+ "block all" \
+ "pass proto { icmp, pfsync, carp }" \
+ "pass proto sctp to port 1234" \
+ "pass proto tcp to port 1234"
+
+ jexec ${j}two pfctl -e
+ pft_set_rules ${j}two \
+ "block all" \
+ "pass proto { icmp, pfsync, carp }" \
+ "pass proto sctp to port 1234" \
+ "pass proto tcp to port 1234"
+
+ # Give carp time to get set up
+ sleep 2
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ jexec ${j}c ping -c 1 198.51.100.1
+
+ # Now start up an SCTP connection
+ touch ${tmp}/input
+ tail -F ${tmp}/input | jexec ${j}srv nc --sctp -l 1234 &
+ sleep 1
+
+ jexec ${j}c nc --sctp 198.51.100.1 1234 > ${tmp}/output &
+ echo "1" >> ${tmp}/input
+
+ # Give time for the traffic to arrive
+ sleep 1
+ line=$(tail -n -1 ${tmp}/output)
+ if [ "${line}" != "1" ];
+ then
+ echo "Found ${line}"
+ cat ${tmp}/output
+ atf_fail "Initial SCTP connection failed"
+ fi
+
+ # Verify that two has the connection too
+ state=$(jexec ${j}two pfctl -ss | grep sctp)
+ if [ -z "${state}" ];
+ then
+ jexec ${j}two pfctl -ss
+ atf_fail "Failed to find SCTP state on secondary pfsync host"
+ fi
+
+ # Now fail over (both carp IPs should switch here)
+ jexec ${j}one sysctl net.inet.carp.demotion=100
+
+ while ! jexec ${j}one ifconfig ${epair_one0}b | grep MASTER;
+ do
+ sleep 1
+ done
+ while ! jexec ${j}one ifconfig ${epair_one1}b | grep MASTER;
+ do
+ sleep 1
+ done
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ jexec ${j}c ping -c 1 198.51.100.1
+
+ # And check that the connection is still live
+ echo "2" >> ${tmp}/input
+ sleep 1
+ line=$(tail -n -1 ${tmp}/output)
+ if [ "${line}" != "2" ];
+ then
+ echo "Found ${line}"
+ cat ${tmp}/output
+ atf_fail "SCTP failover failed"
+ fi
+}
+
+pfsync_cleanup()
+{
+ pfsynct_cleanup
+}
+
+atf_test_case "timeout" "cleanup"
+timeout_head()
+{
+ atf_set descr 'Test setting and retrieving timeout values'
+ atf_set require.user root
+}
+
+timeout_body()
+{
+ sctp_init
+
+ vnet_mkjail timeout
+
+ pft_set_rules timeout \
+ "set timeout sctp.first 13" \
+ "set timeout sctp.opening 14"
+
+ atf_check -s exit:0 -o match:"sctp.first.*13" \
+ jexec timeout pfctl -st
+ atf_check -s exit:0 -o match:"sctp.opening.*14" \
+ jexec timeout pfctl -st
+ # We've not changed other timeouts
+ atf_check -s exit:0 -o match:"sctp.established.*86400" \
+ jexec timeout pfctl -st
+}
+
+timeout_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "related_icmp" "cleanup"
+related_icmp_head()
+{
+ atf_set descr 'Verify that ICMP messages related to an SCTP connection are allowed'
+ atf_set require.user root
+}
+
+related_icmp_body()
+{
+ sctp_init
+
+ epair_cl=$(vnet_mkepair)
+ epair_rtr=$(vnet_mkepair)
+ epair_srv=$(vnet_mkepair)
+
+ ifconfig ${epair_cl}a 192.0.2.1/24 up
+ route add default 192.0.2.2
+
+ vnet_mkjail rtr ${epair_cl}b ${epair_rtr}a
+ jexec rtr ifconfig ${epair_cl}b 192.0.2.2/24 up
+ jexec rtr ifconfig ${epair_rtr}a 198.51.100.1/24 up
+ jexec rtr sysctl net.inet.ip.forwarding=1
+ jexec rtr route add default 198.51.100.2
+
+ vnet_mkjail rtr2 ${epair_rtr}b ${epair_srv}a
+ jexec rtr2 ifconfig ${epair_rtr}b 198.51.100.2/24 up
+ jexec rtr2 ifconfig ${epair_srv}a 203.0.113.1/24 up
+ jexec rtr2 ifconfig ${epair_srv}a mtu 1300
+ jexec rtr2 sysctl net.inet.ip.forwarding=1
+ jexec rtr2 route add default 198.51.100.1
+
+ vnet_mkjail srv ${epair_srv}b
+ jexec srv ifconfig ${epair_srv}b 203.0.113.2/24 up
+ jexec srv ifconfig ${epair_srv}b mtu 1300
+ jexec srv route add default 203.0.113.1
+
+ # Sanity checks
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.2
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 198.51.100.1
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 198.51.100.2
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 203.0.113.1
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 203.0.113.2
+
+ jexec rtr pfctl -e
+ pft_set_rules rtr \
+ "block proto icmp" \
+ "pass proto sctp"
+
+ # Make sure SCTP traffic passes
+ echo "foo" | jexec srv nc --sctp -N -l 1234 &
+ sleep 1
+
+ out=$(nc --sctp -N -w 3 203.0.113.2 1234)
+ if [ "$out" != "foo" ]; then
+ jexec rtr pfctl -ss -vv
+ jexec rtr pfctl -sr -vv
+ atf_fail "SCTP connection failed"
+ fi
+
+ # Do we see ICMP traffic if we send overly large traffic?
+ echo "foo" | jexec srv nc --sctp -l 1234 >/dev/null &
+ sleep 1
+
+ atf_check -s exit:0 -o not-match:".*destination unreachable:.*" \
+ netstat -s -p icmp
+
+ # Generate traffic that will be fragmented by rtr2, and will provoke an
+ # ICMP unreachable - need to frag (mtu 1300) message
+ dd if=/dev/random bs=10000 count=1 | nc --sctp -N -w 3 203.0.113.2 1234
+
+ # We'd expect to see an ICMP message
+ atf_check -s exit:0 -o match:".*destination unreachable: [1-9]" \
+ netstat -s -p icmp
+}
+
+related_icmp_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "basic_v4"
+ atf_add_test_case "basic_v6"
+ atf_add_test_case "reuse"
+ atf_add_test_case "abort_v4"
+ atf_add_test_case "abort_v6"
+ atf_add_test_case "nat_v4"
+ atf_add_test_case "nat_v6"
+ atf_add_test_case "rdr_v4"
+ atf_add_test_case "pfsync"
+ atf_add_test_case "timeout"
+ atf_add_test_case "related_icmp"
+}
diff --git a/tests/sys/netpfil/pf/set_skip.sh b/tests/sys/netpfil/pf/set_skip.sh
new file mode 100644
index 000000000000..e984377721b8
--- /dev/null
+++ b/tests/sys/netpfil/pf/set_skip.sh
@@ -0,0 +1,231 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2018 Kristof Provost <kp@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "unset" "cleanup"
+unset_head()
+{
+ atf_set descr 'Unset set skip test'
+ atf_set require.user root
+}
+
+unset_body()
+{
+ pft_init
+
+ vnet_mkjail alcatraz
+ jexec alcatraz ifconfig lo0 127.0.0.1/8 up
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz "set skip on lo0" \
+ "block in proto icmp"
+
+ echo "set skip"
+ jexec alcatraz pfctl -v -sI
+
+ jexec alcatraz ifconfig
+ atf_check -s exit:0 -o ignore jexec alcatraz ping -c 1 127.0.0.1
+
+ # Unset the skip on the group
+ pft_set_rules noflush alcatraz \
+ "block in proto icmp"
+
+ echo "No setskip"
+ jexec alcatraz pfctl -v -sI
+
+ # Do flush states
+ jexec alcatraz pfctl -Fs
+
+ # And now our ping is blocked
+ atf_check -s exit:2 -o ignore jexec alcatraz ping -c 1 127.0.0.1
+
+ jexec alcatraz pfctl -v -sI
+}
+
+unset_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "set_skip_group" "cleanup"
+set_skip_group_head()
+{
+ atf_set descr 'Basic set skip test'
+ atf_set require.user root
+}
+
+set_skip_group_body()
+{
+ # See PR 229241
+ pft_init
+
+ vnet_mkjail alcatraz
+ jexec alcatraz ifconfig lo0 127.0.0.1/8 up
+ jexec alcatraz ifconfig lo0 group foo
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz "set skip on foo" \
+ "block in proto icmp"
+
+ echo "set skip"
+ jexec alcatraz pfctl -v -sI
+
+ jexec alcatraz ifconfig
+ atf_check -s exit:0 -o ignore jexec alcatraz ping -c 1 127.0.0.1
+
+ # Unset the skip on the group
+ pft_set_rules noflush alcatraz \
+ "block in proto icmp"
+
+ # Do flush states
+ jexec alcatraz pfctl -Fs
+
+ # And now our ping is blocked
+ atf_check -s exit:2 -o ignore jexec alcatraz ping -c 1 127.0.0.1
+
+ echo "No setskip"
+ jexec alcatraz pfctl -v -sI
+}
+
+set_skip_group_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "set_skip_group_lo" "cleanup"
+set_skip_group_lo_head()
+{
+ atf_set descr 'Basic set skip test, lo'
+ atf_set require.user root
+}
+
+set_skip_group_lo_body()
+{
+ # See PR 229241
+ pft_init
+
+ vnet_mkjail alcatraz
+ jexec alcatraz ifconfig lo0 127.0.0.1/8 up
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz "set skip on lo" \
+ "block on lo0"
+
+ atf_check -s exit:0 -o ignore jexec alcatraz ping -c 1 127.0.0.1
+ pft_set_rules noflush alcatraz "set skip on lo" \
+ "block on lo0"
+ atf_check -s exit:0 -o ignore jexec alcatraz ping -c 1 127.0.0.1
+ jexec alcatraz pfctl -s rules
+}
+
+set_skip_group_lo_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "set_skip_dynamic" "cleanup"
+set_skip_dynamic_head()
+{
+ atf_set descr "Cope with group changes"
+ atf_set require.user root
+}
+
+set_skip_dynamic_body()
+{
+ pft_init
+
+ set -x
+
+ vnet_mkjail alcatraz
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz "set skip on epair" \
+ "block on ! lo"
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a 192.0.2.2/24 up
+ vnet_ifmove ${epair}b alcatraz
+
+ jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up
+
+ atf_check -s exit:0 -o ignore jexec alcatraz ping -c 1 192.0.2.2
+}
+
+set_skip_dynamic_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "pr255852" "cleanup"
+pr255852_head()
+{
+ atf_set descr "PR 255852"
+ atf_set require.user root
+}
+
+pr255852_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+
+ ifconfig ${epair}a 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig lo0 127.0.0.1/8 up
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz "set skip on { lo0, epair }" \
+ "block"
+ jexec alcatraz pfctl -vsI
+
+ # We're skipping on epair, so this should work
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ # Note: flushing avoid the issue
+ pft_set_rules noflush alcatraz "set skip on { lo0 }" \
+ "block"
+
+ jexec alcatraz pfctl -vsI
+
+ # No longer skipping, so this should fail
+ atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.2
+}
+
+pr255852_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "unset"
+ atf_add_test_case "set_skip_group"
+ atf_add_test_case "set_skip_group_lo"
+ atf_add_test_case "set_skip_dynamic"
+ atf_add_test_case "pr255852"
+}
diff --git a/tests/sys/netpfil/pf/set_tos.sh b/tests/sys/netpfil/pf/set_tos.sh
new file mode 100644
index 000000000000..842377ee97c6
--- /dev/null
+++ b/tests/sys/netpfil/pf/set_tos.sh
@@ -0,0 +1,217 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2017 Kristof Provost <kp@FreeBSD.org>
+#
+# Copyright (c) 2021 Samuel Robinette
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+common_dir=$(atf_get_srcdir)/../common
+
+atf_test_case "v4" "cleanup"
+v4_head()
+{
+ atf_set descr 'set-tos test'
+ atf_set require.user root
+
+ # We need scapy to be installed for out test scripts to work
+ atf_set require.progs python3 scapy
+}
+
+v4_body()
+{
+ pft_init
+
+ epair_send=$(vnet_mkepair)
+ ifconfig ${epair_send}a 192.0.2.1/24 up
+
+ epair_recv=$(vnet_mkepair)
+ ifconfig ${epair_recv}a up
+
+ vnet_mkjail alcatraz ${epair_send}b ${epair_recv}b
+ jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up
+ jexec alcatraz ifconfig ${epair_recv}b 198.51.100.2/24 up
+ jexec alcatraz sysctl net.inet.ip.forwarding=1
+ jexec alcatraz arp -s 198.51.100.3 00:01:02:03:04:05
+ route add -net 198.51.100.0/24 192.0.2.2
+
+ jexec alcatraz pfctl -e
+
+ # No change is done if not requested
+ pft_set_rules alcatraz "scrub out proto icmp"
+ atf_check -s exit:1 -o ignore ${common_dir}/pft_ping.py \
+ --sendif ${epair_send}a \
+ --to 198.51.100.3 \
+ --recvif ${epair_recv}a \
+ --expect-tc 42
+
+ # The requested ToS is set
+ pft_set_rules alcatraz "scrub out proto icmp set-tos 42"
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_send}a \
+ --to 198.51.100.3 \
+ --recvif ${epair_recv}a \
+ --expect-tc 42
+
+ # ToS is not changed if the scrub rule does not match
+ pft_set_rules alcatraz "scrub out proto tcp set-tos 42"
+ atf_check -s exit:1 -o ignore ${common_dir}/pft_ping.py \
+ --sendif ${epair_send}a \
+ --to 198.51.100.3 \
+ --recvif ${epair_recv}a \
+ --expect-tc 42
+
+ # Multiple scrub rules match as expected
+ pft_set_rules alcatraz "scrub out proto tcp set-tos 13" \
+ "scrub out proto icmp set-tos 14"
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_send}a \
+ --to 198.51.100.3 \
+ --recvif ${epair_recv}a \
+ --expect-tc 14
+
+ # And this works even if the packet already has ToS values set
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_send}a \
+ --to 198.51.100.3 \
+ --recvif ${epair_recv}a \
+ --send-tc 42 \
+ --expect-tc 14
+
+ # ToS values are unmolested if the packets do not match a scrub rule
+ pft_set_rules alcatraz "scrub out proto tcp set-tos 13"
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_send}a \
+ --to 198.51.100.3 \
+ --recvif ${epair_recv}a \
+ --send-tc 42 \
+ --expect-tc 42
+}
+
+v4_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "v6" "cleanup"
+v6_head()
+{
+ atf_set descr 'set-tos6 test'
+ atf_set require.user root
+
+ # We need scapy to be installed for out test scripts to work
+ atf_set require.progs python3 scapy
+}
+
+v6_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a inet6 add 2001:db8:192::1
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b inet6 add 2001:db8:192::2
+
+ route -6 add 2001:db8:192::2 2001:db8:192::1
+ jexec alcatraz route -6 add 2001:db8:192::1 2001:db8:192::2
+
+ jexec alcatraz pfctl -e
+
+ # No change is done if not requested
+ pft_set_rules alcatraz "scrub out proto ipv6-icmp"
+ atf_check -s exit:1 -o ignore -e ignore ${common_dir}/pft_ping.py \
+ --sendif ${epair}a \
+ --to 2001:db8:192::2 \
+ --replyif ${epair}a \
+ --expect-tc 42
+
+ # The requested ToS is set
+ pft_set_rules alcatraz "scrub out proto ipv6-icmp set-tos 42"
+ atf_check -s exit:0 -o ignore -e ignore ${common_dir}/pft_ping.py \
+ --sendif ${epair}a \
+ --to 2001:db8:192::2 \
+ --replyif ${epair}a \
+ --expect-tc 42
+
+ # ToS is not changed if the scrub rule does not match
+ pft_set_rules alcatraz "scrub out from 2001:db8:192::3 set-tos 42"
+ atf_check -s exit:1 -o ignore -e ignore ${common_dir}/pft_ping.py \
+ --sendif ${epair}a \
+ --to 2001:db8:192::2 \
+ --replyif ${epair}a \
+ --expect-tc 42
+
+ # Multiple scrub rules match as expected
+ pft_set_rules alcatraz "scrub out from 2001:db8:192::3 set-tos 13" \
+ "scrub out proto ipv6-icmp set-tos 14"
+ atf_check -s exit:0 -o ignore -e ignore ${common_dir}/pft_ping.py \
+ --sendif ${epair}a \
+ --to 2001:db8:192::2 \
+ --replyif ${epair}a \
+ --expect-tc 14
+
+ # And this works even if the packet already has ToS values set
+ atf_check -s exit:0 -o ignore -e ignore ${common_dir}/pft_ping.py \
+ --sendif ${epair}a \
+ --to 2001:db8:192::2 \
+ --replyif ${epair}a \
+ --send-tc 42 \
+ --expect-tc 14
+
+ # ToS values are unmolested if the packets do not match a scrub rule
+ pft_set_rules alcatraz "scrub out from 2001:db8:192::3 set-tos 13"
+ atf_check -s exit:0 -o ignore -e ignore ${common_dir}/pft_ping.py \
+ --sendif ${epair}a \
+ --to 2001:db8:192::2 \
+ --replyif ${epair}a \
+ --expect-tc 0
+
+ # We can set tos on pass rules
+ pft_set_rules alcatraz "pass out set tos 13"
+ atf_check -s exit:0 -o ignore -e ignore ${common_dir}/pft_ping.py \
+ --sendif ${epair}a \
+ --to 2001:db8:192::2 \
+ --replyif ${epair}a \
+ --expect-tc 13
+
+ # And that still works with 'scrub' options too
+ pft_set_rules alcatraz "pass out set tos 14 scrub (min-ttl 64)"
+ atf_check -s exit:0 -o ignore -e ignore ${common_dir}/pft_ping.py \
+ --sendif ${epair}a \
+ --to 2001:db8:192::2 \
+ --replyif ${epair}a \
+ --expect-tc 14
+}
+
+v6_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "v4"
+ atf_add_test_case "v6"
+}
diff --git a/tests/sys/netpfil/pf/snmp.sh b/tests/sys/netpfil/pf/snmp.sh
new file mode 100644
index 000000000000..37cc4b75cf92
--- /dev/null
+++ b/tests/sys/netpfil/pf/snmp.sh
@@ -0,0 +1,123 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2025 Kristof Provost <kp@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "basic" "cleanup"
+basic_head()
+{
+ atf_set descr 'Basic pf_snmp test'
+ atf_set require.user root
+}
+
+basic_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+
+ ifconfig ${epair}b 192.0.2.2/24 up
+
+ vnet_mkjail alcatraz ${epair}a
+ jexec alcatraz ifconfig ${epair}a 192.0.2.1/24 up
+
+ # Start bsnmpd
+ jexec alcatraz bsnmpd -c $(atf_get_srcdir)/bsnmpd.conf
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "pass"
+
+ # Sanity check, and create state
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.1
+
+ # pf should be enabled
+ atf_check -s exit:0 -o match:'pfStatusRunning.0 = true' \
+ bsnmpwalk -s public@192.0.2.1 -i pf_tree.def begemot
+}
+
+basic_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "table" "cleanup"
+table_head()
+{
+ atf_set descr 'Test tables and pf_snmp'
+ atf_set require.user root
+}
+
+table_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+
+ ifconfig ${epair}b 192.0.2.2/24 up
+
+ vnet_mkjail alcatraz ${epair}a
+ jexec alcatraz ifconfig ${epair}a 192.0.2.1/24 up
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "table <foo> counters { 192.0.2.0/24 }" \
+ "pass in from <foo>"
+
+ # Start bsnmpd after creating the table so we don't have to wait for
+ # a refresh timeout
+ jexec alcatraz bsnmpd -c $(atf_get_srcdir)/bsnmpd.conf
+
+ # Sanity check, and create state
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 192.0.2.1
+
+ # We should have one table
+ atf_check -s exit:0 -o match:'pfTablesTblNumber.0 = 1' \
+ bsnmpwalk -s public@192.0.2.1 -i pf_tree.def begemot
+
+ # We have the 'foo' table
+ atf_check -s exit:0 -o match:'pfTablesTblDescr.* = foo' \
+ bsnmpwalk -s public@192.0.2.1 -i pf_tree.def pfTables
+
+ # Which contains address 192.0.2.0/24
+ atf_check -s exit:0 -o match:'pfTablesAddrNet.* = 192.0.2.0' \
+ bsnmpwalk -s public@192.0.2.1 -i pf_tree.def pfTables
+ atf_check -s exit:0 -o match:'pfTablesAddrPrefix.* = 24' \
+ bsnmpwalk -s public@192.0.2.1 -i pf_tree.def pfTables
+}
+
+table_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "basic"
+ atf_add_test_case "table"
+}
diff --git a/tests/sys/netpfil/pf/src_track.sh b/tests/sys/netpfil/pf/src_track.sh
new file mode 100755
index 000000000000..c24f88062c4d
--- /dev/null
+++ b/tests/sys/netpfil/pf/src_track.sh
@@ -0,0 +1,515 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Kristof Provost <kp@FreeBSD.org>
+# Copyright (c) 2024 Kajetan Staszkiewicz <vegeta@tuxpowered.net>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "source_track" "cleanup"
+source_track_head()
+{
+ atf_set descr 'Basic source tracking test'
+ atf_set require.user root
+}
+
+source_track_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair}b
+
+ ifconfig ${epair}a 192.0.2.2/24 up
+ jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up
+
+ # Enable pf!
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "pass in keep state (source-track)" \
+ "pass out keep state (source-track)"
+
+ ping -c 3 192.0.2.1
+ atf_check -s exit:0 -o match:'192.0.2.2 -> 0.0.0.0 \( states 1,.*' \
+ jexec alcatraz pfctl -sS
+
+ # Flush all source nodes
+ jexec alcatraz pfctl -FS
+
+ # We can't find the previous source node any more
+ atf_check -s exit:0 -o not-match:'192.0.2.2 -> 0.0.0.0 \( states 1,.*' \
+ jexec alcatraz pfctl -sS
+
+ # But we still have the state
+ atf_check -s exit:0 -o match:'all icmp 192.0.2.1:8 <- 192.0.2.2:.*' \
+ jexec alcatraz pfctl -ss
+}
+
+source_track_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "kill" "cleanup"
+kill_head()
+{
+ atf_set descr 'Test killing source nodes'
+ atf_set require.user root
+}
+
+kill_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ vnet_mkjail alcatraz ${epair}b
+
+ ifconfig ${epair}a 192.0.2.2/24 up
+ ifconfig ${epair}a inet alias 192.0.2.3/24 up
+ jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up
+
+ # Enable pf!
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "pass in keep state (source-track)" \
+ "pass out keep state (source-track)"
+
+ # Establish two sources
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 -S 192.0.2.2 192.0.2.1
+ atf_check -s exit:0 -o ignore \
+ ping -c 1 -S 192.0.2.3 192.0.2.1
+
+ # Check that both source nodes exist
+ atf_check -s exit:0 -o match:'192.0.2.2 -> 0.0.0.0 \( states 1,.*' \
+ jexec alcatraz pfctl -sS
+ atf_check -s exit:0 -o match:'192.0.2.3 -> 0.0.0.0 \( states 1,.*' \
+ jexec alcatraz pfctl -sS
+
+
+jexec alcatraz pfctl -sS
+
+ # Kill the 192.0.2.2 source
+ jexec alcatraz pfctl -K 192.0.2.2
+
+ # The other source still exists
+ atf_check -s exit:0 -o match:'192.0.2.3 -> 0.0.0.0 \( states 1,.*' \
+ jexec alcatraz pfctl -sS
+
+ # But not the one we killed
+ atf_check -s exit:0 -o not-match:'192.0.2.2 -> 0.0.0.0 \( states 1,.*' \
+ jexec alcatraz pfctl -sS
+}
+
+kill_cleanup()
+{
+ pft_cleanup
+}
+
+max_src_conn_rule_head()
+{
+ atf_set descr 'Max connections per source per rule'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+max_src_conn_rule_body()
+{
+ setup_router_server_ipv6
+
+ # Clients will connect from another network behind the router.
+ # This allows for using multiple source addresses and for tester jail
+ # to not respond with RST packets for SYN+ACKs.
+ jexec router route add -6 2001:db8:44::0/64 2001:db8:42::2
+ jexec server route add -6 2001:db8:44::0/64 2001:db8:43::1
+
+ pft_set_rules router \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in on ${epair_tester}b inet6 proto tcp keep state (max-src-conn 3 source-track rule overload <bad_hosts>)" \
+ "pass out on ${epair_server}a inet6 proto tcp keep state"
+
+ # Limiting of connections is done for connections which have successfully
+ # finished the 3-way handshake. Once the handshake is done, the state
+ # is moved to CLOSED state. We use pft_ping.py to check that the handshake
+ # was really successful and after that we check what is in pf state table.
+
+ # 3 connections from host ::1 will be allowed.
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4201 --fromaddr 2001:db8:44::1
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4202 --fromaddr 2001:db8:44::1
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4203 --fromaddr 2001:db8:44::1
+ # The 4th connection from host ::1 will have its state killed.
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4204 --fromaddr 2001:db8:44::1
+ # A connection from host :2 is will be allowed.
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4205 --fromaddr 2001:db8:44::2
+
+ states=$(mktemp) || exit 1
+ jexec router pfctl -qss | normalize_pfctl_s | grep 'tcp 2001:db8:43::2\[9\] <-' > $states
+
+ grep -qE '2001:db8:44::1\[4201\] ESTABLISHED:ESTABLISHED' $states || atf_fail "State for port 4201 not found or not established"
+ grep -qE '2001:db8:44::1\[4202\] ESTABLISHED:ESTABLISHED' $states || atf_fail "State for port 4202 not found or not established"
+ grep -qE '2001:db8:44::1\[4203\] ESTABLISHED:ESTABLISHED' $states || atf_fail "State for port 4203 not found or not established"
+ grep -qE '2001:db8:44::2\[4205\] ESTABLISHED:ESTABLISHED' $states || atf_fail "State for port 4205 not found or not established"
+
+ if (
+ grep -qE '2001:db8:44::1\[4204\] ' $states &&
+ ! grep -qE '2001:db8:44::1\[4204\] CLOSED:CLOSED' $states
+ ); then
+ atf_fail "State for port 4204 found but not closed"
+ fi
+
+ jexec router pfctl -T test -t bad_hosts 2001:db8:44::1 || atf_fail "Host not found in overload table"
+}
+
+max_src_conn_rule_cleanup()
+{
+ pft_cleanup
+}
+
+max_src_states_rule_head()
+{
+ atf_set descr 'Max states per source per rule'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+max_src_states_rule_body()
+{
+ setup_router_server_ipv6
+
+ # Clients will connect from another network behind the router.
+ # This allows for using multiple source addresses and for tester jail
+ # to not respond with RST packets for SYN+ACKs.
+ jexec router route add -6 2001:db8:44::0/64 2001:db8:42::2
+ jexec server route add -6 2001:db8:44::0/64 2001:db8:43::1
+
+ pft_set_rules router \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in on ${epair_tester}b inet6 proto tcp from port 4210:4219 keep state (max-src-states 3 source-track rule) label rule_A" \
+ "pass in on ${epair_tester}b inet6 proto tcp from port 4220:4229 keep state (max-src-states 3 source-track rule) label rule_B" \
+ "pass out on ${epair_server}a keep state"
+
+ # The option max-src-states prevents even the initial SYN packet going
+ # through. It's enough that we check ping_server_check_reply, no need to
+ # bother checking created states.
+
+ # 2 connections from host ::1 matching rule_A will be allowed, 1 will fail to create a state.
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4211 --fromaddr 2001:db8:44::1
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4212 --fromaddr 2001:db8:44::1
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4213 --fromaddr 2001:db8:44::1
+ ping_server_check_reply exit:1 --ping-type=tcp3way --send-sport=4214 --fromaddr 2001:db8:44::1
+
+ # 2 connections from host ::1 matching rule_B will be allowed, 1 will fail to create a state.
+ # Limits from rule_A don't interfere with rule_B.
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4221 --fromaddr 2001:db8:44::1
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4222 --fromaddr 2001:db8:44::1
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4223 --fromaddr 2001:db8:44::1
+ ping_server_check_reply exit:1 --ping-type=tcp3way --send-sport=4224 --fromaddr 2001:db8:44::1
+
+ # 2 connections from host ::2 matching rule_B will be allowed, 1 will fail to create a state.
+ # Limits for host ::1 will not interfere with host ::2.
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4224 --fromaddr 2001:db8:44::2
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4225 --fromaddr 2001:db8:44::2
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4226 --fromaddr 2001:db8:44::2
+ ping_server_check_reply exit:1 --ping-type=tcp3way --send-sport=4227 --fromaddr 2001:db8:44::2
+
+ # We will check the resulting source nodes, though.
+ # Order of source nodes in output is not guaranteed, find each one separately.
+ nodes=$(mktemp) || exit 1
+ jexec router pfctl -qvsS | normalize_pfctl_s > $nodes
+ for node_regexp in \
+ '2001:db8:44::1 -> :: \( states 3, connections 3, rate [0-9/\.]+s \) age [0-9:]+, 9 pkts, [0-9]+ bytes, filter rule 3, limit source-track$' \
+ '2001:db8:44::1 -> :: \( states 3, connections 3, rate [0-9/\.]+s \) age [0-9:]+, 9 pkts, [0-9]+ bytes, filter rule 4, limit source-track$' \
+ '2001:db8:44::2 -> :: \( states 3, connections 3, rate [0-9/\.]+s \) age [0-9:]+, 9 pkts, [0-9]+ bytes, filter rule 4, limit source-track$' \
+ ; do
+ grep -qE "${node_regexp}" $nodes || atf_fail "Source node not found for '${node_regexp}'"
+ done
+
+ # Check if limit counters have been properly set.
+ jexec router pfctl -qvvsi | grep -qE 'max-src-states\s+3\s+' || atf_fail "max-src-states not set to 3"
+}
+
+max_src_states_rule_cleanup()
+{
+ pft_cleanup
+}
+
+max_src_states_global_head()
+{
+ atf_set descr 'Max states per source global'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+max_src_states_global_body()
+{
+ setup_router_server_ipv6
+
+ # Clients will connect from another network behind the router.
+ # This allows for using multiple source addresses and for tester jail
+ # to not respond with RST packets for SYN+ACKs.
+ jexec router route add -6 2001:db8:44::0/64 2001:db8:42::2
+ jexec server route add -6 2001:db8:44::0/64 2001:db8:43::1
+
+ pft_set_rules router \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in on ${epair_tester}b inet6 proto tcp from port 4210:4219 keep state (max-src-states 3 source-track global) label rule_A" \
+ "pass in on ${epair_tester}b inet6 proto tcp from port 4220:4229 keep state (max-src-states 3 source-track global) label rule_B" \
+ "pass out on ${epair_server}a keep state"
+
+ # Global source tracking creates a single source node shared between all
+ # rules for each connecting source IP address and counts states created
+ # by all rules. Each rule has its own max-src-conn value checked against
+ # that single source node.
+
+ # 3 connections from host …::1 matching rule_A will be allowed.
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4211 --fromaddr 2001:db8:44::1
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4212 --fromaddr 2001:db8:44::1
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4213 --fromaddr 2001:db8:44::1
+ # The 4th connection matching rule_A from host …::1 will have its state killed.
+ ping_server_check_reply exit:1 --ping-type=tcp3way --send-sport=4214 --fromaddr 2001:db8:44::1
+ # A connection matching rule_B from host …::1 will have its state killed too.
+ ping_server_check_reply exit:1 --ping-type=tcp3way --send-sport=4221 --fromaddr 2001:db8:44::1
+
+ nodes=$(mktemp) || exit 1
+ jexec router pfctl -qvsS | normalize_pfctl_s > $nodes
+ cat $nodes
+ node_regexp='2001:db8:44::1 -> :: \( states 3, connections 3, rate [0-9/\.]+s \) age [0-9:]+, 9 pkts, [0-9]+ bytes, limit source-track'
+ grep -qE "$node_regexp" $nodes || atf_fail "Source nodes not matching expected output"
+}
+
+max_src_states_global_cleanup()
+{
+ pft_cleanup
+}
+
+sn_types_compat_head()
+{
+ atf_set descr 'Combination of source node types with compat NAT rules'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+sn_types_compat_body()
+{
+ setup_router_dummy_ipv6
+
+ # Clients will connect from another network behind the router.
+ # This allows for using multiple source addresses.
+ jexec router route add -6 2001:db8:44::0/64 2001:db8:42::2
+
+ # Additional gateways for route-to.
+ rtgw=${net_server_host_server%::*}::2:1
+ jexec router ndp -s ${rtgw} 00:01:02:03:04:05
+
+ # This test will check for proper source node creation for:
+ # max-src-states -> PF_SN_LIMIT
+ # sticky-address -> PF_SN_NAT
+ # route-to -> PF_SN_ROUTE
+ # The test expands to all 8 combinations of those source nodes being
+ # present or not.
+
+ pft_set_rules router \
+ "table <rtgws> { ${rtgw} }" \
+ "table <rdrgws> { 2001:db8:45::1 }" \
+ "rdr on ${epair_tester}b inet6 proto tcp from 2001:db8:44::10/124 to 2001:db8:45::1 -> <rdrgws> port 4242 sticky-address" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in quick on ${epair_tester}b route-to ( ${epair_server}a <rtgws>) inet6 proto tcp from port 4211 keep state label rule_3" \
+ "pass in quick on ${epair_tester}b route-to ( ${epair_server}a <rtgws>) sticky-address inet6 proto tcp from port 4212 keep state label rule_4" \
+ "pass in quick on ${epair_tester}b route-to ( ${epair_server}a <rtgws>) inet6 proto tcp from port 4213 keep state (max-src-states 3 source-track rule) label rule_5" \
+ "pass in quick on ${epair_tester}b route-to ( ${epair_server}a <rtgws>) sticky-address inet6 proto tcp from port 4214 keep state (max-src-states 3 source-track rule) label rule_6" \
+ "pass out quick on ${epair_server}a keep state"
+
+ # We don't check if state limits are properly enforced, this is tested
+ # by other tests in this file.
+ # Source address will not match the NAT rule
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-sport=4211 --fromaddr 2001:db8:44::01 --to 2001:db8:45::1
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-sport=4212 --fromaddr 2001:db8:44::02 --to 2001:db8:45::1
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-sport=4213 --fromaddr 2001:db8:44::03 --to 2001:db8:45::1
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-sport=4214 --fromaddr 2001:db8:44::04 --to 2001:db8:45::1
+ # Source address will match the NAT rule
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-sport=4211 --fromaddr 2001:db8:44::11 --to 2001:db8:45::1
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-sport=4212 --fromaddr 2001:db8:44::12 --to 2001:db8:45::1
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-sport=4213 --fromaddr 2001:db8:44::13 --to 2001:db8:45::1
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-sport=4214 --fromaddr 2001:db8:44::14 --to 2001:db8:45::1
+
+ states=$(mktemp) || exit 1
+ jexec router pfctl -qvss | normalize_pfctl_s > $states
+ nodes=$(mktemp) || exit 1
+ jexec router pfctl -qvvsS | normalize_pfctl_s > $nodes
+
+ # Order of states in output is not guaranteed, find each one separately.
+ for state_regexp in \
+ 'all tcp 2001:db8:45::1\[9\] <- 2001:db8:44::1\[4211\] .* 1:0 pkts, 76:0 bytes, rule 3$' \
+ 'all tcp 2001:db8:45::1\[9\] <- 2001:db8:44::2\[4212\] .* 1:0 pkts, 76:0 bytes, rule 4, route sticky-address$' \
+ 'all tcp 2001:db8:45::1\[9\] <- 2001:db8:44::3\[4213\] .* 1:0 pkts, 76:0 bytes, rule 5, limit source-track$' \
+ 'all tcp 2001:db8:45::1\[9\] <- 2001:db8:44::4\[4214\] .* 1:0 pkts, 76:0 bytes, rule 6, limit source-track, route sticky-address$' \
+ 'all tcp 2001:db8:45::1\[4242\] \(2001:db8:45::1\[9\]\) <- 2001:db8:44::11\[4211\] .* 1:0 pkts, 76:0 bytes, rule 3, NAT/RDR sticky-address' \
+ 'all tcp 2001:db8:45::1\[4242\] \(2001:db8:45::1\[9\]\) <- 2001:db8:44::12\[4212\] .* 1:0 pkts, 76:0 bytes, rule 4, NAT/RDR sticky-address, route sticky-address' \
+ 'all tcp 2001:db8:45::1\[4242\] \(2001:db8:45::1\[9\]\) <- 2001:db8:44::13\[4213\] .* 1:0 pkts, 76:0 bytes, rule 5, limit source-track, NAT/RDR sticky-address' \
+ 'all tcp 2001:db8:45::1\[4242\] \(2001:db8:45::1\[9\]\) <- 2001:db8:44::14\[4214\] .* 1:0 pkts, 76:0 bytes, rule 6, limit source-track, NAT/RDR sticky-address, route sticky-address' \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+
+ # Order of source nodes in output is not guaranteed, find each one separately.
+ for node_regexp in \
+ '2001:db8:44::2 -> 2001:db8:43::2:1 \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, filter rule 4, route sticky-address' \
+ '2001:db8:44::3 -> :: \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, filter rule 5, limit source-track' \
+ '2001:db8:44::4 -> 2001:db8:43::2:1 \( states 1, connections 0, rate 0.0/0s ) age [0-9:]+, 1 pkts, 76 bytes, filter rule 6, route sticky-address' \
+ '2001:db8:44::4 -> :: \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, filter rule 6, limit source-track' \
+ '2001:db8:44::11 -> 2001:db8:45::1 \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, rdr rule 0, NAT/RDR sticky-address' \
+ '2001:db8:44::12 -> 2001:db8:45::1 \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, rdr rule 0, NAT/RDR sticky-address' \
+ '2001:db8:44::12 -> 2001:db8:43::2:1 \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, filter rule 4, route sticky-address' \
+ '2001:db8:44::13 -> 2001:db8:45::1 \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, rdr rule 0, NAT/RDR sticky-address' \
+ '2001:db8:44::13 -> :: \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, filter rule 5, limit source-track' \
+ '2001:db8:44::14 -> 2001:db8:45::1 \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, rdr rule 0, NAT/RDR sticky-address' \
+ '2001:db8:44::14 -> 2001:db8:43::2:1 \( states 1, connections 0, rate 0.0/0s ) age [0-9:]+, 1 pkts, 76 bytes, filter rule 6, route sticky-address' \
+ '2001:db8:44::14 -> :: \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, filter rule 6, limit source-track' \
+ ; do
+ grep -qE "${node_regexp}" $nodes || atf_fail "Source node not found for '${node_regexp}'"
+ done
+
+ ! grep -q 'filter rule 3' $nodes || atf_fail "Source node found for rule 3"
+}
+
+sn_types_compat_cleanup()
+{
+ pft_cleanup
+}
+
+sn_types_pass_head()
+{
+ atf_set descr 'Combination of source node types with pass NAT rules'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+sn_types_pass_body()
+{
+ setup_router_dummy_ipv6
+
+ # Clients will connect from another network behind the router.
+ # This allows for using multiple source addresses.
+ jexec router route add -6 2001:db8:44::0/64 2001:db8:42::2
+
+ # Additional gateways for route-to.
+ rtgw=${net_server_host_server%::*}::2:1
+ jexec router ndp -s ${rtgw} 00:01:02:03:04:05
+
+ # This test will check for proper source node creation for:
+ # max-src-states -> PF_SN_LIMIT
+ # sticky-address -> PF_SN_NAT
+ # route-to -> PF_SN_ROUTE
+ # The test expands to all 8 combinations of those source nodes being
+ # present or not.
+
+ pft_set_rules router \
+ "table <rtgws> { ${rtgw} }" \
+ "table <rdrgws> { 2001:db8:45::1 }" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "match in on ${epair_tester}b inet6 proto tcp from 2001:db8:44::10/124 to 2001:db8:45::1 rdr-to <rdrgws> port 4242 sticky-address label rule_3" \
+ "pass in quick on ${epair_tester}b route-to ( ${epair_server}a <rtgws>) inet6 proto tcp from port 4211 keep state label rule_4" \
+ "pass in quick on ${epair_tester}b route-to ( ${epair_server}a <rtgws>) sticky-address inet6 proto tcp from port 4212 keep state label rule_5" \
+ "pass in quick on ${epair_tester}b route-to ( ${epair_server}a <rtgws>) inet6 proto tcp from port 4213 keep state (max-src-states 3 source-track rule) label rule_6" \
+ "pass in quick on ${epair_tester}b route-to ( ${epair_server}a <rtgws>) sticky-address inet6 proto tcp from port 4214 keep state (max-src-states 3 source-track rule) label rule_7" \
+ "pass out quick on ${epair_server}a keep state"
+
+ # We don't check if state limits are properly enforced, this is tested
+ # by other tests in this file.
+ # Source address will not match the NAT rule
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-sport=4211 --fromaddr 2001:db8:44::01 --to 2001:db8:45::1
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-sport=4212 --fromaddr 2001:db8:44::02 --to 2001:db8:45::1
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-sport=4213 --fromaddr 2001:db8:44::03 --to 2001:db8:45::1
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-sport=4214 --fromaddr 2001:db8:44::04 --to 2001:db8:45::1
+ # Source address will match the NAT rule
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-sport=4211 --fromaddr 2001:db8:44::11 --to 2001:db8:45::1
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-sport=4212 --fromaddr 2001:db8:44::12 --to 2001:db8:45::1
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-sport=4213 --fromaddr 2001:db8:44::13 --to 2001:db8:45::1
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-sport=4214 --fromaddr 2001:db8:44::14 --to 2001:db8:45::1
+
+ states=$(mktemp) || exit 1
+ jexec router pfctl -qvss | normalize_pfctl_s > $states
+ nodes=$(mktemp) || exit 1
+ jexec router pfctl -qvvsS | normalize_pfctl_s > $nodes
+
+ echo " === states ==="
+ cat $states
+ echo " === nodes ==="
+ cat $nodes
+ echo " === end === "
+
+ # Order of states in output is not guaranteed, find each one separately.
+ for state_regexp in \
+ 'all tcp 2001:db8:45::1\[9\] <- 2001:db8:44::1\[4211\] .* 1:0 pkts, 76:0 bytes, rule 4$' \
+ 'all tcp 2001:db8:45::1\[9\] <- 2001:db8:44::2\[4212\] .* 1:0 pkts, 76:0 bytes, rule 5, route sticky-address$' \
+ 'all tcp 2001:db8:45::1\[9\] <- 2001:db8:44::3\[4213\] .* 1:0 pkts, 76:0 bytes, rule 6, limit source-track$' \
+ 'all tcp 2001:db8:45::1\[9\] <- 2001:db8:44::4\[4214\] .* 1:0 pkts, 76:0 bytes, rule 7, limit source-track, route sticky-address$' \
+ 'all tcp 2001:db8:45::1\[4242\] \(2001:db8:45::1\[9\]\) <- 2001:db8:44::11\[4211\] .* 1:0 pkts, 76:0 bytes, rule 4, NAT/RDR sticky-address' \
+ 'all tcp 2001:db8:45::1\[4242\] \(2001:db8:45::1\[9\]\) <- 2001:db8:44::12\[4212\] .* 1:0 pkts, 76:0 bytes, rule 5, NAT/RDR sticky-address, route sticky-address' \
+ 'all tcp 2001:db8:45::1\[4242\] \(2001:db8:45::1\[9\]\) <- 2001:db8:44::13\[4213\] .* 1:0 pkts, 76:0 bytes, rule 6, limit source-track, NAT/RDR sticky-address' \
+ 'all tcp 2001:db8:45::1\[4242\] \(2001:db8:45::1\[9\]\) <- 2001:db8:44::14\[4214\] .* 1:0 pkts, 76:0 bytes, rule 7, limit source-track, NAT/RDR sticky-address, route sticky-address' \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+
+ # Order of source nodes in output is not guaranteed, find each one separately.
+ for node_regexp in \
+ '2001:db8:44::2 -> 2001:db8:43::2:1 \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, filter rule 5, route sticky-address' \
+ '2001:db8:44::3 -> :: \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, filter rule 6, limit source-track' \
+ '2001:db8:44::4 -> 2001:db8:43::2:1 \( states 1, connections 0, rate 0.0/0s ) age [0-9:]+, 1 pkts, 76 bytes, filter rule 7, route sticky-address' \
+ '2001:db8:44::4 -> :: \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, filter rule 7, limit source-track' \
+ '2001:db8:44::11 -> 2001:db8:45::1 \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, filter rule 3, NAT/RDR sticky-address' \
+ '2001:db8:44::12 -> 2001:db8:45::1 \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, filter rule 3, NAT/RDR sticky-address' \
+ '2001:db8:44::12 -> 2001:db8:43::2:1 \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, filter rule 5, route sticky-address' \
+ '2001:db8:44::13 -> 2001:db8:45::1 \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, filter rule 3, NAT/RDR sticky-address' \
+ '2001:db8:44::13 -> :: \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, filter rule 6, limit source-track' \
+ '2001:db8:44::14 -> 2001:db8:45::1 \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, filter rule 3, NAT/RDR sticky-address' \
+ '2001:db8:44::14 -> 2001:db8:43::2:1 \( states 1, connections 0, rate 0.0/0s ) age [0-9:]+, 1 pkts, 76 bytes, filter rule 7, route sticky-address' \
+ '2001:db8:44::14 -> :: \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, filter rule 7, limit source-track' \
+ ; do
+ grep -qE "${node_regexp}" $nodes || atf_fail "Source node not found for '${node_regexp}'"
+ done
+}
+
+sn_types_pass_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "source_track"
+ atf_add_test_case "kill"
+ atf_add_test_case "max_src_conn_rule"
+ atf_add_test_case "max_src_states_rule"
+ atf_add_test_case "max_src_states_global"
+ atf_add_test_case "sn_types_compat"
+ atf_add_test_case "sn_types_pass"
+}
diff --git a/tests/sys/netpfil/pf/status.sh b/tests/sys/netpfil/pf/status.sh
new file mode 100644
index 000000000000..bfd916a40c01
--- /dev/null
+++ b/tests/sys/netpfil/pf/status.sh
@@ -0,0 +1,73 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "basic" "cleanup"
+basic_head()
+{
+ atf_set descr 'Basic get/clear status test case'
+ atf_set require.user root
+}
+
+basic_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail one ${epair}a
+ jexec one ifconfig ${epair}a 192.0.2.1/24 up
+ vnet_mkjail two ${epair}b
+ jexec two ifconfig ${epair}b 192.0.2.2/24 up
+
+ jexec one pfctl -e
+ pft_set_rules one "pass"
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ jexec two ping -c 1 192.0.2.1
+
+ atf_check -s exit:0 -o not-match:'searches[[:space:]]+0' \
+ jexec one pfctl -si
+
+ atf_check -s exit:0 -o ignore -e ignore \
+ jexec one pfctl -Fi
+
+ atf_check -s exit:0 -o match:'searches[[:space:]]+0' \
+ jexec one pfctl -si
+}
+
+basic_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "basic"
+}
+
diff --git a/tests/sys/netpfil/pf/syncookie.sh b/tests/sys/netpfil/pf/syncookie.sh
new file mode 100644
index 000000000000..fad90f3b2618
--- /dev/null
+++ b/tests/sys/netpfil/pf/syncookie.sh
@@ -0,0 +1,577 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2021 Modirum MDPay
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+common_dir=$(atf_get_srcdir)/../common
+
+syncookie_state()
+{
+ jail=$1
+
+ jexec $jail pfctl -si -v | grep -A 2 '^Syncookies' | grep active \
+ | awk '{ print($2); }'
+}
+
+atf_test_case "basic" "cleanup"
+basic_head()
+{
+ atf_set descr 'Basic syncookie test'
+ atf_set require.user root
+}
+
+basic_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up
+ jexec alcatraz /usr/sbin/inetd -p ${PWD}/inetd-alcatraz.pid \
+ $(atf_get_srcdir)/echo_inetd.conf
+
+ ifconfig ${epair}a 192.0.2.2/24 up
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "set syncookies always" \
+ "pass in" \
+ "pass out"
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+
+ reply=$(echo foo | nc -N -w 5 192.0.2.1 7)
+ if [ "${reply}" != "foo" ];
+ then
+ atf_fail "Failed to connect to syncookie protected echo daemon"
+ fi
+
+ # Check that status shows syncookies as being active
+ active=$(syncookie_state alcatraz)
+ if [ "$active" != "active" ];
+ then
+ atf_fail "syncookies not active"
+ fi
+}
+
+basic_cleanup()
+{
+ rm -f ${PWD}/inetd-alcatraz.pid
+ pft_cleanup
+}
+
+atf_test_case "basic_v6" "cleanup"
+basic_v6_head()
+{
+ atf_set descr 'Basic syncookie IPv6 test'
+ atf_set require.user root
+}
+
+basic_v6_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad
+ jexec alcatraz /usr/sbin/inetd -p ${PWD}/inetd-alcatraz.pid \
+ $(atf_get_srcdir)/echo_inetd.conf
+
+ ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "set syncookies always" \
+ "pass in" \
+ "pass out"
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping6 -c 1 2001:db8::1
+
+ reply=$(echo foo | nc -N -w 5 2001:db8::1 7)
+ if [ "${reply}" != "foo" ];
+ then
+ atf_fail "Failed to connect to syncookie protected echo daemon"
+ fi
+
+ # Check that status shows syncookies as being active
+ active=$(syncookie_state alcatraz)
+ if [ "$active" != "active" ];
+ then
+ atf_fail "syncookies not active"
+ fi
+}
+
+basic_v6_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "forward" "cleanup"
+forward_head()
+{
+ atf_set descr 'Syncookies for forwarded hosts'
+ atf_set require.user root
+}
+
+forward_body()
+{
+ pft_init
+
+ epair_in=$(vnet_mkepair)
+ epair_out=$(vnet_mkepair)
+
+ vnet_mkjail fwd ${epair_in}b ${epair_out}a
+ vnet_mkjail srv ${epair_out}b
+
+ jexec fwd ifconfig ${epair_in}b 192.0.2.1/24 up
+ jexec fwd ifconfig ${epair_out}a 198.51.100.1/24 up
+ jexec fwd sysctl net.inet.ip.forwarding=1
+
+ jexec srv ifconfig ${epair_out}b 198.51.100.2/24 up
+ jexec srv route add default 198.51.100.1
+ jexec srv /usr/sbin/inetd -p ${PWD}/inetd-alcatraz.pid \
+ $(atf_get_srcdir)/echo_inetd.conf
+
+ ifconfig ${epair_in}a 192.0.2.2/24 up
+ route add -net 198.51.100.0/24 192.0.2.1
+
+ jexec fwd pfctl -e
+ pft_set_rules fwd \
+ "set syncookies always" \
+ "pass in" \
+ "pass out"
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 198.51.100.2
+
+ reply=$(echo foo | nc -N -w 5 198.51.100.2 7)
+ if [ "${reply}" != "foo" ];
+ then
+ atf_fail "Failed to connect to syncookie protected echo daemon"
+ fi
+}
+
+forward_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "forward_v6" "cleanup"
+forward_v6_head()
+{
+ atf_set descr 'Syncookies for forwarded hosts'
+ atf_set require.user root
+}
+
+forward_v6_body()
+{
+ pft_init
+
+ epair_in=$(vnet_mkepair)
+ epair_out=$(vnet_mkepair)
+
+ vnet_mkjail fwd ${epair_in}b ${epair_out}a
+ vnet_mkjail srv ${epair_out}b
+
+ jexec fwd ifconfig ${epair_in}b inet6 2001:db8::1/64 up no_dad
+ jexec fwd ifconfig ${epair_out}a inet6 2001:db8:1::1/64 up no_dad
+ jexec fwd sysctl net.inet6.ip6.forwarding=1
+
+ jexec srv ifconfig ${epair_out}b inet6 2001:db8:1::2/64 up no_dad
+ jexec srv route -6 add default 2001:db8:1::1
+ jexec srv /usr/sbin/inetd -p ${PWD}/inetd-alcatraz.pid \
+ $(atf_get_srcdir)/echo_inetd.conf
+
+ ifconfig ${epair_in}a inet6 2001:db8::2/64 up no_dad
+ route -6 add -net 2001:db8:1::/64 2001:db8::1
+
+ jexec fwd pfctl -e
+ pft_set_rules fwd \
+ "set syncookies always" \
+ "pass in" \
+ "pass out"
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping6 -c 1 2001:db8:1::2
+
+ reply=$(echo foo | nc -N -w 5 2001:db8:1::2 7)
+ if [ "${reply}" != "foo" ];
+ then
+ atf_fail "Failed to connect to syncookie protected echo daemon"
+ fi
+}
+
+forward_v6_cleanup()
+{
+ pft_cleanup
+}
+
+loopback_test()
+{
+ local addr port
+
+ addr=$1
+ port=$2
+
+ # syncookies don't work without state tracking enabled.
+ atf_check -e ignore pfctl -e
+ atf_check pfctl -f - <<__EOF__
+set syncookies always
+pass all keep state
+__EOF__
+
+ # Try to transmit data over a loopback connection.
+ cat <<__EOF__ >in
+Creativity, no.
+__EOF__
+ nc -l $addr $port >out &
+
+ atf_check nc -N $addr $port < in
+
+ atf_check -o file:in cat out
+
+ atf_check -e ignore pfctl -d
+}
+
+atf_test_case "loopback" "cleanup"
+loopback_head()
+{
+ atf_set descr 'Make sure that loopback v4 TCP connections work with syncookies on'
+ atf_set require.user root
+}
+
+loopback_body()
+{
+ local epair
+
+ pft_init
+
+ atf_check ifconfig lo0 127.0.0.1/8
+ atf_check ifconfig lo0 up
+
+ loopback_test 127.0.0.1 8080
+
+ epair=$(vnet_mkepair)
+ atf_check ifconfig ${epair}a inet 192.0.2.1/24
+
+ loopback_test 192.0.2.1 8081
+}
+
+loopback_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "loopback_v6" "cleanup"
+loopback_v6_head()
+{
+ atf_set descr 'Make sure that loopback v6 TCP connections work with syncookies on'
+ atf_set require.user root
+}
+
+loopback_v6_body()
+{
+ local epair
+
+ pft_init
+
+ atf_check ifconfig lo0 up
+
+ loopback_test ::1 8080
+
+ epair=$(vnet_mkepair)
+ atf_check ifconfig ${epair}a inet6 2001:db8::1/64
+
+ loopback_test 2001:db8::1 8081
+}
+
+loopback_v6_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nostate" "cleanup"
+nostate_head()
+{
+ atf_set descr 'Ensure that we do not create until SYN|ACK'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+nostate_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a 192.0.2.2/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "set syncookies always" \
+ "pass in" \
+ "pass out"
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+
+ # Now syn flood to create many states
+ ${common_dir}/pft_synflood.py \
+ --sendif ${epair}a \
+ --to 192.0.2.2 \
+ --count 20
+
+ states=$(jexec alcatraz pfctl -ss | grep tcp)
+ if [ -n "$states" ];
+ then
+ echo "$states"
+ atf_fail "Found unexpected state"
+ fi
+}
+
+nostate_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "nostate_v6" "cleanup"
+nostate_v6_head()
+{
+ atf_set descr 'Ensure that we do not create until SYN|ACK'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+nostate_v6_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "set syncookies always" \
+ "pass in" \
+ "pass out"
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping6 -c 1 2001:db8::1
+
+ # Now syn flood to create many states
+ ${common_dir}/pft_synflood.py \
+ --ip6 \
+ --sendif ${epair}a \
+ --to 2001:db8::2 \
+ --count 20
+
+ states=$(jexec alcatraz pfctl -ss | grep tcp)
+ if [ -n "$states" ];
+ then
+ echo "$states"
+ atf_fail "Found unexpected state"
+ fi
+}
+
+nostate_v6_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "adaptive" "cleanup"
+adaptive_head()
+{
+ atf_set descr 'Adaptive mode test'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+adaptive_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a 192.0.2.2/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "set limit states 100" \
+ "set syncookies adaptive (start 10%%, end 5%%)" \
+ "pass in" \
+ "pass out"
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+
+ # Check that status shows syncookies as being inactive
+ active=$(syncookie_state alcatraz)
+ if [ "$active" != "inactive" ];
+ then
+ atf_fail "syncookies active when they should not be"
+ fi
+
+ # Now syn flood to create many states
+ ${common_dir}/pft_synflood.py \
+ --sendif ${epair}a \
+ --to 192.0.2.2 \
+ --count 100
+
+ # Check that status shows syncookies as being active
+ active=$(syncookie_state alcatraz)
+ if [ "$active" != "active" ];
+ then
+ atf_fail "syncookies not active"
+ fi
+
+ # Adaptive mode should kick in and stop us from creating more than
+ # about 10 states
+ states=$(jexec alcatraz pfctl -ss | grep tcp | wc -l)
+ if [ "$states" -gt 20 ];
+ then
+ echo "$states"
+ atf_fail "Found unexpected states"
+ fi
+}
+
+adaptive_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "limits" "cleanup"
+limits_head()
+{
+ atf_set descr 'Ensure limit calculation works for low or high state limits'
+ atf_set require.user root
+}
+
+limits_body()
+{
+ pft_init
+
+ vnet_mkjail alcatraz
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz \
+ "set limit states 1" \
+ "set syncookies adaptive (start 10%%, end 5%%)" \
+ "pass in" \
+ "pass out"
+
+ pft_set_rules alcatraz \
+ "set limit states 326000000" \
+ "set syncookies adaptive (start 10%%, end 5%%)" \
+ "pass in" \
+ "pass out"
+}
+
+limits_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "port_reuse" "cleanup"
+port_reuse_head()
+{
+ atf_set descr 'Test rapid port re-use'
+ atf_set require.user root
+}
+
+port_reuse_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair}b
+ vnet_mkjail singsing
+ jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up
+ jexec alcatraz /usr/sbin/inetd -p ${PWD}/inetd-alcatraz.pid \
+ $(atf_get_srcdir)/echo_inetd.conf
+
+ ifconfig ${epair}a 192.0.2.2/24 up
+
+ jexec alcatraz pfctl -e
+ jexec alcatraz pfctl -x loud
+ pft_set_rules alcatraz \
+ "set syncookies always" \
+ "pass in" \
+ "pass out"
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+
+ reply=$(echo foo | nc -p 1234 -N -w 5 192.0.2.1 7)
+ if [ "${reply}" != "foo" ];
+ then
+ atf_fail "Failed to connect to syncookie protected echo daemon"
+ fi
+
+ # We can't re-use the source IP/port combo quickly enough, so we're
+ # going to play a really dirty trick, and move our interface to a new
+ # jail, and do it from there.
+ ifconfig ${epair}a vnet singsing
+ jexec singsing ifconfig ${epair}a 192.0.2.2/24 up
+ atf_check -s exit:0 -o ignore jexec singsing ping -c 1 192.0.2.1
+
+ reply=$(echo bar | jexec singsing nc -p 1234 -N -w 5 192.0.2.1 7)
+ if [ "${reply}" != "bar" ];
+ then
+ atf_fail "Failed to connect to syncookie protected echo daemon (2)"
+ fi
+}
+
+port_reuse_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "basic"
+ atf_add_test_case "basic_v6"
+ atf_add_test_case "forward"
+ atf_add_test_case "forward_v6"
+ atf_add_test_case "loopback"
+ atf_add_test_case "loopback_v6"
+ atf_add_test_case "nostate"
+ atf_add_test_case "nostate_v6"
+ atf_add_test_case "adaptive"
+ atf_add_test_case "limits"
+ atf_add_test_case "port_reuse"
+}
diff --git a/tests/sys/netpfil/pf/synproxy.sh b/tests/sys/netpfil/pf/synproxy.sh
new file mode 100644
index 000000000000..617fa6ba2afc
--- /dev/null
+++ b/tests/sys/netpfil/pf/synproxy.sh
@@ -0,0 +1,164 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2018 Kristof Provost <kp@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "synproxy" "cleanup"
+synproxy_head()
+{
+ atf_set descr 'Basic synproxy test'
+ atf_set require.user root
+}
+
+synproxy_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a 192.0.2.1/24 up
+ route add -net 198.51.100.0/24 192.0.2.2
+
+ link=$(vnet_mkepair)
+
+ vnet_mkjail alcatraz ${epair}b ${link}a
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+ jexec alcatraz ifconfig ${link}a 198.51.100.1/24 up
+ jexec alcatraz sysctl net.inet.ip.forwarding=1
+
+ vnet_mkjail singsing ${link}b
+ jexec singsing ifconfig ${link}b 198.51.100.2/24 up
+ jexec singsing route add default 198.51.100.1
+
+ jexec singsing /usr/sbin/inetd -p ${PWD}/inetd-singsing.pid $(atf_get_srcdir)/echo_inetd.conf
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz "set fail-policy return" \
+ "scrub in all fragment reassemble" \
+ "pass out quick on ${epair}b all no state allow-opts" \
+ "pass in quick on ${epair}b proto tcp from any to any port 7 synproxy state" \
+ "pass in quick on ${epair}b all no state"
+
+ # Sanity check, can we ping singing
+ atf_check -s exit:0 -o ignore ping -c 1 198.51.100.2
+
+ # Check that we can talk to the singsing jail, after synproxying
+ reply=$(echo ping | nc -N 198.51.100.2 7)
+ if [ "${reply}" != "ping" ];
+ then
+ atf_fail "echo failed"
+ fi
+}
+
+synproxy_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "local" "cleanup"
+local_head()
+{
+ atf_set descr 'Synproxy a locally terminated connection'
+ atf_set require.user root
+}
+
+local_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a 192.0.2.2/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up
+ jexec alcatraz /usr/sbin/inetd -p ${PWD}/inetd-alcatraz.pid \
+ $(atf_get_srcdir)/echo_inetd.conf
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz "set fail-policy return" \
+ "scrub in all fragment reassemble" \
+ "pass in quick on ${epair}b proto tcp from any to any port 7 synproxy state"
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1
+
+ # Check that we can talk to the jail, after synproxying
+ reply=$(echo ping | nc -N -w 5 192.0.2.1 7)
+ if [ "${reply}" != "ping" ];
+ then
+ atf_fail "echo failed"
+ fi
+}
+
+local_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "local_v6" "cleanup"
+local_v6_head()
+{
+ atf_set descr 'Synproxy (v6) a locally terminated connection'
+ atf_set require.user root
+}
+
+local_v6_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a inet6 2001:db8:42::1/64 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b inet6 2001:db8:42::2/64 up
+ jexec alcatraz /usr/sbin/inetd -p ${PWD}/inetd-alcatraz.pid \
+ $(atf_get_srcdir)/echo_inetd.conf
+
+ jexec alcatraz pfctl -e
+ pft_set_rules alcatraz "set fail-policy return" \
+ "scrub in all fragment reassemble" \
+ "pass in quick on ${epair}b proto tcp from any to any port 7 synproxy state"
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore ping6 -c 1 2001:db8:42::2
+
+ reply=$(echo ping | nc -N -w 5 2001:db8:42::2 7)
+ if [ "${reply}" != "ping" ];
+ then
+ atf_fail "echo failed"
+ fi
+}
+
+local_v6_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "synproxy"
+ atf_add_test_case "local"
+ atf_add_test_case "local_v6"
+}
diff --git a/tests/sys/netpfil/pf/table.sh b/tests/sys/netpfil/pf/table.sh
new file mode 100644
index 000000000000..5e5fccdaca20
--- /dev/null
+++ b/tests/sys/netpfil/pf/table.sh
@@ -0,0 +1,628 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Mark Johnston <markj@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+TABLE_STATS_ZERO_REGEXP='Packets: 0[[:space:]]*Bytes: 0[[:space:]]'
+TABLE_STATS_NONZERO_REGEXP='Packets: [1-9][0-9]*[[:space:]]*Bytes: [1-9][0-9]*[[:space:]]'
+
+atf_test_case "v4_counters" "cleanup"
+v4_counters_head()
+{
+ atf_set descr 'Verify per-address counters for v4'
+ atf_set require.user root
+}
+
+v4_counters_body()
+{
+ pft_init
+
+ epair_send=$(vnet_mkepair)
+ ifconfig ${epair_send}a 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair_send}b
+ jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up
+ jexec alcatraz pfctl -e
+
+ pft_set_rules alcatraz \
+ "table <foo> counters { 192.0.2.1 }" \
+ "block all" \
+ "pass in from <foo> to any" \
+ "pass out from any to <foo>" \
+ "set skip on lo"
+
+ atf_check -s exit:0 -o ignore ping -c 3 192.0.2.2
+
+ atf_check -s exit:0 -e ignore \
+ -o match:'In/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \
+ -o match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
+ -o match:'Out/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \
+ -o match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
+ jexec alcatraz pfctl -t foo -T show -vv
+}
+
+v4_counters_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "v6_counters" "cleanup"
+v6_counters_head()
+{
+ atf_set descr 'Verify per-address counters for v6'
+ atf_set require.user root
+}
+
+v6_counters_body()
+{
+ pft_init
+
+ epair_send=$(vnet_mkepair)
+ ifconfig ${epair_send}a inet6 2001:db8:42::1/64 up no_dad -ifdisabled
+
+ vnet_mkjail alcatraz ${epair_send}b
+ jexec alcatraz ifconfig ${epair_send}b inet6 2001:db8:42::2/64 up no_dad
+ jexec alcatraz pfctl -e
+
+ pft_set_rules alcatraz \
+ "table <foo6> counters { 2001:db8:42::1 }" \
+ "block all" \
+ "pass in from <foo6> to any" \
+ "pass out from any to <foo6>" \
+ "set skip on lo"
+
+ atf_check -s exit:0 -o ignore ping -6 -c 3 2001:db8:42::2
+
+ atf_check -s exit:0 -e ignore \
+ -o match:'In/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \
+ -o match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
+ -o match:'Out/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \
+ -o match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
+ jexec alcatraz pfctl -t foo6 -T show -vv
+}
+
+v6_counters_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "match_counters" "cleanup"
+match_counters_head()
+{
+ atf_set descr 'Test that counters for tables in match rules work'
+ atf_set require.user root
+}
+
+match_counters_body()
+{
+ pft_init
+
+ epair_send=$(vnet_mkepair)
+ ifconfig ${epair_send}a 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair_send}b
+ jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up
+ jexec alcatraz pfctl -e
+
+ pft_set_rules alcatraz \
+ "table <foo> counters { 192.0.2.1 }" \
+ "pass all" \
+ "match in from <foo> to any" \
+ "match out from any to <foo>" \
+ "set skip on lo"
+
+ atf_check -s exit:0 -o ignore ping -c 3 192.0.2.2
+
+ atf_check -s exit:0 -e ignore \
+ -o match:'In/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \
+ -o match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
+ -o match:'Out/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \
+ -o match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
+ jexec alcatraz pfctl -t foo -T show -vv
+}
+
+match_counters_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "zero_one" "cleanup"
+zero_one_head()
+{
+ atf_set descr 'Test zeroing a single address in a table'
+ atf_set require.user root
+}
+
+pft_cleared_ctime()
+{
+ jexec "$1" pfctl -t "$2" -vvT show | awk -v ip="$3" '
+ ($1 == ip) { m = 1 }
+ ($1 == "Cleared:" && m) {
+ sub("[[:space:]]*Cleared:[[:space:]]*", ""); print; exit }'
+}
+
+ctime_to_unixtime()
+{
+ # NB: it's not TZ=UTC, it's TZ=/etc/localtime
+ date -jf '%a %b %d %H:%M:%S %Y' "$1" '+%s'
+}
+
+zero_one_body()
+{
+ pft_init
+
+ epair_send=$(vnet_mkepair)
+ ifconfig ${epair_send}a 192.0.2.1/24 up
+ ifconfig ${epair_send}a inet alias 192.0.2.3/24
+
+ vnet_mkjail alcatraz ${epair_send}b
+ jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up
+ jexec alcatraz pfctl -e
+
+ pft_set_rules alcatraz \
+ "table <foo> counters { 192.0.2.1, 192.0.2.3 }" \
+ "block all" \
+ "pass in from <foo> to any" \
+ "pass out from any to <foo>" \
+ "set skip on lo"
+
+ atf_check -s exit:0 -o ignore ping -c 3 -S 192.0.2.1 192.0.2.2
+ atf_check -s exit:0 -o ignore ping -c 3 -S 192.0.2.3 192.0.2.2
+
+ jexec alcatraz pfctl -t foo -T show -vv
+
+ atf_check -s exit:0 -e ignore \
+ -o match:'In/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \
+ -o match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
+ -o match:'Out/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \
+ -o match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
+ jexec alcatraz pfctl -t foo -T show -vv
+
+ local uniq base ts1 ts3
+ uniq=`jexec alcatraz pfctl -t foo -vvT show | sort -u | grep -c Cleared`
+ atf_check_equal 1 "$uniq" # time they were added
+
+ base=`pft_cleared_ctime alcatraz foo 192.0.2.1`
+
+ atf_check -s exit:0 -e ignore \
+ jexec alcatraz pfctl -t foo -T zero 192.0.2.3
+
+ ts1=`pft_cleared_ctime alcatraz foo 192.0.2.1`
+ atf_check_equal "$base" "$ts1"
+
+ ts3=`pft_cleared_ctime alcatraz foo 192.0.2.3`
+ atf_check test "$ts1" != "$ts3"
+
+ ts1=`ctime_to_unixtime "$ts1"`
+ ts3=`ctime_to_unixtime "$ts3"`
+ atf_check test $(( "$ts3" - "$ts1" )) -lt 10 # (3 pings * 2) + epsilon
+ atf_check test "$ts1" -lt "$ts3"
+
+ # We now have a zeroed and a non-zeroed counter, so both patterns
+ # should match
+ atf_check -s exit:0 -e ignore \
+ -o match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
+ -o match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
+ -o match:'In/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \
+ -o match:'Out/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \
+ jexec alcatraz pfctl -t foo -T show -vv
+}
+
+zero_one_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "zero_all" "cleanup"
+zero_all_head()
+{
+ atf_set descr 'Test zeroing all table entries'
+ atf_set require.user root
+}
+
+zero_all_body()
+{
+ pft_init
+
+ epair_send=$(vnet_mkepair)
+ ifconfig ${epair_send}a 192.0.2.1/24 up
+ ifconfig ${epair_send}a inet alias 192.0.2.3/24
+
+ vnet_mkjail alcatraz ${epair_send}b
+ jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up
+ jexec alcatraz pfctl -e
+
+ pft_set_rules alcatraz \
+ "table <foo> counters { 192.0.2.1, 192.0.2.3 }" \
+ "block all" \
+ "pass in from <foo> to any" \
+ "pass out from any to <foo>" \
+ "set skip on lo"
+
+ atf_check -s exit:0 -o ignore ping -c 3 -S 192.0.2.1 192.0.2.2
+ atf_check -s exit:0 -o ignore ping -c 3 -S 192.0.2.3 192.0.2.2
+
+ jexec alcatraz pfctl -t foo -T show -vv
+ atf_check -s exit:0 -e ignore \
+ -o match:'In/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \
+ -o match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
+ -o match:'Out/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \
+ -o match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
+ jexec alcatraz pfctl -t foo -T show -vv
+
+ atf_check -s exit:0 -e ignore \
+ jexec alcatraz pfctl -t foo -T zero
+
+ jexec alcatraz pfctl -t foo -T show -vv
+ atf_check -s exit:0 -e ignore \
+ -o match:'In/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \
+ -o match:'Out/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \
+ -o match:'In/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \
+ -o match:'Out/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \
+ jexec alcatraz pfctl -t foo -T show -vv
+}
+
+zero_all_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "reset_nonzero" "cleanup"
+reset_nonzero_head()
+{
+ atf_set descr 'Test zeroing an address with non-zero counters'
+ atf_set require.user root
+}
+
+reset_nonzero_body()
+{
+ pft_init
+
+ epair_send=$(vnet_mkepair)
+ ifconfig ${epair_send}a 192.0.2.1/24 up
+ ifconfig ${epair_send}a inet alias 192.0.2.3/24
+
+ vnet_mkjail alcatraz ${epair_send}b
+ jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up
+ jexec alcatraz pfctl -e
+
+ pft_set_rules alcatraz \
+ "table <foo> counters { 192.0.2.1, 192.0.2.3 }" \
+ "table <bar> counters { }" \
+ "block all" \
+ "pass in from <foo> to any" \
+ "pass out from any to <foo>" \
+ "pass on notReallyAnIf from <bar> to <bar>" \
+ "set skip on lo"
+
+ # Nonexisting table can't be reset, following `-T show`.
+ atf_check -o ignore \
+ -s not-exit:0 \
+ -e inline:"pfctl: Table does not exist.\n" \
+ jexec alcatraz pfctl -t nonexistent -T reset
+
+ atf_check -o ignore \
+ -s exit:0 \
+ -e inline:"0/0 stats cleared.\n" \
+ jexec alcatraz pfctl -t bar -T reset
+
+ # No-op is a valid operation.
+ atf_check -s exit:0 \
+ -e inline:"0/2 stats cleared.\n" \
+ jexec alcatraz pfctl -t foo -T reset
+
+ atf_check -s exit:0 -o ignore ping -c 3 -S 192.0.2.3 192.0.2.2
+
+ atf_check -s exit:0 -e ignore \
+ -o match:'In/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \
+ -o match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
+ -o match:'Out/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \
+ -o match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
+ jexec alcatraz pfctl -t foo -vvT show
+
+ local clrd uniq
+ clrd=`jexec alcatraz pfctl -t foo -vvT show | grep -c Cleared`
+ uniq=`jexec alcatraz pfctl -t foo -vvT show | sort -u | grep -c Cleared`
+ atf_check_equal "$clrd" 2
+ atf_check_equal "$uniq" 1 # time they were added
+
+ atf_check -s exit:0 -e ignore \
+ -e inline:"1/2 stats cleared.\n" \
+ jexec alcatraz pfctl -t foo -T reset
+
+ clrd=`jexec alcatraz pfctl -t foo -vvT show | grep -c Cleared`
+ uniq=`jexec alcatraz pfctl -t foo -vvT show | sort -u | grep -c Cleared`
+ atf_check_equal "$clrd" 2
+ atf_check_equal "$uniq" 2 # 192.0.2.3 should get new timestamp
+
+ atf_check -s exit:0 -e ignore \
+ -o not-match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
+ -o not-match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
+ -o match:'In/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \
+ -o match:'Out/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \
+ jexec alcatraz pfctl -t foo -vvT show
+}
+
+reset_nonzero_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "pr251414" "cleanup"
+pr251414_head()
+{
+ atf_set descr 'Test PR 251414'
+ atf_set require.user root
+}
+
+pr251414_body()
+{
+ pft_init
+
+ epair_send=$(vnet_mkepair)
+ ifconfig ${epair_send}a 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair_send}b
+ jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up
+ jexec alcatraz pfctl -e
+
+ pft_set_rules alcatraz \
+ "pass all" \
+ "table <tab> { self }" \
+ "pass in log to <tab>"
+
+ pft_set_rules noflush alcatraz \
+ "pass all" \
+ "table <tab> counters { self }" \
+ "pass in log to <tab>"
+
+ atf_check -s exit:0 -o ignore ping -c 3 192.0.2.2
+
+ jexec alcatraz pfctl -t tab -T show -vv
+}
+
+pr251414_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "automatic" "cleanup"
+automatic_head()
+{
+ atf_set descr "Test automatic - optimizer generated - tables"
+ atf_set require.user root
+}
+
+automatic_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+ jexec alcatraz pfctl -e
+
+ pft_set_rules alcatraz \
+ "block in" \
+ "pass in proto icmp from 192.0.2.1" \
+ "pass in proto icmp from 192.0.2.3" \
+ "pass in proto icmp from 192.0.2.4" \
+ "pass in proto icmp from 192.0.2.5" \
+ "pass in proto icmp from 192.0.2.6" \
+ "pass in proto icmp from 192.0.2.7" \
+ "pass in proto icmp from 192.0.2.8" \
+ "pass in proto icmp from 192.0.2.9"
+
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+}
+
+automatic_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "network" "cleanup"
+network_head()
+{
+ atf_set descr 'Test <ifgroup>:network'
+ atf_set require.user root
+}
+
+network_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+ jexec alcatraz pfctl -e
+
+ pft_set_rules alcatraz \
+ "table <allow> const { epair:network }"\
+ "block in" \
+ "pass in from <allow>"
+
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+}
+
+network_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "pr259689" "cleanup"
+pr259689_head()
+{
+ atf_set descr 'Test PR 259689'
+ atf_set require.user root
+}
+
+pr259689_body()
+{
+ pft_init
+
+ vnet_mkjail alcatraz
+ jexec alcatraz pfctl -e
+
+ pft_set_rules alcatraz \
+ "pass in" \
+ "block in inet from { 1.1.1.1, 1.1.1.2, 2.2.2.2, 2.2.2.3, 4.4.4.4, 4.4.4.5 }"
+
+ atf_check -o match:'block drop in inet from <__automatic_.*:6> to any' \
+ -e ignore \
+ jexec alcatraz pfctl -sr -vv
+}
+
+pr259689_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "precreate" "cleanup"
+precreate_head()
+{
+ atf_set descr 'Test creating a table without counters, then loading rules that add counters'
+ atf_set require.user root
+}
+
+precreate_body()
+{
+ pft_init
+
+ vnet_mkjail alcatraz
+
+ jexec alcatraz pfctl -t foo -T add 192.0.2.1
+ jexec alcatraz pfctl -t foo -T show
+
+ pft_set_rules noflush alcatraz \
+ "table <foo> counters persist" \
+ "pass in from <foo>"
+
+ # Expect all counters to be zero
+ atf_check -s exit:0 -e ignore \
+ -o match:'In/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \
+ -o match:'In/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \
+ -o match:'Out/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \
+ -o match:'Out/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \
+ jexec alcatraz pfctl -t foo -T show -vv
+
+}
+
+precreate_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "anchor" "cleanup"
+anchor_head()
+{
+ atf_set descr 'Test tables in anchors'
+ atf_set require.user root
+}
+
+anchor_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+ jexec alcatraz pfctl -e
+
+ (echo "table <testtable> persist"
+ echo "block in quick from <testtable> to any"
+ ) | jexec alcatraz pfctl -a anchorage -f -
+
+ pft_set_rules noflush alcatraz \
+ "pass" \
+ "anchor anchorage"
+
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ # Tables belong to anchors, so this is a different table and won't affect anything
+ jexec alcatraz pfctl -t testtable -T add 192.0.2.1
+ atf_check -s exit:0 -o ignore ping -c 1 192.0.2.2
+
+ # But when we add the address to the table in the anchor it does block traffic
+ jexec alcatraz pfctl -a anchorage -t testtable -T add 192.0.2.1
+ atf_check -s exit:2 -o ignore ping -c 1 192.0.2.2
+}
+
+anchor_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "flush" "cleanup"
+flush_head()
+{
+ atf_set descr 'Test flushing addresses from tables'
+ atf_set require.user root
+}
+
+flush_body()
+{
+ pft_init
+
+ vnet_mkjail alcatraz
+
+ atf_check -s exit:0 -e match:"1/1 addresses added." \
+ jexec alcatraz pfctl -t foo -T add 1.2.3.4
+ atf_check -s exit:0 -o match:" 1.2.3.4" \
+ jexec alcatraz pfctl -t foo -T show
+ atf_check -s exit:0 -e match:"1 addresses deleted." \
+ jexec alcatraz pfctl -t foo -T flush
+ atf_check -s exit:0 -o not-match:"1.2.3.4" \
+ jexec alcatraz pfctl -t foo -T show
+}
+
+flush_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "v4_counters"
+ atf_add_test_case "v6_counters"
+ atf_add_test_case "match_counters"
+ atf_add_test_case "zero_one"
+ atf_add_test_case "zero_all"
+ atf_add_test_case "reset_nonzero"
+ atf_add_test_case "pr251414"
+ atf_add_test_case "automatic"
+ atf_add_test_case "network"
+ atf_add_test_case "pr259689"
+ atf_add_test_case "precreate"
+ atf_add_test_case "anchor"
+ atf_add_test_case "flush"
+}
diff --git a/tests/sys/netpfil/pf/tcp.py b/tests/sys/netpfil/pf/tcp.py
new file mode 100644
index 000000000000..53e0658f419c
--- /dev/null
+++ b/tests/sys/netpfil/pf/tcp.py
@@ -0,0 +1,158 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2025 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import sys
+import pytest
+import random
+import socket
+import selectors
+from utils import DelayedSend
+from atf_python.sys.net.tools import ToolsHelper
+from atf_python.sys.net.vnet import VnetTestTemplate
+
+class TCPClient:
+ def __init__(self, src, dst, sport, dport, sp):
+ self.src = src
+ self.dst = dst
+ self.sport = sport
+ self.dport = dport
+ self.sp = sp
+ self.seq = random.randrange(1, (2**32)-1)
+ self.ack = 0
+
+ def syn(self):
+ syn = self.sp.IP(src=self.src, dst=self.dst) \
+ / self.sp.TCP(sport=self.sport, dport=self.dport, flags="S", seq=self.seq)
+ return syn
+
+ def connect(self):
+ syn = self.syn()
+ r = self.sp.sr1(syn, timeout=5)
+
+ assert r
+ t = r.getlayer(self.sp.TCP)
+ assert t
+ assert t.sport == self.dport
+ assert t.dport == self.sport
+ assert t.flags == "SA"
+
+ self.seq += 1
+ self.ack = t.seq + 1
+ ack = self.sp.IP(src=self.src, dst=self.dst) \
+ / self.sp.TCP(sport=self.sport, dport=self.dport, flags="A", ack=self.ack, seq=self.seq)
+ self.sp.send(ack)
+
+ def send(self, data):
+ length = len(data)
+ pkt = self.sp.IP(src=self.src, dst=self.dst) \
+ / self.sp.TCP(sport=self.sport, dport=self.dport, ack=self.ack, seq=self.seq, flags="") \
+ / self.sp.Raw(data)
+ self.seq += length
+ pkt.show()
+ self.sp.send(pkt)
+
+class TestTcp(VnetTestTemplate):
+ REQUIRED_MODULES = [ "pf" ]
+ TOPOLOGY = {
+ "vnet1": {"ifaces": ["if1"]},
+ "vnet2": {"ifaces": ["if1"]},
+ "if1": {"prefixes4": [("192.0.2.1/24", "192.0.2.2/24")]},
+ }
+
+ def vnet2_handler(self, vnet):
+ ToolsHelper.print_output("/usr/sbin/arp -s 192.0.2.3 00:01:02:03:04:05")
+ ToolsHelper.print_output("/sbin/pfctl -e")
+ ToolsHelper.pf_rules([
+ "pass"
+ ])
+ ToolsHelper.print_output("/sbin/pfctl -x loud")
+
+ # Start TCP listener
+ sel = selectors.DefaultSelector()
+ t = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ t.bind(("0.0.0.0", 1234))
+ t.listen(100)
+ t.setblocking(False)
+ sel.register(t, selectors.EVENT_READ, data=None)
+
+ while True:
+ events = sel.select(timeout=2)
+ for key, mask in events:
+ sock = key.fileobj
+ if key.data is None:
+ conn, addr = sock.accept()
+ print(f"Accepted connection from {addr}")
+ events = selectors.EVENT_READ | selectors.EVENT_WRITE
+ sel.register(conn, events, data="TCP")
+ else:
+ if mask & selectors.EVENT_READ:
+ recv_data = sock.recv(1024)
+ print(f"Received TCP {recv_data}")
+ ToolsHelper.print_output("/sbin/pfctl -ss -vv")
+ sock.send(recv_data)
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_challenge_ack(self):
+ vnet = self.vnet_map["vnet1"]
+ ifname = vnet.iface_alias_map["if1"].name
+
+ # Import in the correct vnet, so at to not confuse Scapy
+ import scapy.all as sp
+
+ a = TCPClient("192.0.2.3", "192.0.2.2", 1234, 1234, sp)
+ a.connect()
+ a.send(b"foo")
+
+ b = TCPClient("192.0.2.3", "192.0.2.2", 1234, 1234, sp)
+ syn = b.syn()
+ syn.show()
+ s = DelayedSend(syn)
+ packets = sp.sniff(iface=ifname, timeout=3)
+ found = False
+ for p in packets:
+ ip = p.getlayer(sp.IP)
+ if not ip:
+ continue
+ tcp = p.getlayer(sp.TCP)
+ if not tcp:
+ continue
+
+ if ip.src != "192.0.2.2":
+ continue
+
+ p.show()
+
+ assert ip.dst == "192.0.2.3"
+ assert tcp.sport == 1234
+ assert tcp.dport == 1234
+ assert tcp.flags == "A"
+
+ # We only expect one
+ assert not found
+ found = True
+
+ assert found
diff --git a/tests/sys/netpfil/pf/tcp.sh b/tests/sys/netpfil/pf/tcp.sh
new file mode 100644
index 000000000000..f6a9ffde1383
--- /dev/null
+++ b/tests/sys/netpfil/pf/tcp.sh
@@ -0,0 +1,110 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2023 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+common_dir=$(atf_get_srcdir)/../common
+
+atf_test_case "rst" "cleanup"
+rst_head()
+{
+ atf_set descr 'Check sequence number validation in RST packets'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+rst_body()
+{
+ pft_init
+ vnet_init_bridge
+
+ epair_srv=$(vnet_mkepair)
+ epair_cl=$(vnet_mkepair)
+ epair_attack=$(vnet_mkepair)
+
+ br=$(vnet_mkbridge)
+ ifconfig ${br} addm ${epair_srv}a
+ ifconfig ${epair_srv}a up
+ ifconfig ${br} addm ${epair_cl}a
+ ifconfig ${epair_cl}a up
+ ifconfig ${br} addm ${epair_attack}a
+ ifconfig ${epair_attack}a up
+ ifconfig ${br} up
+
+ vnet_mkjail srv ${epair_srv}b
+ jexec srv ifconfig ${epair_srv}b 192.0.2.1/24 up
+ jexec srv ifconfig lo0 inet 127.0.0.1/8 up
+
+ vnet_mkjail cl ${epair_cl}b
+ jexec cl ifconfig ${epair_cl}b 192.0.2.2/24 up
+ jexec cl ifconfig lo0 inet 127.0.0.1/8 up
+
+ jexec cl pfctl -e
+ pft_set_rules cl \
+ "pass keep state"
+
+ # Not required, but pf should log the bad RST packet with this set.
+ jexec cl pfctl -x loud
+
+ vnet_mkjail attack ${epair_attack}b
+ jexec attack ifconfig ${epair_attack}b 192.0.2.3/24 up
+
+ # Sanity check
+ atf_check -s exit:0 -o ignore \
+ jexec cl ping -c 1 192.0.2.1
+
+ echo "bar" | jexec srv nc -l 1234 &
+ # Allow server time to start
+ sleep 1
+
+ echo "foo" | jexec cl nc -p 4321 192.0.2.1 1234 &
+ # Allow connection time to set up
+ sleep 1
+
+ # Connection should be established now
+ atf_check -s exit:0 -e ignore \
+ -o match:"ESTABLISHED:ESTABLISHED" \
+ jexec cl pfctl -ss -v
+
+ # Now insert a fake RST
+ atf_check -s exit:0 -o ignore \
+ jexec attack ${common_dir}/pft_rst.py 192.0.2.1 1234 192.0.2.2 4321
+
+ # Connection should remain established
+ atf_check -s exit:0 -e ignore \
+ -o match:"ESTABLISHED:ESTABLISHED" \
+ jexec cl pfctl -ss -v
+}
+
+rst_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "rst"
+}
diff --git a/tests/sys/netpfil/pf/tos.sh b/tests/sys/netpfil/pf/tos.sh
new file mode 100644
index 000000000000..47063efee32a
--- /dev/null
+++ b/tests/sys/netpfil/pf/tos.sh
@@ -0,0 +1,94 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2021 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "v4" "cleanup"
+v4_head()
+{
+ atf_set descr 'tos matching test'
+ atf_set require.user root
+}
+
+v4_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a 192.0.2.1/24 up
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up
+ jexec alcatraz pfctl -e
+
+ pft_set_rules alcatraz "pass" \
+ "block in tos va"
+
+ atf_check -s exit:0 -o ignore ping -t 1 -c 1 192.0.2.2
+ atf_check -s exit:2 -o ignore ping -t 1 -c 1 -z 0xb0 192.0.2.2
+}
+
+v4_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "v6" "cleanup"
+v6_head()
+{
+ atf_set descr 'IPv6 tos matching test'
+ atf_set require.user root
+}
+
+v6_body()
+{
+ pft_init
+
+ epair=$(vnet_mkepair)
+ ifconfig ${epair}a inet6 2001:db8:42::1/64 up no_dad -ifdisabled
+
+ vnet_mkjail alcatraz ${epair}b
+ jexec alcatraz ifconfig ${epair}b inet6 2001:db8:42::2/64 \
+ up no_dad -ifdisabled
+ jexec alcatraz pfctl -e
+
+ pft_set_rules alcatraz "pass" \
+ "block in tos va"
+
+ atf_check -s exit:0 -o ignore ping6 -t 1 -c 1 2001:db8:42::2
+ atf_check -s exit:2 -o ignore ping6 -t 1 -c 1 -z 176 2001:db8:42::2
+}
+
+v6_cleanup()
+{
+ pft_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case "v4"
+ atf_add_test_case "v6"
+}
diff --git a/tests/sys/netpfil/pf/utils.py b/tests/sys/netpfil/pf/utils.py
new file mode 100644
index 000000000000..3d1c1de86aad
--- /dev/null
+++ b/tests/sys/netpfil/pf/utils.py
@@ -0,0 +1,46 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2025 Rubicon Communications, LLC (Netgate)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+import threading
+import time
+
+class DelayedSend(threading.Thread):
+ def __init__(self, packet, sendif=None):
+ threading.Thread.__init__(self)
+ self._packet = packet
+ self._sendif = sendif
+
+ self.start()
+
+ def run(self):
+ import scapy.all as sp
+ time.sleep(1)
+
+ if self._sendif:
+ sp.sendp(self._packet, iface=self._sendif)
+ else:
+ sp.send(self._packet)
+
diff --git a/tests/sys/netpfil/pf/utils.subr b/tests/sys/netpfil/pf/utils.subr
new file mode 100644
index 000000000000..3f8d437920f9
--- /dev/null
+++ b/tests/sys/netpfil/pf/utils.subr
@@ -0,0 +1,414 @@
+# Utility functions
+##
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2017 Kristof Provost <kp@FreeBSD.org>
+# Copyright (c) 2023 Kajetan Staszkiewicz <vegeta@tuxpowered.net>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/../../common/vnet.subr
+common_dir=$(atf_get_srcdir)/../common
+
+pft_onerror()
+{
+ status=$?
+
+ echo "Debug log."
+ echo "=========="
+ echo "Test exit status: $?"
+ echo
+
+ if [ -f created_jails.lst ]; then
+ for jailname in `cat created_jails.lst`
+ do
+ echo "Jail ${jailname}"
+ echo "----------------"
+ jexec ${jailname} ifconfig
+ jexec ${jailname} netstat -rn
+ jexec ${jailname} pfctl -sa -v
+ done
+ fi
+
+ echo "Created interfaces:"
+ echo "-------------------"
+ cat created_interfaces.lst
+
+ echo "Host interfaces:"
+ echo "----------------"
+ ifconfig
+}
+
+pft_init()
+{
+ if [ "$1" == "debug" ]
+ then
+ trap pft_onerror EXIT
+ fi
+
+ vnet_init
+
+ if [ ! -c /dev/pf ]; then
+ atf_skip "This test requires pf"
+ fi
+}
+
+pfsynct_init()
+{
+ pft_init
+
+ if ! kldstat -q -m pfsync; then
+ atf_skip "This test requires pfsync"
+ fi
+}
+
+pflog_init()
+{
+ pft_init
+
+ if ! kldstat -q -m pflog; then
+ atf_skip "This test requires pflog"
+ fi
+}
+
+pflow_init()
+{
+ pft_init
+
+ if ! kldstat -q -m pflow; then
+ atf_skip "This test requires pflow"
+ fi
+}
+
+dummynet_init()
+{
+ pft_init
+
+ if ! kldstat -q -m dummynet; then
+ atf_skip "This test requires dummynet"
+ fi
+}
+
+pft_set_rules()
+{
+ jname=$1
+ shift
+
+ if [ $jname == "noflush" ];
+ then
+ jname=$1
+ shift
+ else
+ # Flush all states, rules, fragments, ...
+ jexec ${jname} pfctl -F all
+ fi
+
+ while [ $# -gt 0 ]; do
+ printf "$1\n"
+ shift
+ done | jexec ${jname} pfctl -f -
+ if [ $? -ne 0 ];
+ then
+ atf_fail "Failed to set PF rules in ${jname}"
+ fi
+}
+
+pft_cleanup()
+{
+ vnet_cleanup
+}
+
+pfsynct_cleanup()
+{
+ pft_cleanup
+}
+
+is_altq_supported()
+{
+ sysctl -q kern.features.altq >/dev/null || \
+ atf_skip "Test requires ALTQ"
+
+ while [ -n "$1" ]
+ do
+ sysctl -q kern.features.altq.${1} >/dev/null || \
+ atf_skip "Test required ALTQ_${1}"
+ shift
+ done
+}
+
+altq_init()
+{
+ pft_init
+ is_altq_supported
+}
+
+altq_cleanup()
+{
+ pft_cleanup
+}
+
+# Create a bare router jail.
+# This function lacks target configuration.
+setup_router_ipv4()
+{
+ pft_init
+
+ epair_tester=$(vnet_mkepair)
+ epair_server=$(vnet_mkepair)
+
+ net_tester=192.0.2.0/24
+ net_tester_mask=24
+ net_tester_host_router=192.0.2.1
+ net_tester_host_tester=192.0.2.2
+
+ net_server=198.51.100.0/24
+ net_server_mask=24
+ net_server_host_router=198.51.100.1
+ net_server_host_server=198.51.100.2
+
+ vnet_mkjail router ${epair_tester}b ${epair_server}a
+
+ ifconfig ${epair_tester}a ${net_tester_host_tester}/${net_tester_mask} up
+ route add -net ${net_server} ${net_tester_host_router}
+
+ jexec router ifconfig ${epair_tester}b ${net_tester_host_router}/${net_tester_mask} up
+ jexec router sysctl net.inet.ip.forwarding=1
+ jexec router ifconfig ${epair_server}a ${net_server_host_router}/${net_server_mask} up
+
+ jexec router pfctl -e
+}
+
+# Create a router jail.
+# The target for tests does not exist but a static ARP entry does
+# so packets to it can be properly routed.
+setup_router_dummy_ipv4()
+{
+ setup_router_ipv4
+ jexec router arp -s ${net_server_host_server} 00:01:02:03:04:05
+ ifconfig ${epair_server}b up
+}
+
+# Create a router and a server jail.
+# The server is capable of responding to pings from the tester.
+setup_router_server_ipv4()
+{
+ setup_router_ipv4
+ vnet_mkjail server ${epair_server}b
+ jexec server ifconfig ${epair_server}b ${net_server_host_server}/${net_server_mask} up
+ jexec server route add -net ${net_tester} ${net_server_host_router}
+ inetd_conf=$(mktemp)
+ echo "discard stream tcp nowait root internal" > $inetd_conf
+ jexec server inetd -p ${PWD}/inetd.pid $inetd_conf
+}
+
+# Create a bare router jail.
+# This function lacks target configuration.
+setup_router_ipv6()
+{
+ pft_init
+
+ epair_tester=$(vnet_mkepair)
+ epair_server=$(vnet_mkepair)
+
+ net_tester=2001:db8:42::/64
+ net_tester_mask=64
+ net_tester_host_router=2001:db8:42::1
+ net_tester_host_tester=2001:db8:42::2
+
+ net_server=2001:db8:43::/64
+ net_server_mask=64
+ net_server_host_router=2001:db8:43::1
+ net_server_host_server=2001:db8:43::2
+
+ vnet_mkjail router ${epair_tester}b ${epair_server}a
+
+ ifconfig ${epair_tester}a inet6 ${net_tester_host_tester}/${net_tester_mask}up no_dad
+ route add -6 ${net_server} ${net_tester_host_router}
+
+ jexec router ifconfig ${epair_tester}b inet6 ${net_tester_host_router}/${net_tester_mask} up no_dad
+ jexec router sysctl net.inet6.ip6.forwarding=1
+ jexec router ifconfig ${epair_server}a inet6 ${net_server_host_router}/${net_server_mask} up no_dad
+
+ jexec router pfctl -e
+}
+
+# Create a router jail.
+# The target for tests does not exist but a static NDP entry does
+# so packets to it can be properly routed.
+setup_router_dummy_ipv6()
+{
+ setup_router_ipv6
+ jexec router ndp -s ${net_server_host_server} 00:01:02:03:04:05
+ ifconfig ${epair_server}b up
+}
+
+# Create a router and a server jail.
+# The server is capable of responding to pings from tester.
+setup_router_server_ipv6()
+{
+ setup_router_ipv6
+ vnet_mkjail server ${epair_server}b
+ jexec server ifconfig ${epair_server}b inet6 ${net_server_host_server}/${net_server_mask} up no_dad
+ jexec server route add -6 ${net_tester} ${net_server_host_router}
+ inetd_conf=$(mktemp)
+ echo "discard stream tcp6 nowait root internal" > $inetd_conf
+ jexec server inetd -p ${PWD}/inetd.pid $inetd_conf
+}
+
+# Create a router and 2 server jails for nat64 and rfc5549 test cases.
+# The router is connected to servers, both are dual-stack, and to the
+# tester jail. All links are dual stack.
+setup_router_server_nat64()
+{
+ pft_init
+
+ epair_tester=$(vnet_mkepair)
+ epair_server1=$(vnet_mkepair)
+ epair_server2=$(vnet_mkepair)
+
+ # Funny how IPv4 address space is to small to even assign nice /24
+ # prefixes on all needed networks. On IPv6 we have a separate /64 for
+ # each link, loopback server, and client/SNAT pool. On IPv4 we must
+ # use small /28 prefixes, so even though we define all networks
+ # as variables we can't easily use them in tests if additional addresses
+ # are needed.
+
+ # IP addresses which can be used by the tester jail.
+ # Can be used as SNAT or as source with pft_ping.py. It is up to
+ # the test code to make them accessible from router.
+ net_clients_4=203.0.113
+ net_clients_4_mask=24
+ net_clients_6=2001:db8:44
+ net_clients_6_mask=64
+
+ # IP addresses on loopback interfaces of both servers. They can be
+ # accessed using the route-to targtet.
+ host_server_4=192.0.2.100
+ host_server_6=2001:db8:4203::100
+
+ net_tester_4=198.51.100
+ net_tester_4_mask=28
+ net_tester_4_host_router=198.51.100.1
+ net_tester_4_host_tester=198.51.100.2
+
+ net_tester_6=2001:db8:4200
+ net_tester_6_mask=64
+ net_tester_6_host_router=2001:db8:4200::1
+ net_tester_6_host_tester=2001:db8:4200::2
+
+ net_server1_4=198.51.100
+ net_server1_4_mask=28
+ net_server1_4_host_router=198.51.100.17
+ net_server1_4_host_server=198.51.100.18
+
+ net_server1_6=2001:db8:4201
+ net_server1_6_mask=64
+ net_server1_6_host_router=2001:db8:4201::1
+ net_server1_6_host_server=2001:db8:4201::2
+
+ net_server2_4=198.51.100
+ net_server2_4_mask=28
+ net_server2_4_host_router=198.51.100.33
+ net_server2_4_host_server=198.51.100.34
+
+ net_server2_6=2001:db8:4202
+ net_server2_6_mask=64
+ net_server2_6_host_router=2001:db8:4202::1
+ net_server2_6_host_server=2001:db8:4202::2
+
+ vnet_mkjail router ${epair_tester}b ${epair_server1}a ${epair_server2}a
+ jexec router ifconfig ${epair_tester}b inet ${net_tester_4_host_router}/${net_tester_4_mask} up
+ jexec router ifconfig ${epair_tester}b inet6 ${net_tester_6_host_router}/${net_tester_6_mask} up no_dad
+ jexec router ifconfig ${epair_server1}a inet ${net_server1_4_host_router}/${net_server1_4_mask} up
+ jexec router ifconfig ${epair_server1}a inet6 ${net_server1_6_host_router}/${net_server1_6_mask} up no_dad
+ jexec router ifconfig ${epair_server2}a inet ${net_server2_4_host_router}/${net_server2_4_mask} up
+ jexec router ifconfig ${epair_server2}a inet6 ${net_server2_6_host_router}/${net_server2_6_mask} up no_dad
+ jexec router sysctl net.inet.ip.forwarding=1
+ jexec router sysctl net.inet6.ip6.forwarding=1
+ jexec router pfctl -e
+
+ ifconfig ${epair_tester}a inet ${net_tester_4_host_tester}/${net_tester_4_mask} up
+ ifconfig ${epair_tester}a inet6 ${net_tester_6_host_tester}/${net_tester_6_mask} up no_dad
+ route add 0.0.0.0/0 ${net_tester_4_host_router}
+ route add -6 ::/0 ${net_tester_6_host_router}
+
+ inetd_conf=$(mktemp)
+ echo "discard stream tcp46 nowait root internal" >> $inetd_conf
+
+ vnet_mkjail server1 ${epair_server1}b
+ jexec server1 /etc/rc.d/netif start lo0
+ jexec server1 ifconfig ${epair_server1}b inet ${net_server1_4_host_server}/${net_server1_4_mask} up
+ jexec server1 ifconfig ${epair_server1}b inet6 ${net_server1_6_host_server}/${net_server1_6_mask} up no_dad
+ jexec server1 ifconfig lo0 ${host_server_4}/32 alias
+ jexec server1 ifconfig lo0 inet6 ${host_server_6}/128 alias
+ jexec server1 inetd -p ${PWD}/inetd_1.pid $inetd_conf
+ jexec server1 route add 0.0.0.0/0 ${net_server1_4_host_router}
+
+ jexec server1 route add -6 ::/0 ${net_server1_6_host_router}
+ vnet_mkjail server2 ${epair_server2}b
+ jexec server2 /etc/rc.d/netif start lo0
+ jexec server2 ifconfig ${epair_server2}b inet ${net_server2_4_host_server}/${net_server2_4_mask} up
+ jexec server2 ifconfig ${epair_server2}b inet6 ${net_server2_6_host_server}/${net_server2_6_mask} up no_dad
+ jexec server2 ifconfig lo0 ${host_server_4}/32 alias
+ jexec server2 ifconfig lo0 inet6 ${host_server_6}/128 alias
+ jexec server2 inetd -p ${PWD}/inetd_2.pid $inetd_conf
+ jexec server2 route add 0.0.0.0/0 ${net_server2_4_host_router}
+ jexec server2 route add -6 ::/0 ${net_server2_6_host_router}
+}
+
+# Ping the dummy static NDP target.
+# Check for pings being forwarded through the router towards the target.
+ping_dummy_check_request()
+{
+ exit_condition=$1
+ shift
+ params=$@
+ atf_check -s ${exit_condition} ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a \
+ --to ${net_server_host_server} \
+ --recvif ${epair_server}b \
+ $params
+}
+
+# Ping the server jail.
+# Check for responses coming back throught the router back to the tester.
+ping_server_check_reply()
+{
+ exit_condition=$1
+ shift
+ params=$@
+ atf_check -s ${exit_condition} ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a \
+ --to ${net_server_host_server} \
+ --replyif ${epair_tester}a \
+ $params
+}
+
+normalize_pfctl_s()
+{
+ # `pfctl -s[rsS]` output is divided into sections. Each rule, state or
+ # source node starts with the beginning of a line and next lines with leading
+ # spaces are various parameters of said rule, state or source node.
+ # Convert it into a single line per entry, and remove multiple spaces,
+ # so that regular expressions for matching them in tests can be simpler.
+ awk '{ if ($0 ~ /^[^ ]/ && NR > 1) print(""); gsub(/ +/, " ", $0); printf("%s", $0); } END {print("");}'
+}
diff --git a/tests/sys/opencrypto/Makefile b/tests/sys/opencrypto/Makefile
new file mode 100644
index 000000000000..a9c06ef2a93b
--- /dev/null
+++ b/tests/sys/opencrypto/Makefile
@@ -0,0 +1,22 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/opencrypto
+BINDIR= ${TESTSDIR}
+
+CFLAGS+= -I${SRCTOP}/tests
+CFLAGS.blake2_test.c += -I${SRCTOP}/sys/opencrypto
+CFLAGS.blake2_test.c += -I${SRCTOP}/sys/contrib/libb2
+CFLAGS.poly1305_test.c += -I${SRCTOP}/sys/opencrypto
+
+ATF_TESTS_C+= blake2_test poly1305_test
+
+TAP_TESTS_SH+= runtests
+
+TEST_METADATA.runtests+= required_programs="python3"
+TEST_METADATA.runtests+= required_user="root"
+
+PYMODULES= cryptodev.py cryptodevh.py cryptotest.py
+
+${PACKAGE}FILES+= ${PYMODULES}
+
+.include <bsd.test.mk>
diff --git a/tests/sys/opencrypto/Makefile.depend b/tests/sys/opencrypto/Makefile.depend
new file mode 100644
index 000000000000..462a1562f495
--- /dev/null
+++ b/tests/sys/opencrypto/Makefile.depend
@@ -0,0 +1,16 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/opencrypto/blake2-kat.h b/tests/sys/opencrypto/blake2-kat.h
new file mode 100644
index 000000000000..3d2072763aa9
--- /dev/null
+++ b/tests/sys/opencrypto/blake2-kat.h
@@ -0,0 +1,16467 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ To the extent possible under law, the author(s) have dedicated all copyright
+ and related and neighboring rights to this software to the public domain
+ worldwide. This software is distributed without any warranty.
+
+ You should have received a copy of the CC0 Public Domain Dedication along with
+ this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+#pragma once
+#ifndef __BLAKE2_KAT_H__
+#define __BLAKE2_KAT_H__
+
+
+#include <stdint.h>
+
+#define KAT_LENGTH 256
+
+
+
+static const uint8_t blake2s_kat[KAT_LENGTH][BLAKE2S_OUTBYTES] =
+{
+ {
+ 0x69, 0x21, 0x7A, 0x30, 0x79, 0x90, 0x80, 0x94,
+ 0xE1, 0x11, 0x21, 0xD0, 0x42, 0x35, 0x4A, 0x7C,
+ 0x1F, 0x55, 0xB6, 0x48, 0x2C, 0xA1, 0xA5, 0x1E,
+ 0x1B, 0x25, 0x0D, 0xFD, 0x1E, 0xD0, 0xEE, 0xF9
+ },
+ {
+ 0xE3, 0x4D, 0x74, 0xDB, 0xAF, 0x4F, 0xF4, 0xC6,
+ 0xAB, 0xD8, 0x71, 0xCC, 0x22, 0x04, 0x51, 0xD2,
+ 0xEA, 0x26, 0x48, 0x84, 0x6C, 0x77, 0x57, 0xFB,
+ 0xAA, 0xC8, 0x2F, 0xE5, 0x1A, 0xD6, 0x4B, 0xEA
+ },
+ {
+ 0xDD, 0xAD, 0x9A, 0xB1, 0x5D, 0xAC, 0x45, 0x49,
+ 0xBA, 0x42, 0xF4, 0x9D, 0x26, 0x24, 0x96, 0xBE,
+ 0xF6, 0xC0, 0xBA, 0xE1, 0xDD, 0x34, 0x2A, 0x88,
+ 0x08, 0xF8, 0xEA, 0x26, 0x7C, 0x6E, 0x21, 0x0C
+ },
+ {
+ 0xE8, 0xF9, 0x1C, 0x6E, 0xF2, 0x32, 0xA0, 0x41,
+ 0x45, 0x2A, 0xB0, 0xE1, 0x49, 0x07, 0x0C, 0xDD,
+ 0x7D, 0xD1, 0x76, 0x9E, 0x75, 0xB3, 0xA5, 0x92,
+ 0x1B, 0xE3, 0x78, 0x76, 0xC4, 0x5C, 0x99, 0x00
+ },
+ {
+ 0x0C, 0xC7, 0x0E, 0x00, 0x34, 0x8B, 0x86, 0xBA,
+ 0x29, 0x44, 0xD0, 0xC3, 0x20, 0x38, 0xB2, 0x5C,
+ 0x55, 0x58, 0x4F, 0x90, 0xDF, 0x23, 0x04, 0xF5,
+ 0x5F, 0xA3, 0x32, 0xAF, 0x5F, 0xB0, 0x1E, 0x20
+ },
+ {
+ 0xEC, 0x19, 0x64, 0x19, 0x10, 0x87, 0xA4, 0xFE,
+ 0x9D, 0xF1, 0xC7, 0x95, 0x34, 0x2A, 0x02, 0xFF,
+ 0xC1, 0x91, 0xA5, 0xB2, 0x51, 0x76, 0x48, 0x56,
+ 0xAE, 0x5B, 0x8B, 0x57, 0x69, 0xF0, 0xC6, 0xCD
+ },
+ {
+ 0xE1, 0xFA, 0x51, 0x61, 0x8D, 0x7D, 0xF4, 0xEB,
+ 0x70, 0xCF, 0x0D, 0x5A, 0x9E, 0x90, 0x6F, 0x80,
+ 0x6E, 0x9D, 0x19, 0xF7, 0xF4, 0xF0, 0x1E, 0x3B,
+ 0x62, 0x12, 0x88, 0xE4, 0x12, 0x04, 0x05, 0xD6
+ },
+ {
+ 0x59, 0x80, 0x01, 0xFA, 0xFB, 0xE8, 0xF9, 0x4E,
+ 0xC6, 0x6D, 0xC8, 0x27, 0xD0, 0x12, 0xCF, 0xCB,
+ 0xBA, 0x22, 0x28, 0x56, 0x9F, 0x44, 0x8E, 0x89,
+ 0xEA, 0x22, 0x08, 0xC8, 0xBF, 0x76, 0x92, 0x93
+ },
+ {
+ 0xC7, 0xE8, 0x87, 0xB5, 0x46, 0x62, 0x36, 0x35,
+ 0xE9, 0x3E, 0x04, 0x95, 0x59, 0x8F, 0x17, 0x26,
+ 0x82, 0x19, 0x96, 0xC2, 0x37, 0x77, 0x05, 0xB9,
+ 0x3A, 0x1F, 0x63, 0x6F, 0x87, 0x2B, 0xFA, 0x2D
+ },
+ {
+ 0xC3, 0x15, 0xA4, 0x37, 0xDD, 0x28, 0x06, 0x2A,
+ 0x77, 0x0D, 0x48, 0x19, 0x67, 0x13, 0x6B, 0x1B,
+ 0x5E, 0xB8, 0x8B, 0x21, 0xEE, 0x53, 0xD0, 0x32,
+ 0x9C, 0x58, 0x97, 0x12, 0x6E, 0x9D, 0xB0, 0x2C
+ },
+ {
+ 0xBB, 0x47, 0x3D, 0xED, 0xDC, 0x05, 0x5F, 0xEA,
+ 0x62, 0x28, 0xF2, 0x07, 0xDA, 0x57, 0x53, 0x47,
+ 0xBB, 0x00, 0x40, 0x4C, 0xD3, 0x49, 0xD3, 0x8C,
+ 0x18, 0x02, 0x63, 0x07, 0xA2, 0x24, 0xCB, 0xFF
+ },
+ {
+ 0x68, 0x7E, 0x18, 0x73, 0xA8, 0x27, 0x75, 0x91,
+ 0xBB, 0x33, 0xD9, 0xAD, 0xF9, 0xA1, 0x39, 0x12,
+ 0xEF, 0xEF, 0xE5, 0x57, 0xCA, 0xFC, 0x39, 0xA7,
+ 0x95, 0x26, 0x23, 0xE4, 0x72, 0x55, 0xF1, 0x6D
+ },
+ {
+ 0x1A, 0xC7, 0xBA, 0x75, 0x4D, 0x6E, 0x2F, 0x94,
+ 0xE0, 0xE8, 0x6C, 0x46, 0xBF, 0xB2, 0x62, 0xAB,
+ 0xBB, 0x74, 0xF4, 0x50, 0xEF, 0x45, 0x6D, 0x6B,
+ 0x4D, 0x97, 0xAA, 0x80, 0xCE, 0x6D, 0xA7, 0x67
+ },
+ {
+ 0x01, 0x2C, 0x97, 0x80, 0x96, 0x14, 0x81, 0x6B,
+ 0x5D, 0x94, 0x94, 0x47, 0x7D, 0x4B, 0x68, 0x7D,
+ 0x15, 0xB9, 0x6E, 0xB6, 0x9C, 0x0E, 0x80, 0x74,
+ 0xA8, 0x51, 0x6F, 0x31, 0x22, 0x4B, 0x5C, 0x98
+ },
+ {
+ 0x91, 0xFF, 0xD2, 0x6C, 0xFA, 0x4D, 0xA5, 0x13,
+ 0x4C, 0x7E, 0xA2, 0x62, 0xF7, 0x88, 0x9C, 0x32,
+ 0x9F, 0x61, 0xF6, 0xA6, 0x57, 0x22, 0x5C, 0xC2,
+ 0x12, 0xF4, 0x00, 0x56, 0xD9, 0x86, 0xB3, 0xF4
+ },
+ {
+ 0xD9, 0x7C, 0x82, 0x8D, 0x81, 0x82, 0xA7, 0x21,
+ 0x80, 0xA0, 0x6A, 0x78, 0x26, 0x83, 0x30, 0x67,
+ 0x3F, 0x7C, 0x4E, 0x06, 0x35, 0x94, 0x7C, 0x04,
+ 0xC0, 0x23, 0x23, 0xFD, 0x45, 0xC0, 0xA5, 0x2D
+ },
+ {
+ 0xEF, 0xC0, 0x4C, 0xDC, 0x39, 0x1C, 0x7E, 0x91,
+ 0x19, 0xBD, 0x38, 0x66, 0x8A, 0x53, 0x4E, 0x65,
+ 0xFE, 0x31, 0x03, 0x6D, 0x6A, 0x62, 0x11, 0x2E,
+ 0x44, 0xEB, 0xEB, 0x11, 0xF9, 0xC5, 0x70, 0x80
+ },
+ {
+ 0x99, 0x2C, 0xF5, 0xC0, 0x53, 0x44, 0x2A, 0x5F,
+ 0xBC, 0x4F, 0xAF, 0x58, 0x3E, 0x04, 0xE5, 0x0B,
+ 0xB7, 0x0D, 0x2F, 0x39, 0xFB, 0xB6, 0xA5, 0x03,
+ 0xF8, 0x9E, 0x56, 0xA6, 0x3E, 0x18, 0x57, 0x8A
+ },
+ {
+ 0x38, 0x64, 0x0E, 0x9F, 0x21, 0x98, 0x3E, 0x67,
+ 0xB5, 0x39, 0xCA, 0xCC, 0xAE, 0x5E, 0xCF, 0x61,
+ 0x5A, 0xE2, 0x76, 0x4F, 0x75, 0xA0, 0x9C, 0x9C,
+ 0x59, 0xB7, 0x64, 0x83, 0xC1, 0xFB, 0xC7, 0x35
+ },
+ {
+ 0x21, 0x3D, 0xD3, 0x4C, 0x7E, 0xFE, 0x4F, 0xB2,
+ 0x7A, 0x6B, 0x35, 0xF6, 0xB4, 0x00, 0x0D, 0x1F,
+ 0xE0, 0x32, 0x81, 0xAF, 0x3C, 0x72, 0x3E, 0x5C,
+ 0x9F, 0x94, 0x74, 0x7A, 0x5F, 0x31, 0xCD, 0x3B
+ },
+ {
+ 0xEC, 0x24, 0x6E, 0xEE, 0xB9, 0xCE, 0xD3, 0xF7,
+ 0xAD, 0x33, 0xED, 0x28, 0x66, 0x0D, 0xD9, 0xBB,
+ 0x07, 0x32, 0x51, 0x3D, 0xB4, 0xE2, 0xFA, 0x27,
+ 0x8B, 0x60, 0xCD, 0xE3, 0x68, 0x2A, 0x4C, 0xCD
+ },
+ {
+ 0xAC, 0x9B, 0x61, 0xD4, 0x46, 0x64, 0x8C, 0x30,
+ 0x05, 0xD7, 0x89, 0x2B, 0xF3, 0xA8, 0x71, 0x9F,
+ 0x4C, 0x81, 0x81, 0xCF, 0xDC, 0xBC, 0x2B, 0x79,
+ 0xFE, 0xF1, 0x0A, 0x27, 0x9B, 0x91, 0x10, 0x95
+ },
+ {
+ 0x7B, 0xF8, 0xB2, 0x29, 0x59, 0xE3, 0x4E, 0x3A,
+ 0x43, 0xF7, 0x07, 0x92, 0x23, 0xE8, 0x3A, 0x97,
+ 0x54, 0x61, 0x7D, 0x39, 0x1E, 0x21, 0x3D, 0xFD,
+ 0x80, 0x8E, 0x41, 0xB9, 0xBE, 0xAD, 0x4C, 0xE7
+ },
+ {
+ 0x68, 0xD4, 0xB5, 0xD4, 0xFA, 0x0E, 0x30, 0x2B,
+ 0x64, 0xCC, 0xC5, 0xAF, 0x79, 0x29, 0x13, 0xAC,
+ 0x4C, 0x88, 0xEC, 0x95, 0xC0, 0x7D, 0xDF, 0x40,
+ 0x69, 0x42, 0x56, 0xEB, 0x88, 0xCE, 0x9F, 0x3D
+ },
+ {
+ 0xB2, 0xC2, 0x42, 0x0F, 0x05, 0xF9, 0xAB, 0xE3,
+ 0x63, 0x15, 0x91, 0x93, 0x36, 0xB3, 0x7E, 0x4E,
+ 0x0F, 0xA3, 0x3F, 0xF7, 0xE7, 0x6A, 0x49, 0x27,
+ 0x67, 0x00, 0x6F, 0xDB, 0x5D, 0x93, 0x54, 0x62
+ },
+ {
+ 0x13, 0x4F, 0x61, 0xBB, 0xD0, 0xBB, 0xB6, 0x9A,
+ 0xED, 0x53, 0x43, 0x90, 0x45, 0x51, 0xA3, 0xE6,
+ 0xC1, 0xAA, 0x7D, 0xCD, 0xD7, 0x7E, 0x90, 0x3E,
+ 0x70, 0x23, 0xEB, 0x7C, 0x60, 0x32, 0x0A, 0xA7
+ },
+ {
+ 0x46, 0x93, 0xF9, 0xBF, 0xF7, 0xD4, 0xF3, 0x98,
+ 0x6A, 0x7D, 0x17, 0x6E, 0x6E, 0x06, 0xF7, 0x2A,
+ 0xD1, 0x49, 0x0D, 0x80, 0x5C, 0x99, 0xE2, 0x53,
+ 0x47, 0xB8, 0xDE, 0x77, 0xB4, 0xDB, 0x6D, 0x9B
+ },
+ {
+ 0x85, 0x3E, 0x26, 0xF7, 0x41, 0x95, 0x3B, 0x0F,
+ 0xD5, 0xBD, 0xB4, 0x24, 0xE8, 0xAB, 0x9E, 0x8B,
+ 0x37, 0x50, 0xEA, 0xA8, 0xEF, 0x61, 0xE4, 0x79,
+ 0x02, 0xC9, 0x1E, 0x55, 0x4E, 0x9C, 0x73, 0xB9
+ },
+ {
+ 0xF7, 0xDE, 0x53, 0x63, 0x61, 0xAB, 0xAA, 0x0E,
+ 0x15, 0x81, 0x56, 0xCF, 0x0E, 0xA4, 0xF6, 0x3A,
+ 0x99, 0xB5, 0xE4, 0x05, 0x4F, 0x8F, 0xA4, 0xC9,
+ 0xD4, 0x5F, 0x62, 0x85, 0xCA, 0xD5, 0x56, 0x94
+ },
+ {
+ 0x4C, 0x23, 0x06, 0x08, 0x86, 0x0A, 0x99, 0xAE,
+ 0x8D, 0x7B, 0xD5, 0xC2, 0xCC, 0x17, 0xFA, 0x52,
+ 0x09, 0x6B, 0x9A, 0x61, 0xBE, 0xDB, 0x17, 0xCB,
+ 0x76, 0x17, 0x86, 0x4A, 0xD2, 0x9C, 0xA7, 0xA6
+ },
+ {
+ 0xAE, 0xB9, 0x20, 0xEA, 0x87, 0x95, 0x2D, 0xAD,
+ 0xB1, 0xFB, 0x75, 0x92, 0x91, 0xE3, 0x38, 0x81,
+ 0x39, 0xA8, 0x72, 0x86, 0x50, 0x01, 0x88, 0x6E,
+ 0xD8, 0x47, 0x52, 0xE9, 0x3C, 0x25, 0x0C, 0x2A
+ },
+ {
+ 0xAB, 0xA4, 0xAD, 0x9B, 0x48, 0x0B, 0x9D, 0xF3,
+ 0xD0, 0x8C, 0xA5, 0xE8, 0x7B, 0x0C, 0x24, 0x40,
+ 0xD4, 0xE4, 0xEA, 0x21, 0x22, 0x4C, 0x2E, 0xB4,
+ 0x2C, 0xBA, 0xE4, 0x69, 0xD0, 0x89, 0xB9, 0x31
+ },
+ {
+ 0x05, 0x82, 0x56, 0x07, 0xD7, 0xFD, 0xF2, 0xD8,
+ 0x2E, 0xF4, 0xC3, 0xC8, 0xC2, 0xAE, 0xA9, 0x61,
+ 0xAD, 0x98, 0xD6, 0x0E, 0xDF, 0xF7, 0xD0, 0x18,
+ 0x98, 0x3E, 0x21, 0x20, 0x4C, 0x0D, 0x93, 0xD1
+ },
+ {
+ 0xA7, 0x42, 0xF8, 0xB6, 0xAF, 0x82, 0xD8, 0xA6,
+ 0xCA, 0x23, 0x57, 0xC5, 0xF1, 0xCF, 0x91, 0xDE,
+ 0xFB, 0xD0, 0x66, 0x26, 0x7D, 0x75, 0xC0, 0x48,
+ 0xB3, 0x52, 0x36, 0x65, 0x85, 0x02, 0x59, 0x62
+ },
+ {
+ 0x2B, 0xCA, 0xC8, 0x95, 0x99, 0x00, 0x0B, 0x42,
+ 0xC9, 0x5A, 0xE2, 0x38, 0x35, 0xA7, 0x13, 0x70,
+ 0x4E, 0xD7, 0x97, 0x89, 0xC8, 0x4F, 0xEF, 0x14,
+ 0x9A, 0x87, 0x4F, 0xF7, 0x33, 0xF0, 0x17, 0xA2
+ },
+ {
+ 0xAC, 0x1E, 0xD0, 0x7D, 0x04, 0x8F, 0x10, 0x5A,
+ 0x9E, 0x5B, 0x7A, 0xB8, 0x5B, 0x09, 0xA4, 0x92,
+ 0xD5, 0xBA, 0xFF, 0x14, 0xB8, 0xBF, 0xB0, 0xE9,
+ 0xFD, 0x78, 0x94, 0x86, 0xEE, 0xA2, 0xB9, 0x74
+ },
+ {
+ 0xE4, 0x8D, 0x0E, 0xCF, 0xAF, 0x49, 0x7D, 0x5B,
+ 0x27, 0xC2, 0x5D, 0x99, 0xE1, 0x56, 0xCB, 0x05,
+ 0x79, 0xD4, 0x40, 0xD6, 0xE3, 0x1F, 0xB6, 0x24,
+ 0x73, 0x69, 0x6D, 0xBF, 0x95, 0xE0, 0x10, 0xE4
+ },
+ {
+ 0x12, 0xA9, 0x1F, 0xAD, 0xF8, 0xB2, 0x16, 0x44,
+ 0xFD, 0x0F, 0x93, 0x4F, 0x3C, 0x4A, 0x8F, 0x62,
+ 0xBA, 0x86, 0x2F, 0xFD, 0x20, 0xE8, 0xE9, 0x61,
+ 0x15, 0x4C, 0x15, 0xC1, 0x38, 0x84, 0xED, 0x3D
+ },
+ {
+ 0x7C, 0xBE, 0xE9, 0x6E, 0x13, 0x98, 0x97, 0xDC,
+ 0x98, 0xFB, 0xEF, 0x3B, 0xE8, 0x1A, 0xD4, 0xD9,
+ 0x64, 0xD2, 0x35, 0xCB, 0x12, 0x14, 0x1F, 0xB6,
+ 0x67, 0x27, 0xE6, 0xE5, 0xDF, 0x73, 0xA8, 0x78
+ },
+ {
+ 0xEB, 0xF6, 0x6A, 0xBB, 0x59, 0x7A, 0xE5, 0x72,
+ 0xA7, 0x29, 0x7C, 0xB0, 0x87, 0x1E, 0x35, 0x5A,
+ 0xCC, 0xAF, 0xAD, 0x83, 0x77, 0xB8, 0xE7, 0x8B,
+ 0xF1, 0x64, 0xCE, 0x2A, 0x18, 0xDE, 0x4B, 0xAF
+ },
+ {
+ 0x71, 0xB9, 0x33, 0xB0, 0x7E, 0x4F, 0xF7, 0x81,
+ 0x8C, 0xE0, 0x59, 0xD0, 0x08, 0x82, 0x9E, 0x45,
+ 0x3C, 0x6F, 0xF0, 0x2E, 0xC0, 0xA7, 0xDB, 0x39,
+ 0x3F, 0xC2, 0xD8, 0x70, 0xF3, 0x7A, 0x72, 0x86
+ },
+ {
+ 0x7C, 0xF7, 0xC5, 0x13, 0x31, 0x22, 0x0B, 0x8D,
+ 0x3E, 0xBA, 0xED, 0x9C, 0x29, 0x39, 0x8A, 0x16,
+ 0xD9, 0x81, 0x56, 0xE2, 0x61, 0x3C, 0xB0, 0x88,
+ 0xF2, 0xB0, 0xE0, 0x8A, 0x1B, 0xE4, 0xCF, 0x4F
+ },
+ {
+ 0x3E, 0x41, 0xA1, 0x08, 0xE0, 0xF6, 0x4A, 0xD2,
+ 0x76, 0xB9, 0x79, 0xE1, 0xCE, 0x06, 0x82, 0x79,
+ 0xE1, 0x6F, 0x7B, 0xC7, 0xE4, 0xAA, 0x1D, 0x21,
+ 0x1E, 0x17, 0xB8, 0x11, 0x61, 0xDF, 0x16, 0x02
+ },
+ {
+ 0x88, 0x65, 0x02, 0xA8, 0x2A, 0xB4, 0x7B, 0xA8,
+ 0xD8, 0x67, 0x10, 0xAA, 0x9D, 0xE3, 0xD4, 0x6E,
+ 0xA6, 0x5C, 0x47, 0xAF, 0x6E, 0xE8, 0xDE, 0x45,
+ 0x0C, 0xCE, 0xB8, 0xB1, 0x1B, 0x04, 0x5F, 0x50
+ },
+ {
+ 0xC0, 0x21, 0xBC, 0x5F, 0x09, 0x54, 0xFE, 0xE9,
+ 0x4F, 0x46, 0xEA, 0x09, 0x48, 0x7E, 0x10, 0xA8,
+ 0x48, 0x40, 0xD0, 0x2F, 0x64, 0x81, 0x0B, 0xC0,
+ 0x8D, 0x9E, 0x55, 0x1F, 0x7D, 0x41, 0x68, 0x14
+ },
+ {
+ 0x20, 0x30, 0x51, 0x6E, 0x8A, 0x5F, 0xE1, 0x9A,
+ 0xE7, 0x9C, 0x33, 0x6F, 0xCE, 0x26, 0x38, 0x2A,
+ 0x74, 0x9D, 0x3F, 0xD0, 0xEC, 0x91, 0xE5, 0x37,
+ 0xD4, 0xBD, 0x23, 0x58, 0xC1, 0x2D, 0xFB, 0x22
+ },
+ {
+ 0x55, 0x66, 0x98, 0xDA, 0xC8, 0x31, 0x7F, 0xD3,
+ 0x6D, 0xFB, 0xDF, 0x25, 0xA7, 0x9C, 0xB1, 0x12,
+ 0xD5, 0x42, 0x58, 0x60, 0x60, 0x5C, 0xBA, 0xF5,
+ 0x07, 0xF2, 0x3B, 0xF7, 0xE9, 0xF4, 0x2A, 0xFE
+ },
+ {
+ 0x2F, 0x86, 0x7B, 0xA6, 0x77, 0x73, 0xFD, 0xC3,
+ 0xE9, 0x2F, 0xCE, 0xD9, 0x9A, 0x64, 0x09, 0xAD,
+ 0x39, 0xD0, 0xB8, 0x80, 0xFD, 0xE8, 0xF1, 0x09,
+ 0xA8, 0x17, 0x30, 0xC4, 0x45, 0x1D, 0x01, 0x78
+ },
+ {
+ 0x17, 0x2E, 0xC2, 0x18, 0xF1, 0x19, 0xDF, 0xAE,
+ 0x98, 0x89, 0x6D, 0xFF, 0x29, 0xDD, 0x98, 0x76,
+ 0xC9, 0x4A, 0xF8, 0x74, 0x17, 0xF9, 0xAE, 0x4C,
+ 0x70, 0x14, 0xBB, 0x4E, 0x4B, 0x96, 0xAF, 0xC7
+ },
+ {
+ 0x3F, 0x85, 0x81, 0x4A, 0x18, 0x19, 0x5F, 0x87,
+ 0x9A, 0xA9, 0x62, 0xF9, 0x5D, 0x26, 0xBD, 0x82,
+ 0xA2, 0x78, 0xF2, 0xB8, 0x23, 0x20, 0x21, 0x8F,
+ 0x6B, 0x3B, 0xD6, 0xF7, 0xF6, 0x67, 0xA6, 0xD9
+ },
+ {
+ 0x1B, 0x61, 0x8F, 0xBA, 0xA5, 0x66, 0xB3, 0xD4,
+ 0x98, 0xC1, 0x2E, 0x98, 0x2C, 0x9E, 0xC5, 0x2E,
+ 0x4D, 0xA8, 0x5A, 0x8C, 0x54, 0xF3, 0x8F, 0x34,
+ 0xC0, 0x90, 0x39, 0x4F, 0x23, 0xC1, 0x84, 0xC1
+ },
+ {
+ 0x0C, 0x75, 0x8F, 0xB5, 0x69, 0x2F, 0xFD, 0x41,
+ 0xA3, 0x57, 0x5D, 0x0A, 0xF0, 0x0C, 0xC7, 0xFB,
+ 0xF2, 0xCB, 0xE5, 0x90, 0x5A, 0x58, 0x32, 0x3A,
+ 0x88, 0xAE, 0x42, 0x44, 0xF6, 0xE4, 0xC9, 0x93
+ },
+ {
+ 0xA9, 0x31, 0x36, 0x0C, 0xAD, 0x62, 0x8C, 0x7F,
+ 0x12, 0xA6, 0xC1, 0xC4, 0xB7, 0x53, 0xB0, 0xF4,
+ 0x06, 0x2A, 0xEF, 0x3C, 0xE6, 0x5A, 0x1A, 0xE3,
+ 0xF1, 0x93, 0x69, 0xDA, 0xDF, 0x3A, 0xE2, 0x3D
+ },
+ {
+ 0xCB, 0xAC, 0x7D, 0x77, 0x3B, 0x1E, 0x3B, 0x3C,
+ 0x66, 0x91, 0xD7, 0xAB, 0xB7, 0xE9, 0xDF, 0x04,
+ 0x5C, 0x8B, 0xA1, 0x92, 0x68, 0xDE, 0xD1, 0x53,
+ 0x20, 0x7F, 0x5E, 0x80, 0x43, 0x52, 0xEC, 0x5D
+ },
+ {
+ 0x23, 0xA1, 0x96, 0xD3, 0x80, 0x2E, 0xD3, 0xC1,
+ 0xB3, 0x84, 0x01, 0x9A, 0x82, 0x32, 0x58, 0x40,
+ 0xD3, 0x2F, 0x71, 0x95, 0x0C, 0x45, 0x80, 0xB0,
+ 0x34, 0x45, 0xE0, 0x89, 0x8E, 0x14, 0x05, 0x3C
+ },
+ {
+ 0xF4, 0x49, 0x54, 0x70, 0xF2, 0x26, 0xC8, 0xC2,
+ 0x14, 0xBE, 0x08, 0xFD, 0xFA, 0xD4, 0xBC, 0x4A,
+ 0x2A, 0x9D, 0xBE, 0xA9, 0x13, 0x6A, 0x21, 0x0D,
+ 0xF0, 0xD4, 0xB6, 0x49, 0x29, 0xE6, 0xFC, 0x14
+ },
+ {
+ 0xE2, 0x90, 0xDD, 0x27, 0x0B, 0x46, 0x7F, 0x34,
+ 0xAB, 0x1C, 0x00, 0x2D, 0x34, 0x0F, 0xA0, 0x16,
+ 0x25, 0x7F, 0xF1, 0x9E, 0x58, 0x33, 0xFD, 0xBB,
+ 0xF2, 0xCB, 0x40, 0x1C, 0x3B, 0x28, 0x17, 0xDE
+ },
+ {
+ 0x9F, 0xC7, 0xB5, 0xDE, 0xD3, 0xC1, 0x50, 0x42,
+ 0xB2, 0xA6, 0x58, 0x2D, 0xC3, 0x9B, 0xE0, 0x16,
+ 0xD2, 0x4A, 0x68, 0x2D, 0x5E, 0x61, 0xAD, 0x1E,
+ 0xFF, 0x9C, 0x63, 0x30, 0x98, 0x48, 0xF7, 0x06
+ },
+ {
+ 0x8C, 0xCA, 0x67, 0xA3, 0x6D, 0x17, 0xD5, 0xE6,
+ 0x34, 0x1C, 0xB5, 0x92, 0xFD, 0x7B, 0xEF, 0x99,
+ 0x26, 0xC9, 0xE3, 0xAA, 0x10, 0x27, 0xEA, 0x11,
+ 0xA7, 0xD8, 0xBD, 0x26, 0x0B, 0x57, 0x6E, 0x04
+ },
+ {
+ 0x40, 0x93, 0x92, 0xF5, 0x60, 0xF8, 0x68, 0x31,
+ 0xDA, 0x43, 0x73, 0xEE, 0x5E, 0x00, 0x74, 0x26,
+ 0x05, 0x95, 0xD7, 0xBC, 0x24, 0x18, 0x3B, 0x60,
+ 0xED, 0x70, 0x0D, 0x45, 0x83, 0xD3, 0xF6, 0xF0
+ },
+ {
+ 0x28, 0x02, 0x16, 0x5D, 0xE0, 0x90, 0x91, 0x55,
+ 0x46, 0xF3, 0x39, 0x8C, 0xD8, 0x49, 0x16, 0x4A,
+ 0x19, 0xF9, 0x2A, 0xDB, 0xC3, 0x61, 0xAD, 0xC9,
+ 0x9B, 0x0F, 0x20, 0xC8, 0xEA, 0x07, 0x10, 0x54
+ },
+ {
+ 0xAD, 0x83, 0x91, 0x68, 0xD9, 0xF8, 0xA4, 0xBE,
+ 0x95, 0xBA, 0x9E, 0xF9, 0xA6, 0x92, 0xF0, 0x72,
+ 0x56, 0xAE, 0x43, 0xFE, 0x6F, 0x98, 0x64, 0xE2,
+ 0x90, 0x69, 0x1B, 0x02, 0x56, 0xCE, 0x50, 0xA9
+ },
+ {
+ 0x75, 0xFD, 0xAA, 0x50, 0x38, 0xC2, 0x84, 0xB8,
+ 0x6D, 0x6E, 0x8A, 0xFF, 0xE8, 0xB2, 0x80, 0x7E,
+ 0x46, 0x7B, 0x86, 0x60, 0x0E, 0x79, 0xAF, 0x36,
+ 0x89, 0xFB, 0xC0, 0x63, 0x28, 0xCB, 0xF8, 0x94
+ },
+ {
+ 0xE5, 0x7C, 0xB7, 0x94, 0x87, 0xDD, 0x57, 0x90,
+ 0x24, 0x32, 0xB2, 0x50, 0x73, 0x38, 0x13, 0xBD,
+ 0x96, 0xA8, 0x4E, 0xFC, 0xE5, 0x9F, 0x65, 0x0F,
+ 0xAC, 0x26, 0xE6, 0x69, 0x6A, 0xEF, 0xAF, 0xC3
+ },
+ {
+ 0x56, 0xF3, 0x4E, 0x8B, 0x96, 0x55, 0x7E, 0x90,
+ 0xC1, 0xF2, 0x4B, 0x52, 0xD0, 0xC8, 0x9D, 0x51,
+ 0x08, 0x6A, 0xCF, 0x1B, 0x00, 0xF6, 0x34, 0xCF,
+ 0x1D, 0xDE, 0x92, 0x33, 0xB8, 0xEA, 0xAA, 0x3E
+ },
+ {
+ 0x1B, 0x53, 0xEE, 0x94, 0xAA, 0xF3, 0x4E, 0x4B,
+ 0x15, 0x9D, 0x48, 0xDE, 0x35, 0x2C, 0x7F, 0x06,
+ 0x61, 0xD0, 0xA4, 0x0E, 0xDF, 0xF9, 0x5A, 0x0B,
+ 0x16, 0x39, 0xB4, 0x09, 0x0E, 0x97, 0x44, 0x72
+ },
+ {
+ 0x05, 0x70, 0x5E, 0x2A, 0x81, 0x75, 0x7C, 0x14,
+ 0xBD, 0x38, 0x3E, 0xA9, 0x8D, 0xDA, 0x54, 0x4E,
+ 0xB1, 0x0E, 0x6B, 0xC0, 0x7B, 0xAE, 0x43, 0x5E,
+ 0x25, 0x18, 0xDB, 0xE1, 0x33, 0x52, 0x53, 0x75
+ },
+ {
+ 0xD8, 0xB2, 0x86, 0x6E, 0x8A, 0x30, 0x9D, 0xB5,
+ 0x3E, 0x52, 0x9E, 0xC3, 0x29, 0x11, 0xD8, 0x2F,
+ 0x5C, 0xA1, 0x6C, 0xFF, 0x76, 0x21, 0x68, 0x91,
+ 0xA9, 0x67, 0x6A, 0xA3, 0x1A, 0xAA, 0x6C, 0x42
+ },
+ {
+ 0xF5, 0x04, 0x1C, 0x24, 0x12, 0x70, 0xEB, 0x04,
+ 0xC7, 0x1E, 0xC2, 0xC9, 0x5D, 0x4C, 0x38, 0xD8,
+ 0x03, 0xB1, 0x23, 0x7B, 0x0F, 0x29, 0xFD, 0x4D,
+ 0xB3, 0xEB, 0x39, 0x76, 0x69, 0xE8, 0x86, 0x99
+ },
+ {
+ 0x9A, 0x4C, 0xE0, 0x77, 0xC3, 0x49, 0x32, 0x2F,
+ 0x59, 0x5E, 0x0E, 0xE7, 0x9E, 0xD0, 0xDA, 0x5F,
+ 0xAB, 0x66, 0x75, 0x2C, 0xBF, 0xEF, 0x8F, 0x87,
+ 0xD0, 0xE9, 0xD0, 0x72, 0x3C, 0x75, 0x30, 0xDD
+ },
+ {
+ 0x65, 0x7B, 0x09, 0xF3, 0xD0, 0xF5, 0x2B, 0x5B,
+ 0x8F, 0x2F, 0x97, 0x16, 0x3A, 0x0E, 0xDF, 0x0C,
+ 0x04, 0xF0, 0x75, 0x40, 0x8A, 0x07, 0xBB, 0xEB,
+ 0x3A, 0x41, 0x01, 0xA8, 0x91, 0x99, 0x0D, 0x62
+ },
+ {
+ 0x1E, 0x3F, 0x7B, 0xD5, 0xA5, 0x8F, 0xA5, 0x33,
+ 0x34, 0x4A, 0xA8, 0xED, 0x3A, 0xC1, 0x22, 0xBB,
+ 0x9E, 0x70, 0xD4, 0xEF, 0x50, 0xD0, 0x04, 0x53,
+ 0x08, 0x21, 0x94, 0x8F, 0x5F, 0xE6, 0x31, 0x5A
+ },
+ {
+ 0x80, 0xDC, 0xCF, 0x3F, 0xD8, 0x3D, 0xFD, 0x0D,
+ 0x35, 0xAA, 0x28, 0x58, 0x59, 0x22, 0xAB, 0x89,
+ 0xD5, 0x31, 0x39, 0x97, 0x67, 0x3E, 0xAF, 0x90,
+ 0x5C, 0xEA, 0x9C, 0x0B, 0x22, 0x5C, 0x7B, 0x5F
+ },
+ {
+ 0x8A, 0x0D, 0x0F, 0xBF, 0x63, 0x77, 0xD8, 0x3B,
+ 0xB0, 0x8B, 0x51, 0x4B, 0x4B, 0x1C, 0x43, 0xAC,
+ 0xC9, 0x5D, 0x75, 0x17, 0x14, 0xF8, 0x92, 0x56,
+ 0x45, 0xCB, 0x6B, 0xC8, 0x56, 0xCA, 0x15, 0x0A
+ },
+ {
+ 0x9F, 0xA5, 0xB4, 0x87, 0x73, 0x8A, 0xD2, 0x84,
+ 0x4C, 0xC6, 0x34, 0x8A, 0x90, 0x19, 0x18, 0xF6,
+ 0x59, 0xA3, 0xB8, 0x9E, 0x9C, 0x0D, 0xFE, 0xEA,
+ 0xD3, 0x0D, 0xD9, 0x4B, 0xCF, 0x42, 0xEF, 0x8E
+ },
+ {
+ 0x80, 0x83, 0x2C, 0x4A, 0x16, 0x77, 0xF5, 0xEA,
+ 0x25, 0x60, 0xF6, 0x68, 0xE9, 0x35, 0x4D, 0xD3,
+ 0x69, 0x97, 0xF0, 0x37, 0x28, 0xCF, 0xA5, 0x5E,
+ 0x1B, 0x38, 0x33, 0x7C, 0x0C, 0x9E, 0xF8, 0x18
+ },
+ {
+ 0xAB, 0x37, 0xDD, 0xB6, 0x83, 0x13, 0x7E, 0x74,
+ 0x08, 0x0D, 0x02, 0x6B, 0x59, 0x0B, 0x96, 0xAE,
+ 0x9B, 0xB4, 0x47, 0x72, 0x2F, 0x30, 0x5A, 0x5A,
+ 0xC5, 0x70, 0xEC, 0x1D, 0xF9, 0xB1, 0x74, 0x3C
+ },
+ {
+ 0x3E, 0xE7, 0x35, 0xA6, 0x94, 0xC2, 0x55, 0x9B,
+ 0x69, 0x3A, 0xA6, 0x86, 0x29, 0x36, 0x1E, 0x15,
+ 0xD1, 0x22, 0x65, 0xAD, 0x6A, 0x3D, 0xED, 0xF4,
+ 0x88, 0xB0, 0xB0, 0x0F, 0xAC, 0x97, 0x54, 0xBA
+ },
+ {
+ 0xD6, 0xFC, 0xD2, 0x32, 0x19, 0xB6, 0x47, 0xE4,
+ 0xCB, 0xD5, 0xEB, 0x2D, 0x0A, 0xD0, 0x1E, 0xC8,
+ 0x83, 0x8A, 0x4B, 0x29, 0x01, 0xFC, 0x32, 0x5C,
+ 0xC3, 0x70, 0x19, 0x81, 0xCA, 0x6C, 0x88, 0x8B
+ },
+ {
+ 0x05, 0x20, 0xEC, 0x2F, 0x5B, 0xF7, 0xA7, 0x55,
+ 0xDA, 0xCB, 0x50, 0xC6, 0xBF, 0x23, 0x3E, 0x35,
+ 0x15, 0x43, 0x47, 0x63, 0xDB, 0x01, 0x39, 0xCC,
+ 0xD9, 0xFA, 0xEF, 0xBB, 0x82, 0x07, 0x61, 0x2D
+ },
+ {
+ 0xAF, 0xF3, 0xB7, 0x5F, 0x3F, 0x58, 0x12, 0x64,
+ 0xD7, 0x66, 0x16, 0x62, 0xB9, 0x2F, 0x5A, 0xD3,
+ 0x7C, 0x1D, 0x32, 0xBD, 0x45, 0xFF, 0x81, 0xA4,
+ 0xED, 0x8A, 0xDC, 0x9E, 0xF3, 0x0D, 0xD9, 0x89
+ },
+ {
+ 0xD0, 0xDD, 0x65, 0x0B, 0xEF, 0xD3, 0xBA, 0x63,
+ 0xDC, 0x25, 0x10, 0x2C, 0x62, 0x7C, 0x92, 0x1B,
+ 0x9C, 0xBE, 0xB0, 0xB1, 0x30, 0x68, 0x69, 0x35,
+ 0xB5, 0xC9, 0x27, 0xCB, 0x7C, 0xCD, 0x5E, 0x3B
+ },
+ {
+ 0xE1, 0x14, 0x98, 0x16, 0xB1, 0x0A, 0x85, 0x14,
+ 0xFB, 0x3E, 0x2C, 0xAB, 0x2C, 0x08, 0xBE, 0xE9,
+ 0xF7, 0x3C, 0xE7, 0x62, 0x21, 0x70, 0x12, 0x46,
+ 0xA5, 0x89, 0xBB, 0xB6, 0x73, 0x02, 0xD8, 0xA9
+ },
+ {
+ 0x7D, 0xA3, 0xF4, 0x41, 0xDE, 0x90, 0x54, 0x31,
+ 0x7E, 0x72, 0xB5, 0xDB, 0xF9, 0x79, 0xDA, 0x01,
+ 0xE6, 0xBC, 0xEE, 0xBB, 0x84, 0x78, 0xEA, 0xE6,
+ 0xA2, 0x28, 0x49, 0xD9, 0x02, 0x92, 0x63, 0x5C
+ },
+ {
+ 0x12, 0x30, 0xB1, 0xFC, 0x8A, 0x7D, 0x92, 0x15,
+ 0xED, 0xC2, 0xD4, 0xA2, 0xDE, 0xCB, 0xDD, 0x0A,
+ 0x6E, 0x21, 0x6C, 0x92, 0x42, 0x78, 0xC9, 0x1F,
+ 0xC5, 0xD1, 0x0E, 0x7D, 0x60, 0x19, 0x2D, 0x94
+ },
+ {
+ 0x57, 0x50, 0xD7, 0x16, 0xB4, 0x80, 0x8F, 0x75,
+ 0x1F, 0xEB, 0xC3, 0x88, 0x06, 0xBA, 0x17, 0x0B,
+ 0xF6, 0xD5, 0x19, 0x9A, 0x78, 0x16, 0xBE, 0x51,
+ 0x4E, 0x3F, 0x93, 0x2F, 0xBE, 0x0C, 0xB8, 0x71
+ },
+ {
+ 0x6F, 0xC5, 0x9B, 0x2F, 0x10, 0xFE, 0xBA, 0x95,
+ 0x4A, 0xA6, 0x82, 0x0B, 0x3C, 0xA9, 0x87, 0xEE,
+ 0x81, 0xD5, 0xCC, 0x1D, 0xA3, 0xC6, 0x3C, 0xE8,
+ 0x27, 0x30, 0x1C, 0x56, 0x9D, 0xFB, 0x39, 0xCE
+ },
+ {
+ 0xC7, 0xC3, 0xFE, 0x1E, 0xEB, 0xDC, 0x7B, 0x5A,
+ 0x93, 0x93, 0x26, 0xE8, 0xDD, 0xB8, 0x3E, 0x8B,
+ 0xF2, 0xB7, 0x80, 0xB6, 0x56, 0x78, 0xCB, 0x62,
+ 0xF2, 0x08, 0xB0, 0x40, 0xAB, 0xDD, 0x35, 0xE2
+ },
+ {
+ 0x0C, 0x75, 0xC1, 0xA1, 0x5C, 0xF3, 0x4A, 0x31,
+ 0x4E, 0xE4, 0x78, 0xF4, 0xA5, 0xCE, 0x0B, 0x8A,
+ 0x6B, 0x36, 0x52, 0x8E, 0xF7, 0xA8, 0x20, 0x69,
+ 0x6C, 0x3E, 0x42, 0x46, 0xC5, 0xA1, 0x58, 0x64
+ },
+ {
+ 0x21, 0x6D, 0xC1, 0x2A, 0x10, 0x85, 0x69, 0xA3,
+ 0xC7, 0xCD, 0xDE, 0x4A, 0xED, 0x43, 0xA6, 0xC3,
+ 0x30, 0x13, 0x9D, 0xDA, 0x3C, 0xCC, 0x4A, 0x10,
+ 0x89, 0x05, 0xDB, 0x38, 0x61, 0x89, 0x90, 0x50
+ },
+ {
+ 0xA5, 0x7B, 0xE6, 0xAE, 0x67, 0x56, 0xF2, 0x8B,
+ 0x02, 0xF5, 0x9D, 0xAD, 0xF7, 0xE0, 0xD7, 0xD8,
+ 0x80, 0x7F, 0x10, 0xFA, 0x15, 0xCE, 0xD1, 0xAD,
+ 0x35, 0x85, 0x52, 0x1A, 0x1D, 0x99, 0x5A, 0x89
+ },
+ {
+ 0x81, 0x6A, 0xEF, 0x87, 0x59, 0x53, 0x71, 0x6C,
+ 0xD7, 0xA5, 0x81, 0xF7, 0x32, 0xF5, 0x3D, 0xD4,
+ 0x35, 0xDA, 0xB6, 0x6D, 0x09, 0xC3, 0x61, 0xD2,
+ 0xD6, 0x59, 0x2D, 0xE1, 0x77, 0x55, 0xD8, 0xA8
+ },
+ {
+ 0x9A, 0x76, 0x89, 0x32, 0x26, 0x69, 0x3B, 0x6E,
+ 0xA9, 0x7E, 0x6A, 0x73, 0x8F, 0x9D, 0x10, 0xFB,
+ 0x3D, 0x0B, 0x43, 0xAE, 0x0E, 0x8B, 0x7D, 0x81,
+ 0x23, 0xEA, 0x76, 0xCE, 0x97, 0x98, 0x9C, 0x7E
+ },
+ {
+ 0x8D, 0xAE, 0xDB, 0x9A, 0x27, 0x15, 0x29, 0xDB,
+ 0xB7, 0xDC, 0x3B, 0x60, 0x7F, 0xE5, 0xEB, 0x2D,
+ 0x32, 0x11, 0x77, 0x07, 0x58, 0xDD, 0x3B, 0x0A,
+ 0x35, 0x93, 0xD2, 0xD7, 0x95, 0x4E, 0x2D, 0x5B
+ },
+ {
+ 0x16, 0xDB, 0xC0, 0xAA, 0x5D, 0xD2, 0xC7, 0x74,
+ 0xF5, 0x05, 0x10, 0x0F, 0x73, 0x37, 0x86, 0xD8,
+ 0xA1, 0x75, 0xFC, 0xBB, 0xB5, 0x9C, 0x43, 0xE1,
+ 0xFB, 0xFF, 0x3E, 0x1E, 0xAF, 0x31, 0xCB, 0x4A
+ },
+ {
+ 0x86, 0x06, 0xCB, 0x89, 0x9C, 0x6A, 0xEA, 0xF5,
+ 0x1B, 0x9D, 0xB0, 0xFE, 0x49, 0x24, 0xA9, 0xFD,
+ 0x5D, 0xAB, 0xC1, 0x9F, 0x88, 0x26, 0xF2, 0xBC,
+ 0x1C, 0x1D, 0x7D, 0xA1, 0x4D, 0x2C, 0x2C, 0x99
+ },
+ {
+ 0x84, 0x79, 0x73, 0x1A, 0xED, 0xA5, 0x7B, 0xD3,
+ 0x7E, 0xAD, 0xB5, 0x1A, 0x50, 0x7E, 0x30, 0x7F,
+ 0x3B, 0xD9, 0x5E, 0x69, 0xDB, 0xCA, 0x94, 0xF3,
+ 0xBC, 0x21, 0x72, 0x60, 0x66, 0xAD, 0x6D, 0xFD
+ },
+ {
+ 0x58, 0x47, 0x3A, 0x9E, 0xA8, 0x2E, 0xFA, 0x3F,
+ 0x3B, 0x3D, 0x8F, 0xC8, 0x3E, 0xD8, 0x86, 0x31,
+ 0x27, 0xB3, 0x3A, 0xE8, 0xDE, 0xAE, 0x63, 0x07,
+ 0x20, 0x1E, 0xDB, 0x6D, 0xDE, 0x61, 0xDE, 0x29
+ },
+ {
+ 0x9A, 0x92, 0x55, 0xD5, 0x3A, 0xF1, 0x16, 0xDE,
+ 0x8B, 0xA2, 0x7C, 0xE3, 0x5B, 0x4C, 0x7E, 0x15,
+ 0x64, 0x06, 0x57, 0xA0, 0xFC, 0xB8, 0x88, 0xC7,
+ 0x0D, 0x95, 0x43, 0x1D, 0xAC, 0xD8, 0xF8, 0x30
+ },
+ {
+ 0x9E, 0xB0, 0x5F, 0xFB, 0xA3, 0x9F, 0xD8, 0x59,
+ 0x6A, 0x45, 0x49, 0x3E, 0x18, 0xD2, 0x51, 0x0B,
+ 0xF3, 0xEF, 0x06, 0x5C, 0x51, 0xD6, 0xE1, 0x3A,
+ 0xBE, 0x66, 0xAA, 0x57, 0xE0, 0x5C, 0xFD, 0xB7
+ },
+ {
+ 0x81, 0xDC, 0xC3, 0xA5, 0x05, 0xEA, 0xCE, 0x3F,
+ 0x87, 0x9D, 0x8F, 0x70, 0x27, 0x76, 0x77, 0x0F,
+ 0x9D, 0xF5, 0x0E, 0x52, 0x1D, 0x14, 0x28, 0xA8,
+ 0x5D, 0xAF, 0x04, 0xF9, 0xAD, 0x21, 0x50, 0xE0
+ },
+ {
+ 0xE3, 0xE3, 0xC4, 0xAA, 0x3A, 0xCB, 0xBC, 0x85,
+ 0x33, 0x2A, 0xF9, 0xD5, 0x64, 0xBC, 0x24, 0x16,
+ 0x5E, 0x16, 0x87, 0xF6, 0xB1, 0xAD, 0xCB, 0xFA,
+ 0xE7, 0x7A, 0x8F, 0x03, 0xC7, 0x2A, 0xC2, 0x8C
+ },
+ {
+ 0x67, 0x46, 0xC8, 0x0B, 0x4E, 0xB5, 0x6A, 0xEA,
+ 0x45, 0xE6, 0x4E, 0x72, 0x89, 0xBB, 0xA3, 0xED,
+ 0xBF, 0x45, 0xEC, 0xF8, 0x20, 0x64, 0x81, 0xFF,
+ 0x63, 0x02, 0x12, 0x29, 0x84, 0xCD, 0x52, 0x6A
+ },
+ {
+ 0x2B, 0x62, 0x8E, 0x52, 0x76, 0x4D, 0x7D, 0x62,
+ 0xC0, 0x86, 0x8B, 0x21, 0x23, 0x57, 0xCD, 0xD1,
+ 0x2D, 0x91, 0x49, 0x82, 0x2F, 0x4E, 0x98, 0x45,
+ 0xD9, 0x18, 0xA0, 0x8D, 0x1A, 0xE9, 0x90, 0xC0
+ },
+ {
+ 0xE4, 0xBF, 0xE8, 0x0D, 0x58, 0xC9, 0x19, 0x94,
+ 0x61, 0x39, 0x09, 0xDC, 0x4B, 0x1A, 0x12, 0x49,
+ 0x68, 0x96, 0xC0, 0x04, 0xAF, 0x7B, 0x57, 0x01,
+ 0x48, 0x3D, 0xE4, 0x5D, 0x28, 0x23, 0xD7, 0x8E
+ },
+ {
+ 0xEB, 0xB4, 0xBA, 0x15, 0x0C, 0xEF, 0x27, 0x34,
+ 0x34, 0x5B, 0x5D, 0x64, 0x1B, 0xBE, 0xD0, 0x3A,
+ 0x21, 0xEA, 0xFA, 0xE9, 0x33, 0xC9, 0x9E, 0x00,
+ 0x92, 0x12, 0xEF, 0x04, 0x57, 0x4A, 0x85, 0x30
+ },
+ {
+ 0x39, 0x66, 0xEC, 0x73, 0xB1, 0x54, 0xAC, 0xC6,
+ 0x97, 0xAC, 0x5C, 0xF5, 0xB2, 0x4B, 0x40, 0xBD,
+ 0xB0, 0xDB, 0x9E, 0x39, 0x88, 0x36, 0xD7, 0x6D,
+ 0x4B, 0x88, 0x0E, 0x3B, 0x2A, 0xF1, 0xAA, 0x27
+ },
+ {
+ 0xEF, 0x7E, 0x48, 0x31, 0xB3, 0xA8, 0x46, 0x36,
+ 0x51, 0x8D, 0x6E, 0x4B, 0xFC, 0xE6, 0x4A, 0x43,
+ 0xDB, 0x2A, 0x5D, 0xDA, 0x9C, 0xCA, 0x2B, 0x44,
+ 0xF3, 0x90, 0x33, 0xBD, 0xC4, 0x0D, 0x62, 0x43
+ },
+ {
+ 0x7A, 0xBF, 0x6A, 0xCF, 0x5C, 0x8E, 0x54, 0x9D,
+ 0xDB, 0xB1, 0x5A, 0xE8, 0xD8, 0xB3, 0x88, 0xC1,
+ 0xC1, 0x97, 0xE6, 0x98, 0x73, 0x7C, 0x97, 0x85,
+ 0x50, 0x1E, 0xD1, 0xF9, 0x49, 0x30, 0xB7, 0xD9
+ },
+ {
+ 0x88, 0x01, 0x8D, 0xED, 0x66, 0x81, 0x3F, 0x0C,
+ 0xA9, 0x5D, 0xEF, 0x47, 0x4C, 0x63, 0x06, 0x92,
+ 0x01, 0x99, 0x67, 0xB9, 0xE3, 0x68, 0x88, 0xDA,
+ 0xDD, 0x94, 0x12, 0x47, 0x19, 0xB6, 0x82, 0xF6
+ },
+ {
+ 0x39, 0x30, 0x87, 0x6B, 0x9F, 0xC7, 0x52, 0x90,
+ 0x36, 0xB0, 0x08, 0xB1, 0xB8, 0xBB, 0x99, 0x75,
+ 0x22, 0xA4, 0x41, 0x63, 0x5A, 0x0C, 0x25, 0xEC,
+ 0x02, 0xFB, 0x6D, 0x90, 0x26, 0xE5, 0x5A, 0x97
+ },
+ {
+ 0x0A, 0x40, 0x49, 0xD5, 0x7E, 0x83, 0x3B, 0x56,
+ 0x95, 0xFA, 0xC9, 0x3D, 0xD1, 0xFB, 0xEF, 0x31,
+ 0x66, 0xB4, 0x4B, 0x12, 0xAD, 0x11, 0x24, 0x86,
+ 0x62, 0x38, 0x3A, 0xE0, 0x51, 0xE1, 0x58, 0x27
+ },
+ {
+ 0x81, 0xDC, 0xC0, 0x67, 0x8B, 0xB6, 0xA7, 0x65,
+ 0xE4, 0x8C, 0x32, 0x09, 0x65, 0x4F, 0xE9, 0x00,
+ 0x89, 0xCE, 0x44, 0xFF, 0x56, 0x18, 0x47, 0x7E,
+ 0x39, 0xAB, 0x28, 0x64, 0x76, 0xDF, 0x05, 0x2B
+ },
+ {
+ 0xE6, 0x9B, 0x3A, 0x36, 0xA4, 0x46, 0x19, 0x12,
+ 0xDC, 0x08, 0x34, 0x6B, 0x11, 0xDD, 0xCB, 0x9D,
+ 0xB7, 0x96, 0xF8, 0x85, 0xFD, 0x01, 0x93, 0x6E,
+ 0x66, 0x2F, 0xE2, 0x92, 0x97, 0xB0, 0x99, 0xA4
+ },
+ {
+ 0x5A, 0xC6, 0x50, 0x3B, 0x0D, 0x8D, 0xA6, 0x91,
+ 0x76, 0x46, 0xE6, 0xDC, 0xC8, 0x7E, 0xDC, 0x58,
+ 0xE9, 0x42, 0x45, 0x32, 0x4C, 0xC2, 0x04, 0xF4,
+ 0xDD, 0x4A, 0xF0, 0x15, 0x63, 0xAC, 0xD4, 0x27
+ },
+ {
+ 0xDF, 0x6D, 0xDA, 0x21, 0x35, 0x9A, 0x30, 0xBC,
+ 0x27, 0x17, 0x80, 0x97, 0x1C, 0x1A, 0xBD, 0x56,
+ 0xA6, 0xEF, 0x16, 0x7E, 0x48, 0x08, 0x87, 0x88,
+ 0x8E, 0x73, 0xA8, 0x6D, 0x3B, 0xF6, 0x05, 0xE9
+ },
+ {
+ 0xE8, 0xE6, 0xE4, 0x70, 0x71, 0xE7, 0xB7, 0xDF,
+ 0x25, 0x80, 0xF2, 0x25, 0xCF, 0xBB, 0xED, 0xF8,
+ 0x4C, 0xE6, 0x77, 0x46, 0x62, 0x66, 0x28, 0xD3,
+ 0x30, 0x97, 0xE4, 0xB7, 0xDC, 0x57, 0x11, 0x07
+ },
+ {
+ 0x53, 0xE4, 0x0E, 0xAD, 0x62, 0x05, 0x1E, 0x19,
+ 0xCB, 0x9B, 0xA8, 0x13, 0x3E, 0x3E, 0x5C, 0x1C,
+ 0xE0, 0x0D, 0xDC, 0xAD, 0x8A, 0xCF, 0x34, 0x2A,
+ 0x22, 0x43, 0x60, 0xB0, 0xAC, 0xC1, 0x47, 0x77
+ },
+ {
+ 0x9C, 0xCD, 0x53, 0xFE, 0x80, 0xBE, 0x78, 0x6A,
+ 0xA9, 0x84, 0x63, 0x84, 0x62, 0xFB, 0x28, 0xAF,
+ 0xDF, 0x12, 0x2B, 0x34, 0xD7, 0x8F, 0x46, 0x87,
+ 0xEC, 0x63, 0x2B, 0xB1, 0x9D, 0xE2, 0x37, 0x1A
+ },
+ {
+ 0xCB, 0xD4, 0x80, 0x52, 0xC4, 0x8D, 0x78, 0x84,
+ 0x66, 0xA3, 0xE8, 0x11, 0x8C, 0x56, 0xC9, 0x7F,
+ 0xE1, 0x46, 0xE5, 0x54, 0x6F, 0xAA, 0xF9, 0x3E,
+ 0x2B, 0xC3, 0xC4, 0x7E, 0x45, 0x93, 0x97, 0x53
+ },
+ {
+ 0x25, 0x68, 0x83, 0xB1, 0x4E, 0x2A, 0xF4, 0x4D,
+ 0xAD, 0xB2, 0x8E, 0x1B, 0x34, 0xB2, 0xAC, 0x0F,
+ 0x0F, 0x4C, 0x91, 0xC3, 0x4E, 0xC9, 0x16, 0x9E,
+ 0x29, 0x03, 0x61, 0x58, 0xAC, 0xAA, 0x95, 0xB9
+ },
+ {
+ 0x44, 0x71, 0xB9, 0x1A, 0xB4, 0x2D, 0xB7, 0xC4,
+ 0xDD, 0x84, 0x90, 0xAB, 0x95, 0xA2, 0xEE, 0x8D,
+ 0x04, 0xE3, 0xEF, 0x5C, 0x3D, 0x6F, 0xC7, 0x1A,
+ 0xC7, 0x4B, 0x2B, 0x26, 0x91, 0x4D, 0x16, 0x41
+ },
+ {
+ 0xA5, 0xEB, 0x08, 0x03, 0x8F, 0x8F, 0x11, 0x55,
+ 0xED, 0x86, 0xE6, 0x31, 0x90, 0x6F, 0xC1, 0x30,
+ 0x95, 0xF6, 0xBB, 0xA4, 0x1D, 0xE5, 0xD4, 0xE7,
+ 0x95, 0x75, 0x8E, 0xC8, 0xC8, 0xDF, 0x8A, 0xF1
+ },
+ {
+ 0xDC, 0x1D, 0xB6, 0x4E, 0xD8, 0xB4, 0x8A, 0x91,
+ 0x0E, 0x06, 0x0A, 0x6B, 0x86, 0x63, 0x74, 0xC5,
+ 0x78, 0x78, 0x4E, 0x9A, 0xC4, 0x9A, 0xB2, 0x77,
+ 0x40, 0x92, 0xAC, 0x71, 0x50, 0x19, 0x34, 0xAC
+ },
+ {
+ 0x28, 0x54, 0x13, 0xB2, 0xF2, 0xEE, 0x87, 0x3D,
+ 0x34, 0x31, 0x9E, 0xE0, 0xBB, 0xFB, 0xB9, 0x0F,
+ 0x32, 0xDA, 0x43, 0x4C, 0xC8, 0x7E, 0x3D, 0xB5,
+ 0xED, 0x12, 0x1B, 0xB3, 0x98, 0xED, 0x96, 0x4B
+ },
+ {
+ 0x02, 0x16, 0xE0, 0xF8, 0x1F, 0x75, 0x0F, 0x26,
+ 0xF1, 0x99, 0x8B, 0xC3, 0x93, 0x4E, 0x3E, 0x12,
+ 0x4C, 0x99, 0x45, 0xE6, 0x85, 0xA6, 0x0B, 0x25,
+ 0xE8, 0xFB, 0xD9, 0x62, 0x5A, 0xB6, 0xB5, 0x99
+ },
+ {
+ 0x38, 0xC4, 0x10, 0xF5, 0xB9, 0xD4, 0x07, 0x20,
+ 0x50, 0x75, 0x5B, 0x31, 0xDC, 0xA8, 0x9F, 0xD5,
+ 0x39, 0x5C, 0x67, 0x85, 0xEE, 0xB3, 0xD7, 0x90,
+ 0xF3, 0x20, 0xFF, 0x94, 0x1C, 0x5A, 0x93, 0xBF
+ },
+ {
+ 0xF1, 0x84, 0x17, 0xB3, 0x9D, 0x61, 0x7A, 0xB1,
+ 0xC1, 0x8F, 0xDF, 0x91, 0xEB, 0xD0, 0xFC, 0x6D,
+ 0x55, 0x16, 0xBB, 0x34, 0xCF, 0x39, 0x36, 0x40,
+ 0x37, 0xBC, 0xE8, 0x1F, 0xA0, 0x4C, 0xEC, 0xB1
+ },
+ {
+ 0x1F, 0xA8, 0x77, 0xDE, 0x67, 0x25, 0x9D, 0x19,
+ 0x86, 0x3A, 0x2A, 0x34, 0xBC, 0xC6, 0x96, 0x2A,
+ 0x2B, 0x25, 0xFC, 0xBF, 0x5C, 0xBE, 0xCD, 0x7E,
+ 0xDE, 0x8F, 0x1F, 0xA3, 0x66, 0x88, 0xA7, 0x96
+ },
+ {
+ 0x5B, 0xD1, 0x69, 0xE6, 0x7C, 0x82, 0xC2, 0xC2,
+ 0xE9, 0x8E, 0xF7, 0x00, 0x8B, 0xDF, 0x26, 0x1F,
+ 0x2D, 0xDF, 0x30, 0xB1, 0xC0, 0x0F, 0x9E, 0x7F,
+ 0x27, 0x5B, 0xB3, 0xE8, 0xA2, 0x8D, 0xC9, 0xA2
+ },
+ {
+ 0xC8, 0x0A, 0xBE, 0xEB, 0xB6, 0x69, 0xAD, 0x5D,
+ 0xEE, 0xB5, 0xF5, 0xEC, 0x8E, 0xA6, 0xB7, 0xA0,
+ 0x5D, 0xDF, 0x7D, 0x31, 0xEC, 0x4C, 0x0A, 0x2E,
+ 0xE2, 0x0B, 0x0B, 0x98, 0xCA, 0xEC, 0x67, 0x46
+ },
+ {
+ 0xE7, 0x6D, 0x3F, 0xBD, 0xA5, 0xBA, 0x37, 0x4E,
+ 0x6B, 0xF8, 0xE5, 0x0F, 0xAD, 0xC3, 0xBB, 0xB9,
+ 0xBA, 0x5C, 0x20, 0x6E, 0xBD, 0xEC, 0x89, 0xA3,
+ 0xA5, 0x4C, 0xF3, 0xDD, 0x84, 0xA0, 0x70, 0x16
+ },
+ {
+ 0x7B, 0xBA, 0x9D, 0xC5, 0xB5, 0xDB, 0x20, 0x71,
+ 0xD1, 0x77, 0x52, 0xB1, 0x04, 0x4C, 0x1E, 0xCE,
+ 0xD9, 0x6A, 0xAF, 0x2D, 0xD4, 0x6E, 0x9B, 0x43,
+ 0x37, 0x50, 0xE8, 0xEA, 0x0D, 0xCC, 0x18, 0x70
+ },
+ {
+ 0xF2, 0x9B, 0x1B, 0x1A, 0xB9, 0xBA, 0xB1, 0x63,
+ 0x01, 0x8E, 0xE3, 0xDA, 0x15, 0x23, 0x2C, 0xCA,
+ 0x78, 0xEC, 0x52, 0xDB, 0xC3, 0x4E, 0xDA, 0x5B,
+ 0x82, 0x2E, 0xC1, 0xD8, 0x0F, 0xC2, 0x1B, 0xD0
+ },
+ {
+ 0x9E, 0xE3, 0xE3, 0xE7, 0xE9, 0x00, 0xF1, 0xE1,
+ 0x1D, 0x30, 0x8C, 0x4B, 0x2B, 0x30, 0x76, 0xD2,
+ 0x72, 0xCF, 0x70, 0x12, 0x4F, 0x9F, 0x51, 0xE1,
+ 0xDA, 0x60, 0xF3, 0x78, 0x46, 0xCD, 0xD2, 0xF4
+ },
+ {
+ 0x70, 0xEA, 0x3B, 0x01, 0x76, 0x92, 0x7D, 0x90,
+ 0x96, 0xA1, 0x85, 0x08, 0xCD, 0x12, 0x3A, 0x29,
+ 0x03, 0x25, 0x92, 0x0A, 0x9D, 0x00, 0xA8, 0x9B,
+ 0x5D, 0xE0, 0x42, 0x73, 0xFB, 0xC7, 0x6B, 0x85
+ },
+ {
+ 0x67, 0xDE, 0x25, 0xC0, 0x2A, 0x4A, 0xAB, 0xA2,
+ 0x3B, 0xDC, 0x97, 0x3C, 0x8B, 0xB0, 0xB5, 0x79,
+ 0x6D, 0x47, 0xCC, 0x06, 0x59, 0xD4, 0x3D, 0xFF,
+ 0x1F, 0x97, 0xDE, 0x17, 0x49, 0x63, 0xB6, 0x8E
+ },
+ {
+ 0xB2, 0x16, 0x8E, 0x4E, 0x0F, 0x18, 0xB0, 0xE6,
+ 0x41, 0x00, 0xB5, 0x17, 0xED, 0x95, 0x25, 0x7D,
+ 0x73, 0xF0, 0x62, 0x0D, 0xF8, 0x85, 0xC1, 0x3D,
+ 0x2E, 0xCF, 0x79, 0x36, 0x7B, 0x38, 0x4C, 0xEE
+ },
+ {
+ 0x2E, 0x7D, 0xEC, 0x24, 0x28, 0x85, 0x3B, 0x2C,
+ 0x71, 0x76, 0x07, 0x45, 0x54, 0x1F, 0x7A, 0xFE,
+ 0x98, 0x25, 0xB5, 0xDD, 0x77, 0xDF, 0x06, 0x51,
+ 0x1D, 0x84, 0x41, 0xA9, 0x4B, 0xAC, 0xC9, 0x27
+ },
+ {
+ 0xCA, 0x9F, 0xFA, 0xC4, 0xC4, 0x3F, 0x0B, 0x48,
+ 0x46, 0x1D, 0xC5, 0xC2, 0x63, 0xBE, 0xA3, 0xF6,
+ 0xF0, 0x06, 0x11, 0xCE, 0xAC, 0xAB, 0xF6, 0xF8,
+ 0x95, 0xBA, 0x2B, 0x01, 0x01, 0xDB, 0xB6, 0x8D
+ },
+ {
+ 0x74, 0x10, 0xD4, 0x2D, 0x8F, 0xD1, 0xD5, 0xE9,
+ 0xD2, 0xF5, 0x81, 0x5C, 0xB9, 0x34, 0x17, 0x99,
+ 0x88, 0x28, 0xEF, 0x3C, 0x42, 0x30, 0xBF, 0xBD,
+ 0x41, 0x2D, 0xF0, 0xA4, 0xA7, 0xA2, 0x50, 0x7A
+ },
+ {
+ 0x50, 0x10, 0xF6, 0x84, 0x51, 0x6D, 0xCC, 0xD0,
+ 0xB6, 0xEE, 0x08, 0x52, 0xC2, 0x51, 0x2B, 0x4D,
+ 0xC0, 0x06, 0x6C, 0xF0, 0xD5, 0x6F, 0x35, 0x30,
+ 0x29, 0x78, 0xDB, 0x8A, 0xE3, 0x2C, 0x6A, 0x81
+ },
+ {
+ 0xAC, 0xAA, 0xB5, 0x85, 0xF7, 0xB7, 0x9B, 0x71,
+ 0x99, 0x35, 0xCE, 0xB8, 0x95, 0x23, 0xDD, 0xC5,
+ 0x48, 0x27, 0xF7, 0x5C, 0x56, 0x88, 0x38, 0x56,
+ 0x15, 0x4A, 0x56, 0xCD, 0xCD, 0x5E, 0xE9, 0x88
+ },
+ {
+ 0x66, 0x6D, 0xE5, 0xD1, 0x44, 0x0F, 0xEE, 0x73,
+ 0x31, 0xAA, 0xF0, 0x12, 0x3A, 0x62, 0xEF, 0x2D,
+ 0x8B, 0xA5, 0x74, 0x53, 0xA0, 0x76, 0x96, 0x35,
+ 0xAC, 0x6C, 0xD0, 0x1E, 0x63, 0x3F, 0x77, 0x12
+ },
+ {
+ 0xA6, 0xF9, 0x86, 0x58, 0xF6, 0xEA, 0xBA, 0xF9,
+ 0x02, 0xD8, 0xB3, 0x87, 0x1A, 0x4B, 0x10, 0x1D,
+ 0x16, 0x19, 0x6E, 0x8A, 0x4B, 0x24, 0x1E, 0x15,
+ 0x58, 0xFE, 0x29, 0x96, 0x6E, 0x10, 0x3E, 0x8D
+ },
+ {
+ 0x89, 0x15, 0x46, 0xA8, 0xB2, 0x9F, 0x30, 0x47,
+ 0xDD, 0xCF, 0xE5, 0xB0, 0x0E, 0x45, 0xFD, 0x55,
+ 0x75, 0x63, 0x73, 0x10, 0x5E, 0xA8, 0x63, 0x7D,
+ 0xFC, 0xFF, 0x54, 0x7B, 0x6E, 0xA9, 0x53, 0x5F
+ },
+ {
+ 0x18, 0xDF, 0xBC, 0x1A, 0xC5, 0xD2, 0x5B, 0x07,
+ 0x61, 0x13, 0x7D, 0xBD, 0x22, 0xC1, 0x7C, 0x82,
+ 0x9D, 0x0F, 0x0E, 0xF1, 0xD8, 0x23, 0x44, 0xE9,
+ 0xC8, 0x9C, 0x28, 0x66, 0x94, 0xDA, 0x24, 0xE8
+ },
+ {
+ 0xB5, 0x4B, 0x9B, 0x67, 0xF8, 0xFE, 0xD5, 0x4B,
+ 0xBF, 0x5A, 0x26, 0x66, 0xDB, 0xDF, 0x4B, 0x23,
+ 0xCF, 0xF1, 0xD1, 0xB6, 0xF4, 0xAF, 0xC9, 0x85,
+ 0xB2, 0xE6, 0xD3, 0x30, 0x5A, 0x9F, 0xF8, 0x0F
+ },
+ {
+ 0x7D, 0xB4, 0x42, 0xE1, 0x32, 0xBA, 0x59, 0xBC,
+ 0x12, 0x89, 0xAA, 0x98, 0xB0, 0xD3, 0xE8, 0x06,
+ 0x00, 0x4F, 0x8E, 0xC1, 0x28, 0x11, 0xAF, 0x1E,
+ 0x2E, 0x33, 0xC6, 0x9B, 0xFD, 0xE7, 0x29, 0xE1
+ },
+ {
+ 0x25, 0x0F, 0x37, 0xCD, 0xC1, 0x5E, 0x81, 0x7D,
+ 0x2F, 0x16, 0x0D, 0x99, 0x56, 0xC7, 0x1F, 0xE3,
+ 0xEB, 0x5D, 0xB7, 0x45, 0x56, 0xE4, 0xAD, 0xF9,
+ 0xA4, 0xFF, 0xAF, 0xBA, 0x74, 0x01, 0x03, 0x96
+ },
+ {
+ 0x4A, 0xB8, 0xA3, 0xDD, 0x1D, 0xDF, 0x8A, 0xD4,
+ 0x3D, 0xAB, 0x13, 0xA2, 0x7F, 0x66, 0xA6, 0x54,
+ 0x4F, 0x29, 0x05, 0x97, 0xFA, 0x96, 0x04, 0x0E,
+ 0x0E, 0x1D, 0xB9, 0x26, 0x3A, 0xA4, 0x79, 0xF8
+ },
+ {
+ 0xEE, 0x61, 0x72, 0x7A, 0x07, 0x66, 0xDF, 0x93,
+ 0x9C, 0xCD, 0xC8, 0x60, 0x33, 0x40, 0x44, 0xC7,
+ 0x9A, 0x3C, 0x9B, 0x15, 0x62, 0x00, 0xBC, 0x3A,
+ 0xA3, 0x29, 0x73, 0x48, 0x3D, 0x83, 0x41, 0xAE
+ },
+ {
+ 0x3F, 0x68, 0xC7, 0xEC, 0x63, 0xAC, 0x11, 0xEB,
+ 0xB9, 0x8F, 0x94, 0xB3, 0x39, 0xB0, 0x5C, 0x10,
+ 0x49, 0x84, 0xFD, 0xA5, 0x01, 0x03, 0x06, 0x01,
+ 0x44, 0xE5, 0xA2, 0xBF, 0xCC, 0xC9, 0xDA, 0x95
+ },
+ {
+ 0x05, 0x6F, 0x29, 0x81, 0x6B, 0x8A, 0xF8, 0xF5,
+ 0x66, 0x82, 0xBC, 0x4D, 0x7C, 0xF0, 0x94, 0x11,
+ 0x1D, 0xA7, 0x73, 0x3E, 0x72, 0x6C, 0xD1, 0x3D,
+ 0x6B, 0x3E, 0x8E, 0xA0, 0x3E, 0x92, 0xA0, 0xD5
+ },
+ {
+ 0xF5, 0xEC, 0x43, 0xA2, 0x8A, 0xCB, 0xEF, 0xF1,
+ 0xF3, 0x31, 0x8A, 0x5B, 0xCA, 0xC7, 0xC6, 0x6D,
+ 0xDB, 0x52, 0x30, 0xB7, 0x9D, 0xB2, 0xD1, 0x05,
+ 0xBC, 0xBE, 0x15, 0xF3, 0xC1, 0x14, 0x8D, 0x69
+ },
+ {
+ 0x2A, 0x69, 0x60, 0xAD, 0x1D, 0x8D, 0xD5, 0x47,
+ 0x55, 0x5C, 0xFB, 0xD5, 0xE4, 0x60, 0x0F, 0x1E,
+ 0xAA, 0x1C, 0x8E, 0xDA, 0x34, 0xDE, 0x03, 0x74,
+ 0xEC, 0x4A, 0x26, 0xEA, 0xAA, 0xA3, 0x3B, 0x4E
+ },
+ {
+ 0xDC, 0xC1, 0xEA, 0x7B, 0xAA, 0xB9, 0x33, 0x84,
+ 0xF7, 0x6B, 0x79, 0x68, 0x66, 0x19, 0x97, 0x54,
+ 0x74, 0x2F, 0x7B, 0x96, 0xD6, 0xB4, 0xC1, 0x20,
+ 0x16, 0x5C, 0x04, 0xA6, 0xC4, 0xF5, 0xCE, 0x10
+ },
+ {
+ 0x13, 0xD5, 0xDF, 0x17, 0x92, 0x21, 0x37, 0x9C,
+ 0x6A, 0x78, 0xC0, 0x7C, 0x79, 0x3F, 0xF5, 0x34,
+ 0x87, 0xCA, 0xE6, 0xBF, 0x9F, 0xE8, 0x82, 0x54,
+ 0x1A, 0xB0, 0xE7, 0x35, 0xE3, 0xEA, 0xDA, 0x3B
+ },
+ {
+ 0x8C, 0x59, 0xE4, 0x40, 0x76, 0x41, 0xA0, 0x1E,
+ 0x8F, 0xF9, 0x1F, 0x99, 0x80, 0xDC, 0x23, 0x6F,
+ 0x4E, 0xCD, 0x6F, 0xCF, 0x52, 0x58, 0x9A, 0x09,
+ 0x9A, 0x96, 0x16, 0x33, 0x96, 0x77, 0x14, 0xE1
+ },
+ {
+ 0x83, 0x3B, 0x1A, 0xC6, 0xA2, 0x51, 0xFD, 0x08,
+ 0xFD, 0x6D, 0x90, 0x8F, 0xEA, 0x2A, 0x4E, 0xE1,
+ 0xE0, 0x40, 0xBC, 0xA9, 0x3F, 0xC1, 0xA3, 0x8E,
+ 0xC3, 0x82, 0x0E, 0x0C, 0x10, 0xBD, 0x82, 0xEA
+ },
+ {
+ 0xA2, 0x44, 0xF9, 0x27, 0xF3, 0xB4, 0x0B, 0x8F,
+ 0x6C, 0x39, 0x15, 0x70, 0xC7, 0x65, 0x41, 0x8F,
+ 0x2F, 0x6E, 0x70, 0x8E, 0xAC, 0x90, 0x06, 0xC5,
+ 0x1A, 0x7F, 0xEF, 0xF4, 0xAF, 0x3B, 0x2B, 0x9E
+ },
+ {
+ 0x3D, 0x99, 0xED, 0x95, 0x50, 0xCF, 0x11, 0x96,
+ 0xE6, 0xC4, 0xD2, 0x0C, 0x25, 0x96, 0x20, 0xF8,
+ 0x58, 0xC3, 0xD7, 0x03, 0x37, 0x4C, 0x12, 0x8C,
+ 0xE7, 0xB5, 0x90, 0x31, 0x0C, 0x83, 0x04, 0x6D
+ },
+ {
+ 0x2B, 0x35, 0xC4, 0x7D, 0x7B, 0x87, 0x76, 0x1F,
+ 0x0A, 0xE4, 0x3A, 0xC5, 0x6A, 0xC2, 0x7B, 0x9F,
+ 0x25, 0x83, 0x03, 0x67, 0xB5, 0x95, 0xBE, 0x8C,
+ 0x24, 0x0E, 0x94, 0x60, 0x0C, 0x6E, 0x33, 0x12
+ },
+ {
+ 0x5D, 0x11, 0xED, 0x37, 0xD2, 0x4D, 0xC7, 0x67,
+ 0x30, 0x5C, 0xB7, 0xE1, 0x46, 0x7D, 0x87, 0xC0,
+ 0x65, 0xAC, 0x4B, 0xC8, 0xA4, 0x26, 0xDE, 0x38,
+ 0x99, 0x1F, 0xF5, 0x9A, 0xA8, 0x73, 0x5D, 0x02
+ },
+ {
+ 0xB8, 0x36, 0x47, 0x8E, 0x1C, 0xA0, 0x64, 0x0D,
+ 0xCE, 0x6F, 0xD9, 0x10, 0xA5, 0x09, 0x62, 0x72,
+ 0xC8, 0x33, 0x09, 0x90, 0xCD, 0x97, 0x86, 0x4A,
+ 0xC2, 0xBF, 0x14, 0xEF, 0x6B, 0x23, 0x91, 0x4A
+ },
+ {
+ 0x91, 0x00, 0xF9, 0x46, 0xD6, 0xCC, 0xDE, 0x3A,
+ 0x59, 0x7F, 0x90, 0xD3, 0x9F, 0xC1, 0x21, 0x5B,
+ 0xAD, 0xDC, 0x74, 0x13, 0x64, 0x3D, 0x85, 0xC2,
+ 0x1C, 0x3E, 0xEE, 0x5D, 0x2D, 0xD3, 0x28, 0x94
+ },
+ {
+ 0xDA, 0x70, 0xEE, 0xDD, 0x23, 0xE6, 0x63, 0xAA,
+ 0x1A, 0x74, 0xB9, 0x76, 0x69, 0x35, 0xB4, 0x79,
+ 0x22, 0x2A, 0x72, 0xAF, 0xBA, 0x5C, 0x79, 0x51,
+ 0x58, 0xDA, 0xD4, 0x1A, 0x3B, 0xD7, 0x7E, 0x40
+ },
+ {
+ 0xF0, 0x67, 0xED, 0x6A, 0x0D, 0xBD, 0x43, 0xAA,
+ 0x0A, 0x92, 0x54, 0xE6, 0x9F, 0xD6, 0x6B, 0xDD,
+ 0x8A, 0xCB, 0x87, 0xDE, 0x93, 0x6C, 0x25, 0x8C,
+ 0xFB, 0x02, 0x28, 0x5F, 0x2C, 0x11, 0xFA, 0x79
+ },
+ {
+ 0x71, 0x5C, 0x99, 0xC7, 0xD5, 0x75, 0x80, 0xCF,
+ 0x97, 0x53, 0xB4, 0xC1, 0xD7, 0x95, 0xE4, 0x5A,
+ 0x83, 0xFB, 0xB2, 0x28, 0xC0, 0xD3, 0x6F, 0xBE,
+ 0x20, 0xFA, 0xF3, 0x9B, 0xDD, 0x6D, 0x4E, 0x85
+ },
+ {
+ 0xE4, 0x57, 0xD6, 0xAD, 0x1E, 0x67, 0xCB, 0x9B,
+ 0xBD, 0x17, 0xCB, 0xD6, 0x98, 0xFA, 0x6D, 0x7D,
+ 0xAE, 0x0C, 0x9B, 0x7A, 0xD6, 0xCB, 0xD6, 0x53,
+ 0x96, 0x34, 0xE3, 0x2A, 0x71, 0x9C, 0x84, 0x92
+ },
+ {
+ 0xEC, 0xE3, 0xEA, 0x81, 0x03, 0xE0, 0x24, 0x83,
+ 0xC6, 0x4A, 0x70, 0xA4, 0xBD, 0xCE, 0xE8, 0xCE,
+ 0xB6, 0x27, 0x8F, 0x25, 0x33, 0xF3, 0xF4, 0x8D,
+ 0xBE, 0xED, 0xFB, 0xA9, 0x45, 0x31, 0xD4, 0xAE
+ },
+ {
+ 0x38, 0x8A, 0xA5, 0xD3, 0x66, 0x7A, 0x97, 0xC6,
+ 0x8D, 0x3D, 0x56, 0xF8, 0xF3, 0xEE, 0x8D, 0x3D,
+ 0x36, 0x09, 0x1F, 0x17, 0xFE, 0x5D, 0x1B, 0x0D,
+ 0x5D, 0x84, 0xC9, 0x3B, 0x2F, 0xFE, 0x40, 0xBD
+ },
+ {
+ 0x8B, 0x6B, 0x31, 0xB9, 0xAD, 0x7C, 0x3D, 0x5C,
+ 0xD8, 0x4B, 0xF9, 0x89, 0x47, 0xB9, 0xCD, 0xB5,
+ 0x9D, 0xF8, 0xA2, 0x5F, 0xF7, 0x38, 0x10, 0x10,
+ 0x13, 0xBE, 0x4F, 0xD6, 0x5E, 0x1D, 0xD1, 0xA3
+ },
+ {
+ 0x06, 0x62, 0x91, 0xF6, 0xBB, 0xD2, 0x5F, 0x3C,
+ 0x85, 0x3D, 0xB7, 0xD8, 0xB9, 0x5C, 0x9A, 0x1C,
+ 0xFB, 0x9B, 0xF1, 0xC1, 0xC9, 0x9F, 0xB9, 0x5A,
+ 0x9B, 0x78, 0x69, 0xD9, 0x0F, 0x1C, 0x29, 0x03
+ },
+ {
+ 0xA7, 0x07, 0xEF, 0xBC, 0xCD, 0xCE, 0xED, 0x42,
+ 0x96, 0x7A, 0x66, 0xF5, 0x53, 0x9B, 0x93, 0xED,
+ 0x75, 0x60, 0xD4, 0x67, 0x30, 0x40, 0x16, 0xC4,
+ 0x78, 0x0D, 0x77, 0x55, 0xA5, 0x65, 0xD4, 0xC4
+ },
+ {
+ 0x38, 0xC5, 0x3D, 0xFB, 0x70, 0xBE, 0x7E, 0x79,
+ 0x2B, 0x07, 0xA6, 0xA3, 0x5B, 0x8A, 0x6A, 0x0A,
+ 0xBA, 0x02, 0xC5, 0xC5, 0xF3, 0x8B, 0xAF, 0x5C,
+ 0x82, 0x3F, 0xDF, 0xD9, 0xE4, 0x2D, 0x65, 0x7E
+ },
+ {
+ 0xF2, 0x91, 0x13, 0x86, 0x50, 0x1D, 0x9A, 0xB9,
+ 0xD7, 0x20, 0xCF, 0x8A, 0xD1, 0x05, 0x03, 0xD5,
+ 0x63, 0x4B, 0xF4, 0xB7, 0xD1, 0x2B, 0x56, 0xDF,
+ 0xB7, 0x4F, 0xEC, 0xC6, 0xE4, 0x09, 0x3F, 0x68
+ },
+ {
+ 0xC6, 0xF2, 0xBD, 0xD5, 0x2B, 0x81, 0xE6, 0xE4,
+ 0xF6, 0x59, 0x5A, 0xBD, 0x4D, 0x7F, 0xB3, 0x1F,
+ 0x65, 0x11, 0x69, 0xD0, 0x0F, 0xF3, 0x26, 0x92,
+ 0x6B, 0x34, 0x94, 0x7B, 0x28, 0xA8, 0x39, 0x59
+ },
+ {
+ 0x29, 0x3D, 0x94, 0xB1, 0x8C, 0x98, 0xBB, 0x32,
+ 0x23, 0x36, 0x6B, 0x8C, 0xE7, 0x4C, 0x28, 0xFB,
+ 0xDF, 0x28, 0xE1, 0xF8, 0x4A, 0x33, 0x50, 0xB0,
+ 0xEB, 0x2D, 0x18, 0x04, 0xA5, 0x77, 0x57, 0x9B
+ },
+ {
+ 0x2C, 0x2F, 0xA5, 0xC0, 0xB5, 0x15, 0x33, 0x16,
+ 0x5B, 0xC3, 0x75, 0xC2, 0x2E, 0x27, 0x81, 0x76,
+ 0x82, 0x70, 0xA3, 0x83, 0x98, 0x5D, 0x13, 0xBD,
+ 0x6B, 0x67, 0xB6, 0xFD, 0x67, 0xF8, 0x89, 0xEB
+ },
+ {
+ 0xCA, 0xA0, 0x9B, 0x82, 0xB7, 0x25, 0x62, 0xE4,
+ 0x3F, 0x4B, 0x22, 0x75, 0xC0, 0x91, 0x91, 0x8E,
+ 0x62, 0x4D, 0x91, 0x16, 0x61, 0xCC, 0x81, 0x1B,
+ 0xB5, 0xFA, 0xEC, 0x51, 0xF6, 0x08, 0x8E, 0xF7
+ },
+ {
+ 0x24, 0x76, 0x1E, 0x45, 0xE6, 0x74, 0x39, 0x53,
+ 0x79, 0xFB, 0x17, 0x72, 0x9C, 0x78, 0xCB, 0x93,
+ 0x9E, 0x6F, 0x74, 0xC5, 0xDF, 0xFB, 0x9C, 0x96,
+ 0x1F, 0x49, 0x59, 0x82, 0xC3, 0xED, 0x1F, 0xE3
+ },
+ {
+ 0x55, 0xB7, 0x0A, 0x82, 0x13, 0x1E, 0xC9, 0x48,
+ 0x88, 0xD7, 0xAB, 0x54, 0xA7, 0xC5, 0x15, 0x25,
+ 0x5C, 0x39, 0x38, 0xBB, 0x10, 0xBC, 0x78, 0x4D,
+ 0xC9, 0xB6, 0x7F, 0x07, 0x6E, 0x34, 0x1A, 0x73
+ },
+ {
+ 0x6A, 0xB9, 0x05, 0x7B, 0x97, 0x7E, 0xBC, 0x3C,
+ 0xA4, 0xD4, 0xCE, 0x74, 0x50, 0x6C, 0x25, 0xCC,
+ 0xCD, 0xC5, 0x66, 0x49, 0x7C, 0x45, 0x0B, 0x54,
+ 0x15, 0xA3, 0x94, 0x86, 0xF8, 0x65, 0x7A, 0x03
+ },
+ {
+ 0x24, 0x06, 0x6D, 0xEE, 0xE0, 0xEC, 0xEE, 0x15,
+ 0xA4, 0x5F, 0x0A, 0x32, 0x6D, 0x0F, 0x8D, 0xBC,
+ 0x79, 0x76, 0x1E, 0xBB, 0x93, 0xCF, 0x8C, 0x03,
+ 0x77, 0xAF, 0x44, 0x09, 0x78, 0xFC, 0xF9, 0x94
+ },
+ {
+ 0x20, 0x00, 0x0D, 0x3F, 0x66, 0xBA, 0x76, 0x86,
+ 0x0D, 0x5A, 0x95, 0x06, 0x88, 0xB9, 0xAA, 0x0D,
+ 0x76, 0xCF, 0xEA, 0x59, 0xB0, 0x05, 0xD8, 0x59,
+ 0x91, 0x4B, 0x1A, 0x46, 0x65, 0x3A, 0x93, 0x9B
+ },
+ {
+ 0xB9, 0x2D, 0xAA, 0x79, 0x60, 0x3E, 0x3B, 0xDB,
+ 0xC3, 0xBF, 0xE0, 0xF4, 0x19, 0xE4, 0x09, 0xB2,
+ 0xEA, 0x10, 0xDC, 0x43, 0x5B, 0xEE, 0xFE, 0x29,
+ 0x59, 0xDA, 0x16, 0x89, 0x5D, 0x5D, 0xCA, 0x1C
+ },
+ {
+ 0xE9, 0x47, 0x94, 0x87, 0x05, 0xB2, 0x06, 0xD5,
+ 0x72, 0xB0, 0xE8, 0xF6, 0x2F, 0x66, 0xA6, 0x55,
+ 0x1C, 0xBD, 0x6B, 0xC3, 0x05, 0xD2, 0x6C, 0xE7,
+ 0x53, 0x9A, 0x12, 0xF9, 0xAA, 0xDF, 0x75, 0x71
+ },
+ {
+ 0x3D, 0x67, 0xC1, 0xB3, 0xF9, 0xB2, 0x39, 0x10,
+ 0xE3, 0xD3, 0x5E, 0x6B, 0x0F, 0x2C, 0xCF, 0x44,
+ 0xA0, 0xB5, 0x40, 0xA4, 0x5C, 0x18, 0xBA, 0x3C,
+ 0x36, 0x26, 0x4D, 0xD4, 0x8E, 0x96, 0xAF, 0x6A
+ },
+ {
+ 0xC7, 0x55, 0x8B, 0xAB, 0xDA, 0x04, 0xBC, 0xCB,
+ 0x76, 0x4D, 0x0B, 0xBF, 0x33, 0x58, 0x42, 0x51,
+ 0x41, 0x90, 0x2D, 0x22, 0x39, 0x1D, 0x9F, 0x8C,
+ 0x59, 0x15, 0x9F, 0xEC, 0x9E, 0x49, 0xB1, 0x51
+ },
+ {
+ 0x0B, 0x73, 0x2B, 0xB0, 0x35, 0x67, 0x5A, 0x50,
+ 0xFF, 0x58, 0xF2, 0xC2, 0x42, 0xE4, 0x71, 0x0A,
+ 0xEC, 0xE6, 0x46, 0x70, 0x07, 0x9C, 0x13, 0x04,
+ 0x4C, 0x79, 0xC9, 0xB7, 0x49, 0x1F, 0x70, 0x00
+ },
+ {
+ 0xD1, 0x20, 0xB5, 0xEF, 0x6D, 0x57, 0xEB, 0xF0,
+ 0x6E, 0xAF, 0x96, 0xBC, 0x93, 0x3C, 0x96, 0x7B,
+ 0x16, 0xCB, 0xE6, 0xE2, 0xBF, 0x00, 0x74, 0x1C,
+ 0x30, 0xAA, 0x1C, 0x54, 0xBA, 0x64, 0x80, 0x1F
+ },
+ {
+ 0x58, 0xD2, 0x12, 0xAD, 0x6F, 0x58, 0xAE, 0xF0,
+ 0xF8, 0x01, 0x16, 0xB4, 0x41, 0xE5, 0x7F, 0x61,
+ 0x95, 0xBF, 0xEF, 0x26, 0xB6, 0x14, 0x63, 0xED,
+ 0xEC, 0x11, 0x83, 0xCD, 0xB0, 0x4F, 0xE7, 0x6D
+ },
+ {
+ 0xB8, 0x83, 0x6F, 0x51, 0xD1, 0xE2, 0x9B, 0xDF,
+ 0xDB, 0xA3, 0x25, 0x56, 0x53, 0x60, 0x26, 0x8B,
+ 0x8F, 0xAD, 0x62, 0x74, 0x73, 0xED, 0xEC, 0xEF,
+ 0x7E, 0xAE, 0xFE, 0xE8, 0x37, 0xC7, 0x40, 0x03
+ },
+ {
+ 0xC5, 0x47, 0xA3, 0xC1, 0x24, 0xAE, 0x56, 0x85,
+ 0xFF, 0xA7, 0xB8, 0xED, 0xAF, 0x96, 0xEC, 0x86,
+ 0xF8, 0xB2, 0xD0, 0xD5, 0x0C, 0xEE, 0x8B, 0xE3,
+ 0xB1, 0xF0, 0xC7, 0x67, 0x63, 0x06, 0x9D, 0x9C
+ },
+ {
+ 0x5D, 0x16, 0x8B, 0x76, 0x9A, 0x2F, 0x67, 0x85,
+ 0x3D, 0x62, 0x95, 0xF7, 0x56, 0x8B, 0xE4, 0x0B,
+ 0xB7, 0xA1, 0x6B, 0x8D, 0x65, 0xBA, 0x87, 0x63,
+ 0x5D, 0x19, 0x78, 0xD2, 0xAB, 0x11, 0xBA, 0x2A
+ },
+ {
+ 0xA2, 0xF6, 0x75, 0xDC, 0x73, 0x02, 0x63, 0x8C,
+ 0xB6, 0x02, 0x01, 0x06, 0x4C, 0xA5, 0x50, 0x77,
+ 0x71, 0x4D, 0x71, 0xFE, 0x09, 0x6A, 0x31, 0x5F,
+ 0x2F, 0xE7, 0x40, 0x12, 0x77, 0xCA, 0xA5, 0xAF
+ },
+ {
+ 0xC8, 0xAA, 0xB5, 0xCD, 0x01, 0x60, 0xAE, 0x78,
+ 0xCD, 0x2E, 0x8A, 0xC5, 0xFB, 0x0E, 0x09, 0x3C,
+ 0xDB, 0x5C, 0x4B, 0x60, 0x52, 0xA0, 0xA9, 0x7B,
+ 0xB0, 0x42, 0x16, 0x82, 0x6F, 0xA7, 0xA4, 0x37
+ },
+ {
+ 0xFF, 0x68, 0xCA, 0x40, 0x35, 0xBF, 0xEB, 0x43,
+ 0xFB, 0xF1, 0x45, 0xFD, 0xDD, 0x5E, 0x43, 0xF1,
+ 0xCE, 0xA5, 0x4F, 0x11, 0xF7, 0xBE, 0xE1, 0x30,
+ 0x58, 0xF0, 0x27, 0x32, 0x9A, 0x4A, 0x5F, 0xA4
+ },
+ {
+ 0x1D, 0x4E, 0x54, 0x87, 0xAE, 0x3C, 0x74, 0x0F,
+ 0x2B, 0xA6, 0xE5, 0x41, 0xAC, 0x91, 0xBC, 0x2B,
+ 0xFC, 0xD2, 0x99, 0x9C, 0x51, 0x8D, 0x80, 0x7B,
+ 0x42, 0x67, 0x48, 0x80, 0x3A, 0x35, 0x0F, 0xD4
+ },
+ {
+ 0x6D, 0x24, 0x4E, 0x1A, 0x06, 0xCE, 0x4E, 0xF5,
+ 0x78, 0xDD, 0x0F, 0x63, 0xAF, 0xF0, 0x93, 0x67,
+ 0x06, 0x73, 0x51, 0x19, 0xCA, 0x9C, 0x8D, 0x22,
+ 0xD8, 0x6C, 0x80, 0x14, 0x14, 0xAB, 0x97, 0x41
+ },
+ {
+ 0xDE, 0xCF, 0x73, 0x29, 0xDB, 0xCC, 0x82, 0x7B,
+ 0x8F, 0xC5, 0x24, 0xC9, 0x43, 0x1E, 0x89, 0x98,
+ 0x02, 0x9E, 0xCE, 0x12, 0xCE, 0x93, 0xB7, 0xB2,
+ 0xF3, 0xE7, 0x69, 0xA9, 0x41, 0xFB, 0x8C, 0xEA
+ },
+ {
+ 0x2F, 0xAF, 0xCC, 0x0F, 0x2E, 0x63, 0xCB, 0xD0,
+ 0x77, 0x55, 0xBE, 0x7B, 0x75, 0xEC, 0xEA, 0x0A,
+ 0xDF, 0xF9, 0xAA, 0x5E, 0xDE, 0x2A, 0x52, 0xFD,
+ 0xAB, 0x4D, 0xFD, 0x03, 0x74, 0xCD, 0x48, 0x3F
+ },
+ {
+ 0xAA, 0x85, 0x01, 0x0D, 0xD4, 0x6A, 0x54, 0x6B,
+ 0x53, 0x5E, 0xF4, 0xCF, 0x5F, 0x07, 0xD6, 0x51,
+ 0x61, 0xE8, 0x98, 0x28, 0xF3, 0xA7, 0x7D, 0xB7,
+ 0xB9, 0xB5, 0x6F, 0x0D, 0xF5, 0x9A, 0xAE, 0x45
+ },
+ {
+ 0x07, 0xE8, 0xE1, 0xEE, 0x73, 0x2C, 0xB0, 0xD3,
+ 0x56, 0xC9, 0xC0, 0xD1, 0x06, 0x9C, 0x89, 0xD1,
+ 0x7A, 0xDF, 0x6A, 0x9A, 0x33, 0x4F, 0x74, 0x5E,
+ 0xC7, 0x86, 0x73, 0x32, 0x54, 0x8C, 0xA8, 0xE9
+ },
+ {
+ 0x0E, 0x01, 0xE8, 0x1C, 0xAD, 0xA8, 0x16, 0x2B,
+ 0xFD, 0x5F, 0x8A, 0x8C, 0x81, 0x8A, 0x6C, 0x69,
+ 0xFE, 0xDF, 0x02, 0xCE, 0xB5, 0x20, 0x85, 0x23,
+ 0xCB, 0xE5, 0x31, 0x3B, 0x89, 0xCA, 0x10, 0x53
+ },
+ {
+ 0x6B, 0xB6, 0xC6, 0x47, 0x26, 0x55, 0x08, 0x43,
+ 0x99, 0x85, 0x2E, 0x00, 0x24, 0x9F, 0x8C, 0xB2,
+ 0x47, 0x89, 0x6D, 0x39, 0x2B, 0x02, 0xD7, 0x3B,
+ 0x7F, 0x0D, 0xD8, 0x18, 0xE1, 0xE2, 0x9B, 0x07
+ },
+ {
+ 0x42, 0xD4, 0x63, 0x6E, 0x20, 0x60, 0xF0, 0x8F,
+ 0x41, 0xC8, 0x82, 0xE7, 0x6B, 0x39, 0x6B, 0x11,
+ 0x2E, 0xF6, 0x27, 0xCC, 0x24, 0xC4, 0x3D, 0xD5,
+ 0xF8, 0x3A, 0x1D, 0x1A, 0x7E, 0xAD, 0x71, 0x1A
+ },
+ {
+ 0x48, 0x58, 0xC9, 0xA1, 0x88, 0xB0, 0x23, 0x4F,
+ 0xB9, 0xA8, 0xD4, 0x7D, 0x0B, 0x41, 0x33, 0x65,
+ 0x0A, 0x03, 0x0B, 0xD0, 0x61, 0x1B, 0x87, 0xC3,
+ 0x89, 0x2E, 0x94, 0x95, 0x1F, 0x8D, 0xF8, 0x52
+ },
+ {
+ 0x3F, 0xAB, 0x3E, 0x36, 0x98, 0x8D, 0x44, 0x5A,
+ 0x51, 0xC8, 0x78, 0x3E, 0x53, 0x1B, 0xE3, 0xA0,
+ 0x2B, 0xE4, 0x0C, 0xD0, 0x47, 0x96, 0xCF, 0xB6,
+ 0x1D, 0x40, 0x34, 0x74, 0x42, 0xD3, 0xF7, 0x94
+ },
+ {
+ 0xEB, 0xAB, 0xC4, 0x96, 0x36, 0xBD, 0x43, 0x3D,
+ 0x2E, 0xC8, 0xF0, 0xE5, 0x18, 0x73, 0x2E, 0xF8,
+ 0xFA, 0x21, 0xD4, 0xD0, 0x71, 0xCC, 0x3B, 0xC4,
+ 0x6C, 0xD7, 0x9F, 0xA3, 0x8A, 0x28, 0xB8, 0x10
+ },
+ {
+ 0xA1, 0xD0, 0x34, 0x35, 0x23, 0xB8, 0x93, 0xFC,
+ 0xA8, 0x4F, 0x47, 0xFE, 0xB4, 0xA6, 0x4D, 0x35,
+ 0x0A, 0x17, 0xD8, 0xEE, 0xF5, 0x49, 0x7E, 0xCE,
+ 0x69, 0x7D, 0x02, 0xD7, 0x91, 0x78, 0xB5, 0x91
+ },
+ {
+ 0x26, 0x2E, 0xBF, 0xD9, 0x13, 0x0B, 0x7D, 0x28,
+ 0x76, 0x0D, 0x08, 0xEF, 0x8B, 0xFD, 0x3B, 0x86,
+ 0xCD, 0xD3, 0xB2, 0x11, 0x3D, 0x2C, 0xAE, 0xF7,
+ 0xEA, 0x95, 0x1A, 0x30, 0x3D, 0xFA, 0x38, 0x46
+ },
+ {
+ 0xF7, 0x61, 0x58, 0xED, 0xD5, 0x0A, 0x15, 0x4F,
+ 0xA7, 0x82, 0x03, 0xED, 0x23, 0x62, 0x93, 0x2F,
+ 0xCB, 0x82, 0x53, 0xAA, 0xE3, 0x78, 0x90, 0x3E,
+ 0xDE, 0xD1, 0xE0, 0x3F, 0x70, 0x21, 0xA2, 0x57
+ },
+ {
+ 0x26, 0x17, 0x8E, 0x95, 0x0A, 0xC7, 0x22, 0xF6,
+ 0x7A, 0xE5, 0x6E, 0x57, 0x1B, 0x28, 0x4C, 0x02,
+ 0x07, 0x68, 0x4A, 0x63, 0x34, 0xA1, 0x77, 0x48,
+ 0xA9, 0x4D, 0x26, 0x0B, 0xC5, 0xF5, 0x52, 0x74
+ },
+ {
+ 0xC3, 0x78, 0xD1, 0xE4, 0x93, 0xB4, 0x0E, 0xF1,
+ 0x1F, 0xE6, 0xA1, 0x5D, 0x9C, 0x27, 0x37, 0xA3,
+ 0x78, 0x09, 0x63, 0x4C, 0x5A, 0xBA, 0xD5, 0xB3,
+ 0x3D, 0x7E, 0x39, 0x3B, 0x4A, 0xE0, 0x5D, 0x03
+ },
+ {
+ 0x98, 0x4B, 0xD8, 0x37, 0x91, 0x01, 0xBE, 0x8F,
+ 0xD8, 0x06, 0x12, 0xD8, 0xEA, 0x29, 0x59, 0xA7,
+ 0x86, 0x5E, 0xC9, 0x71, 0x85, 0x23, 0x55, 0x01,
+ 0x07, 0xAE, 0x39, 0x38, 0xDF, 0x32, 0x01, 0x1B
+ },
+ {
+ 0xC6, 0xF2, 0x5A, 0x81, 0x2A, 0x14, 0x48, 0x58,
+ 0xAC, 0x5C, 0xED, 0x37, 0xA9, 0x3A, 0x9F, 0x47,
+ 0x59, 0xBA, 0x0B, 0x1C, 0x0F, 0xDC, 0x43, 0x1D,
+ 0xCE, 0x35, 0xF9, 0xEC, 0x1F, 0x1F, 0x4A, 0x99
+ },
+ {
+ 0x92, 0x4C, 0x75, 0xC9, 0x44, 0x24, 0xFF, 0x75,
+ 0xE7, 0x4B, 0x8B, 0x4E, 0x94, 0x35, 0x89, 0x58,
+ 0xB0, 0x27, 0xB1, 0x71, 0xDF, 0x5E, 0x57, 0x89,
+ 0x9A, 0xD0, 0xD4, 0xDA, 0xC3, 0x73, 0x53, 0xB6
+ },
+ {
+ 0x0A, 0xF3, 0x58, 0x92, 0xA6, 0x3F, 0x45, 0x93,
+ 0x1F, 0x68, 0x46, 0xED, 0x19, 0x03, 0x61, 0xCD,
+ 0x07, 0x30, 0x89, 0xE0, 0x77, 0x16, 0x57, 0x14,
+ 0xB5, 0x0B, 0x81, 0xA2, 0xE3, 0xDD, 0x9B, 0xA1
+ },
+ {
+ 0xCC, 0x80, 0xCE, 0xFB, 0x26, 0xC3, 0xB2, 0xB0,
+ 0xDA, 0xEF, 0x23, 0x3E, 0x60, 0x6D, 0x5F, 0xFC,
+ 0x80, 0xFA, 0x17, 0x42, 0x7D, 0x18, 0xE3, 0x04,
+ 0x89, 0x67, 0x3E, 0x06, 0xEF, 0x4B, 0x87, 0xF7
+ },
+ {
+ 0xC2, 0xF8, 0xC8, 0x11, 0x74, 0x47, 0xF3, 0x97,
+ 0x8B, 0x08, 0x18, 0xDC, 0xF6, 0xF7, 0x01, 0x16,
+ 0xAC, 0x56, 0xFD, 0x18, 0x4D, 0xD1, 0x27, 0x84,
+ 0x94, 0xE1, 0x03, 0xFC, 0x6D, 0x74, 0xA8, 0x87
+ },
+ {
+ 0xBD, 0xEC, 0xF6, 0xBF, 0xC1, 0xBA, 0x0D, 0xF6,
+ 0xE8, 0x62, 0xC8, 0x31, 0x99, 0x22, 0x07, 0x79,
+ 0x6A, 0xCC, 0x79, 0x79, 0x68, 0x35, 0x88, 0x28,
+ 0xC0, 0x6E, 0x7A, 0x51, 0xE0, 0x90, 0x09, 0x8F
+ },
+ {
+ 0x24, 0xD1, 0xA2, 0x6E, 0x3D, 0xAB, 0x02, 0xFE,
+ 0x45, 0x72, 0xD2, 0xAA, 0x7D, 0xBD, 0x3E, 0xC3,
+ 0x0F, 0x06, 0x93, 0xDB, 0x26, 0xF2, 0x73, 0xD0,
+ 0xAB, 0x2C, 0xB0, 0xC1, 0x3B, 0x5E, 0x64, 0x51
+ },
+ {
+ 0xEC, 0x56, 0xF5, 0x8B, 0x09, 0x29, 0x9A, 0x30,
+ 0x0B, 0x14, 0x05, 0x65, 0xD7, 0xD3, 0xE6, 0x87,
+ 0x82, 0xB6, 0xE2, 0xFB, 0xEB, 0x4B, 0x7E, 0xA9,
+ 0x7A, 0xC0, 0x57, 0x98, 0x90, 0x61, 0xDD, 0x3F
+ },
+ {
+ 0x11, 0xA4, 0x37, 0xC1, 0xAB, 0xA3, 0xC1, 0x19,
+ 0xDD, 0xFA, 0xB3, 0x1B, 0x3E, 0x8C, 0x84, 0x1D,
+ 0xEE, 0xEB, 0x91, 0x3E, 0xF5, 0x7F, 0x7E, 0x48,
+ 0xF2, 0xC9, 0xCF, 0x5A, 0x28, 0xFA, 0x42, 0xBC
+ },
+ {
+ 0x53, 0xC7, 0xE6, 0x11, 0x4B, 0x85, 0x0A, 0x2C,
+ 0xB4, 0x96, 0xC9, 0xB3, 0xC6, 0x9A, 0x62, 0x3E,
+ 0xAE, 0xA2, 0xCB, 0x1D, 0x33, 0xDD, 0x81, 0x7E,
+ 0x47, 0x65, 0xED, 0xAA, 0x68, 0x23, 0xC2, 0x28
+ },
+ {
+ 0x15, 0x4C, 0x3E, 0x96, 0xFE, 0xE5, 0xDB, 0x14,
+ 0xF8, 0x77, 0x3E, 0x18, 0xAF, 0x14, 0x85, 0x79,
+ 0x13, 0x50, 0x9D, 0xA9, 0x99, 0xB4, 0x6C, 0xDD,
+ 0x3D, 0x4C, 0x16, 0x97, 0x60, 0xC8, 0x3A, 0xD2
+ },
+ {
+ 0x40, 0xB9, 0x91, 0x6F, 0x09, 0x3E, 0x02, 0x7A,
+ 0x87, 0x86, 0x64, 0x18, 0x18, 0x92, 0x06, 0x20,
+ 0x47, 0x2F, 0xBC, 0xF6, 0x8F, 0x70, 0x1D, 0x1B,
+ 0x68, 0x06, 0x32, 0xE6, 0x99, 0x6B, 0xDE, 0xD3
+ },
+ {
+ 0x24, 0xC4, 0xCB, 0xBA, 0x07, 0x11, 0x98, 0x31,
+ 0xA7, 0x26, 0xB0, 0x53, 0x05, 0xD9, 0x6D, 0xA0,
+ 0x2F, 0xF8, 0xB1, 0x48, 0xF0, 0xDA, 0x44, 0x0F,
+ 0xE2, 0x33, 0xBC, 0xAA, 0x32, 0xC7, 0x2F, 0x6F
+ },
+ {
+ 0x5D, 0x20, 0x15, 0x10, 0x25, 0x00, 0x20, 0xB7,
+ 0x83, 0x68, 0x96, 0x88, 0xAB, 0xBF, 0x8E, 0xCF,
+ 0x25, 0x94, 0xA9, 0x6A, 0x08, 0xF2, 0xBF, 0xEC,
+ 0x6C, 0xE0, 0x57, 0x44, 0x65, 0xDD, 0xED, 0x71
+ },
+ {
+ 0x04, 0x3B, 0x97, 0xE3, 0x36, 0xEE, 0x6F, 0xDB,
+ 0xBE, 0x2B, 0x50, 0xF2, 0x2A, 0xF8, 0x32, 0x75,
+ 0xA4, 0x08, 0x48, 0x05, 0xD2, 0xD5, 0x64, 0x59,
+ 0x62, 0x45, 0x4B, 0x6C, 0x9B, 0x80, 0x53, 0xA0
+ },
+ {
+ 0x56, 0x48, 0x35, 0xCB, 0xAE, 0xA7, 0x74, 0x94,
+ 0x85, 0x68, 0xBE, 0x36, 0xCF, 0x52, 0xFC, 0xDD,
+ 0x83, 0x93, 0x4E, 0xB0, 0xA2, 0x75, 0x12, 0xDB,
+ 0xE3, 0xE2, 0xDB, 0x47, 0xB9, 0xE6, 0x63, 0x5A
+ },
+ {
+ 0xF2, 0x1C, 0x33, 0xF4, 0x7B, 0xDE, 0x40, 0xA2,
+ 0xA1, 0x01, 0xC9, 0xCD, 0xE8, 0x02, 0x7A, 0xAF,
+ 0x61, 0xA3, 0x13, 0x7D, 0xE2, 0x42, 0x2B, 0x30,
+ 0x03, 0x5A, 0x04, 0xC2, 0x70, 0x89, 0x41, 0x83
+ },
+ {
+ 0x9D, 0xB0, 0xEF, 0x74, 0xE6, 0x6C, 0xBB, 0x84,
+ 0x2E, 0xB0, 0xE0, 0x73, 0x43, 0xA0, 0x3C, 0x5C,
+ 0x56, 0x7E, 0x37, 0x2B, 0x3F, 0x23, 0xB9, 0x43,
+ 0xC7, 0x88, 0xA4, 0xF2, 0x50, 0xF6, 0x78, 0x91
+ },
+ {
+ 0xAB, 0x8D, 0x08, 0x65, 0x5F, 0xF1, 0xD3, 0xFE,
+ 0x87, 0x58, 0xD5, 0x62, 0x23, 0x5F, 0xD2, 0x3E,
+ 0x7C, 0xF9, 0xDC, 0xAA, 0xD6, 0x58, 0x87, 0x2A,
+ 0x49, 0xE5, 0xD3, 0x18, 0x3B, 0x6C, 0xCE, 0xBD
+ },
+ {
+ 0x6F, 0x27, 0xF7, 0x7E, 0x7B, 0xCF, 0x46, 0xA1,
+ 0xE9, 0x63, 0xAD, 0xE0, 0x30, 0x97, 0x33, 0x54,
+ 0x30, 0x31, 0xDC, 0xCD, 0xD4, 0x7C, 0xAA, 0xC1,
+ 0x74, 0xD7, 0xD2, 0x7C, 0xE8, 0x07, 0x7E, 0x8B
+ },
+ {
+ 0xE3, 0xCD, 0x54, 0xDA, 0x7E, 0x44, 0x4C, 0xAA,
+ 0x62, 0x07, 0x56, 0x95, 0x25, 0xA6, 0x70, 0xEB,
+ 0xAE, 0x12, 0x78, 0xDE, 0x4E, 0x3F, 0xE2, 0x68,
+ 0x4B, 0x3E, 0x33, 0xF5, 0xEF, 0x90, 0xCC, 0x1B
+ },
+ {
+ 0xB2, 0xC3, 0xE3, 0x3A, 0x51, 0xD2, 0x2C, 0x4C,
+ 0x08, 0xFC, 0x09, 0x89, 0xC8, 0x73, 0xC9, 0xCC,
+ 0x41, 0x50, 0x57, 0x9B, 0x1E, 0x61, 0x63, 0xFA,
+ 0x69, 0x4A, 0xD5, 0x1D, 0x53, 0xD7, 0x12, 0xDC
+ },
+ {
+ 0xBE, 0x7F, 0xDA, 0x98, 0x3E, 0x13, 0x18, 0x9B,
+ 0x4C, 0x77, 0xE0, 0xA8, 0x09, 0x20, 0xB6, 0xE0,
+ 0xE0, 0xEA, 0x80, 0xC3, 0xB8, 0x4D, 0xBE, 0x7E,
+ 0x71, 0x17, 0xD2, 0x53, 0xF4, 0x81, 0x12, 0xF4
+ },
+ {
+ 0xB6, 0x00, 0x8C, 0x28, 0xFA, 0xE0, 0x8A, 0xA4,
+ 0x27, 0xE5, 0xBD, 0x3A, 0xAD, 0x36, 0xF1, 0x00,
+ 0x21, 0xF1, 0x6C, 0x77, 0xCF, 0xEA, 0xBE, 0xD0,
+ 0x7F, 0x97, 0xCC, 0x7D, 0xC1, 0xF1, 0x28, 0x4A
+ },
+ {
+ 0x6E, 0x4E, 0x67, 0x60, 0xC5, 0x38, 0xF2, 0xE9,
+ 0x7B, 0x3A, 0xDB, 0xFB, 0xBC, 0xDE, 0x57, 0xF8,
+ 0x96, 0x6B, 0x7E, 0xA8, 0xFC, 0xB5, 0xBF, 0x7E,
+ 0xFE, 0xC9, 0x13, 0xFD, 0x2A, 0x2B, 0x0C, 0x55
+ },
+ {
+ 0x4A, 0xE5, 0x1F, 0xD1, 0x83, 0x4A, 0xA5, 0xBD,
+ 0x9A, 0x6F, 0x7E, 0xC3, 0x9F, 0xC6, 0x63, 0x33,
+ 0x8D, 0xC5, 0xD2, 0xE2, 0x07, 0x61, 0x56, 0x6D,
+ 0x90, 0xCC, 0x68, 0xB1, 0xCB, 0x87, 0x5E, 0xD8
+ },
+ {
+ 0xB6, 0x73, 0xAA, 0xD7, 0x5A, 0xB1, 0xFD, 0xB5,
+ 0x40, 0x1A, 0xBF, 0xA1, 0xBF, 0x89, 0xF3, 0xAD,
+ 0xD2, 0xEB, 0xC4, 0x68, 0xDF, 0x36, 0x24, 0xA4,
+ 0x78, 0xF4, 0xFE, 0x85, 0x9D, 0x8D, 0x55, 0xE2
+ },
+ {
+ 0x13, 0xC9, 0x47, 0x1A, 0x98, 0x55, 0x91, 0x35,
+ 0x39, 0x83, 0x66, 0x60, 0x39, 0x8D, 0xA0, 0xF3,
+ 0xF9, 0x9A, 0xDA, 0x08, 0x47, 0x9C, 0x69, 0xD1,
+ 0xB7, 0xFC, 0xAA, 0x34, 0x61, 0xDD, 0x7E, 0x59
+ },
+ {
+ 0x2C, 0x11, 0xF4, 0xA7, 0xF9, 0x9A, 0x1D, 0x23,
+ 0xA5, 0x8B, 0xB6, 0x36, 0x35, 0x0F, 0xE8, 0x49,
+ 0xF2, 0x9C, 0xBA, 0xC1, 0xB2, 0xA1, 0x11, 0x2D,
+ 0x9F, 0x1E, 0xD5, 0xBC, 0x5B, 0x31, 0x3C, 0xCD
+ },
+ {
+ 0xC7, 0xD3, 0xC0, 0x70, 0x6B, 0x11, 0xAE, 0x74,
+ 0x1C, 0x05, 0xA1, 0xEF, 0x15, 0x0D, 0xD6, 0x5B,
+ 0x54, 0x94, 0xD6, 0xD5, 0x4C, 0x9A, 0x86, 0xE2,
+ 0x61, 0x78, 0x54, 0xE6, 0xAE, 0xEE, 0xBB, 0xD9
+ },
+ {
+ 0x19, 0x4E, 0x10, 0xC9, 0x38, 0x93, 0xAF, 0xA0,
+ 0x64, 0xC3, 0xAC, 0x04, 0xC0, 0xDD, 0x80, 0x8D,
+ 0x79, 0x1C, 0x3D, 0x4B, 0x75, 0x56, 0xE8, 0x9D,
+ 0x8D, 0x9C, 0xB2, 0x25, 0xC4, 0xB3, 0x33, 0x39
+ },
+ {
+ 0x6F, 0xC4, 0x98, 0x8B, 0x8F, 0x78, 0x54, 0x6B,
+ 0x16, 0x88, 0x99, 0x18, 0x45, 0x90, 0x8F, 0x13,
+ 0x4B, 0x6A, 0x48, 0x2E, 0x69, 0x94, 0xB3, 0xD4,
+ 0x83, 0x17, 0xBF, 0x08, 0xDB, 0x29, 0x21, 0x85
+ },
+ {
+ 0x56, 0x65, 0xBE, 0xB8, 0xB0, 0x95, 0x55, 0x25,
+ 0x81, 0x3B, 0x59, 0x81, 0xCD, 0x14, 0x2E, 0xD4,
+ 0xD0, 0x3F, 0xBA, 0x38, 0xA6, 0xF3, 0xE5, 0xAD,
+ 0x26, 0x8E, 0x0C, 0xC2, 0x70, 0xD1, 0xCD, 0x11
+ },
+ {
+ 0xB8, 0x83, 0xD6, 0x8F, 0x5F, 0xE5, 0x19, 0x36,
+ 0x43, 0x1B, 0xA4, 0x25, 0x67, 0x38, 0x05, 0x3B,
+ 0x1D, 0x04, 0x26, 0xD4, 0xCB, 0x64, 0xB1, 0x6E,
+ 0x83, 0xBA, 0xDC, 0x5E, 0x9F, 0xBE, 0x3B, 0x81
+ },
+ {
+ 0x53, 0xE7, 0xB2, 0x7E, 0xA5, 0x9C, 0x2F, 0x6D,
+ 0xBB, 0x50, 0x76, 0x9E, 0x43, 0x55, 0x4D, 0xF3,
+ 0x5A, 0xF8, 0x9F, 0x48, 0x22, 0xD0, 0x46, 0x6B,
+ 0x00, 0x7D, 0xD6, 0xF6, 0xDE, 0xAF, 0xFF, 0x02
+ },
+ {
+ 0x1F, 0x1A, 0x02, 0x29, 0xD4, 0x64, 0x0F, 0x01,
+ 0x90, 0x15, 0x88, 0xD9, 0xDE, 0xC2, 0x2D, 0x13,
+ 0xFC, 0x3E, 0xB3, 0x4A, 0x61, 0xB3, 0x29, 0x38,
+ 0xEF, 0xBF, 0x53, 0x34, 0xB2, 0x80, 0x0A, 0xFA
+ },
+ {
+ 0xC2, 0xB4, 0x05, 0xAF, 0xA0, 0xFA, 0x66, 0x68,
+ 0x85, 0x2A, 0xEE, 0x4D, 0x88, 0x04, 0x08, 0x53,
+ 0xFA, 0xB8, 0x00, 0xE7, 0x2B, 0x57, 0x58, 0x14,
+ 0x18, 0xE5, 0x50, 0x6F, 0x21, 0x4C, 0x7D, 0x1F
+ },
+ {
+ 0xC0, 0x8A, 0xA1, 0xC2, 0x86, 0xD7, 0x09, 0xFD,
+ 0xC7, 0x47, 0x37, 0x44, 0x97, 0x71, 0x88, 0xC8,
+ 0x95, 0xBA, 0x01, 0x10, 0x14, 0x24, 0x7E, 0x4E,
+ 0xFA, 0x8D, 0x07, 0xE7, 0x8F, 0xEC, 0x69, 0x5C
+ },
+ {
+ 0xF0, 0x3F, 0x57, 0x89, 0xD3, 0x33, 0x6B, 0x80,
+ 0xD0, 0x02, 0xD5, 0x9F, 0xDF, 0x91, 0x8B, 0xDB,
+ 0x77, 0x5B, 0x00, 0x95, 0x6E, 0xD5, 0x52, 0x8E,
+ 0x86, 0xAA, 0x99, 0x4A, 0xCB, 0x38, 0xFE, 0x2D
+ },
+};
+
+
+
+
+static const uint8_t blake2s_keyed_kat[KAT_LENGTH][BLAKE2S_OUTBYTES] =
+{
+ {
+ 0x48, 0xA8, 0x99, 0x7D, 0xA4, 0x07, 0x87, 0x6B,
+ 0x3D, 0x79, 0xC0, 0xD9, 0x23, 0x25, 0xAD, 0x3B,
+ 0x89, 0xCB, 0xB7, 0x54, 0xD8, 0x6A, 0xB7, 0x1A,
+ 0xEE, 0x04, 0x7A, 0xD3, 0x45, 0xFD, 0x2C, 0x49
+ },
+ {
+ 0x40, 0xD1, 0x5F, 0xEE, 0x7C, 0x32, 0x88, 0x30,
+ 0x16, 0x6A, 0xC3, 0xF9, 0x18, 0x65, 0x0F, 0x80,
+ 0x7E, 0x7E, 0x01, 0xE1, 0x77, 0x25, 0x8C, 0xDC,
+ 0x0A, 0x39, 0xB1, 0x1F, 0x59, 0x80, 0x66, 0xF1
+ },
+ {
+ 0x6B, 0xB7, 0x13, 0x00, 0x64, 0x4C, 0xD3, 0x99,
+ 0x1B, 0x26, 0xCC, 0xD4, 0xD2, 0x74, 0xAC, 0xD1,
+ 0xAD, 0xEA, 0xB8, 0xB1, 0xD7, 0x91, 0x45, 0x46,
+ 0xC1, 0x19, 0x8B, 0xBE, 0x9F, 0xC9, 0xD8, 0x03
+ },
+ {
+ 0x1D, 0x22, 0x0D, 0xBE, 0x2E, 0xE1, 0x34, 0x66,
+ 0x1F, 0xDF, 0x6D, 0x9E, 0x74, 0xB4, 0x17, 0x04,
+ 0x71, 0x05, 0x56, 0xF2, 0xF6, 0xE5, 0xA0, 0x91,
+ 0xB2, 0x27, 0x69, 0x74, 0x45, 0xDB, 0xEA, 0x6B
+ },
+ {
+ 0xF6, 0xC3, 0xFB, 0xAD, 0xB4, 0xCC, 0x68, 0x7A,
+ 0x00, 0x64, 0xA5, 0xBE, 0x6E, 0x79, 0x1B, 0xEC,
+ 0x63, 0xB8, 0x68, 0xAD, 0x62, 0xFB, 0xA6, 0x1B,
+ 0x37, 0x57, 0xEF, 0x9C, 0xA5, 0x2E, 0x05, 0xB2
+ },
+ {
+ 0x49, 0xC1, 0xF2, 0x11, 0x88, 0xDF, 0xD7, 0x69,
+ 0xAE, 0xA0, 0xE9, 0x11, 0xDD, 0x6B, 0x41, 0xF1,
+ 0x4D, 0xAB, 0x10, 0x9D, 0x2B, 0x85, 0x97, 0x7A,
+ 0xA3, 0x08, 0x8B, 0x5C, 0x70, 0x7E, 0x85, 0x98
+ },
+ {
+ 0xFD, 0xD8, 0x99, 0x3D, 0xCD, 0x43, 0xF6, 0x96,
+ 0xD4, 0x4F, 0x3C, 0xEA, 0x0F, 0xF3, 0x53, 0x45,
+ 0x23, 0x4E, 0xC8, 0xEE, 0x08, 0x3E, 0xB3, 0xCA,
+ 0xDA, 0x01, 0x7C, 0x7F, 0x78, 0xC1, 0x71, 0x43
+ },
+ {
+ 0xE6, 0xC8, 0x12, 0x56, 0x37, 0x43, 0x8D, 0x09,
+ 0x05, 0xB7, 0x49, 0xF4, 0x65, 0x60, 0xAC, 0x89,
+ 0xFD, 0x47, 0x1C, 0xF8, 0x69, 0x2E, 0x28, 0xFA,
+ 0xB9, 0x82, 0xF7, 0x3F, 0x01, 0x9B, 0x83, 0xA9
+ },
+ {
+ 0x19, 0xFC, 0x8C, 0xA6, 0x97, 0x9D, 0x60, 0xE6,
+ 0xED, 0xD3, 0xB4, 0x54, 0x1E, 0x2F, 0x96, 0x7C,
+ 0xED, 0x74, 0x0D, 0xF6, 0xEC, 0x1E, 0xAE, 0xBB,
+ 0xFE, 0x81, 0x38, 0x32, 0xE9, 0x6B, 0x29, 0x74
+ },
+ {
+ 0xA6, 0xAD, 0x77, 0x7C, 0xE8, 0x81, 0xB5, 0x2B,
+ 0xB5, 0xA4, 0x42, 0x1A, 0xB6, 0xCD, 0xD2, 0xDF,
+ 0xBA, 0x13, 0xE9, 0x63, 0x65, 0x2D, 0x4D, 0x6D,
+ 0x12, 0x2A, 0xEE, 0x46, 0x54, 0x8C, 0x14, 0xA7
+ },
+ {
+ 0xF5, 0xC4, 0xB2, 0xBA, 0x1A, 0x00, 0x78, 0x1B,
+ 0x13, 0xAB, 0xA0, 0x42, 0x52, 0x42, 0xC6, 0x9C,
+ 0xB1, 0x55, 0x2F, 0x3F, 0x71, 0xA9, 0xA3, 0xBB,
+ 0x22, 0xB4, 0xA6, 0xB4, 0x27, 0x7B, 0x46, 0xDD
+ },
+ {
+ 0xE3, 0x3C, 0x4C, 0x9B, 0xD0, 0xCC, 0x7E, 0x45,
+ 0xC8, 0x0E, 0x65, 0xC7, 0x7F, 0xA5, 0x99, 0x7F,
+ 0xEC, 0x70, 0x02, 0x73, 0x85, 0x41, 0x50, 0x9E,
+ 0x68, 0xA9, 0x42, 0x38, 0x91, 0xE8, 0x22, 0xA3
+ },
+ {
+ 0xFB, 0xA1, 0x61, 0x69, 0xB2, 0xC3, 0xEE, 0x10,
+ 0x5B, 0xE6, 0xE1, 0xE6, 0x50, 0xE5, 0xCB, 0xF4,
+ 0x07, 0x46, 0xB6, 0x75, 0x3D, 0x03, 0x6A, 0xB5,
+ 0x51, 0x79, 0x01, 0x4A, 0xD7, 0xEF, 0x66, 0x51
+ },
+ {
+ 0xF5, 0xC4, 0xBE, 0xC6, 0xD6, 0x2F, 0xC6, 0x08,
+ 0xBF, 0x41, 0xCC, 0x11, 0x5F, 0x16, 0xD6, 0x1C,
+ 0x7E, 0xFD, 0x3F, 0xF6, 0xC6, 0x56, 0x92, 0xBB,
+ 0xE0, 0xAF, 0xFF, 0xB1, 0xFE, 0xDE, 0x74, 0x75
+ },
+ {
+ 0xA4, 0x86, 0x2E, 0x76, 0xDB, 0x84, 0x7F, 0x05,
+ 0xBA, 0x17, 0xED, 0xE5, 0xDA, 0x4E, 0x7F, 0x91,
+ 0xB5, 0x92, 0x5C, 0xF1, 0xAD, 0x4B, 0xA1, 0x27,
+ 0x32, 0xC3, 0x99, 0x57, 0x42, 0xA5, 0xCD, 0x6E
+ },
+ {
+ 0x65, 0xF4, 0xB8, 0x60, 0xCD, 0x15, 0xB3, 0x8E,
+ 0xF8, 0x14, 0xA1, 0xA8, 0x04, 0x31, 0x4A, 0x55,
+ 0xBE, 0x95, 0x3C, 0xAA, 0x65, 0xFD, 0x75, 0x8A,
+ 0xD9, 0x89, 0xFF, 0x34, 0xA4, 0x1C, 0x1E, 0xEA
+ },
+ {
+ 0x19, 0xBA, 0x23, 0x4F, 0x0A, 0x4F, 0x38, 0x63,
+ 0x7D, 0x18, 0x39, 0xF9, 0xD9, 0xF7, 0x6A, 0xD9,
+ 0x1C, 0x85, 0x22, 0x30, 0x71, 0x43, 0xC9, 0x7D,
+ 0x5F, 0x93, 0xF6, 0x92, 0x74, 0xCE, 0xC9, 0xA7
+ },
+ {
+ 0x1A, 0x67, 0x18, 0x6C, 0xA4, 0xA5, 0xCB, 0x8E,
+ 0x65, 0xFC, 0xA0, 0xE2, 0xEC, 0xBC, 0x5D, 0xDC,
+ 0x14, 0xAE, 0x38, 0x1B, 0xB8, 0xBF, 0xFE, 0xB9,
+ 0xE0, 0xA1, 0x03, 0x44, 0x9E, 0x3E, 0xF0, 0x3C
+ },
+ {
+ 0xAF, 0xBE, 0xA3, 0x17, 0xB5, 0xA2, 0xE8, 0x9C,
+ 0x0B, 0xD9, 0x0C, 0xCF, 0x5D, 0x7F, 0xD0, 0xED,
+ 0x57, 0xFE, 0x58, 0x5E, 0x4B, 0xE3, 0x27, 0x1B,
+ 0x0A, 0x6B, 0xF0, 0xF5, 0x78, 0x6B, 0x0F, 0x26
+ },
+ {
+ 0xF1, 0xB0, 0x15, 0x58, 0xCE, 0x54, 0x12, 0x62,
+ 0xF5, 0xEC, 0x34, 0x29, 0x9D, 0x6F, 0xB4, 0x09,
+ 0x00, 0x09, 0xE3, 0x43, 0x4B, 0xE2, 0xF4, 0x91,
+ 0x05, 0xCF, 0x46, 0xAF, 0x4D, 0x2D, 0x41, 0x24
+ },
+ {
+ 0x13, 0xA0, 0xA0, 0xC8, 0x63, 0x35, 0x63, 0x5E,
+ 0xAA, 0x74, 0xCA, 0x2D, 0x5D, 0x48, 0x8C, 0x79,
+ 0x7B, 0xBB, 0x4F, 0x47, 0xDC, 0x07, 0x10, 0x50,
+ 0x15, 0xED, 0x6A, 0x1F, 0x33, 0x09, 0xEF, 0xCE
+ },
+ {
+ 0x15, 0x80, 0xAF, 0xEE, 0xBE, 0xBB, 0x34, 0x6F,
+ 0x94, 0xD5, 0x9F, 0xE6, 0x2D, 0xA0, 0xB7, 0x92,
+ 0x37, 0xEA, 0xD7, 0xB1, 0x49, 0x1F, 0x56, 0x67,
+ 0xA9, 0x0E, 0x45, 0xED, 0xF6, 0xCA, 0x8B, 0x03
+ },
+ {
+ 0x20, 0xBE, 0x1A, 0x87, 0x5B, 0x38, 0xC5, 0x73,
+ 0xDD, 0x7F, 0xAA, 0xA0, 0xDE, 0x48, 0x9D, 0x65,
+ 0x5C, 0x11, 0xEF, 0xB6, 0xA5, 0x52, 0x69, 0x8E,
+ 0x07, 0xA2, 0xD3, 0x31, 0xB5, 0xF6, 0x55, 0xC3
+ },
+ {
+ 0xBE, 0x1F, 0xE3, 0xC4, 0xC0, 0x40, 0x18, 0xC5,
+ 0x4C, 0x4A, 0x0F, 0x6B, 0x9A, 0x2E, 0xD3, 0xC5,
+ 0x3A, 0xBE, 0x3A, 0x9F, 0x76, 0xB4, 0xD2, 0x6D,
+ 0xE5, 0x6F, 0xC9, 0xAE, 0x95, 0x05, 0x9A, 0x99
+ },
+ {
+ 0xE3, 0xE3, 0xAC, 0xE5, 0x37, 0xEB, 0x3E, 0xDD,
+ 0x84, 0x63, 0xD9, 0xAD, 0x35, 0x82, 0xE1, 0x3C,
+ 0xF8, 0x65, 0x33, 0xFF, 0xDE, 0x43, 0xD6, 0x68,
+ 0xDD, 0x2E, 0x93, 0xBB, 0xDB, 0xD7, 0x19, 0x5A
+ },
+ {
+ 0x11, 0x0C, 0x50, 0xC0, 0xBF, 0x2C, 0x6E, 0x7A,
+ 0xEB, 0x7E, 0x43, 0x5D, 0x92, 0xD1, 0x32, 0xAB,
+ 0x66, 0x55, 0x16, 0x8E, 0x78, 0xA2, 0xDE, 0xCD,
+ 0xEC, 0x33, 0x30, 0x77, 0x76, 0x84, 0xD9, 0xC1
+ },
+ {
+ 0xE9, 0xBA, 0x8F, 0x50, 0x5C, 0x9C, 0x80, 0xC0,
+ 0x86, 0x66, 0xA7, 0x01, 0xF3, 0x36, 0x7E, 0x6C,
+ 0xC6, 0x65, 0xF3, 0x4B, 0x22, 0xE7, 0x3C, 0x3C,
+ 0x04, 0x17, 0xEB, 0x1C, 0x22, 0x06, 0x08, 0x2F
+ },
+ {
+ 0x26, 0xCD, 0x66, 0xFC, 0xA0, 0x23, 0x79, 0xC7,
+ 0x6D, 0xF1, 0x23, 0x17, 0x05, 0x2B, 0xCA, 0xFD,
+ 0x6C, 0xD8, 0xC3, 0xA7, 0xB8, 0x90, 0xD8, 0x05,
+ 0xF3, 0x6C, 0x49, 0x98, 0x97, 0x82, 0x43, 0x3A
+ },
+ {
+ 0x21, 0x3F, 0x35, 0x96, 0xD6, 0xE3, 0xA5, 0xD0,
+ 0xE9, 0x93, 0x2C, 0xD2, 0x15, 0x91, 0x46, 0x01,
+ 0x5E, 0x2A, 0xBC, 0x94, 0x9F, 0x47, 0x29, 0xEE,
+ 0x26, 0x32, 0xFE, 0x1E, 0xDB, 0x78, 0xD3, 0x37
+ },
+ {
+ 0x10, 0x15, 0xD7, 0x01, 0x08, 0xE0, 0x3B, 0xE1,
+ 0xC7, 0x02, 0xFE, 0x97, 0x25, 0x36, 0x07, 0xD1,
+ 0x4A, 0xEE, 0x59, 0x1F, 0x24, 0x13, 0xEA, 0x67,
+ 0x87, 0x42, 0x7B, 0x64, 0x59, 0xFF, 0x21, 0x9A
+ },
+ {
+ 0x3C, 0xA9, 0x89, 0xDE, 0x10, 0xCF, 0xE6, 0x09,
+ 0x90, 0x94, 0x72, 0xC8, 0xD3, 0x56, 0x10, 0x80,
+ 0x5B, 0x2F, 0x97, 0x77, 0x34, 0xCF, 0x65, 0x2C,
+ 0xC6, 0x4B, 0x3B, 0xFC, 0x88, 0x2D, 0x5D, 0x89
+ },
+ {
+ 0xB6, 0x15, 0x6F, 0x72, 0xD3, 0x80, 0xEE, 0x9E,
+ 0xA6, 0xAC, 0xD1, 0x90, 0x46, 0x4F, 0x23, 0x07,
+ 0xA5, 0xC1, 0x79, 0xEF, 0x01, 0xFD, 0x71, 0xF9,
+ 0x9F, 0x2D, 0x0F, 0x7A, 0x57, 0x36, 0x0A, 0xEA
+ },
+ {
+ 0xC0, 0x3B, 0xC6, 0x42, 0xB2, 0x09, 0x59, 0xCB,
+ 0xE1, 0x33, 0xA0, 0x30, 0x3E, 0x0C, 0x1A, 0xBF,
+ 0xF3, 0xE3, 0x1E, 0xC8, 0xE1, 0xA3, 0x28, 0xEC,
+ 0x85, 0x65, 0xC3, 0x6D, 0xEC, 0xFF, 0x52, 0x65
+ },
+ {
+ 0x2C, 0x3E, 0x08, 0x17, 0x6F, 0x76, 0x0C, 0x62,
+ 0x64, 0xC3, 0xA2, 0xCD, 0x66, 0xFE, 0xC6, 0xC3,
+ 0xD7, 0x8D, 0xE4, 0x3F, 0xC1, 0x92, 0x45, 0x7B,
+ 0x2A, 0x4A, 0x66, 0x0A, 0x1E, 0x0E, 0xB2, 0x2B
+ },
+ {
+ 0xF7, 0x38, 0xC0, 0x2F, 0x3C, 0x1B, 0x19, 0x0C,
+ 0x51, 0x2B, 0x1A, 0x32, 0xDE, 0xAB, 0xF3, 0x53,
+ 0x72, 0x8E, 0x0E, 0x9A, 0xB0, 0x34, 0x49, 0x0E,
+ 0x3C, 0x34, 0x09, 0x94, 0x6A, 0x97, 0xAE, 0xEC
+ },
+ {
+ 0x8B, 0x18, 0x80, 0xDF, 0x30, 0x1C, 0xC9, 0x63,
+ 0x41, 0x88, 0x11, 0x08, 0x89, 0x64, 0x83, 0x92,
+ 0x87, 0xFF, 0x7F, 0xE3, 0x1C, 0x49, 0xEA, 0x6E,
+ 0xBD, 0x9E, 0x48, 0xBD, 0xEE, 0xE4, 0x97, 0xC5
+ },
+ {
+ 0x1E, 0x75, 0xCB, 0x21, 0xC6, 0x09, 0x89, 0x02,
+ 0x03, 0x75, 0xF1, 0xA7, 0xA2, 0x42, 0x83, 0x9F,
+ 0x0B, 0x0B, 0x68, 0x97, 0x3A, 0x4C, 0x2A, 0x05,
+ 0xCF, 0x75, 0x55, 0xED, 0x5A, 0xAE, 0xC4, 0xC1
+ },
+ {
+ 0x62, 0xBF, 0x8A, 0x9C, 0x32, 0xA5, 0xBC, 0xCF,
+ 0x29, 0x0B, 0x6C, 0x47, 0x4D, 0x75, 0xB2, 0xA2,
+ 0xA4, 0x09, 0x3F, 0x1A, 0x9E, 0x27, 0x13, 0x94,
+ 0x33, 0xA8, 0xF2, 0xB3, 0xBC, 0xE7, 0xB8, 0xD7
+ },
+ {
+ 0x16, 0x6C, 0x83, 0x50, 0xD3, 0x17, 0x3B, 0x5E,
+ 0x70, 0x2B, 0x78, 0x3D, 0xFD, 0x33, 0xC6, 0x6E,
+ 0xE0, 0x43, 0x27, 0x42, 0xE9, 0xB9, 0x2B, 0x99,
+ 0x7F, 0xD2, 0x3C, 0x60, 0xDC, 0x67, 0x56, 0xCA
+ },
+ {
+ 0x04, 0x4A, 0x14, 0xD8, 0x22, 0xA9, 0x0C, 0xAC,
+ 0xF2, 0xF5, 0xA1, 0x01, 0x42, 0x8A, 0xDC, 0x8F,
+ 0x41, 0x09, 0x38, 0x6C, 0xCB, 0x15, 0x8B, 0xF9,
+ 0x05, 0xC8, 0x61, 0x8B, 0x8E, 0xE2, 0x4E, 0xC3
+ },
+ {
+ 0x38, 0x7D, 0x39, 0x7E, 0xA4, 0x3A, 0x99, 0x4B,
+ 0xE8, 0x4D, 0x2D, 0x54, 0x4A, 0xFB, 0xE4, 0x81,
+ 0xA2, 0x00, 0x0F, 0x55, 0x25, 0x26, 0x96, 0xBB,
+ 0xA2, 0xC5, 0x0C, 0x8E, 0xBD, 0x10, 0x13, 0x47
+ },
+ {
+ 0x56, 0xF8, 0xCC, 0xF1, 0xF8, 0x64, 0x09, 0xB4,
+ 0x6C, 0xE3, 0x61, 0x66, 0xAE, 0x91, 0x65, 0x13,
+ 0x84, 0x41, 0x57, 0x75, 0x89, 0xDB, 0x08, 0xCB,
+ 0xC5, 0xF6, 0x6C, 0xA2, 0x97, 0x43, 0xB9, 0xFD
+ },
+ {
+ 0x97, 0x06, 0xC0, 0x92, 0xB0, 0x4D, 0x91, 0xF5,
+ 0x3D, 0xFF, 0x91, 0xFA, 0x37, 0xB7, 0x49, 0x3D,
+ 0x28, 0xB5, 0x76, 0xB5, 0xD7, 0x10, 0x46, 0x9D,
+ 0xF7, 0x94, 0x01, 0x66, 0x22, 0x36, 0xFC, 0x03
+ },
+ {
+ 0x87, 0x79, 0x68, 0x68, 0x6C, 0x06, 0x8C, 0xE2,
+ 0xF7, 0xE2, 0xAD, 0xCF, 0xF6, 0x8B, 0xF8, 0x74,
+ 0x8E, 0xDF, 0x3C, 0xF8, 0x62, 0xCF, 0xB4, 0xD3,
+ 0x94, 0x7A, 0x31, 0x06, 0x95, 0x80, 0x54, 0xE3
+ },
+ {
+ 0x88, 0x17, 0xE5, 0x71, 0x98, 0x79, 0xAC, 0xF7,
+ 0x02, 0x47, 0x87, 0xEC, 0xCD, 0xB2, 0x71, 0x03,
+ 0x55, 0x66, 0xCF, 0xA3, 0x33, 0xE0, 0x49, 0x40,
+ 0x7C, 0x01, 0x78, 0xCC, 0xC5, 0x7A, 0x5B, 0x9F
+ },
+ {
+ 0x89, 0x38, 0x24, 0x9E, 0x4B, 0x50, 0xCA, 0xDA,
+ 0xCC, 0xDF, 0x5B, 0x18, 0x62, 0x13, 0x26, 0xCB,
+ 0xB1, 0x52, 0x53, 0xE3, 0x3A, 0x20, 0xF5, 0x63,
+ 0x6E, 0x99, 0x5D, 0x72, 0x47, 0x8D, 0xE4, 0x72
+ },
+ {
+ 0xF1, 0x64, 0xAB, 0xBA, 0x49, 0x63, 0xA4, 0x4D,
+ 0x10, 0x72, 0x57, 0xE3, 0x23, 0x2D, 0x90, 0xAC,
+ 0xA5, 0xE6, 0x6A, 0x14, 0x08, 0x24, 0x8C, 0x51,
+ 0x74, 0x1E, 0x99, 0x1D, 0xB5, 0x22, 0x77, 0x56
+ },
+ {
+ 0xD0, 0x55, 0x63, 0xE2, 0xB1, 0xCB, 0xA0, 0xC4,
+ 0xA2, 0xA1, 0xE8, 0xBD, 0xE3, 0xA1, 0xA0, 0xD9,
+ 0xF5, 0xB4, 0x0C, 0x85, 0xA0, 0x70, 0xD6, 0xF5,
+ 0xFB, 0x21, 0x06, 0x6E, 0xAD, 0x5D, 0x06, 0x01
+ },
+ {
+ 0x03, 0xFB, 0xB1, 0x63, 0x84, 0xF0, 0xA3, 0x86,
+ 0x6F, 0x4C, 0x31, 0x17, 0x87, 0x76, 0x66, 0xEF,
+ 0xBF, 0x12, 0x45, 0x97, 0x56, 0x4B, 0x29, 0x3D,
+ 0x4A, 0xAB, 0x0D, 0x26, 0x9F, 0xAB, 0xDD, 0xFA
+ },
+ {
+ 0x5F, 0xA8, 0x48, 0x6A, 0xC0, 0xE5, 0x29, 0x64,
+ 0xD1, 0x88, 0x1B, 0xBE, 0x33, 0x8E, 0xB5, 0x4B,
+ 0xE2, 0xF7, 0x19, 0x54, 0x92, 0x24, 0x89, 0x20,
+ 0x57, 0xB4, 0xDA, 0x04, 0xBA, 0x8B, 0x34, 0x75
+ },
+ {
+ 0xCD, 0xFA, 0xBC, 0xEE, 0x46, 0x91, 0x11, 0x11,
+ 0x23, 0x6A, 0x31, 0x70, 0x8B, 0x25, 0x39, 0xD7,
+ 0x1F, 0xC2, 0x11, 0xD9, 0xB0, 0x9C, 0x0D, 0x85,
+ 0x30, 0xA1, 0x1E, 0x1D, 0xBF, 0x6E, 0xED, 0x01
+ },
+ {
+ 0x4F, 0x82, 0xDE, 0x03, 0xB9, 0x50, 0x47, 0x93,
+ 0xB8, 0x2A, 0x07, 0xA0, 0xBD, 0xCD, 0xFF, 0x31,
+ 0x4D, 0x75, 0x9E, 0x7B, 0x62, 0xD2, 0x6B, 0x78,
+ 0x49, 0x46, 0xB0, 0xD3, 0x6F, 0x91, 0x6F, 0x52
+ },
+ {
+ 0x25, 0x9E, 0xC7, 0xF1, 0x73, 0xBC, 0xC7, 0x6A,
+ 0x09, 0x94, 0xC9, 0x67, 0xB4, 0xF5, 0xF0, 0x24,
+ 0xC5, 0x60, 0x57, 0xFB, 0x79, 0xC9, 0x65, 0xC4,
+ 0xFA, 0xE4, 0x18, 0x75, 0xF0, 0x6A, 0x0E, 0x4C
+ },
+ {
+ 0x19, 0x3C, 0xC8, 0xE7, 0xC3, 0xE0, 0x8B, 0xB3,
+ 0x0F, 0x54, 0x37, 0xAA, 0x27, 0xAD, 0xE1, 0xF1,
+ 0x42, 0x36, 0x9B, 0x24, 0x6A, 0x67, 0x5B, 0x23,
+ 0x83, 0xE6, 0xDA, 0x9B, 0x49, 0xA9, 0x80, 0x9E
+ },
+ {
+ 0x5C, 0x10, 0x89, 0x6F, 0x0E, 0x28, 0x56, 0xB2,
+ 0xA2, 0xEE, 0xE0, 0xFE, 0x4A, 0x2C, 0x16, 0x33,
+ 0x56, 0x5D, 0x18, 0xF0, 0xE9, 0x3E, 0x1F, 0xAB,
+ 0x26, 0xC3, 0x73, 0xE8, 0xF8, 0x29, 0x65, 0x4D
+ },
+ {
+ 0xF1, 0x60, 0x12, 0xD9, 0x3F, 0x28, 0x85, 0x1A,
+ 0x1E, 0xB9, 0x89, 0xF5, 0xD0, 0xB4, 0x3F, 0x3F,
+ 0x39, 0xCA, 0x73, 0xC9, 0xA6, 0x2D, 0x51, 0x81,
+ 0xBF, 0xF2, 0x37, 0x53, 0x6B, 0xD3, 0x48, 0xC3
+ },
+ {
+ 0x29, 0x66, 0xB3, 0xCF, 0xAE, 0x1E, 0x44, 0xEA,
+ 0x99, 0x6D, 0xC5, 0xD6, 0x86, 0xCF, 0x25, 0xFA,
+ 0x05, 0x3F, 0xB6, 0xF6, 0x72, 0x01, 0xB9, 0xE4,
+ 0x6E, 0xAD, 0xE8, 0x5D, 0x0A, 0xD6, 0xB8, 0x06
+ },
+ {
+ 0xDD, 0xB8, 0x78, 0x24, 0x85, 0xE9, 0x00, 0xBC,
+ 0x60, 0xBC, 0xF4, 0xC3, 0x3A, 0x6F, 0xD5, 0x85,
+ 0x68, 0x0C, 0xC6, 0x83, 0xD5, 0x16, 0xEF, 0xA0,
+ 0x3E, 0xB9, 0x98, 0x5F, 0xAD, 0x87, 0x15, 0xFB
+ },
+ {
+ 0x4C, 0x4D, 0x6E, 0x71, 0xAE, 0xA0, 0x57, 0x86,
+ 0x41, 0x31, 0x48, 0xFC, 0x7A, 0x78, 0x6B, 0x0E,
+ 0xCA, 0xF5, 0x82, 0xCF, 0xF1, 0x20, 0x9F, 0x5A,
+ 0x80, 0x9F, 0xBA, 0x85, 0x04, 0xCE, 0x66, 0x2C
+ },
+ {
+ 0xFB, 0x4C, 0x5E, 0x86, 0xD7, 0xB2, 0x22, 0x9B,
+ 0x99, 0xB8, 0xBA, 0x6D, 0x94, 0xC2, 0x47, 0xEF,
+ 0x96, 0x4A, 0xA3, 0xA2, 0xBA, 0xE8, 0xED, 0xC7,
+ 0x75, 0x69, 0xF2, 0x8D, 0xBB, 0xFF, 0x2D, 0x4E
+ },
+ {
+ 0xE9, 0x4F, 0x52, 0x6D, 0xE9, 0x01, 0x96, 0x33,
+ 0xEC, 0xD5, 0x4A, 0xC6, 0x12, 0x0F, 0x23, 0x95,
+ 0x8D, 0x77, 0x18, 0xF1, 0xE7, 0x71, 0x7B, 0xF3,
+ 0x29, 0x21, 0x1A, 0x4F, 0xAE, 0xED, 0x4E, 0x6D
+ },
+ {
+ 0xCB, 0xD6, 0x66, 0x0A, 0x10, 0xDB, 0x3F, 0x23,
+ 0xF7, 0xA0, 0x3D, 0x4B, 0x9D, 0x40, 0x44, 0xC7,
+ 0x93, 0x2B, 0x28, 0x01, 0xAC, 0x89, 0xD6, 0x0B,
+ 0xC9, 0xEB, 0x92, 0xD6, 0x5A, 0x46, 0xC2, 0xA0
+ },
+ {
+ 0x88, 0x18, 0xBB, 0xD3, 0xDB, 0x4D, 0xC1, 0x23,
+ 0xB2, 0x5C, 0xBB, 0xA5, 0xF5, 0x4C, 0x2B, 0xC4,
+ 0xB3, 0xFC, 0xF9, 0xBF, 0x7D, 0x7A, 0x77, 0x09,
+ 0xF4, 0xAE, 0x58, 0x8B, 0x26, 0x7C, 0x4E, 0xCE
+ },
+ {
+ 0xC6, 0x53, 0x82, 0x51, 0x3F, 0x07, 0x46, 0x0D,
+ 0xA3, 0x98, 0x33, 0xCB, 0x66, 0x6C, 0x5E, 0xD8,
+ 0x2E, 0x61, 0xB9, 0xE9, 0x98, 0xF4, 0xB0, 0xC4,
+ 0x28, 0x7C, 0xEE, 0x56, 0xC3, 0xCC, 0x9B, 0xCD
+ },
+ {
+ 0x89, 0x75, 0xB0, 0x57, 0x7F, 0xD3, 0x55, 0x66,
+ 0xD7, 0x50, 0xB3, 0x62, 0xB0, 0x89, 0x7A, 0x26,
+ 0xC3, 0x99, 0x13, 0x6D, 0xF0, 0x7B, 0xAB, 0xAB,
+ 0xBD, 0xE6, 0x20, 0x3F, 0xF2, 0x95, 0x4E, 0xD4
+ },
+ {
+ 0x21, 0xFE, 0x0C, 0xEB, 0x00, 0x52, 0xBE, 0x7F,
+ 0xB0, 0xF0, 0x04, 0x18, 0x7C, 0xAC, 0xD7, 0xDE,
+ 0x67, 0xFA, 0x6E, 0xB0, 0x93, 0x8D, 0x92, 0x76,
+ 0x77, 0xF2, 0x39, 0x8C, 0x13, 0x23, 0x17, 0xA8
+ },
+ {
+ 0x2E, 0xF7, 0x3F, 0x3C, 0x26, 0xF1, 0x2D, 0x93,
+ 0x88, 0x9F, 0x3C, 0x78, 0xB6, 0xA6, 0x6C, 0x1D,
+ 0x52, 0xB6, 0x49, 0xDC, 0x9E, 0x85, 0x6E, 0x2C,
+ 0x17, 0x2E, 0xA7, 0xC5, 0x8A, 0xC2, 0xB5, 0xE3
+ },
+ {
+ 0x38, 0x8A, 0x3C, 0xD5, 0x6D, 0x73, 0x86, 0x7A,
+ 0xBB, 0x5F, 0x84, 0x01, 0x49, 0x2B, 0x6E, 0x26,
+ 0x81, 0xEB, 0x69, 0x85, 0x1E, 0x76, 0x7F, 0xD8,
+ 0x42, 0x10, 0xA5, 0x60, 0x76, 0xFB, 0x3D, 0xD3
+ },
+ {
+ 0xAF, 0x53, 0x3E, 0x02, 0x2F, 0xC9, 0x43, 0x9E,
+ 0x4E, 0x3C, 0xB8, 0x38, 0xEC, 0xD1, 0x86, 0x92,
+ 0x23, 0x2A, 0xDF, 0x6F, 0xE9, 0x83, 0x95, 0x26,
+ 0xD3, 0xC3, 0xDD, 0x1B, 0x71, 0x91, 0x0B, 0x1A
+ },
+ {
+ 0x75, 0x1C, 0x09, 0xD4, 0x1A, 0x93, 0x43, 0x88,
+ 0x2A, 0x81, 0xCD, 0x13, 0xEE, 0x40, 0x81, 0x8D,
+ 0x12, 0xEB, 0x44, 0xC6, 0xC7, 0xF4, 0x0D, 0xF1,
+ 0x6E, 0x4A, 0xEA, 0x8F, 0xAB, 0x91, 0x97, 0x2A
+ },
+ {
+ 0x5B, 0x73, 0xDD, 0xB6, 0x8D, 0x9D, 0x2B, 0x0A,
+ 0xA2, 0x65, 0xA0, 0x79, 0x88, 0xD6, 0xB8, 0x8A,
+ 0xE9, 0xAA, 0xC5, 0x82, 0xAF, 0x83, 0x03, 0x2F,
+ 0x8A, 0x9B, 0x21, 0xA2, 0xE1, 0xB7, 0xBF, 0x18
+ },
+ {
+ 0x3D, 0xA2, 0x91, 0x26, 0xC7, 0xC5, 0xD7, 0xF4,
+ 0x3E, 0x64, 0x24, 0x2A, 0x79, 0xFE, 0xAA, 0x4E,
+ 0xF3, 0x45, 0x9C, 0xDE, 0xCC, 0xC8, 0x98, 0xED,
+ 0x59, 0xA9, 0x7F, 0x6E, 0xC9, 0x3B, 0x9D, 0xAB
+ },
+ {
+ 0x56, 0x6D, 0xC9, 0x20, 0x29, 0x3D, 0xA5, 0xCB,
+ 0x4F, 0xE0, 0xAA, 0x8A, 0xBD, 0xA8, 0xBB, 0xF5,
+ 0x6F, 0x55, 0x23, 0x13, 0xBF, 0xF1, 0x90, 0x46,
+ 0x64, 0x1E, 0x36, 0x15, 0xC1, 0xE3, 0xED, 0x3F
+ },
+ {
+ 0x41, 0x15, 0xBE, 0xA0, 0x2F, 0x73, 0xF9, 0x7F,
+ 0x62, 0x9E, 0x5C, 0x55, 0x90, 0x72, 0x0C, 0x01,
+ 0xE7, 0xE4, 0x49, 0xAE, 0x2A, 0x66, 0x97, 0xD4,
+ 0xD2, 0x78, 0x33, 0x21, 0x30, 0x36, 0x92, 0xF9
+ },
+ {
+ 0x4C, 0xE0, 0x8F, 0x47, 0x62, 0x46, 0x8A, 0x76,
+ 0x70, 0x01, 0x21, 0x64, 0x87, 0x8D, 0x68, 0x34,
+ 0x0C, 0x52, 0xA3, 0x5E, 0x66, 0xC1, 0x88, 0x4D,
+ 0x5C, 0x86, 0x48, 0x89, 0xAB, 0xC9, 0x66, 0x77
+ },
+ {
+ 0x81, 0xEA, 0x0B, 0x78, 0x04, 0x12, 0x4E, 0x0C,
+ 0x22, 0xEA, 0x5F, 0xC7, 0x11, 0x04, 0xA2, 0xAF,
+ 0xCB, 0x52, 0xA1, 0xFA, 0x81, 0x6F, 0x3E, 0xCB,
+ 0x7D, 0xCB, 0x5D, 0x9D, 0xEA, 0x17, 0x86, 0xD0
+ },
+ {
+ 0xFE, 0x36, 0x27, 0x33, 0xB0, 0x5F, 0x6B, 0xED,
+ 0xAF, 0x93, 0x79, 0xD7, 0xF7, 0x93, 0x6E, 0xDE,
+ 0x20, 0x9B, 0x1F, 0x83, 0x23, 0xC3, 0x92, 0x25,
+ 0x49, 0xD9, 0xE7, 0x36, 0x81, 0xB5, 0xDB, 0x7B
+ },
+ {
+ 0xEF, 0xF3, 0x7D, 0x30, 0xDF, 0xD2, 0x03, 0x59,
+ 0xBE, 0x4E, 0x73, 0xFD, 0xF4, 0x0D, 0x27, 0x73,
+ 0x4B, 0x3D, 0xF9, 0x0A, 0x97, 0xA5, 0x5E, 0xD7,
+ 0x45, 0x29, 0x72, 0x94, 0xCA, 0x85, 0xD0, 0x9F
+ },
+ {
+ 0x17, 0x2F, 0xFC, 0x67, 0x15, 0x3D, 0x12, 0xE0,
+ 0xCA, 0x76, 0xA8, 0xB6, 0xCD, 0x5D, 0x47, 0x31,
+ 0x88, 0x5B, 0x39, 0xCE, 0x0C, 0xAC, 0x93, 0xA8,
+ 0x97, 0x2A, 0x18, 0x00, 0x6C, 0x8B, 0x8B, 0xAF
+ },
+ {
+ 0xC4, 0x79, 0x57, 0xF1, 0xCC, 0x88, 0xE8, 0x3E,
+ 0xF9, 0x44, 0x58, 0x39, 0x70, 0x9A, 0x48, 0x0A,
+ 0x03, 0x6B, 0xED, 0x5F, 0x88, 0xAC, 0x0F, 0xCC,
+ 0x8E, 0x1E, 0x70, 0x3F, 0xFA, 0xAC, 0x13, 0x2C
+ },
+ {
+ 0x30, 0xF3, 0x54, 0x83, 0x70, 0xCF, 0xDC, 0xED,
+ 0xA5, 0xC3, 0x7B, 0x56, 0x9B, 0x61, 0x75, 0xE7,
+ 0x99, 0xEE, 0xF1, 0xA6, 0x2A, 0xAA, 0x94, 0x32,
+ 0x45, 0xAE, 0x76, 0x69, 0xC2, 0x27, 0xA7, 0xB5
+ },
+ {
+ 0xC9, 0x5D, 0xCB, 0x3C, 0xF1, 0xF2, 0x7D, 0x0E,
+ 0xEF, 0x2F, 0x25, 0xD2, 0x41, 0x38, 0x70, 0x90,
+ 0x4A, 0x87, 0x7C, 0x4A, 0x56, 0xC2, 0xDE, 0x1E,
+ 0x83, 0xE2, 0xBC, 0x2A, 0xE2, 0xE4, 0x68, 0x21
+ },
+ {
+ 0xD5, 0xD0, 0xB5, 0xD7, 0x05, 0x43, 0x4C, 0xD4,
+ 0x6B, 0x18, 0x57, 0x49, 0xF6, 0x6B, 0xFB, 0x58,
+ 0x36, 0xDC, 0xDF, 0x6E, 0xE5, 0x49, 0xA2, 0xB7,
+ 0xA4, 0xAE, 0xE7, 0xF5, 0x80, 0x07, 0xCA, 0xAF
+ },
+ {
+ 0xBB, 0xC1, 0x24, 0xA7, 0x12, 0xF1, 0x5D, 0x07,
+ 0xC3, 0x00, 0xE0, 0x5B, 0x66, 0x83, 0x89, 0xA4,
+ 0x39, 0xC9, 0x17, 0x77, 0xF7, 0x21, 0xF8, 0x32,
+ 0x0C, 0x1C, 0x90, 0x78, 0x06, 0x6D, 0x2C, 0x7E
+ },
+ {
+ 0xA4, 0x51, 0xB4, 0x8C, 0x35, 0xA6, 0xC7, 0x85,
+ 0x4C, 0xFA, 0xAE, 0x60, 0x26, 0x2E, 0x76, 0x99,
+ 0x08, 0x16, 0x38, 0x2A, 0xC0, 0x66, 0x7E, 0x5A,
+ 0x5C, 0x9E, 0x1B, 0x46, 0xC4, 0x34, 0x2D, 0xDF
+ },
+ {
+ 0xB0, 0xD1, 0x50, 0xFB, 0x55, 0xE7, 0x78, 0xD0,
+ 0x11, 0x47, 0xF0, 0xB5, 0xD8, 0x9D, 0x99, 0xEC,
+ 0xB2, 0x0F, 0xF0, 0x7E, 0x5E, 0x67, 0x60, 0xD6,
+ 0xB6, 0x45, 0xEB, 0x5B, 0x65, 0x4C, 0x62, 0x2B
+ },
+ {
+ 0x34, 0xF7, 0x37, 0xC0, 0xAB, 0x21, 0x99, 0x51,
+ 0xEE, 0xE8, 0x9A, 0x9F, 0x8D, 0xAC, 0x29, 0x9C,
+ 0x9D, 0x4C, 0x38, 0xF3, 0x3F, 0xA4, 0x94, 0xC5,
+ 0xC6, 0xEE, 0xFC, 0x92, 0xB6, 0xDB, 0x08, 0xBC
+ },
+ {
+ 0x1A, 0x62, 0xCC, 0x3A, 0x00, 0x80, 0x0D, 0xCB,
+ 0xD9, 0x98, 0x91, 0x08, 0x0C, 0x1E, 0x09, 0x84,
+ 0x58, 0x19, 0x3A, 0x8C, 0xC9, 0xF9, 0x70, 0xEA,
+ 0x99, 0xFB, 0xEF, 0xF0, 0x03, 0x18, 0xC2, 0x89
+ },
+ {
+ 0xCF, 0xCE, 0x55, 0xEB, 0xAF, 0xC8, 0x40, 0xD7,
+ 0xAE, 0x48, 0x28, 0x1C, 0x7F, 0xD5, 0x7E, 0xC8,
+ 0xB4, 0x82, 0xD4, 0xB7, 0x04, 0x43, 0x74, 0x95,
+ 0x49, 0x5A, 0xC4, 0x14, 0xCF, 0x4A, 0x37, 0x4B
+ },
+ {
+ 0x67, 0x46, 0xFA, 0xCF, 0x71, 0x14, 0x6D, 0x99,
+ 0x9D, 0xAB, 0xD0, 0x5D, 0x09, 0x3A, 0xE5, 0x86,
+ 0x64, 0x8D, 0x1E, 0xE2, 0x8E, 0x72, 0x61, 0x7B,
+ 0x99, 0xD0, 0xF0, 0x08, 0x6E, 0x1E, 0x45, 0xBF
+ },
+ {
+ 0x57, 0x1C, 0xED, 0x28, 0x3B, 0x3F, 0x23, 0xB4,
+ 0xE7, 0x50, 0xBF, 0x12, 0xA2, 0xCA, 0xF1, 0x78,
+ 0x18, 0x47, 0xBD, 0x89, 0x0E, 0x43, 0x60, 0x3C,
+ 0xDC, 0x59, 0x76, 0x10, 0x2B, 0x7B, 0xB1, 0x1B
+ },
+ {
+ 0xCF, 0xCB, 0x76, 0x5B, 0x04, 0x8E, 0x35, 0x02,
+ 0x2C, 0x5D, 0x08, 0x9D, 0x26, 0xE8, 0x5A, 0x36,
+ 0xB0, 0x05, 0xA2, 0xB8, 0x04, 0x93, 0xD0, 0x3A,
+ 0x14, 0x4E, 0x09, 0xF4, 0x09, 0xB6, 0xAF, 0xD1
+ },
+ {
+ 0x40, 0x50, 0xC7, 0xA2, 0x77, 0x05, 0xBB, 0x27,
+ 0xF4, 0x20, 0x89, 0xB2, 0x99, 0xF3, 0xCB, 0xE5,
+ 0x05, 0x4E, 0xAD, 0x68, 0x72, 0x7E, 0x8E, 0xF9,
+ 0x31, 0x8C, 0xE6, 0xF2, 0x5C, 0xD6, 0xF3, 0x1D
+ },
+ {
+ 0x18, 0x40, 0x70, 0xBD, 0x5D, 0x26, 0x5F, 0xBD,
+ 0xC1, 0x42, 0xCD, 0x1C, 0x5C, 0xD0, 0xD7, 0xE4,
+ 0x14, 0xE7, 0x03, 0x69, 0xA2, 0x66, 0xD6, 0x27,
+ 0xC8, 0xFB, 0xA8, 0x4F, 0xA5, 0xE8, 0x4C, 0x34
+ },
+ {
+ 0x9E, 0xDD, 0xA9, 0xA4, 0x44, 0x39, 0x02, 0xA9,
+ 0x58, 0x8C, 0x0D, 0x0C, 0xCC, 0x62, 0xB9, 0x30,
+ 0x21, 0x84, 0x79, 0xA6, 0x84, 0x1E, 0x6F, 0xE7,
+ 0xD4, 0x30, 0x03, 0xF0, 0x4B, 0x1F, 0xD6, 0x43
+ },
+ {
+ 0xE4, 0x12, 0xFE, 0xEF, 0x79, 0x08, 0x32, 0x4A,
+ 0x6D, 0xA1, 0x84, 0x16, 0x29, 0xF3, 0x5D, 0x3D,
+ 0x35, 0x86, 0x42, 0x01, 0x93, 0x10, 0xEC, 0x57,
+ 0xC6, 0x14, 0x83, 0x6B, 0x63, 0xD3, 0x07, 0x63
+ },
+ {
+ 0x1A, 0x2B, 0x8E, 0xDF, 0xF3, 0xF9, 0xAC, 0xC1,
+ 0x55, 0x4F, 0xCB, 0xAE, 0x3C, 0xF1, 0xD6, 0x29,
+ 0x8C, 0x64, 0x62, 0xE2, 0x2E, 0x5E, 0xB0, 0x25,
+ 0x96, 0x84, 0xF8, 0x35, 0x01, 0x2B, 0xD1, 0x3F
+ },
+ {
+ 0x28, 0x8C, 0x4A, 0xD9, 0xB9, 0x40, 0x97, 0x62,
+ 0xEA, 0x07, 0xC2, 0x4A, 0x41, 0xF0, 0x4F, 0x69,
+ 0xA7, 0xD7, 0x4B, 0xEE, 0x2D, 0x95, 0x43, 0x53,
+ 0x74, 0xBD, 0xE9, 0x46, 0xD7, 0x24, 0x1C, 0x7B
+ },
+ {
+ 0x80, 0x56, 0x91, 0xBB, 0x28, 0x67, 0x48, 0xCF,
+ 0xB5, 0x91, 0xD3, 0xAE, 0xBE, 0x7E, 0x6F, 0x4E,
+ 0x4D, 0xC6, 0xE2, 0x80, 0x8C, 0x65, 0x14, 0x3C,
+ 0xC0, 0x04, 0xE4, 0xEB, 0x6F, 0xD0, 0x9D, 0x43
+ },
+ {
+ 0xD4, 0xAC, 0x8D, 0x3A, 0x0A, 0xFC, 0x6C, 0xFA,
+ 0x7B, 0x46, 0x0A, 0xE3, 0x00, 0x1B, 0xAE, 0xB3,
+ 0x6D, 0xAD, 0xB3, 0x7D, 0xA0, 0x7D, 0x2E, 0x8A,
+ 0xC9, 0x18, 0x22, 0xDF, 0x34, 0x8A, 0xED, 0x3D
+ },
+ {
+ 0xC3, 0x76, 0x61, 0x70, 0x14, 0xD2, 0x01, 0x58,
+ 0xBC, 0xED, 0x3D, 0x3B, 0xA5, 0x52, 0xB6, 0xEC,
+ 0xCF, 0x84, 0xE6, 0x2A, 0xA3, 0xEB, 0x65, 0x0E,
+ 0x90, 0x02, 0x9C, 0x84, 0xD1, 0x3E, 0xEA, 0x69
+ },
+ {
+ 0xC4, 0x1F, 0x09, 0xF4, 0x3C, 0xEC, 0xAE, 0x72,
+ 0x93, 0xD6, 0x00, 0x7C, 0xA0, 0xA3, 0x57, 0x08,
+ 0x7D, 0x5A, 0xE5, 0x9B, 0xE5, 0x00, 0xC1, 0xCD,
+ 0x5B, 0x28, 0x9E, 0xE8, 0x10, 0xC7, 0xB0, 0x82
+ },
+ {
+ 0x03, 0xD1, 0xCE, 0xD1, 0xFB, 0xA5, 0xC3, 0x91,
+ 0x55, 0xC4, 0x4B, 0x77, 0x65, 0xCB, 0x76, 0x0C,
+ 0x78, 0x70, 0x8D, 0xCF, 0xC8, 0x0B, 0x0B, 0xD8,
+ 0xAD, 0xE3, 0xA5, 0x6D, 0xA8, 0x83, 0x0B, 0x29
+ },
+ {
+ 0x09, 0xBD, 0xE6, 0xF1, 0x52, 0x21, 0x8D, 0xC9,
+ 0x2C, 0x41, 0xD7, 0xF4, 0x53, 0x87, 0xE6, 0x3E,
+ 0x58, 0x69, 0xD8, 0x07, 0xEC, 0x70, 0xB8, 0x21,
+ 0x40, 0x5D, 0xBD, 0x88, 0x4B, 0x7F, 0xCF, 0x4B
+ },
+ {
+ 0x71, 0xC9, 0x03, 0x6E, 0x18, 0x17, 0x9B, 0x90,
+ 0xB3, 0x7D, 0x39, 0xE9, 0xF0, 0x5E, 0xB8, 0x9C,
+ 0xC5, 0xFC, 0x34, 0x1F, 0xD7, 0xC4, 0x77, 0xD0,
+ 0xD7, 0x49, 0x32, 0x85, 0xFA, 0xCA, 0x08, 0xA4
+ },
+ {
+ 0x59, 0x16, 0x83, 0x3E, 0xBB, 0x05, 0xCD, 0x91,
+ 0x9C, 0xA7, 0xFE, 0x83, 0xB6, 0x92, 0xD3, 0x20,
+ 0x5B, 0xEF, 0x72, 0x39, 0x2B, 0x2C, 0xF6, 0xBB,
+ 0x0A, 0x6D, 0x43, 0xF9, 0x94, 0xF9, 0x5F, 0x11
+ },
+ {
+ 0xF6, 0x3A, 0xAB, 0x3E, 0xC6, 0x41, 0xB3, 0xB0,
+ 0x24, 0x96, 0x4C, 0x2B, 0x43, 0x7C, 0x04, 0xF6,
+ 0x04, 0x3C, 0x4C, 0x7E, 0x02, 0x79, 0x23, 0x99,
+ 0x95, 0x40, 0x19, 0x58, 0xF8, 0x6B, 0xBE, 0x54
+ },
+ {
+ 0xF1, 0x72, 0xB1, 0x80, 0xBF, 0xB0, 0x97, 0x40,
+ 0x49, 0x31, 0x20, 0xB6, 0x32, 0x6C, 0xBD, 0xC5,
+ 0x61, 0xE4, 0x77, 0xDE, 0xF9, 0xBB, 0xCF, 0xD2,
+ 0x8C, 0xC8, 0xC1, 0xC5, 0xE3, 0x37, 0x9A, 0x31
+ },
+ {
+ 0xCB, 0x9B, 0x89, 0xCC, 0x18, 0x38, 0x1D, 0xD9,
+ 0x14, 0x1A, 0xDE, 0x58, 0x86, 0x54, 0xD4, 0xE6,
+ 0xA2, 0x31, 0xD5, 0xBF, 0x49, 0xD4, 0xD5, 0x9A,
+ 0xC2, 0x7D, 0x86, 0x9C, 0xBE, 0x10, 0x0C, 0xF3
+ },
+ {
+ 0x7B, 0xD8, 0x81, 0x50, 0x46, 0xFD, 0xD8, 0x10,
+ 0xA9, 0x23, 0xE1, 0x98, 0x4A, 0xAE, 0xBD, 0xCD,
+ 0xF8, 0x4D, 0x87, 0xC8, 0x99, 0x2D, 0x68, 0xB5,
+ 0xEE, 0xB4, 0x60, 0xF9, 0x3E, 0xB3, 0xC8, 0xD7
+ },
+ {
+ 0x60, 0x7B, 0xE6, 0x68, 0x62, 0xFD, 0x08, 0xEE,
+ 0x5B, 0x19, 0xFA, 0xCA, 0xC0, 0x9D, 0xFD, 0xBC,
+ 0xD4, 0x0C, 0x31, 0x21, 0x01, 0xD6, 0x6E, 0x6E,
+ 0xBD, 0x2B, 0x84, 0x1F, 0x1B, 0x9A, 0x93, 0x25
+ },
+ {
+ 0x9F, 0xE0, 0x3B, 0xBE, 0x69, 0xAB, 0x18, 0x34,
+ 0xF5, 0x21, 0x9B, 0x0D, 0xA8, 0x8A, 0x08, 0xB3,
+ 0x0A, 0x66, 0xC5, 0x91, 0x3F, 0x01, 0x51, 0x96,
+ 0x3C, 0x36, 0x05, 0x60, 0xDB, 0x03, 0x87, 0xB3
+ },
+ {
+ 0x90, 0xA8, 0x35, 0x85, 0x71, 0x7B, 0x75, 0xF0,
+ 0xE9, 0xB7, 0x25, 0xE0, 0x55, 0xEE, 0xEE, 0xB9,
+ 0xE7, 0xA0, 0x28, 0xEA, 0x7E, 0x6C, 0xBC, 0x07,
+ 0xB2, 0x09, 0x17, 0xEC, 0x03, 0x63, 0xE3, 0x8C
+ },
+ {
+ 0x33, 0x6E, 0xA0, 0x53, 0x0F, 0x4A, 0x74, 0x69,
+ 0x12, 0x6E, 0x02, 0x18, 0x58, 0x7E, 0xBB, 0xDE,
+ 0x33, 0x58, 0xA0, 0xB3, 0x1C, 0x29, 0xD2, 0x00,
+ 0xF7, 0xDC, 0x7E, 0xB1, 0x5C, 0x6A, 0xAD, 0xD8
+ },
+ {
+ 0xA7, 0x9E, 0x76, 0xDC, 0x0A, 0xBC, 0xA4, 0x39,
+ 0x6F, 0x07, 0x47, 0xCD, 0x7B, 0x74, 0x8D, 0xF9,
+ 0x13, 0x00, 0x76, 0x26, 0xB1, 0xD6, 0x59, 0xDA,
+ 0x0C, 0x1F, 0x78, 0xB9, 0x30, 0x3D, 0x01, 0xA3
+ },
+ {
+ 0x44, 0xE7, 0x8A, 0x77, 0x37, 0x56, 0xE0, 0x95,
+ 0x15, 0x19, 0x50, 0x4D, 0x70, 0x38, 0xD2, 0x8D,
+ 0x02, 0x13, 0xA3, 0x7E, 0x0C, 0xE3, 0x75, 0x37,
+ 0x17, 0x57, 0xBC, 0x99, 0x63, 0x11, 0xE3, 0xB8
+ },
+ {
+ 0x77, 0xAC, 0x01, 0x2A, 0x3F, 0x75, 0x4D, 0xCF,
+ 0xEA, 0xB5, 0xEB, 0x99, 0x6B, 0xE9, 0xCD, 0x2D,
+ 0x1F, 0x96, 0x11, 0x1B, 0x6E, 0x49, 0xF3, 0x99,
+ 0x4D, 0xF1, 0x81, 0xF2, 0x85, 0x69, 0xD8, 0x25
+ },
+ {
+ 0xCE, 0x5A, 0x10, 0xDB, 0x6F, 0xCC, 0xDA, 0xF1,
+ 0x40, 0xAA, 0xA4, 0xDE, 0xD6, 0x25, 0x0A, 0x9C,
+ 0x06, 0xE9, 0x22, 0x2B, 0xC9, 0xF9, 0xF3, 0x65,
+ 0x8A, 0x4A, 0xFF, 0x93, 0x5F, 0x2B, 0x9F, 0x3A
+ },
+ {
+ 0xEC, 0xC2, 0x03, 0xA7, 0xFE, 0x2B, 0xE4, 0xAB,
+ 0xD5, 0x5B, 0xB5, 0x3E, 0x6E, 0x67, 0x35, 0x72,
+ 0xE0, 0x07, 0x8D, 0xA8, 0xCD, 0x37, 0x5E, 0xF4,
+ 0x30, 0xCC, 0x97, 0xF9, 0xF8, 0x00, 0x83, 0xAF
+ },
+ {
+ 0x14, 0xA5, 0x18, 0x6D, 0xE9, 0xD7, 0xA1, 0x8B,
+ 0x04, 0x12, 0xB8, 0x56, 0x3E, 0x51, 0xCC, 0x54,
+ 0x33, 0x84, 0x0B, 0x4A, 0x12, 0x9A, 0x8F, 0xF9,
+ 0x63, 0xB3, 0x3A, 0x3C, 0x4A, 0xFE, 0x8E, 0xBB
+ },
+ {
+ 0x13, 0xF8, 0xEF, 0x95, 0xCB, 0x86, 0xE6, 0xA6,
+ 0x38, 0x93, 0x1C, 0x8E, 0x10, 0x76, 0x73, 0xEB,
+ 0x76, 0xBA, 0x10, 0xD7, 0xC2, 0xCD, 0x70, 0xB9,
+ 0xD9, 0x92, 0x0B, 0xBE, 0xED, 0x92, 0x94, 0x09
+ },
+ {
+ 0x0B, 0x33, 0x8F, 0x4E, 0xE1, 0x2F, 0x2D, 0xFC,
+ 0xB7, 0x87, 0x13, 0x37, 0x79, 0x41, 0xE0, 0xB0,
+ 0x63, 0x21, 0x52, 0x58, 0x1D, 0x13, 0x32, 0x51,
+ 0x6E, 0x4A, 0x2C, 0xAB, 0x19, 0x42, 0xCC, 0xA4
+ },
+ {
+ 0xEA, 0xAB, 0x0E, 0xC3, 0x7B, 0x3B, 0x8A, 0xB7,
+ 0x96, 0xE9, 0xF5, 0x72, 0x38, 0xDE, 0x14, 0xA2,
+ 0x64, 0xA0, 0x76, 0xF3, 0x88, 0x7D, 0x86, 0xE2,
+ 0x9B, 0xB5, 0x90, 0x6D, 0xB5, 0xA0, 0x0E, 0x02
+ },
+ {
+ 0x23, 0xCB, 0x68, 0xB8, 0xC0, 0xE6, 0xDC, 0x26,
+ 0xDC, 0x27, 0x76, 0x6D, 0xDC, 0x0A, 0x13, 0xA9,
+ 0x94, 0x38, 0xFD, 0x55, 0x61, 0x7A, 0xA4, 0x09,
+ 0x5D, 0x8F, 0x96, 0x97, 0x20, 0xC8, 0x72, 0xDF
+ },
+ {
+ 0x09, 0x1D, 0x8E, 0xE3, 0x0D, 0x6F, 0x29, 0x68,
+ 0xD4, 0x6B, 0x68, 0x7D, 0xD6, 0x52, 0x92, 0x66,
+ 0x57, 0x42, 0xDE, 0x0B, 0xB8, 0x3D, 0xCC, 0x00,
+ 0x04, 0xC7, 0x2C, 0xE1, 0x00, 0x07, 0xA5, 0x49
+ },
+ {
+ 0x7F, 0x50, 0x7A, 0xBC, 0x6D, 0x19, 0xBA, 0x00,
+ 0xC0, 0x65, 0xA8, 0x76, 0xEC, 0x56, 0x57, 0x86,
+ 0x88, 0x82, 0xD1, 0x8A, 0x22, 0x1B, 0xC4, 0x6C,
+ 0x7A, 0x69, 0x12, 0x54, 0x1F, 0x5B, 0xC7, 0xBA
+ },
+ {
+ 0xA0, 0x60, 0x7C, 0x24, 0xE1, 0x4E, 0x8C, 0x22,
+ 0x3D, 0xB0, 0xD7, 0x0B, 0x4D, 0x30, 0xEE, 0x88,
+ 0x01, 0x4D, 0x60, 0x3F, 0x43, 0x7E, 0x9E, 0x02,
+ 0xAA, 0x7D, 0xAF, 0xA3, 0xCD, 0xFB, 0xAD, 0x94
+ },
+ {
+ 0xDD, 0xBF, 0xEA, 0x75, 0xCC, 0x46, 0x78, 0x82,
+ 0xEB, 0x34, 0x83, 0xCE, 0x5E, 0x2E, 0x75, 0x6A,
+ 0x4F, 0x47, 0x01, 0xB7, 0x6B, 0x44, 0x55, 0x19,
+ 0xE8, 0x9F, 0x22, 0xD6, 0x0F, 0xA8, 0x6E, 0x06
+ },
+ {
+ 0x0C, 0x31, 0x1F, 0x38, 0xC3, 0x5A, 0x4F, 0xB9,
+ 0x0D, 0x65, 0x1C, 0x28, 0x9D, 0x48, 0x68, 0x56,
+ 0xCD, 0x14, 0x13, 0xDF, 0x9B, 0x06, 0x77, 0xF5,
+ 0x3E, 0xCE, 0x2C, 0xD9, 0xE4, 0x77, 0xC6, 0x0A
+ },
+ {
+ 0x46, 0xA7, 0x3A, 0x8D, 0xD3, 0xE7, 0x0F, 0x59,
+ 0xD3, 0x94, 0x2C, 0x01, 0xDF, 0x59, 0x9D, 0xEF,
+ 0x78, 0x3C, 0x9D, 0xA8, 0x2F, 0xD8, 0x32, 0x22,
+ 0xCD, 0x66, 0x2B, 0x53, 0xDC, 0xE7, 0xDB, 0xDF
+ },
+ {
+ 0xAD, 0x03, 0x8F, 0xF9, 0xB1, 0x4D, 0xE8, 0x4A,
+ 0x80, 0x1E, 0x4E, 0x62, 0x1C, 0xE5, 0xDF, 0x02,
+ 0x9D, 0xD9, 0x35, 0x20, 0xD0, 0xC2, 0xFA, 0x38,
+ 0xBF, 0xF1, 0x76, 0xA8, 0xB1, 0xD1, 0x69, 0x8C
+ },
+ {
+ 0xAB, 0x70, 0xC5, 0xDF, 0xBD, 0x1E, 0xA8, 0x17,
+ 0xFE, 0xD0, 0xCD, 0x06, 0x72, 0x93, 0xAB, 0xF3,
+ 0x19, 0xE5, 0xD7, 0x90, 0x1C, 0x21, 0x41, 0xD5,
+ 0xD9, 0x9B, 0x23, 0xF0, 0x3A, 0x38, 0xE7, 0x48
+ },
+ {
+ 0x1F, 0xFF, 0xDA, 0x67, 0x93, 0x2B, 0x73, 0xC8,
+ 0xEC, 0xAF, 0x00, 0x9A, 0x34, 0x91, 0xA0, 0x26,
+ 0x95, 0x3B, 0xAB, 0xFE, 0x1F, 0x66, 0x3B, 0x06,
+ 0x97, 0xC3, 0xC4, 0xAE, 0x8B, 0x2E, 0x7D, 0xCB
+ },
+ {
+ 0xB0, 0xD2, 0xCC, 0x19, 0x47, 0x2D, 0xD5, 0x7F,
+ 0x2B, 0x17, 0xEF, 0xC0, 0x3C, 0x8D, 0x58, 0xC2,
+ 0x28, 0x3D, 0xBB, 0x19, 0xDA, 0x57, 0x2F, 0x77,
+ 0x55, 0x85, 0x5A, 0xA9, 0x79, 0x43, 0x17, 0xA0
+ },
+ {
+ 0xA0, 0xD1, 0x9A, 0x6E, 0xE3, 0x39, 0x79, 0xC3,
+ 0x25, 0x51, 0x0E, 0x27, 0x66, 0x22, 0xDF, 0x41,
+ 0xF7, 0x15, 0x83, 0xD0, 0x75, 0x01, 0xB8, 0x70,
+ 0x71, 0x12, 0x9A, 0x0A, 0xD9, 0x47, 0x32, 0xA5
+ },
+ {
+ 0x72, 0x46, 0x42, 0xA7, 0x03, 0x2D, 0x10, 0x62,
+ 0xB8, 0x9E, 0x52, 0xBE, 0xA3, 0x4B, 0x75, 0xDF,
+ 0x7D, 0x8F, 0xE7, 0x72, 0xD9, 0xFE, 0x3C, 0x93,
+ 0xDD, 0xF3, 0xC4, 0x54, 0x5A, 0xB5, 0xA9, 0x9B
+ },
+ {
+ 0xAD, 0xE5, 0xEA, 0xA7, 0xE6, 0x1F, 0x67, 0x2D,
+ 0x58, 0x7E, 0xA0, 0x3D, 0xAE, 0x7D, 0x7B, 0x55,
+ 0x22, 0x9C, 0x01, 0xD0, 0x6B, 0xC0, 0xA5, 0x70,
+ 0x14, 0x36, 0xCB, 0xD1, 0x83, 0x66, 0xA6, 0x26
+ },
+ {
+ 0x01, 0x3B, 0x31, 0xEB, 0xD2, 0x28, 0xFC, 0xDD,
+ 0xA5, 0x1F, 0xAB, 0xB0, 0x3B, 0xB0, 0x2D, 0x60,
+ 0xAC, 0x20, 0xCA, 0x21, 0x5A, 0xAF, 0xA8, 0x3B,
+ 0xDD, 0x85, 0x5E, 0x37, 0x55, 0xA3, 0x5F, 0x0B
+ },
+ {
+ 0x33, 0x2E, 0xD4, 0x0B, 0xB1, 0x0D, 0xDE, 0x3C,
+ 0x95, 0x4A, 0x75, 0xD7, 0xB8, 0x99, 0x9D, 0x4B,
+ 0x26, 0xA1, 0xC0, 0x63, 0xC1, 0xDC, 0x6E, 0x32,
+ 0xC1, 0xD9, 0x1B, 0xAB, 0x7B, 0xBB, 0x7D, 0x16
+ },
+ {
+ 0xC7, 0xA1, 0x97, 0xB3, 0xA0, 0x5B, 0x56, 0x6B,
+ 0xCC, 0x9F, 0xAC, 0xD2, 0x0E, 0x44, 0x1D, 0x6F,
+ 0x6C, 0x28, 0x60, 0xAC, 0x96, 0x51, 0xCD, 0x51,
+ 0xD6, 0xB9, 0xD2, 0xCD, 0xEE, 0xEA, 0x03, 0x90
+ },
+ {
+ 0xBD, 0x9C, 0xF6, 0x4E, 0xA8, 0x95, 0x3C, 0x03,
+ 0x71, 0x08, 0xE6, 0xF6, 0x54, 0x91, 0x4F, 0x39,
+ 0x58, 0xB6, 0x8E, 0x29, 0xC1, 0x67, 0x00, 0xDC,
+ 0x18, 0x4D, 0x94, 0xA2, 0x17, 0x08, 0xFF, 0x60
+ },
+ {
+ 0x88, 0x35, 0xB0, 0xAC, 0x02, 0x11, 0x51, 0xDF,
+ 0x71, 0x64, 0x74, 0xCE, 0x27, 0xCE, 0x4D, 0x3C,
+ 0x15, 0xF0, 0xB2, 0xDA, 0xB4, 0x80, 0x03, 0xCF,
+ 0x3F, 0x3E, 0xFD, 0x09, 0x45, 0x10, 0x6B, 0x9A
+ },
+ {
+ 0x3B, 0xFE, 0xFA, 0x33, 0x01, 0xAA, 0x55, 0xC0,
+ 0x80, 0x19, 0x0C, 0xFF, 0xDA, 0x8E, 0xAE, 0x51,
+ 0xD9, 0xAF, 0x48, 0x8B, 0x4C, 0x1F, 0x24, 0xC3,
+ 0xD9, 0xA7, 0x52, 0x42, 0xFD, 0x8E, 0xA0, 0x1D
+ },
+ {
+ 0x08, 0x28, 0x4D, 0x14, 0x99, 0x3C, 0xD4, 0x7D,
+ 0x53, 0xEB, 0xAE, 0xCF, 0x0D, 0xF0, 0x47, 0x8C,
+ 0xC1, 0x82, 0xC8, 0x9C, 0x00, 0xE1, 0x85, 0x9C,
+ 0x84, 0x85, 0x16, 0x86, 0xDD, 0xF2, 0xC1, 0xB7
+ },
+ {
+ 0x1E, 0xD7, 0xEF, 0x9F, 0x04, 0xC2, 0xAC, 0x8D,
+ 0xB6, 0xA8, 0x64, 0xDB, 0x13, 0x10, 0x87, 0xF2,
+ 0x70, 0x65, 0x09, 0x8E, 0x69, 0xC3, 0xFE, 0x78,
+ 0x71, 0x8D, 0x9B, 0x94, 0x7F, 0x4A, 0x39, 0xD0
+ },
+ {
+ 0xC1, 0x61, 0xF2, 0xDC, 0xD5, 0x7E, 0x9C, 0x14,
+ 0x39, 0xB3, 0x1A, 0x9D, 0xD4, 0x3D, 0x8F, 0x3D,
+ 0x7D, 0xD8, 0xF0, 0xEB, 0x7C, 0xFA, 0xC6, 0xFB,
+ 0x25, 0xA0, 0xF2, 0x8E, 0x30, 0x6F, 0x06, 0x61
+ },
+ {
+ 0xC0, 0x19, 0x69, 0xAD, 0x34, 0xC5, 0x2C, 0xAF,
+ 0x3D, 0xC4, 0xD8, 0x0D, 0x19, 0x73, 0x5C, 0x29,
+ 0x73, 0x1A, 0xC6, 0xE7, 0xA9, 0x20, 0x85, 0xAB,
+ 0x92, 0x50, 0xC4, 0x8D, 0xEA, 0x48, 0xA3, 0xFC
+ },
+ {
+ 0x17, 0x20, 0xB3, 0x65, 0x56, 0x19, 0xD2, 0xA5,
+ 0x2B, 0x35, 0x21, 0xAE, 0x0E, 0x49, 0xE3, 0x45,
+ 0xCB, 0x33, 0x89, 0xEB, 0xD6, 0x20, 0x8A, 0xCA,
+ 0xF9, 0xF1, 0x3F, 0xDA, 0xCC, 0xA8, 0xBE, 0x49
+ },
+ {
+ 0x75, 0x62, 0x88, 0x36, 0x1C, 0x83, 0xE2, 0x4C,
+ 0x61, 0x7C, 0xF9, 0x5C, 0x90, 0x5B, 0x22, 0xD0,
+ 0x17, 0xCD, 0xC8, 0x6F, 0x0B, 0xF1, 0xD6, 0x58,
+ 0xF4, 0x75, 0x6C, 0x73, 0x79, 0x87, 0x3B, 0x7F
+ },
+ {
+ 0xE7, 0xD0, 0xED, 0xA3, 0x45, 0x26, 0x93, 0xB7,
+ 0x52, 0xAB, 0xCD, 0xA1, 0xB5, 0x5E, 0x27, 0x6F,
+ 0x82, 0x69, 0x8F, 0x5F, 0x16, 0x05, 0x40, 0x3E,
+ 0xFF, 0x83, 0x0B, 0xEA, 0x00, 0x71, 0xA3, 0x94
+ },
+ {
+ 0x2C, 0x82, 0xEC, 0xAA, 0x6B, 0x84, 0x80, 0x3E,
+ 0x04, 0x4A, 0xF6, 0x31, 0x18, 0xAF, 0xE5, 0x44,
+ 0x68, 0x7C, 0xB6, 0xE6, 0xC7, 0xDF, 0x49, 0xED,
+ 0x76, 0x2D, 0xFD, 0x7C, 0x86, 0x93, 0xA1, 0xBC
+ },
+ {
+ 0x61, 0x36, 0xCB, 0xF4, 0xB4, 0x41, 0x05, 0x6F,
+ 0xA1, 0xE2, 0x72, 0x24, 0x98, 0x12, 0x5D, 0x6D,
+ 0xED, 0x45, 0xE1, 0x7B, 0x52, 0x14, 0x39, 0x59,
+ 0xC7, 0xF4, 0xD4, 0xE3, 0x95, 0x21, 0x8A, 0xC2
+ },
+ {
+ 0x72, 0x1D, 0x32, 0x45, 0xAA, 0xFE, 0xF2, 0x7F,
+ 0x6A, 0x62, 0x4F, 0x47, 0x95, 0x4B, 0x6C, 0x25,
+ 0x50, 0x79, 0x52, 0x6F, 0xFA, 0x25, 0xE9, 0xFF,
+ 0x77, 0xE5, 0xDC, 0xFF, 0x47, 0x3B, 0x15, 0x97
+ },
+ {
+ 0x9D, 0xD2, 0xFB, 0xD8, 0xCE, 0xF1, 0x6C, 0x35,
+ 0x3C, 0x0A, 0xC2, 0x11, 0x91, 0xD5, 0x09, 0xEB,
+ 0x28, 0xDD, 0x9E, 0x3E, 0x0D, 0x8C, 0xEA, 0x5D,
+ 0x26, 0xCA, 0x83, 0x93, 0x93, 0x85, 0x1C, 0x3A
+ },
+ {
+ 0xB2, 0x39, 0x4C, 0xEA, 0xCD, 0xEB, 0xF2, 0x1B,
+ 0xF9, 0xDF, 0x2C, 0xED, 0x98, 0xE5, 0x8F, 0x1C,
+ 0x3A, 0x4B, 0xBB, 0xFF, 0x66, 0x0D, 0xD9, 0x00,
+ 0xF6, 0x22, 0x02, 0xD6, 0x78, 0x5C, 0xC4, 0x6E
+ },
+ {
+ 0x57, 0x08, 0x9F, 0x22, 0x27, 0x49, 0xAD, 0x78,
+ 0x71, 0x76, 0x5F, 0x06, 0x2B, 0x11, 0x4F, 0x43,
+ 0xBA, 0x20, 0xEC, 0x56, 0x42, 0x2A, 0x8B, 0x1E,
+ 0x3F, 0x87, 0x19, 0x2C, 0x0E, 0xA7, 0x18, 0xC6
+ },
+ {
+ 0xE4, 0x9A, 0x94, 0x59, 0x96, 0x1C, 0xD3, 0x3C,
+ 0xDF, 0x4A, 0xAE, 0x1B, 0x10, 0x78, 0xA5, 0xDE,
+ 0xA7, 0xC0, 0x40, 0xE0, 0xFE, 0xA3, 0x40, 0xC9,
+ 0x3A, 0x72, 0x48, 0x72, 0xFC, 0x4A, 0xF8, 0x06
+ },
+ {
+ 0xED, 0xE6, 0x7F, 0x72, 0x0E, 0xFF, 0xD2, 0xCA,
+ 0x9C, 0x88, 0x99, 0x41, 0x52, 0xD0, 0x20, 0x1D,
+ 0xEE, 0x6B, 0x0A, 0x2D, 0x2C, 0x07, 0x7A, 0xCA,
+ 0x6D, 0xAE, 0x29, 0xF7, 0x3F, 0x8B, 0x63, 0x09
+ },
+ {
+ 0xE0, 0xF4, 0x34, 0xBF, 0x22, 0xE3, 0x08, 0x80,
+ 0x39, 0xC2, 0x1F, 0x71, 0x9F, 0xFC, 0x67, 0xF0,
+ 0xF2, 0xCB, 0x5E, 0x98, 0xA7, 0xA0, 0x19, 0x4C,
+ 0x76, 0xE9, 0x6B, 0xF4, 0xE8, 0xE1, 0x7E, 0x61
+ },
+ {
+ 0x27, 0x7C, 0x04, 0xE2, 0x85, 0x34, 0x84, 0xA4,
+ 0xEB, 0xA9, 0x10, 0xAD, 0x33, 0x6D, 0x01, 0xB4,
+ 0x77, 0xB6, 0x7C, 0xC2, 0x00, 0xC5, 0x9F, 0x3C,
+ 0x8D, 0x77, 0xEE, 0xF8, 0x49, 0x4F, 0x29, 0xCD
+ },
+ {
+ 0x15, 0x6D, 0x57, 0x47, 0xD0, 0xC9, 0x9C, 0x7F,
+ 0x27, 0x09, 0x7D, 0x7B, 0x7E, 0x00, 0x2B, 0x2E,
+ 0x18, 0x5C, 0xB7, 0x2D, 0x8D, 0xD7, 0xEB, 0x42,
+ 0x4A, 0x03, 0x21, 0x52, 0x81, 0x61, 0x21, 0x9F
+ },
+ {
+ 0x20, 0xDD, 0xD1, 0xED, 0x9B, 0x1C, 0xA8, 0x03,
+ 0x94, 0x6D, 0x64, 0xA8, 0x3A, 0xE4, 0x65, 0x9D,
+ 0xA6, 0x7F, 0xBA, 0x7A, 0x1A, 0x3E, 0xDD, 0xB1,
+ 0xE1, 0x03, 0xC0, 0xF5, 0xE0, 0x3E, 0x3A, 0x2C
+ },
+ {
+ 0xF0, 0xAF, 0x60, 0x4D, 0x3D, 0xAB, 0xBF, 0x9A,
+ 0x0F, 0x2A, 0x7D, 0x3D, 0xDA, 0x6B, 0xD3, 0x8B,
+ 0xBA, 0x72, 0xC6, 0xD0, 0x9B, 0xE4, 0x94, 0xFC,
+ 0xEF, 0x71, 0x3F, 0xF1, 0x01, 0x89, 0xB6, 0xE6
+ },
+ {
+ 0x98, 0x02, 0xBB, 0x87, 0xDE, 0xF4, 0xCC, 0x10,
+ 0xC4, 0xA5, 0xFD, 0x49, 0xAA, 0x58, 0xDF, 0xE2,
+ 0xF3, 0xFD, 0xDB, 0x46, 0xB4, 0x70, 0x88, 0x14,
+ 0xEA, 0xD8, 0x1D, 0x23, 0xBA, 0x95, 0x13, 0x9B
+ },
+ {
+ 0x4F, 0x8C, 0xE1, 0xE5, 0x1D, 0x2F, 0xE7, 0xF2,
+ 0x40, 0x43, 0xA9, 0x04, 0xD8, 0x98, 0xEB, 0xFC,
+ 0x91, 0x97, 0x54, 0x18, 0x75, 0x34, 0x13, 0xAA,
+ 0x09, 0x9B, 0x79, 0x5E, 0xCB, 0x35, 0xCE, 0xDB
+ },
+ {
+ 0xBD, 0xDC, 0x65, 0x14, 0xD7, 0xEE, 0x6A, 0xCE,
+ 0x0A, 0x4A, 0xC1, 0xD0, 0xE0, 0x68, 0x11, 0x22,
+ 0x88, 0xCB, 0xCF, 0x56, 0x04, 0x54, 0x64, 0x27,
+ 0x05, 0x63, 0x01, 0x77, 0xCB, 0xA6, 0x08, 0xBD
+ },
+ {
+ 0xD6, 0x35, 0x99, 0x4F, 0x62, 0x91, 0x51, 0x7B,
+ 0x02, 0x81, 0xFF, 0xDD, 0x49, 0x6A, 0xFA, 0x86,
+ 0x27, 0x12, 0xE5, 0xB3, 0xC4, 0xE5, 0x2E, 0x4C,
+ 0xD5, 0xFD, 0xAE, 0x8C, 0x0E, 0x72, 0xFB, 0x08
+ },
+ {
+ 0x87, 0x8D, 0x9C, 0xA6, 0x00, 0xCF, 0x87, 0xE7,
+ 0x69, 0xCC, 0x30, 0x5C, 0x1B, 0x35, 0x25, 0x51,
+ 0x86, 0x61, 0x5A, 0x73, 0xA0, 0xDA, 0x61, 0x3B,
+ 0x5F, 0x1C, 0x98, 0xDB, 0xF8, 0x12, 0x83, 0xEA
+ },
+ {
+ 0xA6, 0x4E, 0xBE, 0x5D, 0xC1, 0x85, 0xDE, 0x9F,
+ 0xDD, 0xE7, 0x60, 0x7B, 0x69, 0x98, 0x70, 0x2E,
+ 0xB2, 0x34, 0x56, 0x18, 0x49, 0x57, 0x30, 0x7D,
+ 0x2F, 0xA7, 0x2E, 0x87, 0xA4, 0x77, 0x02, 0xD6
+ },
+ {
+ 0xCE, 0x50, 0xEA, 0xB7, 0xB5, 0xEB, 0x52, 0xBD,
+ 0xC9, 0xAD, 0x8E, 0x5A, 0x48, 0x0A, 0xB7, 0x80,
+ 0xCA, 0x93, 0x20, 0xE4, 0x43, 0x60, 0xB1, 0xFE,
+ 0x37, 0xE0, 0x3F, 0x2F, 0x7A, 0xD7, 0xDE, 0x01
+ },
+ {
+ 0xEE, 0xDD, 0xB7, 0xC0, 0xDB, 0x6E, 0x30, 0xAB,
+ 0xE6, 0x6D, 0x79, 0xE3, 0x27, 0x51, 0x1E, 0x61,
+ 0xFC, 0xEB, 0xBC, 0x29, 0xF1, 0x59, 0xB4, 0x0A,
+ 0x86, 0xB0, 0x46, 0xEC, 0xF0, 0x51, 0x38, 0x23
+ },
+ {
+ 0x78, 0x7F, 0xC9, 0x34, 0x40, 0xC1, 0xEC, 0x96,
+ 0xB5, 0xAD, 0x01, 0xC1, 0x6C, 0xF7, 0x79, 0x16,
+ 0xA1, 0x40, 0x5F, 0x94, 0x26, 0x35, 0x6E, 0xC9,
+ 0x21, 0xD8, 0xDF, 0xF3, 0xEA, 0x63, 0xB7, 0xE0
+ },
+ {
+ 0x7F, 0x0D, 0x5E, 0xAB, 0x47, 0xEE, 0xFD, 0xA6,
+ 0x96, 0xC0, 0xBF, 0x0F, 0xBF, 0x86, 0xAB, 0x21,
+ 0x6F, 0xCE, 0x46, 0x1E, 0x93, 0x03, 0xAB, 0xA6,
+ 0xAC, 0x37, 0x41, 0x20, 0xE8, 0x90, 0xE8, 0xDF
+ },
+ {
+ 0xB6, 0x80, 0x04, 0xB4, 0x2F, 0x14, 0xAD, 0x02,
+ 0x9F, 0x4C, 0x2E, 0x03, 0xB1, 0xD5, 0xEB, 0x76,
+ 0xD5, 0x71, 0x60, 0xE2, 0x64, 0x76, 0xD2, 0x11,
+ 0x31, 0xBE, 0xF2, 0x0A, 0xDA, 0x7D, 0x27, 0xF4
+ },
+ {
+ 0xB0, 0xC4, 0xEB, 0x18, 0xAE, 0x25, 0x0B, 0x51,
+ 0xA4, 0x13, 0x82, 0xEA, 0xD9, 0x2D, 0x0D, 0xC7,
+ 0x45, 0x5F, 0x93, 0x79, 0xFC, 0x98, 0x84, 0x42,
+ 0x8E, 0x47, 0x70, 0x60, 0x8D, 0xB0, 0xFA, 0xEC
+ },
+ {
+ 0xF9, 0x2B, 0x7A, 0x87, 0x0C, 0x05, 0x9F, 0x4D,
+ 0x46, 0x46, 0x4C, 0x82, 0x4E, 0xC9, 0x63, 0x55,
+ 0x14, 0x0B, 0xDC, 0xE6, 0x81, 0x32, 0x2C, 0xC3,
+ 0xA9, 0x92, 0xFF, 0x10, 0x3E, 0x3F, 0xEA, 0x52
+ },
+ {
+ 0x53, 0x64, 0x31, 0x26, 0x14, 0x81, 0x33, 0x98,
+ 0xCC, 0x52, 0x5D, 0x4C, 0x4E, 0x14, 0x6E, 0xDE,
+ 0xB3, 0x71, 0x26, 0x5F, 0xBA, 0x19, 0x13, 0x3A,
+ 0x2C, 0x3D, 0x21, 0x59, 0x29, 0x8A, 0x17, 0x42
+ },
+ {
+ 0xF6, 0x62, 0x0E, 0x68, 0xD3, 0x7F, 0xB2, 0xAF,
+ 0x50, 0x00, 0xFC, 0x28, 0xE2, 0x3B, 0x83, 0x22,
+ 0x97, 0xEC, 0xD8, 0xBC, 0xE9, 0x9E, 0x8B, 0xE4,
+ 0xD0, 0x4E, 0x85, 0x30, 0x9E, 0x3D, 0x33, 0x74
+ },
+ {
+ 0x53, 0x16, 0xA2, 0x79, 0x69, 0xD7, 0xFE, 0x04,
+ 0xFF, 0x27, 0xB2, 0x83, 0x96, 0x1B, 0xFF, 0xC3,
+ 0xBF, 0x5D, 0xFB, 0x32, 0xFB, 0x6A, 0x89, 0xD1,
+ 0x01, 0xC6, 0xC3, 0xB1, 0x93, 0x7C, 0x28, 0x71
+ },
+ {
+ 0x81, 0xD1, 0x66, 0x4F, 0xDF, 0x3C, 0xB3, 0x3C,
+ 0x24, 0xEE, 0xBA, 0xC0, 0xBD, 0x64, 0x24, 0x4B,
+ 0x77, 0xC4, 0xAB, 0xEA, 0x90, 0xBB, 0xE8, 0xB5,
+ 0xEE, 0x0B, 0x2A, 0xAF, 0xCF, 0x2D, 0x6A, 0x53
+ },
+ {
+ 0x34, 0x57, 0x82, 0xF2, 0x95, 0xB0, 0x88, 0x03,
+ 0x52, 0xE9, 0x24, 0xA0, 0x46, 0x7B, 0x5F, 0xBC,
+ 0x3E, 0x8F, 0x3B, 0xFB, 0xC3, 0xC7, 0xE4, 0x8B,
+ 0x67, 0x09, 0x1F, 0xB5, 0xE8, 0x0A, 0x94, 0x42
+ },
+ {
+ 0x79, 0x41, 0x11, 0xEA, 0x6C, 0xD6, 0x5E, 0x31,
+ 0x1F, 0x74, 0xEE, 0x41, 0xD4, 0x76, 0xCB, 0x63,
+ 0x2C, 0xE1, 0xE4, 0xB0, 0x51, 0xDC, 0x1D, 0x9E,
+ 0x9D, 0x06, 0x1A, 0x19, 0xE1, 0xD0, 0xBB, 0x49
+ },
+ {
+ 0x2A, 0x85, 0xDA, 0xF6, 0x13, 0x88, 0x16, 0xB9,
+ 0x9B, 0xF8, 0xD0, 0x8B, 0xA2, 0x11, 0x4B, 0x7A,
+ 0xB0, 0x79, 0x75, 0xA7, 0x84, 0x20, 0xC1, 0xA3,
+ 0xB0, 0x6A, 0x77, 0x7C, 0x22, 0xDD, 0x8B, 0xCB
+ },
+ {
+ 0x89, 0xB0, 0xD5, 0xF2, 0x89, 0xEC, 0x16, 0x40,
+ 0x1A, 0x06, 0x9A, 0x96, 0x0D, 0x0B, 0x09, 0x3E,
+ 0x62, 0x5D, 0xA3, 0xCF, 0x41, 0xEE, 0x29, 0xB5,
+ 0x9B, 0x93, 0x0C, 0x58, 0x20, 0x14, 0x54, 0x55
+ },
+ {
+ 0xD0, 0xFD, 0xCB, 0x54, 0x39, 0x43, 0xFC, 0x27,
+ 0xD2, 0x08, 0x64, 0xF5, 0x21, 0x81, 0x47, 0x1B,
+ 0x94, 0x2C, 0xC7, 0x7C, 0xA6, 0x75, 0xBC, 0xB3,
+ 0x0D, 0xF3, 0x1D, 0x35, 0x8E, 0xF7, 0xB1, 0xEB
+ },
+ {
+ 0xB1, 0x7E, 0xA8, 0xD7, 0x70, 0x63, 0xC7, 0x09,
+ 0xD4, 0xDC, 0x6B, 0x87, 0x94, 0x13, 0xC3, 0x43,
+ 0xE3, 0x79, 0x0E, 0x9E, 0x62, 0xCA, 0x85, 0xB7,
+ 0x90, 0x0B, 0x08, 0x6F, 0x6B, 0x75, 0xC6, 0x72
+ },
+ {
+ 0xE7, 0x1A, 0x3E, 0x2C, 0x27, 0x4D, 0xB8, 0x42,
+ 0xD9, 0x21, 0x14, 0xF2, 0x17, 0xE2, 0xC0, 0xEA,
+ 0xC8, 0xB4, 0x50, 0x93, 0xFD, 0xFD, 0x9D, 0xF4,
+ 0xCA, 0x71, 0x62, 0x39, 0x48, 0x62, 0xD5, 0x01
+ },
+ {
+ 0xC0, 0x47, 0x67, 0x59, 0xAB, 0x7A, 0xA3, 0x33,
+ 0x23, 0x4F, 0x6B, 0x44, 0xF5, 0xFD, 0x85, 0x83,
+ 0x90, 0xEC, 0x23, 0x69, 0x4C, 0x62, 0x2C, 0xB9,
+ 0x86, 0xE7, 0x69, 0xC7, 0x8E, 0xDD, 0x73, 0x3E
+ },
+ {
+ 0x9A, 0xB8, 0xEA, 0xBB, 0x14, 0x16, 0x43, 0x4D,
+ 0x85, 0x39, 0x13, 0x41, 0xD5, 0x69, 0x93, 0xC5,
+ 0x54, 0x58, 0x16, 0x7D, 0x44, 0x18, 0xB1, 0x9A,
+ 0x0F, 0x2A, 0xD8, 0xB7, 0x9A, 0x83, 0xA7, 0x5B
+ },
+ {
+ 0x79, 0x92, 0xD0, 0xBB, 0xB1, 0x5E, 0x23, 0x82,
+ 0x6F, 0x44, 0x3E, 0x00, 0x50, 0x5D, 0x68, 0xD3,
+ 0xED, 0x73, 0x72, 0x99, 0x5A, 0x5C, 0x3E, 0x49,
+ 0x86, 0x54, 0x10, 0x2F, 0xBC, 0xD0, 0x96, 0x4E
+ },
+ {
+ 0xC0, 0x21, 0xB3, 0x00, 0x85, 0x15, 0x14, 0x35,
+ 0xDF, 0x33, 0xB0, 0x07, 0xCC, 0xEC, 0xC6, 0x9D,
+ 0xF1, 0x26, 0x9F, 0x39, 0xBA, 0x25, 0x09, 0x2B,
+ 0xED, 0x59, 0xD9, 0x32, 0xAC, 0x0F, 0xDC, 0x28
+ },
+ {
+ 0x91, 0xA2, 0x5E, 0xC0, 0xEC, 0x0D, 0x9A, 0x56,
+ 0x7F, 0x89, 0xC4, 0xBF, 0xE1, 0xA6, 0x5A, 0x0E,
+ 0x43, 0x2D, 0x07, 0x06, 0x4B, 0x41, 0x90, 0xE2,
+ 0x7D, 0xFB, 0x81, 0x90, 0x1F, 0xD3, 0x13, 0x9B
+ },
+ {
+ 0x59, 0x50, 0xD3, 0x9A, 0x23, 0xE1, 0x54, 0x5F,
+ 0x30, 0x12, 0x70, 0xAA, 0x1A, 0x12, 0xF2, 0xE6,
+ 0xC4, 0x53, 0x77, 0x6E, 0x4D, 0x63, 0x55, 0xDE,
+ 0x42, 0x5C, 0xC1, 0x53, 0xF9, 0x81, 0x88, 0x67
+ },
+ {
+ 0xD7, 0x9F, 0x14, 0x72, 0x0C, 0x61, 0x0A, 0xF1,
+ 0x79, 0xA3, 0x76, 0x5D, 0x4B, 0x7C, 0x09, 0x68,
+ 0xF9, 0x77, 0x96, 0x2D, 0xBF, 0x65, 0x5B, 0x52,
+ 0x12, 0x72, 0xB6, 0xF1, 0xE1, 0x94, 0x48, 0x8E
+ },
+ {
+ 0xE9, 0x53, 0x1B, 0xFC, 0x8B, 0x02, 0x99, 0x5A,
+ 0xEA, 0xA7, 0x5B, 0xA2, 0x70, 0x31, 0xFA, 0xDB,
+ 0xCB, 0xF4, 0xA0, 0xDA, 0xB8, 0x96, 0x1D, 0x92,
+ 0x96, 0xCD, 0x7E, 0x84, 0xD2, 0x5D, 0x60, 0x06
+ },
+ {
+ 0x34, 0xE9, 0xC2, 0x6A, 0x01, 0xD7, 0xF1, 0x61,
+ 0x81, 0xB4, 0x54, 0xA9, 0xD1, 0x62, 0x3C, 0x23,
+ 0x3C, 0xB9, 0x9D, 0x31, 0xC6, 0x94, 0x65, 0x6E,
+ 0x94, 0x13, 0xAC, 0xA3, 0xE9, 0x18, 0x69, 0x2F
+ },
+ {
+ 0xD9, 0xD7, 0x42, 0x2F, 0x43, 0x7B, 0xD4, 0x39,
+ 0xDD, 0xD4, 0xD8, 0x83, 0xDA, 0xE2, 0xA0, 0x83,
+ 0x50, 0x17, 0x34, 0x14, 0xBE, 0x78, 0x15, 0x51,
+ 0x33, 0xFF, 0xF1, 0x96, 0x4C, 0x3D, 0x79, 0x72
+ },
+ {
+ 0x4A, 0xEE, 0x0C, 0x7A, 0xAF, 0x07, 0x54, 0x14,
+ 0xFF, 0x17, 0x93, 0xEA, 0xD7, 0xEA, 0xCA, 0x60,
+ 0x17, 0x75, 0xC6, 0x15, 0xDB, 0xD6, 0x0B, 0x64,
+ 0x0B, 0x0A, 0x9F, 0x0C, 0xE5, 0x05, 0xD4, 0x35
+ },
+ {
+ 0x6B, 0xFD, 0xD1, 0x54, 0x59, 0xC8, 0x3B, 0x99,
+ 0xF0, 0x96, 0xBF, 0xB4, 0x9E, 0xE8, 0x7B, 0x06,
+ 0x3D, 0x69, 0xC1, 0x97, 0x4C, 0x69, 0x28, 0xAC,
+ 0xFC, 0xFB, 0x40, 0x99, 0xF8, 0xC4, 0xEF, 0x67
+ },
+ {
+ 0x9F, 0xD1, 0xC4, 0x08, 0xFD, 0x75, 0xC3, 0x36,
+ 0x19, 0x3A, 0x2A, 0x14, 0xD9, 0x4F, 0x6A, 0xF5,
+ 0xAD, 0xF0, 0x50, 0xB8, 0x03, 0x87, 0xB4, 0xB0,
+ 0x10, 0xFB, 0x29, 0xF4, 0xCC, 0x72, 0x70, 0x7C
+ },
+ {
+ 0x13, 0xC8, 0x84, 0x80, 0xA5, 0xD0, 0x0D, 0x6C,
+ 0x8C, 0x7A, 0xD2, 0x11, 0x0D, 0x76, 0xA8, 0x2D,
+ 0x9B, 0x70, 0xF4, 0xFA, 0x66, 0x96, 0xD4, 0xE5,
+ 0xDD, 0x42, 0xA0, 0x66, 0xDC, 0xAF, 0x99, 0x20
+ },
+ {
+ 0x82, 0x0E, 0x72, 0x5E, 0xE2, 0x5F, 0xE8, 0xFD,
+ 0x3A, 0x8D, 0x5A, 0xBE, 0x4C, 0x46, 0xC3, 0xBA,
+ 0x88, 0x9D, 0xE6, 0xFA, 0x91, 0x91, 0xAA, 0x22,
+ 0xBA, 0x67, 0xD5, 0x70, 0x54, 0x21, 0x54, 0x2B
+ },
+ {
+ 0x32, 0xD9, 0x3A, 0x0E, 0xB0, 0x2F, 0x42, 0xFB,
+ 0xBC, 0xAF, 0x2B, 0xAD, 0x00, 0x85, 0xB2, 0x82,
+ 0xE4, 0x60, 0x46, 0xA4, 0xDF, 0x7A, 0xD1, 0x06,
+ 0x57, 0xC9, 0xD6, 0x47, 0x63, 0x75, 0xB9, 0x3E
+ },
+ {
+ 0xAD, 0xC5, 0x18, 0x79, 0x05, 0xB1, 0x66, 0x9C,
+ 0xD8, 0xEC, 0x9C, 0x72, 0x1E, 0x19, 0x53, 0x78,
+ 0x6B, 0x9D, 0x89, 0xA9, 0xBA, 0xE3, 0x07, 0x80,
+ 0xF1, 0xE1, 0xEA, 0xB2, 0x4A, 0x00, 0x52, 0x3C
+ },
+ {
+ 0xE9, 0x07, 0x56, 0xFF, 0x7F, 0x9A, 0xD8, 0x10,
+ 0xB2, 0x39, 0xA1, 0x0C, 0xED, 0x2C, 0xF9, 0xB2,
+ 0x28, 0x43, 0x54, 0xC1, 0xF8, 0xC7, 0xE0, 0xAC,
+ 0xCC, 0x24, 0x61, 0xDC, 0x79, 0x6D, 0x6E, 0x89
+ },
+ {
+ 0x12, 0x51, 0xF7, 0x6E, 0x56, 0x97, 0x84, 0x81,
+ 0x87, 0x53, 0x59, 0x80, 0x1D, 0xB5, 0x89, 0xA0,
+ 0xB2, 0x2F, 0x86, 0xD8, 0xD6, 0x34, 0xDC, 0x04,
+ 0x50, 0x6F, 0x32, 0x2E, 0xD7, 0x8F, 0x17, 0xE8
+ },
+ {
+ 0x3A, 0xFA, 0x89, 0x9F, 0xD9, 0x80, 0xE7, 0x3E,
+ 0xCB, 0x7F, 0x4D, 0x8B, 0x8F, 0x29, 0x1D, 0xC9,
+ 0xAF, 0x79, 0x6B, 0xC6, 0x5D, 0x27, 0xF9, 0x74,
+ 0xC6, 0xF1, 0x93, 0xC9, 0x19, 0x1A, 0x09, 0xFD
+ },
+ {
+ 0xAA, 0x30, 0x5B, 0xE2, 0x6E, 0x5D, 0xED, 0xDC,
+ 0x3C, 0x10, 0x10, 0xCB, 0xC2, 0x13, 0xF9, 0x5F,
+ 0x05, 0x1C, 0x78, 0x5C, 0x5B, 0x43, 0x1E, 0x6A,
+ 0x7C, 0xD0, 0x48, 0xF1, 0x61, 0x78, 0x75, 0x28
+ },
+ {
+ 0x8E, 0xA1, 0x88, 0x4F, 0xF3, 0x2E, 0x9D, 0x10,
+ 0xF0, 0x39, 0xB4, 0x07, 0xD0, 0xD4, 0x4E, 0x7E,
+ 0x67, 0x0A, 0xBD, 0x88, 0x4A, 0xEE, 0xE0, 0xFB,
+ 0x75, 0x7A, 0xE9, 0x4E, 0xAA, 0x97, 0x37, 0x3D
+ },
+ {
+ 0xD4, 0x82, 0xB2, 0x15, 0x5D, 0x4D, 0xEC, 0x6B,
+ 0x47, 0x36, 0xA1, 0xF1, 0x61, 0x7B, 0x53, 0xAA,
+ 0xA3, 0x73, 0x10, 0x27, 0x7D, 0x3F, 0xEF, 0x0C,
+ 0x37, 0xAD, 0x41, 0x76, 0x8F, 0xC2, 0x35, 0xB4
+ },
+ {
+ 0x4D, 0x41, 0x39, 0x71, 0x38, 0x7E, 0x7A, 0x88,
+ 0x98, 0xA8, 0xDC, 0x2A, 0x27, 0x50, 0x07, 0x78,
+ 0x53, 0x9E, 0xA2, 0x14, 0xA2, 0xDF, 0xE9, 0xB3,
+ 0xD7, 0xE8, 0xEB, 0xDC, 0xE5, 0xCF, 0x3D, 0xB3
+ },
+ {
+ 0x69, 0x6E, 0x5D, 0x46, 0xE6, 0xC5, 0x7E, 0x87,
+ 0x96, 0xE4, 0x73, 0x5D, 0x08, 0x91, 0x6E, 0x0B,
+ 0x79, 0x29, 0xB3, 0xCF, 0x29, 0x8C, 0x29, 0x6D,
+ 0x22, 0xE9, 0xD3, 0x01, 0x96, 0x53, 0x37, 0x1C
+ },
+ {
+ 0x1F, 0x56, 0x47, 0xC1, 0xD3, 0xB0, 0x88, 0x22,
+ 0x88, 0x85, 0x86, 0x5C, 0x89, 0x40, 0x90, 0x8B,
+ 0xF4, 0x0D, 0x1A, 0x82, 0x72, 0x82, 0x19, 0x73,
+ 0xB1, 0x60, 0x00, 0x8E, 0x7A, 0x3C, 0xE2, 0xEB
+ },
+ {
+ 0xB6, 0xE7, 0x6C, 0x33, 0x0F, 0x02, 0x1A, 0x5B,
+ 0xDA, 0x65, 0x87, 0x50, 0x10, 0xB0, 0xED, 0xF0,
+ 0x91, 0x26, 0xC0, 0xF5, 0x10, 0xEA, 0x84, 0x90,
+ 0x48, 0x19, 0x20, 0x03, 0xAE, 0xF4, 0xC6, 0x1C
+ },
+ {
+ 0x3C, 0xD9, 0x52, 0xA0, 0xBE, 0xAD, 0xA4, 0x1A,
+ 0xBB, 0x42, 0x4C, 0xE4, 0x7F, 0x94, 0xB4, 0x2B,
+ 0xE6, 0x4E, 0x1F, 0xFB, 0x0F, 0xD0, 0x78, 0x22,
+ 0x76, 0x80, 0x79, 0x46, 0xD0, 0xD0, 0xBC, 0x55
+ },
+ {
+ 0x98, 0xD9, 0x26, 0x77, 0x43, 0x9B, 0x41, 0xB7,
+ 0xBB, 0x51, 0x33, 0x12, 0xAF, 0xB9, 0x2B, 0xCC,
+ 0x8E, 0xE9, 0x68, 0xB2, 0xE3, 0xB2, 0x38, 0xCE,
+ 0xCB, 0x9B, 0x0F, 0x34, 0xC9, 0xBB, 0x63, 0xD0
+ },
+ {
+ 0xEC, 0xBC, 0xA2, 0xCF, 0x08, 0xAE, 0x57, 0xD5,
+ 0x17, 0xAD, 0x16, 0x15, 0x8A, 0x32, 0xBF, 0xA7,
+ 0xDC, 0x03, 0x82, 0xEA, 0xED, 0xA1, 0x28, 0xE9,
+ 0x18, 0x86, 0x73, 0x4C, 0x24, 0xA0, 0xB2, 0x9D
+ },
+ {
+ 0x94, 0x2C, 0xC7, 0xC0, 0xB5, 0x2E, 0x2B, 0x16,
+ 0xA4, 0xB8, 0x9F, 0xA4, 0xFC, 0x7E, 0x0B, 0xF6,
+ 0x09, 0xE2, 0x9A, 0x08, 0xC1, 0xA8, 0x54, 0x34,
+ 0x52, 0xB7, 0x7C, 0x7B, 0xFD, 0x11, 0xBB, 0x28
+ },
+ {
+ 0x8A, 0x06, 0x5D, 0x8B, 0x61, 0xA0, 0xDF, 0xFB,
+ 0x17, 0x0D, 0x56, 0x27, 0x73, 0x5A, 0x76, 0xB0,
+ 0xE9, 0x50, 0x60, 0x37, 0x80, 0x8C, 0xBA, 0x16,
+ 0xC3, 0x45, 0x00, 0x7C, 0x9F, 0x79, 0xCF, 0x8F
+ },
+ {
+ 0x1B, 0x9F, 0xA1, 0x97, 0x14, 0x65, 0x9C, 0x78,
+ 0xFF, 0x41, 0x38, 0x71, 0x84, 0x92, 0x15, 0x36,
+ 0x10, 0x29, 0xAC, 0x80, 0x2B, 0x1C, 0xBC, 0xD5,
+ 0x4E, 0x40, 0x8B, 0xD8, 0x72, 0x87, 0xF8, 0x1F
+ },
+ {
+ 0x8D, 0xAB, 0x07, 0x1B, 0xCD, 0x6C, 0x72, 0x92,
+ 0xA9, 0xEF, 0x72, 0x7B, 0x4A, 0xE0, 0xD8, 0x67,
+ 0x13, 0x30, 0x1D, 0xA8, 0x61, 0x8D, 0x9A, 0x48,
+ 0xAD, 0xCE, 0x55, 0xF3, 0x03, 0xA8, 0x69, 0xA1
+ },
+ {
+ 0x82, 0x53, 0xE3, 0xE7, 0xC7, 0xB6, 0x84, 0xB9,
+ 0xCB, 0x2B, 0xEB, 0x01, 0x4C, 0xE3, 0x30, 0xFF,
+ 0x3D, 0x99, 0xD1, 0x7A, 0xBB, 0xDB, 0xAB, 0xE4,
+ 0xF4, 0xD6, 0x74, 0xDE, 0xD5, 0x3F, 0xFC, 0x6B
+ },
+ {
+ 0xF1, 0x95, 0xF3, 0x21, 0xE9, 0xE3, 0xD6, 0xBD,
+ 0x7D, 0x07, 0x45, 0x04, 0xDD, 0x2A, 0xB0, 0xE6,
+ 0x24, 0x1F, 0x92, 0xE7, 0x84, 0xB1, 0xAA, 0x27,
+ 0x1F, 0xF6, 0x48, 0xB1, 0xCA, 0xB6, 0xD7, 0xF6
+ },
+ {
+ 0x27, 0xE4, 0xCC, 0x72, 0x09, 0x0F, 0x24, 0x12,
+ 0x66, 0x47, 0x6A, 0x7C, 0x09, 0x49, 0x5F, 0x2D,
+ 0xB1, 0x53, 0xD5, 0xBC, 0xBD, 0x76, 0x19, 0x03,
+ 0xEF, 0x79, 0x27, 0x5E, 0xC5, 0x6B, 0x2E, 0xD8
+ },
+ {
+ 0x89, 0x9C, 0x24, 0x05, 0x78, 0x8E, 0x25, 0xB9,
+ 0x9A, 0x18, 0x46, 0x35, 0x5E, 0x64, 0x6D, 0x77,
+ 0xCF, 0x40, 0x00, 0x83, 0x41, 0x5F, 0x7D, 0xC5,
+ 0xAF, 0xE6, 0x9D, 0x6E, 0x17, 0xC0, 0x00, 0x23
+ },
+ {
+ 0xA5, 0x9B, 0x78, 0xC4, 0x90, 0x57, 0x44, 0x07,
+ 0x6B, 0xFE, 0xE8, 0x94, 0xDE, 0x70, 0x7D, 0x4F,
+ 0x12, 0x0B, 0x5C, 0x68, 0x93, 0xEA, 0x04, 0x00,
+ 0x29, 0x7D, 0x0B, 0xB8, 0x34, 0x72, 0x76, 0x32
+ },
+ {
+ 0x59, 0xDC, 0x78, 0xB1, 0x05, 0x64, 0x97, 0x07,
+ 0xA2, 0xBB, 0x44, 0x19, 0xC4, 0x8F, 0x00, 0x54,
+ 0x00, 0xD3, 0x97, 0x3D, 0xE3, 0x73, 0x66, 0x10,
+ 0x23, 0x04, 0x35, 0xB1, 0x04, 0x24, 0xB2, 0x4F
+ },
+ {
+ 0xC0, 0x14, 0x9D, 0x1D, 0x7E, 0x7A, 0x63, 0x53,
+ 0xA6, 0xD9, 0x06, 0xEF, 0xE7, 0x28, 0xF2, 0xF3,
+ 0x29, 0xFE, 0x14, 0xA4, 0x14, 0x9A, 0x3E, 0xA7,
+ 0x76, 0x09, 0xBC, 0x42, 0xB9, 0x75, 0xDD, 0xFA
+ },
+ {
+ 0xA3, 0x2F, 0x24, 0x14, 0x74, 0xA6, 0xC1, 0x69,
+ 0x32, 0xE9, 0x24, 0x3B, 0xE0, 0xCF, 0x09, 0xBC,
+ 0xDC, 0x7E, 0x0C, 0xA0, 0xE7, 0xA6, 0xA1, 0xB9,
+ 0xB1, 0xA0, 0xF0, 0x1E, 0x41, 0x50, 0x23, 0x77
+ },
+ {
+ 0xB2, 0x39, 0xB2, 0xE4, 0xF8, 0x18, 0x41, 0x36,
+ 0x1C, 0x13, 0x39, 0xF6, 0x8E, 0x2C, 0x35, 0x9F,
+ 0x92, 0x9A, 0xF9, 0xAD, 0x9F, 0x34, 0xE0, 0x1A,
+ 0xAB, 0x46, 0x31, 0xAD, 0x6D, 0x55, 0x00, 0xB0
+ },
+ {
+ 0x85, 0xFB, 0x41, 0x9C, 0x70, 0x02, 0xA3, 0xE0,
+ 0xB4, 0xB6, 0xEA, 0x09, 0x3B, 0x4C, 0x1A, 0xC6,
+ 0x93, 0x66, 0x45, 0xB6, 0x5D, 0xAC, 0x5A, 0xC1,
+ 0x5A, 0x85, 0x28, 0xB7, 0xB9, 0x4C, 0x17, 0x54
+ },
+ {
+ 0x96, 0x19, 0x72, 0x06, 0x25, 0xF1, 0x90, 0xB9,
+ 0x3A, 0x3F, 0xAD, 0x18, 0x6A, 0xB3, 0x14, 0x18,
+ 0x96, 0x33, 0xC0, 0xD3, 0xA0, 0x1E, 0x6F, 0x9B,
+ 0xC8, 0xC4, 0xA8, 0xF8, 0x2F, 0x38, 0x3D, 0xBF
+ },
+ {
+ 0x7D, 0x62, 0x0D, 0x90, 0xFE, 0x69, 0xFA, 0x46,
+ 0x9A, 0x65, 0x38, 0x38, 0x89, 0x70, 0xA1, 0xAA,
+ 0x09, 0xBB, 0x48, 0xA2, 0xD5, 0x9B, 0x34, 0x7B,
+ 0x97, 0xE8, 0xCE, 0x71, 0xF4, 0x8C, 0x7F, 0x46
+ },
+ {
+ 0x29, 0x43, 0x83, 0x56, 0x85, 0x96, 0xFB, 0x37,
+ 0xC7, 0x5B, 0xBA, 0xCD, 0x97, 0x9C, 0x5F, 0xF6,
+ 0xF2, 0x0A, 0x55, 0x6B, 0xF8, 0x87, 0x9C, 0xC7,
+ 0x29, 0x24, 0x85, 0x5D, 0xF9, 0xB8, 0x24, 0x0E
+ },
+ {
+ 0x16, 0xB1, 0x8A, 0xB3, 0x14, 0x35, 0x9C, 0x2B,
+ 0x83, 0x3C, 0x1C, 0x69, 0x86, 0xD4, 0x8C, 0x55,
+ 0xA9, 0xFC, 0x97, 0xCD, 0xE9, 0xA3, 0xC1, 0xF1,
+ 0x0A, 0x31, 0x77, 0x14, 0x0F, 0x73, 0xF7, 0x38
+ },
+ {
+ 0x8C, 0xBB, 0xDD, 0x14, 0xBC, 0x33, 0xF0, 0x4C,
+ 0xF4, 0x58, 0x13, 0xE4, 0xA1, 0x53, 0xA2, 0x73,
+ 0xD3, 0x6A, 0xDA, 0xD5, 0xCE, 0x71, 0xF4, 0x99,
+ 0xEE, 0xB8, 0x7F, 0xB8, 0xAC, 0x63, 0xB7, 0x29
+ },
+ {
+ 0x69, 0xC9, 0xA4, 0x98, 0xDB, 0x17, 0x4E, 0xCA,
+ 0xEF, 0xCC, 0x5A, 0x3A, 0xC9, 0xFD, 0xED, 0xF0,
+ 0xF8, 0x13, 0xA5, 0xBE, 0xC7, 0x27, 0xF1, 0xE7,
+ 0x75, 0xBA, 0xBD, 0xEC, 0x77, 0x18, 0x81, 0x6E
+ },
+ {
+ 0xB4, 0x62, 0xC3, 0xBE, 0x40, 0x44, 0x8F, 0x1D,
+ 0x4F, 0x80, 0x62, 0x62, 0x54, 0xE5, 0x35, 0xB0,
+ 0x8B, 0xC9, 0xCD, 0xCF, 0xF5, 0x99, 0xA7, 0x68,
+ 0x57, 0x8D, 0x4B, 0x28, 0x81, 0xA8, 0xE3, 0xF0
+ },
+ {
+ 0x55, 0x3E, 0x9D, 0x9C, 0x5F, 0x36, 0x0A, 0xC0,
+ 0xB7, 0x4A, 0x7D, 0x44, 0xE5, 0xA3, 0x91, 0xDA,
+ 0xD4, 0xCE, 0xD0, 0x3E, 0x0C, 0x24, 0x18, 0x3B,
+ 0x7E, 0x8E, 0xCA, 0xBD, 0xF1, 0x71, 0x5A, 0x64
+ },
+ {
+ 0x7A, 0x7C, 0x55, 0xA5, 0x6F, 0xA9, 0xAE, 0x51,
+ 0xE6, 0x55, 0xE0, 0x19, 0x75, 0xD8, 0xA6, 0xFF,
+ 0x4A, 0xE9, 0xE4, 0xB4, 0x86, 0xFC, 0xBE, 0x4E,
+ 0xAC, 0x04, 0x45, 0x88, 0xF2, 0x45, 0xEB, 0xEA
+ },
+ {
+ 0x2A, 0xFD, 0xF3, 0xC8, 0x2A, 0xBC, 0x48, 0x67,
+ 0xF5, 0xDE, 0x11, 0x12, 0x86, 0xC2, 0xB3, 0xBE,
+ 0x7D, 0x6E, 0x48, 0x65, 0x7B, 0xA9, 0x23, 0xCF,
+ 0xBF, 0x10, 0x1A, 0x6D, 0xFC, 0xF9, 0xDB, 0x9A
+ },
+ {
+ 0x41, 0x03, 0x7D, 0x2E, 0xDC, 0xDC, 0xE0, 0xC4,
+ 0x9B, 0x7F, 0xB4, 0xA6, 0xAA, 0x09, 0x99, 0xCA,
+ 0x66, 0x97, 0x6C, 0x74, 0x83, 0xAF, 0xE6, 0x31,
+ 0xD4, 0xED, 0xA2, 0x83, 0x14, 0x4F, 0x6D, 0xFC
+ },
+ {
+ 0xC4, 0x46, 0x6F, 0x84, 0x97, 0xCA, 0x2E, 0xEB,
+ 0x45, 0x83, 0xA0, 0xB0, 0x8E, 0x9D, 0x9A, 0xC7,
+ 0x43, 0x95, 0x70, 0x9F, 0xDA, 0x10, 0x9D, 0x24,
+ 0xF2, 0xE4, 0x46, 0x21, 0x96, 0x77, 0x9C, 0x5D
+ },
+ {
+ 0x75, 0xF6, 0x09, 0x33, 0x8A, 0xA6, 0x7D, 0x96,
+ 0x9A, 0x2A, 0xE2, 0xA2, 0x36, 0x2B, 0x2D, 0xA9,
+ 0xD7, 0x7C, 0x69, 0x5D, 0xFD, 0x1D, 0xF7, 0x22,
+ 0x4A, 0x69, 0x01, 0xDB, 0x93, 0x2C, 0x33, 0x64
+ },
+ {
+ 0x68, 0x60, 0x6C, 0xEB, 0x98, 0x9D, 0x54, 0x88,
+ 0xFC, 0x7C, 0xF6, 0x49, 0xF3, 0xD7, 0xC2, 0x72,
+ 0xEF, 0x05, 0x5D, 0xA1, 0xA9, 0x3F, 0xAE, 0xCD,
+ 0x55, 0xFE, 0x06, 0xF6, 0x96, 0x70, 0x98, 0xCA
+ },
+ {
+ 0x44, 0x34, 0x6B, 0xDE, 0xB7, 0xE0, 0x52, 0xF6,
+ 0x25, 0x50, 0x48, 0xF0, 0xD9, 0xB4, 0x2C, 0x42,
+ 0x5B, 0xAB, 0x9C, 0x3D, 0xD2, 0x41, 0x68, 0x21,
+ 0x2C, 0x3E, 0xCF, 0x1E, 0xBF, 0x34, 0xE6, 0xAE
+ },
+ {
+ 0x8E, 0x9C, 0xF6, 0xE1, 0xF3, 0x66, 0x47, 0x1F,
+ 0x2A, 0xC7, 0xD2, 0xEE, 0x9B, 0x5E, 0x62, 0x66,
+ 0xFD, 0xA7, 0x1F, 0x8F, 0x2E, 0x41, 0x09, 0xF2,
+ 0x23, 0x7E, 0xD5, 0xF8, 0x81, 0x3F, 0xC7, 0x18
+ },
+ {
+ 0x84, 0xBB, 0xEB, 0x84, 0x06, 0xD2, 0x50, 0x95,
+ 0x1F, 0x8C, 0x1B, 0x3E, 0x86, 0xA7, 0xC0, 0x10,
+ 0x08, 0x29, 0x21, 0x83, 0x3D, 0xFD, 0x95, 0x55,
+ 0xA2, 0xF9, 0x09, 0xB1, 0x08, 0x6E, 0xB4, 0xB8
+ },
+ {
+ 0xEE, 0x66, 0x6F, 0x3E, 0xEF, 0x0F, 0x7E, 0x2A,
+ 0x9C, 0x22, 0x29, 0x58, 0xC9, 0x7E, 0xAF, 0x35,
+ 0xF5, 0x1C, 0xED, 0x39, 0x3D, 0x71, 0x44, 0x85,
+ 0xAB, 0x09, 0xA0, 0x69, 0x34, 0x0F, 0xDF, 0x88
+ },
+ {
+ 0xC1, 0x53, 0xD3, 0x4A, 0x65, 0xC4, 0x7B, 0x4A,
+ 0x62, 0xC5, 0xCA, 0xCF, 0x24, 0x01, 0x09, 0x75,
+ 0xD0, 0x35, 0x6B, 0x2F, 0x32, 0xC8, 0xF5, 0xDA,
+ 0x53, 0x0D, 0x33, 0x88, 0x16, 0xAD, 0x5D, 0xE6
+ },
+ {
+ 0x9F, 0xC5, 0x45, 0x01, 0x09, 0xE1, 0xB7, 0x79,
+ 0xF6, 0xC7, 0xAE, 0x79, 0xD5, 0x6C, 0x27, 0x63,
+ 0x5C, 0x8D, 0xD4, 0x26, 0xC5, 0xA9, 0xD5, 0x4E,
+ 0x25, 0x78, 0xDB, 0x98, 0x9B, 0x8C, 0x3B, 0x4E
+ },
+ {
+ 0xD1, 0x2B, 0xF3, 0x73, 0x2E, 0xF4, 0xAF, 0x5C,
+ 0x22, 0xFA, 0x90, 0x35, 0x6A, 0xF8, 0xFC, 0x50,
+ 0xFC, 0xB4, 0x0F, 0x8F, 0x2E, 0xA5, 0xC8, 0x59,
+ 0x47, 0x37, 0xA3, 0xB3, 0xD5, 0xAB, 0xDB, 0xD7
+ },
+ {
+ 0x11, 0x03, 0x0B, 0x92, 0x89, 0xBB, 0xA5, 0xAF,
+ 0x65, 0x26, 0x06, 0x72, 0xAB, 0x6F, 0xEE, 0x88,
+ 0xB8, 0x74, 0x20, 0xAC, 0xEF, 0x4A, 0x17, 0x89,
+ 0xA2, 0x07, 0x3B, 0x7E, 0xC2, 0xF2, 0xA0, 0x9E
+ },
+ {
+ 0x69, 0xCB, 0x19, 0x2B, 0x84, 0x44, 0x00, 0x5C,
+ 0x8C, 0x0C, 0xEB, 0x12, 0xC8, 0x46, 0x86, 0x07,
+ 0x68, 0x18, 0x8C, 0xDA, 0x0A, 0xEC, 0x27, 0xA9,
+ 0xC8, 0xA5, 0x5C, 0xDE, 0xE2, 0x12, 0x36, 0x32
+ },
+ {
+ 0xDB, 0x44, 0x4C, 0x15, 0x59, 0x7B, 0x5F, 0x1A,
+ 0x03, 0xD1, 0xF9, 0xED, 0xD1, 0x6E, 0x4A, 0x9F,
+ 0x43, 0xA6, 0x67, 0xCC, 0x27, 0x51, 0x75, 0xDF,
+ 0xA2, 0xB7, 0x04, 0xE3, 0xBB, 0x1A, 0x9B, 0x83
+ },
+ {
+ 0x3F, 0xB7, 0x35, 0x06, 0x1A, 0xBC, 0x51, 0x9D,
+ 0xFE, 0x97, 0x9E, 0x54, 0xC1, 0xEE, 0x5B, 0xFA,
+ 0xD0, 0xA9, 0xD8, 0x58, 0xB3, 0x31, 0x5B, 0xAD,
+ 0x34, 0xBD, 0xE9, 0x99, 0xEF, 0xD7, 0x24, 0xDD
+ },
+};
+
+
+
+
+static const uint8_t blake2b_kat[KAT_LENGTH][BLAKE2B_OUTBYTES] =
+{
+ {
+ 0x78, 0x6A, 0x02, 0xF7, 0x42, 0x01, 0x59, 0x03,
+ 0xC6, 0xC6, 0xFD, 0x85, 0x25, 0x52, 0xD2, 0x72,
+ 0x91, 0x2F, 0x47, 0x40, 0xE1, 0x58, 0x47, 0x61,
+ 0x8A, 0x86, 0xE2, 0x17, 0xF7, 0x1F, 0x54, 0x19,
+ 0xD2, 0x5E, 0x10, 0x31, 0xAF, 0xEE, 0x58, 0x53,
+ 0x13, 0x89, 0x64, 0x44, 0x93, 0x4E, 0xB0, 0x4B,
+ 0x90, 0x3A, 0x68, 0x5B, 0x14, 0x48, 0xB7, 0x55,
+ 0xD5, 0x6F, 0x70, 0x1A, 0xFE, 0x9B, 0xE2, 0xCE
+ },
+ {
+ 0x2F, 0xA3, 0xF6, 0x86, 0xDF, 0x87, 0x69, 0x95,
+ 0x16, 0x7E, 0x7C, 0x2E, 0x5D, 0x74, 0xC4, 0xC7,
+ 0xB6, 0xE4, 0x8F, 0x80, 0x68, 0xFE, 0x0E, 0x44,
+ 0x20, 0x83, 0x44, 0xD4, 0x80, 0xF7, 0x90, 0x4C,
+ 0x36, 0x96, 0x3E, 0x44, 0x11, 0x5F, 0xE3, 0xEB,
+ 0x2A, 0x3A, 0xC8, 0x69, 0x4C, 0x28, 0xBC, 0xB4,
+ 0xF5, 0xA0, 0xF3, 0x27, 0x6F, 0x2E, 0x79, 0x48,
+ 0x7D, 0x82, 0x19, 0x05, 0x7A, 0x50, 0x6E, 0x4B
+ },
+ {
+ 0x1C, 0x08, 0x79, 0x8D, 0xC6, 0x41, 0xAB, 0xA9,
+ 0xDE, 0xE4, 0x35, 0xE2, 0x25, 0x19, 0xA4, 0x72,
+ 0x9A, 0x09, 0xB2, 0xBF, 0xE0, 0xFF, 0x00, 0xEF,
+ 0x2D, 0xCD, 0x8E, 0xD6, 0xF8, 0xA0, 0x7D, 0x15,
+ 0xEA, 0xF4, 0xAE, 0xE5, 0x2B, 0xBF, 0x18, 0xAB,
+ 0x56, 0x08, 0xA6, 0x19, 0x0F, 0x70, 0xB9, 0x04,
+ 0x86, 0xC8, 0xA7, 0xD4, 0x87, 0x37, 0x10, 0xB1,
+ 0x11, 0x5D, 0x3D, 0xEB, 0xBB, 0x43, 0x27, 0xB5
+ },
+ {
+ 0x40, 0xA3, 0x74, 0x72, 0x73, 0x02, 0xD9, 0xA4,
+ 0x76, 0x9C, 0x17, 0xB5, 0xF4, 0x09, 0xFF, 0x32,
+ 0xF5, 0x8A, 0xA2, 0x4F, 0xF1, 0x22, 0xD7, 0x60,
+ 0x3E, 0x4F, 0xDA, 0x15, 0x09, 0xE9, 0x19, 0xD4,
+ 0x10, 0x7A, 0x52, 0xC5, 0x75, 0x70, 0xA6, 0xD9,
+ 0x4E, 0x50, 0x96, 0x7A, 0xEA, 0x57, 0x3B, 0x11,
+ 0xF8, 0x6F, 0x47, 0x3F, 0x53, 0x75, 0x65, 0xC6,
+ 0x6F, 0x70, 0x39, 0x83, 0x0A, 0x85, 0xD1, 0x86
+ },
+ {
+ 0x77, 0xDD, 0xF4, 0xB1, 0x44, 0x25, 0xEB, 0x3D,
+ 0x05, 0x3C, 0x1E, 0x84, 0xE3, 0x46, 0x9D, 0x92,
+ 0xC4, 0xCD, 0x91, 0x0E, 0xD2, 0x0F, 0x92, 0x03,
+ 0x5E, 0x0C, 0x99, 0xD8, 0xA7, 0xA8, 0x6C, 0xEC,
+ 0xAF, 0x69, 0xF9, 0x66, 0x3C, 0x20, 0xA7, 0xAA,
+ 0x23, 0x0B, 0xC8, 0x2F, 0x60, 0xD2, 0x2F, 0xB4,
+ 0xA0, 0x0B, 0x09, 0xD3, 0xEB, 0x8F, 0xC6, 0x5E,
+ 0xF5, 0x47, 0xFE, 0x63, 0xC8, 0xD3, 0xDD, 0xCE
+ },
+ {
+ 0xCB, 0xAA, 0x0B, 0xA7, 0xD4, 0x82, 0xB1, 0xF3,
+ 0x01, 0x10, 0x9A, 0xE4, 0x10, 0x51, 0x99, 0x1A,
+ 0x32, 0x89, 0xBC, 0x11, 0x98, 0x00, 0x5A, 0xF2,
+ 0x26, 0xC5, 0xE4, 0xF1, 0x03, 0xB6, 0x65, 0x79,
+ 0xF4, 0x61, 0x36, 0x10, 0x44, 0xC8, 0xBA, 0x34,
+ 0x39, 0xFF, 0x12, 0xC5, 0x15, 0xFB, 0x29, 0xC5,
+ 0x21, 0x61, 0xB7, 0xEB, 0x9C, 0x28, 0x37, 0xB7,
+ 0x6A, 0x5D, 0xC3, 0x3F, 0x7C, 0xB2, 0xE2, 0xE8
+ },
+ {
+ 0xF9, 0x5D, 0x45, 0xCF, 0x69, 0xAF, 0x5C, 0x20,
+ 0x23, 0xBD, 0xB5, 0x05, 0x82, 0x1E, 0x62, 0xE8,
+ 0x5D, 0x7C, 0xAE, 0xDF, 0x7B, 0xED, 0xA1, 0x2C,
+ 0x02, 0x48, 0x77, 0x5B, 0x0C, 0x88, 0x20, 0x5E,
+ 0xEB, 0x35, 0xAF, 0x3A, 0x90, 0x81, 0x6F, 0x66,
+ 0x08, 0xCE, 0x7D, 0xD4, 0x4E, 0xC2, 0x8D, 0xB1,
+ 0x14, 0x06, 0x14, 0xE1, 0xDD, 0xEB, 0xF3, 0xAA,
+ 0x9C, 0xD1, 0x84, 0x3E, 0x0F, 0xAD, 0x2C, 0x36
+ },
+ {
+ 0x8F, 0x94, 0x5B, 0xA7, 0x00, 0xF2, 0x53, 0x0E,
+ 0x5C, 0x2A, 0x7D, 0xF7, 0xD5, 0xDC, 0xE0, 0xF8,
+ 0x3F, 0x9E, 0xFC, 0x78, 0xC0, 0x73, 0xFE, 0x71,
+ 0xAE, 0x1F, 0x88, 0x20, 0x4A, 0x4F, 0xD1, 0xCF,
+ 0x70, 0xA0, 0x73, 0xF5, 0xD1, 0xF9, 0x42, 0xED,
+ 0x62, 0x3A, 0xA1, 0x6E, 0x90, 0xA8, 0x71, 0x24,
+ 0x6C, 0x90, 0xC4, 0x5B, 0x62, 0x1B, 0x34, 0x01,
+ 0xA5, 0xDD, 0xBD, 0x9D, 0xF6, 0x26, 0x41, 0x65
+ },
+ {
+ 0xE9, 0x98, 0xE0, 0xDC, 0x03, 0xEC, 0x30, 0xEB,
+ 0x99, 0xBB, 0x6B, 0xFA, 0xAF, 0x66, 0x18, 0xAC,
+ 0xC6, 0x20, 0x32, 0x0D, 0x72, 0x20, 0xB3, 0xAF,
+ 0x2B, 0x23, 0xD1, 0x12, 0xD8, 0xE9, 0xCB, 0x12,
+ 0x62, 0xF3, 0xC0, 0xD6, 0x0D, 0x18, 0x3B, 0x1E,
+ 0xE7, 0xF0, 0x96, 0xD1, 0x2D, 0xAE, 0x42, 0xC9,
+ 0x58, 0x41, 0x86, 0x00, 0x21, 0x4D, 0x04, 0xF5,
+ 0xED, 0x6F, 0x5E, 0x71, 0x8B, 0xE3, 0x55, 0x66
+ },
+ {
+ 0x6A, 0x9A, 0x09, 0x0C, 0x61, 0xB3, 0x41, 0x0A,
+ 0xED, 0xE7, 0xEC, 0x91, 0x38, 0x14, 0x6C, 0xEB,
+ 0x2C, 0x69, 0x66, 0x2F, 0x46, 0x0C, 0x3D, 0xA5,
+ 0x3C, 0x65, 0x15, 0xC1, 0xEB, 0x31, 0xF4, 0x1C,
+ 0xA3, 0xD2, 0x80, 0xE5, 0x67, 0x88, 0x2F, 0x95,
+ 0xCF, 0x66, 0x4A, 0x94, 0x14, 0x7D, 0x78, 0xF4,
+ 0x2C, 0xFC, 0x71, 0x4A, 0x40, 0xD2, 0x2E, 0xF1,
+ 0x94, 0x70, 0xE0, 0x53, 0x49, 0x35, 0x08, 0xA2
+ },
+ {
+ 0x29, 0x10, 0x25, 0x11, 0xD7, 0x49, 0xDB, 0x3C,
+ 0xC9, 0xB4, 0xE3, 0x35, 0xFA, 0x1F, 0x5E, 0x8F,
+ 0xAC, 0xA8, 0x42, 0x1D, 0x55, 0x8F, 0x6A, 0x3F,
+ 0x33, 0x21, 0xD5, 0x0D, 0x04, 0x4A, 0x24, 0x8B,
+ 0xA5, 0x95, 0xCF, 0xC3, 0xEF, 0xD3, 0xD2, 0xAD,
+ 0xC9, 0x73, 0x34, 0xDA, 0x73, 0x24, 0x13, 0xF5,
+ 0xCB, 0xF4, 0x75, 0x1C, 0x36, 0x2B, 0xA1, 0xD5,
+ 0x38, 0x62, 0xAC, 0x1E, 0x8D, 0xAB, 0xEE, 0xE8
+ },
+ {
+ 0xC9, 0x7A, 0x47, 0x79, 0xD4, 0x7E, 0x6F, 0x77,
+ 0x72, 0x9B, 0x59, 0x17, 0xD0, 0x13, 0x8A, 0xBB,
+ 0x35, 0x98, 0x0A, 0xB6, 0x41, 0xBD, 0x73, 0xA8,
+ 0x85, 0x9E, 0xB1, 0xAC, 0x98, 0xC0, 0x53, 0x62,
+ 0xED, 0x7D, 0x60, 0x8F, 0x2E, 0x95, 0x87, 0xD6,
+ 0xBA, 0x9E, 0x27, 0x1D, 0x34, 0x31, 0x25, 0xD4,
+ 0x0D, 0x93, 0x3A, 0x8E, 0xD0, 0x4E, 0xC1, 0xFE,
+ 0x75, 0xEC, 0x40, 0x7C, 0x7A, 0x53, 0xC3, 0x4E
+ },
+ {
+ 0x10, 0xF0, 0xDC, 0x91, 0xB9, 0xF8, 0x45, 0xFB,
+ 0x95, 0xFA, 0xD6, 0x86, 0x0E, 0x6C, 0xE1, 0xAD,
+ 0xFA, 0x00, 0x2C, 0x7F, 0xC3, 0x27, 0x11, 0x6D,
+ 0x44, 0xD0, 0x47, 0xCD, 0x7D, 0x58, 0x70, 0xD7,
+ 0x72, 0xBB, 0x12, 0xB5, 0xFA, 0xC0, 0x0E, 0x02,
+ 0xB0, 0x8A, 0xC2, 0xA0, 0x17, 0x4D, 0x04, 0x46,
+ 0xC3, 0x6A, 0xB3, 0x5F, 0x14, 0xCA, 0x31, 0x89,
+ 0x4C, 0xD6, 0x1C, 0x78, 0xC8, 0x49, 0xB4, 0x8A
+ },
+ {
+ 0xDE, 0xA9, 0x10, 0x1C, 0xAC, 0x62, 0xB8, 0xF6,
+ 0xA3, 0xC6, 0x50, 0xF9, 0x0E, 0xEA, 0x5B, 0xFA,
+ 0xE2, 0x65, 0x3A, 0x4E, 0xAF, 0xD6, 0x3A, 0x6D,
+ 0x1F, 0x0F, 0x13, 0x2D, 0xB9, 0xE4, 0xF2, 0xB1,
+ 0xB6, 0x62, 0x43, 0x2E, 0xC8, 0x5B, 0x17, 0xBC,
+ 0xAC, 0x41, 0xE7, 0x75, 0x63, 0x78, 0x81, 0xF6,
+ 0xAA, 0xB3, 0x8D, 0xD6, 0x6D, 0xCB, 0xD0, 0x80,
+ 0xF0, 0x99, 0x0A, 0x7A, 0x6E, 0x98, 0x54, 0xFE
+ },
+ {
+ 0x44, 0x1F, 0xFA, 0xA0, 0x8C, 0xD7, 0x9D, 0xFF,
+ 0x4A, 0xFC, 0x9B, 0x9E, 0x5B, 0x56, 0x20, 0xEE,
+ 0xC0, 0x86, 0x73, 0x0C, 0x25, 0xF6, 0x61, 0xB1,
+ 0xD6, 0xFB, 0xFB, 0xD1, 0xCE, 0xC3, 0x14, 0x8D,
+ 0xD7, 0x22, 0x58, 0xC6, 0x56, 0x41, 0xF2, 0xFC,
+ 0xA5, 0xEB, 0x15, 0x5F, 0xAD, 0xBC, 0xAB, 0xB1,
+ 0x3C, 0x6E, 0x21, 0xDC, 0x11, 0xFA, 0xF7, 0x2C,
+ 0x2A, 0x28, 0x1B, 0x7D, 0x56, 0x14, 0x5F, 0x19
+ },
+ {
+ 0x44, 0x4B, 0x24, 0x0F, 0xE3, 0xED, 0x86, 0xD0,
+ 0xE2, 0xEF, 0x4C, 0xE7, 0xD8, 0x51, 0xED, 0xDE,
+ 0x22, 0x15, 0x55, 0x82, 0xAA, 0x09, 0x14, 0x79,
+ 0x7B, 0x72, 0x6C, 0xD0, 0x58, 0xB6, 0xF4, 0x59,
+ 0x32, 0xE0, 0xE1, 0x29, 0x51, 0x68, 0x76, 0x52,
+ 0x7B, 0x1D, 0xD8, 0x8F, 0xC6, 0x6D, 0x71, 0x19,
+ 0xF4, 0xAB, 0x3B, 0xED, 0x93, 0xA6, 0x1A, 0x0E,
+ 0x2D, 0x2D, 0x2A, 0xEA, 0xC3, 0x36, 0xD9, 0x58
+ },
+ {
+ 0xBF, 0xBA, 0xBB, 0xEF, 0x45, 0x55, 0x4C, 0xCF,
+ 0xA0, 0xDC, 0x83, 0x75, 0x2A, 0x19, 0xCC, 0x35,
+ 0xD5, 0x92, 0x09, 0x56, 0xB3, 0x01, 0xD5, 0x58,
+ 0xD7, 0x72, 0x28, 0x2B, 0xC8, 0x67, 0x00, 0x91,
+ 0x68, 0xE9, 0xE9, 0x86, 0x06, 0xBB, 0x5B, 0xA7,
+ 0x3A, 0x38, 0x5D, 0xE5, 0x74, 0x92, 0x28, 0xC9,
+ 0x25, 0xA8, 0x50, 0x19, 0xB7, 0x1F, 0x72, 0xFE,
+ 0x29, 0xB3, 0xCD, 0x37, 0xCA, 0x52, 0xEF, 0xE6
+ },
+ {
+ 0x9C, 0x4D, 0x0C, 0x3E, 0x1C, 0xDB, 0xBF, 0x48,
+ 0x5B, 0xEC, 0x86, 0xF4, 0x1C, 0xEC, 0x7C, 0x98,
+ 0x37, 0x3F, 0x0E, 0x09, 0xF3, 0x92, 0x84, 0x9A,
+ 0xAA, 0x22, 0x9E, 0xBF, 0xBF, 0x39, 0x7B, 0x22,
+ 0x08, 0x55, 0x29, 0xCB, 0x7E, 0xF3, 0x9F, 0x9C,
+ 0x7C, 0x22, 0x22, 0xA5, 0x14, 0x18, 0x2B, 0x1E,
+ 0xFF, 0xAA, 0x17, 0x8C, 0xC3, 0x68, 0x7B, 0x1B,
+ 0x2B, 0x6C, 0xBC, 0xB6, 0xFD, 0xEB, 0x96, 0xF8
+ },
+ {
+ 0x47, 0x71, 0x76, 0xB3, 0xBF, 0xCB, 0xAD, 0xD7,
+ 0x65, 0x7C, 0x23, 0xC2, 0x46, 0x25, 0xE4, 0xD0,
+ 0xD6, 0x74, 0xD1, 0x86, 0x8F, 0x00, 0x60, 0x06,
+ 0x39, 0x8A, 0xF9, 0x7A, 0xA4, 0x18, 0x77, 0xC8,
+ 0xE7, 0x0D, 0x3D, 0x14, 0xC3, 0xBB, 0xC9, 0xBB,
+ 0xCD, 0xCE, 0xA8, 0x01, 0xBD, 0x0E, 0x15, 0x99,
+ 0xAF, 0x1F, 0x3E, 0xEC, 0x67, 0x40, 0x51, 0x70,
+ 0xF4, 0xE2, 0x6C, 0x96, 0x4A, 0x57, 0xA8, 0xB7
+ },
+ {
+ 0xA7, 0x8C, 0x49, 0x0E, 0xDA, 0x31, 0x73, 0xBB,
+ 0x3F, 0x10, 0xDE, 0xE5, 0x2F, 0x11, 0x0F, 0xB1,
+ 0xC0, 0x8E, 0x03, 0x02, 0x23, 0x0B, 0x85, 0xDD,
+ 0xD7, 0xC1, 0x12, 0x57, 0xD9, 0x2D, 0xE1, 0x48,
+ 0x78, 0x5E, 0xF0, 0x0C, 0x03, 0x9C, 0x0B, 0xB8,
+ 0xEB, 0x98, 0x08, 0xA3, 0x5B, 0x2D, 0x8C, 0x08,
+ 0x0F, 0x57, 0x28, 0x59, 0x71, 0x4C, 0x9D, 0x40,
+ 0x69, 0xC5, 0xBC, 0xAF, 0x09, 0x0E, 0x89, 0x8E
+ },
+ {
+ 0x58, 0xD0, 0x23, 0x39, 0x7B, 0xEB, 0x5B, 0x41,
+ 0x45, 0xCB, 0x22, 0x55, 0xB0, 0x7D, 0x74, 0x29,
+ 0x0B, 0x36, 0xD9, 0xFD, 0x1E, 0x59, 0x4A, 0xFB,
+ 0xD8, 0xEE, 0xA4, 0x7C, 0x20, 0x5B, 0x2E, 0xFB,
+ 0xFE, 0x6F, 0x46, 0x19, 0x0F, 0xAF, 0x95, 0xAF,
+ 0x50, 0x4A, 0xB0, 0x72, 0xE3, 0x6F, 0x6C, 0x85,
+ 0xD7, 0x67, 0xA3, 0x21, 0xBF, 0xD7, 0xF2, 0x26,
+ 0x87, 0xA4, 0xAB, 0xBF, 0x49, 0x4A, 0x68, 0x9C
+ },
+ {
+ 0x40, 0x01, 0xEC, 0x74, 0xD5, 0xA4, 0x6F, 0xD2,
+ 0x9C, 0x2C, 0x3C, 0xDB, 0xE5, 0xD1, 0xB9, 0xF2,
+ 0x0E, 0x51, 0xA9, 0x41, 0xBE, 0x98, 0xD2, 0xA4,
+ 0xE1, 0xE2, 0xFB, 0xF8, 0x66, 0xA6, 0x72, 0x12,
+ 0x1D, 0xB6, 0xF8, 0x1A, 0x51, 0x4C, 0xFD, 0x10,
+ 0xE7, 0x35, 0x8D, 0x57, 0x1B, 0xDB, 0xA4, 0x8E,
+ 0x4C, 0xE7, 0x08, 0xB9, 0xD1, 0x24, 0x89, 0x4B,
+ 0xC0, 0xB5, 0xED, 0x55, 0x49, 0x35, 0xF7, 0x3A
+ },
+ {
+ 0xCC, 0xD1, 0xB2, 0x2D, 0xAB, 0x65, 0x11, 0x22,
+ 0x5D, 0x24, 0x01, 0xEA, 0x2D, 0x86, 0x25, 0xD2,
+ 0x06, 0xA1, 0x24, 0x73, 0xCC, 0x73, 0x2B, 0x61,
+ 0x5E, 0x56, 0x40, 0xCE, 0xFF, 0xF0, 0xA4, 0xAD,
+ 0xF9, 0x71, 0xB0, 0xE8, 0x27, 0xA6, 0x19, 0xE0,
+ 0xA8, 0x0F, 0x5D, 0xB9, 0xCC, 0xD0, 0x96, 0x23,
+ 0x29, 0x01, 0x0D, 0x07, 0xE3, 0x4A, 0x20, 0x64,
+ 0xE7, 0x31, 0xC5, 0x20, 0x81, 0x7B, 0x21, 0x83
+ },
+ {
+ 0xB4, 0xA0, 0xA9, 0xE3, 0x57, 0x4E, 0xDB, 0x9E,
+ 0x1E, 0x72, 0xAA, 0x31, 0xE3, 0x9C, 0xC5, 0xF3,
+ 0x0D, 0xBF, 0x94, 0x3F, 0x8C, 0xAB, 0xC4, 0x08,
+ 0x44, 0x96, 0x54, 0xA3, 0x91, 0x31, 0xE6, 0x6D,
+ 0x71, 0x8A, 0x18, 0x81, 0x91, 0x43, 0xE3, 0xEA,
+ 0x96, 0xB4, 0xA1, 0x89, 0x59, 0x88, 0xA1, 0xC0,
+ 0x05, 0x6C, 0xF2, 0xB6, 0xE0, 0x4F, 0x9A, 0xC1,
+ 0x9D, 0x65, 0x73, 0x83, 0xC2, 0x91, 0x0C, 0x44
+ },
+ {
+ 0x44, 0x7B, 0xEC, 0xAB, 0x16, 0x63, 0x06, 0x08,
+ 0xD3, 0x9F, 0x4F, 0x05, 0x8B, 0x16, 0xF7, 0xAF,
+ 0x95, 0xB8, 0x5A, 0x76, 0xAA, 0x0F, 0xA7, 0xCE,
+ 0xA2, 0xB8, 0x07, 0x55, 0xFB, 0x76, 0xE9, 0xC8,
+ 0x04, 0xF2, 0xCA, 0x78, 0xF0, 0x26, 0x43, 0xC9,
+ 0x15, 0xFB, 0xF2, 0xFC, 0xE5, 0xE1, 0x9D, 0xE8,
+ 0x60, 0x00, 0xDE, 0x03, 0xB1, 0x88, 0x61, 0x81,
+ 0x5A, 0x83, 0x12, 0x60, 0x71, 0xF8, 0xA3, 0x7B
+ },
+ {
+ 0x54, 0xE6, 0xDA, 0xB9, 0x97, 0x73, 0x80, 0xA5,
+ 0x66, 0x58, 0x22, 0xDB, 0x93, 0x37, 0x4E, 0xDA,
+ 0x52, 0x8D, 0x9B, 0xEB, 0x62, 0x6F, 0x9B, 0x94,
+ 0x02, 0x70, 0x71, 0xCB, 0x26, 0x67, 0x5E, 0x11,
+ 0x2B, 0x4A, 0x7F, 0xEC, 0x94, 0x1E, 0xE6, 0x0A,
+ 0x81, 0xE4, 0xD2, 0xEA, 0x3F, 0xF7, 0xBC, 0x52,
+ 0xCF, 0xC4, 0x5D, 0xFB, 0xFE, 0x73, 0x5A, 0x1C,
+ 0x64, 0x6B, 0x2C, 0xF6, 0xD6, 0xA4, 0x9B, 0x62
+ },
+ {
+ 0x3E, 0xA6, 0x26, 0x25, 0x94, 0x9E, 0x36, 0x46,
+ 0x70, 0x4D, 0x7E, 0x3C, 0x90, 0x6F, 0x82, 0xF6,
+ 0xC0, 0x28, 0xF5, 0x40, 0xF5, 0xF7, 0x2A, 0x79,
+ 0x4B, 0x0C, 0x57, 0xBF, 0x97, 0xB7, 0x64, 0x9B,
+ 0xFE, 0xB9, 0x0B, 0x01, 0xD3, 0xCA, 0x3E, 0x82,
+ 0x9D, 0xE2, 0x1B, 0x38, 0x26, 0xE6, 0xF8, 0x70,
+ 0x14, 0xD3, 0xC7, 0x73, 0x50, 0xCB, 0x5A, 0x15,
+ 0xFF, 0x5D, 0x46, 0x8A, 0x81, 0xBE, 0xC1, 0x60
+ },
+ {
+ 0x21, 0x3C, 0xFE, 0x14, 0x5C, 0x54, 0xA3, 0x36,
+ 0x91, 0x56, 0x99, 0x80, 0xE5, 0x93, 0x8C, 0x88,
+ 0x83, 0xA4, 0x6D, 0x84, 0xD1, 0x49, 0xC8, 0xFF,
+ 0x1A, 0x67, 0xCD, 0x28, 0x7B, 0x4D, 0x49, 0xC6,
+ 0xDA, 0x69, 0xD3, 0xA0, 0x35, 0x44, 0x3D, 0xB0,
+ 0x85, 0x98, 0x3D, 0x0E, 0xFE, 0x63, 0x70, 0x6B,
+ 0xD5, 0xB6, 0xF1, 0x5A, 0x7D, 0xA4, 0x59, 0xE8,
+ 0xD5, 0x0A, 0x19, 0x09, 0x3D, 0xB5, 0x5E, 0x80
+ },
+ {
+ 0x57, 0x16, 0xC4, 0xA3, 0x8F, 0x38, 0xDB, 0x10,
+ 0x4E, 0x49, 0x4A, 0x0A, 0x27, 0xCB, 0xE8, 0x9A,
+ 0x26, 0xA6, 0xBB, 0x6F, 0x49, 0x9E, 0xC0, 0x1C,
+ 0x8C, 0x01, 0xAA, 0x7C, 0xB8, 0x84, 0x97, 0xE7,
+ 0x51, 0x48, 0xCD, 0x6E, 0xEE, 0x12, 0xA7, 0x16,
+ 0x8B, 0x6F, 0x78, 0xAB, 0x74, 0xE4, 0xBE, 0x74,
+ 0x92, 0x51, 0xA1, 0xA7, 0x4C, 0x38, 0xC8, 0x6D,
+ 0x61, 0x29, 0x17, 0x7E, 0x28, 0x89, 0xE0, 0xB6
+ },
+ {
+ 0x03, 0x04, 0x60, 0xA9, 0x8B, 0xDF, 0x9F, 0xF1,
+ 0x7C, 0xD9, 0x64, 0x04, 0xF2, 0x8F, 0xC3, 0x04,
+ 0xF2, 0xB7, 0xC0, 0x4E, 0xAA, 0xDE, 0x53, 0x67,
+ 0x7F, 0xD2, 0x8F, 0x78, 0x8C, 0xA2, 0x21, 0x86,
+ 0xB8, 0xBC, 0x80, 0xDD, 0x21, 0xD1, 0x7F, 0x85,
+ 0x49, 0xC7, 0x11, 0xAF, 0xF0, 0xE5, 0x14, 0xE1,
+ 0x9D, 0x4E, 0x15, 0xF5, 0x99, 0x02, 0x52, 0xA0,
+ 0x3E, 0x08, 0x2F, 0x28, 0xDC, 0x20, 0x52, 0xF6
+ },
+ {
+ 0x19, 0xE7, 0xF1, 0xCC, 0xEE, 0x88, 0xA1, 0x06,
+ 0x72, 0x33, 0x3E, 0x39, 0x0C, 0xF2, 0x20, 0x13,
+ 0xA8, 0xC7, 0x34, 0xC6, 0xCB, 0x9E, 0xAB, 0x41,
+ 0xF1, 0x7C, 0x3C, 0x80, 0x32, 0xA2, 0xE4, 0xAC,
+ 0xA0, 0x56, 0x9E, 0xA3, 0x6F, 0x08, 0x60, 0xC7,
+ 0xA1, 0xAF, 0x28, 0xFA, 0x47, 0x68, 0x40, 0xD6,
+ 0x60, 0x11, 0x16, 0x88, 0x59, 0x33, 0x4A, 0x9E,
+ 0x4E, 0xF9, 0xCC, 0x2E, 0x61, 0xA0, 0xE2, 0x9E
+ },
+ {
+ 0x29, 0xF8, 0xB8, 0xC7, 0x8C, 0x80, 0xF2, 0xFC,
+ 0xB4, 0xBD, 0xF7, 0x82, 0x5E, 0xD9, 0x0A, 0x70,
+ 0xD6, 0x25, 0xFF, 0x78, 0x5D, 0x26, 0x26, 0x77,
+ 0xE2, 0x50, 0xC0, 0x4F, 0x37, 0x20, 0xC8, 0x88,
+ 0xD0, 0x3F, 0x80, 0x45, 0xE4, 0xED, 0xF3, 0xF5,
+ 0x28, 0x5B, 0xD3, 0x9D, 0x92, 0x8A, 0x10, 0xA7,
+ 0xD0, 0xA5, 0xDF, 0x00, 0xB8, 0x48, 0x4A, 0xC2,
+ 0x86, 0x81, 0x42, 0xA1, 0xE8, 0xBE, 0xA3, 0x51
+ },
+ {
+ 0x5C, 0x52, 0x92, 0x0A, 0x72, 0x63, 0xE3, 0x9D,
+ 0x57, 0x92, 0x0C, 0xA0, 0xCB, 0x75, 0x2A, 0xC6,
+ 0xD7, 0x9A, 0x04, 0xFE, 0xF8, 0xA7, 0xA2, 0x16,
+ 0xA1, 0xEC, 0xB7, 0x11, 0x5C, 0xE0, 0x6D, 0x89,
+ 0xFD, 0x7D, 0x73, 0x5B, 0xD6, 0xF4, 0x27, 0x25,
+ 0x55, 0xDB, 0xA2, 0x2C, 0x2D, 0x1C, 0x96, 0xE6,
+ 0x35, 0x23, 0x22, 0xC6, 0x2C, 0x56, 0x30, 0xFD,
+ 0xE0, 0xF4, 0x77, 0x7A, 0x76, 0xC3, 0xDE, 0x2C
+ },
+ {
+ 0x83, 0xB0, 0x98, 0xF2, 0x62, 0x25, 0x1B, 0xF6,
+ 0x60, 0x06, 0x4A, 0x9D, 0x35, 0x11, 0xCE, 0x76,
+ 0x87, 0xA0, 0x9E, 0x6D, 0xFB, 0xB8, 0x78, 0x29,
+ 0x9C, 0x30, 0xE9, 0x3D, 0xFB, 0x43, 0xA9, 0x31,
+ 0x4D, 0xB9, 0xA6, 0x00, 0x33, 0x7D, 0xB2, 0x6E,
+ 0xBE, 0xED, 0xAF, 0x22, 0x56, 0xA9, 0x6D, 0xAB,
+ 0xE9, 0xB2, 0x9E, 0x75, 0x73, 0xAD, 0x11, 0xC3,
+ 0x52, 0x3D, 0x87, 0x4D, 0xDE, 0x5B, 0xE7, 0xED
+ },
+ {
+ 0x94, 0x47, 0xD9, 0x8A, 0xA5, 0xC9, 0x33, 0x13,
+ 0x52, 0xF4, 0x3D, 0x3E, 0x56, 0xD0, 0xA9, 0xA9,
+ 0xF9, 0x58, 0x18, 0x65, 0x99, 0x8E, 0x28, 0x85,
+ 0xCC, 0x56, 0xDD, 0x0A, 0x0B, 0xD5, 0xA7, 0xB5,
+ 0x05, 0x95, 0xBD, 0x10, 0xF7, 0x52, 0x9B, 0xCD,
+ 0x31, 0xF3, 0x7D, 0xC1, 0x6A, 0x14, 0x65, 0xD5,
+ 0x94, 0x07, 0x96, 0x67, 0xDA, 0x2A, 0x3F, 0xCB,
+ 0x70, 0x40, 0x14, 0x98, 0x83, 0x7C, 0xED, 0xEB
+ },
+ {
+ 0x86, 0x77, 0x32, 0xF2, 0xFE, 0xEB, 0x23, 0x89,
+ 0x30, 0x97, 0x56, 0x1A, 0xC7, 0x10, 0xA4, 0xBF,
+ 0xF4, 0x53, 0xBE, 0x9C, 0xFB, 0xED, 0xBA, 0x8B,
+ 0xA3, 0x24, 0xF9, 0xD3, 0x12, 0xA8, 0x2D, 0x73,
+ 0x2E, 0x1B, 0x83, 0xB8, 0x29, 0xFD, 0xCD, 0x17,
+ 0x7B, 0x88, 0x2C, 0xA0, 0xC1, 0xBF, 0x54, 0x4B,
+ 0x22, 0x3B, 0xE5, 0x29, 0x92, 0x4A, 0x24, 0x6A,
+ 0x63, 0xCF, 0x05, 0x9B, 0xFD, 0xC5, 0x0A, 0x1B
+ },
+ {
+ 0xF1, 0x5A, 0xB2, 0x6D, 0x4C, 0xDF, 0xCF, 0x56,
+ 0xE1, 0x96, 0xBB, 0x6B, 0xA1, 0x70, 0xA8, 0xFC,
+ 0xCC, 0x41, 0x4D, 0xE9, 0x28, 0x5A, 0xFD, 0x98,
+ 0xA3, 0xD3, 0xCF, 0x2F, 0xB8, 0x8F, 0xCB, 0xC0,
+ 0xF1, 0x98, 0x32, 0xAC, 0x43, 0x3A, 0x5B, 0x2C,
+ 0xC2, 0x39, 0x2A, 0x4C, 0xE3, 0x43, 0x32, 0x98,
+ 0x7D, 0x8D, 0x2C, 0x2B, 0xEF, 0x6C, 0x34, 0x66,
+ 0x13, 0x8D, 0xB0, 0xC6, 0xE4, 0x2F, 0xA4, 0x7B
+ },
+ {
+ 0x28, 0x13, 0x51, 0x6D, 0x68, 0xED, 0x4A, 0x08,
+ 0xB3, 0x9D, 0x64, 0x8A, 0xA6, 0xAA, 0xCD, 0x81,
+ 0xE9, 0xD6, 0x55, 0xEC, 0xD5, 0xF0, 0xC1, 0x35,
+ 0x56, 0xC6, 0x0F, 0xDF, 0x0D, 0x33, 0x3E, 0xA3,
+ 0x84, 0x64, 0xB3, 0x6C, 0x02, 0xBA, 0xCC, 0xD7,
+ 0x46, 0xE9, 0x57, 0x5E, 0x96, 0xC6, 0x30, 0x14,
+ 0xF0, 0x74, 0xAE, 0x34, 0xA0, 0xA2, 0x5B, 0x32,
+ 0x0F, 0x0F, 0xBE, 0xDD, 0x6A, 0xCF, 0x76, 0x65
+ },
+ {
+ 0xD3, 0x25, 0x9A, 0xFC, 0xA8, 0xA4, 0x89, 0x62,
+ 0xFA, 0x89, 0x2E, 0x14, 0x5A, 0xCF, 0x54, 0x7F,
+ 0x26, 0x92, 0x3A, 0xE8, 0xD4, 0x92, 0x4C, 0x8A,
+ 0x53, 0x15, 0x81, 0x52, 0x6B, 0x04, 0xB4, 0x4C,
+ 0x7A, 0xF8, 0x3C, 0x64, 0x3E, 0xF5, 0xA0, 0xBC,
+ 0x28, 0x2D, 0x36, 0xF3, 0xFB, 0x04, 0xC8, 0x4E,
+ 0x28, 0xB3, 0x51, 0xF4, 0x0C, 0x74, 0xB6, 0x9D,
+ 0xC7, 0x84, 0x0B, 0xC7, 0x17, 0xB6, 0xF1, 0x5F
+ },
+ {
+ 0xF1, 0x4B, 0x06, 0x1A, 0xE3, 0x59, 0xFA, 0x31,
+ 0xB9, 0x89, 0xE3, 0x03, 0x32, 0xBF, 0xE8, 0xDE,
+ 0x8C, 0xC8, 0xCD, 0xB5, 0x68, 0xE1, 0x4B, 0xE2,
+ 0x14, 0xA2, 0x22, 0x3B, 0x84, 0xCA, 0xAB, 0x74,
+ 0x19, 0x54, 0x9E, 0xCF, 0xCC, 0x96, 0xCE, 0x2A,
+ 0xCE, 0xC1, 0x19, 0x48, 0x5D, 0x87, 0xD1, 0x57,
+ 0xD3, 0xA8, 0x73, 0x4F, 0xC4, 0x26, 0x59, 0x7D,
+ 0x64, 0xF3, 0x65, 0x70, 0xCE, 0xAF, 0x22, 0x4D
+ },
+ {
+ 0x55, 0xE7, 0x0B, 0x01, 0xD1, 0xFB, 0xF8, 0xB2,
+ 0x3B, 0x57, 0xFB, 0x62, 0xE2, 0x6C, 0x2C, 0xE5,
+ 0x4F, 0x13, 0xF8, 0xFA, 0x24, 0x64, 0xE6, 0xEB,
+ 0x98, 0xD1, 0x6A, 0x61, 0x17, 0x02, 0x6D, 0x8B,
+ 0x90, 0x81, 0x90, 0x12, 0x49, 0x6D, 0x40, 0x71,
+ 0xEB, 0xE2, 0xE5, 0x95, 0x57, 0xEC, 0xE3, 0x51,
+ 0x9A, 0x7A, 0xA4, 0x58, 0x02, 0xF9, 0x61, 0x53,
+ 0x74, 0x87, 0x73, 0x32, 0xB7, 0x34, 0x90, 0xB3
+ },
+ {
+ 0x25, 0x26, 0x1E, 0xB2, 0x96, 0x97, 0x1D, 0x6E,
+ 0x4A, 0x71, 0xB2, 0x92, 0x8E, 0x64, 0x83, 0x9C,
+ 0x67, 0xD4, 0x22, 0x87, 0x2B, 0xF9, 0xF3, 0xC3,
+ 0x19, 0x93, 0x61, 0x52, 0x22, 0xDE, 0x9F, 0x8F,
+ 0x0B, 0x2C, 0x4B, 0xE8, 0x54, 0x85, 0x59, 0xB4,
+ 0xB3, 0x54, 0xE7, 0x36, 0x41, 0x6E, 0x32, 0x18,
+ 0xD4, 0xE8, 0xA1, 0xE2, 0x19, 0xA4, 0xA6, 0xD4,
+ 0x3E, 0x1A, 0x9A, 0x52, 0x1D, 0x0E, 0x75, 0xFC
+ },
+ {
+ 0x08, 0x30, 0x7F, 0x34, 0x7C, 0x41, 0x29, 0x4E,
+ 0x34, 0xBB, 0x54, 0xCB, 0x42, 0xB1, 0x52, 0x2D,
+ 0x22, 0xF8, 0x24, 0xF7, 0xB6, 0xE5, 0xDB, 0x50,
+ 0xFD, 0xA0, 0x96, 0x79, 0x8E, 0x18, 0x1A, 0x8F,
+ 0x02, 0x6F, 0xA2, 0x7B, 0x4A, 0xE4, 0x5D, 0x52,
+ 0xA6, 0x2C, 0xAF, 0x9D, 0x51, 0x98, 0xE2, 0x4A,
+ 0x49, 0x13, 0xC6, 0x67, 0x17, 0x75, 0xB2, 0xD7,
+ 0x23, 0xC1, 0x23, 0x9B, 0xFB, 0xF0, 0x16, 0xD7
+ },
+ {
+ 0x1E, 0x5C, 0x62, 0xE7, 0xE9, 0xBF, 0xA1, 0xB1,
+ 0x18, 0x74, 0x7A, 0x2D, 0xE0, 0x8B, 0x3C, 0xA1,
+ 0x01, 0x12, 0xAF, 0x96, 0xA4, 0x6E, 0x4B, 0x22,
+ 0xC3, 0xFC, 0x06, 0xF9, 0xBF, 0xEE, 0x4E, 0xB5,
+ 0xC4, 0x9E, 0x05, 0x7A, 0x4A, 0x48, 0x86, 0x23,
+ 0x43, 0x24, 0x57, 0x25, 0x76, 0xBB, 0x9B, 0x5E,
+ 0xCF, 0xDE, 0x0D, 0x99, 0xB0, 0xDE, 0x4F, 0x98,
+ 0xEC, 0x16, 0xE4, 0xD1, 0xB8, 0x5F, 0xA9, 0x47
+ },
+ {
+ 0xC7, 0x4A, 0x77, 0x39, 0x5F, 0xB8, 0xBC, 0x12,
+ 0x64, 0x47, 0x45, 0x48, 0x38, 0xE5, 0x61, 0xE9,
+ 0x62, 0x85, 0x3D, 0xC7, 0xEB, 0x49, 0xA1, 0xE3,
+ 0xCB, 0x67, 0xC3, 0xD0, 0x85, 0x1F, 0x3E, 0x39,
+ 0x51, 0x7B, 0xE8, 0xC3, 0x50, 0xAC, 0x91, 0x09,
+ 0x03, 0xD4, 0x9C, 0xD2, 0xBF, 0xDF, 0x54, 0x5C,
+ 0x99, 0x31, 0x6D, 0x03, 0x46, 0x17, 0x0B, 0x73,
+ 0x9F, 0x0A, 0xDD, 0x5D, 0x53, 0x3C, 0x2C, 0xFC
+ },
+ {
+ 0x0D, 0xD5, 0x7B, 0x42, 0x3C, 0xC0, 0x1E, 0xB2,
+ 0x86, 0x13, 0x91, 0xEB, 0x88, 0x6A, 0x0D, 0x17,
+ 0x07, 0x9B, 0x93, 0x3F, 0xC7, 0x6E, 0xB3, 0xFC,
+ 0x08, 0xA1, 0x9F, 0x8A, 0x74, 0x95, 0x2C, 0xB6,
+ 0x8F, 0x6B, 0xCD, 0xC6, 0x44, 0xF7, 0x73, 0x70,
+ 0x96, 0x6E, 0x4D, 0x13, 0xE8, 0x05, 0x60, 0xBC,
+ 0xF0, 0x82, 0xEF, 0x04, 0x79, 0xD4, 0x8F, 0xBB,
+ 0xAB, 0x4D, 0xF0, 0x3B, 0x53, 0xA4, 0xE1, 0x78
+ },
+ {
+ 0x4D, 0x8D, 0xC3, 0x92, 0x3E, 0xDC, 0xCD, 0xFC,
+ 0xE7, 0x00, 0x72, 0x39, 0x8B, 0x8A, 0x3D, 0xA5,
+ 0xC3, 0x1F, 0xCB, 0x3E, 0xE3, 0xB6, 0x45, 0xC8,
+ 0x5F, 0x71, 0x7C, 0xBA, 0xEB, 0x4B, 0x67, 0x3A,
+ 0x19, 0x39, 0x44, 0x25, 0xA5, 0x85, 0xBF, 0xB4,
+ 0x64, 0xD9, 0x2F, 0x15, 0x97, 0xD0, 0xB7, 0x54,
+ 0xD1, 0x63, 0xF9, 0x7C, 0xED, 0x34, 0x3B, 0x25,
+ 0xDB, 0x5A, 0x70, 0xEF, 0x48, 0xEB, 0xB3, 0x4F
+ },
+ {
+ 0xF0, 0xA5, 0x05, 0x53, 0xE4, 0xDF, 0xB0, 0xC4,
+ 0xE3, 0xE3, 0xD3, 0xBA, 0x82, 0x03, 0x48, 0x57,
+ 0xE3, 0xB1, 0xE5, 0x09, 0x18, 0xF5, 0xB8, 0xA7,
+ 0xD6, 0x98, 0xE1, 0x0D, 0x24, 0x2B, 0x0F, 0xB5,
+ 0x44, 0xAF, 0x6C, 0x92, 0xD0, 0xC3, 0xAA, 0xF9,
+ 0x93, 0x22, 0x20, 0x41, 0x61, 0x17, 0xB4, 0xE7,
+ 0x8E, 0xCB, 0x8A, 0x8F, 0x43, 0x0E, 0x13, 0xB8,
+ 0x2A, 0x59, 0x15, 0x29, 0x0A, 0x58, 0x19, 0xC5
+ },
+ {
+ 0xB1, 0x55, 0x43, 0xF3, 0xF7, 0x36, 0x08, 0x66,
+ 0x27, 0xCC, 0x53, 0x65, 0xE7, 0xE8, 0x98, 0x8C,
+ 0x2E, 0xF1, 0x55, 0xC0, 0xFD, 0x4F, 0x42, 0x89,
+ 0x61, 0xB0, 0x0D, 0x15, 0x26, 0xF0, 0x4D, 0x6D,
+ 0x6A, 0x65, 0x8B, 0x4B, 0x8E, 0xD3, 0x2C, 0x5D,
+ 0x86, 0x21, 0xE7, 0xF4, 0xF8, 0xE8, 0xA9, 0x33,
+ 0xD9, 0xEC, 0xC9, 0xDD, 0x1B, 0x83, 0x33, 0xCB,
+ 0xE2, 0x8C, 0xFC, 0x37, 0xD9, 0x71, 0x9E, 0x1C
+ },
+ {
+ 0x7B, 0x4F, 0xA1, 0x58, 0xE4, 0x15, 0xFE, 0xF0,
+ 0x23, 0x24, 0x72, 0x64, 0xCB, 0xBE, 0x15, 0xD1,
+ 0x6D, 0x91, 0xA4, 0x44, 0x24, 0xA8, 0xDB, 0x70,
+ 0x7E, 0xB1, 0xE2, 0x03, 0x3C, 0x30, 0xE9, 0xE1,
+ 0xE7, 0xC8, 0xC0, 0x86, 0x45, 0x95, 0xD2, 0xCB,
+ 0x8C, 0x58, 0x0E, 0xB4, 0x7E, 0x9D, 0x16, 0xAB,
+ 0xBD, 0x7E, 0x44, 0xE8, 0x24, 0xF7, 0xCE, 0xDB,
+ 0x7D, 0xEF, 0x57, 0x13, 0x0E, 0x52, 0xCF, 0xE9
+ },
+ {
+ 0x60, 0x42, 0x4F, 0xF2, 0x32, 0x34, 0xC3, 0x4D,
+ 0xC9, 0x68, 0x7A, 0xD5, 0x02, 0x86, 0x93, 0x72,
+ 0xCC, 0x31, 0xA5, 0x93, 0x80, 0x18, 0x6B, 0xC2,
+ 0x36, 0x1C, 0x83, 0x5D, 0x97, 0x2F, 0x49, 0x66,
+ 0x6E, 0xB1, 0xAC, 0x69, 0x62, 0x9D, 0xE6, 0x46,
+ 0xF0, 0x3F, 0x9B, 0x4D, 0xB9, 0xE2, 0xAC, 0xE0,
+ 0x93, 0xFB, 0xFD, 0xF8, 0xF2, 0x0A, 0xB5, 0xF9,
+ 0x85, 0x41, 0x97, 0x8B, 0xE8, 0xEF, 0x54, 0x9F
+ },
+ {
+ 0x74, 0x06, 0x01, 0x8C, 0xE7, 0x04, 0xD8, 0x4F,
+ 0x5E, 0xB9, 0xC7, 0x9F, 0xEA, 0x97, 0xDA, 0x34,
+ 0x56, 0x99, 0x46, 0x8A, 0x35, 0x0E, 0xE0, 0xB2,
+ 0xD0, 0xF3, 0xA4, 0xBF, 0x20, 0x70, 0x30, 0x4E,
+ 0xA8, 0x62, 0xD7, 0x2A, 0x51, 0xC5, 0x7D, 0x30,
+ 0x64, 0x94, 0x72, 0x86, 0xF5, 0x31, 0xE0, 0xEA,
+ 0xF7, 0x56, 0x37, 0x02, 0x26, 0x2E, 0x6C, 0x72,
+ 0x4A, 0xBF, 0x5E, 0xD8, 0xC8, 0x39, 0x8D, 0x17
+ },
+ {
+ 0x14, 0xEF, 0x5C, 0x6D, 0x64, 0x7B, 0x3B, 0xD1,
+ 0xE6, 0xE3, 0x20, 0x06, 0xC2, 0x31, 0x19, 0x98,
+ 0x10, 0xDE, 0x5C, 0x4D, 0xC8, 0x8E, 0x70, 0x24,
+ 0x02, 0x73, 0xB0, 0xEA, 0x18, 0xE6, 0x51, 0xA3,
+ 0xEB, 0x4F, 0x5C, 0xA3, 0x11, 0x4B, 0x8A, 0x56,
+ 0x71, 0x69, 0x69, 0xC7, 0xCD, 0xA2, 0x7E, 0x0C,
+ 0x8D, 0xB8, 0x32, 0xAD, 0x5E, 0x89, 0xA2, 0xDC,
+ 0x6C, 0xB0, 0xAD, 0xBE, 0x7D, 0x93, 0xAB, 0xD1
+ },
+ {
+ 0x38, 0xCF, 0x6C, 0x24, 0xE3, 0xE0, 0x8B, 0xCF,
+ 0x1F, 0x6C, 0xF3, 0xD1, 0xB1, 0xF6, 0x5B, 0x90,
+ 0x52, 0x39, 0xA3, 0x11, 0x80, 0x33, 0x24, 0x9E,
+ 0x44, 0x81, 0x13, 0xEC, 0x63, 0x2E, 0xA6, 0xDC,
+ 0x34, 0x6F, 0xEE, 0xB2, 0x57, 0x1C, 0x38, 0xBD,
+ 0x9A, 0x73, 0x98, 0xB2, 0x22, 0x12, 0x80, 0x32,
+ 0x80, 0x02, 0xB2, 0x3E, 0x1A, 0x45, 0xAD, 0xAF,
+ 0xFE, 0x66, 0xD9, 0x3F, 0x65, 0x64, 0xEA, 0xA2
+ },
+ {
+ 0x6C, 0xD7, 0x20, 0x8A, 0x4B, 0xC7, 0xE7, 0xE5,
+ 0x62, 0x01, 0xBB, 0xBA, 0x02, 0xA0, 0xF4, 0x89,
+ 0xCD, 0x38, 0x4A, 0xBE, 0x40, 0xAF, 0xD4, 0x22,
+ 0x2F, 0x15, 0x8B, 0x3D, 0x98, 0x6E, 0xE7, 0x2A,
+ 0x54, 0xC5, 0x0F, 0xB6, 0x4F, 0xD4, 0xED, 0x25,
+ 0x30, 0xED, 0xA2, 0xC8, 0xAF, 0x29, 0x28, 0xA0,
+ 0xDA, 0x6D, 0x4F, 0x83, 0x0A, 0xE1, 0xC9, 0xDB,
+ 0x46, 0x9D, 0xFD, 0x97, 0x0F, 0x12, 0xA5, 0x6F
+ },
+ {
+ 0x65, 0x98, 0x58, 0xF0, 0xB5, 0xC9, 0xED, 0xAB,
+ 0x5B, 0x94, 0xFD, 0x73, 0x2F, 0x6E, 0x6B, 0x17,
+ 0xC5, 0x1C, 0xC0, 0x96, 0x10, 0x4F, 0x09, 0xBE,
+ 0xB3, 0xAF, 0xC3, 0xAA, 0x46, 0x7C, 0x2E, 0xCF,
+ 0x88, 0x5C, 0x4C, 0x65, 0x41, 0xEF, 0xFA, 0x90,
+ 0x23, 0xD3, 0xB5, 0x73, 0x8A, 0xE5, 0xA1, 0x4D,
+ 0x86, 0x7E, 0x15, 0xDB, 0x06, 0xFE, 0x1F, 0x9D,
+ 0x11, 0x27, 0xB7, 0x7E, 0x1A, 0xAB, 0xB5, 0x16
+ },
+ {
+ 0x26, 0xCC, 0xA0, 0x12, 0x6F, 0x5D, 0x1A, 0x81,
+ 0x3C, 0x62, 0xE5, 0xC7, 0x10, 0x01, 0xC0, 0x46,
+ 0xF9, 0xC9, 0x20, 0x95, 0x70, 0x45, 0x50, 0xBE,
+ 0x58, 0x73, 0xA4, 0x95, 0xA9, 0x99, 0xAD, 0x01,
+ 0x0A, 0x4F, 0x79, 0x49, 0x1F, 0x24, 0xF2, 0x86,
+ 0x50, 0x0A, 0xDC, 0xE1, 0xA1, 0x37, 0xBC, 0x20,
+ 0x84, 0xE4, 0x94, 0x9F, 0x5B, 0x72, 0x94, 0xCE,
+ 0xFE, 0x51, 0xEC, 0xAF, 0xF8, 0xE9, 0x5C, 0xBA
+ },
+ {
+ 0x41, 0x47, 0xC1, 0xF5, 0x51, 0x72, 0x78, 0x8C,
+ 0x55, 0x67, 0xC5, 0x61, 0xFE, 0xEF, 0x87, 0x6F,
+ 0x62, 0x1F, 0xFF, 0x1C, 0xE8, 0x77, 0x86, 0xB8,
+ 0x46, 0x76, 0x37, 0xE7, 0x0D, 0xFB, 0xCD, 0x0D,
+ 0xBD, 0xB6, 0x41, 0x5C, 0xB6, 0x00, 0x95, 0x4A,
+ 0xB9, 0xC0, 0x4C, 0x0E, 0x45, 0x7E, 0x62, 0x5B,
+ 0x40, 0x72, 0x22, 0xC0, 0xFE, 0x1A, 0xE2, 0x1B,
+ 0x21, 0x43, 0x68, 0x8A, 0xDA, 0x94, 0xDC, 0x58
+ },
+ {
+ 0x5B, 0x1B, 0xF1, 0x54, 0xC6, 0x2A, 0x8A, 0xF6,
+ 0xE9, 0x3D, 0x35, 0xF1, 0x8F, 0x7F, 0x90, 0xAB,
+ 0xB1, 0x6A, 0x6E, 0xF0, 0xE8, 0xD1, 0xAE, 0xCD,
+ 0x11, 0x8B, 0xF7, 0x01, 0x67, 0xBA, 0xB2, 0xAF,
+ 0x08, 0x93, 0x5C, 0x6F, 0xDC, 0x06, 0x63, 0xCE,
+ 0x74, 0x48, 0x2D, 0x17, 0xA8, 0xE5, 0x4B, 0x54,
+ 0x6D, 0x1C, 0x29, 0x66, 0x31, 0xC6, 0x5F, 0x3B,
+ 0x52, 0x2A, 0x51, 0x58, 0x39, 0xD4, 0x3D, 0x71
+ },
+ {
+ 0x9F, 0x60, 0x04, 0x19, 0xA4, 0xE8, 0xF4, 0xFB,
+ 0x83, 0x4C, 0x24, 0xB0, 0xF7, 0xFC, 0x13, 0xBF,
+ 0x4E, 0x27, 0x9D, 0x98, 0xE8, 0xA3, 0xC7, 0x65,
+ 0xEE, 0x93, 0x49, 0x17, 0x40, 0x3E, 0x3A, 0x66,
+ 0x09, 0x71, 0x82, 0xEA, 0x21, 0x45, 0x3C, 0xB6,
+ 0x3E, 0xBB, 0xE8, 0xB7, 0x3A, 0x9C, 0x21, 0x67,
+ 0x59, 0x64, 0x46, 0x43, 0x8C, 0x57, 0x62, 0x7F,
+ 0x33, 0x0B, 0xAD, 0xD4, 0xF5, 0x69, 0xF7, 0xD6
+ },
+ {
+ 0x45, 0x7E, 0xF6, 0x46, 0x6A, 0x89, 0x24, 0xFD,
+ 0x80, 0x11, 0xA3, 0x44, 0x71, 0xA5, 0xA1, 0xAC,
+ 0x8C, 0xCD, 0x9B, 0xD0, 0xD0, 0x7A, 0x97, 0x41,
+ 0x4A, 0xC9, 0x43, 0x02, 0x1C, 0xE4, 0xB9, 0xE4,
+ 0xB9, 0xC8, 0xDB, 0x0A, 0x28, 0xF0, 0x16, 0xED,
+ 0x43, 0xB1, 0x54, 0x24, 0x81, 0x99, 0x00, 0x22,
+ 0x14, 0x7B, 0x31, 0x3E, 0x19, 0x46, 0x71, 0x13,
+ 0x1E, 0x70, 0x8D, 0xD4, 0x3A, 0x3E, 0xD7, 0xDC
+ },
+ {
+ 0x99, 0x97, 0xB2, 0x19, 0x4D, 0x9A, 0xF6, 0xDF,
+ 0xCB, 0x91, 0x43, 0xF4, 0x1C, 0x0E, 0xD8, 0x3D,
+ 0x3A, 0x3F, 0x43, 0x88, 0x36, 0x11, 0x03, 0xD3,
+ 0x8C, 0x2A, 0x49, 0xB2, 0x80, 0xA5, 0x81, 0x21,
+ 0x27, 0x15, 0xFD, 0x90, 0x8D, 0x41, 0xC6, 0x51,
+ 0xF5, 0xC7, 0x15, 0xCA, 0x38, 0xC0, 0xCE, 0x28,
+ 0x30, 0xA3, 0x7E, 0x00, 0xE5, 0x08, 0xCE, 0xD1,
+ 0xBC, 0xDC, 0x32, 0x0E, 0x5E, 0x4D, 0x1E, 0x2E
+ },
+ {
+ 0x5C, 0x6B, 0xBF, 0x16, 0xBA, 0xA1, 0x80, 0xF9,
+ 0x86, 0xBD, 0x40, 0xA1, 0x28, 0x7E, 0xD4, 0xC5,
+ 0x49, 0x77, 0x0E, 0x72, 0x84, 0x85, 0x8F, 0xC4,
+ 0x7B, 0xC2, 0x1A, 0xB9, 0x5E, 0xBB, 0xF3, 0x37,
+ 0x4B, 0x4E, 0xE3, 0xFD, 0x9F, 0x2A, 0xF6, 0x0F,
+ 0x33, 0x95, 0x22, 0x1B, 0x2A, 0xCC, 0x76, 0xF2,
+ 0xD3, 0x4C, 0x13, 0x29, 0x54, 0x04, 0x9F, 0x8A,
+ 0x3A, 0x99, 0x6F, 0x1E, 0x32, 0xEC, 0x84, 0xE5
+ },
+ {
+ 0xD1, 0x0B, 0xF9, 0xA1, 0x5B, 0x1C, 0x9F, 0xC8,
+ 0xD4, 0x1F, 0x89, 0xBB, 0x14, 0x0B, 0xF0, 0xBE,
+ 0x08, 0xD2, 0xF3, 0x66, 0x61, 0x76, 0xD1, 0x3B,
+ 0xAA, 0xC4, 0xD3, 0x81, 0x35, 0x8A, 0xD0, 0x74,
+ 0xC9, 0xD4, 0x74, 0x8C, 0x30, 0x05, 0x20, 0xEB,
+ 0x02, 0x6D, 0xAE, 0xAE, 0xA7, 0xC5, 0xB1, 0x58,
+ 0x89, 0x2F, 0xDE, 0x4E, 0x8E, 0xC1, 0x7D, 0xC9,
+ 0x98, 0xDC, 0xD5, 0x07, 0xDF, 0x26, 0xEB, 0x63
+ },
+ {
+ 0x2F, 0xC6, 0xE6, 0x9F, 0xA2, 0x6A, 0x89, 0xA5,
+ 0xED, 0x26, 0x90, 0x92, 0xCB, 0x9B, 0x2A, 0x44,
+ 0x9A, 0x44, 0x09, 0xA7, 0xA4, 0x40, 0x11, 0xEE,
+ 0xCA, 0xD1, 0x3D, 0x7C, 0x4B, 0x04, 0x56, 0x60,
+ 0x2D, 0x40, 0x2F, 0xA5, 0x84, 0x4F, 0x1A, 0x7A,
+ 0x75, 0x81, 0x36, 0xCE, 0x3D, 0x5D, 0x8D, 0x0E,
+ 0x8B, 0x86, 0x92, 0x1F, 0xFF, 0xF4, 0xF6, 0x92,
+ 0xDD, 0x95, 0xBD, 0xC8, 0xE5, 0xFF, 0x00, 0x52
+ },
+ {
+ 0xFC, 0xBE, 0x8B, 0xE7, 0xDC, 0xB4, 0x9A, 0x32,
+ 0xDB, 0xDF, 0x23, 0x94, 0x59, 0xE2, 0x63, 0x08,
+ 0xB8, 0x4D, 0xFF, 0x1E, 0xA4, 0x80, 0xDF, 0x8D,
+ 0x10, 0x4E, 0xEF, 0xF3, 0x4B, 0x46, 0xFA, 0xE9,
+ 0x86, 0x27, 0xB4, 0x50, 0xC2, 0x26, 0x7D, 0x48,
+ 0xC0, 0x94, 0x6A, 0x69, 0x7C, 0x5B, 0x59, 0x53,
+ 0x14, 0x52, 0xAC, 0x04, 0x84, 0xF1, 0xC8, 0x4E,
+ 0x3A, 0x33, 0xD0, 0xC3, 0x39, 0xBB, 0x2E, 0x28
+ },
+ {
+ 0xA1, 0x90, 0x93, 0xA6, 0xE3, 0xBC, 0xF5, 0x95,
+ 0x2F, 0x85, 0x0F, 0x20, 0x30, 0xF6, 0x9B, 0x96,
+ 0x06, 0xF1, 0x47, 0xF9, 0x0B, 0x8B, 0xAE, 0xE3,
+ 0x36, 0x2D, 0xA7, 0x1D, 0x9F, 0x35, 0xB4, 0x4E,
+ 0xF9, 0xD8, 0xF0, 0xA7, 0x71, 0x2B, 0xA1, 0x87,
+ 0x7F, 0xDD, 0xCD, 0x2D, 0x8E, 0xA8, 0xF1, 0xE5,
+ 0xA7, 0x73, 0xD0, 0xB7, 0x45, 0xD4, 0x72, 0x56,
+ 0x05, 0x98, 0x3A, 0x2D, 0xE9, 0x01, 0xF8, 0x03
+ },
+ {
+ 0x3C, 0x20, 0x06, 0x42, 0x3F, 0x73, 0xE2, 0x68,
+ 0xFA, 0x59, 0xD2, 0x92, 0x03, 0x77, 0xEB, 0x29,
+ 0xA4, 0xF9, 0xA8, 0xB4, 0x62, 0xBE, 0x15, 0x98,
+ 0x3E, 0xE3, 0xB8, 0x5A, 0xE8, 0xA7, 0x8E, 0x99,
+ 0x26, 0x33, 0x58, 0x1A, 0x90, 0x99, 0x89, 0x3B,
+ 0x63, 0xDB, 0x30, 0x24, 0x1C, 0x34, 0xF6, 0x43,
+ 0x02, 0x7D, 0xC8, 0x78, 0x27, 0x9A, 0xF5, 0x85,
+ 0x0D, 0x7E, 0x2D, 0x4A, 0x26, 0x53, 0x07, 0x3A
+ },
+ {
+ 0xD0, 0xF2, 0xF2, 0xE3, 0x78, 0x76, 0x53, 0xF7,
+ 0x7C, 0xCE, 0x2F, 0xA2, 0x48, 0x35, 0x78, 0x5B,
+ 0xBD, 0x0C, 0x43, 0x3F, 0xC7, 0x79, 0x46, 0x5A,
+ 0x11, 0x51, 0x49, 0x90, 0x5A, 0x9D, 0xD1, 0xCB,
+ 0x82, 0x7A, 0x62, 0x85, 0x06, 0xD4, 0x57, 0xFC,
+ 0xF1, 0x24, 0xA0, 0xC2, 0xAE, 0xF9, 0xCE, 0x2D,
+ 0x2A, 0x0A, 0x0F, 0x63, 0x54, 0x55, 0x70, 0xD8,
+ 0x66, 0x7F, 0xF9, 0xE2, 0xEB, 0xA0, 0x73, 0x34
+ },
+ {
+ 0x78, 0xA9, 0xFC, 0x04, 0x8E, 0x25, 0xC6, 0xDC,
+ 0xB5, 0xDE, 0x45, 0x66, 0x7D, 0xE8, 0xFF, 0xDD,
+ 0x3A, 0x93, 0x71, 0x11, 0x41, 0xD5, 0x94, 0xE9,
+ 0xFA, 0x62, 0xA9, 0x59, 0x47, 0x5D, 0xA6, 0x07,
+ 0x5E, 0xA8, 0xF0, 0x91, 0x6E, 0x84, 0xE4, 0x5A,
+ 0xD9, 0x11, 0xB7, 0x54, 0x67, 0x07, 0x7E, 0xE5,
+ 0x2D, 0x2C, 0x9A, 0xEB, 0xF4, 0xD5, 0x8F, 0x20,
+ 0xCE, 0x4A, 0x3A, 0x00, 0x45, 0x8B, 0x05, 0xD4
+ },
+ {
+ 0x45, 0x81, 0x3F, 0x44, 0x17, 0x69, 0xAB, 0x6E,
+ 0xD3, 0x7D, 0x34, 0x9F, 0xF6, 0xE7, 0x22, 0x67,
+ 0xD7, 0x6A, 0xE6, 0xBB, 0x3E, 0x3C, 0x61, 0x2E,
+ 0xC0, 0x5C, 0x6E, 0x02, 0xA1, 0x2A, 0xF5, 0xA3,
+ 0x7C, 0x91, 0x8B, 0x52, 0xBF, 0x74, 0x26, 0x7C,
+ 0x3F, 0x6A, 0x3F, 0x18, 0x3A, 0x80, 0x64, 0xFF,
+ 0x84, 0xC0, 0x7B, 0x19, 0x3D, 0x08, 0x06, 0x67,
+ 0x89, 0xA0, 0x1A, 0xCC, 0xDB, 0x6F, 0x93, 0x40
+ },
+ {
+ 0x95, 0x6D, 0xA1, 0xC6, 0x8D, 0x83, 0xA7, 0xB8,
+ 0x81, 0xE0, 0x1B, 0x9A, 0x96, 0x6C, 0x3C, 0x0B,
+ 0xF2, 0x7F, 0x68, 0x60, 0x6A, 0x8B, 0x71, 0xD4,
+ 0x57, 0xBD, 0x01, 0x6D, 0x4C, 0x41, 0xDD, 0x8A,
+ 0x38, 0x0C, 0x70, 0x9A, 0x29, 0x6C, 0xB4, 0xC6,
+ 0x54, 0x47, 0x92, 0x92, 0x0F, 0xD7, 0x88, 0x83,
+ 0x57, 0x71, 0xA0, 0x7D, 0x4A, 0x16, 0xFB, 0x52,
+ 0xED, 0x48, 0x05, 0x03, 0x31, 0xDC, 0x4C, 0x8B
+ },
+ {
+ 0xDF, 0x18, 0x6C, 0x2D, 0xC0, 0x9C, 0xAA, 0x48,
+ 0xE1, 0x4E, 0x94, 0x2F, 0x75, 0xDE, 0x5A, 0xC1,
+ 0xB7, 0xA2, 0x1E, 0x4F, 0x9F, 0x07, 0x2A, 0x5B,
+ 0x37, 0x1E, 0x09, 0xE0, 0x73, 0x45, 0xB0, 0x74,
+ 0x0C, 0x76, 0x17, 0x7B, 0x01, 0x27, 0x88, 0x08,
+ 0xFE, 0xC0, 0x25, 0xED, 0xED, 0x98, 0x22, 0xC1,
+ 0x22, 0xAF, 0xD1, 0xC6, 0x3E, 0x6F, 0x0C, 0xE2,
+ 0xE3, 0x26, 0x31, 0x04, 0x10, 0x63, 0x14, 0x5C
+ },
+ {
+ 0x87, 0x47, 0x56, 0x40, 0x96, 0x6A, 0x9F, 0xDC,
+ 0xD6, 0xD3, 0xA3, 0xB5, 0xA2, 0xCC, 0xA5, 0xC0,
+ 0x8F, 0x0D, 0x88, 0x2B, 0x10, 0x24, 0x3C, 0x0E,
+ 0xC1, 0xBF, 0x3C, 0x6B, 0x1C, 0x37, 0xF2, 0xCD,
+ 0x32, 0x12, 0xF1, 0x9A, 0x05, 0x78, 0x64, 0x47,
+ 0x7D, 0x5E, 0xAF, 0x8F, 0xAE, 0xD7, 0x3F, 0x29,
+ 0x37, 0xC7, 0x68, 0xA0, 0xAF, 0x41, 0x5E, 0x84,
+ 0xBB, 0xCE, 0x6B, 0xD7, 0xDE, 0x23, 0xB6, 0x60
+ },
+ {
+ 0xC3, 0xB5, 0x73, 0xBB, 0xE1, 0x09, 0x49, 0xA0,
+ 0xFB, 0xD4, 0xFF, 0x88, 0x4C, 0x44, 0x6F, 0x22,
+ 0x29, 0xB7, 0x69, 0x02, 0xF9, 0xDF, 0xDB, 0xB8,
+ 0xA0, 0x35, 0x3D, 0xA5, 0xC8, 0x3C, 0xA1, 0x4E,
+ 0x81, 0x51, 0xBB, 0xAA, 0xC8, 0x2F, 0xD1, 0x57,
+ 0x6A, 0x00, 0x9A, 0xDC, 0x6F, 0x19, 0x35, 0xCF,
+ 0x26, 0xED, 0xD4, 0xF1, 0xFB, 0x8D, 0xA4, 0x83,
+ 0xE6, 0xC5, 0xCD, 0x9D, 0x89, 0x23, 0xAD, 0xC3
+ },
+ {
+ 0xB0, 0x9D, 0x8D, 0x0B, 0xBA, 0x8A, 0x72, 0x86,
+ 0xE4, 0x35, 0x68, 0xF7, 0x90, 0x75, 0x50, 0xE4,
+ 0x20, 0x36, 0xD6, 0x74, 0xE3, 0xC8, 0xFC, 0x34,
+ 0xD8, 0xCA, 0x46, 0xF7, 0x71, 0xD6, 0x46, 0x6B,
+ 0x70, 0xFB, 0x60, 0x58, 0x75, 0xF6, 0xA8, 0x63,
+ 0xC8, 0x77, 0xD1, 0x2F, 0x07, 0x06, 0x3F, 0xDC,
+ 0x2E, 0x90, 0xCC, 0xD4, 0x59, 0xB1, 0x91, 0x0D,
+ 0xCD, 0x52, 0xD8, 0xF1, 0x0B, 0x2B, 0x0A, 0x15
+ },
+ {
+ 0xAF, 0x3A, 0x22, 0xBF, 0x75, 0xB2, 0x1A, 0xBF,
+ 0xB0, 0xAC, 0xD5, 0x44, 0x22, 0xBA, 0x1B, 0x73,
+ 0x00, 0xA9, 0x52, 0xEF, 0xF0, 0x2E, 0xBE, 0xB6,
+ 0x5B, 0x5C, 0x23, 0x44, 0x71, 0xA9, 0x8D, 0xF3,
+ 0x2F, 0x4F, 0x96, 0x43, 0xCE, 0x19, 0x04, 0x10,
+ 0x8A, 0x16, 0x87, 0x67, 0x92, 0x42, 0x80, 0xBD,
+ 0x76, 0xC8, 0x3F, 0x8C, 0x82, 0xD9, 0xA7, 0x9D,
+ 0x92, 0x59, 0xB1, 0x95, 0x36, 0x2A, 0x2A, 0x04
+ },
+ {
+ 0xBF, 0x4F, 0xF2, 0x22, 0x1B, 0x7E, 0x69, 0x57,
+ 0xA7, 0x24, 0xCD, 0x96, 0x4A, 0xA3, 0xD5, 0xD0,
+ 0xD9, 0x94, 0x1F, 0x54, 0x04, 0x13, 0x75, 0x2F,
+ 0x46, 0x99, 0xD8, 0x10, 0x1B, 0x3E, 0x53, 0x75,
+ 0x08, 0xBF, 0x09, 0xF8, 0x50, 0x8B, 0x31, 0x77,
+ 0x36, 0xFF, 0xD2, 0x65, 0xF2, 0x84, 0x7A, 0xA7,
+ 0xD8, 0x4B, 0xD2, 0xD9, 0x75, 0x69, 0xC4, 0x9D,
+ 0x63, 0x2A, 0xED, 0x99, 0x45, 0xE5, 0xFA, 0x5E
+ },
+ {
+ 0x9C, 0x6B, 0x6B, 0x78, 0x19, 0x9B, 0x1B, 0xDA,
+ 0xCB, 0x43, 0x00, 0xE3, 0x14, 0x79, 0xFA, 0x62,
+ 0x2A, 0x6B, 0x5B, 0xC8, 0x0D, 0x46, 0x78, 0xA6,
+ 0x07, 0x8F, 0x88, 0xA8, 0x26, 0x8C, 0xD7, 0x20,
+ 0x6A, 0x27, 0x99, 0xE8, 0xD4, 0x62, 0x1A, 0x46,
+ 0x4E, 0xF6, 0xB4, 0x3D, 0xD8, 0xAD, 0xFF, 0xE9,
+ 0x7C, 0xAF, 0x22, 0x1B, 0x22, 0xB6, 0xB8, 0x77,
+ 0x8B, 0x14, 0x9A, 0x82, 0x2A, 0xEF, 0xBB, 0x09
+ },
+ {
+ 0x89, 0x06, 0x56, 0xF0, 0x9C, 0x99, 0xD2, 0x80,
+ 0xB5, 0xEC, 0xB3, 0x81, 0xF5, 0x64, 0x27, 0xB8,
+ 0x13, 0x75, 0x1B, 0xC6, 0x52, 0xC7, 0x82, 0x80,
+ 0x78, 0xB2, 0x3A, 0x4A, 0xF8, 0x3B, 0x4E, 0x3A,
+ 0x61, 0xFD, 0xBA, 0xC6, 0x1F, 0x89, 0xBE, 0xE8,
+ 0x4E, 0xA6, 0xBE, 0xE7, 0x60, 0xC0, 0x47, 0xF2,
+ 0x5C, 0x6B, 0x0A, 0x20, 0x1C, 0x69, 0xA3, 0x8F,
+ 0xD6, 0xFD, 0x97, 0x1A, 0xF1, 0x85, 0x88, 0xBB
+ },
+ {
+ 0x31, 0xA0, 0x46, 0xF7, 0x88, 0x2F, 0xFE, 0x6F,
+ 0x83, 0xCE, 0x47, 0x2E, 0x9A, 0x07, 0x01, 0x83,
+ 0x2E, 0xC7, 0xB3, 0xF7, 0x6F, 0xBC, 0xFD, 0x1D,
+ 0xF6, 0x0F, 0xE3, 0xEA, 0x48, 0xFD, 0xE1, 0x65,
+ 0x12, 0x54, 0x24, 0x7C, 0x3F, 0xD9, 0x5E, 0x10,
+ 0x0F, 0x91, 0x72, 0x73, 0x1E, 0x17, 0xFD, 0x52,
+ 0x97, 0xC1, 0x1F, 0x4B, 0xB3, 0x28, 0x36, 0x3C,
+ 0xA3, 0x61, 0x62, 0x4A, 0x81, 0xAF, 0x79, 0x7C
+ },
+ {
+ 0x27, 0xA6, 0x0B, 0x2D, 0x00, 0xE7, 0xA6, 0x71,
+ 0xD4, 0x7D, 0x0A, 0xEC, 0x2A, 0x68, 0x6A, 0x0A,
+ 0xC0, 0x4B, 0x52, 0xF4, 0x0A, 0xB6, 0x62, 0x90,
+ 0x28, 0xEB, 0x7D, 0x13, 0xF4, 0xBA, 0xA9, 0x9A,
+ 0xC0, 0xFE, 0x46, 0xEE, 0x6C, 0x81, 0x49, 0x44,
+ 0xF2, 0xF4, 0xB4, 0xD2, 0x0E, 0x93, 0x78, 0xE4,
+ 0x84, 0x7E, 0xA4, 0x4C, 0x13, 0x17, 0x80, 0x91,
+ 0xE2, 0x77, 0xB8, 0x7E, 0xA7, 0xA5, 0x57, 0x11
+ },
+ {
+ 0x8B, 0x5C, 0xCE, 0xF1, 0x94, 0x16, 0x2C, 0x1F,
+ 0x19, 0xD6, 0x8F, 0x91, 0xE0, 0xB0, 0x92, 0x8F,
+ 0x28, 0x9E, 0xC5, 0x28, 0x37, 0x20, 0x84, 0x0C,
+ 0x2F, 0x73, 0xD2, 0x53, 0x11, 0x12, 0x38, 0xDC,
+ 0xFE, 0x94, 0xAF, 0x2B, 0x59, 0xC2, 0xC1, 0xCA,
+ 0x25, 0x91, 0x90, 0x1A, 0x7B, 0xC0, 0x60, 0xE7,
+ 0x45, 0x9B, 0x6C, 0x47, 0xDF, 0x0F, 0x71, 0x70,
+ 0x1A, 0x35, 0xCC, 0x0A, 0xA8, 0x31, 0xB5, 0xB6
+ },
+ {
+ 0x57, 0xAB, 0x6C, 0x4B, 0x22, 0x29, 0xAE, 0xB3,
+ 0xB7, 0x04, 0x76, 0xD8, 0x03, 0xCD, 0x63, 0x81,
+ 0x2F, 0x10, 0x7C, 0xE6, 0xDA, 0x17, 0xFE, 0xD9,
+ 0xB1, 0x78, 0x75, 0xE8, 0xF8, 0x6C, 0x72, 0x4F,
+ 0x49, 0xE0, 0x24, 0xCB, 0xF3, 0xA1, 0xB8, 0xB1,
+ 0x19, 0xC5, 0x03, 0x57, 0x65, 0x2B, 0x81, 0x87,
+ 0x9D, 0x2A, 0xDE, 0x2D, 0x58, 0x8B, 0x9E, 0x4F,
+ 0x7C, 0xED, 0xBA, 0x0E, 0x46, 0x44, 0xC9, 0xEE
+ },
+ {
+ 0x01, 0x90, 0xA8, 0xDA, 0xC3, 0x20, 0xA7, 0x39,
+ 0xF3, 0x22, 0xE1, 0x57, 0x31, 0xAA, 0x14, 0x0D,
+ 0xDA, 0xF5, 0xBE, 0xD2, 0x94, 0xD5, 0xC8, 0x2E,
+ 0x54, 0xFE, 0xF2, 0x9F, 0x21, 0x4E, 0x18, 0xAA,
+ 0xFA, 0xA8, 0x4F, 0x8B, 0xE9, 0x9A, 0xF6, 0x29,
+ 0x50, 0x26, 0x6B, 0x8F, 0x90, 0x1F, 0x15, 0xDD,
+ 0x4C, 0x5D, 0x35, 0x51, 0x6F, 0xC3, 0x5B, 0x4C,
+ 0xAB, 0x2E, 0x96, 0xE4, 0x69, 0x5B, 0xBE, 0x1C
+ },
+ {
+ 0xD1, 0x4D, 0x7C, 0x4C, 0x41, 0x5E, 0xEB, 0x0E,
+ 0x10, 0xB1, 0x59, 0x22, 0x4B, 0xEA, 0x12, 0x7E,
+ 0xBD, 0x84, 0xF9, 0x59, 0x1C, 0x70, 0x2A, 0x33,
+ 0x0F, 0x5B, 0xB7, 0xBB, 0x7A, 0xA4, 0x4E, 0xA3,
+ 0x9D, 0xE6, 0xED, 0x01, 0xF1, 0x8D, 0xA7, 0xAD,
+ 0xF4, 0x0C, 0xFB, 0x97, 0xC5, 0xD1, 0x52, 0xC2,
+ 0x75, 0x28, 0x82, 0x4B, 0x21, 0xE2, 0x39, 0x52,
+ 0x6A, 0xF8, 0xF3, 0x6B, 0x21, 0x4E, 0x0C, 0xFB
+ },
+ {
+ 0xBE, 0x28, 0xC4, 0xBE, 0x70, 0x69, 0x70, 0x48,
+ 0x8F, 0xAC, 0x7D, 0x29, 0xC3, 0xBD, 0x5C, 0x4E,
+ 0x98, 0x60, 0x85, 0xC4, 0xC3, 0x33, 0x2F, 0x1F,
+ 0x3F, 0xD3, 0x09, 0x73, 0xDB, 0x61, 0x41, 0x64,
+ 0xBA, 0x2F, 0x31, 0xA7, 0x88, 0x75, 0xFF, 0xDC,
+ 0x15, 0x03, 0x25, 0xC8, 0x83, 0x27, 0xA9, 0x44,
+ 0x3E, 0xD0, 0x4F, 0xDF, 0xE5, 0xBE, 0x93, 0x87,
+ 0x6D, 0x16, 0x28, 0x56, 0x0C, 0x76, 0x4A, 0x80
+ },
+ {
+ 0x03, 0x1D, 0xA1, 0x06, 0x9E, 0x3A, 0x2E, 0x9C,
+ 0x33, 0x82, 0xE4, 0x36, 0xFF, 0xD7, 0x9D, 0xF7,
+ 0x4B, 0x1C, 0xA6, 0xA8, 0xAD, 0xB2, 0xDE, 0xAB,
+ 0xE6, 0x76, 0xAB, 0x45, 0x99, 0x4C, 0xBC, 0x05,
+ 0x4F, 0x03, 0x7D, 0x2F, 0x0E, 0xAC, 0xE8, 0x58,
+ 0xD3, 0x2C, 0x14, 0xE2, 0xD1, 0xC8, 0xB4, 0x60,
+ 0x77, 0x30, 0x8E, 0x3B, 0xDC, 0x2C, 0x1B, 0x53,
+ 0x17, 0x2E, 0xCF, 0x7A, 0x8C, 0x14, 0xE3, 0x49
+ },
+ {
+ 0x46, 0x65, 0xCE, 0xF8, 0xBA, 0x4D, 0xB4, 0xD0,
+ 0xAC, 0xB1, 0x18, 0xF2, 0x98, 0x7F, 0x0B, 0xB0,
+ 0x9F, 0x8F, 0x86, 0xAA, 0x44, 0x5A, 0xA3, 0xD5,
+ 0xFC, 0x9A, 0x8B, 0x34, 0x68, 0x64, 0x78, 0x74,
+ 0x89, 0xE8, 0xFC, 0xEC, 0xC1, 0x25, 0xD1, 0x7E,
+ 0x9B, 0x56, 0xE1, 0x29, 0x88, 0xEA, 0xC5, 0xEC,
+ 0xC7, 0x28, 0x68, 0x83, 0xDB, 0x06, 0x61, 0xB8,
+ 0xFF, 0x05, 0xDA, 0x2A, 0xFF, 0xF3, 0x0F, 0xE4
+ },
+ {
+ 0x63, 0xB7, 0x03, 0x2E, 0x5F, 0x93, 0x0C, 0xC9,
+ 0x93, 0x95, 0x17, 0xF9, 0xE9, 0x86, 0x81, 0x6C,
+ 0xFB, 0xEC, 0x2B, 0xE5, 0x9B, 0x95, 0x68, 0xB1,
+ 0x3F, 0x2E, 0xAD, 0x05, 0xBA, 0xE7, 0x77, 0x7C,
+ 0xAB, 0x62, 0x0C, 0x66, 0x59, 0x40, 0x4F, 0x74,
+ 0x09, 0xE4, 0x19, 0x9A, 0x3B, 0xE5, 0xF7, 0x86,
+ 0x5A, 0xA7, 0xCB, 0xDF, 0x8C, 0x42, 0x53, 0xF7,
+ 0xE8, 0x21, 0x9B, 0x1B, 0xD5, 0xF4, 0x6F, 0xEA
+ },
+ {
+ 0x9F, 0x09, 0xBF, 0x09, 0x3A, 0x2B, 0x0F, 0xF8,
+ 0xC2, 0x63, 0x4B, 0x49, 0xE3, 0x7F, 0x1B, 0x21,
+ 0x35, 0xB4, 0x47, 0xAA, 0x91, 0x44, 0xC9, 0x78,
+ 0x7D, 0xBF, 0xD9, 0x21, 0x29, 0x31, 0x6C, 0x99,
+ 0xE8, 0x8A, 0xAB, 0x8A, 0x21, 0xFD, 0xEF, 0x23,
+ 0x72, 0xD1, 0x18, 0x9A, 0xEC, 0x50, 0x0F, 0x95,
+ 0x77, 0x5F, 0x1F, 0x92, 0xBF, 0xB4, 0x55, 0x45,
+ 0xE4, 0x25, 0x9F, 0xB9, 0xB7, 0xB0, 0x2D, 0x14
+ },
+ {
+ 0xF9, 0xF8, 0x49, 0x3C, 0x68, 0x08, 0x88, 0x07,
+ 0xDF, 0x7F, 0x6A, 0x26, 0x93, 0xD6, 0x4E, 0xA5,
+ 0x9F, 0x03, 0xE9, 0xE0, 0x5A, 0x22, 0x3E, 0x68,
+ 0x52, 0x4C, 0xA3, 0x21, 0x95, 0xA4, 0x73, 0x4B,
+ 0x65, 0x4F, 0xCE, 0xA4, 0xD2, 0x73, 0x4C, 0x86,
+ 0x6C, 0xF9, 0x5C, 0x88, 0x9F, 0xB1, 0x0C, 0x49,
+ 0x15, 0x9B, 0xE2, 0xF5, 0x04, 0x3D, 0xC9, 0x8B,
+ 0xB5, 0x5E, 0x02, 0xEF, 0x7B, 0xDC, 0xB0, 0x82
+ },
+ {
+ 0x3C, 0x9A, 0x73, 0x59, 0xAB, 0x4F, 0xEB, 0xCE,
+ 0x07, 0xB2, 0x0A, 0xC4, 0x47, 0xB0, 0x6A, 0x24,
+ 0x0B, 0x7F, 0xE1, 0xDA, 0xE5, 0x43, 0x9C, 0x49,
+ 0xB6, 0x0B, 0x58, 0x19, 0xF7, 0x81, 0x2E, 0x4C,
+ 0x17, 0x24, 0x06, 0xC1, 0xAA, 0xC3, 0x16, 0x71,
+ 0x3C, 0xF0, 0xDD, 0xED, 0x10, 0x38, 0x07, 0x72,
+ 0x58, 0xE2, 0xEF, 0xF5, 0xB3, 0x39, 0x13, 0xD9,
+ 0xD9, 0x5C, 0xAE, 0xB4, 0xE6, 0xC6, 0xB9, 0x70
+ },
+ {
+ 0xAD, 0x6A, 0xAB, 0x80, 0x84, 0x51, 0x0E, 0x82,
+ 0x2C, 0xFC, 0xE8, 0x62, 0x5D, 0x62, 0xCF, 0x4D,
+ 0xE6, 0x55, 0xF4, 0x76, 0x38, 0x84, 0xC7, 0x1E,
+ 0x80, 0xBA, 0xB9, 0xAC, 0x9D, 0x53, 0x18, 0xDB,
+ 0xA4, 0xA6, 0x03, 0x3E, 0xD2, 0x90, 0x84, 0xE6,
+ 0x52, 0x16, 0xC0, 0x31, 0x60, 0x6C, 0xA1, 0x76,
+ 0x15, 0xDC, 0xFE, 0x3B, 0xA1, 0x1D, 0x26, 0x85,
+ 0x1A, 0xE0, 0x99, 0x9C, 0xA6, 0xE2, 0x32, 0xCF
+ },
+ {
+ 0x15, 0x6E, 0x9E, 0x62, 0x61, 0x37, 0x4C, 0x9D,
+ 0xC8, 0x84, 0xF3, 0x6E, 0x70, 0xF0, 0xFE, 0x1A,
+ 0xB9, 0x29, 0x79, 0x97, 0xB8, 0x36, 0xFA, 0x7D,
+ 0x17, 0x0A, 0x9C, 0x9E, 0xBF, 0x57, 0x5B, 0x88,
+ 0x1E, 0x7B, 0xCE, 0xA4, 0x4D, 0x6C, 0x02, 0x48,
+ 0xD3, 0x55, 0x97, 0x90, 0x71, 0x54, 0x82, 0x89,
+ 0x55, 0xBE, 0x19, 0x13, 0x58, 0x52, 0xF9, 0x22,
+ 0x88, 0x15, 0xEC, 0xA0, 0x24, 0xA8, 0xAD, 0xFB
+ },
+ {
+ 0x42, 0x15, 0x40, 0x76, 0x33, 0xF4, 0xCC, 0xA9,
+ 0xB6, 0x78, 0x8B, 0xE9, 0x3E, 0x6A, 0xA3, 0xD9,
+ 0x63, 0xC7, 0xD6, 0xCE, 0x4B, 0x14, 0x72, 0x47,
+ 0x09, 0x9F, 0x46, 0xA3, 0xAC, 0xB5, 0x00, 0xA3,
+ 0x00, 0x38, 0xCB, 0x3E, 0x78, 0x8C, 0x3D, 0x29,
+ 0xF1, 0x32, 0xAD, 0x84, 0x4E, 0x80, 0xE9, 0xE9,
+ 0x92, 0x51, 0xF6, 0xDB, 0x96, 0xAC, 0xD8, 0xA0,
+ 0x91, 0xCF, 0xC7, 0x70, 0xAF, 0x53, 0x84, 0x7B
+ },
+ {
+ 0x1C, 0x07, 0x7E, 0x27, 0x9D, 0xE6, 0x54, 0x85,
+ 0x23, 0x50, 0x2B, 0x6D, 0xF8, 0x00, 0xFF, 0xDA,
+ 0xB5, 0xE2, 0xC3, 0xE9, 0x44, 0x2E, 0xB8, 0x38,
+ 0xF5, 0x8C, 0x29, 0x5F, 0x3B, 0x14, 0x7C, 0xEF,
+ 0x9D, 0x70, 0x1C, 0x41, 0xC3, 0x21, 0x28, 0x3F,
+ 0x00, 0xC7, 0x1A, 0xFF, 0xA0, 0x61, 0x93, 0x10,
+ 0x39, 0x91, 0x26, 0x29, 0x5B, 0x78, 0xDD, 0x4D,
+ 0x1A, 0x74, 0x57, 0x2E, 0xF9, 0xED, 0x51, 0x35
+ },
+ {
+ 0xF0, 0x7A, 0x55, 0x5F, 0x49, 0xFE, 0x48, 0x1C,
+ 0xF4, 0xCD, 0x0A, 0x87, 0xB7, 0x1B, 0x82, 0xE4,
+ 0xA9, 0x50, 0x64, 0xD0, 0x66, 0x77, 0xFD, 0xD9,
+ 0x0A, 0x0E, 0xB5, 0x98, 0x87, 0x7B, 0xA1, 0xC8,
+ 0x3D, 0x46, 0x77, 0xB3, 0x93, 0xC3, 0xA3, 0xB6,
+ 0x66, 0x1C, 0x42, 0x1F, 0x5B, 0x12, 0xCB, 0x99,
+ 0xD2, 0x03, 0x76, 0xBA, 0x72, 0x75, 0xC2, 0xF3,
+ 0xA8, 0xF5, 0xA9, 0xB7, 0x82, 0x17, 0x20, 0xDA
+ },
+ {
+ 0xB5, 0x91, 0x1B, 0x38, 0x0D, 0x20, 0xC7, 0xB0,
+ 0x43, 0x23, 0xE4, 0x02, 0x6B, 0x38, 0xE2, 0x00,
+ 0xF5, 0x34, 0x25, 0x92, 0x33, 0xB5, 0x81, 0xE0,
+ 0x2C, 0x1E, 0x3E, 0x2D, 0x84, 0x38, 0xD6, 0xC6,
+ 0x6D, 0x5A, 0x4E, 0xB2, 0x01, 0xD5, 0xA8, 0xB7,
+ 0x50, 0x72, 0xC4, 0xEC, 0x29, 0x10, 0x63, 0x34,
+ 0xDA, 0x70, 0xBC, 0x79, 0x52, 0x1B, 0x0C, 0xED,
+ 0x2C, 0xFD, 0x53, 0x3F, 0x5F, 0xF8, 0x4F, 0x95
+ },
+ {
+ 0x01, 0xF0, 0x70, 0xA0, 0x9B, 0xAE, 0x91, 0x12,
+ 0x96, 0x36, 0x1F, 0x91, 0xAA, 0x0E, 0x8E, 0x0D,
+ 0x09, 0xA7, 0x72, 0x54, 0x78, 0x53, 0x6D, 0x9D,
+ 0x48, 0xC5, 0xFE, 0x1E, 0x5E, 0x7C, 0x3C, 0x5B,
+ 0x9B, 0x9D, 0x6E, 0xB0, 0x77, 0x96, 0xF6, 0xDA,
+ 0x57, 0xAE, 0x56, 0x2A, 0x7D, 0x70, 0xE8, 0x82,
+ 0xE3, 0x7A, 0xDF, 0xDE, 0x83, 0xF0, 0xC4, 0x33,
+ 0xC2, 0xCD, 0x36, 0x35, 0x36, 0xBB, 0x22, 0xC8
+ },
+ {
+ 0x6F, 0x79, 0x3E, 0xB4, 0x37, 0x4A, 0x48, 0xB0,
+ 0x77, 0x5A, 0xCA, 0xF9, 0xAD, 0xCF, 0x8E, 0x45,
+ 0xE5, 0x42, 0x70, 0xC9, 0x47, 0x5F, 0x00, 0x4A,
+ 0xD8, 0xD5, 0x97, 0x3E, 0x2A, 0xCA, 0x52, 0x74,
+ 0x7F, 0xF4, 0xED, 0x04, 0xAE, 0x96, 0x72, 0x75,
+ 0xB9, 0xF9, 0xEB, 0x0E, 0x1F, 0xF7, 0x5F, 0xB4,
+ 0xF7, 0x94, 0xFA, 0x8B, 0xE9, 0xAD, 0xD7, 0xA4,
+ 0x13, 0x04, 0x86, 0x8D, 0x10, 0x3F, 0xAB, 0x10
+ },
+ {
+ 0x96, 0x5F, 0x20, 0xF1, 0x39, 0x76, 0x5F, 0xCC,
+ 0x4C, 0xE4, 0xBA, 0x37, 0x94, 0x67, 0x58, 0x63,
+ 0xCA, 0xC2, 0x4D, 0xB4, 0x72, 0xCD, 0x2B, 0x79,
+ 0x9D, 0x03, 0x5B, 0xCE, 0x3D, 0xBE, 0xA5, 0x02,
+ 0xDA, 0x7B, 0x52, 0x48, 0x65, 0xF6, 0xB8, 0x11,
+ 0xD8, 0xC5, 0x82, 0x8D, 0x3A, 0x88, 0x96, 0x46,
+ 0xFE, 0x64, 0xA3, 0x80, 0xDA, 0x1A, 0xA7, 0xC7,
+ 0x04, 0x4E, 0x9F, 0x24, 0x5D, 0xCE, 0xD1, 0x28
+ },
+ {
+ 0xEC, 0x29, 0x5B, 0x57, 0x83, 0x60, 0x12, 0x44,
+ 0xC3, 0x0E, 0x46, 0x41, 0xE3, 0xB4, 0x5B, 0xE2,
+ 0x22, 0xC4, 0xDC, 0xE7, 0x7A, 0x58, 0x70, 0x0F,
+ 0x53, 0xBC, 0x8E, 0xC5, 0x2A, 0x94, 0x16, 0x90,
+ 0xB4, 0xD0, 0xB0, 0x87, 0xFB, 0x6F, 0xCB, 0x3F,
+ 0x39, 0x83, 0x2B, 0x9D, 0xE8, 0xF7, 0x5E, 0xC2,
+ 0x0B, 0xD4, 0x30, 0x79, 0x81, 0x17, 0x49, 0xCD,
+ 0xC9, 0x07, 0xED, 0xB9, 0x41, 0x57, 0xD1, 0x80
+ },
+ {
+ 0x61, 0xC7, 0x2F, 0x8C, 0xCC, 0x91, 0xDB, 0xB5,
+ 0x4C, 0xA6, 0x75, 0x0B, 0xC4, 0x89, 0x67, 0x2D,
+ 0xE0, 0x9F, 0xAE, 0xDB, 0x8F, 0xDD, 0x4F, 0x94,
+ 0xFF, 0x23, 0x20, 0x90, 0x9A, 0x30, 0x3F, 0x5D,
+ 0x5A, 0x98, 0x48, 0x1C, 0x0B, 0xC1, 0xA6, 0x25,
+ 0x41, 0x9F, 0xB4, 0xDE, 0xBF, 0xBF, 0x7F, 0x8A,
+ 0x53, 0xBB, 0x07, 0xEC, 0x3D, 0x98, 0x5E, 0x8E,
+ 0xA1, 0x1E, 0x72, 0xD5, 0x59, 0x94, 0x07, 0x80
+ },
+ {
+ 0xAF, 0xD8, 0x14, 0x5B, 0x25, 0x9E, 0xEF, 0xC8,
+ 0xD1, 0x26, 0x20, 0xC3, 0xC5, 0xB0, 0x3E, 0x1E,
+ 0xD8, 0xFD, 0x2C, 0xCE, 0xFE, 0x03, 0x65, 0x07,
+ 0x8C, 0x80, 0xFD, 0x42, 0xC1, 0x77, 0x0E, 0x28,
+ 0xB4, 0x49, 0x48, 0xF2, 0x7E, 0x65, 0xA1, 0x88,
+ 0x66, 0x90, 0x11, 0x0D, 0xB8, 0x14, 0x39, 0x7B,
+ 0x68, 0xE4, 0x3D, 0x80, 0xD1, 0xBA, 0x16, 0xDF,
+ 0xA3, 0x58, 0xE7, 0x39, 0xC8, 0x98, 0xCF, 0xA3
+ },
+ {
+ 0x55, 0x2F, 0xC7, 0x89, 0x3C, 0xF1, 0xCE, 0x93,
+ 0x3A, 0xDA, 0x35, 0xC0, 0xDA, 0x98, 0x84, 0x4E,
+ 0x41, 0x54, 0x5E, 0x24, 0x4C, 0x31, 0x57, 0xA1,
+ 0x42, 0x8D, 0x7B, 0x4C, 0x21, 0xF9, 0xCD, 0x7E,
+ 0x40, 0x71, 0xAE, 0xD7, 0x7B, 0x7C, 0xA9, 0xF1,
+ 0xC3, 0x8F, 0xBA, 0x32, 0x23, 0x74, 0x12, 0xEF,
+ 0x21, 0xA3, 0x42, 0x74, 0x2E, 0xC8, 0x32, 0x43,
+ 0x78, 0xF2, 0x1E, 0x50, 0x7F, 0xAF, 0xDD, 0x88
+ },
+ {
+ 0x46, 0x7A, 0x33, 0xFB, 0xAD, 0xF5, 0xEB, 0xC5,
+ 0x25, 0x96, 0xEF, 0x86, 0xAA, 0xAE, 0xFC, 0x6F,
+ 0xAB, 0xA8, 0xEE, 0x65, 0x1B, 0x1C, 0xE0, 0x4D,
+ 0xE3, 0x68, 0xA0, 0x3A, 0x5A, 0x90, 0x40, 0xEF,
+ 0x28, 0x35, 0xE0, 0x0A, 0xDB, 0x09, 0xAB, 0xB3,
+ 0xFB, 0xD2, 0xBC, 0xE8, 0x18, 0xA2, 0x41, 0x3D,
+ 0x0B, 0x02, 0x53, 0xB5, 0xBD, 0xA4, 0xFC, 0x5B,
+ 0x2F, 0x6F, 0x85, 0xF3, 0xFD, 0x5B, 0x55, 0xF2
+ },
+ {
+ 0x22, 0xEF, 0xF8, 0xE6, 0xDD, 0x52, 0x36, 0xF5,
+ 0xF5, 0x7D, 0x94, 0xED, 0xE8, 0x74, 0xD6, 0xC9,
+ 0x42, 0x8E, 0x8F, 0x5D, 0x56, 0x6F, 0x17, 0xCD,
+ 0x6D, 0x18, 0x48, 0xCD, 0x75, 0x2F, 0xE1, 0x3C,
+ 0x65, 0x5C, 0xB1, 0x0F, 0xBA, 0xAF, 0xF7, 0x68,
+ 0x72, 0xF2, 0xBF, 0x2D, 0xA9, 0x9E, 0x15, 0xDC,
+ 0x62, 0x40, 0x75, 0xE1, 0xEC, 0x2F, 0x58, 0xA3,
+ 0xF6, 0x40, 0x72, 0x12, 0x18, 0x38, 0x56, 0x9E
+ },
+ {
+ 0x9C, 0xEC, 0x6B, 0xBF, 0x62, 0xC4, 0xBC, 0xE4,
+ 0x13, 0x8A, 0xBA, 0xE1, 0xCB, 0xEC, 0x8D, 0xAD,
+ 0x31, 0x95, 0x04, 0x44, 0xE9, 0x03, 0x21, 0xB1,
+ 0x34, 0x71, 0x96, 0x83, 0x4C, 0x11, 0x4B, 0x86,
+ 0x4A, 0xF3, 0xF3, 0xCC, 0x35, 0x08, 0xF8, 0x37,
+ 0x51, 0xFF, 0xB4, 0xED, 0xA7, 0xC8, 0x4D, 0x14,
+ 0x07, 0x34, 0xBB, 0x42, 0x63, 0xC3, 0x62, 0x5C,
+ 0x00, 0xF0, 0x4F, 0x4C, 0x80, 0x68, 0x98, 0x1B
+ },
+ {
+ 0xA8, 0xB6, 0x0F, 0xA4, 0xFC, 0x24, 0x42, 0xF6,
+ 0xF1, 0x51, 0x4A, 0xD7, 0x40, 0x26, 0x26, 0x92,
+ 0x0C, 0xC7, 0xC2, 0xC9, 0xF7, 0x21, 0x24, 0xB8,
+ 0xCB, 0xA8, 0xEE, 0x2C, 0xB7, 0xC4, 0x58, 0x6F,
+ 0x65, 0x8A, 0x44, 0x10, 0xCF, 0xFC, 0xC0, 0xAB,
+ 0x88, 0x34, 0x39, 0x55, 0xE0, 0x94, 0xC6, 0xAF,
+ 0x0D, 0x20, 0xD0, 0xC7, 0x14, 0xFB, 0x0A, 0x98,
+ 0x8F, 0x54, 0x3F, 0x30, 0x0F, 0x58, 0xD3, 0x89
+ },
+ {
+ 0x82, 0x71, 0xCC, 0x45, 0xDF, 0xA5, 0xE4, 0x17,
+ 0x0E, 0x84, 0x7E, 0x86, 0x30, 0xB9, 0x52, 0xCF,
+ 0x9C, 0x2A, 0xA7, 0x77, 0xD0, 0x6F, 0x26, 0xA7,
+ 0x58, 0x5B, 0x83, 0x81, 0xF1, 0x88, 0xDA, 0xCC,
+ 0x73, 0x37, 0x39, 0x1C, 0xFC, 0xC9, 0x4B, 0x05,
+ 0x3D, 0xC4, 0xEC, 0x29, 0xCC, 0x17, 0xF0, 0x77,
+ 0x87, 0x04, 0x28, 0xF1, 0xAC, 0x23, 0xFD, 0xDD,
+ 0xA1, 0x65, 0xEF, 0x5A, 0x3F, 0x15, 0x5F, 0x39
+ },
+ {
+ 0xBF, 0x23, 0xC0, 0xC2, 0x5C, 0x80, 0x60, 0xE4,
+ 0xF6, 0x99, 0x5F, 0x16, 0x23, 0xA3, 0xBE, 0xBE,
+ 0xCA, 0xA9, 0x6E, 0x30, 0x86, 0x80, 0x00, 0x0A,
+ 0x8A, 0xA3, 0xCD, 0x56, 0xBB, 0x1A, 0x6D, 0xA0,
+ 0x99, 0xE1, 0x0D, 0x92, 0x31, 0xB3, 0x7F, 0x45,
+ 0x19, 0xB2, 0xEF, 0xD2, 0xC2, 0x4D, 0xE7, 0x2F,
+ 0x31, 0xA5, 0xF1, 0x95, 0x35, 0x24, 0x1B, 0x4A,
+ 0x59, 0xFA, 0x3C, 0x03, 0xCE, 0xB7, 0x90, 0xE7
+ },
+ {
+ 0x87, 0x7F, 0xD6, 0x52, 0xC0, 0x52, 0x81, 0x00,
+ 0x9C, 0x0A, 0x52, 0x50, 0xE7, 0xA3, 0xA6, 0x71,
+ 0xF8, 0xB1, 0x8C, 0x10, 0x88, 0x17, 0xFE, 0x4A,
+ 0x87, 0x4D, 0xE2, 0x2D, 0xA8, 0xE4, 0x5D, 0xB1,
+ 0x19, 0x58, 0xA6, 0x00, 0xC5, 0xF6, 0x2E, 0x67,
+ 0xD3, 0x6C, 0xBF, 0x84, 0x47, 0x4C, 0xF2, 0x44,
+ 0xA9, 0xC2, 0xB0, 0x3A, 0x9F, 0xB9, 0xDC, 0x71,
+ 0x1C, 0xD1, 0xA2, 0xCA, 0xB6, 0xF3, 0xFA, 0xE0
+ },
+ {
+ 0x29, 0xDF, 0x4D, 0x87, 0xEA, 0x44, 0x4B, 0xAF,
+ 0x5B, 0xCD, 0xF5, 0xF4, 0xE4, 0x15, 0x79, 0xE2,
+ 0x8A, 0x67, 0xDE, 0x84, 0x14, 0x9F, 0x06, 0xC0,
+ 0x3F, 0x11, 0x0E, 0xA8, 0x4F, 0x57, 0x2A, 0x9F,
+ 0x67, 0x6A, 0xDD, 0xD0, 0x4C, 0x48, 0x78, 0xF4,
+ 0x9C, 0x5C, 0x00, 0xAC, 0xCD, 0xA4, 0x41, 0xB1,
+ 0xA3, 0x87, 0xCA, 0xCE, 0xB2, 0xE9, 0x93, 0xBB,
+ 0x7A, 0x10, 0xCD, 0x8C, 0x2D, 0x67, 0x17, 0xE1
+ },
+ {
+ 0x71, 0x0D, 0xAC, 0xB1, 0x66, 0x84, 0x46, 0x39,
+ 0xCD, 0x7B, 0x63, 0x7C, 0x27, 0x42, 0x09, 0x42,
+ 0x4E, 0x24, 0x49, 0xDC, 0x35, 0xD7, 0x90, 0xBB,
+ 0xFA, 0x4F, 0x76, 0x17, 0x70, 0x54, 0xA3, 0x6B,
+ 0x3B, 0x76, 0xFA, 0xC0, 0xCA, 0x6E, 0x61, 0xDF,
+ 0x1E, 0x68, 0x70, 0x00, 0x67, 0x8A, 0xC0, 0x74,
+ 0x6D, 0xF7, 0x5D, 0x0A, 0x39, 0x54, 0x89, 0x76,
+ 0x81, 0xFD, 0x39, 0x3A, 0x15, 0x5A, 0x1B, 0xB4
+ },
+ {
+ 0xC1, 0xD5, 0xF9, 0x3B, 0x8D, 0xEA, 0x1F, 0x25,
+ 0x71, 0xBA, 0xBC, 0xCB, 0xC0, 0x17, 0x64, 0x54,
+ 0x1A, 0x0C, 0xDA, 0x87, 0xE4, 0x44, 0xD6, 0x73,
+ 0xC5, 0x09, 0x66, 0xCA, 0x55, 0x9C, 0x33, 0x35,
+ 0x4B, 0x3A, 0xCB, 0x26, 0xE5, 0xD5, 0x78, 0x1F,
+ 0xFB, 0x28, 0x84, 0x7A, 0x4B, 0x47, 0x54, 0xD7,
+ 0x70, 0x08, 0xC6, 0x2A, 0x83, 0x58, 0x35, 0xF5,
+ 0x00, 0xDE, 0xA7, 0xC3, 0xB5, 0x8B, 0xDA, 0xE2
+ },
+ {
+ 0xA4, 0x1E, 0x41, 0x27, 0x1C, 0xDA, 0xB8, 0xAF,
+ 0x4D, 0x72, 0xB1, 0x04, 0xBF, 0xB2, 0xAD, 0x04,
+ 0x1A, 0xC4, 0xDF, 0x14, 0x67, 0x7D, 0xA6, 0x71,
+ 0xD8, 0x56, 0x40, 0xC4, 0xB1, 0x87, 0xF5, 0x0C,
+ 0x2B, 0x66, 0x51, 0x3C, 0x46, 0x19, 0xFB, 0xD5,
+ 0xD5, 0xDC, 0x4F, 0xE6, 0x5D, 0xD3, 0x7B, 0x90,
+ 0x42, 0xE9, 0x84, 0x8D, 0xDA, 0x55, 0x6A, 0x50,
+ 0x4C, 0xAA, 0x2B, 0x1C, 0x6A, 0xFE, 0x47, 0x30
+ },
+ {
+ 0xE7, 0xBC, 0xBA, 0xCD, 0xC3, 0x79, 0xC4, 0x3D,
+ 0x81, 0xEB, 0xAD, 0xCB, 0x37, 0x78, 0x15, 0x52,
+ 0xFC, 0x1D, 0x75, 0x3E, 0x8C, 0xF3, 0x10, 0xD9,
+ 0x68, 0x39, 0x2D, 0x06, 0xC9, 0x1F, 0x1D, 0x64,
+ 0xCC, 0x9E, 0x90, 0xCE, 0x1D, 0x22, 0xC3, 0x2D,
+ 0x27, 0x7F, 0xC6, 0xCD, 0xA4, 0x33, 0xA4, 0xD4,
+ 0x42, 0xC7, 0x62, 0xE9, 0xEA, 0xCF, 0x2C, 0x25,
+ 0x9F, 0x32, 0xD6, 0x4C, 0xF9, 0xDA, 0x3A, 0x22
+ },
+ {
+ 0x51, 0x75, 0x5B, 0x4A, 0xC5, 0x45, 0x6B, 0x13,
+ 0x21, 0x8A, 0x19, 0xC5, 0xB9, 0x24, 0x2F, 0x57,
+ 0xC4, 0xA9, 0x81, 0xE4, 0xD4, 0xEC, 0xDC, 0xE0,
+ 0x9A, 0x31, 0x93, 0x36, 0x2B, 0x80, 0x8A, 0x57,
+ 0x93, 0x45, 0xD4, 0x88, 0x1C, 0x26, 0x07, 0xA5,
+ 0x65, 0x34, 0xDD, 0x7F, 0x21, 0x95, 0x6A, 0xFF,
+ 0x72, 0xC2, 0xF4, 0x17, 0x3A, 0x6E, 0x7B, 0x6C,
+ 0xC2, 0x21, 0x2B, 0xA0, 0xE3, 0xDA, 0xEE, 0x1F
+ },
+ {
+ 0xDC, 0xC2, 0xC4, 0xBE, 0xB9, 0xC1, 0xF2, 0x60,
+ 0x7B, 0x78, 0x6C, 0x20, 0xC6, 0x31, 0x97, 0x23,
+ 0x47, 0x03, 0x4C, 0x1C, 0xC0, 0x2F, 0xCC, 0x7D,
+ 0x02, 0xFF, 0x01, 0x09, 0x9C, 0xFE, 0x1C, 0x69,
+ 0x89, 0x84, 0x0A, 0xC2, 0x13, 0x92, 0x36, 0x29,
+ 0x11, 0x3A, 0xA8, 0xBA, 0xD7, 0x13, 0xCC, 0xF0,
+ 0xFE, 0x4C, 0xE1, 0x32, 0x64, 0xFB, 0x32, 0xB8,
+ 0xB0, 0xFE, 0x37, 0x2D, 0xA3, 0x82, 0x54, 0x4A
+ },
+ {
+ 0x3D, 0x55, 0x17, 0x6A, 0xCE, 0xA4, 0xA7, 0xE3,
+ 0xA6, 0x5F, 0xFA, 0x9F, 0xB1, 0x0A, 0x7A, 0x17,
+ 0x67, 0x19, 0x9C, 0xF0, 0x77, 0xCE, 0xE9, 0xF7,
+ 0x15, 0x32, 0xD6, 0x7C, 0xD7, 0xC7, 0x3C, 0x9F,
+ 0x93, 0xCF, 0xC3, 0x7C, 0xCD, 0xCC, 0x1F, 0xDE,
+ 0xF5, 0x0A, 0xAD, 0x46, 0xA5, 0x04, 0xA6, 0x50,
+ 0xD2, 0x98, 0xD5, 0x97, 0xA3, 0xA9, 0xFA, 0x95,
+ 0xC6, 0xC4, 0x0C, 0xB7, 0x1F, 0xA5, 0xE7, 0x25
+ },
+ {
+ 0xD0, 0x77, 0x13, 0xC0, 0x05, 0xDE, 0x96, 0xDD,
+ 0x21, 0xD2, 0xEB, 0x8B, 0xBE, 0xCA, 0x66, 0x74,
+ 0x6E, 0xA5, 0x1A, 0x31, 0xAE, 0x92, 0x2A, 0x3E,
+ 0x74, 0x86, 0x48, 0x89, 0x54, 0x0A, 0x48, 0xDB,
+ 0x27, 0xD7, 0xE4, 0xC9, 0x03, 0x11, 0x63, 0x8B,
+ 0x22, 0x4B, 0xF0, 0x20, 0x1B, 0x50, 0x18, 0x91,
+ 0x75, 0x48, 0x48, 0x11, 0x3C, 0x26, 0x61, 0x08,
+ 0xD0, 0xAD, 0xB1, 0x3D, 0xB7, 0x19, 0x09, 0xC7
+ },
+ {
+ 0x58, 0x98, 0x3C, 0x21, 0x43, 0x3D, 0x95, 0x0C,
+ 0xAA, 0x23, 0xE4, 0xBC, 0x18, 0x54, 0x3B, 0x8E,
+ 0x60, 0x1C, 0x20, 0x43, 0x18, 0x53, 0x21, 0x52,
+ 0xDA, 0xF5, 0xE1, 0x59, 0xA0, 0xCD, 0x14, 0x80,
+ 0x18, 0x3D, 0x29, 0x28, 0x5C, 0x05, 0xF1, 0x29,
+ 0xCB, 0x0C, 0xC3, 0x16, 0x46, 0x87, 0x92, 0x80,
+ 0x86, 0xFF, 0xE3, 0x80, 0x15, 0x8D, 0xF1, 0xD3,
+ 0x94, 0xC6, 0xAC, 0x0D, 0x42, 0x88, 0xBC, 0xA8
+ },
+ {
+ 0x81, 0x00, 0xA8, 0xDC, 0x52, 0x8D, 0x2B, 0x68,
+ 0x2A, 0xB4, 0x25, 0x08, 0x01, 0xBA, 0x33, 0xF0,
+ 0x2A, 0x3E, 0x94, 0xC5, 0x4D, 0xAC, 0x0A, 0xE1,
+ 0x48, 0x2A, 0xA2, 0x1F, 0x51, 0xEF, 0x3A, 0x82,
+ 0xF3, 0x80, 0x7E, 0x6F, 0xAC, 0xB0, 0xAE, 0xB0,
+ 0x59, 0x47, 0xBF, 0x7A, 0xA2, 0xAD, 0xCB, 0x03,
+ 0x43, 0x56, 0xF9, 0x0F, 0xA4, 0x56, 0x0E, 0xDE,
+ 0x02, 0x20, 0x1A, 0x37, 0xE4, 0x11, 0xEC, 0x1A
+ },
+ {
+ 0x07, 0x02, 0x5F, 0x1B, 0xB6, 0xC7, 0x84, 0xF3,
+ 0xFE, 0x49, 0xDE, 0x5C, 0x14, 0xB9, 0x36, 0xA5,
+ 0xAC, 0xAC, 0xAC, 0xAA, 0xB3, 0x3F, 0x6A, 0xC4,
+ 0xD0, 0xE0, 0x0A, 0xB6, 0xA1, 0x24, 0x83, 0xD6,
+ 0xBE, 0xC0, 0x0B, 0x4F, 0xE6, 0x7C, 0x7C, 0xA5,
+ 0xCC, 0x50, 0x8C, 0x2A, 0x53, 0xEF, 0xB5, 0xBF,
+ 0xA5, 0x39, 0x87, 0x69, 0xD8, 0x43, 0xFF, 0x0D,
+ 0x9E, 0x8B, 0x14, 0xD3, 0x6A, 0x01, 0xA7, 0x7F
+ },
+ {
+ 0xBA, 0x6A, 0xEF, 0xD9, 0x72, 0xB6, 0x18, 0x6E,
+ 0x02, 0x7A, 0x76, 0x27, 0x3A, 0x4A, 0x72, 0x33,
+ 0x21, 0xA3, 0xF5, 0x80, 0xCF, 0xA8, 0x94, 0xDA,
+ 0x5A, 0x9C, 0xE8, 0xE7, 0x21, 0xC8, 0x28, 0x55,
+ 0x2C, 0x64, 0xDA, 0xCE, 0xE3, 0xA7, 0xFD, 0x2D,
+ 0x74, 0x3B, 0x5C, 0x35, 0xAD, 0x0C, 0x8E, 0xFA,
+ 0x71, 0xF8, 0xCE, 0x99, 0xBF, 0x96, 0x33, 0x47,
+ 0x10, 0xE2, 0xC2, 0x34, 0x6E, 0x8F, 0x3C, 0x52
+ },
+ {
+ 0xE0, 0x72, 0x1E, 0x02, 0x51, 0x7A, 0xED, 0xFA,
+ 0x4E, 0x7E, 0x9B, 0xA5, 0x03, 0xE0, 0x25, 0xFD,
+ 0x46, 0xE7, 0x14, 0x56, 0x6D, 0xC8, 0x89, 0xA8,
+ 0x4C, 0xBF, 0xE5, 0x6A, 0x55, 0xDF, 0xBE, 0x2F,
+ 0xC4, 0x93, 0x8A, 0xC4, 0x12, 0x05, 0x88, 0x33,
+ 0x5D, 0xEA, 0xC8, 0xEF, 0x3F, 0xA2, 0x29, 0xAD,
+ 0xC9, 0x64, 0x7F, 0x54, 0xAD, 0x2E, 0x34, 0x72,
+ 0x23, 0x4F, 0x9B, 0x34, 0xEF, 0xC4, 0x65, 0x43
+ },
+ {
+ 0xB6, 0x29, 0x26, 0x69, 0xCC, 0xD3, 0x8D, 0x5F,
+ 0x01, 0xCA, 0xAE, 0x96, 0xBA, 0x27, 0x2C, 0x76,
+ 0xA8, 0x79, 0xA4, 0x57, 0x43, 0xAF, 0xA0, 0x72,
+ 0x5D, 0x83, 0xB9, 0xEB, 0xB2, 0x66, 0x65, 0xB7,
+ 0x31, 0xF1, 0x84, 0x8C, 0x52, 0xF1, 0x19, 0x72,
+ 0xB6, 0x64, 0x4F, 0x55, 0x4C, 0x06, 0x4F, 0xA9,
+ 0x07, 0x80, 0xDB, 0xBB, 0xF3, 0xA8, 0x9D, 0x4F,
+ 0xC3, 0x1F, 0x67, 0xDF, 0x3E, 0x58, 0x57, 0xEF
+ },
+ {
+ 0x23, 0x19, 0xE3, 0x78, 0x9C, 0x47, 0xE2, 0xDA,
+ 0xA5, 0xFE, 0x80, 0x7F, 0x61, 0xBE, 0xC2, 0xA1,
+ 0xA6, 0x53, 0x7F, 0xA0, 0x3F, 0x19, 0xFF, 0x32,
+ 0xE8, 0x7E, 0xEC, 0xBF, 0xD6, 0x4B, 0x7E, 0x0E,
+ 0x8C, 0xCF, 0xF4, 0x39, 0xAC, 0x33, 0x3B, 0x04,
+ 0x0F, 0x19, 0xB0, 0xC4, 0xDD, 0xD1, 0x1A, 0x61,
+ 0xE2, 0x4A, 0xC1, 0xFE, 0x0F, 0x10, 0xA0, 0x39,
+ 0x80, 0x6C, 0x5D, 0xCC, 0x0D, 0xA3, 0xD1, 0x15
+ },
+ {
+ 0xF5, 0x97, 0x11, 0xD4, 0x4A, 0x03, 0x1D, 0x5F,
+ 0x97, 0xA9, 0x41, 0x3C, 0x06, 0x5D, 0x1E, 0x61,
+ 0x4C, 0x41, 0x7E, 0xDE, 0x99, 0x85, 0x90, 0x32,
+ 0x5F, 0x49, 0xBA, 0xD2, 0xFD, 0x44, 0x4D, 0x3E,
+ 0x44, 0x18, 0xBE, 0x19, 0xAE, 0xC4, 0xE1, 0x14,
+ 0x49, 0xAC, 0x1A, 0x57, 0x20, 0x78, 0x98, 0xBC,
+ 0x57, 0xD7, 0x6A, 0x1B, 0xCF, 0x35, 0x66, 0x29,
+ 0x2C, 0x20, 0xC6, 0x83, 0xA5, 0xC4, 0x64, 0x8F
+ },
+ {
+ 0xDF, 0x0A, 0x9D, 0x0C, 0x21, 0x28, 0x43, 0xA6,
+ 0xA9, 0x34, 0xE3, 0x90, 0x2B, 0x2D, 0xD3, 0x0D,
+ 0x17, 0xFB, 0xA5, 0xF9, 0x69, 0xD2, 0x03, 0x0B,
+ 0x12, 0xA5, 0x46, 0xD8, 0xA6, 0xA4, 0x5E, 0x80,
+ 0xCF, 0x56, 0x35, 0xF0, 0x71, 0xF0, 0x45, 0x2E,
+ 0x9C, 0x91, 0x92, 0x75, 0xDA, 0x99, 0xBE, 0xD5,
+ 0x1E, 0xB1, 0x17, 0x3C, 0x1A, 0xF0, 0x51, 0x87,
+ 0x26, 0xB7, 0x5B, 0x0E, 0xC3, 0xBA, 0xE2, 0xB5
+ },
+ {
+ 0xA3, 0xEB, 0x6E, 0x6C, 0x7B, 0xF2, 0xFB, 0x8B,
+ 0x28, 0xBF, 0xE8, 0xB1, 0x5E, 0x15, 0xBB, 0x50,
+ 0x0F, 0x78, 0x1E, 0xCC, 0x86, 0xF7, 0x78, 0xC3,
+ 0xA4, 0xE6, 0x55, 0xFC, 0x58, 0x69, 0xBF, 0x28,
+ 0x46, 0xA2, 0x45, 0xD4, 0xE3, 0x3B, 0x7B, 0x14,
+ 0x43, 0x6A, 0x17, 0xE6, 0x3B, 0xE7, 0x9B, 0x36,
+ 0x65, 0x5C, 0x22, 0x6A, 0x50, 0xFF, 0xBC, 0x71,
+ 0x24, 0x20, 0x7B, 0x02, 0x02, 0x34, 0x2D, 0xB5
+ },
+ {
+ 0x56, 0xD4, 0xCB, 0xCD, 0x07, 0x05, 0x63, 0x42,
+ 0x6A, 0x01, 0x70, 0x69, 0x42, 0x5C, 0x2C, 0xD2,
+ 0xAE, 0x54, 0x06, 0x68, 0x28, 0x7A, 0x5F, 0xB9,
+ 0xDA, 0xC4, 0x32, 0xEB, 0x8A, 0xB1, 0xA3, 0x53,
+ 0xA3, 0x0F, 0x2F, 0xE1, 0xF4, 0x0D, 0x83, 0x33,
+ 0x3A, 0xFE, 0x69, 0x6A, 0x26, 0x77, 0x95, 0x40,
+ 0x8A, 0x92, 0xFE, 0x7D, 0xA0, 0x7A, 0x0C, 0x18,
+ 0x14, 0xCF, 0x77, 0xF3, 0x6E, 0x10, 0x5E, 0xE8
+ },
+ {
+ 0xE5, 0x9B, 0x99, 0x87, 0xD4, 0x28, 0xB3, 0xED,
+ 0xA3, 0x7D, 0x80, 0xAB, 0xDB, 0x16, 0xCD, 0x2B,
+ 0x0A, 0xEF, 0x67, 0x4C, 0x2B, 0x1D, 0xDA, 0x44,
+ 0x32, 0xEA, 0x91, 0xEE, 0x6C, 0x93, 0x5C, 0x68,
+ 0x4B, 0x48, 0xB4, 0x42, 0x8A, 0x8C, 0xC7, 0x40,
+ 0xE5, 0x79, 0xA3, 0x0D, 0xEF, 0xF3, 0x5A, 0x80,
+ 0x30, 0x13, 0x82, 0x0D, 0xD2, 0x3F, 0x14, 0xAE,
+ 0x1D, 0x84, 0x13, 0xB5, 0xC8, 0x67, 0x2A, 0xEC
+ },
+ {
+ 0xCD, 0x9F, 0xCC, 0x99, 0xF9, 0x9D, 0x4C, 0xC1,
+ 0x6D, 0x03, 0x19, 0x00, 0xB2, 0xA7, 0x36, 0xE1,
+ 0x50, 0x8D, 0xB4, 0xB5, 0x86, 0x81, 0x4E, 0x63,
+ 0x45, 0x85, 0x7F, 0x35, 0x4A, 0x70, 0xCC, 0xEC,
+ 0xB1, 0xDF, 0x3B, 0x50, 0xA1, 0x9A, 0xDA, 0xF4,
+ 0x3C, 0x27, 0x8E, 0xFA, 0x42, 0x3F, 0xF4, 0xBB,
+ 0x6C, 0x52, 0x3E, 0xC7, 0xFD, 0x78, 0x59, 0xB9,
+ 0x7B, 0x16, 0x8A, 0x7E, 0xBF, 0xF8, 0x46, 0x7C
+ },
+ {
+ 0x06, 0x02, 0x18, 0x5D, 0x8C, 0x3A, 0x78, 0x73,
+ 0x8B, 0x99, 0x16, 0x4B, 0x8B, 0xC6, 0xFF, 0xB2,
+ 0x1C, 0x7D, 0xEB, 0xEB, 0xBF, 0x80, 0x63, 0x72,
+ 0xE0, 0xDA, 0x44, 0xD1, 0x21, 0x54, 0x55, 0x97,
+ 0xB9, 0xC6, 0x62, 0xA2, 0x55, 0xDC, 0x31, 0x54,
+ 0x2C, 0xF9, 0x95, 0xEC, 0xBE, 0x6A, 0x50, 0xFB,
+ 0x5E, 0x6E, 0x0E, 0xE4, 0xEF, 0x24, 0x0F, 0xE5,
+ 0x57, 0xED, 0xED, 0x11, 0x88, 0x08, 0x7E, 0x86
+ },
+ {
+ 0xC0, 0x8A, 0xFA, 0x5B, 0x92, 0x7B, 0xF0, 0x80,
+ 0x97, 0xAF, 0xC5, 0xFF, 0xF9, 0xCA, 0x4E, 0x78,
+ 0x00, 0x12, 0x5C, 0x1F, 0x52, 0xF2, 0xAF, 0x35,
+ 0x53, 0xFA, 0x2B, 0x89, 0xE1, 0xE3, 0x01, 0x5C,
+ 0x4F, 0x87, 0xD5, 0xE0, 0xA4, 0x89, 0x56, 0xAD,
+ 0x31, 0x45, 0x0B, 0x08, 0x3D, 0xAD, 0x14, 0x7F,
+ 0xFB, 0x5E, 0xC0, 0x34, 0x34, 0xA2, 0x68, 0x30,
+ 0xCF, 0x37, 0xD1, 0x03, 0xAB, 0x50, 0xC5, 0xDA
+ },
+ {
+ 0x36, 0xF1, 0xE1, 0xC1, 0x1D, 0x6E, 0xF6, 0xBC,
+ 0x3B, 0x53, 0x6D, 0x50, 0x5D, 0x54, 0x4A, 0x87,
+ 0x15, 0x22, 0xC5, 0xC2, 0xA2, 0x53, 0x06, 0x7E,
+ 0xC9, 0x93, 0x3B, 0x6E, 0xC2, 0x54, 0x64, 0xDA,
+ 0xF9, 0x85, 0x52, 0x5F, 0x5B, 0x95, 0x60, 0xA1,
+ 0x6D, 0x89, 0x02, 0x59, 0xAC, 0x1B, 0xB5, 0xCC,
+ 0x67, 0xC0, 0xC4, 0x69, 0xCD, 0xE1, 0x33, 0xDE,
+ 0xF0, 0x00, 0xEA, 0x1D, 0x68, 0x6F, 0x4F, 0x5D
+ },
+ {
+ 0xBF, 0x2A, 0xB2, 0xE2, 0x47, 0x0F, 0x54, 0x38,
+ 0xC3, 0xB6, 0x89, 0xE6, 0x6E, 0x76, 0x86, 0xFF,
+ 0xFA, 0x0C, 0xB1, 0xE1, 0x79, 0x8A, 0xD3, 0xA8,
+ 0x6F, 0xF9, 0x90, 0x75, 0xBF, 0x61, 0x38, 0xE3,
+ 0x3D, 0x9C, 0x0C, 0xE5, 0x9A, 0xFB, 0x24, 0xAC,
+ 0x67, 0xA0, 0x2A, 0xF3, 0x44, 0x28, 0x19, 0x1A,
+ 0x9A, 0x0A, 0x60, 0x41, 0xC0, 0x74, 0x71, 0xB7,
+ 0xC3, 0xB1, 0xA7, 0x52, 0xD6, 0xFC, 0x0B, 0x8B
+ },
+ {
+ 0xD4, 0x00, 0x60, 0x1F, 0x97, 0x28, 0xCC, 0xC4,
+ 0xC9, 0x23, 0x42, 0xD9, 0x78, 0x7D, 0x8D, 0x28,
+ 0xAB, 0x32, 0x3A, 0xF3, 0x75, 0xCA, 0x56, 0x24,
+ 0xB4, 0xBB, 0x91, 0xD1, 0x72, 0x71, 0xFB, 0xAE,
+ 0x86, 0x2E, 0x41, 0x3B, 0xE7, 0x3F, 0x1F, 0x68,
+ 0xE6, 0x15, 0xB8, 0xC5, 0xC3, 0x91, 0xBE, 0x0D,
+ 0xBD, 0x91, 0x44, 0x74, 0x6E, 0xB3, 0x39, 0xAD,
+ 0x54, 0x15, 0x47, 0xBA, 0x9C, 0x46, 0x8A, 0x17
+ },
+ {
+ 0x79, 0xFE, 0x2F, 0xE1, 0x57, 0xEB, 0x85, 0xA0,
+ 0x38, 0xAB, 0xB8, 0xEB, 0xBC, 0x64, 0x77, 0x31,
+ 0xD2, 0xC8, 0x3F, 0x51, 0xB0, 0xAC, 0x6E, 0xE1,
+ 0x4A, 0xA2, 0x84, 0xCB, 0x6A, 0x35, 0x49, 0xA4,
+ 0xDC, 0xCE, 0xB3, 0x00, 0x74, 0x0A, 0x82, 0x5F,
+ 0x52, 0xF5, 0xFB, 0x30, 0xB0, 0x3B, 0x8C, 0x4D,
+ 0x8B, 0x0F, 0x4A, 0xA6, 0x7A, 0x63, 0xF4, 0xA9,
+ 0x4E, 0x33, 0x03, 0xC4, 0xED, 0xA4, 0xC0, 0x2B
+ },
+ {
+ 0x75, 0x35, 0x13, 0x13, 0xB5, 0x2A, 0x85, 0x29,
+ 0x29, 0x8D, 0x8C, 0x18, 0x6B, 0x17, 0x68, 0x66,
+ 0x6D, 0xCC, 0xA8, 0x59, 0x53, 0x17, 0xD7, 0xA4,
+ 0x81, 0x6E, 0xB8, 0x8C, 0x06, 0x20, 0x20, 0xC0,
+ 0xC8, 0xEF, 0xC5, 0x54, 0xBB, 0x34, 0x1B, 0x64,
+ 0x68, 0x8D, 0xB5, 0xCC, 0xAF, 0xC3, 0x5F, 0x3C,
+ 0x3C, 0xD0, 0x9D, 0x65, 0x64, 0xB3, 0x6D, 0x7B,
+ 0x04, 0xA2, 0x48, 0xE1, 0x46, 0x98, 0x0D, 0x4B
+ },
+ {
+ 0xE3, 0x12, 0x8B, 0x1D, 0x31, 0x1D, 0x02, 0x17,
+ 0x9D, 0x7F, 0x25, 0xF9, 0x7A, 0x5A, 0x8B, 0xEE,
+ 0x2C, 0xC8, 0xC8, 0x63, 0x03, 0x64, 0x4F, 0xCD,
+ 0x66, 0x4E, 0x15, 0x7D, 0x1F, 0xEF, 0x00, 0xF2,
+ 0x3E, 0x46, 0xF9, 0xA5, 0xE8, 0xE5, 0xC8, 0x90,
+ 0xCE, 0x56, 0x5B, 0xB6, 0xAB, 0xD4, 0x30, 0x2C,
+ 0xE0, 0x64, 0x69, 0xD5, 0x2A, 0x5B, 0xD5, 0x3E,
+ 0x1C, 0x5A, 0x54, 0xD0, 0x46, 0x49, 0xDC, 0x03
+ },
+ {
+ 0xC2, 0x38, 0x2A, 0x72, 0xD2, 0xD3, 0xAC, 0xE9,
+ 0xD5, 0x93, 0x3D, 0x00, 0xB6, 0x08, 0x27, 0xED,
+ 0x38, 0x0C, 0xDA, 0x08, 0xD0, 0xBA, 0x5F, 0x6D,
+ 0xD4, 0x1E, 0x29, 0xEE, 0x6D, 0xBE, 0x8E, 0xCB,
+ 0x92, 0x35, 0xF0, 0x6B, 0xE9, 0x5D, 0x83, 0xB6,
+ 0x81, 0x6A, 0x2F, 0xB7, 0xA5, 0xAD, 0x47, 0x03,
+ 0x5E, 0x8A, 0x4B, 0x69, 0xA4, 0x88, 0x4B, 0x99,
+ 0xE4, 0xBE, 0xCE, 0x58, 0xCA, 0xB2, 0x5D, 0x44
+ },
+ {
+ 0x6B, 0x1C, 0x69, 0x46, 0x0B, 0xBD, 0x50, 0xAC,
+ 0x2E, 0xD6, 0xF3, 0x2E, 0x6E, 0x88, 0x7C, 0xFE,
+ 0xD4, 0x07, 0xD4, 0x7D, 0xCF, 0x0A, 0xAA, 0x60,
+ 0x38, 0x7F, 0xE3, 0x20, 0xD7, 0x80, 0xBD, 0x03,
+ 0xEA, 0xB6, 0xD7, 0xBA, 0xEB, 0x2A, 0x07, 0xD1,
+ 0x0C, 0xD5, 0x52, 0xA3, 0x00, 0x34, 0x13, 0x54,
+ 0xEA, 0x9A, 0x5F, 0x03, 0x18, 0x3A, 0x62, 0x3F,
+ 0x92, 0xA2, 0xD4, 0xD9, 0xF0, 0x09, 0x26, 0xAF
+ },
+ {
+ 0x6C, 0xDA, 0x20, 0x6C, 0x80, 0xCD, 0xC9, 0xC4,
+ 0x4B, 0xA9, 0x90, 0xE0, 0x32, 0x8C, 0x31, 0x4F,
+ 0x81, 0x9B, 0x14, 0x2D, 0x00, 0x63, 0x04, 0x04,
+ 0xC4, 0x8C, 0x05, 0xDC, 0x76, 0xD1, 0xB0, 0x0C,
+ 0xE4, 0xD7, 0x2F, 0xC6, 0xA4, 0x8E, 0x14, 0x69,
+ 0xDD, 0xEF, 0x60, 0x94, 0x12, 0xC3, 0x64, 0x82,
+ 0x08, 0x54, 0x21, 0x4B, 0x48, 0x69, 0xAF, 0x09,
+ 0x0F, 0x00, 0xD3, 0xC1, 0xBA, 0x44, 0x3E, 0x1B
+ },
+ {
+ 0x7F, 0xFC, 0x8C, 0x26, 0xFB, 0xD6, 0xA0, 0xF7,
+ 0xA6, 0x09, 0xE6, 0xE1, 0x93, 0x9F, 0x6A, 0x9E,
+ 0xDF, 0x1B, 0x0B, 0x06, 0x66, 0x41, 0xFB, 0x76,
+ 0xC4, 0xF9, 0x60, 0x2E, 0xD7, 0x48, 0xD1, 0x16,
+ 0x02, 0x49, 0x6B, 0x35, 0x35, 0x5B, 0x1A, 0xA2,
+ 0x55, 0x85, 0x0A, 0x50, 0x9D, 0x2F, 0x8E, 0xE1,
+ 0x8C, 0x8F, 0x3E, 0x1D, 0x7D, 0xCB, 0xC3, 0x7A,
+ 0x13, 0x65, 0x98, 0xF5, 0x6A, 0x59, 0xED, 0x17
+ },
+ {
+ 0x70, 0xDE, 0x1F, 0x08, 0xDD, 0x4E, 0x09, 0xD5,
+ 0xFC, 0x15, 0x1F, 0x17, 0xFC, 0x99, 0x1A, 0x23,
+ 0xAB, 0xFC, 0x05, 0x10, 0x42, 0x90, 0xD5, 0x04,
+ 0x68, 0x88, 0x2E, 0xFA, 0xF5, 0x82, 0xB6, 0xEC,
+ 0x2F, 0x14, 0xF5, 0x77, 0xC0, 0xD6, 0x8C, 0x3A,
+ 0xD0, 0x66, 0x26, 0x91, 0x6E, 0x3C, 0x86, 0xE6,
+ 0xDA, 0xAB, 0x6C, 0x53, 0xE5, 0x16, 0x3E, 0x82,
+ 0xB6, 0xBD, 0x0C, 0xE4, 0x9F, 0xC0, 0xD8, 0xDF
+ },
+ {
+ 0x4F, 0x81, 0x93, 0x57, 0x56, 0xED, 0x35, 0xEE,
+ 0x20, 0x58, 0xEE, 0x0C, 0x6A, 0x61, 0x10, 0xD6,
+ 0xFA, 0xC5, 0xCB, 0x6A, 0x4F, 0x46, 0xAA, 0x94,
+ 0x11, 0x60, 0x3F, 0x99, 0x96, 0x58, 0x23, 0xB6,
+ 0xDA, 0x48, 0x38, 0x27, 0x6C, 0x5C, 0x06, 0xBC,
+ 0x78, 0x80, 0xE3, 0x76, 0xD9, 0x27, 0x58, 0x36,
+ 0x9E, 0xE7, 0x30, 0x5B, 0xCE, 0xC8, 0xD3, 0xCF,
+ 0xD2, 0x8C, 0xCA, 0xBB, 0x7B, 0x4F, 0x05, 0x79
+ },
+ {
+ 0xAB, 0xCB, 0x61, 0xCB, 0x36, 0x83, 0xD1, 0x8F,
+ 0x27, 0xAD, 0x52, 0x79, 0x08, 0xED, 0x2D, 0x32,
+ 0xA0, 0x42, 0x6C, 0xB7, 0xBB, 0x4B, 0xF1, 0x80,
+ 0x61, 0x90, 0x3A, 0x7D, 0xC4, 0x2E, 0x7E, 0x76,
+ 0xF9, 0x82, 0x38, 0x23, 0x04, 0xD1, 0x8A, 0xF8,
+ 0xC8, 0x0D, 0x91, 0xDD, 0x58, 0xDD, 0x47, 0xAF,
+ 0x76, 0xF8, 0xE2, 0xC3, 0x6E, 0x28, 0xAF, 0x24,
+ 0x76, 0xB4, 0xBC, 0xCF, 0x82, 0xE8, 0x9F, 0xDF
+ },
+ {
+ 0x02, 0xD2, 0x61, 0xAD, 0x56, 0xA5, 0x26, 0x33,
+ 0x1B, 0x64, 0x3D, 0xD2, 0x18, 0x6D, 0xE9, 0xA8,
+ 0x2E, 0x72, 0xA5, 0x82, 0x23, 0xCD, 0x1E, 0x72,
+ 0x36, 0x86, 0xC5, 0x3D, 0x86, 0x9B, 0x83, 0xB9,
+ 0x46, 0x32, 0xB7, 0xB6, 0x47, 0xAB, 0x2A, 0xFC,
+ 0x0D, 0x52, 0x2E, 0x29, 0xDA, 0x3A, 0x56, 0x15,
+ 0xB7, 0x41, 0xD8, 0x28, 0x52, 0xE0, 0xDF, 0x41,
+ 0xB6, 0x60, 0x07, 0xDB, 0xCB, 0xA9, 0x05, 0x43
+ },
+ {
+ 0xC5, 0x83, 0x27, 0x41, 0xFA, 0x30, 0xC5, 0x43,
+ 0x68, 0x23, 0x01, 0x53, 0x83, 0xD2, 0x97, 0xFF,
+ 0x4C, 0x4A, 0x5D, 0x72, 0x76, 0xC3, 0xF9, 0x02,
+ 0x12, 0x20, 0x66, 0xE0, 0x4B, 0xE5, 0x43, 0x1B,
+ 0x1A, 0x85, 0xFA, 0xF7, 0x3B, 0x91, 0x84, 0x34,
+ 0xF9, 0x30, 0x09, 0x63, 0xD1, 0xDE, 0xA9, 0xE8,
+ 0xAC, 0x39, 0x24, 0xEF, 0x49, 0x02, 0x26, 0xED,
+ 0xEE, 0xA5, 0xF7, 0x43, 0xE4, 0x10, 0x66, 0x9F
+ },
+ {
+ 0xCF, 0xAE, 0xAB, 0x26, 0x8C, 0xD0, 0x75, 0xA5,
+ 0xA6, 0xAE, 0xD5, 0x15, 0x02, 0x3A, 0x03, 0x2D,
+ 0x54, 0xF2, 0xF2, 0xFF, 0x73, 0x3C, 0xE0, 0xCB,
+ 0xC7, 0x8D, 0xB5, 0x1D, 0xB4, 0x50, 0x4D, 0x67,
+ 0x59, 0x23, 0xF8, 0x27, 0x46, 0xD6, 0x59, 0x46,
+ 0x06, 0xAD, 0x5D, 0x67, 0x73, 0x4B, 0x11, 0xA6,
+ 0x7C, 0xC6, 0xA4, 0x68, 0xC2, 0x03, 0x2E, 0x43,
+ 0xCA, 0x1A, 0x94, 0xC6, 0x27, 0x3A, 0x98, 0x5E
+ },
+ {
+ 0x86, 0x08, 0x50, 0xF9, 0x2E, 0xB2, 0x68, 0x27,
+ 0x2B, 0x67, 0xD1, 0x33, 0x60, 0x9B, 0xD6, 0x4E,
+ 0x34, 0xF6, 0x1B, 0xF0, 0x3F, 0x4C, 0x17, 0x38,
+ 0x64, 0x5C, 0x17, 0xFE, 0xC8, 0x18, 0x46, 0x5D,
+ 0x7E, 0xCD, 0x2B, 0xE2, 0x90, 0x76, 0x41, 0x13,
+ 0x00, 0x25, 0xFD, 0xA7, 0x94, 0x70, 0xAB, 0x73,
+ 0x16, 0x46, 0xE7, 0xF6, 0x94, 0x40, 0xE8, 0x36,
+ 0x7E, 0xA7, 0x6A, 0xC4, 0xCE, 0xE8, 0xA1, 0xDF
+ },
+ {
+ 0x84, 0xB1, 0x54, 0xED, 0x29, 0xBB, 0xED, 0xEF,
+ 0xA6, 0x48, 0x28, 0x68, 0x39, 0x04, 0x6F, 0x4B,
+ 0x5A, 0xA3, 0x44, 0x30, 0xE2, 0xD6, 0x7F, 0x74,
+ 0x96, 0xE4, 0xC3, 0x9F, 0x2C, 0x7E, 0xA7, 0x89,
+ 0x95, 0xF6, 0x9E, 0x12, 0x92, 0x20, 0x00, 0x16,
+ 0xF1, 0x6A, 0xC3, 0xB3, 0x77, 0x00, 0xE6, 0xC7,
+ 0xE7, 0x86, 0x1A, 0xFC, 0x39, 0x6B, 0x64, 0xA5,
+ 0x9A, 0x1D, 0xBF, 0x47, 0xA5, 0x5C, 0x4B, 0xBC
+ },
+ {
+ 0xAE, 0xEE, 0xC2, 0x60, 0xA5, 0xD8, 0xEF, 0xF5,
+ 0xCC, 0xAB, 0x8B, 0x95, 0xDA, 0x43, 0x5A, 0x63,
+ 0xED, 0x7A, 0x21, 0xEA, 0x7F, 0xC7, 0x55, 0x94,
+ 0x13, 0xFD, 0x61, 0x7E, 0x33, 0x60, 0x9F, 0x8C,
+ 0x29, 0x0E, 0x64, 0xBB, 0xAC, 0xC5, 0x28, 0xF6,
+ 0xC0, 0x80, 0x26, 0x22, 0x88, 0xB0, 0xF0, 0xA3,
+ 0x21, 0x9B, 0xE2, 0x23, 0xC9, 0x91, 0xBE, 0xE9,
+ 0x2E, 0x72, 0x34, 0x95, 0x93, 0xE6, 0x76, 0x38
+ },
+ {
+ 0x8A, 0xD7, 0x8A, 0x9F, 0x26, 0x60, 0x1D, 0x12,
+ 0x7E, 0x8D, 0x2F, 0x2F, 0x97, 0x6E, 0x63, 0xD1,
+ 0x9A, 0x05, 0x4A, 0x17, 0xDC, 0xF5, 0x9E, 0x0F,
+ 0x01, 0x3A, 0xB5, 0x4A, 0x68, 0x87, 0xBB, 0xDF,
+ 0xFD, 0xE7, 0xAA, 0xAE, 0x11, 0x7E, 0x0F, 0xBF,
+ 0x32, 0x71, 0x01, 0x65, 0x95, 0xB9, 0xD9, 0xC7,
+ 0x12, 0xC0, 0x1B, 0x2C, 0x53, 0xE9, 0x65, 0x5A,
+ 0x38, 0x2B, 0xC4, 0x52, 0x2E, 0x61, 0x66, 0x45
+ },
+ {
+ 0x89, 0x34, 0x15, 0x9D, 0xAD, 0xE1, 0xAC, 0x74,
+ 0x14, 0x7D, 0xFA, 0x28, 0x2C, 0x75, 0x95, 0x4F,
+ 0xCE, 0xF4, 0x43, 0xEF, 0x25, 0xF8, 0x0D, 0xFE,
+ 0x9F, 0xB6, 0xEA, 0x63, 0x3B, 0x85, 0x45, 0x11,
+ 0x1D, 0x08, 0xB3, 0x4E, 0xF4, 0x3F, 0xFF, 0x17,
+ 0x02, 0x6C, 0x79, 0x64, 0xF5, 0xDE, 0xAC, 0x6D,
+ 0x2B, 0x3C, 0x29, 0xDA, 0xCF, 0x27, 0x47, 0xF0,
+ 0x22, 0xDF, 0x59, 0x67, 0xDF, 0xDC, 0x1A, 0x0A
+ },
+ {
+ 0xCD, 0x36, 0xDD, 0x0B, 0x24, 0x06, 0x14, 0xCF,
+ 0x2F, 0xA2, 0xB9, 0xE9, 0x59, 0x67, 0x9D, 0xCD,
+ 0xD7, 0x2E, 0xC0, 0xCD, 0x58, 0xA4, 0x3D, 0xA3,
+ 0x79, 0x0A, 0x92, 0xF6, 0xCD, 0xEB, 0x9E, 0x1E,
+ 0x79, 0x5E, 0x47, 0x8A, 0x0A, 0x47, 0xD3, 0x71,
+ 0x10, 0x0D, 0x34, 0x0C, 0x5C, 0xED, 0xCD, 0xBB,
+ 0xC9, 0xE6, 0x8B, 0x3F, 0x46, 0x08, 0x18, 0xE5,
+ 0xBD, 0xFF, 0x7B, 0x4C, 0xDA, 0x4C, 0x27, 0x44
+ },
+ {
+ 0x00, 0xDF, 0x4E, 0x09, 0x9B, 0x80, 0x71, 0x37,
+ 0xA8, 0x59, 0x90, 0xF4, 0x9D, 0x3A, 0x94, 0x31,
+ 0x5E, 0x5A, 0x5F, 0x7F, 0x7A, 0x60, 0x76, 0xB3,
+ 0x03, 0xE9, 0x6B, 0x05, 0x6F, 0xB9, 0x38, 0x00,
+ 0x11, 0x1F, 0x47, 0x96, 0x28, 0xE2, 0xF8, 0xDB,
+ 0x59, 0xAE, 0xB6, 0xAC, 0x70, 0xC3, 0xB6, 0x1F,
+ 0x51, 0xF9, 0xB4, 0x6E, 0x80, 0xFF, 0xDE, 0xAE,
+ 0x25, 0xEB, 0xDD, 0xB4, 0xAF, 0x6C, 0xB4, 0xEE
+ },
+ {
+ 0x2B, 0x9C, 0x95, 0x5E, 0x6C, 0xAE, 0xD4, 0xB7,
+ 0xC9, 0xE2, 0x46, 0xB8, 0x6F, 0x9A, 0x17, 0x26,
+ 0xE8, 0x10, 0xC5, 0x9D, 0x12, 0x6C, 0xEE, 0x66,
+ 0xED, 0x71, 0xBF, 0x01, 0x5B, 0x83, 0x55, 0x8A,
+ 0x4B, 0x6D, 0x84, 0xD1, 0x8D, 0xC3, 0xFF, 0x46,
+ 0x20, 0xC2, 0xFF, 0xB7, 0x22, 0x35, 0x9F, 0xDE,
+ 0xF8, 0x5B, 0xA0, 0xD4, 0xE2, 0xD2, 0x2E, 0xCB,
+ 0xE0, 0xED, 0x78, 0x4F, 0x99, 0xAF, 0xE5, 0x87
+ },
+ {
+ 0x18, 0x1D, 0xF0, 0xA2, 0x61, 0xA2, 0xF7, 0xD2,
+ 0x9E, 0xA5, 0xA1, 0x57, 0x72, 0x71, 0x51, 0x05,
+ 0xD4, 0x50, 0xA4, 0xB6, 0xC2, 0x36, 0xF6, 0x99,
+ 0xF4, 0x62, 0xD6, 0x0C, 0xA7, 0x64, 0x87, 0xFE,
+ 0xED, 0xFC, 0x9F, 0x5E, 0xB9, 0x2D, 0xF8, 0x38,
+ 0xE8, 0xFB, 0x5D, 0xC3, 0x69, 0x4E, 0x84, 0xC5,
+ 0xE0, 0xF4, 0xA1, 0x0B, 0x76, 0x1F, 0x50, 0x67,
+ 0x62, 0xBE, 0x05, 0x2C, 0x74, 0x5A, 0x6E, 0xE8
+ },
+ {
+ 0x21, 0xFB, 0x20, 0x34, 0x58, 0xBF, 0x3A, 0x7E,
+ 0x9A, 0x80, 0x43, 0x9F, 0x9A, 0x90, 0x28, 0x99,
+ 0xCD, 0x5D, 0xE0, 0x13, 0x9D, 0xFD, 0x56, 0xF7,
+ 0x11, 0x0C, 0x9D, 0xEC, 0x84, 0x37, 0xB2, 0x6B,
+ 0xDA, 0x63, 0xDE, 0x2F, 0x56, 0x59, 0x26, 0xD8,
+ 0x5E, 0xDB, 0x1D, 0x6C, 0x68, 0x25, 0x66, 0x97,
+ 0x43, 0xDD, 0x99, 0x92, 0x65, 0x3D, 0x13, 0x97,
+ 0x95, 0x44, 0xD5, 0xDC, 0x82, 0x28, 0xBF, 0xAA
+ },
+ {
+ 0xEF, 0x02, 0x1F, 0x29, 0xC5, 0xFF, 0xB8, 0x30,
+ 0xE6, 0x4B, 0x9A, 0xA9, 0x05, 0x8D, 0xD6, 0x60,
+ 0xFD, 0x2F, 0xCB, 0x81, 0xC4, 0x97, 0xA7, 0xE6,
+ 0x98, 0xBC, 0xFB, 0xF5, 0x9D, 0xE5, 0xAD, 0x4A,
+ 0x86, 0xFF, 0x93, 0xC1, 0x0A, 0x4B, 0x9D, 0x1A,
+ 0xE5, 0x77, 0x47, 0x25, 0xF9, 0x07, 0x2D, 0xCD,
+ 0xE9, 0xE1, 0xF1, 0x99, 0xBA, 0xB9, 0x1F, 0x8B,
+ 0xFF, 0x92, 0x18, 0x64, 0xAA, 0x50, 0x2E, 0xEE
+ },
+ {
+ 0xB3, 0xCF, 0xDA, 0x40, 0x52, 0x6B, 0x7F, 0x1D,
+ 0x37, 0x56, 0x9B, 0xDF, 0xCD, 0xF9, 0x11, 0xE5,
+ 0xA6, 0xEF, 0xE6, 0xB2, 0xEC, 0x90, 0xA0, 0x45,
+ 0x4C, 0x47, 0xB2, 0xC0, 0x46, 0xBF, 0x13, 0x0F,
+ 0xC3, 0xB3, 0x52, 0xB3, 0x4D, 0xF4, 0x81, 0x3D,
+ 0x48, 0xD3, 0x3A, 0xB8, 0xE2, 0x69, 0xB6, 0x9B,
+ 0x07, 0x56, 0x76, 0xCB, 0x6D, 0x00, 0xA8, 0xDC,
+ 0xF9, 0xE1, 0xF9, 0x67, 0xEC, 0x19, 0x1B, 0x2C
+ },
+ {
+ 0xB4, 0xC6, 0xC3, 0xB2, 0x67, 0x07, 0x1E, 0xEF,
+ 0xB9, 0xC8, 0xC7, 0x2E, 0x0E, 0x2B, 0x94, 0x12,
+ 0x93, 0x64, 0x1F, 0x86, 0x73, 0xCB, 0x70, 0xC1,
+ 0xCC, 0x26, 0xAD, 0x1E, 0x73, 0xCF, 0x14, 0x17,
+ 0x55, 0x86, 0x0A, 0xD1, 0x9B, 0x34, 0xC2, 0xF3,
+ 0x4E, 0xD3, 0x5B, 0xB5, 0x2E, 0xC4, 0x50, 0x7C,
+ 0xC1, 0xFE, 0x59, 0x04, 0x77, 0x43, 0xA5, 0xF0,
+ 0xC6, 0xFE, 0xBD, 0xE6, 0x25, 0xE2, 0x60, 0x91
+ },
+ {
+ 0x57, 0xA3, 0x4F, 0x2B, 0xCC, 0xA6, 0x0D, 0x4B,
+ 0x85, 0x10, 0x3B, 0x83, 0x0C, 0x9D, 0x79, 0x52,
+ 0xA4, 0x16, 0xBE, 0x52, 0x63, 0xAE, 0x42, 0x9C,
+ 0x9E, 0x5E, 0x53, 0xFE, 0x85, 0x90, 0xA8, 0xF7,
+ 0x8E, 0xC6, 0x5A, 0x51, 0x10, 0x9E, 0xA8, 0x5D,
+ 0xCD, 0xF7, 0xB6, 0x22, 0x3F, 0x9F, 0x2B, 0x34,
+ 0x05, 0x39, 0xFA, 0xD8, 0x19, 0x23, 0xDB, 0xF8,
+ 0xED, 0xAB, 0xF9, 0x51, 0x29, 0xE4, 0xDF, 0xF6
+ },
+ {
+ 0x9C, 0xF4, 0x66, 0x62, 0xFC, 0xD6, 0x1A, 0x23,
+ 0x22, 0x77, 0xB6, 0x85, 0x66, 0x3B, 0x8B, 0x5D,
+ 0xA8, 0x32, 0xDF, 0xD9, 0xA3, 0xB8, 0xCC, 0xFE,
+ 0xEC, 0x99, 0x3E, 0xC6, 0xAC, 0x41, 0x5A, 0xD0,
+ 0x7E, 0x04, 0x8A, 0xDF, 0xE4, 0x14, 0xDF, 0x27,
+ 0x27, 0x70, 0xDB, 0xA8, 0x67, 0xDA, 0x5C, 0x12,
+ 0x24, 0xC6, 0xFD, 0x0A, 0xA0, 0xC2, 0x18, 0x7D,
+ 0x42, 0x6A, 0xC6, 0x47, 0xE9, 0x88, 0x73, 0x61
+ },
+ {
+ 0x5C, 0xE1, 0x04, 0x2A, 0xB4, 0xD5, 0x42, 0xC2,
+ 0xF9, 0xEE, 0x9D, 0x17, 0x26, 0x2A, 0xF8, 0x16,
+ 0x40, 0x98, 0x93, 0x5B, 0xEF, 0x17, 0x3D, 0x0E,
+ 0x18, 0x48, 0x9B, 0x04, 0x84, 0x17, 0x46, 0xCD,
+ 0x2F, 0x2D, 0xF8, 0x66, 0xBD, 0x7D, 0xA6, 0xE5,
+ 0xEF, 0x90, 0x24, 0xC6, 0x48, 0x02, 0x3E, 0xC7,
+ 0x23, 0xAB, 0x9C, 0x62, 0xFD, 0x80, 0x28, 0x57,
+ 0x39, 0xD8, 0x4F, 0x15, 0xD2, 0xAB, 0x51, 0x5A
+ },
+ {
+ 0x84, 0x88, 0x39, 0x6B, 0xD4, 0xA8, 0x72, 0x9B,
+ 0x7A, 0x47, 0x31, 0x78, 0xF2, 0x32, 0xDA, 0xDF,
+ 0x3F, 0x0F, 0x8E, 0x22, 0x67, 0x8B, 0xA5, 0xA4,
+ 0x3E, 0x04, 0x1E, 0x72, 0xDA, 0x1E, 0x2C, 0xF8,
+ 0x21, 0x94, 0xC3, 0x07, 0x20, 0x7A, 0x54, 0xCB,
+ 0x81, 0x56, 0x29, 0x33, 0x39, 0xEA, 0xEC, 0x69,
+ 0x3F, 0xF6, 0x6B, 0xFC, 0xD5, 0xEF, 0xC6, 0x5E,
+ 0x95, 0xE4, 0xEC, 0xAF, 0x54, 0x53, 0x0A, 0xBD
+ },
+ {
+ 0xF5, 0x98, 0xDA, 0x90, 0x1C, 0x38, 0x35, 0xBC,
+ 0xA5, 0x60, 0x77, 0x90, 0x37, 0xDF, 0xDE, 0x9F,
+ 0x0C, 0x51, 0xDC, 0x61, 0xC0, 0xB7, 0x60, 0xFC,
+ 0x15, 0x22, 0xD7, 0xB4, 0x70, 0xEE, 0x63, 0xF5,
+ 0xBD, 0xC6, 0x49, 0x84, 0x76, 0xE8, 0x60, 0x49,
+ 0xAD, 0x86, 0xE4, 0xE2, 0x1A, 0xF2, 0x85, 0x4A,
+ 0x98, 0x4C, 0xC9, 0x05, 0x42, 0x7D, 0x2F, 0x17,
+ 0xF6, 0x6B, 0x1F, 0x41, 0xC3, 0xDA, 0x6F, 0x61
+ },
+ {
+ 0x5F, 0x93, 0x26, 0x97, 0x98, 0xCF, 0x02, 0x13,
+ 0x21, 0x07, 0x33, 0x76, 0x60, 0xA8, 0xD7, 0xA1,
+ 0x77, 0x35, 0x4C, 0x02, 0x12, 0xEB, 0x93, 0xE5,
+ 0x55, 0xE7, 0xC3, 0x7A, 0x08, 0xAE, 0xF3, 0xD8,
+ 0xDC, 0xE0, 0x12, 0x17, 0x01, 0x1C, 0xD9, 0x65,
+ 0xC0, 0x4D, 0xD2, 0xC1, 0x05, 0xF2, 0xE2, 0xB6,
+ 0xCA, 0xE5, 0xE4, 0xE6, 0xBC, 0xAF, 0x09, 0xDF,
+ 0xBE, 0xE3, 0xE0, 0xA6, 0xA6, 0x35, 0x7C, 0x37
+ },
+ {
+ 0x0E, 0xCF, 0x58, 0x1D, 0x47, 0xBA, 0xC9, 0x23,
+ 0x09, 0x86, 0xFA, 0xAB, 0xD7, 0x0C, 0x2F, 0x5B,
+ 0x80, 0xE9, 0x10, 0x66, 0xF0, 0xEC, 0x55, 0xA8,
+ 0x42, 0x93, 0x78, 0x82, 0x28, 0x6D, 0x2C, 0xA0,
+ 0x07, 0xBB, 0x4E, 0x97, 0x3B, 0x0B, 0x09, 0x1D,
+ 0x52, 0x16, 0x7F, 0xF7, 0xC4, 0x00, 0x9C, 0x7A,
+ 0xB4, 0xAD, 0x38, 0xFF, 0xF1, 0xDC, 0xEA, 0xCD,
+ 0xB7, 0xBE, 0x81, 0xEF, 0x4A, 0x45, 0x29, 0x52
+ },
+ {
+ 0x5A, 0xEC, 0xA8, 0xAB, 0xE1, 0x52, 0x85, 0x82,
+ 0xB2, 0xA3, 0x07, 0xB4, 0x00, 0x95, 0x85, 0x49,
+ 0x8A, 0x3D, 0x46, 0x7C, 0xA6, 0x10, 0x1C, 0xB0,
+ 0xC5, 0x12, 0x6F, 0x99, 0x76, 0x05, 0x6E, 0x9F,
+ 0xFC, 0x12, 0x3C, 0xC2, 0x0C, 0x30, 0x2B, 0x2A,
+ 0x73, 0x7F, 0x49, 0x2C, 0x75, 0xD2, 0x1F, 0x01,
+ 0x51, 0x2C, 0x90, 0xCA, 0x05, 0x41, 0xDF, 0xA5,
+ 0x6E, 0x95, 0x0A, 0x32, 0x1D, 0xCB, 0x28, 0xD8
+ },
+ {
+ 0x73, 0x2F, 0xBF, 0x8F, 0x1C, 0xB2, 0xB8, 0x32,
+ 0x92, 0x63, 0xED, 0xE2, 0x78, 0x58, 0xFE, 0x46,
+ 0xF8, 0xD3, 0x35, 0x4D, 0x37, 0x6B, 0xCD, 0xA0,
+ 0x54, 0x8E, 0x7C, 0xE1, 0xFA, 0x9D, 0xD1, 0x1F,
+ 0x85, 0xEB, 0x66, 0x1F, 0xE9, 0x50, 0xB5, 0x43,
+ 0xAA, 0x63, 0x5C, 0xA4, 0xD3, 0xF0, 0x4E, 0xDE,
+ 0x5B, 0x32, 0xD6, 0xB6, 0x56, 0xE5, 0xCE, 0x1C,
+ 0x44, 0xD3, 0x5C, 0x4A, 0x6C, 0x56, 0xCF, 0xF8
+ },
+ {
+ 0xD5, 0xE9, 0x38, 0x73, 0x5D, 0x63, 0x78, 0x8C,
+ 0x80, 0x10, 0x0A, 0xEF, 0xD1, 0x86, 0x48, 0xD1,
+ 0x8C, 0xF2, 0x72, 0xF6, 0x9F, 0x20, 0xFF, 0x24,
+ 0xCF, 0xE2, 0x89, 0x5C, 0x08, 0x8A, 0xD0, 0x8B,
+ 0x01, 0x04, 0xDA, 0x16, 0x72, 0xA4, 0xEB, 0x26,
+ 0xFC, 0x52, 0x54, 0x5C, 0xC7, 0xD7, 0xA0, 0x1B,
+ 0x26, 0x6C, 0xF5, 0x46, 0xC4, 0x03, 0xC4, 0x5B,
+ 0xD1, 0x29, 0xEB, 0x41, 0xBD, 0xD9, 0x20, 0x0B
+ },
+ {
+ 0x65, 0xA2, 0x45, 0xB4, 0x93, 0x52, 0xEE, 0x29,
+ 0x7D, 0x91, 0xAF, 0x8C, 0x8B, 0xE0, 0x05, 0x28,
+ 0xAC, 0x6E, 0x04, 0x6D, 0xD8, 0x3A, 0xC7, 0xBD,
+ 0x46, 0x5A, 0x98, 0x81, 0x6D, 0xD6, 0x8F, 0x3E,
+ 0x00, 0xE1, 0xAE, 0x8F, 0x89, 0x53, 0x27, 0xA7,
+ 0xE9, 0xA8, 0xC9, 0x32, 0x65, 0x98, 0x37, 0x9A,
+ 0x29, 0xC9, 0xFC, 0x91, 0xEC, 0x0C, 0x6E, 0xEF,
+ 0x08, 0xF3, 0xE2, 0xB2, 0x16, 0xC1, 0x10, 0x08
+ },
+ {
+ 0xC9, 0x56, 0x54, 0xB6, 0x30, 0x19, 0x13, 0x0A,
+ 0xB4, 0x5D, 0xD0, 0xFB, 0x49, 0x41, 0xB9, 0x8A,
+ 0xEB, 0x3A, 0xF2, 0xA1, 0x23, 0x91, 0x3E, 0xCA,
+ 0x2C, 0xE9, 0x9B, 0x3E, 0x97, 0x41, 0x0A, 0x7B,
+ 0xF8, 0x66, 0x1C, 0xC7, 0xFB, 0xAA, 0x2B, 0xC1,
+ 0xCF, 0x2B, 0x13, 0x11, 0x3B, 0x1E, 0xD4, 0x0A,
+ 0x01, 0x18, 0xB8, 0x8E, 0x5F, 0xFF, 0xC3, 0x54,
+ 0x27, 0x59, 0xEA, 0x00, 0x7E, 0xD4, 0xC5, 0x8D
+ },
+ {
+ 0x1E, 0xB2, 0x62, 0xF3, 0x8F, 0xA4, 0x94, 0x43,
+ 0x1F, 0x01, 0x7D, 0xAD, 0x44, 0xC0, 0xDF, 0xB6,
+ 0x93, 0x24, 0xAC, 0x03, 0x2F, 0x04, 0xB6, 0x57,
+ 0xFC, 0x91, 0xA8, 0x86, 0x47, 0xBB, 0x74, 0x76,
+ 0x0F, 0x24, 0xE7, 0xC9, 0x56, 0x51, 0x4F, 0x0C,
+ 0xF0, 0x02, 0x99, 0x0B, 0x18, 0x2C, 0x16, 0x42,
+ 0xB9, 0xB2, 0x42, 0x6E, 0x96, 0xA6, 0x11, 0x87,
+ 0xE4, 0xE0, 0x12, 0xF0, 0x0E, 0x21, 0x7D, 0x84
+ },
+ {
+ 0x3B, 0x95, 0x5A, 0xEE, 0xBF, 0xA5, 0x15, 0x1A,
+ 0xC1, 0xAB, 0x8E, 0x3F, 0x5C, 0xC1, 0xE3, 0x76,
+ 0x70, 0x84, 0xC8, 0x42, 0xA5, 0x75, 0xD3, 0x62,
+ 0x69, 0x83, 0x6E, 0x97, 0x35, 0x3D, 0x41, 0x62,
+ 0x2B, 0x73, 0x1D, 0xDD, 0xCD, 0x5F, 0x26, 0x95,
+ 0x50, 0xA3, 0xA5, 0xB8, 0x7B, 0xE1, 0xE9, 0x03,
+ 0x26, 0x34, 0x0B, 0x6E, 0x0E, 0x62, 0x55, 0x58,
+ 0x15, 0xD9, 0x60, 0x05, 0x97, 0xAC, 0x6E, 0xF9
+ },
+ {
+ 0x68, 0x28, 0x9F, 0x66, 0x05, 0x47, 0x3B, 0xA0,
+ 0xE4, 0xF2, 0x41, 0xBA, 0xF7, 0x47, 0x7A, 0x98,
+ 0x85, 0x42, 0x6A, 0x85, 0x8F, 0x19, 0xEF, 0x2A,
+ 0x18, 0xB0, 0xD4, 0x0E, 0xF8, 0xE4, 0x12, 0x82,
+ 0xED, 0x55, 0x26, 0xB5, 0x19, 0x79, 0x9E, 0x27,
+ 0x0F, 0x13, 0x88, 0x13, 0x27, 0x91, 0x82, 0x78,
+ 0x75, 0x57, 0x11, 0x07, 0x1D, 0x85, 0x11, 0xFE,
+ 0x96, 0x3E, 0x3B, 0x56, 0x06, 0xAA, 0x37, 0x16
+ },
+ {
+ 0x80, 0xA3, 0x37, 0x87, 0x54, 0x26, 0x12, 0xC3,
+ 0x8F, 0x6B, 0xCD, 0x7C, 0xD8, 0x6C, 0xAB, 0x46,
+ 0x02, 0x27, 0x50, 0x9B, 0x1C, 0xBA, 0xD5, 0xEC,
+ 0x40, 0x8A, 0x91, 0x41, 0x3D, 0x51, 0x15, 0x5A,
+ 0x04, 0x76, 0xDA, 0xDB, 0xF3, 0xA2, 0x51, 0x8E,
+ 0x4A, 0x6E, 0x77, 0xCC, 0x34, 0x66, 0x22, 0xE3,
+ 0x47, 0xA4, 0x69, 0xBF, 0x8B, 0xAA, 0x5F, 0x04,
+ 0xEB, 0x2D, 0x98, 0x70, 0x53, 0x55, 0xD0, 0x63
+ },
+ {
+ 0x34, 0x62, 0x9B, 0xC6, 0xD8, 0x31, 0x39, 0x1C,
+ 0x4C, 0xDF, 0x8A, 0xF1, 0xB4, 0xB7, 0xB6, 0xB8,
+ 0xE8, 0xEE, 0x17, 0xCF, 0x98, 0xC7, 0x0E, 0x5D,
+ 0xD5, 0x86, 0xCD, 0x99, 0xF1, 0x4B, 0x11, 0xDF,
+ 0x94, 0x51, 0x66, 0x23, 0x6A, 0x95, 0x71, 0xE6,
+ 0xD5, 0x91, 0xBB, 0x83, 0xEE, 0x4D, 0x16, 0x4D,
+ 0x46, 0xF6, 0xB9, 0xD8, 0xEF, 0x86, 0xFF, 0x86,
+ 0x5A, 0x81, 0xBF, 0xB9, 0x1B, 0x00, 0x42, 0x4B
+ },
+ {
+ 0x8B, 0x7C, 0xC3, 0x39, 0x16, 0x38, 0x63, 0xBB,
+ 0x43, 0x83, 0xE5, 0x42, 0xB0, 0xEF, 0x0E, 0x7C,
+ 0xF3, 0x6B, 0x84, 0xAD, 0x93, 0x2C, 0xDF, 0x5A,
+ 0x80, 0x41, 0x9E, 0xC9, 0xAD, 0x69, 0x2E, 0x7A,
+ 0x7E, 0x78, 0x4D, 0x2C, 0x7C, 0xB3, 0x79, 0x6A,
+ 0x18, 0xB8, 0xF8, 0x00, 0x03, 0x5F, 0x3A, 0xA0,
+ 0x6C, 0x82, 0x41, 0x00, 0x61, 0x11, 0x20, 0xA7,
+ 0xBD, 0xEB, 0x35, 0x61, 0x8C, 0xCB, 0x81, 0xB7
+ },
+ {
+ 0x4F, 0x08, 0x4E, 0x49, 0x39, 0xDD, 0x5A, 0x7F,
+ 0x5A, 0x65, 0x8F, 0xAD, 0x58, 0xA1, 0x8A, 0x15,
+ 0xC2, 0x5C, 0x32, 0xEC, 0x1C, 0x7F, 0xD5, 0xC5,
+ 0xC6, 0xC3, 0xE8, 0x92, 0xB3, 0x97, 0x1A, 0xEA,
+ 0xAC, 0x30, 0x83, 0x04, 0xEF, 0x17, 0xB1, 0xC4,
+ 0x72, 0x39, 0xEA, 0x4B, 0xB3, 0x98, 0xB3, 0xFD,
+ 0x6D, 0x45, 0x28, 0xD8, 0xDE, 0x8E, 0x76, 0x8A,
+ 0xE0, 0xF1, 0xA5, 0xA5, 0xC6, 0xB5, 0xC2, 0x97
+ },
+ {
+ 0x48, 0xF4, 0x07, 0xA1, 0xAF, 0x5B, 0x80, 0x09,
+ 0xB2, 0x05, 0x17, 0x42, 0xE8, 0xCF, 0x5C, 0xD5,
+ 0x65, 0x66, 0x69, 0xE7, 0xD7, 0x22, 0xEE, 0x8E,
+ 0x7B, 0xD2, 0x02, 0x06, 0x08, 0x49, 0x44, 0x21,
+ 0x68, 0xD8, 0xFA, 0xCC, 0x11, 0x7C, 0x01, 0x2B,
+ 0xFB, 0x7B, 0xF4, 0x49, 0xD9, 0x9B, 0xEF, 0xFF,
+ 0x6A, 0x34, 0xAE, 0xA2, 0x03, 0xF1, 0xD8, 0xD3,
+ 0x52, 0x72, 0x2B, 0xE5, 0x01, 0x4E, 0xC8, 0x18
+ },
+ {
+ 0xA6, 0xAA, 0x82, 0xCD, 0x1E, 0x42, 0x6F, 0x9A,
+ 0x73, 0xBF, 0xA3, 0x9A, 0x29, 0x03, 0x78, 0x76,
+ 0x11, 0x46, 0x55, 0xB8, 0xC2, 0x2D, 0x6D, 0x3F,
+ 0xF8, 0xB6, 0x38, 0xAE, 0x7D, 0xEA, 0x6B, 0x17,
+ 0x84, 0x3E, 0x09, 0xE5, 0x2E, 0xB6, 0x6F, 0xA1,
+ 0xE4, 0x75, 0xE4, 0xA8, 0xA3, 0xDE, 0x42, 0x9B,
+ 0x7D, 0x0F, 0x4A, 0x77, 0x6F, 0xCB, 0x8B, 0xDC,
+ 0x9B, 0x9F, 0xED, 0xE7, 0xD5, 0x2E, 0x81, 0x5F
+ },
+ {
+ 0x58, 0x17, 0x02, 0x7D, 0x6B, 0xDD, 0x00, 0xC5,
+ 0xDD, 0x10, 0xAC, 0x59, 0x3C, 0xD5, 0x60, 0x37,
+ 0x22, 0x70, 0x77, 0x5A, 0x18, 0x52, 0x6D, 0x7E,
+ 0x6F, 0x13, 0x87, 0x2A, 0x2E, 0x20, 0xEA, 0xB6,
+ 0x64, 0x62, 0x5B, 0xE7, 0x16, 0x8A, 0xC4, 0xBD,
+ 0x7C, 0x9E, 0x0C, 0xE7, 0xFC, 0x40, 0x99, 0xE0,
+ 0xF4, 0x84, 0x42, 0xE2, 0xC7, 0x67, 0x19, 0x1C,
+ 0x6E, 0x12, 0x84, 0xE9, 0xB2, 0xCC, 0xEA, 0x8C
+ },
+ {
+ 0x08, 0xE4, 0x10, 0x28, 0x34, 0x0A, 0x45, 0xC7,
+ 0x4E, 0x40, 0x52, 0xB3, 0xA8, 0xD6, 0x38, 0x9E,
+ 0x22, 0xE0, 0x43, 0xA1, 0xAD, 0xAB, 0x5E, 0x28,
+ 0xD9, 0x76, 0x19, 0x45, 0x0D, 0x72, 0x34, 0x69,
+ 0xB6, 0x20, 0xCA, 0xA5, 0x19, 0xB8, 0x1C, 0x14,
+ 0x52, 0x38, 0x54, 0xF6, 0x19, 0xFD, 0x30, 0x27,
+ 0xE3, 0x84, 0x7B, 0xD0, 0x32, 0x76, 0xE6, 0x06,
+ 0x04, 0xA8, 0x0D, 0xDB, 0x4D, 0xE8, 0x76, 0xD6
+ },
+ {
+ 0x13, 0x0B, 0x84, 0x20, 0x53, 0x7E, 0xB0, 0x7D,
+ 0x72, 0xAB, 0xDA, 0x07, 0xC8, 0x5A, 0xCB, 0xD8,
+ 0xB9, 0xA4, 0x4F, 0x16, 0x32, 0x1D, 0xD0, 0x42,
+ 0x21, 0x45, 0xF8, 0x09, 0x67, 0x3D, 0x30, 0xF2,
+ 0xB5, 0x32, 0x13, 0x26, 0xE2, 0xBF, 0xF3, 0x17,
+ 0xEF, 0x3F, 0xEF, 0x98, 0x3C, 0x51, 0xC4, 0xF8,
+ 0xAB, 0x24, 0xA3, 0x25, 0xD2, 0x98, 0xE3, 0x4A,
+ 0xFC, 0xE5, 0x69, 0xA8, 0x25, 0x55, 0x77, 0x4C
+ },
+ {
+ 0xAC, 0x49, 0xB8, 0x44, 0xAF, 0xAA, 0x01, 0x2E,
+ 0x31, 0xC4, 0x74, 0xCA, 0x26, 0x36, 0x48, 0x84,
+ 0x4F, 0xD2, 0xF6, 0x30, 0x79, 0x92, 0xC2, 0xF7,
+ 0x52, 0xAC, 0xA0, 0x2C, 0x38, 0x28, 0x96, 0x51,
+ 0x75, 0x79, 0x4D, 0xEE, 0xE2, 0xD2, 0xEE, 0x95,
+ 0xC6, 0x1C, 0xD2, 0x84, 0xF6, 0xB5, 0xA2, 0xD7,
+ 0x5E, 0x2E, 0xF2, 0xB2, 0x9E, 0xE8, 0x14, 0x9E,
+ 0x77, 0xFB, 0x81, 0x44, 0x7B, 0x2F, 0xD0, 0x4B
+ },
+ {
+ 0xB9, 0xD7, 0xCA, 0x81, 0xCC, 0x60, 0xBB, 0x95,
+ 0x78, 0xE4, 0x40, 0x24, 0xE5, 0xA0, 0xA0, 0xBE,
+ 0x80, 0xF2, 0x73, 0x36, 0xA6, 0xA9, 0xF4, 0xE5,
+ 0x3D, 0xF3, 0x99, 0x9C, 0xB1, 0x91, 0x28, 0x0B,
+ 0x09, 0x0E, 0x2A, 0xC2, 0xD2, 0x9C, 0x5B, 0xAA,
+ 0xD9, 0xD7, 0x14, 0x15, 0xBD, 0xC1, 0x29, 0xE6,
+ 0x9A, 0xA2, 0x66, 0x7A, 0xF6, 0xA7, 0xFD, 0x5E,
+ 0x18, 0x9F, 0xCC, 0xDC, 0xEE, 0x81, 0x73, 0x40
+ },
+ {
+ 0xA7, 0x55, 0xE1, 0x13, 0x38, 0x65, 0x72, 0xC7,
+ 0x5C, 0xED, 0x61, 0xD7, 0x19, 0x70, 0x60, 0x70,
+ 0xB9, 0x14, 0x60, 0x48, 0xE4, 0x2A, 0x9F, 0x8C,
+ 0xD3, 0x56, 0x67, 0xA0, 0x88, 0xB4, 0x2F, 0x08,
+ 0x80, 0x8A, 0xBD, 0xF7, 0x7E, 0x61, 0x8A, 0xBD,
+ 0x95, 0x9A, 0xFC, 0x75, 0x73, 0x79, 0xCA, 0x2C,
+ 0x00, 0xBC, 0xC1, 0xA4, 0x83, 0x90, 0xFA, 0x2B,
+ 0xFF, 0x61, 0x8B, 0x1E, 0x00, 0x78, 0xA6, 0x13
+ },
+ {
+ 0xA7, 0x3C, 0x7D, 0xEB, 0xED, 0x32, 0x6F, 0x1C,
+ 0x0D, 0xB0, 0x79, 0x5E, 0xE7, 0xD6, 0xE3, 0x94,
+ 0x68, 0x94, 0xB8, 0x26, 0xB1, 0xF8, 0x10, 0x1C,
+ 0x56, 0xC8, 0x23, 0xBA, 0x17, 0x16, 0x83, 0x12,
+ 0xE7, 0xF5, 0x3F, 0xC7, 0xDB, 0xE5, 0x2C, 0x3E,
+ 0x11, 0xE6, 0x98, 0x52, 0xC4, 0x04, 0x85, 0xE2,
+ 0xEF, 0x18, 0x24, 0x77, 0x86, 0x2E, 0xA6, 0xA3,
+ 0x4E, 0xC1, 0x36, 0xE2, 0xDF, 0xEE, 0xA6, 0xF4
+ },
+ {
+ 0x6C, 0xB8, 0xF9, 0xD5, 0x2C, 0x56, 0xD8, 0x2C,
+ 0xAC, 0x28, 0xF3, 0x9E, 0xA1, 0x59, 0x3E, 0x8B,
+ 0xB2, 0x50, 0x62, 0x93, 0xAC, 0x0D, 0x68, 0x37,
+ 0x6A, 0x17, 0x09, 0xB6, 0x2A, 0x46, 0xDF, 0x14,
+ 0xA4, 0xAE, 0x64, 0xB2, 0xD8, 0xFA, 0xB7, 0x67,
+ 0x33, 0xA1, 0xCE, 0xD2, 0xD5, 0x48, 0xE3, 0xF3,
+ 0xC6, 0xFC, 0xB4, 0x9D, 0x40, 0xC3, 0xD5, 0x80,
+ 0x8E, 0x44, 0x9C, 0xD8, 0x3D, 0x1C, 0x2A, 0xA2
+ },
+ {
+ 0x68, 0x3F, 0xA2, 0xB2, 0x36, 0x9A, 0x10, 0x16,
+ 0x2C, 0x1C, 0x1C, 0x7B, 0x24, 0xBC, 0x97, 0x0E,
+ 0xE6, 0x7D, 0xA2, 0x20, 0x56, 0x4F, 0x32, 0x20,
+ 0x3F, 0x62, 0x56, 0x96, 0xC0, 0x35, 0x2A, 0x0B,
+ 0x9A, 0xD9, 0x66, 0x24, 0x36, 0x2D, 0x95, 0x2D,
+ 0x84, 0x46, 0x3C, 0x11, 0x06, 0xA2, 0xDB, 0xA7,
+ 0xA0, 0x92, 0x59, 0x98, 0x84, 0xB3, 0x5A, 0x0B,
+ 0x89, 0xC8, 0xF1, 0xB6, 0xA9, 0xB5, 0xA6, 0x1E
+ },
+ {
+ 0xAA, 0xD9, 0xAD, 0x44, 0x61, 0x01, 0x18, 0xB7,
+ 0x7D, 0x50, 0x8A, 0xEB, 0x1B, 0xBC, 0xD1, 0xC1,
+ 0xB7, 0xD0, 0x17, 0x13, 0x97, 0xFB, 0x51, 0x0A,
+ 0x40, 0x1B, 0xBC, 0x0E, 0xC3, 0x46, 0x23, 0x67,
+ 0x0D, 0x86, 0xA2, 0xDC, 0x3C, 0x8F, 0x3A, 0xB5,
+ 0xA2, 0x04, 0x4D, 0xF7, 0x30, 0x25, 0x67, 0x27,
+ 0x54, 0x5F, 0x08, 0x60, 0xCE, 0x21, 0xA1, 0xEA,
+ 0xC7, 0x17, 0xDF, 0xC4, 0x8F, 0x5D, 0x22, 0x8E
+ },
+ {
+ 0xC4, 0x25, 0x78, 0xDE, 0x23, 0xB4, 0xC9, 0x87,
+ 0xD5, 0xE1, 0xAC, 0x4D, 0x68, 0x9E, 0xD5, 0xDE,
+ 0x4B, 0x04, 0x17, 0xF9, 0x70, 0x4B, 0xC6, 0xBC,
+ 0xE9, 0x69, 0xFA, 0x13, 0x47, 0x15, 0x85, 0xD6,
+ 0x2C, 0x2C, 0xB1, 0x21, 0x2A, 0x94, 0x4F, 0x39,
+ 0x7F, 0xC9, 0xCA, 0x2C, 0x37, 0x47, 0xC3, 0xBE,
+ 0xB6, 0x94, 0xEC, 0x4C, 0x5B, 0xE6, 0x88, 0x28,
+ 0xDD, 0xA5, 0x3E, 0xF4, 0x3F, 0xAE, 0xC6, 0xC0
+ },
+ {
+ 0x47, 0x0F, 0x00, 0x84, 0x1E, 0xE8, 0x24, 0x4E,
+ 0x63, 0xED, 0x2C, 0x7E, 0xA3, 0x0E, 0x2E, 0x41,
+ 0x98, 0x97, 0xC1, 0x97, 0x46, 0x2E, 0xCC, 0xCE,
+ 0xCF, 0x71, 0x3B, 0x42, 0xA5, 0x06, 0x5F, 0xFF,
+ 0x59, 0x14, 0xBC, 0x9B, 0x79, 0xAF, 0xFE, 0x8F,
+ 0x6B, 0x65, 0x78, 0x75, 0xE7, 0x89, 0xAE, 0x21,
+ 0x3B, 0xD9, 0x14, 0xCD, 0x35, 0xBD, 0x17, 0x4D,
+ 0x46, 0xE9, 0xD1, 0x8B, 0xD8, 0x43, 0x77, 0x3D
+ },
+ {
+ 0x34, 0xFC, 0x42, 0x13, 0x73, 0x0F, 0x47, 0xA5,
+ 0xE9, 0xA3, 0x58, 0x0F, 0x64, 0x3E, 0x12, 0x94,
+ 0x5C, 0xFC, 0xB3, 0x1B, 0xF2, 0x06, 0xF6, 0xAD,
+ 0x45, 0x0C, 0xE5, 0x28, 0xDA, 0x3F, 0xA4, 0x32,
+ 0xE0, 0x05, 0xD6, 0xB0, 0xEC, 0xCE, 0x10, 0xDC,
+ 0xA7, 0xC5, 0x99, 0x5F, 0x6A, 0xAC, 0xC5, 0x15,
+ 0x0E, 0x1B, 0x00, 0x9E, 0x19, 0x75, 0x1E, 0x83,
+ 0x09, 0xF8, 0x85, 0x95, 0x31, 0x84, 0x43, 0x74
+ },
+ {
+ 0xFB, 0x3C, 0x1F, 0x0F, 0x56, 0xA5, 0x6F, 0x8E,
+ 0x31, 0x6F, 0xDF, 0x5D, 0x85, 0x3C, 0x8C, 0x87,
+ 0x2C, 0x39, 0x63, 0x5D, 0x08, 0x36, 0x34, 0xC3,
+ 0x90, 0x4F, 0xC3, 0xAC, 0x07, 0xD1, 0xB5, 0x78,
+ 0xE8, 0x5F, 0xF0, 0xE4, 0x80, 0xE9, 0x2D, 0x44,
+ 0xAD, 0xE3, 0x3B, 0x62, 0xE8, 0x93, 0xEE, 0x32,
+ 0x34, 0x3E, 0x79, 0xDD, 0xF6, 0xEF, 0x29, 0x2E,
+ 0x89, 0xB5, 0x82, 0xD3, 0x12, 0x50, 0x23, 0x14
+ },
+ {
+ 0xC7, 0xC9, 0x7F, 0xC6, 0x5D, 0xD2, 0xB9, 0xE3,
+ 0xD3, 0xD6, 0x07, 0xD3, 0x15, 0x98, 0xD3, 0xF8,
+ 0x42, 0x61, 0xE9, 0x91, 0x92, 0x51, 0xE9, 0xC8,
+ 0xE5, 0x7B, 0xB5, 0xF8, 0x29, 0x37, 0x7D, 0x5F,
+ 0x73, 0xEA, 0xBB, 0xED, 0x55, 0xC6, 0xC3, 0x81,
+ 0x18, 0x0F, 0x29, 0xAD, 0x02, 0xE5, 0xBE, 0x79,
+ 0x7F, 0xFE, 0xC7, 0xE5, 0x7B, 0xDE, 0xCB, 0xC5,
+ 0x0A, 0xD3, 0xD0, 0x62, 0xF0, 0x99, 0x3A, 0xB0
+ },
+ {
+ 0xA5, 0x7A, 0x49, 0xCD, 0xBE, 0x67, 0xAE, 0x7D,
+ 0x9F, 0x79, 0x7B, 0xB5, 0xCC, 0x7E, 0xFC, 0x2D,
+ 0xF0, 0x7F, 0x4E, 0x1B, 0x15, 0x95, 0x5F, 0x85,
+ 0xDA, 0xE7, 0x4B, 0x76, 0xE2, 0xEC, 0xB8, 0x5A,
+ 0xFB, 0x6C, 0xD9, 0xEE, 0xED, 0x88, 0x88, 0xD5,
+ 0xCA, 0x3E, 0xC5, 0xAB, 0x65, 0xD2, 0x7A, 0x7B,
+ 0x19, 0xE5, 0x78, 0x47, 0x57, 0x60, 0xA0, 0x45,
+ 0xAC, 0x3C, 0x92, 0xE1, 0x3A, 0x93, 0x8E, 0x77
+ },
+ {
+ 0xC7, 0x14, 0x3F, 0xCE, 0x96, 0x14, 0xA1, 0x7F,
+ 0xD6, 0x53, 0xAE, 0xB1, 0x40, 0x72, 0x6D, 0xC9,
+ 0xC3, 0xDB, 0xB1, 0xDE, 0x6C, 0xC5, 0x81, 0xB2,
+ 0x72, 0x68, 0x97, 0xEC, 0x24, 0xB7, 0xA5, 0x03,
+ 0x59, 0xAD, 0x49, 0x22, 0x43, 0xBE, 0x66, 0xD9,
+ 0xED, 0xD8, 0xC9, 0x33, 0xB5, 0xB8, 0x0E, 0x0B,
+ 0x91, 0xBB, 0x61, 0xEA, 0x98, 0x05, 0x60, 0x06,
+ 0x51, 0x69, 0x76, 0xFA, 0xE8, 0xD9, 0x9A, 0x35
+ },
+ {
+ 0x65, 0xBB, 0x58, 0xD0, 0x7F, 0x93, 0x7E, 0x2D,
+ 0x3C, 0x7E, 0x65, 0x38, 0x5F, 0x9C, 0x54, 0x73,
+ 0x0B, 0x70, 0x41, 0x05, 0xCC, 0xDB, 0x69, 0x1F,
+ 0x6E, 0x14, 0x6D, 0x4E, 0xE8, 0xF6, 0xC0, 0x86,
+ 0xF4, 0x95, 0x11, 0x03, 0x51, 0x10, 0xA9, 0xAD,
+ 0x60, 0x31, 0xFD, 0xCE, 0xB9, 0x43, 0xE0, 0xF9,
+ 0x61, 0x3B, 0xCB, 0x27, 0x6D, 0xD4, 0x0F, 0x06,
+ 0x24, 0xEF, 0x0F, 0x92, 0x4F, 0x80, 0x97, 0x83
+ },
+ {
+ 0xE5, 0x40, 0x27, 0x7F, 0x68, 0x3B, 0x11, 0x86,
+ 0xDD, 0x3B, 0x5B, 0x3F, 0x61, 0x43, 0x33, 0x96,
+ 0x58, 0x1A, 0x35, 0xFE, 0xB1, 0x20, 0x02, 0xBE,
+ 0x8C, 0x6A, 0x62, 0x31, 0xFC, 0x40, 0xFF, 0xA7,
+ 0x0F, 0x08, 0x08, 0x1B, 0xC5, 0x8B, 0x2D, 0x94,
+ 0xF7, 0x64, 0x95, 0x43, 0x61, 0x4A, 0x43, 0x5F,
+ 0xAA, 0x2D, 0x62, 0x11, 0x0E, 0x13, 0xDA, 0xBC,
+ 0x7B, 0x86, 0x62, 0x9B, 0x63, 0xAF, 0x9C, 0x24
+ },
+ {
+ 0x41, 0x85, 0x00, 0x87, 0x8C, 0x5F, 0xBC, 0xB5,
+ 0x84, 0xC4, 0x32, 0xF4, 0x28, 0x5E, 0x05, 0xE4,
+ 0x9F, 0x2E, 0x3E, 0x07, 0x53, 0x99, 0xA0, 0xDB,
+ 0xFC, 0xF8, 0x74, 0xEB, 0xF8, 0xC0, 0x3D, 0x02,
+ 0xBF, 0x16, 0xBC, 0x69, 0x89, 0xD1, 0x61, 0xC7,
+ 0x7C, 0xA0, 0x78, 0x6B, 0x05, 0x05, 0x3C, 0x6C,
+ 0x70, 0x94, 0x33, 0x71, 0x23, 0x19, 0x19, 0x21,
+ 0x28, 0x83, 0x5C, 0xF0, 0xB6, 0x60, 0x59, 0x5B
+ },
+ {
+ 0x88, 0x90, 0x90, 0xDB, 0xB1, 0x94, 0x4B, 0xDC,
+ 0x94, 0x33, 0xEE, 0x5E, 0xF1, 0x01, 0x0C, 0x7A,
+ 0x4A, 0x24, 0xA8, 0xE7, 0x1E, 0xCE, 0xA8, 0xE1,
+ 0x2A, 0x31, 0x31, 0x8C, 0xE4, 0x9D, 0xCA, 0xB0,
+ 0xAC, 0xA5, 0xC3, 0x80, 0x23, 0x34, 0xAA, 0xB2,
+ 0xCC, 0x84, 0xB1, 0x4C, 0x6B, 0x93, 0x21, 0xFE,
+ 0x58, 0x6B, 0xF3, 0xF8, 0x76, 0xF1, 0x9C, 0xD4,
+ 0x06, 0xEB, 0x11, 0x27, 0xFB, 0x94, 0x48, 0x01
+ },
+ {
+ 0x53, 0xB6, 0xA2, 0x89, 0x10, 0xAA, 0x92, 0xE2,
+ 0x7E, 0x53, 0x6F, 0xB5, 0x49, 0xCF, 0x9B, 0x99,
+ 0x18, 0x79, 0x10, 0x60, 0x89, 0x8E, 0x0B, 0x9F,
+ 0xE1, 0x83, 0x57, 0x7F, 0xF4, 0x3B, 0x5E, 0x9C,
+ 0x76, 0x89, 0xC7, 0x45, 0xB3, 0x2E, 0x41, 0x22,
+ 0x69, 0x83, 0x7C, 0x31, 0xB8, 0x9E, 0x6C, 0xC1,
+ 0x2B, 0xF7, 0x6E, 0x13, 0xCA, 0xD3, 0x66, 0xB7,
+ 0x4E, 0xCE, 0x48, 0xBB, 0x85, 0xFD, 0x09, 0xE9
+ },
+ {
+ 0x7C, 0x09, 0x20, 0x80, 0xC6, 0xA8, 0x0D, 0x67,
+ 0x24, 0x09, 0xD0, 0x81, 0xD3, 0xD1, 0x77, 0x10,
+ 0x6B, 0xCD, 0x63, 0x56, 0x77, 0x85, 0x14, 0x07,
+ 0x19, 0x49, 0x09, 0x50, 0xAE, 0x07, 0xAE, 0x8F,
+ 0xCA, 0xAB, 0xBA, 0xAA, 0xB3, 0x30, 0xCF, 0xBC,
+ 0xF7, 0x37, 0x44, 0x82, 0xC2, 0x20, 0xAF, 0x2E,
+ 0xAD, 0xEE, 0xB7, 0x3D, 0xCB, 0xB3, 0x5E, 0xD8,
+ 0x23, 0x34, 0x4E, 0x14, 0x4E, 0x7D, 0x48, 0x99
+ },
+ {
+ 0x9C, 0xCD, 0xE5, 0x66, 0xD2, 0x40, 0x05, 0x09,
+ 0x18, 0x11, 0x11, 0xF3, 0x2D, 0xDE, 0x4C, 0xD6,
+ 0x32, 0x09, 0xFE, 0x59, 0xA3, 0x0C, 0x11, 0x45,
+ 0x46, 0xAD, 0x27, 0x76, 0xD8, 0x89, 0xA4, 0x1B,
+ 0xAD, 0x8F, 0xA1, 0xBB, 0x46, 0x8C, 0xB2, 0xF9,
+ 0xD4, 0x2C, 0xA9, 0x92, 0x8A, 0x77, 0x70, 0xFE,
+ 0xF8, 0xE8, 0xBA, 0x4D, 0x0C, 0x81, 0x2D, 0x9A,
+ 0x1E, 0x75, 0xC3, 0xD8, 0xD2, 0xCC, 0xD7, 0x5A
+ },
+ {
+ 0x6E, 0x29, 0x3B, 0xF5, 0xD0, 0x3F, 0xE4, 0x39,
+ 0x77, 0xCF, 0xE3, 0xF5, 0x7C, 0xCD, 0xB3, 0xAE,
+ 0x28, 0x2A, 0x85, 0x45, 0x5D, 0xCA, 0x33, 0xF3,
+ 0x7F, 0x4B, 0x74, 0xF8, 0x39, 0x8C, 0xC6, 0x12,
+ 0x43, 0x3D, 0x75, 0x5C, 0xBE, 0xC4, 0x12, 0xF8,
+ 0xF8, 0x2A, 0x3B, 0xD3, 0xBC, 0x4A, 0x27, 0x8F,
+ 0x7E, 0xCD, 0x0D, 0xFA, 0x9B, 0xBD, 0xC4, 0x0B,
+ 0xE7, 0xA7, 0x87, 0xC8, 0xF1, 0x59, 0xB2, 0xDF
+ },
+ {
+ 0xC5, 0x65, 0x46, 0xFB, 0x21, 0x78, 0x45, 0x6F,
+ 0x33, 0x61, 0x64, 0xC1, 0x8B, 0x90, 0xDE, 0xFF,
+ 0xC8, 0x3A, 0xE2, 0xB5, 0xA3, 0xAC, 0xA7, 0x7B,
+ 0x68, 0x84, 0xD3, 0x6D, 0x2C, 0x1D, 0xB3, 0x95,
+ 0x01, 0xB3, 0xE6, 0x5E, 0x36, 0xC7, 0x58, 0xC6,
+ 0x6E, 0x31, 0x88, 0x45, 0x1F, 0xDB, 0x35, 0x15,
+ 0xEE, 0x16, 0x2C, 0x00, 0x1F, 0x06, 0xC3, 0xE8,
+ 0xCB, 0x57, 0x3A, 0xDF, 0x30, 0xF7, 0xA1, 0x01
+ },
+ {
+ 0x6F, 0x82, 0xF8, 0x9F, 0x29, 0x9E, 0xBC, 0xA2,
+ 0xFE, 0x01, 0x4B, 0x59, 0xBF, 0xFE, 0x1A, 0xA8,
+ 0x4E, 0x88, 0xB1, 0x91, 0x5F, 0xE2, 0x56, 0xAF,
+ 0xB6, 0x46, 0xFD, 0x84, 0x48, 0xAF, 0x2B, 0x88,
+ 0x91, 0xA7, 0xFA, 0xB3, 0x7A, 0x4E, 0xA6, 0xF9,
+ 0xA5, 0x0E, 0x6C, 0x31, 0x70, 0x39, 0xD8, 0xCF,
+ 0x87, 0x8F, 0x4C, 0x8E, 0x1A, 0x0D, 0xD4, 0x64,
+ 0xF0, 0xB4, 0xD6, 0xFF, 0x1C, 0x7E, 0xA8, 0x53
+ },
+ {
+ 0x2B, 0x85, 0x99, 0xFF, 0x9C, 0x3D, 0x61, 0x98,
+ 0x63, 0x7A, 0xD5, 0x1E, 0x57, 0xD1, 0x99, 0x8B,
+ 0x0D, 0x75, 0x31, 0x3F, 0xE2, 0xDD, 0x61, 0xA5,
+ 0x33, 0xC9, 0x64, 0xA6, 0xDD, 0x96, 0x07, 0xC6,
+ 0xF7, 0x23, 0xE9, 0x45, 0x2C, 0xE4, 0x6E, 0x01,
+ 0x4B, 0x1C, 0x1D, 0x6D, 0xE7, 0x7B, 0xA5, 0xB8,
+ 0x8C, 0x91, 0x4D, 0x1C, 0x59, 0x7B, 0xF1, 0xEA,
+ 0xE1, 0x34, 0x74, 0xB4, 0x29, 0x0E, 0x89, 0xB2
+ },
+ {
+ 0x08, 0xBF, 0x34, 0x6D, 0x38, 0xE1, 0xDF, 0x06,
+ 0xC8, 0x26, 0x0E, 0xDB, 0x1D, 0xA7, 0x55, 0x79,
+ 0x27, 0x59, 0x48, 0xD5, 0xC0, 0xA0, 0xAA, 0x9E,
+ 0xD2, 0x88, 0x6F, 0x88, 0x56, 0xDE, 0x54, 0x17,
+ 0xA1, 0x56, 0x99, 0x87, 0x58, 0xF5, 0xB1, 0x7E,
+ 0x52, 0xF1, 0x01, 0xCA, 0x95, 0x7A, 0x71, 0x13,
+ 0x74, 0x73, 0xDF, 0xD1, 0x8D, 0x7D, 0x20, 0x9C,
+ 0x4C, 0x10, 0xD9, 0x23, 0x3C, 0x93, 0x69, 0x1D
+ },
+ {
+ 0x6D, 0xF2, 0x15, 0x6D, 0x77, 0x31, 0x14, 0xD3,
+ 0x10, 0xB6, 0x3D, 0xB9, 0xEE, 0x53, 0x50, 0xD7,
+ 0x7E, 0x6B, 0xCF, 0x25, 0xB0, 0x5F, 0xCD, 0x91,
+ 0x0F, 0x9B, 0x31, 0xBC, 0x42, 0xBB, 0x13, 0xFE,
+ 0x82, 0x25, 0xEB, 0xCB, 0x2A, 0x23, 0xA6, 0x22,
+ 0x80, 0x77, 0x7B, 0x6B, 0xF7, 0x4E, 0x2C, 0xD0,
+ 0x91, 0x7C, 0x76, 0x40, 0xB4, 0x3D, 0xEF, 0xE4,
+ 0x68, 0xCD, 0x1E, 0x18, 0xC9, 0x43, 0xC6, 0x6A
+ },
+ {
+ 0x7C, 0x70, 0x38, 0xBC, 0x13, 0xA9, 0x11, 0x51,
+ 0x82, 0x8A, 0x5B, 0xA8, 0x2B, 0x4A, 0x96, 0x04,
+ 0x0F, 0x25, 0x8A, 0x4D, 0xFB, 0x1B, 0x13, 0x73,
+ 0xF0, 0xD3, 0x59, 0x16, 0x8A, 0xFB, 0x05, 0x17,
+ 0xA2, 0x0B, 0x28, 0xA1, 0x2D, 0x36, 0x44, 0x04,
+ 0x6B, 0xE6, 0x6B, 0x8D, 0x08, 0xD8, 0xAE, 0x7F,
+ 0x6A, 0x92, 0x3E, 0xA1, 0xC0, 0x01, 0x87, 0xC6,
+ 0xD1, 0x1D, 0xC5, 0x02, 0xBA, 0xC7, 0x13, 0x05
+ },
+ {
+ 0xBC, 0xD1, 0xB3, 0x0D, 0x80, 0x8F, 0xB7, 0x39,
+ 0xB9, 0x87, 0xCB, 0xF1, 0x54, 0xBE, 0xA0, 0x0D,
+ 0xA9, 0xD4, 0x03, 0x80, 0xB8, 0x61, 0xD4, 0xC1,
+ 0xD6, 0x37, 0x71, 0x22, 0xDA, 0xDD, 0x61, 0xC0,
+ 0xE5, 0x90, 0x18, 0xB7, 0x19, 0x41, 0xCF, 0xB6,
+ 0x2E, 0x00, 0xDC, 0xD7, 0x0A, 0xEB, 0x9A, 0xBF,
+ 0x04, 0x73, 0xE8, 0x0F, 0x0A, 0x7E, 0xCA, 0x6B,
+ 0x6D, 0xEA, 0x24, 0x6A, 0xB2, 0x29, 0xDD, 0x2B
+ },
+ {
+ 0x7E, 0xD4, 0x46, 0x8D, 0x96, 0x85, 0x30, 0xFE,
+ 0x7A, 0xB2, 0xC3, 0x35, 0x40, 0xB2, 0x6D, 0x8C,
+ 0x3B, 0xD3, 0xED, 0x44, 0xB3, 0x4F, 0xBE, 0x8C,
+ 0x2A, 0x9D, 0x7F, 0x80, 0x5B, 0x5A, 0xDA, 0x0E,
+ 0xA2, 0x52, 0xEE, 0xAD, 0xE4, 0xFC, 0xE9, 0x7F,
+ 0x89, 0x72, 0x8A, 0xD8, 0x5B, 0xC8, 0xBB, 0x24,
+ 0x30, 0xB1, 0xBE, 0xF2, 0xCD, 0xDD, 0x32, 0xC8,
+ 0x44, 0x6E, 0x59, 0xB8, 0xE8, 0xBA, 0x3C, 0x67
+ },
+ {
+ 0x6D, 0x30, 0xB7, 0xC6, 0xCE, 0x8A, 0x32, 0x36,
+ 0xC0, 0xCA, 0x2F, 0x8D, 0x72, 0x8B, 0x10, 0x88,
+ 0xCA, 0x06, 0x98, 0x3A, 0x80, 0x43, 0xE6, 0x21,
+ 0xD5, 0xDC, 0xF0, 0xC5, 0x37, 0xD1, 0x3B, 0x08,
+ 0x79, 0x1E, 0xDE, 0xB0, 0x1A, 0x3C, 0xF0, 0x94,
+ 0x3E, 0xC1, 0xC8, 0x90, 0xAB, 0x6E, 0x29, 0xB1,
+ 0x46, 0xA2, 0x36, 0xCD, 0x46, 0xBC, 0xB9, 0xD9,
+ 0x3B, 0xF5, 0x16, 0xFB, 0x67, 0xC6, 0x3F, 0xE5
+ },
+ {
+ 0x97, 0xFE, 0x03, 0xCE, 0xF3, 0x14, 0x38, 0x50,
+ 0x89, 0x11, 0xBD, 0xED, 0x97, 0x59, 0x80, 0xA6,
+ 0x60, 0x29, 0x30, 0x5D, 0xC5, 0xE3, 0xFA, 0x8A,
+ 0xD1, 0xB4, 0xFB, 0x22, 0xFC, 0xDF, 0x5A, 0x19,
+ 0xA7, 0x33, 0x32, 0x03, 0x27, 0xD8, 0xF7, 0x1C,
+ 0xCF, 0x49, 0x6C, 0xB3, 0xA4, 0x4A, 0x77, 0xAF,
+ 0x56, 0xE3, 0xDD, 0xE7, 0x3D, 0x3A, 0x5F, 0x17,
+ 0x68, 0x96, 0xCC, 0x57, 0xC9, 0xA5, 0xAD, 0x99
+ },
+ {
+ 0x78, 0x5A, 0x9D, 0x0F, 0xBD, 0x21, 0x13, 0x6D,
+ 0xBC, 0xE8, 0xFA, 0x7E, 0xAF, 0xD6, 0x3C, 0x9D,
+ 0xAD, 0x22, 0x00, 0x52, 0x97, 0x84, 0x16, 0xB3,
+ 0x1D, 0x97, 0x53, 0xEA, 0xA1, 0x49, 0x09, 0x78,
+ 0x47, 0xED, 0x9B, 0x30, 0xA6, 0x5C, 0x70, 0x50,
+ 0x7E, 0xFF, 0x01, 0x87, 0x91, 0x49, 0xED, 0x5C,
+ 0xF0, 0x47, 0x1D, 0x37, 0x79, 0x8E, 0xDC, 0x05,
+ 0xAB, 0xD5, 0x6A, 0xD4, 0xA2, 0xCC, 0xCB, 0x1D
+ },
+ {
+ 0xAD, 0x40, 0x8D, 0x2A, 0xBD, 0xDF, 0xD3, 0x7B,
+ 0x3B, 0xF3, 0x47, 0x94, 0xC1, 0xA3, 0x37, 0x1D,
+ 0x92, 0x8E, 0xD7, 0xFC, 0x8D, 0x96, 0x62, 0x25,
+ 0x33, 0x35, 0x84, 0xC5, 0x66, 0x58, 0x17, 0x83,
+ 0x2A, 0x37, 0xC0, 0x7F, 0x0D, 0xC7, 0xCB, 0x5A,
+ 0xA8, 0x74, 0xCD, 0x7D, 0x20, 0xFE, 0x8F, 0xAB,
+ 0x8E, 0xAB, 0xCB, 0x9B, 0x33, 0xD2, 0xE0, 0x84,
+ 0x1F, 0x6E, 0x20, 0x09, 0x60, 0x89, 0x9D, 0x95
+ },
+ {
+ 0x97, 0x66, 0x8F, 0x74, 0x5B, 0x60, 0x32, 0xFC,
+ 0x81, 0x5D, 0x95, 0x79, 0x32, 0x27, 0x69, 0xDC,
+ 0xCD, 0x95, 0x01, 0xA5, 0x08, 0x00, 0x29, 0xB8,
+ 0xAE, 0x82, 0x6B, 0xEF, 0xB6, 0x74, 0x23, 0x31,
+ 0xBD, 0x9F, 0x76, 0xEF, 0xEB, 0x3E, 0x2B, 0x8E,
+ 0x81, 0xA9, 0x78, 0x6B, 0x28, 0x2F, 0x50, 0x68,
+ 0xA3, 0xA2, 0x42, 0x46, 0x97, 0xA7, 0x7C, 0x41,
+ 0x87, 0x6B, 0x7E, 0x75, 0x3F, 0x4C, 0x77, 0x67
+ },
+ {
+ 0x26, 0xBB, 0x98, 0x5F, 0x47, 0xE7, 0xFE, 0xE0,
+ 0xCF, 0xD2, 0x52, 0xD4, 0xEF, 0x96, 0xBE, 0xD4,
+ 0x2B, 0x9C, 0x37, 0x0C, 0x1C, 0x6A, 0x3E, 0x8C,
+ 0x9E, 0xB0, 0x4E, 0xF7, 0xF7, 0x81, 0x8B, 0x83,
+ 0x3A, 0x0D, 0x1F, 0x04, 0x3E, 0xBA, 0xFB, 0x91,
+ 0x1D, 0xC7, 0x79, 0xE0, 0x27, 0x40, 0xA0, 0x2A,
+ 0x44, 0xD3, 0xA1, 0xEA, 0x45, 0xED, 0x4A, 0xD5,
+ 0x5E, 0x68, 0x6C, 0x92, 0x7C, 0xAF, 0xE9, 0x7E
+ },
+ {
+ 0x5B, 0xFE, 0x2B, 0x1D, 0xCF, 0x7F, 0xE9, 0xB9,
+ 0x50, 0x88, 0xAC, 0xED, 0xB5, 0x75, 0xC1, 0x90,
+ 0x16, 0xC7, 0x43, 0xB2, 0xE7, 0x63, 0xBF, 0x58,
+ 0x51, 0xAC, 0x40, 0x7C, 0x9E, 0xDA, 0x43, 0x71,
+ 0x5E, 0xDF, 0xA4, 0x8B, 0x48, 0x25, 0x49, 0x2C,
+ 0x51, 0x79, 0x59, 0x3F, 0xFF, 0x21, 0x35, 0x1B,
+ 0x76, 0xE8, 0xB7, 0xE0, 0x34, 0xE4, 0xC5, 0x3C,
+ 0x79, 0xF6, 0x1F, 0x29, 0xC4, 0x79, 0xBD, 0x08
+ },
+ {
+ 0xC7, 0x65, 0x09, 0xEF, 0x72, 0xF4, 0xA6, 0xF9,
+ 0xC9, 0xC4, 0x06, 0x18, 0xED, 0x52, 0xB2, 0x08,
+ 0x4F, 0x83, 0x50, 0x22, 0x32, 0xE0, 0xAC, 0x8B,
+ 0xDA, 0xF3, 0x26, 0x43, 0x68, 0xE4, 0xD0, 0x18,
+ 0x0F, 0x68, 0x54, 0xC4, 0xAB, 0xF4, 0xF6, 0x50,
+ 0x9C, 0x79, 0xCA, 0xAF, 0xC4, 0x4C, 0xF3, 0x19,
+ 0x4A, 0xFC, 0x57, 0xBD, 0x07, 0x7B, 0xD7, 0xB3,
+ 0xC9, 0xBD, 0xA3, 0xD4, 0xB8, 0x77, 0x58, 0x16
+ },
+ {
+ 0xD6, 0x6F, 0x2B, 0xEA, 0xB9, 0x90, 0xE3, 0x54,
+ 0xCC, 0xB9, 0x10, 0xE4, 0xE9, 0xC7, 0xAC, 0x61,
+ 0x8C, 0x7B, 0x63, 0xEF, 0x29, 0x2A, 0x96, 0xB5,
+ 0x52, 0x34, 0x1D, 0xE7, 0x8D, 0xC4, 0x6D, 0x3E,
+ 0xC8, 0xCF, 0xAB, 0xC6, 0x99, 0xB5, 0x0A, 0xF4,
+ 0x1F, 0xDA, 0x39, 0xCF, 0x1B, 0x01, 0x73, 0x66,
+ 0x09, 0x23, 0x51, 0x0A, 0xD6, 0x7F, 0xAE, 0xDE,
+ 0xF5, 0x20, 0x7C, 0xFF, 0xE8, 0x64, 0x1D, 0x20
+ },
+ {
+ 0x7D, 0x8F, 0x06, 0x72, 0x99, 0x2B, 0x79, 0xBE,
+ 0x3A, 0x36, 0x4D, 0x8E, 0x59, 0x04, 0xF4, 0xAB,
+ 0x71, 0x3B, 0xBC, 0x8A, 0xB0, 0x1B, 0x4F, 0x30,
+ 0x9A, 0xD8, 0xCC, 0xF2, 0x23, 0xCE, 0x10, 0x34,
+ 0xA8, 0x60, 0xDC, 0xB0, 0xB0, 0x05, 0x50, 0x61,
+ 0x2C, 0xC2, 0xFA, 0x17, 0xF2, 0x96, 0x9E, 0x18,
+ 0xF2, 0x2E, 0x14, 0x27, 0xD2, 0x54, 0xB4, 0xA8,
+ 0x2B, 0x3A, 0x03, 0xA3, 0xEB, 0x39, 0x4A, 0xDF
+ },
+ {
+ 0xA5, 0x6D, 0x67, 0x25, 0xBF, 0xB3, 0xDE, 0x47,
+ 0xC1, 0x41, 0x4A, 0xDF, 0x25, 0xFC, 0x8F, 0x0F,
+ 0xC9, 0x84, 0x6F, 0x69, 0x87, 0x72, 0x2B, 0xC0,
+ 0x63, 0x66, 0xD5, 0xCA, 0x4E, 0x89, 0x72, 0x29,
+ 0x25, 0xEB, 0xBC, 0x88, 0x14, 0x18, 0x84, 0x40,
+ 0x75, 0x39, 0x7A, 0x0C, 0xA8, 0x98, 0x42, 0xC7,
+ 0xB9, 0xE9, 0xE0, 0x7E, 0x1D, 0x9D, 0x18, 0x3E,
+ 0xBE, 0xB3, 0x9E, 0x12, 0x0B, 0x48, 0x3B, 0xF7
+ },
+ {
+ 0xAF, 0x5E, 0x03, 0xD7, 0xFE, 0x60, 0xC6, 0x7E,
+ 0x10, 0x31, 0x33, 0x44, 0x43, 0x4E, 0x79, 0x48,
+ 0x5A, 0x03, 0xA7, 0x58, 0xD6, 0xDC, 0xE9, 0x85,
+ 0x57, 0x47, 0x45, 0x76, 0x3C, 0x1C, 0x5C, 0x77,
+ 0xD4, 0xFB, 0x3E, 0x6F, 0xB1, 0x22, 0x30, 0x36,
+ 0x83, 0x70, 0x99, 0x3B, 0xF9, 0x0F, 0xEE, 0xD0,
+ 0xC5, 0xD1, 0x60, 0x75, 0x24, 0x56, 0x2D, 0x7C,
+ 0x09, 0xC0, 0xC2, 0x10, 0xED, 0x39, 0x3D, 0x7C
+ },
+ {
+ 0x7A, 0x20, 0x54, 0x0C, 0xC0, 0x7B, 0xF7, 0x2B,
+ 0x58, 0x24, 0x21, 0xFC, 0x34, 0x2E, 0x82, 0xF5,
+ 0x21, 0x34, 0xB6, 0x98, 0x41, 0xEC, 0x28, 0xED,
+ 0x18, 0x9E, 0x2E, 0xA6, 0xA2, 0x9D, 0xD2, 0xF8,
+ 0x2A, 0x64, 0x03, 0x52, 0xD2, 0x22, 0xB5, 0x2F,
+ 0x29, 0x11, 0xDC, 0x72, 0xA7, 0xDA, 0xB3, 0x1C,
+ 0xAA, 0xDD, 0x80, 0xC6, 0x11, 0x8F, 0x13, 0xC5,
+ 0x6B, 0x2A, 0x1E, 0x43, 0x73, 0xBE, 0x0E, 0xA3
+ },
+ {
+ 0x48, 0x6F, 0x02, 0xC6, 0x3E, 0x54, 0x67, 0xEA,
+ 0x1F, 0xDD, 0xE7, 0xE8, 0x2B, 0xFA, 0xCC, 0x2C,
+ 0x1B, 0xA5, 0xD6, 0x36, 0xD9, 0xF3, 0xD0, 0x8B,
+ 0x21, 0x0D, 0xA3, 0xF3, 0x72, 0xF7, 0x06, 0xEC,
+ 0x21, 0x8C, 0xC1, 0x7F, 0xF6, 0x0A, 0xEF, 0x70,
+ 0x3B, 0xBE, 0x0C, 0x15, 0xC3, 0x8A, 0xE5, 0x5D,
+ 0x28, 0x6A, 0x68, 0x4F, 0x86, 0x4C, 0x78, 0x21,
+ 0x1C, 0xCA, 0xB4, 0x17, 0x8C, 0x92, 0xAD, 0xBA
+ },
+ {
+ 0x1C, 0x7A, 0x5C, 0x1D, 0xED, 0xCD, 0x04, 0xA9,
+ 0x21, 0x78, 0x8F, 0x7E, 0xB2, 0x33, 0x61, 0xCA,
+ 0x19, 0x53, 0xB0, 0x4B, 0x9C, 0x7A, 0xEC, 0x35,
+ 0xD6, 0x5E, 0xA3, 0xE4, 0x99, 0x6D, 0xB2, 0x6F,
+ 0x28, 0x12, 0x78, 0xEA, 0x4A, 0xE6, 0x66, 0xAD,
+ 0x81, 0x02, 0x7D, 0x98, 0xAF, 0x57, 0x26, 0x2C,
+ 0xDB, 0xFA, 0x4C, 0x08, 0x5F, 0x42, 0x10, 0x56,
+ 0x8C, 0x7E, 0x15, 0xEE, 0xC7, 0x80, 0x51, 0x14
+ },
+ {
+ 0x9C, 0xE3, 0xFA, 0x9A, 0x86, 0x0B, 0xDB, 0xD5,
+ 0x37, 0x8F, 0xD6, 0xD7, 0xB8, 0xB6, 0x71, 0xC6,
+ 0xCB, 0x76, 0x92, 0x91, 0x0C, 0xE8, 0xF9, 0xB6,
+ 0xCB, 0x41, 0x22, 0xCB, 0xCB, 0xE6, 0xAC, 0x06,
+ 0xCA, 0x04, 0x22, 0xCE, 0xF1, 0x22, 0x59, 0x35,
+ 0x05, 0x3B, 0x7D, 0x19, 0x3A, 0x81, 0xB9, 0xE9,
+ 0x72, 0xEB, 0x85, 0xA1, 0xD3, 0x07, 0x4F, 0x14,
+ 0xCB, 0xB5, 0xEC, 0x9F, 0x05, 0x73, 0x89, 0x2D
+ },
+ {
+ 0xA9, 0x11, 0x87, 0xBE, 0x5C, 0x37, 0x1C, 0x42,
+ 0x65, 0xC1, 0x74, 0xFD, 0x46, 0x53, 0xB8, 0xAB,
+ 0x70, 0x85, 0x51, 0xF8, 0x3D, 0x1F, 0xEE, 0x1C,
+ 0xC1, 0x47, 0x95, 0x81, 0xBC, 0x00, 0x6D, 0x6F,
+ 0xB7, 0x8F, 0xCC, 0x9A, 0x5D, 0xEE, 0x1D, 0xB3,
+ 0x66, 0x6F, 0x50, 0x8F, 0x97, 0x80, 0xA3, 0x75,
+ 0x93, 0xEB, 0xCC, 0xCF, 0x5F, 0xBE, 0xD3, 0x96,
+ 0x67, 0xDC, 0x63, 0x61, 0xE9, 0x21, 0xF7, 0x79
+ },
+ {
+ 0x46, 0x25, 0x76, 0x7D, 0x7B, 0x1D, 0x3D, 0x3E,
+ 0xD2, 0xFB, 0xC6, 0x74, 0xAF, 0x14, 0xE0, 0x24,
+ 0x41, 0x52, 0xF2, 0xA4, 0x02, 0x1F, 0xCF, 0x33,
+ 0x11, 0x50, 0x5D, 0x89, 0xBD, 0x81, 0xE2, 0xF9,
+ 0xF9, 0xA5, 0x00, 0xC3, 0xB1, 0x99, 0x91, 0x4D,
+ 0xB4, 0x95, 0x00, 0xB3, 0xC9, 0x8D, 0x03, 0xEA,
+ 0x93, 0x28, 0x67, 0x51, 0xA6, 0x86, 0xA3, 0xB8,
+ 0x75, 0xDA, 0xAB, 0x0C, 0xCD, 0x63, 0xB4, 0x4F
+ },
+ {
+ 0x43, 0xDF, 0xDF, 0xE1, 0xB0, 0x14, 0xFE, 0xD3,
+ 0xA2, 0xAC, 0xAB, 0xB7, 0xF3, 0xE9, 0xA1, 0x82,
+ 0xF2, 0xAA, 0x18, 0x01, 0x9D, 0x27, 0xE3, 0xE6,
+ 0xCD, 0xCF, 0x31, 0xA1, 0x5B, 0x42, 0x8E, 0x91,
+ 0xE7, 0xB0, 0x8C, 0xF5, 0xE5, 0xC3, 0x76, 0xFC,
+ 0xE2, 0xD8, 0xA2, 0x8F, 0xF8, 0x5A, 0xB0, 0xA0,
+ 0xA1, 0x65, 0x6E, 0xDB, 0x4A, 0x0A, 0x91, 0x53,
+ 0x26, 0x20, 0x09, 0x6D, 0x9A, 0x5A, 0x65, 0x2D
+ },
+ {
+ 0x27, 0x9E, 0x32, 0x02, 0xBE, 0x39, 0x89, 0xBA,
+ 0x31, 0x12, 0x77, 0x25, 0x85, 0x17, 0x74, 0x87,
+ 0xE4, 0xFE, 0x3E, 0xE3, 0xEA, 0xB4, 0x9C, 0x2F,
+ 0x7F, 0xA7, 0xFE, 0x87, 0xCF, 0xE7, 0xB8, 0x0D,
+ 0x3E, 0x03, 0x55, 0xED, 0xFF, 0x6D, 0x03, 0x1E,
+ 0x6C, 0x96, 0xC7, 0x95, 0xDB, 0x1C, 0x6F, 0x04,
+ 0x18, 0x80, 0xEC, 0x38, 0x24, 0xDE, 0xFA, 0xCF,
+ 0x92, 0x63, 0x82, 0x0A, 0x8E, 0x73, 0x27, 0xDE
+ },
+ {
+ 0xEA, 0x2D, 0x06, 0x6A, 0xC2, 0x29, 0xD4, 0xD4,
+ 0xB6, 0x16, 0xA8, 0xBE, 0xDE, 0xC7, 0x34, 0x32,
+ 0x52, 0x24, 0xE4, 0xB4, 0xE5, 0x8F, 0x1A, 0xE6,
+ 0xDA, 0xD7, 0xE4, 0x0C, 0x2D, 0xA2, 0x91, 0x96,
+ 0xC3, 0xB1, 0xEA, 0x95, 0x71, 0xDA, 0xCC, 0x81,
+ 0xE8, 0x73, 0x28, 0xCA, 0xA0, 0x21, 0x1E, 0x09,
+ 0x02, 0x7B, 0x05, 0x24, 0xAA, 0x3F, 0x4A, 0x84,
+ 0x99, 0x17, 0xB3, 0x58, 0x67, 0x47, 0xEB, 0xBB
+ },
+ {
+ 0x49, 0xF0, 0x14, 0xF5, 0xC6, 0x18, 0x22, 0xC8,
+ 0x99, 0xAB, 0x5C, 0xAE, 0x51, 0xBE, 0x40, 0x44,
+ 0xA4, 0x49, 0x5E, 0x77, 0x7D, 0xEB, 0x7D, 0xA9,
+ 0xB6, 0xD8, 0x49, 0x0E, 0xFB, 0xB8, 0x75, 0x30,
+ 0xAD, 0xF2, 0x93, 0xDA, 0xF0, 0x79, 0xF9, 0x4C,
+ 0x33, 0xB7, 0x04, 0x4E, 0xF6, 0x2E, 0x2E, 0x5B,
+ 0xB3, 0xEB, 0x11, 0xE1, 0x73, 0x04, 0xF8, 0x45,
+ 0x3E, 0xE6, 0xCE, 0x24, 0xF0, 0x33, 0xDD, 0xB0
+ },
+ {
+ 0x92, 0x33, 0x49, 0x03, 0x44, 0xE5, 0xB0, 0xDC,
+ 0x59, 0x12, 0x67, 0x1B, 0x7A, 0xE5, 0x4C, 0xEE,
+ 0x77, 0x30, 0xDB, 0xE1, 0xF4, 0xC7, 0xD9, 0x2A,
+ 0x4D, 0x3E, 0x3A, 0xAB, 0x50, 0x57, 0x17, 0x08,
+ 0xDB, 0x51, 0xDC, 0xF9, 0xC2, 0x94, 0x45, 0x91,
+ 0xDB, 0x65, 0x1D, 0xB3, 0x2D, 0x22, 0x93, 0x5B,
+ 0x86, 0x94, 0x49, 0x69, 0xBE, 0x77, 0xD5, 0xB5,
+ 0xFE, 0xAE, 0x6C, 0x38, 0x40, 0xA8, 0xDB, 0x26
+ },
+ {
+ 0xB6, 0xE7, 0x5E, 0x6F, 0x4C, 0x7F, 0x45, 0x3B,
+ 0x74, 0x65, 0xD2, 0x5B, 0x5A, 0xC8, 0xC7, 0x19,
+ 0x69, 0x02, 0xEA, 0xA9, 0x53, 0x87, 0x52, 0x28,
+ 0xC8, 0x63, 0x4E, 0x16, 0xE2, 0xAE, 0x1F, 0x38,
+ 0xBC, 0x32, 0x75, 0x30, 0x43, 0x35, 0xF5, 0x98,
+ 0x9E, 0xCC, 0xC1, 0xE3, 0x41, 0x67, 0xD4, 0xE6,
+ 0x8D, 0x77, 0x19, 0x96, 0x8F, 0xBA, 0x8E, 0x2F,
+ 0xE6, 0x79, 0x47, 0xC3, 0x5C, 0x48, 0xE8, 0x06
+ },
+ {
+ 0xCC, 0x14, 0xCA, 0x66, 0x5A, 0xF1, 0x48, 0x3E,
+ 0xFB, 0xC3, 0xAF, 0x80, 0x08, 0x0E, 0x65, 0x0D,
+ 0x50, 0x46, 0xA3, 0x93, 0x2F, 0x4F, 0x51, 0xF3,
+ 0xFE, 0x90, 0xA0, 0x70, 0x5E, 0xC2, 0x51, 0x04,
+ 0xAD, 0xF0, 0x78, 0x39, 0x26, 0x5D, 0xC5, 0x1D,
+ 0x43, 0x40, 0x14, 0x11, 0x24, 0x6E, 0x47, 0x4F,
+ 0x0D, 0x5E, 0x56, 0x37, 0xAF, 0x94, 0x76, 0x72,
+ 0x83, 0xD5, 0x3E, 0x06, 0x17, 0xE9, 0x81, 0xF4
+ },
+ {
+ 0x23, 0x0A, 0x1C, 0x85, 0x7C, 0xB2, 0xE7, 0x85,
+ 0x2E, 0x41, 0xB6, 0x47, 0xE9, 0x0E, 0x45, 0x85,
+ 0xD2, 0xD8, 0x81, 0xE1, 0x73, 0x4D, 0xC3, 0x89,
+ 0x55, 0x35, 0x6E, 0x8D, 0xD7, 0xBF, 0xF3, 0x90,
+ 0x53, 0x09, 0x2C, 0x6B, 0x38, 0xE2, 0x36, 0xE1,
+ 0x89, 0x95, 0x25, 0x64, 0x70, 0x73, 0xDD, 0xDF,
+ 0x68, 0x95, 0xD6, 0x42, 0x06, 0x32, 0x5E, 0x76,
+ 0x47, 0xF2, 0x75, 0x56, 0x7B, 0x25, 0x59, 0x09
+ },
+ {
+ 0xCB, 0xB6, 0x53, 0x21, 0xAC, 0x43, 0x6E, 0x2F,
+ 0xFD, 0xAB, 0x29, 0x36, 0x35, 0x9C, 0xE4, 0x90,
+ 0x23, 0xF7, 0xDE, 0xE7, 0x61, 0x4E, 0xF2, 0x8D,
+ 0x17, 0x3C, 0x3D, 0x27, 0xC5, 0xD1, 0xBF, 0xFA,
+ 0x51, 0x55, 0x3D, 0x43, 0x3F, 0x8E, 0xE3, 0xC9,
+ 0xE4, 0x9C, 0x05, 0xA2, 0xB8, 0x83, 0xCC, 0xE9,
+ 0x54, 0xC9, 0xA8, 0x09, 0x3B, 0x80, 0x61, 0x2A,
+ 0x0C, 0xDD, 0x47, 0x32, 0xE0, 0x41, 0xF9, 0x95
+ },
+ {
+ 0x3E, 0x7E, 0x57, 0x00, 0x74, 0x33, 0x72, 0x75,
+ 0xEF, 0xB5, 0x13, 0x15, 0x58, 0x80, 0x34, 0xC3,
+ 0xCF, 0x0D, 0xDD, 0xCA, 0x20, 0xB4, 0x61, 0x2E,
+ 0x0B, 0xD5, 0xB8, 0x81, 0xE7, 0xE5, 0x47, 0x6D,
+ 0x31, 0x9C, 0xE4, 0xFE, 0x9F, 0x19, 0x18, 0x6E,
+ 0x4C, 0x08, 0x26, 0xF4, 0x4F, 0x13, 0x1E, 0xB0,
+ 0x48, 0xE6, 0x5B, 0xE2, 0x42, 0xB1, 0x17, 0x2C,
+ 0x63, 0xBA, 0xDB, 0x12, 0x3A, 0xB0, 0xCB, 0xE8
+ },
+ {
+ 0xD3, 0x2E, 0x9E, 0xC0, 0x2D, 0x38, 0xD4, 0xE1,
+ 0xB8, 0x24, 0x9D, 0xF8, 0xDC, 0xB0, 0x0C, 0x5B,
+ 0x9C, 0x68, 0xEB, 0x89, 0x22, 0x67, 0x2E, 0x35,
+ 0x05, 0x39, 0x3B, 0x6A, 0x21, 0x0B, 0xA5, 0x6F,
+ 0x94, 0x96, 0xE5, 0xEE, 0x04, 0x90, 0xEF, 0x38,
+ 0x7C, 0x3C, 0xDE, 0xC0, 0x61, 0xF0, 0x6B, 0xC0,
+ 0x38, 0x2D, 0x93, 0x04, 0xCA, 0xFB, 0xB8, 0xE0,
+ 0xCD, 0x33, 0xD5, 0x70, 0x29, 0xE6, 0x2D, 0xF2
+ },
+ {
+ 0x8C, 0x15, 0x12, 0x46, 0x60, 0x89, 0xF0, 0x5B,
+ 0x37, 0x75, 0xC2, 0x62, 0xB6, 0x2D, 0x22, 0xB8,
+ 0x38, 0x54, 0xA8, 0x32, 0x18, 0x13, 0x0B, 0x4E,
+ 0xC9, 0x1B, 0x3C, 0xCB, 0xD2, 0x93, 0xD2, 0xA5,
+ 0x43, 0x02, 0xCE, 0xCA, 0xAB, 0x9B, 0x10, 0x0C,
+ 0x68, 0xD1, 0xE6, 0xDD, 0xC8, 0xF0, 0x7C, 0xDD,
+ 0xBD, 0xFE, 0x6F, 0xDA, 0xAA, 0xF0, 0x99, 0xCC,
+ 0x09, 0xD6, 0xB7, 0x25, 0x87, 0x9C, 0x63, 0x69
+ },
+ {
+ 0x91, 0xA7, 0xF6, 0x1C, 0x97, 0xC2, 0x91, 0x1E,
+ 0x4C, 0x81, 0x2E, 0xF7, 0x1D, 0x78, 0x0A, 0xD8,
+ 0xFA, 0x78, 0x87, 0x94, 0x56, 0x1D, 0x08, 0x30,
+ 0x3F, 0xD1, 0xC1, 0xCB, 0x60, 0x8A, 0x46, 0xA1,
+ 0x25, 0x63, 0x08, 0x6E, 0xC5, 0xB3, 0x9D, 0x47,
+ 0x1A, 0xED, 0x94, 0xFB, 0x0F, 0x6C, 0x67, 0x8A,
+ 0x43, 0xB8, 0x79, 0x29, 0x32, 0xF9, 0x02, 0x8D,
+ 0x77, 0x2A, 0x22, 0x76, 0x8E, 0xA2, 0x3A, 0x9B
+ },
+ {
+ 0x4F, 0x6B, 0xB2, 0x22, 0xA3, 0x95, 0xE8, 0xB1,
+ 0x8F, 0x6B, 0xA1, 0x55, 0x47, 0x7A, 0xED, 0x3F,
+ 0x07, 0x29, 0xAC, 0x9E, 0x83, 0xE1, 0x6D, 0x31,
+ 0xA2, 0xA8, 0xBC, 0x65, 0x54, 0x22, 0xB8, 0x37,
+ 0xC8, 0x91, 0xC6, 0x19, 0x9E, 0x6F, 0x0D, 0x75,
+ 0x79, 0x9E, 0x3B, 0x69, 0x15, 0x25, 0xC5, 0x81,
+ 0x95, 0x35, 0x17, 0xF2, 0x52, 0xC4, 0xB9, 0xE3,
+ 0xA2, 0x7A, 0x28, 0xFB, 0xAF, 0x49, 0x64, 0x4C
+ },
+ {
+ 0x5D, 0x06, 0xC0, 0x7E, 0x7A, 0x64, 0x6C, 0x41,
+ 0x3A, 0x50, 0x1C, 0x3F, 0x4B, 0xB2, 0xFC, 0x38,
+ 0x12, 0x7D, 0xE7, 0x50, 0x9B, 0x70, 0x77, 0xC4,
+ 0xD9, 0xB5, 0x61, 0x32, 0x01, 0xC1, 0xAA, 0x02,
+ 0xFD, 0x5F, 0x79, 0xD2, 0x74, 0x59, 0x15, 0xDD,
+ 0x57, 0xFB, 0xCB, 0x4C, 0xE0, 0x86, 0x95, 0xF6,
+ 0xEF, 0xC0, 0xCB, 0x3D, 0x2D, 0x33, 0x0E, 0x19,
+ 0xB4, 0xB0, 0xE6, 0x00, 0x4E, 0xA6, 0x47, 0x1E
+ },
+ {
+ 0xB9, 0x67, 0x56, 0xE5, 0x79, 0x09, 0x96, 0x8F,
+ 0x14, 0xB7, 0x96, 0xA5, 0xD3, 0x0F, 0x4C, 0x9D,
+ 0x67, 0x14, 0x72, 0xCF, 0x82, 0xC8, 0xCF, 0xB2,
+ 0xCA, 0xCA, 0x7A, 0xC7, 0xA4, 0x4C, 0xA0, 0xA1,
+ 0x4C, 0x98, 0x42, 0xD0, 0x0C, 0x82, 0xE3, 0x37,
+ 0x50, 0x2C, 0x94, 0xD5, 0x96, 0x0A, 0xCA, 0x4C,
+ 0x49, 0x2E, 0xA7, 0xB0, 0xDF, 0x91, 0x9D, 0xDF,
+ 0x1A, 0xAD, 0xA2, 0xA2, 0x75, 0xBB, 0x10, 0xD4
+ },
+ {
+ 0xFF, 0x0A, 0x01, 0x5E, 0x98, 0xDB, 0x9C, 0x99,
+ 0xF0, 0x39, 0x77, 0x71, 0x0A, 0xAC, 0x3E, 0x65,
+ 0x8C, 0x0D, 0x89, 0x6F, 0x6D, 0x71, 0xD6, 0x18,
+ 0xBA, 0x79, 0xDC, 0x6C, 0xF7, 0x2A, 0xC7, 0x5B,
+ 0x7C, 0x03, 0x8E, 0xB6, 0x86, 0x2D, 0xED, 0xE4,
+ 0x54, 0x3E, 0x14, 0x54, 0x13, 0xA6, 0x36, 0x8D,
+ 0x69, 0xF5, 0x72, 0x2C, 0x82, 0x7B, 0xA3, 0xEF,
+ 0x25, 0xB6, 0xAE, 0x64, 0x40, 0xD3, 0x92, 0x76
+ },
+ {
+ 0x5B, 0x21, 0xC5, 0xFD, 0x88, 0x68, 0x36, 0x76,
+ 0x12, 0x47, 0x4F, 0xA2, 0xE7, 0x0E, 0x9C, 0xFA,
+ 0x22, 0x01, 0xFF, 0xEE, 0xE8, 0xFA, 0xFA, 0xB5,
+ 0x79, 0x7A, 0xD5, 0x8F, 0xEF, 0xA1, 0x7C, 0x9B,
+ 0x5B, 0x10, 0x7D, 0xA4, 0xA3, 0xDB, 0x63, 0x20,
+ 0xBA, 0xAF, 0x2C, 0x86, 0x17, 0xD5, 0xA5, 0x1D,
+ 0xF9, 0x14, 0xAE, 0x88, 0xDA, 0x38, 0x67, 0xC2,
+ 0xD4, 0x1F, 0x0C, 0xC1, 0x4F, 0xA6, 0x79, 0x28
+ },
+};
+
+
+
+
+static const uint8_t blake2b_keyed_kat[KAT_LENGTH][BLAKE2B_OUTBYTES] =
+{
+ {
+ 0x10, 0xEB, 0xB6, 0x77, 0x00, 0xB1, 0x86, 0x8E,
+ 0xFB, 0x44, 0x17, 0x98, 0x7A, 0xCF, 0x46, 0x90,
+ 0xAE, 0x9D, 0x97, 0x2F, 0xB7, 0xA5, 0x90, 0xC2,
+ 0xF0, 0x28, 0x71, 0x79, 0x9A, 0xAA, 0x47, 0x86,
+ 0xB5, 0xE9, 0x96, 0xE8, 0xF0, 0xF4, 0xEB, 0x98,
+ 0x1F, 0xC2, 0x14, 0xB0, 0x05, 0xF4, 0x2D, 0x2F,
+ 0xF4, 0x23, 0x34, 0x99, 0x39, 0x16, 0x53, 0xDF,
+ 0x7A, 0xEF, 0xCB, 0xC1, 0x3F, 0xC5, 0x15, 0x68
+ },
+ {
+ 0x96, 0x1F, 0x6D, 0xD1, 0xE4, 0xDD, 0x30, 0xF6,
+ 0x39, 0x01, 0x69, 0x0C, 0x51, 0x2E, 0x78, 0xE4,
+ 0xB4, 0x5E, 0x47, 0x42, 0xED, 0x19, 0x7C, 0x3C,
+ 0x5E, 0x45, 0xC5, 0x49, 0xFD, 0x25, 0xF2, 0xE4,
+ 0x18, 0x7B, 0x0B, 0xC9, 0xFE, 0x30, 0x49, 0x2B,
+ 0x16, 0xB0, 0xD0, 0xBC, 0x4E, 0xF9, 0xB0, 0xF3,
+ 0x4C, 0x70, 0x03, 0xFA, 0xC0, 0x9A, 0x5E, 0xF1,
+ 0x53, 0x2E, 0x69, 0x43, 0x02, 0x34, 0xCE, 0xBD
+ },
+ {
+ 0xDA, 0x2C, 0xFB, 0xE2, 0xD8, 0x40, 0x9A, 0x0F,
+ 0x38, 0x02, 0x61, 0x13, 0x88, 0x4F, 0x84, 0xB5,
+ 0x01, 0x56, 0x37, 0x1A, 0xE3, 0x04, 0xC4, 0x43,
+ 0x01, 0x73, 0xD0, 0x8A, 0x99, 0xD9, 0xFB, 0x1B,
+ 0x98, 0x31, 0x64, 0xA3, 0x77, 0x07, 0x06, 0xD5,
+ 0x37, 0xF4, 0x9E, 0x0C, 0x91, 0x6D, 0x9F, 0x32,
+ 0xB9, 0x5C, 0xC3, 0x7A, 0x95, 0xB9, 0x9D, 0x85,
+ 0x74, 0x36, 0xF0, 0x23, 0x2C, 0x88, 0xA9, 0x65
+ },
+ {
+ 0x33, 0xD0, 0x82, 0x5D, 0xDD, 0xF7, 0xAD, 0xA9,
+ 0x9B, 0x0E, 0x7E, 0x30, 0x71, 0x04, 0xAD, 0x07,
+ 0xCA, 0x9C, 0xFD, 0x96, 0x92, 0x21, 0x4F, 0x15,
+ 0x61, 0x35, 0x63, 0x15, 0xE7, 0x84, 0xF3, 0xE5,
+ 0xA1, 0x7E, 0x36, 0x4A, 0xE9, 0xDB, 0xB1, 0x4C,
+ 0xB2, 0x03, 0x6D, 0xF9, 0x32, 0xB7, 0x7F, 0x4B,
+ 0x29, 0x27, 0x61, 0x36, 0x5F, 0xB3, 0x28, 0xDE,
+ 0x7A, 0xFD, 0xC6, 0xD8, 0x99, 0x8F, 0x5F, 0xC1
+ },
+ {
+ 0xBE, 0xAA, 0x5A, 0x3D, 0x08, 0xF3, 0x80, 0x71,
+ 0x43, 0xCF, 0x62, 0x1D, 0x95, 0xCD, 0x69, 0x05,
+ 0x14, 0xD0, 0xB4, 0x9E, 0xFF, 0xF9, 0xC9, 0x1D,
+ 0x24, 0xB5, 0x92, 0x41, 0xEC, 0x0E, 0xEF, 0xA5,
+ 0xF6, 0x01, 0x96, 0xD4, 0x07, 0x04, 0x8B, 0xBA,
+ 0x8D, 0x21, 0x46, 0x82, 0x8E, 0xBC, 0xB0, 0x48,
+ 0x8D, 0x88, 0x42, 0xFD, 0x56, 0xBB, 0x4F, 0x6D,
+ 0xF8, 0xE1, 0x9C, 0x4B, 0x4D, 0xAA, 0xB8, 0xAC
+ },
+ {
+ 0x09, 0x80, 0x84, 0xB5, 0x1F, 0xD1, 0x3D, 0xEA,
+ 0xE5, 0xF4, 0x32, 0x0D, 0xE9, 0x4A, 0x68, 0x8E,
+ 0xE0, 0x7B, 0xAE, 0xA2, 0x80, 0x04, 0x86, 0x68,
+ 0x9A, 0x86, 0x36, 0x11, 0x7B, 0x46, 0xC1, 0xF4,
+ 0xC1, 0xF6, 0xAF, 0x7F, 0x74, 0xAE, 0x7C, 0x85,
+ 0x76, 0x00, 0x45, 0x6A, 0x58, 0xA3, 0xAF, 0x25,
+ 0x1D, 0xC4, 0x72, 0x3A, 0x64, 0xCC, 0x7C, 0x0A,
+ 0x5A, 0xB6, 0xD9, 0xCA, 0xC9, 0x1C, 0x20, 0xBB
+ },
+ {
+ 0x60, 0x44, 0x54, 0x0D, 0x56, 0x08, 0x53, 0xEB,
+ 0x1C, 0x57, 0xDF, 0x00, 0x77, 0xDD, 0x38, 0x10,
+ 0x94, 0x78, 0x1C, 0xDB, 0x90, 0x73, 0xE5, 0xB1,
+ 0xB3, 0xD3, 0xF6, 0xC7, 0x82, 0x9E, 0x12, 0x06,
+ 0x6B, 0xBA, 0xCA, 0x96, 0xD9, 0x89, 0xA6, 0x90,
+ 0xDE, 0x72, 0xCA, 0x31, 0x33, 0xA8, 0x36, 0x52,
+ 0xBA, 0x28, 0x4A, 0x6D, 0x62, 0x94, 0x2B, 0x27,
+ 0x1F, 0xFA, 0x26, 0x20, 0xC9, 0xE7, 0x5B, 0x1F
+ },
+ {
+ 0x7A, 0x8C, 0xFE, 0x9B, 0x90, 0xF7, 0x5F, 0x7E,
+ 0xCB, 0x3A, 0xCC, 0x05, 0x3A, 0xAE, 0xD6, 0x19,
+ 0x31, 0x12, 0xB6, 0xF6, 0xA4, 0xAE, 0xEB, 0x3F,
+ 0x65, 0xD3, 0xDE, 0x54, 0x19, 0x42, 0xDE, 0xB9,
+ 0xE2, 0x22, 0x81, 0x52, 0xA3, 0xC4, 0xBB, 0xBE,
+ 0x72, 0xFC, 0x3B, 0x12, 0x62, 0x95, 0x28, 0xCF,
+ 0xBB, 0x09, 0xFE, 0x63, 0x0F, 0x04, 0x74, 0x33,
+ 0x9F, 0x54, 0xAB, 0xF4, 0x53, 0xE2, 0xED, 0x52
+ },
+ {
+ 0x38, 0x0B, 0xEA, 0xF6, 0xEA, 0x7C, 0xC9, 0x36,
+ 0x5E, 0x27, 0x0E, 0xF0, 0xE6, 0xF3, 0xA6, 0x4F,
+ 0xB9, 0x02, 0xAC, 0xAE, 0x51, 0xDD, 0x55, 0x12,
+ 0xF8, 0x42, 0x59, 0xAD, 0x2C, 0x91, 0xF4, 0xBC,
+ 0x41, 0x08, 0xDB, 0x73, 0x19, 0x2A, 0x5B, 0xBF,
+ 0xB0, 0xCB, 0xCF, 0x71, 0xE4, 0x6C, 0x3E, 0x21,
+ 0xAE, 0xE1, 0xC5, 0xE8, 0x60, 0xDC, 0x96, 0xE8,
+ 0xEB, 0x0B, 0x7B, 0x84, 0x26, 0xE6, 0xAB, 0xE9
+ },
+ {
+ 0x60, 0xFE, 0x3C, 0x45, 0x35, 0xE1, 0xB5, 0x9D,
+ 0x9A, 0x61, 0xEA, 0x85, 0x00, 0xBF, 0xAC, 0x41,
+ 0xA6, 0x9D, 0xFF, 0xB1, 0xCE, 0xAD, 0xD9, 0xAC,
+ 0xA3, 0x23, 0xE9, 0xA6, 0x25, 0xB6, 0x4D, 0xA5,
+ 0x76, 0x3B, 0xAD, 0x72, 0x26, 0xDA, 0x02, 0xB9,
+ 0xC8, 0xC4, 0xF1, 0xA5, 0xDE, 0x14, 0x0A, 0xC5,
+ 0xA6, 0xC1, 0x12, 0x4E, 0x4F, 0x71, 0x8C, 0xE0,
+ 0xB2, 0x8E, 0xA4, 0x73, 0x93, 0xAA, 0x66, 0x37
+ },
+ {
+ 0x4F, 0xE1, 0x81, 0xF5, 0x4A, 0xD6, 0x3A, 0x29,
+ 0x83, 0xFE, 0xAA, 0xF7, 0x7D, 0x1E, 0x72, 0x35,
+ 0xC2, 0xBE, 0xB1, 0x7F, 0xA3, 0x28, 0xB6, 0xD9,
+ 0x50, 0x5B, 0xDA, 0x32, 0x7D, 0xF1, 0x9F, 0xC3,
+ 0x7F, 0x02, 0xC4, 0xB6, 0xF0, 0x36, 0x8C, 0xE2,
+ 0x31, 0x47, 0x31, 0x3A, 0x8E, 0x57, 0x38, 0xB5,
+ 0xFA, 0x2A, 0x95, 0xB2, 0x9D, 0xE1, 0xC7, 0xF8,
+ 0x26, 0x4E, 0xB7, 0x7B, 0x69, 0xF5, 0x85, 0xCD
+ },
+ {
+ 0xF2, 0x28, 0x77, 0x3C, 0xE3, 0xF3, 0xA4, 0x2B,
+ 0x5F, 0x14, 0x4D, 0x63, 0x23, 0x7A, 0x72, 0xD9,
+ 0x96, 0x93, 0xAD, 0xB8, 0x83, 0x7D, 0x0E, 0x11,
+ 0x2A, 0x8A, 0x0F, 0x8F, 0xFF, 0xF2, 0xC3, 0x62,
+ 0x85, 0x7A, 0xC4, 0x9C, 0x11, 0xEC, 0x74, 0x0D,
+ 0x15, 0x00, 0x74, 0x9D, 0xAC, 0x9B, 0x1F, 0x45,
+ 0x48, 0x10, 0x8B, 0xF3, 0x15, 0x57, 0x94, 0xDC,
+ 0xC9, 0xE4, 0x08, 0x28, 0x49, 0xE2, 0xB8, 0x5B
+ },
+ {
+ 0x96, 0x24, 0x52, 0xA8, 0x45, 0x5C, 0xC5, 0x6C,
+ 0x85, 0x11, 0x31, 0x7E, 0x3B, 0x1F, 0x3B, 0x2C,
+ 0x37, 0xDF, 0x75, 0xF5, 0x88, 0xE9, 0x43, 0x25,
+ 0xFD, 0xD7, 0x70, 0x70, 0x35, 0x9C, 0xF6, 0x3A,
+ 0x9A, 0xE6, 0xE9, 0x30, 0x93, 0x6F, 0xDF, 0x8E,
+ 0x1E, 0x08, 0xFF, 0xCA, 0x44, 0x0C, 0xFB, 0x72,
+ 0xC2, 0x8F, 0x06, 0xD8, 0x9A, 0x21, 0x51, 0xD1,
+ 0xC4, 0x6C, 0xD5, 0xB2, 0x68, 0xEF, 0x85, 0x63
+ },
+ {
+ 0x43, 0xD4, 0x4B, 0xFA, 0x18, 0x76, 0x8C, 0x59,
+ 0x89, 0x6B, 0xF7, 0xED, 0x17, 0x65, 0xCB, 0x2D,
+ 0x14, 0xAF, 0x8C, 0x26, 0x02, 0x66, 0x03, 0x90,
+ 0x99, 0xB2, 0x5A, 0x60, 0x3E, 0x4D, 0xDC, 0x50,
+ 0x39, 0xD6, 0xEF, 0x3A, 0x91, 0x84, 0x7D, 0x10,
+ 0x88, 0xD4, 0x01, 0xC0, 0xC7, 0xE8, 0x47, 0x78,
+ 0x1A, 0x8A, 0x59, 0x0D, 0x33, 0xA3, 0xC6, 0xCB,
+ 0x4D, 0xF0, 0xFA, 0xB1, 0xC2, 0xF2, 0x23, 0x55
+ },
+ {
+ 0xDC, 0xFF, 0xA9, 0xD5, 0x8C, 0x2A, 0x4C, 0xA2,
+ 0xCD, 0xBB, 0x0C, 0x7A, 0xA4, 0xC4, 0xC1, 0xD4,
+ 0x51, 0x65, 0x19, 0x00, 0x89, 0xF4, 0xE9, 0x83,
+ 0xBB, 0x1C, 0x2C, 0xAB, 0x4A, 0xAE, 0xFF, 0x1F,
+ 0xA2, 0xB5, 0xEE, 0x51, 0x6F, 0xEC, 0xD7, 0x80,
+ 0x54, 0x02, 0x40, 0xBF, 0x37, 0xE5, 0x6C, 0x8B,
+ 0xCC, 0xA7, 0xFA, 0xB9, 0x80, 0xE1, 0xE6, 0x1C,
+ 0x94, 0x00, 0xD8, 0xA9, 0xA5, 0xB1, 0x4A, 0xC6
+ },
+ {
+ 0x6F, 0xBF, 0x31, 0xB4, 0x5A, 0xB0, 0xC0, 0xB8,
+ 0xDA, 0xD1, 0xC0, 0xF5, 0xF4, 0x06, 0x13, 0x79,
+ 0x91, 0x2D, 0xDE, 0x5A, 0xA9, 0x22, 0x09, 0x9A,
+ 0x03, 0x0B, 0x72, 0x5C, 0x73, 0x34, 0x6C, 0x52,
+ 0x42, 0x91, 0xAD, 0xEF, 0x89, 0xD2, 0xF6, 0xFD,
+ 0x8D, 0xFC, 0xDA, 0x6D, 0x07, 0xDA, 0xD8, 0x11,
+ 0xA9, 0x31, 0x45, 0x36, 0xC2, 0x91, 0x5E, 0xD4,
+ 0x5D, 0xA3, 0x49, 0x47, 0xE8, 0x3D, 0xE3, 0x4E
+ },
+ {
+ 0xA0, 0xC6, 0x5B, 0xDD, 0xDE, 0x8A, 0xDE, 0xF5,
+ 0x72, 0x82, 0xB0, 0x4B, 0x11, 0xE7, 0xBC, 0x8A,
+ 0xAB, 0x10, 0x5B, 0x99, 0x23, 0x1B, 0x75, 0x0C,
+ 0x02, 0x1F, 0x4A, 0x73, 0x5C, 0xB1, 0xBC, 0xFA,
+ 0xB8, 0x75, 0x53, 0xBB, 0xA3, 0xAB, 0xB0, 0xC3,
+ 0xE6, 0x4A, 0x0B, 0x69, 0x55, 0x28, 0x51, 0x85,
+ 0xA0, 0xBD, 0x35, 0xFB, 0x8C, 0xFD, 0xE5, 0x57,
+ 0x32, 0x9B, 0xEB, 0xB1, 0xF6, 0x29, 0xEE, 0x93
+ },
+ {
+ 0xF9, 0x9D, 0x81, 0x55, 0x50, 0x55, 0x8E, 0x81,
+ 0xEC, 0xA2, 0xF9, 0x67, 0x18, 0xAE, 0xD1, 0x0D,
+ 0x86, 0xF3, 0xF1, 0xCF, 0xB6, 0x75, 0xCC, 0xE0,
+ 0x6B, 0x0E, 0xFF, 0x02, 0xF6, 0x17, 0xC5, 0xA4,
+ 0x2C, 0x5A, 0xA7, 0x60, 0x27, 0x0F, 0x26, 0x79,
+ 0xDA, 0x26, 0x77, 0xC5, 0xAE, 0xB9, 0x4F, 0x11,
+ 0x42, 0x27, 0x7F, 0x21, 0xC7, 0xF7, 0x9F, 0x3C,
+ 0x4F, 0x0C, 0xCE, 0x4E, 0xD8, 0xEE, 0x62, 0xB1
+ },
+ {
+ 0x95, 0x39, 0x1D, 0xA8, 0xFC, 0x7B, 0x91, 0x7A,
+ 0x20, 0x44, 0xB3, 0xD6, 0xF5, 0x37, 0x4E, 0x1C,
+ 0xA0, 0x72, 0xB4, 0x14, 0x54, 0xD5, 0x72, 0xC7,
+ 0x35, 0x6C, 0x05, 0xFD, 0x4B, 0xC1, 0xE0, 0xF4,
+ 0x0B, 0x8B, 0xB8, 0xB4, 0xA9, 0xF6, 0xBC, 0xE9,
+ 0xBE, 0x2C, 0x46, 0x23, 0xC3, 0x99, 0xB0, 0xDC,
+ 0xA0, 0xDA, 0xB0, 0x5C, 0xB7, 0x28, 0x1B, 0x71,
+ 0xA2, 0x1B, 0x0E, 0xBC, 0xD9, 0xE5, 0x56, 0x70
+ },
+ {
+ 0x04, 0xB9, 0xCD, 0x3D, 0x20, 0xD2, 0x21, 0xC0,
+ 0x9A, 0xC8, 0x69, 0x13, 0xD3, 0xDC, 0x63, 0x04,
+ 0x19, 0x89, 0xA9, 0xA1, 0xE6, 0x94, 0xF1, 0xE6,
+ 0x39, 0xA3, 0xBA, 0x7E, 0x45, 0x18, 0x40, 0xF7,
+ 0x50, 0xC2, 0xFC, 0x19, 0x1D, 0x56, 0xAD, 0x61,
+ 0xF2, 0xE7, 0x93, 0x6B, 0xC0, 0xAC, 0x8E, 0x09,
+ 0x4B, 0x60, 0xCA, 0xEE, 0xD8, 0x78, 0xC1, 0x87,
+ 0x99, 0x04, 0x54, 0x02, 0xD6, 0x1C, 0xEA, 0xF9
+ },
+ {
+ 0xEC, 0x0E, 0x0E, 0xF7, 0x07, 0xE4, 0xED, 0x6C,
+ 0x0C, 0x66, 0xF9, 0xE0, 0x89, 0xE4, 0x95, 0x4B,
+ 0x05, 0x80, 0x30, 0xD2, 0xDD, 0x86, 0x39, 0x8F,
+ 0xE8, 0x40, 0x59, 0x63, 0x1F, 0x9E, 0xE5, 0x91,
+ 0xD9, 0xD7, 0x73, 0x75, 0x35, 0x51, 0x49, 0x17,
+ 0x8C, 0x0C, 0xF8, 0xF8, 0xE7, 0xC4, 0x9E, 0xD2,
+ 0xA5, 0xE4, 0xF9, 0x54, 0x88, 0xA2, 0x24, 0x70,
+ 0x67, 0xC2, 0x08, 0x51, 0x0F, 0xAD, 0xC4, 0x4C
+ },
+ {
+ 0x9A, 0x37, 0xCC, 0xE2, 0x73, 0xB7, 0x9C, 0x09,
+ 0x91, 0x36, 0x77, 0x51, 0x0E, 0xAF, 0x76, 0x88,
+ 0xE8, 0x9B, 0x33, 0x14, 0xD3, 0x53, 0x2F, 0xD2,
+ 0x76, 0x4C, 0x39, 0xDE, 0x02, 0x2A, 0x29, 0x45,
+ 0xB5, 0x71, 0x0D, 0x13, 0x51, 0x7A, 0xF8, 0xDD,
+ 0xC0, 0x31, 0x66, 0x24, 0xE7, 0x3B, 0xEC, 0x1C,
+ 0xE6, 0x7D, 0xF1, 0x52, 0x28, 0x30, 0x20, 0x36,
+ 0xF3, 0x30, 0xAB, 0x0C, 0xB4, 0xD2, 0x18, 0xDD
+ },
+ {
+ 0x4C, 0xF9, 0xBB, 0x8F, 0xB3, 0xD4, 0xDE, 0x8B,
+ 0x38, 0xB2, 0xF2, 0x62, 0xD3, 0xC4, 0x0F, 0x46,
+ 0xDF, 0xE7, 0x47, 0xE8, 0xFC, 0x0A, 0x41, 0x4C,
+ 0x19, 0x3D, 0x9F, 0xCF, 0x75, 0x31, 0x06, 0xCE,
+ 0x47, 0xA1, 0x8F, 0x17, 0x2F, 0x12, 0xE8, 0xA2,
+ 0xF1, 0xC2, 0x67, 0x26, 0x54, 0x53, 0x58, 0xE5,
+ 0xEE, 0x28, 0xC9, 0xE2, 0x21, 0x3A, 0x87, 0x87,
+ 0xAA, 0xFB, 0xC5, 0x16, 0xD2, 0x34, 0x31, 0x52
+ },
+ {
+ 0x64, 0xE0, 0xC6, 0x3A, 0xF9, 0xC8, 0x08, 0xFD,
+ 0x89, 0x31, 0x37, 0x12, 0x98, 0x67, 0xFD, 0x91,
+ 0x93, 0x9D, 0x53, 0xF2, 0xAF, 0x04, 0xBE, 0x4F,
+ 0xA2, 0x68, 0x00, 0x61, 0x00, 0x06, 0x9B, 0x2D,
+ 0x69, 0xDA, 0xA5, 0xC5, 0xD8, 0xED, 0x7F, 0xDD,
+ 0xCB, 0x2A, 0x70, 0xEE, 0xEC, 0xDF, 0x2B, 0x10,
+ 0x5D, 0xD4, 0x6A, 0x1E, 0x3B, 0x73, 0x11, 0x72,
+ 0x8F, 0x63, 0x9A, 0xB4, 0x89, 0x32, 0x6B, 0xC9
+ },
+ {
+ 0x5E, 0x9C, 0x93, 0x15, 0x8D, 0x65, 0x9B, 0x2D,
+ 0xEF, 0x06, 0xB0, 0xC3, 0xC7, 0x56, 0x50, 0x45,
+ 0x54, 0x26, 0x62, 0xD6, 0xEE, 0xE8, 0xA9, 0x6A,
+ 0x89, 0xB7, 0x8A, 0xDE, 0x09, 0xFE, 0x8B, 0x3D,
+ 0xCC, 0x09, 0x6D, 0x4F, 0xE4, 0x88, 0x15, 0xD8,
+ 0x8D, 0x8F, 0x82, 0x62, 0x01, 0x56, 0x60, 0x2A,
+ 0xF5, 0x41, 0x95, 0x5E, 0x1F, 0x6C, 0xA3, 0x0D,
+ 0xCE, 0x14, 0xE2, 0x54, 0xC3, 0x26, 0xB8, 0x8F
+ },
+ {
+ 0x77, 0x75, 0xDF, 0xF8, 0x89, 0x45, 0x8D, 0xD1,
+ 0x1A, 0xEF, 0x41, 0x72, 0x76, 0x85, 0x3E, 0x21,
+ 0x33, 0x5E, 0xB8, 0x8E, 0x4D, 0xEC, 0x9C, 0xFB,
+ 0x4E, 0x9E, 0xDB, 0x49, 0x82, 0x00, 0x88, 0x55,
+ 0x1A, 0x2C, 0xA6, 0x03, 0x39, 0xF1, 0x20, 0x66,
+ 0x10, 0x11, 0x69, 0xF0, 0xDF, 0xE8, 0x4B, 0x09,
+ 0x8F, 0xDD, 0xB1, 0x48, 0xD9, 0xDA, 0x6B, 0x3D,
+ 0x61, 0x3D, 0xF2, 0x63, 0x88, 0x9A, 0xD6, 0x4B
+ },
+ {
+ 0xF0, 0xD2, 0x80, 0x5A, 0xFB, 0xB9, 0x1F, 0x74,
+ 0x39, 0x51, 0x35, 0x1A, 0x6D, 0x02, 0x4F, 0x93,
+ 0x53, 0xA2, 0x3C, 0x7C, 0xE1, 0xFC, 0x2B, 0x05,
+ 0x1B, 0x3A, 0x8B, 0x96, 0x8C, 0x23, 0x3F, 0x46,
+ 0xF5, 0x0F, 0x80, 0x6E, 0xCB, 0x15, 0x68, 0xFF,
+ 0xAA, 0x0B, 0x60, 0x66, 0x1E, 0x33, 0x4B, 0x21,
+ 0xDD, 0xE0, 0x4F, 0x8F, 0xA1, 0x55, 0xAC, 0x74,
+ 0x0E, 0xEB, 0x42, 0xE2, 0x0B, 0x60, 0xD7, 0x64
+ },
+ {
+ 0x86, 0xA2, 0xAF, 0x31, 0x6E, 0x7D, 0x77, 0x54,
+ 0x20, 0x1B, 0x94, 0x2E, 0x27, 0x53, 0x64, 0xAC,
+ 0x12, 0xEA, 0x89, 0x62, 0xAB, 0x5B, 0xD8, 0xD7,
+ 0xFB, 0x27, 0x6D, 0xC5, 0xFB, 0xFF, 0xC8, 0xF9,
+ 0xA2, 0x8C, 0xAE, 0x4E, 0x48, 0x67, 0xDF, 0x67,
+ 0x80, 0xD9, 0xB7, 0x25, 0x24, 0x16, 0x09, 0x27,
+ 0xC8, 0x55, 0xDA, 0x5B, 0x60, 0x78, 0xE0, 0xB5,
+ 0x54, 0xAA, 0x91, 0xE3, 0x1C, 0xB9, 0xCA, 0x1D
+ },
+ {
+ 0x10, 0xBD, 0xF0, 0xCA, 0xA0, 0x80, 0x27, 0x05,
+ 0xE7, 0x06, 0x36, 0x9B, 0xAF, 0x8A, 0x3F, 0x79,
+ 0xD7, 0x2C, 0x0A, 0x03, 0xA8, 0x06, 0x75, 0xA7,
+ 0xBB, 0xB0, 0x0B, 0xE3, 0xA4, 0x5E, 0x51, 0x64,
+ 0x24, 0xD1, 0xEE, 0x88, 0xEF, 0xB5, 0x6F, 0x6D,
+ 0x57, 0x77, 0x54, 0x5A, 0xE6, 0xE2, 0x77, 0x65,
+ 0xC3, 0xA8, 0xF5, 0xE4, 0x93, 0xFC, 0x30, 0x89,
+ 0x15, 0x63, 0x89, 0x33, 0xA1, 0xDF, 0xEE, 0x55
+ },
+ {
+ 0xB0, 0x17, 0x81, 0x09, 0x2B, 0x17, 0x48, 0x45,
+ 0x9E, 0x2E, 0x4E, 0xC1, 0x78, 0x69, 0x66, 0x27,
+ 0xBF, 0x4E, 0xBA, 0xFE, 0xBB, 0xA7, 0x74, 0xEC,
+ 0xF0, 0x18, 0xB7, 0x9A, 0x68, 0xAE, 0xB8, 0x49,
+ 0x17, 0xBF, 0x0B, 0x84, 0xBB, 0x79, 0xD1, 0x7B,
+ 0x74, 0x31, 0x51, 0x14, 0x4C, 0xD6, 0x6B, 0x7B,
+ 0x33, 0xA4, 0xB9, 0xE5, 0x2C, 0x76, 0xC4, 0xE1,
+ 0x12, 0x05, 0x0F, 0xF5, 0x38, 0x5B, 0x7F, 0x0B
+ },
+ {
+ 0xC6, 0xDB, 0xC6, 0x1D, 0xEC, 0x6E, 0xAE, 0xAC,
+ 0x81, 0xE3, 0xD5, 0xF7, 0x55, 0x20, 0x3C, 0x8E,
+ 0x22, 0x05, 0x51, 0x53, 0x4A, 0x0B, 0x2F, 0xD1,
+ 0x05, 0xA9, 0x18, 0x89, 0x94, 0x5A, 0x63, 0x85,
+ 0x50, 0x20, 0x4F, 0x44, 0x09, 0x3D, 0xD9, 0x98,
+ 0xC0, 0x76, 0x20, 0x5D, 0xFF, 0xAD, 0x70, 0x3A,
+ 0x0E, 0x5C, 0xD3, 0xC7, 0xF4, 0x38, 0xA7, 0xE6,
+ 0x34, 0xCD, 0x59, 0xFE, 0xDE, 0xDB, 0x53, 0x9E
+ },
+ {
+ 0xEB, 0xA5, 0x1A, 0xCF, 0xFB, 0x4C, 0xEA, 0x31,
+ 0xDB, 0x4B, 0x8D, 0x87, 0xE9, 0xBF, 0x7D, 0xD4,
+ 0x8F, 0xE9, 0x7B, 0x02, 0x53, 0xAE, 0x67, 0xAA,
+ 0x58, 0x0F, 0x9A, 0xC4, 0xA9, 0xD9, 0x41, 0xF2,
+ 0xBE, 0xA5, 0x18, 0xEE, 0x28, 0x68, 0x18, 0xCC,
+ 0x9F, 0x63, 0x3F, 0x2A, 0x3B, 0x9F, 0xB6, 0x8E,
+ 0x59, 0x4B, 0x48, 0xCD, 0xD6, 0xD5, 0x15, 0xBF,
+ 0x1D, 0x52, 0xBA, 0x6C, 0x85, 0xA2, 0x03, 0xA7
+ },
+ {
+ 0x86, 0x22, 0x1F, 0x3A, 0xDA, 0x52, 0x03, 0x7B,
+ 0x72, 0x22, 0x4F, 0x10, 0x5D, 0x79, 0x99, 0x23,
+ 0x1C, 0x5E, 0x55, 0x34, 0xD0, 0x3D, 0xA9, 0xD9,
+ 0xC0, 0xA1, 0x2A, 0xCB, 0x68, 0x46, 0x0C, 0xD3,
+ 0x75, 0xDA, 0xF8, 0xE2, 0x43, 0x86, 0x28, 0x6F,
+ 0x96, 0x68, 0xF7, 0x23, 0x26, 0xDB, 0xF9, 0x9B,
+ 0xA0, 0x94, 0x39, 0x24, 0x37, 0xD3, 0x98, 0xE9,
+ 0x5B, 0xB8, 0x16, 0x1D, 0x71, 0x7F, 0x89, 0x91
+ },
+ {
+ 0x55, 0x95, 0xE0, 0x5C, 0x13, 0xA7, 0xEC, 0x4D,
+ 0xC8, 0xF4, 0x1F, 0xB7, 0x0C, 0xB5, 0x0A, 0x71,
+ 0xBC, 0xE1, 0x7C, 0x02, 0x4F, 0xF6, 0xDE, 0x7A,
+ 0xF6, 0x18, 0xD0, 0xCC, 0x4E, 0x9C, 0x32, 0xD9,
+ 0x57, 0x0D, 0x6D, 0x3E, 0xA4, 0x5B, 0x86, 0x52,
+ 0x54, 0x91, 0x03, 0x0C, 0x0D, 0x8F, 0x2B, 0x18,
+ 0x36, 0xD5, 0x77, 0x8C, 0x1C, 0xE7, 0x35, 0xC1,
+ 0x77, 0x07, 0xDF, 0x36, 0x4D, 0x05, 0x43, 0x47
+ },
+ {
+ 0xCE, 0x0F, 0x4F, 0x6A, 0xCA, 0x89, 0x59, 0x0A,
+ 0x37, 0xFE, 0x03, 0x4D, 0xD7, 0x4D, 0xD5, 0xFA,
+ 0x65, 0xEB, 0x1C, 0xBD, 0x0A, 0x41, 0x50, 0x8A,
+ 0xAD, 0xDC, 0x09, 0x35, 0x1A, 0x3C, 0xEA, 0x6D,
+ 0x18, 0xCB, 0x21, 0x89, 0xC5, 0x4B, 0x70, 0x0C,
+ 0x00, 0x9F, 0x4C, 0xBF, 0x05, 0x21, 0xC7, 0xEA,
+ 0x01, 0xBE, 0x61, 0xC5, 0xAE, 0x09, 0xCB, 0x54,
+ 0xF2, 0x7B, 0xC1, 0xB4, 0x4D, 0x65, 0x8C, 0x82
+ },
+ {
+ 0x7E, 0xE8, 0x0B, 0x06, 0xA2, 0x15, 0xA3, 0xBC,
+ 0xA9, 0x70, 0xC7, 0x7C, 0xDA, 0x87, 0x61, 0x82,
+ 0x2B, 0xC1, 0x03, 0xD4, 0x4F, 0xA4, 0xB3, 0x3F,
+ 0x4D, 0x07, 0xDC, 0xB9, 0x97, 0xE3, 0x6D, 0x55,
+ 0x29, 0x8B, 0xCE, 0xAE, 0x12, 0x24, 0x1B, 0x3F,
+ 0xA0, 0x7F, 0xA6, 0x3B, 0xE5, 0x57, 0x60, 0x68,
+ 0xDA, 0x38, 0x7B, 0x8D, 0x58, 0x59, 0xAE, 0xAB,
+ 0x70, 0x13, 0x69, 0x84, 0x8B, 0x17, 0x6D, 0x42
+ },
+ {
+ 0x94, 0x0A, 0x84, 0xB6, 0xA8, 0x4D, 0x10, 0x9A,
+ 0xAB, 0x20, 0x8C, 0x02, 0x4C, 0x6C, 0xE9, 0x64,
+ 0x76, 0x76, 0xBA, 0x0A, 0xAA, 0x11, 0xF8, 0x6D,
+ 0xBB, 0x70, 0x18, 0xF9, 0xFD, 0x22, 0x20, 0xA6,
+ 0xD9, 0x01, 0xA9, 0x02, 0x7F, 0x9A, 0xBC, 0xF9,
+ 0x35, 0x37, 0x27, 0x27, 0xCB, 0xF0, 0x9E, 0xBD,
+ 0x61, 0xA2, 0xA2, 0xEE, 0xB8, 0x76, 0x53, 0xE8,
+ 0xEC, 0xAD, 0x1B, 0xAB, 0x85, 0xDC, 0x83, 0x27
+ },
+ {
+ 0x20, 0x20, 0xB7, 0x82, 0x64, 0xA8, 0x2D, 0x9F,
+ 0x41, 0x51, 0x14, 0x1A, 0xDB, 0xA8, 0xD4, 0x4B,
+ 0xF2, 0x0C, 0x5E, 0xC0, 0x62, 0xEE, 0xE9, 0xB5,
+ 0x95, 0xA1, 0x1F, 0x9E, 0x84, 0x90, 0x1B, 0xF1,
+ 0x48, 0xF2, 0x98, 0xE0, 0xC9, 0xF8, 0x77, 0x7D,
+ 0xCD, 0xBC, 0x7C, 0xC4, 0x67, 0x0A, 0xAC, 0x35,
+ 0x6C, 0xC2, 0xAD, 0x8C, 0xCB, 0x16, 0x29, 0xF1,
+ 0x6F, 0x6A, 0x76, 0xBC, 0xEF, 0xBE, 0xE7, 0x60
+ },
+ {
+ 0xD1, 0xB8, 0x97, 0xB0, 0xE0, 0x75, 0xBA, 0x68,
+ 0xAB, 0x57, 0x2A, 0xDF, 0x9D, 0x9C, 0x43, 0x66,
+ 0x63, 0xE4, 0x3E, 0xB3, 0xD8, 0xE6, 0x2D, 0x92,
+ 0xFC, 0x49, 0xC9, 0xBE, 0x21, 0x4E, 0x6F, 0x27,
+ 0x87, 0x3F, 0xE2, 0x15, 0xA6, 0x51, 0x70, 0xE6,
+ 0xBE, 0xA9, 0x02, 0x40, 0x8A, 0x25, 0xB4, 0x95,
+ 0x06, 0xF4, 0x7B, 0xAB, 0xD0, 0x7C, 0xEC, 0xF7,
+ 0x11, 0x3E, 0xC1, 0x0C, 0x5D, 0xD3, 0x12, 0x52
+ },
+ {
+ 0xB1, 0x4D, 0x0C, 0x62, 0xAB, 0xFA, 0x46, 0x9A,
+ 0x35, 0x71, 0x77, 0xE5, 0x94, 0xC1, 0x0C, 0x19,
+ 0x42, 0x43, 0xED, 0x20, 0x25, 0xAB, 0x8A, 0xA5,
+ 0xAD, 0x2F, 0xA4, 0x1A, 0xD3, 0x18, 0xE0, 0xFF,
+ 0x48, 0xCD, 0x5E, 0x60, 0xBE, 0xC0, 0x7B, 0x13,
+ 0x63, 0x4A, 0x71, 0x1D, 0x23, 0x26, 0xE4, 0x88,
+ 0xA9, 0x85, 0xF3, 0x1E, 0x31, 0x15, 0x33, 0x99,
+ 0xE7, 0x30, 0x88, 0xEF, 0xC8, 0x6A, 0x5C, 0x55
+ },
+ {
+ 0x41, 0x69, 0xC5, 0xCC, 0x80, 0x8D, 0x26, 0x97,
+ 0xDC, 0x2A, 0x82, 0x43, 0x0D, 0xC2, 0x3E, 0x3C,
+ 0xD3, 0x56, 0xDC, 0x70, 0xA9, 0x45, 0x66, 0x81,
+ 0x05, 0x02, 0xB8, 0xD6, 0x55, 0xB3, 0x9A, 0xBF,
+ 0x9E, 0x7F, 0x90, 0x2F, 0xE7, 0x17, 0xE0, 0x38,
+ 0x92, 0x19, 0x85, 0x9E, 0x19, 0x45, 0xDF, 0x1A,
+ 0xF6, 0xAD, 0xA4, 0x2E, 0x4C, 0xCD, 0xA5, 0x5A,
+ 0x19, 0x7B, 0x71, 0x00, 0xA3, 0x0C, 0x30, 0xA1
+ },
+ {
+ 0x25, 0x8A, 0x4E, 0xDB, 0x11, 0x3D, 0x66, 0xC8,
+ 0x39, 0xC8, 0xB1, 0xC9, 0x1F, 0x15, 0xF3, 0x5A,
+ 0xDE, 0x60, 0x9F, 0x11, 0xCD, 0x7F, 0x86, 0x81,
+ 0xA4, 0x04, 0x5B, 0x9F, 0xEF, 0x7B, 0x0B, 0x24,
+ 0xC8, 0x2C, 0xDA, 0x06, 0xA5, 0xF2, 0x06, 0x7B,
+ 0x36, 0x88, 0x25, 0xE3, 0x91, 0x4E, 0x53, 0xD6,
+ 0x94, 0x8E, 0xDE, 0x92, 0xEF, 0xD6, 0xE8, 0x38,
+ 0x7F, 0xA2, 0xE5, 0x37, 0x23, 0x9B, 0x5B, 0xEE
+ },
+ {
+ 0x79, 0xD2, 0xD8, 0x69, 0x6D, 0x30, 0xF3, 0x0F,
+ 0xB3, 0x46, 0x57, 0x76, 0x11, 0x71, 0xA1, 0x1E,
+ 0x6C, 0x3F, 0x1E, 0x64, 0xCB, 0xE7, 0xBE, 0xBE,
+ 0xE1, 0x59, 0xCB, 0x95, 0xBF, 0xAF, 0x81, 0x2B,
+ 0x4F, 0x41, 0x1E, 0x2F, 0x26, 0xD9, 0xC4, 0x21,
+ 0xDC, 0x2C, 0x28, 0x4A, 0x33, 0x42, 0xD8, 0x23,
+ 0xEC, 0x29, 0x38, 0x49, 0xE4, 0x2D, 0x1E, 0x46,
+ 0xB0, 0xA4, 0xAC, 0x1E, 0x3C, 0x86, 0xAB, 0xAA
+ },
+ {
+ 0x8B, 0x94, 0x36, 0x01, 0x0D, 0xC5, 0xDE, 0xE9,
+ 0x92, 0xAE, 0x38, 0xAE, 0xA9, 0x7F, 0x2C, 0xD6,
+ 0x3B, 0x94, 0x6D, 0x94, 0xFE, 0xDD, 0x2E, 0xC9,
+ 0x67, 0x1D, 0xCD, 0xE3, 0xBD, 0x4C, 0xE9, 0x56,
+ 0x4D, 0x55, 0x5C, 0x66, 0xC1, 0x5B, 0xB2, 0xB9,
+ 0x00, 0xDF, 0x72, 0xED, 0xB6, 0xB8, 0x91, 0xEB,
+ 0xCA, 0xDF, 0xEF, 0xF6, 0x3C, 0x9E, 0xA4, 0x03,
+ 0x6A, 0x99, 0x8B, 0xE7, 0x97, 0x39, 0x81, 0xE7
+ },
+ {
+ 0xC8, 0xF6, 0x8E, 0x69, 0x6E, 0xD2, 0x82, 0x42,
+ 0xBF, 0x99, 0x7F, 0x5B, 0x3B, 0x34, 0x95, 0x95,
+ 0x08, 0xE4, 0x2D, 0x61, 0x38, 0x10, 0xF1, 0xE2,
+ 0xA4, 0x35, 0xC9, 0x6E, 0xD2, 0xFF, 0x56, 0x0C,
+ 0x70, 0x22, 0xF3, 0x61, 0xA9, 0x23, 0x4B, 0x98,
+ 0x37, 0xFE, 0xEE, 0x90, 0xBF, 0x47, 0x92, 0x2E,
+ 0xE0, 0xFD, 0x5F, 0x8D, 0xDF, 0x82, 0x37, 0x18,
+ 0xD8, 0x6D, 0x1E, 0x16, 0xC6, 0x09, 0x00, 0x71
+ },
+ {
+ 0xB0, 0x2D, 0x3E, 0xEE, 0x48, 0x60, 0xD5, 0x86,
+ 0x8B, 0x2C, 0x39, 0xCE, 0x39, 0xBF, 0xE8, 0x10,
+ 0x11, 0x29, 0x05, 0x64, 0xDD, 0x67, 0x8C, 0x85,
+ 0xE8, 0x78, 0x3F, 0x29, 0x30, 0x2D, 0xFC, 0x13,
+ 0x99, 0xBA, 0x95, 0xB6, 0xB5, 0x3C, 0xD9, 0xEB,
+ 0xBF, 0x40, 0x0C, 0xCA, 0x1D, 0xB0, 0xAB, 0x67,
+ 0xE1, 0x9A, 0x32, 0x5F, 0x2D, 0x11, 0x58, 0x12,
+ 0xD2, 0x5D, 0x00, 0x97, 0x8A, 0xD1, 0xBC, 0xA4
+ },
+ {
+ 0x76, 0x93, 0xEA, 0x73, 0xAF, 0x3A, 0xC4, 0xDA,
+ 0xD2, 0x1C, 0xA0, 0xD8, 0xDA, 0x85, 0xB3, 0x11,
+ 0x8A, 0x7D, 0x1C, 0x60, 0x24, 0xCF, 0xAF, 0x55,
+ 0x76, 0x99, 0x86, 0x82, 0x17, 0xBC, 0x0C, 0x2F,
+ 0x44, 0xA1, 0x99, 0xBC, 0x6C, 0x0E, 0xDD, 0x51,
+ 0x97, 0x98, 0xBA, 0x05, 0xBD, 0x5B, 0x1B, 0x44,
+ 0x84, 0x34, 0x6A, 0x47, 0xC2, 0xCA, 0xDF, 0x6B,
+ 0xF3, 0x0B, 0x78, 0x5C, 0xC8, 0x8B, 0x2B, 0xAF
+ },
+ {
+ 0xA0, 0xE5, 0xC1, 0xC0, 0x03, 0x1C, 0x02, 0xE4,
+ 0x8B, 0x7F, 0x09, 0xA5, 0xE8, 0x96, 0xEE, 0x9A,
+ 0xEF, 0x2F, 0x17, 0xFC, 0x9E, 0x18, 0xE9, 0x97,
+ 0xD7, 0xF6, 0xCA, 0xC7, 0xAE, 0x31, 0x64, 0x22,
+ 0xC2, 0xB1, 0xE7, 0x79, 0x84, 0xE5, 0xF3, 0xA7,
+ 0x3C, 0xB4, 0x5D, 0xEE, 0xD5, 0xD3, 0xF8, 0x46,
+ 0x00, 0x10, 0x5E, 0x6E, 0xE3, 0x8F, 0x2D, 0x09,
+ 0x0C, 0x7D, 0x04, 0x42, 0xEA, 0x34, 0xC4, 0x6D
+ },
+ {
+ 0x41, 0xDA, 0xA6, 0xAD, 0xCF, 0xDB, 0x69, 0xF1,
+ 0x44, 0x0C, 0x37, 0xB5, 0x96, 0x44, 0x01, 0x65,
+ 0xC1, 0x5A, 0xDA, 0x59, 0x68, 0x13, 0xE2, 0xE2,
+ 0x2F, 0x06, 0x0F, 0xCD, 0x55, 0x1F, 0x24, 0xDE,
+ 0xE8, 0xE0, 0x4B, 0xA6, 0x89, 0x03, 0x87, 0x88,
+ 0x6C, 0xEE, 0xC4, 0xA7, 0xA0, 0xD7, 0xFC, 0x6B,
+ 0x44, 0x50, 0x63, 0x92, 0xEC, 0x38, 0x22, 0xC0,
+ 0xD8, 0xC1, 0xAC, 0xFC, 0x7D, 0x5A, 0xEB, 0xE8
+ },
+ {
+ 0x14, 0xD4, 0xD4, 0x0D, 0x59, 0x84, 0xD8, 0x4C,
+ 0x5C, 0xF7, 0x52, 0x3B, 0x77, 0x98, 0xB2, 0x54,
+ 0xE2, 0x75, 0xA3, 0xA8, 0xCC, 0x0A, 0x1B, 0xD0,
+ 0x6E, 0xBC, 0x0B, 0xEE, 0x72, 0x68, 0x56, 0xAC,
+ 0xC3, 0xCB, 0xF5, 0x16, 0xFF, 0x66, 0x7C, 0xDA,
+ 0x20, 0x58, 0xAD, 0x5C, 0x34, 0x12, 0x25, 0x44,
+ 0x60, 0xA8, 0x2C, 0x92, 0x18, 0x70, 0x41, 0x36,
+ 0x3C, 0xC7, 0x7A, 0x4D, 0xC2, 0x15, 0xE4, 0x87
+ },
+ {
+ 0xD0, 0xE7, 0xA1, 0xE2, 0xB9, 0xA4, 0x47, 0xFE,
+ 0xE8, 0x3E, 0x22, 0x77, 0xE9, 0xFF, 0x80, 0x10,
+ 0xC2, 0xF3, 0x75, 0xAE, 0x12, 0xFA, 0x7A, 0xAA,
+ 0x8C, 0xA5, 0xA6, 0x31, 0x78, 0x68, 0xA2, 0x6A,
+ 0x36, 0x7A, 0x0B, 0x69, 0xFB, 0xC1, 0xCF, 0x32,
+ 0xA5, 0x5D, 0x34, 0xEB, 0x37, 0x06, 0x63, 0x01,
+ 0x6F, 0x3D, 0x21, 0x10, 0x23, 0x0E, 0xBA, 0x75,
+ 0x40, 0x28, 0xA5, 0x6F, 0x54, 0xAC, 0xF5, 0x7C
+ },
+ {
+ 0xE7, 0x71, 0xAA, 0x8D, 0xB5, 0xA3, 0xE0, 0x43,
+ 0xE8, 0x17, 0x8F, 0x39, 0xA0, 0x85, 0x7B, 0xA0,
+ 0x4A, 0x3F, 0x18, 0xE4, 0xAA, 0x05, 0x74, 0x3C,
+ 0xF8, 0xD2, 0x22, 0xB0, 0xB0, 0x95, 0x82, 0x53,
+ 0x50, 0xBA, 0x42, 0x2F, 0x63, 0x38, 0x2A, 0x23,
+ 0xD9, 0x2E, 0x41, 0x49, 0x07, 0x4E, 0x81, 0x6A,
+ 0x36, 0xC1, 0xCD, 0x28, 0x28, 0x4D, 0x14, 0x62,
+ 0x67, 0x94, 0x0B, 0x31, 0xF8, 0x81, 0x8E, 0xA2
+ },
+ {
+ 0xFE, 0xB4, 0xFD, 0x6F, 0x9E, 0x87, 0xA5, 0x6B,
+ 0xEF, 0x39, 0x8B, 0x32, 0x84, 0xD2, 0xBD, 0xA5,
+ 0xB5, 0xB0, 0xE1, 0x66, 0x58, 0x3A, 0x66, 0xB6,
+ 0x1E, 0x53, 0x84, 0x57, 0xFF, 0x05, 0x84, 0x87,
+ 0x2C, 0x21, 0xA3, 0x29, 0x62, 0xB9, 0x92, 0x8F,
+ 0xFA, 0xB5, 0x8D, 0xE4, 0xAF, 0x2E, 0xDD, 0x4E,
+ 0x15, 0xD8, 0xB3, 0x55, 0x70, 0x52, 0x32, 0x07,
+ 0xFF, 0x4E, 0x2A, 0x5A, 0xA7, 0x75, 0x4C, 0xAA
+ },
+ {
+ 0x46, 0x2F, 0x17, 0xBF, 0x00, 0x5F, 0xB1, 0xC1,
+ 0xB9, 0xE6, 0x71, 0x77, 0x9F, 0x66, 0x52, 0x09,
+ 0xEC, 0x28, 0x73, 0xE3, 0xE4, 0x11, 0xF9, 0x8D,
+ 0xAB, 0xF2, 0x40, 0xA1, 0xD5, 0xEC, 0x3F, 0x95,
+ 0xCE, 0x67, 0x96, 0xB6, 0xFC, 0x23, 0xFE, 0x17,
+ 0x19, 0x03, 0xB5, 0x02, 0x02, 0x34, 0x67, 0xDE,
+ 0xC7, 0x27, 0x3F, 0xF7, 0x48, 0x79, 0xB9, 0x29,
+ 0x67, 0xA2, 0xA4, 0x3A, 0x5A, 0x18, 0x3D, 0x33
+ },
+ {
+ 0xD3, 0x33, 0x81, 0x93, 0xB6, 0x45, 0x53, 0xDB,
+ 0xD3, 0x8D, 0x14, 0x4B, 0xEA, 0x71, 0xC5, 0x91,
+ 0x5B, 0xB1, 0x10, 0xE2, 0xD8, 0x81, 0x80, 0xDB,
+ 0xC5, 0xDB, 0x36, 0x4F, 0xD6, 0x17, 0x1D, 0xF3,
+ 0x17, 0xFC, 0x72, 0x68, 0x83, 0x1B, 0x5A, 0xEF,
+ 0x75, 0xE4, 0x34, 0x2B, 0x2F, 0xAD, 0x87, 0x97,
+ 0xBA, 0x39, 0xED, 0xDC, 0xEF, 0x80, 0xE6, 0xEC,
+ 0x08, 0x15, 0x93, 0x50, 0xB1, 0xAD, 0x69, 0x6D
+ },
+ {
+ 0xE1, 0x59, 0x0D, 0x58, 0x5A, 0x3D, 0x39, 0xF7,
+ 0xCB, 0x59, 0x9A, 0xBD, 0x47, 0x90, 0x70, 0x96,
+ 0x64, 0x09, 0xA6, 0x84, 0x6D, 0x43, 0x77, 0xAC,
+ 0xF4, 0x47, 0x1D, 0x06, 0x5D, 0x5D, 0xB9, 0x41,
+ 0x29, 0xCC, 0x9B, 0xE9, 0x25, 0x73, 0xB0, 0x5E,
+ 0xD2, 0x26, 0xBE, 0x1E, 0x9B, 0x7C, 0xB0, 0xCA,
+ 0xBE, 0x87, 0x91, 0x85, 0x89, 0xF8, 0x0D, 0xAD,
+ 0xD4, 0xEF, 0x5E, 0xF2, 0x5A, 0x93, 0xD2, 0x8E
+ },
+ {
+ 0xF8, 0xF3, 0x72, 0x6A, 0xC5, 0xA2, 0x6C, 0xC8,
+ 0x01, 0x32, 0x49, 0x3A, 0x6F, 0xED, 0xCB, 0x0E,
+ 0x60, 0x76, 0x0C, 0x09, 0xCF, 0xC8, 0x4C, 0xAD,
+ 0x17, 0x81, 0x75, 0x98, 0x68, 0x19, 0x66, 0x5E,
+ 0x76, 0x84, 0x2D, 0x7B, 0x9F, 0xED, 0xF7, 0x6D,
+ 0xDD, 0xEB, 0xF5, 0xD3, 0xF5, 0x6F, 0xAA, 0xAD,
+ 0x44, 0x77, 0x58, 0x7A, 0xF2, 0x16, 0x06, 0xD3,
+ 0x96, 0xAE, 0x57, 0x0D, 0x8E, 0x71, 0x9A, 0xF2
+ },
+ {
+ 0x30, 0x18, 0x60, 0x55, 0xC0, 0x79, 0x49, 0x94,
+ 0x81, 0x83, 0xC8, 0x50, 0xE9, 0xA7, 0x56, 0xCC,
+ 0x09, 0x93, 0x7E, 0x24, 0x7D, 0x9D, 0x92, 0x8E,
+ 0x86, 0x9E, 0x20, 0xBA, 0xFC, 0x3C, 0xD9, 0x72,
+ 0x17, 0x19, 0xD3, 0x4E, 0x04, 0xA0, 0x89, 0x9B,
+ 0x92, 0xC7, 0x36, 0x08, 0x45, 0x50, 0x18, 0x68,
+ 0x86, 0xEF, 0xBA, 0x2E, 0x79, 0x0D, 0x8B, 0xE6,
+ 0xEB, 0xF0, 0x40, 0xB2, 0x09, 0xC4, 0x39, 0xA4
+ },
+ {
+ 0xF3, 0xC4, 0x27, 0x6C, 0xB8, 0x63, 0x63, 0x77,
+ 0x12, 0xC2, 0x41, 0xC4, 0x44, 0xC5, 0xCC, 0x1E,
+ 0x35, 0x54, 0xE0, 0xFD, 0xDB, 0x17, 0x4D, 0x03,
+ 0x58, 0x19, 0xDD, 0x83, 0xEB, 0x70, 0x0B, 0x4C,
+ 0xE8, 0x8D, 0xF3, 0xAB, 0x38, 0x41, 0xBA, 0x02,
+ 0x08, 0x5E, 0x1A, 0x99, 0xB4, 0xE1, 0x73, 0x10,
+ 0xC5, 0x34, 0x10, 0x75, 0xC0, 0x45, 0x8B, 0xA3,
+ 0x76, 0xC9, 0x5A, 0x68, 0x18, 0xFB, 0xB3, 0xE2
+ },
+ {
+ 0x0A, 0xA0, 0x07, 0xC4, 0xDD, 0x9D, 0x58, 0x32,
+ 0x39, 0x30, 0x40, 0xA1, 0x58, 0x3C, 0x93, 0x0B,
+ 0xCA, 0x7D, 0xC5, 0xE7, 0x7E, 0xA5, 0x3A, 0xDD,
+ 0x7E, 0x2B, 0x3F, 0x7C, 0x8E, 0x23, 0x13, 0x68,
+ 0x04, 0x35, 0x20, 0xD4, 0xA3, 0xEF, 0x53, 0xC9,
+ 0x69, 0xB6, 0xBB, 0xFD, 0x02, 0x59, 0x46, 0xF6,
+ 0x32, 0xBD, 0x7F, 0x76, 0x5D, 0x53, 0xC2, 0x10,
+ 0x03, 0xB8, 0xF9, 0x83, 0xF7, 0x5E, 0x2A, 0x6A
+ },
+ {
+ 0x08, 0xE9, 0x46, 0x47, 0x20, 0x53, 0x3B, 0x23,
+ 0xA0, 0x4E, 0xC2, 0x4F, 0x7A, 0xE8, 0xC1, 0x03,
+ 0x14, 0x5F, 0x76, 0x53, 0x87, 0xD7, 0x38, 0x77,
+ 0x7D, 0x3D, 0x34, 0x34, 0x77, 0xFD, 0x1C, 0x58,
+ 0xDB, 0x05, 0x21, 0x42, 0xCA, 0xB7, 0x54, 0xEA,
+ 0x67, 0x43, 0x78, 0xE1, 0x87, 0x66, 0xC5, 0x35,
+ 0x42, 0xF7, 0x19, 0x70, 0x17, 0x1C, 0xC4, 0xF8,
+ 0x16, 0x94, 0x24, 0x6B, 0x71, 0x7D, 0x75, 0x64
+ },
+ {
+ 0xD3, 0x7F, 0xF7, 0xAD, 0x29, 0x79, 0x93, 0xE7,
+ 0xEC, 0x21, 0xE0, 0xF1, 0xB4, 0xB5, 0xAE, 0x71,
+ 0x9C, 0xDC, 0x83, 0xC5, 0xDB, 0x68, 0x75, 0x27,
+ 0xF2, 0x75, 0x16, 0xCB, 0xFF, 0xA8, 0x22, 0x88,
+ 0x8A, 0x68, 0x10, 0xEE, 0x5C, 0x1C, 0xA7, 0xBF,
+ 0xE3, 0x32, 0x11, 0x19, 0xBE, 0x1A, 0xB7, 0xBF,
+ 0xA0, 0xA5, 0x02, 0x67, 0x1C, 0x83, 0x29, 0x49,
+ 0x4D, 0xF7, 0xAD, 0x6F, 0x52, 0x2D, 0x44, 0x0F
+ },
+ {
+ 0xDD, 0x90, 0x42, 0xF6, 0xE4, 0x64, 0xDC, 0xF8,
+ 0x6B, 0x12, 0x62, 0xF6, 0xAC, 0xCF, 0xAF, 0xBD,
+ 0x8C, 0xFD, 0x90, 0x2E, 0xD3, 0xED, 0x89, 0xAB,
+ 0xF7, 0x8F, 0xFA, 0x48, 0x2D, 0xBD, 0xEE, 0xB6,
+ 0x96, 0x98, 0x42, 0x39, 0x4C, 0x9A, 0x11, 0x68,
+ 0xAE, 0x3D, 0x48, 0x1A, 0x01, 0x78, 0x42, 0xF6,
+ 0x60, 0x00, 0x2D, 0x42, 0x44, 0x7C, 0x6B, 0x22,
+ 0xF7, 0xB7, 0x2F, 0x21, 0xAA, 0xE0, 0x21, 0xC9
+ },
+ {
+ 0xBD, 0x96, 0x5B, 0xF3, 0x1E, 0x87, 0xD7, 0x03,
+ 0x27, 0x53, 0x6F, 0x2A, 0x34, 0x1C, 0xEB, 0xC4,
+ 0x76, 0x8E, 0xCA, 0x27, 0x5F, 0xA0, 0x5E, 0xF9,
+ 0x8F, 0x7F, 0x1B, 0x71, 0xA0, 0x35, 0x12, 0x98,
+ 0xDE, 0x00, 0x6F, 0xBA, 0x73, 0xFE, 0x67, 0x33,
+ 0xED, 0x01, 0xD7, 0x58, 0x01, 0xB4, 0xA9, 0x28,
+ 0xE5, 0x42, 0x31, 0xB3, 0x8E, 0x38, 0xC5, 0x62,
+ 0xB2, 0xE3, 0x3E, 0xA1, 0x28, 0x49, 0x92, 0xFA
+ },
+ {
+ 0x65, 0x67, 0x6D, 0x80, 0x06, 0x17, 0x97, 0x2F,
+ 0xBD, 0x87, 0xE4, 0xB9, 0x51, 0x4E, 0x1C, 0x67,
+ 0x40, 0x2B, 0x7A, 0x33, 0x10, 0x96, 0xD3, 0xBF,
+ 0xAC, 0x22, 0xF1, 0xAB, 0xB9, 0x53, 0x74, 0xAB,
+ 0xC9, 0x42, 0xF1, 0x6E, 0x9A, 0xB0, 0xEA, 0xD3,
+ 0x3B, 0x87, 0xC9, 0x19, 0x68, 0xA6, 0xE5, 0x09,
+ 0xE1, 0x19, 0xFF, 0x07, 0x78, 0x7B, 0x3E, 0xF4,
+ 0x83, 0xE1, 0xDC, 0xDC, 0xCF, 0x6E, 0x30, 0x22
+ },
+ {
+ 0x93, 0x9F, 0xA1, 0x89, 0x69, 0x9C, 0x5D, 0x2C,
+ 0x81, 0xDD, 0xD1, 0xFF, 0xC1, 0xFA, 0x20, 0x7C,
+ 0x97, 0x0B, 0x6A, 0x36, 0x85, 0xBB, 0x29, 0xCE,
+ 0x1D, 0x3E, 0x99, 0xD4, 0x2F, 0x2F, 0x74, 0x42,
+ 0xDA, 0x53, 0xE9, 0x5A, 0x72, 0x90, 0x73, 0x14,
+ 0xF4, 0x58, 0x83, 0x99, 0xA3, 0xFF, 0x5B, 0x0A,
+ 0x92, 0xBE, 0xB3, 0xF6, 0xBE, 0x26, 0x94, 0xF9,
+ 0xF8, 0x6E, 0xCF, 0x29, 0x52, 0xD5, 0xB4, 0x1C
+ },
+ {
+ 0xC5, 0x16, 0x54, 0x17, 0x01, 0x86, 0x3F, 0x91,
+ 0x00, 0x5F, 0x31, 0x41, 0x08, 0xCE, 0xEC, 0xE3,
+ 0xC6, 0x43, 0xE0, 0x4F, 0xC8, 0xC4, 0x2F, 0xD2,
+ 0xFF, 0x55, 0x62, 0x20, 0xE6, 0x16, 0xAA, 0xA6,
+ 0xA4, 0x8A, 0xEB, 0x97, 0xA8, 0x4B, 0xAD, 0x74,
+ 0x78, 0x2E, 0x8D, 0xFF, 0x96, 0xA1, 0xA2, 0xFA,
+ 0x94, 0x93, 0x39, 0xD7, 0x22, 0xED, 0xCA, 0xA3,
+ 0x2B, 0x57, 0x06, 0x70, 0x41, 0xDF, 0x88, 0xCC
+ },
+ {
+ 0x98, 0x7F, 0xD6, 0xE0, 0xD6, 0x85, 0x7C, 0x55,
+ 0x3E, 0xAE, 0xBB, 0x3D, 0x34, 0x97, 0x0A, 0x2C,
+ 0x2F, 0x6E, 0x89, 0xA3, 0x54, 0x8F, 0x49, 0x25,
+ 0x21, 0x72, 0x2B, 0x80, 0xA1, 0xC2, 0x1A, 0x15,
+ 0x38, 0x92, 0x34, 0x6D, 0x2C, 0xBA, 0x64, 0x44,
+ 0x21, 0x2D, 0x56, 0xDA, 0x9A, 0x26, 0xE3, 0x24,
+ 0xDC, 0xCB, 0xC0, 0xDC, 0xDE, 0x85, 0xD4, 0xD2,
+ 0xEE, 0x43, 0x99, 0xEE, 0xC5, 0xA6, 0x4E, 0x8F
+ },
+ {
+ 0xAE, 0x56, 0xDE, 0xB1, 0xC2, 0x32, 0x8D, 0x9C,
+ 0x40, 0x17, 0x70, 0x6B, 0xCE, 0x6E, 0x99, 0xD4,
+ 0x13, 0x49, 0x05, 0x3B, 0xA9, 0xD3, 0x36, 0xD6,
+ 0x77, 0xC4, 0xC2, 0x7D, 0x9F, 0xD5, 0x0A, 0xE6,
+ 0xAE, 0xE1, 0x7E, 0x85, 0x31, 0x54, 0xE1, 0xF4,
+ 0xFE, 0x76, 0x72, 0x34, 0x6D, 0xA2, 0xEA, 0xA3,
+ 0x1E, 0xEA, 0x53, 0xFC, 0xF2, 0x4A, 0x22, 0x80,
+ 0x4F, 0x11, 0xD0, 0x3D, 0xA6, 0xAB, 0xFC, 0x2B
+ },
+ {
+ 0x49, 0xD6, 0xA6, 0x08, 0xC9, 0xBD, 0xE4, 0x49,
+ 0x18, 0x70, 0x49, 0x85, 0x72, 0xAC, 0x31, 0xAA,
+ 0xC3, 0xFA, 0x40, 0x93, 0x8B, 0x38, 0xA7, 0x81,
+ 0x8F, 0x72, 0x38, 0x3E, 0xB0, 0x40, 0xAD, 0x39,
+ 0x53, 0x2B, 0xC0, 0x65, 0x71, 0xE1, 0x3D, 0x76,
+ 0x7E, 0x69, 0x45, 0xAB, 0x77, 0xC0, 0xBD, 0xC3,
+ 0xB0, 0x28, 0x42, 0x53, 0x34, 0x3F, 0x9F, 0x6C,
+ 0x12, 0x44, 0xEB, 0xF2, 0xFF, 0x0D, 0xF8, 0x66
+ },
+ {
+ 0xDA, 0x58, 0x2A, 0xD8, 0xC5, 0x37, 0x0B, 0x44,
+ 0x69, 0xAF, 0x86, 0x2A, 0xA6, 0x46, 0x7A, 0x22,
+ 0x93, 0xB2, 0xB2, 0x8B, 0xD8, 0x0A, 0xE0, 0xE9,
+ 0x1F, 0x42, 0x5A, 0xD3, 0xD4, 0x72, 0x49, 0xFD,
+ 0xF9, 0x88, 0x25, 0xCC, 0x86, 0xF1, 0x40, 0x28,
+ 0xC3, 0x30, 0x8C, 0x98, 0x04, 0xC7, 0x8B, 0xFE,
+ 0xEE, 0xEE, 0x46, 0x14, 0x44, 0xCE, 0x24, 0x36,
+ 0x87, 0xE1, 0xA5, 0x05, 0x22, 0x45, 0x6A, 0x1D
+ },
+ {
+ 0xD5, 0x26, 0x6A, 0xA3, 0x33, 0x11, 0x94, 0xAE,
+ 0xF8, 0x52, 0xEE, 0xD8, 0x6D, 0x7B, 0x5B, 0x26,
+ 0x33, 0xA0, 0xAF, 0x1C, 0x73, 0x59, 0x06, 0xF2,
+ 0xE1, 0x32, 0x79, 0xF1, 0x49, 0x31, 0xA9, 0xFC,
+ 0x3B, 0x0E, 0xAC, 0x5C, 0xE9, 0x24, 0x52, 0x73,
+ 0xBD, 0x1A, 0xA9, 0x29, 0x05, 0xAB, 0xE1, 0x62,
+ 0x78, 0xEF, 0x7E, 0xFD, 0x47, 0x69, 0x47, 0x89,
+ 0xA7, 0x28, 0x3B, 0x77, 0xDA, 0x3C, 0x70, 0xF8
+ },
+ {
+ 0x29, 0x62, 0x73, 0x4C, 0x28, 0x25, 0x21, 0x86,
+ 0xA9, 0xA1, 0x11, 0x1C, 0x73, 0x2A, 0xD4, 0xDE,
+ 0x45, 0x06, 0xD4, 0xB4, 0x48, 0x09, 0x16, 0x30,
+ 0x3E, 0xB7, 0x99, 0x1D, 0x65, 0x9C, 0xCD, 0xA0,
+ 0x7A, 0x99, 0x11, 0x91, 0x4B, 0xC7, 0x5C, 0x41,
+ 0x8A, 0xB7, 0xA4, 0x54, 0x17, 0x57, 0xAD, 0x05,
+ 0x47, 0x96, 0xE2, 0x67, 0x97, 0xFE, 0xAF, 0x36,
+ 0xE9, 0xF6, 0xAD, 0x43, 0xF1, 0x4B, 0x35, 0xA4
+ },
+ {
+ 0xE8, 0xB7, 0x9E, 0xC5, 0xD0, 0x6E, 0x11, 0x1B,
+ 0xDF, 0xAF, 0xD7, 0x1E, 0x9F, 0x57, 0x60, 0xF0,
+ 0x0A, 0xC8, 0xAC, 0x5D, 0x8B, 0xF7, 0x68, 0xF9,
+ 0xFF, 0x6F, 0x08, 0xB8, 0xF0, 0x26, 0x09, 0x6B,
+ 0x1C, 0xC3, 0xA4, 0xC9, 0x73, 0x33, 0x30, 0x19,
+ 0xF1, 0xE3, 0x55, 0x3E, 0x77, 0xDA, 0x3F, 0x98,
+ 0xCB, 0x9F, 0x54, 0x2E, 0x0A, 0x90, 0xE5, 0xF8,
+ 0xA9, 0x40, 0xCC, 0x58, 0xE5, 0x98, 0x44, 0xB3
+ },
+ {
+ 0xDF, 0xB3, 0x20, 0xC4, 0x4F, 0x9D, 0x41, 0xD1,
+ 0xEF, 0xDC, 0xC0, 0x15, 0xF0, 0x8D, 0xD5, 0x53,
+ 0x9E, 0x52, 0x6E, 0x39, 0xC8, 0x7D, 0x50, 0x9A,
+ 0xE6, 0x81, 0x2A, 0x96, 0x9E, 0x54, 0x31, 0xBF,
+ 0x4F, 0xA7, 0xD9, 0x1F, 0xFD, 0x03, 0xB9, 0x81,
+ 0xE0, 0xD5, 0x44, 0xCF, 0x72, 0xD7, 0xB1, 0xC0,
+ 0x37, 0x4F, 0x88, 0x01, 0x48, 0x2E, 0x6D, 0xEA,
+ 0x2E, 0xF9, 0x03, 0x87, 0x7E, 0xBA, 0x67, 0x5E
+ },
+ {
+ 0xD8, 0x86, 0x75, 0x11, 0x8F, 0xDB, 0x55, 0xA5,
+ 0xFB, 0x36, 0x5A, 0xC2, 0xAF, 0x1D, 0x21, 0x7B,
+ 0xF5, 0x26, 0xCE, 0x1E, 0xE9, 0xC9, 0x4B, 0x2F,
+ 0x00, 0x90, 0xB2, 0xC5, 0x8A, 0x06, 0xCA, 0x58,
+ 0x18, 0x7D, 0x7F, 0xE5, 0x7C, 0x7B, 0xED, 0x9D,
+ 0x26, 0xFC, 0xA0, 0x67, 0xB4, 0x11, 0x0E, 0xEF,
+ 0xCD, 0x9A, 0x0A, 0x34, 0x5D, 0xE8, 0x72, 0xAB,
+ 0xE2, 0x0D, 0xE3, 0x68, 0x00, 0x1B, 0x07, 0x45
+ },
+ {
+ 0xB8, 0x93, 0xF2, 0xFC, 0x41, 0xF7, 0xB0, 0xDD,
+ 0x6E, 0x2F, 0x6A, 0xA2, 0xE0, 0x37, 0x0C, 0x0C,
+ 0xFF, 0x7D, 0xF0, 0x9E, 0x3A, 0xCF, 0xCC, 0x0E,
+ 0x92, 0x0B, 0x6E, 0x6F, 0xAD, 0x0E, 0xF7, 0x47,
+ 0xC4, 0x06, 0x68, 0x41, 0x7D, 0x34, 0x2B, 0x80,
+ 0xD2, 0x35, 0x1E, 0x8C, 0x17, 0x5F, 0x20, 0x89,
+ 0x7A, 0x06, 0x2E, 0x97, 0x65, 0xE6, 0xC6, 0x7B,
+ 0x53, 0x9B, 0x6B, 0xA8, 0xB9, 0x17, 0x05, 0x45
+ },
+ {
+ 0x6C, 0x67, 0xEC, 0x56, 0x97, 0xAC, 0xCD, 0x23,
+ 0x5C, 0x59, 0xB4, 0x86, 0xD7, 0xB7, 0x0B, 0xAE,
+ 0xED, 0xCB, 0xD4, 0xAA, 0x64, 0xEB, 0xD4, 0xEE,
+ 0xF3, 0xC7, 0xEA, 0xC1, 0x89, 0x56, 0x1A, 0x72,
+ 0x62, 0x50, 0xAE, 0xC4, 0xD4, 0x8C, 0xAD, 0xCA,
+ 0xFB, 0xBE, 0x2C, 0xE3, 0xC1, 0x6C, 0xE2, 0xD6,
+ 0x91, 0xA8, 0xCC, 0xE0, 0x6E, 0x88, 0x79, 0x55,
+ 0x6D, 0x44, 0x83, 0xED, 0x71, 0x65, 0xC0, 0x63
+ },
+ {
+ 0xF1, 0xAA, 0x2B, 0x04, 0x4F, 0x8F, 0x0C, 0x63,
+ 0x8A, 0x3F, 0x36, 0x2E, 0x67, 0x7B, 0x5D, 0x89,
+ 0x1D, 0x6F, 0xD2, 0xAB, 0x07, 0x65, 0xF6, 0xEE,
+ 0x1E, 0x49, 0x87, 0xDE, 0x05, 0x7E, 0xAD, 0x35,
+ 0x78, 0x83, 0xD9, 0xB4, 0x05, 0xB9, 0xD6, 0x09,
+ 0xEE, 0xA1, 0xB8, 0x69, 0xD9, 0x7F, 0xB1, 0x6D,
+ 0x9B, 0x51, 0x01, 0x7C, 0x55, 0x3F, 0x3B, 0x93,
+ 0xC0, 0xA1, 0xE0, 0xF1, 0x29, 0x6F, 0xED, 0xCD
+ },
+ {
+ 0xCB, 0xAA, 0x25, 0x95, 0x72, 0xD4, 0xAE, 0xBF,
+ 0xC1, 0x91, 0x7A, 0xCD, 0xDC, 0x58, 0x2B, 0x9F,
+ 0x8D, 0xFA, 0xA9, 0x28, 0xA1, 0x98, 0xCA, 0x7A,
+ 0xCD, 0x0F, 0x2A, 0xA7, 0x6A, 0x13, 0x4A, 0x90,
+ 0x25, 0x2E, 0x62, 0x98, 0xA6, 0x5B, 0x08, 0x18,
+ 0x6A, 0x35, 0x0D, 0x5B, 0x76, 0x26, 0x69, 0x9F,
+ 0x8C, 0xB7, 0x21, 0xA3, 0xEA, 0x59, 0x21, 0xB7,
+ 0x53, 0xAE, 0x3A, 0x2D, 0xCE, 0x24, 0xBA, 0x3A
+ },
+ {
+ 0xFA, 0x15, 0x49, 0xC9, 0x79, 0x6C, 0xD4, 0xD3,
+ 0x03, 0xDC, 0xF4, 0x52, 0xC1, 0xFB, 0xD5, 0x74,
+ 0x4F, 0xD9, 0xB9, 0xB4, 0x70, 0x03, 0xD9, 0x20,
+ 0xB9, 0x2D, 0xE3, 0x48, 0x39, 0xD0, 0x7E, 0xF2,
+ 0xA2, 0x9D, 0xED, 0x68, 0xF6, 0xFC, 0x9E, 0x6C,
+ 0x45, 0xE0, 0x71, 0xA2, 0xE4, 0x8B, 0xD5, 0x0C,
+ 0x50, 0x84, 0xE9, 0x6B, 0x65, 0x7D, 0xD0, 0x40,
+ 0x40, 0x45, 0xA1, 0xDD, 0xEF, 0xE2, 0x82, 0xED
+ },
+ {
+ 0x5C, 0xF2, 0xAC, 0x89, 0x7A, 0xB4, 0x44, 0xDC,
+ 0xB5, 0xC8, 0xD8, 0x7C, 0x49, 0x5D, 0xBD, 0xB3,
+ 0x4E, 0x18, 0x38, 0xB6, 0xB6, 0x29, 0x42, 0x7C,
+ 0xAA, 0x51, 0x70, 0x2A, 0xD0, 0xF9, 0x68, 0x85,
+ 0x25, 0xF1, 0x3B, 0xEC, 0x50, 0x3A, 0x3C, 0x3A,
+ 0x2C, 0x80, 0xA6, 0x5E, 0x0B, 0x57, 0x15, 0xE8,
+ 0xAF, 0xAB, 0x00, 0xFF, 0xA5, 0x6E, 0xC4, 0x55,
+ 0xA4, 0x9A, 0x1A, 0xD3, 0x0A, 0xA2, 0x4F, 0xCD
+ },
+ {
+ 0x9A, 0xAF, 0x80, 0x20, 0x7B, 0xAC, 0xE1, 0x7B,
+ 0xB7, 0xAB, 0x14, 0x57, 0x57, 0xD5, 0x69, 0x6B,
+ 0xDE, 0x32, 0x40, 0x6E, 0xF2, 0x2B, 0x44, 0x29,
+ 0x2E, 0xF6, 0x5D, 0x45, 0x19, 0xC3, 0xBB, 0x2A,
+ 0xD4, 0x1A, 0x59, 0xB6, 0x2C, 0xC3, 0xE9, 0x4B,
+ 0x6F, 0xA9, 0x6D, 0x32, 0xA7, 0xFA, 0xAD, 0xAE,
+ 0x28, 0xAF, 0x7D, 0x35, 0x09, 0x72, 0x19, 0xAA,
+ 0x3F, 0xD8, 0xCD, 0xA3, 0x1E, 0x40, 0xC2, 0x75
+ },
+ {
+ 0xAF, 0x88, 0xB1, 0x63, 0x40, 0x2C, 0x86, 0x74,
+ 0x5C, 0xB6, 0x50, 0xC2, 0x98, 0x8F, 0xB9, 0x52,
+ 0x11, 0xB9, 0x4B, 0x03, 0xEF, 0x29, 0x0E, 0xED,
+ 0x96, 0x62, 0x03, 0x42, 0x41, 0xFD, 0x51, 0xCF,
+ 0x39, 0x8F, 0x80, 0x73, 0xE3, 0x69, 0x35, 0x4C,
+ 0x43, 0xEA, 0xE1, 0x05, 0x2F, 0x9B, 0x63, 0xB0,
+ 0x81, 0x91, 0xCA, 0xA1, 0x38, 0xAA, 0x54, 0xFE,
+ 0xA8, 0x89, 0xCC, 0x70, 0x24, 0x23, 0x68, 0x97
+ },
+ {
+ 0x48, 0xFA, 0x7D, 0x64, 0xE1, 0xCE, 0xEE, 0x27,
+ 0xB9, 0x86, 0x4D, 0xB5, 0xAD, 0xA4, 0xB5, 0x3D,
+ 0x00, 0xC9, 0xBC, 0x76, 0x26, 0x55, 0x58, 0x13,
+ 0xD3, 0xCD, 0x67, 0x30, 0xAB, 0x3C, 0xC0, 0x6F,
+ 0xF3, 0x42, 0xD7, 0x27, 0x90, 0x5E, 0x33, 0x17,
+ 0x1B, 0xDE, 0x6E, 0x84, 0x76, 0xE7, 0x7F, 0xB1,
+ 0x72, 0x08, 0x61, 0xE9, 0x4B, 0x73, 0xA2, 0xC5,
+ 0x38, 0xD2, 0x54, 0x74, 0x62, 0x85, 0xF4, 0x30
+ },
+ {
+ 0x0E, 0x6F, 0xD9, 0x7A, 0x85, 0xE9, 0x04, 0xF8,
+ 0x7B, 0xFE, 0x85, 0xBB, 0xEB, 0x34, 0xF6, 0x9E,
+ 0x1F, 0x18, 0x10, 0x5C, 0xF4, 0xED, 0x4F, 0x87,
+ 0xAE, 0xC3, 0x6C, 0x6E, 0x8B, 0x5F, 0x68, 0xBD,
+ 0x2A, 0x6F, 0x3D, 0xC8, 0xA9, 0xEC, 0xB2, 0xB6,
+ 0x1D, 0xB4, 0xEE, 0xDB, 0x6B, 0x2E, 0xA1, 0x0B,
+ 0xF9, 0xCB, 0x02, 0x51, 0xFB, 0x0F, 0x8B, 0x34,
+ 0x4A, 0xBF, 0x7F, 0x36, 0x6B, 0x6D, 0xE5, 0xAB
+ },
+ {
+ 0x06, 0x62, 0x2D, 0xA5, 0x78, 0x71, 0x76, 0x28,
+ 0x7F, 0xDC, 0x8F, 0xED, 0x44, 0x0B, 0xAD, 0x18,
+ 0x7D, 0x83, 0x00, 0x99, 0xC9, 0x4E, 0x6D, 0x04,
+ 0xC8, 0xE9, 0xC9, 0x54, 0xCD, 0xA7, 0x0C, 0x8B,
+ 0xB9, 0xE1, 0xFC, 0x4A, 0x6D, 0x0B, 0xAA, 0x83,
+ 0x1B, 0x9B, 0x78, 0xEF, 0x66, 0x48, 0x68, 0x1A,
+ 0x48, 0x67, 0xA1, 0x1D, 0xA9, 0x3E, 0xE3, 0x6E,
+ 0x5E, 0x6A, 0x37, 0xD8, 0x7F, 0xC6, 0x3F, 0x6F
+ },
+ {
+ 0x1D, 0xA6, 0x77, 0x2B, 0x58, 0xFA, 0xBF, 0x9C,
+ 0x61, 0xF6, 0x8D, 0x41, 0x2C, 0x82, 0xF1, 0x82,
+ 0xC0, 0x23, 0x6D, 0x7D, 0x57, 0x5E, 0xF0, 0xB5,
+ 0x8D, 0xD2, 0x24, 0x58, 0xD6, 0x43, 0xCD, 0x1D,
+ 0xFC, 0x93, 0xB0, 0x38, 0x71, 0xC3, 0x16, 0xD8,
+ 0x43, 0x0D, 0x31, 0x29, 0x95, 0xD4, 0x19, 0x7F,
+ 0x08, 0x74, 0xC9, 0x91, 0x72, 0xBA, 0x00, 0x4A,
+ 0x01, 0xEE, 0x29, 0x5A, 0xBA, 0xC2, 0x4E, 0x46
+ },
+ {
+ 0x3C, 0xD2, 0xD9, 0x32, 0x0B, 0x7B, 0x1D, 0x5F,
+ 0xB9, 0xAA, 0xB9, 0x51, 0xA7, 0x60, 0x23, 0xFA,
+ 0x66, 0x7B, 0xE1, 0x4A, 0x91, 0x24, 0xE3, 0x94,
+ 0x51, 0x39, 0x18, 0xA3, 0xF4, 0x40, 0x96, 0xAE,
+ 0x49, 0x04, 0xBA, 0x0F, 0xFC, 0x15, 0x0B, 0x63,
+ 0xBC, 0x7A, 0xB1, 0xEE, 0xB9, 0xA6, 0xE2, 0x57,
+ 0xE5, 0xC8, 0xF0, 0x00, 0xA7, 0x03, 0x94, 0xA5,
+ 0xAF, 0xD8, 0x42, 0x71, 0x5D, 0xE1, 0x5F, 0x29
+ },
+ {
+ 0x04, 0xCD, 0xC1, 0x4F, 0x74, 0x34, 0xE0, 0xB4,
+ 0xBE, 0x70, 0xCB, 0x41, 0xDB, 0x4C, 0x77, 0x9A,
+ 0x88, 0xEA, 0xEF, 0x6A, 0xCC, 0xEB, 0xCB, 0x41,
+ 0xF2, 0xD4, 0x2F, 0xFF, 0xE7, 0xF3, 0x2A, 0x8E,
+ 0x28, 0x1B, 0x5C, 0x10, 0x3A, 0x27, 0x02, 0x1D,
+ 0x0D, 0x08, 0x36, 0x22, 0x50, 0x75, 0x3C, 0xDF,
+ 0x70, 0x29, 0x21, 0x95, 0xA5, 0x3A, 0x48, 0x72,
+ 0x8C, 0xEB, 0x58, 0x44, 0xC2, 0xD9, 0x8B, 0xAB
+ },
+ {
+ 0x90, 0x71, 0xB7, 0xA8, 0xA0, 0x75, 0xD0, 0x09,
+ 0x5B, 0x8F, 0xB3, 0xAE, 0x51, 0x13, 0x78, 0x57,
+ 0x35, 0xAB, 0x98, 0xE2, 0xB5, 0x2F, 0xAF, 0x91,
+ 0xD5, 0xB8, 0x9E, 0x44, 0xAA, 0xC5, 0xB5, 0xD4,
+ 0xEB, 0xBF, 0x91, 0x22, 0x3B, 0x0F, 0xF4, 0xC7,
+ 0x19, 0x05, 0xDA, 0x55, 0x34, 0x2E, 0x64, 0x65,
+ 0x5D, 0x6E, 0xF8, 0xC8, 0x9A, 0x47, 0x68, 0xC3,
+ 0xF9, 0x3A, 0x6D, 0xC0, 0x36, 0x6B, 0x5B, 0xC8
+ },
+ {
+ 0xEB, 0xB3, 0x02, 0x40, 0xDD, 0x96, 0xC7, 0xBC,
+ 0x8D, 0x0A, 0xBE, 0x49, 0xAA, 0x4E, 0xDC, 0xBB,
+ 0x4A, 0xFD, 0xC5, 0x1F, 0xF9, 0xAA, 0xF7, 0x20,
+ 0xD3, 0xF9, 0xE7, 0xFB, 0xB0, 0xF9, 0xC6, 0xD6,
+ 0x57, 0x13, 0x50, 0x50, 0x17, 0x69, 0xFC, 0x4E,
+ 0xBD, 0x0B, 0x21, 0x41, 0x24, 0x7F, 0xF4, 0x00,
+ 0xD4, 0xFD, 0x4B, 0xE4, 0x14, 0xED, 0xF3, 0x77,
+ 0x57, 0xBB, 0x90, 0xA3, 0x2A, 0xC5, 0xC6, 0x5A
+ },
+ {
+ 0x85, 0x32, 0xC5, 0x8B, 0xF3, 0xC8, 0x01, 0x5D,
+ 0x9D, 0x1C, 0xBE, 0x00, 0xEE, 0xF1, 0xF5, 0x08,
+ 0x2F, 0x8F, 0x36, 0x32, 0xFB, 0xE9, 0xF1, 0xED,
+ 0x4F, 0x9D, 0xFB, 0x1F, 0xA7, 0x9E, 0x82, 0x83,
+ 0x06, 0x6D, 0x77, 0xC4, 0x4C, 0x4A, 0xF9, 0x43,
+ 0xD7, 0x6B, 0x30, 0x03, 0x64, 0xAE, 0xCB, 0xD0,
+ 0x64, 0x8C, 0x8A, 0x89, 0x39, 0xBD, 0x20, 0x41,
+ 0x23, 0xF4, 0xB5, 0x62, 0x60, 0x42, 0x2D, 0xEC
+ },
+ {
+ 0xFE, 0x98, 0x46, 0xD6, 0x4F, 0x7C, 0x77, 0x08,
+ 0x69, 0x6F, 0x84, 0x0E, 0x2D, 0x76, 0xCB, 0x44,
+ 0x08, 0xB6, 0x59, 0x5C, 0x2F, 0x81, 0xEC, 0x6A,
+ 0x28, 0xA7, 0xF2, 0xF2, 0x0C, 0xB8, 0x8C, 0xFE,
+ 0x6A, 0xC0, 0xB9, 0xE9, 0xB8, 0x24, 0x4F, 0x08,
+ 0xBD, 0x70, 0x95, 0xC3, 0x50, 0xC1, 0xD0, 0x84,
+ 0x2F, 0x64, 0xFB, 0x01, 0xBB, 0x7F, 0x53, 0x2D,
+ 0xFC, 0xD4, 0x73, 0x71, 0xB0, 0xAE, 0xEB, 0x79
+ },
+ {
+ 0x28, 0xF1, 0x7E, 0xA6, 0xFB, 0x6C, 0x42, 0x09,
+ 0x2D, 0xC2, 0x64, 0x25, 0x7E, 0x29, 0x74, 0x63,
+ 0x21, 0xFB, 0x5B, 0xDA, 0xEA, 0x98, 0x73, 0xC2,
+ 0xA7, 0xFA, 0x9D, 0x8F, 0x53, 0x81, 0x8E, 0x89,
+ 0x9E, 0x16, 0x1B, 0xC7, 0x7D, 0xFE, 0x80, 0x90,
+ 0xAF, 0xD8, 0x2B, 0xF2, 0x26, 0x6C, 0x5C, 0x1B,
+ 0xC9, 0x30, 0xA8, 0xD1, 0x54, 0x76, 0x24, 0x43,
+ 0x9E, 0x66, 0x2E, 0xF6, 0x95, 0xF2, 0x6F, 0x24
+ },
+ {
+ 0xEC, 0x6B, 0x7D, 0x7F, 0x03, 0x0D, 0x48, 0x50,
+ 0xAC, 0xAE, 0x3C, 0xB6, 0x15, 0xC2, 0x1D, 0xD2,
+ 0x52, 0x06, 0xD6, 0x3E, 0x84, 0xD1, 0xDB, 0x8D,
+ 0x95, 0x73, 0x70, 0x73, 0x7B, 0xA0, 0xE9, 0x84,
+ 0x67, 0xEA, 0x0C, 0xE2, 0x74, 0xC6, 0x61, 0x99,
+ 0x90, 0x1E, 0xAE, 0xC1, 0x8A, 0x08, 0x52, 0x57,
+ 0x15, 0xF5, 0x3B, 0xFD, 0xB0, 0xAA, 0xCB, 0x61,
+ 0x3D, 0x34, 0x2E, 0xBD, 0xCE, 0xED, 0xDC, 0x3B
+ },
+ {
+ 0xB4, 0x03, 0xD3, 0x69, 0x1C, 0x03, 0xB0, 0xD3,
+ 0x41, 0x8D, 0xF3, 0x27, 0xD5, 0x86, 0x0D, 0x34,
+ 0xBB, 0xFC, 0xC4, 0x51, 0x9B, 0xFB, 0xCE, 0x36,
+ 0xBF, 0x33, 0xB2, 0x08, 0x38, 0x5F, 0xAD, 0xB9,
+ 0x18, 0x6B, 0xC7, 0x8A, 0x76, 0xC4, 0x89, 0xD8,
+ 0x9F, 0xD5, 0x7E, 0x7D, 0xC7, 0x54, 0x12, 0xD2,
+ 0x3B, 0xCD, 0x1D, 0xAE, 0x84, 0x70, 0xCE, 0x92,
+ 0x74, 0x75, 0x4B, 0xB8, 0x58, 0x5B, 0x13, 0xC5
+ },
+ {
+ 0x31, 0xFC, 0x79, 0x73, 0x8B, 0x87, 0x72, 0xB3,
+ 0xF5, 0x5C, 0xD8, 0x17, 0x88, 0x13, 0xB3, 0xB5,
+ 0x2D, 0x0D, 0xB5, 0xA4, 0x19, 0xD3, 0x0B, 0xA9,
+ 0x49, 0x5C, 0x4B, 0x9D, 0xA0, 0x21, 0x9F, 0xAC,
+ 0x6D, 0xF8, 0xE7, 0xC2, 0x3A, 0x81, 0x15, 0x51,
+ 0xA6, 0x2B, 0x82, 0x7F, 0x25, 0x6E, 0xCD, 0xB8,
+ 0x12, 0x4A, 0xC8, 0xA6, 0x79, 0x2C, 0xCF, 0xEC,
+ 0xC3, 0xB3, 0x01, 0x27, 0x22, 0xE9, 0x44, 0x63
+ },
+ {
+ 0xBB, 0x20, 0x39, 0xEC, 0x28, 0x70, 0x91, 0xBC,
+ 0xC9, 0x64, 0x2F, 0xC9, 0x00, 0x49, 0xE7, 0x37,
+ 0x32, 0xE0, 0x2E, 0x57, 0x7E, 0x28, 0x62, 0xB3,
+ 0x22, 0x16, 0xAE, 0x9B, 0xED, 0xCD, 0x73, 0x0C,
+ 0x4C, 0x28, 0x4E, 0xF3, 0x96, 0x8C, 0x36, 0x8B,
+ 0x7D, 0x37, 0x58, 0x4F, 0x97, 0xBD, 0x4B, 0x4D,
+ 0xC6, 0xEF, 0x61, 0x27, 0xAC, 0xFE, 0x2E, 0x6A,
+ 0xE2, 0x50, 0x91, 0x24, 0xE6, 0x6C, 0x8A, 0xF4
+ },
+ {
+ 0xF5, 0x3D, 0x68, 0xD1, 0x3F, 0x45, 0xED, 0xFC,
+ 0xB9, 0xBD, 0x41, 0x5E, 0x28, 0x31, 0xE9, 0x38,
+ 0x35, 0x0D, 0x53, 0x80, 0xD3, 0x43, 0x22, 0x78,
+ 0xFC, 0x1C, 0x0C, 0x38, 0x1F, 0xCB, 0x7C, 0x65,
+ 0xC8, 0x2D, 0xAF, 0xE0, 0x51, 0xD8, 0xC8, 0xB0,
+ 0xD4, 0x4E, 0x09, 0x74, 0xA0, 0xE5, 0x9E, 0xC7,
+ 0xBF, 0x7E, 0xD0, 0x45, 0x9F, 0x86, 0xE9, 0x6F,
+ 0x32, 0x9F, 0xC7, 0x97, 0x52, 0x51, 0x0F, 0xD3
+ },
+ {
+ 0x8D, 0x56, 0x8C, 0x79, 0x84, 0xF0, 0xEC, 0xDF,
+ 0x76, 0x40, 0xFB, 0xC4, 0x83, 0xB5, 0xD8, 0xC9,
+ 0xF8, 0x66, 0x34, 0xF6, 0xF4, 0x32, 0x91, 0x84,
+ 0x1B, 0x30, 0x9A, 0x35, 0x0A, 0xB9, 0xC1, 0x13,
+ 0x7D, 0x24, 0x06, 0x6B, 0x09, 0xDA, 0x99, 0x44,
+ 0xBA, 0xC5, 0x4D, 0x5B, 0xB6, 0x58, 0x0D, 0x83,
+ 0x60, 0x47, 0xAA, 0xC7, 0x4A, 0xB7, 0x24, 0xB8,
+ 0x87, 0xEB, 0xF9, 0x3D, 0x4B, 0x32, 0xEC, 0xA9
+ },
+ {
+ 0xC0, 0xB6, 0x5C, 0xE5, 0xA9, 0x6F, 0xF7, 0x74,
+ 0xC4, 0x56, 0xCA, 0xC3, 0xB5, 0xF2, 0xC4, 0xCD,
+ 0x35, 0x9B, 0x4F, 0xF5, 0x3E, 0xF9, 0x3A, 0x3D,
+ 0xA0, 0x77, 0x8B, 0xE4, 0x90, 0x0D, 0x1E, 0x8D,
+ 0xA1, 0x60, 0x1E, 0x76, 0x9E, 0x8F, 0x1B, 0x02,
+ 0xD2, 0xA2, 0xF8, 0xC5, 0xB9, 0xFA, 0x10, 0xB4,
+ 0x4F, 0x1C, 0x18, 0x69, 0x85, 0x46, 0x8F, 0xEE,
+ 0xB0, 0x08, 0x73, 0x02, 0x83, 0xA6, 0x65, 0x7D
+ },
+ {
+ 0x49, 0x00, 0xBB, 0xA6, 0xF5, 0xFB, 0x10, 0x3E,
+ 0xCE, 0x8E, 0xC9, 0x6A, 0xDA, 0x13, 0xA5, 0xC3,
+ 0xC8, 0x54, 0x88, 0xE0, 0x55, 0x51, 0xDA, 0x6B,
+ 0x6B, 0x33, 0xD9, 0x88, 0xE6, 0x11, 0xEC, 0x0F,
+ 0xE2, 0xE3, 0xC2, 0xAA, 0x48, 0xEA, 0x6A, 0xE8,
+ 0x98, 0x6A, 0x3A, 0x23, 0x1B, 0x22, 0x3C, 0x5D,
+ 0x27, 0xCE, 0xC2, 0xEA, 0xDD, 0xE9, 0x1C, 0xE0,
+ 0x79, 0x81, 0xEE, 0x65, 0x28, 0x62, 0xD1, 0xE4
+ },
+ {
+ 0xC7, 0xF5, 0xC3, 0x7C, 0x72, 0x85, 0xF9, 0x27,
+ 0xF7, 0x64, 0x43, 0x41, 0x4D, 0x43, 0x57, 0xFF,
+ 0x78, 0x96, 0x47, 0xD7, 0xA0, 0x05, 0xA5, 0xA7,
+ 0x87, 0xE0, 0x3C, 0x34, 0x6B, 0x57, 0xF4, 0x9F,
+ 0x21, 0xB6, 0x4F, 0xA9, 0xCF, 0x4B, 0x7E, 0x45,
+ 0x57, 0x3E, 0x23, 0x04, 0x90, 0x17, 0x56, 0x71,
+ 0x21, 0xA9, 0xC3, 0xD4, 0xB2, 0xB7, 0x3E, 0xC5,
+ 0xE9, 0x41, 0x35, 0x77, 0x52, 0x5D, 0xB4, 0x5A
+ },
+ {
+ 0xEC, 0x70, 0x96, 0x33, 0x07, 0x36, 0xFD, 0xB2,
+ 0xD6, 0x4B, 0x56, 0x53, 0xE7, 0x47, 0x5D, 0xA7,
+ 0x46, 0xC2, 0x3A, 0x46, 0x13, 0xA8, 0x26, 0x87,
+ 0xA2, 0x80, 0x62, 0xD3, 0x23, 0x63, 0x64, 0x28,
+ 0x4A, 0xC0, 0x17, 0x20, 0xFF, 0xB4, 0x06, 0xCF,
+ 0xE2, 0x65, 0xC0, 0xDF, 0x62, 0x6A, 0x18, 0x8C,
+ 0x9E, 0x59, 0x63, 0xAC, 0xE5, 0xD3, 0xD5, 0xBB,
+ 0x36, 0x3E, 0x32, 0xC3, 0x8C, 0x21, 0x90, 0xA6
+ },
+ {
+ 0x82, 0xE7, 0x44, 0xC7, 0x5F, 0x46, 0x49, 0xEC,
+ 0x52, 0xB8, 0x07, 0x71, 0xA7, 0x7D, 0x47, 0x5A,
+ 0x3B, 0xC0, 0x91, 0x98, 0x95, 0x56, 0x96, 0x0E,
+ 0x27, 0x6A, 0x5F, 0x9E, 0xAD, 0x92, 0xA0, 0x3F,
+ 0x71, 0x87, 0x42, 0xCD, 0xCF, 0xEA, 0xEE, 0x5C,
+ 0xB8, 0x5C, 0x44, 0xAF, 0x19, 0x8A, 0xDC, 0x43,
+ 0xA4, 0xA4, 0x28, 0xF5, 0xF0, 0xC2, 0xDD, 0xB0,
+ 0xBE, 0x36, 0x05, 0x9F, 0x06, 0xD7, 0xDF, 0x73
+ },
+ {
+ 0x28, 0x34, 0xB7, 0xA7, 0x17, 0x0F, 0x1F, 0x5B,
+ 0x68, 0x55, 0x9A, 0xB7, 0x8C, 0x10, 0x50, 0xEC,
+ 0x21, 0xC9, 0x19, 0x74, 0x0B, 0x78, 0x4A, 0x90,
+ 0x72, 0xF6, 0xE5, 0xD6, 0x9F, 0x82, 0x8D, 0x70,
+ 0xC9, 0x19, 0xC5, 0x03, 0x9F, 0xB1, 0x48, 0xE3,
+ 0x9E, 0x2C, 0x8A, 0x52, 0x11, 0x83, 0x78, 0xB0,
+ 0x64, 0xCA, 0x8D, 0x50, 0x01, 0xCD, 0x10, 0xA5,
+ 0x47, 0x83, 0x87, 0xB9, 0x66, 0x71, 0x5E, 0xD6
+ },
+ {
+ 0x16, 0xB4, 0xAD, 0xA8, 0x83, 0xF7, 0x2F, 0x85,
+ 0x3B, 0xB7, 0xEF, 0x25, 0x3E, 0xFC, 0xAB, 0x0C,
+ 0x3E, 0x21, 0x61, 0x68, 0x7A, 0xD6, 0x15, 0x43,
+ 0xA0, 0xD2, 0x82, 0x4F, 0x91, 0xC1, 0xF8, 0x13,
+ 0x47, 0xD8, 0x6B, 0xE7, 0x09, 0xB1, 0x69, 0x96,
+ 0xE1, 0x7F, 0x2D, 0xD4, 0x86, 0x92, 0x7B, 0x02,
+ 0x88, 0xAD, 0x38, 0xD1, 0x30, 0x63, 0xC4, 0xA9,
+ 0x67, 0x2C, 0x39, 0x39, 0x7D, 0x37, 0x89, 0xB6
+ },
+ {
+ 0x78, 0xD0, 0x48, 0xF3, 0xA6, 0x9D, 0x8B, 0x54,
+ 0xAE, 0x0E, 0xD6, 0x3A, 0x57, 0x3A, 0xE3, 0x50,
+ 0xD8, 0x9F, 0x7C, 0x6C, 0xF1, 0xF3, 0x68, 0x89,
+ 0x30, 0xDE, 0x89, 0x9A, 0xFA, 0x03, 0x76, 0x97,
+ 0x62, 0x9B, 0x31, 0x4E, 0x5C, 0xD3, 0x03, 0xAA,
+ 0x62, 0xFE, 0xEA, 0x72, 0xA2, 0x5B, 0xF4, 0x2B,
+ 0x30, 0x4B, 0x6C, 0x6B, 0xCB, 0x27, 0xFA, 0xE2,
+ 0x1C, 0x16, 0xD9, 0x25, 0xE1, 0xFB, 0xDA, 0xC3
+ },
+ {
+ 0x0F, 0x74, 0x6A, 0x48, 0x74, 0x92, 0x87, 0xAD,
+ 0xA7, 0x7A, 0x82, 0x96, 0x1F, 0x05, 0xA4, 0xDA,
+ 0x4A, 0xBD, 0xB7, 0xD7, 0x7B, 0x12, 0x20, 0xF8,
+ 0x36, 0xD0, 0x9E, 0xC8, 0x14, 0x35, 0x9C, 0x0E,
+ 0xC0, 0x23, 0x9B, 0x8C, 0x7B, 0x9F, 0xF9, 0xE0,
+ 0x2F, 0x56, 0x9D, 0x1B, 0x30, 0x1E, 0xF6, 0x7C,
+ 0x46, 0x12, 0xD1, 0xDE, 0x4F, 0x73, 0x0F, 0x81,
+ 0xC1, 0x2C, 0x40, 0xCC, 0x06, 0x3C, 0x5C, 0xAA
+ },
+ {
+ 0xF0, 0xFC, 0x85, 0x9D, 0x3B, 0xD1, 0x95, 0xFB,
+ 0xDC, 0x2D, 0x59, 0x1E, 0x4C, 0xDA, 0xC1, 0x51,
+ 0x79, 0xEC, 0x0F, 0x1D, 0xC8, 0x21, 0xC1, 0x1D,
+ 0xF1, 0xF0, 0xC1, 0xD2, 0x6E, 0x62, 0x60, 0xAA,
+ 0xA6, 0x5B, 0x79, 0xFA, 0xFA, 0xCA, 0xFD, 0x7D,
+ 0x3A, 0xD6, 0x1E, 0x60, 0x0F, 0x25, 0x09, 0x05,
+ 0xF5, 0x87, 0x8C, 0x87, 0x45, 0x28, 0x97, 0x64,
+ 0x7A, 0x35, 0xB9, 0x95, 0xBC, 0xAD, 0xC3, 0xA3
+ },
+ {
+ 0x26, 0x20, 0xF6, 0x87, 0xE8, 0x62, 0x5F, 0x6A,
+ 0x41, 0x24, 0x60, 0xB4, 0x2E, 0x2C, 0xEF, 0x67,
+ 0x63, 0x42, 0x08, 0xCE, 0x10, 0xA0, 0xCB, 0xD4,
+ 0xDF, 0xF7, 0x04, 0x4A, 0x41, 0xB7, 0x88, 0x00,
+ 0x77, 0xE9, 0xF8, 0xDC, 0x3B, 0x8D, 0x12, 0x16,
+ 0xD3, 0x37, 0x6A, 0x21, 0xE0, 0x15, 0xB5, 0x8F,
+ 0xB2, 0x79, 0xB5, 0x21, 0xD8, 0x3F, 0x93, 0x88,
+ 0xC7, 0x38, 0x2C, 0x85, 0x05, 0x59, 0x0B, 0x9B
+ },
+ {
+ 0x22, 0x7E, 0x3A, 0xED, 0x8D, 0x2C, 0xB1, 0x0B,
+ 0x91, 0x8F, 0xCB, 0x04, 0xF9, 0xDE, 0x3E, 0x6D,
+ 0x0A, 0x57, 0xE0, 0x84, 0x76, 0xD9, 0x37, 0x59,
+ 0xCD, 0x7B, 0x2E, 0xD5, 0x4A, 0x1C, 0xBF, 0x02,
+ 0x39, 0xC5, 0x28, 0xFB, 0x04, 0xBB, 0xF2, 0x88,
+ 0x25, 0x3E, 0x60, 0x1D, 0x3B, 0xC3, 0x8B, 0x21,
+ 0x79, 0x4A, 0xFE, 0xF9, 0x0B, 0x17, 0x09, 0x4A,
+ 0x18, 0x2C, 0xAC, 0x55, 0x77, 0x45, 0xE7, 0x5F
+ },
+ {
+ 0x1A, 0x92, 0x99, 0x01, 0xB0, 0x9C, 0x25, 0xF2,
+ 0x7D, 0x6B, 0x35, 0xBE, 0x7B, 0x2F, 0x1C, 0x47,
+ 0x45, 0x13, 0x1F, 0xDE, 0xBC, 0xA7, 0xF3, 0xE2,
+ 0x45, 0x19, 0x26, 0x72, 0x04, 0x34, 0xE0, 0xDB,
+ 0x6E, 0x74, 0xFD, 0x69, 0x3A, 0xD2, 0x9B, 0x77,
+ 0x7D, 0xC3, 0x35, 0x5C, 0x59, 0x2A, 0x36, 0x1C,
+ 0x48, 0x73, 0xB0, 0x11, 0x33, 0xA5, 0x7C, 0x2E,
+ 0x3B, 0x70, 0x75, 0xCB, 0xDB, 0x86, 0xF4, 0xFC
+ },
+ {
+ 0x5F, 0xD7, 0x96, 0x8B, 0xC2, 0xFE, 0x34, 0xF2,
+ 0x20, 0xB5, 0xE3, 0xDC, 0x5A, 0xF9, 0x57, 0x17,
+ 0x42, 0xD7, 0x3B, 0x7D, 0x60, 0x81, 0x9F, 0x28,
+ 0x88, 0xB6, 0x29, 0x07, 0x2B, 0x96, 0xA9, 0xD8,
+ 0xAB, 0x2D, 0x91, 0xB8, 0x2D, 0x0A, 0x9A, 0xAB,
+ 0xA6, 0x1B, 0xBD, 0x39, 0x95, 0x81, 0x32, 0xFC,
+ 0xC4, 0x25, 0x70, 0x23, 0xD1, 0xEC, 0xA5, 0x91,
+ 0xB3, 0x05, 0x4E, 0x2D, 0xC8, 0x1C, 0x82, 0x00
+ },
+ {
+ 0xDF, 0xCC, 0xE8, 0xCF, 0x32, 0x87, 0x0C, 0xC6,
+ 0xA5, 0x03, 0xEA, 0xDA, 0xFC, 0x87, 0xFD, 0x6F,
+ 0x78, 0x91, 0x8B, 0x9B, 0x4D, 0x07, 0x37, 0xDB,
+ 0x68, 0x10, 0xBE, 0x99, 0x6B, 0x54, 0x97, 0xE7,
+ 0xE5, 0xCC, 0x80, 0xE3, 0x12, 0xF6, 0x1E, 0x71,
+ 0xFF, 0x3E, 0x96, 0x24, 0x43, 0x60, 0x73, 0x15,
+ 0x64, 0x03, 0xF7, 0x35, 0xF5, 0x6B, 0x0B, 0x01,
+ 0x84, 0x5C, 0x18, 0xF6, 0xCA, 0xF7, 0x72, 0xE6
+ },
+ {
+ 0x02, 0xF7, 0xEF, 0x3A, 0x9C, 0xE0, 0xFF, 0xF9,
+ 0x60, 0xF6, 0x70, 0x32, 0xB2, 0x96, 0xEF, 0xCA,
+ 0x30, 0x61, 0xF4, 0x93, 0x4D, 0x69, 0x07, 0x49,
+ 0xF2, 0xD0, 0x1C, 0x35, 0xC8, 0x1C, 0x14, 0xF3,
+ 0x9A, 0x67, 0xFA, 0x35, 0x0B, 0xC8, 0xA0, 0x35,
+ 0x9B, 0xF1, 0x72, 0x4B, 0xFF, 0xC3, 0xBC, 0xA6,
+ 0xD7, 0xC7, 0xBB, 0xA4, 0x79, 0x1F, 0xD5, 0x22,
+ 0xA3, 0xAD, 0x35, 0x3C, 0x02, 0xEC, 0x5A, 0xA8
+ },
+ {
+ 0x64, 0xBE, 0x5C, 0x6A, 0xBA, 0x65, 0xD5, 0x94,
+ 0x84, 0x4A, 0xE7, 0x8B, 0xB0, 0x22, 0xE5, 0xBE,
+ 0xBE, 0x12, 0x7F, 0xD6, 0xB6, 0xFF, 0xA5, 0xA1,
+ 0x37, 0x03, 0x85, 0x5A, 0xB6, 0x3B, 0x62, 0x4D,
+ 0xCD, 0x1A, 0x36, 0x3F, 0x99, 0x20, 0x3F, 0x63,
+ 0x2E, 0xC3, 0x86, 0xF3, 0xEA, 0x76, 0x7F, 0xC9,
+ 0x92, 0xE8, 0xED, 0x96, 0x86, 0x58, 0x6A, 0xA2,
+ 0x75, 0x55, 0xA8, 0x59, 0x9D, 0x5B, 0x80, 0x8F
+ },
+ {
+ 0xF7, 0x85, 0x85, 0x50, 0x5C, 0x4E, 0xAA, 0x54,
+ 0xA8, 0xB5, 0xBE, 0x70, 0xA6, 0x1E, 0x73, 0x5E,
+ 0x0F, 0xF9, 0x7A, 0xF9, 0x44, 0xDD, 0xB3, 0x00,
+ 0x1E, 0x35, 0xD8, 0x6C, 0x4E, 0x21, 0x99, 0xD9,
+ 0x76, 0x10, 0x4B, 0x6A, 0xE3, 0x17, 0x50, 0xA3,
+ 0x6A, 0x72, 0x6E, 0xD2, 0x85, 0x06, 0x4F, 0x59,
+ 0x81, 0xB5, 0x03, 0x88, 0x9F, 0xEF, 0x82, 0x2F,
+ 0xCD, 0xC2, 0x89, 0x8D, 0xDD, 0xB7, 0x88, 0x9A
+ },
+ {
+ 0xE4, 0xB5, 0x56, 0x60, 0x33, 0x86, 0x95, 0x72,
+ 0xED, 0xFD, 0x87, 0x47, 0x9A, 0x5B, 0xB7, 0x3C,
+ 0x80, 0xE8, 0x75, 0x9B, 0x91, 0x23, 0x28, 0x79,
+ 0xD9, 0x6B, 0x1D, 0xDA, 0x36, 0xC0, 0x12, 0x07,
+ 0x6E, 0xE5, 0xA2, 0xED, 0x7A, 0xE2, 0xDE, 0x63,
+ 0xEF, 0x84, 0x06, 0xA0, 0x6A, 0xEA, 0x82, 0xC1,
+ 0x88, 0x03, 0x1B, 0x56, 0x0B, 0xEA, 0xFB, 0x58,
+ 0x3F, 0xB3, 0xDE, 0x9E, 0x57, 0x95, 0x2A, 0x7E
+ },
+ {
+ 0xE1, 0xB3, 0xE7, 0xED, 0x86, 0x7F, 0x6C, 0x94,
+ 0x84, 0xA2, 0xA9, 0x7F, 0x77, 0x15, 0xF2, 0x5E,
+ 0x25, 0x29, 0x4E, 0x99, 0x2E, 0x41, 0xF6, 0xA7,
+ 0xC1, 0x61, 0xFF, 0xC2, 0xAD, 0xC6, 0xDA, 0xAE,
+ 0xB7, 0x11, 0x31, 0x02, 0xD5, 0xE6, 0x09, 0x02,
+ 0x87, 0xFE, 0x6A, 0xD9, 0x4C, 0xE5, 0xD6, 0xB7,
+ 0x39, 0xC6, 0xCA, 0x24, 0x0B, 0x05, 0xC7, 0x6F,
+ 0xB7, 0x3F, 0x25, 0xDD, 0x02, 0x4B, 0xF9, 0x35
+ },
+ {
+ 0x85, 0xFD, 0x08, 0x5F, 0xDC, 0x12, 0xA0, 0x80,
+ 0x98, 0x3D, 0xF0, 0x7B, 0xD7, 0x01, 0x2B, 0x0D,
+ 0x40, 0x2A, 0x0F, 0x40, 0x43, 0xFC, 0xB2, 0x77,
+ 0x5A, 0xDF, 0x0B, 0xAD, 0x17, 0x4F, 0x9B, 0x08,
+ 0xD1, 0x67, 0x6E, 0x47, 0x69, 0x85, 0x78, 0x5C,
+ 0x0A, 0x5D, 0xCC, 0x41, 0xDB, 0xFF, 0x6D, 0x95,
+ 0xEF, 0x4D, 0x66, 0xA3, 0xFB, 0xDC, 0x4A, 0x74,
+ 0xB8, 0x2B, 0xA5, 0x2D, 0xA0, 0x51, 0x2B, 0x74
+ },
+ {
+ 0xAE, 0xD8, 0xFA, 0x76, 0x4B, 0x0F, 0xBF, 0xF8,
+ 0x21, 0xE0, 0x52, 0x33, 0xD2, 0xF7, 0xB0, 0x90,
+ 0x0E, 0xC4, 0x4D, 0x82, 0x6F, 0x95, 0xE9, 0x3C,
+ 0x34, 0x3C, 0x1B, 0xC3, 0xBA, 0x5A, 0x24, 0x37,
+ 0x4B, 0x1D, 0x61, 0x6E, 0x7E, 0x7A, 0xBA, 0x45,
+ 0x3A, 0x0A, 0xDA, 0x5E, 0x4F, 0xAB, 0x53, 0x82,
+ 0x40, 0x9E, 0x0D, 0x42, 0xCE, 0x9C, 0x2B, 0xC7,
+ 0xFB, 0x39, 0xA9, 0x9C, 0x34, 0x0C, 0x20, 0xF0
+ },
+ {
+ 0x7B, 0xA3, 0xB2, 0xE2, 0x97, 0x23, 0x35, 0x22,
+ 0xEE, 0xB3, 0x43, 0xBD, 0x3E, 0xBC, 0xFD, 0x83,
+ 0x5A, 0x04, 0x00, 0x77, 0x35, 0xE8, 0x7F, 0x0C,
+ 0xA3, 0x00, 0xCB, 0xEE, 0x6D, 0x41, 0x65, 0x65,
+ 0x16, 0x21, 0x71, 0x58, 0x1E, 0x40, 0x20, 0xFF,
+ 0x4C, 0xF1, 0x76, 0x45, 0x0F, 0x12, 0x91, 0xEA,
+ 0x22, 0x85, 0xCB, 0x9E, 0xBF, 0xFE, 0x4C, 0x56,
+ 0x66, 0x06, 0x27, 0x68, 0x51, 0x45, 0x05, 0x1C
+ },
+ {
+ 0xDE, 0x74, 0x8B, 0xCF, 0x89, 0xEC, 0x88, 0x08,
+ 0x47, 0x21, 0xE1, 0x6B, 0x85, 0xF3, 0x0A, 0xDB,
+ 0x1A, 0x61, 0x34, 0xD6, 0x64, 0xB5, 0x84, 0x35,
+ 0x69, 0xBA, 0xBC, 0x5B, 0xBD, 0x1A, 0x15, 0xCA,
+ 0x9B, 0x61, 0x80, 0x3C, 0x90, 0x1A, 0x4F, 0xEF,
+ 0x32, 0x96, 0x5A, 0x17, 0x49, 0xC9, 0xF3, 0xA4,
+ 0xE2, 0x43, 0xE1, 0x73, 0x93, 0x9D, 0xC5, 0xA8,
+ 0xDC, 0x49, 0x5C, 0x67, 0x1A, 0xB5, 0x21, 0x45
+ },
+ {
+ 0xAA, 0xF4, 0xD2, 0xBD, 0xF2, 0x00, 0xA9, 0x19,
+ 0x70, 0x6D, 0x98, 0x42, 0xDC, 0xE1, 0x6C, 0x98,
+ 0x14, 0x0D, 0x34, 0xBC, 0x43, 0x3D, 0xF3, 0x20,
+ 0xAB, 0xA9, 0xBD, 0x42, 0x9E, 0x54, 0x9A, 0xA7,
+ 0xA3, 0x39, 0x76, 0x52, 0xA4, 0xD7, 0x68, 0x27,
+ 0x77, 0x86, 0xCF, 0x99, 0x3C, 0xDE, 0x23, 0x38,
+ 0x67, 0x3E, 0xD2, 0xE6, 0xB6, 0x6C, 0x96, 0x1F,
+ 0xEF, 0xB8, 0x2C, 0xD2, 0x0C, 0x93, 0x33, 0x8F
+ },
+ {
+ 0xC4, 0x08, 0x21, 0x89, 0x68, 0xB7, 0x88, 0xBF,
+ 0x86, 0x4F, 0x09, 0x97, 0xE6, 0xBC, 0x4C, 0x3D,
+ 0xBA, 0x68, 0xB2, 0x76, 0xE2, 0x12, 0x5A, 0x48,
+ 0x43, 0x29, 0x60, 0x52, 0xFF, 0x93, 0xBF, 0x57,
+ 0x67, 0xB8, 0xCD, 0xCE, 0x71, 0x31, 0xF0, 0x87,
+ 0x64, 0x30, 0xC1, 0x16, 0x5F, 0xEC, 0x6C, 0x4F,
+ 0x47, 0xAD, 0xAA, 0x4F, 0xD8, 0xBC, 0xFA, 0xCE,
+ 0xF4, 0x63, 0xB5, 0xD3, 0xD0, 0xFA, 0x61, 0xA0
+ },
+ {
+ 0x76, 0xD2, 0xD8, 0x19, 0xC9, 0x2B, 0xCE, 0x55,
+ 0xFA, 0x8E, 0x09, 0x2A, 0xB1, 0xBF, 0x9B, 0x9E,
+ 0xAB, 0x23, 0x7A, 0x25, 0x26, 0x79, 0x86, 0xCA,
+ 0xCF, 0x2B, 0x8E, 0xE1, 0x4D, 0x21, 0x4D, 0x73,
+ 0x0D, 0xC9, 0xA5, 0xAA, 0x2D, 0x7B, 0x59, 0x6E,
+ 0x86, 0xA1, 0xFD, 0x8F, 0xA0, 0x80, 0x4C, 0x77,
+ 0x40, 0x2D, 0x2F, 0xCD, 0x45, 0x08, 0x36, 0x88,
+ 0xB2, 0x18, 0xB1, 0xCD, 0xFA, 0x0D, 0xCB, 0xCB
+ },
+ {
+ 0x72, 0x06, 0x5E, 0xE4, 0xDD, 0x91, 0xC2, 0xD8,
+ 0x50, 0x9F, 0xA1, 0xFC, 0x28, 0xA3, 0x7C, 0x7F,
+ 0xC9, 0xFA, 0x7D, 0x5B, 0x3F, 0x8A, 0xD3, 0xD0,
+ 0xD7, 0xA2, 0x56, 0x26, 0xB5, 0x7B, 0x1B, 0x44,
+ 0x78, 0x8D, 0x4C, 0xAF, 0x80, 0x62, 0x90, 0x42,
+ 0x5F, 0x98, 0x90, 0xA3, 0xA2, 0xA3, 0x5A, 0x90,
+ 0x5A, 0xB4, 0xB3, 0x7A, 0xCF, 0xD0, 0xDA, 0x6E,
+ 0x45, 0x17, 0xB2, 0x52, 0x5C, 0x96, 0x51, 0xE4
+ },
+ {
+ 0x64, 0x47, 0x5D, 0xFE, 0x76, 0x00, 0xD7, 0x17,
+ 0x1B, 0xEA, 0x0B, 0x39, 0x4E, 0x27, 0xC9, 0xB0,
+ 0x0D, 0x8E, 0x74, 0xDD, 0x1E, 0x41, 0x6A, 0x79,
+ 0x47, 0x36, 0x82, 0xAD, 0x3D, 0xFD, 0xBB, 0x70,
+ 0x66, 0x31, 0x55, 0x80, 0x55, 0xCF, 0xC8, 0xA4,
+ 0x0E, 0x07, 0xBD, 0x01, 0x5A, 0x45, 0x40, 0xDC,
+ 0xDE, 0xA1, 0x58, 0x83, 0xCB, 0xBF, 0x31, 0x41,
+ 0x2D, 0xF1, 0xDE, 0x1C, 0xD4, 0x15, 0x2B, 0x91
+ },
+ {
+ 0x12, 0xCD, 0x16, 0x74, 0xA4, 0x48, 0x8A, 0x5D,
+ 0x7C, 0x2B, 0x31, 0x60, 0xD2, 0xE2, 0xC4, 0xB5,
+ 0x83, 0x71, 0xBE, 0xDA, 0xD7, 0x93, 0x41, 0x8D,
+ 0x6F, 0x19, 0xC6, 0xEE, 0x38, 0x5D, 0x70, 0xB3,
+ 0xE0, 0x67, 0x39, 0x36, 0x9D, 0x4D, 0xF9, 0x10,
+ 0xED, 0xB0, 0xB0, 0xA5, 0x4C, 0xBF, 0xF4, 0x3D,
+ 0x54, 0x54, 0x4C, 0xD3, 0x7A, 0xB3, 0xA0, 0x6C,
+ 0xFA, 0x0A, 0x3D, 0xDA, 0xC8, 0xB6, 0x6C, 0x89
+ },
+ {
+ 0x60, 0x75, 0x69, 0x66, 0x47, 0x9D, 0xED, 0xC6,
+ 0xDD, 0x4B, 0xCF, 0xF8, 0xEA, 0x7D, 0x1D, 0x4C,
+ 0xE4, 0xD4, 0xAF, 0x2E, 0x7B, 0x09, 0x7E, 0x32,
+ 0xE3, 0x76, 0x35, 0x18, 0x44, 0x11, 0x47, 0xCC,
+ 0x12, 0xB3, 0xC0, 0xEE, 0x6D, 0x2E, 0xCA, 0xBF,
+ 0x11, 0x98, 0xCE, 0xC9, 0x2E, 0x86, 0xA3, 0x61,
+ 0x6F, 0xBA, 0x4F, 0x4E, 0x87, 0x2F, 0x58, 0x25,
+ 0x33, 0x0A, 0xDB, 0xB4, 0xC1, 0xDE, 0xE4, 0x44
+ },
+ {
+ 0xA7, 0x80, 0x3B, 0xCB, 0x71, 0xBC, 0x1D, 0x0F,
+ 0x43, 0x83, 0xDD, 0xE1, 0xE0, 0x61, 0x2E, 0x04,
+ 0xF8, 0x72, 0xB7, 0x15, 0xAD, 0x30, 0x81, 0x5C,
+ 0x22, 0x49, 0xCF, 0x34, 0xAB, 0xB8, 0xB0, 0x24,
+ 0x91, 0x5C, 0xB2, 0xFC, 0x9F, 0x4E, 0x7C, 0xC4,
+ 0xC8, 0xCF, 0xD4, 0x5B, 0xE2, 0xD5, 0xA9, 0x1E,
+ 0xAB, 0x09, 0x41, 0xC7, 0xD2, 0x70, 0xE2, 0xDA,
+ 0x4C, 0xA4, 0xA9, 0xF7, 0xAC, 0x68, 0x66, 0x3A
+ },
+ {
+ 0xB8, 0x4E, 0xF6, 0xA7, 0x22, 0x9A, 0x34, 0xA7,
+ 0x50, 0xD9, 0xA9, 0x8E, 0xE2, 0x52, 0x98, 0x71,
+ 0x81, 0x6B, 0x87, 0xFB, 0xE3, 0xBC, 0x45, 0xB4,
+ 0x5F, 0xA5, 0xAE, 0x82, 0xD5, 0x14, 0x15, 0x40,
+ 0x21, 0x11, 0x65, 0xC3, 0xC5, 0xD7, 0xA7, 0x47,
+ 0x6B, 0xA5, 0xA4, 0xAA, 0x06, 0xD6, 0x64, 0x76,
+ 0xF0, 0xD9, 0xDC, 0x49, 0xA3, 0xF1, 0xEE, 0x72,
+ 0xC3, 0xAC, 0xAB, 0xD4, 0x98, 0x96, 0x74, 0x14
+ },
+ {
+ 0xFA, 0xE4, 0xB6, 0xD8, 0xEF, 0xC3, 0xF8, 0xC8,
+ 0xE6, 0x4D, 0x00, 0x1D, 0xAB, 0xEC, 0x3A, 0x21,
+ 0xF5, 0x44, 0xE8, 0x27, 0x14, 0x74, 0x52, 0x51,
+ 0xB2, 0xB4, 0xB3, 0x93, 0xF2, 0xF4, 0x3E, 0x0D,
+ 0xA3, 0xD4, 0x03, 0xC6, 0x4D, 0xB9, 0x5A, 0x2C,
+ 0xB6, 0xE2, 0x3E, 0xBB, 0x7B, 0x9E, 0x94, 0xCD,
+ 0xD5, 0xDD, 0xAC, 0x54, 0xF0, 0x7C, 0x4A, 0x61,
+ 0xBD, 0x3C, 0xB1, 0x0A, 0xA6, 0xF9, 0x3B, 0x49
+ },
+ {
+ 0x34, 0xF7, 0x28, 0x66, 0x05, 0xA1, 0x22, 0x36,
+ 0x95, 0x40, 0x14, 0x1D, 0xED, 0x79, 0xB8, 0x95,
+ 0x72, 0x55, 0xDA, 0x2D, 0x41, 0x55, 0xAB, 0xBF,
+ 0x5A, 0x8D, 0xBB, 0x89, 0xC8, 0xEB, 0x7E, 0xDE,
+ 0x8E, 0xEE, 0xF1, 0xDA, 0xA4, 0x6D, 0xC2, 0x9D,
+ 0x75, 0x1D, 0x04, 0x5D, 0xC3, 0xB1, 0xD6, 0x58,
+ 0xBB, 0x64, 0xB8, 0x0F, 0xF8, 0x58, 0x9E, 0xDD,
+ 0xB3, 0x82, 0x4B, 0x13, 0xDA, 0x23, 0x5A, 0x6B
+ },
+ {
+ 0x3B, 0x3B, 0x48, 0x43, 0x4B, 0xE2, 0x7B, 0x9E,
+ 0xAB, 0xAB, 0xBA, 0x43, 0xBF, 0x6B, 0x35, 0xF1,
+ 0x4B, 0x30, 0xF6, 0xA8, 0x8D, 0xC2, 0xE7, 0x50,
+ 0xC3, 0x58, 0x47, 0x0D, 0x6B, 0x3A, 0xA3, 0xC1,
+ 0x8E, 0x47, 0xDB, 0x40, 0x17, 0xFA, 0x55, 0x10,
+ 0x6D, 0x82, 0x52, 0xF0, 0x16, 0x37, 0x1A, 0x00,
+ 0xF5, 0xF8, 0xB0, 0x70, 0xB7, 0x4B, 0xA5, 0xF2,
+ 0x3C, 0xFF, 0xC5, 0x51, 0x1C, 0x9F, 0x09, 0xF0
+ },
+ {
+ 0xBA, 0x28, 0x9E, 0xBD, 0x65, 0x62, 0xC4, 0x8C,
+ 0x3E, 0x10, 0xA8, 0xAD, 0x6C, 0xE0, 0x2E, 0x73,
+ 0x43, 0x3D, 0x1E, 0x93, 0xD7, 0xC9, 0x27, 0x9D,
+ 0x4D, 0x60, 0xA7, 0xE8, 0x79, 0xEE, 0x11, 0xF4,
+ 0x41, 0xA0, 0x00, 0xF4, 0x8E, 0xD9, 0xF7, 0xC4,
+ 0xED, 0x87, 0xA4, 0x51, 0x36, 0xD7, 0xDC, 0xCD,
+ 0xCA, 0x48, 0x21, 0x09, 0xC7, 0x8A, 0x51, 0x06,
+ 0x2B, 0x3B, 0xA4, 0x04, 0x4A, 0xDA, 0x24, 0x69
+ },
+ {
+ 0x02, 0x29, 0x39, 0xE2, 0x38, 0x6C, 0x5A, 0x37,
+ 0x04, 0x98, 0x56, 0xC8, 0x50, 0xA2, 0xBB, 0x10,
+ 0xA1, 0x3D, 0xFE, 0xA4, 0x21, 0x2B, 0x4C, 0x73,
+ 0x2A, 0x88, 0x40, 0xA9, 0xFF, 0xA5, 0xFA, 0xF5,
+ 0x48, 0x75, 0xC5, 0x44, 0x88, 0x16, 0xB2, 0x78,
+ 0x5A, 0x00, 0x7D, 0xA8, 0xA8, 0xD2, 0xBC, 0x7D,
+ 0x71, 0xA5, 0x4E, 0x4E, 0x65, 0x71, 0xF1, 0x0B,
+ 0x60, 0x0C, 0xBD, 0xB2, 0x5D, 0x13, 0xED, 0xE3
+ },
+ {
+ 0xE6, 0xFE, 0xC1, 0x9D, 0x89, 0xCE, 0x87, 0x17,
+ 0xB1, 0xA0, 0x87, 0x02, 0x46, 0x70, 0xFE, 0x02,
+ 0x6F, 0x6C, 0x7C, 0xBD, 0xA1, 0x1C, 0xAE, 0xF9,
+ 0x59, 0xBB, 0x2D, 0x35, 0x1B, 0xF8, 0x56, 0xF8,
+ 0x05, 0x5D, 0x1C, 0x0E, 0xBD, 0xAA, 0xA9, 0xD1,
+ 0xB1, 0x78, 0x86, 0xFC, 0x2C, 0x56, 0x2B, 0x5E,
+ 0x99, 0x64, 0x2F, 0xC0, 0x64, 0x71, 0x0C, 0x0D,
+ 0x34, 0x88, 0xA0, 0x2B, 0x5E, 0xD7, 0xF6, 0xFD
+ },
+ {
+ 0x94, 0xC9, 0x6F, 0x02, 0xA8, 0xF5, 0x76, 0xAC,
+ 0xA3, 0x2B, 0xA6, 0x1C, 0x2B, 0x20, 0x6F, 0x90,
+ 0x72, 0x85, 0xD9, 0x29, 0x9B, 0x83, 0xAC, 0x17,
+ 0x5C, 0x20, 0x9A, 0x8D, 0x43, 0xD5, 0x3B, 0xFE,
+ 0x68, 0x3D, 0xD1, 0xD8, 0x3E, 0x75, 0x49, 0xCB,
+ 0x90, 0x6C, 0x28, 0xF5, 0x9A, 0xB7, 0xC4, 0x6F,
+ 0x87, 0x51, 0x36, 0x6A, 0x28, 0xC3, 0x9D, 0xD5,
+ 0xFE, 0x26, 0x93, 0xC9, 0x01, 0x96, 0x66, 0xC8
+ },
+ {
+ 0x31, 0xA0, 0xCD, 0x21, 0x5E, 0xBD, 0x2C, 0xB6,
+ 0x1D, 0xE5, 0xB9, 0xED, 0xC9, 0x1E, 0x61, 0x95,
+ 0xE3, 0x1C, 0x59, 0xA5, 0x64, 0x8D, 0x5C, 0x9F,
+ 0x73, 0x7E, 0x12, 0x5B, 0x26, 0x05, 0x70, 0x8F,
+ 0x2E, 0x32, 0x5A, 0xB3, 0x38, 0x1C, 0x8D, 0xCE,
+ 0x1A, 0x3E, 0x95, 0x88, 0x86, 0xF1, 0xEC, 0xDC,
+ 0x60, 0x31, 0x8F, 0x88, 0x2C, 0xFE, 0x20, 0xA2,
+ 0x41, 0x91, 0x35, 0x2E, 0x61, 0x7B, 0x0F, 0x21
+ },
+ {
+ 0x91, 0xAB, 0x50, 0x4A, 0x52, 0x2D, 0xCE, 0x78,
+ 0x77, 0x9F, 0x4C, 0x6C, 0x6B, 0xA2, 0xE6, 0xB6,
+ 0xDB, 0x55, 0x65, 0xC7, 0x6D, 0x3E, 0x7E, 0x7C,
+ 0x92, 0x0C, 0xAF, 0x7F, 0x75, 0x7E, 0xF9, 0xDB,
+ 0x7C, 0x8F, 0xCF, 0x10, 0xE5, 0x7F, 0x03, 0x37,
+ 0x9E, 0xA9, 0xBF, 0x75, 0xEB, 0x59, 0x89, 0x5D,
+ 0x96, 0xE1, 0x49, 0x80, 0x0B, 0x6A, 0xAE, 0x01,
+ 0xDB, 0x77, 0x8B, 0xB9, 0x0A, 0xFB, 0xC9, 0x89
+ },
+ {
+ 0xD8, 0x5C, 0xAB, 0xC6, 0xBD, 0x5B, 0x1A, 0x01,
+ 0xA5, 0xAF, 0xD8, 0xC6, 0x73, 0x47, 0x40, 0xDA,
+ 0x9F, 0xD1, 0xC1, 0xAC, 0xC6, 0xDB, 0x29, 0xBF,
+ 0xC8, 0xA2, 0xE5, 0xB6, 0x68, 0xB0, 0x28, 0xB6,
+ 0xB3, 0x15, 0x4B, 0xFB, 0x87, 0x03, 0xFA, 0x31,
+ 0x80, 0x25, 0x1D, 0x58, 0x9A, 0xD3, 0x80, 0x40,
+ 0xCE, 0xB7, 0x07, 0xC4, 0xBA, 0xD1, 0xB5, 0x34,
+ 0x3C, 0xB4, 0x26, 0xB6, 0x1E, 0xAA, 0x49, 0xC1
+ },
+ {
+ 0xD6, 0x2E, 0xFB, 0xEC, 0x2C, 0xA9, 0xC1, 0xF8,
+ 0xBD, 0x66, 0xCE, 0x8B, 0x3F, 0x6A, 0x89, 0x8C,
+ 0xB3, 0xF7, 0x56, 0x6B, 0xA6, 0x56, 0x8C, 0x61,
+ 0x8A, 0xD1, 0xFE, 0xB2, 0xB6, 0x5B, 0x76, 0xC3,
+ 0xCE, 0x1D, 0xD2, 0x0F, 0x73, 0x95, 0x37, 0x2F,
+ 0xAF, 0x28, 0x42, 0x7F, 0x61, 0xC9, 0x27, 0x80,
+ 0x49, 0xCF, 0x01, 0x40, 0xDF, 0x43, 0x4F, 0x56,
+ 0x33, 0x04, 0x8C, 0x86, 0xB8, 0x1E, 0x03, 0x99
+ },
+ {
+ 0x7C, 0x8F, 0xDC, 0x61, 0x75, 0x43, 0x9E, 0x2C,
+ 0x3D, 0xB1, 0x5B, 0xAF, 0xA7, 0xFB, 0x06, 0x14,
+ 0x3A, 0x6A, 0x23, 0xBC, 0x90, 0xF4, 0x49, 0xE7,
+ 0x9D, 0xEE, 0xF7, 0x3C, 0x3D, 0x49, 0x2A, 0x67,
+ 0x17, 0x15, 0xC1, 0x93, 0xB6, 0xFE, 0xA9, 0xF0,
+ 0x36, 0x05, 0x0B, 0x94, 0x60, 0x69, 0x85, 0x6B,
+ 0x89, 0x7E, 0x08, 0xC0, 0x07, 0x68, 0xF5, 0xEE,
+ 0x5D, 0xDC, 0xF7, 0x0B, 0x7C, 0xD6, 0xD0, 0xE0
+ },
+ {
+ 0x58, 0x60, 0x2E, 0xE7, 0x46, 0x8E, 0x6B, 0xC9,
+ 0xDF, 0x21, 0xBD, 0x51, 0xB2, 0x3C, 0x00, 0x5F,
+ 0x72, 0xD6, 0xCB, 0x01, 0x3F, 0x0A, 0x1B, 0x48,
+ 0xCB, 0xEC, 0x5E, 0xCA, 0x29, 0x92, 0x99, 0xF9,
+ 0x7F, 0x09, 0xF5, 0x4A, 0x9A, 0x01, 0x48, 0x3E,
+ 0xAE, 0xB3, 0x15, 0xA6, 0x47, 0x8B, 0xAD, 0x37,
+ 0xBA, 0x47, 0xCA, 0x13, 0x47, 0xC7, 0xC8, 0xFC,
+ 0x9E, 0x66, 0x95, 0x59, 0x2C, 0x91, 0xD7, 0x23
+ },
+ {
+ 0x27, 0xF5, 0xB7, 0x9E, 0xD2, 0x56, 0xB0, 0x50,
+ 0x99, 0x3D, 0x79, 0x34, 0x96, 0xED, 0xF4, 0x80,
+ 0x7C, 0x1D, 0x85, 0xA7, 0xB0, 0xA6, 0x7C, 0x9C,
+ 0x4F, 0xA9, 0x98, 0x60, 0x75, 0x0B, 0x0A, 0xE6,
+ 0x69, 0x89, 0x67, 0x0A, 0x8F, 0xFD, 0x78, 0x56,
+ 0xD7, 0xCE, 0x41, 0x15, 0x99, 0xE5, 0x8C, 0x4D,
+ 0x77, 0xB2, 0x32, 0xA6, 0x2B, 0xEF, 0x64, 0xD1,
+ 0x52, 0x75, 0xBE, 0x46, 0xA6, 0x82, 0x35, 0xFF
+ },
+ {
+ 0x39, 0x57, 0xA9, 0x76, 0xB9, 0xF1, 0x88, 0x7B,
+ 0xF0, 0x04, 0xA8, 0xDC, 0xA9, 0x42, 0xC9, 0x2D,
+ 0x2B, 0x37, 0xEA, 0x52, 0x60, 0x0F, 0x25, 0xE0,
+ 0xC9, 0xBC, 0x57, 0x07, 0xD0, 0x27, 0x9C, 0x00,
+ 0xC6, 0xE8, 0x5A, 0x83, 0x9B, 0x0D, 0x2D, 0x8E,
+ 0xB5, 0x9C, 0x51, 0xD9, 0x47, 0x88, 0xEB, 0xE6,
+ 0x24, 0x74, 0xA7, 0x91, 0xCA, 0xDF, 0x52, 0xCC,
+ 0xCF, 0x20, 0xF5, 0x07, 0x0B, 0x65, 0x73, 0xFC
+ },
+ {
+ 0xEA, 0xA2, 0x37, 0x6D, 0x55, 0x38, 0x0B, 0xF7,
+ 0x72, 0xEC, 0xCA, 0x9C, 0xB0, 0xAA, 0x46, 0x68,
+ 0xC9, 0x5C, 0x70, 0x71, 0x62, 0xFA, 0x86, 0xD5,
+ 0x18, 0xC8, 0xCE, 0x0C, 0xA9, 0xBF, 0x73, 0x62,
+ 0xB9, 0xF2, 0xA0, 0xAD, 0xC3, 0xFF, 0x59, 0x92,
+ 0x2D, 0xF9, 0x21, 0xB9, 0x45, 0x67, 0xE8, 0x1E,
+ 0x45, 0x2F, 0x6C, 0x1A, 0x07, 0xFC, 0x81, 0x7C,
+ 0xEB, 0xE9, 0x96, 0x04, 0xB3, 0x50, 0x5D, 0x38
+ },
+ {
+ 0xC1, 0xE2, 0xC7, 0x8B, 0x6B, 0x27, 0x34, 0xE2,
+ 0x48, 0x0E, 0xC5, 0x50, 0x43, 0x4C, 0xB5, 0xD6,
+ 0x13, 0x11, 0x1A, 0xDC, 0xC2, 0x1D, 0x47, 0x55,
+ 0x45, 0xC3, 0xB1, 0xB7, 0xE6, 0xFF, 0x12, 0x44,
+ 0x44, 0x76, 0xE5, 0xC0, 0x55, 0x13, 0x2E, 0x22,
+ 0x29, 0xDC, 0x0F, 0x80, 0x70, 0x44, 0xBB, 0x91,
+ 0x9B, 0x1A, 0x56, 0x62, 0xDD, 0x38, 0xA9, 0xEE,
+ 0x65, 0xE2, 0x43, 0xA3, 0x91, 0x1A, 0xED, 0x1A
+ },
+ {
+ 0x8A, 0xB4, 0x87, 0x13, 0x38, 0x9D, 0xD0, 0xFC,
+ 0xF9, 0xF9, 0x65, 0xD3, 0xCE, 0x66, 0xB1, 0xE5,
+ 0x59, 0xA1, 0xF8, 0xC5, 0x87, 0x41, 0xD6, 0x76,
+ 0x83, 0xCD, 0x97, 0x13, 0x54, 0xF4, 0x52, 0xE6,
+ 0x2D, 0x02, 0x07, 0xA6, 0x5E, 0x43, 0x6C, 0x5D,
+ 0x5D, 0x8F, 0x8E, 0xE7, 0x1C, 0x6A, 0xBF, 0xE5,
+ 0x0E, 0x66, 0x90, 0x04, 0xC3, 0x02, 0xB3, 0x1A,
+ 0x7E, 0xA8, 0x31, 0x1D, 0x4A, 0x91, 0x60, 0x51
+ },
+ {
+ 0x24, 0xCE, 0x0A, 0xDD, 0xAA, 0x4C, 0x65, 0x03,
+ 0x8B, 0xD1, 0xB1, 0xC0, 0xF1, 0x45, 0x2A, 0x0B,
+ 0x12, 0x87, 0x77, 0xAA, 0xBC, 0x94, 0xA2, 0x9D,
+ 0xF2, 0xFD, 0x6C, 0x7E, 0x2F, 0x85, 0xF8, 0xAB,
+ 0x9A, 0xC7, 0xEF, 0xF5, 0x16, 0xB0, 0xE0, 0xA8,
+ 0x25, 0xC8, 0x4A, 0x24, 0xCF, 0xE4, 0x92, 0xEA,
+ 0xAD, 0x0A, 0x63, 0x08, 0xE4, 0x6D, 0xD4, 0x2F,
+ 0xE8, 0x33, 0x3A, 0xB9, 0x71, 0xBB, 0x30, 0xCA
+ },
+ {
+ 0x51, 0x54, 0xF9, 0x29, 0xEE, 0x03, 0x04, 0x5B,
+ 0x6B, 0x0C, 0x00, 0x04, 0xFA, 0x77, 0x8E, 0xDE,
+ 0xE1, 0xD1, 0x39, 0x89, 0x32, 0x67, 0xCC, 0x84,
+ 0x82, 0x5A, 0xD7, 0xB3, 0x6C, 0x63, 0xDE, 0x32,
+ 0x79, 0x8E, 0x4A, 0x16, 0x6D, 0x24, 0x68, 0x65,
+ 0x61, 0x35, 0x4F, 0x63, 0xB0, 0x07, 0x09, 0xA1,
+ 0x36, 0x4B, 0x3C, 0x24, 0x1D, 0xE3, 0xFE, 0xBF,
+ 0x07, 0x54, 0x04, 0x58, 0x97, 0x46, 0x7C, 0xD4
+ },
+ {
+ 0xE7, 0x4E, 0x90, 0x79, 0x20, 0xFD, 0x87, 0xBD,
+ 0x5A, 0xD6, 0x36, 0xDD, 0x11, 0x08, 0x5E, 0x50,
+ 0xEE, 0x70, 0x45, 0x9C, 0x44, 0x3E, 0x1C, 0xE5,
+ 0x80, 0x9A, 0xF2, 0xBC, 0x2E, 0xBA, 0x39, 0xF9,
+ 0xE6, 0xD7, 0x12, 0x8E, 0x0E, 0x37, 0x12, 0xC3,
+ 0x16, 0xDA, 0x06, 0xF4, 0x70, 0x5D, 0x78, 0xA4,
+ 0x83, 0x8E, 0x28, 0x12, 0x1D, 0x43, 0x44, 0xA2,
+ 0xC7, 0x9C, 0x5E, 0x0D, 0xB3, 0x07, 0xA6, 0x77
+ },
+ {
+ 0xBF, 0x91, 0xA2, 0x23, 0x34, 0xBA, 0xC2, 0x0F,
+ 0x3F, 0xD8, 0x06, 0x63, 0xB3, 0xCD, 0x06, 0xC4,
+ 0xE8, 0x80, 0x2F, 0x30, 0xE6, 0xB5, 0x9F, 0x90,
+ 0xD3, 0x03, 0x5C, 0xC9, 0x79, 0x8A, 0x21, 0x7E,
+ 0xD5, 0xA3, 0x1A, 0xBB, 0xDA, 0x7F, 0xA6, 0x84,
+ 0x28, 0x27, 0xBD, 0xF2, 0xA7, 0xA1, 0xC2, 0x1F,
+ 0x6F, 0xCF, 0xCC, 0xBB, 0x54, 0xC6, 0xC5, 0x29,
+ 0x26, 0xF3, 0x2D, 0xA8, 0x16, 0x26, 0x9B, 0xE1
+ },
+ {
+ 0xD9, 0xD5, 0xC7, 0x4B, 0xE5, 0x12, 0x1B, 0x0B,
+ 0xD7, 0x42, 0xF2, 0x6B, 0xFF, 0xB8, 0xC8, 0x9F,
+ 0x89, 0x17, 0x1F, 0x3F, 0x93, 0x49, 0x13, 0x49,
+ 0x2B, 0x09, 0x03, 0xC2, 0x71, 0xBB, 0xE2, 0xB3,
+ 0x39, 0x5E, 0xF2, 0x59, 0x66, 0x9B, 0xEF, 0x43,
+ 0xB5, 0x7F, 0x7F, 0xCC, 0x30, 0x27, 0xDB, 0x01,
+ 0x82, 0x3F, 0x6B, 0xAE, 0xE6, 0x6E, 0x4F, 0x9F,
+ 0xEA, 0xD4, 0xD6, 0x72, 0x6C, 0x74, 0x1F, 0xCE
+ },
+ {
+ 0x50, 0xC8, 0xB8, 0xCF, 0x34, 0xCD, 0x87, 0x9F,
+ 0x80, 0xE2, 0xFA, 0xAB, 0x32, 0x30, 0xB0, 0xC0,
+ 0xE1, 0xCC, 0x3E, 0x9D, 0xCA, 0xDE, 0xB1, 0xB9,
+ 0xD9, 0x7A, 0xB9, 0x23, 0x41, 0x5D, 0xD9, 0xA1,
+ 0xFE, 0x38, 0xAD, 0xDD, 0x5C, 0x11, 0x75, 0x6C,
+ 0x67, 0x99, 0x0B, 0x25, 0x6E, 0x95, 0xAD, 0x6D,
+ 0x8F, 0x9F, 0xED, 0xCE, 0x10, 0xBF, 0x1C, 0x90,
+ 0x67, 0x9C, 0xDE, 0x0E, 0xCF, 0x1B, 0xE3, 0x47
+ },
+ {
+ 0x0A, 0x38, 0x6E, 0x7C, 0xD5, 0xDD, 0x9B, 0x77,
+ 0xA0, 0x35, 0xE0, 0x9F, 0xE6, 0xFE, 0xE2, 0xC8,
+ 0xCE, 0x61, 0xB5, 0x38, 0x3C, 0x87, 0xEA, 0x43,
+ 0x20, 0x50, 0x59, 0xC5, 0xE4, 0xCD, 0x4F, 0x44,
+ 0x08, 0x31, 0x9B, 0xB0, 0xA8, 0x23, 0x60, 0xF6,
+ 0xA5, 0x8E, 0x6C, 0x9C, 0xE3, 0xF4, 0x87, 0xC4,
+ 0x46, 0x06, 0x3B, 0xF8, 0x13, 0xBC, 0x6B, 0xA5,
+ 0x35, 0xE1, 0x7F, 0xC1, 0x82, 0x6C, 0xFC, 0x91
+ },
+ {
+ 0x1F, 0x14, 0x59, 0xCB, 0x6B, 0x61, 0xCB, 0xAC,
+ 0x5F, 0x0E, 0xFE, 0x8F, 0xC4, 0x87, 0x53, 0x8F,
+ 0x42, 0x54, 0x89, 0x87, 0xFC, 0xD5, 0x62, 0x21,
+ 0xCF, 0xA7, 0xBE, 0xB2, 0x25, 0x04, 0x76, 0x9E,
+ 0x79, 0x2C, 0x45, 0xAD, 0xFB, 0x1D, 0x6B, 0x3D,
+ 0x60, 0xD7, 0xB7, 0x49, 0xC8, 0xA7, 0x5B, 0x0B,
+ 0xDF, 0x14, 0xE8, 0xEA, 0x72, 0x1B, 0x95, 0xDC,
+ 0xA5, 0x38, 0xCA, 0x6E, 0x25, 0x71, 0x12, 0x09
+ },
+ {
+ 0xE5, 0x8B, 0x38, 0x36, 0xB7, 0xD8, 0xFE, 0xDB,
+ 0xB5, 0x0C, 0xA5, 0x72, 0x5C, 0x65, 0x71, 0xE7,
+ 0x4C, 0x07, 0x85, 0xE9, 0x78, 0x21, 0xDA, 0xB8,
+ 0xB6, 0x29, 0x8C, 0x10, 0xE4, 0xC0, 0x79, 0xD4,
+ 0xA6, 0xCD, 0xF2, 0x2F, 0x0F, 0xED, 0xB5, 0x50,
+ 0x32, 0x92, 0x5C, 0x16, 0x74, 0x81, 0x15, 0xF0,
+ 0x1A, 0x10, 0x5E, 0x77, 0xE0, 0x0C, 0xEE, 0x3D,
+ 0x07, 0x92, 0x4D, 0xC0, 0xD8, 0xF9, 0x06, 0x59
+ },
+ {
+ 0xB9, 0x29, 0xCC, 0x65, 0x05, 0xF0, 0x20, 0x15,
+ 0x86, 0x72, 0xDE, 0xDA, 0x56, 0xD0, 0xDB, 0x08,
+ 0x1A, 0x2E, 0xE3, 0x4C, 0x00, 0xC1, 0x10, 0x00,
+ 0x29, 0xBD, 0xF8, 0xEA, 0x98, 0x03, 0x4F, 0xA4,
+ 0xBF, 0x3E, 0x86, 0x55, 0xEC, 0x69, 0x7F, 0xE3,
+ 0x6F, 0x40, 0x55, 0x3C, 0x5B, 0xB4, 0x68, 0x01,
+ 0x64, 0x4A, 0x62, 0x7D, 0x33, 0x42, 0xF4, 0xFC,
+ 0x92, 0xB6, 0x1F, 0x03, 0x29, 0x0F, 0xB3, 0x81
+ },
+ {
+ 0x72, 0xD3, 0x53, 0x99, 0x4B, 0x49, 0xD3, 0xE0,
+ 0x31, 0x53, 0x92, 0x9A, 0x1E, 0x4D, 0x4F, 0x18,
+ 0x8E, 0xE5, 0x8A, 0xB9, 0xE7, 0x2E, 0xE8, 0xE5,
+ 0x12, 0xF2, 0x9B, 0xC7, 0x73, 0x91, 0x38, 0x19,
+ 0xCE, 0x05, 0x7D, 0xDD, 0x70, 0x02, 0xC0, 0x43,
+ 0x3E, 0xE0, 0xA1, 0x61, 0x14, 0xE3, 0xD1, 0x56,
+ 0xDD, 0x2C, 0x4A, 0x7E, 0x80, 0xEE, 0x53, 0x37,
+ 0x8B, 0x86, 0x70, 0xF2, 0x3E, 0x33, 0xEF, 0x56
+ },
+ {
+ 0xC7, 0x0E, 0xF9, 0xBF, 0xD7, 0x75, 0xD4, 0x08,
+ 0x17, 0x67, 0x37, 0xA0, 0x73, 0x6D, 0x68, 0x51,
+ 0x7C, 0xE1, 0xAA, 0xAD, 0x7E, 0x81, 0xA9, 0x3C,
+ 0x8C, 0x1E, 0xD9, 0x67, 0xEA, 0x21, 0x4F, 0x56,
+ 0xC8, 0xA3, 0x77, 0xB1, 0x76, 0x3E, 0x67, 0x66,
+ 0x15, 0xB6, 0x0F, 0x39, 0x88, 0x24, 0x1E, 0xAE,
+ 0x6E, 0xAB, 0x96, 0x85, 0xA5, 0x12, 0x49, 0x29,
+ 0xD2, 0x81, 0x88, 0xF2, 0x9E, 0xAB, 0x06, 0xF7
+ },
+ {
+ 0xC2, 0x30, 0xF0, 0x80, 0x26, 0x79, 0xCB, 0x33,
+ 0x82, 0x2E, 0xF8, 0xB3, 0xB2, 0x1B, 0xF7, 0xA9,
+ 0xA2, 0x89, 0x42, 0x09, 0x29, 0x01, 0xD7, 0xDA,
+ 0xC3, 0x76, 0x03, 0x00, 0x83, 0x10, 0x26, 0xCF,
+ 0x35, 0x4C, 0x92, 0x32, 0xDF, 0x3E, 0x08, 0x4D,
+ 0x99, 0x03, 0x13, 0x0C, 0x60, 0x1F, 0x63, 0xC1,
+ 0xF4, 0xA4, 0xA4, 0xB8, 0x10, 0x6E, 0x46, 0x8C,
+ 0xD4, 0x43, 0xBB, 0xE5, 0xA7, 0x34, 0xF4, 0x5F
+ },
+ {
+ 0x6F, 0x43, 0x09, 0x4C, 0xAF, 0xB5, 0xEB, 0xF1,
+ 0xF7, 0xA4, 0x93, 0x7E, 0xC5, 0x0F, 0x56, 0xA4,
+ 0xC9, 0xDA, 0x30, 0x3C, 0xBB, 0x55, 0xAC, 0x1F,
+ 0x27, 0xF1, 0xF1, 0x97, 0x6C, 0xD9, 0x6B, 0xED,
+ 0xA9, 0x46, 0x4F, 0x0E, 0x7B, 0x9C, 0x54, 0x62,
+ 0x0B, 0x8A, 0x9F, 0xBA, 0x98, 0x31, 0x64, 0xB8,
+ 0xBE, 0x35, 0x78, 0x42, 0x5A, 0x02, 0x4F, 0x5F,
+ 0xE1, 0x99, 0xC3, 0x63, 0x56, 0xB8, 0x89, 0x72
+ },
+ {
+ 0x37, 0x45, 0x27, 0x3F, 0x4C, 0x38, 0x22, 0x5D,
+ 0xB2, 0x33, 0x73, 0x81, 0x87, 0x1A, 0x0C, 0x6A,
+ 0xAF, 0xD3, 0xAF, 0x9B, 0x01, 0x8C, 0x88, 0xAA,
+ 0x02, 0x02, 0x58, 0x50, 0xA5, 0xDC, 0x3A, 0x42,
+ 0xA1, 0xA3, 0xE0, 0x3E, 0x56, 0xCB, 0xF1, 0xB0,
+ 0x87, 0x6D, 0x63, 0xA4, 0x41, 0xF1, 0xD2, 0x85,
+ 0x6A, 0x39, 0xB8, 0x80, 0x1E, 0xB5, 0xAF, 0x32,
+ 0x52, 0x01, 0xC4, 0x15, 0xD6, 0x5E, 0x97, 0xFE
+ },
+ {
+ 0xC5, 0x0C, 0x44, 0xCC, 0xA3, 0xEC, 0x3E, 0xDA,
+ 0xAE, 0x77, 0x9A, 0x7E, 0x17, 0x94, 0x50, 0xEB,
+ 0xDD, 0xA2, 0xF9, 0x70, 0x67, 0xC6, 0x90, 0xAA,
+ 0x6C, 0x5A, 0x4A, 0xC7, 0xC3, 0x01, 0x39, 0xBB,
+ 0x27, 0xC0, 0xDF, 0x4D, 0xB3, 0x22, 0x0E, 0x63,
+ 0xCB, 0x11, 0x0D, 0x64, 0xF3, 0x7F, 0xFE, 0x07,
+ 0x8D, 0xB7, 0x26, 0x53, 0xE2, 0xDA, 0xAC, 0xF9,
+ 0x3A, 0xE3, 0xF0, 0xA2, 0xD1, 0xA7, 0xEB, 0x2E
+ },
+ {
+ 0x8A, 0xEF, 0x26, 0x3E, 0x38, 0x5C, 0xBC, 0x61,
+ 0xE1, 0x9B, 0x28, 0x91, 0x42, 0x43, 0x26, 0x2A,
+ 0xF5, 0xAF, 0xE8, 0x72, 0x6A, 0xF3, 0xCE, 0x39,
+ 0xA7, 0x9C, 0x27, 0x02, 0x8C, 0xF3, 0xEC, 0xD3,
+ 0xF8, 0xD2, 0xDF, 0xD9, 0xCF, 0xC9, 0xAD, 0x91,
+ 0xB5, 0x8F, 0x6F, 0x20, 0x77, 0x8F, 0xD5, 0xF0,
+ 0x28, 0x94, 0xA3, 0xD9, 0x1C, 0x7D, 0x57, 0xD1,
+ 0xE4, 0xB8, 0x66, 0xA7, 0xF3, 0x64, 0xB6, 0xBE
+ },
+ {
+ 0x28, 0x69, 0x61, 0x41, 0xDE, 0x6E, 0x2D, 0x9B,
+ 0xCB, 0x32, 0x35, 0x57, 0x8A, 0x66, 0x16, 0x6C,
+ 0x14, 0x48, 0xD3, 0xE9, 0x05, 0xA1, 0xB4, 0x82,
+ 0xD4, 0x23, 0xBE, 0x4B, 0xC5, 0x36, 0x9B, 0xC8,
+ 0xC7, 0x4D, 0xAE, 0x0A, 0xCC, 0x9C, 0xC1, 0x23,
+ 0xE1, 0xD8, 0xDD, 0xCE, 0x9F, 0x97, 0x91, 0x7E,
+ 0x8C, 0x01, 0x9C, 0x55, 0x2D, 0xA3, 0x2D, 0x39,
+ 0xD2, 0x21, 0x9B, 0x9A, 0xBF, 0x0F, 0xA8, 0xC8
+ },
+ {
+ 0x2F, 0xB9, 0xEB, 0x20, 0x85, 0x83, 0x01, 0x81,
+ 0x90, 0x3A, 0x9D, 0xAF, 0xE3, 0xDB, 0x42, 0x8E,
+ 0xE1, 0x5B, 0xE7, 0x66, 0x22, 0x24, 0xEF, 0xD6,
+ 0x43, 0x37, 0x1F, 0xB2, 0x56, 0x46, 0xAE, 0xE7,
+ 0x16, 0xE5, 0x31, 0xEC, 0xA6, 0x9B, 0x2B, 0xDC,
+ 0x82, 0x33, 0xF1, 0xA8, 0x08, 0x1F, 0xA4, 0x3D,
+ 0xA1, 0x50, 0x03, 0x02, 0x97, 0x5A, 0x77, 0xF4,
+ 0x2F, 0xA5, 0x92, 0x13, 0x67, 0x10, 0xE9, 0xDC
+ },
+ {
+ 0x66, 0xF9, 0xA7, 0x14, 0x3F, 0x7A, 0x33, 0x14,
+ 0xA6, 0x69, 0xBF, 0x2E, 0x24, 0xBB, 0xB3, 0x50,
+ 0x14, 0x26, 0x1D, 0x63, 0x9F, 0x49, 0x5B, 0x6C,
+ 0x9C, 0x1F, 0x10, 0x4F, 0xE8, 0xE3, 0x20, 0xAC,
+ 0xA6, 0x0D, 0x45, 0x50, 0xD6, 0x9D, 0x52, 0xED,
+ 0xBD, 0x5A, 0x3C, 0xDE, 0xB4, 0x01, 0x4A, 0xE6,
+ 0x5B, 0x1D, 0x87, 0xAA, 0x77, 0x0B, 0x69, 0xAE,
+ 0x5C, 0x15, 0xF4, 0x33, 0x0B, 0x0B, 0x0A, 0xD8
+ },
+ {
+ 0xF4, 0xC4, 0xDD, 0x1D, 0x59, 0x4C, 0x35, 0x65,
+ 0xE3, 0xE2, 0x5C, 0xA4, 0x3D, 0xAD, 0x82, 0xF6,
+ 0x2A, 0xBE, 0xA4, 0x83, 0x5E, 0xD4, 0xCD, 0x81,
+ 0x1B, 0xCD, 0x97, 0x5E, 0x46, 0x27, 0x98, 0x28,
+ 0xD4, 0x4D, 0x4C, 0x62, 0xC3, 0x67, 0x9F, 0x1B,
+ 0x7F, 0x7B, 0x9D, 0xD4, 0x57, 0x1D, 0x7B, 0x49,
+ 0x55, 0x73, 0x47, 0xB8, 0xC5, 0x46, 0x0C, 0xBD,
+ 0xC1, 0xBE, 0xF6, 0x90, 0xFB, 0x2A, 0x08, 0xC0
+ },
+ {
+ 0x8F, 0x1D, 0xC9, 0x64, 0x9C, 0x3A, 0x84, 0x55,
+ 0x1F, 0x8F, 0x6E, 0x91, 0xCA, 0xC6, 0x82, 0x42,
+ 0xA4, 0x3B, 0x1F, 0x8F, 0x32, 0x8E, 0xE9, 0x22,
+ 0x80, 0x25, 0x73, 0x87, 0xFA, 0x75, 0x59, 0xAA,
+ 0x6D, 0xB1, 0x2E, 0x4A, 0xEA, 0xDC, 0x2D, 0x26,
+ 0x09, 0x91, 0x78, 0x74, 0x9C, 0x68, 0x64, 0xB3,
+ 0x57, 0xF3, 0xF8, 0x3B, 0x2F, 0xB3, 0xEF, 0xA8,
+ 0xD2, 0xA8, 0xDB, 0x05, 0x6B, 0xED, 0x6B, 0xCC
+ },
+ {
+ 0x31, 0x39, 0xC1, 0xA7, 0xF9, 0x7A, 0xFD, 0x16,
+ 0x75, 0xD4, 0x60, 0xEB, 0xBC, 0x07, 0xF2, 0x72,
+ 0x8A, 0xA1, 0x50, 0xDF, 0x84, 0x96, 0x24, 0x51,
+ 0x1E, 0xE0, 0x4B, 0x74, 0x3B, 0xA0, 0xA8, 0x33,
+ 0x09, 0x2F, 0x18, 0xC1, 0x2D, 0xC9, 0x1B, 0x4D,
+ 0xD2, 0x43, 0xF3, 0x33, 0x40, 0x2F, 0x59, 0xFE,
+ 0x28, 0xAB, 0xDB, 0xBB, 0xAE, 0x30, 0x1E, 0x7B,
+ 0x65, 0x9C, 0x7A, 0x26, 0xD5, 0xC0, 0xF9, 0x79
+ },
+ {
+ 0x06, 0xF9, 0x4A, 0x29, 0x96, 0x15, 0x8A, 0x81,
+ 0x9F, 0xE3, 0x4C, 0x40, 0xDE, 0x3C, 0xF0, 0x37,
+ 0x9F, 0xD9, 0xFB, 0x85, 0xB3, 0xE3, 0x63, 0xBA,
+ 0x39, 0x26, 0xA0, 0xE7, 0xD9, 0x60, 0xE3, 0xF4,
+ 0xC2, 0xE0, 0xC7, 0x0C, 0x7C, 0xE0, 0xCC, 0xB2,
+ 0xA6, 0x4F, 0xC2, 0x98, 0x69, 0xF6, 0xE7, 0xAB,
+ 0x12, 0xBD, 0x4D, 0x3F, 0x14, 0xFC, 0xE9, 0x43,
+ 0x27, 0x90, 0x27, 0xE7, 0x85, 0xFB, 0x5C, 0x29
+ },
+ {
+ 0xC2, 0x9C, 0x39, 0x9E, 0xF3, 0xEE, 0xE8, 0x96,
+ 0x1E, 0x87, 0x56, 0x5C, 0x1C, 0xE2, 0x63, 0x92,
+ 0x5F, 0xC3, 0xD0, 0xCE, 0x26, 0x7D, 0x13, 0xE4,
+ 0x8D, 0xD9, 0xE7, 0x32, 0xEE, 0x67, 0xB0, 0xF6,
+ 0x9F, 0xAD, 0x56, 0x40, 0x1B, 0x0F, 0x10, 0xFC,
+ 0xAA, 0xC1, 0x19, 0x20, 0x10, 0x46, 0xCC, 0xA2,
+ 0x8C, 0x5B, 0x14, 0xAB, 0xDE, 0xA3, 0x21, 0x2A,
+ 0xE6, 0x55, 0x62, 0xF7, 0xF1, 0x38, 0xDB, 0x3D
+ },
+ {
+ 0x4C, 0xEC, 0x4C, 0x9D, 0xF5, 0x2E, 0xEF, 0x05,
+ 0xC3, 0xF6, 0xFA, 0xAA, 0x97, 0x91, 0xBC, 0x74,
+ 0x45, 0x93, 0x71, 0x83, 0x22, 0x4E, 0xCC, 0x37,
+ 0xA1, 0xE5, 0x8D, 0x01, 0x32, 0xD3, 0x56, 0x17,
+ 0x53, 0x1D, 0x7E, 0x79, 0x5F, 0x52, 0xAF, 0x7B,
+ 0x1E, 0xB9, 0xD1, 0x47, 0xDE, 0x12, 0x92, 0xD3,
+ 0x45, 0xFE, 0x34, 0x18, 0x23, 0xF8, 0xE6, 0xBC,
+ 0x1E, 0x5B, 0xAD, 0xCA, 0x5C, 0x65, 0x61, 0x08
+ },
+ {
+ 0x89, 0x8B, 0xFB, 0xAE, 0x93, 0xB3, 0xE1, 0x8D,
+ 0x00, 0x69, 0x7E, 0xAB, 0x7D, 0x97, 0x04, 0xFA,
+ 0x36, 0xEC, 0x33, 0x9D, 0x07, 0x61, 0x31, 0xCE,
+ 0xFD, 0xF3, 0x0E, 0xDB, 0xE8, 0xD9, 0xCC, 0x81,
+ 0xC3, 0xA8, 0x0B, 0x12, 0x96, 0x59, 0xB1, 0x63,
+ 0xA3, 0x23, 0xBA, 0xB9, 0x79, 0x3D, 0x4F, 0xEE,
+ 0xD9, 0x2D, 0x54, 0xDA, 0xE9, 0x66, 0xC7, 0x75,
+ 0x29, 0x76, 0x4A, 0x09, 0xBE, 0x88, 0xDB, 0x45
+ },
+ {
+ 0xEE, 0x9B, 0xD0, 0x46, 0x9D, 0x3A, 0xAF, 0x4F,
+ 0x14, 0x03, 0x5B, 0xE4, 0x8A, 0x2C, 0x3B, 0x84,
+ 0xD9, 0xB4, 0xB1, 0xFF, 0xF1, 0xD9, 0x45, 0xE1,
+ 0xF1, 0xC1, 0xD3, 0x89, 0x80, 0xA9, 0x51, 0xBE,
+ 0x19, 0x7B, 0x25, 0xFE, 0x22, 0xC7, 0x31, 0xF2,
+ 0x0A, 0xEA, 0xCC, 0x93, 0x0B, 0xA9, 0xC4, 0xA1,
+ 0xF4, 0x76, 0x22, 0x27, 0x61, 0x7A, 0xD3, 0x50,
+ 0xFD, 0xAB, 0xB4, 0xE8, 0x02, 0x73, 0xA0, 0xF4
+ },
+ {
+ 0x3D, 0x4D, 0x31, 0x13, 0x30, 0x05, 0x81, 0xCD,
+ 0x96, 0xAC, 0xBF, 0x09, 0x1C, 0x3D, 0x0F, 0x3C,
+ 0x31, 0x01, 0x38, 0xCD, 0x69, 0x79, 0xE6, 0x02,
+ 0x6C, 0xDE, 0x62, 0x3E, 0x2D, 0xD1, 0xB2, 0x4D,
+ 0x4A, 0x86, 0x38, 0xBE, 0xD1, 0x07, 0x33, 0x44,
+ 0x78, 0x3A, 0xD0, 0x64, 0x9C, 0xC6, 0x30, 0x5C,
+ 0xCE, 0xC0, 0x4B, 0xEB, 0x49, 0xF3, 0x1C, 0x63,
+ 0x30, 0x88, 0xA9, 0x9B, 0x65, 0x13, 0x02, 0x67
+ },
+ {
+ 0x95, 0xC0, 0x59, 0x1A, 0xD9, 0x1F, 0x92, 0x1A,
+ 0xC7, 0xBE, 0x6D, 0x9C, 0xE3, 0x7E, 0x06, 0x63,
+ 0xED, 0x80, 0x11, 0xC1, 0xCF, 0xD6, 0xD0, 0x16,
+ 0x2A, 0x55, 0x72, 0xE9, 0x43, 0x68, 0xBA, 0xC0,
+ 0x20, 0x24, 0x48, 0x5E, 0x6A, 0x39, 0x85, 0x4A,
+ 0xA4, 0x6F, 0xE3, 0x8E, 0x97, 0xD6, 0xC6, 0xB1,
+ 0x94, 0x7C, 0xD2, 0x72, 0xD8, 0x6B, 0x06, 0xBB,
+ 0x5B, 0x2F, 0x78, 0xB9, 0xB6, 0x8D, 0x55, 0x9D
+ },
+ {
+ 0x22, 0x7B, 0x79, 0xDE, 0xD3, 0x68, 0x15, 0x3B,
+ 0xF4, 0x6C, 0x0A, 0x3C, 0xA9, 0x78, 0xBF, 0xDB,
+ 0xEF, 0x31, 0xF3, 0x02, 0x4A, 0x56, 0x65, 0x84,
+ 0x24, 0x68, 0x49, 0x0B, 0x0F, 0xF7, 0x48, 0xAE,
+ 0x04, 0xE7, 0x83, 0x2E, 0xD4, 0xC9, 0xF4, 0x9D,
+ 0xE9, 0xB1, 0x70, 0x67, 0x09, 0xD6, 0x23, 0xE5,
+ 0xC8, 0xC1, 0x5E, 0x3C, 0xAE, 0xCA, 0xE8, 0xD5,
+ 0xE4, 0x33, 0x43, 0x0F, 0xF7, 0x2F, 0x20, 0xEB
+ },
+ {
+ 0x5D, 0x34, 0xF3, 0x95, 0x2F, 0x01, 0x05, 0xEE,
+ 0xF8, 0x8A, 0xE8, 0xB6, 0x4C, 0x6C, 0xE9, 0x5E,
+ 0xBF, 0xAD, 0xE0, 0xE0, 0x2C, 0x69, 0xB0, 0x87,
+ 0x62, 0xA8, 0x71, 0x2D, 0x2E, 0x49, 0x11, 0xAD,
+ 0x3F, 0x94, 0x1F, 0xC4, 0x03, 0x4D, 0xC9, 0xB2,
+ 0xE4, 0x79, 0xFD, 0xBC, 0xD2, 0x79, 0xB9, 0x02,
+ 0xFA, 0xF5, 0xD8, 0x38, 0xBB, 0x2E, 0x0C, 0x64,
+ 0x95, 0xD3, 0x72, 0xB5, 0xB7, 0x02, 0x98, 0x13
+ },
+ {
+ 0x7F, 0x93, 0x9B, 0xF8, 0x35, 0x3A, 0xBC, 0xE4,
+ 0x9E, 0x77, 0xF1, 0x4F, 0x37, 0x50, 0xAF, 0x20,
+ 0xB7, 0xB0, 0x39, 0x02, 0xE1, 0xA1, 0xE7, 0xFB,
+ 0x6A, 0xAF, 0x76, 0xD0, 0x25, 0x9C, 0xD4, 0x01,
+ 0xA8, 0x31, 0x90, 0xF1, 0x56, 0x40, 0xE7, 0x4F,
+ 0x3E, 0x6C, 0x5A, 0x90, 0xE8, 0x39, 0xC7, 0x82,
+ 0x1F, 0x64, 0x74, 0x75, 0x7F, 0x75, 0xC7, 0xBF,
+ 0x90, 0x02, 0x08, 0x4D, 0xDC, 0x7A, 0x62, 0xDC
+ },
+ {
+ 0x06, 0x2B, 0x61, 0xA2, 0xF9, 0xA3, 0x3A, 0x71,
+ 0xD7, 0xD0, 0xA0, 0x61, 0x19, 0x64, 0x4C, 0x70,
+ 0xB0, 0x71, 0x6A, 0x50, 0x4D, 0xE7, 0xE5, 0xE1,
+ 0xBE, 0x49, 0xBD, 0x7B, 0x86, 0xE7, 0xED, 0x68,
+ 0x17, 0x71, 0x4F, 0x9F, 0x0F, 0xC3, 0x13, 0xD0,
+ 0x61, 0x29, 0x59, 0x7E, 0x9A, 0x22, 0x35, 0xEC,
+ 0x85, 0x21, 0xDE, 0x36, 0xF7, 0x29, 0x0A, 0x90,
+ 0xCC, 0xFC, 0x1F, 0xFA, 0x6D, 0x0A, 0xEE, 0x29
+ },
+ {
+ 0xF2, 0x9E, 0x01, 0xEE, 0xAE, 0x64, 0x31, 0x1E,
+ 0xB7, 0xF1, 0xC6, 0x42, 0x2F, 0x94, 0x6B, 0xF7,
+ 0xBE, 0xA3, 0x63, 0x79, 0x52, 0x3E, 0x7B, 0x2B,
+ 0xBA, 0xBA, 0x7D, 0x1D, 0x34, 0xA2, 0x2D, 0x5E,
+ 0xA5, 0xF1, 0xC5, 0xA0, 0x9D, 0x5C, 0xE1, 0xFE,
+ 0x68, 0x2C, 0xCE, 0xD9, 0xA4, 0x79, 0x8D, 0x1A,
+ 0x05, 0xB4, 0x6C, 0xD7, 0x2D, 0xFF, 0x5C, 0x1B,
+ 0x35, 0x54, 0x40, 0xB2, 0xA2, 0xD4, 0x76, 0xBC
+ },
+ {
+ 0xEC, 0x38, 0xCD, 0x3B, 0xBA, 0xB3, 0xEF, 0x35,
+ 0xD7, 0xCB, 0x6D, 0x5C, 0x91, 0x42, 0x98, 0x35,
+ 0x1D, 0x8A, 0x9D, 0xC9, 0x7F, 0xCE, 0xE0, 0x51,
+ 0xA8, 0xA0, 0x2F, 0x58, 0xE3, 0xED, 0x61, 0x84,
+ 0xD0, 0xB7, 0x81, 0x0A, 0x56, 0x15, 0x41, 0x1A,
+ 0xB1, 0xB9, 0x52, 0x09, 0xC3, 0xC8, 0x10, 0x11,
+ 0x4F, 0xDE, 0xB2, 0x24, 0x52, 0x08, 0x4E, 0x77,
+ 0xF3, 0xF8, 0x47, 0xC6, 0xDB, 0xAA, 0xFE, 0x16
+ },
+ {
+ 0xC2, 0xAE, 0xF5, 0xE0, 0xCA, 0x43, 0xE8, 0x26,
+ 0x41, 0x56, 0x5B, 0x8C, 0xB9, 0x43, 0xAA, 0x8B,
+ 0xA5, 0x35, 0x50, 0xCA, 0xEF, 0x79, 0x3B, 0x65,
+ 0x32, 0xFA, 0xFA, 0xD9, 0x4B, 0x81, 0x60, 0x82,
+ 0xF0, 0x11, 0x3A, 0x3E, 0xA2, 0xF6, 0x36, 0x08,
+ 0xAB, 0x40, 0x43, 0x7E, 0xCC, 0x0F, 0x02, 0x29,
+ 0xCB, 0x8F, 0xA2, 0x24, 0xDC, 0xF1, 0xC4, 0x78,
+ 0xA6, 0x7D, 0x9B, 0x64, 0x16, 0x2B, 0x92, 0xD1
+ },
+ {
+ 0x15, 0xF5, 0x34, 0xEF, 0xFF, 0x71, 0x05, 0xCD,
+ 0x1C, 0x25, 0x4D, 0x07, 0x4E, 0x27, 0xD5, 0x89,
+ 0x8B, 0x89, 0x31, 0x3B, 0x7D, 0x36, 0x6D, 0xC2,
+ 0xD7, 0xD8, 0x71, 0x13, 0xFA, 0x7D, 0x53, 0xAA,
+ 0xE1, 0x3F, 0x6D, 0xBA, 0x48, 0x7A, 0xD8, 0x10,
+ 0x3D, 0x5E, 0x85, 0x4C, 0x91, 0xFD, 0xB6, 0xE1,
+ 0xE7, 0x4B, 0x2E, 0xF6, 0xD1, 0x43, 0x17, 0x69,
+ 0xC3, 0x07, 0x67, 0xDD, 0xE0, 0x67, 0xA3, 0x5C
+ },
+ {
+ 0x89, 0xAC, 0xBC, 0xA0, 0xB1, 0x69, 0x89, 0x7A,
+ 0x0A, 0x27, 0x14, 0xC2, 0xDF, 0x8C, 0x95, 0xB5,
+ 0xB7, 0x9C, 0xB6, 0x93, 0x90, 0x14, 0x2B, 0x7D,
+ 0x60, 0x18, 0xBB, 0x3E, 0x30, 0x76, 0xB0, 0x99,
+ 0xB7, 0x9A, 0x96, 0x41, 0x52, 0xA9, 0xD9, 0x12,
+ 0xB1, 0xB8, 0x64, 0x12, 0xB7, 0xE3, 0x72, 0xE9,
+ 0xCE, 0xCA, 0xD7, 0xF2, 0x5D, 0x4C, 0xBA, 0xB8,
+ 0xA3, 0x17, 0xBE, 0x36, 0x49, 0x2A, 0x67, 0xD7
+ },
+ {
+ 0xE3, 0xC0, 0x73, 0x91, 0x90, 0xED, 0x84, 0x9C,
+ 0x9C, 0x96, 0x2F, 0xD9, 0xDB, 0xB5, 0x5E, 0x20,
+ 0x7E, 0x62, 0x4F, 0xCA, 0xC1, 0xEB, 0x41, 0x76,
+ 0x91, 0x51, 0x54, 0x99, 0xEE, 0xA8, 0xD8, 0x26,
+ 0x7B, 0x7E, 0x8F, 0x12, 0x87, 0xA6, 0x36, 0x33,
+ 0xAF, 0x50, 0x11, 0xFD, 0xE8, 0xC4, 0xDD, 0xF5,
+ 0x5B, 0xFD, 0xF7, 0x22, 0xED, 0xF8, 0x88, 0x31,
+ 0x41, 0x4F, 0x2C, 0xFA, 0xED, 0x59, 0xCB, 0x9A
+ },
+ {
+ 0x8D, 0x6C, 0xF8, 0x7C, 0x08, 0x38, 0x0D, 0x2D,
+ 0x15, 0x06, 0xEE, 0xE4, 0x6F, 0xD4, 0x22, 0x2D,
+ 0x21, 0xD8, 0xC0, 0x4E, 0x58, 0x5F, 0xBF, 0xD0,
+ 0x82, 0x69, 0xC9, 0x8F, 0x70, 0x28, 0x33, 0xA1,
+ 0x56, 0x32, 0x6A, 0x07, 0x24, 0x65, 0x64, 0x00,
+ 0xEE, 0x09, 0x35, 0x1D, 0x57, 0xB4, 0x40, 0x17,
+ 0x5E, 0x2A, 0x5D, 0xE9, 0x3C, 0xC5, 0xF8, 0x0D,
+ 0xB6, 0xDA, 0xF8, 0x35, 0x76, 0xCF, 0x75, 0xFA
+ },
+ {
+ 0xDA, 0x24, 0xBE, 0xDE, 0x38, 0x36, 0x66, 0xD5,
+ 0x63, 0xEE, 0xED, 0x37, 0xF6, 0x31, 0x9B, 0xAF,
+ 0x20, 0xD5, 0xC7, 0x5D, 0x16, 0x35, 0xA6, 0xBA,
+ 0x5E, 0xF4, 0xCF, 0xA1, 0xAC, 0x95, 0x48, 0x7E,
+ 0x96, 0xF8, 0xC0, 0x8A, 0xF6, 0x00, 0xAA, 0xB8,
+ 0x7C, 0x98, 0x6E, 0xBA, 0xD4, 0x9F, 0xC7, 0x0A,
+ 0x58, 0xB4, 0x89, 0x0B, 0x9C, 0x87, 0x6E, 0x09,
+ 0x10, 0x16, 0xDA, 0xF4, 0x9E, 0x1D, 0x32, 0x2E
+ },
+ {
+ 0xF9, 0xD1, 0xD1, 0xB1, 0xE8, 0x7E, 0xA7, 0xAE,
+ 0x75, 0x3A, 0x02, 0x97, 0x50, 0xCC, 0x1C, 0xF3,
+ 0xD0, 0x15, 0x7D, 0x41, 0x80, 0x5E, 0x24, 0x5C,
+ 0x56, 0x17, 0xBB, 0x93, 0x4E, 0x73, 0x2F, 0x0A,
+ 0xE3, 0x18, 0x0B, 0x78, 0xE0, 0x5B, 0xFE, 0x76,
+ 0xC7, 0xC3, 0x05, 0x1E, 0x3E, 0x3A, 0xC7, 0x8B,
+ 0x9B, 0x50, 0xC0, 0x51, 0x42, 0x65, 0x7E, 0x1E,
+ 0x03, 0x21, 0x5D, 0x6E, 0xC7, 0xBF, 0xD0, 0xFC
+ },
+ {
+ 0x11, 0xB7, 0xBC, 0x16, 0x68, 0x03, 0x20, 0x48,
+ 0xAA, 0x43, 0x34, 0x3D, 0xE4, 0x76, 0x39, 0x5E,
+ 0x81, 0x4B, 0xBB, 0xC2, 0x23, 0x67, 0x8D, 0xB9,
+ 0x51, 0xA1, 0xB0, 0x3A, 0x02, 0x1E, 0xFA, 0xC9,
+ 0x48, 0xCF, 0xBE, 0x21, 0x5F, 0x97, 0xFE, 0x9A,
+ 0x72, 0xA2, 0xF6, 0xBC, 0x03, 0x9E, 0x39, 0x56,
+ 0xBF, 0xA4, 0x17, 0xC1, 0xA9, 0xF1, 0x0D, 0x6D,
+ 0x7B, 0xA5, 0xD3, 0xD3, 0x2F, 0xF3, 0x23, 0xE5
+ },
+ {
+ 0xB8, 0xD9, 0x00, 0x0E, 0x4F, 0xC2, 0xB0, 0x66,
+ 0xED, 0xB9, 0x1A, 0xFE, 0xE8, 0xE7, 0xEB, 0x0F,
+ 0x24, 0xE3, 0xA2, 0x01, 0xDB, 0x8B, 0x67, 0x93,
+ 0xC0, 0x60, 0x85, 0x81, 0xE6, 0x28, 0xED, 0x0B,
+ 0xCC, 0x4E, 0x5A, 0xA6, 0x78, 0x79, 0x92, 0xA4,
+ 0xBC, 0xC4, 0x4E, 0x28, 0x80, 0x93, 0xE6, 0x3E,
+ 0xE8, 0x3A, 0xBD, 0x0B, 0xC3, 0xEC, 0x6D, 0x09,
+ 0x34, 0xA6, 0x74, 0xA4, 0xDA, 0x13, 0x83, 0x8A
+ },
+ {
+ 0xCE, 0x32, 0x5E, 0x29, 0x4F, 0x9B, 0x67, 0x19,
+ 0xD6, 0xB6, 0x12, 0x78, 0x27, 0x6A, 0xE0, 0x6A,
+ 0x25, 0x64, 0xC0, 0x3B, 0xB0, 0xB7, 0x83, 0xFA,
+ 0xFE, 0x78, 0x5B, 0xDF, 0x89, 0xC7, 0xD5, 0xAC,
+ 0xD8, 0x3E, 0x78, 0x75, 0x6D, 0x30, 0x1B, 0x44,
+ 0x56, 0x99, 0x02, 0x4E, 0xAE, 0xB7, 0x7B, 0x54,
+ 0xD4, 0x77, 0x33, 0x6E, 0xC2, 0xA4, 0xF3, 0x32,
+ 0xF2, 0xB3, 0xF8, 0x87, 0x65, 0xDD, 0xB0, 0xC3
+ },
+ {
+ 0x29, 0xAC, 0xC3, 0x0E, 0x96, 0x03, 0xAE, 0x2F,
+ 0xCC, 0xF9, 0x0B, 0xF9, 0x7E, 0x6C, 0xC4, 0x63,
+ 0xEB, 0xE2, 0x8C, 0x1B, 0x2F, 0x9B, 0x4B, 0x76,
+ 0x5E, 0x70, 0x53, 0x7C, 0x25, 0xC7, 0x02, 0xA2,
+ 0x9D, 0xCB, 0xFB, 0xF1, 0x4C, 0x99, 0xC5, 0x43,
+ 0x45, 0xBA, 0x2B, 0x51, 0xF1, 0x7B, 0x77, 0xB5,
+ 0xF1, 0x5D, 0xB9, 0x2B, 0xBA, 0xD8, 0xFA, 0x95,
+ 0xC4, 0x71, 0xF5, 0xD0, 0x70, 0xA1, 0x37, 0xCC
+ },
+ {
+ 0x33, 0x79, 0xCB, 0xAA, 0xE5, 0x62, 0xA8, 0x7B,
+ 0x4C, 0x04, 0x25, 0x55, 0x0F, 0xFD, 0xD6, 0xBF,
+ 0xE1, 0x20, 0x3F, 0x0D, 0x66, 0x6C, 0xC7, 0xEA,
+ 0x09, 0x5B, 0xE4, 0x07, 0xA5, 0xDF, 0xE6, 0x1E,
+ 0xE9, 0x14, 0x41, 0xCD, 0x51, 0x54, 0xB3, 0xE5,
+ 0x3B, 0x4F, 0x5F, 0xB3, 0x1A, 0xD4, 0xC7, 0xA9,
+ 0xAD, 0x5C, 0x7A, 0xF4, 0xAE, 0x67, 0x9A, 0xA5,
+ 0x1A, 0x54, 0x00, 0x3A, 0x54, 0xCA, 0x6B, 0x2D
+ },
+ {
+ 0x30, 0x95, 0xA3, 0x49, 0xD2, 0x45, 0x70, 0x8C,
+ 0x7C, 0xF5, 0x50, 0x11, 0x87, 0x03, 0xD7, 0x30,
+ 0x2C, 0x27, 0xB6, 0x0A, 0xF5, 0xD4, 0xE6, 0x7F,
+ 0xC9, 0x78, 0xF8, 0xA4, 0xE6, 0x09, 0x53, 0xC7,
+ 0xA0, 0x4F, 0x92, 0xFC, 0xF4, 0x1A, 0xEE, 0x64,
+ 0x32, 0x1C, 0xCB, 0x70, 0x7A, 0x89, 0x58, 0x51,
+ 0x55, 0x2B, 0x1E, 0x37, 0xB0, 0x0B, 0xC5, 0xE6,
+ 0xB7, 0x2F, 0xA5, 0xBC, 0xEF, 0x9E, 0x3F, 0xFF
+ },
+ {
+ 0x07, 0x26, 0x2D, 0x73, 0x8B, 0x09, 0x32, 0x1F,
+ 0x4D, 0xBC, 0xCE, 0xC4, 0xBB, 0x26, 0xF4, 0x8C,
+ 0xB0, 0xF0, 0xED, 0x24, 0x6C, 0xE0, 0xB3, 0x1B,
+ 0x9A, 0x6E, 0x7B, 0xC6, 0x83, 0x04, 0x9F, 0x1F,
+ 0x3E, 0x55, 0x45, 0xF2, 0x8C, 0xE9, 0x32, 0xDD,
+ 0x98, 0x5C, 0x5A, 0xB0, 0xF4, 0x3B, 0xD6, 0xDE,
+ 0x07, 0x70, 0x56, 0x0A, 0xF3, 0x29, 0x06, 0x5E,
+ 0xD2, 0xE4, 0x9D, 0x34, 0x62, 0x4C, 0x2C, 0xBB
+ },
+ {
+ 0xB6, 0x40, 0x5E, 0xCA, 0x8E, 0xE3, 0x31, 0x6C,
+ 0x87, 0x06, 0x1C, 0xC6, 0xEC, 0x18, 0xDB, 0xA5,
+ 0x3E, 0x6C, 0x25, 0x0C, 0x63, 0xBA, 0x1F, 0x3B,
+ 0xAE, 0x9E, 0x55, 0xDD, 0x34, 0x98, 0x03, 0x6A,
+ 0xF0, 0x8C, 0xD2, 0x72, 0xAA, 0x24, 0xD7, 0x13,
+ 0xC6, 0x02, 0x0D, 0x77, 0xAB, 0x2F, 0x39, 0x19,
+ 0xAF, 0x1A, 0x32, 0xF3, 0x07, 0x42, 0x06, 0x18,
+ 0xAB, 0x97, 0xE7, 0x39, 0x53, 0x99, 0x4F, 0xB4
+ },
+ {
+ 0x7E, 0xE6, 0x82, 0xF6, 0x31, 0x48, 0xEE, 0x45,
+ 0xF6, 0xE5, 0x31, 0x5D, 0xA8, 0x1E, 0x5C, 0x6E,
+ 0x55, 0x7C, 0x2C, 0x34, 0x64, 0x1F, 0xC5, 0x09,
+ 0xC7, 0xA5, 0x70, 0x10, 0x88, 0xC3, 0x8A, 0x74,
+ 0x75, 0x61, 0x68, 0xE2, 0xCD, 0x8D, 0x35, 0x1E,
+ 0x88, 0xFD, 0x1A, 0x45, 0x1F, 0x36, 0x0A, 0x01,
+ 0xF5, 0xB2, 0x58, 0x0F, 0x9B, 0x5A, 0x2E, 0x8C,
+ 0xFC, 0x13, 0x8F, 0x3D, 0xD5, 0x9A, 0x3F, 0xFC
+ },
+ {
+ 0x1D, 0x26, 0x3C, 0x17, 0x9D, 0x6B, 0x26, 0x8F,
+ 0x6F, 0xA0, 0x16, 0xF3, 0xA4, 0xF2, 0x9E, 0x94,
+ 0x38, 0x91, 0x12, 0x5E, 0xD8, 0x59, 0x3C, 0x81,
+ 0x25, 0x60, 0x59, 0xF5, 0xA7, 0xB4, 0x4A, 0xF2,
+ 0xDC, 0xB2, 0x03, 0x0D, 0x17, 0x5C, 0x00, 0xE6,
+ 0x2E, 0xCA, 0xF7, 0xEE, 0x96, 0x68, 0x2A, 0xA0,
+ 0x7A, 0xB2, 0x0A, 0x61, 0x10, 0x24, 0xA2, 0x85,
+ 0x32, 0xB1, 0xC2, 0x5B, 0x86, 0x65, 0x79, 0x02
+ },
+ {
+ 0x10, 0x6D, 0x13, 0x2C, 0xBD, 0xB4, 0xCD, 0x25,
+ 0x97, 0x81, 0x28, 0x46, 0xE2, 0xBC, 0x1B, 0xF7,
+ 0x32, 0xFE, 0xC5, 0xF0, 0xA5, 0xF6, 0x5D, 0xBB,
+ 0x39, 0xEC, 0x4E, 0x6D, 0xC6, 0x4A, 0xB2, 0xCE,
+ 0x6D, 0x24, 0x63, 0x0D, 0x0F, 0x15, 0xA8, 0x05,
+ 0xC3, 0x54, 0x00, 0x25, 0xD8, 0x4A, 0xFA, 0x98,
+ 0xE3, 0x67, 0x03, 0xC3, 0xDB, 0xEE, 0x71, 0x3E,
+ 0x72, 0xDD, 0xE8, 0x46, 0x5B, 0xC1, 0xBE, 0x7E
+ },
+ {
+ 0x0E, 0x79, 0x96, 0x82, 0x26, 0x65, 0x06, 0x67,
+ 0xA8, 0xD8, 0x62, 0xEA, 0x8D, 0xA4, 0x89, 0x1A,
+ 0xF5, 0x6A, 0x4E, 0x3A, 0x8B, 0x6D, 0x17, 0x50,
+ 0xE3, 0x94, 0xF0, 0xDE, 0xA7, 0x6D, 0x64, 0x0D,
+ 0x85, 0x07, 0x7B, 0xCE, 0xC2, 0xCC, 0x86, 0x88,
+ 0x6E, 0x50, 0x67, 0x51, 0xB4, 0xF6, 0xA5, 0x83,
+ 0x8F, 0x7F, 0x0B, 0x5F, 0xEF, 0x76, 0x5D, 0x9D,
+ 0xC9, 0x0D, 0xCD, 0xCB, 0xAF, 0x07, 0x9F, 0x08
+ },
+ {
+ 0x52, 0x11, 0x56, 0xA8, 0x2A, 0xB0, 0xC4, 0xE5,
+ 0x66, 0xE5, 0x84, 0x4D, 0x5E, 0x31, 0xAD, 0x9A,
+ 0xAF, 0x14, 0x4B, 0xBD, 0x5A, 0x46, 0x4F, 0xDC,
+ 0xA3, 0x4D, 0xBD, 0x57, 0x17, 0xE8, 0xFF, 0x71,
+ 0x1D, 0x3F, 0xFE, 0xBB, 0xFA, 0x08, 0x5D, 0x67,
+ 0xFE, 0x99, 0x6A, 0x34, 0xF6, 0xD3, 0xE4, 0xE6,
+ 0x0B, 0x13, 0x96, 0xBF, 0x4B, 0x16, 0x10, 0xC2,
+ 0x63, 0xBD, 0xBB, 0x83, 0x4D, 0x56, 0x08, 0x16
+ },
+ {
+ 0x1A, 0xBA, 0x88, 0xBE, 0xFC, 0x55, 0xBC, 0x25,
+ 0xEF, 0xBC, 0xE0, 0x2D, 0xB8, 0xB9, 0x93, 0x3E,
+ 0x46, 0xF5, 0x76, 0x61, 0xBA, 0xEA, 0xBE, 0xB2,
+ 0x1C, 0xC2, 0x57, 0x4D, 0x2A, 0x51, 0x8A, 0x3C,
+ 0xBA, 0x5D, 0xC5, 0xA3, 0x8E, 0x49, 0x71, 0x34,
+ 0x40, 0xB2, 0x5F, 0x9C, 0x74, 0x4E, 0x75, 0xF6,
+ 0xB8, 0x5C, 0x9D, 0x8F, 0x46, 0x81, 0xF6, 0x76,
+ 0x16, 0x0F, 0x61, 0x05, 0x35, 0x7B, 0x84, 0x06
+ },
+ {
+ 0x5A, 0x99, 0x49, 0xFC, 0xB2, 0xC4, 0x73, 0xCD,
+ 0xA9, 0x68, 0xAC, 0x1B, 0x5D, 0x08, 0x56, 0x6D,
+ 0xC2, 0xD8, 0x16, 0xD9, 0x60, 0xF5, 0x7E, 0x63,
+ 0xB8, 0x98, 0xFA, 0x70, 0x1C, 0xF8, 0xEB, 0xD3,
+ 0xF5, 0x9B, 0x12, 0x4D, 0x95, 0xBF, 0xBB, 0xED,
+ 0xC5, 0xF1, 0xCF, 0x0E, 0x17, 0xD5, 0xEA, 0xED,
+ 0x0C, 0x02, 0xC5, 0x0B, 0x69, 0xD8, 0xA4, 0x02,
+ 0xCA, 0xBC, 0xCA, 0x44, 0x33, 0xB5, 0x1F, 0xD4
+ },
+ {
+ 0xB0, 0xCE, 0xAD, 0x09, 0x80, 0x7C, 0x67, 0x2A,
+ 0xF2, 0xEB, 0x2B, 0x0F, 0x06, 0xDD, 0xE4, 0x6C,
+ 0xF5, 0x37, 0x0E, 0x15, 0xA4, 0x09, 0x6B, 0x1A,
+ 0x7D, 0x7C, 0xBB, 0x36, 0xEC, 0x31, 0xC2, 0x05,
+ 0xFB, 0xEF, 0xCA, 0x00, 0xB7, 0xA4, 0x16, 0x2F,
+ 0xA8, 0x9F, 0xB4, 0xFB, 0x3E, 0xB7, 0x8D, 0x79,
+ 0x77, 0x0C, 0x23, 0xF4, 0x4E, 0x72, 0x06, 0x66,
+ 0x4C, 0xE3, 0xCD, 0x93, 0x1C, 0x29, 0x1E, 0x5D
+ },
+ {
+ 0xBB, 0x66, 0x64, 0x93, 0x1E, 0xC9, 0x70, 0x44,
+ 0xE4, 0x5B, 0x2A, 0xE4, 0x20, 0xAE, 0x1C, 0x55,
+ 0x1A, 0x88, 0x74, 0xBC, 0x93, 0x7D, 0x08, 0xE9,
+ 0x69, 0x39, 0x9C, 0x39, 0x64, 0xEB, 0xDB, 0xA8,
+ 0x34, 0x6C, 0xDD, 0x5D, 0x09, 0xCA, 0xAF, 0xE4,
+ 0xC2, 0x8B, 0xA7, 0xEC, 0x78, 0x81, 0x91, 0xCE,
+ 0xCA, 0x65, 0xDD, 0xD6, 0xF9, 0x5F, 0x18, 0x58,
+ 0x3E, 0x04, 0x0D, 0x0F, 0x30, 0xD0, 0x36, 0x4D
+ },
+ {
+ 0x65, 0xBC, 0x77, 0x0A, 0x5F, 0xAA, 0x37, 0x92,
+ 0x36, 0x98, 0x03, 0x68, 0x3E, 0x84, 0x4B, 0x0B,
+ 0xE7, 0xEE, 0x96, 0xF2, 0x9F, 0x6D, 0x6A, 0x35,
+ 0x56, 0x80, 0x06, 0xBD, 0x55, 0x90, 0xF9, 0xA4,
+ 0xEF, 0x63, 0x9B, 0x7A, 0x80, 0x61, 0xC7, 0xB0,
+ 0x42, 0x4B, 0x66, 0xB6, 0x0A, 0xC3, 0x4A, 0xF3,
+ 0x11, 0x99, 0x05, 0xF3, 0x3A, 0x9D, 0x8C, 0x3A,
+ 0xE1, 0x83, 0x82, 0xCA, 0x9B, 0x68, 0x99, 0x00
+ },
+ {
+ 0xEA, 0x9B, 0x4D, 0xCA, 0x33, 0x33, 0x36, 0xAA,
+ 0xF8, 0x39, 0xA4, 0x5C, 0x6E, 0xAA, 0x48, 0xB8,
+ 0xCB, 0x4C, 0x7D, 0xDA, 0xBF, 0xFE, 0xA4, 0xF6,
+ 0x43, 0xD6, 0x35, 0x7E, 0xA6, 0x62, 0x8A, 0x48,
+ 0x0A, 0x5B, 0x45, 0xF2, 0xB0, 0x52, 0xC1, 0xB0,
+ 0x7D, 0x1F, 0xED, 0xCA, 0x91, 0x8B, 0x6F, 0x11,
+ 0x39, 0xD8, 0x0F, 0x74, 0xC2, 0x45, 0x10, 0xDC,
+ 0xBA, 0xA4, 0xBE, 0x70, 0xEA, 0xCC, 0x1B, 0x06
+ },
+ {
+ 0xE6, 0x34, 0x2F, 0xB4, 0xA7, 0x80, 0xAD, 0x97,
+ 0x5D, 0x0E, 0x24, 0xBC, 0xE1, 0x49, 0x98, 0x9B,
+ 0x91, 0xD3, 0x60, 0x55, 0x7E, 0x87, 0x99, 0x4F,
+ 0x6B, 0x45, 0x7B, 0x89, 0x55, 0x75, 0xCC, 0x02,
+ 0xD0, 0xC1, 0x5B, 0xAD, 0x3C, 0xE7, 0x57, 0x7F,
+ 0x4C, 0x63, 0x92, 0x7F, 0xF1, 0x3F, 0x3E, 0x38,
+ 0x1F, 0xF7, 0xE7, 0x2B, 0xDB, 0xE7, 0x45, 0x32,
+ 0x48, 0x44, 0xA9, 0xD2, 0x7E, 0x3F, 0x1C, 0x01
+ },
+ {
+ 0x3E, 0x20, 0x9C, 0x9B, 0x33, 0xE8, 0xE4, 0x61,
+ 0x17, 0x8A, 0xB4, 0x6B, 0x1C, 0x64, 0xB4, 0x9A,
+ 0x07, 0xFB, 0x74, 0x5F, 0x1C, 0x8B, 0xC9, 0x5F,
+ 0xBF, 0xB9, 0x4C, 0x6B, 0x87, 0xC6, 0x95, 0x16,
+ 0x65, 0x1B, 0x26, 0x4E, 0xF9, 0x80, 0x93, 0x7F,
+ 0xAD, 0x41, 0x23, 0x8B, 0x91, 0xDD, 0xC0, 0x11,
+ 0xA5, 0xDD, 0x77, 0x7C, 0x7E, 0xFD, 0x44, 0x94,
+ 0xB4, 0xB6, 0xEC, 0xD3, 0xA9, 0xC2, 0x2A, 0xC0
+ },
+ {
+ 0xFD, 0x6A, 0x3D, 0x5B, 0x18, 0x75, 0xD8, 0x04,
+ 0x86, 0xD6, 0xE6, 0x96, 0x94, 0xA5, 0x6D, 0xBB,
+ 0x04, 0xA9, 0x9A, 0x4D, 0x05, 0x1F, 0x15, 0xDB,
+ 0x26, 0x89, 0x77, 0x6B, 0xA1, 0xC4, 0x88, 0x2E,
+ 0x6D, 0x46, 0x2A, 0x60, 0x3B, 0x70, 0x15, 0xDC,
+ 0x9F, 0x4B, 0x74, 0x50, 0xF0, 0x53, 0x94, 0x30,
+ 0x3B, 0x86, 0x52, 0xCF, 0xB4, 0x04, 0xA2, 0x66,
+ 0x96, 0x2C, 0x41, 0xBA, 0xE6, 0xE1, 0x8A, 0x94
+ },
+ {
+ 0x95, 0x1E, 0x27, 0x51, 0x7E, 0x6B, 0xAD, 0x9E,
+ 0x41, 0x95, 0xFC, 0x86, 0x71, 0xDE, 0xE3, 0xE7,
+ 0xE9, 0xBE, 0x69, 0xCE, 0xE1, 0x42, 0x2C, 0xB9,
+ 0xFE, 0xCF, 0xCE, 0x0D, 0xBA, 0x87, 0x5F, 0x7B,
+ 0x31, 0x0B, 0x93, 0xEE, 0x3A, 0x3D, 0x55, 0x8F,
+ 0x94, 0x1F, 0x63, 0x5F, 0x66, 0x8F, 0xF8, 0x32,
+ 0xD2, 0xC1, 0xD0, 0x33, 0xC5, 0xE2, 0xF0, 0x99,
+ 0x7E, 0x4C, 0x66, 0xF1, 0x47, 0x34, 0x4E, 0x02
+ },
+ {
+ 0x8E, 0xBA, 0x2F, 0x87, 0x4F, 0x1A, 0xE8, 0x40,
+ 0x41, 0x90, 0x3C, 0x7C, 0x42, 0x53, 0xC8, 0x22,
+ 0x92, 0x53, 0x0F, 0xC8, 0x50, 0x95, 0x50, 0xBF,
+ 0xDC, 0x34, 0xC9, 0x5C, 0x7E, 0x28, 0x89, 0xD5,
+ 0x65, 0x0B, 0x0A, 0xD8, 0xCB, 0x98, 0x8E, 0x5C,
+ 0x48, 0x94, 0xCB, 0x87, 0xFB, 0xFB, 0xB1, 0x96,
+ 0x12, 0xEA, 0x93, 0xCC, 0xC4, 0xC5, 0xCA, 0xD1,
+ 0x71, 0x58, 0xB9, 0x76, 0x34, 0x64, 0xB4, 0x92
+ },
+ {
+ 0x16, 0xF7, 0x12, 0xEA, 0xA1, 0xB7, 0xC6, 0x35,
+ 0x47, 0x19, 0xA8, 0xE7, 0xDB, 0xDF, 0xAF, 0x55,
+ 0xE4, 0x06, 0x3A, 0x4D, 0x27, 0x7D, 0x94, 0x75,
+ 0x50, 0x01, 0x9B, 0x38, 0xDF, 0xB5, 0x64, 0x83,
+ 0x09, 0x11, 0x05, 0x7D, 0x50, 0x50, 0x61, 0x36,
+ 0xE2, 0x39, 0x4C, 0x3B, 0x28, 0x94, 0x5C, 0xC9,
+ 0x64, 0x96, 0x7D, 0x54, 0xE3, 0x00, 0x0C, 0x21,
+ 0x81, 0x62, 0x6C, 0xFB, 0x9B, 0x73, 0xEF, 0xD2
+ },
+ {
+ 0xC3, 0x96, 0x39, 0xE7, 0xD5, 0xC7, 0xFB, 0x8C,
+ 0xDD, 0x0F, 0xD3, 0xE6, 0xA5, 0x20, 0x96, 0x03,
+ 0x94, 0x37, 0x12, 0x2F, 0x21, 0xC7, 0x8F, 0x16,
+ 0x79, 0xCE, 0xA9, 0xD7, 0x8A, 0x73, 0x4C, 0x56,
+ 0xEC, 0xBE, 0xB2, 0x86, 0x54, 0xB4, 0xF1, 0x8E,
+ 0x34, 0x2C, 0x33, 0x1F, 0x6F, 0x72, 0x29, 0xEC,
+ 0x4B, 0x4B, 0xC2, 0x81, 0xB2, 0xD8, 0x0A, 0x6E,
+ 0xB5, 0x00, 0x43, 0xF3, 0x17, 0x96, 0xC8, 0x8C
+ },
+ {
+ 0x72, 0xD0, 0x81, 0xAF, 0x99, 0xF8, 0xA1, 0x73,
+ 0xDC, 0xC9, 0xA0, 0xAC, 0x4E, 0xB3, 0x55, 0x74,
+ 0x05, 0x63, 0x9A, 0x29, 0x08, 0x4B, 0x54, 0xA4,
+ 0x01, 0x72, 0x91, 0x2A, 0x2F, 0x8A, 0x39, 0x51,
+ 0x29, 0xD5, 0x53, 0x6F, 0x09, 0x18, 0xE9, 0x02,
+ 0xF9, 0xE8, 0xFA, 0x60, 0x00, 0x99, 0x5F, 0x41,
+ 0x68, 0xDD, 0xC5, 0xF8, 0x93, 0x01, 0x1B, 0xE6,
+ 0xA0, 0xDB, 0xC9, 0xB8, 0xA1, 0xA3, 0xF5, 0xBB
+ },
+ {
+ 0xC1, 0x1A, 0xA8, 0x1E, 0x5E, 0xFD, 0x24, 0xD5,
+ 0xFC, 0x27, 0xEE, 0x58, 0x6C, 0xFD, 0x88, 0x47,
+ 0xFB, 0xB0, 0xE2, 0x76, 0x01, 0xCC, 0xEC, 0xE5,
+ 0xEC, 0xCA, 0x01, 0x98, 0xE3, 0xC7, 0x76, 0x53,
+ 0x93, 0xBB, 0x74, 0x45, 0x7C, 0x7E, 0x7A, 0x27,
+ 0xEB, 0x91, 0x70, 0x35, 0x0E, 0x1F, 0xB5, 0x38,
+ 0x57, 0x17, 0x75, 0x06, 0xBE, 0x3E, 0x76, 0x2C,
+ 0xC0, 0xF1, 0x4D, 0x8C, 0x3A, 0xFE, 0x90, 0x77
+ },
+ {
+ 0xC2, 0x8F, 0x21, 0x50, 0xB4, 0x52, 0xE6, 0xC0,
+ 0xC4, 0x24, 0xBC, 0xDE, 0x6F, 0x8D, 0x72, 0x00,
+ 0x7F, 0x93, 0x10, 0xFE, 0xD7, 0xF2, 0xF8, 0x7D,
+ 0xE0, 0xDB, 0xB6, 0x4F, 0x44, 0x79, 0xD6, 0xC1,
+ 0x44, 0x1B, 0xA6, 0x6F, 0x44, 0xB2, 0xAC, 0xCE,
+ 0xE6, 0x16, 0x09, 0x17, 0x7E, 0xD3, 0x40, 0x12,
+ 0x8B, 0x40, 0x7E, 0xCE, 0xC7, 0xC6, 0x4B, 0xBE,
+ 0x50, 0xD6, 0x3D, 0x22, 0xD8, 0x62, 0x77, 0x27
+ },
+ {
+ 0xF6, 0x3D, 0x88, 0x12, 0x28, 0x77, 0xEC, 0x30,
+ 0xB8, 0xC8, 0xB0, 0x0D, 0x22, 0xE8, 0x90, 0x00,
+ 0xA9, 0x66, 0x42, 0x61, 0x12, 0xBD, 0x44, 0x16,
+ 0x6E, 0x2F, 0x52, 0x5B, 0x76, 0x9C, 0xCB, 0xE9,
+ 0xB2, 0x86, 0xD4, 0x37, 0xA0, 0x12, 0x91, 0x30,
+ 0xDD, 0xE1, 0xA8, 0x6C, 0x43, 0xE0, 0x4B, 0xED,
+ 0xB5, 0x94, 0xE6, 0x71, 0xD9, 0x82, 0x83, 0xAF,
+ 0xE6, 0x4C, 0xE3, 0x31, 0xDE, 0x98, 0x28, 0xFD
+ },
+ {
+ 0x34, 0x8B, 0x05, 0x32, 0x88, 0x0B, 0x88, 0xA6,
+ 0x61, 0x4A, 0x8D, 0x74, 0x08, 0xC3, 0xF9, 0x13,
+ 0x35, 0x7F, 0xBB, 0x60, 0xE9, 0x95, 0xC6, 0x02,
+ 0x05, 0xBE, 0x91, 0x39, 0xE7, 0x49, 0x98, 0xAE,
+ 0xDE, 0x7F, 0x45, 0x81, 0xE4, 0x2F, 0x6B, 0x52,
+ 0x69, 0x8F, 0x7F, 0xA1, 0x21, 0x97, 0x08, 0xC1,
+ 0x44, 0x98, 0x06, 0x7F, 0xD1, 0xE0, 0x95, 0x02,
+ 0xDE, 0x83, 0xA7, 0x7D, 0xD2, 0x81, 0x15, 0x0C
+ },
+ {
+ 0x51, 0x33, 0xDC, 0x8B, 0xEF, 0x72, 0x53, 0x59,
+ 0xDF, 0xF5, 0x97, 0x92, 0xD8, 0x5E, 0xAF, 0x75,
+ 0xB7, 0xE1, 0xDC, 0xD1, 0x97, 0x8B, 0x01, 0xC3,
+ 0x5B, 0x1B, 0x85, 0xFC, 0xEB, 0xC6, 0x33, 0x88,
+ 0xAD, 0x99, 0xA1, 0x7B, 0x63, 0x46, 0xA2, 0x17,
+ 0xDC, 0x1A, 0x96, 0x22, 0xEB, 0xD1, 0x22, 0xEC,
+ 0xF6, 0x91, 0x3C, 0x4D, 0x31, 0xA6, 0xB5, 0x2A,
+ 0x69, 0x5B, 0x86, 0xAF, 0x00, 0xD7, 0x41, 0xA0
+ },
+ {
+ 0x27, 0x53, 0xC4, 0xC0, 0xE9, 0x8E, 0xCA, 0xD8,
+ 0x06, 0xE8, 0x87, 0x80, 0xEC, 0x27, 0xFC, 0xCD,
+ 0x0F, 0x5C, 0x1A, 0xB5, 0x47, 0xF9, 0xE4, 0xBF,
+ 0x16, 0x59, 0xD1, 0x92, 0xC2, 0x3A, 0xA2, 0xCC,
+ 0x97, 0x1B, 0x58, 0xB6, 0x80, 0x25, 0x80, 0xBA,
+ 0xEF, 0x8A, 0xDC, 0x3B, 0x77, 0x6E, 0xF7, 0x08,
+ 0x6B, 0x25, 0x45, 0xC2, 0x98, 0x7F, 0x34, 0x8E,
+ 0xE3, 0x71, 0x9C, 0xDE, 0xF2, 0x58, 0xC4, 0x03
+ },
+ {
+ 0xB1, 0x66, 0x35, 0x73, 0xCE, 0x4B, 0x9D, 0x8C,
+ 0xAE, 0xFC, 0x86, 0x50, 0x12, 0xF3, 0xE3, 0x97,
+ 0x14, 0xB9, 0x89, 0x8A, 0x5D, 0xA6, 0xCE, 0x17,
+ 0xC2, 0x5A, 0x6A, 0x47, 0x93, 0x1A, 0x9D, 0xDB,
+ 0x9B, 0xBE, 0x98, 0xAD, 0xAA, 0x55, 0x3B, 0xEE,
+ 0xD4, 0x36, 0xE8, 0x95, 0x78, 0x45, 0x54, 0x16,
+ 0xC2, 0xA5, 0x2A, 0x52, 0x5C, 0xF2, 0x86, 0x2B,
+ 0x8D, 0x1D, 0x49, 0xA2, 0x53, 0x1B, 0x73, 0x91
+ },
+ {
+ 0x64, 0xF5, 0x8B, 0xD6, 0xBF, 0xC8, 0x56, 0xF5,
+ 0xE8, 0x73, 0xB2, 0xA2, 0x95, 0x6E, 0xA0, 0xED,
+ 0xA0, 0xD6, 0xDB, 0x0D, 0xA3, 0x9C, 0x8C, 0x7F,
+ 0xC6, 0x7C, 0x9F, 0x9F, 0xEE, 0xFC, 0xFF, 0x30,
+ 0x72, 0xCD, 0xF9, 0xE6, 0xEA, 0x37, 0xF6, 0x9A,
+ 0x44, 0xF0, 0xC6, 0x1A, 0xA0, 0xDA, 0x36, 0x93,
+ 0xC2, 0xDB, 0x5B, 0x54, 0x96, 0x0C, 0x02, 0x81,
+ 0xA0, 0x88, 0x15, 0x1D, 0xB4, 0x2B, 0x11, 0xE8
+ },
+ {
+ 0x07, 0x64, 0xC7, 0xBE, 0x28, 0x12, 0x5D, 0x90,
+ 0x65, 0xC4, 0xB9, 0x8A, 0x69, 0xD6, 0x0A, 0xED,
+ 0xE7, 0x03, 0x54, 0x7C, 0x66, 0xA1, 0x2E, 0x17,
+ 0xE1, 0xC6, 0x18, 0x99, 0x41, 0x32, 0xF5, 0xEF,
+ 0x82, 0x48, 0x2C, 0x1E, 0x3F, 0xE3, 0x14, 0x6C,
+ 0xC6, 0x53, 0x76, 0xCC, 0x10, 0x9F, 0x01, 0x38,
+ 0xED, 0x9A, 0x80, 0xE4, 0x9F, 0x1F, 0x3C, 0x7D,
+ 0x61, 0x0D, 0x2F, 0x24, 0x32, 0xF2, 0x06, 0x05
+ },
+ {
+ 0xF7, 0x48, 0x78, 0x43, 0x98, 0xA2, 0xFF, 0x03,
+ 0xEB, 0xEB, 0x07, 0xE1, 0x55, 0xE6, 0x61, 0x16,
+ 0xA8, 0x39, 0x74, 0x1A, 0x33, 0x6E, 0x32, 0xDA,
+ 0x71, 0xEC, 0x69, 0x60, 0x01, 0xF0, 0xAD, 0x1B,
+ 0x25, 0xCD, 0x48, 0xC6, 0x9C, 0xFC, 0xA7, 0x26,
+ 0x5E, 0xCA, 0x1D, 0xD7, 0x19, 0x04, 0xA0, 0xCE,
+ 0x74, 0x8A, 0xC4, 0x12, 0x4F, 0x35, 0x71, 0x07,
+ 0x6D, 0xFA, 0x71, 0x16, 0xA9, 0xCF, 0x00, 0xE9
+ },
+ {
+ 0x3F, 0x0D, 0xBC, 0x01, 0x86, 0xBC, 0xEB, 0x6B,
+ 0x78, 0x5B, 0xA7, 0x8D, 0x2A, 0x2A, 0x01, 0x3C,
+ 0x91, 0x0B, 0xE1, 0x57, 0xBD, 0xAF, 0xFA, 0xE8,
+ 0x1B, 0xB6, 0x66, 0x3B, 0x1A, 0x73, 0x72, 0x2F,
+ 0x7F, 0x12, 0x28, 0x79, 0x5F, 0x3E, 0xCA, 0xDA,
+ 0x87, 0xCF, 0x6E, 0xF0, 0x07, 0x84, 0x74, 0xAF,
+ 0x73, 0xF3, 0x1E, 0xCA, 0x0C, 0xC2, 0x00, 0xED,
+ 0x97, 0x5B, 0x68, 0x93, 0xF7, 0x61, 0xCB, 0x6D
+ },
+ {
+ 0xD4, 0x76, 0x2C, 0xD4, 0x59, 0x98, 0x76, 0xCA,
+ 0x75, 0xB2, 0xB8, 0xFE, 0x24, 0x99, 0x44, 0xDB,
+ 0xD2, 0x7A, 0xCE, 0x74, 0x1F, 0xDA, 0xB9, 0x36,
+ 0x16, 0xCB, 0xC6, 0xE4, 0x25, 0x46, 0x0F, 0xEB,
+ 0x51, 0xD4, 0xE7, 0xAD, 0xCC, 0x38, 0x18, 0x0E,
+ 0x7F, 0xC4, 0x7C, 0x89, 0x02, 0x4A, 0x7F, 0x56,
+ 0x19, 0x1A, 0xDB, 0x87, 0x8D, 0xFD, 0xE4, 0xEA,
+ 0xD6, 0x22, 0x23, 0xF5, 0xA2, 0x61, 0x0E, 0xFE
+ },
+ {
+ 0xCD, 0x36, 0xB3, 0xD5, 0xB4, 0xC9, 0x1B, 0x90,
+ 0xFC, 0xBB, 0xA7, 0x95, 0x13, 0xCF, 0xEE, 0x19,
+ 0x07, 0xD8, 0x64, 0x5A, 0x16, 0x2A, 0xFD, 0x0C,
+ 0xD4, 0xCF, 0x41, 0x92, 0xD4, 0xA5, 0xF4, 0xC8,
+ 0x92, 0x18, 0x3A, 0x8E, 0xAC, 0xDB, 0x2B, 0x6B,
+ 0x6A, 0x9D, 0x9A, 0xA8, 0xC1, 0x1A, 0xC1, 0xB2,
+ 0x61, 0xB3, 0x80, 0xDB, 0xEE, 0x24, 0xCA, 0x46,
+ 0x8F, 0x1B, 0xFD, 0x04, 0x3C, 0x58, 0xEE, 0xFE
+ },
+ {
+ 0x98, 0x59, 0x34, 0x52, 0x28, 0x16, 0x61, 0xA5,
+ 0x3C, 0x48, 0xA9, 0xD8, 0xCD, 0x79, 0x08, 0x26,
+ 0xC1, 0xA1, 0xCE, 0x56, 0x77, 0x38, 0x05, 0x3D,
+ 0x0B, 0xEE, 0x4A, 0x91, 0xA3, 0xD5, 0xBD, 0x92,
+ 0xEE, 0xFD, 0xBA, 0xBE, 0xBE, 0x32, 0x04, 0xF2,
+ 0x03, 0x1C, 0xA5, 0xF7, 0x81, 0xBD, 0xA9, 0x9E,
+ 0xF5, 0xD8, 0xAE, 0x56, 0xE5, 0xB0, 0x4A, 0x9E,
+ 0x1E, 0xCD, 0x21, 0xB0, 0xEB, 0x05, 0xD3, 0xE1
+ },
+ {
+ 0x77, 0x1F, 0x57, 0xDD, 0x27, 0x75, 0xCC, 0xDA,
+ 0xB5, 0x59, 0x21, 0xD3, 0xE8, 0xE3, 0x0C, 0xCF,
+ 0x48, 0x4D, 0x61, 0xFE, 0x1C, 0x1B, 0x9C, 0x2A,
+ 0xE8, 0x19, 0xD0, 0xFB, 0x2A, 0x12, 0xFA, 0xB9,
+ 0xBE, 0x70, 0xC4, 0xA7, 0xA1, 0x38, 0xDA, 0x84,
+ 0xE8, 0x28, 0x04, 0x35, 0xDA, 0xAD, 0xE5, 0xBB,
+ 0xE6, 0x6A, 0xF0, 0x83, 0x6A, 0x15, 0x4F, 0x81,
+ 0x7F, 0xB1, 0x7F, 0x33, 0x97, 0xE7, 0x25, 0xA3
+ },
+ {
+ 0xC6, 0x08, 0x97, 0xC6, 0xF8, 0x28, 0xE2, 0x1F,
+ 0x16, 0xFB, 0xB5, 0xF1, 0x5B, 0x32, 0x3F, 0x87,
+ 0xB6, 0xC8, 0x95, 0x5E, 0xAB, 0xF1, 0xD3, 0x80,
+ 0x61, 0xF7, 0x07, 0xF6, 0x08, 0xAB, 0xDD, 0x99,
+ 0x3F, 0xAC, 0x30, 0x70, 0x63, 0x3E, 0x28, 0x6C,
+ 0xF8, 0x33, 0x9C, 0xE2, 0x95, 0xDD, 0x35, 0x2D,
+ 0xF4, 0xB4, 0xB4, 0x0B, 0x2F, 0x29, 0xDA, 0x1D,
+ 0xD5, 0x0B, 0x3A, 0x05, 0xD0, 0x79, 0xE6, 0xBB
+ },
+ {
+ 0x82, 0x10, 0xCD, 0x2C, 0x2D, 0x3B, 0x13, 0x5C,
+ 0x2C, 0xF0, 0x7F, 0xA0, 0xD1, 0x43, 0x3C, 0xD7,
+ 0x71, 0xF3, 0x25, 0xD0, 0x75, 0xC6, 0x46, 0x9D,
+ 0x9C, 0x7F, 0x1B, 0xA0, 0x94, 0x3C, 0xD4, 0xAB,
+ 0x09, 0x80, 0x8C, 0xAB, 0xF4, 0xAC, 0xB9, 0xCE,
+ 0x5B, 0xB8, 0x8B, 0x49, 0x89, 0x29, 0xB4, 0xB8,
+ 0x47, 0xF6, 0x81, 0xAD, 0x2C, 0x49, 0x0D, 0x04,
+ 0x2D, 0xB2, 0xAE, 0xC9, 0x42, 0x14, 0xB0, 0x6B
+ },
+ {
+ 0x1D, 0x4E, 0xDF, 0xFF, 0xD8, 0xFD, 0x80, 0xF7,
+ 0xE4, 0x10, 0x78, 0x40, 0xFA, 0x3A, 0xA3, 0x1E,
+ 0x32, 0x59, 0x84, 0x91, 0xE4, 0xAF, 0x70, 0x13,
+ 0xC1, 0x97, 0xA6, 0x5B, 0x7F, 0x36, 0xDD, 0x3A,
+ 0xC4, 0xB4, 0x78, 0x45, 0x61, 0x11, 0xCD, 0x43,
+ 0x09, 0xD9, 0x24, 0x35, 0x10, 0x78, 0x2F, 0xA3,
+ 0x1B, 0x7C, 0x4C, 0x95, 0xFA, 0x95, 0x15, 0x20,
+ 0xD0, 0x20, 0xEB, 0x7E, 0x5C, 0x36, 0xE4, 0xEF
+ },
+ {
+ 0xAF, 0x8E, 0x6E, 0x91, 0xFA, 0xB4, 0x6C, 0xE4,
+ 0x87, 0x3E, 0x1A, 0x50, 0xA8, 0xEF, 0x44, 0x8C,
+ 0xC2, 0x91, 0x21, 0xF7, 0xF7, 0x4D, 0xEE, 0xF3,
+ 0x4A, 0x71, 0xEF, 0x89, 0xCC, 0x00, 0xD9, 0x27,
+ 0x4B, 0xC6, 0xC2, 0x45, 0x4B, 0xBB, 0x32, 0x30,
+ 0xD8, 0xB2, 0xEC, 0x94, 0xC6, 0x2B, 0x1D, 0xEC,
+ 0x85, 0xF3, 0x59, 0x3B, 0xFA, 0x30, 0xEA, 0x6F,
+ 0x7A, 0x44, 0xD7, 0xC0, 0x94, 0x65, 0xA2, 0x53
+ },
+ {
+ 0x29, 0xFD, 0x38, 0x4E, 0xD4, 0x90, 0x6F, 0x2D,
+ 0x13, 0xAA, 0x9F, 0xE7, 0xAF, 0x90, 0x59, 0x90,
+ 0x93, 0x8B, 0xED, 0x80, 0x7F, 0x18, 0x32, 0x45,
+ 0x4A, 0x37, 0x2A, 0xB4, 0x12, 0xEE, 0xA1, 0xF5,
+ 0x62, 0x5A, 0x1F, 0xCC, 0x9A, 0xC8, 0x34, 0x3B,
+ 0x7C, 0x67, 0xC5, 0xAB, 0xA6, 0xE0, 0xB1, 0xCC,
+ 0x46, 0x44, 0x65, 0x49, 0x13, 0x69, 0x2C, 0x6B,
+ 0x39, 0xEB, 0x91, 0x87, 0xCE, 0xAC, 0xD3, 0xEC
+ },
+ {
+ 0xA2, 0x68, 0xC7, 0x88, 0x5D, 0x98, 0x74, 0xA5,
+ 0x1C, 0x44, 0xDF, 0xFE, 0xD8, 0xEA, 0x53, 0xE9,
+ 0x4F, 0x78, 0x45, 0x6E, 0x0B, 0x2E, 0xD9, 0x9F,
+ 0xF5, 0xA3, 0x92, 0x47, 0x60, 0x81, 0x38, 0x26,
+ 0xD9, 0x60, 0xA1, 0x5E, 0xDB, 0xED, 0xBB, 0x5D,
+ 0xE5, 0x22, 0x6B, 0xA4, 0xB0, 0x74, 0xE7, 0x1B,
+ 0x05, 0xC5, 0x5B, 0x97, 0x56, 0xBB, 0x79, 0xE5,
+ 0x5C, 0x02, 0x75, 0x4C, 0x2C, 0x7B, 0x6C, 0x8A
+ },
+ {
+ 0x0C, 0xF8, 0x54, 0x54, 0x88, 0xD5, 0x6A, 0x86,
+ 0x81, 0x7C, 0xD7, 0xEC, 0xB1, 0x0F, 0x71, 0x16,
+ 0xB7, 0xEA, 0x53, 0x0A, 0x45, 0xB6, 0xEA, 0x49,
+ 0x7B, 0x6C, 0x72, 0xC9, 0x97, 0xE0, 0x9E, 0x3D,
+ 0x0D, 0xA8, 0x69, 0x8F, 0x46, 0xBB, 0x00, 0x6F,
+ 0xC9, 0x77, 0xC2, 0xCD, 0x3D, 0x11, 0x77, 0x46,
+ 0x3A, 0xC9, 0x05, 0x7F, 0xDD, 0x16, 0x62, 0xC8,
+ 0x5D, 0x0C, 0x12, 0x64, 0x43, 0xC1, 0x04, 0x73
+ },
+ {
+ 0xB3, 0x96, 0x14, 0x26, 0x8F, 0xDD, 0x87, 0x81,
+ 0x51, 0x5E, 0x2C, 0xFE, 0xBF, 0x89, 0xB4, 0xD5,
+ 0x40, 0x2B, 0xAB, 0x10, 0xC2, 0x26, 0xE6, 0x34,
+ 0x4E, 0x6B, 0x9A, 0xE0, 0x00, 0xFB, 0x0D, 0x6C,
+ 0x79, 0xCB, 0x2F, 0x3E, 0xC8, 0x0E, 0x80, 0xEA,
+ 0xEB, 0x19, 0x80, 0xD2, 0xF8, 0x69, 0x89, 0x16,
+ 0xBD, 0x2E, 0x9F, 0x74, 0x72, 0x36, 0x65, 0x51,
+ 0x16, 0x64, 0x9C, 0xD3, 0xCA, 0x23, 0xA8, 0x37
+ },
+ {
+ 0x74, 0xBE, 0xF0, 0x92, 0xFC, 0x6F, 0x1E, 0x5D,
+ 0xBA, 0x36, 0x63, 0xA3, 0xFB, 0x00, 0x3B, 0x2A,
+ 0x5B, 0xA2, 0x57, 0x49, 0x65, 0x36, 0xD9, 0x9F,
+ 0x62, 0xB9, 0xD7, 0x3F, 0x8F, 0x9E, 0xB3, 0xCE,
+ 0x9F, 0xF3, 0xEE, 0xC7, 0x09, 0xEB, 0x88, 0x36,
+ 0x55, 0xEC, 0x9E, 0xB8, 0x96, 0xB9, 0x12, 0x8F,
+ 0x2A, 0xFC, 0x89, 0xCF, 0x7D, 0x1A, 0xB5, 0x8A,
+ 0x72, 0xF4, 0xA3, 0xBF, 0x03, 0x4D, 0x2B, 0x4A
+ },
+ {
+ 0x3A, 0x98, 0x8D, 0x38, 0xD7, 0x56, 0x11, 0xF3,
+ 0xEF, 0x38, 0xB8, 0x77, 0x49, 0x80, 0xB3, 0x3E,
+ 0x57, 0x3B, 0x6C, 0x57, 0xBE, 0xE0, 0x46, 0x9B,
+ 0xA5, 0xEE, 0xD9, 0xB4, 0x4F, 0x29, 0x94, 0x5E,
+ 0x73, 0x47, 0x96, 0x7F, 0xBA, 0x2C, 0x16, 0x2E,
+ 0x1C, 0x3B, 0xE7, 0xF3, 0x10, 0xF2, 0xF7, 0x5E,
+ 0xE2, 0x38, 0x1E, 0x7B, 0xFD, 0x6B, 0x3F, 0x0B,
+ 0xAE, 0xA8, 0xD9, 0x5D, 0xFB, 0x1D, 0xAF, 0xB1
+ },
+ {
+ 0x58, 0xAE, 0xDF, 0xCE, 0x6F, 0x67, 0xDD, 0xC8,
+ 0x5A, 0x28, 0xC9, 0x92, 0xF1, 0xC0, 0xBD, 0x09,
+ 0x69, 0xF0, 0x41, 0xE6, 0x6F, 0x1E, 0xE8, 0x80,
+ 0x20, 0xA1, 0x25, 0xCB, 0xFC, 0xFE, 0xBC, 0xD6,
+ 0x17, 0x09, 0xC9, 0xC4, 0xEB, 0xA1, 0x92, 0xC1,
+ 0x5E, 0x69, 0xF0, 0x20, 0xD4, 0x62, 0x48, 0x60,
+ 0x19, 0xFA, 0x8D, 0xEA, 0x0C, 0xD7, 0xA4, 0x29,
+ 0x21, 0xA1, 0x9D, 0x2F, 0xE5, 0x46, 0xD4, 0x3D
+ },
+ {
+ 0x93, 0x47, 0xBD, 0x29, 0x14, 0x73, 0xE6, 0xB4,
+ 0xE3, 0x68, 0x43, 0x7B, 0x8E, 0x56, 0x1E, 0x06,
+ 0x5F, 0x64, 0x9A, 0x6D, 0x8A, 0xDA, 0x47, 0x9A,
+ 0xD0, 0x9B, 0x19, 0x99, 0xA8, 0xF2, 0x6B, 0x91,
+ 0xCF, 0x61, 0x20, 0xFD, 0x3B, 0xFE, 0x01, 0x4E,
+ 0x83, 0xF2, 0x3A, 0xCF, 0xA4, 0xC0, 0xAD, 0x7B,
+ 0x37, 0x12, 0xB2, 0xC3, 0xC0, 0x73, 0x32, 0x70,
+ 0x66, 0x31, 0x12, 0xCC, 0xD9, 0x28, 0x5C, 0xD9
+ },
+ {
+ 0xB3, 0x21, 0x63, 0xE7, 0xC5, 0xDB, 0xB5, 0xF5,
+ 0x1F, 0xDC, 0x11, 0xD2, 0xEA, 0xC8, 0x75, 0xEF,
+ 0xBB, 0xCB, 0x7E, 0x76, 0x99, 0x09, 0x0A, 0x7E,
+ 0x7F, 0xF8, 0xA8, 0xD5, 0x07, 0x95, 0xAF, 0x5D,
+ 0x74, 0xD9, 0xFF, 0x98, 0x54, 0x3E, 0xF8, 0xCD,
+ 0xF8, 0x9A, 0xC1, 0x3D, 0x04, 0x85, 0x27, 0x87,
+ 0x56, 0xE0, 0xEF, 0x00, 0xC8, 0x17, 0x74, 0x56,
+ 0x61, 0xE1, 0xD5, 0x9F, 0xE3, 0x8E, 0x75, 0x37
+ },
+ {
+ 0x10, 0x85, 0xD7, 0x83, 0x07, 0xB1, 0xC4, 0xB0,
+ 0x08, 0xC5, 0x7A, 0x2E, 0x7E, 0x5B, 0x23, 0x46,
+ 0x58, 0xA0, 0xA8, 0x2E, 0x4F, 0xF1, 0xE4, 0xAA,
+ 0xAC, 0x72, 0xB3, 0x12, 0xFD, 0xA0, 0xFE, 0x27,
+ 0xD2, 0x33, 0xBC, 0x5B, 0x10, 0xE9, 0xCC, 0x17,
+ 0xFD, 0xC7, 0x69, 0x7B, 0x54, 0x0C, 0x7D, 0x95,
+ 0xEB, 0x21, 0x5A, 0x19, 0xA1, 0xA0, 0xE2, 0x0E,
+ 0x1A, 0xBF, 0xA1, 0x26, 0xEF, 0xD5, 0x68, 0xC7
+ },
+ {
+ 0x4E, 0x5C, 0x73, 0x4C, 0x7D, 0xDE, 0x01, 0x1D,
+ 0x83, 0xEA, 0xC2, 0xB7, 0x34, 0x7B, 0x37, 0x35,
+ 0x94, 0xF9, 0x2D, 0x70, 0x91, 0xB9, 0xCA, 0x34,
+ 0xCB, 0x9C, 0x6F, 0x39, 0xBD, 0xF5, 0xA8, 0xD2,
+ 0xF1, 0x34, 0x37, 0x9E, 0x16, 0xD8, 0x22, 0xF6,
+ 0x52, 0x21, 0x70, 0xCC, 0xF2, 0xDD, 0xD5, 0x5C,
+ 0x84, 0xB9, 0xE6, 0xC6, 0x4F, 0xC9, 0x27, 0xAC,
+ 0x4C, 0xF8, 0xDF, 0xB2, 0xA1, 0x77, 0x01, 0xF2
+ },
+ {
+ 0x69, 0x5D, 0x83, 0xBD, 0x99, 0x0A, 0x11, 0x17,
+ 0xB3, 0xD0, 0xCE, 0x06, 0xCC, 0x88, 0x80, 0x27,
+ 0xD1, 0x2A, 0x05, 0x4C, 0x26, 0x77, 0xFD, 0x82,
+ 0xF0, 0xD4, 0xFB, 0xFC, 0x93, 0x57, 0x55, 0x23,
+ 0xE7, 0x99, 0x1A, 0x5E, 0x35, 0xA3, 0x75, 0x2E,
+ 0x9B, 0x70, 0xCE, 0x62, 0x99, 0x2E, 0x26, 0x8A,
+ 0x87, 0x77, 0x44, 0xCD, 0xD4, 0x35, 0xF5, 0xF1,
+ 0x30, 0x86, 0x9C, 0x9A, 0x20, 0x74, 0xB3, 0x38
+ },
+ {
+ 0xA6, 0x21, 0x37, 0x43, 0x56, 0x8E, 0x3B, 0x31,
+ 0x58, 0xB9, 0x18, 0x43, 0x01, 0xF3, 0x69, 0x08,
+ 0x47, 0x55, 0x4C, 0x68, 0x45, 0x7C, 0xB4, 0x0F,
+ 0xC9, 0xA4, 0xB8, 0xCF, 0xD8, 0xD4, 0xA1, 0x18,
+ 0xC3, 0x01, 0xA0, 0x77, 0x37, 0xAE, 0xDA, 0x0F,
+ 0x92, 0x9C, 0x68, 0x91, 0x3C, 0x5F, 0x51, 0xC8,
+ 0x03, 0x94, 0xF5, 0x3B, 0xFF, 0x1C, 0x3E, 0x83,
+ 0xB2, 0xE4, 0x0C, 0xA9, 0x7E, 0xBA, 0x9E, 0x15
+ },
+ {
+ 0xD4, 0x44, 0xBF, 0xA2, 0x36, 0x2A, 0x96, 0xDF,
+ 0x21, 0x3D, 0x07, 0x0E, 0x33, 0xFA, 0x84, 0x1F,
+ 0x51, 0x33, 0x4E, 0x4E, 0x76, 0x86, 0x6B, 0x81,
+ 0x39, 0xE8, 0xAF, 0x3B, 0xB3, 0x39, 0x8B, 0xE2,
+ 0xDF, 0xAD, 0xDC, 0xBC, 0x56, 0xB9, 0x14, 0x6D,
+ 0xE9, 0xF6, 0x81, 0x18, 0xDC, 0x58, 0x29, 0xE7,
+ 0x4B, 0x0C, 0x28, 0xD7, 0x71, 0x19, 0x07, 0xB1,
+ 0x21, 0xF9, 0x16, 0x1C, 0xB9, 0x2B, 0x69, 0xA9
+ },
+ {
+ 0x14, 0x27, 0x09, 0xD6, 0x2E, 0x28, 0xFC, 0xCC,
+ 0xD0, 0xAF, 0x97, 0xFA, 0xD0, 0xF8, 0x46, 0x5B,
+ 0x97, 0x1E, 0x82, 0x20, 0x1D, 0xC5, 0x10, 0x70,
+ 0xFA, 0xA0, 0x37, 0x2A, 0xA4, 0x3E, 0x92, 0x48,
+ 0x4B, 0xE1, 0xC1, 0xE7, 0x3B, 0xA1, 0x09, 0x06,
+ 0xD5, 0xD1, 0x85, 0x3D, 0xB6, 0xA4, 0x10, 0x6E,
+ 0x0A, 0x7B, 0xF9, 0x80, 0x0D, 0x37, 0x3D, 0x6D,
+ 0xEE, 0x2D, 0x46, 0xD6, 0x2E, 0xF2, 0xA4, 0x61
+ },
+};
+
+
+
+
+static const uint8_t blake2sp_kat[KAT_LENGTH][BLAKE2S_OUTBYTES] =
+{
+ {
+ 0xDD, 0x0E, 0x89, 0x17, 0x76, 0x93, 0x3F, 0x43,
+ 0xC7, 0xD0, 0x32, 0xB0, 0x8A, 0x91, 0x7E, 0x25,
+ 0x74, 0x1F, 0x8A, 0xA9, 0xA1, 0x2C, 0x12, 0xE1,
+ 0xCA, 0xC8, 0x80, 0x15, 0x00, 0xF2, 0xCA, 0x4F
+ },
+ {
+ 0xA6, 0xB9, 0xEE, 0xCC, 0x25, 0x22, 0x7A, 0xD7,
+ 0x88, 0xC9, 0x9D, 0x3F, 0x23, 0x6D, 0xEB, 0xC8,
+ 0xDA, 0x40, 0x88, 0x49, 0xE9, 0xA5, 0x17, 0x89,
+ 0x78, 0x72, 0x7A, 0x81, 0x45, 0x7F, 0x72, 0x39
+ },
+ {
+ 0xDA, 0xCA, 0xDE, 0xCE, 0x7A, 0x8E, 0x6B, 0xF3,
+ 0xAB, 0xFE, 0x32, 0x4C, 0xA6, 0x95, 0x43, 0x69,
+ 0x84, 0xB8, 0x19, 0x5D, 0x29, 0xF6, 0xBB, 0xD8,
+ 0x96, 0xE4, 0x1E, 0x18, 0xE2, 0x1C, 0x91, 0x45
+ },
+ {
+ 0xED, 0x14, 0x41, 0x3B, 0x40, 0xDA, 0x68, 0x9F,
+ 0x1F, 0x7F, 0xED, 0x2B, 0x08, 0xDF, 0xF4, 0x5B,
+ 0x80, 0x92, 0xDB, 0x5E, 0xC2, 0xC3, 0x61, 0x0E,
+ 0x02, 0x72, 0x4D, 0x20, 0x2F, 0x42, 0x3C, 0x46
+ },
+ {
+ 0x9B, 0x8A, 0x52, 0x7B, 0x52, 0x72, 0x25, 0x0A,
+ 0x1E, 0xC3, 0x97, 0x38, 0x8F, 0x04, 0x09, 0x14,
+ 0x95, 0x48, 0x06, 0xE7, 0x94, 0xDB, 0x04, 0xB7,
+ 0x0A, 0x46, 0x11, 0xBC, 0x59, 0x58, 0x6A, 0x83
+ },
+ {
+ 0x2B, 0xB6, 0x33, 0x37, 0x29, 0x00, 0x0B, 0xE3,
+ 0xD5, 0xA2, 0x1B, 0x98, 0xF8, 0xE7, 0xEA, 0xD0,
+ 0x77, 0xF1, 0x51, 0xA5, 0x39, 0x39, 0x19, 0xEB,
+ 0x67, 0xC8, 0x76, 0xEE, 0x00, 0xBB, 0xBB, 0x04
+ },
+ {
+ 0x63, 0xC0, 0x14, 0x08, 0x15, 0x4A, 0xD1, 0x9D,
+ 0x7F, 0xB7, 0x39, 0xF3, 0x11, 0x78, 0x17, 0x80,
+ 0x46, 0x2C, 0xF2, 0xEE, 0xCC, 0xE6, 0x0F, 0x06,
+ 0x4E, 0x85, 0x34, 0x87, 0xC2, 0x72, 0xE3, 0xEB
+ },
+ {
+ 0x3D, 0x05, 0x1A, 0x11, 0x76, 0x01, 0x9C, 0xA3,
+ 0x7B, 0xF3, 0x3D, 0x60, 0x42, 0x7F, 0x8D, 0x9D,
+ 0x1C, 0x3A, 0xBD, 0x59, 0x82, 0x97, 0xCF, 0xB4,
+ 0x23, 0x5F, 0x74, 0x7D, 0x7C, 0x7C, 0x7F, 0xEC
+ },
+ {
+ 0x39, 0x1E, 0xA9, 0x12, 0xDF, 0x4D, 0x4D, 0x79,
+ 0xA4, 0x64, 0x6D, 0x9D, 0xA2, 0x54, 0x9A, 0x44,
+ 0x6D, 0x22, 0x40, 0xF6, 0x24, 0x15, 0xD0, 0x70,
+ 0xA2, 0xE0, 0x93, 0x99, 0x2B, 0x47, 0x1F, 0xBA
+ },
+ {
+ 0x32, 0x46, 0x40, 0x44, 0x0E, 0xA5, 0xC3, 0x08,
+ 0x2D, 0xDC, 0x30, 0x9E, 0x78, 0x09, 0xD7, 0x41,
+ 0xD6, 0xCC, 0x1B, 0x2D, 0x49, 0x0F, 0xF8, 0xC0,
+ 0x52, 0x12, 0x8A, 0x6E, 0xEB, 0x40, 0x9D, 0x62
+ },
+ {
+ 0xAB, 0x85, 0x5E, 0x6F, 0xA3, 0x9A, 0x5E, 0x8F,
+ 0xC9, 0x0E, 0xAC, 0xB9, 0x99, 0xC7, 0xF7, 0x8A,
+ 0xE7, 0x1E, 0x59, 0xC3, 0xD9, 0x7D, 0x60, 0xAF,
+ 0xE5, 0x17, 0xD5, 0x87, 0x92, 0x3B, 0x77, 0x11
+ },
+ {
+ 0x2A, 0x39, 0xDA, 0x45, 0x86, 0xEF, 0xC4, 0x77,
+ 0x85, 0xA7, 0xA8, 0xDA, 0x85, 0x68, 0x3A, 0x51,
+ 0x72, 0x4C, 0xDE, 0xF5, 0x41, 0x3B, 0x35, 0x6D,
+ 0xC4, 0xFB, 0x50, 0x05, 0x13, 0xF8, 0xFA, 0x2E
+ },
+ {
+ 0x8A, 0x00, 0x57, 0xC1, 0xF7, 0x8A, 0xD6, 0x21,
+ 0x45, 0x55, 0xC0, 0x67, 0x07, 0x33, 0xE2, 0x9A,
+ 0x4C, 0x7E, 0x95, 0x62, 0x27, 0x66, 0x0E, 0xFE,
+ 0xB1, 0xD7, 0xFC, 0x79, 0xF5, 0x8E, 0xC6, 0xF2
+ },
+ {
+ 0x07, 0x64, 0xB0, 0x01, 0x7F, 0x5B, 0xD9, 0x51,
+ 0xF0, 0x1D, 0x9F, 0xDF, 0x95, 0xC0, 0xCB, 0x41,
+ 0x38, 0x98, 0x5D, 0x84, 0x79, 0x9C, 0xD4, 0x29,
+ 0x84, 0xE2, 0x5B, 0x51, 0x28, 0x00, 0xE7, 0x3C
+ },
+ {
+ 0xCC, 0x02, 0x49, 0x56, 0x93, 0xC8, 0xE1, 0x84,
+ 0xAD, 0x2E, 0xD0, 0x9D, 0x53, 0x3D, 0xC3, 0x3B,
+ 0x76, 0xA7, 0x78, 0x3D, 0x62, 0x07, 0xFC, 0xAC,
+ 0xCB, 0x64, 0xF3, 0xED, 0x2C, 0x6D, 0x66, 0xE0
+ },
+ {
+ 0xC0, 0xDF, 0x49, 0xC2, 0x06, 0xA3, 0x42, 0x88,
+ 0x14, 0x32, 0x16, 0x84, 0x7D, 0xF3, 0x34, 0xD4,
+ 0x56, 0x9D, 0xAD, 0x73, 0xC2, 0xB1, 0xFF, 0x62,
+ 0x84, 0x88, 0x4F, 0xD3, 0x89, 0x41, 0xFB, 0x95
+ },
+ {
+ 0xB9, 0x19, 0x45, 0x19, 0xE4, 0x97, 0x8A, 0x9D,
+ 0xC8, 0x93, 0xB2, 0x8B, 0xD8, 0x08, 0xCD, 0xFA,
+ 0xBB, 0x1B, 0xD5, 0x10, 0xD8, 0x62, 0xB3, 0x17,
+ 0x1F, 0xF6, 0xE0, 0x17, 0xA4, 0x1B, 0x80, 0x4C
+ },
+ {
+ 0xBB, 0xA9, 0x27, 0xAC, 0xF1, 0x1B, 0xEB, 0xD3,
+ 0x62, 0xA3, 0xA3, 0xEB, 0x78, 0xC4, 0xBB, 0x65,
+ 0xE6, 0x02, 0xA8, 0x70, 0x9F, 0xCE, 0xF3, 0x8D,
+ 0xC6, 0xC8, 0xB7, 0xBD, 0xA6, 0x64, 0xC3, 0x2C
+ },
+ {
+ 0xEC, 0xB4, 0x90, 0x0A, 0x63, 0x92, 0x4E, 0x72,
+ 0x0D, 0x40, 0xF2, 0xD2, 0xB1, 0x4D, 0x1B, 0xB3,
+ 0x9C, 0x37, 0x01, 0xAD, 0x73, 0x46, 0xBD, 0x0B,
+ 0x67, 0x23, 0x42, 0x70, 0xBF, 0xBE, 0x7E, 0x70
+ },
+ {
+ 0xF8, 0x31, 0x5A, 0x21, 0xB2, 0x5E, 0x6B, 0xA8,
+ 0xBF, 0x59, 0xB1, 0x7B, 0x05, 0x91, 0x3B, 0x8C,
+ 0xA4, 0x65, 0x9F, 0x1C, 0xD8, 0x38, 0xFC, 0xC7,
+ 0x73, 0xC9, 0xEB, 0x12, 0xE7, 0x00, 0x4E, 0x09
+ },
+ {
+ 0x4B, 0x77, 0xAF, 0x67, 0xA9, 0x23, 0x2B, 0xF1,
+ 0x18, 0x4E, 0x57, 0x81, 0x82, 0x94, 0x03, 0x1E,
+ 0x55, 0xF1, 0xF8, 0x53, 0xC9, 0x4D, 0xBA, 0xB5,
+ 0x57, 0x75, 0x47, 0x33, 0x0D, 0x65, 0xAA, 0x61
+ },
+ {
+ 0x76, 0x85, 0x68, 0x39, 0x0F, 0xD2, 0xB8, 0x70,
+ 0x94, 0x11, 0x4E, 0xD4, 0xCF, 0x72, 0x3E, 0xA3,
+ 0x20, 0xFE, 0x97, 0x7B, 0x53, 0x18, 0x03, 0x05,
+ 0xC3, 0x84, 0x33, 0x54, 0x79, 0xF0, 0xB5, 0x9B
+ },
+ {
+ 0xA4, 0x31, 0xCB, 0x27, 0x0F, 0x3E, 0x2C, 0x9B,
+ 0x7A, 0x95, 0x93, 0xB1, 0x55, 0xCC, 0xEC, 0xFF,
+ 0x5B, 0x5C, 0x4A, 0x2D, 0xCD, 0x5D, 0x6B, 0xB1,
+ 0xC4, 0x85, 0xAA, 0x28, 0x69, 0x97, 0xF9, 0x15
+ },
+ {
+ 0xD6, 0x91, 0xFA, 0x6A, 0x79, 0x0B, 0x1A, 0x51,
+ 0x79, 0x80, 0x08, 0x7F, 0x50, 0xB0, 0x3D, 0xED,
+ 0x8C, 0x6E, 0xD4, 0x86, 0xD0, 0x84, 0x22, 0x1C,
+ 0x82, 0x7D, 0x9B, 0xD9, 0x22, 0xBE, 0xB8, 0xC0
+ },
+ {
+ 0x8F, 0x97, 0x8A, 0x49, 0x32, 0xF4, 0x45, 0x98,
+ 0x13, 0xE8, 0xFE, 0x15, 0x68, 0x6E, 0x4E, 0xFA,
+ 0x25, 0xC2, 0xC5, 0xFF, 0x5A, 0x3A, 0x4F, 0x8C,
+ 0x9B, 0x14, 0x96, 0x5D, 0x2F, 0x0B, 0xE4, 0x61
+ },
+ {
+ 0x1E, 0xFB, 0xD0, 0xC1, 0x31, 0x44, 0x91, 0x42,
+ 0xF2, 0x29, 0x5F, 0x2D, 0x42, 0x41, 0x1D, 0xFE,
+ 0x0F, 0x48, 0xD4, 0xAC, 0xAE, 0x76, 0x2D, 0x8D,
+ 0xF6, 0x7A, 0x57, 0x0B, 0xF7, 0xB1, 0xDC, 0xD5
+ },
+ {
+ 0xD5, 0x3B, 0xA9, 0x33, 0x46, 0x14, 0x3A, 0xB8,
+ 0xE0, 0xD3, 0xD1, 0xBF, 0x27, 0x27, 0x06, 0xD1,
+ 0x69, 0xE6, 0x6C, 0x69, 0xC7, 0xB8, 0xF4, 0xA5,
+ 0xE8, 0x2F, 0xEF, 0x44, 0x07, 0x02, 0xBC, 0xF2
+ },
+ {
+ 0xF7, 0x1A, 0x3E, 0xC0, 0x1A, 0xA3, 0x82, 0xEA,
+ 0x76, 0x99, 0x2B, 0x43, 0x0A, 0x7F, 0x42, 0xC7,
+ 0xAD, 0x2A, 0x86, 0xAE, 0xA9, 0xC1, 0x9E, 0x76,
+ 0xCD, 0x17, 0x32, 0xEC, 0x68, 0x30, 0xDE, 0x6F
+ },
+ {
+ 0x80, 0xA6, 0xAB, 0x7B, 0x71, 0x04, 0x64, 0xF9,
+ 0x3E, 0x6C, 0xBA, 0x96, 0x86, 0x4A, 0xA6, 0x40,
+ 0x9B, 0xCA, 0xFC, 0x1B, 0xF4, 0xB3, 0x2A, 0x30,
+ 0x93, 0x72, 0xE8, 0x57, 0xE8, 0x04, 0x06, 0x8C
+ },
+ {
+ 0xDB, 0xDE, 0x81, 0xE5, 0x1A, 0x52, 0x17, 0x4B,
+ 0x10, 0x14, 0x90, 0x1B, 0x53, 0xBE, 0xF8, 0x8D,
+ 0xE9, 0x3B, 0x29, 0xE2, 0x74, 0x34, 0x7E, 0x8E,
+ 0x9A, 0x7B, 0x03, 0x74, 0x56, 0x62, 0x9F, 0x35
+ },
+ {
+ 0x75, 0xF2, 0x74, 0x46, 0x6B, 0x1A, 0x2D, 0x0F,
+ 0xD8, 0x45, 0xBB, 0xB5, 0x7C, 0x38, 0xC9, 0x89,
+ 0x51, 0x6E, 0x15, 0x68, 0x32, 0x0A, 0xB5, 0x17,
+ 0xB1, 0x63, 0xEA, 0xF7, 0x09, 0x23, 0x4C, 0xC7
+ },
+ {
+ 0xAF, 0xE1, 0xA0, 0x59, 0x1C, 0x49, 0x1D, 0x41,
+ 0x6E, 0xB6, 0x4F, 0x62, 0x86, 0xF3, 0xBA, 0x29,
+ 0xD4, 0xC9, 0x99, 0x82, 0x14, 0xA3, 0x83, 0x1C,
+ 0x39, 0x01, 0x4A, 0xC0, 0x30, 0x55, 0x79, 0x45
+ },
+ {
+ 0x67, 0xFF, 0x6A, 0xCD, 0xBE, 0x8A, 0x99, 0xA1,
+ 0x66, 0xA5, 0xD9, 0xCF, 0x32, 0x13, 0x65, 0x06,
+ 0xB5, 0x48, 0xD6, 0xC9, 0x47, 0xC2, 0x4C, 0x69,
+ 0x9C, 0xEA, 0x3A, 0xFD, 0x92, 0xAD, 0xFA, 0xCA
+ },
+ {
+ 0xBF, 0xB4, 0xD0, 0xC7, 0x11, 0x20, 0x75, 0x26,
+ 0x2C, 0x2D, 0xD2, 0x48, 0xF3, 0x34, 0xB2, 0xEF,
+ 0x15, 0x40, 0x08, 0x7E, 0xCC, 0x73, 0x82, 0xBC,
+ 0x2A, 0x27, 0x25, 0x75, 0xC5, 0x00, 0x9F, 0x70
+ },
+ {
+ 0x17, 0xC9, 0x4B, 0x9C, 0x53, 0x72, 0x43, 0xF2,
+ 0x33, 0x5B, 0x86, 0x39, 0x49, 0xB2, 0xB9, 0x1C,
+ 0x98, 0xA6, 0x95, 0x6D, 0x7C, 0x10, 0xAA, 0x98,
+ 0x99, 0x59, 0xA8, 0x0F, 0x91, 0x0C, 0x25, 0x22
+ },
+ {
+ 0xF6, 0x33, 0x8F, 0x43, 0x4D, 0x31, 0x94, 0x10,
+ 0x19, 0x6D, 0x95, 0x19, 0xAB, 0xCA, 0xEF, 0xF7,
+ 0xD5, 0x54, 0x39, 0xFD, 0x2A, 0xA5, 0xBA, 0xBF,
+ 0x7A, 0x7E, 0x79, 0x13, 0xB2, 0x94, 0xED, 0x4D
+ },
+ {
+ 0x08, 0xEF, 0x7D, 0x65, 0xF9, 0xBB, 0xF3, 0xDA,
+ 0x1F, 0x78, 0x84, 0xAE, 0x9B, 0x75, 0x90, 0x1F,
+ 0xD8, 0x52, 0x95, 0x66, 0x2A, 0x6E, 0xA7, 0x1D,
+ 0xE0, 0x8B, 0xEE, 0x38, 0x34, 0x57, 0x62, 0x78
+ },
+ {
+ 0x16, 0x47, 0xEC, 0xC2, 0xBA, 0x13, 0xF8, 0xB9,
+ 0x3B, 0x2F, 0xBC, 0xDC, 0x4E, 0x8F, 0x1D, 0xFA,
+ 0x47, 0xFE, 0x3B, 0xE1, 0x2A, 0xAA, 0x0E, 0x45,
+ 0x9B, 0x0E, 0x5A, 0x87, 0xF3, 0xA6, 0x9B, 0xB0
+ },
+ {
+ 0xFF, 0x92, 0x7A, 0x71, 0x78, 0x81, 0xF6, 0xFD,
+ 0x8E, 0xD8, 0xBF, 0x5D, 0x5E, 0x35, 0xBD, 0x80,
+ 0x16, 0x15, 0x73, 0xE5, 0x82, 0x94, 0x04, 0xC3,
+ 0x2D, 0x2A, 0x27, 0x6A, 0x01, 0xF4, 0xB9, 0x06
+ },
+ {
+ 0xC8, 0xCA, 0xF1, 0x36, 0xFF, 0x20, 0x9C, 0x82,
+ 0xE0, 0x24, 0x0C, 0x1E, 0x62, 0xA3, 0xBC, 0x7E,
+ 0x9C, 0xAC, 0x87, 0x3B, 0x01, 0x1C, 0xF7, 0xC5,
+ 0xE6, 0x7E, 0xC1, 0x87, 0xA5, 0xFB, 0xCD, 0x96
+ },
+ {
+ 0xD9, 0xAC, 0xC7, 0x3E, 0x3F, 0x42, 0x1E, 0x18,
+ 0x83, 0xB5, 0xED, 0x53, 0xD8, 0x2A, 0x9A, 0xEC,
+ 0x8F, 0x5D, 0xC9, 0x80, 0xC4, 0x2B, 0xCA, 0xEB,
+ 0x0E, 0x7D, 0x89, 0x76, 0xA3, 0x38, 0xEF, 0x51
+ },
+ {
+ 0x9F, 0x17, 0x3F, 0xCF, 0x08, 0xA5, 0x36, 0x21,
+ 0x93, 0xF3, 0x52, 0xC8, 0x25, 0x6A, 0xE5, 0x34,
+ 0xAE, 0x9C, 0xE7, 0xBF, 0xA4, 0xBC, 0x09, 0xFA,
+ 0xC9, 0x00, 0x98, 0xF9, 0x8A, 0x71, 0x62, 0x94
+ },
+ {
+ 0x0A, 0x72, 0x45, 0x79, 0xDC, 0x80, 0xBC, 0x0C,
+ 0x90, 0x04, 0xE5, 0x1B, 0xE7, 0xEF, 0xF3, 0xAF,
+ 0xA5, 0x30, 0x75, 0xAB, 0x4A, 0x32, 0x55, 0x77,
+ 0x33, 0x58, 0x6E, 0x82, 0x0F, 0xD3, 0x64, 0x23
+ },
+ {
+ 0x38, 0xF7, 0xC3, 0x40, 0xF4, 0xB1, 0x59, 0xB1,
+ 0xE5, 0x94, 0xF6, 0xEB, 0x83, 0x28, 0x49, 0x17,
+ 0xB7, 0xAA, 0x19, 0xC7, 0x4F, 0x57, 0x11, 0x7A,
+ 0x4E, 0x08, 0xCF, 0x7C, 0x4E, 0x32, 0xA2, 0x3C
+ },
+ {
+ 0x1C, 0x67, 0x4B, 0xE2, 0x57, 0xE9, 0xB3, 0x31,
+ 0x34, 0xD4, 0x16, 0x8F, 0x15, 0x2F, 0x8B, 0x63,
+ 0xDF, 0xD7, 0x80, 0xC9, 0x7D, 0xC4, 0xDC, 0x37,
+ 0xAC, 0x26, 0xCC, 0x0A, 0xEF, 0xB7, 0x9C, 0x1A
+ },
+ {
+ 0x2F, 0x0C, 0x59, 0x76, 0x16, 0xD5, 0x75, 0x17,
+ 0x14, 0xA5, 0xFB, 0x4E, 0xBF, 0x3C, 0x48, 0x1A,
+ 0x96, 0xC3, 0xAD, 0x14, 0x5E, 0xBD, 0xE0, 0x65,
+ 0x09, 0xF3, 0xA2, 0xE5, 0xF2, 0xC1, 0x3F, 0xC8
+ },
+ {
+ 0xFD, 0xDC, 0x69, 0xE0, 0xC9, 0x83, 0xCD, 0x82,
+ 0x83, 0xED, 0x81, 0x88, 0xBE, 0xC4, 0xE5, 0xF4,
+ 0x1D, 0xEA, 0x3D, 0x01, 0xB9, 0xE7, 0x4C, 0x4B,
+ 0xAF, 0x73, 0x41, 0xD8, 0xB4, 0xBF, 0x55, 0x3D
+ },
+ {
+ 0x24, 0xD0, 0x83, 0xCB, 0xA0, 0x38, 0xC8, 0x7E,
+ 0x9A, 0xCB, 0x86, 0x81, 0x82, 0x02, 0x08, 0xB7,
+ 0x5C, 0xB3, 0x29, 0x3A, 0x96, 0xC9, 0xEF, 0xA7,
+ 0x5D, 0x2C, 0x63, 0xF1, 0x6B, 0x85, 0xFE, 0x1E
+ },
+ {
+ 0x7F, 0x6A, 0x64, 0x9C, 0xCA, 0x89, 0xB2, 0x53,
+ 0xFF, 0xBD, 0x20, 0xC0, 0x16, 0x98, 0x01, 0x00,
+ 0xA8, 0x7C, 0x16, 0x81, 0x09, 0x62, 0x8F, 0xCC,
+ 0x66, 0x52, 0x5D, 0x8B, 0xAA, 0xFE, 0x50, 0x5F
+ },
+ {
+ 0x6D, 0xA3, 0x73, 0xB4, 0xC1, 0x87, 0x92, 0xB3,
+ 0x20, 0x9A, 0xDD, 0x15, 0xA5, 0x07, 0x4A, 0x1D,
+ 0x70, 0xC1, 0x0B, 0xB3, 0x94, 0x80, 0xCA, 0x3F,
+ 0xE5, 0xC4, 0x39, 0xD9, 0x5F, 0xC2, 0x86, 0xCA
+ },
+ {
+ 0x27, 0x0A, 0xFF, 0xA6, 0x42, 0x6F, 0x1A, 0x51,
+ 0x5C, 0x9B, 0x76, 0xDF, 0xC2, 0x7D, 0x18, 0x1F,
+ 0xC2, 0xFD, 0x57, 0xD0, 0x82, 0xA3, 0xBA, 0x2C,
+ 0x1E, 0xEF, 0x07, 0x15, 0x33, 0xA6, 0xDF, 0xB7
+ },
+ {
+ 0xC2, 0x2E, 0x15, 0xCF, 0xC5, 0xA3, 0xD1, 0x4B,
+ 0x64, 0xD1, 0x31, 0xF3, 0x5F, 0xB3, 0x5D, 0xD5,
+ 0xE6, 0xC5, 0x7D, 0xC4, 0xAF, 0xC5, 0x52, 0x27,
+ 0x75, 0x01, 0xEC, 0xA7, 0x64, 0xDA, 0x74, 0xBF
+ },
+ {
+ 0xAD, 0x68, 0x3E, 0x96, 0xB8, 0xAC, 0x65, 0x8C,
+ 0x4F, 0x3F, 0x10, 0xAD, 0x22, 0xD9, 0x9B, 0x07,
+ 0xCB, 0x5E, 0xF9, 0xE3, 0x1C, 0xBE, 0x11, 0xE7,
+ 0xF7, 0xDC, 0x29, 0xF2, 0xAE, 0xE5, 0x02, 0x4C
+ },
+ {
+ 0x78, 0xD3, 0xCE, 0xDA, 0x1C, 0xE0, 0x52, 0x93,
+ 0xF4, 0x30, 0xF6, 0x16, 0x7B, 0x33, 0xC9, 0x9F,
+ 0x0B, 0x1D, 0x6D, 0xAD, 0xE5, 0x21, 0x43, 0xC2,
+ 0x92, 0x55, 0x77, 0xC0, 0xBA, 0x82, 0x53, 0xEB
+ },
+ {
+ 0xE0, 0x06, 0x45, 0x63, 0x44, 0xF9, 0x0F, 0x50,
+ 0x1C, 0x25, 0x81, 0x3F, 0x9B, 0xE2, 0xA3, 0xF4,
+ 0x0B, 0x98, 0x74, 0xFA, 0x05, 0x63, 0x98, 0x1C,
+ 0xD4, 0x56, 0xEE, 0x8D, 0x44, 0x80, 0x7C, 0x93
+ },
+ {
+ 0x39, 0x08, 0xE8, 0xD5, 0x47, 0xC0, 0xAF, 0xB1,
+ 0x13, 0x49, 0x49, 0x46, 0x63, 0x04, 0xA1, 0x45,
+ 0x02, 0x7E, 0x6B, 0xB7, 0xA7, 0x4D, 0xD1, 0xC1,
+ 0x62, 0xCD, 0xF0, 0xBC, 0xF7, 0x72, 0x37, 0xE8
+ },
+ {
+ 0x1B, 0x6C, 0x87, 0xA3, 0x48, 0x38, 0xC7, 0xCD,
+ 0x5F, 0xD0, 0x89, 0x14, 0x22, 0x4E, 0x90, 0xC2,
+ 0x2A, 0xBF, 0x5A, 0x97, 0xB1, 0x06, 0x46, 0xD9,
+ 0x8C, 0x49, 0x16, 0xD3, 0xA8, 0x93, 0x9E, 0x62
+ },
+ {
+ 0xB0, 0xD3, 0x8F, 0x82, 0xF2, 0x48, 0x91, 0x69,
+ 0x52, 0xB3, 0x16, 0xB6, 0xD3, 0x6D, 0x9E, 0x02,
+ 0x2D, 0xF6, 0xEE, 0xCC, 0x26, 0xC7, 0x62, 0xA6,
+ 0x55, 0xCF, 0x5F, 0x0A, 0xE6, 0x49, 0xE2, 0xBD
+ },
+ {
+ 0x8D, 0x66, 0xFC, 0x9C, 0xED, 0xA5, 0xED, 0xDF,
+ 0xB1, 0xE0, 0x4D, 0x09, 0x6C, 0xA7, 0x0E, 0xF5,
+ 0x06, 0x50, 0xFB, 0x87, 0xCC, 0x6A, 0x9F, 0xFB,
+ 0xB3, 0xD2, 0x0B, 0xCE, 0x7B, 0x5A, 0x60, 0x74
+ },
+ {
+ 0x06, 0x43, 0x54, 0xE8, 0xE1, 0x1C, 0xF7, 0x13,
+ 0xB2, 0xC7, 0x2B, 0xA6, 0x7A, 0xC7, 0xD7, 0x6E,
+ 0x41, 0xBA, 0x61, 0xDB, 0x9C, 0x2D, 0xEA, 0x52,
+ 0x2E, 0x0B, 0xDA, 0x17, 0xCB, 0xA5, 0xE3, 0x92
+ },
+ {
+ 0xC8, 0xEF, 0x5F, 0x49, 0x8B, 0xD1, 0xBC, 0x70,
+ 0x7F, 0xBC, 0x7B, 0x5C, 0xBC, 0x2D, 0xFF, 0x04,
+ 0x93, 0x14, 0x4A, 0xC5, 0x27, 0x86, 0xDB, 0x3C,
+ 0x79, 0x3E, 0xF4, 0xAE, 0x8A, 0x83, 0x88, 0x47
+ },
+ {
+ 0x8A, 0x23, 0x97, 0xDF, 0x31, 0xE7, 0xF0, 0xCC,
+ 0x29, 0x0D, 0xA9, 0xA8, 0xBB, 0xE4, 0xF5, 0xF7,
+ 0xA3, 0xA1, 0x37, 0x50, 0x73, 0x0D, 0xB6, 0x2D,
+ 0xC2, 0x54, 0x0F, 0xDB, 0xD6, 0x18, 0x85, 0x89
+ },
+ {
+ 0xF1, 0x2D, 0x0B, 0x13, 0xC6, 0xAD, 0xFB, 0x3B,
+ 0xE5, 0x0A, 0x51, 0xEB, 0x6B, 0xAF, 0x65, 0xAB,
+ 0xFB, 0x17, 0x00, 0xBA, 0xA8, 0x7E, 0x52, 0x7D,
+ 0xBE, 0x3E, 0x67, 0x5A, 0x7A, 0x99, 0x46, 0x61
+ },
+ {
+ 0x10, 0x24, 0xC9, 0x40, 0xBE, 0x73, 0x41, 0x44,
+ 0x9B, 0x50, 0x10, 0x52, 0x2B, 0x50, 0x9F, 0x65,
+ 0xBB, 0xDC, 0x12, 0x87, 0xB4, 0x55, 0xC2, 0xBB,
+ 0x7F, 0x72, 0xB2, 0xC9, 0x2F, 0xD0, 0xD1, 0x89
+ },
+ {
+ 0x52, 0x60, 0x3B, 0x6C, 0xBF, 0xAD, 0x49, 0x66,
+ 0xCB, 0x04, 0x4C, 0xB2, 0x67, 0x56, 0x83, 0x85,
+ 0xCF, 0x35, 0xF2, 0x1E, 0x6C, 0x45, 0xCF, 0x30,
+ 0xAE, 0xD1, 0x98, 0x32, 0xCB, 0x51, 0xE9, 0xF5
+ },
+ {
+ 0xFF, 0xF2, 0x4D, 0x3C, 0xC7, 0x29, 0xD3, 0x95,
+ 0xDA, 0xF9, 0x78, 0xB0, 0x15, 0x73, 0x06, 0xCB,
+ 0x49, 0x57, 0x97, 0xE6, 0xC8, 0xDC, 0xA1, 0x73,
+ 0x1D, 0x2F, 0x6F, 0x81, 0xB8, 0x49, 0xBA, 0xAE
+ },
+ {
+ 0x41, 0xEE, 0xE9, 0x0D, 0x47, 0xEC, 0x27, 0x72,
+ 0xCD, 0x35, 0x2D, 0xFD, 0x67, 0xE0, 0x60, 0x5F,
+ 0xBD, 0xFC, 0x5F, 0xD6, 0xD8, 0x26, 0x45, 0x1E,
+ 0x3D, 0x06, 0x4D, 0x38, 0x28, 0xBD, 0x3B, 0xAE
+ },
+ {
+ 0x30, 0x0B, 0x6B, 0x36, 0xE5, 0x9F, 0x85, 0x1D,
+ 0xDD, 0xC2, 0x9B, 0xFA, 0x93, 0x08, 0x25, 0x20,
+ 0xCD, 0x77, 0xC5, 0x1E, 0x00, 0x7E, 0x00, 0xD2,
+ 0xD7, 0x8B, 0x26, 0xF4, 0xAF, 0x96, 0x15, 0x32
+ },
+ {
+ 0x9E, 0xF3, 0x03, 0x14, 0x83, 0x4E, 0x40, 0x1C,
+ 0x87, 0x1A, 0x20, 0x04, 0xE3, 0x8D, 0x5C, 0xE3,
+ 0x2E, 0xD2, 0x8E, 0x11, 0x37, 0xF1, 0x97, 0x0F,
+ 0x4F, 0x43, 0x78, 0xC7, 0x37, 0x06, 0x76, 0x3D
+ },
+ {
+ 0x3F, 0xBD, 0xCD, 0xE7, 0xB6, 0x43, 0x04, 0x02,
+ 0x5E, 0xC0, 0x58, 0x26, 0x09, 0x03, 0x1E, 0xC2,
+ 0x66, 0xD5, 0x0F, 0x56, 0x83, 0x5A, 0xE0, 0xCB,
+ 0x72, 0xD8, 0xCD, 0xB4, 0xCF, 0xAF, 0x44, 0x19
+ },
+ {
+ 0xE9, 0x0E, 0xAD, 0x3B, 0x98, 0x2B, 0x43, 0x5B,
+ 0x66, 0x36, 0x6A, 0x49, 0x6C, 0x3F, 0x8A, 0xE6,
+ 0x5B, 0x17, 0x61, 0x37, 0x00, 0xF5, 0x47, 0x67,
+ 0x3F, 0x62, 0x15, 0x35, 0x41, 0x91, 0x28, 0x64
+ },
+ {
+ 0xAB, 0xE3, 0x54, 0x7B, 0x33, 0x6D, 0x6E, 0x24,
+ 0x0D, 0x7F, 0xE6, 0x82, 0xD7, 0x4B, 0x9C, 0xC7,
+ 0xE8, 0xD7, 0xF9, 0xB5, 0x66, 0x48, 0x58, 0xB9,
+ 0x4D, 0xF5, 0x9E, 0x9F, 0xC3, 0x30, 0xD9, 0xE5
+ },
+ {
+ 0xB2, 0x99, 0x64, 0x20, 0x95, 0xB8, 0x28, 0x6C,
+ 0x52, 0x1C, 0xDB, 0x21, 0xED, 0x0F, 0xE0, 0x57,
+ 0x27, 0x80, 0x21, 0xBB, 0x40, 0x38, 0xEB, 0x5A,
+ 0x3D, 0x79, 0x54, 0x2F, 0x5D, 0x75, 0x1F, 0x54
+ },
+ {
+ 0xE4, 0xD7, 0x58, 0x35, 0x9F, 0x08, 0x67, 0x93,
+ 0xA8, 0x37, 0x54, 0xAC, 0xA6, 0x96, 0x8C, 0x3E,
+ 0x9F, 0xD9, 0x4B, 0x40, 0x49, 0x7F, 0x2E, 0xC2,
+ 0x24, 0xA2, 0x91, 0x60, 0x63, 0xA2, 0x14, 0xA3
+ },
+ {
+ 0x59, 0xA3, 0x04, 0xFC, 0x03, 0xAB, 0x75, 0xD5,
+ 0x57, 0xDB, 0x04, 0xEB, 0xD0, 0x2D, 0xD4, 0xC6,
+ 0xB8, 0x10, 0xA1, 0x38, 0xBB, 0xFE, 0xEA, 0x5D,
+ 0xFC, 0xEE, 0xAA, 0x2B, 0x75, 0xB0, 0x64, 0x91
+ },
+ {
+ 0x39, 0x95, 0x10, 0x22, 0x15, 0xF5, 0xFE, 0x92,
+ 0x10, 0xEB, 0x30, 0xD9, 0x52, 0xD8, 0xC9, 0x19,
+ 0x58, 0x9E, 0x71, 0x45, 0xFC, 0xD4, 0x95, 0xEA,
+ 0x78, 0xD0, 0x2B, 0x9C, 0x14, 0x8F, 0xAF, 0x09
+ },
+ {
+ 0x47, 0x2E, 0xE7, 0x11, 0x56, 0x35, 0x06, 0xA5,
+ 0xF0, 0x08, 0x3F, 0xE8, 0x2B, 0x08, 0xB9, 0x92,
+ 0x3C, 0xF6, 0xC8, 0x40, 0x4D, 0x0C, 0xBA, 0xCB,
+ 0xF8, 0x48, 0x64, 0xF6, 0x48, 0x54, 0x2A, 0xC0
+ },
+ {
+ 0x68, 0xFD, 0xB8, 0x2A, 0xDA, 0xE7, 0x9B, 0xEF,
+ 0x59, 0x0A, 0xBA, 0x62, 0xD7, 0xAC, 0x55, 0x32,
+ 0x12, 0x06, 0x1C, 0x36, 0xE3, 0x6F, 0x12, 0xC0,
+ 0xEF, 0xA2, 0x9A, 0x17, 0x62, 0xDE, 0x3B, 0x6A
+ },
+ {
+ 0x75, 0x85, 0xC0, 0x77, 0x33, 0x83, 0xF1, 0x74,
+ 0xFD, 0x66, 0x65, 0x49, 0xA8, 0x35, 0x2B, 0x30,
+ 0x5B, 0xF6, 0x85, 0x5B, 0xC9, 0x8B, 0xEA, 0x28,
+ 0xC3, 0x91, 0xB3, 0xC0, 0x34, 0xDA, 0x5A, 0x5A
+ },
+ {
+ 0xAC, 0xC5, 0x75, 0xFE, 0x2C, 0xD7, 0xBA, 0x2A,
+ 0x31, 0xFC, 0x7D, 0x67, 0x0A, 0x92, 0x34, 0xAF,
+ 0x68, 0x50, 0x33, 0x86, 0xE9, 0x59, 0x07, 0x3D,
+ 0x16, 0xA8, 0x1B, 0x33, 0xB9, 0x22, 0xB5, 0x0E
+ },
+ {
+ 0x9E, 0xC7, 0xD2, 0x99, 0x59, 0x43, 0xD3, 0x9D,
+ 0x6B, 0x97, 0x14, 0x93, 0xB8, 0x97, 0xA0, 0xEE,
+ 0x2D, 0x33, 0x92, 0xA7, 0x2D, 0xB8, 0x75, 0xC2,
+ 0x40, 0x5D, 0x35, 0x71, 0x78, 0xFB, 0x69, 0x11
+ },
+ {
+ 0x2D, 0x7E, 0xF1, 0x94, 0x01, 0x42, 0x5A, 0xBA,
+ 0x45, 0x0E, 0x82, 0xD3, 0x6D, 0x0F, 0xE7, 0xB2,
+ 0x08, 0x5E, 0xA0, 0xAF, 0x60, 0x45, 0xA5, 0x99,
+ 0x4C, 0xF4, 0x31, 0xEA, 0x59, 0x93, 0x9C, 0xC9
+ },
+ {
+ 0xF3, 0x2F, 0xD8, 0x55, 0xF0, 0x11, 0xC7, 0x18,
+ 0x02, 0x7F, 0x2E, 0xBE, 0x37, 0x7D, 0x69, 0x39,
+ 0xF1, 0x23, 0x70, 0xCA, 0xFF, 0x15, 0x1C, 0x1E,
+ 0x5A, 0xCE, 0x43, 0x8D, 0x70, 0x3C, 0x6D, 0x9F
+ },
+ {
+ 0xB2, 0xBD, 0x83, 0xD2, 0x31, 0x0D, 0x3D, 0x7B,
+ 0x1D, 0x2D, 0x5A, 0xAF, 0x43, 0x59, 0xFA, 0xE2,
+ 0x86, 0x12, 0x96, 0x27, 0x19, 0xFD, 0xDE, 0x4D,
+ 0xDA, 0xF6, 0x9E, 0x78, 0x20, 0xF3, 0x3F, 0x61
+ },
+ {
+ 0x1A, 0x7A, 0x9D, 0x0F, 0x44, 0xDD, 0xFA, 0x7F,
+ 0xC2, 0xF4, 0x77, 0x0C, 0xAD, 0x74, 0x22, 0xFA,
+ 0x6C, 0x4E, 0x37, 0xE6, 0xCB, 0x03, 0x6D, 0x89,
+ 0x9E, 0x10, 0x27, 0x50, 0xE5, 0x94, 0xFF, 0xCD
+ },
+ {
+ 0xDC, 0x69, 0xF6, 0x14, 0x1C, 0x8E, 0x10, 0x3F,
+ 0xF6, 0x1F, 0x62, 0x98, 0xA2, 0xC4, 0x4F, 0x52,
+ 0xD1, 0x47, 0x36, 0x6D, 0xDB, 0xD9, 0xC7, 0x9C,
+ 0xC3, 0x08, 0xFE, 0x84, 0x33, 0x6A, 0x95, 0x64
+ },
+ {
+ 0xE3, 0x4E, 0xD4, 0x17, 0xB0, 0x79, 0x1D, 0x9A,
+ 0x77, 0xEE, 0x1E, 0x50, 0xCC, 0x2C, 0x20, 0x7E,
+ 0x54, 0x0C, 0x77, 0x14, 0x04, 0x21, 0xC4, 0x6C,
+ 0xE0, 0x86, 0x28, 0x78, 0xAA, 0xEB, 0x27, 0x09
+ },
+ {
+ 0x21, 0x74, 0x42, 0x5C, 0x8C, 0xCA, 0xE3, 0x98,
+ 0xC4, 0xFF, 0x06, 0xF8, 0x48, 0x99, 0x1C, 0x5E,
+ 0x9B, 0xC0, 0xF3, 0x46, 0x11, 0x11, 0x70, 0x6F,
+ 0xB9, 0x5D, 0x0B, 0xE1, 0xC6, 0x8E, 0x47, 0x60
+ },
+ {
+ 0x18, 0x94, 0x58, 0x2A, 0x8A, 0x25, 0xFE, 0x8F,
+ 0x84, 0x7A, 0x4A, 0x03, 0x25, 0x74, 0xB7, 0x7B,
+ 0x8B, 0x36, 0xBF, 0x19, 0x99, 0x75, 0x26, 0xBB,
+ 0x4B, 0xC8, 0x5F, 0x38, 0x24, 0x53, 0x7F, 0xEB
+ },
+ {
+ 0x17, 0xED, 0x18, 0x8A, 0xE3, 0xC9, 0x53, 0xD6,
+ 0x55, 0x44, 0x59, 0x83, 0xB8, 0x32, 0x5B, 0xAF,
+ 0xFF, 0x32, 0xE2, 0x22, 0xB2, 0xDF, 0xEB, 0x16,
+ 0xE8, 0x61, 0x7A, 0xBF, 0x86, 0xEE, 0x7C, 0xC5
+ },
+ {
+ 0xF1, 0x48, 0x9A, 0xD1, 0xC3, 0x54, 0xCD, 0xE9,
+ 0x78, 0x92, 0x37, 0xEA, 0x6D, 0xBF, 0x67, 0xFC,
+ 0x1E, 0x44, 0xD1, 0xAC, 0xC8, 0xDC, 0x66, 0xAD,
+ 0x83, 0x87, 0x27, 0xF4, 0x7D, 0x9A, 0x91, 0xFE
+ },
+ {
+ 0x36, 0x7F, 0x22, 0x16, 0x5B, 0x8B, 0x66, 0xE9,
+ 0x7F, 0x66, 0x70, 0xF3, 0x4E, 0xBA, 0x27, 0x49,
+ 0xD2, 0x64, 0x3B, 0x21, 0xBE, 0xAD, 0xAD, 0xFE,
+ 0xFE, 0xA2, 0x57, 0x4B, 0x7C, 0x9B, 0x21, 0x96
+ },
+ {
+ 0x3D, 0x8D, 0xFE, 0xA1, 0x7E, 0xEA, 0x5D, 0x64,
+ 0x5A, 0xC1, 0xD4, 0x1A, 0x5B, 0x59, 0x22, 0x6C,
+ 0x48, 0x6C, 0x36, 0xBD, 0x77, 0xED, 0x44, 0xBB,
+ 0x34, 0x91, 0x70, 0xD0, 0x80, 0xE3, 0x0E, 0x68
+ },
+ {
+ 0x41, 0x15, 0xF8, 0x9E, 0x0B, 0x3B, 0x5C, 0x8F,
+ 0x61, 0x22, 0xC0, 0x25, 0x00, 0x17, 0x1D, 0xCF,
+ 0xFB, 0xCE, 0xA4, 0x66, 0x2A, 0x8C, 0x5F, 0x8C,
+ 0x1C, 0x01, 0xA9, 0xCA, 0x7B, 0x10, 0x27, 0xBB
+ },
+ {
+ 0xED, 0x6E, 0x91, 0x0B, 0x96, 0x02, 0x55, 0xD7,
+ 0xD7, 0x92, 0xEB, 0xE6, 0x7F, 0x26, 0x0A, 0x14,
+ 0x3C, 0xFA, 0xC1, 0x05, 0x1D, 0xFC, 0x05, 0x90,
+ 0x25, 0xEE, 0x0C, 0x1B, 0xFC, 0xBC, 0x56, 0x81
+ },
+ {
+ 0x55, 0x8F, 0xA8, 0xAF, 0xA1, 0x2B, 0xBE, 0xE5,
+ 0x4A, 0xF7, 0x8F, 0x6B, 0x74, 0x45, 0xF9, 0x96,
+ 0x65, 0xD4, 0xE3, 0x56, 0xBC, 0x07, 0xD3, 0xEF,
+ 0xFD, 0x8F, 0xD6, 0x5A, 0xB9, 0xC7, 0x47, 0x16
+ },
+ {
+ 0x5B, 0x60, 0x12, 0x76, 0x20, 0x53, 0xB8, 0x73,
+ 0x4A, 0xF0, 0xE5, 0x55, 0xE6, 0xA2, 0xBB, 0x4F,
+ 0xD4, 0x84, 0x0A, 0xF3, 0xB0, 0x4F, 0xCF, 0x63,
+ 0x50, 0xA2, 0xB8, 0xA5, 0x1B, 0x67, 0x96, 0xAD
+ },
+ {
+ 0xAB, 0x7A, 0xCC, 0xA5, 0xD7, 0x77, 0x10, 0xBA,
+ 0xD3, 0x7B, 0xA0, 0xFF, 0x4C, 0xEA, 0xE2, 0x7E,
+ 0x84, 0x71, 0x79, 0xF7, 0xFD, 0x7A, 0xEC, 0x88,
+ 0x69, 0xC6, 0x49, 0xB3, 0x3F, 0x8D, 0x25, 0x77
+ },
+ {
+ 0xFF, 0x77, 0x30, 0xB4, 0x74, 0xEC, 0x21, 0x45,
+ 0xA9, 0x2D, 0xD1, 0xCF, 0xFE, 0x45, 0xC3, 0x42,
+ 0xC6, 0xFD, 0x6B, 0xAC, 0x58, 0x0F, 0xF9, 0x5A,
+ 0x75, 0xED, 0xA3, 0xBF, 0x90, 0xEB, 0x4F, 0x01
+ },
+ {
+ 0xD1, 0x0F, 0x06, 0x1D, 0x5B, 0x9C, 0xB4, 0x4E,
+ 0xE0, 0x78, 0xA9, 0x6B, 0x33, 0x18, 0x57, 0x9E,
+ 0x5E, 0xF5, 0x0A, 0xEF, 0x3E, 0xD9, 0x6E, 0x4F,
+ 0x62, 0x14, 0x9B, 0x2E, 0x9F, 0x7C, 0x66, 0x0C
+ },
+ {
+ 0x67, 0xD2, 0x2B, 0x8E, 0xDF, 0x20, 0x01, 0xD8,
+ 0x64, 0x22, 0x13, 0x6A, 0xC6, 0x51, 0x6C, 0xF3,
+ 0x9F, 0x7F, 0xC6, 0xA7, 0x02, 0x98, 0x92, 0xFD,
+ 0x75, 0xC9, 0x87, 0x90, 0x96, 0x4A, 0x72, 0x0B
+ },
+ {
+ 0x7A, 0x5E, 0xC5, 0xBA, 0x76, 0x25, 0x9B, 0x07,
+ 0xB4, 0xDA, 0x03, 0xF3, 0x81, 0xFE, 0x7B, 0xEA,
+ 0x48, 0x65, 0xC8, 0x6C, 0x42, 0x4A, 0xBA, 0xA0,
+ 0xDD, 0x1E, 0xCF, 0x74, 0xF8, 0x7D, 0x2A, 0xC0
+ },
+ {
+ 0xE0, 0xFF, 0x60, 0xD6, 0x90, 0x29, 0xE6, 0xBD,
+ 0x1C, 0x15, 0x95, 0x3E, 0x91, 0x50, 0x9C, 0x0C,
+ 0x59, 0xED, 0x5D, 0xA5, 0x00, 0x01, 0x99, 0xF2,
+ 0x16, 0xD2, 0x9F, 0x96, 0x07, 0x9C, 0x2F, 0xEF
+ },
+ {
+ 0xFC, 0x13, 0xEA, 0xD8, 0x41, 0x01, 0x8F, 0x59,
+ 0x90, 0x3B, 0x40, 0xF2, 0x02, 0x0C, 0x66, 0x38,
+ 0xA6, 0x6A, 0x54, 0xC3, 0xA3, 0x38, 0x41, 0x4D,
+ 0x97, 0xA5, 0xC3, 0x94, 0xF3, 0x26, 0x6F, 0x33
+ },
+ {
+ 0x0C, 0x2F, 0x62, 0xB8, 0x98, 0xFB, 0x2F, 0x63,
+ 0x61, 0x7E, 0x78, 0x73, 0x45, 0x26, 0x3C, 0xB9,
+ 0xCF, 0x60, 0x65, 0x4B, 0x55, 0x3B, 0x20, 0x3E,
+ 0xE4, 0x9D, 0xCB, 0xB8, 0xF2, 0xA6, 0xAF, 0xAC
+ },
+ {
+ 0xD7, 0xD6, 0xCB, 0x55, 0x2A, 0xEB, 0x36, 0xEB,
+ 0x96, 0xB1, 0xD5, 0xE0, 0x52, 0xF8, 0xD9, 0x21,
+ 0xC3, 0x24, 0x5A, 0x97, 0x0D, 0x0B, 0xC8, 0x41,
+ 0x0C, 0xD6, 0x5E, 0xA1, 0x04, 0xC8, 0xE7, 0x79
+ },
+ {
+ 0xB7, 0x14, 0x1F, 0x30, 0x5E, 0xFD, 0xFE, 0xE5,
+ 0x56, 0xBD, 0x13, 0xE0, 0x40, 0x0D, 0x1E, 0x8C,
+ 0xFD, 0x65, 0x48, 0xBF, 0x81, 0xEE, 0x5D, 0x15,
+ 0x32, 0x7E, 0x49, 0x95, 0xCA, 0x8A, 0xD6, 0xFD
+ },
+ {
+ 0xB6, 0xB6, 0x38, 0xD2, 0x2B, 0x7A, 0x12, 0x82,
+ 0x53, 0x74, 0xF7, 0x03, 0x48, 0xD7, 0x44, 0x8D,
+ 0x4E, 0x7D, 0x90, 0x8C, 0xF6, 0xE7, 0xBB, 0xEF,
+ 0x8C, 0x93, 0xEF, 0x67, 0x9B, 0x2A, 0x54, 0x78
+ },
+ {
+ 0x0D, 0xF4, 0x58, 0x56, 0x41, 0xFA, 0x09, 0xF6,
+ 0xCB, 0xA4, 0xCC, 0x16, 0x5A, 0x10, 0xAD, 0xDE,
+ 0x34, 0xF8, 0x0D, 0x42, 0x5A, 0x70, 0xDB, 0x67,
+ 0xE2, 0xFD, 0x23, 0x7B, 0x62, 0x7F, 0x43, 0x8A
+ },
+ {
+ 0x10, 0x6B, 0x2B, 0x35, 0x4D, 0x95, 0xAC, 0xEC,
+ 0xD0, 0xD9, 0x58, 0x8F, 0xBC, 0x23, 0x1F, 0x8B,
+ 0xEA, 0x2E, 0x94, 0xEA, 0x66, 0x2D, 0xDD, 0x3F,
+ 0x13, 0x9E, 0x1B, 0x67, 0x87, 0x46, 0x1E, 0xED
+ },
+ {
+ 0xAE, 0x5C, 0x69, 0xEE, 0xFE, 0x90, 0x89, 0xB2,
+ 0x9C, 0x6C, 0x1A, 0x23, 0x70, 0xD2, 0x05, 0x52,
+ 0xBA, 0x40, 0xC3, 0xD5, 0xE3, 0x71, 0x3C, 0x12,
+ 0xDE, 0xFC, 0xAE, 0x99, 0x7F, 0x43, 0x3E, 0xCD
+ },
+ {
+ 0x1A, 0xAE, 0xF5, 0x5D, 0x4F, 0xA8, 0x92, 0xB6,
+ 0x35, 0xFB, 0x2A, 0x7A, 0x25, 0xF9, 0xA8, 0xE0,
+ 0x3B, 0x9F, 0xFB, 0x08, 0x2A, 0xE9, 0xC0, 0x7C,
+ 0x20, 0x42, 0xA0, 0x49, 0xC6, 0x51, 0x5E, 0x45
+ },
+ {
+ 0x29, 0x7D, 0xAA, 0xC4, 0xD5, 0x4D, 0xC4, 0x1C,
+ 0x83, 0xE3, 0x23, 0x94, 0x59, 0x9F, 0x17, 0x1C,
+ 0xDA, 0xA9, 0xDD, 0xB7, 0x17, 0x26, 0xDA, 0x4E,
+ 0xCE, 0x3C, 0xCF, 0x95, 0xC1, 0x1F, 0x56, 0xDF
+ },
+ {
+ 0x2C, 0x45, 0xAC, 0xF4, 0x91, 0xEC, 0x2F, 0x4B,
+ 0x7E, 0x30, 0x9E, 0x7E, 0xDD, 0x81, 0x5B, 0xE5,
+ 0xA5, 0x4C, 0x44, 0x58, 0xD1, 0xA5, 0x7C, 0x4F,
+ 0x9B, 0x76, 0x3B, 0x0C, 0x67, 0x18, 0xD4, 0x3E
+ },
+ {
+ 0x2F, 0x92, 0xF9, 0x01, 0x70, 0xD3, 0xAE, 0x95,
+ 0xAB, 0xFA, 0xC3, 0xA6, 0x98, 0x9A, 0x2A, 0x60,
+ 0xCB, 0x28, 0xB8, 0x58, 0x78, 0x2B, 0xE7, 0xEA,
+ 0x17, 0x9B, 0x48, 0xA7, 0x27, 0x6D, 0xD8, 0x60
+ },
+ {
+ 0xB4, 0x01, 0xE8, 0x4B, 0x15, 0xAC, 0xC4, 0x70,
+ 0x93, 0x6D, 0x6E, 0x37, 0xF7, 0x88, 0x83, 0x33,
+ 0x09, 0x27, 0x31, 0x13, 0x3B, 0x25, 0x1B, 0xEA,
+ 0x22, 0x16, 0x58, 0xCA, 0x19, 0xA7, 0x56, 0x69
+ },
+ {
+ 0xF8, 0xB3, 0x40, 0xD2, 0xB9, 0xB3, 0x3D, 0x43,
+ 0xA0, 0xA6, 0x6F, 0x34, 0x97, 0x82, 0x0A, 0xFA,
+ 0xAE, 0xE4, 0x34, 0xC4, 0xE3, 0xC0, 0xC1, 0x7E,
+ 0x89, 0x8B, 0x83, 0x01, 0xC5, 0x7A, 0x26, 0xBD
+ },
+ {
+ 0x56, 0x6D, 0xA2, 0x83, 0x99, 0x03, 0x89, 0x13,
+ 0x8A, 0xA6, 0xF2, 0xAA, 0xA3, 0xB9, 0xE4, 0x0C,
+ 0xBF, 0x90, 0x84, 0x0E, 0xC7, 0x62, 0xBD, 0x96,
+ 0xB7, 0xE3, 0x3A, 0x31, 0x13, 0xB1, 0x01, 0x08
+ },
+ {
+ 0x34, 0x06, 0x72, 0xB7, 0x04, 0x67, 0x60, 0x42,
+ 0xC9, 0xBF, 0x3F, 0x33, 0x7B, 0xA7, 0x9F, 0x11,
+ 0x33, 0x6A, 0xEB, 0xB5, 0xEC, 0x5D, 0x31, 0xDF,
+ 0x54, 0xEB, 0x6A, 0xD3, 0xB0, 0x43, 0x04, 0x42
+ },
+ {
+ 0x50, 0x50, 0xB7, 0x3B, 0x93, 0x16, 0xEE, 0xA2,
+ 0xF1, 0x49, 0xBF, 0xFD, 0x22, 0xAE, 0xE3, 0x84,
+ 0xDC, 0x54, 0x03, 0xB1, 0x8E, 0x16, 0xFA, 0x88,
+ 0x82, 0x5E, 0x18, 0x16, 0x09, 0x49, 0x6F, 0xD2
+ },
+ {
+ 0x13, 0x65, 0xCC, 0x6F, 0xB9, 0x26, 0x0E, 0x86,
+ 0x88, 0x9B, 0x3A, 0xFB, 0xD1, 0xC8, 0xBC, 0x12,
+ 0x92, 0x31, 0x97, 0x71, 0x5D, 0xB2, 0x66, 0xCC,
+ 0x7A, 0x01, 0xCA, 0x57, 0x15, 0x9F, 0x75, 0x96
+ },
+ {
+ 0x29, 0x46, 0x6F, 0x51, 0xC0, 0x11, 0xFD, 0x10,
+ 0x18, 0x14, 0x94, 0xA9, 0x37, 0x9B, 0x61, 0x59,
+ 0xB8, 0x08, 0xAE, 0x0F, 0xCB, 0x01, 0x61, 0xF8,
+ 0xF0, 0x79, 0x09, 0xFF, 0x04, 0x1B, 0x19, 0x65
+ },
+ {
+ 0x65, 0x91, 0xA3, 0xC3, 0xC7, 0x67, 0xB3, 0x8D,
+ 0x80, 0x5E, 0xD3, 0xF7, 0xEB, 0x67, 0x63, 0xE8,
+ 0xB3, 0xD2, 0xD6, 0x42, 0xE7, 0x30, 0x77, 0x45,
+ 0xCD, 0x34, 0x18, 0xEF, 0xF6, 0x9A, 0x19, 0xED
+ },
+ {
+ 0x1D, 0x84, 0xB0, 0x4B, 0x13, 0x38, 0xB0, 0xD2,
+ 0xE3, 0xC9, 0x8F, 0x7A, 0xEA, 0x3E, 0x98, 0xEF,
+ 0xFC, 0x53, 0x0A, 0x50, 0x44, 0xB9, 0x3B, 0x96,
+ 0xC6, 0x7E, 0xE3, 0x79, 0xD6, 0x2E, 0x81, 0x5F
+ },
+ {
+ 0x6F, 0xA2, 0x95, 0x27, 0x25, 0x32, 0xE9, 0x83,
+ 0xE1, 0x66, 0xB1, 0x2E, 0x49, 0x99, 0xC0, 0x52,
+ 0xF8, 0x9D, 0x9F, 0x30, 0xAE, 0x14, 0x81, 0xF3,
+ 0xD6, 0x0E, 0xAE, 0x85, 0xF8, 0xEE, 0x17, 0x8A
+ },
+ {
+ 0x4E, 0xD8, 0xCA, 0xA9, 0x8E, 0xC3, 0x9F, 0x6A,
+ 0x62, 0x9F, 0x9A, 0x65, 0x4A, 0x44, 0x7E, 0x7E,
+ 0x3E, 0x4F, 0xAE, 0xEC, 0xF3, 0x4D, 0xCF, 0x65,
+ 0x8D, 0x2D, 0x4B, 0x98, 0xB7, 0xA2, 0xEC, 0x1A
+ },
+ {
+ 0xCF, 0xAB, 0x82, 0x99, 0xA0, 0xDA, 0x0C, 0x2A,
+ 0x7E, 0x8F, 0xF5, 0x4D, 0x0A, 0x67, 0x6D, 0x14,
+ 0x1A, 0xB2, 0x6B, 0xC0, 0x01, 0x2E, 0x5F, 0x66,
+ 0x8E, 0x85, 0xD8, 0x14, 0xBC, 0x98, 0x88, 0xB0
+ },
+ {
+ 0xA6, 0x26, 0x54, 0x3C, 0x27, 0x1F, 0xCC, 0xC3,
+ 0xE4, 0x45, 0x0B, 0x48, 0xD6, 0x6B, 0xC9, 0xCB,
+ 0xDE, 0xB2, 0x5E, 0x5D, 0x07, 0x7A, 0x62, 0x13,
+ 0xCD, 0x90, 0xCB, 0xBD, 0x0F, 0xD2, 0x20, 0x76
+ },
+ {
+ 0x05, 0xCF, 0x3A, 0x90, 0x04, 0x91, 0x16, 0xDC,
+ 0x60, 0xEF, 0xC3, 0x15, 0x36, 0xAA, 0xA3, 0xD1,
+ 0x67, 0x76, 0x29, 0x94, 0x89, 0x28, 0x76, 0xDC,
+ 0xB7, 0xEF, 0x3F, 0xBE, 0xCD, 0x74, 0x49, 0xC0
+ },
+ {
+ 0xCC, 0xD6, 0x1C, 0x92, 0x6C, 0xC1, 0xE5, 0xE9,
+ 0x12, 0x8C, 0x02, 0x1C, 0x0C, 0x6E, 0x92, 0xAE,
+ 0xFC, 0x4F, 0xFB, 0xDE, 0x39, 0x4D, 0xD6, 0xF3,
+ 0xB7, 0xD8, 0x7A, 0x8C, 0xED, 0x89, 0x60, 0x14
+ },
+ {
+ 0x3F, 0xFA, 0x4F, 0x6D, 0xAF, 0xA5, 0x7F, 0x1C,
+ 0x50, 0xF1, 0xAF, 0xA4, 0xF8, 0x12, 0x92, 0xAE,
+ 0x71, 0xA0, 0x6F, 0xE4, 0xF8, 0xFF, 0x46, 0xC5,
+ 0x1D, 0x32, 0xFF, 0x26, 0x13, 0x48, 0x9F, 0x2B
+ },
+ {
+ 0x19, 0xD3, 0x92, 0x1C, 0xFC, 0x0F, 0x1A, 0x2B,
+ 0xB8, 0x13, 0xB3, 0xDF, 0xA9, 0x6D, 0xF9, 0x0E,
+ 0x2C, 0x6B, 0x87, 0xD7, 0x8E, 0x92, 0x38, 0xF8,
+ 0x5B, 0xBC, 0x77, 0xAE, 0x9A, 0x73, 0xF9, 0x8F
+ },
+ {
+ 0xF5, 0xC9, 0x16, 0xFF, 0x2B, 0xAD, 0xDE, 0x3E,
+ 0x29, 0xA5, 0xF9, 0x40, 0x23, 0x3E, 0xA3, 0x40,
+ 0x07, 0xD8, 0xF1, 0x82, 0xA4, 0x8A, 0x80, 0x8B,
+ 0x46, 0xBB, 0x80, 0x58, 0x00, 0x3F, 0x19, 0x03
+ },
+ {
+ 0x6B, 0xA0, 0x7A, 0x1A, 0xF7, 0x58, 0xE6, 0x82,
+ 0xD3, 0xE0, 0x9A, 0xDD, 0x2D, 0x3D, 0xCD, 0xF3,
+ 0x5D, 0x95, 0x53, 0xF6, 0x79, 0x98, 0x54, 0xA2,
+ 0x7E, 0x53, 0x60, 0x63, 0xC5, 0x7F, 0x81, 0xA5
+ },
+ {
+ 0xB7, 0x83, 0x78, 0xFB, 0x44, 0x6C, 0x54, 0x4B,
+ 0x04, 0xD4, 0xA1, 0x52, 0xAC, 0x49, 0x57, 0x31,
+ 0x61, 0xB3, 0xDD, 0xEB, 0xF6, 0x93, 0x86, 0x77,
+ 0x0A, 0x55, 0xA7, 0xD4, 0x7B, 0x88, 0x0E, 0x5D
+ },
+ {
+ 0xB5, 0x19, 0x53, 0x8F, 0xE1, 0x62, 0x6F, 0x0C,
+ 0x59, 0x59, 0x45, 0xAD, 0xA5, 0x8A, 0x34, 0x4F,
+ 0xAA, 0xC0, 0x06, 0x17, 0x61, 0xCC, 0x9D, 0x4A,
+ 0x84, 0x14, 0x19, 0xBD, 0x32, 0xEE, 0xC0, 0xD9
+ },
+ {
+ 0x96, 0xE4, 0x88, 0xB0, 0x27, 0x89, 0x64, 0x13,
+ 0xF4, 0x03, 0x4B, 0x03, 0x54, 0xF4, 0x84, 0x84,
+ 0xF6, 0xCF, 0xC1, 0x0F, 0x8E, 0xC5, 0x7B, 0x02,
+ 0x6F, 0xD2, 0x1A, 0x3B, 0x88, 0x36, 0x1A, 0x74
+ },
+ {
+ 0x77, 0x0C, 0x8A, 0x5F, 0x47, 0xBF, 0xD7, 0x69,
+ 0xCE, 0xD3, 0x5A, 0x71, 0xAF, 0xC3, 0xCA, 0x1F,
+ 0xF4, 0xC1, 0xF1, 0xE7, 0xCC, 0x3D, 0x23, 0x56,
+ 0xDE, 0x94, 0x50, 0x04, 0x36, 0x8D, 0x81, 0x45
+ },
+ {
+ 0x6D, 0xF9, 0xD8, 0xD0, 0xD3, 0xA8, 0xD9, 0x8C,
+ 0x83, 0x50, 0xD7, 0x16, 0x2B, 0xD1, 0x55, 0x79,
+ 0xD5, 0x70, 0x7A, 0xDD, 0x76, 0x11, 0xA0, 0x0E,
+ 0xEB, 0x6C, 0xA5, 0x74, 0x3E, 0xD7, 0x8C, 0xB7
+ },
+ {
+ 0x4F, 0x0F, 0xE8, 0xFC, 0x17, 0x90, 0x15, 0x91,
+ 0xCF, 0x34, 0x87, 0x30, 0xE1, 0x87, 0xDE, 0x52,
+ 0x3D, 0x6D, 0x75, 0x68, 0xC1, 0xFB, 0xD8, 0x24,
+ 0x85, 0x91, 0x39, 0x85, 0xEB, 0x67, 0x97, 0x1C
+ },
+ {
+ 0x0E, 0xF3, 0xBB, 0x35, 0xCF, 0x37, 0x2B, 0xD9,
+ 0x4E, 0x3F, 0x80, 0xEE, 0xCE, 0xBD, 0x50, 0xEF,
+ 0x0D, 0x03, 0x08, 0xE0, 0x1E, 0x0E, 0xD6, 0xDE,
+ 0x0F, 0x5A, 0x8A, 0x8C, 0x81, 0x8A, 0x00, 0x74
+ },
+ {
+ 0xC0, 0x38, 0xD3, 0xE8, 0x09, 0xA5, 0xE3, 0xA5,
+ 0x8D, 0xB2, 0xF9, 0x1C, 0x15, 0xAE, 0x12, 0x43,
+ 0x95, 0x78, 0xF7, 0x54, 0x85, 0xCD, 0x84, 0xF5,
+ 0x56, 0xC6, 0x97, 0x1E, 0x8E, 0x25, 0x06, 0x20
+ },
+ {
+ 0xCE, 0x39, 0x9A, 0x0F, 0x08, 0x27, 0x7D, 0x8D,
+ 0x48, 0x16, 0x09, 0x50, 0x60, 0xEB, 0xBF, 0x33,
+ 0xDA, 0x01, 0x6F, 0xB4, 0x3A, 0x6C, 0x35, 0x6D,
+ 0x5A, 0x3F, 0xE4, 0xBB, 0x57, 0x4C, 0x5E, 0x7B
+ },
+ {
+ 0x86, 0x9F, 0x7E, 0x31, 0x6B, 0x19, 0x4F, 0x95,
+ 0x31, 0xBC, 0xAF, 0x33, 0xF7, 0x91, 0x3F, 0x1B,
+ 0x9C, 0xFC, 0x6B, 0xB5, 0xDC, 0xF8, 0x6B, 0x69,
+ 0x2B, 0xF8, 0xCA, 0xB2, 0x9B, 0x8A, 0xA9, 0x6F
+ },
+ {
+ 0x32, 0x7D, 0xFA, 0x46, 0x44, 0x59, 0xD9, 0xE4,
+ 0x8F, 0x5E, 0x55, 0xC7, 0xF5, 0xBA, 0xA6, 0x8F,
+ 0xC4, 0xA2, 0x5A, 0xD6, 0x22, 0xBC, 0x7B, 0xF0,
+ 0x1A, 0xCA, 0x82, 0xFD, 0x5E, 0x72, 0x31, 0x4C
+ },
+ {
+ 0xE0, 0x0D, 0xAD, 0x31, 0x51, 0xB9, 0x08, 0x5E,
+ 0xAE, 0x78, 0x69, 0x84, 0xFE, 0x20, 0x73, 0x52,
+ 0x32, 0xB7, 0xFF, 0x7F, 0x1B, 0x1D, 0xB7, 0x96,
+ 0x1F, 0xD0, 0xD0, 0xE0, 0xF6, 0x05, 0xDB, 0x9A
+ },
+ {
+ 0x07, 0x6F, 0x64, 0x45, 0x20, 0xD0, 0xB4, 0x73,
+ 0x2D, 0x6C, 0x53, 0x1C, 0x93, 0x49, 0x08, 0x90,
+ 0x26, 0x93, 0x6D, 0x99, 0x82, 0x04, 0x61, 0xDA,
+ 0x87, 0x74, 0x9A, 0x52, 0x0F, 0xBE, 0x90, 0xCE
+ },
+ {
+ 0xB4, 0x41, 0x4C, 0xA1, 0x37, 0x3B, 0xE4, 0x6F,
+ 0x15, 0xCE, 0xA6, 0xB1, 0x25, 0x5A, 0x7D, 0x18,
+ 0x86, 0xC6, 0xFD, 0xB0, 0x8E, 0xD5, 0xAF, 0x96,
+ 0x57, 0xD5, 0xAA, 0xC3, 0x17, 0xDE, 0x3A, 0x29
+ },
+ {
+ 0x8D, 0x1A, 0xB0, 0x26, 0x3D, 0xAB, 0x7B, 0x86,
+ 0xEC, 0xEE, 0x21, 0x91, 0x62, 0xD9, 0x99, 0xA0,
+ 0x12, 0x45, 0x57, 0x22, 0x69, 0xDE, 0x31, 0x10,
+ 0x0E, 0x5D, 0x88, 0xFC, 0x1B, 0x1E, 0xAA, 0x69
+ },
+ {
+ 0xB4, 0x8D, 0x1C, 0x1F, 0x83, 0x92, 0x4A, 0x02,
+ 0xA2, 0x3E, 0x5E, 0x0F, 0x97, 0x1E, 0x16, 0xE8,
+ 0x7F, 0xC4, 0x88, 0x48, 0x53, 0x83, 0x34, 0x85,
+ 0x19, 0x1A, 0x2B, 0x60, 0x72, 0x2F, 0xE2, 0x69
+ },
+ {
+ 0xF2, 0xED, 0xD5, 0xF7, 0x50, 0xA2, 0x0A, 0x54,
+ 0x1D, 0x3F, 0x6B, 0xD5, 0xDF, 0x80, 0x83, 0x8F,
+ 0x11, 0x82, 0x5B, 0x25, 0xA9, 0x8F, 0x3D, 0xA5,
+ 0xE1, 0x52, 0x3B, 0xFF, 0x81, 0x3B, 0xB5, 0x60
+ },
+ {
+ 0x07, 0x16, 0x60, 0x04, 0xEF, 0x88, 0xE1, 0x61,
+ 0x4E, 0xBD, 0xC8, 0x87, 0xDF, 0xC7, 0xDA, 0x42,
+ 0xEB, 0xCD, 0xA0, 0x2D, 0x92, 0xC1, 0x2F, 0x18,
+ 0xD1, 0x18, 0x6C, 0xE3, 0xC9, 0x87, 0x10, 0xE4
+ },
+ {
+ 0x69, 0xF8, 0x3A, 0xA1, 0x01, 0xD6, 0x9B, 0x8F,
+ 0x12, 0x20, 0xDC, 0x1C, 0x53, 0x8D, 0x89, 0x34,
+ 0x45, 0x84, 0x20, 0xBE, 0x33, 0x5F, 0xEB, 0x46,
+ 0xFF, 0xC4, 0x7A, 0x2C, 0x8E, 0x2E, 0x6A, 0x8A
+ },
+ {
+ 0xE1, 0x46, 0x9F, 0x16, 0xC6, 0xFC, 0xA1, 0x51,
+ 0x19, 0xA2, 0x72, 0xE5, 0x85, 0xC7, 0xF5, 0x04,
+ 0x21, 0xBC, 0x8A, 0x41, 0x4C, 0x86, 0x4F, 0xC7,
+ 0x6B, 0x01, 0x04, 0x8D, 0x4C, 0x6F, 0xC5, 0xD2
+ },
+ {
+ 0x67, 0x63, 0x34, 0x3A, 0x1C, 0x80, 0xF1, 0x92,
+ 0x83, 0xA8, 0x0A, 0xF8, 0x54, 0xE7, 0xE9, 0x06,
+ 0x5C, 0x2A, 0x83, 0x49, 0xEF, 0x11, 0xF1, 0x1B,
+ 0xFB, 0x76, 0xBA, 0x9F, 0x97, 0x04, 0x85, 0x39
+ },
+ {
+ 0x1A, 0xE3, 0xA0, 0xB8, 0xB2, 0xC7, 0x88, 0x5B,
+ 0xA3, 0x18, 0xAD, 0x6F, 0xD4, 0x49, 0xFC, 0x4D,
+ 0x7F, 0x84, 0x04, 0xB5, 0x9C, 0xF3, 0x27, 0x5F,
+ 0xCD, 0xEA, 0x13, 0x56, 0x34, 0x25, 0x77, 0x2D
+ },
+ {
+ 0x3A, 0x71, 0x18, 0x4C, 0xBE, 0x8E, 0xB5, 0x8E,
+ 0x68, 0x12, 0xBA, 0x7A, 0x7A, 0x1D, 0xCA, 0x0C,
+ 0xA2, 0x8E, 0xEC, 0x63, 0x78, 0x2F, 0x2E, 0x6E,
+ 0x3C, 0x0B, 0x87, 0x07, 0x3F, 0x53, 0x3F, 0xFD
+ },
+ {
+ 0x18, 0x4C, 0xCF, 0x2A, 0x52, 0xF3, 0x88, 0xC9,
+ 0xF8, 0x97, 0xA8, 0x57, 0xFE, 0x7C, 0xCE, 0xC2,
+ 0x95, 0x99, 0x11, 0xA8, 0xD1, 0xE0, 0x9E, 0xE8,
+ 0x80, 0x4D, 0x8D, 0x5D, 0x50, 0x8D, 0xD9, 0x18
+ },
+ {
+ 0xA6, 0x6D, 0x40, 0x9A, 0xF7, 0xAF, 0xD7, 0x5B,
+ 0xE8, 0x31, 0xDD, 0x49, 0x8C, 0x19, 0x6E, 0xF1,
+ 0x2C, 0x73, 0xC3, 0x11, 0x29, 0xEC, 0x02, 0xD5,
+ 0xF1, 0x2A, 0xB0, 0x2A, 0x2C, 0x63, 0xA2, 0x5E
+ },
+ {
+ 0x58, 0xB3, 0x74, 0x97, 0xFC, 0xF0, 0xBE, 0x0E,
+ 0x0C, 0xF1, 0x73, 0x40, 0x45, 0xC2, 0x95, 0xB2,
+ 0x86, 0xC7, 0x6A, 0x7C, 0x04, 0x8E, 0x87, 0xC5,
+ 0x40, 0x28, 0xED, 0x36, 0x91, 0x5B, 0x5D, 0xF3
+ },
+ {
+ 0x2C, 0x73, 0x33, 0x54, 0x0A, 0x83, 0x2D, 0x64,
+ 0x45, 0x6E, 0x43, 0x05, 0x8C, 0x50, 0xD9, 0x3C,
+ 0x93, 0x2A, 0xD9, 0xB1, 0x8B, 0x3F, 0xC3, 0xA0,
+ 0x59, 0x92, 0x07, 0xCD, 0xA3, 0xB3, 0xC7, 0xA6
+ },
+ {
+ 0x3D, 0xC0, 0x62, 0xFF, 0xB5, 0x7D, 0x83, 0x5F,
+ 0xE3, 0xAA, 0x40, 0x94, 0x66, 0x82, 0x2F, 0x91,
+ 0x86, 0x91, 0x84, 0x23, 0x94, 0x75, 0x05, 0x16,
+ 0x5F, 0xDC, 0xDF, 0xB7, 0x30, 0x6F, 0x72, 0x59
+ },
+ {
+ 0x89, 0x20, 0x48, 0x44, 0xAC, 0xB9, 0x2F, 0x35,
+ 0x3B, 0xFC, 0x89, 0xA3, 0xCE, 0x8A, 0x98, 0x17,
+ 0x21, 0x9C, 0x10, 0x13, 0x85, 0xC5, 0x93, 0xCF,
+ 0x60, 0xE0, 0xBE, 0xFA, 0x96, 0x38, 0xE1, 0x4E
+ },
+ {
+ 0x78, 0x2B, 0xA9, 0x02, 0xE9, 0x12, 0x32, 0x94,
+ 0x1C, 0x78, 0xC4, 0x9C, 0xD9, 0x77, 0x1A, 0x5D,
+ 0x99, 0x92, 0xF9, 0xB0, 0x7D, 0x9C, 0x0A, 0x2D,
+ 0xF8, 0x2D, 0x38, 0x5D, 0x15, 0xC4, 0x2B, 0xB3
+ },
+ {
+ 0x0D, 0xC3, 0xFF, 0x7D, 0xF0, 0xDF, 0xC0, 0x23,
+ 0x76, 0x3D, 0x76, 0x34, 0xE1, 0x8D, 0xA2, 0x73,
+ 0x93, 0xFC, 0x9F, 0xDB, 0x1C, 0x15, 0x46, 0x46,
+ 0x86, 0x10, 0x75, 0xF0, 0xA8, 0x7D, 0x0E, 0x90
+ },
+ {
+ 0xB9, 0x5C, 0x65, 0xFB, 0x6F, 0x25, 0x4E, 0xDB,
+ 0xDE, 0x8C, 0x03, 0x7D, 0x5C, 0x8B, 0x20, 0x39,
+ 0x34, 0x0F, 0x4A, 0xC2, 0xB0, 0x23, 0xA6, 0xAA,
+ 0x28, 0xA8, 0xFC, 0xD2, 0xD2, 0x68, 0x9C, 0xF4
+ },
+ {
+ 0x87, 0xE8, 0xF5, 0x15, 0x72, 0xA5, 0xD6, 0xA2,
+ 0x39, 0xF8, 0x5B, 0xC5, 0x3E, 0x11, 0x74, 0xE1,
+ 0x5B, 0xE1, 0x2F, 0xCD, 0xF1, 0x51, 0xA0, 0xB9,
+ 0xA2, 0xB4, 0x36, 0x40, 0xCA, 0xF7, 0x4C, 0x1D
+ },
+ {
+ 0x2A, 0x6F, 0x3E, 0x46, 0x2C, 0x40, 0x5C, 0x35,
+ 0x4F, 0xE8, 0x0F, 0xCC, 0xCE, 0xD1, 0xC9, 0xBE,
+ 0x44, 0x32, 0x5D, 0x29, 0xE0, 0x7D, 0xA3, 0x09,
+ 0x60, 0xB6, 0x25, 0xA7, 0x6E, 0xA4, 0x2F, 0x83
+ },
+ {
+ 0x20, 0xB4, 0x6C, 0x8F, 0xBF, 0xCA, 0x97, 0x45,
+ 0x32, 0x62, 0x46, 0x0F, 0x84, 0x98, 0xA7, 0xE2,
+ 0xAF, 0x15, 0xAC, 0x79, 0xB5, 0x9D, 0xDF, 0xB0,
+ 0x27, 0xBB, 0x52, 0xF2, 0xD6, 0x8E, 0x8F, 0x51
+ },
+ {
+ 0x31, 0xB0, 0x76, 0x3C, 0xB9, 0xBA, 0x92, 0x40,
+ 0x3D, 0xCA, 0x1A, 0xBD, 0xD7, 0x34, 0x2D, 0x7D,
+ 0xE9, 0x4C, 0x58, 0x1E, 0x76, 0xF7, 0xC9, 0xA6,
+ 0x1E, 0x51, 0x59, 0x28, 0xE1, 0x0B, 0x4E, 0x77
+ },
+ {
+ 0xE1, 0x91, 0xE1, 0x17, 0x06, 0x3C, 0xFA, 0xC9,
+ 0x64, 0x2C, 0xD9, 0x3C, 0xB4, 0x2B, 0x39, 0xED,
+ 0xDD, 0x9E, 0x4A, 0xB6, 0x5F, 0x1D, 0x03, 0x97,
+ 0xE3, 0xE1, 0x7D, 0xD0, 0x4C, 0xAB, 0x11, 0x80
+ },
+ {
+ 0x22, 0x5A, 0x20, 0x21, 0x07, 0xA7, 0x47, 0x03,
+ 0xE0, 0x41, 0xC6, 0xCC, 0xA4, 0xEA, 0xCF, 0x4F,
+ 0x21, 0xEE, 0xA6, 0xF2, 0x2A, 0x14, 0x6D, 0x8D,
+ 0xA2, 0xAB, 0x8C, 0xF6, 0x19, 0x72, 0x29, 0xA5
+ },
+ {
+ 0xEF, 0xC4, 0x83, 0x6B, 0xE4, 0xAC, 0x3E, 0x97,
+ 0x91, 0xD2, 0xEC, 0x62, 0x22, 0x6E, 0x7D, 0xF6,
+ 0x41, 0x18, 0xF4, 0x56, 0x5C, 0x19, 0xE6, 0xC9,
+ 0xE8, 0x40, 0x63, 0xF5, 0x66, 0x1C, 0x7B, 0x2F
+ },
+ {
+ 0x3A, 0x76, 0xB0, 0x15, 0x2C, 0x0E, 0x1D, 0x1F,
+ 0xD7, 0xAC, 0x9D, 0x91, 0xA2, 0x8A, 0x18, 0xE1,
+ 0xA4, 0xC0, 0x60, 0x80, 0xF2, 0xB7, 0xEC, 0xEF,
+ 0xB6, 0xEF, 0xFE, 0x28, 0xB8, 0xCF, 0xC7, 0x65
+ },
+ {
+ 0x0D, 0x46, 0xAD, 0x03, 0x90, 0x70, 0x11, 0x58,
+ 0x28, 0xF9, 0x4E, 0xB6, 0xB7, 0x29, 0x63, 0xE6,
+ 0x0A, 0x7D, 0x2D, 0xB7, 0xCA, 0x89, 0x91, 0xD2,
+ 0x25, 0xC3, 0x87, 0x7B, 0x14, 0x9B, 0x0A, 0x8A
+ },
+ {
+ 0xE4, 0x4C, 0xFC, 0x42, 0x11, 0x8F, 0x09, 0x6B,
+ 0xFC, 0x51, 0x52, 0x1C, 0xB1, 0x8D, 0x5D, 0x65,
+ 0x25, 0x58, 0x6B, 0x98, 0x9F, 0x4E, 0xE2, 0xB8,
+ 0x28, 0xC5, 0x19, 0x9F, 0xEA, 0xB9, 0x4B, 0x82
+ },
+ {
+ 0x6D, 0x4B, 0xD2, 0xE0, 0x73, 0xEC, 0x49, 0x66,
+ 0x84, 0x7F, 0x5C, 0xBE, 0x88, 0xDD, 0xFA, 0xBA,
+ 0x2B, 0xE4, 0xCA, 0xF2, 0xF3, 0x33, 0x55, 0x2B,
+ 0x85, 0x53, 0xDA, 0x53, 0x34, 0x87, 0xC2, 0x5B
+ },
+ {
+ 0xBB, 0xC4, 0x6D, 0xB4, 0x37, 0xD1, 0x07, 0xC9,
+ 0x67, 0xCA, 0x6D, 0x91, 0x45, 0x5B, 0xBD, 0xFE,
+ 0x05, 0x21, 0x18, 0xAB, 0xD1, 0xD0, 0x69, 0xF0,
+ 0x43, 0x59, 0x48, 0x7E, 0x13, 0xAE, 0xA0, 0xE1
+ },
+ {
+ 0xB9, 0x74, 0xC1, 0x4D, 0xB7, 0xD3, 0x17, 0x4D,
+ 0xD0, 0x60, 0x84, 0xBB, 0x30, 0x31, 0x08, 0xB2,
+ 0xF0, 0xDA, 0xF5, 0x0E, 0xCC, 0xC3, 0x29, 0x35,
+ 0x43, 0x79, 0x5C, 0x96, 0x36, 0xC6, 0x24, 0x82
+ },
+ {
+ 0x0E, 0xEE, 0x23, 0x5B, 0x06, 0x93, 0x6A, 0xED,
+ 0x71, 0x73, 0xC8, 0xC1, 0x9A, 0xA7, 0xC2, 0x17,
+ 0xB9, 0xEE, 0xDA, 0xEB, 0x1A, 0x88, 0xF3, 0x05,
+ 0x52, 0xE9, 0x22, 0x51, 0x45, 0x14, 0x9E, 0x82
+ },
+ {
+ 0x36, 0xD0, 0x89, 0xE0, 0x25, 0xB5, 0x68, 0x69,
+ 0x37, 0x74, 0x28, 0x25, 0xE6, 0xEE, 0x3D, 0x83,
+ 0xE7, 0xD7, 0xA5, 0x0C, 0x82, 0x3C, 0x82, 0x88,
+ 0x34, 0x60, 0xF3, 0x85, 0x14, 0x7D, 0xC1, 0x7B
+ },
+ {
+ 0x77, 0xEE, 0x4F, 0xFC, 0x9F, 0x5D, 0xD6, 0x05,
+ 0x47, 0x0D, 0xC0, 0xE7, 0x4D, 0x6B, 0x17, 0xC5,
+ 0x13, 0x0D, 0x8B, 0x73, 0x91, 0x3F, 0x36, 0xD5,
+ 0xF8, 0x78, 0x7E, 0x61, 0x9A, 0x94, 0x7C, 0xA0
+ },
+ {
+ 0x0F, 0xE6, 0xC2, 0xAB, 0x75, 0x42, 0x33, 0x36,
+ 0x0D, 0x68, 0xB9, 0xAC, 0x80, 0xCD, 0x61, 0x18,
+ 0x4B, 0xFA, 0xA7, 0xD3, 0x56, 0x29, 0x41, 0x80,
+ 0x02, 0x5F, 0xE4, 0x06, 0x39, 0xC7, 0x6C, 0x36
+ },
+ {
+ 0x99, 0x60, 0x88, 0xC7, 0x94, 0x56, 0xEC, 0xDD,
+ 0xA1, 0xFB, 0xC0, 0x2E, 0xE1, 0xBA, 0x42, 0xD9,
+ 0x1D, 0x85, 0x8C, 0x31, 0x0A, 0x5A, 0x8B, 0x46,
+ 0x74, 0xFE, 0x6A, 0x7C, 0x14, 0x44, 0x14, 0xA1
+ },
+ {
+ 0x9E, 0x33, 0x8A, 0xED, 0x0B, 0xC7, 0x1C, 0x0C,
+ 0x97, 0xF1, 0x98, 0x55, 0xBF, 0x49, 0x17, 0x4F,
+ 0x70, 0xA9, 0xD7, 0x70, 0x14, 0x87, 0x36, 0x63,
+ 0x21, 0x34, 0x27, 0x50, 0x2B, 0xD8, 0x5D, 0x9F
+ },
+ {
+ 0x4A, 0x84, 0x3D, 0x26, 0xAD, 0xEC, 0x52, 0x0E,
+ 0x4B, 0x5D, 0xBF, 0x01, 0x45, 0xCC, 0x4F, 0x50,
+ 0x24, 0xFA, 0xFC, 0xDC, 0x20, 0x25, 0x82, 0x4A,
+ 0x8C, 0x64, 0x65, 0x06, 0x17, 0x68, 0x7E, 0xE7
+ },
+ {
+ 0xC9, 0x16, 0x78, 0xC4, 0xA6, 0x4E, 0x2F, 0xA4,
+ 0xB7, 0x4D, 0xE6, 0x1A, 0xD0, 0xC0, 0x6F, 0xF0,
+ 0x6B, 0x5D, 0x67, 0x2F, 0xA7, 0xC6, 0x87, 0x7A,
+ 0x40, 0x14, 0xCE, 0x9E, 0x91, 0xBE, 0x38, 0xD7
+ },
+ {
+ 0xFF, 0x77, 0x77, 0x40, 0x5D, 0x32, 0x7A, 0xDB,
+ 0x58, 0x30, 0x1C, 0x71, 0x1E, 0xCD, 0xC2, 0xBC,
+ 0xE1, 0xBF, 0xA8, 0x29, 0xFF, 0xC9, 0xB1, 0x17,
+ 0xF2, 0x1A, 0x2B, 0x19, 0x8D, 0x0D, 0x68, 0x84
+ },
+ {
+ 0x0A, 0x8D, 0xDA, 0xF1, 0x72, 0x8C, 0x5C, 0xD9,
+ 0x3A, 0x25, 0x5D, 0x56, 0x23, 0xC3, 0xDA, 0xDA,
+ 0x2D, 0x3D, 0x05, 0x71, 0xBF, 0x14, 0x38, 0xAD,
+ 0xC8, 0xC9, 0x64, 0xA9, 0xAA, 0xD1, 0x18, 0xCB
+ },
+ {
+ 0xC1, 0x33, 0xAB, 0xBD, 0x0D, 0x2D, 0x80, 0x8A,
+ 0x67, 0xB6, 0x74, 0x5B, 0x4B, 0x36, 0x50, 0xB4,
+ 0xA6, 0x4D, 0xC2, 0x76, 0xCF, 0x98, 0xE3, 0x03,
+ 0x57, 0xB6, 0xAB, 0xD5, 0xC1, 0xD2, 0x2A, 0x9B
+ },
+ {
+ 0xC5, 0x9E, 0xE5, 0xC1, 0x96, 0xBA, 0x3C, 0xFE,
+ 0xF9, 0x40, 0x87, 0x79, 0x82, 0x07, 0xBD, 0xCE,
+ 0xF1, 0x39, 0xCE, 0x2C, 0xF7, 0x8D, 0xCE, 0xD6,
+ 0x19, 0x8F, 0x0F, 0xA3, 0xA4, 0x09, 0x13, 0x1C
+ },
+ {
+ 0xC7, 0xFD, 0xAD, 0xE5, 0x9C, 0x46, 0x99, 0x38,
+ 0x5E, 0xBA, 0x59, 0xE7, 0x56, 0xC2, 0xB1, 0x71,
+ 0xB0, 0x23, 0xDE, 0xAE, 0x08, 0x2E, 0x5A, 0x6E,
+ 0x3B, 0xFB, 0xDC, 0x10, 0x73, 0xA3, 0x20, 0x03
+ },
+ {
+ 0x97, 0x53, 0x27, 0xC5, 0xF4, 0xDE, 0xC6, 0x41,
+ 0x4B, 0x6E, 0x00, 0xCB, 0x04, 0x23, 0x37, 0xB8,
+ 0xD2, 0xA6, 0x56, 0x46, 0x37, 0xA7, 0x44, 0x2A,
+ 0xEC, 0x7B, 0xE8, 0xF8, 0xC8, 0x9A, 0x2F, 0x1C
+ },
+ {
+ 0xA2, 0xF7, 0x24, 0x6D, 0xF4, 0xA2, 0x4E, 0xFB,
+ 0xAC, 0xD3, 0xFD, 0x60, 0x68, 0x3A, 0xBC, 0x86,
+ 0x8B, 0xEF, 0x25, 0x32, 0x70, 0x52, 0xCF, 0x2F,
+ 0x1D, 0x93, 0xEC, 0xE4, 0xFF, 0xCD, 0x73, 0xC6
+ },
+ {
+ 0x49, 0x7F, 0xB2, 0xAC, 0xAC, 0xF1, 0x23, 0xF3,
+ 0x59, 0x5E, 0x40, 0xFC, 0x51, 0xA7, 0xBD, 0x24,
+ 0x45, 0x8B, 0xBC, 0xBA, 0x4A, 0x29, 0x40, 0xA5,
+ 0xCB, 0x03, 0xD6, 0x08, 0xFB, 0xDF, 0x28, 0x25
+ },
+ {
+ 0x0E, 0x97, 0xD2, 0x27, 0x93, 0xCE, 0x6F, 0x28,
+ 0x3D, 0x5C, 0x74, 0x0D, 0x30, 0x8A, 0x27, 0xAD,
+ 0x7C, 0x3B, 0x0D, 0x9A, 0xFC, 0xD3, 0xD9, 0xE9,
+ 0xB9, 0xCA, 0xC5, 0x6B, 0x10, 0x29, 0x0C, 0x8F
+ },
+ {
+ 0x66, 0x30, 0xB3, 0x56, 0x18, 0xE7, 0x00, 0xD9,
+ 0x10, 0x68, 0x38, 0x93, 0x79, 0x5E, 0xF7, 0x0B,
+ 0xF0, 0x7E, 0xB1, 0x56, 0xF5, 0x5F, 0xFE, 0x3B,
+ 0x69, 0xAD, 0x88, 0xA4, 0xB8, 0xB0, 0xBF, 0xA1
+ },
+ {
+ 0x02, 0xF7, 0x42, 0xC6, 0xE9, 0x52, 0x78, 0x12,
+ 0x1A, 0x05, 0xE4, 0x42, 0x05, 0x44, 0x4F, 0xC5,
+ 0xEA, 0x6A, 0xF5, 0xE7, 0x41, 0xC5, 0x35, 0xBC,
+ 0x2C, 0xBC, 0x3B, 0x23, 0x5A, 0x2E, 0xA2, 0xB0
+ },
+ {
+ 0x46, 0x22, 0xF3, 0x6E, 0xB8, 0x98, 0x38, 0x3F,
+ 0x60, 0xD5, 0xBE, 0xD8, 0x09, 0xAC, 0x5C, 0x47,
+ 0x45, 0xC5, 0xD6, 0xAB, 0x84, 0xBC, 0xAD, 0xF7,
+ 0x9C, 0xF2, 0xA9, 0x6D, 0x4E, 0xC8, 0x88, 0x18
+ },
+ {
+ 0xCC, 0xD1, 0x1F, 0xAA, 0xA0, 0x58, 0x1E, 0xC3,
+ 0x2C, 0x3A, 0x40, 0x3F, 0x92, 0xEF, 0x43, 0xD5,
+ 0xDC, 0xF1, 0x95, 0xC1, 0xA1, 0x01, 0xDB, 0xFD,
+ 0x49, 0x5D, 0xBB, 0x4D, 0xCE, 0x80, 0x69, 0xE0
+ },
+ {
+ 0x06, 0x02, 0x4D, 0x6B, 0x07, 0xE0, 0x00, 0xBC,
+ 0xE6, 0x13, 0x47, 0x0A, 0x28, 0x80, 0x51, 0x9B,
+ 0x8B, 0xE4, 0xA3, 0x6B, 0xF3, 0x3C, 0x99, 0xC9,
+ 0x17, 0x89, 0x3E, 0xC7, 0x5D, 0xD9, 0x0F, 0xE3
+ },
+ {
+ 0xD9, 0x3A, 0xF9, 0x47, 0xB1, 0x46, 0x3A, 0x81,
+ 0x7D, 0xB4, 0x41, 0xA4, 0x74, 0x58, 0x8D, 0x6F,
+ 0x99, 0x6D, 0x24, 0x39, 0x83, 0xE8, 0x3C, 0x7E,
+ 0xEE, 0x90, 0xE1, 0xEF, 0xA4, 0x40, 0xD9, 0xBA
+ },
+ {
+ 0x94, 0x89, 0x89, 0x45, 0xA7, 0xDB, 0x25, 0x9E,
+ 0x1B, 0x2E, 0x7C, 0xBE, 0xA4, 0x8A, 0xA0, 0xC6,
+ 0xD6, 0x57, 0x0D, 0x18, 0x17, 0x9F, 0x06, 0x18,
+ 0x47, 0x1C, 0x88, 0xF3, 0xEC, 0x3B, 0x0F, 0xC3
+ },
+ {
+ 0x4C, 0x2D, 0x93, 0x52, 0x56, 0x39, 0x2A, 0xA2,
+ 0xBE, 0x6E, 0x10, 0x78, 0xC0, 0x59, 0x38, 0x15,
+ 0xAB, 0xEF, 0x46, 0x9D, 0xE9, 0x69, 0xB5, 0x7B,
+ 0x88, 0x1B, 0x93, 0xAF, 0x55, 0x84, 0x65, 0xFA
+ },
+ {
+ 0xAA, 0xC7, 0xBE, 0x16, 0xE5, 0x2F, 0x79, 0x0E,
+ 0x4F, 0xF7, 0x0B, 0x24, 0x01, 0x5C, 0xB1, 0x1B,
+ 0x40, 0x61, 0x6E, 0x94, 0xDB, 0x13, 0x88, 0x2B,
+ 0x41, 0xD3, 0xDD, 0x8C, 0x8C, 0x19, 0x52, 0xB7
+ },
+ {
+ 0x04, 0x34, 0xB4, 0x7C, 0x0E, 0xE7, 0xE6, 0xF5,
+ 0x39, 0x06, 0x79, 0x9A, 0x43, 0x20, 0x9D, 0x3F,
+ 0xC3, 0x7D, 0x3F, 0xD1, 0xF7, 0x45, 0x55, 0xDE,
+ 0x67, 0xAB, 0xAC, 0xB9, 0x51, 0xB0, 0x06, 0xF4
+ },
+ {
+ 0x04, 0x42, 0xFB, 0xDD, 0x5B, 0x58, 0x49, 0x6E,
+ 0xC7, 0x81, 0x59, 0xCC, 0xAA, 0x88, 0x7C, 0x88,
+ 0xA8, 0x61, 0xFC, 0xCA, 0x70, 0xE7, 0xAB, 0xC9,
+ 0x76, 0xF2, 0x4C, 0x11, 0x58, 0x8B, 0xE6, 0xEE
+ },
+ {
+ 0xA7, 0x3E, 0x68, 0xBB, 0x18, 0xB0, 0x07, 0x64,
+ 0x8E, 0x76, 0xB5, 0x52, 0x8D, 0x1E, 0x50, 0xE7,
+ 0xFA, 0x65, 0x4D, 0xA3, 0x97, 0x0E, 0xC3, 0x49,
+ 0xBF, 0x59, 0x1A, 0x30, 0xD9, 0x32, 0xC8, 0xF6
+ },
+ {
+ 0x84, 0x9C, 0xF8, 0x73, 0x16, 0x2B, 0xA7, 0x2C,
+ 0x4B, 0x80, 0x08, 0xE6, 0x8F, 0x93, 0x2F, 0xB3,
+ 0xA0, 0x15, 0xA7, 0x4F, 0xCF, 0x95, 0x71, 0x98,
+ 0xD5, 0x6A, 0x0D, 0xC4, 0x62, 0x5A, 0x74, 0xF5
+ },
+ {
+ 0xA6, 0xDE, 0xC6, 0xFC, 0x89, 0x49, 0x34, 0x9C,
+ 0x4E, 0x9A, 0x9C, 0x62, 0x36, 0x87, 0xFB, 0xA4,
+ 0xC9, 0xB2, 0x75, 0xBD, 0xB2, 0x30, 0x50, 0x9B,
+ 0x72, 0xE3, 0xD6, 0x71, 0x19, 0x14, 0xE2, 0xD8
+ },
+ {
+ 0x58, 0xAF, 0xC2, 0xB2, 0x4A, 0x19, 0xFD, 0xBF,
+ 0x76, 0xA0, 0x9B, 0x70, 0xB1, 0xE3, 0xB7, 0x7F,
+ 0xCB, 0xD4, 0x06, 0x50, 0x01, 0xD9, 0x63, 0x66,
+ 0x40, 0xEB, 0x5A, 0x26, 0x28, 0xF4, 0x42, 0xCC
+ },
+ {
+ 0x47, 0x3A, 0x43, 0xAA, 0x1D, 0x6A, 0x02, 0x87,
+ 0x67, 0x43, 0x2A, 0x83, 0x0A, 0xD1, 0x22, 0x1E,
+ 0x02, 0x9C, 0x58, 0x9A, 0xF9, 0xFD, 0x4D, 0x68,
+ 0xD5, 0x6C, 0x4F, 0xB8, 0x20, 0x25, 0x93, 0x52
+ },
+ {
+ 0xA0, 0xAE, 0xB4, 0xA5, 0xAD, 0x89, 0x9A, 0xF2,
+ 0xE2, 0x91, 0xB2, 0xE7, 0x9D, 0xBB, 0x6B, 0x0B,
+ 0xF5, 0x6B, 0x58, 0x44, 0x67, 0x6B, 0x95, 0x5D,
+ 0x94, 0x5B, 0x6C, 0x4A, 0xE1, 0xC0, 0x1E, 0xED
+ },
+ {
+ 0xCF, 0xC3, 0x02, 0x9A, 0x9E, 0xEB, 0x15, 0x22,
+ 0x22, 0xD9, 0x66, 0x53, 0x49, 0x2E, 0x46, 0xCA,
+ 0x64, 0xCA, 0x4F, 0x0D, 0x64, 0x68, 0x30, 0x28,
+ 0xD3, 0xAE, 0xE5, 0xA4, 0x9C, 0xB4, 0x71, 0x63
+ },
+ {
+ 0x74, 0x67, 0xCF, 0x77, 0x61, 0xCD, 0x9F, 0x55,
+ 0x61, 0x8D, 0x30, 0xC9, 0xD8, 0xC5, 0xB4, 0x1E,
+ 0x47, 0x01, 0x51, 0x0C, 0x7D, 0x16, 0xAB, 0x4E,
+ 0x5D, 0x89, 0xA5, 0xD7, 0x71, 0x46, 0xB0, 0x92
+ },
+ {
+ 0xC0, 0x16, 0xD8, 0x42, 0x4E, 0x53, 0x1E, 0xFC,
+ 0x57, 0x37, 0xC0, 0x3F, 0xC9, 0x0A, 0x5E, 0xFC,
+ 0x9F, 0x90, 0x22, 0xE4, 0xD5, 0xBA, 0x3B, 0x06,
+ 0x95, 0xF7, 0xAE, 0x53, 0x82, 0x60, 0xC2, 0xEE
+ },
+ {
+ 0x5D, 0x38, 0x11, 0x89, 0xE6, 0x00, 0x0F, 0xC1,
+ 0x17, 0xC7, 0x1F, 0x59, 0xF7, 0x86, 0xFB, 0x4B,
+ 0x79, 0xFD, 0xD4, 0xEC, 0x5D, 0x4C, 0xD3, 0x0A,
+ 0xAC, 0x21, 0x57, 0xF7, 0x5D, 0xEA, 0xD7, 0x78
+ },
+ {
+ 0x7C, 0x9C, 0xDD, 0x15, 0xC4, 0xC9, 0xAB, 0xCA,
+ 0xCB, 0xFE, 0x6F, 0x66, 0x4A, 0x7F, 0x5F, 0x8B,
+ 0x2E, 0x25, 0x91, 0x83, 0x29, 0x1A, 0xE5, 0xCC,
+ 0x91, 0x30, 0xA0, 0xB2, 0x41, 0xE5, 0x73, 0x7F
+ },
+ {
+ 0xB8, 0x81, 0x31, 0x72, 0xF5, 0x21, 0x8A, 0xC3,
+ 0xEB, 0x68, 0x7B, 0xC4, 0xAF, 0xAF, 0xF8, 0x3F,
+ 0xBC, 0xA4, 0xE9, 0xC1, 0xA4, 0x62, 0x96, 0x33,
+ 0x01, 0xDD, 0x44, 0x59, 0x85, 0x01, 0x50, 0xA2
+ },
+ {
+ 0xE3, 0xD1, 0x30, 0xE3, 0x6A, 0x02, 0x8E, 0xA8,
+ 0x0C, 0x57, 0xA2, 0xAA, 0x48, 0x19, 0xFD, 0x34,
+ 0xE4, 0xDB, 0xBE, 0xB1, 0x4A, 0x49, 0x58, 0x94,
+ 0xB1, 0x5A, 0x87, 0x87, 0xDB, 0x1A, 0x9F, 0x9C
+ },
+ {
+ 0xFF, 0xF1, 0xB4, 0x40, 0x0F, 0x48, 0x9E, 0x07,
+ 0xD2, 0x23, 0x51, 0xC1, 0xF0, 0x95, 0x65, 0xE2,
+ 0x65, 0xB6, 0x8A, 0xD2, 0x9F, 0x63, 0x29, 0x87,
+ 0x9E, 0x6B, 0x5F, 0x7F, 0x6B, 0x41, 0x93, 0x50
+ },
+ {
+ 0x55, 0x9E, 0xD5, 0xBB, 0x3E, 0x5F, 0x39, 0x85,
+ 0xFB, 0x57, 0x82, 0x28, 0xBF, 0x8C, 0x0F, 0x0B,
+ 0x17, 0x3F, 0x8D, 0x11, 0x53, 0xFA, 0xEB, 0x9F,
+ 0xEC, 0x75, 0x6F, 0xFD, 0x18, 0xA8, 0x72, 0x38
+ },
+ {
+ 0x88, 0x13, 0x12, 0x53, 0x01, 0x4D, 0x23, 0xC5,
+ 0xE3, 0x8E, 0x78, 0xBD, 0xA1, 0x94, 0x55, 0xD8,
+ 0xA0, 0x23, 0xBD, 0x7A, 0x7E, 0x72, 0x74, 0x57,
+ 0xA1, 0x52, 0xA8, 0x1D, 0x0B, 0x17, 0x18, 0xA7
+ },
+ {
+ 0xF4, 0xD3, 0xFA, 0xE7, 0xCD, 0xE6, 0xBB, 0x66,
+ 0x71, 0x5A, 0x19, 0x8F, 0xA4, 0x8D, 0x21, 0x0C,
+ 0x10, 0xF8, 0xDF, 0x32, 0x04, 0xAE, 0x5E, 0x33,
+ 0xA6, 0x02, 0x46, 0x7F, 0x1B, 0x62, 0x26, 0x85
+ },
+ {
+ 0xE6, 0x2B, 0x62, 0x2A, 0xC8, 0xA2, 0x13, 0x66,
+ 0xBF, 0x2D, 0xED, 0x30, 0xF4, 0x08, 0x2A, 0x53,
+ 0xE7, 0x7A, 0x9A, 0xA6, 0x96, 0xB1, 0xF3, 0xEE,
+ 0x8C, 0xFE, 0x99, 0xC5, 0x93, 0x12, 0xD9, 0xC7
+ },
+ {
+ 0x3D, 0x39, 0xFF, 0xA8, 0x55, 0x12, 0xC3, 0xC8,
+ 0x89, 0x0D, 0x4B, 0xDF, 0x31, 0x88, 0x9C, 0xA6,
+ 0x6E, 0x5C, 0xEC, 0xB6, 0x3C, 0xFE, 0xED, 0x57,
+ 0xB9, 0x26, 0x37, 0x08, 0xE7, 0x4C, 0x55, 0x0B
+ },
+ {
+ 0xB1, 0x70, 0x3B, 0x8A, 0x00, 0xE2, 0x61, 0x24,
+ 0x97, 0xD1, 0x1C, 0x64, 0x9D, 0x15, 0x0A, 0x6C,
+ 0x96, 0x3B, 0xF4, 0xFD, 0x38, 0xFE, 0xB1, 0xC3,
+ 0x81, 0xFE, 0x0D, 0x9B, 0x04, 0xC0, 0x2B, 0x22
+ },
+ {
+ 0x12, 0xFB, 0xAD, 0x9D, 0x37, 0x82, 0x81, 0x2D,
+ 0x71, 0x17, 0x9A, 0x50, 0xFB, 0xD9, 0xB4, 0x56,
+ 0x6C, 0x7B, 0x06, 0xF5, 0xD7, 0x7C, 0x6F, 0x32,
+ 0x97, 0x17, 0xFB, 0x4A, 0xE2, 0xC5, 0xB4, 0xEC
+ },
+ {
+ 0x76, 0x8B, 0x65, 0x9A, 0x82, 0x4B, 0x43, 0xF9,
+ 0xCA, 0x56, 0x60, 0xB9, 0xDD, 0xF0, 0x5F, 0x8B,
+ 0xA2, 0xBC, 0x49, 0x93, 0x86, 0x6B, 0x7C, 0x9B,
+ 0xE6, 0x87, 0x91, 0xF5, 0xB2, 0x46, 0x44, 0xB3
+ },
+ {
+ 0xC0, 0x20, 0x4E, 0x23, 0xCA, 0x86, 0xBE, 0x20,
+ 0x5E, 0xED, 0x0C, 0xC3, 0xDD, 0x72, 0x25, 0xCE,
+ 0x5F, 0xFE, 0x1E, 0xE1, 0x2D, 0xAC, 0xB9, 0x3C,
+ 0x5D, 0x06, 0x29, 0xB7, 0x69, 0x9C, 0xD7, 0x33
+ },
+ {
+ 0xF4, 0x32, 0x96, 0x96, 0x1F, 0x8E, 0xAE, 0xCC,
+ 0xD8, 0x54, 0x41, 0x3D, 0xC5, 0xAD, 0xDA, 0x62,
+ 0x39, 0x3A, 0x34, 0x46, 0x27, 0xE8, 0x6C, 0x06,
+ 0x6E, 0x79, 0x07, 0x55, 0x00, 0x40, 0x74, 0x4F
+ },
+ {
+ 0x82, 0xF4, 0x46, 0x9E, 0x80, 0x78, 0x90, 0x21,
+ 0xC6, 0x1D, 0xB7, 0xE3, 0x2F, 0x36, 0xAC, 0xBE,
+ 0x59, 0x1A, 0x64, 0xF2, 0x60, 0x59, 0x26, 0x57,
+ 0x70, 0xAE, 0x65, 0x8D, 0x62, 0xBD, 0xE7, 0xEF
+ },
+ {
+ 0x2A, 0x85, 0x67, 0x1A, 0x55, 0xC8, 0x9F, 0xA1,
+ 0x56, 0xE2, 0x96, 0xF7, 0x5D, 0xF1, 0xC7, 0xDB,
+ 0xAB, 0x17, 0x8E, 0xBB, 0xA6, 0x52, 0x04, 0xA7,
+ 0xE8, 0x17, 0x8C, 0x91, 0x6A, 0xD0, 0x87, 0xF8
+ },
+ {
+ 0x33, 0xE2, 0x45, 0x00, 0x28, 0x08, 0xF6, 0x93,
+ 0x4B, 0x9B, 0xE3, 0xA6, 0xFA, 0x8E, 0x86, 0x70,
+ 0xC9, 0x0B, 0xAA, 0x62, 0x57, 0x17, 0xB9, 0x20,
+ 0x1E, 0xB9, 0xB9, 0xDD, 0x91, 0x2F, 0x5C, 0xE2
+ },
+ {
+ 0x58, 0xEE, 0x5E, 0x79, 0x91, 0x84, 0xAD, 0x9D,
+ 0xA9, 0xA1, 0x7C, 0x5B, 0x46, 0xA4, 0x81, 0x0E,
+ 0x28, 0xBD, 0xD0, 0x8C, 0x35, 0x81, 0x63, 0x4C,
+ 0x83, 0x50, 0x30, 0x53, 0x9B, 0x79, 0x54, 0x4D
+ },
+ {
+ 0x26, 0xD8, 0xFA, 0x08, 0xDB, 0x30, 0x8E, 0xDF,
+ 0x2F, 0x96, 0xF8, 0x2A, 0xF6, 0xB6, 0x0C, 0x17,
+ 0xD8, 0xF1, 0xFF, 0x85, 0x8C, 0x52, 0xF2, 0xD0,
+ 0xF3, 0x83, 0x10, 0x78, 0x12, 0x75, 0x26, 0xA3
+ },
+ {
+ 0x25, 0xA5, 0x8D, 0xF4, 0x03, 0x92, 0x47, 0xA2,
+ 0x2F, 0x68, 0xFF, 0x2B, 0x71, 0x76, 0x6B, 0x7B,
+ 0x56, 0x00, 0xDD, 0xF4, 0x01, 0xD9, 0x9F, 0xF2,
+ 0xC1, 0x95, 0x5A, 0xE7, 0xBB, 0x43, 0xE5, 0x6A
+ },
+ {
+ 0xBE, 0x43, 0xE8, 0x68, 0x61, 0x60, 0xE9, 0x07,
+ 0xBA, 0x54, 0x7D, 0x5A, 0x87, 0x9D, 0x10, 0xF7,
+ 0x88, 0xAF, 0xC8, 0x42, 0xB8, 0xEB, 0xB9, 0xF3,
+ 0xF7, 0x88, 0x53, 0x25, 0x15, 0x91, 0x2A, 0xE4
+ },
+ {
+ 0xAA, 0x4A, 0xCB, 0x95, 0xD8, 0x79, 0x19, 0x2A,
+ 0x69, 0x08, 0xE8, 0x8A, 0xE3, 0xD6, 0x58, 0x9F,
+ 0x4E, 0x3E, 0xB3, 0xD4, 0xE0, 0x3A, 0x80, 0x6C,
+ 0xCD, 0xB9, 0xB5, 0xD6, 0xA9, 0x58, 0x6F, 0xDF
+ },
+ {
+ 0x84, 0x66, 0xD5, 0xE4, 0x4C, 0xE9, 0x5B, 0x4F,
+ 0xA1, 0x79, 0x99, 0x24, 0x44, 0xB8, 0xC2, 0x48,
+ 0x5B, 0x88, 0x64, 0x48, 0xA6, 0xDC, 0xCF, 0xCF,
+ 0x0B, 0xC3, 0x0B, 0xC5, 0xF0, 0xF5, 0x6B, 0x01
+ },
+ {
+ 0x00, 0x56, 0xD7, 0xE0, 0xAC, 0x33, 0x35, 0x57,
+ 0x83, 0x65, 0x9B, 0x38, 0xEC, 0x8B, 0xEC, 0xCB,
+ 0xF7, 0x83, 0x93, 0x99, 0x67, 0xFE, 0x37, 0xAE,
+ 0xAC, 0xF3, 0x69, 0xDD, 0xB6, 0x70, 0xAD, 0xA0
+ },
+ {
+ 0x90, 0x4F, 0x42, 0xF3, 0x45, 0x53, 0x0A, 0xC8,
+ 0xA3, 0x52, 0xD0, 0x9B, 0x68, 0x72, 0xC5, 0xBC,
+ 0xA3, 0x66, 0x1A, 0xBC, 0xA6, 0xCA, 0x64, 0xC8,
+ 0x09, 0x9F, 0x2F, 0xB6, 0x86, 0x7C, 0x30, 0xFE
+ },
+ {
+ 0xA8, 0xC3, 0xBF, 0x46, 0xF0, 0xB8, 0x8B, 0xBD,
+ 0x16, 0xFD, 0xA4, 0xA8, 0xB5, 0xCA, 0x81, 0xF5,
+ 0x24, 0x35, 0x20, 0xC3, 0x85, 0xD3, 0x8C, 0x0B,
+ 0x4D, 0x23, 0x52, 0xAB, 0x34, 0xEA, 0x35, 0xE6
+ },
+ {
+ 0x8D, 0x33, 0x17, 0xFC, 0x60, 0x6E, 0x56, 0x6D,
+ 0x30, 0x2E, 0xDA, 0xB5, 0x5E, 0x80, 0x16, 0x11,
+ 0xD8, 0xC1, 0x3F, 0x4A, 0x9A, 0x19, 0xD1, 0x85,
+ 0x97, 0x8D, 0xEF, 0x72, 0x83, 0x9C, 0xDA, 0xA3
+ },
+ {
+ 0x97, 0x38, 0x80, 0x11, 0xF5, 0x7A, 0x49, 0x86,
+ 0x90, 0xEC, 0x79, 0x88, 0xEF, 0xF9, 0x03, 0xFF,
+ 0x9B, 0x23, 0x58, 0xF5, 0xB6, 0x1B, 0xAA, 0x20,
+ 0xF7, 0x32, 0x90, 0xD6, 0x29, 0x6C, 0x1C, 0x0B
+ },
+ {
+ 0xCF, 0xB8, 0x0C, 0xAB, 0x89, 0x90, 0x95, 0x08,
+ 0x09, 0x12, 0x3F, 0xBF, 0x85, 0xE9, 0x76, 0x45,
+ 0x47, 0x08, 0xE0, 0xAF, 0xED, 0x69, 0x8E, 0x33,
+ 0x52, 0xA3, 0x16, 0x35, 0x90, 0x9D, 0xB3, 0xE5
+ },
+ {
+ 0x0D, 0xAA, 0xCA, 0x55, 0x13, 0x2A, 0x23, 0x5B,
+ 0x83, 0x1A, 0x5E, 0xFF, 0x4E, 0xA4, 0x67, 0xCD,
+ 0x10, 0xAF, 0x44, 0x20, 0x08, 0x47, 0x73, 0x5A,
+ 0x1F, 0xFD, 0x51, 0xFA, 0x37, 0xEA, 0xA2, 0xA2
+ },
+ {
+ 0x69, 0xB2, 0x14, 0x97, 0xEB, 0xB8, 0x24, 0xBA,
+ 0x66, 0x53, 0x68, 0x18, 0x88, 0x25, 0xE6, 0xF6,
+ 0xF1, 0x4C, 0xF2, 0xC3, 0xF7, 0xB5, 0x53, 0x0B,
+ 0xB3, 0x4F, 0xA6, 0x58, 0xEE, 0xD9, 0xA7, 0x39
+ },
+ {
+ 0xB9, 0xA1, 0x9F, 0x50, 0x9B, 0xE0, 0x3F, 0xBC,
+ 0x40, 0xE2, 0x43, 0xA5, 0x8A, 0x3D, 0xED, 0x11,
+ 0xF0, 0xD5, 0x1F, 0x80, 0xE3, 0xE2, 0x9A, 0x50,
+ 0x56, 0x44, 0xCC, 0x05, 0x74, 0x38, 0x14, 0xEC
+ },
+ {
+ 0xC4, 0xBC, 0xB2, 0x00, 0x25, 0x55, 0xD5, 0x44,
+ 0xFD, 0x0B, 0x02, 0x77, 0x06, 0x23, 0x89, 0x1E,
+ 0x70, 0xEE, 0xEC, 0x77, 0x44, 0x86, 0x5D, 0xD6,
+ 0x45, 0x5A, 0xD6, 0x65, 0xCC, 0x82, 0xE8, 0x61
+ },
+ {
+ 0x91, 0x2D, 0x24, 0xDC, 0x3D, 0x69, 0x23, 0xA4,
+ 0x83, 0xC2, 0x63, 0xEB, 0xA8, 0x1B, 0x7A, 0x87,
+ 0x97, 0xF2, 0x3C, 0xBF, 0x2F, 0x78, 0xB5, 0x1E,
+ 0x22, 0x26, 0x63, 0x9F, 0x84, 0xA5, 0x90, 0x47
+ },
+ {
+ 0x56, 0x82, 0x7A, 0x18, 0x88, 0x3A, 0xFD, 0xF9,
+ 0xCE, 0xEC, 0x56, 0x2B, 0x20, 0x66, 0xD8, 0xAC,
+ 0xB2, 0xC1, 0x95, 0x05, 0xEC, 0xE6, 0xF7, 0xA8,
+ 0x3E, 0x9F, 0x33, 0x46, 0xCB, 0xB8, 0x28, 0xC9
+ },
+ {
+ 0x25, 0x1D, 0x8D, 0x09, 0xFC, 0x48, 0xDD, 0x1D,
+ 0x6A, 0xF8, 0xFF, 0xDF, 0x39, 0x50, 0x91, 0xA4,
+ 0x6E, 0x05, 0xB8, 0xB7, 0xC5, 0xEC, 0x0C, 0x79,
+ 0xB6, 0x8A, 0x89, 0x04, 0xC8, 0x27, 0xBD, 0xEA
+ },
+ {
+ 0xC2, 0xD1, 0x4D, 0x69, 0xFD, 0x0B, 0xBD, 0x1C,
+ 0x0F, 0xE8, 0xC8, 0x45, 0xD5, 0xFD, 0x6A, 0x8F,
+ 0x74, 0x01, 0x51, 0xB1, 0xD8, 0xEB, 0x4D, 0x26,
+ 0x36, 0x4B, 0xB0, 0x2D, 0xAE, 0x0C, 0x13, 0xBC
+ },
+ {
+ 0x2E, 0x5F, 0xE2, 0x1F, 0x8F, 0x1B, 0x63, 0x97,
+ 0xA3, 0x8A, 0x60, 0x3D, 0x60, 0xB6, 0xF5, 0x3C,
+ 0x3B, 0x5D, 0xB2, 0x0A, 0xA5, 0x6C, 0x6D, 0x44,
+ 0xBE, 0xBD, 0x48, 0x28, 0xCE, 0x28, 0xF9, 0x0F
+ },
+ {
+ 0x25, 0x05, 0x9F, 0x10, 0x60, 0x5E, 0x67, 0xAD,
+ 0xFE, 0x68, 0x13, 0x50, 0x66, 0x6E, 0x15, 0xAE,
+ 0x97, 0x6A, 0x5A, 0x57, 0x1C, 0x13, 0xCF, 0x5B,
+ 0xC8, 0x05, 0x3F, 0x43, 0x0E, 0x12, 0x0A, 0x52
+ },
+};
+
+
+
+
+static const uint8_t blake2sp_keyed_kat[KAT_LENGTH][BLAKE2S_OUTBYTES] =
+{
+ {
+ 0x71, 0x5C, 0xB1, 0x38, 0x95, 0xAE, 0xB6, 0x78,
+ 0xF6, 0x12, 0x41, 0x60, 0xBF, 0xF2, 0x14, 0x65,
+ 0xB3, 0x0F, 0x4F, 0x68, 0x74, 0x19, 0x3F, 0xC8,
+ 0x51, 0xB4, 0x62, 0x10, 0x43, 0xF0, 0x9C, 0xC6
+ },
+ {
+ 0x40, 0x57, 0x8F, 0xFA, 0x52, 0xBF, 0x51, 0xAE,
+ 0x18, 0x66, 0xF4, 0x28, 0x4D, 0x3A, 0x15, 0x7F,
+ 0xC1, 0xBC, 0xD3, 0x6A, 0xC1, 0x3C, 0xBD, 0xCB,
+ 0x03, 0x77, 0xE4, 0xD0, 0xCD, 0x0B, 0x66, 0x03
+ },
+ {
+ 0x67, 0xE3, 0x09, 0x75, 0x45, 0xBA, 0xD7, 0xE8,
+ 0x52, 0xD7, 0x4D, 0x4E, 0xB5, 0x48, 0xEC, 0xA7,
+ 0xC2, 0x19, 0xC2, 0x02, 0xA7, 0xD0, 0x88, 0xDB,
+ 0x0E, 0xFE, 0xAC, 0x0E, 0xAC, 0x30, 0x42, 0x49
+ },
+ {
+ 0x8D, 0xBC, 0xC0, 0x58, 0x9A, 0x3D, 0x17, 0x29,
+ 0x6A, 0x7A, 0x58, 0xE2, 0xF1, 0xEF, 0xF0, 0xE2,
+ 0xAA, 0x42, 0x10, 0xB5, 0x8D, 0x1F, 0x88, 0xB8,
+ 0x6D, 0x7B, 0xA5, 0xF2, 0x9D, 0xD3, 0xB5, 0x83
+ },
+ {
+ 0xA9, 0xA9, 0x65, 0x2C, 0x8C, 0x67, 0x75, 0x94,
+ 0xC8, 0x72, 0x12, 0xD8, 0x9D, 0x5A, 0x75, 0xFB,
+ 0x31, 0xEF, 0x4F, 0x47, 0xC6, 0x58, 0x2C, 0xDE,
+ 0x5F, 0x1E, 0xF6, 0x6B, 0xD4, 0x94, 0x53, 0x3A
+ },
+ {
+ 0x05, 0xA7, 0x18, 0x0E, 0x59, 0x50, 0x54, 0x73,
+ 0x99, 0x48, 0xC5, 0xE3, 0x38, 0xC9, 0x5F, 0xE0,
+ 0xB7, 0xFC, 0x61, 0xAC, 0x58, 0xA7, 0x35, 0x74,
+ 0x74, 0x56, 0x33, 0xBB, 0xC1, 0xF7, 0x70, 0x31
+ },
+ {
+ 0x81, 0x4D, 0xE8, 0x31, 0x53, 0xB8, 0xD7, 0x5D,
+ 0xFA, 0xDE, 0x29, 0xFD, 0x39, 0xAC, 0x72, 0xDD,
+ 0x09, 0xCA, 0x0F, 0x9B, 0xC8, 0xB7, 0xAB, 0x6A,
+ 0x06, 0xBA, 0xEE, 0x7D, 0xD0, 0xF9, 0xF0, 0x83
+ },
+ {
+ 0xDF, 0xD4, 0x19, 0x44, 0x91, 0x29, 0xFF, 0x60,
+ 0x4F, 0x0A, 0x14, 0x8B, 0x4C, 0x7D, 0x68, 0xF1,
+ 0x17, 0x4F, 0x7D, 0x0F, 0x8C, 0x8D, 0x2C, 0xE7,
+ 0x7F, 0x44, 0x8F, 0xD3, 0x41, 0x9C, 0x6F, 0xB0
+ },
+ {
+ 0xB9, 0xED, 0x22, 0xE7, 0xDD, 0x8D, 0xD1, 0x4E,
+ 0xE8, 0xC9, 0x5B, 0x20, 0xE7, 0x63, 0x2E, 0x85,
+ 0x53, 0xA2, 0x68, 0xD9, 0xFF, 0x86, 0x33, 0xED,
+ 0x3C, 0x21, 0xD1, 0xB8, 0xC9, 0xA7, 0x0B, 0xE1
+ },
+ {
+ 0x95, 0xF0, 0x31, 0x67, 0x1A, 0x4E, 0x3C, 0x54,
+ 0x44, 0x1C, 0xEE, 0x9D, 0xBE, 0xF4, 0xB7, 0xAC,
+ 0xA4, 0x46, 0x18, 0xA3, 0xA3, 0x33, 0xAD, 0x74,
+ 0x06, 0xD1, 0x97, 0xAC, 0x5B, 0xA0, 0x79, 0x1A
+ },
+ {
+ 0xE2, 0x92, 0x5B, 0x9D, 0x5C, 0xA0, 0xFF, 0x62,
+ 0x88, 0xC5, 0xEA, 0x1A, 0xF2, 0xD2, 0x2B, 0x0A,
+ 0x6B, 0x79, 0xE2, 0xDA, 0xE0, 0x8B, 0xFD, 0x36,
+ 0xC3, 0xBE, 0x10, 0xBB, 0x8D, 0x71, 0xD8, 0x39
+ },
+ {
+ 0x16, 0x24, 0x9C, 0x74, 0x4E, 0x49, 0x51, 0x45,
+ 0x1D, 0x4C, 0x89, 0x4F, 0xB5, 0x9A, 0x3E, 0xCB,
+ 0x3F, 0xBF, 0xB7, 0xA4, 0x5F, 0x96, 0xF8, 0x5D,
+ 0x15, 0x80, 0xAC, 0x0B, 0x84, 0x2D, 0x96, 0xDA
+ },
+ {
+ 0x43, 0x2B, 0xC9, 0x1C, 0x52, 0xAC, 0xEB, 0x9D,
+ 0xAE, 0xD8, 0x83, 0x28, 0x81, 0x64, 0x86, 0x50,
+ 0xC1, 0xB8, 0x1D, 0x11, 0x7A, 0xBD, 0x68, 0xE0,
+ 0x84, 0x51, 0x50, 0x8A, 0x63, 0xBE, 0x00, 0x81
+ },
+ {
+ 0xCD, 0xE8, 0x20, 0x2B, 0xCF, 0xA3, 0xF3, 0xE9,
+ 0x5D, 0x79, 0xBA, 0xCC, 0x16, 0x5D, 0x52, 0x70,
+ 0x0E, 0xF7, 0x1D, 0x87, 0x4A, 0x3C, 0x63, 0x7E,
+ 0x63, 0x4F, 0x64, 0x44, 0x73, 0x72, 0x0D, 0x6B
+ },
+ {
+ 0x16, 0x21, 0x62, 0x1F, 0x5C, 0x3E, 0xE4, 0x46,
+ 0x89, 0x9D, 0x3C, 0x8A, 0xAE, 0x49, 0x17, 0xB1,
+ 0xE6, 0xDB, 0x4A, 0x0E, 0xD0, 0x42, 0x31, 0x5F,
+ 0xB2, 0xC1, 0x74, 0x82, 0x5E, 0x0A, 0x18, 0x19
+ },
+ {
+ 0x33, 0x6E, 0x8E, 0xBC, 0x71, 0xE2, 0x09, 0x5C,
+ 0x27, 0xF8, 0x64, 0xA3, 0x12, 0x1E, 0xFD, 0x0F,
+ 0xAA, 0x7A, 0x41, 0x28, 0x57, 0x25, 0xA5, 0x92,
+ 0xF6, 0x1B, 0xED, 0xED, 0x9D, 0xDE, 0x86, 0xED
+ },
+ {
+ 0x07, 0x9B, 0xE0, 0x41, 0x0E, 0x78, 0x9B, 0x36,
+ 0xEE, 0x7F, 0x55, 0xC1, 0x9F, 0xAA, 0xC6, 0x91,
+ 0x65, 0x6E, 0xB0, 0x52, 0x1F, 0x42, 0x94, 0x9B,
+ 0x84, 0xEE, 0x29, 0xFE, 0x2A, 0x0E, 0x7F, 0x36
+ },
+ {
+ 0x17, 0x27, 0x0C, 0x4F, 0x34, 0x88, 0x08, 0x2D,
+ 0x9F, 0xF9, 0x93, 0x7E, 0xAB, 0x3C, 0xA9, 0x9C,
+ 0x97, 0xC5, 0xB4, 0x59, 0x61, 0x47, 0x37, 0x2D,
+ 0xD4, 0xE9, 0x8A, 0xCF, 0x13, 0xDB, 0x28, 0x10
+ },
+ {
+ 0x18, 0x3C, 0x38, 0x75, 0x4D, 0x03, 0x41, 0xCE,
+ 0x07, 0xC1, 0x7A, 0x6C, 0xB6, 0xC2, 0xFD, 0x8B,
+ 0xBC, 0xC1, 0x40, 0x4F, 0xDD, 0x01, 0x41, 0x99,
+ 0xC7, 0x8B, 0xE1, 0xA9, 0x75, 0x59, 0xA9, 0x28
+ },
+ {
+ 0x6E, 0x52, 0xD7, 0x28, 0xA4, 0x05, 0xA6, 0xE1,
+ 0xF8, 0x75, 0x87, 0xBB, 0xC2, 0xAC, 0x91, 0xC5,
+ 0xC0, 0x9B, 0x2D, 0x82, 0x8A, 0xC8, 0x1E, 0x5C,
+ 0x4A, 0x81, 0xD0, 0x3D, 0xD4, 0xAA, 0x8D, 0x5C
+ },
+ {
+ 0xF4, 0xE0, 0x8E, 0x05, 0x9B, 0x74, 0x14, 0x4B,
+ 0xF9, 0x48, 0x14, 0x6D, 0x14, 0xA2, 0xC8, 0x1E,
+ 0x46, 0xDC, 0x15, 0xFF, 0x26, 0xEB, 0x52, 0x34,
+ 0x4C, 0xDD, 0x47, 0x4A, 0xBE, 0xA1, 0x4B, 0xC0
+ },
+ {
+ 0x0F, 0x2E, 0x0A, 0x10, 0x0E, 0xD8, 0xA1, 0x17,
+ 0x85, 0x96, 0x2A, 0xD4, 0x59, 0x6A, 0xF9, 0x55,
+ 0xE3, 0x0B, 0x9A, 0xEF, 0x93, 0x0A, 0x24, 0x8D,
+ 0xA9, 0x32, 0x2B, 0x70, 0x2D, 0x4B, 0x68, 0x72
+ },
+ {
+ 0x51, 0x90, 0xFC, 0xC7, 0x32, 0xF4, 0x04, 0xAA,
+ 0xD4, 0x36, 0x4A, 0xC7, 0x96, 0x0C, 0xFD, 0x5B,
+ 0x4E, 0x34, 0x86, 0x29, 0xC3, 0x72, 0xEE, 0xB3,
+ 0x25, 0xB5, 0xC6, 0xC7, 0xCB, 0xCE, 0x59, 0xAB
+ },
+ {
+ 0xC0, 0xC4, 0xCB, 0x86, 0xEA, 0x25, 0xEA, 0x95,
+ 0x7E, 0xEC, 0x5B, 0x22, 0xD2, 0x55, 0x0A, 0x16,
+ 0x49, 0xE6, 0xDF, 0xFA, 0x31, 0x6B, 0xB8, 0xF4,
+ 0xC9, 0x1B, 0x8F, 0xF7, 0xA2, 0x4B, 0x25, 0x31
+ },
+ {
+ 0x2C, 0x9E, 0xDA, 0x13, 0x5A, 0x30, 0xAE, 0xCA,
+ 0xF3, 0xAC, 0xB3, 0xD2, 0x3A, 0x30, 0x35, 0xFB,
+ 0xAB, 0xBA, 0x98, 0x33, 0x31, 0x65, 0xD8, 0x7F,
+ 0xCB, 0xF8, 0xFE, 0x10, 0x33, 0x6E, 0xCF, 0x20
+ },
+ {
+ 0x3C, 0xD6, 0x69, 0xE8, 0xD5, 0x62, 0x62, 0xA2,
+ 0x37, 0x13, 0x67, 0x22, 0x4D, 0xAE, 0x6D, 0x75,
+ 0x9E, 0xE1, 0x52, 0xC3, 0x15, 0x33, 0xB2, 0x63,
+ 0xFA, 0x2E, 0x64, 0x92, 0x08, 0x77, 0xB2, 0xA7
+ },
+ {
+ 0x18, 0xA9, 0xA0, 0xC2, 0xD0, 0xEA, 0x6C, 0x3B,
+ 0xB3, 0x32, 0x83, 0x0F, 0x89, 0x18, 0xB0, 0x68,
+ 0x4F, 0x5D, 0x39, 0x94, 0xDF, 0x48, 0x67, 0x46,
+ 0x2D, 0xD0, 0x6E, 0xF0, 0x86, 0x24, 0x24, 0xCC
+ },
+ {
+ 0x73, 0x90, 0xEA, 0x41, 0x04, 0xA9, 0xF4, 0xEE,
+ 0xA9, 0x0F, 0x81, 0xE2, 0x6A, 0x12, 0x9D, 0xCF,
+ 0x9F, 0x4A, 0xF3, 0x83, 0x52, 0xD9, 0xCB, 0x6A,
+ 0x81, 0x2C, 0xC8, 0x05, 0x69, 0x09, 0x05, 0x0E
+ },
+ {
+ 0xE4, 0x9E, 0x01, 0x14, 0xC6, 0x29, 0xB4, 0x94,
+ 0xB1, 0x1E, 0xA9, 0x8E, 0xCD, 0x40, 0x32, 0x73,
+ 0x1F, 0x15, 0x3B, 0x46, 0x50, 0xAC, 0xAC, 0xD7,
+ 0xE0, 0xF6, 0xE7, 0xDE, 0x3D, 0xF0, 0x19, 0x77
+ },
+ {
+ 0x27, 0xC5, 0x70, 0x2B, 0xE1, 0x04, 0xB3, 0xA9,
+ 0x4F, 0xC4, 0x34, 0x23, 0xAE, 0xEE, 0x83, 0xAC,
+ 0x3C, 0xA7, 0x3B, 0x7F, 0x87, 0x83, 0x9A, 0x6B,
+ 0x2E, 0x29, 0x60, 0x79, 0x03, 0xB7, 0xF2, 0x87
+ },
+ {
+ 0x81, 0xD2, 0xE1, 0x2E, 0xB2, 0xF4, 0x27, 0x60,
+ 0xC6, 0xE3, 0xBA, 0xA7, 0x8F, 0x84, 0x07, 0x3A,
+ 0xE6, 0xF5, 0x61, 0x60, 0x70, 0xFE, 0x25, 0xBE,
+ 0xDE, 0x7C, 0x7C, 0x82, 0x48, 0xAB, 0x1F, 0xBA
+ },
+ {
+ 0xFA, 0xB2, 0x35, 0xD5, 0x93, 0x48, 0xAB, 0x8C,
+ 0xE4, 0x9B, 0xEC, 0x77, 0xC0, 0xF1, 0x93, 0x28,
+ 0xFD, 0x04, 0x5D, 0xFD, 0x60, 0x8A, 0x53, 0x03,
+ 0x36, 0xDF, 0x4F, 0x94, 0xE1, 0x72, 0xA5, 0xC8
+ },
+ {
+ 0x8A, 0xAA, 0x8D, 0x80, 0x5C, 0x58, 0x88, 0x1F,
+ 0xF3, 0x79, 0xFB, 0xD4, 0x2C, 0x6B, 0xF6, 0xF1,
+ 0x4C, 0x6C, 0x73, 0xDF, 0x80, 0x71, 0xB3, 0xB2,
+ 0x28, 0x98, 0x11, 0x09, 0xCC, 0xC0, 0x15, 0xF9
+ },
+ {
+ 0x91, 0xFD, 0xD2, 0x62, 0x20, 0x39, 0x16, 0x39,
+ 0x47, 0x40, 0x95, 0x2B, 0xCE, 0x72, 0xB6, 0x4B,
+ 0xAB, 0xB6, 0xF7, 0x21, 0x34, 0x4D, 0xEE, 0x82,
+ 0x50, 0xBF, 0x0E, 0x46, 0xF1, 0xBA, 0x18, 0x8F
+ },
+ {
+ 0xF7, 0xE5, 0x7B, 0x8F, 0x85, 0xF4, 0x7D, 0x59,
+ 0x03, 0xAD, 0x4C, 0xCB, 0x8A, 0xF6, 0x2A, 0x3E,
+ 0x85, 0x8A, 0xAB, 0x2B, 0x8C, 0xC2, 0x26, 0x49,
+ 0x4F, 0x7B, 0x00, 0xBE, 0xDB, 0xF5, 0xB0, 0xD0
+ },
+ {
+ 0xF7, 0x6F, 0x21, 0xAD, 0xDA, 0xE9, 0x6A, 0x96,
+ 0x46, 0xFC, 0x06, 0xF9, 0xBF, 0x52, 0xAE, 0x08,
+ 0x48, 0xF1, 0x8C, 0x35, 0x26, 0xB1, 0x29, 0xE1,
+ 0x5B, 0x2C, 0x35, 0x5E, 0x2E, 0x79, 0xE5, 0xDA
+ },
+ {
+ 0x8A, 0xEB, 0x1C, 0x79, 0x5F, 0x34, 0x90, 0x01,
+ 0x5E, 0xF4, 0xCD, 0x61, 0xA2, 0x80, 0x7B, 0x23,
+ 0x0E, 0xFD, 0xC8, 0x46, 0x01, 0x73, 0xDA, 0xD0,
+ 0x26, 0xA4, 0xA0, 0xFC, 0xC2, 0xFB, 0xF2, 0x2A
+ },
+ {
+ 0xC5, 0x64, 0xFF, 0xC6, 0x23, 0x07, 0x77, 0x65,
+ 0xBB, 0x97, 0x87, 0x58, 0x56, 0x54, 0xCE, 0x74,
+ 0x5D, 0xBD, 0x10, 0x8C, 0xEF, 0x24, 0x8A, 0xB0,
+ 0x0A, 0xD1, 0xA2, 0x64, 0x7D, 0x99, 0x03, 0x87
+ },
+ {
+ 0xFE, 0x89, 0x42, 0xA3, 0xE5, 0xF5, 0xE8, 0xCD,
+ 0x70, 0x51, 0x04, 0xF8, 0x82, 0x10, 0x72, 0x6E,
+ 0x53, 0xDD, 0x7E, 0xB3, 0xF9, 0xA2, 0x02, 0xBF,
+ 0x93, 0x14, 0xB3, 0xB9, 0x06, 0x5E, 0xB7, 0x12
+ },
+ {
+ 0xDC, 0x29, 0x53, 0x59, 0xD4, 0x36, 0xEE, 0xA7,
+ 0x80, 0x84, 0xE7, 0xB0, 0x77, 0xFE, 0x09, 0xB1,
+ 0x9C, 0x5B, 0xF3, 0xD2, 0xA7, 0x96, 0xDA, 0xB0,
+ 0x19, 0xE4, 0x20, 0x05, 0x99, 0xFD, 0x82, 0x02
+ },
+ {
+ 0x70, 0xB3, 0xF7, 0x2F, 0x74, 0x90, 0x32, 0xE2,
+ 0x5E, 0x38, 0x3B, 0x96, 0x43, 0x78, 0xEA, 0x1C,
+ 0x54, 0x3E, 0x9C, 0x15, 0xDE, 0x3A, 0x27, 0xD8,
+ 0x6D, 0x2A, 0x9D, 0x22, 0x31, 0xEF, 0xF4, 0x8A
+ },
+ {
+ 0x79, 0x82, 0xB5, 0x4C, 0x08, 0xDB, 0x2B, 0xFB,
+ 0x6F, 0x45, 0xF3, 0x5B, 0xC3, 0x23, 0xBC, 0x09,
+ 0x37, 0x79, 0xB6, 0xBB, 0x0E, 0x3E, 0xEA, 0x3E,
+ 0x8C, 0x98, 0xB1, 0xDE, 0x99, 0xD3, 0xC5, 0x5E
+ },
+ {
+ 0x75, 0xE4, 0x16, 0x22, 0x57, 0x01, 0x4B, 0xED,
+ 0xCC, 0x05, 0xC2, 0x94, 0x4D, 0xCE, 0x0D, 0xF0,
+ 0xC3, 0x5E, 0xBA, 0x13, 0x19, 0x54, 0x06, 0x4F,
+ 0x6E, 0x4E, 0x09, 0x5F, 0xD0, 0x84, 0x45, 0xEE
+ },
+ {
+ 0x4A, 0x12, 0x9E, 0xA6, 0xCD, 0xBA, 0xBC, 0x2D,
+ 0x39, 0x24, 0x79, 0x37, 0x2F, 0x97, 0x5B, 0x9C,
+ 0xF5, 0xA1, 0xB7, 0xDE, 0xB6, 0x9A, 0x32, 0x66,
+ 0xF0, 0x3E, 0xBC, 0x6D, 0x11, 0x13, 0x93, 0xC4
+ },
+ {
+ 0x8F, 0xED, 0x70, 0xF2, 0x79, 0x55, 0xDC, 0x8A,
+ 0xD9, 0xF1, 0xB7, 0xB3, 0xF6, 0xF5, 0xDF, 0xBD,
+ 0x96, 0x2A, 0x33, 0x59, 0x2B, 0x42, 0xDE, 0x85,
+ 0x6D, 0x42, 0x1E, 0x29, 0x12, 0xBA, 0xB8, 0x6B
+ },
+ {
+ 0xE2, 0xF2, 0x06, 0x60, 0x37, 0x6F, 0x2B, 0x18,
+ 0x39, 0x66, 0x7C, 0xBF, 0xE5, 0xE1, 0x6E, 0xF0,
+ 0x75, 0xAC, 0x39, 0x43, 0x64, 0x4F, 0x35, 0x32,
+ 0x28, 0x2F, 0x8B, 0xB0, 0x72, 0x3B, 0x99, 0x86
+ },
+ {
+ 0xAB, 0xF8, 0x4C, 0x91, 0x3A, 0x83, 0xDF, 0x98,
+ 0xC7, 0x00, 0x29, 0x81, 0x9C, 0x06, 0x5F, 0x6D,
+ 0x6D, 0xE4, 0xF6, 0xD4, 0x3A, 0xBF, 0x60, 0x0D,
+ 0xAD, 0xE0, 0x35, 0xB2, 0x3B, 0xED, 0x7B, 0xAA
+ },
+ {
+ 0x45, 0x9C, 0x15, 0xD4, 0x85, 0x6C, 0x7E, 0xCF,
+ 0x82, 0x62, 0x03, 0x51, 0xC3, 0xC1, 0xC7, 0x6C,
+ 0x40, 0x3F, 0x3E, 0x97, 0x07, 0x74, 0x13, 0x87,
+ 0xE2, 0x99, 0x07, 0x3F, 0xB1, 0x70, 0x4B, 0x2B
+ },
+ {
+ 0x9A, 0xB9, 0x12, 0xED, 0xA0, 0x76, 0x8A, 0xBD,
+ 0xF8, 0x26, 0xB6, 0xE0, 0x5D, 0x0D, 0x73, 0x58,
+ 0x39, 0xE6, 0xA5, 0xF0, 0x2E, 0x04, 0xC4, 0xCC,
+ 0x75, 0x65, 0x0B, 0x2C, 0x8C, 0xAB, 0x67, 0x49
+ },
+ {
+ 0x47, 0x40, 0xEB, 0xEC, 0xAC, 0x90, 0x03, 0x1B,
+ 0xB7, 0xE6, 0x8E, 0x51, 0xC5, 0x53, 0x91, 0xAF,
+ 0xB1, 0x89, 0xB3, 0x17, 0xF2, 0xDE, 0x55, 0x87,
+ 0x66, 0xF7, 0x8F, 0x5C, 0xB7, 0x1F, 0x81, 0xB6
+ },
+ {
+ 0x3C, 0xC4, 0x7F, 0x0E, 0xF6, 0x48, 0x21, 0x58,
+ 0x7C, 0x93, 0x7C, 0xDD, 0xBA, 0x85, 0xC9, 0x93,
+ 0xD3, 0xCE, 0x2D, 0xD0, 0xCE, 0xD4, 0x0D, 0x3B,
+ 0xE3, 0x3C, 0xB7, 0xDC, 0x7E, 0xDA, 0xBC, 0xF1
+ },
+ {
+ 0x9F, 0x47, 0x6A, 0x22, 0xDB, 0x54, 0xD6, 0xBB,
+ 0x9B, 0xEF, 0xDB, 0x26, 0x0C, 0x66, 0x57, 0x8A,
+ 0xE1, 0xD8, 0xA5, 0xF8, 0x7D, 0x3D, 0x8C, 0x01,
+ 0x7F, 0xDB, 0x74, 0x75, 0x08, 0x0F, 0xA8, 0xE1
+ },
+ {
+ 0x8B, 0x68, 0xC6, 0xFB, 0x07, 0x06, 0xA7, 0x95,
+ 0xF3, 0xA8, 0x39, 0xD6, 0xFE, 0x25, 0xFD, 0x4A,
+ 0xA7, 0xF9, 0x2E, 0x66, 0x4F, 0x76, 0x2D, 0x61,
+ 0x53, 0x81, 0xBC, 0x85, 0x9A, 0xFA, 0x29, 0x2C
+ },
+ {
+ 0xF6, 0x40, 0xD2, 0x25, 0xA6, 0xBC, 0xD2, 0xFC,
+ 0x8A, 0xCC, 0xAF, 0xBE, 0xD5, 0xA8, 0x4B, 0x5B,
+ 0xBB, 0x5D, 0x8A, 0xE5, 0xDB, 0x06, 0xA1, 0x0B,
+ 0x6D, 0x9D, 0x93, 0x16, 0x0B, 0x39, 0x2E, 0xE0
+ },
+ {
+ 0x70, 0x48, 0x60, 0xA7, 0xF5, 0xBA, 0x68, 0xDB,
+ 0x27, 0x03, 0x1C, 0x15, 0xF2, 0x25, 0x50, 0x0D,
+ 0x69, 0x2A, 0xB2, 0x47, 0x53, 0x42, 0x81, 0xC4,
+ 0xF6, 0x84, 0xF6, 0xC6, 0xC8, 0xCD, 0x88, 0xC7
+ },
+ {
+ 0xC1, 0xA7, 0x5B, 0xDD, 0xA1, 0x2B, 0x8B, 0x2A,
+ 0xB1, 0xB9, 0x24, 0x84, 0x38, 0x58, 0x18, 0x3A,
+ 0x09, 0xD2, 0x02, 0x42, 0x1F, 0xDB, 0xCD, 0xF0,
+ 0xE6, 0x3E, 0xAE, 0x46, 0xF3, 0x7D, 0x91, 0xED
+ },
+ {
+ 0x9A, 0x8C, 0xAB, 0x7A, 0x5F, 0x2E, 0x57, 0x62,
+ 0x21, 0xA6, 0xA8, 0x5E, 0x5F, 0xDD, 0xEE, 0x75,
+ 0x67, 0x8E, 0x06, 0x53, 0x24, 0xA6, 0x1D, 0xB0,
+ 0x3A, 0x39, 0x26, 0x1D, 0xDF, 0x75, 0xE3, 0xF4
+ },
+ {
+ 0x05, 0xC2, 0xB2, 0x6B, 0x03, 0xCE, 0x6C, 0xA5,
+ 0x87, 0x1B, 0xE0, 0xDE, 0x84, 0xEE, 0x27, 0x86,
+ 0xA7, 0x9B, 0xCD, 0x9F, 0x30, 0x03, 0x3E, 0x81,
+ 0x9B, 0x4A, 0x87, 0xCC, 0xA2, 0x7A, 0xFC, 0x6A
+ },
+ {
+ 0xB0, 0xB0, 0x99, 0x3C, 0x6D, 0x0C, 0x6E, 0xD5,
+ 0xC3, 0x59, 0x04, 0x80, 0xF8, 0x65, 0xF4, 0x67,
+ 0xF4, 0x33, 0x1A, 0x58, 0xDD, 0x8E, 0x47, 0xBD,
+ 0x98, 0xEB, 0xBC, 0xDB, 0x8E, 0xB4, 0xF9, 0x4D
+ },
+ {
+ 0xE5, 0x7C, 0x10, 0x3C, 0xF7, 0xB6, 0xBB, 0xEB,
+ 0x8A, 0x0D, 0xC8, 0xF0, 0x48, 0x62, 0x5C, 0x3F,
+ 0x4C, 0xE4, 0xF1, 0xA5, 0xAD, 0x4D, 0x07, 0x9C,
+ 0x11, 0x87, 0xBF, 0xE9, 0xEE, 0x3B, 0x8A, 0x5F
+ },
+ {
+ 0xF1, 0x00, 0x23, 0xE1, 0x5F, 0x3B, 0x72, 0xB7,
+ 0x38, 0xAD, 0x61, 0xAE, 0x65, 0xAB, 0x9A, 0x07,
+ 0xE7, 0x77, 0x4E, 0x2D, 0x7A, 0xB0, 0x2D, 0xBA,
+ 0x4E, 0x0C, 0xAF, 0x56, 0x02, 0xC8, 0x01, 0x78
+ },
+ {
+ 0x9A, 0x8F, 0xB3, 0xB5, 0x38, 0xC1, 0xD6, 0xC4,
+ 0x50, 0x51, 0xFA, 0x9E, 0xD9, 0xB0, 0x7D, 0x3E,
+ 0x89, 0xB4, 0x43, 0x03, 0x30, 0x01, 0x4A, 0x1E,
+ 0xFA, 0x28, 0x23, 0xC0, 0x82, 0x3C, 0xF2, 0x37
+ },
+ {
+ 0x30, 0x75, 0xC5, 0xBC, 0x7C, 0x3A, 0xD7, 0xE3,
+ 0x92, 0x01, 0x01, 0xBC, 0x68, 0x99, 0xC5, 0x8E,
+ 0xA7, 0x01, 0x67, 0xA7, 0x77, 0x2C, 0xA2, 0x8E,
+ 0x38, 0xE2, 0xC1, 0xB0, 0xD3, 0x25, 0xE5, 0xA0
+ },
+ {
+ 0xE8, 0x55, 0x94, 0x70, 0x0E, 0x39, 0x22, 0xA1,
+ 0xE8, 0xE4, 0x1E, 0xB8, 0xB0, 0x64, 0xE7, 0xAC,
+ 0x6D, 0x94, 0x9D, 0x13, 0xB5, 0xA3, 0x45, 0x23,
+ 0xE5, 0xA6, 0xBE, 0xAC, 0x03, 0xC8, 0xAB, 0x29
+ },
+ {
+ 0x1D, 0x37, 0x01, 0xA5, 0x66, 0x1B, 0xD3, 0x1A,
+ 0xB2, 0x05, 0x62, 0xBD, 0x07, 0xB7, 0x4D, 0xD1,
+ 0x9A, 0xC8, 0xF3, 0x52, 0x4B, 0x73, 0xCE, 0x7B,
+ 0xC9, 0x96, 0xB7, 0x88, 0xAF, 0xD2, 0xF3, 0x17
+ },
+ {
+ 0x87, 0x4E, 0x19, 0x38, 0x03, 0x3D, 0x7D, 0x38,
+ 0x35, 0x97, 0xA2, 0xA6, 0x5F, 0x58, 0xB5, 0x54,
+ 0xE4, 0x11, 0x06, 0xF6, 0xD1, 0xD5, 0x0E, 0x9B,
+ 0xA0, 0xEB, 0x68, 0x5F, 0x6B, 0x6D, 0xA0, 0x71
+ },
+ {
+ 0x93, 0xF2, 0xF3, 0xD6, 0x9B, 0x2D, 0x36, 0x52,
+ 0x95, 0x56, 0xEC, 0xCA, 0xF9, 0xF9, 0x9A, 0xDB,
+ 0xE8, 0x95, 0xE1, 0x57, 0x22, 0x31, 0xE6, 0x49,
+ 0xB5, 0x05, 0x84, 0xB5, 0xD7, 0xD0, 0x8A, 0xF8
+ },
+ {
+ 0x06, 0xE0, 0x6D, 0x61, 0x0F, 0x2E, 0xEB, 0xBA,
+ 0x36, 0x76, 0x82, 0x3E, 0x77, 0x44, 0xD7, 0x51,
+ 0xAF, 0xF7, 0x30, 0x76, 0xED, 0x65, 0xF3, 0xCF,
+ 0xF5, 0xE7, 0x2F, 0xD2, 0x27, 0x99, 0x9C, 0x77
+ },
+ {
+ 0x8D, 0xF7, 0x57, 0xB3, 0xA1, 0xE0, 0xF4, 0x80,
+ 0xFA, 0x76, 0xC7, 0xF3, 0x58, 0xED, 0x03, 0x98,
+ 0xBE, 0x3F, 0x2A, 0x8F, 0x7B, 0x90, 0xEA, 0x8C,
+ 0x80, 0x75, 0x99, 0xDE, 0xDA, 0x1D, 0x05, 0x34
+ },
+ {
+ 0xEE, 0xC9, 0xC5, 0xC6, 0x3C, 0xC5, 0x16, 0x9D,
+ 0x96, 0x7B, 0xB1, 0x62, 0x4E, 0x9E, 0xE5, 0xCE,
+ 0xD9, 0x28, 0x97, 0x73, 0x6E, 0xFB, 0xD1, 0x57,
+ 0x54, 0x8D, 0x82, 0xE8, 0x7C, 0xC7, 0x2F, 0x25
+ },
+ {
+ 0xCC, 0x2B, 0x58, 0x32, 0xAD, 0x27, 0x2C, 0xC5,
+ 0x5C, 0x10, 0xD4, 0xF8, 0xC7, 0xF8, 0xBB, 0x38,
+ 0xE6, 0xE4, 0xEB, 0x92, 0x2F, 0x93, 0x86, 0x83,
+ 0x0F, 0x90, 0xB1, 0xE3, 0xDA, 0x39, 0x37, 0xD5
+ },
+ {
+ 0x36, 0x89, 0x85, 0xD5, 0x38, 0x7C, 0x0B, 0xFC,
+ 0x92, 0x8A, 0xC2, 0x54, 0xFA, 0x6D, 0x16, 0x67,
+ 0x3E, 0x70, 0x94, 0x75, 0x66, 0x96, 0x1B, 0x5F,
+ 0xB3, 0x32, 0x5A, 0x58, 0x8A, 0xB3, 0x17, 0x3A
+ },
+ {
+ 0xF1, 0xE4, 0x42, 0xAF, 0xB8, 0x72, 0x15, 0x1F,
+ 0x81, 0x34, 0x95, 0x6C, 0x54, 0x8A, 0xE3, 0x24,
+ 0x0D, 0x07, 0xE6, 0xE3, 0x38, 0xD4, 0xA7, 0xA6,
+ 0xAF, 0x8D, 0xA4, 0x11, 0x9A, 0xB0, 0xE2, 0xB0
+ },
+ {
+ 0xB0, 0x12, 0xC7, 0x54, 0x6A, 0x39, 0xC4, 0x0C,
+ 0xAD, 0xEC, 0xE4, 0xE0, 0x4E, 0x7F, 0x33, 0xC5,
+ 0x93, 0xAD, 0x18, 0x2E, 0xBC, 0x5A, 0x46, 0xD2,
+ 0xDB, 0xF4, 0xAD, 0x1A, 0x92, 0xF5, 0x9E, 0x7B
+ },
+ {
+ 0x6C, 0x60, 0x97, 0xCD, 0x20, 0x33, 0x09, 0x6B,
+ 0x4D, 0xF3, 0x17, 0xDE, 0x8A, 0x90, 0x8B, 0x7D,
+ 0x0C, 0x72, 0x94, 0x39, 0x0C, 0x5A, 0x39, 0x9C,
+ 0x30, 0x1B, 0xF2, 0xA2, 0x65, 0x2E, 0x82, 0x62
+ },
+ {
+ 0xBA, 0x83, 0xFE, 0xB5, 0x10, 0xB4, 0x9A, 0xDE,
+ 0x4F, 0xAE, 0xFB, 0xE9, 0x42, 0x78, 0x1E, 0xAF,
+ 0xD4, 0x1A, 0xD5, 0xD4, 0x36, 0x88, 0x85, 0x31,
+ 0xB6, 0x88, 0x59, 0xF2, 0x2C, 0x2D, 0x16, 0x4A
+ },
+ {
+ 0x5A, 0x06, 0x9E, 0x43, 0x92, 0x19, 0x5A, 0xC9,
+ 0xD2, 0x84, 0xA4, 0x7F, 0x3B, 0xD8, 0x54, 0xAF,
+ 0x8F, 0xD0, 0xD7, 0xFD, 0xC3, 0x48, 0x3D, 0x2C,
+ 0x5F, 0x34, 0x24, 0xCC, 0xFD, 0xA1, 0x5C, 0x8E
+ },
+ {
+ 0x7E, 0x88, 0xD6, 0x4B, 0xBB, 0xE2, 0x02, 0x4F,
+ 0x44, 0x54, 0xBA, 0x13, 0x98, 0xB3, 0xD8, 0x65,
+ 0x2D, 0xCE, 0xC8, 0x20, 0xB1, 0x4C, 0x3B, 0x0A,
+ 0xBF, 0xBF, 0x0F, 0x4F, 0x33, 0x06, 0xBB, 0x5E
+ },
+ {
+ 0xF8, 0x74, 0x2F, 0xF4, 0x6D, 0xFD, 0xF3, 0xEC,
+ 0x82, 0x64, 0xF9, 0x94, 0x5B, 0x20, 0x41, 0x94,
+ 0x62, 0xF0, 0x69, 0xE8, 0x33, 0xC5, 0x94, 0xEC,
+ 0x80, 0xFF, 0xAC, 0x5E, 0x7E, 0x51, 0x34, 0xF9
+ },
+ {
+ 0xD3, 0xE0, 0xB7, 0x38, 0xD2, 0xE9, 0x2F, 0x3C,
+ 0x47, 0xC7, 0x94, 0x66, 0x66, 0x09, 0xC0, 0xF5,
+ 0x50, 0x4F, 0x67, 0xEC, 0x4E, 0x76, 0x0E, 0xEE,
+ 0xCC, 0xF8, 0x64, 0x4E, 0x68, 0x33, 0x34, 0x11
+ },
+ {
+ 0x0C, 0x90, 0xCE, 0x10, 0xED, 0xF0, 0xCE, 0x1D,
+ 0x47, 0xEE, 0xB5, 0x0B, 0x5B, 0x7A, 0xFF, 0x8E,
+ 0xE8, 0xA4, 0x3B, 0x64, 0xA8, 0x89, 0xC1, 0xC6,
+ 0xC6, 0xB8, 0xE3, 0x1A, 0x3C, 0xFC, 0x45, 0xEE
+ },
+ {
+ 0x83, 0x91, 0x7A, 0xC1, 0xCD, 0xAD, 0xE8, 0xF0,
+ 0xE3, 0xBF, 0x42, 0x6F, 0xEA, 0xC1, 0x38, 0x8B,
+ 0x3F, 0xCB, 0xE3, 0xE1, 0xBF, 0x98, 0x79, 0x8C,
+ 0x81, 0x58, 0xBF, 0x75, 0x8E, 0x8D, 0x5D, 0x4E
+ },
+ {
+ 0xDC, 0x8E, 0xB0, 0xC0, 0x13, 0xFA, 0x9D, 0x06,
+ 0x4E, 0xE3, 0x76, 0x23, 0x36, 0x9F, 0xB3, 0x94,
+ 0xAF, 0x97, 0x4B, 0x1A, 0xAC, 0x82, 0x40, 0x5B,
+ 0x88, 0x97, 0x6C, 0xD8, 0xFC, 0xA1, 0x25, 0x30
+ },
+ {
+ 0x9A, 0xF4, 0xFC, 0x92, 0xEA, 0x8D, 0x6B, 0x5F,
+ 0xE7, 0x99, 0x0E, 0x3A, 0x02, 0x70, 0x1E, 0xC2,
+ 0x2B, 0x2D, 0xFD, 0x71, 0x00, 0xB9, 0x0D, 0x05,
+ 0x51, 0x86, 0x94, 0x17, 0x95, 0x5E, 0x44, 0xC8
+ },
+ {
+ 0xC7, 0x22, 0xCE, 0xC1, 0x31, 0xBA, 0xA1, 0x63,
+ 0xF4, 0x7E, 0x4B, 0x33, 0x9E, 0x1F, 0xB9, 0xB4,
+ 0xAC, 0xA2, 0x48, 0xC4, 0x75, 0x93, 0x45, 0xEA,
+ 0xDB, 0xD6, 0xC6, 0xA7, 0xDD, 0xB5, 0x04, 0x77
+ },
+ {
+ 0x18, 0x37, 0xB1, 0x20, 0xD4, 0xE4, 0x04, 0x6C,
+ 0x6D, 0xE8, 0xCC, 0xAF, 0x09, 0xF1, 0xCA, 0xF3,
+ 0x02, 0xAD, 0x56, 0x23, 0x4E, 0x6B, 0x42, 0x2C,
+ 0xE9, 0x0A, 0x61, 0xBF, 0x06, 0xAE, 0xE4, 0x3D
+ },
+ {
+ 0x87, 0xAC, 0x9D, 0x0F, 0x8A, 0x0B, 0x11, 0xBF,
+ 0xED, 0xD6, 0x99, 0x1A, 0x6D, 0xAF, 0x34, 0xC8,
+ 0xAA, 0x5D, 0x7E, 0x8A, 0xE1, 0xB9, 0xDF, 0x4A,
+ 0xF7, 0x38, 0x00, 0x5F, 0xE7, 0x8C, 0xE9, 0x3C
+ },
+ {
+ 0xE2, 0x1F, 0xB6, 0x68, 0xEB, 0xB8, 0xBF, 0x2D,
+ 0x82, 0x08, 0x6D, 0xED, 0xCB, 0x3A, 0x53, 0x71,
+ 0xC2, 0xC4, 0x6F, 0xA1, 0xAC, 0x11, 0xD2, 0xE2,
+ 0xC5, 0x66, 0xD1, 0x4A, 0xD3, 0xC3, 0x65, 0x3F
+ },
+ {
+ 0x5A, 0x9A, 0x69, 0x81, 0x5E, 0x4D, 0x3E, 0xB7,
+ 0x72, 0xED, 0x90, 0x8F, 0xE6, 0x58, 0xCE, 0x50,
+ 0x87, 0x31, 0x0E, 0xC1, 0xD5, 0x0C, 0xB9, 0x4F,
+ 0x56, 0x28, 0x33, 0x9A, 0x61, 0xDC, 0xD9, 0xEE
+ },
+ {
+ 0xAA, 0xC2, 0x85, 0xF1, 0x20, 0x8F, 0x70, 0xA6,
+ 0x47, 0x97, 0xD0, 0xA9, 0x40, 0x0D, 0xA6, 0x46,
+ 0x53, 0x30, 0x18, 0x38, 0xFE, 0xF6, 0x69, 0x0B,
+ 0x87, 0xCD, 0xA9, 0x15, 0x9E, 0xE0, 0x7E, 0xF4
+ },
+ {
+ 0x05, 0x64, 0x3C, 0x1C, 0x6F, 0x26, 0x59, 0x25,
+ 0xA6, 0x50, 0x93, 0xF9, 0xDE, 0x8A, 0x19, 0x1C,
+ 0x4F, 0x6F, 0xD1, 0x41, 0x8F, 0xBF, 0x66, 0xBE,
+ 0x80, 0x59, 0xA9, 0x1B, 0xA8, 0xDC, 0xDA, 0x61
+ },
+ {
+ 0x1C, 0x6C, 0xDE, 0x5B, 0x78, 0x10, 0x3C, 0x9E,
+ 0x6F, 0x04, 0x6D, 0xFE, 0x30, 0xF5, 0x12, 0x1C,
+ 0xF9, 0xD4, 0x03, 0x9E, 0xFE, 0x22, 0x25, 0x40,
+ 0xA4, 0x1B, 0xBC, 0x06, 0xE4, 0x69, 0xFE, 0xB6
+ },
+ {
+ 0xB4, 0x9B, 0xB4, 0x6D, 0x1B, 0x19, 0x3B, 0x04,
+ 0x5E, 0x74, 0x12, 0x05, 0x9F, 0xE7, 0x2D, 0x55,
+ 0x25, 0x52, 0xA8, 0xFB, 0x6C, 0x36, 0x41, 0x07,
+ 0x23, 0xDC, 0x7D, 0x05, 0xFC, 0xCE, 0xDE, 0xD3
+ },
+ {
+ 0xB6, 0x12, 0xD3, 0xD2, 0x1F, 0xC4, 0xDE, 0x3C,
+ 0x79, 0x1A, 0xF7, 0x35, 0xE5, 0x9F, 0xB7, 0x17,
+ 0xD8, 0x39, 0x72, 0x3B, 0x42, 0x50, 0x8E, 0x9E,
+ 0xBF, 0x78, 0x06, 0xD9, 0x3E, 0x9C, 0x83, 0x7F
+ },
+ {
+ 0x7C, 0x33, 0x90, 0xA3, 0xE5, 0xCB, 0x27, 0xD1,
+ 0x86, 0x8B, 0xA4, 0x55, 0xCF, 0xEB, 0x32, 0x22,
+ 0xFD, 0xE2, 0x7B, 0xCD, 0xA4, 0xBF, 0x24, 0x8E,
+ 0x3D, 0x29, 0xCF, 0x1F, 0x34, 0x32, 0x9F, 0x25
+ },
+ {
+ 0xBD, 0x42, 0xEE, 0xA7, 0xB3, 0x54, 0x86, 0xCD,
+ 0xD0, 0x90, 0x7C, 0xB4, 0x71, 0x2E, 0xDE, 0x2F,
+ 0x4D, 0xEE, 0xCC, 0xBC, 0xA1, 0x91, 0x60, 0x38,
+ 0x65, 0xA1, 0xCC, 0x80, 0x9F, 0x12, 0xB4, 0x46
+ },
+ {
+ 0xD1, 0xDD, 0x62, 0x01, 0x74, 0x0C, 0xFA, 0xAD,
+ 0x53, 0xCE, 0xCC, 0xB7, 0x56, 0xB1, 0x10, 0xF3,
+ 0xD5, 0x0F, 0x81, 0x7B, 0x43, 0xD7, 0x55, 0x95,
+ 0x57, 0xE5, 0x7A, 0xAD, 0x14, 0x3A, 0x85, 0xD9
+ },
+ {
+ 0x58, 0x29, 0x64, 0x3C, 0x1B, 0x10, 0xE1, 0xC8,
+ 0xCC, 0xF2, 0x0C, 0x9B, 0x4A, 0xF8, 0x21, 0xEA,
+ 0x05, 0x2D, 0x7F, 0x0F, 0x7C, 0x22, 0xF7, 0x38,
+ 0x0B, 0xBB, 0xCF, 0xAF, 0xB9, 0x77, 0xE2, 0x1F
+ },
+ {
+ 0xFC, 0x4C, 0xF2, 0xA7, 0xFB, 0xE0, 0xB1, 0xE8,
+ 0xAE, 0xFB, 0xE4, 0xB4, 0xB7, 0x9E, 0xD8, 0x4E,
+ 0xC9, 0x7B, 0x03, 0x4F, 0x51, 0xB4, 0xE9, 0x7F,
+ 0x76, 0x0B, 0x20, 0x63, 0x97, 0x65, 0xB9, 0x33
+ },
+ {
+ 0x4D, 0x7C, 0x3B, 0x34, 0x38, 0xA0, 0xBD, 0xA2,
+ 0x8E, 0x7A, 0x96, 0xE4, 0x20, 0x27, 0xD8, 0x13,
+ 0xE8, 0x8A, 0xE6, 0x28, 0x85, 0x49, 0x98, 0x33,
+ 0xD3, 0xC5, 0xF6, 0x35, 0x9E, 0xF7, 0xED, 0xBC
+ },
+ {
+ 0x34, 0xCB, 0xD3, 0x20, 0x68, 0xEF, 0x7E, 0x82,
+ 0x09, 0x9E, 0x58, 0x0B, 0xF9, 0xE2, 0x64, 0x23,
+ 0xE9, 0x81, 0xE3, 0x1B, 0x1B, 0xBC, 0xE6, 0x1A,
+ 0xEA, 0xB1, 0x4C, 0x32, 0xA2, 0x73, 0xE4, 0xCB
+ },
+ {
+ 0xA0, 0x5D, 0xDA, 0x7D, 0x0D, 0xA9, 0xE0, 0x94,
+ 0xAE, 0x22, 0x53, 0x3F, 0x79, 0xE7, 0xDC, 0xCD,
+ 0x26, 0xB1, 0x75, 0x7C, 0xEF, 0xB9, 0x5B, 0xCF,
+ 0x62, 0xC4, 0xFF, 0x9C, 0x26, 0x92, 0xE1, 0xC0
+ },
+ {
+ 0x22, 0x4C, 0xCF, 0xFA, 0x7C, 0xCA, 0x4C, 0xE3,
+ 0x4A, 0xFD, 0x47, 0xF6, 0x2A, 0xDE, 0x53, 0xC5,
+ 0xE8, 0x48, 0x9B, 0x04, 0xAC, 0x9C, 0x41, 0xF7,
+ 0xFA, 0xD0, 0xC8, 0xED, 0xEB, 0x89, 0xE9, 0x41
+ },
+ {
+ 0x6B, 0xC6, 0x07, 0x64, 0x83, 0xAA, 0x11, 0xC0,
+ 0x7F, 0xBA, 0x55, 0xC0, 0xF9, 0xA1, 0xB5, 0xDA,
+ 0x87, 0xEC, 0xBF, 0xFE, 0xA7, 0x55, 0x98, 0xCC,
+ 0x31, 0x8A, 0x51, 0x4C, 0xEC, 0x7B, 0x3B, 0x6A
+ },
+ {
+ 0x9A, 0x03, 0x60, 0xE2, 0x3A, 0x22, 0xF4, 0xF7,
+ 0x6C, 0x0E, 0x95, 0x28, 0xDA, 0xFD, 0x12, 0x9B,
+ 0xB4, 0x67, 0x5F, 0xB8, 0x8D, 0x44, 0xEA, 0xF8,
+ 0x57, 0x77, 0x30, 0x0C, 0xEC, 0x9B, 0xCC, 0x79
+ },
+ {
+ 0x79, 0x01, 0x99, 0xB4, 0xCA, 0x90, 0xDE, 0xDC,
+ 0xCF, 0xE3, 0x24, 0x74, 0xE8, 0x5B, 0x17, 0x4F,
+ 0x06, 0x9E, 0x35, 0x42, 0xBE, 0x31, 0x04, 0xC1,
+ 0x12, 0x5C, 0x2F, 0xDB, 0xD6, 0x9D, 0x32, 0xC7
+ },
+ {
+ 0x55, 0x83, 0x99, 0x25, 0x83, 0x4C, 0xA3, 0xE8,
+ 0x25, 0xE9, 0x92, 0x41, 0x87, 0x4D, 0x16, 0xD6,
+ 0xC2, 0x62, 0x36, 0x29, 0xC4, 0xC2, 0xAD, 0xDD,
+ 0xF0, 0xDB, 0xA0, 0x1E, 0x6C, 0xE8, 0xA0, 0xDC
+ },
+ {
+ 0x61, 0x5F, 0xF8, 0x46, 0xD9, 0x93, 0x00, 0x7D,
+ 0x38, 0xDE, 0x1A, 0xEC, 0xB3, 0x17, 0x82, 0x89,
+ 0xDE, 0xD0, 0x9E, 0x6B, 0xB5, 0xCB, 0xD6, 0x0F,
+ 0x69, 0xC6, 0xAA, 0x36, 0x38, 0x30, 0x20, 0xF7
+ },
+ {
+ 0xF0, 0xE4, 0x0B, 0x4E, 0xD4, 0x0D, 0x34, 0x85,
+ 0x1E, 0x72, 0xB4, 0xEE, 0x4D, 0x00, 0xEA, 0x6A,
+ 0x40, 0xEA, 0x1C, 0x1B, 0xF9, 0xE5, 0xC2, 0x69,
+ 0x71, 0x0C, 0x9D, 0x51, 0xCB, 0xB8, 0xA3, 0xC9
+ },
+ {
+ 0x0B, 0x07, 0xB2, 0x33, 0x3B, 0x08, 0xD0, 0x8C,
+ 0x11, 0xCA, 0x34, 0xAB, 0x44, 0x9B, 0x71, 0xD2,
+ 0x9A, 0x0F, 0x43, 0xE1, 0xF7, 0x78, 0xE0, 0x73,
+ 0xE7, 0x90, 0x06, 0xCC, 0xB7, 0x30, 0xED, 0x62
+ },
+ {
+ 0xD1, 0xF4, 0xC2, 0x9D, 0x9F, 0x23, 0xEA, 0x35,
+ 0xEC, 0x40, 0x35, 0xB3, 0x77, 0xD5, 0x06, 0x53,
+ 0x8E, 0x72, 0x8B, 0xC7, 0x39, 0xC1, 0x45, 0x96,
+ 0x80, 0xCF, 0x1C, 0xC6, 0x94, 0x24, 0x92, 0x4D
+ },
+ {
+ 0x12, 0x79, 0xCF, 0x6F, 0x66, 0x9F, 0x92, 0xF6,
+ 0xBF, 0xC2, 0x5D, 0x60, 0x5B, 0x94, 0x40, 0xC7,
+ 0xDC, 0xCB, 0xD2, 0x5D, 0xF2, 0x8D, 0xC7, 0x35,
+ 0x3A, 0xBC, 0x1C, 0x05, 0x30, 0x40, 0x5D, 0xC4
+ },
+ {
+ 0x1F, 0xA0, 0xAF, 0x00, 0x77, 0x5D, 0xC2, 0xCE,
+ 0x76, 0x50, 0x6D, 0x32, 0x80, 0xF4, 0x72, 0xD2,
+ 0xF6, 0xFF, 0x97, 0xA2, 0x15, 0x1F, 0xAA, 0x82,
+ 0x79, 0x42, 0xFE, 0xA4, 0x4A, 0xD0, 0xBA, 0x1F
+ },
+ {
+ 0x3E, 0x1A, 0xD5, 0x4A, 0x5F, 0x83, 0x5B, 0x98,
+ 0x3B, 0xD2, 0xAA, 0xB0, 0xED, 0x2A, 0x4C, 0x0B,
+ 0xDD, 0x72, 0x16, 0x20, 0x9C, 0x36, 0xA7, 0x9E,
+ 0x9E, 0x2A, 0xAB, 0xB9, 0x9F, 0xAF, 0x35, 0x12
+ },
+ {
+ 0xC6, 0xED, 0x39, 0xE2, 0xD8, 0xB6, 0x36, 0xEC,
+ 0xCB, 0xA2, 0x45, 0xEF, 0x4E, 0x88, 0x64, 0xF4,
+ 0xCD, 0x94, 0x6B, 0xE2, 0x16, 0xB9, 0xBE, 0x48,
+ 0x30, 0x3E, 0x08, 0xB9, 0x2D, 0xD0, 0x94, 0x34
+ },
+ {
+ 0xE2, 0x47, 0x36, 0xC1, 0x3E, 0xCB, 0x9F, 0x36,
+ 0xA0, 0xD8, 0x29, 0xD4, 0x79, 0x8D, 0x76, 0x99,
+ 0xC1, 0x4C, 0xC6, 0x5B, 0x6D, 0xC4, 0x4E, 0xD6,
+ 0xF1, 0x0C, 0xD4, 0x85, 0x3D, 0x6E, 0x07, 0x57
+ },
+ {
+ 0x38, 0x9B, 0xE8, 0x80, 0x52, 0xA3, 0x81, 0x27,
+ 0x2C, 0x6D, 0xF7, 0x41, 0xA8, 0x8A, 0xD3, 0x49,
+ 0xB7, 0x12, 0x71, 0x84, 0x35, 0x48, 0x0A, 0x81,
+ 0x90, 0xB7, 0x04, 0x77, 0x1D, 0x2D, 0xE6, 0x37
+ },
+ {
+ 0x88, 0x9F, 0x2D, 0x57, 0x8A, 0x5D, 0xAE, 0xFD,
+ 0x34, 0x1C, 0x21, 0x09, 0x84, 0xE1, 0x26, 0xD1,
+ 0xD9, 0x6D, 0xA2, 0xDE, 0xE3, 0xC8, 0x1F, 0x7A,
+ 0x60, 0x80, 0xBF, 0x84, 0x56, 0x9B, 0x31, 0x14
+ },
+ {
+ 0xE9, 0x36, 0x09, 0x5B, 0x9B, 0x98, 0x2F, 0xFC,
+ 0x85, 0x6D, 0x2F, 0x52, 0x76, 0xA4, 0xE5, 0x29,
+ 0xEC, 0x73, 0x95, 0xDA, 0x31, 0x6D, 0x62, 0x87,
+ 0x02, 0xFB, 0x28, 0x1A, 0xDA, 0x6F, 0x38, 0x99
+ },
+ {
+ 0xEF, 0x89, 0xCE, 0x1D, 0x6F, 0x8B, 0x48, 0xEA,
+ 0x5C, 0xD6, 0xAE, 0xAB, 0x6A, 0x83, 0xD0, 0xCC,
+ 0x98, 0xC9, 0xA3, 0xA2, 0x07, 0xA1, 0x08, 0x57,
+ 0x32, 0xF0, 0x47, 0xD9, 0x40, 0x38, 0xC2, 0x88
+ },
+ {
+ 0xF9, 0x25, 0x01, 0x6D, 0x79, 0xF2, 0xAC, 0xA8,
+ 0xC4, 0x9E, 0xDF, 0xCD, 0x66, 0x21, 0xD5, 0xBE,
+ 0x3C, 0x8C, 0xEC, 0x61, 0xBD, 0x58, 0x71, 0xD8,
+ 0xC1, 0xD3, 0xA5, 0x65, 0xF3, 0x5E, 0x0C, 0x9F
+ },
+ {
+ 0x63, 0xE8, 0x63, 0x4B, 0x75, 0x7A, 0x38, 0xF9,
+ 0x2B, 0x92, 0xFD, 0x23, 0x89, 0x3B, 0xA2, 0x99,
+ 0x85, 0x3A, 0x86, 0x13, 0x67, 0x9F, 0xDF, 0x7E,
+ 0x05, 0x11, 0x09, 0x5C, 0x0F, 0x04, 0x7B, 0xCA
+ },
+ {
+ 0xCF, 0x2C, 0xCA, 0x07, 0x72, 0xB7, 0x05, 0xEB,
+ 0x57, 0xD2, 0x89, 0x43, 0xF8, 0x3D, 0x35, 0x3F,
+ 0xE2, 0x91, 0xE5, 0xB3, 0x77, 0x78, 0x0B, 0x37,
+ 0x4C, 0x8B, 0xA4, 0x66, 0x58, 0x30, 0xBE, 0x87
+ },
+ {
+ 0x46, 0xDF, 0x5B, 0x87, 0xC8, 0x0E, 0x7E, 0x40,
+ 0x74, 0xAE, 0xE6, 0x85, 0x59, 0x42, 0x47, 0x42,
+ 0x84, 0x5B, 0x9B, 0x35, 0x0F, 0x51, 0xBA, 0x55,
+ 0xB0, 0x74, 0xBB, 0xAE, 0x4C, 0x62, 0x6A, 0xAB
+ },
+ {
+ 0x65, 0x8A, 0xA4, 0xF9, 0xD2, 0xBC, 0xBD, 0x4F,
+ 0x7F, 0x8E, 0xB6, 0x3E, 0x68, 0xF5, 0x36, 0x7E,
+ 0xDB, 0xC5, 0x00, 0xA0, 0xB1, 0xFB, 0xB4, 0x1E,
+ 0x9D, 0xF1, 0x41, 0xBC, 0xBA, 0x8F, 0xCD, 0x53
+ },
+ {
+ 0xEE, 0x80, 0x55, 0x50, 0x08, 0xA7, 0x16, 0x55,
+ 0xE0, 0x81, 0x09, 0x2B, 0xBA, 0x6F, 0x67, 0x0E,
+ 0xD9, 0x8A, 0xF9, 0xA0, 0x9F, 0xB5, 0xAF, 0xB9,
+ 0x4C, 0xBC, 0x5C, 0x75, 0x48, 0x14, 0xDB, 0x4F
+ },
+ {
+ 0x2C, 0x5F, 0x9D, 0x04, 0x82, 0x20, 0xB0, 0x41,
+ 0xB6, 0xD4, 0x52, 0x4B, 0x44, 0x90, 0xCF, 0x8C,
+ 0x66, 0xFC, 0xB8, 0xE1, 0x4B, 0x0D, 0x64, 0x88,
+ 0x7A, 0xA1, 0xE4, 0x76, 0x1A, 0x60, 0x2B, 0x39
+ },
+ {
+ 0x44, 0xCB, 0x63, 0x11, 0xD0, 0x75, 0x0B, 0x7E,
+ 0x33, 0xF7, 0x33, 0x3A, 0xA7, 0x8A, 0xAC, 0xA9,
+ 0xC3, 0x4A, 0xD5, 0xF7, 0x9C, 0x1B, 0x15, 0x91,
+ 0xEC, 0x33, 0x95, 0x1E, 0x69, 0xC4, 0xC4, 0x61
+ },
+ {
+ 0x0C, 0x6C, 0xE3, 0x2A, 0x3E, 0xA0, 0x56, 0x12,
+ 0xC5, 0xF8, 0x09, 0x0F, 0x6A, 0x7E, 0x87, 0xF5,
+ 0xAB, 0x30, 0xE4, 0x1B, 0x70, 0x7D, 0xCB, 0xE5,
+ 0x41, 0x55, 0x62, 0x0A, 0xD7, 0x70, 0xA3, 0x40
+ },
+ {
+ 0xC6, 0x59, 0x38, 0xDD, 0x3A, 0x05, 0x3C, 0x72,
+ 0x9C, 0xF5, 0xB7, 0xC8, 0x9F, 0x39, 0x0B, 0xFE,
+ 0xBB, 0x51, 0x12, 0x76, 0x6B, 0xB0, 0x0A, 0xA5,
+ 0xFA, 0x31, 0x64, 0xDF, 0xDF, 0x3B, 0x56, 0x47
+ },
+ {
+ 0x7D, 0xE7, 0xF0, 0xD5, 0x9A, 0x90, 0x39, 0xAF,
+ 0xF3, 0xAA, 0xF3, 0x2C, 0x3E, 0xE5, 0x2E, 0x79,
+ 0x17, 0x53, 0x57, 0x29, 0x06, 0x21, 0x68, 0xD2,
+ 0x49, 0x0B, 0x6B, 0x6C, 0xE2, 0x44, 0xB3, 0x80
+ },
+ {
+ 0x89, 0x58, 0x98, 0xF5, 0x3A, 0x8F, 0x39, 0xE4,
+ 0x24, 0x10, 0xDA, 0x77, 0xB6, 0xC4, 0x81, 0x5B,
+ 0x0B, 0xB2, 0x39, 0x5E, 0x39, 0x22, 0xF5, 0xBE,
+ 0xD0, 0xE1, 0xFB, 0xF2, 0xA4, 0xC6, 0xDF, 0xEB
+ },
+ {
+ 0xC9, 0x05, 0xA8, 0x49, 0x84, 0x34, 0x8A, 0x64,
+ 0xDB, 0x1F, 0x54, 0x20, 0x83, 0x74, 0x8A, 0xD9,
+ 0x0A, 0x4B, 0xAD, 0x98, 0x33, 0xCB, 0x6D, 0xA3,
+ 0x87, 0x29, 0x34, 0x31, 0xF1, 0x9E, 0x7C, 0x9C
+ },
+ {
+ 0xED, 0x37, 0xD1, 0xA4, 0xD0, 0x6C, 0x90, 0xD1,
+ 0x95, 0x78, 0x48, 0x66, 0x7E, 0x95, 0x48, 0xFE,
+ 0xBB, 0x5D, 0x42, 0x3E, 0xAB, 0x4F, 0x56, 0x78,
+ 0x5C, 0xC4, 0xB5, 0x41, 0x6B, 0x78, 0x00, 0x08
+ },
+ {
+ 0x0B, 0xC6, 0x5D, 0x99, 0x97, 0xFB, 0x73, 0x4A,
+ 0x56, 0x1F, 0xB1, 0xE9, 0xF8, 0xC0, 0x95, 0x8A,
+ 0x02, 0xC7, 0xA4, 0xDB, 0xD0, 0x96, 0xEB, 0xEF,
+ 0x1A, 0x17, 0x51, 0xAE, 0xD9, 0x59, 0xEE, 0xD7
+ },
+ {
+ 0x7C, 0x5F, 0x43, 0x2E, 0xB8, 0xB7, 0x35, 0x2A,
+ 0x94, 0x94, 0xDE, 0xA4, 0xD5, 0x3C, 0x21, 0x38,
+ 0x70, 0x31, 0xCE, 0x70, 0xE8, 0x5D, 0x94, 0x08,
+ 0xFC, 0x6F, 0x8C, 0xD9, 0x8A, 0x6A, 0xAA, 0x1E
+ },
+ {
+ 0xB8, 0xBF, 0x8E, 0x2C, 0x34, 0xE0, 0x33, 0x98,
+ 0x36, 0x39, 0x90, 0x9E, 0xAA, 0x37, 0x64, 0x0D,
+ 0x87, 0x7B, 0x04, 0x8F, 0xE2, 0x99, 0xB4, 0x70,
+ 0xAF, 0x2D, 0x0B, 0xA8, 0x2A, 0x5F, 0x14, 0xC0
+ },
+ {
+ 0x88, 0xA9, 0xDD, 0x13, 0xD5, 0xDA, 0xDB, 0xDE,
+ 0xE6, 0xBF, 0xF7, 0xEE, 0x1E, 0xF8, 0xC7, 0x1C,
+ 0xC1, 0x93, 0xAA, 0x4B, 0xF3, 0xE8, 0x4F, 0x8F,
+ 0xE8, 0x0C, 0xB0, 0x75, 0x68, 0x3C, 0x07, 0x79
+ },
+ {
+ 0x9A, 0xED, 0xB8, 0x87, 0x6D, 0xD2, 0x1C, 0x8C,
+ 0x84, 0xD2, 0xE7, 0x02, 0xA1, 0x36, 0x25, 0x98,
+ 0x04, 0x62, 0xF6, 0x8B, 0xF0, 0xA1, 0xB7, 0x25,
+ 0x4A, 0xD8, 0x06, 0xC3, 0x84, 0x03, 0xC9, 0xDE
+ },
+ {
+ 0xD0, 0x97, 0x57, 0x3D, 0xF2, 0xD6, 0xB2, 0x48,
+ 0x9A, 0x47, 0x94, 0x84, 0x86, 0x98, 0x00, 0xA1,
+ 0xF8, 0x33, 0xEA, 0x16, 0x9E, 0xFF, 0x32, 0xAE,
+ 0x3C, 0xE6, 0x3A, 0x20, 0x79, 0x54, 0x8D, 0x78
+ },
+ {
+ 0xD1, 0x8F, 0x27, 0xA3, 0xE5, 0x55, 0xD7, 0xF9,
+ 0x1A, 0x00, 0x7C, 0x67, 0xAC, 0xEE, 0xDE, 0x39,
+ 0x1F, 0x75, 0xA6, 0x1F, 0xA4, 0x2A, 0x0B, 0x45,
+ 0x66, 0xEB, 0x58, 0x2C, 0xA0, 0x5E, 0xBC, 0xE7
+ },
+ {
+ 0xDF, 0x1D, 0xAA, 0x90, 0xB1, 0x70, 0x23, 0x13,
+ 0xE6, 0xA5, 0x90, 0x1C, 0x7A, 0xFC, 0x5E, 0xD9,
+ 0x65, 0x77, 0x17, 0xA7, 0x15, 0xFA, 0x53, 0xA4,
+ 0x18, 0x9E, 0xC1, 0xE5, 0xDF, 0x29, 0x3A, 0x68
+ },
+ {
+ 0x04, 0xE3, 0xA4, 0x96, 0xB6, 0x69, 0x96, 0xC6,
+ 0x6E, 0x32, 0x91, 0x9E, 0xD1, 0xF9, 0x4C, 0x36,
+ 0xEE, 0xBB, 0xF2, 0x40, 0x63, 0x3A, 0x2F, 0x73,
+ 0x98, 0x45, 0xF0, 0x29, 0x5D, 0x34, 0xAF, 0xBA
+ },
+ {
+ 0x8C, 0x45, 0xD8, 0x8C, 0x4E, 0x9C, 0x9D, 0x0C,
+ 0x8C, 0x67, 0x7F, 0xE4, 0x8F, 0xA5, 0x44, 0x9B,
+ 0xA3, 0x01, 0x78, 0xD4, 0x0A, 0xF0, 0xF0, 0x21,
+ 0x79, 0x21, 0xC6, 0x2E, 0x4B, 0x60, 0xCD, 0xD3
+ },
+ {
+ 0xE1, 0x49, 0xA6, 0xB1, 0x3B, 0xDE, 0xDE, 0xA2,
+ 0xEE, 0xEE, 0x00, 0x9C, 0xE9, 0x44, 0x5E, 0x8D,
+ 0xCF, 0x76, 0xB7, 0x6E, 0x55, 0xA5, 0x01, 0xD8,
+ 0xF5, 0xB4, 0x3F, 0xF8, 0x96, 0x79, 0x6A, 0xD1
+ },
+ {
+ 0xA8, 0x37, 0xC4, 0xC7, 0xC6, 0xF5, 0xCF, 0xB9,
+ 0x9E, 0x10, 0x85, 0xFD, 0x43, 0x28, 0x7A, 0x41,
+ 0x05, 0xCB, 0x28, 0xB7, 0x6F, 0xC3, 0x8B, 0x60,
+ 0x55, 0xC5, 0xDC, 0xFF, 0x78, 0xB8, 0x25, 0x65
+ },
+ {
+ 0x42, 0x41, 0x1F, 0x28, 0x78, 0x0B, 0x4F, 0x16,
+ 0x38, 0x54, 0x0B, 0x87, 0x05, 0x21, 0xEC, 0x45,
+ 0xBC, 0xEB, 0x1E, 0x0C, 0x71, 0x31, 0xF7, 0xE1,
+ 0xC4, 0x67, 0x2E, 0x43, 0x6C, 0x88, 0xC8, 0xE9
+ },
+ {
+ 0x34, 0xB4, 0xE8, 0x76, 0x76, 0x94, 0x71, 0xDF,
+ 0x55, 0x2E, 0x55, 0x22, 0xCE, 0xA7, 0x84, 0xFA,
+ 0x53, 0xAC, 0x61, 0xBE, 0xDE, 0x8C, 0xFE, 0x29,
+ 0x14, 0x09, 0xE6, 0x8B, 0x69, 0xE8, 0x77, 0x6F
+ },
+ {
+ 0x8F, 0x31, 0xD6, 0x37, 0xA9, 0x1D, 0xBD, 0x0E,
+ 0xCB, 0x0B, 0xA0, 0xE6, 0x94, 0xBE, 0xC1, 0x44,
+ 0x76, 0x58, 0xCE, 0x6C, 0x27, 0xEA, 0x9B, 0x95,
+ 0xFF, 0x36, 0x70, 0x1C, 0xAF, 0x36, 0xF0, 0x01
+ },
+ {
+ 0xB5, 0xC8, 0x95, 0xEB, 0x07, 0x1E, 0x3D, 0x38,
+ 0x52, 0x8D, 0x47, 0x5D, 0x3B, 0xB0, 0xBA, 0x88,
+ 0xB7, 0x17, 0x95, 0xE4, 0x0A, 0x98, 0x2E, 0x2A,
+ 0xC2, 0xD8, 0x44, 0x22, 0xA0, 0xF2, 0x68, 0x5D
+ },
+ {
+ 0xE9, 0x06, 0x25, 0x7C, 0x41, 0x9D, 0x94, 0x1E,
+ 0xD2, 0xB8, 0xA9, 0xC1, 0x27, 0x81, 0xDB, 0x97,
+ 0x59, 0xA3, 0xFC, 0xF3, 0xDC, 0x7C, 0xDB, 0x03,
+ 0x15, 0x99, 0xE1, 0x08, 0x6B, 0x67, 0x2F, 0x10
+ },
+ {
+ 0x98, 0xAD, 0x24, 0x39, 0x7C, 0x6E, 0xAE, 0x4C,
+ 0xF7, 0x3E, 0xA8, 0xBB, 0xEF, 0x5A, 0x0B, 0x74,
+ 0xD2, 0x1A, 0xD1, 0x5F, 0x33, 0x92, 0x0F, 0x44,
+ 0x07, 0x0A, 0x98, 0xBD, 0xF5, 0x3D, 0x0B, 0x3A
+ },
+ {
+ 0xDD, 0x51, 0x0C, 0xA5, 0x5B, 0x11, 0x70, 0xF9,
+ 0xCE, 0xFD, 0xBB, 0x16, 0xFC, 0x14, 0x52, 0x62,
+ 0xAA, 0x36, 0x3A, 0x87, 0x0A, 0x01, 0xE1, 0xBC,
+ 0x4F, 0xBE, 0x40, 0x23, 0x4B, 0x4B, 0x6F, 0x2F
+ },
+ {
+ 0xF2, 0xD8, 0xD9, 0x31, 0xB9, 0x2E, 0x1C, 0xB6,
+ 0x98, 0xE5, 0x6E, 0xD0, 0x28, 0x19, 0xEA, 0x11,
+ 0xD2, 0x66, 0x19, 0xB8, 0x3A, 0x62, 0x09, 0xAD,
+ 0x67, 0x22, 0x53, 0x68, 0xFE, 0x11, 0x95, 0x71
+ },
+ {
+ 0xE4, 0x63, 0x70, 0x55, 0xDB, 0x91, 0xF9, 0x43,
+ 0x7C, 0xF4, 0x60, 0xEF, 0x40, 0xB5, 0x14, 0x5F,
+ 0x69, 0x98, 0x26, 0x6A, 0x5E, 0x74, 0xE9, 0x6A,
+ 0x00, 0x78, 0x2C, 0x62, 0xCF, 0x30, 0xCF, 0x1C
+ },
+ {
+ 0x35, 0x63, 0x53, 0x0A, 0x89, 0xD3, 0x2B, 0x75,
+ 0xF7, 0x8D, 0x83, 0xE9, 0x87, 0x2A, 0xD4, 0xC5,
+ 0x75, 0xF5, 0x20, 0x39, 0x9D, 0x65, 0x03, 0x5D,
+ 0xED, 0x99, 0xE5, 0xEE, 0xC5, 0x80, 0x71, 0x50
+ },
+ {
+ 0x8E, 0x79, 0xF9, 0x2C, 0x86, 0x5B, 0xEB, 0x3E,
+ 0x1C, 0xDB, 0xF0, 0x8F, 0x75, 0x4A, 0x26, 0x06,
+ 0xE8, 0x53, 0x49, 0x05, 0x3D, 0x66, 0xD6, 0x16,
+ 0x02, 0x4A, 0x81, 0x3F, 0xCA, 0x54, 0x1A, 0x4D
+ },
+ {
+ 0x86, 0x42, 0x26, 0xF2, 0x83, 0x9C, 0x76, 0xB1,
+ 0xD5, 0xF7, 0xC1, 0x3D, 0x98, 0xC2, 0xA5, 0x15,
+ 0x8C, 0x2A, 0xBB, 0x71, 0xD9, 0xD8, 0xF0, 0xFA,
+ 0x1F, 0x7C, 0x3F, 0x74, 0x68, 0x00, 0x16, 0x03
+ },
+ {
+ 0xD3, 0xE3, 0xF5, 0xB8, 0xCE, 0xEB, 0xB1, 0x11,
+ 0x84, 0x80, 0x35, 0x35, 0x90, 0x0B, 0x6E, 0xED,
+ 0xDA, 0x60, 0x6E, 0xEB, 0x36, 0x97, 0x51, 0xA7,
+ 0xCD, 0xA3, 0x6C, 0xA3, 0x02, 0x29, 0xFB, 0x02
+ },
+ {
+ 0x8C, 0x7D, 0x6B, 0x98, 0x72, 0x69, 0x16, 0x90,
+ 0x31, 0xF7, 0x1F, 0xD7, 0xE4, 0xC4, 0x45, 0x01,
+ 0x2D, 0x3E, 0x6A, 0x3C, 0x88, 0x09, 0xF6, 0x47,
+ 0x9B, 0xD6, 0x67, 0xCF, 0x31, 0x1E, 0x27, 0x6E
+ },
+ {
+ 0xB9, 0x04, 0xB5, 0x71, 0x1B, 0xF1, 0x9E, 0x85,
+ 0x32, 0xF7, 0xAD, 0x64, 0x27, 0x41, 0x0A, 0x62,
+ 0xA1, 0xF7, 0x7F, 0x77, 0xB9, 0xB6, 0xD7, 0x1D,
+ 0x2F, 0xC4, 0x3B, 0xC9, 0x0F, 0x73, 0x23, 0x5A
+ },
+ {
+ 0x45, 0x36, 0x63, 0x43, 0x15, 0xC8, 0x67, 0x28,
+ 0xF5, 0xAB, 0x74, 0x49, 0xEB, 0x2D, 0x04, 0x02,
+ 0x0E, 0x9E, 0xAE, 0x8D, 0xD6, 0x79, 0x55, 0x00,
+ 0xE9, 0xEC, 0x9A, 0x00, 0x66, 0x38, 0x6E, 0x69
+ },
+ {
+ 0xFD, 0x5E, 0x49, 0xFE, 0xD4, 0x9D, 0xC4, 0x4B,
+ 0xDE, 0x89, 0xF4, 0x60, 0xA9, 0x50, 0x19, 0x1E,
+ 0xBB, 0x06, 0x7C, 0x69, 0x8A, 0x3F, 0x21, 0xEA,
+ 0x14, 0x30, 0x8C, 0x74, 0x13, 0xB9, 0x16, 0x81
+ },
+ {
+ 0x31, 0xF0, 0x1D, 0x03, 0x0B, 0x9B, 0x22, 0xD0,
+ 0x0A, 0x0F, 0x71, 0xED, 0x2C, 0xEB, 0x5D, 0x2D,
+ 0xC8, 0x1A, 0xF2, 0xC2, 0x4B, 0xF5, 0x67, 0x0F,
+ 0xDE, 0x19, 0xA6, 0x85, 0xE8, 0xD1, 0x39, 0x2E
+ },
+ {
+ 0x5F, 0x84, 0xD9, 0xDE, 0x28, 0x4B, 0x1E, 0x4F,
+ 0x67, 0x8E, 0x31, 0xAB, 0x6A, 0x76, 0xF5, 0x66,
+ 0x1B, 0x5A, 0xEA, 0xA7, 0x68, 0x53, 0x93, 0x84,
+ 0xAA, 0x38, 0xF9, 0xE4, 0x9C, 0xCE, 0x6E, 0x6E
+ },
+ {
+ 0xB2, 0x07, 0x9E, 0x59, 0x97, 0xA4, 0xEA, 0xD3,
+ 0xA7, 0x1F, 0xEF, 0xC0, 0x2F, 0x90, 0xA7, 0x48,
+ 0x3A, 0x10, 0xFD, 0x2E, 0x6F, 0x31, 0xBD, 0xA9,
+ 0xD2, 0x08, 0x44, 0x85, 0xCC, 0x01, 0x6B, 0xBD
+ },
+ {
+ 0xE0, 0xF8, 0x4D, 0x7F, 0x52, 0x5B, 0x6F, 0xED,
+ 0x79, 0x1F, 0x77, 0x28, 0x9A, 0xE5, 0x8F, 0x7D,
+ 0x50, 0xA2, 0x94, 0x32, 0xD4, 0x2C, 0x25, 0xC1,
+ 0xE8, 0x39, 0x29, 0xB8, 0x38, 0x89, 0x1D, 0x79
+ },
+ {
+ 0x70, 0x46, 0x96, 0x90, 0x95, 0x6D, 0x79, 0x18,
+ 0xAC, 0xE7, 0xBA, 0x5F, 0x41, 0x30, 0x2D, 0xA1,
+ 0x38, 0xC9, 0xB5, 0x6E, 0xCD, 0x41, 0x55, 0x44,
+ 0xFA, 0xCE, 0x8D, 0x99, 0x8C, 0x21, 0xAB, 0xEB
+ },
+ {
+ 0x45, 0xC9, 0x1A, 0x62, 0x24, 0x9B, 0x39, 0xCD,
+ 0xA9, 0x4E, 0x50, 0x82, 0x95, 0xBE, 0xC7, 0x66,
+ 0x71, 0x19, 0x44, 0x77, 0x65, 0xEF, 0x80, 0xEF,
+ 0xA8, 0x2D, 0x1E, 0x92, 0xD5, 0x70, 0x67, 0xD8
+ },
+ {
+ 0x1D, 0x9E, 0x00, 0x73, 0xEE, 0xD0, 0x73, 0x15,
+ 0x54, 0xC3, 0xBE, 0xAA, 0x47, 0x46, 0x0D, 0x51,
+ 0x1A, 0xD2, 0x61, 0xDD, 0x4D, 0x4A, 0x3B, 0xED,
+ 0x9D, 0x8D, 0x20, 0x2F, 0x22, 0xF2, 0x15, 0x89
+ },
+ {
+ 0x40, 0x82, 0x62, 0x73, 0x6D, 0x8A, 0xEC, 0x0B,
+ 0x84, 0x7D, 0xBA, 0x25, 0x02, 0x58, 0x60, 0x8A,
+ 0x43, 0x45, 0xA6, 0x3A, 0x1E, 0xB1, 0x95, 0xE5,
+ 0xC7, 0xAE, 0x2E, 0xE8, 0x74, 0xC3, 0x4D, 0xA8
+ },
+ {
+ 0x23, 0xD2, 0xB7, 0x04, 0x39, 0x46, 0x99, 0x49,
+ 0x98, 0x23, 0x90, 0x53, 0x8D, 0x7E, 0x5A, 0xDE,
+ 0x9F, 0x18, 0xC8, 0xE3, 0xBB, 0xF6, 0x60, 0x5A,
+ 0xFC, 0xF4, 0x9B, 0x00, 0xC0, 0x61, 0xE8, 0x37
+ },
+ {
+ 0x23, 0x2F, 0xB1, 0x87, 0xD2, 0x71, 0xBE, 0xA9,
+ 0x12, 0xEF, 0xD4, 0x07, 0xFF, 0xE0, 0x80, 0x56,
+ 0xD6, 0xA4, 0x2E, 0x53, 0x21, 0xEC, 0x79, 0x2D,
+ 0xF3, 0xD5, 0x84, 0xA9, 0x4F, 0x63, 0x0A, 0xB2
+ },
+ {
+ 0x13, 0x8E, 0x19, 0x44, 0xE4, 0xB5, 0x4D, 0xE8,
+ 0x68, 0x1D, 0x7E, 0x48, 0xC4, 0xF0, 0x81, 0x48,
+ 0xE4, 0x0A, 0x56, 0x7E, 0x5C, 0xAD, 0x94, 0x6A,
+ 0x6A, 0xF4, 0xE8, 0xD5, 0xD2, 0x6F, 0x75, 0xC7
+ },
+ {
+ 0x80, 0xC1, 0x51, 0x32, 0x5F, 0xBF, 0xC6, 0x78,
+ 0xB7, 0xBE, 0x4E, 0x40, 0xB3, 0x0F, 0x29, 0xFE,
+ 0x31, 0xCD, 0xBE, 0x1C, 0x84, 0x12, 0x6E, 0x00,
+ 0x6D, 0xF3, 0xC1, 0x85, 0x24, 0xBD, 0x2D, 0x6C
+ },
+ {
+ 0xA6, 0x42, 0x26, 0x73, 0x01, 0x66, 0x9D, 0xF2,
+ 0x61, 0xB8, 0x39, 0xF8, 0x73, 0x65, 0x76, 0x29,
+ 0x05, 0xFF, 0x32, 0x0A, 0x0A, 0x2F, 0xC4, 0xBD,
+ 0xC4, 0x8E, 0x5A, 0x8E, 0x15, 0xD1, 0x32, 0x33
+ },
+ {
+ 0x0F, 0x8B, 0x10, 0x99, 0x38, 0x60, 0x93, 0x7A,
+ 0x74, 0xCC, 0x2D, 0xE4, 0x0A, 0x27, 0x31, 0xDD,
+ 0x99, 0x54, 0xB6, 0x54, 0xBB, 0x94, 0xC3, 0x4E,
+ 0x87, 0x66, 0x52, 0xE9, 0x8D, 0x4B, 0xBD, 0x16
+ },
+ {
+ 0xE6, 0x34, 0xA5, 0x85, 0x12, 0x49, 0x32, 0x73,
+ 0x26, 0x0F, 0x10, 0xD4, 0x49, 0x53, 0xCD, 0x99,
+ 0x8E, 0x34, 0xCB, 0x82, 0x81, 0xC4, 0x1B, 0xF4,
+ 0x2E, 0x0A, 0xE2, 0xF2, 0x5C, 0xBD, 0x1F, 0x75
+ },
+ {
+ 0xBD, 0xE6, 0xAF, 0x9B, 0xAF, 0x3C, 0x07, 0xE9,
+ 0x54, 0x23, 0xCA, 0xB5, 0x04, 0xDE, 0xE7, 0x0E,
+ 0xDC, 0xC3, 0x31, 0x8B, 0x22, 0xDD, 0x1E, 0xB6,
+ 0xFD, 0x85, 0xBE, 0x44, 0x7A, 0xC9, 0xF2, 0x09
+ },
+ {
+ 0x91, 0x4B, 0x37, 0xAB, 0x5B, 0x8C, 0xFD, 0xE6,
+ 0xA4, 0x80, 0x46, 0x6A, 0x0D, 0x82, 0x43, 0x2C,
+ 0x7D, 0x76, 0x32, 0x8E, 0x9A, 0x88, 0xEF, 0x5B,
+ 0x4F, 0x52, 0x42, 0x9F, 0x7A, 0x3F, 0xFC, 0x7D
+ },
+ {
+ 0x55, 0xBE, 0x66, 0xE9, 0xA5, 0xAA, 0x67, 0x1A,
+ 0x23, 0x88, 0x2E, 0xF3, 0xE7, 0xD9, 0xD3, 0x6E,
+ 0xA9, 0x54, 0x87, 0xDC, 0x71, 0xB7, 0x25, 0xA5,
+ 0xAD, 0x4B, 0x79, 0x8A, 0x87, 0x91, 0x43, 0xD0
+ },
+ {
+ 0x3F, 0xD0, 0x45, 0x89, 0x4B, 0x83, 0x6E, 0x44,
+ 0xE9, 0xCA, 0x75, 0xFB, 0xE3, 0xEA, 0xDC, 0x48,
+ 0x6C, 0xBB, 0xD0, 0xD8, 0xCE, 0xE1, 0xB3, 0xCF,
+ 0x14, 0xF7, 0x6E, 0x7F, 0x1E, 0x77, 0xAE, 0xF3
+ },
+ {
+ 0xCE, 0x60, 0x34, 0x3D, 0xC4, 0x87, 0x4B, 0x66,
+ 0x04, 0xE1, 0xFB, 0x23, 0x1E, 0x37, 0xEC, 0x1E,
+ 0xEC, 0x3F, 0x06, 0x56, 0x6E, 0x42, 0x8A, 0xE7,
+ 0x64, 0xEF, 0xFF, 0xA2, 0x30, 0xAD, 0xD4, 0x85
+ },
+ {
+ 0xE3, 0x8C, 0x9D, 0xF0, 0x24, 0xDE, 0x21, 0x53,
+ 0xD2, 0x26, 0x73, 0x8A, 0x0E, 0x5B, 0xA9, 0xB8,
+ 0xC6, 0x78, 0x4D, 0xAC, 0xA6, 0x5C, 0x22, 0xA7,
+ 0x62, 0x8E, 0xB5, 0x8E, 0xA0, 0xD4, 0x95, 0xA7
+ },
+ {
+ 0x8D, 0xFE, 0xC0, 0xD4, 0xF3, 0x65, 0x8A, 0x20,
+ 0xA0, 0xBA, 0xD6, 0x6F, 0x21, 0x60, 0x83, 0x2B,
+ 0x16, 0x4E, 0x70, 0x0A, 0x21, 0xEC, 0x5A, 0x01,
+ 0x65, 0xC3, 0x67, 0x72, 0xB2, 0x08, 0x61, 0x11
+ },
+ {
+ 0x44, 0x01, 0xB5, 0x0E, 0x09, 0x86, 0x5F, 0x42,
+ 0x38, 0x24, 0x3B, 0x82, 0x25, 0xCA, 0x40, 0xA0,
+ 0x8D, 0xBB, 0x46, 0x85, 0xF5, 0xF8, 0x62, 0xFB,
+ 0xDD, 0x72, 0x98, 0x04, 0x31, 0xA8, 0x5D, 0x3F
+ },
+ {
+ 0x86, 0x68, 0x94, 0x27, 0x88, 0xC4, 0xCE, 0x8A,
+ 0x33, 0x19, 0x0F, 0xFC, 0xFA, 0xD1, 0xC6, 0x78,
+ 0xC4, 0xFA, 0x41, 0xE9, 0x94, 0x17, 0x09, 0x4E,
+ 0x24, 0x0F, 0x4A, 0x43, 0xF3, 0x87, 0xA3, 0xB6
+ },
+ {
+ 0xA7, 0x28, 0x8D, 0x5E, 0x09, 0x80, 0x9B, 0x69,
+ 0x69, 0x84, 0xEC, 0xD5, 0x32, 0x6C, 0xDD, 0x84,
+ 0xFB, 0xE3, 0x5F, 0xCF, 0x67, 0x23, 0x5D, 0x81,
+ 0x1C, 0x82, 0x00, 0x25, 0x36, 0xA3, 0xC5, 0xE1
+ },
+ {
+ 0x8E, 0x92, 0x5C, 0x3C, 0x14, 0x6B, 0xAC, 0xF3,
+ 0x35, 0x1E, 0xC5, 0x32, 0x41, 0xAC, 0xE5, 0xF7,
+ 0x3E, 0x8F, 0xC9, 0xBD, 0x8C, 0x61, 0xCA, 0xD9,
+ 0x7F, 0xD7, 0x72, 0xB0, 0x7E, 0x1B, 0x83, 0x73
+ },
+ {
+ 0xC7, 0xEB, 0x9E, 0x6D, 0xED, 0x2F, 0x99, 0x3D,
+ 0x48, 0xB0, 0x17, 0x0D, 0xA2, 0x7C, 0x5B, 0x75,
+ 0x3B, 0x12, 0x17, 0x6B, 0xE1, 0x26, 0xC7, 0xBA,
+ 0x2D, 0x6A, 0xF8, 0x5F, 0x85, 0x93, 0xB7, 0x52
+ },
+ {
+ 0xCA, 0x27, 0xF1, 0x6F, 0x94, 0xE4, 0xEC, 0x0E,
+ 0x62, 0x8E, 0x7F, 0x8A, 0xEF, 0xC6, 0x65, 0x7B,
+ 0xED, 0xC9, 0x37, 0x42, 0x96, 0x59, 0x40, 0xAE,
+ 0x78, 0x6A, 0x73, 0xB5, 0xFD, 0x59, 0x3B, 0x97
+ },
+ {
+ 0x8C, 0x21, 0xE6, 0x56, 0x8B, 0xC6, 0xDC, 0x00,
+ 0xE3, 0xD6, 0xEB, 0xC0, 0x9E, 0xA9, 0xC2, 0xCE,
+ 0x00, 0x6C, 0xD3, 0x11, 0xD3, 0xB3, 0xE9, 0xCC,
+ 0x9D, 0x8D, 0xDB, 0xFB, 0x3C, 0x5A, 0x77, 0x76
+ },
+ {
+ 0x52, 0x56, 0x66, 0x96, 0x8B, 0x3B, 0x7D, 0x00,
+ 0x7B, 0xB9, 0x26, 0xB6, 0xEF, 0xDC, 0x7E, 0x21,
+ 0x2A, 0x31, 0x15, 0x4C, 0x9A, 0xE1, 0x8D, 0x43,
+ 0xEE, 0x0E, 0xB7, 0xE6, 0xB1, 0xA9, 0x38, 0xD3
+ },
+ {
+ 0xE0, 0x9A, 0x4F, 0xA5, 0xC2, 0x8B, 0xDC, 0xD7,
+ 0xC8, 0x39, 0x84, 0x0E, 0x0A, 0x38, 0x3E, 0x4F,
+ 0x7A, 0x10, 0x2D, 0x0B, 0x1B, 0xC8, 0x49, 0xC9,
+ 0x49, 0x62, 0x7C, 0x41, 0x00, 0xC1, 0x7D, 0xD3
+ },
+ {
+ 0xC1, 0x9F, 0x3E, 0x29, 0x5D, 0xB2, 0xFC, 0x0E,
+ 0x74, 0x81, 0xC4, 0xF1, 0x6A, 0xF0, 0x11, 0x55,
+ 0xDD, 0xB0, 0xD7, 0xD1, 0x38, 0x3D, 0x4A, 0x1F,
+ 0xF1, 0x69, 0x9D, 0xB7, 0x11, 0x77, 0x34, 0x0C
+ },
+ {
+ 0x76, 0x9E, 0x67, 0x8C, 0x0A, 0x09, 0x09, 0xA2,
+ 0x02, 0x1C, 0x4D, 0xC2, 0x6B, 0x1A, 0x3C, 0x9B,
+ 0xC5, 0x57, 0xAD, 0xB2, 0x1A, 0x50, 0x83, 0x4C,
+ 0xDC, 0x5C, 0x92, 0x93, 0xF7, 0x53, 0x65, 0xF8
+ },
+ {
+ 0xB6, 0x48, 0x74, 0xAD, 0xAB, 0x6B, 0xCB, 0x85,
+ 0xB9, 0x4B, 0xD9, 0xA6, 0xC5, 0x65, 0xD0, 0xD2,
+ 0xBC, 0x35, 0x44, 0x5D, 0x75, 0x28, 0xBC, 0x85,
+ 0xB4, 0x1F, 0xDC, 0x79, 0xDC, 0x76, 0xE3, 0x4F
+ },
+ {
+ 0xFA, 0xF2, 0x50, 0xDE, 0x15, 0x82, 0x0F, 0x7F,
+ 0xC6, 0x10, 0xDD, 0x53, 0xEE, 0xAE, 0x44, 0x60,
+ 0x1C, 0x3E, 0xFF, 0xA3, 0xAC, 0xCD, 0x08, 0x8E,
+ 0xB6, 0x69, 0x05, 0xBB, 0x26, 0x53, 0xBE, 0x8C
+ },
+ {
+ 0x1E, 0x20, 0x38, 0x73, 0x9B, 0x2C, 0x01, 0x8B,
+ 0x0E, 0x9E, 0x0E, 0x1E, 0x52, 0x2F, 0xD9, 0x65,
+ 0x12, 0x87, 0xEE, 0x6E, 0x36, 0x65, 0x91, 0x9B,
+ 0x24, 0xC2, 0x12, 0x4F, 0x0C, 0x1A, 0x3F, 0x3A
+ },
+ {
+ 0x5F, 0xEC, 0x3A, 0xA0, 0x08, 0x61, 0xDE, 0x1A,
+ 0xC5, 0xDA, 0xB3, 0xC1, 0x37, 0x06, 0x5D, 0x1E,
+ 0x01, 0xBB, 0x03, 0xF6, 0x9D, 0xCC, 0x7D, 0x1C,
+ 0xF7, 0xCA, 0x4F, 0x43, 0x56, 0xAE, 0xC9, 0xA3
+ },
+ {
+ 0x44, 0x51, 0xFE, 0x6B, 0xBE, 0xF3, 0x93, 0x43,
+ 0x91, 0x92, 0x44, 0xC5, 0x1D, 0xAE, 0x1E, 0xA9,
+ 0xA9, 0x54, 0xCF, 0x2C, 0x09, 0x66, 0xAB, 0x04,
+ 0x5B, 0x15, 0x52, 0x1E, 0xCF, 0x35, 0x00, 0x81
+ },
+ {
+ 0x8C, 0x62, 0x2F, 0xA2, 0x16, 0x0E, 0x8E, 0x99,
+ 0x18, 0x13, 0xF1, 0x80, 0xBF, 0xEC, 0x0B, 0x43,
+ 0x1C, 0x6D, 0xBF, 0xA2, 0x95, 0x6D, 0x91, 0x75,
+ 0x81, 0x6A, 0x23, 0xC3, 0x82, 0xC4, 0xF2, 0x00
+ },
+ {
+ 0x81, 0x7D, 0x5C, 0x8F, 0x92, 0xE7, 0xB5, 0xCA,
+ 0x57, 0xF5, 0xE1, 0x63, 0x90, 0x16, 0xAD, 0x57,
+ 0x60, 0xE4, 0x46, 0xD6, 0xE9, 0xCA, 0xA7, 0x49,
+ 0x84, 0x14, 0xAC, 0xE8, 0x22, 0x80, 0xB5, 0xCD
+ },
+ {
+ 0xA6, 0xA1, 0xAD, 0x58, 0xCE, 0xE5, 0x4E, 0x69,
+ 0xCB, 0xBC, 0xAA, 0x87, 0xDF, 0x07, 0xA6, 0x70,
+ 0x7E, 0xB2, 0x24, 0x73, 0x9C, 0x21, 0x76, 0x13,
+ 0x46, 0x0A, 0xB4, 0x54, 0xB4, 0x59, 0xCA, 0x9C
+ },
+ {
+ 0x63, 0xB8, 0x47, 0x27, 0x52, 0x26, 0x60, 0x5B,
+ 0xE6, 0x76, 0x81, 0x25, 0x8F, 0x7D, 0x00, 0xBB,
+ 0xB3, 0x07, 0xC6, 0x6F, 0x19, 0x59, 0xBF, 0x2E,
+ 0x46, 0x7A, 0x41, 0xAE, 0xE7, 0x14, 0xE5, 0x5C
+ },
+ {
+ 0xFE, 0x52, 0xEB, 0xE5, 0xCF, 0xCF, 0xE6, 0xA2,
+ 0x29, 0x7B, 0x53, 0x9F, 0xA3, 0xDA, 0xDB, 0xD6,
+ 0xEB, 0xD2, 0x01, 0xAA, 0x2C, 0xA1, 0x35, 0x63,
+ 0xE3, 0xD7, 0xF1, 0x4D, 0x15, 0xAB, 0xFF, 0x63
+ },
+ {
+ 0xB7, 0xBE, 0xF9, 0xFA, 0x5A, 0x3D, 0x10, 0x42,
+ 0x62, 0x46, 0xB5, 0xF6, 0x58, 0xC0, 0x8F, 0xDF,
+ 0x80, 0x66, 0xEA, 0xA3, 0xE5, 0x5A, 0x2F, 0x7D,
+ 0xA1, 0x59, 0x1E, 0x05, 0xC8, 0x7D, 0xF8, 0xC7
+ },
+ {
+ 0xDE, 0xD1, 0xD6, 0xCA, 0xA9, 0xF8, 0xF3, 0xBD,
+ 0xA9, 0x2C, 0xEA, 0x7F, 0x65, 0x49, 0xB1, 0xFB,
+ 0x86, 0xA2, 0x21, 0x14, 0x78, 0xC4, 0xEC, 0x28,
+ 0x9B, 0x83, 0x7E, 0xFC, 0x2B, 0x5C, 0x27, 0xD7
+ },
+ {
+ 0x9F, 0x30, 0x00, 0x8A, 0x2E, 0xB0, 0x50, 0xF1,
+ 0x8E, 0x56, 0xA7, 0x6B, 0xE9, 0x20, 0x91, 0xB2,
+ 0xFD, 0xC1, 0x64, 0xD5, 0x6E, 0x32, 0xC8, 0x7D,
+ 0xD6, 0x4C, 0x9E, 0x3A, 0x61, 0x10, 0x41, 0xB1
+ },
+ {
+ 0x01, 0x0B, 0x6A, 0x3B, 0x11, 0x86, 0x00, 0x88,
+ 0xF0, 0xAB, 0xC8, 0x0A, 0x89, 0x72, 0xCB, 0xBC,
+ 0x32, 0x9D, 0x52, 0x75, 0x34, 0x29, 0x50, 0xEB,
+ 0x9A, 0x04, 0x5A, 0xFD, 0xC8, 0xBB, 0xED, 0x24
+ },
+ {
+ 0x0C, 0xD2, 0x10, 0xAA, 0xC1, 0x1F, 0x1C, 0x1C,
+ 0xED, 0x49, 0x7F, 0x67, 0x3E, 0x53, 0xDB, 0x68,
+ 0xC3, 0xEC, 0x36, 0x07, 0xF0, 0xC5, 0x78, 0x7D,
+ 0xDC, 0x60, 0xA3, 0x55, 0xDF, 0xE5, 0x6C, 0x25
+ },
+ {
+ 0x0E, 0x56, 0xFD, 0x01, 0xDA, 0x3B, 0x4F, 0x8B,
+ 0xE2, 0xC9, 0x90, 0x55, 0x2A, 0xAC, 0x8D, 0x1E,
+ 0x8D, 0xA2, 0x09, 0xBC, 0xF4, 0xAA, 0xD4, 0xFF,
+ 0xB5, 0x42, 0x7F, 0xD6, 0x31, 0x72, 0x46, 0x3E
+ },
+ {
+ 0xD6, 0xD5, 0xCD, 0xB1, 0x14, 0x40, 0xE3, 0x4A,
+ 0xCA, 0x3A, 0x2F, 0xCF, 0x30, 0xF5, 0x9E, 0x08,
+ 0xB1, 0x1A, 0x2A, 0x3D, 0xE5, 0x39, 0xE3, 0xE6,
+ 0x51, 0x3E, 0xD7, 0x8A, 0x4F, 0xEE, 0x51, 0x3B
+ },
+ {
+ 0xAA, 0x35, 0xAC, 0x90, 0x68, 0x06, 0x70, 0xC7,
+ 0x32, 0xED, 0x1E, 0xF3, 0x7E, 0x8C, 0xBA, 0xAE,
+ 0x49, 0xA4, 0xD8, 0x8E, 0xCF, 0x4D, 0xF2, 0xB6,
+ 0x89, 0xA0, 0xF1, 0x01, 0xB7, 0x56, 0xAE, 0x47
+ },
+ {
+ 0x27, 0x8E, 0x56, 0x12, 0x88, 0x72, 0x26, 0x30,
+ 0xE2, 0x6A, 0x5F, 0xC9, 0x54, 0xBF, 0x2D, 0xCD,
+ 0x6A, 0x65, 0x81, 0x67, 0x39, 0xAB, 0xEE, 0x7B,
+ 0xE1, 0x43, 0x07, 0xA9, 0x61, 0x74, 0xE5, 0xB0
+ },
+ {
+ 0xAB, 0x4B, 0x2C, 0xA1, 0xA2, 0xB3, 0x49, 0x98,
+ 0x15, 0x24, 0xB6, 0x15, 0x54, 0x62, 0xF0, 0xFF,
+ 0x10, 0x60, 0xBF, 0x9B, 0xFA, 0x07, 0xFB, 0x9E,
+ 0xC6, 0x9C, 0xA4, 0x71, 0x64, 0x5B, 0x6A, 0x18
+ },
+ {
+ 0x18, 0xA9, 0xBB, 0xEC, 0x3C, 0x8E, 0x1F, 0x8E,
+ 0xE9, 0x57, 0x12, 0x97, 0xA9, 0x34, 0x36, 0xDE,
+ 0x42, 0x7C, 0xD2, 0x70, 0xEC, 0x69, 0xDF, 0xE8,
+ 0x88, 0xDB, 0x7D, 0xBF, 0x10, 0xB6, 0x49, 0x93
+ },
+ {
+ 0xBA, 0xFC, 0x7E, 0x43, 0xD2, 0x65, 0xA1, 0x73,
+ 0x02, 0x1A, 0x9D, 0x9E, 0x58, 0x3D, 0x60, 0xED,
+ 0x42, 0xA8, 0x03, 0xFA, 0xCD, 0x6B, 0x83, 0x60,
+ 0xDE, 0x1F, 0x91, 0x68, 0x35, 0x38, 0x9B, 0xF0
+ },
+ {
+ 0xA5, 0xB6, 0x7B, 0xE9, 0x50, 0xFB, 0xC2, 0xF0,
+ 0xDD, 0x32, 0x3A, 0x79, 0xA1, 0x9E, 0x3E, 0xD1,
+ 0xF4, 0xAE, 0x4B, 0xA7, 0x89, 0x4F, 0x93, 0x0E,
+ 0xA5, 0xEF, 0x73, 0x4D, 0xE7, 0xDB, 0x83, 0xAE
+ },
+ {
+ 0xBF, 0x1E, 0x65, 0xF3, 0xCD, 0x84, 0x98, 0x88,
+ 0x4D, 0x9D, 0x5C, 0x19, 0xEB, 0xF7, 0xB9, 0x16,
+ 0x06, 0x76, 0x37, 0x60, 0x4E, 0x26, 0xDB, 0xE2,
+ 0xB7, 0x28, 0x8E, 0xCB, 0x11, 0x42, 0x60, 0x68
+ },
+ {
+ 0xC3, 0x34, 0x2C, 0xF9, 0xCB, 0xBF, 0x29, 0xD4,
+ 0x06, 0xD7, 0x89, 0x5D, 0xD4, 0xD9, 0x54, 0x8D,
+ 0x4A, 0xC7, 0x8B, 0x4D, 0x00, 0xE9, 0xB6, 0x3E,
+ 0x20, 0x3E, 0x5E, 0x19, 0xE9, 0x97, 0x46, 0x20
+ },
+ {
+ 0x1C, 0x0B, 0xE6, 0x02, 0x77, 0x43, 0x4B, 0x0E,
+ 0x00, 0x4B, 0x7B, 0x38, 0x8A, 0x37, 0x55, 0x9F,
+ 0x84, 0xB3, 0x0C, 0x6C, 0xF8, 0x60, 0x0F, 0x52,
+ 0x8B, 0xFC, 0xD3, 0x3C, 0xAF, 0x52, 0xCB, 0x1E
+ },
+ {
+ 0x73, 0x95, 0x45, 0x30, 0xD0, 0x3F, 0x10, 0xBE,
+ 0xF5, 0x2A, 0xD5, 0xBC, 0x7F, 0xB4, 0xC0, 0x76,
+ 0xF8, 0x3F, 0x63, 0x31, 0xC8, 0xBD, 0x1E, 0xEE,
+ 0xC3, 0x88, 0x7F, 0x4A, 0xA2, 0x06, 0x92, 0x40
+ },
+ {
+ 0x69, 0xC1, 0x1E, 0xE0, 0x49, 0x44, 0xDE, 0xA9,
+ 0x85, 0xAC, 0x9F, 0x13, 0x96, 0x0E, 0x73, 0x98,
+ 0x0E, 0x1B, 0xB0, 0xE3, 0x09, 0xF4, 0x38, 0x4A,
+ 0x16, 0x76, 0xF8, 0xEF, 0xAB, 0x38, 0x42, 0x88
+ },
+ {
+ 0x36, 0xFB, 0x8F, 0xDE, 0x0E, 0xC2, 0x8C, 0xE8,
+ 0x53, 0xFB, 0x71, 0x75, 0xC1, 0xB7, 0x9D, 0xA3,
+ 0xB5, 0xE8, 0xC3, 0x91, 0x86, 0xE7, 0x8A, 0xAE,
+ 0xCE, 0x54, 0x64, 0xDB, 0xD9, 0xFE, 0x2A, 0xA2
+ },
+ {
+ 0x6B, 0xB2, 0xA0, 0x9D, 0xFC, 0xAF, 0x96, 0x96,
+ 0x2D, 0xE0, 0x0C, 0x8A, 0x08, 0x2D, 0x6D, 0xF9,
+ 0x32, 0x2B, 0x49, 0x66, 0xAE, 0x8D, 0x2E, 0xCF,
+ 0x73, 0x24, 0x11, 0xA7, 0x6A, 0x1A, 0x0E, 0xE6
+ },
+ {
+ 0x74, 0x12, 0xE7, 0xDD, 0x1B, 0xF1, 0xAA, 0x93,
+ 0x97, 0x41, 0x1B, 0xBA, 0x4D, 0x3E, 0x02, 0x76,
+ 0xD2, 0xE7, 0xA1, 0xA2, 0x9A, 0x24, 0x77, 0x15,
+ 0x7A, 0xD6, 0x03, 0x60, 0xD3, 0x3D, 0x4E, 0x76
+ },
+ {
+ 0xDD, 0xDE, 0xAF, 0xCF, 0xC7, 0x23, 0x21, 0xC8,
+ 0x49, 0xFB, 0x25, 0x94, 0x7A, 0xB4, 0x2C, 0x1A,
+ 0xF2, 0xA5, 0xE4, 0x3F, 0xEF, 0x68, 0x1B, 0xE4,
+ 0x2C, 0x7E, 0xAF, 0x36, 0x60, 0x08, 0x0A, 0xD3
+ },
+ {
+ 0x9D, 0xEF, 0xEB, 0xAD, 0xBD, 0xCB, 0x0A, 0x0E,
+ 0x7F, 0xF9, 0x92, 0xF9, 0x47, 0xCE, 0xD3, 0xD0,
+ 0xA4, 0xC8, 0x99, 0xE6, 0x4F, 0xE7, 0x73, 0x60,
+ 0xE8, 0x1E, 0x1F, 0x0E, 0x97, 0xF8, 0xC1, 0xA2
+ },
+ {
+ 0x84, 0x4C, 0x59, 0xFB, 0xE6, 0x47, 0x6F, 0xD1,
+ 0x89, 0x23, 0x99, 0x54, 0xF1, 0x7E, 0x36, 0xE1,
+ 0xF6, 0x9E, 0x24, 0xAA, 0xED, 0x5D, 0x5C, 0x8B,
+ 0x84, 0x05, 0xEF, 0x2A, 0x83, 0x0C, 0xC2, 0xA0
+ },
+ {
+ 0xFF, 0x3F, 0xAF, 0xB6, 0x77, 0x86, 0xE0, 0x1A,
+ 0x0C, 0x38, 0xEA, 0xDF, 0x99, 0xC4, 0xCA, 0xE8,
+ 0x02, 0x9D, 0xA8, 0xCF, 0x29, 0x87, 0x5F, 0xC4,
+ 0x19, 0xBF, 0x68, 0x00, 0x09, 0xB3, 0xBD, 0xB3
+ },
+ {
+ 0xCA, 0x67, 0x60, 0xF3, 0x45, 0x67, 0x8F, 0x30,
+ 0xA2, 0x8D, 0x62, 0x82, 0x94, 0x27, 0x2A, 0x19,
+ 0xE3, 0x07, 0x2E, 0xBC, 0x61, 0xB1, 0x9F, 0xF1,
+ 0x3B, 0x31, 0x89, 0x73, 0xE9, 0x7C, 0x27, 0x38
+ },
+ {
+ 0xC0, 0x8E, 0x1A, 0x90, 0x47, 0xC5, 0x05, 0x26,
+ 0x4A, 0x16, 0x44, 0x7C, 0x9E, 0xD9, 0x81, 0xA7,
+ 0x19, 0xD3, 0x81, 0xF2, 0x8E, 0x60, 0x5F, 0xD7,
+ 0xCA, 0xA9, 0xE8, 0xBD, 0xBB, 0x42, 0x99, 0x6A
+ },
+ {
+ 0xF1, 0x73, 0xBA, 0x9D, 0x45, 0x84, 0xCD, 0x12,
+ 0x60, 0x50, 0xC6, 0x9F, 0xC2, 0x19, 0xA9, 0x19,
+ 0x0A, 0x0B, 0xF0, 0xAE, 0xCE, 0xCB, 0xE6, 0x11,
+ 0xBE, 0xED, 0x19, 0x3D, 0xA6, 0xCA, 0x4D, 0xE7
+ },
+ {
+ 0xB1, 0x84, 0x87, 0x65, 0x20, 0xDE, 0xD8, 0xBD,
+ 0x7D, 0xE2, 0x5E, 0xAE, 0xFB, 0xD3, 0xE0, 0x36,
+ 0x88, 0xC3, 0xBE, 0x39, 0xC1, 0x9F, 0xB7, 0x3E,
+ 0x1F, 0x0E, 0xCC, 0xAC, 0x7C, 0xC0, 0xF0, 0x14
+ },
+ {
+ 0x90, 0x25, 0xDB, 0x07, 0x58, 0xBD, 0xFB, 0x48,
+ 0xF0, 0x66, 0x7E, 0xBD, 0x7E, 0x12, 0x02, 0x46,
+ 0x59, 0x8F, 0xED, 0x01, 0xC2, 0x58, 0x76, 0x4F,
+ 0xA0, 0xFA, 0xE3, 0x34, 0xA2, 0xA0, 0x0A, 0x97
+ },
+ {
+ 0xE8, 0x3D, 0x80, 0x86, 0xFA, 0xBC, 0x46, 0x0D,
+ 0x5E, 0xFC, 0x45, 0x9F, 0x95, 0xA2, 0x68, 0xF5,
+ 0xDC, 0x4A, 0xC2, 0x84, 0x09, 0x3C, 0x24, 0x7C,
+ 0xA6, 0xEC, 0x84, 0x1A, 0xD6, 0x18, 0x3F, 0xE1
+ },
+ {
+ 0xCC, 0x9D, 0xF4, 0x1D, 0x35, 0xAA, 0x75, 0x92,
+ 0x8C, 0x18, 0x5F, 0x73, 0x93, 0x66, 0x61, 0x10,
+ 0xB8, 0x0F, 0x09, 0x86, 0xA2, 0x21, 0xC3, 0x70,
+ 0xF4, 0x5C, 0x2E, 0xB9, 0x01, 0x6C, 0x9A, 0x3B
+ },
+ {
+ 0x92, 0xF9, 0xA5, 0x94, 0x95, 0x45, 0x90, 0xFA,
+ 0x81, 0x98, 0x17, 0xE5, 0xD1, 0xC2, 0x8A, 0xAB,
+ 0x2B, 0x1C, 0xC5, 0x04, 0xD8, 0x6D, 0xBA, 0x44,
+ 0x36, 0x76, 0xBD, 0xF8, 0x66, 0x79, 0x68, 0x11
+ },
+ {
+ 0x72, 0x95, 0x62, 0xA1, 0xE0, 0x7B, 0x0E, 0x26,
+ 0x05, 0x49, 0x48, 0x09, 0xBD, 0x48, 0x0F, 0x15,
+ 0x37, 0xCE, 0xA1, 0x0D, 0xCA, 0xD4, 0x3E, 0xF9,
+ 0xF6, 0x8C, 0x66, 0xE8, 0x25, 0xDC, 0x46, 0xB1
+ },
+ {
+ 0x26, 0xF1, 0x60, 0xAB, 0x96, 0xF5, 0x58, 0x20,
+ 0x45, 0x14, 0x6E, 0xAF, 0xF2, 0xE2, 0xA8, 0xD4,
+ 0xDA, 0xB2, 0x98, 0xB4, 0xC5, 0x7E, 0x11, 0x7C,
+ 0xDF, 0xC5, 0xD0, 0x25, 0xC9, 0x2A, 0x22, 0x68
+ },
+ {
+ 0x87, 0xEB, 0xE7, 0x21, 0x38, 0x38, 0x73, 0xD2,
+ 0x47, 0xF8, 0x61, 0x82, 0xE3, 0xF5, 0x99, 0xA7,
+ 0x63, 0x4F, 0xCA, 0xEC, 0x5E, 0x07, 0xB1, 0xE8,
+ 0x3E, 0xBB, 0x79, 0x62, 0x5B, 0xA3, 0x54, 0xE6
+ },
+ {
+ 0xE0, 0x8D, 0x38, 0x9F, 0x75, 0x69, 0x4A, 0xDC,
+ 0x99, 0x6C, 0x22, 0xF5, 0x5D, 0x4F, 0x85, 0x9F,
+ 0xFD, 0x0C, 0x13, 0x19, 0xFF, 0x9C, 0xED, 0xF7,
+ 0x8C, 0x31, 0xBE, 0x84, 0xB6, 0xF2, 0x1A, 0xBC
+ },
+ {
+ 0x13, 0x63, 0xE2, 0x29, 0x13, 0xC6, 0xE1, 0x8E,
+ 0x7A, 0xA6, 0x5B, 0x83, 0xE7, 0x51, 0xC8, 0xA2,
+ 0xC6, 0x1B, 0x0F, 0x30, 0x71, 0x55, 0x86, 0x5A,
+ 0x57, 0xDB, 0xA5, 0x69, 0xA9, 0x9C, 0x7B, 0x0E
+ },
+ {
+ 0x88, 0x78, 0x08, 0x8E, 0xB2, 0xD1, 0xF6, 0xD0,
+ 0xBB, 0x48, 0x1B, 0x4B, 0xB1, 0x87, 0xDA, 0x04,
+ 0xBC, 0xD8, 0xC2, 0xC6, 0x39, 0xF0, 0x05, 0xB0,
+ 0x80, 0x54, 0xCC, 0x41, 0x75, 0x39, 0x05, 0xFB
+ },
+ {
+ 0x04, 0x18, 0xD6, 0x0D, 0x05, 0xB4, 0xE1, 0x24,
+ 0x64, 0x6E, 0xE5, 0x0E, 0x77, 0x49, 0xA1, 0xD2,
+ 0x09, 0x45, 0x7B, 0xC5, 0x43, 0xE3, 0xCC, 0x11,
+ 0x30, 0x27, 0x4A, 0xEA, 0x0F, 0x7B, 0xF3, 0xC1
+ },
+ {
+ 0x7A, 0x39, 0x7E, 0x50, 0x3F, 0x29, 0x3B, 0xC4,
+ 0x2D, 0x5F, 0x7E, 0xF5, 0xEC, 0x37, 0x87, 0x24,
+ 0x60, 0xA4, 0xF5, 0xB5, 0xCC, 0xDE, 0x77, 0xFB,
+ 0x4D, 0x47, 0xAC, 0x06, 0x81, 0xE5, 0xA0, 0x49
+ },
+ {
+ 0x5C, 0x0D, 0x29, 0x83, 0xE7, 0x2A, 0x6D, 0xD4,
+ 0xE6, 0x52, 0xD7, 0x23, 0xC1, 0xDF, 0xC1, 0x2B,
+ 0x41, 0x4C, 0x87, 0x3D, 0x4A, 0xB4, 0xA0, 0xA1,
+ 0x50, 0x40, 0x8E, 0xB3, 0x43, 0x47, 0xE9, 0x95
+ },
+ {
+ 0x56, 0x23, 0x36, 0x54, 0x53, 0xC0, 0x49, 0x89,
+ 0xC7, 0xCF, 0x33, 0x63, 0x5E, 0x0F, 0xC4, 0xCD,
+ 0xDD, 0x68, 0x6F, 0xC9, 0x5A, 0x33, 0xDF, 0xED,
+ 0xCF, 0x33, 0x35, 0x79, 0x4C, 0x7D, 0xC3, 0x44
+ },
+ {
+ 0x11, 0xF6, 0xDA, 0xD1, 0x88, 0x02, 0x8F, 0xDF,
+ 0x13, 0x78, 0xA2, 0x56, 0xE4, 0x57, 0x0E, 0x90,
+ 0x63, 0x10, 0x7B, 0x8F, 0x79, 0xDC, 0x66, 0x3F,
+ 0xA5, 0x55, 0x6F, 0x56, 0xFD, 0x44, 0xA0, 0xF0
+ },
+ {
+ 0x0E, 0xD8, 0x16, 0x17, 0x97, 0xEC, 0xEE, 0x88,
+ 0x1E, 0x7D, 0x0E, 0x3F, 0x4C, 0x5F, 0xB8, 0x39,
+ 0xC8, 0x4E, 0xB7, 0xA9, 0x24, 0x26, 0x57, 0xCC,
+ 0x48, 0x30, 0x68, 0x07, 0xB3, 0x2B, 0xEF, 0xDE
+ },
+ {
+ 0x73, 0x66, 0x67, 0xC9, 0x36, 0x4C, 0xE1, 0x2D,
+ 0xB8, 0xF6, 0xB1, 0x43, 0xC6, 0xC1, 0x78, 0xCD,
+ 0xEF, 0x1E, 0x14, 0x45, 0xBC, 0x5A, 0x2F, 0x26,
+ 0x34, 0xF0, 0x8E, 0x99, 0x32, 0x27, 0x3C, 0xAA
+ },
+ {
+ 0xE1, 0x5F, 0x36, 0x8B, 0x44, 0x06, 0xC1, 0xF6,
+ 0x55, 0x57, 0xC8, 0x35, 0x5C, 0xBE, 0x69, 0x4B,
+ 0x63, 0x3E, 0x26, 0xF1, 0x55, 0xF5, 0x2B, 0x7D,
+ 0xA9, 0x4C, 0xFB, 0x23, 0xFD, 0x4A, 0x5D, 0x96
+ },
+ {
+ 0x43, 0x7A, 0xB2, 0xD7, 0x4F, 0x50, 0xCA, 0x86,
+ 0xCC, 0x3D, 0xE9, 0xBE, 0x70, 0xE4, 0x55, 0x48,
+ 0x25, 0xE3, 0x3D, 0x82, 0x4B, 0x3A, 0x49, 0x23,
+ 0x62, 0xE2, 0xE9, 0xD6, 0x11, 0xBC, 0x57, 0x9D
+ },
+ {
+ 0x2B, 0x91, 0x58, 0xC7, 0x22, 0x89, 0x8E, 0x52,
+ 0x6D, 0x2C, 0xDD, 0x3F, 0xC0, 0x88, 0xE9, 0xFF,
+ 0xA7, 0x9A, 0x9B, 0x73, 0xB7, 0xD2, 0xD2, 0x4B,
+ 0xC4, 0x78, 0xE2, 0x1C, 0xDB, 0x3B, 0x67, 0x63
+ },
+ {
+ 0x0C, 0x8A, 0x36, 0x59, 0x7D, 0x74, 0x61, 0xC6,
+ 0x3A, 0x94, 0x73, 0x28, 0x21, 0xC9, 0x41, 0x85,
+ 0x6C, 0x66, 0x83, 0x76, 0x60, 0x6C, 0x86, 0xA5,
+ 0x2D, 0xE0, 0xEE, 0x41, 0x04, 0xC6, 0x15, 0xDB
+ },
+};
+
+
+
+
+static const uint8_t blake2bp_kat[KAT_LENGTH][BLAKE2B_OUTBYTES] =
+{
+ {
+ 0xB5, 0xEF, 0x81, 0x1A, 0x80, 0x38, 0xF7, 0x0B,
+ 0x62, 0x8F, 0xA8, 0xB2, 0x94, 0xDA, 0xAE, 0x74,
+ 0x92, 0xB1, 0xEB, 0xE3, 0x43, 0xA8, 0x0E, 0xAA,
+ 0xBB, 0xF1, 0xF6, 0xAE, 0x66, 0x4D, 0xD6, 0x7B,
+ 0x9D, 0x90, 0xB0, 0x12, 0x07, 0x91, 0xEA, 0xB8,
+ 0x1D, 0xC9, 0x69, 0x85, 0xF2, 0x88, 0x49, 0xF6,
+ 0xA3, 0x05, 0x18, 0x6A, 0x85, 0x50, 0x1B, 0x40,
+ 0x51, 0x14, 0xBF, 0xA6, 0x78, 0xDF, 0x93, 0x80
+ },
+ {
+ 0xA1, 0x39, 0x28, 0x0E, 0x72, 0x75, 0x7B, 0x72,
+ 0x3E, 0x64, 0x73, 0xD5, 0xBE, 0x59, 0xF3, 0x6E,
+ 0x9D, 0x50, 0xFC, 0x5C, 0xD7, 0xD4, 0x58, 0x5C,
+ 0xBC, 0x09, 0x80, 0x48, 0x95, 0xA3, 0x6C, 0x52,
+ 0x12, 0x42, 0xFB, 0x27, 0x89, 0xF8, 0x5C, 0xB9,
+ 0xE3, 0x54, 0x91, 0xF3, 0x1D, 0x4A, 0x69, 0x52,
+ 0xF9, 0xD8, 0xE0, 0x97, 0xAE, 0xF9, 0x4F, 0xA1,
+ 0xCA, 0x0B, 0x12, 0x52, 0x57, 0x21, 0xF0, 0x3D
+ },
+ {
+ 0xEF, 0x8C, 0xDA, 0x96, 0x35, 0xD5, 0x06, 0x3A,
+ 0xF8, 0x11, 0x15, 0xDA, 0x3C, 0x52, 0x32, 0x5A,
+ 0x86, 0xE8, 0x40, 0x74, 0xF9, 0xF7, 0x24, 0xB7,
+ 0xCB, 0xD0, 0xB0, 0x85, 0x6F, 0xF0, 0x01, 0x77,
+ 0xCD, 0xD2, 0x83, 0xC2, 0x98, 0x32, 0x6C, 0xD0,
+ 0x91, 0x77, 0x54, 0xC5, 0x24, 0x1F, 0x14, 0x80,
+ 0xFB, 0x50, 0x9C, 0xF2, 0xD2, 0xC4, 0x49, 0x81,
+ 0x80, 0x77, 0xAE, 0x35, 0xFC, 0x33, 0x07, 0x37
+ },
+ {
+ 0x8C, 0xF9, 0x33, 0xA2, 0xD3, 0x61, 0xA3, 0xE6,
+ 0xA1, 0x36, 0xDB, 0xE4, 0xA0, 0x1E, 0x79, 0x03,
+ 0x79, 0x7A, 0xD6, 0xCE, 0x76, 0x6E, 0x2B, 0x91,
+ 0xB9, 0xB4, 0xA4, 0x03, 0x51, 0x27, 0xD6, 0x5F,
+ 0x4B, 0xE8, 0x65, 0x50, 0x11, 0x94, 0x18, 0xE2,
+ 0x2D, 0xA0, 0x0F, 0xD0, 0x6B, 0xF2, 0xB2, 0x75,
+ 0x96, 0xB3, 0x7F, 0x06, 0xBE, 0x0A, 0x15, 0x4A,
+ 0xAF, 0x7E, 0xCA, 0x54, 0xC4, 0x52, 0x0B, 0x97
+ },
+ {
+ 0x24, 0xDC, 0x1E, 0x6D, 0xC4, 0xE5, 0x1A, 0x3A,
+ 0x3C, 0x8D, 0xA6, 0x7A, 0xAC, 0xB4, 0xC5, 0x41,
+ 0xE4, 0x18, 0x18, 0xD1, 0x80, 0xE5, 0xBB, 0x69,
+ 0x75, 0x3D, 0xBB, 0xFF, 0x2F, 0x44, 0xD0, 0xE7,
+ 0xDA, 0x83, 0x03, 0x86, 0xBF, 0xC8, 0x3B, 0x27,
+ 0xA5, 0x9D, 0xBB, 0x62, 0xB9, 0x64, 0xFC, 0x8E,
+ 0xA6, 0xCB, 0xDF, 0x30, 0x49, 0xBF, 0xF8, 0x1F,
+ 0x24, 0xF3, 0x48, 0xDB, 0x4E, 0xFD, 0x0D, 0x07
+ },
+ {
+ 0xBC, 0x23, 0xF5, 0xAB, 0xDF, 0xFD, 0x6A, 0x32,
+ 0xA5, 0xD4, 0x08, 0x11, 0x26, 0x2E, 0xD4, 0x47,
+ 0x9E, 0xF7, 0x0B, 0x42, 0x33, 0xCA, 0x20, 0x5B,
+ 0xC5, 0xB9, 0xBF, 0x85, 0x96, 0x73, 0x19, 0x82,
+ 0xD0, 0x41, 0x69, 0xA9, 0x04, 0xDD, 0x43, 0xB0,
+ 0xE0, 0xF9, 0x48, 0x99, 0xF7, 0x33, 0x02, 0x2D,
+ 0x24, 0xD8, 0x4F, 0xAD, 0x0A, 0x99, 0x16, 0x00,
+ 0xF1, 0x97, 0x9B, 0x27, 0x2A, 0xD6, 0x20, 0x73
+ },
+ {
+ 0xEF, 0x10, 0x7F, 0xCD, 0x0D, 0x92, 0xD8, 0x4E,
+ 0xF5, 0xEF, 0x94, 0x63, 0xE6, 0xE9, 0x62, 0x41,
+ 0x25, 0x45, 0x29, 0xD2, 0xB9, 0x7F, 0xDB, 0xE5,
+ 0x64, 0x19, 0x07, 0x0A, 0xDB, 0xC7, 0xD5, 0x70,
+ 0x6F, 0xEB, 0x8F, 0x44, 0x95, 0x79, 0x81, 0x9E,
+ 0xD4, 0xBE, 0x61, 0x97, 0x85, 0xFF, 0xFA, 0xAF,
+ 0x0D, 0x97, 0x89, 0xCF, 0xE7, 0x26, 0x24, 0x9A,
+ 0xB0, 0x8C, 0x94, 0x68, 0xCB, 0x5F, 0xDE, 0x22
+ },
+ {
+ 0x23, 0x1F, 0xBF, 0xB7, 0xA1, 0xDD, 0xC5, 0xB7,
+ 0x49, 0x33, 0xA2, 0x85, 0xA4, 0x22, 0x4C, 0x04,
+ 0x9C, 0xBA, 0x14, 0x85, 0xCE, 0x35, 0x64, 0x0D,
+ 0x9C, 0x51, 0x6E, 0xD7, 0x8E, 0xAA, 0x22, 0x6D,
+ 0x36, 0xF6, 0x5B, 0x25, 0x89, 0xB8, 0x26, 0xC4,
+ 0x59, 0xFA, 0x6A, 0x91, 0xC4, 0x26, 0xFD, 0x2A,
+ 0x8A, 0xB4, 0x61, 0xC9, 0x76, 0x7E, 0x7B, 0xDD,
+ 0x99, 0x6B, 0xEF, 0x5A, 0x78, 0xF4, 0x81, 0xB7
+ },
+ {
+ 0x3A, 0x83, 0x1F, 0x2D, 0xA9, 0x69, 0xB9, 0xB7,
+ 0x36, 0x0E, 0x74, 0xEE, 0x53, 0xB5, 0x18, 0x98,
+ 0x0A, 0x5E, 0xBC, 0xDF, 0xD4, 0xEE, 0x23, 0xED,
+ 0x80, 0x5C, 0x26, 0x39, 0x4D, 0x18, 0x24, 0x20,
+ 0x8D, 0x7E, 0x8F, 0x63, 0x27, 0xD4, 0xEC, 0x87,
+ 0x97, 0x9C, 0xE4, 0xAF, 0x8A, 0xB0, 0x97, 0xD6,
+ 0x9E, 0x26, 0x1C, 0xA3, 0x2D, 0xB0, 0xEE, 0xFD,
+ 0xBC, 0x18, 0xD1, 0x63, 0x77, 0xA6, 0xBD, 0x20
+ },
+ {
+ 0x83, 0x49, 0xA2, 0x0F, 0xDD, 0xBA, 0xE1, 0xD8,
+ 0x47, 0x2B, 0x67, 0xF0, 0x34, 0x7A, 0xA0, 0xFD,
+ 0x40, 0x4D, 0x65, 0xC6, 0xFA, 0x14, 0x72, 0xB3,
+ 0x10, 0x39, 0x0D, 0x75, 0x65, 0xBA, 0x6B, 0xC1,
+ 0x02, 0x60, 0xD3, 0xDC, 0xE6, 0xA1, 0x4F, 0x4D,
+ 0xD9, 0xB8, 0xB3, 0xE0, 0xA0, 0xC4, 0x7F, 0x6D,
+ 0xB7, 0xE7, 0x10, 0x0A, 0x7A, 0x9B, 0x64, 0xA8,
+ 0x44, 0xF0, 0x10, 0x64, 0xD0, 0x79, 0x05, 0xC5
+ },
+ {
+ 0x23, 0x9A, 0xE3, 0xD6, 0x85, 0x9C, 0x7C, 0x97,
+ 0x2A, 0x5D, 0xC8, 0xB9, 0xC5, 0x5A, 0xEB, 0x93,
+ 0x85, 0x90, 0xCF, 0xB8, 0x55, 0x2A, 0xA3, 0x05,
+ 0xA6, 0xF6, 0xF3, 0x1F, 0xFA, 0x95, 0xA8, 0x40,
+ 0xF4, 0xEC, 0x36, 0xF6, 0xFB, 0x8F, 0x83, 0xB6,
+ 0x9C, 0x1D, 0xA9, 0x81, 0xFC, 0x9B, 0xA1, 0x63,
+ 0x60, 0xDB, 0x0F, 0x4F, 0x7C, 0x68, 0xEB, 0x54,
+ 0x3E, 0xD5, 0x8B, 0x28, 0x75, 0x6A, 0x1E, 0x0D
+ },
+ {
+ 0x7C, 0x56, 0x73, 0x28, 0x63, 0x08, 0x40, 0x8F,
+ 0xBC, 0x62, 0x24, 0x0E, 0x07, 0x47, 0x28, 0xB2,
+ 0x7A, 0x57, 0x5C, 0xAD, 0x2A, 0x15, 0x6E, 0x00,
+ 0xB5, 0xC0, 0x8B, 0x21, 0x8D, 0x88, 0x87, 0x79,
+ 0x1E, 0x47, 0xBF, 0x10, 0xB0, 0xBC, 0x61, 0xA5,
+ 0x82, 0x54, 0x5A, 0x24, 0x69, 0x63, 0x9C, 0xE6,
+ 0x28, 0xC4, 0x0F, 0x20, 0xEA, 0x8B, 0x84, 0x9C,
+ 0xD0, 0x05, 0x44, 0x5F, 0x29, 0xA0, 0x8C, 0xCE
+ },
+ {
+ 0xDD, 0x07, 0x7E, 0x76, 0x9E, 0x0D, 0xEF, 0x78,
+ 0xDD, 0x7A, 0xAD, 0xD5, 0x7D, 0x58, 0x42, 0x1B,
+ 0xDA, 0x3A, 0x1A, 0x4E, 0x69, 0x72, 0x05, 0x9F,
+ 0x8E, 0x64, 0x9C, 0xD6, 0xBC, 0xA4, 0x4A, 0x13,
+ 0xAB, 0x71, 0xEB, 0x53, 0x5D, 0x24, 0x49, 0x22,
+ 0x94, 0x84, 0x65, 0xD7, 0x3B, 0xD6, 0x4E, 0xFB,
+ 0x09, 0x10, 0x46, 0x94, 0x90, 0x66, 0x65, 0x36,
+ 0x03, 0x57, 0x5A, 0x2E, 0x89, 0x1E, 0xBD, 0x54
+ },
+ {
+ 0xB3, 0x6C, 0xEF, 0x28, 0x53, 0x2B, 0x40, 0xD8,
+ 0x17, 0x86, 0x28, 0xF0, 0xFA, 0xB5, 0xE5, 0xB4,
+ 0xA1, 0xDE, 0xC0, 0xC0, 0xE9, 0x11, 0xD7, 0x27,
+ 0xBF, 0x09, 0x49, 0x0F, 0x5E, 0x8D, 0x9F, 0xAC,
+ 0x57, 0x21, 0x3F, 0xD2, 0xA2, 0xD1, 0x2E, 0xD3,
+ 0xD7, 0x7A, 0x41, 0xF5, 0xE2, 0xFE, 0xCC, 0x40,
+ 0xE4, 0xEE, 0xCA, 0x16, 0x12, 0xF5, 0x1C, 0x45,
+ 0x23, 0x31, 0xAE, 0x93, 0x96, 0x62, 0x35, 0xBC
+ },
+ {
+ 0xDE, 0x73, 0x7D, 0xBC, 0x61, 0x2E, 0xBD, 0x31,
+ 0xBC, 0x49, 0xA2, 0xD7, 0xC6, 0x44, 0xD4, 0xB1,
+ 0x37, 0x81, 0x74, 0x19, 0x42, 0x1C, 0x32, 0xF4,
+ 0xE7, 0x51, 0x14, 0xD8, 0x99, 0xE3, 0x13, 0x1D,
+ 0x45, 0xCA, 0x54, 0x51, 0x24, 0x8F, 0x24, 0x16,
+ 0x9F, 0xBF, 0x17, 0xEE, 0x60, 0xA9, 0xB7, 0x07,
+ 0x98, 0xA4, 0xB9, 0x37, 0xCE, 0xA6, 0x27, 0x95,
+ 0x28, 0x96, 0x39, 0xD1, 0x8F, 0xCD, 0x89, 0xE4
+ },
+ {
+ 0xB4, 0xC1, 0xBB, 0xCB, 0xBC, 0xCD, 0xFC, 0xE4,
+ 0xD2, 0xBE, 0x9D, 0xCD, 0xB9, 0x83, 0xC1, 0xB0,
+ 0x20, 0xC5, 0xF7, 0x20, 0xDA, 0x5B, 0xEC, 0xF4,
+ 0xCB, 0x2A, 0x9A, 0x3D, 0x1B, 0x8D, 0x23, 0xCE,
+ 0xA7, 0xA9, 0xF5, 0xFD, 0x70, 0xD3, 0x74, 0x0E,
+ 0xCD, 0x67, 0xCE, 0x7D, 0x1E, 0x9C, 0x5E, 0x31,
+ 0xA3, 0x30, 0x2D, 0xF6, 0x6A, 0x9B, 0x5D, 0x54,
+ 0x30, 0x44, 0x90, 0xFB, 0xE1, 0xC4, 0xA8, 0xB9
+ },
+ {
+ 0xB1, 0xD6, 0x5E, 0x70, 0xC6, 0x9B, 0xA7, 0xE3,
+ 0xA7, 0x28, 0xE8, 0xB6, 0x44, 0x94, 0x93, 0xF2,
+ 0x37, 0x51, 0x0B, 0x23, 0xB6, 0xE7, 0x7D, 0x95,
+ 0x84, 0xD0, 0x5F, 0xF4, 0xD3, 0xF0, 0x87, 0x80,
+ 0x92, 0x9D, 0x74, 0xFA, 0x5B, 0xED, 0x9B, 0x75,
+ 0xD4, 0xD6, 0xD1, 0xCA, 0x91, 0xAB, 0x8D, 0x26,
+ 0x37, 0xDC, 0x2E, 0x79, 0xBA, 0x0F, 0xE0, 0x59,
+ 0x4A, 0xCD, 0x68, 0xFB, 0x3C, 0xC6, 0x60, 0xB9
+ },
+ {
+ 0xDA, 0x79, 0xF7, 0x29, 0xEA, 0xB9, 0x8C, 0x04,
+ 0xF3, 0x7F, 0xCC, 0x85, 0x4B, 0x69, 0xA8, 0x4E,
+ 0x46, 0x7D, 0xEA, 0x1E, 0x77, 0x82, 0xE7, 0xAF,
+ 0x02, 0xCB, 0x44, 0xA4, 0x9D, 0x21, 0x0D, 0x25,
+ 0x23, 0x68, 0x3D, 0x42, 0x0A, 0xC1, 0xDE, 0xC8,
+ 0xAD, 0x1F, 0xB4, 0x0E, 0x65, 0xAB, 0x3F, 0xE2,
+ 0x51, 0xA8, 0x51, 0xE2, 0x83, 0xD8, 0x58, 0x38,
+ 0x08, 0x42, 0x61, 0x30, 0x1E, 0xCD, 0x08, 0x9B
+ },
+ {
+ 0x71, 0x40, 0x40, 0x40, 0x39, 0x21, 0xAE, 0x55,
+ 0x48, 0xA2, 0x03, 0x39, 0xD6, 0x9E, 0x09, 0x3F,
+ 0x60, 0x9A, 0xA9, 0x9C, 0x22, 0xDB, 0x72, 0x59,
+ 0x1D, 0x1E, 0xF4, 0xFC, 0xB0, 0xAF, 0x01, 0x61,
+ 0x73, 0xE5, 0x77, 0xD8, 0xC1, 0xA3, 0x06, 0x3B,
+ 0x44, 0x3A, 0x0E, 0x48, 0xF3, 0x13, 0xCF, 0x2E,
+ 0x0F, 0x9B, 0x0C, 0x2E, 0xF9, 0x6A, 0x96, 0xC4,
+ 0x24, 0x32, 0x2C, 0xCC, 0x0C, 0xD5, 0x30, 0x4C
+ },
+ {
+ 0x8B, 0x2E, 0x8C, 0x3F, 0x0E, 0x3C, 0x31, 0x9B,
+ 0xA6, 0x7E, 0x86, 0x01, 0x4B, 0xDA, 0x68, 0x3E,
+ 0x53, 0x57, 0xA0, 0x40, 0x37, 0xB4, 0x56, 0x32,
+ 0x86, 0xAC, 0x89, 0xCD, 0xDB, 0x7E, 0xE0, 0x4C,
+ 0xF6, 0x67, 0x5F, 0x9A, 0xB6, 0x1F, 0xC8, 0x33,
+ 0x2D, 0x21, 0x8D, 0x2B, 0xCA, 0x97, 0x15, 0xE7,
+ 0xDB, 0xE5, 0x83, 0x72, 0xD1, 0xEE, 0xBF, 0x6B,
+ 0xC2, 0x94, 0x84, 0x71, 0xCF, 0xCE, 0xBB, 0x77
+ },
+ {
+ 0x32, 0xEE, 0x95, 0x49, 0xD4, 0xE3, 0x2F, 0x4B,
+ 0xE9, 0xC5, 0x00, 0xBD, 0x85, 0x43, 0xAF, 0xD0,
+ 0xB6, 0x97, 0x82, 0xD0, 0xB3, 0xFF, 0x7E, 0xD4,
+ 0x7A, 0x88, 0x1A, 0x0E, 0x49, 0x1F, 0x37, 0x65,
+ 0x0A, 0x21, 0xB2, 0x6C, 0x3F, 0x5D, 0x0A, 0x64,
+ 0xE0, 0x90, 0x58, 0xB3, 0x00, 0x4A, 0x23, 0x68,
+ 0xB9, 0x50, 0xE4, 0x72, 0x30, 0xC2, 0x29, 0x66,
+ 0xD3, 0xF7, 0x9D, 0xA7, 0xBA, 0xA0, 0xB8, 0x7F
+ },
+ {
+ 0xCA, 0xE7, 0xF2, 0x92, 0x71, 0x37, 0x82, 0xC4,
+ 0x71, 0xFE, 0x31, 0x78, 0xA9, 0x42, 0x0C, 0xD4,
+ 0xC1, 0x1F, 0xCD, 0x3F, 0x6D, 0xBE, 0x5D, 0x15,
+ 0xC8, 0x4A, 0xB7, 0x35, 0x3C, 0x73, 0x9E, 0xF0,
+ 0x64, 0x16, 0x39, 0xA2, 0xF9, 0x2A, 0xED, 0x31,
+ 0xC5, 0x6A, 0x20, 0x21, 0xCC, 0x5E, 0x58, 0xCB,
+ 0xEA, 0xD3, 0x74, 0xE2, 0xDC, 0x8A, 0x0D, 0xBC,
+ 0xE5, 0x45, 0x0F, 0xE7, 0xA0, 0x18, 0xCF, 0xA4
+ },
+ {
+ 0xF1, 0x7F, 0xEF, 0xAE, 0xAE, 0x7D, 0x40, 0xCD,
+ 0x88, 0x5D, 0xAC, 0x0B, 0xC3, 0x50, 0xC0, 0x27,
+ 0x36, 0x68, 0xEA, 0x02, 0x22, 0xDF, 0x5C, 0x75,
+ 0x69, 0x4F, 0x5C, 0xB3, 0xA3, 0x21, 0x51, 0x9F,
+ 0x6E, 0x0E, 0xC4, 0x3B, 0xA0, 0xC8, 0x59, 0x3D,
+ 0xC7, 0x34, 0x13, 0x41, 0xE5, 0x19, 0x48, 0x8F,
+ 0x20, 0xAB, 0xD5, 0xB8, 0x12, 0x4D, 0xFA, 0xCE,
+ 0xA5, 0xCD, 0xE0, 0x96, 0x5B, 0x69, 0x70, 0xF9
+ },
+ {
+ 0xE2, 0xCF, 0x86, 0xDD, 0xC8, 0x42, 0x4E, 0xE5,
+ 0x47, 0xEB, 0x72, 0x45, 0xB7, 0x32, 0x5E, 0x02,
+ 0xF2, 0xE3, 0xAC, 0x01, 0x3C, 0x8D, 0x38, 0x6B,
+ 0x3D, 0x2E, 0x09, 0x20, 0x8A, 0x9B, 0xCC, 0x0B,
+ 0x44, 0xC4, 0xC4, 0x38, 0xEA, 0xAF, 0x52, 0xD2,
+ 0x07, 0x7E, 0x91, 0x77, 0xEB, 0x8E, 0xE1, 0xD5,
+ 0x90, 0x75, 0xB5, 0x25, 0x92, 0x20, 0x20, 0x62,
+ 0x22, 0x93, 0x54, 0xBF, 0x23, 0xC9, 0x62, 0x39
+ },
+ {
+ 0x38, 0xF2, 0x6A, 0x11, 0x02, 0xCB, 0x16, 0x2D,
+ 0x35, 0x1F, 0x84, 0x3B, 0x3C, 0x49, 0xF6, 0xFF,
+ 0x85, 0x44, 0x16, 0x33, 0xB6, 0x70, 0x4A, 0x28,
+ 0x6A, 0xF8, 0x1C, 0xCB, 0xAE, 0x5A, 0x67, 0xD3,
+ 0x01, 0x5C, 0xC0, 0xEF, 0xAF, 0xB7, 0x05, 0x7D,
+ 0xC2, 0xB2, 0x8D, 0x67, 0x66, 0xE8, 0x2A, 0x06,
+ 0x8A, 0x4C, 0x0B, 0x52, 0x4B, 0x66, 0xD0, 0xA6,
+ 0x32, 0x77, 0x5D, 0x93, 0x06, 0x15, 0x75, 0xF9
+ },
+ {
+ 0xA2, 0xC4, 0x30, 0x2D, 0xAC, 0xA7, 0xA7, 0xC6,
+ 0x32, 0xF6, 0x76, 0x30, 0x4E, 0x62, 0x75, 0xC1,
+ 0xC1, 0xF0, 0xDB, 0xFE, 0x38, 0xDC, 0x57, 0x1C,
+ 0xB2, 0x3E, 0x1F, 0x7B, 0xA5, 0xDC, 0x18, 0x18,
+ 0x0F, 0xC4, 0x8A, 0x01, 0x5F, 0x92, 0x7C, 0x89,
+ 0x96, 0x7C, 0x1E, 0x10, 0x4E, 0x66, 0xF5, 0xEA,
+ 0x5B, 0x2D, 0xD3, 0x1D, 0x78, 0x1C, 0x38, 0x49,
+ 0xBF, 0xC6, 0x49, 0x22, 0x0C, 0x38, 0x5C, 0x82
+ },
+ {
+ 0xC1, 0x9C, 0x6B, 0x3F, 0xB5, 0x35, 0x2B, 0xB3,
+ 0x94, 0xC2, 0x68, 0x46, 0x52, 0x3C, 0x25, 0xE8,
+ 0x26, 0x5D, 0x50, 0x5F, 0x50, 0x1F, 0x96, 0x03,
+ 0xA4, 0xF8, 0xBD, 0x55, 0x38, 0x6C, 0xF4, 0xCC,
+ 0x9F, 0x4D, 0x71, 0xF3, 0x8F, 0xF4, 0x45, 0xF4,
+ 0xEF, 0xC8, 0x30, 0x98, 0xD4, 0x79, 0x69, 0x33,
+ 0x4E, 0x79, 0xA2, 0xBC, 0xB4, 0x02, 0x6B, 0xC6,
+ 0x3B, 0x79, 0x59, 0xDE, 0xDB, 0x62, 0xB7, 0xBD
+ },
+ {
+ 0x1F, 0x4A, 0xB9, 0x84, 0x0A, 0x1C, 0xFA, 0x8F,
+ 0xE6, 0xC5, 0x62, 0x2D, 0x9B, 0x53, 0x8B, 0xEC,
+ 0xB8, 0x80, 0x7A, 0x87, 0x78, 0xB6, 0x9D, 0x93,
+ 0x05, 0xF9, 0x08, 0x57, 0x65, 0x73, 0xB2, 0x0C,
+ 0xA3, 0x70, 0x4E, 0x89, 0x12, 0x97, 0x26, 0xD5,
+ 0x02, 0xE1, 0x98, 0x58, 0x8D, 0x07, 0x26, 0x68,
+ 0xBF, 0x03, 0x63, 0x0B, 0x5B, 0x5A, 0x92, 0x32,
+ 0xFF, 0x39, 0x25, 0x27, 0x24, 0x9D, 0xF9, 0x9B
+ },
+ {
+ 0xFE, 0x03, 0x17, 0x7B, 0x58, 0xB4, 0x88, 0x83,
+ 0xA8, 0x6D, 0x42, 0x68, 0x33, 0x4B, 0x95, 0x91,
+ 0xD9, 0xFB, 0xD8, 0xBF, 0x7C, 0xC2, 0xAA, 0xCC,
+ 0x50, 0x25, 0xEF, 0x47, 0x6B, 0x45, 0x33, 0xBA,
+ 0x7B, 0xD7, 0x81, 0xDF, 0x01, 0x11, 0x47, 0xB3,
+ 0xCF, 0x51, 0x1D, 0x8B, 0x3D, 0xCD, 0x8C, 0x78,
+ 0x0D, 0x30, 0xD7, 0xDA, 0x71, 0x8C, 0x22, 0x44,
+ 0x23, 0x19, 0x81, 0x7B, 0xE3, 0x18, 0x6B, 0xC5
+ },
+ {
+ 0xF4, 0xC3, 0xB0, 0x59, 0x10, 0x5B, 0x6A, 0xA5,
+ 0xFE, 0x78, 0x84, 0x3A, 0x07, 0xD9, 0x4F, 0x71,
+ 0x20, 0x62, 0xCB, 0x5A, 0x4D, 0xD6, 0x05, 0x9F,
+ 0x97, 0x90, 0x4D, 0x0C, 0x57, 0x97, 0x3B, 0xA8,
+ 0xDF, 0x71, 0xD1, 0x5A, 0x51, 0x1A, 0x06, 0x68,
+ 0x64, 0xFE, 0x45, 0x5E, 0xDC, 0x9E, 0x5F, 0x16,
+ 0x52, 0x4C, 0xEC, 0x7E, 0xE2, 0x48, 0xEE, 0x3E,
+ 0xC9, 0x29, 0x06, 0x3B, 0xD1, 0x07, 0x98, 0xDA
+ },
+ {
+ 0x57, 0xA1, 0x6F, 0x96, 0x4B, 0x18, 0x1B, 0x12,
+ 0x03, 0xA5, 0x80, 0x3B, 0x73, 0x81, 0x7D, 0x77,
+ 0x44, 0x83, 0x82, 0x6C, 0xEA, 0x11, 0x3B, 0x9C,
+ 0xCF, 0xCF, 0x0E, 0xB8, 0x7C, 0xB2, 0x30, 0x64,
+ 0x28, 0x49, 0x62, 0xD8, 0x47, 0xBB, 0x1F, 0xAE,
+ 0x8C, 0xBF, 0x5C, 0xC6, 0x3B, 0x3C, 0xEA, 0xA1,
+ 0x24, 0x1E, 0xA4, 0x2C, 0x63, 0xF8, 0x98, 0x01,
+ 0x1F, 0xC4, 0xDB, 0xCA, 0xE6, 0xF5, 0xE8, 0xC5
+ },
+ {
+ 0x79, 0x52, 0xFC, 0x83, 0xAC, 0xF1, 0x3A, 0x95,
+ 0xCA, 0x9C, 0x27, 0xA2, 0x15, 0x6D, 0x9C, 0x1B,
+ 0x63, 0x00, 0xB0, 0xEF, 0x79, 0x0F, 0x57, 0x2B,
+ 0xC3, 0x94, 0xC6, 0x77, 0xF7, 0xC1, 0x46, 0x29,
+ 0xEB, 0xD8, 0xE7, 0xD5, 0xD7, 0xC7, 0xF1, 0xA5,
+ 0xEB, 0xBD, 0xC3, 0x90, 0xCC, 0x08, 0xCD, 0x58,
+ 0xC2, 0x00, 0x89, 0x00, 0xCB, 0x55, 0xEB, 0x05,
+ 0xE4, 0x44, 0xA6, 0x8C, 0x3B, 0x39, 0x3E, 0x60
+ },
+ {
+ 0x2C, 0x22, 0x40, 0xD6, 0xB5, 0x41, 0xF4, 0x29,
+ 0x4F, 0xF9, 0x76, 0x79, 0x1D, 0x35, 0xE6, 0xA2,
+ 0xD4, 0x92, 0xF5, 0x7A, 0x91, 0x5F, 0xBA, 0xC5,
+ 0x83, 0x26, 0x60, 0xC1, 0x0E, 0x9C, 0x96, 0x46,
+ 0x5C, 0x7B, 0xD5, 0xFC, 0xA7, 0x51, 0xBF, 0x68,
+ 0xE2, 0x67, 0x3A, 0x63, 0x8E, 0x3A, 0xF7, 0x35,
+ 0xB0, 0x20, 0x91, 0xD7, 0x5D, 0x1A, 0x7F, 0x89,
+ 0xE3, 0xF7, 0x61, 0xC5, 0xDF, 0x82, 0x1A, 0x6B
+ },
+ {
+ 0x59, 0xDC, 0x84, 0x6D, 0x34, 0x05, 0xCC, 0xD8,
+ 0x06, 0xF8, 0xFA, 0x20, 0xC8, 0x96, 0x9E, 0xF6,
+ 0x8A, 0x43, 0x85, 0xEF, 0x6C, 0x27, 0x4E, 0xEE,
+ 0x6D, 0xC0, 0x69, 0x2C, 0x3E, 0xCF, 0xB1, 0xA8,
+ 0x34, 0xCE, 0x64, 0x43, 0x76, 0xC5, 0x2B, 0x80,
+ 0x42, 0x1B, 0xAE, 0x94, 0xD6, 0xC7, 0xFD, 0xCC,
+ 0xA5, 0xA8, 0xF1, 0x85, 0x9C, 0x45, 0xA1, 0x0C,
+ 0x4E, 0xB2, 0x74, 0x82, 0x6F, 0x1F, 0x08, 0x9F
+ },
+ {
+ 0xB7, 0x52, 0x96, 0x27, 0x07, 0xA1, 0x7B, 0x66,
+ 0x4F, 0xAE, 0xB3, 0x13, 0xE2, 0xB9, 0x52, 0xDC,
+ 0x03, 0xE7, 0x4A, 0x7E, 0x94, 0x47, 0x09, 0x8A,
+ 0xA6, 0xD4, 0xEA, 0x5B, 0xD2, 0x87, 0xD0, 0x7A,
+ 0x12, 0x25, 0xEC, 0xED, 0xA9, 0x81, 0x15, 0x70,
+ 0x58, 0x0A, 0x51, 0x2B, 0x2B, 0x20, 0xB3, 0xFC,
+ 0xFC, 0xA7, 0x0B, 0x44, 0xF6, 0x45, 0x4E, 0xF3,
+ 0xC3, 0x52, 0x4C, 0xCA, 0x6B, 0x69, 0x47, 0x5B
+ },
+ {
+ 0xDA, 0x0D, 0x8E, 0x54, 0x61, 0xF8, 0x10, 0x24,
+ 0xEF, 0xFE, 0xED, 0x5D, 0x70, 0x76, 0xA0, 0x4F,
+ 0xED, 0xED, 0xAC, 0x57, 0xE7, 0xC9, 0x8A, 0x59,
+ 0x45, 0xBF, 0xDE, 0x66, 0x75, 0x58, 0x18, 0x85,
+ 0x1B, 0xE1, 0x13, 0x6B, 0x71, 0xF4, 0x33, 0xA5,
+ 0x6B, 0xDA, 0x18, 0x41, 0xAE, 0x71, 0x39, 0x2C,
+ 0x4B, 0x82, 0x90, 0x82, 0x63, 0x59, 0xF5, 0x87,
+ 0x22, 0x3C, 0x3E, 0xF7, 0x37, 0xFF, 0x73, 0x2A
+ },
+ {
+ 0xED, 0xB8, 0x6A, 0x23, 0x7C, 0x6F, 0x13, 0x7D,
+ 0xFB, 0xB3, 0x47, 0x01, 0x1E, 0xDB, 0x4C, 0x6E,
+ 0x86, 0x1F, 0x4D, 0x58, 0x14, 0x60, 0x85, 0x46,
+ 0x34, 0x41, 0x04, 0x2F, 0xA3, 0x63, 0x16, 0xF1,
+ 0xFA, 0xF8, 0x87, 0x11, 0xBB, 0x0F, 0x18, 0x11,
+ 0xDF, 0xBB, 0xBF, 0xA7, 0xB5, 0x1F, 0x9C, 0xE2,
+ 0xD4, 0x96, 0x05, 0x24, 0x3E, 0xD0, 0x16, 0xCB,
+ 0xAD, 0x68, 0x85, 0xEA, 0xE2, 0x03, 0x67, 0x4F
+ },
+ {
+ 0xE6, 0xD8, 0xE0, 0xFB, 0xAA, 0x29, 0xDB, 0xEB,
+ 0x60, 0xF3, 0xC7, 0xF9, 0x85, 0xBA, 0xD7, 0x54,
+ 0xD7, 0x21, 0xAA, 0xC6, 0x3D, 0xA6, 0xF4, 0x49,
+ 0x0C, 0x9D, 0x7E, 0xA2, 0x31, 0xD2, 0x62, 0x2F,
+ 0xDF, 0xDE, 0xF1, 0x48, 0xD0, 0xCA, 0x44, 0x2B,
+ 0x8D, 0x59, 0xCF, 0x3E, 0x4F, 0x98, 0x35, 0xCB,
+ 0xC2, 0x40, 0xAF, 0x40, 0xFB, 0xA6, 0x3A, 0x2E,
+ 0xA5, 0xA2, 0x35, 0xD4, 0x6E, 0xEA, 0x6E, 0xAC
+ },
+ {
+ 0xD4, 0xE4, 0x63, 0xC4, 0x88, 0x29, 0x87, 0xEB,
+ 0x44, 0xA5, 0xED, 0x0C, 0x82, 0x1D, 0x68, 0xB0,
+ 0xFE, 0xF9, 0x9D, 0x6F, 0x53, 0xA5, 0x7B, 0xF3,
+ 0x19, 0xBD, 0xAC, 0x25, 0xAC, 0x38, 0xEB, 0x0B,
+ 0x23, 0xE1, 0x13, 0x8C, 0x00, 0x12, 0xF5, 0xF3,
+ 0x83, 0x46, 0xA1, 0xDE, 0x9D, 0x4A, 0x99, 0x2A,
+ 0x64, 0xB9, 0x42, 0x83, 0x4A, 0x85, 0x6E, 0xFB,
+ 0xAA, 0x06, 0x20, 0xBD, 0xA2, 0x9F, 0x6A, 0x86
+ },
+ {
+ 0x42, 0xD8, 0x10, 0xD0, 0x1C, 0x2D, 0xA2, 0x47,
+ 0x35, 0xF0, 0x4A, 0x5E, 0x90, 0x13, 0x38, 0xFD,
+ 0xFC, 0x2D, 0xE1, 0x71, 0x5F, 0xF6, 0x64, 0x3A,
+ 0x37, 0x2F, 0x88, 0x0E, 0x6C, 0x5C, 0x6C, 0x13,
+ 0xD2, 0xB3, 0xAD, 0x70, 0x77, 0x46, 0x9D, 0x64,
+ 0x33, 0x54, 0x05, 0x4D, 0x32, 0xDD, 0x80, 0x49,
+ 0xEA, 0x63, 0x73, 0x2B, 0x57, 0x45, 0xBD, 0xB2,
+ 0x3B, 0xE2, 0xB5, 0x8E, 0x48, 0xC1, 0x01, 0x3A
+ },
+ {
+ 0xCF, 0xBF, 0x54, 0x30, 0x07, 0x6F, 0x82, 0x5A,
+ 0x3B, 0xBB, 0x88, 0xC1, 0xBC, 0x0A, 0xEF, 0x61,
+ 0x25, 0x9E, 0x8F, 0x4D, 0x5F, 0xA3, 0x3C, 0x39,
+ 0x82, 0x50, 0x62, 0xF1, 0x5D, 0x19, 0xFD, 0x4A,
+ 0x01, 0x82, 0xCD, 0x97, 0x36, 0xD2, 0xAE, 0xC9,
+ 0x74, 0x9C, 0xCF, 0x83, 0x18, 0x6C, 0x35, 0x74,
+ 0xAB, 0x94, 0x42, 0x65, 0x40, 0x66, 0x0A, 0x9D,
+ 0xB8, 0xC3, 0xAA, 0xBB, 0xCB, 0xDD, 0x9D, 0x0F
+ },
+ {
+ 0x6C, 0x24, 0x34, 0xA1, 0xAF, 0xA1, 0x57, 0xAC,
+ 0xCC, 0x34, 0xA5, 0xC4, 0x87, 0x2D, 0xFF, 0x69,
+ 0xFE, 0x7F, 0x31, 0x96, 0xCB, 0x1A, 0x75, 0x0C,
+ 0x54, 0x1D, 0x8B, 0x73, 0x92, 0x28, 0x88, 0xBA,
+ 0xBE, 0x89, 0xB1, 0xC3, 0x82, 0x02, 0x21, 0x86,
+ 0x20, 0xD8, 0x8D, 0x77, 0xDA, 0xD9, 0xDF, 0xBA,
+ 0xB3, 0xFB, 0xF7, 0x40, 0xB2, 0xD1, 0xD8, 0xF3,
+ 0x7E, 0xAD, 0x25, 0x8E, 0x2E, 0xF1, 0x06, 0x52
+ },
+ {
+ 0x48, 0xB7, 0x26, 0x8A, 0xA4, 0x34, 0x2F, 0xAB,
+ 0x02, 0x1D, 0x14, 0x72, 0xE9, 0x25, 0x7F, 0x76,
+ 0x58, 0x5C, 0xC5, 0x68, 0x10, 0xC8, 0xF2, 0xA6,
+ 0xE1, 0xD4, 0xA8, 0x94, 0x6B, 0x77, 0x71, 0x42,
+ 0xD4, 0x4A, 0xE5, 0x13, 0xA8, 0x80, 0x9F, 0x2D,
+ 0x6D, 0xC7, 0x26, 0x30, 0x5F, 0x79, 0x44, 0x60,
+ 0x4D, 0x95, 0x2D, 0x4A, 0x9F, 0x08, 0x5C, 0x5C,
+ 0x10, 0x50, 0xBA, 0xFD, 0xD2, 0x1D, 0x1E, 0x60
+ },
+ {
+ 0xCE, 0xCF, 0xCE, 0x4B, 0x12, 0xC6, 0xCF, 0x53,
+ 0xD1, 0xB1, 0xB2, 0xD4, 0x18, 0xA4, 0x93, 0xE3,
+ 0xF4, 0x29, 0x17, 0x03, 0x21, 0xE8, 0x1A, 0xA2,
+ 0x52, 0x63, 0xAA, 0xA7, 0x15, 0xD5, 0xCA, 0x38,
+ 0x9F, 0x65, 0xC3, 0xAC, 0xF9, 0x9B, 0x18, 0x0E,
+ 0x44, 0x6B, 0x50, 0xE6, 0x01, 0xFC, 0xBF, 0x44,
+ 0x61, 0xD0, 0x42, 0x6A, 0x85, 0x92, 0xA0, 0x77,
+ 0x42, 0x20, 0x18, 0x57, 0x12, 0x5F, 0x71, 0xEE
+ },
+ {
+ 0x38, 0x5A, 0x75, 0x22, 0x42, 0xEB, 0x9E, 0xD5,
+ 0x6B, 0x07, 0x4B, 0x70, 0x2C, 0x91, 0xE7, 0x5A,
+ 0xEC, 0x0B, 0xE9, 0x06, 0x4B, 0xD9, 0xCF, 0x88,
+ 0x03, 0x04, 0xC2, 0x13, 0x27, 0x0C, 0xB2, 0xEA,
+ 0xE8, 0xE2, 0x1D, 0x9A, 0xE8, 0xC6, 0x08, 0x15,
+ 0x19, 0xF7, 0x5D, 0xFA, 0xBB, 0x00, 0x3B, 0x24,
+ 0x32, 0xB0, 0x47, 0x55, 0xB8, 0xC3, 0x2C, 0x97,
+ 0xAC, 0x29, 0x14, 0xE8, 0xBF, 0x45, 0xB2, 0x34
+ },
+ {
+ 0xD8, 0x9A, 0x12, 0x4A, 0x9B, 0x95, 0x8B, 0xA2,
+ 0x3D, 0x09, 0x20, 0x7A, 0xCF, 0xA6, 0x2A, 0x33,
+ 0xB8, 0x70, 0x89, 0xB2, 0x86, 0xE8, 0x43, 0x8B,
+ 0xDC, 0x01, 0xE2, 0x33, 0xAB, 0x2A, 0x86, 0x30,
+ 0xA1, 0xEE, 0xB6, 0xB2, 0xB9, 0xBA, 0x6B, 0x7D,
+ 0x21, 0x00, 0x10, 0x77, 0x33, 0xDE, 0xAF, 0x4C,
+ 0x20, 0x47, 0x8C, 0x26, 0xF2, 0x49, 0xC6, 0x89,
+ 0xC5, 0x26, 0x84, 0x73, 0xE2, 0xE9, 0xFA, 0x60
+ },
+ {
+ 0x43, 0xDE, 0x10, 0x92, 0xFF, 0x9F, 0xF5, 0x28,
+ 0x20, 0x6C, 0x6F, 0xCF, 0x81, 0x32, 0x2E, 0xAD,
+ 0x3D, 0x22, 0xEA, 0xA4, 0xC8, 0x54, 0x52, 0x15,
+ 0x77, 0xDF, 0x33, 0x62, 0x47, 0x49, 0x5C, 0xE1,
+ 0x72, 0xFC, 0x87, 0x39, 0x95, 0x30, 0x0B, 0x21,
+ 0xB9, 0x46, 0x10, 0xC9, 0xD2, 0xF6, 0x33, 0xB5,
+ 0x33, 0xBD, 0xE4, 0x56, 0x8C, 0xA0, 0x9C, 0x38,
+ 0x0E, 0x84, 0x68, 0xFE, 0x6A, 0xD8, 0xD8, 0x1D
+ },
+ {
+ 0x86, 0x8B, 0x60, 0x11, 0x99, 0xEF, 0x00, 0x0B,
+ 0x70, 0x5C, 0xD6, 0x4D, 0x39, 0x30, 0x26, 0x2A,
+ 0x5A, 0xB9, 0x10, 0xE3, 0x4E, 0x2D, 0x78, 0xE8,
+ 0x58, 0x7B, 0x4E, 0x01, 0x0D, 0x37, 0x6D, 0xD4,
+ 0xA0, 0x0D, 0xE4, 0x48, 0x67, 0xD0, 0xE9, 0x33,
+ 0xEE, 0x39, 0xA1, 0xFA, 0x91, 0x47, 0xD4, 0x99,
+ 0xD1, 0x84, 0xF3, 0xA9, 0xCF, 0x35, 0x4F, 0x2D,
+ 0x3C, 0x51, 0x14, 0x6F, 0xF7, 0x15, 0x2D, 0x68
+ },
+ {
+ 0x15, 0x17, 0xF8, 0xF0, 0x44, 0x2F, 0x0D, 0x50,
+ 0xBB, 0xC0, 0xAA, 0xB6, 0x84, 0x6F, 0xDC, 0xE3,
+ 0xB7, 0x0F, 0xAE, 0xA4, 0xBB, 0x51, 0x13, 0xAC,
+ 0xB2, 0x3A, 0xBE, 0x10, 0x1D, 0x99, 0xA4, 0x0A,
+ 0x1B, 0x76, 0xC1, 0xE8, 0xDC, 0x2E, 0xA1, 0x93,
+ 0x62, 0x94, 0x82, 0x3A, 0xD8, 0x35, 0x4C, 0x11,
+ 0xE2, 0xE9, 0x6C, 0x67, 0x12, 0xBE, 0x4C, 0xF7,
+ 0x7C, 0x58, 0x3F, 0xD0, 0x6B, 0x5E, 0x5C, 0x55
+ },
+ {
+ 0xAF, 0x4C, 0x6C, 0x67, 0xC5, 0xCA, 0x38, 0x38,
+ 0x73, 0x48, 0xCA, 0x3E, 0xC2, 0xBE, 0xD7, 0xFB,
+ 0xA8, 0xC2, 0xB3, 0xD2, 0x2D, 0xE1, 0x48, 0xD0,
+ 0x8A, 0x61, 0x8C, 0x29, 0x70, 0x23, 0xFB, 0x7B,
+ 0x6D, 0x2C, 0x15, 0x3D, 0x5E, 0xFC, 0xD1, 0x68,
+ 0x89, 0x99, 0x91, 0x0B, 0x20, 0xE1, 0xEA, 0xC7,
+ 0xC1, 0x00, 0xA2, 0xC5, 0xA6, 0xC1, 0xAC, 0xF5,
+ 0xE9, 0x8F, 0x14, 0x3B, 0x41, 0xDC, 0x8A, 0x12
+ },
+ {
+ 0xA2, 0xAD, 0x94, 0x24, 0x3B, 0x8E, 0xEA, 0x68,
+ 0xF5, 0xFA, 0xDD, 0x69, 0x08, 0xAD, 0xB0, 0xDA,
+ 0xCD, 0xAA, 0x6A, 0x6D, 0x24, 0xC2, 0x50, 0xD3,
+ 0x39, 0x40, 0x3D, 0xBA, 0x82, 0x31, 0xBD, 0x51,
+ 0xE8, 0x87, 0xCB, 0x5B, 0x1B, 0x7B, 0xDE, 0x27,
+ 0x74, 0xC6, 0xB0, 0x8A, 0xCC, 0xE0, 0xF7, 0x49,
+ 0x56, 0x48, 0xDA, 0x3B, 0xEB, 0xC7, 0xB1, 0xC2,
+ 0x82, 0x15, 0x08, 0xC4, 0xD3, 0x82, 0xF7, 0x30
+ },
+ {
+ 0x28, 0xF8, 0x8C, 0xDB, 0xE9, 0x03, 0xAD, 0x63,
+ 0xA0, 0x23, 0x31, 0xDE, 0x1A, 0x32, 0xAF, 0x6D,
+ 0xBB, 0xA8, 0x2D, 0x7F, 0xC0, 0x79, 0x87, 0x02,
+ 0x72, 0x49, 0x33, 0xDA, 0x77, 0x38, 0x07, 0xBC,
+ 0x80, 0x42, 0x78, 0x13, 0x47, 0x81, 0xF1, 0x26,
+ 0x23, 0x32, 0x20, 0xE3, 0x07, 0x92, 0x81, 0x31,
+ 0xB2, 0x47, 0x10, 0xB4, 0x67, 0x4E, 0xD7, 0x05,
+ 0x11, 0x2F, 0x95, 0xD1, 0xAA, 0x37, 0xA2, 0xDC
+ },
+ {
+ 0x5B, 0xB2, 0x92, 0x65, 0xE2, 0x46, 0xB8, 0x84,
+ 0xFF, 0x40, 0x91, 0x4F, 0xFA, 0x93, 0xD9, 0xA1,
+ 0x2E, 0xDC, 0x19, 0xEE, 0xE9, 0xCC, 0x8A, 0x83,
+ 0x63, 0x1D, 0x68, 0xBD, 0x46, 0xAA, 0xD3, 0x35,
+ 0x4B, 0xA6, 0x67, 0x4B, 0x91, 0x3F, 0x4F, 0x82,
+ 0x3E, 0x79, 0x1F, 0x0C, 0xB1, 0x9E, 0xA6, 0xA6,
+ 0x7C, 0x6E, 0x32, 0xE9, 0xBE, 0x0D, 0x0F, 0xF5,
+ 0x76, 0x0F, 0x16, 0xDD, 0x75, 0xA8, 0x7B, 0x5D
+ },
+ {
+ 0xBF, 0x3C, 0x06, 0xDC, 0x6D, 0x94, 0xE3, 0x85,
+ 0x9A, 0x4D, 0xAA, 0x50, 0xEC, 0xA1, 0xAF, 0x53,
+ 0x57, 0xE3, 0x45, 0x79, 0xE5, 0x99, 0xF8, 0x20,
+ 0x49, 0xE1, 0xCC, 0xA7, 0xA7, 0xD4, 0xF3, 0x3F,
+ 0xEA, 0x44, 0x3B, 0x44, 0x69, 0x1B, 0xD4, 0x36,
+ 0x88, 0xF5, 0x55, 0x05, 0x31, 0xCF, 0x22, 0xB7,
+ 0x12, 0x77, 0x89, 0x0B, 0xFF, 0xAE, 0x1E, 0xCE,
+ 0x78, 0x3F, 0x56, 0x63, 0xA1, 0xC4, 0xD7, 0x1A
+ },
+ {
+ 0xC9, 0x0D, 0xF5, 0x32, 0xF2, 0xF1, 0x49, 0x3A,
+ 0x11, 0x55, 0xBE, 0x8C, 0x2A, 0x44, 0x00, 0x92,
+ 0x20, 0x49, 0x97, 0x4E, 0x7D, 0x4F, 0x4B, 0x54,
+ 0xF8, 0x20, 0xC2, 0x26, 0x9D, 0x3B, 0x16, 0x1B,
+ 0x6E, 0x88, 0xEB, 0x77, 0x6B, 0x85, 0x9B, 0x89,
+ 0xB8, 0x56, 0x7F, 0xBC, 0x55, 0x0C, 0x4F, 0x54,
+ 0xAA, 0xD2, 0x7A, 0x16, 0x10, 0x65, 0x6D, 0x62,
+ 0x5C, 0x32, 0x7F, 0x66, 0x5D, 0xCA, 0x70, 0x7C
+ },
+ {
+ 0x3D, 0x39, 0xEE, 0xCC, 0x9E, 0x90, 0x42, 0x36,
+ 0xDC, 0x85, 0x7B, 0xA4, 0x9D, 0x55, 0xD3, 0xBA,
+ 0xD7, 0x65, 0x72, 0xA9, 0x1A, 0x75, 0x95, 0x03,
+ 0x37, 0x6B, 0x77, 0x08, 0xD6, 0x2D, 0x5A, 0x78,
+ 0x5C, 0x23, 0x06, 0x80, 0x59, 0xCF, 0x68, 0x89,
+ 0x7F, 0x23, 0xEE, 0xC5, 0x07, 0x21, 0x9B, 0x0A,
+ 0x02, 0xED, 0xA2, 0xD8, 0xBC, 0x94, 0xFA, 0x69,
+ 0x89, 0xA5, 0x14, 0x82, 0x22, 0x03, 0xC8, 0xD1
+ },
+ {
+ 0xE0, 0x8C, 0x54, 0xD9, 0x98, 0xF9, 0x2B, 0x7A,
+ 0x54, 0xA2, 0x4C, 0xA6, 0xAE, 0xB1, 0x53, 0xA6,
+ 0x4F, 0x9C, 0x9F, 0x1F, 0xC3, 0x36, 0x58, 0xB3,
+ 0xED, 0xAC, 0x2C, 0x4B, 0xB5, 0x26, 0x31, 0x58,
+ 0xDA, 0xDF, 0x00, 0xD3, 0x51, 0x9A, 0x11, 0x9A,
+ 0x56, 0x14, 0xC7, 0xF3, 0x79, 0x40, 0xE5, 0x5D,
+ 0x13, 0xCC, 0xE4, 0x66, 0xCB, 0x71, 0xA4, 0x07,
+ 0xC3, 0x9F, 0xC5, 0x1E, 0x1E, 0xFE, 0x18, 0xDA
+ },
+ {
+ 0x74, 0x76, 0x76, 0x07, 0x04, 0x1D, 0xD4, 0xB7,
+ 0xC5, 0x6B, 0x18, 0x9E, 0xE8, 0xF2, 0x77, 0x31,
+ 0xA5, 0x16, 0x72, 0x23, 0xEB, 0x7A, 0xF9, 0xB9,
+ 0x39, 0xE1, 0x18, 0xF8, 0x7D, 0x80, 0xB4, 0x9E,
+ 0xA8, 0xD0, 0xD0, 0x1F, 0x74, 0xF3, 0x98, 0xB1,
+ 0x72, 0xA8, 0xAD, 0x0D, 0xBF, 0x99, 0x41, 0x4F,
+ 0x08, 0xD2, 0xB7, 0xD8, 0xD7, 0x52, 0x16, 0xA1,
+ 0x82, 0x25, 0x27, 0x3D, 0x8D, 0x7F, 0xD0, 0x5D
+ },
+ {
+ 0xFE, 0xE8, 0x9A, 0x92, 0xCC, 0xF9, 0xF1, 0xEB,
+ 0x08, 0x4A, 0xAB, 0xA9, 0x54, 0x97, 0xEF, 0x0F,
+ 0x30, 0x13, 0x4C, 0x19, 0x1C, 0xF9, 0x0A, 0x49,
+ 0xD2, 0x2C, 0x7D, 0x2F, 0x66, 0x14, 0x99, 0x3C,
+ 0xBE, 0x1A, 0x4B, 0x65, 0x13, 0xED, 0xC1, 0x53,
+ 0x86, 0x8A, 0x3D, 0x56, 0x2B, 0x5B, 0x02, 0x26,
+ 0xBA, 0x8E, 0x1B, 0x0D, 0xCB, 0x69, 0xED, 0x45,
+ 0xAF, 0x47, 0xCE, 0x4F, 0x86, 0xBA, 0x47, 0x4A
+ },
+ {
+ 0xCD, 0xAE, 0x94, 0xB6, 0xD1, 0xD8, 0x35, 0xF6,
+ 0xC7, 0x4C, 0x76, 0xEC, 0x3A, 0x2D, 0xB6, 0x5B,
+ 0xBD, 0xFA, 0xE1, 0x9D, 0x7B, 0x05, 0x0D, 0xC9,
+ 0x5D, 0x65, 0x87, 0x33, 0xB8, 0xB2, 0x2C, 0x6F,
+ 0x9E, 0x0B, 0x63, 0xCC, 0x90, 0x5A, 0x29, 0xEA,
+ 0x88, 0x78, 0xCA, 0x39, 0x45, 0x56, 0xB3, 0x67,
+ 0x3C, 0x62, 0x79, 0x15, 0x46, 0xA9, 0xA1, 0xF0,
+ 0xD1, 0x56, 0x5F, 0xAD, 0xC5, 0x35, 0x36, 0xC1
+ },
+ {
+ 0xC7, 0x22, 0x8B, 0x6F, 0x00, 0x00, 0x17, 0xD2,
+ 0xBE, 0x4B, 0xF2, 0xAE, 0x48, 0xAD, 0xDB, 0x78,
+ 0x5E, 0x27, 0x35, 0xBF, 0x3C, 0x61, 0x4D, 0x3C,
+ 0x34, 0x23, 0x1F, 0x1D, 0x0C, 0x88, 0x7D, 0x3A,
+ 0x8E, 0x88, 0x88, 0x0B, 0x67, 0xAD, 0x3B, 0x2F,
+ 0x65, 0x23, 0xDD, 0x67, 0x19, 0x34, 0x2C, 0xD4,
+ 0xF0, 0x59, 0x35, 0xD2, 0xE5, 0x26, 0x7F, 0x36,
+ 0x80, 0xE7, 0x73, 0xBD, 0x5E, 0xAD, 0xFE, 0x1D
+ },
+ {
+ 0x12, 0x27, 0x44, 0xFE, 0x3F, 0xFF, 0x9A, 0x05,
+ 0x5F, 0x0F, 0x3B, 0xDE, 0x01, 0xEB, 0x2F, 0x44,
+ 0x6B, 0x0C, 0xDA, 0xF3, 0xAE, 0xD7, 0x2C, 0xAA,
+ 0x29, 0x40, 0x74, 0x19, 0x20, 0x12, 0x0A, 0x96,
+ 0x4F, 0xCF, 0xF8, 0x70, 0x99, 0xB0, 0x8E, 0xF3,
+ 0x34, 0x96, 0xE3, 0x99, 0x03, 0x2A, 0x82, 0xDA,
+ 0xAD, 0x4F, 0xED, 0x30, 0x31, 0x17, 0x2F, 0x77,
+ 0x47, 0x92, 0x58, 0xFA, 0x39, 0xDB, 0x92, 0xFD
+ },
+ {
+ 0x1F, 0xB4, 0xE3, 0x67, 0xEA, 0xB6, 0x42, 0xB7,
+ 0x2E, 0x43, 0xAD, 0x4A, 0xBD, 0xFC, 0xAD, 0x74,
+ 0x62, 0x0C, 0x3F, 0x6C, 0x63, 0xA8, 0x91, 0x31,
+ 0x28, 0xD2, 0x22, 0x6E, 0xB1, 0x92, 0xF9, 0x99,
+ 0x2E, 0xB9, 0xC8, 0xF7, 0x6A, 0xE2, 0x06, 0xD3,
+ 0xF5, 0xDE, 0xC7, 0x26, 0xA5, 0xA6, 0x86, 0xB4,
+ 0xAE, 0x37, 0xB5, 0x57, 0xAB, 0x57, 0xF9, 0x56,
+ 0x48, 0x53, 0x34, 0xF7, 0x3D, 0xCE, 0x02, 0xE0
+ },
+ {
+ 0x04, 0x25, 0xCA, 0xAA, 0x92, 0x3B, 0x47, 0xB3,
+ 0x50, 0x45, 0xEB, 0x50, 0x82, 0x9C, 0x04, 0x8B,
+ 0xC8, 0x90, 0x44, 0x4A, 0xFE, 0xEF, 0xC0, 0xAF,
+ 0xC9, 0xD1, 0x87, 0x7B, 0x82, 0x1E, 0x04, 0x3C,
+ 0x9C, 0x7B, 0x9D, 0x6D, 0xC3, 0x3F, 0xBB, 0xDF,
+ 0xA5, 0x37, 0xC1, 0xEC, 0xE3, 0x11, 0x96, 0x5B,
+ 0x2F, 0xEE, 0x89, 0x82, 0xBC, 0x46, 0xA2, 0xA7,
+ 0x50, 0xBF, 0xC7, 0x1D, 0x79, 0xDB, 0xEA, 0x04
+ },
+ {
+ 0x6B, 0x9D, 0x86, 0xF1, 0x5C, 0x09, 0x0A, 0x00,
+ 0xFC, 0x3D, 0x90, 0x7F, 0x90, 0x6C, 0x5E, 0xB7,
+ 0x92, 0x65, 0xE5, 0x8B, 0x88, 0xEB, 0x64, 0x29,
+ 0x4B, 0x4C, 0xC4, 0xE2, 0xB8, 0x9B, 0x1A, 0x7C,
+ 0x5E, 0xE3, 0x12, 0x7E, 0xD2, 0x1B, 0x45, 0x68,
+ 0x62, 0xDE, 0x6B, 0x2A, 0xBD, 0xA5, 0x9E, 0xAA,
+ 0xCF, 0x2D, 0xCB, 0xE9, 0x22, 0xCA, 0x75, 0x5E,
+ 0x40, 0x73, 0x5B, 0xE8, 0x1D, 0x9C, 0x88, 0xA5
+ },
+ {
+ 0x14, 0x6A, 0x18, 0x7A, 0x99, 0xE8, 0xA2, 0xD2,
+ 0x33, 0xE0, 0xEB, 0x37, 0x3D, 0x43, 0x7B, 0x02,
+ 0xBF, 0xA8, 0xD6, 0x51, 0x5B, 0x3C, 0xA1, 0xDE,
+ 0x48, 0xA6, 0xB6, 0xAC, 0xF7, 0x43, 0x7E, 0xB7,
+ 0xE7, 0xAC, 0x3F, 0x2D, 0x19, 0xEF, 0x3B, 0xB9,
+ 0xB8, 0x33, 0xCC, 0x57, 0x61, 0xDB, 0xA2, 0x2D,
+ 0x1A, 0xD0, 0x60, 0xBE, 0x76, 0xCD, 0xCB, 0x81,
+ 0x2D, 0x64, 0xD5, 0x78, 0xE9, 0x89, 0xA5, 0xA4
+ },
+ {
+ 0x25, 0x75, 0x4C, 0xA6, 0x66, 0x9C, 0x48, 0x70,
+ 0x84, 0x03, 0x88, 0xEA, 0x64, 0xE9, 0x5B, 0xD2,
+ 0xE0, 0x81, 0x0D, 0x36, 0x3C, 0x4C, 0xF6, 0xA1,
+ 0x6E, 0xA1, 0xBD, 0x06, 0x68, 0x6A, 0x93, 0xC8,
+ 0xA1, 0x25, 0xF2, 0x30, 0x22, 0x9D, 0x94, 0x84,
+ 0x85, 0xE1, 0xA8, 0x2D, 0xE4, 0x82, 0x00, 0x35,
+ 0x8F, 0x3E, 0x02, 0xB5, 0x05, 0xDA, 0xBC, 0x4F,
+ 0x13, 0x9C, 0x03, 0x79, 0xDC, 0x2B, 0x30, 0x80
+ },
+ {
+ 0x0E, 0x26, 0xCB, 0xC7, 0x8D, 0xC7, 0x54, 0xEC,
+ 0xA0, 0x6C, 0xF8, 0xCB, 0x31, 0xFC, 0xBA, 0xBB,
+ 0x18, 0x88, 0x92, 0xC1, 0x04, 0x50, 0x89, 0x05,
+ 0x49, 0xB2, 0xD4, 0x03, 0xA2, 0xA3, 0xC4, 0x57,
+ 0x70, 0x01, 0xF7, 0x4A, 0x76, 0xBD, 0x38, 0x99,
+ 0x0D, 0x75, 0x5B, 0xAE, 0x05, 0x26, 0x64, 0x83,
+ 0x29, 0xF6, 0x35, 0x45, 0xED, 0x16, 0x99, 0x5C,
+ 0xB1, 0xE6, 0x34, 0x3F, 0x18, 0x9F, 0x8E, 0x6F
+ },
+ {
+ 0x58, 0xE7, 0x98, 0x0B, 0x8B, 0x1A, 0x0B, 0x88,
+ 0xDA, 0x9D, 0xA8, 0x64, 0x0F, 0x2B, 0x96, 0xE3,
+ 0xE0, 0x48, 0x36, 0x61, 0x30, 0xC2, 0x66, 0x21,
+ 0x7D, 0xDC, 0x79, 0x53, 0x50, 0x8F, 0x4A, 0x40,
+ 0xD1, 0x67, 0x4D, 0xAB, 0xD3, 0x92, 0x89, 0xE3,
+ 0xF1, 0x0C, 0x61, 0x19, 0x68, 0xCC, 0xD1, 0xE9,
+ 0xCC, 0xC1, 0x8C, 0xAD, 0xC7, 0x77, 0x4A, 0x99,
+ 0x7D, 0xD1, 0xFA, 0x94, 0xE8, 0x35, 0x47, 0x07
+ },
+ {
+ 0x69, 0x6F, 0xB8, 0x47, 0x63, 0xE0, 0x23, 0x58,
+ 0x4B, 0x35, 0x90, 0x7A, 0x8B, 0x8A, 0xAA, 0x9E,
+ 0x0E, 0x78, 0x6F, 0x2C, 0xA5, 0x91, 0x45, 0x41,
+ 0x91, 0x58, 0x48, 0xFB, 0x6D, 0xDA, 0xB8, 0xD3,
+ 0xD2, 0xEA, 0xB6, 0x00, 0xC1, 0x38, 0xCE, 0x67,
+ 0x17, 0xB0, 0xC7, 0x02, 0x59, 0xD3, 0x19, 0x3E,
+ 0xA1, 0x56, 0x95, 0xC8, 0x50, 0x53, 0x7F, 0x2C,
+ 0x70, 0x6C, 0xA4, 0xAF, 0x15, 0x8E, 0x95, 0x7E
+ },
+ {
+ 0x23, 0xDE, 0x6E, 0x73, 0x07, 0x9C, 0x8C, 0x20,
+ 0x47, 0xA7, 0x84, 0x6A, 0x83, 0xCC, 0xAC, 0xAB,
+ 0xD3, 0x71, 0x16, 0x3B, 0x7B, 0x6D, 0x54, 0xEB,
+ 0x03, 0x2B, 0xC4, 0x9B, 0x66, 0x97, 0x42, 0xBE,
+ 0x71, 0x7B, 0x99, 0xDA, 0x12, 0xC6, 0x46, 0xAD,
+ 0x52, 0x57, 0x06, 0xF2, 0x22, 0xE1, 0xDF, 0x4A,
+ 0x91, 0xDD, 0x0C, 0xC6, 0x4D, 0xF1, 0x82, 0xDA,
+ 0x00, 0x73, 0x1D, 0x43, 0x9C, 0x46, 0xF8, 0xD2
+ },
+ {
+ 0xBB, 0x74, 0xF3, 0x6A, 0x9D, 0xB6, 0x96, 0xC9,
+ 0x33, 0x35, 0xE6, 0xC4, 0x6A, 0xAB, 0x58, 0xDB,
+ 0x10, 0xCB, 0x07, 0xEA, 0x4F, 0x1B, 0x71, 0x93,
+ 0x63, 0x05, 0x22, 0x83, 0x90, 0x95, 0x94, 0x78,
+ 0xF8, 0x73, 0x4E, 0x21, 0x54, 0x90, 0xE9, 0xAE,
+ 0x2A, 0x3E, 0xC8, 0xF7, 0xF7, 0x67, 0x33, 0xAE,
+ 0x3F, 0x8B, 0x9A, 0x3F, 0xD7, 0xC4, 0x06, 0xC6,
+ 0xCA, 0xC7, 0x09, 0x97, 0x5C, 0x40, 0xF8, 0x56
+ },
+ {
+ 0xEC, 0x63, 0x04, 0xD3, 0x8E, 0x23, 0x2C, 0x09,
+ 0x6A, 0xB5, 0x86, 0xCA, 0xDF, 0x27, 0x02, 0x6D,
+ 0xC5, 0xE5, 0x32, 0x17, 0xD0, 0xE8, 0xB0, 0xC6,
+ 0x0A, 0xDA, 0xAE, 0x22, 0xF4, 0xE8, 0xC2, 0x2D,
+ 0x30, 0xBC, 0x51, 0x77, 0xF1, 0xC8, 0x3A, 0xCD,
+ 0x92, 0x5E, 0x02, 0xA2, 0xDA, 0x89, 0x59, 0x5F,
+ 0xC1, 0x06, 0x09, 0x0E, 0x2E, 0x53, 0xED, 0xB3,
+ 0x1C, 0xDB, 0x76, 0xFF, 0x37, 0xEB, 0x61, 0x80
+ },
+ {
+ 0x92, 0xF9, 0xFC, 0x6B, 0xC5, 0x9A, 0x54, 0x3F,
+ 0x0D, 0xC9, 0xA1, 0x79, 0x8F, 0xB1, 0xE5, 0xD5,
+ 0x23, 0x47, 0x4E, 0x48, 0xFF, 0x3E, 0x29, 0x49,
+ 0x7F, 0x72, 0x80, 0xD1, 0xC4, 0x08, 0xC8, 0x66,
+ 0x33, 0x48, 0xFE, 0x2A, 0xF7, 0x8F, 0x6C, 0x4E,
+ 0x5E, 0xF5, 0xC0, 0xA0, 0x17, 0xF3, 0xD3, 0xF2,
+ 0x15, 0xEC, 0xDD, 0x7A, 0x40, 0x0A, 0xC5, 0x77,
+ 0x3B, 0x9E, 0x25, 0x60, 0x68, 0x84, 0x5A, 0x92
+ },
+ {
+ 0x4A, 0x25, 0xB5, 0x62, 0xF2, 0xFA, 0x01, 0xDD,
+ 0xEE, 0x7E, 0xA2, 0xE9, 0xFB, 0xF5, 0x2F, 0x8C,
+ 0x75, 0x6D, 0x28, 0xDB, 0x4A, 0x8B, 0xF7, 0x0E,
+ 0x74, 0x0E, 0x90, 0x27, 0x42, 0x6E, 0x51, 0x63,
+ 0x9D, 0xF8, 0x78, 0x8D, 0x13, 0x38, 0x56, 0x85,
+ 0x8D, 0x01, 0xFD, 0xDB, 0xDD, 0x5B, 0x98, 0x79,
+ 0x44, 0xC3, 0x00, 0xDC, 0x7F, 0x82, 0x41, 0xFB,
+ 0xCE, 0xFA, 0x4F, 0x12, 0x94, 0x8A, 0xFE, 0xAE
+ },
+ {
+ 0x34, 0x21, 0x2D, 0xD9, 0xF0, 0x65, 0x1F, 0x81,
+ 0x80, 0x9A, 0x14, 0xED, 0xBC, 0xF7, 0xF3, 0xAC,
+ 0xDE, 0xDE, 0x78, 0x72, 0xC7, 0xA4, 0x84, 0x7B,
+ 0xEA, 0x9F, 0x7A, 0xB7, 0x59, 0x73, 0x82, 0x47,
+ 0x7A, 0x4C, 0xB8, 0x47, 0x9A, 0x27, 0x63, 0x21,
+ 0x23, 0x5E, 0x90, 0x21, 0x57, 0x94, 0x46, 0xA4,
+ 0x38, 0x8A, 0x99, 0xE5, 0x60, 0xA3, 0x90, 0x7A,
+ 0xEE, 0xF2, 0xB4, 0x38, 0xFE, 0x6B, 0x90, 0xC4
+ },
+ {
+ 0xD6, 0x2C, 0xF7, 0xAB, 0xBC, 0x7D, 0x7B, 0xCD,
+ 0x5B, 0xEB, 0x1E, 0xE4, 0x8C, 0x43, 0xB8, 0x04,
+ 0xFD, 0x0D, 0xB4, 0x55, 0xE7, 0xF4, 0xFE, 0xBB,
+ 0xCF, 0xF1, 0x4B, 0x05, 0xBE, 0x90, 0x47, 0xE2,
+ 0x7E, 0x51, 0x8D, 0x6D, 0x3A, 0x6A, 0xDA, 0x4D,
+ 0x58, 0x63, 0xB7, 0xEC, 0x7F, 0x84, 0x92, 0x45,
+ 0x89, 0x40, 0xAC, 0x6B, 0xDD, 0xB5, 0x06, 0x59,
+ 0x2C, 0xCB, 0xC8, 0x96, 0xAF, 0xBB, 0x77, 0xA3
+ },
+ {
+ 0x33, 0xA3, 0xA2, 0x63, 0x6F, 0x91, 0x98, 0xD3,
+ 0x7A, 0x5F, 0xF1, 0xBF, 0xF9, 0xEB, 0x10, 0x02,
+ 0x4B, 0x28, 0x46, 0x80, 0x39, 0xF4, 0x91, 0x40,
+ 0x2D, 0x39, 0xB7, 0x08, 0xC5, 0x5D, 0x27, 0xE5,
+ 0xE8, 0xDF, 0x5E, 0x3E, 0x19, 0x49, 0x95, 0x82,
+ 0x35, 0xCA, 0xD9, 0x80, 0x74, 0x20, 0x96, 0xF2,
+ 0x77, 0x9A, 0x1D, 0x71, 0xDA, 0xD5, 0x8F, 0xAF,
+ 0xA3, 0xCD, 0x02, 0xCB, 0x5E, 0xAA, 0x98, 0xC5
+ },
+ {
+ 0xB7, 0xA3, 0x89, 0x90, 0xE6, 0xF4, 0x56, 0x4A,
+ 0xA3, 0xD9, 0x3A, 0x79, 0x37, 0x10, 0x0C, 0x29,
+ 0xF9, 0x40, 0xAF, 0xF7, 0xCB, 0x20, 0x86, 0x5A,
+ 0x1C, 0x21, 0x89, 0x81, 0xA5, 0x42, 0x04, 0x86,
+ 0x08, 0x17, 0x81, 0xF8, 0xD5, 0x0C, 0x86, 0x62,
+ 0x5C, 0xC5, 0xD7, 0x6D, 0x0F, 0x5C, 0xCC, 0x4E,
+ 0xB6, 0x5D, 0x43, 0x66, 0x09, 0x62, 0x4F, 0x21,
+ 0xD0, 0x53, 0x39, 0xAB, 0x0C, 0xF7, 0x9F, 0x4C
+ },
+ {
+ 0x9D, 0x66, 0x5A, 0x3F, 0xDD, 0x10, 0x45, 0x9E,
+ 0x77, 0xF0, 0x3A, 0xC8, 0xC0, 0xE2, 0x39, 0x01,
+ 0x94, 0x89, 0x69, 0x3C, 0xC9, 0x31, 0x5A, 0xA3,
+ 0xFF, 0x11, 0x29, 0x11, 0xD2, 0xAC, 0xF0, 0xB7,
+ 0xD2, 0x76, 0xAC, 0x76, 0x9B, 0xED, 0xFD, 0x85,
+ 0x2D, 0x28, 0x89, 0xDD, 0x12, 0xDB, 0x91, 0x39,
+ 0x8B, 0x01, 0xC4, 0xF4, 0xA5, 0xDA, 0x27, 0x80,
+ 0xB1, 0xDE, 0xFE, 0x0D, 0x95, 0xB6, 0x32, 0x70
+ },
+ {
+ 0x70, 0xFB, 0x9E, 0xFD, 0x5B, 0xCA, 0x7F, 0x19,
+ 0xB6, 0xE3, 0x1D, 0x64, 0x0D, 0xCF, 0x88, 0xD7,
+ 0x7E, 0x76, 0x8A, 0xE2, 0x27, 0xEC, 0xB3, 0xFD,
+ 0x6B, 0x47, 0x13, 0x78, 0x94, 0xF5, 0x49, 0xBF,
+ 0x1C, 0xF0, 0x6E, 0x5D, 0xB4, 0x54, 0x60, 0x44,
+ 0xDD, 0x9F, 0x46, 0x5C, 0x9C, 0x85, 0xF7, 0x28,
+ 0x4F, 0xE5, 0x4D, 0x2B, 0x71, 0x52, 0x69, 0x9B,
+ 0xE4, 0xBD, 0x55, 0x5A, 0x90, 0x9A, 0x88, 0xA9
+ },
+ {
+ 0x7A, 0xFD, 0xB0, 0x19, 0x30, 0x87, 0xE0, 0xC9,
+ 0xF8, 0xB4, 0xDD, 0x8B, 0x48, 0xD9, 0xF2, 0x0A,
+ 0xCE, 0x27, 0x13, 0xAF, 0xC7, 0x1B, 0xCC, 0x93,
+ 0x82, 0xB5, 0x42, 0x90, 0xAE, 0xBF, 0xFE, 0xB2,
+ 0xD1, 0x38, 0xF4, 0xDC, 0xF0, 0x28, 0xF9, 0xC4,
+ 0x3C, 0xC1, 0x80, 0x89, 0x84, 0x77, 0xA3, 0x9E,
+ 0x3F, 0x53, 0xA8, 0xD1, 0xBF, 0x67, 0xCE, 0xB6,
+ 0x08, 0x26, 0x1F, 0xAE, 0x6D, 0xDB, 0x1A, 0xBC
+ },
+ {
+ 0x05, 0x99, 0x0D, 0x7D, 0x7D, 0xF1, 0xD4, 0x84,
+ 0xF5, 0xB1, 0xCA, 0xE9, 0xEE, 0x5D, 0xFC, 0xB4,
+ 0x3F, 0x2C, 0xBE, 0x18, 0x6C, 0x1A, 0x5B, 0x18,
+ 0x1A, 0x37, 0x31, 0xD4, 0xB1, 0x54, 0x8E, 0xBF,
+ 0xF5, 0xBF, 0x61, 0xCB, 0x0F, 0x6D, 0x9F, 0xC2,
+ 0x30, 0xF2, 0x5E, 0x86, 0x78, 0xB7, 0x99, 0xE0,
+ 0xE8, 0x30, 0x26, 0xA0, 0x86, 0x6B, 0xF0, 0xAC,
+ 0xAB, 0x08, 0x9E, 0x10, 0x2E, 0x67, 0xAB, 0x6B
+ },
+ {
+ 0x1A, 0xF7, 0xA5, 0xCE, 0x58, 0x7C, 0x8D, 0x87,
+ 0xC7, 0xB7, 0x9F, 0xA3, 0xE7, 0x23, 0xD7, 0x4C,
+ 0xE0, 0x26, 0xB5, 0x28, 0x67, 0x52, 0xFD, 0x0C,
+ 0x37, 0x42, 0xC6, 0xF0, 0x41, 0x8E, 0xD7, 0x85,
+ 0x99, 0x0D, 0x21, 0xF2, 0x8D, 0xA8, 0x39, 0xCE,
+ 0x82, 0x12, 0xED, 0x55, 0x0C, 0x37, 0x3E, 0x6D,
+ 0x3A, 0x75, 0xD5, 0x5C, 0x31, 0x77, 0x04, 0x41,
+ 0xEE, 0xAF, 0xF2, 0xD5, 0x0F, 0x6E, 0x61, 0xB6
+ },
+ {
+ 0xDD, 0xEE, 0x0C, 0x76, 0xC9, 0xBD, 0xD3, 0x2D,
+ 0x70, 0x49, 0x35, 0x4C, 0xFC, 0x85, 0xDC, 0x68,
+ 0x67, 0xE2, 0x49, 0x2E, 0x47, 0xFE, 0xB0, 0x8E,
+ 0x39, 0x83, 0xD0, 0xB6, 0x78, 0x84, 0x5D, 0x7E,
+ 0xC6, 0xC9, 0x79, 0x3C, 0x33, 0x26, 0xBF, 0xDC,
+ 0x1E, 0x11, 0x32, 0x76, 0xD1, 0x77, 0xFE, 0x38,
+ 0x82, 0x52, 0x04, 0xDD, 0x00, 0x07, 0x39, 0x89,
+ 0xC0, 0x81, 0xCC, 0x3B, 0x71, 0xC6, 0x8D, 0x5F
+ },
+ {
+ 0xDE, 0x07, 0x06, 0x48, 0xB3, 0x7C, 0x47, 0xDC,
+ 0x9F, 0x2F, 0x6D, 0x2A, 0xB2, 0x07, 0x73, 0xCD,
+ 0x82, 0xFA, 0x57, 0x25, 0xA6, 0x90, 0x0E, 0xB7,
+ 0x1C, 0xDD, 0xB0, 0xC9, 0xF3, 0x9B, 0x31, 0xDF,
+ 0x6D, 0x07, 0x73, 0x24, 0x6E, 0x8E, 0xF9, 0x03,
+ 0x49, 0x67, 0x75, 0x2D, 0xB7, 0xED, 0x22, 0x73,
+ 0x3F, 0x43, 0x79, 0x94, 0x8D, 0xC3, 0x96, 0xDC,
+ 0x35, 0xAD, 0xBB, 0xE9, 0xF6, 0x53, 0x77, 0x40
+ },
+ {
+ 0xA6, 0x45, 0x6F, 0xBC, 0xFF, 0x9E, 0x3D, 0x5B,
+ 0x11, 0x6A, 0x0E, 0x33, 0x1A, 0x1F, 0x97, 0x4F,
+ 0x07, 0x0E, 0x95, 0x56, 0x09, 0x78, 0x1F, 0xA5,
+ 0x99, 0xD6, 0x08, 0xA3, 0x1D, 0xA7, 0x6A, 0xD8,
+ 0xAB, 0xFE, 0x34, 0x66, 0x17, 0xC2, 0x57, 0x86,
+ 0x51, 0x3B, 0x2C, 0x44, 0xBF, 0xE2, 0xCB, 0x45,
+ 0x7C, 0x43, 0xFA, 0x6F, 0x45, 0x36, 0x1C, 0xA9,
+ 0xC6, 0x34, 0x13, 0x11, 0xB7, 0xDD, 0xFB, 0xD5
+ },
+ {
+ 0x5C, 0x95, 0xD3, 0x82, 0x02, 0x18, 0x91, 0x04,
+ 0x8B, 0x5E, 0xC8, 0x1C, 0xC8, 0x8E, 0x66, 0xB1,
+ 0xB4, 0xD8, 0x0A, 0x00, 0xB5, 0xEE, 0x66, 0xB3,
+ 0xC0, 0x30, 0x77, 0x49, 0xE6, 0xF2, 0x4D, 0x17,
+ 0x0D, 0x23, 0xFA, 0xCC, 0x8E, 0xB2, 0x53, 0xB3,
+ 0x56, 0x2B, 0xF8, 0xA4, 0x5C, 0x37, 0x99, 0x0C,
+ 0xD2, 0xD3, 0xE4, 0x43, 0xB1, 0x8C, 0x68, 0xBB,
+ 0xCC, 0x6C, 0x83, 0x1D, 0xFD, 0xE2, 0xF8, 0xE5
+ },
+ {
+ 0xE3, 0x74, 0x00, 0xDB, 0xD9, 0x21, 0x0F, 0x31,
+ 0x37, 0xAC, 0xAF, 0x49, 0x24, 0x2F, 0xA1, 0x23,
+ 0xA0, 0x52, 0x95, 0x8A, 0x4C, 0x0D, 0x98, 0x90,
+ 0x62, 0x47, 0xD5, 0x35, 0xA3, 0x51, 0xFD, 0x52,
+ 0x29, 0x6E, 0x70, 0x10, 0x32, 0x5B, 0xDA, 0x84,
+ 0x1F, 0xA2, 0xAA, 0xB4, 0x47, 0x63, 0x76, 0x3C,
+ 0x55, 0x04, 0xD7, 0xB3, 0x0C, 0x6D, 0x79, 0xFC,
+ 0x1D, 0xC8, 0xCF, 0x10, 0x24, 0x46, 0x6D, 0xB0
+ },
+ {
+ 0x52, 0x73, 0xA3, 0xA1, 0x3C, 0xF0, 0xEC, 0x72,
+ 0x00, 0x44, 0x2C, 0xBD, 0x7B, 0x37, 0x44, 0x66,
+ 0xA7, 0x19, 0x0D, 0xDC, 0xA1, 0x31, 0xD9, 0x63,
+ 0xF8, 0xF8, 0x39, 0x65, 0xAE, 0xD3, 0xDD, 0x86,
+ 0xE9, 0xD4, 0x5A, 0xB4, 0x89, 0xB9, 0xC5, 0x62,
+ 0x47, 0xC9, 0xF2, 0xAA, 0x69, 0xFD, 0x7E, 0x31,
+ 0x87, 0xB8, 0xFA, 0x0D, 0xAC, 0x77, 0xC4, 0x7C,
+ 0xB2, 0x95, 0xBA, 0x62, 0x96, 0x78, 0x43, 0x94
+ },
+ {
+ 0x2A, 0xDB, 0x93, 0x49, 0xA9, 0xEC, 0x37, 0xFF,
+ 0x49, 0x62, 0xF4, 0x21, 0x7E, 0x80, 0xEB, 0xDC,
+ 0xD3, 0x60, 0x96, 0x7B, 0x51, 0x3D, 0x12, 0x02,
+ 0xD9, 0x98, 0x28, 0x31, 0x15, 0x5D, 0x2F, 0x43,
+ 0xEB, 0x9A, 0xDD, 0x63, 0xB5, 0xEC, 0x10, 0xD3,
+ 0xD0, 0x43, 0x0D, 0xC9, 0xCF, 0x76, 0x48, 0x11,
+ 0x7F, 0xC6, 0x0B, 0xAB, 0xBF, 0x8E, 0xBF, 0x19,
+ 0xFA, 0xCE, 0xE5, 0x50, 0x45, 0x5B, 0x60, 0xC9
+ },
+ {
+ 0xAC, 0xAA, 0xDA, 0x3E, 0x47, 0x37, 0xC6, 0x63,
+ 0xEB, 0xF0, 0x3C, 0x02, 0x49, 0xCC, 0xA6, 0xF3,
+ 0x17, 0x9A, 0x03, 0x84, 0xEA, 0x2A, 0xB1, 0x35,
+ 0xD4, 0xD7, 0xA2, 0xBB, 0x8A, 0x2F, 0x40, 0x53,
+ 0x9C, 0xDC, 0xE8, 0xA3, 0x76, 0x0F, 0xD1, 0x3D,
+ 0xEE, 0xEC, 0xD1, 0x60, 0x61, 0x7F, 0x72, 0xDE,
+ 0x63, 0x75, 0x4E, 0x21, 0x57, 0xCA, 0xDC, 0xF0,
+ 0x67, 0x32, 0x9C, 0x2A, 0x51, 0x98, 0xF8, 0xE0
+ },
+ {
+ 0xEF, 0x15, 0xE6, 0xDB, 0x96, 0xE6, 0xD0, 0xC1,
+ 0x8C, 0x70, 0xAD, 0xC3, 0xCD, 0xB3, 0x2B, 0x28,
+ 0x67, 0x74, 0x02, 0xE8, 0xEA, 0x44, 0x11, 0xEA,
+ 0x2F, 0x34, 0x68, 0xED, 0x93, 0x82, 0xE1, 0x9B,
+ 0xFE, 0xCA, 0xF5, 0xAC, 0xB8, 0x28, 0xA5, 0x2B,
+ 0xE1, 0x6B, 0x98, 0x1E, 0x48, 0x7E, 0x5B, 0xB4,
+ 0xA1, 0x43, 0x08, 0x65, 0x35, 0x8E, 0x97, 0x9F,
+ 0xB1, 0x07, 0x1F, 0xB9, 0x51, 0x14, 0xFF, 0xDD
+ },
+ {
+ 0x05, 0x7E, 0xAB, 0x8F, 0xA6, 0x1C, 0x23, 0x09,
+ 0x67, 0xD9, 0x5D, 0xFB, 0x75, 0x45, 0x57, 0x0E,
+ 0x34, 0x1A, 0xE3, 0xC6, 0x73, 0x7C, 0x7D, 0xB2,
+ 0xA2, 0x27, 0xD9, 0x0F, 0xF3, 0x15, 0xD0, 0x98,
+ 0xD4, 0x76, 0xF7, 0x15, 0x77, 0x9E, 0x67, 0x72,
+ 0xB4, 0xED, 0x37, 0x54, 0x82, 0x66, 0xE6, 0x59,
+ 0x8C, 0x6F, 0x09, 0x69, 0x13, 0xC2, 0xFD, 0xD8,
+ 0xD6, 0xE4, 0x4F, 0xE2, 0xB5, 0x4D, 0x97, 0x80
+ },
+ {
+ 0xED, 0xE6, 0x8D, 0x1B, 0x13, 0xE7, 0xEF, 0x78,
+ 0xD9, 0xC4, 0xEE, 0x10, 0xEC, 0xEB, 0x1D, 0x2A,
+ 0xEE, 0xC3, 0xB8, 0x15, 0x7F, 0xDB, 0x91, 0x41,
+ 0x8C, 0x22, 0x19, 0xF6, 0x41, 0x49, 0x74, 0x70,
+ 0x17, 0xAC, 0xA7, 0xD4, 0x65, 0xB8, 0xB4, 0x7F,
+ 0xFA, 0x53, 0x64, 0x4B, 0x8B, 0xC6, 0xDA, 0x12,
+ 0xDD, 0x45, 0xD1, 0x05, 0x5E, 0x47, 0xB4, 0xD8,
+ 0x39, 0x0E, 0xB2, 0xBD, 0x60, 0x2B, 0xA0, 0x30
+ },
+ {
+ 0x27, 0xF8, 0x56, 0xE6, 0x3E, 0xB9, 0x4D, 0x08,
+ 0xFB, 0xBE, 0x50, 0x22, 0xB0, 0xED, 0xDB, 0xC7,
+ 0xD8, 0xDB, 0x86, 0x5E, 0xF4, 0xFE, 0xC2, 0x05,
+ 0x86, 0xDF, 0x3D, 0xD9, 0x02, 0xA0, 0x5B, 0x26,
+ 0x35, 0x9E, 0x26, 0x7C, 0x78, 0x8D, 0x7C, 0x88,
+ 0x03, 0x2E, 0x76, 0x6B, 0x11, 0x87, 0x40, 0x20,
+ 0x0F, 0x49, 0xCB, 0x4D, 0x6E, 0xDB, 0x15, 0x61,
+ 0xB2, 0xDE, 0x7D, 0xC6, 0x5E, 0xE6, 0x42, 0x3B
+ },
+ {
+ 0xE9, 0xE9, 0x8D, 0x6D, 0xE0, 0xEF, 0x53, 0xFD,
+ 0x24, 0x27, 0x66, 0x1E, 0x1A, 0xCF, 0x10, 0x3D,
+ 0x4C, 0xAA, 0x4D, 0xC6, 0x10, 0x03, 0x62, 0x09,
+ 0xEC, 0x99, 0x74, 0x19, 0xC1, 0x20, 0x63, 0x1C,
+ 0x2C, 0x09, 0x4A, 0x8E, 0xE7, 0x82, 0x2D, 0x43,
+ 0xF8, 0x77, 0x80, 0x11, 0xC6, 0x03, 0x11, 0x1F,
+ 0x26, 0x28, 0xF8, 0x97, 0xC9, 0xB4, 0x31, 0x31,
+ 0x54, 0x77, 0x75, 0x6B, 0x03, 0x2E, 0x1F, 0x8D
+ },
+ {
+ 0x52, 0xEB, 0x1E, 0x6C, 0x8A, 0x54, 0x49, 0x2C,
+ 0xA7, 0x60, 0xB5, 0x6C, 0xA8, 0x7D, 0xA3, 0xE1,
+ 0xA9, 0xA6, 0xD8, 0xA4, 0x21, 0x92, 0x19, 0x35,
+ 0x1D, 0x18, 0x71, 0x5A, 0x9A, 0x2C, 0x26, 0x70,
+ 0x8B, 0xB7, 0x12, 0xCD, 0xAC, 0x04, 0x34, 0x48,
+ 0x2E, 0x55, 0x1C, 0xB0, 0x9E, 0x3F, 0x16, 0x33,
+ 0x8D, 0xE2, 0x9B, 0xE2, 0xC6, 0x67, 0x40, 0xC3,
+ 0x44, 0xDF, 0x54, 0x88, 0xC5, 0xC2, 0xBB, 0x26
+ },
+ {
+ 0x47, 0x3F, 0xA6, 0xC5, 0x1A, 0x48, 0x10, 0x5F,
+ 0x72, 0x1C, 0x5C, 0xB8, 0xDB, 0xA6, 0x1C, 0x64,
+ 0xA1, 0xE3, 0xDD, 0xCC, 0xC3, 0x25, 0x0E, 0x68,
+ 0x22, 0x62, 0xF2, 0x12, 0xC0, 0x1A, 0xB4, 0x87,
+ 0x4A, 0xFF, 0x68, 0x8F, 0xEA, 0x96, 0x37, 0x73,
+ 0x9E, 0x2A, 0x25, 0xD2, 0xEE, 0x88, 0xDB, 0xDC,
+ 0xC4, 0xF0, 0x4D, 0x01, 0x47, 0x9B, 0x30, 0x17,
+ 0x17, 0x53, 0x3A, 0x64, 0x32, 0xB8, 0x50, 0xCD
+ },
+ {
+ 0x6B, 0x76, 0x60, 0xD4, 0x10, 0xEA, 0xE5, 0xF3,
+ 0x5A, 0xD0, 0xAE, 0x85, 0xE6, 0x3D, 0xA4, 0x53,
+ 0xEB, 0xB0, 0x57, 0xE4, 0x3F, 0x42, 0xE8, 0x42,
+ 0xCB, 0xF6, 0x25, 0x0D, 0xA6, 0x78, 0x66, 0xB4,
+ 0x24, 0x0D, 0x57, 0xC8, 0x3B, 0x77, 0x1B, 0x0F,
+ 0x70, 0x66, 0x3E, 0x17, 0xFB, 0xD9, 0x08, 0x7F,
+ 0x76, 0xB4, 0xCE, 0x6B, 0xCD, 0x0B, 0x50, 0x2E,
+ 0x33, 0x74, 0xB1, 0x50, 0x9B, 0xBA, 0x55, 0xA8
+ },
+ {
+ 0xA4, 0xD0, 0x8A, 0xCA, 0x7A, 0x9E, 0xA6, 0x43,
+ 0x99, 0x99, 0xEA, 0x21, 0xE4, 0xCF, 0xE9, 0x86,
+ 0x9B, 0xB9, 0x0E, 0x3A, 0x01, 0x48, 0x71, 0xAD,
+ 0x88, 0xED, 0x3A, 0x97, 0xAA, 0x89, 0x15, 0x95,
+ 0x1C, 0x3F, 0xD0, 0xB3, 0x93, 0x3A, 0x50, 0x85,
+ 0x88, 0x93, 0x8A, 0xF7, 0x54, 0x49, 0x44, 0xEF,
+ 0x43, 0xC4, 0x40, 0xAA, 0x8F, 0xF1, 0xE5, 0xA8,
+ 0x18, 0xA4, 0x66, 0x43, 0x5D, 0xE7, 0x0F, 0xA8
+ },
+ {
+ 0x85, 0xE0, 0xE9, 0xB5, 0x0D, 0x2D, 0xB0, 0x22,
+ 0xC2, 0x39, 0xD7, 0x23, 0x2A, 0xE4, 0x7C, 0x02,
+ 0x59, 0x22, 0xE4, 0xF0, 0x7E, 0x2A, 0xFC, 0x65,
+ 0x6C, 0xDC, 0x55, 0x53, 0xA2, 0x7D, 0x95, 0xBF,
+ 0xA5, 0x8A, 0x57, 0x4D, 0x4E, 0xC3, 0xA9, 0x73,
+ 0x28, 0x1A, 0x8F, 0x4E, 0x46, 0xA7, 0x1A, 0xB0,
+ 0x34, 0x1C, 0x25, 0x77, 0x28, 0x74, 0x63, 0xE2,
+ 0x51, 0x04, 0x4D, 0xB2, 0x39, 0x8D, 0x55, 0xE2
+ },
+ {
+ 0x81, 0xA0, 0xD0, 0x24, 0x42, 0x90, 0x51, 0x91,
+ 0x16, 0x33, 0x70, 0xAE, 0x29, 0xC7, 0xF8, 0x9C,
+ 0x0F, 0x48, 0xBC, 0x1A, 0x1E, 0xB2, 0x94, 0x70,
+ 0x47, 0xDA, 0x1C, 0x62, 0x2B, 0x86, 0x77, 0xE9,
+ 0xEA, 0x9B, 0xEC, 0xED, 0x55, 0xD3, 0x3A, 0xDB,
+ 0x15, 0x53, 0xBD, 0x58, 0x4A, 0xD2, 0xF8, 0x6A,
+ 0x62, 0x07, 0xE8, 0x4E, 0x40, 0xE4, 0x60, 0x7E,
+ 0x11, 0x65, 0x0E, 0xE2, 0x87, 0x9F, 0x4E, 0x0B
+ },
+ {
+ 0x87, 0x79, 0x0D, 0xF6, 0xCF, 0x73, 0x94, 0x45,
+ 0x1B, 0xCC, 0x73, 0x0E, 0x53, 0xFC, 0x57, 0xBE,
+ 0x56, 0x45, 0x22, 0x77, 0x1E, 0x14, 0x43, 0x2A,
+ 0x80, 0xAB, 0x0B, 0x06, 0xB7, 0xB1, 0xD2, 0x09,
+ 0xAD, 0x69, 0x89, 0x95, 0x12, 0x53, 0x85, 0xDB,
+ 0x8B, 0x3C, 0x09, 0x59, 0xB8, 0xA5, 0x33, 0x9E,
+ 0xDA, 0x0A, 0xE6, 0x78, 0x59, 0xD8, 0x47, 0xF4,
+ 0x4C, 0x81, 0x59, 0x72, 0x72, 0xCB, 0xF1, 0x95
+ },
+ {
+ 0xCC, 0x06, 0x4E, 0xA8, 0x53, 0xDC, 0x01, 0x52,
+ 0xCC, 0x03, 0xFE, 0xB5, 0xFB, 0x5D, 0xE7, 0x8B,
+ 0x9B, 0x88, 0xE9, 0x61, 0x55, 0xD5, 0x35, 0x8B,
+ 0xCE, 0x84, 0xA5, 0x4C, 0x0E, 0x0C, 0x42, 0xFB,
+ 0xDA, 0x09, 0x2F, 0x22, 0xD0, 0x56, 0xDF, 0x99,
+ 0x93, 0x26, 0x2E, 0x2B, 0xA4, 0x4A, 0x5B, 0x2D,
+ 0x53, 0xC3, 0x75, 0x9D, 0x09, 0x45, 0xFE, 0xBA,
+ 0xA6, 0xFD, 0x51, 0xB8, 0xFF, 0x38, 0xD8, 0x39
+ },
+ {
+ 0x7E, 0x51, 0x7F, 0xC3, 0x83, 0xEE, 0x8C, 0x9F,
+ 0x0A, 0x01, 0x68, 0x1D, 0x39, 0xE7, 0x3B, 0xEB,
+ 0xA5, 0x96, 0x95, 0x95, 0xCE, 0x77, 0x92, 0x7F,
+ 0x91, 0x69, 0x1F, 0x33, 0xBB, 0x3E, 0x13, 0x07,
+ 0xEE, 0x03, 0x61, 0x6C, 0x27, 0xE6, 0x79, 0x51,
+ 0x86, 0xF6, 0x94, 0x0F, 0xED, 0xD9, 0xD5, 0xC7,
+ 0xF2, 0x1B, 0x6D, 0x2A, 0xAF, 0x70, 0x29, 0x9C,
+ 0xDD, 0x83, 0x51, 0x25, 0x05, 0x0A, 0x8B, 0x3C
+ },
+ {
+ 0x84, 0x5F, 0xCF, 0xA6, 0x7F, 0x6E, 0x06, 0x55,
+ 0x10, 0xD2, 0x62, 0xF1, 0xDD, 0x69, 0x39, 0xEA,
+ 0x4C, 0x0A, 0x4A, 0x59, 0xC8, 0xEE, 0x39, 0x77,
+ 0xDB, 0x70, 0x05, 0xE1, 0xAE, 0xE4, 0x20, 0xBD,
+ 0x3F, 0x38, 0x26, 0xEC, 0xFE, 0x59, 0x01, 0x5B,
+ 0x4D, 0xFA, 0x0B, 0xD5, 0xBB, 0xF8, 0xD8, 0xA4,
+ 0x34, 0x48, 0x5D, 0xC1, 0x1C, 0xB9, 0xCC, 0x85,
+ 0x97, 0xCB, 0x8C, 0x95, 0x66, 0x11, 0x5F, 0x31
+ },
+ {
+ 0x17, 0xCF, 0x2C, 0x23, 0x21, 0x5B, 0xCD, 0xFC,
+ 0x24, 0x3D, 0x8A, 0x94, 0x5F, 0x3C, 0x5C, 0x25,
+ 0x1D, 0x27, 0x18, 0xA3, 0xF7, 0x5F, 0xED, 0x6F,
+ 0x33, 0x20, 0xBC, 0xC6, 0xFD, 0x92, 0x73, 0x86,
+ 0xD5, 0x6F, 0x87, 0x19, 0xCC, 0xA0, 0x2E, 0xC5,
+ 0xE9, 0x9C, 0xDA, 0xC4, 0xEA, 0x10, 0x95, 0xB4,
+ 0x65, 0xBA, 0x9A, 0x29, 0x8B, 0x1D, 0x23, 0x8E,
+ 0x38, 0xB3, 0xFA, 0x15, 0xE8, 0xB1, 0x4E, 0xE4
+ },
+ {
+ 0xD7, 0x89, 0xCE, 0xC7, 0xD7, 0x52, 0x0F, 0x10,
+ 0xE8, 0xB8, 0xB6, 0xC8, 0x40, 0x95, 0x89, 0xDF,
+ 0x57, 0xB8, 0x56, 0xB8, 0x24, 0x55, 0x68, 0xF6,
+ 0x4E, 0x2D, 0x21, 0x83, 0xE3, 0x59, 0xA7, 0x84,
+ 0xC8, 0xD2, 0x6C, 0xF9, 0xB7, 0x20, 0xF5, 0xDF,
+ 0x56, 0x7B, 0x01, 0xF3, 0xF4, 0x8D, 0xE6, 0x4D,
+ 0x4F, 0x0D, 0xB1, 0x56, 0xBE, 0x52, 0x5D, 0x7C,
+ 0x7A, 0x66, 0x5A, 0xAD, 0xC5, 0x91, 0xF0, 0xB6
+ },
+ {
+ 0xB5, 0xE2, 0x46, 0xA9, 0x02, 0x77, 0x10, 0xC0,
+ 0xB0, 0x55, 0xC7, 0x1F, 0x11, 0x67, 0xE0, 0xEE,
+ 0x36, 0xEB, 0xC4, 0x32, 0xCF, 0x5D, 0x14, 0x27,
+ 0x75, 0xA7, 0xAE, 0xCC, 0xCE, 0xA7, 0x83, 0x25,
+ 0xED, 0x8C, 0x12, 0xF5, 0x0F, 0xBE, 0x64, 0x8A,
+ 0xDD, 0xF0, 0x59, 0xB8, 0xC0, 0x2A, 0x61, 0x49,
+ 0x2F, 0x83, 0x57, 0xBE, 0xE1, 0x42, 0xE7, 0xF7,
+ 0xDE, 0x04, 0x33, 0x78, 0xDB, 0xCF, 0x2D, 0x33
+ },
+ {
+ 0xB5, 0x23, 0xFD, 0x77, 0xAB, 0x9E, 0xEE, 0x42,
+ 0x48, 0x72, 0xBC, 0x2E, 0x83, 0xFC, 0x0A, 0x77,
+ 0xFF, 0x8A, 0x90, 0xC9, 0xA0, 0xCE, 0x9E, 0x8C,
+ 0x87, 0x68, 0x0A, 0x0F, 0x62, 0x86, 0x33, 0x1F,
+ 0x15, 0xC9, 0x3A, 0x2A, 0xFE, 0xCF, 0x75, 0x66,
+ 0x65, 0x3F, 0x24, 0xD9, 0x30, 0xC3, 0x23, 0x19,
+ 0x2D, 0x30, 0x43, 0xB9, 0x05, 0x72, 0x1C, 0xBD,
+ 0xB6, 0x31, 0x11, 0xCA, 0x42, 0xF2, 0x8F, 0x4E
+ },
+ {
+ 0x43, 0x59, 0xA4, 0x58, 0x76, 0xBF, 0x6A, 0xCC,
+ 0x0A, 0xEC, 0xE7, 0xB9, 0xB4, 0xB4, 0xA8, 0x38,
+ 0xB9, 0xDB, 0xA5, 0x77, 0x6A, 0x3B, 0x14, 0xDA,
+ 0x2F, 0xBA, 0x91, 0x02, 0xE7, 0x8B, 0xF6, 0x48,
+ 0xFF, 0xB4, 0xD8, 0x67, 0xBA, 0xE8, 0x5F, 0xD9,
+ 0xB7, 0x13, 0x12, 0xDC, 0x46, 0x02, 0xD0, 0xD4,
+ 0x9C, 0x90, 0x7B, 0xB9, 0x28, 0x9B, 0x22, 0x95,
+ 0x96, 0x1E, 0x54, 0x13, 0x81, 0x23, 0xF5, 0x4A
+ },
+ {
+ 0xD3, 0xF2, 0xC8, 0xE7, 0x4F, 0x34, 0x3A, 0x4E,
+ 0x71, 0x90, 0xD4, 0x75, 0xCF, 0x9A, 0xF7, 0x54,
+ 0xEE, 0xD5, 0x57, 0x72, 0x62, 0xB3, 0x5B, 0xD9,
+ 0xA9, 0xC4, 0x2B, 0x58, 0xCE, 0x88, 0x26, 0x2E,
+ 0x31, 0x14, 0x91, 0x7F, 0xB9, 0xE6, 0x83, 0xC6,
+ 0x2D, 0x9F, 0x89, 0x47, 0xB5, 0x8A, 0x29, 0x4D,
+ 0xA5, 0x06, 0xFB, 0x86, 0xB3, 0xED, 0xF2, 0x5C,
+ 0xB9, 0xE2, 0xD2, 0xDF, 0x61, 0x1C, 0xD4, 0x48
+ },
+ {
+ 0x41, 0xB8, 0x90, 0xF8, 0xE8, 0x45, 0x0D, 0xAD,
+ 0xB6, 0x95, 0x9A, 0xCC, 0xBA, 0x19, 0x49, 0x17,
+ 0xE0, 0x2F, 0x30, 0x67, 0x82, 0x1D, 0x4E, 0x99,
+ 0x5A, 0x37, 0xAC, 0x18, 0xBA, 0x3E, 0x47, 0xC7,
+ 0x50, 0x6E, 0x7A, 0x3D, 0xD1, 0xE1, 0x12, 0xE6,
+ 0xEC, 0x41, 0xBE, 0xF5, 0x30, 0x85, 0x11, 0x20,
+ 0x89, 0x4A, 0x7B, 0x34, 0xB3, 0xDB, 0xCD, 0xAE,
+ 0x40, 0x73, 0x27, 0xF0, 0xC5, 0x73, 0x6E, 0xDF
+ },
+ {
+ 0x19, 0xD7, 0x14, 0x4F, 0x0C, 0x85, 0x1E, 0xB8,
+ 0xB0, 0x53, 0xA3, 0xA4, 0x35, 0x86, 0x52, 0x6D,
+ 0xC5, 0xC7, 0x73, 0xE4, 0x97, 0x97, 0x51, 0x64,
+ 0xD1, 0x11, 0x51, 0x36, 0x43, 0x68, 0xDF, 0x24,
+ 0xBC, 0x44, 0xD5, 0x36, 0x07, 0x23, 0x04, 0xD7,
+ 0x06, 0x31, 0xA8, 0x40, 0xB6, 0x36, 0xB9, 0x66,
+ 0xFD, 0x02, 0x8F, 0x61, 0x06, 0x2B, 0xFC, 0x52,
+ 0x85, 0x67, 0x01, 0x53, 0xA6, 0x36, 0x3A, 0x0A
+ },
+ {
+ 0xC2, 0x18, 0x4C, 0x1A, 0x81, 0xE9, 0x83, 0xBE,
+ 0x2C, 0x96, 0xE4, 0xCF, 0xD6, 0x5A, 0xFB, 0xDA,
+ 0x1A, 0xC6, 0xEF, 0x35, 0x26, 0x6E, 0xE4, 0xB3,
+ 0xAB, 0x1F, 0xB0, 0x3A, 0xBA, 0xDD, 0xFD, 0xD4,
+ 0x03, 0xFF, 0xFC, 0xAF, 0xB4, 0xAD, 0xE0, 0xE9,
+ 0x2D, 0xA3, 0x82, 0xDA, 0x8C, 0x40, 0x22, 0x2E,
+ 0x10, 0xE9, 0xFD, 0xE8, 0x56, 0xC5, 0x1B, 0xDA,
+ 0xCD, 0xE7, 0x41, 0xA6, 0x49, 0xF7, 0x33, 0x5D
+ },
+ {
+ 0x48, 0x8C, 0x0D, 0x65, 0x2E, 0x42, 0xFD, 0x78,
+ 0xAB, 0x3A, 0x2D, 0xC2, 0x8C, 0xF3, 0xEB, 0x35,
+ 0xFC, 0xDD, 0xC8, 0xDE, 0xF7, 0xEA, 0xD4, 0x81,
+ 0x7B, 0xFF, 0xB6, 0x4C, 0x1A, 0xE0, 0xF2, 0x08,
+ 0xF7, 0x8C, 0xF4, 0x09, 0x76, 0xF7, 0xE2, 0xA2,
+ 0xCB, 0x2D, 0xD3, 0x0F, 0x1C, 0x99, 0x13, 0x02,
+ 0x08, 0xCE, 0xB6, 0x92, 0xC6, 0x68, 0x80, 0xD9,
+ 0x52, 0x8C, 0xD6, 0xD3, 0x8A, 0xD2, 0x9D, 0xB2
+ },
+ {
+ 0x51, 0x5B, 0x65, 0xBF, 0x65, 0x68, 0x83, 0x99,
+ 0x57, 0x5F, 0x0E, 0x06, 0x77, 0xBB, 0x6A, 0x91,
+ 0x9B, 0x66, 0x33, 0x55, 0x46, 0xD6, 0xCA, 0xE3,
+ 0x36, 0xF5, 0xC6, 0xFE, 0xAE, 0x5E, 0x2B, 0xF7,
+ 0x45, 0xE3, 0xA7, 0xB1, 0x3C, 0x32, 0x05, 0xDD,
+ 0x8B, 0x5B, 0x92, 0xCF, 0x05, 0x3B, 0xE9, 0x69,
+ 0xDF, 0x71, 0x20, 0xFC, 0xEF, 0x77, 0xE3, 0x89,
+ 0x5F, 0x56, 0x0F, 0xD2, 0x32, 0xFB, 0x89, 0x50
+ },
+ {
+ 0x3F, 0xDB, 0xC7, 0xD6, 0x9F, 0x4B, 0x53, 0xC2,
+ 0x25, 0x66, 0x3D, 0xA3, 0x0D, 0x80, 0xF7, 0x2E,
+ 0x54, 0x28, 0x10, 0x44, 0xA2, 0x2B, 0x98, 0x82,
+ 0xC6, 0x63, 0x8F, 0x55, 0x26, 0x83, 0x4B, 0xD3,
+ 0x16, 0x01, 0xCA, 0x5E, 0xB2, 0xCC, 0xA4, 0xF5,
+ 0xFF, 0xCF, 0x67, 0x5D, 0xCB, 0xCF, 0xCA, 0x60,
+ 0xC8, 0xA3, 0x61, 0x2D, 0x1A, 0xA9, 0xDA, 0xB6,
+ 0x93, 0xB2, 0x35, 0x60, 0x69, 0x60, 0x3A, 0x0E
+ },
+ {
+ 0x4F, 0xF6, 0xC3, 0x1A, 0x8F, 0xC0, 0x01, 0xAC,
+ 0x3B, 0x7A, 0xE0, 0x20, 0xC5, 0xF7, 0xC4, 0x5E,
+ 0xFB, 0x62, 0x71, 0xA2, 0xD7, 0xCC, 0xAB, 0x87,
+ 0x13, 0xE5, 0x48, 0xB7, 0x29, 0xF0, 0xFF, 0xF9,
+ 0xC8, 0x2F, 0xD4, 0xDB, 0x5C, 0xF6, 0x56, 0x43,
+ 0xD4, 0x07, 0x6A, 0x3F, 0xB1, 0x7B, 0x3E, 0x89,
+ 0x3C, 0x30, 0x2D, 0xC7, 0x5B, 0x61, 0x22, 0xFF,
+ 0x86, 0x81, 0xD0, 0x37, 0x12, 0x0E, 0x27, 0x6A
+ },
+ {
+ 0x43, 0xDF, 0xF2, 0x60, 0xDF, 0xEF, 0x1C, 0xB2,
+ 0xD6, 0x16, 0x00, 0xE2, 0x40, 0xAA, 0xD6, 0xB7,
+ 0x20, 0xE5, 0xF4, 0xF8, 0x30, 0x86, 0xE2, 0x6A,
+ 0x49, 0xA0, 0xCE, 0x3E, 0x0C, 0xA4, 0x4B, 0x9A,
+ 0x60, 0xFC, 0xF4, 0x6A, 0x8C, 0x3F, 0x1B, 0xB1,
+ 0xA6, 0xF5, 0x76, 0x2B, 0x66, 0x51, 0x3F, 0xE3,
+ 0xF7, 0xC5, 0xB0, 0xBC, 0x15, 0x0C, 0x08, 0x49,
+ 0x1A, 0xCB, 0xC4, 0x36, 0x1C, 0xAB, 0xCF, 0xDF
+ },
+ {
+ 0xB4, 0xDE, 0xA9, 0x4C, 0x9D, 0x36, 0x75, 0xBE,
+ 0x05, 0x12, 0xEF, 0xDE, 0xA8, 0x16, 0x38, 0x70,
+ 0xFE, 0x34, 0x25, 0xDC, 0xD7, 0x61, 0xF3, 0x63,
+ 0xC4, 0x3A, 0x0C, 0xA5, 0x71, 0x6B, 0x76, 0x54,
+ 0x06, 0x63, 0xFB, 0x2B, 0xE4, 0x9E, 0x2D, 0xB1,
+ 0x06, 0x48, 0x5C, 0x9C, 0xDD, 0x3C, 0x16, 0x48,
+ 0x98, 0xA9, 0x54, 0xB5, 0x87, 0x48, 0xC4, 0x2F,
+ 0xEA, 0x16, 0xA4, 0x0F, 0xC4, 0x53, 0xD2, 0x10
+ },
+ {
+ 0xE5, 0x27, 0x7B, 0x6F, 0x93, 0xEA, 0x1D, 0xE3,
+ 0xE2, 0xD9, 0xFC, 0xD8, 0xC6, 0x79, 0x79, 0x3C,
+ 0x6C, 0xCB, 0x8A, 0x3B, 0xE2, 0x6E, 0x8E, 0x31,
+ 0x14, 0xF3, 0x5D, 0xA4, 0xF2, 0xAC, 0x01, 0x4F,
+ 0x55, 0xC2, 0xF1, 0x5E, 0x09, 0xE9, 0x4A, 0xA0,
+ 0x71, 0x29, 0x81, 0x67, 0xA2, 0xFB, 0x9B, 0xE3,
+ 0x11, 0x70, 0x1F, 0xFB, 0xA9, 0xD3, 0xEE, 0xFF,
+ 0x8F, 0xFC, 0x79, 0x93, 0xA3, 0xCE, 0xCE, 0x18
+ },
+ {
+ 0xF0, 0x95, 0xA7, 0xC6, 0xE2, 0xB9, 0x16, 0x64,
+ 0x73, 0x4F, 0x3E, 0x23, 0xF1, 0x8E, 0xB2, 0xBA,
+ 0x9B, 0x00, 0xE7, 0x1F, 0xBF, 0xCB, 0x99, 0x31,
+ 0xC0, 0xA6, 0x14, 0x79, 0x2A, 0x9D, 0x86, 0x75,
+ 0x62, 0x2A, 0x87, 0x4C, 0x1B, 0xF5, 0x24, 0x1A,
+ 0x2A, 0x87, 0x41, 0xED, 0x1C, 0x89, 0x3B, 0xDF,
+ 0xA8, 0xE2, 0x8C, 0x2E, 0x20, 0xBB, 0x1C, 0x58,
+ 0xEB, 0x4D, 0xE7, 0xD8, 0x01, 0x11, 0x6C, 0x78
+ },
+ {
+ 0xDF, 0xA1, 0xFD, 0x80, 0x3A, 0x1D, 0x4A, 0x3E,
+ 0x66, 0x1D, 0xF0, 0x1F, 0x49, 0x43, 0xEA, 0x66,
+ 0x26, 0x0A, 0x18, 0xFE, 0xCE, 0x13, 0x4D, 0x62,
+ 0xF9, 0x7D, 0xAC, 0xDB, 0x8B, 0x3B, 0xF9, 0xC8,
+ 0x00, 0xAF, 0xE5, 0x79, 0xCF, 0xD1, 0x3F, 0xC0,
+ 0x14, 0x8B, 0xDE, 0xFB, 0xFF, 0x4E, 0x76, 0x83,
+ 0x56, 0x1C, 0x06, 0xA6, 0xF7, 0x22, 0x5E, 0x47,
+ 0x81, 0x99, 0x3B, 0x4F, 0x4F, 0x2B, 0xCB, 0xFA
+ },
+ {
+ 0x2B, 0x86, 0xCE, 0xB2, 0x70, 0xF6, 0x90, 0x8D,
+ 0x8B, 0x16, 0x00, 0x75, 0xEA, 0x7F, 0x57, 0x16,
+ 0x3A, 0xF5, 0xD5, 0xC6, 0xF8, 0xAA, 0xC5, 0x20,
+ 0x40, 0xCC, 0x68, 0x7C, 0x17, 0xAB, 0xF3, 0xC7,
+ 0x78, 0xC1, 0x39, 0x06, 0xE0, 0xE6, 0xF2, 0x9A,
+ 0x6A, 0xB1, 0x23, 0xDE, 0xEB, 0xCE, 0x39, 0x1F,
+ 0x90, 0x7D, 0x75, 0xD3, 0xA2, 0xCE, 0xFA, 0x0E,
+ 0xFC, 0xB8, 0x80, 0xA0, 0xE7, 0x0D, 0x71, 0x96
+ },
+ {
+ 0x32, 0x46, 0x6B, 0xCB, 0xDE, 0xD5, 0x38, 0xE5,
+ 0x68, 0x79, 0x54, 0x30, 0x35, 0x25, 0x36, 0xFE,
+ 0xB9, 0x19, 0xBF, 0x4D, 0x97, 0xCC, 0x44, 0xAB,
+ 0x1D, 0x80, 0x50, 0x40, 0xF4, 0xBC, 0x4C, 0x2E,
+ 0x79, 0x52, 0x72, 0x10, 0x18, 0x95, 0x8B, 0x4E,
+ 0xE7, 0x83, 0x03, 0x59, 0x0E, 0xF6, 0xAC, 0x45,
+ 0x0D, 0xF9, 0x2E, 0xC7, 0x7F, 0x47, 0x70, 0x54,
+ 0xBF, 0xF8, 0x67, 0xB8, 0x89, 0x71, 0xD4, 0x21
+ },
+ {
+ 0xEA, 0x64, 0xB0, 0x03, 0xA1, 0x35, 0x76, 0x61,
+ 0x21, 0xCF, 0xBC, 0xCB, 0xDC, 0x08, 0xDC, 0xA2,
+ 0x40, 0x29, 0x26, 0xBE, 0x78, 0xCE, 0xA3, 0xD0,
+ 0xA7, 0x25, 0x3D, 0x9E, 0xC9, 0xE6, 0x3B, 0x8A,
+ 0xCD, 0xD9, 0x94, 0x55, 0x99, 0x17, 0xE0, 0xE0,
+ 0x3B, 0x5E, 0x15, 0x5F, 0x94, 0x4D, 0x71, 0x98,
+ 0xD9, 0x92, 0x45, 0xA7, 0x94, 0xCE, 0x19, 0xC9,
+ 0xB4, 0xDF, 0x4D, 0xA4, 0xA3, 0x39, 0x93, 0x34
+ },
+ {
+ 0x05, 0xAD, 0x0F, 0x27, 0x1F, 0xAF, 0x7E, 0x36,
+ 0x13, 0x20, 0x51, 0x84, 0x52, 0x81, 0x3F, 0xF9,
+ 0xFB, 0x99, 0x76, 0xAC, 0x37, 0x80, 0x50, 0xB6,
+ 0xEE, 0xFB, 0x05, 0xF7, 0x86, 0x7B, 0x57, 0x7B,
+ 0x8F, 0x14, 0x47, 0x57, 0x94, 0xCF, 0xF6, 0x1B,
+ 0x2B, 0xC0, 0x62, 0xD3, 0x46, 0xA7, 0xC6, 0x5C,
+ 0x6E, 0x00, 0x67, 0xC6, 0x0A, 0x37, 0x4A, 0xF7,
+ 0x94, 0x0F, 0x10, 0xAA, 0x44, 0x9D, 0x5F, 0xB9
+ },
+ {
+ 0xB5, 0x45, 0x88, 0x02, 0x94, 0xAF, 0xA1, 0x53,
+ 0xF8, 0xB9, 0xF4, 0x9C, 0x73, 0xD9, 0x52, 0xB5,
+ 0xD1, 0x22, 0x8F, 0x1A, 0x1A, 0xB5, 0xEB, 0xCB,
+ 0x05, 0xFF, 0x79, 0xE5, 0x60, 0xC0, 0x30, 0xF7,
+ 0x50, 0x0F, 0xE2, 0x56, 0xA4, 0x0B, 0x6A, 0x0E,
+ 0x6C, 0xB3, 0xD4, 0x2A, 0xCD, 0x4B, 0x98, 0x59,
+ 0x5C, 0x5B, 0x51, 0xEA, 0xEC, 0x5A, 0xD6, 0x9C,
+ 0xD4, 0x0F, 0x1F, 0xC1, 0x6D, 0x2D, 0x5F, 0x50
+ },
+ {
+ 0xBB, 0xFB, 0x94, 0x77, 0xEC, 0x6A, 0x9F, 0x0C,
+ 0x25, 0x40, 0x5A, 0xCD, 0x8A, 0x30, 0xD5, 0xDD,
+ 0x7C, 0x73, 0x57, 0x1F, 0x1D, 0x1A, 0x6E, 0x8C,
+ 0xE7, 0x2F, 0x8B, 0x9C, 0x94, 0x1C, 0xF7, 0x79,
+ 0xB7, 0x64, 0x03, 0xAC, 0x7F, 0x04, 0x50, 0x05,
+ 0x25, 0x84, 0x39, 0x0A, 0x14, 0xEA, 0xA3, 0x7C,
+ 0x20, 0xB5, 0xBD, 0xB0, 0x38, 0x10, 0x54, 0xA9,
+ 0xA4, 0x95, 0x34, 0xF8, 0x14, 0x66, 0xBA, 0x9D
+ },
+ {
+ 0xC8, 0x28, 0x7E, 0x93, 0x3D, 0x95, 0x04, 0xBF,
+ 0xFD, 0x7B, 0xE2, 0xAC, 0x02, 0x2B, 0x32, 0xF3,
+ 0xF4, 0x6D, 0x87, 0xA7, 0xA0, 0xE7, 0x9B, 0xB2,
+ 0xA1, 0xCB, 0xAA, 0xCC, 0x2E, 0x84, 0xCD, 0x70,
+ 0x84, 0x5D, 0x0D, 0x42, 0x78, 0x48, 0xA6, 0xD7,
+ 0x88, 0xD3, 0x96, 0x22, 0xE1, 0x0F, 0x43, 0x42,
+ 0x23, 0x7E, 0xEF, 0xA6, 0xD3, 0xC0, 0x12, 0xDA,
+ 0xE9, 0x6C, 0xC8, 0xA6, 0x50, 0xCC, 0x2E, 0x30
+ },
+ {
+ 0xC4, 0x59, 0x6F, 0xCB, 0x0A, 0x28, 0xD2, 0x4A,
+ 0xAD, 0x70, 0xCF, 0x18, 0x53, 0xEC, 0x29, 0xDA,
+ 0xC0, 0xFB, 0x20, 0x2D, 0x8E, 0xC1, 0x40, 0xDA,
+ 0x30, 0x00, 0x88, 0xBB, 0x85, 0xB9, 0x2C, 0x30,
+ 0x29, 0x19, 0x46, 0xAD, 0x30, 0x7C, 0x09, 0x6E,
+ 0x3B, 0x28, 0x66, 0x33, 0x5C, 0x93, 0x17, 0xAF,
+ 0xE2, 0x8C, 0xAD, 0xAB, 0x5D, 0x62, 0xC3, 0x54,
+ 0x32, 0x9C, 0x98, 0xD9, 0x93, 0xC5, 0xBE, 0x1C
+ },
+ {
+ 0xE8, 0x8C, 0x38, 0xE6, 0x7E, 0x8D, 0x19, 0x83,
+ 0x58, 0x08, 0x85, 0x46, 0x70, 0x77, 0x9E, 0xCA,
+ 0x60, 0xBA, 0xD8, 0x54, 0xC5, 0x77, 0x87, 0x90,
+ 0xA0, 0x72, 0x54, 0xA3, 0x0A, 0x14, 0xAE, 0x82,
+ 0xB6, 0x1B, 0xB1, 0x69, 0x11, 0xFE, 0x57, 0x77,
+ 0x1D, 0x19, 0xE9, 0xB7, 0xF5, 0x02, 0x3C, 0x0D,
+ 0x4E, 0x8A, 0x8D, 0x37, 0x2E, 0x3D, 0x85, 0xE4,
+ 0x3B, 0x03, 0xE5, 0xE0, 0x0E, 0x6E, 0xBA, 0x4B
+ },
+ {
+ 0x2D, 0x66, 0x3E, 0x03, 0xE6, 0xF3, 0x55, 0x2C,
+ 0xCD, 0xFB, 0xA4, 0x96, 0xA1, 0x4C, 0xC6, 0x22,
+ 0x4C, 0xEB, 0x1E, 0xB6, 0x1A, 0xA2, 0x65, 0xE6,
+ 0xA7, 0xD4, 0xA2, 0x6E, 0x54, 0x10, 0x61, 0x04,
+ 0xA9, 0x6E, 0x33, 0x09, 0x59, 0xF9, 0x71, 0x3B,
+ 0x34, 0x87, 0xC1, 0xB9, 0x49, 0x7C, 0xCF, 0x82,
+ 0x61, 0x1D, 0xBF, 0xA3, 0x4F, 0xF1, 0x1D, 0x31,
+ 0x33, 0xB5, 0xB5, 0xD1, 0xF1, 0xE4, 0xF8, 0xD0
+ },
+ {
+ 0x70, 0x7D, 0x6A, 0x58, 0x42, 0x1B, 0x8F, 0x7E,
+ 0x44, 0xFF, 0x1F, 0x83, 0x62, 0xBC, 0x70, 0x0F,
+ 0x71, 0xEF, 0x7C, 0x39, 0x35, 0xE0, 0x76, 0x4B,
+ 0xD1, 0x4D, 0x39, 0x0C, 0x1C, 0x72, 0x79, 0x2A,
+ 0xF9, 0xC2, 0xC0, 0x2F, 0xB7, 0x2A, 0x2B, 0x9D,
+ 0x9A, 0x07, 0x29, 0xCB, 0x3E, 0x99, 0x62, 0x6C,
+ 0xF0, 0x34, 0xDF, 0x54, 0xB5, 0x06, 0xB5, 0xB1,
+ 0x64, 0x64, 0xF4, 0x75, 0x86, 0x4F, 0x25, 0x90
+ },
+ {
+ 0x9D, 0x88, 0xF8, 0xBA, 0xA4, 0xEB, 0x0F, 0x9A,
+ 0xB2, 0x29, 0x2E, 0x49, 0x82, 0xAC, 0x80, 0x44,
+ 0x53, 0x58, 0x22, 0x7D, 0x7F, 0x9C, 0xE7, 0xA4,
+ 0xA6, 0x29, 0xF1, 0x80, 0xF7, 0x14, 0x1E, 0x08,
+ 0xFE, 0x63, 0x55, 0xC6, 0x45, 0x21, 0xA6, 0x9B,
+ 0xA2, 0xBF, 0xBD, 0x1C, 0x4A, 0x3E, 0xA0, 0x48,
+ 0xD0, 0xBC, 0x8A, 0xB3, 0x70, 0x1F, 0x30, 0xEA,
+ 0x83, 0xFB, 0xE0, 0x24, 0x74, 0xD8, 0x92, 0xBF
+ },
+ {
+ 0x65, 0xEA, 0x4D, 0xB0, 0x4A, 0x75, 0x81, 0xC1,
+ 0x81, 0x94, 0xA8, 0x92, 0x1A, 0xFD, 0xFA, 0x4F,
+ 0x8D, 0x9A, 0xF6, 0x29, 0xDE, 0xD2, 0x77, 0x2C,
+ 0x65, 0x8E, 0x08, 0x48, 0x5F, 0x67, 0xAD, 0x2C,
+ 0xE2, 0x1A, 0x98, 0xCD, 0x29, 0x3F, 0xF2, 0x8D,
+ 0x4D, 0xFC, 0xDF, 0x65, 0x8C, 0xDC, 0x7A, 0xE6,
+ 0x70, 0x27, 0x84, 0x8E, 0x71, 0xCC, 0xC1, 0x15,
+ 0xA3, 0xFF, 0xBA, 0xC4, 0xFA, 0x61, 0xBB, 0x73
+ },
+ {
+ 0x0B, 0x4A, 0x68, 0x92, 0x9E, 0x7F, 0x15, 0xCA,
+ 0x91, 0xBB, 0x44, 0x39, 0xF2, 0x40, 0x37, 0x02,
+ 0x03, 0x4C, 0xD4, 0x74, 0x8E, 0x46, 0x92, 0x7A,
+ 0xBA, 0x95, 0xCB, 0xEF, 0x80, 0x04, 0x8B, 0x25,
+ 0xA6, 0x75, 0x97, 0x0F, 0xAC, 0x33, 0xC8, 0x74,
+ 0xAB, 0xD3, 0xD8, 0x3A, 0xA0, 0xF3, 0x7B, 0xE2,
+ 0x30, 0x83, 0x10, 0xE8, 0xDD, 0x79, 0x4F, 0x81,
+ 0x92, 0x93, 0x0E, 0xD5, 0x6E, 0x70, 0xA8, 0xE4
+ },
+ {
+ 0xC1, 0xC5, 0xD8, 0xAC, 0xFE, 0x3F, 0xDE, 0x67,
+ 0x4E, 0xDD, 0x36, 0x20, 0x15, 0x7A, 0x8B, 0x6B,
+ 0x4C, 0x8E, 0x67, 0xC6, 0xA7, 0xA9, 0x72, 0x67,
+ 0x41, 0xD9, 0xC3, 0x05, 0xE2, 0xA5, 0x2A, 0x87,
+ 0x97, 0xFD, 0xA0, 0xB2, 0xF1, 0x3A, 0xC7, 0x87,
+ 0x34, 0xDB, 0x2F, 0x4F, 0xC8, 0x3E, 0xF3, 0x24,
+ 0x14, 0xD9, 0x31, 0xEB, 0xAE, 0xAE, 0xCD, 0x82,
+ 0x6D, 0x7C, 0x2B, 0xE2, 0x03, 0xBD, 0xC2, 0xD1
+ },
+ {
+ 0x2D, 0xAD, 0xC8, 0xC9, 0xF7, 0x42, 0x5A, 0x01,
+ 0x14, 0x49, 0x12, 0x87, 0xBD, 0xC6, 0x8E, 0xAE,
+ 0x4F, 0xB6, 0x19, 0x4D, 0x1A, 0x10, 0x9D, 0xB9,
+ 0xB6, 0xE8, 0xA2, 0xAC, 0x94, 0xD4, 0xE4, 0x40,
+ 0x90, 0x99, 0x85, 0xC4, 0x29, 0x1F, 0xE8, 0x9F,
+ 0xD8, 0x28, 0x1F, 0x8F, 0xCE, 0xF6, 0xF6, 0xBC,
+ 0x32, 0x55, 0x0E, 0x53, 0xCB, 0x7A, 0x49, 0x42,
+ 0x89, 0x81, 0xE8, 0xD5, 0x3C, 0xF5, 0xA2, 0x12
+ },
+ {
+ 0xE5, 0x55, 0xF2, 0xA5, 0x8A, 0xCA, 0xC5, 0x50,
+ 0x3F, 0x9E, 0x2D, 0x97, 0xB2, 0x46, 0x87, 0x2B,
+ 0x4C, 0xA7, 0x8B, 0xD5, 0x6D, 0x47, 0xB7, 0x65,
+ 0xF0, 0x52, 0xAA, 0xB3, 0xDC, 0x77, 0xDB, 0xE9,
+ 0x93, 0x93, 0x6F, 0x22, 0x52, 0xF0, 0xAB, 0x2E,
+ 0x01, 0xFB, 0x08, 0x74, 0x72, 0xCC, 0xB5, 0xA1,
+ 0x21, 0xDD, 0xFF, 0xDE, 0x53, 0x1D, 0x3D, 0xC4,
+ 0x02, 0x2A, 0x7D, 0x19, 0x56, 0xCE, 0x0E, 0x20
+ },
+ {
+ 0x9B, 0x4E, 0xAE, 0x12, 0x95, 0x00, 0x0A, 0xEA,
+ 0x79, 0x83, 0xEC, 0x3B, 0xCB, 0x48, 0x57, 0xCC,
+ 0x71, 0x25, 0xFD, 0x73, 0x06, 0x78, 0x7C, 0x63,
+ 0x13, 0x24, 0x73, 0xCF, 0xE8, 0xF4, 0xEB, 0x45,
+ 0x31, 0x8A, 0x60, 0xDA, 0xAD, 0x64, 0x6D, 0x63,
+ 0xA2, 0x7C, 0x4B, 0x9D, 0x1F, 0x50, 0x73, 0x70,
+ 0x0A, 0x30, 0x57, 0xDE, 0x22, 0xA7, 0xFD, 0xF0,
+ 0x9A, 0x87, 0xAA, 0xC6, 0x6E, 0xBE, 0x47, 0x58
+ },
+ {
+ 0x96, 0x64, 0xAC, 0xC2, 0xDC, 0x72, 0x98, 0xB9,
+ 0x86, 0x8D, 0xB4, 0x95, 0xEE, 0xBC, 0x6B, 0x59,
+ 0x65, 0x7D, 0x13, 0x9A, 0x6A, 0xF0, 0x60, 0xA7,
+ 0x2F, 0xB6, 0x91, 0x24, 0xBD, 0xD3, 0xA6, 0x59,
+ 0x18, 0x88, 0xF0, 0x35, 0x4F, 0x70, 0x2B, 0x1B,
+ 0x88, 0x86, 0x84, 0x41, 0x10, 0x58, 0xA3, 0x75,
+ 0x9F, 0x7F, 0xD3, 0x7F, 0x06, 0xEA, 0xFB, 0x3B,
+ 0x58, 0xEC, 0xF2, 0x6F, 0x45, 0x53, 0xBE, 0x27
+ },
+ {
+ 0xFC, 0x16, 0xE0, 0x92, 0x5A, 0x35, 0xAA, 0xD4,
+ 0x7A, 0xD6, 0x95, 0x54, 0xB2, 0x57, 0x96, 0xFC,
+ 0xF9, 0x26, 0x0C, 0xB5, 0x0E, 0x6C, 0xC3, 0x74,
+ 0x75, 0x35, 0x55, 0x9E, 0x99, 0xC8, 0x58, 0x81,
+ 0xC7, 0x58, 0x89, 0xAC, 0x79, 0x3A, 0xB7, 0x8B,
+ 0x88, 0xB0, 0x5F, 0xB1, 0x60, 0x89, 0x56, 0x55,
+ 0xE4, 0xD6, 0x63, 0xA2, 0xA0, 0x9B, 0xA9, 0xFA,
+ 0x61, 0x4A, 0x10, 0xC2, 0x29, 0x47, 0x21, 0x0D
+ },
+ {
+ 0x22, 0x5E, 0x73, 0x41, 0xF8, 0x57, 0x52, 0x4F,
+ 0x78, 0x90, 0x37, 0x6C, 0x50, 0xE6, 0x35, 0x4B,
+ 0x16, 0xC1, 0xCD, 0xFB, 0xF5, 0x8F, 0xE5, 0xF3,
+ 0xA4, 0x03, 0x94, 0x93, 0xB5, 0xDD, 0x40, 0x8D,
+ 0x79, 0xD4, 0x8C, 0x56, 0xE1, 0xF8, 0x9B, 0x68,
+ 0x7F, 0xBE, 0x33, 0x62, 0xA7, 0x7F, 0xA7, 0x5A,
+ 0x54, 0x37, 0x4B, 0x7A, 0x48, 0x5E, 0x91, 0xB1,
+ 0x89, 0xAF, 0x2E, 0x2F, 0x74, 0x9E, 0x2A, 0xDB
+ },
+ {
+ 0xA0, 0x7A, 0x4C, 0x02, 0x3A, 0xC7, 0x04, 0xCE,
+ 0x7C, 0x09, 0xDD, 0x6C, 0x92, 0xC6, 0xF1, 0x84,
+ 0xF5, 0x3E, 0x8D, 0xD9, 0x6F, 0xE3, 0xBE, 0x9E,
+ 0x93, 0xC3, 0x9C, 0x53, 0x44, 0x85, 0xB6, 0x4B,
+ 0x39, 0xD5, 0xBE, 0x7F, 0x7B, 0x71, 0x70, 0x60,
+ 0x4D, 0xE7, 0x7C, 0xE5, 0xA4, 0x37, 0xA9, 0x8E,
+ 0x71, 0x2C, 0xC4, 0x4F, 0x19, 0xE2, 0x1D, 0x41,
+ 0xF0, 0xE6, 0xE3, 0xEC, 0x1E, 0x00, 0xAC, 0x55
+ },
+ {
+ 0x62, 0x85, 0x84, 0x63, 0x58, 0x2D, 0x22, 0xE6,
+ 0x8E, 0x52, 0x27, 0xBF, 0xBA, 0xB5, 0x40, 0x04,
+ 0x8F, 0x65, 0xED, 0xD6, 0xA6, 0x75, 0x5F, 0x6F,
+ 0xAB, 0x53, 0xC0, 0x25, 0xB6, 0x63, 0xCA, 0x37,
+ 0x7A, 0x0E, 0xD5, 0xEF, 0xD6, 0xAF, 0x16, 0x6C,
+ 0xA5, 0x5A, 0x9C, 0x73, 0x3F, 0xCA, 0x80, 0x5A,
+ 0xC4, 0xE4, 0x09, 0xCA, 0x56, 0x17, 0x7A, 0xA7,
+ 0x49, 0x40, 0xDB, 0x9F, 0x40, 0xC3, 0xB9, 0xFF
+ },
+ {
+ 0xA1, 0xAC, 0x53, 0x9D, 0x1A, 0xBB, 0xC2, 0xB0,
+ 0x96, 0xFF, 0xAB, 0x81, 0x3B, 0x64, 0x45, 0x7F,
+ 0xE6, 0xEB, 0x3B, 0x50, 0xFC, 0xD8, 0x89, 0x53,
+ 0xD0, 0xCD, 0x9F, 0x65, 0x02, 0xF6, 0x89, 0x62,
+ 0x0A, 0xD4, 0x42, 0xB5, 0x51, 0x70, 0x90, 0xB5,
+ 0x0C, 0xFF, 0xB9, 0x58, 0x86, 0x6D, 0x7C, 0x16,
+ 0x1D, 0x8A, 0x7D, 0x75, 0x60, 0xC8, 0x93, 0xE1,
+ 0xDE, 0xF6, 0xAE, 0xC4, 0x37, 0xAD, 0x6D, 0x06
+ },
+ {
+ 0xB5, 0x86, 0xB7, 0x5D, 0xA7, 0x0F, 0x6C, 0xC0,
+ 0x62, 0x7E, 0xF3, 0xCF, 0x12, 0x37, 0xC9, 0x4B,
+ 0x12, 0xD0, 0xF7, 0x4D, 0xCB, 0xA2, 0x6A, 0x9E,
+ 0x7C, 0x7B, 0xC6, 0xC2, 0x1A, 0x33, 0x53, 0x37,
+ 0xBF, 0x9F, 0x5B, 0x83, 0x0C, 0x63, 0x24, 0xAF,
+ 0xA6, 0xEF, 0x64, 0x9E, 0x95, 0xAF, 0x87, 0x90,
+ 0x87, 0x52, 0x34, 0xC6, 0xE6, 0x61, 0xD3, 0xF5,
+ 0xE9, 0x8C, 0xA0, 0x12, 0xAE, 0x81, 0x48, 0x8A
+ },
+ {
+ 0x56, 0x68, 0xA2, 0x98, 0x21, 0x37, 0xCB, 0xC6,
+ 0x22, 0xEF, 0x8D, 0x06, 0xCF, 0x4E, 0x86, 0x16,
+ 0x8C, 0xDD, 0x4A, 0x89, 0x9C, 0xD4, 0x46, 0x2A,
+ 0xF6, 0xC3, 0xD4, 0x15, 0x42, 0x61, 0x56, 0xA5,
+ 0xD8, 0xDD, 0x67, 0xC9, 0x60, 0x4F, 0x31, 0xB5,
+ 0x7D, 0x6C, 0x9D, 0x59, 0x72, 0x50, 0x45, 0x7E,
+ 0x4A, 0xB5, 0x2A, 0x58, 0x11, 0x55, 0x42, 0xAC,
+ 0xF2, 0x7F, 0x92, 0x59, 0x30, 0xF6, 0xA1, 0x12
+ },
+ {
+ 0xF2, 0xB1, 0xBD, 0x16, 0xD8, 0x8E, 0x37, 0xF3,
+ 0xA5, 0x18, 0xD1, 0x93, 0xED, 0x06, 0x1A, 0x1D,
+ 0xF7, 0xB4, 0x43, 0xA1, 0x8C, 0xE9, 0xF8, 0x44,
+ 0x45, 0xEF, 0x86, 0xEF, 0xFB, 0xDF, 0xF1, 0x60,
+ 0x55, 0x02, 0x3C, 0xD4, 0xE7, 0x8D, 0x03, 0x4D,
+ 0xE4, 0x03, 0x2A, 0x77, 0xDD, 0xC1, 0xD3, 0x43,
+ 0x52, 0xFE, 0x61, 0x7F, 0x82, 0x56, 0x24, 0x45,
+ 0x9B, 0xC3, 0x26, 0x9F, 0x70, 0x4F, 0x34, 0x5B
+ },
+ {
+ 0xF0, 0x85, 0xF3, 0xD8, 0xBD, 0x13, 0x8E, 0x05,
+ 0x69, 0x24, 0x3F, 0x74, 0x52, 0x3E, 0x87, 0xFF,
+ 0x37, 0x6F, 0x04, 0xEA, 0xBD, 0x5A, 0x2F, 0x6E,
+ 0x53, 0xDF, 0x38, 0x99, 0x00, 0x0E, 0x2E, 0x94,
+ 0xAF, 0x0D, 0x2B, 0xC7, 0x1C, 0x3F, 0x71, 0x10,
+ 0x25, 0xC5, 0x38, 0xA6, 0xC8, 0xB1, 0x0B, 0x09,
+ 0x04, 0xDF, 0xC3, 0x46, 0xAD, 0xAD, 0x7E, 0xF3,
+ 0x6B, 0x1A, 0xE8, 0x8A, 0x6C, 0xFE, 0xAB, 0xBD
+ },
+ {
+ 0x82, 0x91, 0xA4, 0xAF, 0xD2, 0xE4, 0xB7, 0x16,
+ 0x61, 0x77, 0x3A, 0x46, 0xB3, 0xD4, 0x45, 0x5A,
+ 0x8D, 0x33, 0xA7, 0x26, 0xD9, 0xD3, 0x87, 0x30,
+ 0x83, 0xAB, 0x33, 0x70, 0x20, 0xC2, 0x7B, 0x4D,
+ 0xD6, 0x43, 0xE2, 0x8C, 0x2F, 0xE4, 0x7A, 0xB2,
+ 0xFB, 0xF5, 0xD1, 0x40, 0x81, 0xA3, 0xFC, 0x1C,
+ 0x83, 0x9B, 0x12, 0xEA, 0x31, 0xD1, 0x3C, 0xF4,
+ 0x9E, 0xEE, 0x97, 0xEF, 0x2E, 0xD7, 0xFA, 0x3E
+ },
+ {
+ 0xB1, 0x26, 0xAE, 0x46, 0xA7, 0xA4, 0x59, 0x5E,
+ 0x31, 0x60, 0x7E, 0xF8, 0x07, 0xA5, 0x60, 0x1F,
+ 0x4E, 0xCD, 0x9E, 0x7D, 0x66, 0xC8, 0x2D, 0xAE,
+ 0xB9, 0x71, 0x5F, 0x8D, 0xA1, 0xC1, 0x7D, 0x7D,
+ 0x71, 0xC3, 0xE6, 0x82, 0x50, 0xC9, 0xDC, 0x01,
+ 0xAC, 0x40, 0xA3, 0x6D, 0x2E, 0x63, 0x8B, 0xEF,
+ 0x3D, 0x7B, 0xC7, 0x0E, 0xA2, 0xD0, 0xE3, 0x31,
+ 0xE3, 0xD3, 0x3E, 0x17, 0x04, 0xEB, 0xA9, 0x2D
+ },
+ {
+ 0x63, 0xB1, 0x4D, 0x8E, 0xD2, 0x47, 0x9C, 0xAA,
+ 0x17, 0xC3, 0xE4, 0xCF, 0x20, 0x3B, 0x23, 0x3A,
+ 0x7E, 0x37, 0x3E, 0xDB, 0x0C, 0x2F, 0x19, 0x71,
+ 0x29, 0xA9, 0xA3, 0x6C, 0x5B, 0x3E, 0x1F, 0x38,
+ 0x38, 0xF2, 0xE8, 0x2A, 0xC2, 0xC2, 0xAD, 0x9D,
+ 0x52, 0xB3, 0x35, 0x79, 0x0B, 0xFF, 0x57, 0x73,
+ 0x04, 0xA3, 0x78, 0xE3, 0x8E, 0xB6, 0xBB, 0x41,
+ 0x62, 0x03, 0x0C, 0xE2, 0xA8, 0xBA, 0x29, 0x3C
+ },
+ {
+ 0x34, 0x42, 0x2A, 0x32, 0x29, 0x66, 0x99, 0x28,
+ 0xC4, 0x90, 0xF5, 0x7B, 0x8E, 0x76, 0x88, 0x52,
+ 0xE5, 0xB7, 0xC0, 0x0D, 0xCA, 0xD6, 0x0B, 0x01,
+ 0x2A, 0x5D, 0xB3, 0x9A, 0x2D, 0x59, 0x7C, 0x3D,
+ 0x0A, 0x63, 0xBE, 0x6A, 0x26, 0x3E, 0xA5, 0x36,
+ 0x08, 0xB7, 0x06, 0x92, 0xD7, 0x8E, 0x1B, 0x42,
+ 0x7E, 0xAC, 0xEC, 0x01, 0xF4, 0xBE, 0xE0, 0xBD,
+ 0xBB, 0x8F, 0x08, 0x81, 0x48, 0x8E, 0xFC, 0x28
+ },
+ {
+ 0xE2, 0x6B, 0x7E, 0xD6, 0xB9, 0x07, 0xB5, 0x4C,
+ 0xA2, 0x65, 0x67, 0xF1, 0x1E, 0xE5, 0xBB, 0x6D,
+ 0x73, 0x9A, 0x00, 0x08, 0xA5, 0x34, 0x37, 0xAD,
+ 0x75, 0x90, 0xA3, 0x13, 0x4C, 0xEB, 0x95, 0x19,
+ 0x6E, 0x49, 0xB3, 0x44, 0x3F, 0x32, 0x49, 0x22,
+ 0x51, 0x75, 0x23, 0xC0, 0xCD, 0x5A, 0x00, 0xD7,
+ 0x7E, 0x4C, 0x4D, 0xE7, 0xA0, 0xDE, 0x96, 0x8A,
+ 0x84, 0xFB, 0x1B, 0x3B, 0xE7, 0xB3, 0xB9, 0x63
+ },
+ {
+ 0x26, 0x01, 0x97, 0xCA, 0xFB, 0xF4, 0x56, 0xB4,
+ 0x11, 0xFA, 0x26, 0xD3, 0x83, 0xD6, 0x4D, 0x61,
+ 0xE8, 0x1E, 0x5E, 0x52, 0xF8, 0x4C, 0xD9, 0xD5,
+ 0x73, 0x86, 0xC7, 0x76, 0x23, 0x0C, 0x65, 0xA2,
+ 0x68, 0x1C, 0xD2, 0xFD, 0xFD, 0x28, 0x67, 0x9F,
+ 0x67, 0xFE, 0x1B, 0xD7, 0x46, 0x9C, 0xF7, 0x26,
+ 0x95, 0x85, 0xFC, 0xCB, 0xAE, 0xCC, 0x22, 0xF5,
+ 0x03, 0xD6, 0xE3, 0xFC, 0x39, 0x30, 0x14, 0x36
+ },
+ {
+ 0xCB, 0xD5, 0xAB, 0xE3, 0x7B, 0xCC, 0x4F, 0x9A,
+ 0x12, 0x70, 0xAD, 0xD0, 0xA5, 0x27, 0x0F, 0x42,
+ 0x83, 0x9C, 0x7D, 0x24, 0x93, 0x20, 0xD1, 0xF1,
+ 0xD8, 0x85, 0x53, 0xD0, 0x5F, 0xAF, 0x9A, 0x26,
+ 0x79, 0xF4, 0x9B, 0x49, 0xC9, 0xE2, 0x0C, 0x1C,
+ 0x85, 0xC6, 0x29, 0xAA, 0x0F, 0x09, 0x0C, 0xAE,
+ 0x8F, 0x6E, 0x32, 0xC6, 0xCA, 0xD7, 0x17, 0x21,
+ 0xFD, 0x06, 0x23, 0xE4, 0xED, 0x25, 0xB2, 0x56
+ },
+ {
+ 0x78, 0x0E, 0x31, 0x4F, 0xD6, 0x97, 0xD2, 0xA9,
+ 0x7D, 0x22, 0x1A, 0x22, 0xC3, 0x90, 0x11, 0xE2,
+ 0x50, 0x69, 0x16, 0x3C, 0xD0, 0x8F, 0x00, 0x70,
+ 0xD0, 0x67, 0xE8, 0xCD, 0xB0, 0xBC, 0x86, 0x73,
+ 0xFD, 0xB0, 0xEC, 0x4F, 0x46, 0xE3, 0x1D, 0x74,
+ 0x8C, 0xD3, 0xBB, 0x3D, 0x61, 0xB9, 0x01, 0x0A,
+ 0x66, 0x12, 0xF3, 0x41, 0xD4, 0x71, 0xD9, 0xC5,
+ 0xA2, 0xDE, 0x6B, 0x6D, 0xD5, 0x38, 0xA6, 0xB5
+ },
+ {
+ 0x40, 0x8F, 0x16, 0xCE, 0x86, 0xF8, 0x01, 0xD0,
+ 0x8B, 0xD0, 0x51, 0x36, 0x4B, 0x3E, 0xCD, 0x9A,
+ 0x39, 0x45, 0x71, 0x58, 0x88, 0xDF, 0x46, 0x63,
+ 0x21, 0x9A, 0x19, 0x0B, 0x35, 0x04, 0xE4, 0x61,
+ 0x8E, 0x7B, 0xF5, 0x51, 0x71, 0x17, 0x8B, 0x04,
+ 0x00, 0xFB, 0xEB, 0xFA, 0xA0, 0x1F, 0x6E, 0xEA,
+ 0xB5, 0x4F, 0xF5, 0xE3, 0x1E, 0x6D, 0x7A, 0x55,
+ 0xB8, 0x4A, 0xDB, 0x9E, 0x03, 0xDF, 0x48, 0x36
+ },
+ {
+ 0x0B, 0xF9, 0x88, 0x69, 0xEC, 0x05, 0x80, 0x19,
+ 0x9C, 0xA3, 0x70, 0x8E, 0xC9, 0xC4, 0x2C, 0x37,
+ 0x6C, 0x5C, 0x36, 0xE0, 0xFB, 0x74, 0x92, 0x42,
+ 0x57, 0x23, 0x98, 0xA0, 0xDA, 0x57, 0xF9, 0x8D,
+ 0x1C, 0x4C, 0xD2, 0x96, 0x3B, 0x37, 0xC3, 0xC6,
+ 0x5A, 0x10, 0xF1, 0x06, 0xB5, 0x6D, 0xCB, 0x96,
+ 0xDC, 0xDD, 0x32, 0x57, 0x96, 0x29, 0x7A, 0xDB,
+ 0xF6, 0xEE, 0x62, 0x70, 0xED, 0xD4, 0x59, 0x2A
+ },
+ {
+ 0x05, 0x2C, 0x32, 0x98, 0x43, 0x87, 0xB1, 0x93,
+ 0x0D, 0x3A, 0x96, 0xBE, 0x72, 0x36, 0x85, 0x35,
+ 0x44, 0x4F, 0x13, 0x07, 0x57, 0xBF, 0x87, 0xE0,
+ 0x76, 0x2D, 0x8B, 0x1C, 0x4F, 0x65, 0x70, 0xF4,
+ 0xDC, 0x67, 0x4C, 0x4E, 0x6F, 0x5E, 0x21, 0xAB,
+ 0xD0, 0xB3, 0x5E, 0x1C, 0xA1, 0x9D, 0xB8, 0x40,
+ 0x68, 0x8D, 0x1B, 0x6E, 0x9E, 0xC9, 0x1F, 0x37,
+ 0x30, 0xE8, 0xB2, 0x88, 0x0E, 0xC2, 0xC3, 0xDF
+ },
+ {
+ 0x4B, 0xB7, 0x14, 0x09, 0xC1, 0x5A, 0x0D, 0x39,
+ 0x32, 0xC5, 0x99, 0xEF, 0x0F, 0xF3, 0xEF, 0xF5,
+ 0xC7, 0x60, 0x2D, 0x70, 0x00, 0xCD, 0xA9, 0x74,
+ 0x08, 0x2C, 0x4A, 0x46, 0x82, 0x24, 0x9A, 0x19,
+ 0xD4, 0x3A, 0x5C, 0x14, 0xE0, 0xAE, 0xEF, 0x89,
+ 0x78, 0x21, 0x05, 0x63, 0x80, 0xAF, 0xF2, 0x75,
+ 0x20, 0x1D, 0x74, 0x59, 0x14, 0x84, 0x96, 0xEA,
+ 0xE9, 0x42, 0x0E, 0x71, 0x82, 0x88, 0xB4, 0x14
+ },
+ {
+ 0x47, 0x95, 0xB2, 0x51, 0xCC, 0x7B, 0x35, 0xE6,
+ 0x96, 0x92, 0xDB, 0x7F, 0xB4, 0x0E, 0xFD, 0x34,
+ 0xF2, 0x94, 0xF5, 0x1A, 0xEC, 0x15, 0xD6, 0xC8,
+ 0x67, 0x3E, 0x59, 0xF2, 0x04, 0xBE, 0xCF, 0x4C,
+ 0xF9, 0xDF, 0x84, 0x95, 0x23, 0xF1, 0xDB, 0x73,
+ 0xBE, 0x2A, 0x66, 0xC8, 0x39, 0xD8, 0x01, 0x97,
+ 0x4D, 0x43, 0x3B, 0x47, 0x80, 0x67, 0x01, 0xA1,
+ 0x63, 0xA7, 0x94, 0xB2, 0x6A, 0x84, 0x6B, 0x06
+ },
+ {
+ 0xDD, 0x50, 0xF9, 0x65, 0xB6, 0x0B, 0xAF, 0x16,
+ 0x8F, 0x5E, 0xA0, 0x5A, 0xC2, 0x0B, 0x8A, 0x78,
+ 0xF4, 0x47, 0x5C, 0x18, 0x61, 0x0B, 0x9D, 0x9F,
+ 0xC2, 0xB7, 0xC3, 0xAD, 0x5C, 0x6F, 0x97, 0xA4,
+ 0xCF, 0x5E, 0xA4, 0x8E, 0xE4, 0x0A, 0x3C, 0xA2,
+ 0x29, 0x3C, 0xC4, 0x21, 0x40, 0x82, 0xCF, 0x0F,
+ 0x8E, 0xC8, 0x95, 0x55, 0x32, 0x69, 0xE1, 0x4D,
+ 0xA9, 0xBD, 0x1A, 0x19, 0x65, 0x62, 0xCA, 0x59
+ },
+ {
+ 0xE0, 0xB5, 0x4B, 0x61, 0x7F, 0x44, 0x92, 0x2C,
+ 0x7F, 0x61, 0xC6, 0xA5, 0x4C, 0x98, 0xC6, 0x1E,
+ 0x93, 0x2D, 0xED, 0x1F, 0xA9, 0x34, 0x02, 0x66,
+ 0xEE, 0xA2, 0x5F, 0x01, 0xE8, 0x18, 0x0D, 0x1D,
+ 0xDC, 0x6A, 0xD8, 0xDD, 0x6A, 0x0B, 0x8F, 0xAB,
+ 0x8C, 0x73, 0xAE, 0xBB, 0x97, 0x73, 0x17, 0x1B,
+ 0xBA, 0x04, 0xA7, 0x81, 0xB1, 0x13, 0x14, 0xD5,
+ 0xA3, 0x0A, 0x9D, 0x1C, 0x28, 0x12, 0xCA, 0x7C
+ },
+ {
+ 0x2D, 0xC4, 0xAD, 0x06, 0x89, 0xA4, 0x46, 0x0B,
+ 0x5B, 0x39, 0x9E, 0x91, 0x1B, 0xDB, 0x41, 0x58,
+ 0x6A, 0xC8, 0xAD, 0x36, 0x7B, 0x7A, 0xA3, 0x9E,
+ 0x3E, 0xAE, 0xC8, 0x89, 0x9A, 0x2D, 0x3C, 0xE3,
+ 0x8E, 0x34, 0xAB, 0x46, 0x08, 0x23, 0x4D, 0x75,
+ 0xEB, 0x67, 0x37, 0xFE, 0x21, 0x58, 0x24, 0xC2,
+ 0xA9, 0x78, 0x83, 0x59, 0x6F, 0x6F, 0x18, 0xDD,
+ 0xEB, 0xBF, 0x16, 0x27, 0xDE, 0xD9, 0x1D, 0x84
+ },
+ {
+ 0xF5, 0x6A, 0x11, 0xCB, 0xBF, 0x8A, 0x99, 0x7E,
+ 0x14, 0x77, 0xEC, 0x76, 0xE5, 0x3C, 0x89, 0x4B,
+ 0x14, 0x8D, 0x69, 0x25, 0xA4, 0x33, 0x6F, 0x0C,
+ 0xB7, 0xAA, 0xB9, 0xD8, 0x02, 0xAC, 0x9B, 0x45,
+ 0x36, 0xF4, 0x80, 0x10, 0x1F, 0x3F, 0x9A, 0x77,
+ 0xEE, 0xCD, 0xCB, 0xAE, 0x7A, 0xA6, 0xEA, 0x44,
+ 0x7A, 0x85, 0xDA, 0x90, 0xB5, 0x01, 0xF7, 0xDB,
+ 0x2E, 0xF8, 0xDD, 0xF5, 0xDE, 0x17, 0x33, 0x63
+ },
+ {
+ 0x6E, 0x17, 0x1D, 0x19, 0x6D, 0x0F, 0xC8, 0x2F,
+ 0xB4, 0x73, 0xE2, 0x9D, 0xA8, 0xF4, 0x0F, 0x37,
+ 0xEE, 0x97, 0x41, 0xAC, 0x3E, 0xAF, 0x17, 0x5D,
+ 0xD4, 0x9F, 0xDB, 0x56, 0x53, 0x0D, 0xB5, 0x98,
+ 0x98, 0xBA, 0xF3, 0xCE, 0xE7, 0x2E, 0xEF, 0x5E,
+ 0x77, 0x27, 0x6C, 0xAD, 0xAB, 0xCD, 0x75, 0x2C,
+ 0xA3, 0xA1, 0xB8, 0x64, 0xC1, 0x0A, 0xD2, 0x8D,
+ 0x27, 0xEA, 0xAD, 0x86, 0xE3, 0xF2, 0x1D, 0x33
+ },
+ {
+ 0x95, 0x20, 0x12, 0x33, 0x0D, 0x92, 0xBB, 0x9C,
+ 0x18, 0x92, 0xF2, 0x5B, 0x7B, 0x5A, 0xA0, 0xFE,
+ 0xD3, 0xC0, 0x39, 0x8A, 0x17, 0x08, 0x50, 0x9A,
+ 0x66, 0x14, 0x74, 0xA3, 0xF5, 0xE5, 0x11, 0xD0,
+ 0x9F, 0x21, 0xC3, 0x00, 0x08, 0x00, 0x2F, 0x10,
+ 0x42, 0xD8, 0x3D, 0x2F, 0x7B, 0x11, 0x33, 0x6B,
+ 0x8C, 0x2F, 0xE1, 0xD9, 0x79, 0xC1, 0xE3, 0x86,
+ 0xE0, 0x20, 0x97, 0x48, 0x9B, 0x2D, 0xFC, 0xF5
+ },
+ {
+ 0x2D, 0xCE, 0x47, 0xC3, 0x3A, 0x7E, 0x7F, 0x21,
+ 0x5D, 0x34, 0xA5, 0x47, 0x1B, 0xCD, 0x11, 0x10,
+ 0x60, 0x6C, 0x77, 0x13, 0x8F, 0x19, 0xD4, 0x17,
+ 0x41, 0xED, 0x5D, 0x1B, 0x89, 0xE8, 0xF7, 0xC7,
+ 0x74, 0xEE, 0xC4, 0xBB, 0xC1, 0x02, 0x76, 0x6E,
+ 0xA1, 0x53, 0x2F, 0x2E, 0x43, 0x13, 0x4A, 0xD3,
+ 0x66, 0xBD, 0xCC, 0x27, 0xD1, 0xA0, 0xCC, 0x95,
+ 0x9E, 0x16, 0x48, 0x65, 0x9E, 0x44, 0xCB, 0xBE
+ },
+ {
+ 0x7F, 0x06, 0x59, 0x59, 0x7E, 0x7A, 0xD1, 0x22,
+ 0xD1, 0xC9, 0xED, 0x91, 0x93, 0x0B, 0x07, 0xDE,
+ 0x40, 0xE2, 0x55, 0x20, 0x1A, 0x33, 0xEB, 0x2B,
+ 0x31, 0x81, 0x37, 0x6E, 0x36, 0x8D, 0xF7, 0x76,
+ 0x4C, 0x0C, 0x14, 0xBF, 0x79, 0x9F, 0x16, 0x1B,
+ 0x9B, 0x00, 0x79, 0x57, 0x8B, 0x47, 0x09, 0x71,
+ 0x3E, 0x24, 0xE4, 0x2F, 0xE7, 0xDD, 0x71, 0xB5,
+ 0x09, 0x43, 0xF4, 0x40, 0xE2, 0x3C, 0xD1, 0xBE
+ },
+ {
+ 0x1E, 0x66, 0xF7, 0xB3, 0x58, 0x80, 0x5D, 0xDD,
+ 0xFF, 0xC5, 0x82, 0x68, 0x3E, 0x0B, 0xAD, 0x81,
+ 0x8C, 0x87, 0x34, 0x03, 0xD4, 0xBA, 0x15, 0x06,
+ 0xB9, 0x2F, 0xB3, 0x20, 0xCA, 0x8C, 0xF9, 0xCE,
+ 0xE8, 0x15, 0x47, 0x15, 0xD6, 0xDB, 0x6F, 0x04,
+ 0x09, 0x3D, 0x4B, 0x3F, 0xD8, 0xA6, 0xFC, 0x8E,
+ 0x7E, 0xDD, 0xEA, 0xF2, 0x79, 0x5B, 0x3D, 0x22,
+ 0xDE, 0x7C, 0x75, 0xEC, 0xFF, 0x6F, 0x92, 0xAF
+ },
+ {
+ 0x1F, 0x60, 0xC1, 0x8D, 0xB1, 0x68, 0xD9, 0x0D,
+ 0x2B, 0x46, 0x60, 0xE7, 0x58, 0xA3, 0xCD, 0x28,
+ 0x02, 0x3D, 0x4C, 0x0B, 0x84, 0x8B, 0x5E, 0x33,
+ 0xEA, 0x5C, 0xC1, 0x56, 0x29, 0xFD, 0x35, 0x2E,
+ 0xAC, 0xB1, 0x4F, 0x05, 0xFD, 0xEC, 0x07, 0xAC,
+ 0x23, 0xDA, 0x92, 0x04, 0x74, 0x5F, 0xA9, 0x73,
+ 0xC3, 0x29, 0x55, 0x13, 0x5F, 0x8E, 0xC7, 0x41,
+ 0x0A, 0x1C, 0xB5, 0x3B, 0xC7, 0x58, 0x06, 0x84
+ },
+ {
+ 0xB9, 0xDF, 0x57, 0xB3, 0x45, 0xEE, 0x6F, 0x87,
+ 0x0E, 0xE0, 0xE6, 0x3C, 0x55, 0x8B, 0x81, 0xC1,
+ 0xBC, 0x38, 0x42, 0x97, 0x6F, 0xD3, 0xCF, 0xB1,
+ 0xB5, 0x3B, 0x76, 0x6B, 0xF4, 0x36, 0xD1, 0xD1,
+ 0x75, 0xF4, 0xD4, 0xC5, 0xF1, 0xBD, 0x8D, 0x7A,
+ 0xF6, 0x5B, 0x5D, 0x18, 0xA7, 0x2F, 0x95, 0x71,
+ 0xF2, 0x34, 0x70, 0x19, 0x32, 0xAF, 0xB7, 0xC3,
+ 0xC9, 0x4A, 0x8C, 0x8F, 0xA0, 0x23, 0xDB, 0x4F
+ },
+ {
+ 0xD8, 0xC8, 0x24, 0x95, 0xA2, 0xB5, 0xF6, 0x64,
+ 0x51, 0xF8, 0xC5, 0xB2, 0xE8, 0xA1, 0x73, 0x33,
+ 0xC2, 0xBE, 0x32, 0x20, 0xCE, 0x06, 0xA8, 0x14,
+ 0xC2, 0xCE, 0xA9, 0x5C, 0xC8, 0x65, 0x92, 0xAA,
+ 0x02, 0x15, 0xBF, 0x29, 0x46, 0x14, 0xA3, 0x28,
+ 0xCF, 0x07, 0x22, 0x2B, 0x73, 0xF9, 0x3F, 0x24,
+ 0x2A, 0x94, 0x8B, 0xCA, 0xE9, 0x56, 0x5F, 0xC9,
+ 0x70, 0x57, 0xB5, 0x2E, 0x02, 0x80, 0xEB, 0x82
+ },
+ {
+ 0x81, 0x34, 0xCE, 0x66, 0xD9, 0x5C, 0x40, 0x88,
+ 0xA5, 0x66, 0xD4, 0xE4, 0x35, 0x99, 0x06, 0x9A,
+ 0xD0, 0x45, 0x53, 0xB0, 0xFE, 0xA3, 0xD7, 0x48,
+ 0x19, 0xA6, 0xFD, 0x76, 0x6F, 0x43, 0x67, 0x42,
+ 0xF6, 0xB6, 0xEC, 0xC8, 0x27, 0x93, 0x98, 0x60,
+ 0x9F, 0x60, 0xB4, 0xE4, 0xBB, 0x44, 0xFD, 0x72,
+ 0xCD, 0xFB, 0xFF, 0x18, 0xD8, 0x03, 0x8A, 0xA7,
+ 0x12, 0x30, 0x83, 0x8B, 0x12, 0x6B, 0xC3, 0x00
+ },
+ {
+ 0x3D, 0xA8, 0x9F, 0x5C, 0x52, 0xB0, 0x52, 0xE0,
+ 0x42, 0xE5, 0x11, 0x7B, 0x96, 0x80, 0x6E, 0xDB,
+ 0x1C, 0x55, 0x22, 0x7E, 0x85, 0x14, 0xB3, 0x9E,
+ 0x8B, 0x22, 0xBE, 0xA4, 0xC9, 0x53, 0x30, 0x80,
+ 0xA4, 0xD7, 0xA9, 0x24, 0x92, 0xB7, 0x51, 0x76,
+ 0x9B, 0x0E, 0x11, 0x9E, 0xF4, 0xDB, 0x2B, 0xB8,
+ 0x8D, 0x5C, 0x1E, 0x75, 0xB4, 0x03, 0x10, 0x74,
+ 0xD7, 0xF2, 0x1A, 0x78, 0x01, 0x4A, 0x1F, 0x96
+ },
+ {
+ 0x9B, 0xDC, 0xB4, 0x69, 0xC2, 0x66, 0x5D, 0xD8,
+ 0x46, 0x83, 0xE5, 0x81, 0x01, 0xFD, 0xAE, 0x5C,
+ 0x88, 0x29, 0x2A, 0x4E, 0x05, 0xC4, 0x00, 0xCA,
+ 0x08, 0x26, 0xDA, 0x79, 0x38, 0x2B, 0x8A, 0x28,
+ 0x26, 0xFF, 0x24, 0xFC, 0xD5, 0x56, 0xC9, 0xD5,
+ 0xB5, 0xAA, 0x89, 0x2F, 0x02, 0xB1, 0x67, 0x04,
+ 0x77, 0x27, 0x9B, 0xD7, 0x5F, 0x1B, 0x2B, 0x7B,
+ 0x67, 0x5E, 0xFA, 0xC3, 0x80, 0x60, 0x70, 0x36
+ },
+ {
+ 0x6C, 0x77, 0x85, 0x7B, 0x38, 0x53, 0x3E, 0x41,
+ 0x4A, 0xF7, 0x38, 0x7C, 0x98, 0x56, 0x8D, 0x71,
+ 0xC8, 0xF0, 0xE3, 0x5E, 0x22, 0xB0, 0x2E, 0x2A,
+ 0x1C, 0x0D, 0xC6, 0xD5, 0x7E, 0x37, 0xD8, 0x68,
+ 0x72, 0x5A, 0xD8, 0x23, 0x58, 0x6A, 0x0B, 0xEE,
+ 0xF3, 0x98, 0x89, 0xCC, 0x31, 0xF1, 0xF7, 0xFA,
+ 0xD0, 0x96, 0x0A, 0x12, 0x5E, 0x29, 0xDF, 0xEA,
+ 0x74, 0x55, 0x12, 0xD1, 0x79, 0xE5, 0xF5, 0x89
+ },
+ {
+ 0x88, 0xC9, 0x83, 0x3A, 0x6D, 0x44, 0xFC, 0x25,
+ 0xBB, 0x64, 0xF3, 0xE9, 0x8E, 0x83, 0x8F, 0xB4,
+ 0xFF, 0x56, 0x48, 0x96, 0xDC, 0xD3, 0x58, 0x3A,
+ 0x8B, 0x57, 0xC9, 0x46, 0x6E, 0x74, 0x0C, 0x62,
+ 0x8B, 0x2D, 0x26, 0xEA, 0x14, 0x7C, 0xB3, 0x11,
+ 0x10, 0xFB, 0xAD, 0xCF, 0x9D, 0x01, 0x08, 0xAC,
+ 0xCE, 0xBE, 0x04, 0x31, 0x7D, 0x19, 0xFC, 0x03,
+ 0x66, 0xDE, 0x0C, 0x28, 0xA1, 0xA4, 0x5E, 0x2A
+ },
+ {
+ 0x0A, 0xAB, 0xB3, 0xA1, 0x78, 0x46, 0x4A, 0x01,
+ 0x47, 0x64, 0x5F, 0x05, 0x71, 0x2A, 0x0A, 0x15,
+ 0x55, 0xC5, 0xB9, 0xA3, 0xE9, 0x99, 0xAB, 0x25,
+ 0x5A, 0xCA, 0x35, 0xC5, 0x03, 0x81, 0xF4, 0x90,
+ 0x55, 0x1A, 0x40, 0x89, 0x31, 0xAA, 0x6B, 0xE9,
+ 0xA4, 0xEF, 0x49, 0x7A, 0x16, 0x5B, 0x36, 0x66,
+ 0x3B, 0x1E, 0x1F, 0x05, 0x13, 0x48, 0x02, 0xB1,
+ 0x78, 0xB7, 0xC7, 0x04, 0x68, 0xCB, 0x98, 0xE8
+ },
+ {
+ 0x58, 0x50, 0xD8, 0x93, 0x70, 0x6B, 0x3B, 0xC2,
+ 0xDB, 0xBA, 0x9C, 0xFA, 0xB0, 0x28, 0xBE, 0xD8,
+ 0x19, 0xA2, 0x83, 0x11, 0xD2, 0xD6, 0xF0, 0xCD,
+ 0x8E, 0x27, 0x2E, 0xE6, 0x77, 0xBC, 0x87, 0x8A,
+ 0x0C, 0xED, 0x6C, 0x0D, 0xEA, 0x9E, 0x5C, 0xC9,
+ 0x4B, 0x2B, 0x4F, 0x59, 0x1A, 0x40, 0xEC, 0x9F,
+ 0xB1, 0x82, 0x22, 0xD6, 0xDE, 0xAC, 0xE1, 0xF9,
+ 0xC0, 0x83, 0xDC, 0x05, 0xDE, 0x11, 0x7A, 0x53
+ },
+ {
+ 0xBE, 0xE6, 0x96, 0xA4, 0x76, 0x4F, 0x94, 0x25,
+ 0xD9, 0x1B, 0x14, 0x17, 0x38, 0x62, 0x5A, 0x04,
+ 0x47, 0xA8, 0x22, 0xBB, 0xA7, 0xA8, 0x47, 0x78,
+ 0xCC, 0x3A, 0x77, 0xA3, 0x86, 0xCB, 0x18, 0x24,
+ 0x87, 0xDB, 0x51, 0x3B, 0xB8, 0xF3, 0x6F, 0xC2,
+ 0xF7, 0xE6, 0xD2, 0x89, 0x6E, 0x44, 0x56, 0xA5,
+ 0x23, 0x46, 0xC4, 0x94, 0x8E, 0x3E, 0xC6, 0x34,
+ 0xCB, 0xF1, 0x8F, 0x39, 0xC4, 0x46, 0xCB, 0xAB
+ },
+ {
+ 0x3D, 0x9F, 0x75, 0xD3, 0xE5, 0x0D, 0x9B, 0xA3,
+ 0xBC, 0xAC, 0x4A, 0x4E, 0x11, 0x6B, 0x9B, 0x30,
+ 0x8D, 0xC6, 0x45, 0x99, 0xA3, 0x86, 0x4A, 0x9D,
+ 0xAF, 0xD7, 0x5C, 0xB7, 0x1F, 0x2D, 0xE3, 0x10,
+ 0x9F, 0x79, 0x56, 0xA7, 0xD2, 0xDD, 0x37, 0x4F,
+ 0x84, 0x06, 0xD7, 0x7F, 0x79, 0x63, 0x11, 0xE3,
+ 0xD3, 0x00, 0x89, 0xE5, 0x4D, 0xD6, 0xCE, 0x8A,
+ 0xBB, 0x02, 0xA8, 0x5A, 0x85, 0xAE, 0x92, 0xE4
+ },
+ {
+ 0xEF, 0x39, 0x51, 0x47, 0x5A, 0x16, 0xDF, 0x64,
+ 0x98, 0x32, 0x24, 0x04, 0x65, 0x30, 0xDC, 0x7C,
+ 0xB0, 0x53, 0xD2, 0x93, 0x94, 0x75, 0x39, 0x11,
+ 0xC4, 0x94, 0x99, 0x50, 0xF2, 0x3E, 0x8A, 0x92,
+ 0xC7, 0x09, 0xF4, 0x63, 0x69, 0xB2, 0x3A, 0x0D,
+ 0x70, 0x3A, 0x6F, 0x36, 0x49, 0x0F, 0x75, 0xBE,
+ 0x1E, 0x3E, 0x81, 0x29, 0xA8, 0x29, 0xF3, 0xDC,
+ 0xD7, 0x2D, 0x0E, 0x55, 0x49, 0x7B, 0x81, 0x33
+ },
+ {
+ 0xD4, 0x19, 0x7D, 0x2A, 0x68, 0x5B, 0xCA, 0x6B,
+ 0xFB, 0xDD, 0x0E, 0x3D, 0x84, 0xC7, 0x48, 0x01,
+ 0x35, 0x48, 0xBC, 0x84, 0x9F, 0xE6, 0x49, 0xDA,
+ 0xE7, 0xC4, 0xA2, 0x77, 0xFC, 0xBD, 0x8F, 0x81,
+ 0x8A, 0x9E, 0xDF, 0xA6, 0xCA, 0x14, 0xD7, 0xFE,
+ 0xEA, 0x72, 0x6B, 0x23, 0xB4, 0xA3, 0x3A, 0xA8,
+ 0xA3, 0xF5, 0xA6, 0x61, 0x67, 0x21, 0x5C, 0x61,
+ 0x48, 0xC0, 0x6B, 0x94, 0xCD, 0x8B, 0xFE, 0x37
+ },
+ {
+ 0x7A, 0x24, 0x40, 0x33, 0x35, 0xB8, 0x64, 0x10,
+ 0xD8, 0xD6, 0x93, 0xF1, 0x63, 0xD6, 0x19, 0x8A,
+ 0x68, 0x0F, 0x7E, 0x3A, 0xC0, 0x25, 0xEC, 0x44,
+ 0x74, 0x24, 0x9B, 0x01, 0x16, 0x77, 0xFE, 0x1C,
+ 0x86, 0x6A, 0xAF, 0x45, 0x3D, 0xB0, 0xE8, 0xF6,
+ 0x54, 0x33, 0x51, 0x50, 0x86, 0x3A, 0xCE, 0x57,
+ 0x66, 0x50, 0x80, 0x31, 0x91, 0x27, 0x8E, 0x9D,
+ 0x4B, 0x54, 0x7A, 0x43, 0x4C, 0x56, 0x54, 0xE2
+ },
+ {
+ 0xAF, 0x07, 0xC6, 0x7D, 0x58, 0x74, 0x3A, 0xEB,
+ 0x18, 0x50, 0xEB, 0x53, 0xB2, 0xDA, 0x78, 0xEC,
+ 0xF7, 0x09, 0x58, 0x18, 0x32, 0x5B, 0xEB, 0x86,
+ 0x6F, 0xF3, 0x13, 0xE3, 0x94, 0xC0, 0x07, 0xE0,
+ 0xC0, 0xB5, 0xA1, 0xCD, 0x7A, 0xE6, 0xBB, 0x37,
+ 0xCD, 0x27, 0x81, 0xB5, 0x2D, 0x15, 0x4D, 0x18,
+ 0x86, 0x5D, 0x5E, 0x37, 0xDB, 0xAA, 0x5F, 0x96,
+ 0x73, 0x9B, 0xF7, 0x69, 0x59, 0x96, 0xAE, 0x30
+ },
+ {
+ 0x28, 0xB3, 0xC2, 0x60, 0xFA, 0x7F, 0x23, 0xB9,
+ 0xCC, 0xAD, 0xD6, 0x15, 0xA1, 0x14, 0x69, 0x49,
+ 0x8A, 0xDB, 0x18, 0xD7, 0xA9, 0xF6, 0x84, 0xFD,
+ 0xE4, 0x35, 0xC0, 0x65, 0x33, 0xF5, 0xF5, 0x08,
+ 0xB2, 0x9B, 0x5E, 0xCD, 0x0E, 0xCD, 0x57, 0x36,
+ 0x9F, 0x22, 0xF1, 0xC5, 0x4E, 0x61, 0xBE, 0x6C,
+ 0xD1, 0x04, 0xC8, 0xF7, 0xD3, 0xE1, 0x84, 0x7A,
+ 0xAD, 0x67, 0x07, 0x3A, 0x47, 0x86, 0xE1, 0xDB
+ },
+ {
+ 0xD6, 0x43, 0x23, 0x33, 0x25, 0x23, 0x9E, 0x2E,
+ 0xBD, 0x41, 0x1F, 0x0E, 0x00, 0x23, 0x30, 0x56,
+ 0x2E, 0xB1, 0xBB, 0x08, 0xE6, 0x88, 0x24, 0xB7,
+ 0x1B, 0x98, 0x19, 0x9C, 0x76, 0xD5, 0x31, 0x58,
+ 0xD9, 0x1D, 0xDD, 0x6F, 0x4F, 0x82, 0x61, 0xEC,
+ 0x1D, 0x72, 0xFC, 0x77, 0xC2, 0xCC, 0x23, 0x7E,
+ 0xDA, 0x15, 0xF0, 0x25, 0x7C, 0xF0, 0x7B, 0x84,
+ 0xCF, 0x1F, 0xBD, 0x1D, 0xBA, 0xFA, 0x1D, 0xFC
+ },
+ {
+ 0x3D, 0x7B, 0x44, 0xCC, 0x82, 0xEF, 0xCA, 0xFC,
+ 0xAB, 0xA6, 0xB1, 0x91, 0x05, 0x48, 0x95, 0x8C,
+ 0x18, 0x0A, 0x0E, 0x8D, 0x84, 0xBC, 0x66, 0x3E,
+ 0x8E, 0xF9, 0x53, 0x3B, 0xD8, 0x0C, 0x4B, 0xBA,
+ 0xAA, 0x25, 0x5B, 0x19, 0x81, 0xF7, 0x56, 0xEB,
+ 0x10, 0x79, 0xAD, 0x0F, 0x34, 0x71, 0xA1, 0xFC,
+ 0x9D, 0x7A, 0x43, 0x23, 0x39, 0x30, 0x3A, 0x57,
+ 0x81, 0xA3, 0x45, 0x35, 0x30, 0x9E, 0x5A, 0x24
+ },
+ {
+ 0xEB, 0x08, 0x12, 0xC9, 0x67, 0x06, 0x46, 0xD5,
+ 0x63, 0x19, 0x8B, 0x11, 0x7A, 0xAF, 0xC5, 0x6F,
+ 0xA1, 0xB6, 0x56, 0x0F, 0x88, 0xB5, 0x75, 0x4E,
+ 0xBF, 0xC3, 0x1B, 0x35, 0x52, 0x16, 0xD8, 0xD7,
+ 0x4D, 0x34, 0x1E, 0x35, 0xB2, 0x43, 0xBC, 0x93,
+ 0x8C, 0xF5, 0x46, 0xAF, 0x1F, 0x73, 0xC1, 0xB0,
+ 0x04, 0x55, 0xDC, 0x06, 0xB2, 0xC6, 0xC5, 0x35,
+ 0x27, 0x9E, 0x87, 0x67, 0x49, 0x8F, 0x14, 0xE6
+ },
+ {
+ 0x7B, 0xBA, 0x7D, 0x73, 0x04, 0x02, 0x1C, 0x75,
+ 0xB5, 0xD6, 0xCE, 0x66, 0xB4, 0xEF, 0xA5, 0x50,
+ 0x19, 0xD9, 0x42, 0xD2, 0x08, 0xAF, 0xAC, 0x82,
+ 0x11, 0xAA, 0x7E, 0x5E, 0x11, 0x1E, 0x27, 0x69,
+ 0x76, 0x70, 0xE4, 0xEC, 0x91, 0xBA, 0x30, 0x8E,
+ 0xBD, 0xFB, 0x19, 0x15, 0x4C, 0x3B, 0xAD, 0x05,
+ 0x26, 0xA6, 0x25, 0x41, 0xAE, 0x5D, 0x43, 0xD0,
+ 0xF5, 0x47, 0xB9, 0xD9, 0x8E, 0x07, 0x36, 0x60
+ },
+ {
+ 0xA8, 0xE2, 0xA9, 0x46, 0x8D, 0xA3, 0xE3, 0x54,
+ 0x3A, 0x23, 0xA5, 0x78, 0x78, 0x0E, 0x25, 0x62,
+ 0xC7, 0xCE, 0x57, 0xFD, 0x11, 0x20, 0xE1, 0xC0,
+ 0x24, 0xD7, 0xEA, 0x32, 0x90, 0x31, 0x70, 0x46,
+ 0x61, 0x6E, 0x14, 0xCD, 0x0F, 0x15, 0xA8, 0x6B,
+ 0x99, 0x39, 0x54, 0x9B, 0x14, 0x76, 0x11, 0xB6,
+ 0xA5, 0x5D, 0x85, 0xAB, 0xC2, 0x5F, 0x63, 0x95,
+ 0x46, 0xB8, 0x9D, 0xD2, 0x3D, 0x39, 0xA9, 0x85
+ },
+ {
+ 0xCE, 0x87, 0x4C, 0xD6, 0xE1, 0x95, 0x8B, 0x9D,
+ 0x7F, 0x11, 0xFF, 0x44, 0xAB, 0x08, 0x32, 0xE8,
+ 0x48, 0x70, 0x2C, 0x8F, 0x26, 0x65, 0x6B, 0xA1,
+ 0x0B, 0xF5, 0x72, 0x0A, 0x7C, 0xAA, 0x1F, 0x59,
+ 0x08, 0xC9, 0x9A, 0x96, 0x03, 0xA9, 0x8B, 0x41,
+ 0x6C, 0x57, 0x22, 0x8C, 0x81, 0x9C, 0xEA, 0xF8,
+ 0x27, 0x01, 0x3B, 0x2E, 0x6D, 0x6B, 0x2D, 0xAE,
+ 0x59, 0xDF, 0xF1, 0x04, 0xB9, 0x02, 0xC3, 0x1B
+ },
+ {
+ 0x30, 0xFF, 0xFE, 0x37, 0x21, 0x8D, 0xB1, 0x94,
+ 0xB2, 0x32, 0x73, 0x49, 0x8F, 0x45, 0x44, 0xD3,
+ 0x84, 0x14, 0xBE, 0xE4, 0x1B, 0x17, 0x55, 0xA0,
+ 0xC6, 0xC2, 0xDB, 0xCB, 0x41, 0x19, 0x42, 0xD5,
+ 0xEC, 0xB9, 0xD4, 0x52, 0x3F, 0xB4, 0x79, 0x4B,
+ 0xA3, 0x6E, 0x57, 0x9A, 0xF2, 0xF8, 0xDD, 0x85,
+ 0x19, 0x99, 0x23, 0x31, 0x83, 0xFA, 0xB2, 0x7B,
+ 0x47, 0xAD, 0xD8, 0x7D, 0xF3, 0x59, 0x14, 0xBB
+ },
+ {
+ 0xCE, 0xF4, 0x43, 0x1D, 0xCE, 0x9F, 0xF5, 0x5A,
+ 0x00, 0x30, 0x0E, 0xC8, 0x64, 0x9E, 0x27, 0x58,
+ 0x36, 0x18, 0x22, 0x43, 0x69, 0xF6, 0x0A, 0x5C,
+ 0x89, 0x6B, 0x2A, 0x31, 0x10, 0xB0, 0x32, 0xB8,
+ 0x7C, 0x9E, 0xE4, 0xF2, 0x6C, 0x5F, 0x0B, 0xDB,
+ 0x50, 0x3E, 0xA7, 0x44, 0x7A, 0x5D, 0xB3, 0xF7,
+ 0x07, 0xFE, 0x34, 0x10, 0xDA, 0xCD, 0xD7, 0x57,
+ 0x22, 0x19, 0xBD, 0xEA, 0x8E, 0x17, 0xDC, 0x04
+ },
+ {
+ 0x8F, 0xF0, 0xBC, 0xB7, 0x5F, 0x00, 0x61, 0xB5,
+ 0xF9, 0x09, 0x29, 0x8F, 0x56, 0x9E, 0x45, 0xC7,
+ 0x5E, 0xD2, 0xD6, 0x4A, 0x81, 0x89, 0xCE, 0xBD,
+ 0x4E, 0x02, 0x56, 0x6E, 0x1A, 0x1B, 0x8B, 0xE5,
+ 0x3A, 0x78, 0x32, 0x28, 0x55, 0x8E, 0x28, 0xB5,
+ 0xF8, 0x7C, 0xCC, 0x2F, 0x42, 0x8F, 0x7F, 0x87,
+ 0x97, 0x44, 0xB5, 0x25, 0xB2, 0x49, 0x62, 0xB3,
+ 0x60, 0x4B, 0x12, 0x0F, 0x06, 0x77, 0x9F, 0x2E
+ },
+ {
+ 0x7F, 0x8D, 0xDF, 0xFB, 0x4D, 0xC1, 0x51, 0x91,
+ 0xDE, 0x3D, 0xDB, 0xE4, 0xA0, 0xF8, 0x8B, 0x7A,
+ 0xB0, 0x2D, 0x48, 0xE2, 0x5C, 0xFC, 0x1F, 0xE9,
+ 0x1D, 0xA5, 0x57, 0xE8, 0x85, 0xD0, 0x12, 0xB8,
+ 0xF6, 0x55, 0x26, 0xC5, 0xB7, 0xB1, 0x01, 0x3F,
+ 0xC8, 0x16, 0x58, 0x50, 0x43, 0xA3, 0x45, 0x60,
+ 0x5A, 0x39, 0xD8, 0xDA, 0xD7, 0x0D, 0x8A, 0x64,
+ 0x48, 0x51, 0x32, 0x50, 0xAA, 0xC4, 0xF3, 0xD5
+ },
+ {
+ 0xB1, 0xFE, 0x8C, 0x68, 0xAE, 0xF6, 0xB4, 0xD4,
+ 0xB2, 0x33, 0x54, 0xEB, 0x8C, 0x1D, 0x8F, 0x5A,
+ 0x56, 0xE3, 0x2E, 0x76, 0xB9, 0x6A, 0xC8, 0x44,
+ 0x3B, 0x2A, 0xB8, 0x35, 0xE4, 0xC8, 0xB6, 0x74,
+ 0xB3, 0x3E, 0x4C, 0x6C, 0x6D, 0xC1, 0x21, 0xD7,
+ 0xC2, 0xD3, 0x4B, 0x59, 0xB3, 0x7A, 0x56, 0x8A,
+ 0x1C, 0x98, 0xD5, 0x00, 0x32, 0x4E, 0x53, 0x08,
+ 0x87, 0x85, 0xB6, 0xB0, 0x80, 0x63, 0x47, 0xD1
+ },
+ {
+ 0x8E, 0x87, 0x34, 0xFC, 0xF9, 0x25, 0x9E, 0xE3,
+ 0x7F, 0xE9, 0xC6, 0xCD, 0xA2, 0x82, 0xC2, 0xD5,
+ 0xEB, 0x83, 0xD0, 0xCF, 0x43, 0x9C, 0x86, 0x19,
+ 0xD4, 0xB0, 0x42, 0xFF, 0x69, 0x96, 0x6B, 0x03,
+ 0x56, 0x5B, 0xE4, 0xDF, 0x96, 0x39, 0x3F, 0xE6,
+ 0xBF, 0x35, 0xAF, 0xA1, 0x6E, 0x02, 0x73, 0xB6,
+ 0xD3, 0x39, 0xC0, 0x09, 0x95, 0xBF, 0x6F, 0x60,
+ 0xA7, 0x14, 0xEF, 0x18, 0x0E, 0xBB, 0x93, 0x15
+ },
+ {
+ 0xAE, 0x15, 0x6D, 0x43, 0xA7, 0x2C, 0x04, 0x29,
+ 0x42, 0x59, 0x58, 0x78, 0xA7, 0x83, 0x07, 0x97,
+ 0x60, 0xF5, 0x21, 0xED, 0xB8, 0xB2, 0xC3, 0xD4,
+ 0x1A, 0x56, 0x6B, 0x7C, 0xF7, 0x4A, 0x4A, 0x08,
+ 0xEA, 0x0F, 0x11, 0x9D, 0x24, 0x0A, 0x62, 0xEC,
+ 0x73, 0xB9, 0x50, 0x97, 0x88, 0xFA, 0x3A, 0xED,
+ 0xF1, 0x20, 0xEE, 0x88, 0xCB, 0x95, 0x1B, 0x69,
+ 0x3F, 0x8F, 0x7C, 0xAF, 0x8C, 0xBA, 0x37, 0x7F
+ },
+ {
+ 0x93, 0x30, 0xAA, 0xCA, 0x8C, 0x08, 0x84, 0x46,
+ 0x58, 0xC2, 0x95, 0x06, 0xB1, 0xC3, 0x42, 0x72,
+ 0xE2, 0xB3, 0xC7, 0xB4, 0xE7, 0x5E, 0x6F, 0xE9,
+ 0x9A, 0x01, 0x07, 0xEC, 0x5D, 0xA4, 0x53, 0x0F,
+ 0xB1, 0xC8, 0x8C, 0xAA, 0x66, 0xDD, 0x9C, 0x47,
+ 0x1E, 0x01, 0xCA, 0x21, 0xA1, 0x3A, 0x5D, 0x6F,
+ 0x82, 0x15, 0xDE, 0xD3, 0x14, 0x7E, 0x94, 0xDE,
+ 0x20, 0x88, 0x57, 0x1F, 0xD1, 0xBF, 0x23, 0xB6
+ },
+ {
+ 0xC1, 0x29, 0xF2, 0x2C, 0x50, 0xF5, 0x99, 0x72,
+ 0x32, 0xE2, 0xB9, 0xF9, 0x3D, 0xFA, 0xA0, 0x0A,
+ 0xD8, 0xA5, 0x34, 0x29, 0xF9, 0xD1, 0x5B, 0x98,
+ 0x42, 0xE3, 0xAE, 0x08, 0xD8, 0x49, 0xEB, 0xDD,
+ 0x45, 0x23, 0x8C, 0x85, 0xF9, 0x2C, 0x6F, 0x91,
+ 0x7E, 0x0F, 0x8F, 0x6F, 0x94, 0xE2, 0x34, 0xBE,
+ 0x07, 0x61, 0x68, 0xE0, 0xDF, 0x43, 0xD0, 0x28,
+ 0x45, 0x52, 0x79, 0xA6, 0xFF, 0x65, 0xDC, 0x84
+ },
+ {
+ 0x0E, 0x2B, 0x4B, 0xC2, 0xF6, 0xA7, 0x5B, 0xE4,
+ 0xB7, 0xC9, 0xD4, 0xB5, 0x3D, 0x10, 0x4D, 0xA0,
+ 0x65, 0x85, 0x8D, 0x38, 0x7B, 0x34, 0x0B, 0xC1,
+ 0x63, 0x4F, 0x3A, 0x83, 0x32, 0xD5, 0x4C, 0xAA,
+ 0x94, 0x30, 0x24, 0xB2, 0x13, 0xDC, 0x8D, 0x4F,
+ 0x21, 0x9E, 0xC8, 0xE1, 0xDE, 0xCA, 0xC7, 0xD5,
+ 0xC6, 0xAE, 0x69, 0xC9, 0xEF, 0xD8, 0x81, 0x49,
+ 0x36, 0x78, 0x38, 0x20, 0x5D, 0x0D, 0xC7, 0xC0
+ },
+ {
+ 0x83, 0xB5, 0x43, 0x85, 0x3B, 0x81, 0x42, 0xA8,
+ 0x3B, 0xEF, 0xF0, 0x73, 0x5F, 0x20, 0x18, 0x91,
+ 0xE7, 0xFF, 0xC6, 0x7D, 0xBD, 0xCD, 0x21, 0xA4,
+ 0x22, 0xBB, 0x33, 0x6D, 0xE3, 0x29, 0x72, 0xAE,
+ 0x03, 0x92, 0x64, 0x6F, 0x68, 0x27, 0xD8, 0x0C,
+ 0xDA, 0x65, 0x4F, 0xD3, 0xA0, 0x77, 0x4C, 0xD2,
+ 0xF9, 0x95, 0x51, 0x7C, 0xF0, 0x64, 0xC6, 0x17,
+ 0xF2, 0x1A, 0x54, 0x27, 0x5F, 0xE5, 0x0C, 0x8D
+ },
+ {
+ 0x09, 0xBE, 0x15, 0xEB, 0x6A, 0x5C, 0x22, 0x6F,
+ 0x6D, 0x95, 0x08, 0xCB, 0xA4, 0xA2, 0x51, 0x9F,
+ 0xBA, 0x17, 0x2A, 0xF8, 0x37, 0x58, 0x27, 0xD7,
+ 0x54, 0xA7, 0xA1, 0xBC, 0x19, 0x25, 0xD1, 0x3F,
+ 0x5E, 0x63, 0x43, 0xF3, 0xE1, 0x4D, 0x08, 0xA0,
+ 0x6E, 0x8D, 0x37, 0xF8, 0xEC, 0x56, 0xFB, 0x43,
+ 0x8E, 0x62, 0x36, 0x66, 0xB6, 0xFB, 0x0E, 0x23,
+ 0xFB, 0x50, 0x47, 0x7D, 0x41, 0x1B, 0x0C, 0x3A
+ },
+ {
+ 0xC3, 0x57, 0x97, 0xE9, 0x83, 0x2D, 0x3E, 0x23,
+ 0x23, 0x33, 0x5B, 0x8C, 0x19, 0xC5, 0xFA, 0x74,
+ 0x91, 0x60, 0x2D, 0xBF, 0x6B, 0xEA, 0x77, 0xFA,
+ 0xEE, 0xC9, 0x51, 0x0B, 0xC2, 0xE8, 0x91, 0xC8,
+ 0xC3, 0x46, 0x21, 0x99, 0xF6, 0x04, 0x18, 0xD2,
+ 0xE0, 0xAB, 0xFF, 0xE3, 0x1B, 0x61, 0x3B, 0xB9,
+ 0x80, 0xEA, 0x32, 0xB7, 0x6C, 0x82, 0x43, 0x8D,
+ 0x02, 0x5F, 0x67, 0x8C, 0xAF, 0x48, 0x24, 0xA4
+ },
+ {
+ 0xCF, 0xC0, 0x57, 0xFD, 0xA7, 0x8A, 0x50, 0x31,
+ 0x8F, 0x49, 0x78, 0xFF, 0xFF, 0xAF, 0x77, 0x17,
+ 0x98, 0xE1, 0x2C, 0x3E, 0xA8, 0xC7, 0x98, 0x19,
+ 0x5B, 0xC5, 0xB4, 0xE6, 0x89, 0x1E, 0x61, 0xAA,
+ 0x25, 0xF7, 0xAF, 0x4A, 0xA7, 0x28, 0x6A, 0xC8,
+ 0x50, 0x76, 0x62, 0xC9, 0x07, 0xED, 0x91, 0x3E,
+ 0xDA, 0x65, 0x8F, 0x63, 0xFC, 0x47, 0x99, 0x7C,
+ 0x59, 0xB8, 0x59, 0x70, 0xF8, 0x78, 0xCA, 0x18
+ },
+ {
+ 0xD8, 0xEB, 0xE0, 0xE6, 0x38, 0xFC, 0x53, 0x5B,
+ 0x52, 0xCB, 0x0A, 0xFC, 0xE0, 0xF8, 0x2D, 0xDE,
+ 0x28, 0x57, 0x01, 0xAF, 0xF3, 0x29, 0xA5, 0x4B,
+ 0xA0, 0x6D, 0xFD, 0x3D, 0x1B, 0x4B, 0x31, 0xF9,
+ 0xF4, 0xB2, 0x4D, 0x9D, 0x68, 0x36, 0xF1, 0x22,
+ 0x3D, 0x6D, 0xE6, 0x6B, 0xAE, 0x78, 0x88, 0xFE,
+ 0xBC, 0x20, 0x40, 0xCF, 0xE9, 0x30, 0xE6, 0x9C,
+ 0xED, 0x59, 0xDA, 0x6D, 0xA8, 0xA0, 0xA6, 0xA6
+ },
+ {
+ 0x16, 0xB8, 0xC5, 0x5C, 0xF2, 0xF1, 0x35, 0xA4,
+ 0x32, 0x59, 0x0D, 0x2D, 0x4C, 0xFA, 0x38, 0x59,
+ 0x2F, 0x59, 0x35, 0xF8, 0xE7, 0x1C, 0xE0, 0x8A,
+ 0x02, 0x06, 0xA0, 0xE5, 0xAB, 0xEA, 0x90, 0xB2,
+ 0xE1, 0x07, 0xEB, 0x86, 0xB9, 0x18, 0x82, 0x3B,
+ 0xDD, 0x3B, 0xD2, 0x66, 0x07, 0x22, 0xC8, 0xDB,
+ 0xFA, 0x66, 0xAB, 0xB9, 0xF8, 0x63, 0x8E, 0x46,
+ 0x34, 0x02, 0xF6, 0x57, 0xA1, 0x68, 0x64, 0x0A
+ },
+ {
+ 0x6A, 0x6E, 0x89, 0x38, 0x4F, 0x53, 0x5F, 0x02,
+ 0x17, 0x6C, 0x48, 0xA9, 0x93, 0xD3, 0x68, 0x7B,
+ 0x38, 0x9B, 0xFC, 0x03, 0x05, 0x0C, 0x77, 0x70,
+ 0x86, 0x35, 0x5C, 0x1A, 0x55, 0x59, 0x77, 0x42,
+ 0xF0, 0xB7, 0x48, 0x34, 0xA7, 0x1D, 0x05, 0x2A,
+ 0xE8, 0xA8, 0x3D, 0xC3, 0x4A, 0x8F, 0xD7, 0xBA,
+ 0x5A, 0xA6, 0x9D, 0xBD, 0x61, 0x2A, 0x4C, 0x22,
+ 0xDF, 0x4F, 0x74, 0xE2, 0x52, 0x8F, 0xB7, 0xA3
+ },
+ {
+ 0x1E, 0x40, 0x38, 0xCF, 0xA5, 0x0D, 0x8B, 0x13,
+ 0xEF, 0x68, 0xBE, 0xC3, 0xB0, 0xFF, 0xD5, 0x62,
+ 0xA0, 0x7A, 0xD6, 0x34, 0xB5, 0x82, 0x82, 0x57,
+ 0xDB, 0xA8, 0x73, 0x04, 0xF8, 0x23, 0xA9, 0x00,
+ 0x49, 0x2A, 0x31, 0x37, 0x19, 0x8B, 0x60, 0x5C,
+ 0xC7, 0xF7, 0x7C, 0x33, 0xB8, 0xCA, 0x3D, 0x94,
+ 0x0F, 0xD9, 0xB3, 0x38, 0xCF, 0x6B, 0x7B, 0x36,
+ 0xE7, 0xD9, 0xD9, 0x27, 0x20, 0x97, 0x93, 0xD0
+ },
+ {
+ 0x5B, 0xA6, 0xCD, 0x98, 0x8F, 0xF9, 0xA4, 0x81,
+ 0x91, 0x42, 0x21, 0x7E, 0xD6, 0x5D, 0x43, 0x7B,
+ 0x41, 0x3B, 0xA5, 0x02, 0x6B, 0x55, 0x4D, 0x8D,
+ 0x94, 0xEA, 0x27, 0x02, 0xC0, 0x96, 0xD1, 0x01,
+ 0x47, 0x75, 0xDB, 0xA2, 0xCA, 0xE9, 0x6F, 0x1E,
+ 0x2E, 0x72, 0x29, 0xC3, 0x78, 0xF2, 0x0B, 0x03,
+ 0x89, 0xE1, 0x19, 0x54, 0x7F, 0xDD, 0x35, 0x22,
+ 0x4A, 0x61, 0x7F, 0xCD, 0xCD, 0x0C, 0xB3, 0xAF
+ },
+ {
+ 0x2D, 0x20, 0x96, 0x12, 0x30, 0xE2, 0x50, 0xF8,
+ 0x1D, 0xDC, 0xD2, 0xD2, 0xAB, 0x3E, 0xF0, 0xDA,
+ 0xCF, 0x96, 0x85, 0x1E, 0xBA, 0xE5, 0x96, 0x34,
+ 0x47, 0x19, 0x2C, 0xDB, 0x89, 0xE4, 0x8E, 0x84,
+ 0xF3, 0x96, 0xEC, 0x9A, 0x09, 0x25, 0x27, 0x84,
+ 0xE1, 0x73, 0xAD, 0xA5, 0x2A, 0x9C, 0x81, 0xAC,
+ 0xDA, 0xB3, 0xD8, 0xD6, 0x83, 0x80, 0x24, 0x7A,
+ 0xE9, 0x75, 0x23, 0x9B, 0x01, 0x7D, 0xC1, 0xCE
+ },
+ {
+ 0x35, 0x38, 0x3E, 0xA7, 0x76, 0x2B, 0x55, 0x31,
+ 0x0A, 0x7D, 0x57, 0xFB, 0xD5, 0xA5, 0x49, 0x97,
+ 0x57, 0x9B, 0x0B, 0xA3, 0x9A, 0x4E, 0xB8, 0x87,
+ 0x94, 0x2B, 0xD1, 0x4F, 0xD8, 0x48, 0x31, 0x88,
+ 0xE5, 0x00, 0x48, 0x83, 0x8D, 0x6C, 0x02, 0xDC,
+ 0x75, 0x89, 0x59, 0xA9, 0xF7, 0x4D, 0x83, 0x37,
+ 0x27, 0x43, 0xE8, 0x64, 0xC6, 0x01, 0xED, 0x70,
+ 0x40, 0xA9, 0xE8, 0x71, 0x52, 0xD4, 0xCF, 0xFB
+ },
+ {
+ 0x0B, 0x22, 0x3B, 0x6A, 0x1C, 0x2D, 0x3A, 0xB3,
+ 0xF9, 0x07, 0x7A, 0x31, 0x7B, 0x7F, 0xE3, 0x2F,
+ 0x6F, 0x95, 0x7B, 0x7B, 0x17, 0x41, 0xF2, 0x71,
+ 0x77, 0x71, 0x83, 0x4D, 0x37, 0x96, 0xA1, 0x9B,
+ 0xA3, 0x62, 0x73, 0xC9, 0xEE, 0xD6, 0x4C, 0x07,
+ 0xFA, 0x4E, 0x9A, 0xF7, 0xA9, 0x8A, 0xCE, 0x9C,
+ 0x78, 0x9A, 0x79, 0xA5, 0xA0, 0xF9, 0x4D, 0x04,
+ 0x05, 0xAA, 0xF0, 0x4A, 0xF3, 0x1E, 0xD7, 0x97
+ },
+ {
+ 0x5A, 0x00, 0x7F, 0x58, 0x95, 0x52, 0x4A, 0x5E,
+ 0x80, 0x37, 0x03, 0x6E, 0x0F, 0x26, 0x39, 0xFD,
+ 0xA8, 0xC5, 0xC1, 0x51, 0x2D, 0x76, 0xE9, 0xD1,
+ 0x9B, 0x3D, 0xD2, 0xD5, 0xBA, 0x43, 0xF5, 0x07,
+ 0x97, 0x41, 0xA4, 0x58, 0x31, 0x3C, 0x5E, 0x02,
+ 0x40, 0x0C, 0xE0, 0x2C, 0xB6, 0x56, 0x80, 0xBE,
+ 0x28, 0x2E, 0xAC, 0xD9, 0xA2, 0x54, 0xEF, 0x1C,
+ 0xDD, 0xEE, 0xBD, 0xCE, 0xE8, 0x5D, 0x41, 0x87
+ },
+ {
+ 0xBE, 0x4D, 0xD1, 0xCC, 0xBD, 0xE1, 0x67, 0x00,
+ 0x04, 0xD0, 0xEF, 0xAB, 0x65, 0x43, 0xE9, 0x1C,
+ 0x4E, 0x46, 0x64, 0xE5, 0xA2, 0xA8, 0x8B, 0xAC,
+ 0x6D, 0xD2, 0x7D, 0x27, 0x64, 0x8D, 0x30, 0x2A,
+ 0x06, 0x5B, 0xE6, 0x07, 0x8B, 0x22, 0xE4, 0xC4,
+ 0xAB, 0x4F, 0x7F, 0x7C, 0xBF, 0xAF, 0xC1, 0xAD,
+ 0x86, 0xEC, 0x2A, 0x50, 0x4F, 0xE5, 0x85, 0x17,
+ 0x66, 0xF7, 0xA3, 0x24, 0x47, 0x57, 0xCB, 0x6F
+ },
+ {
+ 0x0F, 0xB4, 0x48, 0x3F, 0x96, 0x59, 0x29, 0x6C,
+ 0xB9, 0x24, 0x5B, 0x57, 0x79, 0x2A, 0x1E, 0x6A,
+ 0x99, 0xF2, 0x87, 0x90, 0x07, 0x72, 0x87, 0x96,
+ 0x8A, 0xB3, 0xEF, 0x35, 0x89, 0xE6, 0x90, 0x24,
+ 0x06, 0xF1, 0xF3, 0x9D, 0xCC, 0xE0, 0x06, 0x1D,
+ 0xEA, 0x94, 0x0F, 0xC8, 0xC1, 0xC4, 0x9F, 0x4B,
+ 0x54, 0x5E, 0xED, 0x59, 0xE9, 0x6D, 0xDA, 0xE9,
+ 0x6A, 0x6C, 0x35, 0xB5, 0x59, 0x3C, 0x29, 0x77
+ },
+ {
+ 0x41, 0xD1, 0xFA, 0xDC, 0x60, 0xA4, 0x6C, 0x9A,
+ 0xD0, 0x12, 0x0A, 0x3F, 0x54, 0xD0, 0x05, 0xF5,
+ 0xA1, 0x07, 0x5E, 0x2F, 0x71, 0xEE, 0x0D, 0xA6,
+ 0x18, 0xBA, 0xC1, 0x46, 0x1E, 0xFA, 0xE9, 0x69,
+ 0xEC, 0xCD, 0x7A, 0xA5, 0x75, 0xC4, 0xCD, 0xAE,
+ 0x97, 0x1D, 0xED, 0x13, 0xAE, 0x13, 0xC5, 0x06,
+ 0x87, 0x2C, 0xEC, 0xB5, 0xB2, 0x08, 0xFA, 0x72,
+ 0xA9, 0x48, 0x40, 0x02, 0x3E, 0xDB, 0x3E, 0xFE
+ },
+ {
+ 0x2F, 0x7F, 0xDC, 0x1D, 0xA4, 0x4B, 0x6E, 0x5D,
+ 0x2D, 0xEC, 0xDE, 0x82, 0x1A, 0xAF, 0x4B, 0x49,
+ 0x16, 0x8C, 0x02, 0xE8, 0xD5, 0xF2, 0x5D, 0x5C,
+ 0x69, 0x98, 0x71, 0x08, 0x3A, 0xEB, 0xD9, 0x28,
+ 0xB7, 0x4D, 0xC2, 0x2D, 0xCB, 0xED, 0xFA, 0xBA,
+ 0x93, 0x16, 0xAE, 0xFC, 0xA8, 0x48, 0xD1, 0x5F,
+ 0x05, 0x17, 0x32, 0x99, 0x03, 0xD3, 0x4B, 0x83,
+ 0x70, 0xDD, 0xF9, 0xBD, 0x58, 0xC6, 0xD0, 0xCD
+ },
+ {
+ 0x88, 0x55, 0x8A, 0x46, 0x4E, 0xE1, 0xA8, 0x80,
+ 0x3B, 0x23, 0x95, 0xAF, 0x6A, 0x64, 0x90, 0x84,
+ 0x2B, 0x5C, 0xD4, 0x3D, 0x41, 0xF6, 0xC0, 0x7C,
+ 0xD6, 0xC5, 0xF8, 0x5F, 0x82, 0xF5, 0x84, 0x32,
+ 0xA0, 0xB1, 0x62, 0xB4, 0x38, 0xBF, 0x0C, 0xB7,
+ 0x08, 0x2A, 0x76, 0x73, 0xE2, 0x87, 0xD6, 0xB9,
+ 0x0F, 0x8D, 0x0D, 0xC8, 0xAA, 0x5C, 0xEB, 0xA3,
+ 0x6B, 0xFA, 0x77, 0xB1, 0x5B, 0xA0, 0x69, 0x16
+ },
+ {
+ 0xEC, 0xC1, 0x49, 0x91, 0x7B, 0x26, 0x63, 0x98,
+ 0xB6, 0xF3, 0x29, 0x7E, 0x96, 0x96, 0x73, 0xB1,
+ 0x4E, 0xAE, 0x69, 0xCE, 0x43, 0x67, 0x1F, 0xD3,
+ 0xC6, 0xC2, 0x15, 0xC7, 0xCF, 0x42, 0xDE, 0xA1,
+ 0x02, 0xFC, 0x6B, 0xD9, 0x0C, 0x87, 0xDB, 0xD4,
+ 0x29, 0x02, 0x51, 0x12, 0x9C, 0xC1, 0x9B, 0x38,
+ 0xCC, 0xF0, 0x0C, 0xBD, 0xB1, 0x6D, 0xD8, 0xDE,
+ 0x51, 0x58, 0x60, 0x1A, 0x41, 0x6B, 0x1F, 0x00
+ },
+ {
+ 0xED, 0x30, 0x12, 0xF8, 0x9D, 0x71, 0xED, 0x13,
+ 0xBB, 0x82, 0x72, 0xEC, 0xDC, 0x3D, 0x0F, 0x51,
+ 0xE1, 0x4A, 0x37, 0xC1, 0xEF, 0x77, 0x57, 0x77,
+ 0x7A, 0xDA, 0x67, 0x12, 0x78, 0x4B, 0xE1, 0x6E,
+ 0xCF, 0xD3, 0xE6, 0x40, 0x58, 0x30, 0xF5, 0x1D,
+ 0xB3, 0x3D, 0xCB, 0x85, 0x52, 0x92, 0x93, 0xE2,
+ 0x3E, 0x47, 0x3A, 0xBF, 0x8C, 0x5C, 0x76, 0x55,
+ 0xD0, 0xC4, 0xF1, 0x52, 0xD0, 0x48, 0xBA, 0xB2
+ },
+ {
+ 0x09, 0x7A, 0x81, 0x19, 0x1E, 0x10, 0x05, 0x67,
+ 0x6D, 0x6E, 0x22, 0xA9, 0x63, 0x48, 0xFA, 0x4A,
+ 0x7C, 0x95, 0x61, 0xFD, 0x4D, 0x22, 0x8E, 0xB2,
+ 0x5F, 0x29, 0x47, 0x56, 0xBB, 0x87, 0xA2, 0xBA,
+ 0x88, 0x47, 0x5B, 0x03, 0x6F, 0x79, 0xFE, 0x37,
+ 0x3D, 0x75, 0x40, 0x87, 0x05, 0x52, 0x00, 0x1D,
+ 0x54, 0x79, 0x5F, 0x25, 0x92, 0x39, 0xBE, 0x6D,
+ 0x32, 0xC4, 0x87, 0xD1, 0x94, 0x4F, 0x1F, 0xE7
+ },
+ {
+ 0x3F, 0xC7, 0x98, 0xE4, 0x69, 0xD3, 0x90, 0x86,
+ 0xBA, 0x0B, 0xB4, 0x06, 0x3E, 0x80, 0x5F, 0xDF,
+ 0xB2, 0x20, 0x8D, 0xE4, 0x99, 0x18, 0x41, 0x73,
+ 0xF9, 0xA2, 0x36, 0x4D, 0x56, 0xBC, 0xD5, 0x63,
+ 0xED, 0x61, 0x9B, 0xB6, 0x87, 0x32, 0x24, 0x25,
+ 0x01, 0x4A, 0x1A, 0xAD, 0x3B, 0xCF, 0x50, 0xD2,
+ 0x2D, 0x83, 0xA9, 0x9D, 0x09, 0x73, 0x0A, 0x92,
+ 0xEC, 0x65, 0x46, 0xB3, 0xFC, 0x40, 0xA2, 0xC6
+ },
+ {
+ 0x69, 0x12, 0xB4, 0xB3, 0x41, 0xC7, 0xDD, 0x70,
+ 0x68, 0x37, 0x38, 0xBA, 0x0E, 0x7D, 0xEB, 0xBA,
+ 0xBF, 0xCA, 0x5F, 0x4F, 0xB0, 0x76, 0x0C, 0x84,
+ 0x97, 0x76, 0xE9, 0x20, 0x75, 0x0B, 0xF1, 0x37,
+ 0x89, 0xA6, 0x99, 0x97, 0x96, 0x23, 0x4E, 0x9E,
+ 0x24, 0x07, 0x15, 0xB2, 0x67, 0x67, 0x78, 0x2B,
+ 0x85, 0xA6, 0x4D, 0x68, 0x0C, 0x6D, 0x4C, 0xD4,
+ 0x26, 0xAD, 0x72, 0xB2, 0xFC, 0xE0, 0x81, 0xE8
+ },
+ {
+ 0xCE, 0xCD, 0x14, 0x01, 0x50, 0x15, 0x7D, 0xC9,
+ 0x06, 0xC0, 0xFF, 0x7F, 0x87, 0xC0, 0x08, 0x8F,
+ 0x31, 0x64, 0x80, 0x78, 0x3B, 0x4F, 0xE0, 0xA5,
+ 0x94, 0x45, 0x10, 0xC6, 0x4A, 0x87, 0xE3, 0xED,
+ 0x06, 0x67, 0x97, 0xA2, 0x7C, 0xE9, 0xD0, 0xF2,
+ 0x84, 0xDC, 0xA5, 0x18, 0x44, 0x18, 0x08, 0xAC,
+ 0x18, 0x29, 0x0A, 0xFD, 0xC0, 0x31, 0x29, 0x4B,
+ 0x31, 0xAA, 0x8B, 0x4A, 0x9F, 0xCD, 0x78, 0xF8
+ },
+ {
+ 0x2A, 0x2B, 0xED, 0x5D, 0x6A, 0xC0, 0x89, 0x28,
+ 0x11, 0xA4, 0x09, 0xD9, 0xF1, 0xFF, 0x63, 0x03,
+ 0xCC, 0xF9, 0x55, 0x44, 0x57, 0x46, 0x99, 0xCD,
+ 0xA7, 0xF7, 0x35, 0x03, 0x01, 0xF6, 0xD0, 0xC4,
+ 0xE8, 0x6E, 0x63, 0x5C, 0x80, 0x87, 0x56, 0x66,
+ 0xE2, 0xBB, 0x39, 0x07, 0x51, 0x0D, 0x0E, 0x72,
+ 0x12, 0x0F, 0x04, 0x86, 0x5E, 0xDC, 0x4C, 0x6C,
+ 0xEE, 0xCB, 0x44, 0x62, 0xD6, 0xAF, 0x60, 0xFB
+ },
+ {
+ 0x03, 0x85, 0xAE, 0x9B, 0x73, 0x5D, 0xC5, 0x9F,
+ 0x30, 0x4D, 0x41, 0x4C, 0xA0, 0x43, 0x74, 0x9A,
+ 0xB5, 0x1A, 0xB6, 0x65, 0xEE, 0x01, 0xBE, 0x5E,
+ 0x52, 0xDC, 0xF7, 0x25, 0xEE, 0x7D, 0xFE, 0xFE,
+ 0xA6, 0xAD, 0x73, 0xF3, 0x35, 0xEE, 0xCF, 0x2A,
+ 0x51, 0x02, 0xE8, 0x88, 0x07, 0xFD, 0xC7, 0x5A,
+ 0xE6, 0xDC, 0x49, 0x0D, 0x7B, 0x8B, 0x5F, 0x11,
+ 0x63, 0x03, 0xEF, 0x60, 0xA5, 0xF1, 0x7C, 0x06
+ },
+ {
+ 0x0C, 0xA3, 0xFF, 0x03, 0x89, 0x65, 0xC0, 0x3B,
+ 0xC6, 0x5B, 0xBE, 0x2D, 0x86, 0x6C, 0xE9, 0xE0,
+ 0xE4, 0xE7, 0xD0, 0x3D, 0xC7, 0xF8, 0x6B, 0xA5,
+ 0x65, 0x0F, 0x82, 0xDD, 0xB3, 0xA9, 0xAA, 0x84,
+ 0x6B, 0x2B, 0x1F, 0x55, 0x3B, 0xD8, 0x9F, 0xB4,
+ 0xF9, 0xB6, 0x2E, 0x3C, 0x7F, 0xAF, 0x9E, 0xC3,
+ 0x10, 0x9F, 0xA9, 0x0E, 0xE5, 0x6C, 0x24, 0x63,
+ 0xE6, 0xEF, 0xD1, 0xAB, 0xAD, 0x8E, 0x28, 0xE6
+ },
+ {
+ 0x6D, 0xFD, 0x4F, 0x22, 0x18, 0x4E, 0xD0, 0x91,
+ 0xFD, 0x5A, 0xBA, 0x03, 0x9F, 0xCD, 0x3D, 0xB9,
+ 0x22, 0xF5, 0xE5, 0x9B, 0xF8, 0x38, 0xC0, 0x37,
+ 0x35, 0x7F, 0xAD, 0x93, 0x4B, 0x45, 0x10, 0x60,
+ 0x3F, 0x43, 0xA7, 0x31, 0x9F, 0xFF, 0xA6, 0x23,
+ 0x86, 0xF8, 0x78, 0x8F, 0xDF, 0x9D, 0xED, 0x40,
+ 0xC6, 0x66, 0xB4, 0xBD, 0xCA, 0x86, 0xD9, 0x32,
+ 0x8F, 0xE5, 0x5A, 0xD8, 0x6B, 0x37, 0x2F, 0xC8
+ },
+ {
+ 0xA3, 0x18, 0x97, 0x61, 0x02, 0x74, 0x7D, 0x80,
+ 0x0F, 0x58, 0x4D, 0xF6, 0x5B, 0xFB, 0x44, 0x3B,
+ 0x85, 0x6F, 0x00, 0x9E, 0x74, 0xF7, 0x29, 0x46,
+ 0xD0, 0x07, 0x6C, 0xED, 0xAC, 0x04, 0x37, 0x6F,
+ 0xAB, 0x97, 0x34, 0x53, 0xAD, 0xAD, 0xC3, 0x10,
+ 0xF7, 0x20, 0x81, 0xCB, 0xBA, 0x96, 0x26, 0x4F,
+ 0xFE, 0x2B, 0x21, 0xA3, 0xB1, 0x8B, 0xE9, 0xD8,
+ 0x8C, 0x42, 0x46, 0xCB, 0xA6, 0xD3, 0x09, 0x01
+ },
+ {
+ 0xB5, 0xE6, 0xE4, 0xFC, 0xA0, 0xCF, 0x98, 0x48,
+ 0xA0, 0x05, 0x89, 0xC6, 0x54, 0x57, 0xDB, 0x68,
+ 0xB3, 0x25, 0x3A, 0x6E, 0x17, 0x78, 0x85, 0x41,
+ 0x47, 0x2E, 0x1F, 0xB9, 0x48, 0x17, 0xF8, 0x04,
+ 0x05, 0x4D, 0x07, 0xA5, 0xD3, 0x2D, 0xFA, 0x0C,
+ 0xDB, 0x6F, 0xB4, 0x4E, 0xED, 0x50, 0xD2, 0x0E,
+ 0x5F, 0x22, 0x64, 0x36, 0x11, 0x32, 0xFA, 0x5F,
+ 0xCF, 0xD6, 0xE1, 0xB3, 0x67, 0xC1, 0xBE, 0x28
+ },
+ {
+ 0x2E, 0xA4, 0x57, 0x38, 0x29, 0x25, 0xE0, 0x3C,
+ 0xF8, 0x11, 0x10, 0x05, 0x0E, 0x63, 0x6A, 0xD6,
+ 0x78, 0xE0, 0xAA, 0x3C, 0xBC, 0x69, 0x00, 0xBD,
+ 0xEF, 0x27, 0x8A, 0xAA, 0x18, 0xF2, 0x35, 0xE2,
+ 0x51, 0x60, 0xA2, 0x0E, 0x23, 0xFE, 0x0E, 0x62,
+ 0xA8, 0x51, 0x1B, 0x5D, 0xD0, 0x59, 0x2F, 0x79,
+ 0xCB, 0xC8, 0xEB, 0x7D, 0xEA, 0x64, 0xAC, 0x86,
+ 0x67, 0x49, 0x43, 0x45, 0xC6, 0x89, 0x2D, 0xD4
+ },
+ {
+ 0x96, 0xB3, 0x49, 0x8B, 0xCC, 0xD7, 0x8B, 0x5A,
+ 0x40, 0x1B, 0x27, 0x38, 0x78, 0x7D, 0x28, 0xA9,
+ 0x8A, 0x0E, 0xDF, 0xDC, 0x7C, 0x0B, 0x5F, 0xF9,
+ 0x43, 0xCF, 0xE1, 0xB1, 0x4E, 0x9C, 0xF5, 0xD9,
+ 0xED, 0x43, 0x10, 0x7D, 0xFB, 0xDD, 0x9E, 0x97,
+ 0x28, 0xD5, 0xFD, 0xD6, 0xF7, 0x1F, 0xBC, 0x77,
+ 0x0E, 0xAD, 0xDC, 0x4F, 0x2E, 0x40, 0x9A, 0xBE,
+ 0x71, 0x92, 0x7B, 0xAE, 0x1F, 0x8F, 0x73, 0xD1
+ },
+ {
+ 0xCE, 0x1B, 0xFB, 0x9A, 0xFE, 0xD2, 0x8A, 0xF4,
+ 0xDC, 0x75, 0x35, 0xAD, 0xEF, 0x71, 0xB8, 0xF1,
+ 0xB8, 0x0A, 0x8D, 0x72, 0x94, 0xB4, 0x11, 0xFD,
+ 0x1E, 0xD3, 0x93, 0xCF, 0x23, 0x2D, 0x3A, 0x5C,
+ 0x5D, 0xF2, 0x3D, 0xBB, 0x1D, 0xB2, 0x6D, 0xDD,
+ 0xF6, 0xF7, 0x45, 0xF8, 0xBC, 0x24, 0xC3, 0x78,
+ 0x1F, 0x2D, 0xBB, 0xC8, 0x18, 0xA0, 0x0A, 0xE1,
+ 0xFB, 0x9D, 0x64, 0x63, 0xE9, 0x5F, 0x29, 0x86
+ },
+ {
+ 0xE6, 0x4D, 0x37, 0x35, 0x6B, 0x29, 0x6B, 0x36,
+ 0x93, 0x0E, 0xAB, 0xE4, 0x54, 0xDB, 0x11, 0xB2,
+ 0x09, 0x7B, 0x0C, 0x04, 0x0B, 0xED, 0x57, 0x98,
+ 0x87, 0x8D, 0x38, 0xA8, 0xC4, 0xD1, 0xC6, 0xF3,
+ 0x26, 0x1F, 0x36, 0xBF, 0xF7, 0x64, 0xE3, 0xB4,
+ 0xD6, 0x06, 0xB3, 0x17, 0xE5, 0xFF, 0x50, 0x04,
+ 0x18, 0x45, 0x92, 0xB0, 0xB7, 0xDD, 0xFB, 0x8C,
+ 0x2F, 0xD8, 0x35, 0x23, 0x26, 0xCD, 0xDD, 0xB1
+ },
+ {
+ 0x85, 0xE6, 0xFE, 0x54, 0xE1, 0xE7, 0x60, 0x46,
+ 0xAF, 0x68, 0xF5, 0xC6, 0x04, 0x4C, 0x1E, 0x3F,
+ 0xFF, 0x3B, 0xFC, 0xA0, 0xBA, 0xEC, 0xAE, 0xF6,
+ 0xA1, 0xDF, 0x90, 0x35, 0x0D, 0xF2, 0xB0, 0xBE,
+ 0xC6, 0xA4, 0x20, 0xEE, 0x8F, 0x49, 0xAD, 0x44,
+ 0x64, 0xEC, 0x4C, 0x1E, 0x7D, 0x71, 0xF6, 0x67,
+ 0x61, 0x4A, 0xCE, 0xBD, 0xAD, 0xA3, 0xDF, 0x32,
+ 0x07, 0x79, 0x07, 0x83, 0x23, 0xF6, 0xA8, 0xAF
+ },
+ {
+ 0xB1, 0x2F, 0xF1, 0xEB, 0x3B, 0xAB, 0x32, 0x0D,
+ 0x78, 0x55, 0xB5, 0x49, 0xD7, 0x2B, 0x72, 0x47,
+ 0x59, 0x91, 0x68, 0x11, 0xCB, 0xCF, 0x3E, 0x1A,
+ 0x12, 0x82, 0x3F, 0x98, 0xB6, 0x4A, 0xB5, 0xC4,
+ 0x59, 0x41, 0x61, 0x0F, 0x6B, 0x47, 0x1E, 0x35,
+ 0xFF, 0x79, 0x28, 0x29, 0xDD, 0x5A, 0xDE, 0x51,
+ 0x79, 0x12, 0x57, 0x38, 0xF3, 0xF2, 0x37, 0x28,
+ 0x63, 0x0F, 0x1E, 0xEC, 0x57, 0x77, 0x5A, 0x19
+ },
+ {
+ 0xB4, 0xDB, 0xE7, 0x2A, 0x1E, 0x21, 0x69, 0x7A,
+ 0x47, 0x44, 0xBE, 0x65, 0x00, 0x0C, 0xB1, 0xBA,
+ 0xD3, 0x7C, 0xE2, 0x14, 0x16, 0xEE, 0x6F, 0xCE,
+ 0xA8, 0x4E, 0xBA, 0xF1, 0x2A, 0x59, 0xC1, 0x1D,
+ 0x7C, 0x08, 0x0D, 0xF9, 0x2F, 0xB2, 0xAA, 0x8F,
+ 0x1C, 0x4E, 0xE8, 0xE2, 0xA2, 0x2D, 0x30, 0xBE,
+ 0x49, 0x85, 0x82, 0xD7, 0xC5, 0xFB, 0xBA, 0x16,
+ 0x5A, 0x47, 0x26, 0x89, 0xAF, 0xF6, 0x01, 0xB6
+ },
+ {
+ 0x34, 0x82, 0x18, 0xBE, 0x4D, 0xE0, 0x8D, 0xFB,
+ 0x24, 0x5B, 0xF2, 0x52, 0x86, 0xE3, 0x66, 0x18,
+ 0x63, 0x1D, 0x3B, 0xDB, 0x58, 0x27, 0xD9, 0xF7,
+ 0x4F, 0xA0, 0x43, 0x01, 0x66, 0x11, 0x31, 0xA4,
+ 0xD5, 0x5C, 0x76, 0x09, 0xB1, 0xA6, 0xA0, 0x3B,
+ 0x85, 0x3F, 0x07, 0x33, 0xE0, 0xAE, 0xC0, 0x26,
+ 0x16, 0xA0, 0xA4, 0x0E, 0x84, 0x91, 0xF4, 0x94,
+ 0xD7, 0x6C, 0x15, 0x43, 0xCF, 0xC6, 0x82, 0x14
+ },
+ {
+ 0x42, 0x87, 0xE1, 0x9B, 0xAB, 0x1D, 0x4F, 0x75,
+ 0xE1, 0xD1, 0x97, 0xCB, 0xB4, 0x3F, 0x11, 0x33,
+ 0x13, 0x07, 0xF2, 0xF7, 0x5B, 0x8D, 0x0D, 0x50,
+ 0x27, 0x8E, 0xEC, 0x54, 0x09, 0x99, 0xA0, 0x09,
+ 0xC0, 0x33, 0x73, 0x52, 0x96, 0x07, 0xFD, 0xA6,
+ 0x05, 0xAA, 0x0F, 0x07, 0x39, 0xE2, 0x0B, 0xD1,
+ 0xFD, 0xAA, 0x27, 0xD7, 0xC0, 0xCD, 0xC8, 0x28,
+ 0x4D, 0x98, 0xE6, 0xC7, 0x55, 0xA7, 0x56, 0x2E
+ },
+ {
+ 0x08, 0x56, 0x0C, 0x99, 0x88, 0xC8, 0xCE, 0x5A,
+ 0x88, 0x76, 0xA6, 0x00, 0xB6, 0xE5, 0x12, 0xB4,
+ 0xE2, 0x43, 0xA4, 0xA4, 0x30, 0x0A, 0xD5, 0xAB,
+ 0x2F, 0xF0, 0x63, 0x7C, 0xC5, 0x6A, 0x04, 0x41,
+ 0x64, 0x5B, 0x3D, 0xEB, 0x16, 0x84, 0x06, 0x4E,
+ 0xA4, 0x3B, 0xAE, 0x1C, 0xB6, 0x2D, 0x3B, 0xC4,
+ 0x15, 0x37, 0xFE, 0x8D, 0x7D, 0xEC, 0xA7, 0x17,
+ 0x29, 0x37, 0x77, 0x6B, 0xBE, 0xD7, 0x93, 0xA9
+ },
+ {
+ 0xB5, 0x36, 0x16, 0x23, 0x94, 0x77, 0x6F, 0xA7,
+ 0xDD, 0x5E, 0x9F, 0xDD, 0x01, 0x53, 0x0F, 0xDA,
+ 0x52, 0xBE, 0x1D, 0x39, 0xBD, 0x60, 0x9B, 0x3F,
+ 0x3B, 0xD0, 0x47, 0x6B, 0x81, 0x60, 0xAA, 0x18,
+ 0xAB, 0x2D, 0x37, 0xD2, 0x99, 0x16, 0x28, 0xBE,
+ 0x2F, 0xCC, 0x12, 0x56, 0xCD, 0x48, 0x55, 0x25,
+ 0xD1, 0xFA, 0x35, 0x6B, 0x04, 0xD3, 0x0E, 0x4A,
+ 0x0F, 0x9F, 0xFF, 0xC9, 0x93, 0x5C, 0xF4, 0x32
+ },
+ {
+ 0x02, 0xAB, 0xC9, 0x71, 0x75, 0xED, 0xB4, 0x7A,
+ 0x4C, 0xB4, 0xBD, 0x38, 0xD8, 0x2F, 0x86, 0xAA,
+ 0x09, 0x9C, 0x8B, 0x8F, 0xA8, 0xAB, 0x3F, 0xE1,
+ 0xCE, 0x10, 0x5A, 0x22, 0xBD, 0x61, 0x65, 0x78,
+ 0xC6, 0xDD, 0x15, 0x15, 0xDF, 0xB0, 0x39, 0x7E,
+ 0x1D, 0x9D, 0x06, 0x71, 0x91, 0x6D, 0xE4, 0xB5,
+ 0x22, 0xE7, 0x4E, 0x63, 0x75, 0x23, 0x68, 0x93,
+ 0xC8, 0xFD, 0xA6, 0xD2, 0x36, 0xBC, 0x8D, 0xA1
+ },
+ {
+ 0x21, 0xE1, 0xEB, 0x73, 0x12, 0x76, 0xA8, 0x35,
+ 0xA6, 0xDD, 0xEA, 0x71, 0x78, 0xB2, 0x3E, 0xBC,
+ 0x9A, 0xEC, 0xAA, 0xBC, 0x7C, 0xCD, 0x70, 0x65,
+ 0x87, 0xD7, 0x1B, 0x85, 0x44, 0x97, 0x93, 0xB0,
+ 0x7E, 0x7B, 0x17, 0x9A, 0x3D, 0xA7, 0xA5, 0x71,
+ 0x98, 0x29, 0x97, 0xE8, 0xF5, 0xA6, 0x7F, 0x8C,
+ 0x93, 0xDA, 0xF1, 0x1A, 0xAA, 0x23, 0xF0, 0x7E,
+ 0x4D, 0xF7, 0xA1, 0x31, 0x05, 0xA5, 0x42, 0x09
+ },
+ {
+ 0x1C, 0xC5, 0x37, 0xD3, 0xE5, 0x0E, 0xD9, 0xFD,
+ 0xCD, 0xC4, 0xF3, 0xCC, 0xB4, 0x81, 0x93, 0x75,
+ 0x41, 0x53, 0x04, 0xD8, 0xE5, 0xA6, 0xC0, 0x58,
+ 0x05, 0xB6, 0xB5, 0xD9, 0xE1, 0xFC, 0x18, 0x25,
+ 0x68, 0x64, 0xF1, 0x0C, 0xD8, 0x12, 0xF8, 0x48,
+ 0x01, 0xB8, 0x61, 0x6A, 0x92, 0xB4, 0x07, 0x95,
+ 0xA1, 0x55, 0x93, 0x24, 0x64, 0xF6, 0x2D, 0xBF,
+ 0x6E, 0xBD, 0x2F, 0x9A, 0xC3, 0xEE, 0x28, 0x16
+ },
+ {
+ 0x6F, 0x6C, 0xD2, 0x60, 0x05, 0xC8, 0xA5, 0x61,
+ 0xCF, 0xF5, 0x1E, 0x30, 0x1D, 0x1A, 0x06, 0x8F,
+ 0xC2, 0x8B, 0x9B, 0x65, 0x0D, 0xDD, 0x27, 0xAE,
+ 0x97, 0xB5, 0x22, 0xDA, 0xE9, 0x63, 0x91, 0x34,
+ 0xD5, 0xA1, 0x50, 0x58, 0x7B, 0x0A, 0x90, 0x1F,
+ 0x3B, 0x9A, 0xAB, 0xC7, 0xE3, 0x97, 0x84, 0x98,
+ 0x4C, 0xC5, 0x85, 0x23, 0x5D, 0x8E, 0x17, 0xCE,
+ 0x9E, 0x3B, 0x42, 0x10, 0x5B, 0xF9, 0x03, 0x4C
+ },
+ {
+ 0x69, 0xC1, 0x7C, 0x28, 0x64, 0xC3, 0x37, 0x9F,
+ 0xAF, 0xB7, 0x14, 0xC0, 0x47, 0x5E, 0x00, 0xCF,
+ 0x7C, 0x9B, 0x37, 0x7D, 0x57, 0xA8, 0xBC, 0x96,
+ 0x98, 0xB4, 0xD3, 0x4A, 0x54, 0x85, 0x41, 0x76,
+ 0xA2, 0xF8, 0xD1, 0x5A, 0xFB, 0x54, 0x77, 0x56,
+ 0x04, 0x78, 0x73, 0x90, 0xD6, 0x00, 0x74, 0xCD,
+ 0x4B, 0xCA, 0x69, 0x02, 0xEA, 0x23, 0xD3, 0xAE,
+ 0x1A, 0xC0, 0x83, 0x40, 0x9F, 0xE3, 0x8A, 0x4D
+ },
+ {
+ 0x86, 0x69, 0xB0, 0xAD, 0x35, 0x82, 0x9E, 0xDC,
+ 0x2A, 0x8A, 0x09, 0x85, 0x2B, 0x0E, 0xE9, 0xB3,
+ 0x90, 0x3B, 0xF6, 0xC1, 0xF8, 0x2F, 0x90, 0xA3,
+ 0xF0, 0xED, 0x95, 0x24, 0x19, 0x2F, 0x10, 0x91,
+ 0xFD, 0x64, 0x84, 0xE0, 0x4C, 0x3F, 0xEA, 0x8B,
+ 0x02, 0x2F, 0x4A, 0x89, 0x50, 0xDB, 0x17, 0xD4,
+ 0x73, 0x41, 0x45, 0xC0, 0xCE, 0xC5, 0xDC, 0x38,
+ 0x74, 0x55, 0xC1, 0x26, 0x90, 0x3F, 0x77, 0x66
+ },
+ {
+ 0x3F, 0x35, 0xC4, 0x5D, 0x24, 0xFC, 0xFB, 0x4A,
+ 0xCC, 0xA6, 0x51, 0x07, 0x6C, 0x08, 0x00, 0x0E,
+ 0x27, 0x9E, 0xBB, 0xFF, 0x37, 0xA1, 0x33, 0x3C,
+ 0xE1, 0x9F, 0xD5, 0x77, 0x20, 0x2D, 0xBD, 0x24,
+ 0xB5, 0x8C, 0x51, 0x4E, 0x36, 0xDD, 0x9B, 0xA6,
+ 0x4A, 0xF4, 0xD7, 0x8E, 0xEA, 0x4E, 0x2D, 0xD1,
+ 0x3B, 0xC1, 0x8D, 0x79, 0x88, 0x87, 0xDD, 0x97,
+ 0x13, 0x76, 0xBC, 0xAE, 0x00, 0x87, 0xE1, 0x7E
+ },
+};
+
+
+
+
+static const uint8_t blake2bp_keyed_kat[KAT_LENGTH][BLAKE2B_OUTBYTES] =
+{
+ {
+ 0x9D, 0x94, 0x61, 0x07, 0x3E, 0x4E, 0xB6, 0x40,
+ 0xA2, 0x55, 0x35, 0x7B, 0x83, 0x9F, 0x39, 0x4B,
+ 0x83, 0x8C, 0x6F, 0xF5, 0x7C, 0x9B, 0x68, 0x6A,
+ 0x3F, 0x76, 0x10, 0x7C, 0x10, 0x66, 0x72, 0x8F,
+ 0x3C, 0x99, 0x56, 0xBD, 0x78, 0x5C, 0xBC, 0x3B,
+ 0xF7, 0x9D, 0xC2, 0xAB, 0x57, 0x8C, 0x5A, 0x0C,
+ 0x06, 0x3B, 0x9D, 0x9C, 0x40, 0x58, 0x48, 0xDE,
+ 0x1D, 0xBE, 0x82, 0x1C, 0xD0, 0x5C, 0x94, 0x0A
+ },
+ {
+ 0xFF, 0x8E, 0x90, 0xA3, 0x7B, 0x94, 0x62, 0x39,
+ 0x32, 0xC5, 0x9F, 0x75, 0x59, 0xF2, 0x60, 0x35,
+ 0x02, 0x9C, 0x37, 0x67, 0x32, 0xCB, 0x14, 0xD4,
+ 0x16, 0x02, 0x00, 0x1C, 0xBB, 0x73, 0xAD, 0xB7,
+ 0x92, 0x93, 0xA2, 0xDB, 0xDA, 0x5F, 0x60, 0x70,
+ 0x30, 0x25, 0x14, 0x4D, 0x15, 0x8E, 0x27, 0x35,
+ 0x52, 0x95, 0x96, 0x25, 0x1C, 0x73, 0xC0, 0x34,
+ 0x5C, 0xA6, 0xFC, 0xCB, 0x1F, 0xB1, 0xE9, 0x7E
+ },
+ {
+ 0xD6, 0x22, 0x0C, 0xA1, 0x95, 0xA0, 0xF3, 0x56,
+ 0xA4, 0x79, 0x5E, 0x07, 0x1C, 0xEE, 0x1F, 0x54,
+ 0x12, 0xEC, 0xD9, 0x5D, 0x8A, 0x5E, 0x01, 0xD7,
+ 0xC2, 0xB8, 0x67, 0x50, 0xCA, 0x53, 0xD7, 0xF6,
+ 0x4C, 0x29, 0xCB, 0xB3, 0xD2, 0x89, 0xC6, 0xF4,
+ 0xEC, 0xC6, 0xC0, 0x1E, 0x3C, 0xA9, 0x33, 0x89,
+ 0x71, 0x17, 0x03, 0x88, 0xE3, 0xE4, 0x02, 0x28,
+ 0x47, 0x90, 0x06, 0xD1, 0xBB, 0xEB, 0xAD, 0x51
+ },
+ {
+ 0x30, 0x30, 0x2C, 0x3F, 0xC9, 0x99, 0x06, 0x5D,
+ 0x10, 0xDC, 0x98, 0x2C, 0x8F, 0xEE, 0xF4, 0x1B,
+ 0xBB, 0x66, 0x42, 0x71, 0x8F, 0x62, 0x4A, 0xF6,
+ 0xE3, 0xEA, 0xBE, 0xA0, 0x83, 0xE7, 0xFE, 0x78,
+ 0x53, 0x40, 0xDB, 0x4B, 0x08, 0x97, 0xEF, 0xFF,
+ 0x39, 0xCE, 0xE1, 0xDC, 0x1E, 0xB7, 0x37, 0xCD,
+ 0x1E, 0xEA, 0x0F, 0xE7, 0x53, 0x84, 0x98, 0x4E,
+ 0x7D, 0x8F, 0x44, 0x6F, 0xAA, 0x68, 0x3B, 0x80
+ },
+ {
+ 0x32, 0xF3, 0x98, 0xA6, 0x0C, 0x1E, 0x53, 0xF1,
+ 0xF8, 0x1D, 0x6D, 0x8D, 0xA2, 0xEC, 0x11, 0x75,
+ 0x42, 0x2D, 0x6B, 0x2C, 0xFA, 0x0C, 0x0E, 0x66,
+ 0xD8, 0xC4, 0xE7, 0x30, 0xB2, 0x96, 0xA4, 0xB5,
+ 0x3E, 0x39, 0x2E, 0x39, 0x85, 0x98, 0x22, 0xA1,
+ 0x45, 0xAE, 0x5F, 0x1A, 0x24, 0xC2, 0x7F, 0x55,
+ 0x33, 0x9E, 0x2B, 0x4B, 0x44, 0x58, 0xE8, 0xC5,
+ 0xEB, 0x19, 0xAA, 0x14, 0x20, 0x64, 0x27, 0xAA
+ },
+ {
+ 0x23, 0x6D, 0xB9, 0x33, 0xF1, 0x8A, 0x9D, 0xBD,
+ 0x4E, 0x50, 0xB7, 0x29, 0x53, 0x90, 0x65, 0xBD,
+ 0xA4, 0x20, 0xDF, 0x97, 0xAC, 0x78, 0x0B, 0xE4,
+ 0x3F, 0x59, 0x10, 0x3C, 0x47, 0x2E, 0x0B, 0xCC,
+ 0xA6, 0xD4, 0x97, 0x38, 0x97, 0x86, 0xAF, 0x22,
+ 0xBA, 0x94, 0x30, 0xB7, 0x4D, 0x6F, 0x74, 0xB1,
+ 0x3F, 0x6F, 0x94, 0x9E, 0x25, 0x6A, 0x14, 0x0A,
+ 0xA3, 0x4B, 0x47, 0x70, 0x0B, 0x10, 0x03, 0x43
+ },
+ {
+ 0x23, 0x8C, 0x9D, 0x08, 0x02, 0x85, 0xE3, 0x54,
+ 0x35, 0xCB, 0x53, 0x15, 0x5D, 0x9F, 0x79, 0x2C,
+ 0xA1, 0xBB, 0x27, 0xDE, 0x4F, 0x9B, 0x6C, 0x87,
+ 0x26, 0xE1, 0x1C, 0x02, 0x8E, 0x7B, 0x87, 0x87,
+ 0x33, 0x54, 0x91, 0x12, 0xA3, 0x28, 0xB5, 0x0E,
+ 0x8C, 0xD8, 0xBA, 0x27, 0x87, 0x21, 0x7E, 0x46,
+ 0xB8, 0x16, 0x8D, 0x57, 0x11, 0x3D, 0xD4, 0x04,
+ 0xD9, 0x14, 0xE2, 0x9A, 0x6A, 0x54, 0x70, 0xE6
+ },
+ {
+ 0x9A, 0x02, 0x1E, 0xBD, 0x50, 0x4A, 0x97, 0x59,
+ 0x6D, 0x0E, 0x85, 0x04, 0x8A, 0xE1, 0xDA, 0x89,
+ 0x99, 0xE3, 0xA0, 0x47, 0x01, 0x6F, 0x17, 0xC6,
+ 0xC5, 0x55, 0x6C, 0x27, 0x31, 0xE9, 0xB1, 0x39,
+ 0x26, 0x1F, 0x84, 0x3F, 0xAD, 0x6B, 0xD4, 0x3F,
+ 0x7C, 0x7C, 0x58, 0x7F, 0x69, 0x8D, 0x69, 0xB6,
+ 0x82, 0xE5, 0x68, 0xB4, 0x42, 0xAC, 0x45, 0x88,
+ 0x98, 0x57, 0xB7, 0x69, 0x07, 0x34, 0xCD, 0xBB
+ },
+ {
+ 0x3A, 0xBA, 0x07, 0xAE, 0x98, 0x0E, 0x33, 0x86,
+ 0x37, 0x47, 0x9D, 0xCA, 0x1E, 0x35, 0x28, 0x00,
+ 0xF4, 0x58, 0x8E, 0x62, 0xD8, 0x23, 0x36, 0x5A,
+ 0xA6, 0x9C, 0x5B, 0x25, 0xFC, 0xE1, 0x29, 0x68,
+ 0xD2, 0x6C, 0x9B, 0xDB, 0xEE, 0x9A, 0x32, 0xBF,
+ 0xFD, 0x42, 0xE6, 0xB2, 0x2C, 0x81, 0x38, 0xA6,
+ 0x1C, 0x1F, 0xCE, 0x49, 0xFF, 0xBC, 0x19, 0x0E,
+ 0x1E, 0x15, 0x16, 0x01, 0x53, 0xCC, 0xB6, 0xB4
+ },
+ {
+ 0x77, 0x4C, 0xDF, 0x9A, 0xBB, 0x50, 0x81, 0xFE,
+ 0x07, 0xEB, 0x57, 0x25, 0xE6, 0x06, 0x9B, 0x8D,
+ 0x6C, 0x7E, 0x60, 0x04, 0xA2, 0x4D, 0x70, 0xF7,
+ 0xDF, 0xAB, 0xFC, 0x03, 0x82, 0x5B, 0xBC, 0x3B,
+ 0x30, 0xE6, 0x20, 0xB6, 0x04, 0x1F, 0x3C, 0xC2,
+ 0x89, 0x6B, 0x14, 0xAB, 0x66, 0x0A, 0xF7, 0x2E,
+ 0x24, 0x95, 0x10, 0xAC, 0x2F, 0xE8, 0x10, 0xCC,
+ 0x77, 0x63, 0xA2, 0xE5, 0xC3, 0xFC, 0xA7, 0xFC
+ },
+ {
+ 0x9E, 0x08, 0x9F, 0x51, 0x65, 0x7B, 0x29, 0xC2,
+ 0x66, 0x8E, 0x28, 0x50, 0x52, 0x4E, 0x53, 0xAE,
+ 0xAA, 0xA7, 0x30, 0x6F, 0x2A, 0xD5, 0xA2, 0x32,
+ 0xB5, 0xF0, 0x7F, 0x68, 0x8D, 0x8A, 0xB2, 0xB4,
+ 0x25, 0xDF, 0x7E, 0xA5, 0xBD, 0x3E, 0x9F, 0xFD,
+ 0x61, 0x68, 0x38, 0x90, 0x15, 0x1D, 0x78, 0xBB,
+ 0x94, 0x03, 0x11, 0x85, 0xAC, 0xA4, 0x81, 0xE2,
+ 0x14, 0x0F, 0xE3, 0x79, 0x85, 0x36, 0x76, 0x43
+ },
+ {
+ 0xB3, 0x5B, 0xD5, 0x4E, 0x4F, 0x81, 0x69, 0x6B,
+ 0x4F, 0x22, 0x31, 0x6A, 0x1E, 0x33, 0x7D, 0x98,
+ 0xD1, 0xC6, 0xB0, 0x61, 0x10, 0x99, 0x87, 0x63,
+ 0xB5, 0x91, 0x33, 0x35, 0x92, 0x3A, 0x40, 0x76,
+ 0xCB, 0x80, 0xD6, 0xD8, 0xA5, 0x18, 0x62, 0x91,
+ 0x13, 0x47, 0x7B, 0x30, 0xA1, 0x32, 0xA6, 0xB2,
+ 0x7F, 0xC1, 0xEE, 0x79, 0xF6, 0xB2, 0xE0, 0xD3,
+ 0x5D, 0x5B, 0xC2, 0x97, 0x27, 0x46, 0x3D, 0xB5
+ },
+ {
+ 0x12, 0x39, 0x30, 0xD5, 0xA4, 0xB7, 0x3B, 0x49,
+ 0x1F, 0x50, 0xE5, 0x6E, 0x2B, 0x73, 0x97, 0xA4,
+ 0x3D, 0x2E, 0x47, 0x87, 0x23, 0x76, 0x02, 0xB6,
+ 0x6F, 0xE0, 0xA8, 0x47, 0xBD, 0x13, 0xCB, 0xE8,
+ 0xB3, 0x7D, 0xC7, 0x03, 0xD7, 0xB2, 0xB4, 0xEA,
+ 0xA8, 0xBF, 0xB9, 0xA5, 0x8A, 0x7D, 0x71, 0x9C,
+ 0x90, 0x8F, 0x19, 0x66, 0xA2, 0xF1, 0x9F, 0xE6,
+ 0xEB, 0x1A, 0x78, 0x96, 0x2A, 0xFA, 0x5B, 0xF9
+ },
+ {
+ 0x08, 0x9C, 0xBC, 0x7E, 0xE1, 0xB1, 0x2C, 0x0C,
+ 0xC9, 0xC8, 0x3F, 0xF6, 0x66, 0xFE, 0xC8, 0x02,
+ 0x6B, 0xB7, 0x1B, 0x90, 0x84, 0x97, 0x9B, 0x0E,
+ 0xA8, 0xB7, 0x23, 0xBB, 0xBE, 0x8B, 0x00, 0xD4,
+ 0x10, 0x08, 0xB6, 0x04, 0x99, 0xF2, 0x4F, 0x24,
+ 0x1B, 0x63, 0x28, 0x1F, 0xE5, 0xB4, 0xD8, 0x89,
+ 0x66, 0x30, 0x9C, 0x0D, 0x7E, 0x64, 0x66, 0x91,
+ 0x05, 0xE5, 0x1E, 0x69, 0xD7, 0xAF, 0x8C, 0xE5
+ },
+ {
+ 0x6B, 0x3C, 0x67, 0x89, 0x47, 0xF6, 0x12, 0x52,
+ 0x65, 0x7C, 0x35, 0x49, 0x78, 0xC1, 0x01, 0xB2,
+ 0xFD, 0xD2, 0x72, 0x9E, 0xC3, 0x49, 0x27, 0xDD,
+ 0x5E, 0xFF, 0x0A, 0x7C, 0x0A, 0x86, 0x58, 0x26,
+ 0xE8, 0x33, 0xC3, 0x63, 0x23, 0x21, 0x31, 0xB1,
+ 0x05, 0x93, 0xBE, 0x1C, 0xCF, 0x6B, 0xA5, 0x4E,
+ 0xCC, 0x14, 0x31, 0x2F, 0x45, 0xBF, 0xFC, 0x24,
+ 0x04, 0x62, 0x9F, 0xF8, 0x02, 0x67, 0xF0, 0x94
+ },
+ {
+ 0xAA, 0x0C, 0x23, 0xEA, 0x1C, 0x6F, 0xE2, 0xE9,
+ 0x0A, 0x77, 0x18, 0xEF, 0x4A, 0xA4, 0x75, 0x1F,
+ 0xF6, 0xBE, 0xB9, 0xD4, 0x61, 0x63, 0x59, 0x5B,
+ 0x5D, 0x4F, 0xB8, 0x96, 0x00, 0x52, 0x5C, 0x5B,
+ 0x6C, 0xF1, 0x9E, 0xCD, 0xB2, 0x47, 0x78, 0x72,
+ 0xA7, 0xA1, 0x2D, 0x40, 0xE5, 0x06, 0x36, 0x08,
+ 0xE5, 0xF0, 0x00, 0x8E, 0x79, 0x72, 0xA9, 0xC0,
+ 0x1A, 0x4B, 0xE2, 0xAF, 0xE9, 0x53, 0x2F, 0x9C
+ },
+ {
+ 0x63, 0x34, 0x7A, 0xB4, 0xCB, 0xB6, 0xF2, 0x89,
+ 0x52, 0x99, 0x2C, 0x07, 0x9D, 0x18, 0xD4, 0x20,
+ 0x01, 0xB7, 0xF3, 0xA9, 0xD0, 0xFD, 0x90, 0xB0,
+ 0xA4, 0x77, 0x1F, 0x69, 0x72, 0xF0, 0xC5, 0x32,
+ 0x89, 0xC8, 0xAE, 0xE1, 0x43, 0x29, 0x4B, 0x50,
+ 0xC6, 0x34, 0x12, 0x58, 0x5C, 0xDC, 0xE4, 0xFF,
+ 0x7B, 0xED, 0x11, 0x2C, 0xD0, 0x3C, 0x9B, 0x1D,
+ 0xF3, 0xDE, 0xF0, 0xCC, 0x32, 0x0D, 0x6B, 0x70
+ },
+ {
+ 0x23, 0x96, 0xC0, 0xCB, 0x9E, 0xDA, 0xAC, 0xA9,
+ 0xD8, 0xB1, 0x04, 0x65, 0x2C, 0xB7, 0xF1, 0x25,
+ 0xF1, 0x93, 0x55, 0x1A, 0xE5, 0xD7, 0xBC, 0x94,
+ 0x63, 0x30, 0x7C, 0x9E, 0x69, 0xCA, 0x7D, 0xA2,
+ 0x3A, 0x9F, 0xBC, 0xBC, 0xB8, 0x66, 0x69, 0xD5,
+ 0xBA, 0x63, 0x43, 0x85, 0x93, 0xE1, 0x32, 0xF9,
+ 0x92, 0xB5, 0x7C, 0x00, 0x17, 0xC8, 0x6D, 0xDB,
+ 0x9B, 0x47, 0x28, 0x6E, 0xF5, 0xB6, 0x87, 0x18
+ },
+ {
+ 0xA9, 0x4B, 0x80, 0x22, 0x57, 0xFD, 0x03, 0x1E,
+ 0xE6, 0x0F, 0x1B, 0xE1, 0x84, 0x38, 0x3A, 0x76,
+ 0x32, 0x85, 0x39, 0xF9, 0xD8, 0x06, 0x08, 0x72,
+ 0xEF, 0x35, 0x73, 0xBE, 0xB6, 0xF2, 0x73, 0x68,
+ 0x08, 0x95, 0x90, 0xED, 0xBB, 0x21, 0xF4, 0xD8,
+ 0xF1, 0x81, 0xBA, 0x66, 0x20, 0x75, 0xF9, 0x19,
+ 0x05, 0x97, 0x4B, 0xEE, 0xEF, 0x1F, 0xC5, 0xCB,
+ 0x9B, 0xCF, 0xB2, 0x8A, 0xAE, 0x1E, 0x4D, 0xE3
+ },
+ {
+ 0x52, 0xC7, 0xD3, 0x39, 0x9A, 0x03, 0x80, 0x04,
+ 0xBE, 0xA5, 0x2D, 0x3E, 0xA9, 0xE9, 0x1E, 0x25,
+ 0x44, 0xC8, 0x65, 0x2A, 0xB8, 0xF5, 0x28, 0x5C,
+ 0x9D, 0x32, 0x18, 0x63, 0x7A, 0x6D, 0x9F, 0xCA,
+ 0xF0, 0xD9, 0x65, 0xB3, 0x58, 0x8E, 0xE6, 0xD7,
+ 0x3F, 0xA5, 0x99, 0xDE, 0xCA, 0x1F, 0x41, 0xDE,
+ 0xD8, 0x02, 0x5B, 0xF7, 0x76, 0x8E, 0x0E, 0x20,
+ 0x0E, 0x8C, 0xD3, 0xFF, 0x86, 0x8C, 0x38, 0x00
+ },
+ {
+ 0xB6, 0x29, 0xF5, 0x71, 0x62, 0x87, 0x6A, 0xDB,
+ 0x8F, 0xA9, 0x57, 0x2E, 0xBA, 0x4E, 0x1E, 0xCD,
+ 0x75, 0xA6, 0x56, 0x73, 0x08, 0xDE, 0x90, 0xDB,
+ 0xB8, 0xFF, 0xDE, 0x77, 0xDE, 0x82, 0x13, 0xA4,
+ 0xD7, 0xF7, 0xCB, 0x85, 0xAE, 0x1B, 0x71, 0xE6,
+ 0x45, 0x7B, 0xC4, 0xE8, 0x9C, 0x0D, 0x9D, 0xE2,
+ 0x41, 0xB6, 0xB9, 0xF3, 0x74, 0xB7, 0x34, 0x19,
+ 0x4D, 0xB2, 0xB2, 0x67, 0x02, 0xD7, 0xCB, 0x7C
+ },
+ {
+ 0x72, 0x28, 0x46, 0xDD, 0xAC, 0xAA, 0x94, 0xFD,
+ 0xE6, 0x63, 0x2A, 0x2D, 0xC7, 0xDC, 0x70, 0x8B,
+ 0xDF, 0x98, 0x31, 0x1C, 0x9F, 0xB6, 0x3C, 0x61,
+ 0xE5, 0x25, 0xFD, 0x4B, 0x0D, 0x87, 0xB6, 0x38,
+ 0x8B, 0x5A, 0xF7, 0x04, 0x20, 0x18, 0xDD, 0xCA,
+ 0x06, 0x5E, 0x8A, 0x55, 0xBB, 0xFD, 0x68, 0xEE,
+ 0x61, 0xFC, 0xD3, 0xC6, 0x87, 0x8F, 0x5B, 0x09,
+ 0xBC, 0xC2, 0x7B, 0xED, 0x61, 0xDD, 0x93, 0xED
+ },
+ {
+ 0x1C, 0xED, 0x6A, 0x0C, 0x78, 0x9D, 0xDB, 0x29,
+ 0x56, 0x78, 0xAD, 0x43, 0xA3, 0x22, 0xD8, 0x96,
+ 0x61, 0x7F, 0xDE, 0x27, 0x5F, 0x13, 0x8C, 0xCC,
+ 0xFB, 0x13, 0x26, 0xCD, 0x3F, 0x76, 0x09, 0xC2,
+ 0xAA, 0xA5, 0xEC, 0x10, 0x26, 0x97, 0x17, 0x3E,
+ 0x12, 0x1A, 0xE1, 0x63, 0x02, 0x4F, 0x42, 0x8C,
+ 0x98, 0x28, 0x35, 0xB4, 0xFA, 0x6D, 0xA6, 0xD6,
+ 0x78, 0xAE, 0xB9, 0xEE, 0x10, 0x6A, 0x3F, 0x6C
+ },
+ {
+ 0xE8, 0x69, 0x14, 0x8C, 0x05, 0x45, 0xB3, 0x58,
+ 0x0E, 0x39, 0x5A, 0xFD, 0xC7, 0x45, 0xCD, 0x24,
+ 0x3B, 0x6B, 0x5F, 0xE3, 0xB6, 0x7E, 0x29, 0x43,
+ 0xF6, 0xF8, 0xD9, 0xF2, 0x4F, 0xFA, 0x40, 0xE8,
+ 0x81, 0x75, 0x6E, 0x1C, 0x18, 0xD9, 0x2F, 0x3E,
+ 0xBE, 0x84, 0x55, 0x9B, 0x57, 0xE2, 0xEE, 0x3A,
+ 0x65, 0xD9, 0xEC, 0xE0, 0x49, 0x72, 0xB3, 0x5D,
+ 0x4C, 0x4E, 0xBE, 0x78, 0x6C, 0x88, 0xDA, 0x62
+ },
+ {
+ 0xDA, 0xDA, 0x15, 0x5E, 0x55, 0x42, 0x32, 0xB1,
+ 0x6E, 0xCA, 0xD9, 0x31, 0xCB, 0x42, 0xE3, 0x25,
+ 0xB5, 0x86, 0xDB, 0xF1, 0xCB, 0xD0, 0xCE, 0x38,
+ 0x14, 0x45, 0x16, 0x6B, 0xD1, 0xBF, 0xA3, 0x32,
+ 0x49, 0x85, 0xE7, 0x7C, 0x6F, 0x0D, 0x51, 0x2A,
+ 0x02, 0x6E, 0x09, 0xD4, 0x86, 0x1C, 0x3B, 0xB8,
+ 0x52, 0x9D, 0x72, 0x02, 0xEA, 0xC1, 0xC0, 0x44,
+ 0x27, 0x44, 0xD3, 0x7C, 0x7F, 0x5A, 0xB8, 0xAF
+ },
+ {
+ 0x2D, 0x14, 0x8C, 0x8E, 0x8F, 0x76, 0xFA, 0xAC,
+ 0x6F, 0x7F, 0x01, 0xF2, 0x03, 0x9E, 0xA0, 0x2A,
+ 0x42, 0xD9, 0x32, 0x57, 0x94, 0xC2, 0xC7, 0xA0,
+ 0x0F, 0x83, 0xF4, 0xA7, 0x79, 0x8A, 0xFB, 0xA9,
+ 0x93, 0xFF, 0x94, 0x91, 0x1E, 0x09, 0x8B, 0x00,
+ 0x1A, 0x0B, 0xDF, 0xF4, 0xC8, 0x5A, 0x2A, 0x61,
+ 0x31, 0xE0, 0xCF, 0xE7, 0x0F, 0x1D, 0x2E, 0x07,
+ 0xAF, 0x02, 0x09, 0xDA, 0x77, 0x96, 0x09, 0x1F
+ },
+ {
+ 0x99, 0x98, 0x3A, 0x75, 0x9C, 0xCF, 0x9C, 0xAC,
+ 0xAE, 0x70, 0x2D, 0xCB, 0xFC, 0xDF, 0x72, 0x04,
+ 0xDD, 0xF0, 0x33, 0x4B, 0xC6, 0x5D, 0xAD, 0x84,
+ 0x6F, 0x83, 0x1F, 0x9F, 0x9D, 0x8A, 0x45, 0x3F,
+ 0x0D, 0x24, 0x93, 0x5C, 0x4C, 0x65, 0x7F, 0xFF,
+ 0x2E, 0xBB, 0xDB, 0xAF, 0x7B, 0xCE, 0x6A, 0xAC,
+ 0xDB, 0xB8, 0x87, 0x6F, 0x16, 0x04, 0x59, 0xB1,
+ 0xA4, 0xAA, 0xC9, 0x56, 0x97, 0xE0, 0x0D, 0x98
+ },
+ {
+ 0x7E, 0x4A, 0x02, 0x12, 0x6D, 0x75, 0x52, 0xF4,
+ 0xC9, 0xB9, 0x4D, 0x80, 0xE3, 0xCF, 0x7B, 0x89,
+ 0x7E, 0x09, 0x84, 0xE4, 0x06, 0xF0, 0x78, 0x13,
+ 0x5C, 0xF4, 0x56, 0xC0, 0xD5, 0x1E, 0x13, 0x91,
+ 0xFF, 0x18, 0xA8, 0x8F, 0x93, 0x12, 0x2C, 0x83,
+ 0x2C, 0xAC, 0x7D, 0x79, 0x6A, 0x6B, 0x42, 0x51,
+ 0x9B, 0x1D, 0xB4, 0xEA, 0xD8, 0xF4, 0x98, 0x40,
+ 0xCE, 0xB5, 0x52, 0x33, 0x6B, 0x29, 0xDE, 0x44
+ },
+ {
+ 0xD7, 0xE1, 0x6F, 0xD1, 0x59, 0x65, 0x8A, 0xD7,
+ 0xEE, 0x25, 0x1E, 0x51, 0x7D, 0xCE, 0x5A, 0x29,
+ 0xF4, 0x6F, 0xD4, 0xB8, 0xD3, 0x19, 0xDB, 0x80,
+ 0x5F, 0xC2, 0x5A, 0xA6, 0x20, 0x35, 0x0F, 0xF4,
+ 0x23, 0xAD, 0x8D, 0x05, 0x37, 0xCD, 0x20, 0x69,
+ 0x43, 0x2E, 0xBF, 0xF2, 0x92, 0x36, 0xF8, 0xC2,
+ 0xA8, 0xA0, 0x4D, 0x04, 0xB3, 0xB4, 0x8C, 0x59,
+ 0xA3, 0x55, 0xFC, 0xC6, 0x2D, 0x27, 0xF8, 0xEE
+ },
+ {
+ 0x0D, 0x45, 0x17, 0xD4, 0xF1, 0xD0, 0x47, 0x30,
+ 0xC6, 0x91, 0x69, 0x18, 0xA0, 0x4C, 0x9E, 0x90,
+ 0xCC, 0xA3, 0xAC, 0x1C, 0x63, 0xD6, 0x45, 0x97,
+ 0x8A, 0x7F, 0x07, 0x03, 0x9F, 0x92, 0x20, 0x64,
+ 0x7C, 0x25, 0xC0, 0x4E, 0x85, 0xF6, 0xE2, 0x28,
+ 0x6D, 0x2E, 0x35, 0x46, 0x0D, 0x0B, 0x2C, 0x1E,
+ 0x25, 0xAF, 0x9D, 0x35, 0x37, 0xEF, 0x33, 0xFD,
+ 0x7F, 0xE5, 0x1E, 0x2B, 0xA8, 0x76, 0x4B, 0x36
+ },
+ {
+ 0x56, 0xB7, 0x2E, 0x51, 0x37, 0xC6, 0x89, 0xB2,
+ 0x73, 0x66, 0xFB, 0x22, 0xC7, 0xC6, 0x75, 0x44,
+ 0xF6, 0xBC, 0xE5, 0x76, 0x19, 0x41, 0x31, 0xC5,
+ 0xBF, 0xAB, 0x1C, 0xF9, 0x3C, 0x2B, 0x51, 0xAA,
+ 0xA3, 0x03, 0x36, 0x8A, 0xA8, 0x44, 0xD5, 0x8D,
+ 0xF0, 0xEE, 0x5D, 0x4E, 0x31, 0x9F, 0xCD, 0x8E,
+ 0xFF, 0xC6, 0x02, 0xCE, 0xE4, 0x35, 0x1B, 0xD2,
+ 0xF5, 0x51, 0x43, 0x0B, 0x92, 0x11, 0xE7, 0x3C
+ },
+ {
+ 0xF3, 0x35, 0xCC, 0x22, 0xFF, 0xEA, 0x5A, 0xA5,
+ 0x9C, 0xDF, 0xC8, 0xF5, 0x02, 0x89, 0xCC, 0x92,
+ 0x31, 0x9B, 0x8B, 0x14, 0x40, 0x8D, 0x7A, 0x5A,
+ 0xA1, 0x23, 0x2A, 0xE2, 0x3A, 0xA1, 0xEA, 0x7F,
+ 0x77, 0x48, 0xCF, 0xEF, 0x03, 0x20, 0x10, 0xF8,
+ 0x62, 0x6D, 0x93, 0x18, 0xED, 0xBA, 0x98, 0xD4,
+ 0x16, 0x62, 0x03, 0x35, 0xC9, 0x01, 0xED, 0x02,
+ 0xEA, 0xBD, 0x27, 0x6A, 0x1B, 0x82, 0x9C, 0x9D
+ },
+ {
+ 0xA9, 0x9A, 0x3D, 0x10, 0xF9, 0x5B, 0x44, 0x2F,
+ 0xFF, 0xF7, 0xC4, 0x18, 0xFA, 0x94, 0x9D, 0x48,
+ 0x30, 0x86, 0x9B, 0x0E, 0x60, 0xEC, 0x8B, 0x97,
+ 0x2C, 0x30, 0xA3, 0x16, 0x9C, 0x27, 0xBE, 0xB5,
+ 0xCF, 0x33, 0x05, 0x94, 0xF0, 0x14, 0xB6, 0x6B,
+ 0x22, 0x00, 0xA7, 0xF0, 0x86, 0xD2, 0xC2, 0xF3,
+ 0xF9, 0xFD, 0x85, 0x32, 0xA5, 0x71, 0x88, 0x76,
+ 0xDF, 0xCA, 0x66, 0x1B, 0xA0, 0xF7, 0xB3, 0x6D
+ },
+ {
+ 0x15, 0x8E, 0x25, 0x70, 0xD0, 0x84, 0xA4, 0x86,
+ 0x9D, 0x96, 0x93, 0x43, 0xC0, 0x10, 0x86, 0x07,
+ 0x17, 0xFF, 0x74, 0x11, 0x61, 0x88, 0x17, 0x5F,
+ 0x2E, 0xD7, 0x4C, 0xD5, 0x78, 0xFA, 0x0D, 0x80,
+ 0x91, 0xB0, 0x3F, 0xAD, 0x0C, 0x65, 0xCF, 0x59,
+ 0xAB, 0x91, 0xDD, 0x73, 0xB3, 0x7F, 0xE3, 0xF5,
+ 0x8A, 0x58, 0xE7, 0xB4, 0x47, 0x9C, 0x87, 0x5A,
+ 0xCD, 0x63, 0xEC, 0x52, 0x58, 0x12, 0x35, 0x3F
+ },
+ {
+ 0x7C, 0x49, 0x50, 0x1C, 0x58, 0x08, 0xB1, 0x5C,
+ 0x0D, 0x31, 0xBD, 0xD5, 0xBB, 0x56, 0x31, 0xD5,
+ 0x3A, 0xE0, 0x0D, 0xF4, 0x31, 0x02, 0x5F, 0xEA,
+ 0x51, 0xEB, 0x47, 0x62, 0x54, 0x4E, 0xFD, 0xEE,
+ 0x97, 0x8A, 0x83, 0x50, 0x8D, 0xEA, 0x6B, 0xFD,
+ 0x3B, 0x93, 0x1A, 0x0E, 0x95, 0x83, 0xCC, 0xFC,
+ 0x04, 0x9E, 0xA8, 0x46, 0x44, 0x70, 0x5D, 0x31,
+ 0x9F, 0xDC, 0x5C, 0x16, 0x3B, 0xF4, 0x82, 0x24
+ },
+ {
+ 0xFE, 0xF4, 0x36, 0xB3, 0x5F, 0x71, 0x7D, 0x59,
+ 0xAC, 0xA1, 0x7E, 0x9B, 0xF5, 0xFF, 0xDA, 0x28,
+ 0xF5, 0xF4, 0x01, 0x94, 0x3E, 0xFE, 0x93, 0xEB,
+ 0x58, 0x0F, 0xFB, 0x98, 0xF1, 0x3B, 0xEA, 0x80,
+ 0x94, 0x69, 0xA3, 0x44, 0xE7, 0x82, 0xA4, 0x43,
+ 0xC6, 0x4E, 0xB2, 0x5A, 0xD0, 0x9D, 0x8D, 0xE2,
+ 0x05, 0xFE, 0xE7, 0xD5, 0x63, 0x96, 0x86, 0xA1,
+ 0x9E, 0x7C, 0x42, 0xB4, 0x0F, 0x70, 0x6A, 0x08
+ },
+ {
+ 0x4D, 0x47, 0xA6, 0x7A, 0x5F, 0x8E, 0x17, 0xB7,
+ 0x22, 0xDF, 0x98, 0x58, 0xAE, 0xB6, 0x7B, 0x99,
+ 0x56, 0xB4, 0x59, 0x62, 0xEC, 0x35, 0x3D, 0xC2,
+ 0xE2, 0x7F, 0x0F, 0x50, 0x1C, 0x39, 0x8E, 0x34,
+ 0x39, 0x7B, 0xEB, 0xE0, 0x2B, 0x54, 0x92, 0x7E,
+ 0x2D, 0x31, 0xF1, 0x2E, 0xCF, 0x55, 0xE8, 0x82,
+ 0x69, 0xFA, 0xB5, 0x37, 0x0E, 0x7F, 0xA5, 0x70,
+ 0x35, 0x26, 0x6F, 0x89, 0xD5, 0xC2, 0x64, 0x41
+ },
+ {
+ 0x1B, 0x58, 0xDC, 0x7A, 0xAC, 0x36, 0x3B, 0x00,
+ 0x44, 0x6E, 0xA8, 0x03, 0xBC, 0xD7, 0x49, 0xC3,
+ 0xF5, 0xCA, 0xBE, 0xAA, 0xF2, 0x23, 0x99, 0x4C,
+ 0x0C, 0x3E, 0xCC, 0x1B, 0x28, 0x47, 0x73, 0x44,
+ 0xD7, 0xBF, 0x97, 0xC0, 0x8A, 0x95, 0x9D, 0x1A,
+ 0xC2, 0x06, 0x0B, 0x47, 0x27, 0x89, 0x86, 0x92,
+ 0x91, 0x88, 0xAD, 0x73, 0xDE, 0x67, 0x07, 0x8B,
+ 0xA6, 0x80, 0x96, 0x3B, 0x9D, 0x3B, 0x12, 0xA4
+ },
+ {
+ 0x3C, 0x52, 0x2C, 0x84, 0x3E, 0x69, 0x74, 0xEC,
+ 0x75, 0x0D, 0xF2, 0x20, 0xD4, 0x1A, 0x00, 0x4A,
+ 0xC2, 0xAD, 0xF0, 0x94, 0x56, 0xFA, 0x78, 0x7F,
+ 0x7C, 0x65, 0x43, 0xAB, 0x17, 0x97, 0x9C, 0x77,
+ 0x7B, 0x3E, 0x79, 0xD1, 0x78, 0x7D, 0xA5, 0xA8,
+ 0x3F, 0x17, 0x8D, 0xA9, 0xF0, 0x4C, 0xF6, 0xF5,
+ 0xB2, 0x55, 0xDD, 0xCB, 0x18, 0x74, 0x84, 0x1B,
+ 0xBF, 0x70, 0x16, 0xE6, 0x13, 0x2B, 0x99, 0x8A
+ },
+ {
+ 0x5A, 0x4F, 0xEB, 0x8F, 0x70, 0x75, 0xB4, 0xDC,
+ 0x9C, 0xA1, 0x6C, 0x6F, 0x05, 0xCD, 0x6B, 0x70,
+ 0x27, 0x48, 0x5F, 0xFE, 0xD9, 0x15, 0x7D, 0x82,
+ 0x4D, 0x9D, 0x1A, 0x17, 0x20, 0xEE, 0xEE, 0xEA,
+ 0x3F, 0x6C, 0x12, 0x5F, 0xDA, 0x4B, 0xA4, 0x40,
+ 0x9D, 0x79, 0x80, 0x49, 0xFD, 0x18, 0x82, 0xC6,
+ 0x90, 0x28, 0x8F, 0x33, 0x54, 0x7A, 0x3D, 0x8D,
+ 0x62, 0x60, 0xB6, 0x54, 0x54, 0x88, 0x53, 0xD7
+ },
+ {
+ 0xBC, 0xAA, 0x79, 0x36, 0x32, 0x56, 0x9E, 0x2F,
+ 0x84, 0x17, 0xCC, 0x60, 0x32, 0x53, 0x53, 0x5B,
+ 0xD7, 0xD8, 0x5F, 0x38, 0x53, 0x19, 0x92, 0x59,
+ 0x1E, 0x56, 0xC1, 0xA4, 0xB6, 0xF5, 0x8E, 0xE7,
+ 0xF8, 0x18, 0xFA, 0xE0, 0x27, 0x88, 0x8A, 0x86,
+ 0x28, 0x43, 0x05, 0x10, 0x1E, 0xC0, 0x46, 0x61,
+ 0xF5, 0x99, 0x53, 0x47, 0xA4, 0x67, 0xED, 0x8B,
+ 0x92, 0x79, 0xF1, 0xAC, 0xC2, 0xB4, 0xBB, 0x1F
+ },
+ {
+ 0x34, 0xAF, 0x91, 0xCC, 0x22, 0xA6, 0x9B, 0xCB,
+ 0x55, 0xDD, 0xBF, 0x7F, 0x0F, 0x43, 0xEC, 0x56,
+ 0x48, 0x40, 0x43, 0x32, 0x13, 0xEA, 0x55, 0xD9,
+ 0xF8, 0x1A, 0xC4, 0x75, 0x20, 0x8D, 0x74, 0x85,
+ 0x1D, 0xB7, 0x0F, 0xE4, 0x96, 0xAF, 0x9D, 0xA1,
+ 0xD3, 0x93, 0xEC, 0xF8, 0x78, 0x69, 0x5D, 0xD3,
+ 0x3F, 0xD5, 0x43, 0x49, 0xA6, 0xF8, 0x24, 0xAE,
+ 0xED, 0x18, 0x3C, 0xB1, 0xB0, 0x8C, 0x54, 0x85
+ },
+ {
+ 0xB8, 0xB7, 0xAD, 0x2E, 0xA2, 0xB6, 0xFA, 0x06,
+ 0xD0, 0x0B, 0xCD, 0x59, 0x9C, 0x99, 0x71, 0xC5,
+ 0xB4, 0xE1, 0x65, 0x58, 0xE1, 0x52, 0x12, 0xC9,
+ 0xBF, 0xD3, 0x73, 0xE4, 0xBC, 0x79, 0x17, 0x05,
+ 0x26, 0x01, 0xFF, 0xDB, 0x68, 0x01, 0xBE, 0x80,
+ 0xBA, 0x50, 0x9D, 0xB8, 0x2A, 0x0B, 0x71, 0x95,
+ 0x92, 0x91, 0x33, 0xAD, 0x53, 0x99, 0x56, 0x06,
+ 0x52, 0x33, 0xF4, 0x9D, 0x07, 0x1C, 0x84, 0xE4
+ },
+ {
+ 0xDC, 0xEE, 0x9C, 0x45, 0xBC, 0x5D, 0x1F, 0xE6,
+ 0x30, 0xB1, 0x8B, 0x06, 0x3C, 0xE8, 0x2C, 0x38,
+ 0x57, 0xE3, 0x0D, 0x20, 0xC6, 0x4B, 0x5C, 0xC2,
+ 0x58, 0x84, 0x94, 0x3E, 0x7A, 0xE9, 0x4E, 0xDF,
+ 0xF8, 0x50, 0xEB, 0x0E, 0x82, 0x44, 0x02, 0x3D,
+ 0x3D, 0x07, 0xA8, 0xA0, 0x07, 0x06, 0xF0, 0x58,
+ 0x2C, 0xC1, 0x02, 0xB6, 0x6C, 0x6D, 0xDA, 0x86,
+ 0xE8, 0xF2, 0xDF, 0x32, 0x56, 0x59, 0x88, 0x6F
+ },
+ {
+ 0x04, 0xF6, 0xE8, 0x22, 0xF1, 0x7C, 0xC7, 0xA5,
+ 0x94, 0x6D, 0xF8, 0x0D, 0x95, 0x8A, 0xEF, 0x06,
+ 0x5D, 0x87, 0x49, 0x16, 0xE1, 0x03, 0xA6, 0x83,
+ 0x0C, 0x6E, 0x46, 0xB6, 0x05, 0x59, 0x18, 0x18,
+ 0x0D, 0x14, 0x52, 0x29, 0x3C, 0x58, 0xA9, 0x74,
+ 0x9C, 0xBC, 0x8F, 0x0A, 0xC4, 0x08, 0xA9, 0xCA,
+ 0x89, 0x57, 0x61, 0xCF, 0xC4, 0x51, 0x16, 0x46,
+ 0x41, 0xA1, 0x79, 0xFB, 0x5C, 0xD8, 0xFE, 0xBC
+ },
+ {
+ 0x51, 0x1F, 0xDB, 0x7C, 0x88, 0x26, 0x85, 0x35,
+ 0xE9, 0x7E, 0x4E, 0xD8, 0x92, 0xF3, 0xC0, 0x65,
+ 0x83, 0x2B, 0x26, 0x59, 0x14, 0xFC, 0x61, 0x07,
+ 0xA1, 0xD2, 0x7D, 0xBB, 0x7D, 0x51, 0xC3, 0x7E,
+ 0x95, 0x98, 0x15, 0x06, 0xC1, 0x14, 0x72, 0x44,
+ 0xD5, 0xBA, 0xE9, 0x0E, 0xE9, 0x0D, 0x08, 0x49,
+ 0x84, 0xBA, 0xA7, 0x58, 0x7F, 0x41, 0xFF, 0x6F,
+ 0x4B, 0xA7, 0x22, 0xC8, 0xB9, 0x2A, 0xEB, 0x99
+ },
+ {
+ 0x2B, 0xA2, 0xBD, 0x17, 0xE9, 0x26, 0x27, 0x5B,
+ 0x06, 0x83, 0xB2, 0x36, 0xBF, 0xE3, 0x76, 0x30,
+ 0x26, 0x6E, 0x37, 0xF4, 0x18, 0x2F, 0x53, 0xA9,
+ 0x82, 0x34, 0xE9, 0x15, 0xAB, 0x64, 0xC9, 0x59,
+ 0x96, 0xC6, 0xCB, 0x7A, 0xE8, 0x80, 0xC3, 0xDF,
+ 0xCB, 0x47, 0xD0, 0x5A, 0xAD, 0xD2, 0x1A, 0xBF,
+ 0x8E, 0x40, 0xB7, 0x3F, 0x40, 0xF3, 0x98, 0xDC,
+ 0x5B, 0x02, 0x14, 0x14, 0x57, 0x45, 0x6A, 0x09
+ },
+ {
+ 0x9B, 0x66, 0x8D, 0x9B, 0x44, 0x47, 0xE3, 0x76,
+ 0xF6, 0xC6, 0xCF, 0xA6, 0x8D, 0xBC, 0x79, 0x19,
+ 0x83, 0x81, 0xAB, 0x60, 0x5F, 0x55, 0xD5, 0xA7,
+ 0xEF, 0x68, 0x3B, 0xCE, 0xD4, 0x6F, 0x9A, 0xFD,
+ 0x36, 0x85, 0x41, 0x1A, 0x66, 0xE2, 0x34, 0x6F,
+ 0x96, 0x07, 0x77, 0xD0, 0xC9, 0x22, 0x71, 0x24,
+ 0x30, 0xE0, 0x18, 0xBF, 0xAE, 0x86, 0x53, 0x01,
+ 0x7E, 0xA2, 0x0E, 0xCD, 0x5F, 0x1F, 0x95, 0x6C
+ },
+ {
+ 0x56, 0x81, 0x02, 0x4F, 0x53, 0x85, 0x88, 0xA0,
+ 0x1B, 0x2C, 0x83, 0x94, 0xCA, 0xE8, 0x73, 0xC6,
+ 0xD8, 0x5D, 0x6A, 0xA0, 0x6E, 0xDD, 0xB3, 0xA5,
+ 0x02, 0x09, 0x6F, 0xC0, 0x82, 0xBB, 0x89, 0xCB,
+ 0x24, 0x15, 0x31, 0xB3, 0x15, 0x75, 0x0D, 0x31,
+ 0xBB, 0x0B, 0x63, 0x01, 0x28, 0xD1, 0x9D, 0x11,
+ 0x39, 0x2B, 0xCF, 0x4B, 0x34, 0x78, 0xD5, 0x23,
+ 0xD7, 0xD2, 0x13, 0xE4, 0x75, 0x0F, 0x55, 0x92
+ },
+ {
+ 0x2A, 0xA9, 0x1B, 0xA6, 0xDE, 0x60, 0x17, 0xF1,
+ 0x93, 0x0F, 0xC7, 0xD9, 0x6D, 0xCC, 0xD6, 0x70,
+ 0x74, 0x8B, 0x7E, 0xB1, 0xD0, 0x94, 0xDF, 0xB4,
+ 0xB3, 0xB1, 0x47, 0x8A, 0x61, 0x2E, 0xBF, 0x03,
+ 0xDD, 0xD7, 0x21, 0x27, 0x9A, 0x26, 0x6D, 0xE3,
+ 0x88, 0x45, 0xE6, 0x12, 0xC9, 0x30, 0x98, 0xC2,
+ 0xEF, 0xFF, 0x34, 0xFE, 0x50, 0x06, 0x17, 0x20,
+ 0x5B, 0x1D, 0xE2, 0xFE, 0xA1, 0xD8, 0x02, 0x46
+ },
+ {
+ 0x82, 0x4D, 0x89, 0xC0, 0x63, 0x7C, 0xE1, 0x78,
+ 0xB6, 0x30, 0x68, 0x4C, 0x72, 0x9E, 0x26, 0x65,
+ 0x3F, 0x34, 0xEA, 0xC7, 0xE9, 0x04, 0x12, 0xE9,
+ 0x63, 0xD3, 0xF1, 0x9D, 0x64, 0x51, 0xE8, 0x25,
+ 0x85, 0x21, 0x67, 0xC4, 0x8D, 0xF7, 0xCC, 0x55,
+ 0xB2, 0x57, 0xB2, 0x50, 0xA7, 0x0C, 0x7B, 0xCC,
+ 0xFA, 0x9A, 0xA1, 0x5C, 0x18, 0x8A, 0xC4, 0x63,
+ 0x7A, 0x52, 0x22, 0x89, 0xC0, 0x87, 0x6A, 0xD4
+ },
+ {
+ 0x87, 0xE4, 0xAE, 0x11, 0xDA, 0x1A, 0x2C, 0xA8,
+ 0x82, 0x2A, 0xE3, 0x30, 0xDC, 0x97, 0xAB, 0x2E,
+ 0x47, 0xFF, 0x62, 0x32, 0x30, 0x93, 0xC2, 0xB7,
+ 0xA6, 0xC0, 0xE2, 0xC1, 0x68, 0x21, 0xCD, 0x7C,
+ 0xEC, 0x92, 0x18, 0x4D, 0xF4, 0xBB, 0x6E, 0x2B,
+ 0x62, 0x6A, 0x44, 0x78, 0x03, 0x90, 0x63, 0xAF,
+ 0xEE, 0xB0, 0xD2, 0x87, 0xF2, 0x42, 0x19, 0x20,
+ 0x78, 0x98, 0xCC, 0xE7, 0xAD, 0xE0, 0x63, 0x9C
+ },
+ {
+ 0xDD, 0x7F, 0x2F, 0x44, 0xA4, 0x02, 0xA0, 0x1E,
+ 0x82, 0x16, 0xB1, 0x03, 0xA4, 0xE7, 0x23, 0x5C,
+ 0x28, 0x30, 0x31, 0x9D, 0x56, 0xAF, 0x63, 0x9F,
+ 0x23, 0xC4, 0x8C, 0x27, 0x59, 0xAB, 0xA6, 0xEB,
+ 0x5E, 0xEE, 0xE3, 0x8C, 0x29, 0x8E, 0xBE, 0x41,
+ 0x98, 0x26, 0x7A, 0x00, 0xEB, 0x2A, 0x08, 0xD9,
+ 0x3A, 0x50, 0x37, 0x03, 0x17, 0x1C, 0x77, 0x33,
+ 0x38, 0x62, 0x10, 0x10, 0x55, 0xBD, 0x7A, 0xD2
+ },
+ {
+ 0x4C, 0xB8, 0x46, 0x59, 0x61, 0x93, 0xF7, 0xF2,
+ 0x78, 0xAA, 0xAA, 0xC5, 0xCC, 0xFF, 0xD5, 0x35,
+ 0x7A, 0xB0, 0xD1, 0x24, 0x5F, 0x69, 0x79, 0xD1,
+ 0x41, 0xA4, 0x71, 0xBD, 0xAB, 0x55, 0xE2, 0x38,
+ 0xB1, 0xAE, 0xD6, 0x7B, 0x73, 0x39, 0x95, 0x04,
+ 0xB9, 0x7D, 0xF1, 0xA2, 0x5E, 0xB6, 0xFE, 0x27,
+ 0x2B, 0x5C, 0xD4, 0x96, 0xA7, 0xC8, 0xA0, 0x60,
+ 0x92, 0x6E, 0x74, 0x04, 0xFD, 0xA0, 0x79, 0x0D
+ },
+ {
+ 0x6F, 0x44, 0xEC, 0xDA, 0xE1, 0x4E, 0x3B, 0x81,
+ 0xA1, 0x91, 0x22, 0x03, 0x01, 0x5F, 0x59, 0x18,
+ 0xEA, 0xC6, 0xFB, 0xF4, 0x96, 0x60, 0x10, 0xF4,
+ 0x9D, 0x2B, 0xC2, 0xBC, 0xEF, 0xE7, 0xB1, 0xDF,
+ 0xEC, 0x5C, 0x83, 0x5D, 0x7D, 0x87, 0xA4, 0x43,
+ 0x71, 0xF1, 0x5A, 0x6C, 0x08, 0x42, 0x52, 0xB9,
+ 0x34, 0x65, 0x26, 0x42, 0x72, 0xA4, 0x10, 0xD5,
+ 0x0F, 0x89, 0xA1, 0x17, 0xF3, 0x1A, 0xF4, 0x63
+ },
+ {
+ 0x1F, 0x70, 0x5F, 0x6E, 0x9F, 0x07, 0x0D, 0x87,
+ 0xFD, 0xE8, 0xE2, 0x77, 0x46, 0x74, 0xFA, 0x9B,
+ 0xF1, 0x20, 0xD2, 0x88, 0xEB, 0x0B, 0xE7, 0xAA,
+ 0x12, 0x8D, 0xFB, 0x5D, 0x10, 0x11, 0xCE, 0x1F,
+ 0xDA, 0x99, 0xB2, 0x55, 0x22, 0x66, 0x65, 0xD8,
+ 0x3F, 0x63, 0x4E, 0x8F, 0xCA, 0xBD, 0xA9, 0xA2,
+ 0x3C, 0x03, 0x51, 0x5E, 0x9C, 0xFE, 0xCE, 0x6E,
+ 0x94, 0xA8, 0xEC, 0x92, 0xE4, 0xED, 0xEC, 0xB7
+ },
+ {
+ 0x2D, 0x96, 0xC5, 0xB0, 0x15, 0x74, 0x72, 0x2B,
+ 0x81, 0x7F, 0xEB, 0x48, 0x6C, 0x5F, 0xC9, 0x8F,
+ 0x5F, 0x84, 0x61, 0xF4, 0xCE, 0xE9, 0x90, 0x5A,
+ 0xF2, 0x06, 0xD4, 0x72, 0x33, 0x86, 0xD1, 0xC4,
+ 0xC7, 0xCA, 0xC5, 0x84, 0x00, 0x28, 0xD7, 0xAF,
+ 0xED, 0x0E, 0x38, 0xAD, 0x13, 0x96, 0x28, 0xEB,
+ 0x6A, 0xF9, 0x2B, 0x4B, 0x88, 0xEB, 0xF0, 0x9B,
+ 0x1F, 0xA0, 0x47, 0xFB, 0xE1, 0x0B, 0xC3, 0x1D
+ },
+ {
+ 0x65, 0xDA, 0x78, 0x0A, 0x0A, 0x37, 0x47, 0x9D,
+ 0xD8, 0xF4, 0xD6, 0x55, 0x64, 0xF9, 0xA7, 0x08,
+ 0x9E, 0x42, 0x07, 0xEB, 0x16, 0xAC, 0xA3, 0xF6,
+ 0x55, 0x31, 0xCF, 0xEE, 0x76, 0x25, 0xBA, 0x13,
+ 0x80, 0xA4, 0x97, 0xB6, 0x24, 0x72, 0xFC, 0x7E,
+ 0x00, 0x07, 0xA6, 0xB0, 0x35, 0x61, 0x04, 0x16,
+ 0xA5, 0xF8, 0x2C, 0x10, 0x82, 0xFA, 0x06, 0x5C,
+ 0x46, 0xDD, 0xEE, 0x49, 0x40, 0xD1, 0xFC, 0x46
+ },
+ {
+ 0x1C, 0x09, 0xA3, 0xB3, 0x80, 0xB8, 0xA7, 0xFC,
+ 0x33, 0x3F, 0xD2, 0x71, 0x4D, 0xF7, 0x12, 0x9B,
+ 0x44, 0xA4, 0x67, 0x68, 0xBA, 0xCF, 0x0A, 0x67,
+ 0xA3, 0x8A, 0x47, 0xB3, 0xAB, 0x31, 0xF5, 0x1B,
+ 0x05, 0x33, 0xC2, 0xAA, 0x2B, 0x4B, 0x7B, 0xBB,
+ 0x6A, 0xE5, 0xED, 0xF3, 0xDC, 0xB0, 0xEC, 0xC1,
+ 0xA2, 0x83, 0xE8, 0x43, 0xF2, 0x90, 0x7B, 0x34,
+ 0x1F, 0x17, 0x9A, 0xFD, 0x8B, 0x67, 0xDA, 0x90
+ },
+ {
+ 0x67, 0x88, 0x8B, 0x83, 0xFA, 0xAF, 0xBB, 0x62,
+ 0x29, 0x34, 0xB8, 0xD5, 0x59, 0x63, 0xE1, 0x86,
+ 0x15, 0x3E, 0x59, 0x51, 0x88, 0x7C, 0x7F, 0x4A,
+ 0x76, 0x35, 0xC7, 0x98, 0xD9, 0xA5, 0x82, 0x94,
+ 0xBE, 0x26, 0xA3, 0xC5, 0x49, 0xC9, 0xFD, 0x59,
+ 0x86, 0xAB, 0xD1, 0x9F, 0x40, 0x1E, 0xE2, 0x4E,
+ 0xDA, 0x36, 0x02, 0x04, 0x2A, 0xD3, 0x83, 0x35,
+ 0x7A, 0x31, 0x7D, 0x38, 0x07, 0x3B, 0x38, 0xCE
+ },
+ {
+ 0xB4, 0xF7, 0x99, 0x63, 0xCA, 0x31, 0xBB, 0x62,
+ 0x26, 0x5D, 0xD9, 0x29, 0xAF, 0x7D, 0x51, 0x27,
+ 0x2F, 0xA6, 0x63, 0x1D, 0xE7, 0xFA, 0x35, 0xF7,
+ 0xA6, 0xB0, 0x3F, 0x9F, 0xCF, 0xDB, 0x8E, 0x3B,
+ 0x5B, 0xAC, 0xE3, 0x35, 0x91, 0xB7, 0xEC, 0x2C,
+ 0xFA, 0xB4, 0x9C, 0x91, 0xA6, 0xDB, 0x1F, 0xF8,
+ 0xF6, 0x78, 0x6D, 0x08, 0xF4, 0x4E, 0x80, 0x62,
+ 0xD2, 0xFF, 0x69, 0x6A, 0x7D, 0x98, 0x41, 0x42
+ },
+ {
+ 0x40, 0x84, 0x83, 0x69, 0x7B, 0xB6, 0xF9, 0xD0,
+ 0x11, 0xA1, 0xF2, 0x9A, 0x23, 0xC2, 0x78, 0xA8,
+ 0x1D, 0x37, 0x57, 0x8D, 0xCC, 0xCF, 0x42, 0x3B,
+ 0xDF, 0x48, 0x93, 0x37, 0xF1, 0x82, 0xEA, 0xB7,
+ 0x9A, 0x50, 0xB0, 0x5F, 0x3D, 0x2C, 0xCC, 0x49,
+ 0x13, 0x37, 0xC7, 0xE4, 0x1F, 0x30, 0x79, 0x3B,
+ 0xD2, 0x7D, 0x76, 0x61, 0xC2, 0xE3, 0x04, 0xC9,
+ 0x46, 0xA5, 0xA4, 0x01, 0xAF, 0x8D, 0x94, 0x6F
+ },
+ {
+ 0xEE, 0xB5, 0xAD, 0xE1, 0xAB, 0x97, 0xE7, 0x15,
+ 0x43, 0x43, 0xA4, 0x6E, 0xB4, 0xCD, 0xD2, 0xA7,
+ 0x73, 0xF3, 0x63, 0x01, 0xED, 0xC6, 0xA1, 0xBC,
+ 0x1D, 0xD6, 0x48, 0x0E, 0x08, 0xF5, 0x87, 0x65,
+ 0xCB, 0x93, 0x87, 0x82, 0x92, 0x3B, 0xC0, 0x1F,
+ 0x8E, 0x0C, 0x61, 0xC6, 0xBE, 0x0D, 0xD1, 0xAB,
+ 0x4C, 0x18, 0xCB, 0x15, 0xED, 0x52, 0x10, 0x11,
+ 0x24, 0x05, 0xF1, 0xEA, 0x8F, 0x2E, 0x8C, 0x4E
+ },
+ {
+ 0x71, 0x4A, 0xD1, 0x85, 0xF1, 0xEE, 0xC4, 0x3F,
+ 0x46, 0xB6, 0x7E, 0x99, 0x2D, 0x2D, 0x38, 0xBC,
+ 0x31, 0x49, 0xE3, 0x7D, 0xA7, 0xB4, 0x47, 0x48,
+ 0xD4, 0xD1, 0x4C, 0x16, 0x1E, 0x08, 0x78, 0x02,
+ 0x04, 0x42, 0x14, 0x95, 0x79, 0xA8, 0x65, 0xD8,
+ 0x04, 0xB0, 0x49, 0xCD, 0x01, 0x55, 0xBA, 0x98,
+ 0x33, 0x78, 0x75, 0x7A, 0x13, 0x88, 0x30, 0x1B,
+ 0xDC, 0x0F, 0xAE, 0x2C, 0xEA, 0xEA, 0x07, 0xDD
+ },
+ {
+ 0x22, 0xB8, 0x24, 0x9E, 0xAF, 0x72, 0x29, 0x64,
+ 0xCE, 0x42, 0x4F, 0x71, 0xA7, 0x4D, 0x03, 0x8F,
+ 0xF9, 0xB6, 0x15, 0xFB, 0xA5, 0xC7, 0xC2, 0x2C,
+ 0xB6, 0x27, 0x97, 0xF5, 0x39, 0x82, 0x24, 0xC3,
+ 0xF0, 0x72, 0xEB, 0xC1, 0xDA, 0xCB, 0xA3, 0x2F,
+ 0xC6, 0xF6, 0x63, 0x60, 0xB3, 0xE1, 0x65, 0x8D,
+ 0x0F, 0xA0, 0xDA, 0x1E, 0xD1, 0xC1, 0xDA, 0x66,
+ 0x2A, 0x20, 0x37, 0xDA, 0x82, 0x3A, 0x33, 0x83
+ },
+ {
+ 0xB8, 0xE9, 0x03, 0xE6, 0x91, 0xB9, 0x92, 0x78,
+ 0x25, 0x28, 0xF8, 0xDB, 0x96, 0x4D, 0x08, 0xE3,
+ 0xBA, 0xAF, 0xBD, 0x08, 0xBA, 0x60, 0xC7, 0x2A,
+ 0xEC, 0x0C, 0x28, 0xEC, 0x6B, 0xFE, 0xCA, 0x4B,
+ 0x2E, 0xC4, 0xC4, 0x6F, 0x22, 0xBF, 0x62, 0x1A,
+ 0x5D, 0x74, 0xF7, 0x5C, 0x0D, 0x29, 0x69, 0x3E,
+ 0x56, 0xC5, 0xC5, 0x84, 0xF4, 0x39, 0x9E, 0x94,
+ 0x2F, 0x3B, 0xD8, 0xD3, 0x86, 0x13, 0xE6, 0x39
+ },
+ {
+ 0xD5, 0xB4, 0x66, 0xFF, 0x1F, 0xD6, 0x8C, 0xFA,
+ 0x8E, 0xDF, 0x0B, 0x68, 0x02, 0x44, 0x8F, 0x30,
+ 0x2D, 0xCC, 0xDA, 0xF5, 0x66, 0x28, 0x78, 0x6B,
+ 0x9D, 0xA0, 0xF6, 0x62, 0xFD, 0xA6, 0x90, 0x26,
+ 0x6B, 0xD4, 0x0A, 0xB6, 0xF0, 0xBE, 0xC0, 0x43,
+ 0xF1, 0x01, 0x28, 0xB3, 0x3D, 0x05, 0xDB, 0x82,
+ 0xD4, 0xAB, 0x26, 0x8A, 0x4F, 0x91, 0xAC, 0x42,
+ 0x86, 0x79, 0x5F, 0xC0, 0xF7, 0xCB, 0x48, 0x5C
+ },
+ {
+ 0x0A, 0x1E, 0x8C, 0x0A, 0x8C, 0x48, 0xB8, 0x4B,
+ 0x71, 0xBA, 0x0F, 0xE5, 0x6F, 0xA0, 0x56, 0x09,
+ 0x8C, 0xA6, 0x92, 0xE9, 0x2F, 0x27, 0x6E, 0x85,
+ 0xB3, 0x38, 0x26, 0xCD, 0x78, 0x75, 0xFC, 0xF8,
+ 0x83, 0x85, 0x13, 0x1B, 0x43, 0xDF, 0x74, 0x53,
+ 0x2E, 0xAA, 0x86, 0xCF, 0x17, 0x1F, 0x50, 0x76,
+ 0xE6, 0xD1, 0x7B, 0x1C, 0x75, 0xFB, 0xA1, 0xDB,
+ 0x00, 0x1B, 0x6E, 0x66, 0x97, 0x7C, 0xB8, 0xD7
+ },
+ {
+ 0x65, 0xAA, 0x17, 0x99, 0x14, 0x36, 0x93, 0xAB,
+ 0xD9, 0xCB, 0x21, 0x8D, 0x9B, 0x5E, 0xC6, 0x0C,
+ 0x0E, 0xDD, 0xB0, 0x67, 0xE6, 0xA3, 0x2F, 0x76,
+ 0x79, 0x60, 0x10, 0xAC, 0xB1, 0x1A, 0xD0, 0x13,
+ 0x6C, 0xE4, 0x9F, 0x97, 0x6E, 0x74, 0xF8, 0x95,
+ 0x04, 0x2F, 0x7C, 0xBF, 0x13, 0xFB, 0x73, 0xD1,
+ 0x9D, 0xC8, 0x89, 0xD7, 0xE9, 0x03, 0x46, 0x9D,
+ 0xEB, 0x33, 0x73, 0x1F, 0x24, 0x06, 0xB6, 0x63
+ },
+ {
+ 0xDE, 0xB7, 0x12, 0xB9, 0xCC, 0x64, 0xF5, 0x88,
+ 0x14, 0x86, 0x0B, 0x51, 0xFA, 0x89, 0xAD, 0x8A,
+ 0x92, 0x6A, 0x69, 0x08, 0xC7, 0x96, 0xDE, 0x55,
+ 0x7F, 0x90, 0xCF, 0xAD, 0xB0, 0xC6, 0x2C, 0x07,
+ 0x87, 0x2F, 0x33, 0xFE, 0x18, 0x4E, 0x5E, 0x21,
+ 0x2A, 0x3C, 0x5C, 0x37, 0x31, 0x74, 0x18, 0x44,
+ 0x6E, 0xFD, 0x95, 0x61, 0x3F, 0x61, 0x8A, 0x35,
+ 0xF7, 0xD2, 0x78, 0x9E, 0xFE, 0x0D, 0x96, 0x60
+ },
+ {
+ 0xB4, 0x2F, 0x4A, 0x40, 0xB3, 0xC8, 0x8B, 0xCE,
+ 0xCF, 0xE3, 0x28, 0xC8, 0x46, 0xBF, 0x06, 0x48,
+ 0xA1, 0x69, 0x90, 0xCA, 0x53, 0x91, 0x95, 0xC0,
+ 0xC1, 0xDC, 0x8D, 0x70, 0x30, 0x80, 0x67, 0x68,
+ 0x5A, 0xF6, 0x77, 0xAD, 0x65, 0xAC, 0x0C, 0x7A,
+ 0x9B, 0xCF, 0xA8, 0xF7, 0xAC, 0xC0, 0xAA, 0xCF,
+ 0x45, 0xCA, 0x18, 0xAC, 0x83, 0x1F, 0xED, 0x64,
+ 0x4E, 0xC3, 0xD9, 0x28, 0x31, 0x01, 0xFF, 0xEF
+ },
+ {
+ 0xED, 0xCF, 0x6C, 0x81, 0xCC, 0xF1, 0x6E, 0x11,
+ 0xDD, 0xF7, 0x19, 0xA3, 0x3D, 0xD0, 0xE5, 0x34,
+ 0x9C, 0xAB, 0xAC, 0x5C, 0xFA, 0xE5, 0x97, 0x00,
+ 0x98, 0x40, 0xE1, 0xC3, 0x93, 0x62, 0xC0, 0xF1,
+ 0x19, 0x82, 0xFE, 0x2C, 0x27, 0x65, 0x85, 0x9A,
+ 0x94, 0x26, 0x2D, 0xA2, 0x8D, 0xD3, 0x37, 0x3D,
+ 0x52, 0x26, 0x93, 0x89, 0x75, 0x11, 0xEB, 0xA5,
+ 0xE0, 0x7B, 0x8B, 0xC6, 0xB6, 0x06, 0x4D, 0xC0
+ },
+ {
+ 0x46, 0xB9, 0x62, 0xD2, 0x28, 0x36, 0x94, 0xD2,
+ 0x79, 0x75, 0xDC, 0xBF, 0x32, 0x56, 0x4C, 0x9B,
+ 0x04, 0x03, 0x2B, 0x30, 0xA9, 0x3E, 0x05, 0x8F,
+ 0xB7, 0x7B, 0x2B, 0x71, 0x8B, 0x4A, 0xD5, 0xFB,
+ 0x78, 0x9A, 0xB7, 0xD7, 0xAA, 0x90, 0x85, 0x2D,
+ 0xA2, 0xBF, 0xB6, 0xB3, 0x93, 0xB0, 0x9F, 0x98,
+ 0xE8, 0x69, 0xB1, 0x6E, 0x41, 0x0E, 0x7D, 0xE2,
+ 0x30, 0xB1, 0x79, 0xF6, 0x2E, 0xB5, 0x74, 0x71
+ },
+ {
+ 0x29, 0x03, 0x6C, 0x3F, 0x53, 0x82, 0xE3, 0x5D,
+ 0xE7, 0xA6, 0x9F, 0xA7, 0xA6, 0x3E, 0xC7, 0xBD,
+ 0xCB, 0xC4, 0xE0, 0xCC, 0x5A, 0x7B, 0x64, 0x14,
+ 0xCF, 0x44, 0xBF, 0x9A, 0x83, 0x83, 0xEF, 0xB5,
+ 0x97, 0x23, 0x50, 0x6F, 0x0D, 0x51, 0xAD, 0x50,
+ 0xAC, 0x1E, 0xAC, 0xF7, 0x04, 0x30, 0x8E, 0x8A,
+ 0xEC, 0xB9, 0x66, 0xF6, 0xAC, 0x94, 0x1D, 0xB1,
+ 0xCD, 0xE4, 0xB5, 0x9E, 0x84, 0xC1, 0xEB, 0xBA
+ },
+ {
+ 0x17, 0x3F, 0x8A, 0xB8, 0x93, 0x3E, 0xB0, 0x7C,
+ 0xC5, 0xFD, 0x6E, 0x4B, 0xCE, 0xBA, 0xE1, 0xFF,
+ 0x35, 0xC7, 0x87, 0x9B, 0x93, 0x8A, 0x5A, 0x15,
+ 0x79, 0xEA, 0x02, 0xF3, 0x83, 0x32, 0x48, 0x86,
+ 0xC7, 0x0E, 0xD9, 0x10, 0x9D, 0xE1, 0x69, 0x0B,
+ 0x8E, 0xE8, 0x01, 0xBC, 0x95, 0x9B, 0x21, 0xD3,
+ 0x81, 0x17, 0xEB, 0xB8, 0x4A, 0xB5, 0x6F, 0x88,
+ 0xF8, 0xA3, 0x72, 0x62, 0x00, 0x2D, 0xD9, 0x8E
+ },
+ {
+ 0xC6, 0xAF, 0xA6, 0xA1, 0x91, 0x93, 0x1F, 0xD4,
+ 0x5C, 0x3B, 0xAD, 0xBA, 0x72, 0x6E, 0x68, 0xA9,
+ 0xBC, 0x73, 0x88, 0xC8, 0xCF, 0x37, 0xAD, 0xEC,
+ 0x7C, 0x64, 0x56, 0x1C, 0xF4, 0x81, 0xFD, 0x25,
+ 0x9A, 0x64, 0x6C, 0x8B, 0xD8, 0x43, 0xE7, 0x70,
+ 0x9E, 0x11, 0xE6, 0x4D, 0xCF, 0xD5, 0xDF, 0xFF,
+ 0xED, 0x79, 0x23, 0x5C, 0x68, 0x9B, 0x42, 0x00,
+ 0xFE, 0x7A, 0xC8, 0xDF, 0xDA, 0xDD, 0xEC, 0xE0
+ },
+ {
+ 0xA6, 0xDC, 0xCD, 0x8C, 0x19, 0x26, 0x64, 0x88,
+ 0xBF, 0x77, 0xB9, 0xF2, 0x4B, 0x91, 0x43, 0xDE,
+ 0xF1, 0xFE, 0xD6, 0x1D, 0x0C, 0x60, 0xB5, 0x00,
+ 0x0A, 0x52, 0x3F, 0x45, 0x0D, 0xA2, 0x3D, 0x74,
+ 0xE4, 0xE3, 0xF6, 0xEF, 0x04, 0x09, 0x0D, 0x10,
+ 0x66, 0xB6, 0xAC, 0xE8, 0x5A, 0xBC, 0x0F, 0x03,
+ 0x01, 0x73, 0xF5, 0x28, 0x17, 0x72, 0x7C, 0x4E,
+ 0x40, 0x43, 0x2D, 0xD3, 0x4C, 0x6E, 0xF9, 0xF0
+ },
+ {
+ 0xAA, 0xF8, 0x90, 0x8D, 0x54, 0x6E, 0x4F, 0x1E,
+ 0x31, 0x4C, 0x00, 0xE9, 0xD2, 0xE8, 0x85, 0x5C,
+ 0xB2, 0x56, 0x44, 0x5A, 0xAE, 0x3E, 0xCA, 0x44,
+ 0x23, 0x83, 0x22, 0xAE, 0xC7, 0x40, 0x34, 0xA1,
+ 0x45, 0x8A, 0x29, 0x36, 0x75, 0xDA, 0xD9, 0x49,
+ 0x40, 0x8D, 0xE5, 0x55, 0x4F, 0x22, 0xD7, 0x34,
+ 0x54, 0xF3, 0xF0, 0x70, 0x9C, 0xBC, 0xCC, 0x85,
+ 0xCB, 0x05, 0x3A, 0x6F, 0x50, 0x38, 0x91, 0xA1
+ },
+ {
+ 0x52, 0x5F, 0x4A, 0xAB, 0x9C, 0x32, 0x7D, 0x2A,
+ 0x6A, 0x3C, 0x9D, 0xF8, 0x1F, 0xB7, 0xBE, 0x97,
+ 0xEE, 0x03, 0xE3, 0xF7, 0xCE, 0x33, 0x21, 0x1C,
+ 0x47, 0x78, 0x8A, 0xCD, 0x13, 0x46, 0x40, 0xDD,
+ 0x90, 0xAD, 0x74, 0x99, 0x2D, 0x3D, 0xD6, 0xAC,
+ 0x80, 0x63, 0x50, 0xF3, 0xBA, 0xBC, 0x7F, 0xE1,
+ 0x98, 0xA6, 0x1D, 0xB3, 0x2D, 0x4A, 0xD1, 0xD6,
+ 0x56, 0x9A, 0xE8, 0x41, 0x31, 0x04, 0xDE, 0xA4
+ },
+ {
+ 0x2D, 0xAC, 0xCD, 0x88, 0x71, 0x9D, 0x0A, 0x00,
+ 0xB5, 0x2C, 0x6E, 0xB7, 0x9E, 0x1C, 0xA8, 0xB4,
+ 0xA1, 0xB4, 0xB4, 0x4F, 0xFA, 0x20, 0x88, 0x9F,
+ 0x23, 0x63, 0xEF, 0x5C, 0x0D, 0x73, 0x7F, 0x1F,
+ 0x81, 0xF5, 0x0D, 0xA1, 0xCA, 0xAC, 0x23, 0x1D,
+ 0x6F, 0xCB, 0x48, 0x89, 0x5E, 0x72, 0x99, 0xB7,
+ 0x7A, 0xF8, 0x1F, 0x0A, 0xA4, 0xA7, 0x61, 0x8A,
+ 0xD2, 0x4B, 0x7A, 0xAF, 0xC8, 0xE3, 0xA2, 0xBE
+ },
+ {
+ 0x7D, 0x28, 0x6F, 0x1F, 0x72, 0x1E, 0xC2, 0xD2,
+ 0x11, 0x5E, 0xF4, 0xCC, 0xD8, 0x28, 0x58, 0xA4,
+ 0xD5, 0x12, 0x21, 0x13, 0x55, 0xD4, 0xFC, 0x58,
+ 0xE5, 0x34, 0xBF, 0xA5, 0x9C, 0x2E, 0x1B, 0xF5,
+ 0x52, 0xA9, 0x6D, 0xC4, 0xB3, 0xE4, 0x6B, 0x01,
+ 0x28, 0x65, 0xDA, 0x88, 0x13, 0x4C, 0xF0, 0x4E,
+ 0x73, 0x1B, 0x19, 0x30, 0x75, 0x9E, 0x15, 0x8F,
+ 0xF6, 0x20, 0xB6, 0xEC, 0x5A, 0xAF, 0xD0, 0x12
+ },
+ {
+ 0x21, 0x82, 0x6B, 0x95, 0x29, 0xC4, 0xBC, 0x51,
+ 0x91, 0x47, 0xF5, 0xF9, 0xFE, 0x6D, 0xB8, 0x78,
+ 0x34, 0x52, 0x15, 0xE5, 0x09, 0x4F, 0x4E, 0x99,
+ 0xB1, 0x31, 0xED, 0x54, 0xE2, 0x49, 0x53, 0xCE,
+ 0xE9, 0xAD, 0xB7, 0x18, 0xD1, 0x74, 0x3E, 0x6C,
+ 0x27, 0xFC, 0x94, 0x51, 0x6A, 0x99, 0x22, 0xFB,
+ 0x97, 0x5A, 0x78, 0x16, 0xB8, 0xAA, 0xB0, 0x21,
+ 0x12, 0x60, 0x8C, 0x03, 0x2B, 0xF1, 0x38, 0xE3
+ },
+ {
+ 0xC1, 0x68, 0x9C, 0x69, 0x8A, 0xB0, 0x65, 0xF6,
+ 0x2E, 0xEE, 0x65, 0xDD, 0xCA, 0x67, 0x6B, 0xAA,
+ 0x45, 0xB5, 0x2F, 0x30, 0x8A, 0xFA, 0x80, 0x4A,
+ 0xB4, 0xAA, 0x6A, 0xB8, 0x4B, 0x7A, 0xC1, 0xAA,
+ 0x1D, 0xFF, 0x07, 0x17, 0x56, 0x10, 0xB1, 0x2A,
+ 0xE1, 0x1F, 0x27, 0xB7, 0xC4, 0x30, 0xAF, 0xD5,
+ 0x75, 0x56, 0xBD, 0x18, 0x1D, 0x02, 0x83, 0x2C,
+ 0xD8, 0xD0, 0xA5, 0xFD, 0xC3, 0x02, 0x01, 0x24
+ },
+ {
+ 0xA1, 0xA6, 0x28, 0x17, 0x47, 0xE3, 0x4D, 0x3E,
+ 0xDE, 0x5E, 0x93, 0x34, 0x01, 0x74, 0x7C, 0xA7,
+ 0xF7, 0x66, 0x28, 0xB6, 0x14, 0xC8, 0xA3, 0x94,
+ 0xF5, 0x02, 0x56, 0x2B, 0xFE, 0xE0, 0xB9, 0x94,
+ 0xEC, 0xB6, 0x5F, 0xBF, 0xE1, 0xFF, 0x70, 0x67,
+ 0xDC, 0xB0, 0x1D, 0x02, 0xA9, 0x2B, 0xA4, 0x62,
+ 0x20, 0x75, 0x87, 0xCE, 0xF7, 0xDC, 0x2C, 0xFD,
+ 0xB4, 0x58, 0x48, 0x48, 0xAD, 0x55, 0x91, 0x4A
+ },
+ {
+ 0x00, 0x70, 0xA0, 0x19, 0x0A, 0xA6, 0x96, 0x57,
+ 0x2D, 0x85, 0x3F, 0x1D, 0x24, 0xAB, 0x63, 0x08,
+ 0x48, 0xAC, 0x56, 0xAD, 0x5C, 0x2E, 0xBF, 0xCF,
+ 0xDE, 0x27, 0xD1, 0x11, 0xCD, 0x55, 0x93, 0x9C,
+ 0x1E, 0x4D, 0x07, 0x87, 0x2D, 0xDE, 0x7C, 0xE7,
+ 0x8B, 0x53, 0x4B, 0x53, 0x0F, 0x0A, 0x39, 0x6E,
+ 0x86, 0xAF, 0x9D, 0x57, 0x53, 0x54, 0xB5, 0xD7,
+ 0xE3, 0x4A, 0xCD, 0xE1, 0x8C, 0xC7, 0x67, 0xAE
+ },
+ {
+ 0x51, 0xB9, 0xB5, 0xED, 0x19, 0x3F, 0xD4, 0xB1,
+ 0xA3, 0xA9, 0x2B, 0x46, 0xBD, 0x4B, 0xD1, 0xF6,
+ 0xEC, 0x6B, 0x38, 0xA6, 0x0F, 0x2D, 0x02, 0x61,
+ 0xD7, 0x2A, 0xBF, 0xD1, 0x64, 0x36, 0x12, 0x8D,
+ 0xCB, 0xF2, 0x2C, 0x25, 0xE3, 0xE3, 0xC4, 0x3F,
+ 0xE4, 0xD2, 0x9D, 0xB9, 0x12, 0x4D, 0x03, 0x33,
+ 0x30, 0x18, 0x45, 0x92, 0xD2, 0x0C, 0x5B, 0x08,
+ 0x2C, 0x23, 0x20, 0x64, 0x54, 0xCB, 0x3D, 0xD7
+ },
+ {
+ 0x57, 0x8F, 0x24, 0x27, 0x46, 0x91, 0x4E, 0x36,
+ 0xD0, 0xD9, 0xD4, 0x80, 0x96, 0x89, 0x57, 0x12,
+ 0x16, 0xA4, 0x3E, 0x47, 0x33, 0x32, 0x39, 0x51,
+ 0x62, 0x0F, 0x5E, 0xE7, 0x8C, 0xCF, 0xEE, 0x91,
+ 0x9B, 0xF5, 0x5F, 0x28, 0x7B, 0x45, 0xA7, 0x3D,
+ 0x44, 0x85, 0xAC, 0x74, 0x22, 0x87, 0x92, 0x39,
+ 0x65, 0x3B, 0x05, 0x91, 0xC3, 0x6C, 0x86, 0x69,
+ 0x41, 0xF8, 0xAF, 0xFE, 0x4A, 0xE5, 0x6E, 0x9E
+ },
+ {
+ 0x94, 0x71, 0x30, 0xEF, 0x0B, 0x94, 0x8E, 0xE0,
+ 0x45, 0x81, 0xAB, 0xA3, 0xE2, 0xCC, 0x4C, 0xEF,
+ 0xC3, 0x8C, 0xCE, 0xDC, 0x86, 0x17, 0x92, 0xB7,
+ 0xB5, 0xDC, 0xD9, 0xD9, 0x36, 0x1C, 0x72, 0x4A,
+ 0x12, 0x20, 0x03, 0xBF, 0x79, 0x6C, 0xE0, 0x97,
+ 0x98, 0x00, 0xAD, 0xAB, 0xC7, 0x45, 0x6F, 0x17,
+ 0x3A, 0xE5, 0x26, 0x93, 0x15, 0xAF, 0xC0, 0x1B,
+ 0x60, 0x6D, 0xB2, 0x9C, 0x75, 0x50, 0xE8, 0xCA
+ },
+ {
+ 0xC8, 0x52, 0xE6, 0x77, 0xF7, 0x7B, 0x14, 0xB5,
+ 0x85, 0xBD, 0x10, 0x2A, 0x0F, 0x14, 0x42, 0x43,
+ 0x05, 0x9D, 0xAB, 0xEC, 0x7C, 0xB0, 0x1F, 0xFA,
+ 0x61, 0xDF, 0x19, 0xFC, 0xE8, 0xAB, 0x43, 0x6B,
+ 0xF5, 0xE2, 0xD5, 0xC7, 0x9A, 0xA2, 0xD7, 0xB6,
+ 0x77, 0xF6, 0xC3, 0x75, 0xE9, 0x34, 0x3D, 0x34,
+ 0x2E, 0x4F, 0xF4, 0xE3, 0xAB, 0x00, 0x1B, 0xC7,
+ 0x98, 0x8C, 0x3C, 0x7A, 0x83, 0xCC, 0xB6, 0x9F
+ },
+ {
+ 0x01, 0x19, 0x75, 0x26, 0x91, 0x7A, 0xC2, 0xC7,
+ 0xBC, 0x53, 0x95, 0x19, 0xE6, 0x8B, 0xB2, 0x79,
+ 0x81, 0x35, 0xF6, 0x03, 0x3E, 0xD5, 0x8F, 0x5C,
+ 0x45, 0x1E, 0x0C, 0xE9, 0x46, 0xAF, 0xF0, 0xF9,
+ 0x8D, 0xFD, 0xD1, 0x51, 0x01, 0x73, 0x1A, 0xC1,
+ 0x66, 0x12, 0x6E, 0xAF, 0xB5, 0xE7, 0xCB, 0xE2,
+ 0xE2, 0x72, 0xEE, 0x23, 0x3F, 0x34, 0xE5, 0xF3,
+ 0xF8, 0xEA, 0x3D, 0x2D, 0x12, 0x24, 0x82, 0xFB
+ },
+ {
+ 0x05, 0x9C, 0x90, 0x85, 0x89, 0x5E, 0xB7, 0x18,
+ 0x30, 0x4E, 0x2D, 0xDA, 0x78, 0x68, 0x6B, 0xD9,
+ 0x57, 0x49, 0x81, 0x5A, 0x5E, 0xE9, 0x02, 0x51,
+ 0x0B, 0x00, 0x9A, 0xF6, 0x92, 0x48, 0xB6, 0xA7,
+ 0xA7, 0x2F, 0xF8, 0xA6, 0x28, 0xD8, 0x17, 0x73,
+ 0xE1, 0x1D, 0x5A, 0x1E, 0x7F, 0x69, 0x7A, 0x44,
+ 0x9B, 0x7A, 0x1E, 0x27, 0x12, 0xD5, 0xCF, 0xAE,
+ 0x7A, 0xB2, 0x65, 0x07, 0xD1, 0x11, 0x29, 0x18
+ },
+ {
+ 0x29, 0x52, 0x43, 0xBD, 0x75, 0x8C, 0xF2, 0x1C,
+ 0x80, 0x31, 0x25, 0xFC, 0xF3, 0x21, 0xDE, 0x5F,
+ 0x97, 0x98, 0x7C, 0x8D, 0xB3, 0xBB, 0x3C, 0xB5,
+ 0x1F, 0xF9, 0x7C, 0x4C, 0xDA, 0xC9, 0xD3, 0xBF,
+ 0x0A, 0x67, 0xCE, 0xE7, 0xED, 0x35, 0x0A, 0x41,
+ 0xFD, 0xE6, 0xAB, 0xCC, 0x25, 0x4F, 0xBC, 0x9F,
+ 0x8E, 0x6B, 0x3E, 0x3C, 0xCE, 0xCB, 0xD0, 0xE4,
+ 0xA6, 0x40, 0xA2, 0x0F, 0x36, 0x2B, 0xA3, 0xA0
+ },
+ {
+ 0xDD, 0x82, 0x32, 0xD2, 0x41, 0x2C, 0xCE, 0xEC,
+ 0xB5, 0x12, 0x31, 0x91, 0xF6, 0xE9, 0x22, 0x1E,
+ 0x85, 0x1E, 0xCC, 0xE0, 0xFA, 0xEB, 0xF0, 0x50,
+ 0x5F, 0x2A, 0xEE, 0xFF, 0x8A, 0x8C, 0x92, 0xD4,
+ 0x1D, 0xAC, 0xF1, 0x77, 0xBD, 0xAE, 0x27, 0x76,
+ 0x3E, 0xA4, 0xA8, 0x62, 0x05, 0xEF, 0x76, 0x34,
+ 0xF7, 0xA6, 0x87, 0xCC, 0x44, 0xBB, 0xBB, 0xDE,
+ 0xEE, 0x5E, 0x11, 0xE6, 0x5F, 0x9F, 0xBD, 0x69
+ },
+ {
+ 0xB0, 0x46, 0xB6, 0x83, 0x71, 0x6D, 0x31, 0xC9,
+ 0x14, 0xC7, 0x0B, 0x10, 0xF7, 0x64, 0x6D, 0xA3,
+ 0x1E, 0xFA, 0xB2, 0x23, 0x63, 0x47, 0x45, 0x9C,
+ 0xF8, 0xFA, 0x2C, 0x09, 0x12, 0x34, 0x31, 0xF7,
+ 0x28, 0x07, 0xF1, 0x1D, 0x86, 0x7C, 0x37, 0x70,
+ 0xB1, 0xF0, 0x61, 0xD5, 0x6C, 0xA0, 0xE5, 0xB1,
+ 0xE8, 0x8A, 0x6B, 0x44, 0xA3, 0x3C, 0xF9, 0x3E,
+ 0x18, 0xBC, 0xC9, 0xCE, 0xBB, 0xA5, 0xAD, 0xE7
+ },
+ {
+ 0x20, 0xE5, 0xA2, 0x55, 0x05, 0x8B, 0xE5, 0x1E,
+ 0x1A, 0x62, 0x9B, 0x4E, 0xBF, 0x81, 0xE5, 0xCB,
+ 0xE0, 0x78, 0x1C, 0xB6, 0x7C, 0xA4, 0xE5, 0x7B,
+ 0xA8, 0x6B, 0x30, 0x88, 0x96, 0xBC, 0xE7, 0x38,
+ 0x20, 0xEB, 0x08, 0x43, 0x1C, 0xE8, 0xC9, 0xBC,
+ 0x58, 0x10, 0xCC, 0x8D, 0x8B, 0x9C, 0x9D, 0x6F,
+ 0xCF, 0x83, 0x4E, 0x42, 0xEA, 0x33, 0xEF, 0x73,
+ 0xCE, 0xC4, 0x7D, 0x71, 0x3B, 0x6D, 0x8D, 0xFD
+ },
+ {
+ 0x1E, 0x48, 0x04, 0xF9, 0xC0, 0xB1, 0xE8, 0x2B,
+ 0x9E, 0xD3, 0x63, 0xBD, 0xE4, 0x47, 0x28, 0xAC,
+ 0xF7, 0xD0, 0x90, 0xA1, 0xBF, 0xE2, 0xDD, 0xF8,
+ 0x81, 0x9D, 0x65, 0x92, 0xEF, 0x45, 0x3B, 0x83,
+ 0x5B, 0xD2, 0xEF, 0xE8, 0xB0, 0x20, 0x6E, 0x29,
+ 0x25, 0x5B, 0x07, 0xFB, 0x90, 0xC7, 0xD3, 0x0D,
+ 0x2C, 0x11, 0x48, 0x00, 0xB8, 0x6C, 0xB0, 0xE3,
+ 0xE0, 0x7D, 0x38, 0x7E, 0x98, 0xCE, 0x95, 0x37
+ },
+ {
+ 0x41, 0xC9, 0x53, 0xD8, 0xD2, 0x2A, 0x86, 0xC3,
+ 0x63, 0x4D, 0xF4, 0x22, 0xB6, 0xDE, 0x4A, 0x4F,
+ 0x14, 0x96, 0x66, 0xBE, 0x8C, 0x4F, 0x58, 0x1B,
+ 0x26, 0x23, 0xEE, 0x65, 0xC3, 0x92, 0xA5, 0xC3,
+ 0x28, 0x36, 0x63, 0x9E, 0xF5, 0x6B, 0x93, 0x68,
+ 0x62, 0x20, 0xF4, 0x5C, 0xE6, 0x5B, 0x4F, 0xA8,
+ 0x58, 0x9C, 0x91, 0x25, 0x64, 0x17, 0x90, 0xB6,
+ 0x92, 0x5F, 0xAA, 0xD9, 0x48, 0xB8, 0xBE, 0x04
+ },
+ {
+ 0x8B, 0xFC, 0xA4, 0xC8, 0xDF, 0xE3, 0xFD, 0xE4,
+ 0x25, 0x7B, 0x75, 0xC3, 0xDB, 0x01, 0x86, 0x2E,
+ 0xD3, 0x11, 0x67, 0xDE, 0x66, 0xC2, 0xE0, 0x3A,
+ 0x25, 0x56, 0xC4, 0xF4, 0x6C, 0x9D, 0xFF, 0xC1,
+ 0xAC, 0x45, 0xF7, 0xBC, 0x59, 0xA6, 0x7A, 0xB9,
+ 0x36, 0x24, 0xBE, 0xB8, 0x6D, 0xDD, 0x0D, 0x02,
+ 0x60, 0x3F, 0x0D, 0xCD, 0x03, 0x64, 0xF0, 0xF8,
+ 0x08, 0x81, 0x9B, 0xE9, 0x6C, 0xD8, 0xD3, 0xB6
+ },
+ {
+ 0xF6, 0xBF, 0x59, 0xD8, 0xD4, 0x5A, 0x55, 0x71,
+ 0x11, 0xA2, 0x36, 0xCB, 0xBA, 0x52, 0x61, 0x9A,
+ 0xE3, 0xDF, 0xCC, 0x43, 0x16, 0x94, 0x38, 0x43,
+ 0xAF, 0xD1, 0x28, 0x1B, 0x28, 0x21, 0x4A, 0x4A,
+ 0x5E, 0x85, 0x1E, 0xF8, 0xC5, 0x4F, 0x50, 0x5E,
+ 0x3C, 0x4B, 0x60, 0x0E, 0xFF, 0xBE, 0xBB, 0x3E,
+ 0xAC, 0x17, 0x08, 0x7F, 0x22, 0x27, 0x58, 0x12,
+ 0x63, 0xF1, 0x7D, 0x7E, 0x5F, 0x68, 0xEA, 0x83
+ },
+ {
+ 0x1B, 0xC9, 0xED, 0xE4, 0xD4, 0x1A, 0x4D, 0xF6,
+ 0xE8, 0xE6, 0xF4, 0x7C, 0x2F, 0x4A, 0xD8, 0x73,
+ 0x37, 0xB6, 0x9B, 0x19, 0xF7, 0x10, 0xF7, 0x66,
+ 0xE1, 0xFA, 0xF5, 0xAA, 0x05, 0xA4, 0x3B, 0x66,
+ 0x45, 0x39, 0x6E, 0x7F, 0xBE, 0xF4, 0x3B, 0xB7,
+ 0x79, 0x5D, 0x39, 0x40, 0x7B, 0x58, 0x15, 0xB9,
+ 0x2E, 0xCC, 0x23, 0xA6, 0xC1, 0x24, 0x14, 0x21,
+ 0x15, 0x3A, 0x55, 0xD5, 0x1F, 0x12, 0xBF, 0xD8
+ },
+ {
+ 0x76, 0xB3, 0x8B, 0x36, 0x31, 0x55, 0x5D, 0xBC,
+ 0xFB, 0x21, 0x21, 0x8F, 0xF9, 0xE4, 0x12, 0xA2,
+ 0x29, 0x88, 0x9E, 0xF2, 0xCE, 0x8A, 0xD7, 0x05,
+ 0xE9, 0x0F, 0x96, 0xAA, 0xBB, 0xD5, 0xBE, 0x7E,
+ 0x53, 0x29, 0xA4, 0x26, 0x53, 0x4C, 0x81, 0x5A,
+ 0x56, 0x53, 0x77, 0x13, 0x18, 0x72, 0x66, 0x41,
+ 0x42, 0x4E, 0x3B, 0x88, 0x29, 0x2F, 0xB1, 0xD8,
+ 0x95, 0x44, 0x40, 0x6A, 0xDE, 0x9B, 0xCC, 0xB5
+ },
+ {
+ 0xE5, 0x3F, 0x60, 0x07, 0x40, 0x22, 0x4E, 0x4D,
+ 0x10, 0xD3, 0x1D, 0x24, 0x38, 0x00, 0x31, 0x43,
+ 0xAF, 0xDB, 0x43, 0x6E, 0xB1, 0x79, 0x1B, 0x15,
+ 0x0D, 0xE3, 0x56, 0x76, 0xF0, 0xE3, 0x2F, 0x80,
+ 0xB0, 0xB6, 0x5F, 0x0A, 0xCF, 0x48, 0x1A, 0x5F,
+ 0xBF, 0x95, 0x96, 0xC0, 0xCB, 0x0A, 0x27, 0xC7,
+ 0xAF, 0xC1, 0x1D, 0x1E, 0x2C, 0x4D, 0x54, 0x02,
+ 0x47, 0x5E, 0x4F, 0xFC, 0xC1, 0xCD, 0xA8, 0x11
+ },
+ {
+ 0x62, 0x06, 0xB9, 0x1F, 0xC0, 0xB6, 0xF1, 0x21,
+ 0x1E, 0x9F, 0xDE, 0xCD, 0xC9, 0xD5, 0x1A, 0x6F,
+ 0x1E, 0xEE, 0x65, 0x54, 0xB1, 0x38, 0xAD, 0xCD,
+ 0x4A, 0x82, 0x3D, 0xF0, 0x0D, 0xDE, 0xF6, 0x75,
+ 0x9A, 0x9B, 0xFD, 0x7A, 0x4E, 0x98, 0x1E, 0x04,
+ 0x52, 0x36, 0x83, 0x8F, 0x4A, 0xF6, 0x93, 0xF6,
+ 0x93, 0x77, 0x93, 0x14, 0x84, 0xB3, 0xE8, 0x1E,
+ 0x3E, 0x3B, 0xC2, 0xCB, 0x7E, 0xF7, 0x9F, 0xE9
+ },
+ {
+ 0x76, 0xFD, 0x02, 0xDA, 0xDD, 0x96, 0x3B, 0xC0,
+ 0x35, 0x39, 0x91, 0x46, 0xCE, 0x42, 0x98, 0x8C,
+ 0xC0, 0x99, 0xD3, 0xCF, 0x4D, 0x32, 0xDF, 0x5C,
+ 0x0B, 0xBF, 0x64, 0x10, 0x12, 0x46, 0xB1, 0xC7,
+ 0x08, 0xD1, 0x67, 0xE2, 0x95, 0x95, 0xD1, 0x1D,
+ 0x09, 0xB3, 0xF6, 0x34, 0x86, 0xB4, 0x05, 0x26,
+ 0xAC, 0x1D, 0xFE, 0x31, 0xBC, 0x22, 0xDE, 0xC7,
+ 0x0B, 0x74, 0x5E, 0x90, 0xE2, 0xEA, 0xAF, 0x5A
+ },
+ {
+ 0xF0, 0xA1, 0xFB, 0xE3, 0x11, 0x63, 0xE4, 0x21,
+ 0x01, 0x50, 0x72, 0x18, 0x3D, 0x68, 0xEE, 0x51,
+ 0x91, 0xA9, 0x9C, 0xFD, 0xA1, 0x69, 0xBA, 0x5A,
+ 0x19, 0x54, 0xC9, 0xF3, 0x10, 0x7D, 0x4E, 0xCA,
+ 0x06, 0x3E, 0x13, 0x7A, 0x71, 0x14, 0xD3, 0x97,
+ 0xC9, 0xDB, 0x67, 0x2B, 0x9F, 0x47, 0x8D, 0x41,
+ 0xC3, 0x4E, 0x99, 0x1B, 0x06, 0x69, 0xA9, 0x51,
+ 0x53, 0x92, 0x90, 0xC8, 0xED, 0x65, 0xE4, 0x6A
+ },
+ {
+ 0x13, 0xC7, 0x2A, 0x6A, 0xA5, 0x71, 0xB1, 0x43,
+ 0xDC, 0xCF, 0x45, 0xAD, 0xCD, 0x98, 0xEA, 0xE6,
+ 0x99, 0xA1, 0x54, 0xB1, 0x10, 0xF2, 0x5E, 0x7E,
+ 0x9E, 0x82, 0xB7, 0x65, 0xB9, 0xA0, 0x89, 0x23,
+ 0x68, 0x8E, 0x8E, 0x0F, 0xF3, 0x11, 0xA6, 0x8A,
+ 0x77, 0x1E, 0x14, 0x50, 0x96, 0xD6, 0x07, 0x76,
+ 0xC6, 0xD6, 0xEE, 0x70, 0xAD, 0x6F, 0x69, 0xFA,
+ 0x2B, 0x76, 0x77, 0x63, 0x40, 0x55, 0xA0, 0x0E
+ },
+ {
+ 0x0E, 0x06, 0x2B, 0xFE, 0x81, 0x8E, 0xE1, 0x0F,
+ 0x33, 0x48, 0x1D, 0xEA, 0x43, 0x02, 0x8B, 0x2C,
+ 0xFB, 0xB4, 0x9E, 0xC9, 0x5E, 0x0F, 0x75, 0xA9,
+ 0xE1, 0x6D, 0x40, 0x4B, 0xC5, 0x19, 0xB9, 0xAD,
+ 0x50, 0xB4, 0xA7, 0x33, 0x69, 0x2C, 0xA5, 0x4E,
+ 0xFB, 0x68, 0x04, 0x69, 0xED, 0x83, 0xDD, 0xEF,
+ 0xBD, 0xDD, 0xB1, 0x39, 0x04, 0x2E, 0x0E, 0x1C,
+ 0x09, 0xC3, 0xEB, 0x79, 0x03, 0xFA, 0x08, 0xDF
+ },
+ {
+ 0x45, 0x3B, 0xE4, 0xAA, 0xB9, 0xF4, 0x23, 0xB3,
+ 0x36, 0x52, 0xA0, 0xB5, 0xD0, 0x2A, 0x9A, 0xF8,
+ 0x55, 0xDD, 0x0D, 0x42, 0xDD, 0x83, 0x11, 0x0B,
+ 0xA3, 0xBC, 0x4B, 0x39, 0x94, 0xEA, 0x3F, 0x88,
+ 0x5A, 0x71, 0x30, 0x89, 0x75, 0x08, 0x9B, 0x49,
+ 0x03, 0xE2, 0xE4, 0xD6, 0xBA, 0x6D, 0xC2, 0xE8,
+ 0x40, 0x31, 0xFF, 0xE9, 0xC8, 0x56, 0x39, 0x75,
+ 0xC8, 0x61, 0x6A, 0xCA, 0x07, 0x42, 0xE8, 0x29
+ },
+ {
+ 0x53, 0x61, 0xE3, 0xE8, 0x93, 0xDD, 0x36, 0x0B,
+ 0xCB, 0xF5, 0x1C, 0x79, 0x3E, 0xC0, 0x92, 0xA6,
+ 0xB0, 0x52, 0x05, 0x4F, 0x5F, 0x00, 0x0B, 0x9F,
+ 0xCE, 0x50, 0x7B, 0x66, 0x45, 0xF8, 0xD4, 0x70,
+ 0x13, 0xA8, 0x70, 0x6A, 0x58, 0xD4, 0xB1, 0x06,
+ 0x29, 0xCC, 0x82, 0xB8, 0xD2, 0xD7, 0x96, 0xFD,
+ 0xD3, 0x7B, 0x60, 0x8A, 0x58, 0x79, 0x52, 0xD6,
+ 0x55, 0x3E, 0x01, 0xD1, 0xAF, 0x0E, 0x04, 0xB8
+ },
+ {
+ 0x74, 0xB5, 0x67, 0x39, 0xF0, 0x1F, 0x82, 0x09,
+ 0xA4, 0x04, 0x44, 0xDF, 0x4C, 0xCD, 0xEE, 0xEA,
+ 0x8F, 0x97, 0xE8, 0xE7, 0x6E, 0xFA, 0x3C, 0x04,
+ 0x33, 0x7F, 0x69, 0x94, 0x5C, 0x4D, 0x44, 0xC0,
+ 0x85, 0xF1, 0xF4, 0x78, 0x96, 0x96, 0x36, 0x1E,
+ 0x3C, 0x97, 0x77, 0x4A, 0x93, 0x5F, 0x86, 0x0D,
+ 0x67, 0x46, 0x86, 0xDC, 0xBA, 0x3D, 0x45, 0xEC,
+ 0xD8, 0x63, 0x9A, 0x64, 0xAE, 0xA0, 0x62, 0x1B
+ },
+ {
+ 0xB4, 0xD3, 0x15, 0x87, 0xB9, 0x2B, 0x53, 0x61,
+ 0xCD, 0xC2, 0xD3, 0xC4, 0x10, 0x86, 0xC1, 0x55,
+ 0x3E, 0x7B, 0x55, 0xA1, 0xF6, 0x1E, 0x94, 0xD2,
+ 0xBC, 0x30, 0xBC, 0x25, 0x1D, 0xAF, 0x8A, 0x5E,
+ 0xBF, 0xC5, 0x07, 0x09, 0xCC, 0x04, 0xCB, 0xAF,
+ 0x4B, 0x3B, 0x4D, 0xA2, 0xD2, 0x6B, 0x81, 0x23,
+ 0x8F, 0xBA, 0x71, 0x8F, 0xA9, 0x17, 0x59, 0xB8,
+ 0x0B, 0xD3, 0x10, 0x3A, 0xEC, 0x11, 0xE0, 0x6F
+ },
+ {
+ 0xAA, 0xF6, 0x12, 0x7F, 0x00, 0xA0, 0x3D, 0x96,
+ 0x40, 0x6B, 0x9F, 0xB4, 0xAC, 0x70, 0x16, 0x0D,
+ 0xB5, 0x22, 0x42, 0x9B, 0x5C, 0xD9, 0x4E, 0x7F,
+ 0xA0, 0x30, 0x3A, 0x74, 0x94, 0x78, 0xFE, 0x31,
+ 0x89, 0xC8, 0xEA, 0x23, 0x93, 0x0A, 0x66, 0x25,
+ 0x2A, 0x80, 0x26, 0x74, 0xDC, 0xAF, 0x77, 0x00,
+ 0x46, 0x82, 0x0D, 0xD9, 0x64, 0xC6, 0x6F, 0x0F,
+ 0x54, 0x75, 0x1A, 0x72, 0xF9, 0x7D, 0x9C, 0x35
+ },
+ {
+ 0x2C, 0x30, 0xD4, 0x8D, 0xF9, 0x98, 0x4E, 0x02,
+ 0xF7, 0x5A, 0x94, 0x54, 0x92, 0x17, 0x18, 0x4D,
+ 0xD0, 0x2A, 0xAD, 0x3B, 0x57, 0x68, 0x3D, 0x09,
+ 0xB5, 0xA8, 0xC2, 0xEF, 0x53, 0xA9, 0x6A, 0xFB,
+ 0x73, 0xFE, 0xB6, 0xF9, 0x14, 0xE2, 0xD8, 0x15,
+ 0xBB, 0x3B, 0x08, 0x65, 0x43, 0x32, 0xFC, 0xFE,
+ 0x79, 0xF8, 0x0E, 0xC5, 0xF0, 0x51, 0xDA, 0x10,
+ 0xD7, 0x21, 0x41, 0x3D, 0xDD, 0xE8, 0xFA, 0x60
+ },
+ {
+ 0x92, 0xE2, 0xC5, 0xF7, 0x5D, 0x0C, 0xEA, 0xFC,
+ 0x81, 0x8F, 0xA7, 0x93, 0x59, 0x39, 0xE4, 0x8B,
+ 0x91, 0x59, 0x41, 0xEF, 0x73, 0x4D, 0x75, 0x27,
+ 0x0E, 0xB3, 0x21, 0xBA, 0x20, 0x80, 0xEF, 0x6D,
+ 0x25, 0x5E, 0x90, 0xEF, 0x96, 0xC6, 0x4C, 0xFF,
+ 0x1D, 0x8C, 0x18, 0xF3, 0x3C, 0x2E, 0xAB, 0x10,
+ 0x7F, 0xEF, 0x53, 0xE0, 0xD8, 0xBB, 0x16, 0x05,
+ 0x16, 0x80, 0x74, 0x80, 0xFC, 0xBA, 0x53, 0x73
+ },
+ {
+ 0x6E, 0x03, 0xA9, 0x1E, 0x20, 0x44, 0x46, 0x27,
+ 0xE3, 0xD2, 0xE2, 0x22, 0x26, 0xCF, 0x47, 0x00,
+ 0x26, 0x69, 0x44, 0x34, 0xED, 0x64, 0x79, 0x82,
+ 0x8C, 0xB6, 0xDC, 0x8F, 0x27, 0x96, 0x0A, 0xEE,
+ 0xE2, 0xF4, 0xAB, 0x87, 0x2A, 0x5C, 0xA2, 0xF7,
+ 0xF6, 0x52, 0xF7, 0xDC, 0x77, 0xD5, 0xF9, 0x6D,
+ 0x85, 0x82, 0x8B, 0x8F, 0x9C, 0x2D, 0x6C, 0x23,
+ 0x9E, 0x79, 0x77, 0x24, 0xA1, 0x31, 0x31, 0xB1
+ },
+ {
+ 0xBA, 0x43, 0x2D, 0xB0, 0xA3, 0x31, 0xBB, 0x8C,
+ 0x39, 0xB1, 0x7B, 0xEE, 0x34, 0x46, 0x2B, 0x26,
+ 0xDD, 0xB7, 0xAD, 0x91, 0xB6, 0xC7, 0x5A, 0xEC,
+ 0x27, 0x65, 0xFB, 0xAE, 0x3A, 0x0E, 0x60, 0xEC,
+ 0x54, 0x6D, 0x45, 0xF8, 0xE5, 0x84, 0x37, 0xB9,
+ 0xD7, 0x7C, 0x3D, 0x2E, 0x8D, 0x7C, 0xE0, 0x69,
+ 0x73, 0x15, 0x66, 0x51, 0xD4, 0x08, 0x22, 0x2A,
+ 0xA2, 0x90, 0xCB, 0x58, 0xCA, 0xBC, 0x0A, 0xE5
+ },
+ {
+ 0x83, 0xA0, 0x1E, 0x23, 0xAB, 0x27, 0x7B, 0x1F,
+ 0xC2, 0x8C, 0xD8, 0xBB, 0x8D, 0xA7, 0xE9, 0x4C,
+ 0x70, 0xF1, 0xDE, 0xE3, 0x2D, 0x19, 0x55, 0xCE,
+ 0xE2, 0x50, 0xEE, 0x58, 0x41, 0x9A, 0x1F, 0xEE,
+ 0x10, 0xA8, 0x99, 0x17, 0x97, 0xCE, 0x3D, 0x20,
+ 0x93, 0x80, 0xCA, 0x9F, 0x98, 0x93, 0x39, 0xE2,
+ 0xD8, 0xA8, 0x1C, 0x67, 0xD7, 0x37, 0xD8, 0x28,
+ 0x8C, 0x7F, 0xAE, 0x46, 0x02, 0x83, 0x4A, 0x8B
+ },
+ {
+ 0x0E, 0xA3, 0x21, 0x72, 0xCC, 0x19, 0x1D, 0xFC,
+ 0x13, 0x1C, 0xD8, 0x8A, 0xA0, 0x3F, 0xF4, 0x18,
+ 0x5C, 0x0B, 0xFA, 0x7B, 0x19, 0x11, 0x12, 0x19,
+ 0xEE, 0xCB, 0x45, 0xB0, 0xFF, 0x60, 0x4D, 0x3E,
+ 0xDB, 0x00, 0x55, 0x0A, 0xBB, 0xA1, 0x11, 0x52,
+ 0x2B, 0x77, 0xAE, 0x61, 0xC9, 0xA8, 0xD6, 0xE9,
+ 0x4F, 0xCA, 0x9D, 0x96, 0xC3, 0x8D, 0x6B, 0x7C,
+ 0xCE, 0x27, 0x52, 0xF0, 0xD0, 0xC3, 0x7E, 0x78
+ },
+ {
+ 0x54, 0xAD, 0xD6, 0x55, 0x2B, 0x08, 0x85, 0x8B,
+ 0x23, 0xD6, 0x64, 0x5F, 0x6C, 0xE7, 0x9E, 0x92,
+ 0xF3, 0x8B, 0x66, 0xAE, 0x91, 0x86, 0x77, 0xE6,
+ 0xD9, 0x1F, 0x71, 0x87, 0xC4, 0x16, 0x05, 0x24,
+ 0xDF, 0xA8, 0xD0, 0x1F, 0x00, 0xEA, 0x93, 0xDD,
+ 0x29, 0x9F, 0x3C, 0xC4, 0x09, 0x01, 0xBD, 0x33,
+ 0x27, 0xA0, 0xF1, 0x8C, 0xCD, 0x7B, 0x6B, 0x8E,
+ 0x4E, 0x47, 0xCD, 0x28, 0xCF, 0x83, 0x8F, 0xAB
+ },
+ {
+ 0xEF, 0x84, 0x74, 0x6D, 0xC2, 0x01, 0x56, 0xB6,
+ 0x6B, 0xA5, 0xC7, 0x8A, 0x50, 0x83, 0x0A, 0xBD,
+ 0x2A, 0xEF, 0x90, 0xE6, 0x67, 0xB9, 0x7E, 0xB5,
+ 0x22, 0x91, 0xBC, 0x86, 0x9D, 0x8A, 0xA2, 0x45,
+ 0x59, 0xA1, 0x42, 0xC6, 0x8F, 0xEA, 0x2E, 0xF3,
+ 0x2A, 0xF2, 0x2D, 0xFC, 0xEA, 0x4C, 0x90, 0xB3,
+ 0xD4, 0x90, 0x8C, 0xC9, 0xEA, 0x5C, 0xFC, 0x4E,
+ 0x91, 0xBF, 0x11, 0xCE, 0x6A, 0x7E, 0x57, 0x61
+ },
+ {
+ 0x5A, 0x1B, 0xF3, 0x81, 0xA0, 0x41, 0x19, 0xF9,
+ 0x42, 0xE4, 0x63, 0xAB, 0xA2, 0xB1, 0x64, 0x38,
+ 0x82, 0x46, 0x8A, 0xEC, 0xC1, 0xB1, 0xAA, 0x1E,
+ 0x7B, 0xCA, 0xAB, 0x3B, 0x47, 0x8F, 0xC5, 0xF0,
+ 0x56, 0xF1, 0x0D, 0xA9, 0x03, 0x7D, 0x40, 0xFA,
+ 0x7F, 0x55, 0x70, 0x8E, 0x10, 0x3B, 0xDA, 0x96,
+ 0x5E, 0x92, 0x0C, 0xF6, 0x7C, 0xE3, 0xAD, 0xF7,
+ 0xE2, 0x00, 0xE8, 0x61, 0x01, 0x4D, 0xEC, 0xC6
+ },
+ {
+ 0xAC, 0xF7, 0x8A, 0xA3, 0x28, 0x45, 0x96, 0xF3,
+ 0x30, 0xB7, 0xE8, 0x47, 0x51, 0xB9, 0x4C, 0x31,
+ 0x4C, 0xD8, 0x36, 0x36, 0x27, 0xBA, 0x99, 0x78,
+ 0x81, 0x30, 0x85, 0x78, 0x87, 0x37, 0x59, 0x89,
+ 0x5D, 0x13, 0xDF, 0xFF, 0xA5, 0xE5, 0x74, 0x50,
+ 0x13, 0x61, 0xF0, 0x43, 0xC7, 0x4F, 0x57, 0xD2,
+ 0xD0, 0xF1, 0x5C, 0x7A, 0x41, 0xC7, 0xC4, 0x5E,
+ 0x3C, 0x09, 0xAD, 0x89, 0xD6, 0x99, 0xA9, 0x77
+ },
+ {
+ 0x18, 0xB3, 0xE9, 0x04, 0x38, 0x44, 0xD4, 0xF3,
+ 0xA2, 0xD0, 0x21, 0xF5, 0x4C, 0x38, 0xFA, 0xCC,
+ 0x36, 0x4F, 0x84, 0xBA, 0x10, 0x58, 0xF2, 0x10,
+ 0x09, 0xFC, 0x37, 0x1D, 0x2E, 0x4F, 0x38, 0xC7,
+ 0x27, 0x51, 0x8A, 0xAB, 0xA6, 0xA2, 0x9E, 0x0F,
+ 0xDA, 0xE6, 0xE7, 0x60, 0xA4, 0xF1, 0xA6, 0xD7,
+ 0x58, 0xEB, 0xE4, 0x2C, 0x2A, 0xFC, 0x9D, 0x2C,
+ 0xDC, 0x6D, 0xD5, 0x80, 0x77, 0x8C, 0x4B, 0x32
+ },
+ {
+ 0x18, 0x96, 0xB2, 0x31, 0x70, 0x33, 0xCF, 0x31,
+ 0x04, 0x68, 0x73, 0xD8, 0x7F, 0x26, 0xE6, 0xA4,
+ 0x2A, 0x9D, 0x77, 0x0B, 0xBA, 0xF6, 0xE0, 0x62,
+ 0xDF, 0x11, 0xF9, 0xB4, 0xA0, 0xEA, 0xB2, 0x75,
+ 0xAA, 0xB1, 0x2C, 0xAA, 0xC2, 0xD3, 0xF5, 0x29,
+ 0xEB, 0x20, 0xD0, 0x70, 0xFD, 0x84, 0x4D, 0x86,
+ 0xD0, 0xA5, 0x71, 0xCD, 0xF6, 0x28, 0x5F, 0x80,
+ 0xE2, 0x30, 0x8B, 0xB8, 0x2C, 0x6C, 0x5B, 0x3B
+ },
+ {
+ 0x8C, 0x3D, 0xC4, 0x01, 0x94, 0xAA, 0x02, 0x1F,
+ 0x3C, 0x4A, 0x1F, 0x9A, 0x05, 0x5E, 0x4D, 0x41,
+ 0x9E, 0xB3, 0xA2, 0x6D, 0x4C, 0x2F, 0x1A, 0x8C,
+ 0x7E, 0x18, 0x8B, 0x73, 0x48, 0x13, 0x40, 0x80,
+ 0xB6, 0x3F, 0x6E, 0x57, 0x0A, 0xD1, 0x1C, 0x28,
+ 0x78, 0x66, 0x53, 0x55, 0x41, 0x9C, 0x10, 0x20,
+ 0xDE, 0x4B, 0x65, 0x5E, 0x7A, 0x6C, 0x2C, 0xCD,
+ 0xE9, 0x07, 0x2C, 0xD4, 0x27, 0xFE, 0x8C, 0x4E
+ },
+ {
+ 0x70, 0xAE, 0x04, 0x30, 0xD5, 0x45, 0xEC, 0x42,
+ 0x7F, 0x85, 0x41, 0x21, 0x1D, 0x4F, 0xE0, 0x42,
+ 0xB9, 0x82, 0x3A, 0xCE, 0xC0, 0x4B, 0x15, 0xC9,
+ 0x0B, 0x7F, 0x4B, 0x8B, 0xDD, 0x3D, 0xC7, 0x85,
+ 0x19, 0x90, 0xF3, 0x70, 0xE7, 0x14, 0x16, 0x75,
+ 0x10, 0x66, 0x49, 0xD3, 0x91, 0x51, 0x09, 0x03,
+ 0x18, 0x23, 0x1E, 0x4D, 0xED, 0x51, 0x22, 0x5D,
+ 0x9A, 0x6F, 0xA6, 0xC4, 0x24, 0x69, 0x5D, 0xE2
+ },
+ {
+ 0x07, 0x33, 0x6C, 0x42, 0xBD, 0x51, 0x49, 0x0E,
+ 0xF8, 0x4D, 0xFB, 0xDF, 0xAB, 0x74, 0x66, 0xF6,
+ 0xB6, 0x39, 0x99, 0xA5, 0xC0, 0x88, 0x72, 0xDF,
+ 0xED, 0xA0, 0x20, 0x6F, 0xDA, 0x80, 0xB9, 0xA6,
+ 0x2D, 0xE7, 0x28, 0xE3, 0xE3, 0xC3, 0xFD, 0x6B,
+ 0x7D, 0x21, 0xA4, 0x38, 0xAA, 0xD1, 0xB8, 0xDD,
+ 0x22, 0x38, 0x63, 0xC0, 0xD2, 0x6A, 0xCA, 0x27,
+ 0x79, 0x01, 0x74, 0xD9, 0xD4, 0x42, 0xA6, 0x4C
+ },
+ {
+ 0x79, 0x26, 0x70, 0x88, 0x59, 0xE6, 0xE2, 0xAB,
+ 0x68, 0xF6, 0x04, 0xDA, 0x69, 0xA9, 0xFB, 0x50,
+ 0x87, 0xBB, 0x33, 0xF4, 0xE8, 0xD8, 0x95, 0x73,
+ 0x0E, 0x30, 0x1A, 0xB2, 0xD7, 0xDF, 0x74, 0x8B,
+ 0x67, 0xDF, 0x0B, 0x6B, 0x86, 0x22, 0xE5, 0x2D,
+ 0xD5, 0x7D, 0x8D, 0x3A, 0xD8, 0x7D, 0x58, 0x20,
+ 0xD4, 0xEC, 0xFD, 0x24, 0x17, 0x8B, 0x2D, 0x2B,
+ 0x78, 0xD6, 0x4F, 0x4F, 0xBD, 0x38, 0x75, 0x82
+ },
+ {
+ 0x92, 0x80, 0xF4, 0xD1, 0x15, 0x70, 0x32, 0xAB,
+ 0x31, 0x5C, 0x10, 0x0D, 0x63, 0x62, 0x83, 0xFB,
+ 0xF4, 0xFB, 0xA2, 0xFB, 0xAD, 0x0F, 0x8B, 0xC0,
+ 0x20, 0x72, 0x1D, 0x76, 0xBC, 0x1C, 0x89, 0x73,
+ 0xCE, 0xD2, 0x88, 0x71, 0xCC, 0x90, 0x7D, 0xAB,
+ 0x60, 0xE5, 0x97, 0x56, 0x98, 0x7B, 0x0E, 0x0F,
+ 0x86, 0x7F, 0xA2, 0xFE, 0x9D, 0x90, 0x41, 0xF2,
+ 0xC9, 0x61, 0x80, 0x74, 0xE4, 0x4F, 0xE5, 0xE9
+ },
+ {
+ 0x55, 0x30, 0xC2, 0xD5, 0x9F, 0x14, 0x48, 0x72,
+ 0xE9, 0x87, 0xE4, 0xE2, 0x58, 0xA7, 0xD8, 0xC3,
+ 0x8C, 0xE8, 0x44, 0xE2, 0xCC, 0x2E, 0xED, 0x94,
+ 0x0F, 0xFC, 0x68, 0x3B, 0x49, 0x88, 0x15, 0xE5,
+ 0x3A, 0xDB, 0x1F, 0xAA, 0xF5, 0x68, 0x94, 0x61,
+ 0x22, 0x80, 0x5A, 0xC3, 0xB8, 0xE2, 0xFE, 0xD4,
+ 0x35, 0xFE, 0xD6, 0x16, 0x2E, 0x76, 0xF5, 0x64,
+ 0xE5, 0x86, 0xBA, 0x46, 0x44, 0x24, 0xE8, 0x85
+ },
+ {
+ 0xDA, 0x85, 0x0A, 0x2F, 0x54, 0xE9, 0x44, 0x89,
+ 0x17, 0xD0, 0xDC, 0xAA, 0x63, 0x93, 0x7B, 0x95,
+ 0xA4, 0xDA, 0x1E, 0xAC, 0x8A, 0xF4, 0xDD, 0xF2,
+ 0x11, 0x3E, 0x5C, 0x8B, 0x0D, 0x4D, 0xB2, 0x66,
+ 0x9A, 0xF3, 0xC2, 0xAC, 0xB0, 0x80, 0x3D, 0x05,
+ 0x32, 0x3F, 0x3E, 0xC5, 0x5A, 0xBD, 0x33, 0xBD,
+ 0xF9, 0xB2, 0xBE, 0x89, 0x0E, 0xE7, 0x9E, 0x7F,
+ 0x3F, 0xCE, 0x4E, 0x19, 0x86, 0x96, 0xA7, 0xA3
+ },
+ {
+ 0xF1, 0x60, 0x95, 0xDD, 0x9F, 0x1E, 0xEB, 0x77,
+ 0xD5, 0xB9, 0x2F, 0x4B, 0x1F, 0xAC, 0x3A, 0x2C,
+ 0x5D, 0xA6, 0xAE, 0x5D, 0x0A, 0xB3, 0xF2, 0x54,
+ 0xE2, 0xA7, 0xFE, 0x52, 0x67, 0x24, 0x11, 0xD0,
+ 0x1C, 0xFA, 0x6A, 0xC0, 0x5B, 0xF3, 0x9E, 0xF6,
+ 0x5F, 0x4B, 0x22, 0x26, 0x4B, 0x41, 0xC3, 0xF3,
+ 0x63, 0x56, 0x3A, 0xBF, 0x0E, 0x92, 0x42, 0x90,
+ 0xC1, 0xC6, 0x80, 0xB1, 0x8A, 0xA6, 0x5B, 0x44
+ },
+ {
+ 0x76, 0xD0, 0x0A, 0x09, 0xC5, 0xBD, 0xD3, 0x9E,
+ 0xD3, 0x28, 0x71, 0x72, 0x2C, 0xFA, 0x00, 0x47,
+ 0x67, 0x4B, 0xEC, 0x8D, 0x35, 0x17, 0x5A, 0xF9,
+ 0x0D, 0x7A, 0xE9, 0x10, 0x74, 0x40, 0xA2, 0xA0,
+ 0x63, 0x88, 0x56, 0xD8, 0x38, 0x4C, 0x81, 0x7D,
+ 0x77, 0x2A, 0x4A, 0x59, 0x7A, 0x89, 0x55, 0x49,
+ 0xC8, 0x48, 0x66, 0x37, 0x56, 0x31, 0xCB, 0xA0,
+ 0x42, 0xF0, 0xEF, 0x6F, 0xFE, 0xB8, 0x9D, 0x44
+ },
+ {
+ 0xA6, 0x51, 0x13, 0x7B, 0x2C, 0x47, 0xFB, 0x79,
+ 0x51, 0xE7, 0xBD, 0xA7, 0x15, 0x43, 0xA6, 0xEB,
+ 0xC6, 0x24, 0x2A, 0xCA, 0xB4, 0x34, 0x7D, 0x38,
+ 0x8B, 0xE8, 0x35, 0x0F, 0x0C, 0x3F, 0xA3, 0xDF,
+ 0x8D, 0x95, 0x2C, 0x7C, 0x8A, 0x3D, 0xAF, 0x01,
+ 0xE0, 0x6C, 0x1D, 0xA6, 0x94, 0x96, 0xBB, 0xA8,
+ 0xDE, 0x62, 0xD8, 0x6B, 0x50, 0x93, 0x25, 0x6F,
+ 0x77, 0xA1, 0x87, 0xB5, 0x3D, 0xB0, 0x39, 0x88
+ },
+ {
+ 0xF3, 0x2F, 0x15, 0x0C, 0x2D, 0x67, 0xC0, 0xC4,
+ 0x37, 0x40, 0x1B, 0x70, 0xF6, 0x0B, 0x38, 0xF0,
+ 0xA3, 0xA4, 0x70, 0x59, 0x03, 0x3E, 0x75, 0x05,
+ 0xE6, 0x9A, 0x1D, 0x30, 0x12, 0x96, 0x03, 0x0B,
+ 0xC9, 0xB2, 0x95, 0x19, 0xC7, 0xF8, 0xB7, 0xD5,
+ 0x9A, 0x71, 0xFA, 0xB9, 0x05, 0x57, 0xDC, 0x3D,
+ 0xC8, 0x23, 0xFA, 0xC9, 0x5B, 0x9E, 0x85, 0xE6,
+ 0x52, 0x52, 0x8C, 0xBF, 0xB0, 0x1B, 0x11, 0x78
+ },
+ {
+ 0x27, 0x02, 0x56, 0x61, 0x36, 0xC4, 0x92, 0xF4,
+ 0x10, 0x89, 0xB0, 0x60, 0x10, 0x84, 0x60, 0xFA,
+ 0x30, 0x22, 0xC9, 0xC2, 0x5D, 0x34, 0x3B, 0xCB,
+ 0xD8, 0xAF, 0x2A, 0xF1, 0x9C, 0x17, 0xEF, 0x4C,
+ 0xA9, 0xF2, 0x22, 0x4F, 0xE7, 0xC4, 0x70, 0x0A,
+ 0x10, 0x19, 0x8E, 0xE5, 0x24, 0x8F, 0x30, 0x0B,
+ 0x54, 0x8E, 0xBF, 0x5C, 0x8E, 0x71, 0x16, 0x32,
+ 0x0C, 0xC8, 0x93, 0xFF, 0x7E, 0x23, 0x1F, 0xFB
+ },
+ {
+ 0xFF, 0xE6, 0x87, 0x9F, 0x46, 0xB6, 0x29, 0x2B,
+ 0x21, 0x96, 0x97, 0x2E, 0x3F, 0xDF, 0x4F, 0xE9,
+ 0xEA, 0x4A, 0x81, 0x6D, 0x18, 0x07, 0xA3, 0x1C,
+ 0xAE, 0xAD, 0x6A, 0xAC, 0x5F, 0x06, 0x3C, 0x8F,
+ 0xE8, 0x77, 0x79, 0x75, 0x59, 0xA7, 0x59, 0xA0,
+ 0x0F, 0x8B, 0xA8, 0xF6, 0x68, 0xD8, 0x96, 0x8F,
+ 0xB3, 0x1D, 0x8A, 0x3B, 0x84, 0x57, 0x35, 0x90,
+ 0x2C, 0x5E, 0x42, 0xE2, 0x89, 0xEE, 0x0B, 0x62
+ },
+ {
+ 0x14, 0x48, 0x84, 0x28, 0x68, 0x22, 0xC2, 0x51,
+ 0x2D, 0x61, 0xB0, 0x46, 0xE6, 0x74, 0xD8, 0x6B,
+ 0x26, 0x4E, 0x9C, 0xC6, 0x89, 0x3E, 0xFF, 0x36,
+ 0x73, 0x11, 0x24, 0xF5, 0x9D, 0x1A, 0x82, 0x00,
+ 0x1E, 0x63, 0xF3, 0xE8, 0x05, 0x1C, 0xFE, 0x52,
+ 0xE7, 0x59, 0x7E, 0x28, 0x73, 0x8E, 0x3C, 0x3A,
+ 0x70, 0xF1, 0xBE, 0xD9, 0x68, 0x0E, 0x2C, 0x0E,
+ 0xF3, 0x72, 0x8B, 0x10, 0xA5, 0x6E, 0xD9, 0x87
+ },
+ {
+ 0x17, 0xC3, 0xF1, 0x46, 0xEE, 0x8D, 0xEC, 0x3B,
+ 0xAF, 0xCB, 0x51, 0xC0, 0xDA, 0x37, 0xF1, 0x78,
+ 0x71, 0xF2, 0x34, 0xC4, 0xA0, 0xFB, 0x7F, 0xA6,
+ 0xD0, 0x70, 0x7A, 0x54, 0x3E, 0x3C, 0xBF, 0x3A,
+ 0xDB, 0x81, 0xE3, 0x0C, 0x1E, 0x0A, 0xE9, 0xE1,
+ 0xAC, 0xE7, 0x22, 0x3B, 0xDA, 0x99, 0xBD, 0x59,
+ 0x19, 0xA3, 0xCF, 0xCC, 0x92, 0xC6, 0xA7, 0x55,
+ 0xE4, 0x56, 0xF0, 0x93, 0x82, 0x3B, 0xD3, 0x3E
+ },
+ {
+ 0x1B, 0x83, 0x7A, 0xF2, 0x33, 0xA8, 0xA6, 0x8B,
+ 0xE7, 0x09, 0x52, 0xF7, 0x83, 0xC4, 0x96, 0x1A,
+ 0x81, 0x52, 0xD1, 0xE0, 0xB0, 0xFA, 0x32, 0x5F,
+ 0xF0, 0x86, 0xEA, 0x5B, 0x5F, 0x13, 0x12, 0xB8,
+ 0x9C, 0x42, 0xE0, 0x1B, 0x8C, 0x3A, 0x47, 0x7C,
+ 0xB5, 0x40, 0xC0, 0x6B, 0x2F, 0x37, 0xEE, 0x0E,
+ 0x39, 0x24, 0xD7, 0x45, 0xB4, 0xFF, 0x5C, 0x6A,
+ 0xF7, 0xD6, 0x1E, 0x0E, 0x37, 0xAC, 0x19, 0x31
+ },
+ {
+ 0x78, 0x97, 0x88, 0x0C, 0x1E, 0xB0, 0x0F, 0xD2,
+ 0x56, 0x7A, 0xE8, 0xA5, 0x9E, 0x64, 0x82, 0xAF,
+ 0xE1, 0x73, 0x49, 0xCF, 0x93, 0x92, 0x4A, 0x91,
+ 0x5F, 0x8C, 0x59, 0x26, 0x93, 0xD4, 0x52, 0x07,
+ 0x55, 0x19, 0x68, 0x9D, 0xFC, 0xD2, 0x93, 0xE3,
+ 0x76, 0x89, 0x7B, 0x3B, 0x0E, 0x03, 0x6F, 0x11,
+ 0x4F, 0xE8, 0x1E, 0xBC, 0xB3, 0x15, 0x36, 0x71,
+ 0xBD, 0x23, 0xBC, 0x2B, 0xED, 0x46, 0xF9, 0xC2
+ },
+ {
+ 0xCA, 0x7B, 0x6C, 0x77, 0x5D, 0x20, 0x1E, 0x5B,
+ 0x5A, 0x77, 0x22, 0x61, 0xDE, 0x52, 0x8E, 0x47,
+ 0x5F, 0x4B, 0xDE, 0x51, 0x76, 0x60, 0x52, 0x9F,
+ 0x41, 0xBE, 0xEB, 0x15, 0x78, 0xB2, 0x4B, 0xCB,
+ 0x94, 0xB9, 0x41, 0x0F, 0x9B, 0xF3, 0x36, 0xC1,
+ 0x09, 0xF9, 0xD4, 0x70, 0x93, 0xA1, 0x0B, 0xA6,
+ 0xDE, 0xBE, 0x50, 0x43, 0x80, 0xD9, 0xD1, 0x50,
+ 0x73, 0xBD, 0xD1, 0x11, 0xC8, 0xD1, 0x29, 0xFA
+ },
+ {
+ 0x57, 0x18, 0xE0, 0xD4, 0x5D, 0xEB, 0xC3, 0x00,
+ 0x2D, 0x52, 0xB2, 0x2C, 0x52, 0x73, 0x29, 0xAE,
+ 0x5E, 0xBF, 0x27, 0xE8, 0xFA, 0x9C, 0x8F, 0xEA,
+ 0xB4, 0x6C, 0x40, 0xBC, 0x64, 0x22, 0xCA, 0x03,
+ 0x35, 0x30, 0x4C, 0xF9, 0xE7, 0xF1, 0x41, 0xDE,
+ 0x7F, 0xA6, 0xAD, 0xB6, 0x78, 0x9B, 0xDB, 0xF3,
+ 0x8D, 0x14, 0xDA, 0xBA, 0x3E, 0x62, 0x97, 0xD2,
+ 0x5B, 0xF1, 0x7D, 0xE1, 0x70, 0xD6, 0xE3, 0xC8
+ },
+ {
+ 0x48, 0xD0, 0xED, 0x24, 0x9F, 0x90, 0x28, 0x41,
+ 0x99, 0x7C, 0x25, 0x5D, 0xAF, 0x99, 0x08, 0x9C,
+ 0x9A, 0x31, 0x24, 0x69, 0x8B, 0x16, 0x4A, 0x30,
+ 0x28, 0x33, 0x0F, 0xDD, 0x4C, 0xEE, 0x41, 0xE1,
+ 0x68, 0x3F, 0xA4, 0xD9, 0xDC, 0x66, 0xB2, 0xA7,
+ 0x9C, 0x8A, 0xA4, 0xC8, 0x28, 0x4E, 0x27, 0xBE,
+ 0xE2, 0xA4, 0x28, 0xA6, 0x71, 0x9D, 0x6E, 0xC6,
+ 0x55, 0xED, 0x76, 0x9D, 0xCB, 0x62, 0x4E, 0x24
+ },
+ {
+ 0x79, 0x4E, 0x0B, 0x64, 0xAC, 0xE1, 0xFE, 0x5A,
+ 0xE3, 0x79, 0x93, 0x70, 0x68, 0xD8, 0x2D, 0xF0,
+ 0x48, 0x68, 0x61, 0x6C, 0xAE, 0x0C, 0x17, 0xD3,
+ 0x05, 0x72, 0xC2, 0x02, 0x4E, 0x77, 0x48, 0x94,
+ 0xE0, 0x66, 0x8C, 0x47, 0x2D, 0x62, 0x3C, 0x90,
+ 0x3C, 0xC5, 0x88, 0x5F, 0x17, 0x84, 0x94, 0x51,
+ 0x10, 0x32, 0x9E, 0xB4, 0x98, 0xA8, 0x95, 0xA9,
+ 0xE5, 0x9A, 0x75, 0xE5, 0x27, 0x15, 0x8A, 0x5C
+ },
+ {
+ 0x21, 0x79, 0xAA, 0x82, 0x0E, 0x03, 0xFA, 0x33,
+ 0xD9, 0xBD, 0xE5, 0x56, 0x8C, 0x26, 0x2E, 0x2D,
+ 0x34, 0x17, 0xA4, 0x02, 0xE0, 0x7A, 0x59, 0x1F,
+ 0x9D, 0x55, 0x70, 0x68, 0x2D, 0xB5, 0xF9, 0xBB,
+ 0xA4, 0xBB, 0x9D, 0x5A, 0x82, 0xEE, 0x5E, 0xFD,
+ 0xB4, 0xF6, 0x5B, 0xBB, 0xFE, 0xEE, 0x2F, 0x4A,
+ 0xB9, 0xE4, 0x6C, 0xF2, 0xCE, 0x7E, 0x3B, 0x05,
+ 0x43, 0x27, 0xA7, 0x18, 0xD3, 0xF1, 0x08, 0x06
+ },
+ {
+ 0xB0, 0xA4, 0x8C, 0x6A, 0xDA, 0x54, 0x87, 0x25,
+ 0x79, 0x9B, 0x59, 0x86, 0xBA, 0xB4, 0x32, 0x69,
+ 0x79, 0x60, 0x92, 0x24, 0xD8, 0x97, 0x18, 0x4B,
+ 0x89, 0x97, 0x10, 0x4E, 0x0C, 0x6A, 0x24, 0xB3,
+ 0xAB, 0xE5, 0x62, 0x16, 0x54, 0x22, 0xA4, 0x5D,
+ 0x8A, 0xC8, 0x19, 0xB9, 0x9D, 0x37, 0x56, 0xEB,
+ 0xBB, 0x64, 0xF8, 0x43, 0xE3, 0xE0, 0x93, 0x4D,
+ 0xEC, 0x48, 0x7A, 0xED, 0x12, 0x13, 0x72, 0x79
+ },
+ {
+ 0x84, 0x8D, 0x7F, 0x2E, 0xAD, 0x41, 0x29, 0x1D,
+ 0x05, 0x38, 0x68, 0x0C, 0x64, 0x9D, 0x07, 0x89,
+ 0x7E, 0x45, 0xC7, 0x0A, 0x0A, 0xA4, 0xF9, 0x35,
+ 0x3F, 0x82, 0xC3, 0xF6, 0xFB, 0xB8, 0xE8, 0x48,
+ 0x9C, 0x75, 0x3E, 0x90, 0xDB, 0xE8, 0x89, 0x00,
+ 0x41, 0xA1, 0xAE, 0xEF, 0x84, 0xCD, 0x31, 0x36,
+ 0x43, 0x4F, 0x53, 0x0E, 0x9D, 0xD9, 0xC2, 0x3F,
+ 0xA5, 0x4F, 0xE1, 0x24, 0xEA, 0xFB, 0x72, 0xAD
+ },
+ {
+ 0x0E, 0xD1, 0x46, 0x26, 0xEE, 0x6D, 0x0C, 0x8E,
+ 0xD3, 0xF0, 0xC2, 0x00, 0xC1, 0x29, 0x85, 0x0F,
+ 0xFF, 0x76, 0x31, 0x8F, 0xFF, 0xA1, 0xDD, 0xD7,
+ 0xDD, 0x56, 0x3A, 0x01, 0xB7, 0x77, 0x97, 0x06,
+ 0x86, 0x2B, 0x23, 0x99, 0x59, 0xB6, 0x15, 0xAE,
+ 0x2E, 0xBE, 0x27, 0xC4, 0x50, 0x37, 0xE6, 0xFF,
+ 0xAF, 0x99, 0x14, 0xDA, 0x8F, 0xF2, 0x77, 0x2B,
+ 0xA5, 0xEE, 0x08, 0x11, 0xCD, 0x9E, 0xD5, 0x32
+ },
+ {
+ 0x52, 0x03, 0xC0, 0x76, 0x38, 0xC4, 0xB6, 0x5F,
+ 0x78, 0x43, 0x1E, 0x8B, 0x02, 0xE2, 0x0F, 0x6D,
+ 0x68, 0x3F, 0x19, 0xFA, 0x8F, 0x83, 0xB5, 0x13,
+ 0x4C, 0xD0, 0xF4, 0xE4, 0x68, 0xC9, 0x7E, 0xAC,
+ 0xB5, 0x26, 0x7C, 0x7D, 0x3E, 0xAB, 0x58, 0x3C,
+ 0xCA, 0xAC, 0xD0, 0xDB, 0xA4, 0xD5, 0x8A, 0xCE,
+ 0x52, 0x19, 0x3A, 0x51, 0x78, 0xA7, 0xB1, 0x2D,
+ 0x27, 0x95, 0xF5, 0xFD, 0xE8, 0xA3, 0x7B, 0xB9
+ },
+ {
+ 0x48, 0xBE, 0x43, 0xD5, 0xE0, 0x04, 0x36, 0x88,
+ 0xDF, 0x35, 0x32, 0xF7, 0x12, 0x1A, 0xFF, 0xFA,
+ 0x16, 0x7D, 0xAB, 0xE4, 0xA4, 0x84, 0xFB, 0x75,
+ 0xA0, 0x3A, 0xF3, 0x04, 0xA5, 0xC6, 0xF8, 0x25,
+ 0xF3, 0x6C, 0xEC, 0xCB, 0xBB, 0xC0, 0x75, 0xEE,
+ 0xF3, 0x20, 0xC4, 0xCD, 0x8D, 0x7E, 0xF8, 0xCB,
+ 0x49, 0xE6, 0xDD, 0x59, 0x73, 0x37, 0x9E, 0xEC,
+ 0x4C, 0x23, 0x3C, 0x45, 0x43, 0xD1, 0x32, 0xCE
+ },
+ {
+ 0xB5, 0x46, 0x4E, 0x6A, 0xBA, 0xF5, 0xD3, 0xD4,
+ 0x08, 0x3D, 0x1D, 0x7D, 0x2A, 0x8B, 0x0B, 0xAB,
+ 0x78, 0xB6, 0x17, 0x09, 0x50, 0x0B, 0xBF, 0x77,
+ 0x82, 0x3F, 0x60, 0x2D, 0x57, 0xD5, 0x13, 0xCA,
+ 0x9E, 0x9F, 0xFF, 0x65, 0xEF, 0xAA, 0x89, 0x9C,
+ 0xFE, 0x7B, 0xF8, 0x8A, 0x01, 0x88, 0x82, 0x9C,
+ 0x24, 0xE4, 0x98, 0xAD, 0x00, 0x23, 0x5A, 0xBE,
+ 0x8E, 0xEF, 0xA7, 0x19, 0xFA, 0x6A, 0xE6, 0xF6
+ },
+ {
+ 0xAF, 0xE5, 0xE5, 0xE8, 0x3F, 0x19, 0xAD, 0xAD,
+ 0x9E, 0x95, 0x90, 0x3E, 0xA9, 0xB2, 0x98, 0x10,
+ 0x7D, 0x37, 0xDD, 0x38, 0x63, 0x2C, 0x95, 0x90,
+ 0xBB, 0xFF, 0xC6, 0x24, 0xD4, 0xDE, 0x95, 0x8C,
+ 0xB6, 0xB6, 0x1A, 0xF0, 0x80, 0xF0, 0x37, 0xAD,
+ 0x17, 0xD0, 0x35, 0xB6, 0xBF, 0x58, 0xF7, 0x80,
+ 0xFA, 0xDF, 0x70, 0xF3, 0xC9, 0x59, 0x66, 0x8A,
+ 0x1B, 0x47, 0x21, 0x98, 0xA5, 0x9A, 0x8A, 0x00
+ },
+ {
+ 0xEF, 0xA2, 0xC7, 0xC8, 0x02, 0xE2, 0x10, 0xD2,
+ 0xD8, 0x0F, 0xB3, 0x50, 0xB3, 0xC2, 0xCB, 0x31,
+ 0x56, 0x13, 0x18, 0x11, 0xE7, 0x18, 0xEE, 0xE5,
+ 0xC9, 0xC6, 0x64, 0x0F, 0x87, 0x68, 0x2A, 0x55,
+ 0x81, 0x2B, 0x10, 0xF4, 0x03, 0x10, 0xBA, 0xA7,
+ 0xB8, 0x2B, 0x27, 0x3E, 0xF3, 0xAC, 0xC5, 0x5F,
+ 0xED, 0xE0, 0xB5, 0xF1, 0x94, 0x9D, 0xE4, 0x29,
+ 0x3D, 0x91, 0xB5, 0x89, 0xA2, 0x17, 0x5F, 0xF7
+ },
+ {
+ 0xD6, 0xC6, 0x2A, 0x61, 0x82, 0x71, 0xF3, 0xBC,
+ 0xBE, 0x00, 0x79, 0x24, 0xA0, 0xC9, 0x81, 0x2F,
+ 0x83, 0x17, 0x44, 0x5F, 0xB6, 0xFB, 0x19, 0xEB,
+ 0x58, 0x9A, 0x62, 0x9F, 0x51, 0x2F, 0xB3, 0x8A,
+ 0x0B, 0x4E, 0x24, 0x7D, 0xEA, 0x88, 0xC5, 0x6A,
+ 0x1B, 0xAF, 0x17, 0x88, 0x33, 0x65, 0xB4, 0x36,
+ 0xF2, 0x84, 0x46, 0xFF, 0x66, 0xEA, 0x43, 0x18,
+ 0x0B, 0xD0, 0x1E, 0xB5, 0xA6, 0x50, 0x9B, 0xD5
+ },
+ {
+ 0x0B, 0x41, 0x16, 0x6B, 0xE6, 0x2F, 0x65, 0xE1,
+ 0x93, 0xB3, 0xB8, 0x65, 0xE6, 0xC4, 0x7A, 0xAD,
+ 0x26, 0x0A, 0xF5, 0xFC, 0xEE, 0xC9, 0xAB, 0x44,
+ 0xAB, 0xAA, 0x46, 0x0A, 0x0C, 0x02, 0x46, 0xB6,
+ 0xC6, 0x9B, 0x67, 0xD7, 0x1D, 0x3A, 0xDF, 0xEC,
+ 0x60, 0xDC, 0x8E, 0x77, 0x37, 0x2F, 0x09, 0x49,
+ 0x52, 0x34, 0x4F, 0xE1, 0x0C, 0x0D, 0x59, 0xEF,
+ 0xEC, 0x0E, 0x11, 0xC4, 0xA5, 0x16, 0x93, 0x6D
+ },
+ {
+ 0x79, 0xD5, 0xF9, 0xFF, 0xC0, 0x5E, 0xCF, 0x33,
+ 0x7D, 0xE9, 0xF1, 0xE0, 0xF1, 0xD8, 0x9B, 0x30,
+ 0xAC, 0xFE, 0xBB, 0xB8, 0x8A, 0x69, 0x35, 0x86,
+ 0x78, 0x18, 0xCD, 0x8D, 0x45, 0xDA, 0x3D, 0x25,
+ 0x18, 0xDE, 0x61, 0xA7, 0xFE, 0x28, 0x75, 0x1B,
+ 0x61, 0x8F, 0x7A, 0x87, 0x5E, 0x11, 0x89, 0x8F,
+ 0xFF, 0x74, 0x15, 0x7A, 0xB9, 0x06, 0x81, 0xBD,
+ 0x53, 0xFA, 0x69, 0x62, 0x67, 0x1E, 0xD9, 0x9D
+ },
+ {
+ 0xBE, 0xA9, 0x83, 0xD7, 0x6F, 0x24, 0xB1, 0xEE,
+ 0xDE, 0x1D, 0x06, 0x71, 0x48, 0x05, 0x76, 0x8F,
+ 0xAA, 0xAD, 0x47, 0x08, 0xC9, 0xA4, 0xFF, 0x9C,
+ 0xD2, 0x42, 0x2F, 0x70, 0x6B, 0x6F, 0x0C, 0x30,
+ 0x6D, 0x8B, 0x67, 0xF3, 0x40, 0x89, 0xC6, 0x5E,
+ 0xD3, 0x88, 0x0C, 0x75, 0xF6, 0x7B, 0xBC, 0x4D,
+ 0x89, 0xAD, 0x87, 0x12, 0x0A, 0x77, 0xD0, 0xFF,
+ 0xE4, 0x36, 0xFB, 0x7B, 0x58, 0xB2, 0xCA, 0x41
+ },
+ {
+ 0x46, 0x6F, 0xD9, 0x15, 0xEF, 0xD9, 0x50, 0xBC,
+ 0x96, 0x65, 0x78, 0xCD, 0x92, 0xC6, 0x85, 0x92,
+ 0x9D, 0x7B, 0x51, 0xA6, 0x3D, 0xB1, 0x42, 0xC7,
+ 0xB9, 0xA9, 0x3D, 0x16, 0x52, 0x04, 0x95, 0x31,
+ 0x9B, 0x87, 0xF6, 0x58, 0xE6, 0xAF, 0xDA, 0x1B,
+ 0x42, 0x77, 0x3E, 0x2D, 0x49, 0xDA, 0x81, 0x45,
+ 0x94, 0xA5, 0x54, 0x90, 0x89, 0xEF, 0xB1, 0xF3,
+ 0xAB, 0x5F, 0x15, 0x90, 0xCA, 0x0A, 0x02, 0xAF
+ },
+ {
+ 0xF6, 0x46, 0x11, 0x13, 0x7A, 0xD2, 0x95, 0x46,
+ 0x70, 0xEA, 0xEC, 0xD6, 0x26, 0xD2, 0x12, 0xCF,
+ 0xC5, 0xB9, 0xF6, 0xBB, 0x41, 0xAA, 0xEB, 0xB1,
+ 0xD7, 0x1E, 0x89, 0x79, 0x2E, 0xB1, 0x31, 0x7A,
+ 0xED, 0xC6, 0x38, 0x13, 0xFE, 0x63, 0xDE, 0x40,
+ 0x17, 0x98, 0xDF, 0x75, 0x6C, 0xA1, 0xF2, 0x20,
+ 0x35, 0xA0, 0xFA, 0xBD, 0x37, 0xFB, 0x11, 0x03,
+ 0x43, 0x7F, 0x89, 0x1E, 0xAD, 0x5E, 0x64, 0x29
+ },
+ {
+ 0x32, 0xE1, 0xF9, 0x38, 0xA2, 0x7F, 0xAA, 0xD8,
+ 0xAC, 0x4A, 0x13, 0xFD, 0x4F, 0x6A, 0x8B, 0xF3,
+ 0xDA, 0xBE, 0x4B, 0xC7, 0x2A, 0xF1, 0x1C, 0x8F,
+ 0x0E, 0x1A, 0x06, 0x56, 0x7E, 0xD7, 0x04, 0xB8,
+ 0xE7, 0x8E, 0x11, 0x40, 0xA0, 0xC7, 0x72, 0x4E,
+ 0x3E, 0xFB, 0x70, 0xD2, 0x38, 0x07, 0xCF, 0x38,
+ 0xE6, 0x27, 0xE3, 0x26, 0xAF, 0xC1, 0x64, 0xCD,
+ 0xED, 0x52, 0xB4, 0x41, 0x39, 0xFF, 0xB3, 0xF3
+ },
+ {
+ 0x48, 0x33, 0xAC, 0x92, 0xE3, 0x02, 0xAC, 0x2B,
+ 0x67, 0xB0, 0x2B, 0x88, 0x27, 0x14, 0x3B, 0xAD,
+ 0xA1, 0x5C, 0xED, 0x22, 0x0E, 0x1D, 0x1F, 0x5B,
+ 0x71, 0x12, 0x0C, 0x51, 0xEE, 0x54, 0xC1, 0x9D,
+ 0x30, 0x1F, 0x29, 0x60, 0xBD, 0xB5, 0xA2, 0xCE,
+ 0x27, 0xD4, 0x41, 0xD1, 0x4A, 0xF0, 0x80, 0xCB,
+ 0x01, 0x0A, 0x8A, 0x23, 0xEE, 0xFF, 0x58, 0x11,
+ 0xDF, 0xA4, 0x4D, 0x1D, 0x7B, 0x35, 0x8B, 0x48
+ },
+ {
+ 0x9A, 0x03, 0x88, 0xCE, 0xE1, 0xAD, 0x01, 0x46,
+ 0x17, 0x7C, 0x48, 0xB5, 0xA0, 0x8A, 0x2D, 0xB3,
+ 0xC4, 0x89, 0xE8, 0x4C, 0xE2, 0xAB, 0xA8, 0xC6,
+ 0x45, 0x11, 0x2A, 0x02, 0x1E, 0x41, 0x1C, 0xF8,
+ 0x29, 0x12, 0x7F, 0xA2, 0xF1, 0xD1, 0xAE, 0x1B,
+ 0xAF, 0x3A, 0x33, 0xEA, 0x53, 0x09, 0x84, 0x77,
+ 0xA7, 0xD1, 0x2B, 0xA7, 0x48, 0xD2, 0xAF, 0x24,
+ 0xD1, 0x66, 0x02, 0xE9, 0x19, 0x07, 0x76, 0x23
+ },
+ {
+ 0xE3, 0xDF, 0x00, 0x74, 0xA9, 0x37, 0x35, 0x13,
+ 0x0D, 0x99, 0x22, 0xD2, 0xBE, 0x91, 0x6F, 0x35,
+ 0x34, 0x3D, 0x98, 0x8C, 0xE5, 0x9D, 0x76, 0x97,
+ 0x15, 0xA9, 0x83, 0xB4, 0xBA, 0x80, 0x7C, 0xE1,
+ 0xEE, 0x70, 0xA3, 0x13, 0xE5, 0x92, 0x31, 0x58,
+ 0x4F, 0x55, 0x6E, 0xBB, 0xA1, 0xB9, 0x0B, 0x1B,
+ 0xB6, 0xA6, 0xC5, 0x81, 0xA4, 0xB4, 0x7C, 0x3F,
+ 0xF5, 0x21, 0x89, 0x65, 0x2A, 0xAB, 0x36, 0xF5
+ },
+ {
+ 0x91, 0x91, 0xCF, 0x46, 0x1B, 0x69, 0x59, 0xBE,
+ 0xC9, 0x3E, 0xAE, 0x7F, 0xB1, 0xC6, 0xE3, 0x70,
+ 0x73, 0xD1, 0xA6, 0x15, 0x27, 0xAD, 0x75, 0xD1,
+ 0x0B, 0x7F, 0x89, 0x49, 0xD9, 0xB8, 0xAF, 0x70,
+ 0xA2, 0x3A, 0xD1, 0x31, 0x2E, 0xD5, 0x1F, 0x70,
+ 0xF0, 0xE9, 0xDF, 0x60, 0x1D, 0xDA, 0xE2, 0x38,
+ 0x90, 0x6C, 0x0F, 0xE3, 0xF7, 0x66, 0xB1, 0x4F,
+ 0x11, 0x3B, 0x26, 0xBC, 0x85, 0x42, 0xD1, 0xD2
+ },
+ {
+ 0x2A, 0x8B, 0xAD, 0xE2, 0x72, 0xEE, 0x7A, 0xC6,
+ 0x43, 0xC5, 0xE3, 0x71, 0x47, 0xFA, 0xAC, 0x92,
+ 0xC3, 0x97, 0x0B, 0xD3, 0x86, 0x2F, 0x53, 0x1E,
+ 0x5D, 0xCE, 0xA5, 0xCE, 0xAC, 0xD1, 0x83, 0x74,
+ 0x53, 0xAA, 0x49, 0x8D, 0x78, 0x5B, 0x4D, 0x1F,
+ 0x89, 0xE1, 0xB2, 0xA7, 0x39, 0xCA, 0x4A, 0x38,
+ 0x49, 0x87, 0x30, 0x27, 0x46, 0xB4, 0xF1, 0x13,
+ 0x42, 0x43, 0x02, 0xC4, 0xA1, 0xE0, 0xF9, 0xDF
+ },
+ {
+ 0x32, 0x3E, 0x67, 0x93, 0xC7, 0xDD, 0x9B, 0x4D,
+ 0x7B, 0xB7, 0xFB, 0xF2, 0x15, 0x31, 0xD3, 0x7F,
+ 0x72, 0x64, 0x53, 0x2C, 0x58, 0xF1, 0x22, 0x55,
+ 0x48, 0xD0, 0x6E, 0x69, 0x40, 0xC6, 0x3E, 0x91,
+ 0x27, 0x09, 0x90, 0xE7, 0xF5, 0x64, 0x32, 0x03,
+ 0xC9, 0x87, 0x64, 0x7E, 0x5C, 0xF6, 0x61, 0x03,
+ 0xE7, 0x9B, 0x71, 0x4C, 0x58, 0x1B, 0xD8, 0x77,
+ 0x2E, 0x19, 0xD0, 0xF0, 0x05, 0xDC, 0x86, 0x33
+ },
+ {
+ 0xF9, 0x22, 0x07, 0x6D, 0x29, 0x5D, 0x23, 0xE2,
+ 0x98, 0x58, 0x30, 0xAA, 0xD2, 0xF2, 0x3F, 0x65,
+ 0x2F, 0x7F, 0x4D, 0xB4, 0x2C, 0x11, 0x9E, 0xD2,
+ 0x20, 0xA5, 0x45, 0x14, 0x88, 0xA4, 0x53, 0xF5,
+ 0x9F, 0xA8, 0xA2, 0xDE, 0x23, 0x03, 0x00, 0x0D,
+ 0x6B, 0xFD, 0x8C, 0x48, 0x23, 0xA8, 0x5F, 0xAD,
+ 0xB4, 0xFB, 0x8E, 0x7E, 0xAC, 0x12, 0x2B, 0xF0,
+ 0x12, 0x47, 0xD7, 0x6F, 0x65, 0x24, 0x7D, 0x45
+ },
+ {
+ 0xDC, 0x40, 0x00, 0x95, 0x60, 0x95, 0x92, 0x91,
+ 0x55, 0x8E, 0xBE, 0x07, 0x20, 0x64, 0xCE, 0x67,
+ 0x12, 0xC9, 0x21, 0xB5, 0x40, 0x9B, 0x44, 0xE0,
+ 0x4F, 0x9A, 0x56, 0x5E, 0xEA, 0xDD, 0x39, 0xA7,
+ 0x71, 0x6E, 0x21, 0xB4, 0x6D, 0xD8, 0x61, 0x65,
+ 0x17, 0xA2, 0x1A, 0x0C, 0x03, 0x41, 0x9E, 0x94,
+ 0xDB, 0x82, 0x0A, 0x35, 0x3F, 0x15, 0x2D, 0x10,
+ 0x83, 0x84, 0xBE, 0x94, 0x70, 0x09, 0x3F, 0x89
+ },
+ {
+ 0x7F, 0xA4, 0xBE, 0x91, 0xCA, 0x52, 0x07, 0xFF,
+ 0x08, 0x7D, 0xE9, 0x2F, 0x1D, 0xB0, 0x9B, 0xF7,
+ 0x1A, 0x67, 0x87, 0x8B, 0xED, 0x19, 0x3A, 0x5C,
+ 0x2C, 0xC4, 0xE3, 0x53, 0x23, 0xB8, 0xDF, 0x99,
+ 0xA2, 0x6E, 0xCB, 0x98, 0x88, 0xD7, 0xB3, 0x4A,
+ 0x73, 0x9D, 0x64, 0x1A, 0x0E, 0xCD, 0x0A, 0x66,
+ 0x47, 0xA6, 0xA0, 0x64, 0x26, 0xF3, 0xCC, 0x1F,
+ 0xEF, 0xDF, 0x90, 0x69, 0x92, 0x2F, 0xAE, 0x4C
+ },
+ {
+ 0xBA, 0xD3, 0xCD, 0x75, 0x90, 0x5D, 0x7B, 0xFD,
+ 0xA3, 0x32, 0x2B, 0x44, 0xA7, 0xD3, 0x58, 0x87,
+ 0x14, 0xD3, 0x33, 0xEE, 0x86, 0x85, 0x5A, 0x87,
+ 0x27, 0x47, 0xE7, 0x04, 0xF6, 0x11, 0x94, 0x84,
+ 0xBD, 0xB7, 0xD0, 0x77, 0xFA, 0x08, 0xED, 0xC4,
+ 0xA7, 0x9D, 0xE0, 0xF4, 0x3F, 0xCA, 0x8D, 0x43,
+ 0x6E, 0x8A, 0x10, 0x08, 0x57, 0xF5, 0x9B, 0xC7,
+ 0xB0, 0x55, 0xB9, 0x87, 0xF9, 0x7A, 0xC6, 0xB9
+ },
+ {
+ 0xB7, 0xDE, 0xE8, 0xE8, 0x33, 0x9D, 0xB2, 0x97,
+ 0xFD, 0xAA, 0x3C, 0xA5, 0xC1, 0xDC, 0x19, 0x88,
+ 0xD9, 0x7F, 0x5F, 0xB6, 0x20, 0x8C, 0x64, 0xDE,
+ 0xA9, 0x5E, 0x1C, 0x78, 0xF3, 0x37, 0xCE, 0x20,
+ 0xA2, 0xB4, 0xDF, 0x17, 0xA7, 0xB8, 0x23, 0x6A,
+ 0x90, 0xD6, 0x28, 0x67, 0x33, 0x16, 0x35, 0x72,
+ 0xC8, 0x67, 0xD9, 0x3D, 0xE8, 0x9E, 0xF6, 0x2F,
+ 0xA0, 0x5D, 0xAB, 0x70, 0x7E, 0xC3, 0xA7, 0x70
+ },
+ {
+ 0xA0, 0xF7, 0xE9, 0x3C, 0xF3, 0x25, 0x02, 0xB9,
+ 0xFD, 0x79, 0xEC, 0x20, 0x54, 0x62, 0x07, 0xF3,
+ 0x31, 0xC5, 0x29, 0x9E, 0xCE, 0xF3, 0x50, 0xD6,
+ 0x6E, 0xA8, 0x55, 0xC8, 0x7F, 0xBD, 0xDF, 0x18,
+ 0xE6, 0x91, 0xC2, 0x0D, 0x04, 0x5A, 0x30, 0x8F,
+ 0x83, 0xF6, 0xCB, 0x8F, 0xCA, 0x69, 0xD7, 0xE2,
+ 0xB3, 0x9B, 0x34, 0xD2, 0xF8, 0x77, 0x27, 0x6C,
+ 0x19, 0x6B, 0xF5, 0x14, 0xBA, 0xC6, 0x02, 0x70
+ },
+ {
+ 0x6F, 0x50, 0x93, 0xCF, 0xC8, 0x83, 0x00, 0xBF,
+ 0x68, 0x8E, 0x88, 0x4B, 0x4C, 0x5E, 0xC2, 0xC3,
+ 0x1A, 0x8C, 0xC2, 0x8D, 0x63, 0x31, 0xAD, 0x7C,
+ 0xA7, 0x1D, 0x97, 0x60, 0x21, 0x64, 0x82, 0x05,
+ 0x28, 0x15, 0xD4, 0x4F, 0xC6, 0x9E, 0x18, 0xA8,
+ 0xDC, 0x8B, 0xD7, 0x1B, 0x31, 0xF2, 0xB5, 0x89,
+ 0xA7, 0xC0, 0x78, 0x0B, 0x61, 0x99, 0x38, 0x5F,
+ 0x8D, 0xAE, 0x6C, 0x9B, 0x79, 0x74, 0xC4, 0xCB
+ },
+ {
+ 0x3C, 0xFF, 0x46, 0xAC, 0x35, 0x46, 0xF6, 0x5A,
+ 0xD7, 0xA7, 0x20, 0x87, 0x1A, 0xFA, 0x20, 0xA9,
+ 0x21, 0x6D, 0xDA, 0x5C, 0x45, 0x18, 0x81, 0x56,
+ 0xA5, 0xBB, 0xED, 0xF2, 0x15, 0x46, 0xD4, 0xBB,
+ 0x39, 0x40, 0xB2, 0x1A, 0x41, 0xA3, 0x94, 0x03,
+ 0xE3, 0xCF, 0xD5, 0xE7, 0xA0, 0xE7, 0x90, 0x4D,
+ 0xA9, 0x5F, 0x4D, 0x8E, 0x0C, 0x5B, 0xF5, 0xB7,
+ 0x0E, 0xB0, 0x29, 0x55, 0x6E, 0xFD, 0x49, 0x7E
+ },
+ {
+ 0xAF, 0x66, 0x8A, 0x80, 0x5E, 0x6D, 0x70, 0x4B,
+ 0x1E, 0x58, 0x1F, 0x1E, 0x8E, 0x3C, 0x00, 0xCF,
+ 0x4C, 0xF3, 0xE5, 0x46, 0x14, 0x7C, 0x40, 0x6D,
+ 0x17, 0xCA, 0x97, 0x4D, 0x19, 0xA0, 0x14, 0xC7,
+ 0x8B, 0x44, 0xE7, 0x2D, 0xDE, 0xEB, 0x65, 0x26,
+ 0x07, 0xE8, 0x6D, 0x69, 0x02, 0x59, 0xDC, 0xAB,
+ 0x0D, 0xDA, 0x81, 0xC7, 0x7C, 0x7E, 0xE2, 0x72,
+ 0x1E, 0x82, 0xBB, 0xB1, 0x39, 0x43, 0x07, 0x1D
+ },
+ {
+ 0x79, 0xDD, 0xEB, 0x5C, 0x54, 0xDE, 0xD1, 0xE4,
+ 0x48, 0x40, 0x71, 0xC4, 0x6B, 0xB4, 0x28, 0x02,
+ 0xD2, 0x3B, 0x3A, 0x08, 0xC1, 0x23, 0x11, 0xBE,
+ 0x36, 0x3C, 0x7C, 0x7A, 0x02, 0x5A, 0x17, 0x64,
+ 0xC8, 0xD8, 0x50, 0x69, 0xFD, 0xA8, 0xD5, 0x17,
+ 0x77, 0x7D, 0x8D, 0xD8, 0x09, 0xE3, 0xD4, 0xA9,
+ 0x56, 0x04, 0x1A, 0x70, 0x79, 0xF9, 0x16, 0x7B,
+ 0x0F, 0xE9, 0x71, 0x2E, 0x5F, 0x12, 0x29, 0xF5
+ },
+ {
+ 0x99, 0x8E, 0x82, 0xF4, 0x26, 0x3D, 0x53, 0xAE,
+ 0xDA, 0xC9, 0x39, 0xEB, 0xB6, 0xEB, 0x8B, 0x19,
+ 0x69, 0x74, 0x6C, 0xB8, 0x15, 0xBD, 0x72, 0x1F,
+ 0x17, 0xA4, 0x8B, 0xEE, 0x9E, 0xCF, 0xF2, 0xFE,
+ 0x59, 0x8C, 0x53, 0x9C, 0x41, 0x9A, 0x60, 0xE0,
+ 0xD5, 0xA0, 0x4F, 0x1C, 0xB5, 0x23, 0xA2, 0xFD,
+ 0x05, 0x38, 0xBB, 0x17, 0x8E, 0x44, 0x75, 0x8D,
+ 0x31, 0x59, 0xAB, 0x9E, 0x02, 0x84, 0x01, 0xA3
+ },
+ {
+ 0x33, 0x96, 0xCF, 0xD5, 0xCD, 0xE1, 0x4A, 0xEC,
+ 0x1A, 0xAE, 0xD3, 0xE1, 0x22, 0x52, 0xCF, 0xD6,
+ 0xE3, 0x42, 0xED, 0x25, 0x5E, 0x8E, 0x9E, 0x1B,
+ 0xE1, 0x0F, 0x1F, 0x27, 0x38, 0x77, 0xF3, 0x63,
+ 0x33, 0x81, 0xE3, 0xC9, 0x61, 0xE6, 0x7E, 0xC4,
+ 0x1E, 0x8F, 0x9E, 0x16, 0x11, 0x0F, 0xC0, 0x3D,
+ 0xDE, 0x88, 0xBF, 0xC0, 0x96, 0xFC, 0x15, 0x14,
+ 0x46, 0x1D, 0x70, 0xD0, 0xBE, 0xCE, 0x0A, 0xF6
+ },
+ {
+ 0x77, 0x7D, 0x9D, 0xC5, 0x5A, 0x2F, 0x57, 0xA4,
+ 0x6E, 0xA0, 0x6A, 0x2F, 0x4C, 0xB9, 0x76, 0x0D,
+ 0x00, 0xD7, 0xA8, 0x62, 0xD0, 0xA2, 0xAA, 0x19,
+ 0x46, 0x7B, 0x57, 0x0F, 0x7C, 0x7D, 0x5E, 0xA7,
+ 0x62, 0x9A, 0x95, 0xEB, 0x20, 0x0E, 0x1F, 0x9D,
+ 0xB0, 0x66, 0x10, 0xCF, 0x8E, 0x30, 0xD5, 0xE6,
+ 0xAD, 0x0A, 0x7B, 0x63, 0x29, 0x77, 0xFC, 0x21,
+ 0xBB, 0x17, 0x89, 0x67, 0xF3, 0xB0, 0xE0, 0x9B
+ },
+ {
+ 0x32, 0xEE, 0x35, 0x7F, 0xC9, 0x16, 0x36, 0xA8,
+ 0x55, 0xBA, 0x01, 0xA0, 0xB8, 0xDA, 0x6F, 0x35,
+ 0x53, 0xB1, 0xD5, 0x20, 0xAD, 0xCF, 0xE8, 0xFE,
+ 0x9D, 0xEB, 0xCC, 0xB2, 0x6C, 0x5C, 0x4C, 0xE8,
+ 0x50, 0x5B, 0xB1, 0xEF, 0xB5, 0xED, 0x5B, 0xAA,
+ 0x4C, 0x52, 0x45, 0xB5, 0x0D, 0x74, 0x46, 0x3F,
+ 0x07, 0x67, 0xB2, 0xC7, 0x83, 0xC4, 0x7A, 0x93,
+ 0xB0, 0xFD, 0xA6, 0x68, 0x95, 0x69, 0x3C, 0xE6
+ },
+ {
+ 0x34, 0x0C, 0x0A, 0x7C, 0xE4, 0x96, 0xFE, 0xBD,
+ 0xA1, 0x3F, 0xA2, 0x40, 0x7A, 0x21, 0xDC, 0x19,
+ 0x83, 0x9B, 0xED, 0xAE, 0x1A, 0x08, 0x6A, 0xD0,
+ 0xFE, 0xD3, 0x91, 0x7D, 0xF9, 0xBF, 0x40, 0x94,
+ 0x4A, 0x78, 0x7F, 0x64, 0x1E, 0x90, 0xDD, 0xBA,
+ 0xE0, 0x3A, 0x93, 0x37, 0x72, 0x3E, 0x51, 0x66,
+ 0x8F, 0xB8, 0x93, 0x77, 0x2C, 0x0F, 0xBD, 0xB3,
+ 0xEB, 0x7E, 0xF7, 0x90, 0xDF, 0xCB, 0xB9, 0xAB
+ },
+ {
+ 0xD8, 0x6A, 0x5B, 0xAA, 0x33, 0x65, 0xAB, 0xD8,
+ 0xF4, 0x42, 0xCD, 0x6E, 0xBB, 0x93, 0x11, 0x38,
+ 0x19, 0xF0, 0xB4, 0x60, 0x61, 0xE1, 0x34, 0x04,
+ 0xEF, 0xAA, 0x1A, 0x58, 0xE1, 0xFF, 0x27, 0x2A,
+ 0xD4, 0xBF, 0xD3, 0x08, 0x15, 0xAD, 0xD8, 0x8A,
+ 0xD9, 0x8F, 0xCE, 0x9A, 0xF0, 0x18, 0x37, 0x4C,
+ 0xA6, 0x0D, 0x89, 0x79, 0x0F, 0x71, 0xA6, 0x07,
+ 0x5F, 0x3D, 0x68, 0xD3, 0x20, 0x21, 0xA9, 0xEB
+ },
+ {
+ 0xA6, 0x7E, 0x6E, 0xC6, 0x57, 0xC9, 0x5E, 0xAB,
+ 0x3C, 0x3C, 0x32, 0xE4, 0x1F, 0xBF, 0x39, 0xCF,
+ 0x20, 0x33, 0xAB, 0x4B, 0xE2, 0xE2, 0xB8, 0x21,
+ 0x10, 0x4A, 0xDB, 0xE6, 0x9D, 0x16, 0xE9, 0x48,
+ 0xDC, 0xE4, 0xC4, 0xC6, 0xA3, 0xCF, 0x22, 0x76,
+ 0x90, 0x1F, 0x7D, 0x4F, 0xFD, 0x69, 0x65, 0x46,
+ 0x49, 0x88, 0x2C, 0x01, 0x4D, 0x2C, 0x10, 0xA1,
+ 0x30, 0x2B, 0x79, 0xC6, 0x15, 0x69, 0xCD, 0x36
+ },
+ {
+ 0x55, 0xCE, 0x19, 0x2A, 0xE4, 0xB3, 0xEA, 0xF8,
+ 0x55, 0x59, 0x0E, 0x2D, 0x44, 0xE6, 0x25, 0xD9,
+ 0xBA, 0x14, 0x6E, 0xB7, 0x50, 0x48, 0xE6, 0xB5,
+ 0x6E, 0x02, 0x50, 0x31, 0xEF, 0xBA, 0x0B, 0xDA,
+ 0x8A, 0xAA, 0xFA, 0x04, 0x70, 0xB7, 0xAC, 0x3D,
+ 0x40, 0x6E, 0x5A, 0xBA, 0x3E, 0x83, 0x2F, 0x27,
+ 0xA5, 0x07, 0x24, 0x6D, 0x1B, 0x5F, 0x33, 0xDE,
+ 0xA1, 0xF7, 0x24, 0xE2, 0xB8, 0x1B, 0x0C, 0x98
+ },
+ {
+ 0xB3, 0xA2, 0x0C, 0x1F, 0xB0, 0xB4, 0xF0, 0xD3,
+ 0x77, 0x26, 0xC2, 0x3B, 0x58, 0x77, 0xDD, 0x8E,
+ 0x72, 0xF6, 0x98, 0x86, 0xE0, 0x9A, 0x8C, 0x68,
+ 0xCF, 0xC3, 0x01, 0xD2, 0xA3, 0xF2, 0xF9, 0x5C,
+ 0xEF, 0xCF, 0xAB, 0xB8, 0x88, 0x99, 0x03, 0xC7,
+ 0x32, 0xF4, 0xE8, 0x14, 0x32, 0xD3, 0xF6, 0x78,
+ 0xCC, 0xDF, 0xC3, 0x98, 0xAC, 0xD8, 0xA2, 0xF0,
+ 0x66, 0x41, 0x10, 0x04, 0x50, 0xD8, 0x9F, 0x32
+ },
+ {
+ 0xF7, 0x27, 0x2D, 0x93, 0xC7, 0x01, 0x2D, 0x38,
+ 0xB2, 0x7F, 0x0C, 0x9A, 0xE2, 0x01, 0x79, 0x58,
+ 0xBB, 0xA6, 0x66, 0xA9, 0xDE, 0x1E, 0x88, 0x12,
+ 0xE9, 0x74, 0x37, 0xAE, 0xB2, 0xE0, 0x3C, 0x99,
+ 0x94, 0x38, 0xF0, 0xBE, 0x33, 0x3D, 0x09, 0xAD,
+ 0xDB, 0xCF, 0xAA, 0xC7, 0xAA, 0x73, 0xF7, 0xB6,
+ 0xCC, 0xEC, 0x67, 0xDC, 0x07, 0x79, 0x98, 0xDE,
+ 0xDB, 0x8C, 0x13, 0x32, 0xBA, 0xC0, 0xFB, 0xA8
+ },
+ {
+ 0x1F, 0xE7, 0xB3, 0xDE, 0x34, 0xC0, 0x47, 0x9C,
+ 0xA8, 0x40, 0x5F, 0x3C, 0xBC, 0xD2, 0xDB, 0x64,
+ 0xBB, 0x18, 0xDB, 0xB2, 0x91, 0xA5, 0xFE, 0xAA,
+ 0x16, 0xC5, 0x22, 0x8C, 0x93, 0xEE, 0x21, 0xC7,
+ 0x11, 0xD6, 0x8A, 0x01, 0x0C, 0x2A, 0xE8, 0x80,
+ 0x05, 0xEB, 0xAC, 0x95, 0x9E, 0x3A, 0x32, 0x24,
+ 0x52, 0xF8, 0x62, 0xDD, 0xE9, 0x4B, 0xB9, 0x41,
+ 0x81, 0x3E, 0x52, 0x4D, 0x23, 0x47, 0xFE, 0xEE
+ },
+ {
+ 0x4E, 0xE1, 0xD3, 0x88, 0x05, 0xC3, 0x22, 0x84,
+ 0xEC, 0xEB, 0xE9, 0x2E, 0x3D, 0xF6, 0xCD, 0x98,
+ 0xC7, 0xD6, 0x68, 0x0E, 0xAB, 0x0D, 0x68, 0x66,
+ 0x4F, 0x96, 0x70, 0x6C, 0x45, 0x63, 0x3B, 0x1E,
+ 0x26, 0x82, 0x22, 0xAA, 0x5A, 0x52, 0x79, 0xEF,
+ 0x01, 0xFC, 0x28, 0x54, 0x32, 0xAB, 0xEE, 0xD7,
+ 0x4B, 0xA3, 0xDF, 0x18, 0x9F, 0x50, 0xA9, 0x89,
+ 0xD5, 0x8E, 0x71, 0x30, 0x62, 0x2D, 0xAA, 0x59
+ },
+ {
+ 0x0E, 0x14, 0x05, 0x87, 0x1C, 0x87, 0xA5, 0xEA,
+ 0x40, 0x83, 0x42, 0xF3, 0x9D, 0x34, 0x94, 0xF9,
+ 0x39, 0xF7, 0x3C, 0x22, 0x60, 0xC2, 0xA4, 0x3A,
+ 0x5C, 0x9F, 0x1B, 0x57, 0x33, 0x0C, 0xCA, 0x40,
+ 0x93, 0xFC, 0x1F, 0x42, 0xF9, 0x6D, 0x83, 0x00,
+ 0x56, 0x77, 0x03, 0x7D, 0xB5, 0x1A, 0xEF, 0x26,
+ 0xF0, 0x54, 0x38, 0x05, 0x7A, 0xE7, 0x9E, 0xD1,
+ 0x44, 0x64, 0xFD, 0x8E, 0x57, 0xD1, 0x55, 0x86
+ },
+ {
+ 0x17, 0xC5, 0xCA, 0xB4, 0x09, 0x10, 0x73, 0x62,
+ 0x1B, 0x5C, 0x24, 0xC3, 0x36, 0x31, 0x6D, 0x0C,
+ 0xF6, 0x49, 0xBA, 0x1E, 0xFF, 0xEB, 0xFC, 0x87,
+ 0xE0, 0x43, 0x9C, 0xDF, 0x57, 0x88, 0x87, 0xB2,
+ 0x21, 0x65, 0x6D, 0x33, 0x9A, 0x6F, 0xD1, 0x98,
+ 0xAB, 0xAE, 0xE6, 0x7E, 0xA1, 0x88, 0xDD, 0x66,
+ 0x56, 0x78, 0x23, 0xFC, 0x22, 0x0C, 0x52, 0xB5,
+ 0x74, 0x90, 0x25, 0x14, 0x69, 0xD2, 0x5D, 0x8C
+ },
+ {
+ 0x57, 0xDC, 0x27, 0x97, 0xD1, 0x42, 0x68, 0x1C,
+ 0x94, 0xFE, 0x48, 0x86, 0x26, 0x98, 0x6E, 0xD4,
+ 0xB2, 0x67, 0x03, 0xCB, 0xF6, 0xBF, 0xE5, 0x93,
+ 0x91, 0x64, 0x36, 0x57, 0x06, 0x5B, 0x2D, 0x46,
+ 0xE4, 0xB1, 0xDD, 0xB3, 0xAA, 0x83, 0x2C, 0x9B,
+ 0xD4, 0x49, 0x75, 0x5A, 0xC8, 0xB1, 0xBF, 0x93,
+ 0x68, 0x97, 0xFB, 0xC6, 0xAD, 0xE3, 0x78, 0xF2,
+ 0xBD, 0x64, 0x93, 0xE4, 0x86, 0xF4, 0x20, 0x29
+ },
+ {
+ 0x44, 0x12, 0xDD, 0x6B, 0xED, 0x6D, 0xB2, 0xA8,
+ 0x03, 0xC2, 0xE0, 0xDF, 0x8F, 0x58, 0x29, 0xE7,
+ 0xA4, 0xB0, 0x41, 0x78, 0x89, 0x51, 0x0D, 0xF7,
+ 0xDF, 0xEE, 0x49, 0x57, 0x4A, 0x71, 0xEC, 0x0D,
+ 0x9E, 0x0D, 0x46, 0x06, 0x50, 0x17, 0xC7, 0x2D,
+ 0xD9, 0x74, 0x39, 0x33, 0xCA, 0x83, 0x9A, 0x76,
+ 0x8D, 0xD1, 0x5A, 0xB0, 0xB7, 0xC1, 0x4C, 0x62,
+ 0x6A, 0x35, 0x41, 0x09, 0x69, 0x01, 0x96, 0xAE
+ },
+ {
+ 0xD0, 0xEB, 0xC7, 0x71, 0x03, 0x1B, 0x7C, 0x16,
+ 0x00, 0x21, 0xC9, 0xB6, 0xFB, 0xB2, 0xB6, 0x70,
+ 0xE3, 0xB4, 0x02, 0x70, 0x02, 0x69, 0x07, 0xA3,
+ 0x91, 0x63, 0xDB, 0x18, 0x73, 0xEC, 0xC3, 0xB8,
+ 0x00, 0x11, 0x1D, 0xD7, 0xBF, 0x13, 0x8F, 0x83,
+ 0xA6, 0x10, 0xDC, 0x04, 0x6D, 0xA2, 0x68, 0xB7,
+ 0x2B, 0x8C, 0x90, 0x86, 0x92, 0x23, 0x77, 0xDB,
+ 0xED, 0x73, 0x94, 0x82, 0x43, 0xCA, 0x1E, 0x14
+ },
+ {
+ 0x10, 0xC4, 0xBA, 0x31, 0x55, 0x91, 0x69, 0x8D,
+ 0xFB, 0x91, 0xA5, 0x73, 0x37, 0x63, 0x18, 0x84,
+ 0xB4, 0x73, 0x8D, 0x9F, 0x59, 0x80, 0x78, 0x51,
+ 0xA6, 0x79, 0x84, 0x0C, 0xC2, 0x87, 0xAC, 0xE3,
+ 0x01, 0x1C, 0xCD, 0xC8, 0xF4, 0xA4, 0x85, 0xBB,
+ 0x19, 0x73, 0x40, 0x4E, 0xF9, 0xEE, 0x9B, 0x9C,
+ 0xF1, 0xEA, 0xDB, 0xC5, 0x40, 0x74, 0xC6, 0xD1,
+ 0x13, 0xDE, 0x8F, 0xC9, 0x1D, 0x07, 0x97, 0xEB
+ },
+ {
+ 0x14, 0x64, 0x34, 0x7B, 0xE3, 0x2C, 0x79, 0x59,
+ 0x17, 0x2B, 0x74, 0x72, 0xD1, 0x1F, 0xE0, 0x78,
+ 0x44, 0xA5, 0x2E, 0x2D, 0x3B, 0x2D, 0x05, 0x8C,
+ 0xC6, 0xBC, 0xC0, 0xA8, 0xA2, 0x75, 0xD6, 0xB8,
+ 0x2B, 0x2D, 0x62, 0x63, 0x75, 0x5E, 0xAF, 0x2A,
+ 0x65, 0x88, 0xB6, 0xA1, 0xEB, 0x79, 0x9A, 0xF8,
+ 0x3A, 0x4C, 0xE7, 0x53, 0xF8, 0xC7, 0x5A, 0x22,
+ 0x84, 0xD0, 0x28, 0x5B, 0xAB, 0x5F, 0x7C, 0x1C
+ },
+ {
+ 0xF4, 0x09, 0x23, 0x1E, 0xD1, 0x87, 0xF5, 0xC4,
+ 0xE8, 0x33, 0xFA, 0x9E, 0x30, 0x42, 0xAC, 0xA6,
+ 0xC8, 0x58, 0xB0, 0x8B, 0x49, 0x6B, 0x25, 0x31,
+ 0xF8, 0x4F, 0xD5, 0xCE, 0xA9, 0x3E, 0xCD, 0x06,
+ 0xDA, 0xFE, 0x0A, 0x10, 0xC3, 0xFF, 0x23, 0x76,
+ 0xC7, 0x4D, 0xC8, 0x0D, 0xA0, 0x7D, 0xA0, 0x18,
+ 0x64, 0xFB, 0xF2, 0x68, 0x59, 0x60, 0xB5, 0x40,
+ 0xB3, 0xA2, 0xE9, 0x42, 0xCB, 0x8D, 0x90, 0x9F
+ },
+ {
+ 0x39, 0x51, 0x32, 0xC5, 0x80, 0xC3, 0x55, 0xB5,
+ 0xB0, 0xE2, 0x35, 0x33, 0x6C, 0x8D, 0xC1, 0x08,
+ 0x5E, 0x59, 0x59, 0x64, 0x04, 0x3D, 0x38, 0x9E,
+ 0x08, 0x1E, 0xFE, 0x48, 0x5B, 0xA4, 0xC6, 0x37,
+ 0x72, 0xDB, 0x8D, 0x7E, 0x0F, 0x18, 0x6C, 0x50,
+ 0x98, 0x2E, 0x12, 0x23, 0xEA, 0x78, 0x5A, 0xDC,
+ 0x74, 0x0B, 0x0C, 0xF2, 0x18, 0x70, 0x74, 0x58,
+ 0xB8, 0xB8, 0x03, 0x40, 0x42, 0xF9, 0x23, 0xC2
+ },
+ {
+ 0xF9, 0x2A, 0xBA, 0xCA, 0x21, 0x32, 0x29, 0x66,
+ 0x06, 0x49, 0xEF, 0x2D, 0x8F, 0x88, 0x11, 0x5B,
+ 0x5B, 0xED, 0x8A, 0xB5, 0xB9, 0xBC, 0xA9, 0xA1,
+ 0xB4, 0xC5, 0x24, 0x57, 0x03, 0x53, 0x10, 0xC4,
+ 0x1A, 0x6B, 0xEA, 0x2B, 0x23, 0xB7, 0x91, 0x8B,
+ 0x5B, 0x8B, 0xF3, 0x8B, 0x52, 0xEA, 0xC6, 0xFF,
+ 0x3B, 0x62, 0x13, 0xA5, 0x22, 0xF3, 0x81, 0xBE,
+ 0x7F, 0xF0, 0x90, 0x6D, 0xBA, 0x7B, 0xD0, 0x0C
+ },
+ {
+ 0xCB, 0xAD, 0xE7, 0xAD, 0x3B, 0x5D, 0xEE, 0x0F,
+ 0xF1, 0xA4, 0x6B, 0x08, 0x2C, 0xF4, 0xE1, 0xE1,
+ 0xDC, 0x21, 0x62, 0x0D, 0xD2, 0xCC, 0x0E, 0xDC,
+ 0x2C, 0x70, 0x7A, 0x21, 0x62, 0xD2, 0x14, 0x99,
+ 0x69, 0xAB, 0xBB, 0x29, 0xC5, 0x72, 0x0B, 0x04,
+ 0xBD, 0x15, 0x68, 0xA9, 0x55, 0x61, 0x95, 0xE6,
+ 0x7F, 0x24, 0x32, 0x2D, 0xD9, 0xAA, 0x4E, 0x83,
+ 0x65, 0x19, 0x1A, 0xA5, 0xB6, 0xC4, 0x45, 0x79
+ },
+ {
+ 0xF5, 0x1B, 0x4A, 0xE4, 0xD4, 0xC5, 0x4A, 0x29,
+ 0xCF, 0x71, 0x35, 0xA8, 0xFE, 0x1E, 0xAB, 0xD5,
+ 0xE1, 0xBC, 0xBF, 0x82, 0x08, 0x96, 0x96, 0x7D,
+ 0xC4, 0x1E, 0x38, 0x49, 0xDA, 0xC2, 0x25, 0x07,
+ 0x69, 0x42, 0x10, 0xCA, 0x11, 0xC4, 0xEB, 0xF1,
+ 0xC2, 0x9A, 0x8D, 0x4F, 0x71, 0xB3, 0x0F, 0x76,
+ 0xC9, 0xB6, 0x01, 0x0A, 0xD9, 0x5B, 0xDF, 0xB0,
+ 0xDE, 0x83, 0x79, 0x25, 0xF0, 0x61, 0x25, 0x97
+ },
+ {
+ 0xCE, 0x38, 0x72, 0x11, 0x5D, 0x83, 0x3B, 0x34,
+ 0x56, 0xCA, 0x94, 0x2E, 0x6E, 0x38, 0x5F, 0x28,
+ 0xA9, 0x03, 0xBE, 0xAB, 0xFB, 0x75, 0x3F, 0x8A,
+ 0xFC, 0xCC, 0x12, 0xF2, 0x58, 0x2C, 0xE1, 0xF3,
+ 0x62, 0x12, 0xBD, 0x05, 0xE0, 0x5A, 0x46, 0xFC,
+ 0x88, 0xD3, 0x19, 0x50, 0xB4, 0x91, 0x1A, 0xE5,
+ 0xDC, 0xD8, 0xFF, 0x7A, 0x0B, 0x50, 0x47, 0x4C,
+ 0xB4, 0x88, 0xCC, 0xF2, 0xA8, 0x9C, 0xD0, 0xEB
+ },
+ {
+ 0x9B, 0xB7, 0x4C, 0xBD, 0x47, 0xA6, 0x24, 0xCB,
+ 0xEA, 0xFC, 0xC1, 0x6D, 0x46, 0x29, 0x47, 0xBB,
+ 0xEA, 0x13, 0x70, 0xB8, 0x5C, 0x96, 0x1A, 0x40,
+ 0x7D, 0xF9, 0x86, 0x3E, 0x54, 0xE6, 0xD9, 0xE6,
+ 0xA8, 0xD2, 0xEF, 0x0C, 0x64, 0x97, 0x20, 0x5E,
+ 0x5E, 0xB7, 0xC3, 0xE5, 0x9E, 0x69, 0x8D, 0x99,
+ 0x24, 0x63, 0xCA, 0x9D, 0xD4, 0xCF, 0x28, 0xCF,
+ 0x9A, 0x2D, 0x4E, 0x30, 0xC1, 0x33, 0xE8, 0x55
+ },
+ {
+ 0x72, 0x96, 0x33, 0x82, 0x0B, 0xF0, 0x13, 0xD9,
+ 0xD2, 0xBD, 0x37, 0x3C, 0xCA, 0xC7, 0xBC, 0x9F,
+ 0x37, 0x16, 0xF6, 0x9E, 0x16, 0xA4, 0x4E, 0x94,
+ 0x9C, 0x7A, 0x9A, 0x93, 0xDC, 0xA1, 0x26, 0xBB,
+ 0x1A, 0xA5, 0x4E, 0x5E, 0x70, 0x40, 0x70, 0x7F,
+ 0x02, 0x87, 0x6A, 0xFD, 0x02, 0x0A, 0xF4, 0x72,
+ 0x63, 0x9D, 0x49, 0xF5, 0x42, 0x0D, 0x29, 0x4C,
+ 0x3A, 0xA3, 0x1D, 0x06, 0x7E, 0x3E, 0x85, 0x75
+ },
+ {
+ 0x06, 0x86, 0x1D, 0xB3, 0x07, 0xC6, 0x78, 0x08,
+ 0x6E, 0x8B, 0x2A, 0xEC, 0xDF, 0x18, 0x29, 0xD2,
+ 0x88, 0x3D, 0x28, 0xB7, 0x31, 0xAB, 0xD0, 0xF1,
+ 0xE7, 0x2F, 0x1C, 0xED, 0x6C, 0x7A, 0xD4, 0x17,
+ 0x2E, 0xCA, 0x63, 0x22, 0xA8, 0x3F, 0xB6, 0xA6,
+ 0x5A, 0xFA, 0x37, 0xE9, 0x4A, 0x3E, 0x2B, 0xA2,
+ 0x05, 0xB8, 0x7B, 0xF3, 0x82, 0xD9, 0x15, 0x88,
+ 0x49, 0x7A, 0x46, 0x50, 0x88, 0x3B, 0xD8, 0x75
+ },
+ {
+ 0x35, 0x6E, 0xCE, 0xAF, 0x17, 0x02, 0xB3, 0x70,
+ 0xF4, 0xAA, 0xB8, 0xEA, 0x82, 0x84, 0x86, 0xF3,
+ 0x30, 0x13, 0xF7, 0x44, 0xB3, 0x9E, 0x7E, 0xA2,
+ 0x6C, 0x69, 0x18, 0xD6, 0x0E, 0x1A, 0xBC, 0xF4,
+ 0x4F, 0xB1, 0x6E, 0xDC, 0xA7, 0x72, 0x0A, 0xCF,
+ 0xC6, 0xA7, 0x01, 0xBF, 0x1E, 0x2C, 0x35, 0xDD,
+ 0xBD, 0x69, 0x5A, 0x8D, 0x40, 0x8E, 0x8C, 0x96,
+ 0x32, 0xE8, 0xCD, 0x27, 0x23, 0x0C, 0xAD, 0x8D
+ },
+ {
+ 0x48, 0x9A, 0x39, 0xD0, 0xFC, 0x3C, 0xDE, 0xAF,
+ 0x42, 0x89, 0x2E, 0xD8, 0x03, 0x85, 0xC1, 0x1C,
+ 0xE2, 0x93, 0xC9, 0x32, 0x21, 0x5B, 0xB2, 0x31,
+ 0x88, 0x69, 0x2A, 0x86, 0xE6, 0x1B, 0xCA, 0xD9,
+ 0x2C, 0x2A, 0x1D, 0x11, 0x42, 0x60, 0x1B, 0x1B,
+ 0xDF, 0x09, 0x82, 0xD1, 0xCD, 0x1E, 0x05, 0xC0,
+ 0x52, 0xDE, 0x81, 0x9E, 0x64, 0xF2, 0x47, 0xDB,
+ 0x35, 0x91, 0x5D, 0xD1, 0xDB, 0x79, 0xA3, 0xB5
+ },
+ {
+ 0xC0, 0x2F, 0x46, 0x4B, 0x4D, 0xD1, 0x81, 0x17,
+ 0xE3, 0x0A, 0x8D, 0xB8, 0xEF, 0x1D, 0xA0, 0x67,
+ 0x13, 0x4B, 0x60, 0x4E, 0xFA, 0x19, 0x51, 0x76,
+ 0x7E, 0xE6, 0x32, 0xDC, 0x02, 0x4D, 0x64, 0xC0,
+ 0x0F, 0x24, 0x49, 0xF0, 0x42, 0xDB, 0x3A, 0xEA,
+ 0x01, 0x74, 0xEB, 0xCD, 0xBB, 0x4F, 0xF5, 0x9D,
+ 0xAE, 0x75, 0x4F, 0x72, 0x39, 0x46, 0xF1, 0xB9,
+ 0x0A, 0x77, 0xFD, 0x95, 0x23, 0x69, 0x0B, 0x7B
+ },
+ {
+ 0xFB, 0x31, 0xE6, 0xDD, 0xB8, 0x6D, 0xBF, 0xF3,
+ 0x72, 0x64, 0x6D, 0x1E, 0x3A, 0x3F, 0x31, 0xDD,
+ 0x61, 0x15, 0x9F, 0xC3, 0x93, 0x65, 0x8C, 0x2E,
+ 0xE9, 0x57, 0x10, 0x3B, 0xF2, 0x11, 0x6B, 0xDE,
+ 0xF8, 0x2C, 0x33, 0xE8, 0x69, 0xF3, 0xC8, 0x3A,
+ 0xC3, 0xC2, 0xF6, 0x38, 0x0C, 0xF6, 0x92, 0xF7,
+ 0xB1, 0xDC, 0xBA, 0xE0, 0xBB, 0x22, 0x7A, 0xD3,
+ 0x47, 0xE7, 0x54, 0x13, 0x74, 0x66, 0xC6, 0x9F
+ },
+ {
+ 0x00, 0x60, 0x62, 0xAB, 0xE1, 0x6C, 0x2F, 0xE7,
+ 0x9A, 0xF8, 0x80, 0x85, 0xE0, 0xB5, 0x82, 0xB1,
+ 0x06, 0xE7, 0xF7, 0x9F, 0x01, 0xA4, 0x39, 0x46,
+ 0xC7, 0x8B, 0x19, 0xF9, 0xBD, 0xD7, 0x25, 0x99,
+ 0x76, 0x36, 0xA3, 0x32, 0xEB, 0x9A, 0x3A, 0xAA,
+ 0x6D, 0xE0, 0xD4, 0xA8, 0xE9, 0xE2, 0x8E, 0x8C,
+ 0x77, 0x87, 0x74, 0x22, 0x4C, 0x66, 0x5B, 0xF7,
+ 0xBC, 0x36, 0x44, 0xFC, 0xE4, 0x11, 0x22, 0x8C
+ },
+ {
+ 0xD4, 0x4A, 0x6D, 0xB3, 0xDE, 0x9F, 0xD4, 0xE4,
+ 0xA7, 0xEF, 0x15, 0x5A, 0x01, 0xBC, 0xCB, 0x91,
+ 0xC1, 0xBC, 0xF1, 0xCB, 0x53, 0x22, 0x56, 0x89,
+ 0xA7, 0x7A, 0x0D, 0x23, 0xB4, 0xD3, 0x9A, 0x89,
+ 0xA1, 0x89, 0xF2, 0x89, 0x80, 0xF9, 0x1C, 0x56,
+ 0xEA, 0xC5, 0x87, 0x9E, 0xAE, 0x93, 0x3C, 0xED,
+ 0x7F, 0x26, 0x7E, 0x2F, 0x70, 0x40, 0xEB, 0x38,
+ 0x0F, 0xDB, 0xBF, 0x34, 0xA6, 0xB7, 0xB6, 0x15
+ },
+ {
+ 0x5A, 0xFB, 0xFE, 0xA1, 0xDE, 0xDA, 0x5A, 0xEA,
+ 0xB9, 0x2E, 0x4D, 0x0C, 0x31, 0xD1, 0x6A, 0x9A,
+ 0x86, 0xBF, 0x7C, 0x75, 0x23, 0x27, 0x4A, 0x05,
+ 0xC5, 0x05, 0x29, 0xF5, 0xC1, 0x39, 0xDB, 0x10,
+ 0x93, 0x3A, 0x52, 0xC6, 0x22, 0x9C, 0xD3, 0x11,
+ 0x08, 0xF0, 0x83, 0xFB, 0x0C, 0x85, 0xCF, 0x52,
+ 0x83, 0x1B, 0x5A, 0x05, 0xF2, 0x55, 0x0A, 0x77,
+ 0xB5, 0x70, 0x3C, 0xC6, 0x68, 0x91, 0x2D, 0xBC
+ },
+ {
+ 0xD1, 0x7F, 0xCA, 0xD4, 0xE0, 0xD8, 0xBD, 0xE2,
+ 0xED, 0xFD, 0xA1, 0x68, 0xBA, 0x47, 0x10, 0x4B,
+ 0xBC, 0xA4, 0xD2, 0x6D, 0xA2, 0xD3, 0x1A, 0x07,
+ 0x0B, 0x0F, 0xBA, 0x0B, 0x26, 0xEE, 0xDD, 0x95,
+ 0xEE, 0xC1, 0xFC, 0x34, 0xD7, 0x6C, 0xD4, 0xA1,
+ 0xCB, 0x15, 0xF2, 0x62, 0x16, 0x88, 0xA9, 0xCC,
+ 0x0E, 0x96, 0x35, 0x8D, 0xE9, 0x93, 0x22, 0x2B,
+ 0xB3, 0xE3, 0xCD, 0x0B, 0xFD, 0xCB, 0x74, 0x6C
+ },
+ {
+ 0xBD, 0x6A, 0x59, 0x21, 0x63, 0x37, 0xB4, 0x5D,
+ 0x6B, 0x71, 0xAE, 0xAC, 0x01, 0x36, 0x6B, 0xFE,
+ 0x96, 0x60, 0xE0, 0xFB, 0xC2, 0x95, 0x9A, 0xDB,
+ 0xB6, 0x8D, 0x52, 0x6C, 0x43, 0xD4, 0x8F, 0xFF,
+ 0xFE, 0x2F, 0xFC, 0x43, 0x05, 0x88, 0xE7, 0x8E,
+ 0x66, 0x54, 0x6A, 0x3C, 0x70, 0x9B, 0x0A, 0xCE,
+ 0xA1, 0x7C, 0xBC, 0x5A, 0x21, 0x8C, 0x53, 0xCD,
+ 0x47, 0xAA, 0x48, 0x71, 0xC1, 0xDD, 0x98, 0x4A
+ },
+ {
+ 0x83, 0xEA, 0x5A, 0xE1, 0x89, 0x11, 0x45, 0xC4,
+ 0x1A, 0x7C, 0x6C, 0x87, 0xFE, 0x92, 0x24, 0x87,
+ 0xF5, 0xD2, 0x82, 0x93, 0x35, 0x69, 0xB7, 0xAE,
+ 0x0E, 0x34, 0x56, 0x53, 0x38, 0x1E, 0xDE, 0x6D,
+ 0x4B, 0x16, 0xE1, 0x44, 0xD1, 0xC3, 0xE8, 0xF0,
+ 0x60, 0x5D, 0xAA, 0x0D, 0xB5, 0x96, 0x5A, 0x7B,
+ 0x79, 0xD9, 0x1A, 0x8A, 0xFE, 0x11, 0xF1, 0xE0,
+ 0xBC, 0x54, 0x9A, 0xC0, 0x74, 0xA0, 0x1A, 0xB7
+ },
+ {
+ 0x37, 0x50, 0x50, 0xCF, 0x2E, 0x43, 0x0D, 0x0E,
+ 0x29, 0x87, 0x58, 0x35, 0x20, 0x8E, 0x89, 0x06,
+ 0xD7, 0x05, 0x2E, 0x47, 0x29, 0x2C, 0x5A, 0x38,
+ 0xA6, 0x30, 0x82, 0x87, 0x3D, 0x31, 0xD5, 0x83,
+ 0x13, 0x5C, 0x07, 0xA2, 0x0C, 0x52, 0xD9, 0x5B,
+ 0x2D, 0x5D, 0xC3, 0xEA, 0xDE, 0x6B, 0xE1, 0x43,
+ 0xCA, 0x34, 0x38, 0xF4, 0x4D, 0x02, 0x0A, 0xAE,
+ 0x16, 0x0E, 0xD7, 0x7A, 0xB9, 0x88, 0x4F, 0x7D
+ },
+ {
+ 0x30, 0x28, 0xB0, 0xE8, 0x24, 0x95, 0x7F, 0xF3,
+ 0xB3, 0x05, 0xE9, 0x7F, 0xF5, 0x92, 0xAA, 0x8E,
+ 0xF2, 0x9B, 0x3B, 0xEC, 0x1D, 0xC4, 0x7B, 0x76,
+ 0x13, 0x3D, 0x10, 0x3F, 0xFE, 0x38, 0x71, 0xBF,
+ 0x05, 0x12, 0xA2, 0x31, 0xAF, 0xCB, 0x1D, 0xF8,
+ 0x65, 0x97, 0xEC, 0x5E, 0x46, 0xE9, 0x23, 0xC8,
+ 0xB9, 0x85, 0xC2, 0x85, 0x08, 0x57, 0xC6, 0x40,
+ 0x01, 0xB2, 0xC5, 0x51, 0xEA, 0x83, 0x3D, 0x0E
+ },
+ {
+ 0x08, 0x7C, 0xCB, 0x1E, 0x5B, 0xD1, 0x72, 0x22,
+ 0xB8, 0xAF, 0x20, 0x6D, 0xD6, 0x39, 0x08, 0xF8,
+ 0x91, 0x72, 0x97, 0x62, 0x1A, 0x8C, 0xB9, 0x33,
+ 0x0A, 0xE0, 0xBA, 0x4A, 0xF3, 0xE9, 0xD6, 0x0C,
+ 0x98, 0xFC, 0xF1, 0xEF, 0xFC, 0xEC, 0x20, 0x13,
+ 0x6B, 0x4F, 0x91, 0x88, 0x12, 0x6D, 0xFA, 0x04,
+ 0x4E, 0x1C, 0x1C, 0xCD, 0xA3, 0xCE, 0xD8, 0x73,
+ 0x73, 0xD9, 0x37, 0x9C, 0xCB, 0xED, 0xBD, 0xB3
+ },
+ {
+ 0x7F, 0x17, 0x06, 0x24, 0x98, 0xBF, 0xA2, 0xBB,
+ 0x58, 0x56, 0xCD, 0x0A, 0x62, 0xC5, 0x68, 0xC5,
+ 0xC6, 0xB8, 0x97, 0x43, 0x24, 0x74, 0xEF, 0xB2,
+ 0xE6, 0xA2, 0xEE, 0x18, 0xCA, 0xFF, 0xD2, 0x1E,
+ 0x1E, 0xF3, 0x0D, 0x06, 0x47, 0x23, 0x85, 0x0F,
+ 0x79, 0x90, 0xD2, 0x1B, 0xA3, 0x4E, 0x8F, 0x2B,
+ 0x3B, 0xB0, 0x67, 0x02, 0x3A, 0x77, 0x27, 0x82,
+ 0x15, 0x8A, 0x27, 0xC6, 0xC4, 0x67, 0xC9, 0x28
+ },
+ {
+ 0x6B, 0xA9, 0x86, 0xA9, 0x42, 0x49, 0x7F, 0xD3,
+ 0x84, 0x62, 0x97, 0x2F, 0x50, 0xA6, 0x19, 0x68,
+ 0xC0, 0x65, 0x2D, 0xAC, 0x56, 0xCE, 0x9B, 0x9A,
+ 0xC1, 0xBC, 0x06, 0x1A, 0xB6, 0x34, 0xFE, 0x5A,
+ 0x77, 0xAC, 0xD0, 0x27, 0x5F, 0x83, 0x96, 0xE3,
+ 0xC0, 0xBE, 0xF0, 0x12, 0xAE, 0x93, 0xB7, 0x27,
+ 0x58, 0xB8, 0xD7, 0x67, 0x9C, 0x87, 0xE8, 0x47,
+ 0xE6, 0x30, 0x17, 0xB5, 0x5A, 0x69, 0xC5, 0xC6
+ },
+ {
+ 0x96, 0x7C, 0x81, 0xF5, 0x61, 0x95, 0x18, 0x33,
+ 0xFA, 0x56, 0x6F, 0x6B, 0x36, 0x07, 0x7E, 0xAD,
+ 0xB2, 0xA6, 0x15, 0xCC, 0x15, 0xF0, 0xED, 0xBB,
+ 0xAE, 0x4F, 0x84, 0x4D, 0xDC, 0x8E, 0x9C, 0x1F,
+ 0xB8, 0x3D, 0x31, 0xA9, 0x3F, 0xCB, 0x17, 0x74,
+ 0xD7, 0x40, 0xD6, 0x92, 0x08, 0xCA, 0x59, 0x30,
+ 0xBC, 0xFA, 0xC4, 0xA1, 0xF9, 0x44, 0x46, 0x9F,
+ 0xEF, 0xD1, 0x9B, 0x6E, 0x93, 0x75, 0xE0, 0xB5
+ },
+ {
+ 0xE8, 0xAE, 0xF1, 0x78, 0xE6, 0xDA, 0x3E, 0xF5,
+ 0xCA, 0xED, 0x65, 0x30, 0xF7, 0xEB, 0x25, 0x60,
+ 0x82, 0x56, 0xC2, 0x37, 0x7C, 0x4C, 0xF9, 0x6B,
+ 0x0C, 0xFD, 0x0D, 0x76, 0xEE, 0xB4, 0xBB, 0x86,
+ 0xEE, 0xFF, 0x7B, 0x7D, 0xF1, 0x58, 0x5C, 0x8D,
+ 0x7A, 0x20, 0xC0, 0x63, 0x3A, 0x67, 0x90, 0x7F,
+ 0x6D, 0x28, 0x67, 0xC3, 0x26, 0x4A, 0x91, 0xC0,
+ 0x51, 0xAB, 0xAE, 0x6E, 0xEA, 0x5A, 0x91, 0xD8
+ },
+ {
+ 0x64, 0x81, 0xDC, 0xC8, 0x15, 0x7A, 0xE6, 0x28,
+ 0xB5, 0xCD, 0x52, 0x6B, 0xAC, 0x8F, 0x93, 0x31,
+ 0x56, 0xDE, 0xDA, 0xC9, 0x56, 0xA2, 0xB2, 0x2A,
+ 0x97, 0x4B, 0xF5, 0xF7, 0xEC, 0x2D, 0xB5, 0x80,
+ 0x6F, 0x53, 0xDD, 0x0E, 0x2D, 0xD5, 0x3D, 0xB8,
+ 0x7C, 0xD8, 0xF5, 0x8A, 0x58, 0x6F, 0x9B, 0x3C,
+ 0x5C, 0x52, 0x23, 0x31, 0xA3, 0x11, 0x74, 0xC4,
+ 0xE7, 0xB9, 0xB6, 0xF7, 0xF0, 0x57, 0xC2, 0x8F
+ },
+ {
+ 0xA7, 0x1E, 0xA4, 0x5C, 0xE6, 0x61, 0x6A, 0x3D,
+ 0x2F, 0x0A, 0x59, 0x2D, 0x5D, 0x02, 0x86, 0x93,
+ 0x2D, 0xA6, 0x3C, 0x6D, 0xB1, 0x1D, 0x59, 0xC6,
+ 0x69, 0x1C, 0x35, 0xA5, 0x6F, 0x7E, 0xE4, 0xF8,
+ 0x0B, 0x6F, 0xC3, 0x40, 0xB4, 0xDB, 0xC1, 0x84,
+ 0x4C, 0x50, 0x40, 0xE6, 0x68, 0xD2, 0x89, 0x2F,
+ 0x4A, 0x4A, 0xE8, 0x53, 0x3F, 0x1B, 0x67, 0x71,
+ 0xBC, 0xFC, 0xE7, 0xC3, 0xA2, 0x3E, 0x0D, 0x97
+ },
+ {
+ 0x96, 0x93, 0x44, 0x87, 0x70, 0xFE, 0xAE, 0x42,
+ 0x17, 0x26, 0xEB, 0x20, 0x3B, 0x01, 0xC7, 0x08,
+ 0x23, 0xD5, 0xF4, 0x4C, 0xC5, 0x21, 0x3E, 0x6A,
+ 0x68, 0x28, 0x47, 0x29, 0xBD, 0x11, 0x7D, 0x9B,
+ 0xD1, 0x8F, 0xEC, 0x4A, 0x0A, 0x82, 0x4A, 0x24,
+ 0x08, 0x0F, 0x29, 0x8B, 0xAC, 0xD2, 0x96, 0xD7,
+ 0xB4, 0x97, 0x83, 0x8F, 0xBD, 0x7B, 0x0D, 0x57,
+ 0x5C, 0x52, 0x49, 0x2B, 0x3E, 0x6F, 0x92, 0x6B
+ },
+ {
+ 0x37, 0xA1, 0x50, 0x66, 0xF2, 0xB9, 0xF9, 0x4C,
+ 0x24, 0x61, 0x1B, 0xC4, 0x53, 0xED, 0x02, 0x74,
+ 0x07, 0x8D, 0x1F, 0x70, 0xB2, 0xD3, 0x4C, 0x8B,
+ 0x96, 0x36, 0x08, 0x48, 0x9D, 0xCB, 0xE8, 0xDF,
+ 0x44, 0x8E, 0xDD, 0x9C, 0x73, 0x36, 0x2B, 0xB2,
+ 0xB6, 0x6B, 0xEE, 0xF6, 0x1F, 0xCE, 0x60, 0x10,
+ 0x6F, 0x70, 0x19, 0xED, 0x37, 0x3C, 0x69, 0x22,
+ 0x59, 0xD9, 0x55, 0x6A, 0x94, 0x0B, 0x1A, 0x06
+ },
+ {
+ 0xBD, 0x44, 0xE7, 0x39, 0xE1, 0xF9, 0xDB, 0x1C,
+ 0x6B, 0xAF, 0x42, 0xCA, 0x4A, 0x12, 0xAC, 0x09,
+ 0x9B, 0x96, 0xF6, 0xB3, 0x6C, 0x4B, 0xCB, 0x1B,
+ 0x72, 0xEE, 0xFF, 0x08, 0xA6, 0x49, 0x68, 0x35,
+ 0xEC, 0x65, 0x15, 0x0B, 0xE8, 0xFE, 0x16, 0xCB,
+ 0xE3, 0x27, 0x07, 0xE3, 0x47, 0x54, 0x7D, 0xC5,
+ 0xA5, 0x83, 0xD2, 0x65, 0x74, 0x6F, 0xA5, 0x95,
+ 0xC5, 0xE7, 0x73, 0x0F, 0xCF, 0x24, 0x58, 0x1E
+ },
+ {
+ 0xFA, 0xB2, 0x03, 0x8E, 0x94, 0x98, 0xA1, 0xC3,
+ 0x9E, 0x05, 0x78, 0xA0, 0xA5, 0xEA, 0x6B, 0x44,
+ 0xF3, 0xC1, 0xB4, 0x1A, 0xE5, 0x67, 0xF9, 0x91,
+ 0x4A, 0x95, 0xB1, 0x31, 0xC4, 0x8D, 0x12, 0x1E,
+ 0xCA, 0xCE, 0xA8, 0x95, 0xA0, 0x9B, 0x1D, 0x4E,
+ 0x04, 0x42, 0xBE, 0xC9, 0xC5, 0x0C, 0x50, 0xE0,
+ 0x0A, 0x9F, 0xAF, 0xEF, 0xFA, 0xE0, 0x70, 0x88,
+ 0x4C, 0x26, 0x25, 0xA8, 0xB1, 0xA2, 0x17, 0x26
+ },
+ {
+ 0x05, 0xA1, 0xB7, 0x6B, 0x2F, 0xD5, 0x62, 0x11,
+ 0xE0, 0xF2, 0xD7, 0x5A, 0x25, 0x16, 0x54, 0xA7,
+ 0x72, 0xF5, 0x5E, 0x18, 0xCA, 0x02, 0x2A, 0xF5,
+ 0x2C, 0xB3, 0x30, 0x19, 0x1E, 0x98, 0xA3, 0xB8,
+ 0xEB, 0x87, 0xE5, 0x11, 0x7B, 0xAE, 0x58, 0x04,
+ 0x4D, 0x94, 0x4C, 0x1F, 0x18, 0x85, 0x45, 0x12,
+ 0x25, 0x41, 0x77, 0x35, 0xFC, 0x72, 0xF7, 0x39,
+ 0x36, 0x69, 0x3C, 0xFF, 0x45, 0x46, 0x9F, 0x8C
+ },
+ {
+ 0x2A, 0x30, 0xC9, 0x6B, 0xDA, 0xC7, 0x8A, 0x39,
+ 0x94, 0xEE, 0xCA, 0xA5, 0xA5, 0x3F, 0x82, 0x7F,
+ 0x58, 0xE1, 0x32, 0x31, 0xA0, 0xD1, 0x13, 0x08,
+ 0x6C, 0x06, 0xB1, 0xBD, 0xAB, 0xDA, 0x38, 0xD0,
+ 0x8F, 0x1A, 0xE2, 0x7D, 0xE2, 0x5F, 0xD2, 0x2E,
+ 0xEA, 0x70, 0xC0, 0x5F, 0x01, 0x32, 0xBF, 0x7A,
+ 0x50, 0x1C, 0x82, 0xAE, 0x62, 0x15, 0xBF, 0xEF,
+ 0x3C, 0x01, 0x63, 0x98, 0xBA, 0xF2, 0xCB, 0x62
+ },
+ {
+ 0x48, 0xDB, 0x53, 0x76, 0x5B, 0x82, 0xBD, 0x6F,
+ 0x25, 0x33, 0xEA, 0xE1, 0x7F, 0x67, 0x69, 0xD7,
+ 0xA4, 0xE3, 0xB2, 0x43, 0x74, 0x60, 0x1C, 0xDD,
+ 0x8E, 0xC0, 0xCA, 0x3A, 0xAB, 0x30, 0x93, 0xFD,
+ 0x2B, 0x99, 0x24, 0x38, 0x46, 0x0B, 0xAF, 0x8D,
+ 0xA5, 0x8F, 0xB9, 0xA8, 0x9B, 0x2C, 0x58, 0xF9,
+ 0x68, 0xE6, 0x36, 0x17, 0xCB, 0xEB, 0x18, 0x44,
+ 0xB0, 0x2D, 0x6A, 0x27, 0xC5, 0xB4, 0xAD, 0x41
+ },
+ {
+ 0x5C, 0x8B, 0x2E, 0x0E, 0x1B, 0x5C, 0x8F, 0x45,
+ 0x7D, 0x7F, 0x7B, 0xD9, 0xF0, 0x5A, 0x97, 0xE5,
+ 0x8D, 0xDA, 0x1D, 0x28, 0xDB, 0x9F, 0x34, 0xD1,
+ 0xCE, 0x73, 0x25, 0x28, 0xF9, 0x68, 0xBE, 0xDD,
+ 0x9E, 0x1C, 0xC9, 0x35, 0x2D, 0x0A, 0x5D, 0xF6,
+ 0x67, 0x29, 0x28, 0xBD, 0xD3, 0xEA, 0x6F, 0x5C,
+ 0xB0, 0x60, 0x77, 0xCF, 0x3A, 0xD3, 0xA7, 0x6E,
+ 0x29, 0xB2, 0x2E, 0x82, 0xBA, 0xC6, 0x7B, 0x61
+ },
+ {
+ 0x5B, 0x73, 0x91, 0xAA, 0x52, 0xF2, 0x76, 0xFA,
+ 0xB9, 0xC1, 0x38, 0x77, 0xF1, 0x22, 0x32, 0x70,
+ 0x84, 0x97, 0xFC, 0x02, 0x8F, 0xAA, 0x17, 0x32,
+ 0xA5, 0xDB, 0x07, 0x9E, 0x7F, 0xE0, 0x73, 0xED,
+ 0x0C, 0xC9, 0x52, 0x9C, 0xFC, 0x86, 0x3A, 0x4E,
+ 0xCB, 0xA4, 0xDC, 0x2F, 0x1E, 0xA9, 0xF6, 0xBD,
+ 0x69, 0x04, 0xF3, 0xA0, 0xC1, 0x07, 0x19, 0x3C,
+ 0x5E, 0x71, 0x1C, 0xB9, 0x11, 0xF3, 0x80, 0x25
+ },
+ {
+ 0x1D, 0x5A, 0xF7, 0x0F, 0x09, 0xA5, 0xFC, 0x69,
+ 0x16, 0xEF, 0x59, 0xA3, 0x8A, 0x86, 0x92, 0x6D,
+ 0xCA, 0xAE, 0x39, 0xA8, 0x95, 0x4D, 0x73, 0xFC,
+ 0x80, 0xA3, 0x50, 0x75, 0x1A, 0xDD, 0xA3, 0x8C,
+ 0x9D, 0x59, 0x75, 0x06, 0xDC, 0x05, 0xE1, 0xED,
+ 0x37, 0xBD, 0x2D, 0xB1, 0x59, 0x0F, 0x99, 0xAA,
+ 0x29, 0x6A, 0xEA, 0x13, 0xAB, 0x84, 0x43, 0xD5,
+ 0xA9, 0x23, 0x47, 0xFB, 0x85, 0xFC, 0x81, 0x6D
+ },
+ {
+ 0x80, 0xE3, 0x70, 0x92, 0x97, 0xD4, 0x41, 0x14,
+ 0xB9, 0xFB, 0xDF, 0x55, 0x67, 0xF0, 0x5F, 0x33,
+ 0x00, 0x94, 0xCF, 0x09, 0xF4, 0xC0, 0xEF, 0xCF,
+ 0xAC, 0x05, 0x09, 0x5C, 0x36, 0x08, 0x10, 0x77,
+ 0x30, 0xC1, 0xAA, 0x07, 0xFF, 0x23, 0x00, 0x25,
+ 0x62, 0xC7, 0xE8, 0x41, 0xA9, 0xF5, 0x66, 0x24,
+ 0xFF, 0xE2, 0xAB, 0xEC, 0x61, 0x1E, 0xB9, 0xE7,
+ 0x3E, 0x1C, 0xCB, 0xD8, 0xF6, 0x2B, 0x11, 0x49
+ },
+ {
+ 0xF9, 0x94, 0x5C, 0x19, 0x06, 0x77, 0x84, 0x61,
+ 0x94, 0x13, 0x2B, 0x49, 0x6E, 0xC6, 0x01, 0x2C,
+ 0x08, 0x75, 0x0E, 0x02, 0x5F, 0xD5, 0x52, 0xED,
+ 0x32, 0x4D, 0x3A, 0x49, 0xD8, 0x63, 0x66, 0xC0,
+ 0x3D, 0xCC, 0xDE, 0x8D, 0x5B, 0x5A, 0xC9, 0xA4,
+ 0xBC, 0xB7, 0x19, 0x5E, 0x63, 0xBC, 0xAA, 0x93,
+ 0x9E, 0x8E, 0xDA, 0x18, 0xF1, 0x16, 0x94, 0xB6,
+ 0xFA, 0x69, 0x37, 0x39, 0x3B, 0xFF, 0xDB, 0xF4
+ },
+ {
+ 0x8D, 0x8F, 0x2E, 0xD9, 0xAE, 0x39, 0x80, 0x9A,
+ 0xAC, 0xAD, 0x2F, 0xCE, 0xDB, 0xD2, 0xDC, 0xA7,
+ 0x30, 0xC7, 0x83, 0xE6, 0x2F, 0xF7, 0x0B, 0x8D,
+ 0x3C, 0x53, 0x62, 0xF0, 0x73, 0xF8, 0x34, 0x67,
+ 0x19, 0x7D, 0x37, 0x56, 0xB4, 0x45, 0x19, 0x5F,
+ 0xE7, 0x52, 0x11, 0x73, 0x64, 0xD9, 0x2C, 0xF4,
+ 0x2C, 0x02, 0x6E, 0x40, 0x9D, 0x5F, 0xF7, 0xA9,
+ 0x53, 0x3E, 0xAB, 0x78, 0xF1, 0x75, 0x4A, 0x2D
+ },
+ {
+ 0x3A, 0xC9, 0x9A, 0xC5, 0x3A, 0xC4, 0x9A, 0x56,
+ 0xFA, 0xA1, 0x86, 0x46, 0xB8, 0xE0, 0x8A, 0x2D,
+ 0x35, 0xBE, 0x80, 0xDF, 0x3E, 0xFB, 0xBB, 0xA6,
+ 0xBD, 0xA4, 0xAE, 0x90, 0x2B, 0x8D, 0x3E, 0x17,
+ 0x0A, 0x7B, 0xE8, 0x60, 0x5C, 0x34, 0xA4, 0xDC,
+ 0x9A, 0x73, 0x62, 0xB1, 0xC2, 0x01, 0xD7, 0x02,
+ 0x39, 0x1B, 0xD7, 0xD5, 0x20, 0x7F, 0x95, 0xFA,
+ 0x39, 0x0C, 0xE3, 0x3C, 0x43, 0x14, 0xD4, 0x11
+ },
+ {
+ 0xE4, 0x69, 0x4B, 0xDB, 0x31, 0x01, 0x6F, 0x25,
+ 0x53, 0x2C, 0x04, 0x3C, 0x5C, 0x63, 0x08, 0xCC,
+ 0x61, 0x9B, 0x0F, 0x87, 0x16, 0xF0, 0xC2, 0x9E,
+ 0xEB, 0x9F, 0x34, 0x0F, 0x47, 0xB0, 0x7B, 0x4A,
+ 0x4C, 0xE0, 0x98, 0x4C, 0x47, 0x24, 0xB1, 0x2A,
+ 0xB3, 0xD3, 0x2A, 0xF5, 0x16, 0xAD, 0xA2, 0x64,
+ 0x4C, 0xA6, 0x55, 0x8C, 0x1C, 0xB5, 0x81, 0x5C,
+ 0x12, 0x12, 0xA9, 0xB5, 0xFA, 0x83, 0x44, 0x12
+ },
+ {
+ 0xC6, 0x3C, 0x70, 0x3E, 0x62, 0x10, 0x8A, 0xA0,
+ 0xED, 0xC6, 0x83, 0xF3, 0x67, 0x8A, 0x00, 0x78,
+ 0x8F, 0xB1, 0x00, 0xC0, 0x96, 0x0B, 0x4E, 0x98,
+ 0xB7, 0x6A, 0x48, 0xE4, 0xE5, 0x92, 0x3D, 0x34,
+ 0x13, 0x44, 0x8D, 0xB8, 0x87, 0x5E, 0x3B, 0xCE,
+ 0xA7, 0xB6, 0xB8, 0x5D, 0x9E, 0x3E, 0xEA, 0xB7,
+ 0x2C, 0xD1, 0x50, 0x96, 0xFB, 0xBB, 0x2C, 0xC4,
+ 0x27, 0x03, 0x17, 0xFC, 0x34, 0xD4, 0x04, 0x71
+ },
+ {
+ 0x90, 0x80, 0xB7, 0xE8, 0x41, 0xEF, 0x51, 0x9C,
+ 0x54, 0x17, 0xE6, 0x90, 0xAA, 0xF4, 0x32, 0x79,
+ 0x07, 0xA8, 0x3D, 0xBC, 0xB7, 0x38, 0xD0, 0xF7,
+ 0x30, 0x8B, 0x1D, 0x61, 0x1D, 0xEF, 0x16, 0x9A,
+ 0x4F, 0x47, 0x42, 0x3E, 0x69, 0x0F, 0x27, 0xA7,
+ 0xE2, 0x74, 0x1A, 0xE7, 0x86, 0x5D, 0xA2, 0x3C,
+ 0x5D, 0x3F, 0x13, 0xC3, 0x16, 0x06, 0x3C, 0x7A,
+ 0xA1, 0xA9, 0x58, 0xE5, 0xBE, 0x83, 0x8F, 0x04
+ },
+ {
+ 0x29, 0x8D, 0xF6, 0x46, 0x91, 0x5F, 0x04, 0xD6,
+ 0x65, 0xE9, 0x67, 0x5E, 0x6A, 0x10, 0x31, 0x87,
+ 0x0D, 0x28, 0xEB, 0x7A, 0x04, 0x05, 0x66, 0x3E,
+ 0xAC, 0x3B, 0x10, 0xD1, 0xB4, 0xFA, 0x2E, 0x86,
+ 0x8E, 0x63, 0x73, 0xA5, 0x86, 0xCD, 0x73, 0xE0,
+ 0x6D, 0x8E, 0x7A, 0xD7, 0x71, 0xB4, 0xFB, 0x0A,
+ 0x8B, 0x4F, 0xC2, 0xDC, 0x6C, 0xE0, 0x9C, 0x64,
+ 0x2E, 0xE8, 0x99, 0x26, 0xFD, 0xC6, 0x52, 0x60
+ },
+ {
+ 0x4F, 0x2D, 0xE9, 0xC4, 0xF4, 0x34, 0x8B, 0xDB,
+ 0x32, 0x3A, 0x66, 0x83, 0x72, 0xE7, 0x71, 0x42,
+ 0x99, 0xC7, 0x76, 0xF9, 0x60, 0x2F, 0x3A, 0xF8,
+ 0xFB, 0x77, 0x46, 0xF1, 0x76, 0x86, 0x8D, 0xF3,
+ 0x54, 0x2B, 0x2F, 0xA6, 0x9E, 0xAE, 0x38, 0xB6,
+ 0xA2, 0x6A, 0x06, 0xCA, 0x89, 0x42, 0xF8, 0x82,
+ 0x78, 0xC6, 0x4E, 0x3D, 0x01, 0x7F, 0xEE, 0x67,
+ 0xA9, 0x4E, 0xA0, 0x23, 0xB2, 0xB5, 0xBE, 0x5F
+ },
+ {
+ 0x40, 0x18, 0xC5, 0xEE, 0x90, 0x93, 0xA6, 0x81,
+ 0x11, 0x2F, 0x4C, 0xE1, 0x93, 0xA1, 0xD6, 0x5E,
+ 0x05, 0x48, 0x72, 0x5F, 0x96, 0xAE, 0x31, 0x53,
+ 0x87, 0xCD, 0x76, 0x5C, 0x2B, 0x9C, 0x30, 0x68,
+ 0xAE, 0x4C, 0xBE, 0x5C, 0xD5, 0x40, 0x2C, 0x11,
+ 0xC5, 0x5A, 0x9D, 0x78, 0x5F, 0xFD, 0xFC, 0x2B,
+ 0xDE, 0x6E, 0x7A, 0xCF, 0x19, 0x61, 0x74, 0x75,
+ 0xDA, 0xE0, 0xEB, 0x01, 0x44, 0x56, 0xCE, 0x45
+ },
+ {
+ 0x6F, 0xCE, 0x66, 0x75, 0xE8, 0x6D, 0x7E, 0x85,
+ 0x70, 0x4C, 0x96, 0xC2, 0x95, 0x70, 0x3C, 0xD9,
+ 0x54, 0x98, 0x59, 0x0E, 0x50, 0x76, 0x4D, 0x23,
+ 0xD7, 0xA7, 0xA3, 0xA3, 0x22, 0x68, 0xA0, 0xB3,
+ 0xC9, 0x91, 0xE8, 0xF7, 0x84, 0x87, 0x69, 0x9A,
+ 0x55, 0x4B, 0x58, 0x1E, 0x33, 0x9C, 0x09, 0xAE,
+ 0xC9, 0x82, 0xE0, 0xBA, 0xA4, 0x31, 0x87, 0x93,
+ 0x62, 0x06, 0x35, 0xE1, 0xE2, 0xC8, 0xD9, 0xF2
+ },
+ {
+ 0xEB, 0xA9, 0x37, 0x85, 0x91, 0x97, 0xC7, 0xFD,
+ 0x41, 0x2D, 0xBC, 0x9A, 0xFC, 0x0D, 0x67, 0xCC,
+ 0x19, 0x81, 0x60, 0xB5, 0xA9, 0xCC, 0xEE, 0x87,
+ 0xC4, 0x1A, 0x86, 0x64, 0x85, 0x9F, 0x3E, 0xFD,
+ 0x96, 0x13, 0x66, 0xA8, 0x09, 0xC7, 0xC6, 0xBC,
+ 0x6F, 0xA8, 0x44, 0x92, 0x68, 0x14, 0xE0, 0xB4,
+ 0xEF, 0xA3, 0x7E, 0xDE, 0x2C, 0x88, 0x44, 0x26,
+ 0x8D, 0x7F, 0x35, 0x56, 0xE4, 0x46, 0x58, 0x1D
+ },
+ {
+ 0x83, 0xF4, 0x33, 0xE4, 0xF1, 0xC5, 0x07, 0x97,
+ 0x49, 0x3C, 0x58, 0xC2, 0x64, 0xCF, 0xFA, 0x70,
+ 0xC4, 0xA7, 0xA2, 0x4C, 0x33, 0x4D, 0xBA, 0xA3,
+ 0xC5, 0x74, 0x89, 0xD9, 0x70, 0xD4, 0x9D, 0x69,
+ 0x49, 0xFE, 0x45, 0xB7, 0x04, 0xF2, 0x65, 0xEF,
+ 0xD2, 0xAE, 0xE1, 0xAC, 0x1B, 0x46, 0xF4, 0xAA,
+ 0x3E, 0x4F, 0xAD, 0x68, 0xB3, 0x79, 0x61, 0xD2,
+ 0xC7, 0x28, 0x0A, 0xE1, 0x96, 0x72, 0xC8, 0x50
+ },
+ {
+ 0xB5, 0x57, 0xEC, 0xE1, 0x22, 0x72, 0x49, 0x3D,
+ 0xC2, 0x7E, 0x88, 0xA0, 0x5A, 0xDC, 0xD8, 0x61,
+ 0x87, 0x5A, 0x0C, 0xD0, 0x0B, 0xD6, 0x8A, 0xDC,
+ 0x3A, 0x30, 0x1D, 0x26, 0x3A, 0x9C, 0xD9, 0x93,
+ 0xA9, 0x6A, 0xE1, 0x4C, 0xFC, 0xDD, 0xCB, 0x99,
+ 0x7C, 0xC9, 0x86, 0x23, 0x93, 0x50, 0x50, 0xEA,
+ 0x43, 0x55, 0x2A, 0x34, 0x11, 0x07, 0x18, 0x7D,
+ 0xE7, 0x5C, 0x4E, 0xDE, 0xD7, 0xC7, 0x86, 0xBD
+ },
+ {
+ 0x95, 0x89, 0xC0, 0x81, 0x3B, 0x73, 0x93, 0xDB,
+ 0xAA, 0xAF, 0xE4, 0x7A, 0xF5, 0xB4, 0x08, 0xB2,
+ 0x3C, 0x8A, 0x8C, 0x8B, 0xAC, 0x62, 0x55, 0x4B,
+ 0x8F, 0xA1, 0x32, 0xA3, 0x58, 0xCE, 0x30, 0x83,
+ 0xB1, 0xD4, 0xE3, 0x97, 0x07, 0xCD, 0x54, 0xA5,
+ 0x5F, 0x67, 0x3D, 0x48, 0x11, 0x6E, 0xB1, 0xF9,
+ 0xED, 0x8D, 0xE9, 0xC9, 0x43, 0xCD, 0x2D, 0xE4,
+ 0x60, 0xA6, 0x8B, 0xDD, 0xF7, 0x1E, 0x98, 0x03
+ },
+ {
+ 0xAE, 0x4C, 0xCF, 0x27, 0xAB, 0x00, 0xA4, 0x0C,
+ 0x36, 0x37, 0xD3, 0xD2, 0xCE, 0x51, 0xA8, 0x3E,
+ 0xFB, 0xA6, 0x2D, 0x4A, 0x6F, 0xDA, 0xD6, 0x95,
+ 0x06, 0x3F, 0xBC, 0x60, 0xA2, 0xD8, 0x2E, 0xC5,
+ 0xA5, 0x4A, 0xCB, 0xE0, 0x9B, 0xA9, 0x38, 0x8F,
+ 0x49, 0xAA, 0xC2, 0x7C, 0x99, 0x2D, 0x84, 0x63,
+ 0x20, 0x36, 0xE1, 0xBD, 0xD4, 0xC5, 0x29, 0xBB,
+ 0xF1, 0x85, 0x1E, 0xAE, 0x0C, 0x6E, 0xA9, 0x02
+ },
+ {
+ 0xA3, 0x94, 0x4B, 0x2C, 0x31, 0xCB, 0x49, 0x40,
+ 0x80, 0xB7, 0xEE, 0x1D, 0xB0, 0x81, 0x68, 0x53,
+ 0xE4, 0x25, 0xB5, 0x4C, 0x48, 0xD6, 0x31, 0x44,
+ 0x7E, 0xA5, 0x2C, 0x1D, 0x29, 0x52, 0x07, 0x9B,
+ 0xD8, 0x8F, 0xAB, 0x9E, 0xD0, 0xB7, 0xD8, 0xC0,
+ 0xBA, 0xAF, 0x0C, 0x4E, 0xCA, 0x19, 0x10, 0xDB,
+ 0x6F, 0x98, 0x53, 0x4F, 0x0D, 0x42, 0xE5, 0xEB,
+ 0xB6, 0xC0, 0xA7, 0x5E, 0xF0, 0xD8, 0xB2, 0xC0
+ },
+ {
+ 0xCF, 0xA1, 0xA2, 0x24, 0x68, 0x5A, 0x5F, 0xB2,
+ 0x01, 0x04, 0x58, 0x20, 0x1C, 0xEB, 0x0C, 0xDA,
+ 0x21, 0xC8, 0x2B, 0x16, 0x02, 0xDC, 0x41, 0x35,
+ 0x85, 0xFB, 0xCE, 0x80, 0x97, 0x6F, 0x06, 0x1C,
+ 0x23, 0x5B, 0x13, 0x67, 0x71, 0x24, 0x98, 0x14,
+ 0x4A, 0xC1, 0x6A, 0x98, 0x54, 0xF6, 0xFB, 0x32,
+ 0x3C, 0xBE, 0xB6, 0x23, 0x69, 0xCF, 0x9B, 0x75,
+ 0x2B, 0x92, 0x52, 0xA2, 0xA7, 0xAC, 0xE1, 0xFD
+ },
+ {
+ 0xFA, 0x62, 0xC6, 0xCF, 0xC8, 0xF0, 0x79, 0xE5,
+ 0x8F, 0x3D, 0x3F, 0xEF, 0xD7, 0xC2, 0x24, 0xE7,
+ 0x1E, 0xBC, 0x69, 0xA9, 0x5B, 0x18, 0x35, 0xCC,
+ 0xC3, 0x2F, 0x35, 0x07, 0x77, 0x05, 0x11, 0x02,
+ 0x61, 0x54, 0x92, 0xD6, 0x7F, 0xB6, 0xDE, 0x62,
+ 0xCF, 0x2A, 0xD5, 0xB1, 0x84, 0x67, 0xFE, 0x87,
+ 0x15, 0x74, 0x88, 0x82, 0xDB, 0x89, 0xFF, 0x86,
+ 0xEF, 0xDF, 0x2F, 0x96, 0xF8, 0x13, 0x5E, 0xD2
+ },
+ {
+ 0xCC, 0x63, 0x3F, 0xD4, 0xEA, 0x6A, 0xC4, 0x08,
+ 0xC3, 0x87, 0x57, 0x56, 0xB9, 0x01, 0x28, 0x8A,
+ 0x1D, 0xE1, 0x91, 0x89, 0x28, 0x32, 0xBE, 0x2E,
+ 0x90, 0x26, 0xDC, 0x65, 0xC2, 0xFF, 0x00, 0x00,
+ 0x9F, 0x14, 0x36, 0xDD, 0xFF, 0x42, 0x06, 0x26,
+ 0x0A, 0x3D, 0x66, 0xEF, 0x61, 0x92, 0x14, 0x3E,
+ 0x57, 0x2F, 0x1E, 0x4B, 0xB8, 0xE5, 0xA7, 0x4B,
+ 0x12, 0x05, 0x5E, 0x42, 0x41, 0x1C, 0x18, 0xBC
+ },
+ {
+ 0x44, 0xD2, 0xBF, 0x7F, 0x36, 0x96, 0xB8, 0x93,
+ 0x3F, 0x25, 0x5B, 0x9B, 0xE1, 0xA4, 0xA6, 0xAE,
+ 0x33, 0x16, 0xC2, 0x5D, 0x03, 0x95, 0xF5, 0x90,
+ 0xB9, 0xB9, 0x89, 0x8F, 0x12, 0x7E, 0x40, 0xD3,
+ 0xF4, 0x12, 0x4D, 0x7B, 0xDB, 0xC8, 0x72, 0x5F,
+ 0x00, 0xB0, 0xD2, 0x81, 0x50, 0xFF, 0x05, 0xB4,
+ 0xA7, 0x9E, 0x5E, 0x04, 0xE3, 0x4A, 0x47, 0xE9,
+ 0x08, 0x7B, 0x3F, 0x79, 0xD4, 0x13, 0xAB, 0x7F
+ },
+ {
+ 0x96, 0xFB, 0xCB, 0xB6, 0x0B, 0xD3, 0x13, 0xB8,
+ 0x84, 0x50, 0x33, 0xE5, 0xBC, 0x05, 0x8A, 0x38,
+ 0x02, 0x74, 0x38, 0x57, 0x2D, 0x7E, 0x79, 0x57,
+ 0xF3, 0x68, 0x4F, 0x62, 0x68, 0xAA, 0xDD, 0x3A,
+ 0xD0, 0x8D, 0x21, 0x76, 0x7E, 0xD6, 0x87, 0x86,
+ 0x85, 0x33, 0x1B, 0xA9, 0x85, 0x71, 0x48, 0x7E,
+ 0x12, 0x47, 0x0A, 0xAD, 0x66, 0x93, 0x26, 0x71,
+ 0x6E, 0x46, 0x66, 0x7F, 0x69, 0xF8, 0xD7, 0xE8
+ },
+};
+
+
+
+
+#endif
+
+
+
diff --git a/tests/sys/opencrypto/blake2_test.c b/tests/sys/opencrypto/blake2_test.c
new file mode 100644
index 000000000000..b397f8a8ec4c
--- /dev/null
+++ b/tests/sys/opencrypto/blake2_test.c
@@ -0,0 +1,217 @@
+/*-
+ * Copyright (c) 2018 Conrad Meyer <cem@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Derived from blake2b-test.c and blake2s-test.c:
+ *
+ * BLAKE2 reference source code package - optimized C implementations
+ *
+ * Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along with
+ * this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include <sys/param.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+/* Be sure to include tree copy rather than system copy. */
+#include "cryptodev.h"
+
+#include "freebsd_test_suite/macros.h"
+
+#include <blake2.h>
+#include "blake2-kat.h"
+
+static uint8_t key2b[BLAKE2B_KEYBYTES];
+static uint8_t key2s[BLAKE2S_KEYBYTES];
+static uint8_t katbuf[KAT_LENGTH];
+
+static void
+initialize_constant_buffers(void)
+{
+ size_t i;
+
+ for (i = 0; i < sizeof(key2b); i++)
+ key2b[i] = (uint8_t)i;
+ for (i = 0; i < sizeof(key2s); i++)
+ key2s[i] = (uint8_t)i;
+ for (i = 0; i < sizeof(katbuf); i++)
+ katbuf[i] = (uint8_t)i;
+}
+
+static int
+lookup_crid(int fd, const char *devname)
+{
+ struct crypt_find_op find;
+
+ find.crid = -1;
+ strlcpy(find.name, devname, sizeof(find.name));
+ ATF_REQUIRE(ioctl(fd, CIOCFINDDEV, &find) != -1);
+ return (find.crid);
+}
+
+static int
+get_handle_fd(void)
+{
+ int fd;
+
+ fd = open("/dev/crypto", O_RDWR);
+ ATF_REQUIRE(fd >= 0);
+ return (fd);
+}
+
+static int
+create_session(int fd, int alg, int crid, const void *key, size_t klen)
+{
+ struct session2_op sop;
+
+ memset(&sop, 0, sizeof(sop));
+
+ sop.mac = alg;
+ sop.mackey = key;
+ sop.mackeylen = klen;
+ sop.crid = crid;
+
+ ATF_REQUIRE_MSG(ioctl(fd, CIOCGSESSION2, &sop) >= 0,
+ "alg %d keylen %zu, errno=%d (%s)", alg, klen, errno,
+ strerror(errno));
+ return (sop.ses);
+}
+
+static void
+do_cryptop(int fd, int ses, size_t inlen, void *out)
+{
+ struct crypt_op cop;
+
+ memset(&cop, 0, sizeof(cop));
+
+ cop.ses = ses;
+ cop.len = inlen;
+ cop.src = katbuf;
+ cop.mac = out;
+ ATF_CHECK_MSG(ioctl(fd, CIOCCRYPT, &cop) >= 0, "ioctl(CIOCCRYPT)");
+}
+
+static void
+test_blake2b_vectors(const char *devname, const char *modname)
+{
+ uint8_t hash[BLAKE2B_OUTBYTES];
+ int crid, fd, ses;
+ size_t i;
+
+ ATF_REQUIRE_KERNEL_MODULE(modname);
+ ATF_REQUIRE_KERNEL_MODULE("cryptodev");
+
+ initialize_constant_buffers();
+ fd = get_handle_fd();
+ crid = lookup_crid(fd, devname);
+ ses = create_session(fd, CRYPTO_BLAKE2B, crid, key2b, sizeof(key2b));
+
+ for (i = 0; i < sizeof(katbuf); i++) {
+ do_cryptop(fd, ses, i, hash);
+ ATF_CHECK_EQ_MSG(
+ memcmp(hash, blake2b_keyed_kat[i], sizeof(hash)),
+ 0,
+ "different at %zu", i);
+ }
+}
+
+static void
+test_blake2s_vectors(const char *devname, const char *modname)
+{
+ uint8_t hash[BLAKE2S_OUTBYTES];
+ int crid, fd, ses;
+ size_t i;
+
+ ATF_REQUIRE_KERNEL_MODULE(modname);
+ ATF_REQUIRE_KERNEL_MODULE("cryptodev");
+
+ initialize_constant_buffers();
+ fd = get_handle_fd();
+ crid = lookup_crid(fd, devname);
+ ses = create_session(fd, CRYPTO_BLAKE2S, crid, key2s, sizeof(key2s));
+
+ for (i = 0; i < sizeof(katbuf); i++) {
+ do_cryptop(fd, ses, i, hash);
+ ATF_CHECK_EQ_MSG(
+ memcmp(hash, blake2s_keyed_kat[i], sizeof(hash)),
+ 0,
+ "different at %zu", i);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(blake2b_vectors);
+ATF_TC_BODY(blake2b_vectors, tc)
+{
+ ATF_REQUIRE_SYSCTL_INT("kern.crypto.allow_soft", 1);
+ test_blake2b_vectors("cryptosoft0", "nexus/cryptosoft");
+}
+
+ATF_TC_WITHOUT_HEAD(blake2s_vectors);
+ATF_TC_BODY(blake2s_vectors, tc)
+{
+ ATF_REQUIRE_SYSCTL_INT("kern.crypto.allow_soft", 1);
+ test_blake2s_vectors("cryptosoft0", "nexus/cryptosoft");
+}
+
+#if defined(__i386__) || defined(__amd64__)
+ATF_TC_WITHOUT_HEAD(blake2b_vectors_x86);
+ATF_TC_BODY(blake2b_vectors_x86, tc)
+{
+ ATF_REQUIRE_SYSCTL_INT("kern.crypto.allow_soft", 1);
+ test_blake2b_vectors("blaketwo0", "nexus/blake2");
+}
+
+ATF_TC_WITHOUT_HEAD(blake2s_vectors_x86);
+ATF_TC_BODY(blake2s_vectors_x86, tc)
+{
+ ATF_REQUIRE_SYSCTL_INT("kern.crypto.allow_soft", 1);
+ test_blake2s_vectors("blaketwo0", "nexus/blake2");
+}
+#endif
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, blake2b_vectors);
+ ATF_TP_ADD_TC(tp, blake2s_vectors);
+#if defined(__i386__) || defined(__amd64__)
+ ATF_TP_ADD_TC(tp, blake2b_vectors_x86);
+ ATF_TP_ADD_TC(tp, blake2s_vectors_x86);
+#endif
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/opencrypto/cryptodev.py b/tests/sys/opencrypto/cryptodev.py
new file mode 100644
index 000000000000..0282c73c8e72
--- /dev/null
+++ b/tests/sys/opencrypto/cryptodev.py
@@ -0,0 +1,736 @@
+#!/usr/local/bin/python3
+#
+# Copyright (c) 2014 The FreeBSD Foundation
+# Copyright 2014 John-Mark Gurney
+# All rights reserved.
+# Copyright 2019 Enji Cooper
+#
+# This software was developed by John-Mark Gurney under
+# the sponsorship from the FreeBSD Foundation.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+
+import array
+import binascii
+from fcntl import ioctl
+import os
+import platform
+import random
+import re
+import signal
+from struct import pack as _pack
+import sys
+import time
+
+import dpkt
+
+from cryptodevh import *
+
+__all__ = [ 'Crypto', 'MismatchError', ]
+
+class FindOp(dpkt.Packet):
+ __byte_order__ = '@'
+ __hdr__ = (
+ ('crid', 'i', 0),
+ ('name', '32s', 0),
+ )
+
+class SessionOp(dpkt.Packet):
+ __byte_order__ = '@'
+ __hdr__ = (
+ ('cipher', 'I', 0),
+ ('mac', 'I', 0),
+ ('keylen', 'I', 0),
+ ('key', 'P', 0),
+ ('mackeylen', 'i', 0),
+ ('mackey', 'P', 0),
+ ('ses', 'I', 0),
+ )
+
+class SessionOp2(dpkt.Packet):
+ __byte_order__ = '@'
+ __hdr__ = (
+ ('cipher', 'I', 0),
+ ('mac', 'I', 0),
+ ('keylen', 'I', 0),
+ ('key', 'P', 0),
+ ('mackeylen', 'i', 0),
+ ('mackey', 'P', 0),
+ ('ses', 'I', 0),
+ ('crid', 'i', 0),
+ ('ivlen', 'i', 0),
+ ('maclen', 'i', 0),
+ ('pad0', 'i', 0),
+ ('pad1', 'i', 0),
+ )
+
+class CryptOp(dpkt.Packet):
+ __byte_order__ = '@'
+ __hdr__ = (
+ ('ses', 'I', 0),
+ ('op', 'H', 0),
+ ('flags', 'H', 0),
+ ('len', 'I', 0),
+ ('src', 'P', 0),
+ ('dst', 'P', 0),
+ ('mac', 'P', 0),
+ ('iv', 'P', 0),
+ )
+
+class CryptAEAD(dpkt.Packet):
+ __byte_order__ = '@'
+ __hdr__ = (
+ ('ses', 'I', 0),
+ ('op', 'H', 0),
+ ('flags', 'H', 0),
+ ('len', 'I', 0),
+ ('aadlen', 'I', 0),
+ ('ivlen', 'I', 0),
+ ('src', 'P', 0),
+ ('dst', 'P', 0),
+ ('aad', 'P', 0),
+ ('tag', 'P', 0),
+ ('iv', 'P', 0),
+ )
+
+# h2py.py can't handle multiarg macros
+CIOCGSESSION = 3224396645
+CIOCFSESSION = 2147771238
+CIOCKEY = 3230688104
+CIOCASYMFEAT = 1074029417
+CIOCKEY2 = 3230688107
+CIOCFINDDEV = 3223610220
+if platform.architecture()[0] == '64bit':
+ CIOCGSESSION2 = 3225445226
+ CIOCCRYPT = 3224396647
+ CIOCCRYPTAEAD = 3225445229
+else:
+ CIOCGSESSION2 = 3224396650
+ CIOCCRYPT = 3223085927
+ CIOCCRYPTAEAD = 3223872365
+
+_cryptodev = os.open('/dev/crypto', os.O_RDWR)
+
+def str_to_ascii(val):
+ if sys.version_info[0] >= 3:
+ if isinstance(val, str):
+ return val.encode("ascii")
+ return val
+
+def _findop(crid, name):
+ fop = FindOp()
+ fop.crid = crid
+ fop.name = str_to_ascii(name)
+ s = array.array('B', fop.pack_hdr())
+ ioctl(_cryptodev, CIOCFINDDEV, s, 1)
+ fop.unpack(s)
+
+ try:
+ idx = fop.name.index(b'\x00')
+ name = fop.name[:idx]
+ except ValueError:
+ name = fop.name
+
+ return fop.crid, name
+
+def array_tobytes(array_obj):
+ if sys.version_info[:2] >= (3, 2):
+ return array_obj.tobytes()
+ return array_obj.tostring()
+
+def empty_bytes():
+ if sys.version_info[0] >= 3:
+ return b''
+ return ""
+
+class Crypto:
+ @staticmethod
+ def findcrid(name):
+ return _findop(-1, name)[0]
+
+ @staticmethod
+ def getcridname(crid):
+ return _findop(crid, '')[1]
+
+ def __init__(self, cipher=0, key=None, mac=0, mackey=None,
+ crid=CRYPTOCAP_F_SOFTWARE | CRYPTOCAP_F_HARDWARE, maclen=None,
+ ivlen=None):
+ self._ses = None
+ self._maclen = maclen
+ ses = SessionOp2()
+ ses.cipher = cipher
+ ses.mac = mac
+
+ if key is not None:
+ ses.keylen = len(key)
+ k = array.array('B', key)
+ ses.key = k.buffer_info()[0]
+ else:
+ self.key = None
+
+ if mackey is not None:
+ ses.mackeylen = len(mackey)
+ mk = array.array('B', mackey)
+ ses.mackey = mk.buffer_info()[0]
+
+ if not cipher and not mac:
+ raise ValueError('one of cipher or mac MUST be specified.')
+ ses.crid = crid
+ if ivlen:
+ ses.ivlen = ivlen
+ if maclen:
+ ses.maclen = maclen
+ #print(ses)
+ s = array.array('B', ses.pack_hdr())
+ #print(s)
+ ioctl(_cryptodev, CIOCGSESSION2, s, 1)
+ ses.unpack(s)
+
+ self._ses = ses.ses
+
+ def __del__(self):
+ if self._ses is None:
+ return
+
+ try:
+ ioctl(_cryptodev, CIOCFSESSION, _pack('I', self._ses))
+ except TypeError:
+ pass
+ self._ses = None
+
+ def _doop(self, op, src, iv, mac=None):
+ cop = CryptOp()
+ cop.ses = self._ses
+ cop.op = op
+ cop.flags = 0
+ if src is not None:
+ cop.len = len(src)
+ s = array.array('B', src)
+ cop.src = cop.dst = s.buffer_info()[0]
+ if mac is not None:
+ assert len(mac) == self._maclen, \
+ '%d != %d' % (len(tag), self._maclen)
+ if self._maclen is not None:
+ if mac is None:
+ m = array.array('B', [0] * self._maclen)
+ else:
+ m = array.array('B', mac)
+ cop.mac = m.buffer_info()[0]
+ ivbuf = array.array('B', str_to_ascii(iv))
+ cop.iv = ivbuf.buffer_info()[0]
+
+ #print('cop:', cop)
+ ioctl(_cryptodev, CIOCCRYPT, bytes(cop))
+
+ if src is not None:
+ s = array_tobytes(s)
+ else:
+ s = empty_bytes()
+ if self._maclen is not None:
+ return s, array_tobytes(m)
+
+ return s
+
+ def _doaead(self, op, src, aad, iv, tag=None):
+ caead = CryptAEAD()
+ caead.ses = self._ses
+ caead.op = op
+ caead.flags = CRD_F_IV_EXPLICIT
+ caead.flags = 0
+ if src:
+ src = str_to_ascii(src)
+ caead.len = len(src)
+ s = array.array('B', src)
+ caead.src = caead.dst = s.buffer_info()[0]
+ aad = str_to_ascii(aad)
+ caead.aadlen = len(aad)
+ saad = array.array('B', aad)
+ caead.aad = saad.buffer_info()[0]
+
+ if self._maclen is None:
+ raise ValueError('must have a tag length')
+
+ tag = str_to_ascii(tag)
+ if tag is None:
+ tag = array.array('B', [0] * self._maclen)
+ else:
+ assert len(tag) == self._maclen, \
+ '%d != %d' % (len(tag), self._maclen)
+ tag = array.array('B', tag)
+
+ caead.tag = tag.buffer_info()[0]
+
+ ivbuf = array.array('B', iv)
+ caead.ivlen = len(iv)
+ caead.iv = ivbuf.buffer_info()[0]
+
+ ioctl(_cryptodev, CIOCCRYPTAEAD, bytes(caead))
+
+ if src:
+ s = array_tobytes(s)
+ else:
+ s = empty_bytes()
+
+ return s, array_tobytes(tag)
+
+ def perftest(self, op, size, timeo=3):
+ inp = array.array('B', (random.randint(0, 255) for x in range(size)))
+ inp = str_to_ascii(inp)
+ out = array.array('B', inp)
+
+ # prep ioctl
+ cop = CryptOp()
+ cop.ses = self._ses
+ cop.op = op
+ cop.flags = 0
+ cop.len = len(inp)
+ s = array.array('B', inp)
+ cop.src = s.buffer_info()[0]
+ cop.dst = out.buffer_info()[0]
+ if self._maclen is not None:
+ m = array.array('B', [0] * self._maclen)
+ cop.mac = m.buffer_info()[0]
+ ivbuf = array.array('B', (random.randint(0, 255) for x in range(16)))
+ cop.iv = ivbuf.buffer_info()[0]
+
+ exit = [ False ]
+ def alarmhandle(a, b, exit=exit):
+ exit[0] = True
+
+ oldalarm = signal.signal(signal.SIGALRM, alarmhandle)
+ signal.alarm(timeo)
+
+ start = time.time()
+ reps = 0
+ cop = bytes(cop)
+ while not exit[0]:
+ ioctl(_cryptodev, CIOCCRYPT, cop)
+ reps += 1
+
+ end = time.time()
+
+ signal.signal(signal.SIGALRM, oldalarm)
+
+ print('time:', end - start)
+ print('perf MB/sec:', (reps * size) / (end - start) / 1024 / 1024)
+
+ def encrypt(self, data, iv, aad=None):
+ if aad is None:
+ return self._doop(COP_ENCRYPT, data, iv)
+ else:
+ return self._doaead(COP_ENCRYPT, data, aad,
+ iv)
+
+ def decrypt(self, data, iv, aad=None, tag=None):
+ if aad is None:
+ return self._doop(COP_DECRYPT, data, iv, mac=tag)
+ else:
+ return self._doaead(COP_DECRYPT, data, aad,
+ iv, tag=tag)
+
+class MismatchError(Exception):
+ pass
+
+class KATParser:
+ def __init__(self, fname, fields):
+ self.fields = set(fields)
+ self._pending = None
+ self.fname = fname
+ self.fp = None
+ self.field_re = re.compile(r"\[(?P<field>[^]]+)\]")
+
+ def __enter__(self):
+ self.fp = open(self.fname)
+ return self
+
+ def __exit__(self, exc_type, exc_value, exc_tb):
+ if self.fp is not None:
+ self.fp.close()
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ while True:
+ while True:
+ if self._pending is not None:
+ i = self._pending
+ self._pending = None
+ else:
+ i = self.fp.readline()
+ if not i:
+ return
+
+ if not i.startswith('#') and i.strip():
+ break
+
+ matches = self.field_re.match(i)
+ if matches is None:
+ raise ValueError("Unknown line: %r" % (i))
+ yield matches.group("field"), self.fielditer()
+
+ def eatblanks(self):
+ while True:
+ line = self.fp.readline()
+ if line == '':
+ break
+
+ line = line.strip()
+ if line:
+ break
+
+ return line
+
+ def fielditer(self):
+ while True:
+ values = {}
+
+ line = self.eatblanks()
+ if not line or line[0] == '[':
+ self._pending = line
+ return
+
+ while True:
+ try:
+ f, v = line.split(' =')
+ except:
+ if line == 'FAIL':
+ f, v = 'FAIL', ''
+ else:
+ print('line:', repr(line))
+ raise
+ v = v.strip()
+
+ if f in values:
+ raise ValueError('already present: %r' % repr(f))
+ values[f] = v
+ line = self.fp.readline().strip()
+ if not line:
+ break
+
+ # we should have everything
+ remain = self.fields.copy() - set(values.keys())
+ # XXX - special case GCM decrypt
+ if remain and not ('FAIL' in values and 'PT' in remain):
+ raise ValueError('not all fields found: %r' % repr(remain))
+
+ yield values
+
+# The CCM files use a bit of a different syntax that doesn't quite fit
+# the generic KATParser. In particular, some keys are set globally at
+# the start of the file, and some are set globally at the start of a
+# section.
+class KATCCMParser:
+ def __init__(self, fname):
+ self._pending = None
+ self.fname = fname
+ self.fp = None
+
+ def __enter__(self):
+ self.fp = open(self.fname)
+ self.read_globals()
+ return self
+
+ def __exit__(self, exc_type, exc_value, exc_tb):
+ if self.fp is not None:
+ self.fp.close()
+
+ def read_globals(self):
+ self.global_values = {}
+ while True:
+ line = self.fp.readline()
+ if not line:
+ return
+ if line[0] == '#' or not line.strip():
+ continue
+ if line[0] == '[':
+ self._pending = line
+ return
+
+ try:
+ f, v = line.split(' =')
+ except:
+ print('line:', repr(line))
+ raise
+
+ v = v.strip()
+
+ if f in self.global_values:
+ raise ValueError('already present: %r' % repr(f))
+ self.global_values[f] = v
+
+ def read_section_values(self, kwpairs):
+ self.section_values = self.global_values.copy()
+ for pair in kwpairs.split(', '):
+ f, v = pair.split(' = ')
+ if f in self.section_values:
+ raise ValueError('already present: %r' % repr(f))
+ self.section_values[f] = v
+
+ while True:
+ line = self.fp.readline()
+ if not line:
+ return
+ if line[0] == '#' or not line.strip():
+ continue
+ if line[0] == '[':
+ self._pending = line
+ return
+
+ try:
+ f, v = line.split(' =')
+ except:
+ print('line:', repr(line))
+ raise
+
+ if f == 'Count':
+ self._pending = line
+ return
+
+ v = v.strip()
+
+ if f in self.section_values:
+ raise ValueError('already present: %r' % repr(f))
+ self.section_values[f] = v
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ while True:
+ if self._pending:
+ line = self._pending
+ self._pending = None
+ else:
+ line = self.fp.readline()
+ if not line:
+ return
+
+ if (line and line[0] == '#') or not line.strip():
+ continue
+
+ if line[0] == '[':
+ section = line[1:].split(']', 1)[0]
+ self.read_section_values(section)
+ continue
+
+ values = self.section_values.copy()
+
+ while True:
+ try:
+ f, v = line.split(' =')
+ except:
+ print('line:', repr(line))
+ raise
+ v = v.strip()
+
+ if f in values:
+ raise ValueError('already present: %r' % repr(f))
+ values[f] = v
+ line = self.fp.readline().strip()
+ if not line:
+ break
+
+ yield values
+
+def _spdechex(s):
+ return binascii.hexlify(''.join(s.split()))
+
+if sys.version_info[0] < 3:
+ KATCCMParser.next = KATCCMParser.__next__
+ KATParser.next = KATParser.__next__
+
+if __name__ == '__main__':
+ if True:
+ try:
+ crid = Crypto.findcrid('aesni0')
+ print('aesni:', crid)
+ except IOError:
+ print('aesni0 not found')
+
+ for i in range(10):
+ try:
+ name = Crypto.getcridname(i)
+ print('%2d: %r' % (i, repr(name)))
+ except IOError:
+ pass
+ elif False:
+ columns = [ 'COUNT', 'DataUnitLen', 'Key', 'DataUnitSeqNumber', 'PT', 'CT' ]
+ fname = '/usr/home/jmg/aesni.testing/format tweak value input - data unit seq no/XTSGenAES128.rsp'
+ with KATParser(fname, columns) as kp:
+ for mode, ni in kp:
+ print(i, ni)
+ for j in ni:
+ print(j)
+ elif False:
+ key = _spdechex('c939cc13397c1d37de6ae0e1cb7c423c')
+ iv = _spdechex('00000000000000000000000000000001')
+ pt = _spdechex('ab3cabed693a32946055524052afe3c9cb49664f09fc8b7da824d924006b7496353b8c1657c5dec564d8f38d7432e1de35aae9d95590e66278d4acce883e51abaf94977fcd3679660109a92bf7b2973ccd547f065ec6cee4cb4a72a5e9f45e615d920d76cb34cba482467b3e21422a7242e7d931330c0fbf465c3a3a46fae943029fd899626dda542750a1eee253df323c6ef1573f1c8c156613e2ea0a6cdbf2ae9701020be2d6a83ecb7f3f9d8e')
+ #pt = _spdechex('00000000000000000000000000000000')
+ ct = _spdechex('f42c33853ecc5ce2949865fdb83de3bff1089e9360c94f830baebfaff72836ab5236f77212f1e7396c8c54ac73d81986375a6e9e299cfeca5ba051ed25e8d1affa5beaf6c1d2b45e90802408f2ced21663497e906de5f29341e5e52ddfea5363d628b3eb7806835e17bae051b3a6da3f8e2941fe44384eac17a9d298d2c331ca8320c775b5d53263a5e905059d891b21dede2d8110fd427c7bd5a9a274ddb47b1945ee79522203b6e297d0e399ef')
+
+ c = Crypto(CRYPTO_AES_ICM, key)
+ enc = c.encrypt(pt, iv)
+
+ print('enc:', binascii.hexlify(enc))
+ print(' ct:', binascii.hexlify(ct))
+
+ assert ct == enc
+
+ dec = c.decrypt(ct, iv)
+
+ print('dec:', binascii.hexlify(dec))
+ print(' pt:', binascii.hexlify(pt))
+
+ assert pt == dec
+ elif False:
+ key = _spdechex('c939cc13397c1d37de6ae0e1cb7c423c')
+ iv = _spdechex('00000000000000000000000000000001')
+ pt = _spdechex('ab3cabed693a32946055524052afe3c9cb49664f09fc8b7da824d924006b7496353b8c1657c5dec564d8f38d7432e1de35aae9d95590e66278d4acce883e51abaf94977fcd3679660109a92bf7b2973ccd547f065ec6cee4cb4a72a5e9f45e615d920d76cb34cba482467b3e21422a7242e7d931330c0fbf465c3a3a46fae943029fd899626dda542750a1eee253df323c6ef1573f1c8c156613e2ea0a6cdbf2ae9701020be2d6a83ecb7f3f9d8e0a3f')
+ #pt = _spdechex('00000000000000000000000000000000')
+ ct = _spdechex('f42c33853ecc5ce2949865fdb83de3bff1089e9360c94f830baebfaff72836ab5236f77212f1e7396c8c54ac73d81986375a6e9e299cfeca5ba051ed25e8d1affa5beaf6c1d2b45e90802408f2ced21663497e906de5f29341e5e52ddfea5363d628b3eb7806835e17bae051b3a6da3f8e2941fe44384eac17a9d298d2c331ca8320c775b5d53263a5e905059d891b21dede2d8110fd427c7bd5a9a274ddb47b1945ee79522203b6e297d0e399ef3768')
+
+ c = Crypto(CRYPTO_AES_ICM, key)
+ enc = c.encrypt(pt, iv)
+
+ print('enc:', binascii.hexlify(enc))
+ print(' ct:', binascii.hexlify(ct))
+
+ assert ct == enc
+
+ dec = c.decrypt(ct, iv)
+
+ print('dec:', binascii.hexlify(dec))
+ print(' pt:', binascii.hexlify(pt))
+
+ assert pt == dec
+ elif False:
+ key = _spdechex('c939cc13397c1d37de6ae0e1cb7c423c')
+ iv = _spdechex('6eba2716ec0bd6fa5cdef5e6d3a795bc')
+ pt = _spdechex('ab3cabed693a32946055524052afe3c9cb49664f09fc8b7da824d924006b7496353b8c1657c5dec564d8f38d7432e1de35aae9d95590e66278d4acce883e51abaf94977fcd3679660109a92bf7b2973ccd547f065ec6cee4cb4a72a5e9f45e615d920d76cb34cba482467b3e21422a7242e7d931330c0fbf465c3a3a46fae943029fd899626dda542750a1eee253df323c6ef1573f1c8c156613e2ea0a6cdbf2ae9701020be2d6a83ecb7f3f9d8e0a3f')
+ ct = _spdechex('f1f81f12e72e992dbdc304032705dc75dc3e4180eff8ee4819906af6aee876d5b00b7c36d282a445ce3620327be481e8e53a8e5a8e5ca9abfeb2281be88d12ffa8f46d958d8224738c1f7eea48bda03edbf9adeb900985f4fa25648b406d13a886c25e70cfdecdde0ad0f2991420eb48a61c64fd797237cf2798c2675b9bb744360b0a3f329ac53bbceb4e3e7456e6514f1a9d2f06c236c31d0f080b79c15dce1096357416602520daa098b17d1af427')
+ c = Crypto(CRYPTO_AES_CBC, key)
+
+ enc = c.encrypt(pt, iv)
+
+ print('enc:', binascii.hexlify(enc))
+ print(' ct:', binascii.hexlify(ct))
+
+ assert ct == enc
+
+ dec = c.decrypt(ct, iv)
+
+ print('dec:', binascii.hexlify(dec))
+ print(' pt:', binascii.hexlify(pt))
+
+ assert pt == dec
+ elif False:
+ key = _spdechex('c939cc13397c1d37de6ae0e1cb7c423c')
+ iv = _spdechex('b3d8cc017cbb89b39e0f67e2')
+ pt = _spdechex('c3b3c41f113a31b73d9a5cd4321030')
+ aad = _spdechex('24825602bd12a984e0092d3e448eda5f')
+ ct = _spdechex('93fe7d9e9bfd10348a5606e5cafa7354')
+ ct = _spdechex('93fe7d9e9bfd10348a5606e5cafa73')
+ tag = _spdechex('0032a1dc85f1c9786925a2e71d8272dd')
+ tag = _spdechex('8d11a0929cb3fbe1fef01a4a38d5f8ea')
+
+ c = Crypto(CRYPTO_AES_NIST_GCM_16, key)
+
+ enc, enctag = c.encrypt(pt, iv, aad=aad)
+
+ print('enc:', binascii.hexlify(enc))
+ print(' ct:', binascii.hexlify(ct))
+
+ assert enc == ct
+
+ print('etg:', binascii.hexlify(enctag))
+ print('tag:', binascii.hexlify(tag))
+ assert enctag == tag
+
+ # Make sure we get EBADMSG
+ #enctag = enctag[:-1] + 'a'
+ dec, dectag = c.decrypt(ct, iv, aad=aad, tag=enctag)
+
+ print('dec:', binascii.hexlify(dec))
+ print(' pt:', binascii.hexlify(pt))
+
+ assert dec == pt
+
+ print('dtg:', binascii.hexlify(dectag))
+ print('tag:', binascii.hexlify(tag))
+
+ assert dectag == tag
+ elif False:
+ key = _spdechex('c939cc13397c1d37de6ae0e1cb7c423c')
+ iv = _spdechex('b3d8cc017cbb89b39e0f67e2')
+ key = key + iv[:4]
+ iv = iv[4:]
+ pt = _spdechex('c3b3c41f113a31b73d9a5cd432103069')
+ aad = _spdechex('24825602bd12a984e0092d3e448eda5f')
+ ct = _spdechex('93fe7d9e9bfd10348a5606e5cafa7354')
+ tag = _spdechex('0032a1dc85f1c9786925a2e71d8272dd')
+
+ c = Crypto(CRYPTO_AES_GCM_16, key)
+
+ enc, enctag = c.encrypt(pt, iv, aad=aad)
+
+ print('enc:', binascii.hexlify(enc))
+ print(' ct:', binascii.hexlify(ct))
+
+ assert enc == ct
+
+ print('etg:', binascii.hexlify(enctag))
+ print('tag:', binascii.hexlify(tag))
+ assert enctag == tag
+ elif False:
+ for i in range(100000):
+ c = Crypto(CRYPTO_AES_XTS, binascii.unhexlify('1bbfeadf539daedcae33ced497343f3ca1f2474ad932b903997d44707db41382'))
+ data = binascii.unhexlify('52a42bca4e9425a25bbc8c8bf6129dec')
+ ct = binascii.unhexlify('517e602becd066b65fa4f4f56ddfe240')
+ iv = _pack('QQ', 71, 0)
+
+ enc = c.encrypt(data, iv)
+ assert enc == ct
+ elif True:
+ c = Crypto(CRYPTO_AES_XTS, binascii.unhexlify('1bbfeadf539daedcae33ced497343f3ca1f2474ad932b903997d44707db41382'))
+ data = binascii.unhexlify('52a42bca4e9425a25bbc8c8bf6129dec')
+ ct = binascii.unhexlify('517e602becd066b65fa4f4f56ddfe240')
+ iv = _pack('QQ', 71, 0)
+
+ enc = c.encrypt(data, iv)
+ assert enc == ct
+
+ dec = c.decrypt(enc, iv)
+ assert dec == data
+
+ #c.perftest(COP_ENCRYPT, 192*1024, reps=30000)
+
+ else:
+ key = binascii.unhexlify('1bbfeadf539daedcae33ced497343f3ca1f2474ad932b903997d44707db41382')
+ print('XTS %d testing:' % (len(key) * 8))
+ c = Crypto(CRYPTO_AES_XTS, key)
+ for i in [ 8192, 192*1024]:
+ print('block size: %d' % i)
+ c.perftest(COP_ENCRYPT, i)
+ c.perftest(COP_DECRYPT, i)
diff --git a/tests/sys/opencrypto/cryptodevh.py b/tests/sys/opencrypto/cryptodevh.py
new file mode 100644
index 000000000000..9607bcd4d115
--- /dev/null
+++ b/tests/sys/opencrypto/cryptodevh.py
@@ -0,0 +1,237 @@
+# Generated by h2py from stdin
+
+# Included from sys/ioccom.h
+IOCPARM_SHIFT = 13
+IOCPARM_MASK = ((1 << IOCPARM_SHIFT) - 1)
+def IOCPARM_LEN(x): return (((x) >> 16) & IOCPARM_MASK)
+
+def IOCBASECMD(x): return ((x) & ~(IOCPARM_MASK << 16))
+
+def IOCGROUP(x): return (((x) >> 8) & 0xff)
+
+IOCPARM_MAX = (1 << IOCPARM_SHIFT)
+IOC_VOID = 0x20000000
+IOC_OUT = 0x40000000
+IOC_IN = 0x80000000
+IOC_INOUT = (IOC_IN|IOC_OUT)
+IOC_DIRMASK = (IOC_VOID|IOC_OUT|IOC_IN)
+
+# Included from sys/cdefs.h
+def __has_feature(x): return 0
+
+def __has_include(x): return 0
+
+def __has_builtin(x): return 0
+
+def __P(protos): return protos
+
+def __STRING(x): return #x
+
+def __XSTRING(x): return __STRING(x)
+
+def __P(protos): return ()
+
+def __STRING(x): return "x"
+
+def __aligned(x): return __attribute__((__aligned__(x)))
+
+def __section(x): return __attribute__((__section__(x)))
+
+def __aligned(x): return __attribute__((__aligned__(x)))
+
+def __section(x): return __attribute__((__section__(x)))
+
+def _Alignas(x): return alignas(x)
+
+def _Alignas(x): return __aligned(x)
+
+def _Alignof(x): return alignof(x)
+
+def _Alignof(x): return __alignof(x)
+
+def __nonnull(x): return __attribute__((__nonnull__(x)))
+
+def __predict_true(exp): return __builtin_expect((exp), 1)
+
+def __predict_false(exp): return __builtin_expect((exp), 0)
+
+def __predict_true(exp): return (exp)
+
+def __predict_false(exp): return (exp)
+
+def __format_arg(fmtarg): return __attribute__((__format_arg__ (fmtarg)))
+
+def __GLOBL(sym): return __GLOBL1(sym)
+
+def __FBSDID(s): return __IDSTRING(__CONCAT(__rcsid_,__LINE__),s)
+
+def __RCSID(s): return __IDSTRING(__CONCAT(__rcsid_,__LINE__),s)
+
+def __RCSID_SOURCE(s): return __IDSTRING(__CONCAT(__rcsid_source_,__LINE__),s)
+
+def __SCCSID(s): return __IDSTRING(__CONCAT(__sccsid_,__LINE__),s)
+
+def __COPYRIGHT(s): return __IDSTRING(__CONCAT(__copyright_,__LINE__),s)
+
+_POSIX_C_SOURCE = 199009
+_POSIX_C_SOURCE = 199209
+__XSI_VISIBLE = 700
+_POSIX_C_SOURCE = 200809
+__XSI_VISIBLE = 600
+_POSIX_C_SOURCE = 200112
+__XSI_VISIBLE = 500
+_POSIX_C_SOURCE = 199506
+_POSIX_C_SOURCE = 198808
+__POSIX_VISIBLE = 200809
+__ISO_C_VISIBLE = 1999
+__POSIX_VISIBLE = 200112
+__ISO_C_VISIBLE = 1999
+__POSIX_VISIBLE = 199506
+__ISO_C_VISIBLE = 1990
+__POSIX_VISIBLE = 199309
+__ISO_C_VISIBLE = 1990
+__POSIX_VISIBLE = 199209
+__ISO_C_VISIBLE = 1990
+__POSIX_VISIBLE = 199009
+__ISO_C_VISIBLE = 1990
+__POSIX_VISIBLE = 198808
+__ISO_C_VISIBLE = 0
+__POSIX_VISIBLE = 0
+__XSI_VISIBLE = 0
+__BSD_VISIBLE = 0
+__ISO_C_VISIBLE = 1990
+__POSIX_VISIBLE = 0
+__XSI_VISIBLE = 0
+__BSD_VISIBLE = 0
+__ISO_C_VISIBLE = 1999
+__POSIX_VISIBLE = 0
+__XSI_VISIBLE = 0
+__BSD_VISIBLE = 0
+__ISO_C_VISIBLE = 2011
+__POSIX_VISIBLE = 200809
+__XSI_VISIBLE = 700
+__BSD_VISIBLE = 1
+__ISO_C_VISIBLE = 2011
+CRYPTO_DRIVERS_INITIAL = 4
+CRYPTO_SW_SESSIONS = 32
+NULL_HASH_LEN = 16
+MD5_HASH_LEN = 16
+SHA1_HASH_LEN = 20
+RIPEMD160_HASH_LEN = 20
+SHA2_256_HASH_LEN = 32
+SHA2_384_HASH_LEN = 48
+SHA2_512_HASH_LEN = 64
+MD5_KPDK_HASH_LEN = 16
+SHA1_KPDK_HASH_LEN = 20
+HASH_MAX_LEN = SHA2_512_HASH_LEN
+NULL_HMAC_BLOCK_LEN = 64
+MD5_HMAC_BLOCK_LEN = 64
+SHA1_HMAC_BLOCK_LEN = 64
+RIPEMD160_HMAC_BLOCK_LEN = 64
+SHA2_256_HMAC_BLOCK_LEN = 64
+SHA2_384_HMAC_BLOCK_LEN = 128
+SHA2_512_HMAC_BLOCK_LEN = 128
+HMAC_MAX_BLOCK_LEN = SHA2_512_HMAC_BLOCK_LEN
+HMAC_IPAD_VAL = 0x36
+HMAC_OPAD_VAL = 0x5C
+NULL_BLOCK_LEN = 4
+DES_BLOCK_LEN = 8
+DES3_BLOCK_LEN = 8
+BLOWFISH_BLOCK_LEN = 8
+SKIPJACK_BLOCK_LEN = 8
+CAST128_BLOCK_LEN = 8
+RIJNDAEL128_BLOCK_LEN = 16
+AES_BLOCK_LEN = RIJNDAEL128_BLOCK_LEN
+CAMELLIA_BLOCK_LEN = 16
+EALG_MAX_BLOCK_LEN = AES_BLOCK_LEN
+AALG_MAX_RESULT_LEN = 64
+CRYPTO_ALGORITHM_MIN = 1
+CRYPTO_DES_CBC = 1
+CRYPTO_3DES_CBC = 2
+CRYPTO_BLF_CBC = 3
+CRYPTO_CAST_CBC = 4
+CRYPTO_SKIPJACK_CBC = 5
+CRYPTO_MD5_HMAC = 6
+CRYPTO_SHA1_HMAC = 7
+CRYPTO_RIPEMD160_HMAC = 8
+CRYPTO_MD5_KPDK = 9
+CRYPTO_SHA1_KPDK = 10
+CRYPTO_RIJNDAEL128_CBC = 11
+CRYPTO_AES_CBC = 11
+CRYPTO_ARC4 = 12
+CRYPTO_MD5 = 13
+CRYPTO_SHA1 = 14
+CRYPTO_NULL_HMAC = 15
+CRYPTO_NULL_CBC = 16
+CRYPTO_DEFLATE_COMP = 17
+CRYPTO_SHA2_256_HMAC = 18
+CRYPTO_SHA2_384_HMAC = 19
+CRYPTO_SHA2_512_HMAC = 20
+CRYPTO_CAMELLIA_CBC = 21
+CRYPTO_AES_XTS = 22
+CRYPTO_AES_ICM = 23
+CRYPTO_AES_NIST_GMAC = 24
+CRYPTO_AES_NIST_GCM_16 = 25
+CRYPTO_BLAKE2B = 29
+CRYPTO_BLAKE2S = 30
+CRYPTO_CHACHA20 = 31
+CRYPTO_SHA2_224_HMAC = 32
+CRYPTO_RIPEMD160 = 33
+CRYPTO_SHA2_224 = 34
+CRYPTO_SHA2_256 = 35
+CRYPTO_SHA2_384 = 36
+CRYPTO_SHA2_512 = 37
+CRYPTO_POLY1305 = 38
+CRYPTO_AES_CCM_CBC_MAC = 39
+CRYPTO_AES_CCM_16 = 40
+CRYPTO_ALGORITHM_MAX = 40
+CRYPTO_ALG_FLAG_SUPPORTED = 0x01
+CRYPTO_ALG_FLAG_RNG_ENABLE = 0x02
+CRYPTO_ALG_FLAG_DSA_SHA = 0x04
+CRYPTO_FLAG_HARDWARE = 0x01000000
+CRYPTO_FLAG_SOFTWARE = 0x02000000
+COP_ENCRYPT = 1
+COP_DECRYPT = 2
+COP_F_BATCH = 0x0008
+CRK_MAXPARAM = 8
+CRK_ALGORITM_MIN = 0
+CRK_MOD_EXP = 0
+CRK_MOD_EXP_CRT = 1
+CRK_DSA_SIGN = 2
+CRK_DSA_VERIFY = 3
+CRK_DH_COMPUTE_KEY = 4
+CRK_ALGORITHM_MAX = 4
+CRF_MOD_EXP = (1 << CRK_MOD_EXP)
+CRF_MOD_EXP_CRT = (1 << CRK_MOD_EXP_CRT)
+CRF_DSA_SIGN = (1 << CRK_DSA_SIGN)
+CRF_DSA_VERIFY = (1 << CRK_DSA_VERIFY)
+CRF_DH_COMPUTE_KEY = (1 << CRK_DH_COMPUTE_KEY)
+CRD_F_ENCRYPT = 0x01
+CRD_F_IV_PRESENT = 0x02
+CRD_F_IV_EXPLICIT = 0x04
+CRD_F_DSA_SHA_NEEDED = 0x08
+CRD_F_COMP = 0x0f
+CRD_F_KEY_EXPLICIT = 0x10
+CRYPTO_F_IMBUF = 0x0001
+CRYPTO_F_IOV = 0x0002
+CRYPTO_F_BATCH = 0x0008
+CRYPTO_F_CBIMM = 0x0010
+CRYPTO_F_DONE = 0x0020
+CRYPTO_F_CBIFSYNC = 0x0040
+CRYPTO_BUF_CONTIG = 0x0
+CRYPTO_BUF_IOV = 0x1
+CRYPTO_BUF_MBUF = 0x2
+CRYPTO_OP_DECRYPT = 0x0
+CRYPTO_OP_ENCRYPT = 0x1
+CRYPTO_HINT_MORE = 0x1
+def CRYPTO_SESID2HID(_sid): return (((_sid) >> 32) & 0x00ffffff)
+
+def CRYPTO_SESID2CAPS(_sid): return (((_sid) >> 32) & 0xff000000)
+
+def CRYPTO_SESID2LID(_sid): return (((u_int32_t) (_sid)) & 0xffffffff)
+
+CRYPTOCAP_F_HARDWARE = CRYPTO_FLAG_HARDWARE
+CRYPTOCAP_F_SOFTWARE = CRYPTO_FLAG_SOFTWARE
+CRYPTOCAP_F_SYNC = 0x04000000
+CRYPTO_SYMQ = 0x1
+CRYPTO_ASYMQ = 0x2
diff --git a/tests/sys/opencrypto/cryptotest.py b/tests/sys/opencrypto/cryptotest.py
new file mode 100644
index 000000000000..c590e7b90c4c
--- /dev/null
+++ b/tests/sys/opencrypto/cryptotest.py
@@ -0,0 +1,456 @@
+#!/usr/local/bin/python3
+#
+# Copyright (c) 2014 The FreeBSD Foundation
+# All rights reserved.
+# Copyright 2019 Enji Cooper
+#
+# This software was developed by John-Mark Gurney under
+# the sponsorship from the FreeBSD Foundation.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+
+
+import binascii
+import errno
+import cryptodev
+import itertools
+import os
+import struct
+import unittest
+from cryptodev import *
+from glob import iglob
+
+katdir = '/usr/local/share/nist-kat'
+
+def katg(base, glob):
+ assert os.path.exists(katdir), "Please 'pkg install nist-kat'"
+ if not os.path.exists(os.path.join(katdir, base)):
+ raise unittest.SkipTest("Missing %s test vectors" % (base))
+ return iglob(os.path.join(katdir, base, glob))
+
+aesmodules = [ 'cryptosoft0', 'aesni0', 'armv8crypto0', 'ccr0', 'ccp0', 'ossl0', 'safexcel0', 'qat0' ]
+shamodules = [ 'cryptosoft0', 'aesni0', 'armv8crypto0', 'ccr0', 'ccp0', 'ossl0', 'safexcel0', 'qat0' ]
+
+def GenTestCase(cname):
+ try:
+ crid = cryptodev.Crypto.findcrid(cname)
+ except IOError:
+ return None
+
+ class GendCryptoTestCase(unittest.TestCase):
+ ###############
+ ##### AES #####
+ ###############
+ @unittest.skipIf(cname not in aesmodules, 'skipping AES-XTS on %s' % (cname))
+ def test_xts(self):
+ for i in katg('XTSTestVectors/format tweak value input - data unit seq no', '*.rsp'):
+ self.runXTS(i, cryptodev.CRYPTO_AES_XTS)
+
+ @unittest.skipIf(cname not in aesmodules, 'skipping AES-CBC on %s' % (cname))
+ def test_cbc(self):
+ for i in katg('KAT_AES', 'CBC[GKV]*.rsp'):
+ self.runCBC(i)
+
+ @unittest.skipIf(cname not in aesmodules, 'skipping AES-CCM on %s' % (cname))
+ def test_ccm(self):
+ for i in katg('ccmtestvectors', 'V*.rsp'):
+ self.runCCMEncrypt(i)
+
+ for i in katg('ccmtestvectors', 'D*.rsp'):
+ self.runCCMDecrypt(i)
+
+ @unittest.skipIf(cname not in aesmodules, 'skipping AES-GCM on %s' % (cname))
+ def test_gcm(self):
+ for i in katg('gcmtestvectors', 'gcmEncrypt*'):
+ self.runGCM(i, 'ENCRYPT')
+
+ for i in katg('gcmtestvectors', 'gcmDecrypt*'):
+ self.runGCM(i, 'DECRYPT')
+
+ def runGCM(self, fname, mode):
+ curfun = None
+ if mode == 'ENCRYPT':
+ swapptct = False
+ curfun = Crypto.encrypt
+ elif mode == 'DECRYPT':
+ swapptct = True
+ curfun = Crypto.decrypt
+ else:
+ raise RuntimeError('unknown mode: %r' % repr(mode))
+
+ columns = [ 'Count', 'Key', 'IV', 'CT', 'AAD', 'Tag', 'PT', ]
+ with cryptodev.KATParser(fname, columns) as parser:
+ self.runGCMWithParser(parser, mode)
+
+ def runGCMWithParser(self, parser, mode):
+ for _, lines in next(parser):
+ for data in lines:
+ curcnt = int(data['Count'])
+ cipherkey = binascii.unhexlify(data['Key'])
+ iv = binascii.unhexlify(data['IV'])
+ aad = binascii.unhexlify(data['AAD'])
+ tag = binascii.unhexlify(data['Tag'])
+ if 'FAIL' not in data:
+ pt = binascii.unhexlify(data['PT'])
+ ct = binascii.unhexlify(data['CT'])
+
+ if len(iv) != 12:
+ # XXX - isn't supported
+ continue
+
+ try:
+ c = Crypto(cryptodev.CRYPTO_AES_NIST_GCM_16,
+ cipherkey, crid=crid,
+ maclen=16)
+ except EnvironmentError as e:
+ # Can't test algorithms the driver does not support.
+ if e.errno != errno.EOPNOTSUPP:
+ raise
+ continue
+
+ if mode == 'ENCRYPT':
+ try:
+ rct, rtag = c.encrypt(pt, iv, aad)
+ except EnvironmentError as e:
+ # Can't test inputs the driver does not support.
+ if e.errno != errno.EINVAL:
+ raise
+ continue
+ rtag = rtag[:len(tag)]
+ data['rct'] = binascii.hexlify(rct)
+ data['rtag'] = binascii.hexlify(rtag)
+ self.assertEqual(rct, ct, repr(data))
+ self.assertEqual(rtag, tag, repr(data))
+ else:
+ if len(tag) != 16:
+ continue
+ args = (ct, iv, aad, tag)
+ if 'FAIL' in data:
+ self.assertRaises(IOError,
+ c.decrypt, *args)
+ else:
+ try:
+ rpt, rtag = c.decrypt(*args)
+ except EnvironmentError as e:
+ # Can't test inputs the driver does not support.
+ if e.errno != errno.EINVAL:
+ raise
+ continue
+ data['rpt'] = binascii.hexlify(rpt)
+ data['rtag'] = binascii.hexlify(rtag)
+ self.assertEqual(rpt, pt,
+ repr(data))
+
+ def runCBC(self, fname):
+ columns = [ 'COUNT', 'KEY', 'IV', 'PLAINTEXT', 'CIPHERTEXT', ]
+ with cryptodev.KATParser(fname, columns) as parser:
+ self.runCBCWithParser(parser)
+
+ def runCBCWithParser(self, parser):
+ curfun = None
+ for mode, lines in next(parser):
+ if mode == 'ENCRYPT':
+ swapptct = False
+ curfun = Crypto.encrypt
+ elif mode == 'DECRYPT':
+ swapptct = True
+ curfun = Crypto.decrypt
+ else:
+ raise RuntimeError('unknown mode: %r' % repr(mode))
+
+ for data in lines:
+ curcnt = int(data['COUNT'])
+ cipherkey = binascii.unhexlify(data['KEY'])
+ iv = binascii.unhexlify(data['IV'])
+ pt = binascii.unhexlify(data['PLAINTEXT'])
+ ct = binascii.unhexlify(data['CIPHERTEXT'])
+
+ if swapptct:
+ pt, ct = ct, pt
+ # run the fun
+ c = Crypto(cryptodev.CRYPTO_AES_CBC, cipherkey, crid=crid)
+ r = curfun(c, pt, iv)
+ self.assertEqual(r, ct)
+
+ def runXTS(self, fname, meth):
+ columns = [ 'COUNT', 'DataUnitLen', 'Key', 'DataUnitSeqNumber', 'PT',
+ 'CT']
+ with cryptodev.KATParser(fname, columns) as parser:
+ self.runXTSWithParser(parser, meth)
+
+ def runXTSWithParser(self, parser, meth):
+ curfun = None
+ for mode, lines in next(parser):
+ if mode == 'ENCRYPT':
+ swapptct = False
+ curfun = Crypto.encrypt
+ elif mode == 'DECRYPT':
+ swapptct = True
+ curfun = Crypto.decrypt
+ else:
+ raise RuntimeError('unknown mode: %r' % repr(mode))
+
+ for data in lines:
+ curcnt = int(data['COUNT'])
+ nbits = int(data['DataUnitLen'])
+ cipherkey = binascii.unhexlify(data['Key'])
+ iv = struct.pack('QQ', int(data['DataUnitSeqNumber']), 0)
+ pt = binascii.unhexlify(data['PT'])
+ ct = binascii.unhexlify(data['CT'])
+
+ if nbits % 128 != 0:
+ # XXX - mark as skipped
+ continue
+ if swapptct:
+ pt, ct = ct, pt
+ # run the fun
+ try:
+ c = Crypto(meth, cipherkey, crid=crid)
+ r = curfun(c, pt, iv)
+ except EnvironmentError as e:
+ # Can't test hashes the driver does not support.
+ if e.errno != errno.EOPNOTSUPP:
+ raise
+ continue
+ self.assertEqual(r, ct)
+
+ def runCCMEncrypt(self, fname):
+ with cryptodev.KATCCMParser(fname) as parser:
+ self.runCCMEncryptWithParser(parser)
+
+ def runCCMEncryptWithParser(self, parser):
+ for data in next(parser):
+ Nlen = int(data['Nlen'])
+ Tlen = int(data['Tlen'])
+ key = binascii.unhexlify(data['Key'])
+ nonce = binascii.unhexlify(data['Nonce'])
+ Alen = int(data['Alen'])
+ Plen = int(data['Plen'])
+ if Alen != 0:
+ aad = binascii.unhexlify(data['Adata'])
+ else:
+ aad = None
+ if Plen != 0:
+ payload = binascii.unhexlify(data['Payload'])
+ else:
+ payload = None
+ ct = binascii.unhexlify(data['CT'])
+
+ try:
+ c = Crypto(crid=crid,
+ cipher=cryptodev.CRYPTO_AES_CCM_16,
+ key=key,
+ mackey=key, maclen=Tlen, ivlen=Nlen)
+ r, tag = Crypto.encrypt(c, payload,
+ nonce, aad)
+ except EnvironmentError as e:
+ if e.errno != errno.EOPNOTSUPP:
+ raise
+ continue
+
+ out = r + tag
+ self.assertEqual(out, ct,
+ "Count " + data['Count'] + " Actual: " + \
+ repr(binascii.hexlify(out)) + " Expected: " + \
+ repr(data) + " on " + cname)
+
+ def runCCMDecrypt(self, fname):
+ with cryptodev.KATCCMParser(fname) as parser:
+ self.runCCMDecryptWithParser(parser)
+
+ def runCCMDecryptWithParser(self, parser):
+ for data in next(parser):
+ Nlen = int(data['Nlen'])
+ Tlen = int(data['Tlen'])
+ key = binascii.unhexlify(data['Key'])
+ nonce = binascii.unhexlify(data['Nonce'])
+ Alen = int(data['Alen'])
+ Plen = int(data['Plen'])
+ if Alen != 0:
+ aad = binascii.unhexlify(data['Adata'])
+ else:
+ aad = None
+ ct = binascii.unhexlify(data['CT'])
+ tag = ct[-Tlen:]
+ if Plen != 0:
+ payload = ct[:-Tlen]
+ else:
+ payload = None
+
+ try:
+ c = Crypto(crid=crid,
+ cipher=cryptodev.CRYPTO_AES_CCM_16,
+ key=key,
+ mackey=key, maclen=Tlen, ivlen=Nlen)
+ except EnvironmentError as e:
+ if e.errno != errno.EOPNOTSUPP:
+ raise
+ continue
+
+ if data['Result'] == 'Fail':
+ self.assertRaises(IOError,
+ c.decrypt, payload, nonce, aad, tag)
+ else:
+ r, tag = Crypto.decrypt(c, payload, nonce,
+ aad, tag)
+
+ payload = binascii.unhexlify(data['Payload'])
+ payload = payload[:Plen]
+ self.assertEqual(r, payload,
+ "Count " + data['Count'] + \
+ " Actual: " + repr(binascii.hexlify(r)) + \
+ " Expected: " + repr(data) + \
+ " on " + cname)
+
+ ###############
+ ##### SHA #####
+ ###############
+ @unittest.skipIf(cname not in shamodules, 'skipping SHA on %s' % str(cname))
+ def test_sha(self):
+ for i in katg('shabytetestvectors', 'SHA*Msg.rsp'):
+ self.runSHA(i)
+
+ def runSHA(self, fname):
+ # Skip SHA512_(224|256) tests
+ if fname.find('SHA512_') != -1:
+ return
+ columns = [ 'Len', 'Msg', 'MD' ]
+ with cryptodev.KATParser(fname, columns) as parser:
+ self.runSHAWithParser(parser)
+
+ def runSHAWithParser(self, parser):
+ for hashlength, lines in next(parser):
+ # E.g., hashlength will be "L=20" (bytes)
+ hashlen = int(hashlength.split("=")[1])
+
+ if hashlen == 20:
+ alg = cryptodev.CRYPTO_SHA1
+ elif hashlen == 28:
+ alg = cryptodev.CRYPTO_SHA2_224
+ elif hashlen == 32:
+ alg = cryptodev.CRYPTO_SHA2_256
+ elif hashlen == 48:
+ alg = cryptodev.CRYPTO_SHA2_384
+ elif hashlen == 64:
+ alg = cryptodev.CRYPTO_SHA2_512
+ else:
+ # Skip unsupported hashes
+ # Slurp remaining input in section
+ for data in lines:
+ continue
+ continue
+
+ for data in lines:
+ msg = binascii.unhexlify(data['Msg'])
+ msg = msg[:int(data['Len'])]
+ md = binascii.unhexlify(data['MD'])
+
+ try:
+ c = Crypto(mac=alg, crid=crid,
+ maclen=hashlen)
+ except EnvironmentError as e:
+ # Can't test hashes the driver does not support.
+ if e.errno != errno.EOPNOTSUPP:
+ raise
+ continue
+
+ _, r = c.encrypt(msg, iv="")
+
+ self.assertEqual(r, md, "Actual: " + \
+ repr(binascii.hexlify(r)) + " Expected: " + repr(data) + " on " + cname)
+
+ @unittest.skipIf(cname not in shamodules, 'skipping SHA-HMAC on %s' % str(cname))
+ def test_sha1hmac(self):
+ for i in katg('hmactestvectors', 'HMAC.rsp'):
+ self.runSHA1HMAC(i)
+
+ def runSHA1HMAC(self, fname):
+ columns = [ 'Count', 'Klen', 'Tlen', 'Key', 'Msg', 'Mac' ]
+ with cryptodev.KATParser(fname, columns) as parser:
+ self.runSHA1HMACWithParser(parser)
+
+ def runSHA1HMACWithParser(self, parser):
+ for hashlength, lines in next(parser):
+ # E.g., hashlength will be "L=20" (bytes)
+ hashlen = int(hashlength.split("=")[1])
+
+ blocksize = None
+ if hashlen == 20:
+ alg = cryptodev.CRYPTO_SHA1_HMAC
+ blocksize = 64
+ elif hashlen == 28:
+ alg = cryptodev.CRYPTO_SHA2_224_HMAC
+ blocksize = 64
+ elif hashlen == 32:
+ alg = cryptodev.CRYPTO_SHA2_256_HMAC
+ blocksize = 64
+ elif hashlen == 48:
+ alg = cryptodev.CRYPTO_SHA2_384_HMAC
+ blocksize = 128
+ elif hashlen == 64:
+ alg = cryptodev.CRYPTO_SHA2_512_HMAC
+ blocksize = 128
+ else:
+ # Skip unsupported hashes
+ # Slurp remaining input in section
+ for data in lines:
+ continue
+ continue
+
+ for data in lines:
+ key = binascii.unhexlify(data['Key'])
+ msg = binascii.unhexlify(data['Msg'])
+ mac = binascii.unhexlify(data['Mac'])
+ tlen = int(data['Tlen'])
+
+ if len(key) > blocksize:
+ continue
+
+ try:
+ c = Crypto(mac=alg, mackey=key,
+ crid=crid, maclen=hashlen)
+ except EnvironmentError as e:
+ # Can't test hashes the driver does not support.
+ if e.errno != errno.EOPNOTSUPP:
+ raise
+ continue
+
+ _, r = c.encrypt(msg, iv="")
+
+ self.assertEqual(r[:tlen], mac, "Actual: " + \
+ repr(binascii.hexlify(r)) + " Expected: " + repr(data))
+
+ return GendCryptoTestCase
+
+cryptosoft = GenTestCase('cryptosoft0')
+aesni = GenTestCase('aesni0')
+armv8crypto = GenTestCase('armv8crypto0')
+ccr = GenTestCase('ccr0')
+ccp = GenTestCase('ccp0')
+ossl = GenTestCase('ossl0')
+safexcel = GenTestCase('safexcel0')
+qat = GenTestCase('qat0')
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/sys/opencrypto/poly1305_test.c b/tests/sys/opencrypto/poly1305_test.c
new file mode 100644
index 000000000000..ab455784efba
--- /dev/null
+++ b/tests/sys/opencrypto/poly1305_test.c
@@ -0,0 +1,394 @@
+/*-
+ * Copyright (c) 2018 Conrad Meyer <cem@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+/* Be sure to include tree copy rather than system copy. */
+#include "cryptodev.h"
+
+#include "freebsd_test_suite/macros.h"
+
+struct poly1305_kat {
+ const char *vector_name;
+ const char *test_key_hex;
+ const char *test_msg_hex;
+ const size_t test_msg_len;
+
+ const char *expected_tag_hex;
+};
+
+static const struct poly1305_kat rfc7539_kats[] = {
+{
+ .vector_name = "RFC 7539 \xc2\xa7 2.5.2",
+ .test_key_hex = "85:d6:be:78:57:55:6d:33:7f:44:52:fe:42:d5:06:a8"
+ ":01:03:80:8a:fb:0d:b2:fd:4a:bf:f6:af:41:49:f5:1b",
+ .test_msg_hex =
+"43 72 79 70 74 6f 67 72 61 70 68 69 63 20 46 6f "
+"72 75 6d 20 52 65 73 65 61 72 63 68 20 47 72 6f "
+"75 70",
+ .test_msg_len = 34,
+ .expected_tag_hex = "a8:06:1d:c1:30:51:36:c6:c2:2b:8b:af:0c:01:27:a9",
+},
+{
+ .vector_name = "RFC 7539 \xc2\xa7 A.3 #1",
+ .test_key_hex =
+"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "
+"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ",
+ .test_msg_hex =
+"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "
+"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "
+"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "
+"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ",
+ .test_msg_len = 64,
+ .expected_tag_hex = "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
+},
+{
+ .vector_name = "RFC 7539 \xc2\xa7 A.3 #2",
+ .test_key_hex =
+"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "
+"36 e5 f6 b5 c5 e0 60 70 f0 ef ca 96 22 7a 86 3e ",
+ .test_msg_hex =
+"41 6e 79 20 73 75 62 6d 69 73 73 69 6f 6e 20 74 "
+"6f 20 74 68 65 20 49 45 54 46 20 69 6e 74 65 6e "
+"64 65 64 20 62 79 20 74 68 65 20 43 6f 6e 74 72 "
+"69 62 75 74 6f 72 20 66 6f 72 20 70 75 62 6c 69 "
+"63 61 74 69 6f 6e 20 61 73 20 61 6c 6c 20 6f 72 "
+"20 70 61 72 74 20 6f 66 20 61 6e 20 49 45 54 46 "
+"20 49 6e 74 65 72 6e 65 74 2d 44 72 61 66 74 20 "
+"6f 72 20 52 46 43 20 61 6e 64 20 61 6e 79 20 73 "
+"74 61 74 65 6d 65 6e 74 20 6d 61 64 65 20 77 69 "
+"74 68 69 6e 20 74 68 65 20 63 6f 6e 74 65 78 74 "
+"20 6f 66 20 61 6e 20 49 45 54 46 20 61 63 74 69 "
+"76 69 74 79 20 69 73 20 63 6f 6e 73 69 64 65 72 "
+"65 64 20 61 6e 20 22 49 45 54 46 20 43 6f 6e 74 "
+"72 69 62 75 74 69 6f 6e 22 2e 20 53 75 63 68 20 "
+"73 74 61 74 65 6d 65 6e 74 73 20 69 6e 63 6c 75 "
+"64 65 20 6f 72 61 6c 20 73 74 61 74 65 6d 65 6e "
+"74 73 20 69 6e 20 49 45 54 46 20 73 65 73 73 69 "
+"6f 6e 73 2c 20 61 73 20 77 65 6c 6c 20 61 73 20 "
+"77 72 69 74 74 65 6e 20 61 6e 64 20 65 6c 65 63 "
+"74 72 6f 6e 69 63 20 63 6f 6d 6d 75 6e 69 63 61 "
+"74 69 6f 6e 73 20 6d 61 64 65 20 61 74 20 61 6e "
+"79 20 74 69 6d 65 20 6f 72 20 70 6c 61 63 65 2c "
+"20 77 68 69 63 68 20 61 72 65 20 61 64 64 72 65 "
+"73 73 65 64 20 74 6f",
+ .test_msg_len = 375,
+ .expected_tag_hex = "36 e5 f6 b5 c5 e0 60 70 f0 ef ca 96 22 7a 86 3e",
+},
+{
+ .vector_name = "RFC 7539 \xc2\xa7 A.3 #3",
+ .test_key_hex =
+"36 e5 f6 b5 c5 e0 60 70 f0 ef ca 96 22 7a 86 3e "
+"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ",
+ .test_msg_hex =
+"41 6e 79 20 73 75 62 6d 69 73 73 69 6f 6e 20 74 "
+"6f 20 74 68 65 20 49 45 54 46 20 69 6e 74 65 6e "
+"64 65 64 20 62 79 20 74 68 65 20 43 6f 6e 74 72 "
+"69 62 75 74 6f 72 20 66 6f 72 20 70 75 62 6c 69 "
+"63 61 74 69 6f 6e 20 61 73 20 61 6c 6c 20 6f 72 "
+"20 70 61 72 74 20 6f 66 20 61 6e 20 49 45 54 46 "
+"20 49 6e 74 65 72 6e 65 74 2d 44 72 61 66 74 20 "
+"6f 72 20 52 46 43 20 61 6e 64 20 61 6e 79 20 73 "
+"74 61 74 65 6d 65 6e 74 20 6d 61 64 65 20 77 69 "
+"74 68 69 6e 20 74 68 65 20 63 6f 6e 74 65 78 74 "
+"20 6f 66 20 61 6e 20 49 45 54 46 20 61 63 74 69 "
+"76 69 74 79 20 69 73 20 63 6f 6e 73 69 64 65 72 "
+"65 64 20 61 6e 20 22 49 45 54 46 20 43 6f 6e 74 "
+"72 69 62 75 74 69 6f 6e 22 2e 20 53 75 63 68 20 "
+"73 74 61 74 65 6d 65 6e 74 73 20 69 6e 63 6c 75 "
+"64 65 20 6f 72 61 6c 20 73 74 61 74 65 6d 65 6e "
+"74 73 20 69 6e 20 49 45 54 46 20 73 65 73 73 69 "
+"6f 6e 73 2c 20 61 73 20 77 65 6c 6c 20 61 73 20 "
+"77 72 69 74 74 65 6e 20 61 6e 64 20 65 6c 65 63 "
+"74 72 6f 6e 69 63 20 63 6f 6d 6d 75 6e 69 63 61 "
+"74 69 6f 6e 73 20 6d 61 64 65 20 61 74 20 61 6e "
+"79 20 74 69 6d 65 20 6f 72 20 70 6c 61 63 65 2c "
+"20 77 68 69 63 68 20 61 72 65 20 61 64 64 72 65 "
+"73 73 65 64 20 74 6f",
+ .test_msg_len = 375,
+ .expected_tag_hex = "f3 47 7e 7c d9 54 17 af 89 a6 b8 79 4c 31 0c f0",
+},
+{
+ .vector_name = "RFC 7539 \xc2\xa7 A.3 #4",
+ .test_key_hex =
+"1c 92 40 a5 eb 55 d3 8a f3 33 88 86 04 f6 b5 f0 "
+"47 39 17 c1 40 2b 80 09 9d ca 5c bc 20 70 75 c0 ",
+ .test_msg_hex =
+"27 54 77 61 73 20 62 72 69 6c 6c 69 67 2c 20 61 "
+"6e 64 20 74 68 65 20 73 6c 69 74 68 79 20 74 6f "
+"76 65 73 0a 44 69 64 20 67 79 72 65 20 61 6e 64 "
+"20 67 69 6d 62 6c 65 20 69 6e 20 74 68 65 20 77 "
+"61 62 65 3a 0a 41 6c 6c 20 6d 69 6d 73 79 20 77 "
+"65 72 65 20 74 68 65 20 62 6f 72 6f 67 6f 76 65 "
+"73 2c 0a 41 6e 64 20 74 68 65 20 6d 6f 6d 65 20 "
+"72 61 74 68 73 20 6f 75 74 67 72 61 62 65 2e",
+ .test_msg_len = 127,
+ .expected_tag_hex = "45 41 66 9a 7e aa ee 61 e7 08 dc 7c bc c5 eb 62",
+},
+{
+ .vector_name = "RFC 7539 \xc2\xa7 A.3 #5",
+ .test_key_hex =
+"02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "
+"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ",
+ .test_msg_hex =
+"FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+ .test_msg_len = 16,
+ .expected_tag_hex = "03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
+},
+{
+ .vector_name = "RFC 7539 \xc2\xa7 A.3 #6",
+ .test_key_hex =
+"02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "
+"FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ",
+ .test_msg_hex =
+"02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
+ .test_msg_len = 16,
+ .expected_tag_hex = "03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
+
+},
+{
+ .vector_name = "RFC 7539 \xc2\xa7 A.3 #7",
+ .test_key_hex =
+"01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "
+"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ",
+ .test_msg_hex =
+"FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF "
+"F0 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF "
+"11 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
+ .test_msg_len = 48,
+ .expected_tag_hex = "05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
+},
+{
+ .vector_name = "RFC 7539 \xc2\xa7 A.3 #8",
+ .test_key_hex =
+"01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "
+"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ",
+ .test_msg_hex =
+"FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF "
+"FB FE FE FE FE FE FE FE FE FE FE FE FE FE FE FE "
+"01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01",
+ .test_msg_len = 48,
+ .expected_tag_hex = "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
+},
+{
+ .vector_name = "RFC 7539 \xc2\xa7 A.3 #9",
+ .test_key_hex =
+"02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "
+"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ",
+ .test_msg_hex =
+"FD FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+ .test_msg_len = 16,
+ .expected_tag_hex = "FA FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF",
+},
+{
+ .vector_name = "RFC 7539 \xc2\xa7 A.3 #10",
+ .test_key_hex =
+"01 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 "
+"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ",
+ .test_msg_hex =
+"E3 35 94 D7 50 5E 43 B9 00 00 00 00 00 00 00 00 "
+"33 94 D7 50 5E 43 79 CD 01 00 00 00 00 00 00 00 "
+"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "
+"01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
+ .test_msg_len = 64,
+ .expected_tag_hex = "14 00 00 00 00 00 00 00 55 00 00 00 00 00 00 00",
+},
+{
+ .vector_name = "RFC 7539 \xc2\xa7 A.3 #11",
+ .test_key_hex =
+"01 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 "
+"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ",
+ .test_msg_hex =
+"E3 35 94 D7 50 5E 43 B9 00 00 00 00 00 00 00 00 "
+"33 94 D7 50 5E 43 79 CD 01 00 00 00 00 00 00 00 "
+"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
+ .test_msg_len = 48,
+ .expected_tag_hex = "13 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
+},
+};
+
+static void
+parse_hex(const struct poly1305_kat *kat, const char *hexstr, void *voutput,
+ size_t explen)
+{
+ /* Space or colon delimited; may contain a single trailing space;
+ * length should match exactly.
+ */
+ const char *sep, *it;
+ size_t sym_len, count;
+ char hbyte[3], *out;
+ int res;
+
+ out = voutput;
+ memset(hbyte, 0, sizeof(hbyte));
+
+ it = hexstr;
+ count = 0;
+ while (true) {
+ sep = strpbrk(it, " :");
+ if (sep == NULL)
+ sym_len = strlen(it);
+ else
+ sym_len = sep - it;
+
+ ATF_REQUIRE_EQ_MSG(sym_len, 2,
+ "invalid hex byte '%.*s' in vector %s", (int)sym_len, it,
+ kat->vector_name);
+
+ memcpy(hbyte, it, 2);
+ res = sscanf(hbyte, "%hhx", &out[count]);
+ ATF_REQUIRE_EQ_MSG(res, 1,
+ "invalid hex byte '%s' in vector %s", hbyte,
+ kat->vector_name);
+
+ count++;
+ ATF_REQUIRE_MSG(count <= explen,
+ "got longer than expected value at %s", kat->vector_name);
+
+ if (sep == NULL)
+ break;
+ it = sep;
+ while (*it == ' ' || *it == ':')
+ it++;
+ if (*it == 0)
+ break;
+ }
+
+ ATF_REQUIRE_EQ_MSG(count, explen, "got short value at %s",
+ kat->vector_name);
+}
+
+static void
+parse_vector(const struct poly1305_kat *kat,
+ uint8_t key[__min_size(POLY1305_KEY_LEN)], char *msg,
+ uint8_t exptag[__min_size(POLY1305_HASH_LEN)])
+{
+ parse_hex(kat, kat->test_key_hex, key, POLY1305_KEY_LEN);
+ parse_hex(kat, kat->test_msg_hex, msg, kat->test_msg_len);
+ parse_hex(kat, kat->expected_tag_hex, exptag, POLY1305_HASH_LEN);
+}
+
+static int
+get_handle_fd(void)
+{
+ int fd;
+
+ fd = open("/dev/crypto", O_RDWR);
+ ATF_REQUIRE(fd >= 0);
+ return (fd);
+}
+
+static int
+create_session(int fd, int alg, int crid, const void *key, size_t klen)
+{
+ struct session2_op sop;
+
+ memset(&sop, 0, sizeof(sop));
+
+ sop.mac = alg;
+ sop.mackey = key;
+ sop.mackeylen = klen;
+ sop.crid = crid;
+
+ ATF_REQUIRE_MSG(ioctl(fd, CIOCGSESSION2, &sop) >= 0,
+ "alg %d keylen %zu, errno=%d (%s)", alg, klen, errno,
+ strerror(errno));
+ return (sop.ses);
+}
+
+static void
+destroy_session(int fd, int _ses)
+{
+ uint32_t ses;
+
+ ses = _ses;
+ ATF_REQUIRE_MSG(ioctl(fd, CIOCFSESSION, &ses) >= 0,
+ "destroy session failed, errno=%d (%s)", errno, strerror(errno));
+}
+
+static void
+do_cryptop(int fd, int ses, const void *inp, size_t inlen, void *out)
+{
+ struct crypt_op cop;
+
+ memset(&cop, 0, sizeof(cop));
+
+ cop.ses = ses;
+ cop.len = inlen;
+ cop.src = inp;
+ cop.mac = out;
+ ATF_CHECK_MSG(ioctl(fd, CIOCCRYPT, &cop) >= 0, "ioctl(CIOCCRYPT)");
+}
+
+static void
+test_rfc7539_poly1305_vectors(int crid, const char *modname)
+{
+ uint8_t comptag[POLY1305_HASH_LEN], exptag[POLY1305_HASH_LEN],
+ key[POLY1305_KEY_LEN], msg[512];
+ int fd, ses;
+ size_t i;
+
+ ATF_REQUIRE_KERNEL_MODULE(modname);
+ ATF_REQUIRE_KERNEL_MODULE("cryptodev");
+
+ fd = get_handle_fd();
+
+ for (i = 0; i < nitems(rfc7539_kats); i++) {
+ const struct poly1305_kat *kat;
+
+ kat = &rfc7539_kats[i];
+ parse_vector(kat, key, msg, exptag);
+
+ ses = create_session(fd, CRYPTO_POLY1305, crid, key, sizeof(key));
+
+ do_cryptop(fd, ses, msg, kat->test_msg_len, comptag);
+ ATF_CHECK_EQ_MSG(memcmp(comptag, exptag, sizeof(exptag)), 0,
+ "KAT %s failed:", kat->vector_name);
+
+ destroy_session(fd, ses);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(poly1305_vectors);
+ATF_TC_BODY(poly1305_vectors, tc)
+{
+ ATF_REQUIRE_SYSCTL_INT("kern.crypto.allow_soft", 1);
+ test_rfc7539_poly1305_vectors(CRYPTO_FLAG_SOFTWARE, "nexus/cryptosoft");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, poly1305_vectors);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/opencrypto/runtests.sh b/tests/sys/opencrypto/runtests.sh
new file mode 100644
index 000000000000..bac49ad3e306
--- /dev/null
+++ b/tests/sys/opencrypto/runtests.sh
@@ -0,0 +1,100 @@
+#!/bin/sh -
+#
+# Copyright (c) 2014 The FreeBSD Foundation
+# All rights reserved.
+# Copyright 2019 Enji Cooper
+#
+# This software was developed by John-Mark Gurney under
+# the sponsorship from the FreeBSD Foundation.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#
+
+: ${PYTHON=python3}
+
+if [ ! -d /usr/local/share/nist-kat ]; then
+ echo "1..0 # SKIP: nist-kat package not installed for test vectors"
+ exit 0
+fi
+
+if ! $PYTHON -c "from dpkt import dpkt"; then
+ echo "1..0 # SKIP: py-dpkt package not installed"
+ exit 0
+fi
+
+loaded_modules=
+cleanup_tests()
+{
+ trap - EXIT INT TERM
+
+ set +e
+
+ if [ -n "$oldcdas" ]; then
+ sysctl "$oldcdas" 2>/dev/null
+ fi
+
+ # Unload modules in reverse order
+ for loaded_module in $(echo $loaded_modules | tr ' ' '\n' | sort -r); do
+ kldunload $loaded_module
+ done
+}
+trap cleanup_tests EXIT INT TERM
+
+cpu_type="$(uname -p)"
+cpu_module=
+
+case ${cpu_type} in
+aarch64)
+ cpu_module="nexus/armv8crypto nexus/ossl"
+ ;;
+amd64|i386)
+ cpu_module="nexus/aesni nexus/ossl"
+ ;;
+esac
+
+for required_module in $cpu_module cryptodev; do
+ if ! kldstat -q -m $required_module; then
+ module_to_load=${required_module#nexus/}
+ if ! kldload ${module_to_load}; then
+ echo "1..0 # SKIP: could not load ${module_to_load}"
+ exit 0
+ fi
+ loaded_modules="$loaded_modules $required_module"
+ fi
+done
+
+cdas_sysctl=kern.crypto.allow_soft
+if ! oldcdas=$(sysctl -e $cdas_sysctl); then
+ echo "1..0 # SKIP: could not resolve sysctl: $cdas_sysctl"
+ exit 0
+fi
+if ! sysctl $cdas_sysctl=1; then
+ echo "1..0 # SKIP: could not enable /dev/crypto access via $cdas_sysctl sysctl."
+ exit 0
+fi
+
+echo "1..1"
+if "$PYTHON" $(dirname $0)/cryptotest.py $CRYPTOTEST_ARGS; then
+ echo "ok 1"
+else
+ echo "not ok 1"
+fi
diff --git a/tests/sys/pjdfstest/Makefile b/tests/sys/pjdfstest/Makefile
new file mode 100644
index 000000000000..f8a6f1cd6b8d
--- /dev/null
+++ b/tests/sys/pjdfstest/Makefile
@@ -0,0 +1,8 @@
+PACKAGE= tests
+
+SUBDIR+= pjdfstest
+SUBDIR+= tests
+
+SUBDIR_PARALLEL=
+
+.include <bsd.subdir.mk>
diff --git a/tests/sys/pjdfstest/Makefile.inc b/tests/sys/pjdfstest/Makefile.inc
new file mode 100644
index 000000000000..cec69b26e149
--- /dev/null
+++ b/tests/sys/pjdfstest/Makefile.inc
@@ -0,0 +1 @@
+.include "${SRCTOP}/tests/Makefile.inc0"
diff --git a/tests/sys/pjdfstest/config.h b/tests/sys/pjdfstest/config.h
new file mode 100644
index 000000000000..76dd4028fcaf
--- /dev/null
+++ b/tests/sys/pjdfstest/config.h
@@ -0,0 +1,205 @@
+/* config.h. Generated from config.h.in by configure. */
+/* config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define to 1 if NFSv4 ACL support is available */
+#define HAS_NFSV4_ACL_SUPPORT 1
+
+/* Define to 1 if you have the `acl_create_entry_np' function. */
+#define HAVE_ACL_CREATE_ENTRY_NP 1
+
+/* Define to 1 if you have the `acl_from_text' function. */
+#define HAVE_ACL_FROM_TEXT 1
+
+/* Define to 1 if you have the `acl_get_entry' function. */
+#define HAVE_ACL_GET_ENTRY 1
+
+/* Define to 1 if you have the `acl_get_file' function. */
+#define HAVE_ACL_GET_FILE 1
+
+/* Define to 1 if you have the `acl_set_file' function. */
+#define HAVE_ACL_SET_FILE 1
+
+/* Define if bindat exists */
+#define HAVE_BINDAT 1
+
+/* Define if chflags exists */
+#define HAVE_CHFLAGS 1
+
+/* Define if chflagsat exists */
+#define HAVE_CHFLAGSAT 1
+
+/* Define if connectat exists */
+#define HAVE_CONNECTAT 1
+
+/* Define if faccessat exists */
+#define HAVE_FACCESSAT 1
+
+/* Define if fchflags exists */
+#define HAVE_FCHFLAGS 1
+
+/* Define if fchmodat exists */
+#define HAVE_FCHMODAT 1
+
+/* Define if fchownat exists */
+#define HAVE_FCHOWNAT 1
+
+/* Define if fstatat exists */
+#define HAVE_FSTATAT 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define if lchflags exists */
+#define HAVE_LCHFLAGS 1
+
+/* Define if lchmod exists */
+#define HAVE_LCHMOD 1
+
+/* Define if linkat exists */
+#define HAVE_LINKAT 1
+
+/* Define if lpathconf exists */
+#define HAVE_LPATHCONF 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define if mkdirat exists */
+#define HAVE_MKDIRAT 1
+
+/* Define if mkfifoat exists */
+#define HAVE_MKFIFOAT 1
+
+/* Define if mknodat exists */
+#define HAVE_MKNODAT 1
+
+/* Define if openat exists */
+#define HAVE_OPENAT 1
+
+/* Define if posix_fallocate exists */
+#define HAVE_POSIX_FALLOCATE 1
+
+/* Define if readlinkat exists */
+#define HAVE_READLINKAT 1
+
+/* Define if renameat exists */
+#define HAVE_RENAMEAT 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if `st_atim' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_ATIM 1
+
+/* Define to 1 if `st_atimespec' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_ATIMESPEC 1
+
+/* Define to 1 if `st_birthtim' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_BIRTHTIM 1
+
+/* Define to 1 if `st_birthtime' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_BIRTHTIME 1
+
+/* Define to 1 if `st_birthtimespec' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC 1
+
+/* Define to 1 if `st_ctim' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_CTIM 1
+
+/* Define to 1 if `st_ctimespec' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_CTIMESPEC 1
+
+/* Define to 1 if `st_mtim' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_MTIM 1
+
+/* Define to 1 if `st_mtimespec' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_MTIMESPEC 1
+
+/* Define if symlinkat exists */
+#define HAVE_SYMLINKAT 1
+
+/* Define to 1 if sys/acl.h is available */
+#define HAVE_SYS_ACL_H 1
+
+/* Define to 1 if you have the <sys/mkdev.h> header file. */
+/* #undef HAVE_SYS_MKDEV_H */
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define if utimensat exists */
+#define HAVE_UTIMENSAT 1
+
+/* Name of package */
+#define PACKAGE "pjdfstest"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "pjdfstest"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "pjdfstest 0.1"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "pjdfstest"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "0.1"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Enable extensions on AIX 3, Interix. */
+#ifndef _ALL_SOURCE
+# define _ALL_SOURCE 1
+#endif
+/* Enable GNU extensions on systems that have them. */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE 1
+#endif
+/* Enable threading extensions on Solaris. */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# define _POSIX_PTHREAD_SEMANTICS 1
+#endif
+/* Enable extensions on HP NonStop. */
+#ifndef _TANDEM_SOURCE
+# define _TANDEM_SOURCE 1
+#endif
+/* Enable general extensions on Solaris. */
+#ifndef __EXTENSIONS__
+# define __EXTENSIONS__ 1
+#endif
+
+
+/* Version number of package */
+#define VERSION "0.1"
+
+/* Define to 1 if on MINIX. */
+/* #undef _MINIX */
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+ this defined. */
+/* #undef _POSIX_1_SOURCE */
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+/* #undef _POSIX_SOURCE */
diff --git a/tests/sys/pjdfstest/pjdfstest/Makefile b/tests/sys/pjdfstest/pjdfstest/Makefile
new file mode 100644
index 000000000000..95e6d953d146
--- /dev/null
+++ b/tests/sys/pjdfstest/pjdfstest/Makefile
@@ -0,0 +1,11 @@
+PACKAGE= tests
+
+.PATH: ${SRCTOP}/contrib/pjdfstest
+
+BINDIR= ${TESTSBASE}/sys/pjdfstest
+PROG= pjdfstest
+MAN=
+
+CFLAGS+= -I${.CURDIR:H}
+
+.include <bsd.prog.mk>
diff --git a/tests/sys/pjdfstest/pjdfstest/Makefile.depend b/tests/sys/pjdfstest/pjdfstest/Makefile.depend
new file mode 100644
index 000000000000..84b8ddd67e34
--- /dev/null
+++ b/tests/sys/pjdfstest/pjdfstest/Makefile.depend
@@ -0,0 +1,16 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/pjdfstest/tests/Makefile b/tests/sys/pjdfstest/tests/Makefile
new file mode 100644
index 000000000000..abe854eb8c35
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/Makefile
@@ -0,0 +1,38 @@
+PACKAGE= tests
+PJDFSTEST_SRCDIR= ${SRCTOP}/contrib/pjdfstest
+
+TESTSDIR= ${TESTSBASE}/sys/pjdfstest
+
+.PATH: ${.CURDIR}
+
+${PACKAGE}FILES+= conf
+${PACKAGE}FILES+= misc.sh
+CLEANFILES+= misc.sh misc.sh.tmp
+
+misc.sh: ${PJDFSTEST_SRCDIR}/tests/misc.sh
+ sed -e '\,maindir=,s,maindir=.*,maindir=`dirname $$0`/../,' < ${.ALLSRC} > ${.TARGET}.tmp
+ mv ${.TARGET}.tmp ${.TARGET}
+
+afterinstall: install-tests-symlink
+install-tests-symlink: .PHONY
+ rm -f ${DESTDIR}${TESTSDIR}/tests
+ ${INSTALL_SYMLINK} -T "package=tests" . ${DESTDIR}${TESTSDIR}/tests
+
+TESTS_SUBDIRS= chflags
+TESTS_SUBDIRS+= chmod
+TESTS_SUBDIRS+= chown
+TESTS_SUBDIRS+= ftruncate
+TESTS_SUBDIRS+= granular
+TESTS_SUBDIRS+= link
+TESTS_SUBDIRS+= mkdir
+TESTS_SUBDIRS+= mkfifo
+TESTS_SUBDIRS+= mknod
+TESTS_SUBDIRS+= open
+TESTS_SUBDIRS+= rename
+TESTS_SUBDIRS+= rmdir
+TESTS_SUBDIRS+= symlink
+TESTS_SUBDIRS+= truncate
+TESTS_SUBDIRS+= unlink
+TESTS_SUBDIRS+= utimensat
+
+.include <bsd.test.mk>
diff --git a/tests/sys/pjdfstest/tests/Makefile.depend b/tests/sys/pjdfstest/tests/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/pjdfstest/tests/Makefile.inc b/tests/sys/pjdfstest/tests/Makefile.inc
new file mode 100644
index 000000000000..01b5f23410c8
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/Makefile.inc
@@ -0,0 +1 @@
+.include "../Makefile.inc"
diff --git a/tests/sys/pjdfstest/tests/chflags/Makefile b/tests/sys/pjdfstest/tests/chflags/Makefile
new file mode 100644
index 000000000000..e0dd497b6807
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/chflags/Makefile
@@ -0,0 +1,16 @@
+TAP_TESTS_SH= 00
+TAP_TESTS_SH+= 01
+TAP_TESTS_SH+= 02
+TAP_TESTS_SH+= 03
+TAP_TESTS_SH+= 04
+TAP_TESTS_SH+= 05
+TAP_TESTS_SH+= 06
+TAP_TESTS_SH+= 07
+TAP_TESTS_SH+= 08
+TAP_TESTS_SH+= 09
+TAP_TESTS_SH+= 10
+TAP_TESTS_SH+= 11
+TAP_TESTS_SH+= 12
+TAP_TESTS_SH+= 13
+
+.include "../pjdfstest.test.mk"
diff --git a/tests/sys/pjdfstest/tests/chflags/Makefile.depend b/tests/sys/pjdfstest/tests/chflags/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/chflags/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/pjdfstest/tests/chmod/Makefile b/tests/sys/pjdfstest/tests/chmod/Makefile
new file mode 100644
index 000000000000..441bec65a357
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/chmod/Makefile
@@ -0,0 +1,15 @@
+TAP_TESTS_SH= 00
+TAP_TESTS_SH+= 01
+TAP_TESTS_SH+= 02
+TAP_TESTS_SH+= 03
+TAP_TESTS_SH+= 04
+TAP_TESTS_SH+= 05
+TAP_TESTS_SH+= 06
+TAP_TESTS_SH+= 07
+TAP_TESTS_SH+= 08
+TAP_TESTS_SH+= 09
+TAP_TESTS_SH+= 10
+TAP_TESTS_SH+= 11
+TAP_TESTS_SH+= 12
+
+.include "../pjdfstest.test.mk"
diff --git a/tests/sys/pjdfstest/tests/chmod/Makefile.depend b/tests/sys/pjdfstest/tests/chmod/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/chmod/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/pjdfstest/tests/chown/Makefile b/tests/sys/pjdfstest/tests/chown/Makefile
new file mode 100644
index 000000000000..8138a674f052
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/chown/Makefile
@@ -0,0 +1,13 @@
+TAP_TESTS_SH= 00
+TAP_TESTS_SH+= 01
+TAP_TESTS_SH+= 02
+TAP_TESTS_SH+= 03
+TAP_TESTS_SH+= 04
+TAP_TESTS_SH+= 05
+TAP_TESTS_SH+= 06
+TAP_TESTS_SH+= 07
+TAP_TESTS_SH+= 08
+TAP_TESTS_SH+= 09
+TAP_TESTS_SH+= 10
+
+.include "../pjdfstest.test.mk"
diff --git a/tests/sys/pjdfstest/tests/chown/Makefile.depend b/tests/sys/pjdfstest/tests/chown/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/chown/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/pjdfstest/tests/conf b/tests/sys/pjdfstest/tests/conf
new file mode 100644
index 000000000000..87793b478e94
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/conf
@@ -0,0 +1,7 @@
+#!/bin/sh
+#
+
+GREP=grep
+mountpoint=$(df . | tail -1 | awk '{print $6}')
+fs=$(mount -p | awk '$2 == "'$mountpoint'" { print toupper($3) }')
+os=FreeBSD
diff --git a/tests/sys/pjdfstest/tests/ftruncate/Makefile b/tests/sys/pjdfstest/tests/ftruncate/Makefile
new file mode 100644
index 000000000000..30aeb30cb525
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/ftruncate/Makefile
@@ -0,0 +1,17 @@
+TAP_TESTS_SH= 00
+TAP_TESTS_SH+= 01
+TAP_TESTS_SH+= 02
+TAP_TESTS_SH+= 03
+TAP_TESTS_SH+= 04
+TAP_TESTS_SH+= 05
+TAP_TESTS_SH+= 06
+TAP_TESTS_SH+= 07
+TAP_TESTS_SH+= 08
+TAP_TESTS_SH+= 09
+TAP_TESTS_SH+= 10
+TAP_TESTS_SH+= 11
+TAP_TESTS_SH+= 12
+TAP_TESTS_SH+= 13
+TAP_TESTS_SH+= 14
+
+.include "../pjdfstest.test.mk"
diff --git a/tests/sys/pjdfstest/tests/ftruncate/Makefile.depend b/tests/sys/pjdfstest/tests/ftruncate/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/ftruncate/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/pjdfstest/tests/granular/Makefile b/tests/sys/pjdfstest/tests/granular/Makefile
new file mode 100644
index 000000000000..28ff001d6958
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/granular/Makefile
@@ -0,0 +1,8 @@
+TAP_TESTS_SH= 00
+TAP_TESTS_SH+= 01
+TAP_TESTS_SH+= 02
+TAP_TESTS_SH+= 03
+TAP_TESTS_SH+= 04
+TAP_TESTS_SH+= 05
+
+.include "../pjdfstest.test.mk"
diff --git a/tests/sys/pjdfstest/tests/granular/Makefile.depend b/tests/sys/pjdfstest/tests/granular/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/granular/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/pjdfstest/tests/link/Makefile b/tests/sys/pjdfstest/tests/link/Makefile
new file mode 100644
index 000000000000..a94c3281a01f
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/link/Makefile
@@ -0,0 +1,20 @@
+TAP_TESTS_SH= 00
+TAP_TESTS_SH+= 01
+TAP_TESTS_SH+= 02
+TAP_TESTS_SH+= 03
+TAP_TESTS_SH+= 04
+TAP_TESTS_SH+= 05
+TAP_TESTS_SH+= 06
+TAP_TESTS_SH+= 07
+TAP_TESTS_SH+= 08
+TAP_TESTS_SH+= 09
+TAP_TESTS_SH+= 10
+TAP_TESTS_SH+= 11
+TAP_TESTS_SH+= 12
+TAP_TESTS_SH+= 13
+TAP_TESTS_SH+= 14
+TAP_TESTS_SH+= 15
+TAP_TESTS_SH+= 16
+TAP_TESTS_SH+= 17
+
+.include "../pjdfstest.test.mk"
diff --git a/tests/sys/pjdfstest/tests/link/Makefile.depend b/tests/sys/pjdfstest/tests/link/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/link/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/pjdfstest/tests/mkdir/Makefile b/tests/sys/pjdfstest/tests/mkdir/Makefile
new file mode 100644
index 000000000000..441bec65a357
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/mkdir/Makefile
@@ -0,0 +1,15 @@
+TAP_TESTS_SH= 00
+TAP_TESTS_SH+= 01
+TAP_TESTS_SH+= 02
+TAP_TESTS_SH+= 03
+TAP_TESTS_SH+= 04
+TAP_TESTS_SH+= 05
+TAP_TESTS_SH+= 06
+TAP_TESTS_SH+= 07
+TAP_TESTS_SH+= 08
+TAP_TESTS_SH+= 09
+TAP_TESTS_SH+= 10
+TAP_TESTS_SH+= 11
+TAP_TESTS_SH+= 12
+
+.include "../pjdfstest.test.mk"
diff --git a/tests/sys/pjdfstest/tests/mkdir/Makefile.depend b/tests/sys/pjdfstest/tests/mkdir/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/mkdir/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/pjdfstest/tests/mkfifo/Makefile b/tests/sys/pjdfstest/tests/mkfifo/Makefile
new file mode 100644
index 000000000000..441bec65a357
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/mkfifo/Makefile
@@ -0,0 +1,15 @@
+TAP_TESTS_SH= 00
+TAP_TESTS_SH+= 01
+TAP_TESTS_SH+= 02
+TAP_TESTS_SH+= 03
+TAP_TESTS_SH+= 04
+TAP_TESTS_SH+= 05
+TAP_TESTS_SH+= 06
+TAP_TESTS_SH+= 07
+TAP_TESTS_SH+= 08
+TAP_TESTS_SH+= 09
+TAP_TESTS_SH+= 10
+TAP_TESTS_SH+= 11
+TAP_TESTS_SH+= 12
+
+.include "../pjdfstest.test.mk"
diff --git a/tests/sys/pjdfstest/tests/mkfifo/Makefile.depend b/tests/sys/pjdfstest/tests/mkfifo/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/mkfifo/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/pjdfstest/tests/mknod/Makefile b/tests/sys/pjdfstest/tests/mknod/Makefile
new file mode 100644
index 000000000000..03f178034e85
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/mknod/Makefile
@@ -0,0 +1,14 @@
+TAP_TESTS_SH= 00
+TAP_TESTS_SH+= 01
+TAP_TESTS_SH+= 02
+TAP_TESTS_SH+= 03
+TAP_TESTS_SH+= 04
+TAP_TESTS_SH+= 05
+TAP_TESTS_SH+= 06
+TAP_TESTS_SH+= 07
+TAP_TESTS_SH+= 08
+TAP_TESTS_SH+= 09
+TAP_TESTS_SH+= 10
+TAP_TESTS_SH+= 11
+
+.include "../pjdfstest.test.mk"
diff --git a/tests/sys/pjdfstest/tests/mknod/Makefile.depend b/tests/sys/pjdfstest/tests/mknod/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/mknod/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/pjdfstest/tests/open/Makefile b/tests/sys/pjdfstest/tests/open/Makefile
new file mode 100644
index 000000000000..12f54ba412d9
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/open/Makefile
@@ -0,0 +1,27 @@
+TAP_TESTS_SH= 00
+TAP_TESTS_SH+= 01
+TAP_TESTS_SH+= 02
+TAP_TESTS_SH+= 03
+TAP_TESTS_SH+= 04
+TAP_TESTS_SH+= 05
+TAP_TESTS_SH+= 06
+TAP_TESTS_SH+= 07
+TAP_TESTS_SH+= 08
+TAP_TESTS_SH+= 09
+TAP_TESTS_SH+= 10
+TAP_TESTS_SH+= 11
+TAP_TESTS_SH+= 12
+TAP_TESTS_SH+= 13
+TAP_TESTS_SH+= 14
+TAP_TESTS_SH+= 15
+TAP_TESTS_SH+= 16
+TAP_TESTS_SH+= 17
+TAP_TESTS_SH+= 18
+TAP_TESTS_SH+= 19
+TAP_TESTS_SH+= 20
+TAP_TESTS_SH+= 21
+TAP_TESTS_SH+= 22
+TAP_TESTS_SH+= 23
+TAP_TESTS_SH+= 24
+
+.include "../pjdfstest.test.mk"
diff --git a/tests/sys/pjdfstest/tests/open/Makefile.depend b/tests/sys/pjdfstest/tests/open/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/open/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/pjdfstest/tests/pjdfstest.test.mk b/tests/sys/pjdfstest/tests/pjdfstest.test.mk
new file mode 100644
index 000000000000..8ddb11b0e272
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/pjdfstest.test.mk
@@ -0,0 +1,13 @@
+
+PJDFSTEST_SRCDIR= ${.CURDIR:H:H:H:H:H}/contrib/pjdfstest
+
+.PATH: ${PJDFSTEST_SRCDIR}/tests/${.CURDIR:T}
+
+TESTSDIR?= ${TESTSBASE}/sys/pjdfstest/${.CURDIR:T}
+
+.for s in ${TAP_TESTS_SH}
+TAP_TESTS_SH_SRC_$s= $s.t
+TEST_METADATA.$s+= required_user="root"
+.endfor
+
+.include <bsd.test.mk>
diff --git a/tests/sys/pjdfstest/tests/rename/Makefile b/tests/sys/pjdfstest/tests/rename/Makefile
new file mode 100644
index 000000000000..b6a4c06c28a4
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/rename/Makefile
@@ -0,0 +1,24 @@
+TAP_TESTS_SH= 00
+TAP_TESTS_SH+= 01
+TAP_TESTS_SH+= 02
+TAP_TESTS_SH+= 03
+TAP_TESTS_SH+= 04
+TAP_TESTS_SH+= 05
+TAP_TESTS_SH+= 06
+TAP_TESTS_SH+= 07
+TAP_TESTS_SH+= 08
+TAP_TESTS_SH+= 09
+TAP_TESTS_SH+= 10
+TAP_TESTS_SH+= 11
+TAP_TESTS_SH+= 12
+TAP_TESTS_SH+= 13
+TAP_TESTS_SH+= 14
+TAP_TESTS_SH+= 15
+TAP_TESTS_SH+= 16
+TAP_TESTS_SH+= 17
+TAP_TESTS_SH+= 18
+TAP_TESTS_SH+= 19
+TAP_TESTS_SH+= 20
+TAP_TESTS_SH+= 21
+
+.include "../pjdfstest.test.mk"
diff --git a/tests/sys/pjdfstest/tests/rename/Makefile.depend b/tests/sys/pjdfstest/tests/rename/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/rename/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/pjdfstest/tests/rmdir/Makefile b/tests/sys/pjdfstest/tests/rmdir/Makefile
new file mode 100644
index 000000000000..974f7f741825
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/rmdir/Makefile
@@ -0,0 +1,18 @@
+TAP_TESTS_SH= 00
+TAP_TESTS_SH+= 01
+TAP_TESTS_SH+= 02
+TAP_TESTS_SH+= 03
+TAP_TESTS_SH+= 04
+TAP_TESTS_SH+= 05
+TAP_TESTS_SH+= 06
+TAP_TESTS_SH+= 07
+TAP_TESTS_SH+= 08
+TAP_TESTS_SH+= 09
+TAP_TESTS_SH+= 10
+TAP_TESTS_SH+= 11
+TAP_TESTS_SH+= 12
+TAP_TESTS_SH+= 13
+TAP_TESTS_SH+= 14
+TAP_TESTS_SH+= 15
+
+.include "../pjdfstest.test.mk"
diff --git a/tests/sys/pjdfstest/tests/rmdir/Makefile.depend b/tests/sys/pjdfstest/tests/rmdir/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/rmdir/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/pjdfstest/tests/symlink/Makefile b/tests/sys/pjdfstest/tests/symlink/Makefile
new file mode 100644
index 000000000000..441bec65a357
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/symlink/Makefile
@@ -0,0 +1,15 @@
+TAP_TESTS_SH= 00
+TAP_TESTS_SH+= 01
+TAP_TESTS_SH+= 02
+TAP_TESTS_SH+= 03
+TAP_TESTS_SH+= 04
+TAP_TESTS_SH+= 05
+TAP_TESTS_SH+= 06
+TAP_TESTS_SH+= 07
+TAP_TESTS_SH+= 08
+TAP_TESTS_SH+= 09
+TAP_TESTS_SH+= 10
+TAP_TESTS_SH+= 11
+TAP_TESTS_SH+= 12
+
+.include "../pjdfstest.test.mk"
diff --git a/tests/sys/pjdfstest/tests/symlink/Makefile.depend b/tests/sys/pjdfstest/tests/symlink/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/symlink/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/pjdfstest/tests/truncate/Makefile b/tests/sys/pjdfstest/tests/truncate/Makefile
new file mode 100644
index 000000000000..30aeb30cb525
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/truncate/Makefile
@@ -0,0 +1,17 @@
+TAP_TESTS_SH= 00
+TAP_TESTS_SH+= 01
+TAP_TESTS_SH+= 02
+TAP_TESTS_SH+= 03
+TAP_TESTS_SH+= 04
+TAP_TESTS_SH+= 05
+TAP_TESTS_SH+= 06
+TAP_TESTS_SH+= 07
+TAP_TESTS_SH+= 08
+TAP_TESTS_SH+= 09
+TAP_TESTS_SH+= 10
+TAP_TESTS_SH+= 11
+TAP_TESTS_SH+= 12
+TAP_TESTS_SH+= 13
+TAP_TESTS_SH+= 14
+
+.include "../pjdfstest.test.mk"
diff --git a/tests/sys/pjdfstest/tests/truncate/Makefile.depend b/tests/sys/pjdfstest/tests/truncate/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/truncate/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/pjdfstest/tests/unlink/Makefile b/tests/sys/pjdfstest/tests/unlink/Makefile
new file mode 100644
index 000000000000..e0dd497b6807
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/unlink/Makefile
@@ -0,0 +1,16 @@
+TAP_TESTS_SH= 00
+TAP_TESTS_SH+= 01
+TAP_TESTS_SH+= 02
+TAP_TESTS_SH+= 03
+TAP_TESTS_SH+= 04
+TAP_TESTS_SH+= 05
+TAP_TESTS_SH+= 06
+TAP_TESTS_SH+= 07
+TAP_TESTS_SH+= 08
+TAP_TESTS_SH+= 09
+TAP_TESTS_SH+= 10
+TAP_TESTS_SH+= 11
+TAP_TESTS_SH+= 12
+TAP_TESTS_SH+= 13
+
+.include "../pjdfstest.test.mk"
diff --git a/tests/sys/pjdfstest/tests/unlink/Makefile.depend b/tests/sys/pjdfstest/tests/unlink/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/unlink/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/pjdfstest/tests/utimensat/Makefile b/tests/sys/pjdfstest/tests/utimensat/Makefile
new file mode 100644
index 000000000000..16b90be6d6d8
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/utimensat/Makefile
@@ -0,0 +1,12 @@
+TAP_TESTS_SH= 00
+TAP_TESTS_SH+= 01
+TAP_TESTS_SH+= 02
+TAP_TESTS_SH+= 03
+TAP_TESTS_SH+= 04
+TAP_TESTS_SH+= 05
+TAP_TESTS_SH+= 06
+TAP_TESTS_SH+= 07
+TAP_TESTS_SH+= 08
+TAP_TESTS_SH+= 09
+
+.include "../pjdfstest.test.mk"
diff --git a/tests/sys/pjdfstest/tests/utimensat/Makefile.depend b/tests/sys/pjdfstest/tests/utimensat/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/tests/sys/pjdfstest/tests/utimensat/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/posixshm/Makefile b/tests/sys/posixshm/Makefile
new file mode 100644
index 000000000000..9b87860fba58
--- /dev/null
+++ b/tests/sys/posixshm/Makefile
@@ -0,0 +1,8 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/posixshm
+
+ATF_TESTS_C+= memfd_test
+ATF_TESTS_C+= posixshm_test
+
+.include <bsd.test.mk>
diff --git a/tests/sys/posixshm/Makefile.depend b/tests/sys/posixshm/Makefile.depend
new file mode 100644
index 000000000000..1af0c88e099c
--- /dev/null
+++ b/tests/sys/posixshm/Makefile.depend
@@ -0,0 +1,17 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/posixshm/memfd_test.c b/tests/sys/posixshm/memfd_test.c
new file mode 100644
index 000000000000..5cae184206b1
--- /dev/null
+++ b/tests/sys/posixshm/memfd_test.c
@@ -0,0 +1,293 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <unistd.h>
+
+ATF_TC_WITHOUT_HEAD(basic);
+ATF_TC_BODY(basic, tc)
+{
+ struct stat sb;
+ int fd;
+ char buf[8];
+
+ ATF_REQUIRE((fd = memfd_create("...", 0)) != -1);
+
+ /* write(2) should grow us out automatically. */
+ ATF_REQUIRE(write(fd, buf, sizeof(buf)) == sizeof(buf));
+ ATF_REQUIRE(fstat(fd, &sb) == 0);
+ ATF_REQUIRE(sb.st_size == sizeof(buf));
+
+ /* ftruncate(2) must succeed without seals */
+ ATF_REQUIRE(ftruncate(fd, 2 * (sizeof(buf) - 1)) == 0);
+
+ /* write(2) again must not be limited by ftruncate(2) size. */
+ ATF_REQUIRE(write(fd, buf, sizeof(buf)) == sizeof(buf));
+
+ /* Sanity check. */
+ ATF_REQUIRE(fstat(fd, &sb) == 0);
+ ATF_REQUIRE(sb.st_size == 2 * sizeof(buf));
+
+ close(fd);
+}
+
+ATF_TC_WITHOUT_HEAD(cloexec);
+ATF_TC_BODY(cloexec, tc)
+{
+ int fd_nocl, fd_cl;
+
+ ATF_REQUIRE((fd_nocl = memfd_create("...", 0)) != -1);
+ ATF_REQUIRE((fd_cl = memfd_create("...", MFD_CLOEXEC)) != -1);
+
+ ATF_REQUIRE((fcntl(fd_nocl, F_GETFD) & FD_CLOEXEC) == 0);
+ ATF_REQUIRE((fcntl(fd_cl, F_GETFD) & FD_CLOEXEC) != 0);
+
+ close(fd_nocl);
+ close(fd_cl);
+}
+
+ATF_TC_WITHOUT_HEAD(disallowed_sealing);
+ATF_TC_BODY(disallowed_sealing, tc)
+{
+ int fd;
+
+ ATF_REQUIRE((fd = memfd_create("...", 0)) != -1);
+ ATF_REQUIRE(fcntl(fd, F_GET_SEALS) == F_SEAL_SEAL);
+ ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == -1);
+ ATF_REQUIRE(errno == EPERM);
+
+ close(fd);
+}
+
+#define BUF_SIZE 1024
+
+ATF_TC_WITHOUT_HEAD(write_seal);
+ATF_TC_BODY(write_seal, tc)
+{
+ int fd;
+ char *addr, buf[BUF_SIZE];
+
+ ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1);
+ ATF_REQUIRE(ftruncate(fd, BUF_SIZE) == 0);
+
+ /* Write once, then we'll seal it and try again */
+ ATF_REQUIRE(write(fd, buf, BUF_SIZE) == BUF_SIZE);
+ ATF_REQUIRE(lseek(fd, 0, SEEK_SET) == 0);
+
+ addr = mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_PRIVATE, fd, 0);
+ ATF_REQUIRE(addr != MAP_FAILED);
+ ATF_REQUIRE(munmap(addr, BUF_SIZE) == 0);
+
+ ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == 0);
+
+ ATF_REQUIRE(write(fd, buf, BUF_SIZE) == -1);
+ ATF_REQUIRE(errno == EPERM);
+
+ ATF_REQUIRE(mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_SHARED,
+ fd, 0) == MAP_FAILED);
+ ATF_REQUIRE(errno == EACCES);
+
+ close(fd);
+}
+
+ATF_TC_WITHOUT_HEAD(mmap_write_seal);
+ATF_TC_BODY(mmap_write_seal, tc)
+{
+ int fd;
+ char *addr, *paddr, *raddr;
+
+ ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1);
+ ATF_REQUIRE(ftruncate(fd, BUF_SIZE) == 0);
+
+ /* Map it, both shared and privately */
+ addr = mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, 0);
+ ATF_REQUIRE(addr != MAP_FAILED);
+ paddr = mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_PRIVATE, fd, 0);
+ ATF_REQUIRE(paddr != MAP_FAILED);
+ raddr = mmap(0, BUF_SIZE, PROT_READ, MAP_SHARED, fd, 0);
+ ATF_REQUIRE(raddr != MAP_FAILED);
+
+ /* Now try to seal it before unmapping */
+ ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == -1);
+ ATF_REQUIRE(errno == EBUSY);
+
+ ATF_REQUIRE(munmap(addr, BUF_SIZE) == 0);
+
+ /*
+ * This should fail, because raddr still exists and it was spawned from
+ * a r/w fd.
+ */
+ ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == -1);
+ ATF_REQUIRE(errno == EBUSY);
+
+ ATF_REQUIRE(munmap(raddr, BUF_SIZE) == 0);
+ /* This one should succeed; only the private mapping remains. */
+ ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == 0);
+
+ ATF_REQUIRE(munmap(paddr, BUF_SIZE) == 0);
+ ATF_REQUIRE(mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_SHARED,
+ fd, 0) == MAP_FAILED);
+ ATF_REQUIRE(errno == EACCES);
+
+ /* Make sure we can still map privately r/w or shared r/o. */
+ paddr = mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_PRIVATE, fd, 0);
+ ATF_REQUIRE(paddr != MAP_FAILED);
+ raddr = mmap(0, BUF_SIZE, PROT_READ, MAP_SHARED, fd, 0);
+ ATF_REQUIRE(raddr != MAP_FAILED);
+ ATF_REQUIRE(munmap(raddr, BUF_SIZE) == 0);
+ ATF_REQUIRE(munmap(paddr, BUF_SIZE) == 0);
+
+ close(fd);
+}
+
+static int
+memfd_truncate_test(int initial_size, int dest_size, int seals)
+{
+ int err, fd;
+
+ ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1);
+ ATF_REQUIRE(ftruncate(fd, initial_size) == 0);
+
+ ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, seals) == 0);
+
+ err = ftruncate(fd, dest_size);
+ if (err != 0)
+ err = errno;
+ close(fd);
+ return (err);
+}
+
+ATF_TC_WITHOUT_HEAD(truncate_seals);
+ATF_TC_BODY(truncate_seals, tc)
+{
+
+ ATF_REQUIRE(memfd_truncate_test(4, 8, F_SEAL_GROW) == EPERM);
+ ATF_REQUIRE(memfd_truncate_test(8, 4, F_SEAL_SHRINK) == EPERM);
+ ATF_REQUIRE(memfd_truncate_test(8, 4, F_SEAL_GROW) == 0);
+ ATF_REQUIRE(memfd_truncate_test(4, 8, F_SEAL_SHRINK) == 0);
+
+ ATF_REQUIRE(memfd_truncate_test(4, 8, F_SEAL_GROW | F_SEAL_SHRINK) ==
+ EPERM);
+ ATF_REQUIRE(memfd_truncate_test(8, 4, F_SEAL_GROW | F_SEAL_SHRINK) ==
+ EPERM);
+ ATF_REQUIRE(memfd_truncate_test(4, 4, F_SEAL_GROW | F_SEAL_SHRINK) ==
+ 0);
+}
+
+ATF_TC_WITHOUT_HEAD(get_seals);
+ATF_TC_BODY(get_seals, tc)
+{
+ int fd;
+ int seals;
+
+ ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1);
+ ATF_REQUIRE(fcntl(fd, F_GET_SEALS) == 0);
+
+ ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE | F_SEAL_GROW) == 0);
+ seals = fcntl(fd, F_GET_SEALS);
+ ATF_REQUIRE(seals == (F_SEAL_WRITE | F_SEAL_GROW));
+
+ close(fd);
+}
+
+ATF_TC_WITHOUT_HEAD(dup_seals);
+ATF_TC_BODY(dup_seals, tc)
+{
+ char buf[8];
+ int fd, fdx;
+ int seals;
+
+ ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1);
+ ATF_REQUIRE((fdx = dup(fd)) != -1);
+ ATF_REQUIRE(fcntl(fd, F_GET_SEALS) == 0);
+
+ ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE | F_SEAL_GROW) == 0);
+ seals = fcntl(fd, F_GET_SEALS);
+ ATF_REQUIRE(seals == (F_SEAL_WRITE | F_SEAL_GROW));
+
+ seals = fcntl(fdx, F_GET_SEALS);
+ ATF_REQUIRE(seals == (F_SEAL_WRITE | F_SEAL_GROW));
+
+ /* Make sure the seal's actually being applied at the inode level */
+ ATF_REQUIRE(write(fdx, buf, sizeof(buf)) == -1);
+ ATF_REQUIRE(errno == EPERM);
+
+ ATF_REQUIRE(mmap(0, BUF_SIZE, (PROT_READ | PROT_WRITE), MAP_SHARED,
+ fdx, 0) == MAP_FAILED);
+ ATF_REQUIRE(errno == EACCES);
+
+ close(fd);
+ close(fdx);
+}
+
+ATF_TC_WITHOUT_HEAD(immutable_seals);
+ATF_TC_BODY(immutable_seals, tc)
+{
+ int fd;
+
+ ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1);
+
+ ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_SEAL) == 0);
+ ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_GROW) == -1);
+ ATF_REQUIRE_MSG(errno == EPERM,
+ "Added unique grow seal after restricting seals");
+
+ close(fd);
+
+ /*
+ * Also check that adding a seal that already exists really doesn't
+ * do anything once we're sealed.
+ */
+ ATF_REQUIRE((fd = memfd_create("...", MFD_ALLOW_SEALING)) != -1);
+
+ ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SEAL) == 0);
+ ATF_REQUIRE(fcntl(fd, F_ADD_SEALS, F_SEAL_GROW) == -1);
+ ATF_REQUIRE_MSG(errno == EPERM,
+ "Added duplicate grow seal after restricting seals");
+ close(fd);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, basic);
+ ATF_TP_ADD_TC(tp, cloexec);
+ ATF_TP_ADD_TC(tp, disallowed_sealing);
+ ATF_TP_ADD_TC(tp, write_seal);
+ ATF_TP_ADD_TC(tp, mmap_write_seal);
+ ATF_TP_ADD_TC(tp, truncate_seals);
+ ATF_TP_ADD_TC(tp, get_seals);
+ ATF_TP_ADD_TC(tp, dup_seals);
+ ATF_TP_ADD_TC(tp, immutable_seals);
+ return (atf_no_error());
+}
diff --git a/tests/sys/posixshm/posixshm_test.c b/tests/sys/posixshm/posixshm_test.c
new file mode 100644
index 000000000000..55514a5f4bde
--- /dev/null
+++ b/tests/sys/posixshm/posixshm_test.c
@@ -0,0 +1,1997 @@
+/*-
+ * Copyright (c) 2006 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Copyright (c) 2021 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Ka Ho Ng
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/sysctl.h>
+#include <sys/wait.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#define TEST_PATH_LEN 256
+static char test_path[TEST_PATH_LEN];
+static char test_path2[TEST_PATH_LEN];
+static unsigned int test_path_idx = 0;
+
+static void
+gen_a_test_path(char *path)
+{
+ snprintf(path, TEST_PATH_LEN, "/%s/tmp.XXXXXX%d",
+ getenv("TMPDIR") == NULL ? "/tmp" : getenv("TMPDIR"),
+ test_path_idx);
+
+ test_path_idx++;
+
+ ATF_REQUIRE_MSG(mkstemp(path) != -1,
+ "mkstemp failed; errno=%d", errno);
+ ATF_REQUIRE_MSG(unlink(path) == 0,
+ "unlink failed; errno=%d", errno);
+}
+
+static void
+gen_test_path(void)
+{
+ gen_a_test_path(test_path);
+}
+
+static void
+gen_test_path2(void)
+{
+ gen_a_test_path(test_path2);
+}
+
+/*
+ * Attempt a shm_open() that should fail with an expected error of 'error'.
+ */
+static void
+shm_open_should_fail(const char *path, int flags, mode_t mode, int error)
+{
+ int fd;
+
+ fd = shm_open(path, flags, mode);
+ ATF_CHECK_MSG(fd == -1, "shm_open didn't fail");
+ ATF_CHECK_MSG(error == errno,
+ "shm_open didn't fail with expected errno; errno=%d; expected "
+ "errno=%d", errno, error);
+}
+
+/*
+ * Attempt a shm_unlink() that should fail with an expected error of 'error'.
+ */
+static void
+shm_unlink_should_fail(const char *path, int error)
+{
+
+ ATF_CHECK_MSG(shm_unlink(path) == -1, "shm_unlink didn't fail");
+ ATF_CHECK_MSG(error == errno,
+ "shm_unlink didn't fail with expected errno; errno=%d; expected "
+ "errno=%d", errno, error);
+}
+
+/*
+ * Open the test object and write a value to the first byte. Returns valid fd
+ * on success and -1 on failure.
+ */
+static int
+scribble_object(const char *path, char value)
+{
+ char *page;
+ int fd, pagesize;
+
+ ATF_REQUIRE(0 < (pagesize = getpagesize()));
+
+ fd = shm_open(path, O_CREAT|O_EXCL|O_RDWR, 0777);
+ if (fd < 0 && errno == EEXIST) {
+ if (shm_unlink(test_path) < 0)
+ atf_tc_fail("shm_unlink");
+ fd = shm_open(test_path, O_CREAT | O_EXCL | O_RDWR, 0777);
+ }
+ if (fd < 0)
+ atf_tc_fail("shm_open failed; errno=%d", errno);
+ if (ftruncate(fd, pagesize) < 0)
+ atf_tc_fail("ftruncate failed; errno=%d", errno);
+
+ page = mmap(0, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (page == MAP_FAILED)
+ atf_tc_fail("mmap failed; errno=%d", errno);
+
+ page[0] = value;
+ ATF_REQUIRE_MSG(munmap(page, pagesize) == 0, "munmap failed; errno=%d",
+ errno);
+
+ return (fd);
+}
+
+/*
+ * Fail the test case if the 'path' does not refer to an shm whose first byte
+ * is equal to expected_value
+ */
+static void
+verify_object(const char *path, char expected_value)
+{
+ int fd;
+ int pagesize;
+ char *page;
+
+ ATF_REQUIRE(0 < (pagesize = getpagesize()));
+
+ fd = shm_open(path, O_RDONLY, 0777);
+ if (fd < 0)
+ atf_tc_fail("shm_open failed in verify_object; errno=%d, path=%s",
+ errno, path);
+
+ page = mmap(0, pagesize, PROT_READ, MAP_SHARED, fd, 0);
+ if (page == MAP_FAILED)
+ atf_tc_fail("mmap(1)");
+ if (page[0] != expected_value)
+ atf_tc_fail("Renamed object has incorrect value; has"
+ "%d (0x%x, '%c'), expected %d (0x%x, '%c')\n",
+ page[0], page[0], isprint(page[0]) ? page[0] : ' ',
+ expected_value, expected_value,
+ isprint(expected_value) ? expected_value : ' ');
+ ATF_REQUIRE_MSG(munmap(page, pagesize) == 0, "munmap failed; errno=%d",
+ errno);
+ close(fd);
+}
+
+static off_t shm_max_pages = 32;
+static const char byte_to_fill = 0x5f;
+
+static int
+shm_fill(int fd, off_t offset, off_t len)
+{
+ int error;
+ size_t blen, page_size;
+ char *buf;
+
+ error = 0;
+ page_size = getpagesize();
+ buf = malloc(page_size);
+ if (buf == NULL)
+ return (1);
+
+ while (len > 0) {
+ blen = len < (off_t)page_size ? (size_t)len : page_size;
+ memset(buf, byte_to_fill, blen);
+ if (pwrite(fd, buf, blen, offset) != (ssize_t)blen) {
+ error = 1;
+ break;
+ }
+ len -= blen;
+ offset += blen;
+ }
+
+ free(buf);
+ return (error);
+}
+
+static int
+check_content_dealloc(int fd, off_t hole_start, off_t hole_len, off_t shm_sz)
+{
+ int error;
+ size_t blen, page_size;
+ off_t offset, resid;
+ struct stat statbuf;
+ char *buf, *sblk;
+
+ error = 0;
+ page_size = getpagesize();
+ buf = malloc(page_size * 2);
+ if (buf == NULL)
+ return (1);
+ sblk = buf + page_size;
+
+ memset(sblk, 0, page_size);
+
+ if ((uint64_t)hole_start + hole_len > (uint64_t)shm_sz)
+ hole_len = shm_sz - hole_start;
+
+ /*
+ * Check hole is zeroed.
+ */
+ offset = hole_start;
+ resid = hole_len;
+ while (resid > 0) {
+ blen = resid < (off_t)page_size ? (size_t)resid : page_size;
+ if (pread(fd, buf, blen, offset) != (ssize_t)blen) {
+ error = 1;
+ break;
+ }
+ if (memcmp(buf, sblk, blen) != 0) {
+ error = 1;
+ break;
+ }
+ resid -= blen;
+ offset += blen;
+ }
+
+ memset(sblk, byte_to_fill, page_size);
+
+ /*
+ * Check file region before hole is zeroed.
+ */
+ offset = 0;
+ resid = hole_start;
+ while (resid > 0) {
+ blen = resid < (off_t)page_size ? (size_t)resid : page_size;
+ if (pread(fd, buf, blen, offset) != (ssize_t)blen) {
+ error = 1;
+ break;
+ }
+ if (memcmp(buf, sblk, blen) != 0) {
+ error = 1;
+ break;
+ }
+ resid -= blen;
+ offset += blen;
+ }
+
+ /*
+ * Check file region after hole is zeroed.
+ */
+ offset = hole_start + hole_len;
+ resid = shm_sz - offset;
+ while (resid > 0) {
+ blen = resid < (off_t)page_size ? (size_t)resid : page_size;
+ if (pread(fd, buf, blen, offset) != (ssize_t)blen) {
+ error = 1;
+ break;
+ }
+ if (memcmp(buf, sblk, blen) != 0) {
+ error = 1;
+ break;
+ }
+ resid -= blen;
+ offset += blen;
+ }
+
+ /*
+ * Check file size matches with expected file size.
+ */
+ if (fstat(fd, &statbuf) == -1)
+ error = -1;
+ if (statbuf.st_size != shm_sz)
+ error = -1;
+
+ free(buf);
+ return (error);
+}
+
+ATF_TC_WITHOUT_HEAD(remap_object);
+ATF_TC_BODY(remap_object, tc)
+{
+ char *page;
+ int fd, pagesize;
+
+ ATF_REQUIRE(0 < (pagesize = getpagesize()));
+
+ gen_test_path();
+ fd = scribble_object(test_path, '1');
+
+ page = mmap(0, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (page == MAP_FAILED)
+ atf_tc_fail("mmap(2) failed; errno=%d", errno);
+
+ if (page[0] != '1')
+ atf_tc_fail("missing data ('%c' != '1')", page[0]);
+
+ close(fd);
+ ATF_REQUIRE_MSG(munmap(page, pagesize) == 0, "munmap failed; errno=%d",
+ errno);
+
+ ATF_REQUIRE_MSG(shm_unlink(test_path) != -1,
+ "shm_unlink failed; errno=%d", errno);
+}
+
+ATF_TC_WITHOUT_HEAD(rename_from_anon);
+ATF_TC_BODY(rename_from_anon, tc)
+{
+ int rc;
+
+ gen_test_path();
+ rc = shm_rename(SHM_ANON, test_path, 0);
+ if (rc != -1)
+ atf_tc_fail("shm_rename from SHM_ANON succeeded unexpectedly");
+}
+
+ATF_TC_WITHOUT_HEAD(rename_bad_path_pointer);
+ATF_TC_BODY(rename_bad_path_pointer, tc)
+{
+ const char *bad_path;
+ int rc;
+
+ bad_path = (const char *)0x1;
+
+ gen_test_path();
+ rc = shm_rename(test_path, bad_path, 0);
+ if (rc != -1)
+ atf_tc_fail("shm_rename of nonexisting shm succeeded unexpectedly");
+
+ rc = shm_rename(bad_path, test_path, 0);
+ if (rc != -1)
+ atf_tc_fail("shm_rename of nonexisting shm succeeded unexpectedly");
+}
+
+ATF_TC_WITHOUT_HEAD(rename_from_nonexisting);
+ATF_TC_BODY(rename_from_nonexisting, tc)
+{
+ int rc;
+
+ gen_test_path();
+ gen_test_path2();
+ rc = shm_rename(test_path, test_path2, 0);
+ if (rc != -1)
+ atf_tc_fail("shm_rename of nonexisting shm succeeded unexpectedly");
+
+ if (errno != ENOENT)
+ atf_tc_fail("Expected ENOENT to rename of nonexistent shm; got %d",
+ errno);
+}
+
+ATF_TC_WITHOUT_HEAD(rename_to_anon);
+ATF_TC_BODY(rename_to_anon, tc)
+{
+ int rc;
+
+ gen_test_path();
+ rc = shm_rename(test_path, SHM_ANON, 0);
+ if (rc != -1)
+ atf_tc_fail("shm_rename to SHM_ANON succeeded unexpectedly");
+}
+
+ATF_TC_WITHOUT_HEAD(rename_to_replace);
+ATF_TC_BODY(rename_to_replace, tc)
+{
+ char expected_value;
+ int fd;
+ int fd2;
+
+ // Some contents we can verify later
+ expected_value = 'g';
+
+ gen_test_path();
+ fd = scribble_object(test_path, expected_value);
+ close(fd);
+
+ // Give the other some different value so we can detect success
+ gen_test_path2();
+ fd2 = scribble_object(test_path2, 'h');
+ close(fd2);
+
+ ATF_REQUIRE_MSG(shm_rename(test_path, test_path2, 0) == 0,
+ "shm_rename failed; errno=%d", errno);
+
+ // Read back renamed; verify contents
+ verify_object(test_path2, expected_value);
+}
+
+ATF_TC_WITHOUT_HEAD(rename_to_noreplace);
+ATF_TC_BODY(rename_to_noreplace, tc)
+{
+ char expected_value_from;
+ char expected_value_to;
+ int fd_from;
+ int fd_to;
+ int rc;
+
+ // Some contents we can verify later
+ expected_value_from = 'g';
+ gen_test_path();
+ fd_from = scribble_object(test_path, expected_value_from);
+ close(fd_from);
+
+ // Give the other some different value so we can detect success
+ expected_value_to = 'h';
+ gen_test_path2();
+ fd_to = scribble_object(test_path2, expected_value_to);
+ close(fd_to);
+
+ rc = shm_rename(test_path, test_path2, SHM_RENAME_NOREPLACE);
+ ATF_REQUIRE_MSG((rc == -1) && (errno == EEXIST),
+ "shm_rename didn't fail as expected; errno: %d; return: %d", errno,
+ rc);
+
+ // Read back renamed; verify contents
+ verify_object(test_path2, expected_value_to);
+}
+
+ATF_TC_WITHOUT_HEAD(rename_to_exchange);
+ATF_TC_BODY(rename_to_exchange, tc)
+{
+ char expected_value_from;
+ char expected_value_to;
+ int fd_from;
+ int fd_to;
+
+ // Some contents we can verify later
+ expected_value_from = 'g';
+ gen_test_path();
+ fd_from = scribble_object(test_path, expected_value_from);
+ close(fd_from);
+
+ // Give the other some different value so we can detect success
+ expected_value_to = 'h';
+ gen_test_path2();
+ fd_to = scribble_object(test_path2, expected_value_to);
+ close(fd_to);
+
+ ATF_REQUIRE_MSG(shm_rename(test_path, test_path2,
+ SHM_RENAME_EXCHANGE) == 0,
+ "shm_rename failed; errno=%d", errno);
+
+ // Read back renamed; verify contents
+ verify_object(test_path, expected_value_to);
+ verify_object(test_path2, expected_value_from);
+}
+
+ATF_TC_WITHOUT_HEAD(rename_to_exchange_nonexisting);
+ATF_TC_BODY(rename_to_exchange_nonexisting, tc)
+{
+ char expected_value_from;
+ int fd_from;
+
+ // Some contents we can verify later
+ expected_value_from = 'g';
+ gen_test_path();
+ fd_from = scribble_object(test_path, expected_value_from);
+ close(fd_from);
+
+ gen_test_path2();
+
+ ATF_REQUIRE_MSG(shm_rename(test_path, test_path2,
+ SHM_RENAME_EXCHANGE) == 0,
+ "shm_rename failed; errno=%d", errno);
+
+ // Read back renamed; verify contents
+ verify_object(test_path2, expected_value_from);
+}
+
+ATF_TC_WITHOUT_HEAD(rename_to_self);
+ATF_TC_BODY(rename_to_self, tc)
+{
+ int fd;
+ char expected_value;
+
+ expected_value = 't';
+
+ gen_test_path();
+ fd = scribble_object(test_path, expected_value);
+ close(fd);
+
+ ATF_REQUIRE_MSG(shm_rename(test_path, test_path, 0) == 0,
+ "shm_rename failed; errno=%d", errno);
+
+ verify_object(test_path, expected_value);
+}
+
+ATF_TC_WITHOUT_HEAD(rename_bad_flag);
+ATF_TC_BODY(rename_bad_flag, tc)
+{
+ int fd;
+ int rc;
+
+ /* Make sure we don't fail out due to ENOENT */
+ gen_test_path();
+ gen_test_path2();
+ fd = scribble_object(test_path, 'd');
+ close(fd);
+ fd = scribble_object(test_path2, 'd');
+ close(fd);
+
+ /*
+ * Note: if we end up with enough flags that we use all the bits,
+ * then remove this test completely.
+ */
+ rc = shm_rename(test_path, test_path2, INT_MIN);
+ ATF_REQUIRE_MSG((rc == -1) && (errno == EINVAL),
+ "shm_rename should have failed with EINVAL; got: return=%d, "
+ "errno=%d", rc, errno);
+}
+
+ATF_TC_WITHOUT_HEAD(reopen_object);
+ATF_TC_BODY(reopen_object, tc)
+{
+ char *page;
+ int fd, pagesize;
+
+ ATF_REQUIRE(0 < (pagesize = getpagesize()));
+
+ gen_test_path();
+ fd = scribble_object(test_path, '1');
+ close(fd);
+
+ fd = shm_open(test_path, O_RDONLY, 0777);
+ if (fd < 0)
+ atf_tc_fail("shm_open(2) failed; errno=%d", errno);
+
+ page = mmap(0, pagesize, PROT_READ, MAP_SHARED, fd, 0);
+ if (page == MAP_FAILED)
+ atf_tc_fail("mmap(2) failed; errno=%d", errno);
+
+ if (page[0] != '1')
+ atf_tc_fail("missing data ('%c' != '1')", page[0]);
+
+ ATF_REQUIRE_MSG(munmap(page, pagesize) == 0, "munmap failed; errno=%d",
+ errno);
+ close(fd);
+ ATF_REQUIRE_MSG(shm_unlink(test_path) != -1,
+ "shm_unlink failed; errno=%d", errno);
+}
+
+ATF_TC_WITHOUT_HEAD(readonly_mmap_write);
+ATF_TC_BODY(readonly_mmap_write, tc)
+{
+ char *page;
+ int fd, pagesize;
+
+ ATF_REQUIRE(0 < (pagesize = getpagesize()));
+
+ gen_test_path();
+
+ fd = shm_open(test_path, O_RDONLY | O_CREAT, 0777);
+ ATF_REQUIRE_MSG(fd >= 0, "shm_open failed; errno=%d", errno);
+
+ /* PROT_WRITE should fail with EACCES. */
+ page = mmap(0, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (page != MAP_FAILED)
+ atf_tc_fail("mmap(PROT_WRITE) succeeded unexpectedly");
+
+ if (errno != EACCES)
+ atf_tc_fail("mmap(PROT_WRITE) didn't fail with EACCES; "
+ "errno=%d", errno);
+
+ close(fd);
+ ATF_REQUIRE_MSG(shm_unlink(test_path) != -1,
+ "shm_unlink failed; errno=%d", errno);
+}
+
+ATF_TC_WITHOUT_HEAD(open_after_link);
+ATF_TC_BODY(open_after_link, tc)
+{
+ int fd;
+
+ gen_test_path();
+
+ fd = shm_open(test_path, O_RDONLY | O_CREAT, 0777);
+ ATF_REQUIRE_MSG(fd >= 0, "shm_open(1) failed; errno=%d", errno);
+ close(fd);
+
+ ATF_REQUIRE_MSG(shm_unlink(test_path) != -1, "shm_unlink failed: %d",
+ errno);
+
+ shm_open_should_fail(test_path, O_RDONLY, 0777, ENOENT);
+}
+
+ATF_TC_WITHOUT_HEAD(open_invalid_path);
+ATF_TC_BODY(open_invalid_path, tc)
+{
+
+ shm_open_should_fail("blah", O_RDONLY, 0777, EINVAL);
+}
+
+ATF_TC_WITHOUT_HEAD(open_write_only);
+ATF_TC_BODY(open_write_only, tc)
+{
+
+ gen_test_path();
+
+ shm_open_should_fail(test_path, O_WRONLY, 0777, EINVAL);
+}
+
+ATF_TC_WITHOUT_HEAD(open_extra_flags);
+ATF_TC_BODY(open_extra_flags, tc)
+{
+
+ gen_test_path();
+
+ shm_open_should_fail(test_path, O_RDONLY | O_DIRECT, 0777, EINVAL);
+}
+
+ATF_TC_WITHOUT_HEAD(open_anon);
+ATF_TC_BODY(open_anon, tc)
+{
+ int fd;
+
+ fd = shm_open(SHM_ANON, O_RDWR, 0777);
+ ATF_REQUIRE_MSG(fd >= 0, "shm_open failed; errno=%d", errno);
+ close(fd);
+}
+
+ATF_TC_WITHOUT_HEAD(open_anon_readonly);
+ATF_TC_BODY(open_anon_readonly, tc)
+{
+
+ shm_open_should_fail(SHM_ANON, O_RDONLY, 0777, EINVAL);
+}
+
+ATF_TC_WITHOUT_HEAD(open_bad_path_pointer);
+ATF_TC_BODY(open_bad_path_pointer, tc)
+{
+
+ shm_open_should_fail((char *)1024, O_RDONLY, 0777, EFAULT);
+}
+
+ATF_TC_WITHOUT_HEAD(open_path_too_long);
+ATF_TC_BODY(open_path_too_long, tc)
+{
+ char *page;
+
+ page = malloc(MAXPATHLEN + 1);
+ memset(page, 'a', MAXPATHLEN);
+ page[MAXPATHLEN] = '\0';
+ shm_open_should_fail(page, O_RDONLY, 0777, ENAMETOOLONG);
+ free(page);
+}
+
+ATF_TC_WITHOUT_HEAD(open_nonexisting_object);
+ATF_TC_BODY(open_nonexisting_object, tc)
+{
+
+ shm_open_should_fail("/notreallythere", O_RDONLY, 0777, ENOENT);
+}
+
+ATF_TC_WITHOUT_HEAD(open_create_existing_object);
+ATF_TC_BODY(open_create_existing_object, tc)
+{
+ int fd;
+
+ gen_test_path();
+
+ fd = shm_open(test_path, O_RDONLY|O_CREAT, 0777);
+ ATF_REQUIRE_MSG(fd >= 0, "shm_open failed; errno=%d", errno);
+ close(fd);
+
+ shm_open_should_fail(test_path, O_RDONLY|O_CREAT|O_EXCL,
+ 0777, EEXIST);
+
+ ATF_REQUIRE_MSG(shm_unlink(test_path) != -1,
+ "shm_unlink failed; errno=%d", errno);
+}
+
+ATF_TC_WITHOUT_HEAD(trunc_resets_object);
+ATF_TC_BODY(trunc_resets_object, tc)
+{
+ struct stat sb;
+ int fd;
+
+ gen_test_path();
+
+ /* Create object and set size to 1024. */
+ fd = shm_open(test_path, O_RDWR | O_CREAT, 0777);
+ ATF_REQUIRE_MSG(fd >= 0, "shm_open(1) failed; errno=%d", errno);
+ ATF_REQUIRE_MSG(ftruncate(fd, 1024) != -1,
+ "ftruncate failed; errno=%d", errno);
+ ATF_REQUIRE_MSG(fstat(fd, &sb) != -1,
+ "fstat(1) failed; errno=%d", errno);
+ ATF_REQUIRE_MSG(sb.st_size == 1024, "size %d != 1024", (int)sb.st_size);
+ close(fd);
+
+ /* Open with O_TRUNC which should reset size to 0. */
+ fd = shm_open(test_path, O_RDWR | O_TRUNC, 0777);
+ ATF_REQUIRE_MSG(fd >= 0, "shm_open(2) failed; errno=%d", errno);
+ ATF_REQUIRE_MSG(fstat(fd, &sb) != -1,
+ "fstat(2) failed; errno=%d", errno);
+ ATF_REQUIRE_MSG(sb.st_size == 0,
+ "size was not 0 after truncation: %d", (int)sb.st_size);
+ close(fd);
+ ATF_REQUIRE_MSG(shm_unlink(test_path) != -1,
+ "shm_unlink failed; errno=%d", errno);
+}
+
+ATF_TC_WITHOUT_HEAD(unlink_bad_path_pointer);
+ATF_TC_BODY(unlink_bad_path_pointer, tc)
+{
+
+ shm_unlink_should_fail((char *)1024, EFAULT);
+}
+
+ATF_TC_WITHOUT_HEAD(unlink_path_too_long);
+ATF_TC_BODY(unlink_path_too_long, tc)
+{
+ char *page;
+
+ page = malloc(MAXPATHLEN + 1);
+ memset(page, 'a', MAXPATHLEN);
+ page[MAXPATHLEN] = '\0';
+ shm_unlink_should_fail(page, ENAMETOOLONG);
+ free(page);
+}
+
+ATF_TC_WITHOUT_HEAD(object_resize);
+ATF_TC_BODY(object_resize, tc)
+{
+ pid_t pid;
+ struct stat sb;
+ char *page;
+ int fd, pagesize, status;
+
+ ATF_REQUIRE(0 < (pagesize = getpagesize()));
+
+ /* Start off with a size of a single page. */
+ fd = shm_open(SHM_ANON, O_CREAT|O_RDWR, 0777);
+ if (fd < 0)
+ atf_tc_fail("shm_open failed; errno=%d", errno);
+
+ if (ftruncate(fd, pagesize) < 0)
+ atf_tc_fail("ftruncate(1) failed; errno=%d", errno);
+
+ if (fstat(fd, &sb) < 0)
+ atf_tc_fail("fstat(1) failed; errno=%d", errno);
+
+ if (sb.st_size != pagesize)
+ atf_tc_fail("first resize failed (%d != %d)",
+ (int)sb.st_size, pagesize);
+
+ /* Write a '1' to the first byte. */
+ page = mmap(0, pagesize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ if (page == MAP_FAILED)
+ atf_tc_fail("mmap(1)");
+
+ page[0] = '1';
+
+ ATF_REQUIRE_MSG(munmap(page, pagesize) == 0, "munmap failed; errno=%d",
+ errno);
+
+ /* Grow the object to 2 pages. */
+ if (ftruncate(fd, pagesize * 2) < 0)
+ atf_tc_fail("ftruncate(2) failed; errno=%d", errno);
+
+ if (fstat(fd, &sb) < 0)
+ atf_tc_fail("fstat(2) failed; errno=%d", errno);
+
+ if (sb.st_size != pagesize * 2)
+ atf_tc_fail("second resize failed (%d != %d)",
+ (int)sb.st_size, pagesize * 2);
+
+ /* Check for '1' at the first byte. */
+ page = mmap(0, pagesize * 2, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ if (page == MAP_FAILED)
+ atf_tc_fail("mmap(2) failed; errno=%d", errno);
+
+ if (page[0] != '1')
+ atf_tc_fail("'%c' != '1'", page[0]);
+
+ /* Write a '2' at the start of the second page. */
+ page[pagesize] = '2';
+
+ /* Shrink the object back to 1 page. */
+ if (ftruncate(fd, pagesize) < 0)
+ atf_tc_fail("ftruncate(3) failed; errno=%d", errno);
+
+ if (fstat(fd, &sb) < 0)
+ atf_tc_fail("fstat(3) failed; errno=%d", errno);
+
+ if (sb.st_size != pagesize)
+ atf_tc_fail("third resize failed (%d != %d)",
+ (int)sb.st_size, pagesize);
+
+ /*
+ * Fork a child process to make sure the second page is no
+ * longer valid.
+ */
+ pid = fork();
+ if (pid == -1)
+ atf_tc_fail("fork failed; errno=%d", errno);
+
+ if (pid == 0) {
+ struct rlimit lim;
+ char c;
+
+ /* Don't generate a core dump. */
+ ATF_REQUIRE(getrlimit(RLIMIT_CORE, &lim) == 0);
+ lim.rlim_cur = 0;
+ ATF_REQUIRE(setrlimit(RLIMIT_CORE, &lim) == 0);
+
+ /*
+ * The previous ftruncate(2) shrunk the backing object
+ * so that this address is no longer valid, so reading
+ * from it should trigger a SIGBUS.
+ */
+ c = page[pagesize];
+ fprintf(stderr, "child: page 1: '%c'\n", c);
+ exit(0);
+ }
+
+ if (wait(&status) < 0)
+ atf_tc_fail("wait failed; errno=%d", errno);
+
+ if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGBUS)
+ atf_tc_fail("child terminated with status %x", status);
+
+ /* Grow the object back to 2 pages. */
+ if (ftruncate(fd, pagesize * 2) < 0)
+ atf_tc_fail("ftruncate(2) failed; errno=%d", errno);
+
+ if (fstat(fd, &sb) < 0)
+ atf_tc_fail("fstat(2) failed; errno=%d", errno);
+
+ if (sb.st_size != pagesize * 2)
+ atf_tc_fail("fourth resize failed (%d != %d)",
+ (int)sb.st_size, pagesize);
+
+ /*
+ * Note that the mapping at 'page' for the second page is
+ * still valid, and now that the shm object has been grown
+ * back up to 2 pages, there is now memory backing this page
+ * so the read will work. However, the data should be zero
+ * rather than '2' as the old data was thrown away when the
+ * object was shrunk and the new pages when an object are
+ * grown are zero-filled.
+ */
+ if (page[pagesize] != 0)
+ atf_tc_fail("invalid data at %d: %x != 0",
+ pagesize, (int)page[pagesize]);
+
+ close(fd);
+}
+
+/* Signal handler which does nothing. */
+static void
+ignoreit(int sig __unused)
+{
+ ;
+}
+
+ATF_TC_WITHOUT_HEAD(shm_functionality_across_fork);
+ATF_TC_BODY(shm_functionality_across_fork, tc)
+{
+ char *cp, c;
+ int error, desc, rv;
+ long scval;
+ sigset_t ss;
+ struct sigaction sa;
+ void *region;
+ size_t i, psize;
+
+#ifndef _POSIX_SHARED_MEMORY_OBJECTS
+ printf("_POSIX_SHARED_MEMORY_OBJECTS is undefined\n");
+#else
+ printf("_POSIX_SHARED_MEMORY_OBJECTS is defined as %ld\n",
+ (long)_POSIX_SHARED_MEMORY_OBJECTS - 0);
+ if (_POSIX_SHARED_MEMORY_OBJECTS - 0 == -1)
+ printf("***Indicates this feature may be unsupported!\n");
+#endif
+ errno = 0;
+ scval = sysconf(_SC_SHARED_MEMORY_OBJECTS);
+ if (scval == -1 && errno != 0) {
+ atf_tc_fail("sysconf(_SC_SHARED_MEMORY_OBJECTS) failed; "
+ "errno=%d", errno);
+ } else {
+ printf("sysconf(_SC_SHARED_MEMORY_OBJECTS) returns %ld\n",
+ scval);
+ if (scval == -1)
+ printf("***Indicates this feature is unsupported!\n");
+ }
+
+ errno = 0;
+ scval = sysconf(_SC_PAGESIZE);
+ if (scval == -1 && errno != 0) {
+ atf_tc_fail("sysconf(_SC_PAGESIZE) failed; errno=%d", errno);
+ } else if (scval <= 0) {
+ fprintf(stderr, "bogus return from sysconf(_SC_PAGESIZE): %ld",
+ scval);
+ psize = 4096;
+ } else {
+ printf("sysconf(_SC_PAGESIZE) returns %ld\n", scval);
+ psize = scval;
+ }
+
+ gen_test_path();
+ desc = shm_open(test_path, O_EXCL | O_CREAT | O_RDWR, 0600);
+
+ ATF_REQUIRE_MSG(desc >= 0, "shm_open failed; errno=%d", errno);
+ ATF_REQUIRE_MSG(shm_unlink(test_path) == 0,
+ "shm_unlink failed; errno=%d", errno);
+ ATF_REQUIRE_MSG(ftruncate(desc, (off_t)psize) != -1,
+ "ftruncate failed; errno=%d", errno);
+
+ region = mmap(NULL, psize, PROT_READ | PROT_WRITE, MAP_SHARED, desc, 0);
+ ATF_REQUIRE_MSG(region != MAP_FAILED, "mmap failed; errno=%d", errno);
+ memset(region, '\377', psize);
+
+ sa.sa_flags = 0;
+ sa.sa_handler = ignoreit;
+ sigemptyset(&sa.sa_mask);
+ ATF_REQUIRE_MSG(sigaction(SIGUSR1, &sa, (struct sigaction *)0) == 0,
+ "sigaction failed; errno=%d", errno);
+
+ sigemptyset(&ss);
+ sigaddset(&ss, SIGUSR1);
+ ATF_REQUIRE_MSG(sigprocmask(SIG_BLOCK, &ss, (sigset_t *)0) == 0,
+ "sigprocmask failed; errno=%d", errno);
+
+ rv = fork();
+ ATF_REQUIRE_MSG(rv != -1, "fork failed; errno=%d", errno);
+ if (rv == 0) {
+ sigemptyset(&ss);
+ sigsuspend(&ss);
+
+ for (cp = region; cp < (char *)region + psize; cp++) {
+ if (*cp != '\151')
+ _exit(1);
+ }
+ if (lseek(desc, 0, SEEK_SET) == -1)
+ _exit(1);
+ for (i = 0; i < psize; i++) {
+ error = read(desc, &c, 1);
+ if (c != '\151')
+ _exit(1);
+ }
+ _exit(0);
+ } else {
+ int status;
+
+ memset(region, '\151', psize - 2);
+ error = pwrite(desc, region, 2, psize - 2);
+ if (error != 2) {
+ if (error >= 0)
+ atf_tc_fail("short write; %d bytes written",
+ error);
+ else
+ atf_tc_fail("shmfd write");
+ }
+ kill(rv, SIGUSR1);
+ waitpid(rv, &status, 0);
+
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+ printf("Functionality test successful\n");
+ } else if (WIFEXITED(status)) {
+ atf_tc_fail("Child process exited with status %d",
+ WEXITSTATUS(status));
+ } else {
+ atf_tc_fail("Child process terminated with %s",
+ strsignal(WTERMSIG(status)));
+ }
+ }
+
+ ATF_REQUIRE_MSG(munmap(region, psize) == 0, "munmap failed; errno=%d",
+ errno);
+ shm_unlink(test_path);
+}
+
+ATF_TC_WITHOUT_HEAD(cloexec);
+ATF_TC_BODY(cloexec, tc)
+{
+ int fd;
+
+ gen_test_path();
+
+ /* shm_open(2) is required to set FD_CLOEXEC */
+ fd = shm_open(SHM_ANON, O_RDWR, 0777);
+ ATF_REQUIRE_MSG(fd >= 0, "shm_open failed; errno=%d", errno);
+ ATF_REQUIRE((fcntl(fd, F_GETFD) & FD_CLOEXEC) != 0);
+ close(fd);
+
+ /* Also make sure that named shm is correct */
+ fd = shm_open(test_path, O_CREAT | O_RDWR, 0600);
+ ATF_REQUIRE_MSG(fd >= 0, "shm_open failed; errno=%d", errno);
+ ATF_REQUIRE((fcntl(fd, F_GETFD) & FD_CLOEXEC) != 0);
+ close(fd);
+}
+
+ATF_TC_WITHOUT_HEAD(mode);
+ATF_TC_BODY(mode, tc)
+{
+ struct stat st;
+ int fd;
+ mode_t restore_mask;
+
+ gen_test_path();
+
+ /* Remove inhibitions from umask */
+ restore_mask = umask(0);
+ fd = shm_open(test_path, O_CREAT | O_RDWR, 0600);
+ ATF_REQUIRE_MSG(fd >= 0, "shm_open failed; errno=%d", errno);
+ ATF_REQUIRE(fstat(fd, &st) == 0);
+ ATF_REQUIRE((st.st_mode & ACCESSPERMS) == 0600);
+ close(fd);
+ ATF_REQUIRE(shm_unlink(test_path) == 0);
+
+ fd = shm_open(test_path, O_CREAT | O_RDWR, 0660);
+ ATF_REQUIRE_MSG(fd >= 0, "shm_open failed; errno=%d", errno);
+ ATF_REQUIRE(fstat(fd, &st) == 0);
+ ATF_REQUIRE((st.st_mode & ACCESSPERMS) == 0660);
+ close(fd);
+ ATF_REQUIRE(shm_unlink(test_path) == 0);
+
+ fd = shm_open(test_path, O_CREAT | O_RDWR, 0666);
+ ATF_REQUIRE_MSG(fd >= 0, "shm_open failed; errno=%d", errno);
+ ATF_REQUIRE(fstat(fd, &st) == 0);
+ ATF_REQUIRE((st.st_mode & ACCESSPERMS) == 0666);
+ close(fd);
+ ATF_REQUIRE(shm_unlink(test_path) == 0);
+
+ umask(restore_mask);
+}
+
+ATF_TC_WITHOUT_HEAD(fallocate);
+ATF_TC_BODY(fallocate, tc)
+{
+ struct stat st;
+ int error, fd, sz;
+
+ /*
+ * Primitive test case for posix_fallocate with shmd. Effectively
+ * expected to work like a smarter ftruncate that will grow the region
+ * as needed in a race-free way.
+ */
+ fd = shm_open(SHM_ANON, O_RDWR, 0666);
+ ATF_REQUIRE_MSG(fd >= 0, "shm_open failed; errno=%d", errno);
+ /* Set the initial size. */
+ sz = 32;
+ ATF_REQUIRE(ftruncate(fd, sz) == 0);
+
+ /* Now grow it. */
+ error = 0;
+ sz *= 2;
+ ATF_REQUIRE_MSG((error = posix_fallocate(fd, 0, sz)) == 0,
+ "posix_fallocate failed; error=%d", error);
+ ATF_REQUIRE(fstat(fd, &st) == 0);
+ ATF_REQUIRE(st.st_size == sz);
+ /* Attempt to shrink it; should succeed, but not change the size. */
+ ATF_REQUIRE_MSG((error = posix_fallocate(fd, 0, sz / 2)) == 0,
+ "posix_fallocate failed; error=%d", error);
+ ATF_REQUIRE(fstat(fd, &st) == 0);
+ ATF_REQUIRE(st.st_size == sz);
+ /* Grow it using an offset of sz and len of sz. */
+ ATF_REQUIRE_MSG((error = posix_fallocate(fd, sz, sz)) == 0,
+ "posix_fallocate failed; error=%d", error);
+ ATF_REQUIRE(fstat(fd, &st) == 0);
+ ATF_REQUIRE(st.st_size == sz * 2);
+
+ close(fd);
+}
+
+ATF_TC_WITHOUT_HEAD(fspacectl);
+ATF_TC_BODY(fspacectl, tc)
+{
+ struct spacectl_range range;
+ off_t offset, length, shm_sz;
+ size_t page_size;
+ int fd, error;
+
+ page_size = getpagesize();
+ shm_sz = shm_max_pages * page_size;
+
+ fd = shm_open("/testtest", O_RDWR | O_CREAT, 0666);
+ ATF_REQUIRE_MSG(fd >= 0, "shm_open failed; errno:%d", errno);
+ ATF_REQUIRE_MSG((error = posix_fallocate(fd, 0, shm_sz)) == 0,
+ "posix_fallocate failed; error=%d", error);
+
+ /* Aligned fspacectl(fd, SPACECTL_DEALLOC, ...) */
+ ATF_REQUIRE(shm_fill(fd, 0, shm_sz) == 0);
+ range.r_offset = offset = page_size;
+ range.r_len = length = ((shm_max_pages - 1) * page_size) -
+ range.r_offset;
+ ATF_CHECK_MSG(fspacectl(fd, SPACECTL_DEALLOC, &range, 0, &range) == 0,
+ "Aligned fspacectl failed; errno=%d", errno);
+ ATF_CHECK_MSG(check_content_dealloc(fd, offset, length, shm_sz) == 0,
+ "Aligned fspacectl content checking failed");
+
+ /* Unaligned fspacectl(fd, SPACECTL_DEALLOC, ...) */
+ ATF_REQUIRE(shm_fill(fd, 0, shm_sz) == 0);
+ range.r_offset = offset = page_size / 2;
+ range.r_len = length = (shm_max_pages - 1) * page_size +
+ (page_size / 2) - offset;
+ ATF_CHECK_MSG(fspacectl(fd, SPACECTL_DEALLOC, &range, 0, &range) == 0,
+ "Unaligned fspacectl failed; errno=%d", errno);
+ ATF_CHECK_MSG(check_content_dealloc(fd, offset, length, shm_sz) == 0,
+ "Unaligned fspacectl content checking failed");
+
+ /* Aligned fspacectl(fd, SPACECTL_DEALLOC, ...) to OFF_MAX */
+ ATF_REQUIRE(shm_fill(fd, 0, shm_sz) == 0);
+ range.r_offset = offset = page_size;
+ range.r_len = length = OFF_MAX - offset;
+ ATF_CHECK_MSG(fspacectl(fd, SPACECTL_DEALLOC, &range, 0, &range) == 0,
+ "Aligned fspacectl to OFF_MAX failed; errno=%d", errno);
+ ATF_CHECK_MSG(check_content_dealloc(fd, offset, length, shm_sz) == 0,
+ "Aligned fspacectl to OFF_MAX content checking failed");
+
+ /* Unaligned fspacectl(fd, SPACECTL_DEALLOC, ...) to OFF_MAX */
+ ATF_REQUIRE(shm_fill(fd, 0, shm_sz) == 0);
+ range.r_offset = offset = page_size / 2;
+ range.r_len = length = OFF_MAX - offset;
+ ATF_CHECK_MSG(fspacectl(fd, SPACECTL_DEALLOC, &range, 0, &range) == 0,
+ "Unaligned fspacectl to OFF_MAX failed; errno=%d", errno);
+ ATF_CHECK_MSG(check_content_dealloc(fd, offset, length, shm_sz) == 0,
+ "Unaligned fspacectl to OFF_MAX content checking failed");
+
+ /* Aligned fspacectl(fd, SPACECTL_DEALLOC, ...) past shm_sz */
+ ATF_REQUIRE(shm_fill(fd, 0, shm_sz) == 0);
+ range.r_offset = offset = page_size;
+ range.r_len = length = (shm_max_pages + 1) * page_size - offset;
+ ATF_CHECK_MSG(fspacectl(fd, SPACECTL_DEALLOC, &range, 0, &range) == 0,
+ "Aligned fspacectl past shm_sz failed; errno=%d", errno);
+ ATF_CHECK_MSG(check_content_dealloc(fd, offset, length, shm_sz) == 0,
+ "Aligned fspacectl past shm_sz content checking failed");
+
+ /* Unaligned fspacectl(fd, SPACECTL_DEALLOC, ...) past shm_sz */
+ ATF_REQUIRE(shm_fill(fd, 0, shm_sz) == 0);
+ range.r_offset = offset = page_size / 2;
+ range.r_len = length = (shm_max_pages + 1) * page_size - offset;
+ ATF_CHECK_MSG(fspacectl(fd, SPACECTL_DEALLOC, &range, 0, &range) == 0,
+ "Unaligned fspacectl past shm_sz failed; errno=%d", errno);
+ ATF_CHECK_MSG(check_content_dealloc(fd, offset, length, shm_sz) == 0,
+ "Unaligned fspacectl past shm_sz content checking failed");
+
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(accounting);
+ATF_TC_BODY(accounting, tc)
+{
+ struct spacectl_range range;
+ struct stat st;
+ off_t shm_sz, len;
+ size_t page_size;
+ int fd, error;
+
+ page_size = getpagesize();
+ shm_sz = shm_max_pages * page_size;
+
+ fd = shm_open("/testtest1", O_RDWR | O_CREAT, 0666);
+ ATF_REQUIRE_MSG(fd >= 0, "shm_open failed; errno:%d", errno);
+ ATF_REQUIRE_MSG((error = posix_fallocate(fd, 0, shm_sz)) == 0,
+ "posix_fallocate failed; error=%d", error);
+
+ ATF_REQUIRE(shm_fill(fd, 0, shm_sz) == 0);
+ ATF_REQUIRE(fstat(fd, &st) == 0);
+ ATF_REQUIRE(st.st_blksize * st.st_blocks == (blkcnt_t)shm_sz);
+
+ range.r_offset = page_size;
+ range.r_len = len = (shm_max_pages - 1) * page_size -
+ range.r_offset;
+ ATF_CHECK_MSG(fspacectl(fd, SPACECTL_DEALLOC, &range, 0, &range) == 0,
+ "SPACECTL_DEALLOC failed; errno=%d", errno);
+ ATF_REQUIRE(fstat(fd, &st) == 0);
+ ATF_REQUIRE(st.st_blksize * st.st_blocks == (blkcnt_t)(shm_sz - len));
+
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(mmap_prot);
+ATF_TC_BODY(mmap_prot, tc)
+{
+ void *p;
+ int fd, pagesize;
+
+ ATF_REQUIRE((pagesize = getpagesize()) > 0);
+
+ gen_test_path();
+ fd = shm_open(test_path, O_RDONLY | O_CREAT, 0644);
+ ATF_REQUIRE(fd >= 0);
+
+ p = mmap(NULL, pagesize, PROT_READ, MAP_SHARED, fd, 0);
+ ATF_REQUIRE(p != MAP_FAILED);
+ ATF_REQUIRE(munmap(p, pagesize) == 0);
+ p = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ ATF_REQUIRE_ERRNO(EACCES, p == MAP_FAILED);
+ p = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+ ATF_REQUIRE(p != MAP_FAILED);
+ ATF_REQUIRE(munmap(p, pagesize) == 0);
+
+ ATF_REQUIRE_MSG(shm_unlink(test_path) == 0,
+ "shm_unlink failed; errno=%d", errno);
+ ATF_REQUIRE_MSG(close(fd) == 0,
+ "close failed; errno=%d", errno);
+}
+
+static int
+shm_open_large(int psind, int policy, size_t sz)
+{
+ int error, fd;
+
+ fd = shm_create_largepage(SHM_ANON, O_CREAT | O_RDWR, psind, policy, 0);
+ if (fd < 0 && errno == ENOTTY)
+ atf_tc_skip("no large page support");
+ ATF_REQUIRE_MSG(fd >= 0, "shm_create_largepage failed; errno=%d", errno);
+
+ error = ftruncate(fd, sz);
+ if (error != 0 && errno == ENOMEM)
+ /*
+ * The test system might not have enough memory to accommodate
+ * the request.
+ */
+ atf_tc_skip("failed to allocate %zu-byte superpage", sz);
+ ATF_REQUIRE_MSG(error == 0, "ftruncate failed; errno=%d", errno);
+
+ return (fd);
+}
+
+static int
+pagesizes(size_t ps[MAXPAGESIZES])
+{
+ int pscnt;
+
+ pscnt = getpagesizes(ps, MAXPAGESIZES);
+ ATF_REQUIRE_MSG(pscnt != -1, "getpagesizes failed; errno=%d", errno);
+ ATF_REQUIRE_MSG(ps[0] != 0, "psind 0 is %zu", ps[0]);
+ ATF_REQUIRE_MSG(pscnt <= MAXPAGESIZES, "invalid pscnt %d", pscnt);
+ if (pscnt == 1)
+ atf_tc_skip("no large page support");
+ return (pscnt);
+}
+
+ATF_TC_WITHOUT_HEAD(largepage_basic);
+ATF_TC_BODY(largepage_basic, tc)
+{
+ char *zeroes;
+ char *addr, *vec;
+ size_t ps[MAXPAGESIZES];
+ int error, fd, pscnt;
+
+ pscnt = pagesizes(ps);
+ zeroes = calloc(1, ps[0]);
+ ATF_REQUIRE(zeroes != NULL);
+ for (int i = 1; i < pscnt; i++) {
+ fd = shm_open_large(i, SHM_LARGEPAGE_ALLOC_DEFAULT, ps[i]);
+
+ addr = mmap(NULL, ps[i], PROT_READ | PROT_WRITE, MAP_SHARED, fd,
+ 0);
+ ATF_REQUIRE_MSG(addr != MAP_FAILED,
+ "mmap(%zu bytes) failed; errno=%d", ps[i], errno);
+ ATF_REQUIRE_MSG(((uintptr_t)addr & (ps[i] - 1)) == 0,
+ "mmap(%zu bytes) returned unaligned mapping; addr=%p",
+ ps[i], addr);
+
+ /* Force a page fault. */
+ *(volatile char *)addr = 0;
+
+ vec = malloc(ps[i] / ps[0]);
+ ATF_REQUIRE(vec != NULL);
+ error = mincore(addr, ps[i], vec);
+ ATF_REQUIRE_MSG(error == 0, "mincore failed; errno=%d", errno);
+
+ /* Verify that all pages in the run are mapped. */
+ for (size_t p = 0; p < ps[i] / ps[0]; p++) {
+ ATF_REQUIRE_MSG((vec[p] & MINCORE_INCORE) != 0,
+ "page %zu is not mapped", p);
+ ATF_REQUIRE_MSG((vec[p] & MINCORE_SUPER) ==
+ MINCORE_PSIND(i),
+ "page %zu is not in a %zu-byte superpage",
+ p, ps[i]);
+ }
+
+ /* Validate zeroing. */
+ for (size_t p = 0; p < ps[i] / ps[0]; p++) {
+ ATF_REQUIRE_MSG(memcmp(addr + p * ps[0], zeroes,
+ ps[0]) == 0, "page %zu miscompare", p);
+ }
+
+ free(vec);
+ ATF_REQUIRE(munmap(addr, ps[i]) == 0);
+ ATF_REQUIRE(close(fd) == 0);
+ }
+
+ free(zeroes);
+}
+
+extern int __sys_shm_open2(const char *, int, mode_t, int, const char *);
+
+ATF_TC_WITHOUT_HEAD(largepage_config);
+ATF_TC_BODY(largepage_config, tc)
+{
+ struct shm_largepage_conf lpc;
+ char *addr, *buf;
+ size_t ps[MAXPAGESIZES + 1]; /* silence warnings if MAXPAGESIZES == 1 */
+ int error, fd;
+
+ (void)pagesizes(ps);
+
+ fd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0);
+ ATF_REQUIRE_MSG(fd >= 0, "shm_open failed; error=%d", errno);
+
+ /*
+ * Configure a large page policy for an object created without
+ * SHM_LARGEPAGE.
+ */
+ lpc.psind = 1;
+ lpc.alloc_policy = SHM_LARGEPAGE_ALLOC_DEFAULT;
+ error = ioctl(fd, FIOSSHMLPGCNF, &lpc);
+ ATF_REQUIRE(error != 0);
+ ATF_REQUIRE_MSG(errno == ENOTTY, "ioctl(FIOSSHMLPGCNF) returned %d",
+ errno);
+ ATF_REQUIRE(close(fd) == 0);
+
+ /*
+ * Create a largepage object and try to use it without actually
+ * configuring anything.
+ */
+ fd = __sys_shm_open2(SHM_ANON, O_CREAT | O_RDWR, 0, SHM_LARGEPAGE,
+ NULL);
+ if (fd < 0 && errno == ENOTTY)
+ atf_tc_skip("no large page support");
+ ATF_REQUIRE_MSG(fd >= 0, "shm_open2 failed; error=%d", errno);
+
+ error = ftruncate(fd, ps[1]);
+ ATF_REQUIRE(error != 0);
+ ATF_REQUIRE_MSG(errno == EINVAL, "ftruncate returned %d", errno);
+
+ addr = mmap(NULL, ps[1], PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ ATF_REQUIRE(addr == MAP_FAILED);
+ ATF_REQUIRE_MSG(errno == EINVAL, "mmap returned %d", errno);
+ addr = mmap(NULL, 0, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ ATF_REQUIRE(addr == MAP_FAILED);
+ ATF_REQUIRE_MSG(errno == EINVAL, "mmap returned %d", errno);
+
+ buf = calloc(1, ps[0]);
+ ATF_REQUIRE(buf != NULL);
+ ATF_REQUIRE(write(fd, buf, ps[0]) == -1);
+ ATF_REQUIRE_MSG(errno == EINVAL, "write returned %d", errno);
+ free(buf);
+ buf = calloc(1, ps[1]);
+ ATF_REQUIRE(buf != NULL);
+ ATF_REQUIRE(write(fd, buf, ps[1]) == -1);
+ ATF_REQUIRE_MSG(errno == EINVAL, "write returned %d", errno);
+ free(buf);
+
+ error = posix_fallocate(fd, 0, ps[0]);
+ ATF_REQUIRE_MSG(error == EINVAL, "posix_fallocate returned %d", error);
+
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(largepage_mmap);
+ATF_TC_BODY(largepage_mmap, tc)
+{
+ char *addr, *addr1, *vec;
+ size_t ps[MAXPAGESIZES];
+ int fd, pscnt;
+
+ pscnt = pagesizes(ps);
+ for (int i = 1; i < pscnt; i++) {
+ fd = shm_open_large(i, SHM_LARGEPAGE_ALLOC_DEFAULT, ps[i]);
+
+ /* For mincore(). */
+ vec = malloc(ps[i]);
+ ATF_REQUIRE(vec != NULL);
+
+ /*
+ * Wrong mapping size.
+ */
+ addr = mmap(NULL, ps[i - 1], PROT_READ | PROT_WRITE, MAP_SHARED,
+ fd, 0);
+ ATF_REQUIRE_MSG(addr == MAP_FAILED,
+ "mmap(%zu bytes) succeeded", ps[i - 1]);
+ ATF_REQUIRE_MSG(errno == EINVAL,
+ "mmap(%zu bytes) failed; error=%d", ps[i - 1], errno);
+
+ /*
+ * Fixed mappings.
+ */
+ addr = mmap(NULL, ps[i], PROT_READ | PROT_WRITE, MAP_SHARED, fd,
+ 0);
+ ATF_REQUIRE_MSG(addr != MAP_FAILED,
+ "mmap(%zu bytes) failed; errno=%d", ps[i], errno);
+ ATF_REQUIRE_MSG(((uintptr_t)addr & (ps[i] - 1)) == 0,
+ "mmap(%zu bytes) returned unaligned mapping; addr=%p",
+ ps[i], addr);
+
+ /* Try mapping a small page with anonymous memory. */
+ addr1 = mmap(addr, ps[i - 1], PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+ ATF_REQUIRE_MSG(addr1 == MAP_FAILED,
+ "anon mmap(%zu bytes) succeeded", ps[i - 1]);
+ ATF_REQUIRE_MSG(errno == EINVAL, "mmap returned %d", errno);
+
+ /* Check MAP_EXCL when creating a second largepage mapping. */
+ addr1 = mmap(addr, ps[i], PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_FIXED | MAP_EXCL, fd, 0);
+ ATF_REQUIRE_MSG(addr1 == MAP_FAILED,
+ "mmap(%zu bytes) succeeded", ps[i]);
+ /* XXX wrong errno */
+ ATF_REQUIRE_MSG(errno == ENOSPC, "mmap returned %d", errno);
+
+ /* Overwrite a largepage mapping with a lagepage mapping. */
+ addr1 = mmap(addr, ps[i], PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_FIXED, fd, 0);
+ ATF_REQUIRE_MSG(addr1 != MAP_FAILED,
+ "mmap(%zu bytes) failed; errno=%d", ps[i], errno);
+ ATF_REQUIRE_MSG(addr == addr1,
+ "mmap(%zu bytes) moved from %p to %p", ps[i], addr, addr1);
+
+ ATF_REQUIRE(munmap(addr, ps[i] == 0));
+
+ /* Clobber an anonymous mapping with a superpage. */
+ addr1 = mmap(NULL, ps[i], PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_PRIVATE | MAP_ALIGNED(ffsl(ps[i]) - 1), -1,
+ 0);
+ ATF_REQUIRE_MSG(addr1 != MAP_FAILED,
+ "mmap failed; error=%d", errno);
+ *(volatile char *)addr1 = '\0';
+ addr = mmap(addr1, ps[i], PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_FIXED, fd, 0);
+ ATF_REQUIRE_MSG(addr != MAP_FAILED,
+ "mmap failed; error=%d", errno);
+ ATF_REQUIRE_MSG(addr == addr1,
+ "mmap disobeyed MAP_FIXED, %p %p", addr, addr1);
+ *(volatile char *)addr = 0; /* fault */
+ ATF_REQUIRE(mincore(addr, ps[i], vec) == 0);
+ for (size_t p = 0; p < ps[i] / ps[0]; p++) {
+ ATF_REQUIRE_MSG((vec[p] & MINCORE_INCORE) != 0,
+ "page %zu is not resident", p);
+ ATF_REQUIRE_MSG((vec[p] & MINCORE_SUPER) ==
+ MINCORE_PSIND(i),
+ "page %zu is not resident", p);
+ }
+
+ /*
+ * Copy-on-write mappings are not permitted.
+ */
+ addr = mmap(NULL, ps[i], PROT_READ | PROT_WRITE, MAP_PRIVATE,
+ fd, 0);
+ ATF_REQUIRE_MSG(addr == MAP_FAILED,
+ "mmap(%zu bytes) succeeded", ps[i]);
+
+ ATF_REQUIRE(close(fd) == 0);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(largepage_munmap);
+ATF_TC_BODY(largepage_munmap, tc)
+{
+ char *addr;
+ size_t ps[MAXPAGESIZES], ps1;
+ int fd, pscnt;
+
+ pscnt = pagesizes(ps);
+ for (int i = 1; i < pscnt; i++) {
+ fd = shm_open_large(i, SHM_LARGEPAGE_ALLOC_DEFAULT, ps[i]);
+ ps1 = ps[i - 1];
+
+ addr = mmap(NULL, ps[i], PROT_READ | PROT_WRITE, MAP_SHARED, fd,
+ 0);
+ ATF_REQUIRE_MSG(addr != MAP_FAILED,
+ "mmap(%zu bytes) failed; errno=%d", ps[i], errno);
+
+ /* Try several unaligned munmap() requests. */
+ ATF_REQUIRE(munmap(addr, ps1) != 0);
+ ATF_REQUIRE_MSG(errno == EINVAL,
+ "unexpected error %d from munmap", errno);
+ ATF_REQUIRE(munmap(addr, ps[i] - ps1));
+ ATF_REQUIRE_MSG(errno == EINVAL,
+ "unexpected error %d from munmap", errno);
+ ATF_REQUIRE(munmap(addr + ps1, ps1) != 0);
+ ATF_REQUIRE_MSG(errno == EINVAL,
+ "unexpected error %d from munmap", errno);
+ ATF_REQUIRE(munmap(addr, 0));
+ ATF_REQUIRE_MSG(errno == EINVAL,
+ "unexpected error %d from munmap", errno);
+
+ ATF_REQUIRE(munmap(addr, ps[i]) == 0);
+ ATF_REQUIRE(close(fd) == 0);
+ }
+}
+
+static void
+largepage_madvise(char *addr, size_t sz, int advice, int error)
+{
+ if (error == 0) {
+ ATF_REQUIRE_MSG(madvise(addr, sz, advice) == 0,
+ "madvise(%zu, %d) failed; error=%d", sz, advice, errno);
+ } else {
+ ATF_REQUIRE_MSG(madvise(addr, sz, advice) != 0,
+ "madvise(%zu, %d) succeeded", sz, advice);
+ ATF_REQUIRE_MSG(errno == error,
+ "unexpected error %d from madvise(%zu, %d)",
+ errno, sz, advice);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(largepage_madvise);
+ATF_TC_BODY(largepage_madvise, tc)
+{
+ char *addr;
+ size_t ps[MAXPAGESIZES];
+ int fd, pscnt;
+
+ pscnt = pagesizes(ps);
+ for (int i = 1; i < pscnt; i++) {
+ fd = shm_open_large(i, SHM_LARGEPAGE_ALLOC_DEFAULT, ps[i]);
+ addr = mmap(NULL, ps[i], PROT_READ | PROT_WRITE, MAP_SHARED, fd,
+ 0);
+ ATF_REQUIRE_MSG(addr != MAP_FAILED,
+ "mmap(%zu bytes) failed; error=%d", ps[i], errno);
+
+ memset(addr, 0, ps[i]);
+
+ /* Advice that requires clipping. */
+ largepage_madvise(addr, ps[0], MADV_NORMAL, EINVAL);
+ largepage_madvise(addr, ps[i], MADV_NORMAL, 0);
+ largepage_madvise(addr, ps[0], MADV_RANDOM, EINVAL);
+ largepage_madvise(addr, ps[i], MADV_RANDOM, 0);
+ largepage_madvise(addr, ps[0], MADV_SEQUENTIAL, EINVAL);
+ largepage_madvise(addr, ps[i], MADV_SEQUENTIAL, 0);
+ largepage_madvise(addr, ps[0], MADV_NOSYNC, EINVAL);
+ largepage_madvise(addr, ps[i], MADV_NOSYNC, 0);
+ largepage_madvise(addr, ps[0], MADV_AUTOSYNC, EINVAL);
+ largepage_madvise(addr, ps[i], MADV_AUTOSYNC, 0);
+ largepage_madvise(addr, ps[0], MADV_CORE, EINVAL);
+ largepage_madvise(addr, ps[i], MADV_CORE, 0);
+ largepage_madvise(addr, ps[0], MADV_NOCORE, EINVAL);
+ largepage_madvise(addr, ps[i], MADV_NOCORE, 0);
+
+ /* Advice that does not result in clipping. */
+ largepage_madvise(addr, ps[0], MADV_DONTNEED, 0);
+ largepage_madvise(addr, ps[i], MADV_DONTNEED, 0);
+ largepage_madvise(addr, ps[0], MADV_WILLNEED, 0);
+ largepage_madvise(addr, ps[i], MADV_WILLNEED, 0);
+ largepage_madvise(addr, ps[0], MADV_FREE, 0);
+ largepage_madvise(addr, ps[i], MADV_FREE, 0);
+
+ ATF_REQUIRE(munmap(addr, ps[i]) == 0);
+ ATF_REQUIRE(close(fd) == 0);
+ }
+}
+
+ATF_TC(largepage_mlock);
+ATF_TC_HEAD(largepage_mlock, tc)
+{
+ /* Needed to set rlimit. */
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(largepage_mlock, tc)
+{
+ struct rlimit rl;
+ char *addr;
+ size_t ps[MAXPAGESIZES], sz;
+ u_long max_wired, wired;
+ int fd, error, pscnt;
+
+ rl.rlim_cur = rl.rlim_max = RLIM_INFINITY;
+ ATF_REQUIRE_MSG(setrlimit(RLIMIT_MEMLOCK, &rl) == 0,
+ "setrlimit failed; error=%d", errno);
+
+ sz = sizeof(max_wired);
+ error = sysctlbyname("vm.max_user_wired", &max_wired, &sz, NULL, 0);
+ ATF_REQUIRE_MSG(error == 0,
+ "sysctlbyname(vm.max_user_wired) failed; error=%d", errno);
+
+ sz = sizeof(wired);
+ error = sysctlbyname("vm.stats.vm.v_user_wire_count", &wired, &sz, NULL,
+ 0);
+ ATF_REQUIRE_MSG(error == 0,
+ "sysctlbyname(vm.stats.vm.v_user_wire_count) failed; error=%d",
+ errno);
+
+ pscnt = pagesizes(ps);
+ for (int i = 1; i < pscnt; i++) {
+ if (ps[i] / ps[0] > max_wired - wired) {
+ /* Cannot wire past the limit. */
+ atf_tc_skip("test would exceed wiring limit");
+ }
+
+ fd = shm_open_large(i, SHM_LARGEPAGE_ALLOC_DEFAULT, ps[i]);
+ addr = mmap(NULL, ps[i], PROT_READ | PROT_WRITE, MAP_SHARED, fd,
+ 0);
+ ATF_REQUIRE_MSG(addr != MAP_FAILED,
+ "mmap(%zu bytes) failed; error=%d", ps[i], errno);
+
+ ATF_REQUIRE(mlock(addr, ps[0]) != 0);
+ ATF_REQUIRE_MSG(errno == EINVAL,
+ "unexpected error %d from mlock(%zu bytes)", errno, ps[i]);
+ ATF_REQUIRE(mlock(addr, ps[i] - ps[0]) != 0);
+ ATF_REQUIRE_MSG(errno == EINVAL,
+ "unexpected error %d from mlock(%zu bytes)", errno, ps[i]);
+
+ ATF_REQUIRE_MSG(mlock(addr, ps[i]) == 0,
+ "mlock failed; error=%d", errno);
+
+ ATF_REQUIRE(munmap(addr, ps[i]) == 0);
+
+ ATF_REQUIRE(mlockall(MCL_FUTURE) == 0);
+ addr = mmap(NULL, ps[i], PROT_READ | PROT_WRITE, MAP_SHARED, fd,
+ 0);
+ ATF_REQUIRE_MSG(addr != MAP_FAILED,
+ "mmap(%zu bytes) failed; error=%d", ps[i], errno);
+
+ ATF_REQUIRE(munmap(addr, ps[i]) == 0);
+ ATF_REQUIRE(close(fd) == 0);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(largepage_msync);
+ATF_TC_BODY(largepage_msync, tc)
+{
+ char *addr;
+ size_t ps[MAXPAGESIZES];
+ int fd, pscnt;
+
+ pscnt = pagesizes(ps);
+ for (int i = 1; i < pscnt; i++) {
+ fd = shm_open_large(i, SHM_LARGEPAGE_ALLOC_DEFAULT, ps[i]);
+ addr = mmap(NULL, ps[i], PROT_READ | PROT_WRITE, MAP_SHARED, fd,
+ 0);
+ ATF_REQUIRE_MSG(addr != MAP_FAILED,
+ "mmap(%zu bytes) failed; error=%d", ps[i], errno);
+
+ memset(addr, 0, ps[i]);
+
+ /*
+ * "Sync" requests are no-ops for SHM objects, so small
+ * PAGE_SIZE-sized requests succeed.
+ */
+ ATF_REQUIRE_MSG(msync(addr, ps[0], MS_ASYNC) == 0,
+ "msync(MS_ASYNC) failed; error=%d", errno);
+ ATF_REQUIRE_MSG(msync(addr, ps[i], MS_ASYNC) == 0,
+ "msync(MS_ASYNC) failed; error=%d", errno);
+ ATF_REQUIRE_MSG(msync(addr, ps[0], MS_SYNC) == 0,
+ "msync(MS_SYNC) failed; error=%d", errno);
+ ATF_REQUIRE_MSG(msync(addr, ps[i], MS_SYNC) == 0,
+ "msync(MS_SYNC) failed; error=%d", errno);
+
+ ATF_REQUIRE_MSG(msync(addr, ps[0], MS_INVALIDATE) != 0,
+ "msync(MS_INVALIDATE) succeeded");
+ /* XXX wrong errno */
+ ATF_REQUIRE_MSG(errno == EBUSY,
+ "unexpected error %d from msync(MS_INVALIDATE)", errno);
+ ATF_REQUIRE_MSG(msync(addr, ps[i], MS_INVALIDATE) == 0,
+ "msync(MS_INVALIDATE) failed; error=%d", errno);
+ memset(addr, 0, ps[i]);
+
+ ATF_REQUIRE(munmap(addr, ps[i]) == 0);
+ ATF_REQUIRE(close(fd) == 0);
+ }
+}
+
+static void
+largepage_protect(char *addr, size_t sz, int prot, int error)
+{
+ if (error == 0) {
+ ATF_REQUIRE_MSG(mprotect(addr, sz, prot) == 0,
+ "mprotect(%zu, %x) failed; error=%d", sz, prot, errno);
+ } else {
+ ATF_REQUIRE_MSG(mprotect(addr, sz, prot) != 0,
+ "mprotect(%zu, %x) succeeded", sz, prot);
+ ATF_REQUIRE_MSG(errno == error,
+ "unexpected error %d from mprotect(%zu, %x)",
+ errno, sz, prot);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(largepage_mprotect);
+ATF_TC_BODY(largepage_mprotect, tc)
+{
+ char *addr, *addr1;
+ size_t ps[MAXPAGESIZES];
+ int fd, pscnt;
+
+ pscnt = pagesizes(ps);
+ for (int i = 1; i < pscnt; i++) {
+ /*
+ * Reserve a contiguous region in the address space to avoid
+ * spurious failures in the face of ASLR.
+ */
+ addr = mmap(NULL, ps[i] * 2, PROT_NONE,
+ MAP_ANON | MAP_ALIGNED(ffsl(ps[i]) - 1), -1, 0);
+ ATF_REQUIRE_MSG(addr != MAP_FAILED,
+ "mmap(%zu bytes) failed; error=%d", ps[i], errno);
+ ATF_REQUIRE(munmap(addr, ps[i] * 2) == 0);
+
+ fd = shm_open_large(i, SHM_LARGEPAGE_ALLOC_DEFAULT, ps[i]);
+ addr = mmap(addr, ps[i], PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_FIXED, fd, 0);
+ ATF_REQUIRE_MSG(addr != MAP_FAILED,
+ "mmap(%zu bytes) failed; error=%d", ps[i], errno);
+
+ /*
+ * These should be no-ops from the pmap perspective since the
+ * page is not yet entered into the pmap.
+ */
+ largepage_protect(addr, ps[0], PROT_READ, EINVAL);
+ largepage_protect(addr, ps[i], PROT_READ, 0);
+ largepage_protect(addr, ps[0], PROT_NONE, EINVAL);
+ largepage_protect(addr, ps[i], PROT_NONE, 0);
+ largepage_protect(addr, ps[0],
+ PROT_READ | PROT_WRITE | PROT_EXEC, EINVAL);
+ largepage_protect(addr, ps[i],
+ PROT_READ | PROT_WRITE | PROT_EXEC, 0);
+
+ /* Trigger creation of a mapping and try again. */
+ *(volatile char *)addr = 0;
+ largepage_protect(addr, ps[0], PROT_READ, EINVAL);
+ largepage_protect(addr, ps[i], PROT_READ, 0);
+ largepage_protect(addr, ps[0], PROT_NONE, EINVAL);
+ largepage_protect(addr, ps[i], PROT_NONE, 0);
+ largepage_protect(addr, ps[0],
+ PROT_READ | PROT_WRITE | PROT_EXEC, EINVAL);
+ largepage_protect(addr, ps[i],
+ PROT_READ | PROT_WRITE | PROT_EXEC, 0);
+
+ memset(addr, 0, ps[i]);
+
+ /* Map two contiguous large pages and merge map entries. */
+ addr1 = mmap(addr + ps[i], ps[i], PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_FIXED | MAP_EXCL, fd, 0);
+ ATF_REQUIRE_MSG(addr1 != MAP_FAILED,
+ "mmap(%zu bytes) failed; error=%d", ps[i], errno);
+
+ largepage_protect(addr1 - ps[0], ps[0] * 2,
+ PROT_READ | PROT_WRITE, EINVAL);
+ largepage_protect(addr, ps[i] * 2, PROT_READ | PROT_WRITE, 0);
+
+ memset(addr, 0, ps[i] * 2);
+
+ ATF_REQUIRE(munmap(addr, ps[i]) == 0);
+ ATF_REQUIRE(munmap(addr1, ps[i]) == 0);
+ ATF_REQUIRE(close(fd) == 0);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(largepage_minherit);
+ATF_TC_BODY(largepage_minherit, tc)
+{
+ char *addr;
+ size_t ps[MAXPAGESIZES];
+ pid_t child;
+ int fd, pscnt, status;
+
+ pscnt = pagesizes(ps);
+ for (int i = 1; i < pscnt; i++) {
+ fd = shm_open_large(i, SHM_LARGEPAGE_ALLOC_DEFAULT, ps[i]);
+ addr = mmap(NULL, ps[i], PROT_READ | PROT_WRITE, MAP_SHARED, fd,
+ 0);
+ ATF_REQUIRE_MSG(addr != MAP_FAILED,
+ "mmap(%zu bytes) failed; error=%d", ps[i], errno);
+
+ ATF_REQUIRE(minherit(addr, ps[0], INHERIT_SHARE) != 0);
+
+ ATF_REQUIRE_MSG(minherit(addr, ps[i], INHERIT_SHARE) == 0,
+ "minherit(%zu bytes) failed; error=%d", ps[i], errno);
+ child = fork();
+ ATF_REQUIRE_MSG(child != -1, "fork failed; error=%d", errno);
+ if (child == 0) {
+ char v;
+
+ *(volatile char *)addr = 0;
+ if (mincore(addr, ps[0], &v) != 0)
+ _exit(1);
+ if ((v & MINCORE_SUPER) == 0)
+ _exit(2);
+ _exit(0);
+ }
+ ATF_REQUIRE_MSG(waitpid(child, &status, 0) == child,
+ "waitpid failed; error=%d", errno);
+ ATF_REQUIRE_MSG(WIFEXITED(status),
+ "child was killed by signal %d", WTERMSIG(status));
+ ATF_REQUIRE_MSG(WEXITSTATUS(status) == 0,
+ "child exited with status %d", WEXITSTATUS(status));
+
+ ATF_REQUIRE_MSG(minherit(addr, ps[i], INHERIT_NONE) == 0,
+ "minherit(%zu bytes) failed; error=%d", ps[i], errno);
+ child = fork();
+ ATF_REQUIRE_MSG(child != -1, "fork failed; error=%d", errno);
+ if (child == 0) {
+ char v;
+
+ if (mincore(addr, ps[0], &v) == 0)
+ _exit(1);
+ _exit(0);
+ }
+ ATF_REQUIRE_MSG(waitpid(child, &status, 0) == child,
+ "waitpid failed; error=%d", errno);
+ ATF_REQUIRE_MSG(WIFEXITED(status),
+ "child was killed by signal %d", WTERMSIG(status));
+ ATF_REQUIRE_MSG(WEXITSTATUS(status) == 0,
+ "child exited with status %d", WEXITSTATUS(status));
+
+ /* Copy-on-write is not supported for static large pages. */
+ ATF_REQUIRE_MSG(minherit(addr, ps[i], INHERIT_COPY) != 0,
+ "minherit(%zu bytes) succeeded", ps[i]);
+
+ ATF_REQUIRE_MSG(minherit(addr, ps[i], INHERIT_ZERO) == 0,
+ "minherit(%zu bytes) failed; error=%d", ps[i], errno);
+ child = fork();
+ ATF_REQUIRE_MSG(child != -1, "fork failed; error=%d", errno);
+ if (child == 0) {
+ char v;
+
+ *(volatile char *)addr = 0;
+ if (mincore(addr, ps[0], &v) != 0)
+ _exit(1);
+ if ((v & MINCORE_SUPER) != 0)
+ _exit(2);
+ _exit(0);
+ }
+ ATF_REQUIRE_MSG(waitpid(child, &status, 0) == child,
+ "waitpid failed; error=%d", errno);
+ ATF_REQUIRE_MSG(WIFEXITED(status),
+ "child was killed by signal %d", WTERMSIG(status));
+ ATF_REQUIRE_MSG(WEXITSTATUS(status) == 0,
+ "child exited with status %d", WEXITSTATUS(status));
+
+ ATF_REQUIRE(munmap(addr, ps[i]) == 0);
+ ATF_REQUIRE(close(fd) == 0);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(largepage_pipe);
+ATF_TC_BODY(largepage_pipe, tc)
+{
+ size_t ps[MAXPAGESIZES];
+ char *addr;
+ ssize_t len;
+ int fd, pfd[2], pscnt, status;
+ pid_t child;
+
+ pscnt = pagesizes(ps);
+
+ for (int i = 1; i < pscnt; i++) {
+ fd = shm_open_large(i, SHM_LARGEPAGE_ALLOC_DEFAULT, ps[i]);
+ addr = mmap(NULL, ps[i], PROT_READ | PROT_WRITE, MAP_SHARED, fd,
+ 0);
+ ATF_REQUIRE_MSG(addr != MAP_FAILED,
+ "mmap(%zu bytes) failed; error=%d", ps[i], errno);
+
+ /* Trigger creation of a mapping. */
+ *(volatile char *)addr = '\0';
+
+ ATF_REQUIRE(pipe(pfd) == 0);
+ child = fork();
+ ATF_REQUIRE_MSG(child != -1, "fork() failed; error=%d", errno);
+ if (child == 0) {
+ char buf[BUFSIZ];
+ ssize_t resid;
+
+ (void)close(pfd[0]);
+ for (resid = (size_t)ps[i]; resid > 0; resid -= len) {
+ len = read(pfd[1], buf, sizeof(buf));
+ if (len < 0)
+ _exit(1);
+ }
+ _exit(0);
+ }
+ ATF_REQUIRE(close(pfd[1]) == 0);
+ len = write(pfd[0], addr, ps[i]);
+ ATF_REQUIRE_MSG(len >= 0, "write() failed; error=%d", errno);
+ ATF_REQUIRE_MSG(len == (ssize_t)ps[i],
+ "short write; len=%zd", len);
+ ATF_REQUIRE(close(pfd[0]) == 0);
+
+ ATF_REQUIRE_MSG(waitpid(child, &status, 0) == child,
+ "waitpid() failed; error=%d", errno);
+ ATF_REQUIRE_MSG(WIFEXITED(status),
+ "child was killed by signal %d", WTERMSIG(status));
+ ATF_REQUIRE_MSG(WEXITSTATUS(status) == 0,
+ "child exited with status %d", WEXITSTATUS(status));
+
+ ATF_REQUIRE(munmap(addr, ps[i]) == 0);
+ ATF_REQUIRE(close(fd) == 0);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(largepage_reopen);
+ATF_TC_BODY(largepage_reopen, tc)
+{
+ char *addr, *vec;
+ size_t ps[MAXPAGESIZES];
+ int fd, psind;
+
+ (void)pagesizes(ps);
+ psind = 1;
+
+ gen_test_path();
+ fd = shm_create_largepage(test_path, O_CREAT | O_RDWR, psind,
+ SHM_LARGEPAGE_ALLOC_DEFAULT, 0600);
+ if (fd < 0 && errno == ENOTTY)
+ atf_tc_skip("no large page support");
+ ATF_REQUIRE_MSG(fd >= 0, "shm_create_largepage failed; error=%d", errno);
+
+ ATF_REQUIRE_MSG(ftruncate(fd, ps[psind]) == 0,
+ "ftruncate failed; error=%d", errno);
+
+ ATF_REQUIRE_MSG(close(fd) == 0, "close failed; error=%d", errno);
+
+ fd = shm_open(test_path, O_RDWR, 0);
+ ATF_REQUIRE_MSG(fd >= 0, "shm_open failed; error=%d", errno);
+
+ addr = mmap(NULL, ps[psind], PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ ATF_REQUIRE_MSG(addr != MAP_FAILED, "mmap failed; error=%d", errno);
+
+ /* Trigger a fault and mapping creation. */
+ *(volatile char *)addr = 0;
+
+ vec = malloc(ps[psind] / ps[0]);
+ ATF_REQUIRE(vec != NULL);
+ ATF_REQUIRE_MSG(mincore(addr, ps[psind], vec) == 0,
+ "mincore failed; error=%d", errno);
+ ATF_REQUIRE_MSG((vec[0] & MINCORE_SUPER) == MINCORE_PSIND(psind),
+ "page not mapped into a %zu-byte superpage", ps[psind]);
+
+ ATF_REQUIRE_MSG(shm_unlink(test_path) == 0,
+ "shm_unlink failed; errno=%d", errno);
+ ATF_REQUIRE_MSG(close(fd) == 0,
+ "close failed; errno=%d", errno);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, remap_object);
+ ATF_TP_ADD_TC(tp, rename_from_anon);
+ ATF_TP_ADD_TC(tp, rename_bad_path_pointer);
+ ATF_TP_ADD_TC(tp, rename_from_nonexisting);
+ ATF_TP_ADD_TC(tp, rename_to_anon);
+ ATF_TP_ADD_TC(tp, rename_to_replace);
+ ATF_TP_ADD_TC(tp, rename_to_noreplace);
+ ATF_TP_ADD_TC(tp, rename_to_exchange);
+ ATF_TP_ADD_TC(tp, rename_to_exchange_nonexisting);
+ ATF_TP_ADD_TC(tp, rename_to_self);
+ ATF_TP_ADD_TC(tp, rename_bad_flag);
+ ATF_TP_ADD_TC(tp, reopen_object);
+ ATF_TP_ADD_TC(tp, readonly_mmap_write);
+ ATF_TP_ADD_TC(tp, open_after_link);
+ ATF_TP_ADD_TC(tp, open_invalid_path);
+ ATF_TP_ADD_TC(tp, open_write_only);
+ ATF_TP_ADD_TC(tp, open_extra_flags);
+ ATF_TP_ADD_TC(tp, open_anon);
+ ATF_TP_ADD_TC(tp, open_anon_readonly);
+ ATF_TP_ADD_TC(tp, open_bad_path_pointer);
+ ATF_TP_ADD_TC(tp, open_path_too_long);
+ ATF_TP_ADD_TC(tp, open_nonexisting_object);
+ ATF_TP_ADD_TC(tp, open_create_existing_object);
+ ATF_TP_ADD_TC(tp, shm_functionality_across_fork);
+ ATF_TP_ADD_TC(tp, trunc_resets_object);
+ ATF_TP_ADD_TC(tp, unlink_bad_path_pointer);
+ ATF_TP_ADD_TC(tp, unlink_path_too_long);
+ ATF_TP_ADD_TC(tp, object_resize);
+ ATF_TP_ADD_TC(tp, cloexec);
+ ATF_TP_ADD_TC(tp, mode);
+ ATF_TP_ADD_TC(tp, fallocate);
+ ATF_TP_ADD_TC(tp, fspacectl);
+ ATF_TP_ADD_TC(tp, accounting);
+ ATF_TP_ADD_TC(tp, mmap_prot);
+ ATF_TP_ADD_TC(tp, largepage_basic);
+ ATF_TP_ADD_TC(tp, largepage_config);
+ ATF_TP_ADD_TC(tp, largepage_mmap);
+ ATF_TP_ADD_TC(tp, largepage_munmap);
+ ATF_TP_ADD_TC(tp, largepage_madvise);
+ ATF_TP_ADD_TC(tp, largepage_mlock);
+ ATF_TP_ADD_TC(tp, largepage_msync);
+ ATF_TP_ADD_TC(tp, largepage_mprotect);
+ ATF_TP_ADD_TC(tp, largepage_minherit);
+ ATF_TP_ADD_TC(tp, largepage_pipe);
+ ATF_TP_ADD_TC(tp, largepage_reopen);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/ses/Makefile b/tests/sys/ses/Makefile
new file mode 100644
index 000000000000..8a0c54a2dd52
--- /dev/null
+++ b/tests/sys/ses/Makefile
@@ -0,0 +1,11 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/ses
+
+ATF_TESTS_C+= destructive
+ATF_TESTS_C+= nondestructive
+
+# Some tests cases alter enclosure state, so they can't run concurrently.
+TEST_METADATA.destructive+= is_exclusive=true
+
+.include <bsd.test.mk>
diff --git a/tests/sys/ses/common.h b/tests/sys/ses/common.h
new file mode 100644
index 000000000000..084cc03e9e3e
--- /dev/null
+++ b/tests/sys/ses/common.h
@@ -0,0 +1,67 @@
+/*-
+ * Copyright (C) 2021 Axcient, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+typedef bool(*ses_cb)(const char *devname, int fd);
+
+// Run a test function on every available ses device
+static void
+for_each_ses_dev(ses_cb cb, int oflags)
+{
+ glob_t g;
+ int r;
+ unsigned i;
+
+ g.gl_pathc = 0;
+ g.gl_pathv = NULL;
+ g.gl_offs = 0;
+
+ r = glob("/dev/ses*", GLOB_NOCHECK | GLOB_NOSORT, NULL, &g);
+ ATF_REQUIRE_EQ(r, 0);
+ if (g.gl_matchc == 0)
+ return;
+
+ for(i = 0; i < g.gl_matchc; i++) {
+ int fd;
+
+ fd = open(g.gl_pathv[i], oflags);
+ ATF_REQUIRE(fd >= 0);
+ cb(g.gl_pathv[i], fd);
+ close(fd);
+ }
+
+ globfree(&g);
+}
+
+static bool
+has_ses(void)
+{
+ glob_t g;
+ int r;
+
+ r = glob("/dev/ses*", GLOB_NOCHECK | GLOB_NOSORT, NULL, &g);
+ ATF_REQUIRE_EQ(r, 0);
+
+ return (g.gl_matchc != 0);
+}
diff --git a/tests/sys/ses/destructive.c b/tests/sys/ses/destructive.c
new file mode 100644
index 000000000000..8203a56bb1ad
--- /dev/null
+++ b/tests/sys/ses/destructive.c
@@ -0,0 +1,305 @@
+/*-
+ * Copyright (C) 2021 Axcient, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Tests that alter an enclosure's state */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <glob.h>
+#include <regex.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <cam/scsi/scsi_enc.h>
+
+#include "common.h"
+
+// Run a test function on just one ses device
+static void
+for_one_ses_dev(ses_cb cb)
+{
+ glob_t g;
+ int fd, r;
+
+ g.gl_pathc = 0;
+ g.gl_pathv = NULL;
+ g.gl_offs = 0;
+
+ r = glob("/dev/ses*", GLOB_NOCHECK | GLOB_NOSORT, NULL, &g);
+ ATF_REQUIRE_EQ(r, 0);
+ if (g.gl_matchc == 0)
+ return;
+
+ fd = open(g.gl_pathv[0], O_RDWR);
+ ATF_REQUIRE(fd >= 0);
+ cb(g.gl_pathv[0], fd);
+ close(fd);
+
+ globfree(&g);
+}
+
+static bool
+do_setelmstat(const char *devname __unused, int fd)
+{
+ encioc_element_t *map;
+ unsigned elm_idx;
+ unsigned nobj;
+ int r;
+ elm_type_t last_elm_type = -1;
+
+ r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
+ ATF_REQUIRE_EQ(r, 0);
+
+ map = calloc(nobj, sizeof(encioc_element_t));
+ ATF_REQUIRE(map != NULL);
+ r = ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) map);
+
+ /* Set the IDENT bit for every disk slot */
+ for (elm_idx = 0; elm_idx < nobj; elm_idx++) {
+ encioc_elm_status_t elmstat;
+ struct ses_ctrl_dev_slot *cslot;
+
+ if (last_elm_type != map[elm_idx].elm_type) {
+ /* skip overall elements */
+ last_elm_type = map[elm_idx].elm_type;
+ continue;
+ }
+ elmstat.elm_idx = elm_idx;
+ if (map[elm_idx].elm_type == ELMTYP_DEVICE ||
+ map[elm_idx].elm_type == ELMTYP_ARRAY_DEV)
+ {
+ r = ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t)&elmstat);
+ ATF_REQUIRE_EQ(r, 0);
+ ses_status_to_ctrl(map[elm_idx].elm_type,
+ &elmstat.cstat[0]);
+
+ cslot = (struct ses_ctrl_dev_slot*)&elmstat.cstat[0];
+
+ ses_ctrl_common_set_select(&cslot->common, 1);
+ ses_ctrl_dev_slot_set_rqst_ident(cslot, 1);
+ r = ioctl(fd, ENCIOC_SETELMSTAT, (caddr_t)&elmstat);
+ ATF_REQUIRE_EQ(r, 0);
+ }
+ }
+
+ /* Check the IDENT bit for every disk slot */
+ last_elm_type = -1;
+ for (elm_idx = 0; elm_idx < nobj; elm_idx++) {
+ encioc_elm_status_t elmstat;
+ struct ses_status_dev_slot *sslot =
+ (struct ses_status_dev_slot*)&elmstat.cstat[0];
+
+ if (last_elm_type != map[elm_idx].elm_type) {
+ /* skip overall elements */
+ last_elm_type = map[elm_idx].elm_type;
+ continue;
+ }
+ elmstat.elm_idx = elm_idx;
+ if (map[elm_idx].elm_type == ELMTYP_DEVICE ||
+ map[elm_idx].elm_type == ELMTYP_ARRAY_DEV)
+ {
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ r = ioctl(fd, ENCIOC_GETELMSTAT,
+ (caddr_t)&elmstat);
+ ATF_REQUIRE_EQ(r, 0);
+ if (0 == ses_status_dev_slot_get_ident(sslot)) {
+ /* Needs more time to take effect */
+ usleep(100000);
+ }
+ }
+ ATF_CHECK(ses_status_dev_slot_get_ident(sslot) != 0);
+
+ }
+ }
+
+ free(map);
+ return (true);
+}
+
+/*
+ * sg_ses doesn't provide "dump and restore" functionality. The closest is to
+ * dump status page 2, then manually edit the file to set every individual
+ * element select bit, then load the entire file. But that is much too hard.
+ * Instead, we'll just clear every ident bit.
+ */
+static bool
+do_setelmstat_cleanup(const char *devname __unused, int fd __unused)
+{
+ encioc_element_t *map;
+ unsigned elm_idx;
+ unsigned nobj;
+ int r;
+ elm_type_t last_elm_type = -1;
+
+ r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
+ ATF_REQUIRE_EQ(r, 0);
+
+ map = calloc(nobj, sizeof(encioc_element_t));
+ ATF_REQUIRE(map != NULL);
+ r = ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) map);
+ ATF_REQUIRE_EQ(r, 0);
+
+ /* Clear the IDENT bit for every disk slot */
+ for (elm_idx = 0; elm_idx < nobj; elm_idx++) {
+ encioc_elm_status_t elmstat;
+ struct ses_ctrl_dev_slot *cslot;
+
+ if (last_elm_type != map[elm_idx].elm_type) {
+ /* skip overall elements */
+ last_elm_type = map[elm_idx].elm_type;
+ continue;
+ }
+ elmstat.elm_idx = elm_idx;
+ if (map[elm_idx].elm_type == ELMTYP_DEVICE ||
+ map[elm_idx].elm_type == ELMTYP_ARRAY_DEV)
+ {
+ r = ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t)&elmstat);
+ ATF_REQUIRE_EQ(r, 0);
+ ses_status_to_ctrl(map[elm_idx].elm_type,
+ &elmstat.cstat[0]);
+
+ cslot = (struct ses_ctrl_dev_slot*)&elmstat.cstat[0];
+
+ ses_ctrl_common_set_select(&cslot->common, 1);
+ ses_ctrl_dev_slot_set_rqst_ident(cslot, 0);
+ r = ioctl(fd, ENCIOC_SETELMSTAT, (caddr_t)&elmstat);
+ ATF_REQUIRE_EQ(r, 0);
+ }
+ }
+
+ return(true);
+}
+
+
+ATF_TC_WITH_CLEANUP(setelmstat);
+ATF_TC_HEAD(setelmstat, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Exercise ENCIOC_SETELMSTAT");
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(setelmstat, tc)
+{
+ if (!has_ses())
+ atf_tc_skip("No ses devices found");
+
+ for_one_ses_dev(do_setelmstat);
+}
+ATF_TC_CLEANUP(setelmstat, tc)
+{
+ if (!has_ses())
+ return;
+
+ for_one_ses_dev(do_setelmstat_cleanup);
+}
+
+
+static bool
+do_setencstat(const char *devname __unused, int fd)
+{
+ unsigned char encstat;
+ int r, i;
+ bool worked = false;
+
+ /*
+ * SES provides no way to read the current setting of the enclosure
+ * control page common status bits. So we'll blindly set CRIT.
+ */
+ encstat = 1 << SES_CTRL_PAGE_CRIT_SHIFT;
+ r = ioctl(fd, ENCIOC_SETENCSTAT, (caddr_t) &encstat);
+ ATF_REQUIRE_EQ(r, 0);
+
+ /* Check that the status has changed */
+ for (i = 0; i < 10; i++) {
+ r = ioctl(fd, ENCIOC_GETENCSTAT, (caddr_t) &encstat);
+ ATF_REQUIRE_EQ(r, 0);
+ if (encstat & SES_CTRL_PAGE_CRIT_MASK) {
+ worked = true;
+ break;
+ }
+ usleep(100000);
+ }
+ if (!worked) {
+ /* Some enclosures don't support setting the enclosure status */
+ return (false);
+ } else
+ return (true);
+}
+
+static bool
+do_setencstat_cleanup(const char *devname __unused, int fd)
+{
+ unsigned char encstat;
+
+ /*
+ * SES provides no way to read the current setting of the enclosure
+ * control page common status bits. So we don't know what they were
+ * set to before the test. We'll blindly clear all bits.
+ */
+ encstat = 0;
+ ioctl(fd, ENCIOC_SETENCSTAT, (caddr_t) &encstat);
+ return (true);
+}
+
+ATF_TC_WITH_CLEANUP(setencstat);
+ATF_TC_HEAD(setencstat, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Exercise ENCIOC_SETENCSTAT");
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(setencstat, tc)
+{
+ if (!has_ses())
+ atf_tc_skip("No ses devices found");
+
+ for_each_ses_dev(do_setencstat, O_RDWR);
+}
+ATF_TC_CLEANUP(setencstat, tc)
+{
+ for_each_ses_dev(do_setencstat_cleanup, O_RDWR);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ /*
+ * Untested ioctls:
+ *
+ * * ENCIOC_INIT because SES doesn't need it and I don't have any
+ * SAF-TE devices.
+ *
+ * * ENCIOC_SETSTRING because it's seriously unsafe! It's normally
+ * used for stuff like firmware updates
+ */
+ ATF_TP_ADD_TC(tp, setelmstat);
+ ATF_TP_ADD_TC(tp, setencstat);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/ses/nondestructive.c b/tests/sys/ses/nondestructive.c
new file mode 100644
index 000000000000..ba3bea4ef992
--- /dev/null
+++ b/tests/sys/ses/nondestructive.c
@@ -0,0 +1,670 @@
+/*-
+ * Copyright (C) 2021 Axcient, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Basic smoke test of the ioctl interface */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <glob.h>
+#include <regex.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <cam/scsi/scsi_enc.h>
+
+#include "common.h"
+
+static bool
+do_getelmdesc(const char *devname, int fd)
+{
+ regex_t re;
+ FILE *pipe;
+ char cmd[256];
+ char line[256];
+ char *actual;
+ unsigned nobj;
+ unsigned elm_idx = 0;
+ int r;
+
+ actual = calloc(UINT16_MAX, sizeof(char));
+ ATF_REQUIRE(actual != NULL);
+ r = regcomp(&re, "(Overall|Element [0-9]+) descriptor: ", REG_EXTENDED);
+ ATF_REQUIRE_EQ(r, 0);
+
+ r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
+ ATF_REQUIRE_EQ(r, 0);
+
+ snprintf(cmd, sizeof(cmd), "sg_ses -p7 %s", devname);
+ pipe = popen(cmd, "r");
+ ATF_REQUIRE(pipe != NULL);
+ while(NULL != fgets(line, sizeof(line), pipe)) {
+ regmatch_t matches[1];
+ encioc_elm_desc_t e_desc;
+ char *expected;
+ size_t elen;
+
+ if (regexec(&re, line, 1, matches, 0) == REG_NOMATCH) {
+ continue;
+ }
+
+ expected = &line[matches[0].rm_eo];
+ /* Remove trailing newline */
+ elen = strnlen(expected, sizeof(line) - matches[0].rm_eo);
+ expected[elen - 1] = '\0';
+ /*
+ * Zero the result string. XXX we wouldn't have to do this if
+ * the kernel would nul-terminate the result.
+ */
+ memset(actual, 0, UINT16_MAX);
+ e_desc.elm_idx = elm_idx;
+ e_desc.elm_desc_len = UINT16_MAX;
+ e_desc.elm_desc_str = actual;
+ r = ioctl(fd, ENCIOC_GETELMDESC, (caddr_t) &e_desc);
+ ATF_REQUIRE_EQ(r, 0);
+ if (0 == strcmp("<empty>", expected)) {
+ /* sg_ses replaces "" with "<empty>" */
+ ATF_CHECK_STREQ("", actual);
+ } else
+ ATF_CHECK_STREQ(expected, actual);
+ elm_idx++;
+ }
+
+ r = pclose(pipe);
+ regfree(&re);
+ free(actual);
+ if (r != 0) {
+ /* Probably an SGPIO device */
+
+ return (false);
+ } else {
+ ATF_CHECK_EQ_MSG(nobj, elm_idx,
+ "Did not find the expected number of element "
+ "descriptors in sg_ses's output");
+ return (true);
+ }
+}
+
+ATF_TC(getelmdesc);
+ATF_TC_HEAD(getelmdesc, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Compare ENCIOC_GETELMDESC's output to sg3_utils'");
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.progs", "sg_ses");
+}
+ATF_TC_BODY(getelmdesc, tc)
+{
+ if (!has_ses())
+ atf_tc_skip("No ses devices found");
+ for_each_ses_dev(do_getelmdesc, O_RDONLY);
+}
+
+static bool
+do_getelmdevnames(const char *devname __unused, int fd)
+{
+ encioc_element_t *map;
+ unsigned nobj;
+ const size_t namesize = 128;
+ int r, status;
+ char *namebuf;
+ unsigned elm_idx;
+
+ r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
+ ATF_REQUIRE_EQ(r, 0);
+
+ namebuf = calloc(namesize, sizeof(char));
+ ATF_REQUIRE(namebuf != NULL);
+ map = calloc(nobj, sizeof(encioc_element_t));
+ ATF_REQUIRE(map != NULL);
+ r = ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) map);
+ ATF_REQUIRE_EQ(r, 0);
+
+ for (elm_idx = 0; elm_idx < nobj; elm_idx++) {
+ /*
+ * devnames should be present if:
+ * * The element is of type Device Slot or Array Device Slot
+ * * It isn't an Overall Element
+ * * The element's status is not "Not Installed"
+ */
+ encioc_elm_status_t e_status;
+ encioc_elm_devnames_t elmdn;
+
+ memset(&e_status, 0, sizeof(e_status));
+ e_status.elm_idx = elm_idx;
+ r = ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t)&e_status);
+ ATF_REQUIRE_EQ(r, 0);
+
+ memset(&elmdn, 0, sizeof(elmdn));
+ elmdn.elm_idx = elm_idx;
+ elmdn.elm_names_size = namesize;
+ elmdn.elm_devnames = namebuf;
+ namebuf[0] = '\0';
+ r = ioctl(fd, ENCIOC_GETELMDEVNAMES, (caddr_t) &elmdn);
+ status = ses_status_common_get_element_status_code(
+ (struct ses_status_common*)&e_status.cstat[0]);
+ if (status != SES_OBJSTAT_UNSUPPORTED &&
+ status != SES_OBJSTAT_NOTINSTALLED &&
+ (map[elm_idx].elm_type == ELMTYP_DEVICE ||
+ map[elm_idx].elm_type == ELMTYP_ARRAY_DEV))
+ {
+ ATF_CHECK_EQ_MSG(r, 0, "devnames not found. This could be due to a buggy ses driver, buggy ses controller, dead HDD, or an ATA HDD in a SAS slot");
+ } else {
+ ATF_CHECK(r != 0);
+ }
+
+ if (r == 0) {
+ size_t z = 0;
+ int da = 0, ada = 0, pass = 0, nda = 0, unknown = 0;
+
+ while(elmdn.elm_devnames[z] != '\0') {
+ size_t e;
+ char *s;
+
+ if (elmdn.elm_devnames[z] == ',')
+ z++; /* Skip the comma */
+ s = elmdn.elm_devnames + z;
+ e = strcspn(s, "0123456789");
+ if (0 == strncmp("da", s, e))
+ da++;
+ else if (0 == strncmp("ada", s, e))
+ ada++;
+ else if (0 == strncmp("pass", s, e))
+ pass++;
+ else if (0 == strncmp("nda", s, e))
+ nda++;
+ else
+ unknown++;
+ z += strcspn(elmdn.elm_devnames + z, ",");
+ }
+ /* There should be one pass dev for each non-pass dev */
+ ATF_CHECK_EQ(pass, da + ada + nda);
+ ATF_CHECK_EQ_MSG(0, unknown,
+ "Unknown device names %s", elmdn.elm_devnames);
+ }
+ }
+ free(map);
+ free(namebuf);
+
+ return (true);
+}
+
+ATF_TC(getelmdevnames);
+ATF_TC_HEAD(getelmdevnames, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Compare ENCIOC_GETELMDEVNAMES's output to sg3_utils'");
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.progs", "sg_ses");
+}
+ATF_TC_BODY(getelmdevnames, tc)
+{
+ if (!has_ses())
+ atf_tc_skip("No ses devices found");
+ for_each_ses_dev(do_getelmdevnames, O_RDONLY);
+}
+
+static int
+elm_type_name2int(const char *name)
+{
+ const char *elm_type_names[] = ELM_TYPE_NAMES;
+ int i;
+
+ for (i = 0; i <= ELMTYP_LAST; i++) {
+ /* sg_ses uses different case than ses(4) */
+ if (0 == strcasecmp(name, elm_type_names[i]))
+ return i;
+ }
+ return (-1);
+}
+
+static bool
+do_getelmmap(const char *devname, int fd)
+{
+ encioc_element_t *map;
+ FILE *pipe;
+ char cmd[256];
+ char line[256];
+ unsigned elm_idx = 0;
+ unsigned nobj, subenc_id;
+ int r, elm_type;
+
+ r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
+ ATF_REQUIRE_EQ(r, 0);
+
+ map = calloc(nobj, sizeof(encioc_element_t));
+ ATF_REQUIRE(map != NULL);
+ r = ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) map);
+ ATF_REQUIRE_EQ(r, 0);
+
+ snprintf(cmd, sizeof(cmd), "sg_ses -p1 %s", devname);
+ pipe = popen(cmd, "r");
+ ATF_REQUIRE(pipe != NULL);
+ while(NULL != fgets(line, sizeof(line), pipe)) {
+ char elm_type_name[80];
+ int i, num_elm;
+
+ r = sscanf(line,
+ " Element type: %[a-zA-Z0-9_ /], subenclosure id: %d",
+ elm_type_name, &subenc_id);
+ if (r == 2) {
+ elm_type = elm_type_name2int(elm_type_name);
+ continue;
+ } else {
+ r = sscanf(line,
+ " Element type: vendor specific [0x%x], subenclosure id: %d",
+ &elm_type, &subenc_id);
+ if (r == 2)
+ continue;
+ }
+ r = sscanf(line, " number of possible elements: %d",
+ &num_elm);
+ if (r != 1)
+ continue;
+
+ /* Skip the Overall elements */
+ elm_idx++;
+ for (i = 0; i < num_elm; i++, elm_idx++) {
+ ATF_CHECK_EQ(map[elm_idx].elm_idx, elm_idx);
+ ATF_CHECK_EQ(map[elm_idx].elm_subenc_id, subenc_id);
+ ATF_CHECK_EQ((int)map[elm_idx].elm_type, elm_type);
+ }
+ }
+
+ free(map);
+ r = pclose(pipe);
+ if (r != 0) {
+ /* Probably an SGPIO device */
+ return (false);
+ } else {
+ ATF_CHECK_EQ_MSG(nobj, elm_idx,
+ "Did not find the expected number of element "
+ "descriptors in sg_ses's output");
+ return (true);
+ }
+}
+
+ATF_TC(getelmmap);
+ATF_TC_HEAD(getelmmap, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Compare ENCIOC_GETELMMAP's output to sg3_utils'");
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.progs", "sg_ses");
+}
+ATF_TC_BODY(getelmmap, tc)
+{
+ if (!has_ses())
+ atf_tc_skip("No ses devices found");
+ for_each_ses_dev(do_getelmmap, O_RDONLY);
+}
+
+static bool
+do_getelmstat(const char *devname, int fd)
+{
+ encioc_element_t *map;
+ unsigned elm_idx;
+ unsigned nobj;
+ int r, elm_subidx;
+ elm_type_t last_elm_type = -1;
+
+ r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
+ ATF_REQUIRE_EQ(r, 0);
+
+ map = calloc(nobj, sizeof(encioc_element_t));
+ ATF_REQUIRE(map != NULL);
+ r = ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) map);
+
+ for (elm_idx = 0; elm_idx < nobj; elm_subidx++, elm_idx++) {
+ encioc_elm_status_t e_status;
+ FILE *pipe;
+ char cmd[256];
+ uint32_t status;
+ int pr;
+
+ if (last_elm_type != map[elm_idx].elm_type)
+ elm_subidx = -1;
+ last_elm_type = map[elm_idx].elm_type;
+
+ snprintf(cmd, sizeof(cmd),
+ "sg_ses -Hp2 --index=_%d,%d --get=0:7:32 %s",
+ map[elm_idx].elm_type, elm_subidx, devname);
+ pipe = popen(cmd, "r");
+ ATF_REQUIRE(pipe != NULL);
+ r = fscanf(pipe, "0x%x", &status);
+ pr = pclose(pipe);
+ if (pr != 0) {
+ /* Probably an SGPIO device */
+ free(map);
+ return (false);
+ }
+ ATF_REQUIRE_EQ(r, 1);
+
+ memset(&e_status, 0, sizeof(e_status));
+ e_status.elm_idx = map[elm_idx].elm_idx;
+ r = ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t)&e_status);
+ ATF_REQUIRE_EQ(r, 0);
+
+ // Compare the common status field
+ ATF_CHECK_EQ(e_status.cstat[0], status >> 24);
+ /*
+ * Ignore the other fields, because some have values that can
+ * change frequently (voltage, temperature, etc)
+ */
+ }
+ free(map);
+
+ return (true);
+}
+
+ATF_TC(getelmstat);
+ATF_TC_HEAD(getelmstat, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Compare ENCIOC_GETELMSTAT's output to sg3_utils'");
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.progs", "sg_ses");
+}
+ATF_TC_BODY(getelmstat, tc)
+{
+ if (!has_ses())
+ atf_tc_skip("No ses devices found");
+ for_each_ses_dev(do_getelmstat, O_RDONLY);
+}
+
+static bool
+do_getencid(const char *devname, int fd)
+{
+ encioc_string_t stri;
+ FILE *pipe;
+ char cmd[256];
+ char encid[32];
+ char line[256];
+ char sg_encid[32];
+ int r, sg_ses_r;
+
+ snprintf(cmd, sizeof(cmd), "sg_ses -p1 %s", devname);
+ pipe = popen(cmd, "r");
+ ATF_REQUIRE(pipe != NULL);
+ sg_encid[0] = '\0';
+ while(NULL != fgets(line, sizeof(line), pipe)) {
+ const char *f = " enclosure logical identifier (hex): %s";
+
+ if (1 == fscanf(pipe, f, sg_encid))
+ break;
+ }
+ sg_ses_r = pclose(pipe);
+
+ stri.bufsiz = sizeof(encid);
+ stri.buf = &encid[0];
+ r = ioctl(fd, ENCIOC_GETENCID, (caddr_t) &stri);
+ ATF_REQUIRE_EQ(r, 0);
+ if (sg_ses_r == 0) {
+ ATF_REQUIRE(sg_encid[0] != '\0');
+ ATF_CHECK_STREQ(sg_encid, (char*)stri.buf);
+ return (true);
+ } else {
+ /* Probably SGPIO; sg_ses unsupported */
+ return (false);
+ }
+}
+
+ATF_TC(getencid);
+ATF_TC_HEAD(getencid, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Compare ENCIOC_GETENCID's output to sg3_utils'");
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.progs", "sg_ses");
+}
+ATF_TC_BODY(getencid, tc)
+{
+ if (!has_ses())
+ atf_tc_skip("No ses devices found");
+ for_each_ses_dev(do_getencid, O_RDONLY);
+}
+
+static bool
+do_getencname(const char *devname, int fd)
+{
+ encioc_string_t stri;
+ FILE *pipe;
+ char cmd[256];
+ char encname[32];
+ char line[256];
+ int r;
+
+ snprintf(cmd, sizeof(cmd), "sg_inq -o %s | awk '"
+ "/Vendor identification/ {vi=$NF} "
+ "/Product identification/ {pi=$NF} "
+ "/Product revision level/ {prl=$NF} "
+ "END {printf(vi \" \" pi \" \" prl)}'", devname);
+ pipe = popen(cmd, "r");
+ ATF_REQUIRE(pipe != NULL);
+ ATF_REQUIRE(NULL != fgets(line, sizeof(line), pipe));
+ pclose(pipe);
+
+ stri.bufsiz = sizeof(encname);
+ stri.buf = &encname[0];
+ r = ioctl(fd, ENCIOC_GETENCNAME, (caddr_t) &stri);
+ ATF_REQUIRE_EQ(r, 0);
+ if (strlen(line) < 3) {
+ // Probably an SGPIO device, INQUIRY unsupported
+ return (false);
+ } else {
+ ATF_CHECK_STREQ(line, (char*)stri.buf);
+ return (true);
+ }
+}
+
+ATF_TC(getencname);
+ATF_TC_HEAD(getencname, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Compare ENCIOC_GETENCNAME's output to sg3_utils'");
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.progs", "sg_inq");
+}
+ATF_TC_BODY(getencname, tc)
+{
+ if (!has_ses())
+ atf_tc_skip("No ses devices found");
+ for_each_ses_dev(do_getencname, O_RDONLY);
+}
+
+static bool
+do_getencstat(const char *devname, int fd)
+{
+ FILE *pipe;
+ char cmd[256];
+ unsigned char e, estat, invop, info, noncrit, crit, unrecov;
+ int r;
+
+ snprintf(cmd, sizeof(cmd), "sg_ses -p2 %s "
+ "| grep 'INVOP='",
+ devname);
+ pipe = popen(cmd, "r");
+ ATF_REQUIRE(pipe != NULL);
+ r = fscanf(pipe,
+ " INVOP=%hhu, INFO=%hhu, NON-CRIT=%hhu, CRIT=%hhu, UNRECOV=%hhu",
+ &invop, &info, &noncrit, &crit, &unrecov);
+ pclose(pipe);
+ if (r != 5) {
+ /* Probably on SGPIO device */
+ return (false);
+ } else {
+ r = ioctl(fd, ENCIOC_GETENCSTAT, (caddr_t) &estat);
+ ATF_REQUIRE_EQ(r, 0);
+ /* Exclude the info bit because it changes frequently */
+ e = (invop << 4) | (noncrit << 2) | (crit << 1) | unrecov;
+ ATF_CHECK_EQ(estat & ~0x08, e);
+ return (true);
+ }
+}
+
+ATF_TC(getencstat);
+ATF_TC_HEAD(getencstat, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Compare ENCIOC_GETENCSTAT's output to sg3_utils'");
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.progs", "sg_ses");
+}
+ATF_TC_BODY(getencstat, tc)
+{
+ if (!has_ses())
+ atf_tc_skip("No ses devices found");
+ for_each_ses_dev(do_getencstat, O_RDONLY);
+}
+
+static bool
+do_getnelm(const char *devname, int fd)
+{
+ FILE *pipe;
+ char cmd[256];
+ char line[256];
+ unsigned nobj, expected = 0;
+ int r, sg_ses_r;
+
+ snprintf(cmd, sizeof(cmd), "sg_ses -p1 %s", devname);
+ pipe = popen(cmd, "r");
+ ATF_REQUIRE(pipe != NULL);
+
+ while(NULL != fgets(line, sizeof(line), pipe)) {
+ unsigned nelm;
+
+ if (1 == fscanf(pipe, " number of possible elements: %u",
+ &nelm))
+ {
+ expected += 1 + nelm; // +1 for the Overall element
+ }
+ }
+ sg_ses_r = pclose(pipe);
+
+ r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
+ ATF_REQUIRE_EQ(r, 0);
+ if (sg_ses_r == 0) {
+ ATF_CHECK_EQ(expected, nobj);
+ return (true);
+ } else {
+ /* Probably SGPIO, sg_ses unsupported */
+ return (false);
+ }
+}
+
+ATF_TC(getnelm);
+ATF_TC_HEAD(getnelm, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Compare ENCIOC_GETNELM's output to sg3_utils'");
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.progs", "sg_ses");
+}
+ATF_TC_BODY(getnelm, tc)
+{
+ if (!has_ses())
+ atf_tc_skip("No ses devices found");
+ for_each_ses_dev(do_getnelm, O_RDONLY);
+}
+
+static bool
+do_getstring(const char *devname, int fd)
+{
+ FILE *pipe;
+ char cmd[256];
+ char *sg_ses_buf, *ses_buf;
+ ssize_t sg_ses_count;
+ encioc_string_t str_in;
+ int r;
+
+ sg_ses_buf = malloc(65535);
+ ATF_REQUIRE(sg_ses_buf != NULL);
+ ses_buf = malloc(65535);
+ ATF_REQUIRE(ses_buf != NULL);
+
+ snprintf(cmd, sizeof(cmd), "sg_ses -p4 -rr %s", devname);
+ pipe = popen(cmd, "r");
+ ATF_REQUIRE(pipe != NULL);
+ sg_ses_count = fread(sg_ses_buf, 1, 65535, pipe);
+ r = pclose(pipe);
+ if (r != 0) {
+ // This SES device does not support the STRINGIN diagnostic page
+ return (false);
+ }
+ ATF_REQUIRE(sg_ses_count > 0);
+
+ str_in.bufsiz = 65535;
+ str_in.buf = ses_buf;
+ r = ioctl(fd, ENCIOC_GETSTRING, (caddr_t) &str_in);
+ ATF_REQUIRE_EQ(r, 0);
+ ATF_CHECK_EQ(sg_ses_count, (ssize_t)str_in.bufsiz);
+ ATF_CHECK_EQ(0, memcmp(sg_ses_buf, ses_buf, str_in.bufsiz));
+
+ free(ses_buf);
+ free(sg_ses_buf);
+
+ return (true);
+}
+
+ATF_TC(getstring);
+ATF_TC_HEAD(getstring, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Compare ENCIOC_GETSTRING's output to sg3_utils'");
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "require.progs", "sg_ses");
+}
+ATF_TC_BODY(getstring, tc)
+{
+ if (!has_ses())
+ atf_tc_skip("No ses devices found");
+ atf_tc_expect_fail("Bug 258188 ENCIO_GETSTRING does not set the string's returned size");
+ for_each_ses_dev(do_getstring, O_RDWR);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ /*
+ * Untested ioctls:
+ *
+ * * ENCIOC_GETTEXT because it was never implemented
+ *
+ */
+ ATF_TP_ADD_TC(tp, getelmdesc);
+ ATF_TP_ADD_TC(tp, getelmdevnames);
+ ATF_TP_ADD_TC(tp, getelmmap);
+ ATF_TP_ADD_TC(tp, getelmstat);
+ ATF_TP_ADD_TC(tp, getencid);
+ ATF_TP_ADD_TC(tp, getencname);
+ ATF_TP_ADD_TC(tp, getencstat);
+ ATF_TP_ADD_TC(tp, getnelm);
+ ATF_TP_ADD_TC(tp, getstring);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/sound/Makefile b/tests/sys/sound/Makefile
new file mode 100644
index 000000000000..74a0765a0540
--- /dev/null
+++ b/tests/sys/sound/Makefile
@@ -0,0 +1,13 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/sound
+
+ATF_TESTS_C+= pcm_read_write
+ATF_TESTS_C+= sndstat
+
+CFLAGS+= -I${SRCTOP}/sys
+LDFLAGS+= -lnv
+
+CFLAGS.pcm_read_write.c+= -Wno-cast-align
+
+.include <bsd.test.mk>
diff --git a/tests/sys/sound/pcm_read_write.c b/tests/sys/sound/pcm_read_write.c
new file mode 100644
index 000000000000..a77b953a78a0
--- /dev/null
+++ b/tests/sys/sound/pcm_read_write.c
@@ -0,0 +1,262 @@
+/*-
+ * Copyright (c) 2025 Florian Walpen
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+/*
+ * These tests exercise conversion functions of the sound module, used to read
+ * pcm samples from a buffer, and write pcm samples to a buffer. The test cases
+ * are non-exhaustive, but should detect systematic errors in conversion of the
+ * various sample formats supported. In particular, the test cases establish
+ * correctness independent of the machine's endianness, making them suitable to
+ * check for architecture-specific problems.
+ */
+
+#include <sys/types.h>
+#include <sys/soundcard.h>
+
+#include <atf-c.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/pcm/pcm.h>
+#include <dev/sound/pcm/g711.h>
+
+/* Generic test data, with buffer content matching the sample values. */
+static struct afmt_test_data {
+ const char *label;
+ uint8_t buffer[4];
+ size_t size;
+ int format;
+ intpcm_t value;
+ _Static_assert((sizeof(intpcm_t) == 4),
+ "Test data assumes 32bit, adjust negative values to new size.");
+} const afmt_tests[] = {
+ /* 8 bit sample formats. */
+ {"s8_1", {0x01, 0x00, 0x00, 0x00}, 1, AFMT_S8, 0x00000001},
+ {"s8_2", {0x81, 0x00, 0x00, 0x00}, 1, AFMT_S8, 0xffffff81},
+ {"u8_1", {0x01, 0x00, 0x00, 0x00}, 1, AFMT_U8, 0xffffff81},
+ {"u8_2", {0x81, 0x00, 0x00, 0x00}, 1, AFMT_U8, 0x00000001},
+
+ /* 16 bit sample formats. */
+ {"s16le_1", {0x01, 0x02, 0x00, 0x00}, 2, AFMT_S16_LE, 0x00000201},
+ {"s16le_2", {0x81, 0x82, 0x00, 0x00}, 2, AFMT_S16_LE, 0xffff8281},
+ {"s16be_1", {0x01, 0x02, 0x00, 0x00}, 2, AFMT_S16_BE, 0x00000102},
+ {"s16be_2", {0x81, 0x82, 0x00, 0x00}, 2, AFMT_S16_BE, 0xffff8182},
+ {"u16le_1", {0x01, 0x02, 0x00, 0x00}, 2, AFMT_U16_LE, 0xffff8201},
+ {"u16le_2", {0x81, 0x82, 0x00, 0x00}, 2, AFMT_U16_LE, 0x00000281},
+ {"u16be_1", {0x01, 0x02, 0x00, 0x00}, 2, AFMT_U16_BE, 0xffff8102},
+ {"u16be_2", {0x81, 0x82, 0x00, 0x00}, 2, AFMT_U16_BE, 0x00000182},
+
+ /* 24 bit sample formats. */
+ {"s24le_1", {0x01, 0x02, 0x03, 0x00}, 3, AFMT_S24_LE, 0x00030201},
+ {"s24le_2", {0x81, 0x82, 0x83, 0x00}, 3, AFMT_S24_LE, 0xff838281},
+ {"s24be_1", {0x01, 0x02, 0x03, 0x00}, 3, AFMT_S24_BE, 0x00010203},
+ {"s24be_2", {0x81, 0x82, 0x83, 0x00}, 3, AFMT_S24_BE, 0xff818283},
+ {"u24le_1", {0x01, 0x02, 0x03, 0x00}, 3, AFMT_U24_LE, 0xff830201},
+ {"u24le_2", {0x81, 0x82, 0x83, 0x00}, 3, AFMT_U24_LE, 0x00038281},
+ {"u24be_1", {0x01, 0x02, 0x03, 0x00}, 3, AFMT_U24_BE, 0xff810203},
+ {"u24be_2", {0x81, 0x82, 0x83, 0x00}, 3, AFMT_U24_BE, 0x00018283},
+
+ /* 32 bit sample formats. */
+ {"s32le_1", {0x01, 0x02, 0x03, 0x04}, 4, AFMT_S32_LE, 0x04030201},
+ {"s32le_2", {0x81, 0x82, 0x83, 0x84}, 4, AFMT_S32_LE, 0x84838281},
+ {"s32be_1", {0x01, 0x02, 0x03, 0x04}, 4, AFMT_S32_BE, 0x01020304},
+ {"s32be_2", {0x81, 0x82, 0x83, 0x84}, 4, AFMT_S32_BE, 0x81828384},
+ {"u32le_1", {0x01, 0x02, 0x03, 0x04}, 4, AFMT_U32_LE, 0x84030201},
+ {"u32le_2", {0x81, 0x82, 0x83, 0x84}, 4, AFMT_U32_LE, 0x04838281},
+ {"u32be_1", {0x01, 0x02, 0x03, 0x04}, 4, AFMT_U32_BE, 0x81020304},
+ {"u32be_2", {0x81, 0x82, 0x83, 0x84}, 4, AFMT_U32_BE, 0x01828384},
+
+ /* 32 bit floating point sample formats. */
+ {"f32le_1", {0x00, 0x00, 0x00, 0x3f}, 4, AFMT_F32_LE, 0x40000000},
+ {"f32le_2", {0x00, 0x00, 0x00, 0xbf}, 4, AFMT_F32_LE, 0xc0000000},
+ {"f32be_1", {0x3f, 0x00, 0x00, 0x00}, 4, AFMT_F32_BE, 0x40000000},
+ {"f32be_2", {0xbf, 0x00, 0x00, 0x00}, 4, AFMT_F32_BE, 0xc0000000},
+
+ /* u-law and A-law sample formats. */
+ {"mulaw_1", {0x01, 0x00, 0x00, 0x00}, 1, AFMT_MU_LAW, 0xffffff87},
+ {"mulaw_2", {0x81, 0x00, 0x00, 0x00}, 1, AFMT_MU_LAW, 0x00000079},
+ {"alaw_1", {0x2a, 0x00, 0x00, 0x00}, 1, AFMT_A_LAW, 0xffffff83},
+ {"alaw_2", {0xab, 0x00, 0x00, 0x00}, 1, AFMT_A_LAW, 0x00000079}
+};
+
+/* Normalize sample values in strictly correct (but slow) c. */
+static intpcm_t
+local_normalize(intpcm_t value, int val_bits, int norm_bits)
+{
+ int32_t divisor;
+ intpcm_t remainder;
+
+ /* Avoid undefined or implementation defined behavior. */
+ if (val_bits < norm_bits)
+ /* Multiply instead of left shift (value may be negative). */
+ return (value * (1 << (norm_bits - val_bits)));
+ else if (val_bits > norm_bits) {
+ divisor = (1 << (val_bits - norm_bits));
+ /* Positive remainder, to discard lowest bits from value. */
+ remainder = value % divisor;
+ remainder = (remainder + divisor) % divisor;
+ /* Divide instead of right shift (value may be negative). */
+ return ((value - remainder) / divisor);
+ }
+ return value;
+}
+
+/* Restrict magnitude of sample value to 24bit for 32bit calculations. */
+static intpcm_t
+local_calc_limit(intpcm_t value, int val_bits)
+{
+ /*
+ * When intpcm32_t is defined to be 32bit, calculations for mixing and
+ * volume changes use 32bit integers instead of 64bit. To get some
+ * headroom for calculations, 32bit sample values are restricted to
+ * 24bit magnitude in that case. Also avoid implementation defined
+ * behavior here.
+ */
+ if (sizeof(intpcm32_t) == (32 / 8) && val_bits == 32)
+ return (local_normalize(value, 32, 24));
+ return value;
+}
+
+ATF_TC(pcm_read);
+ATF_TC_HEAD(pcm_read, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Read and verify different pcm sample formats.");
+}
+ATF_TC_BODY(pcm_read, tc)
+{
+ const struct afmt_test_data *test;
+ uint8_t src[4];
+ intpcm_t expected, result;
+ size_t i;
+
+ for (i = 0; i < nitems(afmt_tests); i++) {
+ test = &afmt_tests[i];
+
+ /* Copy byte representation, fill with distinctive pattern. */
+ memset(src, 0x66, sizeof(src));
+ memcpy(src, test->buffer, test->size);
+
+ /* Read sample at format magnitude. */
+ expected = test->value;
+ result = pcm_sample_read(src, test->format);
+ ATF_CHECK_MSG(result == expected,
+ "pcm_read[\"%s\"].value: expected=0x%08x, result=0x%08x",
+ test->label, expected, result);
+
+ /* Read sample at format magnitude, for calculations. */
+ expected = local_calc_limit(test->value, test->size * 8);
+ result = pcm_sample_read_calc(src, test->format);
+ ATF_CHECK_MSG(result == expected,
+ "pcm_read[\"%s\"].calc: expected=0x%08x, result=0x%08x",
+ test->label, expected, result);
+
+ /* Read sample at full 32 bit magnitude. */
+ expected = local_normalize(test->value, test->size * 8, 32);
+ result = pcm_sample_read_norm(src, test->format);
+ ATF_CHECK_MSG(result == expected,
+ "pcm_read[\"%s\"].norm: expected=0x%08x, result=0x%08x",
+ test->label, expected, result);
+ }
+}
+
+ATF_TC(pcm_write);
+ATF_TC_HEAD(pcm_write, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Write and verify different pcm sample formats.");
+}
+ATF_TC_BODY(pcm_write, tc)
+{
+ const struct afmt_test_data *test;
+ uint8_t expected[4];
+ uint8_t dst[4];
+ intpcm_t value;
+ size_t i;
+
+ for (i = 0; i < nitems(afmt_tests); i++) {
+ test = &afmt_tests[i];
+
+ /* Write sample of format specific magnitude. */
+ memcpy(expected, test->buffer, sizeof(expected));
+ memset(dst, 0x00, sizeof(dst));
+ value = test->value;
+ pcm_sample_write(dst, value, test->format);
+ ATF_CHECK_MSG(memcmp(dst, expected, sizeof(dst)) == 0,
+ "pcm_write[\"%s\"].value: "
+ "expected={0x%02x, 0x%02x, 0x%02x, 0x%02x}, "
+ "result={0x%02x, 0x%02x, 0x%02x, 0x%02x}, ", test->label,
+ expected[0], expected[1], expected[2], expected[3],
+ dst[0], dst[1], dst[2], dst[3]);
+
+ /* Write sample of format specific, calculation magnitude. */
+ memcpy(expected, test->buffer, sizeof(expected));
+ memset(dst, 0x00, sizeof(dst));
+ value = local_calc_limit(test->value, test->size * 8);
+ if (value != test->value) {
+ /*
+ * 32 bit sample was reduced to 24 bit resolution
+ * for calculation, least significant byte is lost.
+ */
+ if (test->format & AFMT_BIGENDIAN)
+ expected[3] = 0x00;
+ else
+ expected[0] = 0x00;
+ }
+ pcm_sample_write_calc(dst, value, test->format);
+ ATF_CHECK_MSG(memcmp(dst, expected, sizeof(dst)) == 0,
+ "pcm_write[\"%s\"].calc: "
+ "expected={0x%02x, 0x%02x, 0x%02x, 0x%02x}, "
+ "result={0x%02x, 0x%02x, 0x%02x, 0x%02x}, ", test->label,
+ expected[0], expected[1], expected[2], expected[3],
+ dst[0], dst[1], dst[2], dst[3]);
+
+ /* Write normalized sample of full 32 bit magnitude. */
+ memcpy(expected, test->buffer, sizeof(expected));
+ memset(dst, 0x00, sizeof(dst));
+ value = local_normalize(test->value, test->size * 8, 32);
+ pcm_sample_write_norm(dst, value, test->format);
+ ATF_CHECK_MSG(memcmp(dst, expected, sizeof(dst)) == 0,
+ "pcm_write[\"%s\"].norm: "
+ "expected={0x%02x, 0x%02x, 0x%02x, 0x%02x}, "
+ "result={0x%02x, 0x%02x, 0x%02x, 0x%02x}, ", test->label,
+ expected[0], expected[1], expected[2], expected[3],
+ dst[0], dst[1], dst[2], dst[3]);
+ }
+}
+
+ATF_TC(pcm_format_bits);
+ATF_TC_HEAD(pcm_format_bits, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify bit width of different pcm sample formats.");
+}
+ATF_TC_BODY(pcm_format_bits, tc)
+{
+ const struct afmt_test_data *test;
+ size_t bits;
+ size_t i;
+
+ for (i = 0; i < nitems(afmt_tests); i++) {
+ test = &afmt_tests[i];
+
+ /* Check bit width determined for given sample format. */
+ bits = AFMT_BIT(test->format);
+ ATF_CHECK_MSG(bits == test->size * 8,
+ "format_bits[%zu].size: expected=%zu, result=%zu",
+ i, test->size * 8, bits);
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, pcm_read);
+ ATF_TP_ADD_TC(tp, pcm_write);
+ ATF_TP_ADD_TC(tp, pcm_format_bits);
+
+ return atf_no_error();
+}
diff --git a/tests/sys/sound/sndstat.c b/tests/sys/sound/sndstat.c
new file mode 100644
index 000000000000..ed292b570429
--- /dev/null
+++ b/tests/sys/sound/sndstat.c
@@ -0,0 +1,395 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 The FreeBSD Foundation
+ *
+ * This software was developed by Christos Margiolis <christos@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <sys/nv.h>
+#include <sys/sndstat.h>
+#include <sys/soundcard.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static void
+load_dummy(void)
+{
+ if (kldload("snd_dummy.ko") < 0 && errno != EEXIST)
+ atf_tc_skip("snd_dummy.ko not found");
+}
+
+ATF_TC(sndstat_nv);
+ATF_TC_HEAD(sndstat_nv, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "/dev/sndstat nvlist test");
+}
+
+ATF_TC_BODY(sndstat_nv, tc)
+{
+ nvlist_t *nvl;
+ const nvlist_t * const *di;
+ const nvlist_t * const *cdi;
+ struct sndstioc_nv_arg arg;
+ size_t nitems, nchans, i, j;
+ int fd, rc, pchan, rchan;
+
+ load_dummy();
+
+ if ((fd = open("/dev/sndstat", O_RDONLY)) < 0)
+ atf_tc_skip("/dev/sndstat not found, load sound(4)");
+
+ rc = ioctl(fd, SNDSTIOC_REFRESH_DEVS, NULL);
+ ATF_REQUIRE_EQ(rc, 0);
+
+ arg.nbytes = 0;
+ arg.buf = NULL;
+ rc = ioctl(fd, SNDSTIOC_GET_DEVS, &arg);
+ ATF_REQUIRE_EQ_MSG(rc, 0, "ioctl(SNDSTIOC_GET_DEVS#1) failed");
+
+ arg.buf = malloc(arg.nbytes);
+ ATF_REQUIRE(arg.buf != NULL);
+
+ rc = ioctl(fd, SNDSTIOC_GET_DEVS, &arg);
+ ATF_REQUIRE_EQ_MSG(rc, 0, "ioctl(SNDSTIOC_GET_DEVS#2) failed");
+
+ nvl = nvlist_unpack(arg.buf, arg.nbytes, 0);
+ ATF_REQUIRE(nvl != NULL);
+
+ if (nvlist_empty(nvl) || !nvlist_exists(nvl, SNDST_DSPS))
+ atf_tc_skip("no soundcards attached");
+
+ di = nvlist_get_nvlist_array(nvl, SNDST_DSPS, &nitems);
+ for (i = 0; i < nitems; i++) {
+#define NV(type, item) do { \
+ ATF_REQUIRE_MSG(nvlist_exists(di[i], SNDST_DSPS_ ## item), \
+ "SNDST_DSPS_" #item " does not exist"); \
+ nvlist_get_ ## type (di[i], SNDST_DSPS_ ## item); \
+} while (0)
+ NV(string, NAMEUNIT);
+ NV(bool, FROM_USER);
+ NV(string, DEVNODE);
+ NV(string, DESC);
+ NV(string, PROVIDER);
+ NV(number, PCHAN);
+ NV(number, RCHAN);
+#undef NV
+
+ /* Cannot asign using the macro. */
+ pchan = nvlist_get_number(di[i], SNDST_DSPS_PCHAN);
+ rchan = nvlist_get_number(di[i], SNDST_DSPS_RCHAN);
+
+ if (pchan && !nvlist_exists(di[i], SNDST_DSPS_INFO_PLAY))
+ atf_tc_fail("playback channel list empty");
+ if (rchan && !nvlist_exists(di[i], SNDST_DSPS_INFO_REC))
+ atf_tc_fail("recording channel list empty");
+
+#define NV(type, mode, item) do { \
+ ATF_REQUIRE_MSG(nvlist_exists(nvlist_get_nvlist(di[i], \
+ SNDST_DSPS_INFO_ ## mode), SNDST_DSPS_INFO_ ## item), \
+ "SNDST_DSPS_INFO_" #item " does not exist"); \
+ nvlist_get_ ## type (nvlist_get_nvlist(di[i], \
+ SNDST_DSPS_INFO_ ## mode), SNDST_DSPS_INFO_ ## item); \
+} while (0)
+ if (pchan) {
+ NV(number, PLAY, MIN_RATE);
+ NV(number, PLAY, MAX_RATE);
+ NV(number, PLAY, FORMATS);
+ NV(number, PLAY, MIN_CHN);
+ NV(number, PLAY, MAX_CHN);
+ }
+ if (rchan) {
+ NV(number, REC, MIN_RATE);
+ NV(number, REC, MAX_RATE);
+ NV(number, REC, FORMATS);
+ NV(number, REC, MIN_CHN);
+ NV(number, REC, MAX_CHN);
+ }
+#undef NV
+
+ if (!nvlist_exists(di[i], SNDST_DSPS_PROVIDER_INFO))
+ continue;
+
+#define NV(type, item) do { \
+ ATF_REQUIRE_MSG(nvlist_exists(nvlist_get_nvlist(di[i], \
+ SNDST_DSPS_PROVIDER_INFO), SNDST_DSPS_SOUND4_ ## item), \
+ "SNDST_DSPS_SOUND4_" #item " does not exist"); \
+ nvlist_get_ ## type (nvlist_get_nvlist(di[i], \
+ SNDST_DSPS_PROVIDER_INFO), SNDST_DSPS_SOUND4_ ## item); \
+} while (0)
+ NV(number, UNIT);
+ NV(string, STATUS);
+ NV(bool, BITPERFECT);
+ NV(bool, PVCHAN);
+ NV(number, PVCHANRATE);
+ NV(number, PVCHANFORMAT);
+ NV(bool, RVCHAN);
+ NV(number, PVCHANRATE);
+ NV(number, PVCHANFORMAT);
+#undef NV
+
+ if (!nvlist_exists(nvlist_get_nvlist(di[i],
+ SNDST_DSPS_PROVIDER_INFO), SNDST_DSPS_SOUND4_CHAN_INFO))
+ atf_tc_fail("channel info list empty");
+
+ cdi = nvlist_get_nvlist_array(
+ nvlist_get_nvlist(di[i], SNDST_DSPS_PROVIDER_INFO),
+ SNDST_DSPS_SOUND4_CHAN_INFO, &nchans);
+ for (j = 0; j < nchans; j++) {
+#define NV(type, item) do { \
+ ATF_REQUIRE_MSG(nvlist_exists(cdi[j], SNDST_DSPS_SOUND4_CHAN_ ## item), \
+ "SNDST_DSPS_SOUND4_CHAN_" #item " does not exist"); \
+ nvlist_get_ ## type (cdi[j], SNDST_DSPS_SOUND4_CHAN_ ## item); \
+} while (0)
+ NV(string, NAME);
+ NV(string, PARENTCHAN);
+ NV(number, UNIT);
+ NV(number, CAPS);
+ NV(number, LATENCY);
+ NV(number, RATE);
+ NV(number, FORMAT);
+ NV(number, PID);
+ NV(string, COMM);
+ NV(number, INTR);
+ NV(number, XRUNS);
+ NV(number, FEEDCNT);
+ NV(number, LEFTVOL);
+ NV(number, RIGHTVOL);
+ NV(number, HWBUF_FORMAT);
+ NV(number, HWBUF_RATE);
+ NV(number, HWBUF_SIZE);
+ NV(number, HWBUF_BLKSZ);
+ NV(number, HWBUF_BLKCNT);
+ NV(number, HWBUF_FREE);
+ NV(number, HWBUF_READY);
+ NV(number, SWBUF_FORMAT);
+ NV(number, SWBUF_RATE);
+ NV(number, SWBUF_SIZE);
+ NV(number, SWBUF_BLKSZ);
+ NV(number, SWBUF_BLKCNT);
+ NV(number, SWBUF_FREE);
+ NV(number, SWBUF_READY);
+ NV(string, FEEDERCHAIN);
+#undef NV
+ }
+ }
+
+ free(arg.buf);
+ nvlist_destroy(nvl);
+ close(fd);
+}
+
+#define UDEV_PROVIDER "sndstat_udev"
+#define UDEV_NAMEUNIT "sndstat_udev"
+#define UDEV_DEVNODE "sndstat_udev"
+#define UDEV_DESC "Test Device"
+#define UDEV_PCHAN 1
+#define UDEV_RCHAN 1
+#define UDEV_MIN_RATE 8000
+#define UDEV_MAX_RATE 96000
+#define UDEV_FORMATS (AFMT_S16_NE | AFMT_S24_NE | AFMT_S32_NE)
+#define UDEV_MIN_CHN 1
+#define UDEV_MAX_CHN 2
+
+ATF_TC(sndstat_udev);
+ATF_TC_HEAD(sndstat_udev, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "/dev/sndstat userdev interface test");
+}
+
+ATF_TC_BODY(sndstat_udev, tc)
+{
+ nvlist_t *nvl, *di, *dichild;
+ const nvlist_t * const *rdi;
+ struct sndstioc_nv_arg arg;
+ const char *str;
+ size_t nitems, i;
+ int fd, rc, pchan, rchan, n;
+
+ load_dummy();
+
+ if ((fd = open("/dev/sndstat", O_RDWR)) < 0)
+ atf_tc_skip("/dev/sndstat not found, load sound(4)");
+
+ nvl = nvlist_create(0);
+ ATF_REQUIRE(nvl != NULL);
+
+ di = nvlist_create(0);
+ ATF_REQUIRE(di != NULL);
+
+ dichild = nvlist_create(0);
+ ATF_REQUIRE(dichild != NULL);
+
+ nvlist_add_string(di, SNDST_DSPS_PROVIDER, UDEV_PROVIDER);
+ nvlist_add_string(di, SNDST_DSPS_NAMEUNIT, UDEV_NAMEUNIT);
+ nvlist_add_string(di, SNDST_DSPS_DESC, UDEV_DESC);
+ nvlist_add_string(di, SNDST_DSPS_DEVNODE, UDEV_DEVNODE);
+ nvlist_add_number(di, SNDST_DSPS_PCHAN, UDEV_PCHAN);
+ nvlist_add_number(di, SNDST_DSPS_RCHAN, UDEV_RCHAN);
+
+ nvlist_add_number(dichild, SNDST_DSPS_INFO_MIN_RATE, UDEV_MIN_RATE);
+ nvlist_add_number(dichild, SNDST_DSPS_INFO_MAX_RATE, UDEV_MAX_RATE);
+ nvlist_add_number(dichild, SNDST_DSPS_INFO_FORMATS, UDEV_FORMATS);
+ nvlist_add_number(dichild, SNDST_DSPS_INFO_MIN_CHN, UDEV_MIN_CHN);
+ nvlist_add_number(dichild, SNDST_DSPS_INFO_MAX_CHN, UDEV_MAX_CHN);
+
+ nvlist_add_nvlist(di, SNDST_DSPS_INFO_PLAY, dichild);
+ nvlist_add_nvlist(di, SNDST_DSPS_INFO_REC, dichild);
+
+ nvlist_append_nvlist_array(nvl, SNDST_DSPS, di);
+ ATF_REQUIRE_EQ(nvlist_error(nvl), 0);
+
+ arg.buf = nvlist_pack(nvl, &arg.nbytes);
+ ATF_REQUIRE_MSG(arg.buf != NULL, "failed to pack nvlist");
+
+ rc = ioctl(fd, SNDSTIOC_ADD_USER_DEVS, &arg);
+ free(arg.buf);
+ ATF_REQUIRE_EQ_MSG(rc, 0, "ioctl(SNDSTIOC_ADD_USER_DEVS) failed");
+
+ nvlist_destroy(di);
+ nvlist_destroy(dichild);
+ nvlist_destroy(nvl);
+
+ /* Read back registered values. */
+ rc = ioctl(fd, SNDSTIOC_REFRESH_DEVS, NULL);
+ ATF_REQUIRE_EQ(rc, 0);
+
+ arg.nbytes = 0;
+ arg.buf = NULL;
+ rc = ioctl(fd, SNDSTIOC_GET_DEVS, &arg);
+ ATF_REQUIRE_EQ_MSG(rc, 0, "ioctl(SNDSTIOC_GET_DEVS#1) failed");
+
+ arg.buf = malloc(arg.nbytes);
+ ATF_REQUIRE(arg.buf != NULL);
+
+ rc = ioctl(fd, SNDSTIOC_GET_DEVS, &arg);
+ ATF_REQUIRE_EQ_MSG(rc, 0, "ioctl(SNDSTIOC_GET_DEVS#2) failed");
+
+ nvl = nvlist_unpack(arg.buf, arg.nbytes, 0);
+ ATF_REQUIRE(nvl != NULL);
+
+ if (nvlist_empty(nvl) || !nvlist_exists(nvl, SNDST_DSPS))
+ atf_tc_skip("no soundcards attached");
+
+ rdi = nvlist_get_nvlist_array(nvl, SNDST_DSPS, &nitems);
+ for (i = 0; i < nitems; i++) {
+#define NV(type, item, var) do { \
+ ATF_REQUIRE_MSG(nvlist_exists(rdi[i], SNDST_DSPS_ ## item), \
+ "SNDST_DSPS_" #item " does not exist"); \
+ var = nvlist_get_ ## type (rdi[i], SNDST_DSPS_ ## item); \
+} while (0)
+ /* Search for our device. */
+ NV(string, NAMEUNIT, str);
+ if (strcmp(str, UDEV_NAMEUNIT) == 0)
+ break;
+ }
+ if (i == nitems)
+ atf_tc_fail("userland device %s not found", UDEV_NAMEUNIT);
+
+ NV(string, NAMEUNIT, str);
+ ATF_CHECK(strcmp(str, UDEV_NAMEUNIT) == 0);
+
+ NV(bool, FROM_USER, n);
+ ATF_CHECK(n);
+
+ NV(string, DEVNODE, str);
+ ATF_CHECK(strcmp(str, UDEV_DEVNODE) == 0);
+
+ NV(string, DESC, str);
+ ATF_CHECK(strcmp(str, UDEV_DESC) == 0);
+
+ NV(string, PROVIDER, str);
+ ATF_CHECK(strcmp(str, UDEV_PROVIDER) == 0);
+
+ NV(number, PCHAN, pchan);
+ ATF_CHECK(pchan == UDEV_PCHAN);
+ if (pchan && !nvlist_exists(rdi[i], SNDST_DSPS_INFO_PLAY))
+ atf_tc_fail("playback channel list empty");
+
+ NV(number, RCHAN, rchan);
+ ATF_CHECK(rchan == UDEV_RCHAN);
+ if (rchan && !nvlist_exists(rdi[i], SNDST_DSPS_INFO_REC))
+ atf_tc_fail("recording channel list empty");
+#undef NV
+
+#define NV(type, mode, item, var) do { \
+ ATF_REQUIRE_MSG(nvlist_exists(nvlist_get_nvlist(rdi[i], \
+ SNDST_DSPS_INFO_ ## mode), SNDST_DSPS_INFO_ ## item), \
+ "SNDST_DSPS_INFO_" #item " does not exist"); \
+ var = nvlist_get_ ## type (nvlist_get_nvlist(rdi[i], \
+ SNDST_DSPS_INFO_ ## mode), SNDST_DSPS_INFO_ ## item); \
+} while (0)
+ if (pchan) {
+ NV(number, PLAY, MIN_RATE, n);
+ ATF_CHECK(n == UDEV_MIN_RATE);
+
+ NV(number, PLAY, MAX_RATE, n);
+ ATF_CHECK(n == UDEV_MAX_RATE);
+
+ NV(number, PLAY, FORMATS, n);
+ ATF_CHECK(n == UDEV_FORMATS);
+
+ NV(number, PLAY, MIN_CHN, n);
+ ATF_CHECK(n == UDEV_MIN_CHN);
+
+ NV(number, PLAY, MAX_CHN, n);
+ ATF_CHECK(n == UDEV_MAX_CHN);
+ }
+ if (rchan) {
+ NV(number, REC, MIN_RATE, n);
+ ATF_CHECK(n == UDEV_MIN_RATE);
+
+ NV(number, REC, MAX_RATE, n);
+ ATF_CHECK(n == UDEV_MAX_RATE);
+
+ NV(number, REC, FORMATS, n);
+ ATF_CHECK(n == UDEV_FORMATS);
+
+ NV(number, REC, MIN_CHN, n);
+ ATF_CHECK(n == UDEV_MIN_CHN);
+
+ NV(number, REC, MAX_CHN, n);
+ ATF_CHECK(n == UDEV_MAX_CHN);
+ }
+#undef NV
+
+ free(arg.buf);
+ nvlist_destroy(nvl);
+ close(fd);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, sndstat_nv);
+ ATF_TP_ADD_TC(tp, sndstat_udev);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/sys/Makefile b/tests/sys/sys/Makefile
new file mode 100644
index 000000000000..a1b4e3234e1c
--- /dev/null
+++ b/tests/sys/sys/Makefile
@@ -0,0 +1,19 @@
+.include <bsd.compiler.mk>
+
+TESTSDIR= ${TESTSBASE}/sys/sys
+
+ATF_TESTS_C= arb_test \
+ bitset_test \
+ bitstring_test \
+ buf_ring_test \
+ qmath_test \
+ queue_test \
+ rb_test \
+ splay_test \
+ time_test
+
+.if ${COMPILER_TYPE} == "gcc"
+CFLAGS.bitstring_test= -fno-strict-overflow
+.endif
+
+.include <bsd.test.mk>
diff --git a/tests/sys/sys/Makefile.depend b/tests/sys/sys/Makefile.depend
new file mode 100644
index 000000000000..1af0c88e099c
--- /dev/null
+++ b/tests/sys/sys/Makefile.depend
@@ -0,0 +1,17 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/sys/arb_test.c b/tests/sys/sys/arb_test.c
new file mode 100644
index 000000000000..8f002897a0ed
--- /dev/null
+++ b/tests/sys/sys/arb_test.c
@@ -0,0 +1,113 @@
+/* $OpenBSD: rb-test.c,v 1.4 2008/04/13 00:22:17 djm Exp $ */
+/*
+ * Copyright 2019 Edward Tomasz Napierala <trasz@FreeBSD.org>
+ * Copyright 2002 Niels Provos <provos@citi.umich.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/types.h>
+
+#include <sys/arb.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <atf-c.h>
+
+struct node {
+ ARB32_ENTRY() next;
+ int key;
+};
+
+static ARB32_HEAD(tree, node) *root;
+
+static int
+compare(const struct node *a, const struct node *b)
+{
+ if (a->key < b->key) return (-1);
+ else if (a->key > b->key) return (1);
+ return (0);
+}
+
+ARB_PROTOTYPE(tree, node, next, compare);
+
+ARB_GENERATE(tree, node, next, compare);
+
+#define ITER 150
+#define MIN 5
+#define MAX 5000
+
+ATF_TC_WITHOUT_HEAD(arb_test);
+ATF_TC_BODY(arb_test, tc)
+{
+ struct node *tmp, *ins;
+ int i, max, min;
+
+ max = min = 42; /* pacify gcc */
+
+ root = (struct tree *)calloc(1, ARB_ALLOCSIZE(root, ITER, tmp));
+
+ ARB_INIT(tmp, next, root, ITER);
+
+ for (i = 0; i < ITER; i++) {
+ tmp = ARB_GETFREE(root, next);
+ ATF_REQUIRE_MSG(tmp != NULL, "ARB_GETFREE failed");
+ do {
+ tmp->key = arc4random_uniform(MAX-MIN);
+ tmp->key += MIN;
+ } while (ARB_FIND(tree, root, tmp) != NULL);
+ if (i == 0)
+ max = min = tmp->key;
+ else {
+ if (tmp->key > max)
+ max = tmp->key;
+ if (tmp->key < min)
+ min = tmp->key;
+ }
+ ATF_REQUIRE_EQ(NULL, ARB_INSERT(tree, root, tmp));
+ }
+
+ ins = ARB_MIN(tree, root);
+ ATF_REQUIRE_MSG(ins != NULL, "ARB_MIN error");
+ ATF_CHECK_EQ(min, ins->key);
+ tmp = ins;
+ ins = ARB_MAX(tree, root);
+ ATF_REQUIRE_MSG(ins != NULL, "ARB_MAX error");
+ ATF_CHECK_EQ(max, ins->key);
+
+ ATF_CHECK_EQ(tmp, ARB_REMOVE(tree, root, tmp));
+
+ for (i = 0; i < ITER - 1; i++) {
+ tmp = ARB_ROOT(root);
+ ATF_REQUIRE_MSG(tmp != NULL, "ARB_ROOT error");
+ ATF_CHECK_EQ(tmp, ARB_REMOVE(tree, root, tmp));
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, arb_test);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/sys/bitset_test.c b/tests/sys/sys/bitset_test.c
new file mode 100644
index 000000000000..bbf1bc131f35
--- /dev/null
+++ b/tests/sys/sys/bitset_test.c
@@ -0,0 +1,90 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2021 The FreeBSD Foundation
+ *
+ * This software was developed by Mark Johnston under sponsorship from
+ * the FreeBSD Foundation.
+ */
+
+#define _WANT_FREEBSD_BITSET
+
+#include <sys/types.h>
+#include <sys/_bitset.h>
+#include <sys/bitset.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <atf-c.h>
+
+BITSET_DEFINE(bs256, 256);
+
+ATF_TC_WITHOUT_HEAD(bit_foreach);
+ATF_TC_BODY(bit_foreach, tc)
+{
+ struct bs256 bs0, bs1, bsrand;
+ int setc, clrc, i;
+
+#define _BIT_FOREACH_COUNT(s, bs) do { \
+ int prev = -1; \
+ setc = clrc = 0; \
+ BIT_FOREACH_ISSET((s), i, (bs)) { \
+ ATF_REQUIRE_MSG(prev < i, "incorrect bit ordering"); \
+ ATF_REQUIRE_MSG(BIT_ISSET((s), i, (bs)), \
+ "bit %d is not set", i); \
+ setc++; \
+ prev = i; \
+ } \
+ prev = -1; \
+ BIT_FOREACH_ISCLR((s), i, (bs)) { \
+ ATF_REQUIRE_MSG(prev < i, "incorrect bit ordering"); \
+ ATF_REQUIRE_MSG(!BIT_ISSET((s), i, (bs)), \
+ "bit %d is set", i); \
+ clrc++; \
+ prev = i; \
+ } \
+} while (0)
+
+ /*
+ * Create several bitsets, and for each one count the number
+ * of set and clear bits and make sure they match what we expect.
+ */
+
+ BIT_FILL(256, &bs1);
+ _BIT_FOREACH_COUNT(256, &bs1);
+ ATF_REQUIRE_MSG(setc == 256, "incorrect set count %d", setc);
+ ATF_REQUIRE_MSG(clrc == 0, "incorrect clear count %d", clrc);
+
+ BIT_ZERO(256, &bs0);
+ _BIT_FOREACH_COUNT(256, &bs0);
+ ATF_REQUIRE_MSG(setc == 0, "incorrect set count %d", setc);
+ ATF_REQUIRE_MSG(clrc == 256, "incorrect clear count %d", clrc);
+
+ BIT_ZERO(256, &bsrand);
+ for (i = 0; i < 256; i++)
+ if (random() % 2 != 0)
+ BIT_SET(256, i, &bsrand);
+ _BIT_FOREACH_COUNT(256, &bsrand);
+ ATF_REQUIRE_MSG(setc + clrc == 256, "incorrect counts %d, %d",
+ setc, clrc);
+
+ /*
+ * Try to verify that we can safely clear bits in the set while
+ * iterating.
+ */
+ BIT_FOREACH_ISSET(256, i, &bsrand) {
+ ATF_REQUIRE(setc-- > 0);
+ BIT_CLR(256, i, &bsrand);
+ }
+ _BIT_FOREACH_COUNT(256, &bsrand);
+ ATF_REQUIRE_MSG(setc == 0, "incorrect set count %d", setc);
+ ATF_REQUIRE_MSG(clrc == 256, "incorrect clear count %d", clrc);
+
+#undef _BIT_FOREACH_COUNT
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, bit_foreach);
+ return (atf_no_error());
+}
diff --git a/tests/sys/sys/bitstring_test.c b/tests/sys/sys/bitstring_test.c
new file mode 100644
index 000000000000..a48042a4a063
--- /dev/null
+++ b/tests/sys/sys/bitstring_test.c
@@ -0,0 +1,977 @@
+/*-
+ * Copyright (c) 2014 Spectra Logic Corporation
+ * Copyright (c) 2023 Klara, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <sys/param.h>
+
+#include <bitstring.h>
+#include <malloc_np.h>
+
+#include <atf-c.h>
+
+typedef void (testfunc_t)(bitstr_t *bstr, int nbits, const char *memloc);
+
+static void
+bitstring_run_stack_test(testfunc_t *test, int nbits)
+{
+ bitstr_t bit_decl(bitstr, nbits);
+
+ test(bitstr, nbits, "stack");
+}
+
+static void
+bitstring_run_heap_test(testfunc_t *test, int nbits)
+{
+ bitstr_t *bitstr = bit_alloc(nbits);
+
+ test(bitstr, nbits, "heap");
+ free(bitstr);
+}
+
+static void
+bitstring_test_runner(testfunc_t *test)
+{
+ const int bitstr_sizes[] = {
+ 0,
+ 1,
+ _BITSTR_BITS - 1,
+ _BITSTR_BITS,
+ _BITSTR_BITS + 1,
+ 2 * _BITSTR_BITS - 1,
+ 2 * _BITSTR_BITS,
+ 1023,
+ 1024
+ };
+
+ for (unsigned long i = 0; i < nitems(bitstr_sizes); i++) {
+ bitstring_run_stack_test(test, bitstr_sizes[i]);
+ bitstring_run_heap_test(test, bitstr_sizes[i]);
+ }
+}
+
+#define BITSTRING_TC_DEFINE(name) \
+ATF_TC_WITHOUT_HEAD(name); \
+static testfunc_t name ## _test; \
+ \
+ATF_TC_BODY(name, tc) \
+{ \
+ bitstring_test_runner(name ## _test); \
+} \
+ \
+static void \
+name ## _test(bitstr_t *bitstr, int nbits, const char *memloc)
+
+#define BITSTRING_TC_ADD(tp, name) \
+do { \
+ ATF_TP_ADD_TC(tp, name); \
+} while (0)
+
+ATF_TC_WITHOUT_HEAD(bitstr_in_struct);
+ATF_TC_BODY(bitstr_in_struct, tc)
+{
+ struct bitstr_containing_struct {
+ bitstr_t bit_decl(bitstr, 8);
+ } test_struct;
+
+ bit_nclear(test_struct.bitstr, 0, 8);
+}
+
+ATF_TC_WITHOUT_HEAD(bitstr_size);
+ATF_TC_BODY(bitstr_size, tc)
+{
+ size_t sob = sizeof(bitstr_t);
+
+ ATF_CHECK_EQ(0, bitstr_size(0));
+ ATF_CHECK_EQ(sob, bitstr_size(1));
+ ATF_CHECK_EQ(sob, bitstr_size(sob * 8));
+ ATF_CHECK_EQ(2 * sob, bitstr_size(sob * 8 + 1));
+}
+
+BITSTRING_TC_DEFINE(bit_set)
+/* bitstr_t *bitstr, int nbits, const char *memloc */
+{
+ memset(bitstr, 0, bitstr_size(nbits));
+
+ for (int i = 0; i < nbits; i++) {
+ bit_set(bitstr, i);
+
+ for (int j = 0; j < nbits; j++) {
+ ATF_REQUIRE_MSG(bit_test(bitstr, j) == (j == i) ? 1 : 0,
+ "bit_set_%d_%s: Failed on bit %d",
+ nbits, memloc, i);
+ }
+
+ bit_clear(bitstr, i);
+ }
+}
+
+BITSTRING_TC_DEFINE(bit_clear)
+/* bitstr_t *bitstr, int nbits, const char *memloc */
+{
+ int i, j;
+
+ memset(bitstr, 0xFF, bitstr_size(nbits));
+ for (i = 0; i < nbits; i++) {
+ bit_clear(bitstr, i);
+
+ for (j = 0; j < nbits; j++) {
+ ATF_REQUIRE_MSG(bit_test(bitstr, j) == (j == i) ? 0 : 1,
+ "bit_clear_%d_%s: Failed on bit %d",
+ nbits, memloc, i);
+ }
+
+ bit_set(bitstr, i);
+ }
+}
+
+BITSTRING_TC_DEFINE(bit_ffs)
+/* bitstr_t *bitstr, int nbits, const char *memloc */
+{
+ int i;
+ int found_set_bit;
+
+ memset(bitstr, 0, bitstr_size(nbits));
+ bit_ffs(bitstr, nbits, &found_set_bit);
+ ATF_REQUIRE_MSG(found_set_bit == -1,
+ "bit_ffs_%d_%s: Failed all clear bits.", nbits, memloc);
+
+ for (i = 0; i < nbits; i++) {
+ memset(bitstr, 0xFF, bitstr_size(nbits));
+ if (i > 0)
+ bit_nclear(bitstr, 0, i - 1);
+
+ bit_ffs(bitstr, nbits, &found_set_bit);
+ ATF_REQUIRE_MSG(found_set_bit == i,
+ "bit_ffs_%d_%s: Failed on bit %d, Result %d",
+ nbits, memloc, i, found_set_bit);
+ }
+}
+
+BITSTRING_TC_DEFINE(bit_ffc)
+/* bitstr_t *bitstr, int nbits, const char *memloc */
+{
+ int i;
+ int found_clear_bit;
+
+ memset(bitstr, 0xFF, bitstr_size(nbits));
+ bit_ffc(bitstr, nbits, &found_clear_bit);
+ ATF_REQUIRE_MSG(found_clear_bit == -1,
+ "bit_ffc_%d_%s: Failed all set bits.", nbits, memloc);
+
+ for (i = 0; i < nbits; i++) {
+ memset(bitstr, 0, bitstr_size(nbits));
+ if (i > 0)
+ bit_nset(bitstr, 0, i - 1);
+
+ bit_ffc(bitstr, nbits, &found_clear_bit);
+ ATF_REQUIRE_MSG(found_clear_bit == i,
+ "bit_ffc_%d_%s: Failed on bit %d, Result %d",
+ nbits, memloc, i, found_clear_bit);
+ }
+}
+
+BITSTRING_TC_DEFINE(bit_ffs_at)
+/* bitstr_t *bitstr, int nbits, const char *memloc */
+{
+ int i;
+ int found_set_bit;
+
+ memset(bitstr, 0xFF, bitstr_size(nbits));
+ for (i = 0; i < nbits; i++) {
+ bit_ffs_at(bitstr, i, nbits, &found_set_bit);
+ ATF_REQUIRE_MSG(found_set_bit == i,
+ "bit_ffs_at_%d_%s: Failed on bit %d, Result %d",
+ nbits, memloc, i, found_set_bit);
+ }
+
+ memset(bitstr, 0, bitstr_size(nbits));
+ for (i = 0; i < nbits; i++) {
+ bit_ffs_at(bitstr, i, nbits, &found_set_bit);
+ ATF_REQUIRE_MSG(found_set_bit == -1,
+ "bit_ffs_at_%d_%s: Failed on bit %d, Result %d",
+ nbits, memloc, i, found_set_bit);
+ }
+
+ memset(bitstr, 0x55, bitstr_size(nbits));
+ for (i = 0; i < nbits; i++) {
+ bit_ffs_at(bitstr, i, nbits, &found_set_bit);
+ if (i == nbits - 1 && (nbits & 1) == 0) {
+ ATF_REQUIRE_MSG(found_set_bit == -1,
+ "bit_ffs_at_%d_%s: Failed on bit %d, Result %d",
+ nbits, memloc, i, found_set_bit);
+ } else {
+ ATF_REQUIRE_MSG(found_set_bit == i + (i & 1),
+ "bit_ffs_at_%d_%s: Failed on bit %d, Result %d",
+ nbits, memloc, i, found_set_bit);
+ }
+ }
+
+ memset(bitstr, 0xAA, bitstr_size(nbits));
+ for (i = 0; i < nbits; i++) {
+ bit_ffs_at(bitstr, i, nbits, &found_set_bit);
+ if (i == nbits - 1 && (nbits & 1) != 0) {
+ ATF_REQUIRE_MSG(found_set_bit == -1,
+ "bit_ffs_at_%d_%s: Failed on bit %d, Result %d",
+ nbits, memloc, i, found_set_bit);
+ } else {
+ ATF_REQUIRE_MSG(
+ found_set_bit == i + ((i & 1) ? 0 : 1),
+ "bit_ffs_at_%d_%s: Failed on bit %d, Result %d",
+ nbits, memloc, i, found_set_bit);
+ }
+ }
+
+ /* Pass a start value beyond the size of the bit string */
+ bit_ffs_at(bitstr, nbits, nbits, &found_set_bit);
+ ATF_REQUIRE_MSG(found_set_bit == -1,
+ "bit_ffs_at_%d_%s: Failed with high start value of %d, Result %d",
+ nbits, memloc, nbits, found_set_bit);
+
+ bit_ffs_at(bitstr, nbits + 3, nbits, &found_set_bit);
+ ATF_REQUIRE_MSG(found_set_bit == -1,
+ "bit_ffs_at_%d_%s: Failed with high start value of %d, Result %d",
+ nbits, memloc, nbits + 3, found_set_bit);
+}
+
+BITSTRING_TC_DEFINE(bit_ffc_at)
+/* bitstr_t *bitstr, int nbits, const char *memloc */
+{
+ int i, found_clear_bit;
+
+ memset(bitstr, 0, bitstr_size(nbits));
+ for (i = 0; i < nbits; i++) {
+ bit_ffc_at(bitstr, i, nbits, &found_clear_bit);
+ ATF_REQUIRE_MSG(found_clear_bit == i,
+ "bit_ffc_at_%d_%s: Failed on bit %d, Result %d",
+ nbits, memloc, i, found_clear_bit);
+ }
+
+ memset(bitstr, 0xFF, bitstr_size(nbits));
+ for (i = 0; i < nbits; i++) {
+ bit_ffc_at(bitstr, i, nbits, &found_clear_bit);
+ ATF_REQUIRE_MSG(found_clear_bit == -1,
+ "bit_ffc_at_%d_%s: Failed on bit %d, Result %d",
+ nbits, memloc, i, found_clear_bit);
+ }
+
+ memset(bitstr, 0x55, bitstr_size(nbits));
+ for (i = 0; i < nbits; i++) {
+ bit_ffc_at(bitstr, i, nbits, &found_clear_bit);
+ if (i == nbits - 1 && (nbits & 1) != 0) {
+ ATF_REQUIRE_MSG(found_clear_bit == -1,
+ "bit_ffc_at_%d_%s: Failed on bit %d, Result %d",
+ nbits, memloc, i, found_clear_bit);
+ } else {
+ ATF_REQUIRE_MSG(
+ found_clear_bit == i + ((i & 1) ? 0 : 1),
+ "bit_ffc_at_%d_%s: Failed on bit %d, Result %d",
+ nbits, memloc, i, found_clear_bit);
+ }
+ }
+
+ memset(bitstr, 0xAA, bitstr_size(nbits));
+ for (i = 0; i < nbits; i++) {
+ bit_ffc_at(bitstr, i, nbits, &found_clear_bit);
+ if (i == nbits - 1 && (nbits & 1) == 0) {
+ ATF_REQUIRE_MSG(found_clear_bit == -1,
+ "bit_ffc_at_%d_%s: Failed on bit %d, Result %d",
+ nbits, memloc, i, found_clear_bit);
+ } else {
+ ATF_REQUIRE_MSG(found_clear_bit == i + (i & 1),
+ "bit_ffc_at_%d_%s: Failed on bit %d, Result %d",
+ nbits, memloc, i, found_clear_bit);
+ }
+ }
+
+ /* Pass a start value beyond the size of the bit string */
+ bit_ffc_at(bitstr, nbits, nbits, &found_clear_bit);
+ ATF_REQUIRE_MSG(found_clear_bit == -1,
+ "bit_ffc_at_%d_%s: Failed with high start value, Result %d",
+ nbits, memloc, found_clear_bit);
+
+ bit_ffc_at(bitstr, nbits + 3, nbits, &found_clear_bit);
+ ATF_REQUIRE_MSG(found_clear_bit == -1,
+ "bit_ffc_at_%d_%s: Failed with high start value of %d, Result %d",
+ nbits, memloc, nbits + 3, found_clear_bit);
+}
+
+BITSTRING_TC_DEFINE(bit_ffc_area_at_all_or_nothing)
+/* bitstr_t *bitstr, int nbits, const char *memloc */
+{
+ int found;
+
+ memset(bitstr, 0, bitstr_size(nbits));
+ if (nbits % _BITSTR_BITS != 0)
+ bit_nset(bitstr, nbits, roundup2(nbits, _BITSTR_BITS) - 1);
+
+ for (int start = 0; start < nbits; start++) {
+ for (int size = 1; size < nbits - start; size++) {
+ bit_ffc_area_at(bitstr, start, nbits, size, &found);
+ ATF_REQUIRE_EQ_MSG(start, found,
+ "bit_ffc_area_at_%d_%s: "
+ "Did not find %d clear bits at %d",
+ nbits, memloc, size, start);
+ }
+ }
+
+ memset(bitstr, 0xff, bitstr_size(nbits));
+ if (nbits % _BITSTR_BITS != 0)
+ bit_nclear(bitstr, nbits, roundup2(nbits, _BITSTR_BITS) - 1);
+
+ for (int start = 0; start < nbits; start++) {
+ for (int size = 1; size < nbits - start; size++) {
+ bit_ffc_area_at(bitstr, start, nbits, size, &found);
+ ATF_REQUIRE_EQ_MSG(-1, found,
+ "bit_ffc_area_at_%d_%s: "
+ "Found %d clear bits at %d",
+ nbits, memloc, size, start);
+ }
+ }
+}
+
+BITSTRING_TC_DEFINE(bit_ffs_area_at_all_or_nothing)
+/* bitstr_t *bitstr, int nbits, const char *memloc */
+{
+ int found;
+
+ memset(bitstr, 0, bitstr_size(nbits));
+ if (nbits % _BITSTR_BITS != 0)
+ bit_nset(bitstr, nbits, roundup2(nbits, _BITSTR_BITS) - 1);
+
+ for (int start = 0; start < nbits; start++) {
+ for (int size = 1; size < nbits - start; size++) {
+ bit_ffs_area_at(bitstr, start, nbits, size, &found);
+ ATF_REQUIRE_EQ_MSG(-1, found,
+ "bit_ffs_area_at_%d_%s: "
+ "Found %d set bits at %d",
+ nbits, memloc, size, start);
+ }
+ }
+
+ memset(bitstr, 0xff, bitstr_size(nbits));
+ if (nbits % _BITSTR_BITS != 0)
+ bit_nclear(bitstr, nbits, roundup2(nbits, _BITSTR_BITS) - 1);
+
+ for (int start = 0; start < nbits; start++) {
+ for (int size = 1; size < nbits - start; size++) {
+ bit_ffs_area_at(bitstr, start, nbits, size, &found);
+ ATF_REQUIRE_EQ_MSG(start, found,
+ "bit_ffs_area_at_%d_%s: "
+ "Did not find %d set bits at %d",
+ nbits, memloc, size, start);
+ }
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(bit_ffs_area);
+ATF_TC_BODY(bit_ffs_area, tc)
+{
+ const int nbits = 72;
+ bitstr_t bit_decl(bitstr, nbits);
+ int location;
+
+ memset(bitstr, 0, bitstr_size(nbits));
+
+ bit_nset(bitstr, 5, 6);
+
+ location = 0;
+ bit_ffs_area(bitstr, nbits, 3, &location);
+ ATF_REQUIRE_EQ_MSG(-1, location,
+ "bit_ffs_area: found location of size 3 when only 2 bits are set");
+ ATF_REQUIRE_EQ_MSG(0, bit_ntest(bitstr, 5, 7, 1),
+ "bit_ntest: found location of size 3 when only 2 bits are set");
+
+ bit_set(bitstr, 7);
+
+ location = 0;
+ bit_ffs_area(bitstr, nbits, 3, &location);
+ ATF_REQUIRE_EQ_MSG(5, location,
+ "bit_ffs_area: failed to find location of size 3 %d", location);
+ ATF_REQUIRE_EQ_MSG(1, bit_ntest(bitstr, 5, 7, 1),
+ "bit_ntest: failed to find all 3 bits set");
+
+ bit_set(bitstr, 8);
+
+ location = 0;
+ bit_ffs_area(bitstr, nbits, 3, &location);
+ ATF_REQUIRE_EQ_MSG(5, location,
+ "bit_ffs_area: failed to find location of size 3");
+
+ location = 0;
+ bit_ffs_area_at(bitstr, 2, nbits, 3, &location);
+ ATF_REQUIRE_EQ_MSG(5, location,
+ "bit_ffs_area_at: failed to find location of size 3");
+
+ location = 0;
+ bit_ffs_area_at(bitstr, 6, nbits, 3, &location);
+ ATF_REQUIRE_EQ_MSG(6, location,
+ "bit_ffs_area_at: failed to find location of size 3");
+
+ location = 0;
+ bit_ffs_area_at(bitstr, 8, nbits, 3, &location);
+ ATF_REQUIRE_EQ_MSG(-1, location,
+ "bit_ffs_area_at: found invalid location");
+
+ bit_nset(bitstr, 69, 71);
+
+ location = 0;
+ bit_ffs_area_at(bitstr, 8, nbits, 3, &location);
+ ATF_REQUIRE_EQ_MSG(69, location,
+ "bit_ffs_area_at: failed to find location of size 3");
+
+ location = 0;
+ bit_ffs_area_at(bitstr, 69, nbits, 3, &location);
+ ATF_REQUIRE_EQ_MSG(69, location,
+ "bit_ffs_area_at: failed to find location of size 3");
+
+ location = 0;
+ bit_ffs_area_at(bitstr, 70, nbits, 3, &location);
+ ATF_REQUIRE_EQ_MSG(-1, location,
+ "bit_ffs_area_at: found invalid location");
+
+ location = 0;
+ bit_ffs_area_at(bitstr, 72, nbits, 3, &location);
+ ATF_REQUIRE_EQ_MSG(-1, location,
+ "bit_ffs_area_at: found invalid location");
+
+ bit_nset(bitstr, 59, 67);
+
+ location = 0;
+ bit_ffs_area(bitstr, nbits, 9, &location);
+ ATF_REQUIRE_EQ_MSG(59, location,
+ "bit_ffs_area: failed to find location of size 9");
+
+ location = 0;
+ bit_ffs_area(bitstr, nbits, 10, &location);
+ ATF_REQUIRE_EQ_MSG(-1, location,
+ "bit_ffs_area: found invalid location");
+}
+
+ATF_TC_WITHOUT_HEAD(bit_ffc_area);
+ATF_TC_BODY(bit_ffc_area, tc)
+{
+ const int nbits = 80;
+ bitstr_t bit_decl(bitstr, nbits);
+ int location;
+
+ /* set all bits */
+ memset(bitstr, 0xFF, bitstr_size(nbits));
+
+ bit_clear(bitstr, 7);
+ bit_clear(bitstr, 8);
+
+ location = 0;
+ bit_ffc_area(bitstr, nbits, 3, &location);
+ ATF_REQUIRE_EQ_MSG(-1, location,
+ "bit_ffc_area: found location of size 3 when only 2 bits are set");
+
+ bit_clear(bitstr, 9);
+
+ location = 0;
+ bit_ffc_area(bitstr, nbits, 3, &location);
+ ATF_REQUIRE_EQ_MSG(7, location,
+ "bit_ffc_area: failed to find location of size 3");
+
+ bit_clear(bitstr, 10);
+
+ location = 0;
+ bit_ffc_area(bitstr, nbits, 3, &location);
+ ATF_REQUIRE_EQ_MSG(7, location,
+ "bit_ffc_area: failed to find location of size 3");
+
+ location = 0;
+ bit_ffc_area_at(bitstr, 2, nbits, 3, &location);
+ ATF_REQUIRE_EQ_MSG(7, location,
+ "bit_ffc_area_at: failed to find location of size 3");
+
+ location = 0;
+ bit_ffc_area_at(bitstr, 8, nbits, 3, &location);
+ ATF_REQUIRE_EQ_MSG(8, location,
+ "bit_ffc_area_at: failed to find location of size 3");
+
+ location = 0;
+ bit_ffc_area_at(bitstr, 9, nbits, 3, &location);
+ ATF_REQUIRE_EQ_MSG(-1, location,
+ "bit_ffc_area_at: found invalid bit location");
+
+ bit_clear(bitstr, 77);
+ bit_clear(bitstr, 78);
+ bit_clear(bitstr, 79);
+
+ location = 0;
+ bit_ffc_area_at(bitstr, 12, nbits, 3, &location);
+ ATF_REQUIRE_EQ_MSG(77, location,
+ "bit_ffc_area_at: failed to find location of size 3");
+
+ location = 0;
+ bit_ffc_area_at(bitstr, 77, nbits, 3, &location);
+ ATF_REQUIRE_EQ_MSG(77, location,
+ "bit_ffc_area_at: failed to find location of size 3");
+
+ location = 0;
+ bit_ffc_area_at(bitstr, 78, nbits, 3, &location);
+ ATF_REQUIRE_EQ_MSG(-1, location,
+ "bit_ffc_area_at: found invalid location");
+
+ location = 0;
+ bit_ffc_area_at(bitstr, 85, nbits, 3, &location);
+ ATF_REQUIRE_EQ_MSG(-1, location,
+ "bit_ffc_area_at: found invalid location");
+}
+
+BITSTRING_TC_DEFINE(bit_nclear)
+/* bitstr_t *bitstr, int nbits, const char *memloc */
+{
+ int i, j;
+ int found_set_bit;
+ int found_clear_bit;
+
+ for (i = 0; i < nbits; i++) {
+ for (j = i; j < nbits; j++) {
+ memset(bitstr, 0xFF, bitstr_size(nbits));
+ bit_nclear(bitstr, i, j);
+
+ bit_ffc(bitstr, nbits, &found_clear_bit);
+ ATF_REQUIRE_MSG(
+ found_clear_bit == i,
+ "bit_nclear_%d_%d_%d%s: Failed with result %d",
+ nbits, i, j, memloc, found_clear_bit);
+
+ bit_ffs_at(bitstr, i, nbits, &found_set_bit);
+ ATF_REQUIRE_MSG(
+ (j + 1 < nbits) ? found_set_bit == j + 1 : -1,
+ "bit_nset_%d_%d_%d%s: Failed with result %d",
+ nbits, i, j, memloc, found_set_bit);
+ }
+ }
+}
+
+BITSTRING_TC_DEFINE(bit_nset)
+/* bitstr_t *bitstr, int nbits, const char *memloc */
+{
+ int i, j;
+ int found_set_bit;
+ int found_clear_bit;
+
+ for (i = 0; i < nbits; i++) {
+ for (j = i; j < nbits; j++) {
+ memset(bitstr, 0, bitstr_size(nbits));
+ bit_nset(bitstr, i, j);
+
+ bit_ffs(bitstr, nbits, &found_set_bit);
+ ATF_REQUIRE_MSG(
+ found_set_bit == i,
+ "bit_nset_%d_%d_%d%s: Failed with result %d",
+ nbits, i, j, memloc, found_set_bit);
+
+ bit_ffc_at(bitstr, i, nbits, &found_clear_bit);
+ ATF_REQUIRE_MSG(
+ (j + 1 < nbits) ? found_clear_bit == j + 1 : -1,
+ "bit_nset_%d_%d_%d%s: Failed with result %d",
+ nbits, i, j, memloc, found_clear_bit);
+ }
+ }
+}
+
+BITSTRING_TC_DEFINE(bit_count)
+/* bitstr_t *bitstr, int nbits, const char *memloc */
+{
+ int result, s, e, expected;
+
+ /* Empty bitstr */
+ memset(bitstr, 0, bitstr_size(nbits));
+ bit_count(bitstr, 0, nbits, &result);
+ ATF_CHECK_MSG(0 == result,
+ "bit_count_%d_%s_%s: Failed with result %d",
+ nbits, "clear", memloc, result);
+
+ /* Full bitstr */
+ memset(bitstr, 0xFF, bitstr_size(nbits));
+ bit_count(bitstr, 0, nbits, &result);
+ ATF_CHECK_MSG(nbits == result,
+ "bit_count_%d_%s_%s: Failed with result %d",
+ nbits, "set", memloc, result);
+
+ /* Invalid _start value */
+ memset(bitstr, 0xFF, bitstr_size(nbits));
+ bit_count(bitstr, nbits, nbits, &result);
+ ATF_CHECK_MSG(0 == result,
+ "bit_count_%d_%s_%s: Failed with result %d",
+ nbits, "invalid_start", memloc, result);
+
+ /* Alternating bitstr, starts with 0 */
+ memset(bitstr, 0xAA, bitstr_size(nbits));
+ bit_count(bitstr, 0, nbits, &result);
+ ATF_CHECK_MSG(nbits / 2 == result,
+ "bit_count_%d_%s_%d_%s: Failed with result %d",
+ nbits, "alternating", 0, memloc, result);
+
+ /* Alternating bitstr, starts with 1 */
+ memset(bitstr, 0x55, bitstr_size(nbits));
+ bit_count(bitstr, 0, nbits, &result);
+ ATF_CHECK_MSG((nbits + 1) / 2 == result,
+ "bit_count_%d_%s_%d_%s: Failed with result %d",
+ nbits, "alternating", 1, memloc, result);
+
+ /* Varying start location */
+ memset(bitstr, 0xAA, bitstr_size(nbits));
+ for (s = 0; s < nbits; s++) {
+ expected = s % 2 == 0 ? (nbits - s) / 2 : (nbits - s + 1) / 2;
+ bit_count(bitstr, s, nbits, &result);
+ ATF_CHECK_MSG(expected == result,
+ "bit_count_%d_%s_%d_%s: Failed with result %d",
+ nbits, "vary_start", s, memloc, result);
+ }
+
+ /* Varying end location */
+ memset(bitstr, 0xAA, bitstr_size(nbits));
+ for (e = 0; e < nbits; e++) {
+ bit_count(bitstr, 0, e, &result);
+ ATF_CHECK_MSG(e / 2 == result,
+ "bit_count_%d_%s_%d_%s: Failed with result %d",
+ nbits, "vary_end", e, memloc, result);
+ }
+
+}
+
+BITSTRING_TC_DEFINE(bit_foreach)
+/* bitstr_t *bitstr, int nbits, const char *memloc */
+{
+ int i, set_bit;
+
+ /* Empty bitstr */
+ memset(bitstr, 0x00, bitstr_size(nbits));
+ bit_foreach (bitstr, nbits, set_bit) {
+ atf_tc_fail("bit_foreach_%d_%s_%s: Failed at location %d",
+ nbits, "clear", memloc, set_bit);
+ }
+
+ /* Full bitstr */
+ i = 0;
+ memset(bitstr, 0xFF, bitstr_size(nbits));
+ bit_foreach(bitstr, nbits, set_bit) {
+ ATF_REQUIRE_MSG(set_bit == i,
+ "bit_foreach_%d_%s_%s: Failed on turn %d at location %d",
+ nbits, "set", memloc, i, set_bit);
+ i++;
+ }
+ ATF_REQUIRE_MSG(i == nbits,
+ "bit_foreach_%d_%s_%s: Invalid number of turns %d",
+ nbits, "set", memloc, i);
+
+ /* Alternating bitstr, starts with 0 */
+ i = 0;
+ memset(bitstr, 0xAA, bitstr_size(nbits));
+ bit_foreach(bitstr, nbits, set_bit) {
+ ATF_REQUIRE_MSG(set_bit == i * 2 + 1,
+ "bit_foreach_%d_%s_%d_%s: "
+ "Failed on turn %d at location %d",
+ nbits, "alternating", 0, memloc, i, set_bit);
+ i++;
+ }
+ ATF_REQUIRE_MSG(i == nbits / 2,
+ "bit_foreach_%d_%s_%d_%s: Invalid number of turns %d",
+ nbits, "alternating", 0, memloc, i);
+
+ /* Alternating bitstr, starts with 1 */
+ i = 0;
+ memset(bitstr, 0x55, bitstr_size(nbits));
+ bit_foreach(bitstr, nbits, set_bit) {
+ ATF_REQUIRE_MSG(set_bit == i * 2,
+ "bit_foreach_%d_%s_%d_%s: "
+ "Failed on turn %d at location %d",
+ nbits, "alternating", 1, memloc, i, set_bit);
+ i++;
+ }
+ ATF_REQUIRE_MSG(i == (nbits + 1) / 2,
+ "bit_foreach_%d_%s_%d_%s: Invalid number of turns %d",
+ nbits, "alternating", 1, memloc, i);
+}
+
+BITSTRING_TC_DEFINE(bit_foreach_at)
+/* bitstr_t *bitstr, int nbits, const char *memloc */
+{
+ int i, s, e, set_bit;
+
+ /* Invalid _start value */
+ memset(bitstr, 0xFF, bitstr_size(nbits));
+ bit_foreach_at(bitstr, nbits, nbits, set_bit) {
+ atf_tc_fail("bit_foreach_at_%d_%s_%s: Failed at location %d",
+ nbits, "invalid_start", memloc, set_bit);
+ }
+
+ /* Varying start location */
+ memset(bitstr, 0xAA, bitstr_size(nbits));
+ for (s = 0; s < nbits; s++) {
+ i = 0;
+ bit_foreach_at(bitstr, s, nbits, set_bit) {
+ ATF_REQUIRE_MSG(set_bit == (i + s / 2) * 2 + 1,
+ "bit_foreach_at_%d_%s_%d_%s: "
+ "Failed on turn %d at location %d",
+ nbits, "vary_start", s, memloc, i, set_bit);
+ i++;
+ }
+ ATF_REQUIRE_MSG(i == nbits / 2 - s / 2,
+ "bit_foreach_at_%d_%s_%d_%s: Invalid number of turns %d",
+ nbits, "vary_start", s, memloc, i);
+ }
+
+ /* Varying end location */
+ memset(bitstr, 0xAA, bitstr_size(nbits));
+ for (e = 0; e < nbits; e++) {
+ i = 0;
+ bit_foreach_at(bitstr, 0, e, set_bit) {
+ ATF_REQUIRE_MSG(set_bit == i * 2 + 1,
+ "bit_foreach_at_%d_%s_%d_%s: "
+ "Failed on turn %d at location %d",
+ nbits, "vary_end", e, memloc, i, set_bit);
+ i++;
+ }
+ ATF_REQUIRE_MSG(i == e / 2,
+ "bit_foreach_at_%d_%s_%d_%s: Invalid number of turns %d",
+ nbits, "vary_end", e, memloc, i);
+ }
+}
+
+BITSTRING_TC_DEFINE(bit_foreach_unset)
+/* bitstr_t *bitstr, int nbits, const char *memloc */
+{
+ int i, unset_bit;
+
+ /* Empty bitstr */
+ i = 0;
+ memset(bitstr, 0, bitstr_size(nbits));
+ bit_foreach_unset(bitstr, nbits, unset_bit) {
+ ATF_REQUIRE_MSG(unset_bit == i,
+ "bit_foreach_unset_%d_%s_%s: "
+ "Failed on turn %d at location %d",
+ nbits, "clear", memloc, i, unset_bit);
+ i++;
+ }
+ ATF_REQUIRE_MSG(i == nbits,
+ "bit_foreach_unset_%d_%s_%s: Invalid number of turns %d",
+ nbits, "set", memloc, i);
+
+ /* Full bitstr */
+ memset(bitstr, 0xFF, bitstr_size(nbits));
+ bit_foreach_unset(bitstr, nbits, unset_bit) {
+ atf_tc_fail("bit_foreach_unset_%d_%s_%s: "
+ "Failed at location %d",
+ nbits, "set", memloc, unset_bit);
+ }
+
+ /* Alternating bitstr, starts with 0 */
+ i = 0;
+ memset(bitstr, 0xAA, bitstr_size(nbits));
+ bit_foreach_unset(bitstr, nbits, unset_bit) {
+ ATF_REQUIRE_MSG(unset_bit == i * 2,
+ "bit_foreach_unset_%d_%s_%d_%s: "
+ "Failed on turn %d at location %d",
+ nbits, "alternating", 0, memloc, i, unset_bit);
+ i++;
+ }
+ ATF_REQUIRE_MSG(i == (nbits + 1) / 2,
+ "bit_foreach_unset_%d_%s_%d_%s: Invalid number of turns %d",
+ nbits, "alternating", 0, memloc, i);
+
+ /* Alternating bitstr, starts with 1 */
+ i = 0;
+ memset(bitstr, 0x55, bitstr_size(nbits));
+ bit_foreach_unset(bitstr, nbits, unset_bit) {
+ ATF_REQUIRE_MSG(unset_bit == i * 2 + 1,
+ "bit_foreach_unset_%d_%s_%d_%s: "
+ "Failed on turn %d at location %d",
+ nbits, "alternating", 1, memloc, i, unset_bit);
+ i++;
+ }
+ ATF_REQUIRE_MSG(i == nbits / 2,
+ "bit_foreach_unset_%d_%s_%d_%s: Invalid number of turns %d",
+ nbits, "alternating", 1, memloc, i);
+}
+
+BITSTRING_TC_DEFINE(bit_foreach_unset_at)
+/* bitstr_t *bitstr, int nbits, const char *memloc */
+{
+ int i, s, e, unset_bit;
+
+ /* Invalid _start value */
+ memset(bitstr, 0, bitstr_size(nbits));
+ bit_foreach_unset_at(bitstr, nbits, nbits, unset_bit) {
+ atf_tc_fail("bit_foreach_unset_at_%d_%s_%s: "
+ "Failed at location %d",
+ nbits, "invalid_start", memloc, unset_bit);
+ }
+
+ /* Varying start location */
+ memset(bitstr, 0xAA, bitstr_size(nbits));
+ for (s = 0; s < nbits; s++) {
+ i = 0;
+ bit_foreach_unset_at(bitstr, s, nbits, unset_bit) {
+ ATF_REQUIRE_MSG(unset_bit == (i + (s + 1) / 2) * 2,
+ "bit_foreach_unset_at_%d_%s_%d_%s: "
+ "Failed on turn %d at location %d",
+ nbits, "vary_start", s, memloc, i, unset_bit);
+ i++;
+ }
+ ATF_REQUIRE_MSG(i == (nbits + 1) / 2 - (s + 1) / 2,
+ "bit_foreach_unset_at_%d_%s_%d_%s: "
+ "Invalid number of turns %d",
+ nbits, "vary_start", s, memloc, i);
+ }
+
+ /* Varying end location */
+ memset(bitstr, 0xAA, bitstr_size(nbits));
+ for (e = 0; e < nbits; e++) {
+ i = 0;
+ bit_foreach_unset_at(bitstr, 0, e, unset_bit) {
+ ATF_REQUIRE_MSG(unset_bit == i * 2,
+ "bit_foreach_unset_at_%d_%s_%d_%s: "
+ "Failed on turn %d at location %d",
+ nbits, "vary_end", e, memloc, i, unset_bit);
+ i++;
+ }
+ ATF_REQUIRE_MSG(i == (e + 1) / 2,
+ "bit_foreach_unset_at_%d_%s_%d_%s: "
+ "Invalid number of turns %d",
+ nbits, "vary_end", e, memloc, i);
+ }
+}
+
+/*
+ * Perform various tests on large bit strings. We can't simply add larger
+ * sizes to bitstring_runner as most of the existing tests are exhaustive
+ * and would take forever to run for large values of nbits.
+ *
+ * On 32-bit platforms, we use nbits = SSIZE_MAX (2147483647) bits, which
+ * is the largest we can hope to support; on 64-bit platforms, we use
+ * nbits = INT_MAX + 30 (2147483677), which is small enough to be
+ * practicable yet large enough to reveal arithmetic overflow bugs.
+ */
+ATF_TC_WITHOUT_HEAD(bitstr_large);
+ATF_TC_BODY(bitstr_large, tc)
+{
+ size_t nbits = INT_MAX < SSIZE_MAX ? (size_t)INT_MAX + 30 : SSIZE_MAX;
+ size_t early = 5, late = nbits - 5;
+ ssize_t fc, fs;
+ bitstr_t *b;
+
+ /* Check for overflow in size calculation */
+ ATF_REQUIRE(nbits >= (size_t)INT_MAX);
+ ATF_REQUIRE(bitstr_size(nbits) >= nbits / 8);
+
+ /* Allocate the bit string */
+ ATF_REQUIRE(b = bit_alloc(nbits));
+
+ /* Check that we allocated enough */
+ ATF_REQUIRE(malloc_usable_size(b) >= bitstr_size(nbits));
+
+ /* Check ffc, ffs on all-zeroes string */
+ bit_ffc(b, nbits, &fc);
+ ATF_CHECK_EQ(0L, fc);
+ bit_ffs(b, nbits, &fs);
+ ATF_CHECK_EQ(-1L, fs);
+
+ /* Set, test, and clear an early bit */
+ bit_set(b, early);
+ bit_ffs(b, nbits, &fs);
+ ATF_CHECK_EQ((ssize_t)early, fs);
+ ATF_CHECK_EQ(0, bit_test(b, early - 1));
+ ATF_CHECK(bit_test(b, early) != 0);
+ ATF_CHECK_EQ(0, bit_test(b, early + 1));
+ bit_clear(b, early);
+ ATF_CHECK_EQ(0, bit_test(b, early));
+
+ /* Set, test, and clear an early bit range */
+ bit_nset(b, early - 1, early + 1);
+ bit_ffs(b, nbits, &fs);
+ ATF_CHECK_EQ((ssize_t)early - 1, fs);
+ ATF_CHECK_EQ(0, bit_test(b, early - 2));
+ ATF_CHECK(bit_test(b, early - 1));
+ ATF_CHECK(bit_test(b, early));
+ ATF_CHECK(bit_test(b, early + 1));
+ ATF_CHECK_EQ(0, bit_test(b, early + 2));
+ bit_nclear(b, early - 1, early + 1);
+ ATF_CHECK_EQ(0, bit_test(b, early - 1));
+ ATF_CHECK_EQ(0, bit_test(b, early));
+ ATF_CHECK_EQ(0, bit_test(b, early + 1));
+
+ /* Set, test, and clear a late bit */
+ bit_set(b, late);
+ bit_ffs(b, nbits, &fs);
+ ATF_CHECK_EQ((ssize_t)late, fs);
+ ATF_CHECK_EQ(0, bit_test(b, late - 1));
+ ATF_CHECK(bit_test(b, late) != 0);
+ ATF_CHECK_EQ(0, bit_test(b, late + 1));
+ bit_clear(b, late);
+ ATF_CHECK_EQ(0, bit_test(b, late));
+
+ /* Set, test, and clear a late bit range */
+ bit_nset(b, late - 1, late + 1);
+ bit_ffs(b, nbits, &fs);
+ ATF_CHECK_EQ((ssize_t)late - 1, fs);
+ ATF_CHECK_EQ(0, bit_test(b, late - 2));
+ ATF_CHECK(bit_test(b, late - 1));
+ ATF_CHECK(bit_test(b, late));
+ ATF_CHECK(bit_test(b, late + 1));
+ ATF_CHECK_EQ(0, bit_test(b, late + 2));
+ bit_nclear(b, late - 1, late + 1);
+ ATF_CHECK_EQ(0, bit_test(b, late - 1));
+ ATF_CHECK_EQ(0, bit_test(b, late));
+ ATF_CHECK_EQ(0, bit_test(b, late + 1));
+
+ free(b);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, bitstr_in_struct);
+ ATF_TP_ADD_TC(tp, bitstr_size);
+ ATF_TP_ADD_TC(tp, bit_ffc_area);
+ ATF_TP_ADD_TC(tp, bit_ffs_area);
+ BITSTRING_TC_ADD(tp, bit_set);
+ BITSTRING_TC_ADD(tp, bit_clear);
+ BITSTRING_TC_ADD(tp, bit_ffs);
+ BITSTRING_TC_ADD(tp, bit_ffc);
+ BITSTRING_TC_ADD(tp, bit_ffs_at);
+ BITSTRING_TC_ADD(tp, bit_ffc_at);
+ BITSTRING_TC_ADD(tp, bit_nclear);
+ BITSTRING_TC_ADD(tp, bit_nset);
+ BITSTRING_TC_ADD(tp, bit_count);
+ BITSTRING_TC_ADD(tp, bit_ffs_area_at_all_or_nothing);
+ BITSTRING_TC_ADD(tp, bit_ffc_area_at_all_or_nothing);
+ BITSTRING_TC_ADD(tp, bit_foreach);
+ BITSTRING_TC_ADD(tp, bit_foreach_at);
+ BITSTRING_TC_ADD(tp, bit_foreach_unset);
+ BITSTRING_TC_ADD(tp, bit_foreach_unset_at);
+ ATF_TP_ADD_TC(tp, bitstr_large);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/sys/buf_ring_test.c b/tests/sys/sys/buf_ring_test.c
new file mode 100644
index 000000000000..611fb23788cc
--- /dev/null
+++ b/tests/sys/sys/buf_ring_test.c
@@ -0,0 +1,180 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Arm Ltd
+ */
+
+#include <sys/types.h>
+
+#include <machine/cpu.h>
+#include <machine/cpufunc.h>
+
+#include <errno.h>
+#include <stdint.h>
+
+#include <atf-c.h>
+
+static void critical_enter(void);
+static void critical_exit(void);
+
+#include <sys/buf_ring.h>
+
+static void
+critical_enter(void)
+{
+}
+
+static void
+critical_exit(void)
+{
+}
+
+static void *
+buf_ring_dequeue_peek(struct buf_ring *br)
+{
+ void *val;
+
+ val = buf_ring_peek(br);
+ if (val != NULL)
+ buf_ring_advance_sc(br);
+ return (val);
+}
+
+static void *
+buf_ring_dequeue_peek_clear_sc(struct buf_ring *br)
+{
+ void *val;
+
+ val = buf_ring_peek_clear_sc(br);
+ if (val != NULL)
+ buf_ring_advance_sc(br);
+ return (val);
+}
+
+#define MC_SC_TEST(dequeue_func) \
+ATF_TC_WITHOUT_HEAD(dequeue_func); \
+ATF_TC_BODY(dequeue_func, tc) \
+{ \
+ struct buf_ring *br; \
+ \
+ br = buf_ring_alloc(4); \
+ ATF_REQUIRE_MSG(br != NULL, "buf_ring_alloc returned NULL"); \
+ \
+ ATF_REQUIRE(dequeue_func(br) == NULL); \
+ ATF_REQUIRE(buf_ring_count(br) == 0); \
+ ATF_REQUIRE(!buf_ring_full(br)); \
+ ATF_REQUIRE(buf_ring_empty(br)); \
+ \
+ /* Try filling the buf_ring */ \
+ ATF_REQUIRE(buf_ring_enqueue(br, (void *)1) == 0); \
+ ATF_REQUIRE(buf_ring_enqueue(br, (void *)2) == 0); \
+ ATF_REQUIRE(buf_ring_enqueue(br, (void *)3) == 0); \
+ ATF_REQUIRE(buf_ring_enqueue(br, (void *)4) == ENOBUFS); \
+ \
+ ATF_REQUIRE(buf_ring_count(br) == 3); \
+ ATF_REQUIRE(buf_ring_full(br)); \
+ ATF_REQUIRE(!buf_ring_empty(br)); \
+ \
+ /* Partially empty it */ \
+ ATF_REQUIRE(dequeue_func(br) == (void *)1); \
+ ATF_REQUIRE(dequeue_func(br) == (void *)2); \
+ \
+ ATF_REQUIRE(buf_ring_count(br) == 1); \
+ ATF_REQUIRE(!buf_ring_full(br)); \
+ ATF_REQUIRE(!buf_ring_empty(br)); \
+ \
+ /* Add more items */ \
+ ATF_REQUIRE(buf_ring_enqueue(br, (void *)5) == 0); \
+ ATF_REQUIRE(buf_ring_count(br) == 2); \
+ \
+ /* Finish emptying it */ \
+ ATF_REQUIRE(dequeue_func(br) == (void *)3); \
+ ATF_REQUIRE(dequeue_func(br) == (void *)5); \
+ ATF_REQUIRE(dequeue_func(br) == NULL); \
+ \
+ ATF_REQUIRE(buf_ring_count(br) == 0); \
+ ATF_REQUIRE(!buf_ring_full(br)); \
+ ATF_REQUIRE(buf_ring_empty(br)); \
+ \
+ for (uintptr_t i = 0; i < 8; i++) { \
+ ATF_REQUIRE(buf_ring_enqueue(br, (void *)(i + 100)) == 0); \
+ ATF_REQUIRE(buf_ring_enqueue(br, (void *)(i + 200)) == 0); \
+ ATF_REQUIRE(buf_ring_enqueue(br, (void *)(i + 300)) == 0); \
+ ATF_REQUIRE(buf_ring_count(br) == 3); \
+ ATF_REQUIRE(dequeue_func(br) == (void *)(i + 100)); \
+ ATF_REQUIRE(dequeue_func(br) == (void *)(i + 200)); \
+ ATF_REQUIRE(dequeue_func(br) == (void *)(i + 300)); \
+ \
+ ATF_REQUIRE(!buf_ring_full(br)); \
+ ATF_REQUIRE(buf_ring_empty(br)); \
+ } \
+ \
+ buf_ring_free(br); \
+}
+
+MC_SC_TEST(buf_ring_dequeue_sc)
+MC_SC_TEST(buf_ring_dequeue_mc)
+MC_SC_TEST(buf_ring_dequeue_peek)
+MC_SC_TEST(buf_ring_dequeue_peek_clear_sc)
+
+ATF_TC_WITHOUT_HEAD(overflow);
+ATF_TC_BODY(overflow, tc)
+{
+ struct buf_ring *br;
+
+ br = buf_ring_alloc(4);
+ ATF_REQUIRE_MSG(br != NULL, "buf_ring_alloc returned NULL");
+
+ br->br_prod_head = br->br_cons_head = br->br_prod_tail =
+ br->br_cons_tail = UINT32_MAX - 1;
+ ATF_REQUIRE(buf_ring_count(br) == 0);
+ ATF_REQUIRE(!buf_ring_full(br));
+ ATF_REQUIRE(buf_ring_empty(br));
+
+ ATF_REQUIRE(buf_ring_enqueue(br, (void *)1) == 0);
+ ATF_REQUIRE(buf_ring_count(br) == 1);
+ ATF_REQUIRE(!buf_ring_full(br));
+ ATF_REQUIRE(!buf_ring_empty(br));
+
+ ATF_REQUIRE(buf_ring_enqueue(br, (void *)2) == 0);
+ ATF_REQUIRE(buf_ring_count(br) == 2);
+ ATF_REQUIRE(!buf_ring_full(br));
+ ATF_REQUIRE(!buf_ring_empty(br));
+
+ ATF_REQUIRE(buf_ring_enqueue(br, (void *)3) == 0);
+ ATF_REQUIRE(buf_ring_count(br) == 3);
+ ATF_REQUIRE(buf_ring_full(br));
+ ATF_REQUIRE(!buf_ring_empty(br));
+
+ ATF_REQUIRE(br->br_prod_head == 1);
+ ATF_REQUIRE(br->br_prod_tail == 1);
+ ATF_REQUIRE(br->br_cons_head == UINT32_MAX - 1);
+ ATF_REQUIRE(br->br_cons_tail == UINT32_MAX - 1);
+
+ ATF_REQUIRE(buf_ring_dequeue_sc(br) == (void *)1);
+ ATF_REQUIRE(buf_ring_count(br) == 2);
+ ATF_REQUIRE(!buf_ring_full(br));
+ ATF_REQUIRE(!buf_ring_empty(br));
+
+ ATF_REQUIRE(buf_ring_dequeue_sc(br) == (void *)2);
+ ATF_REQUIRE(buf_ring_count(br) == 1);
+ ATF_REQUIRE(!buf_ring_full(br));
+ ATF_REQUIRE(!buf_ring_empty(br));
+
+ ATF_REQUIRE(buf_ring_dequeue_sc(br) == (void *)3);
+ ATF_REQUIRE(buf_ring_count(br) == 0);
+ ATF_REQUIRE(!buf_ring_full(br));
+ ATF_REQUIRE(buf_ring_empty(br));
+
+ buf_ring_free(br);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, buf_ring_dequeue_sc);
+ ATF_TP_ADD_TC(tp, buf_ring_dequeue_mc);
+ ATF_TP_ADD_TC(tp, buf_ring_dequeue_peek);
+ ATF_TP_ADD_TC(tp, buf_ring_dequeue_peek_clear_sc);
+ ATF_TP_ADD_TC(tp, overflow);
+ return (atf_no_error());
+}
diff --git a/tests/sys/sys/qmath_test.c b/tests/sys/sys/qmath_test.c
new file mode 100644
index 000000000000..b77f8f78f102
--- /dev/null
+++ b/tests/sys/sys/qmath_test.c
@@ -0,0 +1,649 @@
+/*-
+ * Copyright (c) 2018 Netflix, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Author: Lawrence Stewart <lstewart@netflix.com>
+ */
+
+#include <sys/param.h>
+#include <sys/qmath.h>
+
+#include <errno.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <atf-c.h>
+
+#define QTEST_IV 3
+#define QTEST_IVSTR "3.00"
+#define QTEST_RPSHFT 2
+#define QTEST_INTBITS(q) (Q_NTBITS(q) - Q_SIGNED(q) - Q_NFBITS(q) - Q_NCBITS)
+#define QTEST_QITRUNC(q, iv) ((iv) >> Q_RPSHFT(q))
+#define QTEST_FFACTOR 32.0
+
+#define bitsperrand 31
+#define GENRAND(a, lb, ub) \
+({ \
+ int _rembits; \
+ do { \
+ _rembits = Q_BITSPERBASEUP(ub) + Q_LTZ(lb); \
+ *(a) = (__typeof(*(a)))0; \
+ while (_rembits > 0) { \
+ *(a) |= (((uint64_t)random()) & \
+ ((1ULL << (_rembits > bitsperrand ? \
+ bitsperrand : _rembits)) - 1)); \
+ *(a) <<= (_rembits - (_rembits > bitsperrand ? \
+ bitsperrand : _rembits)); \
+ _rembits -= bitsperrand; \
+ } \
+ *(a) += lb; \
+ } while (*(a) < (lb) || (uint64_t)*(a) > (ub)); \
+ *(a); \
+})
+
+/*
+ * Smoke tests for basic qmath operations, such as initialization
+ * or string formatting.
+ */
+ATF_TC_WITHOUT_HEAD(basic_s8q);
+ATF_TC_BODY(basic_s8q, tc)
+{
+ char buf[128];
+ s8q_t s8;
+
+ Q_INI(&s8, QTEST_IV, 0, QTEST_RPSHFT);
+ Q_TOSTR(s8, -1, 10, buf, sizeof(buf));
+ ATF_CHECK_STREQ(QTEST_IVSTR, buf);
+ ATF_CHECK_EQ(sizeof(s8) << 3, Q_NTBITS(s8));
+ ATF_CHECK_EQ(QTEST_RPSHFT, Q_NFBITS(s8));
+ ATF_CHECK_EQ(QTEST_INTBITS(s8), Q_NIBITS(s8));
+ ATF_CHECK_EQ(QTEST_QITRUNC(s8, INT8_MAX), Q_IMAXVAL(s8));
+ ATF_CHECK_EQ(-Q_IMAXVAL(s8), Q_IMINVAL(s8));
+}
+
+ATF_TC_WITHOUT_HEAD(basic_s16q);
+ATF_TC_BODY(basic_s16q, tc)
+{
+ char buf[128];
+ s16q_t s16;
+
+ Q_INI(&s16, QTEST_IV, 0, QTEST_RPSHFT);
+ Q_TOSTR(s16, -1, 10, buf, sizeof(buf));
+ ATF_CHECK_STREQ(QTEST_IVSTR, buf);
+ ATF_CHECK_EQ(sizeof(s16) << 3, Q_NTBITS(s16));
+ ATF_CHECK_EQ(QTEST_RPSHFT, Q_NFBITS(s16));
+ ATF_CHECK_EQ(QTEST_INTBITS(s16), Q_NIBITS(s16));
+ ATF_CHECK_EQ(QTEST_QITRUNC(s16, INT16_MAX), Q_IMAXVAL(s16));
+ ATF_CHECK_EQ(-Q_IMAXVAL(s16), Q_IMINVAL(s16));
+}
+
+ATF_TC_WITHOUT_HEAD(basic_s32q);
+ATF_TC_BODY(basic_s32q, tc)
+{
+ char buf[128];
+ s32q_t s32;
+
+ Q_INI(&s32, QTEST_IV, 0, QTEST_RPSHFT);
+ Q_TOSTR(s32, -1, 10, buf, sizeof(buf));
+ ATF_CHECK_STREQ(QTEST_IVSTR, buf);
+ ATF_CHECK_EQ(sizeof(s32) << 3, Q_NTBITS(s32));
+ ATF_CHECK_EQ(QTEST_RPSHFT, Q_NFBITS(s32));
+ ATF_CHECK_EQ(QTEST_INTBITS(s32), Q_NIBITS(s32));
+ ATF_CHECK_EQ(QTEST_QITRUNC(s32, INT32_MAX), Q_IMAXVAL(s32));
+ ATF_CHECK_EQ(-Q_IMAXVAL(s32), Q_IMINVAL(s32));
+}
+
+ATF_TC_WITHOUT_HEAD(basic_s64q);
+ATF_TC_BODY(basic_s64q, tc)
+{
+ char buf[128];
+ s64q_t s64;
+
+ Q_INI(&s64, QTEST_IV, 0, QTEST_RPSHFT);
+ Q_TOSTR(s64, -1, 10, buf, sizeof(buf));
+ ATF_CHECK_STREQ(QTEST_IVSTR, buf);
+ ATF_CHECK_EQ(sizeof(s64) << 3, Q_NTBITS(s64));
+ ATF_CHECK_EQ(QTEST_RPSHFT, Q_NFBITS(s64));
+ ATF_CHECK_EQ(QTEST_INTBITS(s64), Q_NIBITS(s64));
+ ATF_CHECK_EQ(QTEST_QITRUNC(s64, INT64_MAX), Q_IMAXVAL(s64));
+ ATF_CHECK_EQ(-Q_IMAXVAL(s64), Q_IMINVAL(s64));
+}
+
+ATF_TC_WITHOUT_HEAD(basic_u8q);
+ATF_TC_BODY(basic_u8q, tc)
+{
+ char buf[128];
+ u8q_t u8;
+
+ Q_INI(&u8, QTEST_IV, 0, QTEST_RPSHFT);
+ Q_TOSTR(u8, -1, 10, buf, sizeof(buf));
+ ATF_CHECK_STREQ(QTEST_IVSTR, buf);
+ ATF_CHECK_EQ(sizeof(u8) << 3, Q_NTBITS(u8));
+ ATF_CHECK_EQ(QTEST_RPSHFT, Q_NFBITS(u8));
+ ATF_CHECK_EQ(QTEST_INTBITS(u8), Q_NIBITS(u8));
+ ATF_CHECK_EQ(QTEST_QITRUNC(u8, UINT8_MAX), Q_IMAXVAL(u8));
+ ATF_CHECK_EQ(0, Q_IMINVAL(u8));
+}
+
+ATF_TC_WITHOUT_HEAD(basic_u16q);
+ATF_TC_BODY(basic_u16q, tc)
+{
+ char buf[128];
+ u16q_t u16;
+
+ Q_INI(&u16, QTEST_IV, 0, QTEST_RPSHFT);
+ Q_TOSTR(u16, -1, 10, buf, sizeof(buf));
+ ATF_CHECK_STREQ(QTEST_IVSTR, buf);
+ ATF_CHECK_EQ(sizeof(u16) << 3, Q_NTBITS(u16));
+ ATF_CHECK_EQ(QTEST_RPSHFT, Q_NFBITS(u16));
+ ATF_CHECK_EQ(QTEST_INTBITS(u16), Q_NIBITS(u16));
+ ATF_CHECK_EQ(QTEST_QITRUNC(u16, UINT16_MAX), Q_IMAXVAL(u16));
+ ATF_CHECK_EQ(0, Q_IMINVAL(u16));
+}
+
+ATF_TC_WITHOUT_HEAD(basic_u32q);
+ATF_TC_BODY(basic_u32q, tc)
+{
+ char buf[128];
+ u32q_t u32;
+
+ Q_INI(&u32, QTEST_IV, 0, QTEST_RPSHFT);
+ Q_TOSTR(u32, -1, 10, buf, sizeof(buf));
+ ATF_CHECK_STREQ(QTEST_IVSTR, buf);
+ ATF_CHECK_EQ(sizeof(u32) << 3, Q_NTBITS(u32));
+ ATF_CHECK_EQ(QTEST_RPSHFT, Q_NFBITS(u32));
+ ATF_CHECK_EQ(QTEST_INTBITS(u32), Q_NIBITS(u32));
+ ATF_CHECK_EQ(QTEST_QITRUNC(u32, UINT32_MAX), Q_IMAXVAL(u32));
+ ATF_CHECK_EQ(0, Q_IMINVAL(u32));
+}
+
+ATF_TC_WITHOUT_HEAD(basic_u64q);
+ATF_TC_BODY(basic_u64q, tc)
+{
+ char buf[128];
+ u64q_t u64;
+
+ Q_INI(&u64, QTEST_IV, 0, QTEST_RPSHFT);
+ Q_TOSTR(u64, -1, 10, buf, sizeof(buf));
+ ATF_CHECK_STREQ(QTEST_IVSTR, buf);
+ ATF_CHECK_EQ(sizeof(u64) << 3, Q_NTBITS(u64));
+ ATF_CHECK_EQ(QTEST_RPSHFT, Q_NFBITS(u64));
+ ATF_CHECK_EQ(QTEST_INTBITS(u64), Q_NIBITS(u64));
+ ATF_CHECK_EQ(QTEST_QITRUNC(u64, UINT64_MAX), Q_IMAXVAL(u64));
+ ATF_CHECK_EQ(0, Q_IMINVAL(u64));
+}
+
+/*
+ * Test Q_QMULQ(3) by applying it to two random Q numbers and comparing
+ * the result with its floating-point counterpart.
+ */
+ATF_TC_WITHOUT_HEAD(qmulq_s64q);
+ATF_TC_BODY(qmulq_s64q, tc)
+{
+ s64q_t a_s64q, b_s64q, r_s64q;
+ double a_dbl, b_dbl, r_dbl, maxe_dbl, delta_dbl;
+#ifdef notyet
+ int64_t a_int, b_int;
+#endif
+ int error;
+
+ srandomdev();
+
+ for (int i = 0; i < 10;) {
+ GENRAND(&a_s64q, INT64_MIN, UINT64_MAX);
+ GENRAND(&b_s64q, INT64_MIN, UINT64_MAX);
+
+ /*
+ * XXX: We cheat a bit, to stand any chance of multiplying
+ * without overflow.
+ */
+ error = Q_QDIVQ(&a_s64q, b_s64q);
+ if (error == EOVERFLOW || error == ERANGE)
+ continue;
+ ATF_CHECK_EQ(0, error);
+
+ /*
+ * XXXLAS: Until Qmath handles precision normalisation, only
+ * test with equal precision.
+ */
+ Q_SCVAL(b_s64q, Q_GCVAL(a_s64q));
+
+ /* Q<op>Q testing. */
+ a_dbl = Q_Q2D(a_s64q);
+ b_dbl = Q_Q2D(b_s64q);
+
+ r_s64q = a_s64q;
+ error = Q_QMULQ(&r_s64q, b_s64q);
+ if (error == EOVERFLOW || error == ERANGE)
+ continue;
+ i++;
+ ATF_CHECK_EQ(0, error);
+
+ r_dbl = a_dbl * b_dbl;
+#ifdef notyet
+ a_int = Q_GIVAL(a_s64q);
+ b_int = Q_GIVAL(b_s64q);
+
+ maxe_dbl = fabs(((1.0 / Q_NFBITS(a_s64q)) * (double)b_int) +
+ ((1.0 / Q_NFBITS(b_s64q)) * (double)a_int));
+#else
+ maxe_dbl = QTEST_FFACTOR;
+#endif
+ delta_dbl = fabs(r_dbl - Q_Q2D(r_s64q));
+ ATF_CHECK_MSG(delta_dbl <= maxe_dbl,
+ "\tQMULQ(%10f * %10f): |%10f - %10f| = %10f "
+ "(max err %f)\n",
+ Q_Q2D(a_s64q), Q_Q2D(b_s64q), Q_Q2D(r_s64q), r_dbl,
+ delta_dbl, maxe_dbl);
+ }
+}
+
+/*
+ * Test Q_QDIVQ(3) by applying it to two random Q numbers and comparing
+ * the result with its floating-point counterpart.
+ */
+ATF_TC_WITHOUT_HEAD(qdivq_s64q);
+ATF_TC_BODY(qdivq_s64q, tc)
+{
+ s64q_t a_s64q, b_s64q, r_s64q;
+ double a_dbl, b_dbl, r_dbl, maxe_dbl, delta_dbl;
+ int error;
+
+ if (atf_tc_get_config_var_as_bool_wd(tc, "ci", false))
+ atf_tc_skip("https://bugs.freebsd.org/240219");
+
+
+ srandomdev();
+
+ for (int i = 0; i < 10; i++) {
+ GENRAND(&a_s64q, INT64_MIN, UINT64_MAX);
+ GENRAND(&b_s64q, INT64_MIN, UINT64_MAX);
+ /*
+ * XXXLAS: Until Qmath handles precision normalisation, only
+ * test with equal precision.
+ */
+ Q_SCVAL(b_s64q, Q_GCVAL(a_s64q));
+
+ /* Q<op>Q testing. */
+ a_dbl = Q_Q2D(a_s64q);
+ b_dbl = Q_Q2D(b_s64q);
+
+ r_s64q = a_s64q;
+ error = Q_QDIVQ(&r_s64q, b_s64q);
+ ATF_CHECK_EQ(0, error);
+
+ r_dbl = a_dbl / b_dbl;
+#ifdef notyet
+ maxe_dbl = fabs(1.0 / (1ULL << Q_NFBITS(a_s64q)));
+#else
+ maxe_dbl = QTEST_FFACTOR * 2;
+#endif
+ delta_dbl = fabs(r_dbl - Q_Q2D(r_s64q));
+ ATF_CHECK_MSG(delta_dbl <= maxe_dbl,
+ "\tQDIVQ(%10f / %10f): |%10f - %10f| = %10f "
+ "(max err %f)\n",
+ Q_Q2D(a_s64q), Q_Q2D(b_s64q), Q_Q2D(r_s64q), r_dbl,
+ delta_dbl, maxe_dbl);
+ }
+}
+
+/*
+ * Test Q_QADDQ(3) by applying it to two random Q numbers and comparing
+ * the result with its floating-point counterpart.
+ */
+ATF_TC_WITHOUT_HEAD(qaddq_s64q);
+ATF_TC_BODY(qaddq_s64q, tc)
+{
+ s64q_t a_s64q, b_s64q, r_s64q;
+ double a_dbl, b_dbl, r_dbl, maxe_dbl, delta_dbl;
+ int error;
+
+ srandomdev();
+
+ for (int i = 0; i < 10;) {
+ GENRAND(&a_s64q, INT64_MIN, UINT64_MAX);
+ GENRAND(&b_s64q, INT64_MIN, UINT64_MAX);
+ /*
+ * XXXLAS: Until Qmath handles precision normalisation, only
+ * test with equal precision.
+ */
+ Q_SCVAL(b_s64q, Q_GCVAL(a_s64q));
+
+ /* Q<op>Q testing. */
+ a_dbl = Q_Q2D(a_s64q);
+ b_dbl = Q_Q2D(b_s64q);
+
+ r_s64q = a_s64q;
+ error = Q_QADDQ(&r_s64q, b_s64q);
+ if (error == EOVERFLOW || error == ERANGE)
+ continue;
+ i++;
+ ATF_CHECK_EQ(0, error);
+
+ r_dbl = a_dbl + b_dbl;
+#ifdef notyet
+ maxe_dbl = 0.5;
+#else
+ maxe_dbl = QTEST_FFACTOR;
+#endif
+ delta_dbl = fabs(r_dbl - Q_Q2D(r_s64q));
+ ATF_CHECK_MSG(delta_dbl <= maxe_dbl,
+ "\tQADDQ(%10f + %10f): |%10f - %10f| = %10f "
+ "(max err %f)\n",
+ Q_Q2D(a_s64q), Q_Q2D(b_s64q), Q_Q2D(r_s64q), r_dbl,
+ delta_dbl, maxe_dbl);
+ }
+}
+
+/*
+ * Test Q_QSUBQ(3) by applying it to two random Q numbers and comparing
+ * the result with its floating-point counterpart.
+ */
+ATF_TC_WITHOUT_HEAD(qsubq_s64q);
+ATF_TC_BODY(qsubq_s64q, tc)
+{
+ s64q_t a_s64q, b_s64q, r_s64q;
+ double a_dbl, b_dbl, r_dbl, maxe_dbl, delta_dbl;
+ int error;
+
+ srandomdev();
+
+ for (int i = 0; i < 10; i++) {
+ GENRAND(&a_s64q, INT64_MIN, UINT64_MAX);
+ GENRAND(&b_s64q, INT64_MIN, UINT64_MAX);
+ /*
+ * XXXLAS: Until Qmath handles precision normalisation, only
+ * test with equal precision.
+ */
+ Q_SCVAL(b_s64q, Q_GCVAL(a_s64q));
+
+ /* Q<op>Q testing. */
+ a_dbl = Q_Q2D(a_s64q);
+ b_dbl = Q_Q2D(b_s64q);
+
+ r_s64q = a_s64q;
+ error = Q_QSUBQ(&r_s64q, b_s64q);
+ ATF_CHECK_EQ(0, error);
+
+ r_dbl = a_dbl - b_dbl;
+#ifdef notyet
+ maxe_dbl = 0.5;
+#else
+ maxe_dbl = QTEST_FFACTOR;
+#endif
+ delta_dbl = fabs(r_dbl - Q_Q2D(r_s64q));
+ ATF_CHECK_MSG(delta_dbl <= maxe_dbl,
+ "\tQSUBQ(%10f - %10f): |%10f - %10f| = %10f "
+ "(max err %f)\n",
+ Q_Q2D(a_s64q), Q_Q2D(b_s64q), Q_Q2D(r_s64q), r_dbl,
+ delta_dbl, maxe_dbl);
+ }
+}
+
+/*
+ * Test Q_QFRACI(3) by applying it to two random integers and comparing
+ * the result with its floating-point counterpart.
+ */
+ATF_TC_WITHOUT_HEAD(qfraci_s64q);
+ATF_TC_BODY(qfraci_s64q, tc)
+{
+ s64q_t a_s64q, b_s64q, r_s64q;
+ double a_dbl, b_dbl, r_dbl, maxe_dbl, delta_dbl;
+ int64_t a_int, b_int;
+ int error;
+
+ srandomdev();
+
+ for (int i = 0; i < 10;) {
+ GENRAND(&a_s64q, INT64_MIN, UINT64_MAX);
+ GENRAND(&b_s64q, INT64_MIN, UINT64_MAX);
+ /*
+ * XXXLAS: Until Qmath handles precision normalisation, only
+ * test with equal precision.
+ */
+ Q_SCVAL(b_s64q, Q_GCVAL(a_s64q));
+ a_int = Q_GIVAL(a_s64q);
+ b_int = Q_GIVAL(b_s64q);
+
+ /* Q<op>I testing. */
+ a_dbl = a_int;
+ b_dbl = b_int;
+
+ Q_INI(&r_s64q, 0, 0, Q_NFBITS(a_s64q));
+ error = Q_QFRACI(&r_s64q, a_int, b_int);
+ if (error == EOVERFLOW || error == ERANGE || error == EINVAL)
+ continue;
+ i++;
+ ATF_CHECK_EQ(0, error);
+
+ r_dbl = a_dbl / b_dbl;
+ maxe_dbl = fabs(1.0 / Q_NFBITS(a_s64q));
+ delta_dbl = fabs(r_dbl - Q_Q2D(r_s64q));
+ ATF_CHECK_MSG(delta_dbl <= maxe_dbl,
+ "\tQFRACI(%jd / %jd): |%10f - %10f| = %10f "
+ "(max err %f)\n",
+ (intmax_t)a_int, (intmax_t)b_int, Q_Q2D(r_s64q),
+ r_dbl, delta_dbl, maxe_dbl);
+ }
+}
+
+/*
+ * Test Q_QMULI(3) by applying it to a random Q number and a random integer
+ * and comparing the result with its floating-point counterpart.
+ */
+ATF_TC_WITHOUT_HEAD(qmuli_s64q);
+ATF_TC_BODY(qmuli_s64q, tc)
+{
+ s64q_t a_s64q, b_s64q, r_s64q;
+ double a_dbl, b_dbl, r_dbl, maxe_dbl, delta_dbl;
+ int64_t a_int, b_int;
+ int error;
+
+ srandomdev();
+
+ for (int i = 0; i < 10;) {
+ GENRAND(&a_s64q, INT64_MIN, UINT64_MAX);
+ GENRAND(&b_s64q, INT64_MIN, UINT64_MAX);
+ /*
+ * XXXLAS: Until Qmath handles precision normalisation, only
+ * test with equal precision.
+ */
+ Q_SCVAL(b_s64q, Q_GCVAL(a_s64q));
+ a_int = Q_GIVAL(a_s64q);
+ b_int = Q_GIVAL(b_s64q);
+
+ /* Q<op>I testing. */
+ a_dbl = a_int;
+ b_dbl = b_int;
+
+ Q_INI(&r_s64q, a_int, 0, Q_NFBITS(a_s64q));
+ error = Q_QMULI(&r_s64q, b_int);
+ if (error == EOVERFLOW || error == ERANGE)
+ continue;
+ i++;
+ ATF_CHECK_EQ(0, error);
+
+ r_dbl = a_dbl * b_dbl;
+ maxe_dbl = fabs((1.0 / Q_NFBITS(a_s64q)) * (double)b_int);
+ delta_dbl = fabs(r_dbl - Q_Q2D(r_s64q));
+ ATF_CHECK_MSG(delta_dbl <= maxe_dbl,
+ "\tQMULI(%jd * %jd): |%10f - %10f| = %10f "
+ "(max err %f)\n",
+ (intmax_t)(intmax_t)a_int, b_int, Q_Q2D(r_s64q),
+ r_dbl, delta_dbl, maxe_dbl);
+ }
+}
+
+/*
+ * Test Q_QADDI(3) by applying it to a random Q number and a random integer
+ * and comparing the result with its floating-point counterpart.
+ */
+ATF_TC_WITHOUT_HEAD(qaddi_s64q);
+ATF_TC_BODY(qaddi_s64q, tc)
+{
+ s64q_t a_s64q, b_s64q, r_s64q;
+ double a_dbl, b_dbl, r_dbl, maxe_dbl, delta_dbl;
+ int64_t a_int, b_int;
+ int error;
+
+ srandomdev();
+
+ for (int i = 0; i < 10;) {
+ GENRAND(&a_s64q, INT64_MIN, UINT64_MAX);
+ GENRAND(&b_s64q, INT64_MIN, UINT64_MAX);
+ /*
+ * XXXLAS: Until Qmath handles precision normalisation, only
+ * test with equal precision.
+ */
+ Q_SCVAL(b_s64q, Q_GCVAL(a_s64q));
+ a_int = Q_GIVAL(a_s64q);
+ b_int = Q_GIVAL(b_s64q);
+
+ /* Q<op>I testing. */
+ a_dbl = a_int;
+ b_dbl = b_int;
+
+ Q_INI(&r_s64q, a_int, 0, Q_NFBITS(a_s64q));
+ error = Q_QADDI(&r_s64q, b_int);
+ if (error == EOVERFLOW || error == ERANGE)
+ continue;
+ i++;
+ ATF_CHECK_EQ(0, error);
+
+ r_dbl = a_dbl + b_dbl;
+#ifdef notyet
+ maxe_dbl = 0.5;
+#else
+ maxe_dbl = QTEST_FFACTOR;
+#endif
+ delta_dbl = fabs(r_dbl - Q_Q2D(r_s64q));
+ ATF_CHECK_MSG(delta_dbl <= maxe_dbl,
+ "\tQADDI(%jd + %jd): |%10f - %10f| = %10f "
+ "(max err %f)\n",
+ (intmax_t)a_int, (intmax_t)b_int, Q_Q2D(r_s64q),
+ r_dbl, delta_dbl, maxe_dbl);
+ }
+}
+
+/*
+ * Test Q_QSUBI(3) by applying it to a random Q number and a random integer
+ * and comparing the result with its floating-point counterpart.
+ */
+ATF_TC_WITHOUT_HEAD(qsubi_s64q);
+ATF_TC_BODY(qsubi_s64q, tc)
+{
+ s64q_t a_s64q, b_s64q, r_s64q;
+ double a_dbl, b_dbl, r_dbl, maxe_dbl, delta_dbl;
+ int64_t a_int, b_int;
+ int error;
+
+ srandomdev();
+
+ for (int i = 0; i < 10; i++) {
+ GENRAND(&a_s64q, INT64_MIN, UINT64_MAX);
+ GENRAND(&b_s64q, INT64_MIN, UINT64_MAX);
+ /*
+ * XXXLAS: Until Qmath handles precision normalisation, only
+ * test with equal precision.
+ */
+ Q_SCVAL(b_s64q, Q_GCVAL(a_s64q));
+ a_int = Q_GIVAL(a_s64q);
+ b_int = Q_GIVAL(b_s64q);
+
+ /* Q<op>I testing. */
+ a_dbl = a_int;
+ b_dbl = b_int;
+
+ Q_INI(&r_s64q, a_int, 0, Q_NFBITS(a_s64q));
+ error = Q_QSUBI(&r_s64q, b_int);
+ ATF_CHECK_EQ(0, error);
+
+ r_dbl = a_dbl - b_dbl;
+#ifdef notyet
+ maxe_dbl = 0.5;
+#else
+ maxe_dbl = QTEST_FFACTOR;
+#endif
+ delta_dbl = fabs(r_dbl - Q_Q2D(r_s64q));
+ ATF_CHECK_MSG(delta_dbl <= maxe_dbl,
+ "\tQSUBI(%jd - %jd): |%10f - %10f| = %10f "
+ "(max err %f)\n",
+ (intmax_t)a_int, (intmax_t)b_int, Q_Q2D(r_s64q),
+ r_dbl, delta_dbl, maxe_dbl);
+ }
+}
+
+/*
+ * Calculate area of a circle with r=42.
+ */
+ATF_TC_WITHOUT_HEAD(circle_u64q);
+ATF_TC_BODY(circle_u64q, tc)
+{
+ char buf[128];
+ u64q_t a, pi, r;
+ int error;
+
+ Q_INI(&a, 0, 0, 16);
+ Q_INI(&pi, 3, 14159, 16);
+ Q_INI(&r, 4, 2, 16);
+
+ error = Q_QCLONEQ(&a, r);
+ ATF_CHECK_EQ(0, error);
+ error = Q_QMULQ(&a, r);
+ ATF_CHECK_EQ(0, error);
+ error = Q_QMULQ(&a, pi);
+ ATF_CHECK_EQ(0, error);
+
+ Q_TOSTR(a, -1, 10, buf, sizeof(buf));
+ ATF_CHECK_STREQ("55.4174804687500000", buf);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, basic_s8q);
+ ATF_TP_ADD_TC(tp, basic_s16q);
+ ATF_TP_ADD_TC(tp, basic_s32q);
+ ATF_TP_ADD_TC(tp, basic_s64q);
+ ATF_TP_ADD_TC(tp, basic_u8q);
+ ATF_TP_ADD_TC(tp, basic_u16q);
+ ATF_TP_ADD_TC(tp, basic_u32q);
+ ATF_TP_ADD_TC(tp, basic_u64q);
+
+ ATF_TP_ADD_TC(tp, qmulq_s64q);
+ ATF_TP_ADD_TC(tp, qdivq_s64q);
+ ATF_TP_ADD_TC(tp, qaddq_s64q);
+ ATF_TP_ADD_TC(tp, qsubq_s64q);
+ ATF_TP_ADD_TC(tp, qfraci_s64q);
+ ATF_TP_ADD_TC(tp, qmuli_s64q);
+ ATF_TP_ADD_TC(tp, qaddi_s64q);
+ ATF_TP_ADD_TC(tp, qsubi_s64q);
+
+ ATF_TP_ADD_TC(tp, circle_u64q);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/sys/queue_test.c b/tests/sys/sys/queue_test.c
new file mode 100644
index 000000000000..7f8738751b85
--- /dev/null
+++ b/tests/sys/sys/queue_test.c
@@ -0,0 +1,293 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 The FreeBSD Foundation
+ *
+ * This software was developed by Olivier Certner <olce@FreeBSD.org> at
+ * Kumacom SARL under sponsorship from the FreeBSD Foundation.
+ */
+
+#include <sys/types.h>
+#define QUEUE_MACRO_DEBUG_ASSERTIONS
+#include <sys/queue.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <atf-c.h>
+
+/*
+ * General utilities.
+ */
+#define DIAG(fmt, ...) do { \
+ fprintf(stderr, "%s(): " fmt "\n", __func__, ##__VA_ARGS__); \
+} while (0)
+
+/*
+ * Common definitions and utilities.
+ *
+ * 'type' should be tailq, stailq, list or slist. 'TYPE' is 'type' in
+ * uppercase.
+ */
+
+#define QUEUE_TESTS_COMMON(type, TYPE) \
+/* \
+ * Definitions and utilities. \
+ */ \
+ \
+struct type ## _id_elem { \
+ TYPE ## _ENTRY(type ## _id_elem) ie_entry; \
+ u_int ie_id; \
+}; \
+ \
+TYPE ## _HEAD(type ## _ids, type ## _id_elem); \
+ \
+static void \
+type ## _check(const struct type ## _ids *const type, \
+ const u_int nb, const u_int id_shift); \
+ \
+/* \
+ * Creates a tailq/list with 'nb' elements with contiguous IDs \
+ * in ascending order starting at 'id_shift'. \
+ */ \
+static struct type ## _ids * \
+type ## _create(const u_int nb, const u_int id_shift) \
+{ \
+ struct type ## _ids *const type = \
+ malloc(sizeof(*type)); \
+ \
+ ATF_REQUIRE_MSG(type != NULL, \
+ "Cannot malloc " #type " head"); \
+ \
+ TYPE ## _INIT(type); \
+ for (u_int i = 0; i < nb; ++i) { \
+ struct type ## _id_elem *const e = \
+ malloc(sizeof(*e)); \
+ \
+ ATF_REQUIRE_MSG(e != NULL, \
+ "Cannot malloc " #type " element %u", i); \
+ e->ie_id = nb - 1 - i + id_shift; \
+ TYPE ## _INSERT_HEAD(type, e, ie_entry); \
+ } \
+ \
+ DIAG("Created " #type " %p with %u elements", \
+ type, nb); \
+ type ## _check(type, nb, id_shift); \
+ return (type); \
+} \
+ \
+/* Performs no check. */ \
+static void \
+type ## _destroy(struct type ## _ids *const type) \
+{ \
+ struct type ## _id_elem *e, *tmp_e; \
+ \
+ DIAG("Destroying " #type" %p", type); \
+ TYPE ## _FOREACH_SAFE(e, type, ie_entry, \
+ tmp_e) { \
+ free(e); \
+ } \
+ free(type); \
+} \
+ \
+ \
+/* Checks that some tailq/list is as produced by *_create(). */ \
+static void \
+type ## _check(const struct type ## _ids *const type, \
+ const u_int nb, const u_int id_shift) \
+{ \
+ struct type ## _id_elem *e; \
+ u_int i = 0; \
+ \
+ TYPE ## _FOREACH(e, type, ie_entry) { \
+ ATF_REQUIRE_MSG(i + 1 <= nb, \
+ #type " %p has more than %u elements", \
+ type, nb); \
+ ATF_REQUIRE_MSG(e->ie_id == i + id_shift, \
+ #type " %p element %p: Found ID %u, " \
+ "expected %u", \
+ type, e, e->ie_id, i + id_shift); \
+ ++i; \
+ } \
+ ATF_REQUIRE_MSG(i == nb, \
+ #type " %p has only %u elements, expected %u", \
+ type, i, nb); \
+} \
+ \
+/* Returns NULL if not enough elements. */ \
+static struct type ## _id_elem * \
+type ## _nth(const struct type ## _ids *const type, \
+ const u_int idx) \
+{ \
+ struct type ## _id_elem *e; \
+ u_int i = 0; \
+ \
+ TYPE ## _FOREACH(e, type, ie_entry) { \
+ if (i == idx) { \
+ DIAG(#type " %p has element %p " \
+ "(ID %u) at index %u", \
+ type, e, e->ie_id, idx); \
+ return (e); \
+ } \
+ ++i; \
+ } \
+ DIAG(#type " %p: Only %u elements, no index %u", \
+ type, i, idx); \
+ return (NULL); \
+} \
+ \
+/* \
+ * Tests. \
+ */ \
+ \
+ATF_TC(type ## _split_after_and_concat); \
+ATF_TC_HEAD(type ## _split_after_and_concat, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", \
+ "Test " #TYPE "_SPLIT_AFTER() followed by " \
+ #TYPE "_CONCAT()"); \
+} \
+ATF_TC_BODY(type ## _split_after_and_concat, tc) \
+{ \
+ struct type ## _ids *const type = \
+ type ## _create(100, 0); \
+ struct type ## _ids rest; \
+ struct type ## _id_elem *e; \
+ \
+ e = type ## _nth(type, 49); \
+ TYPE ## _SPLIT_AFTER(type, e, &rest, ie_entry); \
+ type ## _check(type, 50, 0); \
+ type ## _check(&rest, 50, 50); \
+ QUEUE_TESTS_ ## TYPE ## _CONCAT(type, &rest); \
+ ATF_REQUIRE_MSG(TYPE ## _EMPTY(&rest), \
+ "'rest' not empty after concat"); \
+ type ## _check(type, 100, 0); \
+ type ## _destroy(type); \
+}
+
+#define QUEUE_TESTS_CHECK_REVERSED(type, TYPE) \
+/* \
+ * Checks that some tailq/list is reversed. \
+ */ \
+static void \
+type ## _check_reversed(const struct type ## _ids *const type, \
+ const u_int nb, const u_int id_shift) \
+{ \
+ struct type ## _id_elem *e; \
+ u_int i = 0; \
+ \
+ TYPE ## _FOREACH(e, type, ie_entry) { \
+ const u_int expected_id = nb - 1 - i + id_shift; \
+ \
+ ATF_REQUIRE_MSG(i < nb, \
+ #type " %p has more than %u elements", \
+ type, nb); \
+ ATF_REQUIRE_MSG(e->ie_id == expected_id, \
+ #type " %p element %p, idx %u: Found ID %u, " \
+ "expected %u", \
+ type, e, i, e->ie_id, expected_id); \
+ ++i; \
+ } \
+ ATF_REQUIRE_MSG(i == nb, \
+ #type " %p has only %u elements, expected %u", \
+ type, i, nb); \
+}
+
+/*
+ * Paper over the *_CONCAT() signature differences.
+ */
+
+#define QUEUE_TESTS_TAILQ_CONCAT(first, second) \
+ TAILQ_CONCAT(first, second, ie_entry)
+
+#define QUEUE_TESTS_LIST_CONCAT(first, second) \
+ LIST_CONCAT(first, second, list_id_elem, ie_entry)
+
+#define QUEUE_TESTS_STAILQ_CONCAT(first, second) \
+ STAILQ_CONCAT(first, second)
+
+#define QUEUE_TESTS_SLIST_CONCAT(first, second) \
+ SLIST_CONCAT(first, second, slist_id_elem, ie_entry)
+
+/*
+ * ATF test registration.
+ */
+
+#define QUEUE_TESTS_REGISTRATION(tp, type) \
+ ATF_TP_ADD_TC(tp, type ## _split_after_and_concat)
+
+/*
+ * Macros defining print functions.
+ *
+ * They are currently not used in the tests above, but are useful for debugging.
+ */
+
+#define QUEUE_TESTS_TQ_PRINT(type, hfp) \
+ static void \
+ type ## _print(const struct type ## _ids *const type) \
+ { \
+ printf(#type " %p: " __STRING(hfp ## _first) \
+ " = %p, " __STRING(hfp ## _last) " = %p\n", \
+ type, type->hfp ## _first, type->hfp ## _last); \
+ }
+
+#define QUEUE_TESTS_L_PRINT(type, hfp) \
+ static void \
+ type ## _print(const struct type ## _ids *const type) \
+ { \
+ printf(#type " %p: " __STRING(hfp ## _first) " = %p\n", \
+ type, type->hfp ## _first); \
+ }
+
+
+/*
+ * Meat.
+ */
+
+/* Common tests. */
+QUEUE_TESTS_COMMON(tailq, TAILQ);
+QUEUE_TESTS_COMMON(list, LIST);
+QUEUE_TESTS_COMMON(stailq, STAILQ);
+QUEUE_TESTS_COMMON(slist, SLIST);
+
+/* STAILQ_REVERSE(). */
+QUEUE_TESTS_CHECK_REVERSED(stailq, STAILQ);
+ATF_TC(stailq_reverse);
+ATF_TC_HEAD(stailq_reverse, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test STAILQ_REVERSE");
+}
+ATF_TC_BODY(stailq_reverse, tc)
+{
+ const u_int size = 100;
+ struct stailq_ids *const stailq = stailq_create(size, 0);
+ struct stailq_ids *const empty_stailq = stailq_create(0, 0);
+ const struct stailq_id_elem *last;
+
+ stailq_check(stailq, size, 0);
+ STAILQ_REVERSE(stailq, stailq_id_elem, ie_entry);
+ stailq_check_reversed(stailq, size, 0);
+ last = STAILQ_LAST(stailq, stailq_id_elem, ie_entry);
+ ATF_REQUIRE_MSG(last->ie_id == 0,
+ "Last element of stailq %p has id %u, expected 0",
+ stailq, last->ie_id);
+ stailq_destroy(stailq);
+
+ STAILQ_REVERSE(empty_stailq, stailq_id_elem, ie_entry);
+ stailq_check(empty_stailq, 0, 0);
+ stailq_destroy(empty_stailq);
+}
+
+/*
+ * Main.
+ */
+ATF_TP_ADD_TCS(tp)
+{
+ QUEUE_TESTS_REGISTRATION(tp, tailq);
+ QUEUE_TESTS_REGISTRATION(tp, list);
+ QUEUE_TESTS_REGISTRATION(tp, stailq);
+ QUEUE_TESTS_REGISTRATION(tp, slist);
+ ATF_TP_ADD_TC(tp, stailq_reverse);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/sys/rb_test.c b/tests/sys/sys/rb_test.c
new file mode 100644
index 000000000000..6e3e1c4782a6
--- /dev/null
+++ b/tests/sys/sys/rb_test.c
@@ -0,0 +1,113 @@
+/* $OpenBSD: rb-test.c,v 1.4 2008/04/13 00:22:17 djm Exp $ */
+/*
+ * Copyright 2002 Niels Provos <provos@citi.umich.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/types.h>
+
+#define _RB_DIAGNOSTIC 1
+#include <sys/tree.h>
+#include <stdlib.h>
+
+#include <atf-c.h>
+
+struct node {
+ RB_ENTRY(node) node;
+ int key;
+};
+
+static RB_HEAD(tree, node) root;
+
+static int
+compare(struct node *a, struct node *b)
+{
+ if (a->key < b->key) return (-1);
+ else if (a->key > b->key) return (1);
+ return (0);
+}
+
+RB_PROTOTYPE(tree, node, node, compare);
+
+RB_GENERATE(tree, node, node, compare);
+
+#define ITER 150
+
+ATF_TC_WITHOUT_HEAD(rb_test);
+ATF_TC_BODY(rb_test, tc)
+{
+ struct node *tmp, *ins, store[ITER];
+ int i, j, k, max, min;
+
+ min = ITER;
+ max = -1;
+
+ RB_INIT(&root);
+
+ /* Initialize keys */
+ for (i = 0; i < ITER; i++)
+ store[i].key = i;
+
+ /* Randomly shuffle keys */
+ for (i = 0; i < ITER; i++) {
+ j = i + arc4random_uniform(ITER - i);
+ k = store[j].key;
+ store[j].key = store[i].key;
+ store[i].key = k;
+ }
+
+ for (i = 0; i < ITER; i++) {
+ for (j = 0; j < i; ++j) {
+ tmp = &store[j];
+ ATF_REQUIRE_EQ(tmp, RB_FIND(tree, &root, tmp));
+ }
+ tmp = &store[i];
+ if (tmp->key > max)
+ max = tmp->key;
+ if (tmp->key < min)
+ min = tmp->key;
+ ATF_REQUIRE_EQ(NULL, RB_INSERT(tree, &root, tmp));
+ ins = RB_MIN(tree, &root);
+ ATF_REQUIRE_MSG(ins != NULL, "RB_MIN error");
+ ATF_CHECK_EQ(min, ins->key);
+ ins = RB_MAX(tree, &root);
+ ATF_REQUIRE_MSG(ins != NULL, "RB_MAX error");
+ ATF_CHECK_EQ(max, ins->key);
+ }
+ tmp = RB_ROOT(&root);
+ ATF_REQUIRE_MSG(tree_RB_RANK(tmp) >= 0, "RB rank balance error");
+ for (i = 0; i < ITER; i++) {
+ tmp = RB_ROOT(&root);
+ ATF_REQUIRE_MSG(tmp != NULL, "RB_ROOT error");
+ ATF_CHECK_EQ(tmp, RB_REMOVE(tree, &root, tmp));
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, rb_test);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/sys/splay_test.c b/tests/sys/sys/splay_test.c
new file mode 100644
index 000000000000..8ff3fc74af5c
--- /dev/null
+++ b/tests/sys/sys/splay_test.c
@@ -0,0 +1,110 @@
+/* $OpenBSD: splay-test.c,v 1.4 2008/04/13 00:22:17 djm Exp $ */
+/*
+ * Copyright 2002 Niels Provos <provos@citi.umich.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/types.h>
+
+#include <sys/tree.h>
+#include <stdlib.h>
+
+#include <atf-c.h>
+
+struct node {
+ SPLAY_ENTRY(node) node;
+ int key;
+};
+
+static SPLAY_HEAD(tree, node) root;
+
+static int
+compare(struct node *a, struct node *b)
+{
+ if (a->key < b->key) return (-1);
+ else if (a->key > b->key) return (1);
+ return (0);
+}
+
+SPLAY_PROTOTYPE(tree, node, node, compare);
+
+SPLAY_GENERATE(tree, node, node, compare);
+
+#define ITER 150
+#define MIN 5
+#define MAX 5000
+
+ATF_TC_WITHOUT_HEAD(splay_test);
+ATF_TC_BODY(splay_test, tc)
+{
+ struct node *tmp, *ins;
+ int i, max, min;
+
+ SPLAY_INIT(&root);
+
+ max = min = 42; /* pacify gcc */
+
+ for (i = 0; i < ITER; i++) {
+ tmp = malloc(sizeof(struct node));
+ ATF_REQUIRE_MSG(tmp != NULL, "malloc failed");
+ do {
+ tmp->key = arc4random_uniform(MAX-MIN);
+ tmp->key += MIN;
+ } while (SPLAY_FIND(tree, &root, tmp) != NULL);
+ if (i == 0)
+ max = min = tmp->key;
+ else {
+ if (tmp->key > max)
+ max = tmp->key;
+ if (tmp->key < min)
+ min = tmp->key;
+ }
+ ATF_REQUIRE_EQ(NULL, SPLAY_INSERT(tree, &root, tmp));
+ }
+
+ ins = SPLAY_MIN(tree, &root);
+ ATF_REQUIRE_MSG(ins != NULL, "SPLAY_MIN error");
+ ATF_CHECK_EQ(min, ins->key);
+ tmp = ins;
+ ins = SPLAY_MAX(tree, &root);
+ ATF_REQUIRE_MSG(ins != NULL, "SPLAY_MAX error");
+ ATF_CHECK_EQ(max, ins->key);
+
+ ATF_CHECK_EQ(tmp, SPLAY_REMOVE(tree, &root, tmp));
+
+ for (i = 0; i < ITER - 1; i++) {
+ tmp = SPLAY_ROOT(&root);
+ ATF_REQUIRE_MSG(tmp != NULL, "SPLAY_ROOT error");
+ ATF_CHECK_EQ(tmp, SPLAY_REMOVE(tree, &root, tmp));
+ free(tmp);
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, splay_test);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/sys/time_test.c b/tests/sys/sys/time_test.c
new file mode 100644
index 000000000000..c6c057c1b06a
--- /dev/null
+++ b/tests/sys/sys/time_test.c
@@ -0,0 +1,222 @@
+/*-
+ * Copyright (c) 2022 Axcient
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <inttypes.h>
+#include <stdio.h>
+
+#include <atf-c.h>
+
+
+static void
+atf_check_nstosbt(sbintime_t expected, int64_t ns) {
+ sbintime_t actual = nstosbt(ns);
+
+ ATF_CHECK_MSG((expected) - 1 <= (actual) && actual <= (expected) + 1,
+ "%"PRId64" != nstosbt(%"PRId64") (%"PRId64")",
+ expected, ns, actual);
+}
+
+ATF_TC_WITHOUT_HEAD(nstosbt);
+ATF_TC_BODY(nstosbt, tc)
+{
+ atf_check_nstosbt(0, 0);
+ atf_check_nstosbt(4, 1);
+ /* 1 second */
+ atf_check_nstosbt((1ll << 32) - 4, 999999999);
+ atf_check_nstosbt(1ll << 32, 1000000000);
+ /* 2 seconds https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=263073 */
+ atf_check_nstosbt((1ll << 33) - 4, 1999999999);
+ atf_check_nstosbt(1ll << 33, 2000000000);
+ /* 4 seconds */
+ atf_check_nstosbt((1ll << 34) - 4, 3999999999);
+ atf_check_nstosbt((1ll << 34), 4000000000);
+ /* Max value */
+ atf_check_nstosbt(((1ll << 31) - 1) << 32,
+ ((1ll << 31) - 1) * 1000000000);
+}
+
+static void
+atf_check_ustosbt(sbintime_t expected, int64_t us) {
+ sbintime_t actual = ustosbt(us);
+
+ ATF_CHECK_MSG((expected) - 1 <= (actual) && actual <= (expected) + 1,
+ "%"PRId64" != ustosbt(%"PRId64") (%"PRId64")",
+ expected, us, actual);
+}
+
+ATF_TC_WITHOUT_HEAD(ustosbt);
+ATF_TC_BODY(ustosbt, tc)
+{
+ atf_check_ustosbt(0, 0);
+ atf_check_ustosbt(4295, 1);
+ /* 1 second */
+ atf_check_ustosbt((1ll << 32) - 4295, 999999);
+ atf_check_ustosbt(1ll << 32, 1000000);
+ /* 2 seconds https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=263073 */
+ atf_check_ustosbt((1ll << 33) - 4295, 1999999);
+ atf_check_ustosbt(1ll << 33, 2000000);
+ /* 4 seconds */
+ atf_check_ustosbt((1ll << 34) - 4295, 3999999);
+ atf_check_ustosbt(1ll << 34, 4000000);
+ /* Max value */
+ atf_check_ustosbt(((1ull << 31) - 1) << 32,
+ ((1ll << 31) - 1) * 1000000);
+}
+
+static void
+atf_check_mstosbt(sbintime_t expected, int64_t ms) {
+ sbintime_t actual = mstosbt(ms);
+
+ ATF_CHECK_MSG((expected) - 1 <= (actual) && actual <= (expected) + 1,
+ "%"PRId64" != mstosbt(%"PRId64") (%"PRId64")",
+ expected, ms, actual);
+}
+
+ATF_TC_WITHOUT_HEAD(mstosbt);
+ATF_TC_BODY(mstosbt, tc)
+{
+ atf_check_mstosbt(0, 0);
+ atf_check_mstosbt(4294967, 1);
+ /* 1 second */
+ atf_check_mstosbt((1ll << 32) - 4294968, 999);
+ atf_check_mstosbt(1ll << 32, 1000);
+ /* 2 seconds https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=263073 */
+ atf_check_mstosbt((1ll << 33) - 4294968, 1999);
+ atf_check_mstosbt(1ll << 33, 2000);
+ /* 4 seconds */
+ atf_check_mstosbt((1ll << 34) - 4294968, 3999);
+ atf_check_mstosbt(1ll << 34, 4000);
+ /* Max value */
+ atf_check_mstosbt(((1ll << 31) - 1) << 32, ((1ll << 31) - 1) * 1000);
+}
+
+static void
+atf_check_sbttons(int64_t expected, sbintime_t sbt) {
+ int64_t actual = sbttons(sbt);
+
+ ATF_CHECK_MSG((expected) - 1 <= (actual) && actual <= (expected) + 1,
+ "%"PRId64" != sbttons(%"PRId64") (%"PRId64")",
+ expected, sbt, actual);
+}
+
+ATF_TC_WITHOUT_HEAD(sbttons);
+ATF_TC_BODY(sbttons, tc)
+{
+ atf_check_sbttons(0, 0);
+ atf_check_sbttons(0, 1);
+ atf_check_sbttons(1, (1ll << 32) / 1000000000);
+ /* 1 second */
+ atf_check_sbttons(1000000000, 1ll << 32);
+ atf_check_sbttons(1999999999, (1ll << 33) - 1);
+ /* 2 seconds */
+ atf_check_sbttons(1999999999, (1ll << 33) - 1);
+ atf_check_sbttons(2000000000, 1ll << 33);
+ /* 4 seconds */
+ atf_check_sbttons(3999999999, (1ll << 34) - 1);
+ atf_check_sbttons(4000000000, 1ll << 34);
+ /* edge cases */
+ atf_check_sbttons(999999999, (1ll << 32) - 1);
+ atf_check_sbttons((1ll << 31) * 1000000000, (1ull << 63) - 1);
+}
+
+static void
+atf_check_sbttous(int64_t expected, sbintime_t sbt) {
+ int64_t actual = sbttous(sbt);
+
+ ATF_CHECK_MSG((expected) - 1 <= (actual) && actual <= (expected) + 1,
+ "%"PRId64" != sbttous(%"PRId64") (%"PRId64")",
+ expected, sbt, actual);
+}
+
+ATF_TC_WITHOUT_HEAD(sbttous);
+ATF_TC_BODY(sbttous, tc)
+{
+ atf_check_sbttous(0, 0);
+ atf_check_sbttous(0, 1);
+ atf_check_sbttous(1, (1ll << 32) / 1000000);
+ /* 1 second */
+ atf_check_sbttous(1000000, 1ll << 32);
+ atf_check_sbttous(1999999, (1ll << 33) - 1);
+ /* 2 seconds */
+ atf_check_sbttous(1999999, (1ll << 33) - 1);
+ atf_check_sbttous(2000000, 1ll << 33);
+ /* 4 seconds */
+ atf_check_sbttous(3999999, (1ll << 34) -1);
+ atf_check_sbttous(4000000, 1ll << 34);
+ /* Overflows (bug 263073) */
+ atf_check_sbttous(1ll << 31, (1ull << 63) / 1000000);
+ atf_check_sbttous(1ll << 31, (1ull << 63) / 1000000 + 1);
+ atf_check_sbttous((1ll << 31) * 1000000, (1ull << 63) - 1);
+}
+
+static void
+atf_check_sbttoms(int64_t expected, sbintime_t sbt) {
+ int64_t actual = sbttoms(sbt);
+
+ ATF_CHECK_MSG((expected) - 1 <= (actual) && actual <= (expected) + 1,
+ "%"PRId64" != sbttoms(%"PRId64") (%"PRId64")",
+ expected, sbt, actual);
+}
+
+ATF_TC_WITHOUT_HEAD(sbttoms);
+ATF_TC_BODY(sbttoms, tc)
+{
+ atf_check_sbttoms(0, 0);
+ atf_check_sbttoms(0, 1);
+ atf_check_sbttoms(1, (1ll << 32) / 1000);
+ /* 1 second */
+ atf_check_sbttoms(999, (1ll << 32) - 1);
+ atf_check_sbttoms(1000, 1ll << 32);
+ /* 2 seconds */
+ atf_check_sbttoms(1999, (1ll << 33) - 1);
+ atf_check_sbttoms(2000, 1ll << 33);
+ /* 4 seconds */
+ atf_check_sbttoms(3999, (1ll << 34) - 1);
+ atf_check_sbttoms(4000, 1ll << 34);
+ /* Overflows (bug 263073) */
+ atf_check_sbttoms(1ll << 31, (1ull << 63) / 1000);
+ atf_check_sbttoms(1ll << 31, (1ull << 63) / 1000 + 1);
+ atf_check_sbttoms((1ll << 31) * 1000, (1ull << 63) - 1);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, nstosbt);
+ ATF_TP_ADD_TC(tp, ustosbt);
+ ATF_TP_ADD_TC(tp, mstosbt);
+ ATF_TP_ADD_TC(tp, sbttons);
+ ATF_TP_ADD_TC(tp, sbttous);
+ ATF_TP_ADD_TC(tp, sbttoms);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/vfs/Makefile b/tests/sys/vfs/Makefile
new file mode 100644
index 000000000000..e25282b2c70a
--- /dev/null
+++ b/tests/sys/vfs/Makefile
@@ -0,0 +1,12 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/vfs
+
+ATF_TESTS_C+= lookup_cap_dotdot
+CFLAGS.lookup_cap_dotdot.c+= -I${SRCTOP}/tests
+
+#ATF_TESTS_SH+= lookup_test
+
+TAP_TESTS_SH+= trailing_slash
+
+.include <bsd.test.mk>
diff --git a/tests/sys/vfs/Makefile.depend b/tests/sys/vfs/Makefile.depend
new file mode 100644
index 000000000000..1af0c88e099c
--- /dev/null
+++ b/tests/sys/vfs/Makefile.depend
@@ -0,0 +1,17 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/vfs/lookup_cap_dotdot.c b/tests/sys/vfs/lookup_cap_dotdot.c
new file mode 100644
index 000000000000..fa26a13568ca
--- /dev/null
+++ b/tests/sys/vfs/lookup_cap_dotdot.c
@@ -0,0 +1,272 @@
+/*-
+ * Copyright (c) 2016 Ed Maste <emaste@FreeBSD.org>
+ * Copyright (c) 2016 Conrad Meyer <cem@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/capsicum.h>
+#include <sys/sysctl.h>
+#include <sys/stat.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "freebsd_test_suite/macros.h"
+
+static int dirfd = -1;
+static char *abspath;
+
+static void
+touchat(int _dirfd, const char *name)
+{
+ int fd;
+
+ ATF_REQUIRE((fd = openat(_dirfd, name, O_CREAT | O_TRUNC | O_WRONLY,
+ 0777)) >= 0);
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+static void
+prepare_dotdot_tests(void)
+{
+ char cwd[MAXPATHLEN];
+
+ ATF_REQUIRE(getcwd(cwd, sizeof(cwd)) != NULL);
+ asprintf(&abspath, "%s/testdir/d1/f1", cwd);
+
+ ATF_REQUIRE(mkdir("testdir", 0777) == 0);
+ ATF_REQUIRE((dirfd = open("testdir", O_RDONLY)) >= 0);
+
+ ATF_REQUIRE(mkdirat(dirfd, "d1", 0777) == 0);
+ ATF_REQUIRE(mkdirat(dirfd, "d1/d2", 0777) == 0);
+ ATF_REQUIRE(mkdirat(dirfd, "d1/d2/d3", 0777) == 0);
+ touchat(dirfd, "d1/f1");
+ touchat(dirfd, "d1/d2/f2");
+ touchat(dirfd, "d1/d2/d3/f3");
+ ATF_REQUIRE(symlinkat("d1/d2/d3", dirfd, "l3") == 0);
+ ATF_REQUIRE(symlinkat("../testdir/d1", dirfd, "lup") == 0);
+ ATF_REQUIRE(symlinkat("../..", dirfd, "d1/d2/d3/ld1") == 0);
+ ATF_REQUIRE(symlinkat("../../f1", dirfd, "d1/d2/d3/lf1") == 0);
+}
+
+static void
+check_capsicum(void)
+{
+ ATF_REQUIRE_FEATURE("security_capabilities");
+ ATF_REQUIRE_FEATURE("security_capability_mode");
+ ATF_REQUIRE_SYSCTL_BOOL("kern.trap_enotcap", false);
+}
+
+/*
+ * Positive tests
+ */
+ATF_TC(openat__basic_positive);
+ATF_TC_HEAD(openat__basic_positive, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Basic positive openat testcases");
+}
+
+ATF_TC_BODY(openat__basic_positive, tc)
+{
+ prepare_dotdot_tests();
+
+ ATF_REQUIRE(openat(dirfd, "d1/d2/d3/f3", O_RDONLY) >= 0);
+ ATF_REQUIRE(openat(dirfd, "d1/d2/d3/../../f1", O_RDONLY) >= 0);
+ ATF_REQUIRE(openat(dirfd, "l3/f3", O_RDONLY) >= 0);
+ ATF_REQUIRE(openat(dirfd, "l3/../../f1", O_RDONLY) >= 0);
+ ATF_REQUIRE(openat(dirfd, "../testdir/d1/f1", O_RDONLY) >= 0);
+ ATF_REQUIRE(openat(dirfd, "lup/f1", O_RDONLY) >= 0);
+ ATF_REQUIRE(openat(dirfd, "l3/ld1", O_RDONLY) >= 0);
+ ATF_REQUIRE(openat(dirfd, "l3/lf1", O_RDONLY) >= 0);
+ ATF_REQUIRE(open(abspath, O_RDONLY) >= 0);
+ ATF_REQUIRE(openat(dirfd, abspath, O_RDONLY) >= 0);
+}
+
+ATF_TC(lookup_cap_dotdot__basic);
+ATF_TC_HEAD(lookup_cap_dotdot__basic, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Validate cap-mode (testdir)/d1/.. lookup");
+}
+
+ATF_TC_BODY(lookup_cap_dotdot__basic, tc)
+{
+ cap_rights_t rights;
+
+ check_capsicum();
+ prepare_dotdot_tests();
+
+ cap_rights_init(&rights, CAP_LOOKUP, CAP_READ);
+ ATF_REQUIRE(cap_rights_limit(dirfd, &rights) >= 0);
+
+ ATF_REQUIRE(cap_enter() >= 0);
+
+ ATF_REQUIRE_MSG(openat(dirfd, "d1/..", O_RDONLY) >= 0, "%s",
+ strerror(errno));
+}
+
+ATF_TC(lookup_cap_dotdot__advanced);
+ATF_TC_HEAD(lookup_cap_dotdot__advanced, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Validate cap-mode (testdir)/d1/.. lookup");
+}
+
+ATF_TC_BODY(lookup_cap_dotdot__advanced, tc)
+{
+ cap_rights_t rights;
+
+ check_capsicum();
+ prepare_dotdot_tests();
+
+ cap_rights_init(&rights, CAP_LOOKUP, CAP_READ);
+ ATF_REQUIRE(cap_rights_limit(dirfd, &rights) >= 0);
+
+ ATF_REQUIRE(cap_enter() >= 0);
+
+ ATF_REQUIRE(openat(dirfd, "d1/d2/d3/../../f1", O_RDONLY) >= 0);
+ ATF_REQUIRE(openat(dirfd, "l3/../../f1", O_RDONLY) >= 0);
+ ATF_REQUIRE(openat(dirfd, "l3/ld1", O_RDONLY) >= 0);
+ ATF_REQUIRE(openat(dirfd, "l3/lf1", O_RDONLY) >= 0);
+}
+
+/*
+ * Negative tests
+ */
+ATF_TC(openat__basic_negative);
+ATF_TC_HEAD(openat__basic_negative, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Basic negative openat testcases");
+}
+
+ATF_TC_BODY(openat__basic_negative, tc)
+{
+ prepare_dotdot_tests();
+
+ ATF_REQUIRE_ERRNO(ENOENT,
+ openat(dirfd, "does-not-exist", O_RDONLY) < 0);
+ ATF_REQUIRE_ERRNO(ENOENT,
+ openat(dirfd, "l3/does-not-exist", O_RDONLY) < 0);
+}
+
+ATF_TC(capmode__negative);
+ATF_TC_HEAD(capmode__negative, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Negative Capability mode testcases");
+}
+
+ATF_TC_BODY(capmode__negative, tc)
+{
+ int subdirfd;
+
+ check_capsicum();
+ prepare_dotdot_tests();
+
+ ATF_REQUIRE(cap_enter() == 0);
+
+ /* open() not permitted in capability mode */
+ ATF_REQUIRE_ERRNO(ECAPMODE, open("testdir", O_RDONLY) < 0);
+
+ /* AT_FDCWD not permitted in capability mode */
+ ATF_REQUIRE_ERRNO(ECAPMODE, openat(AT_FDCWD, "d1/f1", O_RDONLY) < 0);
+
+ /* Relative path above dirfd not capable */
+ ATF_REQUIRE_ERRNO(ENOTCAPABLE, openat(dirfd, "..", O_RDONLY) < 0);
+ ATF_REQUIRE((subdirfd = openat(dirfd, "l3", O_RDONLY)) >= 0);
+ ATF_REQUIRE_ERRNO(ENOTCAPABLE,
+ openat(subdirfd, "../../f1", O_RDONLY) < 0);
+
+ /* Absolute paths not capable */
+ ATF_REQUIRE_ERRNO(ENOTCAPABLE, openat(dirfd, abspath, O_RDONLY) < 0);
+
+ /* Symlink above dirfd */
+ ATF_REQUIRE_ERRNO(ENOTCAPABLE, openat(dirfd, "lup/f1", O_RDONLY) < 0);
+}
+
+ATF_TC(lookup_cap_dotdot__negative);
+ATF_TC_HEAD(lookup_cap_dotdot__negative, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Validate cap-mode (testdir)/.. lookup fails");
+}
+
+ATF_TC_BODY(lookup_cap_dotdot__negative, tc)
+{
+ cap_rights_t rights;
+
+ check_capsicum();
+ prepare_dotdot_tests();
+
+ cap_rights_init(&rights, CAP_LOOKUP, CAP_READ);
+ ATF_REQUIRE(cap_rights_limit(dirfd, &rights) >= 0);
+
+ ATF_REQUIRE(cap_enter() >= 0);
+
+ ATF_REQUIRE_ERRNO(ENOTCAPABLE, openat(dirfd, "..", O_RDONLY) < 0);
+ ATF_REQUIRE_ERRNO(ENOTCAPABLE, openat(dirfd, "d1/../..", O_RDONLY) < 0);
+ ATF_REQUIRE_ERRNO(ENOTCAPABLE, openat(dirfd, "../testdir/d1/f1", O_RDONLY) < 0);
+}
+
+ATF_TC(lookup_cap_dotdot__root);
+ATF_TC_HEAD(lookup_cap_dotdot__root, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Validate cap-mode /.. lookup fails");
+}
+
+ATF_TC_BODY(lookup_cap_dotdot__root, tc)
+{
+ int dfd, dfd2;
+
+ check_capsicum();
+
+ dfd = open("/", O_DIRECTORY);
+ ATF_REQUIRE(dfd >= 0);
+
+ dfd2 = openat(dfd, "..", O_DIRECTORY);
+ ATF_REQUIRE(dfd2 >= 0);
+ ATF_REQUIRE(close(dfd2) == 0);
+
+ ATF_REQUIRE(cap_enter() >= 0);
+
+ dfd2 = openat(dfd, "..", O_DIRECTORY);
+ ATF_REQUIRE_ERRNO(ENOTCAPABLE, openat(dfd, "..", O_DIRECTORY));
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, openat__basic_positive);
+ ATF_TP_ADD_TC(tp, openat__basic_negative);
+
+ ATF_TP_ADD_TC(tp, capmode__negative);
+
+ ATF_TP_ADD_TC(tp, lookup_cap_dotdot__basic);
+ ATF_TP_ADD_TC(tp, lookup_cap_dotdot__advanced);
+ ATF_TP_ADD_TC(tp, lookup_cap_dotdot__negative);
+ ATF_TP_ADD_TC(tp, lookup_cap_dotdot__root);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/vfs/lookup_test.sh b/tests/sys/vfs/lookup_test.sh
new file mode 100644
index 000000000000..cb52cdf8865e
--- /dev/null
+++ b/tests/sys/vfs/lookup_test.sh
@@ -0,0 +1,16 @@
+
+long_symlink_head()
+{
+ atf_set "descr" "Test for 1023 (PATH_MAX-1) symlink support"
+}
+long_symlink_body()
+{
+ atf_check -s exit:0 ln -s aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa sym_long
+ # was: "stat: sym_long: stat: File name too long"; exit 1
+ atf_check -s exit:0 -o ignore stat -L sym_long
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case long_symlink
+}
diff --git a/tests/sys/vfs/trailing_slash.sh b/tests/sys/vfs/trailing_slash.sh
new file mode 100644
index 000000000000..3c298eed2b96
--- /dev/null
+++ b/tests/sys/vfs/trailing_slash.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+#
+#
+# Tests vfs_lookup()'s handling of trailing slashes for symlinks that
+# point to files. See kern/21768 for details. Fixed in r193028.
+#
+
+: ${TMPDIR=/tmp}
+testfile="$TMPDIR/testfile-$$"
+testlink="$TMPDIR/testlink-$$"
+
+tests="
+$testfile:$testlink:$testfile:0
+$testfile:$testlink:$testfile/:1
+$testfile:$testlink:$testlink:0
+$testfile:$testlink:$testlink/:1
+$testfile/:$testlink:$testlink:1
+$testfile/:$testlink:$testlink/:1
+"
+
+touch $testfile || exit 1
+trap "rm $testfile $testlink" EXIT
+
+set $tests
+echo "1..$#"
+n=1
+for testspec ; do
+ (
+ IFS=:
+ set $testspec
+ unset IFS
+ ln -fs "$1" "$2" || exit 1
+ cat "$3" >/dev/null 2>&1
+ ret=$?
+ if [ "$ret" -eq "$4" ] ; then
+ echo "ok $n"
+ else
+ echo "fail $n - expected $4, got $ret"
+ fi
+ )
+ n=$((n+1))
+done
diff --git a/tests/sys/vm/Makefile b/tests/sys/vm/Makefile
new file mode 100644
index 000000000000..8be4b8666fdf
--- /dev/null
+++ b/tests/sys/vm/Makefile
@@ -0,0 +1,19 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/vm
+
+ATF_TESTS_C+= mlock_test \
+ mmap_test \
+ page_fault_signal \
+ shared_shadow_inval_test
+
+BINDIR= ${TESTSDIR}
+ATF_TESTS_SH+= mmap_map_32bit_test
+PROGS+= mmap_map_32bit_helper
+
+SUBDIR= soxstack
+TESTS_SUBDIRS+= stack
+
+SUBDIR_DEPEND_stack=soxstack
+
+.include <bsd.test.mk>
diff --git a/tests/sys/vm/Makefile.depend b/tests/sys/vm/Makefile.depend
new file mode 100644
index 000000000000..1af0c88e099c
--- /dev/null
+++ b/tests/sys/vm/Makefile.depend
@@ -0,0 +1,17 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/tests/sys/vm/mlock_test.c b/tests/sys/vm/mlock_test.c
new file mode 100644
index 000000000000..8c8ce4a2e8de
--- /dev/null
+++ b/tests/sys/vm/mlock_test.c
@@ -0,0 +1,286 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Mark Johnston <markj@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdatomic.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+static void
+test_wired_copy_on_write(void *addr, size_t len)
+{
+ int status, val;
+ pid_t pid;
+
+ pid = fork();
+ if (pid == -1)
+ atf_tc_fail("fork() failed: %s", strerror(errno));
+ if (pid == 0) {
+ if (mlock(addr, len) != 0)
+ _exit(1);
+ if (ptrace(PT_TRACE_ME, 0, NULL, 0) != 0)
+ _exit(2);
+ if (raise(SIGSTOP) != 0)
+ _exit(3);
+ if (munlock(addr, len) != 0)
+ _exit(4);
+ _exit(0);
+ }
+
+ ATF_REQUIRE(waitpid(pid, &status, 0) == pid);
+ ATF_REQUIRE_MSG(!WIFEXITED(status),
+ "child exited with status %d", WEXITSTATUS(status));
+ ATF_REQUIRE(WIFSTOPPED(status));
+ ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP);
+
+ errno = 0;
+ val = ptrace(PT_READ_D, pid, addr, 0);
+ ATF_REQUIRE(errno == 0);
+ ATF_REQUIRE(ptrace(PT_WRITE_D, pid, addr, val) == 0);
+ ATF_REQUIRE(ptrace(PT_CONTINUE, pid, (caddr_t)1, 0) == 0);
+ ATF_REQUIRE(waitpid(pid, &status, 0) == pid);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE_MSG(WEXITSTATUS(status) == 0,
+ "child exited with status %d", WSTOPSIG(status));
+}
+
+/*
+ * Use ptrace(2) to trigger a copy-on-write fault of anonymous memory.
+ */
+ATF_TC_WITHOUT_HEAD(mlock__copy_on_write_anon);
+ATF_TC_BODY(mlock__copy_on_write_anon, tc)
+{
+ char *addr;
+ int len;
+
+ len = getpagesize();
+ addr = mmap(NULL, len, PROT_READ, MAP_ANON, -1, 0);
+ ATF_REQUIRE(addr != MAP_FAILED);
+
+ test_wired_copy_on_write(addr, len);
+}
+
+/*
+ * Use ptrace(2) to trigger a copy-on-write fault of a read-only text page.
+ */
+ATF_TC_WITHOUT_HEAD(mlock__copy_on_write_vnode);
+ATF_TC_BODY(mlock__copy_on_write_vnode, tc)
+{
+ void *addr;
+ int len;
+
+ len = getpagesize();
+ addr = (void *)((uintptr_t)test_wired_copy_on_write & ~(len - 1));
+
+ test_wired_copy_on_write(addr, len);
+}
+
+/*
+ * Try truncating and then resizing an mlock()ed mapping.
+ */
+ATF_TC_WITHOUT_HEAD(mlock__truncate_and_resize);
+ATF_TC_BODY(mlock__truncate_and_resize, tc)
+{
+ char filename[16];
+ char *addr;
+ int fd, i, len;
+
+ snprintf(filename, sizeof(filename), "tmp.XXXXXX");
+ fd = mkstemp(filename);
+ ATF_REQUIRE(fd >= 0);
+ ATF_REQUIRE(unlink(filename) == 0);
+
+ len = getpagesize();
+ ATF_REQUIRE(ftruncate(fd, len) == 0);
+
+ addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ ATF_REQUIRE(addr != MAP_FAILED);
+ ATF_REQUIRE(mlock(addr, len) == 0);
+ memset(addr, 1, len);
+ ATF_REQUIRE(ftruncate(fd, 0) == 0);
+ ATF_REQUIRE(ftruncate(fd, len) == 0);
+ for (i = 0; i < len; i++)
+ ATF_REQUIRE(addr[i] == 0);
+ ATF_REQUIRE(munlock(addr, len) == 0);
+}
+
+/*
+ * Make sure that we can munlock() a truncated mapping.
+ */
+ATF_TC_WITHOUT_HEAD(mlock__truncate_and_unlock);
+ATF_TC_BODY(mlock__truncate_and_unlock, tc)
+{
+ char filename[16];
+ void *addr;
+ int fd, len;
+
+ snprintf(filename, sizeof(filename), "tmp.XXXXXX");
+ fd = mkstemp(filename);
+ ATF_REQUIRE(fd >= 0);
+ ATF_REQUIRE(unlink(filename) == 0);
+
+ len = getpagesize();
+ ATF_REQUIRE(ftruncate(fd, len) == 0);
+
+ addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ ATF_REQUIRE(addr != MAP_FAILED);
+ ATF_REQUIRE(mlock(addr, len) == 0);
+ ATF_REQUIRE(ftruncate(fd, 0) == 0);
+ ATF_REQUIRE(munlock(addr, len) == 0);
+}
+
+/*
+ * Exercise a corner case involving an interaction between mlock() and superpage
+ * creation: a truncation of the object backing a mapping results in the
+ * truncated region being unmapped by the pmap, but does not affect the logical
+ * mapping. In particular, the truncated region remains mlock()ed. If the
+ * mapping is later extended, a page fault in the formerly truncated region can
+ * result in superpage creation via a call to pmap_enter(psind = 1).
+ */
+ATF_TC(mlock__superpage_fault);
+ATF_TC_HEAD(mlock__superpage_fault, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(mlock__superpage_fault, tc)
+{
+ struct rlimit rlim;
+ void *addr1, *addr2;
+ size_t len, pagesizes[MAXPAGESIZES];
+ int count, error, shmfd;
+ char vec;
+
+ count = getpagesizes(pagesizes, MAXPAGESIZES);
+ ATF_REQUIRE_MSG(count >= 1,
+ "failed to get page sizes: %s", strerror(errno));
+ if (count == 1)
+ atf_tc_skip("system does not support multiple page sizes");
+ len = pagesizes[1];
+
+ error = getrlimit(RLIMIT_MEMLOCK, &rlim);
+ ATF_REQUIRE_MSG(error == 0, "getrlimit: %s", strerror(errno));
+ rlim.rlim_cur += len;
+ rlim.rlim_max += len;
+ error = setrlimit(RLIMIT_MEMLOCK, &rlim);
+ ATF_REQUIRE_MSG(error == 0, "setrlimit: %s", strerror(errno));
+
+ shmfd = shm_open(SHM_ANON, O_RDWR | O_CREAT, 0600);
+ ATF_REQUIRE_MSG(shmfd >= 0, "shm_open: %s", strerror(errno));
+ error = ftruncate(shmfd, len);
+ ATF_REQUIRE_MSG(error == 0, "ftruncate: %s", strerror(errno));
+
+ addr1 = mmap(NULL, len, PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ALIGNED_SUPER, shmfd, 0);
+ ATF_REQUIRE_MSG(addr1 != MAP_FAILED, "mmap: %s", strerror(errno));
+ ATF_REQUIRE_MSG(((uintptr_t)addr1 & (len - 1)) == 0,
+ "addr %p is misaligned", addr1);
+ addr2 = mmap(NULL, len, PROT_READ,
+ MAP_SHARED | MAP_ALIGNED_SUPER, shmfd, 0);
+ ATF_REQUIRE_MSG(addr2 != MAP_FAILED, "mmap: %s", strerror(errno));
+ ATF_REQUIRE_MSG(((uintptr_t)addr2 & (len - 1)) == 0,
+ "addr %p is misaligned", addr2);
+
+ memset(addr1, 0x42, len);
+ error = mincore(addr1, pagesizes[0], &vec);
+ ATF_REQUIRE_MSG(error == 0, "mincore: %s", strerror(errno));
+ if ((vec & MINCORE_SUPER) == 0)
+ atf_tc_skip("initial superpage promotion failed");
+
+ error = mlock(addr2, len);
+ ATF_REQUIRE_MSG(error == 0, "mlock: %s", strerror(errno));
+ error = mincore(addr2, pagesizes[0], &vec);
+ ATF_REQUIRE_MSG(error == 0, "mincore: %s", strerror(errno));
+ ATF_REQUIRE((vec & MINCORE_SUPER) != 0);
+
+ /*
+ * Free a page back to the superpage reservation, demoting both
+ * mappings.
+ */
+ error = ftruncate(shmfd, len - pagesizes[0]);
+ ATF_REQUIRE_MSG(error == 0, "ftruncate: %s", strerror(errno));
+
+ /*
+ * Extend the mapping back to its original size.
+ */
+ error = ftruncate(shmfd, len);
+ ATF_REQUIRE_MSG(error == 0, "ftruncate: %s", strerror(errno));
+
+ /*
+ * Trigger re-promotion.
+ */
+ error = mincore(addr1, pagesizes[0], &vec);
+ ATF_REQUIRE_MSG(error == 0, "mincore: %s", strerror(errno));
+ ATF_REQUIRE((vec & MINCORE_SUPER) == 0);
+ memset((char *)addr1 + len - pagesizes[0], 0x43, pagesizes[0]);
+ error = mincore(addr1, pagesizes[0], &vec);
+ ATF_REQUIRE_MSG(error == 0, "mincore: %s", strerror(errno));
+ ATF_REQUIRE((vec & MINCORE_SUPER) != 0);
+
+ /*
+ * Trigger a read fault, which should install a superpage mapping
+ * without promotion.
+ */
+ error = mincore(addr2, pagesizes[0], &vec);
+ ATF_REQUIRE_MSG(error == 0, "mincore: %s", strerror(errno));
+ ATF_REQUIRE((vec & MINCORE_SUPER) == 0);
+ (void)atomic_load(
+ (_Atomic int *)(void *)((char *)addr2 + len - pagesizes[0]));
+ error = mincore(addr2, pagesizes[0], &vec);
+ ATF_REQUIRE_MSG(error == 0, "mincore: %s", strerror(errno));
+ ATF_REQUIRE((vec & MINCORE_SUPER) != 0);
+
+ /*
+ * Trigger demotion of the wired mapping.
+ */
+ error = munlock(addr2, pagesizes[0]);
+ ATF_REQUIRE_MSG(error == 0, "munlock: %s", strerror(errno));
+
+ ATF_REQUIRE(close(shmfd) == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, mlock__copy_on_write_anon);
+ ATF_TP_ADD_TC(tp, mlock__copy_on_write_vnode);
+ ATF_TP_ADD_TC(tp, mlock__truncate_and_resize);
+ ATF_TP_ADD_TC(tp, mlock__truncate_and_unlock);
+ ATF_TP_ADD_TC(tp, mlock__superpage_fault);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/vm/mmap_map_32bit_helper.c b/tests/sys/vm/mmap_map_32bit_helper.c
new file mode 100644
index 000000000000..763bba35d2b9
--- /dev/null
+++ b/tests/sys/vm/mmap_map_32bit_helper.c
@@ -0,0 +1,50 @@
+/*-
+ * Copyright (c) 2023 Dmitry Chagin <dchagin@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/mman.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define MAP_32BIT_MAX_ADDR ((vm_offset_t)1 << 31)
+
+int
+main(void)
+{
+ size_t pagesize;
+ void *s32;
+ int fd;
+
+ if ((pagesize = getpagesize()) <= 0)
+ err(1, "getpagesize");
+
+ fd = open("/dev/zero", O_RDONLY);
+ if (fd <= 0)
+ err(1, "open failed");
+
+ s32 = mmap(NULL, pagesize, PROT_READ, MAP_32BIT | MAP_PRIVATE, fd, 0);
+ if (s32 == MAP_FAILED)
+ err(1, "mmap MAP_32BIT | MAP_PRIVATE failed");
+ if (((vm_offset_t)s32 + pagesize) > MAP_32BIT_MAX_ADDR)
+ errx(1, "mmap invalid result %p", s32);
+
+ close(fd);
+ if (munmap(s32, pagesize) != 0)
+ err(1, "munmap failed");
+
+ s32 = mmap(NULL, pagesize, PROT_READ | PROT_WRITE,
+ MAP_32BIT | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ if (s32 == MAP_FAILED)
+ err(1, "mmap MAP_32BIT | MAP_ANONYMOUS | MAP_PRIVATE failed");
+ if (((vm_offset_t)s32 + pagesize) > MAP_32BIT_MAX_ADDR)
+ errx(1, "mmap invalid result %p", s32);
+
+ if (munmap(s32, pagesize) != 0)
+ err(1, "munmap failed");
+ exit(0);
+}
diff --git a/tests/sys/vm/mmap_map_32bit_test.sh b/tests/sys/vm/mmap_map_32bit_test.sh
new file mode 100644
index 000000000000..2df53e78f1b7
--- /dev/null
+++ b/tests/sys/vm/mmap_map_32bit_test.sh
@@ -0,0 +1,37 @@
+#
+# Copyright (c) 2022 Dmitry Chagin <dchagin@FreeBSD.org>
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Simple test of MAP_32BIT flag w/wo ASLR
+
+map_32bit_w_aslr_head()
+{
+ atf_set descr "MAP_32BIT with ASLR"
+ atf_set require.progs proccontrol
+}
+
+map_32bit_w_aslr_body()
+{
+ atf_check -s exit:0 -x proccontrol -m aslr -s enable \
+ $(atf_get_srcdir)/mmap_map_32bit_helper
+}
+
+map_32bit_wo_aslr_head()
+{
+ atf_set descr "MAP_32BIT without ASLR"
+ atf_set require.progs proccontrol
+}
+
+map_32bit_wo_aslr_body()
+{
+ atf_check -s exit:0 -x proccontrol -m aslr -s disable \
+ $(atf_get_srcdir)/mmap_map_32bit_helper
+}
+
+
+atf_init_test_cases()
+{
+ atf_add_test_case map_32bit_w_aslr
+ atf_add_test_case map_32bit_wo_aslr
+}
diff --git a/tests/sys/vm/mmap_test.c b/tests/sys/vm/mmap_test.c
new file mode 100644
index 000000000000..6bc30f73ca95
--- /dev/null
+++ b/tests/sys/vm/mmap_test.c
@@ -0,0 +1,376 @@
+/*-
+ * Copyright (c) 2009 Simon L. Nielsen <simon@FreeBSD.org>,
+ * Bjoern A. Zeeb <bz@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/sysctl.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static const struct {
+ void *addr;
+ int ok[2]; /* Depending on security.bsd.map_at_zero {0, !=0}. */
+} map_at_zero_tests[] = {
+ { (void *)0, { 0, 1 } }, /* Test sysctl. */
+ { (void *)1, { 0, 0 } },
+ { (void *)(PAGE_SIZE - 1), { 0, 0 } },
+ { (void *)PAGE_SIZE, { 1, 1 } },
+ { (void *)-1, { 0, 0 } },
+ { (void *)(-PAGE_SIZE), { 0, 0 } },
+ { (void *)(-1 - PAGE_SIZE), { 0, 0 } },
+ { (void *)(-1 - PAGE_SIZE - 1), { 0, 0 } },
+ { (void *)(0x1000 * PAGE_SIZE), { 1, 1 } },
+};
+
+#define MAP_AT_ZERO "security.bsd.map_at_zero"
+
+#ifdef __LP64__
+#define ALLOW_WX "kern.elf64.allow_wx"
+#else
+#define ALLOW_WX "kern.elf32.allow_wx"
+#endif
+
+ATF_TC_WITHOUT_HEAD(mmap__map_at_zero);
+ATF_TC_BODY(mmap__map_at_zero, tc)
+{
+ void *p;
+ size_t len;
+ unsigned int i;
+ int map_at_zero;
+ bool allow_wx;
+ int prot_flags;
+
+ len = sizeof(map_at_zero);
+ if (sysctlbyname(MAP_AT_ZERO, &map_at_zero, &len, NULL, 0) == -1) {
+ atf_tc_skip("sysctl for %s failed: %s\n", MAP_AT_ZERO,
+ strerror(errno));
+ return;
+ }
+
+ len = sizeof(allow_wx);
+ if (sysctlbyname(ALLOW_WX, &allow_wx, &len, NULL, 0) == -1) {
+ if (errno == ENOENT) {
+ /* Allow W+X if sysctl isn't present */
+ allow_wx = true;
+ } else {
+ atf_tc_skip("sysctl for %s failed: %s\n", ALLOW_WX,
+ strerror(errno));
+ return;
+ }
+ }
+
+ /* Normalize to 0 or 1 for array access. */
+ map_at_zero = !!map_at_zero;
+
+ for (i = 0; i < nitems(map_at_zero_tests); i++) {
+ prot_flags = PROT_READ | PROT_WRITE;
+ if (allow_wx)
+ prot_flags |= PROT_EXEC;
+ p = mmap((void *)map_at_zero_tests[i].addr, PAGE_SIZE,
+ prot_flags, MAP_ANON | MAP_FIXED, -1, 0);
+ if (p == MAP_FAILED) {
+ ATF_CHECK_MSG(map_at_zero_tests[i].ok[map_at_zero] == 0,
+ "mmap(%p, ...) failed", map_at_zero_tests[i].addr);
+ } else {
+ ATF_CHECK_MSG(map_at_zero_tests[i].ok[map_at_zero] == 1,
+ "mmap(%p, ...) succeeded: p=%p\n",
+ map_at_zero_tests[i].addr, p);
+ }
+ }
+}
+
+static void
+checked_mmap(int prot, int flags, int fd, int error, const char *msg)
+{
+ void *p;
+ int pagesize;
+
+ ATF_REQUIRE((pagesize = getpagesize()) > 0);
+ p = mmap(NULL, pagesize, prot, flags, fd, 0);
+ if (p == MAP_FAILED) {
+ if (error == 0)
+ ATF_CHECK_MSG(0, "%s failed with errno %d", msg,
+ errno);
+ else
+ ATF_CHECK_EQ_MSG(error, errno,
+ "%s failed with wrong errno %d (expected %d)", msg,
+ errno, error);
+ } else {
+ ATF_CHECK_MSG(error == 0, "%s succeeded", msg);
+ munmap(p, pagesize);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(mmap__bad_arguments);
+ATF_TC_BODY(mmap__bad_arguments, tc)
+{
+ int devstatfd, pagesize, shmfd, zerofd;
+
+ ATF_REQUIRE((pagesize = getpagesize()) > 0);
+ ATF_REQUIRE((devstatfd = open("/dev/devstat", O_RDONLY)) >= 0);
+ ATF_REQUIRE((shmfd = shm_open(SHM_ANON, O_RDWR, 0644)) >= 0);
+ ATF_REQUIRE(ftruncate(shmfd, pagesize) == 0);
+ ATF_REQUIRE((zerofd = open("/dev/zero", O_RDONLY)) >= 0);
+
+ /* These should work. */
+ checked_mmap(PROT_READ | PROT_WRITE, MAP_ANON, -1, 0,
+ "simple MAP_ANON");
+ checked_mmap(PROT_READ | PROT_WRITE, MAP_SHARED, shmfd, 0,
+ "simple shm fd shared");
+ checked_mmap(PROT_READ | PROT_WRITE, MAP_PRIVATE, shmfd, 0,
+ "simple shm fd private");
+ checked_mmap(PROT_READ, MAP_SHARED, zerofd, 0,
+ "simple /dev/zero shared");
+ checked_mmap(PROT_READ | PROT_WRITE, MAP_PRIVATE, zerofd, 0,
+ "simple /dev/zero private");
+ checked_mmap(PROT_READ, MAP_SHARED, devstatfd, 0,
+ "simple /dev/devstat shared");
+
+ /* Extra PROT flags. */
+ checked_mmap(PROT_READ | PROT_WRITE | 0x100000, MAP_ANON, -1, EINVAL,
+ "MAP_ANON with extra PROT flags");
+ checked_mmap(0xffff, MAP_SHARED, shmfd, EINVAL,
+ "shm fd with garbage PROT");
+
+ /* Undefined flag. */
+ checked_mmap(PROT_READ | PROT_WRITE, MAP_ANON | MAP_RESERVED0080, -1,
+ EINVAL, "Undefined flag");
+
+ /* Both MAP_SHARED and MAP_PRIVATE */
+ checked_mmap(PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE |
+ MAP_SHARED, -1, EINVAL, "MAP_ANON with both SHARED and PRIVATE");
+ checked_mmap(PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_SHARED, shmfd,
+ EINVAL, "shm fd with both SHARED and PRIVATE");
+
+ /* At least one of MAP_SHARED or MAP_PRIVATE without ANON */
+ checked_mmap(PROT_READ | PROT_WRITE, 0, shmfd, EINVAL,
+ "shm fd without sharing flag");
+
+ /* MAP_ANON with either sharing flag (impacts fork). */
+ checked_mmap(PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0,
+ "shared MAP_ANON");
+ checked_mmap(PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0,
+ "private MAP_ANON");
+
+ /* MAP_ANON should require an fd of -1. */
+ checked_mmap(PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, 0, EINVAL,
+ "MAP_ANON with fd != -1");
+
+ /* Writable MAP_SHARED should fail on read-only descriptors. */
+ checked_mmap(PROT_READ | PROT_WRITE, MAP_SHARED, zerofd, EACCES,
+ "MAP_SHARED of read-only /dev/zero");
+
+ /*
+ * Character devices other than /dev/zero do not support private
+ * mappings.
+ */
+ checked_mmap(PROT_READ, MAP_PRIVATE, devstatfd, EINVAL,
+ "MAP_PRIVATE of /dev/devstat");
+
+ close(devstatfd);
+ close(shmfd);
+ close(zerofd);
+}
+
+ATF_TC_WITHOUT_HEAD(mmap__dev_zero_private);
+ATF_TC_BODY(mmap__dev_zero_private, tc)
+{
+ char *p1, *p2, *p3;
+ int fd, i, pagesize;
+
+ ATF_REQUIRE((pagesize = getpagesize()) > 0);
+ ATF_REQUIRE((fd = open("/dev/zero", O_RDONLY)) >= 0);
+
+ p1 = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+ ATF_REQUIRE(p1 != MAP_FAILED);
+
+ p2 = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+ ATF_REQUIRE(p2 != MAP_FAILED);
+
+ for (i = 0; i < pagesize; i++)
+ ATF_REQUIRE_EQ_MSG(0, p1[i], "byte at p1[%d] is %x", i, p1[i]);
+
+ ATF_REQUIRE(memcmp(p1, p2, pagesize) == 0);
+
+ p1[0] = 1;
+
+ ATF_REQUIRE(p2[0] == 0);
+
+ p2[0] = 2;
+
+ ATF_REQUIRE(p1[0] == 1);
+
+ p3 = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+ ATF_REQUIRE(p3 != MAP_FAILED);
+
+ ATF_REQUIRE(p3[0] == 0);
+
+ munmap(p1, pagesize);
+ munmap(p2, pagesize);
+ munmap(p3, pagesize);
+ close(fd);
+}
+
+ATF_TC_WITHOUT_HEAD(mmap__dev_zero_shared);
+ATF_TC_BODY(mmap__dev_zero_shared, tc)
+{
+ char *p1, *p2, *p3;
+ int fd, i, pagesize;
+
+ ATF_REQUIRE((pagesize = getpagesize()) > 0);
+ ATF_REQUIRE((fd = open("/dev/zero", O_RDWR)) >= 0);
+
+ p1 = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ ATF_REQUIRE(p1 != MAP_FAILED);
+
+ p2 = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ ATF_REQUIRE(p2 != MAP_FAILED);
+
+ for (i = 0; i < pagesize; i++)
+ ATF_REQUIRE_EQ_MSG(0, p1[i], "byte at p1[%d] is %x", i, p1[i]);
+
+ ATF_REQUIRE(memcmp(p1, p2, pagesize) == 0);
+
+ p1[0] = 1;
+
+ ATF_REQUIRE(p2[0] == 0);
+
+ p2[0] = 2;
+
+ ATF_REQUIRE(p1[0] == 1);
+
+ p3 = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
+ 0);
+ ATF_REQUIRE(p3 != MAP_FAILED);
+
+ ATF_REQUIRE(p3[0] == 0);
+
+ munmap(p1, pagesize);
+ munmap(p2, pagesize);
+ munmap(p3, pagesize);
+ close(fd);
+}
+
+ATF_TC_WITHOUT_HEAD(mmap__write_only);
+ATF_TC_BODY(mmap__write_only, tc)
+{
+ void *p;
+ int pagesize;
+
+ ATF_REQUIRE((pagesize = getpagesize()) > 0);
+ p = mmap(NULL, pagesize, PROT_WRITE, MAP_ANON, -1, 0);
+ ATF_REQUIRE(p != MAP_FAILED);
+
+ *(volatile uint32_t *)p = 0x12345678;
+
+ munmap(p, pagesize);
+}
+
+ATF_TC_WITHOUT_HEAD(mmap__maxprot_basic);
+ATF_TC_BODY(mmap__maxprot_basic, tc)
+{
+ void *p;
+ int error, pagesize;
+
+ ATF_REQUIRE((pagesize = getpagesize()) > 0);
+
+ p = mmap(NULL, pagesize, PROT_READ | PROT_MAX(PROT_READ),
+ MAP_ANON, -1, 0);
+ ATF_REQUIRE(p != MAP_FAILED);
+
+ error = mprotect(p, pagesize, PROT_WRITE);
+ ATF_REQUIRE_ERRNO(EACCES, error == -1);
+ error = mprotect(p, pagesize, PROT_READ | PROT_WRITE);
+ ATF_REQUIRE_ERRNO(EACCES, error == -1);
+ error = mprotect(p, pagesize, PROT_READ | PROT_EXEC);
+ ATF_REQUIRE_ERRNO(EACCES, error == -1);
+
+ ATF_REQUIRE(munmap(p, pagesize) == 0);
+}
+
+/* Make sure that PROT_MAX applies as expected to mappings of shm objects */
+ATF_TC_WITHOUT_HEAD(mmap__maxprot_shm);
+ATF_TC_BODY(mmap__maxprot_shm, tc)
+{
+ void *p;
+ int error, fd, pagesize;
+
+ ATF_REQUIRE((pagesize = getpagesize()) > 0);
+
+ fd = shm_open(SHM_ANON, O_RDWR, 0644);
+ ATF_REQUIRE(fd >= 0);
+
+ error = ftruncate(fd, pagesize);
+ ATF_REQUIRE(error == 0);
+
+ p = mmap(NULL, pagesize, PROT_READ | PROT_MAX(PROT_READ),
+ MAP_PRIVATE, fd, 0);
+ ATF_REQUIRE(p != MAP_FAILED);
+
+ error = mprotect(p, pagesize, PROT_WRITE);
+ ATF_REQUIRE_ERRNO(EACCES, error == -1);
+ error = mprotect(p, pagesize, PROT_READ | PROT_WRITE);
+ ATF_REQUIRE_ERRNO(EACCES, error == -1);
+ error = mprotect(p, pagesize, PROT_READ | PROT_EXEC);
+ ATF_REQUIRE_ERRNO(EACCES, error == -1);
+
+ ATF_REQUIRE(munmap(p, pagesize) == 0);
+
+ /* Again, this time with a shared mapping. */
+ p = mmap(NULL, pagesize, PROT_READ | PROT_MAX(PROT_READ),
+ MAP_SHARED, fd, 0);
+ ATF_REQUIRE(p != MAP_FAILED);
+
+ error = mprotect(p, pagesize, PROT_WRITE);
+ ATF_REQUIRE_ERRNO(EACCES, error == -1);
+ error = mprotect(p, pagesize, PROT_READ | PROT_WRITE);
+ ATF_REQUIRE_ERRNO(EACCES, error == -1);
+ error = mprotect(p, pagesize, PROT_READ | PROT_EXEC);
+ ATF_REQUIRE_ERRNO(EACCES, error == -1);
+
+ ATF_REQUIRE(munmap(p, pagesize) == 0);
+
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, mmap__map_at_zero);
+ ATF_TP_ADD_TC(tp, mmap__bad_arguments);
+ ATF_TP_ADD_TC(tp, mmap__dev_zero_private);
+ ATF_TP_ADD_TC(tp, mmap__dev_zero_shared);
+ ATF_TP_ADD_TC(tp, mmap__write_only);
+ ATF_TP_ADD_TC(tp, mmap__maxprot_basic);
+ ATF_TP_ADD_TC(tp, mmap__maxprot_shm);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/vm/page_fault_signal.c b/tests/sys/vm/page_fault_signal.c
new file mode 100644
index 000000000000..a698f0b064f0
--- /dev/null
+++ b/tests/sys/vm/page_fault_signal.c
@@ -0,0 +1,179 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Jilles Tjoelker
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/mman.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+
+static sigjmp_buf sig_env;
+static volatile int last_sig, last_code;
+
+static void
+sighandler(int sig, siginfo_t *info, void *context __unused)
+{
+
+ last_sig = sig;
+ last_code = info->si_code;
+ siglongjmp(sig_env, 1);
+}
+
+static void
+setup_signals(void)
+{
+ struct sigaction sa;
+ int r;
+
+ sa.sa_sigaction = sighandler;
+ sa.sa_flags = SA_RESTART | SA_RESETHAND | SA_SIGINFO;
+ r = sigfillset(&sa.sa_mask);
+ ATF_REQUIRE(r != -1);
+ r = sigaction(SIGILL, &sa, NULL);
+ ATF_REQUIRE(r != -1);
+ r = sigaction(SIGBUS, &sa, NULL);
+ ATF_REQUIRE(r != -1);
+ r = sigaction(SIGSEGV, &sa, NULL);
+ ATF_REQUIRE(r != -1);
+}
+
+ATF_TC_WITHOUT_HEAD(page_fault_signal__segv_maperr_1);
+ATF_TC_BODY(page_fault_signal__segv_maperr_1, tc)
+{
+ int *p;
+ int r;
+ int sz;
+
+ sz = getpagesize();
+ p = mmap(NULL, sz, PROT_READ, MAP_ANON, -1, 0);
+ ATF_REQUIRE(p != MAP_FAILED);
+ r = munmap(p, sz);
+ ATF_REQUIRE(r != -1);
+ if (sigsetjmp(sig_env, 1) == 0) {
+ setup_signals();
+ *(volatile int *)p = 1;
+ }
+ ATF_CHECK_EQ(SIGSEGV, last_sig);
+ ATF_CHECK_EQ(SEGV_MAPERR, last_code);
+}
+
+ATF_TC_WITHOUT_HEAD(page_fault_signal__segv_accerr_1);
+ATF_TC_BODY(page_fault_signal__segv_accerr_1, tc)
+{
+ int *p;
+ int sz;
+
+ sz = getpagesize();
+ p = mmap(NULL, sz, PROT_READ, MAP_ANON, -1, 0);
+ ATF_REQUIRE(p != MAP_FAILED);
+ if (sigsetjmp(sig_env, 1) == 0) {
+ setup_signals();
+ *(volatile int *)p = 1;
+ }
+ (void)munmap(p, sz);
+ ATF_CHECK_EQ(SIGSEGV, last_sig);
+ ATF_CHECK_EQ(SEGV_ACCERR, last_code);
+}
+
+ATF_TC_WITHOUT_HEAD(page_fault_signal__segv_accerr_2);
+ATF_TC_BODY(page_fault_signal__segv_accerr_2, tc)
+{
+ int *p;
+ int sz;
+
+ sz = getpagesize();
+ p = mmap(NULL, sz, PROT_NONE, MAP_ANON, -1, 0);
+ ATF_REQUIRE(p != MAP_FAILED);
+ if (sigsetjmp(sig_env, 1) == 0) {
+ setup_signals();
+ (void)*(volatile int *)p;
+ }
+ (void)munmap(p, sz);
+ ATF_CHECK_EQ(SIGSEGV, last_sig);
+ ATF_CHECK_EQ(SEGV_ACCERR, last_code);
+}
+
+ATF_TC_WITHOUT_HEAD(page_fault_signal__bus_objerr_1);
+ATF_TC_BODY(page_fault_signal__bus_objerr_1, tc)
+{
+ int *p;
+ int fd;
+ int sz;
+
+ sz = getpagesize();
+ fd = shm_open(SHM_ANON, O_RDWR | O_CREAT, 0600);
+ ATF_REQUIRE(fd != -1);
+ p = mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ ATF_REQUIRE(p != MAP_FAILED);
+ if (sigsetjmp(sig_env, 1) == 0) {
+ setup_signals();
+ *(volatile int *)p = 1;
+ }
+ (void)munmap(p, sz);
+ (void)close(fd);
+ ATF_CHECK_EQ(SIGBUS, last_sig);
+ ATF_CHECK_EQ(BUS_OBJERR, last_code);
+}
+
+ATF_TC_WITHOUT_HEAD(page_fault_signal__bus_objerr_2);
+ATF_TC_BODY(page_fault_signal__bus_objerr_2, tc)
+{
+ int *p;
+ int fd;
+ int r;
+ int sz;
+
+ sz = getpagesize();
+ fd = shm_open(SHM_ANON, O_RDWR | O_CREAT, 0600);
+ ATF_REQUIRE(fd != -1);
+ r = ftruncate(fd, sz);
+ ATF_REQUIRE(r != -1);
+ p = mmap(NULL, sz * 2, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ ATF_REQUIRE(p != MAP_FAILED);
+ if (sigsetjmp(sig_env, 1) == 0) {
+ setup_signals();
+ ((volatile int *)p)[sz / sizeof(int)] = 1;
+ }
+ (void)munmap(p, sz * 2);
+ (void)close(fd);
+ ATF_CHECK_EQ(SIGBUS, last_sig);
+ ATF_CHECK_EQ(BUS_OBJERR, last_code);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, page_fault_signal__segv_maperr_1);
+ ATF_TP_ADD_TC(tp, page_fault_signal__segv_accerr_1);
+ ATF_TP_ADD_TC(tp, page_fault_signal__segv_accerr_2);
+ ATF_TP_ADD_TC(tp, page_fault_signal__bus_objerr_1);
+ ATF_TP_ADD_TC(tp, page_fault_signal__bus_objerr_2);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/vm/shared_shadow_inval_test.c b/tests/sys/vm/shared_shadow_inval_test.c
new file mode 100644
index 000000000000..761a7ce16cf0
--- /dev/null
+++ b/tests/sys/vm/shared_shadow_inval_test.c
@@ -0,0 +1,435 @@
+/*
+ * Copyright (c) 2021 Dell Inc. or its subsidiaries. All Rights Reserved.
+ * Copyright (c) 2022 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Mark Johnston under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test behavior when a mapping of a shared shadow vm object is
+ * invalidated by COW from another mapping. In particular, when
+ * minherit(INHERT_SHARE) is applied to a COW mapping, a subsequently
+ * forked child process will share the parent's shadow object. Thus,
+ * pages already mapped into one sharing process may be written from
+ * another, triggering a copy into the shadow object. The VM system
+ * expects that a fully shadowed page is unmapped, but at one point the
+ * use of a shared shadow object could break this invariant.
+ *
+ * This is a regression test for an issue isolated by rlibby@FreeBSD.org
+ * from an issue detected by stress2's collapse.sh by jeff@FreeBSD.org.
+ * The issue became CVE-2021-29626.
+ *
+ * This file is written as an ATF test suite but may be compiled as a
+ * standalone program with -DSTANDALONE (and optionally -DDEBUG).
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/procctl.h>
+#include <sys/resource.h>
+#include <sys/sysctl.h>
+#include <sys/wait.h>
+
+#include <machine/atomic.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifdef STANDALONE
+#define ATF_REQUIRE(x) do { \
+ if (!(x)) \
+ errx(1, "%s", #x); \
+} while (0)
+#else
+#include <atf-c.h>
+#endif
+
+#ifdef DEBUG
+#define dprintf(...) printf(__VA_ARGS__)
+#else
+#define dprintf(...)
+#endif
+
+#define DEPTH 5
+
+#define FLAG_COLLAPSE 0x1
+#define FLAG_BLOCK_XFER 0x2
+#define FLAG_FULLMOD 0x4
+#define FLAG_MASK (FLAG_COLLAPSE | FLAG_BLOCK_XFER | FLAG_FULLMOD)
+
+struct shared_state {
+ void *p;
+ size_t len;
+ size_t modlen;
+ size_t pagesize;
+ bool collapse;
+ bool block_xfer;
+ bool lazy_cow;
+ bool okay;
+ volatile bool exiting[DEPTH];
+ volatile bool exit;
+ volatile bool p3_did_write;
+};
+
+/*
+ * Program flow. There are three or four processes that are descendants
+ * of the process running the test (P0), where arrows go from parents to
+ * children, and thicker arrows indicate sharing a certain memory region
+ * without COW semantics:
+ * P0 -> P1 -> P2 => P3
+ * \=> P4
+ * The main idea is that P1 maps a memory region, and that region is
+ * shared with P2/P3, but with COW semantics. When P3 modifies the
+ * memory, P2 ought to see that modification. P4 optionally exists to
+ * defeat a COW optimization.
+ */
+
+#define child_err(...) do { \
+ ss->exit = true; \
+ err(1, __VA_ARGS__); \
+} while (0)
+
+#define child_errx(...) do { \
+ ss->exit = true; \
+ errx(1, __VA_ARGS__); \
+} while (0)
+
+#define SLEEP_TIME_US 1000
+
+static void child(struct shared_state *ss, int depth);
+
+static pid_t
+child_fork(struct shared_state *ss, int depth)
+{
+ pid_t pid = fork();
+ if (pid == -1)
+ child_err("fork");
+ else if (pid == 0)
+ child(ss, depth);
+ return pid;
+}
+
+static void
+child_fault(struct shared_state *ss)
+{
+ size_t i;
+
+ for (i = 0; i < ss->len; i += ss->pagesize)
+ (void)((volatile char *)ss->p)[i];
+}
+
+static void
+child_write(struct shared_state *ss, int val, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; i += ss->pagesize)
+ ((int *)ss->p)[i / sizeof(int)] = val;
+ atomic_thread_fence_rel();
+}
+
+static void
+child_wait_p3_write(struct shared_state *ss)
+{
+ while (!ss->p3_did_write) {
+ if (ss->exit)
+ exit(1);
+ usleep(SLEEP_TIME_US);
+ }
+ atomic_thread_fence_acq();
+}
+
+static void
+child_verify(struct shared_state *ss, int depth, int newval, int oldval)
+{
+ size_t i;
+ int expectval, foundval;
+
+ for (i = 0; i < ss->len; i += ss->pagesize) {
+ expectval = i < ss->modlen ? newval : oldval;
+ foundval = ((int *)ss->p)[i / sizeof(int)];
+ if (foundval == expectval)
+ continue;
+ child_errx("P%d saw %d but expected %d, %d was the old value",
+ depth, foundval, expectval, oldval);
+ }
+}
+
+static void
+child(struct shared_state *ss, int depth)
+{
+ pid_t mypid, oldval, pid;
+
+ if (depth < 1 || depth >= DEPTH)
+ child_errx("Bad depth %d", depth);
+ mypid = getpid();
+ dprintf("P%d (pid %d) started\n", depth, mypid);
+ switch (depth) {
+ case 1:
+ /* Shared memory undergoing test. */
+ ss->p = mmap(NULL, ss->len, PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANON, -1, 0);
+ if (ss->p == MAP_FAILED)
+ child_err("mmap");
+
+ /* P1 stamps the shared memory. */
+ child_write(ss, mypid, ss->len);
+ if (!ss->lazy_cow) {
+ if (mlock(ss->p, ss->len) == -1)
+ child_err("mlock");
+ if (mprotect(ss->p, ss->len, PROT_READ) == -1)
+ child_err("mprotect");
+ }
+ if (ss->block_xfer) {
+ /*
+ * P4 is forked so that its existence blocks a page COW
+ * path where the page is simply transferred between
+ * objects, rather than being copied.
+ */
+ child_fork(ss, 4);
+ }
+ /*
+ * P1 specifies that modifications from its child processes not
+ * be shared with P1. Child process reads can be serviced from
+ * pages in P1's object, but writes must be COW'd.
+ */
+ if (minherit(ss->p, ss->len, INHERIT_COPY) != 0)
+ child_err("minherit");
+ /* Fork P2. */
+ child_fork(ss, depth + 1);
+ /* P1 and P4 wait for P3's writes before exiting. */
+ child_wait_p3_write(ss);
+ child_verify(ss, depth, mypid, mypid);
+ if (!ss->collapse) {
+ /* Hang around to prevent collapse. */
+ while (!ss->exit)
+ usleep(SLEEP_TIME_US);
+ }
+ /* Exit so the P2 -> P1/P4 shadow chain can collapse. */
+ break;
+ case 2:
+ /*
+ * P2 now specifies that modifications from its child processes
+ * be shared. P2 and P3 will share a shadow object.
+ */
+ if (minherit(ss->p, ss->len, INHERIT_SHARE) != 0)
+ child_err("minherit");
+
+ /*
+ * P2 faults a page in P1's object before P1 exits and the
+ * shadow chain is collapsed. This may be redundant if the
+ * (read-only) mappings were copied by fork(), but it doesn't
+ * hurt.
+ */
+ child_fault(ss);
+ oldval = atomic_load_acq_int(ss->p);
+
+ /* Fork P3. */
+ pid = child_fork(ss, depth + 1);
+ if (ss->collapse) {
+ /* Wait for P1 and P4 to exit, triggering collapse. */
+ while (!ss->exiting[1] ||
+ (ss->block_xfer && !ss->exiting[4]))
+ usleep(SLEEP_TIME_US);
+ /*
+ * This is racy, just guess at how long it may take
+ * them to finish exiting.
+ */
+ usleep(100 * 1000);
+ }
+ /* P2 waits for P3's modification. */
+ child_wait_p3_write(ss);
+ child_verify(ss, depth, pid, oldval);
+ ss->okay = true;
+ ss->exit = true;
+ break;
+ case 3:
+ /*
+ * Use mlock()+mprotect() to trigger the COW. This
+ * exercises a different COW handler than the one used
+ * for lazy faults.
+ */
+ if (!ss->lazy_cow) {
+ if (mlock(ss->p, ss->len) == -1)
+ child_err("mlock");
+ if (mprotect(ss->p, ss->len, PROT_READ | PROT_WRITE) ==
+ -1)
+ child_err("mprotect");
+ }
+
+ /*
+ * P3 writes the memory. A page is faulted into the shared
+ * P2/P3 shadow object. P2's mapping of the page in P1's
+ * object must now be shot down, or else P2 will wrongly
+ * continue to have that page mapped.
+ */
+ child_write(ss, mypid, ss->modlen);
+ ss->p3_did_write = true;
+ dprintf("P3 (pid %d) wrote its pid\n", mypid);
+ break;
+ case 4:
+ /* Just hang around until P3 is done writing. */
+ oldval = atomic_load_acq_int(ss->p);
+ child_wait_p3_write(ss);
+ child_verify(ss, depth, oldval, oldval);
+ break;
+ default:
+ child_errx("Bad depth %d", depth);
+ }
+
+ dprintf("P%d (pid %d) exiting\n", depth, mypid);
+ ss->exiting[depth] = true;
+ exit(0);
+}
+
+static void
+do_one_shared_shadow_inval(bool lazy_cow, size_t pagesize, size_t len,
+ unsigned int flags)
+{
+ struct shared_state *ss;
+ pid_t pid;
+ int status;
+
+ pid = getpid();
+
+ dprintf("P0 (pid %d) %s(collapse=%d, block_xfer=%d, full_mod=%d)\n",
+ pid, __func__, (int)collapse, (int)block_xfer, (int)full_mod);
+
+ ATF_REQUIRE(procctl(P_PID, pid, PROC_REAP_ACQUIRE, NULL) == 0);
+
+ /* Shared memory for coordination. */
+ ss = mmap(NULL, sizeof(*ss), PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANON, -1, 0);
+ ATF_REQUIRE(ss != MAP_FAILED);
+
+ ss->len = len;
+ ss->modlen = (flags & FLAG_FULLMOD) ? ss->len : ss->len / 2;
+ ss->pagesize = pagesize;
+ ss->collapse = (flags & FLAG_COLLAPSE) != 0;
+ ss->block_xfer = (flags & FLAG_BLOCK_XFER) != 0;
+ ss->lazy_cow = lazy_cow;
+
+ pid = fork();
+ ATF_REQUIRE(pid != -1);
+ if (pid == 0)
+ child(ss, 1);
+
+ /* Wait for all descendants to exit. */
+ do {
+ pid = wait(&status);
+ ATF_REQUIRE(WIFEXITED(status));
+ } while (pid != -1 || errno != ECHILD);
+
+ atomic_thread_fence_acq();
+ ATF_REQUIRE(ss->okay);
+
+ ATF_REQUIRE(munmap(ss, sizeof(*ss)) == 0);
+ ATF_REQUIRE(procctl(P_PID, getpid(), PROC_REAP_RELEASE, NULL) == 0);
+}
+
+static void
+do_shared_shadow_inval(bool lazy_cow)
+{
+ size_t largepagesize, pagesize, pagesizes[MAXPAGESIZES], sysctllen;
+
+ sysctllen = sizeof(pagesizes);
+ ATF_REQUIRE(sysctlbyname("hw.pagesizes", pagesizes, &sysctllen, NULL,
+ 0) == 0);
+ ATF_REQUIRE(sysctllen >= sizeof(size_t));
+
+ pagesize = pagesizes[0];
+ largepagesize = MAXPAGESIZES >= 2 &&
+ sysctllen >= 2 * sizeof(size_t) && pagesizes[1] != 0 ?
+ pagesizes[1] : 2 * 1024 * 1024;
+
+ for (unsigned int i = 0; i <= FLAG_MASK; i++) {
+ do_one_shared_shadow_inval(lazy_cow, pagesize,
+ pagesize, i);
+ do_one_shared_shadow_inval(lazy_cow, pagesize,
+ 2 * pagesize, i);
+ do_one_shared_shadow_inval(lazy_cow, pagesize,
+ largepagesize - pagesize, i);
+ do_one_shared_shadow_inval(lazy_cow, pagesize,
+ largepagesize, i);
+ do_one_shared_shadow_inval(lazy_cow, pagesize,
+ largepagesize + pagesize, i);
+ }
+}
+
+static void
+do_shared_shadow_inval_eager(void)
+{
+ struct rlimit rl;
+
+ rl.rlim_cur = rl.rlim_max = RLIM_INFINITY;
+ ATF_REQUIRE(setrlimit(RLIMIT_MEMLOCK, &rl) == 0);
+
+ do_shared_shadow_inval(false);
+}
+
+static void
+do_shared_shadow_inval_lazy(void)
+{
+ do_shared_shadow_inval(true);
+}
+
+#ifdef STANDALONE
+int
+main(void)
+{
+ do_shared_shadow_inval_lazy();
+ do_shared_shadow_inval_eager();
+ printf("pass\n");
+}
+#else
+ATF_TC_WITHOUT_HEAD(shared_shadow_inval__lazy_cow);
+ATF_TC_BODY(shared_shadow_inval__lazy_cow, tc)
+{
+ do_shared_shadow_inval_lazy();
+}
+
+ATF_TC(shared_shadow_inval__eager_cow);
+ATF_TC_HEAD(shared_shadow_inval__eager_cow, tc)
+{
+ /* Needed to raise the mlock() limit. */
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(shared_shadow_inval__eager_cow, tc)
+{
+ do_shared_shadow_inval_eager();
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, shared_shadow_inval__lazy_cow);
+ ATF_TP_ADD_TC(tp, shared_shadow_inval__eager_cow);
+ return (atf_no_error());
+}
+#endif /* !STANDALONE */
diff --git a/tests/sys/vm/soxstack/Makefile b/tests/sys/vm/soxstack/Makefile
new file mode 100644
index 000000000000..f9f3bd55b50a
--- /dev/null
+++ b/tests/sys/vm/soxstack/Makefile
@@ -0,0 +1,16 @@
+PACKAGE= tests
+SHLIB= soxstack
+SHLIB_NAME= libsoxstack.so
+SHLIB_MAJOR= 1
+
+WITHOUT_STATIC=
+WITHOUT_PROFILE=
+WITHOUT_PIC=
+
+SRCS= soxstack.c
+LDFLAGS+= -Wl,-z,execstack
+LIBADD+= procstat
+
+LIBDIR= ${TESTSBASE}/sys/vm/stack
+
+.include <bsd.lib.mk>
diff --git a/tests/sys/vm/soxstack/soxstack.c b/tests/sys/vm/soxstack/soxstack.c
new file mode 100644
index 000000000000..ecb672c1b6dc
--- /dev/null
+++ b/tests/sys/vm/soxstack/soxstack.c
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 2023 Dmitry Chagin <dchagin@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+
+#include <assert.h>
+#include <libprocstat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+int checkstack(void);
+
+#define _STACK_FLAG_GROWS (KVME_FLAG_GROWS_UP | KVME_FLAG_GROWS_DOWN)
+int
+checkstack(void)
+{
+ struct kinfo_vmentry *freep, *kve;
+ struct kinfo_proc *p;
+ struct procstat *prstat;
+ uintptr_t stack;
+ int i, cnt;
+
+ prstat = procstat_open_sysctl();
+ assert(prstat != NULL);
+ p = procstat_getprocs(prstat, KERN_PROC_PID, getpid(), &cnt);
+ assert(p != NULL);
+ freep = procstat_getvmmap(prstat, p, &cnt);
+ assert(freep != NULL);
+
+ stack = (uintptr_t)&i;
+ for (i = 0; i < cnt; i++) {
+ kve = &freep[i];
+ if (stack < kve->kve_start || stack > kve->kve_end)
+ continue;
+ if ((kve->kve_flags & _STACK_FLAG_GROWS) != 0 &&
+ (kve->kve_protection & KVME_PROT_EXEC) != 0)
+ stack = 0;
+ break;
+ }
+
+ free(freep);
+ procstat_freeprocs(prstat, p);
+ procstat_close(prstat);
+ return (stack != 0);
+}
diff --git a/tests/sys/vm/stack/Makefile b/tests/sys/vm/stack/Makefile
new file mode 100644
index 000000000000..06ffcf4d4b4a
--- /dev/null
+++ b/tests/sys/vm/stack/Makefile
@@ -0,0 +1,13 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/vm/stack
+
+ATF_TESTS_C+= stack_dt_need_exec_test
+ATF_TESTS_C+= stack_dlopen_exec_test
+ATF_TESTS_C+= stack_mprotect_exec_test
+
+LDFLAGS.stack_dt_need_exec_test+= -Wl,-rpath,${TESTSDIR} -L${.OBJDIR:H}/soxstack
+LDADD.stack_dt_need_exec_test+= -lsoxstack
+LDFLAGS.stack_dlopen_exec_test+= -Wl,-rpath,${TESTSDIR}
+
+.include <bsd.test.mk>
diff --git a/tests/sys/vm/stack/stack_dlopen_exec_test.c b/tests/sys/vm/stack/stack_dlopen_exec_test.c
new file mode 100644
index 000000000000..d4d5fc7c5cf0
--- /dev/null
+++ b/tests/sys/vm/stack/stack_dlopen_exec_test.c
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 2023 Dmitry Chagin <dchagin@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/systm.h>
+#include <vm/vm_param.h>
+
+#include <atf-c.h>
+#include <dlfcn.h>
+
+static int jumpstack0(void) __noinline;
+static int jumpstack1(void) __noinline;
+
+static int (*socheckstack)(void) = NULL;
+
+static int
+checkstack(void)
+{
+ void *fh;
+
+ if (socheckstack == NULL) {
+ fh = dlopen("libsoxstack.so", RTLD_LAZY);
+ ATF_REQUIRE(fh != NULL);
+ socheckstack = dlsym(fh, "checkstack");
+ ATF_REQUIRE(socheckstack != NULL);
+ }
+ return (socheckstack());
+}
+
+static int
+jumpstack0(void)
+{
+ char stack[SGROWSIZ];
+
+ explicit_bzero(stack, sizeof(stack));
+ return (checkstack());
+}
+
+static int
+jumpstack1(void)
+{
+ char stack[SGROWSIZ * 2];
+
+ explicit_bzero(stack, sizeof(stack));
+ return (checkstack());
+}
+
+ATF_TC_WITHOUT_HEAD(dlopen_test);
+ATF_TC_BODY(dlopen_test, tc)
+{
+
+ ATF_REQUIRE(jumpstack0() == 0);
+ ATF_REQUIRE(jumpstack1() == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, dlopen_test);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/vm/stack/stack_dt_need_exec_test.c b/tests/sys/vm/stack/stack_dt_need_exec_test.c
new file mode 100644
index 000000000000..8a234abe3da5
--- /dev/null
+++ b/tests/sys/vm/stack/stack_dt_need_exec_test.c
@@ -0,0 +1,50 @@
+/*-
+ * Copyright (c) 2023 Dmitry Chagin <dchagin@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/systm.h>
+#include <vm/vm_param.h>
+
+#include <atf-c.h>
+
+extern int checkstack(void);
+
+static int jumpstack0(void) __noinline;
+static int jumpstack1(void) __noinline;
+
+
+static int
+jumpstack0(void)
+{
+ char stack[SGROWSIZ];
+
+ explicit_bzero(stack, sizeof(stack));
+ return (checkstack());
+}
+
+static int
+jumpstack1(void)
+{
+ char stack[SGROWSIZ * 2];
+
+ explicit_bzero(stack, sizeof(stack));
+ return (checkstack());
+}
+
+ATF_TC_WITHOUT_HEAD(dt_need_test);
+ATF_TC_BODY(dt_need_test, tc)
+{
+
+ ATF_REQUIRE(jumpstack0() == 0);
+ ATF_REQUIRE(jumpstack1() == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, dt_need_test);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/vm/stack/stack_mprotect_exec_test.c b/tests/sys/vm/stack/stack_mprotect_exec_test.c
new file mode 100644
index 000000000000..ba5a1d5cb859
--- /dev/null
+++ b/tests/sys/vm/stack/stack_mprotect_exec_test.c
@@ -0,0 +1,57 @@
+/*-
+ * Copyright (c) 2023 Dmitry Chagin <dchagin@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * PR: 272585
+ * Test provided by John F. Carr
+ */
+
+#include <sys/systm.h>
+#include <sys/mman.h>
+#include <vm/vm_param.h>
+
+#include <atf-c.h>
+#include <signal.h>
+#include <unistd.h>
+
+static void
+sigsegv_handler(int sig __unused)
+{
+
+ atf_tc_fail("Invalid stack protection mode after grows");
+}
+
+ATF_TC_WITHOUT_HEAD(mprotect_exec_test);
+ATF_TC_BODY(mprotect_exec_test, tc)
+{
+ long pagesize;
+ char *addr, *guard;
+ size_t alloc_size;
+
+ signal(SIGSEGV, sigsegv_handler);
+
+ pagesize = sysconf(_SC_PAGESIZE);
+ ATF_REQUIRE(pagesize > 0);
+
+ alloc_size = SGROWSIZ * 2;
+ addr = mmap(NULL, alloc_size, PROT_READ | PROT_WRITE,
+ MAP_STACK | MAP_PRIVATE | MAP_ANON, -1, 0);
+ ATF_REQUIRE(addr != MAP_FAILED);
+
+ /*
+ * Change prot of the last page in the mmaped stack area.
+ */
+ guard = addr + alloc_size - SGROWSIZ;
+ ATF_REQUIRE(mprotect(guard, pagesize, PROT_NONE) == 0);
+
+ ((volatile char *)guard)[-1];
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, mprotect_exec_test);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/vmm/Makefile b/tests/sys/vmm/Makefile
new file mode 100644
index 000000000000..544d98421a95
--- /dev/null
+++ b/tests/sys/vmm/Makefile
@@ -0,0 +1,11 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/sys/vmm
+
+BINDIR= ${TESTSDIR}
+
+ATF_TESTS_SH+= vmm_cred_jail
+
+${PACKAGE}FILES+= utils.subr
+
+.include <bsd.test.mk>
diff --git a/tests/sys/vmm/utils.subr b/tests/sys/vmm/utils.subr
new file mode 100644
index 000000000000..d515770a7a8f
--- /dev/null
+++ b/tests/sys/vmm/utils.subr
@@ -0,0 +1,47 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2021 The FreeBSD Foundation
+#
+# This software was developed by Cyril Zhang under sponsorship from
+# the FreeBSD Foundation.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+vmm_mkjail()
+{
+ jailname=$1
+ jail -c name=${jailname} persist allow.vmm
+ echo $jailname >> created_jails.lst
+}
+vmm_cleanup()
+{
+ if [ -f created_jails.lst ]
+ then
+ for jailname in `cat created_jails.lst`
+ do
+ jail -r ${jailname}
+ done
+ rm created_jails.lst
+ fi
+}
diff --git a/tests/sys/vmm/vmm_cred_jail.sh b/tests/sys/vmm/vmm_cred_jail.sh
new file mode 100644
index 000000000000..5b02b5dc0b42
--- /dev/null
+++ b/tests/sys/vmm/vmm_cred_jail.sh
@@ -0,0 +1,80 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2021 The FreeBSD Foundation
+#
+# This software was developed by Cyril Zhang under sponsorship from
+# the FreeBSD Foundation.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case vmm_cred_jail_host cleanup
+vmm_cred_jail_host_head()
+{
+ atf_set "descr" "Tests deleting the host's VM from within a jail"
+ atf_set "require.user" "root"
+}
+vmm_cred_jail_host_body()
+{
+ if ! -c /dev/vmmctl; then
+ atf_skip "vmm is not loaded"
+ fi
+ bhyvectl --vm=testvm --create
+ vmm_mkjail myjail
+ atf_check -s exit:1 -e ignore jexec myjail bhyvectl --vm=testvm --destroy
+}
+vmm_cred_jail_host_cleanup()
+{
+ bhyvectl --vm=testvm --destroy
+ vmm_cleanup
+}
+
+atf_test_case vmm_cred_jail_other cleanup
+vmm_cred_jail_other_head()
+{
+ atf_set "descr" "Tests deleting a jail's VM from within another jail"
+ atf_set "require.user" "root"
+}
+vmm_cred_jail_other_body()
+{
+ if ! -c /dev/vmmctl; then
+ atf_skip "vmm is not loaded"
+ fi
+ vmm_mkjail myjail1
+ vmm_mkjail myjail2
+ atf_check -s exit:0 jexec myjail1 bhyvectl --vm=testvm --create
+ atf_check -s exit:1 -e ignore jexec myjail2 bhyvectl --vm=testvm --destroy
+}
+vmm_cred_jail_other_cleanup()
+{
+ bhyvectl --vm=testvm --destroy
+ vmm_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case vmm_cred_jail_host
+ atf_add_test_case vmm_cred_jail_other
+}